summaryrefslogtreecommitdiff
path: root/rust/helpers/rbtree.c
blob: 6d404b84a9b53990cf7b301cb501f3b3332749e9 (plain)
1
2
3
4
5
6
7
8
9
// SPDX-License-Identifier: GPL-2.0

#include <linux/rbtree.h>

void rust_helper_rb_link_node(struct rb_node *node, struct rb_node *parent,
			      struct rb_node **rb_link)
{
	rb_link_node(node, parent, rb_link);
}
/option> Russell King's ARM Linux kernel treeRussell King
summaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/.gitignore2
-rw-r--r--drivers/scsi/3w-9xxx.c461
-rw-r--r--drivers/scsi/3w-9xxx.h287
-rw-r--r--drivers/scsi/3w-sas.c326
-rw-r--r--drivers/scsi/3w-sas.h133
-rw-r--r--drivers/scsi/3w-xxxx.c461
-rw-r--r--drivers/scsi/3w-xxxx.h216
-rw-r--r--drivers/scsi/53c700.c333
-rw-r--r--drivers/scsi/53c700.h36
-rw-r--r--drivers/scsi/BusLogic.c4528
-rw-r--r--drivers/scsi/BusLogic.h1487
-rw-r--r--drivers/scsi/FlashPoint.c847
-rw-r--r--drivers/scsi/Kconfig1081
-rw-r--r--drivers/scsi/Makefile85
-rw-r--r--drivers/scsi/NCR5380.c3674
-rw-r--r--drivers/scsi/NCR5380.h358
-rw-r--r--drivers/scsi/NCR53c406a.c1091
-rw-r--r--drivers/scsi/NCR_D700.c405
-rw-r--r--drivers/scsi/NCR_D700.h29
-rw-r--r--drivers/scsi/NCR_Q720.c377
-rw-r--r--drivers/scsi/NCR_Q720.h28
-rw-r--r--drivers/scsi/a100u2w.c79
-rw-r--r--drivers/scsi/a2091.c116
-rw-r--r--drivers/scsi/a2091.h1
-rw-r--r--drivers/scsi/a3000.c131
-rw-r--r--drivers/scsi/a3000.h1
-rw-r--r--drivers/scsi/a4000t.c34
-rw-r--r--drivers/scsi/aacraid/Makefile3
-rw-r--r--drivers/scsi/aacraid/TODO3
-rw-r--r--drivers/scsi/aacraid/aachba.c2424
-rw-r--r--drivers/scsi/aacraid/aacraid.h1076
-rw-r--r--drivers/scsi/aacraid/commctrl.c493
-rw-r--r--drivers/scsi/aacraid/comminit.c482
-rw-r--r--drivers/scsi/aacraid/commsup.c1562
-rw-r--r--drivers/scsi/aacraid/dpcsup.c190
-rw-r--r--drivers/scsi/aacraid/linit.c1381
-rw-r--r--drivers/scsi/aacraid/nark.c24
-rw-r--r--drivers/scsi/aacraid/rkt.c28
-rw-r--r--drivers/scsi/aacraid/rx.c107
-rw-r--r--drivers/scsi/aacraid/sa.c83
-rw-r--r--drivers/scsi/aacraid/src.c1186
-rw-r--r--drivers/scsi/advansys.c3225
-rw-r--r--drivers/scsi/aha152x.c1795
-rw-r--r--drivers/scsi/aha152x.h1
-rw-r--r--drivers/scsi/aha1542.c1963
-rw-r--r--drivers/scsi/aha1542.h142
-rw-r--r--drivers/scsi/aha1740.c67
-rw-r--r--drivers/scsi/aha1740.h6
-rw-r--r--drivers/scsi/aic7xxx/.gitignore1
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic79xx83
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic7xxx109
-rw-r--r--drivers/scsi/aic7xxx/Makefile14
-rw-r--r--drivers/scsi/aic7xxx/aic7770.c21
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c10
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.h69
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c475
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c204
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.h65
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm_pci.c54
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_pci.c38
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_proc.c180
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg.h_shipped1251
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped34
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_seq.h_shipped3
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.h23
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.reg2
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_93cx6.c10
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_core.c412
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c336
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.h61
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm_pci.c55
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_pci.c21
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_proc.c170
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped44
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped3
-rw-r--r--drivers/scsi/aic7xxx/aicasm/Makefile64
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.c9
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.h4
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_gram.y6
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h1
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y5
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l4
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_scan.l7
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c5
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h6
-rw-r--r--drivers/scsi/aic7xxx/aiclib.c34
-rw-r--r--drivers/scsi/aic7xxx/aiclib.h15
-rw-r--r--drivers/scsi/aic7xxx/queue.h2
-rw-r--r--drivers/scsi/aic7xxx/scsi_message.h30
-rw-r--r--drivers/scsi/aic7xxx_old.c11149
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx.h28
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx.reg1401
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx.seq1539
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx_proc.c373
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx_reg.h629
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx_seq.c817
-rw-r--r--drivers/scsi/aic7xxx_old/scsi_message.h49
-rw-r--r--drivers/scsi/aic7xxx_old/sequencer.h135
-rw-r--r--drivers/scsi/aic94xx/Kconfig22
-rw-r--r--drivers/scsi/aic94xx/Makefile18
-rw-r--r--drivers/scsi/aic94xx/aic94xx.h34
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dev.c85
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dump.c208
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dump.h20
-rw-r--r--drivers/scsi/aic94xx/aic94xx_hwi.c66
-rw-r--r--drivers/scsi/aic94xx/aic94xx_hwi.h25
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c154
-rw-r--r--drivers/scsi/aic94xx/aic94xx_reg.c20
-rw-r--r--drivers/scsi/aic94xx/aic94xx_reg.h20
-rw-r--r--drivers/scsi/aic94xx/aic94xx_reg_def.h20
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sas.h69
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c64
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sds.c49
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sds.h20
-rw-r--r--drivers/scsi/aic94xx/aic94xx_seq.c31
-rw-r--r--drivers/scsi/aic94xx/aic94xx_seq.h20
-rw-r--r--drivers/scsi/aic94xx/aic94xx_task.c124
-rw-r--r--drivers/scsi/aic94xx/aic94xx_tmf.c65
-rw-r--r--drivers/scsi/am53c974.c542
-rw-r--r--drivers/scsi/arcmsr/Makefile1
-rw-r--r--drivers/scsi/arcmsr/arcmsr.h812
-rw-r--r--drivers/scsi/arcmsr/arcmsr_attr.c167
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c4135
-rw-r--r--drivers/scsi/arm/Kconfig22
-rw-r--r--drivers/scsi/arm/Makefile1
-rw-r--r--drivers/scsi/arm/acornscsi-io.S5
-rw-r--r--drivers/scsi/arm/acornscsi.c389
-rw-r--r--drivers/scsi/arm/acornscsi.h5
-rw-r--r--drivers/scsi/arm/arm_scsi.h (renamed from drivers/scsi/arm/scsi.h)42
-rw-r--r--drivers/scsi/arm/arxescsi.c62
-rw-r--r--drivers/scsi/arm/cumana_1.c176
-rw-r--r--drivers/scsi/arm/cumana_2.c98
-rw-r--r--drivers/scsi/arm/eesox.c86
-rw-r--r--drivers/scsi/arm/fas216.c221
-rw-r--r--drivers/scsi/arm/fas216.h29
-rw-r--r--drivers/scsi/arm/msgqueue.c5
-rw-r--r--drivers/scsi/arm/msgqueue.h5
-rw-r--r--drivers/scsi/arm/oak.c99
-rw-r--r--drivers/scsi/arm/powertec.c74
-rw-r--r--drivers/scsi/arm/queue.c18
-rw-r--r--drivers/scsi/arm/queue.h5
-rw-r--r--drivers/scsi/atari_NCR5380.c2989
-rw-r--r--drivers/scsi/atari_scsi.c952
-rw-r--r--drivers/scsi/atari_scsi.h184
-rw-r--r--drivers/scsi/atp870u.c4080
-rw-r--r--drivers/scsi/atp870u.h20
-rw-r--r--drivers/scsi/be2iscsi/Kconfig6
-rw-r--r--drivers/scsi/be2iscsi/Makefile3
-rw-r--r--drivers/scsi/be2iscsi/be.h69
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c1876
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h767
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c1182
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h36
-rw-r--r--drivers/scsi/be2iscsi/be_main.c5952
-rw-r--r--drivers/scsi/be2iscsi/be_main.h664
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c1752
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h269
-rw-r--r--drivers/scsi/bfa/Makefile3
-rw-r--r--drivers/scsi/bfa/bfa.h250
-rw-r--r--drivers/scsi/bfa/bfa_core.c1508
-rw-r--r--drivers/scsi/bfa/bfa_cs.h89
-rw-r--r--drivers/scsi/bfa/bfa_defs.h906
-rw-r--r--drivers/scsi/bfa/bfa_defs_fcs.h84
-rw-r--r--drivers/scsi/bfa/bfa_defs_svc.h845
-rw-r--r--drivers/scsi/bfa/bfa_fc.h79
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.c673
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.h116
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.c982
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.h222
-rw-r--r--drivers/scsi/bfa/bfa_fcs.c559
-rw-r--r--drivers/scsi/bfa/bfa_fcs.h443
-rw-r--r--drivers/scsi/bfa/bfa_fcs_fcpim.c219
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c1750
-rw-r--r--drivers/scsi/bfa/bfa_fcs_rport.c1015
-rw-r--r--drivers/scsi/bfa/bfa_hw_cb.c145
-rw-r--r--drivers/scsi/bfa/bfa_hw_ct.c129
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c4852
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h812
-rw-r--r--drivers/scsi/bfa/bfa_ioc_cb.c187
-rw-r--r--drivers/scsi/bfa/bfa_ioc_ct.c702
-rw-r--r--drivers/scsi/bfa/bfa_modules.h131
-rw-r--r--drivers/scsi/bfa/bfa_plog.h17
-rw-r--r--drivers/scsi/bfa/bfa_port.c492
-rw-r--r--drivers/scsi/bfa/bfa_port.h84
-rw-r--r--drivers/scsi/bfa/bfa_svc.c3022
-rw-r--r--drivers/scsi/bfa/bfa_svc.h357
-rw-r--r--drivers/scsi/bfa/bfad.c803
-rw-r--r--drivers/scsi/bfa/bfad_attr.c353
-rw-r--r--drivers/scsi/bfa/bfad_bsg.c3612
-rw-r--r--drivers/scsi/bfa/bfad_bsg.h829
-rw-r--r--drivers/scsi/bfa/bfad_debugfs.c129
-rw-r--r--drivers/scsi/bfa/bfad_drv.h86
-rw-r--r--drivers/scsi/bfa/bfad_im.c365
-rw-r--r--drivers/scsi/bfa/bfad_im.h88
-rw-r--r--drivers/scsi/bfa/bfi.h850
-rw-r--r--drivers/scsi/bfa/bfi_cbreg.h305
-rw-r--r--drivers/scsi/bfa/bfi_ctreg.h636
-rw-r--r--drivers/scsi/bfa/bfi_ms.h196
-rw-r--r--drivers/scsi/bfa/bfi_reg.h452
-rw-r--r--drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h1174
-rw-r--r--drivers/scsi/bnx2fc/Kconfig16
-rw-r--r--drivers/scsi/bnx2fc/Makefile4
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc.h250
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_constants.h154
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_debug.c84
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_debug.h87
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_els.c527
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c1808
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_hwi.c1099
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_io.c878
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_tgt.c342
-rw-r--r--drivers/scsi/bnx2i/57xx_iscsi_constants.h11
-rw-r--r--drivers/scsi/bnx2i/57xx_iscsi_hsi.h48
-rw-r--r--drivers/scsi/bnx2i/Kconfig11
-rw-r--r--drivers/scsi/bnx2i/Makefile1
-rw-r--r--drivers/scsi/bnx2i/bnx2i.h135
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c554
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c201
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c311
-rw-r--r--drivers/scsi/bnx2i/bnx2i_sysfs.c29
-rw-r--r--drivers/scsi/bvme6000_scsi.c10
-rw-r--r--drivers/scsi/ch.c300
-rw-r--r--drivers/scsi/constants.c1396
-rw-r--r--drivers/scsi/csiostor/Kconfig20
-rw-r--r--drivers/scsi/csiostor/Makefile13
-rw-r--r--drivers/scsi/csiostor/csio_attr.c805
-rw-r--r--drivers/scsi/csiostor/csio_defs.h135
-rw-r--r--drivers/scsi/csiostor/csio_hw.c4434
-rw-r--r--drivers/scsi/csiostor/csio_hw.h666
-rw-r--r--drivers/scsi/csiostor/csio_hw_chip.h135
-rw-r--r--drivers/scsi/csiostor/csio_hw_t5.c369
-rw-r--r--drivers/scsi/csiostor/csio_init.c1253
-rw-r--r--drivers/scsi/csiostor/csio_init.h136
-rw-r--r--drivers/scsi/csiostor/csio_isr.c610
-rw-r--r--drivers/scsi/csiostor/csio_lnode.c2152
-rw-r--r--drivers/scsi/csiostor/csio_lnode.h242
-rw-r--r--drivers/scsi/csiostor/csio_mb.c1690
-rw-r--r--drivers/scsi/csiostor/csio_mb.h263
-rw-r--r--drivers/scsi/csiostor/csio_rnode.c921
-rw-r--r--drivers/scsi/csiostor/csio_rnode.h141
-rw-r--r--drivers/scsi/csiostor/csio_scsi.c2529
-rw-r--r--drivers/scsi/csiostor/csio_scsi.h352
-rw-r--r--drivers/scsi/csiostor/csio_wr.c1719
-rw-r--r--drivers/scsi/csiostor/csio_wr.h512
-rw-r--r--drivers/scsi/csiostor/t4fw_api_stor.h539
-rw-r--r--drivers/scsi/cxgbi/Kconfig1
-rw-r--r--drivers/scsi/cxgbi/Makefile3
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/Kbuild4
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/Kconfig9
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.c288
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.h2
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/Kbuild4
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/Kconfig13
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c1567
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.h19
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c2024
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.h329
-rw-r--r--drivers/scsi/dc395x.c1350
-rw-r--r--drivers/scsi/dc395x.h39
-rw-r--r--drivers/scsi/device_handler/Kconfig13
-rw-r--r--drivers/scsi/device_handler/Makefile2
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c565
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c1540
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c381
-rw-r--r--drivers/scsi/device_handler/scsi_dh_hp_sw.c364
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c546
-rw-r--r--drivers/scsi/dmx3191d.c111
-rw-r--r--drivers/scsi/dpt/dpti_i2o.h446
-rw-r--r--drivers/scsi/dpt/dpti_ioctl.h139
-rw-r--r--drivers/scsi/dpt/dptsig.h336
-rw-r--r--drivers/scsi/dpt/osd_defs.h79
-rw-r--r--drivers/scsi/dpt/osd_util.h358
-rw-r--r--drivers/scsi/dpt/sys_info.h417
-rw-r--r--drivers/scsi/dpt_i2o.c3700
-rw-r--r--drivers/scsi/dpti.h337
-rw-r--r--drivers/scsi/dtc.c486
-rw-r--r--drivers/scsi/dtc.h99
-rw-r--r--drivers/scsi/eata.c2590
-rw-r--r--drivers/scsi/eata_generic.h400
-rw-r--r--drivers/scsi/eata_pio.c1001
-rw-r--r--drivers/scsi/eata_pio.h53
-rw-r--r--drivers/scsi/elx/Kconfig9
-rw-r--r--drivers/scsi/elx/Makefile18
-rw-r--r--drivers/scsi/elx/efct/efct_driver.c783
-rw-r--r--drivers/scsi/elx/efct/efct_driver.h108
-rw-r--r--drivers/scsi/elx/efct/efct_hw.c3581
-rw-r--r--drivers/scsi/elx/efct/efct_hw.h764
-rw-r--r--drivers/scsi/elx/efct/efct_hw_queues.c677
-rw-r--r--drivers/scsi/elx/efct/efct_io.c190
-rw-r--r--drivers/scsi/elx/efct/efct_io.h174
-rw-r--r--drivers/scsi/elx/efct/efct_lio.c1681
-rw-r--r--drivers/scsi/elx/efct/efct_lio.h189
-rw-r--r--drivers/scsi/elx/efct/efct_scsi.c1157
-rw-r--r--drivers/scsi/elx/efct/efct_scsi.h203
-rw-r--r--drivers/scsi/elx/efct/efct_unsol.c492
-rw-r--r--drivers/scsi/elx/efct/efct_unsol.h17
-rw-r--r--drivers/scsi/elx/efct/efct_xport.c1111
-rw-r--r--drivers/scsi/elx/efct/efct_xport.h186
-rw-r--r--drivers/scsi/elx/include/efc_common.h37
-rw-r--r--drivers/scsi/elx/libefc/efc.h52
-rw-r--r--drivers/scsi/elx/libefc/efc_cmds.c782
-rw-r--r--drivers/scsi/elx/libefc/efc_cmds.h35
-rw-r--r--drivers/scsi/elx/libefc/efc_device.c1602
-rw-r--r--drivers/scsi/elx/libefc/efc_device.h72
-rw-r--r--drivers/scsi/elx/libefc/efc_domain.c1088
-rw-r--r--drivers/scsi/elx/libefc/efc_domain.h54
-rw-r--r--drivers/scsi/elx/libefc/efc_els.c1094
-rw-r--r--drivers/scsi/elx/libefc/efc_els.h107
-rw-r--r--drivers/scsi/elx/libefc/efc_fabric.c1563
-rw-r--r--drivers/scsi/elx/libefc/efc_fabric.h116
-rw-r--r--drivers/scsi/elx/libefc/efc_node.c1102
-rw-r--r--drivers/scsi/elx/libefc/efc_node.h191
-rw-r--r--drivers/scsi/elx/libefc/efc_nport.c777
-rw-r--r--drivers/scsi/elx/libefc/efc_nport.h50
-rw-r--r--drivers/scsi/elx/libefc/efc_sm.c54
-rw-r--r--drivers/scsi/elx/libefc/efc_sm.h197
-rw-r--r--drivers/scsi/elx/libefc/efclib.c81
-rw-r--r--drivers/scsi/elx/libefc/efclib.h623
-rw-r--r--drivers/scsi/elx/libefc_sli/sli4.c5155
-rw-r--r--drivers/scsi/elx/libefc_sli/sli4.h4132
-rw-r--r--drivers/scsi/esas2r/Kconfig6
-rw-r--r--drivers/scsi/esas2r/Makefile6
-rw-r--r--drivers/scsi/esas2r/atioctl.h1255
-rw-r--r--drivers/scsi/esas2r/atvda.h1319
-rw-r--r--drivers/scsi/esas2r/esas2r.h1421
-rw-r--r--drivers/scsi/esas2r/esas2r_disc.c1185
-rw-r--r--drivers/scsi/esas2r/esas2r_flash.c1522
-rw-r--r--drivers/scsi/esas2r/esas2r_init.c1698
-rw-r--r--drivers/scsi/esas2r/esas2r_int.c944
-rw-r--r--drivers/scsi/esas2r/esas2r_io.c877
-rw-r--r--drivers/scsi/esas2r/esas2r_ioctl.c2083
-rw-r--r--drivers/scsi/esas2r/esas2r_log.c252
-rw-r--r--drivers/scsi/esas2r/esas2r_log.h118
-rw-r--r--drivers/scsi/esas2r/esas2r_main.c1912
-rw-r--r--drivers/scsi/esas2r/esas2r_targdb.c306
-rw-r--r--drivers/scsi/esas2r/esas2r_vda.c507
-rw-r--r--drivers/scsi/esp_scsi.c867
-rw-r--r--drivers/scsi/esp_scsi.h97
-rw-r--r--drivers/scsi/fcoe/Makefile3
-rw-r--r--drivers/scsi/fcoe/fcoe.c1569
-rw-r--r--drivers/scsi/fcoe/fcoe.h53
-rw-r--r--drivers/scsi/fcoe/fcoe_ctlr.c963
-rw-r--r--drivers/scsi/fcoe/fcoe_sysfs.c1042
-rw-r--r--drivers/scsi/fcoe/fcoe_transport.c421
-rw-r--r--drivers/scsi/fcoe/libfcoe.h21
-rw-r--r--drivers/scsi/fd_mcs.c1355
-rw-r--r--drivers/scsi/fdomain.c2208
-rw-r--r--drivers/scsi/fdomain.h136
-rw-r--r--drivers/scsi/fdomain_isa.c219
-rw-r--r--drivers/scsi/fdomain_pci.c68
-rw-r--r--drivers/scsi/fnic/Makefile8
-rw-r--r--drivers/scsi/fnic/cq_desc.h14
-rw-r--r--drivers/scsi/fnic/cq_enet_desc.h14
-rw-r--r--drivers/scsi/fnic/cq_exch_desc.h14
-rw-r--r--drivers/scsi/fnic/fcpio.h14
-rw-r--r--drivers/scsi/fnic/fdls_disc.c5093
-rw-r--r--drivers/scsi/fnic/fdls_fc.h253
-rw-r--r--drivers/scsi/fnic/fip.c1005
-rw-r--r--drivers/scsi/fnic/fip.h159
-rw-r--r--drivers/scsi/fnic/fnic.h497
-rw-r--r--drivers/scsi/fnic/fnic_attrs.c48
-rw-r--r--drivers/scsi/fnic/fnic_debugfs.c721
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c1208
-rw-r--r--drivers/scsi/fnic/fnic_fdls.h435
-rw-r--r--drivers/scsi/fnic/fnic_io.h26
-rw-r--r--drivers/scsi/fnic/fnic_isr.c230
-rw-r--r--drivers/scsi/fnic/fnic_main.c1071
-rw-r--r--drivers/scsi/fnic/fnic_pci_subsys_devid.c131
-rw-r--r--drivers/scsi/fnic/fnic_res.c100
-rw-r--r--drivers/scsi/fnic/fnic_res.h14
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c2724
-rw-r--r--drivers/scsi/fnic/fnic_stats.h175
-rw-r--r--drivers/scsi/fnic/fnic_trace.c883
-rw-r--r--drivers/scsi/fnic/fnic_trace.h115
-rw-r--r--drivers/scsi/fnic/rq_enet_desc.h14
-rw-r--r--drivers/scsi/fnic/vnic_cq.c14
-rw-r--r--drivers/scsi/fnic/vnic_cq.h14
-rw-r--r--drivers/scsi/fnic/vnic_cq_copy.h14
-rw-r--r--drivers/scsi/fnic/vnic_dev.c358
-rw-r--r--drivers/scsi/fnic/vnic_dev.h18
-rw-r--r--drivers/scsi/fnic/vnic_devcmd.h241
-rw-r--r--drivers/scsi/fnic/vnic_intr.c14
-rw-r--r--drivers/scsi/fnic/vnic_intr.h14
-rw-r--r--drivers/scsi/fnic/vnic_nic.h14
-rw-r--r--drivers/scsi/fnic/vnic_resource.h21
-rw-r--r--drivers/scsi/fnic/vnic_rq.c20
-rw-r--r--drivers/scsi/fnic/vnic_rq.h14
-rw-r--r--drivers/scsi/fnic/vnic_scsi.h31
-rw-r--r--drivers/scsi/fnic/vnic_stats.h14
-rw-r--r--drivers/scsi/fnic/vnic_wq.c83
-rw-r--r--drivers/scsi/fnic/vnic_wq.h22
-rw-r--r--drivers/scsi/fnic/vnic_wq_copy.c23
-rw-r--r--drivers/scsi/fnic/vnic_wq_copy.h14
-rw-r--r--drivers/scsi/fnic/wq_enet_desc.h14
-rw-r--r--drivers/scsi/g_NCR5380.c1464
-rw-r--r--drivers/scsi/g_NCR5380.h137
-rw-r--r--drivers/scsi/g_NCR5380_mmio.c10
-rw-r--r--drivers/scsi/gdth.c5244
-rw-r--r--drivers/scsi/gdth.h1021
-rw-r--r--drivers/scsi/gdth_ioctl.h339
-rw-r--r--drivers/scsi/gdth_proc.c760
-rw-r--r--drivers/scsi/gdth_proc.h25
-rw-r--r--drivers/scsi/gvp11.c156
-rw-r--r--drivers/scsi/gvp11.h1
-rw-r--r--drivers/scsi/hisi_sas/Kconfig26
-rw-r--r--drivers/scsi/hisi_sas/Makefile4
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas.h692
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_main.c2702
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v1_hw.c1818
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v2_hw.c3667
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v3_hw.c5329
-rw-r--r--drivers/scsi/hosts.c361
-rw-r--r--drivers/scsi/hpsa.c8808
-rw-r--r--drivers/scsi/hpsa.h469
-rw-r--r--drivers/scsi/hpsa_cmd.h726
-rw-r--r--drivers/scsi/hptiop.c596
-rw-r--r--drivers/scsi/hptiop.h95
-rw-r--r--drivers/scsi/ibmmca.c2380
-rw-r--r--drivers/scsi/ibmvscsi/Makefile9
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c3217
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h614
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c904
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.h53
-rw-r--r--drivers/scsi/ibmvscsi/ibmvstgt.c1004
-rw-r--r--drivers/scsi/ibmvscsi/iseries_vscsi.c173
-rw-r--r--drivers/scsi/ibmvscsi/rpa_vscsi.c368
-rw-r--r--drivers/scsi/ibmvscsi/viosrp.h217
-rw-r--r--drivers/scsi/ibmvscsi_tgt/Makefile4
-rw-r--r--drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c4070
-rw-r--r--drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h362
-rw-r--r--drivers/scsi/ibmvscsi_tgt/libsrp.c420
-rw-r--r--drivers/scsi/ibmvscsi_tgt/libsrp.h127
-rw-r--r--drivers/scsi/imm.c333
-rw-r--r--drivers/scsi/imm.h10
-rw-r--r--drivers/scsi/in2000.c2337
-rw-r--r--drivers/scsi/in2000.h412
-rw-r--r--drivers/scsi/initio.c175
-rw-r--r--drivers/scsi/initio.h34
-rw-r--r--drivers/scsi/ipr.c4158
-rw-r--r--drivers/scsi/ipr.h339
-rw-r--r--drivers/scsi/ips.c668
-rw-r--r--drivers/scsi/ips.h60
-rw-r--r--drivers/scsi/isci/Makefile1
-rw-r--r--drivers/scsi/isci/firmware/Makefile19
-rw-r--r--drivers/scsi/isci/firmware/README36
-rw-r--r--drivers/scsi/isci/firmware/create_fw.c99
-rw-r--r--drivers/scsi/isci/firmware/create_fw.h77
-rw-r--r--drivers/scsi/isci/host.c1205
-rw-r--r--drivers/scsi/isci/host.h194
-rw-r--r--drivers/scsi/isci/init.c438
-rw-r--r--drivers/scsi/isci/isci.h30
-rw-r--r--drivers/scsi/isci/phy.c486
-rw-r--r--drivers/scsi/isci/phy.h165
-rw-r--r--drivers/scsi/isci/port.c622
-rw-r--r--drivers/scsi/isci/port.h139
-rw-r--r--drivers/scsi/isci/port_config.c98
-rw-r--r--drivers/scsi/isci/probe_roms.c16
-rw-r--r--drivers/scsi/isci/probe_roms.h95
-rw-r--r--drivers/scsi/isci/registers.h173
-rw-r--r--drivers/scsi/isci/remote_device.c805
-rw-r--r--drivers/scsi/isci/remote_device.h300
-rw-r--r--drivers/scsi/isci/remote_node_context.c432
-rw-r--r--drivers/scsi/isci/remote_node_context.h142
-rw-r--r--drivers/scsi/isci/remote_node_table.c64
-rw-r--r--drivers/scsi/isci/remote_node_table.h2
-rw-r--r--drivers/scsi/isci/request.c1601
-rw-r--r--drivers/scsi/isci/request.h343
-rw-r--r--drivers/scsi/isci/sas.h4
-rw-r--r--drivers/scsi/isci/scu_completion_codes.h2
-rw-r--r--drivers/scsi/isci/scu_task_context.h57
-rw-r--r--drivers/scsi/isci/task.c1433
-rw-r--r--drivers/scsi/isci/task.h208
-rw-r--r--drivers/scsi/isci/unsolicited_frame_control.c30
-rw-r--r--drivers/scsi/isci/unsolicited_frame_control.h8
-rw-r--r--drivers/scsi/iscsi_boot_sysfs.c117
-rw-r--r--drivers/scsi/iscsi_tcp.c526
-rw-r--r--drivers/scsi/iscsi_tcp.h24
-rw-r--r--drivers/scsi/jazz_esp.c66
-rw-r--r--drivers/scsi/lasi700.c28
-rw-r--r--drivers/scsi/libfc/Makefile1
-rw-r--r--drivers/scsi/libfc/fc_disc.c181
-rw-r--r--drivers/scsi/libfc/fc_elsct.c25
-rw-r--r--drivers/scsi/libfc/fc_encode.h953
-rw-r--r--drivers/scsi/libfc/fc_exch.c812
-rw-r--r--drivers/scsi/libfc/fc_fcp.c514
-rw-r--r--drivers/scsi/libfc/fc_frame.c16
-rw-r--r--drivers/scsi/libfc/fc_libfc.c33
-rw-r--r--drivers/scsi/libfc/fc_libfc.h54
-rw-r--r--drivers/scsi/libfc/fc_lport.c774
-rw-r--r--drivers/scsi/libfc/fc_npiv.c17
-rw-r--r--drivers/scsi/libfc/fc_rport.c818
-rw-r--r--drivers/scsi/libiscsi.c1752
-rw-r--r--drivers/scsi/libiscsi_tcp.c320
-rw-r--r--drivers/scsi/libsas/Kconfig19
-rw-r--r--drivers/scsi/libsas/Makefile20
-rw-r--r--drivers/scsi/libsas/sas_ata.c1342
-rw-r--r--drivers/scsi/libsas/sas_discover.c503
-rw-r--r--drivers/scsi/libsas/sas_dump.c72
-rw-r--r--drivers/scsi/libsas/sas_dump.h30
-rw-r--r--drivers/scsi/libsas/sas_event.c225
-rw-r--r--drivers/scsi/libsas/sas_expander.c1303
-rw-r--r--drivers/scsi/libsas/sas_host_smp.c222
-rw-r--r--drivers/scsi/libsas/sas_init.c610
-rw-r--r--drivers/scsi/libsas/sas_internal.h287
-rw-r--r--drivers/scsi/libsas/sas_phy.c129
-rw-r--r--drivers/scsi/libsas/sas_port.c260
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c1219
-rw-r--r--drivers/scsi/libsas/sas_task.c30
-rw-r--r--drivers/scsi/libsrp.c446
-rw-r--r--drivers/scsi/lpfc/Makefile17
-rw-r--r--drivers/scsi/lpfc/lpfc.h1254
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c5836
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.h128
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c3478
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.h148
-rw-r--r--drivers/scsi/lpfc/lpfc_compat.h9
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h382
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c3576
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c4961
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h581
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h167
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c9173
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c3878
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h1188
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h2551
-rw-r--r--drivers/scsi/lpfc/lpfc_ids.h156
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c11550
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h58
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c736
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c412
-rw-r--r--drivers/scsi/lpfc/lpfc_nl.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c1898
-rw-r--r--drivers/scsi/lpfc/lpfc_nvme.c2887
-rw-r--r--drivers/scsi/lpfc/lpfc_nvme.h253
-rw-r--r--drivers/scsi/lpfc/lpfc_nvmet.c3656
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c6021
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h104
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c14844
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h296
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h779
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h17
-rw-r--r--drivers/scsi/lpfc/lpfc_vmid.c325
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c532
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.h9
-rw-r--r--drivers/scsi/mac53c94.c91
-rw-r--r--drivers/scsi/mac53c94.h12
-rw-r--r--drivers/scsi/mac_esp.c294
-rw-r--r--drivers/scsi/mac_scsi.c964
-rw-r--r--drivers/scsi/mac_scsi.h83
-rw-r--r--drivers/scsi/megaraid.c1982
-rw-r--r--drivers/scsi/megaraid.h107
-rw-r--r--drivers/scsi/megaraid/Kconfig.megaraid121
-rw-r--r--drivers/scsi/megaraid/Makefile3
-rw-r--r--drivers/scsi/megaraid/mbox_defs.h9
-rw-r--r--drivers/scsi/megaraid/mega_common.h10
-rw-r--r--drivers/scsi/megaraid/megaraid_ioctl.h12
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c350
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.h10
-rw-r--r--drivers/scsi/megaraid/megaraid_mm.c133
-rw-r--r--drivers/scsi/megaraid/megaraid_mm.h8
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h1744
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c7069
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_debugfs.c179
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fp.c1312
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c5063
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.h1088
-rw-r--r--drivers/scsi/mesh.c104
-rw-r--r--drivers/scsi/mesh.h12
-rw-r--r--drivers/scsi/mpi3mr/Kconfig9
-rw-r--r--drivers/scsi/mpi3mr/Makefile6
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h2566
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_image.h280
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_init.h147
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_ioc.h1092
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_pci.h20
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_sas.h47
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_tool.h45
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_transport.h490
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr.h1605
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_app.c3462
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_debug.h197
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_fw.c6431
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_os.c6048
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_transport.c3354
-rw-r--r--drivers/scsi/mpt2sas/Makefile7
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2.h1134
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h2803
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_init.h448
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_ioc.h1530
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_raid.h303
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_sas.h288
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_tool.h432
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c4192
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h1038
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_config.c1434
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c3028
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c7807
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c2027
-rw-r--r--drivers/scsi/mpt3sas/Kconfig (renamed from drivers/scsi/mpt2sas/Kconfig)54
-rw-r--r--drivers/scsi/mpt3sas/Makefile11
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2.h1307
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h4013
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_image.h502
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_init.h591
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_ioc.h1856
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_pci.h113
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_raid.h356
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_sas.h304
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_tool.h565
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_type.h (renamed from drivers/scsi/mpt2sas/mpi/mpi2_type.h)27
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c9044
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.h2064
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_config.c2715
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_ctl.c4522
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_ctl.h (renamed from drivers/scsi/mpt2sas/mpt2sas_ctl.h)288
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_debug.h (renamed from drivers/scsi/mpt2sas/mpt2sas_debug.h)95
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_debugfs.c157
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c12909
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_transport.c2194
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c473
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h194
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h94
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_warpdrive.c298
-rw-r--r--drivers/scsi/mvme147.c135
-rw-r--r--drivers/scsi/mvme147.h1
-rw-r--r--drivers/scsi/mvme16x_scsi.c12
-rw-r--r--drivers/scsi/mvsas/Kconfig31
-rw-r--r--drivers/scsi/mvsas/Makefile17
-rw-r--r--drivers/scsi/mvsas/mv_64xx.c147
-rw-r--r--drivers/scsi/mvsas/mv_64xx.h22
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c735
-rw-r--r--drivers/scsi/mvsas/mv_94xx.h260
-rw-r--r--drivers/scsi/mvsas/mv_chips.h37
-rw-r--r--drivers/scsi/mvsas/mv_defs.h41
-rw-r--r--drivers/scsi/mvsas/mv_init.c378
-rw-r--r--drivers/scsi/mvsas/mv_sas.c1047
-rw-r--r--drivers/scsi/mvsas/mv_sas.h171
-rw-r--r--drivers/scsi/mvumi.c2633
-rw-r--r--drivers/scsi/mvumi.h566
-rw-r--r--drivers/scsi/myrb.c3560
-rw-r--r--drivers/scsi/myrb.h957
-rw-r--r--drivers/scsi/myrs.c3168
-rw-r--r--drivers/scsi/myrs.h1133
-rw-r--r--drivers/scsi/ncr53c8xx.c401
-rw-r--r--drivers/scsi/ncr53c8xx.h40
-rw-r--r--drivers/scsi/nsp32.c614
-rw-r--r--drivers/scsi/nsp32.h20
-rw-r--r--drivers/scsi/nsp32_debug.c2
-rw-r--r--drivers/scsi/osd/Kbuild20
-rw-r--r--drivers/scsi/osd/Kconfig53
-rw-r--r--drivers/scsi/osd/osd_debug.h30
-rw-r--r--drivers/scsi/osd/osd_initiator.c2066
-rw-r--r--drivers/scsi/osd/osd_uld.c571
-rw-r--r--drivers/scsi/osst.c6096
-rw-r--r--drivers/scsi/osst.h650
-rw-r--r--drivers/scsi/osst_detect.h6
-rw-r--r--drivers/scsi/osst_options.h106
-rw-r--r--drivers/scsi/pas16.c638
-rw-r--r--drivers/scsi/pas16.h175
-rw-r--r--drivers/scsi/pcmcia/Kconfig10
-rw-r--r--drivers/scsi/pcmcia/Makefile4
-rw-r--r--drivers/scsi/pcmcia/aha152x_core.c2
-rw-r--r--drivers/scsi/pcmcia/aha152x_stub.c24
-rw-r--r--drivers/scsi/pcmcia/fdomain_core.c2
-rw-r--r--drivers/scsi/pcmcia/fdomain_cs.c97
-rw-r--r--drivers/scsi/pcmcia/fdomain_stub.c209
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c389
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.h32
-rw-r--r--drivers/scsi/pcmcia/nsp_debug.c2
-rw-r--r--drivers/scsi/pcmcia/qlogic_stub.c35
-rw-r--r--drivers/scsi/pcmcia/sym53c500_cs.c94
-rw-r--r--drivers/scsi/pm8001/Makefile13
-rw-r--r--drivers/scsi/pm8001/pm8001_chips.h4
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.c812
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.h11
-rw-r--r--drivers/scsi/pm8001/pm8001_defs.h66
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c3254
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.h23
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c1420
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c1400
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.h482
-rw-r--r--drivers/scsi/pm8001/pm80xx_hwi.c4999
-rw-r--r--drivers/scsi/pm8001/pm80xx_hwi.h1667
-rw-r--r--drivers/scsi/pm8001/pm80xx_tracepoints.c10
-rw-r--r--drivers/scsi/pm8001/pm80xx_tracepoints.h113
-rw-r--r--drivers/scsi/pmcraid.c1490
-rw-r--r--drivers/scsi/pmcraid.h77
-rw-r--r--drivers/scsi/ppa.c283
-rw-r--r--drivers/scsi/ppa.h5
-rw-r--r--drivers/scsi/ps3rom.c51
-rw-r--r--drivers/scsi/qedf/Kconfig12
-rw-r--r--drivers/scsi/qedf/Makefile6
-rw-r--r--drivers/scsi/qedf/drv_fcoe_fw_funcs.c196
-rw-r--r--drivers/scsi/qedf/drv_fcoe_fw_funcs.h90
-rw-r--r--drivers/scsi/qedf/drv_scsi_fw_funcs.c41
-rw-r--r--drivers/scsi/qedf/drv_scsi_fw_funcs.h82
-rw-r--r--drivers/scsi/qedf/qedf.h605
-rw-r--r--drivers/scsi/qedf/qedf_attr.c186
-rw-r--r--drivers/scsi/qedf/qedf_dbg.c175
-rw-r--r--drivers/scsi/qedf/qedf_dbg.h158
-rw-r--r--drivers/scsi/qedf/qedf_debugfs.c494
-rw-r--r--drivers/scsi/qedf/qedf_els.c1066
-rw-r--r--drivers/scsi/qedf/qedf_fip.c301
-rw-r--r--drivers/scsi/qedf/qedf_hsi.h351
-rw-r--r--drivers/scsi/qedf/qedf_io.c2595
-rw-r--r--drivers/scsi/qedf/qedf_main.c4237
-rw-r--r--drivers/scsi/qedf/qedf_version.h12
-rw-r--r--drivers/scsi/qedi/Kconfig13
-rw-r--r--drivers/scsi/qedi/Makefile6
-rw-r--r--drivers/scsi/qedi/qedi.h393
-rw-r--r--drivers/scsi/qedi/qedi_dbg.c105
-rw-r--r--drivers/scsi/qedi/qedi_dbg.h126
-rw-r--r--drivers/scsi/qedi/qedi_debugfs.c224
-rw-r--r--drivers/scsi/qedi/qedi_fw.c2158
-rw-r--r--drivers/scsi/qedi/qedi_fw_api.c802
-rw-r--r--drivers/scsi/qedi/qedi_fw_iscsi.h114
-rw-r--r--drivers/scsi/qedi/qedi_fw_scsi.h52
-rw-r--r--drivers/scsi/qedi/qedi_gbl.h73
-rw-r--r--drivers/scsi/qedi/qedi_hsi.h49
-rw-r--r--drivers/scsi/qedi/qedi_iscsi.c1710
-rw-r--r--drivers/scsi/qedi/qedi_iscsi.h239
-rw-r--r--drivers/scsi/qedi/qedi_main.c2957
-rw-r--r--drivers/scsi/qedi/qedi_nvm_iscsi_cfg.h207
-rw-r--r--drivers/scsi/qedi/qedi_sysfs.c58
-rw-r--r--drivers/scsi/qedi/qedi_version.h11
-rw-r--r--drivers/scsi/qla1280.c392
-rw-r--r--drivers/scsi/qla1280.h32
-rw-r--r--drivers/scsi/qla2xxx/Kconfig59
-rw-r--r--drivers/scsi/qla2xxx/Makefile5
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c2475
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c2826
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.h187
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c2120
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h674
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h3567
-rw-r--r--drivers/scsi/qla2xxx/qla_devtbl.h1
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c708
-rw-r--r--drivers/scsi/qla2xxx/qla_dsd.h32
-rw-r--r--drivers/scsi/qla2xxx/qla_edif.c3715
-rw-r--r--drivers/scsi/qla2xxx/qla_edif.h150
-rw-r--r--drivers/scsi/qla2xxx/qla_edif_bsg.h271
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h1268
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h694
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c3687
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c8183
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h611
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c3910
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c4192
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c4890
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c845
-rw-r--r--drivers/scsi/qla2xxx/qla_mr.c3405
-rw-r--r--drivers/scsi/qla2xxx/qla_mr.h529
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.c1328
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.h150
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c2866
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.h385
-rw-r--r--drivers/scsi/qla2xxx/qla_nx2.c4075
-rw-r--r--drivers/scsi/qla2xxx/qla_nx2.h579
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c7343
-rw-r--r--drivers/scsi/qla2xxx/qla_settings.h5
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c1913
-rw-r--r--drivers/scsi/qla2xxx/qla_target.c8423
-rw-r--r--drivers/scsi/qla2xxx/qla_target.h1184
-rw-r--r--drivers/scsi/qla2xxx/qla_tmpl.c1100
-rw-r--r--drivers/scsi/qla2xxx/qla_tmpl.h245
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h15
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c1961
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.h80
-rw-r--r--drivers/scsi/qla4xxx/Kconfig10
-rw-r--r--drivers/scsi/qla4xxx/Makefile3
-rw-r--r--drivers/scsi/qla4xxx/ql4_83xx.c1584
-rw-r--r--drivers/scsi/qla4xxx/ql4_83xx.h353
-rw-r--r--drivers/scsi/qla4xxx/ql4_attr.c328
-rw-r--r--drivers/scsi/qla4xxx/ql4_bsg.c872
-rw-r--r--drivers/scsi/qla4xxx/ql4_bsg.h31
-rw-r--r--drivers/scsi/qla4xxx/ql4_dbg.c42
-rw-r--r--drivers/scsi/qla4xxx/ql4_dbg.h12
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h531
-rw-r--r--drivers/scsi/qla4xxx/ql4_fw.h532
-rw-r--r--drivers/scsi/qla4xxx/ql4_glbl.h230
-rw-r--r--drivers/scsi/qla4xxx/ql4_init.c1221
-rw-r--r--drivers/scsi/qla4xxx/ql4_inline.h17
-rw-r--r--drivers/scsi/qla4xxx/ql4_iocb.c212
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c841
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c1697
-rw-r--r--drivers/scsi/qla4xxx/ql4_nvram.c26
-rw-r--r--drivers/scsi/qla4xxx/ql4_nvram.h5
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c2859
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.h273
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c8558
-rw-r--r--drivers/scsi/qla4xxx/ql4_version.h7
-rw-r--r--drivers/scsi/qlogicfas.c19
-rw-r--r--drivers/scsi/qlogicfas408.c163
-rw-r--r--drivers/scsi/qlogicfas408.h5
-rw-r--r--drivers/scsi/qlogicpti.c119
-rw-r--r--drivers/scsi/qlogicpti.h8
-rw-r--r--drivers/scsi/raid_class.c55
-rw-r--r--drivers/scsi/script_asm.pl15
-rw-r--r--drivers/scsi/scsi.c1314
-rw-r--r--drivers/scsi/scsi.h48
-rw-r--r--drivers/scsi/scsi_bsg.c103
-rw-r--r--drivers/scsi/scsi_common.c401
-rw-r--r--drivers/scsi/scsi_debug.c10187
-rw-r--r--drivers/scsi/scsi_debugfs.c75
-rw-r--r--drivers/scsi/scsi_debugfs.h5
-rw-r--r--drivers/scsi/scsi_devinfo.c393
-rw-r--r--drivers/scsi/scsi_dh.c373
-rw-r--r--drivers/scsi/scsi_error.c1655
-rw-r--r--drivers/scsi/scsi_ioctl.c917
-rw-r--r--drivers/scsi/scsi_lib.c3741
-rw-r--r--drivers/scsi/scsi_lib_dma.c2
-rw-r--r--drivers/scsi/scsi_lib_test.c337
-rw-r--r--drivers/scsi/scsi_logging.c438
-rw-r--r--drivers/scsi/scsi_logging.h12
-rw-r--r--drivers/scsi/scsi_module.c73
-rw-r--r--drivers/scsi/scsi_netlink.c587
-rw-r--r--drivers/scsi/scsi_pm.c196
-rw-r--r--drivers/scsi/scsi_priv.h119
-rw-r--r--drivers/scsi/scsi_proc.c286
-rw-r--r--drivers/scsi/scsi_proto_test.c56
-rw-r--r--drivers/scsi/scsi_sas_internal.h3
-rw-r--r--drivers/scsi/scsi_scan.c1088
-rw-r--r--drivers/scsi/scsi_sysctl.c25
-rw-r--r--drivers/scsi/scsi_sysfs.c1121
-rw-r--r--drivers/scsi/scsi_tgt_if.c398
-rw-r--r--drivers/scsi/scsi_tgt_lib.c660
-rw-r--r--drivers/scsi/scsi_tgt_priv.h32
-rw-r--r--drivers/scsi/scsi_trace.c353
-rw-r--r--drivers/scsi/scsi_transport_api.h1
-rw-r--r--drivers/scsi/scsi_transport_fc.c1508
-rw-r--r--drivers/scsi/scsi_transport_fc_internal.h26
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c3741
-rw-r--r--drivers/scsi/scsi_transport_sas.c362
-rw-r--r--drivers/scsi/scsi_transport_spi.c189
-rw-r--r--drivers/scsi/scsi_transport_srp.c677
-rw-r--r--drivers/scsi/scsi_transport_srp_internal.h25
-rw-r--r--drivers/scsi/scsi_typedefs.h2
-rw-r--r--drivers/scsi/scsi_wait_scan.c42
-rw-r--r--drivers/scsi/scsicam.c199
-rw-r--r--drivers/scsi/sd.c3970
-rw-r--r--drivers/scsi/sd.h232
-rw-r--r--drivers/scsi/sd_dif.c456
-rw-r--r--drivers/scsi/sd_trace.h84
-rw-r--r--drivers/scsi/sd_zbc.c641
-rw-r--r--drivers/scsi/sense_codes.h879
-rw-r--r--drivers/scsi/ses.c446
-rw-r--r--drivers/scsi/sg.c1934
-rw-r--r--drivers/scsi/sgiwd93.c63
-rw-r--r--drivers/scsi/sim710.c169
-rw-r--r--drivers/scsi/smartpqi/Kconfig56
-rw-r--r--drivers/scsi/smartpqi/Makefile3
-rw-r--r--drivers/scsi/smartpqi/smartpqi.h1718
-rw-r--r--drivers/scsi/smartpqi/smartpqi_init.c11217
-rw-r--r--drivers/scsi/smartpqi/smartpqi_sas_transport.c574
-rw-r--r--drivers/scsi/smartpqi/smartpqi_sis.c562
-rw-r--r--drivers/scsi/smartpqi/smartpqi_sis.h40
-rw-r--r--drivers/scsi/sni_53c710.c52
-rw-r--r--drivers/scsi/snic/Makefile18
-rw-r--r--drivers/scsi/snic/cq_desc.h63
-rw-r--r--drivers/scsi/snic/cq_enet_desc.h24
-rw-r--r--drivers/scsi/snic/snic.h402
-rw-r--r--drivers/scsi/snic/snic_attrs.c71
-rw-r--r--drivers/scsi/snic/snic_ctl.c257
-rw-r--r--drivers/scsi/snic/snic_debugfs.c440
-rw-r--r--drivers/scsi/snic/snic_disc.c554
-rw-r--r--drivers/scsi/snic/snic_disc.h110
-rw-r--r--drivers/scsi/snic/snic_fwint.h513
-rw-r--r--drivers/scsi/snic/snic_io.c555
-rw-r--r--drivers/scsi/snic/snic_io.h104
-rw-r--r--drivers/scsi/snic/snic_isr.c180
-rw-r--r--drivers/scsi/snic/snic_main.c998
-rw-r--r--drivers/scsi/snic/snic_res.c281
-rw-r--r--drivers/scsi/snic/snic_res.h83
-rw-r--r--drivers/scsi/snic/snic_scsi.c2637
-rw-r--r--drivers/scsi/snic/snic_stats.h115
-rw-r--r--drivers/scsi/snic/snic_trc.c155
-rw-r--r--drivers/scsi/snic/snic_trc.h104
-rw-r--r--drivers/scsi/snic/vnic_cq.c66
-rw-r--r--drivers/scsi/snic/vnic_cq.h96
-rw-r--r--drivers/scsi/snic/vnic_cq_fw.h48
-rw-r--r--drivers/scsi/snic/vnic_dev.c749
-rw-r--r--drivers/scsi/snic/vnic_dev.h96
-rw-r--r--drivers/scsi/snic/vnic_devcmd.h256
-rw-r--r--drivers/scsi/snic/vnic_intr.c45
-rw-r--r--drivers/scsi/snic/vnic_intr.h91
-rw-r--r--drivers/scsi/snic/vnic_resource.h54
-rw-r--r--drivers/scsi/snic/vnic_snic.h40
-rw-r--r--drivers/scsi/snic/vnic_stats.h54
-rw-r--r--drivers/scsi/snic/vnic_wq.c223
-rw-r--r--drivers/scsi/snic/vnic_wq.h156
-rw-r--r--drivers/scsi/snic/wq_enet_desc.h82
-rw-r--r--drivers/scsi/sr.c629
-rw-r--r--drivers/scsi/sr.h22
-rw-r--r--drivers/scsi/sr_ioctl.c109
-rw-r--r--drivers/scsi/sr_vendor.c67
-rw-r--r--drivers/scsi/st.c2008
-rw-r--r--drivers/scsi/st.h45
-rw-r--r--drivers/scsi/st_options.h1
-rw-r--r--drivers/scsi/stex.c573
-rw-r--r--drivers/scsi/storvsc_drv.c2245
-rw-r--r--drivers/scsi/sun3_NCR5380.c3012
-rw-r--r--drivers/scsi/sun3_scsi.c826
-rw-r--r--drivers/scsi/sun3_scsi.h379
-rw-r--r--drivers/scsi/sun3_scsi_vme.c584
-rw-r--r--drivers/scsi/sun3x_esp.c70
-rw-r--r--drivers/scsi/sun_esp.c132
-rw-r--r--drivers/scsi/sym53c416.c846
-rw-r--r--drivers/scsi/sym53c416.h33
-rw-r--r--drivers/scsi/sym53c8xx_2/Makefile1
-rw-r--r--drivers/scsi/sym53c8xx_2/sym53c8xx.h15
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_defs.h15
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw.c27
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw.h15
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw1.h15
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw2.h15
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c424
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.h19
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.c67
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.h17
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_malloc.c15
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_misc.h15
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_nvram.c22
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_nvram.h15
-rw-r--r--drivers/scsi/t128.c447
-rw-r--r--drivers/scsi/t128.h152
-rw-r--r--drivers/scsi/tmscsim.c2622
-rw-r--r--drivers/scsi/tmscsim.h550
-rw-r--r--drivers/scsi/u14-34f.c1972
-rw-r--r--drivers/scsi/ultrastor.c1211
-rw-r--r--drivers/scsi/ultrastor.h80
-rw-r--r--drivers/scsi/virtio_scsi.c1101
-rw-r--r--drivers/scsi/vmw_pvscsi.c557
-rw-r--r--drivers/scsi/vmw_pvscsi.h136
-rw-r--r--drivers/scsi/wd33c93.c461
-rw-r--r--drivers/scsi/wd33c93.h24
-rw-r--r--drivers/scsi/wd7000.c1677
-rw-r--r--drivers/scsi/wd719x.c995
-rw-r--r--drivers/scsi/wd719x.h248
-rw-r--r--drivers/scsi/xen-scsifront.c1238
-rw-r--r--drivers/scsi/zalon.c16
-rw-r--r--drivers/scsi/zorro7xx.c17
-rw-r--r--drivers/scsi/zorro_esp.c960
942 files changed, 541231 insertions, 225498 deletions
diff --git a/drivers/scsi/.gitignore b/drivers/scsi/.gitignore
index c89ae9a04399..5f65cb75f534 100644
--- a/drivers/scsi/.gitignore
+++ b/drivers/scsi/.gitignore
@@ -1 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
53c700_d.h
+scsi_devinfo_tbl.c
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c
index b7bd5b0cc7aa..a377a6f6900a 100644
--- a/drivers/scsi/3w-9xxx.c
+++ b/drivers/scsi/3w-9xxx.c
@@ -1,8 +1,8 @@
/*
3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.
- Written By: Adam Radford <linuxraid@lsi.com>
- Modifications By: Tom Couch <linuxraid@lsi.com>
+ Written By: Adam Radford <aradford@gmail.com>
+ Modifications By: Tom Couch
Copyright (C) 2004-2009 Applied Micro Circuits Corporation.
Copyright (C) 2010 LSI Corporation.
@@ -41,10 +41,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Bugs/Comments/Suggestions should be mailed to:
- linuxraid@lsi.com
-
- For more information, goto:
- http://www.lsi.com
+ aradford@gmail.com
Note: This version of the driver does not contain a bundled firmware
image.
@@ -95,7 +92,7 @@
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
@@ -131,14 +128,14 @@ static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_
static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
- u32 set_features, unsigned short current_fw_srl,
- unsigned short current_fw_arch_id,
- unsigned short current_fw_branch,
- unsigned short current_fw_build,
- unsigned short *fw_on_ctlr_srl,
- unsigned short *fw_on_ctlr_arch_id,
- unsigned short *fw_on_ctlr_branch,
- unsigned short *fw_on_ctlr_build,
+ u32 set_features, unsigned short current_fw_srl,
+ unsigned short current_fw_arch_id,
+ unsigned short current_fw_branch,
+ unsigned short current_fw_build,
+ unsigned short *fw_on_ctlr_srl,
+ unsigned short *fw_on_ctlr_arch_id,
+ unsigned short *fw_on_ctlr_branch,
+ unsigned short *fw_on_ctlr_build,
u32 *init_connect_result);
static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
@@ -146,10 +143,11 @@ static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int secon
static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
static int twa_reset_device_extension(TW_Device_Extension *tw_dev);
static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
-static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg);
+static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
+ unsigned char *cdb, int use_sg,
+ TW_SG_Entry *sglistarg);
static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
-static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);
/* Functions */
@@ -163,60 +161,49 @@ static ssize_t twa_show_stats(struct device *dev,
ssize_t len;
spin_lock_irqsave(tw_dev->host->host_lock, flags);
- len = snprintf(buf, PAGE_SIZE, "3w-9xxx Driver version: %s\n"
- "Current commands posted: %4d\n"
- "Max commands posted: %4d\n"
- "Current pending commands: %4d\n"
- "Max pending commands: %4d\n"
- "Last sgl length: %4d\n"
- "Max sgl length: %4d\n"
- "Last sector count: %4d\n"
- "Max sector count: %4d\n"
- "SCSI Host Resets: %4d\n"
- "AEN's: %4d\n",
- TW_DRIVER_VERSION,
- tw_dev->posted_request_count,
- tw_dev->max_posted_request_count,
- tw_dev->pending_request_count,
- tw_dev->max_pending_request_count,
- tw_dev->sgl_entries,
- tw_dev->max_sgl_entries,
- tw_dev->sector_count,
- tw_dev->max_sector_count,
- tw_dev->num_resets,
- tw_dev->aen_count);
+ len = sysfs_emit(buf, "3w-9xxx Driver version: %s\n"
+ "Current commands posted: %4d\n"
+ "Max commands posted: %4d\n"
+ "Current pending commands: %4d\n"
+ "Max pending commands: %4d\n"
+ "Last sgl length: %4d\n"
+ "Max sgl length: %4d\n"
+ "Last sector count: %4d\n"
+ "Max sector count: %4d\n"
+ "SCSI Host Resets: %4d\n"
+ "AEN's: %4d\n",
+ TW_DRIVER_VERSION,
+ tw_dev->posted_request_count,
+ tw_dev->max_posted_request_count,
+ tw_dev->pending_request_count,
+ tw_dev->max_pending_request_count,
+ tw_dev->sgl_entries,
+ tw_dev->max_sgl_entries,
+ tw_dev->sector_count,
+ tw_dev->max_sector_count,
+ tw_dev->num_resets,
+ tw_dev->aen_count);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
return len;
} /* End twa_show_stats() */
-/* This function will set a devices queue depth */
-static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (queue_depth > TW_Q_LENGTH-2)
- queue_depth = TW_Q_LENGTH-2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
-} /* End twa_change_queue_depth() */
-
/* Create sysfs 'stats' entry */
static struct device_attribute twa_host_stats_attr = {
.attr = {
- .name = "stats",
+ .name = "stats",
.mode = S_IRUGO,
},
.show = twa_show_stats
};
/* Host attributes initializer */
-static struct device_attribute *twa_host_attrs[] = {
- &twa_host_stats_attr,
+static struct attribute *twa_host_attrs[] = {
+ &twa_host_stats_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(twa_host);
+
/* File operations struct for character device */
static const struct file_operations twa_fops = {
.owner = THIS_MODULE,
@@ -226,6 +213,17 @@ static const struct file_operations twa_fops = {
.llseek = noop_llseek,
};
+/*
+ * The controllers use an inline buffer instead of a mapped SGL for small,
+ * single entry buffers. Note that we treat a zero-length transfer like
+ * a mapped SGL.
+ */
+static bool twa_command_mapped(struct scsi_cmnd *cmd)
+{
+ return scsi_sg_count(cmd) != 1 ||
+ scsi_bufflen(cmd) >= TW_MIN_SGL_LENGTH;
+}
+
/* This function will complete an aen request from the isr */
static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id)
{
@@ -246,7 +244,7 @@ static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id)
/* Keep reading the queue in case there are more aen's */
if (twa_aen_read_queue(tw_dev, request_id))
goto out2;
- else {
+ else {
retval = 0;
goto out;
}
@@ -284,7 +282,7 @@ out:
static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
{
int request_id = 0;
- char cdb[TW_MAX_CDB_LEN];
+ unsigned char cdb[TW_MAX_CDB_LEN];
TW_SG_Entry sglist[1];
int finished = 0, count = 0;
TW_Command_Full *full_command_packet;
@@ -307,10 +305,10 @@ static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
/* Initialize sglist */
memset(&sglist, 0, sizeof(TW_SG_Entry));
- sglist[0].length = TW_SECTOR_SIZE;
- sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+ sglist[0].length = cpu_to_le32(TW_SECTOR_SIZE);
+ sglist[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
- if (sglist[0].address & TW_ALIGNMENT_9000_SGL) {
+ if (tw_dev->generic_buffer_phys[request_id] & TW_ALIGNMENT_9000_SGL) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Found unaligned address during AEN drain");
goto out;
}
@@ -375,7 +373,6 @@ out:
static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
{
u32 local_time;
- struct timeval time;
TW_Event *event;
unsigned short aen;
char host[16];
@@ -398,8 +395,8 @@ static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_H
memset(event, 0, sizeof(TW_Event));
event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
- do_gettimeofday(&time);
- local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
+ /* event->time_stamp_sec overflows in y2106 */
+ local_time = (u32)(ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60));
event->time_stamp_sec = local_time;
event->aen_code = aen;
event->retrieved = TW_AEN_NOT_RETRIEVED;
@@ -430,7 +427,7 @@ static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_H
/* This function will read the aen queue from the isr */
static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{
- char cdb[TW_MAX_CDB_LEN];
+ unsigned char cdb[TW_MAX_CDB_LEN];
TW_SG_Entry sglist[1];
TW_Command_Full *full_command_packet;
int retval = 1;
@@ -445,8 +442,8 @@ static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
/* Initialize sglist */
memset(&sglist, 0, sizeof(TW_SG_Entry));
- sglist[0].length = TW_SECTOR_SIZE;
- sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+ sglist[0].length = cpu_to_le32(TW_SECTOR_SIZE);
+ sglist[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
@@ -479,11 +476,10 @@ out:
static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
{
u32 schedulertime;
- struct timeval utc;
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param;
- u32 local_time;
+ time64_t local_time;
/* Fill out the command packet */
full_command_packet = tw_dev->command_packet_virt[request_id];
@@ -503,14 +499,12 @@ static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */
param->parameter_size_bytes = cpu_to_le16(4);
- /* Convert system time in UTC to local time seconds since last
+ /* Convert system time in UTC to local time seconds since last
Sunday 12:00AM */
- do_gettimeofday(&utc);
- local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
- schedulertime = local_time - (3 * 86400);
- schedulertime = cpu_to_le32(schedulertime % 604800);
+ local_time = (ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60));
+ div_u64_rem(local_time - (3 * 86400), 604800, &schedulertime);
- memcpy(param->data, &schedulertime, sizeof(u32));
+ memcpy(param->data, &(__le32){cpu_to_le32(schedulertime)}, sizeof(__le32));
/* Mark internal command */
tw_dev->srb[request_id] = NULL;
@@ -527,7 +521,8 @@ static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
unsigned long *cpu_addr;
int retval = 1;
- cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev,
+ size * TW_Q_LENGTH, &dma_handle, GFP_KERNEL);
if (!cpu_addr) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
goto out;
@@ -535,7 +530,8 @@ static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
if ((unsigned long)cpu_addr % (TW_ALIGNMENT_9000)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x6, "Failed to allocate correctly aligned memory");
- pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, size * TW_Q_LENGTH,
+ cpu_addr, dma_handle);
goto out;
}
@@ -621,7 +617,8 @@ static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
}
/* Load rest of compatibility struct */
- strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
+ strscpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION,
+ sizeof(tw_dev->tw_compat_info.driver_version));
tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL;
tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD;
@@ -640,7 +637,7 @@ out:
/* This function handles ioctl for the character device */
static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
long timeout;
unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
dma_addr_t dma_handle;
@@ -653,8 +650,7 @@ static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
TW_Command_Full *full_command_packet;
TW_Compatibility_Info *tw_compat_info;
TW_Event *event;
- struct timeval current_time;
- u32 current_time_ms;
+ ktime_t current_time;
TW_Device_Extension *tw_dev = twa_device_extension_list[iminor(inode)];
int retval = TW_IOCTL_ERROR_OS_EFAULT;
void __user *argp = (void __user *)arg;
@@ -681,7 +677,9 @@ static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;
/* Now allocate ioctl buf memory */
- cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle, GFP_KERNEL);
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev,
+ sizeof(TW_Ioctl_Buf_Apache) + data_buffer_length_adjusted,
+ &dma_handle, GFP_KERNEL);
if (!cpu_addr) {
retval = TW_IOCTL_ERROR_OS_ENOMEM;
goto out2;
@@ -690,7 +688,7 @@ static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
/* Now copy down the entire ioctl */
- if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+ if (copy_from_user(tw_ioctl, argp, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length))
goto out3;
/* See which ioctl we are doing */
@@ -734,7 +732,7 @@ static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
/* Now copy in the command packet response */
memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
-
+
/* Now complete the io */
spin_lock_irqsave(tw_dev->host->host_lock, flags);
tw_dev->posted_request_count--;
@@ -771,7 +769,7 @@ static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
if (tw_dev->aen_clobber) {
tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
tw_dev->aen_clobber = 0;
- } else
+ } else
tw_ioctl->driver_command.status = 0;
event_index = tw_dev->error_index;
} else {
@@ -845,17 +843,17 @@ static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
break;
case TW_IOCTL_GET_LOCK:
tw_lock = (TW_Lock *)tw_ioctl->data_buffer;
- do_gettimeofday(&current_time);
- current_time_ms = (current_time.tv_sec * 1000) + (current_time.tv_usec / 1000);
+ current_time = ktime_get();
- if ((tw_lock->force_flag == 1) || (tw_dev->ioctl_sem_lock == 0) || (current_time_ms >= tw_dev->ioctl_msec)) {
+ if ((tw_lock->force_flag == 1) || (tw_dev->ioctl_sem_lock == 0) ||
+ ktime_after(current_time, tw_dev->ioctl_time)) {
tw_dev->ioctl_sem_lock = 1;
- tw_dev->ioctl_msec = current_time_ms + tw_lock->timeout_msec;
+ tw_dev->ioctl_time = ktime_add_ms(current_time, tw_lock->timeout_msec);
tw_ioctl->driver_command.status = 0;
tw_lock->time_remaining_msec = tw_lock->timeout_msec;
} else {
tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_LOCKED;
- tw_lock->time_remaining_msec = tw_dev->ioctl_msec - current_time_ms;
+ tw_lock->time_remaining_msec = ktime_ms_delta(tw_dev->ioctl_time, current_time);
}
break;
case TW_IOCTL_RELEASE_LOCK:
@@ -872,11 +870,13 @@ static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
}
/* Now copy the entire response to userspace */
- if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length) == 0)
retval = 0;
out3:
/* Now free ioctl buf memory */
- dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
+ sizeof(TW_Ioctl_Buf_Apache) + data_buffer_length_adjusted,
+ cpu_addr, dma_handle);
out2:
mutex_unlock(&tw_dev->ioctl_lock);
out:
@@ -891,6 +891,11 @@ static int twa_chrdev_open(struct inode *inode, struct file *file)
unsigned int minor_number;
int retval = TW_IOCTL_ERROR_OS_ENODEV;
+ if (!capable(CAP_SYS_ADMIN)) {
+ retval = -EACCES;
+ goto out;
+ }
+
minor_number = iminor(inode);
if (minor_number >= twa_device_extension_count)
goto out;
@@ -939,13 +944,13 @@ out:
/* This function will empty the response queue */
static int twa_empty_response_queue(TW_Device_Extension *tw_dev)
{
- u32 status_reg_value, response_que_value;
+ u32 status_reg_value;
int count = 0, retval = 1;
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) {
- response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
count++;
}
@@ -1000,19 +1005,13 @@ static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_
if (print_host)
printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n",
tw_dev->host->host_no,
- TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
- full_command_packet->header.status_block.error,
- error_str[0] == '\0' ?
- twa_string_lookup(twa_error_table,
- full_command_packet->header.status_block.error) : error_str,
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR, error,
+ error_str[0] ? error_str : twa_string_lookup(twa_error_table, error),
full_command_packet->header.err_specific_desc);
else
printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s:%s.\n",
- TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
- full_command_packet->header.status_block.error,
- error_str[0] == '\0' ?
- twa_string_lookup(twa_error_table,
- full_command_packet->header.status_block.error) : error_str,
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR, error,
+ error_str[0] ? error_str : twa_string_lookup(twa_error_table, error),
full_command_packet->header.err_specific_desc);
}
@@ -1031,16 +1030,16 @@ out:
static void twa_free_device_extension(TW_Device_Extension *tw_dev)
{
if (tw_dev->command_packet_virt[0])
- pci_free_consistent(tw_dev->tw_pci_dev,
- sizeof(TW_Command_Full)*TW_Q_LENGTH,
- tw_dev->command_packet_virt[0],
- tw_dev->command_packet_phys[0]);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
+ sizeof(TW_Command_Full) * TW_Q_LENGTH,
+ tw_dev->command_packet_virt[0],
+ tw_dev->command_packet_phys[0]);
if (tw_dev->generic_buffer_virt[0])
- pci_free_consistent(tw_dev->tw_pci_dev,
- TW_SECTOR_SIZE*TW_Q_LENGTH,
- tw_dev->generic_buffer_virt[0],
- tw_dev->generic_buffer_phys[0]);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
+ TW_SECTOR_SIZE * TW_Q_LENGTH,
+ tw_dev->generic_buffer_virt[0],
+ tw_dev->generic_buffer_phys[0]);
kfree(tw_dev->event_queue[0]);
} /* End twa_free_device_extension() */
@@ -1067,8 +1066,8 @@ static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl
command_packet = &full_command_packet->command.oldcommand;
command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
- command_packet->size = TW_COMMAND_SIZE;
- command_packet->request_id = request_id;
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->request_id = request_id;
command_packet->byte6_offset.block_count = cpu_to_le16(1);
/* Now setup the param */
@@ -1106,14 +1105,14 @@ static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
/* This function will send an initconnection command to controller */
static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
- u32 set_features, unsigned short current_fw_srl,
- unsigned short current_fw_arch_id,
- unsigned short current_fw_branch,
- unsigned short current_fw_build,
- unsigned short *fw_on_ctlr_srl,
- unsigned short *fw_on_ctlr_arch_id,
- unsigned short *fw_on_ctlr_branch,
- unsigned short *fw_on_ctlr_build,
+ u32 set_features, unsigned short current_fw_srl,
+ unsigned short current_fw_arch_id,
+ unsigned short current_fw_branch,
+ unsigned short current_fw_build,
+ unsigned short *fw_on_ctlr_srl,
+ unsigned short *fw_on_ctlr_arch_id,
+ unsigned short *fw_on_ctlr_branch,
+ unsigned short *fw_on_ctlr_build,
u32 *init_connect_result)
{
TW_Command_Full *full_command_packet;
@@ -1124,17 +1123,16 @@ static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
full_command_packet = tw_dev->command_packet_virt[request_id];
memset(full_command_packet, 0, sizeof(TW_Command_Full));
full_command_packet->header.header_desc.size_header = 128;
-
+
tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION);
tw_initconnect->request_id = request_id;
tw_initconnect->message_credits = cpu_to_le16(message_credits);
- tw_initconnect->features = set_features;
/* Turn on 64-bit sgl support if we need to */
- tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0;
+ set_features |= sizeof(dma_addr_t) > 4 ? 1 : 0;
- tw_initconnect->features = cpu_to_le32(tw_initconnect->features);
+ tw_initconnect->features = cpu_to_le32(set_features);
if (set_features & TW_EXTENDED_INIT_CONNECT) {
tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
@@ -1142,7 +1140,7 @@ static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id);
tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch);
tw_initconnect->fw_build = cpu_to_le16(current_fw_build);
- } else
+ } else
tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
/* Send command packet to the board */
@@ -1342,21 +1340,24 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance)
/* If error, command failed */
if (error == 1) {
/* Ask for a host reset */
- cmd->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ cmd->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
}
/* Report residual bytes for single sgl */
if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) {
- if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id]))
- scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length);
+ u32 length = le32_to_cpu(full_command_packet->command.newcommand.sg_list[0].length);
+
+ if (length < scsi_bufflen(cmd))
+ scsi_set_resid(cmd, scsi_bufflen(cmd) - length);
}
/* Now complete the io */
+ if (twa_command_mapped(cmd))
+ scsi_dma_unmap(cmd);
+ scsi_done(cmd);
tw_dev->state[request_id] = TW_S_COMPLETED;
twa_free_request_id(tw_dev, request_id);
tw_dev->posted_request_count--;
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
- twa_unmap_scsi_data(tw_dev, request_id);
}
/* Check for valid status after each drain */
@@ -1389,13 +1390,13 @@ static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_comm
if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {
newcommand = &full_command_packet->command.newcommand;
newcommand->request_id__lunl =
- cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id));
+ TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id);
if (length) {
- newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache));
newcommand->sg_list[0].length = cpu_to_le32(length);
}
newcommand->sgl_entries__lunh =
- cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0));
+ TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0);
} else {
oldcommand = &full_command_packet->command.oldcommand;
oldcommand->request_id = request_id;
@@ -1406,7 +1407,7 @@ static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_comm
sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry)/4) + pae);
else
sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset));
- sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache));
sgl->length = cpu_to_le32(length);
oldcommand->size += pae;
@@ -1414,26 +1415,6 @@ static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_comm
}
} /* End twa_load_sgl() */
-/* This function will perform a pci-dma mapping for a scatter gather list */
-static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
-{
- int use_sg;
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- use_sg = scsi_dma_map(cmd);
- if (!use_sg)
- return 0;
- else if (use_sg < 0) {
- TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
- return 0;
- }
-
- cmd->SCp.phase = TW_PHASE_SGLIST;
- cmd->SCp.have_data_in = use_sg;
-
- return use_sg;
-} /* End twa_map_scsi_sg_data() */
-
/* This function will poll for a response interrupt of a request */
static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
{
@@ -1474,7 +1455,7 @@ out:
/* This function will poll the status register for a flag */
static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{
- u32 status_reg_value;
+ u32 status_reg_value;
unsigned long before;
int retval = 1;
@@ -1612,9 +1593,12 @@ static int twa_reset_device_extension(TW_Device_Extension *tw_dev)
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) {
if (tw_dev->srb[i]) {
- tw_dev->srb[i]->result = (DID_RESET << 16);
- tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
- twa_unmap_scsi_data(tw_dev, i);
+ struct scsi_cmnd *cmd = tw_dev->srb[i];
+
+ cmd->result = (DID_RESET << 16);
+ if (twa_command_mapped(cmd))
+ scsi_dma_unmap(cmd);
+ scsi_done(cmd);
}
}
}
@@ -1711,12 +1695,9 @@ out:
} /* End twa_reset_sequence() */
/* This funciton returns unit geometry in cylinders/heads/sectors */
-static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
+static int twa_scsi_biosparam(struct scsi_device *sdev, struct gendisk *unused, sector_t capacity, int geom[])
{
int heads, sectors, cylinders;
- TW_Device_Extension *tw_dev;
-
- tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
if (capacity >= 0x200000) {
heads = 255;
@@ -1765,8 +1746,9 @@ out:
} /* End twa_scsi_eh_reset() */
/* This is the main scsi queue function to handle scsi opcodes */
-static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
int request_id, retval;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
@@ -1784,28 +1766,26 @@ static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_
goto out;
}
- /* Save done function into scsi_cmnd struct */
- SCpnt->scsi_done = done;
-
/* Get a free request id */
twa_get_request_id(tw_dev, &request_id);
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
- /* Initialize phase to zero */
- SCpnt->SCp.phase = TW_PHASE_INITIAL;
-
retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
switch (retval) {
case SCSI_MLQUEUE_HOST_BUSY:
+ if (twa_command_mapped(SCpnt))
+ scsi_dma_unmap(SCpnt);
twa_free_request_id(tw_dev, request_id);
break;
case 1:
- tw_dev->state[request_id] = TW_S_COMPLETED;
- twa_free_request_id(tw_dev, request_id);
SCpnt->result = (DID_ERROR << 16);
+ if (twa_command_mapped(SCpnt))
+ scsi_dma_unmap(SCpnt);
done(SCpnt);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
retval = 0;
}
out:
@@ -1815,21 +1795,20 @@ out:
static DEF_SCSI_QCMD(twa_scsi_queue)
/* This function hands scsi cdb's to the firmware */
-static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg)
+static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
+ unsigned char *cdb, int use_sg,
+ TW_SG_Entry *sglistarg)
{
TW_Command_Full *full_command_packet;
TW_Command_Apache *command_packet;
u32 num_sectors = 0x0;
int i, sg_count;
struct scsi_cmnd *srb = NULL;
- struct scatterlist *sglist = NULL, *sg;
+ struct scatterlist *sg;
int retval = 1;
- if (tw_dev->srb[request_id]) {
+ if (tw_dev->srb[request_id])
srb = tw_dev->srb[request_id];
- if (scsi_sglist(srb))
- sglist = scsi_sglist(srb);
- }
/* Initialize command packet */
full_command_packet = tw_dev->command_packet_virt[request_id];
@@ -1850,10 +1829,10 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
if (srb) {
command_packet->unit = srb->device->id;
command_packet->request_id__lunl =
- cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id));
+ TW_REQ_LUN_IN(srb->device->lun, request_id);
} else {
command_packet->request_id__lunl =
- cpu_to_le16(TW_REQ_LUN_IN(0, request_id));
+ TW_REQ_LUN_IN(0, request_id);
command_packet->unit = 0;
}
@@ -1863,8 +1842,7 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
/* Map sglist from scsi layer to cmd packet */
if (scsi_sg_count(srb)) {
- if ((scsi_sg_count(srb) == 1) &&
- (scsi_bufflen(srb) < TW_MIN_SGL_LENGTH)) {
+ if (!twa_command_mapped(srb)) {
if (srb->sc_data_direction == DMA_TO_DEVICE ||
srb->sc_data_direction == DMA_BIDIRECTIONAL)
scsi_sg_copy_to_buffer(srb,
@@ -1873,8 +1851,8 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
command_packet->sg_list[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
command_packet->sg_list[0].length = cpu_to_le32(TW_MIN_SGL_LENGTH);
} else {
- sg_count = twa_map_scsi_sg_data(tw_dev, request_id);
- if (sg_count == 0)
+ sg_count = scsi_dma_map(srb);
+ if (sg_count < 0)
goto out;
scsi_for_each_sg(srb, sg, sg_count, i) {
@@ -1886,19 +1864,19 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
}
}
}
- command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id])));
+ command_packet->sgl_entries__lunh = TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id]));
}
} else {
/* Internal cdb post */
for (i = 0; i < use_sg; i++) {
- command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address);
- command_packet->sg_list[i].length = cpu_to_le32(sglistarg[i].length);
+ command_packet->sg_list[i].address = sglistarg[i].address;
+ command_packet->sg_list[i].length = sglistarg[i].length;
if (command_packet->sg_list[i].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2f, "Found unaligned sgl address during internal post");
goto out;
}
}
- command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg));
+ command_packet->sgl_entries__lunh = TW_REQ_LUN_IN(0, use_sg);
}
if (srb) {
@@ -1937,7 +1915,7 @@ static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int re
{
struct scsi_cmnd *cmd = tw_dev->srb[request_id];
- if (scsi_bufflen(cmd) < TW_MIN_SGL_LENGTH &&
+ if (!twa_command_mapped(cmd) &&
(cmd->sc_data_direction == DMA_FROM_DEVICE ||
cmd->sc_data_direction == DMA_BIDIRECTIONAL)) {
if (scsi_sg_count(cmd) == 1) {
@@ -1989,68 +1967,59 @@ static char *twa_string_lookup(twa_message_type *table, unsigned int code)
return(table[index].text);
} /* End twa_string_lookup() */
-/* This function will perform a pci-dma unmap */
-static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
-{
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- if (cmd->SCp.phase == TW_PHASE_SGLIST)
- scsi_dma_unmap(cmd);
-} /* End twa_unmap_scsi_data() */
-
/* This function gets called when a disk is coming on-line */
-static int twa_slave_configure(struct scsi_device *sdev)
+static int twa_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
/* Force 60 second timeout */
blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
return 0;
-} /* End twa_slave_configure() */
+} /* End twa_sdev_configure() */
-/* scsi_host_template initializer */
-static struct scsi_host_template driver_template = {
+static const struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = "3ware 9000 Storage Controller",
.queuecommand = twa_scsi_queue,
.eh_host_reset_handler = twa_scsi_eh_reset,
.bios_param = twa_scsi_biosparam,
- .change_queue_depth = twa_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.can_queue = TW_Q_LENGTH-2,
- .slave_configure = twa_slave_configure,
+ .sdev_configure = twa_sdev_configure,
.this_id = -1,
.sg_tablesize = TW_APACHE_MAX_SGL_LENGTH,
.max_sectors = TW_MAX_SECTORS,
.cmd_per_lun = TW_MAX_CMDS_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = twa_host_attrs,
- .emulated = 1
+ .shost_groups = twa_host_groups,
+ .emulated = 1,
+ .no_write_same = 1,
};
/* This function will probe and initialize a card */
-static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+static int twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
{
struct Scsi_Host *host = NULL;
TW_Device_Extension *tw_dev;
unsigned long mem_addr, mem_len;
- int retval = -ENODEV;
+ int retval;
retval = pci_enable_device(pdev);
if (retval) {
TW_PRINTK(host, TW_DRIVER, 0x34, "Failed to enable pci device");
- goto out_disable_device;
+ return -ENODEV;
}
pci_set_master(pdev);
pci_try_set_mwi(pdev);
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
- TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
- retval = -ENODEV;
- goto out_disable_device;
- }
+ retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (retval)
+ retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (retval) {
+ TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
if (!host) {
@@ -2066,6 +2035,7 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
if (twa_initialize_device_extension(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
+ retval = -ENOMEM;
goto out_free_device_extension;
}
@@ -2088,6 +2058,7 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
tw_dev->base_addr = ioremap(mem_addr, mem_len);
if (!tw_dev->base_addr) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x35, "Failed to ioremap");
+ retval = -ENOMEM;
goto out_release_mem_region;
}
@@ -2095,8 +2066,10 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
TW_DISABLE_INTERRUPTS(tw_dev);
/* Initialize the card */
- if (twa_reset_sequence(tw_dev, 0))
+ if (twa_reset_sequence(tw_dev, 0)) {
+ retval = -ENOMEM;
goto out_iounmap;
+ }
/* Set host specific parameters */
if ((pdev->device == PCI_DEVICE_ID_3WARE_9650SE) ||
@@ -2128,7 +2101,7 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH),
(char *)twa_get_param(tw_dev, 1, TW_VERSION_TABLE,
TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH),
- le32_to_cpu(*(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE,
+ le32_to_cpu(*(__le32 *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE,
TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH)));
/* Try to enable MSI */
@@ -2210,10 +2183,10 @@ static void twa_remove(struct pci_dev *pdev)
twa_device_extension_count--;
} /* End twa_remove() */
-#ifdef CONFIG_PM
/* This function is called on PCI suspend */
-static int twa_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused twa_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct Scsi_Host *host = pci_get_drvdata(pdev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
@@ -2233,42 +2206,29 @@ static int twa_suspend(struct pci_dev *pdev, pm_message_t state)
}
TW_CLEAR_ALL_INTERRUPTS(tw_dev);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
return 0;
} /* End twa_suspend() */
/* This function is called on PCI resume */
-static int twa_resume(struct pci_dev *pdev)
+static int __maybe_unused twa_resume(struct device *dev)
{
int retval = 0;
+ struct pci_dev *pdev = to_pci_dev(dev);
struct Scsi_Host *host = pci_get_drvdata(pdev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
printk(KERN_WARNING "3w-9xxx: Resuming host %d.\n", tw_dev->host->host_no);
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
-
- retval = pci_enable_device(pdev);
- if (retval) {
- TW_PRINTK(tw_dev->host, TW_DRIVER, 0x39, "Enable device failed during resume");
- return retval;
- }
- pci_set_master(pdev);
pci_try_set_mwi(pdev);
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
- TW_PRINTK(host, TW_DRIVER, 0x40, "Failed to set dma mask during resume");
- retval = -ENODEV;
- goto out_disable_device;
- }
+ retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (retval)
+ retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (retval) {
+ TW_PRINTK(host, TW_DRIVER, 0x40, "Failed to set dma mask during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
/* Initialize the card */
if (twa_reset_sequence(tw_dev, 0)) {
@@ -2296,14 +2256,12 @@ static int twa_resume(struct pci_dev *pdev)
out_disable_device:
scsi_remove_host(host);
- pci_disable_device(pdev);
return retval;
} /* End twa_resume() */
-#endif
/* PCI Devices supported by this driver */
-static struct pci_device_id twa_pci_tbl[] __devinitdata = {
+static const struct pci_device_id twa_pci_tbl[] = {
{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9550SX,
@@ -2316,16 +2274,15 @@ static struct pci_device_id twa_pci_tbl[] __devinitdata = {
};
MODULE_DEVICE_TABLE(pci, twa_pci_tbl);
+static SIMPLE_DEV_PM_OPS(twa_pm_ops, twa_suspend, twa_resume);
+
/* pci_driver initializer */
static struct pci_driver twa_driver = {
.name = "3w-9xxx",
.id_table = twa_pci_tbl,
.probe = twa_probe,
.remove = twa_remove,
-#ifdef CONFIG_PM
- .suspend = twa_suspend,
- .resume = twa_resume,
-#endif
+ .driver.pm = &twa_pm_ops,
.shutdown = twa_shutdown
};
diff --git a/drivers/scsi/3w-9xxx.h b/drivers/scsi/3w-9xxx.h
index 040f7214e5b7..0b23b0422e88 100644
--- a/drivers/scsi/3w-9xxx.h
+++ b/drivers/scsi/3w-9xxx.h
@@ -1,8 +1,8 @@
/*
3w-9xxx.h -- 3ware 9000 Storage Controller device driver for Linux.
- Written By: Adam Radford <linuxraid@lsi.com>
- Modifications By: Tom Couch <linuxraid@lsi.com>
+ Written By: Adam Radford <aradford@gmail.com>
+ Modifications By: Tom Couch
Copyright (C) 2004-2009 Applied Micro Circuits Corporation.
Copyright (C) 2010 LSI Corporation.
@@ -41,10 +41,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Bugs/Comments/Suggestions should be mailed to:
- linuxraid@lsi.com
-
- For more information, goto:
- http://www.lsi.com
+ aradford@gmail.com
*/
#ifndef _3W_9XXX_H
@@ -52,8 +49,8 @@
/* AEN string type */
typedef struct TAG_twa_message_type {
- unsigned int code;
- char* text;
+ unsigned int code;
+ char *text;
} twa_message_type;
/* AEN strings */
@@ -266,9 +263,9 @@ static twa_message_type twa_error_table[] = {
#define TW_CONTROL_ENABLE_INTERRUPTS 0x00000080
#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040
#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020
-#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
-#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
-#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
+#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
+#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
+#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
/* Status register bit definitions */
#define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000
@@ -287,25 +284,25 @@ static twa_message_type twa_error_table[] = {
#define TW_STATUS_COMMAND_QUEUE_EMPTY 0x00001000
#define TW_STATUS_EXPECTED_BITS 0x00002000
#define TW_STATUS_UNEXPECTED_BITS 0x00F00000
-#define TW_STATUS_VALID_INTERRUPT 0x00DF0000
+#define TW_STATUS_VALID_INTERRUPT 0x00DF0000
/* PCI related defines */
#define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
#define TW_PCI_CLEAR_PCI_ABORT 0x2000
/* Command packet opcodes used by the driver */
-#define TW_OP_INIT_CONNECTION 0x1
-#define TW_OP_GET_PARAM 0x12
-#define TW_OP_SET_PARAM 0x13
-#define TW_OP_EXECUTE_SCSI 0x10
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_GET_PARAM 0x12
+#define TW_OP_SET_PARAM 0x13
+#define TW_OP_EXECUTE_SCSI 0x10
#define TW_OP_DOWNLOAD_FIRMWARE 0x16
-#define TW_OP_RESET 0x1C
+#define TW_OP_RESET 0x1C
/* Asynchronous Event Notification (AEN) codes used by the driver */
-#define TW_AEN_QUEUE_EMPTY 0x0000
-#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_QUEUE_EMPTY 0x0000
+#define TW_AEN_SOFT_RESET 0x0001
#define TW_AEN_SYNC_TIME_WITH_HOST 0x031
-#define TW_AEN_SEVERITY_ERROR 0x1
+#define TW_AEN_SEVERITY_ERROR 0x1
#define TW_AEN_SEVERITY_DEBUG 0x4
#define TW_AEN_NOT_RETRIEVED 0x1
#define TW_AEN_RETRIEVED 0x2
@@ -324,16 +321,11 @@ static twa_message_type twa_error_table[] = {
#define TW_CURRENT_DRIVER_BUILD 0
#define TW_CURRENT_DRIVER_BRANCH 0
-/* Phase defines */
-#define TW_PHASE_INITIAL 0
-#define TW_PHASE_SINGLE 1
-#define TW_PHASE_SGLIST 2
-
/* Misc defines */
#define TW_9550SX_DRAIN_COMPLETED 0xFFFF
-#define TW_SECTOR_SIZE 512
-#define TW_ALIGNMENT_9000 4 /* 4 bytes */
-#define TW_ALIGNMENT_9000_SGL 0x3
+#define TW_SECTOR_SIZE 512
+#define TW_ALIGNMENT_9000 4 /* 4 bytes */
+#define TW_ALIGNMENT_9000_SGL 0x3
#define TW_MAX_UNITS 16
#define TW_MAX_UNITS_9650SE 32
#define TW_INIT_MESSAGE_CREDITS 0x100
@@ -346,7 +338,7 @@ static twa_message_type twa_error_table[] = {
#define TW_BASE_FW_SRL 24
#define TW_BASE_FW_BRANCH 0
#define TW_BASE_FW_BUILD 1
-#define TW_FW_SRL_LUNS_SUPPORTED 28
+#define TW_FW_SRL_LUNS_SUPPORTED 28
#define TW_Q_LENGTH 256
#define TW_Q_START 0
#define TW_MAX_SLOT 32
@@ -354,19 +346,19 @@ static twa_message_type twa_error_table[] = {
#define TW_MAX_CMDS_PER_LUN 254
#define TW_MAX_RESPONSE_DRAIN 256
#define TW_MAX_AEN_DRAIN 255
-#define TW_IN_RESET 2
+#define TW_IN_RESET 2
#define TW_USING_MSI 3
#define TW_IN_ATTENTION_LOOP 4
-#define TW_MAX_SECTORS 256
-#define TW_AEN_WAIT_TIME 1000
-#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
-#define TW_MAX_CDB_LEN 16
-#define TW_ISR_DONT_COMPLETE 2
-#define TW_ISR_DONT_RESULT 3
-#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
-#define TW_IOCTL_CHRDEV_FREE -1
-#define TW_COMMAND_OFFSET 128 /* 128 bytes */
-#define TW_VERSION_TABLE 0x0402
+#define TW_MAX_SECTORS 256
+#define TW_AEN_WAIT_TIME 1000
+#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
+#define TW_MAX_CDB_LEN 16
+#define TW_ISR_DONT_COMPLETE 2
+#define TW_ISR_DONT_RESULT 3
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
+#define TW_COMMAND_OFFSET 128 /* 128 bytes */
+#define TW_VERSION_TABLE 0x0402
#define TW_TIMEKEEP_TABLE 0x040A
#define TW_INFORMATION_TABLE 0x0403
#define TW_PARAM_FWVER 3
@@ -375,22 +367,22 @@ static twa_message_type twa_error_table[] = {
#define TW_PARAM_BIOSVER_LENGTH 16
#define TW_PARAM_PORTCOUNT 3
#define TW_PARAM_PORTCOUNT_LENGTH 1
-#define TW_MIN_SGL_LENGTH 0x200 /* 512 bytes */
-#define TW_MAX_SENSE_LENGTH 256
-#define TW_EVENT_SOURCE_AEN 0x1000
-#define TW_EVENT_SOURCE_COMMAND 0x1001
-#define TW_EVENT_SOURCE_PCHIP 0x1002
-#define TW_EVENT_SOURCE_DRIVER 0x1003
+#define TW_MIN_SGL_LENGTH 0x200 /* 512 bytes */
+#define TW_MAX_SENSE_LENGTH 256
+#define TW_EVENT_SOURCE_AEN 0x1000
+#define TW_EVENT_SOURCE_COMMAND 0x1001
+#define TW_EVENT_SOURCE_PCHIP 0x1002
+#define TW_EVENT_SOURCE_DRIVER 0x1003
#define TW_IOCTL_GET_COMPATIBILITY_INFO 0x101
-#define TW_IOCTL_GET_LAST_EVENT 0x102
-#define TW_IOCTL_GET_FIRST_EVENT 0x103
-#define TW_IOCTL_GET_NEXT_EVENT 0x104
-#define TW_IOCTL_GET_PREVIOUS_EVENT 0x105
-#define TW_IOCTL_GET_LOCK 0x106
-#define TW_IOCTL_RELEASE_LOCK 0x107
-#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108
+#define TW_IOCTL_GET_LAST_EVENT 0x102
+#define TW_IOCTL_GET_FIRST_EVENT 0x103
+#define TW_IOCTL_GET_NEXT_EVENT 0x104
+#define TW_IOCTL_GET_PREVIOUS_EVENT 0x105
+#define TW_IOCTL_GET_LOCK 0x106
+#define TW_IOCTL_RELEASE_LOCK 0x107
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108
#define TW_IOCTL_ERROR_STATUS_NOT_LOCKED 0x1001 // Not locked
-#define TW_IOCTL_ERROR_STATUS_LOCKED 0x1002 // Already locked
+#define TW_IOCTL_ERROR_STATUS_LOCKED 0x1002 // Already locked
#define TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS 0x1003 // No more events
#define TW_IOCTL_ERROR_STATUS_AEN_CLOBBER 0x1004 // AEN clobber occurred
#define TW_IOCTL_ERROR_OS_EFAULT -EFAULT // Bad address
@@ -405,12 +397,12 @@ static twa_message_type twa_error_table[] = {
#define TW_SENSE_DATA_LENGTH 18
#define TW_STATUS_CHECK_CONDITION 2
#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a
-#define TW_ERROR_UNIT_OFFLINE 0x128
+#define TW_ERROR_UNIT_OFFLINE 0x128
#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3
#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4
-#define TW_MESSAGE_SOURCE_LINUX_DRIVER 6
+#define TW_MESSAGE_SOURCE_LINUX_DRIVER 6
#define TW_DRIVER TW_MESSAGE_SOURCE_LINUX_DRIVER
-#define TW_MESSAGE_SOURCE_LINUX_OS 9
+#define TW_MESSAGE_SOURCE_LINUX_OS 9
#define TW_OS TW_MESSAGE_SOURCE_LINUX_OS
#ifndef PCI_DEVICE_ID_3WARE_9000
#define PCI_DEVICE_ID_3WARE_9000 0x1002
@@ -442,24 +434,38 @@ static twa_message_type twa_error_table[] = {
#define TW_RESID_OUT(x) ((x >> 4) & 0xff)
/* request_id: 12, lun: 4 */
-#define TW_REQ_LUN_IN(lun, request_id) (((lun << 12) & 0xf000) | (request_id & 0xfff))
-#define TW_LUN_OUT(lun) ((lun >> 12) & 0xf)
+#define TW_REQ_LUN_IN(lun, request_id) \
+ cpu_to_le16(((lun << 12) & 0xf000) | (request_id & 0xfff))
+#define TW_LUN_OUT(lun) ((le16_to_cpu(lun) >> 12) & 0xf)
/* Macros */
#define TW_CONTROL_REG_ADDR(x) (x->base_addr)
#define TW_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0x4)
-#define TW_COMMAND_QUEUE_REG_ADDR(x) (sizeof(dma_addr_t) > 4 ? ((unsigned char __iomem *)x->base_addr + 0x20) : ((unsigned char __iomem *)x->base_addr + 0x8))
-#define TW_COMMAND_QUEUE_REG_ADDR_LARGE(x) ((unsigned char __iomem *)x->base_addr + 0x20)
-#define TW_RESPONSE_QUEUE_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0xC)
-#define TW_RESPONSE_QUEUE_REG_ADDR_LARGE(x) ((unsigned char __iomem *)x->base_addr + 0x30)
-#define TW_CLEAR_ALL_INTERRUPTS(x) (writel(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_CLEAR_ATTENTION_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_CLEAR_HOST_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_DISABLE_INTERRUPTS(x) (writel(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
-#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
-#define TW_MASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_UNMASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_SOFT_RESET(x) (writel(TW_CONTROL_ISSUE_SOFT_RESET | \
+#define TW_COMMAND_QUEUE_REG_ADDR(x) \
+ (sizeof(dma_addr_t) > 4 ? ((unsigned char __iomem *)x->base_addr + 0x20) : ((unsigned char __iomem *)x->base_addr + 0x8))
+#define TW_COMMAND_QUEUE_REG_ADDR_LARGE(x) \
+ ((unsigned char __iomem *)x->base_addr + 0x20)
+#define TW_RESPONSE_QUEUE_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + 0xC)
+#define TW_RESPONSE_QUEUE_REG_ADDR_LARGE(x) \
+ ((unsigned char __iomem *)x->base_addr + 0x30)
+#define TW_CLEAR_ALL_INTERRUPTS(x) \
+ (writel(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_ATTENTION_INTERRUPT(x) \
+ (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_HOST_INTERRUPT(x) \
+ (writel(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_DISABLE_INTERRUPTS(x) \
+ (writel(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) \
+ (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
+ TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | \
+ TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_MASK_COMMAND_INTERRUPT(x) \
+ (writel(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_UNMASK_COMMAND_INTERRUPT(x) \
+ (writel(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_SOFT_RESET(x) (writel(TW_CONTROL_ISSUE_SOFT_RESET | \
TW_CONTROL_CLEAR_HOST_INTERRUPT | \
TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
TW_CONTROL_MASK_COMMAND_INTERRUPT | \
@@ -477,70 +483,75 @@ printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \
#define TW_APACHE_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 72 : 109)
#define TW_ESCALADE_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 41 : 62)
#define TW_PADDING_LENGTH (sizeof(dma_addr_t) > 4 ? 8 : 0)
-#define TW_CPU_TO_SGL(x) (sizeof(dma_addr_t) > 4 ? cpu_to_le64(x) : cpu_to_le32(x))
-#pragma pack(1)
+#if IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT)
+typedef __le64 twa_addr_t;
+#define TW_CPU_TO_SGL(x) cpu_to_le64(x)
+#else
+typedef __le32 twa_addr_t;
+#define TW_CPU_TO_SGL(x) cpu_to_le32(x)
+#endif
/* Scatter Gather List Entry */
typedef struct TAG_TW_SG_Entry {
- dma_addr_t address;
- u32 length;
-} TW_SG_Entry;
+ twa_addr_t address;
+ __le32 length;
+} __packed TW_SG_Entry;
/* Command Packet */
typedef struct TW_Command {
- unsigned char opcode__sgloffset;
- unsigned char size;
- unsigned char request_id;
- unsigned char unit__hostid;
+ u8 opcode__sgloffset;
+ u8 size;
+ u8 request_id;
+ u8 unit__hostid;
/* Second DWORD */
- unsigned char status;
- unsigned char flags;
+ u8 status;
+ u8 flags;
union {
- unsigned short block_count;
- unsigned short parameter_count;
+ __le16 block_count;
+ __le16 parameter_count;
} byte6_offset;
union {
struct {
- u32 lba;
- TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
- dma_addr_t padding;
+ __le32 lba;
+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
+ twa_addr_t padding;
} io;
struct {
- TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
- u32 padding;
- dma_addr_t padding2;
+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
+ __le32 padding;
+ twa_addr_t padding2;
} param;
} byte8_offset;
} TW_Command;
/* Command Packet for 9000+ controllers */
typedef struct TAG_TW_Command_Apache {
- unsigned char opcode__reserved;
- unsigned char unit;
- unsigned short request_id__lunl;
- unsigned char status;
- unsigned char sgl_offset;
- unsigned short sgl_entries__lunh;
- unsigned char cdb[16];
- TW_SG_Entry sg_list[TW_APACHE_MAX_SGL_LENGTH];
- unsigned char padding[TW_PADDING_LENGTH];
+ u8 opcode__reserved;
+ u8 unit;
+ __le16 request_id__lunl;
+ u8 status;
+ u8 sgl_offset;
+ __le16 sgl_entries__lunh;
+ u8 cdb[16];
+ TW_SG_Entry sg_list[TW_APACHE_MAX_SGL_LENGTH];
+ u8 padding[TW_PADDING_LENGTH];
} TW_Command_Apache;
/* New command packet header */
typedef struct TAG_TW_Command_Apache_Header {
unsigned char sense_data[TW_SENSE_DATA_LENGTH];
struct {
- char reserved[4];
- unsigned short error;
- unsigned char padding;
- unsigned char severity__reserved;
+ u8 reserved[4];
+ __le16 error;
+ u8 padding;
+ u8 severity__reserved;
} status_block;
unsigned char err_specific_desc[98];
struct {
- unsigned char size_header;
- unsigned short reserved;
- unsigned char size_sense;
+ u8 size_header;
+ u8 reserved[2];
+ u8 size_sense;
} header_desc;
} TW_Command_Apache_Header;
@@ -555,19 +566,19 @@ typedef struct TAG_TW_Command_Full {
/* Initconnection structure */
typedef struct TAG_TW_Initconnect {
- unsigned char opcode__reserved;
- unsigned char size;
- unsigned char request_id;
- unsigned char res2;
- unsigned char status;
- unsigned char flags;
- unsigned short message_credits;
- u32 features;
- unsigned short fw_srl;
- unsigned short fw_arch_id;
- unsigned short fw_branch;
- unsigned short fw_build;
- u32 result;
+ u8 opcode__reserved;
+ u8 size;
+ u8 request_id;
+ u8 res2;
+ u8 status;
+ u8 flags;
+ __le16 message_credits;
+ __le32 features;
+ __le16 fw_srl;
+ __le16 fw_arch_id;
+ __le16 fw_branch;
+ __le16 fw_build;
+ __le32 result;
} TW_Initconnect;
/* Event info structure */
@@ -594,9 +605,9 @@ typedef struct TAG_TW_Ioctl_Driver_Command {
typedef struct TAG_TW_Ioctl_Apache {
TW_Ioctl_Driver_Command driver_command;
- char padding[488];
+ char padding[488];
TW_Command_Full firmware_command;
- char data_buffer[1];
+ char data_buffer[];
} TW_Ioctl_Buf_Apache;
/* Lock structure for ioctl get/release lock */
@@ -608,11 +619,11 @@ typedef struct TAG_TW_Lock {
/* GetParam descriptor */
typedef struct {
- unsigned short table_id;
- unsigned short parameter_id;
- unsigned short parameter_size_bytes;
- unsigned short actual_parameter_size_bytes;
- unsigned char data[1];
+ __le16 table_id;
+ __le16 parameter_id;
+ __le16 parameter_size_bytes;
+ __le16 actual_parameter_size_bytes;
+ u8 data[];
} TW_Param_Apache, *PTW_Param_Apache;
/* Response queue */
@@ -639,13 +650,11 @@ typedef struct TAG_TW_Compatibility_Info
unsigned short fw_on_ctlr_build;
} TW_Compatibility_Info;
-#pragma pack()
-
typedef struct TAG_TW_Device_Extension {
- u32 __iomem *base_addr;
- unsigned long *generic_buffer_virt[TW_Q_LENGTH];
- dma_addr_t generic_buffer_phys[TW_Q_LENGTH];
- TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
+ u32 __iomem *base_addr;
+ unsigned long *generic_buffer_virt[TW_Q_LENGTH];
+ dma_addr_t generic_buffer_phys[TW_Q_LENGTH];
+ TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
dma_addr_t command_packet_phys[TW_Q_LENGTH];
struct pci_dev *tw_pci_dev;
struct scsi_cmnd *srb[TW_Q_LENGTH];
@@ -655,10 +664,10 @@ typedef struct TAG_TW_Device_Extension {
unsigned char pending_queue[TW_Q_LENGTH];
unsigned char pending_head;
unsigned char pending_tail;
- int state[TW_Q_LENGTH];
+ int state[TW_Q_LENGTH];
unsigned int posted_request_count;
unsigned int max_posted_request_count;
- unsigned int pending_request_count;
+ unsigned int pending_request_count;
unsigned int max_pending_request_count;
unsigned int max_sgl_entries;
unsigned int sgl_entries;
@@ -669,12 +678,12 @@ typedef struct TAG_TW_Device_Extension {
struct Scsi_Host *host;
long flags;
int reset_print;
- TW_Event *event_queue[TW_Q_LENGTH];
- unsigned char error_index;
+ TW_Event *event_queue[TW_Q_LENGTH];
+ unsigned char error_index;
unsigned char event_queue_wrapped;
- unsigned int error_sequence_id;
- int ioctl_sem_lock;
- u32 ioctl_msec;
+ unsigned int error_sequence_id;
+ int ioctl_sem_lock;
+ ktime_t ioctl_time;
int chrdev_request_id;
wait_queue_head_t ioctl_wqueue;
struct mutex ioctl_lock;
diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c
index 13e39e1fdfe2..e319be7d369c 100644
--- a/drivers/scsi/3w-sas.c
+++ b/drivers/scsi/3w-sas.c
@@ -1,7 +1,7 @@
/*
3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
- Written By: Adam Radford <linuxraid@lsi.com>
+ Written By: Adam Radford <aradford@gmail.com>
Copyright (C) 2009 LSI Corporation.
@@ -43,10 +43,7 @@
LSI 3ware 9750 6Gb/s SAS/SATA-RAID
Bugs/Comments/Suggestions should be mailed to:
- linuxraid@lsi.com
-
- For more information, goto:
- http://www.lsi.com
+ aradford@gmail.com
History
-------
@@ -67,7 +64,7 @@
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
@@ -99,7 +96,7 @@ static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_res
/* This function returns AENs through sysfs */
static ssize_t twl_sysfs_aen_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *outbuf, loff_t offset, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
@@ -119,18 +116,18 @@ static ssize_t twl_sysfs_aen_read(struct file *filp, struct kobject *kobj,
} /* End twl_sysfs_aen_read() */
/* aen_read sysfs attribute initializer */
-static struct bin_attribute twl_sysfs_aen_read_attr = {
+static const struct bin_attribute twl_sysfs_aen_read_attr = {
.attr = {
.name = "3ware_aen_read",
.mode = S_IRUSR,
- },
+ },
.size = 0,
.read = twl_sysfs_aen_read
};
/* This function returns driver compatibility info through sysfs */
static ssize_t twl_sysfs_compat_info(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *outbuf, loff_t offset, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
@@ -150,11 +147,11 @@ static ssize_t twl_sysfs_compat_info(struct file *filp, struct kobject *kobj,
} /* End twl_sysfs_compat_info() */
/* compat_info sysfs attribute initializer */
-static struct bin_attribute twl_sysfs_compat_info_attr = {
+static const struct bin_attribute twl_sysfs_compat_info_attr = {
.attr = {
.name = "3ware_compat_info",
.mode = S_IRUSR,
- },
+ },
.size = 0,
.read = twl_sysfs_compat_info
};
@@ -169,56 +166,45 @@ static ssize_t twl_show_stats(struct device *dev,
ssize_t len;
spin_lock_irqsave(tw_dev->host->host_lock, flags);
- len = snprintf(buf, PAGE_SIZE, "3w-sas Driver version: %s\n"
- "Current commands posted: %4d\n"
- "Max commands posted: %4d\n"
- "Last sgl length: %4d\n"
- "Max sgl length: %4d\n"
- "Last sector count: %4d\n"
- "Max sector count: %4d\n"
- "SCSI Host Resets: %4d\n"
- "AEN's: %4d\n",
- TW_DRIVER_VERSION,
- tw_dev->posted_request_count,
- tw_dev->max_posted_request_count,
- tw_dev->sgl_entries,
- tw_dev->max_sgl_entries,
- tw_dev->sector_count,
- tw_dev->max_sector_count,
- tw_dev->num_resets,
- tw_dev->aen_count);
+ len = sysfs_emit(buf, "3w-sas Driver version: %s\n"
+ "Current commands posted: %4d\n"
+ "Max commands posted: %4d\n"
+ "Last sgl length: %4d\n"
+ "Max sgl length: %4d\n"
+ "Last sector count: %4d\n"
+ "Max sector count: %4d\n"
+ "SCSI Host Resets: %4d\n"
+ "AEN's: %4d\n",
+ TW_DRIVER_VERSION,
+ tw_dev->posted_request_count,
+ tw_dev->max_posted_request_count,
+ tw_dev->sgl_entries,
+ tw_dev->max_sgl_entries,
+ tw_dev->sector_count,
+ tw_dev->max_sector_count,
+ tw_dev->num_resets,
+ tw_dev->aen_count);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
return len;
} /* End twl_show_stats() */
-/* This function will set a devices queue depth */
-static int twl_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (queue_depth > TW_Q_LENGTH-2)
- queue_depth = TW_Q_LENGTH-2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
-} /* End twl_change_queue_depth() */
-
/* stats sysfs attribute initializer */
static struct device_attribute twl_host_stats_attr = {
.attr = {
- .name = "3ware_stats",
+ .name = "3ware_stats",
.mode = S_IRUGO,
},
.show = twl_show_stats
};
/* Host attributes initializer */
-static struct device_attribute *twl_host_attrs[] = {
- &twl_host_stats_attr,
+static struct attribute *twl_host_attrs[] = {
+ &twl_host_stats_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(twl_host);
+
/* This function will look up an AEN severity string */
static char *twl_aen_severity_lookup(unsigned char severity_code)
{
@@ -237,7 +223,6 @@ out:
static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
{
u32 local_time;
- struct timeval time;
TW_Event *event;
unsigned short aen;
char host[16];
@@ -256,8 +241,8 @@ static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_H
memset(event, 0, sizeof(TW_Event));
event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
- do_gettimeofday(&time);
- local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
+ /* event->time_stamp_sec overflows in y2106 */
+ local_time = (u32)(ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60));
event->time_stamp_sec = local_time;
event->aen_code = aen;
event->retrieved = TW_AEN_NOT_RETRIEVED;
@@ -303,41 +288,20 @@ static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
return 0;
} /* End twl_post_command_packet() */
-/* This function will perform a pci-dma mapping for a scatter gather list */
-static int twl_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
-{
- int use_sg;
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- use_sg = scsi_dma_map(cmd);
- if (!use_sg)
- return 0;
- else if (use_sg < 0) {
- TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Failed to map scatter gather list");
- return 0;
- }
-
- cmd->SCp.phase = TW_PHASE_SGLIST;
- cmd->SCp.have_data_in = use_sg;
-
- return use_sg;
-} /* End twl_map_scsi_sg_data() */
-
/* This function hands scsi cdb's to the firmware */
-static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg)
+static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
+ unsigned char *cdb, int use_sg,
+ TW_SG_Entry_ISO *sglistarg)
{
TW_Command_Full *full_command_packet;
TW_Command_Apache *command_packet;
int i, sg_count;
struct scsi_cmnd *srb = NULL;
- struct scatterlist *sglist = NULL, *sg;
+ struct scatterlist *sg;
int retval = 1;
- if (tw_dev->srb[request_id]) {
+ if (tw_dev->srb[request_id])
srb = tw_dev->srb[request_id];
- if (scsi_sglist(srb))
- sglist = scsi_sglist(srb);
- }
/* Initialize command packet */
full_command_packet = tw_dev->command_packet_virt[request_id];
@@ -370,8 +334,8 @@ static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
if (!sglistarg) {
/* Map sglist from scsi layer to cmd packet */
if (scsi_sg_count(srb)) {
- sg_count = twl_map_scsi_sg_data(tw_dev, request_id);
- if (sg_count == 0)
+ sg_count = scsi_dma_map(srb);
+ if (sg_count <= 0)
goto out;
scsi_for_each_sg(srb, sg, sg_count, i) {
@@ -409,7 +373,7 @@ out:
/* This function will read the aen queue from the isr */
static int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{
- char cdb[TW_MAX_CDB_LEN];
+ unsigned char cdb[TW_MAX_CDB_LEN];
TW_SG_Entry_ISO sglist[1];
TW_Command_Full *full_command_packet;
int retval = 1;
@@ -444,11 +408,10 @@ out:
static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
{
u32 schedulertime;
- struct timeval utc;
TW_Command_Full *full_command_packet;
TW_Command *command_packet;
TW_Param_Apache *param;
- u32 local_time;
+ time64_t local_time;
/* Fill out the command packet */
full_command_packet = tw_dev->command_packet_virt[request_id];
@@ -468,12 +431,11 @@ static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */
param->parameter_size_bytes = cpu_to_le16(4);
- /* Convert system time in UTC to local time seconds since last
+ /* Convert system time in UTC to local time seconds since last
Sunday 12:00AM */
- do_gettimeofday(&utc);
- local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
- schedulertime = local_time - (3 * 86400);
- schedulertime = cpu_to_le32(schedulertime % 604800);
+ local_time = (ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60));
+ div_u64_rem(local_time - (3 * 86400), 604800, &schedulertime);
+ schedulertime = cpu_to_le32(schedulertime);
memcpy(param->data, &schedulertime, sizeof(u32));
@@ -520,7 +482,7 @@ static int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id)
/* Keep reading the queue in case there are more aen's */
if (twl_aen_read_queue(tw_dev, request_id))
goto out2;
- else {
+ else {
retval = 0;
goto out;
}
@@ -585,7 +547,7 @@ static int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int se
msleep(50);
}
retval = 0;
-out:
+out:
return retval;
} /* End twl_poll_response() */
@@ -593,7 +555,7 @@ out:
static int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
{
int request_id = 0;
- char cdb[TW_MAX_CDB_LEN];
+ unsigned char cdb[TW_MAX_CDB_LEN];
TW_SG_Entry_ISO sglist[1];
int finished = 0, count = 0;
TW_Command_Full *full_command_packet;
@@ -683,14 +645,14 @@ static int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
unsigned long *cpu_addr;
int retval = 1;
- cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev,
+ size * TW_Q_LENGTH, &dma_handle,
+ GFP_KERNEL);
if (!cpu_addr) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
goto out;
}
- memset(cpu_addr, 0, size*TW_Q_LENGTH);
-
for (i = 0; i < TW_Q_LENGTH; i++) {
switch(which) {
case 0:
@@ -728,7 +690,7 @@ static void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_comm
newcommand->request_id__lunl =
cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id));
if (length) {
- newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache));
newcommand->sg_list[0].length = TW_CPU_TO_SGL(length);
}
newcommand->sgl_entries__lunh =
@@ -740,7 +702,7 @@ static void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_comm
if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) {
/* Load the sg list */
sgl = (TW_SG_Entry_ISO *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry_ISO)/4) + pae + (sizeof(dma_addr_t) > 4 ? 1 : 0));
- sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache));
sgl->length = TW_CPU_TO_SGL(length);
oldcommand->size += pae;
oldcommand->size += sizeof(dma_addr_t) > 4 ? 1 : 0;
@@ -757,7 +719,7 @@ static long twl_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
dma_addr_t dma_handle;
int request_id = 0;
TW_Ioctl_Driver_Command driver_command;
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file_inode(file);
TW_Ioctl_Buf_Apache *tw_ioctl;
TW_Command_Full *full_command_packet;
TW_Device_Extension *tw_dev = twl_device_extension_list[iminor(inode)];
@@ -786,7 +748,7 @@ static long twl_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;
/* Now allocate ioctl buf memory */
- cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle, GFP_KERNEL);
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_Ioctl_Buf_Apache), &dma_handle, GFP_KERNEL);
if (!cpu_addr) {
retval = -ENOMEM;
goto out2;
@@ -795,7 +757,7 @@ static long twl_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
/* Now copy down the entire ioctl */
- if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+ if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache)))
goto out3;
/* See which ioctl we are doing */
@@ -839,7 +801,7 @@ static long twl_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
/* Now copy in the command packet response */
memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
-
+
/* Now complete the io */
spin_lock_irqsave(tw_dev->host->host_lock, flags);
tw_dev->posted_request_count--;
@@ -853,11 +815,11 @@ static long twl_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long
}
/* Now copy the entire response to userspace */
- if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length) == 0)
retval = 0;
out3:
/* Now free ioctl buf memory */
- dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_Ioctl_Buf_Apache), cpu_addr, dma_handle);
out2:
mutex_unlock(&tw_dev->ioctl_lock);
out:
@@ -900,7 +862,6 @@ static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, in
TW_Command_Full *full_command_packet;
unsigned short error;
char *error_str;
- int retval = 1;
header = tw_dev->sense_buffer_virt[i];
full_command_packet = tw_dev->command_packet_virt[request_id];
@@ -916,7 +877,7 @@ static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, in
tw_dev->host->host_no,
TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
header->status_block.error,
- error_str,
+ error_str,
header->err_specific_desc);
else
printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s:%s.\n",
@@ -932,26 +893,26 @@ static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, in
goto out;
}
out:
- return retval;
+ return 1;
} /* End twl_fill_sense() */
/* This function will free up device extension resources */
static void twl_free_device_extension(TW_Device_Extension *tw_dev)
{
if (tw_dev->command_packet_virt[0])
- pci_free_consistent(tw_dev->tw_pci_dev,
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
sizeof(TW_Command_Full)*TW_Q_LENGTH,
tw_dev->command_packet_virt[0],
tw_dev->command_packet_phys[0]);
if (tw_dev->generic_buffer_virt[0])
- pci_free_consistent(tw_dev->tw_pci_dev,
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
TW_SECTOR_SIZE*TW_Q_LENGTH,
tw_dev->generic_buffer_virt[0],
tw_dev->generic_buffer_phys[0]);
if (tw_dev->sense_buffer_virt[0])
- pci_free_consistent(tw_dev->tw_pci_dev,
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
sizeof(TW_Command_Apache_Header)*
TW_Q_LENGTH,
tw_dev->sense_buffer_virt[0],
@@ -974,8 +935,8 @@ static void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl
command_packet = &full_command_packet->command.oldcommand;
command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
- command_packet->size = TW_COMMAND_SIZE;
- command_packet->request_id = request_id;
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->request_id = request_id;
command_packet->byte6_offset.block_count = cpu_to_le16(1);
/* Now setup the param */
@@ -1005,14 +966,14 @@ static void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl
/* This function will send an initconnection command to controller */
static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits,
- u32 set_features, unsigned short current_fw_srl,
- unsigned short current_fw_arch_id,
- unsigned short current_fw_branch,
- unsigned short current_fw_build,
- unsigned short *fw_on_ctlr_srl,
- unsigned short *fw_on_ctlr_arch_id,
- unsigned short *fw_on_ctlr_branch,
- unsigned short *fw_on_ctlr_build,
+ u32 set_features, unsigned short current_fw_srl,
+ unsigned short current_fw_arch_id,
+ unsigned short current_fw_branch,
+ unsigned short current_fw_build,
+ unsigned short *fw_on_ctlr_srl,
+ unsigned short *fw_on_ctlr_arch_id,
+ unsigned short *fw_on_ctlr_branch,
+ unsigned short *fw_on_ctlr_build,
u32 *init_connect_result)
{
TW_Command_Full *full_command_packet;
@@ -1023,7 +984,7 @@ static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits,
full_command_packet = tw_dev->command_packet_virt[request_id];
memset(full_command_packet, 0, sizeof(TW_Command_Full));
full_command_packet->header.header_desc.size_header = 128;
-
+
tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION);
tw_initconnect->request_id = request_id;
@@ -1041,7 +1002,7 @@ static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits,
tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id);
tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch);
tw_initconnect->fw_build = cpu_to_le16(current_fw_build);
- } else
+ } else
tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
/* Send command packet to the board */
@@ -1116,15 +1077,6 @@ out:
return retval;
} /* End twl_initialize_device_extension() */
-/* This function will perform a pci-dma unmap */
-static void twl_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
-{
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- if (cmd->SCp.phase == TW_PHASE_SGLIST)
- scsi_dma_unmap(cmd);
-} /* End twl_unmap_scsi_data() */
-
/* This function will handle attention interrupts */
static int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev)
{
@@ -1257,7 +1209,7 @@ static irqreturn_t twl_interrupt(int irq, void *dev_instance)
if (!error)
cmd->result = (DID_OK << 16);
-
+
/* Report residual bytes for single sgl */
if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) {
if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id]))
@@ -1265,11 +1217,11 @@ static irqreturn_t twl_interrupt(int irq, void *dev_instance)
}
/* Now complete the io */
+ scsi_dma_unmap(cmd);
+ scsi_done(cmd);
tw_dev->state[request_id] = TW_S_COMPLETED;
twl_free_request_id(tw_dev, request_id);
tw_dev->posted_request_count--;
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
- twl_unmap_scsi_data(tw_dev, request_id);
}
/* Check for another response interrupt */
@@ -1291,7 +1243,7 @@ static int twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value,
reg_value = readl(reg);
before = jiffies;
- while ((reg_value & value) != result) {
+ while ((reg_value & value) != result) {
reg_value = readl(reg);
if (time_after(jiffies, before + HZ * seconds))
goto out;
@@ -1374,7 +1326,8 @@ static int twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
}
/* Load rest of compatibility struct */
- strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
+ strscpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION,
+ sizeof(tw_dev->tw_compat_info.driver_version));
tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL;
tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD;
@@ -1414,10 +1367,12 @@ static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_res
if ((tw_dev->state[i] != TW_S_FINISHED) &&
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) {
- if (tw_dev->srb[i]) {
- tw_dev->srb[i]->result = (DID_RESET << 16);
- tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
- twl_unmap_scsi_data(tw_dev, i);
+ struct scsi_cmnd *cmd = tw_dev->srb[i];
+
+ if (cmd) {
+ cmd->result = (DID_RESET << 16);
+ scsi_dma_unmap(cmd);
+ scsi_done(cmd);
}
}
}
@@ -1449,12 +1404,9 @@ out:
} /* End twl_reset_device_extension() */
/* This funciton returns unit geometry in cylinders/heads/sectors */
-static int twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
+static int twl_scsi_biosparam(struct scsi_device *sdev, struct gendisk *unused, sector_t capacity, int geom[])
{
int heads, sectors;
- TW_Device_Extension *tw_dev;
-
- tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
if (capacity >= 0x200000) {
heads = 255;
@@ -1501,8 +1453,9 @@ out:
} /* End twl_scsi_eh_reset() */
/* This is the main scsi queue function to handle scsi opcodes */
-static int twl_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+static int twl_scsi_queue_lck(struct scsi_cmnd *SCpnt)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
int request_id, retval;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
@@ -1512,18 +1465,12 @@ static int twl_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_
goto out;
}
- /* Save done function into scsi_cmnd struct */
- SCpnt->scsi_done = done;
-
/* Get a free request id */
twl_get_request_id(tw_dev, &request_id);
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
- /* Initialize phase to zero */
- SCpnt->SCp.phase = TW_PHASE_INITIAL;
-
retval = twl_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
if (retval) {
tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -1571,40 +1518,40 @@ static void twl_shutdown(struct pci_dev *pdev)
tw_dev = (TW_Device_Extension *)host->hostdata;
- if (tw_dev->online)
+ if (tw_dev->online)
__twl_shutdown(tw_dev);
} /* End twl_shutdown() */
/* This function configures unit settings when a unit is coming on-line */
-static int twl_slave_configure(struct scsi_device *sdev)
+static int twl_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
/* Force 60 second timeout */
blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
return 0;
-} /* End twl_slave_configure() */
+} /* End twl_sdev_configure() */
-/* scsi_host_template initializer */
-static struct scsi_host_template driver_template = {
+static const struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = "3w-sas",
.queuecommand = twl_scsi_queue,
.eh_host_reset_handler = twl_scsi_eh_reset,
.bios_param = twl_scsi_biosparam,
- .change_queue_depth = twl_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.can_queue = TW_Q_LENGTH-2,
- .slave_configure = twl_slave_configure,
+ .sdev_configure = twl_sdev_configure,
.this_id = -1,
.sg_tablesize = TW_LIBERATOR_MAX_SGL_LENGTH,
.max_sectors = TW_MAX_SECTORS,
.cmd_per_lun = TW_MAX_CMDS_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = twl_host_attrs,
- .emulated = 1
+ .shost_groups = twl_host_groups,
+ .emulated = 1,
+ .no_write_same = 1,
};
/* This function will probe and initialize a card */
-static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+static int twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
{
struct Scsi_Host *host = NULL;
TW_Device_Extension *tw_dev;
@@ -1620,14 +1567,12 @@ static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id
pci_set_master(pdev);
pci_try_set_mwi(pdev);
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
- TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask");
- retval = -ENODEV;
- goto out_disable_device;
- }
+ retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (retval) {
+ TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
if (!host) {
@@ -1643,6 +1588,7 @@ static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id
if (twl_initialize_device_extension(tw_dev)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Failed to initialize device extension");
+ retval = -ENOMEM;
goto out_free_device_extension;
}
@@ -1657,6 +1603,7 @@ static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id
tw_dev->base_addr = pci_iomap(pdev, 1, 0);
if (!tw_dev->base_addr) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to ioremap");
+ retval = -ENOMEM;
goto out_release_mem_region;
}
@@ -1666,6 +1613,7 @@ static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id
/* Initialize the card */
if (twl_reset_sequence(tw_dev, 0)) {
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Controller reset failed during probe");
+ retval = -ENOMEM;
goto out_iounmap;
}
@@ -1719,7 +1667,7 @@ static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id
/* Re-enable interrupts on the card */
TWL_UNMASK_INTERRUPTS(tw_dev);
-
+
/* Finally, scan the host */
scsi_scan_host(host);
@@ -1800,11 +1748,10 @@ static void twl_remove(struct pci_dev *pdev)
twl_device_extension_count--;
} /* End twl_remove() */
-#ifdef CONFIG_PM
/* This function is called on PCI suspend */
-static int twl_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused twl_suspend(struct device *dev)
{
- struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct Scsi_Host *host = dev_get_drvdata(dev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
printk(KERN_WARNING "3w-sas: Suspending host %d.\n", tw_dev->host->host_no);
@@ -1823,43 +1770,27 @@ static int twl_suspend(struct pci_dev *pdev, pm_message_t state)
/* Clear doorbell interrupt */
TWL_CLEAR_DB_INTERRUPT(tw_dev);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
return 0;
} /* End twl_suspend() */
/* This function is called on PCI resume */
-static int twl_resume(struct pci_dev *pdev)
+static int __maybe_unused twl_resume(struct device *dev)
{
int retval = 0;
+ struct pci_dev *pdev = to_pci_dev(dev);
struct Scsi_Host *host = pci_get_drvdata(pdev);
TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
printk(KERN_WARNING "3w-sas: Resuming host %d.\n", tw_dev->host->host_no);
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
+ pci_try_set_mwi(pdev);
- retval = pci_enable_device(pdev);
+ retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (retval) {
- TW_PRINTK(tw_dev->host, TW_DRIVER, 0x24, "Enable device failed during resume");
- return retval;
+ TW_PRINTK(host, TW_DRIVER, 0x25, "Failed to set dma mask during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
}
- pci_set_master(pdev);
- pci_try_set_mwi(pdev);
-
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
- || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
- TW_PRINTK(host, TW_DRIVER, 0x25, "Failed to set dma mask during resume");
- retval = -ENODEV;
- goto out_disable_device;
- }
-
/* Initialize the card */
if (twl_reset_sequence(tw_dev, 0)) {
retval = -ENODEV;
@@ -1886,29 +1817,26 @@ static int twl_resume(struct pci_dev *pdev)
out_disable_device:
scsi_remove_host(host);
- pci_disable_device(pdev);
return retval;
} /* End twl_resume() */
-#endif
/* PCI Devices supported by this driver */
-static struct pci_device_id twl_pci_tbl[] __devinitdata = {
+static const struct pci_device_id twl_pci_tbl[] = {
{ PCI_VDEVICE(3WARE, PCI_DEVICE_ID_3WARE_9750) },
{ }
};
MODULE_DEVICE_TABLE(pci, twl_pci_tbl);
+static SIMPLE_DEV_PM_OPS(twl_pm_ops, twl_suspend, twl_resume);
+
/* pci_driver initializer */
static struct pci_driver twl_driver = {
.name = "3w-sas",
.id_table = twl_pci_tbl,
.probe = twl_probe,
.remove = twl_remove,
-#ifdef CONFIG_PM
- .suspend = twl_suspend,
- .resume = twl_resume,
-#endif
+ .driver.pm = &twl_pm_ops,
.shutdown = twl_shutdown
};
diff --git a/drivers/scsi/3w-sas.h b/drivers/scsi/3w-sas.h
index d474892701d4..096dec29e2ac 100644
--- a/drivers/scsi/3w-sas.h
+++ b/drivers/scsi/3w-sas.h
@@ -1,7 +1,7 @@
/*
3w-sas.h -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
- Written By: Adam Radford <linuxraid@lsi.com>
+ Written By: Adam Radford <aradford@gmail.com>
Copyright (C) 2009 LSI Corporation.
@@ -39,10 +39,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Bugs/Comments/Suggestions should be mailed to:
- linuxraid@lsi.com
-
- For more information, goto:
- http://www.lsi.com
+ aradford@gmail.com
*/
#ifndef _3W_SAS_H
@@ -55,17 +52,17 @@ static char *twl_aen_severity_table[] =
};
/* Liberator register offsets */
-#define TWL_STATUS 0x0 /* Status */
-#define TWL_HIBDB 0x20 /* Inbound doorbell */
-#define TWL_HISTAT 0x30 /* Host interrupt status */
-#define TWL_HIMASK 0x34 /* Host interrupt mask */
+#define TWL_STATUS 0x0 /* Status */
+#define TWL_HIBDB 0x20 /* Inbound doorbell */
+#define TWL_HISTAT 0x30 /* Host interrupt status */
+#define TWL_HIMASK 0x34 /* Host interrupt mask */
#define TWL_HOBDB 0x9C /* Outbound doorbell */
-#define TWL_HOBDBC 0xA0 /* Outbound doorbell clear */
-#define TWL_SCRPD3 0xBC /* Scratchpad */
-#define TWL_HIBQPL 0xC0 /* Host inbound Q low */
-#define TWL_HIBQPH 0xC4 /* Host inbound Q high */
-#define TWL_HOBQPL 0xC8 /* Host outbound Q low */
-#define TWL_HOBQPH 0xCC /* Host outbound Q high */
+#define TWL_HOBDBC 0xA0 /* Outbound doorbell clear */
+#define TWL_SCRPD3 0xBC /* Scratchpad */
+#define TWL_HIBQPL 0xC0 /* Host inbound Q low */
+#define TWL_HIBQPH 0xC4 /* Host inbound Q high */
+#define TWL_HOBQPL 0xC8 /* Host outbound Q low */
+#define TWL_HOBQPH 0xCC /* Host outbound Q high */
#define TWL_HISTATUS_VALID_INTERRUPT 0xC
#define TWL_HISTATUS_ATTENTION_INTERRUPT 0x4
#define TWL_HISTATUS_RESPONSE_INTERRUPT 0x8
@@ -83,12 +80,12 @@ static char *twl_aen_severity_table[] =
#define TW_OP_EXECUTE_SCSI 0x10
/* Asynchronous Event Notification (AEN) codes used by the driver */
-#define TW_AEN_QUEUE_EMPTY 0x0000
-#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_QUEUE_EMPTY 0x0000
+#define TW_AEN_SOFT_RESET 0x0001
#define TW_AEN_SYNC_TIME_WITH_HOST 0x031
-#define TW_AEN_SEVERITY_ERROR 0x1
-#define TW_AEN_SEVERITY_DEBUG 0x4
-#define TW_AEN_NOT_RETRIEVED 0x1
+#define TW_AEN_SEVERITY_ERROR 0x1
+#define TW_AEN_SEVERITY_DEBUG 0x4
+#define TW_AEN_NOT_RETRIEVED 0x1
/* Command state defines */
#define TW_S_INITIAL 0x1 /* Initial state */
@@ -103,12 +100,8 @@ static char *twl_aen_severity_table[] =
#define TW_CURRENT_DRIVER_BUILD 0
#define TW_CURRENT_DRIVER_BRANCH 0
-/* Phase defines */
-#define TW_PHASE_INITIAL 0
-#define TW_PHASE_SGLIST 2
-
/* Misc defines */
-#define TW_SECTOR_SIZE 512
+#define TW_SECTOR_SIZE 512
#define TW_MAX_UNITS 32
#define TW_INIT_MESSAGE_CREDITS 0x100
#define TW_INIT_COMMAND_PACKET_SIZE 0x3
@@ -123,15 +116,15 @@ static char *twl_aen_severity_table[] =
#define TW_MAX_RESET_TRIES 2
#define TW_MAX_CMDS_PER_LUN 254
#define TW_MAX_AEN_DRAIN 255
-#define TW_IN_RESET 2
+#define TW_IN_RESET 2
#define TW_USING_MSI 3
#define TW_IN_ATTENTION_LOOP 4
-#define TW_MAX_SECTORS 256
-#define TW_MAX_CDB_LEN 16
-#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
-#define TW_IOCTL_CHRDEV_FREE -1
-#define TW_COMMAND_OFFSET 128 /* 128 bytes */
-#define TW_VERSION_TABLE 0x0402
+#define TW_MAX_SECTORS 256
+#define TW_MAX_CDB_LEN 16
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
+#define TW_COMMAND_OFFSET 128 /* 128 bytes */
+#define TW_VERSION_TABLE 0x0402
#define TW_TIMEKEEP_TABLE 0x040A
#define TW_INFORMATION_TABLE 0x0403
#define TW_PARAM_FWVER 3
@@ -143,15 +136,15 @@ static char *twl_aen_severity_table[] =
#define TW_PARAM_PHY_SUMMARY_TABLE 1
#define TW_PARAM_PHYCOUNT 2
#define TW_PARAM_PHYCOUNT_LENGTH 1
-#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108 // Used by smartmontools
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108 // Used by smartmontools
#define TW_ALLOCATION_LENGTH 128
#define TW_SENSE_DATA_LENGTH 18
#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a
#define TW_ERROR_INVALID_FIELD_IN_CDB 0x10d
-#define TW_ERROR_UNIT_OFFLINE 0x128
+#define TW_ERROR_UNIT_OFFLINE 0x128
#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3
#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4
-#define TW_DRIVER 6
+#define TW_DRIVER 6
#ifndef PCI_DEVICE_ID_3WARE_9750
#define PCI_DEVICE_ID_3WARE_9750 0x1010
#endif
@@ -174,25 +167,41 @@ static char *twl_aen_severity_table[] =
#define TW_NOTMFA_OUT(x) (x & 0x1)
/* request_id: 12, lun: 4 */
-#define TW_REQ_LUN_IN(lun, request_id) (((lun << 12) & 0xf000) | (request_id & 0xfff))
+#define TW_REQ_LUN_IN(lun, request_id) \
+ (((lun << 12) & 0xf000) | (request_id & 0xfff))
#define TW_LUN_OUT(lun) ((lun >> 12) & 0xf)
/* Register access macros */
-#define TWL_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_STATUS)
-#define TWL_HOBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPL)
-#define TWL_HOBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPH)
-#define TWL_HOBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDB)
-#define TWL_HOBDBC_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDBC)
-#define TWL_HIMASK_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIMASK)
-#define TWL_HISTAT_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HISTAT)
-#define TWL_HIBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPH)
-#define TWL_HIBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPL)
-#define TWL_HIBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBDB)
-#define TWL_SCRPD3_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_SCRPD3)
-#define TWL_MASK_INTERRUPTS(x) (writel(~0, TWL_HIMASK_REG_ADDR(tw_dev)))
-#define TWL_UNMASK_INTERRUPTS(x) (writel(~TWL_HISTATUS_VALID_INTERRUPT, TWL_HIMASK_REG_ADDR(tw_dev)))
-#define TWL_CLEAR_DB_INTERRUPT(x) (writel(~0, TWL_HOBDBC_REG_ADDR(tw_dev)))
-#define TWL_SOFT_RESET(x) (writel(TWL_ISSUE_SOFT_RESET, TWL_HIBDB_REG_ADDR(tw_dev)))
+#define TWL_STATUS_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_STATUS)
+#define TWL_HOBQPL_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HOBQPL)
+#define TWL_HOBQPH_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HOBQPH)
+#define TWL_HOBDB_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HOBDB)
+#define TWL_HOBDBC_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HOBDBC)
+#define TWL_HIMASK_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HIMASK)
+#define TWL_HISTAT_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HISTAT)
+#define TWL_HIBQPH_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HIBQPH)
+#define TWL_HIBQPL_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HIBQPL)
+#define TWL_HIBDB_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_HIBDB)
+#define TWL_SCRPD3_REG_ADDR(x) \
+ ((unsigned char __iomem *)x->base_addr + TWL_SCRPD3)
+#define TWL_MASK_INTERRUPTS(x) \
+ (writel(~0, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_UNMASK_INTERRUPTS(x) \
+ (writel(~TWL_HISTATUS_VALID_INTERRUPT, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_CLEAR_DB_INTERRUPT(x) \
+ (writel(~0, TWL_HOBDBC_REG_ADDR(tw_dev)))
+#define TWL_SOFT_RESET(x) \
+ (writel(TWL_ISSUE_SOFT_RESET, TWL_HIBDB_REG_ADDR(tw_dev)))
/* Macros */
#define TW_PRINTK(h,a,b,c) { \
@@ -324,9 +333,9 @@ typedef struct TAG_TW_Ioctl_Driver_Command {
typedef struct TAG_TW_Ioctl_Apache {
TW_Ioctl_Driver_Command driver_command;
- char padding[488];
+ char padding[488];
TW_Command_Full firmware_command;
- char data_buffer[1];
+ char data_buffer[];
} TW_Ioctl_Buf_Apache;
/* GetParam descriptor */
@@ -335,7 +344,7 @@ typedef struct {
unsigned short parameter_id;
unsigned short parameter_size_bytes;
unsigned short actual_parameter_size_bytes;
- unsigned char data[1];
+ unsigned char data[];
} TW_Param_Apache;
/* Compatibility information structure */
@@ -359,10 +368,10 @@ typedef struct TAG_TW_Compatibility_Info
#pragma pack()
typedef struct TAG_TW_Device_Extension {
- void __iomem *base_addr;
- unsigned long *generic_buffer_virt[TW_Q_LENGTH];
- dma_addr_t generic_buffer_phys[TW_Q_LENGTH];
- TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
+ void __iomem *base_addr;
+ unsigned long *generic_buffer_virt[TW_Q_LENGTH];
+ dma_addr_t generic_buffer_phys[TW_Q_LENGTH];
+ TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
dma_addr_t command_packet_phys[TW_Q_LENGTH];
TW_Command_Apache_Header *sense_buffer_virt[TW_Q_LENGTH];
dma_addr_t sense_buffer_phys[TW_Q_LENGTH];
@@ -371,7 +380,7 @@ typedef struct TAG_TW_Device_Extension {
unsigned char free_queue[TW_Q_LENGTH];
unsigned char free_head;
unsigned char free_tail;
- int state[TW_Q_LENGTH];
+ int state[TW_Q_LENGTH];
unsigned int posted_request_count;
unsigned int max_posted_request_count;
unsigned int max_sgl_entries;
@@ -382,9 +391,9 @@ typedef struct TAG_TW_Device_Extension {
unsigned int aen_count;
struct Scsi_Host *host;
long flags;
- TW_Event *event_queue[TW_Q_LENGTH];
- unsigned char error_index;
- unsigned int error_sequence_id;
+ TW_Event *event_queue[TW_Q_LENGTH];
+ unsigned char error_index;
+ unsigned int error_sequence_id;
int chrdev_request_id;
wait_queue_head_t ioctl_wqueue;
struct mutex ioctl_lock;
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index 7fe96ff60c58..0306a228c702 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -1,56 +1,55 @@
-/*
+/*
3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
- Written By: Adam Radford <linuxraid@lsi.com>
+ Written By: Adam Radford <aradford@gmail.com>
Modifications By: Joel Jacobson <linux@3ware.com>
- Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Brad Strand <linux@3ware.com>
Copyright (C) 1999-2010 3ware Inc.
- Kernel compatibility By: Andre Hedrick <andre@suse.com>
+ Kernel compatibility By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
-
+
Further tiny build fixes and trivial hoovering Alan Cox
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- NO WARRANTY
- THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- solely responsible for determining the appropriateness of using and
- distributing the Program and assumes all risks associated with its
- exercise of rights under this Agreement, including but not limited to
- the risks and costs of program errors, damage to or loss of data,
- programs or equipment, and unavailability or interruption of operations.
-
- DISCLAIMER OF LIABILITY
- NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- Bugs/Comments/Suggestions should be mailed to:
- linuxraid@lsi.com
-
- For more information, goto:
- http://www.lsi.com
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+
+ aradford@gmail.com
+
History
-------
@@ -71,7 +70,7 @@
1.02.00.003 - Fix tw_interrupt() to report error to scsi layer when
controller status is non-zero.
Added handling of request_sense opcode.
- Fix possible null pointer dereference in
+ Fix possible null pointer dereference in
tw_reset_device_extension()
1.02.00.004 - Add support for device id of 3ware 7000 series controllers.
Make tw_setfeature() call with interrupts disabled.
@@ -173,7 +172,7 @@
Initialize queues correctly when loading with no valid units.
1.02.00.034 - Fix tw_decode_bits() to handle multiple errors.
Add support for user configurable cmd_per_lun.
- Add support for sht->slave_configure().
+ Add support for sht->sdev_configure().
1.02.00.035 - Improve tw_allocate_memory() memory allocation.
Fix tw_chrdev_ioctl() to sleep correctly.
1.02.00.036 - Increase character ioctl timeout to 60 seconds.
@@ -211,11 +210,12 @@
#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_eh.h>
#include "3w-xxxx.h"
/* Globals */
@@ -239,7 +239,7 @@ static int tw_reset_device_extension(TW_Device_Extension *tw_dev);
/* This function will check the status register for unexpected bits */
static int tw_check_bits(u32 status_reg_value)
{
- if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) {
+ if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) {
dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value);
return 1;
}
@@ -291,7 +291,7 @@ static int tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value, int
}
return 1;
}
-
+
return 0;
} /* End tw_decode_bits() */
@@ -390,7 +390,7 @@ static int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
} else {
tw_dev->pending_tail = tw_dev->pending_tail + 1;
}
- }
+ }
TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
return 1;
}
@@ -403,7 +403,7 @@ static int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill
int i;
TW_Command *command;
- dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n");
+ dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n");
command = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, TW_UNIT_OUT(command->unit__hostid));
@@ -429,7 +429,7 @@ static int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill
/* Additional sense code qualifier */
tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3];
- tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ tw_dev->srb[request_id]->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
return TW_ISR_DONT_RESULT; /* Special case for isr to not over-write result */
}
}
@@ -443,10 +443,10 @@ static int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill
} /* End tw_decode_sense() */
/* This function will report controller error status */
-static int tw_check_errors(TW_Device_Extension *tw_dev)
+static int tw_check_errors(TW_Device_Extension *tw_dev)
{
u32 status_reg_value;
-
+
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) {
@@ -458,14 +458,14 @@ static int tw_check_errors(TW_Device_Extension *tw_dev)
} /* End tw_check_errors() */
/* This function will empty the response que */
-static void tw_empty_response_que(TW_Device_Extension *tw_dev)
+static void tw_empty_response_que(TW_Device_Extension *tw_dev)
{
- u32 status_reg_value, response_que_value;
+ u32 status_reg_value;
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
- response_que_value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
}
} /* End tw_empty_response_que() */
@@ -496,62 +496,51 @@ static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr,
ssize_t len;
spin_lock_irqsave(tw_dev->host->host_lock, flags);
- len = snprintf(buf, PAGE_SIZE, "3w-xxxx Driver version: %s\n"
- "Current commands posted: %4d\n"
- "Max commands posted: %4d\n"
- "Current pending commands: %4d\n"
- "Max pending commands: %4d\n"
- "Last sgl length: %4d\n"
- "Max sgl length: %4d\n"
- "Last sector count: %4d\n"
- "Max sector count: %4d\n"
- "SCSI Host Resets: %4d\n"
- "AEN's: %4d\n",
- TW_DRIVER_VERSION,
- tw_dev->posted_request_count,
- tw_dev->max_posted_request_count,
- tw_dev->pending_request_count,
- tw_dev->max_pending_request_count,
- tw_dev->sgl_entries,
- tw_dev->max_sgl_entries,
- tw_dev->sector_count,
- tw_dev->max_sector_count,
- tw_dev->num_resets,
- tw_dev->aen_count);
+ len = sysfs_emit(buf, "3w-xxxx Driver version: %s\n"
+ "Current commands posted: %4d\n"
+ "Max commands posted: %4d\n"
+ "Current pending commands: %4d\n"
+ "Max pending commands: %4d\n"
+ "Last sgl length: %4d\n"
+ "Max sgl length: %4d\n"
+ "Last sector count: %4d\n"
+ "Max sector count: %4d\n"
+ "SCSI Host Resets: %4d\n"
+ "AEN's: %4d\n",
+ TW_DRIVER_VERSION,
+ tw_dev->posted_request_count,
+ tw_dev->max_posted_request_count,
+ tw_dev->pending_request_count,
+ tw_dev->max_pending_request_count,
+ tw_dev->sgl_entries,
+ tw_dev->max_sgl_entries,
+ tw_dev->sector_count,
+ tw_dev->max_sector_count,
+ tw_dev->num_resets,
+ tw_dev->aen_count);
spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
return len;
} /* End tw_show_stats() */
-/* This function will set a devices queue depth */
-static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (queue_depth > TW_Q_LENGTH-2)
- queue_depth = TW_Q_LENGTH-2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
-} /* End tw_change_queue_depth() */
-
/* Create sysfs 'stats' entry */
static struct device_attribute tw_host_stats_attr = {
.attr = {
- .name = "stats",
+ .name = "stats",
.mode = S_IRUGO,
},
.show = tw_show_stats
};
/* Host attributes initializer */
-static struct device_attribute *tw_host_attrs[] = {
- &tw_host_stats_attr,
+static struct attribute *tw_host_attrs[] = {
+ &tw_host_stats_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(tw_host);
+
/* This function will read the aen queue from the isr */
-static int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
+static int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{
TW_Command *command_packet;
TW_Param *param;
@@ -617,7 +606,7 @@ static int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
} /* End tw_aen_read_queue() */
/* This function will complete an aen request from the isr */
-static int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
+static int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
{
TW_Param *param;
unsigned short aen;
@@ -641,7 +630,7 @@ static int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
} else {
- if (aen != 0x0)
+ if (aen != 0x0)
printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
}
} else {
@@ -759,7 +748,7 @@ static int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n");
return 1;
}
-
+
if (command_packet->status != 0) {
if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
/* Bad response */
@@ -847,15 +836,17 @@ static int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
- cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev,
+ size * TW_Q_LENGTH, &dma_handle, GFP_KERNEL);
if (cpu_addr == NULL) {
- printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n");
+ printk(KERN_WARNING "3w-xxxx: dma_alloc_coherent() failed.\n");
return 1;
}
if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
- pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, size * TW_Q_LENGTH,
+ cpu_addr, dma_handle);
return 1;
}
@@ -889,7 +880,7 @@ static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long a
unsigned long flags;
unsigned int data_buffer_length = 0;
unsigned long data_buffer_length_adjusted = 0;
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file_inode(file);
unsigned long *cpu_addr;
long timeout;
TW_New_Ioctl *tw_ioctl;
@@ -919,9 +910,9 @@ static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long a
/* Hardware can only do multiple of 512 byte transfers */
data_buffer_length_adjusted = (data_buffer_length + 511) & ~511;
-
+
/* Now allocate ioctl buf memory */
- cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle, GFP_KERNEL);
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_New_Ioctl), &dma_handle, GFP_KERNEL);
if (cpu_addr == NULL) {
retval = -ENOMEM;
goto out;
@@ -930,7 +921,7 @@ static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long a
tw_ioctl = (TW_New_Ioctl *)cpu_addr;
/* Now copy down the entire ioctl */
- if (copy_from_user(tw_ioctl, argp, data_buffer_length + sizeof(TW_New_Ioctl) - 1))
+ if (copy_from_user(tw_ioctl, argp, data_buffer_length + sizeof(TW_New_Ioctl)))
goto out2;
passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
@@ -975,15 +966,15 @@ static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long a
/* Load the sg list */
switch (TW_SGL_OUT(tw_ioctl->firmware_command.opcode__sgloffset)) {
case 2:
- tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl);
tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted;
break;
case 3:
- tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl);
tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted;
break;
case 5:
- passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl);
passthru->sg_list[0].length = data_buffer_length_adjusted;
break;
}
@@ -1026,12 +1017,12 @@ static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long a
}
/* Now copy the response to userspace */
- if (copy_to_user(argp, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length - 1))
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length))
goto out2;
retval = 0;
out2:
/* Now free ioctl buf memory */
- dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_New_Ioctl), cpu_addr, dma_handle);
out:
mutex_unlock(&tw_dev->ioctl_lock);
mutex_unlock(&tw_mutex);
@@ -1046,6 +1037,9 @@ static int tw_chrdev_open(struct inode *inode, struct file *file)
dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n");
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
minor_number = iminor(inode);
if (minor_number >= tw_device_extension_count)
return -ENODEV;
@@ -1057,6 +1051,7 @@ static int tw_chrdev_open(struct inode *inode, struct file *file)
static const struct file_operations tw_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tw_chrdev_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.open = tw_chrdev_open,
.release = NULL,
.llseek = noop_llseek,
@@ -1069,14 +1064,20 @@ static void tw_free_device_extension(TW_Device_Extension *tw_dev)
/* Free command packet and generic buffer memory */
if (tw_dev->command_packet_virtual_address[0])
- pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Command)*TW_Q_LENGTH, tw_dev->command_packet_virtual_address[0], tw_dev->command_packet_physical_address[0]);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
+ sizeof(TW_Command) * TW_Q_LENGTH,
+ tw_dev->command_packet_virtual_address[0],
+ tw_dev->command_packet_physical_address[0]);
if (tw_dev->alignment_virtual_address[0])
- pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_Q_LENGTH, tw_dev->alignment_virtual_address[0], tw_dev->alignment_physical_address[0]);
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev,
+ sizeof(TW_Sector) * TW_Q_LENGTH,
+ tw_dev->alignment_virtual_address[0],
+ tw_dev->alignment_physical_address[0]);
} /* End tw_free_device_extension() */
/* This function will send an initconnection command to controller */
-static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
+static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
{
unsigned long command_que_value;
TW_Command *command_packet;
@@ -1106,10 +1107,10 @@ static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n");
return 1;
}
-
+
/* Send command packet to the board */
outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
-
+
/* Poll for completion */
if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
@@ -1131,7 +1132,7 @@ static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
/* Set a value in the features table */
static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
- unsigned char *val)
+ unsigned char *val)
{
TW_Param *param;
TW_Command *command_packet;
@@ -1140,7 +1141,7 @@ static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
unsigned long command_que_value;
unsigned long param_value;
- /* Initialize SetParam command packet */
+ /* Initialize SetParam command packet */
if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet virtual address.\n");
return 1;
@@ -1161,7 +1162,7 @@ static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->srb[request_id]->result = (DID_OK << 16);
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ scsi_done(tw_dev->srb[request_id]);
}
command_packet->byte8.param.sgl[0].address = param_value;
command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
@@ -1170,10 +1171,10 @@ static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
command_packet->request_id = request_id;
command_packet->byte6.parameter_count = 1;
- command_que_value = tw_dev->command_packet_physical_address[request_id];
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
if (command_que_value == 0) {
printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet physical address.\n");
- return 1;
+ return 1;
}
/* Send command packet to the board */
@@ -1200,7 +1201,7 @@ static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
} /* End tw_setfeature() */
/* This function will reset a controller */
-static int tw_reset_sequence(TW_Device_Extension *tw_dev)
+static int tw_reset_sequence(TW_Device_Extension *tw_dev)
{
int error = 0;
int tries = 0;
@@ -1283,32 +1284,6 @@ static int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
return 0;
} /* End tw_initialize_device_extension() */
-static int tw_map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
- int use_sg;
-
- dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data()\n");
-
- use_sg = scsi_dma_map(cmd);
- if (use_sg < 0) {
- printk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data(): pci_map_sg() failed.\n");
- return 0;
- }
-
- cmd->SCp.phase = TW_PHASE_SGLIST;
- cmd->SCp.have_data_in = use_sg;
-
- return use_sg;
-} /* End tw_map_scsi_sg_data() */
-
-static void tw_unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
- dprintk(KERN_WARNING "3w-xxxx: tw_unmap_scsi_data()\n");
-
- if (cmd->SCp.phase == TW_PHASE_SGLIST)
- scsi_dma_unmap(cmd);
-} /* End tw_unmap_scsi_data() */
-
/* This function will reset a device extension */
static int tw_reset_device_extension(TW_Device_Extension *tw_dev)
{
@@ -1325,14 +1300,14 @@ static int tw_reset_device_extension(TW_Device_Extension *tw_dev)
/* Abort all requests that are in progress */
for (i=0;i<TW_Q_LENGTH;i++) {
- if ((tw_dev->state[i] != TW_S_FINISHED) &&
+ if ((tw_dev->state[i] != TW_S_FINISHED) &&
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) {
srb = tw_dev->srb[i];
if (srb != NULL) {
srb->result = (DID_RESET << 16);
- tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
- tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[i]);
+ scsi_dma_unmap(srb);
+ scsi_done(srb);
}
}
}
@@ -1365,14 +1340,12 @@ static int tw_reset_device_extension(TW_Device_Extension *tw_dev)
} /* End tw_reset_device_extension() */
/* This funciton returns unit geometry in cylinders/heads/sectors */
-static int tw_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int geom[])
+static int tw_scsi_biosparam(struct scsi_device *sdev, struct gendisk *unused,
+ sector_t capacity, int geom[])
{
int heads, sectors, cylinders;
- TW_Device_Extension *tw_dev;
-
+
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam()\n");
- tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
heads = 64;
sectors = 32;
@@ -1385,7 +1358,7 @@ static int tw_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev
}
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam(): heads = %d, sectors = %d, cylinders = %d\n", heads, sectors, cylinders);
- geom[0] = heads;
+ geom[0] = heads;
geom[1] = sectors;
geom[2] = cylinders;
@@ -1393,7 +1366,7 @@ static int tw_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev
} /* End tw_scsi_biosparam() */
/* This is the new scsi eh reset function */
-static int tw_scsi_eh_reset(struct scsi_cmnd *SCpnt)
+static int tw_scsi_eh_reset(struct scsi_cmnd *SCpnt)
{
TW_Device_Extension *tw_dev=NULL;
int retval = FAILED;
@@ -1534,7 +1507,7 @@ static int tw_scsiop_mode_sense(TW_Device_Extension *tw_dev, int request_id)
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->srb[request_id]->result = (DID_OK << 16);
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ scsi_done(tw_dev->srb[request_id]);
return 0;
}
@@ -1581,7 +1554,7 @@ static int tw_scsiop_mode_sense(TW_Device_Extension *tw_dev, int request_id)
/* Now try to post the command packet */
tw_post_command_packet(tw_dev, request_id);
-
+
return 0;
} /* End tw_scsiop_mode_sense() */
@@ -1602,16 +1575,16 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques
flags = (char *)&(param->data[0]);
memset(request_buffer, 0, sizeof(request_buffer));
- request_buffer[0] = 0xf; /* mode data length */
- request_buffer[1] = 0; /* default medium type */
- request_buffer[2] = 0x10; /* dpo/fua support on */
- request_buffer[3] = 0; /* no block descriptors */
- request_buffer[4] = 0x8; /* caching page */
- request_buffer[5] = 0xa; /* page length */
+ request_buffer[0] = 0xf; /* mode data length */
+ request_buffer[1] = 0; /* default medium type */
+ request_buffer[2] = 0x10; /* dpo/fua support on */
+ request_buffer[3] = 0; /* no block descriptors */
+ request_buffer[4] = 0x8; /* caching page */
+ request_buffer[5] = 0xa; /* page length */
if (*flags & 0x1)
- request_buffer[6] = 0x5; /* WCE on, RCD on */
+ request_buffer[6] = 0x5; /* WCE on, RCD on */
else
- request_buffer[6] = 0x1; /* WCE off, RCD on */
+ request_buffer[6] = 0x1; /* WCE off, RCD on */
tw_transfer_internal(tw_dev, request_id, request_buffer,
sizeof(request_buffer));
@@ -1619,7 +1592,7 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques
} /* End tw_scsiop_mode_sense_complete() */
/* This function handles scsi read_capacity commands */
-static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
+static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
{
TW_Param *param;
TW_Command *command_packet;
@@ -1651,8 +1624,8 @@ static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
}
param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
memset(param, 0, sizeof(TW_Sector));
- param->table_id = TW_UNIT_INFORMATION_TABLE_BASE +
- tw_dev->srb[request_id]->device->id;
+ param->table_id = TW_UNIT_INFORMATION_TABLE_BASE +
+ tw_dev->srb[request_id]->device->id;
param->parameter_id = 4; /* unitcapacity parameter */
param->parameter_size_bytes = 4;
param_value = tw_dev->alignment_physical_address[request_id];
@@ -1660,7 +1633,7 @@ static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment physical address.\n");
return 1;
}
-
+
command_packet->byte8.param.sgl[0].address = param_value;
command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
command_que_value = tw_dev->command_packet_physical_address[request_id];
@@ -1671,7 +1644,7 @@ static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
/* Now try to post the command to the board */
tw_post_command_packet(tw_dev, request_id);
-
+
return 0;
} /* End tw_scsiop_read_capacity() */
@@ -1693,7 +1666,7 @@ static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int req
}
param_data = &(param->data[0]);
- capacity = (param_data[3] << 24) | (param_data[2] << 16) |
+ capacity = (param_data[3] << 24) | (param_data[2] << 16) |
(param_data[1] << 8) | param_data[0];
/* Subtract one sector to fix get last sector ioctl */
@@ -1719,7 +1692,7 @@ static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int req
} /* End tw_scsiop_read_capacity_complete() */
/* This function handles scsi read or write commands */
-static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
+static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
{
TW_Command *command_packet;
unsigned long command_que_value;
@@ -1769,18 +1742,18 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
lba = ((u32)srb->cmnd[2] << 24) | ((u32)srb->cmnd[3] << 16) | ((u32)srb->cmnd[4] << 8) | (u32)srb->cmnd[5];
num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
}
-
+
/* Update sector statistic */
tw_dev->sector_count = num_sectors;
if (tw_dev->sector_count > tw_dev->max_sector_count)
tw_dev->max_sector_count = tw_dev->sector_count;
-
+
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): lba = 0x%x num_sectors = 0x%x\n", lba, num_sectors);
command_packet->byte8.io.lba = lba;
command_packet->byte6.block_count = num_sectors;
- use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
- if (!use_sg)
+ use_sg = scsi_dma_map(srb);
+ if (use_sg <= 0)
return 1;
scsi_for_each_sg(tw_dev->srb[request_id], sg, use_sg, i) {
@@ -1799,7 +1772,7 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Bad command packet physical address.\n");
return 1;
}
-
+
/* Now try to post the command to the board */
tw_post_command_packet(tw_dev, request_id);
@@ -1825,7 +1798,7 @@ static int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
/* If we got a request_sense, we probably want a reset, return error */
tw_dev->srb[request_id]->result = (DID_ERROR << 16);
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ scsi_done(tw_dev->srb[request_id]);
return 0;
} /* End tw_scsiop_request_sense() */
@@ -1947,8 +1920,9 @@ static int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int r
} /* End tw_scsiop_test_unit_ready_complete() */
/* This is the main scsi queue function to handle scsi opcodes */
-static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
unsigned char *command = SCpnt->cmnd;
int request_id = 0;
int retval = 1;
@@ -1958,60 +1932,54 @@ static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_c
if (test_bit(TW_IN_RESET, &tw_dev->flags))
return SCSI_MLQUEUE_HOST_BUSY;
- /* Save done function into Scsi_Cmnd struct */
- SCpnt->scsi_done = done;
-
/* Queue the command and get a request id */
tw_state_request_start(tw_dev, &request_id);
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
- /* Initialize phase to zero */
- SCpnt->SCp.phase = TW_PHASE_INITIAL;
-
switch (*command) {
- case READ_10:
- case READ_6:
- case WRITE_10:
- case WRITE_6:
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ/WRITE.\n");
- retval = tw_scsiop_read_write(tw_dev, request_id);
- break;
- case TEST_UNIT_READY:
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TEST_UNIT_READY.\n");
- retval = tw_scsiop_test_unit_ready(tw_dev, request_id);
- break;
- case INQUIRY:
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught INQUIRY.\n");
- retval = tw_scsiop_inquiry(tw_dev, request_id);
- break;
- case READ_CAPACITY:
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
- retval = tw_scsiop_read_capacity(tw_dev, request_id);
- break;
- case REQUEST_SENSE:
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
- retval = tw_scsiop_request_sense(tw_dev, request_id);
- break;
- case MODE_SENSE:
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught MODE_SENSE.\n");
- retval = tw_scsiop_mode_sense(tw_dev, request_id);
- break;
- case SYNCHRONIZE_CACHE:
- dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught SYNCHRONIZE_CACHE.\n");
- retval = tw_scsiop_synchronize_cache(tw_dev, request_id);
- break;
- case TW_IOCTL:
- printk(KERN_WARNING "3w-xxxx: SCSI_IOCTL_SEND_COMMAND deprecated, please update your 3ware tools.\n");
- break;
- default:
- printk(KERN_NOTICE "3w-xxxx: scsi%d: Unknown scsi opcode: 0x%x\n", tw_dev->host->host_no, *command);
- tw_dev->state[request_id] = TW_S_COMPLETED;
- tw_state_request_finish(tw_dev, request_id);
- SCpnt->result = (DID_BAD_TARGET << 16);
- done(SCpnt);
- retval = 0;
+ case READ_10:
+ case READ_6:
+ case WRITE_10:
+ case WRITE_6:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ/WRITE.\n");
+ retval = tw_scsiop_read_write(tw_dev, request_id);
+ break;
+ case TEST_UNIT_READY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TEST_UNIT_READY.\n");
+ retval = tw_scsiop_test_unit_ready(tw_dev, request_id);
+ break;
+ case INQUIRY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught INQUIRY.\n");
+ retval = tw_scsiop_inquiry(tw_dev, request_id);
+ break;
+ case READ_CAPACITY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
+ retval = tw_scsiop_read_capacity(tw_dev, request_id);
+ break;
+ case REQUEST_SENSE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
+ retval = tw_scsiop_request_sense(tw_dev, request_id);
+ break;
+ case MODE_SENSE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught MODE_SENSE.\n");
+ retval = tw_scsiop_mode_sense(tw_dev, request_id);
+ break;
+ case SYNCHRONIZE_CACHE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught SYNCHRONIZE_CACHE.\n");
+ retval = tw_scsiop_synchronize_cache(tw_dev, request_id);
+ break;
+ case TW_IOCTL:
+ printk(KERN_WARNING "3w-xxxx: SCSI_IOCTL_SEND_COMMAND deprecated, please update your 3ware tools.\n");
+ break;
+ default:
+ printk(KERN_NOTICE "3w-xxxx: scsi%d: Unknown scsi opcode: 0x%x\n", tw_dev->host->host_no, *command);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ scsi_build_sense(SCpnt, 1, ILLEGAL_REQUEST, 0x20, 0);
+ done(SCpnt);
+ retval = 0;
}
if (retval) {
tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -2026,7 +1994,7 @@ static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_c
static DEF_SCSI_QCMD(tw_scsi_queue)
/* This function is the interrupt service routine */
-static irqreturn_t tw_interrupt(int irq, void *dev_instance)
+static irqreturn_t tw_interrupt(int irq, void *dev_instance)
{
int request_id;
u32 status_reg_value;
@@ -2102,7 +2070,7 @@ static irqreturn_t tw_interrupt(int irq, void *dev_instance)
}
}
/* If there are no more pending requests, we mask command interrupt */
- if (tw_dev->pending_request_count == 0)
+ if (tw_dev->pending_request_count == 0)
TW_MASK_COMMAND_INTERRUPT(tw_dev);
}
@@ -2191,20 +2159,19 @@ static irqreturn_t tw_interrupt(int irq, void *dev_instance)
/* If error, command failed */
if (error == 1) {
/* Ask for a host reset */
- tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ tw_dev->srb[request_id]->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
}
/* Now complete the io */
if ((error != TW_ISR_DONT_COMPLETE)) {
+ scsi_dma_unmap(tw_dev->srb[request_id]);
+ scsi_done(tw_dev->srb[request_id]);
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->posted_request_count--;
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
-
- tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
}
}
-
+
/* Check for valid status after each drain */
status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
if (tw_check_bits(status_reg_value)) {
@@ -2254,38 +2221,38 @@ static void tw_shutdown(struct pci_dev *pdev)
} /* End tw_shutdown() */
/* This function gets called when a disk is coming online */
-static int tw_slave_configure(struct scsi_device *sdev)
+static int tw_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
/* Force 60 second timeout */
blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
return 0;
-} /* End tw_slave_configure() */
+} /* End tw_sdev_configure() */
-static struct scsi_host_template driver_template = {
+static const struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = "3ware Storage Controller",
.queuecommand = tw_scsi_queue,
.eh_host_reset_handler = tw_scsi_eh_reset,
.bios_param = tw_scsi_biosparam,
- .change_queue_depth = tw_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.can_queue = TW_Q_LENGTH-2,
- .slave_configure = tw_slave_configure,
+ .sdev_configure = tw_sdev_configure,
.this_id = -1,
.sg_tablesize = TW_MAX_SGL_LENGTH,
.max_sectors = TW_MAX_SECTORS,
- .cmd_per_lun = TW_MAX_CMDS_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = tw_host_attrs,
- .emulated = 1
+ .cmd_per_lun = TW_MAX_CMDS_PER_LUN,
+ .shost_groups = tw_host_groups,
+ .emulated = 1,
+ .no_write_same = 1,
};
/* This function will probe and initialize a card */
-static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+static int tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
{
struct Scsi_Host *host = NULL;
TW_Device_Extension *tw_dev;
- int retval = -ENODEV;
+ int retval;
retval = pci_enable_device(pdev);
if (retval) {
@@ -2295,7 +2262,7 @@ static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id *
pci_set_master(pdev);
- retval = pci_set_dma_mask(pdev, TW_DMA_MASK);
+ retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (retval) {
printk(KERN_WARNING "3w-xxxx: Failed to set dma mask.");
goto out_disable_device;
@@ -2315,6 +2282,7 @@ static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id *
if (tw_initialize_device_extension(tw_dev)) {
printk(KERN_WARNING "3w-xxxx: Failed to initialize device extension.");
+ retval = -ENOMEM;
goto out_free_device_extension;
}
@@ -2329,6 +2297,7 @@ static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id *
tw_dev->base_addr = pci_resource_start(pdev, 0);
if (!tw_dev->base_addr) {
printk(KERN_WARNING "3w-xxxx: Failed to get io address.");
+ retval = -ENOMEM;
goto out_release_mem_region;
}
@@ -2336,8 +2305,10 @@ static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id *
TW_DISABLE_INTERRUPTS(tw_dev);
/* Initialize the card */
- if (tw_reset_sequence(tw_dev))
+ if (tw_reset_sequence(tw_dev)) {
+ retval = -EINVAL;
goto out_release_mem_region;
+ }
/* Set host specific parameters */
host->max_id = TW_MAX_UNITS;
@@ -2422,7 +2393,7 @@ static void tw_remove(struct pci_dev *pdev)
} /* End tw_remove() */
/* PCI Devices supported by this driver */
-static struct pci_device_id tw_pci_tbl[] __devinitdata = {
+static const struct pci_device_id tw_pci_tbl[] = {
{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_1000,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_7000,
diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h
index 49dcf03c631a..120a087bdf3c 100644
--- a/drivers/scsi/3w-xxxx.h
+++ b/drivers/scsi/3w-xxxx.h
@@ -1,9 +1,9 @@
-/*
+/*
3w-xxxx.h -- 3ware Storage Controller device driver for Linux.
-
- Written By: Adam Radford <linuxraid@lsi.com>
+
+ Written By: Adam Radford <aradford@gmail.com>
Modifications By: Joel Jacobson <linux@3ware.com>
- Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Brad Strand <linux@3ware.com>
Copyright (C) 1999-2010 3ware Inc.
@@ -15,38 +15,39 @@
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- NO WARRANTY
- THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- solely responsible for determining the appropriateness of using and
- distributing the Program and assumes all risks associated with its
- exercise of rights under this Agreement, including but not limited to
- the risks and costs of program errors, damage to or loss of data,
- programs or equipment, and unavailability or interruption of operations.
-
- DISCLAIMER OF LIABILITY
- NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- Bugs/Comments/Suggestions should be mailed to:
- linuxraid@lsi.com
-
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+
+ aradford@gmail.com
+
For more information, goto:
http://www.lsi.com
*/
@@ -98,21 +99,21 @@ static char *tw_aen_string[] = {
static unsigned char tw_sense_table[][4] =
{
/* Codes for newer firmware */
- // ATA Error SCSI Error
- {0x01, 0x03, 0x13, 0x00}, // Address mark not found Address mark not found for data field
- {0x04, 0x0b, 0x00, 0x00}, // Aborted command Aborted command
- {0x10, 0x0b, 0x14, 0x00}, // ID not found Recorded entity not found
- {0x40, 0x03, 0x11, 0x00}, // Uncorrectable ECC error Unrecovered read error
- {0x61, 0x04, 0x00, 0x00}, // Device fault Hardware error
- {0x84, 0x0b, 0x47, 0x00}, // Data CRC error SCSI parity error
- {0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command
- {0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command
- {0x37, 0x02, 0x04, 0x00}, // Unit offline Not ready
- {0x09, 0x02, 0x04, 0x00}, // Unrecovered disk error Not ready
-
- /* Codes for older firmware */
- // 3ware Error SCSI Error
- {0x51, 0x0b, 0x00, 0x00} // Unspecified Aborted command
+ // ATA Error SCSI Error
+ {0x01, 0x03, 0x13, 0x00}, // Address mark not found Address mark not found for data field
+ {0x04, 0x0b, 0x00, 0x00}, // Aborted command Aborted command
+ {0x10, 0x0b, 0x14, 0x00}, // ID not found Recorded entity not found
+ {0x40, 0x03, 0x11, 0x00}, // Uncorrectable ECC error Unrecovered read error
+ {0x61, 0x04, 0x00, 0x00}, // Device fault Hardware error
+ {0x84, 0x0b, 0x47, 0x00}, // Data CRC error SCSI parity error
+ {0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command
+ {0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command
+ {0x37, 0x02, 0x04, 0x00}, // Unit offline Not ready
+ {0x09, 0x02, 0x04, 0x00}, // Unrecovered disk error Not ready
+
+ /* Codes for older firmware */
+ // 3ware Error SCSI Error
+ {0x51, 0x0b, 0x00, 0x00} // Unspecified Aborted command
};
/* Control register bit definitions */
@@ -127,9 +128,9 @@ static unsigned char tw_sense_table[][4] =
#define TW_CONTROL_ENABLE_INTERRUPTS 0x00000080
#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040
#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020
-#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
-#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
-#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
+#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
+#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
+#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
#define TW_CONTROL_CLEAR_SBUF_WRITE_ERROR 0x00000008
/* Status register bit definitions */
@@ -151,8 +152,8 @@ static unsigned char tw_sense_table[][4] =
#define TW_STATUS_CLEARABLE_BITS 0x00D00000
#define TW_STATUS_EXPECTED_BITS 0x00002000
#define TW_STATUS_UNEXPECTED_BITS 0x00F00008
-#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008
-#define TW_STATUS_VALID_INTERRUPT 0x00DF0008
+#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008
+#define TW_STATUS_VALID_INTERRUPT 0x00DF0008
/* RESPONSE QUEUE BIT DEFINITIONS */
#define TW_RESPONSE_ID_MASK 0x00000FF0
@@ -178,38 +179,33 @@ static unsigned char tw_sense_table[][4] =
#define TW_OP_SECTOR_INFO 0x1a
#define TW_OP_AEN_LISTEN 0x1c
#define TW_OP_FLUSH_CACHE 0x0e
-#define TW_CMD_PACKET 0x1d
+#define TW_CMD_PACKET 0x1d
#define TW_CMD_PACKET_WITH_DATA 0x1f
/* Asynchronous Event Notification (AEN) Codes */
#define TW_AEN_QUEUE_EMPTY 0x0000
-#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_SOFT_RESET 0x0001
#define TW_AEN_DEGRADED_MIRROR 0x0002
#define TW_AEN_CONTROLLER_ERROR 0x0003
#define TW_AEN_REBUILD_FAIL 0x0004
#define TW_AEN_REBUILD_DONE 0x0005
-#define TW_AEN_QUEUE_FULL 0x00ff
+#define TW_AEN_QUEUE_FULL 0x00ff
#define TW_AEN_TABLE_UNDEFINED 0x15
#define TW_AEN_APORT_TIMEOUT 0x0009
#define TW_AEN_DRIVE_ERROR 0x000A
-#define TW_AEN_SMART_FAIL 0x000F
-#define TW_AEN_SBUF_FAIL 0x0024
-
-/* Phase defines */
-#define TW_PHASE_INITIAL 0
-#define TW_PHASE_SINGLE 1
-#define TW_PHASE_SGLIST 2
+#define TW_AEN_SMART_FAIL 0x000F
+#define TW_AEN_SBUF_FAIL 0x0024
/* Misc defines */
#define TW_ALIGNMENT_6000 64 /* 64 bytes */
-#define TW_ALIGNMENT_7000 4 /* 4 bytes */
+#define TW_ALIGNMENT_7000 4 /* 4 bytes */
#define TW_MAX_UNITS 16
#define TW_COMMAND_ALIGNMENT_MASK 0x1ff
#define TW_INIT_MESSAGE_CREDITS 0x100
#define TW_INIT_COMMAND_PACKET_SIZE 0x3
-#define TW_POLL_MAX_RETRIES 20000
+#define TW_POLL_MAX_RETRIES 20000
#define TW_MAX_SGL_LENGTH 62
-#define TW_ATA_PASS_SGL_MAX 60
+#define TW_ATA_PASS_SGL_MAX 60
#define TW_Q_LENGTH 256
#define TW_Q_START 0
#define TW_MAX_SLOT 32
@@ -220,21 +216,20 @@ static unsigned char tw_sense_table[][4] =
chrdev ioctl, one for
internal aen post */
#define TW_BLOCK_SIZE 0x200 /* 512-byte blocks */
-#define TW_IOCTL 0x80
-#define TW_UNIT_ONLINE 1
-#define TW_IN_INTR 1
-#define TW_IN_RESET 2
-#define TW_IN_CHRDEV_IOCTL 3
-#define TW_MAX_SECTORS 256
+#define TW_IOCTL 0x80
+#define TW_UNIT_ONLINE 1
+#define TW_IN_INTR 1
+#define TW_IN_RESET 2
+#define TW_IN_CHRDEV_IOCTL 3
+#define TW_MAX_SECTORS 256
#define TW_MAX_IOCTL_SECTORS 512
-#define TW_AEN_WAIT_TIME 1000
-#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
-#define TW_ISR_DONT_COMPLETE 2
-#define TW_ISR_DONT_RESULT 3
-#define TW_IOCTL_TIMEOUT 25 /* 25 seconds */
-#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
-#define TW_IOCTL_CHRDEV_FREE -1
-#define TW_DMA_MASK DMA_BIT_MASK(32)
+#define TW_AEN_WAIT_TIME 1000
+#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
+#define TW_ISR_DONT_COMPLETE 2
+#define TW_ISR_DONT_RESULT 3
+#define TW_IOCTL_TIMEOUT 25 /* 25 seconds */
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
#define TW_MAX_CDB_LEN 16
/* Bitmask macros to eliminate bitfields */
@@ -255,26 +250,35 @@ static unsigned char tw_sense_table[][4] =
#define TW_STATUS_REG_ADDR(x) (x->base_addr + 0x4)
#define TW_COMMAND_QUEUE_REG_ADDR(x) (x->base_addr + 0x8)
#define TW_RESPONSE_QUEUE_REG_ADDR(x) (x->base_addr + 0xC)
-#define TW_CLEAR_ALL_INTERRUPTS(x) (outl(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_CLEAR_ATTENTION_INTERRUPT(x) (outl(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_CLEAR_HOST_INTERRUPT(x) (outl(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_DISABLE_INTERRUPTS(x) (outl(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
-#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) (outl(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
-#define TW_MASK_COMMAND_INTERRUPT(x) (outl(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_UNMASK_COMMAND_INTERRUPT(x) (outl(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
-#define TW_SOFT_RESET(x) (outl(TW_CONTROL_ISSUE_SOFT_RESET | \
- TW_CONTROL_CLEAR_HOST_INTERRUPT | \
- TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
- TW_CONTROL_MASK_COMMAND_INTERRUPT | \
- TW_CONTROL_MASK_RESPONSE_INTERRUPT | \
- TW_CONTROL_CLEAR_ERROR_STATUS | \
- TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
-#define TW_STATUS_ERRORS(x) \
- (((x & TW_STATUS_PCI_ABORT) || \
- (x & TW_STATUS_PCI_PARITY_ERROR) || \
- (x & TW_STATUS_QUEUE_ERROR) || \
- (x & TW_STATUS_MICROCONTROLLER_ERROR)) && \
- (x & TW_STATUS_MICROCONTROLLER_READY))
+#define TW_CLEAR_ALL_INTERRUPTS(x) \
+ (outl(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_ATTENTION_INTERRUPT(x) \
+ (outl(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_HOST_INTERRUPT(x) \
+ (outl(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_DISABLE_INTERRUPTS(x) \
+ (outl(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) \
+ (outl(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
+ TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | \
+ TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_MASK_COMMAND_INTERRUPT(x) \
+ (outl(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_UNMASK_COMMAND_INTERRUPT(x) \
+ (outl(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_SOFT_RESET(x) (outl(TW_CONTROL_ISSUE_SOFT_RESET | \
+ TW_CONTROL_CLEAR_HOST_INTERRUPT | \
+ TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
+ TW_CONTROL_MASK_COMMAND_INTERRUPT | \
+ TW_CONTROL_MASK_RESPONSE_INTERRUPT | \
+ TW_CONTROL_CLEAR_ERROR_STATUS | \
+ TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_STATUS_ERRORS(x) \
+ (((x & TW_STATUS_PCI_ABORT) || \
+ (x & TW_STATUS_PCI_PARITY_ERROR) || \
+ (x & TW_STATUS_QUEUE_ERROR) || \
+ (x & TW_STATUS_MICROCONTROLLER_ERROR)) && \
+ (x & TW_STATUS_MICROCONTROLLER_READY))
#ifdef TW_DEBUG
#define dprintk(msg...) printk(msg)
@@ -344,7 +348,7 @@ typedef struct TAG_TW_New_Ioctl {
unsigned int data_buffer_length;
unsigned char padding [508];
TW_Command firmware_command;
- char data_buffer[1];
+ char data_buffer[];
} TW_New_Ioctl;
/* GetParam descriptor */
@@ -392,6 +396,8 @@ typedef struct TAG_TW_Passthru
unsigned char padding[12];
} TW_Passthru;
+#pragma pack()
+
typedef struct TAG_TW_Device_Extension {
u32 base_addr;
unsigned long *alignment_virtual_address[TW_Q_LENGTH];
@@ -430,6 +436,4 @@ typedef struct TAG_TW_Device_Extension {
wait_queue_head_t ioctl_wqueue;
} TW_Device_Extension;
-#pragma pack()
-
#endif /* _3W_XXXX_H */
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index f672491774eb..71b7ac027f48 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -1,23 +1,10 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
+// SPDX-License-Identifier: GPL-2.0-or-later
/* NCR (or Symbios) 53c700 and 53c700-66 Driver
*
* Copyright (C) 2001 by James.Bottomley@HansenPartnership.com
**-----------------------------------------------------------------------------
**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
*/
@@ -128,10 +115,9 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/device.h>
+#include <linux/pgtable.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
-#include <asm/pgtable.h>
#include <asm/byteorder.h>
#include <scsi/scsi.h>
@@ -169,17 +155,16 @@ MODULE_LICENSE("GPL");
STATIC int NCR_700_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *);
STATIC int NCR_700_abort(struct scsi_cmnd * SCpnt);
-STATIC int NCR_700_bus_reset(struct scsi_cmnd * SCpnt);
STATIC int NCR_700_host_reset(struct scsi_cmnd * SCpnt);
STATIC void NCR_700_chip_setup(struct Scsi_Host *host);
STATIC void NCR_700_chip_reset(struct Scsi_Host *host);
-STATIC int NCR_700_slave_alloc(struct scsi_device *SDpnt);
-STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt);
-STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt);
-static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth, int reason);
-static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth);
+STATIC int NCR_700_sdev_init(struct scsi_device *SDpnt);
+STATIC int NCR_700_sdev_configure(struct scsi_device *SDpnt,
+ struct queue_limits *lim);
+STATIC void NCR_700_sdev_destroy(struct scsi_device *SDpnt);
+static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth);
-STATIC struct device_attribute *NCR_700_dev_attrs[];
+STATIC const struct attribute_group *NCR_700_dev_groups[];
STATIC struct scsi_transport_template *NCR_700_transport_template = NULL;
@@ -284,6 +269,27 @@ NCR_700_get_SXFER(struct scsi_device *SDp)
spi_period(SDp->sdev_target));
}
+static inline dma_addr_t virt_to_dma(struct NCR_700_Host_Parameters *h, void *p)
+{
+ return h->pScript + ((uintptr_t)p - (uintptr_t)h->script);
+}
+
+static inline void dma_sync_to_dev(struct NCR_700_Host_Parameters *h,
+ void *addr, size_t size)
+{
+ if (h->noncoherent)
+ dma_sync_single_for_device(h->dev, virt_to_dma(h, addr),
+ size, DMA_BIDIRECTIONAL);
+}
+
+static inline void dma_sync_from_dev(struct NCR_700_Host_Parameters *h,
+ void *addr, size_t size)
+{
+ if (h->noncoherent)
+ dma_sync_single_for_device(h->dev, virt_to_dma(h, addr), size,
+ DMA_BIDIRECTIONAL);
+}
+
struct Scsi_Host *
NCR_700_detect(struct scsi_host_template *tpnt,
struct NCR_700_Host_Parameters *hostdata, struct device *dev)
@@ -295,13 +301,17 @@ NCR_700_detect(struct scsi_host_template *tpnt,
static int banner = 0;
int j;
- if(tpnt->sdev_attrs == NULL)
- tpnt->sdev_attrs = NCR_700_dev_attrs;
+ if (tpnt->sdev_groups == NULL)
+ tpnt->sdev_groups = NCR_700_dev_groups;
- memory = dma_alloc_noncoherent(hostdata->dev, TOTAL_MEM_SIZE,
- &pScript, GFP_KERNEL);
- if(memory == NULL) {
- printk(KERN_ERR "53c700: Failed to allocate memory for driver, detatching\n");
+ memory = dma_alloc_coherent(dev, TOTAL_MEM_SIZE, &pScript, GFP_KERNEL);
+ if (!memory) {
+ hostdata->noncoherent = 1;
+ memory = dma_alloc_noncoherent(dev, TOTAL_MEM_SIZE, &pScript,
+ DMA_BIDIRECTIONAL, GFP_KERNEL);
+ }
+ if (!memory) {
+ printk(KERN_ERR "53c700: Failed to allocate memory for driver, detaching\n");
return NULL;
}
@@ -317,17 +327,14 @@ NCR_700_detect(struct scsi_host_template *tpnt,
/* Fill in the missing routines from the host template */
tpnt->queuecommand = NCR_700_queuecommand;
tpnt->eh_abort_handler = NCR_700_abort;
- tpnt->eh_bus_reset_handler = NCR_700_bus_reset;
tpnt->eh_host_reset_handler = NCR_700_host_reset;
tpnt->can_queue = NCR_700_COMMAND_SLOTS_PER_HOST;
tpnt->sg_tablesize = NCR_700_SG_SEGMENTS;
tpnt->cmd_per_lun = NCR_700_CMD_PER_LUN;
- tpnt->use_clustering = ENABLE_CLUSTERING;
- tpnt->slave_configure = NCR_700_slave_configure;
- tpnt->slave_destroy = NCR_700_slave_destroy;
- tpnt->slave_alloc = NCR_700_slave_alloc;
+ tpnt->sdev_configure = NCR_700_sdev_configure;
+ tpnt->sdev_destroy = NCR_700_sdev_destroy;
+ tpnt->sdev_init = NCR_700_sdev_init;
tpnt->change_queue_depth = NCR_700_change_queue_depth;
- tpnt->change_queue_type = NCR_700_change_queue_type;
if(tpnt->name == NULL)
tpnt->name = "53c700";
@@ -357,11 +364,11 @@ NCR_700_detect(struct scsi_host_template *tpnt,
for (j = 0; j < PATCHES; j++)
script[LABELPATCHES[j]] = bS_to_host(pScript + SCRIPT[LABELPATCHES[j]]);
/* now patch up fixed addresses. */
- script_patch_32(hostdata->dev, script, MessageLocation,
+ script_patch_32(hostdata, script, MessageLocation,
pScript + MSGOUT_OFFSET);
- script_patch_32(hostdata->dev, script, StatusAddress,
+ script_patch_32(hostdata, script, StatusAddress,
pScript + STATUS_OFFSET);
- script_patch_32(hostdata->dev, script, ReceiveMsgAddress,
+ script_patch_32(hostdata, script, ReceiveMsgAddress,
pScript + MSGIN_OFFSET);
hostdata->script = script;
@@ -413,8 +420,13 @@ NCR_700_release(struct Scsi_Host *host)
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)host->hostdata[0];
- dma_free_noncoherent(hostdata->dev, TOTAL_MEM_SIZE,
- hostdata->script, hostdata->pScript);
+ if (hostdata->noncoherent)
+ dma_free_noncoherent(hostdata->dev, TOTAL_MEM_SIZE,
+ hostdata->script, hostdata->pScript,
+ DMA_BIDIRECTIONAL);
+ else
+ dma_free_coherent(hostdata->dev, TOTAL_MEM_SIZE,
+ hostdata->script, hostdata->pScript);
return 1;
}
@@ -593,19 +605,14 @@ NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata,
hostdata->cmd = NULL;
if(SCp != NULL) {
- struct NCR_700_command_slot *slot =
+ struct NCR_700_command_slot *slot =
(struct NCR_700_command_slot *)SCp->host_scribble;
-
+
dma_unmap_single(hostdata->dev, slot->pCmd,
MAX_COMMAND_SIZE, DMA_TO_DEVICE);
if (slot->flags == NCR_700_FLAG_AUTOSENSE) {
char *cmnd = NCR_700_get_sense_cmnd(SCp->device);
-#ifdef NCR_700_DEBUG
- printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is\n",
- SCp, SCp->cmnd[7], result);
- scsi_print_sense("53c700", SCp);
-#endif
dma_unmap_single(hostdata->dev, slot->dma_handle,
SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
/* restore the old result if the request sense was
@@ -628,7 +635,7 @@ NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata,
SCp->host_scribble = NULL;
SCp->result = result;
- SCp->scsi_done(SCp);
+ scsi_done(SCp);
} else {
printk(KERN_ERR "53c700: SCSI DONE HAS NULL SCp\n");
}
@@ -827,8 +834,8 @@ process_extended_message(struct Scsi_Host *host,
shost_printk(KERN_WARNING, host,
"Unexpected SDTR msg\n");
hostdata->msgout[0] = A_REJECT_MSG;
- dma_cache_sync(hostdata->dev, hostdata->msgout, 1, DMA_TO_DEVICE);
- script_patch_16(hostdata->dev, hostdata->script,
+ dma_sync_to_dev(hostdata, hostdata->msgout, 1);
+ script_patch_16(hostdata, hostdata->script,
MessageCount, 1);
/* SendMsgOut returns, so set up the return
* address */
@@ -840,9 +847,8 @@ process_extended_message(struct Scsi_Host *host,
printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, Rejecting\n",
host->host_no, pun, lun);
hostdata->msgout[0] = A_REJECT_MSG;
- dma_cache_sync(hostdata->dev, hostdata->msgout, 1, DMA_TO_DEVICE);
- script_patch_16(hostdata->dev, hostdata->script, MessageCount,
- 1);
+ dma_sync_to_dev(hostdata, hostdata->msgout, 1);
+ script_patch_16(hostdata, hostdata->script, MessageCount, 1);
resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
break;
@@ -855,9 +861,8 @@ process_extended_message(struct Scsi_Host *host,
printk("\n");
/* just reject it */
hostdata->msgout[0] = A_REJECT_MSG;
- dma_cache_sync(hostdata->dev, hostdata->msgout, 1, DMA_TO_DEVICE);
- script_patch_16(hostdata->dev, hostdata->script, MessageCount,
- 1);
+ dma_sync_to_dev(hostdata, hostdata->msgout, 1);
+ script_patch_16(hostdata, hostdata->script, MessageCount, 1);
/* SendMsgOut returns, so set up the return
* address */
resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
@@ -907,8 +912,10 @@ process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata
/* we're done negotiating */
NCR_700_set_tag_neg_state(SCp->device, NCR_700_FINISHED_TAG_NEGOTIATION);
hostdata->tag_negotiated &= ~(1<<scmd_id(SCp));
+
SCp->device->tagged_supported = 0;
- scsi_deactivate_tcq(SCp->device, host->cmd_per_lun);
+ SCp->device->simple_tags = 0;
+ scsi_change_queue_depth(SCp->device, host->cmd_per_lun);
} else {
shost_printk(KERN_WARNING, host,
"(%d:%d) Unexpected REJECT Message %s\n",
@@ -938,9 +945,8 @@ process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata
printk("\n");
/* just reject it */
hostdata->msgout[0] = A_REJECT_MSG;
- dma_cache_sync(hostdata->dev, hostdata->msgout, 1, DMA_TO_DEVICE);
- script_patch_16(hostdata->dev, hostdata->script, MessageCount,
- 1);
+ dma_sync_to_dev(hostdata, hostdata->msgout, 1);
+ script_patch_16(hostdata, hostdata->script, MessageCount, 1);
/* SendMsgOut returns, so set up the return
* address */
resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
@@ -949,7 +955,7 @@ process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata
}
NCR_700_writel(temp, host, TEMP_REG);
/* set us up to receive another message */
- dma_cache_sync(hostdata->dev, hostdata->msgin, MSG_ARRAY_SIZE, DMA_FROM_DEVICE);
+ dma_sync_from_dev(hostdata, hostdata->msgin, MSG_ARRAY_SIZE);
return resume_offset;
}
@@ -973,10 +979,10 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
if (NCR_700_get_tag_neg_state(SCp->device) == NCR_700_DURING_TAG_NEGOTIATION)
NCR_700_set_tag_neg_state(SCp->device,
NCR_700_FINISHED_TAG_NEGOTIATION);
-
- /* check for contingent allegiance contitions */
- if(status_byte(hostdata->status[0]) == CHECK_CONDITION ||
- status_byte(hostdata->status[0]) == COMMAND_TERMINATED) {
+
+ /* check for contingent allegiance conditions */
+ if (hostdata->status[0] == SAM_STAT_CHECK_CONDITION ||
+ hostdata->status[0] == SAM_STAT_COMMAND_TERMINATED) {
struct NCR_700_command_slot *slot =
(struct NCR_700_command_slot *)SCp->host_scribble;
if(slot->flags == NCR_700_FLAG_AUTOSENSE) {
@@ -1006,7 +1012,7 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
DMA_TO_DEVICE);
cmnd[0] = REQUEST_SENSE;
- cmnd[1] = (SCp->device->lun & 0x7) << 5;
+ cmnd[1] = (lun & 0x7) << 5;
cmnd[2] = 0;
cmnd[3] = 0;
cmnd[4] = SCSI_SENSE_BUFFERSIZE;
@@ -1029,8 +1035,8 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
slot->SG[1].ins = bS_to_host(SCRIPT_RETURN);
slot->SG[1].pAddr = 0;
slot->resume_offset = hostdata->pScript;
- dma_cache_sync(hostdata->dev, slot->SG, sizeof(slot->SG[0])*2, DMA_TO_DEVICE);
- dma_cache_sync(hostdata->dev, SCp->sense_buffer, SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ dma_sync_to_dev(hostdata, slot->SG, sizeof(slot->SG[0])*2);
+ dma_sync_from_dev(hostdata, SCp->sense_buffer, SCSI_SENSE_BUFFERSIZE);
/* queue the command for reissue */
slot->state = NCR_700_SLOT_QUEUED;
@@ -1112,7 +1118,9 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
BUG();
}
if(hostdata->msgin[1] == A_SIMPLE_TAG_MSG) {
- struct scsi_cmnd *SCp = scsi_find_tag(SDp, hostdata->msgin[2]);
+ struct scsi_cmnd *SCp;
+
+ SCp = scsi_host_find_tag(SDp->host, hostdata->msgin[2]);
if(unlikely(SCp == NULL)) {
printk(KERN_ERR "scsi%d: (%d:%d) no saved request for tag %d\n",
host->host_no, reselection_id, lun, hostdata->msgin[2]);
@@ -1124,7 +1132,9 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
"reselection is tag %d, slot %p(%d)\n",
hostdata->msgin[2], slot, slot->tag);
} else {
- struct scsi_cmnd *SCp = scsi_find_tag(SDp, SCSI_NO_TAG);
+ struct NCR_700_Device_Parameters *p = SDp->hostdata;
+ struct scsi_cmnd *SCp = p->current_cmnd;
+
if(unlikely(SCp == NULL)) {
sdev_printk(KERN_ERR, SDp,
"no saved request for untagged cmd\n");
@@ -1146,11 +1156,11 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
hostdata->cmd = slot->cmnd;
/* re-patch for this command */
- script_patch_32_abs(hostdata->dev, hostdata->script,
+ script_patch_32_abs(hostdata, hostdata->script,
CommandAddress, slot->pCmd);
- script_patch_16(hostdata->dev, hostdata->script,
+ script_patch_16(hostdata, hostdata->script,
CommandCount, slot->cmnd->cmd_len);
- script_patch_32_abs(hostdata->dev, hostdata->script,
+ script_patch_32_abs(hostdata, hostdata->script,
SGScriptStartAddress,
to32bit(&slot->pSG[0].ins));
@@ -1161,14 +1171,14 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
* should therefore always clear ACK */
NCR_700_writeb(NCR_700_get_SXFER(hostdata->cmd->device),
host, SXFER_REG);
- dma_cache_sync(hostdata->dev, hostdata->msgin,
- MSG_ARRAY_SIZE, DMA_FROM_DEVICE);
- dma_cache_sync(hostdata->dev, hostdata->msgout,
- MSG_ARRAY_SIZE, DMA_TO_DEVICE);
+ dma_sync_from_dev(hostdata, hostdata->msgin,
+ MSG_ARRAY_SIZE);
+ dma_sync_to_dev(hostdata, hostdata->msgout,
+ MSG_ARRAY_SIZE);
/* I'm just being paranoid here, the command should
* already have been flushed from the cache */
- dma_cache_sync(hostdata->dev, slot->cmnd->cmnd,
- slot->cmnd->cmd_len, DMA_TO_DEVICE);
+ dma_sync_to_dev(hostdata, slot->cmnd->cmnd,
+ slot->cmnd->cmd_len);
@@ -1231,8 +1241,7 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
hostdata->reselection_id = reselection_id;
/* just in case we have a stale simple tag message, clear it */
hostdata->msgin[1] = 0;
- dma_cache_sync(hostdata->dev, hostdata->msgin,
- MSG_ARRAY_SIZE, DMA_BIDIRECTIONAL);
+ dma_sync_to_dev(hostdata, hostdata->msgin, MSG_ARRAY_SIZE);
if(hostdata->tag_negotiated & (1<<reselection_id)) {
resume_offset = hostdata->pScript + Ent_GetReselectionWithTag;
} else {
@@ -1346,8 +1355,7 @@ process_selection(struct Scsi_Host *host, __u32 dsp)
hostdata->cmd = NULL;
/* clear any stale simple tag message */
hostdata->msgin[1] = 0;
- dma_cache_sync(hostdata->dev, hostdata->msgin, MSG_ARRAY_SIZE,
- DMA_BIDIRECTIONAL);
+ dma_sync_to_dev(hostdata, hostdata->msgin, MSG_ARRAY_SIZE);
if(id == 0xff) {
/* Selected as target, Ignore */
@@ -1397,7 +1405,8 @@ NCR_700_start_command(struct scsi_cmnd *SCp)
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
__u16 count = 1; /* for IDENTIFY message */
-
+ u8 lun = SCp->device->lun;
+
if(hostdata->state != NCR_700_HOST_FREE) {
/* keep this inside the lock to close the race window where
* the running command finishes on another CPU while we don't
@@ -1416,7 +1425,7 @@ NCR_700_start_command(struct scsi_cmnd *SCp)
hostdata->msgout[0] = NCR_700_identify((SCp->cmnd[0] != REQUEST_SENSE &&
slot->flags != NCR_700_FLAG_AUTOSENSE),
- SCp->device->lun);
+ lun);
/* for INQUIRY or REQUEST_SENSE commands, we cannot be sure
* if the negotiated transfer parameters still hold, so
* always renegotiate them */
@@ -1432,7 +1441,7 @@ NCR_700_start_command(struct scsi_cmnd *SCp)
if((hostdata->tag_negotiated & (1<<scmd_id(SCp)))
&& (slot->tag != SCSI_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE &&
slot->flags != NCR_700_FLAG_AUTOSENSE)) {
- count += scsi_populate_tag_msg(SCp, &hostdata->msgout[count]);
+ count += spi_populate_tag_msg(&hostdata->msgout[count], SCp);
}
if(hostdata->fast &&
@@ -1443,30 +1452,26 @@ NCR_700_start_command(struct scsi_cmnd *SCp)
NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION);
}
- script_patch_16(hostdata->dev, hostdata->script, MessageCount, count);
-
+ script_patch_16(hostdata, hostdata->script, MessageCount, count);
- script_patch_ID(hostdata->dev, hostdata->script,
- Device_ID, 1<<scmd_id(SCp));
+ script_patch_ID(hostdata, hostdata->script, Device_ID, 1<<scmd_id(SCp));
- script_patch_32_abs(hostdata->dev, hostdata->script, CommandAddress,
+ script_patch_32_abs(hostdata, hostdata->script, CommandAddress,
slot->pCmd);
- script_patch_16(hostdata->dev, hostdata->script, CommandCount,
- SCp->cmd_len);
+ script_patch_16(hostdata, hostdata->script, CommandCount, SCp->cmd_len);
/* finally plumb the beginning of the SG list into the script
* */
- script_patch_32_abs(hostdata->dev, hostdata->script,
+ script_patch_32_abs(hostdata, hostdata->script,
SGScriptStartAddress, to32bit(&slot->pSG[0].ins));
NCR_700_clear_fifo(SCp->device->host);
if(slot->resume_offset == 0)
slot->resume_offset = hostdata->pScript;
/* now perform all the writebacks and invalidates */
- dma_cache_sync(hostdata->dev, hostdata->msgout, count, DMA_TO_DEVICE);
- dma_cache_sync(hostdata->dev, hostdata->msgin, MSG_ARRAY_SIZE,
- DMA_FROM_DEVICE);
- dma_cache_sync(hostdata->dev, SCp->cmnd, SCp->cmd_len, DMA_TO_DEVICE);
- dma_cache_sync(hostdata->dev, hostdata->status, 1, DMA_FROM_DEVICE);
+ dma_sync_to_dev(hostdata, hostdata->msgout, count);
+ dma_sync_from_dev(hostdata, hostdata->msgin, MSG_ARRAY_SIZE);
+ dma_sync_to_dev(hostdata, SCp->cmnd, SCp->cmd_len);
+ dma_sync_from_dev(hostdata, hostdata->status, 1);
/* set the synchronous period/offset */
NCR_700_writeb(NCR_700_get_SXFER(SCp->device),
@@ -1501,11 +1506,8 @@ NCR_700_intr(int irq, void *dev_id)
__u8 sstat0 = 0, dstat = 0;
__u32 dsp;
struct scsi_cmnd *SCp = hostdata->cmd;
- enum NCR_700_Host_State state;
handled = 1;
- state = hostdata->state;
- SCp = hostdata->cmd;
if(istat & SCSI_INT_PENDING) {
udelay(10);
@@ -1569,7 +1571,7 @@ NCR_700_intr(int irq, void *dev_id)
* deadlock on the
* hostdata->state_lock */
SCp->result = DID_RESET << 16;
- SCp->scsi_done(SCp);
+ scsi_done(SCp);
}
mdelay(25);
NCR_700_chip_setup(host);
@@ -1597,7 +1599,7 @@ NCR_700_intr(int irq, void *dev_id)
printk("scsi%d (%d:%d) PHASE MISMATCH IN SEND MESSAGE %d remain, return %p[%04x], phase %s\n", host->host_no, pun, lun, count, (void *)temp, temp - hostdata->pScript, sbcl_to_string(NCR_700_readb(host, SBCL_REG)));
#endif
resume_offset = hostdata->pScript + Ent_SendMessagePhaseMismatch;
- } else if(dsp >= to32bit(&slot->pSG[0].ins) &&
+ } else if (slot && dsp >= to32bit(&slot->pSG[0].ins) &&
dsp <= to32bit(&slot->pSG[NCR_700_SG_SEGMENTS].ins)) {
int data_transfer = NCR_700_readl(host, DBC_REG) & 0xffffff;
int SGcount = (dsp - to32bit(&slot->pSG[0].ins))/sizeof(struct NCR_700_SG_List);
@@ -1642,7 +1644,7 @@ NCR_700_intr(int irq, void *dev_id)
slot->SG[i].ins = bS_to_host(SCRIPT_NOP);
slot->SG[i].pAddr = 0;
}
- dma_cache_sync(hostdata->dev, slot->SG, sizeof(slot->SG), DMA_TO_DEVICE);
+ dma_sync_to_dev(hostdata, slot->SG, sizeof(slot->SG));
/* and pretend we disconnected after
* the command phase */
resume_offset = hostdata->pScript + Ent_MsgInDuringData;
@@ -1749,13 +1751,11 @@ NCR_700_intr(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-static int
-NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *))
+static int NCR_700_queuecommand_lck(struct scsi_cmnd *SCp)
{
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
__u32 move_ins;
- enum dma_data_direction direction;
struct NCR_700_command_slot *slot;
if(hostdata->command_slot_count >= NCR_700_COMMAND_SLOTS_PER_HOST) {
@@ -1772,7 +1772,7 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)
*/
if(NCR_700_get_depth(SCp->device) != 0
&& (!(hostdata->tag_negotiated & (1<<scmd_id(SCp)))
- || !blk_rq_tagged(SCp->request))) {
+ || !(SCp->flags & SCMD_TAGGED))) {
CDEBUG(KERN_ERR, SCp, "has non zero depth %d\n",
NCR_700_get_depth(SCp->device));
return SCSI_MLQUEUE_DEVICE_BUSY;
@@ -1791,16 +1791,13 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)
slot->cmnd = SCp;
- SCp->scsi_done = done;
SCp->host_scribble = (unsigned char *)slot;
- SCp->SCp.ptr = NULL;
- SCp->SCp.buffer = NULL;
#ifdef NCR_700_DEBUG
printk("53c700: scsi%d, command ", SCp->device->host->host_no);
scsi_print_command(SCp);
#endif
- if(blk_rq_tagged(SCp->request)
+ if ((SCp->flags & SCMD_TAGGED)
&& (hostdata->tag_negotiated &(1<<scmd_id(SCp))) == 0
&& NCR_700_get_tag_neg_state(SCp->device) == NCR_700_START_TAG_NEGOTIATION) {
scmd_printk(KERN_ERR, SCp, "Enabling Tag Command Queuing\n");
@@ -1814,21 +1811,23 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)
*
* FIXME: This will royally screw up on multiple LUN devices
* */
- if(!blk_rq_tagged(SCp->request)
+ if (!(SCp->flags & SCMD_TAGGED)
&& (hostdata->tag_negotiated &(1<<scmd_id(SCp)))) {
scmd_printk(KERN_INFO, SCp, "Disabling Tag Command Queuing\n");
hostdata->tag_negotiated &= ~(1<<scmd_id(SCp));
}
- if((hostdata->tag_negotiated &(1<<scmd_id(SCp)))
- && scsi_get_tag_type(SCp->device)) {
- slot->tag = SCp->request->tag;
+ if ((hostdata->tag_negotiated & (1<<scmd_id(SCp))) &&
+ SCp->device->simple_tags) {
+ slot->tag = scsi_cmd_to_rq(SCp)->tag;
CDEBUG(KERN_DEBUG, SCp, "sending out tag %d, slot %p\n",
slot->tag, slot);
} else {
+ struct NCR_700_Device_Parameters *p = SCp->device->hostdata;
+
slot->tag = SCSI_NO_TAG;
- /* must populate current_cmnd for scsi_find_tag to work */
- SCp->device->current_cmnd = SCp;
+ /* save current command for reselection */
+ p->current_cmnd = SCp;
}
/* sanity check: some of the commands generated by the mid-layer
* have an eccentric idea of their sc_data_direction */
@@ -1846,7 +1845,7 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)
case REQUEST_SENSE:
/* clear the internal sense magic */
SCp->cmnd[6] = 0;
- /* fall through */
+ fallthrough;
default:
/* OK, get it from the command */
switch(SCp->sc_data_direction) {
@@ -1870,7 +1869,6 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)
}
/* now build the scatter gather list */
- direction = SCp->sc_data_direction;
if(move_ins != 0) {
int i;
int sg_count;
@@ -1892,8 +1890,8 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)
}
slot->SG[i].ins = bS_to_host(SCRIPT_RETURN);
slot->SG[i].pAddr = 0;
- dma_cache_sync(hostdata->dev, slot->SG, sizeof(slot->SG), DMA_TO_DEVICE);
- DEBUG((" SETTING %08lx to %x\n",
+ dma_sync_to_dev(hostdata, slot->SG, sizeof(slot->SG));
+ DEBUG((" SETTING %p to %x\n",
(&slot->pSG[i].ins),
slot->SG[i].ins));
}
@@ -1911,9 +1909,7 @@ NCR_700_abort(struct scsi_cmnd * SCp)
{
struct NCR_700_command_slot *slot;
- scmd_printk(KERN_INFO, SCp,
- "New error handler wants to abort command\n\t");
- scsi_print_command(SCp);
+ scmd_printk(KERN_INFO, SCp, "abort command\n");
slot = (struct NCR_700_command_slot *)SCp->host_scribble;
@@ -1939,14 +1935,14 @@ NCR_700_abort(struct scsi_cmnd * SCp)
}
STATIC int
-NCR_700_bus_reset(struct scsi_cmnd * SCp)
+NCR_700_host_reset(struct scsi_cmnd * SCp)
{
DECLARE_COMPLETION_ONSTACK(complete);
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
scmd_printk(KERN_INFO, SCp,
- "New error handler wants BUS reset, cmd %p\n\t", SCp);
+ "New error handler wants HOST reset, cmd %p\n\t", SCp);
scsi_print_command(SCp);
/* In theory, eh_complete should always be null because the
@@ -1961,6 +1957,7 @@ NCR_700_bus_reset(struct scsi_cmnd * SCp)
hostdata->eh_complete = &complete;
NCR_700_internal_bus_reset(SCp->device->host);
+ NCR_700_chip_reset(SCp->device->host);
spin_unlock_irq(SCp->device->host->host_lock);
wait_for_completion(&complete);
@@ -1975,22 +1972,6 @@ NCR_700_bus_reset(struct scsi_cmnd * SCp)
return SUCCESS;
}
-STATIC int
-NCR_700_host_reset(struct scsi_cmnd * SCp)
-{
- scmd_printk(KERN_INFO, SCp, "New error handler wants HOST reset\n\t");
- scsi_print_command(SCp);
-
- spin_lock_irq(SCp->device->host->host_lock);
-
- NCR_700_internal_bus_reset(SCp->device->host);
- NCR_700_chip_reset(SCp->device->host);
-
- spin_unlock_irq(SCp->device->host->host_lock);
-
- return SUCCESS;
-}
-
STATIC void
NCR_700_set_period(struct scsi_target *STp, int period)
{
@@ -2037,7 +2018,7 @@ NCR_700_set_offset(struct scsi_target *STp, int offset)
}
STATIC int
-NCR_700_slave_alloc(struct scsi_device *SDp)
+NCR_700_sdev_init(struct scsi_device *SDp)
{
SDp->hostdata = kzalloc(sizeof(struct NCR_700_Device_Parameters),
GFP_KERNEL);
@@ -2049,20 +2030,17 @@ NCR_700_slave_alloc(struct scsi_device *SDp)
}
STATIC int
-NCR_700_slave_configure(struct scsi_device *SDp)
+NCR_700_sdev_configure(struct scsi_device *SDp, struct queue_limits *lim)
{
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SDp->host->hostdata[0];
/* to do here: allocate memory; build a queue_full list */
if(SDp->tagged_supported) {
- scsi_set_tag_type(SDp, MSG_ORDERED_TAG);
- scsi_activate_tcq(SDp, NCR_700_DEFAULT_TAGS);
+ scsi_change_queue_depth(SDp, NCR_700_DEFAULT_TAGS);
NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION);
- } else {
- /* initialise to default depth */
- scsi_adjust_queue_depth(SDp, 0, SDp->host->cmd_per_lun);
}
+
if(hostdata->fast) {
/* Find the correct offset and period via domain validation */
if (!spi_initial_dv(SDp->sdev_target))
@@ -2075,57 +2053,18 @@ NCR_700_slave_configure(struct scsi_device *SDp)
}
STATIC void
-NCR_700_slave_destroy(struct scsi_device *SDp)
+NCR_700_sdev_destroy(struct scsi_device *SDp)
{
kfree(SDp->hostdata);
SDp->hostdata = NULL;
}
static int
-NCR_700_change_queue_depth(struct scsi_device *SDp, int depth, int reason)
+NCR_700_change_queue_depth(struct scsi_device *SDp, int depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (depth > NCR_700_MAX_TAGS)
depth = NCR_700_MAX_TAGS;
-
- scsi_adjust_queue_depth(SDp, scsi_get_tag_type(SDp), depth);
- return depth;
-}
-
-static int NCR_700_change_queue_type(struct scsi_device *SDp, int tag_type)
-{
- int change_tag = ((tag_type ==0 && scsi_get_tag_type(SDp) != 0)
- || (tag_type != 0 && scsi_get_tag_type(SDp) == 0));
- struct NCR_700_Host_Parameters *hostdata =
- (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0];
-
- scsi_set_tag_type(SDp, tag_type);
-
- /* We have a global (per target) flag to track whether TCQ is
- * enabled, so we'll be turning it off for the entire target here.
- * our tag algorithm will fail if we mix tagged and untagged commands,
- * so quiesce the device before doing this */
- if (change_tag)
- scsi_target_quiesce(SDp->sdev_target);
-
- if (!tag_type) {
- /* shift back to the default unqueued number of commands
- * (the user can still raise this) */
- scsi_deactivate_tcq(SDp, SDp->host->cmd_per_lun);
- hostdata->tag_negotiated &= ~(1 << sdev_id(SDp));
- } else {
- /* Here, we cleared the negotiation flag above, so this
- * will force the driver to renegotiate */
- scsi_activate_tcq(SDp, SDp->queue_depth);
- if (change_tag)
- NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION);
- }
- if (change_tag)
- scsi_target_resume(SDp->sdev_target);
-
- return tag_type;
+ return scsi_change_queue_depth(SDp, depth);
}
static ssize_t
@@ -2133,7 +2072,7 @@ NCR_700_show_active_tags(struct device *dev, struct device_attribute *attr, char
{
struct scsi_device *SDp = to_scsi_device(dev);
- return snprintf(buf, 20, "%d\n", NCR_700_get_depth(SDp));
+ return sysfs_emit(buf, "%d\n", NCR_700_get_depth(SDp));
}
static struct device_attribute NCR_700_active_tags_attr = {
@@ -2144,11 +2083,13 @@ static struct device_attribute NCR_700_active_tags_attr = {
.show = NCR_700_show_active_tags,
};
-STATIC struct device_attribute *NCR_700_dev_attrs[] = {
- &NCR_700_active_tags_attr,
+STATIC struct attribute *NCR_700_dev_attrs[] = {
+ &NCR_700_active_tags_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(NCR_700_dev);
+
EXPORT_SYMBOL(NCR_700_detect);
EXPORT_SYMBOL(NCR_700_release);
EXPORT_SYMBOL(NCR_700_intr);
diff --git a/drivers/scsi/53c700.h b/drivers/scsi/53c700.h
index e06bdfeab420..2df347ca91af 100644
--- a/drivers/scsi/53c700.h
+++ b/drivers/scsi/53c700.h
@@ -1,4 +1,4 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
+/* SPDX-License-Identifier: GPL-2.0 */
/* Driver for 53c700 and 53c700-66 chips from NCR and Symbios
*
@@ -82,13 +82,14 @@ struct NCR_700_Device_Parameters {
* cmnd[1], this could be in static storage */
unsigned char cmnd[MAX_COMMAND_SIZE];
__u8 depth;
+ struct scsi_cmnd *current_cmnd; /* currently active command */
};
/* The SYNC negotiation sequence looks like:
*
* If DEV_NEGOTIATED_SYNC not set, tack and SDTR message on to the
- * initial identify for the device and set DEV_BEGIN_SYNC_NEGOTATION
+ * initial identify for the device and set DEV_BEGIN_SYNC_NEGOTIATION
* If we get an SDTR reply, work out the SXFER parameters, squirrel
* them away here, clear DEV_BEGIN_SYNC_NEGOTIATION and set
* DEV_NEGOTIATED_SYNC. If we get a REJECT msg, squirrel
@@ -207,6 +208,7 @@ struct NCR_700_Host_Parameters {
#endif
__u32 chip710:1; /* set if really a 710 not 700 */
__u32 burst_length:4; /* set to 0 to disable 710 bursting */
+ __u32 noncoherent:1; /* needs to use non-coherent DMA */
/* NOTHING BELOW HERE NEEDS ALTERING */
__u32 fast:1; /* if we can alter the SCSI bus clock
@@ -420,31 +422,33 @@ struct NCR_700_Host_Parameters {
#define NCR_710_MIN_XFERP 0
#define NCR_700_MIN_PERIOD 25 /* for SDTR message, 100ns */
-#define script_patch_32(dev, script, symbol, value) \
+#define script_patch_32(h, script, symbol, value) \
{ \
int i; \
+ dma_addr_t da = value; \
for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
- __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]) + value; \
+ __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]) + da; \
(script)[A_##symbol##_used[i]] = bS_to_host(val); \
- dma_cache_sync((dev), &(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
- DEBUG((" script, patching %s at %d to 0x%lx\n", \
- #symbol, A_##symbol##_used[i], (value))); \
+ dma_sync_to_dev((h), &(script)[A_##symbol##_used[i]], 4); \
+ DEBUG((" script, patching %s at %d to %pad\n", \
+ #symbol, A_##symbol##_used[i], &da)); \
} \
}
-#define script_patch_32_abs(dev, script, symbol, value) \
+#define script_patch_32_abs(h, script, symbol, value) \
{ \
int i; \
+ dma_addr_t da = value; \
for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
- (script)[A_##symbol##_used[i]] = bS_to_host(value); \
- dma_cache_sync((dev), &(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
- DEBUG((" script, patching %s at %d to 0x%lx\n", \
- #symbol, A_##symbol##_used[i], (value))); \
+ (script)[A_##symbol##_used[i]] = bS_to_host(da); \
+ dma_sync_to_dev((h), &(script)[A_##symbol##_used[i]], 4); \
+ DEBUG((" script, patching %s at %d to %pad\n", \
+ #symbol, A_##symbol##_used[i], &da)); \
} \
}
/* Used for patching the SCSI ID in the SELECT instruction */
-#define script_patch_ID(dev, script, symbol, value) \
+#define script_patch_ID(h, script, symbol, value) \
{ \
int i; \
for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
@@ -452,13 +456,13 @@ struct NCR_700_Host_Parameters {
val &= 0xff00ffff; \
val |= ((value) & 0xff) << 16; \
(script)[A_##symbol##_used[i]] = bS_to_host(val); \
- dma_cache_sync((dev), &(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
+ dma_sync_to_dev((h), &(script)[A_##symbol##_used[i]], 4); \
DEBUG((" script, patching ID field %s at %d to 0x%x\n", \
#symbol, A_##symbol##_used[i], val)); \
} \
}
-#define script_patch_16(dev, script, symbol, value) \
+#define script_patch_16(h, script, symbol, value) \
{ \
int i; \
for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
@@ -466,7 +470,7 @@ struct NCR_700_Host_Parameters {
val &= 0xffff0000; \
val |= ((value) & 0xffff); \
(script)[A_##symbol##_used[i]] = bS_to_host(val); \
- dma_cache_sync((dev), &(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
+ dma_sync_to_dev((h), &(script)[A_##symbol##_used[i]], 4); \
DEBUG((" script, patching short field %s at %d to 0x%x\n", \
#symbol, A_##symbol##_used[i], val)); \
} \
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
index f66c33b9ab41..a86d780d1ba4 100644
--- a/drivers/scsi/BusLogic.c
+++ b/drivers/scsi/BusLogic.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
@@ -5,14 +6,6 @@
Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
- This program is free software; you may redistribute and/or modify it under
- the terms of the GNU General Public License Version 2 as published by the
- Free Software Foundation.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for complete details.
The author respectfully requests that any modifications to this software be
sent directly to him for evaluation and testing.
@@ -26,8 +19,8 @@
*/
-#define BusLogic_DriverVersion "2.1.16"
-#define BusLogic_DriverDate "18 July 2002"
+#define blogic_drvr_version "2.1.17"
+#define blogic_drvr_date "12 September 2013"
#include <linux/module.h>
#include <linux/init.h>
@@ -43,11 +36,11 @@
#include <linux/jiffies.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/msdos_partition.h>
#include <scsi/scsicam.h>
#include <asm/dma.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -61,30 +54,31 @@
#define FAILURE (-1)
#endif
-static struct scsi_host_template Bus_Logic_template;
+static const struct scsi_host_template blogic_template;
/*
- BusLogic_DriverOptionsCount is a count of the number of BusLogic Driver
+ blogic_drvr_options_count is a count of the number of BusLogic Driver
Options specifications provided via the Linux Kernel Command Line or via
the Loadable Kernel Module Installation Facility.
*/
-static int BusLogic_DriverOptionsCount;
+static int blogic_drvr_options_count;
/*
- BusLogic_DriverOptions is an array of Driver Options structures representing
+ blogic_drvr_options is an array of Driver Options structures representing
BusLogic Driver Options specifications provided via the Linux Kernel Command
Line or via the Loadable Kernel Module Installation Facility.
*/
-static struct BusLogic_DriverOptions BusLogic_DriverOptions[BusLogic_MaxHostAdapters];
+static struct blogic_drvr_options blogic_drvr_options[BLOGIC_MAX_ADAPTERS];
/*
BusLogic can be assigned a string by insmod.
*/
+MODULE_DESCRIPTION("BusLogic MultiMaster and FlashPoint SCSI Host Adapter driver");
MODULE_LICENSE("GPL");
#ifdef MODULE
static char *BusLogic;
@@ -93,241 +87,254 @@ module_param(BusLogic, charp, 0);
/*
- BusLogic_ProbeOptions is a set of Probe Options to be applied across
+ blogic_probe_options is a set of Probe Options to be applied across
all BusLogic Host Adapters.
*/
-static struct BusLogic_ProbeOptions BusLogic_ProbeOptions;
+static struct blogic_probe_options blogic_probe_options;
/*
- BusLogic_GlobalOptions is a set of Global Options to be applied across
+ blogic_global_options is a set of Global Options to be applied across
all BusLogic Host Adapters.
*/
-static struct BusLogic_GlobalOptions BusLogic_GlobalOptions;
+static struct blogic_global_options blogic_global_options;
-static LIST_HEAD(BusLogic_host_list);
+static LIST_HEAD(blogic_host_list);
/*
- BusLogic_ProbeInfoCount is the number of entries in BusLogic_ProbeInfoList.
+ blogic_probeinfo_count is the number of entries in blogic_probeinfo_list.
*/
-static int BusLogic_ProbeInfoCount;
+static int blogic_probeinfo_count;
/*
- BusLogic_ProbeInfoList is the list of I/O Addresses and Bus Probe Information
+ blogic_probeinfo_list is the list of I/O Addresses and Bus Probe Information
to be checked for potential BusLogic Host Adapters. It is initialized by
interrogating the PCI Configuration Space on PCI machines as well as from the
list of standard BusLogic I/O Addresses.
*/
-static struct BusLogic_ProbeInfo *BusLogic_ProbeInfoList;
+static struct blogic_probeinfo *blogic_probeinfo_list;
/*
- BusLogic_CommandFailureReason holds a string identifying the reason why a
- call to BusLogic_Command failed. It is only non-NULL when BusLogic_Command
+ blogic_cmd_failure_reason holds a string identifying the reason why a
+ call to blogic_cmd failed. It is only non-NULL when blogic_cmd
returns a failure code.
*/
-static char *BusLogic_CommandFailureReason;
+static char *blogic_cmd_failure_reason;
/*
- BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
+ blogic_announce_drvr announces the Driver Version and Date, Author's
Name, Copyright Notice, and Electronic Mail Address.
*/
-static void BusLogic_AnnounceDriver(struct BusLogic_HostAdapter *HostAdapter)
+static void blogic_announce_drvr(struct blogic_adapter *adapter)
{
- BusLogic_Announce("***** BusLogic SCSI Driver Version " BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n", HostAdapter);
- BusLogic_Announce("Copyright 1995-1998 by Leonard N. Zubkoff " "<lnz@dandelion.com>\n", HostAdapter);
+ blogic_announce("***** BusLogic SCSI Driver Version " blogic_drvr_version " of " blogic_drvr_date " *****\n", adapter);
+ blogic_announce("Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>\n", adapter);
}
/*
- BusLogic_DriverInfo returns the Host Adapter Name to identify this SCSI
+ blogic_drvr_info returns the Host Adapter Name to identify this SCSI
Driver and Host Adapter.
*/
-static const char *BusLogic_DriverInfo(struct Scsi_Host *Host)
+static const char *blogic_drvr_info(struct Scsi_Host *host)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Host->hostdata;
- return HostAdapter->FullModelName;
+ struct blogic_adapter *adapter =
+ (struct blogic_adapter *) host->hostdata;
+ return adapter->full_model;
}
/*
- BusLogic_InitializeCCBs initializes a group of Command Control Blocks (CCBs)
- for Host Adapter from the BlockSize bytes located at BlockPointer. The newly
+ blogic_init_ccbs initializes a group of Command Control Blocks (CCBs)
+ for Host Adapter from the blk_size bytes located at blk_pointer. The newly
created CCBs are added to Host Adapter's free list.
*/
-static void BusLogic_InitializeCCBs(struct BusLogic_HostAdapter *HostAdapter, void *BlockPointer, int BlockSize, dma_addr_t BlockPointerHandle)
+static void blogic_init_ccbs(struct blogic_adapter *adapter, void *blk_pointer,
+ int blk_size, dma_addr_t blkp)
{
- struct BusLogic_CCB *CCB = (struct BusLogic_CCB *) BlockPointer;
+ struct blogic_ccb *ccb = (struct blogic_ccb *) blk_pointer;
unsigned int offset = 0;
- memset(BlockPointer, 0, BlockSize);
- CCB->AllocationGroupHead = BlockPointerHandle;
- CCB->AllocationGroupSize = BlockSize;
- while ((BlockSize -= sizeof(struct BusLogic_CCB)) >= 0) {
- CCB->Status = BusLogic_CCB_Free;
- CCB->HostAdapter = HostAdapter;
- CCB->DMA_Handle = (u32) BlockPointerHandle + offset;
- if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
- CCB->CallbackFunction = BusLogic_QueueCompletedCCB;
- CCB->BaseAddress = HostAdapter->FlashPointInfo.BaseAddress;
+ memset(blk_pointer, 0, blk_size);
+ ccb->allocgrp_head = blkp;
+ ccb->allocgrp_size = blk_size;
+ while ((blk_size -= sizeof(struct blogic_ccb)) >= 0) {
+ ccb->status = BLOGIC_CCB_FREE;
+ ccb->adapter = adapter;
+ ccb->dma_handle = (u32) blkp + offset;
+ if (blogic_flashpoint_type(adapter)) {
+ ccb->callback = blogic_qcompleted_ccb;
+ ccb->base_addr = adapter->fpinfo.base_addr;
}
- CCB->Next = HostAdapter->Free_CCBs;
- CCB->NextAll = HostAdapter->All_CCBs;
- HostAdapter->Free_CCBs = CCB;
- HostAdapter->All_CCBs = CCB;
- HostAdapter->AllocatedCCBs++;
- CCB++;
- offset += sizeof(struct BusLogic_CCB);
+ ccb->next = adapter->free_ccbs;
+ ccb->next_all = adapter->all_ccbs;
+ adapter->free_ccbs = ccb;
+ adapter->all_ccbs = ccb;
+ adapter->alloc_ccbs++;
+ ccb++;
+ offset += sizeof(struct blogic_ccb);
}
}
/*
- BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter.
+ blogic_create_initccbs allocates the initial CCBs for Host Adapter.
*/
-static bool __init BusLogic_CreateInitialCCBs(struct BusLogic_HostAdapter *HostAdapter)
+static bool __init blogic_create_initccbs(struct blogic_adapter *adapter)
{
- int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(struct BusLogic_CCB);
- void *BlockPointer;
- dma_addr_t BlockPointerHandle;
- while (HostAdapter->AllocatedCCBs < HostAdapter->InitialCCBs) {
- BlockPointer = pci_alloc_consistent(HostAdapter->PCI_Device, BlockSize, &BlockPointerHandle);
- if (BlockPointer == NULL) {
- BusLogic_Error("UNABLE TO ALLOCATE CCB GROUP - DETACHING\n", HostAdapter);
+ int blk_size = BLOGIC_CCB_GRP_ALLOCSIZE * sizeof(struct blogic_ccb);
+ void *blk_pointer;
+ dma_addr_t blkp;
+
+ while (adapter->alloc_ccbs < adapter->initccbs) {
+ blk_pointer = dma_alloc_coherent(&adapter->pci_device->dev,
+ blk_size, &blkp, GFP_KERNEL);
+ if (blk_pointer == NULL) {
+ blogic_err("UNABLE TO ALLOCATE CCB GROUP - DETACHING\n",
+ adapter);
return false;
}
- BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize, BlockPointerHandle);
+ blogic_init_ccbs(adapter, blk_pointer, blk_size, blkp);
}
return true;
}
/*
- BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter.
+ blogic_destroy_ccbs deallocates the CCBs for Host Adapter.
*/
-static void BusLogic_DestroyCCBs(struct BusLogic_HostAdapter *HostAdapter)
+static void blogic_destroy_ccbs(struct blogic_adapter *adapter)
{
- struct BusLogic_CCB *NextCCB = HostAdapter->All_CCBs, *CCB, *Last_CCB = NULL;
- HostAdapter->All_CCBs = NULL;
- HostAdapter->Free_CCBs = NULL;
- while ((CCB = NextCCB) != NULL) {
- NextCCB = CCB->NextAll;
- if (CCB->AllocationGroupHead) {
- if (Last_CCB)
- pci_free_consistent(HostAdapter->PCI_Device, Last_CCB->AllocationGroupSize, Last_CCB, Last_CCB->AllocationGroupHead);
- Last_CCB = CCB;
+ struct blogic_ccb *next_ccb = adapter->all_ccbs, *ccb, *lastccb = NULL;
+ adapter->all_ccbs = NULL;
+ adapter->free_ccbs = NULL;
+ while ((ccb = next_ccb) != NULL) {
+ next_ccb = ccb->next_all;
+ if (ccb->allocgrp_head) {
+ if (lastccb)
+ dma_free_coherent(&adapter->pci_device->dev,
+ lastccb->allocgrp_size, lastccb,
+ lastccb->allocgrp_head);
+ lastccb = ccb;
}
}
- if (Last_CCB)
- pci_free_consistent(HostAdapter->PCI_Device, Last_CCB->AllocationGroupSize, Last_CCB, Last_CCB->AllocationGroupHead);
+ if (lastccb)
+ dma_free_coherent(&adapter->pci_device->dev,
+ lastccb->allocgrp_size, lastccb,
+ lastccb->allocgrp_head);
}
/*
- BusLogic_CreateAdditionalCCBs allocates Additional CCBs for Host Adapter. If
+ blogic_create_addlccbs allocates Additional CCBs for Host Adapter. If
allocation fails and there are no remaining CCBs available, the Driver Queue
Depth is decreased to a known safe value to avoid potential deadlocks when
multiple host adapters share the same IRQ Channel.
*/
-static void BusLogic_CreateAdditionalCCBs(struct BusLogic_HostAdapter *HostAdapter, int AdditionalCCBs, bool SuccessMessageP)
+static void blogic_create_addlccbs(struct blogic_adapter *adapter,
+ int addl_ccbs, bool print_success)
{
- int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(struct BusLogic_CCB);
- int PreviouslyAllocated = HostAdapter->AllocatedCCBs;
- void *BlockPointer;
- dma_addr_t BlockPointerHandle;
- if (AdditionalCCBs <= 0)
+ int blk_size = BLOGIC_CCB_GRP_ALLOCSIZE * sizeof(struct blogic_ccb);
+ int prev_alloc = adapter->alloc_ccbs;
+ void *blk_pointer;
+ dma_addr_t blkp;
+ if (addl_ccbs <= 0)
return;
- while (HostAdapter->AllocatedCCBs - PreviouslyAllocated < AdditionalCCBs) {
- BlockPointer = pci_alloc_consistent(HostAdapter->PCI_Device, BlockSize, &BlockPointerHandle);
- if (BlockPointer == NULL)
+ while (adapter->alloc_ccbs - prev_alloc < addl_ccbs) {
+ blk_pointer = dma_alloc_coherent(&adapter->pci_device->dev,
+ blk_size, &blkp, GFP_KERNEL);
+ if (blk_pointer == NULL)
break;
- BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize, BlockPointerHandle);
+ blogic_init_ccbs(adapter, blk_pointer, blk_size, blkp);
}
- if (HostAdapter->AllocatedCCBs > PreviouslyAllocated) {
- if (SuccessMessageP)
- BusLogic_Notice("Allocated %d additional CCBs (total now %d)\n", HostAdapter, HostAdapter->AllocatedCCBs - PreviouslyAllocated, HostAdapter->AllocatedCCBs);
+ if (adapter->alloc_ccbs > prev_alloc) {
+ if (print_success)
+ blogic_notice("Allocated %d additional CCBs (total now %d)\n", adapter, adapter->alloc_ccbs - prev_alloc, adapter->alloc_ccbs);
return;
}
- BusLogic_Notice("Failed to allocate additional CCBs\n", HostAdapter);
- if (HostAdapter->DriverQueueDepth > HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount) {
- HostAdapter->DriverQueueDepth = HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount;
- HostAdapter->SCSI_Host->can_queue = HostAdapter->DriverQueueDepth;
+ blogic_notice("Failed to allocate additional CCBs\n", adapter);
+ if (adapter->drvr_qdepth > adapter->alloc_ccbs - adapter->tgt_count) {
+ adapter->drvr_qdepth = adapter->alloc_ccbs - adapter->tgt_count;
+ adapter->scsi_host->can_queue = adapter->drvr_qdepth;
}
}
/*
- BusLogic_AllocateCCB allocates a CCB from Host Adapter's free list,
+ blogic_alloc_ccb allocates a CCB from Host Adapter's free list,
allocating more memory from the Kernel if necessary. The Host Adapter's
Lock should already have been acquired by the caller.
*/
-static struct BusLogic_CCB *BusLogic_AllocateCCB(struct BusLogic_HostAdapter
- *HostAdapter)
+static struct blogic_ccb *blogic_alloc_ccb(struct blogic_adapter *adapter)
{
- static unsigned long SerialNumber = 0;
- struct BusLogic_CCB *CCB;
- CCB = HostAdapter->Free_CCBs;
- if (CCB != NULL) {
- CCB->SerialNumber = ++SerialNumber;
- HostAdapter->Free_CCBs = CCB->Next;
- CCB->Next = NULL;
- if (HostAdapter->Free_CCBs == NULL)
- BusLogic_CreateAdditionalCCBs(HostAdapter, HostAdapter->IncrementalCCBs, true);
- return CCB;
- }
- BusLogic_CreateAdditionalCCBs(HostAdapter, HostAdapter->IncrementalCCBs, true);
- CCB = HostAdapter->Free_CCBs;
- if (CCB == NULL)
+ static unsigned long serial;
+ struct blogic_ccb *ccb;
+ ccb = adapter->free_ccbs;
+ if (ccb != NULL) {
+ ccb->serial = ++serial;
+ adapter->free_ccbs = ccb->next;
+ ccb->next = NULL;
+ if (adapter->free_ccbs == NULL)
+ blogic_create_addlccbs(adapter, adapter->inc_ccbs,
+ true);
+ return ccb;
+ }
+ blogic_create_addlccbs(adapter, adapter->inc_ccbs, true);
+ ccb = adapter->free_ccbs;
+ if (ccb == NULL)
return NULL;
- CCB->SerialNumber = ++SerialNumber;
- HostAdapter->Free_CCBs = CCB->Next;
- CCB->Next = NULL;
- return CCB;
+ ccb->serial = ++serial;
+ adapter->free_ccbs = ccb->next;
+ ccb->next = NULL;
+ return ccb;
}
/*
- BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+ blogic_dealloc_ccb deallocates a CCB, returning it to the Host Adapter's
free list. The Host Adapter's Lock should already have been acquired by the
caller.
*/
-static void BusLogic_DeallocateCCB(struct BusLogic_CCB *CCB)
+static void blogic_dealloc_ccb(struct blogic_ccb *ccb, int dma_unmap)
{
- struct BusLogic_HostAdapter *HostAdapter = CCB->HostAdapter;
-
- scsi_dma_unmap(CCB->Command);
- pci_unmap_single(HostAdapter->PCI_Device, CCB->SenseDataPointer,
- CCB->SenseDataLength, PCI_DMA_FROMDEVICE);
-
- CCB->Command = NULL;
- CCB->Status = BusLogic_CCB_Free;
- CCB->Next = HostAdapter->Free_CCBs;
- HostAdapter->Free_CCBs = CCB;
+ struct blogic_adapter *adapter = ccb->adapter;
+
+ if (ccb->command != NULL)
+ scsi_dma_unmap(ccb->command);
+ if (dma_unmap)
+ dma_unmap_single(&adapter->pci_device->dev, ccb->sensedata,
+ ccb->sense_datalen, DMA_FROM_DEVICE);
+
+ ccb->command = NULL;
+ ccb->status = BLOGIC_CCB_FREE;
+ ccb->next = adapter->free_ccbs;
+ adapter->free_ccbs = ccb;
}
/*
- BusLogic_Command sends the command OperationCode to HostAdapter, optionally
- providing ParameterLength bytes of ParameterData and receiving at most
- ReplyLength bytes of ReplyData; any excess reply data is received but
+ blogic_cmd sends the command opcode to adapter, optionally
+ providing paramlen bytes of param and receiving at most
+ replylen bytes of reply; any excess reply data is received but
discarded.
On success, this function returns the number of reply bytes read from
the Host Adapter (including any discarded data); on failure, it returns
-1 if the command was invalid, or -2 if a timeout occurred.
- BusLogic_Command is called exclusively during host adapter detection and
+ blogic_cmd is called exclusively during host adapter detection and
initialization, so performance and latency are not critical, and exclusive
access to the Host Adapter hardware is assumed. Once the host adapter and
driver are initialized, the only Host Adapter command that is issued is the
@@ -335,281 +342,256 @@ static void BusLogic_DeallocateCCB(struct BusLogic_CCB *CCB)
waiting for the Host Adapter Ready bit to be set in the Status Register.
*/
-static int BusLogic_Command(struct BusLogic_HostAdapter *HostAdapter, enum BusLogic_OperationCode OperationCode, void *ParameterData, int ParameterLength, void *ReplyData, int ReplyLength)
+static int blogic_cmd(struct blogic_adapter *adapter, enum blogic_opcode opcode,
+ void *param, int paramlen, void *reply, int replylen)
{
- unsigned char *ParameterPointer = (unsigned char *) ParameterData;
- unsigned char *ReplyPointer = (unsigned char *) ReplyData;
- union BusLogic_StatusRegister StatusRegister;
- union BusLogic_InterruptRegister InterruptRegister;
- unsigned long ProcessorFlags = 0;
- int ReplyBytes = 0, Result;
- long TimeoutCounter;
+ unsigned char *param_p = (unsigned char *) param;
+ unsigned char *reply_p = (unsigned char *) reply;
+ union blogic_stat_reg statusreg;
+ union blogic_int_reg intreg;
+ unsigned long processor_flag = 0;
+ int reply_b = 0, result;
+ long timeout;
/*
Clear out the Reply Data if provided.
*/
- if (ReplyLength > 0)
- memset(ReplyData, 0, ReplyLength);
+ if (replylen > 0)
+ memset(reply, 0, replylen);
/*
- If the IRQ Channel has not yet been acquired, then interrupts must be
- disabled while issuing host adapter commands since a Command Complete
- interrupt could occur if the IRQ Channel was previously enabled by another
- BusLogic Host Adapter or another driver sharing the same IRQ Channel.
+ If the IRQ Channel has not yet been acquired, then interrupts
+ must be disabled while issuing host adapter commands since a
+ Command Complete interrupt could occur if the IRQ Channel was
+ previously enabled by another BusLogic Host Adapter or another
+ driver sharing the same IRQ Channel.
*/
- if (!HostAdapter->IRQ_ChannelAcquired)
- local_irq_save(ProcessorFlags);
+ if (!adapter->irq_acquired)
+ local_irq_save(processor_flag);
/*
- Wait for the Host Adapter Ready bit to be set and the Command/Parameter
- Register Busy bit to be reset in the Status Register.
+ Wait for the Host Adapter Ready bit to be set and the
+ Command/Parameter Register Busy bit to be reset in the Status
+ Register.
*/
- TimeoutCounter = 10000;
- while (--TimeoutCounter >= 0) {
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (StatusRegister.sr.HostAdapterReady && !StatusRegister.sr.CommandParameterRegisterBusy)
+ timeout = 10000;
+ while (--timeout >= 0) {
+ statusreg.all = blogic_rdstatus(adapter);
+ if (statusreg.sr.adapter_ready && !statusreg.sr.cmd_param_busy)
break;
udelay(100);
}
- if (TimeoutCounter < 0) {
- BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
- Result = -2;
- goto Done;
+ if (timeout < 0) {
+ blogic_cmd_failure_reason =
+ "Timeout waiting for Host Adapter Ready";
+ result = -2;
+ goto done;
}
/*
- Write the OperationCode to the Command/Parameter Register.
+ Write the opcode to the Command/Parameter Register.
*/
- HostAdapter->HostAdapterCommandCompleted = false;
- BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+ adapter->adapter_cmd_complete = false;
+ blogic_setcmdparam(adapter, opcode);
/*
Write any additional Parameter Bytes.
*/
- TimeoutCounter = 10000;
- while (ParameterLength > 0 && --TimeoutCounter >= 0) {
+ timeout = 10000;
+ while (paramlen > 0 && --timeout >= 0) {
/*
- Wait 100 microseconds to give the Host Adapter enough time to determine
- whether the last value written to the Command/Parameter Register was
- valid or not. If the Command Complete bit is set in the Interrupt
- Register, then the Command Invalid bit in the Status Register will be
- reset if the Operation Code or Parameter was valid and the command
- has completed, or set if the Operation Code or Parameter was invalid.
- If the Data In Register Ready bit is set in the Status Register, then
- the Operation Code was valid, and data is waiting to be read back
- from the Host Adapter. Otherwise, wait for the Command/Parameter
- Register Busy bit in the Status Register to be reset.
+ Wait 100 microseconds to give the Host Adapter enough
+ time to determine whether the last value written to the
+ Command/Parameter Register was valid or not. If the
+ Command Complete bit is set in the Interrupt Register,
+ then the Command Invalid bit in the Status Register will
+ be reset if the Operation Code or Parameter was valid
+ and the command has completed, or set if the Operation
+ Code or Parameter was invalid. If the Data In Register
+ Ready bit is set in the Status Register, then the
+ Operation Code was valid, and data is waiting to be read
+ back from the Host Adapter. Otherwise, wait for the
+ Command/Parameter Register Busy bit in the Status
+ Register to be reset.
*/
udelay(100);
- InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (InterruptRegister.ir.CommandComplete)
+ intreg.all = blogic_rdint(adapter);
+ statusreg.all = blogic_rdstatus(adapter);
+ if (intreg.ir.cmd_complete)
break;
- if (HostAdapter->HostAdapterCommandCompleted)
+ if (adapter->adapter_cmd_complete)
break;
- if (StatusRegister.sr.DataInRegisterReady)
+ if (statusreg.sr.datain_ready)
break;
- if (StatusRegister.sr.CommandParameterRegisterBusy)
+ if (statusreg.sr.cmd_param_busy)
continue;
- BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
- ParameterLength--;
- }
- if (TimeoutCounter < 0) {
- BusLogic_CommandFailureReason = "Timeout waiting for Parameter Acceptance";
- Result = -2;
- goto Done;
- }
- /*
- The Modify I/O Address command does not cause a Command Complete Interrupt.
- */
- if (OperationCode == BusLogic_ModifyIOAddress) {
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (StatusRegister.sr.CommandInvalid) {
- BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
- Result = -1;
- goto Done;
+ blogic_setcmdparam(adapter, *param_p++);
+ paramlen--;
+ }
+ if (timeout < 0) {
+ blogic_cmd_failure_reason =
+ "Timeout waiting for Parameter Acceptance";
+ result = -2;
+ goto done;
+ }
+ /*
+ The Modify I/O Address command does not cause a Command Complete
+ Interrupt.
+ */
+ if (opcode == BLOGIC_MOD_IOADDR) {
+ statusreg.all = blogic_rdstatus(adapter);
+ if (statusreg.sr.cmd_invalid) {
+ blogic_cmd_failure_reason =
+ "Modify I/O Address Invalid";
+ result = -1;
+ goto done;
}
- if (BusLogic_GlobalOptions.TraceConfiguration)
- BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: " "(Modify I/O Address)\n", HostAdapter, OperationCode, StatusRegister.All);
- Result = 0;
- goto Done;
+ if (blogic_global_options.trace_config)
+ blogic_notice("blogic_cmd(%02X) Status = %02X: (Modify I/O Address)\n", adapter, opcode, statusreg.all);
+ result = 0;
+ goto done;
}
/*
Select an appropriate timeout value for awaiting command completion.
*/
- switch (OperationCode) {
- case BusLogic_InquireInstalledDevicesID0to7:
- case BusLogic_InquireInstalledDevicesID8to15:
- case BusLogic_InquireTargetDevices:
+ switch (opcode) {
+ case BLOGIC_INQ_DEV0TO7:
+ case BLOGIC_INQ_DEV8TO15:
+ case BLOGIC_INQ_DEV:
/* Approximately 60 seconds. */
- TimeoutCounter = 60 * 10000;
+ timeout = 60 * 10000;
break;
default:
/* Approximately 1 second. */
- TimeoutCounter = 10000;
+ timeout = 10000;
break;
}
/*
- Receive any Reply Bytes, waiting for either the Command Complete bit to
- be set in the Interrupt Register, or for the Interrupt Handler to set the
- Host Adapter Command Completed bit in the Host Adapter structure.
+ Receive any Reply Bytes, waiting for either the Command
+ Complete bit to be set in the Interrupt Register, or for the
+ Interrupt Handler to set the Host Adapter Command Completed
+ bit in the Host Adapter structure.
*/
- while (--TimeoutCounter >= 0) {
- InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (InterruptRegister.ir.CommandComplete)
+ while (--timeout >= 0) {
+ intreg.all = blogic_rdint(adapter);
+ statusreg.all = blogic_rdstatus(adapter);
+ if (intreg.ir.cmd_complete)
break;
- if (HostAdapter->HostAdapterCommandCompleted)
+ if (adapter->adapter_cmd_complete)
break;
- if (StatusRegister.sr.DataInRegisterReady) {
- if (++ReplyBytes <= ReplyLength)
- *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
+ if (statusreg.sr.datain_ready) {
+ if (++reply_b <= replylen)
+ *reply_p++ = blogic_rddatain(adapter);
else
- BusLogic_ReadDataInRegister(HostAdapter);
+ blogic_rddatain(adapter);
}
- if (OperationCode == BusLogic_FetchHostAdapterLocalRAM && StatusRegister.sr.HostAdapterReady)
+ if (opcode == BLOGIC_FETCH_LOCALRAM &&
+ statusreg.sr.adapter_ready)
break;
udelay(100);
}
- if (TimeoutCounter < 0) {
- BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
- Result = -2;
- goto Done;
+ if (timeout < 0) {
+ blogic_cmd_failure_reason =
+ "Timeout waiting for Command Complete";
+ result = -2;
+ goto done;
}
/*
Clear any pending Command Complete Interrupt.
*/
- BusLogic_InterruptReset(HostAdapter);
+ blogic_intreset(adapter);
/*
Provide tracing information if requested.
*/
- if (BusLogic_GlobalOptions.TraceConfiguration) {
+ if (blogic_global_options.trace_config) {
int i;
- BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:", HostAdapter, OperationCode, StatusRegister.All, ReplyLength, ReplyBytes);
- if (ReplyLength > ReplyBytes)
- ReplyLength = ReplyBytes;
- for (i = 0; i < ReplyLength; i++)
- BusLogic_Notice(" %02X", HostAdapter, ((unsigned char *) ReplyData)[i]);
- BusLogic_Notice("\n", HostAdapter);
+ blogic_notice("blogic_cmd(%02X) Status = %02X: %2d ==> %2d:",
+ adapter, opcode, statusreg.all, replylen,
+ reply_b);
+ if (replylen > reply_b)
+ replylen = reply_b;
+ for (i = 0; i < replylen; i++)
+ blogic_notice(" %02X", adapter,
+ ((unsigned char *) reply)[i]);
+ blogic_notice("\n", adapter);
}
/*
Process Command Invalid conditions.
*/
- if (StatusRegister.sr.CommandInvalid) {
+ if (statusreg.sr.cmd_invalid) {
/*
- Some early BusLogic Host Adapters may not recover properly from
- a Command Invalid condition, so if this appears to be the case,
- a Soft Reset is issued to the Host Adapter. Potentially invalid
- commands are never attempted after Mailbox Initialization is
- performed, so there should be no Host Adapter state lost by a
+ Some early BusLogic Host Adapters may not recover
+ properly from a Command Invalid condition, so if this
+ appears to be the case, a Soft Reset is issued to the
+ Host Adapter. Potentially invalid commands are never
+ attempted after Mailbox Initialization is performed,
+ so there should be no Host Adapter state lost by a
Soft Reset in response to a Command Invalid condition.
*/
udelay(1000);
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (StatusRegister.sr.CommandInvalid ||
- StatusRegister.sr.Reserved ||
- StatusRegister.sr.DataInRegisterReady ||
- StatusRegister.sr.CommandParameterRegisterBusy || !StatusRegister.sr.HostAdapterReady || !StatusRegister.sr.InitializationRequired || StatusRegister.sr.DiagnosticActive || StatusRegister.sr.DiagnosticFailure) {
- BusLogic_SoftReset(HostAdapter);
+ statusreg.all = blogic_rdstatus(adapter);
+ if (statusreg.sr.cmd_invalid || statusreg.sr.rsvd ||
+ statusreg.sr.datain_ready ||
+ statusreg.sr.cmd_param_busy ||
+ !statusreg.sr.adapter_ready ||
+ !statusreg.sr.init_reqd ||
+ statusreg.sr.diag_active ||
+ statusreg.sr.diag_failed) {
+ blogic_softreset(adapter);
udelay(1000);
}
- BusLogic_CommandFailureReason = "Command Invalid";
- Result = -1;
- goto Done;
+ blogic_cmd_failure_reason = "Command Invalid";
+ result = -1;
+ goto done;
}
/*
Handle Excess Parameters Supplied conditions.
*/
- if (ParameterLength > 0) {
- BusLogic_CommandFailureReason = "Excess Parameters Supplied";
- Result = -1;
- goto Done;
+ if (paramlen > 0) {
+ blogic_cmd_failure_reason = "Excess Parameters Supplied";
+ result = -1;
+ goto done;
}
/*
Indicate the command completed successfully.
*/
- BusLogic_CommandFailureReason = NULL;
- Result = ReplyBytes;
+ blogic_cmd_failure_reason = NULL;
+ result = reply_b;
/*
Restore the interrupt status if necessary and return.
*/
- Done:
- if (!HostAdapter->IRQ_ChannelAcquired)
- local_irq_restore(ProcessorFlags);
- return Result;
+done:
+ if (!adapter->irq_acquired)
+ local_irq_restore(processor_flag);
+ return result;
}
/*
- BusLogic_AppendProbeAddressISA appends a single ISA I/O Address to the list
- of I/O Address and Bus Probe Information to be checked for potential BusLogic
- Host Adapters.
-*/
-
-static void __init BusLogic_AppendProbeAddressISA(unsigned long IO_Address)
-{
- struct BusLogic_ProbeInfo *ProbeInfo;
- if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters)
- return;
- ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
- ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
- ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
- ProbeInfo->IO_Address = IO_Address;
- ProbeInfo->PCI_Device = NULL;
-}
-
-
-/*
- BusLogic_InitializeProbeInfoListISA initializes the list of I/O Address and
- Bus Probe Information to be checked for potential BusLogic SCSI Host Adapters
- only from the list of standard BusLogic MultiMaster ISA I/O Addresses.
-*/
-
-static void __init BusLogic_InitializeProbeInfoListISA(struct BusLogic_HostAdapter
- *PrototypeHostAdapter)
-{
- /*
- If BusLogic Driver Options specifications requested that ISA Bus Probes
- be inhibited, do not proceed further.
- */
- if (BusLogic_ProbeOptions.NoProbeISA)
- return;
- /*
- Append the list of standard BusLogic MultiMaster ISA I/O Addresses.
- */
- if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe330)
- BusLogic_AppendProbeAddressISA(0x330);
- if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe334)
- BusLogic_AppendProbeAddressISA(0x334);
- if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe230)
- BusLogic_AppendProbeAddressISA(0x230);
- if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe234)
- BusLogic_AppendProbeAddressISA(0x234);
- if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe130)
- BusLogic_AppendProbeAddressISA(0x130);
- if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe134)
- BusLogic_AppendProbeAddressISA(0x134);
-}
-
-
-#ifdef CONFIG_PCI
-
-
-/*
- BusLogic_SortProbeInfo sorts a section of BusLogic_ProbeInfoList in order
+ blogic_sort_probeinfo sorts a section of blogic_probeinfo_list in order
of increasing PCI Bus and Device Number.
*/
-static void __init BusLogic_SortProbeInfo(struct BusLogic_ProbeInfo *ProbeInfoList, int ProbeInfoCount)
+static void __init blogic_sort_probeinfo(struct blogic_probeinfo
+ *probeinfo_list, int probeinfo_cnt)
{
- int LastInterchange = ProbeInfoCount - 1, Bound, j;
- while (LastInterchange > 0) {
- Bound = LastInterchange;
- LastInterchange = 0;
- for (j = 0; j < Bound; j++) {
- struct BusLogic_ProbeInfo *ProbeInfo1 = &ProbeInfoList[j];
- struct BusLogic_ProbeInfo *ProbeInfo2 = &ProbeInfoList[j + 1];
- if (ProbeInfo1->Bus > ProbeInfo2->Bus || (ProbeInfo1->Bus == ProbeInfo2->Bus && (ProbeInfo1->Device > ProbeInfo2->Device))) {
- struct BusLogic_ProbeInfo TempProbeInfo;
- memcpy(&TempProbeInfo, ProbeInfo1, sizeof(struct BusLogic_ProbeInfo));
- memcpy(ProbeInfo1, ProbeInfo2, sizeof(struct BusLogic_ProbeInfo));
- memcpy(ProbeInfo2, &TempProbeInfo, sizeof(struct BusLogic_ProbeInfo));
- LastInterchange = j;
+ int last_exchange = probeinfo_cnt - 1, bound, j;
+
+ while (last_exchange > 0) {
+ bound = last_exchange;
+ last_exchange = 0;
+ for (j = 0; j < bound; j++) {
+ struct blogic_probeinfo *probeinfo1 =
+ &probeinfo_list[j];
+ struct blogic_probeinfo *probeinfo2 =
+ &probeinfo_list[j + 1];
+ if (probeinfo1->bus > probeinfo2->bus ||
+ (probeinfo1->bus == probeinfo2->bus &&
+ (probeinfo1->dev > probeinfo2->dev))) {
+ struct blogic_probeinfo tmp_probeinfo;
+
+ memcpy(&tmp_probeinfo, probeinfo1,
+ sizeof(struct blogic_probeinfo));
+ memcpy(probeinfo1, probeinfo2,
+ sizeof(struct blogic_probeinfo));
+ memcpy(probeinfo2, &tmp_probeinfo,
+ sizeof(struct blogic_probeinfo));
+ last_exchange = j;
}
}
}
@@ -617,84 +599,85 @@ static void __init BusLogic_SortProbeInfo(struct BusLogic_ProbeInfo *ProbeInfoLi
/*
- BusLogic_InitializeMultiMasterProbeInfo initializes the list of I/O Address
+ blogic_init_mm_probeinfo initializes the list of I/O Address
and Bus Probe Information to be checked for potential BusLogic MultiMaster
SCSI Host Adapters by interrogating the PCI Configuration Space on PCI
machines as well as from the list of standard BusLogic MultiMaster ISA
I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found.
*/
-static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAdapter
- *PrototypeHostAdapter)
+static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
{
- struct BusLogic_ProbeInfo *PrimaryProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
- int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
- int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
- bool ForceBusDeviceScanningOrder = false;
- bool ForceBusDeviceScanningOrderChecked = false;
- bool StandardAddressSeen[6];
- struct pci_dev *PCI_Device = NULL;
+ struct blogic_probeinfo *pr_probeinfo =
+ &blogic_probeinfo_list[blogic_probeinfo_count];
+ int nonpr_mmindex = blogic_probeinfo_count + 1;
+ int nonpr_mmcount = 0, mmcount = 0;
+ bool force_scan_order = false;
+ bool force_scan_order_checked = false;
+ struct pci_dev *pci_device = NULL;
int i;
- if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters)
+ if (blogic_probeinfo_count >= BLOGIC_MAX_ADAPTERS)
return 0;
- BusLogic_ProbeInfoCount++;
- for (i = 0; i < 6; i++)
- StandardAddressSeen[i] = false;
- /*
- Iterate over the MultiMaster PCI Host Adapters. For each enumerated host
- adapter, determine whether its ISA Compatible I/O Port is enabled and if
- so, whether it is assigned the Primary I/O Address. A host adapter that is
- assigned the Primary I/O Address will always be the preferred boot device.
- The MultiMaster BIOS will first recognize a host adapter at the Primary I/O
- Address, then any other PCI host adapters, and finally any host adapters
- located at the remaining standard ISA I/O Addresses. When a PCI host
- adapter is found with its ISA Compatible I/O Port enabled, a command is
- issued to disable the ISA Compatible I/O Port, and it is noted that the
+ blogic_probeinfo_count++;
+ /*
+ Iterate over the MultiMaster PCI Host Adapters. For each
+ enumerated host adapter, determine whether its ISA Compatible
+ I/O Port is enabled and if so, whether it is assigned the
+ Primary I/O Address. A host adapter that is assigned the
+ Primary I/O Address will always be the preferred boot device.
+ The MultiMaster BIOS will first recognize a host adapter at
+ the Primary I/O Address, then any other PCI host adapters,
+ and finally any host adapters located at the remaining
+ standard ISA I/O Addresses. When a PCI host adapter is found
+ with its ISA Compatible I/O Port enabled, a command is issued
+ to disable the ISA Compatible I/O Port, and it is noted that the
particular standard ISA I/O Address need not be probed.
*/
- PrimaryProbeInfo->IO_Address = 0;
- while ((PCI_Device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER, PCI_Device)) != NULL) {
- struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter;
- struct BusLogic_PCIHostAdapterInformation PCIHostAdapterInformation;
- enum BusLogic_ISACompatibleIOPort ModifyIOAddressRequest;
- unsigned char Bus;
- unsigned char Device;
- unsigned int IRQ_Channel;
- unsigned long BaseAddress0;
- unsigned long BaseAddress1;
- unsigned long IO_Address;
- unsigned long PCI_Address;
-
- if (pci_enable_device(PCI_Device))
+ pr_probeinfo->io_addr = 0;
+ while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC,
+ PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
+ pci_device)) != NULL) {
+ struct blogic_adapter *host_adapter = adapter;
+ struct blogic_adapter_info adapter_info;
+ enum blogic_isa_ioport mod_ioaddr_req;
+ unsigned char bus;
+ unsigned char device;
+ unsigned int irq_ch;
+ unsigned long base_addr0;
+ unsigned long base_addr1;
+ unsigned long io_addr;
+ unsigned long pci_addr;
+
+ if (pci_enable_device(pci_device))
continue;
- if (pci_set_dma_mask(PCI_Device, DMA_BIT_MASK(32) ))
+ if (dma_set_mask(&pci_device->dev, DMA_BIT_MASK(32)))
continue;
- Bus = PCI_Device->bus->number;
- Device = PCI_Device->devfn >> 3;
- IRQ_Channel = PCI_Device->irq;
- IO_Address = BaseAddress0 = pci_resource_start(PCI_Device, 0);
- PCI_Address = BaseAddress1 = pci_resource_start(PCI_Device, 1);
+ bus = pci_device->bus->number;
+ device = pci_device->devfn >> 3;
+ irq_ch = pci_device->irq;
+ io_addr = base_addr0 = pci_resource_start(pci_device, 0);
+ pci_addr = base_addr1 = pci_resource_start(pci_device, 1);
- if (pci_resource_flags(PCI_Device, 0) & IORESOURCE_MEM) {
- BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "MultiMaster Host Adapter\n", NULL, BaseAddress0);
- BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ if (pci_resource_flags(pci_device, 0) & IORESOURCE_MEM) {
+ blogic_err("BusLogic: Base Address0 0x%lX not I/O for MultiMaster Host Adapter\n", NULL, base_addr0);
+ blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr);
continue;
}
- if (pci_resource_flags(PCI_Device, 1) & IORESOURCE_IO) {
- BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "MultiMaster Host Adapter\n", NULL, BaseAddress1);
- BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n", NULL, Bus, Device, PCI_Address);
+ if (pci_resource_flags(pci_device, 1) & IORESOURCE_IO) {
+ blogic_err("BusLogic: Base Address1 0x%lX not Memory for MultiMaster Host Adapter\n", NULL, base_addr1);
+ blogic_err("at PCI Bus %d Device %d PCI Address 0x%lX\n", NULL, bus, device, pci_addr);
continue;
}
- if (IRQ_Channel == 0) {
- BusLogic_Error("BusLogic: IRQ Channel %d invalid for " "MultiMaster Host Adapter\n", NULL, IRQ_Channel);
- BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ if (irq_ch == 0) {
+ blogic_err("BusLogic: IRQ Channel %d invalid for MultiMaster Host Adapter\n", NULL, irq_ch);
+ blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr);
continue;
}
- if (BusLogic_GlobalOptions.TraceProbe) {
- BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter " "detected at\n", NULL);
- BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address " "0x%X PCI Address 0x%X\n", NULL, Bus, Device, IO_Address, PCI_Address);
+ if (blogic_global_options.trace_probe) {
+ blogic_notice("BusLogic: PCI MultiMaster Host Adapter detected at\n", NULL);
+ blogic_notice("BusLogic: PCI Bus %d Device %d I/O Address 0x%lX PCI Address 0x%lX\n", NULL, bus, device, io_addr, pci_addr);
}
/*
Issue the Inquire PCI Host Adapter Information command to determine
@@ -702,238 +685,216 @@ static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAd
known and enabled, note that the particular Standard ISA I/O
Address should not be probed.
*/
- HostAdapter->IO_Address = IO_Address;
- BusLogic_InterruptReset(HostAdapter);
- if (BusLogic_Command(HostAdapter, BusLogic_InquirePCIHostAdapterInformation, NULL, 0, &PCIHostAdapterInformation, sizeof(PCIHostAdapterInformation))
- == sizeof(PCIHostAdapterInformation)) {
- if (PCIHostAdapterInformation.ISACompatibleIOPort < 6)
- StandardAddressSeen[PCIHostAdapterInformation.ISACompatibleIOPort] = true;
- } else
- PCIHostAdapterInformation.ISACompatibleIOPort = BusLogic_IO_Disable;
+ host_adapter->io_addr = io_addr;
+ blogic_intreset(host_adapter);
+ if (blogic_cmd(host_adapter, BLOGIC_INQ_PCI_INFO, NULL, 0,
+ &adapter_info, sizeof(adapter_info)) !=
+ sizeof(adapter_info))
+ adapter_info.isa_port = BLOGIC_IO_DISABLE;
/*
- * Issue the Modify I/O Address command to disable the ISA Compatible
- * I/O Port. On PCI Host Adapters, the Modify I/O Address command
- * allows modification of the ISA compatible I/O Address that the Host
- * Adapter responds to; it does not affect the PCI compliant I/O Address
- * assigned at system initialization.
+ Issue the Modify I/O Address command to disable the
+ ISA Compatible I/O Port. On PCI Host Adapters, the
+ Modify I/O Address command allows modification of the
+ ISA compatible I/O Address that the Host Adapter
+ responds to; it does not affect the PCI compliant
+ I/O Address assigned at system initialization.
*/
- ModifyIOAddressRequest = BusLogic_IO_Disable;
- BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress, &ModifyIOAddressRequest, sizeof(ModifyIOAddressRequest), NULL, 0);
+ mod_ioaddr_req = BLOGIC_IO_DISABLE;
+ blogic_cmd(host_adapter, BLOGIC_MOD_IOADDR, &mod_ioaddr_req,
+ sizeof(mod_ioaddr_req), NULL, 0);
/*
- For the first MultiMaster Host Adapter enumerated, issue the Fetch
- Host Adapter Local RAM command to read byte 45 of the AutoSCSI area,
- for the setting of the "Use Bus And Device # For PCI Scanning Seq."
- option. Issue the Inquire Board ID command since this option is
+ For the first MultiMaster Host Adapter enumerated,
+ issue the Fetch Host Adapter Local RAM command to read
+ byte 45 of the AutoSCSI area, for the setting of the
+ "Use Bus And Device # For PCI Scanning Seq." option.
+ Issue the Inquire Board ID command since this option is
only valid for the BT-948/958/958D.
*/
- if (!ForceBusDeviceScanningOrderChecked) {
- struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest;
- struct BusLogic_AutoSCSIByte45 AutoSCSIByte45;
- struct BusLogic_BoardID BoardID;
- FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_AutoSCSI_BaseOffset + 45;
- FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIByte45);
- BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &AutoSCSIByte45, sizeof(AutoSCSIByte45));
- BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, &BoardID, sizeof(BoardID));
- if (BoardID.FirmwareVersion1stDigit == '5')
- ForceBusDeviceScanningOrder = AutoSCSIByte45.ForceBusDeviceScanningOrder;
- ForceBusDeviceScanningOrderChecked = true;
+ if (!force_scan_order_checked) {
+ struct blogic_fetch_localram fetch_localram;
+ struct blogic_autoscsi_byte45 autoscsi_byte45;
+ struct blogic_board_id id;
+
+ fetch_localram.offset = BLOGIC_AUTOSCSI_BASE + 45;
+ fetch_localram.count = sizeof(autoscsi_byte45);
+ blogic_cmd(host_adapter, BLOGIC_FETCH_LOCALRAM,
+ &fetch_localram, sizeof(fetch_localram),
+ &autoscsi_byte45,
+ sizeof(autoscsi_byte45));
+ blogic_cmd(host_adapter, BLOGIC_GET_BOARD_ID, NULL, 0,
+ &id, sizeof(id));
+ if (id.fw_ver_digit1 == '5')
+ force_scan_order =
+ autoscsi_byte45.force_scan_order;
+ force_scan_order_checked = true;
}
/*
- Determine whether this MultiMaster Host Adapter has its ISA
- Compatible I/O Port enabled and is assigned the Primary I/O Address.
- If it does, then it is the Primary MultiMaster Host Adapter and must
- be recognized first. If it does not, then it is added to the list
- for probing after any Primary MultiMaster Host Adapter is probed.
+ Determine whether this MultiMaster Host Adapter has its
+ ISA Compatible I/O Port enabled and is assigned the
+ Primary I/O Address. If it does, then it is the Primary
+ MultiMaster Host Adapter and must be recognized first.
+ If it does not, then it is added to the list for probing
+ after any Primary MultiMaster Host Adapter is probed.
*/
- if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330) {
- PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
- PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
- PrimaryProbeInfo->IO_Address = IO_Address;
- PrimaryProbeInfo->PCI_Address = PCI_Address;
- PrimaryProbeInfo->Bus = Bus;
- PrimaryProbeInfo->Device = Device;
- PrimaryProbeInfo->IRQ_Channel = IRQ_Channel;
- PrimaryProbeInfo->PCI_Device = pci_dev_get(PCI_Device);
- PCIMultiMasterCount++;
- } else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) {
- struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
- ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
- ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
- ProbeInfo->IO_Address = IO_Address;
- ProbeInfo->PCI_Address = PCI_Address;
- ProbeInfo->Bus = Bus;
- ProbeInfo->Device = Device;
- ProbeInfo->IRQ_Channel = IRQ_Channel;
- ProbeInfo->PCI_Device = pci_dev_get(PCI_Device);
- NonPrimaryPCIMultiMasterCount++;
- PCIMultiMasterCount++;
+ if (adapter_info.isa_port == BLOGIC_IO_330) {
+ pr_probeinfo->adapter_type = BLOGIC_MULTIMASTER;
+ pr_probeinfo->adapter_bus_type = BLOGIC_PCI_BUS;
+ pr_probeinfo->io_addr = io_addr;
+ pr_probeinfo->pci_addr = pci_addr;
+ pr_probeinfo->bus = bus;
+ pr_probeinfo->dev = device;
+ pr_probeinfo->irq_ch = irq_ch;
+ pr_probeinfo->pci_device = pci_dev_get(pci_device);
+ mmcount++;
+ } else if (blogic_probeinfo_count < BLOGIC_MAX_ADAPTERS) {
+ struct blogic_probeinfo *probeinfo =
+ &blogic_probeinfo_list[blogic_probeinfo_count++];
+ probeinfo->adapter_type = BLOGIC_MULTIMASTER;
+ probeinfo->adapter_bus_type = BLOGIC_PCI_BUS;
+ probeinfo->io_addr = io_addr;
+ probeinfo->pci_addr = pci_addr;
+ probeinfo->bus = bus;
+ probeinfo->dev = device;
+ probeinfo->irq_ch = irq_ch;
+ probeinfo->pci_device = pci_dev_get(pci_device);
+ nonpr_mmcount++;
+ mmcount++;
} else
- BusLogic_Warning("BusLogic: Too many Host Adapters " "detected\n", NULL);
- }
- /*
- If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option is ON
- for the first enumerated MultiMaster Host Adapter, and if that host adapter
- is a BT-948/958/958D, then the MultiMaster BIOS will recognize MultiMaster
- Host Adapters in the order of increasing PCI Bus and Device Number. In
- that case, sort the probe information into the same order the BIOS uses.
- If this option is OFF, then the MultiMaster BIOS will recognize MultiMaster
- Host Adapters in the order they are enumerated by the PCI BIOS, and hence
- no sorting is necessary.
- */
- if (ForceBusDeviceScanningOrder)
- BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[NonPrimaryPCIMultiMasterIndex], NonPrimaryPCIMultiMasterCount);
- /*
- If no PCI MultiMaster Host Adapter is assigned the Primary I/O Address,
- then the Primary I/O Address must be probed explicitly before any PCI
- host adapters are probed.
- */
- if (!BusLogic_ProbeOptions.NoProbeISA)
- if (PrimaryProbeInfo->IO_Address == 0 &&
- (!BusLogic_ProbeOptions.LimitedProbeISA ||
- BusLogic_ProbeOptions.Probe330)) {
- PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
- PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
- PrimaryProbeInfo->IO_Address = 0x330;
- }
- /*
- Append the list of standard BusLogic MultiMaster ISA I/O Addresses,
- omitting the Primary I/O Address which has already been handled.
- */
- if (!BusLogic_ProbeOptions.NoProbeISA) {
- if (!StandardAddressSeen[1] &&
- (!BusLogic_ProbeOptions.LimitedProbeISA ||
- BusLogic_ProbeOptions.Probe334))
- BusLogic_AppendProbeAddressISA(0x334);
- if (!StandardAddressSeen[2] &&
- (!BusLogic_ProbeOptions.LimitedProbeISA ||
- BusLogic_ProbeOptions.Probe230))
- BusLogic_AppendProbeAddressISA(0x230);
- if (!StandardAddressSeen[3] &&
- (!BusLogic_ProbeOptions.LimitedProbeISA ||
- BusLogic_ProbeOptions.Probe234))
- BusLogic_AppendProbeAddressISA(0x234);
- if (!StandardAddressSeen[4] &&
- (!BusLogic_ProbeOptions.LimitedProbeISA ||
- BusLogic_ProbeOptions.Probe130))
- BusLogic_AppendProbeAddressISA(0x130);
- if (!StandardAddressSeen[5] &&
- (!BusLogic_ProbeOptions.LimitedProbeISA ||
- BusLogic_ProbeOptions.Probe134))
- BusLogic_AppendProbeAddressISA(0x134);
+ blogic_warn("BusLogic: Too many Host Adapters detected\n", NULL);
}
/*
+ If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq."
+ option is ON for the first enumerated MultiMaster Host Adapter,
+ and if that host adapter is a BT-948/958/958D, then the
+ MultiMaster BIOS will recognize MultiMaster Host Adapters in
+ the order of increasing PCI Bus and Device Number. In that case,
+ sort the probe information into the same order the BIOS uses.
+ If this option is OFF, then the MultiMaster BIOS will recognize
+ MultiMaster Host Adapters in the order they are enumerated by
+ the PCI BIOS, and hence no sorting is necessary.
+ */
+ if (force_scan_order)
+ blogic_sort_probeinfo(&blogic_probeinfo_list[nonpr_mmindex],
+ nonpr_mmcount);
+ /*
Iterate over the older non-compliant MultiMaster PCI Host Adapters,
noting the PCI bus location and assigned IRQ Channel.
*/
- PCI_Device = NULL;
- while ((PCI_Device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, PCI_Device)) != NULL) {
- unsigned char Bus;
- unsigned char Device;
- unsigned int IRQ_Channel;
- unsigned long IO_Address;
+ pci_device = NULL;
+ while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC,
+ PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC,
+ pci_device)) != NULL) {
+ unsigned char bus;
+ unsigned char device;
+ unsigned int irq_ch;
+ unsigned long io_addr;
- if (pci_enable_device(PCI_Device))
+ if (pci_enable_device(pci_device))
continue;
- if (pci_set_dma_mask(PCI_Device, DMA_BIT_MASK(32)))
+ if (dma_set_mask(&pci_device->dev, DMA_BIT_MASK(32)))
continue;
- Bus = PCI_Device->bus->number;
- Device = PCI_Device->devfn >> 3;
- IRQ_Channel = PCI_Device->irq;
- IO_Address = pci_resource_start(PCI_Device, 0);
+ bus = pci_device->bus->number;
+ device = pci_device->devfn >> 3;
+ irq_ch = pci_device->irq;
+ io_addr = pci_resource_start(pci_device, 0);
- if (IO_Address == 0 || IRQ_Channel == 0)
+ if (io_addr == 0 || irq_ch == 0)
continue;
- for (i = 0; i < BusLogic_ProbeInfoCount; i++) {
- struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[i];
- if (ProbeInfo->IO_Address == IO_Address && ProbeInfo->HostAdapterType == BusLogic_MultiMaster) {
- ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
- ProbeInfo->PCI_Address = 0;
- ProbeInfo->Bus = Bus;
- ProbeInfo->Device = Device;
- ProbeInfo->IRQ_Channel = IRQ_Channel;
- ProbeInfo->PCI_Device = pci_dev_get(PCI_Device);
+ for (i = 0; i < blogic_probeinfo_count; i++) {
+ struct blogic_probeinfo *probeinfo =
+ &blogic_probeinfo_list[i];
+ if (probeinfo->io_addr == io_addr &&
+ probeinfo->adapter_type == BLOGIC_MULTIMASTER) {
+ probeinfo->adapter_bus_type = BLOGIC_PCI_BUS;
+ probeinfo->pci_addr = 0;
+ probeinfo->bus = bus;
+ probeinfo->dev = device;
+ probeinfo->irq_ch = irq_ch;
+ probeinfo->pci_device = pci_dev_get(pci_device);
break;
}
}
}
- return PCIMultiMasterCount;
+ return mmcount;
}
/*
- BusLogic_InitializeFlashPointProbeInfo initializes the list of I/O Address
+ blogic_init_fp_probeinfo initializes the list of I/O Address
and Bus Probe Information to be checked for potential BusLogic FlashPoint
Host Adapters by interrogating the PCI Configuration Space. It returns the
number of FlashPoint Host Adapters found.
*/
-static int __init BusLogic_InitializeFlashPointProbeInfo(struct BusLogic_HostAdapter
- *PrototypeHostAdapter)
+static int __init blogic_init_fp_probeinfo(struct blogic_adapter *adapter)
{
- int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
- struct pci_dev *PCI_Device = NULL;
+ int fpindex = blogic_probeinfo_count, fpcount = 0;
+ struct pci_dev *pci_device = NULL;
/*
Interrogate PCI Configuration Space for any FlashPoint Host Adapters.
*/
- while ((PCI_Device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT, PCI_Device)) != NULL) {
- unsigned char Bus;
- unsigned char Device;
- unsigned int IRQ_Channel;
- unsigned long BaseAddress0;
- unsigned long BaseAddress1;
- unsigned long IO_Address;
- unsigned long PCI_Address;
-
- if (pci_enable_device(PCI_Device))
+ while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC,
+ PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT,
+ pci_device)) != NULL) {
+ unsigned char bus;
+ unsigned char device;
+ unsigned int irq_ch;
+ unsigned long base_addr0;
+ unsigned long base_addr1;
+ unsigned long io_addr;
+ unsigned long pci_addr;
+
+ if (pci_enable_device(pci_device))
continue;
- if (pci_set_dma_mask(PCI_Device, DMA_BIT_MASK(32)))
+ if (dma_set_mask(&pci_device->dev, DMA_BIT_MASK(32)))
continue;
- Bus = PCI_Device->bus->number;
- Device = PCI_Device->devfn >> 3;
- IRQ_Channel = PCI_Device->irq;
- IO_Address = BaseAddress0 = pci_resource_start(PCI_Device, 0);
- PCI_Address = BaseAddress1 = pci_resource_start(PCI_Device, 1);
+ bus = pci_device->bus->number;
+ device = pci_device->devfn >> 3;
+ irq_ch = pci_device->irq;
+ io_addr = base_addr0 = pci_resource_start(pci_device, 0);
+ pci_addr = base_addr1 = pci_resource_start(pci_device, 1);
#ifdef CONFIG_SCSI_FLASHPOINT
- if (pci_resource_flags(PCI_Device, 0) & IORESOURCE_MEM) {
- BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "FlashPoint Host Adapter\n", NULL, BaseAddress0);
- BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ if (pci_resource_flags(pci_device, 0) & IORESOURCE_MEM) {
+ blogic_err("BusLogic: Base Address0 0x%lX not I/O for FlashPoint Host Adapter\n", NULL, base_addr0);
+ blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr);
continue;
}
- if (pci_resource_flags(PCI_Device, 1) & IORESOURCE_IO) {
- BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "FlashPoint Host Adapter\n", NULL, BaseAddress1);
- BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n", NULL, Bus, Device, PCI_Address);
+ if (pci_resource_flags(pci_device, 1) & IORESOURCE_IO) {
+ blogic_err("BusLogic: Base Address1 0x%lX not Memory for FlashPoint Host Adapter\n", NULL, base_addr1);
+ blogic_err("at PCI Bus %d Device %d PCI Address 0x%lX\n", NULL, bus, device, pci_addr);
continue;
}
- if (IRQ_Channel == 0) {
- BusLogic_Error("BusLogic: IRQ Channel %d invalid for " "FlashPoint Host Adapter\n", NULL, IRQ_Channel);
- BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ if (irq_ch == 0) {
+ blogic_err("BusLogic: IRQ Channel %d invalid for FlashPoint Host Adapter\n", NULL, irq_ch);
+ blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr);
continue;
}
- if (BusLogic_GlobalOptions.TraceProbe) {
- BusLogic_Notice("BusLogic: FlashPoint Host Adapter " "detected at\n", NULL);
- BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address " "0x%X PCI Address 0x%X\n", NULL, Bus, Device, IO_Address, PCI_Address);
+ if (blogic_global_options.trace_probe) {
+ blogic_notice("BusLogic: FlashPoint Host Adapter detected at\n", NULL);
+ blogic_notice("BusLogic: PCI Bus %d Device %d I/O Address 0x%lX PCI Address 0x%lX\n", NULL, bus, device, io_addr, pci_addr);
}
- if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) {
- struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
- ProbeInfo->HostAdapterType = BusLogic_FlashPoint;
- ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
- ProbeInfo->IO_Address = IO_Address;
- ProbeInfo->PCI_Address = PCI_Address;
- ProbeInfo->Bus = Bus;
- ProbeInfo->Device = Device;
- ProbeInfo->IRQ_Channel = IRQ_Channel;
- ProbeInfo->PCI_Device = pci_dev_get(PCI_Device);
- FlashPointCount++;
+ if (blogic_probeinfo_count < BLOGIC_MAX_ADAPTERS) {
+ struct blogic_probeinfo *probeinfo =
+ &blogic_probeinfo_list[blogic_probeinfo_count++];
+ probeinfo->adapter_type = BLOGIC_FLASHPOINT;
+ probeinfo->adapter_bus_type = BLOGIC_PCI_BUS;
+ probeinfo->io_addr = io_addr;
+ probeinfo->pci_addr = pci_addr;
+ probeinfo->bus = bus;
+ probeinfo->dev = device;
+ probeinfo->irq_ch = irq_ch;
+ probeinfo->pci_device = pci_dev_get(pci_device);
+ fpcount++;
} else
- BusLogic_Warning("BusLogic: Too many Host Adapters " "detected\n", NULL);
+ blogic_warn("BusLogic: Too many Host Adapters detected\n", NULL);
#else
- BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " "PCI Bus %d Device %d\n", NULL, Bus, Device);
- BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, irq %d, " "but FlashPoint\n", NULL, IO_Address, PCI_Address, IRQ_Channel);
- BusLogic_Error("BusLogic: support was omitted in this kernel " "configuration.\n", NULL);
+ blogic_err("BusLogic: FlashPoint Host Adapter detected at PCI Bus %d Device %d\n", NULL, bus, device);
+ blogic_err("BusLogic: I/O Address 0x%lX PCI Address 0x%lX, irq %d, but FlashPoint\n", NULL, io_addr, pci_addr, irq_ch);
+ blogic_err("BusLogic: support was omitted in this kernel configuration.\n", NULL);
#endif
}
/*
@@ -941,13 +902,13 @@ static int __init BusLogic_InitializeFlashPointProbeInfo(struct BusLogic_HostAda
increasing PCI Bus and Device Number, so sort the probe information into
the same order the BIOS uses.
*/
- BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[FlashPointIndex], FlashPointCount);
- return FlashPointCount;
+ blogic_sort_probeinfo(&blogic_probeinfo_list[fpindex], fpcount);
+ return fpcount;
}
/*
- BusLogic_InitializeProbeInfoList initializes the list of I/O Address and Bus
+ blogic_init_probeinfo_list initializes the list of I/O Address and Bus
Probe Information to be checked for potential BusLogic SCSI Host Adapters by
interrogating the PCI Configuration Space on PCI machines as well as from the
list of standard BusLogic MultiMaster ISA I/O Addresses. By default, if both
@@ -959,104 +920,117 @@ static int __init BusLogic_InitializeFlashPointProbeInfo(struct BusLogic_HostAda
a particular probe order.
*/
-static void __init BusLogic_InitializeProbeInfoList(struct BusLogic_HostAdapter
- *PrototypeHostAdapter)
+static void __init blogic_init_probeinfo_list(struct blogic_adapter *adapter)
{
/*
- If a PCI BIOS is present, interrogate it for MultiMaster and FlashPoint
- Host Adapters; otherwise, default to the standard ISA MultiMaster probe.
- */
- if (!BusLogic_ProbeOptions.NoProbePCI) {
- if (BusLogic_ProbeOptions.MultiMasterFirst) {
- BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
- BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
- } else if (BusLogic_ProbeOptions.FlashPointFirst) {
- BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
- BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
+ If a PCI BIOS is present, interrogate it for MultiMaster and
+ FlashPoint Host Adapters; otherwise, default to the standard
+ ISA MultiMaster probe.
+ */
+ if (!blogic_probe_options.noprobe_pci) {
+ if (blogic_probe_options.multimaster_first) {
+ blogic_init_mm_probeinfo(adapter);
+ blogic_init_fp_probeinfo(adapter);
+ } else if (blogic_probe_options.flashpoint_first) {
+ blogic_init_fp_probeinfo(adapter);
+ blogic_init_mm_probeinfo(adapter);
} else {
- int FlashPointCount = BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
- int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
- if (FlashPointCount > 0 && PCIMultiMasterCount > 0) {
- struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[FlashPointCount];
- struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter;
- struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest;
- struct BusLogic_BIOSDriveMapByte Drive0MapByte;
- while (ProbeInfo->HostAdapterBusType != BusLogic_PCI_Bus)
- ProbeInfo++;
- HostAdapter->IO_Address = ProbeInfo->IO_Address;
- FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_BIOS_BaseOffset + BusLogic_BIOS_DriveMapOffset + 0;
- FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(Drive0MapByte);
- BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &Drive0MapByte, sizeof(Drive0MapByte));
+ int fpcount = blogic_init_fp_probeinfo(adapter);
+ int mmcount = blogic_init_mm_probeinfo(adapter);
+ if (fpcount > 0 && mmcount > 0) {
+ struct blogic_probeinfo *probeinfo =
+ &blogic_probeinfo_list[fpcount];
+ struct blogic_adapter *myadapter = adapter;
+ struct blogic_fetch_localram fetch_localram;
+ struct blogic_bios_drvmap d0_mapbyte;
+
+ while (probeinfo->adapter_bus_type !=
+ BLOGIC_PCI_BUS)
+ probeinfo++;
+ myadapter->io_addr = probeinfo->io_addr;
+ fetch_localram.offset =
+ BLOGIC_BIOS_BASE + BLOGIC_BIOS_DRVMAP;
+ fetch_localram.count = sizeof(d0_mapbyte);
+ blogic_cmd(myadapter, BLOGIC_FETCH_LOCALRAM,
+ &fetch_localram,
+ sizeof(fetch_localram),
+ &d0_mapbyte,
+ sizeof(d0_mapbyte));
/*
- If the Map Byte for BIOS Drive 0 indicates that BIOS Drive 0
- is controlled by this PCI MultiMaster Host Adapter, then
- reverse the probe order so that MultiMaster Host Adapters are
- probed before FlashPoint Host Adapters.
+ If the Map Byte for BIOS Drive 0 indicates
+ that BIOS Drive 0 is controlled by this
+ PCI MultiMaster Host Adapter, then reverse
+ the probe order so that MultiMaster Host
+ Adapters are probed before FlashPoint Host
+ Adapters.
*/
- if (Drive0MapByte.DiskGeometry != BusLogic_BIOS_Disk_Not_Installed) {
- struct BusLogic_ProbeInfo SavedProbeInfo[BusLogic_MaxHostAdapters];
- int MultiMasterCount = BusLogic_ProbeInfoCount - FlashPointCount;
- memcpy(SavedProbeInfo, BusLogic_ProbeInfoList, BusLogic_ProbeInfoCount * sizeof(struct BusLogic_ProbeInfo));
- memcpy(&BusLogic_ProbeInfoList[0], &SavedProbeInfo[FlashPointCount], MultiMasterCount * sizeof(struct BusLogic_ProbeInfo));
- memcpy(&BusLogic_ProbeInfoList[MultiMasterCount], &SavedProbeInfo[0], FlashPointCount * sizeof(struct BusLogic_ProbeInfo));
+ if (d0_mapbyte.diskgeom != BLOGIC_BIOS_NODISK) {
+ struct blogic_probeinfo saved_probeinfo[BLOGIC_MAX_ADAPTERS];
+ int mmcount = blogic_probeinfo_count - fpcount;
+
+ memcpy(saved_probeinfo,
+ blogic_probeinfo_list,
+ blogic_probeinfo_count * sizeof(struct blogic_probeinfo));
+ memcpy(&blogic_probeinfo_list[0],
+ &saved_probeinfo[fpcount],
+ mmcount * sizeof(struct blogic_probeinfo));
+ memcpy(&blogic_probeinfo_list[mmcount],
+ &saved_probeinfo[0],
+ fpcount * sizeof(struct blogic_probeinfo));
}
}
}
- } else
- BusLogic_InitializeProbeInfoListISA(PrototypeHostAdapter);
+ }
}
-#else
-#define BusLogic_InitializeProbeInfoList(adapter) \
- BusLogic_InitializeProbeInfoListISA(adapter)
-#endif /* CONFIG_PCI */
-
-
/*
- BusLogic_Failure prints a standardized error message, and then returns false.
+ blogic_failure prints a standardized error message, and then returns false.
*/
-static bool BusLogic_Failure(struct BusLogic_HostAdapter *HostAdapter, char *ErrorMessage)
+static bool blogic_failure(struct blogic_adapter *adapter, char *msg)
{
- BusLogic_AnnounceDriver(HostAdapter);
- if (HostAdapter->HostAdapterBusType == BusLogic_PCI_Bus) {
- BusLogic_Error("While configuring BusLogic PCI Host Adapter at\n", HostAdapter);
- BusLogic_Error("Bus %d Device %d I/O Address 0x%X PCI Address 0x%X:\n", HostAdapter, HostAdapter->Bus, HostAdapter->Device, HostAdapter->IO_Address, HostAdapter->PCI_Address);
+ blogic_announce_drvr(adapter);
+ if (adapter->adapter_bus_type == BLOGIC_PCI_BUS) {
+ blogic_err("While configuring BusLogic PCI Host Adapter at\n",
+ adapter);
+ blogic_err("Bus %d Device %d I/O Address 0x%lX PCI Address 0x%lX:\n", adapter, adapter->bus, adapter->dev, adapter->io_addr, adapter->pci_addr);
} else
- BusLogic_Error("While configuring BusLogic Host Adapter at " "I/O Address 0x%X:\n", HostAdapter, HostAdapter->IO_Address);
- BusLogic_Error("%s FAILED - DETACHING\n", HostAdapter, ErrorMessage);
- if (BusLogic_CommandFailureReason != NULL)
- BusLogic_Error("ADDITIONAL FAILURE INFO - %s\n", HostAdapter, BusLogic_CommandFailureReason);
+ blogic_err("While configuring BusLogic Host Adapter at I/O Address 0x%lX:\n", adapter, adapter->io_addr);
+ blogic_err("%s FAILED - DETACHING\n", adapter, msg);
+ if (blogic_cmd_failure_reason != NULL)
+ blogic_err("ADDITIONAL FAILURE INFO - %s\n", adapter,
+ blogic_cmd_failure_reason);
return false;
}
/*
- BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
+ blogic_probe probes for a BusLogic Host Adapter.
*/
-static bool __init BusLogic_ProbeHostAdapter(struct BusLogic_HostAdapter *HostAdapter)
+static bool __init blogic_probe(struct blogic_adapter *adapter)
{
- union BusLogic_StatusRegister StatusRegister;
- union BusLogic_InterruptRegister InterruptRegister;
- union BusLogic_GeometryRegister GeometryRegister;
+ union blogic_stat_reg statusreg;
+ union blogic_int_reg intreg;
+ union blogic_geo_reg georeg;
/*
FlashPoint Host Adapters are Probed by the FlashPoint SCCB Manager.
*/
- if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
- struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo;
- FlashPointInfo->BaseAddress = (u32) HostAdapter->IO_Address;
- FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
- FlashPointInfo->Present = false;
- if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && FlashPointInfo->Present)) {
- BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " "PCI Bus %d Device %d\n", HostAdapter, HostAdapter->Bus, HostAdapter->Device);
- BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, " "but FlashPoint\n", HostAdapter, HostAdapter->IO_Address, HostAdapter->PCI_Address);
- BusLogic_Error("BusLogic: Probe Function failed to validate it.\n", HostAdapter);
+ if (blogic_flashpoint_type(adapter)) {
+ struct fpoint_info *fpinfo = &adapter->fpinfo;
+ fpinfo->base_addr = (u32) adapter->io_addr;
+ fpinfo->irq_ch = adapter->irq_ch;
+ fpinfo->present = false;
+ if (!(FlashPoint_ProbeHostAdapter(fpinfo) == 0 &&
+ fpinfo->present)) {
+ blogic_err("BusLogic: FlashPoint Host Adapter detected at PCI Bus %d Device %d\n", adapter, adapter->bus, adapter->dev);
+ blogic_err("BusLogic: I/O Address 0x%lX PCI Address 0x%lX, but FlashPoint\n", adapter, adapter->io_addr, adapter->pci_addr);
+ blogic_err("BusLogic: Probe Function failed to validate it.\n", adapter);
return false;
}
- if (BusLogic_GlobalOptions.TraceProbe)
- BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Found\n", HostAdapter, HostAdapter->IO_Address);
+ if (blogic_global_options.trace_probe)
+ blogic_notice("BusLogic_Probe(0x%lX): FlashPoint Found\n", adapter, adapter->io_addr);
/*
Indicate the Host Adapter Probe completed successfully.
*/
@@ -1069,28 +1043,32 @@ static bool __init BusLogic_ProbeHostAdapter(struct BusLogic_HostAdapter *HostAd
case there is definitely no BusLogic Host Adapter at this base I/O Address.
The test here is a subset of that used by the BusLogic Host Adapter BIOS.
*/
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
- GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
- if (BusLogic_GlobalOptions.TraceProbe)
- BusLogic_Notice("BusLogic_Probe(0x%X): Status 0x%02X, Interrupt 0x%02X, " "Geometry 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All, InterruptRegister.All, GeometryRegister.All);
- if (StatusRegister.All == 0 || StatusRegister.sr.DiagnosticActive || StatusRegister.sr.CommandParameterRegisterBusy || StatusRegister.sr.Reserved || StatusRegister.sr.CommandInvalid || InterruptRegister.ir.Reserved != 0)
+ statusreg.all = blogic_rdstatus(adapter);
+ intreg.all = blogic_rdint(adapter);
+ georeg.all = blogic_rdgeom(adapter);
+ if (blogic_global_options.trace_probe)
+ blogic_notice("BusLogic_Probe(0x%lX): Status 0x%02X, Interrupt 0x%02X, Geometry 0x%02X\n", adapter, adapter->io_addr, statusreg.all, intreg.all, georeg.all);
+ if (statusreg.all == 0 || statusreg.sr.diag_active ||
+ statusreg.sr.cmd_param_busy || statusreg.sr.rsvd ||
+ statusreg.sr.cmd_invalid || intreg.ir.rsvd != 0)
return false;
/*
- Check the undocumented Geometry Register to test if there is an I/O port
- that responded. Adaptec Host Adapters do not implement the Geometry
- Register, so this test helps serve to avoid incorrectly recognizing an
- Adaptec 1542A or 1542B as a BusLogic. Unfortunately, the Adaptec 1542C
- series does respond to the Geometry Register I/O port, but it will be
- rejected later when the Inquire Extended Setup Information command is
- issued in BusLogic_CheckHostAdapter. The AMI FastDisk Host Adapter is a
- BusLogic clone that implements the same interface as earlier BusLogic
- Host Adapters, including the undocumented commands, and is therefore
- supported by this driver. However, the AMI FastDisk always returns 0x00
- upon reading the Geometry Register, so the extended translation option
- should always be left disabled on the AMI FastDisk.
- */
- if (GeometryRegister.All == 0xFF)
+ Check the undocumented Geometry Register to test if there is
+ an I/O port that responded. Adaptec Host Adapters do not
+ implement the Geometry Register, so this test helps serve to
+ avoid incorrectly recognizing an Adaptec 1542A or 1542B as a
+ BusLogic. Unfortunately, the Adaptec 1542C series does respond
+ to the Geometry Register I/O port, but it will be rejected
+ later when the Inquire Extended Setup Information command is
+ issued in blogic_checkadapter. The AMI FastDisk Host Adapter
+ is a BusLogic clone that implements the same interface as
+ earlier BusLogic Host Adapters, including the undocumented
+ commands, and is therefore supported by this driver. However,
+ the AMI FastDisk always returns 0x00 upon reading the Geometry
+ Register, so the extended translation option should always be
+ left disabled on the AMI FastDisk.
+ */
+ if (georeg.all == 0xFF)
return false;
/*
Indicate the Host Adapter Probe completed successfully.
@@ -1100,27 +1078,28 @@ static bool __init BusLogic_ProbeHostAdapter(struct BusLogic_HostAdapter *HostAd
/*
- BusLogic_HardwareResetHostAdapter issues a Hardware Reset to the Host Adapter
- and waits for Host Adapter Diagnostics to complete. If HardReset is true, a
+ blogic_hwreset issues a Hardware Reset to the Host Adapter
+ and waits for Host Adapter Diagnostics to complete. If hard_reset is true, a
Hard Reset is performed which also initiates a SCSI Bus Reset. Otherwise, a
Soft Reset is performed which only resets the Host Adapter without forcing a
SCSI Bus Reset.
*/
-static bool BusLogic_HardwareResetHostAdapter(struct BusLogic_HostAdapter
- *HostAdapter, bool HardReset)
+static bool blogic_hwreset(struct blogic_adapter *adapter, bool hard_reset)
{
- union BusLogic_StatusRegister StatusRegister;
- int TimeoutCounter;
+ union blogic_stat_reg statusreg;
+ int timeout;
/*
- FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
+ FlashPoint Host Adapters are Hard Reset by the FlashPoint
+ SCCB Manager.
*/
- if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
- struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo;
- FlashPointInfo->HostSoftReset = !HardReset;
- FlashPointInfo->ReportDataUnderrun = true;
- HostAdapter->CardHandle = FlashPoint_HardwareResetHostAdapter(FlashPointInfo);
- if (HostAdapter->CardHandle == FlashPoint_BadCardHandle)
+ if (blogic_flashpoint_type(adapter)) {
+ struct fpoint_info *fpinfo = &adapter->fpinfo;
+ fpinfo->softreset = !hard_reset;
+ fpinfo->report_underrun = true;
+ adapter->cardhandle =
+ FlashPoint_HardwareResetHostAdapter(fpinfo);
+ if (adapter->cardhandle == (void *)FPOINT_BADCARD_HANDLE)
return false;
/*
Indicate the Host Adapter Hard Reset completed successfully.
@@ -1128,26 +1107,27 @@ static bool BusLogic_HardwareResetHostAdapter(struct BusLogic_HostAdapter
return true;
}
/*
- Issue a Hard Reset or Soft Reset Command to the Host Adapter. The Host
- Adapter should respond by setting Diagnostic Active in the Status Register.
+ Issue a Hard Reset or Soft Reset Command to the Host Adapter.
+ The Host Adapter should respond by setting Diagnostic Active in
+ the Status Register.
*/
- if (HardReset)
- BusLogic_HardReset(HostAdapter);
+ if (hard_reset)
+ blogic_hardreset(adapter);
else
- BusLogic_SoftReset(HostAdapter);
+ blogic_softreset(adapter);
/*
Wait until Diagnostic Active is set in the Status Register.
*/
- TimeoutCounter = 5 * 10000;
- while (--TimeoutCounter >= 0) {
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (StatusRegister.sr.DiagnosticActive)
+ timeout = 5 * 10000;
+ while (--timeout >= 0) {
+ statusreg.all = blogic_rdstatus(adapter);
+ if (statusreg.sr.diag_active)
break;
udelay(100);
}
- if (BusLogic_GlobalOptions.TraceHardwareReset)
- BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Active, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All);
- if (TimeoutCounter < 0)
+ if (blogic_global_options.trace_hw_reset)
+ blogic_notice("BusLogic_HardwareReset(0x%lX): Diagnostic Active, Status 0x%02X\n", adapter, adapter->io_addr, statusreg.all);
+ if (timeout < 0)
return false;
/*
Wait 100 microseconds to allow completion of any initial diagnostic
@@ -1158,45 +1138,47 @@ static bool BusLogic_HardwareResetHostAdapter(struct BusLogic_HostAdapter
/*
Wait until Diagnostic Active is reset in the Status Register.
*/
- TimeoutCounter = 10 * 10000;
- while (--TimeoutCounter >= 0) {
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (!StatusRegister.sr.DiagnosticActive)
+ timeout = 10 * 10000;
+ while (--timeout >= 0) {
+ statusreg.all = blogic_rdstatus(adapter);
+ if (!statusreg.sr.diag_active)
break;
udelay(100);
}
- if (BusLogic_GlobalOptions.TraceHardwareReset)
- BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Completed, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All);
- if (TimeoutCounter < 0)
+ if (blogic_global_options.trace_hw_reset)
+ blogic_notice("BusLogic_HardwareReset(0x%lX): Diagnostic Completed, Status 0x%02X\n", adapter, adapter->io_addr, statusreg.all);
+ if (timeout < 0)
return false;
/*
- Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
- or Data In Register Ready bits is set in the Status Register.
+ Wait until at least one of the Diagnostic Failure, Host Adapter
+ Ready, or Data In Register Ready bits is set in the Status Register.
*/
- TimeoutCounter = 10000;
- while (--TimeoutCounter >= 0) {
- StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
- if (StatusRegister.sr.DiagnosticFailure || StatusRegister.sr.HostAdapterReady || StatusRegister.sr.DataInRegisterReady)
+ timeout = 10000;
+ while (--timeout >= 0) {
+ statusreg.all = blogic_rdstatus(adapter);
+ if (statusreg.sr.diag_failed || statusreg.sr.adapter_ready ||
+ statusreg.sr.datain_ready)
break;
udelay(100);
}
- if (BusLogic_GlobalOptions.TraceHardwareReset)
- BusLogic_Notice("BusLogic_HardwareReset(0x%X): Host Adapter Ready, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All);
- if (TimeoutCounter < 0)
+ if (blogic_global_options.trace_hw_reset)
+ blogic_notice("BusLogic_HardwareReset(0x%lX): Host Adapter Ready, Status 0x%02X\n", adapter, adapter->io_addr, statusreg.all);
+ if (timeout < 0)
return false;
/*
- If Diagnostic Failure is set or Host Adapter Ready is reset, then an
- error occurred during the Host Adapter diagnostics. If Data In Register
- Ready is set, then there is an Error Code available.
- */
- if (StatusRegister.sr.DiagnosticFailure || !StatusRegister.sr.HostAdapterReady) {
- BusLogic_CommandFailureReason = NULL;
- BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS");
- BusLogic_Error("HOST ADAPTER STATUS REGISTER = %02X\n", HostAdapter, StatusRegister.All);
- if (StatusRegister.sr.DataInRegisterReady) {
- unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter);
- BusLogic_Error("HOST ADAPTER ERROR CODE = %d\n", HostAdapter, ErrorCode);
- }
+ If Diagnostic Failure is set or Host Adapter Ready is reset,
+ then an error occurred during the Host Adapter diagnostics.
+ If Data In Register Ready is set, then there is an Error Code
+ available.
+ */
+ if (statusreg.sr.diag_failed || !statusreg.sr.adapter_ready) {
+ blogic_cmd_failure_reason = NULL;
+ blogic_failure(adapter, "HARD RESET DIAGNOSTICS");
+ blogic_err("HOST ADAPTER STATUS REGISTER = %02X\n", adapter,
+ statusreg.all);
+ if (statusreg.sr.datain_ready)
+ blogic_err("HOST ADAPTER ERROR CODE = %d\n", adapter,
+ blogic_rddatain(adapter));
return false;
}
/*
@@ -1207,161 +1189,175 @@ static bool BusLogic_HardwareResetHostAdapter(struct BusLogic_HostAdapter
/*
- BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
+ blogic_checkadapter checks to be sure this really is a BusLogic
Host Adapter.
*/
-static bool __init BusLogic_CheckHostAdapter(struct BusLogic_HostAdapter *HostAdapter)
+static bool __init blogic_checkadapter(struct blogic_adapter *adapter)
{
- struct BusLogic_ExtendedSetupInformation ExtendedSetupInformation;
- unsigned char RequestedReplyLength;
- bool Result = true;
+ struct blogic_ext_setup ext_setupinfo;
+ unsigned char req_replylen;
+ bool result = true;
/*
FlashPoint Host Adapters do not require this protection.
*/
- if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ if (blogic_flashpoint_type(adapter))
return true;
/*
- Issue the Inquire Extended Setup Information command. Only genuine
- BusLogic Host Adapters and true clones support this command. Adaptec 1542C
- series Host Adapters that respond to the Geometry Register I/O port will
- fail this command.
+ Issue the Inquire Extended Setup Information command. Only genuine
+ BusLogic Host Adapters and true clones support this command.
+ Adaptec 1542C series Host Adapters that respond to the Geometry
+ Register I/O port will fail this command.
*/
- RequestedReplyLength = sizeof(ExtendedSetupInformation);
- if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &ExtendedSetupInformation, sizeof(ExtendedSetupInformation))
- != sizeof(ExtendedSetupInformation))
- Result = false;
+ req_replylen = sizeof(ext_setupinfo);
+ if (blogic_cmd(adapter, BLOGIC_INQ_EXTSETUP, &req_replylen,
+ sizeof(req_replylen), &ext_setupinfo,
+ sizeof(ext_setupinfo)) != sizeof(ext_setupinfo))
+ result = false;
/*
Provide tracing information if requested and return.
*/
- if (BusLogic_GlobalOptions.TraceProbe)
- BusLogic_Notice("BusLogic_Check(0x%X): MultiMaster %s\n", HostAdapter, HostAdapter->IO_Address, (Result ? "Found" : "Not Found"));
- return Result;
+ if (blogic_global_options.trace_probe)
+ blogic_notice("BusLogic_Check(0x%lX): MultiMaster %s\n", adapter,
+ adapter->io_addr,
+ (result ? "Found" : "Not Found"));
+ return result;
}
/*
- BusLogic_ReadHostAdapterConfiguration reads the Configuration Information
+ blogic_rdconfig reads the Configuration Information
from Host Adapter and initializes the Host Adapter structure.
*/
-static bool __init BusLogic_ReadHostAdapterConfiguration(struct BusLogic_HostAdapter
- *HostAdapter)
+static bool __init blogic_rdconfig(struct blogic_adapter *adapter)
{
- struct BusLogic_BoardID BoardID;
- struct BusLogic_Configuration Configuration;
- struct BusLogic_SetupInformation SetupInformation;
- struct BusLogic_ExtendedSetupInformation ExtendedSetupInformation;
- unsigned char HostAdapterModelNumber[5];
- unsigned char FirmwareVersion3rdDigit;
- unsigned char FirmwareVersionLetter;
- struct BusLogic_PCIHostAdapterInformation PCIHostAdapterInformation;
- struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest;
- struct BusLogic_AutoSCSIData AutoSCSIData;
- union BusLogic_GeometryRegister GeometryRegister;
- unsigned char RequestedReplyLength;
- unsigned char *TargetPointer, Character;
- int TargetID, i;
- /*
- Configuration Information for FlashPoint Host Adapters is provided in the
- FlashPoint_Info structure by the FlashPoint SCCB Manager's Probe Function.
- Initialize fields in the Host Adapter structure from the FlashPoint_Info
- structure.
- */
- if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
- struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo;
- TargetPointer = HostAdapter->ModelName;
- *TargetPointer++ = 'B';
- *TargetPointer++ = 'T';
- *TargetPointer++ = '-';
- for (i = 0; i < sizeof(FlashPointInfo->ModelNumber); i++)
- *TargetPointer++ = FlashPointInfo->ModelNumber[i];
- *TargetPointer++ = '\0';
- strcpy(HostAdapter->FirmwareVersion, FlashPoint_FirmwareVersion);
- HostAdapter->SCSI_ID = FlashPointInfo->SCSI_ID;
- HostAdapter->ExtendedTranslationEnabled = FlashPointInfo->ExtendedTranslationEnabled;
- HostAdapter->ParityCheckingEnabled = FlashPointInfo->ParityCheckingEnabled;
- HostAdapter->BusResetEnabled = !FlashPointInfo->HostSoftReset;
- HostAdapter->LevelSensitiveInterrupt = true;
- HostAdapter->HostWideSCSI = FlashPointInfo->HostWideSCSI;
- HostAdapter->HostDifferentialSCSI = false;
- HostAdapter->HostSupportsSCAM = true;
- HostAdapter->HostUltraSCSI = true;
- HostAdapter->ExtendedLUNSupport = true;
- HostAdapter->TerminationInfoValid = true;
- HostAdapter->LowByteTerminated = FlashPointInfo->LowByteTerminated;
- HostAdapter->HighByteTerminated = FlashPointInfo->HighByteTerminated;
- HostAdapter->SCAM_Enabled = FlashPointInfo->SCAM_Enabled;
- HostAdapter->SCAM_Level2 = FlashPointInfo->SCAM_Level2;
- HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
- HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
- HostAdapter->MaxLogicalUnits = 32;
- HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize;
- HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize;
- HostAdapter->DriverQueueDepth = 255;
- HostAdapter->HostAdapterQueueDepth = HostAdapter->DriverQueueDepth;
- HostAdapter->SynchronousPermitted = FlashPointInfo->SynchronousPermitted;
- HostAdapter->FastPermitted = FlashPointInfo->FastPermitted;
- HostAdapter->UltraPermitted = FlashPointInfo->UltraPermitted;
- HostAdapter->WidePermitted = FlashPointInfo->WidePermitted;
- HostAdapter->DisconnectPermitted = FlashPointInfo->DisconnectPermitted;
- HostAdapter->TaggedQueuingPermitted = 0xFFFF;
- goto Common;
+ struct blogic_board_id id;
+ struct blogic_config config;
+ struct blogic_setup_info setupinfo;
+ struct blogic_ext_setup ext_setupinfo;
+ unsigned char model[5];
+ unsigned char fw_ver_digit3;
+ unsigned char fw_ver_letter;
+ struct blogic_adapter_info adapter_info;
+ struct blogic_fetch_localram fetch_localram;
+ struct blogic_autoscsi autoscsi;
+ union blogic_geo_reg georeg;
+ unsigned char req_replylen;
+ unsigned char *tgt, ch;
+ int tgt_id, i;
+ /*
+ Configuration Information for FlashPoint Host Adapters is
+ provided in the fpoint_info structure by the FlashPoint
+ SCCB Manager's Probe Function. Initialize fields in the
+ Host Adapter structure from the fpoint_info structure.
+ */
+ if (blogic_flashpoint_type(adapter)) {
+ struct fpoint_info *fpinfo = &adapter->fpinfo;
+ tgt = adapter->model;
+ *tgt++ = 'B';
+ *tgt++ = 'T';
+ *tgt++ = '-';
+ for (i = 0; i < sizeof(fpinfo->model); i++)
+ *tgt++ = fpinfo->model[i];
+ *tgt++ = '\0';
+ strcpy(adapter->fw_ver, FLASHPOINT_FW_VER);
+ adapter->scsi_id = fpinfo->scsi_id;
+ adapter->ext_trans_enable = fpinfo->ext_trans_enable;
+ adapter->parity = fpinfo->parity;
+ adapter->reset_enabled = !fpinfo->softreset;
+ adapter->level_int = true;
+ adapter->wide = fpinfo->wide;
+ adapter->differential = false;
+ adapter->scam = true;
+ adapter->ultra = true;
+ adapter->ext_lun = true;
+ adapter->terminfo_valid = true;
+ adapter->low_term = fpinfo->low_term;
+ adapter->high_term = fpinfo->high_term;
+ adapter->scam_enabled = fpinfo->scam_enabled;
+ adapter->scam_lev2 = fpinfo->scam_lev2;
+ adapter->drvr_sglimit = BLOGIC_SG_LIMIT;
+ adapter->maxdev = (adapter->wide ? 16 : 8);
+ adapter->maxlun = 32;
+ adapter->initccbs = 4 * BLOGIC_CCB_GRP_ALLOCSIZE;
+ adapter->inc_ccbs = BLOGIC_CCB_GRP_ALLOCSIZE;
+ adapter->drvr_qdepth = 255;
+ adapter->adapter_qdepth = adapter->drvr_qdepth;
+ adapter->sync_ok = fpinfo->sync_ok;
+ adapter->fast_ok = fpinfo->fast_ok;
+ adapter->ultra_ok = fpinfo->ultra_ok;
+ adapter->wide_ok = fpinfo->wide_ok;
+ adapter->discon_ok = fpinfo->discon_ok;
+ adapter->tagq_ok = 0xFFFF;
+ goto common;
}
/*
Issue the Inquire Board ID command.
*/
- if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, &BoardID, sizeof(BoardID)) != sizeof(BoardID))
- return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID");
+ if (blogic_cmd(adapter, BLOGIC_GET_BOARD_ID, NULL, 0, &id,
+ sizeof(id)) != sizeof(id))
+ return blogic_failure(adapter, "INQUIRE BOARD ID");
/*
Issue the Inquire Configuration command.
*/
- if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0, &Configuration, sizeof(Configuration))
- != sizeof(Configuration))
- return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION");
+ if (blogic_cmd(adapter, BLOGIC_INQ_CONFIG, NULL, 0, &config,
+ sizeof(config))
+ != sizeof(config))
+ return blogic_failure(adapter, "INQUIRE CONFIGURATION");
/*
Issue the Inquire Setup Information command.
*/
- RequestedReplyLength = sizeof(SetupInformation);
- if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &SetupInformation, sizeof(SetupInformation))
- != sizeof(SetupInformation))
- return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+ req_replylen = sizeof(setupinfo);
+ if (blogic_cmd(adapter, BLOGIC_INQ_SETUPINFO, &req_replylen,
+ sizeof(req_replylen), &setupinfo,
+ sizeof(setupinfo)) != sizeof(setupinfo))
+ return blogic_failure(adapter, "INQUIRE SETUP INFORMATION");
/*
Issue the Inquire Extended Setup Information command.
*/
- RequestedReplyLength = sizeof(ExtendedSetupInformation);
- if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &ExtendedSetupInformation, sizeof(ExtendedSetupInformation))
- != sizeof(ExtendedSetupInformation))
- return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
+ req_replylen = sizeof(ext_setupinfo);
+ if (blogic_cmd(adapter, BLOGIC_INQ_EXTSETUP, &req_replylen,
+ sizeof(req_replylen), &ext_setupinfo,
+ sizeof(ext_setupinfo)) != sizeof(ext_setupinfo))
+ return blogic_failure(adapter,
+ "INQUIRE EXTENDED SETUP INFORMATION");
/*
Issue the Inquire Firmware Version 3rd Digit command.
*/
- FirmwareVersion3rdDigit = '\0';
- if (BoardID.FirmwareVersion1stDigit > '0')
- if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit, NULL, 0, &FirmwareVersion3rdDigit, sizeof(FirmwareVersion3rdDigit))
- != sizeof(FirmwareVersion3rdDigit))
- return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+ fw_ver_digit3 = '\0';
+ if (id.fw_ver_digit1 > '0')
+ if (blogic_cmd(adapter, BLOGIC_INQ_FWVER_D3, NULL, 0,
+ &fw_ver_digit3,
+ sizeof(fw_ver_digit3)) != sizeof(fw_ver_digit3))
+ return blogic_failure(adapter,
+ "INQUIRE FIRMWARE 3RD DIGIT");
/*
Issue the Inquire Host Adapter Model Number command.
*/
- if (ExtendedSetupInformation.BusType == 'A' && BoardID.FirmwareVersion1stDigit == '2')
+ if (ext_setupinfo.bus_type == 'A' && id.fw_ver_digit1 == '2')
/* BusLogic BT-542B ISA 2.xx */
- strcpy(HostAdapterModelNumber, "542B");
- else if (ExtendedSetupInformation.BusType == 'E' && BoardID.FirmwareVersion1stDigit == '2' && (BoardID.FirmwareVersion2ndDigit <= '1' || (BoardID.FirmwareVersion2ndDigit == '2' && FirmwareVersion3rdDigit == '0')))
+ strcpy(model, "542B");
+ else if (ext_setupinfo.bus_type == 'E' && id.fw_ver_digit1 == '2' &&
+ (id.fw_ver_digit2 <= '1' || (id.fw_ver_digit2 == '2' &&
+ fw_ver_digit3 == '0')))
/* BusLogic BT-742A EISA 2.1x or 2.20 */
- strcpy(HostAdapterModelNumber, "742A");
- else if (ExtendedSetupInformation.BusType == 'E' && BoardID.FirmwareVersion1stDigit == '0')
+ strcpy(model, "742A");
+ else if (ext_setupinfo.bus_type == 'E' && id.fw_ver_digit1 == '0')
/* AMI FastDisk EISA Series 441 0.x */
- strcpy(HostAdapterModelNumber, "747A");
+ strcpy(model, "747A");
else {
- RequestedReplyLength = sizeof(HostAdapterModelNumber);
- if (BusLogic_Command(HostAdapter, BusLogic_InquireHostAdapterModelNumber, &RequestedReplyLength, sizeof(RequestedReplyLength), &HostAdapterModelNumber, sizeof(HostAdapterModelNumber))
- != sizeof(HostAdapterModelNumber))
- return BusLogic_Failure(HostAdapter, "INQUIRE HOST ADAPTER MODEL NUMBER");
+ req_replylen = sizeof(model);
+ if (blogic_cmd(adapter, BLOGIC_INQ_MODELNO, &req_replylen,
+ sizeof(req_replylen), &model,
+ sizeof(model)) != sizeof(model))
+ return blogic_failure(adapter,
+ "INQUIRE HOST ADAPTER MODEL NUMBER");
}
/*
- BusLogic MultiMaster Host Adapters can be identified by their model number
- and the major version number of their firmware as follows:
+ BusLogic MultiMaster Host Adapters can be identified by their
+ model number and the major version number of their firmware
+ as follows:
5.xx BusLogic "W" Series Host Adapters:
BT-948/958/958D
@@ -1375,498 +1371,497 @@ static bool __init BusLogic_ReadHostAdapterConfiguration(struct BusLogic_HostAda
0.xx AMI FastDisk VLB/EISA BusLogic Clone Host Adapter
*/
/*
- Save the Model Name and Host Adapter Name in the Host Adapter structure.
+ Save the Model Name and Host Adapter Name in the Host Adapter
+ structure.
*/
- TargetPointer = HostAdapter->ModelName;
- *TargetPointer++ = 'B';
- *TargetPointer++ = 'T';
- *TargetPointer++ = '-';
- for (i = 0; i < sizeof(HostAdapterModelNumber); i++) {
- Character = HostAdapterModelNumber[i];
- if (Character == ' ' || Character == '\0')
+ tgt = adapter->model;
+ *tgt++ = 'B';
+ *tgt++ = 'T';
+ *tgt++ = '-';
+ for (i = 0; i < sizeof(model); i++) {
+ ch = model[i];
+ if (ch == ' ' || ch == '\0')
break;
- *TargetPointer++ = Character;
+ *tgt++ = ch;
}
- *TargetPointer++ = '\0';
+ *tgt++ = '\0';
/*
Save the Firmware Version in the Host Adapter structure.
*/
- TargetPointer = HostAdapter->FirmwareVersion;
- *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
- *TargetPointer++ = '.';
- *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
- if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
- *TargetPointer++ = FirmwareVersion3rdDigit;
- *TargetPointer = '\0';
+ tgt = adapter->fw_ver;
+ *tgt++ = id.fw_ver_digit1;
+ *tgt++ = '.';
+ *tgt++ = id.fw_ver_digit2;
+ if (fw_ver_digit3 != ' ' && fw_ver_digit3 != '\0')
+ *tgt++ = fw_ver_digit3;
+ *tgt = '\0';
/*
Issue the Inquire Firmware Version Letter command.
*/
- if (strcmp(HostAdapter->FirmwareVersion, "3.3") >= 0) {
- if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter, NULL, 0, &FirmwareVersionLetter, sizeof(FirmwareVersionLetter))
- != sizeof(FirmwareVersionLetter))
- return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
- if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
- *TargetPointer++ = FirmwareVersionLetter;
- *TargetPointer = '\0';
+ if (strcmp(adapter->fw_ver, "3.3") >= 0) {
+ if (blogic_cmd(adapter, BLOGIC_INQ_FWVER_LETTER, NULL, 0,
+ &fw_ver_letter,
+ sizeof(fw_ver_letter)) != sizeof(fw_ver_letter))
+ return blogic_failure(adapter,
+ "INQUIRE FIRMWARE VERSION LETTER");
+ if (fw_ver_letter != ' ' && fw_ver_letter != '\0')
+ *tgt++ = fw_ver_letter;
+ *tgt = '\0';
}
/*
Save the Host Adapter SCSI ID in the Host Adapter structure.
*/
- HostAdapter->SCSI_ID = Configuration.HostAdapterID;
- /*
- Determine the Bus Type and save it in the Host Adapter structure, determine
- and save the IRQ Channel if necessary, and determine and save the DMA
- Channel for ISA Host Adapters.
- */
- HostAdapter->HostAdapterBusType = BusLogic_HostAdapterBusTypes[HostAdapter->ModelName[3] - '4'];
- if (HostAdapter->IRQ_Channel == 0) {
- if (Configuration.IRQ_Channel9)
- HostAdapter->IRQ_Channel = 9;
- else if (Configuration.IRQ_Channel10)
- HostAdapter->IRQ_Channel = 10;
- else if (Configuration.IRQ_Channel11)
- HostAdapter->IRQ_Channel = 11;
- else if (Configuration.IRQ_Channel12)
- HostAdapter->IRQ_Channel = 12;
- else if (Configuration.IRQ_Channel14)
- HostAdapter->IRQ_Channel = 14;
- else if (Configuration.IRQ_Channel15)
- HostAdapter->IRQ_Channel = 15;
- }
- if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus) {
- if (Configuration.DMA_Channel5)
- HostAdapter->DMA_Channel = 5;
- else if (Configuration.DMA_Channel6)
- HostAdapter->DMA_Channel = 6;
- else if (Configuration.DMA_Channel7)
- HostAdapter->DMA_Channel = 7;
+ adapter->scsi_id = config.id;
+ /*
+ Determine the Bus Type and save it in the Host Adapter structure,
+ determine and save the IRQ Channel if necessary, and determine
+ and save the DMA Channel for ISA Host Adapters.
+ */
+ adapter->adapter_bus_type =
+ blogic_adater_bus_types[adapter->model[3] - '4'];
+ if (adapter->irq_ch == 0) {
+ if (config.irq_ch9)
+ adapter->irq_ch = 9;
+ else if (config.irq_ch10)
+ adapter->irq_ch = 10;
+ else if (config.irq_ch11)
+ adapter->irq_ch = 11;
+ else if (config.irq_ch12)
+ adapter->irq_ch = 12;
+ else if (config.irq_ch14)
+ adapter->irq_ch = 14;
+ else if (config.irq_ch15)
+ adapter->irq_ch = 15;
}
/*
Determine whether Extended Translation is enabled and save it in
the Host Adapter structure.
*/
- GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
- HostAdapter->ExtendedTranslationEnabled = GeometryRegister.gr.ExtendedTranslationEnabled;
+ georeg.all = blogic_rdgeom(adapter);
+ adapter->ext_trans_enable = georeg.gr.ext_trans_enable;
/*
Save the Scatter Gather Limits, Level Sensitive Interrupt flag, Wide
SCSI flag, Differential SCSI flag, SCAM Supported flag, and
Ultra SCSI flag in the Host Adapter structure.
*/
- HostAdapter->HostAdapterScatterGatherLimit = ExtendedSetupInformation.ScatterGatherLimit;
- HostAdapter->DriverScatterGatherLimit = HostAdapter->HostAdapterScatterGatherLimit;
- if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
- HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
- if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupt)
- HostAdapter->LevelSensitiveInterrupt = true;
- HostAdapter->HostWideSCSI = ExtendedSetupInformation.HostWideSCSI;
- HostAdapter->HostDifferentialSCSI = ExtendedSetupInformation.HostDifferentialSCSI;
- HostAdapter->HostSupportsSCAM = ExtendedSetupInformation.HostSupportsSCAM;
- HostAdapter->HostUltraSCSI = ExtendedSetupInformation.HostUltraSCSI;
+ adapter->adapter_sglimit = ext_setupinfo.sg_limit;
+ adapter->drvr_sglimit = adapter->adapter_sglimit;
+ if (adapter->adapter_sglimit > BLOGIC_SG_LIMIT)
+ adapter->drvr_sglimit = BLOGIC_SG_LIMIT;
+ if (ext_setupinfo.misc.level_int)
+ adapter->level_int = true;
+ adapter->wide = ext_setupinfo.wide;
+ adapter->differential = ext_setupinfo.differential;
+ adapter->scam = ext_setupinfo.scam;
+ adapter->ultra = ext_setupinfo.ultra;
/*
Determine whether Extended LUN Format CCBs are supported and save the
information in the Host Adapter structure.
*/
- if (HostAdapter->FirmwareVersion[0] == '5' || (HostAdapter->FirmwareVersion[0] == '4' && HostAdapter->HostWideSCSI))
- HostAdapter->ExtendedLUNSupport = true;
+ if (adapter->fw_ver[0] == '5' || (adapter->fw_ver[0] == '4' &&
+ adapter->wide))
+ adapter->ext_lun = true;
/*
Issue the Inquire PCI Host Adapter Information command to read the
Termination Information from "W" series MultiMaster Host Adapters.
*/
- if (HostAdapter->FirmwareVersion[0] == '5') {
- if (BusLogic_Command(HostAdapter, BusLogic_InquirePCIHostAdapterInformation, NULL, 0, &PCIHostAdapterInformation, sizeof(PCIHostAdapterInformation))
- != sizeof(PCIHostAdapterInformation))
- return BusLogic_Failure(HostAdapter, "INQUIRE PCI HOST ADAPTER INFORMATION");
+ if (adapter->fw_ver[0] == '5') {
+ if (blogic_cmd(adapter, BLOGIC_INQ_PCI_INFO, NULL, 0,
+ &adapter_info,
+ sizeof(adapter_info)) != sizeof(adapter_info))
+ return blogic_failure(adapter,
+ "INQUIRE PCI HOST ADAPTER INFORMATION");
/*
- Save the Termination Information in the Host Adapter structure.
+ Save the Termination Information in the Host Adapter
+ structure.
*/
- if (PCIHostAdapterInformation.GenericInfoValid) {
- HostAdapter->TerminationInfoValid = true;
- HostAdapter->LowByteTerminated = PCIHostAdapterInformation.LowByteTerminated;
- HostAdapter->HighByteTerminated = PCIHostAdapterInformation.HighByteTerminated;
+ if (adapter_info.genericinfo_valid) {
+ adapter->terminfo_valid = true;
+ adapter->low_term = adapter_info.low_term;
+ adapter->high_term = adapter_info.high_term;
}
}
/*
- Issue the Fetch Host Adapter Local RAM command to read the AutoSCSI data
- from "W" and "C" series MultiMaster Host Adapters.
+ Issue the Fetch Host Adapter Local RAM command to read the
+ AutoSCSI data from "W" and "C" series MultiMaster Host Adapters.
*/
- if (HostAdapter->FirmwareVersion[0] >= '4') {
- FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_AutoSCSI_BaseOffset;
- FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIData);
- if (BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &AutoSCSIData, sizeof(AutoSCSIData))
- != sizeof(AutoSCSIData))
- return BusLogic_Failure(HostAdapter, "FETCH HOST ADAPTER LOCAL RAM");
+ if (adapter->fw_ver[0] >= '4') {
+ fetch_localram.offset = BLOGIC_AUTOSCSI_BASE;
+ fetch_localram.count = sizeof(autoscsi);
+ if (blogic_cmd(adapter, BLOGIC_FETCH_LOCALRAM, &fetch_localram,
+ sizeof(fetch_localram), &autoscsi,
+ sizeof(autoscsi)) != sizeof(autoscsi))
+ return blogic_failure(adapter,
+ "FETCH HOST ADAPTER LOCAL RAM");
/*
- Save the Parity Checking Enabled, Bus Reset Enabled, and Termination
- Information in the Host Adapter structure.
+ Save the Parity Checking Enabled, Bus Reset Enabled,
+ and Termination Information in the Host Adapter structure.
*/
- HostAdapter->ParityCheckingEnabled = AutoSCSIData.ParityCheckingEnabled;
- HostAdapter->BusResetEnabled = AutoSCSIData.BusResetEnabled;
- if (HostAdapter->FirmwareVersion[0] == '4') {
- HostAdapter->TerminationInfoValid = true;
- HostAdapter->LowByteTerminated = AutoSCSIData.LowByteTerminated;
- HostAdapter->HighByteTerminated = AutoSCSIData.HighByteTerminated;
+ adapter->parity = autoscsi.parity;
+ adapter->reset_enabled = autoscsi.reset_enabled;
+ if (adapter->fw_ver[0] == '4') {
+ adapter->terminfo_valid = true;
+ adapter->low_term = autoscsi.low_term;
+ adapter->high_term = autoscsi.high_term;
}
/*
- Save the Wide Permitted, Fast Permitted, Synchronous Permitted,
- Disconnect Permitted, Ultra Permitted, and SCAM Information in the
- Host Adapter structure.
+ Save the Wide Permitted, Fast Permitted, Synchronous
+ Permitted, Disconnect Permitted, Ultra Permitted, and
+ SCAM Information in the Host Adapter structure.
*/
- HostAdapter->WidePermitted = AutoSCSIData.WidePermitted;
- HostAdapter->FastPermitted = AutoSCSIData.FastPermitted;
- HostAdapter->SynchronousPermitted = AutoSCSIData.SynchronousPermitted;
- HostAdapter->DisconnectPermitted = AutoSCSIData.DisconnectPermitted;
- if (HostAdapter->HostUltraSCSI)
- HostAdapter->UltraPermitted = AutoSCSIData.UltraPermitted;
- if (HostAdapter->HostSupportsSCAM) {
- HostAdapter->SCAM_Enabled = AutoSCSIData.SCAM_Enabled;
- HostAdapter->SCAM_Level2 = AutoSCSIData.SCAM_Level2;
+ adapter->wide_ok = autoscsi.wide_ok;
+ adapter->fast_ok = autoscsi.fast_ok;
+ adapter->sync_ok = autoscsi.sync_ok;
+ adapter->discon_ok = autoscsi.discon_ok;
+ if (adapter->ultra)
+ adapter->ultra_ok = autoscsi.ultra_ok;
+ if (adapter->scam) {
+ adapter->scam_enabled = autoscsi.scam_enabled;
+ adapter->scam_lev2 = autoscsi.scam_lev2;
}
}
/*
- Initialize fields in the Host Adapter structure for "S" and "A" series
- MultiMaster Host Adapters.
+ Initialize fields in the Host Adapter structure for "S" and "A"
+ series MultiMaster Host Adapters.
*/
- if (HostAdapter->FirmwareVersion[0] < '4') {
- if (SetupInformation.SynchronousInitiationEnabled) {
- HostAdapter->SynchronousPermitted = 0xFF;
- if (HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus) {
- if (ExtendedSetupInformation.Misc.FastOnEISA)
- HostAdapter->FastPermitted = 0xFF;
- if (strcmp(HostAdapter->ModelName, "BT-757") == 0)
- HostAdapter->WidePermitted = 0xFF;
+ if (adapter->fw_ver[0] < '4') {
+ if (setupinfo.sync) {
+ adapter->sync_ok = 0xFF;
+ if (adapter->adapter_bus_type == BLOGIC_EISA_BUS) {
+ if (ext_setupinfo.misc.fast_on_eisa)
+ adapter->fast_ok = 0xFF;
+ if (strcmp(adapter->model, "BT-757") == 0)
+ adapter->wide_ok = 0xFF;
}
}
- HostAdapter->DisconnectPermitted = 0xFF;
- HostAdapter->ParityCheckingEnabled = SetupInformation.ParityCheckingEnabled;
- HostAdapter->BusResetEnabled = true;
+ adapter->discon_ok = 0xFF;
+ adapter->parity = setupinfo.parity;
+ adapter->reset_enabled = true;
}
/*
- Determine the maximum number of Target IDs and Logical Units supported by
- this driver for Wide and Narrow Host Adapters.
+ Determine the maximum number of Target IDs and Logical Units
+ supported by this driver for Wide and Narrow Host Adapters.
*/
- HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
- HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8);
+ adapter->maxdev = (adapter->wide ? 16 : 8);
+ adapter->maxlun = (adapter->ext_lun ? 32 : 8);
/*
Select appropriate values for the Mailbox Count, Driver Queue Depth,
- Initial CCBs, and Incremental CCBs variables based on whether or not Strict
- Round Robin Mode is supported. If Strict Round Robin Mode is supported,
- then there is no performance degradation in using the maximum possible
- number of Outgoing and Incoming Mailboxes and allowing the Tagged and
- Untagged Queue Depths to determine the actual utilization. If Strict Round
- Robin Mode is not supported, then the Host Adapter must scan all the
- Outgoing Mailboxes whenever an Outgoing Mailbox entry is made, which can
- cause a substantial performance penalty. The host adapters actually have
- room to store the following number of CCBs internally; that is, they can
- internally queue and manage this many active commands on the SCSI bus
- simultaneously. Performance measurements demonstrate that the Driver Queue
- Depth should be set to the Mailbox Count, rather than the Host Adapter
- Queue Depth (internal CCB capacity), as it is more efficient to have the
- queued commands waiting in Outgoing Mailboxes if necessary than to block
- the process in the higher levels of the SCSI Subsystem.
+ Initial CCBs, and Incremental CCBs variables based on whether
+ or not Strict Round Robin Mode is supported. If Strict Round
+ Robin Mode is supported, then there is no performance degradation
+ in using the maximum possible number of Outgoing and Incoming
+ Mailboxes and allowing the Tagged and Untagged Queue Depths to
+ determine the actual utilization. If Strict Round Robin Mode is
+ not supported, then the Host Adapter must scan all the Outgoing
+ Mailboxes whenever an Outgoing Mailbox entry is made, which can
+ cause a substantial performance penalty. The host adapters
+ actually have room to store the following number of CCBs
+ internally; that is, they can internally queue and manage this
+ many active commands on the SCSI bus simultaneously. Performance
+ measurements demonstrate that the Driver Queue Depth should be
+ set to the Mailbox Count, rather than the Host Adapter Queue
+ Depth (internal CCB capacity), as it is more efficient to have the
+ queued commands waiting in Outgoing Mailboxes if necessary than
+ to block the process in the higher levels of the SCSI Subsystem.
192 BT-948/958/958D
100 BT-946C/956C/956CD/747C/757C/757CD/445C
50 BT-545C/540CF
30 BT-747S/747D/757S/757D/445S/545S/542D/542B/742A
*/
- if (HostAdapter->FirmwareVersion[0] == '5')
- HostAdapter->HostAdapterQueueDepth = 192;
- else if (HostAdapter->FirmwareVersion[0] == '4')
- HostAdapter->HostAdapterQueueDepth = (HostAdapter->HostAdapterBusType != BusLogic_ISA_Bus ? 100 : 50);
+ if (adapter->fw_ver[0] == '5')
+ adapter->adapter_qdepth = 192;
+ else if (adapter->fw_ver[0] == '4')
+ adapter->adapter_qdepth = 100;
else
- HostAdapter->HostAdapterQueueDepth = 30;
- if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0) {
- HostAdapter->StrictRoundRobinModeSupport = true;
- HostAdapter->MailboxCount = BusLogic_MaxMailboxes;
+ adapter->adapter_qdepth = 30;
+ if (strcmp(adapter->fw_ver, "3.31") >= 0) {
+ adapter->strict_rr = true;
+ adapter->mbox_count = BLOGIC_MAX_MAILBOX;
} else {
- HostAdapter->StrictRoundRobinModeSupport = false;
- HostAdapter->MailboxCount = 32;
+ adapter->strict_rr = false;
+ adapter->mbox_count = 32;
}
- HostAdapter->DriverQueueDepth = HostAdapter->MailboxCount;
- HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize;
- HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize;
+ adapter->drvr_qdepth = adapter->mbox_count;
+ adapter->initccbs = 4 * BLOGIC_CCB_GRP_ALLOCSIZE;
+ adapter->inc_ccbs = BLOGIC_CCB_GRP_ALLOCSIZE;
/*
- Tagged Queuing support is available and operates properly on all "W" series
- MultiMaster Host Adapters, on "C" series MultiMaster Host Adapters with
- firmware version 4.22 and above, and on "S" series MultiMaster Host
- Adapters with firmware version 3.35 and above.
+ Tagged Queuing support is available and operates properly on
+ all "W" series MultiMaster Host Adapters, on "C" series
+ MultiMaster Host Adapters with firmware version 4.22 and above,
+ and on "S" series MultiMaster Host Adapters with firmware version
+ 3.35 and above.
*/
- HostAdapter->TaggedQueuingPermitted = 0;
- switch (HostAdapter->FirmwareVersion[0]) {
+ adapter->tagq_ok = 0;
+ switch (adapter->fw_ver[0]) {
case '5':
- HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ adapter->tagq_ok = 0xFFFF;
break;
case '4':
- if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0)
- HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ if (strcmp(adapter->fw_ver, "4.22") >= 0)
+ adapter->tagq_ok = 0xFFFF;
break;
case '3':
- if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0)
- HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ if (strcmp(adapter->fw_ver, "3.35") >= 0)
+ adapter->tagq_ok = 0xFFFF;
break;
}
/*
Determine the Host Adapter BIOS Address if the BIOS is enabled and
save it in the Host Adapter structure. The BIOS is disabled if the
- BIOS_Address is 0.
- */
- HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
- /*
- ISA Host Adapters require Bounce Buffers if there is more than 16MB memory.
+ bios_addr is 0.
*/
- if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus && (void *) high_memory > (void *) MAX_DMA_ADDRESS)
- HostAdapter->BounceBuffersRequired = true;
+ adapter->bios_addr = ext_setupinfo.bios_addr << 12;
/*
- BusLogic BT-445S Host Adapters prior to board revision E have a hardware
- bug whereby when the BIOS is enabled, transfers to/from the same address
- range the BIOS occupies modulo 16MB are handled incorrectly. Only properly
- functioning BT-445S Host Adapters have firmware version 3.37, so require
- that ISA Bounce Buffers be used for the buggy BT-445S models if there is
- more than 16MB memory.
+ BusLogic BT-445S Host Adapters prior to board revision E have a
+ hardware bug whereby when the BIOS is enabled, transfers to/from
+ the same address range the BIOS occupies modulo 16MB are handled
+ incorrectly. Only properly functioning BT-445S Host Adapters
+ have firmware version 3.37.
*/
- if (HostAdapter->BIOS_Address > 0 && strcmp(HostAdapter->ModelName, "BT-445S") == 0 && strcmp(HostAdapter->FirmwareVersion, "3.37") < 0 && (void *) high_memory > (void *) MAX_DMA_ADDRESS)
- HostAdapter->BounceBuffersRequired = true;
+ if (adapter->bios_addr > 0 &&
+ strcmp(adapter->model, "BT-445S") == 0 &&
+ strcmp(adapter->fw_ver, "3.37") < 0)
+ return blogic_failure(adapter, "Too old firmware");
/*
- Initialize parameters common to MultiMaster and FlashPoint Host Adapters.
+ Initialize parameters common to MultiMaster and FlashPoint
+ Host Adapters.
*/
- Common:
+common:
/*
Initialize the Host Adapter Full Model Name from the Model Name.
*/
- strcpy(HostAdapter->FullModelName, "BusLogic ");
- strcat(HostAdapter->FullModelName, HostAdapter->ModelName);
+ strcpy(adapter->full_model, "BusLogic ");
+ strcat(adapter->full_model, adapter->model);
/*
Select an appropriate value for the Tagged Queue Depth either from a
BusLogic Driver Options specification, or based on whether this Host
- Adapter requires that ISA Bounce Buffers be used. The Tagged Queue Depth
- is left at 0 for automatic determination in BusLogic_SelectQueueDepths.
- Initialize the Untagged Queue Depth.
- */
- for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) {
- unsigned char QueueDepth = 0;
- if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->QueueDepth[TargetID] > 0)
- QueueDepth = HostAdapter->DriverOptions->QueueDepth[TargetID];
- else if (HostAdapter->BounceBuffersRequired)
- QueueDepth = BusLogic_TaggedQueueDepthBB;
- HostAdapter->QueueDepth[TargetID] = QueueDepth;
- }
- if (HostAdapter->BounceBuffersRequired)
- HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepthBB;
- else
- HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth;
- if (HostAdapter->DriverOptions != NULL)
- HostAdapter->CommonQueueDepth = HostAdapter->DriverOptions->CommonQueueDepth;
- if (HostAdapter->CommonQueueDepth > 0 && HostAdapter->CommonQueueDepth < HostAdapter->UntaggedQueueDepth)
- HostAdapter->UntaggedQueueDepth = HostAdapter->CommonQueueDepth;
+ Adapter requires that ISA Bounce Buffers be used. The Tagged Queue
+ Depth is left at 0 for automatic determination in
+ BusLogic_SelectQueueDepths. Initialize the Untagged Queue Depth.
+ */
+ for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++) {
+ unsigned char qdepth = 0;
+ if (adapter->drvr_opts != NULL &&
+ adapter->drvr_opts->qdepth[tgt_id] > 0)
+ qdepth = adapter->drvr_opts->qdepth[tgt_id];
+ adapter->qdepth[tgt_id] = qdepth;
+ }
+ adapter->untag_qdepth = BLOGIC_UNTAG_DEPTH;
+ if (adapter->drvr_opts != NULL)
+ adapter->common_qdepth = adapter->drvr_opts->common_qdepth;
+ if (adapter->common_qdepth > 0 &&
+ adapter->common_qdepth < adapter->untag_qdepth)
+ adapter->untag_qdepth = adapter->common_qdepth;
/*
Tagged Queuing is only allowed if Disconnect/Reconnect is permitted.
Therefore, mask the Tagged Queuing Permitted Default bits with the
Disconnect/Reconnect Permitted bits.
*/
- HostAdapter->TaggedQueuingPermitted &= HostAdapter->DisconnectPermitted;
+ adapter->tagq_ok &= adapter->discon_ok;
/*
- Combine the default Tagged Queuing Permitted bits with any BusLogic Driver
- Options Tagged Queuing specification.
+ Combine the default Tagged Queuing Permitted bits with any
+ BusLogic Driver Options Tagged Queuing specification.
*/
- if (HostAdapter->DriverOptions != NULL)
- HostAdapter->TaggedQueuingPermitted =
- (HostAdapter->DriverOptions->TaggedQueuingPermitted & HostAdapter->DriverOptions->TaggedQueuingPermittedMask) | (HostAdapter->TaggedQueuingPermitted & ~HostAdapter->DriverOptions->TaggedQueuingPermittedMask);
+ if (adapter->drvr_opts != NULL)
+ adapter->tagq_ok = (adapter->drvr_opts->tagq_ok &
+ adapter->drvr_opts->tagq_ok_mask) |
+ (adapter->tagq_ok & ~adapter->drvr_opts->tagq_ok_mask);
/*
- Select an appropriate value for Bus Settle Time either from a BusLogic
- Driver Options specification, or from BusLogic_DefaultBusSettleTime.
+ Select an appropriate value for Bus Settle Time either from a
+ BusLogic Driver Options specification, or from
+ BLOGIC_BUS_SETTLE_TIME.
*/
- if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->BusSettleTime > 0)
- HostAdapter->BusSettleTime = HostAdapter->DriverOptions->BusSettleTime;
+ if (adapter->drvr_opts != NULL &&
+ adapter->drvr_opts->bus_settle_time > 0)
+ adapter->bus_settle_time = adapter->drvr_opts->bus_settle_time;
else
- HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime;
+ adapter->bus_settle_time = BLOGIC_BUS_SETTLE_TIME;
/*
- Indicate reading the Host Adapter Configuration completed successfully.
+ Indicate reading the Host Adapter Configuration completed
+ successfully.
*/
return true;
}
/*
- BusLogic_ReportHostAdapterConfiguration reports the configuration of
- Host Adapter.
+ blogic_reportconfig reports the configuration of Host Adapter.
*/
-static bool __init BusLogic_ReportHostAdapterConfiguration(struct BusLogic_HostAdapter
- *HostAdapter)
+static bool __init blogic_reportconfig(struct blogic_adapter *adapter)
{
- unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1;
- unsigned short SynchronousPermitted, FastPermitted;
- unsigned short UltraPermitted, WidePermitted;
- unsigned short DisconnectPermitted, TaggedQueuingPermitted;
- bool CommonSynchronousNegotiation, CommonTaggedQueueDepth;
- char SynchronousString[BusLogic_MaxTargetDevices + 1];
- char WideString[BusLogic_MaxTargetDevices + 1];
- char DisconnectString[BusLogic_MaxTargetDevices + 1];
- char TaggedQueuingString[BusLogic_MaxTargetDevices + 1];
- char *SynchronousMessage = SynchronousString;
- char *WideMessage = WideString;
- char *DisconnectMessage = DisconnectString;
- char *TaggedQueuingMessage = TaggedQueuingString;
- int TargetID;
- BusLogic_Info("Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n",
- HostAdapter, HostAdapter->ModelName,
- BusLogic_HostAdapterBusNames[HostAdapter->HostAdapterBusType], (HostAdapter->HostWideSCSI ? " Wide" : ""), (HostAdapter->HostDifferentialSCSI ? " Differential" : ""), (HostAdapter->HostUltraSCSI ? " Ultra" : ""));
- BusLogic_Info(" Firmware Version: %s, I/O Address: 0x%X, " "IRQ Channel: %d/%s\n", HostAdapter, HostAdapter->FirmwareVersion, HostAdapter->IO_Address, HostAdapter->IRQ_Channel, (HostAdapter->LevelSensitiveInterrupt ? "Level" : "Edge"));
- if (HostAdapter->HostAdapterBusType != BusLogic_PCI_Bus) {
- BusLogic_Info(" DMA Channel: ", HostAdapter);
- if (HostAdapter->DMA_Channel > 0)
- BusLogic_Info("%d, ", HostAdapter, HostAdapter->DMA_Channel);
- else
- BusLogic_Info("None, ", HostAdapter);
- if (HostAdapter->BIOS_Address > 0)
- BusLogic_Info("BIOS Address: 0x%X, ", HostAdapter, HostAdapter->BIOS_Address);
+ unsigned short alltgt_mask = (1 << adapter->maxdev) - 1;
+ unsigned short sync_ok, fast_ok;
+ unsigned short ultra_ok, wide_ok;
+ unsigned short discon_ok, tagq_ok;
+ bool common_syncneg, common_tagq_depth;
+ char syncstr[BLOGIC_MAXDEV + 1];
+ char widestr[BLOGIC_MAXDEV + 1];
+ char discon_str[BLOGIC_MAXDEV + 1];
+ char tagq_str[BLOGIC_MAXDEV + 1];
+ char *syncmsg = syncstr;
+ char *widemsg = widestr;
+ char *discon_msg = discon_str;
+ char *tagq_msg = tagq_str;
+ int tgt_id;
+
+ blogic_info("Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n", adapter, adapter->model, blogic_adapter_busnames[adapter->adapter_bus_type], (adapter->wide ? " Wide" : ""), (adapter->differential ? " Differential" : ""), (adapter->ultra ? " Ultra" : ""));
+ blogic_info(" Firmware Version: %s, I/O Address: 0x%lX, IRQ Channel: %d/%s\n", adapter, adapter->fw_ver, adapter->io_addr, adapter->irq_ch, (adapter->level_int ? "Level" : "Edge"));
+ if (adapter->adapter_bus_type != BLOGIC_PCI_BUS) {
+ blogic_info(" DMA Channel: None, ", adapter);
+ if (adapter->bios_addr > 0)
+ blogic_info("BIOS Address: 0x%X, ", adapter,
+ adapter->bios_addr);
else
- BusLogic_Info("BIOS Address: None, ", HostAdapter);
+ blogic_info("BIOS Address: None, ", adapter);
} else {
- BusLogic_Info(" PCI Bus: %d, Device: %d, Address: ", HostAdapter, HostAdapter->Bus, HostAdapter->Device);
- if (HostAdapter->PCI_Address > 0)
- BusLogic_Info("0x%X, ", HostAdapter, HostAdapter->PCI_Address);
+ blogic_info(" PCI Bus: %d, Device: %d, Address: ", adapter,
+ adapter->bus, adapter->dev);
+ if (adapter->pci_addr > 0)
+ blogic_info("0x%lX, ", adapter, adapter->pci_addr);
else
- BusLogic_Info("Unassigned, ", HostAdapter);
- }
- BusLogic_Info("Host Adapter SCSI ID: %d\n", HostAdapter, HostAdapter->SCSI_ID);
- BusLogic_Info(" Parity Checking: %s, Extended Translation: %s\n", HostAdapter, (HostAdapter->ParityCheckingEnabled ? "Enabled" : "Disabled"), (HostAdapter->ExtendedTranslationEnabled ? "Enabled" : "Disabled"));
- AllTargetsMask &= ~(1 << HostAdapter->SCSI_ID);
- SynchronousPermitted = HostAdapter->SynchronousPermitted & AllTargetsMask;
- FastPermitted = HostAdapter->FastPermitted & AllTargetsMask;
- UltraPermitted = HostAdapter->UltraPermitted & AllTargetsMask;
- if ((BusLogic_MultiMasterHostAdapterP(HostAdapter) && (HostAdapter->FirmwareVersion[0] >= '4' || HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus)) || BusLogic_FlashPointHostAdapterP(HostAdapter)) {
- CommonSynchronousNegotiation = false;
- if (SynchronousPermitted == 0) {
- SynchronousMessage = "Disabled";
- CommonSynchronousNegotiation = true;
- } else if (SynchronousPermitted == AllTargetsMask) {
- if (FastPermitted == 0) {
- SynchronousMessage = "Slow";
- CommonSynchronousNegotiation = true;
- } else if (FastPermitted == AllTargetsMask) {
- if (UltraPermitted == 0) {
- SynchronousMessage = "Fast";
- CommonSynchronousNegotiation = true;
- } else if (UltraPermitted == AllTargetsMask) {
- SynchronousMessage = "Ultra";
- CommonSynchronousNegotiation = true;
+ blogic_info("Unassigned, ", adapter);
+ }
+ blogic_info("Host Adapter SCSI ID: %d\n", adapter, adapter->scsi_id);
+ blogic_info(" Parity Checking: %s, Extended Translation: %s\n",
+ adapter, (adapter->parity ? "Enabled" : "Disabled"),
+ (adapter->ext_trans_enable ? "Enabled" : "Disabled"));
+ alltgt_mask &= ~(1 << adapter->scsi_id);
+ sync_ok = adapter->sync_ok & alltgt_mask;
+ fast_ok = adapter->fast_ok & alltgt_mask;
+ ultra_ok = adapter->ultra_ok & alltgt_mask;
+ if ((blogic_multimaster_type(adapter) &&
+ (adapter->fw_ver[0] >= '4' ||
+ adapter->adapter_bus_type == BLOGIC_EISA_BUS)) ||
+ blogic_flashpoint_type(adapter)) {
+ common_syncneg = false;
+ if (sync_ok == 0) {
+ syncmsg = "Disabled";
+ common_syncneg = true;
+ } else if (sync_ok == alltgt_mask) {
+ if (fast_ok == 0) {
+ syncmsg = "Slow";
+ common_syncneg = true;
+ } else if (fast_ok == alltgt_mask) {
+ if (ultra_ok == 0) {
+ syncmsg = "Fast";
+ common_syncneg = true;
+ } else if (ultra_ok == alltgt_mask) {
+ syncmsg = "Ultra";
+ common_syncneg = true;
}
}
}
- if (!CommonSynchronousNegotiation) {
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- SynchronousString[TargetID] = ((!(SynchronousPermitted & (1 << TargetID))) ? 'N' : (!(FastPermitted & (1 << TargetID)) ? 'S' : (!(UltraPermitted & (1 << TargetID)) ? 'F' : 'U')));
- SynchronousString[HostAdapter->SCSI_ID] = '#';
- SynchronousString[HostAdapter->MaxTargetDevices] = '\0';
+ if (!common_syncneg) {
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ syncstr[tgt_id] = ((!(sync_ok & (1 << tgt_id))) ? 'N' : (!(fast_ok & (1 << tgt_id)) ? 'S' : (!(ultra_ok & (1 << tgt_id)) ? 'F' : 'U')));
+ syncstr[adapter->scsi_id] = '#';
+ syncstr[adapter->maxdev] = '\0';
}
} else
- SynchronousMessage = (SynchronousPermitted == 0 ? "Disabled" : "Enabled");
- WidePermitted = HostAdapter->WidePermitted & AllTargetsMask;
- if (WidePermitted == 0)
- WideMessage = "Disabled";
- else if (WidePermitted == AllTargetsMask)
- WideMessage = "Enabled";
+ syncmsg = (sync_ok == 0 ? "Disabled" : "Enabled");
+ wide_ok = adapter->wide_ok & alltgt_mask;
+ if (wide_ok == 0)
+ widemsg = "Disabled";
+ else if (wide_ok == alltgt_mask)
+ widemsg = "Enabled";
else {
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- WideString[TargetID] = ((WidePermitted & (1 << TargetID)) ? 'Y' : 'N');
- WideString[HostAdapter->SCSI_ID] = '#';
- WideString[HostAdapter->MaxTargetDevices] = '\0';
- }
- DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask;
- if (DisconnectPermitted == 0)
- DisconnectMessage = "Disabled";
- else if (DisconnectPermitted == AllTargetsMask)
- DisconnectMessage = "Enabled";
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ widestr[tgt_id] = ((wide_ok & (1 << tgt_id)) ? 'Y' : 'N');
+ widestr[adapter->scsi_id] = '#';
+ widestr[adapter->maxdev] = '\0';
+ }
+ discon_ok = adapter->discon_ok & alltgt_mask;
+ if (discon_ok == 0)
+ discon_msg = "Disabled";
+ else if (discon_ok == alltgt_mask)
+ discon_msg = "Enabled";
else {
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- DisconnectString[TargetID] = ((DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
- DisconnectString[HostAdapter->SCSI_ID] = '#';
- DisconnectString[HostAdapter->MaxTargetDevices] = '\0';
- }
- TaggedQueuingPermitted = HostAdapter->TaggedQueuingPermitted & AllTargetsMask;
- if (TaggedQueuingPermitted == 0)
- TaggedQueuingMessage = "Disabled";
- else if (TaggedQueuingPermitted == AllTargetsMask)
- TaggedQueuingMessage = "Enabled";
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ discon_str[tgt_id] = ((discon_ok & (1 << tgt_id)) ? 'Y' : 'N');
+ discon_str[adapter->scsi_id] = '#';
+ discon_str[adapter->maxdev] = '\0';
+ }
+ tagq_ok = adapter->tagq_ok & alltgt_mask;
+ if (tagq_ok == 0)
+ tagq_msg = "Disabled";
+ else if (tagq_ok == alltgt_mask)
+ tagq_msg = "Enabled";
else {
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- TaggedQueuingString[TargetID] = ((TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
- TaggedQueuingString[HostAdapter->SCSI_ID] = '#';
- TaggedQueuingString[HostAdapter->MaxTargetDevices] = '\0';
- }
- BusLogic_Info(" Synchronous Negotiation: %s, Wide Negotiation: %s\n", HostAdapter, SynchronousMessage, WideMessage);
- BusLogic_Info(" Disconnect/Reconnect: %s, Tagged Queuing: %s\n", HostAdapter, DisconnectMessage, TaggedQueuingMessage);
- if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
- BusLogic_Info(" Scatter/Gather Limit: %d of %d segments, " "Mailboxes: %d\n", HostAdapter, HostAdapter->DriverScatterGatherLimit, HostAdapter->HostAdapterScatterGatherLimit, HostAdapter->MailboxCount);
- BusLogic_Info(" Driver Queue Depth: %d, " "Host Adapter Queue Depth: %d\n", HostAdapter, HostAdapter->DriverQueueDepth, HostAdapter->HostAdapterQueueDepth);
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ tagq_str[tgt_id] = ((tagq_ok & (1 << tgt_id)) ? 'Y' : 'N');
+ tagq_str[adapter->scsi_id] = '#';
+ tagq_str[adapter->maxdev] = '\0';
+ }
+ blogic_info(" Synchronous Negotiation: %s, Wide Negotiation: %s\n",
+ adapter, syncmsg, widemsg);
+ blogic_info(" Disconnect/Reconnect: %s, Tagged Queuing: %s\n", adapter,
+ discon_msg, tagq_msg);
+ if (blogic_multimaster_type(adapter)) {
+ blogic_info(" Scatter/Gather Limit: %d of %d segments, Mailboxes: %d\n", adapter, adapter->drvr_sglimit, adapter->adapter_sglimit, adapter->mbox_count);
+ blogic_info(" Driver Queue Depth: %d, Host Adapter Queue Depth: %d\n", adapter, adapter->drvr_qdepth, adapter->adapter_qdepth);
} else
- BusLogic_Info(" Driver Queue Depth: %d, " "Scatter/Gather Limit: %d segments\n", HostAdapter, HostAdapter->DriverQueueDepth, HostAdapter->DriverScatterGatherLimit);
- BusLogic_Info(" Tagged Queue Depth: ", HostAdapter);
- CommonTaggedQueueDepth = true;
- for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- if (HostAdapter->QueueDepth[TargetID] != HostAdapter->QueueDepth[0]) {
- CommonTaggedQueueDepth = false;
+ blogic_info(" Driver Queue Depth: %d, Scatter/Gather Limit: %d segments\n", adapter, adapter->drvr_qdepth, adapter->drvr_sglimit);
+ blogic_info(" Tagged Queue Depth: ", adapter);
+ common_tagq_depth = true;
+ for (tgt_id = 1; tgt_id < adapter->maxdev; tgt_id++)
+ if (adapter->qdepth[tgt_id] != adapter->qdepth[0]) {
+ common_tagq_depth = false;
break;
}
- if (CommonTaggedQueueDepth) {
- if (HostAdapter->QueueDepth[0] > 0)
- BusLogic_Info("%d", HostAdapter, HostAdapter->QueueDepth[0]);
+ if (common_tagq_depth) {
+ if (adapter->qdepth[0] > 0)
+ blogic_info("%d", adapter, adapter->qdepth[0]);
else
- BusLogic_Info("Automatic", HostAdapter);
+ blogic_info("Automatic", adapter);
} else
- BusLogic_Info("Individual", HostAdapter);
- BusLogic_Info(", Untagged Queue Depth: %d\n", HostAdapter, HostAdapter->UntaggedQueueDepth);
- if (HostAdapter->TerminationInfoValid) {
- if (HostAdapter->HostWideSCSI)
- BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter, (HostAdapter->LowByteTerminated ? (HostAdapter->HighByteTerminated ? "Both Enabled" : "Low Enabled")
- : (HostAdapter->HighByteTerminated ? "High Enabled" : "Both Disabled")));
+ blogic_info("Individual", adapter);
+ blogic_info(", Untagged Queue Depth: %d\n", adapter,
+ adapter->untag_qdepth);
+ if (adapter->terminfo_valid) {
+ if (adapter->wide)
+ blogic_info(" SCSI Bus Termination: %s", adapter,
+ (adapter->low_term ? (adapter->high_term ? "Both Enabled" : "Low Enabled") : (adapter->high_term ? "High Enabled" : "Both Disabled")));
else
- BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter, (HostAdapter->LowByteTerminated ? "Enabled" : "Disabled"));
- if (HostAdapter->HostSupportsSCAM)
- BusLogic_Info(", SCAM: %s", HostAdapter, (HostAdapter->SCAM_Enabled ? (HostAdapter->SCAM_Level2 ? "Enabled, Level 2" : "Enabled, Level 1")
- : "Disabled"));
- BusLogic_Info("\n", HostAdapter);
+ blogic_info(" SCSI Bus Termination: %s", adapter,
+ (adapter->low_term ? "Enabled" : "Disabled"));
+ if (adapter->scam)
+ blogic_info(", SCAM: %s", adapter,
+ (adapter->scam_enabled ? (adapter->scam_lev2 ? "Enabled, Level 2" : "Enabled, Level 1") : "Disabled"));
+ blogic_info("\n", adapter);
}
/*
- Indicate reporting the Host Adapter configuration completed successfully.
+ Indicate reporting the Host Adapter configuration completed
+ successfully.
*/
return true;
}
/*
- BusLogic_AcquireResources acquires the system resources necessary to use
+ blogic_getres acquires the system resources necessary to use
Host Adapter.
*/
-static bool __init BusLogic_AcquireResources(struct BusLogic_HostAdapter *HostAdapter)
+static bool __init blogic_getres(struct blogic_adapter *adapter)
{
- if (HostAdapter->IRQ_Channel == 0) {
- BusLogic_Error("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n", HostAdapter);
+ if (adapter->irq_ch == 0) {
+ blogic_err("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n",
+ adapter);
return false;
}
/*
Acquire shared access to the IRQ Channel.
*/
- if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler, IRQF_SHARED, HostAdapter->FullModelName, HostAdapter) < 0) {
- BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", HostAdapter, HostAdapter->IRQ_Channel);
+ if (request_irq(adapter->irq_ch, blogic_inthandler, IRQF_SHARED,
+ adapter->full_model, adapter) < 0) {
+ blogic_err("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
+ adapter, adapter->irq_ch);
return false;
}
- HostAdapter->IRQ_ChannelAcquired = true;
- /*
- Acquire exclusive access to the DMA Channel.
- */
- if (HostAdapter->DMA_Channel > 0) {
- if (request_dma(HostAdapter->DMA_Channel, HostAdapter->FullModelName) < 0) {
- BusLogic_Error("UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n", HostAdapter, HostAdapter->DMA_Channel);
- return false;
- }
- set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE);
- enable_dma(HostAdapter->DMA_Channel);
- HostAdapter->DMA_ChannelAcquired = true;
- }
+ adapter->irq_acquired = true;
/*
Indicate the System Resource Acquisition completed successfully,
*/
@@ -1875,127 +1870,142 @@ static bool __init BusLogic_AcquireResources(struct BusLogic_HostAdapter *HostAd
/*
- BusLogic_ReleaseResources releases any system resources previously acquired
- by BusLogic_AcquireResources.
+ blogic_relres releases any system resources previously acquired
+ by blogic_getres.
*/
-static void BusLogic_ReleaseResources(struct BusLogic_HostAdapter *HostAdapter)
+static void blogic_relres(struct blogic_adapter *adapter)
{
/*
Release shared access to the IRQ Channel.
*/
- if (HostAdapter->IRQ_ChannelAcquired)
- free_irq(HostAdapter->IRQ_Channel, HostAdapter);
- /*
- Release exclusive access to the DMA Channel.
- */
- if (HostAdapter->DMA_ChannelAcquired)
- free_dma(HostAdapter->DMA_Channel);
+ if (adapter->irq_acquired)
+ free_irq(adapter->irq_ch, adapter);
/*
Release any allocated memory structs not released elsewhere
*/
- if (HostAdapter->MailboxSpace)
- pci_free_consistent(HostAdapter->PCI_Device, HostAdapter->MailboxSize, HostAdapter->MailboxSpace, HostAdapter->MailboxSpaceHandle);
- pci_dev_put(HostAdapter->PCI_Device);
- HostAdapter->MailboxSpace = NULL;
- HostAdapter->MailboxSpaceHandle = 0;
- HostAdapter->MailboxSize = 0;
+ if (adapter->mbox_space)
+ dma_free_coherent(&adapter->pci_device->dev, adapter->mbox_sz,
+ adapter->mbox_space, adapter->mbox_space_handle);
+ pci_dev_put(adapter->pci_device);
+ adapter->mbox_space = NULL;
+ adapter->mbox_space_handle = 0;
+ adapter->mbox_sz = 0;
}
/*
- BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only
+ blogic_initadapter initializes Host Adapter. This is the only
function called during SCSI Host Adapter detection which modifies the state
of the Host Adapter from its initial power on or hard reset state.
*/
-static bool BusLogic_InitializeHostAdapter(struct BusLogic_HostAdapter
- *HostAdapter)
+static bool blogic_initadapter(struct blogic_adapter *adapter)
{
- struct BusLogic_ExtendedMailboxRequest ExtendedMailboxRequest;
- enum BusLogic_RoundRobinModeRequest RoundRobinModeRequest;
- enum BusLogic_SetCCBFormatRequest SetCCBFormatRequest;
- int TargetID;
+ struct blogic_extmbox_req extmbox_req;
+ enum blogic_rr_req rr_req;
+ enum blogic_setccb_fmt setccb_fmt;
+ int tgt_id;
+
/*
- Initialize the pointers to the first and last CCBs that are queued for
- completion processing.
+ Initialize the pointers to the first and last CCBs that are
+ queued for completion processing.
*/
- HostAdapter->FirstCompletedCCB = NULL;
- HostAdapter->LastCompletedCCB = NULL;
+ adapter->firstccb = NULL;
+ adapter->lastccb = NULL;
+
/*
Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active,
Command Successful Flag, Active Commands, and Commands Since Reset
for each Target Device.
*/
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
- HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL;
- HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false;
- HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag = false;
- HostAdapter->ActiveCommands[TargetID] = 0;
- HostAdapter->CommandsSinceReset[TargetID] = 0;
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) {
+ adapter->bdr_pend[tgt_id] = NULL;
+ adapter->tgt_flags[tgt_id].tagq_active = false;
+ adapter->tgt_flags[tgt_id].cmd_good = false;
+ adapter->active_cmds[tgt_id] = 0;
+ adapter->cmds_since_rst[tgt_id] = 0;
}
+
/*
FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes.
*/
- if (BusLogic_FlashPointHostAdapterP(HostAdapter))
- goto Done;
+ if (blogic_flashpoint_type(adapter))
+ goto done;
+
/*
Initialize the Outgoing and Incoming Mailbox pointers.
*/
- HostAdapter->MailboxSize = HostAdapter->MailboxCount * (sizeof(struct BusLogic_OutgoingMailbox) + sizeof(struct BusLogic_IncomingMailbox));
- HostAdapter->MailboxSpace = pci_alloc_consistent(HostAdapter->PCI_Device, HostAdapter->MailboxSize, &HostAdapter->MailboxSpaceHandle);
- if (HostAdapter->MailboxSpace == NULL)
- return BusLogic_Failure(HostAdapter, "MAILBOX ALLOCATION");
- HostAdapter->FirstOutgoingMailbox = (struct BusLogic_OutgoingMailbox *) HostAdapter->MailboxSpace;
- HostAdapter->LastOutgoingMailbox = HostAdapter->FirstOutgoingMailbox + HostAdapter->MailboxCount - 1;
- HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
- HostAdapter->FirstIncomingMailbox = (struct BusLogic_IncomingMailbox *) (HostAdapter->LastOutgoingMailbox + 1);
- HostAdapter->LastIncomingMailbox = HostAdapter->FirstIncomingMailbox + HostAdapter->MailboxCount - 1;
- HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ adapter->mbox_sz = adapter->mbox_count * (sizeof(struct blogic_outbox) + sizeof(struct blogic_inbox));
+ adapter->mbox_space = dma_alloc_coherent(&adapter->pci_device->dev,
+ adapter->mbox_sz, &adapter->mbox_space_handle,
+ GFP_KERNEL);
+ if (adapter->mbox_space == NULL)
+ return blogic_failure(adapter, "MAILBOX ALLOCATION");
+ adapter->first_outbox = (struct blogic_outbox *) adapter->mbox_space;
+ adapter->last_outbox = adapter->first_outbox + adapter->mbox_count - 1;
+ adapter->next_outbox = adapter->first_outbox;
+ adapter->first_inbox = (struct blogic_inbox *) (adapter->last_outbox + 1);
+ adapter->last_inbox = adapter->first_inbox + adapter->mbox_count - 1;
+ adapter->next_inbox = adapter->first_inbox;
/*
Initialize the Outgoing and Incoming Mailbox structures.
*/
- memset(HostAdapter->FirstOutgoingMailbox, 0, HostAdapter->MailboxCount * sizeof(struct BusLogic_OutgoingMailbox));
- memset(HostAdapter->FirstIncomingMailbox, 0, HostAdapter->MailboxCount * sizeof(struct BusLogic_IncomingMailbox));
+ memset(adapter->first_outbox, 0,
+ adapter->mbox_count * sizeof(struct blogic_outbox));
+ memset(adapter->first_inbox, 0,
+ adapter->mbox_count * sizeof(struct blogic_inbox));
+
/*
- Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
+ Initialize the Host Adapter's Pointer to the Outgoing/Incoming
+ Mailboxes.
*/
- ExtendedMailboxRequest.MailboxCount = HostAdapter->MailboxCount;
- ExtendedMailboxRequest.BaseMailboxAddress = (u32) HostAdapter->MailboxSpaceHandle;
- if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox, &ExtendedMailboxRequest, sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
- return BusLogic_Failure(HostAdapter, "MAILBOX INITIALIZATION");
+ extmbox_req.mbox_count = adapter->mbox_count;
+ extmbox_req.base_mbox_addr = (u32) adapter->mbox_space_handle;
+ if (blogic_cmd(adapter, BLOGIC_INIT_EXT_MBOX, &extmbox_req,
+ sizeof(extmbox_req), NULL, 0) < 0)
+ return blogic_failure(adapter, "MAILBOX INITIALIZATION");
/*
- Enable Strict Round Robin Mode if supported by the Host Adapter. In
- Strict Round Robin Mode, the Host Adapter only looks at the next Outgoing
- Mailbox for each new command, rather than scanning through all the
- Outgoing Mailboxes to find any that have new commands in them. Strict
- Round Robin Mode is significantly more efficient.
+ Enable Strict Round Robin Mode if supported by the Host Adapter. In
+ Strict Round Robin Mode, the Host Adapter only looks at the next
+ Outgoing Mailbox for each new command, rather than scanning
+ through all the Outgoing Mailboxes to find any that have new
+ commands in them. Strict Round Robin Mode is significantly more
+ efficient.
*/
- if (HostAdapter->StrictRoundRobinModeSupport) {
- RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
- if (BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode, &RoundRobinModeRequest, sizeof(RoundRobinModeRequest), NULL, 0) < 0)
- return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE");
+ if (adapter->strict_rr) {
+ rr_req = BLOGIC_STRICT_RR_MODE;
+ if (blogic_cmd(adapter, BLOGIC_STRICT_RR, &rr_req,
+ sizeof(rr_req), NULL, 0) < 0)
+ return blogic_failure(adapter,
+ "ENABLE STRICT ROUND ROBIN MODE");
}
+
/*
- For Host Adapters that support Extended LUN Format CCBs, issue the Set CCB
- Format command to allow 32 Logical Units per Target Device.
+ For Host Adapters that support Extended LUN Format CCBs, issue the
+ Set CCB Format command to allow 32 Logical Units per Target Device.
*/
- if (HostAdapter->ExtendedLUNSupport) {
- SetCCBFormatRequest = BusLogic_ExtendedLUNFormatCCB;
- if (BusLogic_Command(HostAdapter, BusLogic_SetCCBFormat, &SetCCBFormatRequest, sizeof(SetCCBFormatRequest), NULL, 0) < 0)
- return BusLogic_Failure(HostAdapter, "SET CCB FORMAT");
+ if (adapter->ext_lun) {
+ setccb_fmt = BLOGIC_EXT_LUN_CCB;
+ if (blogic_cmd(adapter, BLOGIC_SETCCB_FMT, &setccb_fmt,
+ sizeof(setccb_fmt), NULL, 0) < 0)
+ return blogic_failure(adapter, "SET CCB FORMAT");
}
+
/*
Announce Successful Initialization.
*/
- Done:
- if (!HostAdapter->HostAdapterInitialized) {
- BusLogic_Info("*** %s Initialized Successfully ***\n", HostAdapter, HostAdapter->FullModelName);
- BusLogic_Info("\n", HostAdapter);
+done:
+ if (!adapter->adapter_initd) {
+ blogic_info("*** %s Initialized Successfully ***\n", adapter,
+ adapter->full_model);
+ blogic_info("\n", adapter);
} else
- BusLogic_Warning("*** %s Initialized Successfully ***\n", HostAdapter, HostAdapter->FullModelName);
- HostAdapter->HostAdapterInitialized = true;
+ blogic_warn("*** %s Initialized Successfully ***\n", adapter,
+ adapter->full_model);
+ adapter->adapter_initd = true;
+
/*
Indicate the Host Adapter Initialization completed successfully.
*/
@@ -2004,109 +2014,116 @@ static bool BusLogic_InitializeHostAdapter(struct BusLogic_HostAdapter
/*
- BusLogic_TargetDeviceInquiry inquires about the Target Devices accessible
+ blogic_inquiry inquires about the Target Devices accessible
through Host Adapter.
*/
-static bool __init BusLogic_TargetDeviceInquiry(struct BusLogic_HostAdapter
- *HostAdapter)
+static bool __init blogic_inquiry(struct blogic_adapter *adapter)
{
- u16 InstalledDevices;
- u8 InstalledDevicesID0to7[8];
- struct BusLogic_SetupInformation SetupInformation;
- u8 SynchronousPeriod[BusLogic_MaxTargetDevices];
- unsigned char RequestedReplyLength;
- int TargetID;
+ u16 installed_devs;
+ u8 installed_devs0to7[8];
+ struct blogic_setup_info setupinfo;
+ u8 sync_period[BLOGIC_MAXDEV];
+ unsigned char req_replylen;
+ int tgt_id;
+
/*
- Wait a few seconds between the Host Adapter Hard Reset which initiates
- a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get
- confused if they receive SCSI Commands too soon after a SCSI Bus Reset.
+ Wait a few seconds between the Host Adapter Hard Reset which
+ initiates a SCSI Bus Reset and issuing any SCSI Commands. Some
+ SCSI devices get confused if they receive SCSI Commands too soon
+ after a SCSI Bus Reset.
*/
- BusLogic_Delay(HostAdapter->BusSettleTime);
+ blogic_delay(adapter->bus_settle_time);
/*
FlashPoint Host Adapters do not provide for Target Device Inquiry.
*/
- if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ if (blogic_flashpoint_type(adapter))
return true;
/*
Inhibit the Target Device Inquiry if requested.
*/
- if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->LocalOptions.InhibitTargetInquiry)
+ if (adapter->drvr_opts != NULL && adapter->drvr_opts->stop_tgt_inquiry)
return true;
/*
- Issue the Inquire Target Devices command for host adapters with firmware
- version 4.25 or later, or the Inquire Installed Devices ID 0 to 7 command
- for older host adapters. This is necessary to force Synchronous Transfer
- Negotiation so that the Inquire Setup Information and Inquire Synchronous
- Period commands will return valid data. The Inquire Target Devices command
- is preferable to Inquire Installed Devices ID 0 to 7 since it only probes
- Logical Unit 0 of each Target Device.
+ Issue the Inquire Target Devices command for host adapters with
+ firmware version 4.25 or later, or the Inquire Installed Devices
+ ID 0 to 7 command for older host adapters. This is necessary to
+ force Synchronous Transfer Negotiation so that the Inquire Setup
+ Information and Inquire Synchronous Period commands will return
+ valid data. The Inquire Target Devices command is preferable to
+ Inquire Installed Devices ID 0 to 7 since it only probes Logical
+ Unit 0 of each Target Device.
*/
- if (strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0) {
+ if (strcmp(adapter->fw_ver, "4.25") >= 0) {
/*
- * Issue a Inquire Target Devices command. Inquire Target Devices only
- * tests Logical Unit 0 of each Target Device unlike the Inquire Installed
- * Devices commands which test Logical Units 0 - 7. Two bytes are
- * returned, where byte 0 bit 0 set indicates that Target Device 0 exists,
- * and so on.
+ Issue a Inquire Target Devices command. Inquire Target
+ Devices only tests Logical Unit 0 of each Target Device
+ unlike the Inquire Installed Devices commands which test
+ Logical Units 0 - 7. Two bytes are returned, where byte
+ 0 bit 0 set indicates that Target Device 0 exists, and so on.
*/
- if (BusLogic_Command(HostAdapter, BusLogic_InquireTargetDevices, NULL, 0, &InstalledDevices, sizeof(InstalledDevices))
- != sizeof(InstalledDevices))
- return BusLogic_Failure(HostAdapter, "INQUIRE TARGET DEVICES");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- HostAdapter->TargetFlags[TargetID].TargetExists = (InstalledDevices & (1 << TargetID) ? true : false);
+ if (blogic_cmd(adapter, BLOGIC_INQ_DEV, NULL, 0,
+ &installed_devs, sizeof(installed_devs))
+ != sizeof(installed_devs))
+ return blogic_failure(adapter, "INQUIRE TARGET DEVICES");
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ adapter->tgt_flags[tgt_id].tgt_exists =
+ (installed_devs & (1 << tgt_id) ? true : false);
} else {
/*
- * Issue an Inquire Installed Devices command. For each Target Device,
- * a byte is returned where bit 0 set indicates that Logical Unit 0
- * exists, bit 1 set indicates that Logical Unit 1 exists, and so on.
+ Issue an Inquire Installed Devices command. For each
+ Target Device, a byte is returned where bit 0 set
+ indicates that Logical Unit 0 * exists, bit 1 set
+ indicates that Logical Unit 1 exists, and so on.
*/
- if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7, NULL, 0, &InstalledDevicesID0to7, sizeof(InstalledDevicesID0to7))
- != sizeof(InstalledDevicesID0to7))
- return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7");
- for (TargetID = 0; TargetID < 8; TargetID++)
- HostAdapter->TargetFlags[TargetID].TargetExists = (InstalledDevicesID0to7[TargetID] != 0 ? true : false);
+ if (blogic_cmd(adapter, BLOGIC_INQ_DEV0TO7, NULL, 0,
+ &installed_devs0to7, sizeof(installed_devs0to7))
+ != sizeof(installed_devs0to7))
+ return blogic_failure(adapter,
+ "INQUIRE INSTALLED DEVICES ID 0 TO 7");
+ for (tgt_id = 0; tgt_id < 8; tgt_id++)
+ adapter->tgt_flags[tgt_id].tgt_exists =
+ installed_devs0to7[tgt_id] != 0;
}
/*
Issue the Inquire Setup Information command.
*/
- RequestedReplyLength = sizeof(SetupInformation);
- if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &SetupInformation, sizeof(SetupInformation))
- != sizeof(SetupInformation))
- return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- HostAdapter->SynchronousOffset[TargetID] = (TargetID < 8 ? SetupInformation.SynchronousValuesID0to7[TargetID].Offset : SetupInformation.SynchronousValuesID8to15[TargetID - 8].Offset);
- if (strcmp(HostAdapter->FirmwareVersion, "5.06L") >= 0)
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- HostAdapter->TargetFlags[TargetID].WideTransfersActive = (TargetID < 8 ? (SetupInformation.WideTransfersActiveID0to7 & (1 << TargetID)
- ? true : false)
- : (SetupInformation.WideTransfersActiveID8to15 & (1 << (TargetID - 8))
- ? true : false));
+ req_replylen = sizeof(setupinfo);
+ if (blogic_cmd(adapter, BLOGIC_INQ_SETUPINFO, &req_replylen,
+ sizeof(req_replylen), &setupinfo, sizeof(setupinfo))
+ != sizeof(setupinfo))
+ return blogic_failure(adapter, "INQUIRE SETUP INFORMATION");
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ adapter->sync_offset[tgt_id] = (tgt_id < 8 ? setupinfo.sync0to7[tgt_id].offset : setupinfo.sync8to15[tgt_id - 8].offset);
+ if (strcmp(adapter->fw_ver, "5.06L") >= 0)
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ adapter->tgt_flags[tgt_id].wide_active = (tgt_id < 8 ? (setupinfo.wide_tx_active0to7 & (1 << tgt_id) ? true : false) : (setupinfo.wide_tx_active8to15 & (1 << (tgt_id - 8)) ? true : false));
/*
Issue the Inquire Synchronous Period command.
*/
- if (HostAdapter->FirmwareVersion[0] >= '3') {
+ if (adapter->fw_ver[0] >= '3') {
- /* Issue a Inquire Synchronous Period command. For each Target Device,
- * a byte is returned which represents the Synchronous Transfer Period
- * in units of 10 nanoseconds.
+ /* Issue a Inquire Synchronous Period command. For each
+ Target Device, a byte is returned which represents the
+ Synchronous Transfer Period in units of 10 nanoseconds.
*/
- RequestedReplyLength = sizeof(SynchronousPeriod);
- if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod, &RequestedReplyLength, sizeof(RequestedReplyLength), &SynchronousPeriod, sizeof(SynchronousPeriod))
- != sizeof(SynchronousPeriod))
- return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- HostAdapter->SynchronousPeriod[TargetID] = SynchronousPeriod[TargetID];
+ req_replylen = sizeof(sync_period);
+ if (blogic_cmd(adapter, BLOGIC_INQ_SYNC_PERIOD, &req_replylen,
+ sizeof(req_replylen), &sync_period,
+ sizeof(sync_period)) != sizeof(sync_period))
+ return blogic_failure(adapter,
+ "INQUIRE SYNCHRONOUS PERIOD");
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ adapter->sync_period[tgt_id] = sync_period[tgt_id];
} else
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0)
- HostAdapter->SynchronousPeriod[TargetID] = 20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID]
- .TransferPeriod;
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ if (setupinfo.sync0to7[tgt_id].offset > 0)
+ adapter->sync_period[tgt_id] = 20 + 5 * setupinfo.sync0to7[tgt_id].tx_period;
/*
Indicate the Target Device Inquiry completed successfully.
*/
@@ -2114,7 +2131,7 @@ static bool __init BusLogic_TargetDeviceInquiry(struct BusLogic_HostAdapter
}
/*
- BusLogic_InitializeHostStructure initializes the fields in the SCSI Host
+ blogic_inithoststruct initializes the fields in the SCSI Host
structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the
SCSI Host structure are intentionally left uninitialized, as this driver
handles acquisition and release of these resources explicitly, as well as
@@ -2122,517 +2139,565 @@ static bool __init BusLogic_TargetDeviceInquiry(struct BusLogic_HostAdapter
through explicit acquisition and release of the Host Adapter's Lock.
*/
-static void __init BusLogic_InitializeHostStructure(struct BusLogic_HostAdapter
- *HostAdapter, struct Scsi_Host *Host)
+static void __init blogic_inithoststruct(struct blogic_adapter *adapter,
+ struct Scsi_Host *host)
{
- Host->max_id = HostAdapter->MaxTargetDevices;
- Host->max_lun = HostAdapter->MaxLogicalUnits;
- Host->max_channel = 0;
- Host->unique_id = HostAdapter->IO_Address;
- Host->this_id = HostAdapter->SCSI_ID;
- Host->can_queue = HostAdapter->DriverQueueDepth;
- Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
- Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired;
- Host->cmd_per_lun = HostAdapter->UntaggedQueueDepth;
+ host->max_id = adapter->maxdev;
+ host->max_lun = adapter->maxlun;
+ host->max_channel = 0;
+ host->unique_id = adapter->io_addr;
+ host->this_id = adapter->scsi_id;
+ host->can_queue = adapter->drvr_qdepth;
+ host->sg_tablesize = adapter->drvr_sglimit;
+ host->cmd_per_lun = adapter->untag_qdepth;
}
/*
- BusLogic_SlaveConfigure will actually set the queue depth on individual
+ blogic_sdev_configure will actually set the queue depth on individual
scsi devices as they are permanently added to the device chain. We
shamelessly rip off the SelectQueueDepths code to make this work mostly
like it used to. Since we don't get called once at the end of the scan
but instead get called for each device, we have to do things a bit
differently.
*/
-static int BusLogic_SlaveConfigure(struct scsi_device *Device)
+static int blogic_sdev_configure(struct scsi_device *dev,
+ struct queue_limits *lim)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Device->host->hostdata;
- int TargetID = Device->id;
- int QueueDepth = HostAdapter->QueueDepth[TargetID];
-
- if (HostAdapter->TargetFlags[TargetID].TaggedQueuingSupported && (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) {
- if (QueueDepth == 0)
- QueueDepth = BusLogic_MaxAutomaticTaggedQueueDepth;
- HostAdapter->QueueDepth[TargetID] = QueueDepth;
- scsi_adjust_queue_depth(Device, MSG_SIMPLE_TAG, QueueDepth);
+ struct blogic_adapter *adapter =
+ (struct blogic_adapter *) dev->host->hostdata;
+ int tgt_id = dev->id;
+ int qdepth = adapter->qdepth[tgt_id];
+
+ if (adapter->tgt_flags[tgt_id].tagq_ok &&
+ (adapter->tagq_ok & (1 << tgt_id))) {
+ if (qdepth == 0)
+ qdepth = BLOGIC_MAX_AUTO_TAG_DEPTH;
+ adapter->qdepth[tgt_id] = qdepth;
+ scsi_change_queue_depth(dev, qdepth);
} else {
- HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
- QueueDepth = HostAdapter->UntaggedQueueDepth;
- HostAdapter->QueueDepth[TargetID] = QueueDepth;
- scsi_adjust_queue_depth(Device, 0, QueueDepth);
- }
- QueueDepth = 0;
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
- if (HostAdapter->TargetFlags[TargetID].TargetExists) {
- QueueDepth += HostAdapter->QueueDepth[TargetID];
- }
- if (QueueDepth > HostAdapter->AllocatedCCBs)
- BusLogic_CreateAdditionalCCBs(HostAdapter, QueueDepth - HostAdapter->AllocatedCCBs, false);
+ adapter->tagq_ok &= ~(1 << tgt_id);
+ qdepth = adapter->untag_qdepth;
+ adapter->qdepth[tgt_id] = qdepth;
+ scsi_change_queue_depth(dev, qdepth);
+ }
+ qdepth = 0;
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
+ if (adapter->tgt_flags[tgt_id].tgt_exists)
+ qdepth += adapter->qdepth[tgt_id];
+ if (qdepth > adapter->alloc_ccbs)
+ blogic_create_addlccbs(adapter, qdepth - adapter->alloc_ccbs,
+ false);
return 0;
}
/*
- BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard
+ blogic_init probes for BusLogic Host Adapters at the standard
I/O Addresses where they may be located, initializing, registering, and
reporting the configuration of each BusLogic Host Adapter it finds. It
returns the number of BusLogic Host Adapters successfully initialized and
registered.
*/
-static int __init BusLogic_init(void)
+static int __init blogic_init(void)
{
- int BusLogicHostAdapterCount = 0, DriverOptionsIndex = 0, ProbeIndex;
- struct BusLogic_HostAdapter *PrototypeHostAdapter;
+ int drvr_optindex = 0, probeindex;
+ struct blogic_adapter *adapter;
int ret = 0;
#ifdef MODULE
if (BusLogic)
- BusLogic_Setup(BusLogic);
+ blogic_setup(BusLogic);
#endif
- if (BusLogic_ProbeOptions.NoProbe)
+ if (blogic_probe_options.noprobe)
return -ENODEV;
- BusLogic_ProbeInfoList =
- kzalloc(BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo), GFP_KERNEL);
- if (BusLogic_ProbeInfoList == NULL) {
- BusLogic_Error("BusLogic: Unable to allocate Probe Info List\n", NULL);
+ blogic_probeinfo_list =
+ kcalloc(BLOGIC_MAX_ADAPTERS, sizeof(struct blogic_probeinfo),
+ GFP_KERNEL);
+ if (blogic_probeinfo_list == NULL) {
+ blogic_err("BusLogic: Unable to allocate Probe Info List\n",
+ NULL);
return -ENOMEM;
}
- PrototypeHostAdapter =
- kzalloc(sizeof(struct BusLogic_HostAdapter), GFP_KERNEL);
- if (PrototypeHostAdapter == NULL) {
- kfree(BusLogic_ProbeInfoList);
- BusLogic_Error("BusLogic: Unable to allocate Prototype " "Host Adapter\n", NULL);
+ adapter = kzalloc(sizeof(struct blogic_adapter), GFP_KERNEL);
+ if (adapter == NULL) {
+ kfree(blogic_probeinfo_list);
+ blogic_err("BusLogic: Unable to allocate Prototype Host Adapter\n", NULL);
return -ENOMEM;
}
#ifdef MODULE
if (BusLogic != NULL)
- BusLogic_Setup(BusLogic);
+ blogic_setup(BusLogic);
#endif
- BusLogic_InitializeProbeInfoList(PrototypeHostAdapter);
- for (ProbeIndex = 0; ProbeIndex < BusLogic_ProbeInfoCount; ProbeIndex++) {
- struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[ProbeIndex];
- struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter;
- struct Scsi_Host *Host;
- if (ProbeInfo->IO_Address == 0)
+ blogic_init_probeinfo_list(adapter);
+ for (probeindex = 0; probeindex < blogic_probeinfo_count; probeindex++) {
+ struct blogic_probeinfo *probeinfo =
+ &blogic_probeinfo_list[probeindex];
+ struct blogic_adapter *myadapter = adapter;
+ struct Scsi_Host *host;
+
+ if (probeinfo->io_addr == 0)
continue;
- memset(HostAdapter, 0, sizeof(struct BusLogic_HostAdapter));
- HostAdapter->HostAdapterType = ProbeInfo->HostAdapterType;
- HostAdapter->HostAdapterBusType = ProbeInfo->HostAdapterBusType;
- HostAdapter->IO_Address = ProbeInfo->IO_Address;
- HostAdapter->PCI_Address = ProbeInfo->PCI_Address;
- HostAdapter->Bus = ProbeInfo->Bus;
- HostAdapter->Device = ProbeInfo->Device;
- HostAdapter->PCI_Device = ProbeInfo->PCI_Device;
- HostAdapter->IRQ_Channel = ProbeInfo->IRQ_Channel;
- HostAdapter->AddressCount = BusLogic_HostAdapterAddressCount[HostAdapter->HostAdapterType];
+ memset(myadapter, 0, sizeof(struct blogic_adapter));
+ myadapter->adapter_type = probeinfo->adapter_type;
+ myadapter->adapter_bus_type = probeinfo->adapter_bus_type;
+ myadapter->io_addr = probeinfo->io_addr;
+ myadapter->pci_addr = probeinfo->pci_addr;
+ myadapter->bus = probeinfo->bus;
+ myadapter->dev = probeinfo->dev;
+ myadapter->pci_device = probeinfo->pci_device;
+ myadapter->irq_ch = probeinfo->irq_ch;
+ myadapter->addr_count =
+ blogic_adapter_addr_count[myadapter->adapter_type];
/*
Make sure region is free prior to probing.
*/
- if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount,
+ if (!request_region(myadapter->io_addr, myadapter->addr_count,
"BusLogic"))
continue;
/*
- Probe the Host Adapter. If unsuccessful, abort further initialization.
+ Probe the Host Adapter. If unsuccessful, abort further
+ initialization.
*/
- if (!BusLogic_ProbeHostAdapter(HostAdapter)) {
- release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ if (!blogic_probe(myadapter)) {
+ release_region(myadapter->io_addr,
+ myadapter->addr_count);
continue;
}
/*
Hard Reset the Host Adapter. If unsuccessful, abort further
initialization.
*/
- if (!BusLogic_HardwareResetHostAdapter(HostAdapter, true)) {
- release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ if (!blogic_hwreset(myadapter, true)) {
+ release_region(myadapter->io_addr,
+ myadapter->addr_count);
continue;
}
/*
- Check the Host Adapter. If unsuccessful, abort further initialization.
+ Check the Host Adapter. If unsuccessful, abort further
+ initialization.
*/
- if (!BusLogic_CheckHostAdapter(HostAdapter)) {
- release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ if (!blogic_checkadapter(myadapter)) {
+ release_region(myadapter->io_addr,
+ myadapter->addr_count);
continue;
}
/*
Initialize the Driver Options field if provided.
*/
- if (DriverOptionsIndex < BusLogic_DriverOptionsCount)
- HostAdapter->DriverOptions = &BusLogic_DriverOptions[DriverOptionsIndex++];
+ if (drvr_optindex < blogic_drvr_options_count)
+ myadapter->drvr_opts =
+ &blogic_drvr_options[drvr_optindex++];
/*
- Announce the Driver Version and Date, Author's Name, Copyright Notice,
- and Electronic Mail Address.
+ Announce the Driver Version and Date, Author's Name,
+ Copyright Notice, and Electronic Mail Address.
*/
- BusLogic_AnnounceDriver(HostAdapter);
+ blogic_announce_drvr(myadapter);
/*
Register the SCSI Host structure.
*/
- Host = scsi_host_alloc(&Bus_Logic_template, sizeof(struct BusLogic_HostAdapter));
- if (Host == NULL) {
- release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ host = scsi_host_alloc(&blogic_template,
+ sizeof(struct blogic_adapter));
+ if (host == NULL) {
+ release_region(myadapter->io_addr,
+ myadapter->addr_count);
continue;
}
- HostAdapter = (struct BusLogic_HostAdapter *) Host->hostdata;
- memcpy(HostAdapter, PrototypeHostAdapter, sizeof(struct BusLogic_HostAdapter));
- HostAdapter->SCSI_Host = Host;
- HostAdapter->HostNumber = Host->host_no;
+ myadapter = (struct blogic_adapter *) host->hostdata;
+ memcpy(myadapter, adapter, sizeof(struct blogic_adapter));
+ myadapter->scsi_host = host;
+ myadapter->host_no = host->host_no;
/*
- Add Host Adapter to the end of the list of registered BusLogic
- Host Adapters.
+ Add Host Adapter to the end of the list of registered
+ BusLogic Host Adapters.
*/
- list_add_tail(&HostAdapter->host_list, &BusLogic_host_list);
+ list_add_tail(&myadapter->host_list, &blogic_host_list);
/*
- Read the Host Adapter Configuration, Configure the Host Adapter,
- Acquire the System Resources necessary to use the Host Adapter, then
- Create the Initial CCBs, Initialize the Host Adapter, and finally
- perform Target Device Inquiry.
-
- From this point onward, any failure will be assumed to be due to a
- problem with the Host Adapter, rather than due to having mistakenly
- identified this port as belonging to a BusLogic Host Adapter. The
- I/O Address range will not be released, thereby preventing it from
- being incorrectly identified as any other type of Host Adapter.
+ Read the Host Adapter Configuration, Configure the Host
+ Adapter, Acquire the System Resources necessary to use
+ the Host Adapter, then Create the Initial CCBs, Initialize
+ the Host Adapter, and finally perform Target Device
+ Inquiry. From this point onward, any failure will be
+ assumed to be due to a problem with the Host Adapter,
+ rather than due to having mistakenly identified this port
+ as belonging to a BusLogic Host Adapter. The I/O Address
+ range will not be released, thereby preventing it from
+ being incorrectly identified as any other type of Host
+ Adapter.
*/
- if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
- BusLogic_ReportHostAdapterConfiguration(HostAdapter) &&
- BusLogic_AcquireResources(HostAdapter) &&
- BusLogic_CreateInitialCCBs(HostAdapter) &&
- BusLogic_InitializeHostAdapter(HostAdapter) &&
- BusLogic_TargetDeviceInquiry(HostAdapter)) {
+ if (blogic_rdconfig(myadapter) &&
+ blogic_reportconfig(myadapter) &&
+ blogic_getres(myadapter) &&
+ blogic_create_initccbs(myadapter) &&
+ blogic_initadapter(myadapter) &&
+ blogic_inquiry(myadapter)) {
/*
- Initialization has been completed successfully. Release and
- re-register usage of the I/O Address range so that the Model
- Name of the Host Adapter will appear, and initialize the SCSI
- Host structure.
+ Initialization has been completed successfully.
+ Release and re-register usage of the I/O Address
+ range so that the Model Name of the Host Adapter
+ will appear, and initialize the SCSI Host structure.
*/
- release_region(HostAdapter->IO_Address,
- HostAdapter->AddressCount);
- if (!request_region(HostAdapter->IO_Address,
- HostAdapter->AddressCount,
- HostAdapter->FullModelName)) {
+ release_region(myadapter->io_addr,
+ myadapter->addr_count);
+ if (!request_region(myadapter->io_addr,
+ myadapter->addr_count,
+ myadapter->full_model)) {
printk(KERN_WARNING
"BusLogic: Release and re-register of "
"port 0x%04lx failed \n",
- (unsigned long)HostAdapter->IO_Address);
- BusLogic_DestroyCCBs(HostAdapter);
- BusLogic_ReleaseResources(HostAdapter);
- list_del(&HostAdapter->host_list);
- scsi_host_put(Host);
+ (unsigned long)myadapter->io_addr);
+ blogic_destroy_ccbs(myadapter);
+ blogic_relres(myadapter);
+ list_del(&myadapter->host_list);
+ scsi_host_put(host);
ret = -ENOMEM;
} else {
- BusLogic_InitializeHostStructure(HostAdapter,
- Host);
- if (scsi_add_host(Host, HostAdapter->PCI_Device
- ? &HostAdapter->PCI_Device->dev
+ blogic_inithoststruct(myadapter,
+ host);
+ if (scsi_add_host(host, myadapter->pci_device
+ ? &myadapter->pci_device->dev
: NULL)) {
printk(KERN_WARNING
"BusLogic: scsi_add_host()"
"failed!\n");
- BusLogic_DestroyCCBs(HostAdapter);
- BusLogic_ReleaseResources(HostAdapter);
- list_del(&HostAdapter->host_list);
- scsi_host_put(Host);
+ blogic_destroy_ccbs(myadapter);
+ blogic_relres(myadapter);
+ list_del(&myadapter->host_list);
+ scsi_host_put(host);
ret = -ENODEV;
- } else {
- scsi_scan_host(Host);
- BusLogicHostAdapterCount++;
- }
+ } else
+ scsi_scan_host(host);
}
} else {
/*
- An error occurred during Host Adapter Configuration Querying, Host
- Adapter Configuration, Resource Acquisition, CCB Creation, Host
- Adapter Initialization, or Target Device Inquiry, so remove Host
- Adapter from the list of registered BusLogic Host Adapters, destroy
- the CCBs, Release the System Resources, and Unregister the SCSI
+ An error occurred during Host Adapter Configuration
+ Querying, Host Adapter Configuration, Resource
+ Acquisition, CCB Creation, Host Adapter
+ Initialization, or Target Device Inquiry, so
+ remove Host Adapter from the list of registered
+ BusLogic Host Adapters, destroy the CCBs, Release
+ the System Resources, and Unregister the SCSI
Host.
*/
- BusLogic_DestroyCCBs(HostAdapter);
- BusLogic_ReleaseResources(HostAdapter);
- list_del(&HostAdapter->host_list);
- scsi_host_put(Host);
+ blogic_destroy_ccbs(myadapter);
+ blogic_relres(myadapter);
+ list_del(&myadapter->host_list);
+ scsi_host_put(host);
ret = -ENODEV;
}
}
- kfree(PrototypeHostAdapter);
- kfree(BusLogic_ProbeInfoList);
- BusLogic_ProbeInfoList = NULL;
+ kfree(adapter);
+ kfree(blogic_probeinfo_list);
+ blogic_probeinfo_list = NULL;
return ret;
}
/*
- BusLogic_ReleaseHostAdapter releases all resources previously acquired to
+ blogic_deladapter releases all resources previously acquired to
support a specific Host Adapter, including the I/O Address range, and
unregisters the BusLogic Host Adapter.
*/
-static int __exit BusLogic_ReleaseHostAdapter(struct BusLogic_HostAdapter *HostAdapter)
+static int __exit blogic_deladapter(struct blogic_adapter *adapter)
{
- struct Scsi_Host *Host = HostAdapter->SCSI_Host;
+ struct Scsi_Host *host = adapter->scsi_host;
- scsi_remove_host(Host);
+ scsi_remove_host(host);
/*
FlashPoint Host Adapters must first be released by the FlashPoint
SCCB Manager.
*/
- if (BusLogic_FlashPointHostAdapterP(HostAdapter))
- FlashPoint_ReleaseHostAdapter(HostAdapter->CardHandle);
+ if (blogic_flashpoint_type(adapter))
+ FlashPoint_ReleaseHostAdapter(adapter->cardhandle);
/*
Destroy the CCBs and release any system resources acquired to
support Host Adapter.
*/
- BusLogic_DestroyCCBs(HostAdapter);
- BusLogic_ReleaseResources(HostAdapter);
+ blogic_destroy_ccbs(adapter);
+ blogic_relres(adapter);
/*
Release usage of the I/O Address range.
*/
- release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ release_region(adapter->io_addr, adapter->addr_count);
/*
- Remove Host Adapter from the list of registered BusLogic Host Adapters.
+ Remove Host Adapter from the list of registered BusLogic
+ Host Adapters.
*/
- list_del(&HostAdapter->host_list);
+ list_del(&adapter->host_list);
- scsi_host_put(Host);
+ scsi_host_put(host);
return 0;
}
/*
- BusLogic_QueueCompletedCCB queues CCB for completion processing.
+ blogic_qcompleted_ccb queues CCB for completion processing.
*/
-static void BusLogic_QueueCompletedCCB(struct BusLogic_CCB *CCB)
+static void blogic_qcompleted_ccb(struct blogic_ccb *ccb)
{
- struct BusLogic_HostAdapter *HostAdapter = CCB->HostAdapter;
- CCB->Status = BusLogic_CCB_Completed;
- CCB->Next = NULL;
- if (HostAdapter->FirstCompletedCCB == NULL) {
- HostAdapter->FirstCompletedCCB = CCB;
- HostAdapter->LastCompletedCCB = CCB;
+ struct blogic_adapter *adapter = ccb->adapter;
+
+ ccb->status = BLOGIC_CCB_COMPLETE;
+ ccb->next = NULL;
+ if (adapter->firstccb == NULL) {
+ adapter->firstccb = ccb;
+ adapter->lastccb = ccb;
} else {
- HostAdapter->LastCompletedCCB->Next = CCB;
- HostAdapter->LastCompletedCCB = CCB;
+ adapter->lastccb->next = ccb;
+ adapter->lastccb = ccb;
}
- HostAdapter->ActiveCommands[CCB->TargetID]--;
+ adapter->active_cmds[ccb->tgt_id]--;
}
/*
- BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from
+ blogic_resultcode computes a SCSI Subsystem Result Code from
the Host Adapter Status and Target Device Status.
*/
-static int BusLogic_ComputeResultCode(struct BusLogic_HostAdapter *HostAdapter, enum BusLogic_HostAdapterStatus HostAdapterStatus, enum BusLogic_TargetDeviceStatus TargetDeviceStatus)
+static int blogic_resultcode(struct blogic_adapter *adapter,
+ enum blogic_adapter_status adapter_status,
+ enum blogic_tgt_status tgt_status)
{
- int HostStatus;
- switch (HostAdapterStatus) {
- case BusLogic_CommandCompletedNormally:
- case BusLogic_LinkedCommandCompleted:
- case BusLogic_LinkedCommandCompletedWithFlag:
- HostStatus = DID_OK;
+ int hoststatus;
+
+ switch (adapter_status) {
+ case BLOGIC_CMD_CMPLT_NORMAL:
+ case BLOGIC_LINK_CMD_CMPLT:
+ case BLOGIC_LINK_CMD_CMPLT_FLAG:
+ hoststatus = DID_OK;
break;
- case BusLogic_SCSISelectionTimeout:
- HostStatus = DID_TIME_OUT;
+ case BLOGIC_SELECT_TIMEOUT:
+ hoststatus = DID_TIME_OUT;
break;
- case BusLogic_InvalidOutgoingMailboxActionCode:
- case BusLogic_InvalidCommandOperationCode:
- case BusLogic_InvalidCommandParameter:
- BusLogic_Warning("BusLogic Driver Protocol Error 0x%02X\n", HostAdapter, HostAdapterStatus);
- case BusLogic_DataUnderRun:
- case BusLogic_DataOverRun:
- case BusLogic_UnexpectedBusFree:
- case BusLogic_LinkedCCBhasInvalidLUN:
- case BusLogic_AutoRequestSenseFailed:
- case BusLogic_TaggedQueuingMessageRejected:
- case BusLogic_UnsupportedMessageReceived:
- case BusLogic_HostAdapterHardwareFailed:
- case BusLogic_TargetDeviceReconnectedImproperly:
- case BusLogic_AbortQueueGenerated:
- case BusLogic_HostAdapterSoftwareError:
- case BusLogic_HostAdapterHardwareTimeoutError:
- case BusLogic_SCSIParityErrorDetected:
- HostStatus = DID_ERROR;
+ case BLOGIC_INVALID_OUTBOX_CODE:
+ case BLOGIC_INVALID_CMD_CODE:
+ case BLOGIC_BAD_CMD_PARAM:
+ blogic_warn("BusLogic Driver Protocol Error 0x%02X\n",
+ adapter, adapter_status);
+ fallthrough;
+ case BLOGIC_DATA_UNDERRUN:
+ case BLOGIC_DATA_OVERRUN:
+ case BLOGIC_NOEXPECT_BUSFREE:
+ case BLOGIC_LINKCCB_BADLUN:
+ case BLOGIC_AUTOREQSENSE_FAIL:
+ case BLOGIC_TAGQUEUE_REJECT:
+ case BLOGIC_BAD_MSG_RCVD:
+ case BLOGIC_HW_FAIL:
+ case BLOGIC_BAD_RECONNECT:
+ case BLOGIC_ABRT_QUEUE:
+ case BLOGIC_ADAPTER_SW_ERROR:
+ case BLOGIC_HW_TIMEOUT:
+ case BLOGIC_PARITY_ERR:
+ hoststatus = DID_ERROR;
break;
- case BusLogic_InvalidBusPhaseRequested:
- case BusLogic_TargetFailedResponseToATN:
- case BusLogic_HostAdapterAssertedRST:
- case BusLogic_OtherDeviceAssertedRST:
- case BusLogic_HostAdapterAssertedBusDeviceReset:
- HostStatus = DID_RESET;
+ case BLOGIC_INVALID_BUSPHASE:
+ case BLOGIC_NORESPONSE_TO_ATN:
+ case BLOGIC_HW_RESET:
+ case BLOGIC_RST_FROM_OTHERDEV:
+ case BLOGIC_HW_BDR:
+ hoststatus = DID_RESET;
break;
default:
- BusLogic_Warning("Unknown Host Adapter Status 0x%02X\n", HostAdapter, HostAdapterStatus);
- HostStatus = DID_ERROR;
+ blogic_warn("Unknown Host Adapter Status 0x%02X\n", adapter,
+ adapter_status);
+ hoststatus = DID_ERROR;
break;
}
- return (HostStatus << 16) | TargetDeviceStatus;
+ return (hoststatus << 16) | tgt_status;
}
+/*
+ * turn the dma address from an inbox into a ccb pointer
+ * This is rather inefficient.
+ */
+static struct blogic_ccb *
+blogic_inbox_to_ccb(struct blogic_adapter *adapter, struct blogic_inbox *inbox)
+{
+ struct blogic_ccb *ccb;
+
+ for (ccb = adapter->all_ccbs; ccb; ccb = ccb->next_all)
+ if (inbox->ccb == ccb->dma_handle)
+ break;
+
+ return ccb;
+}
/*
- BusLogic_ScanIncomingMailboxes scans the Incoming Mailboxes saving any
+ blogic_scan_inbox scans the Incoming Mailboxes saving any
Incoming Mailbox entries for completion processing.
*/
-
-static void BusLogic_ScanIncomingMailboxes(struct BusLogic_HostAdapter *HostAdapter)
+static void blogic_scan_inbox(struct blogic_adapter *adapter)
{
/*
- Scan through the Incoming Mailboxes in Strict Round Robin fashion, saving
- any completed CCBs for further processing. It is essential that for each
- CCB and SCSI Command issued, command completion processing is performed
- exactly once. Therefore, only Incoming Mailboxes with completion code
- Command Completed Without Error, Command Completed With Error, or Command
- Aborted At Host Request are saved for completion processing. When an
- Incoming Mailbox has a completion code of Aborted Command Not Found, the
- CCB had already completed or been aborted before the current Abort request
- was processed, and so completion processing has already occurred and no
- further action should be taken.
- */
- struct BusLogic_IncomingMailbox *NextIncomingMailbox = HostAdapter->NextIncomingMailbox;
- enum BusLogic_CompletionCode CompletionCode;
- while ((CompletionCode = NextIncomingMailbox->CompletionCode) != BusLogic_IncomingMailboxFree) {
- /*
- We are only allowed to do this because we limit our architectures we
- run on to machines where bus_to_virt() actually works. There *needs*
- to be a dma_addr_to_virt() in the new PCI DMA mapping interface to
- replace bus_to_virt() or else this code is going to become very
- innefficient.
- */
- struct BusLogic_CCB *CCB = (struct BusLogic_CCB *) Bus_to_Virtual(NextIncomingMailbox->CCB);
- if (CompletionCode != BusLogic_AbortedCommandNotFound) {
- if (CCB->Status == BusLogic_CCB_Active || CCB->Status == BusLogic_CCB_Reset) {
+ Scan through the Incoming Mailboxes in Strict Round Robin
+ fashion, saving any completed CCBs for further processing. It
+ is essential that for each CCB and SCSI Command issued, command
+ completion processing is performed exactly once. Therefore,
+ only Incoming Mailboxes with completion code Command Completed
+ Without Error, Command Completed With Error, or Command Aborted
+ At Host Request are saved for completion processing. When an
+ Incoming Mailbox has a completion code of Aborted Command Not
+ Found, the CCB had already completed or been aborted before the
+ current Abort request was processed, and so completion processing
+ has already occurred and no further action should be taken.
+ */
+ struct blogic_inbox *next_inbox = adapter->next_inbox;
+ enum blogic_cmplt_code comp_code;
+
+ while ((comp_code = next_inbox->comp_code) != BLOGIC_INBOX_FREE) {
+ struct blogic_ccb *ccb = blogic_inbox_to_ccb(adapter, next_inbox);
+ if (!ccb) {
+ /*
+ * This should never happen, unless the CCB list is
+ * corrupted in memory.
+ */
+ blogic_warn("Could not find CCB for dma address %x\n", adapter, next_inbox->ccb);
+ } else if (comp_code != BLOGIC_CMD_NOTFOUND) {
+ if (ccb->status == BLOGIC_CCB_ACTIVE ||
+ ccb->status == BLOGIC_CCB_RESET) {
/*
- Save the Completion Code for this CCB and queue the CCB
- for completion processing.
+ Save the Completion Code for this CCB and
+ queue the CCB for completion processing.
*/
- CCB->CompletionCode = CompletionCode;
- BusLogic_QueueCompletedCCB(CCB);
+ ccb->comp_code = comp_code;
+ blogic_qcompleted_ccb(ccb);
} else {
/*
- If a CCB ever appears in an Incoming Mailbox and is not marked
- as status Active or Reset, then there is most likely a bug in
+ If a CCB ever appears in an Incoming Mailbox
+ and is not marked as status Active or Reset,
+ then there is most likely a bug in
the Host Adapter firmware.
*/
- BusLogic_Warning("Illegal CCB #%ld status %d in " "Incoming Mailbox\n", HostAdapter, CCB->SerialNumber, CCB->Status);
+ blogic_warn("Illegal CCB #%ld status %d in Incoming Mailbox\n", adapter, ccb->serial, ccb->status);
}
}
- NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
- if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
- NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ next_inbox->comp_code = BLOGIC_INBOX_FREE;
+ if (++next_inbox > adapter->last_inbox)
+ next_inbox = adapter->first_inbox;
}
- HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
+ adapter->next_inbox = next_inbox;
}
/*
- BusLogic_ProcessCompletedCCBs iterates over the completed CCBs for Host
+ blogic_process_ccbs iterates over the completed CCBs for Host
Adapter setting the SCSI Command Result Codes, deallocating the CCBs, and
calling the SCSI Subsystem Completion Routines. The Host Adapter's Lock
should already have been acquired by the caller.
*/
-static void BusLogic_ProcessCompletedCCBs(struct BusLogic_HostAdapter *HostAdapter)
+static void blogic_process_ccbs(struct blogic_adapter *adapter)
{
- if (HostAdapter->ProcessCompletedCCBsActive)
+ if (adapter->processing_ccbs)
return;
- HostAdapter->ProcessCompletedCCBsActive = true;
- while (HostAdapter->FirstCompletedCCB != NULL) {
- struct BusLogic_CCB *CCB = HostAdapter->FirstCompletedCCB;
- struct scsi_cmnd *Command = CCB->Command;
- HostAdapter->FirstCompletedCCB = CCB->Next;
- if (HostAdapter->FirstCompletedCCB == NULL)
- HostAdapter->LastCompletedCCB = NULL;
+ adapter->processing_ccbs = true;
+ while (adapter->firstccb != NULL) {
+ struct blogic_ccb *ccb = adapter->firstccb;
+ struct scsi_cmnd *command = ccb->command;
+ adapter->firstccb = ccb->next;
+ if (adapter->firstccb == NULL)
+ adapter->lastccb = NULL;
/*
Process the Completed CCB.
*/
- if (CCB->Opcode == BusLogic_BusDeviceReset) {
- int TargetID = CCB->TargetID;
- BusLogic_Warning("Bus Device Reset CCB #%ld to Target " "%d Completed\n", HostAdapter, CCB->SerialNumber, TargetID);
- BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].BusDeviceResetsCompleted);
- HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false;
- HostAdapter->CommandsSinceReset[TargetID] = 0;
- HostAdapter->LastResetCompleted[TargetID] = jiffies;
+ if (ccb->opcode == BLOGIC_BDR) {
+ int tgt_id = ccb->tgt_id;
+
+ blogic_warn("Bus Device Reset CCB #%ld to Target %d Completed\n", adapter, ccb->serial, tgt_id);
+ blogic_inc_count(&adapter->tgt_stats[tgt_id].bdr_done);
+ adapter->tgt_flags[tgt_id].tagq_active = false;
+ adapter->cmds_since_rst[tgt_id] = 0;
+ adapter->last_resetdone[tgt_id] = jiffies;
/*
Place CCB back on the Host Adapter's free list.
*/
- BusLogic_DeallocateCCB(CCB);
-#if 0 /* this needs to be redone different for new EH */
+ blogic_dealloc_ccb(ccb, 1);
+#if 0 /* this needs to be redone different for new EH */
/*
- Bus Device Reset CCBs have the Command field non-NULL only when a
- Bus Device Reset was requested for a Command that did not have a
- currently active CCB in the Host Adapter (i.e., a Synchronous
- Bus Device Reset), and hence would not have its Completion Routine
- called otherwise.
+ Bus Device Reset CCBs have the command field
+ non-NULL only when a Bus Device Reset was requested
+ for a command that did not have a currently active
+ CCB in the Host Adapter (i.e., a Synchronous Bus
+ Device Reset), and hence would not have its
+ Completion Routine called otherwise.
*/
- while (Command != NULL) {
- struct scsi_cmnd *NextCommand = Command->reset_chain;
- Command->reset_chain = NULL;
- Command->result = DID_RESET << 16;
- Command->scsi_done(Command);
- Command = NextCommand;
+ while (command != NULL) {
+ struct scsi_cmnd *nxt_cmd =
+ command->reset_chain;
+ command->reset_chain = NULL;
+ command->result = DID_RESET << 16;
+ scsi_done(command);
+ command = nxt_cmd;
}
#endif
/*
- Iterate over the CCBs for this Host Adapter performing completion
- processing for any CCBs marked as Reset for this Target.
+ Iterate over the CCBs for this Host Adapter
+ performing completion processing for any CCBs
+ marked as Reset for this Target.
*/
- for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
- if (CCB->Status == BusLogic_CCB_Reset && CCB->TargetID == TargetID) {
- Command = CCB->Command;
- BusLogic_DeallocateCCB(CCB);
- HostAdapter->ActiveCommands[TargetID]--;
- Command->result = DID_RESET << 16;
- Command->scsi_done(Command);
+ for (ccb = adapter->all_ccbs; ccb != NULL;
+ ccb = ccb->next_all)
+ if (ccb->status == BLOGIC_CCB_RESET &&
+ ccb->tgt_id == tgt_id) {
+ command = ccb->command;
+ blogic_dealloc_ccb(ccb, 1);
+ adapter->active_cmds[tgt_id]--;
+ command->result = DID_RESET << 16;
+ scsi_done(command);
}
- HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL;
+ adapter->bdr_pend[tgt_id] = NULL;
} else {
/*
- Translate the Completion Code, Host Adapter Status, and Target
- Device Status into a SCSI Subsystem Result Code.
+ Translate the Completion Code, Host Adapter Status,
+ and Target Device Status into a SCSI Subsystem
+ Result Code.
*/
- switch (CCB->CompletionCode) {
- case BusLogic_IncomingMailboxFree:
- case BusLogic_AbortedCommandNotFound:
- case BusLogic_InvalidCCB:
- BusLogic_Warning("CCB #%ld to Target %d Impossible State\n", HostAdapter, CCB->SerialNumber, CCB->TargetID);
+ switch (ccb->comp_code) {
+ case BLOGIC_INBOX_FREE:
+ case BLOGIC_CMD_NOTFOUND:
+ case BLOGIC_INVALID_CCB:
+ blogic_warn("CCB #%ld to Target %d Impossible State\n", adapter, ccb->serial, ccb->tgt_id);
break;
- case BusLogic_CommandCompletedWithoutError:
- HostAdapter->TargetStatistics[CCB->TargetID]
- .CommandsCompleted++;
- HostAdapter->TargetFlags[CCB->TargetID]
- .CommandSuccessfulFlag = true;
- Command->result = DID_OK << 16;
+ case BLOGIC_CMD_COMPLETE_GOOD:
+ adapter->tgt_stats[ccb->tgt_id]
+ .cmds_complete++;
+ adapter->tgt_flags[ccb->tgt_id]
+ .cmd_good = true;
+ command->result = DID_OK << 16;
break;
- case BusLogic_CommandAbortedAtHostRequest:
- BusLogic_Warning("CCB #%ld to Target %d Aborted\n", HostAdapter, CCB->SerialNumber, CCB->TargetID);
- BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[CCB->TargetID]
- .CommandAbortsCompleted);
- Command->result = DID_ABORT << 16;
+ case BLOGIC_CMD_ABORT_BY_HOST:
+ blogic_warn("CCB #%ld to Target %d Aborted\n",
+ adapter, ccb->serial, ccb->tgt_id);
+ blogic_inc_count(&adapter->tgt_stats[ccb->tgt_id].aborts_done);
+ command->result = DID_ABORT << 16;
break;
- case BusLogic_CommandCompletedWithError:
- Command->result = BusLogic_ComputeResultCode(HostAdapter, CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
- if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout) {
- HostAdapter->TargetStatistics[CCB->TargetID]
- .CommandsCompleted++;
- if (BusLogic_GlobalOptions.TraceErrors) {
+ case BLOGIC_CMD_COMPLETE_ERROR:
+ command->result = blogic_resultcode(adapter,
+ ccb->adapter_status, ccb->tgt_status);
+ if (ccb->adapter_status != BLOGIC_SELECT_TIMEOUT) {
+ adapter->tgt_stats[ccb->tgt_id]
+ .cmds_complete++;
+ if (blogic_global_options.trace_err) {
int i;
- BusLogic_Notice("CCB #%ld Target %d: Result %X Host "
- "Adapter Status %02X " "Target Status %02X\n", HostAdapter, CCB->SerialNumber, CCB->TargetID, Command->result, CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
- BusLogic_Notice("CDB ", HostAdapter);
- for (i = 0; i < CCB->CDB_Length; i++)
- BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]);
- BusLogic_Notice("\n", HostAdapter);
- BusLogic_Notice("Sense ", HostAdapter);
- for (i = 0; i < CCB->SenseDataLength; i++)
- BusLogic_Notice(" %02X", HostAdapter, Command->sense_buffer[i]);
- BusLogic_Notice("\n", HostAdapter);
+ blogic_notice("CCB #%ld Target %d: Result %X Host "
+ "Adapter Status %02X Target Status %02X\n", adapter, ccb->serial, ccb->tgt_id, command->result, ccb->adapter_status, ccb->tgt_status);
+ blogic_notice("CDB ", adapter);
+ for (i = 0; i < ccb->cdblen; i++)
+ blogic_notice(" %02X", adapter, ccb->cdb[i]);
+ blogic_notice("\n", adapter);
+ blogic_notice("Sense ", adapter);
+ for (i = 0; i < ccb->sense_datalen; i++)
+ blogic_notice(" %02X", adapter, command->sense_buffer[i]);
+ blogic_notice("\n", adapter);
}
}
break;
@@ -2642,141 +2707,145 @@ static void BusLogic_ProcessCompletedCCBs(struct BusLogic_HostAdapter *HostAdapt
CmdQue (Tagged Queuing Supported) and WBus16 (16 Bit
Wide Data Transfers Supported) bits.
*/
- if (CCB->CDB[0] == INQUIRY && CCB->CDB[1] == 0 && CCB->HostAdapterStatus == BusLogic_CommandCompletedNormally) {
- struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[CCB->TargetID];
- struct SCSI_Inquiry *InquiryResult =
- (struct SCSI_Inquiry *) scsi_sglist(Command);
- TargetFlags->TargetExists = true;
- TargetFlags->TaggedQueuingSupported = InquiryResult->CmdQue;
- TargetFlags->WideTransfersSupported = InquiryResult->WBus16;
+ if (ccb->cdb[0] == INQUIRY && ccb->cdb[1] == 0 &&
+ ccb->adapter_status == BLOGIC_CMD_CMPLT_NORMAL) {
+ struct blogic_tgt_flags *tgt_flags =
+ &adapter->tgt_flags[ccb->tgt_id];
+ struct scsi_inquiry *inquiry =
+ (struct scsi_inquiry *) scsi_sglist(command);
+ tgt_flags->tgt_exists = true;
+ tgt_flags->tagq_ok = inquiry->CmdQue;
+ tgt_flags->wide_ok = inquiry->WBus16;
}
/*
Place CCB back on the Host Adapter's free list.
*/
- BusLogic_DeallocateCCB(CCB);
+ blogic_dealloc_ccb(ccb, 1);
/*
Call the SCSI Command Completion Routine.
*/
- Command->scsi_done(Command);
+ scsi_done(command);
}
}
- HostAdapter->ProcessCompletedCCBsActive = false;
+ adapter->processing_ccbs = false;
}
/*
- BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
+ blogic_inthandler handles hardware interrupts from BusLogic Host
Adapters.
*/
-static irqreturn_t BusLogic_InterruptHandler(int IRQ_Channel, void *DeviceIdentifier)
+static irqreturn_t blogic_inthandler(int irq_ch, void *devid)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) DeviceIdentifier;
- unsigned long ProcessorFlags;
+ struct blogic_adapter *adapter = (struct blogic_adapter *) devid;
+ unsigned long processor_flag;
/*
Acquire exclusive access to Host Adapter.
*/
- spin_lock_irqsave(HostAdapter->SCSI_Host->host_lock, ProcessorFlags);
+ spin_lock_irqsave(adapter->scsi_host->host_lock, processor_flag);
/*
Handle Interrupts appropriately for each Host Adapter type.
*/
- if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
- union BusLogic_InterruptRegister InterruptRegister;
+ if (blogic_multimaster_type(adapter)) {
+ union blogic_int_reg intreg;
/*
Read the Host Adapter Interrupt Register.
*/
- InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
- if (InterruptRegister.ir.InterruptValid) {
+ intreg.all = blogic_rdint(adapter);
+ if (intreg.ir.int_valid) {
/*
Acknowledge the interrupt and reset the Host Adapter
Interrupt Register.
*/
- BusLogic_InterruptReset(HostAdapter);
+ blogic_intreset(adapter);
/*
- Process valid External SCSI Bus Reset and Incoming Mailbox
- Loaded Interrupts. Command Complete Interrupts are noted,
- and Outgoing Mailbox Available Interrupts are ignored, as
- they are never enabled.
+ Process valid External SCSI Bus Reset and Incoming
+ Mailbox Loaded Interrupts. Command Complete
+ Interrupts are noted, and Outgoing Mailbox Available
+ Interrupts are ignored, as they are never enabled.
*/
- if (InterruptRegister.ir.ExternalBusReset)
- HostAdapter->HostAdapterExternalReset = true;
- else if (InterruptRegister.ir.IncomingMailboxLoaded)
- BusLogic_ScanIncomingMailboxes(HostAdapter);
- else if (InterruptRegister.ir.CommandComplete)
- HostAdapter->HostAdapterCommandCompleted = true;
+ if (intreg.ir.ext_busreset)
+ adapter->adapter_extreset = true;
+ else if (intreg.ir.mailin_loaded)
+ blogic_scan_inbox(adapter);
+ else if (intreg.ir.cmd_complete)
+ adapter->adapter_cmd_complete = true;
}
} else {
/*
Check if there is a pending interrupt for this Host Adapter.
*/
- if (FlashPoint_InterruptPending(HostAdapter->CardHandle))
- switch (FlashPoint_HandleInterrupt(HostAdapter->CardHandle)) {
- case FlashPoint_NormalInterrupt:
+ if (FlashPoint_InterruptPending(adapter->cardhandle))
+ switch (FlashPoint_HandleInterrupt(adapter->cardhandle)) {
+ case FPOINT_NORMAL_INT:
break;
- case FlashPoint_ExternalBusReset:
- HostAdapter->HostAdapterExternalReset = true;
+ case FPOINT_EXT_RESET:
+ adapter->adapter_extreset = true;
break;
- case FlashPoint_InternalError:
- BusLogic_Warning("Internal FlashPoint Error detected" " - Resetting Host Adapter\n", HostAdapter);
- HostAdapter->HostAdapterInternalError = true;
+ case FPOINT_INTERN_ERR:
+ blogic_warn("Internal FlashPoint Error detected - Resetting Host Adapter\n", adapter);
+ adapter->adapter_intern_err = true;
break;
}
}
/*
Process any completed CCBs.
*/
- if (HostAdapter->FirstCompletedCCB != NULL)
- BusLogic_ProcessCompletedCCBs(HostAdapter);
+ if (adapter->firstccb != NULL)
+ blogic_process_ccbs(adapter);
/*
Reset the Host Adapter if requested.
*/
- if (HostAdapter->HostAdapterExternalReset) {
- BusLogic_Warning("Resetting %s due to External SCSI Bus Reset\n", HostAdapter, HostAdapter->FullModelName);
- BusLogic_IncrementErrorCounter(&HostAdapter->ExternalHostAdapterResets);
- BusLogic_ResetHostAdapter(HostAdapter, false);
- HostAdapter->HostAdapterExternalReset = false;
- } else if (HostAdapter->HostAdapterInternalError) {
- BusLogic_Warning("Resetting %s due to Host Adapter Internal Error\n", HostAdapter, HostAdapter->FullModelName);
- BusLogic_IncrementErrorCounter(&HostAdapter->HostAdapterInternalErrors);
- BusLogic_ResetHostAdapter(HostAdapter, true);
- HostAdapter->HostAdapterInternalError = false;
+ if (adapter->adapter_extreset) {
+ blogic_warn("Resetting %s due to External SCSI Bus Reset\n", adapter, adapter->full_model);
+ blogic_inc_count(&adapter->ext_resets);
+ blogic_resetadapter(adapter, false);
+ adapter->adapter_extreset = false;
+ } else if (adapter->adapter_intern_err) {
+ blogic_warn("Resetting %s due to Host Adapter Internal Error\n", adapter, adapter->full_model);
+ blogic_inc_count(&adapter->adapter_intern_errors);
+ blogic_resetadapter(adapter, true);
+ adapter->adapter_intern_err = false;
}
/*
Release exclusive access to Host Adapter.
*/
- spin_unlock_irqrestore(HostAdapter->SCSI_Host->host_lock, ProcessorFlags);
+ spin_unlock_irqrestore(adapter->scsi_host->host_lock, processor_flag);
return IRQ_HANDLED;
}
/*
- BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing
+ blogic_write_outbox places CCB and Action Code into an Outgoing
Mailbox for execution by Host Adapter. The Host Adapter's Lock should
already have been acquired by the caller.
*/
-static bool BusLogic_WriteOutgoingMailbox(struct BusLogic_HostAdapter
- *HostAdapter, enum BusLogic_ActionCode ActionCode, struct BusLogic_CCB *CCB)
+static bool blogic_write_outbox(struct blogic_adapter *adapter,
+ enum blogic_action action, struct blogic_ccb *ccb)
{
- struct BusLogic_OutgoingMailbox *NextOutgoingMailbox;
- NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
- if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree) {
- CCB->Status = BusLogic_CCB_Active;
+ struct blogic_outbox *next_outbox;
+
+ next_outbox = adapter->next_outbox;
+ if (next_outbox->action == BLOGIC_OUTBOX_FREE) {
+ ccb->status = BLOGIC_CCB_ACTIVE;
/*
- The CCB field must be written before the Action Code field since
- the Host Adapter is operating asynchronously and the locking code
- does not protect against simultaneous access by the Host Adapter.
+ The CCB field must be written before the Action Code field
+ since the Host Adapter is operating asynchronously and the
+ locking code does not protect against simultaneous access
+ by the Host Adapter.
*/
- NextOutgoingMailbox->CCB = CCB->DMA_Handle;
- NextOutgoingMailbox->ActionCode = ActionCode;
- BusLogic_StartMailboxCommand(HostAdapter);
- if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
- NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
- HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
- if (ActionCode == BusLogic_MailboxStartCommand) {
- HostAdapter->ActiveCommands[CCB->TargetID]++;
- if (CCB->Opcode != BusLogic_BusDeviceReset)
- HostAdapter->TargetStatistics[CCB->TargetID].CommandsAttempted++;
+ next_outbox->ccb = ccb->dma_handle;
+ next_outbox->action = action;
+ blogic_execmbox(adapter);
+ if (++next_outbox > adapter->last_outbox)
+ next_outbox = adapter->first_outbox;
+ adapter->next_outbox = next_outbox;
+ if (action == BLOGIC_MBOX_START) {
+ adapter->active_cmds[ccb->tgt_id]++;
+ if (ccb->opcode != BLOGIC_BDR)
+ adapter->tgt_stats[ccb->tgt_id].cmds_tried++;
}
return true;
}
@@ -2785,65 +2854,73 @@ static bool BusLogic_WriteOutgoingMailbox(struct BusLogic_HostAdapter
/* Error Handling (EH) support */
-static int BusLogic_host_reset(struct scsi_cmnd * SCpnt)
+static int blogic_hostreset(struct scsi_cmnd *SCpnt)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) SCpnt->device->host->hostdata;
+ struct blogic_adapter *adapter =
+ (struct blogic_adapter *) SCpnt->device->host->hostdata;
unsigned int id = SCpnt->device->id;
- struct BusLogic_TargetStatistics *stats = &HostAdapter->TargetStatistics[id];
+ struct blogic_tgt_stats *stats = &adapter->tgt_stats[id];
int rc;
spin_lock_irq(SCpnt->device->host->host_lock);
- BusLogic_IncrementErrorCounter(&stats->HostAdapterResetsRequested);
+ blogic_inc_count(&stats->adapter_reset_req);
- rc = BusLogic_ResetHostAdapter(HostAdapter, false);
+ rc = blogic_resetadapter(adapter, false);
spin_unlock_irq(SCpnt->device->host->host_lock);
return rc;
}
/*
- BusLogic_QueueCommand creates a CCB for Command and places it into an
+ blogic_qcmd creates a CCB for Command and places it into an
Outgoing Mailbox for execution by the associated Host Adapter.
*/
-static int BusLogic_QueueCommand_lck(struct scsi_cmnd *Command, void (*CompletionRoutine) (struct scsi_cmnd *))
+static int blogic_qcmd_lck(struct scsi_cmnd *command)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Command->device->host->hostdata;
- struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[Command->device->id];
- struct BusLogic_TargetStatistics *TargetStatistics = HostAdapter->TargetStatistics;
- unsigned char *CDB = Command->cmnd;
- int CDB_Length = Command->cmd_len;
- int TargetID = Command->device->id;
- int LogicalUnit = Command->device->lun;
- int BufferLength = scsi_bufflen(Command);
- int Count;
- struct BusLogic_CCB *CCB;
- /*
- SCSI REQUEST_SENSE commands will be executed automatically by the Host
- Adapter for any errors, so they should not be executed explicitly unless
- the Sense Data is zero indicating that no error occurred.
- */
- if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0) {
- Command->result = DID_OK << 16;
- CompletionRoutine(Command);
+ void (*comp_cb)(struct scsi_cmnd *) = scsi_done;
+ struct blogic_adapter *adapter =
+ (struct blogic_adapter *) command->device->host->hostdata;
+ struct blogic_tgt_flags *tgt_flags =
+ &adapter->tgt_flags[command->device->id];
+ struct blogic_tgt_stats *tgt_stats = adapter->tgt_stats;
+ unsigned char *cdb = command->cmnd;
+ int cdblen = command->cmd_len;
+ int tgt_id = command->device->id;
+ int lun = command->device->lun;
+ int buflen = scsi_bufflen(command);
+ int count;
+ struct blogic_ccb *ccb;
+ dma_addr_t sense_buf;
+
+ /*
+ SCSI REQUEST_SENSE commands will be executed automatically by the
+ Host Adapter for any errors, so they should not be executed
+ explicitly unless the Sense Data is zero indicating that no error
+ occurred.
+ */
+ if (cdb[0] == REQUEST_SENSE && command->sense_buffer[0] != 0) {
+ command->result = DID_OK << 16;
+ comp_cb(command);
return 0;
}
/*
- Allocate a CCB from the Host Adapter's free list. In the unlikely event
- that there are none available and memory allocation fails, wait 1 second
- and try again. If that fails, the Host Adapter is probably hung so signal
- an error as a Host Adapter Hard Reset should be initiated soon.
- */
- CCB = BusLogic_AllocateCCB(HostAdapter);
- if (CCB == NULL) {
- spin_unlock_irq(HostAdapter->SCSI_Host->host_lock);
- BusLogic_Delay(1);
- spin_lock_irq(HostAdapter->SCSI_Host->host_lock);
- CCB = BusLogic_AllocateCCB(HostAdapter);
- if (CCB == NULL) {
- Command->result = DID_ERROR << 16;
- CompletionRoutine(Command);
+ Allocate a CCB from the Host Adapter's free list. In the unlikely
+ event that there are none available and memory allocation fails,
+ wait 1 second and try again. If that fails, the Host Adapter is
+ probably hung so signal an error as a Host Adapter Hard Reset
+ should be initiated soon.
+ */
+ ccb = blogic_alloc_ccb(adapter);
+ if (ccb == NULL) {
+ spin_unlock_irq(adapter->scsi_host->host_lock);
+ blogic_delay(1);
+ spin_lock_irq(adapter->scsi_host->host_lock);
+ ccb = blogic_alloc_ccb(adapter);
+ if (ccb == NULL) {
+ command->result = DID_ERROR << 16;
+ comp_cb(command);
return 0;
}
}
@@ -2851,217 +2928,247 @@ static int BusLogic_QueueCommand_lck(struct scsi_cmnd *Command, void (*Completio
/*
Initialize the fields in the BusLogic Command Control Block (CCB).
*/
- Count = scsi_dma_map(Command);
- BUG_ON(Count < 0);
- if (Count) {
+ count = scsi_dma_map(command);
+ BUG_ON(count < 0);
+ if (count) {
struct scatterlist *sg;
int i;
- CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
- CCB->DataLength = Count * sizeof(struct BusLogic_ScatterGatherSegment);
- if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
- CCB->DataPointer = (unsigned int) CCB->DMA_Handle + ((unsigned long) &CCB->ScatterGatherList - (unsigned long) CCB);
+ ccb->opcode = BLOGIC_INITIATOR_CCB_SG;
+ ccb->datalen = count * sizeof(struct blogic_sg_seg);
+ if (blogic_multimaster_type(adapter))
+ ccb->data = (unsigned int) ccb->dma_handle +
+ ((unsigned long) &ccb->sglist -
+ (unsigned long) ccb);
else
- CCB->DataPointer = Virtual_to_32Bit_Virtual(CCB->ScatterGatherList);
+ ccb->data = virt_to_32bit_virt(ccb->sglist);
- scsi_for_each_sg(Command, sg, Count, i) {
- CCB->ScatterGatherList[i].SegmentByteCount =
- sg_dma_len(sg);
- CCB->ScatterGatherList[i].SegmentDataPointer =
- sg_dma_address(sg);
+ scsi_for_each_sg(command, sg, count, i) {
+ ccb->sglist[i].segbytes = sg_dma_len(sg);
+ ccb->sglist[i].segdata = sg_dma_address(sg);
}
- } else if (!Count) {
- CCB->Opcode = BusLogic_InitiatorCCB;
- CCB->DataLength = BufferLength;
- CCB->DataPointer = 0;
+ } else if (!count) {
+ ccb->opcode = BLOGIC_INITIATOR_CCB;
+ ccb->datalen = buflen;
+ ccb->data = 0;
}
- switch (CDB[0]) {
+ switch (cdb[0]) {
case READ_6:
case READ_10:
- CCB->DataDirection = BusLogic_DataInLengthChecked;
- TargetStatistics[TargetID].ReadCommands++;
- BusLogic_IncrementByteCounter(&TargetStatistics[TargetID].TotalBytesRead, BufferLength);
- BusLogic_IncrementSizeBucket(TargetStatistics[TargetID].ReadCommandSizeBuckets, BufferLength);
+ ccb->datadir = BLOGIC_DATAIN_CHECKED;
+ tgt_stats[tgt_id].read_cmds++;
+ blogic_addcount(&tgt_stats[tgt_id].bytesread, buflen);
+ blogic_incszbucket(tgt_stats[tgt_id].read_sz_buckets, buflen);
break;
case WRITE_6:
case WRITE_10:
- CCB->DataDirection = BusLogic_DataOutLengthChecked;
- TargetStatistics[TargetID].WriteCommands++;
- BusLogic_IncrementByteCounter(&TargetStatistics[TargetID].TotalBytesWritten, BufferLength);
- BusLogic_IncrementSizeBucket(TargetStatistics[TargetID].WriteCommandSizeBuckets, BufferLength);
+ ccb->datadir = BLOGIC_DATAOUT_CHECKED;
+ tgt_stats[tgt_id].write_cmds++;
+ blogic_addcount(&tgt_stats[tgt_id].byteswritten, buflen);
+ blogic_incszbucket(tgt_stats[tgt_id].write_sz_buckets, buflen);
break;
default:
- CCB->DataDirection = BusLogic_UncheckedDataTransfer;
+ ccb->datadir = BLOGIC_UNCHECKED_TX;
break;
}
- CCB->CDB_Length = CDB_Length;
- CCB->HostAdapterStatus = 0;
- CCB->TargetDeviceStatus = 0;
- CCB->TargetID = TargetID;
- CCB->LogicalUnit = LogicalUnit;
- CCB->TagEnable = false;
- CCB->LegacyTagEnable = false;
- /*
- BusLogic recommends that after a Reset the first couple of commands that
- are sent to a Target Device be sent in a non Tagged Queue fashion so that
- the Host Adapter and Target Device can establish Synchronous and Wide
- Transfer before Queue Tag messages can interfere with the Synchronous and
- Wide Negotiation messages. By waiting to enable Tagged Queuing until after
- the first BusLogic_MaxTaggedQueueDepth commands have been queued, it is
- assured that after a Reset any pending commands are requeued before Tagged
- Queuing is enabled and that the Tagged Queuing message will not occur while
- the partition table is being printed. In addition, some devices do not
- properly handle the transition from non-tagged to tagged commands, so it is
- necessary to wait until there are no pending commands for a target device
+ ccb->cdblen = cdblen;
+ ccb->adapter_status = 0;
+ ccb->tgt_status = 0;
+ ccb->tgt_id = tgt_id;
+ ccb->lun = lun;
+ ccb->tag_enable = false;
+ ccb->legacytag_enable = false;
+ /*
+ BusLogic recommends that after a Reset the first couple of
+ commands that are sent to a Target Device be sent in a non
+ Tagged Queue fashion so that the Host Adapter and Target Device
+ can establish Synchronous and Wide Transfer before Queue Tag
+ messages can interfere with the Synchronous and Wide Negotiation
+ messages. By waiting to enable Tagged Queuing until after the
+ first BLOGIC_MAX_TAG_DEPTH commands have been queued, it is
+ assured that after a Reset any pending commands are requeued
+ before Tagged Queuing is enabled and that the Tagged Queuing
+ message will not occur while the partition table is being printed.
+ In addition, some devices do not properly handle the transition
+ from non-tagged to tagged commands, so it is necessary to wait
+ until there are no pending commands for a target device
before queuing tagged commands.
*/
- if (HostAdapter->CommandsSinceReset[TargetID]++ >=
- BusLogic_MaxTaggedQueueDepth && !TargetFlags->TaggedQueuingActive && HostAdapter->ActiveCommands[TargetID] == 0 && TargetFlags->TaggedQueuingSupported && (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) {
- TargetFlags->TaggedQueuingActive = true;
- BusLogic_Notice("Tagged Queuing now active for Target %d\n", HostAdapter, TargetID);
- }
- if (TargetFlags->TaggedQueuingActive) {
- enum BusLogic_QueueTag QueueTag = BusLogic_SimpleQueueTag;
+ if (adapter->cmds_since_rst[tgt_id]++ >= BLOGIC_MAX_TAG_DEPTH &&
+ !tgt_flags->tagq_active &&
+ adapter->active_cmds[tgt_id] == 0
+ && tgt_flags->tagq_ok &&
+ (adapter->tagq_ok & (1 << tgt_id))) {
+ tgt_flags->tagq_active = true;
+ blogic_notice("Tagged Queuing now active for Target %d\n",
+ adapter, tgt_id);
+ }
+ if (tgt_flags->tagq_active) {
+ enum blogic_queuetag queuetag = BLOGIC_SIMPLETAG;
/*
- When using Tagged Queuing with Simple Queue Tags, it appears that disk
- drive controllers do not guarantee that a queued command will not
- remain in a disconnected state indefinitely if commands that read or
- write nearer the head position continue to arrive without interruption.
- Therefore, for each Target Device this driver keeps track of the last
- time either the queue was empty or an Ordered Queue Tag was issued. If
- more than 4 seconds (one fifth of the 20 second disk timeout) have
- elapsed since this last sequence point, this command will be issued
- with an Ordered Queue Tag rather than a Simple Queue Tag, which forces
- the Target Device to complete all previously queued commands before
- this command may be executed.
+ When using Tagged Queuing with Simple Queue Tags, it
+ appears that disk drive controllers do not guarantee that
+ a queued command will not remain in a disconnected state
+ indefinitely if commands that read or write nearer the
+ head position continue to arrive without interruption.
+ Therefore, for each Target Device this driver keeps track
+ of the last time either the queue was empty or an Ordered
+ Queue Tag was issued. If more than 4 seconds (one fifth
+ of the 20 second disk timeout) have elapsed since this
+ last sequence point, this command will be issued with an
+ Ordered Queue Tag rather than a Simple Queue Tag, which
+ forces the Target Device to complete all previously
+ queued commands before this command may be executed.
*/
- if (HostAdapter->ActiveCommands[TargetID] == 0)
- HostAdapter->LastSequencePoint[TargetID] = jiffies;
- else if (time_after(jiffies, HostAdapter->LastSequencePoint[TargetID] + 4 * HZ)) {
- HostAdapter->LastSequencePoint[TargetID] = jiffies;
- QueueTag = BusLogic_OrderedQueueTag;
+ if (adapter->active_cmds[tgt_id] == 0)
+ adapter->last_seqpoint[tgt_id] = jiffies;
+ else if (time_after(jiffies,
+ adapter->last_seqpoint[tgt_id] + 4 * HZ)) {
+ adapter->last_seqpoint[tgt_id] = jiffies;
+ queuetag = BLOGIC_ORDEREDTAG;
}
- if (HostAdapter->ExtendedLUNSupport) {
- CCB->TagEnable = true;
- CCB->QueueTag = QueueTag;
+ if (adapter->ext_lun) {
+ ccb->tag_enable = true;
+ ccb->queuetag = queuetag;
} else {
- CCB->LegacyTagEnable = true;
- CCB->LegacyQueueTag = QueueTag;
+ ccb->legacytag_enable = true;
+ ccb->legacy_tag = queuetag;
}
}
- memcpy(CCB->CDB, CDB, CDB_Length);
- CCB->SenseDataLength = SCSI_SENSE_BUFFERSIZE;
- CCB->SenseDataPointer = pci_map_single(HostAdapter->PCI_Device, Command->sense_buffer, CCB->SenseDataLength, PCI_DMA_FROMDEVICE);
- CCB->Command = Command;
- Command->scsi_done = CompletionRoutine;
- if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
+ memcpy(ccb->cdb, cdb, cdblen);
+ ccb->sense_datalen = SCSI_SENSE_BUFFERSIZE;
+ ccb->command = command;
+ sense_buf = dma_map_single(&adapter->pci_device->dev,
+ command->sense_buffer, ccb->sense_datalen,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&adapter->pci_device->dev, sense_buf)) {
+ blogic_err("DMA mapping for sense data buffer failed\n",
+ adapter);
+ blogic_dealloc_ccb(ccb, 0);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ ccb->sensedata = sense_buf;
+ if (blogic_multimaster_type(adapter)) {
/*
- Place the CCB in an Outgoing Mailbox. The higher levels of the SCSI
- Subsystem should not attempt to queue more commands than can be placed
- in Outgoing Mailboxes, so there should always be one free. In the
- unlikely event that there are none available, wait 1 second and try
- again. If that fails, the Host Adapter is probably hung so signal an
- error as a Host Adapter Hard Reset should be initiated soon.
+ Place the CCB in an Outgoing Mailbox. The higher levels
+ of the SCSI Subsystem should not attempt to queue more
+ commands than can be placed in Outgoing Mailboxes, so
+ there should always be one free. In the unlikely event
+ that there are none available, wait 1 second and try
+ again. If that fails, the Host Adapter is probably hung
+ so signal an error as a Host Adapter Hard Reset should
+ be initiated soon.
*/
- if (!BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxStartCommand, CCB)) {
- spin_unlock_irq(HostAdapter->SCSI_Host->host_lock);
- BusLogic_Warning("Unable to write Outgoing Mailbox - " "Pausing for 1 second\n", HostAdapter);
- BusLogic_Delay(1);
- spin_lock_irq(HostAdapter->SCSI_Host->host_lock);
- if (!BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxStartCommand, CCB)) {
- BusLogic_Warning("Still unable to write Outgoing Mailbox - " "Host Adapter Dead?\n", HostAdapter);
- BusLogic_DeallocateCCB(CCB);
- Command->result = DID_ERROR << 16;
- Command->scsi_done(Command);
+ if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START, ccb)) {
+ spin_unlock_irq(adapter->scsi_host->host_lock);
+ blogic_warn("Unable to write Outgoing Mailbox - Pausing for 1 second\n", adapter);
+ blogic_delay(1);
+ spin_lock_irq(adapter->scsi_host->host_lock);
+ if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START,
+ ccb)) {
+ blogic_warn("Still unable to write Outgoing Mailbox - Host Adapter Dead?\n", adapter);
+ blogic_dealloc_ccb(ccb, 1);
+ command->result = DID_ERROR << 16;
+ scsi_done(command);
}
}
} else {
/*
- Call the FlashPoint SCCB Manager to start execution of the CCB.
+ Call the FlashPoint SCCB Manager to start execution of
+ the CCB.
*/
- CCB->Status = BusLogic_CCB_Active;
- HostAdapter->ActiveCommands[TargetID]++;
- TargetStatistics[TargetID].CommandsAttempted++;
- FlashPoint_StartCCB(HostAdapter->CardHandle, CCB);
+ ccb->status = BLOGIC_CCB_ACTIVE;
+ adapter->active_cmds[tgt_id]++;
+ tgt_stats[tgt_id].cmds_tried++;
+ FlashPoint_StartCCB(adapter->cardhandle, ccb);
/*
- The Command may have already completed and BusLogic_QueueCompletedCCB
- been called, or it may still be pending.
+ The Command may have already completed and
+ blogic_qcompleted_ccb been called, or it may still be
+ pending.
*/
- if (CCB->Status == BusLogic_CCB_Completed)
- BusLogic_ProcessCompletedCCBs(HostAdapter);
+ if (ccb->status == BLOGIC_CCB_COMPLETE)
+ blogic_process_ccbs(adapter);
}
return 0;
}
-static DEF_SCSI_QCMD(BusLogic_QueueCommand)
+static DEF_SCSI_QCMD(blogic_qcmd)
#if 0
/*
- BusLogic_AbortCommand aborts Command if possible.
+ blogic_abort aborts Command if possible.
*/
-static int BusLogic_AbortCommand(struct scsi_cmnd *Command)
+static int blogic_abort(struct scsi_cmnd *command)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Command->device->host->hostdata;
+ struct blogic_adapter *adapter =
+ (struct blogic_adapter *) command->device->host->hostdata;
+
+ int tgt_id = command->device->id;
+ struct blogic_ccb *ccb;
+ blogic_inc_count(&adapter->tgt_stats[tgt_id].aborts_request);
- int TargetID = Command->device->id;
- struct BusLogic_CCB *CCB;
- BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsRequested);
/*
- Attempt to find an Active CCB for this Command. If no Active CCB for this
- Command is found, then no Abort is necessary.
+ Attempt to find an Active CCB for this Command. If no Active
+ CCB for this Command is found, then no Abort is necessary.
*/
- for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
- if (CCB->Command == Command)
+ for (ccb = adapter->all_ccbs; ccb != NULL; ccb = ccb->next_all)
+ if (ccb->command == command)
break;
- if (CCB == NULL) {
- BusLogic_Warning("Unable to Abort Command to Target %d - " "No CCB Found\n", HostAdapter, TargetID);
+ if (ccb == NULL) {
+ blogic_warn("Unable to Abort Command to Target %d - No CCB Found\n", adapter, tgt_id);
return SUCCESS;
- } else if (CCB->Status == BusLogic_CCB_Completed) {
- BusLogic_Warning("Unable to Abort Command to Target %d - " "CCB Completed\n", HostAdapter, TargetID);
+ } else if (ccb->status == BLOGIC_CCB_COMPLETE) {
+ blogic_warn("Unable to Abort Command to Target %d - CCB Completed\n", adapter, tgt_id);
return SUCCESS;
- } else if (CCB->Status == BusLogic_CCB_Reset) {
- BusLogic_Warning("Unable to Abort Command to Target %d - " "CCB Reset\n", HostAdapter, TargetID);
+ } else if (ccb->status == BLOGIC_CCB_RESET) {
+ blogic_warn("Unable to Abort Command to Target %d - CCB Reset\n", adapter, tgt_id);
return SUCCESS;
}
- if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
+ if (blogic_multimaster_type(adapter)) {
/*
- Attempt to Abort this CCB. MultiMaster Firmware versions prior to 5.xx
- do not generate Abort Tag messages, but only generate the non-tagged
- Abort message. Since non-tagged commands are not sent by the Host
- Adapter until the queue of outstanding tagged commands has completed,
- and the Abort message is treated as a non-tagged command, it is
- effectively impossible to abort commands when Tagged Queuing is active.
- Firmware version 5.xx does generate Abort Tag messages, so it is
- possible to abort commands when Tagged Queuing is active.
+ Attempt to Abort this CCB. MultiMaster Firmware versions
+ prior to 5.xx do not generate Abort Tag messages, but only
+ generate the non-tagged Abort message. Since non-tagged
+ commands are not sent by the Host Adapter until the queue
+ of outstanding tagged commands has completed, and the
+ Abort message is treated as a non-tagged command, it is
+ effectively impossible to abort commands when Tagged
+ Queuing is active. Firmware version 5.xx does generate
+ Abort Tag messages, so it is possible to abort commands
+ when Tagged Queuing is active.
*/
- if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive && HostAdapter->FirmwareVersion[0] < '5') {
- BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - " "Abort Tag Not Supported\n", HostAdapter, CCB->SerialNumber, TargetID);
+ if (adapter->tgt_flags[tgt_id].tagq_active &&
+ adapter->fw_ver[0] < '5') {
+ blogic_warn("Unable to Abort CCB #%ld to Target %d - Abort Tag Not Supported\n", adapter, ccb->serial, tgt_id);
return FAILURE;
- } else if (BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxAbortCommand, CCB)) {
- BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID);
- BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted);
+ } else if (blogic_write_outbox(adapter, BLOGIC_MBOX_ABORT,
+ ccb)) {
+ blogic_warn("Aborting CCB #%ld to Target %d\n",
+ adapter, ccb->serial, tgt_id);
+ blogic_inc_count(&adapter->tgt_stats[tgt_id].aborts_tried);
return SUCCESS;
} else {
- BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - " "No Outgoing Mailboxes\n", HostAdapter, CCB->SerialNumber, TargetID);
+ blogic_warn("Unable to Abort CCB #%ld to Target %d - No Outgoing Mailboxes\n", adapter, ccb->serial, tgt_id);
return FAILURE;
}
} else {
/*
- Call the FlashPoint SCCB Manager to abort execution of the CCB.
+ Call the FlashPoint SCCB Manager to abort execution of
+ the CCB.
*/
- BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID);
- BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted);
- FlashPoint_AbortCCB(HostAdapter->CardHandle, CCB);
+ blogic_warn("Aborting CCB #%ld to Target %d\n", adapter,
+ ccb->serial, tgt_id);
+ blogic_inc_count(&adapter->tgt_stats[tgt_id].aborts_tried);
+ FlashPoint_AbortCCB(adapter->cardhandle, ccb);
/*
The Abort may have already been completed and
- BusLogic_QueueCompletedCCB been called, or it
+ blogic_qcompleted_ccb been called, or it
may still be pending.
*/
- if (CCB->Status == BusLogic_CCB_Completed) {
- BusLogic_ProcessCompletedCCBs(HostAdapter);
- }
+ if (ccb->status == BLOGIC_CCB_COMPLETE)
+ blogic_process_ccbs(adapter);
return SUCCESS;
}
return SUCCESS;
@@ -3069,21 +3176,23 @@ static int BusLogic_AbortCommand(struct scsi_cmnd *Command)
#endif
/*
- BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
+ blogic_resetadapter resets Host Adapter if possible, marking all
currently executing SCSI Commands as having been Reset.
*/
-static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *HostAdapter, bool HardReset)
+static int blogic_resetadapter(struct blogic_adapter *adapter, bool hard_reset)
{
- struct BusLogic_CCB *CCB;
- int TargetID;
+ struct blogic_ccb *ccb;
+ int tgt_id;
/*
* Attempt to Reset and Reinitialize the Host Adapter.
*/
- if (!(BusLogic_HardwareResetHostAdapter(HostAdapter, HardReset) && BusLogic_InitializeHostAdapter(HostAdapter))) {
- BusLogic_Error("Resetting %s Failed\n", HostAdapter, HostAdapter->FullModelName);
+ if (!(blogic_hwreset(adapter, hard_reset) &&
+ blogic_initadapter(adapter))) {
+ blogic_err("Resetting %s Failed\n", adapter,
+ adapter->full_model);
return FAILURE;
}
@@ -3091,9 +3200,9 @@ static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *HostAdapter, b
* Deallocate all currently executing CCBs.
*/
- for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
- if (CCB->Status == BusLogic_CCB_Active)
- BusLogic_DeallocateCCB(CCB);
+ for (ccb = adapter->all_ccbs; ccb != NULL; ccb = ccb->next_all)
+ if (ccb->status == BLOGIC_CCB_ACTIVE)
+ blogic_dealloc_ccb(ccb, 1);
/*
* Wait a few seconds between the Host Adapter Hard Reset which
* initiates a SCSI Bus Reset and issuing any SCSI Commands. Some
@@ -3101,21 +3210,21 @@ static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *HostAdapter, b
* after a SCSI Bus Reset.
*/
- if (HardReset) {
- spin_unlock_irq(HostAdapter->SCSI_Host->host_lock);
- BusLogic_Delay(HostAdapter->BusSettleTime);
- spin_lock_irq(HostAdapter->SCSI_Host->host_lock);
+ if (hard_reset) {
+ spin_unlock_irq(adapter->scsi_host->host_lock);
+ blogic_delay(adapter->bus_settle_time);
+ spin_lock_irq(adapter->scsi_host->host_lock);
}
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
- HostAdapter->LastResetAttempted[TargetID] = jiffies;
- HostAdapter->LastResetCompleted[TargetID] = jiffies;
+ for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) {
+ adapter->last_resettried[tgt_id] = jiffies;
+ adapter->last_resetdone[tgt_id] = jiffies;
}
return SUCCESS;
}
/*
- BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk
+ blogic_diskparam returns the Heads/Sectors/Cylinders BIOS Disk
Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and
the appropriate number of cylinders so as not to exceed drive capacity. In
order for disks equal to or larger than 1 GB to be addressable by the BIOS
@@ -3131,66 +3240,71 @@ static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *HostAdapter, b
the BIOS, and a warning may be displayed.
*/
-static int BusLogic_BIOSDiskParameters(struct scsi_device *sdev, struct block_device *Device, sector_t capacity, int *Parameters)
+static int blogic_diskparam(struct scsi_device *sdev, struct gendisk *disk,
+ sector_t capacity, int *params)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) sdev->host->hostdata;
- struct BIOS_DiskParameters *DiskParameters = (struct BIOS_DiskParameters *) Parameters;
+ struct blogic_adapter *adapter =
+ (struct blogic_adapter *) sdev->host->hostdata;
+ struct bios_diskparam *diskparam = (struct bios_diskparam *) params;
unsigned char *buf;
- if (HostAdapter->ExtendedTranslationEnabled && capacity >= 2 * 1024 * 1024 /* 1 GB in 512 byte sectors */ ) {
- if (capacity >= 4 * 1024 * 1024 /* 2 GB in 512 byte sectors */ ) {
- DiskParameters->Heads = 255;
- DiskParameters->Sectors = 63;
+
+ if (adapter->ext_trans_enable && capacity >= 2 * 1024 * 1024 /* 1 GB in 512 byte sectors */) {
+ if (capacity >= 4 * 1024 * 1024 /* 2 GB in 512 byte sectors */) {
+ diskparam->heads = 255;
+ diskparam->sectors = 63;
} else {
- DiskParameters->Heads = 128;
- DiskParameters->Sectors = 32;
+ diskparam->heads = 128;
+ diskparam->sectors = 32;
}
} else {
- DiskParameters->Heads = 64;
- DiskParameters->Sectors = 32;
+ diskparam->heads = 64;
+ diskparam->sectors = 32;
}
- DiskParameters->Cylinders = (unsigned long) capacity / (DiskParameters->Heads * DiskParameters->Sectors);
- buf = scsi_bios_ptable(Device);
+ diskparam->cylinders = (unsigned long) capacity / (diskparam->heads * diskparam->sectors);
+ buf = scsi_bios_ptable(disk);
if (buf == NULL)
return 0;
/*
- If the boot sector partition table flag is valid, search for a partition
- table entry whose end_head matches one of the standard BusLogic geometry
- translations (64/32, 128/32, or 255/63).
- */
- if (*(unsigned short *) (buf + 64) == 0xAA55) {
- struct partition *FirstPartitionEntry = (struct partition *) buf;
- struct partition *PartitionEntry = FirstPartitionEntry;
- int SavedCylinders = DiskParameters->Cylinders, PartitionNumber;
- unsigned char PartitionEntryEndHead = 0, PartitionEntryEndSector = 0;
- for (PartitionNumber = 0; PartitionNumber < 4; PartitionNumber++) {
- PartitionEntryEndHead = PartitionEntry->end_head;
- PartitionEntryEndSector = PartitionEntry->end_sector & 0x3F;
- if (PartitionEntryEndHead == 64 - 1) {
- DiskParameters->Heads = 64;
- DiskParameters->Sectors = 32;
+ If the boot sector partition table flag is valid, search for
+ a partition table entry whose end_head matches one of the
+ standard BusLogic geometry translations (64/32, 128/32, or 255/63).
+ */
+ if (*(unsigned short *) (buf + 64) == MSDOS_LABEL_MAGIC) {
+ struct msdos_partition *part1_entry =
+ (struct msdos_partition *)buf;
+ struct msdos_partition *part_entry = part1_entry;
+ int saved_cyl = diskparam->cylinders, part_no;
+ unsigned char part_end_head = 0, part_end_sector = 0;
+
+ for (part_no = 0; part_no < 4; part_no++) {
+ part_end_head = part_entry->end_head;
+ part_end_sector = part_entry->end_sector & 0x3F;
+ if (part_end_head == 64 - 1) {
+ diskparam->heads = 64;
+ diskparam->sectors = 32;
break;
- } else if (PartitionEntryEndHead == 128 - 1) {
- DiskParameters->Heads = 128;
- DiskParameters->Sectors = 32;
+ } else if (part_end_head == 128 - 1) {
+ diskparam->heads = 128;
+ diskparam->sectors = 32;
break;
- } else if (PartitionEntryEndHead == 255 - 1) {
- DiskParameters->Heads = 255;
- DiskParameters->Sectors = 63;
+ } else if (part_end_head == 255 - 1) {
+ diskparam->heads = 255;
+ diskparam->sectors = 63;
break;
}
- PartitionEntry++;
+ part_entry++;
}
- if (PartitionNumber == 4) {
- PartitionEntryEndHead = FirstPartitionEntry->end_head;
- PartitionEntryEndSector = FirstPartitionEntry->end_sector & 0x3F;
+ if (part_no == 4) {
+ part_end_head = part1_entry->end_head;
+ part_end_sector = part1_entry->end_sector & 0x3F;
}
- DiskParameters->Cylinders = (unsigned long) capacity / (DiskParameters->Heads * DiskParameters->Sectors);
- if (PartitionNumber < 4 && PartitionEntryEndSector == DiskParameters->Sectors) {
- if (DiskParameters->Cylinders != SavedCylinders)
- BusLogic_Warning("Adopting Geometry %d/%d from Partition Table\n", HostAdapter, DiskParameters->Heads, DiskParameters->Sectors);
- } else if (PartitionEntryEndHead > 0 || PartitionEntryEndSector > 0) {
- BusLogic_Warning("Warning: Partition Table appears to " "have Geometry %d/%d which is\n", HostAdapter, PartitionEntryEndHead + 1, PartitionEntryEndSector);
- BusLogic_Warning("not compatible with current BusLogic " "Host Adapter Geometry %d/%d\n", HostAdapter, DiskParameters->Heads, DiskParameters->Sectors);
+ diskparam->cylinders = (unsigned long) capacity / (diskparam->heads * diskparam->sectors);
+ if (part_no < 4 && part_end_sector == diskparam->sectors) {
+ if (diskparam->cylinders != saved_cyl)
+ blogic_warn("Adopting Geometry %d/%d from Partition Table\n", adapter, diskparam->heads, diskparam->sectors);
+ } else if (part_end_head > 0 || part_end_sector > 0) {
+ blogic_warn("Warning: Partition Table appears to have Geometry %d/%d which is\n", adapter, part_end_head + 1, part_end_sector);
+ blogic_warn("not compatible with current BusLogic Host Adapter Geometry %d/%d\n", adapter, diskparam->heads, diskparam->sectors);
}
}
kfree(buf);
@@ -3202,186 +3316,190 @@ static int BusLogic_BIOSDiskParameters(struct scsi_device *sdev, struct block_de
BugLogic_ProcDirectoryInfo implements /proc/scsi/BusLogic/<N>.
*/
-static int BusLogic_ProcDirectoryInfo(struct Scsi_Host *shost, char *ProcBuffer, char **StartPointer, off_t Offset, int BytesAvailable, int WriteFlag)
+static int blogic_write_info(struct Scsi_Host *shost, char *procbuf,
+ int bytes_avail)
{
- struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) shost->hostdata;
- struct BusLogic_TargetStatistics *TargetStatistics;
- int TargetID, Length;
- char *Buffer;
-
- TargetStatistics = HostAdapter->TargetStatistics;
- if (WriteFlag) {
- HostAdapter->ExternalHostAdapterResets = 0;
- HostAdapter->HostAdapterInternalErrors = 0;
- memset(TargetStatistics, 0, BusLogic_MaxTargetDevices * sizeof(struct BusLogic_TargetStatistics));
- return 0;
- }
- Buffer = HostAdapter->MessageBuffer;
- Length = HostAdapter->MessageBufferLength;
- Length += sprintf(&Buffer[Length], "\n\
+ struct blogic_adapter *adapter =
+ (struct blogic_adapter *) shost->hostdata;
+ struct blogic_tgt_stats *tgt_stats;
+
+ tgt_stats = adapter->tgt_stats;
+ adapter->ext_resets = 0;
+ adapter->adapter_intern_errors = 0;
+ memset(tgt_stats, 0, BLOGIC_MAXDEV * sizeof(struct blogic_tgt_stats));
+ return 0;
+}
+
+static int blogic_show_info(struct seq_file *m, struct Scsi_Host *shost)
+{
+ struct blogic_adapter *adapter = (struct blogic_adapter *) shost->hostdata;
+ struct blogic_tgt_stats *tgt_stats;
+ int tgt;
+
+ tgt_stats = adapter->tgt_stats;
+ seq_write(m, adapter->msgbuf, adapter->msgbuflen);
+ seq_printf(m, "\n\
Current Driver Queue Depth: %d\n\
-Currently Allocated CCBs: %d\n", HostAdapter->DriverQueueDepth, HostAdapter->AllocatedCCBs);
- Length += sprintf(&Buffer[Length], "\n\n\
+Currently Allocated CCBs: %d\n", adapter->drvr_qdepth, adapter->alloc_ccbs);
+ seq_puts(m, "\n\n\
DATA TRANSFER STATISTICS\n\
\n\
Target Tagged Queuing Queue Depth Active Attempted Completed\n\
====== ============== =========== ====== ========= =========\n");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
- struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
- if (!TargetFlags->TargetExists)
+ for (tgt = 0; tgt < adapter->maxdev; tgt++) {
+ struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt];
+ if (!tgt_flags->tgt_exists)
continue;
- Length += sprintf(&Buffer[Length], " %2d %s", TargetID, (TargetFlags->TaggedQueuingSupported ? (TargetFlags->TaggedQueuingActive ? " Active" : (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)
+ seq_printf(m, " %2d %s", tgt, (tgt_flags->tagq_ok ? (tgt_flags->tagq_active ? " Active" : (adapter->tagq_ok & (1 << tgt)
? " Permitted" : " Disabled"))
: "Not Supported"));
- Length += sprintf(&Buffer[Length],
- " %3d %3u %9u %9u\n", HostAdapter->QueueDepth[TargetID], HostAdapter->ActiveCommands[TargetID], TargetStatistics[TargetID].CommandsAttempted, TargetStatistics[TargetID].CommandsCompleted);
+ seq_printf(m,
+ " %3d %3u %9u %9u\n", adapter->qdepth[tgt], adapter->active_cmds[tgt], tgt_stats[tgt].cmds_tried, tgt_stats[tgt].cmds_complete);
}
- Length += sprintf(&Buffer[Length], "\n\
+ seq_puts(m, "\n\
Target Read Commands Write Commands Total Bytes Read Total Bytes Written\n\
====== ============= ============== =================== ===================\n");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
- struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
- if (!TargetFlags->TargetExists)
+ for (tgt = 0; tgt < adapter->maxdev; tgt++) {
+ struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt];
+ if (!tgt_flags->tgt_exists)
continue;
- Length += sprintf(&Buffer[Length], " %2d %9u %9u", TargetID, TargetStatistics[TargetID].ReadCommands, TargetStatistics[TargetID].WriteCommands);
- if (TargetStatistics[TargetID].TotalBytesRead.Billions > 0)
- Length += sprintf(&Buffer[Length], " %9u%09u", TargetStatistics[TargetID].TotalBytesRead.Billions, TargetStatistics[TargetID].TotalBytesRead.Units);
+ seq_printf(m, " %2d %9u %9u", tgt, tgt_stats[tgt].read_cmds, tgt_stats[tgt].write_cmds);
+ if (tgt_stats[tgt].bytesread.billions > 0)
+ seq_printf(m, " %9u%09u", tgt_stats[tgt].bytesread.billions, tgt_stats[tgt].bytesread.units);
else
- Length += sprintf(&Buffer[Length], " %9u", TargetStatistics[TargetID].TotalBytesRead.Units);
- if (TargetStatistics[TargetID].TotalBytesWritten.Billions > 0)
- Length += sprintf(&Buffer[Length], " %9u%09u\n", TargetStatistics[TargetID].TotalBytesWritten.Billions, TargetStatistics[TargetID].TotalBytesWritten.Units);
+ seq_printf(m, " %9u", tgt_stats[tgt].bytesread.units);
+ if (tgt_stats[tgt].byteswritten.billions > 0)
+ seq_printf(m, " %9u%09u\n", tgt_stats[tgt].byteswritten.billions, tgt_stats[tgt].byteswritten.units);
else
- Length += sprintf(&Buffer[Length], " %9u\n", TargetStatistics[TargetID].TotalBytesWritten.Units);
+ seq_printf(m, " %9u\n", tgt_stats[tgt].byteswritten.units);
}
- Length += sprintf(&Buffer[Length], "\n\
+ seq_puts(m, "\n\
Target Command 0-1KB 1-2KB 2-4KB 4-8KB 8-16KB\n\
====== ======= ========= ========= ========= ========= =========\n");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
- struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
- if (!TargetFlags->TargetExists)
+ for (tgt = 0; tgt < adapter->maxdev; tgt++) {
+ struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt];
+ if (!tgt_flags->tgt_exists)
continue;
- Length +=
- sprintf(&Buffer[Length],
- " %2d Read %9u %9u %9u %9u %9u\n", TargetID,
- TargetStatistics[TargetID].ReadCommandSizeBuckets[0],
- TargetStatistics[TargetID].ReadCommandSizeBuckets[1], TargetStatistics[TargetID].ReadCommandSizeBuckets[2], TargetStatistics[TargetID].ReadCommandSizeBuckets[3], TargetStatistics[TargetID].ReadCommandSizeBuckets[4]);
- Length +=
- sprintf(&Buffer[Length],
- " %2d Write %9u %9u %9u %9u %9u\n", TargetID,
- TargetStatistics[TargetID].WriteCommandSizeBuckets[0],
- TargetStatistics[TargetID].WriteCommandSizeBuckets[1], TargetStatistics[TargetID].WriteCommandSizeBuckets[2], TargetStatistics[TargetID].WriteCommandSizeBuckets[3], TargetStatistics[TargetID].WriteCommandSizeBuckets[4]);
- }
- Length += sprintf(&Buffer[Length], "\n\
+ seq_printf(m,
+ " %2d Read %9u %9u %9u %9u %9u\n", tgt,
+ tgt_stats[tgt].read_sz_buckets[0],
+ tgt_stats[tgt].read_sz_buckets[1], tgt_stats[tgt].read_sz_buckets[2], tgt_stats[tgt].read_sz_buckets[3], tgt_stats[tgt].read_sz_buckets[4]);
+ seq_printf(m,
+ " %2d Write %9u %9u %9u %9u %9u\n", tgt,
+ tgt_stats[tgt].write_sz_buckets[0],
+ tgt_stats[tgt].write_sz_buckets[1], tgt_stats[tgt].write_sz_buckets[2], tgt_stats[tgt].write_sz_buckets[3], tgt_stats[tgt].write_sz_buckets[4]);
+ }
+ seq_puts(m, "\n\
Target Command 16-32KB 32-64KB 64-128KB 128-256KB 256KB+\n\
====== ======= ========= ========= ========= ========= =========\n");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
- struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
- if (!TargetFlags->TargetExists)
+ for (tgt = 0; tgt < adapter->maxdev; tgt++) {
+ struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt];
+ if (!tgt_flags->tgt_exists)
continue;
- Length +=
- sprintf(&Buffer[Length],
- " %2d Read %9u %9u %9u %9u %9u\n", TargetID,
- TargetStatistics[TargetID].ReadCommandSizeBuckets[5],
- TargetStatistics[TargetID].ReadCommandSizeBuckets[6], TargetStatistics[TargetID].ReadCommandSizeBuckets[7], TargetStatistics[TargetID].ReadCommandSizeBuckets[8], TargetStatistics[TargetID].ReadCommandSizeBuckets[9]);
- Length +=
- sprintf(&Buffer[Length],
- " %2d Write %9u %9u %9u %9u %9u\n", TargetID,
- TargetStatistics[TargetID].WriteCommandSizeBuckets[5],
- TargetStatistics[TargetID].WriteCommandSizeBuckets[6], TargetStatistics[TargetID].WriteCommandSizeBuckets[7], TargetStatistics[TargetID].WriteCommandSizeBuckets[8], TargetStatistics[TargetID].WriteCommandSizeBuckets[9]);
- }
- Length += sprintf(&Buffer[Length], "\n\n\
+ seq_printf(m,
+ " %2d Read %9u %9u %9u %9u %9u\n", tgt,
+ tgt_stats[tgt].read_sz_buckets[5],
+ tgt_stats[tgt].read_sz_buckets[6], tgt_stats[tgt].read_sz_buckets[7], tgt_stats[tgt].read_sz_buckets[8], tgt_stats[tgt].read_sz_buckets[9]);
+ seq_printf(m,
+ " %2d Write %9u %9u %9u %9u %9u\n", tgt,
+ tgt_stats[tgt].write_sz_buckets[5],
+ tgt_stats[tgt].write_sz_buckets[6], tgt_stats[tgt].write_sz_buckets[7], tgt_stats[tgt].write_sz_buckets[8], tgt_stats[tgt].write_sz_buckets[9]);
+ }
+ seq_puts(m, "\n\n\
ERROR RECOVERY STATISTICS\n\
\n\
Command Aborts Bus Device Resets Host Adapter Resets\n\
Target Requested Completed Requested Completed Requested Completed\n\
ID \\\\\\\\ Attempted //// \\\\\\\\ Attempted //// \\\\\\\\ Attempted ////\n\
====== ===== ===== ===== ===== ===== ===== ===== ===== =====\n");
- for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
- struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
- if (!TargetFlags->TargetExists)
+ for (tgt = 0; tgt < adapter->maxdev; tgt++) {
+ struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt];
+ if (!tgt_flags->tgt_exists)
continue;
- Length += sprintf(&Buffer[Length], "\
- %2d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", TargetID, TargetStatistics[TargetID].CommandAbortsRequested, TargetStatistics[TargetID].CommandAbortsAttempted, TargetStatistics[TargetID].CommandAbortsCompleted, TargetStatistics[TargetID].BusDeviceResetsRequested, TargetStatistics[TargetID].BusDeviceResetsAttempted, TargetStatistics[TargetID].BusDeviceResetsCompleted, TargetStatistics[TargetID].HostAdapterResetsRequested, TargetStatistics[TargetID].HostAdapterResetsAttempted, TargetStatistics[TargetID].HostAdapterResetsCompleted);
- }
- Length += sprintf(&Buffer[Length], "\nExternal Host Adapter Resets: %d\n", HostAdapter->ExternalHostAdapterResets);
- Length += sprintf(&Buffer[Length], "Host Adapter Internal Errors: %d\n", HostAdapter->HostAdapterInternalErrors);
- if (Length >= BusLogic_MessageBufferSize)
- BusLogic_Error("Message Buffer length %d exceeds size %d\n", HostAdapter, Length, BusLogic_MessageBufferSize);
- if ((Length -= Offset) <= 0)
- return 0;
- if (Length >= BytesAvailable)
- Length = BytesAvailable;
- memcpy(ProcBuffer, HostAdapter->MessageBuffer + Offset, Length);
- *StartPointer = ProcBuffer;
- return Length;
+ seq_printf(m, " %2d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n",
+ tgt, tgt_stats[tgt].aborts_request,
+ tgt_stats[tgt].aborts_tried,
+ tgt_stats[tgt].aborts_done,
+ tgt_stats[tgt].bdr_request,
+ tgt_stats[tgt].bdr_tried,
+ tgt_stats[tgt].bdr_done,
+ tgt_stats[tgt].adapter_reset_req,
+ tgt_stats[tgt].adapter_reset_attempt,
+ tgt_stats[tgt].adapter_reset_done);
+ }
+ seq_printf(m, "\nExternal Host Adapter Resets: %d\n", adapter->ext_resets);
+ seq_printf(m, "Host Adapter Internal Errors: %d\n", adapter->adapter_intern_errors);
+ return 0;
}
/*
- BusLogic_Message prints Driver Messages.
+ blogic_msg prints Driver Messages.
*/
-
-static void BusLogic_Message(enum BusLogic_MessageLevel MessageLevel, char *Format, struct BusLogic_HostAdapter *HostAdapter, ...)
+__printf(2, 4)
+static void blogic_msg(enum blogic_msglevel msglevel, char *fmt,
+ struct blogic_adapter *adapter, ...)
{
- static char Buffer[BusLogic_LineBufferSize];
- static bool BeginningOfLine = true;
- va_list Arguments;
- int Length = 0;
- va_start(Arguments, HostAdapter);
- Length = vsprintf(Buffer, Format, Arguments);
- va_end(Arguments);
- if (MessageLevel == BusLogic_AnnounceLevel) {
- static int AnnouncementLines = 0;
- strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength], Buffer);
- HostAdapter->MessageBufferLength += Length;
- if (++AnnouncementLines <= 2)
- printk("%sscsi: %s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
- } else if (MessageLevel == BusLogic_InfoLevel) {
- strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength], Buffer);
- HostAdapter->MessageBufferLength += Length;
- if (BeginningOfLine) {
- if (Buffer[0] != '\n' || Length > 1)
- printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], HostAdapter->HostNumber, Buffer);
+ static char buf[BLOGIC_LINEBUF_SIZE];
+ static bool begin = true;
+ va_list args;
+ int len = 0;
+
+ va_start(args, adapter);
+ len = vscnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (msglevel == BLOGIC_ANNOUNCE_LEVEL) {
+ static int msglines = 0;
+ strcpy(&adapter->msgbuf[adapter->msgbuflen], buf);
+ adapter->msgbuflen += len;
+ if (++msglines <= 2)
+ printk("%sscsi: %s", blogic_msglevelmap[msglevel], buf);
+ } else if (msglevel == BLOGIC_INFO_LEVEL) {
+ strcpy(&adapter->msgbuf[adapter->msgbuflen], buf);
+ adapter->msgbuflen += len;
+ if (begin) {
+ if (buf[0] != '\n' || len > 1)
+ printk("%sscsi%d: %s", blogic_msglevelmap[msglevel], adapter->host_no, buf);
} else
- printk("%s", Buffer);
+ pr_cont("%s", buf);
} else {
- if (BeginningOfLine) {
- if (HostAdapter != NULL && HostAdapter->HostAdapterInitialized)
- printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], HostAdapter->HostNumber, Buffer);
+ if (begin) {
+ if (adapter != NULL && adapter->adapter_initd)
+ printk("%sscsi%d: %s", blogic_msglevelmap[msglevel], adapter->host_no, buf);
else
- printk("%s%s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
+ printk("%s%s", blogic_msglevelmap[msglevel], buf);
} else
- printk("%s", Buffer);
+ pr_cont("%s", buf);
}
- BeginningOfLine = (Buffer[Length - 1] == '\n');
+ begin = (buf[len - 1] == '\n');
}
/*
- BusLogic_ParseKeyword parses an individual option keyword. It returns true
+ blogic_parse parses an individual option keyword. It returns true
and updates the pointer if the keyword is recognized and false otherwise.
*/
-static bool __init BusLogic_ParseKeyword(char **StringPointer, char *Keyword)
+static bool __init blogic_parse(char **str, char *keyword)
{
- char *Pointer = *StringPointer;
- while (*Keyword != '\0') {
- char StringChar = *Pointer++;
- char KeywordChar = *Keyword++;
- if (StringChar >= 'A' && StringChar <= 'Z')
- StringChar += 'a' - 'Z';
- if (KeywordChar >= 'A' && KeywordChar <= 'Z')
- KeywordChar += 'a' - 'Z';
- if (StringChar != KeywordChar)
+ char *pointer = *str;
+ while (*keyword != '\0') {
+ char strch = *pointer++;
+ char keywordch = *keyword++;
+ if (strch >= 'A' && strch <= 'Z')
+ strch += 'a' - 'Z';
+ if (keywordch >= 'A' && keywordch <= 'Z')
+ keywordch += 'a' - 'Z';
+ if (strch != keywordch)
return false;
}
- *StringPointer = Pointer;
+ *str = pointer;
return true;
}
/*
- BusLogic_ParseDriverOptions handles processing of BusLogic Driver Options
+ blogic_parseopts handles processing of BusLogic Driver Options
specifications.
BusLogic Driver Options may be specified either via the Linux Kernel Command
@@ -3394,167 +3512,150 @@ static bool __init BusLogic_ParseKeyword(char **StringPointer, char *Keyword)
selected host adapter.
The BusLogic Driver Probing Options are described in
- <file:Documentation/scsi/BusLogic.txt>.
+ <file:Documentation/scsi/BusLogic.rst>.
*/
-static int __init BusLogic_ParseDriverOptions(char *OptionsString)
+static int __init blogic_parseopts(char *options)
{
while (true) {
- struct BusLogic_DriverOptions *DriverOptions = &BusLogic_DriverOptions[BusLogic_DriverOptionsCount++];
- int TargetID;
- memset(DriverOptions, 0, sizeof(struct BusLogic_DriverOptions));
- while (*OptionsString != '\0' && *OptionsString != ';') {
- /* Probing Options. */
- if (BusLogic_ParseKeyword(&OptionsString, "IO:")) {
- unsigned long IO_Address = simple_strtoul(OptionsString, &OptionsString, 0);
- BusLogic_ProbeOptions.LimitedProbeISA = true;
- switch (IO_Address) {
- case 0x330:
- BusLogic_ProbeOptions.Probe330 = true;
- break;
- case 0x334:
- BusLogic_ProbeOptions.Probe334 = true;
- break;
- case 0x230:
- BusLogic_ProbeOptions.Probe230 = true;
- break;
- case 0x234:
- BusLogic_ProbeOptions.Probe234 = true;
- break;
- case 0x130:
- BusLogic_ProbeOptions.Probe130 = true;
- break;
- case 0x134:
- BusLogic_ProbeOptions.Probe134 = true;
- break;
- default:
- BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid I/O Address 0x%X)\n", NULL, IO_Address);
- return 0;
- }
- } else if (BusLogic_ParseKeyword(&OptionsString, "NoProbeISA"))
- BusLogic_ProbeOptions.NoProbeISA = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "NoProbePCI"))
- BusLogic_ProbeOptions.NoProbePCI = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "NoProbe"))
- BusLogic_ProbeOptions.NoProbe = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "NoSortPCI"))
- BusLogic_ProbeOptions.NoSortPCI = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "MultiMasterFirst"))
- BusLogic_ProbeOptions.MultiMasterFirst = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "FlashPointFirst"))
- BusLogic_ProbeOptions.FlashPointFirst = true;
+ struct blogic_drvr_options *drvr_opts =
+ &blogic_drvr_options[blogic_drvr_options_count++];
+ int tgt_id;
+
+ memset(drvr_opts, 0, sizeof(struct blogic_drvr_options));
+ while (*options != '\0' && *options != ';') {
+ if (blogic_parse(&options, "NoProbePCI"))
+ blogic_probe_options.noprobe_pci = true;
+ else if (blogic_parse(&options, "NoProbe"))
+ blogic_probe_options.noprobe = true;
+ else if (blogic_parse(&options, "NoSortPCI"))
+ blogic_probe_options.nosort_pci = true;
+ else if (blogic_parse(&options, "MultiMasterFirst"))
+ blogic_probe_options.multimaster_first = true;
+ else if (blogic_parse(&options, "FlashPointFirst"))
+ blogic_probe_options.flashpoint_first = true;
/* Tagged Queuing Options. */
- else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:[") || BusLogic_ParseKeyword(&OptionsString, "QD:[")) {
- for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) {
- unsigned short QueueDepth = simple_strtoul(OptionsString, &OptionsString, 0);
- if (QueueDepth > BusLogic_MaxTaggedQueueDepth) {
- BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Queue Depth %d)\n", NULL, QueueDepth);
+ else if (blogic_parse(&options, "QueueDepth:[") ||
+ blogic_parse(&options, "QD:[")) {
+ for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++) {
+ unsigned short qdepth = simple_strtoul(options, &options, 0);
+ if (qdepth > BLOGIC_MAX_TAG_DEPTH) {
+ blogic_err("BusLogic: Invalid Driver Options (invalid Queue Depth %d)\n", NULL, qdepth);
return 0;
}
- DriverOptions->QueueDepth[TargetID] = QueueDepth;
- if (*OptionsString == ',')
- OptionsString++;
- else if (*OptionsString == ']')
+ drvr_opts->qdepth[tgt_id] = qdepth;
+ if (*options == ',')
+ options++;
+ else if (*options == ']')
break;
else {
- BusLogic_Error("BusLogic: Invalid Driver Options " "(',' or ']' expected at '%s')\n", NULL, OptionsString);
+ blogic_err("BusLogic: Invalid Driver Options (',' or ']' expected at '%s')\n", NULL, options);
return 0;
}
}
- if (*OptionsString != ']') {
- BusLogic_Error("BusLogic: Invalid Driver Options " "(']' expected at '%s')\n", NULL, OptionsString);
+ if (*options != ']') {
+ blogic_err("BusLogic: Invalid Driver Options (']' expected at '%s')\n", NULL, options);
return 0;
} else
- OptionsString++;
- } else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:") || BusLogic_ParseKeyword(&OptionsString, "QD:")) {
- unsigned short QueueDepth = simple_strtoul(OptionsString, &OptionsString, 0);
- if (QueueDepth == 0 || QueueDepth > BusLogic_MaxTaggedQueueDepth) {
- BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Queue Depth %d)\n", NULL, QueueDepth);
+ options++;
+ } else if (blogic_parse(&options, "QueueDepth:") || blogic_parse(&options, "QD:")) {
+ unsigned short qdepth = simple_strtoul(options, &options, 0);
+ if (qdepth == 0 ||
+ qdepth > BLOGIC_MAX_TAG_DEPTH) {
+ blogic_err("BusLogic: Invalid Driver Options (invalid Queue Depth %d)\n", NULL, qdepth);
return 0;
}
- DriverOptions->CommonQueueDepth = QueueDepth;
- for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
- DriverOptions->QueueDepth[TargetID] = QueueDepth;
- } else if (BusLogic_ParseKeyword(&OptionsString, "TaggedQueuing:") || BusLogic_ParseKeyword(&OptionsString, "TQ:")) {
- if (BusLogic_ParseKeyword(&OptionsString, "Default")) {
- DriverOptions->TaggedQueuingPermitted = 0x0000;
- DriverOptions->TaggedQueuingPermittedMask = 0x0000;
- } else if (BusLogic_ParseKeyword(&OptionsString, "Enable")) {
- DriverOptions->TaggedQueuingPermitted = 0xFFFF;
- DriverOptions->TaggedQueuingPermittedMask = 0xFFFF;
- } else if (BusLogic_ParseKeyword(&OptionsString, "Disable")) {
- DriverOptions->TaggedQueuingPermitted = 0x0000;
- DriverOptions->TaggedQueuingPermittedMask = 0xFFFF;
+ drvr_opts->common_qdepth = qdepth;
+ for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++)
+ drvr_opts->qdepth[tgt_id] = qdepth;
+ } else if (blogic_parse(&options, "TaggedQueuing:") ||
+ blogic_parse(&options, "TQ:")) {
+ if (blogic_parse(&options, "Default")) {
+ drvr_opts->tagq_ok = 0x0000;
+ drvr_opts->tagq_ok_mask = 0x0000;
+ } else if (blogic_parse(&options, "Enable")) {
+ drvr_opts->tagq_ok = 0xFFFF;
+ drvr_opts->tagq_ok_mask = 0xFFFF;
+ } else if (blogic_parse(&options, "Disable")) {
+ drvr_opts->tagq_ok = 0x0000;
+ drvr_opts->tagq_ok_mask = 0xFFFF;
} else {
- unsigned short TargetBit;
- for (TargetID = 0, TargetBit = 1; TargetID < BusLogic_MaxTargetDevices; TargetID++, TargetBit <<= 1)
- switch (*OptionsString++) {
+ unsigned short tgt_bit;
+ for (tgt_id = 0, tgt_bit = 1;
+ tgt_id < BLOGIC_MAXDEV;
+ tgt_id++, tgt_bit <<= 1)
+ switch (*options++) {
case 'Y':
- DriverOptions->TaggedQueuingPermitted |= TargetBit;
- DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ drvr_opts->tagq_ok |= tgt_bit;
+ drvr_opts->tagq_ok_mask |= tgt_bit;
break;
case 'N':
- DriverOptions->TaggedQueuingPermitted &= ~TargetBit;
- DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ drvr_opts->tagq_ok &= ~tgt_bit;
+ drvr_opts->tagq_ok_mask |= tgt_bit;
break;
case 'X':
break;
default:
- OptionsString--;
- TargetID = BusLogic_MaxTargetDevices;
+ options--;
+ tgt_id = BLOGIC_MAXDEV;
break;
}
}
}
/* Miscellaneous Options. */
- else if (BusLogic_ParseKeyword(&OptionsString, "BusSettleTime:") || BusLogic_ParseKeyword(&OptionsString, "BST:")) {
- unsigned short BusSettleTime = simple_strtoul(OptionsString, &OptionsString, 0);
- if (BusSettleTime > 5 * 60) {
- BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Bus Settle Time %d)\n", NULL, BusSettleTime);
+ else if (blogic_parse(&options, "BusSettleTime:") ||
+ blogic_parse(&options, "BST:")) {
+ unsigned short bus_settle_time =
+ simple_strtoul(options, &options, 0);
+ if (bus_settle_time > 5 * 60) {
+ blogic_err("BusLogic: Invalid Driver Options (invalid Bus Settle Time %d)\n", NULL, bus_settle_time);
return 0;
}
- DriverOptions->BusSettleTime = BusSettleTime;
- } else if (BusLogic_ParseKeyword(&OptionsString, "InhibitTargetInquiry"))
- DriverOptions->LocalOptions.InhibitTargetInquiry = true;
+ drvr_opts->bus_settle_time = bus_settle_time;
+ } else if (blogic_parse(&options,
+ "InhibitTargetInquiry"))
+ drvr_opts->stop_tgt_inquiry = true;
/* Debugging Options. */
- else if (BusLogic_ParseKeyword(&OptionsString, "TraceProbe"))
- BusLogic_GlobalOptions.TraceProbe = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "TraceHardwareReset"))
- BusLogic_GlobalOptions.TraceHardwareReset = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "TraceConfiguration"))
- BusLogic_GlobalOptions.TraceConfiguration = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "TraceErrors"))
- BusLogic_GlobalOptions.TraceErrors = true;
- else if (BusLogic_ParseKeyword(&OptionsString, "Debug")) {
- BusLogic_GlobalOptions.TraceProbe = true;
- BusLogic_GlobalOptions.TraceHardwareReset = true;
- BusLogic_GlobalOptions.TraceConfiguration = true;
- BusLogic_GlobalOptions.TraceErrors = true;
+ else if (blogic_parse(&options, "TraceProbe"))
+ blogic_global_options.trace_probe = true;
+ else if (blogic_parse(&options, "TraceHardwareReset"))
+ blogic_global_options.trace_hw_reset = true;
+ else if (blogic_parse(&options, "TraceConfiguration"))
+ blogic_global_options.trace_config = true;
+ else if (blogic_parse(&options, "TraceErrors"))
+ blogic_global_options.trace_err = true;
+ else if (blogic_parse(&options, "Debug")) {
+ blogic_global_options.trace_probe = true;
+ blogic_global_options.trace_hw_reset = true;
+ blogic_global_options.trace_config = true;
+ blogic_global_options.trace_err = true;
}
- if (*OptionsString == ',')
- OptionsString++;
- else if (*OptionsString != ';' && *OptionsString != '\0') {
- BusLogic_Error("BusLogic: Unexpected Driver Option '%s' " "ignored\n", NULL, OptionsString);
- *OptionsString = '\0';
+ if (*options == ',')
+ options++;
+ else if (*options != ';' && *options != '\0') {
+ blogic_err("BusLogic: Unexpected Driver Option '%s' ignored\n", NULL, options);
+ *options = '\0';
}
}
- if (!(BusLogic_DriverOptionsCount == 0 || BusLogic_ProbeInfoCount == 0 || BusLogic_DriverOptionsCount == BusLogic_ProbeInfoCount)) {
- BusLogic_Error("BusLogic: Invalid Driver Options " "(all or no I/O Addresses must be specified)\n", NULL);
+ if (!(blogic_drvr_options_count == 0 ||
+ blogic_probeinfo_count == 0 ||
+ blogic_drvr_options_count == blogic_probeinfo_count)) {
+ blogic_err("BusLogic: Invalid Driver Options (all or no I/O Addresses must be specified)\n", NULL);
return 0;
}
/*
Tagged Queuing is disabled when the Queue Depth is 1 since queuing
multiple commands is not possible.
*/
- for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
- if (DriverOptions->QueueDepth[TargetID] == 1) {
- unsigned short TargetBit = 1 << TargetID;
- DriverOptions->TaggedQueuingPermitted &= ~TargetBit;
- DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++)
+ if (drvr_opts->qdepth[tgt_id] == 1) {
+ unsigned short tgt_bit = 1 << tgt_id;
+ drvr_opts->tagq_ok &= ~tgt_bit;
+ drvr_opts->tagq_ok_mask |= tgt_bit;
}
- if (*OptionsString == ';')
- OptionsString++;
- if (*OptionsString == '\0')
+ if (*options == ';')
+ options++;
+ if (*options == '\0')
return 0;
}
return 1;
@@ -3564,59 +3665,57 @@ static int __init BusLogic_ParseDriverOptions(char *OptionsString)
Get it all started
*/
-static struct scsi_host_template Bus_Logic_template = {
+static const struct scsi_host_template blogic_template = {
.module = THIS_MODULE,
.proc_name = "BusLogic",
- .proc_info = BusLogic_ProcDirectoryInfo,
+ .write_info = blogic_write_info,
+ .show_info = blogic_show_info,
.name = "BusLogic",
- .info = BusLogic_DriverInfo,
- .queuecommand = BusLogic_QueueCommand,
- .slave_configure = BusLogic_SlaveConfigure,
- .bios_param = BusLogic_BIOSDiskParameters,
- .eh_host_reset_handler = BusLogic_host_reset,
+ .info = blogic_drvr_info,
+ .queuecommand = blogic_qcmd,
+ .sdev_configure = blogic_sdev_configure,
+ .bios_param = blogic_diskparam,
+ .eh_host_reset_handler = blogic_hostreset,
#if 0
- .eh_abort_handler = BusLogic_AbortCommand,
+ .eh_abort_handler = blogic_abort,
#endif
- .unchecked_isa_dma = 1,
.max_sectors = 128,
- .use_clustering = ENABLE_CLUSTERING,
};
/*
- BusLogic_Setup handles processing of Kernel Command Line Arguments.
+ blogic_setup handles processing of Kernel Command Line Arguments.
*/
-static int __init BusLogic_Setup(char *str)
+static int __init blogic_setup(char *str)
{
int ints[3];
(void) get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] != 0) {
- BusLogic_Error("BusLogic: Obsolete Command Line Entry " "Format Ignored\n", NULL);
+ blogic_err("BusLogic: Obsolete Command Line Entry Format Ignored\n", NULL);
return 0;
}
if (str == NULL || *str == '\0')
return 0;
- return BusLogic_ParseDriverOptions(str);
+ return blogic_parseopts(str);
}
/*
* Exit function. Deletes all hosts associated with this driver.
*/
-static void __exit BusLogic_exit(void)
+static void __exit blogic_exit(void)
{
- struct BusLogic_HostAdapter *ha, *next;
+ struct blogic_adapter *ha, *next;
- list_for_each_entry_safe(ha, next, &BusLogic_host_list, host_list)
- BusLogic_ReleaseHostAdapter(ha);
+ list_for_each_entry_safe(ha, next, &blogic_host_list, host_list)
+ blogic_deladapter(ha);
}
-__setup("BusLogic=", BusLogic_Setup);
+__setup("BusLogic=", blogic_setup);
-#ifdef MODULE
-static struct pci_device_id BusLogic_pci_tbl[] __devinitdata = {
+/*static const struct pci_device_id blogic_pci_tbl[] = {
{ PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC,
@@ -3624,9 +3723,14 @@ static struct pci_device_id BusLogic_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ }
+};*/
+static const struct pci_device_id blogic_pci_tbl[] __maybe_unused = {
+ {PCI_DEVICE(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT)},
+ {0, },
};
-#endif
-MODULE_DEVICE_TABLE(pci, BusLogic_pci_tbl);
+MODULE_DEVICE_TABLE(pci, blogic_pci_tbl);
-module_init(BusLogic_init);
-module_exit(BusLogic_exit);
+module_init(blogic_init);
+module_exit(blogic_exit);
diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h
index 649fcb31f26d..79de815e33b0 100644
--- a/drivers/scsi/BusLogic.h
+++ b/drivers/scsi/BusLogic.h
@@ -1,17 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
- This program is free software; you may redistribute and/or modify it under
- the terms of the GNU General Public License Version 2 as published by the
- Free Software Foundation.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for complete details.
The author respectfully requests that any modifications to this software be
sent directly to him for evaluation and testing.
@@ -37,14 +30,14 @@
Define the maximum number of BusLogic Host Adapters supported by this driver.
*/
-#define BusLogic_MaxHostAdapters 16
+#define BLOGIC_MAX_ADAPTERS 16
/*
Define the maximum number of Target Devices supported by this driver.
*/
-#define BusLogic_MaxTargetDevices 16
+#define BLOGIC_MAXDEV 16
/*
@@ -53,7 +46,7 @@
large as the largest single request generated by the I/O Subsystem.
*/
-#define BusLogic_ScatterGatherLimit 128
+#define BLOGIC_SG_LIMIT 128
/*
@@ -62,12 +55,12 @@
Tagged Queuing and whether or not ISA Bounce Buffers are required.
*/
-#define BusLogic_MaxTaggedQueueDepth 64
-#define BusLogic_MaxAutomaticTaggedQueueDepth 28
-#define BusLogic_MinAutomaticTaggedQueueDepth 7
-#define BusLogic_TaggedQueueDepthBB 3
-#define BusLogic_UntaggedQueueDepth 3
-#define BusLogic_UntaggedQueueDepthBB 2
+#define BLOGIC_MAX_TAG_DEPTH 64
+#define BLOGIC_MAX_AUTO_TAG_DEPTH 28
+#define BLOGIC_MIN_AUTO_TAG_DEPTH 7
+#define BLOGIC_TAG_DEPTH_BB 3
+#define BLOGIC_UNTAG_DEPTH 3
+#define BLOGIC_UNTAG_DEPTH_BB 2
/*
@@ -77,7 +70,7 @@
a SCSI Bus Reset.
*/
-#define BusLogic_DefaultBusSettleTime 2
+#define BLOGIC_BUS_SETTLE_TIME 2
/*
@@ -87,7 +80,7 @@
does not cross an allocation block size boundary.
*/
-#define BusLogic_MaxMailboxes 211
+#define BLOGIC_MAX_MAILBOX 211
/*
@@ -95,50 +88,50 @@
Kernel memory allocation.
*/
-#define BusLogic_CCB_AllocationGroupSize 7
+#define BLOGIC_CCB_GRP_ALLOCSIZE 7
/*
Define the Host Adapter Line and Message Buffer Sizes.
*/
-#define BusLogic_LineBufferSize 100
-#define BusLogic_MessageBufferSize 9700
+#define BLOGIC_LINEBUF_SIZE 100
+#define BLOGIC_MSGBUF_SIZE 9700
/*
Define the Driver Message Levels.
*/
-enum BusLogic_MessageLevel {
- BusLogic_AnnounceLevel = 0,
- BusLogic_InfoLevel = 1,
- BusLogic_NoticeLevel = 2,
- BusLogic_WarningLevel = 3,
- BusLogic_ErrorLevel = 4
+enum blogic_msglevel {
+ BLOGIC_ANNOUNCE_LEVEL = 0,
+ BLOGIC_INFO_LEVEL = 1,
+ BLOGIC_NOTICE_LEVEL = 2,
+ BLOGIC_WARN_LEVEL = 3,
+ BLOGIC_ERR_LEVEL = 4
};
-static char *BusLogic_MessageLevelMap[] = { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, KERN_ERR };
+static char *blogic_msglevelmap[] = { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, KERN_ERR };
/*
Define Driver Message macros.
*/
-#define BusLogic_Announce(Format, Arguments...) \
- BusLogic_Message(BusLogic_AnnounceLevel, Format, ##Arguments)
+#define blogic_announce(format, args...) \
+ blogic_msg(BLOGIC_ANNOUNCE_LEVEL, format, ##args)
-#define BusLogic_Info(Format, Arguments...) \
- BusLogic_Message(BusLogic_InfoLevel, Format, ##Arguments)
+#define blogic_info(format, args...) \
+ blogic_msg(BLOGIC_INFO_LEVEL, format, ##args)
-#define BusLogic_Notice(Format, Arguments...) \
- BusLogic_Message(BusLogic_NoticeLevel, Format, ##Arguments)
+#define blogic_notice(format, args...) \
+ blogic_msg(BLOGIC_NOTICE_LEVEL, format, ##args)
-#define BusLogic_Warning(Format, Arguments...) \
- BusLogic_Message(BusLogic_WarningLevel, Format, ##Arguments)
+#define blogic_warn(format, args...) \
+ blogic_msg(BLOGIC_WARN_LEVEL, format, ##args)
-#define BusLogic_Error(Format, Arguments...) \
- BusLogic_Message(BusLogic_ErrorLevel, Format, ##Arguments)
+#define blogic_err(format, args...) \
+ blogic_msg(BLOGIC_ERR_LEVEL, format, ##args)
/*
@@ -146,15 +139,15 @@ static char *BusLogic_MessageLevelMap[] = { KERN_NOTICE, KERN_NOTICE, KERN_NOTIC
of I/O Addresses required by each type.
*/
-enum BusLogic_HostAdapterType {
- BusLogic_MultiMaster = 1,
- BusLogic_FlashPoint = 2
+enum blogic_adapter_type {
+ BLOGIC_MULTIMASTER = 1,
+ BLOGIC_FLASHPOINT = 2
} PACKED;
-#define BusLogic_MultiMasterAddressCount 4
-#define BusLogic_FlashPointAddressCount 256
+#define BLOGIC_MULTIMASTER_ADDR_COUNT 4
+#define BLOGIC_FLASHPOINT_ADDR_COUNT 256
-static int BusLogic_HostAdapterAddressCount[3] = { 0, BusLogic_MultiMasterAddressCount, BusLogic_FlashPointAddressCount };
+static int blogic_adapter_addr_count[3] = { 0, BLOGIC_MULTIMASTER_ADDR_COUNT, BLOGIC_FLASHPOINT_ADDR_COUNT };
/*
@@ -163,19 +156,16 @@ static int BusLogic_HostAdapterAddressCount[3] = { 0, BusLogic_MultiMasterAddres
#ifdef CONFIG_SCSI_FLASHPOINT
-#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
- (HostAdapter->HostAdapterType == BusLogic_MultiMaster)
+#define blogic_multimaster_type(adapter) \
+ (adapter->adapter_type == BLOGIC_MULTIMASTER)
-#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
- (HostAdapter->HostAdapterType == BusLogic_FlashPoint)
+#define blogic_flashpoint_type(adapter) \
+ (adapter->adapter_type == BLOGIC_FLASHPOINT)
#else
-#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
- (true)
-
-#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
- (false)
+#define blogic_multimaster_type(adapter) (true)
+#define blogic_flashpoint_type(adapter) (false)
#endif
@@ -184,35 +174,35 @@ static int BusLogic_HostAdapterAddressCount[3] = { 0, BusLogic_MultiMasterAddres
Define the possible Host Adapter Bus Types.
*/
-enum BusLogic_HostAdapterBusType {
- BusLogic_Unknown_Bus = 0,
- BusLogic_ISA_Bus = 1,
- BusLogic_EISA_Bus = 2,
- BusLogic_PCI_Bus = 3,
- BusLogic_VESA_Bus = 4,
- BusLogic_MCA_Bus = 5
+enum blogic_adapter_bus_type {
+ BLOGIC_UNKNOWN_BUS = 0,
+ BLOGIC_ISA_BUS = 1,
+ BLOGIC_EISA_BUS = 2,
+ BLOGIC_PCI_BUS = 3,
+ BLOGIC_VESA_BUS = 4,
+ BLOGIC_MCA_BUS = 5
} PACKED;
-static char *BusLogic_HostAdapterBusNames[] = { "Unknown", "ISA", "EISA", "PCI", "VESA", "MCA" };
+static char *blogic_adapter_busnames[] = { "Unknown", "ISA", "EISA", "PCI", "VESA", "MCA" };
-static enum BusLogic_HostAdapterBusType BusLogic_HostAdapterBusTypes[] = {
- BusLogic_VESA_Bus, /* BT-4xx */
- BusLogic_ISA_Bus, /* BT-5xx */
- BusLogic_MCA_Bus, /* BT-6xx */
- BusLogic_EISA_Bus, /* BT-7xx */
- BusLogic_Unknown_Bus, /* BT-8xx */
- BusLogic_PCI_Bus /* BT-9xx */
+static enum blogic_adapter_bus_type blogic_adater_bus_types[] = {
+ BLOGIC_VESA_BUS, /* BT-4xx */
+ BLOGIC_ISA_BUS, /* BT-5xx */
+ BLOGIC_MCA_BUS, /* BT-6xx */
+ BLOGIC_EISA_BUS, /* BT-7xx */
+ BLOGIC_UNKNOWN_BUS, /* BT-8xx */
+ BLOGIC_PCI_BUS /* BT-9xx */
};
/*
Define the possible Host Adapter BIOS Disk Geometry Translations.
*/
-enum BusLogic_BIOS_DiskGeometryTranslation {
- BusLogic_BIOS_Disk_Not_Installed = 0,
- BusLogic_BIOS_Disk_Installed_64x32 = 1,
- BusLogic_BIOS_Disk_Installed_128x32 = 2,
- BusLogic_BIOS_Disk_Installed_255x63 = 3
+enum blogic_bios_diskgeometry {
+ BLOGIC_BIOS_NODISK = 0,
+ BLOGIC_BIOS_DISK64x32 = 1,
+ BLOGIC_BIOS_DISK128x32 = 2,
+ BLOGIC_BIOS_DISK255x63 = 3
} PACKED;
@@ -220,9 +210,9 @@ enum BusLogic_BIOS_DiskGeometryTranslation {
Define a 10^18 Statistics Byte Counter data type.
*/
-struct BusLogic_ByteCounter {
- unsigned int Units;
- unsigned int Billions;
+struct blogic_byte_count {
+ unsigned int units;
+ unsigned int billions;
};
@@ -230,79 +220,63 @@ struct BusLogic_ByteCounter {
Define the structure for I/O Address and Bus Probing Information.
*/
-struct BusLogic_ProbeInfo {
- enum BusLogic_HostAdapterType HostAdapterType;
- enum BusLogic_HostAdapterBusType HostAdapterBusType;
- unsigned long IO_Address;
- unsigned long PCI_Address;
- struct pci_dev *PCI_Device;
- unsigned char Bus;
- unsigned char Device;
- unsigned char IRQ_Channel;
+struct blogic_probeinfo {
+ enum blogic_adapter_type adapter_type;
+ enum blogic_adapter_bus_type adapter_bus_type;
+ unsigned long io_addr;
+ unsigned long pci_addr;
+ struct pci_dev *pci_device;
+ unsigned char bus;
+ unsigned char dev;
+ unsigned char irq_ch;
};
/*
Define the Probe Options.
*/
-struct BusLogic_ProbeOptions {
- bool NoProbe:1; /* Bit 0 */
- bool NoProbeISA:1; /* Bit 1 */
- bool NoProbePCI:1; /* Bit 2 */
- bool NoSortPCI:1; /* Bit 3 */
- bool MultiMasterFirst:1;/* Bit 4 */
- bool FlashPointFirst:1; /* Bit 5 */
- bool LimitedProbeISA:1; /* Bit 6 */
- bool Probe330:1; /* Bit 7 */
- bool Probe334:1; /* Bit 8 */
- bool Probe230:1; /* Bit 9 */
- bool Probe234:1; /* Bit 10 */
- bool Probe130:1; /* Bit 11 */
- bool Probe134:1; /* Bit 12 */
+struct blogic_probe_options {
+ bool noprobe:1; /* Bit 0 */
+ bool noprobe_pci:1; /* Bit 2 */
+ bool nosort_pci:1; /* Bit 3 */
+ bool multimaster_first:1; /* Bit 4 */
+ bool flashpoint_first:1; /* Bit 5 */
};
/*
Define the Global Options.
*/
-struct BusLogic_GlobalOptions {
- bool TraceProbe:1; /* Bit 0 */
- bool TraceHardwareReset:1; /* Bit 1 */
- bool TraceConfiguration:1; /* Bit 2 */
- bool TraceErrors:1; /* Bit 3 */
-};
-
-/*
- Define the Local Options.
-*/
-
-struct BusLogic_LocalOptions {
- bool InhibitTargetInquiry:1; /* Bit 0 */
+struct blogic_global_options {
+ bool trace_probe:1; /* Bit 0 */
+ bool trace_hw_reset:1; /* Bit 1 */
+ bool trace_config:1; /* Bit 2 */
+ bool trace_err:1; /* Bit 3 */
};
/*
Define the BusLogic SCSI Host Adapter I/O Register Offsets.
*/
-#define BusLogic_ControlRegisterOffset 0 /* WO register */
-#define BusLogic_StatusRegisterOffset 0 /* RO register */
-#define BusLogic_CommandParameterRegisterOffset 1 /* WO register */
-#define BusLogic_DataInRegisterOffset 1 /* RO register */
-#define BusLogic_InterruptRegisterOffset 2 /* RO register */
-#define BusLogic_GeometryRegisterOffset 3 /* RO register */
+#define BLOGIC_CNTRL_REG 0 /* WO register */
+#define BLOGIC_STATUS_REG 0 /* RO register */
+#define BLOGIC_CMD_PARM_REG 1 /* WO register */
+#define BLOGIC_DATAIN_REG 1 /* RO register */
+#define BLOGIC_INT_REG 2 /* RO register */
+#define BLOGIC_GEOMETRY_REG 3 /* RO register */
/*
Define the structure of the write-only Control Register.
*/
-union BusLogic_ControlRegister {
- unsigned char All;
+union blogic_cntrl_reg {
+ unsigned char all;
struct {
unsigned char:4; /* Bits 0-3 */
- bool SCSIBusReset:1; /* Bit 4 */
- bool InterruptReset:1; /* Bit 5 */
- bool SoftReset:1; /* Bit 6 */
- bool HardReset:1; /* Bit 7 */
+ bool bus_reset:1; /* Bit 4 */
+ bool int_reset:1; /* Bit 5 */
+ bool soft_reset:1; /* Bit 6 */
+ bool hard_reset:1; /* Bit 7 */
} cr;
};
@@ -310,17 +284,17 @@ union BusLogic_ControlRegister {
Define the structure of the read-only Status Register.
*/
-union BusLogic_StatusRegister {
- unsigned char All;
+union blogic_stat_reg {
+ unsigned char all;
struct {
- bool CommandInvalid:1; /* Bit 0 */
- bool Reserved:1; /* Bit 1 */
- bool DataInRegisterReady:1; /* Bit 2 */
- bool CommandParameterRegisterBusy:1; /* Bit 3 */
- bool HostAdapterReady:1; /* Bit 4 */
- bool InitializationRequired:1; /* Bit 5 */
- bool DiagnosticFailure:1; /* Bit 6 */
- bool DiagnosticActive:1; /* Bit 7 */
+ bool cmd_invalid:1; /* Bit 0 */
+ bool rsvd:1; /* Bit 1 */
+ bool datain_ready:1; /* Bit 2 */
+ bool cmd_param_busy:1; /* Bit 3 */
+ bool adapter_ready:1; /* Bit 4 */
+ bool init_reqd:1; /* Bit 5 */
+ bool diag_failed:1; /* Bit 6 */
+ bool diag_active:1; /* Bit 7 */
} sr;
};
@@ -328,15 +302,15 @@ union BusLogic_StatusRegister {
Define the structure of the read-only Interrupt Register.
*/
-union BusLogic_InterruptRegister {
- unsigned char All;
+union blogic_int_reg {
+ unsigned char all;
struct {
- bool IncomingMailboxLoaded:1; /* Bit 0 */
- bool OutgoingMailboxAvailable:1;/* Bit 1 */
- bool CommandComplete:1; /* Bit 2 */
- bool ExternalBusReset:1; /* Bit 3 */
- unsigned char Reserved:3; /* Bits 4-6 */
- bool InterruptValid:1; /* Bit 7 */
+ bool mailin_loaded:1; /* Bit 0 */
+ bool mailout_avail:1; /* Bit 1 */
+ bool cmd_complete:1; /* Bit 2 */
+ bool ext_busreset:1; /* Bit 3 */
+ unsigned char rsvd:3; /* Bits 4-6 */
+ bool int_valid:1; /* Bit 7 */
} ir;
};
@@ -344,13 +318,13 @@ union BusLogic_InterruptRegister {
Define the structure of the read-only Geometry Register.
*/
-union BusLogic_GeometryRegister {
- unsigned char All;
+union blogic_geo_reg {
+ unsigned char all;
struct {
- enum BusLogic_BIOS_DiskGeometryTranslation Drive0Geometry:2; /* Bits 0-1 */
- enum BusLogic_BIOS_DiskGeometryTranslation Drive1Geometry:2; /* Bits 2-3 */
+ enum blogic_bios_diskgeometry d0_geo:2; /* Bits 0-1 */
+ enum blogic_bios_diskgeometry d1_geo:2; /* Bits 2-3 */
unsigned char:3; /* Bits 4-6 */
- bool ExtendedTranslationEnabled:1; /* Bit 7 */
+ bool ext_trans_enable:1; /* Bit 7 */
} gr;
};
@@ -358,82 +332,82 @@ union BusLogic_GeometryRegister {
Define the BusLogic SCSI Host Adapter Command Register Operation Codes.
*/
-enum BusLogic_OperationCode {
- BusLogic_TestCommandCompleteInterrupt = 0x00,
- BusLogic_InitializeMailbox = 0x01,
- BusLogic_ExecuteMailboxCommand = 0x02,
- BusLogic_ExecuteBIOSCommand = 0x03,
- BusLogic_InquireBoardID = 0x04,
- BusLogic_EnableOutgoingMailboxAvailableInt = 0x05,
- BusLogic_SetSCSISelectionTimeout = 0x06,
- BusLogic_SetPreemptTimeOnBus = 0x07,
- BusLogic_SetTimeOffBus = 0x08,
- BusLogic_SetBusTransferRate = 0x09,
- BusLogic_InquireInstalledDevicesID0to7 = 0x0A,
- BusLogic_InquireConfiguration = 0x0B,
- BusLogic_EnableTargetMode = 0x0C,
- BusLogic_InquireSetupInformation = 0x0D,
- BusLogic_WriteAdapterLocalRAM = 0x1A,
- BusLogic_ReadAdapterLocalRAM = 0x1B,
- BusLogic_WriteBusMasterChipFIFO = 0x1C,
- BusLogic_ReadBusMasterChipFIFO = 0x1D,
- BusLogic_EchoCommandData = 0x1F,
- BusLogic_HostAdapterDiagnostic = 0x20,
- BusLogic_SetAdapterOptions = 0x21,
- BusLogic_InquireInstalledDevicesID8to15 = 0x23,
- BusLogic_InquireTargetDevices = 0x24,
- BusLogic_DisableHostAdapterInterrupt = 0x25,
- BusLogic_InitializeExtendedMailbox = 0x81,
- BusLogic_ExecuteSCSICommand = 0x83,
- BusLogic_InquireFirmwareVersion3rdDigit = 0x84,
- BusLogic_InquireFirmwareVersionLetter = 0x85,
- BusLogic_InquirePCIHostAdapterInformation = 0x86,
- BusLogic_InquireHostAdapterModelNumber = 0x8B,
- BusLogic_InquireSynchronousPeriod = 0x8C,
- BusLogic_InquireExtendedSetupInformation = 0x8D,
- BusLogic_EnableStrictRoundRobinMode = 0x8F,
- BusLogic_StoreHostAdapterLocalRAM = 0x90,
- BusLogic_FetchHostAdapterLocalRAM = 0x91,
- BusLogic_StoreLocalDataInEEPROM = 0x92,
- BusLogic_UploadAutoSCSICode = 0x94,
- BusLogic_ModifyIOAddress = 0x95,
- BusLogic_SetCCBFormat = 0x96,
- BusLogic_WriteInquiryBuffer = 0x9A,
- BusLogic_ReadInquiryBuffer = 0x9B,
- BusLogic_FlashROMUploadDownload = 0xA7,
- BusLogic_ReadSCAMData = 0xA8,
- BusLogic_WriteSCAMData = 0xA9
+enum blogic_opcode {
+ BLOGIC_TEST_CMP_COMPLETE = 0x00,
+ BLOGIC_INIT_MBOX = 0x01,
+ BLOGIC_EXEC_MBOX_CMD = 0x02,
+ BLOGIC_EXEC_BIOS_CMD = 0x03,
+ BLOGIC_GET_BOARD_ID = 0x04,
+ BLOGIC_ENABLE_OUTBOX_AVAIL_INT = 0x05,
+ BLOGIC_SET_SELECT_TIMEOUT = 0x06,
+ BLOGIC_SET_PREEMPT_TIME = 0x07,
+ BLOGIC_SET_TIMEOFF_BUS = 0x08,
+ BLOGIC_SET_TXRATE = 0x09,
+ BLOGIC_INQ_DEV0TO7 = 0x0A,
+ BLOGIC_INQ_CONFIG = 0x0B,
+ BLOGIC_TGT_MODE = 0x0C,
+ BLOGIC_INQ_SETUPINFO = 0x0D,
+ BLOGIC_WRITE_LOCALRAM = 0x1A,
+ BLOGIC_READ_LOCALRAM = 0x1B,
+ BLOGIC_WRITE_BUSMASTER_FIFO = 0x1C,
+ BLOGIC_READ_BUSMASTER_FIFO = 0x1D,
+ BLOGIC_ECHO_CMDDATA = 0x1F,
+ BLOGIC_ADAPTER_DIAG = 0x20,
+ BLOGIC_SET_OPTIONS = 0x21,
+ BLOGIC_INQ_DEV8TO15 = 0x23,
+ BLOGIC_INQ_DEV = 0x24,
+ BLOGIC_DISABLE_INT = 0x25,
+ BLOGIC_INIT_EXT_MBOX = 0x81,
+ BLOGIC_EXEC_SCS_CMD = 0x83,
+ BLOGIC_INQ_FWVER_D3 = 0x84,
+ BLOGIC_INQ_FWVER_LETTER = 0x85,
+ BLOGIC_INQ_PCI_INFO = 0x86,
+ BLOGIC_INQ_MODELNO = 0x8B,
+ BLOGIC_INQ_SYNC_PERIOD = 0x8C,
+ BLOGIC_INQ_EXTSETUP = 0x8D,
+ BLOGIC_STRICT_RR = 0x8F,
+ BLOGIC_STORE_LOCALRAM = 0x90,
+ BLOGIC_FETCH_LOCALRAM = 0x91,
+ BLOGIC_STORE_TO_EEPROM = 0x92,
+ BLOGIC_LOAD_AUTOSCSICODE = 0x94,
+ BLOGIC_MOD_IOADDR = 0x95,
+ BLOGIC_SETCCB_FMT = 0x96,
+ BLOGIC_WRITE_INQBUF = 0x9A,
+ BLOGIC_READ_INQBUF = 0x9B,
+ BLOGIC_FLASH_LOAD = 0xA7,
+ BLOGIC_READ_SCAMDATA = 0xA8,
+ BLOGIC_WRITE_SCAMDATA = 0xA9
};
/*
Define the Inquire Board ID reply structure.
*/
-struct BusLogic_BoardID {
- unsigned char BoardType; /* Byte 0 */
- unsigned char CustomFeatures; /* Byte 1 */
- unsigned char FirmwareVersion1stDigit; /* Byte 2 */
- unsigned char FirmwareVersion2ndDigit; /* Byte 3 */
+struct blogic_board_id {
+ unsigned char type; /* Byte 0 */
+ unsigned char custom_features; /* Byte 1 */
+ unsigned char fw_ver_digit1; /* Byte 2 */
+ unsigned char fw_ver_digit2; /* Byte 3 */
};
/*
Define the Inquire Configuration reply structure.
*/
-struct BusLogic_Configuration {
+struct blogic_config {
unsigned char:5; /* Byte 0 Bits 0-4 */
- bool DMA_Channel5:1; /* Byte 0 Bit 5 */
- bool DMA_Channel6:1; /* Byte 0 Bit 6 */
- bool DMA_Channel7:1; /* Byte 0 Bit 7 */
- bool IRQ_Channel9:1; /* Byte 1 Bit 0 */
- bool IRQ_Channel10:1; /* Byte 1 Bit 1 */
- bool IRQ_Channel11:1; /* Byte 1 Bit 2 */
- bool IRQ_Channel12:1; /* Byte 1 Bit 3 */
+ bool dma_ch5:1; /* Byte 0 Bit 5 */
+ bool dma_ch6:1; /* Byte 0 Bit 6 */
+ bool dma_ch7:1; /* Byte 0 Bit 7 */
+ bool irq_ch9:1; /* Byte 1 Bit 0 */
+ bool irq_ch10:1; /* Byte 1 Bit 1 */
+ bool irq_ch11:1; /* Byte 1 Bit 2 */
+ bool irq_ch12:1; /* Byte 1 Bit 3 */
unsigned char:1; /* Byte 1 Bit 4 */
- bool IRQ_Channel14:1; /* Byte 1 Bit 5 */
- bool IRQ_Channel15:1; /* Byte 1 Bit 6 */
+ bool irq_ch14:1; /* Byte 1 Bit 5 */
+ bool irq_ch15:1; /* Byte 1 Bit 6 */
unsigned char:1; /* Byte 1 Bit 7 */
- unsigned char HostAdapterID:4; /* Byte 2 Bits 0-3 */
+ unsigned char id:4; /* Byte 2 Bits 0-3 */
unsigned char:4; /* Byte 2 Bits 4-7 */
};
@@ -441,42 +415,42 @@ struct BusLogic_Configuration {
Define the Inquire Setup Information reply structure.
*/
-struct BusLogic_SynchronousValue {
- unsigned char Offset:4; /* Bits 0-3 */
- unsigned char TransferPeriod:3; /* Bits 4-6 */
- bool Synchronous:1; /* Bit 7 */
+struct blogic_syncval {
+ unsigned char offset:4; /* Bits 0-3 */
+ unsigned char tx_period:3; /* Bits 4-6 */
+ bool sync:1; /* Bit 7 */
};
-struct BusLogic_SetupInformation {
- bool SynchronousInitiationEnabled:1; /* Byte 0 Bit 0 */
- bool ParityCheckingEnabled:1; /* Byte 0 Bit 1 */
- unsigned char:6; /* Byte 0 Bits 2-7 */
- unsigned char BusTransferRate; /* Byte 1 */
- unsigned char PreemptTimeOnBus; /* Byte 2 */
- unsigned char TimeOffBus; /* Byte 3 */
- unsigned char MailboxCount; /* Byte 4 */
- unsigned char MailboxAddress[3]; /* Bytes 5-7 */
- struct BusLogic_SynchronousValue SynchronousValuesID0to7[8]; /* Bytes 8-15 */
- unsigned char DisconnectPermittedID0to7; /* Byte 16 */
- unsigned char Signature; /* Byte 17 */
- unsigned char CharacterD; /* Byte 18 */
- unsigned char HostBusType; /* Byte 19 */
- unsigned char WideTransfersPermittedID0to7; /* Byte 20 */
- unsigned char WideTransfersActiveID0to7; /* Byte 21 */
- struct BusLogic_SynchronousValue SynchronousValuesID8to15[8]; /* Bytes 22-29 */
- unsigned char DisconnectPermittedID8to15; /* Byte 30 */
- unsigned char:8; /* Byte 31 */
- unsigned char WideTransfersPermittedID8to15; /* Byte 32 */
- unsigned char WideTransfersActiveID8to15; /* Byte 33 */
+struct blogic_setup_info {
+ bool sync:1; /* Byte 0 Bit 0 */
+ bool parity:1; /* Byte 0 Bit 1 */
+ unsigned char:6; /* Byte 0 Bits 2-7 */
+ unsigned char tx_rate; /* Byte 1 */
+ unsigned char preempt_time; /* Byte 2 */
+ unsigned char timeoff_bus; /* Byte 3 */
+ unsigned char mbox_count; /* Byte 4 */
+ unsigned char mbox_addr[3]; /* Bytes 5-7 */
+ struct blogic_syncval sync0to7[8]; /* Bytes 8-15 */
+ unsigned char disconnect_ok0to7; /* Byte 16 */
+ unsigned char sig; /* Byte 17 */
+ unsigned char char_d; /* Byte 18 */
+ unsigned char bus_type; /* Byte 19 */
+ unsigned char wide_tx_ok0to7; /* Byte 20 */
+ unsigned char wide_tx_active0to7; /* Byte 21 */
+ struct blogic_syncval sync8to15[8]; /* Bytes 22-29 */
+ unsigned char disconnect_ok8to15; /* Byte 30 */
+ unsigned char:8; /* Byte 31 */
+ unsigned char wide_tx_ok8to15; /* Byte 32 */
+ unsigned char wide_tx_active8to15; /* Byte 33 */
};
/*
Define the Initialize Extended Mailbox request structure.
*/
-struct BusLogic_ExtendedMailboxRequest {
- unsigned char MailboxCount; /* Byte 0 */
- u32 BaseMailboxAddress; /* Bytes 1-4 */
+struct blogic_extmbox_req {
+ unsigned char mbox_count; /* Byte 0 */
+ u32 base_mbox_addr; /* Bytes 1-4 */
} PACKED;
@@ -486,63 +460,63 @@ struct BusLogic_ExtendedMailboxRequest {
the Modify I/O Address command.
*/
-enum BusLogic_ISACompatibleIOPort {
- BusLogic_IO_330 = 0,
- BusLogic_IO_334 = 1,
- BusLogic_IO_230 = 2,
- BusLogic_IO_234 = 3,
- BusLogic_IO_130 = 4,
- BusLogic_IO_134 = 5,
- BusLogic_IO_Disable = 6,
- BusLogic_IO_Disable2 = 7
+enum blogic_isa_ioport {
+ BLOGIC_IO_330 = 0,
+ BLOGIC_IO_334 = 1,
+ BLOGIC_IO_230 = 2,
+ BLOGIC_IO_234 = 3,
+ BLOGIC_IO_130 = 4,
+ BLOGIC_IO_134 = 5,
+ BLOGIC_IO_DISABLE = 6,
+ BLOGIC_IO_DISABLE2 = 7
} PACKED;
-struct BusLogic_PCIHostAdapterInformation {
- enum BusLogic_ISACompatibleIOPort ISACompatibleIOPort; /* Byte 0 */
- unsigned char PCIAssignedIRQChannel; /* Byte 1 */
- bool LowByteTerminated:1; /* Byte 2 Bit 0 */
- bool HighByteTerminated:1; /* Byte 2 Bit 1 */
- unsigned char:2; /* Byte 2 Bits 2-3 */
- bool JP1:1; /* Byte 2 Bit 4 */
- bool JP2:1; /* Byte 2 Bit 5 */
- bool JP3:1; /* Byte 2 Bit 6 */
- bool GenericInfoValid:1;/* Byte 2 Bit 7 */
- unsigned char:8; /* Byte 3 */
+struct blogic_adapter_info {
+ enum blogic_isa_ioport isa_port; /* Byte 0 */
+ unsigned char irq_ch; /* Byte 1 */
+ bool low_term:1; /* Byte 2 Bit 0 */
+ bool high_term:1; /* Byte 2 Bit 1 */
+ unsigned char:2; /* Byte 2 Bits 2-3 */
+ bool JP1:1; /* Byte 2 Bit 4 */
+ bool JP2:1; /* Byte 2 Bit 5 */
+ bool JP3:1; /* Byte 2 Bit 6 */
+ bool genericinfo_valid:1; /* Byte 2 Bit 7 */
+ unsigned char:8; /* Byte 3 */
};
/*
Define the Inquire Extended Setup Information reply structure.
*/
-struct BusLogic_ExtendedSetupInformation {
- unsigned char BusType; /* Byte 0 */
- unsigned char BIOS_Address; /* Byte 1 */
- unsigned short ScatterGatherLimit; /* Bytes 2-3 */
- unsigned char MailboxCount; /* Byte 4 */
- u32 BaseMailboxAddress; /* Bytes 5-8 */
+struct blogic_ext_setup {
+ unsigned char bus_type; /* Byte 0 */
+ unsigned char bios_addr; /* Byte 1 */
+ unsigned short sg_limit; /* Bytes 2-3 */
+ unsigned char mbox_count; /* Byte 4 */
+ u32 base_mbox_addr; /* Bytes 5-8 */
struct {
unsigned char:2; /* Byte 9 Bits 0-1 */
- bool FastOnEISA:1; /* Byte 9 Bit 2 */
+ bool fast_on_eisa:1; /* Byte 9 Bit 2 */
unsigned char:3; /* Byte 9 Bits 3-5 */
- bool LevelSensitiveInterrupt:1; /* Byte 9 Bit 6 */
+ bool level_int:1; /* Byte 9 Bit 6 */
unsigned char:1; /* Byte 9 Bit 7 */
- } Misc;
- unsigned char FirmwareRevision[3]; /* Bytes 10-12 */
- bool HostWideSCSI:1; /* Byte 13 Bit 0 */
- bool HostDifferentialSCSI:1; /* Byte 13 Bit 1 */
- bool HostSupportsSCAM:1; /* Byte 13 Bit 2 */
- bool HostUltraSCSI:1; /* Byte 13 Bit 3 */
- bool HostSmartTermination:1; /* Byte 13 Bit 4 */
- unsigned char:3; /* Byte 13 Bits 5-7 */
+ } misc;
+ unsigned char fw_rev[3]; /* Bytes 10-12 */
+ bool wide:1; /* Byte 13 Bit 0 */
+ bool differential:1; /* Byte 13 Bit 1 */
+ bool scam:1; /* Byte 13 Bit 2 */
+ bool ultra:1; /* Byte 13 Bit 3 */
+ bool smart_term:1; /* Byte 13 Bit 4 */
+ unsigned char:3; /* Byte 13 Bits 5-7 */
} PACKED;
/*
Define the Enable Strict Round Robin Mode request type.
*/
-enum BusLogic_RoundRobinModeRequest {
- BusLogic_AggressiveRoundRobinMode = 0,
- BusLogic_StrictRoundRobinMode = 1
+enum blogic_rr_req {
+ BLOGIC_AGGRESSIVE_RR = 0,
+ BLOGIC_STRICT_RR_MODE = 1
} PACKED;
@@ -550,95 +524,95 @@ enum BusLogic_RoundRobinModeRequest {
Define the Fetch Host Adapter Local RAM request type.
*/
-#define BusLogic_BIOS_BaseOffset 0
-#define BusLogic_AutoSCSI_BaseOffset 64
+#define BLOGIC_BIOS_BASE 0
+#define BLOGIC_AUTOSCSI_BASE 64
-struct BusLogic_FetchHostAdapterLocalRAMRequest {
- unsigned char ByteOffset; /* Byte 0 */
- unsigned char ByteCount; /* Byte 1 */
+struct blogic_fetch_localram {
+ unsigned char offset; /* Byte 0 */
+ unsigned char count; /* Byte 1 */
};
/*
Define the Host Adapter Local RAM AutoSCSI structure.
*/
-struct BusLogic_AutoSCSIData {
- unsigned char InternalFactorySignature[2]; /* Bytes 0-1 */
- unsigned char InformationByteCount; /* Byte 2 */
- unsigned char HostAdapterType[6]; /* Bytes 3-8 */
- unsigned char:8; /* Byte 9 */
- bool FloppyEnabled:1; /* Byte 10 Bit 0 */
- bool FloppySecondary:1; /* Byte 10 Bit 1 */
- bool LevelSensitiveInterrupt:1; /* Byte 10 Bit 2 */
- unsigned char:2; /* Byte 10 Bits 3-4 */
- unsigned char SystemRAMAreaForBIOS:3; /* Byte 10 Bits 5-7 */
- unsigned char DMA_Channel:7; /* Byte 11 Bits 0-6 */
- bool DMA_AutoConfiguration:1; /* Byte 11 Bit 7 */
- unsigned char IRQ_Channel:7; /* Byte 12 Bits 0-6 */
- bool IRQ_AutoConfiguration:1; /* Byte 12 Bit 7 */
- unsigned char DMA_TransferRate; /* Byte 13 */
- unsigned char SCSI_ID; /* Byte 14 */
- bool LowByteTerminated:1; /* Byte 15 Bit 0 */
- bool ParityCheckingEnabled:1; /* Byte 15 Bit 1 */
- bool HighByteTerminated:1; /* Byte 15 Bit 2 */
- bool NoisyCablingEnvironment:1; /* Byte 15 Bit 3 */
- bool FastSynchronousNegotiation:1; /* Byte 15 Bit 4 */
- bool BusResetEnabled:1; /* Byte 15 Bit 5 */
- bool:1; /* Byte 15 Bit 6 */
- bool ActiveNegationEnabled:1; /* Byte 15 Bit 7 */
- unsigned char BusOnDelay; /* Byte 16 */
- unsigned char BusOffDelay; /* Byte 17 */
- bool HostAdapterBIOSEnabled:1; /* Byte 18 Bit 0 */
- bool BIOSRedirectionOfINT19Enabled:1; /* Byte 18 Bit 1 */
- bool ExtendedTranslationEnabled:1; /* Byte 18 Bit 2 */
- bool MapRemovableAsFixedEnabled:1; /* Byte 18 Bit 3 */
- bool:1; /* Byte 18 Bit 4 */
- bool BIOSSupportsMoreThan2DrivesEnabled:1; /* Byte 18 Bit 5 */
- bool BIOSInterruptModeEnabled:1; /* Byte 18 Bit 6 */
- bool FlopticalSupportEnabled:1; /* Byte 19 Bit 7 */
- unsigned short DeviceEnabled; /* Bytes 19-20 */
- unsigned short WidePermitted; /* Bytes 21-22 */
- unsigned short FastPermitted; /* Bytes 23-24 */
- unsigned short SynchronousPermitted; /* Bytes 25-26 */
- unsigned short DisconnectPermitted; /* Bytes 27-28 */
- unsigned short SendStartUnitCommand; /* Bytes 29-30 */
- unsigned short IgnoreInBIOSScan; /* Bytes 31-32 */
- unsigned char PCIInterruptPin:2; /* Byte 33 Bits 0-1 */
- unsigned char HostAdapterIOPortAddress:2; /* Byte 33 Bits 2-3 */
- bool StrictRoundRobinModeEnabled:1; /* Byte 33 Bit 4 */
- bool VESABusSpeedGreaterThan33MHz:1; /* Byte 33 Bit 5 */
- bool VESABurstWriteEnabled:1; /* Byte 33 Bit 6 */
- bool VESABurstReadEnabled:1; /* Byte 33 Bit 7 */
- unsigned short UltraPermitted; /* Bytes 34-35 */
- unsigned int:32; /* Bytes 36-39 */
- unsigned char:8; /* Byte 40 */
- unsigned char AutoSCSIMaximumLUN; /* Byte 41 */
- bool:1; /* Byte 42 Bit 0 */
- bool SCAM_Dominant:1; /* Byte 42 Bit 1 */
- bool SCAM_Enabled:1; /* Byte 42 Bit 2 */
- bool SCAM_Level2:1; /* Byte 42 Bit 3 */
- unsigned char:4; /* Byte 42 Bits 4-7 */
- bool INT13ExtensionEnabled:1; /* Byte 43 Bit 0 */
- bool:1; /* Byte 43 Bit 1 */
- bool CDROMBootEnabled:1; /* Byte 43 Bit 2 */
- unsigned char:5; /* Byte 43 Bits 3-7 */
- unsigned char BootTargetID:4; /* Byte 44 Bits 0-3 */
- unsigned char BootChannel:4; /* Byte 44 Bits 4-7 */
- unsigned char ForceBusDeviceScanningOrder:1; /* Byte 45 Bit 0 */
- unsigned char:7; /* Byte 45 Bits 1-7 */
- unsigned short NonTaggedToAlternateLUNPermitted; /* Bytes 46-47 */
- unsigned short RenegotiateSyncAfterCheckCondition; /* Bytes 48-49 */
- unsigned char Reserved[10]; /* Bytes 50-59 */
- unsigned char ManufacturingDiagnostic[2]; /* Bytes 60-61 */
- unsigned short Checksum; /* Bytes 62-63 */
+struct blogic_autoscsi {
+ unsigned char factory_sig[2]; /* Bytes 0-1 */
+ unsigned char info_bytes; /* Byte 2 */
+ unsigned char adapter_type[6]; /* Bytes 3-8 */
+ unsigned char:8; /* Byte 9 */
+ bool floppy:1; /* Byte 10 Bit 0 */
+ bool floppy_sec:1; /* Byte 10 Bit 1 */
+ bool level_int:1; /* Byte 10 Bit 2 */
+ unsigned char:2; /* Byte 10 Bits 3-4 */
+ unsigned char systemram_bios:3; /* Byte 10 Bits 5-7 */
+ unsigned char dma_ch:7; /* Byte 11 Bits 0-6 */
+ bool dma_autoconf:1; /* Byte 11 Bit 7 */
+ unsigned char irq_ch:7; /* Byte 12 Bits 0-6 */
+ bool irq_autoconf:1; /* Byte 12 Bit 7 */
+ unsigned char dma_tx_rate; /* Byte 13 */
+ unsigned char scsi_id; /* Byte 14 */
+ bool low_term:1; /* Byte 15 Bit 0 */
+ bool parity:1; /* Byte 15 Bit 1 */
+ bool high_term:1; /* Byte 15 Bit 2 */
+ bool noisy_cable:1; /* Byte 15 Bit 3 */
+ bool fast_sync_neg:1; /* Byte 15 Bit 4 */
+ bool reset_enabled:1; /* Byte 15 Bit 5 */
+ bool:1; /* Byte 15 Bit 6 */
+ bool active_negation:1; /* Byte 15 Bit 7 */
+ unsigned char bus_on_delay; /* Byte 16 */
+ unsigned char bus_off_delay; /* Byte 17 */
+ bool bios_enabled:1; /* Byte 18 Bit 0 */
+ bool int19_redir_enabled:1; /* Byte 18 Bit 1 */
+ bool ext_trans_enable:1; /* Byte 18 Bit 2 */
+ bool removable_as_fixed:1; /* Byte 18 Bit 3 */
+ bool:1; /* Byte 18 Bit 4 */
+ bool morethan2_drives:1; /* Byte 18 Bit 5 */
+ bool bios_int:1; /* Byte 18 Bit 6 */
+ bool floptical:1; /* Byte 19 Bit 7 */
+ unsigned short dev_enabled; /* Bytes 19-20 */
+ unsigned short wide_ok; /* Bytes 21-22 */
+ unsigned short fast_ok; /* Bytes 23-24 */
+ unsigned short sync_ok; /* Bytes 25-26 */
+ unsigned short discon_ok; /* Bytes 27-28 */
+ unsigned short send_start_unit; /* Bytes 29-30 */
+ unsigned short ignore_bios_scan; /* Bytes 31-32 */
+ unsigned char pci_int_pin:2; /* Byte 33 Bits 0-1 */
+ unsigned char adapter_ioport:2; /* Byte 33 Bits 2-3 */
+ bool strict_rr_enabled:1; /* Byte 33 Bit 4 */
+ bool vesabus_33mhzplus:1; /* Byte 33 Bit 5 */
+ bool vesa_burst_write:1; /* Byte 33 Bit 6 */
+ bool vesa_burst_read:1; /* Byte 33 Bit 7 */
+ unsigned short ultra_ok; /* Bytes 34-35 */
+ unsigned int:32; /* Bytes 36-39 */
+ unsigned char:8; /* Byte 40 */
+ unsigned char autoscsi_maxlun; /* Byte 41 */
+ bool:1; /* Byte 42 Bit 0 */
+ bool scam_dominant:1; /* Byte 42 Bit 1 */
+ bool scam_enabled:1; /* Byte 42 Bit 2 */
+ bool scam_lev2:1; /* Byte 42 Bit 3 */
+ unsigned char:4; /* Byte 42 Bits 4-7 */
+ bool int13_exten:1; /* Byte 43 Bit 0 */
+ bool:1; /* Byte 43 Bit 1 */
+ bool cd_boot:1; /* Byte 43 Bit 2 */
+ unsigned char:5; /* Byte 43 Bits 3-7 */
+ unsigned char boot_id:4; /* Byte 44 Bits 0-3 */
+ unsigned char boot_ch:4; /* Byte 44 Bits 4-7 */
+ unsigned char force_scan_order:1; /* Byte 45 Bit 0 */
+ unsigned char:7; /* Byte 45 Bits 1-7 */
+ unsigned short nontagged_to_alt_ok; /* Bytes 46-47 */
+ unsigned short reneg_sync_on_check; /* Bytes 48-49 */
+ unsigned char rsvd[10]; /* Bytes 50-59 */
+ unsigned char manuf_diag[2]; /* Bytes 60-61 */
+ unsigned short cksum; /* Bytes 62-63 */
} PACKED;
/*
Define the Host Adapter Local RAM Auto SCSI Byte 45 structure.
*/
-struct BusLogic_AutoSCSIByte45 {
- unsigned char ForceBusDeviceScanningOrder:1; /* Bit 0 */
+struct blogic_autoscsi_byte45 {
+ unsigned char force_scan_order:1; /* Bit 0 */
unsigned char:7; /* Bits 1-7 */
};
@@ -646,13 +620,13 @@ struct BusLogic_AutoSCSIByte45 {
Define the Host Adapter Local RAM BIOS Drive Map Byte structure.
*/
-#define BusLogic_BIOS_DriveMapOffset 17
+#define BLOGIC_BIOS_DRVMAP 17
-struct BusLogic_BIOSDriveMapByte {
- unsigned char TargetIDBit3:1; /* Bit 0 */
- unsigned char:2; /* Bits 1-2 */
- enum BusLogic_BIOS_DiskGeometryTranslation DiskGeometry:2; /* Bits 3-4 */
- unsigned char TargetID:3; /* Bits 5-7 */
+struct blogic_bios_drvmap {
+ unsigned char tgt_idbit3:1; /* Bit 0 */
+ unsigned char:2; /* Bits 1-2 */
+ enum blogic_bios_diskgeometry diskgeom:2; /* Bits 3-4 */
+ unsigned char tgt_id:3; /* Bits 5-7 */
};
/*
@@ -660,19 +634,19 @@ struct BusLogic_BIOSDriveMapByte {
necessary to support more than 8 Logical Units per Target Device.
*/
-enum BusLogic_SetCCBFormatRequest {
- BusLogic_LegacyLUNFormatCCB = 0,
- BusLogic_ExtendedLUNFormatCCB = 1
+enum blogic_setccb_fmt {
+ BLOGIC_LEGACY_LUN_CCB = 0,
+ BLOGIC_EXT_LUN_CCB = 1
} PACKED;
/*
Define the Outgoing Mailbox Action Codes.
*/
-enum BusLogic_ActionCode {
- BusLogic_OutgoingMailboxFree = 0x00,
- BusLogic_MailboxStartCommand = 0x01,
- BusLogic_MailboxAbortCommand = 0x02
+enum blogic_action {
+ BLOGIC_OUTBOX_FREE = 0x00,
+ BLOGIC_MBOX_START = 0x01,
+ BLOGIC_MBOX_ABORT = 0x02
} PACKED;
@@ -682,26 +656,26 @@ enum BusLogic_ActionCode {
completion codes are stored in the CCB; it only uses codes 1, 2, 4, and 5.
*/
-enum BusLogic_CompletionCode {
- BusLogic_IncomingMailboxFree = 0x00,
- BusLogic_CommandCompletedWithoutError = 0x01,
- BusLogic_CommandAbortedAtHostRequest = 0x02,
- BusLogic_AbortedCommandNotFound = 0x03,
- BusLogic_CommandCompletedWithError = 0x04,
- BusLogic_InvalidCCB = 0x05
+enum blogic_cmplt_code {
+ BLOGIC_INBOX_FREE = 0x00,
+ BLOGIC_CMD_COMPLETE_GOOD = 0x01,
+ BLOGIC_CMD_ABORT_BY_HOST = 0x02,
+ BLOGIC_CMD_NOTFOUND = 0x03,
+ BLOGIC_CMD_COMPLETE_ERROR = 0x04,
+ BLOGIC_INVALID_CCB = 0x05
} PACKED;
/*
Define the Command Control Block (CCB) Opcodes.
*/
-enum BusLogic_CCB_Opcode {
- BusLogic_InitiatorCCB = 0x00,
- BusLogic_TargetCCB = 0x01,
- BusLogic_InitiatorCCB_ScatterGather = 0x02,
- BusLogic_InitiatorCCB_ResidualDataLength = 0x03,
- BusLogic_InitiatorCCB_ScatterGatherResidual = 0x04,
- BusLogic_BusDeviceReset = 0x81
+enum blogic_ccb_opcode {
+ BLOGIC_INITIATOR_CCB = 0x00,
+ BLOGIC_TGT_CCB = 0x01,
+ BLOGIC_INITIATOR_CCB_SG = 0x02,
+ BLOGIC_INITIATOR_CCBB_RESIDUAL = 0x03,
+ BLOGIC_INITIATOR_CCB_SG_RESIDUAL = 0x04,
+ BLOGIC_BDR = 0x81
} PACKED;
@@ -709,11 +683,11 @@ enum BusLogic_CCB_Opcode {
Define the CCB Data Direction Codes.
*/
-enum BusLogic_DataDirection {
- BusLogic_UncheckedDataTransfer = 0,
- BusLogic_DataInLengthChecked = 1,
- BusLogic_DataOutLengthChecked = 2,
- BusLogic_NoDataTransfer = 3
+enum blogic_datadir {
+ BLOGIC_UNCHECKED_TX = 0,
+ BLOGIC_DATAIN_CHECKED = 1,
+ BLOGIC_DATAOUT_CHECKED = 2,
+ BLOGIC_NOTX = 3
};
@@ -722,32 +696,32 @@ enum BusLogic_DataDirection {
return status code 0x0C; it uses 0x12 for both overruns and underruns.
*/
-enum BusLogic_HostAdapterStatus {
- BusLogic_CommandCompletedNormally = 0x00,
- BusLogic_LinkedCommandCompleted = 0x0A,
- BusLogic_LinkedCommandCompletedWithFlag = 0x0B,
- BusLogic_DataUnderRun = 0x0C,
- BusLogic_SCSISelectionTimeout = 0x11,
- BusLogic_DataOverRun = 0x12,
- BusLogic_UnexpectedBusFree = 0x13,
- BusLogic_InvalidBusPhaseRequested = 0x14,
- BusLogic_InvalidOutgoingMailboxActionCode = 0x15,
- BusLogic_InvalidCommandOperationCode = 0x16,
- BusLogic_LinkedCCBhasInvalidLUN = 0x17,
- BusLogic_InvalidCommandParameter = 0x1A,
- BusLogic_AutoRequestSenseFailed = 0x1B,
- BusLogic_TaggedQueuingMessageRejected = 0x1C,
- BusLogic_UnsupportedMessageReceived = 0x1D,
- BusLogic_HostAdapterHardwareFailed = 0x20,
- BusLogic_TargetFailedResponseToATN = 0x21,
- BusLogic_HostAdapterAssertedRST = 0x22,
- BusLogic_OtherDeviceAssertedRST = 0x23,
- BusLogic_TargetDeviceReconnectedImproperly = 0x24,
- BusLogic_HostAdapterAssertedBusDeviceReset = 0x25,
- BusLogic_AbortQueueGenerated = 0x26,
- BusLogic_HostAdapterSoftwareError = 0x27,
- BusLogic_HostAdapterHardwareTimeoutError = 0x30,
- BusLogic_SCSIParityErrorDetected = 0x34
+enum blogic_adapter_status {
+ BLOGIC_CMD_CMPLT_NORMAL = 0x00,
+ BLOGIC_LINK_CMD_CMPLT = 0x0A,
+ BLOGIC_LINK_CMD_CMPLT_FLAG = 0x0B,
+ BLOGIC_DATA_UNDERRUN = 0x0C,
+ BLOGIC_SELECT_TIMEOUT = 0x11,
+ BLOGIC_DATA_OVERRUN = 0x12,
+ BLOGIC_NOEXPECT_BUSFREE = 0x13,
+ BLOGIC_INVALID_BUSPHASE = 0x14,
+ BLOGIC_INVALID_OUTBOX_CODE = 0x15,
+ BLOGIC_INVALID_CMD_CODE = 0x16,
+ BLOGIC_LINKCCB_BADLUN = 0x17,
+ BLOGIC_BAD_CMD_PARAM = 0x1A,
+ BLOGIC_AUTOREQSENSE_FAIL = 0x1B,
+ BLOGIC_TAGQUEUE_REJECT = 0x1C,
+ BLOGIC_BAD_MSG_RCVD = 0x1D,
+ BLOGIC_HW_FAIL = 0x20,
+ BLOGIC_NORESPONSE_TO_ATN = 0x21,
+ BLOGIC_HW_RESET = 0x22,
+ BLOGIC_RST_FROM_OTHERDEV = 0x23,
+ BLOGIC_BAD_RECONNECT = 0x24,
+ BLOGIC_HW_BDR = 0x25,
+ BLOGIC_ABRT_QUEUE = 0x26,
+ BLOGIC_ADAPTER_SW_ERROR = 0x27,
+ BLOGIC_HW_TIMEOUT = 0x30,
+ BLOGIC_PARITY_ERR = 0x34
} PACKED;
@@ -755,30 +729,28 @@ enum BusLogic_HostAdapterStatus {
Define the SCSI Target Device Status Codes.
*/
-enum BusLogic_TargetDeviceStatus {
- BusLogic_OperationGood = 0x00,
- BusLogic_CheckCondition = 0x02,
- BusLogic_DeviceBusy = 0x08
+enum blogic_tgt_status {
+ BLOGIC_OP_GOOD = 0x00,
+ BLOGIC_CHECKCONDITION = 0x02,
+ BLOGIC_DEVBUSY = 0x08
} PACKED;
/*
Define the Queue Tag Codes.
*/
-enum BusLogic_QueueTag {
- BusLogic_SimpleQueueTag = 0,
- BusLogic_HeadOfQueueTag = 1,
- BusLogic_OrderedQueueTag = 2,
- BusLogic_ReservedQT = 3
+enum blogic_queuetag {
+ BLOGIC_SIMPLETAG = 0,
+ BLOGIC_HEADTAG = 1,
+ BLOGIC_ORDEREDTAG = 2,
+ BLOGIC_RSVDTAG = 3
};
/*
Define the SCSI Command Descriptor Block (CDB).
*/
-#define BusLogic_CDB_MaxLength 12
-
-typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
+#define BLOGIC_CDB_MAXLEN 12
/*
@@ -786,20 +758,20 @@ typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
Firmware Interface and the FlashPoint SCCB Manager.
*/
-struct BusLogic_ScatterGatherSegment {
- u32 SegmentByteCount; /* Bytes 0-3 */
- u32 SegmentDataPointer; /* Bytes 4-7 */
+struct blogic_sg_seg {
+ u32 segbytes; /* Bytes 0-3 */
+ u32 segdata; /* Bytes 4-7 */
};
/*
Define the Driver CCB Status Codes.
*/
-enum BusLogic_CCB_Status {
- BusLogic_CCB_Free = 0,
- BusLogic_CCB_Active = 1,
- BusLogic_CCB_Completed = 2,
- BusLogic_CCB_Reset = 3
+enum blogic_ccb_status {
+ BLOGIC_CCB_FREE = 0,
+ BLOGIC_CCB_ACTIVE = 1,
+ BLOGIC_CCB_COMPLETE = 2,
+ BLOGIC_CCB_RESET = 3
} PACKED;
@@ -822,79 +794,81 @@ enum BusLogic_CCB_Status {
32 Logical Units per Target Device.
*/
-struct BusLogic_CCB {
+struct blogic_ccb {
/*
MultiMaster Firmware and FlashPoint SCCB Manager Common Portion.
*/
- enum BusLogic_CCB_Opcode Opcode; /* Byte 0 */
- unsigned char:3; /* Byte 1 Bits 0-2 */
- enum BusLogic_DataDirection DataDirection:2; /* Byte 1 Bits 3-4 */
- bool TagEnable:1; /* Byte 1 Bit 5 */
- enum BusLogic_QueueTag QueueTag:2; /* Byte 1 Bits 6-7 */
- unsigned char CDB_Length; /* Byte 2 */
- unsigned char SenseDataLength; /* Byte 3 */
- u32 DataLength; /* Bytes 4-7 */
- u32 DataPointer; /* Bytes 8-11 */
- unsigned char:8; /* Byte 12 */
- unsigned char:8; /* Byte 13 */
- enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 14 */
- enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 15 */
- unsigned char TargetID; /* Byte 16 */
- unsigned char LogicalUnit:5; /* Byte 17 Bits 0-4 */
- bool LegacyTagEnable:1; /* Byte 17 Bit 5 */
- enum BusLogic_QueueTag LegacyQueueTag:2; /* Byte 17 Bits 6-7 */
- SCSI_CDB_T CDB; /* Bytes 18-29 */
- unsigned char:8; /* Byte 30 */
- unsigned char:8; /* Byte 31 */
- unsigned int:32; /* Bytes 32-35 */
- u32 SenseDataPointer; /* Bytes 36-39 */
+ enum blogic_ccb_opcode opcode; /* Byte 0 */
+ unsigned char:3; /* Byte 1 Bits 0-2 */
+ enum blogic_datadir datadir:2; /* Byte 1 Bits 3-4 */
+ bool tag_enable:1; /* Byte 1 Bit 5 */
+ enum blogic_queuetag queuetag:2; /* Byte 1 Bits 6-7 */
+ unsigned char cdblen; /* Byte 2 */
+ unsigned char sense_datalen; /* Byte 3 */
+ u32 datalen; /* Bytes 4-7 */
+ u32 data; /* Bytes 8-11 */
+ unsigned char:8; /* Byte 12 */
+ unsigned char:8; /* Byte 13 */
+ enum blogic_adapter_status adapter_status; /* Byte 14 */
+ enum blogic_tgt_status tgt_status; /* Byte 15 */
+ unsigned char tgt_id; /* Byte 16 */
+ unsigned char lun:5; /* Byte 17 Bits 0-4 */
+ bool legacytag_enable:1; /* Byte 17 Bit 5 */
+ enum blogic_queuetag legacy_tag:2; /* Byte 17 Bits 6-7 */
+ unsigned char cdb[BLOGIC_CDB_MAXLEN]; /* Bytes 18-29 */
+ unsigned char:8; /* Byte 30 */
+ unsigned char:8; /* Byte 31 */
+ u32 rsvd_int; /* Bytes 32-35 */
+ u32 sensedata; /* Bytes 36-39 */
/*
FlashPoint SCCB Manager Defined Portion.
*/
- void (*CallbackFunction) (struct BusLogic_CCB *); /* Bytes 40-43 */
- u32 BaseAddress; /* Bytes 44-47 */
- enum BusLogic_CompletionCode CompletionCode; /* Byte 48 */
+ void (*callback) (struct blogic_ccb *); /* Bytes 40-43 */
+ u32 base_addr; /* Bytes 44-47 */
+ enum blogic_cmplt_code comp_code; /* Byte 48 */
#ifdef CONFIG_SCSI_FLASHPOINT
- unsigned char:8; /* Byte 49 */
- unsigned short OS_Flags; /* Bytes 50-51 */
- unsigned char Private[48]; /* Bytes 52-99 */
+ unsigned char:8; /* Byte 49 */
+ u16 os_flags; /* Bytes 50-51 */
+ unsigned char private[24]; /* Bytes 52-99 */
+ void *rsvd1;
+ void *rsvd2;
+ unsigned char private2[16];
#endif
/*
BusLogic Linux Driver Defined Portion.
*/
- dma_addr_t AllocationGroupHead;
- unsigned int AllocationGroupSize;
- u32 DMA_Handle;
- enum BusLogic_CCB_Status Status;
- unsigned long SerialNumber;
- struct scsi_cmnd *Command;
- struct BusLogic_HostAdapter *HostAdapter;
- struct BusLogic_CCB *Next;
- struct BusLogic_CCB *NextAll;
- struct BusLogic_ScatterGatherSegment
- ScatterGatherList[BusLogic_ScatterGatherLimit];
+ dma_addr_t allocgrp_head;
+ unsigned int allocgrp_size;
+ u32 dma_handle;
+ enum blogic_ccb_status status;
+ unsigned long serial;
+ struct scsi_cmnd *command;
+ struct blogic_adapter *adapter;
+ struct blogic_ccb *next;
+ struct blogic_ccb *next_all;
+ struct blogic_sg_seg sglist[BLOGIC_SG_LIMIT];
};
/*
Define the 32 Bit Mode Outgoing Mailbox structure.
*/
-struct BusLogic_OutgoingMailbox {
- u32 CCB; /* Bytes 0-3 */
- unsigned int:24; /* Bytes 4-6 */
- enum BusLogic_ActionCode ActionCode; /* Byte 7 */
+struct blogic_outbox {
+ u32 ccb; /* Bytes 0-3 */
+ u32:24; /* Bytes 4-6 */
+ enum blogic_action action; /* Byte 7 */
};
/*
Define the 32 Bit Mode Incoming Mailbox structure.
*/
-struct BusLogic_IncomingMailbox {
- u32 CCB; /* Bytes 0-3 */
- enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 4 */
- enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 5 */
- unsigned char:8; /* Byte 6 */
- enum BusLogic_CompletionCode CompletionCode; /* Byte 7 */
+struct blogic_inbox {
+ u32 ccb; /* Bytes 0-3 */
+ enum blogic_adapter_status adapter_status; /* Byte 4 */
+ enum blogic_tgt_status tgt_status; /* Byte 5 */
+ unsigned char:8; /* Byte 6 */
+ enum blogic_cmplt_code comp_code; /* Byte 7 */
};
@@ -902,64 +876,60 @@ struct BusLogic_IncomingMailbox {
Define the BusLogic Driver Options structure.
*/
-struct BusLogic_DriverOptions {
- unsigned short TaggedQueuingPermitted;
- unsigned short TaggedQueuingPermittedMask;
- unsigned short BusSettleTime;
- struct BusLogic_LocalOptions LocalOptions;
- unsigned char CommonQueueDepth;
- unsigned char QueueDepth[BusLogic_MaxTargetDevices];
+struct blogic_drvr_options {
+ unsigned short tagq_ok;
+ unsigned short tagq_ok_mask;
+ unsigned short bus_settle_time;
+ unsigned short stop_tgt_inquiry;
+ unsigned char common_qdepth;
+ unsigned char qdepth[BLOGIC_MAXDEV];
};
/*
Define the Host Adapter Target Flags structure.
*/
-struct BusLogic_TargetFlags {
- bool TargetExists:1;
- bool TaggedQueuingSupported:1;
- bool WideTransfersSupported:1;
- bool TaggedQueuingActive:1;
- bool WideTransfersActive:1;
- bool CommandSuccessfulFlag:1;
- bool TargetInfoReported:1;
+struct blogic_tgt_flags {
+ bool tgt_exists:1;
+ bool tagq_ok:1;
+ bool wide_ok:1;
+ bool tagq_active:1;
+ bool wide_active:1;
+ bool cmd_good:1;
+ bool tgt_info_in:1;
};
/*
Define the Host Adapter Target Statistics structure.
*/
-#define BusLogic_SizeBuckets 10
-
-typedef unsigned int BusLogic_CommandSizeBuckets_T[BusLogic_SizeBuckets];
-
-struct BusLogic_TargetStatistics {
- unsigned int CommandsAttempted;
- unsigned int CommandsCompleted;
- unsigned int ReadCommands;
- unsigned int WriteCommands;
- struct BusLogic_ByteCounter TotalBytesRead;
- struct BusLogic_ByteCounter TotalBytesWritten;
- BusLogic_CommandSizeBuckets_T ReadCommandSizeBuckets;
- BusLogic_CommandSizeBuckets_T WriteCommandSizeBuckets;
- unsigned short CommandAbortsRequested;
- unsigned short CommandAbortsAttempted;
- unsigned short CommandAbortsCompleted;
- unsigned short BusDeviceResetsRequested;
- unsigned short BusDeviceResetsAttempted;
- unsigned short BusDeviceResetsCompleted;
- unsigned short HostAdapterResetsRequested;
- unsigned short HostAdapterResetsAttempted;
- unsigned short HostAdapterResetsCompleted;
+#define BLOGIC_SZ_BUCKETS 10
+
+struct blogic_tgt_stats {
+ unsigned int cmds_tried;
+ unsigned int cmds_complete;
+ unsigned int read_cmds;
+ unsigned int write_cmds;
+ struct blogic_byte_count bytesread;
+ struct blogic_byte_count byteswritten;
+ unsigned int read_sz_buckets[BLOGIC_SZ_BUCKETS];
+ unsigned int write_sz_buckets[BLOGIC_SZ_BUCKETS];
+ unsigned short aborts_request;
+ unsigned short aborts_tried;
+ unsigned short aborts_done;
+ unsigned short bdr_request;
+ unsigned short bdr_tried;
+ unsigned short bdr_done;
+ unsigned short adapter_reset_req;
+ unsigned short adapter_reset_attempt;
+ unsigned short adapter_reset_done;
};
/*
Define the FlashPoint Card Handle data type.
*/
-#define FlashPoint_BadCardHandle 0xFFFFFFFF
-
-typedef unsigned int FlashPoint_CardHandle_T;
+#define FPOINT_BADCARD_HANDLE 0xFFFFFFFFL
/*
@@ -967,179 +937,176 @@ typedef unsigned int FlashPoint_CardHandle_T;
by the FlashPoint SCCB Manager.
*/
-struct FlashPoint_Info {
- u32 BaseAddress; /* Bytes 0-3 */
- bool Present; /* Byte 4 */
- unsigned char IRQ_Channel; /* Byte 5 */
- unsigned char SCSI_ID; /* Byte 6 */
- unsigned char SCSI_LUN; /* Byte 7 */
- unsigned short FirmwareRevision; /* Bytes 8-9 */
- unsigned short SynchronousPermitted; /* Bytes 10-11 */
- unsigned short FastPermitted; /* Bytes 12-13 */
- unsigned short UltraPermitted; /* Bytes 14-15 */
- unsigned short DisconnectPermitted; /* Bytes 16-17 */
- unsigned short WidePermitted; /* Bytes 18-19 */
- bool ParityCheckingEnabled:1; /* Byte 20 Bit 0 */
- bool HostWideSCSI:1; /* Byte 20 Bit 1 */
- bool HostSoftReset:1; /* Byte 20 Bit 2 */
- bool ExtendedTranslationEnabled:1; /* Byte 20 Bit 3 */
- bool LowByteTerminated:1; /* Byte 20 Bit 4 */
- bool HighByteTerminated:1; /* Byte 20 Bit 5 */
- bool ReportDataUnderrun:1; /* Byte 20 Bit 6 */
- bool SCAM_Enabled:1; /* Byte 20 Bit 7 */
- bool SCAM_Level2:1; /* Byte 21 Bit 0 */
- unsigned char:7; /* Byte 21 Bits 1-7 */
- unsigned char Family; /* Byte 22 */
- unsigned char BusType; /* Byte 23 */
- unsigned char ModelNumber[3]; /* Bytes 24-26 */
- unsigned char RelativeCardNumber; /* Byte 27 */
- unsigned char Reserved[4]; /* Bytes 28-31 */
- unsigned int OS_Reserved; /* Bytes 32-35 */
- unsigned char TranslationInfo[4]; /* Bytes 36-39 */
- unsigned int Reserved2[5]; /* Bytes 40-59 */
- unsigned int SecondaryRange; /* Bytes 60-63 */
+struct fpoint_info {
+ u32 base_addr; /* Bytes 0-3 */
+ bool present; /* Byte 4 */
+ unsigned char irq_ch; /* Byte 5 */
+ unsigned char scsi_id; /* Byte 6 */
+ unsigned char scsi_lun; /* Byte 7 */
+ u16 fw_rev; /* Bytes 8-9 */
+ u16 sync_ok; /* Bytes 10-11 */
+ u16 fast_ok; /* Bytes 12-13 */
+ u16 ultra_ok; /* Bytes 14-15 */
+ u16 discon_ok; /* Bytes 16-17 */
+ u16 wide_ok; /* Bytes 18-19 */
+ bool parity:1; /* Byte 20 Bit 0 */
+ bool wide:1; /* Byte 20 Bit 1 */
+ bool softreset:1; /* Byte 20 Bit 2 */
+ bool ext_trans_enable:1; /* Byte 20 Bit 3 */
+ bool low_term:1; /* Byte 20 Bit 4 */
+ bool high_term:1; /* Byte 20 Bit 5 */
+ bool report_underrun:1; /* Byte 20 Bit 6 */
+ bool scam_enabled:1; /* Byte 20 Bit 7 */
+ bool scam_lev2:1; /* Byte 21 Bit 0 */
+ unsigned char:7; /* Byte 21 Bits 1-7 */
+ unsigned char family; /* Byte 22 */
+ unsigned char bus_type; /* Byte 23 */
+ unsigned char model[3]; /* Bytes 24-26 */
+ unsigned char relative_cardnum; /* Byte 27 */
+ unsigned char rsvd[4]; /* Bytes 28-31 */
+ u32 os_rsvd; /* Bytes 32-35 */
+ unsigned char translation_info[4]; /* Bytes 36-39 */
+ u32 rsvd2[5]; /* Bytes 40-59 */
+ u32 sec_range; /* Bytes 60-63 */
};
/*
Define the BusLogic Driver Host Adapter structure.
*/
-struct BusLogic_HostAdapter {
- struct Scsi_Host *SCSI_Host;
- struct pci_dev *PCI_Device;
- enum BusLogic_HostAdapterType HostAdapterType;
- enum BusLogic_HostAdapterBusType HostAdapterBusType;
- unsigned long IO_Address;
- unsigned long PCI_Address;
- unsigned short AddressCount;
- unsigned char HostNumber;
- unsigned char ModelName[9];
- unsigned char FirmwareVersion[6];
- unsigned char FullModelName[18];
- unsigned char Bus;
- unsigned char Device;
- unsigned char IRQ_Channel;
- unsigned char DMA_Channel;
- unsigned char SCSI_ID;
- bool IRQ_ChannelAcquired:1;
- bool DMA_ChannelAcquired:1;
- bool ExtendedTranslationEnabled:1;
- bool ParityCheckingEnabled:1;
- bool BusResetEnabled:1;
- bool LevelSensitiveInterrupt:1;
- bool HostWideSCSI:1;
- bool HostDifferentialSCSI:1;
- bool HostSupportsSCAM:1;
- bool HostUltraSCSI:1;
- bool ExtendedLUNSupport:1;
- bool TerminationInfoValid:1;
- bool LowByteTerminated:1;
- bool HighByteTerminated:1;
- bool BounceBuffersRequired:1;
- bool StrictRoundRobinModeSupport:1;
- bool SCAM_Enabled:1;
- bool SCAM_Level2:1;
- bool HostAdapterInitialized:1;
- bool HostAdapterExternalReset:1;
- bool HostAdapterInternalError:1;
- bool ProcessCompletedCCBsActive;
- volatile bool HostAdapterCommandCompleted;
- unsigned short HostAdapterScatterGatherLimit;
- unsigned short DriverScatterGatherLimit;
- unsigned short MaxTargetDevices;
- unsigned short MaxLogicalUnits;
- unsigned short MailboxCount;
- unsigned short InitialCCBs;
- unsigned short IncrementalCCBs;
- unsigned short AllocatedCCBs;
- unsigned short DriverQueueDepth;
- unsigned short HostAdapterQueueDepth;
- unsigned short UntaggedQueueDepth;
- unsigned short CommonQueueDepth;
- unsigned short BusSettleTime;
- unsigned short SynchronousPermitted;
- unsigned short FastPermitted;
- unsigned short UltraPermitted;
- unsigned short WidePermitted;
- unsigned short DisconnectPermitted;
- unsigned short TaggedQueuingPermitted;
- unsigned short ExternalHostAdapterResets;
- unsigned short HostAdapterInternalErrors;
- unsigned short TargetDeviceCount;
- unsigned short MessageBufferLength;
- u32 BIOS_Address;
- struct BusLogic_DriverOptions *DriverOptions;
- struct FlashPoint_Info FlashPointInfo;
- FlashPoint_CardHandle_T CardHandle;
+struct blogic_adapter {
+ struct Scsi_Host *scsi_host;
+ struct pci_dev *pci_device;
+ enum blogic_adapter_type adapter_type;
+ enum blogic_adapter_bus_type adapter_bus_type;
+ unsigned long io_addr;
+ unsigned long pci_addr;
+ unsigned short addr_count;
+ unsigned char host_no;
+ unsigned char model[9];
+ unsigned char fw_ver[6];
+ unsigned char full_model[18];
+ unsigned char bus;
+ unsigned char dev;
+ unsigned char irq_ch;
+ unsigned char scsi_id;
+ bool irq_acquired:1;
+ bool ext_trans_enable:1;
+ bool parity:1;
+ bool reset_enabled:1;
+ bool level_int:1;
+ bool wide:1;
+ bool differential:1;
+ bool scam:1;
+ bool ultra:1;
+ bool ext_lun:1;
+ bool terminfo_valid:1;
+ bool low_term:1;
+ bool high_term:1;
+ bool strict_rr:1;
+ bool scam_enabled:1;
+ bool scam_lev2:1;
+ bool adapter_initd:1;
+ bool adapter_extreset:1;
+ bool adapter_intern_err:1;
+ bool processing_ccbs;
+ volatile bool adapter_cmd_complete;
+ unsigned short adapter_sglimit;
+ unsigned short drvr_sglimit;
+ unsigned short maxdev;
+ unsigned short maxlun;
+ unsigned short mbox_count;
+ unsigned short initccbs;
+ unsigned short inc_ccbs;
+ unsigned short alloc_ccbs;
+ unsigned short drvr_qdepth;
+ unsigned short adapter_qdepth;
+ unsigned short untag_qdepth;
+ unsigned short common_qdepth;
+ unsigned short bus_settle_time;
+ unsigned short sync_ok;
+ unsigned short fast_ok;
+ unsigned short ultra_ok;
+ unsigned short wide_ok;
+ unsigned short discon_ok;
+ unsigned short tagq_ok;
+ unsigned short ext_resets;
+ unsigned short adapter_intern_errors;
+ unsigned short tgt_count;
+ unsigned short msgbuflen;
+ u32 bios_addr;
+ struct blogic_drvr_options *drvr_opts;
+ struct fpoint_info fpinfo;
+ void *cardhandle;
struct list_head host_list;
- struct BusLogic_CCB *All_CCBs;
- struct BusLogic_CCB *Free_CCBs;
- struct BusLogic_CCB *FirstCompletedCCB;
- struct BusLogic_CCB *LastCompletedCCB;
- struct BusLogic_CCB *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices];
- struct BusLogic_TargetFlags TargetFlags[BusLogic_MaxTargetDevices];
- unsigned char QueueDepth[BusLogic_MaxTargetDevices];
- unsigned char SynchronousPeriod[BusLogic_MaxTargetDevices];
- unsigned char SynchronousOffset[BusLogic_MaxTargetDevices];
- unsigned char ActiveCommands[BusLogic_MaxTargetDevices];
- unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices];
- unsigned long LastSequencePoint[BusLogic_MaxTargetDevices];
- unsigned long LastResetAttempted[BusLogic_MaxTargetDevices];
- unsigned long LastResetCompleted[BusLogic_MaxTargetDevices];
- struct BusLogic_OutgoingMailbox *FirstOutgoingMailbox;
- struct BusLogic_OutgoingMailbox *LastOutgoingMailbox;
- struct BusLogic_OutgoingMailbox *NextOutgoingMailbox;
- struct BusLogic_IncomingMailbox *FirstIncomingMailbox;
- struct BusLogic_IncomingMailbox *LastIncomingMailbox;
- struct BusLogic_IncomingMailbox *NextIncomingMailbox;
- struct BusLogic_TargetStatistics TargetStatistics[BusLogic_MaxTargetDevices];
- unsigned char *MailboxSpace;
- dma_addr_t MailboxSpaceHandle;
- unsigned int MailboxSize;
- unsigned long CCB_Offset;
- char MessageBuffer[BusLogic_MessageBufferSize];
+ struct blogic_ccb *all_ccbs;
+ struct blogic_ccb *free_ccbs;
+ struct blogic_ccb *firstccb;
+ struct blogic_ccb *lastccb;
+ struct blogic_ccb *bdr_pend[BLOGIC_MAXDEV];
+ struct blogic_tgt_flags tgt_flags[BLOGIC_MAXDEV];
+ unsigned char qdepth[BLOGIC_MAXDEV];
+ unsigned char sync_period[BLOGIC_MAXDEV];
+ unsigned char sync_offset[BLOGIC_MAXDEV];
+ unsigned char active_cmds[BLOGIC_MAXDEV];
+ unsigned int cmds_since_rst[BLOGIC_MAXDEV];
+ unsigned long last_seqpoint[BLOGIC_MAXDEV];
+ unsigned long last_resettried[BLOGIC_MAXDEV];
+ unsigned long last_resetdone[BLOGIC_MAXDEV];
+ struct blogic_outbox *first_outbox;
+ struct blogic_outbox *last_outbox;
+ struct blogic_outbox *next_outbox;
+ struct blogic_inbox *first_inbox;
+ struct blogic_inbox *last_inbox;
+ struct blogic_inbox *next_inbox;
+ struct blogic_tgt_stats tgt_stats[BLOGIC_MAXDEV];
+ unsigned char *mbox_space;
+ dma_addr_t mbox_space_handle;
+ unsigned int mbox_sz;
+ unsigned long ccb_offset;
+ char msgbuf[BLOGIC_MSGBUF_SIZE];
};
/*
Define a structure for the BIOS Disk Parameters.
*/
-struct BIOS_DiskParameters {
- int Heads;
- int Sectors;
- int Cylinders;
+struct bios_diskparam {
+ int heads;
+ int sectors;
+ int cylinders;
};
/*
Define a structure for the SCSI Inquiry command results.
*/
-struct SCSI_Inquiry {
- unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */
- unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */
- unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */
- bool RMB:1; /* Byte 1 Bit 7 */
- unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */
- unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */
- unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */
- unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */
- unsigned char:2; /* Byte 3 Bits 4-5 */
- bool TrmIOP:1; /* Byte 3 Bit 6 */
- bool AENC:1; /* Byte 3 Bit 7 */
- unsigned char AdditionalLength; /* Byte 4 */
- unsigned char:8; /* Byte 5 */
- unsigned char:8; /* Byte 6 */
- bool SftRe:1; /* Byte 7 Bit 0 */
- bool CmdQue:1; /* Byte 7 Bit 1 */
- bool:1; /* Byte 7 Bit 2 */
- bool Linked:1; /* Byte 7 Bit 3 */
- bool Sync:1; /* Byte 7 Bit 4 */
- bool WBus16:1; /* Byte 7 Bit 5 */
- bool WBus32:1; /* Byte 7 Bit 6 */
- bool RelAdr:1; /* Byte 7 Bit 7 */
- unsigned char VendorIdentification[8]; /* Bytes 8-15 */
- unsigned char ProductIdentification[16]; /* Bytes 16-31 */
- unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */
+struct scsi_inquiry {
+ unsigned char devtype:5; /* Byte 0 Bits 0-4 */
+ unsigned char dev_qual:3; /* Byte 0 Bits 5-7 */
+ unsigned char dev_modifier:7; /* Byte 1 Bits 0-6 */
+ bool rmb:1; /* Byte 1 Bit 7 */
+ unsigned char ansi_ver:3; /* Byte 2 Bits 0-2 */
+ unsigned char ecma_ver:3; /* Byte 2 Bits 3-5 */
+ unsigned char iso_ver:2; /* Byte 2 Bits 6-7 */
+ unsigned char resp_fmt:4; /* Byte 3 Bits 0-3 */
+ unsigned char:2; /* Byte 3 Bits 4-5 */
+ bool TrmIOP:1; /* Byte 3 Bit 6 */
+ bool AENC:1; /* Byte 3 Bit 7 */
+ unsigned char addl_len; /* Byte 4 */
+ unsigned char:8; /* Byte 5 */
+ unsigned char:8; /* Byte 6 */
+ bool SftRe:1; /* Byte 7 Bit 0 */
+ bool CmdQue:1; /* Byte 7 Bit 1 */
+ bool:1; /* Byte 7 Bit 2 */
+ bool linked:1; /* Byte 7 Bit 3 */
+ bool sync:1; /* Byte 7 Bit 4 */
+ bool WBus16:1; /* Byte 7 Bit 5 */
+ bool WBus32:1; /* Byte 7 Bit 6 */
+ bool RelAdr:1; /* Byte 7 Bit 7 */
+ unsigned char vendor[8]; /* Bytes 8-15 */
+ unsigned char product[16]; /* Bytes 16-31 */
+ unsigned char product_rev[4]; /* Bytes 32-35 */
};
@@ -1148,185 +1115,171 @@ struct SCSI_Inquiry {
Host Adapter I/O Registers.
*/
-static inline void BusLogic_SCSIBusReset(struct BusLogic_HostAdapter *HostAdapter)
+static inline void blogic_busreset(struct blogic_adapter *adapter)
{
- union BusLogic_ControlRegister ControlRegister;
- ControlRegister.All = 0;
- ControlRegister.cr.SCSIBusReset = true;
- outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+ union blogic_cntrl_reg cr;
+ cr.all = 0;
+ cr.cr.bus_reset = true;
+ outb(cr.all, adapter->io_addr + BLOGIC_CNTRL_REG);
}
-static inline void BusLogic_InterruptReset(struct BusLogic_HostAdapter *HostAdapter)
+static inline void blogic_intreset(struct blogic_adapter *adapter)
{
- union BusLogic_ControlRegister ControlRegister;
- ControlRegister.All = 0;
- ControlRegister.cr.InterruptReset = true;
- outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+ union blogic_cntrl_reg cr;
+ cr.all = 0;
+ cr.cr.int_reset = true;
+ outb(cr.all, adapter->io_addr + BLOGIC_CNTRL_REG);
}
-static inline void BusLogic_SoftReset(struct BusLogic_HostAdapter *HostAdapter)
+static inline void blogic_softreset(struct blogic_adapter *adapter)
{
- union BusLogic_ControlRegister ControlRegister;
- ControlRegister.All = 0;
- ControlRegister.cr.SoftReset = true;
- outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+ union blogic_cntrl_reg cr;
+ cr.all = 0;
+ cr.cr.soft_reset = true;
+ outb(cr.all, adapter->io_addr + BLOGIC_CNTRL_REG);
}
-static inline void BusLogic_HardReset(struct BusLogic_HostAdapter *HostAdapter)
+static inline void blogic_hardreset(struct blogic_adapter *adapter)
{
- union BusLogic_ControlRegister ControlRegister;
- ControlRegister.All = 0;
- ControlRegister.cr.HardReset = true;
- outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+ union blogic_cntrl_reg cr;
+ cr.all = 0;
+ cr.cr.hard_reset = true;
+ outb(cr.all, adapter->io_addr + BLOGIC_CNTRL_REG);
}
-static inline unsigned char BusLogic_ReadStatusRegister(struct BusLogic_HostAdapter *HostAdapter)
+static inline unsigned char blogic_rdstatus(struct blogic_adapter *adapter)
{
- return inb(HostAdapter->IO_Address + BusLogic_StatusRegisterOffset);
+ return inb(adapter->io_addr + BLOGIC_STATUS_REG);
}
-static inline void BusLogic_WriteCommandParameterRegister(struct BusLogic_HostAdapter
- *HostAdapter, unsigned char Value)
+static inline void blogic_setcmdparam(struct blogic_adapter *adapter,
+ unsigned char value)
{
- outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegisterOffset);
+ outb(value, adapter->io_addr + BLOGIC_CMD_PARM_REG);
}
-static inline unsigned char BusLogic_ReadDataInRegister(struct BusLogic_HostAdapter *HostAdapter)
+static inline unsigned char blogic_rddatain(struct blogic_adapter *adapter)
{
- return inb(HostAdapter->IO_Address + BusLogic_DataInRegisterOffset);
+ return inb(adapter->io_addr + BLOGIC_DATAIN_REG);
}
-static inline unsigned char BusLogic_ReadInterruptRegister(struct BusLogic_HostAdapter *HostAdapter)
+static inline unsigned char blogic_rdint(struct blogic_adapter *adapter)
{
- return inb(HostAdapter->IO_Address + BusLogic_InterruptRegisterOffset);
+ return inb(adapter->io_addr + BLOGIC_INT_REG);
}
-static inline unsigned char BusLogic_ReadGeometryRegister(struct BusLogic_HostAdapter *HostAdapter)
+static inline unsigned char blogic_rdgeom(struct blogic_adapter *adapter)
{
- return inb(HostAdapter->IO_Address + BusLogic_GeometryRegisterOffset);
+ return inb(adapter->io_addr + BLOGIC_GEOMETRY_REG);
}
/*
- BusLogic_StartMailboxCommand issues an Execute Mailbox Command, which
+ blogic_execmbox issues an Execute Mailbox Command, which
notifies the Host Adapter that an entry has been made in an Outgoing
Mailbox.
*/
-static inline void BusLogic_StartMailboxCommand(struct BusLogic_HostAdapter *HostAdapter)
+static inline void blogic_execmbox(struct blogic_adapter *adapter)
{
- BusLogic_WriteCommandParameterRegister(HostAdapter, BusLogic_ExecuteMailboxCommand);
+ blogic_setcmdparam(adapter, BLOGIC_EXEC_MBOX_CMD);
}
/*
- BusLogic_Delay waits for Seconds to elapse.
+ blogic_delay waits for Seconds to elapse.
*/
-static inline void BusLogic_Delay(int Seconds)
-{
- mdelay(1000 * Seconds);
-}
-
-/*
- Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses
- and PCI/VLB/EISA/ISA Bus Addresses.
-*/
-
-static inline u32 Virtual_to_Bus(void *VirtualAddress)
-{
- return (u32) virt_to_bus(VirtualAddress);
-}
-
-static inline void *Bus_to_Virtual(u32 BusAddress)
+static inline void blogic_delay(int seconds)
{
- return (void *) bus_to_virt(BusAddress);
+ mdelay(1000 * seconds);
}
/*
- Virtual_to_32Bit_Virtual maps between Kernel Virtual Addresses and
+ virt_to_32bit_virt maps between Kernel Virtual Addresses and
32 bit Kernel Virtual Addresses. This avoids compilation warnings
on 64 bit architectures.
*/
-static inline u32 Virtual_to_32Bit_Virtual(void *VirtualAddress)
+static inline u32 virt_to_32bit_virt(void *virt_addr)
{
- return (u32) (unsigned long) VirtualAddress;
+ return (u32) (unsigned long) virt_addr;
}
/*
- BusLogic_IncrementErrorCounter increments Error Counter by 1, stopping at
+ blogic_inc_count increments counter by 1, stopping at
65535 rather than wrapping around to 0.
*/
-static inline void BusLogic_IncrementErrorCounter(unsigned short *ErrorCounter)
+static inline void blogic_inc_count(unsigned short *count)
{
- if (*ErrorCounter < 65535)
- (*ErrorCounter)++;
+ if (*count < 65535)
+ (*count)++;
}
/*
- BusLogic_IncrementByteCounter increments Byte Counter by Amount.
+ blogic_addcount increments Byte Counter by Amount.
*/
-static inline void BusLogic_IncrementByteCounter(struct BusLogic_ByteCounter
- *ByteCounter, unsigned int Amount)
+static inline void blogic_addcount(struct blogic_byte_count *bytecount,
+ unsigned int amount)
{
- ByteCounter->Units += Amount;
- if (ByteCounter->Units > 999999999) {
- ByteCounter->Units -= 1000000000;
- ByteCounter->Billions++;
+ bytecount->units += amount;
+ if (bytecount->units > 999999999) {
+ bytecount->units -= 1000000000;
+ bytecount->billions++;
}
}
/*
- BusLogic_IncrementSizeBucket increments the Bucket for Amount.
+ blogic_incszbucket increments the Bucket for Amount.
*/
-static inline void BusLogic_IncrementSizeBucket(BusLogic_CommandSizeBuckets_T CommandSizeBuckets, unsigned int Amount)
+static inline void blogic_incszbucket(unsigned int *cmdsz_buckets,
+ unsigned int amount)
{
- int Index = 0;
- if (Amount < 8 * 1024) {
- if (Amount < 2 * 1024)
- Index = (Amount < 1 * 1024 ? 0 : 1);
+ int index = 0;
+ if (amount < 8 * 1024) {
+ if (amount < 2 * 1024)
+ index = (amount < 1 * 1024 ? 0 : 1);
else
- Index = (Amount < 4 * 1024 ? 2 : 3);
- } else if (Amount < 128 * 1024) {
- if (Amount < 32 * 1024)
- Index = (Amount < 16 * 1024 ? 4 : 5);
+ index = (amount < 4 * 1024 ? 2 : 3);
+ } else if (amount < 128 * 1024) {
+ if (amount < 32 * 1024)
+ index = (amount < 16 * 1024 ? 4 : 5);
else
- Index = (Amount < 64 * 1024 ? 6 : 7);
+ index = (amount < 64 * 1024 ? 6 : 7);
} else
- Index = (Amount < 256 * 1024 ? 8 : 9);
- CommandSizeBuckets[Index]++;
+ index = (amount < 256 * 1024 ? 8 : 9);
+ cmdsz_buckets[index]++;
}
/*
Define the version number of the FlashPoint Firmware (SCCB Manager).
*/
-#define FlashPoint_FirmwareVersion "5.02"
+#define FLASHPOINT_FW_VER "5.02"
/*
Define the possible return values from FlashPoint_HandleInterrupt.
*/
-#define FlashPoint_NormalInterrupt 0x00
-#define FlashPoint_InternalError 0xFE
-#define FlashPoint_ExternalBusReset 0xFF
+#define FPOINT_NORMAL_INT 0x00
+#define FPOINT_INTERN_ERR 0xFE
+#define FPOINT_EXT_RESET 0xFF
/*
Define prototypes for the forward referenced BusLogic Driver
Internal Functions.
*/
-static const char *BusLogic_DriverInfo(struct Scsi_Host *);
-static int BusLogic_QueueCommand(struct Scsi_Host *h, struct scsi_cmnd *);
-static int BusLogic_BIOSDiskParameters(struct scsi_device *, struct block_device *, sector_t, int *);
-static int BusLogic_ProcDirectoryInfo(struct Scsi_Host *, char *, char **, off_t, int, int);
-static int BusLogic_SlaveConfigure(struct scsi_device *);
-static void BusLogic_QueueCompletedCCB(struct BusLogic_CCB *);
-static irqreturn_t BusLogic_InterruptHandler(int, void *);
-static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *, bool HardReset);
-static void BusLogic_Message(enum BusLogic_MessageLevel, char *, struct BusLogic_HostAdapter *, ...);
-static int __init BusLogic_Setup(char *);
+static const char *blogic_drvr_info(struct Scsi_Host *);
+static int blogic_qcmd(struct Scsi_Host *h, struct scsi_cmnd *);
+static int blogic_diskparam(struct scsi_device *, struct gendisk *, sector_t, int *);
+static int blogic_sdev_configure(struct scsi_device *,
+ struct queue_limits *lim);
+static void blogic_qcompleted_ccb(struct blogic_ccb *);
+static irqreturn_t blogic_inthandler(int, void *);
+static int blogic_resetadapter(struct blogic_adapter *, bool hard_reset);
+static void blogic_msg(enum blogic_msglevel, char *, struct blogic_adapter *, ...);
+static int __init blogic_setup(char *);
#endif /* _BUSLOGIC_H */
diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c
index dcd716d68600..9e77b8e1ea7c 100644
--- a/drivers/scsi/FlashPoint.c
+++ b/drivers/scsi/FlashPoint.c
@@ -29,27 +29,27 @@ struct sccb;
typedef void (*CALL_BK_FN) (struct sccb *);
struct sccb_mgr_info {
- unsigned long si_baseaddr;
+ u32 si_baseaddr;
unsigned char si_present;
unsigned char si_intvect;
unsigned char si_id;
unsigned char si_lun;
- unsigned short si_fw_revision;
- unsigned short si_per_targ_init_sync;
- unsigned short si_per_targ_fast_nego;
- unsigned short si_per_targ_ultra_nego;
- unsigned short si_per_targ_no_disc;
- unsigned short si_per_targ_wide_nego;
- unsigned short si_flags;
+ u16 si_fw_revision;
+ u16 si_per_targ_init_sync;
+ u16 si_per_targ_fast_nego;
+ u16 si_per_targ_ultra_nego;
+ u16 si_per_targ_no_disc;
+ u16 si_per_targ_wide_nego;
+ u16 si_mflags;
unsigned char si_card_family;
unsigned char si_bustype;
unsigned char si_card_model[3];
unsigned char si_relative_cardnum;
unsigned char si_reserved[4];
- unsigned long si_OS_reserved;
+ u32 si_OS_reserved;
unsigned char si_XlatInfo[4];
- unsigned long si_reserved2[5];
- unsigned long si_secondary_range;
+ u32 si_reserved2[5];
+ u32 si_secondary_range;
};
#define SCSI_PARITY_ENA 0x0001
@@ -70,14 +70,14 @@ struct sccb_mgr_info {
* The UCB Manager treats the SCCB as it's 'native hardware structure'
*/
-#pragma pack(1)
+/*#pragma pack(1)*/
struct sccb {
unsigned char OperationCode;
unsigned char ControlByte;
unsigned char CdbLength;
unsigned char RequestSenseLength;
- unsigned long DataLength;
- unsigned long DataPointer;
+ u32 DataLength;
+ void *DataPointer;
unsigned char CcbRes[2];
unsigned char HostStatus;
unsigned char TargetStatus;
@@ -86,32 +86,32 @@ struct sccb {
unsigned char Cdb[12];
unsigned char CcbRes1;
unsigned char Reserved1;
- unsigned long Reserved2;
- unsigned long SensePointer;
+ u32 Reserved2;
+ u32 SensePointer;
CALL_BK_FN SccbCallback; /* VOID (*SccbCallback)(); */
- unsigned long SccbIOPort; /* Identifies board base port */
+ u32 SccbIOPort; /* Identifies board base port */
unsigned char SccbStatus;
unsigned char SCCBRes2;
- unsigned short SccbOSFlags;
-
- unsigned long Sccb_XferCnt; /* actual transfer count */
- unsigned long Sccb_ATC;
- unsigned long SccbVirtDataPtr; /* virtual addr for OS/2 */
- unsigned long Sccb_res1;
- unsigned short Sccb_MGRFlags;
- unsigned short Sccb_sgseg;
+ u16 SccbOSFlags;
+
+ u32 Sccb_XferCnt; /* actual transfer count */
+ u32 Sccb_ATC;
+ u32 SccbVirtDataPtr; /* virtual addr for OS/2 */
+ u32 Sccb_res1;
+ u16 Sccb_MGRFlags;
+ u16 Sccb_sgseg;
unsigned char Sccb_scsimsg; /* identify msg for selection */
unsigned char Sccb_tag;
unsigned char Sccb_scsistat;
unsigned char Sccb_idmsg; /* image of last msg in */
struct sccb *Sccb_forwardlink;
struct sccb *Sccb_backlink;
- unsigned long Sccb_savedATC;
+ u32 Sccb_savedATC;
unsigned char Save_Cdb[6];
unsigned char Save_CdbLen;
unsigned char Sccb_XferState;
- unsigned long Sccb_SGoffset;
+ u32 Sccb_SGoffset;
};
#pragma pack()
@@ -223,15 +223,21 @@ struct sccb_mgr_tar_info {
};
struct nvram_info {
- unsigned char niModel; /* Model No. of card */
- unsigned char niCardNo; /* Card no. */
- unsigned long niBaseAddr; /* Port Address of card */
- unsigned char niSysConf; /* Adapter Configuration byte - Byte 16 of eeprom map */
- unsigned char niScsiConf; /* SCSI Configuration byte - Byte 17 of eeprom map */
- unsigned char niScamConf; /* SCAM Configuration byte - Byte 20 of eeprom map */
- unsigned char niAdapId; /* Host Adapter ID - Byte 24 of eerpom map */
- unsigned char niSyncTbl[MAX_SCSI_TAR / 2]; /* Sync/Wide byte of targets */
- unsigned char niScamTbl[MAX_SCSI_TAR][4]; /* Compressed Scam name string of Targets */
+ unsigned char niModel; /* Model No. of card */
+ unsigned char niCardNo; /* Card no. */
+ u32 niBaseAddr; /* Port Address of card */
+ unsigned char niSysConf; /* Adapter Configuration byte -
+ Byte 16 of eeprom map */
+ unsigned char niScsiConf; /* SCSI Configuration byte -
+ Byte 17 of eeprom map */
+ unsigned char niScamConf; /* SCAM Configuration byte -
+ Byte 20 of eeprom map */
+ unsigned char niAdapId; /* Host Adapter ID -
+ Byte 24 of eerpom map */
+ unsigned char niSyncTbl[MAX_SCSI_TAR / 2]; /* Sync/Wide byte
+ of targets */
+ unsigned char niScamTbl[MAX_SCSI_TAR][4]; /* Compressed Scam name
+ string of Targets */
};
#define MODEL_LT 1
@@ -243,7 +249,7 @@ struct sccb_card {
struct sccb *currentSCCB;
struct sccb_mgr_info *cardInfo;
- unsigned long ioPort;
+ u32 ioPort;
unsigned short cmdCounter;
unsigned char discQCount;
@@ -298,40 +304,12 @@ typedef struct SCCBscam_info {
} SCCBSCAM_INFO;
-#define SCSI_REQUEST_SENSE 0x03
-#define SCSI_READ 0x08
-#define SCSI_WRITE 0x0A
-#define SCSI_START_STOP_UNIT 0x1B
-#define SCSI_READ_EXTENDED 0x28
-#define SCSI_WRITE_EXTENDED 0x2A
-#define SCSI_WRITE_AND_VERIFY 0x2E
-
-#define SSGOOD 0x00
-#define SSCHECK 0x02
-#define SSQ_FULL 0x28
-
-#define SMCMD_COMP 0x00
-#define SMEXT 0x01
-#define SMSAVE_DATA_PTR 0x02
-#define SMREST_DATA_PTR 0x03
-#define SMDISC 0x04
-#define SMABORT 0x06
-#define SMREJECT 0x07
-#define SMNO_OP 0x08
-#define SMPARITY 0x09
-#define SMDEV_RESET 0x0C
-#define SMABORT_TAG 0x0D
-#define SMINIT_RECOVERY 0x0F
-#define SMREL_RECOVERY 0x10
#define SMIDENT 0x80
#define DISC_PRIV 0x40
-#define SMSYNC 0x01
-#define SMWDTR 0x03
#define SM8BIT 0x00
#define SM16BIT 0x01
-#define SMIGNORWR 0x23 /* Ignore Wide Residue */
#define SIX_BYTE_CMD 0x06
#define TWELVE_BYTE_CMD 0x0C
@@ -780,37 +758,37 @@ typedef struct SCCBscam_info {
#define MENABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
(RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE)))
-static unsigned char FPT_sisyncn(unsigned long port, unsigned char p_card,
+static unsigned char FPT_sisyncn(u32 port, unsigned char p_card,
unsigned char syncFlag);
-static void FPT_ssel(unsigned long port, unsigned char p_card);
-static void FPT_sres(unsigned long port, unsigned char p_card,
+static void FPT_ssel(u32 port, unsigned char p_card);
+static void FPT_sres(u32 port, unsigned char p_card,
struct sccb_card *pCurrCard);
-static void FPT_shandem(unsigned long port, unsigned char p_card,
+static void FPT_shandem(u32 port, unsigned char p_card,
struct sccb *pCurrSCCB);
-static void FPT_stsyncn(unsigned long port, unsigned char p_card);
-static void FPT_sisyncr(unsigned long port, unsigned char sync_pulse,
+static void FPT_stsyncn(u32 port, unsigned char p_card);
+static void FPT_sisyncr(u32 port, unsigned char sync_pulse,
unsigned char offset);
-static void FPT_sssyncv(unsigned long p_port, unsigned char p_id,
+static void FPT_sssyncv(u32 p_port, unsigned char p_id,
unsigned char p_sync_value,
struct sccb_mgr_tar_info *currTar_Info);
-static void FPT_sresb(unsigned long port, unsigned char p_card);
-static void FPT_sxfrp(unsigned long p_port, unsigned char p_card);
-static void FPT_schkdd(unsigned long port, unsigned char p_card);
-static unsigned char FPT_RdStack(unsigned long port, unsigned char index);
-static void FPT_WrStack(unsigned long portBase, unsigned char index,
+static void FPT_sresb(u32 port, unsigned char p_card);
+static void FPT_sxfrp(u32 p_port, unsigned char p_card);
+static void FPT_schkdd(u32 port, unsigned char p_card);
+static unsigned char FPT_RdStack(u32 port, unsigned char index);
+static void FPT_WrStack(u32 portBase, unsigned char index,
unsigned char data);
-static unsigned char FPT_ChkIfChipInitialized(unsigned long ioPort);
+static unsigned char FPT_ChkIfChipInitialized(u32 ioPort);
-static void FPT_SendMsg(unsigned long port, unsigned char message);
+static void FPT_SendMsg(u32 port, unsigned char message);
static void FPT_queueFlushTargSccb(unsigned char p_card, unsigned char thisTarg,
unsigned char error_code);
static void FPT_sinits(struct sccb *p_sccb, unsigned char p_card);
static void FPT_RNVRamData(struct nvram_info *pNvRamInfo);
-static unsigned char FPT_siwidn(unsigned long port, unsigned char p_card);
-static void FPT_stwidn(unsigned long port, unsigned char p_card);
-static void FPT_siwidr(unsigned long port, unsigned char width);
+static unsigned char FPT_siwidn(u32 port, unsigned char p_card);
+static void FPT_stwidn(u32 port, unsigned char p_card);
+static void FPT_siwidr(u32 port, unsigned char width);
static void FPT_queueSelectFail(struct sccb_card *pCurrCard,
unsigned char p_card);
@@ -827,45 +805,45 @@ static void FPT_utilUpdateResidual(struct sccb *p_SCCB);
static unsigned short FPT_CalcCrc16(unsigned char buffer[]);
static unsigned char FPT_CalcLrc(unsigned char buffer[]);
-static void FPT_Wait1Second(unsigned long p_port);
-static void FPT_Wait(unsigned long p_port, unsigned char p_delay);
-static void FPT_utilEEWriteOnOff(unsigned long p_port, unsigned char p_mode);
-static void FPT_utilEEWrite(unsigned long p_port, unsigned short ee_data,
+static void FPT_Wait1Second(u32 p_port);
+static void FPT_Wait(u32 p_port, unsigned char p_delay);
+static void FPT_utilEEWriteOnOff(u32 p_port, unsigned char p_mode);
+static void FPT_utilEEWrite(u32 p_port, unsigned short ee_data,
unsigned short ee_addr);
-static unsigned short FPT_utilEERead(unsigned long p_port,
+static unsigned short FPT_utilEERead(u32 p_port,
unsigned short ee_addr);
-static unsigned short FPT_utilEEReadOrg(unsigned long p_port,
+static unsigned short FPT_utilEEReadOrg(u32 p_port,
unsigned short ee_addr);
-static void FPT_utilEESendCmdAddr(unsigned long p_port, unsigned char ee_cmd,
+static void FPT_utilEESendCmdAddr(u32 p_port, unsigned char ee_cmd,
unsigned short ee_addr);
-static void FPT_phaseDataOut(unsigned long port, unsigned char p_card);
-static void FPT_phaseDataIn(unsigned long port, unsigned char p_card);
-static void FPT_phaseCommand(unsigned long port, unsigned char p_card);
-static void FPT_phaseStatus(unsigned long port, unsigned char p_card);
-static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card);
-static void FPT_phaseMsgIn(unsigned long port, unsigned char p_card);
-static void FPT_phaseIllegal(unsigned long port, unsigned char p_card);
+static void FPT_phaseDataOut(u32 port, unsigned char p_card);
+static void FPT_phaseDataIn(u32 port, unsigned char p_card);
+static void FPT_phaseCommand(u32 port, unsigned char p_card);
+static void FPT_phaseStatus(u32 port, unsigned char p_card);
+static void FPT_phaseMsgOut(u32 port, unsigned char p_card);
+static void FPT_phaseMsgIn(u32 port, unsigned char p_card);
+static void FPT_phaseIllegal(u32 port, unsigned char p_card);
-static void FPT_phaseDecode(unsigned long port, unsigned char p_card);
-static void FPT_phaseChkFifo(unsigned long port, unsigned char p_card);
-static void FPT_phaseBusFree(unsigned long p_port, unsigned char p_card);
+static void FPT_phaseDecode(u32 port, unsigned char p_card);
+static void FPT_phaseChkFifo(u32 port, unsigned char p_card);
+static void FPT_phaseBusFree(u32 p_port, unsigned char p_card);
-static void FPT_XbowInit(unsigned long port, unsigned char scamFlg);
-static void FPT_BusMasterInit(unsigned long p_port);
-static void FPT_DiagEEPROM(unsigned long p_port);
+static void FPT_XbowInit(u32 port, unsigned char scamFlg);
+static void FPT_BusMasterInit(u32 p_port);
+static void FPT_DiagEEPROM(u32 p_port);
-static void FPT_dataXferProcessor(unsigned long port,
+static void FPT_dataXferProcessor(u32 port,
struct sccb_card *pCurrCard);
-static void FPT_busMstrSGDataXferStart(unsigned long port,
+static void FPT_busMstrSGDataXferStart(u32 port,
struct sccb *pCurrSCCB);
-static void FPT_busMstrDataXferStart(unsigned long port,
+static void FPT_busMstrDataXferStart(u32 port,
struct sccb *pCurrSCCB);
-static void FPT_hostDataXferAbort(unsigned long port, unsigned char p_card,
+static void FPT_hostDataXferAbort(u32 port, unsigned char p_card,
struct sccb *pCurrSCCB);
static void FPT_hostDataXferRestart(struct sccb *currSCCB);
-static unsigned char FPT_SccbMgr_bad_isr(unsigned long p_port,
+static unsigned char FPT_SccbMgr_bad_isr(u32 p_port,
unsigned char p_card,
struct sccb_card *pCurrCard,
unsigned short p_int);
@@ -879,28 +857,28 @@ static void FPT_SccbMgrTableInitTarget(unsigned char p_card,
static void FPT_scini(unsigned char p_card, unsigned char p_our_id,
unsigned char p_power_up);
-static int FPT_scarb(unsigned long p_port, unsigned char p_sel_type);
-static void FPT_scbusf(unsigned long p_port);
-static void FPT_scsel(unsigned long p_port);
-static void FPT_scasid(unsigned char p_card, unsigned long p_port);
-static unsigned char FPT_scxferc(unsigned long p_port, unsigned char p_data);
-static unsigned char FPT_scsendi(unsigned long p_port,
+static int FPT_scarb(u32 p_port, unsigned char p_sel_type);
+static void FPT_scbusf(u32 p_port);
+static void FPT_scsel(u32 p_port);
+static void FPT_scasid(unsigned char p_card, u32 p_port);
+static unsigned char FPT_scxferc(u32 p_port, unsigned char p_data);
+static unsigned char FPT_scsendi(u32 p_port,
unsigned char p_id_string[]);
-static unsigned char FPT_sciso(unsigned long p_port,
+static unsigned char FPT_sciso(u32 p_port,
unsigned char p_id_string[]);
-static void FPT_scwirod(unsigned long p_port, unsigned char p_data_bit);
-static void FPT_scwiros(unsigned long p_port, unsigned char p_data_bit);
+static void FPT_scwirod(u32 p_port, unsigned char p_data_bit);
+static void FPT_scwiros(u32 p_port, unsigned char p_data_bit);
static unsigned char FPT_scvalq(unsigned char p_quintet);
-static unsigned char FPT_scsell(unsigned long p_port, unsigned char targ_id);
-static void FPT_scwtsel(unsigned long p_port);
-static void FPT_inisci(unsigned char p_card, unsigned long p_port,
+static unsigned char FPT_scsell(u32 p_port, unsigned char targ_id);
+static void FPT_scwtsel(u32 p_port);
+static void FPT_inisci(unsigned char p_card, u32 p_port,
unsigned char p_our_id);
-static void FPT_scsavdi(unsigned char p_card, unsigned long p_port);
+static void FPT_scsavdi(unsigned char p_card, u32 p_port);
static unsigned char FPT_scmachid(unsigned char p_card,
unsigned char p_id_string[]);
-static void FPT_autoCmdCmplt(unsigned long p_port, unsigned char p_card);
-static void FPT_autoLoadDefaultMap(unsigned long p_port);
+static void FPT_autoCmdCmplt(u32 p_port, unsigned char p_card);
+static void FPT_autoLoadDefaultMap(u32 p_port);
static struct sccb_mgr_tar_info FPT_sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR] =
{ {{0}} };
@@ -918,7 +896,7 @@ static unsigned char FPT_scamHAString[] =
static unsigned short FPT_default_intena = 0;
-static void (*FPT_s_PhaseTbl[8]) (unsigned long, unsigned char) = {
+static void (*FPT_s_PhaseTbl[8]) (u32, unsigned char) = {
0};
/*---------------------------------------------------------------------
@@ -935,7 +913,7 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
unsigned char i, j, id, ScamFlg;
unsigned short temp, temp2, temp3, temp4, temp5, temp6;
- unsigned long ioport;
+ u32 ioport;
struct nvram_info *pCurrNvRam;
ioport = pCardInfo->si_baseaddr;
@@ -1028,11 +1006,14 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
temp6 >>= 1;
switch (temp & 0x3) {
case AUTO_RATE_20: /* Synchronous, 20 mega-transfers/second */
- temp6 |= 0x8000; /* Fall through */
+ temp6 |= 0x8000;
+ fallthrough;
case AUTO_RATE_10: /* Synchronous, 10 mega-transfers/second */
- temp5 |= 0x8000; /* Fall through */
+ temp5 |= 0x8000;
+ fallthrough;
case AUTO_RATE_05: /* Synchronous, 5 mega-transfers/second */
- temp2 |= 0x8000; /* Fall through */
+ temp2 |= 0x8000;
+ fallthrough;
case AUTO_RATE_00: /* Asynchronous */
break;
}
@@ -1064,22 +1045,22 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
ScamFlg =
(unsigned char)FPT_utilEERead(ioport, SCAM_CONFIG / 2);
- pCardInfo->si_flags = 0x0000;
+ pCardInfo->si_mflags = 0x0000;
if (i & 0x01)
- pCardInfo->si_flags |= SCSI_PARITY_ENA;
+ pCardInfo->si_mflags |= SCSI_PARITY_ENA;
if (!(i & 0x02))
- pCardInfo->si_flags |= SOFT_RESET;
+ pCardInfo->si_mflags |= SOFT_RESET;
if (i & 0x10)
- pCardInfo->si_flags |= EXTENDED_TRANSLATION;
+ pCardInfo->si_mflags |= EXTENDED_TRANSLATION;
if (ScamFlg & SCAM_ENABLED)
- pCardInfo->si_flags |= FLAG_SCAM_ENABLED;
+ pCardInfo->si_mflags |= FLAG_SCAM_ENABLED;
if (ScamFlg & SCAM_LEVEL2)
- pCardInfo->si_flags |= FLAG_SCAM_LEVEL2;
+ pCardInfo->si_mflags |= FLAG_SCAM_LEVEL2;
j = (RD_HARPOON(ioport + hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
if (i & 0x04) {
@@ -1095,7 +1076,7 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
if (!(RD_HARPOON(ioport + hp_page_ctrl) & NARROW_SCSI_CARD))
- pCardInfo->si_flags |= SUPPORT_16TAR_32LUN;
+ pCardInfo->si_mflags |= SUPPORT_16TAR_32LUN;
pCardInfo->si_card_family = HARPOON_FAMILY;
pCardInfo->si_bustype = BUSTYPE_PCI;
@@ -1131,15 +1112,15 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
if (pCardInfo->si_card_model[1] == '3') {
if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
- pCardInfo->si_flags |= LOW_BYTE_TERM;
+ pCardInfo->si_mflags |= LOW_BYTE_TERM;
} else if (pCardInfo->si_card_model[2] == '0') {
temp = RD_HARPOON(ioport + hp_xfer_pad);
WR_HARPOON(ioport + hp_xfer_pad, (temp & ~BIT(4)));
if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
- pCardInfo->si_flags |= LOW_BYTE_TERM;
+ pCardInfo->si_mflags |= LOW_BYTE_TERM;
WR_HARPOON(ioport + hp_xfer_pad, (temp | BIT(4)));
if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
- pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ pCardInfo->si_mflags |= HIGH_BYTE_TERM;
WR_HARPOON(ioport + hp_xfer_pad, temp);
} else {
temp = RD_HARPOON(ioport + hp_ee_ctrl);
@@ -1157,9 +1138,9 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
WR_HARPOON(ioport + hp_ee_ctrl, temp);
WR_HARPOON(ioport + hp_xfer_pad, temp2);
if (!(temp3 & BIT(7)))
- pCardInfo->si_flags |= LOW_BYTE_TERM;
+ pCardInfo->si_mflags |= LOW_BYTE_TERM;
if (!(temp3 & BIT(6)))
- pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ pCardInfo->si_mflags |= HIGH_BYTE_TERM;
}
ARAM_ACCESS(ioport);
@@ -1201,23 +1182,21 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
*
*---------------------------------------------------------------------*/
-static unsigned long FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
+static void *FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
*pCardInfo)
{
struct sccb_card *CurrCard = NULL;
struct nvram_info *pCurrNvRam;
unsigned char i, j, thisCard, ScamFlg;
unsigned short temp, sync_bit_map, id;
- unsigned long ioport;
+ u32 ioport;
ioport = pCardInfo->si_baseaddr;
for (thisCard = 0; thisCard <= MAX_CARDS; thisCard++) {
- if (thisCard == MAX_CARDS) {
-
- return FAILURE;
- }
+ if (thisCard == MAX_CARDS)
+ return (void *)FAILURE;
if (FPT_BL_Card[thisCard].ioPort == ioport) {
@@ -1268,7 +1247,7 @@ static unsigned long FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
WR_HARPOON(ioport + hp_arb_id, pCardInfo->si_id);
CurrCard->ourId = pCardInfo->si_id;
- i = (unsigned char)pCardInfo->si_flags;
+ i = (unsigned char)pCardInfo->si_mflags;
if (i & SCSI_PARITY_ENA)
WR_HARPOON(ioport + hp_portctrl_1, (HOST_MODE8 | CHK_SCSI_P));
@@ -1282,14 +1261,14 @@ static unsigned long FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
j |= SCSI_TERM_ENA_H;
WR_HARPOON(ioport + hp_ee_ctrl, j);
- if (!(pCardInfo->si_flags & SOFT_RESET)) {
+ if (!(pCardInfo->si_mflags & SOFT_RESET)) {
FPT_sresb(ioport, thisCard);
FPT_scini(thisCard, pCardInfo->si_id, 0);
}
- if (pCardInfo->si_flags & POST_ALL_UNDERRRUNS)
+ if (pCardInfo->si_mflags & POST_ALL_UNDERRRUNS)
CurrCard->globalFlags |= F_NO_FILTER;
if (pCurrNvRam) {
@@ -1384,16 +1363,16 @@ static unsigned long FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
(unsigned char)(RD_HARPOON((ioport + hp_semaphore)) |
SCCB_MGR_PRESENT));
- return (unsigned long)CurrCard;
+ return (void *)CurrCard;
}
-static void FlashPoint_ReleaseHostAdapter(unsigned long pCurrCard)
+static void FlashPoint_ReleaseHostAdapter(void *pCurrCard)
{
unsigned char i;
- unsigned long portBase;
- unsigned long regOffset;
- unsigned long scamData;
- unsigned long *pScamTbl;
+ u32 portBase;
+ u32 regOffset;
+ u32 scamData;
+ u32 *pScamTbl;
struct nvram_info *pCurrNvRam;
pCurrNvRam = ((struct sccb_card *)pCurrCard)->pNvRamInfo;
@@ -1414,7 +1393,7 @@ static void FlashPoint_ReleaseHostAdapter(unsigned long pCurrCard)
for (i = 0; i < MAX_SCSI_TAR; i++) {
regOffset = hp_aramBase + 64 + i * 4;
- pScamTbl = (unsigned long *)&pCurrNvRam->niScamTbl[i];
+ pScamTbl = (u32 *)&pCurrNvRam->niScamTbl[i];
scamData = *pScamTbl;
WR_HARP32(portBase, regOffset, scamData);
}
@@ -1427,10 +1406,10 @@ static void FlashPoint_ReleaseHostAdapter(unsigned long pCurrCard)
static void FPT_RNVRamData(struct nvram_info *pNvRamInfo)
{
unsigned char i;
- unsigned long portBase;
- unsigned long regOffset;
- unsigned long scamData;
- unsigned long *pScamTbl;
+ u32 portBase;
+ u32 regOffset;
+ u32 scamData;
+ u32 *pScamTbl;
pNvRamInfo->niModel = FPT_RdStack(pNvRamInfo->niBaseAddr, 0);
pNvRamInfo->niSysConf = FPT_RdStack(pNvRamInfo->niBaseAddr, 1);
@@ -1447,26 +1426,25 @@ static void FPT_RNVRamData(struct nvram_info *pNvRamInfo)
for (i = 0; i < MAX_SCSI_TAR; i++) {
regOffset = hp_aramBase + 64 + i * 4;
RD_HARP32(portBase, regOffset, scamData);
- pScamTbl = (unsigned long *)&pNvRamInfo->niScamTbl[i];
+ pScamTbl = (u32 *)&pNvRamInfo->niScamTbl[i];
*pScamTbl = scamData;
}
}
-static unsigned char FPT_RdStack(unsigned long portBase, unsigned char index)
+static unsigned char FPT_RdStack(u32 portBase, unsigned char index)
{
WR_HARPOON(portBase + hp_stack_addr, index);
return RD_HARPOON(portBase + hp_stack_data);
}
-static void FPT_WrStack(unsigned long portBase, unsigned char index,
- unsigned char data)
+static void FPT_WrStack(u32 portBase, unsigned char index, unsigned char data)
{
WR_HARPOON(portBase + hp_stack_addr, index);
WR_HARPOON(portBase + hp_stack_data, data);
}
-static unsigned char FPT_ChkIfChipInitialized(unsigned long ioPort)
+static unsigned char FPT_ChkIfChipInitialized(u32 ioPort)
{
if ((RD_HARPOON(ioPort + hp_arb_id) & 0x0f) != FPT_RdStack(ioPort, 4))
return 0;
@@ -1489,15 +1467,16 @@ static unsigned char FPT_ChkIfChipInitialized(unsigned long ioPort)
* callback function.
*
*---------------------------------------------------------------------*/
-static void FlashPoint_StartCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
+static void FlashPoint_StartCCB(void *curr_card, struct sccb *p_Sccb)
{
- unsigned long ioport;
+ u32 ioport;
unsigned char thisCard, lun;
struct sccb *pSaveSccb;
CALL_BK_FN callback;
+ struct sccb_card *pCurrCard = curr_card;
- thisCard = ((struct sccb_card *)pCurrCard)->cardIndex;
- ioport = ((struct sccb_card *)pCurrCard)->ioPort;
+ thisCard = pCurrCard->cardIndex;
+ ioport = pCurrCard->ioPort;
if ((p_Sccb->TargID >= MAX_SCSI_TAR) || (p_Sccb->Lun >= MAX_LUN)) {
@@ -1512,18 +1491,18 @@ static void FlashPoint_StartCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
FPT_sinits(p_Sccb, thisCard);
- if (!((struct sccb_card *)pCurrCard)->cmdCounter) {
+ if (!pCurrCard->cmdCounter) {
WR_HARPOON(ioport + hp_semaphore,
(RD_HARPOON(ioport + hp_semaphore)
| SCCB_MGR_ACTIVE));
- if (((struct sccb_card *)pCurrCard)->globalFlags & F_GREEN_PC) {
+ if (pCurrCard->globalFlags & F_GREEN_PC) {
WR_HARPOON(ioport + hp_clkctrl_0, CLKCTRL_DEFAULT);
WR_HARPOON(ioport + hp_sys_ctrl, 0x00);
}
}
- ((struct sccb_card *)pCurrCard)->cmdCounter++;
+ pCurrCard->cmdCounter++;
if (RD_HARPOON(ioport + hp_semaphore) & BIOS_IN_USE) {
@@ -1532,10 +1511,10 @@ static void FlashPoint_StartCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
| TICKLE_ME));
if (p_Sccb->OperationCode == RESET_COMMAND) {
pSaveSccb =
- ((struct sccb_card *)pCurrCard)->currentSCCB;
- ((struct sccb_card *)pCurrCard)->currentSCCB = p_Sccb;
+ pCurrCard->currentSCCB;
+ pCurrCard->currentSCCB = p_Sccb;
FPT_queueSelectFail(&FPT_BL_Card[thisCard], thisCard);
- ((struct sccb_card *)pCurrCard)->currentSCCB =
+ pCurrCard->currentSCCB =
pSaveSccb;
} else {
FPT_queueAddSccb(p_Sccb, thisCard);
@@ -1546,10 +1525,10 @@ static void FlashPoint_StartCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
if (p_Sccb->OperationCode == RESET_COMMAND) {
pSaveSccb =
- ((struct sccb_card *)pCurrCard)->currentSCCB;
- ((struct sccb_card *)pCurrCard)->currentSCCB = p_Sccb;
+ pCurrCard->currentSCCB;
+ pCurrCard->currentSCCB = p_Sccb;
FPT_queueSelectFail(&FPT_BL_Card[thisCard], thisCard);
- ((struct sccb_card *)pCurrCard)->currentSCCB =
+ pCurrCard->currentSCCB =
pSaveSccb;
} else {
FPT_queueAddSccb(p_Sccb, thisCard);
@@ -1560,34 +1539,29 @@ static void FlashPoint_StartCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
MDISABLE_INT(ioport);
- if ((((struct sccb_card *)pCurrCard)->globalFlags & F_CONLUN_IO)
- &&
+ if ((pCurrCard->globalFlags & F_CONLUN_IO) &&
((FPT_sccbMgrTbl[thisCard][p_Sccb->TargID].
TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
lun = p_Sccb->Lun;
else
lun = 0;
- if ((((struct sccb_card *)pCurrCard)->currentSCCB == NULL) &&
+ if ((pCurrCard->currentSCCB == NULL) &&
(FPT_sccbMgrTbl[thisCard][p_Sccb->TargID].TarSelQ_Cnt == 0)
&& (FPT_sccbMgrTbl[thisCard][p_Sccb->TargID].TarLUNBusy[lun]
== 0)) {
- ((struct sccb_card *)pCurrCard)->currentSCCB = p_Sccb;
+ pCurrCard->currentSCCB = p_Sccb;
FPT_ssel(p_Sccb->SccbIOPort, thisCard);
}
else {
if (p_Sccb->OperationCode == RESET_COMMAND) {
- pSaveSccb =
- ((struct sccb_card *)pCurrCard)->
- currentSCCB;
- ((struct sccb_card *)pCurrCard)->currentSCCB =
- p_Sccb;
+ pSaveSccb = pCurrCard->currentSCCB;
+ pCurrCard->currentSCCB = p_Sccb;
FPT_queueSelectFail(&FPT_BL_Card[thisCard],
thisCard);
- ((struct sccb_card *)pCurrCard)->currentSCCB =
- pSaveSccb;
+ pCurrCard->currentSCCB = pSaveSccb;
} else {
FPT_queueAddSccb(p_Sccb, thisCard);
}
@@ -1607,13 +1581,12 @@ static void FlashPoint_StartCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
* callback function.
*
*---------------------------------------------------------------------*/
-static int FlashPoint_AbortCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
+static int FlashPoint_AbortCCB(void *pCurrCard, struct sccb *p_Sccb)
{
- unsigned long ioport;
+ u32 ioport;
unsigned char thisCard;
CALL_BK_FN callback;
- unsigned char TID;
struct sccb *pSaveSCCB;
struct sccb_mgr_tar_info *currTar_Info;
@@ -1650,9 +1623,6 @@ static int FlashPoint_AbortCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
}
else {
-
- TID = p_Sccb->TargID;
-
if (p_Sccb->Sccb_tag) {
MDISABLE_INT(ioport);
if (((struct sccb_card *)pCurrCard)->
@@ -1662,7 +1632,7 @@ static int FlashPoint_AbortCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
p_Sccb->Sccb_scsistat =
ABORT_ST;
p_Sccb->Sccb_scsimsg =
- SMABORT_TAG;
+ ABORT_TASK;
if (((struct sccb_card *)
pCurrCard)->currentSCCB ==
@@ -1715,9 +1685,9 @@ static int FlashPoint_AbortCCB(unsigned long pCurrCard, struct sccb *p_Sccb)
* interrupt for this card and disable the IRQ Pin if so.
*
*---------------------------------------------------------------------*/
-static unsigned char FlashPoint_InterruptPending(unsigned long pCurrCard)
+static unsigned char FlashPoint_InterruptPending(void *pCurrCard)
{
- unsigned long ioport;
+ u32 ioport;
ioport = ((struct sccb_card *)pCurrCard)->ioPort;
@@ -1739,38 +1709,36 @@ static unsigned char FlashPoint_InterruptPending(unsigned long pCurrCard)
* us.
*
*---------------------------------------------------------------------*/
-static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
+static int FlashPoint_HandleInterrupt(void *pcard)
{
struct sccb *currSCCB;
- unsigned char thisCard, result, bm_status, bm_int_st;
+ unsigned char thisCard, result, bm_status;
unsigned short hp_int;
unsigned char i, target;
- unsigned long ioport;
+ struct sccb_card *pCurrCard = pcard;
+ u32 ioport;
- thisCard = ((struct sccb_card *)pCurrCard)->cardIndex;
- ioport = ((struct sccb_card *)pCurrCard)->ioPort;
+ thisCard = pCurrCard->cardIndex;
+ ioport = pCurrCard->ioPort;
MDISABLE_INT(ioport);
- if ((bm_int_st = RD_HARPOON(ioport + hp_int_status)) & EXT_STATUS_ON)
- bm_status =
- RD_HARPOON(ioport +
- hp_ext_status) & (unsigned char)BAD_EXT_STATUS;
+ if (RD_HARPOON(ioport + hp_int_status) & EXT_STATUS_ON)
+ bm_status = RD_HARPOON(ioport + hp_ext_status) &
+ (unsigned char)BAD_EXT_STATUS;
else
bm_status = 0;
WR_HARPOON(ioport + hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
- while ((hp_int =
- RDW_HARPOON((ioport +
- hp_intstat)) & FPT_default_intena) | bm_status) {
+ while ((hp_int = RDW_HARPOON((ioport + hp_intstat)) &
+ FPT_default_intena) | bm_status) {
- currSCCB = ((struct sccb_card *)pCurrCard)->currentSCCB;
+ currSCCB = pCurrCard->currentSCCB;
if (hp_int & (FIFO | TIMEOUT | RESET | SCAM_SEL) || bm_status) {
result =
- FPT_SccbMgr_bad_isr(ioport, thisCard,
- ((struct sccb_card *)pCurrCard),
+ FPT_SccbMgr_bad_isr(ioport, thisCard, pCurrCard,
hp_int);
WRW_HARPOON((ioport + hp_intstat),
(FIFO | TIMEOUT | RESET | SCAM_SEL));
@@ -1796,8 +1764,7 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
(BUS_FREE | RSEL))) ;
}
- if (((struct sccb_card *)pCurrCard)->
- globalFlags & F_HOST_XFER_ACT)
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
FPT_phaseChkFifo(ioport, thisCard);
@@ -1813,14 +1780,11 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
else if (hp_int & ITAR_DISC) {
- if (((struct sccb_card *)pCurrCard)->
- globalFlags & F_HOST_XFER_ACT) {
-
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
FPT_phaseChkFifo(ioport, thisCard);
- }
-
- if (RD_HARPOON(ioport + hp_gp_reg_1) == SMSAVE_DATA_PTR) {
+ if (RD_HARPOON(ioport + hp_gp_reg_1) ==
+ SAVE_POINTERS) {
WR_HARPOON(ioport + hp_gp_reg_1, 0x00);
currSCCB->Sccb_XferState |= F_NO_DATA_YET;
@@ -1859,8 +1823,7 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
WRW_HARPOON((ioport + hp_intstat),
(BUS_FREE | ITAR_DISC));
- ((struct sccb_card *)pCurrCard)->globalFlags |=
- F_NEW_SCCB_CMD;
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
}
@@ -1870,13 +1833,11 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
(PROG_HLT | RSEL | PHASE | BUS_FREE));
if (RDW_HARPOON((ioport + hp_intstat)) & ITAR_DISC) {
- if (((struct sccb_card *)pCurrCard)->
- globalFlags & F_HOST_XFER_ACT) {
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
FPT_phaseChkFifo(ioport, thisCard);
- }
if (RD_HARPOON(ioport + hp_gp_reg_1) ==
- SMSAVE_DATA_PTR) {
+ SAVE_POINTERS) {
WR_HARPOON(ioport + hp_gp_reg_1, 0x00);
currSCCB->Sccb_XferState |=
F_NO_DATA_YET;
@@ -1890,8 +1851,7 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
FPT_queueDisconnect(currSCCB, thisCard);
}
- FPT_sres(ioport, thisCard,
- ((struct sccb_card *)pCurrCard));
+ FPT_sres(ioport, thisCard, pCurrCard);
FPT_phaseDecode(ioport, thisCard);
}
@@ -1948,8 +1908,7 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
WRW_HARPOON((ioport + hp_intstat), BUS_FREE);
- if (((struct sccb_card *)pCurrCard)->
- globalFlags & F_HOST_XFER_ACT) {
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT) {
FPT_hostDataXferAbort(ioport, thisCard,
currSCCB);
@@ -1961,27 +1920,19 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
else if (hp_int & ITICKLE) {
WRW_HARPOON((ioport + hp_intstat), ITICKLE);
- ((struct sccb_card *)pCurrCard)->globalFlags |=
- F_NEW_SCCB_CMD;
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
}
if (((struct sccb_card *)pCurrCard)->
globalFlags & F_NEW_SCCB_CMD) {
- ((struct sccb_card *)pCurrCard)->globalFlags &=
- ~F_NEW_SCCB_CMD;
+ pCurrCard->globalFlags &= ~F_NEW_SCCB_CMD;
- if (((struct sccb_card *)pCurrCard)->currentSCCB ==
- NULL) {
-
- FPT_queueSearchSelect(((struct sccb_card *)
- pCurrCard), thisCard);
- }
+ if (pCurrCard->currentSCCB == NULL)
+ FPT_queueSearchSelect(pCurrCard, thisCard);
- if (((struct sccb_card *)pCurrCard)->currentSCCB !=
- NULL) {
- ((struct sccb_card *)pCurrCard)->globalFlags &=
- ~F_NEW_SCCB_CMD;
+ if (pCurrCard->currentSCCB != NULL) {
+ pCurrCard->globalFlags &= ~F_NEW_SCCB_CMD;
FPT_ssel(ioport, thisCard);
}
@@ -2006,8 +1957,7 @@ static int FlashPoint_HandleInterrupt(unsigned long pCurrCard)
* processing time.
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_SccbMgr_bad_isr(unsigned long p_port,
- unsigned char p_card,
+static unsigned char FPT_SccbMgr_bad_isr(u32 p_port, unsigned char p_card,
struct sccb_card *pCurrCard,
unsigned short p_int)
{
@@ -2157,7 +2107,7 @@ static unsigned char FPT_SccbMgr_bad_isr(unsigned long p_port,
*
*---------------------------------------------------------------------*/
-static void FPT_SccbMgrTableInitAll()
+static void FPT_SccbMgrTableInitAll(void)
{
unsigned char thisCard;
@@ -2254,7 +2204,7 @@ static void FPT_SccbMgrTableInitTarget(unsigned char p_card,
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_sfm(unsigned long port, struct sccb *pCurrSCCB)
+static unsigned char FPT_sfm(u32 port, struct sccb *pCurrSCCB)
{
unsigned char message;
unsigned short TimeOutLoop;
@@ -2280,7 +2230,7 @@ static unsigned char FPT_sfm(unsigned long port, struct sccb *pCurrSCCB)
WR_HARPOON(port + hp_fiforead, 0);
WR_HARPOON(port + hp_fifowrite, 0);
if (pCurrSCCB != NULL) {
- pCurrSCCB->Sccb_scsimsg = SMPARITY;
+ pCurrSCCB->Sccb_scsimsg = MSG_PARITY_ERROR;
}
message = 0x00;
do {
@@ -2322,12 +2272,12 @@ static unsigned char FPT_sfm(unsigned long port, struct sccb *pCurrSCCB)
*
*---------------------------------------------------------------------*/
-static void FPT_ssel(unsigned long port, unsigned char p_card)
+static void FPT_ssel(u32 port, unsigned char p_card)
{
unsigned char auto_loaded, i, target, *theCCB;
- unsigned long cdb_reg;
+ u32 cdb_reg;
struct sccb_card *CurrCard;
struct sccb *currSCCB;
struct sccb_mgr_tar_info *currTar_Info;
@@ -2433,7 +2383,7 @@ static void FPT_ssel(unsigned long port, unsigned char p_card)
WRW_HARPOON((port + ID_MSG_STRT + 2), BRH_OP + ALWAYS + NP);
- currSCCB->Sccb_scsimsg = SMDEV_RESET;
+ currSCCB->Sccb_scsimsg = TARGET_RESET;
WR_HARPOON(port + hp_autostart_3, (SELECT + SELCHK_STRT));
auto_loaded = 1;
@@ -2621,7 +2571,7 @@ static void FPT_ssel(unsigned long port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_sres(unsigned long port, unsigned char p_card,
+static void FPT_sres(u32 port, unsigned char p_card,
struct sccb_card *pCurrCard)
{
@@ -2681,7 +2631,6 @@ static void FPT_sres(unsigned long port, unsigned char p_card,
WRW_HARPOON((port + hp_fiforead), (unsigned short)0x00);
our_target = (unsigned char)(RD_HARPOON(port + hp_select_id) >> 4);
- currTar_Info = &FPT_sccbMgrTbl[p_card][our_target];
msgRetryCount = 0;
do {
@@ -2780,9 +2729,9 @@ static void FPT_sres(unsigned long port, unsigned char p_card,
if (message == 0) {
msgRetryCount++;
if (msgRetryCount == 1) {
- FPT_SendMsg(port, SMPARITY);
+ FPT_SendMsg(port, MSG_PARITY_ERROR);
} else {
- FPT_SendMsg(port, SMDEV_RESET);
+ FPT_SendMsg(port, TARGET_RESET);
FPT_sssyncv(port, our_target, NARROW_SCSI,
currTar_Info);
@@ -2857,7 +2806,7 @@ static void FPT_sres(unsigned long port, unsigned char p_card,
(RD_HARPOON(port + hp_scsisig) & SCSI_BSY)) ;
}
-static void FPT_SendMsg(unsigned long port, unsigned char message)
+static void FPT_SendMsg(u32 port, unsigned char message)
{
while (!(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)) {
if (!(RD_HARPOON(port + hp_scsisig) & SCSI_BSY)) {
@@ -2882,8 +2831,8 @@ static void FPT_SendMsg(unsigned long port, unsigned char message)
WR_HARPOON(port + hp_portctrl_0, 0x00);
- if ((message == SMABORT) || (message == SMDEV_RESET) ||
- (message == SMABORT_TAG)) {
+ if ((message == ABORT_TASK_SET) || (message == TARGET_RESET) ||
+ (message == ABORT_TASK)) {
while (!
(RDW_HARPOON((port + hp_intstat)) &
(BUS_FREE | PHASE))) {
@@ -2904,8 +2853,7 @@ static void FPT_SendMsg(unsigned long port, unsigned char message)
* target device.
*
*---------------------------------------------------------------------*/
-static void FPT_sdecm(unsigned char message, unsigned long port,
- unsigned char p_card)
+static void FPT_sdecm(unsigned char message, u32 port, unsigned char p_card)
{
struct sccb *currSCCB;
struct sccb_card *CurrCard;
@@ -2916,7 +2864,7 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
currTar_Info = &FPT_sccbMgrTbl[p_card][currSCCB->TargID];
- if (message == SMREST_DATA_PTR) {
+ if (message == RESTORE_POINTERS) {
if (!(currSCCB->Sccb_XferState & F_NO_DATA_YET)) {
currSCCB->Sccb_ATC = currSCCB->Sccb_savedATC;
@@ -2928,7 +2876,7 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
(AUTO_IMMED + DISCONNECT_START));
}
- else if (message == SMCMD_COMP) {
+ else if (message == COMMAND_COMPLETE) {
if (currSCCB->Sccb_scsistat == SELECT_Q_ST) {
currTar_Info->TarStatus &=
@@ -2940,15 +2888,16 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
}
- else if ((message == SMNO_OP) || (message >= SMIDENT)
- || (message == SMINIT_RECOVERY) || (message == SMREL_RECOVERY)) {
+ else if ((message == NOP) || (message >= IDENTIFY_BASE) ||
+ (message == INITIATE_RECOVERY) ||
+ (message == RELEASE_RECOVERY)) {
ACCEPT_MSG(port);
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
}
- else if (message == SMREJECT) {
+ else if (message == MESSAGE_REJECT) {
if ((currSCCB->Sccb_scsistat == SELECT_SN_ST) ||
(currSCCB->Sccb_scsistat == SELECT_WN_ST) ||
@@ -2966,7 +2915,7 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
}
if (currSCCB->Lun == 0x00) {
- if ((currSCCB->Sccb_scsistat == SELECT_SN_ST)) {
+ if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {
currTar_Info->TarStatus |=
(unsigned char)SYNC_SUPPORTED;
@@ -2975,8 +2924,8 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
~EE_SYNC_MASK;
}
- else if ((currSCCB->Sccb_scsistat ==
- SELECT_WN_ST)) {
+ else if (currSCCB->Sccb_scsistat ==
+ SELECT_WN_ST) {
currTar_Info->TarStatus =
(currTar_Info->
@@ -3049,19 +2998,19 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
}
}
- else if (message == SMEXT) {
+ else if (message == EXTENDED_MESSAGE) {
ACCEPT_MSG(port);
FPT_shandem(port, p_card, currSCCB);
}
- else if (message == SMIGNORWR) {
+ else if (message == IGNORE_WIDE_RESIDUE) {
ACCEPT_MSG(port); /* ACK the RESIDUE MSG */
message = FPT_sfm(port, currSCCB);
- if (currSCCB->Sccb_scsimsg != SMPARITY)
+ if (currSCCB->Sccb_scsimsg != MSG_PARITY_ERROR)
ACCEPT_MSG(port);
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
@@ -3070,7 +3019,7 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
else {
currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
- currSCCB->Sccb_scsimsg = SMREJECT;
+ currSCCB->Sccb_scsimsg = MESSAGE_REJECT;
ACCEPT_MSG_ATN(port);
WR_HARPOON(port + hp_autostart_1,
@@ -3085,8 +3034,7 @@ static void FPT_sdecm(unsigned char message, unsigned long port,
* Description: Decide what to do with the extended message.
*
*---------------------------------------------------------------------*/
-static void FPT_shandem(unsigned long port, unsigned char p_card,
- struct sccb *pCurrSCCB)
+static void FPT_shandem(u32 port, unsigned char p_card, struct sccb *pCurrSCCB)
{
unsigned char length, message;
@@ -3097,7 +3045,7 @@ static void FPT_shandem(unsigned long port, unsigned char p_card,
message = FPT_sfm(port, pCurrSCCB);
if (message) {
- if (message == SMSYNC) {
+ if (message == EXTENDED_SDTR) {
if (length == 0x03) {
@@ -3105,10 +3053,10 @@ static void FPT_shandem(unsigned long port, unsigned char p_card,
FPT_stsyncn(port, p_card);
} else {
- pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ pCurrSCCB->Sccb_scsimsg = MESSAGE_REJECT;
ACCEPT_MSG_ATN(port);
}
- } else if (message == SMWDTR) {
+ } else if (message == EXTENDED_WDTR) {
if (length == 0x02) {
@@ -3116,7 +3064,7 @@ static void FPT_shandem(unsigned long port, unsigned char p_card,
FPT_stwidn(port, p_card);
} else {
- pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ pCurrSCCB->Sccb_scsimsg = MESSAGE_REJECT;
ACCEPT_MSG_ATN(port);
WR_HARPOON(port + hp_autostart_1,
@@ -3125,20 +3073,20 @@ static void FPT_shandem(unsigned long port, unsigned char p_card,
}
} else {
- pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ pCurrSCCB->Sccb_scsimsg = MESSAGE_REJECT;
ACCEPT_MSG_ATN(port);
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
}
} else {
- if (pCurrSCCB->Sccb_scsimsg != SMPARITY)
+ if (pCurrSCCB->Sccb_scsimsg != MSG_PARITY_ERROR)
ACCEPT_MSG(port);
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
}
} else {
- if (pCurrSCCB->Sccb_scsimsg == SMPARITY)
+ if (pCurrSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
}
@@ -3153,7 +3101,7 @@ static void FPT_shandem(unsigned long port, unsigned char p_card,
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_sisyncn(unsigned long port, unsigned char p_card,
+static unsigned char FPT_sisyncn(u32 port, unsigned char p_card,
unsigned char syncFlag)
{
struct sccb *currSCCB;
@@ -3172,10 +3120,10 @@ static unsigned char FPT_sisyncn(unsigned long port, unsigned char p_card,
WRW_HARPOON((port + ID_MSG_STRT + 2), BRH_OP + ALWAYS + CMDPZ);
WRW_HARPOON((port + SYNC_MSGS + 0),
- (MPM_OP + AMSG_OUT + SMEXT));
+ (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x03));
WRW_HARPOON((port + SYNC_MSGS + 4),
- (MPM_OP + AMSG_OUT + SMSYNC));
+ (MPM_OP + AMSG_OUT + EXTENDED_SDTR));
if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)
@@ -3234,7 +3182,7 @@ static unsigned char FPT_sisyncn(unsigned long port, unsigned char p_card,
* necessary.
*
*---------------------------------------------------------------------*/
-static void FPT_stsyncn(unsigned long port, unsigned char p_card)
+static void FPT_stsyncn(u32 port, unsigned char p_card)
{
unsigned char sync_msg, offset, sync_reg, our_sync_msg;
struct sccb *currSCCB;
@@ -3245,7 +3193,7 @@ static void FPT_stsyncn(unsigned long port, unsigned char p_card)
sync_msg = FPT_sfm(port, currSCCB);
- if ((sync_msg == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY)) {
+ if ((sync_msg == 0x00) && (currSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)) {
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
return;
@@ -3255,7 +3203,7 @@ static void FPT_stsyncn(unsigned long port, unsigned char p_card)
offset = FPT_sfm(port, currSCCB);
- if ((offset == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY)) {
+ if ((offset == 0x00) && (currSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)) {
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
return;
@@ -3363,13 +3311,15 @@ static void FPT_stsyncn(unsigned long port, unsigned char p_card)
* Description: Answer the targets sync message.
*
*---------------------------------------------------------------------*/
-static void FPT_sisyncr(unsigned long port, unsigned char sync_pulse,
+static void FPT_sisyncr(u32 port, unsigned char sync_pulse,
unsigned char offset)
{
ARAM_ACCESS(port);
- WRW_HARPOON((port + SYNC_MSGS + 0), (MPM_OP + AMSG_OUT + SMEXT));
+ WRW_HARPOON((port + SYNC_MSGS + 0),
+ (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x03));
- WRW_HARPOON((port + SYNC_MSGS + 4), (MPM_OP + AMSG_OUT + SMSYNC));
+ WRW_HARPOON((port + SYNC_MSGS + 4),
+ (MPM_OP + AMSG_OUT + EXTENDED_SDTR));
WRW_HARPOON((port + SYNC_MSGS + 6), (MPM_OP + AMSG_OUT + sync_pulse));
WRW_HARPOON((port + SYNC_MSGS + 8), (RAT_OP));
WRW_HARPOON((port + SYNC_MSGS + 10), (MPM_OP + AMSG_OUT + offset));
@@ -3394,7 +3344,7 @@ static void FPT_sisyncr(unsigned long port, unsigned char sync_pulse,
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_siwidn(unsigned long port, unsigned char p_card)
+static unsigned char FPT_siwidn(u32 port, unsigned char p_card)
{
struct sccb *currSCCB;
struct sccb_mgr_tar_info *currTar_Info;
@@ -3412,10 +3362,10 @@ static unsigned char FPT_siwidn(unsigned long port, unsigned char p_card)
WRW_HARPOON((port + ID_MSG_STRT + 2), BRH_OP + ALWAYS + CMDPZ);
WRW_HARPOON((port + SYNC_MSGS + 0),
- (MPM_OP + AMSG_OUT + SMEXT));
+ (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x02));
WRW_HARPOON((port + SYNC_MSGS + 4),
- (MPM_OP + AMSG_OUT + SMWDTR));
+ (MPM_OP + AMSG_OUT + EXTENDED_WDTR));
WRW_HARPOON((port + SYNC_MSGS + 6), (RAT_OP));
WRW_HARPOON((port + SYNC_MSGS + 8),
(MPM_OP + AMSG_OUT + SM16BIT));
@@ -3449,7 +3399,7 @@ static unsigned char FPT_siwidn(unsigned long port, unsigned char p_card)
* necessary.
*
*---------------------------------------------------------------------*/
-static void FPT_stwidn(unsigned long port, unsigned char p_card)
+static void FPT_stwidn(u32 port, unsigned char p_card)
{
unsigned char width;
struct sccb *currSCCB;
@@ -3460,7 +3410,7 @@ static void FPT_stwidn(unsigned long port, unsigned char p_card)
width = FPT_sfm(port, currSCCB);
- if ((width == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY)) {
+ if ((width == 0x00) && (currSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)) {
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
return;
@@ -3520,12 +3470,14 @@ static void FPT_stwidn(unsigned long port, unsigned char p_card)
* Description: Answer the targets Wide nego message.
*
*---------------------------------------------------------------------*/
-static void FPT_siwidr(unsigned long port, unsigned char width)
+static void FPT_siwidr(u32 port, unsigned char width)
{
ARAM_ACCESS(port);
- WRW_HARPOON((port + SYNC_MSGS + 0), (MPM_OP + AMSG_OUT + SMEXT));
+ WRW_HARPOON((port + SYNC_MSGS + 0),
+ (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x02));
- WRW_HARPOON((port + SYNC_MSGS + 4), (MPM_OP + AMSG_OUT + SMWDTR));
+ WRW_HARPOON((port + SYNC_MSGS + 4),
+ (MPM_OP + AMSG_OUT + EXTENDED_WDTR));
WRW_HARPOON((port + SYNC_MSGS + 6), (RAT_OP));
WRW_HARPOON((port + SYNC_MSGS + 8), (MPM_OP + AMSG_OUT + width));
WRW_HARPOON((port + SYNC_MSGS + 10), (BRH_OP + ALWAYS + NP));
@@ -3548,7 +3500,7 @@ static void FPT_siwidr(unsigned long port, unsigned char width)
* ID specified.
*
*---------------------------------------------------------------------*/
-static void FPT_sssyncv(unsigned long p_port, unsigned char p_id,
+static void FPT_sssyncv(u32 p_port, unsigned char p_id,
unsigned char p_sync_value,
struct sccb_mgr_tar_info *currTar_Info)
{
@@ -3620,7 +3572,7 @@ static void FPT_sssyncv(unsigned long p_port, unsigned char p_id,
* Description: Reset the desired card's SCSI bus.
*
*---------------------------------------------------------------------*/
-static void FPT_sresb(unsigned long port, unsigned char p_card)
+static void FPT_sresb(u32 port, unsigned char p_card)
{
unsigned char scsiID, i;
@@ -3706,14 +3658,14 @@ static void FPT_ssenss(struct sccb_card *pCurrCard)
}
currSCCB->CdbLength = SIX_BYTE_CMD;
- currSCCB->Cdb[0] = SCSI_REQUEST_SENSE;
+ currSCCB->Cdb[0] = REQUEST_SENSE;
currSCCB->Cdb[1] = currSCCB->Cdb[1] & (unsigned char)0xE0; /*Keep LUN. */
currSCCB->Cdb[2] = 0x00;
currSCCB->Cdb[3] = 0x00;
currSCCB->Cdb[4] = currSCCB->RequestSenseLength;
currSCCB->Cdb[5] = 0x00;
- currSCCB->Sccb_XferCnt = (unsigned long)currSCCB->RequestSenseLength;
+ currSCCB->Sccb_XferCnt = (u32)currSCCB->RequestSenseLength;
currSCCB->Sccb_ATC = 0x00;
@@ -3737,7 +3689,7 @@ static void FPT_ssenss(struct sccb_card *pCurrCard)
*
*---------------------------------------------------------------------*/
-static void FPT_sxfrp(unsigned long p_port, unsigned char p_card)
+static void FPT_sxfrp(u32 p_port, unsigned char p_card)
{
unsigned char curr_phz;
@@ -3819,7 +3771,7 @@ static void FPT_sxfrp(unsigned long p_port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_schkdd(unsigned long port, unsigned char p_card)
+static void FPT_schkdd(u32 port, unsigned char p_card)
{
unsigned short TimeOutLoop;
unsigned char sPhase;
@@ -3963,13 +3915,9 @@ static void FPT_sinits(struct sccb *p_sccb, unsigned char p_card)
*/
if ((currTar_Info->TarStatus & TAR_ALLOW_DISC) ||
(currTar_Info->TarStatus & TAG_Q_TRYING)) {
- p_sccb->Sccb_idmsg =
- (unsigned char)(SMIDENT | DISC_PRIV) | p_sccb->Lun;
- }
-
- else {
-
- p_sccb->Sccb_idmsg = (unsigned char)SMIDENT | p_sccb->Lun;
+ p_sccb->Sccb_idmsg = IDENTIFY(true, p_sccb->Lun);
+ } else {
+ p_sccb->Sccb_idmsg = IDENTIFY(false, p_sccb->Lun);
}
p_sccb->HostStatus = 0x00;
@@ -3986,7 +3934,7 @@ static void FPT_sinits(struct sccb *p_sccb, unsigned char p_card)
*/
p_sccb->Sccb_scsistat = BUS_FREE_ST;
p_sccb->SccbStatus = SCCB_IN_PROCESS;
- p_sccb->Sccb_scsimsg = SMNO_OP;
+ p_sccb->Sccb_scsimsg = NOP;
}
@@ -3998,10 +3946,10 @@ static void FPT_sinits(struct sccb *p_sccb, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseDecode(unsigned long p_port, unsigned char p_card)
+static void FPT_phaseDecode(u32 p_port, unsigned char p_card)
{
unsigned char phase_ref;
- void (*phase) (unsigned long, unsigned char);
+ void (*phase) (u32, unsigned char);
DISABLE_AUTO(p_port);
@@ -4021,7 +3969,7 @@ static void FPT_phaseDecode(unsigned long p_port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseDataOut(unsigned long port, unsigned char p_card)
+static void FPT_phaseDataOut(u32 port, unsigned char p_card)
{
struct sccb *currSCCB;
@@ -4062,7 +4010,7 @@ static void FPT_phaseDataOut(unsigned long port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseDataIn(unsigned long port, unsigned char p_card)
+static void FPT_phaseDataIn(u32 port, unsigned char p_card)
{
struct sccb *currSCCB;
@@ -4106,10 +4054,10 @@ static void FPT_phaseDataIn(unsigned long port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseCommand(unsigned long p_port, unsigned char p_card)
+static void FPT_phaseCommand(u32 p_port, unsigned char p_card)
{
struct sccb *currSCCB;
- unsigned long cdb_reg;
+ u32 cdb_reg;
unsigned char i;
currSCCB = FPT_BL_Card[p_card].currentSCCB;
@@ -4157,7 +4105,7 @@ static void FPT_phaseCommand(unsigned long p_port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseStatus(unsigned long port, unsigned char p_card)
+static void FPT_phaseStatus(u32 port, unsigned char p_card)
{
/* Start-up the automation to finish off this command and let the
isr handle the interrupt for command complete when it comes in.
@@ -4178,7 +4126,7 @@ static void FPT_phaseStatus(unsigned long port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card)
+static void FPT_phaseMsgOut(u32 port, unsigned char p_card)
{
unsigned char message, scsiID;
struct sccb *currSCCB;
@@ -4191,7 +4139,7 @@ static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card)
message = currSCCB->Sccb_scsimsg;
scsiID = currSCCB->TargID;
- if (message == SMDEV_RESET) {
+ if (message == TARGET_RESET) {
currTar_Info = &FPT_sccbMgrTbl[p_card][scsiID];
currTar_Info->TarSyncCtrl = 0;
@@ -4227,7 +4175,7 @@ static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card)
else if (currSCCB->Sccb_scsistat < COMMAND_ST) {
- if (message == SMNO_OP) {
+ if (message == NOP) {
currSCCB->Sccb_MGRFlags |= F_DEV_SELECTED;
FPT_ssel(port, p_card);
@@ -4235,13 +4183,13 @@ static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card)
}
} else {
- if (message == SMABORT)
+ if (message == ABORT_TASK_SET)
FPT_queueFlushSccb(p_card, SCCB_COMPLETE);
}
} else {
- message = SMABORT;
+ message = ABORT_TASK_SET;
}
WRW_HARPOON((port + hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));
@@ -4256,8 +4204,8 @@ static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card)
WR_HARPOON(port + hp_portctrl_0, 0x00);
- if ((message == SMABORT) || (message == SMDEV_RESET) ||
- (message == SMABORT_TAG)) {
+ if ((message == ABORT_TASK_SET) || (message == TARGET_RESET) ||
+ (message == ABORT_TASK)) {
while (!(RDW_HARPOON((port + hp_intstat)) & (BUS_FREE | PHASE))) {
}
@@ -4299,8 +4247,8 @@ static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card)
else {
- if (message == SMPARITY) {
- currSCCB->Sccb_scsimsg = SMNO_OP;
+ if (message == MSG_PARITY_ERROR) {
+ currSCCB->Sccb_scsimsg = NOP;
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
} else {
@@ -4317,7 +4265,7 @@ static void FPT_phaseMsgOut(unsigned long port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseMsgIn(unsigned long port, unsigned char p_card)
+static void FPT_phaseMsgIn(u32 port, unsigned char p_card)
{
unsigned char message;
struct sccb *currSCCB;
@@ -4330,7 +4278,7 @@ static void FPT_phaseMsgIn(unsigned long port, unsigned char p_card)
}
message = RD_HARPOON(port + hp_scsidata_0);
- if ((message == SMDISC) || (message == SMSAVE_DATA_PTR)) {
+ if ((message == DISCONNECT) || (message == SAVE_POINTERS)) {
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + END_DATA_START));
@@ -4345,7 +4293,7 @@ static void FPT_phaseMsgIn(unsigned long port, unsigned char p_card)
FPT_sdecm(message, port, p_card);
} else {
- if (currSCCB->Sccb_scsimsg != SMPARITY)
+ if (currSCCB->Sccb_scsimsg != MSG_PARITY_ERROR)
ACCEPT_MSG(port);
WR_HARPOON(port + hp_autostart_1,
(AUTO_IMMED + DISCONNECT_START));
@@ -4364,7 +4312,7 @@ static void FPT_phaseMsgIn(unsigned long port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseIllegal(unsigned long port, unsigned char p_card)
+static void FPT_phaseIllegal(u32 port, unsigned char p_card)
{
struct sccb *currSCCB;
@@ -4375,7 +4323,7 @@ static void FPT_phaseIllegal(unsigned long port, unsigned char p_card)
currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
currSCCB->Sccb_scsistat = ABORT_ST;
- currSCCB->Sccb_scsimsg = SMABORT;
+ currSCCB->Sccb_scsimsg = ABORT_TASK_SET;
}
ACCEPT_MSG_ATN(port);
@@ -4390,9 +4338,9 @@ static void FPT_phaseIllegal(unsigned long port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_phaseChkFifo(unsigned long port, unsigned char p_card)
+static void FPT_phaseChkFifo(u32 port, unsigned char p_card)
{
- unsigned long xfercnt;
+ u32 xfercnt;
struct sccb *currSCCB;
currSCCB = FPT_BL_Card[p_card].currentSCCB;
@@ -4461,7 +4409,7 @@ static void FPT_phaseChkFifo(unsigned long port, unsigned char p_card)
* because of command complete or from a disconnect.
*
*---------------------------------------------------------------------*/
-static void FPT_phaseBusFree(unsigned long port, unsigned char p_card)
+static void FPT_phaseBusFree(u32 port, unsigned char p_card)
{
struct sccb *currSCCB;
@@ -4554,12 +4502,12 @@ static void FPT_phaseBusFree(unsigned long port, unsigned char p_card)
*
* Function: Auto Load Default Map
*
- * Description: Load the Automation RAM with the defualt map values.
+ * Description: Load the Automation RAM with the default map values.
*
*---------------------------------------------------------------------*/
-static void FPT_autoLoadDefaultMap(unsigned long p_port)
+static void FPT_autoLoadDefaultMap(u32 p_port)
{
- unsigned long map_addr;
+ u32 map_addr;
ARAM_ACCESS(p_port);
map_addr = p_port + hp_aramBase;
@@ -4663,7 +4611,7 @@ static void FPT_autoLoadDefaultMap(unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static void FPT_autoCmdCmplt(unsigned long p_port, unsigned char p_card)
+static void FPT_autoCmdCmplt(u32 p_port, unsigned char p_card)
{
struct sccb *currSCCB;
unsigned char status_byte;
@@ -4674,9 +4622,9 @@ static void FPT_autoCmdCmplt(unsigned long p_port, unsigned char p_card)
FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA = 0;
- if (status_byte != SSGOOD) {
+ if (status_byte != SAM_STAT_GOOD) {
- if (status_byte == SSQ_FULL) {
+ if (status_byte == SAM_STAT_TASK_SET_FULL) {
if (((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
@@ -4808,7 +4756,7 @@ static void FPT_autoCmdCmplt(unsigned long p_port, unsigned char p_card)
}
- if (status_byte == SSCHECK) {
+ if (status_byte == SAM_STAT_CHECK_CONDITION) {
if (FPT_BL_Card[p_card].globalFlags & F_DO_RENEGO) {
if (FPT_sccbMgrTbl[p_card][currSCCB->TargID].
TarEEValue & EE_SYNC_MASK) {
@@ -4830,7 +4778,7 @@ static void FPT_autoCmdCmplt(unsigned long p_port, unsigned char p_card)
currSCCB->SccbStatus = SCCB_ERROR;
currSCCB->TargetStatus = status_byte;
- if (status_byte == SSCHECK) {
+ if (status_byte == SAM_STAT_CHECK_CONDITION) {
FPT_sccbMgrTbl[p_card][currSCCB->TargID].
TarLUN_CA = 1;
@@ -4936,8 +4884,7 @@ static void FPT_autoCmdCmplt(unsigned long p_port, unsigned char p_card)
*
*---------------------------------------------------------------------*/
-static void FPT_dataXferProcessor(unsigned long port,
- struct sccb_card *pCurrCard)
+static void FPT_dataXferProcessor(u32 port, struct sccb_card *pCurrCard)
{
struct sccb *currSCCB;
@@ -4970,22 +4917,18 @@ static void FPT_dataXferProcessor(unsigned long port,
* Description:
*
*---------------------------------------------------------------------*/
-static void FPT_busMstrSGDataXferStart(unsigned long p_port,
- struct sccb *pcurrSCCB)
+static void FPT_busMstrSGDataXferStart(u32 p_port, struct sccb *pcurrSCCB)
{
- unsigned long count, addr, tmpSGCnt;
+ u32 count, addr, tmpSGCnt;
unsigned int sg_index;
unsigned char sg_count, i;
- unsigned long reg_offset;
-
- if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
-
- count = ((unsigned long)HOST_RD_CMD) << 24;
- }
+ u32 reg_offset;
+ struct blogic_sg_seg *segp;
- else {
- count = ((unsigned long)HOST_WRT_CMD) << 24;
- }
+ if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR)
+ count = ((u32)HOST_RD_CMD) << 24;
+ else
+ count = ((u32)HOST_WRT_CMD) << 24;
sg_count = 0;
tmpSGCnt = 0;
@@ -4998,25 +4941,20 @@ static void FPT_busMstrSGDataXferStart(unsigned long p_port,
WR_HARPOON(p_port + hp_page_ctrl, i);
while ((sg_count < (unsigned char)SG_BUF_CNT) &&
- ((unsigned long)(sg_index * (unsigned int)SG_ELEMENT_SIZE) <
- pcurrSCCB->DataLength)) {
-
- tmpSGCnt += *(((unsigned long *)pcurrSCCB->DataPointer) +
- (sg_index * 2));
+ ((sg_index * (unsigned int)SG_ELEMENT_SIZE) <
+ pcurrSCCB->DataLength)) {
- count |= *(((unsigned long *)pcurrSCCB->DataPointer) +
- (sg_index * 2));
-
- addr = *(((unsigned long *)pcurrSCCB->DataPointer) +
- ((sg_index * 2) + 1));
+ segp = (struct blogic_sg_seg *)(pcurrSCCB->DataPointer) +
+ sg_index;
+ tmpSGCnt += segp->segbytes;
+ count |= segp->segbytes;
+ addr = segp->segdata;
if ((!sg_count) && (pcurrSCCB->Sccb_SGoffset)) {
-
addr +=
((count & 0x00FFFFFFL) - pcurrSCCB->Sccb_SGoffset);
count =
(count & 0xFF000000L) | pcurrSCCB->Sccb_SGoffset;
-
tmpSGCnt = count & 0x00FFFFFFL;
}
@@ -5072,17 +5010,15 @@ static void FPT_busMstrSGDataXferStart(unsigned long p_port,
* Description:
*
*---------------------------------------------------------------------*/
-static void FPT_busMstrDataXferStart(unsigned long p_port,
- struct sccb *pcurrSCCB)
+static void FPT_busMstrDataXferStart(u32 p_port, struct sccb *pcurrSCCB)
{
- unsigned long addr, count;
+ u32 addr, count;
if (!(pcurrSCCB->Sccb_XferState & F_AUTO_SENSE)) {
count = pcurrSCCB->Sccb_XferCnt;
- addr =
- (unsigned long)pcurrSCCB->DataPointer + pcurrSCCB->Sccb_ATC;
+ addr = (u32)(unsigned long)pcurrSCCB->DataPointer + pcurrSCCB->Sccb_ATC;
}
else {
@@ -5127,7 +5063,7 @@ static void FPT_busMstrDataXferStart(unsigned long p_port,
* command busy is also time out, it'll just give up.
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_busMstrTimeOut(unsigned long p_port)
+static unsigned char FPT_busMstrTimeOut(u32 p_port)
{
unsigned long timeout;
@@ -5166,13 +5102,14 @@ static unsigned char FPT_busMstrTimeOut(unsigned long p_port)
* Description: Abort any in progress transfer.
*
*---------------------------------------------------------------------*/
-static void FPT_hostDataXferAbort(unsigned long port, unsigned char p_card,
+static void FPT_hostDataXferAbort(u32 port, unsigned char p_card,
struct sccb *pCurrSCCB)
{
unsigned long timeout;
unsigned long remain_cnt;
- unsigned int sg_ptr;
+ u32 sg_ptr;
+ struct blogic_sg_seg *segp;
FPT_BL_Card[p_card].globalFlags &= ~F_HOST_XFER_ACT;
@@ -5236,9 +5173,8 @@ static void FPT_hostDataXferAbort(unsigned long port, unsigned char p_card,
(unsigned int)(pCurrSCCB->DataLength /
SG_ELEMENT_SIZE)) {
- sg_ptr =
- (unsigned int)(pCurrSCCB->DataLength /
- SG_ELEMENT_SIZE);
+ sg_ptr = (u32)(pCurrSCCB->DataLength /
+ SG_ELEMENT_SIZE);
}
remain_cnt = pCurrSCCB->Sccb_XferCnt;
@@ -5246,23 +5182,13 @@ static void FPT_hostDataXferAbort(unsigned long port, unsigned char p_card,
while (remain_cnt < 0x01000000L) {
sg_ptr--;
-
- if (remain_cnt >
- (unsigned
- long)(*(((unsigned long *)pCurrSCCB->
- DataPointer) + (sg_ptr * 2)))) {
-
+ segp = (struct blogic_sg_seg *)(pCurrSCCB->
+ DataPointer) + (sg_ptr * 2);
+ if (remain_cnt > (unsigned long)segp->segbytes)
remain_cnt -=
- (unsigned
- long)(*(((unsigned long *)
- pCurrSCCB->DataPointer) +
- (sg_ptr * 2)));
- }
-
- else {
-
+ (unsigned long)segp->segbytes;
+ else
break;
- }
}
if (remain_cnt < 0x01000000L) {
@@ -5418,23 +5344,18 @@ static void FPT_hostDataXferAbort(unsigned long port, unsigned char p_card,
pCurrSCCB->Sccb_SGoffset = 0x00;
- if ((unsigned long)(pCurrSCCB->Sccb_sgseg *
- SG_ELEMENT_SIZE) >=
- pCurrSCCB->DataLength) {
+ if ((u32)(pCurrSCCB->Sccb_sgseg * SG_ELEMENT_SIZE) >=
+ pCurrSCCB->DataLength) {
pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
-
pCurrSCCB->Sccb_sgseg =
(unsigned short)(pCurrSCCB->DataLength /
SG_ELEMENT_SIZE);
-
}
}
else {
-
if (!(pCurrSCCB->Sccb_XferState & F_AUTO_SENSE))
-
pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
}
}
@@ -5454,21 +5375,22 @@ static void FPT_hostDataXferRestart(struct sccb *currSCCB)
{
unsigned long data_count;
unsigned int sg_index;
- unsigned long *sg_ptr;
+ struct blogic_sg_seg *segp;
if (currSCCB->Sccb_XferState & F_SG_XFER) {
currSCCB->Sccb_XferCnt = 0;
sg_index = 0xffff; /*Index by long words into sg list. */
- data_count = 0; /*Running count of SG xfer counts. */
+ data_count = 0; /*Running count of SG xfer counts. */
- sg_ptr = (unsigned long *)currSCCB->DataPointer;
while (data_count < currSCCB->Sccb_ATC) {
sg_index++;
- data_count += *(sg_ptr + (sg_index * 2));
+ segp = (struct blogic_sg_seg *)(currSCCB->DataPointer) +
+ (sg_index * 2);
+ data_count += segp->segbytes;
}
if (data_count == currSCCB->Sccb_ATC) {
@@ -5504,7 +5426,7 @@ static void FPT_scini(unsigned char p_card, unsigned char p_our_id,
{
unsigned char loser, assigned_id;
- unsigned long p_port;
+ u32 p_port;
unsigned char i, k, ScamFlg;
struct sccb_card *currCard;
@@ -5709,7 +5631,7 @@ static void FPT_scini(unsigned char p_card, unsigned char p_our_id,
*
*---------------------------------------------------------------------*/
-static int FPT_scarb(unsigned long p_port, unsigned char p_sel_type)
+static int FPT_scarb(u32 p_port, unsigned char p_sel_type)
{
if (p_sel_type == INIT_SELTD) {
@@ -5771,7 +5693,7 @@ static int FPT_scarb(unsigned long p_port, unsigned char p_sel_type)
*
*---------------------------------------------------------------------*/
-static void FPT_scbusf(unsigned long p_port)
+static void FPT_scbusf(u32 p_port)
{
WR_HARPOON(p_port + hp_page_ctrl,
(RD_HARPOON(p_port + hp_page_ctrl) | G_INT_DISABLE));
@@ -5803,7 +5725,7 @@ static void FPT_scbusf(unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static void FPT_scasid(unsigned char p_card, unsigned long p_port)
+static void FPT_scasid(unsigned char p_card, u32 p_port)
{
unsigned char temp_id_string[ID_STRING_LENGTH];
@@ -5880,7 +5802,7 @@ static void FPT_scasid(unsigned char p_card, unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static void FPT_scsel(unsigned long p_port)
+static void FPT_scsel(u32 p_port)
{
WR_HARPOON(p_port + hp_scsisig, SCSI_SEL);
@@ -5914,7 +5836,7 @@ static void FPT_scsel(unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_scxferc(unsigned long p_port, unsigned char p_data)
+static unsigned char FPT_scxferc(u32 p_port, unsigned char p_data)
{
unsigned char curr_data, ret_data;
@@ -5964,8 +5886,7 @@ static unsigned char FPT_scxferc(unsigned long p_port, unsigned char p_data)
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_scsendi(unsigned long p_port,
- unsigned char p_id_string[])
+static unsigned char FPT_scsendi(u32 p_port, unsigned char p_id_string[])
{
unsigned char ret_data, byte_cnt, bit_cnt, defer;
@@ -6016,8 +5937,7 @@ static unsigned char FPT_scsendi(unsigned long p_port,
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_sciso(unsigned long p_port,
- unsigned char p_id_string[])
+static unsigned char FPT_sciso(u32 p_port, unsigned char p_id_string[])
{
unsigned char ret_data, the_data, byte_cnt, bit_cnt;
@@ -6075,7 +5995,7 @@ static unsigned char FPT_sciso(unsigned long p_port,
*
*---------------------------------------------------------------------*/
-static void FPT_scwirod(unsigned long p_port, unsigned char p_data_bit)
+static void FPT_scwirod(u32 p_port, unsigned char p_data_bit)
{
unsigned char i;
@@ -6102,7 +6022,7 @@ static void FPT_scwirod(unsigned long p_port, unsigned char p_data_bit)
*
*---------------------------------------------------------------------*/
-static void FPT_scwiros(unsigned long p_port, unsigned char p_data_bit)
+static void FPT_scwiros(u32 p_port, unsigned char p_data_bit)
{
unsigned char i;
@@ -6154,7 +6074,7 @@ static unsigned char FPT_scvalq(unsigned char p_quintet)
*
*---------------------------------------------------------------------*/
-static unsigned char FPT_scsell(unsigned long p_port, unsigned char targ_id)
+static unsigned char FPT_scsell(u32 p_port, unsigned char targ_id)
{
unsigned long i;
@@ -6236,7 +6156,7 @@ static unsigned char FPT_scsell(unsigned long p_port, unsigned char targ_id)
*
*---------------------------------------------------------------------*/
-static void FPT_scwtsel(unsigned long p_port)
+static void FPT_scwtsel(u32 p_port)
{
while (!(RDW_HARPOON((p_port + hp_intstat)) & SCAM_SEL)) {
}
@@ -6250,8 +6170,7 @@ static void FPT_scwtsel(unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static void FPT_inisci(unsigned char p_card, unsigned long p_port,
- unsigned char p_our_id)
+static void FPT_inisci(unsigned char p_card, u32 p_port, unsigned char p_our_id)
{
unsigned char i, k, max_id;
unsigned short ee_data;
@@ -6437,7 +6356,7 @@ static unsigned char FPT_scmachid(unsigned char p_card,
*
*---------------------------------------------------------------------*/
-static void FPT_scsavdi(unsigned char p_card, unsigned long p_port)
+static void FPT_scsavdi(unsigned char p_card, u32 p_port)
{
unsigned char i, k, max_id;
unsigned short ee_data, sum_data;
@@ -6482,7 +6401,7 @@ static void FPT_scsavdi(unsigned char p_card, unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static void FPT_XbowInit(unsigned long port, unsigned char ScamFlg)
+static void FPT_XbowInit(u32 port, unsigned char ScamFlg)
{
unsigned char i;
@@ -6531,7 +6450,7 @@ static void FPT_XbowInit(unsigned long port, unsigned char ScamFlg)
*
*---------------------------------------------------------------------*/
-static void FPT_BusMasterInit(unsigned long p_port)
+static void FPT_BusMasterInit(u32 p_port)
{
WR_HARPOON(p_port + hp_sys_ctrl, DRVR_RST);
@@ -6558,7 +6477,7 @@ static void FPT_BusMasterInit(unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static void FPT_DiagEEPROM(unsigned long p_port)
+static void FPT_DiagEEPROM(u32 p_port)
{
unsigned short index, temp, max_wd_cnt;
@@ -6921,14 +6840,14 @@ static void FPT_queueCmdComplete(struct sccb_card *pCurrCard,
if ((p_sccb->
ControlByte & (SCCB_DATA_XFER_OUT | SCCB_DATA_XFER_IN))
&& (p_sccb->HostStatus == SCCB_COMPLETE)
- && (p_sccb->TargetStatus != SSCHECK))
-
- if ((SCSIcmd == SCSI_READ) ||
- (SCSIcmd == SCSI_WRITE) ||
- (SCSIcmd == SCSI_READ_EXTENDED) ||
- (SCSIcmd == SCSI_WRITE_EXTENDED) ||
- (SCSIcmd == SCSI_WRITE_AND_VERIFY) ||
- (SCSIcmd == SCSI_START_STOP_UNIT) ||
+ && (p_sccb->TargetStatus != SAM_STAT_CHECK_CONDITION))
+
+ if ((SCSIcmd == READ_6) ||
+ (SCSIcmd == WRITE_6) ||
+ (SCSIcmd == READ_10) ||
+ (SCSIcmd == WRITE_10) ||
+ (SCSIcmd == WRITE_VERIFY) ||
+ (SCSIcmd == START_STOP) ||
(pCurrCard->globalFlags & F_NO_FILTER)
)
p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
@@ -7206,7 +7125,7 @@ static void FPT_utilUpdateResidual(struct sccb *p_SCCB)
{
unsigned long partial_cnt;
unsigned int sg_index;
- unsigned long *sg_ptr;
+ struct blogic_sg_seg *segp;
if (p_SCCB->Sccb_XferState & F_ALL_XFERRED) {
@@ -7219,7 +7138,6 @@ static void FPT_utilUpdateResidual(struct sccb *p_SCCB)
sg_index = p_SCCB->Sccb_sgseg;
- sg_ptr = (unsigned long *)p_SCCB->DataPointer;
if (p_SCCB->Sccb_SGoffset) {
@@ -7229,8 +7147,9 @@ static void FPT_utilUpdateResidual(struct sccb *p_SCCB)
while (((unsigned long)sg_index *
(unsigned long)SG_ELEMENT_SIZE) < p_SCCB->DataLength) {
-
- partial_cnt += *(sg_ptr + (sg_index * 2));
+ segp = (struct blogic_sg_seg *)(p_SCCB->DataPointer) +
+ (sg_index * 2);
+ partial_cnt += segp->segbytes;
sg_index++;
}
@@ -7251,7 +7170,7 @@ static void FPT_utilUpdateResidual(struct sccb *p_SCCB)
*
*---------------------------------------------------------------------*/
-static void FPT_Wait1Second(unsigned long p_port)
+static void FPT_Wait1Second(u32 p_port)
{
unsigned char i;
@@ -7275,7 +7194,7 @@ static void FPT_Wait1Second(unsigned long p_port)
*
*---------------------------------------------------------------------*/
-static void FPT_Wait(unsigned long p_port, unsigned char p_delay)
+static void FPT_Wait(u32 p_port, unsigned char p_delay)
{
unsigned char old_timer;
unsigned char green_flag;
@@ -7321,7 +7240,7 @@ static void FPT_Wait(unsigned long p_port, unsigned char p_delay)
*
*---------------------------------------------------------------------*/
-static void FPT_utilEEWriteOnOff(unsigned long p_port, unsigned char p_mode)
+static void FPT_utilEEWriteOnOff(u32 p_port, unsigned char p_mode)
{
unsigned char ee_value;
@@ -7350,7 +7269,7 @@ static void FPT_utilEEWriteOnOff(unsigned long p_port, unsigned char p_mode)
*
*---------------------------------------------------------------------*/
-static void FPT_utilEEWrite(unsigned long p_port, unsigned short ee_data,
+static void FPT_utilEEWrite(u32 p_port, unsigned short ee_data,
unsigned short ee_addr)
{
@@ -7401,7 +7320,7 @@ static void FPT_utilEEWrite(unsigned long p_port, unsigned short ee_data,
*
*---------------------------------------------------------------------*/
-static unsigned short FPT_utilEERead(unsigned long p_port,
+static unsigned short FPT_utilEERead(u32 p_port,
unsigned short ee_addr)
{
unsigned short i, ee_data1, ee_data2;
@@ -7431,8 +7350,7 @@ static unsigned short FPT_utilEERead(unsigned long p_port,
*
*---------------------------------------------------------------------*/
-static unsigned short FPT_utilEEReadOrg(unsigned long p_port,
- unsigned short ee_addr)
+static unsigned short FPT_utilEEReadOrg(u32 p_port, unsigned short ee_addr)
{
unsigned char ee_value;
@@ -7479,7 +7397,7 @@ static unsigned short FPT_utilEEReadOrg(unsigned long p_port,
*
*---------------------------------------------------------------------*/
-static void FPT_utilEESendCmdAddr(unsigned long p_port, unsigned char ee_cmd,
+static void FPT_utilEESendCmdAddr(u32 p_port, unsigned char ee_cmd,
unsigned short ee_addr)
{
unsigned char ee_value;
@@ -7573,47 +7491,45 @@ static unsigned char FPT_CalcLrc(unsigned char buffer[])
*/
static inline unsigned char
-FlashPoint__ProbeHostAdapter(struct FlashPoint_Info *FlashPointInfo)
+FlashPoint__ProbeHostAdapter(struct fpoint_info *FlashPointInfo)
{
return FlashPoint_ProbeHostAdapter((struct sccb_mgr_info *)
FlashPointInfo);
}
-static inline FlashPoint_CardHandle_T
-FlashPoint__HardwareResetHostAdapter(struct FlashPoint_Info *FlashPointInfo)
+static inline void *
+FlashPoint__HardwareResetHostAdapter(struct fpoint_info *FlashPointInfo)
{
return FlashPoint_HardwareResetHostAdapter((struct sccb_mgr_info *)
FlashPointInfo);
}
static inline void
-FlashPoint__ReleaseHostAdapter(FlashPoint_CardHandle_T CardHandle)
+FlashPoint__ReleaseHostAdapter(void *CardHandle)
{
FlashPoint_ReleaseHostAdapter(CardHandle);
}
static inline void
-FlashPoint__StartCCB(FlashPoint_CardHandle_T CardHandle,
- struct BusLogic_CCB *CCB)
+FlashPoint__StartCCB(void *CardHandle, struct blogic_ccb *CCB)
{
FlashPoint_StartCCB(CardHandle, (struct sccb *)CCB);
}
static inline void
-FlashPoint__AbortCCB(FlashPoint_CardHandle_T CardHandle,
- struct BusLogic_CCB *CCB)
+FlashPoint__AbortCCB(void *CardHandle, struct blogic_ccb *CCB)
{
FlashPoint_AbortCCB(CardHandle, (struct sccb *)CCB);
}
static inline bool
-FlashPoint__InterruptPending(FlashPoint_CardHandle_T CardHandle)
+FlashPoint__InterruptPending(void *CardHandle)
{
return FlashPoint_InterruptPending(CardHandle);
}
static inline int
-FlashPoint__HandleInterrupt(FlashPoint_CardHandle_T CardHandle)
+FlashPoint__HandleInterrupt(void *CardHandle)
{
return FlashPoint_HandleInterrupt(CardHandle);
}
@@ -7632,13 +7548,12 @@ FlashPoint__HandleInterrupt(FlashPoint_CardHandle_T CardHandle)
Define prototypes for the FlashPoint SCCB Manager Functions.
*/
-extern unsigned char FlashPoint_ProbeHostAdapter(struct FlashPoint_Info *);
-extern FlashPoint_CardHandle_T
-FlashPoint_HardwareResetHostAdapter(struct FlashPoint_Info *);
-extern void FlashPoint_StartCCB(FlashPoint_CardHandle_T, struct BusLogic_CCB *);
-extern int FlashPoint_AbortCCB(FlashPoint_CardHandle_T, struct BusLogic_CCB *);
-extern bool FlashPoint_InterruptPending(FlashPoint_CardHandle_T);
-extern int FlashPoint_HandleInterrupt(FlashPoint_CardHandle_T);
-extern void FlashPoint_ReleaseHostAdapter(FlashPoint_CardHandle_T);
+extern unsigned char FlashPoint_ProbeHostAdapter(struct fpoint_info *);
+extern void *FlashPoint_HardwareResetHostAdapter(struct fpoint_info *);
+extern void FlashPoint_StartCCB(void *, struct blogic_ccb *);
+extern int FlashPoint_AbortCCB(void *, struct blogic_ccb *);
+extern bool FlashPoint_InterruptPending(void *);
+extern int FlashPoint_HandleInterrupt(void *);
+extern void FlashPoint_ReleaseHostAdapter(void *);
#endif /* CONFIG_SCSI_FLASHPOINT */
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 8d9dae89f065..19d0884479a2 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1,23 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0-only
menu "SCSI device support"
config SCSI_MOD
- tristate
- default y if SCSI=n || SCSI=y
- default m if SCSI=m
+ tristate
+ default y if SCSI=n || SCSI=y
+ default m if SCSI=m
+ depends on BLOCK
config RAID_ATTRS
tristate "RAID Transport Class"
default n
depends on BLOCK
depends on SCSI_MOD
- ---help---
+ help
Provides RAID
+config SCSI_COMMON
+ tristate
+
config SCSI
tristate "SCSI device support"
depends on BLOCK
select SCSI_DMA if HAS_DMA
- ---help---
+ select SG_POOL
+ select SCSI_COMMON
+ select BLK_DEV_BSG_COMMON if BLK_DEV_BSG
+ help
If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or
any other SCSI device under Linux, say Y and make sure that you know
the name of your SCSI host adapter (the card inside your computer
@@ -30,7 +38,7 @@ config SCSI
Channel, and FireWire storage.
To compile this driver as a module, choose M here and read
- <file:Documentation/scsi/scsi.txt>.
+ <file:Documentation/scsi/scsi.rst>.
The module will be called scsi_mod.
However, do not compile this as a module if your root file system
@@ -40,37 +48,41 @@ config SCSI_DMA
bool
default n
-config SCSI_TGT
- tristate "SCSI target support"
- depends on SCSI && EXPERIMENTAL
- ---help---
- If you want to use SCSI target mode drivers enable this option.
- If you choose M, the module will be called scsi_tgt.
+config SCSI_ESP_PIO
+ bool
config SCSI_NETLINK
bool
- default n
- select NET
+ default n
+ depends on NET
config SCSI_PROC_FS
bool "legacy /proc/scsi/ support"
depends on SCSI && PROC_FS
default y
- ---help---
+ help
This option enables support for the various files in
/proc/scsi. In Linux 2.6 this has been superseded by
files in sysfs but many legacy applications rely on this.
If unsure say Y.
+config SCSI_LIB_KUNIT_TEST
+ tristate "KUnit tests for SCSI Mid Layer's scsi_lib" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Run SCSI Mid Layer's KUnit tests for scsi_lib.
+
+ If unsure say N.
+
comment "SCSI support type (disk, tape, CD-ROM)"
depends on SCSI
config BLK_DEV_SD
tristate "SCSI disk support"
depends on SCSI
- select CRC_T10DIF if BLK_DEV_INTEGRITY
- ---help---
+ help
If you want to use SCSI hard disks, Fibre Channel disks,
Serial ATA (SATA) or Parallel ATA (PATA) hard disks,
USB storage or the SCSI or parallel port version of
@@ -80,7 +92,7 @@ config BLK_DEV_SD
CD-ROMs.
To compile this driver as a module, choose M here and read
- <file:Documentation/scsi/scsi.txt>.
+ <file:Documentation/scsi/scsi.rst>.
The module will be called sd_mod.
Do not compile this driver as a module if your root file system
@@ -91,42 +103,21 @@ config BLK_DEV_SD
config CHR_DEV_ST
tristate "SCSI tape support"
depends on SCSI
- ---help---
+ help
If you want to use a SCSI tape drive under Linux, say Y and read the
SCSI-HOWTO, available from
<http://www.tldp.org/docs.html#howto>, and
- <file:Documentation/scsi/st.txt> in the kernel source. This is NOT
+ <file:Documentation/scsi/st.rst> in the kernel source. This is NOT
for SCSI CD-ROMs.
To compile this driver as a module, choose M here and read
- <file:Documentation/scsi/scsi.txt>. The module will be called st.
-
-config CHR_DEV_OSST
- tristate "SCSI OnStream SC-x0 tape support"
- depends on SCSI
- ---help---
- The OnStream SC-x0 SCSI tape drives cannot be driven by the
- standard st driver, but instead need this special osst driver and
- use the /dev/osstX char device nodes (major 206). Via usb-storage,
- you may be able to drive the USB-x0 and DI-x0 drives as well.
- Note that there is also a second generation of OnStream
- tape drives (ADR-x0) that supports the standard SCSI-2 commands for
- tapes (QIC-157) and can be driven by the standard driver st.
- For more information, you may have a look at the SCSI-HOWTO
- <http://www.tldp.org/docs.html#howto> and
- <file:Documentation/scsi/osst.txt> in the kernel source.
- More info on the OnStream driver may be found on
- <http://sourceforge.net/projects/osst/>
- Please also have a look at the standard st docu, as most of it
- applies to osst as well.
-
- To compile this driver as a module, choose M here and read
- <file:Documentation/scsi/scsi.txt>. The module will be called osst.
+ <file:Documentation/scsi/scsi.rst>. The module will be called st.
config BLK_DEV_SR
tristate "SCSI CDROM support"
- depends on SCSI
- ---help---
+ depends on SCSI && BLK_DEV
+ select CDROM
+ help
If you want to use a CD or DVD drive attached to your computer
by SCSI, FireWire, USB or ATAPI, say Y and read the SCSI-HOWTO
and the CDROM-HOWTO at <http://www.tldp.org/docs.html#howto>.
@@ -134,22 +125,13 @@ config BLK_DEV_SR
Make sure to say Y or M to "ISO 9660 CD-ROM file system support".
To compile this driver as a module, choose M here and read
- <file:Documentation/scsi/scsi.txt>.
+ <file:Documentation/scsi/scsi.rst>.
The module will be called sr_mod.
-config BLK_DEV_SR_VENDOR
- bool "Enable vendor-specific extensions (for SCSI CDROM)"
- depends on BLK_DEV_SR
- help
- This enables the usage of vendor specific SCSI commands. This is
- required to support multisession CDs with old NEC/TOSHIBA cdrom
- drives (and HP Writers). If you have such a drive and get the first
- session only, try saying Y here; everybody else says N.
-
config CHR_DEV_SG
tristate "SCSI generic support"
depends on SCSI
- ---help---
+ help
If you want to use SCSI scanners, synthesizers or CD-writers or just
about anything having "SCSI" in its name other than hard disks,
CD-ROMs or tapes, say Y here. These won't be supported by the kernel
@@ -158,71 +140,70 @@ config CHR_DEV_SG
For scanners, look at SANE (<http://www.sane-project.org/>). For CD
writer software look at Cdrtools
- (<http://cdrecord.berlios.de/private/cdrecord.html>)
+ (<http://cdrtools.sourceforge.net/>)
and for burning a "disk at once": CDRDAO
(<http://cdrdao.sourceforge.net/>). Cdparanoia is a high
quality digital reader of audio CDs (<http://www.xiph.org/paranoia/>).
For other devices, it's possible that you'll have to write the
driver software yourself. Please read the file
- <file:Documentation/scsi/scsi-generic.txt> for more information.
+ <file:Documentation/scsi/scsi-generic.rst> for more information.
To compile this driver as a module, choose M here and read
- <file:Documentation/scsi/scsi.txt>. The module will be called sg.
+ <file:Documentation/scsi/scsi.rst>. The module will be called sg.
If unsure, say N.
+config BLK_DEV_BSG
+ bool "/dev/bsg support (SG v4)"
+ depends on SCSI
+ default y
+ help
+ Saying Y here will enable generic SG (SCSI generic) v4 support
+ for any SCSI device.
+
+ This option is required by UDEV to access device serial numbers, etc.
+
+ If unsure, say Y.
+
config CHR_DEV_SCH
tristate "SCSI media changer support"
depends on SCSI
- ---help---
+ help
This is a driver for SCSI media changers. Most common devices are
tape libraries and MOD/CDROM jukeboxes. *Real* jukeboxes, you
don't need this for those tiny 6-slot cdrom changers. Media
changers are listed as "Type: Medium Changer" in /proc/scsi/scsi.
If you have such hardware and want to use it with linux, say Y
- here. Check <file:Documentation/scsi/scsi-changer.txt> for details.
+ here. Check <file:Documentation/scsi/scsi-changer.rst> for details.
If you want to compile this as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
- say M here and read <file:Documentation/kbuild/modules.txt> and
- <file:Documentation/scsi/scsi.txt>. The module will be called ch.o.
+ say M here and read <file:Documentation/kbuild/modules.rst> and
+ <file:Documentation/scsi/scsi.rst>. The module will be called ch.o.
If unsure, say N.
config SCSI_ENCLOSURE
tristate "SCSI Enclosure Support"
depends on SCSI && ENCLOSURE_SERVICES
+ depends on m || SCSI_SAS_ATTRS != m
help
Enclosures are devices sitting on or in SCSI backplanes that
manage devices. If you have a disk cage, the chances are that
it has an enclosure device. Selecting this option will just allow
certain enclosure conditions to be reported and is not required.
-config SCSI_MULTI_LUN
- bool "Probe all LUNs on each SCSI device"
- depends on SCSI
- help
- Some devices support more than one LUN (Logical Unit Number) in order
- to allow access to several media, e.g. CD jukebox, USB card reader,
- mobile phone in mass storage mode. This option forces the kernel to
- probe for all LUNs by default. This setting can be overriden by
- max_luns boot/module parameter. Note that this option does not affect
- devices conforming to SCSI-3 or higher as they can explicitely report
- their number of LUNs. It is safe to say Y here unless you have one of
- those rare devices which reacts in an unexpected way when probed for
- multiple LUNs.
-
config SCSI_CONSTANTS
- bool "Verbose SCSI error reporting (kernel size +=12K)"
+ bool "Verbose SCSI error reporting (kernel size += 36K)"
depends on SCSI
help
The error messages regarding your SCSI hardware will be easier to
understand if you say Y here; it will enlarge your kernel by about
- 12 KB. If in doubt, say Y.
+ 36 KB. If in doubt, say Y.
config SCSI_LOGGING
bool "SCSI logging facility"
depends on SCSI
- ---help---
+ help
This turns on a logging facility that can be used to debug a number
of SCSI related problems.
@@ -253,32 +234,16 @@ config SCSI_SCAN_ASYNC
system continues booting, and even probe devices on different
busses in parallel, leading to a significant speed-up.
- If you have built SCSI as modules, enabling this option can
- be a problem as the devices may not have been found by the
- time your system expects them to have been. You can load the
- scsi_wait_scan module to ensure that all scans have completed.
- If you build your SCSI drivers into the kernel, then everything
- will work fine if you say Y here.
-
You can override this choice by specifying "scsi_mod.scan=sync"
or async on the kernel's command line.
-config SCSI_WAIT_SCAN
- tristate # No prompt here, this is an invisible symbol.
- default m
- depends on SCSI
- depends on MODULES
-# scsi_wait_scan is a loadable module which waits until all the async scans are
-# complete. The idea is to use it in initrd/ initramfs scripts. You modprobe
-# it after all the modprobes of the root SCSI drivers and it will wait until
-# they have all finished scanning their buses before allowing the boot to
-# proceed. (This method is not applicable if targets boot independently in
-# parallel with the initiator, or with transports with non-deterministic target
-# discovery schemes, or if a transport driver does not support scsi_wait_scan.)
-#
-# This symbol is not exposed as a prompt because little is to be gained by
-# disabling it, whereas people who accidentally switch it off may wonder why
-# their mkinitrd gets into trouble.
+ Note that this setting also affects whether resuming from
+ system suspend will be performed asynchronously.
+
+config SCSI_PROTO_TEST
+ tristate "scsi_proto.h unit tests" if !KUNIT_ALL_TESTS
+ depends on SCSI && KUNIT
+ default KUNIT_ALL_TESTS
menu "SCSI Transports"
depends on SCSI
@@ -292,23 +257,18 @@ config SCSI_SPI_ATTRS
config SCSI_FC_ATTRS
tristate "FiberChannel Transport Attributes"
- depends on SCSI
+ depends on SCSI && NET
+ select BLK_DEV_BSGLIB
select SCSI_NETLINK
help
If you wish to export transport-specific information about
each attached FiberChannel device to sysfs, say Y.
Otherwise, say N.
-config SCSI_FC_TGT_ATTRS
- bool "SCSI target support for FiberChannel Transport Attributes"
- depends on SCSI_FC_ATTRS
- depends on SCSI_TGT = y || SCSI_TGT = SCSI_FC_ATTRS
- help
- If you want to use SCSI target mode drivers enable this option.
-
config SCSI_ISCSI_ATTRS
tristate "iSCSI Transport Attributes"
depends on SCSI && NET
+ select BLK_DEV_BSGLIB
help
If you wish to export transport-specific information about
each attached iSCSI device to sysfs, say Y.
@@ -317,7 +277,7 @@ config SCSI_ISCSI_ATTRS
config SCSI_SAS_ATTRS
tristate "SAS Transport Attributes"
depends on SCSI
- select BLK_DEV_BSG
+ select BLK_DEV_BSGLIB
help
If you wish to export transport-specific information about
each attached SAS device to sysfs, say Y.
@@ -331,13 +291,6 @@ config SCSI_SRP_ATTRS
If you wish to export transport-specific information about
each attached SRP device to sysfs, say Y.
-config SCSI_SRP_TGT_ATTRS
- bool "SCSI target support for SRP Transport Attributes"
- depends on SCSI_SRP_ATTRS
- depends on SCSI_TGT = y || SCSI_TGT = SCSI_SRP_ATTRS
- help
- If you want to use SCSI target mode drivers enable this option.
-
endmenu
menuconfig SCSI_LOWLEVEL
@@ -350,9 +303,9 @@ if SCSI_LOWLEVEL && SCSI
config ISCSI_TCP
tristate "iSCSI Initiator over TCP/IP"
depends on SCSI && INET
+ select CRC32
select CRYPTO
select CRYPTO_MD5
- select CRYPTO_CRC32C
select SCSI_ISCSI_ATTRS
help
The iSCSI Driver provides a host with the ability to access storage
@@ -373,7 +326,7 @@ config ISCSI_TCP
config ISCSI_BOOT_SYSFS
tristate "iSCSI Boot Sysfs Interface"
- default n
+ default n
help
This option enables support for exposing iSCSI boot information
via sysfs to userspace. If you wish to export this information,
@@ -387,13 +340,13 @@ source "drivers/scsi/be2iscsi/Kconfig"
config SGIWD93_SCSI
tristate "SGI WD93C93 SCSI Driver"
depends on SGI_HAS_WD93 && SCSI
- help
+ help
If you have a Western Digital WD93 SCSI controller on
an SGI MIPS system, say Y. Otherwise, say N.
config BLK_DEV_3W_XXXX_RAID
tristate "3ware 5/6/7/8xxx ATA-RAID support"
- depends on PCI && SCSI
+ depends on PCI && HAS_IOPORT && SCSI
help
3ware is the only hardware ATA-Raid product in Linux to date.
This card is 2,4, or 8 channel master mode support only.
@@ -407,6 +360,8 @@ config BLK_DEV_3W_XXXX_RAID
config SCSI_HPSA
tristate "HP Smart Array SCSI driver"
depends on PCI && SCSI
+ select CHECK_SIGNATURE
+ select SCSI_SAS_ATTRS
help
This driver supports HP Smart Array Controllers (circa 2009).
It is a SCSI alternative to the cciss driver, which is a block
@@ -436,21 +391,9 @@ config SCSI_3W_SAS
Please read the comments at the top of
<file:drivers/scsi/3w-sas.c>.
-config SCSI_7000FASST
- tristate "7000FASST SCSI support"
- depends on ISA && SCSI && ISA_DMA_API
- select CHECK_SIGNATURE
- help
- This driver supports the Western Digital 7000 SCSI host adapter
- family. Some information is in the source:
- <file:drivers/scsi/wd7000.c>.
-
- To compile this driver as a module, choose M here: the
- module will be called wd7000.
-
config SCSI_ACARD
tristate "ACARD SCSI support"
- depends on PCI && SCSI
+ depends on PCI && HAS_IOPORT && SCSI
help
This driver supports the ACARD SCSI host adapter.
Support Chip <ATP870 ATP876 ATP880 ATP885>
@@ -459,17 +402,18 @@ config SCSI_ACARD
config SCSI_AHA152X
tristate "Adaptec AHA152X/2825 support"
- depends on ISA && SCSI && !64BIT
+ depends on ISA && SCSI
+ depends on !HIGHMEM
select SCSI_SPI_ATTRS
select CHECK_SIGNATURE
- ---help---
+ help
This is a driver for the AHA-1510, AHA-1520, AHA-1522, and AHA-2825
SCSI host adapters. It also works for the AVA-1505, but the IRQ etc.
must be manually specified in this case.
It is explained in section 3.3 of the SCSI-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. You might also want to
- read the file <file:Documentation/scsi/aha152x.txt>.
+ read the file <file:Documentation/scsi/aha152x.rst>.
To compile this driver as a module, choose M here: the
module will be called aha152x.
@@ -477,7 +421,7 @@ config SCSI_AHA152X
config SCSI_AHA1542
tristate "Adaptec AHA1542 support"
depends on ISA && SCSI && ISA_DMA_API
- ---help---
+ help
This is support for a SCSI host adapter. It is explained in section
3.4 of the SCSI-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. Note that Trantor was
@@ -491,7 +435,7 @@ config SCSI_AHA1542
config SCSI_AHA1740
tristate "Adaptec AHA1740 support"
depends on EISA && SCSI
- ---help---
+ help
This is support for a SCSI host adapter. It is explained in section
3.5 of the SCSI-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. If it doesn't work out
@@ -507,73 +451,32 @@ config SCSI_AACRAID
help
This driver supports a variety of Dell, HP, Adaptec, IBM and
ICP storage products. For a list of supported products, refer
- to <file:Documentation/scsi/aacraid.txt>.
+ to <file:Documentation/scsi/aacraid.rst>.
To compile this driver as a module, choose M here: the module
will be called aacraid.
source "drivers/scsi/aic7xxx/Kconfig.aic7xxx"
-
-config SCSI_AIC7XXX_OLD
- tristate "Adaptec AIC7xxx support (old driver)"
- depends on (ISA || EISA || PCI ) && SCSI
- help
- WARNING This driver is an older aic7xxx driver and is no longer
- under active development. Adaptec, Inc. is writing a new driver to
- take the place of this one, and it is recommended that whenever
- possible, people should use the new Adaptec written driver instead
- of this one. This driver will eventually be phased out entirely.
-
- This is support for the various aic7xxx based Adaptec SCSI
- controllers. These include the 274x EISA cards; 284x VLB cards;
- 2902, 2910, 293x, 294x, 394x, 3985 and several other PCI and
- motherboard based SCSI controllers from Adaptec. It does not support
- the AAA-13x RAID controllers from Adaptec, nor will it likely ever
- support them. It does not support the 2920 cards from Adaptec that
- use the Future Domain SCSI controller chip. For those cards, you
- need the "Future Domain 16xx SCSI support" driver.
-
- In general, if the controller is based on an Adaptec SCSI controller
- chip from the aic777x series or the aic78xx series, this driver
- should work. The only exception is the 7810 which is specifically
- not supported (that's the RAID controller chip on the AAA-13x
- cards).
-
- Note that the AHA2920 SCSI host adapter is *not* supported by this
- driver; choose "Future Domain 16xx SCSI support" instead if you have
- one of those.
-
- Information on the configuration options for this controller can be
- found by checking the help file for each of the available
- configuration options. You should read
- <file:Documentation/scsi/aic7xxx_old.txt> at a minimum before
- contacting the maintainer with any questions. The SCSI-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>, can also
- be of great help.
-
- To compile this driver as a module, choose M here: the
- module will be called aic7xxx_old.
-
source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
source "drivers/scsi/aic94xx/Kconfig"
+source "drivers/scsi/hisi_sas/Kconfig"
source "drivers/scsi/mvsas/Kconfig"
-config SCSI_DPT_I2O
- tristate "Adaptec I2O RAID support "
- depends on SCSI && PCI && VIRT_TO_BUS
+config SCSI_MVUMI
+ tristate "Marvell UMI driver"
+ depends on SCSI && PCI
help
- This driver supports all of Adaptec's I2O based RAID controllers as
- well as the DPT SmartRaid V cards. This is an Adaptec maintained
- driver by Deanna Bonds. See <file:Documentation/scsi/dpti.txt>.
+ Module for Marvell Universal Message Interface(UMI) driver
To compile this driver as a module, choose M here: the
- module will be called dpt_i2o.
+ module will be called mvumi.
config SCSI_ADVANSYS
tristate "AdvanSys SCSI support"
- depends on SCSI && VIRT_TO_BUS
- depends on ISA || EISA || PCI
+ depends on SCSI
+ depends on (ISA || EISA || PCI) && HAS_IOPORT
+ depends on ISA_DMA_API || !ISA
help
This is a driver for all SCSI host adapters manufactured by
AdvanSys. It is documented in the kernel source in
@@ -582,18 +485,6 @@ config SCSI_ADVANSYS
To compile this driver as a module, choose M here: the
module will be called advansys.
-config SCSI_IN2000
- tristate "Always IN2000 SCSI support"
- depends on ISA && SCSI
- help
- This is support for an ISA bus SCSI host adapter. You'll find more
- information in <file:Documentation/scsi/in2000.txt>. If it doesn't work
- out of the box, you may have to change the jumpers for IRQ or
- address selection.
-
- To compile this driver as a module, choose M here: the
- module will be called in2000.
-
config SCSI_ARCMSR
tristate "ARECA (ARC11xx/12xx/13xx/16xx) SATA/SAS RAID Host Adapter"
depends on PCI && SCSI
@@ -607,22 +498,11 @@ config SCSI_ARCMSR
To compile this driver as a module, choose M here: the
module will be called arcmsr (modprobe arcmsr).
-config SCSI_ARCMSR_AER
- bool "Enable PCI Error Recovery Capability in Areca Driver(ARCMSR)"
- depends on SCSI_ARCMSR && PCIEAER
- default n
- help
- The advanced error reporting(AER) capability is "NOT" provided by
- ARC1200/1201/1202 SATA RAID controllers cards.
- If your card is one of ARC1200/1201/1202, please use the default setting, n.
- If your card is other models, you could pick it
- on condition that the kernel version is greater than 2.6.19.
- This function is maintained driver by Nick Cheng. If you have any
- problems or suggestion, you are welcome to contact with <nick.cheng@areca.com.tw>.
- To enable this function, choose Y here.
-
+source "drivers/scsi/esas2r/Kconfig"
source "drivers/scsi/megaraid/Kconfig.megaraid"
-source "drivers/scsi/mpt2sas/Kconfig"
+source "drivers/scsi/mpt3sas/Kconfig"
+source "drivers/scsi/mpi3mr/Kconfig"
+source "drivers/scsi/smartpqi/Kconfig"
config SCSI_HPTIOP
tristate "HighPoint RocketRAID 3xxx/4xxx Controller support"
@@ -636,13 +516,13 @@ config SCSI_HPTIOP
config SCSI_BUSLOGIC
tristate "BusLogic SCSI support"
- depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API && VIRT_TO_BUS
- ---help---
+ depends on SCSI && PCI && HAS_IOPORT
+ help
This is support for BusLogic MultiMaster and FlashPoint SCSI Host
Adapters. Consult the SCSI-HOWTO, available from
<http://www.tldp.org/docs.html#howto>, and the files
- <file:Documentation/scsi/BusLogic.txt> and
- <file:Documentation/scsi/FlashPoint.txt> for more information.
+ <file:Documentation/scsi/BusLogic.rst> and
+ <file:Documentation/scsi/FlashPoint.rst> for more information.
Note that support for FlashPoint is only available for 32-bit
x86 configurations.
@@ -651,13 +531,44 @@ config SCSI_BUSLOGIC
config SCSI_FLASHPOINT
bool "FlashPoint support"
- depends on SCSI_BUSLOGIC && PCI && X86_32
+ depends on SCSI_BUSLOGIC && PCI && HAS_IOPORT
help
This option allows you to add FlashPoint support to the
BusLogic SCSI driver. The FlashPoint SCCB Manager code is
substantial, so users of MultiMaster Host Adapters may not
wish to include it.
+config SCSI_MYRB
+ tristate "Mylex DAC960/DAC1100 PCI RAID Controller (Block Interface)"
+ depends on PCI
+ select RAID_ATTRS
+ help
+ This driver adds support for the Mylex DAC960, AcceleRAID, and
+ eXtremeRAID PCI RAID controllers. This driver supports the
+ older, block based interface.
+ This driver is a reimplementation of the original DAC960
+ driver. If you have used the DAC960 driver you should enable
+ this module.
+
+ To compile this driver as a module, choose M here: the
+ module will be called myrb.
+
+config SCSI_MYRS
+ tristate "Mylex DAC960/DAC1100 PCI RAID Controller (SCSI Interface)"
+ depends on PCI
+ depends on !CPU_BIG_ENDIAN || COMPILE_TEST
+ select RAID_ATTRS
+ help
+ This driver adds support for the Mylex DAC960, AcceleRAID, and
+ eXtremeRAID PCI RAID controllers. This driver supports the
+ newer, SCSI-based interface only.
+ This driver is a reimplementation of the original DAC960
+ driver. If you have used the DAC960 driver you should enable
+ this module.
+
+ To compile this driver as a module, choose M here: the
+ module will be called myrs.
+
config VMWARE_PVSCSI
tristate "VMware PVSCSI driver support"
depends on PCI && SCSI && X86
@@ -666,134 +577,94 @@ config VMWARE_PVSCSI
To compile this driver as a module, choose M here: the
module will be called vmw_pvscsi.
+config XEN_SCSI_FRONTEND
+ tristate "XEN SCSI frontend driver"
+ depends on SCSI && XEN
+ select XEN_XENBUS_FRONTEND
+ help
+ The XEN SCSI frontend driver allows the kernel to access SCSI Devices
+ within another guest OS (usually Dom0).
+ Only needed if the kernel is running in a XEN guest and generic
+ SCSI access to a device is needed.
+
+config HYPERV_STORAGE
+ tristate "Microsoft Hyper-V virtual storage driver"
+ depends on SCSI && HYPERV_VMBUS
+ depends on m || SCSI_FC_ATTRS != m
+ default HYPERV
+ help
+ Select this option to enable the Hyper-V virtual storage driver.
+
config LIBFC
tristate "LibFC module"
- select SCSI_FC_ATTRS
+ depends on SCSI_FC_ATTRS
select CRC32
- ---help---
+ help
Fibre Channel library module
config LIBFCOE
tristate "LibFCoE module"
- select LIBFC
- ---help---
+ depends on LIBFC
+ help
Library for Fibre Channel over Ethernet module
config FCOE
tristate "FCoE module"
depends on PCI
- select LIBFCOE
- ---help---
+ depends on LIBFCOE
+ help
Fibre Channel over Ethernet module
config FCOE_FNIC
tristate "Cisco FNIC Driver"
depends on PCI && X86
- select LIBFCOE
+ depends on LIBFCOE
help
This is support for the Cisco PCI-Express FCoE HBA.
To compile this driver as a module, choose M here and read
- <file:Documentation/scsi/scsi.txt>.
+ <file:Documentation/scsi/scsi.rst>.
The module will be called fnic.
-config SCSI_DMX3191D
- tristate "DMX3191D SCSI support"
+config SCSI_SNIC
+ tristate "Cisco SNIC Driver"
depends on PCI && SCSI
- select SCSI_SPI_ATTRS
help
- This is support for Domex DMX3191D SCSI Host Adapters.
+ This is support for the Cisco PCI-Express SCSI HBA.
- To compile this driver as a module, choose M here: the
- module will be called dmx3191d.
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.rst>.
+ The module will be called snic.
-config SCSI_DTC3280
- tristate "DTC3180/3280 SCSI support"
- depends on ISA && SCSI
- select SCSI_SPI_ATTRS
- select CHECK_SIGNATURE
+config SCSI_SNIC_DEBUG_FS
+ bool "Cisco SNIC Driver Debugfs Support"
+ depends on SCSI_SNIC && DEBUG_FS
help
- This is support for DTC 3180/3280 SCSI Host Adapters. Please read
- the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>, and the file
- <file:Documentation/scsi/dtc3x80.txt>.
+ This enables to list debugging information from SNIC Driver
+ available via debugfs file system
- To compile this driver as a module, choose M here: the
- module will be called dtc.
-
-config SCSI_EATA
- tristate "EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support"
- depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
- ---help---
- This driver supports all EATA/DMA-compliant SCSI host adapters. DPT
- ISA and all EISA I/O addresses are probed looking for the "EATA"
- signature. The addresses of all the PCI SCSI controllers reported
- by the PCI subsystem are probed as well.
-
- You want to read the start of <file:drivers/scsi/eata.c> and the
- SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
+config SCSI_DMX3191D
+ tristate "DMX3191D SCSI support"
+ depends on PCI && HAS_IOPORT && SCSI
+ select SCSI_SPI_ATTRS
+ help
+ This is support for Domex DMX3191D SCSI Host Adapters.
To compile this driver as a module, choose M here: the
- module will be called eata.
-
-config SCSI_EATA_TAGGED_QUEUE
- bool "enable tagged command queueing"
- depends on SCSI_EATA
- help
- This is a feature of SCSI-2 which improves performance: the host
- adapter can send several SCSI commands to a device's queue even if
- previous commands haven't finished yet.
- This is equivalent to the "eata=tc:y" boot option.
+ module will be called dmx3191d.
-config SCSI_EATA_LINKED_COMMANDS
- bool "enable elevator sorting"
- depends on SCSI_EATA
- help
- This option enables elevator sorting for all probed SCSI disks and
- CD-ROMs. It definitely reduces the average seek distance when doing
- random seeks, but this does not necessarily result in a noticeable
- performance improvement: your mileage may vary...
- This is equivalent to the "eata=lc:y" boot option.
+config SCSI_FDOMAIN
+ tristate
+ depends on SCSI
-config SCSI_EATA_MAX_TAGS
- int "maximum number of queued commands"
- depends on SCSI_EATA
- default "16"
+config SCSI_FDOMAIN_PCI
+ tristate "Future Domain TMC-3260/AHA-2920A PCI SCSI support"
+ depends on PCI && HAS_IOPORT && SCSI
+ select SCSI_FDOMAIN
help
- This specifies how many SCSI commands can be maximally queued for
- each probed SCSI device. You should reduce the default value of 16
- only if you have disks with buggy or limited tagged command support.
- Minimum is 2 and maximum is 62. This value is also the window size
- used by the elevator sorting option above. The effective value used
- by the driver for each probed SCSI device is reported at boot time.
- This is equivalent to the "eata=mq:8" boot option.
-
-config SCSI_EATA_PIO
- tristate "EATA-PIO (old DPT PM2001, PM2012A) support"
- depends on (ISA || EISA || PCI) && SCSI && BROKEN
- ---help---
- This driver supports all EATA-PIO protocol compliant SCSI Host
- Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant
- host adapters could also use this driver but are discouraged from
- doing so, since this driver only supports hard disks and lacks
- numerous features. You might want to have a look at the SCSI-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here: the
- module will be called eata_pio.
-
-config SCSI_FUTURE_DOMAIN
- tristate "Future Domain 16xx SCSI/AHA-2920A support"
- depends on (ISA || PCI) && SCSI
- select CHECK_SIGNATURE
- ---help---
- This is support for Future Domain's 16-bit SCSI host adapters
- (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and
- other adapters based on the Future Domain chipsets (Quantum
- ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board).
- It is explained in section 3.7 of the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
+ This is support for Future Domain's PCI SCSI host adapters (TMC-3260)
+ and other adapters with PCI bus based on the Future Domain chipsets
+ (Adaptec AHA-2920A).
NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip
and should use the aic7xxx driver ("Adaptec AIC7xxx chipset SCSI
@@ -801,168 +672,48 @@ config SCSI_FUTURE_DOMAIN
Adaptec AHA-2920A boards with a Future Domain chip on them.
To compile this driver as a module, choose M here: the
- module will be called fdomain.
-
-config SCSI_FD_MCS
- tristate "Future Domain MCS-600/700 SCSI support"
- depends on MCA_LEGACY && SCSI
- ---help---
- This is support for Future Domain MCS 600/700 MCA SCSI adapters.
- Some PS/2 computers are equipped with IBM Fast SCSI Adapter/A which
- is identical to the MCS 700 and hence also supported by this driver.
- This driver also supports the Reply SB16/SCSI card (the SCSI part).
- It supports multiple adapters in the same system.
-
- To compile this driver as a module, choose M here: the
- module will be called fd_mcs.
-
-config SCSI_GDTH
- tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support"
- depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
- ---help---
- Formerly called GDT SCSI Disk Array Controller Support.
+ module will be called fdomain_pci.
- This is a driver for RAID/SCSI Disk Array Controllers (EISA/ISA/PCI)
- manufactured by Intel Corporation/ICP vortex GmbH. It is documented
- in the kernel source in <file:drivers/scsi/gdth.c> and
- <file:drivers/scsi/gdth.h>.
+config SCSI_FDOMAIN_ISA
+ tristate "Future Domain 16xx ISA SCSI support"
+ depends on ISA && SCSI
+ select CHECK_SIGNATURE
+ select SCSI_FDOMAIN
+ help
+ This is support for Future Domain's 16-bit SCSI host adapters
+ (TMC-1660/1680, TMC-1650/1670, TMC-1610M/MER/MEX) and other adapters
+ with ISA bus based on the Future Domain chipsets (Quantum ISA-200S,
+ ISA-250MG; and at least one IBM board).
To compile this driver as a module, choose M here: the
- module will be called gdth.
+ module will be called fdomain_isa.
config SCSI_ISCI
tristate "Intel(R) C600 Series Chipset SAS Controller"
depends on PCI && SCSI
depends on X86
- # (temporary): known alpha quality driver
- depends on EXPERIMENTAL
select SCSI_SAS_LIBSAS
- ---help---
+ help
This driver supports the 6Gb/s SAS capabilities of the storage
control unit found in the Intel(R) C600 series chipset.
- The experimental tag will be removed after the driver exits alpha
-
config SCSI_GENERIC_NCR5380
- tristate "Generic NCR5380/53c400 SCSI PIO support"
- depends on ISA && SCSI
+ tristate "Generic NCR5380/53c400 SCSI ISA card support"
+ depends on ISA && SCSI && HAS_IOPORT_MAP
select SCSI_SPI_ATTRS
- ---help---
- This is a driver for the old NCR 53c80 series of SCSI controllers
- on boards using PIO. Most boards such as the Trantor T130 fit this
- category, along with a large number of ISA 8bit controllers shipped
- for free with SCSI scanners. If you have a PAS16, T128 or DMX3191
- you should select the specific driver for that card rather than
- generic 5380 support.
-
- It is explained in section 3.8 of the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. If it doesn't work out
- of the box, you may have to change some settings in
- <file:drivers/scsi/g_NCR5380.h>.
+ help
+ This is a driver for old ISA card SCSI controllers based on a
+ NCR 5380, 53C80, 53C400, 53C400A, or DTC 436 device.
+ Most boards such as the Trantor T130 fit this category, as do
+ various 8-bit and 16-bit ISA cards bundled with SCSI scanners.
To compile this driver as a module, choose M here: the
module will be called g_NCR5380.
-config SCSI_GENERIC_NCR5380_MMIO
- tristate "Generic NCR5380/53c400 SCSI MMIO support"
- depends on ISA && SCSI
- select SCSI_SPI_ATTRS
- ---help---
- This is a driver for the old NCR 53c80 series of SCSI controllers
- on boards using memory mapped I/O.
- It is explained in section 3.8 of the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. If it doesn't work out
- of the box, you may have to change some settings in
- <file:drivers/scsi/g_NCR5380.h>.
-
- To compile this driver as a module, choose M here: the
- module will be called g_NCR5380_mmio.
-
-config SCSI_GENERIC_NCR53C400
- bool "Enable NCR53c400 extensions"
- depends on SCSI_GENERIC_NCR5380
- help
- This enables certain optimizations for the NCR53c400 SCSI cards.
- You might as well try it out. Note that this driver will only probe
- for the Trantor T130B in its default configuration; you might have
- to pass a command line option to the kernel at boot time if it does
- not detect your card. See the file
- <file:Documentation/scsi/g_NCR5380.txt> for details.
-
-config SCSI_IBMMCA
- tristate "IBMMCA SCSI support"
- depends on MCA && SCSI
- ---help---
- This is support for the IBM SCSI adapter found in many of the PS/2
- series computers. These machines have an MCA bus, so you need to
- answer Y to "MCA support" as well and read
- <file:Documentation/mca.txt>.
-
- If the adapter isn't found during boot (a common problem for models
- 56, 57, 76, and 77) you'll need to use the 'ibmmcascsi=<pun>' kernel
- option, where <pun> is the id of the SCSI subsystem (usually 7, but
- if that doesn't work check your reference diskette). Owners of
- model 95 with a LED-matrix-display can in addition activate some
- activity info like under OS/2, but more informative, by setting
- 'ibmmcascsi=display' as an additional kernel parameter. Try "man
- bootparam" or see the documentation of your boot loader about how to
- pass options to the kernel.
-
- To compile this driver as a module, choose M here: the
- module will be called ibmmca.
-
-config IBMMCA_SCSI_ORDER_STANDARD
- bool "Standard SCSI-order"
- depends on SCSI_IBMMCA
- ---help---
- In the PC-world and in most modern SCSI-BIOS-setups, SCSI-hard disks
- are assigned to the drive letters, starting with the lowest SCSI-id
- (physical number -- pun) to be drive C:, as seen from DOS and
- similar operating systems. When looking into papers describing the
- ANSI-SCSI-standard, this assignment of drives appears to be wrong.
- The SCSI-standard follows a hardware-hierarchy which says that id 7
- has the highest priority and id 0 the lowest. Therefore, the host
- adapters are still today everywhere placed as SCSI-id 7 by default.
- In the SCSI-standard, the drive letters express the priority of the
- disk. C: should be the hard disk, or a partition on it, with the
- highest priority. This must therefore be the disk with the highest
- SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the
- original definition of the SCSI-standard as also industrial- and
- process-control-machines, like VME-CPUs running under realtime-OSes
- (e.g. LynxOS, OS9) do.
-
- If you like to run Linux on your MCA-machine with the same
- assignment of hard disks as seen from e.g. DOS or OS/2 on your
- machine, which is in addition conformant to the SCSI-standard, you
- must say Y here. This is also necessary for MCA-Linux users who want
- to keep downward compatibility to older releases of the
- IBM-MCA-SCSI-driver (older than driver-release 2.00 and older than
- June 1997).
-
- If you like to have the lowest SCSI-id assigned as drive C:, as
- modern SCSI-BIOSes do, which does not conform to the standard, but
- is widespread and common in the PC-world of today, you must say N
- here. If unsure, say Y.
-
-config IBMMCA_SCSI_DEV_RESET
- bool "Reset SCSI-devices at boottime"
- depends on SCSI_IBMMCA
- ---help---
- By default, SCSI-devices are reset when the machine is powered on.
- However, some devices exist, like special-control-devices,
- SCSI-CNC-machines, SCSI-printer or scanners of older type, that do
- not reset when switched on. If you say Y here, each device connected
- to your SCSI-bus will be issued a reset-command after it has been
- probed, while the kernel is booting. This may cause problems with
- more modern devices, like hard disks, which do not appreciate these
- reset commands, and can cause your system to hang. So say Y only if
- you know that one of your older devices needs it; N is the safe
- answer.
-
config SCSI_IPS
tristate "IBM ServeRAID support"
- depends on PCI && SCSI
- ---help---
+ depends on PCI && HAS_IOPORT && SCSI
+ help
This is support for the IBM ServeRAID hardware RAID controllers.
See <http://www.developer.ibm.com/welcome/netfinity/serveraid.html>
and <http://www-947.ibm.com/support/entry/portal/docdisplay?brand=5000008&lndocid=SERV-RAID>
@@ -975,33 +726,35 @@ config SCSI_IPS
config SCSI_IBMVSCSI
tristate "IBM Virtual SCSI support"
- depends on PPC_PSERIES || PPC_ISERIES
+ depends on PPC_PSERIES
select SCSI_SRP_ATTRS
- select VIOPATH if PPC_ISERIES
help
This is the IBM POWER Virtual SCSI Client
To compile this driver as a module, choose M here: the
- module will be called ibmvscsic.
+ module will be called ibmvscsi.
config SCSI_IBMVSCSIS
tristate "IBM Virtual SCSI Server support"
- depends on PPC_PSERIES && SCSI_SRP && SCSI_SRP_TGT_ATTRS
+ depends on PPC_PSERIES && TARGET_CORE && SCSI && PCI
help
- This is the SRP target driver for IBM pSeries virtual environments.
+ This is the IBM POWER Virtual SCSI Target Server
+ This driver uses the SRP protocol for communication between servers
+ guest and/or the host that run on the same server.
+ More information on VSCSI protocol can be found at www.power.org
- The userspace component needed to initialize the driver and
- documentation can be found:
+ The userspace configuration needed to initialize the driver can be
+ be found here:
- http://stgt.berlios.de/
+ https://github.com/powervm/ibmvscsis/wiki/Configuration
To compile this driver as a module, choose M here: the
- module will be called ibmvstgt.
+ module will be called ibmvscsis.
config SCSI_IBMVFC
tristate "IBM Virtual FC support"
depends on PPC_PSERIES && SCSI
- select SCSI_FC_ATTRS
+ depends on SCSI_FC_ATTRS
help
This is the IBM POWER Virtual FC Client
@@ -1019,7 +772,7 @@ config SCSI_IBMVFC_TRACE
config SCSI_INITIO
tristate "Initio 9100U(W) support"
- depends on PCI && SCSI
+ depends on PCI && HAS_IOPORT && SCSI
help
This is support for the Initio 91XXU(W) SCSI host adapter. Please
read the SCSI-HOWTO, available from
@@ -1030,7 +783,7 @@ config SCSI_INITIO
config SCSI_INIA100
tristate "Initio INI-A100U2W support"
- depends on PCI && SCSI
+ depends on PCI && HAS_IOPORT && SCSI
help
This is support for the Initio INI-A100U2W SCSI host adapter.
Please read the SCSI-HOWTO, available from
@@ -1042,7 +795,9 @@ config SCSI_INIA100
config SCSI_PPA
tristate "IOMEGA parallel port (ppa - older drives)"
depends on SCSI && PARPORT_PC
- ---help---
+ depends on HAS_IOPORT
+ depends on !HIGHMEM
+ help
This driver supports older versions of IOMEGA's parallel port ZIP
drive (a 100 MB removable media device).
@@ -1056,7 +811,7 @@ config SCSI_PPA
newer drives)", below.
For more information about this driver and how to use it you should
- read the file <file:Documentation/scsi/ppa.txt>. You should also read
+ read the file <file:Documentation/scsi/ppa.rst>. You should also read
the SCSI-HOWTO, which is available from
<http://www.tldp.org/docs.html#howto>. If you use this driver,
you will still be able to use the parallel port for other tasks,
@@ -1069,7 +824,8 @@ config SCSI_PPA
config SCSI_IMM
tristate "IOMEGA parallel port (imm - newer drives)"
depends on SCSI && PARPORT_PC
- ---help---
+ depends on !HIGHMEM
+ help
This driver supports newer versions of IOMEGA's parallel port ZIP
drive (a 100 MB removable media device).
@@ -1083,7 +839,7 @@ config SCSI_IMM
here and Y to "IOMEGA Parallel Port (ppa - older drives)", above.
For more information about this driver and how to use it you should
- read the file <file:Documentation/scsi/ppa.txt>. You should also read
+ read the file <file:Documentation/scsi/ppa.rst>. You should also read
the SCSI-HOWTO, which is available from
<http://www.tldp.org/docs.html#howto>. If you use this driver,
you will still be able to use the parallel port for other tasks,
@@ -1093,21 +849,6 @@ config SCSI_IMM
To compile this driver as a module, choose M here: the
module will be called imm.
-config SCSI_IZIP_EPP16
- bool "ppa/imm option - Use slow (but safe) EPP-16"
- depends on SCSI_PPA || SCSI_IMM
- ---help---
- EPP (Enhanced Parallel Port) is a standard for parallel ports which
- allows them to act as expansion buses that can handle up to 64
- peripheral devices.
-
- Some parallel port chipsets are slower than their motherboard, and
- so we have to control the state of the chipset's FIFO queue every
- now and then to avoid data loss. This will be done if you say Y
- here.
-
- Generally, saying Y is the safe option and slows things down a bit.
-
config SCSI_IZIP_SLOW_CTR
bool "ppa/imm option - Assume slow parport control register"
depends on SCSI_PPA || SCSI_IMM
@@ -1122,30 +863,6 @@ config SCSI_IZIP_SLOW_CTR
Generally, saying N is fine.
-config SCSI_NCR53C406A
- tristate "NCR53c406a SCSI support"
- depends on ISA && SCSI
- help
- This is support for the NCR53c406a SCSI host adapter. For user
- configurable parameters, check out <file:drivers/scsi/NCR53c406a.c>
- in the kernel source. Also read the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here: the
- module will be called NCR53c406.
-
-config SCSI_NCR_D700
- tristate "NCR Dual 700 MCA SCSI support"
- depends on MCA && SCSI
- select SCSI_SPI_ATTRS
- help
- This is a driver for the MicroChannel Dual 700 card produced by
- NCR and commonly used in 345x/35xx/4100 class machines. It always
- tries to negotiate sync and uses tag command queueing.
-
- Unless you have an NCR manufactured machine, the chances are that
- you do not have this SCSI card, so say N.
-
config SCSI_LASI700
tristate "HP Lasi SCSI support for 53c700/710"
depends on GSC && SCSI
@@ -1166,13 +883,13 @@ config SCSI_SNI_53C710
config 53C700_LE_ON_BE
bool
- depends on SCSI_LASI700
+ depends on SCSI_LASI700 || SCSI_SNI_53C710
default y
config SCSI_STEX
tristate "Promise SuperTrak EX Series support"
depends on PCI && SCSI
- ---help---
+ help
This driver supports Promise SuperTrak EX series storage controllers.
Promise provides Linux RAID configuration utility for these
@@ -1190,21 +907,21 @@ config SCSI_SYM53C8XX_2
tristate "SYM53C8XX Version 2 SCSI support"
depends on PCI && SCSI
select SCSI_SPI_ATTRS
- ---help---
+ help
This driver supports the whole NCR53C8XX/SYM53C8XX family of
PCI-SCSI controllers. It also supports the subset of LSI53C10XX
Ultra-160 controllers that are based on the SYM53C8XX SCRIPTS
language. It does not support LSI53C10XX Ultra-320 PCI-X SCSI
controllers; you need to use the Fusion MPT driver for that.
- Please read <file:Documentation/scsi/sym53c8xx_2.txt> for more
+ Please read <file:Documentation/scsi/sym53c8xx_2.rst> for more
information.
config SCSI_SYM53C8XX_DMA_ADDRESSING_MODE
int "DMA addressing mode"
depends on SCSI_SYM53C8XX_2
default "1"
- ---help---
+ help
This option only applies to PCI-SCSI chips that are PCI DAC
capable (875A, 895A, 896, 1010-33, 1010-66, 1000).
@@ -1255,9 +972,11 @@ config SCSI_SYM53C8XX_MMIO
config SCSI_IPR
tristate "IBM Power Linux RAID adapter support"
- depends on PCI && SCSI && ATA
+ depends on PCI && SCSI
select FW_LOADER
- ---help---
+ select IRQ_POLL
+ select SGL_ALLOC
+ help
This driver supports the IBM Power Linux family RAID adapters.
This includes IBM pSeries 5712, 5703, 5709, and 570A, as well
as IBM iSeries 5702, 5703, 5709, and 570A.
@@ -1291,23 +1010,11 @@ config SCSI_ZALON
used on the add-in Bluefish, Barracuda & Shrike SCSI cards.
Say Y here if you have one of these machines or cards.
-config SCSI_NCR_Q720
- tristate "NCR Quad 720 MCA SCSI support"
- depends on MCA && SCSI
- select SCSI_SPI_ATTRS
- help
- This is a driver for the MicroChannel Quad 720 card produced by
- NCR and commonly used in 345x/35xx/4100 class machines. It always
- tries to negotiate sync and uses tag command queueing.
-
- Unless you have an NCR manufactured machine, the chances are that
- you do not have this SCSI card, so say N.
-
config SCSI_NCR53C8XX_DEFAULT_TAGS
int "default tagged command queue depth"
- depends on SCSI_ZALON || SCSI_NCR_Q720
+ depends on SCSI_ZALON
default "8"
- ---help---
+ help
"Tagged command queuing" is a feature of SCSI-2 which improves
performance: the host adapter can send several SCSI commands to a
device's queue even if previous commands haven't finished yet.
@@ -1331,9 +1038,9 @@ config SCSI_NCR53C8XX_DEFAULT_TAGS
config SCSI_NCR53C8XX_MAX_TAGS
int "maximum number of queued commands"
- depends on SCSI_ZALON || SCSI_NCR_Q720
+ depends on SCSI_ZALON
default "32"
- ---help---
+ help
This option allows you to specify the maximum number of commands
that can be queued to any device, when tagged command queuing is
possible. The default value is 32. Minimum is 2, maximum is 64.
@@ -1348,9 +1055,9 @@ config SCSI_NCR53C8XX_MAX_TAGS
config SCSI_NCR53C8XX_SYNC
int "synchronous transfers frequency in MHz"
- depends on SCSI_ZALON || SCSI_NCR_Q720
+ depends on SCSI_ZALON
default "20"
- ---help---
+ help
The SCSI Parallel Interface-2 Standard defines 5 classes of transfer
rates: FAST-5, FAST-10, FAST-20, FAST-40 and FAST-80. The numbers
are respectively the maximum data transfer rates in mega-transfers
@@ -1382,7 +1089,7 @@ config SCSI_NCR53C8XX_SYNC
config SCSI_NCR53C8XX_NO_DISCONNECT
bool "not allow targets to disconnect"
- depends on (SCSI_ZALON || SCSI_NCR_Q720) && SCSI_NCR53C8XX_DEFAULT_TAGS=0
+ depends on SCSI_ZALON && SCSI_NCR53C8XX_DEFAULT_TAGS=0
help
This option is only provided for safety if you suspect some SCSI
device of yours to not support properly the target-disconnect
@@ -1390,24 +1097,10 @@ config SCSI_NCR53C8XX_NO_DISCONNECT
not allow targets to disconnect is not reasonable if there is more
than 1 device on a SCSI bus. The normal answer therefore is N.
-config SCSI_PAS16
- tristate "PAS16 SCSI support"
- depends on ISA && SCSI
- select SCSI_SPI_ATTRS
- ---help---
- This is support for a SCSI host adapter. It is explained in section
- 3.10 of the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. If it doesn't work out
- of the box, you may have to change some settings in
- <file:drivers/scsi/pas16.h>.
-
- To compile this driver as a module, choose M here: the
- module will be called pas16.
-
config SCSI_QLOGIC_FAS
tristate "Qlogic FAS SCSI support"
depends on ISA && SCSI
- ---help---
+ help
This is a driver for the ISA, VLB, and PCMCIA versions of the Qlogic
FastSCSI! cards as well as any other card based on the FASXX chip
(including the Control Concepts SCSI/IDE/SIO/PIO/FDC cards).
@@ -1417,7 +1110,7 @@ config SCSI_QLOGIC_FAS
SCSI support"), below.
Information about this driver is contained in
- <file:Documentation/scsi/qlogicfas.txt>. You should also read the
+ <file:Documentation/scsi/qlogicfas.rst>. You should also read the
SCSI-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
@@ -1447,11 +1140,18 @@ config SCSI_QLOGICPTI
source "drivers/scsi/qla2xxx/Kconfig"
source "drivers/scsi/qla4xxx/Kconfig"
+source "drivers/scsi/qedi/Kconfig"
+source "drivers/scsi/qedf/Kconfig"
config SCSI_LPFC
tristate "Emulex LightPulse Fibre Channel Support"
depends on PCI && SCSI
- select SCSI_FC_ATTRS
+ depends on CPU_FREQ
+ depends on SCSI_FC_ATTRS
+ depends on NVME_TARGET_FC || NVME_TARGET_FC=n
+ depends on NVME_FC || NVME_FC=n
+ select CRC_T10DIF
+ select IRQ_POLL
help
This lpfc driver supports the Emulex LightPulse
Family of Fibre Channel PCI host adapters.
@@ -1463,150 +1163,52 @@ config SCSI_LPFC_DEBUG_FS
This makes debugging information from the lpfc driver
available via the debugfs filesystem.
+source "drivers/scsi/elx/Kconfig"
+
config SCSI_SIM710
tristate "Simple 53c710 SCSI support (Compaq, NCR machines)"
- depends on (EISA || MCA) && SCSI
+ depends on EISA && SCSI
select SCSI_SPI_ATTRS
- ---help---
+ help
This driver is for NCR53c710 based SCSI host adapters.
- It currently supports Compaq EISA cards and NCR MCA cards
-
-config SCSI_SYM53C416
- tristate "Symbios 53c416 SCSI support"
- depends on ISA && SCSI
- ---help---
- This is support for the sym53c416 SCSI host adapter, the SCSI
- adapter that comes with some HP scanners. This driver requires that
- the sym53c416 is configured first using some sort of PnP
- configuration program (e.g. isapnp) or by a PnP aware BIOS. If you
- are using isapnp then you need to compile this driver as a module
- and then load it using insmod after isapnp has run. The parameters
- of the configured card(s) should be passed to the driver. The format
- is:
-
- insmod sym53c416 sym53c416=<base>,<irq> [sym53c416_1=<base>,<irq>]
-
- To compile this driver as a module, choose M here: the
- module will be called sym53c416.
+ It currently supports Compaq EISA cards.
config SCSI_DC395x
- tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support (EXPERIMENTAL)"
- depends on PCI && SCSI && EXPERIMENTAL
- ---help---
+ tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support"
+ depends on PCI && HAS_IOPORT && SCSI
+ select SCSI_SPI_ATTRS
+ help
This driver supports PCI SCSI host adapters based on the ASIC
TRM-S1040 chip, e.g Tekram DC395(U/UW/F) and DC315(U) variants.
This driver works, but is still in experimental status. So better
have a bootable disk and a backup in case of emergency.
- Documentation can be found in <file:Documentation/scsi/dc395x.txt>.
+ Documentation can be found in <file:Documentation/scsi/dc395x.rst>.
To compile this driver as a module, choose M here: the
module will be called dc395x.
-config SCSI_DC390T
- tristate "Tekram DC390(T) and Am53/79C974 SCSI support"
+config SCSI_AM53C974
+ tristate "Tekram DC390(T) and Am53/79C974 SCSI support (new driver)"
depends on PCI && SCSI
- ---help---
+ select SCSI_SPI_ATTRS
+ help
This driver supports PCI SCSI host adapters based on the Am53C974A
chip, e.g. Tekram DC390(T), DawiControl 2974 and some onboard
PCscsi/PCnet (Am53/79C974) solutions.
-
- Documentation can be found in <file:Documentation/scsi/tmscsim.txt>.
+ This is a new implementation base on the generic esp_scsi driver.
Note that this driver does NOT support Tekram DC390W/U/F, which are
based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those.
To compile this driver as a module, choose M here: the
- module will be called tmscsim.
-
-config SCSI_T128
- tristate "Trantor T128/T128F/T228 SCSI support"
- depends on ISA && SCSI
- select SCSI_SPI_ATTRS
- select CHECK_SIGNATURE
- ---help---
- This is support for a SCSI host adapter. It is explained in section
- 3.11 of the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. If it doesn't work out
- of the box, you may have to change some settings in
- <file:drivers/scsi/t128.h>. Note that Trantor was purchased by
- Adaptec, and some former Trantor products are being sold under the
- Adaptec name.
-
- To compile this driver as a module, choose M here: the
- module will be called t128.
-
-config SCSI_U14_34F
- tristate "UltraStor 14F/34F support"
- depends on ISA && SCSI && ISA_DMA_API
- ---help---
- This is support for the UltraStor 14F and 34F SCSI-2 host adapters.
- The source at <file:drivers/scsi/u14-34f.c> contains some
- information about this hardware. If the driver doesn't work out of
- the box, you may have to change some settings in
- <file: drivers/scsi/u14-34f.c>. Read the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Note that there is also
- another driver for the same hardware: "UltraStor SCSI support",
- below. You should say Y to both only if you want 24F support as
- well.
-
- To compile this driver as a module, choose M here: the
- module will be called u14-34f.
-
-config SCSI_U14_34F_TAGGED_QUEUE
- bool "enable tagged command queueing"
- depends on SCSI_U14_34F
- help
- This is a feature of SCSI-2 which improves performance: the host
- adapter can send several SCSI commands to a device's queue even if
- previous commands haven't finished yet.
- This is equivalent to the "u14-34f=tc:y" boot option.
-
-config SCSI_U14_34F_LINKED_COMMANDS
- bool "enable elevator sorting"
- depends on SCSI_U14_34F
- help
- This option enables elevator sorting for all probed SCSI disks and
- CD-ROMs. It definitely reduces the average seek distance when doing
- random seeks, but this does not necessarily result in a noticeable
- performance improvement: your mileage may vary...
- This is equivalent to the "u14-34f=lc:y" boot option.
-
-config SCSI_U14_34F_MAX_TAGS
- int "maximum number of queued commands"
- depends on SCSI_U14_34F
- default "8"
- help
- This specifies how many SCSI commands can be maximally queued for
- each probed SCSI device. You should reduce the default value of 8
- only if you have disks with buggy or limited tagged command support.
- Minimum is 2 and maximum is 14. This value is also the window size
- used by the elevator sorting option above. The effective value used
- by the driver for each probed SCSI device is reported at boot time.
- This is equivalent to the "u14-34f=mq:8" boot option.
-
-config SCSI_ULTRASTOR
- tristate "UltraStor SCSI support"
- depends on X86 && ISA && SCSI
- ---help---
- This is support for the UltraStor 14F, 24F and 34F SCSI-2 host
- adapter family. This driver is explained in section 3.12 of the
- SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. If it doesn't work out
- of the box, you may have to change some settings in
- <file:drivers/scsi/ultrastor.h>.
-
- Note that there is also another driver for the same hardware:
- "UltraStor 14F/34F support", above.
-
- To compile this driver as a module, choose M here: the
- module will be called ultrastor.
+ module will be called am53c974.
config SCSI_NSP32
tristate "Workbit NinjaSCSI-32Bi/UDE support"
- depends on PCI && SCSI && !64BIT
+ depends on PCI && SCSI && !64BIT && HAS_IOPORT
help
This is support for the Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus
SCSI host adapter. Please read the SCSI-HOWTO, available from
@@ -1615,19 +1217,26 @@ config SCSI_NSP32
To compile this driver as a module, choose M here: the
module will be called nsp32.
+config SCSI_WD719X
+ tristate "Western Digital WD7193/7197/7296 support"
+ depends on PCI && SCSI
+ select EEPROM_93CX6
+ help
+ This is a driver for Western Digital WD7193, WD7197 and WD7296 PCI
+ SCSI controllers (based on WD33C296A chip).
+
config SCSI_DEBUG
- tristate "SCSI debugging host simulator"
+ tristate "SCSI debugging host and device simulator"
depends on SCSI
select CRC_T10DIF
help
- This is a host adapter simulator that can simulate multiple hosts
- each with multiple dummy SCSI devices (disks). It defaults to one
- host adapter with one dummy SCSI disk. Each dummy disk uses kernel
- RAM as storage (i.e. it is a ramdisk). To save space when multiple
- dummy disks are simulated, they share the same kernel RAM for
- their storage. See <http://sg.danny.cz/sg/sdebug26.html> for more
- information. This driver is primarily of use to those testing the
- SCSI and block subsystems. If unsure, say N.
+ This pseudo driver simulates one or more hosts (SCSI initiators),
+ each with one or more targets, each with one or more logical units.
+ Defaults to one of each, creating a small RAM disk device. Many
+ parameters found in the /sys/bus/pseudo/drivers/scsi_debug
+ directory can be tweaked at run time.
+ See <http://sg.danny.cz/sg/sdebug26.html> for more information.
+ Mainly used for testing and best as a module. If unsure, say N.
config SCSI_MESH
tristate "MESH (Power Mac internal SCSI) support"
@@ -1676,7 +1285,7 @@ source "drivers/scsi/arm/Kconfig"
config JAZZ_ESP
bool "MIPS JAZZ FAS216 SCSI support"
- depends on MACH_JAZZ && SCSI
+ depends on MACH_JAZZ && SCSI=y
select SCSI_SPI_ATTRS
help
This is the driver for the onboard SCSI host adapter of MIPS Magnum
@@ -1706,7 +1315,7 @@ config A2091_SCSI
config GVP11_SCSI
tristate "GVP Series II WD33C93A support"
depends on ZORRO && SCSI
- ---help---
+ help
If you have a Great Valley Products Series II SCSI controller,
answer Y. Also say Y if you have a later model of GVP SCSI
controller (such as the GVP A4008 or a Combo board). Otherwise,
@@ -1717,8 +1326,8 @@ config GVP11_SCSI
module will be called gvp11.
config SCSI_A4000T
- tristate "A4000T NCR53c710 SCSI support (EXPERIMENTAL)"
- depends on AMIGA && SCSI && EXPERIMENTAL
+ tristate "A4000T NCR53c710 SCSI support"
+ depends on AMIGA && SCSI
select SCSI_SPI_ATTRS
help
If you have an Amiga 4000T and have SCSI devices connected to the
@@ -1728,8 +1337,8 @@ config SCSI_A4000T
module will be called a4000t.
config SCSI_ZORRO7XX
- tristate "Zorro NCR53c710 SCSI support (EXPERIMENTAL)"
- depends on ZORRO && SCSI && EXPERIMENTAL
+ tristate "Zorro NCR53c710 SCSI support"
+ depends on ZORRO && SCSI
select SCSI_SPI_ATTRS
help
Support for various NCR53c710-based SCSI controllers on Zorro
@@ -1743,44 +1352,42 @@ config SCSI_ZORRO7XX
accelerator card for the Amiga 1200,
- the SCSI controller on the GVP Turbo 040/060 accelerator.
+config SCSI_ZORRO_ESP
+ tristate "Zorro ESP SCSI support"
+ depends on ZORRO && SCSI
+ select SCSI_SPI_ATTRS
+ select SCSI_ESP_PIO
+ help
+ Support for various NCR53C9x (ESP) based SCSI controllers on Zorro
+ expansion boards for the Amiga.
+ This includes:
+ - the Phase5 Blizzard 1230 II and IV SCSI controllers,
+ - the Phase5 Blizzard 2060 SCSI controller,
+ - the Phase5 Blizzard Cyberstorm and Cyberstorm II SCSI
+ controllers,
+ - the Fastlane Zorro III SCSI controller.
+
config ATARI_SCSI
tristate "Atari native SCSI support"
depends on ATARI && SCSI
select SCSI_SPI_ATTRS
- select NVRAM
- ---help---
+ help
If you have an Atari with built-in NCR5380 SCSI controller (TT,
Falcon, ...) say Y to get it supported. Of course also, if you have
a compatible SCSI controller (e.g. for Medusa).
- To compile this driver as a module, choose M here: the
- module will be called atari_scsi.
+ To compile this driver as a module, choose M here: the module will
+ be called atari_scsi. If you also enable NVRAM support, the SCSI
+ host's ID is taken from the setting in TT RTC NVRAM.
This driver supports both styles of NCR integration into the
system: the TT style (separate DMA), and the Falcon style (via
ST-DMA, replacing ACSI). It does NOT support other schemes, like
in the Hades (without DMA).
-config ATARI_SCSI_TOSHIBA_DELAY
- bool "Long delays for Toshiba CD-ROMs"
- depends on ATARI_SCSI
- help
- This option increases the delay after a SCSI arbitration to
- accommodate some flaky Toshiba CD-ROM drives. Say Y if you intend to
- use a Toshiba CD-ROM drive; otherwise, the option is not needed and
- would impact performance a bit, so say N.
-
-config ATARI_SCSI_RESET_BOOT
- bool "Reset SCSI-devices at boottime"
- depends on ATARI_SCSI
- help
- Reset the devices on your Atari whenever it boots. This makes the
- boot process fractionally longer but may assist recovery from errors
- that leave the devices with SCSI operations partway completed.
-
config MAC_SCSI
- bool "Macintosh NCR5380 SCSI"
- depends on MAC && SCSI=y
+ tristate "Macintosh NCR5380 SCSI"
+ depends on MAC && SCSI
select SCSI_SPI_ATTRS
help
This is the NCR 5380 SCSI controller included on most of the 68030
@@ -1792,6 +1399,7 @@ config SCSI_MAC_ESP
tristate "Macintosh NCR53c9[46] SCSI"
depends on MAC && SCSI
select SCSI_SPI_ATTRS
+ select SCSI_ESP_PIO
help
This is the NCR 53c9x SCSI controller found on most of the 68040
based Macintoshes.
@@ -1858,27 +1466,29 @@ config SCSI_SUNESP
module will be called sun_esp.
config ZFCP
- tristate "FCP host bus adapter driver for IBM eServer zSeries"
+ tristate "FCP host bus adapter driver for IBM mainframes"
depends on S390 && QDIO && SCSI
- select SCSI_FC_ATTRS
+ depends on SCSI_FC_ATTRS
help
- If you want to access SCSI devices attached to your IBM eServer
- zSeries by means of Fibre Channel interfaces say Y.
- For details please refer to the documentation provided by IBM at
- <http://oss.software.ibm.com/developerworks/opensource/linux390>
+ If you want to access SCSI devices attached to your IBM mainframe by
+ means of Fibre Channel Protocol host bus adapters say Y.
+
+ Supported HBAs include different models of the FICON Express and FCP
+ Express I/O cards.
+
+ For a more complete list, and for more details about setup and
+ operation refer to the IBM publication "Device Drivers, Features, and
+ Commands", SC33-8411.
This driver is also available as a module. This module will be
called zfcp. If you want to compile it as a module, say M here
- and read <file:Documentation/kbuild/modules.txt>.
-
-config ZFCP_DIF
- tristate "T10 DIF/DIX support for the zfcp driver (EXPERIMENTAL)"
- depends on ZFCP && EXPERIMENTAL
+ and read <file:Documentation/kbuild/modules.rst>.
config SCSI_PMCRAID
tristate "PMC SIERRA Linux MaxRAID adapter support"
depends on PCI && SCSI && NET
- ---help---
+ select SGL_ALLOC
+ help
This driver supports the PMC SIERRA MaxRAID adapters.
config SCSI_PM8001
@@ -1889,32 +1499,29 @@ config SCSI_PM8001
This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip
based host adapters.
-config SCSI_SRP
- tristate "SCSI RDMA Protocol helper library"
- depends on SCSI && PCI
- select SCSI_TGT
- help
- If you wish to use SRP target drivers, say Y.
-
- To compile this driver as a module, choose M here: the
- module will be called libsrp.
-
config SCSI_BFA_FC
tristate "Brocade BFA Fibre Channel Support"
depends on PCI && SCSI
- select SCSI_FC_ATTRS
+ depends on SCSI_FC_ATTRS
help
This bfa driver supports all Brocade PCIe FC/FCOE host adapters.
To compile this driver as a module, choose M here. The module will
be called bfa.
-endif # SCSI_LOWLEVEL
+config SCSI_VIRTIO
+ tristate "virtio-scsi support"
+ depends on VIRTIO
+ help
+ This is the virtual HBA driver for virtio. If the kernel will
+ be used in a virtual machine, say Y or M.
+
+source "drivers/scsi/csiostor/Kconfig"
source "drivers/scsi/pcmcia/Kconfig"
-source "drivers/scsi/device_handler/Kconfig"
+endif # SCSI_LOWLEVEL
-source "drivers/scsi/osd/Kconfig"
+source "drivers/scsi/device_handler/Kconfig"
endmenu
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 3c08f5352b2d..16de3e41f94c 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for linux/drivers/scsi
#
@@ -15,15 +16,16 @@
CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF
-CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_SCSI) += scsi_mod.o
-obj-$(CONFIG_SCSI_TGT) += scsi_tgt.o
+obj-$(CONFIG_SCSI_COMMON) += scsi_common.o
obj-$(CONFIG_RAID_ATTRS) += raid_class.o
+obj-$(CONFIG_SCSI_PROTO_TEST) += scsi_proto_test.o
+
# --- NOTE ORDERING HERE ---
# For kernel non-modular link, transport attributes need to
# be initialised before drivers
@@ -40,12 +42,15 @@ obj-$(CONFIG_LIBFC) += libfc/
obj-$(CONFIG_LIBFCOE) += fcoe/
obj-$(CONFIG_FCOE) += fcoe/
obj-$(CONFIG_FCOE_FNIC) += fnic/
+obj-$(CONFIG_SCSI_SNIC) += snic/
obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/
+obj-$(CONFIG_QEDF) += qedf/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o
obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o
obj-$(CONFIG_SCSI_ZORRO7XX) += 53c700.o zorro7xx.o
+obj-$(CONFIG_SCSI_ZORRO_ESP) += esp_scsi.o zorro_esp.o
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o
obj-$(CONFIG_GVP11_SCSI) += gvp11.o wd33c93.o
@@ -60,62 +65,51 @@ obj-$(CONFIG_BVME6000_SCSI) += 53c700.o bvme6000_scsi.o
obj-$(CONFIG_SCSI_SIM710) += 53c700.o sim710.o
obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o
obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o
-obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o
-obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o
obj-$(CONFIG_SCSI_ARCMSR) += arcmsr/
-obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o
obj-$(CONFIG_SCSI_AHA152X) += aha152x.o
obj-$(CONFIG_SCSI_AHA1542) += aha1542.o
obj-$(CONFIG_SCSI_AHA1740) += aha1740.o
obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx/
obj-$(CONFIG_SCSI_AIC79XX) += aic7xxx/
obj-$(CONFIG_SCSI_AACRAID) += aacraid/
-obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/
obj-$(CONFIG_SCSI_PM8001) += pm8001/
obj-$(CONFIG_SCSI_ISCI) += isci/
obj-$(CONFIG_SCSI_IPS) += ips.o
-obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
-obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
-obj-$(CONFIG_SCSI_IN2000) += in2000.o
+obj-$(CONFIG_SCSI_FDOMAIN) += fdomain.o
+obj-$(CONFIG_SCSI_FDOMAIN_PCI) += fdomain_pci.o
+obj-$(CONFIG_SCSI_FDOMAIN_ISA) += fdomain_isa.o
obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o
-obj-$(CONFIG_SCSI_GENERIC_NCR5380_MMIO) += g_NCR5380_mmio.o
-obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o
-obj-$(CONFIG_SCSI_NCR_D700) += 53c700.o NCR_D700.o
-obj-$(CONFIG_SCSI_NCR_Q720) += NCR_Q720_mod.o
-obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o
obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas408.o qlogicfas.o
obj-$(CONFIG_PCMCIA_QLOGIC) += qlogicfas408.o
obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx/
-obj-$(CONFIG_SCSI_QLA_ISCSI) += qla4xxx/
+obj-$(CONFIG_SCSI_QLA_ISCSI) += libiscsi.o qla4xxx/
obj-$(CONFIG_SCSI_LPFC) += lpfc/
+obj-$(CONFIG_SCSI_EFCT) += elx/
obj-$(CONFIG_SCSI_BFA_FC) += bfa/
-obj-$(CONFIG_SCSI_PAS16) += pas16.o
-obj-$(CONFIG_SCSI_T128) += t128.o
+obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/
obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o
obj-$(CONFIG_SCSI_HPSA) += hpsa.o
-obj-$(CONFIG_SCSI_DTC3280) += dtc.o
+obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/
obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/
obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o
-obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o
-obj-$(CONFIG_SCSI_7000FASST) += wd7000.o
-obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o
-obj-$(CONFIG_SCSI_EATA) += eata.o
obj-$(CONFIG_SCSI_DC395x) += dc395x.o
-obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
+obj-$(CONFIG_SCSI_AM53C974) += esp_scsi.o am53c974.o
obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
-obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/
+obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/
+obj-$(CONFIG_SCSI_MPI3MR) += mpi3mr/
obj-$(CONFIG_SCSI_ACARD) += atp870u.o
obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o
-obj-$(CONFIG_SCSI_GDTH) += gdth.o
obj-$(CONFIG_SCSI_INITIO) += initio.o
obj-$(CONFIG_SCSI_INIA100) += a100u2w.o
obj-$(CONFIG_SCSI_QLOGICPTI) += qlogicpti.o
obj-$(CONFIG_SCSI_MESH) += mesh.o
obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o
+obj-$(CONFIG_SCSI_MYRB) += myrb.o
+obj-$(CONFIG_SCSI_MYRS) += myrs.o
obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o
obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o
obj-$(CONFIG_SCSI_3W_SAS) += 3w-sas.o
@@ -127,52 +121,59 @@ obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o
obj-$(CONFIG_SCSI_SNI_53C710) += 53c700.o sni_53c710.o
obj-$(CONFIG_SCSI_NSP32) += nsp32.o
obj-$(CONFIG_SCSI_IPR) += ipr.o
-obj-$(CONFIG_SCSI_SRP) += libsrp.o
obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/
-obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/
+obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi_tgt/
obj-$(CONFIG_SCSI_IBMVFC) += ibmvscsi/
obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o
obj-$(CONFIG_SCSI_STEX) += stex.o
obj-$(CONFIG_SCSI_MVSAS) += mvsas/
+obj-$(CONFIG_SCSI_MVUMI) += mvumi.o
obj-$(CONFIG_PS3_ROM) += ps3rom.o
obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgbi/
obj-$(CONFIG_SCSI_CXGB4_ISCSI) += libiscsi.o libiscsi_tcp.o cxgbi/
obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
+obj-$(CONFIG_QEDI) += libiscsi.o qedi/
obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/
+obj-$(CONFIG_SCSI_ESAS2R) += esas2r/
obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
+obj-$(CONFIG_SCSI_VIRTIO) += virtio_scsi.o
obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
+obj-$(CONFIG_XEN_SCSI_FRONTEND) += xen-scsifront.o
+obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
+obj-$(CONFIG_SCSI_WD719X) += wd719x.o
obj-$(CONFIG_ARM) += arm/
obj-$(CONFIG_CHR_DEV_ST) += st.o
-obj-$(CONFIG_CHR_DEV_OSST) += osst.o
obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o
obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
obj-$(CONFIG_CHR_DEV_SG) += sg.o
obj-$(CONFIG_CHR_DEV_SCH) += ch.o
obj-$(CONFIG_SCSI_ENCLOSURE) += ses.o
-obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/
+obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/
# This goes last, so that "real" scsi devices probe earlier
obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
-
-obj-$(CONFIG_SCSI_WAIT_SCAN) += scsi_wait_scan.o
-
-scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \
+scsi_mod-y += scsi.o hosts.o scsi_ioctl.o \
scsicam.o scsi_error.o scsi_lib.o
+scsi_mod-$(CONFIG_SCSI_CONSTANTS) += constants.o
scsi_mod-$(CONFIG_SCSI_DMA) += scsi_lib_dma.o
scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o
scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o
scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o
scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o
-scsi_mod-y += scsi_trace.o
+scsi_mod-$(CONFIG_BLK_DEBUG_FS) += scsi_debugfs.o
+scsi_mod-y += scsi_trace.o scsi_logging.o
scsi_mod-$(CONFIG_PM) += scsi_pm.o
+scsi_mod-$(CONFIG_SCSI_DH) += scsi_dh.o
+scsi_mod-$(CONFIG_BLK_DEV_BSG) += scsi_bsg.o
-scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o
+hv_storvsc-y := storvsc_drv.o
sd_mod-objs := sd.o
sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o
+sd_mod-$(CONFIG_BLK_DEV_ZONED) += sd_zbc.o
sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
@@ -180,13 +181,21 @@ ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
-DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m)
zalon7xx-objs := zalon.o ncr53c8xx.o
-NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o
-oktagon_esp_mod-objs := oktagon_esp.o oktagon_io.o
# Files generated that shall be removed upon make clean
clean-files := 53c700_d.h 53c700_u.h
-$(obj)/53c700.o $(MODVERDIR)/$(obj)/53c700.ver: $(obj)/53c700_d.h
+$(obj)/53c700.o: $(obj)/53c700_d.h
+
+$(obj)/scsi_sysfs.o: $(obj)/scsi_devinfo_tbl.c
+
+quiet_cmd_bflags = GEN $@
+ cmd_bflags = sed -n 's/.*define *BLIST_\([A-Z0-9_]*\) *.*/BLIST_FLAG_NAME(\1),/p' $< > $@
+
+$(obj)/scsi_devinfo_tbl.c: include/scsi/scsi_devinfo.h FORCE
+ $(call if_changed,bflags)
+
+targets += scsi_devinfo_tbl.c
# If you want to play with the firmware, uncomment
# GENERATE_FIRMWARE := 1
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index 165e4dd865d9..0e10502660de 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -1,19 +1,18 @@
-/*
+// SPDX-License-Identifier: GPL-2.0
+/*
* NCR 5380 generic driver routines. These should make it *trivial*
- * to implement 5380 SCSI drivers under Linux with a non-trantor
- * architecture.
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
*
- * Note that these routines also work with NR53c400 family chips.
+ * Note that these routines also work with NR53c400 family chips.
*
* Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 666-5836
- *
- * DISTRIBUTION RELEASE 6.
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
*
- * For more information, please consult
+ * For more information, please consult
*
* NCR 5380 Family
* SCSI Protocol Controller
@@ -27,140 +26,39 @@
*/
/*
- * $Log: NCR5380.c,v $
-
- * Revision 1.10 1998/9/2 Alan Cox
- * (alan@lxorguk.ukuu.org.uk)
- * Fixed up the timer lockups reported so far. Things still suck. Looking
- * forward to 2.3 and per device request queues. Then it'll be possible to
- * SMP thread this beast and improve life no end.
-
- * Revision 1.9 1997/7/27 Ronald van Cuijlenborg
- * (ronald.van.cuijlenborg@tip.nl or nutty@dds.nl)
- * (hopefully) fixed and enhanced USLEEP
- * added support for DTC3181E card (for Mustek scanner)
- *
-
- * Revision 1.8 Ingmar Baumgart
- * (ingmar@gonzo.schwaben.de)
- * added support for NCR53C400a card
- *
-
- * Revision 1.7 1996/3/2 Ray Van Tassle (rayvt@comm.mot.com)
- * added proc_info
- * added support needed for DTC 3180/3280
- * fixed a couple of bugs
- *
-
- * Revision 1.5 1994/01/19 09:14:57 drew
- * Fixed udelay() hack that was being used on DATAOUT phases
- * instead of a proper wait for the final handshake.
- *
- * Revision 1.4 1994/01/19 06:44:25 drew
- * *** empty log message ***
- *
- * Revision 1.3 1994/01/19 05:24:40 drew
- * Added support for TCR LAST_BYTE_SENT bit.
- *
- * Revision 1.2 1994/01/15 06:14:11 drew
- * REAL DMA support, bug fixes.
- *
- * Revision 1.1 1994/01/15 06:00:54 drew
- * Initial revision
- *
- */
-
-/*
- * Further development / testing that should be done :
- * 1. Cleanup the NCR5380_transfer_dma function and DMA operation complete
- * code so that everything does the same thing that's done at the
- * end of a pseudo-DMA read operation.
- *
- * 2. Fix REAL_DMA (interrupt driven, polled works fine) -
- * basically, transfer size needs to be reduced by one
- * and the last byte read as is done with PSEUDO_DMA.
- *
- * 4. Test SCSI-II tagged queueing (I have no devices which support
- * tagged queueing)
- *
- * 5. Test linked command handling code after Eric is ready with
- * the high level code.
+ * With contributions from Ray Van Tassle, Ingmar Baumgart,
+ * Ronald van Cuijlenborg, Alan Cox and others.
*/
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_transport_spi.h>
-#ifndef NDEBUG
-#define NDEBUG 0
-#endif
-#ifndef NDEBUG_ABORT
-#define NDEBUG_ABORT 0
-#endif
+/* Ported to Atari by Roman Hodek and others. */
-#if (NDEBUG & NDEBUG_LISTS)
-#define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
-#define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
-#else
-#define LIST(x,y)
-#define REMOVE(w,x,y,z)
-#endif
-
-#ifndef notyet
-#undef LINKED
-#undef REAL_DMA
-#endif
-
-#ifdef REAL_DMA_POLL
-#undef READ_OVERRUNS
-#define READ_OVERRUNS
-#endif
-
-#ifdef BOARD_REQUIRES_NO_DELAY
-#define io_recovery_delay(x)
-#else
-#define io_recovery_delay(x) udelay(x)
-#endif
+/* Adapted for the Sun 3 by Sam Creasey. */
/*
* Design
*
- * This is a generic 5380 driver. To use it on a different platform,
+ * This is a generic 5380 driver. To use it on a different platform,
* one simply writes appropriate system specific macros (ie, data
- * transfer - some PC's will use the I/O bus, 68K's must use
+ * transfer - some PC's will use the I/O bus, 68K's must use
* memory mapped) and drops this file in their 'C' wrapper.
*
- * (Note from hch: unfortunately it was not enough for the different
- * m68k folks and instead of improving this driver they copied it
- * and hacked it up for their needs. As a consequence they lost
- * most updates to this driver. Maybe someone will fix all these
- * drivers to use a common core one day..)
- *
- * As far as command queueing, two queues are maintained for
+ * As far as command queueing, two queues are maintained for
* each 5380 in the system - commands that haven't been issued yet,
- * and commands that are currently executing. This means that an
- * unlimited number of commands may be queued, letting
- * more commands propagate from the higher driver levels giving higher
- * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
- * allowing multiple commands to propagate all the way to a SCSI-II device
+ * and commands that are currently executing. This means that an
+ * unlimited number of commands may be queued, letting
+ * more commands propagate from the higher driver levels giving higher
+ * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
+ * allowing multiple commands to propagate all the way to a SCSI-II device
* while a command is already executing.
*
*
- * Issues specific to the NCR5380 :
- *
- * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
- * piece of hardware that requires you to sit in a loop polling for
- * the REQ signal as long as you are connected. Some devices are
- * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
- * while doing long seek operations.
- *
- * The workaround for this is to keep track of devices that have
- * disconnected. If the device hasn't disconnected, for commands that
- * should disconnect, we do something like
+ * Issues specific to the NCR5380 :
*
- * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
- *
- * Some tweaking of N and M needs to be done. An algorithm based
- * on "time to data" would give the best results as long as short time
- * to datas (ie, on the same track) were considered, however these
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
+ * piece of hardware that requires you to sit in a loop polling for
+ * the REQ signal as long as you are connected. Some devices are
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
+ * while doing long seek operations. [...] These
* broken devices are the exception rather than the rule and I'd rather
* spend my time optimizing for the normal case.
*
@@ -170,24 +68,23 @@
* which is started from a workqueue for each NCR5380 host in the
* system. It attempts to establish I_T_L or I_T_L_Q nexuses by
* removing the commands from the issue queue and calling
- * NCR5380_select() if a nexus is not established.
+ * NCR5380_select() if a nexus is not established.
*
* Once a nexus is established, the NCR5380_information_transfer()
* phase goes through the various phases as instructed by the target.
* if the target goes into MSG IN and sends a DISCONNECT message,
* the command structure is placed into the per instance disconnected
- * queue, and NCR5380_main tries to find more work. If the target is
+ * queue, and NCR5380_main tries to find more work. If the target is
* idle for too long, the system will try to sleep.
*
* If a command has disconnected, eventually an interrupt will trigger,
* calling NCR5380_intr() which will in turn call NCR5380_reselect
* to reestablish a nexus. This will run main if necessary.
*
- * On command termination, the done function will be called as
+ * On command termination, the done function will be called as
* appropriate.
*
- * SCSI pointers are maintained in the SCp field of SCSI command
- * structures, being initialized after the command is connected
+ * The command data pointer is initialized after the command is connected
* in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
* Note that in violation of the standard, an implicit SAVE POINTERS operation
* is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
@@ -196,731 +93,410 @@
/*
* Using this file :
* This file a skeleton Linux SCSI driver for the NCR 5380 series
- * of chips. To use it, you write an architecture specific functions
+ * of chips. To use it, you write an architecture specific functions
* and macros and include this file in your driver.
*
- * These macros control options :
- * AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be
- * defined.
- *
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
- * transceivers.
- *
- * DONT_USE_INTR - if defined, never use interrupts, even if we probe or
- * override-configure an IRQ.
- *
- * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
- * bytes at a time. Since interrupts are disabled by default during
- * these transfers, we might need this to give reasonable interrupt
- * service time if the transfer size gets too large.
- *
- * LINKED - if defined, linked commands are supported.
- *
- * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
- *
- * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
- *
- * REAL_DMA_POLL - if defined, REAL DMA is used but the driver doesn't
- * rely on phase mismatch and EOP interrupts to determine end
- * of phase.
- *
- * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
- * only really want to use this if you're having a problem with
- * dropped characters during high speed communications, and even
- * then, you're going to be better off twiddling with transfersize
- * in the high level code.
- *
- * Defaults for these will be provided although the user may want to adjust
- * these to allocate CPU resources to the SCSI driver or "real" code.
- *
- * USLEEP_SLEEP - amount of time, in jiffies, to sleep
- *
- * USLEEP_POLL - amount of time, in jiffies, to poll
- *
* These macros MUST be defined :
- * NCR5380_local_declare() - declare any local variables needed for your
- * transfer routines.
*
- * NCR5380_setup(instance) - initialize any local variables needed from a given
- * instance of the host adapter for NCR5380_{read,write,pread,pwrite}
- *
* NCR5380_read(register) - read from the specified register
*
- * NCR5380_write(register, value) - write to the specific register
+ * NCR5380_write(register, value) - write to the specific register
*
- * NCR5380_implementation_fields - additional fields needed for this
- * specific implementation of the NCR5380
+ * NCR5380_implementation_fields - additional fields needed for this
+ * specific implementation of the NCR5380
*
* Either real DMA *or* pseudo DMA may be implemented
- * REAL functions :
- * NCR5380_REAL_DMA should be defined if real DMA is to be used.
- * Note that the DMA setup functions should return the number of bytes
- * that they were able to program the controller for.
- *
- * Also note that generic i386/PC versions of these macros are
- * available as NCR5380_i386_dma_write_setup,
- * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
*
- * NCR5380_dma_write_setup(instance, src, count) - initialize
- * NCR5380_dma_read_setup(instance, dst, count) - initialize
- * NCR5380_dma_residual(instance); - residual count
- *
- * PSEUDO functions :
- * NCR5380_pwrite(instance, src, count)
- * NCR5380_pread(instance, dst, count);
+ * NCR5380_dma_xfer_len - determine size of DMA/PDMA transfer
+ * NCR5380_dma_send_setup - execute DMA/PDMA from memory to 5380
+ * NCR5380_dma_recv_setup - execute DMA/PDMA from 5380 to memory
+ * NCR5380_dma_residual - residual byte count
*
* The generic driver is initialized by calling NCR5380_init(instance),
- * after setting the appropriate host specific fields and ID. If the
- * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
- * possible) function may be used.
+ * after setting the appropriate host specific fields and ID.
*/
-static int do_abort(struct Scsi_Host *host);
-static void do_reset(struct Scsi_Host *host);
+#ifndef NCR5380_io_delay
+#define NCR5380_io_delay(x)
+#endif
-/*
- * initialize_SCp - init the scsi pointer field
- * @cmd: command block to set up
+#ifndef NCR5380_acquire_dma_irq
+#define NCR5380_acquire_dma_irq(x) (1)
+#endif
+
+#ifndef NCR5380_release_dma_irq
+#define NCR5380_release_dma_irq(x)
+#endif
+
+static unsigned int disconnect_mask = ~0;
+module_param(disconnect_mask, int, 0444);
+
+static int do_abort(struct Scsi_Host *, unsigned int);
+static void do_reset(struct Scsi_Host *);
+static void bus_reset_cleanup(struct Scsi_Host *);
+
+/**
+ * initialize_SCp - init the scsi pointer field
+ * @cmd: command block to set up
*
- * Set up the internal fields in the SCSI command.
+ * Set up the internal fields in the SCSI command.
*/
-static __inline__ void initialize_SCp(Scsi_Cmnd * cmd)
+static inline void initialize_SCp(struct scsi_cmnd *cmd)
{
- /*
- * Initialize the Scsi Pointer field so that all of the commands in the
- * various queues are valid.
- */
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(cmd);
if (scsi_bufflen(cmd)) {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ ncmd->buffer = scsi_sglist(cmd);
+ ncmd->ptr = sg_virt(ncmd->buffer);
+ ncmd->this_residual = ncmd->buffer->length;
} else {
- cmd->SCp.buffer = NULL;
- cmd->SCp.buffers_residual = 0;
- cmd->SCp.ptr = NULL;
- cmd->SCp.this_residual = 0;
+ ncmd->buffer = NULL;
+ ncmd->ptr = NULL;
+ ncmd->this_residual = 0;
}
+
+ ncmd->status = 0;
+}
+
+static inline void advance_sg_buffer(struct NCR5380_cmd *ncmd)
+{
+ struct scatterlist *s = ncmd->buffer;
+
+ if (!ncmd->this_residual && s && !sg_is_last(s)) {
+ ncmd->buffer = sg_next(s);
+ ncmd->ptr = sg_virt(ncmd->buffer);
+ ncmd->this_residual = ncmd->buffer->length;
+ }
+}
+
+static inline void set_resid_from_SCp(struct scsi_cmnd *cmd)
+{
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(cmd);
+ int resid = ncmd->this_residual;
+ struct scatterlist *s = ncmd->buffer;
+
+ if (s)
+ while (!sg_is_last(s)) {
+ s = sg_next(s);
+ resid += s->length;
+ }
+ scsi_set_resid(cmd, resid);
}
/**
- * NCR5380_poll_politely - wait for NCR5380 status bits
- * @instance: controller to poll
- * @reg: 5380 register to poll
- * @bit: Bitmask to check
- * @val: Value required to exit
- *
- * Polls the NCR5380 in a reasonably efficient manner waiting for
- * an event to occur, after a short quick poll we begin giving the
- * CPU back in non IRQ contexts
- *
- * Returns the value of the register or a negative error code.
+ * NCR5380_poll_politely2 - wait for two chip register values
+ * @hostdata: host private data
+ * @reg1: 5380 register to poll
+ * @bit1: Bitmask to check
+ * @val1: Expected value
+ * @reg2: Second 5380 register to poll
+ * @bit2: Second bitmask to check
+ * @val2: Second expected value
+ * @wait: Time-out in jiffies, 0 if sleeping is not allowed
+ *
+ * Polls the chip in a reasonably efficient manner waiting for an
+ * event to occur. After a short quick poll we begin to yield the CPU
+ * (if possible). In irq contexts the time-out is arbitrarily limited.
+ *
+ * Returns 0 if either or both event(s) occurred otherwise -ETIMEDOUT.
*/
-
-static int NCR5380_poll_politely(struct Scsi_Host *instance, int reg, int bit, int val, int t)
+
+static int NCR5380_poll_politely2(struct NCR5380_hostdata *hostdata,
+ unsigned int reg1, u8 bit1, u8 val1,
+ unsigned int reg2, u8 bit2, u8 val2,
+ unsigned long wait)
{
- NCR5380_local_declare();
- int n = 500; /* At about 8uS a cycle for the cpu access */
- unsigned long end = jiffies + t;
- int r;
-
- NCR5380_setup(instance);
-
- while( n-- > 0)
- {
- r = NCR5380_read(reg);
- if((r & bit) == val)
+ unsigned long n = hostdata->poll_loops;
+ unsigned long deadline = jiffies + wait;
+
+ do {
+ if ((NCR5380_read(reg1) & bit1) == val1)
+ return 0;
+ if ((NCR5380_read(reg2) & bit2) == val2)
return 0;
cpu_relax();
- }
-
- /* t time yet ? */
- while(time_before(jiffies, end))
- {
- r = NCR5380_read(reg);
- if((r & bit) == val)
+ } while (n--);
+
+ if (!wait)
+ return -ETIMEDOUT;
+
+ /* Repeatedly sleep for 1 ms until deadline */
+ while (time_is_after_jiffies(deadline)) {
+ schedule_timeout_uninterruptible(1);
+ if ((NCR5380_read(reg1) & bit1) == val1)
+ return 0;
+ if ((NCR5380_read(reg2) & bit2) == val2)
return 0;
- if(!in_interrupt())
- cond_resched();
- else
- cpu_relax();
}
+
return -ETIMEDOUT;
}
-static struct {
- unsigned char value;
- const char *name;
-} phases[] __maybe_unused = {
- {PHASE_DATAOUT, "DATAOUT"},
- {PHASE_DATAIN, "DATAIN"},
- {PHASE_CMDOUT, "CMDOUT"},
- {PHASE_STATIN, "STATIN"},
- {PHASE_MSGOUT, "MSGOUT"},
- {PHASE_MSGIN, "MSGIN"},
- {PHASE_UNKNOWN, "UNKNOWN"}
-};
-
#if NDEBUG
static struct {
unsigned char mask;
const char *name;
-} signals[] = {
- {SR_DBP, "PARITY"},
- {SR_RST, "RST"},
- {SR_BSY, "BSY"},
- {SR_REQ, "REQ"},
- {SR_MSG, "MSG"},
- {SR_CD, "CD"},
- {SR_IO, "IO"},
- {SR_SEL, "SEL"},
+} signals[] = {
+ {SR_DBP, "PARITY"},
+ {SR_RST, "RST"},
+ {SR_BSY, "BSY"},
+ {SR_REQ, "REQ"},
+ {SR_MSG, "MSG"},
+ {SR_CD, "CD"},
+ {SR_IO, "IO"},
+ {SR_SEL, "SEL"},
{0, NULL}
-},
+},
basrs[] = {
- {BASR_ATN, "ATN"},
- {BASR_ACK, "ACK"},
+ {BASR_END_DMA_TRANSFER, "END OF DMA"},
+ {BASR_DRQ, "DRQ"},
+ {BASR_PARITY_ERROR, "PARITY ERROR"},
+ {BASR_IRQ, "IRQ"},
+ {BASR_PHASE_MATCH, "PHASE MATCH"},
+ {BASR_BUSY_ERROR, "BUSY ERROR"},
+ {BASR_ATN, "ATN"},
+ {BASR_ACK, "ACK"},
{0, NULL}
-},
-icrs[] = {
- {ICR_ASSERT_RST, "ASSERT RST"},
- {ICR_ASSERT_ACK, "ASSERT ACK"},
- {ICR_ASSERT_BSY, "ASSERT BSY"},
- {ICR_ASSERT_SEL, "ASSERT SEL"},
- {ICR_ASSERT_ATN, "ASSERT ATN"},
- {ICR_ASSERT_DATA, "ASSERT DATA"},
+},
+icrs[] = {
+ {ICR_ASSERT_RST, "ASSERT RST"},
+ {ICR_ARBITRATION_PROGRESS, "ARB. IN PROGRESS"},
+ {ICR_ARBITRATION_LOST, "LOST ARB."},
+ {ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"},
+ {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"},
+ {ICR_ASSERT_DATA, "ASSERT DATA"},
{0, NULL}
-},
-mrs[] = {
- {MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"},
- {MR_TARGET, "MODE TARGET"},
- {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"},
- {MR_ENABLE_PAR_INTR, "MODE PARITY INTR"},
- {MR_MONITOR_BSY, "MODE MONITOR BSY"},
- {MR_DMA_MODE, "MODE DMA"},
- {MR_ARBITRATE, "MODE ARBITRATION"},
+},
+mrs[] = {
+ {MR_BLOCK_DMA_MODE, "BLOCK DMA MODE"},
+ {MR_TARGET, "TARGET"},
+ {MR_ENABLE_PAR_CHECK, "PARITY CHECK"},
+ {MR_ENABLE_PAR_INTR, "PARITY INTR"},
+ {MR_ENABLE_EOP_INTR, "EOP INTR"},
+ {MR_MONITOR_BSY, "MONITOR BSY"},
+ {MR_DMA_MODE, "DMA MODE"},
+ {MR_ARBITRATE, "ARBITRATE"},
{0, NULL}
};
/**
- * NCR5380_print - print scsi bus signals
- * @instance: adapter state to dump
- *
- * Print the SCSI bus signals for debugging purposes
+ * NCR5380_print - print scsi bus signals
+ * @instance: adapter state to dump
*
- * Locks: caller holds hostdata lock (not essential)
+ * Print the SCSI bus signals for debugging purposes
*/
static void NCR5380_print(struct Scsi_Host *instance)
{
- NCR5380_local_declare();
- unsigned char status, data, basr, mr, icr, i;
- NCR5380_setup(instance);
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned char status, basr, mr, icr, i;
- data = NCR5380_read(CURRENT_SCSI_DATA_REG);
status = NCR5380_read(STATUS_REG);
mr = NCR5380_read(MODE_REG);
icr = NCR5380_read(INITIATOR_COMMAND_REG);
basr = NCR5380_read(BUS_AND_STATUS_REG);
- printk("STATUS_REG: %02x ", status);
+ printk(KERN_DEBUG "SR = 0x%02x : ", status);
for (i = 0; signals[i].mask; ++i)
if (status & signals[i].mask)
- printk(",%s", signals[i].name);
- printk("\nBASR: %02x ", basr);
+ printk(KERN_CONT "%s, ", signals[i].name);
+ printk(KERN_CONT "\nBASR = 0x%02x : ", basr);
for (i = 0; basrs[i].mask; ++i)
if (basr & basrs[i].mask)
- printk(",%s", basrs[i].name);
- printk("\nICR: %02x ", icr);
+ printk(KERN_CONT "%s, ", basrs[i].name);
+ printk(KERN_CONT "\nICR = 0x%02x : ", icr);
for (i = 0; icrs[i].mask; ++i)
if (icr & icrs[i].mask)
- printk(",%s", icrs[i].name);
- printk("\nMODE: %02x ", mr);
+ printk(KERN_CONT "%s, ", icrs[i].name);
+ printk(KERN_CONT "\nMR = 0x%02x : ", mr);
for (i = 0; mrs[i].mask; ++i)
if (mr & mrs[i].mask)
- printk(",%s", mrs[i].name);
- printk("\n");
+ printk(KERN_CONT "%s, ", mrs[i].name);
+ printk(KERN_CONT "\n");
}
+static struct {
+ unsigned char value;
+ const char *name;
+} phases[] = {
+ {PHASE_DATAOUT, "DATAOUT"},
+ {PHASE_DATAIN, "DATAIN"},
+ {PHASE_CMDOUT, "CMDOUT"},
+ {PHASE_STATIN, "STATIN"},
+ {PHASE_MSGOUT, "MSGOUT"},
+ {PHASE_MSGIN, "MSGIN"},
+ {PHASE_UNKNOWN, "UNKNOWN"}
+};
-/*
- * NCR5380_print_phase - show SCSI phase
- * @instance: adapter to dump
- *
- * Print the current SCSI phase for debugging purposes
+/**
+ * NCR5380_print_phase - show SCSI phase
+ * @instance: adapter to dump
*
- * Locks: none
+ * Print the current SCSI phase for debugging purposes
*/
static void NCR5380_print_phase(struct Scsi_Host *instance)
{
- NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
unsigned char status;
int i;
- NCR5380_setup(instance);
status = NCR5380_read(STATUS_REG);
if (!(status & SR_REQ))
- printk("scsi%d : REQ not asserted, phase unknown.\n", instance->host_no);
+ shost_printk(KERN_DEBUG, instance, "REQ not asserted, phase unknown.\n");
else {
- for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i);
- printk("scsi%d : phase %s\n", instance->host_no, phases[i].name);
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i)
+ ;
+ shost_printk(KERN_DEBUG, instance, "phase %s\n", phases[i].name);
}
}
#endif
-/*
- * These need tweaking, and would probably work best as per-device
- * flags initialized differently for disk, tape, cd, etc devices.
- * People with broken devices are free to experiment as to what gives
- * the best results for them.
- *
- * USLEEP_SLEEP should be a minimum seek time.
- *
- * USLEEP_POLL should be a maximum rotational latency.
- */
-#ifndef USLEEP_SLEEP
-/* 20 ms (reasonable hard disk speed) */
-#define USLEEP_SLEEP (20*HZ/1000)
-#endif
-/* 300 RPM (floppy speed) */
-#ifndef USLEEP_POLL
-#define USLEEP_POLL (200*HZ/1000)
-#endif
-#ifndef USLEEP_WAITLONG
-/* RvC: (reasonable time to wait on select error) */
-#define USLEEP_WAITLONG USLEEP_SLEEP
-#endif
-
-/*
- * Function : int should_disconnect (unsigned char cmd)
- *
- * Purpose : decide whether a command would normally disconnect or
- * not, since if it won't disconnect we should go to sleep.
- *
- * Input : cmd - opcode of SCSI command
- *
- * Returns : DISCONNECT_LONG if we should disconnect for a really long
- * time (ie always, sleep, look for REQ active, sleep),
- * DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal
- * time-to-data delay, DISCONNECT_NONE if this command would return
- * immediately.
- *
- * Future sleep algorithms based on time to data can exploit
- * something like this so they can differentiate between "normal"
- * (ie, read, write, seek) and unusual commands (ie, * format).
- *
- * Note : We don't deal with commands that handle an immediate disconnect,
- *
- */
-
-static int should_disconnect(unsigned char cmd)
-{
- switch (cmd) {
- case READ_6:
- case WRITE_6:
- case SEEK_6:
- case READ_10:
- case WRITE_10:
- case SEEK_10:
- return DISCONNECT_TIME_TO_DATA;
- case FORMAT_UNIT:
- case SEARCH_HIGH:
- case SEARCH_LOW:
- case SEARCH_EQUAL:
- return DISCONNECT_LONG;
- default:
- return DISCONNECT_NONE;
- }
-}
-
-static void NCR5380_set_timer(struct NCR5380_hostdata *hostdata, unsigned long timeout)
-{
- hostdata->time_expires = jiffies + timeout;
- schedule_delayed_work(&hostdata->coroutine, timeout);
-}
-
-
-static int probe_irq __initdata = 0;
-
-/**
- * probe_intr - helper for IRQ autoprobe
- * @irq: interrupt number
- * @dev_id: unused
- * @regs: unused
- *
- * Set a flag to indicate the IRQ in question was received. This is
- * used by the IRQ probe code.
- */
-
-static irqreturn_t __init probe_intr(int irq, void *dev_id)
-{
- probe_irq = irq;
- return IRQ_HANDLED;
-}
-
-/**
- * NCR5380_probe_irq - find the IRQ of an NCR5380
- * @instance: NCR5380 controller
- * @possible: bitmask of ISA IRQ lines
- *
- * Autoprobe for the IRQ line used by the NCR5380 by triggering an IRQ
- * and then looking to see what interrupt actually turned up.
- *
- * Locks: none, irqs must be enabled on entry
- */
-
-static int __init __maybe_unused NCR5380_probe_irq(struct Scsi_Host *instance,
- int possible)
-{
- NCR5380_local_declare();
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- unsigned long timeout;
- int trying_irqs, i, mask;
- NCR5380_setup(instance);
-
- for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1)
- if ((mask & possible) && (request_irq(i, &probe_intr, IRQF_DISABLED, "NCR-probe", NULL) == 0))
- trying_irqs |= mask;
-
- timeout = jiffies + (250 * HZ / 1000);
- probe_irq = SCSI_IRQ_NONE;
-
- /*
- * A interrupt is triggered whenever BSY = false, SEL = true
- * and a bit set in the SELECT_ENABLE_REG is asserted on the
- * SCSI bus.
- *
- * Note that the bus is only driven when the phase control signals
- * (I/O, C/D, and MSG) match those in the TCR, so we must reset that
- * to zero.
- */
-
- NCR5380_write(TARGET_COMMAND_REG, 0);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL);
-
- while (probe_irq == SCSI_IRQ_NONE && time_before(jiffies, timeout))
- schedule_timeout_uninterruptible(1);
-
- NCR5380_write(SELECT_ENABLE_REG, 0);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
- if (trying_irqs & mask)
- free_irq(i, NULL);
-
- return probe_irq;
-}
-
-/**
- * NCR58380_print_options - show options
- * @instance: unused for now
- *
- * Called by probe code indicating the NCR5380 driver options that
- * were selected. At some point this will switch to runtime options
- * read from the adapter in question
- *
- * Locks: none
- */
-
-static void __init __maybe_unused
-NCR5380_print_options(struct Scsi_Host *instance)
-{
- printk(" generic options"
-#ifdef AUTOPROBE_IRQ
- " AUTOPROBE_IRQ"
-#endif
-#ifdef AUTOSENSE
- " AUTOSENSE"
-#endif
-#ifdef DIFFERENTIAL
- " DIFFERENTIAL"
-#endif
-#ifdef REAL_DMA
- " REAL DMA"
-#endif
-#ifdef REAL_DMA_POLL
- " REAL DMA POLL"
-#endif
-#ifdef PARITY
- " PARITY"
-#endif
-#ifdef PSEUDO_DMA
- " PSEUDO DMA"
-#endif
-#ifdef UNSAFE
- " UNSAFE "
-#endif
- );
- printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
- printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
- if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) {
- printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
- }
-}
-
/**
- * NCR5380_print_status - dump controller info
- * @instance: controller to dump
- *
- * Print commands in the various queues, called from NCR5380_abort
- * and NCR5380_debug to aid debugging.
- *
- * Locks: called functions disable irqs
- */
-
-static void NCR5380_print_status(struct Scsi_Host *instance)
-{
- NCR5380_dprint(NDEBUG_ANY, instance);
- NCR5380_dprint_phase(NDEBUG_ANY, instance);
-}
-
-/******************************************/
-/*
- * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
- *
- * *buffer: I/O buffer
- * **start: if inout == FALSE pointer into buffer where user read should start
- * offset: current offset
- * length: length of buffer
- * hostno: Scsi_Host host_no
- * inout: TRUE - user is writing; FALSE - user is reading
+ * NCR5380_info - report driver and host information
+ * @instance: relevant scsi host instance
*
- * Return the number of bytes read from or written
+ * For use as the host template info() handler.
*/
-#undef SPRINTF
-#define SPRINTF(args...) do { if(pos < buffer + length-80) pos += sprintf(pos, ## args); } while(0)
-static
-char *lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, char *pos, char *buffer, int length);
-static
-char *lprint_command(unsigned char *cmd, char *pos, char *buffer, int len);
-static
-char *lprint_opcode(int opcode, char *pos, char *buffer, int length);
-
-static int __maybe_unused NCR5380_proc_info(struct Scsi_Host *instance,
- char *buffer, char **start, off_t offset, int length, int inout)
+static const char *NCR5380_info(struct Scsi_Host *instance)
{
- char *pos = buffer;
- struct NCR5380_hostdata *hostdata;
- Scsi_Cmnd *ptr;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
- hostdata = (struct NCR5380_hostdata *) instance->hostdata;
-
- if (inout) { /* Has data been written to the file ? */
-#ifdef DTC_PUBLIC_RELEASE
- dtc_wmaxi = dtc_maxi = 0;
-#endif
-#ifdef PAS16_PUBLIC_RELEASE
- pas_wmaxi = pas_maxi = 0;
-#endif
- return (-ENOSYS); /* Currently this is a no-op */
- }
- SPRINTF("NCR5380 core release=%d. ", NCR5380_PUBLIC_RELEASE);
- if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400)
- SPRINTF("ncr53c400 release=%d. ", NCR53C400_PUBLIC_RELEASE);
-#ifdef DTC_PUBLIC_RELEASE
- SPRINTF("DTC 3180/3280 release %d", DTC_PUBLIC_RELEASE);
-#endif
-#ifdef T128_PUBLIC_RELEASE
- SPRINTF("T128 release %d", T128_PUBLIC_RELEASE);
-#endif
-#ifdef GENERIC_NCR5380_PUBLIC_RELEASE
- SPRINTF("Generic5380 release %d", GENERIC_NCR5380_PUBLIC_RELEASE);
-#endif
-#ifdef PAS16_PUBLIC_RELEASE
- SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE);
-#endif
-
- SPRINTF("\nBase Addr: 0x%05lX ", (long) instance->base);
- SPRINTF("io_port: %04x ", (int) instance->io_port);
- if (instance->irq == SCSI_IRQ_NONE)
- SPRINTF("IRQ: None.\n");
- else
- SPRINTF("IRQ: %d.\n", instance->irq);
-
-#ifdef DTC_PUBLIC_RELEASE
- SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", dtc_wmaxi, dtc_maxi);
-#endif
-#ifdef PAS16_PUBLIC_RELEASE
- SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi);
-#endif
- spin_lock_irq(instance->host_lock);
- if (!hostdata->connected)
- SPRINTF("scsi%d: no currently connected command\n", instance->host_no);
- else
- pos = lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, pos, buffer, length);
- SPRINTF("scsi%d: issue_queue\n", instance->host_no);
- for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
- pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
-
- SPRINTF("scsi%d: disconnected_queue\n", instance->host_no);
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
- pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
- spin_unlock_irq(instance->host_lock);
-
- *start = buffer;
- if (pos - buffer < offset)
- return 0;
- else if (pos - buffer - offset < length)
- return pos - buffer - offset;
- return length;
-}
-
-static char *lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, char *pos, char *buffer, int length)
-{
- SPRINTF("scsi%d : destination target %d, lun %d\n", cmd->device->host->host_no, cmd->device->id, cmd->device->lun);
- SPRINTF(" command = ");
- pos = lprint_command(cmd->cmnd, pos, buffer, length);
- return (pos);
-}
-
-static char *lprint_command(unsigned char *command, char *pos, char *buffer, int length)
-{
- int i, s;
- pos = lprint_opcode(command[0], pos, buffer, length);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- SPRINTF("%02x ", command[i]);
- SPRINTF("\n");
- return (pos);
-}
-
-static char *lprint_opcode(int opcode, char *pos, char *buffer, int length)
-{
- SPRINTF("%2d (0x%02x)", opcode, opcode);
- return (pos);
+ return hostdata->info;
}
-
/**
- * NCR5380_init - initialise an NCR5380
- * @instance: adapter to configure
- * @flags: control flags
+ * NCR5380_init - initialise an NCR5380
+ * @instance: adapter to configure
+ * @flags: control flags
*
- * Initializes *instance and corresponding 5380 chip,
- * with flags OR'd into the initial flags value.
+ * Initializes *instance and corresponding 5380 chip,
+ * with flags OR'd into the initial flags value.
*
- * Notes : I assume that the host, hostno, and id bits have been
- * set correctly. I don't care about the irq and other fields.
+ * Notes : I assume that the host, hostno, and id bits have been
+ * set correctly. I don't care about the irq and other fields.
*
- * Returns 0 for success
- *
- * Locks: interrupts must be enabled when we are called
+ * Returns 0 for success
*/
-static int __devinit NCR5380_init(struct Scsi_Host *instance, int flags)
+static int NCR5380_init(struct Scsi_Host *instance, int flags)
{
- NCR5380_local_declare();
- int i, pass;
- unsigned long timeout;
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
-
- if(in_interrupt())
- printk(KERN_ERR "NCR5380_init called with interrupts off!\n");
- /*
- * On NCR53C400 boards, NCR5380 registers are mapped 8 past
- * the base address.
- */
-
-#ifdef NCR53C400
- if (flags & FLAG_NCR53C400)
- instance->NCR5380_instance_name += NCR53C400_address_adjust;
-#endif
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ int i;
+ unsigned long deadline;
+ unsigned long accesses_per_ms;
- NCR5380_setup(instance);
+ instance->max_lun = 7;
- hostdata->aborted = 0;
+ hostdata->host = instance;
hostdata->id_mask = 1 << instance->this_id;
+ hostdata->id_higher_mask = 0;
for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
if (i > hostdata->id_mask)
hostdata->id_higher_mask |= i;
for (i = 0; i < 8; ++i)
hostdata->busy[i] = 0;
-#ifdef REAL_DMA
- hostdata->dmalen = 0;
-#endif
- hostdata->targets_present = 0;
- hostdata->connected = NULL;
- hostdata->issue_queue = NULL;
- hostdata->disconnected_queue = NULL;
-
- INIT_DELAYED_WORK(&hostdata->coroutine, NCR5380_main);
-
-#ifdef NCR5380_STATS
- for (i = 0; i < 8; ++i) {
- hostdata->time_read[i] = 0;
- hostdata->time_write[i] = 0;
- hostdata->bytes_read[i] = 0;
- hostdata->bytes_write[i] = 0;
- }
- hostdata->timebase = 0;
- hostdata->pendingw = 0;
- hostdata->pendingr = 0;
-#endif
-
- /* The CHECK code seems to break the 53C400. Will check it later maybe */
- if (flags & FLAG_NCR53C400)
- hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags;
- else
- hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
-
- hostdata->host = instance;
- hostdata->time_expires = 0;
+ hostdata->dma_len = 0;
-#ifndef AUTOSENSE
- if ((instance->cmd_per_lun > 1) || instance->can_queue > 1)
- printk(KERN_WARNING "scsi%d : WARNING : support for multiple outstanding commands enabled\n" " without AUTOSENSE option, contingent allegiance conditions may\n"
- " be incorrectly cleared.\n", instance->host_no);
-#endif /* def AUTOSENSE */
+ spin_lock_init(&hostdata->lock);
+ hostdata->connected = NULL;
+ hostdata->sensing = NULL;
+ INIT_LIST_HEAD(&hostdata->autosense);
+ INIT_LIST_HEAD(&hostdata->unissued);
+ INIT_LIST_HEAD(&hostdata->disconnected);
+
+ hostdata->flags = flags;
+
+ INIT_WORK(&hostdata->main_task, NCR5380_main);
+ hostdata->work_q = alloc_workqueue("ncr5380_%d",
+ WQ_UNBOUND | WQ_MEM_RECLAIM,
+ 0, instance->host_no);
+ if (!hostdata->work_q)
+ return -ENOMEM;
+
+ snprintf(hostdata->info, sizeof(hostdata->info),
+ "%s, irq %d, io_port 0x%lx, base 0x%lx, can_queue %d, cmd_per_lun %d, sg_tablesize %d, this_id %d, flags { %s%s%s}",
+ instance->hostt->name, instance->irq, hostdata->io_port,
+ hostdata->base, instance->can_queue, instance->cmd_per_lun,
+ instance->sg_tablesize, instance->this_id,
+ hostdata->flags & FLAG_DMA_FIXUP ? "DMA_FIXUP " : "",
+ hostdata->flags & FLAG_NO_PSEUDO_DMA ? "NO_PSEUDO_DMA " : "",
+ hostdata->flags & FLAG_TOSHIBA_DELAY ? "TOSHIBA_DELAY " : "");
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(TARGET_COMMAND_REG, 0);
NCR5380_write(SELECT_ENABLE_REG, 0);
-#ifdef NCR53C400
- if (hostdata->flags & FLAG_NCR53C400) {
- NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
- }
-#endif
+ /* Calibrate register polling loop */
+ i = 0;
+ deadline = jiffies + 1;
+ do {
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ deadline += msecs_to_jiffies(256);
+ do {
+ NCR5380_read(STATUS_REG);
+ ++i;
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ accesses_per_ms = i / 256;
+ hostdata->poll_loops = NCR5380_REG_POLL_TIME * accesses_per_ms / 2;
- /*
- * Detect and correct bus wedge problems.
- *
- * If the system crashed, it may have crashed in a state
- * where a SCSI command was still executing, and the
- * SCSI bus is not in a BUS FREE STATE.
- *
- * If this is the case, we'll try to abort the currently
- * established nexus which we know nothing about, and that
- * failing, do a hard reset of the SCSI bus
- */
+ return 0;
+}
+
+/**
+ * NCR5380_maybe_reset_bus - Detect and correct bus wedge problems.
+ * @instance: adapter to check
+ *
+ * If the system crashed, it may have crashed with a connected target and
+ * the SCSI bus busy. Check for BUS FREE phase. If not, try to abort the
+ * currently established nexus, which we know nothing about. Failing that
+ * do a bus reset.
+ *
+ * Note that a bus reset will cause the chip to assert IRQ.
+ *
+ * Returns 0 if successful, otherwise -ENXIO.
+ */
+
+static int NCR5380_maybe_reset_bus(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ int pass;
for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) && pass <= 6; ++pass) {
switch (pass) {
case 1:
case 3:
case 5:
- printk(KERN_INFO "scsi%d: SCSI bus busy, waiting up to five seconds\n", instance->host_no);
- timeout = jiffies + 5 * HZ;
- NCR5380_poll_politely(instance, STATUS_REG, SR_BSY, 0, 5*HZ);
+ shost_printk(KERN_ERR, instance, "SCSI bus busy, waiting up to five seconds\n");
+ NCR5380_poll_politely(hostdata,
+ STATUS_REG, SR_BSY, 0, 5 * HZ);
break;
case 2:
- printk(KERN_WARNING "scsi%d: bus busy, attempting abort\n", instance->host_no);
- do_abort(instance);
+ shost_printk(KERN_ERR, instance, "bus busy, attempting abort\n");
+ do_abort(instance, 1);
break;
case 4:
- printk(KERN_WARNING "scsi%d: bus busy, attempting reset\n", instance->host_no);
+ shost_printk(KERN_ERR, instance, "bus busy, attempting reset\n");
do_reset(instance);
+ /* Wait after a reset; the SCSI standard calls for
+ * 250ms, we wait 500ms to be on the safe side.
+ * But some Toshiba CD-ROMs need ten times that.
+ */
+ if (hostdata->flags & FLAG_TOSHIBA_DELAY)
+ msleep(2500);
+ else
+ msleep(500);
break;
case 6:
- printk(KERN_ERR "scsi%d: bus locked solid or invalid override\n", instance->host_no);
+ shost_printk(KERN_ERR, instance, "bus locked solid\n");
return -ENXIO;
}
}
@@ -928,499 +504,618 @@ static int __devinit NCR5380_init(struct Scsi_Host *instance, int flags)
}
/**
- * NCR5380_exit - remove an NCR5380
- * @instance: adapter to remove
+ * NCR5380_exit - remove an NCR5380
+ * @instance: adapter to remove
+ *
+ * Assumes that no more work can be queued (e.g. by NCR5380_intr).
*/
static void NCR5380_exit(struct Scsi_Host *instance)
{
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
- cancel_delayed_work_sync(&hostdata->coroutine);
+ cancel_work_sync(&hostdata->main_task);
+ destroy_workqueue(hostdata->work_q);
}
/**
- * NCR5380_queue_command - queue a command
- * @cmd: SCSI command
- * @done: completion handler
- *
- * cmd is added to the per instance issue_queue, with minor
- * twiddling done to the host specific fields of cmd. If the
- * main coroutine is not running, it is restarted.
+ * complete_cmd - finish processing a command and return it to the SCSI ML
+ * @instance: the host instance
+ * @cmd: command to complete
+ */
+
+static void complete_cmd(struct Scsi_Host *instance,
+ struct scsi_cmnd *cmd)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ dsprintk(NDEBUG_QUEUES, instance, "complete_cmd: cmd %p\n", cmd);
+
+ if (hostdata->sensing == cmd) {
+ /* Autosense processing ends here */
+ if (get_status_byte(cmd) != SAM_STAT_GOOD) {
+ scsi_eh_restore_cmnd(cmd, &hostdata->ses);
+ } else {
+ scsi_eh_restore_cmnd(cmd, &hostdata->ses);
+ set_status_byte(cmd, SAM_STAT_CHECK_CONDITION);
+ }
+ hostdata->sensing = NULL;
+ }
+
+ scsi_done(cmd);
+}
+
+/**
+ * NCR5380_queue_command - queue a command
+ * @instance: the relevant SCSI adapter
+ * @cmd: SCSI command
*
- * Locks: host lock taken by caller
+ * cmd is added to the per-instance issue queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
*/
-static int NCR5380_queue_command_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+static int NCR5380_queue_command(struct Scsi_Host *instance,
+ struct scsi_cmnd *cmd)
{
- struct Scsi_Host *instance = cmd->device->host;
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- Scsi_Cmnd *tmp;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(cmd);
+ unsigned long flags;
#if (NDEBUG & NDEBUG_NO_WRITE)
switch (cmd->cmnd[0]) {
case WRITE_6:
case WRITE_10:
- printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", instance->host_no);
+ shost_printk(KERN_DEBUG, instance, "WRITE attempted with NDEBUG_NO_WRITE set\n");
cmd->result = (DID_ERROR << 16);
- done(cmd);
+ scsi_done(cmd);
return 0;
}
-#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
-#ifdef NCR5380_STATS
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingw++;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingr++;
- break;
- }
-#endif
+ cmd->result = 0;
- /*
- * We use the host_scribble field as a pointer to the next command
- * in a queue
- */
+ spin_lock_irqsave(&hostdata->lock, flags);
- cmd->host_scribble = NULL;
- cmd->scsi_done = done;
- cmd->result = 0;
+ if (!NCR5380_acquire_dma_irq(instance)) {
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
- /*
- * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
* commands are added to the head of the queue since any command will
- * clear the contingent allegiance condition that exists and the
+ * clear the contingent allegiance condition that exists and the
* sense data is only guaranteed to be valid while the condition exists.
*/
- if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
- LIST(cmd, hostdata->issue_queue);
- cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
- hostdata->issue_queue = cmd;
- } else {
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble);
- LIST(cmd, tmp);
- tmp->host_scribble = (unsigned char *) cmd;
- }
- dprintk(NDEBUG_QUEUES, ("scsi%d : command added to %s of queue\n", instance->host_no, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
+ if (cmd->cmnd[0] == REQUEST_SENSE)
+ list_add(&ncmd->list, &hostdata->unissued);
+ else
+ list_add_tail(&ncmd->list, &hostdata->unissued);
+
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+
+ dsprintk(NDEBUG_QUEUES, instance, "command %p added to %s of queue\n",
+ cmd, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
- /* Run the coroutine if it isn't already running. */
/* Kick off command processing */
- schedule_delayed_work(&hostdata->coroutine, 0);
+ queue_work(hostdata->work_q, &hostdata->main_task);
return 0;
}
-static DEF_SCSI_QCMD(NCR5380_queue_command)
+static inline void maybe_release_dma_irq(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ /* Caller does the locking needed to set & test these data atomically */
+ if (list_empty(&hostdata->disconnected) &&
+ list_empty(&hostdata->unissued) &&
+ list_empty(&hostdata->autosense) &&
+ !hostdata->connected &&
+ !hostdata->selecting) {
+ NCR5380_release_dma_irq(instance);
+ }
+}
+
+/**
+ * dequeue_next_cmd - dequeue a command for processing
+ * @instance: the scsi host instance
+ *
+ * Priority is given to commands on the autosense queue. These commands
+ * need autosense because of a CHECK CONDITION result.
+ *
+ * Returns a command pointer if a command is found for a target that is
+ * not already busy. Otherwise returns NULL.
+ */
+
+static struct scsi_cmnd *dequeue_next_cmd(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct NCR5380_cmd *ncmd;
+ struct scsi_cmnd *cmd;
+
+ if (hostdata->sensing || list_empty(&hostdata->autosense)) {
+ list_for_each_entry(ncmd, &hostdata->unissued, list) {
+ cmd = NCR5380_to_scmd(ncmd);
+ dsprintk(NDEBUG_QUEUES, instance, "dequeue: cmd=%p target=%d busy=0x%02x lun=%llu\n",
+ cmd, scmd_id(cmd), hostdata->busy[scmd_id(cmd)], cmd->device->lun);
+
+ if (!(hostdata->busy[scmd_id(cmd)] & (1 << cmd->device->lun))) {
+ list_del(&ncmd->list);
+ dsprintk(NDEBUG_QUEUES, instance,
+ "dequeue: removed %p from issue queue\n", cmd);
+ return cmd;
+ }
+ }
+ } else {
+ /* Autosense processing begins here */
+ ncmd = list_first_entry(&hostdata->autosense,
+ struct NCR5380_cmd, list);
+ list_del(&ncmd->list);
+ cmd = NCR5380_to_scmd(ncmd);
+ dsprintk(NDEBUG_QUEUES, instance,
+ "dequeue: removed %p from autosense queue\n", cmd);
+ scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0);
+ hostdata->sensing = cmd;
+ return cmd;
+ }
+ return NULL;
+}
+
+static void requeue_cmd(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(cmd);
+
+ if (hostdata->sensing == cmd) {
+ scsi_eh_restore_cmnd(cmd, &hostdata->ses);
+ list_add(&ncmd->list, &hostdata->autosense);
+ hostdata->sensing = NULL;
+ } else
+ list_add(&ncmd->list, &hostdata->unissued);
+}
/**
- * NCR5380_main - NCR state machines
- *
- * NCR5380_main is a coroutine that runs as long as more work can
- * be done on the NCR5380 host adapters in a system. Both
- * NCR5380_queue_command() and NCR5380_intr() will try to start it
- * in case it is not running.
- *
- * Locks: called as its own thread with no locks held. Takes the
- * host lock and called routines may take the isa dma lock.
+ * NCR5380_main - NCR state machines
+ *
+ * NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
*/
static void NCR5380_main(struct work_struct *work)
{
struct NCR5380_hostdata *hostdata =
- container_of(work, struct NCR5380_hostdata, coroutine.work);
+ container_of(work, struct NCR5380_hostdata, main_task);
struct Scsi_Host *instance = hostdata->host;
- Scsi_Cmnd *tmp, *prev;
int done;
-
- spin_lock_irq(instance->host_lock);
+
do {
- /* Lock held here */
done = 1;
- if (!hostdata->connected && !hostdata->selecting) {
- dprintk(NDEBUG_MAIN, ("scsi%d : not connected\n", instance->host_no));
+
+ spin_lock_irq(&hostdata->lock);
+ while (!hostdata->connected && !hostdata->selecting) {
+ struct scsi_cmnd *cmd = dequeue_next_cmd(instance);
+
+ if (!cmd)
+ break;
+
+ dsprintk(NDEBUG_MAIN, instance, "main: dequeued %p\n", cmd);
+
/*
- * Search through the issue_queue for a command destined
- * for a target that's not busy.
+ * Attempt to establish an I_T_L nexus here.
+ * On success, instance->hostdata->connected is set.
+ * On failure, we must add the command back to the
+ * issue queue so we can keep trying.
+ */
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
*/
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
- {
- if (prev != tmp)
- dprintk(NDEBUG_LISTS, ("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun));
- /* When we find one, remove it from the issue queue. */
- if (!(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))) {
- if (prev) {
- REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble);
- prev->host_scribble = tmp->host_scribble;
- } else {
- REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
- hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
- }
- tmp->host_scribble = NULL;
- /*
- * Attempt to establish an I_T_L nexus here.
- * On success, instance->hostdata->connected is set.
- * On failure, we must add the command back to the
- * issue queue so we can keep trying.
- */
- dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, ("scsi%d : main() : command for target %d lun %d removed from issue_queue\n", instance->host_no, tmp->target, tmp->lun));
-
- /*
- * A successful selection is defined as one that
- * leaves us with the command connected and
- * in hostdata->connected, OR has terminated the
- * command.
- *
- * With successful commands, we fall through
- * and see if we can do an information transfer,
- * with failures we will restart.
- */
- hostdata->selecting = NULL;
- /* RvC: have to preset this to indicate a new command is being performed */
-
- if (!NCR5380_select(instance, tmp,
- /*
- * REQUEST SENSE commands are issued without tagged
- * queueing, even on SCSI-II devices because the
- * contingent allegiance condition exists for the
- * entire unit.
- */
- (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) {
- break;
- } else {
- LIST(tmp, hostdata->issue_queue);
- tmp->host_scribble = (unsigned char *) hostdata->issue_queue;
- hostdata->issue_queue = tmp;
- done = 0;
- dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, ("scsi%d : main(): select() failed, returned to issue_queue\n", instance->host_no));
- }
- /* lock held here still */
- } /* if target/lun is not busy */
- } /* for */
- /* exited locked */
- } /* if (!hostdata->connected) */
- if (hostdata->selecting) {
- tmp = (Scsi_Cmnd *) hostdata->selecting;
- /* Selection will drop and retake the lock */
- if (!NCR5380_select(instance, tmp, (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) {
- /* Ok ?? */
+ if (!NCR5380_select(instance, cmd)) {
+ dsprintk(NDEBUG_MAIN, instance, "main: select complete\n");
} else {
- /* RvC: device failed, so we wait a long time
- this is needed for Mustek scanners, that
- do not respond to commands immediately
- after a scan */
- printk(KERN_DEBUG "scsi%d: device %d did not respond in time\n", instance->host_no, tmp->device->id);
- LIST(tmp, hostdata->issue_queue);
- tmp->host_scribble = (unsigned char *) hostdata->issue_queue;
- hostdata->issue_queue = tmp;
- NCR5380_set_timer(hostdata, USLEEP_WAITLONG);
+ dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, instance,
+ "main: select failed, returning %p to queue\n", cmd);
+ requeue_cmd(instance, cmd);
}
- } /* if hostdata->selecting */
- if (hostdata->connected
-#ifdef REAL_DMA
- && !hostdata->dmalen
-#endif
- && (!hostdata->time_expires || time_before_eq(hostdata->time_expires, jiffies))
- ) {
- dprintk(NDEBUG_MAIN, ("scsi%d : main() : performing information transfer\n", instance->host_no));
+ }
+ if (hostdata->connected && !hostdata->dma_len) {
+ dsprintk(NDEBUG_MAIN, instance, "main: performing information transfer\n");
NCR5380_information_transfer(instance);
- dprintk(NDEBUG_MAIN, ("scsi%d : main() : done set false\n", instance->host_no));
done = 0;
- } else
- break;
+ }
+ if (!hostdata->connected) {
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ maybe_release_dma_irq(instance);
+ }
+ spin_unlock_irq(&hostdata->lock);
+ if (!done)
+ cond_resched();
} while (!done);
-
- spin_unlock_irq(instance->host_lock);
}
-#ifndef DONT_USE_INTR
+/*
+ * NCR5380_dma_complete - finish DMA transfer
+ * @instance: the scsi host instance
+ *
+ * Called by the interrupt handler when DMA finishes or a phase
+ * mismatch occurs (which would end the DMA transfer).
+ */
+
+static void NCR5380_dma_complete(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(hostdata->connected);
+ int transferred;
+ unsigned char **data;
+ int *count;
+ int saved_data = 0, overrun = 0;
+ unsigned char p;
+
+ if (hostdata->read_overruns) {
+ p = ncmd->phase;
+ if (p & SR_IO) {
+ udelay(10);
+ if ((NCR5380_read(BUS_AND_STATUS_REG) &
+ (BASR_PHASE_MATCH | BASR_ACK)) ==
+ (BASR_PHASE_MATCH | BASR_ACK)) {
+ saved_data = NCR5380_read(INPUT_DATA_REG);
+ overrun = 1;
+ dsprintk(NDEBUG_DMA, instance, "read overrun handled\n");
+ }
+ }
+ }
+
+#ifdef CONFIG_SUN3
+ if (sun3scsi_dma_finish(hostdata->connected->sc_data_direction)) {
+ pr_err("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n",
+ instance->host_no);
+ BUG();
+ }
+
+ if ((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) ==
+ (BASR_PHASE_MATCH | BASR_ACK)) {
+ pr_err("scsi%d: BASR %02x\n", instance->host_no,
+ NCR5380_read(BUS_AND_STATUS_REG));
+ pr_err("scsi%d: bus stuck in data phase -- probably a single byte overrun!\n",
+ instance->host_no);
+ BUG();
+ }
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+
+ transferred = hostdata->dma_len - NCR5380_dma_residual(hostdata);
+ hostdata->dma_len = 0;
+
+ data = (unsigned char **)&ncmd->ptr;
+ count = &ncmd->this_residual;
+ *data += transferred;
+ *count -= transferred;
+
+ if (hostdata->read_overruns) {
+ int cnt, toPIO;
+
+ if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) {
+ cnt = toPIO = hostdata->read_overruns;
+ if (overrun) {
+ dsprintk(NDEBUG_DMA, instance,
+ "Got an input overrun, using saved byte\n");
+ *(*data)++ = saved_data;
+ (*count)--;
+ cnt--;
+ toPIO--;
+ }
+ if (toPIO > 0) {
+ dsprintk(NDEBUG_DMA, instance,
+ "Doing %d byte PIO to 0x%p\n", cnt, *data);
+ NCR5380_transfer_pio(instance, &p, &cnt, data, 0);
+ *count -= toPIO - cnt;
+ }
+ }
+ }
+}
/**
- * NCR5380_intr - generic NCR5380 irq handler
- * @irq: interrupt number
- * @dev_id: device info
- *
- * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
- * from the disconnected queue, and restarting NCR5380_main()
- * as required.
- *
- * Locks: takes the needed instance locks
+ * NCR5380_intr - generic NCR5380 irq handler
+ * @irq: interrupt number
+ * @dev_id: device info
+ *
+ * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
+ *
+ * The chip can assert IRQ in any of six different conditions. The IRQ flag
+ * is then cleared by reading the Reset Parity/Interrupt Register (RPIR).
+ * Three of these six conditions are latched in the Bus and Status Register:
+ * - End of DMA (cleared by ending DMA Mode)
+ * - Parity error (cleared by reading RPIR)
+ * - Loss of BSY (cleared by reading RPIR)
+ * Two conditions have flag bits that are not latched:
+ * - Bus phase mismatch (non-maskable in DMA Mode, cleared by ending DMA Mode)
+ * - Bus reset (non-maskable)
+ * The remaining condition has no flag bit at all:
+ * - Selection/reselection
+ *
+ * Hence, establishing the cause(s) of any interrupt is partly guesswork.
+ * In "The DP8490 and DP5380 Comparison Guide", National Semiconductor
+ * claimed that "the design of the [DP8490] interrupt logic ensures
+ * interrupts will not be lost (they can be on the DP5380)."
+ * The L5380/53C80 datasheet from LOGIC Devices has more details.
+ *
+ * Checking for bus reset by reading RST is futile because of interrupt
+ * latency, but a bus reset will reset chip logic. Checking for parity error
+ * is unnecessary because that interrupt is never enabled. A Loss of BSY
+ * condition will clear DMA Mode. We can tell when this occurs because the
+ * Busy Monitor interrupt is enabled together with DMA Mode.
*/
-static irqreturn_t NCR5380_intr(int dummy, void *dev_id)
+static irqreturn_t __maybe_unused NCR5380_intr(int irq, void *dev_id)
{
- NCR5380_local_declare();
struct Scsi_Host *instance = dev_id;
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- int done;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ int handled = 0;
unsigned char basr;
unsigned long flags;
- dprintk(NDEBUG_INTR, ("scsi : NCR5380 irq %d triggered\n",
- instance->irq));
+ spin_lock_irqsave(&hostdata->lock, flags);
- do {
- done = 1;
- spin_lock_irqsave(instance->host_lock, flags);
- /* Look for pending interrupts */
- NCR5380_setup(instance);
- basr = NCR5380_read(BUS_AND_STATUS_REG);
- /* XXX dispatch to appropriate routine if found and done=0 */
- if (basr & BASR_IRQ) {
- NCR5380_dprint(NDEBUG_INTR, instance);
- if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) {
- done = 0;
- dprintk(NDEBUG_INTR, ("scsi%d : SEL interrupt\n", instance->host_no));
- NCR5380_reselect(instance);
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- } else if (basr & BASR_PARITY_ERROR) {
- dprintk(NDEBUG_INTR, ("scsi%d : PARITY interrupt\n", instance->host_no));
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
- dprintk(NDEBUG_INTR, ("scsi%d : RESET interrupt\n", instance->host_no));
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- } else {
-#if defined(REAL_DMA)
- /*
- * We should only get PHASE MISMATCH and EOP interrupts
- * if we have DMA enabled, so do a sanity check based on
- * the current setting of the MODE register.
- */
-
- if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr & BASR_END_DMA_TRANSFER) || !(basr & BASR_PHASE_MATCH))) {
- int transferred;
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ if (basr & BASR_IRQ) {
+ unsigned char mr = NCR5380_read(MODE_REG);
+ unsigned char sr = NCR5380_read(STATUS_REG);
- if (!hostdata->connected)
- panic("scsi%d : received end of DMA interrupt with no connected cmd\n", instance->hostno);
+ dsprintk(NDEBUG_INTR, instance, "IRQ %d, BASR 0x%02x, SR 0x%02x, MR 0x%02x\n",
+ irq, basr, sr, mr);
- transferred = (hostdata->dmalen - NCR5380_dma_residual(instance));
- hostdata->connected->SCp.this_residual -= transferred;
- hostdata->connected->SCp.ptr += transferred;
- hostdata->dmalen = 0;
+ if ((mr & MR_DMA_MODE) || (mr & MR_MONITOR_BSY)) {
+ /* Probably End of DMA, Phase Mismatch or Loss of BSY.
+ * We ack IRQ after clearing Mode Register. Workarounds
+ * for End of DMA errata need to happen in DMA Mode.
+ */
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-
- /* FIXME: we need to poll briefly then defer a workqueue task ! */
- NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG, BASR_ACK, 0, 2*HZ);
+ dsprintk(NDEBUG_INTR, instance, "interrupt in DMA mode\n");
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- }
-#else
- dprintk(NDEBUG_INTR, ("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)));
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-#endif
+ if (hostdata->connected) {
+ NCR5380_dma_complete(instance);
+ queue_work(hostdata->work_q, &hostdata->main_task);
+ } else {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
}
- } /* if BASR_IRQ */
- spin_unlock_irqrestore(instance->host_lock, flags);
- if(!done)
- schedule_delayed_work(&hostdata->coroutine, 0);
- } while (!done);
- return IRQ_HANDLED;
-}
+ } else if ((NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_mask) &&
+ (sr & (SR_SEL | SR_IO | SR_BSY | SR_RST)) == (SR_SEL | SR_IO)) {
+ /* Probably reselected */
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-#endif
+ dsprintk(NDEBUG_INTR, instance, "interrupt with SEL and IO\n");
-/**
- * collect_stats - collect stats on a scsi command
- * @hostdata: adapter
- * @cmd: command being issued
- *
- * Update the statistical data by parsing the command in question
- */
-
-static void collect_stats(struct NCR5380_hostdata *hostdata, Scsi_Cmnd * cmd)
-{
-#ifdef NCR5380_STATS
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[scmd_id(cmd)] += (jiffies - hostdata->timebase);
- hostdata->pendingw--;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[scmd_id(cmd)] += (jiffies - hostdata->timebase);
- hostdata->pendingr--;
- break;
- }
+ if (!hostdata->connected) {
+ NCR5380_reselect(instance);
+ queue_work(hostdata->work_q, &hostdata->main_task);
+ }
+ if (!hostdata->connected)
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ } else {
+ /* Probably Bus Reset */
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+
+ if (sr & SR_RST) {
+ /* Certainly Bus Reset */
+ shost_printk(KERN_WARNING, instance,
+ "bus reset interrupt\n");
+ bus_reset_cleanup(instance);
+ } else {
+ dsprintk(NDEBUG_INTR, instance, "unknown interrupt\n");
+ }
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
#endif
-}
+ }
+ handled = 1;
+ } else {
+ dsprintk(NDEBUG_INTR, instance, "interrupt without IRQ bit\n");
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
+ }
+ spin_unlock_irqrestore(&hostdata->lock, flags);
-/*
- * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
- * int tag);
- *
- * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
- * including ARBITRATION, SELECTION, and initial message out for
- * IDENTIFY and queue messages.
+ return IRQ_RETVAL(handled);
+}
+
+/**
+ * NCR5380_select - attempt arbitration and selection for a given command
+ * @instance: the Scsi_Host instance
+ * @cmd: the scsi_cmnd to execute
*
- * Inputs : instance - instantiation of the 5380 driver on which this
- * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
- * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
- * the command that is presently connected.
- *
- * Returns : -1 if selection could not execute for some reason,
- * 0 if selection succeeded or failed because the target
- * did not respond.
+ * This routine establishes an I_T_L nexus for a SCSI command. This involves
+ * ARBITRATION, SELECTION and MESSAGE OUT phases and an IDENTIFY message.
*
- * Side effects :
- * If bus busy, arbitration failed, etc, NCR5380_select() will exit
- * with registers as they should have been on entry - ie
- * SELECT_ENABLE will be set appropriately, the NCR5380
- * will cease to drive any SCSI bus signals.
+ * Returns true if the operation should be retried.
+ * Returns false if it should not be retried.
*
- * If successful : I_T_L or I_T_L_Q nexus will be established,
- * instance->connected will be set to cmd.
- * SELECT interrupt will be disabled.
+ * Side effects :
+ * If bus busy, arbitration failed, etc, NCR5380_select() will exit
+ * with registers as they should have been on entry - ie
+ * SELECT_ENABLE will be set appropriately, the NCR5380
+ * will cease to drive any SCSI bus signals.
*
- * If failed (no target) : cmd->scsi_done() will be called, and the
- * cmd->result host byte set to DID_BAD_TARGET.
+ * If successful : the I_T_L nexus will be established, and
+ * hostdata->connected will be set to cmd.
+ * SELECT interrupt will be disabled.
*
- * Locks: caller holds hostdata lock in IRQ mode
+ * If failed (no target) : scsi_done() will be called, and the
+ * cmd->result host byte set to DID_BAD_TARGET.
*/
-
-static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag)
+
+static bool NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
+ __releases(&hostdata->lock) __acquires(&hostdata->lock)
{
- NCR5380_local_declare();
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
unsigned char tmp[3], phase;
unsigned char *data;
int len;
- unsigned long timeout;
- unsigned char value;
int err;
- NCR5380_setup(instance);
-
- if (hostdata->selecting)
- goto part2;
-
- hostdata->restart_select = 0;
+ bool ret = true;
+ bool can_disconnect = instance->irq != NO_IRQ &&
+ cmd->cmnd[0] != REQUEST_SENSE &&
+ (disconnect_mask & BIT(scmd_id(cmd)));
NCR5380_dprint(NDEBUG_ARBITRATION, instance);
- dprintk(NDEBUG_ARBITRATION, ("scsi%d : starting arbitration, id = %d\n", instance->host_no, instance->this_id));
+ dsprintk(NDEBUG_ARBITRATION, instance, "starting arbitration, id = %d\n",
+ instance->this_id);
- /*
- * Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ /*
+ * Arbitration and selection phases are slow and involve dropping the
+ * lock, so we have to watch out for EH. An exception handler may
+ * change 'selecting' to NULL. This function will then return false
+ * so that the caller will forget about 'cmd'. (During information
+ * transfer phases, EH may change 'connected' to NULL.)
+ */
+ hostdata->selecting = cmd;
+
+ /*
+ * Set the phase bits to 0, otherwise the NCR5380 won't drive the
* data bus during SELECTION.
*/
NCR5380_write(TARGET_COMMAND_REG, 0);
- /*
+ /*
* Start arbitration.
*/
NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
NCR5380_write(MODE_REG, MR_ARBITRATE);
+ /* The chip now waits for BUS FREE phase. Then after the 800 ns
+ * Bus Free Delay, arbitration will begin.
+ */
- /* We can be relaxed here, interrupts are on, we are
- in workqueue context, the birds are singing in the trees */
- spin_unlock_irq(instance->host_lock);
- err = NCR5380_poll_politely(instance, INITIATOR_COMMAND_REG, ICR_ARBITRATION_PROGRESS, ICR_ARBITRATION_PROGRESS, 5*HZ);
- spin_lock_irq(instance->host_lock);
+ spin_unlock_irq(&hostdata->lock);
+ err = NCR5380_poll_politely2(hostdata, MODE_REG, MR_ARBITRATE, 0,
+ INITIATOR_COMMAND_REG, ICR_ARBITRATION_PROGRESS,
+ ICR_ARBITRATION_PROGRESS, HZ);
+ spin_lock_irq(&hostdata->lock);
+ if (!(NCR5380_read(MODE_REG) & MR_ARBITRATE)) {
+ /* Reselection interrupt */
+ goto out;
+ }
+ if (!hostdata->selecting) {
+ /* Command was aborted */
+ NCR5380_write(MODE_REG, MR_BASE);
+ return false;
+ }
if (err < 0) {
- printk(KERN_DEBUG "scsi: arbitration timeout at %d\n", __LINE__);
NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- goto failed;
+ shost_printk(KERN_ERR, instance,
+ "select: arbitration timeout\n");
+ goto out;
}
+ spin_unlock_irq(&hostdata->lock);
- dprintk(NDEBUG_ARBITRATION, ("scsi%d : arbitration complete\n", instance->host_no));
-
- /*
- * The arbitration delay is 2.2us, but this is a minimum and there is
- * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
- * the integral nature of udelay().
- *
- */
-
+ /* The SCSI-2 arbitration delay is 2.4 us */
udelay(3);
/* Check for lost arbitration */
- if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) || (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
- NCR5380_write(MODE_REG, MR_BASE);
- dprintk(NDEBUG_ARBITRATION, ("scsi%d : lost arbitration, deasserting MR_ARBITRATE\n", instance->host_no));
- goto failed;
- }
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
-
- if (!(hostdata->flags & FLAG_DTC3181E) &&
- /* RvC: DTC3181E has some trouble with this
- * so we simply removed it. Seems to work with
- * only Mustek scanner attached
- */
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- dprintk(NDEBUG_ARBITRATION, ("scsi%d : lost arbitration, deasserting ICR_ASSERT_SEL\n", instance->host_no));
- goto failed;
+ dsprintk(NDEBUG_ARBITRATION, instance, "lost arbitration, deasserting MR_ARBITRATE\n");
+ spin_lock_irq(&hostdata->lock);
+ goto out;
}
- /*
- * Again, bus clear + bus settle time is 1.2us, however, this is
+
+ /* After/during arbitration, BSY should be asserted.
+ * IBM DPES-31080 Version S31Q works now
+ * Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman)
+ */
+ NCR5380_write(INITIATOR_COMMAND_REG,
+ ICR_BASE | ICR_ASSERT_SEL | ICR_ASSERT_BSY);
+
+ /*
+ * Again, bus clear + bus settle time is 1.2us, however, this is
* a minimum so we'll udelay ceil(1.2)
*/
- udelay(2);
+ if (hostdata->flags & FLAG_TOSHIBA_DELAY)
+ udelay(15);
+ else
+ udelay(2);
- dprintk(NDEBUG_ARBITRATION, ("scsi%d : won arbitration\n", instance->host_no));
+ spin_lock_irq(&hostdata->lock);
- /*
- * Now that we have won arbitration, start Selection process, asserting
+ /* NCR5380_reselect() clears MODE_REG after a reselection interrupt */
+ if (!(NCR5380_read(MODE_REG) & MR_ARBITRATE))
+ goto out;
+
+ if (!hostdata->selecting) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return false;
+ }
+
+ dsprintk(NDEBUG_ARBITRATION, instance, "won arbitration\n");
+
+ /*
+ * Now that we have won arbitration, start Selection process, asserting
* the host and target ID's on the SCSI bus.
*/
- NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << scmd_id(cmd))));
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask | (1 << scmd_id(cmd)));
- /*
+ /*
* Raise ATN while SEL is true before BSY goes false from arbitration,
* since this is the only way to guarantee that we'll get a MESSAGE OUT
* phase immediately after selection.
*/
- NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL);
NCR5380_write(MODE_REG, MR_BASE);
- /*
+ /*
* Reselect interrupts must be turned off prior to the dropping of BSY,
* otherwise we will trigger an interrupt.
*/
NCR5380_write(SELECT_ENABLE_REG, 0);
+ spin_unlock_irq(&hostdata->lock);
+
/*
- * The initiator shall then wait at least two deskew delays and release
+ * The initiator shall then wait at least two deskew delays and release
* the BSY signal.
*/
- udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
+ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
/* Reset BSY */
- NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_ATN | ICR_ASSERT_SEL);
- /*
+ /*
* Something weird happens when we cease to drive BSY - looks
- * like the board/chip is letting us do another read before the
+ * like the board/chip is letting us do another read before the
* appropriate propagation delay has expired, and we're confusing
* a BSY signal from ourselves as the target's response to SELECTION.
*
* A small delay (the 'C++' frontend breaks the pipeline with an
* unnecessary jump, making it work on my 386-33/Trantor T128, the
- * tighter 'C' code breaks and requires this) solves the problem -
- * the 1 us delay is arbitrary, and only used because this delay will
- * be the same on other platforms and since it works here, it should
+ * tighter 'C' code breaks and requires this) solves the problem -
+ * the 1 us delay is arbitrary, and only used because this delay will
+ * be the same on other platforms and since it works here, it should
* work there.
*
* wingel suggests that this could be due to failing to wait
@@ -1429,50 +1124,43 @@ static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag)
udelay(1);
- dprintk(NDEBUG_SELECTION, ("scsi%d : selecting target %d\n", instance->host_no, scmd_id(cmd)));
+ dsprintk(NDEBUG_SELECTION, instance, "selecting target %d\n", scmd_id(cmd));
- /*
- * The SCSI specification calls for a 250 ms timeout for the actual
+ /*
+ * The SCSI specification calls for a 250 ms timeout for the actual
* selection.
*/
- timeout = jiffies + (250 * HZ / 1000);
-
- /*
- * XXX very interesting - we're seeing a bounce where the BSY we
- * asserted is being reflected / still asserted (propagation delay?)
- * and it's detecting as true. Sigh.
- */
-
- hostdata->select_time = 0; /* we count the clock ticks at which we polled */
- hostdata->selecting = cmd;
+ err = NCR5380_poll_politely(hostdata, STATUS_REG, SR_BSY, SR_BSY,
+ msecs_to_jiffies(250));
-part2:
- /* RvC: here we enter after a sleeping period, or immediately after
- execution of part 1
- we poll only once ech clock tick */
- value = NCR5380_read(STATUS_REG) & (SR_BSY | SR_IO);
-
- if (!value && (hostdata->select_time < HZ/4)) {
- /* RvC: we still must wait for a device response */
- hostdata->select_time++; /* after 25 ticks the device has failed */
- NCR5380_set_timer(hostdata, 1);
- return 0; /* RvC: we return here with hostdata->selecting set,
- to go to sleep */
- }
-
- hostdata->selecting = NULL;/* clear this pointer, because we passed the
- waiting period */
if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) {
+ spin_lock_irq(&hostdata->lock);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
NCR5380_reselect(instance);
- printk("scsi%d : reselection after won arbitration?\n", instance->host_no);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
+ shost_printk(KERN_ERR, instance, "reselection after won arbitration?\n");
+ goto out;
}
- /*
- * No less than two deskew delays after the initiator detects the
- * BSY signal is true, it shall release the SEL signal and may
+
+ if (err < 0) {
+ spin_lock_irq(&hostdata->lock);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /* Can't touch cmd if it has been reclaimed by the scsi ML */
+ if (!hostdata->selecting)
+ return false;
+
+ cmd->result = DID_BAD_TARGET << 16;
+ complete_cmd(instance, cmd);
+ dsprintk(NDEBUG_SELECTION, instance,
+ "target did not respond within 250ms\n");
+ ret = false;
+ goto out;
+ }
+
+ /*
+ * No less than two deskew delays after the initiator detects the
+ * BSY signal is true, it shall release the SEL signal and may
* change the DATA BUS. -wingel
*/
@@ -1480,161 +1168,117 @@ part2:
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- if (hostdata->targets_present & (1 << scmd_id(cmd))) {
- printk(KERN_DEBUG "scsi%d : weirdness\n", instance->host_no);
- if (hostdata->restart_select)
- printk(KERN_DEBUG "\trestart select\n");
- NCR5380_dprint(NDEBUG_SELECTION, instance);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
- }
- cmd->result = DID_BAD_TARGET << 16;
- collect_stats(hostdata, cmd);
- cmd->scsi_done(cmd);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- dprintk(NDEBUG_SELECTION, ("scsi%d : target did not respond within 250ms\n", instance->host_no));
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return 0;
- }
- hostdata->targets_present |= (1 << scmd_id(cmd));
-
/*
- * Since we followed the SCSI spec, and raised ATN while SEL
+ * Since we followed the SCSI spec, and raised ATN while SEL
* was true but before BSY was false during selection, the information
* transfer phase should be a MESSAGE OUT phase so that we can send the
* IDENTIFY message.
- *
- * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
- * message (2 bytes) with a tag ID that we increment with every command
- * until it wraps back to 0.
- *
- * XXX - it turns out that there are some broken SCSI-II devices,
- * which claim to support tagged queuing but fail when more than
- * some number of commands are issued at once.
*/
/* Wait for start of REQ/ACK handshake */
- spin_unlock_irq(instance->host_lock);
- err = NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, HZ);
- spin_lock_irq(instance->host_lock);
-
- if(err) {
- printk(KERN_ERR "scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- goto failed;
+ err = NCR5380_poll_politely(hostdata, STATUS_REG, SR_REQ, SR_REQ, HZ);
+ spin_lock_irq(&hostdata->lock);
+ if (err < 0) {
+ shost_printk(KERN_ERR, instance, "select: REQ timeout\n");
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ goto out;
+ }
+ if (!hostdata->selecting) {
+ do_abort(instance, 0);
+ return false;
}
- dprintk(NDEBUG_SELECTION, ("scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->device->id));
- tmp[0] = IDENTIFY(((instance->irq == SCSI_IRQ_NONE) ? 0 : 1), cmd->device->lun);
+ dsprintk(NDEBUG_SELECTION, instance, "target %d selected, going into MESSAGE OUT phase.\n",
+ scmd_id(cmd));
+ tmp[0] = IDENTIFY(can_disconnect, cmd->device->lun);
len = 1;
- cmd->tag = 0;
-
- /* Send message(s) */
data = tmp;
phase = PHASE_MSGOUT;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- dprintk(NDEBUG_SELECTION, ("scsi%d : nexus established.\n", instance->host_no));
- /* XXX need to handle errors here */
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 0);
+ if (len) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ cmd->result = DID_ERROR << 16;
+ complete_cmd(instance, cmd);
+ dsprintk(NDEBUG_SELECTION, instance, "IDENTIFY message transfer failed\n");
+ ret = false;
+ goto out;
+ }
+
+ dsprintk(NDEBUG_SELECTION, instance, "nexus established.\n");
+
hostdata->connected = cmd;
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+ hostdata->busy[cmd->device->id] |= 1 << cmd->device->lun;
- initialize_SCp(cmd);
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
- return 0;
+ initialize_SCp(cmd);
- /* Selection failed */
-failed:
- return -1;
+ ret = false;
+out:
+ if (!hostdata->selecting)
+ return false;
+ hostdata->selecting = NULL;
+ return ret;
}
-/*
- * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
- * unsigned char *phase, int *count, unsigned char **data)
- *
- * Purpose : transfers data in given phase using polled I/O
- *
- * Inputs : instance - instance of driver, *phase - pointer to
- * what phase is expected, *count - pointer to number of
- * bytes to transfer, **data - pointer to data pointer.
- *
- * Returns : -1 when different phase is entered without transferring
- * maximum number of bytes, 0 if all bytes or transferred or exit
- * is in same phase.
- *
- * Also, *phase, *count, *data are modified in place.
- *
- * XXX Note : handling for bus free may be useful.
+/**
+ * NCR5380_transfer_pio() - transfers data in given phase using polled I/O
+ * @instance: instance of driver
+ * @phase: pointer to what phase is expected
+ * @count: pointer to number of bytes to transfer
+ * @data: pointer to data pointer
+ * @can_sleep: 1 or 0 when sleeping is permitted or not, respectively
+ *
+ * Returns: void. *phase, *count, *data are modified in place.
*/
/*
- * Note : this code is not as quick as it could be, however it
+ * Note : this code is not as quick as it could be, however it
* IS 100% reliable, and for the actual data transfer where speed
* counts, we will always do a pseudo DMA or DMA transfer.
*/
-static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data) {
- NCR5380_local_declare();
+static void NCR5380_transfer_pio(struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data, unsigned int can_sleep)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
unsigned char p = *phase, tmp;
int c = *count;
unsigned char *d = *data;
- /*
- * RvC: some administrative data to process polling time
- */
- int break_allowed = 0;
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- NCR5380_setup(instance);
- if (!(p & SR_IO))
- dprintk(NDEBUG_PIO, ("scsi%d : pio write %d bytes\n", instance->host_no, c));
- else
- dprintk(NDEBUG_PIO, ("scsi%d : pio read %d bytes\n", instance->host_no, c));
-
- /*
- * The NCR5380 chip will only drive the SCSI bus when the
+ /*
+ * The NCR5380 chip will only drive the SCSI bus when the
* phase specified in the appropriate bits of the TARGET COMMAND
* REGISTER match the STATUS REGISTER
*/
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
- /* RvC: don't know if this is necessary, but other SCSI I/O is short
- * so breaks are not necessary there
- */
- if ((p == PHASE_DATAIN) || (p == PHASE_DATAOUT)) {
- break_allowed = 1;
- }
do {
- /*
- * Wait for assertion of REQ, after which the phase bits will be
- * valid
+ /*
+ * Wait for assertion of REQ, after which the phase bits will be
+ * valid
*/
- /* RvC: we simply poll once, after that we stop temporarily
- * and let the device buffer fill up
- * if breaking is not allowed, we keep polling as long as needed
- */
-
- /* FIXME */
- while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) && !break_allowed);
- if (!(tmp & SR_REQ)) {
- /* timeout condition */
- NCR5380_set_timer(hostdata, USLEEP_SLEEP);
+ if (NCR5380_poll_politely(hostdata, STATUS_REG, SR_REQ | SR_BSY,
+ SR_REQ | SR_BSY, HZ * can_sleep) < 0)
break;
- }
- dprintk(NDEBUG_HANDSHAKE, ("scsi%d : REQ detected\n", instance->host_no));
+ dsprintk(NDEBUG_HANDSHAKE, instance, "REQ asserted\n");
/* Check for phase mismatch */
- if ((tmp & PHASE_MASK) != p) {
- dprintk(NDEBUG_HANDSHAKE, ("scsi%d : phase mismatch\n", instance->host_no));
- NCR5380_dprint_phase(NDEBUG_HANDSHAKE, instance);
+ if ((NCR5380_read(STATUS_REG) & PHASE_MASK) != p) {
+ dsprintk(NDEBUG_PIO, instance, "phase mismatch\n");
+ NCR5380_dprint_phase(NDEBUG_PIO, instance);
break;
}
+
/* Do actual transfer from SCSI bus to / from memory */
if (!(p & SR_IO))
NCR5380_write(OUTPUT_DATA_REG, *d);
@@ -1643,7 +1287,7 @@ static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase
++d;
- /*
+ /*
* The SCSI standard suggests that in MSGOUT phase, the initiator
* should drop ATN on the last byte of the message phase
* after REQ has been asserted for the handshake but before
@@ -1654,32 +1298,39 @@ static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase
if (!((p & SR_MSG) && c > 1)) {
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
NCR5380_dprint(NDEBUG_PIO, instance);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ACK);
} else {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN);
NCR5380_dprint(NDEBUG_PIO, instance);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
}
} else {
NCR5380_dprint(NDEBUG_PIO, instance);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
}
- /* FIXME - if this fails bus reset ?? */
- NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, 0, 5*HZ);
- dprintk(NDEBUG_HANDSHAKE, ("scsi%d : req false, handshake complete\n", instance->host_no));
+ if (NCR5380_poll_politely(hostdata,
+ STATUS_REG, SR_REQ, 0, 5 * HZ * can_sleep) < 0)
+ break;
-/*
- * We have several special cases to consider during REQ/ACK handshaking :
- * 1. We were in MSGOUT phase, and we are on the last byte of the
- * message. ATN must be dropped as ACK is dropped.
- *
- * 2. We are in a MSGIN phase, and we are on the last byte of the
- * message. We must exit with ACK asserted, so that the calling
- * code may raise ATN before dropping ACK to reject the message.
- *
- * 3. ACK and ATN are clear and the target may proceed as normal.
- */
+ dsprintk(NDEBUG_HANDSHAKE, instance, "REQ negated, handshake complete\n");
+
+ /*
+ * We have several special cases to consider during REQ/ACK
+ * handshaking:
+ *
+ * 1. We were in MSGOUT phase, and we are on the last byte of
+ * the message. ATN must be dropped as ACK is dropped.
+ *
+ * 2. We are in MSGIN phase, and we are on the last byte of the
+ * message. We must exit with ACK asserted, so that the calling
+ * code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3. ACK and ATN are clear & the target may proceed as normal.
+ */
if (!(p == PHASE_MSGIN && c == 1)) {
if (p == PHASE_MSGOUT && c > 1)
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
@@ -1688,426 +1339,342 @@ static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase
}
} while (--c);
- dprintk(NDEBUG_PIO, ("scsi%d : residual %d\n", instance->host_no, c));
+ dsprintk(NDEBUG_PIO, instance, "residual %d\n", c);
*count = c;
*data = d;
tmp = NCR5380_read(STATUS_REG);
- if (tmp & SR_REQ)
+ /* The phase read from the bus is valid if either REQ is (already)
+ * asserted or if ACK hasn't been released yet. The latter applies if
+ * we're in MSG IN, DATA IN or STATUS and all bytes have been received.
+ */
+ if ((tmp & SR_REQ) || ((tmp & SR_IO) && c == 0))
*phase = tmp & PHASE_MASK;
else
*phase = PHASE_UNKNOWN;
-
- if (!c || (*phase == p))
- return 0;
- else
- return -1;
}
/**
- * do_reset - issue a reset command
- * @host: adapter to reset
+ * do_reset - issue a reset command
+ * @instance: adapter to reset
*
- * Issue a reset sequence to the NCR5380 and try and get the bus
- * back into sane shape.
+ * Issue a reset sequence to the NCR5380 and try and get the bus
+ * back into sane shape.
*
- * Locks: caller holds queue lock
+ * This clears the reset interrupt flag because there may be no handler for
+ * it. When the driver is initialized, the NCR5380_intr() handler has not yet
+ * been installed. And when in EH we may have released the ST DMA interrupt.
*/
-
-static void do_reset(struct Scsi_Host *host) {
- NCR5380_local_declare();
- NCR5380_setup(host);
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
+static void do_reset(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata __maybe_unused *hostdata = shost_priv(instance);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ NCR5380_write(TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
- udelay(25);
+ udelay(50);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ local_irq_restore(flags);
}
-/*
- * Function : do_abort (Scsi_Host *host)
- *
- * Purpose : abort the currently established nexus. Should only be
- * called from a routine which can drop into a
- *
- * Returns : 0 on success, -1 on failure.
- *
- * Locks: queue lock held by caller
- * FIXME: sort this out and get new_eh running
+/**
+ * do_abort - abort the currently established nexus by going to
+ * MESSAGE OUT phase and sending an ABORT message.
+ * @instance: relevant scsi host instance
+ * @can_sleep: 1 or 0 when sleeping is permitted or not, respectively
+ *
+ * Returns 0 on success, negative error code on failure.
*/
-static int do_abort(struct Scsi_Host *host) {
- NCR5380_local_declare();
+static int do_abort(struct Scsi_Host *instance, unsigned int can_sleep)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
unsigned char *msgptr, phase, tmp;
int len;
int rc;
- NCR5380_setup(host);
-
/* Request message out phase */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- /*
- * Wait for the target to indicate a valid phase by asserting
- * REQ. Once this happens, we'll have either a MSGOUT phase
- * and can immediately send the ABORT message, or we'll have some
+ /*
+ * Wait for the target to indicate a valid phase by asserting
+ * REQ. Once this happens, we'll have either a MSGOUT phase
+ * and can immediately send the ABORT message, or we'll have some
* other phase and will have to source/sink data.
- *
+ *
* We really don't care what value was on the bus or what value
* the target sees, so we just handshake.
*/
- rc = NCR5380_poll_politely(host, STATUS_REG, SR_REQ, SR_REQ, 60 * HZ);
-
- if(rc < 0)
- return -1;
+ rc = NCR5380_poll_politely(hostdata, STATUS_REG, SR_REQ, SR_REQ,
+ 10 * HZ * can_sleep);
+ if (rc < 0)
+ goto out;
+
+ tmp = NCR5380_read(STATUS_REG) & PHASE_MASK;
- tmp = (unsigned char)rc;
-
NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
- if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
- rc = NCR5380_poll_politely(host, STATUS_REG, SR_REQ, 0, 3*HZ);
+ if (tmp != PHASE_MSGOUT) {
+ NCR5380_write(INITIATOR_COMMAND_REG,
+ ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ rc = NCR5380_poll_politely(hostdata, STATUS_REG, SR_REQ, 0,
+ 3 * HZ * can_sleep);
+ if (rc < 0)
+ goto out;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- if(rc == -1)
- return -1;
}
+
tmp = ABORT;
msgptr = &tmp;
len = 1;
phase = PHASE_MSGOUT;
- NCR5380_transfer_pio(host, &phase, &len, &msgptr);
+ NCR5380_transfer_pio(instance, &phase, &len, &msgptr, can_sleep);
+ if (len)
+ rc = -ENXIO;
/*
* If we got here, and the command completed successfully,
* we're about to go into bus free state.
*/
- return len ? -1 : 0;
+out:
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return rc;
}
-#if defined(REAL_DMA) || defined(PSEUDO_DMA) || defined (REAL_DMA_POLL)
-/*
- * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
- * unsigned char *phase, int *count, unsigned char **data)
+/*
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
*
* Purpose : transfers data in given phase using either real
- * or pseudo DMA.
+ * or pseudo DMA.
*
- * Inputs : instance - instance of driver, *phase - pointer to
- * what phase is expected, *count - pointer to number of
- * bytes to transfer, **data - pointer to data pointer.
- *
- * Returns : -1 when different phase is entered without transferring
- * maximum number of bytes, 0 if all bytes or transferred or exit
- * is in same phase.
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
*
- * Also, *phase, *count, *data are modified in place.
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transferred or exit
+ * is in same phase.
*
- * Locks: io_request lock held by caller
+ * Also, *phase, *count, *data are modified in place.
*/
-static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data) {
- NCR5380_local_declare();
- register int c = *count;
- register unsigned char p = *phase;
- register unsigned char *d = *data;
+static int NCR5380_transfer_dma(struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(hostdata->connected);
+ int c = *count;
+ unsigned char p = *phase;
+ unsigned char *d = *data;
unsigned char tmp;
- int foo;
-#if defined(REAL_DMA_POLL)
- int cnt, toPIO;
- unsigned char saved_data = 0, overrun = 0, residue;
-#endif
-
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
-
- NCR5380_setup(instance);
+ int result = 0;
if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
*phase = tmp;
return -1;
}
-#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
-#ifdef READ_OVERRUNS
+
+ ncmd->phase = p;
+
if (p & SR_IO) {
- c -= 2;
+ if (hostdata->read_overruns)
+ c -= hostdata->read_overruns;
+ else if (hostdata->flags & FLAG_DMA_FIXUP)
+ --c;
}
-#endif
- dprintk(NDEBUG_DMA, ("scsi%d : initializing DMA channel %d for %s, %d bytes %s %0x\n", instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" : "writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d));
- hostdata->dma_len = (p & SR_IO) ? NCR5380_dma_read_setup(instance, d, c) : NCR5380_dma_write_setup(instance, d, c);
-#endif
-
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-#ifdef REAL_DMA
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
-#elif defined(REAL_DMA_POLL)
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
-#else
- /*
- * Note : on my sample board, watch-dog timeouts occurred when interrupts
- * were not disabled for the duration of a single DMA transfer, from
- * before the setting of DMA mode to after transfer of the last byte.
- */
+ dsprintk(NDEBUG_DMA, instance, "initializing DMA %s: length %d, address %p\n",
+ (p & SR_IO) ? "receive" : "send", c, d);
-#if defined(PSEUDO_DMA) && defined(UNSAFE)
- spin_unlock_irq(instance->host_lock);
+#ifdef CONFIG_SUN3
+ /* send start chain */
+ sun3scsi_dma_start(c, *data);
#endif
- /* KLL May need eop and parity in 53c400 */
- if (hostdata->flags & FLAG_NCR53C400)
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE |
- MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR |
- MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
- else
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
-#endif /* def REAL_DMA */
- dprintk(NDEBUG_DMA, ("scsi%d : mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG)));
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_MONITOR_BSY |
+ MR_ENABLE_EOP_INTR);
+
+ if (!(hostdata->flags & FLAG_LATE_DMA_SETUP)) {
+ /* On the Medusa, it is a must to initialize the DMA before
+ * starting the NCR. This is also the cleaner way for the TT.
+ */
+ if (p & SR_IO)
+ result = NCR5380_dma_recv_setup(hostdata, d, c);
+ else
+ result = NCR5380_dma_send_setup(hostdata, d, c);
+ }
- /*
- * On the PAS16 at least I/O recovery delays are not needed here.
- * Everyone else seems to want them.
+ /*
+ * On the PAS16 at least I/O recovery delays are not needed here.
+ * Everyone else seems to want them.
*/
if (p & SR_IO) {
- io_recovery_delay(1);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_io_delay(1);
NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
} else {
- io_recovery_delay(1);
+ NCR5380_io_delay(1);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
- io_recovery_delay(1);
+ NCR5380_io_delay(1);
NCR5380_write(START_DMA_SEND_REG, 0);
- io_recovery_delay(1);
+ NCR5380_io_delay(1);
}
-#if defined(REAL_DMA_POLL)
- do {
- tmp = NCR5380_read(BUS_AND_STATUS_REG);
- } while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR | BASR_END_DMA_TRANSFER)));
-
-/*
- At this point, either we've completed DMA, or we have a phase mismatch,
- or we've unexpectedly lost BUSY (which is a real error).
-
- For write DMAs, we want to wait until the last byte has been
- transferred out over the bus before we turn off DMA mode. Alas, there
- seems to be no terribly good way of doing this on a 5380 under all
- conditions. For non-scatter-gather operations, we can wait until REQ
- and ACK both go false, or until a phase mismatch occurs. Gather-writes
- are nastier, since the device will be expecting more data than we
- are prepared to send it, and REQ will remain asserted. On a 53C8[01] we
- could test LAST BIT SENT to assure transfer (I imagine this is precisely
- why this signal was added to the newer chips) but on the older 538[01]
- this signal does not exist. The workaround for this lack is a watchdog;
- we bail out of the wait-loop after a modest amount of wait-time if
- the usual exit conditions are not met. Not a terribly clean or
- correct solution :-%
-
- Reads are equally tricky due to a nasty characteristic of the NCR5380.
- If the chip is in DMA mode for an READ, it will respond to a target's
- REQ by latching the SCSI data into the INPUT DATA register and asserting
- ACK, even if it has _already_ been notified by the DMA controller that
- the current DMA transfer has completed! If the NCR5380 is then taken
- out of DMA mode, this already-acknowledged byte is lost.
-
- This is not a problem for "one DMA transfer per command" reads, because
- the situation will never arise... either all of the data is DMA'ed
- properly, or the target switches to MESSAGE IN phase to signal a
- disconnection (either operation bringing the DMA to a clean halt).
- However, in order to handle scatter-reads, we must work around the
- problem. The chosen fix is to DMA N-2 bytes, then check for the
- condition before taking the NCR5380 out of DMA mode. One or two extra
- bytes are transferred via PIO as necessary to fill out the original
- request.
- */
-
- if (p & SR_IO) {
-#ifdef READ_OVERRUNS
- udelay(10);
- if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) == (BASR_PHASE_MATCH | BASR_ACK))) {
- saved_data = NCR5380_read(INPUT_DATA_REGISTER);
- overrun = 1;
- }
+#ifdef CONFIG_SUN3
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
#endif
- } else {
- int limit = 100;
- while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) || (NCR5380_read(STATUS_REG) & SR_REQ)) {
- if (!(tmp & BASR_PHASE_MATCH))
- break;
- if (--limit < 0)
- break;
- }
+ sun3_dma_active = 1;
+#endif
+
+ if (hostdata->flags & FLAG_LATE_DMA_SETUP) {
+ /* On the Falcon, the DMA setup must be done after the last
+ * NCR access, else the DMA setup gets trashed!
+ */
+ if (p & SR_IO)
+ result = NCR5380_dma_recv_setup(hostdata, d, c);
+ else
+ result = NCR5380_dma_send_setup(hostdata, d, c);
}
- dprintk(NDEBUG_DMA, ("scsi%d : polled DMA transfer complete, basr 0x%X, sr 0x%X\n", instance->host_no, tmp, NCR5380_read(STATUS_REG)));
+ /* On failure, NCR5380_dma_xxxx_setup() returns a negative int. */
+ if (result < 0)
+ return result;
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- residue = NCR5380_dma_residual(instance);
- c -= residue;
- *count -= c;
- *data += c;
- *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
-
-#ifdef READ_OVERRUNS
- if (*phase == p && (p & SR_IO) && residue == 0) {
- if (overrun) {
- dprintk(NDEBUG_DMA, ("Got an input overrun, using saved byte\n"));
- **data = saved_data;
- *data += 1;
- *count -= 1;
- cnt = toPIO = 1;
- } else {
- printk("No overrun??\n");
- cnt = toPIO = 2;
- }
- dprintk(NDEBUG_DMA, ("Doing %d-byte PIO to 0x%X\n", cnt, *data));
- NCR5380_transfer_pio(instance, phase, &cnt, data);
- *count -= toPIO - cnt;
+ /* For real DMA, result is the byte count. DMA interrupt is expected. */
+ if (result > 0) {
+ hostdata->dma_len = result;
+ return 0;
}
-#endif
- dprintk(NDEBUG_DMA, ("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n", *data, *count, *(*data + *count - 1), *(*data + *count)));
- return 0;
+ /* The result is zero iff pseudo DMA send/receive was completed. */
+ hostdata->dma_len = c;
-#elif defined(REAL_DMA)
- return 0;
-#else /* defined(REAL_DMA_POLL) */
- if (p & SR_IO) {
-#ifdef DMA_WORKS_RIGHT
- foo = NCR5380_pread(instance, d, c);
-#else
- int diff = 1;
- if (hostdata->flags & FLAG_NCR53C400) {
- diff = 0;
- }
- if (!(foo = NCR5380_pread(instance, d, c - diff))) {
- /*
- * We can't disable DMA mode after successfully transferring
- * what we plan to be the last byte, since that would open up
- * a race condition where if the target asserted REQ before
- * we got the DMA mode reset, the NCR5380 would have latched
- * an additional byte into the INPUT DATA register and we'd
- * have dropped it.
- *
- * The workaround was to transfer one fewer bytes than we
- * intended to with the pseudo-DMA read function, wait for
- * the chip to latch the last byte, read it, and then disable
- * pseudo-DMA mode.
- *
- * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
- * REQ is deasserted when ACK is asserted, and not reasserted
- * until ACK goes false. Since the NCR5380 won't lower ACK
- * until DACK is asserted, which won't happen unless we twiddle
- * the DMA port or we take the NCR5380 out of DMA mode, we
- * can guarantee that we won't handshake another extra
- * byte.
- */
+ /*
+ * A note regarding the DMA errata workarounds for early NMOS silicon.
+ *
+ * For DMA sends, we want to wait until the last byte has been
+ * transferred out over the bus before we turn off DMA mode. Alas, there
+ * seems to be no terribly good way of doing this on a 5380 under all
+ * conditions. For non-scatter-gather operations, we can wait until REQ
+ * and ACK both go false, or until a phase mismatch occurs. Gather-sends
+ * are nastier, since the device will be expecting more data than we
+ * are prepared to send it, and REQ will remain asserted. On a 53C8[01]
+ * we could test Last Byte Sent to assure transfer (I imagine this is
+ * precisely why this signal was added to the newer chips) but on the
+ * older 538[01] this signal does not exist. The workaround for this
+ * lack is a watchdog; we bail out of the wait-loop after a modest
+ * amount of wait-time if the usual exit conditions are not met.
+ * Not a terribly clean or correct solution :-%
+ *
+ * DMA receive is equally tricky due to a nasty characteristic of the
+ * NCR5380. If the chip is in DMA receive mode, it will respond to a
+ * target's REQ by latching the SCSI data into the INPUT DATA register
+ * and asserting ACK, even if it has _already_ been notified by the
+ * DMA controller that the current DMA transfer has completed! If the
+ * NCR5380 is then taken out of DMA mode, this already-acknowledged
+ * byte is lost.
+ *
+ * This is not a problem for "one DMA transfer per READ
+ * command", because the situation will never arise... either all of
+ * the data is DMA'ed properly, or the target switches to MESSAGE IN
+ * phase to signal a disconnection (either operation bringing the DMA
+ * to a clean halt). However, in order to handle scatter-receive, we
+ * must work around the problem. The chosen fix is to DMA fewer bytes,
+ * then check for the condition before taking the NCR5380 out of DMA
+ * mode. One or two extra bytes are transferred via PIO as necessary
+ * to fill out the original request.
+ */
- if (!(hostdata->flags & FLAG_NCR53C400)) {
- while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
- /* Wait for clean handshake */
- while (NCR5380_read(STATUS_REG) & SR_REQ);
- d[c - 1] = NCR5380_read(INPUT_DATA_REG);
- }
- }
-#endif
- } else {
-#ifdef DMA_WORKS_RIGHT
- foo = NCR5380_pwrite(instance, d, c);
-#else
- int timeout;
- dprintk(NDEBUG_C400_PWRITE, ("About to pwrite %d bytes\n", c));
- if (!(foo = NCR5380_pwrite(instance, d, c))) {
- /*
- * Wait for the last byte to be sent. If REQ is being asserted for
- * the byte we're interested, we'll ACK it and it will go false.
- */
- if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
- timeout = 20000;
- while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH));
-
- if (!timeout)
- dprintk(NDEBUG_LAST_BYTE_SENT, ("scsi%d : timed out on last byte\n", instance->host_no));
-
- if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
- hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
- if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
- hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
- dprintk(NDEBUG_LAST_WRITE_SENT, ("scsi%d : last bit sent works\n", instance->host_no));
- }
+ if ((hostdata->flags & FLAG_DMA_FIXUP) &&
+ (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
+ /*
+ * The workaround was to transfer fewer bytes than we
+ * intended to with the pseudo-DMA receive function, wait for
+ * the chip to latch the last byte, read it, and then disable
+ * DMA mode.
+ *
+ * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
+ * REQ is deasserted when ACK is asserted, and not reasserted
+ * until ACK goes false. Since the NCR5380 won't lower ACK
+ * until DACK is asserted, which won't happen unless we twiddle
+ * the DMA port or we take the NCR5380 out of DMA mode, we
+ * can guarantee that we won't handshake another extra
+ * byte.
+ *
+ * If sending, wait for the last byte to be sent. If REQ is
+ * being asserted for the byte we're interested, we'll ACK it
+ * and it will go false.
+ */
+ if (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
+ BASR_DRQ, BASR_DRQ, 0)) {
+ if ((p & SR_IO) &&
+ (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
+ if (!NCR5380_poll_politely(hostdata, STATUS_REG,
+ SR_REQ, 0, 0)) {
+ d[c] = NCR5380_read(INPUT_DATA_REG);
+ --ncmd->this_residual;
+ } else {
+ result = -1;
+ scmd_printk(KERN_ERR, hostdata->connected,
+ "PDMA fixup: !REQ timeout\n");
}
- } else {
- dprintk(NDEBUG_C400_PWRITE, ("Waiting for LASTBYTE\n"));
- while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
- dprintk(NDEBUG_C400_PWRITE, ("Got LASTBYTE\n"));
}
+ } else if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH) {
+ result = -1;
+ scmd_printk(KERN_ERR, hostdata->connected,
+ "PDMA fixup: DRQ timeout\n");
}
-#endif
}
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) {
- dprintk(NDEBUG_C400_PWRITE, ("53C400w: Checking for IRQ\n"));
- if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) {
- dprintk(NDEBUG_C400_PWRITE, ("53C400w: got it, reading reset interrupt reg\n"));
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- } else {
- printk("53C400w: IRQ NOT THERE!\n");
- }
- }
- *data = d + c;
- *count = 0;
- *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
-#if defined(PSEUDO_DMA) && defined(UNSAFE)
- spin_lock_irq(instance->host_lock);
-#endif /* defined(REAL_DMA_POLL) */
- return foo;
-#endif /* def REAL_DMA */
+ NCR5380_dma_complete(instance);
+ return result;
}
-#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */
/*
* Function : NCR5380_information_transfer (struct Scsi_Host *instance)
*
- * Purpose : run through the various SCSI phases and do as the target
- * directs us to. Operates on the currently connected command,
- * instance->connected.
+ * Purpose : run through the various SCSI phases and do as the target
+ * directs us to. Operates on the currently connected command,
+ * instance->connected.
*
* Inputs : instance, instance for which we are doing commands
*
- * Side effects : SCSI things happen, the disconnected queue will be
- * modified if a command disconnects, *instance->connected will
- * change.
- *
- * XXX Note : we need to watch for bus free or a reset condition here
- * to recover from an unexpected bus free condition.
- *
- * Locks: io_request_lock held by caller in IRQ mode
+ * Side effects : SCSI things happen, the disconnected queue will be
+ * modified if a command disconnects, *instance->connected will
+ * change.
*/
-static void NCR5380_information_transfer(struct Scsi_Host *instance) {
- NCR5380_local_declare();
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+static void NCR5380_information_transfer(struct Scsi_Host *instance)
+ __releases(&hostdata->lock) __acquires(&hostdata->lock)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
unsigned char msgout = NOP;
int sink = 0;
int len;
-#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
int transfersize;
-#endif
unsigned char *data;
unsigned char phase, tmp, extended_msg[10], old_phase = 0xff;
- Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
- /* RvC: we need to set the end of the polling time */
- unsigned long poll_time = jiffies + USLEEP_POLL;
+ struct scsi_cmnd *cmd;
+
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
- NCR5380_setup(instance);
+ while ((cmd = hostdata->connected)) {
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(cmd);
- while (1) {
tmp = NCR5380_read(STATUS_REG);
/* We only have a valid SCSI phase when REQ is asserted */
if (tmp & SR_REQ) {
@@ -2116,40 +1683,69 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) {
old_phase = phase;
NCR5380_dprint_phase(NDEBUG_INFORMATION, instance);
}
+#ifdef CONFIG_SUN3
+ if (phase == PHASE_CMDOUT &&
+ sun3_dma_setup_done != cmd) {
+ int count;
+
+ advance_sg_buffer(ncmd);
+
+ count = sun3scsi_dma_xfer_len(hostdata, cmd);
+
+ if (count > 0) {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ sun3scsi_dma_send_setup(hostdata,
+ ncmd->ptr, count);
+ else
+ sun3scsi_dma_recv_setup(hostdata,
+ ncmd->ptr, count);
+ sun3_dma_setup_done = cmd;
+ }
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
+ }
+#endif /* CONFIG_SUN3 */
+
if (sink && (phase != PHASE_MSGOUT)) {
NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
- while (NCR5380_read(STATUS_REG) & SR_REQ);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ)
+ ;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
sink = 0;
continue;
}
+
switch (phase) {
- case PHASE_DATAIN:
case PHASE_DATAOUT:
#if (NDEBUG & NDEBUG_NO_DATAOUT)
- printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n", instance->host_no);
+ shost_printk(KERN_DEBUG, instance, "NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n");
sink = 1;
- do_abort(instance);
+ do_abort(instance, 0);
cmd->result = DID_ERROR << 16;
- cmd->scsi_done(cmd);
+ complete_cmd(instance, cmd);
+ hostdata->connected = NULL;
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
return;
#endif
- /*
+ case PHASE_DATAIN:
+ /*
* If there is no room left in the current buffer in the
* scatter-gather list, move onto the next one.
*/
- if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
- ++cmd->SCp.buffer;
- --cmd->SCp.buffers_residual;
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- dprintk(NDEBUG_INFORMATION, ("scsi%d : %d bytes and %d buffers left\n", instance->host_no, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
- }
+ advance_sg_buffer(ncmd);
+ dsprintk(NDEBUG_INFORMATION, instance,
+ "this residual %d, sg ents %d\n",
+ ncmd->this_residual,
+ sg_nents(ncmd->buffer));
+
/*
- * The preferred transfer method is going to be
+ * The preferred transfer method is going to be
* PSEUDO-DMA for systems that are strictly PIO,
* since we can let the hardware do the handshaking.
*
@@ -2158,159 +1754,88 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) {
* in an unconditional loop.
*/
-#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
- /* KLL
- * PSEUDO_DMA is defined here. If this is the g_NCR5380
- * driver then it will always be defined, so the
- * FLAG_NO_PSEUDO_DMA is used to inhibit PDMA in the base
- * NCR5380 case. I think this is a fairly clean solution.
- * We supplement these 2 if's with the flag.
- */
-#ifdef NCR5380_dma_xfer_len
- if (!cmd->device->borken && !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
-#else
- transfersize = cmd->transfersize;
-
-#ifdef LIMIT_TRANSFERSIZE /* If we have problems with interrupt service */
- if (transfersize > 512)
- transfersize = 512;
-#endif /* LIMIT_TRANSFERSIZE */
-
- if (!cmd->device->borken && transfersize && !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && cmd->SCp.this_residual && !(cmd->SCp.this_residual % transfersize)) {
- /* Limit transfers to 32K, for xx400 & xx406
- * pseudoDMA that transfers in 128 bytes blocks. */
- if (transfersize > 32 * 1024)
- transfersize = 32 * 1024;
-#endif
+ transfersize = 0;
+ if (!cmd->device->borken)
+ transfersize = NCR5380_dma_xfer_len(hostdata, cmd);
+
+ if (transfersize > 0) {
len = transfersize;
- if (NCR5380_transfer_dma(instance, &phase, &len, (unsigned char **) &cmd->SCp.ptr)) {
+ if (NCR5380_transfer_dma(instance, &phase,
+ &len, (unsigned char **)&ncmd->ptr)) {
/*
- * If the watchdog timer fires, all future accesses to this
- * device will use the polled-IO.
+ * If the watchdog timer fires, all future
+ * accesses to this device will use the
+ * polled-IO.
*/
scmd_printk(KERN_INFO, cmd,
- "switching to slow handshake\n");
+ "switching to slow handshake\n");
cmd->device->borken = 1;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- sink = 1;
- do_abort(instance);
- cmd->result = DID_ERROR << 16;
- cmd->scsi_done(cmd);
- /* XXX - need to source or sink data here, as appropriate */
- } else
- cmd->SCp.this_residual -= transfersize - len;
- } else
-#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
- NCR5380_transfer_pio(instance, &phase, (int *) &cmd->SCp.this_residual, (unsigned char **)
- &cmd->SCp.ptr);
- break;
+ do_reset(instance);
+ bus_reset_cleanup(instance);
+ }
+ } else {
+ /* Transfer a small chunk so that the
+ * irq mode lock is not held too long.
+ */
+ transfersize = min(ncmd->this_residual,
+ NCR5380_PIO_CHUNK_SIZE);
+ len = transfersize;
+ NCR5380_transfer_pio(instance, &phase, &len,
+ (unsigned char **)&ncmd->ptr,
+ 0);
+ ncmd->this_residual -= transfersize - len;
+ }
+#ifdef CONFIG_SUN3
+ if (sun3_dma_setup_done == cmd)
+ sun3_dma_setup_done = NULL;
+#endif
+ return;
case PHASE_MSGIN:
len = 1;
+ tmp = 0xff;
data = &tmp;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- cmd->SCp.Message = tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 0);
+ if (tmp == 0xff)
+ break;
switch (tmp) {
- /*
- * Linking lets us reduce the time required to get the
- * next command out to the device, hopefully this will
- * mean we don't waste another revolution due to the delays
- * required by ARBITRATION and another SELECTION.
- *
- * In the current implementation proposal, low level drivers
- * merely have to start the next command, pointed to by
- * next_link, done() is called as with unlinked commands.
- */
-#ifdef LINKED
- case LINKED_CMD_COMPLETE:
- case LINKED_FLG_CMD_COMPLETE:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- dprintk(NDEBUG_LINKED, ("scsi%d : target %d lun %d linked command complete.\n", instance->host_no, cmd->device->id, cmd->device->lun));
- /*
- * Sanity check : A linked command should only terminate with
- * one of these messages if there are more linked commands
- * available.
- */
- if (!cmd->next_link) {
- printk("scsi%d : target %d lun %d linked command complete, no next_link\n" instance->host_no, cmd->device->id, cmd->device->lun);
- sink = 1;
- do_abort(instance);
- return;
- }
- initialize_SCp(cmd->next_link);
- /* The next command is still part of this process */
- cmd->next_link->tag = cmd->tag;
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- dprintk(NDEBUG_LINKED, ("scsi%d : target %d lun %d linked request done, calling scsi_done().\n", instance->host_no, cmd->device->id, cmd->device->lun));
- collect_stats(hostdata, cmd);
- cmd->scsi_done(cmd);
- cmd = hostdata->connected;
- break;
-#endif /* def LINKED */
case ABORT:
+ set_host_byte(cmd, DID_ABORT);
+ fallthrough;
case COMMAND_COMPLETE:
/* Accept message by clearing ACK */
sink = 1;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- hostdata->connected = NULL;
- dprintk(NDEBUG_QUEUES, ("scsi%d : command for target %d, lun %d completed\n", instance->host_no, cmd->device->id, cmd->device->lun));
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-
- /*
- * I'm not sure what the correct thing to do here is :
- *
- * If the command that just executed is NOT a request
- * sense, the obvious thing to do is to set the result
- * code to the values of the stored parameters.
- *
- * If it was a REQUEST SENSE command, we need some way
- * to differentiate between the failure code of the original
- * and the failure code of the REQUEST sense - the obvious
- * case is success, where we fall through and leave the result
- * code unchanged.
- *
- * The non-obvious place is where the REQUEST SENSE failed
- */
-
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- else if (status_byte(cmd->SCp.Status) != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ dsprintk(NDEBUG_QUEUES, instance,
+ "COMMAND COMPLETE %p target %d lun %llu\n",
+ cmd, scmd_id(cmd), cmd->device->lun);
-#ifdef AUTOSENSE
- if ((cmd->cmnd[0] == REQUEST_SENSE) &&
- hostdata->ses.cmd_len) {
- scsi_eh_restore_cmnd(cmd, &hostdata->ses);
- hostdata->ses.cmd_len = 0 ;
- }
-
- if ((cmd->cmnd[0] != REQUEST_SENSE) && (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
- scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0);
-
- dprintk(NDEBUG_AUTOSENSE, ("scsi%d : performing request sense\n", instance->host_no));
-
- LIST(cmd, hostdata->issue_queue);
- cmd->host_scribble = (unsigned char *)
- hostdata->issue_queue;
- hostdata->issue_queue = (Scsi_Cmnd *) cmd;
- dprintk(NDEBUG_QUEUES, ("scsi%d : REQUEST SENSE added to head of issue queue\n", instance->host_no));
- } else
-#endif /* def AUTOSENSE */
- {
- collect_stats(hostdata, cmd);
- cmd->scsi_done(cmd);
+ hostdata->connected = NULL;
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
+
+ set_status_byte(cmd, ncmd->status);
+
+ set_resid_from_SCp(cmd);
+
+ if (cmd->cmnd[0] == REQUEST_SENSE)
+ complete_cmd(instance, cmd);
+ else {
+ if (ncmd->status == SAM_STAT_CHECK_CONDITION ||
+ ncmd->status == SAM_STAT_COMMAND_TERMINATED) {
+ dsprintk(NDEBUG_QUEUES, instance, "autosense: adding cmd %p to tail of autosense queue\n",
+ cmd);
+ list_add_tail(&ncmd->list,
+ &hostdata->autosense);
+ } else
+ complete_cmd(instance, cmd);
}
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /*
- * Restore phase bits to 0 so an interrupted selection,
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
* arbitration can resume.
*/
NCR5380_write(TARGET_COMMAND_REG, 0);
- while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
- barrier();
return;
case MESSAGE_REJECT:
/* Accept message by clearing ACK */
@@ -2320,43 +1845,39 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) {
case ORDERED_QUEUE_TAG:
case SIMPLE_QUEUE_TAG:
cmd->device->simple_tags = 0;
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+ hostdata->busy[cmd->device->id] |= (1 << (cmd->device->lun & 0xFF));
break;
default:
break;
}
- case DISCONNECT:{
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- cmd->device->disconnect = 1;
- LIST(cmd, hostdata->disconnected_queue);
- cmd->host_scribble = (unsigned char *)
- hostdata->disconnected_queue;
- hostdata->connected = NULL;
- hostdata->disconnected_queue = cmd;
- dprintk(NDEBUG_QUEUES, ("scsi%d : command for target %d lun %d was moved from connected to" " the disconnected_queue\n", instance->host_no, cmd->device->id, cmd->device->lun));
- /*
- * Restore phase bits to 0 so an interrupted selection,
- * arbitration can resume.
- */
- NCR5380_write(TARGET_COMMAND_REG, 0);
-
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /* Wait for bus free to avoid nasty timeouts - FIXME timeout !*/
- /* NCR538_poll_politely(instance, STATUS_REG, SR_BSY, 0, 30 * HZ); */
- while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
- barrier();
- return;
- }
- /*
+ break;
+ case DISCONNECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ hostdata->connected = NULL;
+ list_add(&ncmd->list, &hostdata->disconnected);
+ dsprintk(NDEBUG_INFORMATION | NDEBUG_QUEUES,
+ instance, "connected command %p for target %d lun %llu moved to disconnected queue\n",
+ cmd, scmd_id(cmd), cmd->device->lun);
+
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
+ return;
+ /*
* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
- * operation, in violation of the SCSI spec so we can safely
+ * operation, in violation of the SCSI spec so we can safely
* ignore SAVE/RESTORE pointers calls.
*
- * Unfortunately, some disks violate the SCSI spec and
+ * Unfortunately, some disks violate the SCSI spec and
* don't issue the required SAVE_POINTERS message before
- * disconnecting, and we have to break spec to remain
+ * disconnecting, and we have to break spec to remain
* compatible.
*/
case SAVE_POINTERS:
@@ -2365,89 +1886,87 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) {
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
break;
case EXTENDED_MESSAGE:
-/*
- * Extended messages are sent in the following format :
- * Byte
- * 0 EXTENDED_MESSAGE == 1
- * 1 length (includes one byte for code, doesn't
- * include first two bytes)
- * 2 code
- * 3..length+1 arguments
- *
- * Start the extended message buffer with the EXTENDED_MESSAGE
- * byte, since spi_print_msg() wants the whole thing.
- */
+ /*
+ * Start the message buffer with the EXTENDED_MESSAGE
+ * byte, since spi_print_msg() wants the whole thing.
+ */
extended_msg[0] = EXTENDED_MESSAGE;
/* Accept first byte by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- dprintk(NDEBUG_EXTENDED, ("scsi%d : receiving extended message\n", instance->host_no));
+
+ spin_unlock_irq(&hostdata->lock);
+
+ dsprintk(NDEBUG_EXTENDED, instance, "receiving extended message\n");
len = 2;
data = extended_msg + 1;
phase = PHASE_MSGIN;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
-
- dprintk(NDEBUG_EXTENDED, ("scsi%d : length=%d, code=0x%02x\n", instance->host_no, (int) extended_msg[1], (int) extended_msg[2]));
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 1);
+ dsprintk(NDEBUG_EXTENDED, instance, "length %d, code 0x%02x\n",
+ (int)extended_msg[1],
+ (int)extended_msg[2]);
- if (!len && extended_msg[1] <= (sizeof(extended_msg) - 1)) {
+ if (!len && extended_msg[1] > 0 &&
+ extended_msg[1] <= sizeof(extended_msg) - 2) {
/* Accept third byte by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
len = extended_msg[1] - 1;
data = extended_msg + 3;
phase = PHASE_MSGIN;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- dprintk(NDEBUG_EXTENDED, ("scsi%d : message received, residual %d\n", instance->host_no, len));
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 1);
+ dsprintk(NDEBUG_EXTENDED, instance, "message received, residual %d\n",
+ len);
switch (extended_msg[2]) {
case EXTENDED_SDTR:
case EXTENDED_WDTR:
- case EXTENDED_MODIFY_DATA_POINTER:
- case EXTENDED_EXTENDED_IDENTIFY:
tmp = 0;
}
} else if (len) {
- printk("scsi%d: error receiving extended message\n", instance->host_no);
+ shost_printk(KERN_ERR, instance, "error receiving extended message\n");
tmp = 0;
} else {
- printk("scsi%d: extended message code %02x length %d is too long\n", instance->host_no, extended_msg[2], extended_msg[1]);
+ shost_printk(KERN_NOTICE, instance, "extended message code %02x length %d is too long\n",
+ extended_msg[2], extended_msg[1]);
tmp = 0;
}
- /* Fall through to reject message */
- /*
- * If we get something weird that we aren't expecting,
- * reject it.
- */
+ spin_lock_irq(&hostdata->lock);
+ if (!hostdata->connected)
+ return;
+
+ /* Reject message */
+ fallthrough;
default:
- if (!tmp) {
- printk("scsi%d: rejecting message ", instance->host_no);
- spi_print_msg(extended_msg);
- printk("\n");
- } else if (tmp != EXTENDED_MESSAGE)
+ /*
+ * If we get something weird that we aren't expecting,
+ * log it.
+ */
+ if (tmp == EXTENDED_MESSAGE)
scmd_printk(KERN_INFO, cmd,
- "rejecting unknown message %02x\n",tmp);
- else
+ "rejecting unknown extended message code %02x, length %d\n",
+ extended_msg[2], extended_msg[1]);
+ else if (tmp)
scmd_printk(KERN_INFO, cmd,
- "rejecting unknown extended message code %02x, length %d\n", extended_msg[1], extended_msg[0]);
+ "rejecting unknown message code %02x\n",
+ tmp);
msgout = MESSAGE_REJECT;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
break;
- } /* switch (tmp) */
+ } /* switch (tmp) */
break;
case PHASE_MSGOUT:
len = 1;
data = &msgout;
hostdata->last_message = msgout;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 0);
if (msgout == ABORT) {
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
hostdata->connected = NULL;
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
cmd->result = DID_ERROR << 16;
- collect_stats(hostdata, cmd);
- cmd->scsi_done(cmd);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ complete_cmd(instance, cmd);
return;
}
msgout = NOP;
@@ -2455,65 +1974,61 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) {
case PHASE_CMDOUT:
len = cmd->cmd_len;
data = cmd->cmnd;
- /*
- * XXX for performance reasons, on machines with a
- * PSEUDO-DMA architecture we should probably
- * use the dma transfer function.
+ /*
+ * XXX for performance reasons, on machines with a
+ * PSEUDO-DMA architecture we should probably
+ * use the dma transfer function.
*/
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- if (!cmd->device->disconnect && should_disconnect(cmd->cmnd[0])) {
- NCR5380_set_timer(hostdata, USLEEP_SLEEP);
- dprintk(NDEBUG_USLEEP, ("scsi%d : issued command, sleeping until %ul\n", instance->host_no, hostdata->time_expires));
- return;
- }
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 0);
break;
case PHASE_STATIN:
len = 1;
+ tmp = ncmd->status;
data = &tmp;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- cmd->SCp.Status = tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 0);
+ ncmd->status = tmp;
break;
default:
- printk("scsi%d : unknown phase\n", instance->host_no);
- NCR5380_dprint(NDEBUG_ALL, instance);
- } /* switch(phase) */
- } /* if (tmp * SR_REQ) */
- else {
- /* RvC: go to sleep if polling time expired
- */
- if (!cmd->device->disconnect && time_after_eq(jiffies, poll_time)) {
- NCR5380_set_timer(hostdata, USLEEP_SLEEP);
- dprintk(NDEBUG_USLEEP, ("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no, hostdata->time_expires));
- return;
+ shost_printk(KERN_ERR, instance, "unknown phase\n");
+ NCR5380_dprint(NDEBUG_ANY, instance);
+ } /* switch(phase) */
+ } else {
+ int err;
+
+ spin_unlock_irq(&hostdata->lock);
+ err = NCR5380_poll_politely(hostdata, STATUS_REG,
+ SR_REQ, SR_REQ, HZ);
+ spin_lock_irq(&hostdata->lock);
+
+ if (err < 0 && hostdata->connected &&
+ !(NCR5380_read(STATUS_REG) & SR_BSY)) {
+ scmd_printk(KERN_ERR, hostdata->connected,
+ "BSY signal lost\n");
+ do_reset(instance);
+ bus_reset_cleanup(instance);
}
}
- } /* while (1) */
+ }
}
/*
* Function : void NCR5380_reselect (struct Scsi_Host *instance)
*
- * Purpose : does reselection, initializing the instance->connected
- * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
- * nexus has been reestablished,
- *
- * Inputs : instance - this instance of the NCR5380.
+ * Purpose : does reselection, initializing the instance->connected
+ * field to point to the scsi_cmnd for which the I_T_L or I_T_L_Q
+ * nexus has been reestablished,
*
- * Locks: io_request_lock held by caller if IRQ driven
+ * Inputs : instance - this instance of the NCR5380.
*/
-static void NCR5380_reselect(struct Scsi_Host *instance) {
- NCR5380_local_declare();
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
- instance->hostdata;
+static void NCR5380_reselect(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
unsigned char target_mask;
- unsigned char lun, phase;
- int len;
+ unsigned char lun;
unsigned char msg[3];
- unsigned char *data;
- Scsi_Cmnd *tmp = NULL, *prev;
- int abort = 0;
- NCR5380_setup(instance);
+ struct NCR5380_cmd *ncmd;
+ struct scsi_cmnd *tmp;
/*
* Disable arbitration, etc. since the host adapter obviously
@@ -2521,12 +2036,15 @@ static void NCR5380_reselect(struct Scsi_Host *instance) {
*/
NCR5380_write(MODE_REG, MR_BASE);
- hostdata->restart_select = 1;
target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
- dprintk(NDEBUG_SELECTION, ("scsi%d : reselect\n", instance->host_no));
+ if (!target_mask || target_mask & (target_mask - 1)) {
+ shost_printk(KERN_WARNING, instance,
+ "reselect: bad target_mask 0x%02x\n", target_mask);
+ return;
+ }
- /*
+ /*
* At this point, we have detected that our SCSI ID is on the bus,
* SEL is true and BSY was false for at least one bus settle delay
* (400 ns).
@@ -2536,311 +2054,357 @@ static void NCR5380_reselect(struct Scsi_Host *instance) {
*/
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
-
- /* FIXME: timeout too long, must fail to workqueue */
- if(NCR5380_poll_politely(instance, STATUS_REG, SR_SEL, 0, 2*HZ)<0)
- abort = 1;
-
+ if (NCR5380_poll_politely(hostdata,
+ STATUS_REG, SR_SEL, 0, 0) < 0) {
+ shost_printk(KERN_ERR, instance, "reselect: !SEL timeout\n");
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return;
+ }
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
/*
* Wait for target to go into MSGIN.
- * FIXME: timeout needed and fail to work queeu
*/
- if(NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, 2*HZ))
- abort = 1;
+ if (NCR5380_poll_politely(hostdata,
+ STATUS_REG, SR_REQ, SR_REQ, 0) < 0) {
+ if ((NCR5380_read(STATUS_REG) & (SR_BSY | SR_SEL)) == 0)
+ /* BUS FREE phase */
+ return;
+ shost_printk(KERN_ERR, instance, "reselect: REQ timeout\n");
+ do_abort(instance, 0);
+ return;
+ }
- len = 1;
- data = msg;
- phase = PHASE_MSGIN;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
+#ifdef CONFIG_SUN3
+ /* acknowledge toggle to MSGIN */
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN));
+
+ /* peek at the byte without really hitting the bus */
+ msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG);
+#else
+ {
+ int len = 1;
+ unsigned char *data = msg;
+ unsigned char phase = PHASE_MSGIN;
+
+ NCR5380_transfer_pio(instance, &phase, &len, &data, 0);
+
+ if (len) {
+ do_abort(instance, 0);
+ return;
+ }
+ }
+#endif /* CONFIG_SUN3 */
if (!(msg[0] & 0x80)) {
- printk(KERN_ERR "scsi%d : expecting IDENTIFY message, got ", instance->host_no);
+ shost_printk(KERN_ERR, instance, "expecting IDENTIFY message, got ");
spi_print_msg(msg);
- abort = 1;
- } else {
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- lun = (msg[0] & 0x07);
+ printk("\n");
+ do_abort(instance, 0);
+ return;
+ }
+ lun = msg[0] & 0x07;
- /*
- * We need to add code for SCSI-II to track which devices have
- * I_T_L_Q nexuses established, and which have simple I_T_L
- * nexuses so we can chose to do additional data transfer.
- */
+ /*
+ * We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer.
+ */
- /*
- * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
- * just reestablished, and remove it from the disconnected queue.
- */
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue.
+ */
+ tmp = NULL;
+ list_for_each_entry(ncmd, &hostdata->disconnected, list) {
+ struct scsi_cmnd *cmd = NCR5380_to_scmd(ncmd);
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
- if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun)
- ) {
- if (prev) {
- REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble);
- prev->host_scribble = tmp->host_scribble;
- } else {
- REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
- hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
- }
- tmp->host_scribble = NULL;
- break;
- }
- if (!tmp) {
- printk(KERN_ERR "scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n", instance->host_no, target_mask, lun);
- /*
- * Since we have an established nexus that we can't do anything with,
- * we must abort it.
- */
- abort = 1;
+ if (target_mask == (1 << scmd_id(cmd)) &&
+ lun == (u8)cmd->device->lun) {
+ list_del(&ncmd->list);
+ tmp = cmd;
+ break;
}
}
- if (abort) {
- do_abort(instance);
+ if (tmp) {
+ dsprintk(NDEBUG_RESELECTION | NDEBUG_QUEUES, instance,
+ "reselect: removed %p from disconnected queue\n", tmp);
} else {
- hostdata->connected = tmp;
- dprintk(NDEBUG_RESELECTION, ("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n", instance->host_no, tmp->target, tmp->lun, tmp->tag));
+ int target = ffs(target_mask) - 1;
+
+ shost_printk(KERN_ERR, instance, "target bitmask 0x%02x lun %d not in disconnected queue.\n",
+ target_mask, lun);
+ /*
+ * Since we have an established nexus that we can't do anything
+ * with, we must abort it.
+ */
+ if (do_abort(instance, 0) == 0)
+ hostdata->busy[target] &= ~(1 << lun);
+ return;
}
-}
-/*
- * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
- *
- * Purpose : called by interrupt handler when DMA finishes or a phase
- * mismatch occurs (which would finish the DMA transfer).
- *
- * Inputs : instance - this instance of the NCR5380.
- *
- * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L
- * nexus has been reestablished, on failure NULL is returned.
- */
+#ifdef CONFIG_SUN3
+ if (sun3_dma_setup_done != tmp) {
+ int count;
-#ifdef REAL_DMA
-static void NCR5380_dma_complete(NCR5380_instance * instance) {
- NCR5380_local_declare();
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- int transferred;
- NCR5380_setup(instance);
+ advance_sg_buffer(ncmd);
- /*
- * XXX this might not be right.
- *
- * Wait for final byte to transfer, ie wait for ACK to go false.
- *
- * We should use the Last Byte Sent bit, unfortunately this is
- * not available on the 5380/5381 (only the various CMOS chips)
- *
- * FIXME: timeout, and need to handle long timeout/irq case
- */
+ count = sun3scsi_dma_xfer_len(hostdata, tmp);
- NCR5380_poll_politely(instance, BUS_AND_STATUS_REG, BASR_ACK, 0, 5*HZ);
+ if (count > 0) {
+ if (tmp->sc_data_direction == DMA_TO_DEVICE)
+ sun3scsi_dma_send_setup(hostdata,
+ ncmd->ptr, count);
+ else
+ sun3scsi_dma_recv_setup(hostdata,
+ ncmd->ptr, count);
+ sun3_dma_setup_done = tmp;
+ }
+ }
- NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+#endif /* CONFIG_SUN3 */
+
+ /* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- /*
- * The only places we should see a phase mismatch and have to send
- * data from the same set of pointers will be the data transfer
- * phases. So, residual, requested length are only important here.
- */
+ hostdata->connected = tmp;
+ dsprintk(NDEBUG_RESELECTION, instance, "nexus established, target %d, lun %llu\n",
+ scmd_id(tmp), tmp->device->lun);
+}
+
+/**
+ * list_find_cmd - test for presence of a command in a linked list
+ * @haystack: list of commands
+ * @needle: command to search for
+ */
+
+static bool list_find_cmd(struct list_head *haystack,
+ struct scsi_cmnd *needle)
+{
+ struct NCR5380_cmd *ncmd;
+
+ list_for_each_entry(ncmd, haystack, list)
+ if (NCR5380_to_scmd(ncmd) == needle)
+ return true;
+ return false;
+}
+
+/**
+ * list_remove_cmd - remove a command from linked list
+ * @haystack: list of commands
+ * @needle: command to remove
+ */
+
+static bool list_del_cmd(struct list_head *haystack,
+ struct scsi_cmnd *needle)
+{
+ if (list_find_cmd(haystack, needle)) {
+ struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(needle);
- if (!(hostdata->connected->SCp.phase & SR_CD)) {
- transferred = instance->dmalen - NCR5380_dma_residual();
- hostdata->connected->SCp.this_residual -= transferred;
- hostdata->connected->SCp.ptr += transferred;
+ list_del(&ncmd->list);
+ return true;
}
+ return false;
}
-#endif /* def REAL_DMA */
-/*
- * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+/**
+ * NCR5380_abort - scsi host eh_abort_handler() method
+ * @cmd: the command to be aborted
+ *
+ * Try to abort a given command by removing it from queues and/or sending
+ * the target an abort message. This may not succeed in causing a target
+ * to abort the command. Nonetheless, the low-level driver must forget about
+ * the command because the mid-layer reclaims it and it may be re-issued.
*
- * Purpose : abort a command
+ * The normal path taken by a command is as follows. For EH we trace this
+ * same path to locate and abort the command.
*
- * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
- * host byte of the result field to, if zero DID_ABORTED is
- * used.
+ * unissued -> selecting -> [unissued -> selecting ->]... connected ->
+ * [disconnected -> connected ->]...
+ * [autosense -> connected ->] done
*
- * Returns : 0 - success, -1 on failure.
+ * If cmd was not found at all then presumably it has already been completed,
+ * in which case return SUCCESS to try to avoid further EH measures.
*
- * XXX - there is no way to abort the command that is currently
- * connected, you have to wait for it to complete. If this is
- * a problem, we could implement longjmp() / setjmp(), setjmp()
- * called where the loop started in NCR5380_main().
+ * If the command has not completed yet, we must not fail to find it.
+ * We have no option but to forget the aborted command (even if it still
+ * lacks sense data). The mid-layer may re-issue a command that is in error
+ * recovery (see scsi_send_eh_cmnd), but the logic and data structures in
+ * this driver are such that a command can appear on one queue only.
*
- * Locks: host lock taken by caller
+ * The lock protects driver data structures, but EH handlers also use it
+ * to serialize their own execution and prevent their own re-entry.
*/
-static int NCR5380_abort(Scsi_Cmnd * cmd) {
- NCR5380_local_declare();
+static int NCR5380_abort(struct scsi_cmnd *cmd)
+{
struct Scsi_Host *instance = cmd->device->host;
- struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- Scsi_Cmnd *tmp, **prev;
-
- printk(KERN_WARNING "scsi%d : aborting command\n", instance->host_no);
- scsi_print_command(cmd);
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned long flags;
+ int result = SUCCESS;
- NCR5380_print_status(instance);
+ spin_lock_irqsave(&hostdata->lock, flags);
- NCR5380_setup(instance);
+#if (NDEBUG & NDEBUG_ANY)
+ scmd_printk(KERN_INFO, cmd, __func__);
+#endif
+ NCR5380_dprint(NDEBUG_ANY, instance);
+ NCR5380_dprint_phase(NDEBUG_ANY, instance);
- dprintk(NDEBUG_ABORT, ("scsi%d : abort called\n", instance->host_no));
- dprintk(NDEBUG_ABORT, (" basr 0x%X, sr 0x%X\n", NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG)));
+ if (list_del_cmd(&hostdata->unissued, cmd)) {
+ dsprintk(NDEBUG_ABORT, instance,
+ "abort: removed %p from issue queue\n", cmd);
+ cmd->result = DID_ABORT << 16;
+ scsi_done(cmd); /* No tag or busy flag to worry about */
+ goto out;
+ }
-#if 0
-/*
- * Case 1 : If the command is the currently executing command,
- * we'll set the aborted flag and return control so that
- * information transfer routine can exit cleanly.
- */
+ if (hostdata->selecting == cmd) {
+ dsprintk(NDEBUG_ABORT, instance,
+ "abort: cmd %p == selecting\n", cmd);
+ hostdata->selecting = NULL;
+ cmd->result = DID_ABORT << 16;
+ complete_cmd(instance, cmd);
+ goto out;
+ }
- if (hostdata->connected == cmd) {
- dprintk(NDEBUG_ABORT, ("scsi%d : aborting connected command\n", instance->host_no));
- hostdata->aborted = 1;
-/*
- * We should perform BSY checking, and make sure we haven't slipped
- * into BUS FREE.
- */
+ if (list_del_cmd(&hostdata->disconnected, cmd)) {
+ dsprintk(NDEBUG_ABORT, instance,
+ "abort: removed %p from disconnected list\n", cmd);
+ /* Can't call NCR5380_select() and send ABORT because that
+ * means releasing the lock. Need a bus reset.
+ */
+ set_host_byte(cmd, DID_ERROR);
+ complete_cmd(instance, cmd);
+ result = FAILED;
+ goto out;
+ }
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN);
-/*
- * Since we can't change phases until we've completed the current
- * handshake, we have to source or sink a byte of data if the current
- * phase is not MSGOUT.
- */
+ if (hostdata->connected == cmd) {
+ dsprintk(NDEBUG_ABORT, instance, "abort: cmd %p is connected\n", cmd);
+ hostdata->connected = NULL;
+ hostdata->dma_len = 0;
+ if (do_abort(instance, 0) < 0) {
+ set_host_byte(cmd, DID_ERROR);
+ complete_cmd(instance, cmd);
+ result = FAILED;
+ goto out;
+ }
+ set_host_byte(cmd, DID_ABORT);
+ complete_cmd(instance, cmd);
+ goto out;
+ }
-/*
- * Return control to the executing NCR drive so we can clear the
- * aborted flag and get back into our main loop.
- */
+ if (list_del_cmd(&hostdata->autosense, cmd)) {
+ dsprintk(NDEBUG_ABORT, instance,
+ "abort: removed %p from sense queue\n", cmd);
+ complete_cmd(instance, cmd);
+ }
- return 0;
+out:
+ if (result == FAILED)
+ dsprintk(NDEBUG_ABORT, instance, "abort: failed to abort %p\n", cmd);
+ else {
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
+ dsprintk(NDEBUG_ABORT, instance, "abort: successfully aborted %p\n", cmd);
}
-#endif
-/*
- * Case 2 : If the command hasn't been issued yet, we simply remove it
- * from the issue queue.
- */
-
- dprintk(NDEBUG_ABORT, ("scsi%d : abort going into loop.\n", instance->host_no));
- for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble)
- if (cmd == tmp) {
- REMOVE(5, *prev, tmp, tmp->host_scribble);
- (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
- tmp->host_scribble = NULL;
- tmp->result = DID_ABORT << 16;
- dprintk(NDEBUG_ABORT, ("scsi%d : abort removed command from issue queue.\n", instance->host_no));
- tmp->scsi_done(tmp);
- return SUCCESS;
- }
-#if (NDEBUG & NDEBUG_ABORT)
- /* KLL */
- else if (prev == tmp)
- printk(KERN_ERR "scsi%d : LOOP\n", instance->host_no);
-#endif
+ queue_work(hostdata->work_q, &hostdata->main_task);
+ spin_unlock_irqrestore(&hostdata->lock, flags);
-/*
- * Case 3 : If any commands are connected, we're going to fail the abort
- * and let the high level SCSI driver retry at a later time or
- * issue a reset.
- *
- * Timeouts, and therefore aborted commands, will be highly unlikely
- * and handling them cleanly in this situation would make the common
- * case of noresets less efficient, and would pollute our code. So,
- * we fail.
- */
+ return result;
+}
- if (hostdata->connected) {
- dprintk(NDEBUG_ABORT, ("scsi%d : abort failed, command connected.\n", instance->host_no));
- return FAILED;
+
+static void bus_reset_cleanup(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ int i;
+ struct NCR5380_cmd *ncmd;
+
+ /* reset NCR registers */
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /* After the reset, there are no more connected or disconnected commands
+ * and no busy units; so clear the low-level status here to avoid
+ * conflicts when the mid-level code tries to wake up the affected
+ * commands!
+ */
+
+ if (hostdata->selecting) {
+ hostdata->selecting->result = DID_RESET << 16;
+ complete_cmd(instance, hostdata->selecting);
+ hostdata->selecting = NULL;
}
-/*
- * Case 4: If the command is currently disconnected from the bus, and
- * there are no connected commands, we reconnect the I_T_L or
- * I_T_L_Q nexus associated with it, go into message out, and send
- * an abort message.
- *
- * This case is especially ugly. In order to reestablish the nexus, we
- * need to call NCR5380_select(). The easiest way to implement this
- * function was to abort if the bus was busy, and let the interrupt
- * handler triggered on the SEL for reselect take care of lost arbitrations
- * where necessary, meaning interrupts need to be enabled.
- *
- * When interrupts are enabled, the queues may change - so we
- * can't remove it from the disconnected queue before selecting it
- * because that could cause a failure in hashing the nexus if that
- * device reselected.
- *
- * Since the queues may change, we can't use the pointers from when we
- * first locate it.
- *
- * So, we must first locate the command, and if NCR5380_select()
- * succeeds, then issue the abort, relocate the command and remove
- * it from the disconnected queue.
- */
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble)
- if (cmd == tmp) {
- dprintk(NDEBUG_ABORT, ("scsi%d : aborting disconnected command.\n", instance->host_no));
+ list_for_each_entry(ncmd, &hostdata->disconnected, list) {
+ struct scsi_cmnd *cmd = NCR5380_to_scmd(ncmd);
+
+ set_host_byte(cmd, DID_RESET);
+ complete_cmd(instance, cmd);
+ }
+ INIT_LIST_HEAD(&hostdata->disconnected);
- if (NCR5380_select(instance, cmd, (int) cmd->tag))
- return FAILED;
- dprintk(NDEBUG_ABORT, ("scsi%d : nexus reestablished.\n", instance->host_no));
+ list_for_each_entry(ncmd, &hostdata->autosense, list) {
+ struct scsi_cmnd *cmd = NCR5380_to_scmd(ncmd);
- do_abort(instance);
+ scsi_done(cmd);
+ }
+ INIT_LIST_HEAD(&hostdata->autosense);
- for (prev = (Scsi_Cmnd **) & (hostdata->disconnected_queue), tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble)
- if (cmd == tmp) {
- REMOVE(5, *prev, tmp, tmp->host_scribble);
- *prev = (Scsi_Cmnd *) tmp->host_scribble;
- tmp->host_scribble = NULL;
- tmp->result = DID_ABORT << 16;
- tmp->scsi_done(tmp);
- return SUCCESS;
- }
- }
-/*
- * Case 5 : If we reached this point, the command was not found in any of
- * the queues.
- *
- * We probably reached this point because of an unlikely race condition
- * between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case something really
- * broke.
- */
- printk(KERN_WARNING "scsi%d : warning : SCSI command probably completed successfully\n"
- " before abortion\n", instance->host_no);
- return FAILED;
-}
+ if (hostdata->connected) {
+ set_host_byte(hostdata->connected, DID_RESET);
+ complete_cmd(instance, hostdata->connected);
+ hostdata->connected = NULL;
+ }
+ for (i = 0; i < 8; ++i)
+ hostdata->busy[i] = 0;
+ hostdata->dma_len = 0;
-/*
- * Function : int NCR5380_bus_reset (Scsi_Cmnd *cmd)
- *
- * Purpose : reset the SCSI bus.
- *
- * Returns : SUCCESS
+ queue_work(hostdata->work_q, &hostdata->main_task);
+}
+
+/**
+ * NCR5380_host_reset - reset the SCSI host
+ * @cmd: SCSI command undergoing EH
*
- * Locks: host lock taken by caller
+ * Returns SUCCESS
*/
-static int NCR5380_bus_reset(Scsi_Cmnd * cmd)
+static int NCR5380_host_reset(struct scsi_cmnd *cmd)
{
struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned long flags;
+ struct NCR5380_cmd *ncmd;
+
+ spin_lock_irqsave(&hostdata->lock, flags);
+
+#if (NDEBUG & NDEBUG_ANY)
+ shost_printk(KERN_INFO, instance, __func__);
+#endif
+ NCR5380_dprint(NDEBUG_ANY, instance);
+ NCR5380_dprint_phase(NDEBUG_ANY, instance);
- NCR5380_local_declare();
- NCR5380_setup(instance);
- NCR5380_print_status(instance);
+ list_for_each_entry(ncmd, &hostdata->unissued, list) {
+ struct scsi_cmnd *scmd = NCR5380_to_scmd(ncmd);
+
+ scmd->result = DID_RESET << 16;
+ scsi_done(scmd);
+ }
+ INIT_LIST_HEAD(&hostdata->unissued);
- spin_lock_irq(instance->host_lock);
do_reset(instance);
- spin_unlock_irq(instance->host_lock);
+ bus_reset_cleanup(instance);
+
+ spin_unlock_irqrestore(&hostdata->lock, flags);
return SUCCESS;
}
diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h
index fd40a32b1f6f..d402d4bffcb2 100644
--- a/drivers/scsi/NCR5380.h
+++ b/drivers/scsi/NCR5380.h
@@ -1,13 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* NCR 5380 defines
*
* Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 666-5836
- *
- * DISTRIBUTION RELEASE 7
+ * Visionary Computing
+ * (Unix consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
*
* For more information, please consult
*
@@ -21,21 +20,16 @@
* 1+ (800) 334-5454
*/
-/*
- * $Log: NCR5380.h,v $
- */
-
#ifndef NCR5380_H
#define NCR5380_H
+#include <linux/delay.h>
#include <linux/interrupt.h>
-
-#ifdef AUTOSENSE
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
-#endif
-
-#define NCR5380_PUBLIC_RELEASE 7
-#define NCR53C400_PUBLIC_RELEASE 2
+#include <scsi/scsi_transport_spi.h>
#define NDEBUG_ARBITRATION 0x1
#define NDEBUG_AUTOSENSE 0x2
@@ -60,6 +54,9 @@
#define NDEBUG_C400_PREAD 0x100000
#define NDEBUG_C400_PWRITE 0x200000
#define NDEBUG_LISTS 0x400000
+#define NDEBUG_ABORT 0x800000
+#define NDEBUG_TAGS 0x1000000
+#define NDEBUG_MERGING 0x2000000
#define NDEBUG_ANY 0xFFFFFFFFUL
@@ -81,15 +78,11 @@
#define ICR_DIFF_ENABLE 0x20 /* wo Set to enable diff. drivers */
#define ICR_ASSERT_ACK 0x10 /* rw ini Set to assert ACK */
#define ICR_ASSERT_BSY 0x08 /* rw Set to assert BSY */
-#define ICR_ASSERT_SEL 0x04 /* rw Set to assert SEL */
+#define ICR_ASSERT_SEL 0x04 /* rw Set to assert SEL */
#define ICR_ASSERT_ATN 0x02 /* rw Set to assert ATN */
#define ICR_ASSERT_DATA 0x01 /* rw SCSI_DATA_REG is asserted */
-#ifdef DIFFERENTIAL
-#define ICR_BASE ICR_DIFF_ENABLE
-#else
#define ICR_BASE 0
-#endif
#define MODE_REG 2
/*
@@ -106,11 +99,7 @@
#define MR_DMA_MODE 0x02 /* rw DMA / pseudo DMA mode */
#define MR_ARBITRATE 0x01 /* rw start arbitration */
-#ifdef PARITY
-#define MR_BASE MR_ENABLE_PAR_CHECK
-#else
#define MR_BASE 0
-#endif
#define TARGET_COMMAND_REG 3
#define TCR_LAST_BYTE_SENT 0x80 /* ro DMA done */
@@ -146,7 +135,7 @@
#define BASR_IRQ 0x10 /* ro mirror of IRQ pin */
#define BASR_PHASE_MATCH 0x08 /* ro Set when MSG CD IO match TCR */
#define BASR_BUSY_ERROR 0x04 /* ro Unexpected change to inactive state */
-#define BASR_ATN 0x02 /* ro BUS status */
+#define BASR_ATN 0x02 /* ro BUS status */
#define BASR_ACK 0x01 /* ro BUS status */
/* Write any value to this register to start a DMA send */
@@ -167,8 +156,7 @@
/* Write any value to this register to start an ini mode DMA receive */
#define START_DMA_INITIATOR_RECEIVE_REG 7 /* wo */
-#define C400_CONTROL_STATUS_REG NCR53C400_register_offset-8 /* rw */
-
+/* NCR 53C400(A) Control Status Register bits: */
#define CSR_RESET 0x80 /* wo Resets 53c400 */
#define CSR_53C80_REG 0x80 /* ro 5380 registers busy */
#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
@@ -179,24 +167,10 @@
#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer read */
#define CSR_GATED_53C80_IRQ 0x01 /* ro Last block xferred */
-#if 0
-#define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR
-#else
#define CSR_BASE CSR_53C80_INTR
-#endif
-
-/* Number of 128-byte blocks to be transferred */
-#define C400_BLOCK_COUNTER_REG NCR53C400_register_offset-7 /* rw */
-
-/* Resume transfer after disconnect */
-#define C400_RESUME_TRANSFER_REG NCR53C400_register_offset-6 /* wo */
-
-/* Access to host buffer stack */
-#define C400_HOST_BUFFER NCR53C400_register_offset-4 /* rw */
-
/* Note : PHASE_* macros are based on the values of the STATUS register */
-#define PHASE_MASK (SR_MSG | SR_CD | SR_IO)
+#define PHASE_MASK (SR_MSG | SR_CD | SR_IO)
#define PHASE_DATAOUT 0
#define PHASE_DATAIN SR_IO
@@ -214,224 +188,144 @@
#define PHASE_SR_TO_TCR(phase) ((phase) >> 2)
-/*
- * The internal should_disconnect() function returns these based on the
- * expected length of a disconnect if a device supports disconnect/
- * reconnect.
- */
+#ifndef NO_IRQ
+#define NO_IRQ 0
+#endif
-#define DISCONNECT_NONE 0
-#define DISCONNECT_TIME_TO_DATA 1
-#define DISCONNECT_LONG 2
+#define FLAG_DMA_FIXUP 1 /* Use DMA errata workarounds */
+#define FLAG_NO_PSEUDO_DMA 8 /* Inhibit DMA */
+#define FLAG_LATE_DMA_SETUP 32 /* Setup NCR before DMA H/W */
+#define FLAG_TOSHIBA_DELAY 128 /* Allow for borken CD-ROMs */
-/*
- * These are "special" values for the tag parameter passed to NCR5380_select.
- */
+struct NCR5380_hostdata {
+ NCR5380_implementation_fields; /* Board-specific data */
+ u8 __iomem *io; /* Remapped 5380 address */
+ u8 __iomem *pdma_io; /* Remapped PDMA address */
+ unsigned long poll_loops; /* Register polling limit */
+ spinlock_t lock; /* Protects this struct */
+ struct scsi_cmnd *connected; /* Currently connected cmnd */
+ struct list_head disconnected; /* Waiting for reconnect */
+ struct Scsi_Host *host; /* SCSI host backpointer */
+ struct workqueue_struct *work_q; /* SCSI host work queue */
+ struct work_struct main_task; /* Work item for main loop */
+ int flags; /* Board-specific quirks */
+ int dma_len; /* Requested length of DMA */
+ int read_overruns; /* Transfer size reduction for DMA erratum */
+ unsigned long io_port; /* Device IO port */
+ unsigned long base; /* Device base address */
+ struct list_head unissued; /* Waiting to be issued */
+ struct scsi_cmnd *selecting; /* Cmnd to be connected */
+ struct list_head autosense; /* Priority cmnd queue */
+ struct scsi_cmnd *sensing; /* Cmnd needing autosense */
+ struct scsi_eh_save ses; /* Cmnd state saved for EH */
+ unsigned char busy[8]; /* Index = target, bit = lun */
+ unsigned char id_mask; /* 1 << Host ID */
+ unsigned char id_higher_mask; /* All bits above id_mask */
+ unsigned char last_message; /* Last Message Out */
+ unsigned long region_size; /* Size of address/port range */
+ char info[168]; /* Host banner message */
+};
-#define TAG_NEXT -1 /* Use next free tag */
-#define TAG_NONE -2 /*
- * Establish I_T_L nexus instead of I_T_L_Q
- * even on SCSI-II devices.
- */
+struct NCR5380_cmd {
+ char *ptr;
+ int this_residual;
+ struct scatterlist *buffer;
+ int status;
+ int phase;
+ struct list_head list;
+};
-/*
- * These are "special" values for the irq and dma_channel fields of the
- * Scsi_Host structure
- */
+#define NCR5380_PIO_CHUNK_SIZE 256
-#define SCSI_IRQ_NONE 255
-#define DMA_NONE 255
-#define IRQ_AUTO 254
-#define DMA_AUTO 254
-#define PORT_AUTO 0xffff /* autoprobe io port for 53c400a */
+/* Time limit (ms) to poll registers when IRQs are disabled, e.g. during PDMA */
+#define NCR5380_REG_POLL_TIME 10
-#define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */
-#define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */
-#define FLAG_NCR53C400 4 /* NCR53c400 */
-#define FLAG_NO_PSEUDO_DMA 8 /* Inhibit DMA */
-#define FLAG_DTC3181E 16 /* DTC3181E */
+static inline struct scsi_cmnd *NCR5380_to_scmd(struct NCR5380_cmd *ncmd_ptr)
+{
+ return ((struct scsi_cmnd *)ncmd_ptr) - 1;
+}
-#ifndef ASM
-struct NCR5380_hostdata {
- NCR5380_implementation_fields; /* implementation specific */
- struct Scsi_Host *host; /* Host backpointer */
- unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */
- unsigned char targets_present; /* targets we have connected
- to, so we can call a select
- failure a retryable condition */
- volatile unsigned char busy[8]; /* index = target, bit = lun */
-#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
- volatile int dma_len; /* requested length of DMA */
-#endif
- volatile unsigned char last_message; /* last message OUT */
- volatile Scsi_Cmnd *connected; /* currently connected command */
- volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
- volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
- volatile int restart_select; /* we have disconnected,
- used to restart
- NCR5380_select() */
- volatile unsigned aborted:1; /* flag, says aborted */
- int flags;
- unsigned long time_expires; /* in jiffies, set prior to sleeping */
- int select_time; /* timer in select for target response */
- volatile Scsi_Cmnd *selecting;
- struct delayed_work coroutine; /* our co-routine */
-#ifdef NCR5380_STATS
- unsigned timebase; /* Base for time calcs */
- long time_read[8]; /* time to do reads */
- long time_write[8]; /* time to do writes */
- unsigned long bytes_read[8]; /* bytes read */
- unsigned long bytes_write[8]; /* bytes written */
- unsigned pendingr;
- unsigned pendingw;
-#endif
-#ifdef AUTOSENSE
- struct scsi_eh_save ses;
+static inline struct NCR5380_cmd *NCR5380_to_ncmd(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
+#ifndef NDEBUG
+#define NDEBUG (0)
#endif
-};
-#ifdef __KERNEL__
+#define dprintk(flg, fmt, ...) \
+ do { if ((NDEBUG) & (flg)) \
+ printk(KERN_DEBUG fmt, ## __VA_ARGS__); } while (0)
-#define dprintk(a,b) do {} while(0)
-#define NCR5380_dprint(a,b) do {} while(0)
-#define NCR5380_dprint_phase(a,b) do {} while(0)
+#define dsprintk(flg, host, fmt, ...) \
+ do { if ((NDEBUG) & (flg)) \
+ shost_printk(KERN_DEBUG, host, fmt, ## __VA_ARGS__); \
+ } while (0)
-#if defined(AUTOPROBE_IRQ)
-static int NCR5380_probe_irq(struct Scsi_Host *instance, int possible);
+#if NDEBUG
+#define NCR5380_dprint(flg, arg) \
+ do { if ((NDEBUG) & (flg)) NCR5380_print(arg); } while (0)
+#define NCR5380_dprint_phase(flg, arg) \
+ do { if ((NDEBUG) & (flg)) NCR5380_print_phase(arg); } while (0)
+static void NCR5380_print_phase(struct Scsi_Host *instance);
+static void NCR5380_print(struct Scsi_Host *instance);
+#else
+#define NCR5380_dprint(flg, arg) do {} while (0)
+#define NCR5380_dprint_phase(flg, arg) do {} while (0)
#endif
+
static int NCR5380_init(struct Scsi_Host *instance, int flags);
+static int NCR5380_maybe_reset_bus(struct Scsi_Host *);
static void NCR5380_exit(struct Scsi_Host *instance);
static void NCR5380_information_transfer(struct Scsi_Host *instance);
-#ifndef DONT_USE_INTR
static irqreturn_t NCR5380_intr(int irq, void *dev_id);
-#endif
static void NCR5380_main(struct work_struct *work);
-static void __maybe_unused NCR5380_print_options(struct Scsi_Host *instance);
-#ifdef NDEBUG
-static void NCR5380_print_phase(struct Scsi_Host *instance);
-static void NCR5380_print(struct Scsi_Host *instance);
-#endif
-static int NCR5380_abort(Scsi_Cmnd * cmd);
-static int NCR5380_bus_reset(Scsi_Cmnd * cmd);
-static int NCR5380_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int __maybe_unused NCR5380_proc_info(struct Scsi_Host *instance,
- char *buffer, char **start, off_t offset, int length, int inout);
-
+static const char *NCR5380_info(struct Scsi_Host *instance);
static void NCR5380_reselect(struct Scsi_Host *instance);
-static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag);
-#if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
+static bool NCR5380_select(struct Scsi_Host *, struct scsi_cmnd *);
static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
-#endif
-static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
-
-#if (defined(REAL_DMA) || defined(REAL_DMA_POLL))
-
-#if defined(i386) || defined(__alpha__)
-
-/**
- * NCR5380_pc_dma_setup - setup ISA DMA
- * @instance: adapter to set up
- * @ptr: block to transfer (virtual address)
- * @count: number of bytes to transfer
- * @mode: DMA controller mode to use
- *
- * Program the DMA controller ready to perform an ISA DMA transfer
- * on this chip.
- *
- * Locks: takes and releases the ISA DMA lock.
- */
-
-static __inline__ int NCR5380_pc_dma_setup(struct Scsi_Host *instance, unsigned char *ptr, unsigned int count, unsigned char mode)
+static void NCR5380_transfer_pio(struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data, unsigned int can_sleep);
+static int NCR5380_poll_politely2(struct NCR5380_hostdata *,
+ unsigned int, u8, u8,
+ unsigned int, u8, u8, unsigned long);
+
+static inline int NCR5380_poll_politely(struct NCR5380_hostdata *hostdata,
+ unsigned int reg, u8 bit, u8 val,
+ unsigned long wait)
{
- unsigned limit;
- unsigned long bus_addr = virt_to_bus(ptr);
- unsigned long flags;
-
- if (instance->dma_channel <= 3) {
- if (count > 65536)
- count = 65536;
- limit = 65536 - (bus_addr & 0xFFFF);
- } else {
- if (count > 65536 * 2)
- count = 65536 * 2;
- limit = 65536 * 2 - (bus_addr & 0x1FFFF);
- }
-
- if (count > limit)
- count = limit;
-
- if ((count & 1) || (bus_addr & 1))
- panic("scsi%d : attempted unaligned DMA transfer\n", instance->host_no);
-
- flags=claim_dma_lock();
- disable_dma(instance->dma_channel);
- clear_dma_ff(instance->dma_channel);
- set_dma_addr(instance->dma_channel, bus_addr);
- set_dma_count(instance->dma_channel, count);
- set_dma_mode(instance->dma_channel, mode);
- enable_dma(instance->dma_channel);
- release_dma_lock(flags);
-
- return count;
+ if ((NCR5380_read(reg) & bit) == val)
+ return 0;
+
+ return NCR5380_poll_politely2(hostdata, reg, bit, val,
+ reg, bit, val, wait);
}
-/**
- * NCR5380_pc_dma_write_setup - setup ISA DMA write
- * @instance: adapter to set up
- * @ptr: block to transfer (virtual address)
- * @count: number of bytes to transfer
- *
- * Program the DMA controller ready to perform an ISA DMA write to the
- * SCSI controller.
- *
- * Locks: called routines take and release the ISA DMA lock.
- */
+static int NCR5380_dma_xfer_len(struct NCR5380_hostdata *,
+ struct scsi_cmnd *);
+static int NCR5380_dma_send_setup(struct NCR5380_hostdata *,
+ unsigned char *, int);
+static int NCR5380_dma_recv_setup(struct NCR5380_hostdata *,
+ unsigned char *, int);
+static int NCR5380_dma_residual(struct NCR5380_hostdata *);
-static __inline__ int NCR5380_pc_dma_write_setup(struct Scsi_Host *instance, unsigned char *src, unsigned int count)
+static inline int NCR5380_dma_xfer_none(struct NCR5380_hostdata *hostdata,
+ struct scsi_cmnd *cmd)
{
- return NCR5380_pc_dma_setup(instance, src, count, DMA_MODE_WRITE);
+ return 0;
}
-/**
- * NCR5380_pc_dma_read_setup - setup ISA DMA read
- * @instance: adapter to set up
- * @ptr: block to transfer (virtual address)
- * @count: number of bytes to transfer
- *
- * Program the DMA controller ready to perform an ISA DMA read from the
- * SCSI controller.
- *
- * Locks: called routines take and release the ISA DMA lock.
- */
-
-static __inline__ int NCR5380_pc_dma_read_setup(struct Scsi_Host *instance, unsigned char *src, unsigned int count)
+static inline int NCR5380_dma_setup_none(struct NCR5380_hostdata *hostdata,
+ unsigned char *data, int count)
{
- return NCR5380_pc_dma_setup(instance, src, count, DMA_MODE_READ);
+ return 0;
}
-/**
- * NCR5380_pc_dma_residual - return bytes left
- * @instance: adapter
- *
- * Reports the number of bytes left over after the DMA was terminated.
- *
- * Locks: takes and releases the ISA DMA lock.
- */
-
-static __inline__ int NCR5380_pc_dma_residual(struct Scsi_Host *instance)
+static inline int NCR5380_dma_residual_none(struct NCR5380_hostdata *hostdata)
{
- unsigned long flags;
- int tmp;
-
- flags = claim_dma_lock();
- clear_dma_ff(instance->dma_channel);
- tmp = get_dma_residue(instance->dma_channel);
- release_dma_lock(flags);
-
- return tmp;
+ return 0;
}
-#endif /* defined(i386) || defined(__alpha__) */
-#endif /* defined(REAL_DMA) */
-#endif /* __KERNEL__ */
-#endif /* ndef ASM */
+
#endif /* NCR5380_H */
diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c
deleted file mode 100644
index c91888a0a23c..000000000000
--- a/drivers/scsi/NCR53c406a.c
+++ /dev/null
@@ -1,1091 +0,0 @@
-/*
- * NCR53c406.c
- * Low-level SCSI driver for NCR53c406a chip.
- * Copyright (C) 1994, 1995, 1996 Normunds Saumanis (normunds@fi.ibm.com)
- *
- * LILO command line usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]
- * Specify IRQ = 0 for non-interrupt driven mode.
- * FASTPIO = 1 for fast pio mode, 0 for slow mode.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- */
-
-#define NCR53C406A_DEBUG 0
-#define VERBOSE_NCR53C406A_DEBUG 0
-
-/* Set this to 1 for PIO mode (recommended) or to 0 for DMA mode */
-#define USE_PIO 1
-
-#define USE_BIOS 0
- /* #define BIOS_ADDR 0xD8000 *//* define this if autoprobe fails */
- /* #define PORT_BASE 0x330 *//* define this if autoprobe fails */
- /* #define IRQ_LEV 0 *//* define this if autoprobe fails */
-#define DMA_CHAN 5 /* this is ignored if DMA is disabled */
-
-/* Set this to 0 if you encounter kernel lockups while transferring
- * data in PIO mode */
-#define USE_FAST_PIO 1
-
-/* ============= End of user configurable parameters ============= */
-
-#include <linux/module.h>
-
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-#include <linux/blkdev.h>
-#include <linux/spinlock.h>
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-/* ============================================================= */
-
-#define WATCHDOG 5000000
-
-#define SYNC_MODE 0 /* Synchronous transfer mode */
-
-#ifdef DEBUG
-#undef NCR53C406A_DEBUG
-#define NCR53C406A_DEBUG 1
-#endif
-
-#if USE_PIO
-#define USE_DMA 0
-#else
-#define USE_DMA 1
-#endif
-
-/* Default configuration */
-#define C1_IMG 0x07 /* ID=7 */
-#define C2_IMG 0x48 /* FE SCSI2 */
-#if USE_DMA
-#define C3_IMG 0x21 /* CDB TE */
-#else
-#define C3_IMG 0x20 /* CDB */
-#endif
-#define C4_IMG 0x04 /* ANE */
-#define C5_IMG 0xb6 /* AA PI SIE POL */
-
-#define REG0 (outb(C4_IMG, CONFIG4))
-#define REG1 (outb(C5_IMG, CONFIG5))
-
-#if NCR53C406A_DEBUG
-#define DEB(x) x
-#else
-#define DEB(x)
-#endif
-
-#if VERBOSE_NCR53C406A_DEBUG
-#define VDEB(x) x
-#else
-#define VDEB(x)
-#endif
-
-#define LOAD_DMA_COUNT(count) \
- outb(count & 0xff, TC_LSB); \
- outb((count >> 8) & 0xff, TC_MSB); \
- outb((count >> 16) & 0xff, TC_HIGH);
-
-/* Chip commands */
-#define DMA_OP 0x80
-
-#define SCSI_NOP 0x00
-#define FLUSH_FIFO 0x01
-#define CHIP_RESET 0x02
-#define SCSI_RESET 0x03
-#define RESELECT 0x40
-#define SELECT_NO_ATN 0x41
-#define SELECT_ATN 0x42
-#define SELECT_ATN_STOP 0x43
-#define ENABLE_SEL 0x44
-#define DISABLE_SEL 0x45
-#define SELECT_ATN3 0x46
-#define RESELECT3 0x47
-#define TRANSFER_INFO 0x10
-#define INIT_CMD_COMPLETE 0x11
-#define MSG_ACCEPT 0x12
-#define TRANSFER_PAD 0x18
-#define SET_ATN 0x1a
-#define RESET_ATN 0x1b
-#define SEND_MSG 0x20
-#define SEND_STATUS 0x21
-#define SEND_DATA 0x22
-#define DISCONN_SEQ 0x23
-#define TERMINATE_SEQ 0x24
-#define TARG_CMD_COMPLETE 0x25
-#define DISCONN 0x27
-#define RECV_MSG 0x28
-#define RECV_CMD 0x29
-#define RECV_DATA 0x2a
-#define RECV_CMD_SEQ 0x2b
-#define TARGET_ABORT_DMA 0x04
-
-/*----------------------------------------------------------------*/
-/* the following will set the monitor border color (useful to find
- where something crashed or gets stuck at */
-/* 1 = blue
- 2 = green
- 3 = cyan
- 4 = red
- 5 = magenta
- 6 = yellow
- 7 = white
-*/
-
-#if NCR53C406A_DEBUG
-#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
-#else
-#define rtrc(i) {}
-#endif
-/*----------------------------------------------------------------*/
-
-enum Phase {
- idle,
- data_out,
- data_in,
- command_ph,
- status_ph,
- message_out,
- message_in
-};
-
-/* Static function prototypes */
-static void NCR53c406a_intr(void *);
-static irqreturn_t do_NCR53c406a_intr(int, void *);
-static void chip_init(void);
-static void calc_port_addr(void);
-#ifndef IRQ_LEV
-static int irq_probe(void);
-#endif
-
-/* ================================================================= */
-
-#if USE_BIOS
-static void *bios_base;
-#endif
-
-#ifdef PORT_BASE
-static int port_base = PORT_BASE;
-#else
-static int port_base;
-#endif
-
-#ifdef IRQ_LEV
-static int irq_level = IRQ_LEV;
-#else
-static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized' */
-#endif
-
-#if USE_DMA
-static int dma_chan;
-#endif
-
-#if USE_PIO
-static int fast_pio = USE_FAST_PIO;
-#endif
-
-static Scsi_Cmnd *current_SC;
-static char info_msg[256];
-
-/* ================================================================= */
-
-/* possible BIOS locations */
-#if USE_BIOS
-static void *addresses[] = {
- (void *) 0xd8000,
- (void *) 0xc8000
-};
-#define ADDRESS_COUNT ARRAY_SIZE(addresses)
-#endif /* USE_BIOS */
-
-/* possible i/o port addresses */
-static unsigned short ports[] = { 0x230, 0x330, 0x280, 0x290, 0x330, 0x340, 0x300, 0x310, 0x348, 0x350 };
-#define PORT_COUNT ARRAY_SIZE(ports)
-
-#ifndef MODULE
-/* possible interrupt channels */
-static unsigned short intrs[] = { 10, 11, 12, 15 };
-#define INTR_COUNT ARRAY_SIZE(intrs)
-#endif /* !MODULE */
-
-/* signatures for NCR 53c406a based controllers */
-#if USE_BIOS
-struct signature {
- char *signature;
- int sig_offset;
- int sig_length;
-} signatures[] __initdata = {
- /* 1 2 3 4 5 6 */
- /* 123456789012345678901234567890123456789012345678901234567890 */
- {
-"Copyright (C) Acculogic, Inc.\r\n2.8M Diskette Extension Bios ver 4.04.03 03/01/1993", 61, 82},};
-
-#define SIGNATURE_COUNT ARRAY_SIZE(signatures)
-#endif /* USE_BIOS */
-
-/* ============================================================ */
-
-/* Control Register Set 0 */
-static int TC_LSB; /* transfer counter lsb */
-static int TC_MSB; /* transfer counter msb */
-static int SCSI_FIFO; /* scsi fifo register */
-static int CMD_REG; /* command register */
-static int STAT_REG; /* status register */
-static int DEST_ID; /* selection/reselection bus id */
-static int INT_REG; /* interrupt status register */
-static int SRTIMOUT; /* select/reselect timeout reg */
-static int SEQ_REG; /* sequence step register */
-static int SYNCPRD; /* synchronous transfer period */
-static int FIFO_FLAGS; /* indicates # of bytes in fifo */
-static int SYNCOFF; /* synchronous offset register */
-static int CONFIG1; /* configuration register */
-static int CLKCONV; /* clock conversion reg */
- /*static int TESTREG;*//* test mode register */
-static int CONFIG2; /* Configuration 2 Register */
-static int CONFIG3; /* Configuration 3 Register */
-static int CONFIG4; /* Configuration 4 Register */
-static int TC_HIGH; /* Transfer Counter High */
- /*static int FIFO_BOTTOM;*//* Reserve FIFO byte register */
-
-/* Control Register Set 1 */
- /*static int JUMPER_SENSE;*//* Jumper sense port reg (r/w) */
- /*static int SRAM_PTR;*//* SRAM address pointer reg (r/w) */
- /*static int SRAM_DATA;*//* SRAM data register (r/w) */
-static int PIO_FIFO; /* PIO FIFO registers (r/w) */
- /*static int PIO_FIFO1;*//* */
- /*static int PIO_FIFO2;*//* */
- /*static int PIO_FIFO3;*//* */
-static int PIO_STATUS; /* PIO status (r/w) */
- /*static int ATA_CMD;*//* ATA command/status reg (r/w) */
- /*static int ATA_ERR;*//* ATA features/error register (r/w) */
-static int PIO_FLAG; /* PIO flag interrupt enable (r/w) */
-static int CONFIG5; /* Configuration 5 register (r/w) */
- /*static int SIGNATURE;*//* Signature Register (r) */
- /*static int CONFIG6;*//* Configuration 6 register (r) */
-
-/* ============================================================== */
-
-#if USE_DMA
-static __inline__ int NCR53c406a_dma_setup(unsigned char *ptr, unsigned int count, unsigned char mode)
-{
- unsigned limit;
- unsigned long flags = 0;
-
- VDEB(printk("dma: before count=%d ", count));
- if (dma_chan <= 3) {
- if (count > 65536)
- count = 65536;
- limit = 65536 - (((unsigned) ptr) & 0xFFFF);
- } else {
- if (count > (65536 << 1))
- count = (65536 << 1);
- limit = (65536 << 1) - (((unsigned) ptr) & 0x1FFFF);
- }
-
- if (count > limit)
- count = limit;
-
- VDEB(printk("after count=%d\n", count));
- if ((count & 1) || (((unsigned) ptr) & 1))
- panic("NCR53c406a: attempted unaligned DMA transfer\n");
-
- flags = claim_dma_lock();
- disable_dma(dma_chan);
- clear_dma_ff(dma_chan);
- set_dma_addr(dma_chan, (long) ptr);
- set_dma_count(dma_chan, count);
- set_dma_mode(dma_chan, mode);
- enable_dma(dma_chan);
- release_dma_lock(flags);
-
- return count;
-}
-
-static __inline__ int NCR53c406a_dma_write(unsigned char *src, unsigned int count)
-{
- return NCR53c406a_dma_setup(src, count, DMA_MODE_WRITE);
-}
-
-static __inline__ int NCR53c406a_dma_read(unsigned char *src, unsigned int count)
-{
- return NCR53c406a_dma_setup(src, count, DMA_MODE_READ);
-}
-
-static __inline__ int NCR53c406a_dma_residual(void)
-{
- register int tmp;
- unsigned long flags;
-
- flags = claim_dma_lock();
- clear_dma_ff(dma_chan);
- tmp = get_dma_residue(dma_chan);
- release_dma_lock(flags);
-
- return tmp;
-}
-#endif /* USE_DMA */
-
-#if USE_PIO
-static __inline__ int NCR53c406a_pio_read(unsigned char *request, unsigned int reqlen)
-{
- int i;
- int len; /* current scsi fifo size */
-
- REG1;
- while (reqlen) {
- i = inb(PIO_STATUS);
- /* VDEB(printk("pio_status=%x\n", i)); */
- if (i & 0x80)
- return 0;
-
- switch (i & 0x1e) {
- default:
- case 0x10:
- len = 0;
- break;
- case 0x0:
- len = 1;
- break;
- case 0x8:
- len = 42;
- break;
- case 0xc:
- len = 84;
- break;
- case 0xe:
- len = 128;
- break;
- }
-
- if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
- return 0;
- }
-
- if (len) {
- if (len > reqlen)
- len = reqlen;
-
- if (fast_pio && len > 3) {
- insl(PIO_FIFO, request, len >> 2);
- request += len & 0xfc;
- reqlen -= len & 0xfc;
- } else {
- while (len--) {
- *request++ = inb(PIO_FIFO);
- reqlen--;
- }
- }
- }
- }
- return 0;
-}
-
-static __inline__ int NCR53c406a_pio_write(unsigned char *request, unsigned int reqlen)
-{
- int i = 0;
- int len; /* current scsi fifo size */
-
- REG1;
- while (reqlen && !(i & 0x40)) {
- i = inb(PIO_STATUS);
- /* VDEB(printk("pio_status=%x\n", i)); */
- if (i & 0x80) /* error */
- return 0;
-
- switch (i & 0x1e) {
- case 0x10:
- len = 128;
- break;
- case 0x0:
- len = 84;
- break;
- case 0x8:
- len = 42;
- break;
- case 0xc:
- len = 1;
- break;
- default:
- case 0xe:
- len = 0;
- break;
- }
-
- if (len) {
- if (len > reqlen)
- len = reqlen;
-
- if (fast_pio && len > 3) {
- outsl(PIO_FIFO, request, len >> 2);
- request += len & 0xfc;
- reqlen -= len & 0xfc;
- } else {
- while (len--) {
- outb(*request++, PIO_FIFO);
- reqlen--;
- }
- }
- }
- }
- return 0;
-}
-#endif /* USE_PIO */
-
-static int __init NCR53c406a_detect(struct scsi_host_template * tpnt)
-{
- int present = 0;
- struct Scsi_Host *shpnt = NULL;
-#ifndef PORT_BASE
- int i;
-#endif
-
-#if USE_BIOS
- int ii, jj;
- bios_base = 0;
- /* look for a valid signature */
- for (ii = 0; ii < ADDRESS_COUNT && !bios_base; ii++)
- for (jj = 0; (jj < SIGNATURE_COUNT) && !bios_base; jj++)
- if (!memcmp((void *) addresses[ii] + signatures[jj].sig_offset, (void *) signatures[jj].signature, (int) signatures[jj].sig_length))
- bios_base = addresses[ii];
-
- if (!bios_base) {
- printk("NCR53c406a: BIOS signature not found\n");
- return 0;
- }
-
- DEB(printk("NCR53c406a BIOS found at 0x%x\n", (unsigned int) bios_base);
- );
-#endif /* USE_BIOS */
-
-#ifdef PORT_BASE
- if (!request_region(port_base, 0x10, "NCR53c406a")) /* ports already snatched */
- port_base = 0;
-
-#else /* autodetect */
- if (port_base) { /* LILO override */
- if (!request_region(port_base, 0x10, "NCR53c406a"))
- port_base = 0;
- } else {
- for (i = 0; i < PORT_COUNT && !port_base; i++) {
- if (!request_region(ports[i], 0x10, "NCR53c406a")) {
- DEB(printk("NCR53c406a: port 0x%x in use\n", ports[i]));
- } else {
- VDEB(printk("NCR53c406a: port 0x%x available\n", ports[i]));
- outb(C5_IMG, ports[i] + 0x0d); /* reg set 1 */
- if ((inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) & 0xf8) == 0x58) {
- port_base = ports[i];
- VDEB(printk("NCR53c406a: Sig register valid\n"));
- VDEB(printk("port_base=0x%x\n", port_base));
- break;
- }
- release_region(ports[i], 0x10);
- }
- }
- }
-#endif /* PORT_BASE */
-
- if (!port_base) { /* no ports found */
- printk("NCR53c406a: no available ports found\n");
- return 0;
- }
-
- DEB(printk("NCR53c406a detected\n"));
-
- calc_port_addr();
- chip_init();
-
-#ifndef IRQ_LEV
- if (irq_level < 0) { /* LILO override if >= 0 */
- irq_level = irq_probe();
- if (irq_level < 0) { /* Trouble */
- printk("NCR53c406a: IRQ problem, irq_level=%d, giving up\n", irq_level);
- goto err_release;
- }
- }
-#endif
-
- DEB(printk("NCR53c406a: using port_base 0x%x\n", port_base));
-
- present = 1;
- tpnt->proc_name = "NCR53c406a";
-
- shpnt = scsi_register(tpnt, 0);
- if (!shpnt) {
- printk("NCR53c406a: Unable to register host, giving up.\n");
- goto err_release;
- }
-
- if (irq_level > 0) {
- if (request_irq(irq_level, do_NCR53c406a_intr, 0, "NCR53c406a", shpnt)) {
- printk("NCR53c406a: unable to allocate IRQ %d\n", irq_level);
- goto err_free_scsi;
- }
- tpnt->can_queue = 1;
- DEB(printk("NCR53c406a: allocated IRQ %d\n", irq_level));
- } else if (irq_level == 0) {
- tpnt->can_queue = 0;
- DEB(printk("NCR53c406a: No interrupts detected\n"));
- printk("NCR53c406a driver no longer supports polling interface\n");
- printk("Please email linux-scsi@vger.kernel.org\n");
-
-#if USE_DMA
- printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n");
-#endif /* USE_DMA */
- goto err_free_scsi;
- } else {
- DEB(printk("NCR53c406a: Shouldn't get here!\n"));
- goto err_free_scsi;
- }
-
-#if USE_DMA
- dma_chan = DMA_CHAN;
- if (request_dma(dma_chan, "NCR53c406a") != 0) {
- printk("NCR53c406a: unable to allocate DMA channel %d\n", dma_chan);
- goto err_free_irq;
- }
-
- DEB(printk("Allocated DMA channel %d\n", dma_chan));
-#endif /* USE_DMA */
-
- shpnt->irq = irq_level;
- shpnt->io_port = port_base;
- shpnt->n_io_port = 0x10;
-#if USE_DMA
- shpnt->dma = dma_chan;
-#endif
-
-#if USE_DMA
- sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, DMA channel %d.", port_base, irq_level, dma_chan);
-#else
- sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, %s PIO mode.", port_base, irq_level, fast_pio ? "fast" : "slow");
-#endif
-
- return (present);
-
-#if USE_DMA
- err_free_irq:
- if (irq_level)
- free_irq(irq_level, shpnt);
-#endif
- err_free_scsi:
- scsi_unregister(shpnt);
- err_release:
- release_region(port_base, 0x10);
- return 0;
-}
-
-static int NCR53c406a_release(struct Scsi_Host *shost)
-{
- if (shost->irq)
- free_irq(shost->irq, NULL);
-#ifdef USE_DMA
- if (shost->dma_channel != 0xff)
- free_dma(shost->dma_channel);
-#endif
- if (shost->io_port && shost->n_io_port)
- release_region(shost->io_port, shost->n_io_port);
-
- scsi_unregister(shost);
- return 0;
-}
-
-#ifndef MODULE
-/* called from init/main.c */
-static int __init NCR53c406a_setup(char *str)
-{
- static size_t setup_idx = 0;
- size_t i;
- int ints[4];
-
- DEB(printk("NCR53c406a: Setup called\n");
- );
-
- if (setup_idx >= PORT_COUNT - 1) {
- printk("NCR53c406a: Setup called too many times. Bad LILO params?\n");
- return 0;
- }
- get_options(str, 4, ints);
- if (ints[0] < 1 || ints[0] > 3) {
- printk("NCR53c406a: Malformed command line\n");
- printk("NCR53c406a: Usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]\n");
- return 0;
- }
- for (i = 0; i < PORT_COUNT && !port_base; i++)
- if (ports[i] == ints[1]) {
- port_base = ints[1];
- DEB(printk("NCR53c406a: Specified port_base 0x%x\n", port_base);
- )
- }
- if (!port_base) {
- printk("NCR53c406a: Invalid PORTBASE 0x%x specified\n", ints[1]);
- return 0;
- }
-
- if (ints[0] > 1) {
- if (ints[2] == 0) {
- irq_level = 0;
- DEB(printk("NCR53c406a: Specified irq %d\n", irq_level);
- )
- } else
- for (i = 0; i < INTR_COUNT && irq_level < 0; i++)
- if (intrs[i] == ints[2]) {
- irq_level = ints[2];
- DEB(printk("NCR53c406a: Specified irq %d\n", port_base);
- )
- }
- if (irq_level < 0)
- printk("NCR53c406a: Invalid IRQ %d specified\n", ints[2]);
- }
-
- if (ints[0] > 2)
- fast_pio = ints[3];
-
- DEB(printk("NCR53c406a: port_base=0x%x, irq=%d, fast_pio=%d\n", port_base, irq_level, fast_pio);)
- return 1;
-}
-
-__setup("ncr53c406a=", NCR53c406a_setup);
-
-#endif /* !MODULE */
-
-static const char *NCR53c406a_info(struct Scsi_Host *SChost)
-{
- DEB(printk("NCR53c406a_info called\n"));
- return (info_msg);
-}
-
-#if 0
-static void wait_intr(void)
-{
- unsigned long i = jiffies + WATCHDOG;
-
- while (time_after(i, jiffies) && !(inb(STAT_REG) & 0xe0)) { /* wait for a pseudo-interrupt */
- cpu_relax();
- barrier();
- }
-
- if (time_before_eq(i, jiffies)) { /* Timed out */
- rtrc(0);
- current_SC->result = DID_TIME_OUT << 16;
- current_SC->SCp.phase = idle;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- NCR53c406a_intr(NULL);
-}
-#endif
-
-static int NCR53c406a_queue_lck(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
-{
- int i;
-
- VDEB(printk("NCR53c406a_queue called\n"));
- DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->target, SCpnt->lun, scsi_bufflen(SCpnt)));
-
-#if 0
- VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
- printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
- VDEB(printk("\n"));
-#endif
-
- current_SC = SCpnt;
- current_SC->scsi_done = done;
- current_SC->SCp.phase = command_ph;
- current_SC->SCp.Status = 0;
- current_SC->SCp.Message = 0;
-
- /* We are locked here already by the mid layer */
- REG0;
- outb(scmd_id(SCpnt), DEST_ID); /* set destination */
- outb(FLUSH_FIFO, CMD_REG); /* reset the fifos */
-
- for (i = 0; i < SCpnt->cmd_len; i++) {
- outb(SCpnt->cmnd[i], SCSI_FIFO);
- }
- outb(SELECT_NO_ATN, CMD_REG);
-
- rtrc(1);
- return 0;
-}
-
-static DEF_SCSI_QCMD(NCR53c406a_queue)
-
-static int NCR53c406a_host_reset(Scsi_Cmnd * SCpnt)
-{
- DEB(printk("NCR53c406a_reset called\n"));
-
- spin_lock_irq(SCpnt->device->host->host_lock);
-
- outb(C4_IMG, CONFIG4); /* Select reg set 0 */
- outb(CHIP_RESET, CMD_REG);
- outb(SCSI_NOP, CMD_REG); /* required after reset */
- outb(SCSI_RESET, CMD_REG);
- chip_init();
-
- rtrc(2);
-
- spin_unlock_irq(SCpnt->device->host->host_lock);
-
- return SUCCESS;
-}
-
-static int NCR53c406a_biosparm(struct scsi_device *disk,
- struct block_device *dev,
- sector_t capacity, int *info_array)
-{
- int size;
-
- DEB(printk("NCR53c406a_biosparm called\n"));
-
- size = capacity;
- info_array[0] = 64; /* heads */
- info_array[1] = 32; /* sectors */
- info_array[2] = size >> 11; /* cylinders */
- if (info_array[2] > 1024) { /* big disk */
- info_array[0] = 255;
- info_array[1] = 63;
- info_array[2] = size / (255 * 63);
- }
- return 0;
-}
-
-static irqreturn_t do_NCR53c406a_intr(int unused, void *dev_id)
-{
- unsigned long flags;
- struct Scsi_Host *dev = dev_id;
-
- spin_lock_irqsave(dev->host_lock, flags);
- NCR53c406a_intr(dev_id);
- spin_unlock_irqrestore(dev->host_lock, flags);
- return IRQ_HANDLED;
-}
-
-static void NCR53c406a_intr(void *dev_id)
-{
- DEB(unsigned char fifo_size;
- )
- DEB(unsigned char seq_reg;
- )
- unsigned char status, int_reg;
-#if USE_PIO
- unsigned char pio_status;
- struct scatterlist *sg;
- int i;
-#endif
-
- VDEB(printk("NCR53c406a_intr called\n"));
-
-#if USE_PIO
- REG1;
- pio_status = inb(PIO_STATUS);
-#endif
- REG0;
- status = inb(STAT_REG);
- DEB(seq_reg = inb(SEQ_REG));
- int_reg = inb(INT_REG);
- DEB(fifo_size = inb(FIFO_FLAGS) & 0x1f);
-
-#if NCR53C406A_DEBUG
- printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x", status, seq_reg, int_reg, fifo_size);
-#if (USE_DMA)
- printk("\n");
-#else
- printk(", pio=%02x\n", pio_status);
-#endif /* USE_DMA */
-#endif /* NCR53C406A_DEBUG */
-
- if (int_reg & 0x80) { /* SCSI reset intr */
- rtrc(3);
- DEB(printk("NCR53c406a: reset intr received\n"));
- current_SC->SCp.phase = idle;
- current_SC->result = DID_RESET << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-#if USE_PIO
- if (pio_status & 0x80) {
- printk("NCR53C406A: Warning: PIO error!\n");
- current_SC->SCp.phase = idle;
- current_SC->result = DID_ERROR << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-#endif /* USE_PIO */
-
- if (status & 0x20) { /* Parity error */
- printk("NCR53c406a: Warning: parity error!\n");
- current_SC->SCp.phase = idle;
- current_SC->result = DID_PARITY << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- if (status & 0x40) { /* Gross error */
- printk("NCR53c406a: Warning: gross error!\n");
- current_SC->SCp.phase = idle;
- current_SC->result = DID_ERROR << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- if (int_reg & 0x20) { /* Disconnect */
- DEB(printk("NCR53c406a: disconnect intr received\n"));
- if (current_SC->SCp.phase != message_in) { /* Unexpected disconnect */
- current_SC->result = DID_NO_CONNECT << 16;
- } else { /* Command complete, return status and message */
- current_SC->result = (current_SC->SCp.Status & 0xff)
- | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
- }
-
- rtrc(0);
- current_SC->SCp.phase = idle;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- switch (status & 0x07) { /* scsi phase */
- case 0x00: /* DATA-OUT */
- if (int_reg & 0x10) { /* Target requesting info transfer */
- rtrc(5);
- current_SC->SCp.phase = data_out;
- VDEB(printk("NCR53c406a: Data-Out phase\n"));
- outb(FLUSH_FIFO, CMD_REG);
- LOAD_DMA_COUNT(scsi_bufflen(current_SC)); /* Max transfer size */
-#if USE_DMA /* No s/g support for DMA */
- NCR53c406a_dma_write(scsi_sglist(current_SC),
- scsdi_bufflen(current_SC));
-
-#endif /* USE_DMA */
- outb(TRANSFER_INFO | DMA_OP, CMD_REG);
-#if USE_PIO
- scsi_for_each_sg(current_SC, sg, scsi_sg_count(current_SC), i) {
- NCR53c406a_pio_write(sg_virt(sg), sg->length);
- }
- REG0;
-#endif /* USE_PIO */
- }
- break;
-
- case 0x01: /* DATA-IN */
- if (int_reg & 0x10) { /* Target requesting info transfer */
- rtrc(6);
- current_SC->SCp.phase = data_in;
- VDEB(printk("NCR53c406a: Data-In phase\n"));
- outb(FLUSH_FIFO, CMD_REG);
- LOAD_DMA_COUNT(scsi_bufflen(current_SC)); /* Max transfer size */
-#if USE_DMA /* No s/g support for DMA */
- NCR53c406a_dma_read(scsi_sglist(current_SC),
- scsdi_bufflen(current_SC));
-#endif /* USE_DMA */
- outb(TRANSFER_INFO | DMA_OP, CMD_REG);
-#if USE_PIO
- scsi_for_each_sg(current_SC, sg, scsi_sg_count(current_SC), i) {
- NCR53c406a_pio_read(sg_virt(sg), sg->length);
- }
- REG0;
-#endif /* USE_PIO */
- }
- break;
-
- case 0x02: /* COMMAND */
- current_SC->SCp.phase = command_ph;
- printk("NCR53c406a: Warning: Unknown interrupt occurred in command phase!\n");
- break;
-
- case 0x03: /* STATUS */
- rtrc(7);
- current_SC->SCp.phase = status_ph;
- VDEB(printk("NCR53c406a: Status phase\n"));
- outb(FLUSH_FIFO, CMD_REG);
- outb(INIT_CMD_COMPLETE, CMD_REG);
- break;
-
- case 0x04: /* Reserved */
- case 0x05: /* Reserved */
- printk("NCR53c406a: WARNING: Reserved phase!!!\n");
- break;
-
- case 0x06: /* MESSAGE-OUT */
- DEB(printk("NCR53c406a: Message-Out phase\n"));
- current_SC->SCp.phase = message_out;
- outb(SET_ATN, CMD_REG); /* Reject the message */
- outb(MSG_ACCEPT, CMD_REG);
- break;
-
- case 0x07: /* MESSAGE-IN */
- rtrc(4);
- VDEB(printk("NCR53c406a: Message-In phase\n"));
- current_SC->SCp.phase = message_in;
-
- current_SC->SCp.Status = inb(SCSI_FIFO);
- current_SC->SCp.Message = inb(SCSI_FIFO);
-
- VDEB(printk("SCSI FIFO size=%d\n", inb(FIFO_FLAGS) & 0x1f));
- DEB(printk("Status = %02x Message = %02x\n", current_SC->SCp.Status, current_SC->SCp.Message));
-
- if (current_SC->SCp.Message == SAVE_POINTERS || current_SC->SCp.Message == DISCONNECT) {
- outb(SET_ATN, CMD_REG); /* Reject message */
- DEB(printk("Discarding SAVE_POINTERS message\n"));
- }
- outb(MSG_ACCEPT, CMD_REG);
- break;
- }
-}
-
-#ifndef IRQ_LEV
-static int irq_probe(void)
-{
- int irqs, irq;
- unsigned long i;
-
- inb(INT_REG); /* clear the interrupt register */
- irqs = probe_irq_on();
-
- /* Invalid command will cause an interrupt */
- REG0;
- outb(0xff, CMD_REG);
-
- /* Wait for the interrupt to occur */
- i = jiffies + WATCHDOG;
- while (time_after(i, jiffies) && !(inb(STAT_REG) & 0x80))
- barrier();
- if (time_before_eq(i, jiffies)) { /* Timed out, must be hardware trouble */
- probe_irq_off(irqs);
- return -1;
- }
-
- irq = probe_irq_off(irqs);
-
- /* Kick the chip */
- outb(CHIP_RESET, CMD_REG);
- outb(SCSI_NOP, CMD_REG);
- chip_init();
-
- return irq;
-}
-#endif /* IRQ_LEV */
-
-static void chip_init(void)
-{
- REG1;
-#if USE_DMA
- outb(0x00, PIO_STATUS);
-#else /* USE_PIO */
- outb(0x01, PIO_STATUS);
-#endif
- outb(0x00, PIO_FLAG);
-
- outb(C4_IMG, CONFIG4); /* REG0; */
- outb(C3_IMG, CONFIG3);
- outb(C2_IMG, CONFIG2);
- outb(C1_IMG, CONFIG1);
-
- outb(0x05, CLKCONV); /* clock conversion factor */
- outb(0x9C, SRTIMOUT); /* Selection timeout */
- outb(0x05, SYNCPRD); /* Synchronous transfer period */
- outb(SYNC_MODE, SYNCOFF); /* synchronous mode */
-}
-
-static void __init calc_port_addr(void)
-{
- /* Control Register Set 0 */
- TC_LSB = (port_base + 0x00);
- TC_MSB = (port_base + 0x01);
- SCSI_FIFO = (port_base + 0x02);
- CMD_REG = (port_base + 0x03);
- STAT_REG = (port_base + 0x04);
- DEST_ID = (port_base + 0x04);
- INT_REG = (port_base + 0x05);
- SRTIMOUT = (port_base + 0x05);
- SEQ_REG = (port_base + 0x06);
- SYNCPRD = (port_base + 0x06);
- FIFO_FLAGS = (port_base + 0x07);
- SYNCOFF = (port_base + 0x07);
- CONFIG1 = (port_base + 0x08);
- CLKCONV = (port_base + 0x09);
- /* TESTREG = (port_base+0x0A); */
- CONFIG2 = (port_base + 0x0B);
- CONFIG3 = (port_base + 0x0C);
- CONFIG4 = (port_base + 0x0D);
- TC_HIGH = (port_base + 0x0E);
- /* FIFO_BOTTOM = (port_base+0x0F); */
-
- /* Control Register Set 1 */
- /* JUMPER_SENSE = (port_base+0x00); */
- /* SRAM_PTR = (port_base+0x01); */
- /* SRAM_DATA = (port_base+0x02); */
- PIO_FIFO = (port_base + 0x04);
- /* PIO_FIFO1 = (port_base+0x05); */
- /* PIO_FIFO2 = (port_base+0x06); */
- /* PIO_FIFO3 = (port_base+0x07); */
- PIO_STATUS = (port_base + 0x08);
- /* ATA_CMD = (port_base+0x09); */
- /* ATA_ERR = (port_base+0x0A); */
- PIO_FLAG = (port_base + 0x0B);
- CONFIG5 = (port_base + 0x0D);
- /* SIGNATURE = (port_base+0x0E); */
- /* CONFIG6 = (port_base+0x0F); */
-}
-
-MODULE_LICENSE("GPL");
-
-/* NOTE: scatter-gather support only works in PIO mode.
- * Use SG_NONE if DMA mode is enabled!
- */
-
-static struct scsi_host_template driver_template =
-{
- .proc_name = "NCR53c406a" /* proc_name */,
- .name = "NCR53c406a" /* name */,
- .detect = NCR53c406a_detect /* detect */,
- .release = NCR53c406a_release,
- .info = NCR53c406a_info /* info */,
- .queuecommand = NCR53c406a_queue /* queuecommand */,
- .eh_host_reset_handler = NCR53c406a_host_reset /* reset */,
- .bios_param = NCR53c406a_biosparm /* biosparm */,
- .can_queue = 1 /* can_queue */,
- .this_id = 7 /* SCSI ID of the chip */,
- .sg_tablesize = 32 /*SG_ALL*/ /*SG_NONE*/,
- .cmd_per_lun = 1 /* commands per lun */,
- .unchecked_isa_dma = 1 /* unchecked_isa_dma */,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-#include "scsi_module.c"
-
-/*
- * Overrides for Emacs so that we get a uniform tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/NCR_D700.c b/drivers/scsi/NCR_D700.c
deleted file mode 100644
index 8647256ad66d..000000000000
--- a/drivers/scsi/NCR_D700.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/* NCR Dual 700 MCA SCSI Driver
- *
- * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com
-**-----------------------------------------------------------------------------
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-**-----------------------------------------------------------------------------
- */
-
-/* Notes:
- *
- * Most of the work is done in the chip specific module, 53c700.o
- *
- * TODO List:
- *
- * 1. Extract the SCSI ID from the voyager CMOS table (necessary to
- * support multi-host environments.
- *
- * */
-
-
-/* CHANGELOG
- *
- * Version 2.2
- *
- * Added mca_set_adapter_name().
- *
- * Version 2.1
- *
- * Modularise the driver into a Board piece (this file) and a chip
- * piece 53c700.[ch] and 53c700.scr, added module options. You can
- * now specify the scsi id by the parameters
- *
- * NCR_D700=slot:<n> [siop:<n>] id:<n> ....
- *
- * They need to be comma separated if compiled into the kernel
- *
- * Version 2.0
- *
- * Initial implementation of TCQ (Tag Command Queueing). TCQ is full
- * featured and uses the clock algorithm to keep track of outstanding
- * tags and guard against individual tag starvation. Also fixed a bug
- * in all of the 1.x versions where the D700_data_residue() function
- * was returning results off by 32 bytes (and thus causing the same 32
- * bytes to be written twice corrupting the data block). It turns out
- * the 53c700 only has a 6 bit DBC and DFIFO registers not 7 bit ones
- * like the 53c710 (The 710 is the only data manual still available,
- * which I'd been using to program the 700).
- *
- * Version 1.2
- *
- * Much improved message handling engine
- *
- * Version 1.1
- *
- * Add code to handle selection reasonably correctly. By the time we
- * get the selection interrupt, we've already responded, but drop off the
- * bus and hope the selector will go away.
- *
- * Version 1.0:
- *
- * Initial release. Fully functional except for procfs and tag
- * command queueing. Has only been tested on cards with 53c700-66
- * chips and only single ended. Features are
- *
- * 1. Synchronous data transfers to offset 8 (limit of 700-66) and
- * 100ns (10MHz) limit of SCSI-2
- *
- * 2. Disconnection and reselection
- *
- * Testing:
- *
- * I've only really tested this with the 700-66 chip, but have done
- * soak tests in multi-device environments to verify that
- * disconnections and reselections are being processed correctly.
- * */
-
-#define NCR_D700_VERSION "2.2"
-
-#include <linux/blkdev.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mca.h>
-#include <linux/slab.h>
-#include <asm/io.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_spi.h>
-
-#include "53c700.h"
-#include "NCR_D700.h"
-
-static char *NCR_D700; /* command line from insmod */
-
-MODULE_AUTHOR("James Bottomley");
-MODULE_DESCRIPTION("NCR Dual700 SCSI Driver");
-MODULE_LICENSE("GPL");
-module_param(NCR_D700, charp, 0);
-
-static __u8 __devinitdata id_array[2*(MCA_MAX_SLOT_NR + 1)] =
- { [0 ... 2*(MCA_MAX_SLOT_NR + 1)-1] = 7 };
-
-#ifdef MODULE
-#define ARG_SEP ' '
-#else
-#define ARG_SEP ','
-#endif
-
-static int __init
-param_setup(char *string)
-{
- char *pos = string, *next;
- int slot = -1, siop = -1;
-
- while(pos != NULL && (next = strchr(pos, ':')) != NULL) {
- int val = (int)simple_strtoul(++next, NULL, 0);
-
- if(!strncmp(pos, "slot:", 5))
- slot = val;
- else if(!strncmp(pos, "siop:", 5))
- siop = val;
- else if(!strncmp(pos, "id:", 3)) {
- if(slot == -1) {
- printk(KERN_WARNING "NCR D700: Must specify slot for id parameter\n");
- } else if(slot > MCA_MAX_SLOT_NR) {
- printk(KERN_WARNING "NCR D700: Illegal slot %d for id %d\n", slot, val);
- } else {
- if(siop != 0 && siop != 1) {
- id_array[slot*2] = val;
- id_array[slot*2 + 1] =val;
- } else {
- id_array[slot*2 + siop] = val;
- }
- }
- }
- if((pos = strchr(pos, ARG_SEP)) != NULL)
- pos++;
- }
- return 1;
-}
-
-/* Host template. The 53c700 routine NCR_700_detect will
- * fill in all of the missing routines */
-static struct scsi_host_template NCR_D700_driver_template = {
- .module = THIS_MODULE,
- .name = "NCR Dual 700 MCA",
- .proc_name = "NCR_D700",
- .this_id = 7,
-};
-
-/* We needs this helper because we have two hosts per struct device */
-struct NCR_D700_private {
- struct device *dev;
- struct Scsi_Host *hosts[2];
- char name[30];
- char pad;
-};
-
-static int __devinit
-NCR_D700_probe_one(struct NCR_D700_private *p, int siop, int irq,
- int slot, u32 region, int differential)
-{
- struct NCR_700_Host_Parameters *hostdata;
- struct Scsi_Host *host;
- int ret;
-
- hostdata = kzalloc(sizeof(*hostdata), GFP_KERNEL);
- if (!hostdata) {
- printk(KERN_ERR "NCR D700: SIOP%d: Failed to allocate host"
- "data, detatching\n", siop);
- return -ENOMEM;
- }
-
- if (!request_region(region, 64, "NCR_D700")) {
- printk(KERN_ERR "NCR D700: Failed to reserve IO region 0x%x\n",
- region);
- ret = -ENODEV;
- goto region_failed;
- }
-
- /* Fill in the three required pieces of hostdata */
- hostdata->base = ioport_map(region, 64);
- hostdata->differential = (((1<<siop) & differential) != 0);
- hostdata->clock = NCR_D700_CLOCK_MHZ;
- hostdata->burst_length = 8;
-
- /* and register the siop */
- host = NCR_700_detect(&NCR_D700_driver_template, hostdata, p->dev);
- if (!host) {
- ret = -ENOMEM;
- goto detect_failed;
- }
-
- p->hosts[siop] = host;
- /* FIXME: read this from SUS */
- host->this_id = id_array[slot * 2 + siop];
- host->irq = irq;
- host->base = region;
- scsi_scan_host(host);
-
- return 0;
-
- detect_failed:
- release_region(region, 64);
- region_failed:
- kfree(hostdata);
-
- return ret;
-}
-
-static irqreturn_t
-NCR_D700_intr(int irq, void *data)
-{
- struct NCR_D700_private *p = (struct NCR_D700_private *)data;
- int i, found = 0;
-
- for (i = 0; i < 2; i++)
- if (p->hosts[i] &&
- NCR_700_intr(irq, p->hosts[i]) == IRQ_HANDLED)
- found++;
-
- return found ? IRQ_HANDLED : IRQ_NONE;
-}
-
-/* Detect a D700 card. Note, because of the setup --- the chips are
- * essentially connectecd to the MCA bus independently, it is easier
- * to set them up as two separate host adapters, rather than one
- * adapter with two channels */
-static int __devinit
-NCR_D700_probe(struct device *dev)
-{
- struct NCR_D700_private *p;
- int differential;
- static int banner = 1;
- struct mca_device *mca_dev = to_mca_device(dev);
- int slot = mca_dev->slot;
- int found = 0;
- int irq, i;
- int pos3j, pos3k, pos3a, pos3b, pos4;
- __u32 base_addr, offset_addr;
-
- /* enable board interrupt */
- pos4 = mca_device_read_pos(mca_dev, 4);
- pos4 |= 0x4;
- mca_device_write_pos(mca_dev, 4, pos4);
-
- mca_device_write_pos(mca_dev, 6, 9);
- pos3j = mca_device_read_pos(mca_dev, 3);
- mca_device_write_pos(mca_dev, 6, 10);
- pos3k = mca_device_read_pos(mca_dev, 3);
- mca_device_write_pos(mca_dev, 6, 0);
- pos3a = mca_device_read_pos(mca_dev, 3);
- mca_device_write_pos(mca_dev, 6, 1);
- pos3b = mca_device_read_pos(mca_dev, 3);
-
- base_addr = ((pos3j << 8) | pos3k) & 0xfffffff0;
- offset_addr = ((pos3a << 8) | pos3b) & 0xffffff70;
-
- irq = (pos4 & 0x3) + 11;
- if(irq >= 13)
- irq++;
- if(banner) {
- printk(KERN_NOTICE "NCR D700: Driver Version " NCR_D700_VERSION "\n"
- "NCR D700: Copyright (c) 2001 by James.Bottomley@HansenPartnership.com\n"
- "NCR D700:\n");
- banner = 0;
- }
- /* now do the bus related transforms */
- irq = mca_device_transform_irq(mca_dev, irq);
- base_addr = mca_device_transform_ioport(mca_dev, base_addr);
- offset_addr = mca_device_transform_ioport(mca_dev, offset_addr);
-
- printk(KERN_NOTICE "NCR D700: found in slot %d irq = %d I/O base = 0x%x\n", slot, irq, offset_addr);
-
- /*outb(BOARD_RESET, base_addr);*/
-
- /* clear any pending interrupts */
- (void)inb(base_addr + 0x08);
- /* get modctl, used later for setting diff bits */
- switch(differential = (inb(base_addr + 0x08) >> 6)) {
- case 0x00:
- /* only SIOP1 differential */
- differential = 0x02;
- break;
- case 0x01:
- /* Both SIOPs differential */
- differential = 0x03;
- break;
- case 0x03:
- /* No SIOPs differential */
- differential = 0x00;
- break;
- default:
- printk(KERN_ERR "D700: UNEXPECTED DIFFERENTIAL RESULT 0x%02x\n",
- differential);
- differential = 0x00;
- break;
- }
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
-
- p->dev = dev;
- snprintf(p->name, sizeof(p->name), "D700(%s)", dev_name(dev));
- if (request_irq(irq, NCR_D700_intr, IRQF_SHARED, p->name, p)) {
- printk(KERN_ERR "D700: request_irq failed\n");
- kfree(p);
- return -EBUSY;
- }
- /* plumb in both 700 chips */
- for (i = 0; i < 2; i++) {
- int err;
-
- if ((err = NCR_D700_probe_one(p, i, irq, slot,
- offset_addr + (0x80 * i),
- differential)) != 0)
- printk("D700: SIOP%d: probe failed, error = %d\n",
- i, err);
- else
- found++;
- }
-
- if (!found) {
- kfree(p);
- return -ENODEV;
- }
-
- mca_device_set_claim(mca_dev, 1);
- mca_device_set_name(mca_dev, "NCR_D700");
- dev_set_drvdata(dev, p);
- return 0;
-}
-
-static void __devexit
-NCR_D700_remove_one(struct Scsi_Host *host)
-{
- scsi_remove_host(host);
- NCR_700_release(host);
- kfree((struct NCR_700_Host_Parameters *)host->hostdata[0]);
- free_irq(host->irq, host);
- release_region(host->base, 64);
-}
-
-static int __devexit
-NCR_D700_remove(struct device *dev)
-{
- struct NCR_D700_private *p = dev_get_drvdata(dev);
- int i;
-
- for (i = 0; i < 2; i++)
- NCR_D700_remove_one(p->hosts[i]);
-
- kfree(p);
- return 0;
-}
-
-static short NCR_D700_id_table[] = { NCR_D700_MCA_ID, 0 };
-
-static struct mca_driver NCR_D700_driver = {
- .id_table = NCR_D700_id_table,
- .driver = {
- .name = "NCR_D700",
- .bus = &mca_bus_type,
- .probe = NCR_D700_probe,
- .remove = __devexit_p(NCR_D700_remove),
- },
-};
-
-static int __init NCR_D700_init(void)
-{
-#ifdef MODULE
- if (NCR_D700)
- param_setup(NCR_D700);
-#endif
-
- return mca_register_driver(&NCR_D700_driver);
-}
-
-static void __exit NCR_D700_exit(void)
-{
- mca_unregister_driver(&NCR_D700_driver);
-}
-
-module_init(NCR_D700_init);
-module_exit(NCR_D700_exit);
-
-__setup("NCR_D700=", param_setup);
diff --git a/drivers/scsi/NCR_D700.h b/drivers/scsi/NCR_D700.h
deleted file mode 100644
index f167af6bd2af..000000000000
--- a/drivers/scsi/NCR_D700.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/* NCR Dual 700 MCA SCSI Driver
- *
- * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com
- */
-
-#ifndef _NCR_D700_H
-#define _NCR_D700_H
-
-/* Don't turn on debugging messages */
-#undef NCR_D700_DEBUG
-
-/* The MCA identifier */
-#define NCR_D700_MCA_ID 0x0092
-
-/* Defines for the Board registers */
-#define BOARD_RESET 0x80 /* board level reset */
-#define ADD_PARENB 0x04 /* Address Parity Enabled */
-#define DAT_PARENB 0x01 /* Data Parity Enabled */
-#define SFBK_ENB 0x10 /* SFDBK Interrupt Enabled */
-#define LED0GREEN 0x20 /* Led 0 (red 0; green 1) */
-#define LED1GREEN 0x40 /* Led 1 (red 0; green 1) */
-#define LED0RED 0xDF /* Led 0 (red 0; green 1) */
-#define LED1RED 0xBF /* Led 1 (red 0; green 1) */
-
-#define NCR_D700_CLOCK_MHZ 50
-
-#endif
diff --git a/drivers/scsi/NCR_Q720.c b/drivers/scsi/NCR_Q720.c
deleted file mode 100644
index afdbb9addf18..000000000000
--- a/drivers/scsi/NCR_Q720.c
+++ /dev/null
@@ -1,377 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/* NCR Quad 720 MCA SCSI Driver
- *
- * Copyright (C) 2003 by James.Bottomley@HansenPartnership.com
- */
-
-#include <linux/blkdev.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mca.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <asm/io.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-#include "ncr53c8xx.h"
-
-#include "NCR_Q720.h"
-
-static struct ncr_chip q720_chip __initdata = {
- .revision_id = 0x0f,
- .burst_max = 3,
- .offset_max = 8,
- .nr_divisor = 4,
- .features = FE_WIDE | FE_DIFF | FE_VARCLK,
-};
-
-MODULE_AUTHOR("James Bottomley");
-MODULE_DESCRIPTION("NCR Quad 720 SCSI Driver");
-MODULE_LICENSE("GPL");
-
-#define NCR_Q720_VERSION "0.9"
-
-/* We needs this helper because we have up to four hosts per struct device */
-struct NCR_Q720_private {
- struct device *dev;
- void __iomem * mem_base;
- __u32 phys_mem_base;
- __u32 mem_size;
- __u8 irq;
- __u8 siops;
- __u8 irq_enable;
- struct Scsi_Host *hosts[4];
-};
-
-static struct scsi_host_template NCR_Q720_tpnt = {
- .module = THIS_MODULE,
- .proc_name = "NCR_Q720",
-};
-
-static irqreturn_t
-NCR_Q720_intr(int irq, void *data)
-{
- struct NCR_Q720_private *p = (struct NCR_Q720_private *)data;
- __u8 sir = (readb(p->mem_base + 0x0d) & 0xf0) >> 4;
- __u8 siop;
-
- sir |= ~p->irq_enable;
-
- if(sir == 0xff)
- return IRQ_NONE;
-
-
- while((siop = ffz(sir)) < p->siops) {
- sir |= 1<<siop;
- ncr53c8xx_intr(irq, p->hosts[siop]);
- }
- return IRQ_HANDLED;
-}
-
-static int __init
-NCR_Q720_probe_one(struct NCR_Q720_private *p, int siop,
- int irq, int slot, __u32 paddr, void __iomem *vaddr)
-{
- struct ncr_device device;
- __u8 scsi_id;
- static int unit = 0;
- __u8 scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1);
- __u8 differential = readb(vaddr + NCR_Q720_SCSR_OFFSET) & 0x20;
- __u8 version;
- int error;
-
- scsi_id = scsr1 >> 4;
- /* enable burst length 16 (FIXME: should allow this) */
- scsr1 |= 0x02;
- /* force a siop reset */
- scsr1 |= 0x04;
- writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1);
- udelay(10);
- version = readb(vaddr + 0x18) >> 4;
-
- memset(&device, 0, sizeof(struct ncr_device));
- /* Initialise ncr_device structure with items required by ncr_attach. */
- device.chip = q720_chip;
- device.chip.revision_id = version;
- device.host_id = scsi_id;
- device.dev = p->dev;
- device.slot.base = paddr;
- device.slot.base_c = paddr;
- device.slot.base_v = vaddr;
- device.slot.irq = irq;
- device.differential = differential ? 2 : 0;
- printk("Q720 probe unit %d (siop%d) at 0x%lx, diff = %d, vers = %d\n", unit, siop,
- (unsigned long)paddr, differential, version);
-
- p->hosts[siop] = ncr_attach(&NCR_Q720_tpnt, unit++, &device);
-
- if (!p->hosts[siop])
- goto fail;
-
- p->irq_enable |= (1<<siop);
- scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1);
- /* clear the disable interrupt bit */
- scsr1 &= ~0x01;
- writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1);
-
- error = scsi_add_host(p->hosts[siop], p->dev);
- if (error)
- ncr53c8xx_release(p->hosts[siop]);
- else
- scsi_scan_host(p->hosts[siop]);
- return error;
-
- fail:
- return -ENODEV;
-}
-
-/* Detect a Q720 card. Note, because of the setup --- the chips are
- * essentially connectecd to the MCA bus independently, it is easier
- * to set them up as two separate host adapters, rather than one
- * adapter with two channels */
-static int __init
-NCR_Q720_probe(struct device *dev)
-{
- struct NCR_Q720_private *p;
- static int banner = 1;
- struct mca_device *mca_dev = to_mca_device(dev);
- int slot = mca_dev->slot;
- int found = 0;
- int irq, i, siops;
- __u8 pos2, pos4, asr2, asr9, asr10;
- __u16 io_base;
- __u32 base_addr, mem_size;
- void __iomem *mem_base;
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
-
- pos2 = mca_device_read_pos(mca_dev, 2);
- /* enable device */
- pos2 |= NCR_Q720_POS2_BOARD_ENABLE | NCR_Q720_POS2_INTERRUPT_ENABLE;
- mca_device_write_pos(mca_dev, 2, pos2);
-
- io_base = (pos2 & NCR_Q720_POS2_IO_MASK) << NCR_Q720_POS2_IO_SHIFT;
-
-
- if(banner) {
- printk(KERN_NOTICE "NCR Q720: Driver Version " NCR_Q720_VERSION "\n"
- "NCR Q720: Copyright (c) 2003 by James.Bottomley@HansenPartnership.com\n"
- "NCR Q720:\n");
- banner = 0;
- }
- io_base = mca_device_transform_ioport(mca_dev, io_base);
-
- /* OK, this is phase one of the bootstrap, we now know the
- * I/O space base address. All the configuration registers
- * are mapped here (including pos) */
-
- /* sanity check I/O mapping */
- i = inb(io_base) | (inb(io_base+1)<<8);
- if(i != NCR_Q720_MCA_ID) {
- printk(KERN_ERR "NCR_Q720, adapter failed to I/O map registers correctly at 0x%x(0x%x)\n", io_base, i);
- kfree(p);
- return -ENODEV;
- }
-
- /* Phase II, find the ram base and memory map the board register */
- pos4 = inb(io_base + 4);
- /* enable streaming data */
- pos4 |= 0x01;
- outb(pos4, io_base + 4);
- base_addr = (pos4 & 0x7e) << 20;
- base_addr += (pos4 & 0x80) << 23;
- asr10 = inb(io_base + 0x12);
- base_addr += (asr10 & 0x80) << 24;
- base_addr += (asr10 & 0x70) << 23;
-
- /* OK, got the base addr, now we need to find the ram size,
- * enable and map it */
- asr9 = inb(io_base + 0x11);
- i = (asr9 & 0xc0) >> 6;
- if(i == 0)
- mem_size = 1024;
- else
- mem_size = 1 << (19 + i);
-
- /* enable the sram mapping */
- asr9 |= 0x20;
-
- /* disable the rom mapping */
- asr9 &= ~0x10;
-
- outb(asr9, io_base + 0x11);
-
- if(!request_mem_region(base_addr, mem_size, "NCR_Q720")) {
- printk(KERN_ERR "NCR_Q720: Failed to claim memory region 0x%lx\n-0x%lx",
- (unsigned long)base_addr,
- (unsigned long)(base_addr + mem_size));
- goto out_free;
- }
-
- if (dma_declare_coherent_memory(dev, base_addr, base_addr,
- mem_size, DMA_MEMORY_MAP)
- != DMA_MEMORY_MAP) {
- printk(KERN_ERR "NCR_Q720: DMA declare memory failed\n");
- goto out_release_region;
- }
-
- /* The first 1k of the memory buffer is a memory map of the registers
- */
- mem_base = dma_mark_declared_memory_occupied(dev, base_addr,
- 1024);
- if (IS_ERR(mem_base)) {
- printk("NCR_Q720 failed to reserve memory mapped region\n");
- goto out_release;
- }
-
- /* now also enable accesses in asr 2 */
- asr2 = inb(io_base + 0x0a);
-
- asr2 |= 0x01;
-
- outb(asr2, io_base + 0x0a);
-
- /* get the number of SIOPs (this should be 2 or 4) */
- siops = ((asr2 & 0xe0) >> 5) + 1;
-
- /* sanity check mapping (again) */
- i = readw(mem_base);
- if(i != NCR_Q720_MCA_ID) {
- printk(KERN_ERR "NCR_Q720, adapter failed to memory map registers correctly at 0x%lx(0x%x)\n", (unsigned long)base_addr, i);
- goto out_release;
- }
-
- irq = readb(mem_base + 5) & 0x0f;
-
-
- /* now do the bus related transforms */
- irq = mca_device_transform_irq(mca_dev, irq);
-
- printk(KERN_NOTICE "NCR Q720: found in slot %d irq = %d mem base = 0x%lx siops = %d\n", slot, irq, (unsigned long)base_addr, siops);
- printk(KERN_NOTICE "NCR Q720: On board ram %dk\n", mem_size/1024);
-
- p->dev = dev;
- p->mem_base = mem_base;
- p->phys_mem_base = base_addr;
- p->mem_size = mem_size;
- p->irq = irq;
- p->siops = siops;
-
- if (request_irq(irq, NCR_Q720_intr, IRQF_SHARED, "NCR_Q720", p)) {
- printk(KERN_ERR "NCR_Q720: request irq %d failed\n", irq);
- goto out_release;
- }
- /* disable all the siop interrupts */
- for(i = 0; i < siops; i++) {
- void __iomem *reg_scsr1 = mem_base + NCR_Q720_CHIP_REGISTER_OFFSET
- + i*NCR_Q720_SIOP_SHIFT + NCR_Q720_SCSR_OFFSET + 1;
- __u8 scsr1 = readb(reg_scsr1);
- scsr1 |= 0x01;
- writeb(scsr1, reg_scsr1);
- }
-
- /* plumb in all 720 chips */
- for (i = 0; i < siops; i++) {
- void __iomem *siop_v_base = mem_base + NCR_Q720_CHIP_REGISTER_OFFSET
- + i*NCR_Q720_SIOP_SHIFT;
- __u32 siop_p_base = base_addr + NCR_Q720_CHIP_REGISTER_OFFSET
- + i*NCR_Q720_SIOP_SHIFT;
- __u16 port = io_base + NCR_Q720_CHIP_REGISTER_OFFSET
- + i*NCR_Q720_SIOP_SHIFT;
- int err;
-
- outb(0xff, port + 0x40);
- outb(0x07, port + 0x41);
- if ((err = NCR_Q720_probe_one(p, i, irq, slot,
- siop_p_base, siop_v_base)) != 0)
- printk("Q720: SIOP%d: probe failed, error = %d\n",
- i, err);
- else
- found++;
- }
-
- if (!found) {
- kfree(p);
- return -ENODEV;
- }
-
- mca_device_set_claim(mca_dev, 1);
- mca_device_set_name(mca_dev, "NCR_Q720");
- dev_set_drvdata(dev, p);
-
- return 0;
-
- out_release:
- dma_release_declared_memory(dev);
- out_release_region:
- release_mem_region(base_addr, mem_size);
- out_free:
- kfree(p);
-
- return -ENODEV;
-}
-
-static void __exit
-NCR_Q720_remove_one(struct Scsi_Host *host)
-{
- scsi_remove_host(host);
- ncr53c8xx_release(host);
-}
-
-static int __exit
-NCR_Q720_remove(struct device *dev)
-{
- struct NCR_Q720_private *p = dev_get_drvdata(dev);
- int i;
-
- for (i = 0; i < p->siops; i++)
- if(p->hosts[i])
- NCR_Q720_remove_one(p->hosts[i]);
-
- dma_release_declared_memory(dev);
- release_mem_region(p->phys_mem_base, p->mem_size);
- free_irq(p->irq, p);
- kfree(p);
- return 0;
-}
-
-static short NCR_Q720_id_table[] = { NCR_Q720_MCA_ID, 0 };
-
-static struct mca_driver NCR_Q720_driver = {
- .id_table = NCR_Q720_id_table,
- .driver = {
- .name = "NCR_Q720",
- .bus = &mca_bus_type,
- .probe = NCR_Q720_probe,
- .remove = __devexit_p(NCR_Q720_remove),
- },
-};
-
-static int __init
-NCR_Q720_init(void)
-{
- int ret = ncr53c8xx_init();
- if (!ret)
- ret = mca_register_driver(&NCR_Q720_driver);
- if (ret)
- ncr53c8xx_exit();
- return ret;
-}
-
-static void __exit
-NCR_Q720_exit(void)
-{
- mca_unregister_driver(&NCR_Q720_driver);
- ncr53c8xx_exit();
-}
-
-module_init(NCR_Q720_init);
-module_exit(NCR_Q720_exit);
diff --git a/drivers/scsi/NCR_Q720.h b/drivers/scsi/NCR_Q720.h
deleted file mode 100644
index 7b9209008187..000000000000
--- a/drivers/scsi/NCR_Q720.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/* NCR Quad 720 MCA SCSI Driver
- *
- * Copyright (C) 2003 by James.Bottomley@HansenPartnership.com
- */
-
-#ifndef _NCR_Q720_H
-#define _NCR_Q720_H
-
-/* The MCA identifier */
-#define NCR_Q720_MCA_ID 0x0720
-
-#define NCR_Q720_CLOCK_MHZ 30
-
-#define NCR_Q720_POS2_BOARD_ENABLE 0x01
-#define NCR_Q720_POS2_INTERRUPT_ENABLE 0x02
-#define NCR_Q720_POS2_PARITY_DISABLE 0x04
-#define NCR_Q720_POS2_IO_MASK 0xf8
-#define NCR_Q720_POS2_IO_SHIFT 8
-
-#define NCR_Q720_CHIP_REGISTER_OFFSET 0x200
-#define NCR_Q720_SCSR_OFFSET 0x070
-#define NCR_Q720_SIOP_SHIFT 0x080
-
-#endif
-
-
diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c
index a391090a17c5..a8979f9e30ff 100644
--- a/drivers/scsi/a100u2w.c
+++ b/drivers/scsi/a100u2w.c
@@ -143,7 +143,7 @@ static u8 wait_chip_ready(struct orc_host * host)
for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */
if (inb(host->base + ORC_HCTRL) & HOSTSTOP) /* Wait HOSTSTOP set */
return 1;
- mdelay(100);
+ msleep(100);
}
return 0;
}
@@ -155,7 +155,7 @@ static u8 wait_firmware_ready(struct orc_host * host)
for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */
if (inb(host->base + ORC_HSTUS) & RREADY) /* Wait READY set */
return 1;
- mdelay(100); /* wait 100ms before try again */
+ msleep(100); /* wait 100ms before try again */
}
return 0;
}
@@ -269,7 +269,7 @@ static u8 orc_nv_read(struct orc_host * host, u8 address, u8 *ptr)
}
/**
- * orc_exec_sb - Queue an SCB with the HA
+ * orc_exec_scb - Queue an SCB with the HA
* @host: host adapter the SCB belongs to
* @scb: SCB to queue for execution
*/
@@ -586,7 +586,7 @@ static int orc_reset_scsi_bus(struct orc_host * host)
* orc_device_reset - device reset handler
* @host: host to reset
* @cmd: command causing the reset
- * @target; target device
+ * @target: target device
*
* Reset registers, reset a hanging bus and kill active and disconnected
* commands for target w/o soft reset
@@ -727,7 +727,7 @@ static void orc_release_scb(struct orc_host *host, struct orc_scb *scb)
spin_unlock_irqrestore(&(host->allocation_lock), flags);
}
-/**
+/*
* orchid_abort_scb - abort a command
*
* Abort a queued command that has been passed to the firmware layer
@@ -888,10 +888,10 @@ static int inia100_build_scb(struct orc_host * host, struct orc_scb * scb, struc
scb->sense_len = SENSE_SIZE;
scb->cdb_len = cmd->cmd_len;
if (scb->cdb_len >= IMAX_CDB) {
- printk("max cdb length= %x\b", cmd->cmd_len);
+ printk("max cdb length= %x\n", cmd->cmd_len);
scb->cdb_len = IMAX_CDB;
}
- scb->ident = cmd->device->lun | DISC_ALLOW;
+ scb->ident = (u8)(cmd->device->lun & 0xff) | DISC_ALLOW;
if (cmd->device->tagged_supported) { /* Tag Support */
scb->tag_msg = SIMPLE_QUEUE_TAG; /* Do simple tag only */
} else {
@@ -902,22 +902,19 @@ static int inia100_build_scb(struct orc_host * host, struct orc_scb * scb, struc
}
/**
- * inia100_queue - queue command with host
+ * inia100_queue_lck - queue command with host
* @cmd: Command block
- * @done: Completion function
*
* Called by the mid layer to queue a command. Process the command
* block, build the host specific scb structures and if there is room
* queue the command down to the controller
*/
-
-static int inia100_queue_lck(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd *))
+static int inia100_queue_lck(struct scsi_cmnd *cmd)
{
struct orc_scb *scb;
struct orc_host *host; /* Point to Host adapter control block */
host = (struct orc_host *) cmd->device->host->hostdata;
- cmd->scsi_done = done;
/* Get free SCSI control block */
if ((scb = orc_alloc_scb(host)) == NULL)
return SCSI_MLQUEUE_HOST_BUSY;
@@ -1042,7 +1039,7 @@ static void inia100_scb_handler(struct orc_host *host, struct orc_scb *scb)
}
cmd->result = scb->tastat | (scb->hastat << 16);
scsi_dma_unmap(cmd);
- cmd->scsi_done(cmd); /* Notify system DONE */
+ scsi_done(cmd); /* Notify system DONE */
orc_release_scb(host, scb); /* Release SCB for current channel */
}
@@ -1068,7 +1065,7 @@ static irqreturn_t inia100_intr(int irqno, void *devid)
return res;
}
-static struct scsi_host_template inia100_template = {
+static const struct scsi_host_template inia100_template = {
.proc_name = "inia100",
.name = inia100_REVID,
.queuecommand = inia100_queue,
@@ -1078,24 +1075,20 @@ static struct scsi_host_template inia100_template = {
.can_queue = 1,
.this_id = 1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
};
-static int __devinit inia100_probe_one(struct pci_dev *pdev,
- const struct pci_device_id *id)
+static int inia100_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
{
struct Scsi_Host *shost;
struct orc_host *host;
unsigned long port, bios;
int error = -ENODEV;
u32 sz;
- unsigned long biosaddr;
- char *bios_phys;
if (pci_enable_device(pdev))
goto out;
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
printk(KERN_WARNING "Unable to set 32bit DMA "
"on inia100 adapter, ignoring.\n");
goto out_disable_device;
@@ -1125,27 +1118,22 @@ static int __devinit inia100_probe_one(struct pci_dev *pdev,
/* Get total memory needed for SCB */
sz = ORC_MAXQUEUE * sizeof(struct orc_scb);
- host->scb_virt = pci_alloc_consistent(pdev, sz,
- &host->scb_phys);
+ host->scb_virt = dma_alloc_coherent(&pdev->dev, sz, &host->scb_phys,
+ GFP_KERNEL);
if (!host->scb_virt) {
printk("inia100: SCB memory allocation error\n");
goto out_host_put;
}
- memset(host->scb_virt, 0, sz);
/* Get total memory needed for ESCB */
sz = ORC_MAXQUEUE * sizeof(struct orc_extended_scb);
- host->escb_virt = pci_alloc_consistent(pdev, sz,
- &host->escb_phys);
+ host->escb_virt = dma_alloc_coherent(&pdev->dev, sz, &host->escb_phys,
+ GFP_KERNEL);
if (!host->escb_virt) {
printk("inia100: ESCB memory allocation error\n");
goto out_free_scb_array;
}
- memset(host->escb_virt, 0, sz);
- biosaddr = host->BIOScfg;
- biosaddr = (biosaddr << 4);
- bios_phys = phys_to_virt(biosaddr);
if (init_orchid(host)) { /* Initialize orchid chip */
printk("inia100: initial orchid fail!!\n");
goto out_free_escb_array;
@@ -1182,10 +1170,12 @@ static int __devinit inia100_probe_one(struct pci_dev *pdev,
out_free_irq:
free_irq(shost->irq, shost);
out_free_escb_array:
- pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(struct orc_extended_scb),
+ dma_free_coherent(&pdev->dev,
+ ORC_MAXQUEUE * sizeof(struct orc_extended_scb),
host->escb_virt, host->escb_phys);
out_free_scb_array:
- pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(struct orc_scb),
+ dma_free_coherent(&pdev->dev,
+ ORC_MAXQUEUE * sizeof(struct orc_scb),
host->scb_virt, host->scb_phys);
out_host_put:
scsi_host_put(shost);
@@ -1197,7 +1187,7 @@ out:
return error;
}
-static void __devexit inia100_remove_one(struct pci_dev *pdev)
+static void inia100_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct orc_host *host = (struct orc_host *)shost->hostdata;
@@ -1205,16 +1195,18 @@ static void __devexit inia100_remove_one(struct pci_dev *pdev)
scsi_remove_host(shost);
free_irq(shost->irq, shost);
- pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(struct orc_extended_scb),
+ dma_free_coherent(&pdev->dev,
+ ORC_MAXQUEUE * sizeof(struct orc_extended_scb),
host->escb_virt, host->escb_phys);
- pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(struct orc_scb),
+ dma_free_coherent(&pdev->dev,
+ ORC_MAXQUEUE * sizeof(struct orc_scb),
host->scb_virt, host->scb_phys);
release_region(shost->io_port, 256);
scsi_host_put(shost);
}
-static struct pci_device_id inia100_pci_tbl[] = {
+static const struct pci_device_id inia100_pci_tbl[] = {
{PCI_VENDOR_ID_INIT, 0x1060, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
};
@@ -1224,22 +1216,11 @@ static struct pci_driver inia100_pci_driver = {
.name = "inia100",
.id_table = inia100_pci_tbl,
.probe = inia100_probe_one,
- .remove = __devexit_p(inia100_remove_one),
+ .remove = inia100_remove_one,
};
-static int __init inia100_init(void)
-{
- return pci_register_driver(&inia100_pci_driver);
-}
-
-static void __exit inia100_exit(void)
-{
- pci_unregister_driver(&inia100_pci_driver);
-}
+module_pci_driver(inia100_pci_driver);
MODULE_DESCRIPTION("Initio A100U2W SCSI driver");
MODULE_AUTHOR("Initio Corporation");
MODULE_LICENSE("Dual BSD/GPL");
-
-module_init(inia100_init);
-module_exit(inia100_exit);
diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c
index 1bb5d3f0e260..204448bfd04b 100644
--- a/drivers/scsi/a2091.c
+++ b/drivers/scsi/a2091.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -5,13 +6,17 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/zorro.h>
+#include <linux/module.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
#include "wd33c93.h"
#include "a2091.h"
@@ -19,8 +24,11 @@
struct a2091_hostdata {
struct WD33C93_hostdata wh;
struct a2091_scsiregs *regs;
+ struct device *dev;
};
+#define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)
+
static irqreturn_t a2091_intr(int irq, void *data)
{
struct Scsi_Host *instance = data;
@@ -39,16 +47,33 @@ static irqreturn_t a2091_intr(int irq, void *data)
static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
{
+ struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
+ unsigned long len = scsi_pointer->this_residual;
struct Scsi_Host *instance = cmd->device->host;
struct a2091_hostdata *hdata = shost_priv(instance);
struct WD33C93_hostdata *wh = &hdata->wh;
struct a2091_scsiregs *regs = hdata->regs;
unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
- unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+ dma_addr_t addr;
+
+ addr = dma_map_single(hdata->dev, scsi_pointer->ptr,
+ len, DMA_DIR(dir_in));
+ if (dma_mapping_error(hdata->dev, addr)) {
+ dev_warn(hdata->dev, "cannot map SCSI data block %p\n",
+ scsi_pointer->ptr);
+ return 1;
+ }
+ scsi_pointer->dma_handle = addr;
/* don't allow DMA if the physical address is bad */
if (addr & A2091_XFER_MASK) {
- wh->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff;
+ /* drop useless mapping */
+ dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
+ scsi_pointer->this_residual,
+ DMA_DIR(dir_in));
+ scsi_pointer->dma_handle = (dma_addr_t) NULL;
+
+ wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff;
wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len,
GFP_KERNEL);
@@ -58,8 +83,21 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
return 1;
}
- /* get the physical address of the bounce buffer */
- addr = virt_to_bus(wh->dma_bounce_buffer);
+ if (!dir_in) {
+ /* copy to bounce buffer for a write */
+ memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr,
+ scsi_pointer->this_residual);
+ }
+
+ /* will flush/invalidate cache for us */
+ addr = dma_map_single(hdata->dev, wh->dma_bounce_buffer,
+ wh->dma_bounce_len, DMA_DIR(dir_in));
+ /* can't map buffer; use PIO */
+ if (dma_mapping_error(hdata->dev, addr)) {
+ dev_warn(hdata->dev, "cannot map bounce buffer %p\n",
+ wh->dma_bounce_buffer);
+ return 1;
+ }
/* the bounce buffer may not be in the first 16M of physmem */
if (addr & A2091_XFER_MASK) {
@@ -70,11 +108,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
return 1;
}
- if (!dir_in) {
- /* copy to bounce buffer for a write */
- memcpy(wh->dma_bounce_buffer, cmd->SCp.ptr,
- cmd->SCp.this_residual);
- }
+ scsi_pointer->dma_handle = addr;
}
/* setup dma direction */
@@ -89,13 +123,8 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
/* setup DMA *physical* address */
regs->ACR = addr;
- if (dir_in) {
- /* invalidate any cache */
- cache_clear(addr, cmd->SCp.this_residual);
- } else {
- /* push any dirty cache */
- cache_push(addr, cmd->SCp.this_residual);
- }
+ /* no more cache flush here - dma_map_single() takes care */
+
/* start DMA */
regs->ST_DMA = 1;
@@ -106,6 +135,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
int status)
{
+ struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt);
struct a2091_hostdata *hdata = shost_priv(instance);
struct WD33C93_hostdata *wh = &hdata->wh;
struct a2091_scsiregs *regs = hdata->regs;
@@ -135,51 +165,39 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
/* restore the CONTROL bits (minus the direction flag) */
regs->CNTR = CNTR_PDMD | CNTR_INTEN;
+ dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
+ scsi_pointer->this_residual,
+ DMA_DIR(wh->dma_dir));
+
/* copy from a bounce buffer, if necessary */
if (status && wh->dma_bounce_buffer) {
if (wh->dma_dir)
- memcpy(SCpnt->SCp.ptr, wh->dma_bounce_buffer,
- SCpnt->SCp.this_residual);
+ memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer,
+ scsi_pointer->this_residual);
kfree(wh->dma_bounce_buffer);
wh->dma_bounce_buffer = NULL;
wh->dma_bounce_len = 0;
}
}
-static int a2091_bus_reset(struct scsi_cmnd *cmd)
-{
- struct Scsi_Host *instance = cmd->device->host;
-
- /* FIXME perform bus-specific reset */
-
- /* FIXME 2: kill this function, and let midlayer fall back
- to the same action, calling wd33c93_host_reset() */
-
- spin_lock_irq(instance->host_lock);
- wd33c93_host_reset(cmd);
- spin_unlock_irq(instance->host_lock);
-
- return SUCCESS;
-}
-
-static struct scsi_host_template a2091_scsi_template = {
+static const struct scsi_host_template a2091_scsi_template = {
.module = THIS_MODULE,
.name = "Commodore A2091/A590 SCSI",
- .proc_info = wd33c93_proc_info,
+ .show_info = wd33c93_show_info,
+ .write_info = wd33c93_write_info,
.proc_name = "A2901",
.queuecommand = wd33c93_queuecommand,
.eh_abort_handler = wd33c93_abort,
- .eh_bus_reset_handler = a2091_bus_reset,
.eh_host_reset_handler = wd33c93_host_reset,
.can_queue = CAN_QUEUE,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = CMD_PER_LUN,
- .use_clustering = DISABLE_CLUSTERING
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct scsi_pointer),
};
-static int __devinit a2091_probe(struct zorro_dev *z,
- const struct zorro_device_id *ent)
+static int a2091_probe(struct zorro_dev *z, const struct zorro_device_id *ent)
{
struct Scsi_Host *instance;
int error;
@@ -187,6 +205,11 @@ static int __devinit a2091_probe(struct zorro_dev *z,
wd33c93_regs wdregs;
struct a2091_hostdata *hdata;
+ if (dma_set_mask_and_coherent(&z->dev, DMA_BIT_MASK(24))) {
+ dev_warn(&z->dev, "cannot use 24 bit DMA\n");
+ return -ENODEV;
+ }
+
if (!request_mem_region(z->resource.start, 256, "wd33c93"))
return -EBUSY;
@@ -200,13 +223,14 @@ static int __devinit a2091_probe(struct zorro_dev *z,
instance->irq = IRQ_AMIGA_PORTS;
instance->unique_id = z->slotaddr;
- regs = (struct a2091_scsiregs *)ZTWO_VADDR(z->resource.start);
+ regs = ZTWO_VADDR(z->resource.start);
regs->DAWR = DAWR_A2091;
wdregs.SASR = &regs->SASR;
wdregs.SCMD = &regs->SCMD;
hdata = shost_priv(instance);
+ hdata->dev = &z->dev;
hdata->wh.no_sync = 0xff;
hdata->wh.fast = 0;
hdata->wh.dma_mode = CTRL_DMA;
@@ -238,7 +262,7 @@ fail_alloc:
return error;
}
-static void __devexit a2091_remove(struct zorro_dev *z)
+static void a2091_remove(struct zorro_dev *z)
{
struct Scsi_Host *instance = zorro_get_drvdata(z);
struct a2091_hostdata *hdata = shost_priv(instance);
@@ -250,7 +274,7 @@ static void __devexit a2091_remove(struct zorro_dev *z)
release_mem_region(z->resource.start, 256);
}
-static struct zorro_device_id a2091_zorro_tbl[] __devinitdata = {
+static struct zorro_device_id a2091_zorro_tbl[] = {
{ ZORRO_PROD_CBM_A590_A2091_1 },
{ ZORRO_PROD_CBM_A590_A2091_2 },
{ 0 }
@@ -261,7 +285,7 @@ static struct zorro_driver a2091_driver = {
.name = "a2091",
.id_table = a2091_zorro_tbl,
.probe = a2091_probe,
- .remove = __devexit_p(a2091_remove),
+ .remove = a2091_remove,
};
static int __init a2091_init(void)
diff --git a/drivers/scsi/a2091.h b/drivers/scsi/a2091.h
index 794b8e65c711..8d8a4074a570 100644
--- a/drivers/scsi/a2091.h
+++ b/drivers/scsi/a2091.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef A2091_H
#define A2091_H
diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c
index d9468027fb61..bf054dd7682b 100644
--- a/drivers/scsi/a3000.c
+++ b/drivers/scsi/a3000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/ioport.h>
@@ -6,13 +7,18 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
#include "wd33c93.h"
#include "a3000.h"
@@ -20,8 +26,11 @@
struct a3000_hostdata {
struct WD33C93_hostdata wh;
struct a3000_scsiregs *regs;
+ struct device *dev;
};
+#define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)
+
static irqreturn_t a3000_intr(int irq, void *data)
{
struct Scsi_Host *instance = data;
@@ -37,43 +46,71 @@ static irqreturn_t a3000_intr(int irq, void *data)
spin_unlock_irqrestore(instance->host_lock, flags);
return IRQ_HANDLED;
}
- pr_warning("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status);
+ pr_warn("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status);
return IRQ_NONE;
}
static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
{
+ struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
+ unsigned long len = scsi_pointer->this_residual;
struct Scsi_Host *instance = cmd->device->host;
struct a3000_hostdata *hdata = shost_priv(instance);
struct WD33C93_hostdata *wh = &hdata->wh;
struct a3000_scsiregs *regs = hdata->regs;
unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
- unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+ dma_addr_t addr;
+
+ addr = dma_map_single(hdata->dev, scsi_pointer->ptr,
+ len, DMA_DIR(dir_in));
+ if (dma_mapping_error(hdata->dev, addr)) {
+ dev_warn(hdata->dev, "cannot map SCSI data block %p\n",
+ scsi_pointer->ptr);
+ return 1;
+ }
+ scsi_pointer->dma_handle = addr;
/*
* if the physical address has the wrong alignment, or if
* physical address is bad, or if it is a write and at the
* end of a physical memory chunk, then allocate a bounce
* buffer
+ * MSch 20220629 - only wrong alignment tested - bounce
+ * buffer returned by kmalloc is guaranteed to be aligned
*/
if (addr & A3000_XFER_MASK) {
- wh->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff;
+ WARN_ONCE(1, "Invalid alignment for DMA!");
+ /* drop useless mapping */
+ dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
+ scsi_pointer->this_residual,
+ DMA_DIR(dir_in));
+
+ wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff;
wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len,
GFP_KERNEL);
/* can't allocate memory; use PIO */
if (!wh->dma_bounce_buffer) {
wh->dma_bounce_len = 0;
+ scsi_pointer->dma_handle = (dma_addr_t) NULL;
return 1;
}
if (!dir_in) {
/* copy to bounce buffer for a write */
- memcpy(wh->dma_bounce_buffer, cmd->SCp.ptr,
- cmd->SCp.this_residual);
+ memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr,
+ scsi_pointer->this_residual);
}
- addr = virt_to_bus(wh->dma_bounce_buffer);
+ addr = dma_map_single(hdata->dev, scsi_pointer->ptr,
+ len, DMA_DIR(dir_in));
+ if (dma_mapping_error(hdata->dev, addr)) {
+ dev_warn(hdata->dev,
+ "cannot map SCSI data block %p\n",
+ scsi_pointer->ptr);
+ return 1;
+ }
+ scsi_pointer->dma_handle = addr;
}
/* setup dma direction */
@@ -88,13 +125,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
/* setup DMA *physical* address */
regs->ACR = addr;
- if (dir_in) {
- /* invalidate any cache */
- cache_clear(addr, cmd->SCp.this_residual);
- } else {
- /* push any dirty cache */
- cache_push(addr, cmd->SCp.this_residual);
- }
+ /* no more cache flush here - dma_map_single() takes care */
/* start DMA */
mb(); /* make sure setup is completed */
@@ -108,6 +139,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
int status)
{
+ struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt);
struct a3000_hostdata *hdata = shost_priv(instance);
struct WD33C93_hostdata *wh = &hdata->wh;
struct a3000_scsiregs *regs = hdata->regs;
@@ -144,12 +176,16 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
regs->CNTR = CNTR_PDMD | CNTR_INTEN;
mb(); /* make sure CNTR is updated before next IO */
+ dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
+ scsi_pointer->this_residual,
+ DMA_DIR(wh->dma_dir));
+
/* copy from a bounce buffer, if necessary */
if (status && wh->dma_bounce_buffer) {
if (SCpnt) {
if (wh->dma_dir && SCpnt)
- memcpy(SCpnt->SCp.ptr, wh->dma_bounce_buffer,
- SCpnt->SCp.this_residual);
+ memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer,
+ scsi_pointer->this_residual);
kfree(wh->dma_bounce_buffer);
wh->dma_bounce_buffer = NULL;
wh->dma_bounce_len = 0;
@@ -161,36 +197,20 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
}
}
-static int a3000_bus_reset(struct scsi_cmnd *cmd)
-{
- struct Scsi_Host *instance = cmd->device->host;
-
- /* FIXME perform bus-specific reset */
-
- /* FIXME 2: kill this entire function, which should
- cause mid-layer to call wd33c93_host_reset anyway? */
-
- spin_lock_irq(instance->host_lock);
- wd33c93_host_reset(cmd);
- spin_unlock_irq(instance->host_lock);
-
- return SUCCESS;
-}
-
-static struct scsi_host_template amiga_a3000_scsi_template = {
+static const struct scsi_host_template amiga_a3000_scsi_template = {
.module = THIS_MODULE,
.name = "Amiga 3000 built-in SCSI",
- .proc_info = wd33c93_proc_info,
+ .show_info = wd33c93_show_info,
+ .write_info = wd33c93_write_info,
.proc_name = "A3000",
.queuecommand = wd33c93_queuecommand,
.eh_abort_handler = wd33c93_abort,
- .eh_bus_reset_handler = a3000_bus_reset,
.eh_host_reset_handler = wd33c93_host_reset,
.can_queue = CAN_QUEUE,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING
+ .cmd_size = sizeof(struct scsi_pointer),
};
static int __init amiga_a3000_scsi_probe(struct platform_device *pdev)
@@ -202,6 +222,11 @@ static int __init amiga_a3000_scsi_probe(struct platform_device *pdev)
wd33c93_regs wdregs;
struct a3000_hostdata *hdata;
+ if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) {
+ dev_warn(&pdev->dev, "cannot use 32 bit DMA\n");
+ return -ENODEV;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
@@ -218,13 +243,14 @@ static int __init amiga_a3000_scsi_probe(struct platform_device *pdev)
instance->irq = IRQ_AMIGA_PORTS;
- regs = (struct a3000_scsiregs *)ZTWO_VADDR(res->start);
+ regs = ZTWO_VADDR(res->start);
regs->DAWR = DAWR_A3000;
wdregs.SASR = &regs->SASR;
wdregs.SCMD = &regs->SCMD;
hdata = shost_priv(instance);
+ hdata->dev = &pdev->dev;
hdata->wh.no_sync = 0xff;
hdata->wh.fast = 0;
hdata->wh.dma_mode = CTRL_DMA;
@@ -256,7 +282,7 @@ fail_alloc:
return error;
}
-static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev)
+static void __exit amiga_a3000_scsi_remove(struct platform_device *pdev)
{
struct Scsi_Host *instance = platform_get_drvdata(pdev);
struct a3000_hostdata *hdata = shost_priv(instance);
@@ -267,29 +293,22 @@ static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev)
free_irq(IRQ_AMIGA_PORTS, instance);
scsi_host_put(instance);
release_mem_region(res->start, resource_size(res));
- return 0;
}
-static struct platform_driver amiga_a3000_scsi_driver = {
+/*
+ * amiga_a3000_scsi_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver amiga_a3000_scsi_driver __refdata = {
.remove = __exit_p(amiga_a3000_scsi_remove),
- .driver = {
- .name = "amiga-a3000-scsi",
- .owner = THIS_MODULE,
+ .driver = {
+ .name = "amiga-a3000-scsi",
},
};
-static int __init amiga_a3000_scsi_init(void)
-{
- return platform_driver_probe(&amiga_a3000_scsi_driver,
- amiga_a3000_scsi_probe);
-}
-module_init(amiga_a3000_scsi_init);
-
-static void __exit amiga_a3000_scsi_exit(void)
-{
- platform_driver_unregister(&amiga_a3000_scsi_driver);
-}
-module_exit(amiga_a3000_scsi_exit);
+module_platform_driver_probe(amiga_a3000_scsi_driver, amiga_a3000_scsi_probe);
MODULE_DESCRIPTION("Amiga 3000 built-in SCSI");
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/a3000.h b/drivers/scsi/a3000.h
index 49db4a335aab..5cb3e7535281 100644
--- a/drivers/scsi/a3000.h
+++ b/drivers/scsi/a3000.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef A3000_H
#define A3000_H
diff --git a/drivers/scsi/a4000t.c b/drivers/scsi/a4000t.c
index 23c76f41883c..75b43047a155 100644
--- a/drivers/scsi/a4000t.c
+++ b/drivers/scsi/a4000t.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Detection routine for the NCR53c710 based Amiga SCSI Controllers for Linux.
* Amiga Technologies A4000T SCSI controller.
@@ -56,7 +57,7 @@ static int __init amiga_a4000t_scsi_probe(struct platform_device *pdev)
scsi_addr = res->start + A4000T_SCSI_OFFSET;
/* Fill in the required pieces of hostdata */
- hostdata->base = (void __iomem *)ZTWO_VADDR(scsi_addr);
+ hostdata->base = ZTWO_VADDR(scsi_addr);
hostdata->clock = 50;
hostdata->chip710 = 1;
hostdata->dmode_extra = DMODE_FC2;
@@ -94,7 +95,7 @@ static int __init amiga_a4000t_scsi_probe(struct platform_device *pdev)
return -ENODEV;
}
-static int __exit amiga_a4000t_scsi_remove(struct platform_device *pdev)
+static void __exit amiga_a4000t_scsi_remove(struct platform_device *pdev)
{
struct Scsi_Host *host = platform_get_drvdata(pdev);
struct NCR_700_Host_Parameters *hostdata = shost_priv(host);
@@ -105,31 +106,22 @@ static int __exit amiga_a4000t_scsi_remove(struct platform_device *pdev)
kfree(hostdata);
free_irq(host->irq, host);
release_mem_region(res->start, resource_size(res));
- return 0;
}
-static struct platform_driver amiga_a4000t_scsi_driver = {
+/*
+ * amiga_a4000t_scsi_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver amiga_a4000t_scsi_driver __refdata = {
.remove = __exit_p(amiga_a4000t_scsi_remove),
- .driver = {
- .name = "amiga-a4000t-scsi",
- .owner = THIS_MODULE,
+ .driver = {
+ .name = "amiga-a4000t-scsi",
},
};
-static int __init amiga_a4000t_scsi_init(void)
-{
- return platform_driver_probe(&amiga_a4000t_scsi_driver,
- amiga_a4000t_scsi_probe);
-}
-
-module_init(amiga_a4000t_scsi_init);
-
-static void __exit amiga_a4000t_scsi_exit(void)
-{
- platform_driver_unregister(&amiga_a4000t_scsi_driver);
-}
-
-module_exit(amiga_a4000t_scsi_exit);
+module_platform_driver_probe(amiga_a4000t_scsi_driver, amiga_a4000t_scsi_probe);
MODULE_AUTHOR("Alan Hourihane <alanh@fairlite.demon.co.uk> / "
"Kars de Jong <jongk@linux-m68k.org>");
diff --git a/drivers/scsi/aacraid/Makefile b/drivers/scsi/aacraid/Makefile
index 1bd9fd18f7f3..8f0eec682bb6 100644
--- a/drivers/scsi/aacraid/Makefile
+++ b/drivers/scsi/aacraid/Makefile
@@ -1,8 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Adaptec aacraid
obj-$(CONFIG_SCSI_AACRAID) := aacraid.o
aacraid-objs := linit.o aachba.o commctrl.o comminit.o commsup.o \
dpcsup.o rx.o sa.o rkt.o nark.o src.o
-
-ccflags-y := -Idrivers/scsi
diff --git a/drivers/scsi/aacraid/TODO b/drivers/scsi/aacraid/TODO
deleted file mode 100644
index 78dc863eff4f..000000000000
--- a/drivers/scsi/aacraid/TODO
+++ /dev/null
@@ -1,3 +0,0 @@
-o Testing
-o More testing
-o I/O size increase
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index 061995741444..0be719f38377 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,22 +7,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Module Name:
+ * aachba.c
*
+ * Abstract: Contains Interfaces to manage IOs.
*/
#include <linux/kernel.h>
@@ -32,8 +24,10 @@
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/blkdev.h>
-#include <asm/uaccess.h>
-#include <linux/highmem.h> /* For flush_kernel_dcache_page */
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include <linux/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -61,6 +55,7 @@
#define SENCODE_END_OF_DATA 0x00
#define SENCODE_BECOMING_READY 0x04
#define SENCODE_INIT_CMD_REQUIRED 0x04
+#define SENCODE_UNRECOVERED_READ_ERROR 0x11
#define SENCODE_PARAM_LIST_LENGTH_ERROR 0x1A
#define SENCODE_INVALID_COMMAND 0x20
#define SENCODE_LBA_OUT_OF_RANGE 0x21
@@ -110,6 +105,41 @@
#define BYTE2(x) (unsigned char)((x) >> 16)
#define BYTE3(x) (unsigned char)((x) >> 24)
+/* MODE_SENSE data format */
+typedef struct {
+ struct {
+ u8 data_length;
+ u8 med_type;
+ u8 dev_par;
+ u8 bd_length;
+ } __attribute__((packed)) hd;
+ struct {
+ u8 dens_code;
+ u8 block_count[3];
+ u8 reserved;
+ u8 block_length[3];
+ } __attribute__((packed)) bd;
+ u8 mpc_buf[3];
+} __attribute__((packed)) aac_modep_data;
+
+/* MODE_SENSE_10 data format */
+typedef struct {
+ struct {
+ u8 data_length[2];
+ u8 med_type;
+ u8 dev_par;
+ u8 rsrvd[2];
+ u8 bd_length[2];
+ } __attribute__((packed)) hd;
+ struct {
+ u8 dens_code;
+ u8 block_count[3];
+ u8 reserved;
+ u8 block_length[3];
+ } __attribute__((packed)) bd;
+ u8 mpc_buf[3];
+} __attribute__((packed)) aac_modep10_data;
+
/*------------------------------------------------------------------------------
* S T R U C T S / T Y P E D E F S
*----------------------------------------------------------------------------*/
@@ -127,14 +157,75 @@ struct inquiry_data {
u8 inqd_prl[4]; /* Product Revision Level */
};
+/* Added for VPD 0x83 */
+struct tvpd_id_descriptor_type_1 {
+ u8 codeset:4; /* VPD_CODE_SET */
+ u8 reserved:4;
+ u8 identifiertype:4; /* VPD_IDENTIFIER_TYPE */
+ u8 reserved2:4;
+ u8 reserved3;
+ u8 identifierlength;
+ u8 venid[8];
+ u8 productid[16];
+ u8 serialnumber[8]; /* SN in ASCII */
+
+};
+
+struct tvpd_id_descriptor_type_2 {
+ u8 codeset:4; /* VPD_CODE_SET */
+ u8 reserved:4;
+ u8 identifiertype:4; /* VPD_IDENTIFIER_TYPE */
+ u8 reserved2:4;
+ u8 reserved3;
+ u8 identifierlength;
+ struct teu64id {
+ u32 Serial;
+ /* The serial number supposed to be 40 bits,
+ * bit we only support 32, so make the last byte zero. */
+ u8 reserved;
+ u8 venid[3];
+ } eu64id;
+
+};
+
+struct tvpd_id_descriptor_type_3 {
+ u8 codeset : 4; /* VPD_CODE_SET */
+ u8 reserved : 4;
+ u8 identifiertype : 4; /* VPD_IDENTIFIER_TYPE */
+ u8 reserved2 : 4;
+ u8 reserved3;
+ u8 identifierlength;
+ u8 Identifier[16];
+};
+
+struct tvpd_page83 {
+ u8 DeviceType:5;
+ u8 DeviceTypeQualifier:3;
+ u8 PageCode;
+ u8 reserved;
+ u8 PageLength;
+ struct tvpd_id_descriptor_type_1 type1;
+ struct tvpd_id_descriptor_type_2 type2;
+ struct tvpd_id_descriptor_type_3 type3;
+};
+
/*
* M O D U L E G L O B A L S
*/
-static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* sgmap);
-static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg);
-static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg);
+static long aac_build_sg(struct scsi_cmnd *scsicmd, struct sgmap *sgmap);
+static long aac_build_sg64(struct scsi_cmnd *scsicmd, struct sgmap64 *psg);
+static long aac_build_sgraw(struct scsi_cmnd *scsicmd, struct sgmapraw *psg);
+static long aac_build_sgraw2(struct scsi_cmnd *scsicmd,
+ struct aac_raw_io2 *rio2, int sg_max);
+static long aac_build_sghba(struct scsi_cmnd *scsicmd,
+ struct aac_hba_cmd_req *hbacmd,
+ int sg_max, u64 sg_address);
+static int aac_convert_sgraw2(struct aac_raw_io2 *rio2,
+ int pages, int nseg, int nseg_new);
+static void aac_probe_container_scsi_done(struct scsi_cmnd *scsi_cmnd);
static int aac_send_srb_fib(struct scsi_cmnd* scsicmd);
+static int aac_send_hba_fib(struct scsi_cmnd *scsicmd);
#ifdef AAC_DETAILED_STATUS_INFO
static char *aac_get_status_string(u32 status);
#endif
@@ -150,7 +241,15 @@ int aac_msi;
int aac_commit = -1;
int startup_timeout = 180;
int aif_timeout = 120;
+int aac_sync_mode; /* Only Sync. transfer - disabled */
+static int aac_convert_sgl = 1; /* convert non-conformable s/g list - enabled */
+module_param(aac_sync_mode, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(aac_sync_mode, "Force sync. transfer mode"
+ " 0=off, 1=on");
+module_param(aac_convert_sgl, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(aac_convert_sgl, "Convert non-conformable s/g list"
+ " 0=off, 1=on");
module_param(nondasd, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices."
" 0=off, 1=on");
@@ -169,10 +268,10 @@ MODULE_PARM_DESC(commit, "Control whether a COMMIT_CONFIG is issued to the"
" 0=off, 1=on");
module_param_named(msi, aac_msi, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(msi, "IRQ handling."
- " 0=PIC(default), 1=MSI, 2=MSI-X(unsupported, uses MSI)");
+ " 0=PIC(default), 1=MSI, 2=MSI-X)");
module_param(startup_timeout, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(startup_timeout, "The duration of time in seconds to wait for"
- " adapter to have it's kernel up and\n"
+ " adapter to have its kernel up and\n"
"running. This is typically adjusted for large systems that do not"
" have a BIOS.");
module_param(aif_timeout, int, S_IRUGO|S_IWUSR);
@@ -181,13 +280,17 @@ MODULE_PARM_DESC(aif_timeout, "The duration of time in seconds to wait for"
"deregistering them. This is typically adjusted for heavily burdened"
" systems.");
+int aac_fib_dump;
+module_param(aac_fib_dump, int, 0644);
+MODULE_PARM_DESC(aac_fib_dump, "Dump controller fibs prior to IOP_RESET 0=off, 1=on");
+
int numacb = -1;
module_param(numacb, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(numacb, "Request a limit to the number of adapter control"
" blocks (FIB) allocated. Valid values are 512 and down. Default is"
" to use suggestion from Firmware.");
-int acbsize = -1;
+static int acbsize = -1;
module_param(acbsize, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB)"
" size. Valid values are 512, 2048, 4096 and 8192. Default is to use"
@@ -198,7 +301,7 @@ module_param(update_interval, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(update_interval, "Interval in seconds between time sync"
" updates issued to adapter.");
-int check_interval = 24 * 60 * 60;
+int check_interval = 60;
module_param(check_interval, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(check_interval, "Interval in seconds between adapter health"
" checks.");
@@ -218,7 +321,7 @@ int aac_reset_devices;
module_param_named(reset_devices, aac_reset_devices, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(reset_devices, "Force an adapter reset at initialization.");
-int aac_wwn = 1;
+static int aac_wwn = 1;
module_param_named(wwn, aac_wwn, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(wwn, "Select a WWN type for the arrays:\n"
"\t0 - Disable\n"
@@ -230,18 +333,16 @@ static inline int aac_valid_context(struct scsi_cmnd *scsicmd,
struct fib *fibptr) {
struct scsi_device *device;
- if (unlikely(!scsicmd || !scsicmd->scsi_done)) {
+ if (unlikely(!scsicmd)) {
dprintk((KERN_WARNING "aac_valid_context: scsi command corrupt\n"));
aac_fib_complete(fibptr);
- aac_fib_free(fibptr);
return 0;
}
- scsicmd->SCp.phase = AAC_OWNER_MIDLEVEL;
+ aac_priv(scsicmd)->owner = AAC_OWNER_MIDLEVEL;
device = scsicmd->device;
- if (unlikely(!device || !scsi_device_online(device))) {
+ if (unlikely(!device)) {
dprintk((KERN_WARNING "aac_valid_context: scsi device corrupt\n"));
aac_fib_complete(fibptr);
- aac_fib_free(fibptr);
return 0;
}
return 1;
@@ -249,7 +350,8 @@ static inline int aac_valid_context(struct scsi_cmnd *scsicmd,
/**
* aac_get_config_status - check the adapter configuration
- * @common: adapter to query
+ * @dev: aac driver data
+ * @commit_flag: force sending CT_COMMIT_CONFIG
*
* Query config status, and commit the configuration if needed.
*/
@@ -341,7 +443,7 @@ static void aac_expose_phy_device(struct scsi_cmnd *scsicmd)
/**
* aac_get_containers - list containers
- * @common: adapter to probe
+ * @dev: aac driver data
*
* Make a list of all containers on this controller
*/
@@ -372,6 +474,11 @@ int aac_get_containers(struct aac_dev *dev)
if (status >= 0) {
dresp = (struct aac_get_container_count_resp *)fib_data(fibptr);
maximum_num_containers = le32_to_cpu(dresp->ContainerSwitchEntries);
+ if (fibptr->dev->supplement_adapter_info.supported_options2 &
+ AAC_OPTION_SUPPORTED_240_VOLUMES) {
+ maximum_num_containers =
+ le32_to_cpu(dresp->MaxSimpleVolumes);
+ }
aac_fib_complete(fibptr);
}
/* FIB should be freed only after getting the response from the F/W */
@@ -380,16 +487,26 @@ int aac_get_containers(struct aac_dev *dev)
if (maximum_num_containers < MAXIMUM_NUM_CONTAINERS)
maximum_num_containers = MAXIMUM_NUM_CONTAINERS;
- fsa_dev_ptr = kzalloc(sizeof(*fsa_dev_ptr) * maximum_num_containers,
- GFP_KERNEL);
- if (!fsa_dev_ptr)
- return -ENOMEM;
+ if (dev->fsa_dev == NULL ||
+ dev->maximum_num_containers != maximum_num_containers) {
+
+ fsa_dev_ptr = dev->fsa_dev;
+
+ dev->fsa_dev = kcalloc(maximum_num_containers,
+ sizeof(*fsa_dev_ptr), GFP_KERNEL);
+
+ kfree(fsa_dev_ptr);
+ fsa_dev_ptr = NULL;
- dev->fsa_dev = fsa_dev_ptr;
- dev->maximum_num_containers = maximum_num_containers;
- for (index = 0; index < dev->maximum_num_containers; ) {
- fsa_dev_ptr[index].devname[0] = '\0';
+ if (!dev->fsa_dev)
+ return -ENOMEM;
+
+ dev->maximum_num_containers = maximum_num_containers;
+ }
+ for (index = 0; index < dev->maximum_num_containers; index++) {
+ dev->fsa_dev[index].devname[0] = '\0';
+ dev->fsa_dev[index].valid = 0;
status = aac_probe_container(dev, index);
@@ -397,16 +514,21 @@ int aac_get_containers(struct aac_dev *dev)
printk(KERN_WARNING "aac_get_containers: SendFIB failed.\n");
break;
}
-
- /*
- * If there are no more containers, then stop asking.
- */
- if (++index >= status)
- break;
}
return status;
}
+static void aac_scsi_done(struct scsi_cmnd *scmd)
+{
+ if (scmd->device->request_queue) {
+ /* SCSI command has been submitted by the SCSI mid-layer. */
+ scsi_done(scmd);
+ } else {
+ /* SCSI command has been submitted by aac_probe_container(). */
+ aac_probe_container_scsi_done(scmd);
+ }
+}
+
static void get_container_name_callback(void *context, struct fib * fibptr)
{
struct aac_get_name_resp * get_name_reply;
@@ -425,7 +547,9 @@ static void get_container_name_callback(void *context, struct fib * fibptr)
if ((le32_to_cpu(get_name_reply->status) == CT_OK)
&& (get_name_reply->data[0] != '\0')) {
char *sp = get_name_reply->data;
- sp[sizeof(((struct aac_get_name_resp *)NULL)->data)-1] = '\0';
+ int data_size = sizeof_field(struct aac_get_name_resp, data);
+
+ sp[data_size - 1] = '\0';
while (*sp == ' ')
++sp;
if (*sp) {
@@ -443,39 +567,41 @@ static void get_container_name_callback(void *context, struct fib * fibptr)
}
}
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
aac_fib_complete(fibptr);
- aac_fib_free(fibptr);
- scsicmd->scsi_done(scsicmd);
+ aac_scsi_done(scsicmd);
}
-/**
+/*
* aac_get_container_name - get container name, none blocking.
*/
static int aac_get_container_name(struct scsi_cmnd * scsicmd)
{
int status;
+ int data_size;
struct aac_get_name *dinfo;
struct fib * cmd_fibcontext;
struct aac_dev * dev;
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
- if (!(cmd_fibcontext = aac_fib_alloc(dev)))
- return -ENOMEM;
+ data_size = sizeof_field(struct aac_get_name_resp, data);
+
+ cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd);
aac_fib_init(cmd_fibcontext);
dinfo = (struct aac_get_name *) fib_data(cmd_fibcontext);
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
dinfo->command = cpu_to_le32(VM_ContainerConfig);
dinfo->type = cpu_to_le32(CT_READ_NAME);
dinfo->cid = cpu_to_le32(scmd_id(scsicmd));
- dinfo->count = cpu_to_le32(sizeof(((struct aac_get_name_resp *)NULL)->data));
+ dinfo->count = cpu_to_le32(data_size - 1);
status = aac_fib_send(ContainerCommand,
cmd_fibcontext,
- sizeof (struct aac_get_name),
+ sizeof(struct aac_get_name_resp),
FsaNormal,
0, 1,
(fib_callback)get_container_name_callback,
@@ -484,14 +610,11 @@ static int aac_get_container_name(struct scsi_cmnd * scsicmd)
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
printk(KERN_WARNING "aac_get_container_name: aac_fib_send failed with status: %d.\n", status);
aac_fib_complete(cmd_fibcontext);
- aac_fib_free(cmd_fibcontext);
return -1;
}
@@ -503,7 +626,7 @@ static int aac_probe_container_callback2(struct scsi_cmnd * scsicmd)
return aac_scsi_cmd(scsicmd);
scsicmd->result = DID_NO_CONNECT << 16;
- scsicmd->scsi_done(scsicmd);
+ aac_scsi_done(scsicmd);
return 0;
}
@@ -511,21 +634,38 @@ static void _aac_probe_container2(void * context, struct fib * fibptr)
{
struct fsa_dev_info *fsa_dev_ptr;
int (*callback)(struct scsi_cmnd *);
- struct scsi_cmnd * scsicmd = (struct scsi_cmnd *)context;
+ struct scsi_cmnd *scsicmd = context;
+ struct aac_cmd_priv *cmd_priv = aac_priv(scsicmd);
+ int i;
if (!aac_valid_context(scsicmd, fibptr))
return;
- scsicmd->SCp.Status = 0;
+ cmd_priv->status = 0;
fsa_dev_ptr = fibptr->dev->fsa_dev;
if (fsa_dev_ptr) {
struct aac_mount * dresp = (struct aac_mount *) fib_data(fibptr);
+ __le32 sup_options2;
+
fsa_dev_ptr += scmd_id(scsicmd);
+ sup_options2 =
+ fibptr->dev->supplement_adapter_info.supported_options2;
if ((le32_to_cpu(dresp->status) == ST_OK) &&
(le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) &&
(le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) {
+ if (!(sup_options2 & AAC_OPTION_VARIABLE_BLOCK_SIZE)) {
+ dresp->mnt[0].fileinfo.bdevinfo.block_size = 0x200;
+ fsa_dev_ptr->block_size = 0x200;
+ } else {
+ fsa_dev_ptr->block_size =
+ le32_to_cpu(dresp->mnt[0].fileinfo.bdevinfo.block_size);
+ }
+ for (i = 0; i < 16; i++)
+ fsa_dev_ptr->identifier[i] =
+ dresp->mnt[0].fileinfo.bdevinfo
+ .identifier[i];
fsa_dev_ptr->valid = 1;
/* sense_key holds the current state of the spin-up */
if (dresp->mnt[0].state & cpu_to_le32(FSCS_NOT_READY))
@@ -540,12 +680,12 @@ static void _aac_probe_container2(void * context, struct fib * fibptr)
}
if ((fsa_dev_ptr->valid & 1) == 0)
fsa_dev_ptr->valid = 0;
- scsicmd->SCp.Status = le32_to_cpu(dresp->count);
+ cmd_priv->status = le32_to_cpu(dresp->count);
}
aac_fib_complete(fibptr);
aac_fib_free(fibptr);
- callback = (int (*)(struct scsi_cmnd *))(scsicmd->SCp.ptr);
- scsicmd->SCp.ptr = NULL;
+ callback = cmd_priv->callback;
+ cmd_priv->callback = NULL;
(*callback)(scsicmd);
return;
}
@@ -558,11 +698,13 @@ static void _aac_probe_container1(void * context, struct fib * fibptr)
int status;
dresp = (struct aac_mount *) fib_data(fibptr);
- dresp->mnt[0].capacityhigh = 0;
- if ((le32_to_cpu(dresp->status) != ST_OK) ||
- (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE)) {
- _aac_probe_container2(context, fibptr);
- return;
+ if (!aac_supports_2T(fibptr->dev)) {
+ dresp->mnt[0].capacityhigh = 0;
+ if ((le32_to_cpu(dresp->status) == ST_OK) &&
+ (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE)) {
+ _aac_probe_container2(context, fibptr);
+ return;
+ }
}
scsicmd = (struct scsi_cmnd *) context;
@@ -573,9 +715,15 @@ static void _aac_probe_container1(void * context, struct fib * fibptr)
dinfo = (struct aac_query_mount *)fib_data(fibptr);
- dinfo->command = cpu_to_le32(VM_NameServe64);
+ if (fibptr->dev->supplement_adapter_info.supported_options2 &
+ AAC_OPTION_VARIABLE_BLOCK_SIZE)
+ dinfo->command = cpu_to_le32(VM_NameServeAllBlk);
+ else
+ dinfo->command = cpu_to_le32(VM_NameServe64);
+
dinfo->count = cpu_to_le32(scmd_id(scsicmd));
dinfo->type = cpu_to_le32(FT_FILESYS);
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
status = aac_fib_send(ContainerCommand,
fibptr,
@@ -587,9 +735,7 @@ static void _aac_probe_container1(void * context, struct fib * fibptr)
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS)
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
- else if (status < 0) {
+ if (status < 0 && status != -EINPROGRESS) {
/* Inherit results from VM_NameServe, if any */
dresp->status = cpu_to_le32(ST_OK);
_aac_probe_container2(context, fibptr);
@@ -598,6 +744,7 @@ static void _aac_probe_container1(void * context, struct fib * fibptr)
static int _aac_probe_container(struct scsi_cmnd * scsicmd, int (*callback)(struct scsi_cmnd *))
{
+ struct aac_cmd_priv *cmd_priv = aac_priv(scsicmd);
struct fib * fibptr;
int status = -ENOMEM;
@@ -608,10 +755,16 @@ static int _aac_probe_container(struct scsi_cmnd * scsicmd, int (*callback)(stru
dinfo = (struct aac_query_mount *)fib_data(fibptr);
- dinfo->command = cpu_to_le32(VM_NameServe);
+ if (fibptr->dev->supplement_adapter_info.supported_options2 &
+ AAC_OPTION_VARIABLE_BLOCK_SIZE)
+ dinfo->command = cpu_to_le32(VM_NameServeAllBlk);
+ else
+ dinfo->command = cpu_to_le32(VM_NameServe);
+
dinfo->count = cpu_to_le32(scmd_id(scsicmd));
dinfo->type = cpu_to_le32(FT_FILESYS);
- scsicmd->SCp.ptr = (char *)callback;
+ cmd_priv->callback = callback;
+ cmd_priv->owner = AAC_OWNER_FIRMWARE;
status = aac_fib_send(ContainerCommand,
fibptr,
@@ -623,12 +776,11 @@ static int _aac_probe_container(struct scsi_cmnd * scsicmd, int (*callback)(stru
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
+
if (status < 0) {
- scsicmd->SCp.ptr = NULL;
+ cmd_priv->callback = NULL;
aac_fib_complete(fibptr);
aac_fib_free(fibptr);
}
@@ -647,9 +799,8 @@ static int _aac_probe_container(struct scsi_cmnd * scsicmd, int (*callback)(stru
}
/**
- * aac_probe_container - query a logical volume
- * @dev: device to query
- * @cid: container identifier
+ * aac_probe_container_callback1 - query a logical volume
+ * @scsicmd: the scsi command block
*
* Queries the controller about the given volume. The volume information
* is updated in the struct fsa_dev_info structure rather than returned.
@@ -660,10 +811,16 @@ static int aac_probe_container_callback1(struct scsi_cmnd * scsicmd)
return 0;
}
+static void aac_probe_container_scsi_done(struct scsi_cmnd *scsi_cmnd)
+{
+ aac_probe_container_callback1(scsi_cmnd);
+}
+
int aac_probe_container(struct aac_dev *dev, int cid)
{
- struct scsi_cmnd *scsicmd = kmalloc(sizeof(*scsicmd), GFP_KERNEL);
- struct scsi_device *scsidev = kmalloc(sizeof(*scsidev), GFP_KERNEL);
+ struct aac_cmd_priv *cmd_priv;
+ struct scsi_cmnd *scsicmd = kzalloc(sizeof(*scsicmd) + sizeof(*cmd_priv), GFP_KERNEL);
+ struct scsi_device *scsidev = kzalloc(sizeof(*scsidev), GFP_KERNEL);
int status;
if (!scsicmd || !scsidev) {
@@ -671,8 +828,6 @@ int aac_probe_container(struct aac_dev *dev, int cid)
kfree(scsidev);
return -ENOMEM;
}
- scsicmd->list.next = NULL;
- scsicmd->scsi_done = (void (*)(struct scsi_cmnd*))aac_probe_container_callback1;
scsicmd->device = scsidev;
scsidev->sdev_state = 0;
@@ -683,7 +838,8 @@ int aac_probe_container(struct aac_dev *dev, int cid)
while (scsicmd->device == scsidev)
schedule();
kfree(scsidev);
- status = scsicmd->SCp.Status;
+ cmd_priv = aac_priv(scsicmd);
+ status = cmd_priv->status;
kfree(scsicmd);
return status;
}
@@ -696,7 +852,7 @@ struct scsi_inq {
};
/**
- * InqStrCopy - string merge
+ * inqstrcpy - string merge
* @a: string to copy from
* @b: string to copy to
*
@@ -754,13 +910,22 @@ char * get_container_type(unsigned tindex)
static void setinqstr(struct aac_dev *dev, void *data, int tindex)
{
struct scsi_inq *str;
+ struct aac_supplement_adapter_info *sup_adap_info;
+ sup_adap_info = &dev->supplement_adapter_info;
str = (struct scsi_inq *)(data); /* cast data to scsi inq block */
memset(str, ' ', sizeof(*str));
- if (dev->supplement_adapter_info.AdapterTypeText[0]) {
- char * cp = dev->supplement_adapter_info.AdapterTypeText;
+ if (sup_adap_info->adapter_type_text[0]) {
int c;
+ char *cp;
+ char *cname = kmemdup(sup_adap_info->adapter_type_text,
+ sizeof(sup_adap_info->adapter_type_text),
+ GFP_ATOMIC);
+ if (!cname)
+ return;
+
+ cp = cname;
if ((cp[0] == 'A') && (cp[1] == 'O') && (cp[2] == 'C'))
inqstrcpy("SMC", str->vid);
else {
@@ -769,8 +934,7 @@ static void setinqstr(struct aac_dev *dev, void *data, int tindex)
++cp;
c = *cp;
*cp = '\0';
- inqstrcpy (dev->supplement_adapter_info.AdapterTypeText,
- str->vid);
+ inqstrcpy(cname, str->vid);
*cp = c;
while (*cp && *cp != ' ')
++cp;
@@ -778,14 +942,11 @@ static void setinqstr(struct aac_dev *dev, void *data, int tindex)
while (*cp == ' ')
++cp;
/* last six chars reserved for vol type */
- c = 0;
- if (strlen(cp) > sizeof(str->pid)) {
- c = cp[sizeof(str->pid)];
+ if (strlen(cp) > sizeof(str->pid))
cp[sizeof(str->pid)] = '\0';
- }
inqstrcpy (cp, str->pid);
- if (c)
- cp[sizeof(str->pid)] = c;
+
+ kfree(cname);
} else {
struct aac_driver_ident *mp = aac_get_driver_ident(dev->cardtype);
@@ -808,6 +969,28 @@ static void setinqstr(struct aac_dev *dev, void *data, int tindex)
inqstrcpy ("V1.0", str->prl);
}
+static void build_vpd83_type3(struct tvpd_page83 *vpdpage83data,
+ struct aac_dev *dev, struct scsi_cmnd *scsicmd)
+{
+ int container;
+
+ vpdpage83data->type3.codeset = 1;
+ vpdpage83data->type3.identifiertype = 3;
+ vpdpage83data->type3.identifierlength = sizeof(vpdpage83data->type3)
+ - 4;
+
+ for (container = 0; container < dev->maximum_num_containers;
+ container++) {
+
+ if (scmd_id(scsicmd) == container) {
+ memcpy(vpdpage83data->type3.Identifier,
+ dev->fsa_dev[container].identifier,
+ 16);
+ break;
+ }
+ }
+}
+
static void get_container_serial_callback(void *context, struct fib * fibptr)
{
struct aac_get_serial_resp * get_serial_reply;
@@ -822,24 +1005,114 @@ static void get_container_serial_callback(void *context, struct fib * fibptr)
get_serial_reply = (struct aac_get_serial_resp *) fib_data(fibptr);
/* Failure is irrelevant, using default value instead */
if (le32_to_cpu(get_serial_reply->status) == CT_OK) {
- char sp[13];
- /* EVPD bit set */
- sp[0] = INQD_PDT_DA;
- sp[1] = scsicmd->cmnd[2];
- sp[2] = 0;
- sp[3] = snprintf(sp+4, sizeof(sp)-4, "%08X",
- le32_to_cpu(get_serial_reply->uid));
- scsi_sg_copy_from_buffer(scsicmd, sp, sizeof(sp));
+ /*Check to see if it's for VPD 0x83 or 0x80 */
+ if (scsicmd->cmnd[2] == 0x83) {
+ /* vpd page 0x83 - Device Identification Page */
+ struct aac_dev *dev;
+ int i;
+ struct tvpd_page83 vpdpage83data;
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+
+ memset(((u8 *)&vpdpage83data), 0,
+ sizeof(vpdpage83data));
+
+ /* DIRECT_ACCESS_DEVIC */
+ vpdpage83data.DeviceType = 0;
+ /* DEVICE_CONNECTED */
+ vpdpage83data.DeviceTypeQualifier = 0;
+ /* VPD_DEVICE_IDENTIFIERS */
+ vpdpage83data.PageCode = 0x83;
+ vpdpage83data.reserved = 0;
+ vpdpage83data.PageLength =
+ sizeof(vpdpage83data.type1) +
+ sizeof(vpdpage83data.type2);
+
+ /* VPD 83 Type 3 is not supported for ARC */
+ if (dev->sa_firmware)
+ vpdpage83data.PageLength +=
+ sizeof(vpdpage83data.type3);
+
+ /* T10 Vendor Identifier Field Format */
+ /* VpdcodesetAscii */
+ vpdpage83data.type1.codeset = 2;
+ /* VpdIdentifierTypeVendorId */
+ vpdpage83data.type1.identifiertype = 1;
+ vpdpage83data.type1.identifierlength =
+ sizeof(vpdpage83data.type1) - 4;
+
+ /* "ADAPTEC " for adaptec */
+ memcpy(vpdpage83data.type1.venid,
+ "ADAPTEC ",
+ sizeof(vpdpage83data.type1.venid));
+ memcpy(vpdpage83data.type1.productid,
+ "ARRAY ",
+ sizeof(
+ vpdpage83data.type1.productid));
+
+ /* Convert to ascii based serial number.
+ * The LSB is the end.
+ */
+ for (i = 0; i < 8; i++) {
+ u8 temp =
+ (u8)((get_serial_reply->uid >> ((7 - i) * 4)) & 0xF);
+ if (temp > 0x9) {
+ vpdpage83data.type1.serialnumber[i] =
+ 'A' + (temp - 0xA);
+ } else {
+ vpdpage83data.type1.serialnumber[i] =
+ '0' + temp;
+ }
+ }
+
+ /* VpdCodeSetBinary */
+ vpdpage83data.type2.codeset = 1;
+ /* VpdidentifiertypeEUI64 */
+ vpdpage83data.type2.identifiertype = 2;
+ vpdpage83data.type2.identifierlength =
+ sizeof(vpdpage83data.type2) - 4;
+
+ vpdpage83data.type2.eu64id.venid[0] = 0xD0;
+ vpdpage83data.type2.eu64id.venid[1] = 0;
+ vpdpage83data.type2.eu64id.venid[2] = 0;
+
+ vpdpage83data.type2.eu64id.Serial =
+ get_serial_reply->uid;
+ vpdpage83data.type2.eu64id.reserved = 0;
+
+ /*
+ * VpdIdentifierTypeFCPHName
+ * VPD 0x83 Type 3 not supported for ARC
+ */
+ if (dev->sa_firmware) {
+ build_vpd83_type3(&vpdpage83data,
+ dev, scsicmd);
+ }
+
+ /* Move the inquiry data to the response buffer. */
+ scsi_sg_copy_from_buffer(scsicmd, &vpdpage83data,
+ sizeof(vpdpage83data));
+ } else {
+ /* It must be for VPD 0x80 */
+ char sp[13];
+ /* EVPD bit set */
+ sp[0] = INQD_PDT_DA;
+ sp[1] = scsicmd->cmnd[2];
+ sp[2] = 0;
+ sp[3] = scnprintf(sp+4, sizeof(sp)-4, "%08X",
+ le32_to_cpu(get_serial_reply->uid));
+ scsi_sg_copy_from_buffer(scsicmd, sp,
+ sizeof(sp));
+ }
}
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
aac_fib_complete(fibptr);
- aac_fib_free(fibptr);
- scsicmd->scsi_done(scsicmd);
+ aac_scsi_done(scsicmd);
}
-/**
+/*
* aac_get_container_serial - get container serial, none blocking.
*/
static int aac_get_container_serial(struct scsi_cmnd * scsicmd)
@@ -851,8 +1124,7 @@ static int aac_get_container_serial(struct scsi_cmnd * scsicmd)
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
- if (!(cmd_fibcontext = aac_fib_alloc(dev)))
- return -ENOMEM;
+ cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd);
aac_fib_init(cmd_fibcontext);
dinfo = (struct aac_get_serial *) fib_data(cmd_fibcontext);
@@ -860,10 +1132,11 @@ static int aac_get_container_serial(struct scsi_cmnd * scsicmd)
dinfo->command = cpu_to_le32(VM_ContainerConfig);
dinfo->type = cpu_to_le32(CT_CID_TO_32BITS_UID);
dinfo->cid = cpu_to_le32(scmd_id(scsicmd));
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
status = aac_fib_send(ContainerCommand,
cmd_fibcontext,
- sizeof (struct aac_get_serial),
+ sizeof(struct aac_get_serial_resp),
FsaNormal,
0, 1,
(fib_callback) get_container_serial_callback,
@@ -872,14 +1145,11 @@ static int aac_get_container_serial(struct scsi_cmnd * scsicmd)
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
printk(KERN_WARNING "aac_get_container_serial: aac_fib_send failed with status: %d.\n", status);
aac_fib_complete(cmd_fibcontext);
- aac_fib_free(cmd_fibcontext);
return -1;
}
@@ -899,8 +1169,8 @@ static int setinqserial(struct aac_dev *dev, void *data, int cid)
/*
* This breaks array migration.
*/
- return snprintf((char *)(data), sizeof(struct scsi_inq) - 4, "%08X%02X",
- le32_to_cpu(dev->adapter_info.serial[0]), cid);
+ return scnprintf((char *)(data), sizeof(struct scsi_inq) - 4, "%08X%02X",
+ le32_to_cpu(dev->adapter_info.serial[0]), cid);
}
static inline void set_sense(struct sense_data *sense_data, u8 sense_key,
@@ -935,15 +1205,14 @@ static int aac_bounds_32(struct aac_dev * dev, struct scsi_cmnd * cmd, u64 lba)
if (lba & 0xffffffff00000000LL) {
int cid = scmd_id(cmd);
dprintk((KERN_DEBUG "aacraid: Illegal lba\n"));
- cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_CHECK_CONDITION;
+ cmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
memcpy(cmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
- cmd->scsi_done(cmd);
+ aac_scsi_done(cmd);
return 1;
}
return 0;
@@ -958,25 +1227,54 @@ static void io_callback(void *context, struct fib * fibptr);
static int aac_read_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count)
{
- u16 fibsize;
- struct aac_raw_io *readcmd;
+ struct aac_dev *dev = fib->dev;
+ u16 fibsize, command;
+ long ret;
+
aac_fib_init(fib);
- readcmd = (struct aac_raw_io *) fib_data(fib);
- readcmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff));
- readcmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
- readcmd->count = cpu_to_le32(count<<9);
- readcmd->cid = cpu_to_le16(scmd_id(cmd));
- readcmd->flags = cpu_to_le16(IO_TYPE_READ);
- readcmd->bpTotal = 0;
- readcmd->bpComplete = 0;
+ if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE2 ||
+ dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) &&
+ !dev->sync_mode) {
+ struct aac_raw_io2 *readcmd2;
+ readcmd2 = (struct aac_raw_io2 *) fib_data(fib);
+ memset(readcmd2, 0, sizeof(struct aac_raw_io2));
+ readcmd2->blockLow = cpu_to_le32((u32)(lba&0xffffffff));
+ readcmd2->blockHigh = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
+ readcmd2->byteCount = cpu_to_le32(count *
+ dev->fsa_dev[scmd_id(cmd)].block_size);
+ readcmd2->cid = cpu_to_le16(scmd_id(cmd));
+ readcmd2->flags = cpu_to_le16(RIO2_IO_TYPE_READ);
+ ret = aac_build_sgraw2(cmd, readcmd2,
+ dev->scsi_host_ptr->sg_tablesize);
+ if (ret < 0)
+ return ret;
+ command = ContainerRawIo2;
+ fibsize = struct_size(readcmd2, sge,
+ le32_to_cpu(readcmd2->sgeCnt));
+ } else {
+ struct aac_raw_io *readcmd;
+ readcmd = (struct aac_raw_io *) fib_data(fib);
+ readcmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff));
+ readcmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
+ readcmd->count = cpu_to_le32(count *
+ dev->fsa_dev[scmd_id(cmd)].block_size);
+ readcmd->cid = cpu_to_le16(scmd_id(cmd));
+ readcmd->flags = cpu_to_le16(RIO_TYPE_READ);
+ readcmd->bpTotal = 0;
+ readcmd->bpComplete = 0;
+ ret = aac_build_sgraw(cmd, &readcmd->sg);
+ if (ret < 0)
+ return ret;
+ command = ContainerRawIo;
+ fibsize = sizeof(struct aac_raw_io) +
+ (le32_to_cpu(readcmd->sg.count) * sizeof(struct sgentryraw));
+ }
- aac_build_sgraw(cmd, &readcmd->sg);
- fibsize = sizeof(struct aac_raw_io) + ((le32_to_cpu(readcmd->sg.count) - 1) * sizeof (struct sgentryraw));
BUG_ON(fibsize > (fib->dev->max_fib_size - sizeof(struct aac_fibhdr)));
/*
* Now send the Fib to the adapter
*/
- return aac_fib_send(ContainerRawIo,
+ return aac_fib_send(command,
fib,
fibsize,
FsaNormal,
@@ -989,6 +1287,8 @@ static int aac_read_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u
{
u16 fibsize;
struct aac_read64 *readcmd;
+ long ret;
+
aac_fib_init(fib);
readcmd = (struct aac_read64 *) fib_data(fib);
readcmd->command = cpu_to_le32(VM_CtHostRead64);
@@ -998,9 +1298,11 @@ static int aac_read_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u
readcmd->pad = 0;
readcmd->flags = 0;
- aac_build_sg64(cmd, &readcmd->sg);
+ ret = aac_build_sg64(cmd, &readcmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_read64) +
- ((le32_to_cpu(readcmd->sg.count) - 1) *
+ (le32_to_cpu(readcmd->sg.count) *
sizeof (struct sgentry64));
BUG_ON (fibsize > (fib->dev->max_fib_size -
sizeof(struct aac_fibhdr)));
@@ -1020,16 +1322,22 @@ static int aac_read_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32
{
u16 fibsize;
struct aac_read *readcmd;
+ struct aac_dev *dev = fib->dev;
+ long ret;
+
aac_fib_init(fib);
readcmd = (struct aac_read *) fib_data(fib);
readcmd->command = cpu_to_le32(VM_CtBlockRead);
readcmd->cid = cpu_to_le32(scmd_id(cmd));
readcmd->block = cpu_to_le32((u32)(lba&0xffffffff));
- readcmd->count = cpu_to_le32(count * 512);
+ readcmd->count = cpu_to_le32(count *
+ dev->fsa_dev[scmd_id(cmd)].block_size);
- aac_build_sg(cmd, &readcmd->sg);
+ ret = aac_build_sg(cmd, &readcmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_read) +
- ((le32_to_cpu(readcmd->sg.count) - 1) *
+ (le32_to_cpu(readcmd->sg.count) *
sizeof (struct sgentry));
BUG_ON (fibsize > (fib->dev->max_fib_size -
sizeof(struct aac_fibhdr)));
@@ -1047,28 +1355,60 @@ static int aac_read_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32
static int aac_write_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count, int fua)
{
- u16 fibsize;
- struct aac_raw_io *writecmd;
+ struct aac_dev *dev = fib->dev;
+ u16 fibsize, command;
+ long ret;
+
aac_fib_init(fib);
- writecmd = (struct aac_raw_io *) fib_data(fib);
- writecmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff));
- writecmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
- writecmd->count = cpu_to_le32(count<<9);
- writecmd->cid = cpu_to_le16(scmd_id(cmd));
- writecmd->flags = (fua && ((aac_cache & 5) != 1) &&
- (((aac_cache & 5) != 5) || !fib->dev->cache_protected)) ?
- cpu_to_le16(IO_TYPE_WRITE|IO_SUREWRITE) :
- cpu_to_le16(IO_TYPE_WRITE);
- writecmd->bpTotal = 0;
- writecmd->bpComplete = 0;
-
- aac_build_sgraw(cmd, &writecmd->sg);
- fibsize = sizeof(struct aac_raw_io) + ((le32_to_cpu(writecmd->sg.count) - 1) * sizeof (struct sgentryraw));
+ if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE2 ||
+ dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) &&
+ !dev->sync_mode) {
+ struct aac_raw_io2 *writecmd2;
+ writecmd2 = (struct aac_raw_io2 *) fib_data(fib);
+ memset(writecmd2, 0, sizeof(struct aac_raw_io2));
+ writecmd2->blockLow = cpu_to_le32((u32)(lba&0xffffffff));
+ writecmd2->blockHigh = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
+ writecmd2->byteCount = cpu_to_le32(count *
+ dev->fsa_dev[scmd_id(cmd)].block_size);
+ writecmd2->cid = cpu_to_le16(scmd_id(cmd));
+ writecmd2->flags = (fua && ((aac_cache & 5) != 1) &&
+ (((aac_cache & 5) != 5) || !fib->dev->cache_protected)) ?
+ cpu_to_le16(RIO2_IO_TYPE_WRITE|RIO2_IO_SUREWRITE) :
+ cpu_to_le16(RIO2_IO_TYPE_WRITE);
+ ret = aac_build_sgraw2(cmd, writecmd2,
+ dev->scsi_host_ptr->sg_tablesize);
+ if (ret < 0)
+ return ret;
+ command = ContainerRawIo2;
+ fibsize = struct_size(writecmd2, sge,
+ le32_to_cpu(writecmd2->sgeCnt));
+ } else {
+ struct aac_raw_io *writecmd;
+ writecmd = (struct aac_raw_io *) fib_data(fib);
+ writecmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff));
+ writecmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
+ writecmd->count = cpu_to_le32(count *
+ dev->fsa_dev[scmd_id(cmd)].block_size);
+ writecmd->cid = cpu_to_le16(scmd_id(cmd));
+ writecmd->flags = (fua && ((aac_cache & 5) != 1) &&
+ (((aac_cache & 5) != 5) || !fib->dev->cache_protected)) ?
+ cpu_to_le16(RIO_TYPE_WRITE|RIO_SUREWRITE) :
+ cpu_to_le16(RIO_TYPE_WRITE);
+ writecmd->bpTotal = 0;
+ writecmd->bpComplete = 0;
+ ret = aac_build_sgraw(cmd, &writecmd->sg);
+ if (ret < 0)
+ return ret;
+ command = ContainerRawIo;
+ fibsize = sizeof(struct aac_raw_io) +
+ (le32_to_cpu(writecmd->sg.count) * sizeof(struct sgentryraw));
+ }
+
BUG_ON(fibsize > (fib->dev->max_fib_size - sizeof(struct aac_fibhdr)));
/*
* Now send the Fib to the adapter
*/
- return aac_fib_send(ContainerRawIo,
+ return aac_fib_send(command,
fib,
fibsize,
FsaNormal,
@@ -1081,6 +1421,8 @@ static int aac_write_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba,
{
u16 fibsize;
struct aac_write64 *writecmd;
+ long ret;
+
aac_fib_init(fib);
writecmd = (struct aac_write64 *) fib_data(fib);
writecmd->command = cpu_to_le32(VM_CtHostWrite64);
@@ -1090,9 +1432,11 @@ static int aac_write_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba,
writecmd->pad = 0;
writecmd->flags = 0;
- aac_build_sg64(cmd, &writecmd->sg);
+ ret = aac_build_sg64(cmd, &writecmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_write64) +
- ((le32_to_cpu(writecmd->sg.count) - 1) *
+ (le32_to_cpu(writecmd->sg.count) *
sizeof (struct sgentry64));
BUG_ON (fibsize > (fib->dev->max_fib_size -
sizeof(struct aac_fibhdr)));
@@ -1112,18 +1456,24 @@ static int aac_write_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u3
{
u16 fibsize;
struct aac_write *writecmd;
+ struct aac_dev *dev = fib->dev;
+ long ret;
+
aac_fib_init(fib);
writecmd = (struct aac_write *) fib_data(fib);
writecmd->command = cpu_to_le32(VM_CtBlockWrite);
writecmd->cid = cpu_to_le32(scmd_id(cmd));
writecmd->block = cpu_to_le32((u32)(lba&0xffffffff));
- writecmd->count = cpu_to_le32(count * 512);
+ writecmd->count = cpu_to_le32(count *
+ dev->fsa_dev[scmd_id(cmd)].block_size);
writecmd->sg.count = cpu_to_le32(1);
/* ->stable is not used - it did mean which type of write */
- aac_build_sg(cmd, &writecmd->sg);
+ ret = aac_build_sg(cmd, &writecmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_write) +
- ((le32_to_cpu(writecmd->sg.count) - 1) *
+ (le32_to_cpu(writecmd->sg.count) *
sizeof (struct sgentry));
BUG_ON (fibsize > (fib->dev->max_fib_size -
sizeof(struct aac_fibhdr)));
@@ -1144,6 +1494,7 @@ static struct aac_srb * aac_scsi_common(struct fib * fib, struct scsi_cmnd * cmd
struct aac_srb * srbcmd;
u32 flag;
u32 timeout;
+ struct aac_dev *dev = fib->dev;
aac_fib_init(fib);
switch(cmd->sc_data_direction){
@@ -1168,23 +1519,72 @@ static struct aac_srb * aac_scsi_common(struct fib * fib, struct scsi_cmnd * cmd
srbcmd->id = cpu_to_le32(scmd_id(cmd));
srbcmd->lun = cpu_to_le32(cmd->device->lun);
srbcmd->flags = cpu_to_le32(flag);
- timeout = cmd->request->timeout/HZ;
+ timeout = scsi_cmd_to_rq(cmd)->timeout / HZ;
if (timeout == 0)
- timeout = 1;
+ timeout = (dev->sa_firmware ? AAC_SA_TIMEOUT : AAC_ARC_TIMEOUT);
srbcmd->timeout = cpu_to_le32(timeout); // timeout in seconds
srbcmd->retry_limit = 0; /* Obsolete parameter */
srbcmd->cdb_size = cpu_to_le32(cmd->cmd_len);
return srbcmd;
}
+static struct aac_hba_cmd_req *aac_construct_hbacmd(struct fib *fib,
+ struct scsi_cmnd *cmd)
+{
+ struct aac_hba_cmd_req *hbacmd;
+ struct aac_dev *dev;
+ int bus, target;
+ u64 address;
+
+ dev = (struct aac_dev *)cmd->device->host->hostdata;
+
+ hbacmd = (struct aac_hba_cmd_req *)fib->hw_fib_va;
+ memset(hbacmd, 0, 96); /* sizeof(*hbacmd) is not necessary */
+ /* iu_type is a parameter of aac_hba_send */
+ switch (cmd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ hbacmd->byte1 = 2;
+ break;
+ case DMA_FROM_DEVICE:
+ case DMA_BIDIRECTIONAL:
+ hbacmd->byte1 = 1;
+ break;
+ case DMA_NONE:
+ default:
+ break;
+ }
+ hbacmd->lun[1] = cpu_to_le32(cmd->device->lun);
+
+ bus = aac_logical_to_phys(scmd_channel(cmd));
+ target = scmd_id(cmd);
+ hbacmd->it_nexus = dev->hba_map[bus][target].rmw_nexus;
+
+ /* we fill in reply_qid later in aac_src_deliver_message */
+ /* we fill in iu_type, request_id later in aac_hba_send */
+ /* we fill in emb_data_desc_count later in aac_build_sghba */
+
+ memcpy(hbacmd->cdb, cmd->cmnd, cmd->cmd_len);
+ hbacmd->data_length = cpu_to_le32(scsi_bufflen(cmd));
+
+ address = (u64)fib->hw_error_pa;
+ hbacmd->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
+ hbacmd->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
+ hbacmd->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
+
+ return hbacmd;
+}
+
static void aac_srb_callback(void *context, struct fib * fibptr);
static int aac_scsi_64(struct fib * fib, struct scsi_cmnd * cmd)
{
u16 fibsize;
struct aac_srb * srbcmd = aac_scsi_common(fib, cmd);
+ long ret;
- aac_build_sg64(cmd, (struct sgmap64*) &srbcmd->sg);
+ ret = aac_build_sg64(cmd, (struct sgmap64 *) &srbcmd->sg);
+ if (ret < 0)
+ return ret;
srbcmd->count = cpu_to_le32(scsi_bufflen(cmd));
memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
@@ -1192,9 +1592,9 @@ static int aac_scsi_64(struct fib * fib, struct scsi_cmnd * cmd)
/*
* Build Scatter/Gather list
*/
- fibsize = sizeof (struct aac_srb) - sizeof (struct sgentry) +
+ fibsize = sizeof(struct aac_srb) +
((le32_to_cpu(srbcmd->sg.count) & 0xff) *
- sizeof (struct sgentry64));
+ sizeof(struct sgentry64));
BUG_ON (fibsize > (fib->dev->max_fib_size -
sizeof(struct aac_fibhdr)));
@@ -1211,8 +1611,11 @@ static int aac_scsi_32(struct fib * fib, struct scsi_cmnd * cmd)
{
u16 fibsize;
struct aac_srb * srbcmd = aac_scsi_common(fib, cmd);
+ long ret;
- aac_build_sg(cmd, (struct sgmap*)&srbcmd->sg);
+ ret = aac_build_sg(cmd, (struct sgmap *)&srbcmd->sg);
+ if (ret < 0)
+ return ret;
srbcmd->count = cpu_to_le32(scsi_bufflen(cmd));
memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
@@ -1221,7 +1624,7 @@ static int aac_scsi_32(struct fib * fib, struct scsi_cmnd * cmd)
* Build Scatter/Gather list
*/
fibsize = sizeof (struct aac_srb) +
- (((le32_to_cpu(srbcmd->sg.count) & 0xff) - 1) *
+ ((le32_to_cpu(srbcmd->sg.count) & 0xff) *
sizeof (struct sgentry));
BUG_ON (fibsize > (fib->dev->max_fib_size -
sizeof(struct aac_fibhdr)));
@@ -1241,11 +1644,402 @@ static int aac_scsi_32_64(struct fib * fib, struct scsi_cmnd * cmd)
return aac_scsi_32(fib, cmd);
}
+static int aac_adapter_hba(struct fib *fib, struct scsi_cmnd *cmd)
+{
+ struct aac_hba_cmd_req *hbacmd = aac_construct_hbacmd(fib, cmd);
+ struct aac_dev *dev;
+ long ret;
+
+ dev = (struct aac_dev *)cmd->device->host->hostdata;
+
+ ret = aac_build_sghba(cmd, hbacmd,
+ dev->scsi_host_ptr->sg_tablesize, (u64)fib->hw_sgl_pa);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Now send the HBA command to the adapter
+ */
+ fib->hbacmd_size = 64 + le32_to_cpu(hbacmd->emb_data_desc_count) *
+ sizeof(struct aac_hba_sgl);
+
+ return aac_hba_send(HBA_IU_TYPE_SCSI_CMD_REQ, fib,
+ (fib_callback) aac_hba_callback,
+ (void *) cmd);
+}
+
+static int aac_send_safw_bmic_cmd(struct aac_dev *dev,
+ struct aac_srb_unit *srbu, void *xfer_buf, int xfer_len)
+{
+ struct fib *fibptr;
+ dma_addr_t addr;
+ int rcode;
+ int fibsize;
+ struct aac_srb *srb;
+ struct aac_srb_reply *srb_reply;
+ struct sgmap64 *sg64;
+ u32 vbus;
+ u32 vid;
+
+ if (!dev->sa_firmware)
+ return 0;
+
+ /* allocate FIB */
+ fibptr = aac_fib_alloc(dev);
+ if (!fibptr)
+ return -ENOMEM;
+
+ aac_fib_init(fibptr);
+ fibptr->hw_fib_va->header.XferState &=
+ ~cpu_to_le32(FastResponseCapable);
+
+ fibsize = sizeof(struct aac_srb) + sizeof(struct sgentry64);
+
+ /* allocate DMA buffer for response */
+ addr = dma_map_single(&dev->pdev->dev, xfer_buf, xfer_len,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&dev->pdev->dev, addr)) {
+ rcode = -ENOMEM;
+ goto fib_error;
+ }
+
+ srb = fib_data(fibptr);
+ memcpy(srb, &srbu->srb, sizeof(struct aac_srb));
+
+ vbus = (u32)le16_to_cpu(
+ dev->supplement_adapter_info.virt_device_bus);
+ vid = (u32)le16_to_cpu(
+ dev->supplement_adapter_info.virt_device_target);
+
+ /* set the common request fields */
+ srb->channel = cpu_to_le32(vbus);
+ srb->id = cpu_to_le32(vid);
+ srb->lun = 0;
+ srb->function = cpu_to_le32(SRBF_ExecuteScsi);
+ srb->timeout = 0;
+ srb->retry_limit = 0;
+ srb->cdb_size = cpu_to_le32(16);
+ srb->count = cpu_to_le32(xfer_len);
+
+ sg64 = (struct sgmap64 *)&srb->sg;
+ sg64->count = cpu_to_le32(1);
+ sg64->sg[0].addr[1] = cpu_to_le32(upper_32_bits(addr));
+ sg64->sg[0].addr[0] = cpu_to_le32(lower_32_bits(addr));
+ sg64->sg[0].count = cpu_to_le32(xfer_len);
+
+ /*
+ * Copy the updated data for other dumping or other usage if needed
+ */
+ memcpy(&srbu->srb, srb, sizeof(struct aac_srb));
+
+ /* issue request to the controller */
+ rcode = aac_fib_send(ScsiPortCommand64, fibptr, fibsize, FsaNormal,
+ 1, 1, NULL, NULL);
+
+ if (rcode == -ERESTARTSYS)
+ rcode = -ERESTART;
+
+ if (unlikely(rcode < 0))
+ goto bmic_error;
+
+ srb_reply = (struct aac_srb_reply *)fib_data(fibptr);
+ memcpy(&srbu->srb_reply, srb_reply, sizeof(struct aac_srb_reply));
+
+bmic_error:
+ dma_unmap_single(&dev->pdev->dev, addr, xfer_len, DMA_BIDIRECTIONAL);
+fib_error:
+ aac_fib_complete(fibptr);
+ aac_fib_free(fibptr);
+ return rcode;
+}
+
+static void aac_set_safw_target_qd(struct aac_dev *dev, int bus, int target)
+{
+
+ struct aac_ciss_identify_pd *identify_resp;
+
+ if (dev->hba_map[bus][target].devtype != AAC_DEVTYPE_NATIVE_RAW)
+ return;
+
+ identify_resp = dev->hba_map[bus][target].safw_identify_resp;
+ if (identify_resp == NULL) {
+ dev->hba_map[bus][target].qd_limit = 32;
+ return;
+ }
+
+ if (identify_resp->current_queue_depth_limit <= 0 ||
+ identify_resp->current_queue_depth_limit > 255)
+ dev->hba_map[bus][target].qd_limit = 32;
+ else
+ dev->hba_map[bus][target].qd_limit =
+ identify_resp->current_queue_depth_limit;
+}
+
+static int aac_issue_safw_bmic_identify(struct aac_dev *dev,
+ struct aac_ciss_identify_pd **identify_resp, u32 bus, u32 target)
+{
+ int rcode = -ENOMEM;
+ int datasize;
+ struct aac_srb_unit srbu;
+ struct aac_srb *srbcmd;
+ struct aac_ciss_identify_pd *identify_reply;
+
+ datasize = sizeof(struct aac_ciss_identify_pd);
+ identify_reply = kmalloc(datasize, GFP_KERNEL);
+ if (!identify_reply)
+ goto out;
+
+ memset(&srbu, 0, sizeof(struct aac_srb_unit));
+
+ srbcmd = &srbu.srb;
+ srbcmd->flags = cpu_to_le32(SRB_DataIn);
+ srbcmd->cdb[0] = 0x26;
+ srbcmd->cdb[2] = (u8)((AAC_MAX_LUN + target) & 0x00FF);
+ srbcmd->cdb[6] = CISS_IDENTIFY_PHYSICAL_DEVICE;
+
+ rcode = aac_send_safw_bmic_cmd(dev, &srbu, identify_reply, datasize);
+ if (unlikely(rcode < 0))
+ goto mem_free_all;
+
+ *identify_resp = identify_reply;
+
+out:
+ return rcode;
+mem_free_all:
+ kfree(identify_reply);
+ goto out;
+}
+
+static inline void aac_free_safw_ciss_luns(struct aac_dev *dev)
+{
+ kfree(dev->safw_phys_luns);
+ dev->safw_phys_luns = NULL;
+}
+
+/**
+ * aac_get_safw_ciss_luns() - Process topology change
+ * @dev: aac_dev structure
+ *
+ * Execute a CISS REPORT PHYS LUNS and process the results into
+ * the current hba_map.
+ */
+static int aac_get_safw_ciss_luns(struct aac_dev *dev)
+{
+ int rcode = -ENOMEM;
+ int datasize;
+ struct aac_srb *srbcmd;
+ struct aac_srb_unit srbu;
+ struct aac_ciss_phys_luns_resp *phys_luns;
+
+ datasize = sizeof(struct aac_ciss_phys_luns_resp) +
+ AAC_MAX_TARGETS * sizeof(struct _ciss_lun);
+ phys_luns = kmalloc(datasize, GFP_KERNEL);
+ if (phys_luns == NULL)
+ goto out;
+
+ memset(&srbu, 0, sizeof(struct aac_srb_unit));
+
+ srbcmd = &srbu.srb;
+ srbcmd->flags = cpu_to_le32(SRB_DataIn);
+ srbcmd->cdb[0] = CISS_REPORT_PHYSICAL_LUNS;
+ srbcmd->cdb[1] = 2; /* extended reporting */
+ srbcmd->cdb[8] = (u8)(datasize >> 8);
+ srbcmd->cdb[9] = (u8)(datasize);
+
+ rcode = aac_send_safw_bmic_cmd(dev, &srbu, phys_luns, datasize);
+ if (unlikely(rcode < 0))
+ goto mem_free_all;
+
+ if (phys_luns->resp_flag != 2) {
+ rcode = -ENOMSG;
+ goto mem_free_all;
+ }
+
+ dev->safw_phys_luns = phys_luns;
+
+out:
+ return rcode;
+mem_free_all:
+ kfree(phys_luns);
+ goto out;
+}
+
+static inline u32 aac_get_safw_phys_lun_count(struct aac_dev *dev)
+{
+ return get_unaligned_be32(&dev->safw_phys_luns->list_length[0])/24;
+}
+
+static inline u32 aac_get_safw_phys_bus(struct aac_dev *dev, int lun)
+{
+ return dev->safw_phys_luns->lun[lun].level2[1] & 0x3f;
+}
+
+static inline u32 aac_get_safw_phys_target(struct aac_dev *dev, int lun)
+{
+ return dev->safw_phys_luns->lun[lun].level2[0];
+}
+
+static inline u32 aac_get_safw_phys_expose_flag(struct aac_dev *dev, int lun)
+{
+ return dev->safw_phys_luns->lun[lun].bus >> 6;
+}
+
+static inline u32 aac_get_safw_phys_attribs(struct aac_dev *dev, int lun)
+{
+ return dev->safw_phys_luns->lun[lun].node_ident[9];
+}
+
+static inline u32 aac_get_safw_phys_nexus(struct aac_dev *dev, int lun)
+{
+ return *((u32 *)&dev->safw_phys_luns->lun[lun].node_ident[12]);
+}
+
+static inline void aac_free_safw_identify_resp(struct aac_dev *dev,
+ int bus, int target)
+{
+ kfree(dev->hba_map[bus][target].safw_identify_resp);
+ dev->hba_map[bus][target].safw_identify_resp = NULL;
+}
+
+static inline void aac_free_safw_all_identify_resp(struct aac_dev *dev,
+ int lun_count)
+{
+ int luns;
+ int i;
+ u32 bus;
+ u32 target;
+
+ luns = aac_get_safw_phys_lun_count(dev);
+
+ if (luns < lun_count)
+ lun_count = luns;
+ else if (lun_count < 0)
+ lun_count = luns;
+
+ for (i = 0; i < lun_count; i++) {
+ bus = aac_get_safw_phys_bus(dev, i);
+ target = aac_get_safw_phys_target(dev, i);
+
+ aac_free_safw_identify_resp(dev, bus, target);
+ }
+}
+
+static int aac_get_safw_attr_all_targets(struct aac_dev *dev)
+{
+ int i;
+ int rcode = 0;
+ u32 lun_count;
+ u32 bus;
+ u32 target;
+ struct aac_ciss_identify_pd *identify_resp = NULL;
+
+ lun_count = aac_get_safw_phys_lun_count(dev);
+
+ for (i = 0; i < lun_count; ++i) {
+
+ bus = aac_get_safw_phys_bus(dev, i);
+ target = aac_get_safw_phys_target(dev, i);
+
+ rcode = aac_issue_safw_bmic_identify(dev,
+ &identify_resp, bus, target);
+
+ if (unlikely(rcode < 0))
+ goto free_identify_resp;
+
+ dev->hba_map[bus][target].safw_identify_resp = identify_resp;
+ }
+
+out:
+ return rcode;
+free_identify_resp:
+ aac_free_safw_all_identify_resp(dev, i);
+ goto out;
+}
+
+/**
+ * aac_set_safw_attr_all_targets- update current hba map with data from FW
+ * @dev: aac_dev structure
+ *
+ * Update our hba map with the information gathered from the FW
+ */
+static void aac_set_safw_attr_all_targets(struct aac_dev *dev)
+{
+ /* ok and extended reporting */
+ u32 lun_count, nexus;
+ u32 i, bus, target;
+ u8 expose_flag, attribs;
+
+ lun_count = aac_get_safw_phys_lun_count(dev);
+
+ dev->scan_counter++;
+
+ for (i = 0; i < lun_count; ++i) {
+
+ bus = aac_get_safw_phys_bus(dev, i);
+ target = aac_get_safw_phys_target(dev, i);
+ expose_flag = aac_get_safw_phys_expose_flag(dev, i);
+ attribs = aac_get_safw_phys_attribs(dev, i);
+ nexus = aac_get_safw_phys_nexus(dev, i);
+
+ if (bus >= AAC_MAX_BUSES || target >= AAC_MAX_TARGETS)
+ continue;
+
+ if (expose_flag != 0) {
+ dev->hba_map[bus][target].devtype =
+ AAC_DEVTYPE_RAID_MEMBER;
+ continue;
+ }
+
+ if (nexus != 0 && (attribs & 8)) {
+ dev->hba_map[bus][target].devtype =
+ AAC_DEVTYPE_NATIVE_RAW;
+ dev->hba_map[bus][target].rmw_nexus =
+ nexus;
+ } else
+ dev->hba_map[bus][target].devtype =
+ AAC_DEVTYPE_ARC_RAW;
+
+ dev->hba_map[bus][target].scan_counter = dev->scan_counter;
+
+ aac_set_safw_target_qd(dev, bus, target);
+ }
+}
+
+static int aac_setup_safw_targets(struct aac_dev *dev)
+{
+ int rcode = 0;
+
+ rcode = aac_get_containers(dev);
+ if (unlikely(rcode < 0))
+ goto out;
+
+ rcode = aac_get_safw_ciss_luns(dev);
+ if (unlikely(rcode < 0))
+ goto out;
+
+ rcode = aac_get_safw_attr_all_targets(dev);
+ if (unlikely(rcode < 0))
+ goto free_ciss_luns;
+
+ aac_set_safw_attr_all_targets(dev);
+
+ aac_free_safw_all_identify_resp(dev, -1);
+free_ciss_luns:
+ aac_free_safw_ciss_luns(dev);
+out:
+ return rcode;
+}
+
+int aac_setup_safw_adapter(struct aac_dev *dev)
+{
+ return aac_setup_safw_targets(dev);
+}
+
int aac_get_adapter_info(struct aac_dev* dev)
{
struct fib* fibptr;
int rcode;
- u32 tmp;
+ u32 tmp, bus, target;
struct aac_adapter_info *info;
struct aac_bus_info *command;
struct aac_bus_info_response *bus_info;
@@ -1276,6 +2070,7 @@ int aac_get_adapter_info(struct aac_dev* dev)
}
memcpy(&dev->adapter_info, info, sizeof(*info));
+ dev->supplement_adapter_info.virt_device_bus = 0xffff;
if (dev->adapter_info.options & AAC_OPT_SUPPLEMENT_ADAPTER_INFO) {
struct aac_supplement_adapter_info * sinfo;
@@ -1303,6 +2098,13 @@ int aac_get_adapter_info(struct aac_dev* dev)
}
+ /* reset all previous mapped devices (i.e. for init. after IOP_RESET) */
+ for (bus = 0; bus < AAC_MAX_BUSES; bus++) {
+ for (target = 0; target < AAC_MAX_TARGETS; target++) {
+ dev->hba_map[bus][target].devtype = 0;
+ dev->hba_map[bus][target].qd_limit = 0;
+ }
+ }
/*
* GetBusInfo
@@ -1345,8 +2147,8 @@ int aac_get_adapter_info(struct aac_dev* dev)
(tmp>>16)&0xff,
tmp&0xff,
le32_to_cpu(dev->adapter_info.kernelbuild),
- (int)sizeof(dev->supplement_adapter_info.BuildDate),
- dev->supplement_adapter_info.BuildDate);
+ (int)sizeof(dev->supplement_adapter_info.build_date),
+ dev->supplement_adapter_info.build_date);
tmp = le32_to_cpu(dev->adapter_info.monitorrev);
printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n",
dev->name, dev->id,
@@ -1362,14 +2164,15 @@ int aac_get_adapter_info(struct aac_dev* dev)
shost_to_class(dev->scsi_host_ptr), buffer))
printk(KERN_INFO "%s%d: serial %s",
dev->name, dev->id, buffer);
- if (dev->supplement_adapter_info.VpdInfo.Tsid[0]) {
+ if (dev->supplement_adapter_info.vpd_info.tsid[0]) {
printk(KERN_INFO "%s%d: TSID %.*s\n",
dev->name, dev->id,
- (int)sizeof(dev->supplement_adapter_info.VpdInfo.Tsid),
- dev->supplement_adapter_info.VpdInfo.Tsid);
+ (int)sizeof(dev->supplement_adapter_info
+ .vpd_info.tsid),
+ dev->supplement_adapter_info.vpd_info.tsid);
}
if (!aac_check_reset || ((aac_check_reset == 1) &&
- (dev->supplement_adapter_info.SupportedOptions2 &
+ (dev->supplement_adapter_info.supported_options2 &
AAC_OPTION_IGNORE_RESET))) {
printk(KERN_INFO "%s%d: Reset Adapter Ignored\n",
dev->name, dev->id);
@@ -1377,7 +2180,7 @@ int aac_get_adapter_info(struct aac_dev* dev)
}
dev->cache_protected = 0;
- dev->jbod = ((dev->supplement_adapter_info.FeatureBits &
+ dev->jbod = ((dev->supplement_adapter_info.feature_bits &
AAC_FEATURE_JBOD) != 0);
dev->nondasd_support = 0;
dev->raid_scsi_mode = 0;
@@ -1432,20 +2235,15 @@ int aac_get_adapter_info(struct aac_dev* dev)
expose_physicals = 0;
}
- if(dev->dac_support != 0) {
- if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(64)) &&
- !pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(64))) {
+ if (dev->dac_support) {
+ if (!dma_set_mask(&dev->pdev->dev, DMA_BIT_MASK(64))) {
if (!dev->in_reset)
- printk(KERN_INFO"%s%d: 64 Bit DAC enabled\n",
- dev->name, dev->id);
- } else if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(32)) &&
- !pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(32))) {
- printk(KERN_INFO"%s%d: DMA mask set failed, 64 Bit DAC disabled\n",
- dev->name, dev->id);
+ dev_info(&dev->pdev->dev, "64 Bit DAC enabled\n");
+ } else if (!dma_set_mask(&dev->pdev->dev, DMA_BIT_MASK(32))) {
+ dev_info(&dev->pdev->dev, "DMA mask set failed, 64 Bit DAC disabled\n");
dev->dac_support = 0;
} else {
- printk(KERN_WARNING"%s%d: No suitable DMA available.\n",
- dev->name, dev->id);
+ dev_info(&dev->pdev->dev, "No suitable DMA available\n");
rcode = -ENOMEM;
}
}
@@ -1468,7 +2266,7 @@ int aac_get_adapter_info(struct aac_dev* dev)
dev->a_ops.adapter_bounds = aac_bounds_32;
dev->scsi_host_ptr->sg_tablesize = (dev->max_fib_size -
sizeof(struct aac_fibhdr) -
- sizeof(struct aac_write) + sizeof(struct sgentry)) /
+ sizeof(struct aac_write)) /
sizeof(struct sgentry);
if (dev->dac_support) {
dev->a_ops.adapter_read = aac_read_block64;
@@ -1479,16 +2277,13 @@ int aac_get_adapter_info(struct aac_dev* dev)
dev->scsi_host_ptr->sg_tablesize =
(dev->max_fib_size -
sizeof(struct aac_fibhdr) -
- sizeof(struct aac_write64) +
- sizeof(struct sgentry64)) /
+ sizeof(struct aac_write64)) /
sizeof(struct sgentry64);
} else {
dev->a_ops.adapter_read = aac_read_block;
dev->a_ops.adapter_write = aac_write_block;
}
dev->scsi_host_ptr->max_sectors = AAC_MAX_32BIT_SGBCOUNT;
- if (dev->adapter_info.options & AAC_OPT_NEW_COMM_TYPE1)
- dev->adapter_info.options |= AAC_OPT_NEW_COMM;
if (!(dev->adapter_info.options & AAC_OPT_NEW_COMM)) {
/*
* Worst case size that could cause sg overflow when
@@ -1503,6 +2298,11 @@ int aac_get_adapter_info(struct aac_dev* dev)
(dev->scsi_host_ptr->sg_tablesize * 8) + 112;
}
}
+ if (!dev->sync_mode && dev->sa_firmware &&
+ dev->scsi_host_ptr->sg_tablesize > HBA_MAX_SG_SEPARATE)
+ dev->scsi_host_ptr->sg_tablesize = dev->sg_tablesize =
+ HBA_MAX_SG_SEPARATE;
+
/* FIB should be freed only after getting the response from the F/W */
if (rcode != -ERESTARTSYS) {
aac_fib_complete(fibptr);
@@ -1570,26 +2370,31 @@ static void io_callback(void *context, struct fib * fibptr)
readreply = (struct aac_read_reply *)fib_data(fibptr);
switch (le32_to_cpu(readreply->status)) {
case ST_OK:
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_GOOD;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
dev->fsa_dev[cid].sense_data.sense_key = NO_SENSE;
break;
case ST_NOT_READY:
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_CHECK_CONDITION;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data, NOT_READY,
SENCODE_BECOMING_READY, ASENCODE_BECOMING_READY, 0, 0);
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
break;
+ case ST_MEDERR:
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
+ set_sense(&dev->fsa_dev[cid].sense_data, MEDIUM_ERROR,
+ SENCODE_UNRECOVERED_READ_ERROR, ASENCODE_NO_SENSE, 0, 0);
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
+ SCSI_SENSE_BUFFERSIZE));
+ break;
default:
#ifdef AAC_DETAILED_STATUS_INFO
printk(KERN_WARNING "io_callback: io failed, status = %d\n",
le32_to_cpu(readreply->status));
#endif
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_CHECK_CONDITION;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
@@ -1599,9 +2404,8 @@ static void io_callback(void *context, struct fib * fibptr)
break;
}
aac_fib_complete(fibptr);
- aac_fib_free(fibptr);
- scsicmd->scsi_done(scsicmd);
+ aac_scsi_done(scsicmd);
}
static int aac_read(struct scsi_cmnd * scsicmd)
@@ -1665,16 +2469,15 @@ static int aac_read(struct scsi_cmnd * scsicmd)
if ((lba + count) > (dev->fsa_dev[scmd_id(scsicmd)].size)) {
cid = scmd_id(scsicmd);
dprintk((KERN_DEBUG "aacraid: Illegal lba\n"));
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_CHECK_CONDITION;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
- HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
+ ILLEGAL_REQUEST, SENCODE_LBA_OUT_OF_RANGE,
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
- scsicmd->scsi_done(scsicmd);
- return 1;
+ aac_scsi_done(scsicmd);
+ return 0;
}
dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %llu, t = %ld.\n",
@@ -1684,27 +2487,22 @@ static int aac_read(struct scsi_cmnd * scsicmd)
/*
* Alocate and initialize a Fib
*/
- if (!(cmd_fibcontext = aac_fib_alloc(dev))) {
- printk(KERN_WARNING "aac_read: fib allocation failed\n");
- return -1;
- }
-
+ cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd);
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
status = aac_adapter_read(cmd_fibcontext, scsicmd, lba, count);
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
printk(KERN_WARNING "aac_read: aac_fib_send failed with status: %d.\n", status);
/*
* For some reason, the Fib didn't queue, return QUEUE_FULL
*/
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_TASK_SET_FULL;
- scsicmd->scsi_done(scsicmd);
+ scsicmd->result = DID_OK << 16 | SAM_STAT_TASK_SET_FULL;
+ aac_scsi_done(scsicmd);
aac_fib_complete(cmd_fibcontext);
aac_fib_free(cmd_fibcontext);
return 0;
@@ -1762,16 +2560,15 @@ static int aac_write(struct scsi_cmnd * scsicmd)
if ((lba + count) > (dev->fsa_dev[scmd_id(scsicmd)].size)) {
cid = scmd_id(scsicmd);
dprintk((KERN_DEBUG "aacraid: Illegal lba\n"));
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_CHECK_CONDITION;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
- HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
+ ILLEGAL_REQUEST, SENCODE_LBA_OUT_OF_RANGE,
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
- scsicmd->scsi_done(scsicmd);
- return 1;
+ aac_scsi_done(scsicmd);
+ return 0;
}
dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %llu, t = %ld.\n",
@@ -1781,33 +2578,22 @@ static int aac_write(struct scsi_cmnd * scsicmd)
/*
* Allocate and initialize a Fib then setup a BlockWrite command
*/
- if (!(cmd_fibcontext = aac_fib_alloc(dev))) {
- /* FIB temporarily unavailable,not catastrophic failure */
-
- /* scsicmd->result = DID_ERROR << 16;
- * scsicmd->scsi_done(scsicmd);
- * return 0;
- */
- printk(KERN_WARNING "aac_write: fib allocation failed\n");
- return -1;
- }
-
+ cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd);
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
status = aac_adapter_write(cmd_fibcontext, scsicmd, lba, count, fua);
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
printk(KERN_WARNING "aac_write: aac_fib_send failed with status: %d\n", status);
/*
* For some reason, the Fib didn't queue, return QUEUE_FULL
*/
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_TASK_SET_FULL;
- scsicmd->scsi_done(scsicmd);
+ scsicmd->result = DID_OK << 16 | SAM_STAT_TASK_SET_FULL;
+ aac_scsi_done(scsicmd);
aac_fib_complete(cmd_fibcontext);
aac_fib_free(cmd_fibcontext);
@@ -1817,9 +2603,7 @@ static int aac_write(struct scsi_cmnd * scsicmd)
static void synchronize_callback(void *context, struct fib *fibptr)
{
struct aac_synchronize_reply *synchronizereply;
- struct scsi_cmnd *cmd;
-
- cmd = context;
+ struct scsi_cmnd *cmd = context;
if (!aac_valid_context(cmd, fibptr))
return;
@@ -1831,8 +2615,7 @@ static void synchronize_callback(void *context, struct fib *fibptr)
synchronizereply = fib_data(fibptr);
if (le32_to_cpu(synchronizereply->status) == CT_OK)
- cmd->result = DID_OK << 16 |
- COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ cmd->result = DID_OK << 16 | SAM_STAT_GOOD;
else {
struct scsi_device *sdev = cmd->device;
struct aac_dev *dev = fibptr->dev;
@@ -1840,8 +2623,7 @@ static void synchronize_callback(void *context, struct fib *fibptr)
printk(KERN_WARNING
"synchronize_callback: synchronize failed, status = %d\n",
le32_to_cpu(synchronizereply->status));
- cmd->result = DID_OK << 16 |
- COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ cmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
@@ -1852,7 +2634,7 @@ static void synchronize_callback(void *context, struct fib *fibptr)
aac_fib_complete(fibptr);
aac_fib_free(fibptr);
- cmd->scsi_done(cmd);
+ aac_scsi_done(cmd);
}
static int aac_synchronize(struct scsi_cmnd *scsicmd)
@@ -1860,77 +2642,8 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd)
int status;
struct fib *cmd_fibcontext;
struct aac_synchronize *synchronizecmd;
- struct scsi_cmnd *cmd;
struct scsi_device *sdev = scsicmd->device;
- int active = 0;
struct aac_dev *aac;
- u64 lba = ((u64)scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) |
- (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
- u32 count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
- unsigned long flags;
-
- /*
- * Wait for all outstanding queued commands to complete to this
- * specific target (block).
- */
- spin_lock_irqsave(&sdev->list_lock, flags);
- list_for_each_entry(cmd, &sdev->cmd_list, list)
- if (cmd->SCp.phase == AAC_OWNER_FIRMWARE) {
- u64 cmnd_lba;
- u32 cmnd_count;
-
- if (cmd->cmnd[0] == WRITE_6) {
- cmnd_lba = ((cmd->cmnd[1] & 0x1F) << 16) |
- (cmd->cmnd[2] << 8) |
- cmd->cmnd[3];
- cmnd_count = cmd->cmnd[4];
- if (cmnd_count == 0)
- cmnd_count = 256;
- } else if (cmd->cmnd[0] == WRITE_16) {
- cmnd_lba = ((u64)cmd->cmnd[2] << 56) |
- ((u64)cmd->cmnd[3] << 48) |
- ((u64)cmd->cmnd[4] << 40) |
- ((u64)cmd->cmnd[5] << 32) |
- ((u64)cmd->cmnd[6] << 24) |
- (cmd->cmnd[7] << 16) |
- (cmd->cmnd[8] << 8) |
- cmd->cmnd[9];
- cmnd_count = (cmd->cmnd[10] << 24) |
- (cmd->cmnd[11] << 16) |
- (cmd->cmnd[12] << 8) |
- cmd->cmnd[13];
- } else if (cmd->cmnd[0] == WRITE_12) {
- cmnd_lba = ((u64)cmd->cmnd[2] << 24) |
- (cmd->cmnd[3] << 16) |
- (cmd->cmnd[4] << 8) |
- cmd->cmnd[5];
- cmnd_count = (cmd->cmnd[6] << 24) |
- (cmd->cmnd[7] << 16) |
- (cmd->cmnd[8] << 8) |
- cmd->cmnd[9];
- } else if (cmd->cmnd[0] == WRITE_10) {
- cmnd_lba = ((u64)cmd->cmnd[2] << 24) |
- (cmd->cmnd[3] << 16) |
- (cmd->cmnd[4] << 8) |
- cmd->cmnd[5];
- cmnd_count = (cmd->cmnd[7] << 8) |
- cmd->cmnd[8];
- } else
- continue;
- if (((cmnd_lba + cmnd_count) < lba) ||
- (count && ((lba + count) < cmnd_lba)))
- continue;
- ++active;
- break;
- }
-
- spin_unlock_irqrestore(&sdev->list_lock, flags);
-
- /*
- * Yield the processor (requeue for later)
- */
- if (active)
- return SCSI_MLQUEUE_DEVICE_BUSY;
aac = (struct aac_dev *)sdev->host->hostdata;
if (aac->in_reset)
@@ -1939,8 +2652,7 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd)
/*
* Allocate and initialize a Fib
*/
- if (!(cmd_fibcontext = aac_fib_alloc(aac)))
- return SCSI_MLQUEUE_HOST_BUSY;
+ cmd_fibcontext = aac_fib_alloc_tag(aac, scsicmd);
aac_fib_init(cmd_fibcontext);
@@ -1950,6 +2662,7 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd)
synchronizecmd->cid = cpu_to_le32(scmd_id(scsicmd));
synchronizecmd->count =
cpu_to_le32(sizeof(((struct aac_synchronize_reply *)NULL)->data));
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
/*
* Now send the Fib to the adapter
@@ -1965,10 +2678,8 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd)
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
printk(KERN_WARNING
"aac_synchronize: aac_fib_send failed with status: %d.\n", status);
@@ -1986,11 +2697,11 @@ static void aac_start_stop_callback(void *context, struct fib *fibptr)
BUG_ON(fibptr == NULL);
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
aac_fib_complete(fibptr);
aac_fib_free(fibptr);
- scsicmd->scsi_done(scsicmd);
+ aac_scsi_done(scsicmd);
}
static int aac_start_stop(struct scsi_cmnd *scsicmd)
@@ -2001,11 +2712,10 @@ static int aac_start_stop(struct scsi_cmnd *scsicmd)
struct scsi_device *sdev = scsicmd->device;
struct aac_dev *aac = (struct aac_dev *)sdev->host->hostdata;
- if (!(aac->supplement_adapter_info.SupportedOptions2 &
+ if (!(aac->supplement_adapter_info.supported_options2 &
AAC_OPTION_POWER_MANAGEMENT)) {
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ aac_scsi_done(scsicmd);
return 0;
}
@@ -2015,9 +2725,7 @@ static int aac_start_stop(struct scsi_cmnd *scsicmd)
/*
* Allocate and initialize a Fib
*/
- cmd_fibcontext = aac_fib_alloc(aac);
- if (!cmd_fibcontext)
- return SCSI_MLQUEUE_HOST_BUSY;
+ cmd_fibcontext = aac_fib_alloc_tag(aac, scsicmd);
aac_fib_init(cmd_fibcontext);
@@ -2030,6 +2738,7 @@ static int aac_start_stop(struct scsi_cmnd *scsicmd)
pmcmd->cid = cpu_to_le32(sdev_id(sdev));
pmcmd->parm = (scsicmd->cmnd[1] & 1) ?
cpu_to_le32(CT_PM_UNIT_IMMEDIATE) : 0;
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
/*
* Now send the Fib to the adapter
@@ -2045,10 +2754,8 @@ static int aac_start_stop(struct scsi_cmnd *scsicmd)
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
aac_fib_complete(cmd_fibcontext);
aac_fib_free(cmd_fibcontext);
@@ -2065,7 +2772,7 @@ static int aac_start_stop(struct scsi_cmnd *scsicmd)
int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
{
- u32 cid;
+ u32 cid, bus;
struct Scsi_Host *host = scsicmd->device->host;
struct aac_dev *dev = (struct aac_dev *)host->hostdata;
struct fsa_dev_info *fsa_dev_ptr = dev->fsa_dev;
@@ -2083,8 +2790,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
if((cid >= dev->maximum_num_containers) ||
(scsicmd->device->lun != 0)) {
scsicmd->result = DID_NO_CONNECT << 16;
- scsicmd->scsi_done(scsicmd);
- return 0;
+ goto scsi_done_ret;
}
/*
@@ -2095,11 +2801,12 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
(fsa_dev_ptr[cid].sense_data.sense_key ==
NOT_READY)) {
switch (scsicmd->cmnd[0]) {
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if (!(dev->raw_io_interface) ||
!(dev->raw_io_64) ||
((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
break;
+ fallthrough;
case INQUIRY:
case READ_CAPACITY:
case TEST_UNIT_READY:
@@ -2112,15 +2819,22 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
}
}
} else { /* check for physical non-dasd devices */
- if (dev->nondasd_support || expose_physicals ||
- dev->jbod) {
+ bus = aac_logical_to_phys(scmd_channel(scsicmd));
+
+ if (bus < AAC_MAX_BUSES && cid < AAC_MAX_TARGETS &&
+ dev->hba_map[bus][cid].devtype
+ == AAC_DEVTYPE_NATIVE_RAW) {
+ if (dev->in_reset)
+ return -1;
+ return aac_send_hba_fib(scsicmd);
+ } else if (dev->nondasd_support || expose_physicals ||
+ dev->jbod) {
if (dev->in_reset)
return -1;
return aac_send_srb_fib(scsicmd);
} else {
scsicmd->result = DID_NO_CONNECT << 16;
- scsicmd->scsi_done(scsicmd);
- return 0;
+ goto scsi_done_ret;
}
}
}
@@ -2131,20 +2845,42 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
(scsicmd->cmnd[0] != TEST_UNIT_READY))
{
dprintk((KERN_WARNING "Only INQUIRY & TUR command supported for controller, rcvd = 0x%x.\n", scsicmd->cmnd[0]));
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
ILLEGAL_REQUEST, SENCODE_INVALID_COMMAND,
ASENCODE_INVALID_COMMAND, 0, 0);
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
- scsicmd->scsi_done(scsicmd);
- return 0;
+ goto scsi_done_ret;
}
-
- /* Handle commands here that don't really require going out to the adapter */
switch (scsicmd->cmnd[0]) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ if (dev->in_reset)
+ return -1;
+ return aac_read(scsicmd);
+
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ if (dev->in_reset)
+ return -1;
+ return aac_write(scsicmd);
+
+ case SYNCHRONIZE_CACHE:
+ if (((aac_cache & 6) == 6) && dev->cache_protected) {
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
+ }
+ /* Issue FIB to tell Firmware to flush it's cache */
+ if ((aac_cache & 6) != 2)
+ return aac_synchronize(scsicmd);
+ fallthrough;
case INQUIRY:
{
struct inquiry_data inq_data;
@@ -2160,14 +2896,14 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
INQD_PDT_PROC : INQD_PDT_DA;
if (scsicmd->cmnd[2] == 0) {
/* supported vital product data pages */
- arr[3] = 2;
+ arr[3] = 3;
arr[4] = 0x0;
arr[5] = 0x80;
+ arr[6] = 0x83;
arr[1] = scsicmd->cmnd[2];
scsi_sg_copy_from_buffer(scsicmd, &inq_data,
sizeof(inq_data));
- scsicmd->result = DID_OK << 16 |
- COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
} else if (scsicmd->cmnd[2] == 0x80) {
/* unit serial number page */
arr[3] = setinqserial(dev, &arr[4],
@@ -2178,14 +2914,19 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
if (aac_wwn != 2)
return aac_get_container_serial(
scsicmd);
- /* SLES 10 SP1 special */
- scsicmd->result = DID_OK << 16 |
- COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ } else if (scsicmd->cmnd[2] == 0x83) {
+ /* vpd page 0x83 - Device Identification Page */
+ char *sno = (char *)&inq_data;
+ sno[3] = setinqserial(dev, &sno[4],
+ scmd_id(scsicmd));
+ if (aac_wwn != 2)
+ return aac_get_container_serial(
+ scsicmd);
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
} else {
/* vpd page not implemented */
- scsicmd->result = DID_OK << 16 |
- COMMAND_COMPLETE << 8 |
- SAM_STAT_CHECK_CONDITION;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
ILLEGAL_REQUEST, SENCODE_INVALID_CDB_FIELD,
ASENCODE_NO_SENSE, 7, 2);
@@ -2195,8 +2936,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
}
- scsicmd->scsi_done(scsicmd);
- return 0;
+ break;
}
inq_data.inqd_ver = 2; /* claim compliance to SCSI-2 */
inq_data.inqd_rdf = 2; /* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */
@@ -2212,9 +2952,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
inq_data.inqd_pdt = INQD_PDT_PROC; /* Processor device */
scsi_sg_copy_from_buffer(scsicmd, &inq_data,
sizeof(inq_data));
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
- return 0;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
}
if (dev->in_reset)
return -1;
@@ -2223,7 +2962,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
scsi_sg_copy_from_buffer(scsicmd, &inq_data, sizeof(inq_data));
return aac_get_container_name(scsicmd);
}
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if (!(dev->raw_io_interface) ||
!(dev->raw_io_64) ||
((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
@@ -2243,10 +2982,10 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
cp[5] = (capacity >> 16) & 0xff;
cp[6] = (capacity >> 8) & 0xff;
cp[7] = (capacity >> 0) & 0xff;
- cp[8] = 0;
- cp[9] = 0;
- cp[10] = 2;
- cp[11] = 0;
+ cp[8] = (fsa_dev_ptr[cid].block_size >> 24) & 0xff;
+ cp[9] = (fsa_dev_ptr[cid].block_size >> 16) & 0xff;
+ cp[10] = (fsa_dev_ptr[cid].block_size >> 8) & 0xff;
+ cp[11] = (fsa_dev_ptr[cid].block_size) & 0xff;
cp[12] = 0;
alloc_len = ((scsicmd->cmnd[10] << 24)
@@ -2262,10 +3001,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
/* Do not cache partition table for arrays */
scsicmd->device->removable = 1;
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
-
- return 0;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
}
case READ_CAPACITY:
@@ -2283,94 +3020,180 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
cp[1] = (capacity >> 16) & 0xff;
cp[2] = (capacity >> 8) & 0xff;
cp[3] = (capacity >> 0) & 0xff;
- cp[4] = 0;
- cp[5] = 0;
- cp[6] = 2;
- cp[7] = 0;
+ cp[4] = (fsa_dev_ptr[cid].block_size >> 24) & 0xff;
+ cp[5] = (fsa_dev_ptr[cid].block_size >> 16) & 0xff;
+ cp[6] = (fsa_dev_ptr[cid].block_size >> 8) & 0xff;
+ cp[7] = (fsa_dev_ptr[cid].block_size) & 0xff;
scsi_sg_copy_from_buffer(scsicmd, cp, sizeof(cp));
/* Do not cache partition table for arrays */
scsicmd->device->removable = 1;
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
-
- return 0;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
}
case MODE_SENSE:
{
- char mode_buf[7];
int mode_buf_length = 4;
+ u32 capacity;
+ aac_modep_data mpd;
+
+ if (fsa_dev_ptr[cid].size <= 0x100000000ULL)
+ capacity = fsa_dev_ptr[cid].size - 1;
+ else
+ capacity = (u32)-1;
dprintk((KERN_DEBUG "MODE SENSE command.\n"));
- mode_buf[0] = 3; /* Mode data length */
- mode_buf[1] = 0; /* Medium type - default */
- mode_buf[2] = 0; /* Device-specific param,
- bit 8: 0/1 = write enabled/protected
- bit 4: 0/1 = FUA enabled */
+ memset((char *)&mpd, 0, sizeof(aac_modep_data));
+
+ /* Mode data length */
+ mpd.hd.data_length = sizeof(mpd.hd) - 1;
+ /* Medium type - default */
+ mpd.hd.med_type = 0;
+ /* Device-specific param,
+ bit 8: 0/1 = write enabled/protected
+ bit 4: 0/1 = FUA enabled */
+ mpd.hd.dev_par = 0;
+
if (dev->raw_io_interface && ((aac_cache & 5) != 1))
- mode_buf[2] = 0x10;
- mode_buf[3] = 0; /* Block descriptor length */
+ mpd.hd.dev_par = 0x10;
+ if (scsicmd->cmnd[1] & 0x8)
+ mpd.hd.bd_length = 0; /* Block descriptor length */
+ else {
+ mpd.hd.bd_length = sizeof(mpd.bd);
+ mpd.hd.data_length += mpd.hd.bd_length;
+ mpd.bd.block_length[0] =
+ (fsa_dev_ptr[cid].block_size >> 16) & 0xff;
+ mpd.bd.block_length[1] =
+ (fsa_dev_ptr[cid].block_size >> 8) & 0xff;
+ mpd.bd.block_length[2] =
+ fsa_dev_ptr[cid].block_size & 0xff;
+
+ mpd.mpc_buf[0] = scsicmd->cmnd[2];
+ if (scsicmd->cmnd[2] == 0x1C) {
+ /* page length */
+ mpd.mpc_buf[1] = 0xa;
+ /* Mode data length */
+ mpd.hd.data_length = 23;
+ } else {
+ /* Mode data length */
+ mpd.hd.data_length = 15;
+ }
+
+ if (capacity > 0xffffff) {
+ mpd.bd.block_count[0] = 0xff;
+ mpd.bd.block_count[1] = 0xff;
+ mpd.bd.block_count[2] = 0xff;
+ } else {
+ mpd.bd.block_count[0] = (capacity >> 16) & 0xff;
+ mpd.bd.block_count[1] = (capacity >> 8) & 0xff;
+ mpd.bd.block_count[2] = capacity & 0xff;
+ }
+ }
if (((scsicmd->cmnd[2] & 0x3f) == 8) ||
((scsicmd->cmnd[2] & 0x3f) == 0x3f)) {
- mode_buf[0] = 6;
- mode_buf[4] = 8;
- mode_buf[5] = 1;
- mode_buf[6] = ((aac_cache & 6) == 2)
+ mpd.hd.data_length += 3;
+ mpd.mpc_buf[0] = 8;
+ mpd.mpc_buf[1] = 1;
+ mpd.mpc_buf[2] = ((aac_cache & 6) == 2)
? 0 : 0x04; /* WCE */
- mode_buf_length = 7;
- if (mode_buf_length > scsicmd->cmnd[4])
- mode_buf_length = scsicmd->cmnd[4];
+ mode_buf_length = sizeof(mpd);
}
- scsi_sg_copy_from_buffer(scsicmd, mode_buf, mode_buf_length);
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
- return 0;
+ if (mode_buf_length > scsicmd->cmnd[4])
+ mode_buf_length = scsicmd->cmnd[4];
+ else
+ mode_buf_length = sizeof(mpd);
+ scsi_sg_copy_from_buffer(scsicmd,
+ (char *)&mpd,
+ mode_buf_length);
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
}
case MODE_SENSE_10:
{
- char mode_buf[11];
+ u32 capacity;
int mode_buf_length = 8;
+ aac_modep10_data mpd10;
+
+ if (fsa_dev_ptr[cid].size <= 0x100000000ULL)
+ capacity = fsa_dev_ptr[cid].size - 1;
+ else
+ capacity = (u32)-1;
dprintk((KERN_DEBUG "MODE SENSE 10 byte command.\n"));
- mode_buf[0] = 0; /* Mode data length (MSB) */
- mode_buf[1] = 6; /* Mode data length (LSB) */
- mode_buf[2] = 0; /* Medium type - default */
- mode_buf[3] = 0; /* Device-specific param,
- bit 8: 0/1 = write enabled/protected
- bit 4: 0/1 = FUA enabled */
+ memset((char *)&mpd10, 0, sizeof(aac_modep10_data));
+ /* Mode data length (MSB) */
+ mpd10.hd.data_length[0] = 0;
+ /* Mode data length (LSB) */
+ mpd10.hd.data_length[1] = sizeof(mpd10.hd) - 1;
+ /* Medium type - default */
+ mpd10.hd.med_type = 0;
+ /* Device-specific param,
+ bit 8: 0/1 = write enabled/protected
+ bit 4: 0/1 = FUA enabled */
+ mpd10.hd.dev_par = 0;
+
if (dev->raw_io_interface && ((aac_cache & 5) != 1))
- mode_buf[3] = 0x10;
- mode_buf[4] = 0; /* reserved */
- mode_buf[5] = 0; /* reserved */
- mode_buf[6] = 0; /* Block descriptor length (MSB) */
- mode_buf[7] = 0; /* Block descriptor length (LSB) */
+ mpd10.hd.dev_par = 0x10;
+ mpd10.hd.rsrvd[0] = 0; /* reserved */
+ mpd10.hd.rsrvd[1] = 0; /* reserved */
+ if (scsicmd->cmnd[1] & 0x8) {
+ /* Block descriptor length (MSB) */
+ mpd10.hd.bd_length[0] = 0;
+ /* Block descriptor length (LSB) */
+ mpd10.hd.bd_length[1] = 0;
+ } else {
+ mpd10.hd.bd_length[0] = 0;
+ mpd10.hd.bd_length[1] = sizeof(mpd10.bd);
+
+ mpd10.hd.data_length[1] += mpd10.hd.bd_length[1];
+
+ mpd10.bd.block_length[0] =
+ (fsa_dev_ptr[cid].block_size >> 16) & 0xff;
+ mpd10.bd.block_length[1] =
+ (fsa_dev_ptr[cid].block_size >> 8) & 0xff;
+ mpd10.bd.block_length[2] =
+ fsa_dev_ptr[cid].block_size & 0xff;
+
+ if (capacity > 0xffffff) {
+ mpd10.bd.block_count[0] = 0xff;
+ mpd10.bd.block_count[1] = 0xff;
+ mpd10.bd.block_count[2] = 0xff;
+ } else {
+ mpd10.bd.block_count[0] =
+ (capacity >> 16) & 0xff;
+ mpd10.bd.block_count[1] =
+ (capacity >> 8) & 0xff;
+ mpd10.bd.block_count[2] =
+ capacity & 0xff;
+ }
+ }
if (((scsicmd->cmnd[2] & 0x3f) == 8) ||
((scsicmd->cmnd[2] & 0x3f) == 0x3f)) {
- mode_buf[1] = 9;
- mode_buf[8] = 8;
- mode_buf[9] = 1;
- mode_buf[10] = ((aac_cache & 6) == 2)
+ mpd10.hd.data_length[1] += 3;
+ mpd10.mpc_buf[0] = 8;
+ mpd10.mpc_buf[1] = 1;
+ mpd10.mpc_buf[2] = ((aac_cache & 6) == 2)
? 0 : 0x04; /* WCE */
- mode_buf_length = 11;
+ mode_buf_length = sizeof(mpd10);
if (mode_buf_length > scsicmd->cmnd[8])
mode_buf_length = scsicmd->cmnd[8];
}
- scsi_sg_copy_from_buffer(scsicmd, mode_buf, mode_buf_length);
+ scsi_sg_copy_from_buffer(scsicmd,
+ (char *)&mpd10,
+ mode_buf_length);
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
-
- return 0;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
}
case REQUEST_SENSE:
dprintk((KERN_DEBUG "REQUEST SENSE command.\n"));
- memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, sizeof (struct sense_data));
- memset(&dev->fsa_dev[cid].sense_data, 0, sizeof (struct sense_data));
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
- return 0;
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ sizeof(struct sense_data));
+ memset(&dev->fsa_dev[cid].sense_data, 0,
+ sizeof(struct sense_data));
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
case ALLOW_MEDIUM_REMOVAL:
dprintk((KERN_DEBUG "LOCK command.\n"));
@@ -2379,16 +3202,14 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
else
fsa_dev_ptr[cid].locked = 0;
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
- return 0;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
/*
* These commands are all No-Ops
*/
case TEST_UNIT_READY:
if (fsa_dev_ptr[cid].sense_data.sense_key == NOT_READY) {
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
- SAM_STAT_CHECK_CONDITION;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data,
NOT_READY, SENCODE_BECOMING_READY,
ASENCODE_BECOMING_READY, 0, 0);
@@ -2397,80 +3218,40 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
min_t(size_t,
sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
- scsicmd->scsi_done(scsicmd);
- return 0;
+ break;
}
- /* FALLTHRU */
- case RESERVE:
- case RELEASE:
+ fallthrough;
+ case RESERVE_6:
+ case RELEASE_6:
case REZERO_UNIT:
case REASSIGN_BLOCKS:
case SEEK_10:
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
- return 0;
+ scsicmd->result = DID_OK << 16 | SAM_STAT_GOOD;
+ break;
case START_STOP:
return aac_start_stop(scsicmd);
- }
- switch (scsicmd->cmnd[0])
- {
- case READ_6:
- case READ_10:
- case READ_12:
- case READ_16:
- if (dev->in_reset)
- return -1;
- /*
- * Hack to keep track of ordinal number of the device that
- * corresponds to a container. Needed to convert
- * containers to /dev/sd device names
- */
-
- if (scsicmd->request->rq_disk)
- strlcpy(fsa_dev_ptr[cid].devname,
- scsicmd->request->rq_disk->disk_name,
- min(sizeof(fsa_dev_ptr[cid].devname),
- sizeof(scsicmd->request->rq_disk->disk_name) + 1));
-
- return aac_read(scsicmd);
-
- case WRITE_6:
- case WRITE_10:
- case WRITE_12:
- case WRITE_16:
- if (dev->in_reset)
- return -1;
- return aac_write(scsicmd);
-
- case SYNCHRONIZE_CACHE:
- if (((aac_cache & 6) == 6) && dev->cache_protected) {
- scsicmd->result = DID_OK << 16 |
- COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
- scsicmd->scsi_done(scsicmd);
- return 0;
- }
- /* Issue FIB to tell Firmware to flush it's cache */
- if ((aac_cache & 6) != 2)
- return aac_synchronize(scsicmd);
- /* FALLTHRU */
- default:
- /*
- * Unhandled commands
- */
- dprintk((KERN_WARNING "Unhandled SCSI Command: 0x%x.\n", scsicmd->cmnd[0]));
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
- set_sense(&dev->fsa_dev[cid].sense_data,
+ default:
+ /*
+ * Unhandled commands
+ */
+ dprintk((KERN_WARNING "Unhandled SCSI Command: 0x%x.\n",
+ scsicmd->cmnd[0]));
+ scsicmd->result = DID_OK << 16 | SAM_STAT_CHECK_CONDITION;
+ set_sense(&dev->fsa_dev[cid].sense_data,
ILLEGAL_REQUEST, SENCODE_INVALID_COMMAND,
ASENCODE_INVALID_COMMAND, 0, 0);
- memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
min_t(size_t,
sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE));
- scsicmd->scsi_done(scsicmd);
- return 0;
}
+
+scsi_done_ret:
+
+ aac_scsi_done(scsicmd);
+ return 0;
}
static int query_disk(struct aac_dev *dev, void __user *arg)
@@ -2483,10 +3264,11 @@ static int query_disk(struct aac_dev *dev, void __user *arg)
return -EBUSY;
if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk)))
return -EFAULT;
- if (qd.cnum == -1)
+ if (qd.cnum == -1) {
+ if (qd.id < 0 || qd.id >= dev->maximum_num_containers)
+ return -EINVAL;
qd.cnum = qd.id;
- else if ((qd.bus == -1) && (qd.id == -1) && (qd.lun == -1))
- {
+ } else if ((qd.bus == -1) && (qd.id == -1) && (qd.lun == -1)) {
if (qd.cnum < 0 || qd.cnum >= dev->maximum_num_containers)
return -EINVAL;
qd.instance = dev->scsi_host_ptr->host_no;
@@ -2505,7 +3287,7 @@ static int query_disk(struct aac_dev *dev, void __user *arg)
else
qd.unmapped = 0;
- strlcpy(qd.name, fsa_dev_ptr[qd.cnum].devname,
+ strscpy(qd.name, fsa_dev_ptr[qd.cnum].devname,
min(sizeof(qd.name), sizeof(fsa_dev_ptr[qd.cnum].devname) + 1));
if (copy_to_user(arg, &qd, sizeof (struct aac_query_disk)))
@@ -2567,7 +3349,7 @@ static int delete_disk(struct aac_dev *dev, void __user *arg)
}
}
-int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg)
+int aac_dev_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg)
{
switch (cmd) {
case FSACTL_QUERY_DISK:
@@ -2584,18 +3366,14 @@ int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg)
}
/**
- *
* aac_srb_callback
* @context: the context set in the fib - here it is scsi cmd
* @fibptr: pointer to the fib
*
* Handles the completion of a scsi command to a non dasd device
- *
*/
-
static void aac_srb_callback(void *context, struct fib * fibptr)
{
- struct aac_dev *dev;
struct aac_srb_reply *srbreply;
struct scsi_cmnd *scsicmd;
@@ -2606,17 +3384,22 @@ static void aac_srb_callback(void *context, struct fib * fibptr)
BUG_ON(fibptr == NULL);
- dev = fibptr->dev;
-
srbreply = (struct aac_srb_reply *) fib_data(fibptr);
scsicmd->sense_buffer[0] = '\0'; /* Initialize sense valid flag to false */
- /*
- * Calculate resid for sg
- */
- scsi_set_resid(scsicmd, scsi_bufflen(scsicmd)
- - le32_to_cpu(srbreply->data_xfer_length));
+ if (fibptr->flags & FIB_CONTEXT_FLAG_FASTRESP) {
+ /* fast response */
+ srbreply->srb_status = cpu_to_le32(SRB_STATUS_SUCCESS);
+ srbreply->scsi_status = cpu_to_le32(SAM_STAT_GOOD);
+ } else {
+ /*
+ * Calculate resid for sg
+ */
+ scsi_set_resid(scsicmd, scsi_bufflen(scsicmd)
+ - le32_to_cpu(srbreply->data_xfer_length));
+ }
+
scsi_dma_unmap(scsicmd);
@@ -2629,26 +3412,29 @@ static void aac_srb_callback(void *context, struct fib * fibptr)
* First check the fib status
*/
- if (le32_to_cpu(srbreply->status) != ST_OK){
+ if (le32_to_cpu(srbreply->status) != ST_OK) {
int len;
- printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status));
+
+ pr_warn("aac_srb_callback: srb failed, status = %d\n",
+ le32_to_cpu(srbreply->status));
len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
SCSI_SENSE_BUFFERSIZE);
- scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
- memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+ scsicmd->result = DID_ERROR << 16 | SAM_STAT_CHECK_CONDITION;
+ memcpy(scsicmd->sense_buffer,
+ srbreply->sense_data, len);
}
/*
* Next check the srb status
*/
- switch( (le32_to_cpu(srbreply->srb_status))&0x3f){
+ switch ((le32_to_cpu(srbreply->srb_status))&0x3f) {
case SRB_STATUS_ERROR_RECOVERY:
case SRB_STATUS_PENDING:
case SRB_STATUS_SUCCESS:
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ scsicmd->result = DID_OK << 16;
break;
case SRB_STATUS_DATA_OVERRUN:
- switch(scsicmd->cmnd[0]){
+ switch (scsicmd->cmnd[0]) {
case READ_6:
case WRITE_6:
case READ_10:
@@ -2657,55 +3443,57 @@ static void aac_srb_callback(void *context, struct fib * fibptr)
case WRITE_12:
case READ_16:
case WRITE_16:
- if (le32_to_cpu(srbreply->data_xfer_length) < scsicmd->underflow) {
- printk(KERN_WARNING"aacraid: SCSI CMD underflow\n");
- } else {
- printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n");
- }
- scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ if (le32_to_cpu(srbreply->data_xfer_length)
+ < scsicmd->underflow)
+ pr_warn("aacraid: SCSI CMD underflow\n");
+ else
+ pr_warn("aacraid: SCSI CMD Data Overrun\n");
+ scsicmd->result = DID_ERROR << 16;
break;
- case INQUIRY: {
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ case INQUIRY:
+ scsicmd->result = DID_OK << 16;
break;
- }
default:
- scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ scsicmd->result = DID_OK << 16;
break;
}
break;
case SRB_STATUS_ABORTED:
- scsicmd->result = DID_ABORT << 16 | ABORT << 8;
+ scsicmd->result = DID_ABORT << 16;
break;
case SRB_STATUS_ABORT_FAILED:
- // Not sure about this one - but assuming the hba was trying to abort for some reason
- scsicmd->result = DID_ERROR << 16 | ABORT << 8;
+ /*
+ * Not sure about this one - but assuming the
+ * hba was trying to abort for some reason
+ */
+ scsicmd->result = DID_ERROR << 16;
break;
case SRB_STATUS_PARITY_ERROR:
- scsicmd->result = DID_PARITY << 16 | MSG_PARITY_ERROR << 8;
+ scsicmd->result = DID_PARITY << 16;
break;
case SRB_STATUS_NO_DEVICE:
case SRB_STATUS_INVALID_PATH_ID:
case SRB_STATUS_INVALID_TARGET_ID:
case SRB_STATUS_INVALID_LUN:
case SRB_STATUS_SELECTION_TIMEOUT:
- scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+ scsicmd->result = DID_NO_CONNECT << 16;
break;
case SRB_STATUS_COMMAND_TIMEOUT:
case SRB_STATUS_TIMEOUT:
- scsicmd->result = DID_TIME_OUT << 16 | COMMAND_COMPLETE << 8;
+ scsicmd->result = DID_TIME_OUT << 16;
break;
case SRB_STATUS_BUSY:
- scsicmd->result = DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8;
+ scsicmd->result = DID_BUS_BUSY << 16;
break;
case SRB_STATUS_BUS_RESET:
- scsicmd->result = DID_RESET << 16 | COMMAND_COMPLETE << 8;
+ scsicmd->result = DID_RESET << 16;
break;
case SRB_STATUS_MESSAGE_REJECTED:
- scsicmd->result = DID_ERROR << 16 | MESSAGE_REJECT << 8;
+ scsicmd->result = DID_ERROR << 16;
break;
case SRB_STATUS_REQUEST_FLUSHED:
case SRB_STATUS_ERROR:
@@ -2723,60 +3511,204 @@ static void aac_srb_callback(void *context, struct fib * fibptr)
case SRB_STATUS_DOMAIN_VALIDATION_FAIL:
default:
#ifdef AAC_DETAILED_STATUS_INFO
- printk("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n",
+ pr_info("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x -scsi status 0x%x\n",
le32_to_cpu(srbreply->srb_status) & 0x3F,
aac_get_status_string(
le32_to_cpu(srbreply->srb_status) & 0x3F),
scsicmd->cmnd[0],
le32_to_cpu(srbreply->scsi_status));
#endif
+ /*
+ * When the CC bit is SET by the host in ATA pass thru CDB,
+ * driver is supposed to return DID_OK
+ *
+ * When the CC bit is RESET by the host, driver should
+ * return DID_ERROR
+ */
if ((scsicmd->cmnd[0] == ATA_12)
- || (scsicmd->cmnd[0] == ATA_16)) {
+ || (scsicmd->cmnd[0] == ATA_16)) {
+
if (scsicmd->cmnd[2] & (0x01 << 5)) {
- scsicmd->result = DID_OK << 16
- | COMMAND_COMPLETE << 8;
- break;
+ scsicmd->result = DID_OK << 16;
} else {
- scsicmd->result = DID_ERROR << 16
- | COMMAND_COMPLETE << 8;
- break;
+ scsicmd->result = DID_ERROR << 16;
}
} else {
- scsicmd->result = DID_ERROR << 16
- | COMMAND_COMPLETE << 8;
- break;
+ scsicmd->result = DID_ERROR << 16;
}
+ break;
}
- if (le32_to_cpu(srbreply->scsi_status) == SAM_STAT_CHECK_CONDITION) {
+ if (le32_to_cpu(srbreply->scsi_status)
+ == SAM_STAT_CHECK_CONDITION) {
int len;
+
scsicmd->result |= SAM_STAT_CHECK_CONDITION;
len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
SCSI_SENSE_BUFFERSIZE);
#ifdef AAC_DETAILED_STATUS_INFO
- printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n",
+ pr_warn("aac_srb_callback: check condition, status = %d len=%d\n",
le32_to_cpu(srbreply->status), len);
#endif
- memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+ memcpy(scsicmd->sense_buffer,
+ srbreply->sense_data, len);
}
+
/*
* OR in the scsi status (already shifted up a bit)
*/
scsicmd->result |= le32_to_cpu(srbreply->scsi_status);
aac_fib_complete(fibptr);
- aac_fib_free(fibptr);
- scsicmd->scsi_done(scsicmd);
+ aac_scsi_done(scsicmd);
+}
+
+static void hba_resp_task_complete(struct aac_dev *dev,
+ struct scsi_cmnd *scsicmd,
+ struct aac_hba_resp *err) {
+
+ scsicmd->result = err->status;
+ /* set residual count */
+ scsi_set_resid(scsicmd, le32_to_cpu(err->residual_count));
+
+ switch (err->status) {
+ case SAM_STAT_GOOD:
+ scsicmd->result |= DID_OK << 16;
+ break;
+ case SAM_STAT_CHECK_CONDITION:
+ {
+ int len;
+
+ len = min_t(u8, err->sense_response_data_len,
+ SCSI_SENSE_BUFFERSIZE);
+ if (len)
+ memcpy(scsicmd->sense_buffer,
+ err->sense_response_buf, len);
+ scsicmd->result |= DID_OK << 16;
+ break;
+ }
+ case SAM_STAT_BUSY:
+ scsicmd->result |= DID_BUS_BUSY << 16;
+ break;
+ case SAM_STAT_TASK_ABORTED:
+ scsicmd->result |= DID_ABORT << 16;
+ break;
+ case SAM_STAT_RESERVATION_CONFLICT:
+ case SAM_STAT_TASK_SET_FULL:
+ default:
+ scsicmd->result |= DID_ERROR << 16;
+ break;
+ }
+}
+
+static void hba_resp_task_failure(struct aac_dev *dev,
+ struct scsi_cmnd *scsicmd,
+ struct aac_hba_resp *err)
+{
+ switch (err->status) {
+ case HBA_RESP_STAT_HBAMODE_DISABLED:
+ {
+ u32 bus, cid;
+
+ bus = aac_logical_to_phys(scmd_channel(scsicmd));
+ cid = scmd_id(scsicmd);
+ if (dev->hba_map[bus][cid].devtype == AAC_DEVTYPE_NATIVE_RAW) {
+ dev->hba_map[bus][cid].devtype = AAC_DEVTYPE_ARC_RAW;
+ dev->hba_map[bus][cid].rmw_nexus = 0xffffffff;
+ }
+ scsicmd->result = DID_NO_CONNECT << 16;
+ break;
+ }
+ case HBA_RESP_STAT_IO_ERROR:
+ case HBA_RESP_STAT_NO_PATH_TO_DEVICE:
+ scsicmd->result = DID_OK << 16 | SAM_STAT_BUSY;
+ break;
+ case HBA_RESP_STAT_IO_ABORTED:
+ scsicmd->result = DID_ABORT << 16;
+ break;
+ case HBA_RESP_STAT_INVALID_DEVICE:
+ scsicmd->result = DID_NO_CONNECT << 16;
+ break;
+ case HBA_RESP_STAT_UNDERRUN:
+ /* UNDERRUN is OK */
+ scsicmd->result = DID_OK << 16;
+ break;
+ case HBA_RESP_STAT_OVERRUN:
+ default:
+ scsicmd->result = DID_ERROR << 16;
+ break;
+ }
}
/**
+ * aac_hba_callback
+ * @context: the context set in the fib - here it is scsi cmd
+ * @fibptr: pointer to the fib
*
- * aac_send_scb_fib
+ * Handles the completion of a native HBA scsi command
+ */
+void aac_hba_callback(void *context, struct fib *fibptr)
+{
+ struct aac_dev *dev;
+ struct scsi_cmnd *scsicmd;
+
+ struct aac_hba_resp *err =
+ &((struct aac_native_hba *)fibptr->hw_fib_va)->resp.err;
+
+ scsicmd = (struct scsi_cmnd *) context;
+
+ if (!aac_valid_context(scsicmd, fibptr))
+ return;
+
+ WARN_ON(fibptr == NULL);
+ dev = fibptr->dev;
+
+ if (!(fibptr->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF))
+ scsi_dma_unmap(scsicmd);
+
+ if (fibptr->flags & FIB_CONTEXT_FLAG_FASTRESP) {
+ /* fast response */
+ scsicmd->result = DID_OK << 16;
+ goto out;
+ }
+
+ switch (err->service_response) {
+ case HBA_RESP_SVCRES_TASK_COMPLETE:
+ hba_resp_task_complete(dev, scsicmd, err);
+ break;
+ case HBA_RESP_SVCRES_FAILURE:
+ hba_resp_task_failure(dev, scsicmd, err);
+ break;
+ case HBA_RESP_SVCRES_TMF_REJECTED:
+ scsicmd->result = DID_ERROR << 16;
+ break;
+ case HBA_RESP_SVCRES_TMF_LUN_INVALID:
+ scsicmd->result = DID_NO_CONNECT << 16;
+ break;
+ case HBA_RESP_SVCRES_TMF_COMPLETE:
+ case HBA_RESP_SVCRES_TMF_SUCCEEDED:
+ scsicmd->result = DID_OK << 16;
+ break;
+ default:
+ scsicmd->result = DID_ERROR << 16;
+ break;
+ }
+
+out:
+ aac_fib_complete(fibptr);
+
+ if (fibptr->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF)
+ aac_priv(scsicmd)->sent_command = 1;
+ else
+ aac_scsi_done(scsicmd);
+}
+
+/**
+ * aac_send_srb_fib
* @scsicmd: the scsi command block
*
* This routine will form a FIB and fill in the aac_srb from the
* scsicmd passed in.
*/
-
static int aac_send_srb_fib(struct scsi_cmnd* scsicmd)
{
struct fib* cmd_fibcontext;
@@ -2787,25 +3719,22 @@ static int aac_send_srb_fib(struct scsi_cmnd* scsicmd)
if (scmd_id(scsicmd) >= dev->maximum_num_physicals ||
scsicmd->device->lun > 7) {
scsicmd->result = DID_NO_CONNECT << 16;
- scsicmd->scsi_done(scsicmd);
+ aac_scsi_done(scsicmd);
return 0;
}
/*
* Allocate and initialize a Fib then setup a BlockWrite command
*/
- if (!(cmd_fibcontext = aac_fib_alloc(dev))) {
- return -1;
- }
+ cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd);
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
status = aac_adapter_scsi(cmd_fibcontext, scsicmd);
/*
* Check that the command queued to the controller
*/
- if (status == -EINPROGRESS) {
- scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
+ if (status == -EINPROGRESS)
return 0;
- }
printk(KERN_WARNING "aac_srb: aac_fib_send failed with status: %d\n", status);
aac_fib_complete(cmd_fibcontext);
@@ -2814,56 +3743,100 @@ static int aac_send_srb_fib(struct scsi_cmnd* scsicmd)
return -1;
}
-static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* psg)
+/**
+ * aac_send_hba_fib
+ * @scsicmd: the scsi command block
+ *
+ * This routine will form a FIB and fill in the aac_hba_cmd_req from the
+ * scsicmd passed in.
+ */
+static int aac_send_hba_fib(struct scsi_cmnd *scsicmd)
{
+ struct fib *cmd_fibcontext;
struct aac_dev *dev;
+ int status;
+
+ dev = shost_priv(scsicmd->device->host);
+ if (scmd_id(scsicmd) >= dev->maximum_num_physicals ||
+ scsicmd->device->lun > AAC_MAX_LUN - 1) {
+ scsicmd->result = DID_NO_CONNECT << 16;
+ aac_scsi_done(scsicmd);
+ return 0;
+ }
+
+ /*
+ * Allocate and initialize a Fib then setup a BlockWrite command
+ */
+ cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd);
+ if (!cmd_fibcontext)
+ return -1;
+
+ aac_priv(scsicmd)->owner = AAC_OWNER_FIRMWARE;
+ status = aac_adapter_hba(cmd_fibcontext, scsicmd);
+
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS)
+ return 0;
+
+ pr_warn("aac_hba_cmd_req: aac_fib_send failed with status: %d\n",
+ status);
+ aac_fib_complete(cmd_fibcontext);
+ aac_fib_free(cmd_fibcontext);
+
+ return -1;
+}
+
+
+static long aac_build_sg(struct scsi_cmnd *scsicmd, struct sgmap *psg)
+{
unsigned long byte_count = 0;
int nseg;
+ struct scatterlist *sg;
+ int i;
- dev = (struct aac_dev *)scsicmd->device->host->hostdata;
// Get rid of old data
psg->count = 0;
psg->sg[0].addr = 0;
psg->sg[0].count = 0;
nseg = scsi_dma_map(scsicmd);
- BUG_ON(nseg < 0);
- if (nseg) {
- struct scatterlist *sg;
- int i;
+ if (nseg <= 0)
+ return nseg;
- psg->count = cpu_to_le32(nseg);
+ psg->count = cpu_to_le32(nseg);
- scsi_for_each_sg(scsicmd, sg, nseg, i) {
- psg->sg[i].addr = cpu_to_le32(sg_dma_address(sg));
- psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
- byte_count += sg_dma_len(sg);
- }
- /* hba wants the size to be exact */
- if (byte_count > scsi_bufflen(scsicmd)) {
- u32 temp = le32_to_cpu(psg->sg[i-1].count) -
- (byte_count - scsi_bufflen(scsicmd));
- psg->sg[i-1].count = cpu_to_le32(temp);
- byte_count = scsi_bufflen(scsicmd);
- }
- /* Check for command underflow */
- if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
- printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
- byte_count, scsicmd->underflow);
- }
+ scsi_for_each_sg(scsicmd, sg, nseg, i) {
+ psg->sg[i].addr = cpu_to_le32(sg_dma_address(sg));
+ psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
+ byte_count += sg_dma_len(sg);
+ }
+ /* hba wants the size to be exact */
+ if (byte_count > scsi_bufflen(scsicmd)) {
+ u32 temp = le32_to_cpu(psg->sg[i-1].count) -
+ (byte_count - scsi_bufflen(scsicmd));
+ psg->sg[i-1].count = cpu_to_le32(temp);
+ byte_count = scsi_bufflen(scsicmd);
+ }
+ /* Check for command underflow */
+ if (scsicmd->underflow && (byte_count < scsicmd->underflow)) {
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
}
+
return byte_count;
}
-static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg)
+static long aac_build_sg64(struct scsi_cmnd *scsicmd, struct sgmap64 *psg)
{
- struct aac_dev *dev;
unsigned long byte_count = 0;
u64 addr;
int nseg;
+ struct scatterlist *sg;
+ int i;
- dev = (struct aac_dev *)scsicmd->device->host->hostdata;
// Get rid of old data
psg->count = 0;
psg->sg[0].addr[0] = 0;
@@ -2871,40 +3844,40 @@ static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* p
psg->sg[0].count = 0;
nseg = scsi_dma_map(scsicmd);
- BUG_ON(nseg < 0);
- if (nseg) {
- struct scatterlist *sg;
- int i;
-
- scsi_for_each_sg(scsicmd, sg, nseg, i) {
- int count = sg_dma_len(sg);
- addr = sg_dma_address(sg);
- psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
- psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
- psg->sg[i].count = cpu_to_le32(count);
- byte_count += count;
- }
- psg->count = cpu_to_le32(nseg);
- /* hba wants the size to be exact */
- if (byte_count > scsi_bufflen(scsicmd)) {
- u32 temp = le32_to_cpu(psg->sg[i-1].count) -
- (byte_count - scsi_bufflen(scsicmd));
- psg->sg[i-1].count = cpu_to_le32(temp);
- byte_count = scsi_bufflen(scsicmd);
- }
- /* Check for command underflow */
- if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
- printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
- byte_count, scsicmd->underflow);
- }
+ if (nseg <= 0)
+ return nseg;
+
+ scsi_for_each_sg(scsicmd, sg, nseg, i) {
+ int count = sg_dma_len(sg);
+ addr = sg_dma_address(sg);
+ psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
+ psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
+ psg->sg[i].count = cpu_to_le32(count);
+ byte_count += count;
+ }
+ psg->count = cpu_to_le32(nseg);
+ /* hba wants the size to be exact */
+ if (byte_count > scsi_bufflen(scsicmd)) {
+ u32 temp = le32_to_cpu(psg->sg[i-1].count) -
+ (byte_count - scsi_bufflen(scsicmd));
+ psg->sg[i-1].count = cpu_to_le32(temp);
+ byte_count = scsi_bufflen(scsicmd);
}
+ /* Check for command underflow */
+ if (scsicmd->underflow && (byte_count < scsicmd->underflow)) {
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+
return byte_count;
}
-static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg)
+static long aac_build_sgraw(struct scsi_cmnd *scsicmd, struct sgmapraw *psg)
{
unsigned long byte_count = 0;
int nseg;
+ struct scatterlist *sg;
+ int i;
// Get rid of old data
psg->count = 0;
@@ -2916,36 +3889,219 @@ static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw*
psg->sg[0].flags = 0;
nseg = scsi_dma_map(scsicmd);
- BUG_ON(nseg < 0);
- if (nseg) {
- struct scatterlist *sg;
- int i;
-
- scsi_for_each_sg(scsicmd, sg, nseg, i) {
- int count = sg_dma_len(sg);
- u64 addr = sg_dma_address(sg);
- psg->sg[i].next = 0;
- psg->sg[i].prev = 0;
- psg->sg[i].addr[1] = cpu_to_le32((u32)(addr>>32));
- psg->sg[i].addr[0] = cpu_to_le32((u32)(addr & 0xffffffff));
- psg->sg[i].count = cpu_to_le32(count);
- psg->sg[i].flags = 0;
- byte_count += count;
+ if (nseg <= 0)
+ return nseg;
+
+ scsi_for_each_sg(scsicmd, sg, nseg, i) {
+ int count = sg_dma_len(sg);
+ u64 addr = sg_dma_address(sg);
+ psg->sg[i].next = 0;
+ psg->sg[i].prev = 0;
+ psg->sg[i].addr[1] = cpu_to_le32((u32)(addr>>32));
+ psg->sg[i].addr[0] = cpu_to_le32((u32)(addr & 0xffffffff));
+ psg->sg[i].count = cpu_to_le32(count);
+ psg->sg[i].flags = 0;
+ byte_count += count;
+ }
+ psg->count = cpu_to_le32(nseg);
+ /* hba wants the size to be exact */
+ if (byte_count > scsi_bufflen(scsicmd)) {
+ u32 temp = le32_to_cpu(psg->sg[i-1].count) -
+ (byte_count - scsi_bufflen(scsicmd));
+ psg->sg[i-1].count = cpu_to_le32(temp);
+ byte_count = scsi_bufflen(scsicmd);
+ }
+ /* Check for command underflow */
+ if (scsicmd->underflow && (byte_count < scsicmd->underflow)) {
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+
+ return byte_count;
+}
+
+static long aac_build_sgraw2(struct scsi_cmnd *scsicmd,
+ struct aac_raw_io2 *rio2, int sg_max)
+{
+ unsigned long byte_count = 0;
+ int nseg;
+ struct scatterlist *sg;
+ int i, conformable = 0;
+ u32 min_size = PAGE_SIZE, cur_size;
+
+ nseg = scsi_dma_map(scsicmd);
+ if (nseg <= 0)
+ return nseg;
+
+ scsi_for_each_sg(scsicmd, sg, nseg, i) {
+ int count = sg_dma_len(sg);
+ u64 addr = sg_dma_address(sg);
+
+ BUG_ON(i >= sg_max);
+ rio2->sge[i].addrHigh = cpu_to_le32((u32)(addr>>32));
+ rio2->sge[i].addrLow = cpu_to_le32((u32)(addr & 0xffffffff));
+ cur_size = cpu_to_le32(count);
+ rio2->sge[i].length = cur_size;
+ rio2->sge[i].flags = 0;
+ if (i == 0) {
+ conformable = 1;
+ rio2->sgeFirstSize = cur_size;
+ } else if (i == 1) {
+ rio2->sgeNominalSize = cur_size;
+ min_size = cur_size;
+ } else if ((i+1) < nseg && cur_size != rio2->sgeNominalSize) {
+ conformable = 0;
+ if (cur_size < min_size)
+ min_size = cur_size;
+ }
+ byte_count += count;
+ }
+
+ /* hba wants the size to be exact */
+ if (byte_count > scsi_bufflen(scsicmd)) {
+ u32 temp = le32_to_cpu(rio2->sge[i-1].length) -
+ (byte_count - scsi_bufflen(scsicmd));
+ rio2->sge[i-1].length = cpu_to_le32(temp);
+ byte_count = scsi_bufflen(scsicmd);
+ }
+
+ rio2->sgeCnt = cpu_to_le32(nseg);
+ rio2->flags |= cpu_to_le16(RIO2_SG_FORMAT_IEEE1212);
+ /* not conformable: evaluate required sg elements */
+ if (!conformable) {
+ int j, nseg_new = nseg, err_found;
+ for (i = min_size / PAGE_SIZE; i >= 1; --i) {
+ err_found = 0;
+ nseg_new = 2;
+ for (j = 1; j < nseg - 1; ++j) {
+ if (rio2->sge[j].length % (i*PAGE_SIZE)) {
+ err_found = 1;
+ break;
+ }
+ nseg_new += (rio2->sge[j].length / (i*PAGE_SIZE));
+ }
+ if (!err_found)
+ break;
}
- psg->count = cpu_to_le32(nseg);
- /* hba wants the size to be exact */
- if (byte_count > scsi_bufflen(scsicmd)) {
- u32 temp = le32_to_cpu(psg->sg[i-1].count) -
- (byte_count - scsi_bufflen(scsicmd));
- psg->sg[i-1].count = cpu_to_le32(temp);
- byte_count = scsi_bufflen(scsicmd);
+ if (i > 0 && nseg_new <= sg_max) {
+ int ret = aac_convert_sgraw2(rio2, i, nseg, nseg_new);
+
+ if (ret < 0)
+ return ret;
}
- /* Check for command underflow */
- if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
- printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
- byte_count, scsicmd->underflow);
+ } else
+ rio2->flags |= cpu_to_le16(RIO2_SGL_CONFORMANT);
+
+ /* Check for command underflow */
+ if (scsicmd->underflow && (byte_count < scsicmd->underflow)) {
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+
+ return byte_count;
+}
+
+static int aac_convert_sgraw2(struct aac_raw_io2 *rio2, int pages, int nseg, int nseg_new)
+{
+ struct sge_ieee1212 *sge;
+ int i, j, pos;
+ u32 addr_low;
+
+ if (aac_convert_sgl == 0)
+ return 0;
+
+ sge = kmalloc_array(nseg_new, sizeof(*sge), GFP_ATOMIC);
+ if (sge == NULL)
+ return -ENOMEM;
+
+ for (i = 1, pos = 1; i < nseg-1; ++i) {
+ for (j = 0; j < rio2->sge[i].length / (pages * PAGE_SIZE); ++j) {
+ addr_low = rio2->sge[i].addrLow + j * pages * PAGE_SIZE;
+ sge[pos].addrLow = addr_low;
+ sge[pos].addrHigh = rio2->sge[i].addrHigh;
+ if (addr_low < rio2->sge[i].addrLow)
+ sge[pos].addrHigh++;
+ sge[pos].length = pages * PAGE_SIZE;
+ sge[pos].flags = 0;
+ pos++;
}
}
+ sge[pos] = rio2->sge[nseg-1];
+ memcpy(&rio2->sge[1], &sge[1], (nseg_new-1)*sizeof(struct sge_ieee1212));
+
+ kfree(sge);
+ rio2->sgeCnt = cpu_to_le32(nseg_new);
+ rio2->flags |= cpu_to_le16(RIO2_SGL_CONFORMANT);
+ rio2->sgeNominalSize = pages * PAGE_SIZE;
+ return 0;
+}
+
+static long aac_build_sghba(struct scsi_cmnd *scsicmd,
+ struct aac_hba_cmd_req *hbacmd,
+ int sg_max,
+ u64 sg_address)
+{
+ unsigned long byte_count = 0;
+ int nseg;
+ struct scatterlist *sg;
+ int i;
+ u32 cur_size;
+ struct aac_hba_sgl *sge;
+
+ nseg = scsi_dma_map(scsicmd);
+ if (nseg <= 0) {
+ byte_count = nseg;
+ goto out;
+ }
+
+ if (nseg > HBA_MAX_SG_EMBEDDED)
+ sge = &hbacmd->sge[2];
+ else
+ sge = &hbacmd->sge[0];
+
+ scsi_for_each_sg(scsicmd, sg, nseg, i) {
+ int count = sg_dma_len(sg);
+ u64 addr = sg_dma_address(sg);
+
+ WARN_ON(i >= sg_max);
+ sge->addr_hi = cpu_to_le32((u32)(addr>>32));
+ sge->addr_lo = cpu_to_le32((u32)(addr & 0xffffffff));
+ cur_size = cpu_to_le32(count);
+ sge->len = cur_size;
+ sge->flags = 0;
+ byte_count += count;
+ sge++;
+ }
+
+ sge--;
+ /* hba wants the size to be exact */
+ if (byte_count > scsi_bufflen(scsicmd)) {
+ u32 temp;
+
+ temp = le32_to_cpu(sge->len) - byte_count
+ - scsi_bufflen(scsicmd);
+ sge->len = cpu_to_le32(temp);
+ byte_count = scsi_bufflen(scsicmd);
+ }
+
+ if (nseg <= HBA_MAX_SG_EMBEDDED) {
+ hbacmd->emb_data_desc_count = cpu_to_le32(nseg);
+ sge->flags = cpu_to_le32(0x40000000);
+ } else {
+ /* not embedded */
+ hbacmd->sge[0].flags = cpu_to_le32(0x80000000);
+ hbacmd->emb_data_desc_count = (u8)cpu_to_le32(1);
+ hbacmd->sge[0].addr_hi = (u32)cpu_to_le32(sg_address >> 32);
+ hbacmd->sge[0].addr_lo =
+ cpu_to_le32((u32)(sg_address & 0xffffffff));
+ }
+
+ /* Check for command underflow */
+ if (scsicmd->underflow && (byte_count < scsicmd->underflow)) {
+ pr_warn("aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+out:
return byte_count;
}
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index ffb587817efc..0a5888b53d6d 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -1,3 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000-2010 Adaptec, Inc.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
+ *
+ * Module Name:
+ * aacraid.h
+ *
+ * Abstract: Contains all routines for control of the aacraid driver
+ */
+
+#ifndef _AACRAID_H_
+#define _AACRAID_H_
#ifndef dprintk
# define dprintk(x)
#endif
@@ -6,39 +26,409 @@
#define nblank(x) _nblank(x)[0]
#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/pci.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
/*------------------------------------------------------------------------------
* D E F I N E S
*----------------------------------------------------------------------------*/
+#define AAC_MAX_MSIX 32 /* vectors */
+#define AAC_PCI_MSI_ENABLE 0x8000
+
+enum {
+ AAC_ENABLE_INTERRUPT = 0x0,
+ AAC_DISABLE_INTERRUPT,
+ AAC_ENABLE_MSIX,
+ AAC_DISABLE_MSIX,
+ AAC_CLEAR_AIF_BIT,
+ AAC_CLEAR_SYNC_BIT,
+ AAC_ENABLE_INTX
+};
+
+#define AAC_INT_MODE_INTX (1<<0)
+#define AAC_INT_MODE_MSI (1<<1)
+#define AAC_INT_MODE_AIF (1<<2)
+#define AAC_INT_MODE_SYNC (1<<3)
+#define AAC_INT_MODE_MSIX (1<<16)
+
+#define AAC_INT_ENABLE_TYPE1_INTX 0xfffffffb
+#define AAC_INT_ENABLE_TYPE1_MSIX 0xfffffffa
+#define AAC_INT_DISABLE_ALL 0xffffffff
+
+/* Bit definitions in IOA->Host Interrupt Register */
+#define PMC_TRANSITION_TO_OPERATIONAL (1<<31)
+#define PMC_IOARCB_TRANSFER_FAILED (1<<28)
+#define PMC_IOA_UNIT_CHECK (1<<27)
+#define PMC_NO_HOST_RRQ_FOR_CMD_RESPONSE (1<<26)
+#define PMC_CRITICAL_IOA_OP_IN_PROGRESS (1<<25)
+#define PMC_IOARRIN_LOST (1<<4)
+#define PMC_SYSTEM_BUS_MMIO_ERROR (1<<3)
+#define PMC_IOA_PROCESSOR_IN_ERROR_STATE (1<<2)
+#define PMC_HOST_RRQ_VALID (1<<1)
+#define PMC_OPERATIONAL_STATUS (1<<31)
+#define PMC_ALLOW_MSIX_VECTOR0 (1<<0)
+
+#define PMC_IOA_ERROR_INTERRUPTS (PMC_IOARCB_TRANSFER_FAILED | \
+ PMC_IOA_UNIT_CHECK | \
+ PMC_NO_HOST_RRQ_FOR_CMD_RESPONSE | \
+ PMC_IOARRIN_LOST | \
+ PMC_SYSTEM_BUS_MMIO_ERROR | \
+ PMC_IOA_PROCESSOR_IN_ERROR_STATE)
+
+#define PMC_ALL_INTERRUPT_BITS (PMC_IOA_ERROR_INTERRUPTS | \
+ PMC_HOST_RRQ_VALID | \
+ PMC_TRANSITION_TO_OPERATIONAL | \
+ PMC_ALLOW_MSIX_VECTOR0)
+#define PMC_GLOBAL_INT_BIT2 0x00000004
+#define PMC_GLOBAL_INT_BIT0 0x00000001
+
#ifndef AAC_DRIVER_BUILD
-# define AAC_DRIVER_BUILD 28000
-# define AAC_DRIVER_BRANCH "-ms"
+# define AAC_DRIVER_BUILD 50983
+# define AAC_DRIVER_BRANCH "-custom"
#endif
#define MAXIMUM_NUM_CONTAINERS 32
#define AAC_NUM_MGT_FIB 8
-#define AAC_NUM_IO_FIB (512 - AAC_NUM_MGT_FIB)
-#define AAC_NUM_FIB (AAC_NUM_IO_FIB + AAC_NUM_MGT_FIB)
+#define AAC_NUM_IO_FIB (1024 - AAC_NUM_MGT_FIB)
-#define AAC_MAX_LUN (8)
+#define AAC_MAX_LUN 256
#define AAC_MAX_HOSTPHYSMEMPAGES (0xfffff)
#define AAC_MAX_32BIT_SGBCOUNT ((unsigned short)256)
#define AAC_DEBUG_INSTRUMENT_AIF_DELETE
+#define AAC_MAX_NATIVE_TARGETS 1024
+/* Thor: 5 phys. buses: #0: empty, 1-4: 256 targets each */
+#define AAC_MAX_BUSES 5
+#define AAC_MAX_TARGETS 256
+#define AAC_BUS_TARGET_LOOP (AAC_MAX_BUSES * AAC_MAX_TARGETS)
+#define AAC_MAX_NATIVE_SIZE 2048
+#define FW_ERROR_BUFFER_SIZE 512
+#define AAC_SA_TIMEOUT 180
+#define AAC_ARC_TIMEOUT 60
+
+#define get_bus_number(x) (x/AAC_MAX_TARGETS)
+#define get_target_number(x) (x%AAC_MAX_TARGETS)
+
+/* Thor AIF events */
+#define SA_AIF_HOTPLUG (1<<1)
+#define SA_AIF_HARDWARE (1<<2)
+#define SA_AIF_PDEV_CHANGE (1<<4)
+#define SA_AIF_LDEV_CHANGE (1<<5)
+#define SA_AIF_BPSTAT_CHANGE (1<<30)
+#define SA_AIF_BPCFG_CHANGE (1U<<31)
+
+#define HBA_MAX_SG_EMBEDDED 28
+#define HBA_MAX_SG_SEPARATE 90
+#define HBA_SENSE_DATA_LEN_MAX 32
+#define HBA_REQUEST_TAG_ERROR_FLAG 0x00000002
+#define HBA_SGL_FLAGS_EXT 0x80000000UL
+
+struct aac_hba_sgl {
+ u32 addr_lo; /* Lower 32-bits of SGL element address */
+ u32 addr_hi; /* Upper 32-bits of SGL element address */
+ u32 len; /* Length of SGL element in bytes */
+ u32 flags; /* SGL element flags */
+};
+
+enum {
+ HBA_IU_TYPE_SCSI_CMD_REQ = 0x40,
+ HBA_IU_TYPE_SCSI_TM_REQ = 0x41,
+ HBA_IU_TYPE_SATA_REQ = 0x42,
+ HBA_IU_TYPE_RESP = 0x60,
+ HBA_IU_TYPE_COALESCED_RESP = 0x61,
+ HBA_IU_TYPE_INT_COALESCING_CFG_REQ = 0x70
+};
+
+enum {
+ HBA_CMD_BYTE1_DATA_DIR_IN = 0x1,
+ HBA_CMD_BYTE1_DATA_DIR_OUT = 0x2,
+ HBA_CMD_BYTE1_DATA_TYPE_DDR = 0x4,
+ HBA_CMD_BYTE1_CRYPTO_ENABLE = 0x8
+};
+
+enum {
+ HBA_CMD_BYTE1_BITOFF_DATA_DIR_IN = 0x0,
+ HBA_CMD_BYTE1_BITOFF_DATA_DIR_OUT,
+ HBA_CMD_BYTE1_BITOFF_DATA_TYPE_DDR,
+ HBA_CMD_BYTE1_BITOFF_CRYPTO_ENABLE
+};
+
+enum {
+ HBA_RESP_DATAPRES_NO_DATA = 0x0,
+ HBA_RESP_DATAPRES_RESPONSE_DATA,
+ HBA_RESP_DATAPRES_SENSE_DATA
+};
+
+enum {
+ HBA_RESP_SVCRES_TASK_COMPLETE = 0x0,
+ HBA_RESP_SVCRES_FAILURE,
+ HBA_RESP_SVCRES_TMF_COMPLETE,
+ HBA_RESP_SVCRES_TMF_SUCCEEDED,
+ HBA_RESP_SVCRES_TMF_REJECTED,
+ HBA_RESP_SVCRES_TMF_LUN_INVALID
+};
+
+enum {
+ HBA_RESP_STAT_IO_ERROR = 0x1,
+ HBA_RESP_STAT_IO_ABORTED,
+ HBA_RESP_STAT_NO_PATH_TO_DEVICE,
+ HBA_RESP_STAT_INVALID_DEVICE,
+ HBA_RESP_STAT_HBAMODE_DISABLED = 0xE,
+ HBA_RESP_STAT_UNDERRUN = 0x51,
+ HBA_RESP_STAT_OVERRUN = 0x75
+};
+
+struct aac_hba_cmd_req {
+ u8 iu_type; /* HBA information unit type */
+ /*
+ * byte1:
+ * [1:0] DIR - 0=No data, 0x1 = IN, 0x2 = OUT
+ * [2] TYPE - 0=PCI, 1=DDR
+ * [3] CRYPTO_ENABLE - 0=Crypto disabled, 1=Crypto enabled
+ */
+ u8 byte1;
+ u8 reply_qid; /* Host reply queue to post response to */
+ u8 reserved1;
+ __le32 it_nexus; /* Device handle for the request */
+ __le32 request_id; /* Sender context */
+ /* Lower 32-bits of tweak value for crypto enabled IOs */
+ __le32 tweak_value_lo;
+ u8 cdb[16]; /* SCSI CDB of the command */
+ u8 lun[8]; /* SCSI LUN of the command */
+
+ /* Total data length in bytes to be read/written (if any) */
+ __le32 data_length;
+
+ /* [2:0] Task Attribute, [6:3] Command Priority */
+ u8 attr_prio;
+
+ /* Number of SGL elements embedded in the HBA req */
+ u8 emb_data_desc_count;
+
+ __le16 dek_index; /* DEK index for crypto enabled IOs */
+
+ /* Lower 32-bits of reserved error data target location on the host */
+ __le32 error_ptr_lo;
+
+ /* Upper 32-bits of reserved error data target location on the host */
+ __le32 error_ptr_hi;
+
+ /* Length of reserved error data area on the host in bytes */
+ __le32 error_length;
+
+ /* Upper 32-bits of tweak value for crypto enabled IOs */
+ __le32 tweak_value_hi;
+
+ struct aac_hba_sgl sge[HBA_MAX_SG_SEPARATE+2]; /* SG list space */
+
+ /*
+ * structure must not exceed
+ * AAC_MAX_NATIVE_SIZE-FW_ERROR_BUFFER_SIZE
+ */
+};
+
+/* Task Management Functions (TMF) */
+#define HBA_TMF_ABORT_TASK 0x01
+#define HBA_TMF_LUN_RESET 0x08
+
+struct aac_hba_tm_req {
+ u8 iu_type; /* HBA information unit type */
+ u8 reply_qid; /* Host reply queue to post response to */
+ u8 tmf; /* Task management function */
+ u8 reserved1;
+
+ __le32 it_nexus; /* Device handle for the command */
+
+ u8 lun[8]; /* SCSI LUN */
+
+ /* Used to hold sender context. */
+ __le32 request_id; /* Sender context */
+ __le32 reserved2;
+
+ /* Request identifier of managed task */
+ __le32 managed_request_id; /* Sender context being managed */
+ __le32 reserved3;
+
+ /* Lower 32-bits of reserved error data target location on the host */
+ __le32 error_ptr_lo;
+ /* Upper 32-bits of reserved error data target location on the host */
+ __le32 error_ptr_hi;
+ /* Length of reserved error data area on the host in bytes */
+ __le32 error_length;
+};
+
+struct aac_hba_reset_req {
+ u8 iu_type; /* HBA information unit type */
+ /* 0 - reset specified device, 1 - reset all devices */
+ u8 reset_type;
+ u8 reply_qid; /* Host reply queue to post response to */
+ u8 reserved1;
+
+ __le32 it_nexus; /* Device handle for the command */
+ __le32 request_id; /* Sender context */
+ /* Lower 32-bits of reserved error data target location on the host */
+ __le32 error_ptr_lo;
+ /* Upper 32-bits of reserved error data target location on the host */
+ __le32 error_ptr_hi;
+ /* Length of reserved error data area on the host in bytes */
+ __le32 error_length;
+};
+
+struct aac_hba_resp {
+ u8 iu_type; /* HBA information unit type */
+ u8 reserved1[3];
+ __le32 request_identifier; /* sender context */
+ __le32 reserved2;
+ u8 service_response; /* SCSI service response */
+ u8 status; /* SCSI status */
+ u8 datapres; /* [1:0] - data present, [7:2] - reserved */
+ u8 sense_response_data_len; /* Sense/response data length */
+ __le32 residual_count; /* Residual data length in bytes */
+ /* Sense/response data */
+ u8 sense_response_buf[HBA_SENSE_DATA_LEN_MAX];
+};
+
+struct aac_native_hba {
+ union {
+ struct aac_hba_cmd_req cmd;
+ struct aac_hba_tm_req tmr;
+ u8 cmd_bytes[AAC_MAX_NATIVE_SIZE-FW_ERROR_BUFFER_SIZE];
+ } cmd;
+ union {
+ struct aac_hba_resp err;
+ u8 resp_bytes[FW_ERROR_BUFFER_SIZE];
+ } resp;
+};
+
+#define CISS_REPORT_PHYSICAL_LUNS 0xc3
+#define WRITE_HOST_WELLNESS 0xa5
+#define CISS_IDENTIFY_PHYSICAL_DEVICE 0x15
+#define BMIC_IN 0x26
+#define BMIC_OUT 0x27
+
+struct aac_ciss_phys_luns_resp {
+ u8 list_length[4]; /* LUN list length (N-7, big endian) */
+ u8 resp_flag; /* extended response_flag */
+ u8 reserved[3];
+ struct _ciss_lun {
+ u8 tid[3]; /* Target ID */
+ u8 bus; /* Bus, flag (bits 6,7) */
+ u8 level3[2];
+ u8 level2[2];
+ u8 node_ident[16]; /* phys. node identifier */
+ } lun[]; /* List of phys. devices */
+};
+
+/*
+ * Interrupts
+ */
+#define AAC_MAX_HRRQ 64
+
+struct aac_ciss_identify_pd {
+ u8 scsi_bus; /* SCSI Bus number on controller */
+ u8 scsi_id; /* SCSI ID on this bus */
+ u16 block_size; /* sector size in bytes */
+ u32 total_blocks; /* number for sectors on drive */
+ u32 reserved_blocks; /* controller reserved (RIS) */
+ u8 model[40]; /* Physical Drive Model */
+ u8 serial_number[40]; /* Drive Serial Number */
+ u8 firmware_revision[8]; /* drive firmware revision */
+ u8 scsi_inquiry_bits; /* inquiry byte 7 bits */
+ u8 compaq_drive_stamp; /* 0 means drive not stamped */
+ u8 last_failure_reason;
+
+ u8 flags;
+ u8 more_flags;
+ u8 scsi_lun; /* SCSI LUN for phys drive */
+ u8 yet_more_flags;
+ u8 even_more_flags;
+ u32 spi_speed_rules; /* SPI Speed :Ultra disable diagnose */
+ u8 phys_connector[2]; /* connector number on controller */
+ u8 phys_box_on_bus; /* phys enclosure this drive resides */
+ u8 phys_bay_in_box; /* phys drv bay this drive resides */
+ u32 rpm; /* Drive rotational speed in rpm */
+ u8 device_type; /* type of drive */
+ u8 sata_version; /* only valid when drive_type is SATA */
+ u64 big_total_block_count;
+ u64 ris_starting_lba;
+ u32 ris_size;
+ u8 wwid[20];
+ u8 controller_phy_map[32];
+ u16 phy_count;
+ u8 phy_connected_dev_type[256];
+ u8 phy_to_drive_bay_num[256];
+ u16 phy_to_attached_dev_index[256];
+ u8 box_index;
+ u8 spitfire_support;
+ u16 extra_physical_drive_flags;
+ u8 negotiated_link_rate[256];
+ u8 phy_to_phy_map[256];
+ u8 redundant_path_present_map;
+ u8 redundant_path_failure_map;
+ u8 active_path_number;
+ u16 alternate_paths_phys_connector[8];
+ u8 alternate_paths_phys_box_on_port[8];
+ u8 multi_lun_device_lun_count;
+ u8 minimum_good_fw_revision[8];
+ u8 unique_inquiry_bytes[20];
+ u8 current_temperature_degreesC;
+ u8 temperature_threshold_degreesC;
+ u8 max_temperature_degreesC;
+ u8 logical_blocks_per_phys_block_exp; /* phyblocksize = 512 * 2^exp */
+ u16 current_queue_depth_limit;
+ u8 switch_name[10];
+ u16 switch_port;
+ u8 alternate_paths_switch_name[40];
+ u8 alternate_paths_switch_port[8];
+ u16 power_on_hours; /* valid only if gas gauge supported */
+ u16 percent_endurance_used; /* valid only if gas gauge supported. */
+ u8 drive_authentication;
+ u8 smart_carrier_authentication;
+ u8 smart_carrier_app_fw_version;
+ u8 smart_carrier_bootloader_fw_version;
+ u8 SanitizeSecureEraseSupport;
+ u8 DriveKeyFlags;
+ u8 encryption_key_name[64];
+ u32 misc_drive_flags;
+ u16 dek_index;
+ u16 drive_encryption_flags;
+ u8 sanitize_maximum_time[6];
+ u8 connector_info_mode;
+ u8 connector_info_number[4];
+ u8 long_connector_name[64];
+ u8 device_unique_identifier[16];
+ u8 padto_2K[17];
+} __packed;
+
/*
* These macros convert from physical channels to virtual channels
*/
#define CONTAINER_CHANNEL (0)
+#define NATIVE_CHANNEL (1)
#define CONTAINER_TO_CHANNEL(cont) (CONTAINER_CHANNEL)
#define CONTAINER_TO_ID(cont) (cont)
#define CONTAINER_TO_LUN(cont) (0)
+#define ENCLOSURE_CHANNEL (3)
+
+#define PMC_DEVICE_S6 0x28b
+#define PMC_DEVICE_S7 0x28c
+#define PMC_DEVICE_S8 0x28d
#define aac_phys_to_logical(x) ((x)+1)
#define aac_logical_to_phys(x) ((x)?(x)-1:0)
+/*
+ * These macros are for keeping track of
+ * character device state.
+ */
+#define AAC_CHARDEV_UNREGISTERED (-1)
+#define AAC_CHARDEV_NEEDS_REINIT (-2)
+
/* #define AAC_DETAILED_STATUS_INFO */
struct diskparm
@@ -100,6 +490,13 @@ struct user_sgentryraw {
u32 flags; /* reserved for F/W use */
};
+struct sge_ieee1212 {
+ u32 addrLow;
+ u32 addrHigh;
+ u32 length;
+ u32 flags;
+};
+
/*
* SGMAP
*
@@ -109,32 +506,27 @@ struct user_sgentryraw {
struct sgmap {
__le32 count;
- struct sgentry sg[1];
+ struct sgentry sg[];
};
struct user_sgmap {
u32 count;
- struct user_sgentry sg[1];
+ struct user_sgentry sg[];
};
struct sgmap64 {
__le32 count;
- struct sgentry64 sg[1];
+ struct sgentry64 sg[];
};
struct user_sgmap64 {
u32 count;
- struct user_sgentry64 sg[1];
+ struct user_sgentry64 sg[];
};
struct sgmapraw {
__le32 count;
- struct sgentryraw sg[1];
-};
-
-struct user_sgmapraw {
- u32 count;
- struct user_sgentryraw sg[1];
+ struct sgentryraw sg[];
};
struct creation_info
@@ -270,6 +662,8 @@ enum aac_queue_types {
*/
#define FIB_MAGIC 0x0001
+#define FIB_MAGIC2 0x0004
+#define FIB_MAGIC2_64 0x0005
/*
* Define the priority levels the FSA communication routines support.
@@ -279,10 +673,10 @@ enum aac_queue_types {
/* transport FIB header (PMC) */
struct aac_fib_xporthdr {
- u64 HostAddress; /* FIB host address w/o xport header */
- u32 Size; /* FIB size excluding xport header */
- u32 Handle; /* driver handle to reference the FIB */
- u64 Reserved[2];
+ __le64 HostAddress; /* FIB host address w/o xport header */
+ __le32 Size; /* FIB size excluding xport header */
+ __le32 Handle; /* driver handle to reference the FIB */
+ __le64 Reserved[2];
};
#define ALIGN32 32
@@ -296,22 +690,20 @@ struct aac_fibhdr {
__le32 XferState; /* Current transfer state for this CCB */
__le16 Command; /* Routing information for the destination */
u8 StructType; /* Type FIB */
- u8 Flags; /* Flags for FIB */
+ u8 Unused; /* Unused */
__le16 Size; /* Size of this FIB in bytes */
__le16 SenderSize; /* Size of the FIB in the sender
(for response sizing) */
__le32 SenderFibAddress; /* Host defined data in the FIB */
- __le32 ReceiverFibAddress;/* Logical address of this FIB for
- the adapter */
- u32 SenderData; /* Place holder for the sender to store data */
union {
- struct {
- __le32 _ReceiverTimeStart; /* Timestamp for
- receipt of fib */
- __le32 _ReceiverTimeDone; /* Timestamp for
- completion of fib */
- } _s;
- } _u;
+ __le32 ReceiverFibAddress;/* Logical address of this FIB for
+ the adapter (old) */
+ __le32 SenderFibAddressHigh;/* upper 32bit of phys. FIB address */
+ __le32 TimeStamp; /* otherwise timestamp for FW internal use */
+ } u;
+ __le32 Handle; /* FIB handle used for MSGU commnunication */
+ u32 Previous; /* FW internal use */
+ u32 Next; /* FW internal use */
};
struct hw_fib {
@@ -361,6 +753,7 @@ struct hw_fib {
#define ContainerCommand 500
#define ContainerCommand64 501
#define ContainerRawIo 502
+#define ContainerRawIo2 503
/*
* Scsi Port commands (scsi passthrough)
*/
@@ -417,39 +810,65 @@ enum fib_xfer_state {
#define ADAPTER_INIT_STRUCT_REVISION 3
#define ADAPTER_INIT_STRUCT_REVISION_4 4 // rocket science
#define ADAPTER_INIT_STRUCT_REVISION_6 6 /* PMC src */
+#define ADAPTER_INIT_STRUCT_REVISION_7 7 /* Denali */
+#define ADAPTER_INIT_STRUCT_REVISION_8 8 // Thor
-struct aac_init
+union aac_init
{
- __le32 InitStructRevision;
- __le32 MiniPortRevision;
- __le32 fsrev;
- __le32 CommHeaderAddress;
- __le32 FastIoCommAreaAddress;
- __le32 AdapterFibsPhysicalAddress;
- __le32 AdapterFibsVirtualAddress;
- __le32 AdapterFibsSize;
- __le32 AdapterFibAlign;
- __le32 printfbuf;
- __le32 printfbufsiz;
- __le32 HostPhysMemPages; /* number of 4k pages of host
- physical memory */
- __le32 HostElapsedSeconds; /* number of seconds since 1970. */
- /*
- * ADAPTER_INIT_STRUCT_REVISION_4 begins here
- */
- __le32 InitFlags; /* flags for supported features */
+ struct _r7 {
+ __le32 init_struct_revision;
+ __le32 no_of_msix_vectors;
+ __le32 fsrev;
+ __le32 comm_header_address;
+ __le32 fast_io_comm_area_address;
+ __le32 adapter_fibs_physical_address;
+ __le32 adapter_fibs_virtual_address;
+ __le32 adapter_fibs_size;
+ __le32 adapter_fib_align;
+ __le32 printfbuf;
+ __le32 printfbufsiz;
+ /* number of 4k pages of host phys. mem. */
+ __le32 host_phys_mem_pages;
+ /* number of seconds since 1970. */
+ __le32 host_elapsed_seconds;
+ /* ADAPTER_INIT_STRUCT_REVISION_4 begins here */
+ __le32 init_flags; /* flags for supported features */
#define INITFLAGS_NEW_COMM_SUPPORTED 0x00000001
#define INITFLAGS_DRIVER_USES_UTC_TIME 0x00000010
#define INITFLAGS_DRIVER_SUPPORTS_PM 0x00000020
-#define INITFLAGS_NEW_COMM_TYPE1_SUPPORTED 0x00000041
- __le32 MaxIoCommands; /* max outstanding commands */
- __le32 MaxIoSize; /* largest I/O command */
- __le32 MaxFibSize; /* largest FIB to adapter */
- /* ADAPTER_INIT_STRUCT_REVISION_5 begins here */
- __le32 MaxNumAif; /* max number of aif */
- /* ADAPTER_INIT_STRUCT_REVISION_6 begins here */
- __le32 HostRRQ_AddrLow;
- __le32 HostRRQ_AddrHigh; /* Host RRQ (response queue) for SRC */
+#define INITFLAGS_NEW_COMM_TYPE1_SUPPORTED 0x00000040
+#define INITFLAGS_FAST_JBOD_SUPPORTED 0x00000080
+#define INITFLAGS_NEW_COMM_TYPE2_SUPPORTED 0x00000100
+#define INITFLAGS_DRIVER_SUPPORTS_HBA_MODE 0x00000400
+ __le32 max_io_commands; /* max outstanding commands */
+ __le32 max_io_size; /* largest I/O command */
+ __le32 max_fib_size; /* largest FIB to adapter */
+ /* ADAPTER_INIT_STRUCT_REVISION_5 begins here */
+ __le32 max_num_aif; /* max number of aif */
+ /* ADAPTER_INIT_STRUCT_REVISION_6 begins here */
+ /* Host RRQ (response queue) for SRC */
+ __le32 host_rrq_addr_low;
+ __le32 host_rrq_addr_high;
+ } r7;
+ struct _r8 {
+ /* ADAPTER_INIT_STRUCT_REVISION_8 */
+ __le32 init_struct_revision;
+ __le32 rr_queue_count;
+ __le32 host_elapsed_seconds; /* number of secs since 1970. */
+ __le32 init_flags;
+ __le32 max_io_size; /* largest I/O command */
+ __le32 max_num_aif; /* max number of aif */
+ __le32 reserved1;
+ __le32 reserved2;
+ struct _rrq {
+ __le32 host_addr_low;
+ __le32 host_addr_high;
+ __le16 msix_id;
+ __le16 element_count;
+ __le16 comp_thresh;
+ __le16 unused;
+ } rrq[] __counted_by_le(rr_queue_count); /* up to 64 RRQ addresses */
+ } r8;
};
enum aac_log_level {
@@ -480,7 +899,8 @@ struct adapter_ops
void (*adapter_enable_int)(struct aac_dev *dev);
int (*adapter_sync_cmd)(struct aac_dev *dev, u32 command, u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, u32 *status, u32 *r1, u32 *r2, u32 *r3, u32 *r4);
int (*adapter_check_health)(struct aac_dev *dev);
- int (*adapter_restart)(struct aac_dev *dev, int bled);
+ int (*adapter_restart)(struct aac_dev *dev, int bled, u8 reset_type);
+ void (*adapter_start)(struct aac_dev *dev);
/* Transport operations */
int (*adapter_ioremap)(struct aac_dev * dev, u32 size);
irq_handler_t adapter_intr;
@@ -546,6 +966,11 @@ struct aac_driver_ident
#define AAC_QUIRK_SCSI_32 0x0020
/*
+ * SRC based adapters support the AifReqEvent functions
+ */
+#define AAC_QUIRK_SRC 0x0040
+
+/*
* The adapter interface specs all queues to be located in the same
* physically contiguous block. The host structure that defines the
* commuication queues will assume they are each a separate physically
@@ -567,7 +992,8 @@ struct aac_queue {
spinlock_t lockdata; /* Actual lock (used only on one side of the lock) */
struct list_head cmdq; /* A queue of FIBs which need to be prcessed by the FS thread. This is */
/* only valid for command queues which receive entries from the adapter. */
- u32 numpending; /* Number of entries on outstanding queue. */
+ /* Number of entries on outstanding queue. */
+ atomic_t numpending;
struct aac_dev * dev; /* Back pointer to adapter structure */
};
@@ -645,7 +1071,8 @@ struct sa_registers {
};
-#define Sa_MINIPORT_REVISION 1
+#define SA_INIT_NUM_MSIXVECTORS 1
+#define SA_MINIPORT_REVISION SA_INIT_NUM_MSIXVECTORS
#define sa_readw(AEP, CSR) readl(&((AEP)->regs.sa->CSR))
#define sa_readl(AEP, CSR) readl(&((AEP)->regs.sa->CSR))
@@ -739,25 +1166,40 @@ struct rkt_registers {
#define src_inbound rx_inbound
struct src_mu_registers {
- /* PCI*| Name */
- __le32 reserved0[8]; /* 00h | Reserved */
- __le32 IDR; /* 20h | Inbound Doorbell Register */
- __le32 IISR; /* 24h | Inbound Int. Status Register */
- __le32 reserved1[3]; /* 28h | Reserved */
- __le32 OIMR; /* 34h | Outbound Int. Mask Register */
- __le32 reserved2[25]; /* 38h | Reserved */
- __le32 ODR_R; /* 9ch | Outbound Doorbell Read */
- __le32 ODR_C; /* a0h | Outbound Doorbell Clear */
- __le32 reserved3[6]; /* a4h | Reserved */
- __le32 OMR; /* bch | Outbound Message Register */
+ /* PCI*| Name */
+ __le32 reserved0[6]; /* 00h | Reserved */
+ __le32 IOAR[2]; /* 18h | IOA->host interrupt register */
+ __le32 IDR; /* 20h | Inbound Doorbell Register */
+ __le32 IISR; /* 24h | Inbound Int. Status Register */
+ __le32 reserved1[3]; /* 28h | Reserved */
+ __le32 OIMR; /* 34h | Outbound Int. Mask Register */
+ __le32 reserved2[25]; /* 38h | Reserved */
+ __le32 ODR_R; /* 9ch | Outbound Doorbell Read */
+ __le32 ODR_C; /* a0h | Outbound Doorbell Clear */
+ __le32 reserved3[3]; /* a4h | Reserved */
+ __le32 SCR0; /* b0h | Scratchpad 0 */
+ __le32 reserved4[2]; /* b4h | Reserved */
+ __le32 OMR; /* bch | Outbound Message Register */
__le32 IQ_L; /* c0h | Inbound Queue (Low address) */
__le32 IQ_H; /* c4h | Inbound Queue (High address) */
+ __le32 ODR_MSI; /* c8h | MSI register for sync./AIF */
+ __le32 reserved5; /* cch | Reserved */
+ __le32 IQN_L; /* d0h | Inbound (native cmd) low */
+ __le32 IQN_H; /* d4h | Inbound (native cmd) high */
};
struct src_registers {
- struct src_mu_registers MUnit; /* 00h - c7h */
- __le32 reserved1[130790]; /* c8h - 7fc5fh */
- struct src_inbound IndexRegs; /* 7fc60h */
+ struct src_mu_registers MUnit; /* 00h - cbh */
+ union {
+ struct {
+ __le32 reserved1[130786]; /* d8h - 7fc5fh */
+ struct src_inbound IndexRegs; /* 7fc60h */
+ } tupelo;
+ struct {
+ __le32 reserved1[970]; /* d8h - fffh */
+ struct src_inbound IndexRegs; /* 1000h */
+ } denali;
+ } u;
};
#define src_readb(AEP, CSR) readb(&((AEP)->regs.src.bar0->CSR))
@@ -766,9 +1208,14 @@ struct src_registers {
&((AEP)->regs.src.bar0->CSR))
#define src_writel(AEP, CSR, value) writel(value, \
&((AEP)->regs.src.bar0->CSR))
+#if defined(writeq)
+#define src_writeq(AEP, CSR, value) writeq(value, \
+ &((AEP)->regs.src.bar0->CSR))
+#endif
#define SRC_ODR_SHIFT 12
#define SRC_IDR_SHIFT 9
+#define SRC_MSI_READ_MASK 0x1000
typedef void (*fib_callback)(void *ctxt, struct fib *fibctx);
@@ -778,7 +1225,7 @@ struct aac_fib_context {
u32 unique; // unique value representing this context
ulong jiffies; // used for cleanup - dmb changed to ulong
struct list_head next; // used to link context's into a linked list
- struct semaphore wait_sem; // this is used to wait for the next fib to arrive.
+ struct completion completion; // this is used to wait for the next fib to arrive.
int wait; // Set to true when thread is in WaitForSingleObject
unsigned long count; // total number of FIBs on FibList
struct list_head fib_list; // this holds fibs and their attachd hw_fibs
@@ -834,6 +1281,8 @@ struct fsa_dev_info {
u8 deleted;
char devname[8];
struct sense_data sense_data;
+ u32 block_size;
+ u8 identifier[16];
};
struct fib {
@@ -848,7 +1297,7 @@ struct fib {
* This is the event the sendfib routine will wait on if the
* caller did not pass one and this is synch io.
*/
- struct semaphore event_wait;
+ struct completion event_wait;
spinlock_t event_lock;
u32 done; /* gets set to 1 when fib is complete */
@@ -861,8 +1310,31 @@ struct fib {
*/
struct list_head fiblink;
void *data;
- struct hw_fib *hw_fib_va; /* Actual shared object */
- dma_addr_t hw_fib_pa; /* physical address of hw_fib*/
+ u32 vector_no;
+ struct hw_fib *hw_fib_va; /* also used for native */
+ dma_addr_t hw_fib_pa; /* physical address of hw_fib*/
+ dma_addr_t hw_sgl_pa; /* extra sgl for native */
+ dma_addr_t hw_error_pa; /* error buffer for native */
+ u32 hbacmd_size; /* cmd size for native */
+};
+
+#define AAC_INIT 0
+#define AAC_RESCAN 1
+
+#define AAC_DEVTYPE_RAID_MEMBER 1
+#define AAC_DEVTYPE_ARC_RAW 2
+#define AAC_DEVTYPE_NATIVE_RAW 3
+
+#define AAC_RESCAN_DELAY (10 * HZ)
+
+struct aac_hba_map_info {
+ __le32 rmw_nexus; /* nexus for native HBA devices */
+ u8 devtype; /* device type */
+ s8 reset_state; /* 0 - no reset, 1..x - */
+ /* after xth TM LUN reset */
+ u16 qd_limit;
+ u32 scan_counter;
+ struct aac_ciss_identify_pd *safw_identify_resp;
};
/*
@@ -898,37 +1370,58 @@ struct aac_adapter_info
struct aac_supplement_adapter_info
{
- u8 AdapterTypeText[17+1];
- u8 Pad[2];
- __le32 FlashMemoryByteSize;
- __le32 FlashImageId;
- __le32 MaxNumberPorts;
- __le32 Version;
- __le32 FeatureBits;
- u8 SlotNumber;
- u8 ReservedPad0[3];
- u8 BuildDate[12];
- __le32 CurrentNumberPorts;
+ u8 adapter_type_text[17+1];
+ u8 pad[2];
+ __le32 flash_memory_byte_size;
+ __le32 flash_image_id;
+ __le32 max_number_ports;
+ __le32 version;
+ __le32 feature_bits;
+ u8 slot_number;
+ u8 reserved_pad0[3];
+ u8 build_date[12];
+ __le32 current_number_ports;
struct {
- u8 AssemblyPn[8];
- u8 FruPn[8];
- u8 BatteryFruPn[8];
- u8 EcVersionString[8];
- u8 Tsid[12];
- } VpdInfo;
- __le32 FlashFirmwareRevision;
- __le32 FlashFirmwareBuild;
- __le32 RaidTypeMorphOptions;
- __le32 FlashFirmwareBootRevision;
- __le32 FlashFirmwareBootBuild;
- u8 MfgPcbaSerialNo[12];
- u8 MfgWWNName[8];
- __le32 SupportedOptions2;
- __le32 StructExpansion;
+ u8 assembly_pn[8];
+ u8 fru_pn[8];
+ u8 battery_fru_pn[8];
+ u8 ec_version_string[8];
+ u8 tsid[12];
+ } vpd_info;
+ __le32 flash_firmware_revision;
+ __le32 flash_firmware_build;
+ __le32 raid_type_morph_options;
+ __le32 flash_firmware_boot_revision;
+ __le32 flash_firmware_boot_build;
+ u8 mfg_pcba_serial_no[12];
+ u8 mfg_wwn_name[8];
+ __le32 supported_options2;
+ __le32 struct_expansion;
/* StructExpansion == 1 */
- __le32 FeatureBits3;
- __le32 SupportedPerformanceModes;
- __le32 ReservedForFutureGrowth[80];
+ __le32 feature_bits3;
+ __le32 supported_performance_modes;
+ u8 host_bus_type; /* uses HOST_BUS_TYPE_xxx defines */
+ u8 host_bus_width; /* actual width in bits or links */
+ u16 host_bus_speed; /* actual bus speed/link rate in MHz */
+ u8 max_rrc_drives; /* max. number of ITP-RRC drives/pool */
+ u8 max_disk_xtasks; /* max. possible num of DiskX Tasks */
+
+ u8 cpld_ver_loaded;
+ u8 cpld_ver_in_flash;
+
+ __le64 max_rrc_capacity;
+ __le32 compiled_max_hist_log_level;
+ u8 custom_board_name[12];
+ u16 supported_cntlr_mode; /* identify supported controller mode */
+ u16 reserved_for_future16;
+ __le32 supported_options3; /* reserved for future options */
+
+ __le16 virt_device_bus; /* virt. SCSI device for Thor */
+ __le16 virt_device_target;
+ __le16 virt_device_lun;
+ __le16 unused;
+ __le32 reserved_for_future_growth[68];
+
};
#define AAC_FEATURE_FALCON cpu_to_le32(0x00000010)
#define AAC_FEATURE_JBOD cpu_to_le32(0x08000000)
@@ -937,6 +1430,14 @@ struct aac_supplement_adapter_info
#define AAC_OPTION_IGNORE_RESET cpu_to_le32(0x00000002)
#define AAC_OPTION_POWER_MANAGEMENT cpu_to_le32(0x00000004)
#define AAC_OPTION_DOORBELL_RESET cpu_to_le32(0x00004000)
+/* 4KB sector size */
+#define AAC_OPTION_VARIABLE_BLOCK_SIZE cpu_to_le32(0x00040000)
+/* 240 simple volume support */
+#define AAC_OPTION_SUPPORTED_240_VOLUMES cpu_to_le32(0x10000000)
+/*
+ * Supports FIB dump sync command send prior to IOP_RESET
+ */
+#define AAC_OPTION_SUPPORTED3_IOP_RESET_FIB_DUMP cpu_to_le32(0x00004000)
#define AAC_SIS_VERSION_V3 3
#define AAC_SIS_SLOT_UNKNOWN 0xFF
@@ -998,7 +1499,27 @@ struct aac_bus_info_response {
#define AAC_OPT_SUPPLEMENT_ADAPTER_INFO cpu_to_le32(1<<16)
#define AAC_OPT_NEW_COMM cpu_to_le32(1<<17)
#define AAC_OPT_NEW_COMM_64 cpu_to_le32(1<<18)
+#define AAC_OPT_EXTENDED cpu_to_le32(1<<23)
+#define AAC_OPT_NATIVE_HBA cpu_to_le32(1<<25)
#define AAC_OPT_NEW_COMM_TYPE1 cpu_to_le32(1<<28)
+#define AAC_OPT_NEW_COMM_TYPE2 cpu_to_le32(1<<29)
+#define AAC_OPT_NEW_COMM_TYPE3 cpu_to_le32(1<<30)
+#define AAC_OPT_NEW_COMM_TYPE4 cpu_to_le32(1<<31)
+
+#define AAC_COMM_PRODUCER 0
+#define AAC_COMM_MESSAGE 1
+#define AAC_COMM_MESSAGE_TYPE1 3
+#define AAC_COMM_MESSAGE_TYPE2 4
+#define AAC_COMM_MESSAGE_TYPE3 5
+
+#define AAC_EXTOPT_SA_FIRMWARE cpu_to_le32(1<<1)
+#define AAC_EXTOPT_SOFT_RESET cpu_to_le32(1<<16)
+
+/* MSIX context */
+struct aac_msix_ctx {
+ int vector_no;
+ struct aac_dev *dev;
+};
struct aac_dev
{
@@ -1009,15 +1530,17 @@ struct aac_dev
/*
* negotiated FIB settings
*/
- unsigned max_fib_size;
- unsigned sg_tablesize;
- unsigned max_num_aif;
+ unsigned int max_fib_size;
+ unsigned int sg_tablesize;
+ unsigned int max_num_aif;
+
+ unsigned int max_cmd_size; /* max_fib_size or MAX_NATIVE */
/*
* Map for 128 fib objects (64k)
*/
- dma_addr_t hw_fib_pa;
- struct hw_fib *hw_fib_va;
+ dma_addr_t hw_fib_pa; /* also used for native cmd */
+ struct hw_fib *hw_fib_va; /* also used for native cmd */
struct hw_fib *aif_base_va;
/*
* Fib Headers
@@ -1027,6 +1550,8 @@ struct aac_dev
struct fib *free_fib;
spinlock_t fib_lock;
+ struct mutex ioctl_mutex;
+ struct mutex scan_mutex;
struct aac_queue_block *queues;
/*
* The user API will use an IOCTL to register itself to receive
@@ -1040,24 +1565,29 @@ struct aac_dev
struct adapter_ops a_ops;
unsigned long fsrev; /* Main driver's revision number */
- unsigned long dbg_base; /* address of UART
+ resource_size_t base_start; /* main IO base */
+ resource_size_t dbg_base; /* address of UART
* debug buffer */
- unsigned base_size, dbg_size; /* Size of
+ resource_size_t base_size, dbg_size; /* Size of
* mapped in region */
-
- struct aac_init *init; /* Holds initialization info to communicate with adapter */
+ /*
+ * Holds initialization info
+ * to communicate with adapter
+ */
+ union aac_init *init;
dma_addr_t init_pa; /* Holds physical address of the init struct */
-
- u32 *host_rrq; /* response queue
- * if AAC_COMM_MESSAGE_TYPE1 */
-
+ /* response queue (if AAC_COMM_MESSAGE_TYPE1) */
+ __le32 *host_rrq;
dma_addr_t host_rrq_pa; /* phys. address */
- u32 host_rrq_idx; /* index into rrq buffer */
-
+ /* index into rrq buffer */
+ u32 host_rrq_idx[AAC_MAX_MSIX];
+ atomic_t rrq_outstanding[AAC_MAX_MSIX];
+ u32 fibs_pushed_no;
struct pci_dev *pdev; /* Our PCI interface */
- void * printfbuf; /* pointer to buffer used for printf's from the adapter */
- void * comm_addr; /* Base address of Comm area */
+ /* pointer to buffer used for printf's from the adapter */
+ void *printfbuf;
+ void *comm_addr; /* Base address of Comm area */
dma_addr_t comm_phys; /* Physical Address of Comm area */
size_t comm_size;
@@ -1067,7 +1597,14 @@ struct aac_dev
int maximum_num_channels;
struct fsa_dev_info *fsa_dev;
struct task_struct *thread;
+ struct delayed_work safw_rescan_work;
+ struct delayed_work src_reinit_aif_worker;
int cardtype;
+ /*
+ *This lock will protect the two 32-bit
+ *writes to the Inbound Queue
+ */
+ spinlock_t iq_lock;
/*
* The following is the device specific extension.
@@ -1076,6 +1613,8 @@ struct aac_dev
# define AAC_MIN_FOOTPRINT_SIZE 8192
# define AAC_MIN_SRC_BAR0_SIZE 0x400000
# define AAC_MIN_SRC_BAR1_SIZE 0x800
+# define AAC_MIN_SRCV_BAR0_SIZE 0x100000
+# define AAC_MIN_SRCV_BAR1_SIZE 0x400
#endif
union
{
@@ -1106,17 +1645,33 @@ struct aac_dev
u8 needs_dac;
u8 raid_scsi_mode;
u8 comm_interface;
-# define AAC_COMM_PRODUCER 0
-# define AAC_COMM_MESSAGE 1
-# define AAC_COMM_MESSAGE_TYPE1 3
u8 raw_io_interface;
u8 raw_io_64;
u8 printf_enabled;
u8 in_reset;
+ u8 in_soft_reset;
u8 msi;
+ u8 sa_firmware;
int management_fib_count;
spinlock_t manage_lock;
-
+ spinlock_t sync_lock;
+ int sync_mode;
+ struct fib *sync_fib;
+ struct list_head sync_fib_list;
+ u32 doorbell_mask;
+ u32 max_msix; /* max. MSI-X vectors */
+ u32 vector_cap; /* MSI-X vector capab.*/
+ int msi_enabled; /* MSI/MSI-X enabled */
+ atomic_t msix_counter;
+ u32 scan_counter;
+ struct msix_entry msixentry[AAC_MAX_MSIX];
+ struct aac_msix_ctx aac_msix[AAC_MAX_MSIX]; /* context */
+ struct aac_hba_map_info hba_map[AAC_MAX_BUSES][AAC_MAX_TARGETS];
+ struct aac_ciss_phys_luns_resp *safw_phys_luns;
+ u8 adapter_shutdown;
+ u32 handle_pci_error;
+ bool init_reset;
+ u8 soft_reset_support;
};
#define aac_adapter_interrupt(dev) \
@@ -1134,11 +1689,11 @@ struct aac_dev
#define aac_adapter_sync_cmd(dev, command, p1, p2, p3, p4, p5, p6, status, r1, r2, r3, r4) \
(dev)->a_ops.adapter_sync_cmd(dev, command, p1, p2, p3, p4, p5, p6, status, r1, r2, r3, r4)
-#define aac_adapter_check_health(dev) \
- (dev)->a_ops.adapter_check_health(dev)
+#define aac_adapter_restart(dev, bled, reset_type) \
+ ((dev)->a_ops.adapter_restart(dev, bled, reset_type))
-#define aac_adapter_restart(dev,bled) \
- (dev)->a_ops.adapter_restart(dev,bled)
+#define aac_adapter_start(dev) \
+ ((dev)->a_ops.adapter_start(dev))
#define aac_adapter_ioremap(dev, size) \
(dev)->a_ops.adapter_ioremap(dev, size)
@@ -1163,6 +1718,12 @@ struct aac_dev
#define FIB_CONTEXT_FLAG_TIMED_OUT (0x00000001)
#define FIB_CONTEXT_FLAG (0x00000002)
+#define FIB_CONTEXT_FLAG_WAIT (0x00000004)
+#define FIB_CONTEXT_FLAG_FASTRESP (0x00000008)
+#define FIB_CONTEXT_FLAG_NATIVE_HBA (0x00000010)
+#define FIB_CONTEXT_FLAG_NATIVE_HBA_TMF (0x00000020)
+#define FIB_CONTEXT_FLAG_SCSI_CMD (0x00000040)
+#define FIB_CONTEXT_FLAG_EH_RESET (0x00000080)
/*
* Define the command values
@@ -1221,6 +1782,7 @@ struct aac_dev
#define ST_IO 5
#define ST_NXIO 6
#define ST_E2BIG 7
+#define ST_MEDERR 8
#define ST_ACCES 13
#define ST_EXIST 17
#define ST_XDEV 18
@@ -1269,6 +1831,22 @@ struct aac_dev
#define CMDATA_SYNCH 4
#define CMUNSTABLE 5
+#define RIO_TYPE_WRITE 0x0000
+#define RIO_TYPE_READ 0x0001
+#define RIO_SUREWRITE 0x0008
+
+#define RIO2_IO_TYPE 0x0003
+#define RIO2_IO_TYPE_WRITE 0x0000
+#define RIO2_IO_TYPE_READ 0x0001
+#define RIO2_IO_TYPE_VERIFY 0x0002
+#define RIO2_IO_ERROR 0x0004
+#define RIO2_IO_SUREWRITE 0x0008
+#define RIO2_SGL_CONFORMANT 0x0010
+#define RIO2_SG_FORMAT 0xF000
+#define RIO2_SG_FORMAT_ARC 0x0000
+#define RIO2_SG_FORMAT_SRL 0x1000
+#define RIO2_SG_FORMAT_IEEE1212 0x2000
+
struct aac_read
{
__le32 command;
@@ -1313,9 +1891,6 @@ struct aac_write64
__le32 block;
__le16 pad;
__le16 flags;
-#define IO_TYPE_WRITE 0x00000000
-#define IO_TYPE_READ 0x00000001
-#define IO_SUREWRITE 0x00000008
struct sgmap64 sg; // Must be last in struct because it is variable
};
struct aac_write_reply
@@ -1336,6 +1911,22 @@ struct aac_raw_io
struct sgmapraw sg;
};
+struct aac_raw_io2 {
+ __le32 blockLow;
+ __le32 blockHigh;
+ __le32 byteCount;
+ __le16 cid;
+ __le16 flags; /* RIO2 flags */
+ __le32 sgeFirstSize; /* size of first sge el. */
+ __le32 sgeNominalSize; /* size of 2nd sge el. (if conformant) */
+ u8 sgeCnt; /* only 8 bits required */
+ u8 bpTotal; /* reserved for F/W use */
+ u8 bpComplete; /* reserved for F/W use */
+ u8 sgeFirstIndex; /* reserved for F/W use */
+ u8 unused[4];
+ struct sge_ieee1212 sge[];
+};
+
#define CT_FLUSH_CACHE 129
struct aac_synchronize {
__le32 command; /* VM_ContainerConfig */
@@ -1430,6 +2021,12 @@ struct aac_srb_reply
__le32 sense_data_size;
u8 sense_data[AAC_SENSE_BUFFERSIZE]; // Can this be SCSI_SENSE_BUFFERSIZE
};
+
+struct aac_srb_unit {
+ struct aac_srb_reply srb_reply;
+ struct aac_srb srb;
+};
+
/*
* SRB Flags
*/
@@ -1524,6 +2121,7 @@ struct aac_srb_reply
#define VM_CtHostWrite64 20
#define VM_DrvErrTblLog 21
#define VM_NameServe64 22
+#define VM_NameServeAllBlk 30
#define MAX_VMCOMMAND_NUM 23 /* used for sizing stats array - leave last */
@@ -1546,8 +2144,15 @@ struct aac_fsinfo {
__le32 fsInodeDensity;
}; /* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+struct aac_blockdevinfo {
+ __le32 block_size;
+ __le32 logical_phys_map;
+ u8 identifier[16];
+};
+
union aac_contentinfo {
- struct aac_fsinfo filesys; /* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+ struct aac_fsinfo filesys;
+ struct aac_blockdevinfo bdevinfo;
};
/*
@@ -1612,6 +2217,7 @@ struct aac_get_container_count_resp {
__le32 MaxContainers;
__le32 ContainerSwitchEntries;
__le32 MaxPartitions;
+ __le32 MaxSimpleVolumes;
};
@@ -1675,7 +2281,7 @@ struct aac_get_name_resp {
__le32 parm3;
__le32 parm4;
__le32 parm5;
- u8 data[16];
+ u8 data[17];
};
#define CT_CID_TO_32BITS_UID 165
@@ -1767,6 +2373,16 @@ struct revision
#define FSACTL_FORCE_DELETE_DISK CTL_CODE(2120, METHOD_NEITHER)
#define FSACTL_GET_CONTAINERS 2131
#define FSACTL_SEND_LARGE_FIB CTL_CODE(2138, METHOD_BUFFERED)
+#define FSACTL_RESET_IOP CTL_CODE(2140, METHOD_BUFFERED)
+#define FSACTL_GET_HBA_INFO CTL_CODE(2150, METHOD_BUFFERED)
+/* flags defined for IOP & HW SOFT RESET */
+#define HW_IOP_RESET 0x01
+#define HW_SOFT_RESET 0x02
+#define IOP_HWSOFT_RESET (HW_IOP_RESET | HW_SOFT_RESET)
+/* HW Soft Reset register offset */
+#define IBW_SWR_OFFSET 0x4000
+#define SOFT_RESET_TIME 60
+
struct aac_common
@@ -1785,6 +2401,8 @@ struct aac_common
#ifdef DBG
u32 FibsSent;
u32 FibRecved;
+ u32 NativeSent;
+ u32 NativeRecved;
u32 NoResponseSent;
u32 NoResponseRecved;
u32 AsyncSent;
@@ -1796,6 +2414,56 @@ struct aac_common
extern struct aac_common aac_config;
+/*
+ * This is for management ioctl purpose only.
+ */
+struct aac_hba_info {
+
+ u8 driver_name[50];
+ u8 adapter_number;
+ u8 system_io_bus_number;
+ u8 device_number;
+ u32 function_number;
+ u32 vendor_id;
+ u32 device_id;
+ u32 sub_vendor_id;
+ u32 sub_system_id;
+ u32 mapped_base_address_size;
+ u32 base_physical_address_high_part;
+ u32 base_physical_address_low_part;
+
+ u32 max_command_size;
+ u32 max_fib_size;
+ u32 max_scatter_gather_from_os;
+ u32 max_scatter_gather_to_fw;
+ u32 max_outstanding_fibs;
+
+ u32 queue_start_threshold;
+ u32 queue_dump_threshold;
+ u32 max_io_size_queued;
+ u32 outstanding_io;
+
+ u32 firmware_build_number;
+ u32 bios_build_number;
+ u32 driver_build_number;
+ u32 serial_number_high_part;
+ u32 serial_number_low_part;
+ u32 supported_options;
+ u32 feature_bits;
+ u32 currentnumber_ports;
+
+ u8 new_comm_interface:1;
+ u8 new_commands_supported:1;
+ u8 disable_passthrough:1;
+ u8 expose_non_dasd:1;
+ u8 queue_allowed:1;
+ u8 bled_check_enabled:1;
+ u8 reserved1:1;
+ u8 reserted2:1;
+
+ u32 reserved3[10];
+
+};
/*
* The following macro is used when sending and receiving FIBs. It is
@@ -1824,9 +2492,13 @@ extern struct aac_common aac_config;
#define GET_DRIVER_BUFFER_PROPERTIES 0x00000023
#define RCV_TEMP_READINGS 0x00000025
#define GET_COMM_PREFERRED_SETTINGS 0x00000026
+#define IOP_RESET_FW_FIB_DUMP 0x00000034
+#define DROP_IO 0x00000035
#define IOP_RESET 0x00001000
#define IOP_RESET_ALWAYS 0x00001001
-#define RE_INIT_ADAPTER 0x000000ee
+#define RE_INIT_ADAPTER 0x000000ee
+
+#define IOP_SRC_RESET_MASK 0x00000100
/*
* Adapter Status Register
@@ -1851,8 +2523,14 @@ extern struct aac_common aac_config;
#define SELF_TEST_FAILED 0x00000004
#define MONITOR_PANIC 0x00000020
+#define KERNEL_BOOTING 0x00000040
#define KERNEL_UP_AND_RUNNING 0x00000080
#define KERNEL_PANIC 0x00000100
+#define FLASH_UPD_PENDING 0x00002000
+#define FLASH_UPD_SUCCESS 0x00004000
+#define FLASH_UPD_FAILED 0x00008000
+#define INVALID_OMR 0xffffffff
+#define FWUPD_TIMEOUT (5 * 60)
/*
* Doorbell bit defines
@@ -1882,6 +2560,8 @@ extern struct aac_common aac_config;
#define AifEnEnclosureManagement 13 /* EM_DRIVE_* */
#define EM_DRIVE_INSERTION 31
#define EM_DRIVE_REMOVAL 32
+#define EM_SES_DRIVE_INSERTION 33
+#define EM_SES_DRIVE_REMOVAL 26
#define AifEnBatteryEvent 14 /* Change in Battery State */
#define AifEnAddContainer 15 /* A new array was created */
#define AifEnDeleteContainer 16 /* A container was deleted */
@@ -1891,6 +2571,10 @@ extern struct aac_common aac_config;
#define AifEnAddJBOD 30 /* JBOD created */
#define AifEnDeleteJBOD 31 /* JBOD deleted */
+#define AifBuManagerEvent 42 /* Bu management*/
+#define AifBuCacheDataLoss 10
+#define AifBuCacheDataRecover 11
+
#define AifCmdJobProgress 2 /* Progress report */
#define AifJobCtrZero 101 /* Array Zero progress */
#define AifJobStsSuccess 1 /* Job completes */
@@ -1913,6 +2597,10 @@ extern struct aac_common aac_config;
/* PMC NEW COMM: Request the event data */
#define AifReqEvent 200
+#define AifRawDeviceRemove 203 /* RAW device deleted */
+#define AifNativeDeviceAdd 204 /* native HBA device added */
+#define AifNativeDeviceRemove 205 /* native HBA device removed */
+
/*
* Adapter Initiated FIB command structures. Start with the adapter
@@ -1923,7 +2611,7 @@ extern struct aac_common aac_config;
struct aac_aifcmd {
__le32 command; /* Tell host what type of notify this is */
__le32 seqnum; /* To allow ordering of reports (if necessary) */
- u8 data[1]; /* Undefined length (from kernel viewpoint) */
+ u8 data[]; /* Undefined length (from kernel viewpoint) */
};
/**
@@ -1937,47 +2625,116 @@ static inline unsigned int cap_to_cyls(sector_t capacity, unsigned divisor)
return capacity;
}
-/* SCp.phase values */
-#define AAC_OWNER_MIDLEVEL 0x101
-#define AAC_OWNER_LOWLEVEL 0x102
-#define AAC_OWNER_ERROR_HANDLER 0x103
-#define AAC_OWNER_FIRMWARE 0x106
+static inline int aac_pci_offline(struct aac_dev *dev)
+{
+ return pci_channel_offline(dev->pdev) || dev->handle_pci_error;
+}
+
+static inline int aac_adapter_check_health(struct aac_dev *dev)
+{
+ if (unlikely(aac_pci_offline(dev)))
+ return -1;
+
+ return (dev)->a_ops.adapter_check_health(dev);
+}
+
+int aac_scan_host(struct aac_dev *dev);
+
+static inline void aac_schedule_safw_scan_worker(struct aac_dev *dev)
+{
+ schedule_delayed_work(&dev->safw_rescan_work, AAC_RESCAN_DELAY);
+}
+
+static inline void aac_schedule_src_reinit_aif_worker(struct aac_dev *dev)
+{
+ schedule_delayed_work(&dev->src_reinit_aif_worker, AAC_RESCAN_DELAY);
+}
+
+static inline void aac_safw_rescan_worker(struct work_struct *work)
+{
+ struct aac_dev *dev = container_of(to_delayed_work(work),
+ struct aac_dev, safw_rescan_work);
+
+ wait_event(dev->scsi_host_ptr->host_wait,
+ !scsi_host_in_recovery(dev->scsi_host_ptr));
+
+ aac_scan_host(dev);
+}
+
+static inline void aac_cancel_rescan_worker(struct aac_dev *dev)
+{
+ cancel_delayed_work_sync(&dev->safw_rescan_work);
+ cancel_delayed_work_sync(&dev->src_reinit_aif_worker);
+}
+
+enum aac_cmd_owner {
+ AAC_OWNER_MIDLEVEL = 0x101,
+ AAC_OWNER_LOWLEVEL = 0x102,
+ AAC_OWNER_ERROR_HANDLER = 0x103,
+ AAC_OWNER_FIRMWARE = 0x106,
+};
+
+struct aac_cmd_priv {
+ int (*callback)(struct scsi_cmnd *);
+ int status;
+ enum aac_cmd_owner owner;
+ bool sent_command;
+};
+
+static inline struct aac_cmd_priv *aac_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
+void aac_safw_rescan_worker(struct work_struct *work);
+void aac_src_reinit_aif_worker(struct work_struct *work);
+int aac_acquire_irq(struct aac_dev *dev);
+void aac_free_irq(struct aac_dev *dev);
+int aac_setup_safw_adapter(struct aac_dev *dev);
const char *aac_driverinfo(struct Scsi_Host *);
+void aac_fib_vector_assign(struct aac_dev *dev);
struct fib *aac_fib_alloc(struct aac_dev *dev);
+struct fib *aac_fib_alloc_tag(struct aac_dev *dev, struct scsi_cmnd *scmd);
int aac_fib_setup(struct aac_dev *dev);
void aac_fib_map_free(struct aac_dev *dev);
void aac_fib_free(struct fib * context);
void aac_fib_init(struct fib * context);
void aac_printf(struct aac_dev *dev, u32 val);
int aac_fib_send(u16 command, struct fib * context, unsigned long size, int priority, int wait, int reply, fib_callback callback, void *ctxt);
+int aac_hba_send(u8 command, struct fib *context,
+ fib_callback callback, void *ctxt);
int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entry **entry);
void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum);
int aac_fib_complete(struct fib * context);
+void aac_hba_callback(void *context, struct fib *fibptr);
#define fib_data(fibctx) ((void *)(fibctx)->hw_fib_va->data)
struct aac_dev *aac_init_adapter(struct aac_dev *dev);
+void aac_src_access_devreg(struct aac_dev *dev, int mode);
+void aac_set_intx_mode(struct aac_dev *dev);
int aac_get_config_status(struct aac_dev *dev, int commit_flag);
int aac_get_containers(struct aac_dev *dev);
int aac_scsi_cmd(struct scsi_cmnd *cmd);
-int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg);
+int aac_dev_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg);
#ifndef shost_to_class
#define shost_to_class(shost) &shost->shost_dev
#endif
ssize_t aac_get_serial_number(struct device *dev, char *buf);
-int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg);
+int aac_do_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg);
int aac_rx_init(struct aac_dev *dev);
int aac_rkt_init(struct aac_dev *dev);
int aac_nark_init(struct aac_dev *dev);
int aac_sa_init(struct aac_dev *dev);
int aac_src_init(struct aac_dev *dev);
+int aac_srcv_init(struct aac_dev *dev);
int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw_fib, int wait, struct fib * fibptr, unsigned long *nonotify);
+void aac_define_int_mode(struct aac_dev *dev);
unsigned int aac_response_normal(struct aac_queue * q);
unsigned int aac_command_normal(struct aac_queue * q);
unsigned int aac_intr_normal(struct aac_dev *dev, u32 Index,
int isAif, int isFastResponse,
struct hw_fib *aif_fib);
-int aac_reset_adapter(struct aac_dev * dev, int forced);
-int aac_check_health(struct aac_dev * dev);
+int aac_reset_adapter(struct aac_dev *dev, int forced, u8 reset_type);
int aac_command_thread(void *data);
int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx);
int aac_fib_adapter_complete(struct fib * fibptr, unsigned short size);
@@ -1988,9 +2745,26 @@ int aac_probe_container(struct aac_dev *dev, int cid);
int _aac_rx_init(struct aac_dev *dev);
int aac_rx_select_comm(struct aac_dev *dev, int comm);
int aac_rx_deliver_producer(struct fib * fib);
+void aac_reinit_aif(struct aac_dev *aac, unsigned int index);
+
+static inline int aac_is_src(struct aac_dev *dev)
+{
+ u16 device = dev->pdev->device;
+
+ if (device == PMC_DEVICE_S6 ||
+ device == PMC_DEVICE_S7 ||
+ device == PMC_DEVICE_S8)
+ return 1;
+ return 0;
+}
+
+static inline int aac_supports_2T(struct aac_dev *dev)
+{
+ return (dev->adapter_info.options & AAC_OPT_NEW_COMM_64);
+}
+
char * get_container_type(unsigned type);
extern int numacb;
-extern int acbsize;
extern char aac_driver_version[];
extern int startup_timeout;
extern int aif_timeout;
@@ -2001,3 +2775,5 @@ extern int aac_commit;
extern int update_interval;
extern int check_interval;
extern int aac_check_reset;
+extern int aac_fib_dump;
+#endif
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
index 8a0b33033177..68240d6f27ab 100644
--- a/drivers/scsi/aacraid/commctrl.c
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,27 +7,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* commctrl.c
*
* Abstract: Contains all routines for control of the AFA comm layer
- *
*/
#include <linux/kernel.h>
@@ -38,14 +25,16 @@
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/blkdev.h>
+#include <linux/compat.h>
#include <linux/delay.h> /* ssleep prototype */
#include <linux/kthread.h>
-#include <linux/semaphore.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <scsi/scsi_host.h>
#include "aacraid.h"
+# define AAC_DEBUG_PREAMBLE KERN_INFO
+# define AAC_DEBUG_POSTAMBLE
/**
* ioctl_send_fib - send a FIB from userspace
* @dev: adapter is being processed
@@ -54,16 +43,13 @@
* This routine sends a fib to the adapter on behalf of a user level
* program.
*/
-# define AAC_DEBUG_PREAMBLE KERN_INFO
-# define AAC_DEBUG_POSTAMBLE
-
static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
{
struct hw_fib * kfib;
struct fib *fibptr;
struct hw_fib * hw_fib = (struct hw_fib *)0;
dma_addr_t hw_fib_pa = (dma_addr_t)0LL;
- unsigned size;
+ unsigned int size, osize;
int retval;
if (dev->in_reset) {
@@ -87,7 +73,8 @@ static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
* will not overrun the buffer when we copy the memory. Return
* an error if we would.
*/
- size = le16_to_cpu(kfib->header.Size) + sizeof(struct aac_fibhdr);
+ osize = size = le16_to_cpu(kfib->header.Size) +
+ sizeof(struct aac_fibhdr);
if (size < le16_to_cpu(kfib->header.SenderSize))
size = le16_to_cpu(kfib->header.SenderSize);
if (size > dev->max_fib_size) {
@@ -98,7 +85,8 @@ static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
goto cleanup;
}
- kfib = pci_alloc_consistent(dev->pdev, size, &daddr);
+ kfib = dma_alloc_coherent(&dev->pdev->dev, size, &daddr,
+ GFP_KERNEL);
if (!kfib) {
retval = -ENOMEM;
goto cleanup;
@@ -118,6 +106,14 @@ static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
goto cleanup;
}
+ /* Sanity check the second copy */
+ if ((osize != le16_to_cpu(kfib->header.Size) +
+ sizeof(struct aac_fibhdr))
+ || (size < le16_to_cpu(kfib->header.SenderSize))) {
+ retval = -EINVAL;
+ goto cleanup;
+ }
+
if (kfib->header.Command == cpu_to_le16(TakeABreakPt)) {
aac_adapter_interrupt(dev);
/*
@@ -150,7 +146,8 @@ static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
retval = -EFAULT;
cleanup:
if (hw_fib) {
- pci_free_consistent(dev->pdev, size, kfib, fibptr->hw_fib_pa);
+ dma_free_coherent(&dev->pdev->dev, size, kfib,
+ fibptr->hw_fib_pa);
fibptr->hw_fib_pa = hw_fib_pa;
fibptr->hw_fib_va = hw_fib;
}
@@ -161,11 +158,12 @@ cleanup:
/**
* open_getadapter_fib - Get the next fib
+ * @dev: adapter is being processed
+ * @arg: arguments to the open call
*
* This routine will get the next Fib, if available, from the AdapterFibContext
* passed in from the user.
*/
-
static int open_getadapter_fib(struct aac_dev * dev, void __user *arg)
{
struct aac_fib_context * fibctx;
@@ -191,7 +189,7 @@ static int open_getadapter_fib(struct aac_dev * dev, void __user *arg)
/*
* Initialize the mutex used to wait for the next AIF.
*/
- sema_init(&fibctx->wait_sem, 0);
+ init_completion(&fibctx->completion);
fibctx->wait = 0;
/*
* Initialize the fibs and set the count of fibs on
@@ -229,6 +227,12 @@ static int open_getadapter_fib(struct aac_dev * dev, void __user *arg)
return status;
}
+struct compat_fib_ioctl {
+ u32 fibctx;
+ s32 wait;
+ compat_uptr_t fib;
+};
+
/**
* next_getadapter_fib - get the next fib
* @dev: adapter to use
@@ -237,7 +241,6 @@ static int open_getadapter_fib(struct aac_dev * dev, void __user *arg)
* This routine will get the next Fib, if available, from the AdapterFibContext
* passed in from the user.
*/
-
static int next_getadapter_fib(struct aac_dev * dev, void __user *arg)
{
struct fib_ioctl f;
@@ -247,8 +250,19 @@ static int next_getadapter_fib(struct aac_dev * dev, void __user *arg)
struct list_head * entry;
unsigned long flags;
- if(copy_from_user((void *)&f, arg, sizeof(struct fib_ioctl)))
- return -EFAULT;
+ if (in_compat_syscall()) {
+ struct compat_fib_ioctl cf;
+
+ if (copy_from_user(&cf, arg, sizeof(struct compat_fib_ioctl)))
+ return -EFAULT;
+
+ f.fibctx = cf.fibctx;
+ f.wait = cf.wait;
+ f.fib = compat_ptr(cf.fib);
+ } else {
+ if (copy_from_user(&f, arg, sizeof(struct fib_ioctl)))
+ return -EFAULT;
+ }
/*
* Verify that the HANDLE passed in was a valid AdapterFibContext
*
@@ -318,11 +332,12 @@ return_fib:
kthread_stop(dev->thread);
ssleep(1);
dev->aif_thread = 0;
- dev->thread = kthread_run(aac_command_thread, dev, dev->name);
+ dev->thread = kthread_run(aac_command_thread, dev,
+ "%s", dev->name);
ssleep(1);
}
if (f.wait) {
- if(down_interruptible(&fibctx->wait_sem) < 0) {
+ if (wait_for_completion_interruptible(&fibctx->completion) < 0) {
status = -ERESTARTSYS;
} else {
/* Lock again and retry */
@@ -457,30 +472,33 @@ static int check_revision(struct aac_dev *dev, void __user *arg)
/**
- *
- * aac_send_raw_scb
- *
+ * aac_send_raw_srb()
+ * @dev: adapter is being processed
+ * @arg: arguments to the send call
*/
-
static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
{
struct fib* srbfib;
int status;
struct aac_srb *srbcmd = NULL;
+ struct aac_hba_cmd_req *hbacmd = NULL;
struct user_aac_srb *user_srbcmd = NULL;
struct user_aac_srb __user *user_srb = arg;
struct aac_srb_reply __user *user_reply;
- struct aac_srb_reply* reply;
+ u32 chn;
u32 fibsize = 0;
u32 flags = 0;
s32 rcode = 0;
u32 data_dir;
- void __user *sg_user[32];
- void *sg_list[32];
+ void __user *sg_user[HBA_MAX_SG_EMBEDDED];
+ void *sg_list[HBA_MAX_SG_EMBEDDED];
+ u32 sg_count[HBA_MAX_SG_EMBEDDED];
u32 sg_indx = 0;
u32 byte_count = 0;
u32 actual_fibsize64, actual_fibsize = 0;
int i;
+ int is_native_device;
+ u64 address;
if (dev->in_reset) {
@@ -497,9 +515,6 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
if (!(srbfib = aac_fib_alloc(dev))) {
return -ENOMEM;
}
- aac_fib_init(srbfib);
-
- srbcmd = (struct aac_srb*) fib_data(srbfib);
memset(sg_list, 0, sizeof(sg_list)); /* cleanup may take issue */
if(copy_from_user(&fibsize, &user_srb->count,sizeof(u32))){
@@ -508,38 +523,20 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
goto cleanup;
}
- if (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr))) {
+ if ((fibsize < sizeof(struct user_aac_srb)) ||
+ (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr)))) {
rcode = -EINVAL;
goto cleanup;
}
- user_srbcmd = kmalloc(fibsize, GFP_KERNEL);
- if (!user_srbcmd) {
- dprintk((KERN_DEBUG"aacraid: Could not make a copy of the srb\n"));
- rcode = -ENOMEM;
- goto cleanup;
- }
- if(copy_from_user(user_srbcmd, user_srb,fibsize)){
- dprintk((KERN_DEBUG"aacraid: Could not copy srb from user\n"));
- rcode = -EFAULT;
+ user_srbcmd = memdup_user(user_srb, fibsize);
+ if (IS_ERR(user_srbcmd)) {
+ rcode = PTR_ERR(user_srbcmd);
+ user_srbcmd = NULL;
goto cleanup;
}
- user_reply = arg+fibsize;
-
flags = user_srbcmd->flags; /* from user in cpu order */
- // Fix up srb for endian and force some values
-
- srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi); // Force this
- srbcmd->channel = cpu_to_le32(user_srbcmd->channel);
- srbcmd->id = cpu_to_le32(user_srbcmd->id);
- srbcmd->lun = cpu_to_le32(user_srbcmd->lun);
- srbcmd->timeout = cpu_to_le32(user_srbcmd->timeout);
- srbcmd->flags = cpu_to_le32(flags);
- srbcmd->retry_limit = 0; // Obsolete parameter
- srbcmd->cdb_size = cpu_to_le32(user_srbcmd->cdb_size);
- memcpy(srbcmd->cdb, user_srbcmd->cdb, sizeof(srbcmd->cdb));
-
switch (flags & (SRB_DataIn | SRB_DataOut)) {
case SRB_DataOut:
data_dir = DMA_TO_DEVICE;
@@ -555,11 +552,16 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
}
if (user_srbcmd->sg.count > ARRAY_SIZE(sg_list)) {
dprintk((KERN_DEBUG"aacraid: too many sg entries %d\n",
- le32_to_cpu(srbcmd->sg.count)));
+ user_srbcmd->sg.count));
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+ if ((data_dir == DMA_NONE) && user_srbcmd->sg.count) {
+ dprintk((KERN_DEBUG"aacraid:SG with no direction specified\n"));
rcode = -EINVAL;
goto cleanup;
}
- actual_fibsize = sizeof(struct aac_srb) - sizeof(struct sgentry) +
+ actual_fibsize = sizeof(struct aac_srb) +
((user_srbcmd->sg.count & 0xff) * sizeof(struct sgentry));
actual_fibsize64 = actual_fibsize + (user_srbcmd->sg.count & 0xff) *
(sizeof(struct sgentry64) - sizeof(struct sgentry));
@@ -575,13 +577,136 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
rcode = -EINVAL;
goto cleanup;
}
- if ((data_dir == DMA_NONE) && user_srbcmd->sg.count) {
- dprintk((KERN_DEBUG"aacraid: SG with no direction specified in Raw SRB command\n"));
- rcode = -EINVAL;
- goto cleanup;
+
+ chn = user_srbcmd->channel;
+ if (chn < AAC_MAX_BUSES && user_srbcmd->id < AAC_MAX_TARGETS &&
+ dev->hba_map[chn][user_srbcmd->id].devtype ==
+ AAC_DEVTYPE_NATIVE_RAW) {
+ is_native_device = 1;
+ hbacmd = (struct aac_hba_cmd_req *)srbfib->hw_fib_va;
+ memset(hbacmd, 0, 96); /* sizeof(*hbacmd) is not necessary */
+
+ /* iu_type is a parameter of aac_hba_send */
+ switch (data_dir) {
+ case DMA_TO_DEVICE:
+ hbacmd->byte1 = 2;
+ break;
+ case DMA_FROM_DEVICE:
+ case DMA_BIDIRECTIONAL:
+ hbacmd->byte1 = 1;
+ break;
+ case DMA_NONE:
+ default:
+ break;
+ }
+ hbacmd->lun[1] = cpu_to_le32(user_srbcmd->lun);
+ hbacmd->it_nexus = dev->hba_map[chn][user_srbcmd->id].rmw_nexus;
+
+ /*
+ * we fill in reply_qid later in aac_src_deliver_message
+ * we fill in iu_type, request_id later in aac_hba_send
+ * we fill in emb_data_desc_count, data_length later
+ * in sg list build
+ */
+
+ memcpy(hbacmd->cdb, user_srbcmd->cdb, sizeof(hbacmd->cdb));
+
+ address = (u64)srbfib->hw_error_pa;
+ hbacmd->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
+ hbacmd->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
+ hbacmd->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
+ hbacmd->emb_data_desc_count =
+ cpu_to_le32(user_srbcmd->sg.count);
+ srbfib->hbacmd_size = 64 +
+ user_srbcmd->sg.count * sizeof(struct aac_hba_sgl);
+
+ } else {
+ is_native_device = 0;
+ aac_fib_init(srbfib);
+
+ /* raw_srb FIB is not FastResponseCapable */
+ srbfib->hw_fib_va->header.XferState &=
+ ~cpu_to_le32(FastResponseCapable);
+
+ srbcmd = (struct aac_srb *) fib_data(srbfib);
+
+ // Fix up srb for endian and force some values
+
+ srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi); // Force this
+ srbcmd->channel = cpu_to_le32(user_srbcmd->channel);
+ srbcmd->id = cpu_to_le32(user_srbcmd->id);
+ srbcmd->lun = cpu_to_le32(user_srbcmd->lun);
+ srbcmd->timeout = cpu_to_le32(user_srbcmd->timeout);
+ srbcmd->flags = cpu_to_le32(flags);
+ srbcmd->retry_limit = 0; // Obsolete parameter
+ srbcmd->cdb_size = cpu_to_le32(user_srbcmd->cdb_size);
+ memcpy(srbcmd->cdb, user_srbcmd->cdb, sizeof(srbcmd->cdb));
}
+
byte_count = 0;
- if (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64) {
+ if (is_native_device) {
+ struct user_sgmap *usg32 = &user_srbcmd->sg;
+ struct user_sgmap64 *usg64 =
+ (struct user_sgmap64 *)&user_srbcmd->sg;
+
+ for (i = 0; i < usg32->count; i++) {
+ void *p;
+ u64 addr;
+
+ sg_count[i] = (actual_fibsize64 == fibsize) ?
+ usg64->sg[i].count : usg32->sg[i].count;
+ if (sg_count[i] >
+ (dev->scsi_host_ptr->max_sectors << 9)) {
+ pr_err("aacraid: upsg->sg[%d].count=%u>%u\n",
+ i, sg_count[i],
+ dev->scsi_host_ptr->max_sectors << 9);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+
+ p = kmalloc(sg_count[i], GFP_KERNEL);
+ if (!p) {
+ rcode = -ENOMEM;
+ goto cleanup;
+ }
+
+ if (actual_fibsize64 == fibsize) {
+ addr = (u64)usg64->sg[i].addr[0];
+ addr += ((u64)usg64->sg[i].addr[1]) << 32;
+ } else {
+ addr = (u64)usg32->sg[i].addr;
+ }
+
+ sg_user[i] = (void __user *)(uintptr_t)addr;
+ sg_list[i] = p; // save so we can clean up later
+ sg_indx = i;
+
+ if (flags & SRB_DataOut) {
+ if (copy_from_user(p, sg_user[i],
+ sg_count[i])) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ }
+ addr = dma_map_single(&dev->pdev->dev, p, sg_count[i],
+ data_dir);
+ hbacmd->sge[i].addr_hi = cpu_to_le32((u32)(addr>>32));
+ hbacmd->sge[i].addr_lo = cpu_to_le32(
+ (u32)(addr & 0xffffffff));
+ hbacmd->sge[i].len = cpu_to_le32(sg_count[i]);
+ hbacmd->sge[i].flags = 0;
+ byte_count += sg_count[i];
+ }
+
+ if (usg32->count > 0) /* embedded sglist */
+ hbacmd->sge[usg32->count-1].flags =
+ cpu_to_le32(0x40000000);
+ hbacmd->data_length = cpu_to_le32(byte_count);
+
+ status = aac_hba_send(HBA_IU_TYPE_SCSI_CMD_REQ, srbfib,
+ NULL, NULL);
+
+ } else if (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64) {
struct user_sgmap64* upsg = (struct user_sgmap64*)&user_srbcmd->sg;
struct sgmap64* psg = (struct sgmap64*)&srbcmd->sg;
@@ -593,7 +718,9 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
u64 addr;
void* p;
- if (upsg->sg[i].count >
+
+ sg_count[i] = upsg->sg[i].count;
+ if (sg_count[i] >
((dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
@@ -601,11 +728,11 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
rcode = -EINVAL;
goto cleanup;
}
- /* Does this really need to be GFP_DMA? */
- p = kmalloc(upsg->sg[i].count,GFP_KERNEL|__GFP_DMA);
+
+ p = kmalloc(sg_count[i], GFP_KERNEL);
if(!p) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
- upsg->sg[i].count,i,upsg->count));
+ sg_count[i], i, upsg->count));
rcode = -ENOMEM;
goto cleanup;
}
@@ -616,48 +743,52 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
sg_indx = i;
if (flags & SRB_DataOut) {
- if(copy_from_user(p,sg_user[i],upsg->sg[i].count)){
+ if (copy_from_user(p, sg_user[i],
+ sg_count[i])){
dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
rcode = -EFAULT;
goto cleanup;
}
}
- addr = pci_map_single(dev->pdev, p, upsg->sg[i].count, data_dir);
+ addr = dma_map_single(&dev->pdev->dev, p,
+ sg_count[i], data_dir);
psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
- byte_count += upsg->sg[i].count;
- psg->sg[i].count = cpu_to_le32(upsg->sg[i].count);
+ byte_count += sg_count[i];
+ psg->sg[i].count = cpu_to_le32(sg_count[i]);
}
} else {
struct user_sgmap* usg;
- usg = kmalloc(actual_fibsize - sizeof(struct aac_srb)
- + sizeof(struct sgmap), GFP_KERNEL);
+ usg = kmemdup(upsg,
+ actual_fibsize - sizeof(struct aac_srb)
+ + sizeof(struct sgmap), GFP_KERNEL);
if (!usg) {
dprintk((KERN_DEBUG"aacraid: Allocation error in Raw SRB command\n"));
rcode = -ENOMEM;
goto cleanup;
}
- memcpy (usg, upsg, actual_fibsize - sizeof(struct aac_srb)
- + sizeof(struct sgmap));
actual_fibsize = actual_fibsize64;
for (i = 0; i < usg->count; i++) {
u64 addr;
void* p;
- if (usg->sg[i].count >
+
+ sg_count[i] = usg->sg[i].count;
+ if (sg_count[i] >
((dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
65536)) {
+ kfree(usg);
rcode = -EINVAL;
goto cleanup;
}
- /* Does this really need to be GFP_DMA? */
- p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
+
+ p = kmalloc(sg_count[i], GFP_KERNEL);
if(!p) {
dprintk((KERN_DEBUG "aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
- usg->sg[i].count,i,usg->count));
+ sg_count[i], i, usg->count));
kfree(usg);
rcode = -ENOMEM;
goto cleanup;
@@ -667,24 +798,29 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
sg_indx = i;
if (flags & SRB_DataOut) {
- if(copy_from_user(p,sg_user[i],upsg->sg[i].count)){
+ if (copy_from_user(p, sg_user[i],
+ sg_count[i])) {
kfree (usg);
dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
rcode = -EFAULT;
goto cleanup;
}
}
- addr = pci_map_single(dev->pdev, p, usg->sg[i].count, data_dir);
+ addr = dma_map_single(&dev->pdev->dev, p,
+ sg_count[i], data_dir);
psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
- byte_count += usg->sg[i].count;
- psg->sg[i].count = cpu_to_le32(usg->sg[i].count);
+ byte_count += sg_count[i];
+ psg->sg[i].count = cpu_to_le32(sg_count[i]);
}
kfree (usg);
}
srbcmd->count = cpu_to_le32(byte_count);
- psg->count = cpu_to_le32(sg_indx+1);
+ if (user_srbcmd->sg.count)
+ psg->count = cpu_to_le32(sg_indx+1);
+ else
+ psg->count = 0;
status = aac_fib_send(ScsiPortCommand64, srbfib, actual_fibsize, FsaNormal, 1, 1,NULL,NULL);
} else {
struct user_sgmap* upsg = &user_srbcmd->sg;
@@ -695,7 +831,9 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
uintptr_t addr;
void* p;
- if (usg->sg[i].count >
+
+ sg_count[i] = usg->sg[i].count;
+ if (sg_count[i] >
((dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
@@ -703,11 +841,10 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
rcode = -EINVAL;
goto cleanup;
}
- /* Does this really need to be GFP_DMA? */
- p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
- if(!p) {
+ p = kmalloc(sg_count[i], GFP_KERNEL);
+ if (!p) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
- usg->sg[i].count,i,usg->count));
+ sg_count[i], i, usg->count));
rcode = -ENOMEM;
goto cleanup;
}
@@ -718,23 +855,28 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
sg_indx = i;
if (flags & SRB_DataOut) {
- if(copy_from_user(p,sg_user[i],usg->sg[i].count)){
+ if (copy_from_user(p, sg_user[i],
+ sg_count[i])){
dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
rcode = -EFAULT;
goto cleanup;
}
}
- addr = pci_map_single(dev->pdev, p, usg->sg[i].count, data_dir);
+ addr = dma_map_single(&dev->pdev->dev, p,
+ usg->sg[i].count,
+ data_dir);
psg->sg[i].addr = cpu_to_le32(addr & 0xffffffff);
byte_count += usg->sg[i].count;
- psg->sg[i].count = cpu_to_le32(usg->sg[i].count);
+ psg->sg[i].count = cpu_to_le32(sg_count[i]);
}
} else {
for (i = 0; i < upsg->count; i++) {
dma_addr_t addr;
void* p;
- if (upsg->sg[i].count >
+
+ sg_count[i] = upsg->sg[i].count;
+ if (sg_count[i] >
((dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
@@ -742,10 +884,10 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
rcode = -EINVAL;
goto cleanup;
}
- p = kmalloc(upsg->sg[i].count, GFP_KERNEL);
+ p = kmalloc(sg_count[i], GFP_KERNEL);
if (!p) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
- upsg->sg[i].count, i, upsg->count));
+ sg_count[i], i, upsg->count));
rcode = -ENOMEM;
goto cleanup;
}
@@ -754,31 +896,35 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
sg_indx = i;
if (flags & SRB_DataOut) {
- if(copy_from_user(p, sg_user[i],
- upsg->sg[i].count)) {
+ if (copy_from_user(p, sg_user[i],
+ sg_count[i])) {
dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
rcode = -EFAULT;
goto cleanup;
}
}
- addr = pci_map_single(dev->pdev, p,
- upsg->sg[i].count, data_dir);
+ addr = dma_map_single(&dev->pdev->dev, p,
+ sg_count[i], data_dir);
psg->sg[i].addr = cpu_to_le32(addr);
- byte_count += upsg->sg[i].count;
- psg->sg[i].count = cpu_to_le32(upsg->sg[i].count);
+ byte_count += sg_count[i];
+ psg->sg[i].count = cpu_to_le32(sg_count[i]);
}
}
srbcmd->count = cpu_to_le32(byte_count);
- psg->count = cpu_to_le32(sg_indx+1);
+ if (user_srbcmd->sg.count)
+ psg->count = cpu_to_le32(sg_indx+1);
+ else
+ psg->count = 0;
status = aac_fib_send(ScsiPortCommand, srbfib, actual_fibsize, FsaNormal, 1, 1, NULL, NULL);
}
+
if (status == -ERESTARTSYS) {
rcode = -ERESTARTSYS;
goto cleanup;
}
- if (status != 0){
+ if (status != 0) {
dprintk((KERN_DEBUG"aacraid: Could not send raw srb fib to hba\n"));
rcode = -ENXIO;
goto cleanup;
@@ -786,11 +932,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
if (flags & SRB_DataIn) {
for(i = 0 ; i <= sg_indx; i++){
- byte_count = le32_to_cpu(
- (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)
- ? ((struct sgmap64*)&srbcmd->sg)->sg[i].count
- : srbcmd->sg.sg[i].count);
- if(copy_to_user(sg_user[i], sg_list[i], byte_count)){
+ if (copy_to_user(sg_user[i], sg_list[i], sg_count[i])) {
dprintk((KERN_DEBUG"aacraid: Could not copy sg data to user\n"));
rcode = -EFAULT;
goto cleanup;
@@ -799,19 +941,53 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
}
}
- reply = (struct aac_srb_reply *) fib_data(srbfib);
- if(copy_to_user(user_reply,reply,sizeof(struct aac_srb_reply))){
- dprintk((KERN_DEBUG"aacraid: Could not copy reply to user\n"));
- rcode = -EFAULT;
- goto cleanup;
+ user_reply = arg + fibsize;
+ if (is_native_device) {
+ struct aac_hba_resp *err =
+ &((struct aac_native_hba *)srbfib->hw_fib_va)->resp.err;
+ struct aac_srb_reply reply;
+
+ memset(&reply, 0, sizeof(reply));
+ reply.status = ST_OK;
+ if (srbfib->flags & FIB_CONTEXT_FLAG_FASTRESP) {
+ /* fast response */
+ reply.srb_status = SRB_STATUS_SUCCESS;
+ reply.scsi_status = 0;
+ reply.data_xfer_length = byte_count;
+ reply.sense_data_size = 0;
+ memset(reply.sense_data, 0, AAC_SENSE_BUFFERSIZE);
+ } else {
+ reply.srb_status = err->service_response;
+ reply.scsi_status = err->status;
+ reply.data_xfer_length = byte_count -
+ le32_to_cpu(err->residual_count);
+ reply.sense_data_size = err->sense_response_data_len;
+ memcpy(reply.sense_data, err->sense_response_buf,
+ AAC_SENSE_BUFFERSIZE);
+ }
+ if (copy_to_user(user_reply, &reply,
+ sizeof(struct aac_srb_reply))) {
+ dprintk((KERN_DEBUG"aacraid: Copy to user failed\n"));
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ } else {
+ struct aac_srb_reply *reply;
+
+ reply = (struct aac_srb_reply *) fib_data(srbfib);
+ if (copy_to_user(user_reply, reply,
+ sizeof(struct aac_srb_reply))) {
+ dprintk((KERN_DEBUG"aacraid: Copy to user failed\n"));
+ rcode = -EFAULT;
+ goto cleanup;
+ }
}
cleanup:
kfree(user_srbcmd);
- for(i=0; i <= sg_indx; i++){
- kfree(sg_list[i]);
- }
if (rcode != -ERESTARTSYS) {
+ for (i = 0; i <= sg_indx; i++)
+ kfree(sg_list[i]);
aac_fib_complete(srbfib);
aac_fib_free(srbfib);
}
@@ -839,18 +1015,68 @@ static int aac_get_pci_info(struct aac_dev* dev, void __user *arg)
return 0;
}
+static int aac_get_hba_info(struct aac_dev *dev, void __user *arg)
+{
+ struct aac_hba_info hbainfo;
-int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg)
+ memset(&hbainfo, 0, sizeof(hbainfo));
+ hbainfo.adapter_number = (u8) dev->id;
+ hbainfo.system_io_bus_number = dev->pdev->bus->number;
+ hbainfo.device_number = (dev->pdev->devfn >> 3);
+ hbainfo.function_number = (dev->pdev->devfn & 0x0007);
+
+ hbainfo.vendor_id = dev->pdev->vendor;
+ hbainfo.device_id = dev->pdev->device;
+ hbainfo.sub_vendor_id = dev->pdev->subsystem_vendor;
+ hbainfo.sub_system_id = dev->pdev->subsystem_device;
+
+ if (copy_to_user(arg, &hbainfo, sizeof(struct aac_hba_info))) {
+ dprintk((KERN_DEBUG "aacraid: Could not copy hba info\n"));
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+struct aac_reset_iop {
+ u8 reset_type;
+};
+
+static int aac_send_reset_adapter(struct aac_dev *dev, void __user *arg)
+{
+ struct aac_reset_iop reset;
+ int retval;
+
+ if (copy_from_user((void *)&reset, arg, sizeof(struct aac_reset_iop)))
+ return -EFAULT;
+
+ dev->adapter_shutdown = 1;
+
+ mutex_unlock(&dev->ioctl_mutex);
+ retval = aac_reset_adapter(dev, 0, reset.reset_type);
+ mutex_lock(&dev->ioctl_mutex);
+
+ return retval;
+}
+
+int aac_do_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg)
{
int status;
+ mutex_lock(&dev->ioctl_mutex);
+
+ if (dev->adapter_shutdown) {
+ status = -EACCES;
+ goto cleanup;
+ }
+
/*
* HBA gets first crack
*/
status = aac_dev_ioctl(dev, cmd, arg);
if (status != -ENOTTY)
- return status;
+ goto cleanup;
switch (cmd) {
case FSACTL_MINIPORT_REV_CHECK:
@@ -875,10 +1101,21 @@ int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg)
case FSACTL_GET_PCI_INFO:
status = aac_get_pci_info(dev,arg);
break;
+ case FSACTL_GET_HBA_INFO:
+ status = aac_get_hba_info(dev, arg);
+ break;
+ case FSACTL_RESET_IOP:
+ status = aac_send_reset_adapter(dev, arg);
+ break;
+
default:
status = -ENOTTY;
break;
}
+
+cleanup:
+ mutex_unlock(&dev->ioctl_mutex);
+
return status;
}
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index 7ac8fdb5577b..726c8531b7d3 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,28 +7,14 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* comminit.c
*
* Abstract: This supports the initialization of the host adapter commuication interface.
* This is a platform dependent module for the pci cyclone board.
- *
*/
#include <linux/kernel.h>
@@ -37,9 +24,12 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/mm.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
#include "aacraid.h"
@@ -47,101 +37,195 @@ struct aac_common aac_config = {
.irq_mod = 1
};
+static inline int aac_is_msix_mode(struct aac_dev *dev)
+{
+ u32 status = 0;
+
+ if (aac_is_src(dev))
+ status = src_readl(dev, MUnit.OMR);
+ return (status & AAC_INT_MODE_MSIX);
+}
+
+static inline void aac_change_to_intx(struct aac_dev *dev)
+{
+ aac_src_access_devreg(dev, AAC_DISABLE_MSIX);
+ aac_src_access_devreg(dev, AAC_ENABLE_INTX);
+}
+
static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign)
{
unsigned char *base;
unsigned long size, align;
- const unsigned long fibsize = 4096;
+ const unsigned long fibsize = dev->max_fib_size;
const unsigned long printfbufsiz = 256;
- unsigned long host_rrq_size = 0;
- struct aac_init *init;
+ unsigned long host_rrq_size, aac_init_size;
+ union aac_init *init;
dma_addr_t phys;
unsigned long aac_max_hostphysmempages;
- if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1)
+ if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) ||
+ (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) ||
+ (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3 &&
+ !dev->sa_firmware)) {
+ host_rrq_size =
+ (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)
+ * sizeof(u32);
+ aac_init_size = sizeof(union aac_init);
+ } else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3 &&
+ dev->sa_firmware) {
host_rrq_size = (dev->scsi_host_ptr->can_queue
- + AAC_NUM_MGT_FIB) * sizeof(u32);
- size = fibsize + sizeof(struct aac_init) + commsize +
- commalign + printfbufsiz + host_rrq_size;
-
- base = pci_alloc_consistent(dev->pdev, size, &phys);
+ + AAC_NUM_MGT_FIB) * sizeof(u32) * AAC_MAX_MSIX;
+ aac_init_size = sizeof(union aac_init) +
+ (AAC_MAX_HRRQ - 1) * sizeof(struct _rrq);
+ } else {
+ host_rrq_size = 0;
+ aac_init_size = sizeof(union aac_init);
+ }
+ size = fibsize + aac_init_size + commsize + commalign +
+ printfbufsiz + host_rrq_size;
- if(base == NULL)
- {
+ base = dma_alloc_coherent(&dev->pdev->dev, size, &phys, GFP_KERNEL);
+ if (base == NULL) {
printk(KERN_ERR "aacraid: unable to create mapping.\n");
return 0;
}
+
dev->comm_addr = (void *)base;
dev->comm_phys = phys;
dev->comm_size = size;
-
- if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) {
+
+ if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) ||
+ (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) ||
+ (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3)) {
dev->host_rrq = (u32 *)(base + fibsize);
dev->host_rrq_pa = phys + fibsize;
memset(dev->host_rrq, 0, host_rrq_size);
}
- dev->init = (struct aac_init *)(base + fibsize + host_rrq_size);
+ dev->init = (union aac_init *)(base + fibsize + host_rrq_size);
dev->init_pa = phys + fibsize + host_rrq_size;
init = dev->init;
- init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION);
- if (dev->max_fib_size != sizeof(struct hw_fib))
- init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4);
- init->MiniPortRevision = cpu_to_le32(Sa_MINIPORT_REVISION);
- init->fsrev = cpu_to_le32(dev->fsrev);
+ if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) {
+ int i;
+ u64 addr;
+
+ init->r8.init_struct_revision =
+ cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_8);
+ init->r8.init_flags = cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED |
+ INITFLAGS_DRIVER_USES_UTC_TIME |
+ INITFLAGS_DRIVER_SUPPORTS_PM);
+ init->r8.init_flags |=
+ cpu_to_le32(INITFLAGS_DRIVER_SUPPORTS_HBA_MODE);
+ init->r8.rr_queue_count = cpu_to_le32(dev->max_msix);
+ init->r8.max_io_size =
+ cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9);
+ init->r8.max_num_aif = init->r8.reserved1 =
+ init->r8.reserved2 = 0;
+
+ for (i = 0; i < dev->max_msix; i++) {
+ addr = (u64)dev->host_rrq_pa + dev->vector_cap * i *
+ sizeof(u32);
+ init->r8.rrq[i].host_addr_high = cpu_to_le32(
+ upper_32_bits(addr));
+ init->r8.rrq[i].host_addr_low = cpu_to_le32(
+ lower_32_bits(addr));
+ init->r8.rrq[i].msix_id = i;
+ init->r8.rrq[i].element_count = cpu_to_le16(
+ (u16)dev->vector_cap);
+ init->r8.rrq[i].comp_thresh =
+ init->r8.rrq[i].unused = 0;
+ }
- /*
- * Adapter Fibs are the first thing allocated so that they
- * start page aligned
- */
- dev->aif_base_va = (struct hw_fib *)base;
-
- init->AdapterFibsVirtualAddress = 0;
- init->AdapterFibsPhysicalAddress = cpu_to_le32((u32)phys);
- init->AdapterFibsSize = cpu_to_le32(fibsize);
- init->AdapterFibAlign = cpu_to_le32(sizeof(struct hw_fib));
- /*
- * number of 4k pages of host physical memory. The aacraid fw needs
- * this number to be less than 4gb worth of pages. New firmware doesn't
- * have any issues with the mapping system, but older Firmware did, and
- * had *troubles* dealing with the math overloading past 32 bits, thus
- * we must limit this field.
- */
- aac_max_hostphysmempages = dma_get_required_mask(&dev->pdev->dev) >> 12;
- if (aac_max_hostphysmempages < AAC_MAX_HOSTPHYSMEMPAGES)
- init->HostPhysMemPages = cpu_to_le32(aac_max_hostphysmempages);
- else
- init->HostPhysMemPages = cpu_to_le32(AAC_MAX_HOSTPHYSMEMPAGES);
-
- init->InitFlags = 0;
- if (dev->comm_interface == AAC_COMM_MESSAGE) {
- init->InitFlags |= cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED);
- dprintk((KERN_WARNING"aacraid: New Comm Interface enabled\n"));
- } else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) {
- init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_6);
- init->InitFlags |= cpu_to_le32(INITFLAGS_NEW_COMM_TYPE1_SUPPORTED);
- dprintk((KERN_WARNING
- "aacraid: New Comm Interface type1 enabled\n"));
- }
- init->InitFlags |= cpu_to_le32(INITFLAGS_DRIVER_USES_UTC_TIME |
- INITFLAGS_DRIVER_SUPPORTS_PM);
- init->MaxIoCommands = cpu_to_le32(dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB);
- init->MaxIoSize = cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9);
- init->MaxFibSize = cpu_to_le32(dev->max_fib_size);
+ pr_warn("aacraid: Comm Interface type3 enabled\n");
+ } else {
+ init->r7.init_struct_revision =
+ cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION);
+ if (dev->max_fib_size != sizeof(struct hw_fib))
+ init->r7.init_struct_revision =
+ cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4);
+ init->r7.no_of_msix_vectors = cpu_to_le32(SA_MINIPORT_REVISION);
+ init->r7.fsrev = cpu_to_le32(dev->fsrev);
- init->MaxNumAif = cpu_to_le32(dev->max_num_aif);
- init->HostRRQ_AddrHigh = (u32)((u64)dev->host_rrq_pa >> 32);
- init->HostRRQ_AddrLow = (u32)(dev->host_rrq_pa & 0xffffffff);
+ /*
+ * Adapter Fibs are the first thing allocated so that they
+ * start page aligned
+ */
+ dev->aif_base_va = (struct hw_fib *)base;
+ init->r7.adapter_fibs_virtual_address = 0;
+ init->r7.adapter_fibs_physical_address = cpu_to_le32((u32)phys);
+ init->r7.adapter_fibs_size = cpu_to_le32(fibsize);
+ init->r7.adapter_fib_align = cpu_to_le32(sizeof(struct hw_fib));
+
+ /*
+ * number of 4k pages of host physical memory. The aacraid fw
+ * needs this number to be less than 4gb worth of pages. New
+ * firmware doesn't have any issues with the mapping system, but
+ * older Firmware did, and had *troubles* dealing with the math
+ * overloading past 32 bits, thus we must limit this field.
+ */
+ aac_max_hostphysmempages =
+ dma_get_required_mask(&dev->pdev->dev) >> 12;
+ if (aac_max_hostphysmempages < AAC_MAX_HOSTPHYSMEMPAGES)
+ init->r7.host_phys_mem_pages =
+ cpu_to_le32(aac_max_hostphysmempages);
+ else
+ init->r7.host_phys_mem_pages =
+ cpu_to_le32(AAC_MAX_HOSTPHYSMEMPAGES);
+
+ init->r7.init_flags =
+ cpu_to_le32(INITFLAGS_DRIVER_USES_UTC_TIME |
+ INITFLAGS_DRIVER_SUPPORTS_PM);
+ init->r7.max_io_commands =
+ cpu_to_le32(dev->scsi_host_ptr->can_queue +
+ AAC_NUM_MGT_FIB);
+ init->r7.max_io_size =
+ cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9);
+ init->r7.max_fib_size = cpu_to_le32(dev->max_fib_size);
+ init->r7.max_num_aif = cpu_to_le32(dev->max_num_aif);
+
+ if (dev->comm_interface == AAC_COMM_MESSAGE) {
+ init->r7.init_flags |=
+ cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED);
+ pr_warn("aacraid: Comm Interface enabled\n");
+ } else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) {
+ init->r7.init_struct_revision =
+ cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_6);
+ init->r7.init_flags |=
+ cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED |
+ INITFLAGS_NEW_COMM_TYPE1_SUPPORTED |
+ INITFLAGS_FAST_JBOD_SUPPORTED);
+ init->r7.host_rrq_addr_high =
+ cpu_to_le32(upper_32_bits(dev->host_rrq_pa));
+ init->r7.host_rrq_addr_low =
+ cpu_to_le32(lower_32_bits(dev->host_rrq_pa));
+ pr_warn("aacraid: Comm Interface type1 enabled\n");
+ } else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) {
+ init->r7.init_struct_revision =
+ cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_7);
+ init->r7.init_flags |=
+ cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED |
+ INITFLAGS_NEW_COMM_TYPE2_SUPPORTED |
+ INITFLAGS_FAST_JBOD_SUPPORTED);
+ init->r7.host_rrq_addr_high =
+ cpu_to_le32(upper_32_bits(dev->host_rrq_pa));
+ init->r7.host_rrq_addr_low =
+ cpu_to_le32(lower_32_bits(dev->host_rrq_pa));
+ init->r7.no_of_msix_vectors =
+ cpu_to_le32(dev->max_msix);
+ /* must be the COMM_PREFERRED_SETTINGS values */
+ pr_warn("aacraid: Comm Interface type2 enabled\n");
+ }
+ }
/*
* Increment the base address by the amount already used
*/
- base = base + fibsize + host_rrq_size + sizeof(struct aac_init);
+ base = base + fibsize + host_rrq_size + aac_init_size;
phys = (dma_addr_t)((ulong)phys + fibsize + host_rrq_size +
- sizeof(struct aac_init));
+ aac_init_size);
/*
* Align the beginning of Headers to commalign
@@ -153,7 +237,8 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co
* Fill in addresses of the Comm Area Headers and Queues
*/
*commaddr = base;
- init->CommHeaderAddress = cpu_to_le32((u32)phys);
+ if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE3)
+ init->r7.comm_header_address = cpu_to_le32((u32)phys);
/*
* Increment the base address by the size of the CommArea
*/
@@ -163,15 +248,17 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co
* Place the Printf buffer area after the Fast I/O comm area.
*/
dev->printfbuf = (void *)base;
- init->printfbuf = cpu_to_le32(phys);
- init->printfbufsiz = cpu_to_le32(printfbufsiz);
+ if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE3) {
+ init->r7.printfbuf = cpu_to_le32(phys);
+ init->r7.printfbufsiz = cpu_to_le32(printfbufsiz);
+ }
memset(base, 0, printfbufsiz);
return 1;
}
-
+
static void aac_queue_init(struct aac_dev * dev, struct aac_queue * q, u32 *mem, int qsize)
{
- q->numpending = 0;
+ atomic_set(&q->numpending, 0);
q->dev = dev;
init_waitqueue_head(&q->cmdready);
INIT_LIST_HEAD(&q->cmdq);
@@ -185,6 +272,37 @@ static void aac_queue_init(struct aac_dev * dev, struct aac_queue * q, u32 *mem,
q->entries = qsize;
}
+static bool wait_for_io_iter(struct scsi_cmnd *cmd, void *data)
+{
+ int *active = data;
+
+ if (aac_priv(cmd)->owner == AAC_OWNER_FIRMWARE)
+ *active = *active + 1;
+ return true;
+}
+static void aac_wait_for_io_completion(struct aac_dev *aac)
+{
+ int i = 0, active;
+
+ for (i = 60; i; --i) {
+
+ active = 0;
+ scsi_host_busy_iter(aac->scsi_host_ptr,
+ wait_for_io_iter, &active);
+ /*
+ * We can exit If all the commands are complete
+ */
+ if (active == 0)
+ break;
+ dev_info(&aac->pdev->dev,
+ "Wait for %d commands to complete\n", active);
+ ssleep(1);
+ }
+ if (active)
+ dev_err(&aac->pdev->dev,
+ "%d outstanding commands during shutdown\n", active);
+}
+
/**
* aac_send_shutdown - shutdown an adapter
* @dev: Adapter to shutdown
@@ -196,7 +314,18 @@ int aac_send_shutdown(struct aac_dev * dev)
{
struct fib * fibctx;
struct aac_close *cmd;
- int status;
+ int status = 0;
+
+ if (aac_adapter_check_health(dev))
+ return status;
+
+ if (!dev->adapter_shutdown) {
+ mutex_lock(&dev->ioctl_mutex);
+ dev->adapter_shutdown = 1;
+ mutex_unlock(&dev->ioctl_mutex);
+ }
+
+ aac_wait_for_io_completion(dev);
fibctx = aac_fib_alloc(dev);
if (!fibctx)
@@ -204,9 +333,8 @@ int aac_send_shutdown(struct aac_dev * dev)
aac_fib_init(fibctx);
cmd = (struct aac_close *) fib_data(fibctx);
-
cmd->command = cpu_to_le32(VM_CloseAll);
- cmd->cid = cpu_to_le32(0xffffffff);
+ cmd->cid = cpu_to_le32(0xfffffffe);
status = aac_fib_send(ContainerCommand,
fibctx,
@@ -220,6 +348,9 @@ int aac_send_shutdown(struct aac_dev * dev)
/* FIB should be freed only after getting the response from the F/W */
if (status != -ERESTARTSYS)
aac_fib_free(fibctx);
+ if (aac_is_src(dev) &&
+ dev->msi_enabled)
+ aac_set_intx_mode(dev);
return status;
}
@@ -321,38 +452,127 @@ static int aac_comm_init(struct aac_dev * dev)
return 0;
}
+void aac_define_int_mode(struct aac_dev *dev)
+{
+ int i, msi_count, min_msix;
+
+ msi_count = i = 0;
+ /* max. vectors from GET_COMM_PREFERRED_SETTINGS */
+ if (dev->max_msix == 0 ||
+ dev->pdev->device == PMC_DEVICE_S6 ||
+ dev->sync_mode) {
+ dev->max_msix = 1;
+ dev->vector_cap =
+ dev->scsi_host_ptr->can_queue +
+ AAC_NUM_MGT_FIB;
+ return;
+ }
+
+ /* Don't bother allocating more MSI-X vectors than cpus */
+ msi_count = min(dev->max_msix,
+ (unsigned int)num_online_cpus());
+
+ dev->max_msix = msi_count;
+
+ if (msi_count > AAC_MAX_MSIX)
+ msi_count = AAC_MAX_MSIX;
+
+ if (msi_count > 1 &&
+ pci_find_capability(dev->pdev, PCI_CAP_ID_MSIX)) {
+ min_msix = 2;
+ i = pci_alloc_irq_vectors(dev->pdev,
+ min_msix, msi_count, PCI_IRQ_MSIX);
+ if (i > 0) {
+ dev->msi_enabled = 1;
+ msi_count = i;
+ } else {
+ dev->msi_enabled = 0;
+ dev_err(&dev->pdev->dev,
+ "MSIX not supported!! Will try INTX 0x%x.\n", i);
+ }
+ }
+
+ if (!dev->msi_enabled)
+ dev->max_msix = msi_count = 1;
+ else {
+ if (dev->max_msix > msi_count)
+ dev->max_msix = msi_count;
+ }
+ if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3 && dev->sa_firmware)
+ dev->vector_cap = dev->scsi_host_ptr->can_queue +
+ AAC_NUM_MGT_FIB;
+ else
+ dev->vector_cap = (dev->scsi_host_ptr->can_queue +
+ AAC_NUM_MGT_FIB) / msi_count;
+
+}
struct aac_dev *aac_init_adapter(struct aac_dev *dev)
{
u32 status[5];
struct Scsi_Host * host = dev->scsi_host_ptr;
+ extern int aac_sync_mode;
/*
* Check the preferred comm settings, defaults from template.
*/
dev->management_fib_count = 0;
spin_lock_init(&dev->manage_lock);
+ spin_lock_init(&dev->sync_lock);
+ spin_lock_init(&dev->iq_lock);
dev->max_fib_size = sizeof(struct hw_fib);
dev->sg_tablesize = host->sg_tablesize = (dev->max_fib_size
- - sizeof(struct aac_fibhdr)
- - sizeof(struct aac_write) + sizeof(struct sgentry))
+ - sizeof(struct aac_fibhdr) - sizeof(struct aac_write))
/ sizeof(struct sgentry);
dev->comm_interface = AAC_COMM_PRODUCER;
dev->raw_io_interface = dev->raw_io_64 = 0;
+
+ /*
+ * Enable INTX mode, if not done already Enabled
+ */
+ if (aac_is_msix_mode(dev)) {
+ aac_change_to_intx(dev);
+ dev_info(&dev->pdev->dev, "Changed firmware to INTX mode");
+ }
+
if ((!aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES,
- 0, 0, 0, 0, 0, 0, status+0, status+1, status+2, NULL, NULL)) &&
- (status[0] == 0x00000001)) {
- if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_64))
+ 0, 0, 0, 0, 0, 0,
+ status+0, status+1, status+2, status+3, status+4)) &&
+ (status[0] == 0x00000001)) {
+ dev->doorbell_mask = status[3];
+ if (status[1] & AAC_OPT_NEW_COMM_64)
dev->raw_io_64 = 1;
- if (dev->a_ops.adapter_comm) {
- if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_TYPE1)) {
+ dev->sync_mode = aac_sync_mode;
+ if (dev->a_ops.adapter_comm &&
+ (status[1] & AAC_OPT_NEW_COMM)) {
+ dev->comm_interface = AAC_COMM_MESSAGE;
+ dev->raw_io_interface = 1;
+ if ((status[1] & AAC_OPT_NEW_COMM_TYPE1)) {
+ /* driver supports TYPE1 (Tupelo) */
dev->comm_interface = AAC_COMM_MESSAGE_TYPE1;
- dev->raw_io_interface = 1;
- } else if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM)) {
- dev->comm_interface = AAC_COMM_MESSAGE;
- dev->raw_io_interface = 1;
+ } else if (status[1] & AAC_OPT_NEW_COMM_TYPE2) {
+ /* driver supports TYPE2 (Denali, Yosemite) */
+ dev->comm_interface = AAC_COMM_MESSAGE_TYPE2;
+ } else if (status[1] & AAC_OPT_NEW_COMM_TYPE3) {
+ /* driver supports TYPE3 (Yosemite, Thor) */
+ dev->comm_interface = AAC_COMM_MESSAGE_TYPE3;
+ } else if (status[1] & AAC_OPT_NEW_COMM_TYPE4) {
+ /* not supported TYPE - switch to sync. mode */
+ dev->comm_interface = AAC_COMM_MESSAGE_TYPE2;
+ dev->sync_mode = 1;
}
}
+ if ((status[1] & le32_to_cpu(AAC_OPT_EXTENDED)) &&
+ (status[4] & le32_to_cpu(AAC_EXTOPT_SA_FIRMWARE)))
+ dev->sa_firmware = 1;
+ else
+ dev->sa_firmware = 0;
+
+ if (status[4] & le32_to_cpu(AAC_EXTOPT_SOFT_RESET))
+ dev->soft_reset_support = 1;
+ else
+ dev->soft_reset_support = 0;
+
if ((dev->comm_interface == AAC_COMM_MESSAGE) &&
(status[2] > dev->base_size)) {
aac_adapter_ioremap(dev, 0);
@@ -368,6 +588,9 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
}
}
}
+ dev->max_msix = 0;
+ dev->msi_enabled = 0;
+ dev->adapter_shutdown = 0;
if ((!aac_adapter_sync_cmd(dev, GET_COMM_PREFERRED_SETTINGS,
0, 0, 0, 0, 0, 0,
status+0, status+1, status+2, status+3, status+4))
@@ -384,54 +607,27 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
dev->max_fib_size = status[1] & 0xFFE0;
host->sg_tablesize = status[2] >> 16;
dev->sg_tablesize = status[2] & 0xFFFF;
- host->can_queue = (status[3] & 0xFFFF) - AAC_NUM_MGT_FIB;
+ if (aac_is_src(dev)) {
+ if (host->can_queue > (status[3] >> 16) -
+ AAC_NUM_MGT_FIB)
+ host->can_queue = (status[3] >> 16) -
+ AAC_NUM_MGT_FIB;
+ } else if (host->can_queue > (status[3] & 0xFFFF) -
+ AAC_NUM_MGT_FIB)
+ host->can_queue = (status[3] & 0xFFFF) -
+ AAC_NUM_MGT_FIB;
+
dev->max_num_aif = status[4] & 0xFFFF;
- /*
- * NOTE:
- * All these overrides are based on a fixed internal
- * knowledge and understanding of existing adapters,
- * acbsize should be set with caution.
- */
- if (acbsize == 512) {
- host->max_sectors = AAC_MAX_32BIT_SGBCOUNT;
- dev->max_fib_size = 512;
- dev->sg_tablesize = host->sg_tablesize
- = (512 - sizeof(struct aac_fibhdr)
- - sizeof(struct aac_write) + sizeof(struct sgentry))
- / sizeof(struct sgentry);
- host->can_queue = AAC_NUM_IO_FIB;
- } else if (acbsize == 2048) {
- host->max_sectors = 512;
- dev->max_fib_size = 2048;
- host->sg_tablesize = 65;
- dev->sg_tablesize = 81;
- host->can_queue = 512 - AAC_NUM_MGT_FIB;
- } else if (acbsize == 4096) {
- host->max_sectors = 1024;
- dev->max_fib_size = 4096;
- host->sg_tablesize = 129;
- dev->sg_tablesize = 166;
- host->can_queue = 256 - AAC_NUM_MGT_FIB;
- } else if (acbsize == 8192) {
- host->max_sectors = 2048;
- dev->max_fib_size = 8192;
- host->sg_tablesize = 257;
- dev->sg_tablesize = 337;
- host->can_queue = 128 - AAC_NUM_MGT_FIB;
- } else if (acbsize > 0) {
- printk("Illegal acbsize=%d ignored\n", acbsize);
- }
}
- {
-
- if (numacb > 0) {
- if (numacb < host->can_queue)
- host->can_queue = numacb;
- else
- printk("numacb=%d ignored\n", numacb);
- }
+ if (numacb > 0) {
+ if (numacb < host->can_queue)
+ host->can_queue = numacb;
+ else
+ pr_warn("numacb=%d ignored\n", numacb);
}
+ if (aac_is_src(dev))
+ aac_define_int_mode(dev);
/*
* Ok now init the communication subsystem
*/
@@ -444,6 +640,7 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
if (aac_comm_init(dev)<0){
kfree(dev->queues);
+ dev->queues = NULL;
return NULL;
}
/*
@@ -451,12 +648,13 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
*/
if (aac_fib_setup(dev) < 0) {
kfree(dev->queues);
+ dev->queues = NULL;
return NULL;
}
INIT_LIST_HEAD(&dev->fib_list);
+ INIT_LIST_HEAD(&dev->sync_fib_list);
return dev;
}
-
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index e7d0d47b9185..7d9a4dce236b 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,32 +7,19 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* commsup.c
*
* Abstract: Contain all routines that are required for FSA host/adapter
* communication.
- *
*/
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/crash_dump.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/pci.h>
@@ -42,7 +30,7 @@
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>
-#include <linux/semaphore.h>
+#include <linux/bcd.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
@@ -60,14 +48,16 @@
static int fib_map_alloc(struct aac_dev *dev)
{
+ dev->max_cmd_size = AAC_MAX_NATIVE_SIZE;
+
dprintk((KERN_INFO
- "allocate hardware fibs pci_alloc_consistent(%p, %d * (%d + %d), %p)\n",
- dev->pdev, dev->max_fib_size, dev->scsi_host_ptr->can_queue,
+ "allocate hardware fibs dma_alloc_coherent(%p, %d * (%d + %d), %p)\n",
+ &dev->pdev->dev, dev->max_cmd_size, dev->scsi_host_ptr->can_queue,
AAC_NUM_MGT_FIB, &dev->hw_fib_pa));
- dev->hw_fib_va = pci_alloc_consistent(dev->pdev,
- (dev->max_fib_size + sizeof(struct aac_fib_xporthdr))
+ dev->hw_fib_va = dma_alloc_coherent(&dev->pdev->dev,
+ (dev->max_cmd_size + sizeof(struct aac_fib_xporthdr))
* (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) + (ALIGN32 - 1),
- &dev->hw_fib_pa);
+ &dev->hw_fib_pa, GFP_KERNEL);
if (dev->hw_fib_va == NULL)
return -ENOMEM;
return 0;
@@ -83,13 +73,46 @@ static int fib_map_alloc(struct aac_dev *dev)
void aac_fib_map_free(struct aac_dev *dev)
{
- pci_free_consistent(dev->pdev,
- dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB),
- dev->hw_fib_va, dev->hw_fib_pa);
+ size_t alloc_size;
+ size_t fib_size;
+ int num_fibs;
+
+ if(!dev->hw_fib_va || !dev->max_cmd_size)
+ return;
+
+ num_fibs = dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB;
+ fib_size = dev->max_fib_size + sizeof(struct aac_fib_xporthdr);
+ alloc_size = fib_size * num_fibs + ALIGN32 - 1;
+
+ dma_free_coherent(&dev->pdev->dev, alloc_size, dev->hw_fib_va,
+ dev->hw_fib_pa);
+
dev->hw_fib_va = NULL;
dev->hw_fib_pa = 0;
}
+void aac_fib_vector_assign(struct aac_dev *dev)
+{
+ u32 i = 0;
+ u32 vector = 1;
+ struct fib *fibptr = NULL;
+
+ for (i = 0, fibptr = &dev->fibs[i];
+ i < (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB);
+ i++, fibptr++) {
+ if ((dev->max_msix == 1) ||
+ (i > ((dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB - 1)
+ - dev->vector_cap))) {
+ fibptr->vector_no = 0;
+ } else {
+ fibptr->vector_no = vector;
+ vector++;
+ if (vector == dev->max_msix)
+ vector = 1;
+ }
+ }
+}
+
/**
* aac_fib_setup - setup the fibs
* @dev: Adapter to set up
@@ -104,31 +127,32 @@ int aac_fib_setup(struct aac_dev * dev)
struct hw_fib *hw_fib;
dma_addr_t hw_fib_pa;
int i;
+ u32 max_cmds;
while (((i = fib_map_alloc(dev)) == -ENOMEM)
&& (dev->scsi_host_ptr->can_queue > (64 - AAC_NUM_MGT_FIB))) {
- dev->init->MaxIoCommands = cpu_to_le32((dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) >> 1);
- dev->scsi_host_ptr->can_queue = le32_to_cpu(dev->init->MaxIoCommands) - AAC_NUM_MGT_FIB;
+ max_cmds = (dev->scsi_host_ptr->can_queue+AAC_NUM_MGT_FIB) >> 1;
+ dev->scsi_host_ptr->can_queue = max_cmds - AAC_NUM_MGT_FIB;
+ if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE3)
+ dev->init->r7.max_io_commands = cpu_to_le32(max_cmds);
}
if (i<0)
return -ENOMEM;
- /* 32 byte alignment for PMC */
- hw_fib_pa = (dev->hw_fib_pa + (ALIGN32 - 1)) & ~(ALIGN32 - 1);
- dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va +
- (hw_fib_pa - dev->hw_fib_pa));
- dev->hw_fib_pa = hw_fib_pa;
memset(dev->hw_fib_va, 0,
- (dev->max_fib_size + sizeof(struct aac_fib_xporthdr)) *
+ (dev->max_cmd_size + sizeof(struct aac_fib_xporthdr)) *
(dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB));
+ /* 32 byte alignment for PMC */
+ hw_fib_pa = (dev->hw_fib_pa + (ALIGN32 - 1)) & ~(ALIGN32 - 1);
+ hw_fib = (struct hw_fib *)((unsigned char *)dev->hw_fib_va +
+ (hw_fib_pa - dev->hw_fib_pa));
+
/* add Xport header */
- dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va +
+ hw_fib = (struct hw_fib *)((unsigned char *)hw_fib +
sizeof(struct aac_fib_xporthdr));
- dev->hw_fib_pa += sizeof(struct aac_fib_xporthdr);
+ hw_fib_pa += sizeof(struct aac_fib_xporthdr);
- hw_fib = dev->hw_fib_va;
- hw_fib_pa = dev->hw_fib_pa;
/*
* Initialise the fibs
*/
@@ -136,32 +160,77 @@ int aac_fib_setup(struct aac_dev * dev)
i < (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB);
i++, fibptr++)
{
+ fibptr->flags = 0;
+ fibptr->size = sizeof(struct fib);
fibptr->dev = dev;
fibptr->hw_fib_va = hw_fib;
fibptr->data = (void *) fibptr->hw_fib_va->data;
fibptr->next = fibptr+1; /* Forward chain the fibs */
- sema_init(&fibptr->event_wait, 0);
+ init_completion(&fibptr->event_wait);
spin_lock_init(&fibptr->event_lock);
hw_fib->header.XferState = cpu_to_le32(0xffffffff);
- hw_fib->header.SenderSize = cpu_to_le16(dev->max_fib_size);
+ hw_fib->header.SenderSize =
+ cpu_to_le16(dev->max_fib_size); /* ?? max_cmd_size */
fibptr->hw_fib_pa = hw_fib_pa;
+ fibptr->hw_sgl_pa = hw_fib_pa +
+ offsetof(struct aac_hba_cmd_req, sge[2]);
+ /*
+ * one element is for the ptr to the separate sg list,
+ * second element for 32 byte alignment
+ */
+ fibptr->hw_error_pa = hw_fib_pa +
+ offsetof(struct aac_native_hba, resp.resp_bytes[0]);
+
hw_fib = (struct hw_fib *)((unsigned char *)hw_fib +
- dev->max_fib_size + sizeof(struct aac_fib_xporthdr));
+ dev->max_cmd_size + sizeof(struct aac_fib_xporthdr));
hw_fib_pa = hw_fib_pa +
- dev->max_fib_size + sizeof(struct aac_fib_xporthdr);
+ dev->max_cmd_size + sizeof(struct aac_fib_xporthdr);
}
+
+ /*
+ *Assign vector numbers to fibs
+ */
+ aac_fib_vector_assign(dev);
+
/*
* Add the fib chain to the free list
*/
dev->fibs[dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB - 1].next = NULL;
/*
- * Enable this to debug out of queue space
- */
- dev->free_fib = &dev->fibs[0];
+ * Set 8 fibs aside for management tools
+ */
+ dev->free_fib = &dev->fibs[dev->scsi_host_ptr->can_queue];
return 0;
}
/**
+ * aac_fib_alloc_tag-allocate a fib using tags
+ * @dev: Adapter to allocate the fib for
+ * @scmd: SCSI command
+ *
+ * Allocate a fib from the adapter fib pool using tags
+ * from the blk layer.
+ */
+
+struct fib *aac_fib_alloc_tag(struct aac_dev *dev, struct scsi_cmnd *scmd)
+{
+ struct fib *fibptr;
+
+ fibptr = &dev->fibs[scsi_cmd_to_rq(scmd)->tag];
+ /*
+ * Null out fields that depend on being zero at the start of
+ * each I/O
+ */
+ fibptr->hw_fib_va->header.XferState = 0;
+ fibptr->type = FSAFS_NTC_FIB_CONTEXT;
+ fibptr->callback_data = NULL;
+ fibptr->callback = NULL;
+ fibptr->flags = 0;
+
+ return fibptr;
+}
+
+/**
* aac_fib_alloc - allocate a fib
* @dev: Adapter to allocate the fib for
*
@@ -207,19 +276,16 @@ struct fib *aac_fib_alloc(struct aac_dev *dev)
void aac_fib_free(struct fib *fibptr)
{
- unsigned long flags, flagsv;
+ unsigned long flags;
- spin_lock_irqsave(&fibptr->event_lock, flagsv);
- if (fibptr->done == 2) {
- spin_unlock_irqrestore(&fibptr->event_lock, flagsv);
+ if (fibptr->done == 2)
return;
- }
- spin_unlock_irqrestore(&fibptr->event_lock, flagsv);
spin_lock_irqsave(&fibptr->dev->fib_lock, flags);
if (unlikely(fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT))
aac_config.fib_timeouts++;
- if (fibptr->hw_fib_va->header.XferState != 0) {
+ if (!(fibptr->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) &&
+ fibptr->hw_fib_va->header.XferState != 0) {
printk(KERN_WARNING "aac_fib_free, XferState != 0, fibptr = 0x%p, XferState = 0x%x\n",
(void*)fibptr,
le32_to_cpu(fibptr->hw_fib_va->header.XferState));
@@ -240,16 +306,16 @@ void aac_fib_init(struct fib *fibptr)
{
struct hw_fib *hw_fib = fibptr->hw_fib_va;
+ memset(&hw_fib->header, 0, sizeof(struct aac_fibhdr));
hw_fib->header.StructType = FIB_MAGIC;
hw_fib->header.Size = cpu_to_le16(fibptr->dev->max_fib_size);
hw_fib->header.XferState = cpu_to_le32(HostOwned | FibInitialized | FibEmpty | FastResponseCapable);
- hw_fib->header.SenderFibAddress = 0; /* Filled in later if needed */
- hw_fib->header.ReceiverFibAddress = cpu_to_le32(fibptr->hw_fib_pa);
+ hw_fib->header.u.ReceiverFibAddress = cpu_to_le32(fibptr->hw_fib_pa);
hw_fib->header.SenderSize = cpu_to_le16(fibptr->dev->max_fib_size);
}
/**
- * fib_deallocate - deallocate a fib
+ * fib_dealloc - deallocate a fib
* @fibptr: fib to deallocate
*
* Will deallocate and return to the free pool the FIB pointed to by the
@@ -259,7 +325,6 @@ void aac_fib_init(struct fib *fibptr)
static void fib_dealloc(struct fib * fibptr)
{
struct hw_fib *hw_fib = fibptr->hw_fib_va;
- BUG_ON(hw_fib->header.StructType != FIB_MAGIC);
hw_fib->header.XferState = 0;
}
@@ -321,7 +386,7 @@ static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entr
/* Queue is full */
if ((*index + 1) == le32_to_cpu(*(q->headers.consumer))) {
printk(KERN_WARNING "Queue %d full, %u outstanding.\n",
- qid, q->numpending);
+ qid, atomic_read(&q->numpending));
return 0;
} else {
*entry = q->base + *index;
@@ -333,8 +398,8 @@ static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entr
* aac_queue_get - get the next free QE
* @dev: Adapter
* @index: Returned index
- * @priority: Priority of fib
- * @fib: Fib to associate with the queue entry
+ * @qid: Queue number
+ * @hw_fib: Fib to associate with the queue entry
* @wait: Wait if queue full
* @fibptr: Driver fib object to go with fib
* @nonotify: Don't notify the adapter
@@ -370,7 +435,7 @@ int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw
entry->size = cpu_to_le32(le16_to_cpu(hw_fib->header.Size));
entry->addr = hw_fib->header.SenderFibAddress;
/* Restore adapters pointer to the FIB */
- hw_fib->header.ReceiverFibAddress = hw_fib->header.SenderFibAddress; /* Let the adapter now where to find its data */
+ hw_fib->header.u.ReceiverFibAddress = hw_fib->header.SenderFibAddress; /* Let the adapter now where to find its data */
map = 0;
}
/*
@@ -414,18 +479,21 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
struct aac_dev * dev = fibptr->dev;
struct hw_fib * hw_fib = fibptr->hw_fib_va;
unsigned long flags = 0;
- unsigned long qflags;
unsigned long mflags = 0;
-
+ unsigned long sflags = 0;
if (!(hw_fib->header.XferState & cpu_to_le32(HostOwned)))
return -EBUSY;
+
+ if (hw_fib->header.XferState & cpu_to_le32(AdapterProcessed))
+ return -EINVAL;
+
/*
* There are 5 cases with the wait and response requested flags.
* The only invalid cases are if the caller requests to wait and
* does not request a response and if the caller does not want a
* response and the Fib is not allocated from pool. If a response
- * is not requesed the Fib will just be deallocaed by the DPC
+ * is not requested the Fib will just be deallocaed by the DPC
* routine when the response comes back from the adapter. No
* further processing will be done besides deleting the Fib. We
* will have a debug mode where the adapter can notify the host
@@ -448,8 +516,15 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
* Map the fib into 32bits by using the fib number
*/
- hw_fib->header.SenderFibAddress = cpu_to_le32(((u32)(fibptr - dev->fibs)) << 2);
- hw_fib->header.SenderData = (u32)(fibptr - dev->fibs);
+ hw_fib->header.SenderFibAddress =
+ cpu_to_le32(((u32)(fibptr - dev->fibs)) << 2);
+
+ /* use the same shifted value for handle to be compatible
+ * with the new native hba command handle
+ */
+ hw_fib->header.Handle =
+ cpu_to_le32((((u32)(fibptr - dev->fibs)) << 2) + 1);
+
/*
* Set FIB state to indicate where it came from and if we want a
* response from the adapter. Also load the command from the
@@ -459,7 +534,6 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
*/
hw_fib->header.Command = cpu_to_le16(command);
hw_fib->header.XferState |= cpu_to_le32(SentFromHost);
- fibptr->hw_fib_va->header.Flags = 0; /* 0 the flags field - internal only*/
/*
* Set the size of the Fib we want to send to the adapter
*/
@@ -512,6 +586,31 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
spin_lock_irqsave(&fibptr->event_lock, flags);
}
+ if (dev->sync_mode) {
+ if (wait)
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+ spin_lock_irqsave(&dev->sync_lock, sflags);
+ if (dev->sync_fib) {
+ list_add_tail(&fibptr->fiblink, &dev->sync_fib_list);
+ spin_unlock_irqrestore(&dev->sync_lock, sflags);
+ } else {
+ dev->sync_fib = fibptr;
+ spin_unlock_irqrestore(&dev->sync_lock, sflags);
+ aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB,
+ (u32)fibptr->hw_fib_pa, 0, 0, 0, 0, 0,
+ NULL, NULL, NULL, NULL, NULL);
+ }
+ if (wait) {
+ fibptr->flags |= FIB_CONTEXT_FLAG_WAIT;
+ if (wait_for_completion_interruptible(&fibptr->event_wait)) {
+ fibptr->flags &= ~FIB_CONTEXT_FLAG_WAIT;
+ return -EFAULT;
+ }
+ return 0;
+ }
+ return -EINPROGRESS;
+ }
+
if (aac_adapter_deliver(fibptr) != 0) {
printk(KERN_ERR "aac_fib_send: returned -EBUSY\n");
if (wait) {
@@ -538,14 +637,12 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
* functioning because an interrupt routing or other
* hardware failure has occurred.
*/
- unsigned long count = 36000000L; /* 3 minutes */
- while (down_trylock(&fibptr->event_wait)) {
+ unsigned long timeout = jiffies + (180 * HZ); /* 3 minutes */
+ while (!try_wait_for_completion(&fibptr->event_wait)) {
int blink;
- if (--count == 0) {
+ if (time_is_before_eq_jiffies(timeout)) {
struct aac_queue * q = &dev->queues->queue[AdapNormCmdQueue];
- spin_lock_irqsave(q->lock, qflags);
- q->numpending--;
- spin_unlock_irqrestore(q->lock, qflags);
+ atomic_dec(&q->numpending);
if (wait == -1) {
printk(KERN_ERR "aacraid: aac_fib_send: first asynchronous command timed out.\n"
"Usually a result of a PCI interrupt routing problem;\n"
@@ -554,6 +651,10 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
}
return -ETIMEDOUT;
}
+
+ if (unlikely(aac_pci_offline(dev)))
+ return -EFAULT;
+
if ((blink = aac_adapter_check_health(dev)) > 0) {
if (wait == -1) {
printk(KERN_ERR "aacraid: aac_fib_send: adapter blinkLED 0x%x.\n"
@@ -562,11 +663,14 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
}
return -EFAULT;
}
- udelay(5);
+ /*
+ * Allow other processes / CPUS to use core
+ */
+ schedule();
}
- } else if (down_interruptible(&fibptr->event_wait)) {
+ } else if (wait_for_completion_interruptible(&fibptr->event_wait)) {
/* Do nothing ... satisfy
- * down_interruptible must_check */
+ * wait_for_completion_interruptible must_check */
}
spin_lock_irqsave(&fibptr->event_lock, flags);
@@ -592,6 +696,88 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
return 0;
}
+int aac_hba_send(u8 command, struct fib *fibptr, fib_callback callback,
+ void *callback_data)
+{
+ struct aac_dev *dev = fibptr->dev;
+ int wait;
+ unsigned long flags = 0;
+ unsigned long mflags = 0;
+ struct aac_hba_cmd_req *hbacmd = (struct aac_hba_cmd_req *)
+ fibptr->hw_fib_va;
+
+ fibptr->flags = (FIB_CONTEXT_FLAG | FIB_CONTEXT_FLAG_NATIVE_HBA);
+ if (callback) {
+ wait = 0;
+ fibptr->callback = callback;
+ fibptr->callback_data = callback_data;
+ } else
+ wait = 1;
+
+
+ hbacmd->iu_type = command;
+
+ if (command == HBA_IU_TYPE_SCSI_CMD_REQ) {
+ /* bit1 of request_id must be 0 */
+ hbacmd->request_id =
+ cpu_to_le32((((u32)(fibptr - dev->fibs)) << 2) + 1);
+ fibptr->flags |= FIB_CONTEXT_FLAG_SCSI_CMD;
+ } else
+ return -EINVAL;
+
+
+ if (wait) {
+ spin_lock_irqsave(&dev->manage_lock, mflags);
+ if (dev->management_fib_count >= AAC_NUM_MGT_FIB) {
+ spin_unlock_irqrestore(&dev->manage_lock, mflags);
+ return -EBUSY;
+ }
+ dev->management_fib_count++;
+ spin_unlock_irqrestore(&dev->manage_lock, mflags);
+ spin_lock_irqsave(&fibptr->event_lock, flags);
+ }
+
+ if (aac_adapter_deliver(fibptr) != 0) {
+ if (wait) {
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+ spin_lock_irqsave(&dev->manage_lock, mflags);
+ dev->management_fib_count--;
+ spin_unlock_irqrestore(&dev->manage_lock, mflags);
+ }
+ return -EBUSY;
+ }
+ FIB_COUNTER_INCREMENT(aac_config.NativeSent);
+
+ if (wait) {
+
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+
+ if (unlikely(aac_pci_offline(dev)))
+ return -EFAULT;
+
+ fibptr->flags |= FIB_CONTEXT_FLAG_WAIT;
+ if (wait_for_completion_interruptible(&fibptr->event_wait))
+ fibptr->done = 2;
+ fibptr->flags &= ~(FIB_CONTEXT_FLAG_WAIT);
+
+ spin_lock_irqsave(&fibptr->event_lock, flags);
+ if ((fibptr->done == 0) || (fibptr->done == 2)) {
+ fibptr->done = 2; /* Tell interrupt we aborted */
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+ return -ERESTARTSYS;
+ }
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+ WARN_ON(fibptr->done == 0);
+
+ if (unlikely(fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT))
+ return -ETIMEDOUT;
+
+ return 0;
+ }
+
+ return -EINPROGRESS;
+}
+
/**
* aac_consumer_get - get the top of the queue
* @dev: Adapter
@@ -682,7 +868,9 @@ int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size)
unsigned long nointr = 0;
unsigned long qflags;
- if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) {
+ if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1 ||
+ dev->comm_interface == AAC_COMM_MESSAGE_TYPE2 ||
+ dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) {
kfree(hw_fib);
return 0;
}
@@ -695,7 +883,9 @@ int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size)
/*
* If we plan to do anything check the structure type first.
*/
- if (hw_fib->header.StructType != FIB_MAGIC) {
+ if (hw_fib->header.StructType != FIB_MAGIC &&
+ hw_fib->header.StructType != FIB_MAGIC2 &&
+ hw_fib->header.StructType != FIB_MAGIC2_64) {
if (dev->comm_interface == AAC_COMM_MESSAGE)
kfree(hw_fib);
return -EINVAL;
@@ -737,27 +927,34 @@ int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size)
/**
* aac_fib_complete - fib completion handler
- * @fib: FIB to complete
+ * @fibptr: FIB to complete
*
* Will do all necessary work to complete a FIB.
*/
int aac_fib_complete(struct fib *fibptr)
{
- unsigned long flags;
struct hw_fib * hw_fib = fibptr->hw_fib_va;
+ if (fibptr->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) {
+ fib_dealloc(fibptr);
+ return 0;
+ }
+
/*
- * Check for a fib which has already been completed
+ * Check for a fib which has already been completed or with a
+ * status wait timeout
*/
- if (hw_fib->header.XferState == 0)
+ if (hw_fib->header.XferState == 0 || fibptr->done == 2)
return 0;
/*
* If we plan to do anything check the structure type first.
*/
- if (hw_fib->header.StructType != FIB_MAGIC)
+ if (hw_fib->header.StructType != FIB_MAGIC &&
+ hw_fib->header.StructType != FIB_MAGIC2 &&
+ hw_fib->header.StructType != FIB_MAGIC2_64)
return -EINVAL;
/*
* This block completes a cdb which orginated on the host and we
@@ -765,12 +962,6 @@ int aac_fib_complete(struct fib *fibptr)
* command is complete that we had sent to the adapter and this
* cdb could be reused.
*/
- spin_lock_irqsave(&fibptr->event_lock, flags);
- if (fibptr->done == 2) {
- spin_unlock_irqrestore(&fibptr->event_lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&fibptr->event_lock, flags);
if((hw_fib->header.XferState & cpu_to_le32(SentFromHost)) &&
(hw_fib->header.XferState & cpu_to_le32(AdapterProcessed)))
@@ -825,7 +1016,33 @@ void aac_printf(struct aac_dev *dev, u32 val)
memset(cp, 0, 256);
}
+static inline int aac_aif_data(struct aac_aifcmd *aifcmd, uint32_t index)
+{
+ return le32_to_cpu(((__le32 *)aifcmd->data)[index]);
+}
+
+static void aac_handle_aif_bu(struct aac_dev *dev, struct aac_aifcmd *aifcmd)
+{
+ switch (aac_aif_data(aifcmd, 1)) {
+ case AifBuCacheDataLoss:
+ if (aac_aif_data(aifcmd, 2))
+ dev_info(&dev->pdev->dev, "Backup unit had cache data loss - [%d]\n",
+ aac_aif_data(aifcmd, 2));
+ else
+ dev_info(&dev->pdev->dev, "Backup Unit had cache data loss\n");
+ break;
+ case AifBuCacheDataRecover:
+ if (aac_aif_data(aifcmd, 2))
+ dev_info(&dev->pdev->dev, "DDR cache data recovered successfully - [%d]\n",
+ aac_aif_data(aifcmd, 2));
+ else
+ dev_info(&dev->pdev->dev, "DDR cache data recovered successfully\n");
+ break;
+ }
+}
+
+#define AIF_SNIFF_TIMEOUT (500*HZ)
/**
* aac_handle_aif - Handle a message from the firmware
* @dev: Which adapter this fib is from
@@ -834,8 +1051,6 @@ void aac_printf(struct aac_dev *dev, u32 val)
* This routine handles a driver notify fib from the adapter and
* dispatches it to the appropriate routine for handling.
*/
-
-#define AIF_SNIFF_TIMEOUT (30*HZ)
static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
{
struct hw_fib * hw_fib = fibptr->hw_fib_va;
@@ -864,6 +1079,28 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
switch (le32_to_cpu(aifcmd->command)) {
case AifCmdDriverNotify:
switch (le32_to_cpu(((__le32 *)aifcmd->data)[0])) {
+ case AifRawDeviceRemove:
+ container = le32_to_cpu(((__le32 *)aifcmd->data)[1]);
+ if ((container >> 28)) {
+ container = (u32)-1;
+ break;
+ }
+ channel = (container >> 24) & 0xF;
+ if (channel >= dev->maximum_num_channels) {
+ container = (u32)-1;
+ break;
+ }
+ id = container & 0xFFFF;
+ if (id >= dev->maximum_num_physicals) {
+ container = (u32)-1;
+ break;
+ }
+ lun = (container >> 16) & 0xFF;
+ container = (u32)-1;
+ channel = aac_phys_to_logical(channel);
+ device_config_needed = DELETE;
+ break;
+
/*
* Morph or Expand complete
*/
@@ -1011,6 +1248,8 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
switch (le32_to_cpu(((__le32 *)aifcmd->data)[3])) {
case EM_DRIVE_INSERTION:
case EM_DRIVE_REMOVAL:
+ case EM_SES_DRIVE_INSERTION:
+ case EM_SES_DRIVE_REMOVAL:
container = le32_to_cpu(
((__le32 *)aifcmd->data)[2]);
if ((container >> 28)) {
@@ -1036,12 +1275,17 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
}
channel = aac_phys_to_logical(channel);
device_config_needed =
- (((__le32 *)aifcmd->data)[3]
- == cpu_to_le32(EM_DRIVE_INSERTION)) ?
+ ((((__le32 *)aifcmd->data)[3]
+ == cpu_to_le32(EM_DRIVE_INSERTION)) ||
+ (((__le32 *)aifcmd->data)[3]
+ == cpu_to_le32(EM_SES_DRIVE_INSERTION))) ?
ADD : DELETE;
break;
}
break;
+ case AifBuManagerEvent:
+ aac_handle_aif_bu(dev, aifcmd);
+ break;
}
/*
@@ -1112,18 +1356,19 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
container = 0;
retry_next:
- if (device_config_needed == NOTHING)
- for (; container < dev->maximum_num_containers; ++container) {
- if ((dev->fsa_dev[container].config_waiting_on == 0) &&
- (dev->fsa_dev[container].config_needed != NOTHING) &&
- time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) {
- device_config_needed =
- dev->fsa_dev[container].config_needed;
- dev->fsa_dev[container].config_needed = NOTHING;
- channel = CONTAINER_TO_CHANNEL(container);
- id = CONTAINER_TO_ID(container);
- lun = CONTAINER_TO_LUN(container);
- break;
+ if (device_config_needed == NOTHING) {
+ for (; container < dev->maximum_num_containers; ++container) {
+ if ((dev->fsa_dev[container].config_waiting_on == 0) &&
+ (dev->fsa_dev[container].config_needed != NOTHING) &&
+ time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) {
+ device_config_needed =
+ dev->fsa_dev[container].config_needed;
+ dev->fsa_dev[container].config_needed = NOTHING;
+ channel = CONTAINER_TO_CHANNEL(container);
+ id = CONTAINER_TO_ID(container);
+ lun = CONTAINER_TO_LUN(container);
+ break;
+ }
}
}
if (device_config_needed == NOTHING)
@@ -1178,7 +1423,7 @@ retry_next:
"enclosure services event");
scsi_device_set_state(device, SDEV_RUNNING);
}
- /* FALLTHRU */
+ fallthrough;
case CHANGE:
if ((channel == CONTAINER_CHANNEL)
&& (!dev->fsa_dev[container].valid)) {
@@ -1194,7 +1439,8 @@ retry_next:
#endif
break;
}
- scsi_rescan_device(&device->sdev_gendev);
+ scsi_rescan_device(device);
+ break;
default:
break;
@@ -1211,15 +1457,23 @@ retry_next:
}
}
-static int _aac_reset_adapter(struct aac_dev *aac, int forced)
+static void aac_schedule_bus_scan(struct aac_dev *aac)
+{
+ if (aac->sa_firmware)
+ aac_schedule_safw_scan_worker(aac);
+ else
+ aac_schedule_src_reinit_aif_worker(aac);
+}
+
+static int _aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type)
{
int index, quirks;
int retval;
- struct Scsi_Host *host;
- struct scsi_device *dev;
- struct scsi_cmnd *command;
- struct scsi_cmnd *command_list;
+ struct Scsi_Host *host = aac->scsi_host_ptr;
int jafo = 0;
+ int bled;
+ u64 dmamask;
+ int num_of_fibs = 0;
/*
* Assumptions:
@@ -1231,12 +1485,11 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
* - The card is dead, or will be very shortly ;-/ so no new
* commands are completing in the interrupt service.
*/
- host = aac->scsi_host_ptr;
- scsi_block_requests(host);
aac_adapter_disable_int(aac);
- if (aac->thread->pid != current->pid) {
+ if (aac->thread && aac->thread->pid != current->pid) {
spin_unlock_irq(host->host_lock);
kthread_stop(aac->thread);
+ aac->thread = NULL;
jafo = 1;
}
@@ -1244,7 +1497,8 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
* If a positive health, means in a known DEAD PANIC
* state and the adapter could be reset to `try again'.
*/
- retval = aac_adapter_restart(aac, forced ? 0 : aac_adapter_check_health(aac));
+ bled = forced ? 0 : aac_adapter_check_health(aac);
+ retval = aac_adapter_restart(aac, bled, reset_type);
if (retval)
goto out;
@@ -1252,13 +1506,23 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
/*
* Loop through the fibs, close the synchronous FIBS
*/
- for (retval = 1, index = 0; index < (aac->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); index++) {
+ retval = 1;
+ num_of_fibs = aac->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB;
+ for (index = 0; index < num_of_fibs; index++) {
+
struct fib *fib = &aac->fibs[index];
- if (!(fib->hw_fib_va->header.XferState & cpu_to_le32(NoResponseExpected | Async)) &&
- (fib->hw_fib_va->header.XferState & cpu_to_le32(ResponseExpected))) {
+ __le32 XferState = fib->hw_fib_va->header.XferState;
+ bool is_response_expected = false;
+
+ if (!(XferState & cpu_to_le32(NoResponseExpected | Async)) &&
+ (XferState & cpu_to_le32(ResponseExpected)))
+ is_response_expected = true;
+
+ if (is_response_expected
+ || fib->flags & FIB_CONTEXT_FLAG_WAIT) {
unsigned long flagv;
spin_lock_irqsave(&fib->event_lock, flagv);
- up(&fib->event_wait);
+ complete(&fib->event_wait);
spin_unlock_irqrestore(&fib->event_lock, flagv);
schedule();
retval = 0;
@@ -1276,34 +1540,44 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
* will ensure that i/o is queisced and the card is flushed in that
* case.
*/
+ aac_free_irq(aac);
aac_fib_map_free(aac);
- pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys);
+ dma_free_coherent(&aac->pdev->dev, aac->comm_size, aac->comm_addr,
+ aac->comm_phys);
+ aac_adapter_ioremap(aac, 0);
aac->comm_addr = NULL;
aac->comm_phys = 0;
kfree(aac->queues);
aac->queues = NULL;
- free_irq(aac->pdev->irq, aac);
kfree(aac->fsa_dev);
aac->fsa_dev = NULL;
+
+ dmamask = DMA_BIT_MASK(32);
quirks = aac_get_driver_ident(index)->quirks;
- if (quirks & AAC_QUIRK_31BIT) {
- if (((retval = pci_set_dma_mask(aac->pdev, DMA_BIT_MASK(31)))) ||
- ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_BIT_MASK(31)))))
- goto out;
- } else {
- if (((retval = pci_set_dma_mask(aac->pdev, DMA_BIT_MASK(32)))) ||
- ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_BIT_MASK(32)))))
- goto out;
+ if (quirks & AAC_QUIRK_31BIT)
+ retval = dma_set_mask(&aac->pdev->dev, dmamask);
+ else if (!(quirks & AAC_QUIRK_SRC))
+ retval = dma_set_mask(&aac->pdev->dev, dmamask);
+ else
+ retval = dma_set_coherent_mask(&aac->pdev->dev, dmamask);
+
+ if (quirks & AAC_QUIRK_31BIT && !retval) {
+ dmamask = DMA_BIT_MASK(31);
+ retval = dma_set_coherent_mask(&aac->pdev->dev, dmamask);
}
+
+ if (retval)
+ goto out;
+
if ((retval = (*(aac_get_driver_ident(index)->init))(aac)))
goto out;
- if (quirks & AAC_QUIRK_31BIT)
- if ((retval = pci_set_dma_mask(aac->pdev, DMA_BIT_MASK(32))))
- goto out;
+
if (jafo) {
- aac->thread = kthread_run(aac_command_thread, aac, aac->name);
+ aac->thread = kthread_run(aac_command_thread, aac, "%s",
+ aac->name);
if (IS_ERR(aac->thread)) {
retval = PTR_ERR(aac->thread);
+ aac->thread = NULL;
goto out;
}
}
@@ -1322,42 +1596,33 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
* This is where the assumption that the Adapter is quiesced
* is important.
*/
- command_list = NULL;
- __shost_for_each_device(dev, host) {
- unsigned long flags;
- spin_lock_irqsave(&dev->list_lock, flags);
- list_for_each_entry(command, &dev->cmd_list, list)
- if (command->SCp.phase == AAC_OWNER_FIRMWARE) {
- command->SCp.buffer = (struct scatterlist *)command_list;
- command_list = command;
- }
- spin_unlock_irqrestore(&dev->list_lock, flags);
- }
- while ((command = command_list)) {
- command_list = (struct scsi_cmnd *)command->SCp.buffer;
- command->SCp.buffer = NULL;
- command->result = DID_OK << 16
- | COMMAND_COMPLETE << 8
- | SAM_STAT_TASK_SET_FULL;
- command->SCp.phase = AAC_OWNER_ERROR_HANDLER;
- command->scsi_done(command);
- }
- retval = 0;
+ scsi_host_complete_all_commands(host, DID_RESET);
+ retval = 0;
out:
aac->in_reset = 0;
- scsi_unblock_requests(host);
+
+ /*
+ * Issue bus rescan to catch any configuration that might have
+ * occurred
+ */
+ if (!retval && !is_kdump_kernel()) {
+ dev_info(&aac->pdev->dev, "Scheduling bus rescan\n");
+ aac_schedule_bus_scan(aac);
+ }
+
if (jafo) {
spin_lock_irq(host->host_lock);
}
return retval;
}
-int aac_reset_adapter(struct aac_dev * aac, int forced)
+int aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type)
{
unsigned long flagv = 0;
- int retval;
- struct Scsi_Host * host;
+ int retval, unblock_retval;
+ struct Scsi_Host *host = aac->scsi_host_ptr;
+ int bled;
if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0)
return -EBUSY;
@@ -1374,41 +1639,20 @@ int aac_reset_adapter(struct aac_dev * aac, int forced)
* target (block maximum 60 seconds). Although not necessary,
* it does make us a good storage citizen.
*/
- host = aac->scsi_host_ptr;
- scsi_block_requests(host);
- if (forced < 2) for (retval = 60; retval; --retval) {
- struct scsi_device * dev;
- struct scsi_cmnd * command;
- int active = 0;
-
- __shost_for_each_device(dev, host) {
- spin_lock_irqsave(&dev->list_lock, flagv);
- list_for_each_entry(command, &dev->cmd_list, list) {
- if (command->SCp.phase == AAC_OWNER_FIRMWARE) {
- active++;
- break;
- }
- }
- spin_unlock_irqrestore(&dev->list_lock, flagv);
- if (active)
- break;
-
- }
- /*
- * We can exit If all the commands are complete
- */
- if (active == 0)
- break;
- ssleep(1);
- }
+ scsi_host_block(host);
/* Quiesce build, flush cache, write through mode */
if (forced < 2)
aac_send_shutdown(aac);
spin_lock_irqsave(host->host_lock, flagv);
- retval = _aac_reset_adapter(aac, forced ? forced : ((aac_check_reset != 0) && (aac_check_reset != 1)));
+ bled = forced ? forced :
+ (aac_check_reset != 0 && aac_check_reset != 1);
+ retval = _aac_reset_adapter(aac, bled, reset_type);
spin_unlock_irqrestore(host->host_lock, flagv);
+ unblock_retval = scsi_host_unblock(host, SDEV_RUNNING);
+ if (!retval)
+ retval = unblock_retval;
if ((forced < 2) && (retval == -ENODEV)) {
/* Unwind aac_send_shutdown() IOP_RESET unsupported/disabled */
struct fib * fibctx = aac_fib_alloc(aac);
@@ -1446,49 +1690,277 @@ int aac_reset_adapter(struct aac_dev * aac, int forced)
return retval;
}
-int aac_check_health(struct aac_dev * aac)
+static inline int is_safw_raid_volume(struct aac_dev *aac, int bus, int target)
{
- int BlinkLED;
- unsigned long time_now, flagv = 0;
- struct list_head * entry;
- struct Scsi_Host * host;
+ return bus == CONTAINER_CHANNEL && target < aac->maximum_num_containers;
+}
- /* Extending the scope of fib_lock slightly to protect aac->in_reset */
- if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0)
- return 0;
+static struct scsi_device *aac_lookup_safw_scsi_device(struct aac_dev *dev,
+ int bus,
+ int target)
+{
+ if (bus != CONTAINER_CHANNEL)
+ bus = aac_phys_to_logical(bus);
- if (aac->in_reset || !(BlinkLED = aac_adapter_check_health(aac))) {
- spin_unlock_irqrestore(&aac->fib_lock, flagv);
- return 0; /* OK */
+ return scsi_device_lookup(dev->scsi_host_ptr, bus, target, 0);
+}
+
+static int aac_add_safw_device(struct aac_dev *dev, int bus, int target)
+{
+ if (bus != CONTAINER_CHANNEL)
+ bus = aac_phys_to_logical(bus);
+
+ return scsi_add_device(dev->scsi_host_ptr, bus, target, 0);
+}
+
+static void aac_put_safw_scsi_device(struct scsi_device *sdev)
+{
+ if (sdev)
+ scsi_device_put(sdev);
+}
+
+static void aac_remove_safw_device(struct aac_dev *dev, int bus, int target)
+{
+ struct scsi_device *sdev;
+
+ sdev = aac_lookup_safw_scsi_device(dev, bus, target);
+ scsi_remove_device(sdev);
+ aac_put_safw_scsi_device(sdev);
+}
+
+static inline int aac_is_safw_scan_count_equal(struct aac_dev *dev,
+ int bus, int target)
+{
+ return dev->hba_map[bus][target].scan_counter == dev->scan_counter;
+}
+
+static int aac_is_safw_target_valid(struct aac_dev *dev, int bus, int target)
+{
+ if (is_safw_raid_volume(dev, bus, target))
+ return dev->fsa_dev[target].valid;
+ else
+ return aac_is_safw_scan_count_equal(dev, bus, target);
+}
+
+static int aac_is_safw_device_exposed(struct aac_dev *dev, int bus, int target)
+{
+ int is_exposed = 0;
+ struct scsi_device *sdev;
+
+ sdev = aac_lookup_safw_scsi_device(dev, bus, target);
+ if (sdev)
+ is_exposed = 1;
+ aac_put_safw_scsi_device(sdev);
+
+ return is_exposed;
+}
+
+static int aac_update_safw_host_devices(struct aac_dev *dev)
+{
+ int i;
+ int bus;
+ int target;
+ int is_exposed = 0;
+ int rcode = 0;
+
+ rcode = aac_setup_safw_adapter(dev);
+ if (unlikely(rcode < 0)) {
+ goto out;
}
- aac->in_reset = 1;
+ for (i = 0; i < AAC_BUS_TARGET_LOOP; i++) {
+
+ bus = get_bus_number(i);
+ target = get_target_number(i);
+
+ is_exposed = aac_is_safw_device_exposed(dev, bus, target);
+
+ if (aac_is_safw_target_valid(dev, bus, target) && !is_exposed)
+ aac_add_safw_device(dev, bus, target);
+ else if (!aac_is_safw_target_valid(dev, bus, target) &&
+ is_exposed)
+ aac_remove_safw_device(dev, bus, target);
+ }
+out:
+ return rcode;
+}
+
+static int aac_scan_safw_host(struct aac_dev *dev)
+{
+ int rcode = 0;
+
+ rcode = aac_update_safw_host_devices(dev);
+ if (rcode)
+ aac_schedule_safw_scan_worker(dev);
+
+ return rcode;
+}
+
+int aac_scan_host(struct aac_dev *dev)
+{
+ int rcode = 0;
+
+ mutex_lock(&dev->scan_mutex);
+ if (dev->sa_firmware)
+ rcode = aac_scan_safw_host(dev);
+ else
+ scsi_scan_host(dev->scsi_host_ptr);
+ mutex_unlock(&dev->scan_mutex);
+
+ return rcode;
+}
+
+void aac_src_reinit_aif_worker(struct work_struct *work)
+{
+ struct aac_dev *dev = container_of(to_delayed_work(work),
+ struct aac_dev, src_reinit_aif_worker);
+
+ wait_event(dev->scsi_host_ptr->host_wait,
+ !scsi_host_in_recovery(dev->scsi_host_ptr));
+ aac_reinit_aif(dev, dev->cardtype);
+}
+
+/**
+ * aac_handle_sa_aif - Handle a message from the firmware
+ * @dev: Which adapter this fib is from
+ * @fibptr: Pointer to fibptr from adapter
+ *
+ * This routine handles a driver notify fib from the adapter and
+ * dispatches it to the appropriate routine for handling.
+ */
+static void aac_handle_sa_aif(struct aac_dev *dev, struct fib *fibptr)
+{
+ int i;
+ u32 events = 0;
+
+ if (fibptr->hbacmd_size & SA_AIF_HOTPLUG)
+ events = SA_AIF_HOTPLUG;
+ else if (fibptr->hbacmd_size & SA_AIF_HARDWARE)
+ events = SA_AIF_HARDWARE;
+ else if (fibptr->hbacmd_size & SA_AIF_PDEV_CHANGE)
+ events = SA_AIF_PDEV_CHANGE;
+ else if (fibptr->hbacmd_size & SA_AIF_LDEV_CHANGE)
+ events = SA_AIF_LDEV_CHANGE;
+ else if (fibptr->hbacmd_size & SA_AIF_BPSTAT_CHANGE)
+ events = SA_AIF_BPSTAT_CHANGE;
+ else if (fibptr->hbacmd_size & SA_AIF_BPCFG_CHANGE)
+ events = SA_AIF_BPCFG_CHANGE;
+
+ switch (events) {
+ case SA_AIF_HOTPLUG:
+ case SA_AIF_HARDWARE:
+ case SA_AIF_PDEV_CHANGE:
+ case SA_AIF_LDEV_CHANGE:
+ case SA_AIF_BPCFG_CHANGE:
+
+ aac_scan_host(dev);
+
+ break;
+
+ case SA_AIF_BPSTAT_CHANGE:
+ /* currently do nothing */
+ break;
+ }
+
+ for (i = 1; i <= 10; ++i) {
+ events = src_readl(dev, MUnit.IDR);
+ if (events & (1<<23)) {
+ pr_warn(" AIF not cleared by firmware - %d/%d)\n",
+ i, 10);
+ ssleep(1);
+ }
+ }
+}
+
+static int get_fib_count(struct aac_dev *dev)
+{
+ unsigned int num = 0;
+ struct list_head *entry;
+ unsigned long flagv;
- /* Fake up an AIF:
- * aac_aifcmd.command = AifCmdEventNotify = 1
- * aac_aifcmd.seqnum = 0xFFFFFFFF
- * aac_aifcmd.data[0] = AifEnExpEvent = 23
- * aac_aifcmd.data[1] = AifExeFirmwarePanic = 3
- * aac.aifcmd.data[2] = AifHighPriority = 3
- * aac.aifcmd.data[3] = BlinkLED
+ /*
+ * Warning: no sleep allowed while
+ * holding spinlock. We take the estimate
+ * and pre-allocate a set of fibs outside the
+ * lock.
*/
+ num = le32_to_cpu(dev->init->r7.adapter_fibs_size)
+ / sizeof(struct hw_fib); /* some extra */
+ spin_lock_irqsave(&dev->fib_lock, flagv);
+ entry = dev->fib_list.next;
+ while (entry != &dev->fib_list) {
+ entry = entry->next;
+ ++num;
+ }
+ spin_unlock_irqrestore(&dev->fib_lock, flagv);
- time_now = jiffies/HZ;
- entry = aac->fib_list.next;
+ return num;
+}
+
+static int fillup_pools(struct aac_dev *dev, struct hw_fib **hw_fib_pool,
+ struct fib **fib_pool,
+ unsigned int num)
+{
+ struct hw_fib **hw_fib_p;
+ struct fib **fib_p;
+
+ hw_fib_p = hw_fib_pool;
+ fib_p = fib_pool;
+ while (hw_fib_p < &hw_fib_pool[num]) {
+ *(hw_fib_p) = kmalloc(sizeof(struct hw_fib), GFP_KERNEL);
+ if (!(*(hw_fib_p++))) {
+ --hw_fib_p;
+ break;
+ }
+
+ *(fib_p) = kmalloc(sizeof(struct fib), GFP_KERNEL);
+ if (!(*(fib_p++))) {
+ kfree(*(--hw_fib_p));
+ break;
+ }
+ }
/*
+ * Get the actual number of allocated fibs
+ */
+ num = hw_fib_p - hw_fib_pool;
+ return num;
+}
+
+static void wakeup_fibctx_threads(struct aac_dev *dev,
+ struct hw_fib **hw_fib_pool,
+ struct fib **fib_pool,
+ struct fib *fib,
+ struct hw_fib *hw_fib,
+ unsigned int num)
+{
+ unsigned long flagv;
+ struct list_head *entry;
+ struct hw_fib **hw_fib_p;
+ struct fib **fib_p;
+ u32 time_now, time_last;
+ struct hw_fib *hw_newfib;
+ struct fib *newfib;
+ struct aac_fib_context *fibctx;
+
+ time_now = jiffies/HZ;
+ spin_lock_irqsave(&dev->fib_lock, flagv);
+ entry = dev->fib_list.next;
+ /*
* For each Context that is on the
* fibctxList, make a copy of the
* fib, and then set the event to wake up the
* thread that is waiting for it.
*/
- while (entry != &aac->fib_list) {
+
+ hw_fib_p = hw_fib_pool;
+ fib_p = fib_pool;
+ while (entry != &dev->fib_list) {
/*
* Extract the fibctx
*/
- struct aac_fib_context *fibctx = list_entry(entry, struct aac_fib_context, next);
- struct hw_fib * hw_fib;
- struct fib * fib;
+ fibctx = list_entry(entry, struct aac_fib_context,
+ next);
/*
* Check if the queue is getting
* backlogged
@@ -1496,10 +1968,10 @@ int aac_check_health(struct aac_dev * aac)
if (fibctx->count > 20) {
/*
* It's *not* jiffies folks,
- * but jiffies / HZ, so do not
+ * but jiffies / HZ so do not
* panic ...
*/
- u32 time_last = fibctx->jiffies;
+ time_last = fibctx->jiffies;
/*
* Has it been > 2 minutes
* since the last read off
@@ -1507,7 +1979,7 @@ int aac_check_health(struct aac_dev * aac)
*/
if ((time_now - time_last) > aif_timeout) {
entry = entry->next;
- aac_close_fib_context(aac, fibctx);
+ aac_close_fib_context(dev, fibctx);
continue;
}
}
@@ -1515,74 +1987,310 @@ int aac_check_health(struct aac_dev * aac)
* Warning: no sleep allowed while
* holding spinlock
*/
- hw_fib = kzalloc(sizeof(struct hw_fib), GFP_ATOMIC);
- fib = kzalloc(sizeof(struct fib), GFP_ATOMIC);
- if (fib && hw_fib) {
- struct aac_aifcmd * aif;
-
- fib->hw_fib_va = hw_fib;
- fib->dev = aac;
- aac_fib_init(fib);
- fib->type = FSAFS_NTC_FIB_CONTEXT;
- fib->size = sizeof (struct fib);
- fib->data = hw_fib->data;
- aif = (struct aac_aifcmd *)hw_fib->data;
- aif->command = cpu_to_le32(AifCmdEventNotify);
- aif->seqnum = cpu_to_le32(0xFFFFFFFF);
- ((__le32 *)aif->data)[0] = cpu_to_le32(AifEnExpEvent);
- ((__le32 *)aif->data)[1] = cpu_to_le32(AifExeFirmwarePanic);
- ((__le32 *)aif->data)[2] = cpu_to_le32(AifHighPriority);
- ((__le32 *)aif->data)[3] = cpu_to_le32(BlinkLED);
-
- /*
- * Put the FIB onto the
- * fibctx's fibs
- */
- list_add_tail(&fib->fiblink, &fibctx->fib_list);
- fibctx->count++;
- /*
- * Set the event to wake up the
- * thread that will waiting.
- */
- up(&fibctx->wait_sem);
- } else {
- printk(KERN_WARNING "aifd: didn't allocate NewFib.\n");
- kfree(fib);
- kfree(hw_fib);
+ if (hw_fib_p >= &hw_fib_pool[num]) {
+ pr_warn("aifd: didn't allocate NewFib\n");
+ entry = entry->next;
+ continue;
}
+
+ hw_newfib = *hw_fib_p;
+ *(hw_fib_p++) = NULL;
+ newfib = *fib_p;
+ *(fib_p++) = NULL;
+ /*
+ * Make the copy of the FIB
+ */
+ memcpy(hw_newfib, hw_fib, sizeof(struct hw_fib));
+ memcpy(newfib, fib, sizeof(struct fib));
+ newfib->hw_fib_va = hw_newfib;
+ /*
+ * Put the FIB onto the
+ * fibctx's fibs
+ */
+ list_add_tail(&newfib->fiblink, &fibctx->fib_list);
+ fibctx->count++;
+ /*
+ * Set the event to wake up the
+ * thread that is waiting.
+ */
+ complete(&fibctx->completion);
+
entry = entry->next;
}
+ /*
+ * Set the status of this FIB
+ */
+ *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK);
+ aac_fib_adapter_complete(fib, sizeof(u32));
+ spin_unlock_irqrestore(&dev->fib_lock, flagv);
- spin_unlock_irqrestore(&aac->fib_lock, flagv);
+}
- if (BlinkLED < 0) {
- printk(KERN_ERR "%s: Host adapter dead %d\n", aac->name, BlinkLED);
- goto out;
+static void aac_process_events(struct aac_dev *dev)
+{
+ struct hw_fib *hw_fib;
+ struct fib *fib;
+ unsigned long flags;
+ spinlock_t *t_lock;
+
+ t_lock = dev->queues->queue[HostNormCmdQueue].lock;
+ spin_lock_irqsave(t_lock, flags);
+
+ while (!list_empty(&(dev->queues->queue[HostNormCmdQueue].cmdq))) {
+ struct list_head *entry;
+ struct aac_aifcmd *aifcmd;
+ unsigned int num;
+ struct hw_fib **hw_fib_pool, **hw_fib_p;
+ struct fib **fib_pool, **fib_p;
+
+ set_current_state(TASK_RUNNING);
+
+ entry = dev->queues->queue[HostNormCmdQueue].cmdq.next;
+ list_del(entry);
+
+ t_lock = dev->queues->queue[HostNormCmdQueue].lock;
+ spin_unlock_irqrestore(t_lock, flags);
+
+ fib = list_entry(entry, struct fib, fiblink);
+ hw_fib = fib->hw_fib_va;
+ if (dev->sa_firmware) {
+ /* Thor AIF */
+ aac_handle_sa_aif(dev, fib);
+ aac_fib_adapter_complete(fib, (u16)sizeof(u32));
+ goto free_fib;
+ }
+ /*
+ * We will process the FIB here or pass it to a
+ * worker thread that is TBD. We Really can't
+ * do anything at this point since we don't have
+ * anything defined for this thread to do.
+ */
+ memset(fib, 0, sizeof(struct fib));
+ fib->type = FSAFS_NTC_FIB_CONTEXT;
+ fib->size = sizeof(struct fib);
+ fib->hw_fib_va = hw_fib;
+ fib->data = hw_fib->data;
+ fib->dev = dev;
+ /*
+ * We only handle AifRequest fibs from the adapter.
+ */
+
+ aifcmd = (struct aac_aifcmd *) hw_fib->data;
+ if (aifcmd->command == cpu_to_le32(AifCmdDriverNotify)) {
+ /* Handle Driver Notify Events */
+ aac_handle_aif(dev, fib);
+ *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK);
+ aac_fib_adapter_complete(fib, (u16)sizeof(u32));
+ goto free_fib;
+ }
+ /*
+ * The u32 here is important and intended. We are using
+ * 32bit wrapping time to fit the adapter field
+ */
+
+ /* Sniff events */
+ if (aifcmd->command == cpu_to_le32(AifCmdEventNotify)
+ || aifcmd->command == cpu_to_le32(AifCmdJobProgress)) {
+ aac_handle_aif(dev, fib);
+ }
+
+ /*
+ * get number of fibs to process
+ */
+ num = get_fib_count(dev);
+ if (!num)
+ goto free_fib;
+
+ hw_fib_pool = kmalloc_array(num, sizeof(struct hw_fib *),
+ GFP_KERNEL);
+ if (!hw_fib_pool)
+ goto free_fib;
+
+ fib_pool = kmalloc_array(num, sizeof(struct fib *), GFP_KERNEL);
+ if (!fib_pool)
+ goto free_hw_fib_pool;
+
+ /*
+ * Fill up fib pointer pools with actual fibs
+ * and hw_fibs
+ */
+ num = fillup_pools(dev, hw_fib_pool, fib_pool, num);
+ if (!num)
+ goto free_mem;
+
+ /*
+ * wakeup the thread that is waiting for
+ * the response from fw (ioctl)
+ */
+ wakeup_fibctx_threads(dev, hw_fib_pool, fib_pool,
+ fib, hw_fib, num);
+
+free_mem:
+ /* Free up the remaining resources */
+ hw_fib_p = hw_fib_pool;
+ fib_p = fib_pool;
+ while (hw_fib_p < &hw_fib_pool[num]) {
+ kfree(*hw_fib_p);
+ kfree(*fib_p);
+ ++fib_p;
+ ++hw_fib_p;
+ }
+ kfree(fib_pool);
+free_hw_fib_pool:
+ kfree(hw_fib_pool);
+free_fib:
+ kfree(fib);
+ t_lock = dev->queues->queue[HostNormCmdQueue].lock;
+ spin_lock_irqsave(t_lock, flags);
}
+ /*
+ * There are no more AIF's
+ */
+ t_lock = dev->queues->queue[HostNormCmdQueue].lock;
+ spin_unlock_irqrestore(t_lock, flags);
+}
- printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED);
+static int aac_send_wellness_command(struct aac_dev *dev, char *wellness_str,
+ u32 datasize)
+{
+ struct aac_srb *srbcmd;
+ struct sgmap64 *sg64;
+ dma_addr_t addr;
+ char *dma_buf;
+ struct fib *fibptr;
+ int ret = -ENOMEM;
+ u32 vbus, vid;
- if (!aac_check_reset || ((aac_check_reset == 1) &&
- (aac->supplement_adapter_info.SupportedOptions2 &
- AAC_OPTION_IGNORE_RESET)))
+ fibptr = aac_fib_alloc(dev);
+ if (!fibptr)
goto out;
- host = aac->scsi_host_ptr;
- if (aac->thread->pid != current->pid)
- spin_lock_irqsave(host->host_lock, flagv);
- BlinkLED = _aac_reset_adapter(aac, aac_check_reset != 1);
- if (aac->thread->pid != current->pid)
- spin_unlock_irqrestore(host->host_lock, flagv);
- return BlinkLED;
+
+ dma_buf = dma_alloc_coherent(&dev->pdev->dev, datasize, &addr,
+ GFP_KERNEL);
+ if (!dma_buf)
+ goto fib_free_out;
+
+ aac_fib_init(fibptr);
+
+ vbus = (u32)le16_to_cpu(dev->supplement_adapter_info.virt_device_bus);
+ vid = (u32)le16_to_cpu(dev->supplement_adapter_info.virt_device_target);
+
+ srbcmd = (struct aac_srb *)fib_data(fibptr);
+
+ srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi);
+ srbcmd->channel = cpu_to_le32(vbus);
+ srbcmd->id = cpu_to_le32(vid);
+ srbcmd->lun = 0;
+ srbcmd->flags = cpu_to_le32(SRB_DataOut);
+ srbcmd->timeout = cpu_to_le32(10);
+ srbcmd->retry_limit = 0;
+ srbcmd->cdb_size = cpu_to_le32(12);
+ srbcmd->count = cpu_to_le32(datasize);
+
+ memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
+ srbcmd->cdb[0] = BMIC_OUT;
+ srbcmd->cdb[6] = WRITE_HOST_WELLNESS;
+ memcpy(dma_buf, (char *)wellness_str, datasize);
+
+ sg64 = (struct sgmap64 *)&srbcmd->sg;
+ sg64->count = cpu_to_le32(1);
+ sg64->sg[0].addr[1] = cpu_to_le32((u32)(((addr) >> 16) >> 16));
+ sg64->sg[0].addr[0] = cpu_to_le32((u32)(addr & 0xffffffff));
+ sg64->sg[0].count = cpu_to_le32(datasize);
+
+ ret = aac_fib_send(ScsiPortCommand64, fibptr,
+ sizeof(struct aac_srb) + sizeof(struct sgentry),
+ FsaNormal, 1, 1, NULL, NULL);
+
+ dma_free_coherent(&dev->pdev->dev, datasize, dma_buf, addr);
+
+ /*
+ * Do not set XferState to zero unless
+ * receives a response from F/W
+ */
+ if (ret >= 0)
+ aac_fib_complete(fibptr);
+
+ /*
+ * FIB should be freed only after
+ * getting the response from the F/W
+ */
+ if (ret != -ERESTARTSYS)
+ goto fib_free_out;
out:
- aac->in_reset = 0;
- return BlinkLED;
+ return ret;
+fib_free_out:
+ aac_fib_free(fibptr);
+ goto out;
}
+static int aac_send_safw_hostttime(struct aac_dev *dev, struct timespec64 *now)
+{
+ struct tm cur_tm;
+ char wellness_str[] = "<HW>TD\010\0\0\0\0\0\0\0\0\0DW\0\0ZZ";
+ u32 datasize = sizeof(wellness_str);
+ time64_t local_time;
+ int ret = -ENODEV;
+
+ if (!dev->sa_firmware)
+ goto out;
+
+ local_time = (now->tv_sec - (sys_tz.tz_minuteswest * 60));
+ time64_to_tm(local_time, 0, &cur_tm);
+ cur_tm.tm_mon += 1;
+ cur_tm.tm_year += 1900;
+ wellness_str[8] = bin2bcd(cur_tm.tm_hour);
+ wellness_str[9] = bin2bcd(cur_tm.tm_min);
+ wellness_str[10] = bin2bcd(cur_tm.tm_sec);
+ wellness_str[12] = bin2bcd(cur_tm.tm_mon);
+ wellness_str[13] = bin2bcd(cur_tm.tm_mday);
+ wellness_str[14] = bin2bcd(cur_tm.tm_year / 100);
+ wellness_str[15] = bin2bcd(cur_tm.tm_year % 100);
+
+ ret = aac_send_wellness_command(dev, wellness_str, datasize);
+
+out:
+ return ret;
+}
+
+static int aac_send_hosttime(struct aac_dev *dev, struct timespec64 *now)
+{
+ int ret = -ENOMEM;
+ struct fib *fibptr;
+ __le32 *info;
+
+ fibptr = aac_fib_alloc(dev);
+ if (!fibptr)
+ goto out;
+
+ aac_fib_init(fibptr);
+ info = (__le32 *)fib_data(fibptr);
+ *info = cpu_to_le32(now->tv_sec); /* overflow in y2106 */
+ ret = aac_fib_send(SendHostTime, fibptr, sizeof(*info), FsaNormal,
+ 1, 1, NULL, NULL);
+
+ /*
+ * Do not set XferState to zero unless
+ * receives a response from F/W
+ */
+ if (ret >= 0)
+ aac_fib_complete(fibptr);
+
+ /*
+ * FIB should be freed only after
+ * getting the response from the F/W
+ */
+ if (ret != -ERESTARTSYS)
+ aac_fib_free(fibptr);
+
+out:
+ return ret;
+}
/**
* aac_command_thread - command processing thread
- * @dev: Adapter to monitor
+ * @data: Adapter to monitor
*
* Waits on the commandready event in it's queue. When the event gets set
* it will pull FIBs off it's queue. It will continue to pull FIBs off
@@ -1593,10 +2301,6 @@ out:
int aac_command_thread(void *data)
{
struct aac_dev *dev = data;
- struct hw_fib *hw_fib, *hw_newfib;
- struct fib *fib, *newfib;
- struct aac_fib_context *fibctx;
- unsigned long flags;
DECLARE_WAITQUEUE(wait, current);
unsigned long next_jiffies = jiffies + HZ;
unsigned long next_check_jiffies = next_jiffies;
@@ -1616,196 +2320,8 @@ int aac_command_thread(void *data)
set_current_state(TASK_INTERRUPTIBLE);
dprintk ((KERN_INFO "aac_command_thread start\n"));
while (1) {
- spin_lock_irqsave(dev->queues->queue[HostNormCmdQueue].lock, flags);
- while(!list_empty(&(dev->queues->queue[HostNormCmdQueue].cmdq))) {
- struct list_head *entry;
- struct aac_aifcmd * aifcmd;
-
- set_current_state(TASK_RUNNING);
- entry = dev->queues->queue[HostNormCmdQueue].cmdq.next;
- list_del(entry);
-
- spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags);
- fib = list_entry(entry, struct fib, fiblink);
- /*
- * We will process the FIB here or pass it to a
- * worker thread that is TBD. We Really can't
- * do anything at this point since we don't have
- * anything defined for this thread to do.
- */
- hw_fib = fib->hw_fib_va;
- memset(fib, 0, sizeof(struct fib));
- fib->type = FSAFS_NTC_FIB_CONTEXT;
- fib->size = sizeof(struct fib);
- fib->hw_fib_va = hw_fib;
- fib->data = hw_fib->data;
- fib->dev = dev;
- /*
- * We only handle AifRequest fibs from the adapter.
- */
- aifcmd = (struct aac_aifcmd *) hw_fib->data;
- if (aifcmd->command == cpu_to_le32(AifCmdDriverNotify)) {
- /* Handle Driver Notify Events */
- aac_handle_aif(dev, fib);
- *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK);
- aac_fib_adapter_complete(fib, (u16)sizeof(u32));
- } else {
- /* The u32 here is important and intended. We are using
- 32bit wrapping time to fit the adapter field */
-
- u32 time_now, time_last;
- unsigned long flagv;
- unsigned num;
- struct hw_fib ** hw_fib_pool, ** hw_fib_p;
- struct fib ** fib_pool, ** fib_p;
-
- /* Sniff events */
- if ((aifcmd->command ==
- cpu_to_le32(AifCmdEventNotify)) ||
- (aifcmd->command ==
- cpu_to_le32(AifCmdJobProgress))) {
- aac_handle_aif(dev, fib);
- }
-
- time_now = jiffies/HZ;
-
- /*
- * Warning: no sleep allowed while
- * holding spinlock. We take the estimate
- * and pre-allocate a set of fibs outside the
- * lock.
- */
- num = le32_to_cpu(dev->init->AdapterFibsSize)
- / sizeof(struct hw_fib); /* some extra */
- spin_lock_irqsave(&dev->fib_lock, flagv);
- entry = dev->fib_list.next;
- while (entry != &dev->fib_list) {
- entry = entry->next;
- ++num;
- }
- spin_unlock_irqrestore(&dev->fib_lock, flagv);
- hw_fib_pool = NULL;
- fib_pool = NULL;
- if (num
- && ((hw_fib_pool = kmalloc(sizeof(struct hw_fib *) * num, GFP_KERNEL)))
- && ((fib_pool = kmalloc(sizeof(struct fib *) * num, GFP_KERNEL)))) {
- hw_fib_p = hw_fib_pool;
- fib_p = fib_pool;
- while (hw_fib_p < &hw_fib_pool[num]) {
- if (!(*(hw_fib_p++) = kmalloc(sizeof(struct hw_fib), GFP_KERNEL))) {
- --hw_fib_p;
- break;
- }
- if (!(*(fib_p++) = kmalloc(sizeof(struct fib), GFP_KERNEL))) {
- kfree(*(--hw_fib_p));
- break;
- }
- }
- if ((num = hw_fib_p - hw_fib_pool) == 0) {
- kfree(fib_pool);
- fib_pool = NULL;
- kfree(hw_fib_pool);
- hw_fib_pool = NULL;
- }
- } else {
- kfree(hw_fib_pool);
- hw_fib_pool = NULL;
- }
- spin_lock_irqsave(&dev->fib_lock, flagv);
- entry = dev->fib_list.next;
- /*
- * For each Context that is on the
- * fibctxList, make a copy of the
- * fib, and then set the event to wake up the
- * thread that is waiting for it.
- */
- hw_fib_p = hw_fib_pool;
- fib_p = fib_pool;
- while (entry != &dev->fib_list) {
- /*
- * Extract the fibctx
- */
- fibctx = list_entry(entry, struct aac_fib_context, next);
- /*
- * Check if the queue is getting
- * backlogged
- */
- if (fibctx->count > 20)
- {
- /*
- * It's *not* jiffies folks,
- * but jiffies / HZ so do not
- * panic ...
- */
- time_last = fibctx->jiffies;
- /*
- * Has it been > 2 minutes
- * since the last read off
- * the queue?
- */
- if ((time_now - time_last) > aif_timeout) {
- entry = entry->next;
- aac_close_fib_context(dev, fibctx);
- continue;
- }
- }
- /*
- * Warning: no sleep allowed while
- * holding spinlock
- */
- if (hw_fib_p < &hw_fib_pool[num]) {
- hw_newfib = *hw_fib_p;
- *(hw_fib_p++) = NULL;
- newfib = *fib_p;
- *(fib_p++) = NULL;
- /*
- * Make the copy of the FIB
- */
- memcpy(hw_newfib, hw_fib, sizeof(struct hw_fib));
- memcpy(newfib, fib, sizeof(struct fib));
- newfib->hw_fib_va = hw_newfib;
- /*
- * Put the FIB onto the
- * fibctx's fibs
- */
- list_add_tail(&newfib->fiblink, &fibctx->fib_list);
- fibctx->count++;
- /*
- * Set the event to wake up the
- * thread that is waiting.
- */
- up(&fibctx->wait_sem);
- } else {
- printk(KERN_WARNING "aifd: didn't allocate NewFib.\n");
- }
- entry = entry->next;
- }
- /*
- * Set the status of this FIB
- */
- *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK);
- aac_fib_adapter_complete(fib, sizeof(u32));
- spin_unlock_irqrestore(&dev->fib_lock, flagv);
- /* Free up the remaining resources */
- hw_fib_p = hw_fib_pool;
- fib_p = fib_pool;
- while (hw_fib_p < &hw_fib_pool[num]) {
- kfree(*hw_fib_p);
- kfree(*fib_p);
- ++fib_p;
- ++hw_fib_p;
- }
- kfree(hw_fib_pool);
- kfree(fib_pool);
- }
- kfree(fib);
- spin_lock_irqsave(dev->queues->queue[HostNormCmdQueue].lock, flags);
- }
- /*
- * There are no more AIF's
- */
- spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags);
+ aac_process_events(dev);
/*
* Background activity
@@ -1813,7 +2329,7 @@ int aac_command_thread(void *data)
if ((time_before(next_check_jiffies,next_jiffies))
&& ((difference = next_check_jiffies - jiffies) <= 0)) {
next_check_jiffies = next_jiffies;
- if (aac_check_health(dev) == 0) {
+ if (aac_adapter_check_health(dev) == 0) {
difference = ((long)(unsigned)check_interval)
* HZ;
next_check_jiffies = jiffies + difference;
@@ -1822,58 +2338,34 @@ int aac_command_thread(void *data)
}
if (!time_before(next_check_jiffies,next_jiffies)
&& ((difference = next_jiffies - jiffies) <= 0)) {
- struct timeval now;
+ struct timespec64 now;
int ret;
/* Don't even try to talk to adapter if its sick */
- ret = aac_check_health(dev);
- if (!ret && !dev->queues)
+ ret = aac_adapter_check_health(dev);
+ if (ret || !dev->queues)
break;
next_check_jiffies = jiffies
+ ((long)(unsigned)check_interval)
* HZ;
- do_gettimeofday(&now);
+ ktime_get_real_ts64(&now);
/* Synchronize our watches */
- if (((1000000 - (1000000 / HZ)) > now.tv_usec)
- && (now.tv_usec > (1000000 / HZ)))
- difference = (((1000000 - now.tv_usec) * HZ)
- + 500000) / 1000000;
- else if (ret == 0) {
- struct fib *fibptr;
-
- if ((fibptr = aac_fib_alloc(dev))) {
- int status;
- __le32 *info;
-
- aac_fib_init(fibptr);
-
- info = (__le32 *) fib_data(fibptr);
- if (now.tv_usec > 500000)
- ++now.tv_sec;
-
- *info = cpu_to_le32(now.tv_sec);
-
- status = aac_fib_send(SendHostTime,
- fibptr,
- sizeof(*info),
- FsaNormal,
- 1, 1,
- NULL,
- NULL);
- /* Do not set XferState to zero unless
- * receives a response from F/W */
- if (status >= 0)
- aac_fib_complete(fibptr);
- /* FIB should be freed only after
- * getting the response from the F/W */
- if (status != -ERESTARTSYS)
- aac_fib_free(fibptr);
- }
+ if (((NSEC_PER_SEC - (NSEC_PER_SEC / HZ)) > now.tv_nsec)
+ && (now.tv_nsec > (NSEC_PER_SEC / HZ)))
+ difference = HZ + HZ / 2 -
+ now.tv_nsec / (NSEC_PER_SEC / HZ);
+ else {
+ if (now.tv_nsec > NSEC_PER_SEC / 2)
+ ++now.tv_sec;
+
+ if (dev->sa_firmware)
+ ret =
+ aac_send_safw_hostttime(dev, &now);
+ else
+ ret = aac_send_hosttime(dev, &now);
+
difference = (long)(unsigned)update_interval*HZ;
- } else {
- /* retry shortly */
- difference = 10 * HZ;
}
next_jiffies = jiffies + difference;
if (time_before(next_check_jiffies,next_jiffies))
@@ -1882,6 +2374,14 @@ int aac_command_thread(void *data)
if (difference <= 0)
difference = 1;
set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ break;
+
+ /*
+ * we probably want usleep_range() here instead of the
+ * jiffies computation
+ */
schedule_timeout(difference);
if (kthread_should_stop())
@@ -1892,3 +2392,63 @@ int aac_command_thread(void *data)
dev->aif_thread = 0;
return 0;
}
+
+int aac_acquire_irq(struct aac_dev *dev)
+{
+ int i;
+ int j;
+ int ret = 0;
+
+ if (!dev->sync_mode && dev->msi_enabled && dev->max_msix > 1) {
+ for (i = 0; i < dev->max_msix; i++) {
+ dev->aac_msix[i].vector_no = i;
+ dev->aac_msix[i].dev = dev;
+ if (request_irq(pci_irq_vector(dev->pdev, i),
+ dev->a_ops.adapter_intr,
+ 0, "aacraid", &(dev->aac_msix[i]))) {
+ printk(KERN_ERR "%s%d: Failed to register IRQ for vector %d.\n",
+ dev->name, dev->id, i);
+ for (j = 0 ; j < i ; j++)
+ free_irq(pci_irq_vector(dev->pdev, j),
+ &(dev->aac_msix[j]));
+ pci_disable_msix(dev->pdev);
+ ret = -1;
+ }
+ }
+ } else {
+ dev->aac_msix[0].vector_no = 0;
+ dev->aac_msix[0].dev = dev;
+
+ if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
+ IRQF_SHARED, "aacraid",
+ &(dev->aac_msix[0])) < 0) {
+ if (dev->msi)
+ pci_disable_msi(dev->pdev);
+ printk(KERN_ERR "%s%d: Interrupt unavailable.\n",
+ dev->name, dev->id);
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+void aac_free_irq(struct aac_dev *dev)
+{
+ int i;
+
+ if (aac_is_src(dev)) {
+ if (dev->max_msix > 1) {
+ for (i = 0; i < dev->max_msix; i++)
+ free_irq(pci_irq_vector(dev->pdev, i),
+ &(dev->aac_msix[i]));
+ } else {
+ free_irq(dev->pdev->irq, &(dev->aac_msix[0]));
+ }
+ } else {
+ free_irq(dev->pdev->irq, dev);
+ }
+ if (dev->msi)
+ pci_disable_msi(dev->pdev);
+ else if (dev->max_msix > 1)
+ pci_disable_msix(dev->pdev);
+}
diff --git a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c
index f0c66a80ad13..fbe334c59f37 100644
--- a/drivers/scsi/aacraid/dpcsup.c
+++ b/drivers/scsi/aacraid/dpcsup.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,28 +7,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* dpcsup.c
*
* Abstract: All DPC processing routines for the cyclone board occur here.
- *
- *
*/
#include <linux/kernel.h>
@@ -37,7 +23,6 @@
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/blkdev.h>
-#include <linux/semaphore.h>
#include "aacraid.h"
@@ -64,7 +49,7 @@ unsigned int aac_response_normal(struct aac_queue * q)
/*
* Keep pulling response QEs off the response queue and waking
* up the waiters until there are no more QEs. We then return
- * back to the system. If no response was requesed we just
+ * back to the system. If no response was requested we just
* deallocate the Fib here and continue.
*/
while(aac_consumer_get(dev, q, &entry))
@@ -84,7 +69,7 @@ unsigned int aac_response_normal(struct aac_queue * q)
* continue. The caller has already been notified that
* the fib timed out.
*/
- dev->queues->queue[AdapNormCmdQueue].numpending--;
+ atomic_dec(&dev->queues->queue[AdapNormCmdQueue].numpending);
if (unlikely(fib->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) {
spin_unlock_irqrestore(q->lock, flags);
@@ -101,6 +86,7 @@ unsigned int aac_response_normal(struct aac_queue * q)
*/
*(__le32 *)hwfib->data = cpu_to_le32(ST_OK);
hwfib->header.XferState |= cpu_to_le32(AdapterProcessed);
+ fib->flags |= FIB_CONTEXT_FLAG_FASTRESP;
}
FIB_COUNTER_INCREMENT(aac_config.FibRecved);
@@ -113,22 +99,22 @@ unsigned int aac_response_normal(struct aac_queue * q)
}
if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected | Async))
{
- if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected))
+ if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected)) {
FIB_COUNTER_INCREMENT(aac_config.NoResponseRecved);
- else
+ } else {
FIB_COUNTER_INCREMENT(aac_config.AsyncRecved);
+ }
/*
* NOTE: we cannot touch the fib after this
* call, because it may have been deallocated.
*/
- fib->flags = 0;
fib->callback(fib->callback_data, fib);
} else {
unsigned long flagv;
spin_lock_irqsave(&fib->event_lock, flagv);
if (!fib->done) {
fib->done = 1;
- up(&fib->event_wait);
+ complete(&fib->event_wait);
}
spin_unlock_irqrestore(&fib->event_lock, flagv);
@@ -244,14 +230,14 @@ static void aac_aif_callback(void *context, struct fib * fibptr)
struct fib *fibctx;
struct aac_dev *dev;
struct aac_aifcmd *cmd;
- int status;
fibctx = (struct fib *)context;
BUG_ON(fibptr == NULL);
dev = fibptr->dev;
- if (fibptr->hw_fib_va->header.XferState &
- cpu_to_le32(NoMoreAifDataAvailable)) {
+ if ((fibptr->hw_fib_va->header.XferState &
+ cpu_to_le32(NoMoreAifDataAvailable)) ||
+ dev->sa_firmware) {
aac_fib_complete(fibptr);
aac_fib_free(fibptr);
return;
@@ -263,7 +249,7 @@ static void aac_aif_callback(void *context, struct fib * fibptr)
cmd = (struct aac_aifcmd *) fib_data(fibctx);
cmd->command = cpu_to_le32(AifReqEvent);
- status = aac_fib_send(AifRequest,
+ aac_fib_send(AifRequest,
fibctx,
sizeof(struct hw_fib)-sizeof(struct aac_fibhdr),
FsaNormal,
@@ -272,7 +258,7 @@ static void aac_aif_callback(void *context, struct fib * fibptr)
}
-/**
+/*
* aac_intr_normal - Handle command replies
* @dev: Device
* @index: completion reference
@@ -281,8 +267,8 @@ static void aac_aif_callback(void *context, struct fib * fibptr)
* know there is a response on our normal priority queue. We will pull off
* all QE there are and wake up all the waiters before exiting.
*/
-unsigned int aac_intr_normal(struct aac_dev *dev, u32 index,
- int isAif, int isFastResponse, struct hw_fib *aif_fib)
+unsigned int aac_intr_normal(struct aac_dev *dev, u32 index, int isAif,
+ int isFastResponse, struct hw_fib *aif_fib)
{
unsigned long mflags;
dprintk((KERN_INFO "aac_intr_normal(%p,%x)\n", dev, index));
@@ -304,12 +290,14 @@ unsigned int aac_intr_normal(struct aac_dev *dev, u32 index,
kfree (fib);
return 1;
}
- if (aif_fib != NULL) {
+ if (dev->sa_firmware) {
+ fib->hbacmd_size = index; /* store event type */
+ } else if (aif_fib != NULL) {
memcpy(hw_fib, aif_fib, sizeof(struct hw_fib));
} else {
- memcpy(hw_fib,
- (struct hw_fib *)(((uintptr_t)(dev->regs.sa)) +
- index), sizeof(struct hw_fib));
+ memcpy(hw_fib, (struct hw_fib *)
+ (((uintptr_t)(dev->regs.sa)) + index),
+ sizeof(struct hw_fib));
}
INIT_LIST_HEAD(&fib->fiblink);
fib->type = FSAFS_NTC_FIB_CONTEXT;
@@ -343,7 +331,7 @@ unsigned int aac_intr_normal(struct aac_dev *dev, u32 index,
(fib_callback)aac_aif_callback, fibctx);
} else {
struct fib *fib = &dev->fibs[index];
- struct hw_fib * hwfib = fib->hw_fib_va;
+ int start_callback = 0;
/*
* Remove this fib from the Outstanding I/O queue.
@@ -353,7 +341,7 @@ unsigned int aac_intr_normal(struct aac_dev *dev, u32 index,
* continue. The caller has already been notified that
* the fib timed out.
*/
- dev->queues->queue[AdapNormCmdQueue].numpending--;
+ atomic_dec(&dev->queues->queue[AdapNormCmdQueue].numpending);
if (unlikely(fib->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) {
aac_fib_complete(fib);
@@ -361,53 +349,103 @@ unsigned int aac_intr_normal(struct aac_dev *dev, u32 index,
return 0;
}
- if (isFastResponse) {
- /*
- * Doctor the fib
- */
- *(__le32 *)hwfib->data = cpu_to_le32(ST_OK);
- hwfib->header.XferState |= cpu_to_le32(AdapterProcessed);
- }
-
FIB_COUNTER_INCREMENT(aac_config.FibRecved);
- if (hwfib->header.Command == cpu_to_le16(NuFileSystem))
- {
- __le32 *pstatus = (__le32 *)hwfib->data;
- if (*pstatus & cpu_to_le32(0xffff0000))
- *pstatus = cpu_to_le32(ST_OK);
- }
- if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected | Async))
- {
- if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected))
- FIB_COUNTER_INCREMENT(aac_config.NoResponseRecved);
- else
- FIB_COUNTER_INCREMENT(aac_config.AsyncRecved);
- /*
- * NOTE: we cannot touch the fib after this
- * call, because it may have been deallocated.
- */
- fib->flags = 0;
- fib->callback(fib->callback_data, fib);
+ if (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) {
+
+ if (isFastResponse)
+ fib->flags |= FIB_CONTEXT_FLAG_FASTRESP;
+
+ if (fib->callback) {
+ start_callback = 1;
+ } else {
+ unsigned long flagv;
+ int completed = 0;
+
+ dprintk((KERN_INFO "event_wait up\n"));
+ spin_lock_irqsave(&fib->event_lock, flagv);
+ if (fib->done == 2) {
+ fib->done = 1;
+ completed = 1;
+ } else {
+ fib->done = 1;
+ complete(&fib->event_wait);
+ }
+ spin_unlock_irqrestore(&fib->event_lock, flagv);
+
+ spin_lock_irqsave(&dev->manage_lock, mflags);
+ dev->management_fib_count--;
+ spin_unlock_irqrestore(&dev->manage_lock,
+ mflags);
+
+ FIB_COUNTER_INCREMENT(aac_config.NativeRecved);
+ if (completed)
+ aac_fib_complete(fib);
+ }
} else {
- unsigned long flagv;
- dprintk((KERN_INFO "event_wait up\n"));
- spin_lock_irqsave(&fib->event_lock, flagv);
- if (!fib->done) {
- fib->done = 1;
- up(&fib->event_wait);
+ struct hw_fib *hwfib = fib->hw_fib_va;
+
+ if (isFastResponse) {
+ /* Doctor the fib */
+ *(__le32 *)hwfib->data = cpu_to_le32(ST_OK);
+ hwfib->header.XferState |=
+ cpu_to_le32(AdapterProcessed);
+ fib->flags |= FIB_CONTEXT_FLAG_FASTRESP;
}
- spin_unlock_irqrestore(&fib->event_lock, flagv);
- spin_lock_irqsave(&dev->manage_lock, mflags);
- dev->management_fib_count--;
- spin_unlock_irqrestore(&dev->manage_lock, mflags);
+ if (hwfib->header.Command ==
+ cpu_to_le16(NuFileSystem)) {
+ __le32 *pstatus = (__le32 *)hwfib->data;
- FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
- if (fib->done == 2) {
+ if (*pstatus & cpu_to_le32(0xffff0000))
+ *pstatus = cpu_to_le32(ST_OK);
+ }
+ if (hwfib->header.XferState &
+ cpu_to_le32(NoResponseExpected | Async)) {
+ if (hwfib->header.XferState & cpu_to_le32(
+ NoResponseExpected)) {
+ FIB_COUNTER_INCREMENT(
+ aac_config.NoResponseRecved);
+ } else {
+ FIB_COUNTER_INCREMENT(
+ aac_config.AsyncRecved);
+ }
+ start_callback = 1;
+ } else {
+ unsigned long flagv;
+ int completed = 0;
+
+ dprintk((KERN_INFO "event_wait up\n"));
spin_lock_irqsave(&fib->event_lock, flagv);
- fib->done = 0;
+ if (fib->done == 2) {
+ fib->done = 1;
+ completed = 1;
+ } else {
+ fib->done = 1;
+ complete(&fib->event_wait);
+ }
spin_unlock_irqrestore(&fib->event_lock, flagv);
+
+ spin_lock_irqsave(&dev->manage_lock, mflags);
+ dev->management_fib_count--;
+ spin_unlock_irqrestore(&dev->manage_lock,
+ mflags);
+
+ FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
+ if (completed)
+ aac_fib_complete(fib);
+ }
+ }
+
+
+ if (start_callback) {
+ /*
+ * NOTE: we cannot touch the fib after this
+ * call, because it may have been deallocated.
+ */
+ if (likely(fib->callback && fib->callback_data)) {
+ fib->callback(fib->callback_data, fib);
+ } else {
aac_fib_complete(fib);
aac_fib_free(fib);
}
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 3382475dc22d..82c6e7c7cdaf 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,21 +7,8 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* linit.c
@@ -44,6 +32,7 @@
#include <linux/syscalls.h>
#include <linux/delay.h>
#include <linux/kthread.h>
+#include <linux/msdos_partition.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -55,7 +44,7 @@
#include "aacraid.h"
-#define AAC_DRIVER_VERSION "1.1-7"
+#define AAC_DRIVER_VERSION "1.2.1"
#ifndef AAC_DRIVER_BRANCH
#define AAC_DRIVER_BRANCH ""
#endif
@@ -78,7 +67,7 @@ MODULE_VERSION(AAC_DRIVER_FULL_VERSION);
static DEFINE_MUTEX(aac_mutex);
static LIST_HEAD(aac_devices);
-static int aac_cfg_major = -1;
+static int aac_cfg_major = AAC_CHARDEV_UNREGISTERED;
char aac_driver_version[] = AAC_DRIVER_FULL_VERSION;
/*
@@ -87,13 +76,7 @@ char aac_driver_version[] = AAC_DRIVER_FULL_VERSION;
*
* Note: The last field is used to index into aac_drivers below.
*/
-#ifdef DECLARE_PCI_DEVICE_TABLE
-static DECLARE_PCI_DEVICE_TABLE(aac_pci_tbl) = {
-#elif defined(__devinitconst)
-static const struct pci_device_id aac_pci_tbl[] __devinitconst = {
-#else
-static const struct pci_device_id aac_pci_tbl[] __devinitdata = {
-#endif
+static const struct pci_device_id aac_pci_tbl[] = {
{ 0x1028, 0x0001, 0x1028, 0x0001, 0, 0, 0 }, /* PERC 2/Si (Iguana/PERC2Si) */
{ 0x1028, 0x0002, 0x1028, 0x0002, 0, 0, 1 }, /* PERC 3/Di (Opal/PERC3Di) */
{ 0x1028, 0x0003, 0x1028, 0x0003, 0, 0, 2 }, /* PERC 3/Si (SlimFast/PERC3Si */
@@ -161,7 +144,9 @@ static const struct pci_device_id aac_pci_tbl[] __devinitdata = {
{ 0x9005, 0x0285, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 59 }, /* Adaptec Catch All */
{ 0x9005, 0x0286, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 60 }, /* Adaptec Rocket Catch All */
{ 0x9005, 0x0288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 61 }, /* Adaptec NEMER/ARK Catch All */
- { 0x9005, 0x028b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 62 }, /* Adaptec PMC Catch All */
+ { 0x9005, 0x028b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 62 }, /* Adaptec PMC Series 6 (Tupelo) */
+ { 0x9005, 0x028c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 63 }, /* Adaptec PMC Series 7 (Denali) */
+ { 0x9005, 0x028d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 64 }, /* Adaptec PMC Series 8 */
{ 0,}
};
MODULE_DEVICE_TABLE(pci, aac_pci_tbl);
@@ -237,39 +222,28 @@ static struct aac_driver_ident aac_drivers[] = {
{ aac_rx_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec Catch All */
{ aac_rkt_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec Rocket Catch All */
{ aac_nark_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec NEMER/ARK Catch All */
- { aac_src_init, "aacraid", "ADAPTEC ", "RAID ", 2 } /* Adaptec PMC Catch All */
+ { aac_src_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 6 (Tupelo) */
+ { aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 7 (Denali) */
+ { aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_SRC }, /* Adaptec PMC Series 8 */
};
/**
* aac_queuecommand - queue a SCSI command
+ * @shost: Scsi host to queue command on
* @cmd: SCSI command to queue
- * @done: Function to call on command completion
*
* Queues a command for execution by the associated Host Adapter.
*
* TODO: unify with aac_scsi_cmd().
*/
-static int aac_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+static int aac_queuecommand(struct Scsi_Host *shost,
+ struct scsi_cmnd *cmd)
{
- struct Scsi_Host *host = cmd->device->host;
- struct aac_dev *dev = (struct aac_dev *)host->hostdata;
- u32 count = 0;
- cmd->scsi_done = done;
- for (; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
- struct fib * fib = &dev->fibs[count];
- struct scsi_cmnd * command;
- if (fib->hw_fib_va->header.XferState &&
- ((command = fib->callback_data)) &&
- (command == cmd) &&
- (cmd->SCp.phase == AAC_OWNER_FIRMWARE))
- return 0; /* Already owned by Adapter */
- }
- cmd->SCp.phase = AAC_OWNER_LOWLEVEL;
- return (aac_scsi_cmd(cmd) ? FAILED : 0);
-}
+ aac_priv(cmd)->owner = AAC_OWNER_LOWLEVEL;
-static DEF_SCSI_QCMD(aac_queuecommand)
+ return aac_scsi_cmd(cmd) ? SCSI_MLQUEUE_HOST_BUSY : 0;
+}
/**
* aac_info - Returns the host adapter name
@@ -299,7 +273,7 @@ struct aac_driver_ident* aac_get_driver_ident(int devtype)
/**
* aac_biosparm - return BIOS parameters for disk
* @sdev: The scsi device corresponding to the disk
- * @bdev: the block device corresponding to the disk
+ * @disk: the gendisk corresponding to the disk
* @capacity: the sector capacity of the disk
* @geom: geometry block to fill in
*
@@ -318,7 +292,7 @@ struct aac_driver_ident* aac_get_driver_ident(int devtype)
* be displayed.
*/
-static int aac_biosparm(struct scsi_device *sdev, struct block_device *bdev,
+static int aac_biosparm(struct scsi_device *sdev, struct gendisk *disk,
sector_t capacity, int *geom)
{
struct diskparm *param = (struct diskparm *)geom;
@@ -350,12 +324,12 @@ static int aac_biosparm(struct scsi_device *sdev, struct block_device *bdev,
* entry whose end_head matches one of the standard geometry
* translations ( 64/32, 128/32, 255/63 ).
*/
- buf = scsi_bios_ptable(bdev);
+ buf = scsi_bios_ptable(disk);
if (!buf)
return 0;
- if(*(__le16 *)(buf + 0x40) == cpu_to_le16(0xaa55)) {
- struct partition *first = (struct partition * )buf;
- struct partition *entry = first;
+ if (*(__le16 *)(buf + 0x40) == cpu_to_le16(MSDOS_LABEL_MAGIC)) {
+ struct msdos_partition *first = (struct msdos_partition *)buf;
+ struct msdos_partition *entry = first;
int saved_cylinders = param->cylinders;
int num;
unsigned char end_head, end_sec;
@@ -387,9 +361,10 @@ static int aac_biosparm(struct scsi_device *sdev, struct block_device *bdev,
param->cylinders = cap_to_cyls(capacity, param->heads * param->sectors);
if (num < 4 && end_sec == param->sectors) {
- if (param->cylinders != saved_cylinders)
+ if (param->cylinders != saved_cylinders) {
dprintk((KERN_DEBUG "Adopting geometry: heads=%d, sectors=%d from partition table %d.\n",
param->heads, param->sectors, num));
+ }
} else if (end_head > 0 || end_sec > 0) {
dprintk((KERN_DEBUG "Strange geometry: heads=%d, sectors=%d in partition table %d.\n",
end_head + 1, end_sec, num));
@@ -402,68 +377,131 @@ static int aac_biosparm(struct scsi_device *sdev, struct block_device *bdev,
}
/**
- * aac_slave_configure - compute queue depths
+ * aac_sdev_configure - compute queue depths
* @sdev: SCSI device we are considering
+ * @lim: Request queue limits
*
* Selects queue depths for each target device based on the host adapter's
* total capacity and the queue depth supported by the target device.
* A queue depth of one automatically disables tagged queueing.
*/
-static int aac_slave_configure(struct scsi_device *sdev)
+static int aac_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct aac_dev *aac = (struct aac_dev *)sdev->host->hostdata;
+ int chn, tid;
+ unsigned int depth = 0;
+ unsigned int set_timeout = 0;
+ int timeout = 0;
+ bool set_qd_dev_type = false;
+ u8 devtype = 0;
+
+ chn = aac_logical_to_phys(sdev_channel(sdev));
+ tid = sdev_id(sdev);
+ if (chn < AAC_MAX_BUSES && tid < AAC_MAX_TARGETS && aac->sa_firmware) {
+ devtype = aac->hba_map[chn][tid].devtype;
+
+ if (devtype == AAC_DEVTYPE_NATIVE_RAW) {
+ depth = aac->hba_map[chn][tid].qd_limit;
+ set_timeout = 1;
+ goto common_config;
+ }
+ if (devtype == AAC_DEVTYPE_ARC_RAW) {
+ set_qd_dev_type = true;
+ set_timeout = 1;
+ goto common_config;
+ }
+ }
+
if (aac->jbod && (sdev->type == TYPE_DISK))
sdev->removable = 1;
- if ((sdev->type == TYPE_DISK) &&
- (sdev_channel(sdev) != CONTAINER_CHANNEL) &&
- (!aac->jbod || sdev->inq_periph_qual) &&
- (!aac->raid_scsi_mode || (sdev_channel(sdev) != 2))) {
+
+ if (sdev->type == TYPE_DISK
+ && sdev_channel(sdev) != CONTAINER_CHANNEL
+ && (!aac->jbod || sdev->inq_periph_qual)
+ && (!aac->raid_scsi_mode || (sdev_channel(sdev) != 2))) {
+
if (expose_physicals == 0)
return -ENXIO;
+
if (expose_physicals < 0)
sdev->no_uld_attach = 1;
}
- if (sdev->tagged_supported && (sdev->type == TYPE_DISK) &&
- (!aac->raid_scsi_mode || (sdev_channel(sdev) != 2)) &&
- !sdev->no_uld_attach) {
+
+ if (sdev->tagged_supported
+ && sdev->type == TYPE_DISK
+ && (!aac->raid_scsi_mode || (sdev_channel(sdev) != 2))
+ && !sdev->no_uld_attach) {
+
struct scsi_device * dev;
struct Scsi_Host *host = sdev->host;
unsigned num_lsu = 0;
unsigned num_one = 0;
- unsigned depth;
unsigned cid;
- /*
- * Firmware has an individual device recovery time typically
- * of 35 seconds, give us a margin.
- */
- if (sdev->request_queue->rq_timeout < (45 * HZ))
- blk_queue_rq_timeout(sdev->request_queue, 45*HZ);
+ set_timeout = 1;
+
for (cid = 0; cid < aac->maximum_num_containers; ++cid)
if (aac->fsa_dev[cid].valid)
++num_lsu;
+
__shost_for_each_device(dev, host) {
- if (dev->tagged_supported && (dev->type == TYPE_DISK) &&
- (!aac->raid_scsi_mode ||
- (sdev_channel(sdev) != 2)) &&
- !dev->no_uld_attach) {
+ if (dev->tagged_supported
+ && dev->type == TYPE_DISK
+ && (!aac->raid_scsi_mode || (sdev_channel(sdev) != 2))
+ && !dev->no_uld_attach) {
if ((sdev_channel(dev) != CONTAINER_CHANNEL)
- || !aac->fsa_dev[sdev_id(dev)].valid)
+ || !aac->fsa_dev[sdev_id(dev)].valid) {
++num_lsu;
- } else
+ }
+ } else {
++num_one;
+ }
}
+
if (num_lsu == 0)
++num_lsu;
+
depth = (host->can_queue - num_one) / num_lsu;
- if (depth > 256)
- depth = 256;
- else if (depth < 2)
- depth = 2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth);
- } else
- scsi_adjust_queue_depth(sdev, 0, 1);
+
+ if (sdev_channel(sdev) != NATIVE_CHANNEL)
+ goto common_config;
+
+ set_qd_dev_type = true;
+
+ }
+
+common_config:
+
+ /*
+ * Check if SATA drive
+ */
+ if (set_qd_dev_type) {
+ if (strncmp(sdev->vendor, "ATA", 3) == 0)
+ depth = 32;
+ else
+ depth = 64;
+ }
+
+ /*
+ * Firmware has an individual device recovery time typically
+ * of 35 seconds, give us a margin. Thor devices can take longer in
+ * error recovery, hence different value.
+ */
+ if (set_timeout) {
+ timeout = aac->sa_firmware ? AAC_SA_TIMEOUT : AAC_ARC_TIMEOUT;
+ blk_queue_rq_timeout(sdev->request_queue, timeout * HZ);
+ }
+
+ if (depth > 256)
+ depth = 256;
+ else if (depth < 1)
+ depth = 1;
+
+ scsi_change_queue_depth(sdev, depth);
+
+ sdev->tagged_supported = 1;
return 0;
}
@@ -477,11 +515,16 @@ static int aac_slave_configure(struct scsi_device *sdev)
* total capacity and the queue depth supported by the target device.
*/
-static int aac_change_queue_depth(struct scsi_device *sdev, int depth,
- int reason)
+static int aac_change_queue_depth(struct scsi_device *sdev, int depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
+ struct aac_dev *aac = (struct aac_dev *)(sdev->host->hostdata);
+ int chn, tid, is_native_device = 0;
+
+ chn = aac_logical_to_phys(sdev_channel(sdev));
+ tid = sdev_id(sdev);
+ if (chn < AAC_MAX_BUSES && tid < AAC_MAX_TARGETS &&
+ aac->hba_map[chn][tid].devtype == AAC_DEVTYPE_NATIVE_RAW)
+ is_native_device = 1;
if (sdev->tagged_supported && (sdev->type == TYPE_DISK) &&
(sdev_channel(sdev) == CONTAINER_CHANNEL)) {
@@ -503,9 +546,12 @@ static int aac_change_queue_depth(struct scsi_device *sdev, int depth,
depth = 256;
else if (depth < 2)
depth = 2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth);
- } else
- scsi_adjust_queue_depth(sdev, 0, 1);
+ return scsi_change_queue_depth(sdev, depth);
+ } else if (is_native_device) {
+ scsi_change_queue_depth(sdev, aac->hba_map[chn][tid].qd_limit);
+ } else {
+ scsi_change_queue_depth(sdev, 1);
+ }
return sdev->queue_depth;
}
@@ -529,142 +575,559 @@ static struct device_attribute aac_raid_level_attr = {
.show = aac_show_raid_level
};
-static struct device_attribute *aac_dev_attrs[] = {
- &aac_raid_level_attr,
+static ssize_t aac_show_unique_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct aac_dev *aac = (struct aac_dev *)(sdev->host->hostdata);
+ unsigned char sn[16];
+
+ memset(sn, 0, sizeof(sn));
+
+ if (sdev_channel(sdev) == CONTAINER_CHANNEL)
+ memcpy(sn, aac->fsa_dev[sdev_id(sdev)].identifier, sizeof(sn));
+
+ return snprintf(buf, 16 * 2 + 2,
+ "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ sn[0], sn[1], sn[2], sn[3],
+ sn[4], sn[5], sn[6], sn[7],
+ sn[8], sn[9], sn[10], sn[11],
+ sn[12], sn[13], sn[14], sn[15]);
+}
+
+static struct device_attribute aac_unique_id_attr = {
+ .attr = {
+ .name = "unique_id",
+ .mode = 0444,
+ },
+ .show = aac_show_unique_id
+};
+
+
+
+static struct attribute *aac_dev_attrs[] = {
+ &aac_raid_level_attr.attr,
+ &aac_unique_id_attr.attr,
NULL,
};
-static int aac_ioctl(struct scsi_device *sdev, int cmd, void __user * arg)
+ATTRIBUTE_GROUPS(aac_dev);
+
+static int aac_ioctl(struct scsi_device *sdev, unsigned int cmd,
+ void __user *arg)
{
+ int retval;
struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
+ retval = aac_adapter_check_health(dev);
+ if (retval)
+ return -EBUSY;
return aac_do_ioctl(dev, cmd, arg);
}
+struct fib_count_data {
+ int mlcnt;
+ int llcnt;
+ int ehcnt;
+ int fwcnt;
+ int krlcnt;
+};
+
+static bool fib_count_iter(struct scsi_cmnd *scmnd, void *data)
+{
+ struct fib_count_data *fib_count = data;
+
+ switch (aac_priv(scmnd)->owner) {
+ case AAC_OWNER_FIRMWARE:
+ fib_count->fwcnt++;
+ break;
+ case AAC_OWNER_ERROR_HANDLER:
+ fib_count->ehcnt++;
+ break;
+ case AAC_OWNER_LOWLEVEL:
+ fib_count->llcnt++;
+ break;
+ case AAC_OWNER_MIDLEVEL:
+ fib_count->mlcnt++;
+ break;
+ default:
+ fib_count->krlcnt++;
+ break;
+ }
+ return true;
+}
+
+/* Called during SCSI EH, so we don't need to block requests */
+static int get_num_of_incomplete_fibs(struct aac_dev *aac)
+{
+ struct Scsi_Host *shost = aac->scsi_host_ptr;
+ struct device *ctrl_dev;
+ struct fib_count_data fcnt = { };
+
+ scsi_host_busy_iter(shost, fib_count_iter, &fcnt);
+
+ ctrl_dev = &aac->pdev->dev;
+
+ dev_info(ctrl_dev, "outstanding cmd: midlevel-%d\n", fcnt.mlcnt);
+ dev_info(ctrl_dev, "outstanding cmd: lowlevel-%d\n", fcnt.llcnt);
+ dev_info(ctrl_dev, "outstanding cmd: error handler-%d\n", fcnt.ehcnt);
+ dev_info(ctrl_dev, "outstanding cmd: firmware-%d\n", fcnt.fwcnt);
+ dev_info(ctrl_dev, "outstanding cmd: kernel-%d\n", fcnt.krlcnt);
+
+ return fcnt.mlcnt + fcnt.llcnt + fcnt.ehcnt + fcnt.fwcnt;
+}
+
static int aac_eh_abort(struct scsi_cmnd* cmd)
{
+ struct aac_cmd_priv *cmd_priv = aac_priv(cmd);
struct scsi_device * dev = cmd->device;
struct Scsi_Host * host = dev->host;
struct aac_dev * aac = (struct aac_dev *)host->hostdata;
- int count;
+ int count, found;
+ u32 bus, cid;
int ret = FAILED;
- printk(KERN_ERR "%s: Host adapter abort request (%d,%d,%d,%d)\n",
- AAC_DRIVERNAME,
- host->host_no, sdev_channel(dev), sdev_id(dev), dev->lun);
- switch (cmd->cmnd[0]) {
- case SERVICE_ACTION_IN:
- if (!(aac->raw_io_interface) ||
- !(aac->raw_io_64) ||
- ((cmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
- break;
- case INQUIRY:
- case READ_CAPACITY:
- /* Mark associated FIB to not complete, eh handler does this */
+ if (aac_adapter_check_health(aac))
+ return ret;
+
+ bus = aac_logical_to_phys(scmd_channel(cmd));
+ cid = scmd_id(cmd);
+ if (aac->hba_map[bus][cid].devtype == AAC_DEVTYPE_NATIVE_RAW) {
+ struct fib *fib;
+ struct aac_hba_tm_req *tmf;
+ int status;
+ u64 address;
+
+ pr_err("%s: Host adapter abort request (%d,%d,%d,%d)\n",
+ AAC_DRIVERNAME,
+ host->host_no, sdev_channel(dev), sdev_id(dev), (int)dev->lun);
+
+ found = 0;
for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
- struct fib * fib = &aac->fibs[count];
- if (fib->hw_fib_va->header.XferState &&
- (fib->flags & FIB_CONTEXT_FLAG) &&
- (fib->callback_data == cmd)) {
- fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
- cmd->SCp.phase = AAC_OWNER_ERROR_HANDLER;
+ fib = &aac->fibs[count];
+ if (*(u8 *)fib->hw_fib_va != 0 &&
+ (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) &&
+ (fib->callback_data == cmd)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ return ret;
+
+ /* start a HBA_TMF_ABORT_TASK TMF request */
+ fib = aac_fib_alloc(aac);
+ if (!fib)
+ return ret;
+
+ tmf = (struct aac_hba_tm_req *)fib->hw_fib_va;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->tmf = HBA_TMF_ABORT_TASK;
+ tmf->it_nexus = aac->hba_map[bus][cid].rmw_nexus;
+ tmf->lun[1] = cmd->device->lun;
+
+ address = (u64)fib->hw_error_pa;
+ tmf->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
+ tmf->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
+ tmf->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
+
+ fib->hbacmd_size = sizeof(*tmf);
+ cmd_priv->sent_command = 0;
+
+ status = aac_hba_send(HBA_IU_TYPE_SCSI_TM_REQ, fib,
+ (fib_callback) aac_hba_callback,
+ (void *) cmd);
+ if (status != -EINPROGRESS) {
+ aac_fib_complete(fib);
+ aac_fib_free(fib);
+ return ret;
+ }
+ /* Wait up to 15 secs for completion */
+ for (count = 0; count < 15; ++count) {
+ if (cmd_priv->sent_command) {
ret = SUCCESS;
+ break;
}
+ msleep(1000);
}
- break;
- case TEST_UNIT_READY:
- /* Mark associated FIB to not complete, eh handler does this */
- for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
- struct scsi_cmnd * command;
- struct fib * fib = &aac->fibs[count];
- if ((fib->hw_fib_va->header.XferState & cpu_to_le32(Async | NoResponseExpected)) &&
- (fib->flags & FIB_CONTEXT_FLAG) &&
- ((command = fib->callback_data)) &&
- (command->device == cmd->device)) {
- fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
- command->SCp.phase = AAC_OWNER_ERROR_HANDLER;
- if (command == cmd)
+
+ if (ret != SUCCESS)
+ pr_err("%s: Host adapter abort request timed out\n",
+ AAC_DRIVERNAME);
+ } else {
+ pr_err(
+ "%s: Host adapter abort request.\n"
+ "%s: Outstanding commands on (%d,%d,%d,%d):\n",
+ AAC_DRIVERNAME, AAC_DRIVERNAME,
+ host->host_no, sdev_channel(dev), sdev_id(dev),
+ (int)dev->lun);
+ switch (cmd->cmnd[0]) {
+ case SERVICE_ACTION_IN_16:
+ if (!(aac->raw_io_interface) ||
+ !(aac->raw_io_64) ||
+ ((cmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
+ break;
+ fallthrough;
+ case INQUIRY:
+ case READ_CAPACITY:
+ /*
+ * Mark associated FIB to not complete,
+ * eh handler does this
+ */
+ for (count = 0;
+ count < (host->can_queue + AAC_NUM_MGT_FIB);
+ ++count) {
+ struct fib *fib = &aac->fibs[count];
+
+ if (fib->hw_fib_va->header.XferState &&
+ (fib->flags & FIB_CONTEXT_FLAG) &&
+ (fib->callback_data == cmd)) {
+ fib->flags |=
+ FIB_CONTEXT_FLAG_TIMED_OUT;
+ cmd_priv->owner =
+ AAC_OWNER_ERROR_HANDLER;
ret = SUCCESS;
+ }
}
+ break;
+ case TEST_UNIT_READY:
+ /*
+ * Mark associated FIB to not complete,
+ * eh handler does this
+ */
+ for (count = 0;
+ count < (host->can_queue + AAC_NUM_MGT_FIB);
+ ++count) {
+ struct scsi_cmnd *command;
+ struct fib *fib = &aac->fibs[count];
+
+ command = fib->callback_data;
+
+ if ((fib->hw_fib_va->header.XferState &
+ cpu_to_le32
+ (Async | NoResponseExpected)) &&
+ (fib->flags & FIB_CONTEXT_FLAG) &&
+ ((command)) &&
+ (command->device == cmd->device)) {
+ fib->flags |=
+ FIB_CONTEXT_FLAG_TIMED_OUT;
+ aac_priv(command)->owner =
+ AAC_OWNER_ERROR_HANDLER;
+ if (command == cmd)
+ ret = SUCCESS;
+ }
+ }
+ break;
}
}
return ret;
}
+static u8 aac_eh_tmf_lun_reset_fib(struct aac_hba_map_info *info,
+ struct fib *fib, u64 tmf_lun)
+{
+ struct aac_hba_tm_req *tmf;
+ u64 address;
+
+ /* start a HBA_TMF_LUN_RESET TMF request */
+ tmf = (struct aac_hba_tm_req *)fib->hw_fib_va;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->tmf = HBA_TMF_LUN_RESET;
+ tmf->it_nexus = info->rmw_nexus;
+ int_to_scsilun(tmf_lun, (struct scsi_lun *)tmf->lun);
+
+ address = (u64)fib->hw_error_pa;
+ tmf->error_ptr_hi = cpu_to_le32
+ ((u32)(address >> 32));
+ tmf->error_ptr_lo = cpu_to_le32
+ ((u32)(address & 0xffffffff));
+ tmf->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
+ fib->hbacmd_size = sizeof(*tmf);
+
+ return HBA_IU_TYPE_SCSI_TM_REQ;
+}
+
+static u8 aac_eh_tmf_hard_reset_fib(struct aac_hba_map_info *info,
+ struct fib *fib)
+{
+ struct aac_hba_reset_req *rst;
+ u64 address;
+
+ /* already tried, start a hard reset now */
+ rst = (struct aac_hba_reset_req *)fib->hw_fib_va;
+ memset(rst, 0, sizeof(*rst));
+ rst->it_nexus = info->rmw_nexus;
+
+ address = (u64)fib->hw_error_pa;
+ rst->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
+ rst->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
+ rst->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
+ fib->hbacmd_size = sizeof(*rst);
+
+ return HBA_IU_TYPE_SATA_REQ;
+}
+
+static void aac_tmf_callback(void *context, struct fib *fibptr)
+{
+ struct aac_hba_resp *err =
+ &((struct aac_native_hba *)fibptr->hw_fib_va)->resp.err;
+ struct aac_hba_map_info *info = context;
+ int res;
+
+ switch (err->service_response) {
+ case HBA_RESP_SVCRES_TMF_REJECTED:
+ res = -1;
+ break;
+ case HBA_RESP_SVCRES_TMF_LUN_INVALID:
+ res = 0;
+ break;
+ case HBA_RESP_SVCRES_TMF_COMPLETE:
+ case HBA_RESP_SVCRES_TMF_SUCCEEDED:
+ res = 0;
+ break;
+ default:
+ res = -2;
+ break;
+ }
+ aac_fib_complete(fibptr);
+
+ info->reset_state = res;
+}
+
/*
- * aac_eh_reset - Reset command handling
+ * aac_eh_dev_reset - Device reset command handling
* @scsi_cmd: SCSI command block causing the reset
*
*/
-static int aac_eh_reset(struct scsi_cmnd* cmd)
+static int aac_eh_dev_reset(struct scsi_cmnd *cmd)
{
struct scsi_device * dev = cmd->device;
struct Scsi_Host * host = dev->host;
- struct scsi_cmnd * command;
+ struct aac_dev * aac = (struct aac_dev *)host->hostdata;
+ struct aac_hba_map_info *info;
int count;
+ u32 bus, cid;
+ struct fib *fib;
+ int ret = FAILED;
+ int status;
+ u8 command;
+
+ bus = aac_logical_to_phys(scmd_channel(cmd));
+ cid = scmd_id(cmd);
+
+ if (bus >= AAC_MAX_BUSES || cid >= AAC_MAX_TARGETS)
+ return FAILED;
+
+ info = &aac->hba_map[bus][cid];
+
+ if (!(info->devtype == AAC_DEVTYPE_NATIVE_RAW &&
+ !(info->reset_state > 0)))
+ return FAILED;
+
+ pr_err("%s: Host device reset request. SCSI hang ?\n",
+ AAC_DRIVERNAME);
+
+ fib = aac_fib_alloc(aac);
+ if (!fib)
+ return ret;
+
+ /* start a HBA_TMF_LUN_RESET TMF request */
+ command = aac_eh_tmf_lun_reset_fib(info, fib, dev->lun);
+
+ info->reset_state = 1;
+
+ status = aac_hba_send(command, fib,
+ (fib_callback) aac_tmf_callback,
+ (void *) info);
+ if (status != -EINPROGRESS) {
+ info->reset_state = 0;
+ aac_fib_complete(fib);
+ aac_fib_free(fib);
+ return ret;
+ }
+ /* Wait up to 15 seconds for completion */
+ for (count = 0; count < 15; ++count) {
+ if (info->reset_state == 0) {
+ ret = info->reset_state == 0 ? SUCCESS : FAILED;
+ break;
+ }
+ msleep(1000);
+ }
+
+ return ret;
+}
+
+/*
+ * aac_eh_target_reset - Target reset command handling
+ * @scsi_cmd: SCSI command block causing the reset
+ *
+ */
+static int aac_eh_target_reset(struct scsi_cmnd *cmd)
+{
+ struct scsi_device * dev = cmd->device;
+ struct Scsi_Host * host = dev->host;
struct aac_dev * aac = (struct aac_dev *)host->hostdata;
- unsigned long flags;
+ struct aac_hba_map_info *info;
+ int count;
+ u32 bus, cid;
+ int ret = FAILED;
+ struct fib *fib;
+ int status;
+ u8 command;
+
+ bus = aac_logical_to_phys(scmd_channel(cmd));
+ cid = scmd_id(cmd);
+
+ if (bus >= AAC_MAX_BUSES || cid >= AAC_MAX_TARGETS)
+ return FAILED;
+
+ info = &aac->hba_map[bus][cid];
+
+ if (!(info->devtype == AAC_DEVTYPE_NATIVE_RAW &&
+ !(info->reset_state > 0)))
+ return FAILED;
+
+ pr_err("%s: Host target reset request. SCSI hang ?\n",
+ AAC_DRIVERNAME);
+
+ fib = aac_fib_alloc(aac);
+ if (!fib)
+ return ret;
- /* Mark the associated FIB to not complete, eh handler does this */
+
+ /* already tried, start a hard reset now */
+ command = aac_eh_tmf_hard_reset_fib(info, fib);
+
+ info->reset_state = 2;
+
+ status = aac_hba_send(command, fib,
+ (fib_callback) aac_tmf_callback,
+ (void *) info);
+
+ if (status != -EINPROGRESS) {
+ info->reset_state = 0;
+ aac_fib_complete(fib);
+ aac_fib_free(fib);
+ return ret;
+ }
+
+ /* Wait up to 15 seconds for completion */
+ for (count = 0; count < 15; ++count) {
+ if (info->reset_state <= 0) {
+ ret = info->reset_state == 0 ? SUCCESS : FAILED;
+ break;
+ }
+ msleep(1000);
+ }
+
+ return ret;
+}
+
+/*
+ * aac_eh_bus_reset - Bus reset command handling
+ * @scsi_cmd: SCSI command block causing the reset
+ *
+ */
+static int aac_eh_bus_reset(struct scsi_cmnd* cmd)
+{
+ struct scsi_device * dev = cmd->device;
+ struct Scsi_Host * host = dev->host;
+ struct aac_dev * aac = (struct aac_dev *)host->hostdata;
+ int count;
+ u32 cmd_bus;
+ int status = 0;
+
+
+ cmd_bus = aac_logical_to_phys(scmd_channel(cmd));
+ /* Mark the assoc. FIB to not complete, eh handler does this */
for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
- struct fib * fib = &aac->fibs[count];
+ struct fib *fib = &aac->fibs[count];
+
if (fib->hw_fib_va->header.XferState &&
- (fib->flags & FIB_CONTEXT_FLAG) &&
- (fib->callback_data == cmd)) {
- fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
- cmd->SCp.phase = AAC_OWNER_ERROR_HANDLER;
+ (fib->flags & FIB_CONTEXT_FLAG) &&
+ (fib->flags & FIB_CONTEXT_FLAG_SCSI_CMD)) {
+ struct aac_hba_map_info *info;
+ u32 bus, cid;
+
+ cmd = (struct scsi_cmnd *)fib->callback_data;
+ bus = aac_logical_to_phys(scmd_channel(cmd));
+ if (bus != cmd_bus)
+ continue;
+ cid = scmd_id(cmd);
+ info = &aac->hba_map[bus][cid];
+ if (bus >= AAC_MAX_BUSES || cid >= AAC_MAX_TARGETS ||
+ info->devtype != AAC_DEVTYPE_NATIVE_RAW) {
+ fib->flags |= FIB_CONTEXT_FLAG_EH_RESET;
+ aac_priv(cmd)->owner = AAC_OWNER_ERROR_HANDLER;
+ }
}
}
- printk(KERN_ERR "%s: Host adapter reset request. SCSI hang ?\n",
- AAC_DRIVERNAME);
- if ((count = aac_check_health(aac)))
- return count;
+ pr_err("%s: Host bus reset request. SCSI hang ?\n", AAC_DRIVERNAME);
+
/*
- * Wait for all commands to complete to this specific
- * target (block maximum 60 seconds).
+ * Check the health of the controller
*/
- for (count = 60; count; --count) {
- int active = aac->in_reset;
+ status = aac_adapter_check_health(aac);
+ if (status)
+ dev_err(&aac->pdev->dev, "Adapter health - %d\n", status);
- if (active == 0)
- __shost_for_each_device(dev, host) {
- spin_lock_irqsave(&dev->list_lock, flags);
- list_for_each_entry(command, &dev->cmd_list, list) {
- if ((command != cmd) &&
- (command->SCp.phase == AAC_OWNER_FIRMWARE)) {
- active++;
- break;
- }
- }
- spin_unlock_irqrestore(&dev->list_lock, flags);
- if (active)
- break;
+ count = get_num_of_incomplete_fibs(aac);
+ return (count == 0) ? SUCCESS : FAILED;
+}
- }
- /*
- * We can exit If all the commands are complete
- */
- if (active == 0)
- return SUCCESS;
- ssleep(1);
+/*
+ * aac_eh_host_reset - Host reset command handling
+ * @scsi_cmd: SCSI command block causing the reset
+ *
+ */
+static int aac_eh_host_reset(struct scsi_cmnd *cmd)
+{
+ struct scsi_device * dev = cmd->device;
+ struct Scsi_Host * host = dev->host;
+ struct aac_dev * aac = (struct aac_dev *)host->hostdata;
+ int ret = FAILED;
+ __le32 supported_options2 = 0;
+ bool is_mu_reset;
+ bool is_ignore_reset;
+ bool is_doorbell_reset;
+
+ /*
+ * Check if reset is supported by the firmware
+ */
+ supported_options2 = aac->supplement_adapter_info.supported_options2;
+ is_mu_reset = supported_options2 & AAC_OPTION_MU_RESET;
+ is_doorbell_reset = supported_options2 & AAC_OPTION_DOORBELL_RESET;
+ is_ignore_reset = supported_options2 & AAC_OPTION_IGNORE_RESET;
+ /*
+ * This adapter needs a blind reset, only do so for
+ * Adapters that support a register, instead of a commanded,
+ * reset.
+ */
+ if ((is_mu_reset || is_doorbell_reset)
+ && aac_check_reset
+ && (aac_check_reset != -1 || !is_ignore_reset)) {
+ /* Bypass wait for command quiesce */
+ if (aac_reset_adapter(aac, 2, IOP_HWSOFT_RESET) == 0)
+ ret = SUCCESS;
}
- printk(KERN_ERR "%s: SCSI bus appears hung\n", AAC_DRIVERNAME);
/*
- * This adapter needs a blind reset, only do so for Adapters that
- * support a register, instead of a commanded, reset.
+ * Reset EH state
*/
- if (((aac->supplement_adapter_info.SupportedOptions2 &
- AAC_OPTION_MU_RESET) ||
- (aac->supplement_adapter_info.SupportedOptions2 &
- AAC_OPTION_DOORBELL_RESET)) &&
- aac_check_reset &&
- ((aac_check_reset != 1) ||
- !(aac->supplement_adapter_info.SupportedOptions2 &
- AAC_OPTION_IGNORE_RESET)))
- aac_reset_adapter(aac, 2); /* Bypass wait for command quiesce */
- return SUCCESS; /* Cause an immediate retry of the command with a ten second delay after successful tur */
+ if (ret == SUCCESS) {
+ int bus, cid;
+ struct aac_hba_map_info *info;
+
+ for (bus = 0; bus < AAC_MAX_BUSES; bus++) {
+ for (cid = 0; cid < AAC_MAX_TARGETS; cid++) {
+ info = &aac->hba_map[bus][cid];
+ if (info->devtype == AAC_DEVTYPE_NATIVE_RAW)
+ info->reset_state = 0;
+ }
+ }
+ }
+ return ret;
}
/**
@@ -700,7 +1163,6 @@ static int aac_cfg_open(struct inode *inode, struct file *file)
/**
* aac_cfg_ioctl - AAC configuration request
- * @inode: inode of device
* @file: file handle
* @cmd: ioctl command code
* @arg: argument
@@ -715,71 +1177,13 @@ static int aac_cfg_open(struct inode *inode, struct file *file)
static long aac_cfg_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
- int ret;
- if (!capable(CAP_SYS_RAWIO))
- return -EPERM;
- mutex_lock(&aac_mutex);
- ret = aac_do_ioctl(file->private_data, cmd, (void __user *)arg);
- mutex_unlock(&aac_mutex);
-
- return ret;
-}
-
-#ifdef CONFIG_COMPAT
-static long aac_compat_do_ioctl(struct aac_dev *dev, unsigned cmd, unsigned long arg)
-{
- long ret;
- mutex_lock(&aac_mutex);
- switch (cmd) {
- case FSACTL_MINIPORT_REV_CHECK:
- case FSACTL_SENDFIB:
- case FSACTL_OPEN_GET_ADAPTER_FIB:
- case FSACTL_CLOSE_GET_ADAPTER_FIB:
- case FSACTL_SEND_RAW_SRB:
- case FSACTL_GET_PCI_INFO:
- case FSACTL_QUERY_DISK:
- case FSACTL_DELETE_DISK:
- case FSACTL_FORCE_DELETE_DISK:
- case FSACTL_GET_CONTAINERS:
- case FSACTL_SEND_LARGE_FIB:
- ret = aac_do_ioctl(dev, cmd, (void __user *)arg);
- break;
-
- case FSACTL_GET_NEXT_ADAPTER_FIB: {
- struct fib_ioctl __user *f;
-
- f = compat_alloc_user_space(sizeof(*f));
- ret = 0;
- if (clear_user(f, sizeof(*f)))
- ret = -EFAULT;
- if (copy_in_user(f, (void __user *)arg, sizeof(struct fib_ioctl) - sizeof(u32)))
- ret = -EFAULT;
- if (!ret)
- ret = aac_do_ioctl(dev, cmd, f);
- break;
- }
+ struct aac_dev *aac = (struct aac_dev *)file->private_data;
- default:
- ret = -ENOIOCTLCMD;
- break;
- }
- mutex_unlock(&aac_mutex);
- return ret;
-}
-
-static int aac_compat_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
-{
- struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
- return aac_compat_do_ioctl(dev, cmd, (unsigned long)arg);
-}
-
-static long aac_compat_cfg_ioctl(struct file *file, unsigned cmd, unsigned long arg)
-{
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- return aac_compat_do_ioctl(file->private_data, cmd, arg);
+
+ return aac_do_ioctl(aac, cmd, (void __user *)arg);
}
-#endif
static ssize_t aac_show_model(struct device *device,
struct device_attribute *attr, char *buf)
@@ -787,8 +1191,8 @@ static ssize_t aac_show_model(struct device *device,
struct aac_dev *dev = (struct aac_dev*)class_to_shost(device)->hostdata;
int len;
- if (dev->supplement_adapter_info.AdapterTypeText[0]) {
- char * cp = dev->supplement_adapter_info.AdapterTypeText;
+ if (dev->supplement_adapter_info.adapter_type_text[0]) {
+ char *cp = dev->supplement_adapter_info.adapter_type_text;
while (*cp && *cp != ' ')
++cp;
while (*cp == ' ')
@@ -804,18 +1208,20 @@ static ssize_t aac_show_vendor(struct device *device,
struct device_attribute *attr, char *buf)
{
struct aac_dev *dev = (struct aac_dev*)class_to_shost(device)->hostdata;
+ struct aac_supplement_adapter_info *sup_adap_info;
int len;
- if (dev->supplement_adapter_info.AdapterTypeText[0]) {
- char * cp = dev->supplement_adapter_info.AdapterTypeText;
+ sup_adap_info = &dev->supplement_adapter_info;
+ if (sup_adap_info->adapter_type_text[0]) {
+ char *cp = sup_adap_info->adapter_type_text;
while (*cp && *cp != ' ')
++cp;
len = snprintf(buf, PAGE_SIZE, "%.*s\n",
- (int)(cp - (char *)dev->supplement_adapter_info.AdapterTypeText),
- dev->supplement_adapter_info.AdapterTypeText);
+ (int)(cp - (char *)sup_adap_info->adapter_type_text),
+ sup_adap_info->adapter_type_text);
} else
len = snprintf(buf, PAGE_SIZE, "%s\n",
- aac_drivers[dev->cardtype].vname);
+ aac_drivers[dev->cardtype].vname);
return len;
}
@@ -828,20 +1234,21 @@ static ssize_t aac_show_flags(struct device *cdev,
if (nblank(dprintk(x)))
len = snprintf(buf, PAGE_SIZE, "dprintk\n");
#ifdef AAC_DETAILED_STATUS_INFO
- len += snprintf(buf + len, PAGE_SIZE - len,
- "AAC_DETAILED_STATUS_INFO\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "AAC_DETAILED_STATUS_INFO\n");
#endif
if (dev->raw_io_interface && dev->raw_io_64)
- len += snprintf(buf + len, PAGE_SIZE - len,
- "SAI_READ_CAPACITY_16\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "SAI_READ_CAPACITY_16\n");
if (dev->jbod)
- len += snprintf(buf + len, PAGE_SIZE - len, "SUPPORTED_JBOD\n");
- if (dev->supplement_adapter_info.SupportedOptions2 &
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "SUPPORTED_JBOD\n");
+ if (dev->supplement_adapter_info.supported_options2 &
AAC_OPTION_POWER_MANAGEMENT)
- len += snprintf(buf + len, PAGE_SIZE - len,
- "SUPPORTED_POWER_MANAGEMENT\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "SUPPORTED_POWER_MANAGEMENT\n");
if (dev->msi)
- len += snprintf(buf + len, PAGE_SIZE - len, "PCI_HAS_MSI\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len, "PCI_HAS_MSI\n");
return len;
}
@@ -887,6 +1294,13 @@ static ssize_t aac_show_bios_version(struct device *device,
return len;
}
+static ssize_t aac_show_driver_version(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", aac_driver_version);
+}
+
static ssize_t aac_show_serial_number(struct device *device,
struct device_attribute *attr, char *buf)
{
@@ -894,16 +1308,17 @@ static ssize_t aac_show_serial_number(struct device *device,
int len = 0;
if (le32_to_cpu(dev->adapter_info.serial[0]) != 0xBAD0)
- len = snprintf(buf, PAGE_SIZE, "%06X\n",
+ len = snprintf(buf, 16, "%06X\n",
le32_to_cpu(dev->adapter_info.serial[0]));
if (len &&
- !memcmp(&dev->supplement_adapter_info.MfgPcbaSerialNo[
- sizeof(dev->supplement_adapter_info.MfgPcbaSerialNo)-len],
+ !memcmp(&dev->supplement_adapter_info.mfg_pcba_serial_no[
+ sizeof(dev->supplement_adapter_info.mfg_pcba_serial_no)-len],
buf, len-1))
- len = snprintf(buf, PAGE_SIZE, "%.*s\n",
- (int)sizeof(dev->supplement_adapter_info.MfgPcbaSerialNo),
- dev->supplement_adapter_info.MfgPcbaSerialNo);
- return len;
+ len = snprintf(buf, 16, "%.*s\n",
+ (int)sizeof(dev->supplement_adapter_info.mfg_pcba_serial_no),
+ dev->supplement_adapter_info.mfg_pcba_serial_no);
+
+ return min(len, 16);
}
static ssize_t aac_show_max_channel(struct device *device,
@@ -928,9 +1343,12 @@ static ssize_t aac_store_reset_adapter(struct device *device,
if (!capable(CAP_SYS_ADMIN))
return retval;
- retval = aac_reset_adapter((struct aac_dev*)class_to_shost(device)->hostdata, buf[0] == '!');
+
+ retval = aac_reset_adapter(shost_priv(class_to_shost(device)),
+ buf[0] == '!', IOP_HWSOFT_RESET);
if (retval >= 0)
retval = count;
+
return retval;
}
@@ -990,6 +1408,13 @@ static struct device_attribute aac_bios_version = {
},
.show = aac_show_bios_version,
};
+static struct device_attribute aac_lld_version = {
+ .attr = {
+ .name = "driver_version",
+ .mode = 0444,
+ },
+ .show = aac_show_driver_version,
+};
static struct device_attribute aac_serial_number = {
.attr = {
.name = "serial_number",
@@ -1020,20 +1445,23 @@ static struct device_attribute aac_reset = {
.show = aac_show_reset_adapter,
};
-static struct device_attribute *aac_attrs[] = {
- &aac_model,
- &aac_vendor,
- &aac_flags,
- &aac_kernel_version,
- &aac_monitor_version,
- &aac_bios_version,
- &aac_serial_number,
- &aac_max_channel,
- &aac_max_id,
- &aac_reset,
+static struct attribute *aac_host_attrs[] = {
+ &aac_model.attr,
+ &aac_vendor.attr,
+ &aac_flags.attr,
+ &aac_kernel_version.attr,
+ &aac_monitor_version.attr,
+ &aac_bios_version.attr,
+ &aac_lld_version.attr,
+ &aac_serial_number.attr,
+ &aac_max_channel.attr,
+ &aac_max_id.attr,
+ &aac_reset.attr,
NULL
};
+ATTRIBUTE_GROUPS(aac_host);
+
ssize_t aac_get_serial_number(struct device *device, char *buf)
{
return aac_show_serial_number(device, &aac_serial_number, buf);
@@ -1043,29 +1471,32 @@ static const struct file_operations aac_cfg_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = aac_cfg_ioctl,
#ifdef CONFIG_COMPAT
- .compat_ioctl = aac_compat_cfg_ioctl,
+ .compat_ioctl = aac_cfg_ioctl,
#endif
.open = aac_cfg_open,
.llseek = noop_llseek,
};
-static struct scsi_host_template aac_driver_template = {
+static const struct scsi_host_template aac_driver_template = {
.module = THIS_MODULE,
.name = "AAC",
.proc_name = AAC_DRIVERNAME,
.info = aac_info,
.ioctl = aac_ioctl,
#ifdef CONFIG_COMPAT
- .compat_ioctl = aac_compat_ioctl,
+ .compat_ioctl = aac_ioctl,
#endif
.queuecommand = aac_queuecommand,
.bios_param = aac_biosparm,
- .shost_attrs = aac_attrs,
- .slave_configure = aac_slave_configure,
+ .shost_groups = aac_host_groups,
+ .sdev_configure = aac_sdev_configure,
.change_queue_depth = aac_change_queue_depth,
- .sdev_attrs = aac_dev_attrs,
+ .sdev_groups = aac_dev_groups,
.eh_abort_handler = aac_eh_abort,
- .eh_host_reset_handler = aac_eh_reset,
+ .eh_device_reset_handler = aac_eh_dev_reset,
+ .eh_target_reset_handler = aac_eh_target_reset,
+ .eh_bus_reset_handler = aac_eh_bus_reset,
+ .eh_host_reset_handler = aac_eh_host_reset,
.can_queue = AAC_NUM_IO_FIB,
.this_id = MAXIMUM_NUM_CONTAINERS,
.sg_tablesize = 16,
@@ -1075,31 +1506,92 @@ static struct scsi_host_template aac_driver_template = {
#else
.cmd_per_lun = AAC_NUM_IO_FIB,
#endif
- .use_clustering = ENABLE_CLUSTERING,
.emulated = 1,
+ .no_write_same = 1,
+ .cmd_size = sizeof(struct aac_cmd_priv),
};
static void __aac_shutdown(struct aac_dev * aac)
{
- if (aac->aif_thread)
+ int i;
+
+ mutex_lock(&aac->ioctl_mutex);
+ aac->adapter_shutdown = 1;
+ mutex_unlock(&aac->ioctl_mutex);
+
+ if (aac->aif_thread) {
+ int i;
+ /* Clear out events first */
+ for (i = 0; i < (aac->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); i++) {
+ struct fib *fib = &aac->fibs[i];
+ if (!(fib->hw_fib_va->header.XferState & cpu_to_le32(NoResponseExpected | Async)) &&
+ (fib->hw_fib_va->header.XferState & cpu_to_le32(ResponseExpected)))
+ complete(&fib->event_wait);
+ }
kthread_stop(aac->thread);
+ aac->thread = NULL;
+ }
+
aac_send_shutdown(aac);
+
aac_adapter_disable_int(aac);
- free_irq(aac->pdev->irq, aac);
+
+ if (aac_is_src(aac)) {
+ if (aac->max_msix > 1) {
+ for (i = 0; i < aac->max_msix; i++) {
+ free_irq(pci_irq_vector(aac->pdev, i),
+ &(aac->aac_msix[i]));
+ }
+ } else {
+ free_irq(aac->pdev->irq,
+ &(aac->aac_msix[0]));
+ }
+ } else {
+ free_irq(aac->pdev->irq, aac);
+ }
if (aac->msi)
pci_disable_msi(aac->pdev);
+ else if (aac->max_msix > 1)
+ pci_disable_msix(aac->pdev);
+}
+static void aac_init_char(void)
+{
+ aac_cfg_major = register_chrdev(0, "aac", &aac_cfg_fops);
+ if (aac_cfg_major < 0) {
+ pr_err("aacraid: unable to register \"aac\" device.\n");
+ }
}
-static int __devinit aac_probe_one(struct pci_dev *pdev,
- const struct pci_device_id *id)
+void aac_reinit_aif(struct aac_dev *aac, unsigned int index)
+{
+ /*
+ * Firmware may send a AIF messages very early and the Driver may have
+ * ignored as it is not fully ready to process the messages. Send
+ * AIF to firmware so that if there are any unprocessed events they
+ * can be processed now.
+ */
+ if (aac_drivers[index].quirks & AAC_QUIRK_SRC)
+ aac_intr_normal(aac, 0, 2, 0, NULL);
+
+}
+
+static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
unsigned index = id->driver_data;
struct Scsi_Host *shost;
struct aac_dev *aac;
struct list_head *insert = &aac_devices;
- int error = -ENODEV;
+ int error;
int unique_id = 0;
u64 dmamask;
+ int mask_bits = 0;
+ extern int aac_sync_mode;
+
+ /*
+ * Only series 7 needs freset.
+ */
+ if (pdev->device == PMC_DEVICE_S7)
+ pdev->needs_freset = 1;
list_for_each_entry(aac, &aac_devices, entry) {
if (aac->id > unique_id)
@@ -1108,36 +1600,57 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
unique_id++;
}
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
+ PCIE_LINK_STATE_CLKPM);
+
error = pci_enable_device(pdev);
if (error)
goto out;
- error = -ENODEV;
+
+ if (!(aac_drivers[index].quirks & AAC_QUIRK_SRC)) {
+ error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (error) {
+ dev_err(&pdev->dev, "PCI 32 BIT dma mask set failed");
+ goto out_disable_pdev;
+ }
+ }
/*
* If the quirk31 bit is set, the adapter needs adapter
* to driver communication memory to be allocated below 2gig
*/
- if (aac_drivers[index].quirks & AAC_QUIRK_31BIT)
+ if (aac_drivers[index].quirks & AAC_QUIRK_31BIT) {
dmamask = DMA_BIT_MASK(31);
- else
+ mask_bits = 31;
+ } else {
dmamask = DMA_BIT_MASK(32);
+ mask_bits = 32;
+ }
- if (pci_set_dma_mask(pdev, dmamask) ||
- pci_set_consistent_dma_mask(pdev, dmamask))
+ error = dma_set_coherent_mask(&pdev->dev, dmamask);
+ if (error) {
+ dev_err(&pdev->dev, "PCI %d B consistent dma mask set failed\n"
+ , mask_bits);
goto out_disable_pdev;
+ }
pci_set_master(pdev);
shost = scsi_host_alloc(&aac_driver_template, sizeof(struct aac_dev));
- if (!shost)
+ if (!shost) {
+ error = -ENOMEM;
goto out_disable_pdev;
+ }
shost->irq = pdev->irq;
- shost->base = pci_resource_start(pdev, 0);
shost->unique_id = unique_id;
shost->max_cmd_len = 16;
+ if (aac_cfg_major == AAC_CHARDEV_NEEDS_REINIT)
+ aac_init_char();
+
aac = (struct aac_dev *)shost->hostdata;
+ aac->base_start = pci_resource_start(pdev, 0);
aac->scsi_host_ptr = shost;
aac->pdev = pdev;
aac->name = aac_driver_template.name;
@@ -1145,17 +1658,48 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
aac->cardtype = index;
INIT_LIST_HEAD(&aac->entry);
- aac->fibs = kmalloc(sizeof(struct fib) * (shost->can_queue + AAC_NUM_MGT_FIB), GFP_KERNEL);
- if (!aac->fibs)
+ if (aac_reset_devices || reset_devices)
+ aac->init_reset = true;
+
+ aac->fibs = kcalloc(shost->can_queue + AAC_NUM_MGT_FIB,
+ sizeof(struct fib),
+ GFP_KERNEL);
+ if (!aac->fibs) {
+ error = -ENOMEM;
goto out_free_host;
+ }
+
spin_lock_init(&aac->fib_lock);
+ mutex_init(&aac->ioctl_mutex);
+ mutex_init(&aac->scan_mutex);
+
+ INIT_DELAYED_WORK(&aac->safw_rescan_work, aac_safw_rescan_worker);
+ INIT_DELAYED_WORK(&aac->src_reinit_aif_worker,
+ aac_src_reinit_aif_worker);
/*
* Map in the registers from the adapter.
*/
aac->base_size = AAC_MIN_FOOTPRINT_SIZE;
- if ((*aac_drivers[index].init)(aac))
+ if ((*aac_drivers[index].init)(aac)) {
+ error = -ENODEV;
goto out_unmap;
+ }
+
+ if (aac->sync_mode) {
+ if (aac_sync_mode)
+ printk(KERN_INFO "%s%d: Sync. mode enforced "
+ "by driver parameter. This will cause "
+ "a significant performance decrease!\n",
+ aac->name,
+ aac->id);
+ else
+ printk(KERN_INFO "%s%d: Async. mode not supported "
+ "by current driver, sync. mode enforced."
+ "\nPlease update driver to get full performance.\n",
+ aac->name,
+ aac->id);
+ }
/*
* Start any kernel threads needed
@@ -1164,18 +1708,10 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
if (IS_ERR(aac->thread)) {
printk(KERN_ERR "aacraid: Unable to create command thread.\n");
error = PTR_ERR(aac->thread);
+ aac->thread = NULL;
goto out_deinit;
}
- /*
- * If we had set a smaller DMA mask earlier, set it to 4gig
- * now since the adapter can dma data to at least a 4gig
- * address space.
- */
- if (aac_drivers[index].quirks & AAC_QUIRK_31BIT)
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))
- goto out_deinit;
-
aac->maximum_num_channels = aac_drivers[index].channels;
error = aac_get_adapter_info(aac);
if (error < 0)
@@ -1196,11 +1732,10 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
shost->max_sectors = (shost->sg_tablesize * 8) + 112;
}
- error = pci_set_dma_max_seg_size(pdev,
- (aac->adapter_info.options & AAC_OPT_NEW_COMM) ?
- (shost->max_sectors << 9) : 65536);
- if (error)
- goto out_deinit;
+ if (aac->adapter_info.options & AAC_OPT_NEW_COMM)
+ shost->max_segment_size = shost->max_sectors << 9;
+ else
+ shost->max_segment_size = 65536;
/*
* Firmware printf works only with older firmware.
@@ -1232,6 +1767,9 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
else
shost->this_id = shost->max_id;
+ if (!aac->sa_firmware && aac_drivers[index].quirks & AAC_QUIRK_SRC)
+ aac_intr_normal(aac, 0, 2, 0, NULL);
+
/*
* dmb - we may need to move the setting of these parms somewhere else once
* we get a fib that can report the actual numbers
@@ -1243,7 +1781,10 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
error = scsi_add_host(shost, &pdev->dev);
if (error)
goto out_deinit;
- scsi_scan_host(shost);
+
+ aac_scan_host(aac);
+
+ pci_save_state(pdev);
return 0;
@@ -1252,8 +1793,8 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
out_unmap:
aac_fib_map_free(aac);
if (aac->comm_addr)
- pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr,
- aac->comm_phys);
+ dma_free_coherent(&aac->pdev->dev, aac->comm_size,
+ aac->comm_addr, aac->comm_phys);
kfree(aac->queues);
aac_adapter_ioremap(aac, 0);
kfree(aac->fibs);
@@ -1266,24 +1807,113 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
return error;
}
+static void aac_release_resources(struct aac_dev *aac)
+{
+ aac_adapter_disable_int(aac);
+ aac_free_irq(aac);
+}
+
+static int aac_acquire_resources(struct aac_dev *dev)
+{
+ unsigned long status;
+ /*
+ * First clear out all interrupts. Then enable the one's that we
+ * can handle.
+ */
+ while (!((status = src_readl(dev, MUnit.OMR)) & KERNEL_UP_AND_RUNNING)
+ || status == 0xffffffff)
+ msleep(20);
+
+ aac_adapter_disable_int(dev);
+ aac_adapter_enable_int(dev);
+
+
+ if (aac_is_src(dev))
+ aac_define_int_mode(dev);
+
+ if (dev->msi_enabled)
+ aac_src_access_devreg(dev, AAC_ENABLE_MSIX);
+
+ if (aac_acquire_irq(dev))
+ goto error_iounmap;
+
+ aac_adapter_enable_int(dev);
+
+ /*max msix may change after EEH
+ * Re-assign vectors to fibs
+ */
+ aac_fib_vector_assign(dev);
+
+ if (!dev->sync_mode) {
+ /* After EEH recovery or suspend resume, max_msix count
+ * may change, therefore updating in init as well.
+ */
+ dev->init->r7.no_of_msix_vectors = cpu_to_le32(dev->max_msix);
+ aac_adapter_start(dev);
+ }
+ return 0;
+
+error_iounmap:
+ return -1;
+
+}
+
+static int __maybe_unused aac_suspend(struct device *dev)
+{
+ struct Scsi_Host *shost = dev_get_drvdata(dev);
+ struct aac_dev *aac = (struct aac_dev *)shost->hostdata;
+
+ scsi_host_block(shost);
+ aac_cancel_rescan_worker(aac);
+ aac_send_shutdown(aac);
+
+ aac_release_resources(aac);
+
+ return 0;
+}
+
+static int __maybe_unused aac_resume(struct device *dev)
+{
+ struct Scsi_Host *shost = dev_get_drvdata(dev);
+ struct aac_dev *aac = (struct aac_dev *)shost->hostdata;
+
+ if (aac_acquire_resources(aac))
+ goto fail_device;
+ /*
+ * reset this flag to unblock ioctl() as it was set at
+ * aac_send_shutdown() to block ioctls from upperlayer
+ */
+ aac->adapter_shutdown = 0;
+ scsi_host_unblock(shost, SDEV_RUNNING);
+
+ return 0;
+
+fail_device:
+ printk(KERN_INFO "%s%d: resume failed.\n", aac->name, aac->id);
+ scsi_host_put(shost);
+ return -ENODEV;
+}
+
static void aac_shutdown(struct pci_dev *dev)
{
struct Scsi_Host *shost = pci_get_drvdata(dev);
- scsi_block_requests(shost);
+
+ scsi_host_block(shost);
__aac_shutdown((struct aac_dev *)shost->hostdata);
}
-static void __devexit aac_remove_one(struct pci_dev *pdev)
+static void aac_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct aac_dev *aac = (struct aac_dev *)shost->hostdata;
+ aac_cancel_rescan_worker(aac);
scsi_remove_host(shost);
__aac_shutdown(aac);
aac_fib_map_free(aac);
- pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr,
- aac->comm_phys);
+ dma_free_coherent(&aac->pdev->dev, aac->comm_size, aac->comm_addr,
+ aac->comm_phys);
kfree(aac->queues);
aac_adapter_ioremap(aac, 0);
@@ -1296,16 +1926,126 @@ static void __devexit aac_remove_one(struct pci_dev *pdev)
pci_disable_device(pdev);
if (list_empty(&aac_devices)) {
unregister_chrdev(aac_cfg_major, "aac");
- aac_cfg_major = -1;
+ aac_cfg_major = AAC_CHARDEV_NEEDS_REINIT;
}
}
+static pci_ers_result_t aac_pci_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t error)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct aac_dev *aac = shost_priv(shost);
+
+ dev_err(&pdev->dev, "aacraid: PCI error detected %x\n", error);
+
+ switch (error) {
+ case pci_channel_io_normal:
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ aac->handle_pci_error = 1;
+
+ scsi_host_block(shost);
+ aac_cancel_rescan_worker(aac);
+ scsi_host_complete_all_commands(shost, DID_NO_CONNECT);
+ aac_release_resources(aac);
+
+ aac_adapter_ioremap(aac, 0);
+
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ aac->handle_pci_error = 1;
+
+ scsi_host_complete_all_commands(shost, DID_NO_CONNECT);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t aac_pci_mmio_enabled(struct pci_dev *pdev)
+{
+ dev_err(&pdev->dev, "aacraid: PCI error - mmio enabled\n");
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t aac_pci_slot_reset(struct pci_dev *pdev)
+{
+ dev_err(&pdev->dev, "aacraid: PCI error - slot reset\n");
+ pci_restore_state(pdev);
+ if (pci_enable_device(pdev)) {
+ dev_warn(&pdev->dev,
+ "aacraid: failed to enable slave\n");
+ goto fail_device;
+ }
+
+ pci_set_master(pdev);
+
+ if (pci_enable_device_mem(pdev)) {
+ dev_err(&pdev->dev, "pci_enable_device_mem failed\n");
+ goto fail_device;
+ }
+
+ return PCI_ERS_RESULT_RECOVERED;
+
+fail_device:
+ dev_err(&pdev->dev, "aacraid: PCI error - slot reset failed\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+}
+
+
+static void aac_pci_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct aac_dev *aac = (struct aac_dev *)shost_priv(shost);
+
+ if (aac_adapter_ioremap(aac, aac->base_size)) {
+
+ dev_err(&pdev->dev, "aacraid: ioremap failed\n");
+ /* remap failed, go back ... */
+ aac->comm_interface = AAC_COMM_PRODUCER;
+ if (aac_adapter_ioremap(aac, AAC_MIN_FOOTPRINT_SIZE)) {
+ dev_warn(&pdev->dev,
+ "aacraid: unable to map adapter.\n");
+
+ return;
+ }
+ }
+
+ msleep(10000);
+
+ aac_acquire_resources(aac);
+
+ /*
+ * reset this flag to unblock ioctl() as it was set
+ * at aac_send_shutdown() to block ioctls from upperlayer
+ */
+ aac->adapter_shutdown = 0;
+ aac->handle_pci_error = 0;
+
+ scsi_host_unblock(shost, SDEV_RUNNING);
+ aac_scan_host(aac);
+ pci_save_state(pdev);
+
+ dev_err(&pdev->dev, "aacraid: PCI error - resume\n");
+}
+
+static const struct pci_error_handlers aac_pci_err_handler = {
+ .error_detected = aac_pci_error_detected,
+ .mmio_enabled = aac_pci_mmio_enabled,
+ .slot_reset = aac_pci_slot_reset,
+ .resume = aac_pci_resume,
+};
+
+static SIMPLE_DEV_PM_OPS(aac_pm_ops, aac_suspend, aac_resume);
+
static struct pci_driver aac_pci_driver = {
.name = AAC_DRIVERNAME,
.id_table = aac_pci_tbl,
.probe = aac_probe_one,
- .remove = __devexit_p(aac_remove_one),
+ .remove = aac_remove_one,
+ .driver.pm = &aac_pm_ops,
.shutdown = aac_shutdown,
+ .err_handler = &aac_pci_err_handler,
};
static int __init aac_init(void)
@@ -1319,11 +2059,8 @@ static int __init aac_init(void)
if (error < 0)
return error;
- aac_cfg_major = register_chrdev( 0, "aac", &aac_cfg_fops);
- if (aac_cfg_major < 0) {
- printk(KERN_WARNING
- "aacraid: unable to register \"aac\" device.\n");
- }
+ aac_init_char();
+
return 0;
}
diff --git a/drivers/scsi/aacraid/nark.c b/drivers/scsi/aacraid/nark.c
index f397d21a0c06..4745a99fba8c 100644
--- a/drivers/scsi/aacraid/nark.c
+++ b/drivers/scsi/aacraid/nark.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
*
@@ -5,27 +6,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* nark.c
*
* Abstract: Hardware Device Interface for NEMER/ARK
- *
*/
#include <linux/pci.h>
@@ -37,6 +24,7 @@
/**
* aac_nark_ioremap
+ * @dev: device to ioremap
* @size: mapping resize request
*
*/
@@ -49,14 +37,14 @@ static int aac_nark_ioremap(struct aac_dev * dev, u32 size)
dev->base = NULL;
return 0;
}
- dev->scsi_host_ptr->base = pci_resource_start(dev->pdev, 2);
+ dev->base_start = pci_resource_start(dev->pdev, 2);
dev->regs.rx = ioremap((u64)pci_resource_start(dev->pdev, 0) |
((u64)pci_resource_start(dev->pdev, 1) << 32),
sizeof(struct rx_registers) - sizeof(struct rx_inbound));
dev->base = NULL;
if (dev->regs.rx == NULL)
return -1;
- dev->base = ioremap(dev->scsi_host_ptr->base, size);
+ dev->base = ioremap(dev->base_start, size);
if (dev->base == NULL) {
iounmap(dev->regs.rx);
dev->regs.rx = NULL;
diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c
index be44de92429a..8ebc67e541af 100644
--- a/drivers/scsi/aacraid/rkt.c
+++ b/drivers/scsi/aacraid/rkt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,27 +7,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* rkt.c
*
* Abstract: Hardware miniport for Drawbridge specific hardware functions.
- *
*/
#include <linux/blkdev.h>
@@ -60,7 +47,7 @@ static int aac_rkt_select_comm(struct aac_dev *dev, int comm)
* case warrants this half baked, but convenient, check here.
*/
if (dev->scsi_host_ptr->can_queue > AAC_NUM_IO_FIB_RKT) {
- dev->init->MaxIoCommands =
+ dev->init->r7.max_io_commands =
cpu_to_le32(AAC_NUM_IO_FIB_RKT + AAC_NUM_MGT_FIB);
dev->scsi_host_ptr->can_queue = AAC_NUM_IO_FIB_RKT;
}
@@ -70,6 +57,7 @@ static int aac_rkt_select_comm(struct aac_dev *dev, int comm)
/**
* aac_rkt_ioremap
+ * @dev: device to ioremap
* @size: mapping resize request
*
*/
@@ -79,7 +67,7 @@ static int aac_rkt_ioremap(struct aac_dev * dev, u32 size)
iounmap(dev->regs.rkt);
return 0;
}
- dev->base = dev->regs.rkt = ioremap(dev->scsi_host_ptr->base, size);
+ dev->base = dev->regs.rkt = ioremap(dev->base_start, size);
if (dev->base == NULL)
return -1;
dev->IndexRegs = &dev->regs.rkt->IndexRegs;
@@ -90,8 +78,8 @@ static int aac_rkt_ioremap(struct aac_dev * dev, u32 size)
* aac_rkt_init - initialize an i960 based AAC card
* @dev: device to configure
*
- * Allocate and set up resources for the i960 based AAC variants. The
- * device_interface in the commregion will be allocated and linked
+ * Allocate and set up resources for the i960 based AAC variants. The
+ * device_interface in the commregion will be allocated and linked
* to the comm region.
*/
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
index ce530f113fdb..e06ff83b69ce 100644
--- a/drivers/scsi/aacraid/rx.c
+++ b/drivers/scsi/aacraid/rx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,27 +7,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* rx.c
*
* Abstract: Hardware miniport for Drawbridge specific hardware functions.
- *
*/
#include <linux/kernel.h>
@@ -157,7 +144,16 @@ static void aac_rx_enable_interrupt_message(struct aac_dev *dev)
* @dev: Adapter
* @command: Command to execute
* @p1: first parameter
- * @ret: adapter status
+ * @p2: second parameter
+ * @p3: third parameter
+ * @p4: forth parameter
+ * @p5: fifth parameter
+ * @p6: sixth parameter
+ * @status: adapter status
+ * @r1: first return value
+ * @r2: second return value
+ * @r3: third return value
+ * @r4: forth return value
*
* This routine will send a synchronous command to the adapter and wait
* for its completion.
@@ -315,10 +311,10 @@ static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event)
static void aac_rx_start_adapter(struct aac_dev *dev)
{
- struct aac_init *init;
+ union aac_init *init;
init = dev->init;
- init->HostElapsedSeconds = cpu_to_le32(get_seconds());
+ init->r7.host_elapsed_seconds = cpu_to_le32(ktime_get_real_seconds());
// We can only use a 32 bit address here
rx_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa,
0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
@@ -354,14 +350,16 @@ static int aac_rx_check_health(struct aac_dev *dev)
if (likely((status & 0xFF000000L) == 0xBC000000L))
return (status >> 16) & 0xFF;
- buffer = pci_alloc_consistent(dev->pdev, 512, &baddr);
+ buffer = dma_alloc_coherent(&dev->pdev->dev, 512, &baddr,
+ GFP_KERNEL);
ret = -2;
if (unlikely(buffer == NULL))
return ret;
- post = pci_alloc_consistent(dev->pdev,
- sizeof(struct POSTSTATUS), &paddr);
+ post = dma_alloc_coherent(&dev->pdev->dev,
+ sizeof(struct POSTSTATUS), &paddr,
+ GFP_KERNEL);
if (unlikely(post == NULL)) {
- pci_free_consistent(dev->pdev, 512, buffer, baddr);
+ dma_free_coherent(&dev->pdev->dev, 512, buffer, baddr);
return ret;
}
memset(buffer, 0, 512);
@@ -370,13 +368,13 @@ static int aac_rx_check_health(struct aac_dev *dev)
rx_writel(dev, MUnit.IMRx[0], paddr);
rx_sync_cmd(dev, COMMAND_POST_RESULTS, baddr, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL);
- pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS),
- post, paddr);
+ dma_free_coherent(&dev->pdev->dev, sizeof(struct POSTSTATUS),
+ post, paddr);
if (likely((buffer[0] == '0') && ((buffer[1] == 'x') || (buffer[1] == 'X')))) {
ret = (hex_to_bin(buffer[2]) << 4) +
hex_to_bin(buffer[3]);
}
- pci_free_consistent(dev->pdev, 512, buffer, baddr);
+ dma_free_coherent(&dev->pdev->dev, 512, buffer, baddr);
return ret;
}
/*
@@ -400,16 +398,13 @@ int aac_rx_deliver_producer(struct fib * fib)
{
struct aac_dev *dev = fib->dev;
struct aac_queue *q = &dev->queues->queue[AdapNormCmdQueue];
- unsigned long qflags;
u32 Index;
unsigned long nointr = 0;
- spin_lock_irqsave(q->lock, qflags);
aac_queue_get( dev, &Index, AdapNormCmdQueue, fib->hw_fib_va, 1, fib, &nointr);
- q->numpending++;
+ atomic_inc(&q->numpending);
*(q->headers.producer) = cpu_to_le32(Index + 1);
- spin_unlock_irqrestore(q->lock, qflags);
if (!(nointr & aac_config.irq_mod))
aac_adapter_notify(dev, AdapNormCmdQueue);
@@ -426,15 +421,12 @@ static int aac_rx_deliver_message(struct fib * fib)
{
struct aac_dev *dev = fib->dev;
struct aac_queue *q = &dev->queues->queue[AdapNormCmdQueue];
- unsigned long qflags;
u32 Index;
u64 addr;
volatile void __iomem *device;
unsigned long count = 10000000L; /* 50 seconds */
- spin_lock_irqsave(q->lock, qflags);
- q->numpending++;
- spin_unlock_irqrestore(q->lock, qflags);
+ atomic_inc(&q->numpending);
for(;;) {
Index = rx_readl(dev, MUnit.InboundQueue);
if (unlikely(Index == 0xFFFFFFFFL))
@@ -442,9 +434,7 @@ static int aac_rx_deliver_message(struct fib * fib)
if (likely(Index != 0xFFFFFFFFL))
break;
if (--count == 0) {
- spin_lock_irqsave(q->lock, qflags);
- q->numpending--;
- spin_unlock_irqrestore(q->lock, qflags);
+ atomic_dec(&q->numpending);
return -ETIMEDOUT;
}
udelay(5);
@@ -462,6 +452,7 @@ static int aac_rx_deliver_message(struct fib * fib)
/**
* aac_rx_ioremap
+ * @dev: adapter
* @size: mapping resize request
*
*/
@@ -471,18 +462,18 @@ static int aac_rx_ioremap(struct aac_dev * dev, u32 size)
iounmap(dev->regs.rx);
return 0;
}
- dev->base = dev->regs.rx = ioremap(dev->scsi_host_ptr->base, size);
+ dev->base = dev->regs.rx = ioremap(dev->base_start, size);
if (dev->base == NULL)
return -1;
dev->IndexRegs = &dev->regs.rx->IndexRegs;
return 0;
}
-static int aac_rx_restart_adapter(struct aac_dev *dev, int bled)
+static int aac_rx_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
{
- u32 var;
+ u32 var = 0;
- if (!(dev->supplement_adapter_info.SupportedOptions2 &
+ if (!(dev->supplement_adapter_info.supported_options2 &
AAC_OPTION_MU_RESET) || (bled >= 0) || (bled == -2)) {
if (bled)
printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n",
@@ -500,13 +491,14 @@ static int aac_rx_restart_adapter(struct aac_dev *dev, int bled)
if (bled && (bled != -ETIMEDOUT))
return -EINVAL;
}
- if (bled || (var == 0x3803000F)) { /* USE_OTHER_METHOD */
+ if (bled && (var == 0x3803000F)) { /* USE_OTHER_METHOD */
rx_writel(dev, MUnit.reserved2, 3);
msleep(5000); /* Delay 5 seconds */
var = 0x00000001;
}
- if (var != 0x00000001)
+ if (bled && (var != 0x00000001))
return -EINVAL;
+ ssleep(5);
if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC)
return -ENODEV;
if (startup_timeout < 300)
@@ -540,7 +532,7 @@ int aac_rx_select_comm(struct aac_dev *dev, int comm)
}
/**
- * aac_rx_init - initialize an i960 based AAC card
+ * _aac_rx_init - initialize an i960 based AAC card
* @dev: device to configure
*
* Allocate and set up resources for the i960 based AAC variants. The
@@ -565,17 +557,23 @@ int _aac_rx_init(struct aac_dev *dev)
dev->a_ops.adapter_sync_cmd = rx_sync_cmd;
dev->a_ops.adapter_enable_int = aac_rx_disable_interrupt;
dev->OIMR = status = rx_readb (dev, MUnit.OIMR);
- if ((((status & 0x0c) != 0x0c) || aac_reset_devices || reset_devices) &&
- !aac_rx_restart_adapter(dev, 0))
- /* Make sure the Hardware FIFO is empty */
- while ((++restart < 512) &&
- (rx_readl(dev, MUnit.OutboundQueue) != 0xFFFFFFFFL));
+
+ if (((status & 0x0c) != 0x0c) || dev->init_reset) {
+ dev->init_reset = false;
+ if (!aac_rx_restart_adapter(dev, 0, IOP_HWSOFT_RESET)) {
+ /* Make sure the Hardware FIFO is empty */
+ while ((++restart < 512) &&
+ (rx_readl(dev, MUnit.OutboundQueue) != 0xFFFFFFFFL));
+ }
+ }
+
/*
* Check to see if the board panic'd while booting.
*/
status = rx_readl(dev, MUnit.OMRx[0]);
if (status & KERNEL_PANIC) {
- if (aac_rx_restart_adapter(dev, aac_rx_check_health(dev)))
+ if (aac_rx_restart_adapter(dev,
+ aac_rx_check_health(dev), IOP_HWSOFT_RESET))
goto error_iounmap;
++restart;
}
@@ -613,7 +611,8 @@ int _aac_rx_init(struct aac_dev *dev)
((startup_timeout > 60)
? (startup_timeout - 60)
: (startup_timeout / 2))))) {
- if (likely(!aac_rx_restart_adapter(dev, aac_rx_check_health(dev))))
+ if (likely(!aac_rx_restart_adapter(dev,
+ aac_rx_check_health(dev), IOP_HWSOFT_RESET)))
start = jiffies;
++restart;
}
@@ -630,6 +629,7 @@ int _aac_rx_init(struct aac_dev *dev)
dev->a_ops.adapter_sync_cmd = rx_sync_cmd;
dev->a_ops.adapter_check_health = aac_rx_check_health;
dev->a_ops.adapter_restart = aac_rx_restart_adapter;
+ dev->a_ops.adapter_start = aac_rx_start_adapter;
/*
* First clear out all interrupts. Then enable the one's that we
@@ -643,16 +643,17 @@ int _aac_rx_init(struct aac_dev *dev)
if (aac_init_adapter(dev) == NULL)
goto error_iounmap;
aac_adapter_comm(dev, dev->comm_interface);
+ dev->sync_mode = 0; /* sync. mode not supported */
dev->msi = aac_msi && !pci_enable_msi(dev->pdev);
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
- IRQF_SHARED|IRQF_DISABLED, "aacraid", dev) < 0) {
+ IRQF_SHARED, "aacraid", dev) < 0) {
if (dev->msi)
pci_disable_msi(dev->pdev);
printk(KERN_ERR "%s%d: Interrupt unavailable.\n",
name, instance);
goto error_iounmap;
}
- dev->dbg_base = dev->scsi_host_ptr->base;
+ dev->dbg_base = dev->base_start;
dev->dbg_base_mapped = dev->base;
dev->dbg_size = dev->base_size;
diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c
index e5d4457121ea..c9a1dad2f563 100644
--- a/drivers/scsi/aacraid/sa.c
+++ b/drivers/scsi/aacraid/sa.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,27 +7,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* sa.c
*
* Abstract: Drawbridge specific support functions
- *
*/
#include <linux/kernel.h>
@@ -148,13 +135,21 @@ static void aac_sa_notify_adapter(struct aac_dev *dev, u32 event)
* @dev: Adapter
* @command: Command to execute
* @p1: first parameter
+ * @p2: second parameter
+ * @p3: third parameter
+ * @p4: forth parameter
+ * @p5: fifth parameter
+ * @p6: sixth parameter
* @ret: adapter status
+ * @r1: first return value
+ * @r2: second return value
+ * @r3: third return value
+ * @r4: forth return value
*
- * This routine will send a synchronous command to the adapter and wait
+ * This routine will send a synchronous command to the adapter and wait
* for its completion.
*/
-
-static int sa_sync_cmd(struct aac_dev *dev, u32 command,
+static int sa_sync_cmd(struct aac_dev *dev, u32 command,
u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6,
u32 *ret, u32 *r1, u32 *r2, u32 *r3, u32 *r4)
{
@@ -245,19 +240,19 @@ static void aac_sa_interrupt_adapter (struct aac_dev *dev)
static void aac_sa_start_adapter(struct aac_dev *dev)
{
- struct aac_init *init;
+ union aac_init *init;
/*
* Fill in the remaining pieces of the init.
*/
init = dev->init;
- init->HostElapsedSeconds = cpu_to_le32(get_seconds());
+ init->r7.host_elapsed_seconds = cpu_to_le32(ktime_get_real_seconds());
/* We can only use a 32 bit address here */
sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,
(u32)(ulong)dev->init_pa, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL);
}
-static int aac_sa_restart_adapter(struct aac_dev *dev, int bled)
+static int aac_sa_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
{
return -EINVAL;
}
@@ -296,6 +291,7 @@ static int aac_sa_check_health(struct aac_dev *dev)
/**
* aac_sa_ioremap
+ * @dev: device to ioremap
* @size: mapping resize request
*
*/
@@ -305,7 +301,7 @@ static int aac_sa_ioremap(struct aac_dev * dev, u32 size)
iounmap(dev->regs.sa);
return 0;
}
- dev->base = dev->regs.sa = ioremap(dev->scsi_host_ptr->base, size);
+ dev->base = dev->regs.sa = ioremap(dev->base_start, size);
return (dev->base == NULL) ? -1 : 0;
}
@@ -313,8 +309,8 @@ static int aac_sa_ioremap(struct aac_dev * dev, u32 size)
* aac_sa_init - initialize an ARM based AAC card
* @dev: device to configure
*
- * Allocate and set up resources for the ARM based AAC variants. The
- * device_interface in the commregion will be allocated and linked
+ * Allocate and set up resources for the ARM based AAC variants. The
+ * device_interface in the commregion will be allocated and linked
* to the comm region.
*/
@@ -328,6 +324,22 @@ int aac_sa_init(struct aac_dev *dev)
instance = dev->id;
name = dev->name;
+ /*
+ * Fill in the function dispatch table.
+ */
+
+ dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
+ dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt;
+ dev->a_ops.adapter_enable_int = aac_sa_enable_interrupt;
+ dev->a_ops.adapter_notify = aac_sa_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
+ dev->a_ops.adapter_check_health = aac_sa_check_health;
+ dev->a_ops.adapter_restart = aac_sa_restart_adapter;
+ dev->a_ops.adapter_start = aac_sa_start_adapter;
+ dev->a_ops.adapter_intr = aac_sa_intr;
+ dev->a_ops.adapter_deliver = aac_rx_deliver_producer;
+ dev->a_ops.adapter_ioremap = aac_sa_ioremap;
+
if (aac_sa_ioremap(dev, dev->base_size)) {
printk(KERN_WARNING "%s: unable to map adapter.\n", name);
goto error_iounmap;
@@ -362,21 +374,6 @@ int aac_sa_init(struct aac_dev *dev)
}
/*
- * Fill in the function dispatch table.
- */
-
- dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
- dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt;
- dev->a_ops.adapter_enable_int = aac_sa_enable_interrupt;
- dev->a_ops.adapter_notify = aac_sa_notify_adapter;
- dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
- dev->a_ops.adapter_check_health = aac_sa_check_health;
- dev->a_ops.adapter_restart = aac_sa_restart_adapter;
- dev->a_ops.adapter_intr = aac_sa_intr;
- dev->a_ops.adapter_deliver = aac_rx_deliver_producer;
- dev->a_ops.adapter_ioremap = aac_sa_ioremap;
-
- /*
* First clear out all interrupts. Then enable the one's that
* we can handle.
*/
@@ -385,14 +382,14 @@ int aac_sa_init(struct aac_dev *dev)
if(aac_init_adapter(dev) == NULL)
goto error_irq;
+ dev->sync_mode = 0; /* sync. mode not supported */
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
- IRQF_SHARED|IRQF_DISABLED,
- "aacraid", (void *)dev ) < 0) {
+ IRQF_SHARED, "aacraid", (void *)dev) < 0) {
printk(KERN_WARNING "%s%d: Interrupt unavailable.\n",
name, instance);
goto error_iounmap;
}
- dev->dbg_base = dev->scsi_host_ptr->base;
+ dev->dbg_base = dev->base_start;
dev->dbg_base_mapped = dev->base;
dev->dbg_size = dev->base_size;
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index c20494660603..28115ed637e8 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Adaptec AAC series RAID controller driver
* (c) Copyright 2001 Red Hat Inc.
@@ -6,27 +7,13 @@
* Adaptec aacraid device driver for Linux.
*
* Copyright (c) 2000-2010 Adaptec, Inc.
- * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 2010-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com)
+ * 2016-2017 Microsemi Corp. (aacraid@microsemi.com)
*
* Module Name:
* src.c
*
* Abstract: Hardware Device Interface for PMC SRC based controllers
- *
*/
#include <linux/kernel.h>
@@ -37,7 +24,6 @@
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/completion.h>
#include <linux/time.h>
#include <linux/interrupt.h>
@@ -45,66 +31,139 @@
#include "aacraid.h"
+static int aac_src_get_sync_status(struct aac_dev *dev);
+
static irqreturn_t aac_src_intr_message(int irq, void *dev_id)
{
- struct aac_dev *dev = dev_id;
+ struct aac_msix_ctx *ctx;
+ struct aac_dev *dev;
unsigned long bellbits, bellbits_shifted;
- int our_interrupt = 0;
- int isFastResponse;
+ int vector_no;
+ int isFastResponse, mode;
u32 index, handle;
- bellbits = src_readl(dev, MUnit.ODR_R);
- if (bellbits & PmDoorBellResponseSent) {
- bellbits = PmDoorBellResponseSent;
- /* handle async. status */
- our_interrupt = 1;
- index = dev->host_rrq_idx;
- if (dev->host_rrq[index] == 0) {
- u32 old_index = index;
- /* adjust index */
- do {
- index++;
- if (index == dev->scsi_host_ptr->can_queue +
- AAC_NUM_MGT_FIB)
- index = 0;
- if (dev->host_rrq[index] != 0)
- break;
- } while (index != old_index);
- dev->host_rrq_idx = index;
+ ctx = (struct aac_msix_ctx *)dev_id;
+ dev = ctx->dev;
+ vector_no = ctx->vector_no;
+
+ if (dev->msi_enabled) {
+ mode = AAC_INT_MODE_MSI;
+ if (vector_no == 0) {
+ bellbits = src_readl(dev, MUnit.ODR_MSI);
+ if (bellbits & 0x40000)
+ mode |= AAC_INT_MODE_AIF;
+ if (bellbits & 0x1000)
+ mode |= AAC_INT_MODE_SYNC;
+ }
+ } else {
+ mode = AAC_INT_MODE_INTX;
+ bellbits = src_readl(dev, MUnit.ODR_R);
+ if (bellbits & PmDoorBellResponseSent) {
+ bellbits = PmDoorBellResponseSent;
+ src_writel(dev, MUnit.ODR_C, bellbits);
+ src_readl(dev, MUnit.ODR_C);
+ } else {
+ bellbits_shifted = (bellbits >> SRC_ODR_SHIFT);
+ src_writel(dev, MUnit.ODR_C, bellbits);
+ src_readl(dev, MUnit.ODR_C);
+
+ if (bellbits_shifted & DoorBellAifPending)
+ mode |= AAC_INT_MODE_AIF;
+ else if (bellbits_shifted & OUTBOUNDDOORBELL_0)
+ mode |= AAC_INT_MODE_SYNC;
+ }
+ }
+
+ if (mode & AAC_INT_MODE_SYNC) {
+ unsigned long sflags;
+ struct list_head *entry;
+ int send_it = 0;
+ extern int aac_sync_mode;
+
+ if (!aac_sync_mode && !dev->msi_enabled) {
+ src_writel(dev, MUnit.ODR_C, bellbits);
+ src_readl(dev, MUnit.ODR_C);
}
+
+ if (dev->sync_fib) {
+ if (dev->sync_fib->callback)
+ dev->sync_fib->callback(dev->sync_fib->callback_data,
+ dev->sync_fib);
+ spin_lock_irqsave(&dev->sync_fib->event_lock, sflags);
+ if (dev->sync_fib->flags & FIB_CONTEXT_FLAG_WAIT) {
+ dev->management_fib_count--;
+ complete(&dev->sync_fib->event_wait);
+ }
+ spin_unlock_irqrestore(&dev->sync_fib->event_lock,
+ sflags);
+ spin_lock_irqsave(&dev->sync_lock, sflags);
+ if (!list_empty(&dev->sync_fib_list)) {
+ entry = dev->sync_fib_list.next;
+ dev->sync_fib = list_entry(entry,
+ struct fib,
+ fiblink);
+ list_del(entry);
+ send_it = 1;
+ } else {
+ dev->sync_fib = NULL;
+ }
+ spin_unlock_irqrestore(&dev->sync_lock, sflags);
+ if (send_it) {
+ aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB,
+ (u32)dev->sync_fib->hw_fib_pa,
+ 0, 0, 0, 0, 0,
+ NULL, NULL, NULL, NULL, NULL);
+ }
+ }
+ if (!dev->msi_enabled)
+ mode = 0;
+
+ }
+
+ if (mode & AAC_INT_MODE_AIF) {
+ /* handle AIF */
+ if (dev->sa_firmware) {
+ u32 events = src_readl(dev, MUnit.SCR0);
+
+ aac_intr_normal(dev, events, 1, 0, NULL);
+ writel(events, &dev->IndexRegs->Mailbox[0]);
+ src_writel(dev, MUnit.IDR, 1 << 23);
+ } else {
+ if (dev->aif_thread && dev->fsa_dev)
+ aac_intr_normal(dev, 0, 2, 0, NULL);
+ }
+ if (dev->msi_enabled)
+ aac_src_access_devreg(dev, AAC_CLEAR_AIF_BIT);
+ mode = 0;
+ }
+
+ if (mode) {
+ index = dev->host_rrq_idx[vector_no];
+
for (;;) {
isFastResponse = 0;
/* remove toggle bit (31) */
- handle = (dev->host_rrq[index] & 0x7fffffff);
- /* check fast response bit (30) */
+ handle = le32_to_cpu((dev->host_rrq[index])
+ & 0x7fffffff);
+ /* check fast response bits (30, 1) */
if (handle & 0x40000000)
isFastResponse = 1;
handle &= 0x0000ffff;
if (handle == 0)
break;
-
- aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL);
-
+ handle >>= 2;
+ if (dev->msi_enabled && dev->max_msix > 1)
+ atomic_dec(&dev->rrq_outstanding[vector_no]);
+ aac_intr_normal(dev, handle, 0, isFastResponse, NULL);
dev->host_rrq[index++] = 0;
- if (index == dev->scsi_host_ptr->can_queue +
- AAC_NUM_MGT_FIB)
- index = 0;
- dev->host_rrq_idx = index;
- }
- } else {
- bellbits_shifted = (bellbits >> SRC_ODR_SHIFT);
- if (bellbits_shifted & DoorBellAifPending) {
- our_interrupt = 1;
- /* handle AIF */
- aac_intr_normal(dev, 0, 2, 0, NULL);
+ if (index == (vector_no + 1) * dev->vector_cap)
+ index = vector_no * dev->vector_cap;
+ dev->host_rrq_idx[vector_no] = index;
}
+ mode = 0;
}
- if (our_interrupt) {
- src_writel(dev, MUnit.ODR_C, bellbits);
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
+ return IRQ_HANDLED;
}
/**
@@ -124,7 +183,7 @@ static void aac_src_disable_interrupt(struct aac_dev *dev)
static void aac_src_enable_interrupt_message(struct aac_dev *dev)
{
- src_writel(dev, MUnit.OIMR, dev->OIMR = 0xfffffff8);
+ aac_src_access_devreg(dev, AAC_ENABLE_INTERRUPT);
}
/**
@@ -132,7 +191,16 @@ static void aac_src_enable_interrupt_message(struct aac_dev *dev)
* @dev: Adapter
* @command: Command to execute
* @p1: first parameter
- * @ret: adapter status
+ * @p2: second parameter
+ * @p3: third parameter
+ * @p4: forth parameter
+ * @p5: fifth parameter
+ * @p6: sixth parameter
+ * @status: adapter status
+ * @r1: first return value
+ * @r2: second return valu
+ * @r3: third return value
+ * @r4: forth return value
*
* This routine will send a synchronous command to the adapter and wait
* for its completion.
@@ -143,6 +211,7 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
u32 *status, u32 * r1, u32 * r2, u32 * r3, u32 * r4)
{
unsigned long start;
+ unsigned long delay;
int ok;
/*
@@ -160,7 +229,10 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
/*
* Clear the synch command doorbell to start on a clean slate.
*/
- src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
+ if (!dev->msi_enabled)
+ src_writel(dev,
+ MUnit.ODR_C,
+ OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
/*
* Disable doorbell interrupts
@@ -178,56 +250,79 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
*/
src_writel(dev, MUnit.IDR, INBOUNDDOORBELL_0 << SRC_IDR_SHIFT);
- ok = 0;
- start = jiffies;
-
- /*
- * Wait up to 30 seconds
- */
- while (time_before(jiffies, start+30*HZ)) {
- /* Delay 5 microseconds to let Mon960 get info. */
- udelay(5);
-
- /* Mon960 will set doorbell0 bit
- * when it has completed the command
+ if ((!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) &&
+ !dev->in_soft_reset) {
+ ok = 0;
+ start = jiffies;
+
+ if (command == IOP_RESET_ALWAYS) {
+ /* Wait up to 10 sec */
+ delay = 10*HZ;
+ } else {
+ /* Wait up to 5 minutes */
+ delay = 300*HZ;
+ }
+ while (time_before(jiffies, start+delay)) {
+ udelay(5); /* Delay 5 microseconds to let Mon960 get info. */
+ /*
+ * Mon960 will set doorbell0 bit when it has completed the command.
+ */
+ if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) {
+ /*
+ * Clear the doorbell.
+ */
+ if (dev->msi_enabled)
+ aac_src_access_devreg(dev,
+ AAC_CLEAR_SYNC_BIT);
+ else
+ src_writel(dev,
+ MUnit.ODR_C,
+ OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
+ ok = 1;
+ break;
+ }
+ /*
+ * Yield the processor in case we are slow
+ */
+ msleep(1);
+ }
+ if (unlikely(ok != 1)) {
+ /*
+ * Restore interrupt mask even though we timed out
+ */
+ aac_adapter_enable_int(dev);
+ return -ETIMEDOUT;
+ }
+ /*
+ * Pull the synch status from Mailbox 0.
*/
- if ((src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT) & OUTBOUNDDOORBELL_0) {
- /* Clear the doorbell */
+ if (status)
+ *status = readl(&dev->IndexRegs->Mailbox[0]);
+ if (r1)
+ *r1 = readl(&dev->IndexRegs->Mailbox[1]);
+ if (r2)
+ *r2 = readl(&dev->IndexRegs->Mailbox[2]);
+ if (r3)
+ *r3 = readl(&dev->IndexRegs->Mailbox[3]);
+ if (r4)
+ *r4 = readl(&dev->IndexRegs->Mailbox[4]);
+ if (command == GET_COMM_PREFERRED_SETTINGS)
+ dev->max_msix =
+ readl(&dev->IndexRegs->Mailbox[5]) & 0xFFFF;
+ /*
+ * Clear the synch command doorbell.
+ */
+ if (!dev->msi_enabled)
src_writel(dev,
MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
- ok = 1;
- break;
- }
-
- /* Yield the processor in case we are slow */
- msleep(1);
- }
- if (unlikely(ok != 1)) {
- /* Restore interrupt mask even though we timed out */
- aac_adapter_enable_int(dev);
- return -ETIMEDOUT;
}
- /* Pull the synch status from Mailbox 0 */
- if (status)
- *status = readl(&dev->IndexRegs->Mailbox[0]);
- if (r1)
- *r1 = readl(&dev->IndexRegs->Mailbox[1]);
- if (r2)
- *r2 = readl(&dev->IndexRegs->Mailbox[2]);
- if (r3)
- *r3 = readl(&dev->IndexRegs->Mailbox[3]);
- if (r4)
- *r4 = readl(&dev->IndexRegs->Mailbox[4]);
-
- /* Clear the synch command doorbell */
- src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
-
- /* Restore interrupt mask */
+ /*
+ * Restore interrupt mask
+ */
aac_adapter_enable_int(dev);
return 0;
-
}
/**
@@ -296,14 +391,36 @@ static void aac_src_notify_adapter(struct aac_dev *dev, u32 event)
static void aac_src_start_adapter(struct aac_dev *dev)
{
- struct aac_init *init;
+ union aac_init *init;
+ int i;
+
+ /* reset host_rrq_idx first */
+ for (i = 0; i < dev->max_msix; i++) {
+ dev->host_rrq_idx[i] = i * dev->vector_cap;
+ atomic_set(&dev->rrq_outstanding[i], 0);
+ }
+ atomic_set(&dev->msix_counter, 0);
+ dev->fibs_pushed_no = 0;
init = dev->init;
- init->HostElapsedSeconds = cpu_to_le32(get_seconds());
+ if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) {
+ init->r8.host_elapsed_seconds =
+ cpu_to_le32(ktime_get_real_seconds());
+ src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,
+ lower_32_bits(dev->init_pa),
+ upper_32_bits(dev->init_pa),
+ sizeof(struct _r8) +
+ AAC_MAX_HRRQ * sizeof(struct _rrq),
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
+ } else {
+ init->r7.host_elapsed_seconds =
+ cpu_to_le32(ktime_get_real_seconds());
+ // We can only use a 32 bit address here
+ src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,
+ (u32)(ulong)dev->init_pa, 0, 0, 0, 0, 0,
+ NULL, NULL, NULL, NULL, NULL);
+ }
- /* We can only use a 32 bit address here */
- src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa,
- 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
}
/**
@@ -318,16 +435,23 @@ static int aac_src_check_health(struct aac_dev *dev)
u32 status = src_readl(dev, MUnit.OMR);
/*
+ * Check to see if the board panic'd.
+ */
+ if (unlikely(status & KERNEL_PANIC))
+ goto err_blink;
+
+ /*
* Check to see if the board failed any self tests.
*/
if (unlikely(status & SELF_TEST_FAILED))
- return -1;
+ goto err_out;
/*
- * Check to see if the board panic'd.
+ * Check to see if the board failed any self tests.
*/
- if (unlikely(status & KERNEL_PANIC))
- return (status >> 16) & 0xFF;
+ if (unlikely(status & MONITOR_PANIC))
+ goto err_out;
+
/*
* Wait for the adapter to be up and running.
*/
@@ -337,6 +461,17 @@ static int aac_src_check_health(struct aac_dev *dev)
* Everything is OK
*/
return 0;
+
+err_out:
+ return -1;
+
+err_blink:
+ return (status >> 16) & 0xFF;
+}
+
+static inline u32 aac_get_vector(struct aac_dev *dev)
+{
+ return atomic_inc_return(&dev->msix_counter)%dev->max_msix;
}
/**
@@ -349,47 +484,144 @@ static int aac_src_deliver_message(struct fib *fib)
{
struct aac_dev *dev = fib->dev;
struct aac_queue *q = &dev->queues->queue[AdapNormCmdQueue];
- unsigned long qflags;
u32 fibsize;
- u64 address;
+ dma_addr_t address;
struct aac_fib_xporthdr *pFibX;
+ int native_hba;
+#if !defined(writeq)
+ unsigned long flags;
+#endif
+
+ u16 vector_no;
+
+ atomic_inc(&q->numpending);
+
+ native_hba = (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) ? 1 : 0;
+
+
+ if (dev->msi_enabled && dev->max_msix > 1 &&
+ (native_hba || fib->hw_fib_va->header.Command != AifRequest)) {
+
+ if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE3)
+ && dev->sa_firmware)
+ vector_no = aac_get_vector(dev);
+ else
+ vector_no = fib->vector_no;
+
+ if (native_hba) {
+ if (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF) {
+ struct aac_hba_tm_req *tm_req;
+
+ tm_req = (struct aac_hba_tm_req *)
+ fib->hw_fib_va;
+ if (tm_req->iu_type ==
+ HBA_IU_TYPE_SCSI_TM_REQ) {
+ ((struct aac_hba_tm_req *)
+ fib->hw_fib_va)->reply_qid
+ = vector_no;
+ ((struct aac_hba_tm_req *)
+ fib->hw_fib_va)->request_id
+ += (vector_no << 16);
+ } else {
+ ((struct aac_hba_reset_req *)
+ fib->hw_fib_va)->reply_qid
+ = vector_no;
+ ((struct aac_hba_reset_req *)
+ fib->hw_fib_va)->request_id
+ += (vector_no << 16);
+ }
+ } else {
+ ((struct aac_hba_cmd_req *)
+ fib->hw_fib_va)->reply_qid
+ = vector_no;
+ ((struct aac_hba_cmd_req *)
+ fib->hw_fib_va)->request_id
+ += (vector_no << 16);
+ }
+ } else {
+ fib->hw_fib_va->header.Handle += (vector_no << 16);
+ }
+ } else {
+ vector_no = 0;
+ }
- spin_lock_irqsave(q->lock, qflags);
- q->numpending++;
- spin_unlock_irqrestore(q->lock, qflags);
-
- /* Calculate the amount to the fibsize bits */
- fibsize = (sizeof(struct aac_fib_xporthdr) +
- fib->hw_fib_va->header.Size + 127) / 128 - 1;
- if (fibsize > (ALIGN32 - 1))
- fibsize = ALIGN32 - 1;
-
- /* Fill XPORT header */
- pFibX = (struct aac_fib_xporthdr *)
- ((unsigned char *)fib->hw_fib_va -
- sizeof(struct aac_fib_xporthdr));
- pFibX->Handle = fib->hw_fib_va->header.SenderData + 1;
- pFibX->HostAddress = fib->hw_fib_pa;
- pFibX->Size = fib->hw_fib_va->header.Size;
- address = fib->hw_fib_pa - (u64)sizeof(struct aac_fib_xporthdr);
-
- src_writel(dev, MUnit.IQ_H, (u32)(address >> 32));
- src_writel(dev, MUnit.IQ_L, (u32)(address & 0xffffffff) + fibsize);
+ atomic_inc(&dev->rrq_outstanding[vector_no]);
+
+ if (native_hba) {
+ address = fib->hw_fib_pa;
+ fibsize = (fib->hbacmd_size + 127) / 128 - 1;
+ if (fibsize > 31)
+ fibsize = 31;
+ address |= fibsize;
+#if defined(writeq)
+ src_writeq(dev, MUnit.IQN_L, (u64)address);
+#else
+ spin_lock_irqsave(&fib->dev->iq_lock, flags);
+ src_writel(dev, MUnit.IQN_H,
+ upper_32_bits(address) & 0xffffffff);
+ src_writel(dev, MUnit.IQN_L, address & 0xffffffff);
+ spin_unlock_irqrestore(&fib->dev->iq_lock, flags);
+#endif
+ } else {
+ if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2 ||
+ dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) {
+ /* Calculate the amount to the fibsize bits */
+ fibsize = (le16_to_cpu(fib->hw_fib_va->header.Size)
+ + 127) / 128 - 1;
+ /* New FIB header, 32-bit */
+ address = fib->hw_fib_pa;
+ fib->hw_fib_va->header.StructType = FIB_MAGIC2;
+ fib->hw_fib_va->header.SenderFibAddress =
+ cpu_to_le32((u32)address);
+ fib->hw_fib_va->header.u.TimeStamp = 0;
+ WARN_ON(upper_32_bits(address) != 0L);
+ } else {
+ /* Calculate the amount to the fibsize bits */
+ fibsize = (sizeof(struct aac_fib_xporthdr) +
+ le16_to_cpu(fib->hw_fib_va->header.Size)
+ + 127) / 128 - 1;
+ /* Fill XPORT header */
+ pFibX = (struct aac_fib_xporthdr *)
+ ((unsigned char *)fib->hw_fib_va -
+ sizeof(struct aac_fib_xporthdr));
+ pFibX->Handle = fib->hw_fib_va->header.Handle;
+ pFibX->HostAddress =
+ cpu_to_le64((u64)fib->hw_fib_pa);
+ pFibX->Size = cpu_to_le32(
+ le16_to_cpu(fib->hw_fib_va->header.Size));
+ address = fib->hw_fib_pa -
+ (u64)sizeof(struct aac_fib_xporthdr);
+ }
+ if (fibsize > 31)
+ fibsize = 31;
+ address |= fibsize;
+
+#if defined(writeq)
+ src_writeq(dev, MUnit.IQ_L, (u64)address);
+#else
+ spin_lock_irqsave(&fib->dev->iq_lock, flags);
+ src_writel(dev, MUnit.IQ_H,
+ upper_32_bits(address) & 0xffffffff);
+ src_writel(dev, MUnit.IQ_L, address & 0xffffffff);
+ spin_unlock_irqrestore(&fib->dev->iq_lock, flags);
+#endif
+ }
return 0;
}
/**
* aac_src_ioremap
+ * @dev: device ioremap
* @size: mapping resize request
*
*/
static int aac_src_ioremap(struct aac_dev *dev, u32 size)
{
if (!size) {
+ iounmap(dev->regs.src.bar1);
+ dev->regs.src.bar1 = NULL;
iounmap(dev->regs.src.bar0);
- dev->regs.src.bar0 = NULL;
- iounmap(dev->base);
- dev->base = NULL;
+ dev->base = dev->regs.src.bar0 = NULL;
return 0;
}
dev->regs.src.bar1 = ioremap(pci_resource_start(dev->pdev, 2),
@@ -397,44 +629,231 @@ static int aac_src_ioremap(struct aac_dev *dev, u32 size)
dev->base = NULL;
if (dev->regs.src.bar1 == NULL)
return -1;
- dev->base = dev->regs.src.bar0 = ioremap(dev->scsi_host_ptr->base,
- size);
+ dev->base = dev->regs.src.bar0 = ioremap(dev->base_start, size);
+ if (dev->base == NULL) {
+ iounmap(dev->regs.src.bar1);
+ dev->regs.src.bar1 = NULL;
+ return -1;
+ }
+ dev->IndexRegs = &((struct src_registers __iomem *)
+ dev->base)->u.tupelo.IndexRegs;
+ return 0;
+}
+
+/**
+ * aac_srcv_ioremap
+ * @dev: device ioremap
+ * @size: mapping resize request
+ *
+ */
+static int aac_srcv_ioremap(struct aac_dev *dev, u32 size)
+{
+ if (!size) {
+ iounmap(dev->regs.src.bar0);
+ dev->base = dev->regs.src.bar0 = NULL;
+ return 0;
+ }
+
+ dev->regs.src.bar1 =
+ ioremap(pci_resource_start(dev->pdev, 2), AAC_MIN_SRCV_BAR1_SIZE);
+ dev->base = NULL;
+ if (dev->regs.src.bar1 == NULL)
+ return -1;
+ dev->base = dev->regs.src.bar0 = ioremap(dev->base_start, size);
if (dev->base == NULL) {
iounmap(dev->regs.src.bar1);
dev->regs.src.bar1 = NULL;
return -1;
}
dev->IndexRegs = &((struct src_registers __iomem *)
- dev->base)->IndexRegs;
+ dev->base)->u.denali.IndexRegs;
return 0;
}
-static int aac_src_restart_adapter(struct aac_dev *dev, int bled)
+void aac_set_intx_mode(struct aac_dev *dev)
+{
+ if (dev->msi_enabled) {
+ aac_src_access_devreg(dev, AAC_ENABLE_INTX);
+ dev->msi_enabled = 0;
+ msleep(5000); /* Delay 5 seconds */
+ }
+}
+
+static void aac_clear_omr(struct aac_dev *dev)
+{
+ u32 omr_value = 0;
+
+ omr_value = src_readl(dev, MUnit.OMR);
+
+ /*
+ * Check for PCI Errors or Kernel Panic
+ */
+ if ((omr_value == INVALID_OMR) || (omr_value & KERNEL_PANIC))
+ omr_value = 0;
+
+ /*
+ * Preserve MSIX Value if any
+ */
+ src_writel(dev, MUnit.OMR, omr_value & AAC_INT_MODE_MSIX);
+ src_readl(dev, MUnit.OMR);
+}
+
+static void aac_dump_fw_fib_iop_reset(struct aac_dev *dev)
+{
+ __le32 supported_options3;
+
+ if (!aac_fib_dump)
+ return;
+
+ supported_options3 = dev->supplement_adapter_info.supported_options3;
+ if (!(supported_options3 & AAC_OPTION_SUPPORTED3_IOP_RESET_FIB_DUMP))
+ return;
+
+ aac_adapter_sync_cmd(dev, IOP_RESET_FW_FIB_DUMP,
+ 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
+}
+
+static bool aac_is_ctrl_up_and_running(struct aac_dev *dev)
{
- u32 var, reset_mask;
-
- if (bled >= 0) {
- if (bled)
- printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n",
- dev->name, dev->id, bled);
- bled = aac_adapter_sync_cmd(dev, IOP_RESET_ALWAYS,
- 0, 0, 0, 0, 0, 0, &var, &reset_mask, NULL, NULL, NULL);
- if (bled || (var != 0x00000001))
- bled = -EINVAL;
- if (dev->supplement_adapter_info.SupportedOptions2 &
- AAC_OPTION_DOORBELL_RESET) {
- src_writel(dev, MUnit.IDR, reset_mask);
- msleep(5000); /* Delay 5 seconds */
+ bool ctrl_up = true;
+ unsigned long status, start;
+ bool is_up = false;
+
+ start = jiffies;
+ do {
+ schedule();
+ status = src_readl(dev, MUnit.OMR);
+
+ if (status == 0xffffffff)
+ status = 0;
+
+ if (status & KERNEL_BOOTING) {
+ start = jiffies;
+ continue;
+ }
+
+ if (time_after(jiffies, start+HZ*SOFT_RESET_TIME)) {
+ ctrl_up = false;
+ break;
+ }
+
+ is_up = status & KERNEL_UP_AND_RUNNING;
+
+ } while (!is_up);
+
+ return ctrl_up;
+}
+
+static void aac_src_drop_io(struct aac_dev *dev)
+{
+ if (!dev->soft_reset_support)
+ return;
+
+ aac_adapter_sync_cmd(dev, DROP_IO,
+ 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);
+}
+
+static void aac_notify_fw_of_iop_reset(struct aac_dev *dev)
+{
+ aac_adapter_sync_cmd(dev, IOP_RESET_ALWAYS, 0, 0, 0, 0, 0, 0, NULL,
+ NULL, NULL, NULL, NULL);
+ aac_src_drop_io(dev);
+}
+
+static void aac_send_iop_reset(struct aac_dev *dev)
+{
+ aac_dump_fw_fib_iop_reset(dev);
+
+ aac_notify_fw_of_iop_reset(dev);
+
+ aac_set_intx_mode(dev);
+
+ aac_clear_omr(dev);
+
+ src_writel(dev, MUnit.IDR, IOP_SRC_RESET_MASK);
+
+ msleep(5000);
+}
+
+static void aac_send_hardware_soft_reset(struct aac_dev *dev)
+{
+ u_int32_t val;
+
+ aac_clear_omr(dev);
+ val = readl(((char *)(dev->base) + IBW_SWR_OFFSET));
+ val |= 0x01;
+ writel(val, ((char *)(dev->base) + IBW_SWR_OFFSET));
+ msleep_interruptible(20000);
+}
+
+static int aac_src_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
+{
+ bool is_ctrl_up;
+ int ret = 0;
+
+ if (bled < 0)
+ goto invalid_out;
+
+ if (bled)
+ dev_err(&dev->pdev->dev, "adapter kernel panic'd %x.\n", bled);
+
+ /*
+ * When there is a BlinkLED, IOP_RESET has not effect
+ */
+ if (bled >= 2 && dev->sa_firmware && reset_type & HW_IOP_RESET)
+ reset_type &= ~HW_IOP_RESET;
+
+ dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
+
+ dev_err(&dev->pdev->dev, "Controller reset type is %d\n", reset_type);
+
+ if (reset_type & HW_IOP_RESET) {
+ dev_info(&dev->pdev->dev, "Issuing IOP reset\n");
+ aac_send_iop_reset(dev);
+
+ /*
+ * Creates a delay or wait till up and running comes thru
+ */
+ is_ctrl_up = aac_is_ctrl_up_and_running(dev);
+ if (!is_ctrl_up)
+ dev_err(&dev->pdev->dev, "IOP reset failed\n");
+ else {
+ dev_info(&dev->pdev->dev, "IOP reset succeeded\n");
+ goto set_startup;
}
}
- if (src_readl(dev, MUnit.OMR) & KERNEL_PANIC)
- return -ENODEV;
+ if (!dev->sa_firmware) {
+ dev_err(&dev->pdev->dev, "ARC Reset attempt failed\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (reset_type & HW_SOFT_RESET) {
+ dev_info(&dev->pdev->dev, "Issuing SOFT reset\n");
+ aac_send_hardware_soft_reset(dev);
+ dev->msi_enabled = 0;
+
+ is_ctrl_up = aac_is_ctrl_up_and_running(dev);
+ if (!is_ctrl_up) {
+ dev_err(&dev->pdev->dev, "SOFT reset failed\n");
+ ret = -ENODEV;
+ goto out;
+ } else
+ dev_info(&dev->pdev->dev, "SOFT reset succeeded\n");
+ }
+set_startup:
if (startup_timeout < 300)
startup_timeout = 300;
- return 0;
+out:
+ return ret;
+
+invalid_out:
+ if (src_readl(dev, MUnit.OMR) & KERNEL_PANIC)
+ ret = -ENODEV;
+goto out;
}
/**
@@ -442,11 +861,10 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled)
* @dev: Adapter
* @comm: communications method
*/
-int aac_src_select_comm(struct aac_dev *dev, int comm)
+static int aac_src_select_comm(struct aac_dev *dev, int comm)
{
switch (comm) {
case AAC_COMM_MESSAGE:
- dev->a_ops.adapter_enable_int = aac_src_enable_interrupt_message;
dev->a_ops.adapter_intr = aac_src_intr_message;
dev->a_ops.adapter_deliver = aac_src_deliver_message;
break;
@@ -482,15 +900,20 @@ int aac_src_init(struct aac_dev *dev)
/* Failure to reset here is an option ... */
dev->a_ops.adapter_sync_cmd = src_sync_cmd;
dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
- if ((aac_reset_devices || reset_devices) &&
- !aac_src_restart_adapter(dev, 0))
- ++restart;
+
+ if (dev->init_reset) {
+ dev->init_reset = false;
+ if (!aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET))
+ ++restart;
+ }
+
/*
* Check to see if the board panic'd while booting.
*/
status = src_readl(dev, MUnit.OMR);
if (status & KERNEL_PANIC) {
- if (aac_src_restart_adapter(dev, aac_src_check_health(dev)))
+ if (aac_src_restart_adapter(dev,
+ aac_src_check_health(dev), IOP_HWSOFT_RESET))
goto error_iounmap;
++restart;
}
@@ -531,7 +954,7 @@ int aac_src_init(struct aac_dev *dev)
? (startup_timeout - 60)
: (startup_timeout / 2))))) {
if (likely(!aac_src_restart_adapter(dev,
- aac_src_check_health(dev))))
+ aac_src_check_health(dev), IOP_HWSOFT_RESET)))
start = jiffies;
++restart;
}
@@ -544,10 +967,12 @@ int aac_src_init(struct aac_dev *dev)
*/
dev->a_ops.adapter_interrupt = aac_src_interrupt_adapter;
dev->a_ops.adapter_disable_int = aac_src_disable_interrupt;
+ dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
dev->a_ops.adapter_notify = aac_src_notify_adapter;
dev->a_ops.adapter_sync_cmd = src_sync_cmd;
dev->a_ops.adapter_check_health = aac_src_check_health;
dev->a_ops.adapter_restart = aac_src_restart_adapter;
+ dev->a_ops.adapter_start = aac_src_start_adapter;
/*
* First clear out all interrupts. Then enable the one's that we
@@ -563,10 +988,13 @@ int aac_src_init(struct aac_dev *dev)
if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE1)
goto error_iounmap;
- dev->msi = aac_msi && !pci_enable_msi(dev->pdev);
+ dev->msi = !pci_enable_msi(dev->pdev);
+
+ dev->aac_msix[0].vector_no = 0;
+ dev->aac_msix[0].dev = dev;
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
- IRQF_SHARED|IRQF_DISABLED, "aacraid", dev) < 0) {
+ IRQF_SHARED, "aacraid", &(dev->aac_msix[0])) < 0) {
if (dev->msi)
pci_disable_msi(dev->pdev);
@@ -578,17 +1006,431 @@ int aac_src_init(struct aac_dev *dev)
dev->dbg_base = pci_resource_start(dev->pdev, 2);
dev->dbg_base_mapped = dev->regs.src.bar1;
dev->dbg_size = AAC_MIN_SRC_BAR1_SIZE;
+ dev->a_ops.adapter_enable_int = aac_src_enable_interrupt_message;
aac_adapter_enable_int(dev);
+
+ if (!dev->sync_mode) {
+ /*
+ * Tell the adapter that all is configured, and it can
+ * start accepting requests
+ */
+ aac_src_start_adapter(dev);
+ }
+ return 0;
+
+error_iounmap:
+
+ return -1;
+}
+
+static int aac_src_wait_sync(struct aac_dev *dev, int *status)
+{
+ unsigned long start = jiffies;
+ unsigned long usecs = 0;
+ int delay = 5 * HZ;
+ int rc = 1;
+
+ while (time_before(jiffies, start+delay)) {
+ /*
+ * Delay 5 microseconds to let Mon960 get info.
+ */
+ udelay(5);
+
+ /*
+ * Mon960 will set doorbell0 bit when it has completed the
+ * command.
+ */
+ if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) {
+ /*
+ * Clear: the doorbell.
+ */
+ if (dev->msi_enabled)
+ aac_src_access_devreg(dev, AAC_CLEAR_SYNC_BIT);
+ else
+ src_writel(dev, MUnit.ODR_C,
+ OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
+ rc = 0;
+
+ break;
+ }
+
+ /*
+ * Yield the processor in case we are slow
+ */
+ usecs = 1 * USEC_PER_MSEC;
+ usleep_range(usecs, usecs + 50);
+ }
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ if (status && !rc) {
+ status[0] = readl(&dev->IndexRegs->Mailbox[0]);
+ status[1] = readl(&dev->IndexRegs->Mailbox[1]);
+ status[2] = readl(&dev->IndexRegs->Mailbox[2]);
+ status[3] = readl(&dev->IndexRegs->Mailbox[3]);
+ status[4] = readl(&dev->IndexRegs->Mailbox[4]);
+ }
+
+ return rc;
+}
+
+/**
+ * aac_src_soft_reset - perform soft reset to speed up
+ * access
+ *
+ * Assumptions: That the controller is in a state where we can
+ * bring it back to life with an init struct. We can only use
+ * fast sync commands, as the timeout is 5 seconds.
+ *
+ * @dev: device to configure
+ *
+ */
+
+static int aac_src_soft_reset(struct aac_dev *dev)
+{
+ u32 status_omr = src_readl(dev, MUnit.OMR);
+ u32 status[5];
+ int rc = 1;
+ int state = 0;
+ char *state_str[7] = {
+ "GET_ADAPTER_PROPERTIES Failed",
+ "GET_ADAPTER_PROPERTIES timeout",
+ "SOFT_RESET not supported",
+ "DROP_IO Failed",
+ "DROP_IO timeout",
+ "Check Health failed"
+ };
+
+ if (status_omr == INVALID_OMR)
+ return 1; // pcie hosed
+
+ if (!(status_omr & KERNEL_UP_AND_RUNNING))
+ return 1; // not up and running
+
/*
- * Tell the adapter that all is configured, and it can
- * start accepting requests
+ * We go into soft reset mode to allow us to handle response
*/
- aac_src_start_adapter(dev);
+ dev->in_soft_reset = 1;
+ dev->msi_enabled = status_omr & AAC_INT_MODE_MSIX;
+
+ /* Get adapter properties */
+ rc = aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES, 0, 0, 0,
+ 0, 0, 0, status+0, status+1, status+2, status+3, status+4);
+ if (rc)
+ goto out;
+
+ state++;
+ if (aac_src_wait_sync(dev, status)) {
+ rc = 1;
+ goto out;
+ }
+ state++;
+ if (!(status[1] & le32_to_cpu(AAC_OPT_EXTENDED) &&
+ (status[4] & le32_to_cpu(AAC_EXTOPT_SOFT_RESET)))) {
+ rc = 2;
+ goto out;
+ }
+
+ if ((status[1] & le32_to_cpu(AAC_OPT_EXTENDED)) &&
+ (status[4] & le32_to_cpu(AAC_EXTOPT_SA_FIRMWARE)))
+ dev->sa_firmware = 1;
+
+ state++;
+ rc = aac_adapter_sync_cmd(dev, DROP_IO, 0, 0, 0, 0, 0, 0,
+ status+0, status+1, status+2, status+3, status+4);
+
+ if (rc)
+ goto out;
+
+ state++;
+ if (aac_src_wait_sync(dev, status)) {
+ rc = 3;
+ goto out;
+ }
+
+ if (status[1])
+ dev_err(&dev->pdev->dev, "%s: %d outstanding I/O pending\n",
+ __func__, status[1]);
+
+ state++;
+ rc = aac_src_check_health(dev);
+
+out:
+ dev->in_soft_reset = 0;
+ dev->msi_enabled = 0;
+ if (rc)
+ dev_err(&dev->pdev->dev, "%s: %s status = %d", __func__,
+ state_str[state], rc);
+
+ return rc;
+}
+/**
+ * aac_srcv_init - initialize an SRCv card
+ * @dev: device to configure
+ *
+ */
+
+int aac_srcv_init(struct aac_dev *dev)
+{
+ unsigned long start;
+ unsigned long status;
+ int restart = 0;
+ int instance = dev->id;
+ const char *name = dev->name;
+
+ dev->a_ops.adapter_ioremap = aac_srcv_ioremap;
+ dev->a_ops.adapter_comm = aac_src_select_comm;
+
+ dev->base_size = AAC_MIN_SRCV_BAR0_SIZE;
+ if (aac_adapter_ioremap(dev, dev->base_size)) {
+ printk(KERN_WARNING "%s: unable to map adapter.\n", name);
+ goto error_iounmap;
+ }
+
+ /* Failure to reset here is an option ... */
+ dev->a_ops.adapter_sync_cmd = src_sync_cmd;
+ dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
+
+ if (dev->init_reset) {
+ dev->init_reset = false;
+ if (aac_src_soft_reset(dev)) {
+ aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET);
+ ++restart;
+ }
+ }
+
+ /*
+ * Check to see if flash update is running.
+ * Wait for the adapter to be up and running. Wait up to 5 minutes
+ */
+ status = src_readl(dev, MUnit.OMR);
+ if (status & FLASH_UPD_PENDING) {
+ start = jiffies;
+ do {
+ status = src_readl(dev, MUnit.OMR);
+ if (time_after(jiffies, start+HZ*FWUPD_TIMEOUT)) {
+ printk(KERN_ERR "%s%d: adapter flash update failed.\n",
+ dev->name, instance);
+ goto error_iounmap;
+ }
+ } while (!(status & FLASH_UPD_SUCCESS) &&
+ !(status & FLASH_UPD_FAILED));
+ /* Delay 10 seconds.
+ * Because right now FW is doing a soft reset,
+ * do not read scratch pad register at this time
+ */
+ ssleep(10);
+ }
+ /*
+ * Check to see if the board panic'd while booting.
+ */
+ status = src_readl(dev, MUnit.OMR);
+ if (status & KERNEL_PANIC) {
+ if (aac_src_restart_adapter(dev,
+ aac_src_check_health(dev), IOP_HWSOFT_RESET))
+ goto error_iounmap;
+ ++restart;
+ }
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ status = src_readl(dev, MUnit.OMR);
+ if (status & SELF_TEST_FAILED) {
+ printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the monitor panic'd while booting.
+ */
+ if (status & MONITOR_PANIC) {
+ printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+
+ start = jiffies;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes
+ */
+ do {
+ status = src_readl(dev, MUnit.OMR);
+ if (status == INVALID_OMR)
+ status = 0;
+
+ if ((restart &&
+ (status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC))) ||
+ time_after(jiffies, start+HZ*startup_timeout)) {
+ printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n",
+ dev->name, instance, status);
+ goto error_iounmap;
+ }
+ if (!restart &&
+ ((status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC)) ||
+ time_after(jiffies, start + HZ *
+ ((startup_timeout > 60)
+ ? (startup_timeout - 60)
+ : (startup_timeout / 2))))) {
+ if (likely(!aac_src_restart_adapter(dev,
+ aac_src_check_health(dev), IOP_HWSOFT_RESET)))
+ start = jiffies;
+ ++restart;
+ }
+ msleep(1);
+ } while (!(status & KERNEL_UP_AND_RUNNING));
+
+ if (restart && aac_commit)
+ aac_commit = 1;
+ /*
+ * Fill in the common function dispatch table.
+ */
+ dev->a_ops.adapter_interrupt = aac_src_interrupt_adapter;
+ dev->a_ops.adapter_disable_int = aac_src_disable_interrupt;
+ dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
+ dev->a_ops.adapter_notify = aac_src_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = src_sync_cmd;
+ dev->a_ops.adapter_check_health = aac_src_check_health;
+ dev->a_ops.adapter_restart = aac_src_restart_adapter;
+ dev->a_ops.adapter_start = aac_src_start_adapter;
+
+ /*
+ * First clear out all interrupts. Then enable the one's that we
+ * can handle.
+ */
+ aac_adapter_comm(dev, AAC_COMM_MESSAGE);
+ aac_adapter_disable_int(dev);
+ src_writel(dev, MUnit.ODR_C, 0xffffffff);
+ aac_adapter_enable_int(dev);
+
+ if (aac_init_adapter(dev) == NULL)
+ goto error_iounmap;
+ if ((dev->comm_interface != AAC_COMM_MESSAGE_TYPE2) &&
+ (dev->comm_interface != AAC_COMM_MESSAGE_TYPE3))
+ goto error_iounmap;
+ if (dev->msi_enabled)
+ aac_src_access_devreg(dev, AAC_ENABLE_MSIX);
+
+ if (aac_acquire_irq(dev))
+ goto error_iounmap;
+
+ dev->dbg_base = pci_resource_start(dev->pdev, 2);
+ dev->dbg_base_mapped = dev->regs.src.bar1;
+ dev->dbg_size = AAC_MIN_SRCV_BAR1_SIZE;
+ dev->a_ops.adapter_enable_int = aac_src_enable_interrupt_message;
+
+ aac_adapter_enable_int(dev);
+
+ if (!dev->sync_mode) {
+ /*
+ * Tell the adapter that all is configured, and it can
+ * start accepting requests
+ */
+ aac_src_start_adapter(dev);
+ }
return 0;
error_iounmap:
return -1;
}
+
+void aac_src_access_devreg(struct aac_dev *dev, int mode)
+{
+ u_int32_t val;
+
+ switch (mode) {
+ case AAC_ENABLE_INTERRUPT:
+ src_writel(dev,
+ MUnit.OIMR,
+ dev->OIMR = (dev->msi_enabled ?
+ AAC_INT_ENABLE_TYPE1_MSIX :
+ AAC_INT_ENABLE_TYPE1_INTX));
+ break;
+
+ case AAC_DISABLE_INTERRUPT:
+ src_writel(dev,
+ MUnit.OIMR,
+ dev->OIMR = AAC_INT_DISABLE_ALL);
+ break;
+
+ case AAC_ENABLE_MSIX:
+ /* set bit 6 */
+ val = src_readl(dev, MUnit.IDR);
+ val |= 0x40;
+ src_writel(dev, MUnit.IDR, val);
+ src_readl(dev, MUnit.IDR);
+ /* unmask int. */
+ val = PMC_ALL_INTERRUPT_BITS;
+ src_writel(dev, MUnit.IOAR, val);
+ val = src_readl(dev, MUnit.OIMR);
+ src_writel(dev,
+ MUnit.OIMR,
+ val & (~(PMC_GLOBAL_INT_BIT2 | PMC_GLOBAL_INT_BIT0)));
+ break;
+
+ case AAC_DISABLE_MSIX:
+ /* reset bit 6 */
+ val = src_readl(dev, MUnit.IDR);
+ val &= ~0x40;
+ src_writel(dev, MUnit.IDR, val);
+ src_readl(dev, MUnit.IDR);
+ break;
+
+ case AAC_CLEAR_AIF_BIT:
+ /* set bit 5 */
+ val = src_readl(dev, MUnit.IDR);
+ val |= 0x20;
+ src_writel(dev, MUnit.IDR, val);
+ src_readl(dev, MUnit.IDR);
+ break;
+
+ case AAC_CLEAR_SYNC_BIT:
+ /* set bit 4 */
+ val = src_readl(dev, MUnit.IDR);
+ val |= 0x10;
+ src_writel(dev, MUnit.IDR, val);
+ src_readl(dev, MUnit.IDR);
+ break;
+
+ case AAC_ENABLE_INTX:
+ /* set bit 7 */
+ val = src_readl(dev, MUnit.IDR);
+ val |= 0x80;
+ src_writel(dev, MUnit.IDR, val);
+ src_readl(dev, MUnit.IDR);
+ /* unmask int. */
+ val = PMC_ALL_INTERRUPT_BITS;
+ src_writel(dev, MUnit.IOAR, val);
+ src_readl(dev, MUnit.IOAR);
+ val = src_readl(dev, MUnit.OIMR);
+ src_writel(dev, MUnit.OIMR,
+ val & (~(PMC_GLOBAL_INT_BIT2)));
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int aac_src_get_sync_status(struct aac_dev *dev)
+{
+ int msix_val = 0;
+ int legacy_val = 0;
+
+ msix_val = src_readl(dev, MUnit.ODR_MSI) & SRC_MSI_READ_MASK ? 1 : 0;
+
+ if (!dev->msi_enabled) {
+ /*
+ * if Legacy int status indicates cmd is not complete
+ * sample MSIx register to see if it indiactes cmd complete,
+ * if yes set the controller in MSIx mode and consider cmd
+ * completed
+ */
+ legacy_val = src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT;
+ if (!(legacy_val & 1) && msix_val)
+ dev->msi_enabled = 1;
+ return legacy_val;
+ }
+
+ return msix_val;
+}
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index bfd618a69499..06223b5ee6da 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -1,18 +1,12 @@
-#define DRV_NAME "advansys"
-#define ASC_VERSION "3.4" /* AdvanSys Driver Version */
-
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
*
* Copyright (c) 1995-2000 Advanced System Products, Inc.
* Copyright (c) 2000-2001 ConnectCom Solutions, Inc.
* Copyright (c) 2007 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (c) 2014 Hannes Reinecke <hare@suse.de>
* All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
/*
@@ -39,9 +33,9 @@
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
+#include <linux/dmapool.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/dma.h>
#include <scsi/scsi_cmnd.h>
@@ -50,26 +44,15 @@
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
+#define DRV_NAME "advansys"
+#define ASC_VERSION "3.5" /* AdvanSys Driver Version */
+
/* FIXME:
*
- * 1. Although all of the necessary command mapping places have the
- * appropriate dma_map.. APIs, the driver still processes its internal
- * queue using bus_to_virt() and virt_to_bus() which are illegal under
- * the API. The entire queue processing structure will need to be
- * altered to fix this.
- * 2. Need to add memory mapping workaround. Test the memory mapping.
- * If it doesn't work revert to I/O port access. Can a test be done
- * safely?
- * 3. Handle an interrupt not working. Keep an interrupt counter in
- * the interrupt handler. In the timeout function if the interrupt
- * has not occurred then print a message and run in polled mode.
- * 4. Need to add support for target mode commands, cf. CAM XPT.
- * 5. check DMA mapping functions for failure
- * 6. Use scsi_transport_spi
- * 7. advansys_info is not safe against multiple simultaneous callers
- * 8. Add module_param to override ISA/VLB ioport array
+ * 1. Use scsi_transport_spi
+ * 2. advansys_info is not safe against multiple simultaneous callers
+ * 3. Add module_param to override ISA/VLB ioport array
*/
-#warning this driver is still not properly converted to the DMA API
/* Enable driver /proc statistics. */
#define ADVANSYS_STATS
@@ -77,31 +60,8 @@
/* Enable driver tracing. */
#undef ADVANSYS_DEBUG
-/*
- * Portable Data Types
- *
- * Any instance where a 32-bit long or pointer type is assumed
- * for precision or HW defined structures, the following define
- * types must be used. In Linux the char, short, and int types
- * are all consistent at 8, 16, and 32 bits respectively. Pointers
- * and long types are 64 bits on Alpha and UltraSPARC.
- */
-#define ASC_PADDR __u32 /* Physical/Bus address data type. */
-#define ASC_VADDR __u32 /* Virtual address data type. */
-#define ASC_DCNT __u32 /* Unsigned Data count type. */
-#define ASC_SDCNT __s32 /* Signed Data count type. */
-
typedef unsigned char uchar;
-#ifndef TRUE
-#define TRUE (1)
-#endif
-#ifndef FALSE
-#define FALSE (0)
-#endif
-
-#define ERR (-1)
-#define UW_ERR (uint)(0xFFFF)
#define isodd_word(val) ((((uint)val) & (uint)0x0001) != 0)
#define PCI_VENDOR_ID_ASP 0x10cd
@@ -112,15 +72,6 @@ typedef unsigned char uchar;
#define PCI_DEVICE_ID_38C0800_REV1 0x2500
#define PCI_DEVICE_ID_38C1600_REV1 0x2700
-/*
- * Enable CC_VERY_LONG_SG_LIST to support up to 64K element SG lists.
- * The SRB structure will have to be changed and the ASC_SRB2SCSIQ()
- * macro re-defined to be able to obtain a ASC_SCSI_Q pointer from the
- * SRB structure.
- */
-#define CC_VERY_LONG_SG_LIST 0
-#define ASC_SRB2SCSIQ(srb_ptr) (srb_ptr)
-
#define PortAddr unsigned int /* port address size */
#define inp(port) inb(port)
#define outp(port, byte) outb((byte), (port))
@@ -133,8 +84,6 @@ typedef unsigned char uchar;
#define ASC_CS_TYPE unsigned short
-#define ASC_IS_ISA (0x0001)
-#define ASC_IS_ISAPNP (0x0081)
#define ASC_IS_EISA (0x0002)
#define ASC_IS_PCI (0x0004)
#define ASC_IS_PCI_ULTRA (0x0104)
@@ -150,11 +99,6 @@ typedef unsigned char uchar;
#define ASC_CHIP_MIN_VER_PCI (0x09)
#define ASC_CHIP_MAX_VER_PCI (0x0F)
#define ASC_CHIP_VER_PCI_BIT (0x08)
-#define ASC_CHIP_MIN_VER_ISA (0x11)
-#define ASC_CHIP_MIN_VER_ISA_PNP (0x21)
-#define ASC_CHIP_MAX_VER_ISA (0x27)
-#define ASC_CHIP_VER_ISA_BIT (0x30)
-#define ASC_CHIP_VER_ISAPNP_BIT (0x20)
#define ASC_CHIP_VER_ASYN_BUG (0x21)
#define ASC_CHIP_VER_PCI 0x08
#define ASC_CHIP_VER_PCI_ULTRA_3150 (ASC_CHIP_VER_PCI | 0x02)
@@ -165,7 +109,6 @@ typedef unsigned char uchar;
#define ASC_CHIP_LATEST_VER_EISA ((ASC_CHIP_MIN_VER_EISA - 1) + 3)
#define ASC_MAX_VL_DMA_COUNT (0x07FFFFFFL)
#define ASC_MAX_PCI_DMA_COUNT (0xFFFFFFFFL)
-#define ASC_MAX_ISA_DMA_COUNT (0x00FFFFFFL)
#define ASC_SCSI_ID_BITS 3
#define ASC_SCSI_TIX_TYPE uchar
@@ -243,7 +186,6 @@ typedef unsigned char uchar;
#define ASC_FLAG_SRB_LINEAR_ADDR 0x08
#define ASC_FLAG_WIN16 0x10
#define ASC_FLAG_WIN32 0x20
-#define ASC_FLAG_ISA_OVER_16MB 0x40
#define ASC_FLAG_DOS_VM_CALLBACK 0x80
#define ASC_TAG_FLAG_EXTRA_BYTES 0x10
#define ASC_TAG_FLAG_DISABLE_DISCONNECT 0x04
@@ -308,15 +250,15 @@ typedef struct asc_scsiq_1 {
uchar sg_queue_cnt;
uchar target_id;
uchar target_lun;
- ASC_PADDR data_addr;
- ASC_DCNT data_cnt;
- ASC_PADDR sense_addr;
+ __le32 data_addr;
+ __le32 data_cnt;
+ __le32 sense_addr;
uchar sense_len;
uchar extra_bytes;
} ASC_SCSIQ_1;
typedef struct asc_scsiq_2 {
- ASC_VADDR srb_ptr;
+ u32 srb_tag;
uchar target_ix;
uchar flag;
uchar cdb_len;
@@ -339,8 +281,8 @@ typedef struct asc_scsiq_4 {
uchar y_res;
ushort x_req_count;
ushort x_reconnect_rtn;
- ASC_PADDR x_saved_data_addr;
- ASC_DCNT x_saved_data_cnt;
+ __le32 x_saved_data_addr;
+ __le32 x_saved_data_cnt;
} ASC_SCSIQ_4;
typedef struct asc_q_done_info {
@@ -352,12 +294,12 @@ typedef struct asc_q_done_info {
uchar sense_len;
uchar extra_bytes;
uchar res;
- ASC_DCNT remain_bytes;
+ u32 remain_bytes;
} ASC_QDONE_INFO;
typedef struct asc_sg_list {
- ASC_PADDR addr;
- ASC_DCNT bytes;
+ __le32 addr;
+ __le32 bytes;
} ASC_SG_LIST;
typedef struct asc_sg_head {
@@ -365,7 +307,7 @@ typedef struct asc_sg_head {
ushort queue_cnt;
ushort entry_to_copy;
ushort res;
- ASC_SG_LIST sg_list[0];
+ ASC_SG_LIST sg_list[];
} ASC_SG_HEAD;
typedef struct asc_scsi_q {
@@ -377,17 +319,6 @@ typedef struct asc_scsi_q {
ushort next_sg_index;
} ASC_SCSI_Q;
-typedef struct asc_scsi_req_q {
- ASC_SCSIQ_1 r1;
- ASC_SCSIQ_2 r2;
- uchar *cdbptr;
- ASC_SG_HEAD *sg_head;
- uchar *sense_ptr;
- ASC_SCSIQ_3 r3;
- uchar cdb[ASC_MAX_CDB_LEN];
- uchar sense[ASC_MIN_SENSE_LEN];
-} ASC_SCSI_REQ_Q;
-
typedef struct asc_scsi_bios_req_q {
ASC_SCSIQ_1 r1;
ASC_SCSIQ_2 r2;
@@ -524,8 +455,6 @@ typedef struct asc_dvc_cfg {
ASC_SCSI_BIT_ID_TYPE disc_enable;
ASC_SCSI_BIT_ID_TYPE sdtr_enable;
uchar chip_scsi_id;
- uchar isa_dma_speed;
- uchar isa_dma_channel;
uchar chip_version;
ushort mcode_date;
ushort mcode_version;
@@ -571,7 +500,7 @@ typedef struct asc_dvc_var {
dma_addr_t overrun_dma;
uchar scsi_reset_wait;
uchar chip_no;
- char is_in_int;
+ bool is_in_int;
uchar max_total_qng;
uchar cur_total_qng;
uchar in_critical_cnt;
@@ -587,15 +516,13 @@ typedef struct asc_dvc_var {
char redo_scam;
ushort res2;
uchar dos_int13_table[ASC_MAX_TID + 1];
- ASC_DCNT max_dma_count;
+ unsigned int max_dma_count;
ASC_SCSI_BIT_ID_TYPE no_scam;
ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer;
uchar min_sdtr_index;
uchar max_sdtr_index;
struct asc_board *drv_ptr;
- int ptr_map_count;
- void **ptr_map;
- ASC_DCNT uc_break;
+ unsigned int uc_break;
} ASC_DVC_VAR;
typedef struct asc_dvc_inq_info {
@@ -603,8 +530,8 @@ typedef struct asc_dvc_inq_info {
} ASC_DVC_INQ_INFO;
typedef struct asc_cap_info {
- ASC_DCNT lba;
- ASC_DCNT blk_size;
+ u32 lba;
+ u32 blk_size;
} ASC_CAP_INFO;
typedef struct asc_cap_info_array {
@@ -634,10 +561,8 @@ typedef struct asc_cap_info_array {
#define ASC_EEP_MAX_RETRY 20
/*
- * These macros keep the chip SCSI id and ISA DMA speed
- * bitfields in board order. C bitfields aren't portable
- * between big and little-endian platforms so they are
- * not used.
+ * These macros keep the chip SCSI id bitfields in board order. C bitfields
+ * aren't portable between big and little-endian platforms so they are not used.
*/
#define ASC_EEP_GET_CHIP_ID(cfg) ((cfg)->id_speed & 0x0f)
@@ -930,31 +855,6 @@ typedef struct asc_mc_saved {
#define AscReadChipDvcID(port) (uchar)inp((port)+IOP_REG_ID)
#define AscWriteChipDvcID(port, data) outp((port)+IOP_REG_ID, data)
-/*
- * Portable Data Types
- *
- * Any instance where a 32-bit long or pointer type is assumed
- * for precision or HW defined structures, the following define
- * types must be used. In Linux the char, short, and int types
- * are all consistent at 8, 16, and 32 bits respectively. Pointers
- * and long types are 64 bits on Alpha and UltraSPARC.
- */
-#define ADV_PADDR __u32 /* Physical address data type. */
-#define ADV_VADDR __u32 /* Virtual address data type. */
-#define ADV_DCNT __u32 /* Unsigned Data count type. */
-#define ADV_SDCNT __s32 /* Signed Data count type. */
-
-/*
- * These macros are used to convert a virtual address to a
- * 32-bit value. This currently can be used on Linux Alpha
- * which uses 64-bit virtual address but a 32-bit bus address.
- * This is likely to break in the future, but doing this now
- * will give us time to change the HW and FW to handle 64-bit
- * addresses.
- */
-#define ADV_VADDR_TO_U32 virt_to_bus
-#define ADV_U32_TO_VADDR bus_to_virt
-
#define AdvPortAddr void __iomem * /* Virtual memory address size */
/*
@@ -966,8 +866,6 @@ typedef struct asc_mc_saved {
#define ADV_MEM_WRITEW(addr, word) writew(word, addr)
#define ADV_MEM_WRITEDW(addr, dword) writel(dword, addr)
-#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 15)
-
/*
* Define total number of simultaneous maximum element scatter-gather
* request blocks per wide adapter. ASC_DEF_MAX_HOST_QNG (253) is the
@@ -1748,44 +1646,37 @@ typedef struct adveep_38C1600_config {
* little-endian.
*/
typedef struct adv_carr_t {
- ADV_VADDR carr_va; /* Carrier Virtual Address */
- ADV_PADDR carr_pa; /* Carrier Physical Address */
- ADV_VADDR areq_vpa; /* ASC_SCSI_REQ_Q Virtual or Physical Address */
+ __le32 carr_va; /* Carrier Virtual Address */
+ __le32 carr_pa; /* Carrier Physical Address */
+ __le32 areq_vpa; /* ADV_SCSI_REQ_Q Virtual or Physical Address */
/*
* next_vpa [31:4] Carrier Virtual or Physical Next Pointer
*
* next_vpa [3:1] Reserved Bits
* next_vpa [0] Done Flag set in Response Queue.
*/
- ADV_VADDR next_vpa;
+ __le32 next_vpa;
} ADV_CARR_T;
/*
* Mask used to eliminate low 4 bits of carrier 'next_vpa' field.
*/
-#define ASC_NEXT_VPA_MASK 0xFFFFFFF0
-
-#define ASC_RQ_DONE 0x00000001
-#define ASC_RQ_GOOD 0x00000002
-#define ASC_CQ_STOPPER 0x00000000
+#define ADV_NEXT_VPA_MASK 0xFFFFFFF0
-#define ASC_GET_CARRP(carrp) ((carrp) & ASC_NEXT_VPA_MASK)
+#define ADV_RQ_DONE 0x00000001
+#define ADV_RQ_GOOD 0x00000002
+#define ADV_CQ_STOPPER 0x00000000
-#define ADV_CARRIER_NUM_PAGE_CROSSING \
- (((ADV_CARRIER_COUNT * sizeof(ADV_CARR_T)) + (PAGE_SIZE - 1))/PAGE_SIZE)
-
-#define ADV_CARRIER_BUFSIZE \
- ((ADV_CARRIER_COUNT + ADV_CARRIER_NUM_PAGE_CROSSING) * sizeof(ADV_CARR_T))
+#define ADV_GET_CARRP(carrp) ((carrp) & ADV_NEXT_VPA_MASK)
/*
- * ASC_SCSI_REQ_Q 'a_flag' definitions
- *
- * The Adv Library should limit use to the lower nibble (4 bits) of
- * a_flag. Drivers are free to use the upper nibble (4 bits) of a_flag.
+ * Each carrier is 64 bytes, and we need three additional
+ * carrier for icq, irq, and the termination carrier.
*/
-#define ADV_POLL_REQUEST 0x01 /* poll for request completion */
-#define ADV_SCSIQ_DONE 0x02 /* request done */
-#define ADV_DONT_RETRY 0x08 /* don't do retry */
+#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 3)
+
+#define ADV_CARRIER_BUFSIZE \
+ (ADV_CARRIER_COUNT * sizeof(ADV_CARR_T))
#define ADV_CHIP_ASC3550 0x01 /* Ultra-Wide IC */
#define ADV_CHIP_ASC38C0800 0x02 /* Ultra2-Wide/LVD IC */
@@ -1817,15 +1708,15 @@ typedef struct adv_dvc_cfg {
struct adv_dvc_var;
struct adv_scsi_req_q;
-typedef struct asc_sg_block {
+typedef struct adv_sg_block {
uchar reserved1;
uchar reserved2;
uchar reserved3;
uchar sg_cnt; /* Valid entries in block. */
- ADV_PADDR sg_ptr; /* Pointer to next sg block. */
+ __le32 sg_ptr; /* Pointer to next sg block. */
struct {
- ADV_PADDR sg_addr; /* SG element address. */
- ADV_DCNT sg_count; /* SG element count. */
+ __le32 sg_addr; /* SG element address. */
+ __le32 sg_count; /* SG element count. */
} sg_list[NO_OF_SG_PER_BLOCK];
} ADV_SG_BLOCK;
@@ -1845,10 +1736,10 @@ typedef struct adv_scsi_req_q {
uchar target_cmd;
uchar target_id; /* Device target identifier. */
uchar target_lun; /* Device target logical unit number. */
- ADV_PADDR data_addr; /* Data buffer physical address. */
- ADV_DCNT data_cnt; /* Data count. Ucode sets to residual. */
- ADV_PADDR sense_addr;
- ADV_PADDR carr_pa;
+ __le32 data_addr; /* Data buffer physical address. */
+ __le32 data_cnt; /* Data count. Ucode sets to residual. */
+ __le32 sense_addr;
+ __le32 carr_pa;
uchar mflag;
uchar sense_len;
uchar cdb_len; /* SCSI CDB length. Must <= 16 bytes. */
@@ -1858,29 +1749,26 @@ typedef struct adv_scsi_req_q {
uchar host_status; /* Ucode host status. */
uchar sg_working_ix;
uchar cdb[12]; /* SCSI CDB bytes 0-11. */
- ADV_PADDR sg_real_addr; /* SG list physical address. */
- ADV_PADDR scsiq_rptr;
+ __le32 sg_real_addr; /* SG list physical address. */
+ __le32 scsiq_rptr;
uchar cdb16[4]; /* SCSI CDB bytes 12-15. */
- ADV_VADDR scsiq_ptr;
- ADV_VADDR carr_va;
+ __le32 scsiq_ptr;
+ __le32 carr_va;
/*
* End of microcode structure - 60 bytes. The rest of the structure
* is used by the Adv Library and ignored by the microcode.
*/
- ADV_VADDR srb_ptr;
+ u32 srb_tag;
ADV_SG_BLOCK *sg_list_ptr; /* SG list virtual address. */
- char *vdata_addr; /* Data buffer virtual address. */
- uchar a_flag;
- uchar pad[2]; /* Pad out to a word boundary. */
} ADV_SCSI_REQ_Q;
/*
* The following two structures are used to process Wide Board requests.
*
* The ADV_SCSI_REQ_Q structure in adv_req_t is passed to the Adv Library
- * and microcode with the ADV_SCSI_REQ_Q field 'srb_ptr' pointing to the
- * adv_req_t. The adv_req_t structure 'cmndp' field in turn points to the
- * Mid-Level SCSI request structure.
+ * and microcode with the ADV_SCSI_REQ_Q field 'srb_tag' set to the
+ * SCSI request tag. The adv_req_t structure 'cmndp' field in turn points
+ * to the Mid-Level SCSI request structure.
*
* Zero or more ADV_SG_BLOCK are used with each ADV_SCSI_REQ_Q. Each
* ADV_SG_BLOCK structure holds 15 scatter-gather elements. Under Linux
@@ -1891,17 +1779,17 @@ typedef struct adv_scsi_req_q {
*/
typedef struct adv_sgblk {
ADV_SG_BLOCK sg_block; /* Sgblock structure. */
- uchar align[32]; /* Sgblock structure padding. */
+ dma_addr_t sg_addr; /* Physical address */
struct adv_sgblk *next_sgblkp; /* Next scatter-gather structure. */
} adv_sgblk_t;
typedef struct adv_req {
ADV_SCSI_REQ_Q scsi_req_q; /* Adv Library request structure. */
- uchar align[32]; /* Request structure padding. */
+ uchar align[24]; /* Request structure padding. */
struct scsi_cmnd *cmndp; /* Mid-Level SCSI command pointer. */
+ dma_addr_t req_addr;
adv_sgblk_t *sgblkp; /* Adv Library scatter-gather pointer. */
- struct adv_req *next_reqp; /* Next Request Structure. */
-} adv_req_t;
+} adv_req_t __aligned(32);
/*
* Adapter operation variable structure.
@@ -1911,7 +1799,7 @@ typedef struct adv_req {
* Field naming convention:
*
* *_able indicates both whether a feature should be enabled or disabled
- * and whether a device isi capable of the feature. At initialization
+ * and whether a device is capable of the feature. At initialization
* this field may be set, but later if a device is found to be incapable
* of the feature, the field is cleared.
*/
@@ -1938,12 +1826,12 @@ typedef struct adv_dvc_var {
uchar chip_scsi_id; /* chip SCSI target ID */
uchar chip_type;
uchar bist_err_code;
- ADV_CARR_T *carrier_buf;
+ ADV_CARR_T *carrier;
ADV_CARR_T *carr_freelist; /* Carrier free list. */
+ dma_addr_t carrier_addr;
ADV_CARR_T *icq_sp; /* Initiator command queue stopper pointer. */
ADV_CARR_T *irq_sp; /* Initiator response queue stopper pointer. */
ushort carr_pending_cnt; /* Count of pending carriers. */
- struct adv_req *orig_reqp; /* adv_req_t memory block. */
/*
* Note: The following fields will not be used after initialization. The
* driver may discard the buffer after initialization is done.
@@ -2069,8 +1957,8 @@ do { \
AdvReadByteRegister((iop_base), IOPB_CHIP_TYPE_REV)
/*
- * Abort an SRB in the chip's RISC Memory. The 'srb_ptr' argument must
- * match the ASC_SCSI_REQ_Q 'srb_ptr' field.
+ * Abort an SRB in the chip's RISC Memory. The 'srb_tag' argument must
+ * match the ADV_SCSI_REQ_Q 'srb_tag' field.
*
* If the request has not yet been sent to the device it will simply be
* aborted from RISC memory. If the request is disconnected it will be
@@ -2080,9 +1968,9 @@ do { \
* ADV_TRUE(1) - Queue was successfully aborted.
* ADV_FALSE(0) - Queue was not found on the active queue list.
*/
-#define AdvAbortQueue(asc_dvc, scsiq) \
- AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
- (ADV_DCNT) (scsiq))
+#define AdvAbortQueue(asc_dvc, srb_tag) \
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
+ (ADV_DCNT) (srb_tag))
/*
* Send a Bus Device Reset Message to the specified target ID.
@@ -2096,8 +1984,8 @@ do { \
* are not purged.
*/
#define AdvResetDevice(asc_dvc, target_id) \
- AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
- (ADV_DCNT) (target_id))
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
+ (ADV_DCNT) (target_id))
/*
* SCSI Wide Type definition.
@@ -2116,7 +2004,7 @@ do { \
#define ADV_TID_TO_TIDMASK(tid) (0x01 << ((tid) & ADV_MAX_TID))
/*
- * ASC_SCSI_REQ_Q 'done_status' and 'host_status' return values.
+ * ADV_SCSI_REQ_Q 'done_status' and 'host_status' return values.
*/
#define QD_NO_STATUS 0x00 /* Request not completed yet. */
@@ -2154,8 +2042,6 @@ do { \
#define QHSTA_M_SGBACKUP_ERROR 0x47 /* Scatter-Gather backup error */
/* Return the address that is aligned at the next doubleword >= to 'addr'. */
-#define ADV_8BALIGN(addr) (((ulong) (addr) + 0x7) & ~0x7)
-#define ADV_16BALIGN(addr) (((ulong) (addr) + 0xF) & ~0xF)
#define ADV_32BALIGN(addr) (((ulong) (addr) + 0x1F) & ~0x1F)
/*
@@ -2179,22 +2065,6 @@ do { \
#define ASC_INFO_SIZE 128 /* advansys_info() line size */
-#ifdef CONFIG_PROC_FS
-/* /proc/scsi/advansys/[0...] related definitions */
-#define ASC_PRTBUF_SIZE 2048
-#define ASC_PRTLINE_SIZE 160
-
-#define ASC_PRT_NEXT() \
- if (cp) { \
- totlen += len; \
- leftlen -= len; \
- if (leftlen == 0) { \
- return totlen; \
- } \
- cp += len; \
- }
-#endif /* CONFIG_PROC_FS */
-
/* Asc Library return codes */
#define ASC_TRUE 1
#define ASC_FALSE 0
@@ -2202,12 +2072,6 @@ do { \
#define ASC_BUSY 0
#define ASC_ERROR (-1)
-/* struct scsi_cmnd function return codes */
-#define STATUS_BYTE(byte) (byte)
-#define MSG_BYTE(byte) ((byte) << 8)
-#define HOST_BYTE(byte) ((byte) << 16)
-#define DRIVER_BYTE(byte) ((byte) << 24)
-
#define ASC_STATS(shost, counter) ASC_STATS_ADD(shost, counter, 1)
#ifndef ADVANSYS_STATS
#define ASC_STATS_ADD(shost, counter, count)
@@ -2332,24 +2196,24 @@ do { \
/* Per board statistics structure */
struct asc_stats {
/* Driver Entrypoint Statistics */
- ADV_DCNT queuecommand; /* # calls to advansys_queuecommand() */
- ADV_DCNT reset; /* # calls to advansys_eh_bus_reset() */
- ADV_DCNT biosparam; /* # calls to advansys_biosparam() */
- ADV_DCNT interrupt; /* # advansys_interrupt() calls */
- ADV_DCNT callback; /* # calls to asc/adv_isr_callback() */
- ADV_DCNT done; /* # calls to request's scsi_done function */
- ADV_DCNT build_error; /* # asc/adv_build_req() ASC_ERROR returns. */
- ADV_DCNT adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */
- ADV_DCNT adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */
+ unsigned int queuecommand; /* # calls to advansys_queuecommand() */
+ unsigned int reset; /* # calls to advansys_eh_bus_reset() */
+ unsigned int biosparam; /* # calls to advansys_biosparam() */
+ unsigned int interrupt; /* # advansys_interrupt() calls */
+ unsigned int callback; /* # calls to asc/adv_isr_callback() */
+ unsigned int done; /* # calls to request's scsi_done function */
+ unsigned int build_error; /* # asc/adv_build_req() ASC_ERROR returns. */
+ unsigned int adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */
+ unsigned int adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */
/* AscExeScsiQueue()/AdvExeScsiQueue() Statistics */
- ADV_DCNT exe_noerror; /* # ASC_NOERROR returns. */
- ADV_DCNT exe_busy; /* # ASC_BUSY returns. */
- ADV_DCNT exe_error; /* # ASC_ERROR returns. */
- ADV_DCNT exe_unknown; /* # unknown returns. */
+ unsigned int exe_noerror; /* # ASC_NOERROR returns. */
+ unsigned int exe_busy; /* # ASC_BUSY returns. */
+ unsigned int exe_error; /* # ASC_ERROR returns. */
+ unsigned int exe_unknown; /* # unknown returns. */
/* Data Transfer Statistics */
- ADV_DCNT xfer_cnt; /* # I/O requests received */
- ADV_DCNT xfer_elem; /* # scatter-gather elements */
- ADV_DCNT xfer_sect; /* # 512-byte blocks */
+ unsigned int xfer_cnt; /* # I/O requests received */
+ unsigned int xfer_elem; /* # scatter-gather elements */
+ unsigned int xfer_sect; /* # 512-byte blocks */
};
#endif /* ADVANSYS_STATS */
@@ -2362,6 +2226,7 @@ struct asc_stats {
*/
struct asc_board {
struct device *dev;
+ struct Scsi_Host *shost;
uint flags; /* Board flags */
unsigned int irq;
union {
@@ -2383,9 +2248,7 @@ struct asc_board {
ADVEEP_38C0800_CONFIG adv_38C0800_eep; /* 38C0800 EEPROM config. */
ADVEEP_38C1600_CONFIG adv_38C1600_eep; /* 38C1600 EEPROM config. */
} eep_config;
- ulong last_reset; /* Saved last reset time */
/* /proc/scsi/advansys/[0...] */
- char *prtbuf; /* /proc print buffer */
#ifdef ADVANSYS_STATS
struct asc_stats asc_stats; /* Board statistics */
#endif /* ADVANSYS_STATS */
@@ -2399,7 +2262,9 @@ struct asc_board {
void __iomem *ioremap_addr; /* I/O Memory remap address. */
ushort ioport; /* I/O Port address. */
adv_req_t *adv_reqp; /* Request structures. */
- adv_sgblk_t *adv_sgblkp; /* Scatter-gather structures. */
+ dma_addr_t adv_reqp_addr;
+ size_t adv_reqp_size;
+ struct dma_pool *adv_sgblk_pool; /* Scatter-gather structures. */
ushort bios_signature; /* BIOS Signature. */
ushort bios_version; /* BIOS Version. */
ushort bios_codeseg; /* BIOS Code Segment. */
@@ -2412,6 +2277,15 @@ struct asc_board {
dvc_var.adv_dvc_var)
#define adv_dvc_to_pdev(adv_dvc) to_pci_dev(adv_dvc_to_board(adv_dvc)->dev)
+struct advansys_cmd {
+ dma_addr_t dma_handle;
+};
+
+static struct advansys_cmd *advansys_cmd(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
#ifdef ADVANSYS_DEBUG
static int asc_dbglvl = 3;
@@ -2462,9 +2336,8 @@ static void asc_prt_asc_dvc_cfg(ASC_DVC_CFG *h)
printk(" disc_enable 0x%x, sdtr_enable 0x%x,\n",
h->disc_enable, h->sdtr_enable);
- printk(" chip_scsi_id %d, isa_dma_speed %d, isa_dma_channel %d, "
- "chip_version %d,\n", h->chip_scsi_id, h->isa_dma_speed,
- h->isa_dma_channel, h->chip_version);
+ printk(" chip_scsi_id %d, chip_version %d,\n",
+ h->chip_scsi_id, h->chip_version);
printk(" mcode_date 0x%x, mcode_version %d\n",
h->mcode_date, h->mcode_version);
@@ -2488,12 +2361,11 @@ static void asc_prt_adv_dvc_var(ADV_DVC_VAR *h)
printk(" start_motor 0x%x, scsi_reset_wait 0x%x\n",
(unsigned)h->start_motor, (unsigned)h->scsi_reset_wait);
- printk(" max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%lxn\n",
+ printk(" max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%p\n",
(unsigned)h->max_host_qng, (unsigned)h->max_dvc_qng,
- (ulong)h->carr_freelist);
+ h->carr_freelist);
- printk(" icq_sp 0x%lx, irq_sp 0x%lx\n",
- (ulong)h->icq_sp, (ulong)h->irq_sp);
+ printk(" icq_sp 0x%p, irq_sp 0x%p\n", h->icq_sp, h->irq_sp);
printk(" no_scam 0x%x, tagqng_able 0x%x\n",
(unsigned)h->no_scam, (unsigned)h->tagqng_able);
@@ -2529,8 +2401,7 @@ static void asc_prt_scsi_host(struct Scsi_Host *s)
struct asc_board *boardp = shost_priv(s);
printk("Scsi_Host at addr 0x%p, device %s\n", s, dev_name(boardp->dev));
- printk(" host_busy %u, host_no %d, last_reset %d,\n",
- s->host_busy, s->host_no, (unsigned)s->last_reset);
+ printk(" host_no %d,\n", s->host_no);
printk(" base 0x%lx, io_port 0x%lx, irq %d,\n",
(ulong)s->base, (ulong)s->io_port, boardp->irq);
@@ -2538,8 +2409,8 @@ static void asc_prt_scsi_host(struct Scsi_Host *s)
printk(" dma_channel %d, this_id %d, can_queue %d,\n",
s->dma_channel, s->this_id, s->can_queue);
- printk(" cmd_per_lun %d, sg_tablesize %d, unchecked_isa_dma %d\n",
- s->cmd_per_lun, s->sg_tablesize, s->unchecked_isa_dma);
+ printk(" cmd_per_lun %d, sg_tablesize %d\n",
+ s->cmd_per_lun, s->sg_tablesize);
if (ASC_NARROW_BOARD(boardp)) {
asc_prt_asc_dvc_var(&boardp->dvc_var.asc_dvc_var);
@@ -2618,8 +2489,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
printk("ASC_SCSI_Q at addr 0x%lx\n", (ulong)q);
printk
- (" target_ix 0x%x, target_lun %u, srb_ptr 0x%lx, tag_code 0x%x,\n",
- q->q2.target_ix, q->q1.target_lun, (ulong)q->q2.srb_ptr,
+ (" target_ix 0x%x, target_lun %u, srb_tag 0x%x, tag_code 0x%x,\n",
+ q->q2.target_ix, q->q1.target_lun, q->q2.srb_tag,
q->q2.tag_code);
printk
@@ -2652,8 +2523,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
static void asc_prt_asc_qdone_info(ASC_QDONE_INFO *q)
{
printk("ASC_QDONE_INFO at addr 0x%lx\n", (ulong)q);
- printk(" srb_ptr 0x%lx, target_ix %u, cdb_len %u, tag_code %u,\n",
- (ulong)q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len,
+ printk(" srb_tag 0x%x, target_ix %u, cdb_len %u, tag_code %u,\n",
+ q->d2.srb_tag, q->d2.target_ix, q->d2.cdb_len,
q->d2.tag_code);
printk
(" done_stat 0x%x, host_stat 0x%x, scsi_stat 0x%x, scsi_msg 0x%x\n",
@@ -2669,17 +2540,17 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
{
int i;
- printk(" ASC_SG_BLOCK at addr 0x%lx (sgblockno %d)\n",
+ printk(" ADV_SG_BLOCK at addr 0x%lx (sgblockno %d)\n",
(ulong)b, sgblockno);
- printk(" sg_cnt %u, sg_ptr 0x%lx\n",
- b->sg_cnt, (ulong)le32_to_cpu(b->sg_ptr));
+ printk(" sg_cnt %u, sg_ptr 0x%x\n",
+ b->sg_cnt, (u32)le32_to_cpu(b->sg_ptr));
BUG_ON(b->sg_cnt > NO_OF_SG_PER_BLOCK);
if (b->sg_ptr != 0)
BUG_ON(b->sg_cnt != NO_OF_SG_PER_BLOCK);
for (i = 0; i < b->sg_cnt; i++) {
- printk(" [%u]: sg_addr 0x%lx, sg_count 0x%lx\n",
- i, (ulong)b->sg_list[i].sg_addr,
- (ulong)b->sg_list[i].sg_count);
+ printk(" [%u]: sg_addr 0x%x, sg_count 0x%x\n",
+ i, (u32)le32_to_cpu(b->sg_list[i].sg_addr),
+ (u32)le32_to_cpu(b->sg_list[i].sg_count));
}
}
@@ -2691,15 +2562,16 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
{
int sg_blk_cnt;
- struct asc_sg_block *sg_ptr;
+ struct adv_sg_block *sg_ptr;
+ adv_sgblk_t *sgblkp;
printk("ADV_SCSI_REQ_Q at addr 0x%lx\n", (ulong)q);
- printk(" target_id %u, target_lun %u, srb_ptr 0x%lx, a_flag 0x%x\n",
- q->target_id, q->target_lun, (ulong)q->srb_ptr, q->a_flag);
+ printk(" target_id %u, target_lun %u, srb_tag 0x%x\n",
+ q->target_id, q->target_lun, q->srb_tag);
- printk(" cntl 0x%x, data_addr 0x%lx, vdata_addr 0x%lx\n",
- q->cntl, (ulong)le32_to_cpu(q->data_addr), (ulong)q->vdata_addr);
+ printk(" cntl 0x%x, data_addr 0x%lx\n",
+ q->cntl, (ulong)le32_to_cpu(q->data_addr));
printk(" data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n",
(ulong)le32_to_cpu(q->data_cnt),
@@ -2718,21 +2590,15 @@ static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
/* Display the request's ADV_SG_BLOCK structures. */
if (q->sg_list_ptr != NULL) {
+ sgblkp = container_of(q->sg_list_ptr, adv_sgblk_t, sg_block);
sg_blk_cnt = 0;
- while (1) {
- /*
- * 'sg_ptr' is a physical address. Convert it to a virtual
- * address by indexing 'sg_blk_cnt' into the virtual address
- * array 'sg_list_ptr'.
- *
- * XXX - Assumes all SG physical blocks are virtually contiguous.
- */
- sg_ptr =
- &(((ADV_SG_BLOCK *)(q->sg_list_ptr))[sg_blk_cnt]);
+ while (sgblkp) {
+ sg_ptr = &sgblkp->sg_block;
asc_prt_adv_sgblock(sg_blk_cnt, sg_ptr);
if (sg_ptr->sg_ptr == 0) {
break;
}
+ sgblkp = sgblkp->next_sgblkp;
sg_blk_cnt++;
}
}
@@ -2740,59 +2606,6 @@ static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
#endif /* ADVANSYS_DEBUG */
/*
- * The advansys chip/microcode contains a 32-bit identifier for each command
- * known as the 'srb'. I don't know what it stands for. The driver used
- * to encode the scsi_cmnd pointer by calling virt_to_bus and retrieve it
- * with bus_to_virt. Now the driver keeps a per-host map of integers to
- * pointers. It auto-expands when full, unless it can't allocate memory.
- * Note that an srb of 0 is treated specially by the chip/firmware, hence
- * the return of i+1 in this routine, and the corresponding subtraction in
- * the inverse routine.
- */
-#define BAD_SRB 0
-static u32 advansys_ptr_to_srb(struct asc_dvc_var *asc_dvc, void *ptr)
-{
- int i;
- void **new_ptr;
-
- for (i = 0; i < asc_dvc->ptr_map_count; i++) {
- if (!asc_dvc->ptr_map[i])
- goto out;
- }
-
- if (asc_dvc->ptr_map_count == 0)
- asc_dvc->ptr_map_count = 1;
- else
- asc_dvc->ptr_map_count *= 2;
-
- new_ptr = krealloc(asc_dvc->ptr_map,
- asc_dvc->ptr_map_count * sizeof(void *), GFP_ATOMIC);
- if (!new_ptr)
- return BAD_SRB;
- asc_dvc->ptr_map = new_ptr;
- out:
- ASC_DBG(3, "Putting ptr %p into array offset %d\n", ptr, i);
- asc_dvc->ptr_map[i] = ptr;
- return i + 1;
-}
-
-static void * advansys_srb_to_ptr(struct asc_dvc_var *asc_dvc, u32 srb)
-{
- void *ptr;
-
- srb--;
- if (srb >= asc_dvc->ptr_map_count) {
- printk("advansys: bad SRB %u, max %u\n", srb,
- asc_dvc->ptr_map_count);
- return NULL;
- }
- ptr = asc_dvc->ptr_map[srb];
- asc_dvc->ptr_map[srb] = NULL;
- ASC_DBG(3, "Returning ptr %p from array offset %d\n", ptr, srb);
- return ptr;
-}
-
-/*
* advansys_info()
*
* Return suitable for printing on the console with the argument
@@ -2813,42 +2626,28 @@ static const char *advansys_info(struct Scsi_Host *shost)
if (ASC_NARROW_BOARD(boardp)) {
asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
ASC_DBG(1, "begin\n");
- if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
- if ((asc_dvc_varp->bus_type & ASC_IS_ISAPNP) ==
- ASC_IS_ISAPNP) {
- busname = "ISA PnP";
+
+ if (asc_dvc_varp->bus_type & ASC_IS_VL) {
+ busname = "VL";
+ } else if (asc_dvc_varp->bus_type & ASC_IS_EISA) {
+ busname = "EISA";
+ } else if (asc_dvc_varp->bus_type & ASC_IS_PCI) {
+ if ((asc_dvc_varp->bus_type & ASC_IS_PCI_ULTRA)
+ == ASC_IS_PCI_ULTRA) {
+ busname = "PCI Ultra";
} else {
- busname = "ISA";
+ busname = "PCI";
}
- sprintf(info,
- "AdvanSys SCSI %s: %s: IO 0x%lX-0x%lX, IRQ 0x%X, DMA 0x%X",
- ASC_VERSION, busname,
- (ulong)shost->io_port,
- (ulong)shost->io_port + ASC_IOADR_GAP - 1,
- boardp->irq, shost->dma_channel);
} else {
- if (asc_dvc_varp->bus_type & ASC_IS_VL) {
- busname = "VL";
- } else if (asc_dvc_varp->bus_type & ASC_IS_EISA) {
- busname = "EISA";
- } else if (asc_dvc_varp->bus_type & ASC_IS_PCI) {
- if ((asc_dvc_varp->bus_type & ASC_IS_PCI_ULTRA)
- == ASC_IS_PCI_ULTRA) {
- busname = "PCI Ultra";
- } else {
- busname = "PCI";
- }
- } else {
- busname = "?";
- shost_printk(KERN_ERR, shost, "unknown bus "
- "type %d\n", asc_dvc_varp->bus_type);
- }
- sprintf(info,
- "AdvanSys SCSI %s: %s: IO 0x%lX-0x%lX, IRQ 0x%X",
- ASC_VERSION, busname, (ulong)shost->io_port,
- (ulong)shost->io_port + ASC_IOADR_GAP - 1,
- boardp->irq);
+ busname = "?";
+ shost_printk(KERN_ERR, shost, "unknown bus "
+ "type %d\n", asc_dvc_varp->bus_type);
}
+ sprintf(info,
+ "AdvanSys SCSI %s: %s: IO 0x%lX-0x%lX, IRQ 0x%X",
+ ASC_VERSION, busname, (ulong)shost->io_port,
+ (ulong)shost->io_port + ASC_IOADR_GAP - 1,
+ boardp->irq);
} else {
/*
* Wide Adapter Information
@@ -2876,64 +2675,21 @@ static const char *advansys_info(struct Scsi_Host *shost)
}
#ifdef CONFIG_PROC_FS
-/*
- * asc_prt_line()
- *
- * If 'cp' is NULL print to the console, otherwise print to a buffer.
- *
- * Return 0 if printing to the console, otherwise return the number of
- * bytes written to the buffer.
- *
- * Note: If any single line is greater than ASC_PRTLINE_SIZE bytes the stack
- * will be corrupted. 's[]' is defined to be ASC_PRTLINE_SIZE bytes.
- */
-static int asc_prt_line(char *buf, int buflen, char *fmt, ...)
-{
- va_list args;
- int ret;
- char s[ASC_PRTLINE_SIZE];
-
- va_start(args, fmt);
- ret = vsprintf(s, fmt, args);
- BUG_ON(ret >= ASC_PRTLINE_SIZE);
- if (buf == NULL) {
- (void)printk(s);
- ret = 0;
- } else {
- ret = min(buflen, ret);
- memcpy(buf, s, ret);
- }
- va_end(args);
- return ret;
-}
/*
* asc_prt_board_devices()
*
* Print driver information for devices attached to the board.
- *
- * Note: no single line should be greater than ASC_PRTLINE_SIZE,
- * cf. asc_prt_line().
- *
- * Return the number of characters copied into 'cp'. No more than
- * 'cplen' characters will be copied to 'cp'.
*/
-static int asc_prt_board_devices(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_board_devices(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
- int leftlen;
- int totlen;
- int len;
int chip_scsi_id;
int i;
- leftlen = cplen;
- totlen = len = 0;
-
- len = asc_prt_line(cp, leftlen,
- "\nDevice Information for AdvanSys SCSI Host %d:\n",
- shost->host_no);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "\nDevice Information for AdvanSys SCSI Host %d:\n",
+ shost->host_no);
if (ASC_NARROW_BOARD(boardp)) {
chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
@@ -2941,60 +2697,40 @@ static int asc_prt_board_devices(struct Scsi_Host *shost, char *cp, int cplen)
chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
}
- len = asc_prt_line(cp, leftlen, "Target IDs Detected:");
- ASC_PRT_NEXT();
+ seq_puts(m, "Target IDs Detected:");
for (i = 0; i <= ADV_MAX_TID; i++) {
- if (boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) {
- len = asc_prt_line(cp, leftlen, " %X,", i);
- ASC_PRT_NEXT();
- }
+ if (boardp->init_tidmask & ADV_TID_TO_TIDMASK(i))
+ seq_printf(m, " %X,", i);
}
- len = asc_prt_line(cp, leftlen, " (%X=Host Adapter)\n", chip_scsi_id);
- ASC_PRT_NEXT();
-
- return totlen;
+ seq_printf(m, " (%X=Host Adapter)\n", chip_scsi_id);
}
/*
* Display Wide Board BIOS Information.
*/
-static int asc_prt_adv_bios(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_adv_bios(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
- int leftlen;
- int totlen;
- int len;
ushort major, minor, letter;
- leftlen = cplen;
- totlen = len = 0;
-
- len = asc_prt_line(cp, leftlen, "\nROM BIOS Version: ");
- ASC_PRT_NEXT();
+ seq_puts(m, "\nROM BIOS Version: ");
/*
* If the BIOS saved a valid signature, then fill in
* the BIOS code segment base address.
*/
if (boardp->bios_signature != 0x55AA) {
- len = asc_prt_line(cp, leftlen, "Disabled or Pre-3.1\n");
- ASC_PRT_NEXT();
- len = asc_prt_line(cp, leftlen,
- "BIOS either disabled or Pre-3.1. If it is pre-3.1, then a newer version\n");
- ASC_PRT_NEXT();
- len = asc_prt_line(cp, leftlen,
- "can be found at the ConnectCom FTP site: ftp://ftp.connectcom.net/pub\n");
- ASC_PRT_NEXT();
+ seq_puts(m, "Disabled or Pre-3.1\n"
+ "BIOS either disabled or Pre-3.1. If it is pre-3.1, then a newer version\n"
+ "can be found at the ConnectCom FTP site: ftp://ftp.connectcom.net/pub\n");
} else {
major = (boardp->bios_version >> 12) & 0xF;
minor = (boardp->bios_version >> 8) & 0xF;
letter = (boardp->bios_version & 0xFF);
- len = asc_prt_line(cp, leftlen, "%d.%d%c\n",
+ seq_printf(m, "%d.%d%c\n",
major, minor,
letter >= 26 ? '?' : letter + 'A');
- ASC_PRT_NEXT();
-
/*
* Current available ROM BIOS release is 3.1I for UW
* and 3.2I for U2W. This code doesn't differentiate
@@ -3002,16 +2738,10 @@ static int asc_prt_adv_bios(struct Scsi_Host *shost, char *cp, int cplen)
*/
if (major < 3 || (major <= 3 && minor < 1) ||
(major <= 3 && minor <= 1 && letter < ('I' - 'A'))) {
- len = asc_prt_line(cp, leftlen,
- "Newer version of ROM BIOS is available at the ConnectCom FTP site:\n");
- ASC_PRT_NEXT();
- len = asc_prt_line(cp, leftlen,
- "ftp://ftp.connectcom.net/pub\n");
- ASC_PRT_NEXT();
+ seq_puts(m, "Newer version of ROM BIOS is available at the ConnectCom FTP site:\n"
+ "ftp://ftp.connectcom.net/pub\n");
}
}
-
- return totlen;
}
/*
@@ -3116,153 +2846,72 @@ static int asc_get_eeprom_string(ushort *serialnum, uchar *cp)
* asc_prt_asc_board_eeprom()
*
* Print board EEPROM configuration.
- *
- * Note: no single line should be greater than ASC_PRTLINE_SIZE,
- * cf. asc_prt_line().
- *
- * Return the number of characters copied into 'cp'. No more than
- * 'cplen' characters will be copied to 'cp'.
*/
-static int asc_prt_asc_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_asc_board_eeprom(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
- ASC_DVC_VAR *asc_dvc_varp;
- int leftlen;
- int totlen;
- int len;
ASCEEP_CONFIG *ep;
int i;
-#ifdef CONFIG_ISA
- int isa_dma_speed[] = { 10, 8, 7, 6, 5, 4, 3, 2 };
-#endif /* CONFIG_ISA */
uchar serialstr[13];
- asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
ep = &boardp->eep_config.asc_eep;
- leftlen = cplen;
- totlen = len = 0;
-
- len = asc_prt_line(cp, leftlen,
- "\nEEPROM Settings for AdvanSys SCSI Host %d:\n",
- shost->host_no);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "\nEEPROM Settings for AdvanSys SCSI Host %d:\n",
+ shost->host_no);
if (asc_get_eeprom_string((ushort *)&ep->adapter_info[0], serialstr)
- == ASC_TRUE) {
- len =
- asc_prt_line(cp, leftlen, " Serial Number: %s\n",
- serialstr);
- ASC_PRT_NEXT();
- } else {
- if (ep->adapter_info[5] == 0xBB) {
- len = asc_prt_line(cp, leftlen,
- " Default Settings Used for EEPROM-less Adapter.\n");
- ASC_PRT_NEXT();
- } else {
- len = asc_prt_line(cp, leftlen,
- " Serial Number Signature Not Present.\n");
- ASC_PRT_NEXT();
- }
- }
-
- len = asc_prt_line(cp, leftlen,
- " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
- ASC_EEP_GET_CHIP_ID(ep), ep->max_total_qng,
- ep->max_tag_qng);
- ASC_PRT_NEXT();
+ == ASC_TRUE)
+ seq_printf(m, " Serial Number: %s\n", serialstr);
+ else if (ep->adapter_info[5] == 0xBB)
+ seq_puts(m,
+ " Default Settings Used for EEPROM-less Adapter.\n");
+ else
+ seq_puts(m, " Serial Number Signature Not Present.\n");
- len = asc_prt_line(cp, leftlen,
- " cntl 0x%x, no_scam 0x%x\n", ep->cntl, ep->no_scam);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ASC_EEP_GET_CHIP_ID(ep), ep->max_total_qng,
+ ep->max_tag_qng);
- len = asc_prt_line(cp, leftlen, " Target ID: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ASC_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %d", i);
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
-
- len = asc_prt_line(cp, leftlen, " Disconnects: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ASC_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (ep->
- disc_enable & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " cntl 0x%x, no_scam 0x%x\n", ep->cntl, ep->no_scam);
- len = asc_prt_line(cp, leftlen, " Command Queuing: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ASC_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (ep->
- use_cmd_qng & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Target ID: ");
+ for (i = 0; i <= ASC_MAX_TID; i++)
+ seq_printf(m, " %d", i);
- len = asc_prt_line(cp, leftlen, " Start Motor: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ASC_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (ep->
- start_motor & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Disconnects: ");
+ for (i = 0; i <= ASC_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (ep->disc_enable & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
- len = asc_prt_line(cp, leftlen, " Synchronous Transfer:");
- ASC_PRT_NEXT();
- for (i = 0; i <= ASC_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (ep->
- init_sdtr & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Command Queuing: ");
+ for (i = 0; i <= ASC_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (ep->use_cmd_qng & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
-#ifdef CONFIG_ISA
- if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
- len = asc_prt_line(cp, leftlen,
- " Host ISA DMA speed: %d MB/S\n",
- isa_dma_speed[ASC_EEP_GET_DMA_SPD(ep)]);
- ASC_PRT_NEXT();
- }
-#endif /* CONFIG_ISA */
+ seq_puts(m, "\n Start Motor: ");
+ for (i = 0; i <= ASC_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (ep->start_motor & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
- return totlen;
+ seq_puts(m, "\n Synchronous Transfer:");
+ for (i = 0; i <= ASC_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (ep->init_sdtr & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ seq_putc(m, '\n');
}
/*
* asc_prt_adv_board_eeprom()
*
* Print board EEPROM configuration.
- *
- * Note: no single line should be greater than ASC_PRTLINE_SIZE,
- * cf. asc_prt_line().
- *
- * Return the number of characters copied into 'cp'. No more than
- * 'cplen' characters will be copied to 'cp'.
*/
-static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_adv_board_eeprom(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
ADV_DVC_VAR *adv_dvc_varp;
- int leftlen;
- int totlen;
- int len;
int i;
char *termstr;
uchar serialstr[13];
@@ -3282,13 +2931,9 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
ep_38C1600 = &boardp->eep_config.adv_38C1600_eep;
}
- leftlen = cplen;
- totlen = len = 0;
-
- len = asc_prt_line(cp, leftlen,
- "\nEEPROM Settings for AdvanSys SCSI Host %d:\n",
- shost->host_no);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "\nEEPROM Settings for AdvanSys SCSI Host %d:\n",
+ shost->host_no);
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
wordp = &ep_3550->serial_number_word1;
@@ -3298,38 +2943,28 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
wordp = &ep_38C1600->serial_number_word1;
}
- if (asc_get_eeprom_string(wordp, serialstr) == ASC_TRUE) {
- len =
- asc_prt_line(cp, leftlen, " Serial Number: %s\n",
- serialstr);
- ASC_PRT_NEXT();
- } else {
- len = asc_prt_line(cp, leftlen,
- " Serial Number Signature Not Present.\n");
- ASC_PRT_NEXT();
- }
+ if (asc_get_eeprom_string(wordp, serialstr) == ASC_TRUE)
+ seq_printf(m, " Serial Number: %s\n", serialstr);
+ else
+ seq_puts(m, " Serial Number Signature Not Present.\n");
- if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
- len = asc_prt_line(cp, leftlen,
- " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
- ep_3550->adapter_scsi_id,
- ep_3550->max_host_qng, ep_3550->max_dvc_qng);
- ASC_PRT_NEXT();
- } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800) {
- len = asc_prt_line(cp, leftlen,
- " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
- ep_38C0800->adapter_scsi_id,
- ep_38C0800->max_host_qng,
- ep_38C0800->max_dvc_qng);
- ASC_PRT_NEXT();
- } else {
- len = asc_prt_line(cp, leftlen,
- " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
- ep_38C1600->adapter_scsi_id,
- ep_38C1600->max_host_qng,
- ep_38C1600->max_dvc_qng);
- ASC_PRT_NEXT();
- }
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ seq_printf(m,
+ " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep_3550->adapter_scsi_id,
+ ep_3550->max_host_qng, ep_3550->max_dvc_qng);
+ else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ seq_printf(m,
+ " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep_38C0800->adapter_scsi_id,
+ ep_38C0800->max_host_qng,
+ ep_38C0800->max_dvc_qng);
+ else
+ seq_printf(m,
+ " Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep_38C1600->adapter_scsi_id,
+ ep_38C1600->max_host_qng,
+ ep_38C1600->max_dvc_qng);
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
word = ep_3550->termination;
} else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800) {
@@ -3353,34 +2988,26 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
break;
}
- if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
- len = asc_prt_line(cp, leftlen,
- " termination: %u (%s), bios_ctrl: 0x%x\n",
- ep_3550->termination, termstr,
- ep_3550->bios_ctrl);
- ASC_PRT_NEXT();
- } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800) {
- len = asc_prt_line(cp, leftlen,
- " termination: %u (%s), bios_ctrl: 0x%x\n",
- ep_38C0800->termination_lvd, termstr,
- ep_38C0800->bios_ctrl);
- ASC_PRT_NEXT();
- } else {
- len = asc_prt_line(cp, leftlen,
- " termination: %u (%s), bios_ctrl: 0x%x\n",
- ep_38C1600->termination_lvd, termstr,
- ep_38C1600->bios_ctrl);
- ASC_PRT_NEXT();
- }
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ seq_printf(m,
+ " termination: %u (%s), bios_ctrl: 0x%x\n",
+ ep_3550->termination, termstr,
+ ep_3550->bios_ctrl);
+ else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ seq_printf(m,
+ " termination: %u (%s), bios_ctrl: 0x%x\n",
+ ep_38C0800->termination_lvd, termstr,
+ ep_38C0800->bios_ctrl);
+ else
+ seq_printf(m,
+ " termination: %u (%s), bios_ctrl: 0x%x\n",
+ ep_38C1600->termination_lvd, termstr,
+ ep_38C1600->bios_ctrl);
- len = asc_prt_line(cp, leftlen, " Target ID: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ADV_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %X", i);
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Target ID: ");
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ seq_printf(m, " %X", i);
+ seq_putc(m, '\n');
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
word = ep_3550->disc_enable;
@@ -3389,15 +3016,11 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
} else {
word = ep_38C1600->disc_enable;
}
- len = asc_prt_line(cp, leftlen, " Disconnects: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ADV_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Disconnects: ");
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ seq_putc(m, '\n');
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
word = ep_3550->tagqng_able;
@@ -3406,15 +3029,11 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
} else {
word = ep_38C1600->tagqng_able;
}
- len = asc_prt_line(cp, leftlen, " Command Queuing: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ADV_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Command Queuing: ");
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ seq_putc(m, '\n');
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
word = ep_3550->start_motor;
@@ -3423,42 +3042,28 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
} else {
word = ep_38C1600->start_motor;
}
- len = asc_prt_line(cp, leftlen, " Start Motor: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ADV_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Start Motor: ");
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ seq_putc(m, '\n');
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
- len = asc_prt_line(cp, leftlen, " Synchronous Transfer:");
- ASC_PRT_NEXT();
- for (i = 0; i <= ADV_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (ep_3550->
- sdtr_able & ADV_TID_TO_TIDMASK(i)) ?
- 'Y' : 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Synchronous Transfer:");
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (ep_3550->sdtr_able & ADV_TID_TO_TIDMASK(i)) ?
+ 'Y' : 'N');
+ seq_putc(m, '\n');
}
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
- len = asc_prt_line(cp, leftlen, " Ultra Transfer: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ADV_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (ep_3550->
- ultra_able & ADV_TID_TO_TIDMASK(i))
- ? 'Y' : 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Ultra Transfer: ");
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (ep_3550->ultra_able & ADV_TID_TO_TIDMASK(i))
+ ? 'Y' : 'N');
+ seq_putc(m, '\n');
}
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550) {
@@ -3468,21 +3073,15 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
} else {
word = ep_38C1600->wdtr_able;
}
- len = asc_prt_line(cp, leftlen, " Wide Transfer: ");
- ASC_PRT_NEXT();
- for (i = 0; i <= ADV_MAX_TID; i++) {
- len = asc_prt_line(cp, leftlen, " %c",
- (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
- ASC_PRT_NEXT();
- }
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " Wide Transfer: ");
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ seq_printf(m, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ seq_putc(m, '\n');
if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800 ||
adv_dvc_varp->chip_type == ADV_CHIP_ASC38C1600) {
- len = asc_prt_line(cp, leftlen,
- " Synchronous Transfer Speed (Mhz):\n ");
- ASC_PRT_NEXT();
+ seq_puts(m, " Synchronous Transfer Speed (Mhz):\n ");
for (i = 0; i <= ADV_MAX_TID; i++) {
char *speed_str;
@@ -3518,99 +3117,53 @@ static int asc_prt_adv_board_eeprom(struct Scsi_Host *shost, char *cp, int cplen
speed_str = "Unk";
break;
}
- len = asc_prt_line(cp, leftlen, "%X:%s ", i, speed_str);
- ASC_PRT_NEXT();
- if (i == 7) {
- len = asc_prt_line(cp, leftlen, "\n ");
- ASC_PRT_NEXT();
- }
+ seq_printf(m, "%X:%s ", i, speed_str);
+ if (i == 7)
+ seq_puts(m, "\n ");
sdtr_speed >>= 4;
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_putc(m, '\n');
}
-
- return totlen;
}
/*
* asc_prt_driver_conf()
- *
- * Note: no single line should be greater than ASC_PRTLINE_SIZE,
- * cf. asc_prt_line().
- *
- * Return the number of characters copied into 'cp'. No more than
- * 'cplen' characters will be copied to 'cp'.
*/
-static int asc_prt_driver_conf(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_driver_conf(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
- int leftlen;
- int totlen;
- int len;
- int chip_scsi_id;
-
- leftlen = cplen;
- totlen = len = 0;
- len = asc_prt_line(cp, leftlen,
- "\nLinux Driver Configuration and Information for AdvanSys SCSI Host %d:\n",
- shost->host_no);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "\nLinux Driver Configuration and Information for AdvanSys SCSI Host %d:\n",
+ shost->host_no);
- len = asc_prt_line(cp, leftlen,
- " host_busy %u, last_reset %u, max_id %u, max_lun %u, max_channel %u\n",
- shost->host_busy, shost->last_reset, shost->max_id,
- shost->max_lun, shost->max_channel);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " host_busy %d, max_id %u, max_lun %llu, max_channel %u\n",
+ scsi_host_busy(shost), shost->max_id,
+ shost->max_lun, shost->max_channel);
- len = asc_prt_line(cp, leftlen,
- " unique_id %d, can_queue %d, this_id %d, sg_tablesize %u, cmd_per_lun %u\n",
- shost->unique_id, shost->can_queue, shost->this_id,
- shost->sg_tablesize, shost->cmd_per_lun);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " unique_id %d, can_queue %d, this_id %d, sg_tablesize %u, cmd_per_lun %u\n",
+ shost->unique_id, shost->can_queue, shost->this_id,
+ shost->sg_tablesize, shost->cmd_per_lun);
- len = asc_prt_line(cp, leftlen,
- " unchecked_isa_dma %d, use_clustering %d\n",
- shost->unchecked_isa_dma, shost->use_clustering);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " flags 0x%x, last_reset 0x%lx, jiffies 0x%lx, asc_n_io_port 0x%x\n",
+ boardp->flags, shost->last_reset, jiffies,
+ boardp->asc_n_io_port);
- len = asc_prt_line(cp, leftlen,
- " flags 0x%x, last_reset 0x%x, jiffies 0x%x, asc_n_io_port 0x%x\n",
- boardp->flags, boardp->last_reset, jiffies,
- boardp->asc_n_io_port);
- ASC_PRT_NEXT();
-
- len = asc_prt_line(cp, leftlen, " io_port 0x%x\n", shost->io_port);
- ASC_PRT_NEXT();
-
- if (ASC_NARROW_BOARD(boardp)) {
- chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
- } else {
- chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
- }
-
- return totlen;
+ seq_printf(m, " io_port 0x%lx\n", shost->io_port);
}
/*
* asc_prt_asc_board_info()
*
* Print dynamic board configuration information.
- *
- * Note: no single line should be greater than ASC_PRTLINE_SIZE,
- * cf. asc_prt_line().
- *
- * Return the number of characters copied into 'cp'. No more than
- * 'cplen' characters will be copied to 'cp'.
*/
-static int asc_prt_asc_board_info(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_asc_board_info(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
int chip_scsi_id;
- int leftlen;
- int totlen;
- int len;
ASC_DVC_VAR *v;
ASC_DVC_CFG *c;
int i;
@@ -3620,105 +3173,75 @@ static int asc_prt_asc_board_info(struct Scsi_Host *shost, char *cp, int cplen)
c = &boardp->dvc_cfg.asc_dvc_cfg;
chip_scsi_id = c->chip_scsi_id;
- leftlen = cplen;
- totlen = len = 0;
-
- len = asc_prt_line(cp, leftlen,
- "\nAsc Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
- shost->host_no);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "\nAsc Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
+ shost->host_no);
- len = asc_prt_line(cp, leftlen, " chip_version %u, mcode_date 0x%x, "
- "mcode_version 0x%x, err_code %u\n",
- c->chip_version, c->mcode_date, c->mcode_version,
- v->err_code);
- ASC_PRT_NEXT();
+ seq_printf(m, " chip_version %u, mcode_date 0x%x, "
+ "mcode_version 0x%x, err_code %u\n",
+ c->chip_version, c->mcode_date, c->mcode_version,
+ v->err_code);
/* Current number of commands waiting for the host. */
- len = asc_prt_line(cp, leftlen,
- " Total Command Pending: %d\n", v->cur_total_qng);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " Total Command Pending: %d\n", v->cur_total_qng);
- len = asc_prt_line(cp, leftlen, " Command Queuing:");
- ASC_PRT_NEXT();
+ seq_puts(m, " Command Queuing:");
for (i = 0; i <= ASC_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:%c",
- i,
- (v->
- use_tagged_qng & ADV_TID_TO_TIDMASK(i)) ?
- 'Y' : 'N');
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%c",
+ i,
+ (v->use_tagged_qng & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
/* Current number of commands waiting for a device. */
- len = asc_prt_line(cp, leftlen, " Command Queue Pending:");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Command Queue Pending:");
for (i = 0; i <= ASC_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:%u", i, v->cur_dvc_qng[i]);
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%u", i, v->cur_dvc_qng[i]);
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
/* Current limit on number of commands that can be sent to a device. */
- len = asc_prt_line(cp, leftlen, " Command Queue Limit:");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Command Queue Limit:");
for (i = 0; i <= ASC_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:%u", i, v->max_dvc_qng[i]);
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%u", i, v->max_dvc_qng[i]);
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
/* Indicate whether the device has returned queue full status. */
- len = asc_prt_line(cp, leftlen, " Command Queue Full:");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Command Queue Full:");
for (i = 0; i <= ASC_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- if (boardp->queue_full & ADV_TID_TO_TIDMASK(i)) {
- len = asc_prt_line(cp, leftlen, " %X:Y-%d",
- i, boardp->queue_full_cnt[i]);
- } else {
- len = asc_prt_line(cp, leftlen, " %X:N", i);
- }
- ASC_PRT_NEXT();
+ if (boardp->queue_full & ADV_TID_TO_TIDMASK(i))
+ seq_printf(m, " %X:Y-%d",
+ i, boardp->queue_full_cnt[i]);
+ else
+ seq_printf(m, " %X:N", i);
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
- len = asc_prt_line(cp, leftlen, " Synchronous Transfer:");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Synchronous Transfer:");
for (i = 0; i <= ASC_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:%c",
- i,
- (v->
- sdtr_done & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%c",
+ i,
+ (v->sdtr_done & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_putc(m, '\n');
for (i = 0; i <= ASC_MAX_TID; i++) {
uchar syn_period_ix;
@@ -3729,69 +3252,47 @@ static int asc_prt_asc_board_info(struct Scsi_Host *shost, char *cp, int cplen)
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:", i);
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:", i);
if ((boardp->sdtr_data[i] & ASC_SYN_MAX_OFFSET) == 0) {
- len = asc_prt_line(cp, leftlen, " Asynchronous");
- ASC_PRT_NEXT();
+ seq_puts(m, " Asynchronous");
} else {
syn_period_ix =
(boardp->sdtr_data[i] >> 4) & (v->max_sdtr_index -
1);
- len = asc_prt_line(cp, leftlen,
- " Transfer Period Factor: %d (%d.%d Mhz),",
- v->sdtr_period_tbl[syn_period_ix],
- 250 /
- v->sdtr_period_tbl[syn_period_ix],
- ASC_TENTHS(250,
- v->
- sdtr_period_tbl
- [syn_period_ix]));
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " Transfer Period Factor: %d (%d.%d Mhz),",
+ v->sdtr_period_tbl[syn_period_ix],
+ 250 / v->sdtr_period_tbl[syn_period_ix],
+ ASC_TENTHS(250,
+ v->sdtr_period_tbl[syn_period_ix]));
- len = asc_prt_line(cp, leftlen, " REQ/ACK Offset: %d",
- boardp->
- sdtr_data[i] & ASC_SYN_MAX_OFFSET);
- ASC_PRT_NEXT();
+ seq_printf(m, " REQ/ACK Offset: %d",
+ boardp->sdtr_data[i] & ASC_SYN_MAX_OFFSET);
}
if ((v->sdtr_done & ADV_TID_TO_TIDMASK(i)) == 0) {
- len = asc_prt_line(cp, leftlen, "*\n");
+ seq_puts(m, "*\n");
renegotiate = 1;
} else {
- len = asc_prt_line(cp, leftlen, "\n");
+ seq_putc(m, '\n');
}
- ASC_PRT_NEXT();
}
if (renegotiate) {
- len = asc_prt_line(cp, leftlen,
- " * = Re-negotiation pending before next command.\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " * = Re-negotiation pending before next command.\n");
}
-
- return totlen;
}
/*
* asc_prt_adv_board_info()
*
* Print dynamic board configuration information.
- *
- * Note: no single line should be greater than ASC_PRTLINE_SIZE,
- * cf. asc_prt_line().
- *
- * Return the number of characters copied into 'cp'. No more than
- * 'cplen' characters will be copied to 'cp'.
*/
-static int asc_prt_adv_board_info(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_adv_board_info(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
- int leftlen;
- int totlen;
- int len;
int i;
ADV_DVC_VAR *v;
ADV_DVC_CFG *c;
@@ -3810,47 +3311,34 @@ static int asc_prt_adv_board_info(struct Scsi_Host *shost, char *cp, int cplen)
iop_base = v->iop_base;
chip_scsi_id = v->chip_scsi_id;
- leftlen = cplen;
- totlen = len = 0;
-
- len = asc_prt_line(cp, leftlen,
- "\nAdv Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
- shost->host_no);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "\nAdv Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
+ shost->host_no);
- len = asc_prt_line(cp, leftlen,
- " iop_base 0x%lx, cable_detect: %X, err_code %u\n",
- v->iop_base,
- AdvReadWordRegister(iop_base,
- IOPW_SCSI_CFG1) & CABLE_DETECT,
- v->err_code);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " iop_base 0x%p, cable_detect: %X, err_code %u\n",
+ v->iop_base,
+ AdvReadWordRegister(iop_base,IOPW_SCSI_CFG1) & CABLE_DETECT,
+ v->err_code);
- len = asc_prt_line(cp, leftlen, " chip_version %u, mcode_date 0x%x, "
- "mcode_version 0x%x\n", c->chip_version,
- c->mcode_date, c->mcode_version);
- ASC_PRT_NEXT();
+ seq_printf(m, " chip_version %u, mcode_date 0x%x, "
+ "mcode_version 0x%x\n", c->chip_version,
+ c->mcode_date, c->mcode_version);
AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
- len = asc_prt_line(cp, leftlen, " Queuing Enabled:");
- ASC_PRT_NEXT();
+ seq_puts(m, " Queuing Enabled:");
for (i = 0; i <= ADV_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:%c",
- i,
- (tagqng_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%c",
+ i,
+ (tagqng_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
- len = asc_prt_line(cp, leftlen, " Queue Limit:");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Queue Limit:");
for (i = 0; i <= ADV_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
@@ -3860,14 +3348,10 @@ static int asc_prt_adv_board_info(struct Scsi_Host *shost, char *cp, int cplen)
AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + i,
lrambyte);
- len = asc_prt_line(cp, leftlen, " %X:%d", i, lrambyte);
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%d", i, lrambyte);
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
- len = asc_prt_line(cp, leftlen, " Command Pending:");
- ASC_PRT_NEXT();
+ seq_puts(m, "\n Command Pending:");
for (i = 0; i <= ADV_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
@@ -3877,33 +3361,26 @@ static int asc_prt_adv_board_info(struct Scsi_Host *shost, char *cp, int cplen)
AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_QUEUED_CMD + i,
lrambyte);
- len = asc_prt_line(cp, leftlen, " %X:%d", i, lrambyte);
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%d", i, lrambyte);
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_putc(m, '\n');
AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
- len = asc_prt_line(cp, leftlen, " Wide Enabled:");
- ASC_PRT_NEXT();
+ seq_puts(m, " Wide Enabled:");
for (i = 0; i <= ADV_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:%c",
- i,
- (wdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%c",
+ i,
+ (wdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_putc(m, '\n');
AdvReadWordLram(iop_base, ASC_MC_WDTR_DONE, wdtr_done);
- len = asc_prt_line(cp, leftlen, " Transfer Bit Width:");
- ASC_PRT_NEXT();
+ seq_puts(m, " Transfer Bit Width:");
for (i = 0; i <= ADV_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
@@ -3914,37 +3391,30 @@ static int asc_prt_adv_board_info(struct Scsi_Host *shost, char *cp, int cplen)
ASC_MC_DEVICE_HSHK_CFG_TABLE + (2 * i),
lramword);
- len = asc_prt_line(cp, leftlen, " %X:%d",
- i, (lramword & 0x8000) ? 16 : 8);
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%d",
+ i, (lramword & 0x8000) ? 16 : 8);
if ((wdtr_able & ADV_TID_TO_TIDMASK(i)) &&
(wdtr_done & ADV_TID_TO_TIDMASK(i)) == 0) {
- len = asc_prt_line(cp, leftlen, "*");
- ASC_PRT_NEXT();
+ seq_putc(m, '*');
renegotiate = 1;
}
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_putc(m, '\n');
AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
- len = asc_prt_line(cp, leftlen, " Synchronous Enabled:");
- ASC_PRT_NEXT();
+ seq_puts(m, " Synchronous Enabled:");
for (i = 0; i <= ADV_MAX_TID; i++) {
if ((chip_scsi_id == i) ||
((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:%c",
- i,
- (sdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' :
- 'N');
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:%c",
+ i,
+ (sdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
}
- len = asc_prt_line(cp, leftlen, "\n");
- ASC_PRT_NEXT();
+ seq_putc(m, '\n');
AdvReadWordLram(iop_base, ASC_MC_SDTR_DONE, sdtr_done);
for (i = 0; i <= ADV_MAX_TID; i++) {
@@ -3960,358 +3430,169 @@ static int asc_prt_adv_board_info(struct Scsi_Host *shost, char *cp, int cplen)
continue;
}
- len = asc_prt_line(cp, leftlen, " %X:", i);
- ASC_PRT_NEXT();
+ seq_printf(m, " %X:", i);
if ((lramword & 0x1F) == 0) { /* Check for REQ/ACK Offset 0. */
- len = asc_prt_line(cp, leftlen, " Asynchronous");
- ASC_PRT_NEXT();
+ seq_puts(m, " Asynchronous");
} else {
- len =
- asc_prt_line(cp, leftlen,
- " Transfer Period Factor: ");
- ASC_PRT_NEXT();
+ seq_puts(m, " Transfer Period Factor: ");
if ((lramword & 0x1F00) == 0x1100) { /* 80 Mhz */
- len =
- asc_prt_line(cp, leftlen, "9 (80.0 Mhz),");
- ASC_PRT_NEXT();
+ seq_puts(m, "9 (80.0 Mhz),");
} else if ((lramword & 0x1F00) == 0x1000) { /* 40 Mhz */
- len =
- asc_prt_line(cp, leftlen, "10 (40.0 Mhz),");
- ASC_PRT_NEXT();
+ seq_puts(m, "10 (40.0 Mhz),");
} else { /* 20 Mhz or below. */
period = (((lramword >> 8) * 25) + 50) / 4;
if (period == 0) { /* Should never happen. */
- len =
- asc_prt_line(cp, leftlen,
- "%d (? Mhz), ");
- ASC_PRT_NEXT();
+ seq_printf(m, "%d (? Mhz), ", period);
} else {
- len = asc_prt_line(cp, leftlen,
- "%d (%d.%d Mhz),",
- period, 250 / period,
- ASC_TENTHS(250,
- period));
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "%d (%d.%d Mhz),",
+ period, 250 / period,
+ ASC_TENTHS(250, period));
}
}
- len = asc_prt_line(cp, leftlen, " REQ/ACK Offset: %d",
- lramword & 0x1F);
- ASC_PRT_NEXT();
+ seq_printf(m, " REQ/ACK Offset: %d",
+ lramword & 0x1F);
}
if ((sdtr_done & ADV_TID_TO_TIDMASK(i)) == 0) {
- len = asc_prt_line(cp, leftlen, "*\n");
+ seq_puts(m, "*\n");
renegotiate = 1;
} else {
- len = asc_prt_line(cp, leftlen, "\n");
+ seq_putc(m, '\n');
}
- ASC_PRT_NEXT();
}
if (renegotiate) {
- len = asc_prt_line(cp, leftlen,
- " * = Re-negotiation pending before next command.\n");
- ASC_PRT_NEXT();
+ seq_puts(m, " * = Re-negotiation pending before next command.\n");
}
-
- return totlen;
-}
-
-/*
- * asc_proc_copy()
- *
- * Copy proc information to a read buffer taking into account the current
- * read offset in the file and the remaining space in the read buffer.
- */
-static int
-asc_proc_copy(off_t advoffset, off_t offset, char *curbuf, int leftlen,
- char *cp, int cplen)
-{
- int cnt = 0;
-
- ASC_DBG(2, "offset %d, advoffset %d, cplen %d\n",
- (unsigned)offset, (unsigned)advoffset, cplen);
- if (offset <= advoffset) {
- /* Read offset below current offset, copy everything. */
- cnt = min(cplen, leftlen);
- ASC_DBG(2, "curbuf 0x%lx, cp 0x%lx, cnt %d\n",
- (ulong)curbuf, (ulong)cp, cnt);
- memcpy(curbuf, cp, cnt);
- } else if (offset < advoffset + cplen) {
- /* Read offset within current range, partial copy. */
- cnt = (advoffset + cplen) - offset;
- cp = (cp + cplen) - cnt;
- cnt = min(cnt, leftlen);
- ASC_DBG(2, "curbuf 0x%lx, cp 0x%lx, cnt %d\n",
- (ulong)curbuf, (ulong)cp, cnt);
- memcpy(curbuf, cp, cnt);
- }
- return cnt;
}
#ifdef ADVANSYS_STATS
/*
* asc_prt_board_stats()
- *
- * Note: no single line should be greater than ASC_PRTLINE_SIZE,
- * cf. asc_prt_line().
- *
- * Return the number of characters copied into 'cp'. No more than
- * 'cplen' characters will be copied to 'cp'.
*/
-static int asc_prt_board_stats(struct Scsi_Host *shost, char *cp, int cplen)
+static void asc_prt_board_stats(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
struct asc_stats *s = &boardp->asc_stats;
- int leftlen = cplen;
- int len, totlen = 0;
-
- len = asc_prt_line(cp, leftlen,
- "\nLinux Driver Statistics for AdvanSys SCSI Host %d:\n",
- shost->host_no);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ "\nLinux Driver Statistics for AdvanSys SCSI Host %d:\n",
+ shost->host_no);
- len = asc_prt_line(cp, leftlen,
- " queuecommand %lu, reset %lu, biosparam %lu, interrupt %lu\n",
- s->queuecommand, s->reset, s->biosparam,
- s->interrupt);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " queuecommand %u, reset %u, biosparam %u, interrupt %u\n",
+ s->queuecommand, s->reset, s->biosparam,
+ s->interrupt);
- len = asc_prt_line(cp, leftlen,
- " callback %lu, done %lu, build_error %lu, build_noreq %lu, build_nosg %lu\n",
- s->callback, s->done, s->build_error,
- s->adv_build_noreq, s->adv_build_nosg);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " callback %u, done %u, build_error %u, build_noreq %u, build_nosg %u\n",
+ s->callback, s->done, s->build_error,
+ s->adv_build_noreq, s->adv_build_nosg);
- len = asc_prt_line(cp, leftlen,
- " exe_noerror %lu, exe_busy %lu, exe_error %lu, exe_unknown %lu\n",
- s->exe_noerror, s->exe_busy, s->exe_error,
- s->exe_unknown);
- ASC_PRT_NEXT();
+ seq_printf(m,
+ " exe_noerror %u, exe_busy %u, exe_error %u, exe_unknown %u\n",
+ s->exe_noerror, s->exe_busy, s->exe_error,
+ s->exe_unknown);
/*
* Display data transfer statistics.
*/
if (s->xfer_cnt > 0) {
- len = asc_prt_line(cp, leftlen, " xfer_cnt %lu, xfer_elem %lu, ",
- s->xfer_cnt, s->xfer_elem);
- ASC_PRT_NEXT();
+ seq_printf(m, " xfer_cnt %u, xfer_elem %u, ",
+ s->xfer_cnt, s->xfer_elem);
- len = asc_prt_line(cp, leftlen, "xfer_bytes %lu.%01lu kb\n",
- s->xfer_sect / 2, ASC_TENTHS(s->xfer_sect, 2));
- ASC_PRT_NEXT();
+ seq_printf(m, "xfer_bytes %u.%01u kb\n",
+ s->xfer_sect / 2, ASC_TENTHS(s->xfer_sect, 2));
/* Scatter gather transfer statistics */
- len = asc_prt_line(cp, leftlen, " avg_num_elem %lu.%01lu, ",
- s->xfer_elem / s->xfer_cnt,
- ASC_TENTHS(s->xfer_elem, s->xfer_cnt));
- ASC_PRT_NEXT();
+ seq_printf(m, " avg_num_elem %u.%01u, ",
+ s->xfer_elem / s->xfer_cnt,
+ ASC_TENTHS(s->xfer_elem, s->xfer_cnt));
- len = asc_prt_line(cp, leftlen, "avg_elem_size %lu.%01lu kb, ",
- (s->xfer_sect / 2) / s->xfer_elem,
- ASC_TENTHS((s->xfer_sect / 2), s->xfer_elem));
- ASC_PRT_NEXT();
+ seq_printf(m, "avg_elem_size %u.%01u kb, ",
+ (s->xfer_sect / 2) / s->xfer_elem,
+ ASC_TENTHS((s->xfer_sect / 2), s->xfer_elem));
- len = asc_prt_line(cp, leftlen, "avg_xfer_size %lu.%01lu kb\n",
- (s->xfer_sect / 2) / s->xfer_cnt,
- ASC_TENTHS((s->xfer_sect / 2), s->xfer_cnt));
- ASC_PRT_NEXT();
+ seq_printf(m, "avg_xfer_size %u.%01u kb\n",
+ (s->xfer_sect / 2) / s->xfer_cnt,
+ ASC_TENTHS((s->xfer_sect / 2), s->xfer_cnt));
}
-
- return totlen;
}
#endif /* ADVANSYS_STATS */
/*
- * advansys_proc_info() - /proc/scsi/advansys/{0,1,2,3,...}
+ * advansys_show_info() - /proc/scsi/advansys/{0,1,2,3,...}
*
- * *buffer: I/O buffer
- * **start: if inout == FALSE pointer into buffer where user read should start
- * offset: current offset into a /proc/scsi/advansys/[0...] file
- * length: length of buffer
- * hostno: Scsi_Host host_no
- * inout: TRUE - user is writing; FALSE - user is reading
+ * m: seq_file to print into
+ * shost: Scsi_Host
*
* Return the number of bytes read from or written to a
* /proc/scsi/advansys/[0...] file.
- *
- * Note: This function uses the per board buffer 'prtbuf' which is
- * allocated when the board is initialized in advansys_detect(). The
- * buffer is ASC_PRTBUF_SIZE bytes. The function asc_proc_copy() is
- * used to write to the buffer. The way asc_proc_copy() is written
- * if 'prtbuf' is too small it will not be overwritten. Instead the
- * user just won't get all the available statistics.
*/
static int
-advansys_proc_info(struct Scsi_Host *shost, char *buffer, char **start,
- off_t offset, int length, int inout)
+advansys_show_info(struct seq_file *m, struct Scsi_Host *shost)
{
struct asc_board *boardp = shost_priv(shost);
- char *cp;
- int cplen;
- int cnt;
- int totcnt;
- int leftlen;
- char *curbuf;
- off_t advoffset;
ASC_DBG(1, "begin\n");
/*
- * User write not supported.
- */
- if (inout == TRUE)
- return -ENOSYS;
-
- /*
* User read of /proc/scsi/advansys/[0...] file.
*/
- /* Copy read data starting at the beginning of the buffer. */
- *start = buffer;
- curbuf = buffer;
- advoffset = 0;
- totcnt = 0;
- leftlen = length;
-
/*
* Get board configuration information.
*
* advansys_info() returns the board string from its own static buffer.
*/
- cp = (char *)advansys_info(shost);
- strcat(cp, "\n");
- cplen = strlen(cp);
/* Copy board information. */
- cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
- totcnt += cnt;
- leftlen -= cnt;
- if (leftlen == 0) {
- ASC_DBG(1, "totcnt %d\n", totcnt);
- return totcnt;
- }
- advoffset += cplen;
- curbuf += cnt;
-
+ seq_printf(m, "%s\n", (char *)advansys_info(shost));
/*
* Display Wide Board BIOS Information.
*/
- if (!ASC_NARROW_BOARD(boardp)) {
- cp = boardp->prtbuf;
- cplen = asc_prt_adv_bios(shost, cp, ASC_PRTBUF_SIZE);
- BUG_ON(cplen >= ASC_PRTBUF_SIZE);
- cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp,
- cplen);
- totcnt += cnt;
- leftlen -= cnt;
- if (leftlen == 0) {
- ASC_DBG(1, "totcnt %d\n", totcnt);
- return totcnt;
- }
- advoffset += cplen;
- curbuf += cnt;
- }
+ if (!ASC_NARROW_BOARD(boardp))
+ asc_prt_adv_bios(m, shost);
/*
* Display driver information for each device attached to the board.
*/
- cp = boardp->prtbuf;
- cplen = asc_prt_board_devices(shost, cp, ASC_PRTBUF_SIZE);
- BUG_ON(cplen >= ASC_PRTBUF_SIZE);
- cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
- totcnt += cnt;
- leftlen -= cnt;
- if (leftlen == 0) {
- ASC_DBG(1, "totcnt %d\n", totcnt);
- return totcnt;
- }
- advoffset += cplen;
- curbuf += cnt;
+ asc_prt_board_devices(m, shost);
/*
* Display EEPROM configuration for the board.
*/
- cp = boardp->prtbuf;
- if (ASC_NARROW_BOARD(boardp)) {
- cplen = asc_prt_asc_board_eeprom(shost, cp, ASC_PRTBUF_SIZE);
- } else {
- cplen = asc_prt_adv_board_eeprom(shost, cp, ASC_PRTBUF_SIZE);
- }
- BUG_ON(cplen >= ASC_PRTBUF_SIZE);
- cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
- totcnt += cnt;
- leftlen -= cnt;
- if (leftlen == 0) {
- ASC_DBG(1, "totcnt %d\n", totcnt);
- return totcnt;
- }
- advoffset += cplen;
- curbuf += cnt;
+ if (ASC_NARROW_BOARD(boardp))
+ asc_prt_asc_board_eeprom(m, shost);
+ else
+ asc_prt_adv_board_eeprom(m, shost);
/*
* Display driver configuration and information for the board.
*/
- cp = boardp->prtbuf;
- cplen = asc_prt_driver_conf(shost, cp, ASC_PRTBUF_SIZE);
- BUG_ON(cplen >= ASC_PRTBUF_SIZE);
- cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
- totcnt += cnt;
- leftlen -= cnt;
- if (leftlen == 0) {
- ASC_DBG(1, "totcnt %d\n", totcnt);
- return totcnt;
- }
- advoffset += cplen;
- curbuf += cnt;
+ asc_prt_driver_conf(m, shost);
#ifdef ADVANSYS_STATS
/*
* Display driver statistics for the board.
*/
- cp = boardp->prtbuf;
- cplen = asc_prt_board_stats(shost, cp, ASC_PRTBUF_SIZE);
- BUG_ON(cplen >= ASC_PRTBUF_SIZE);
- cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
- totcnt += cnt;
- leftlen -= cnt;
- if (leftlen == 0) {
- ASC_DBG(1, "totcnt %d\n", totcnt);
- return totcnt;
- }
- advoffset += cplen;
- curbuf += cnt;
+ asc_prt_board_stats(m, shost);
#endif /* ADVANSYS_STATS */
/*
* Display Asc Library dynamic configuration information
* for the board.
*/
- cp = boardp->prtbuf;
- if (ASC_NARROW_BOARD(boardp)) {
- cplen = asc_prt_asc_board_info(shost, cp, ASC_PRTBUF_SIZE);
- } else {
- cplen = asc_prt_adv_board_info(shost, cp, ASC_PRTBUF_SIZE);
- }
- BUG_ON(cplen >= ASC_PRTBUF_SIZE);
- cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
- totcnt += cnt;
- leftlen -= cnt;
- if (leftlen == 0) {
- ASC_DBG(1, "totcnt %d\n", totcnt);
- return totcnt;
- }
- advoffset += cplen;
- curbuf += cnt;
-
- ASC_DBG(1, "totcnt %d\n", totcnt);
-
- return totcnt;
+ if (ASC_NARROW_BOARD(boardp))
+ asc_prt_asc_board_info(m, shost);
+ else
+ asc_prt_adv_board_info(m, shost);
+ return 0;
}
#endif /* CONFIG_PROC_FS */
@@ -4319,7 +3600,7 @@ static void asc_scsi_done(struct scsi_cmnd *scp)
{
scsi_dma_unmap(scp);
ASC_STATS(scp->device->host, done);
- scp->scsi_done(scp);
+ scsi_done(scp);
}
static void AscSetBank(PortAddr iop_base, uchar bank)
@@ -4356,7 +3637,7 @@ static int AscStartChip(PortAddr iop_base)
return (1);
}
-static int AscStopChip(PortAddr iop_base)
+static bool AscStopChip(PortAddr iop_base)
{
uchar cc_val;
@@ -4367,22 +3648,22 @@ static int AscStopChip(PortAddr iop_base)
AscSetChipIH(iop_base, INS_HALT);
AscSetChipIH(iop_base, INS_RFLAG_WTM);
if ((AscGetChipStatus(iop_base) & CSW_HALTED) == 0) {
- return (0);
+ return false;
}
- return (1);
+ return true;
}
-static int AscIsChipHalted(PortAddr iop_base)
+static bool AscIsChipHalted(PortAddr iop_base)
{
if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
if ((AscGetChipControl(iop_base) & CC_HALT) != 0) {
- return (1);
+ return true;
}
}
- return (0);
+ return false;
}
-static int AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc)
+static bool AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc)
{
PortAddr iop_base;
int i = 10;
@@ -4465,20 +3746,6 @@ static ushort AscReadLramWord(PortAddr iop_base, ushort addr)
return (word_data);
}
-#if CC_VERY_LONG_SG_LIST
-static ASC_DCNT AscReadLramDWord(PortAddr iop_base, ushort addr)
-{
- ushort val_low, val_high;
- ASC_DCNT dword_data;
-
- AscSetChipLramAddr(iop_base, addr);
- val_low = AscGetChipLramData(iop_base);
- val_high = AscGetChipLramData(iop_base);
- dword_data = ((ASC_DCNT) val_high << 16) | (ASC_DCNT) val_low;
- return (dword_data);
-}
-#endif /* CC_VERY_LONG_SG_LIST */
-
static void
AscMemWordSetLram(PortAddr iop_base, ushort s_addr, ushort set_wval, int words)
{
@@ -4580,27 +3847,24 @@ AscMemWordCopyPtrFromLram(PortAddr iop_base,
}
}
-static ASC_DCNT AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words)
+static u32 AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words)
{
- ASC_DCNT sum;
+ u32 sum = 0;
int i;
- sum = 0L;
for (i = 0; i < words; i++, s_addr += 2) {
sum += AscReadLramWord(iop_base, s_addr);
}
return (sum);
}
-static ushort AscInitLram(ASC_DVC_VAR *asc_dvc)
+static void AscInitLram(ASC_DVC_VAR *asc_dvc)
{
uchar i;
ushort s_addr;
PortAddr iop_base;
- ushort warn_code;
iop_base = asc_dvc->iop_base;
- warn_code = 0;
AscMemWordSetLram(iop_base, ASC_QADR_BEG, 0,
(ushort)(((int)(asc_dvc->max_total_qng + 2 + 1) *
64) >> 1));
@@ -4639,14 +3903,13 @@ static ushort AscInitLram(ASC_DVC_VAR *asc_dvc)
AscWriteLramByte(iop_base,
(ushort)(s_addr + (ushort)ASC_SCSIQ_B_QNO), i);
}
- return warn_code;
}
-static ASC_DCNT
+static u32
AscLoadMicroCode(PortAddr iop_base, ushort s_addr,
const uchar *mcode_buf, ushort mcode_size)
{
- ASC_DCNT chksum;
+ u32 chksum;
ushort mcode_word_size;
ushort mcode_chksum;
@@ -4698,13 +3961,13 @@ static void AscInitQLinkVar(ASC_DVC_VAR *asc_dvc)
}
}
-static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
+static int AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
{
int i;
- ushort warn_code;
+ int warn_code;
PortAddr iop_base;
- ASC_PADDR phy_addr;
- ASC_DCNT phy_size;
+ __le32 phy_addr;
+ __le32 phy_size;
struct asc_board *board = asc_dvc_to_board(asc_dvc);
iop_base = asc_dvc->iop_base;
@@ -4743,12 +4006,12 @@ static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
- warn_code = UW_ERR;
+ warn_code = -EINVAL;
goto err_mcode_start;
}
if (AscStartChip(iop_base) != 1) {
asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
- warn_code = UW_ERR;
+ warn_code = -EIO;
goto err_mcode_start;
}
@@ -4762,13 +4025,13 @@ err_dma_map:
return warn_code;
}
-static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
+static int AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
{
const struct firmware *fw;
const char fwname[] = "advansys/mcode.bin";
int err;
unsigned long chksum;
- ushort warn_code;
+ int warn_code;
PortAddr iop_base;
iop_base = asc_dvc->iop_base;
@@ -4780,15 +4043,13 @@ static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
}
asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC;
if (asc_dvc->err_code != 0)
- return UW_ERR;
+ return ASC_ERROR;
if (!AscFindSignature(asc_dvc->iop_base)) {
asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
return warn_code;
}
AscDisableInterrupt(iop_base);
- warn_code |= AscInitLram(asc_dvc);
- if (asc_dvc->err_code != 0)
- return UW_ERR;
+ AscInitLram(asc_dvc);
err = request_firmware(&fw, fwname, asc_dvc->drv_ptr->dev);
if (err) {
@@ -4848,7 +4109,7 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf,
int size, int memsize, int chksum)
{
int i, j, end, len = 0;
- ADV_DCNT sum;
+ u32 sum;
AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
@@ -4894,38 +4155,72 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf,
return 0;
}
-static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc)
+static void AdvBuildCarrierFreelist(struct adv_dvc_var *adv_dvc)
{
- ADV_CARR_T *carrp;
- ADV_SDCNT buf_size;
- ADV_PADDR carr_paddr;
+ off_t carr_offset = 0, next_offset;
+ dma_addr_t carr_paddr;
+ int carr_num = ADV_CARRIER_BUFSIZE / sizeof(ADV_CARR_T), i;
- carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf);
- asc_dvc->carr_freelist = NULL;
- if (carrp == asc_dvc->carrier_buf) {
- buf_size = ADV_CARRIER_BUFSIZE;
- } else {
- buf_size = ADV_CARRIER_BUFSIZE - sizeof(ADV_CARR_T);
+ for (i = 0; i < carr_num; i++) {
+ carr_offset = i * sizeof(ADV_CARR_T);
+ /* Get physical address of the carrier 'carrp'. */
+ carr_paddr = adv_dvc->carrier_addr + carr_offset;
+
+ adv_dvc->carrier[i].carr_pa = cpu_to_le32(carr_paddr);
+ adv_dvc->carrier[i].carr_va = cpu_to_le32(carr_offset);
+ adv_dvc->carrier[i].areq_vpa = 0;
+ next_offset = carr_offset + sizeof(ADV_CARR_T);
+ if (i == carr_num)
+ next_offset = ~0;
+ adv_dvc->carrier[i].next_vpa = cpu_to_le32(next_offset);
}
+ /*
+ * We cannot have a carrier with 'carr_va' of '0', as
+ * a reference to this carrier would be interpreted as
+ * list termination.
+ * So start at carrier 1 with the freelist.
+ */
+ adv_dvc->carr_freelist = &adv_dvc->carrier[1];
+}
- do {
- /* Get physical address of the carrier 'carrp'. */
- carr_paddr = cpu_to_le32(virt_to_bus(carrp));
+static ADV_CARR_T *adv_get_carrier(struct adv_dvc_var *adv_dvc, u32 offset)
+{
+ int index;
- buf_size -= sizeof(ADV_CARR_T);
+ BUG_ON(offset > ADV_CARRIER_BUFSIZE);
- carrp->carr_pa = carr_paddr;
- carrp->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(carrp));
+ index = offset / sizeof(ADV_CARR_T);
+ return &adv_dvc->carrier[index];
+}
- /*
- * Insert the carrier at the beginning of the freelist.
- */
- carrp->next_vpa =
- cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
- asc_dvc->carr_freelist = carrp;
+static ADV_CARR_T *adv_get_next_carrier(struct adv_dvc_var *adv_dvc)
+{
+ ADV_CARR_T *carrp = adv_dvc->carr_freelist;
+ u32 next_vpa = le32_to_cpu(carrp->next_vpa);
+
+ if (next_vpa == 0 || next_vpa == ~0) {
+ ASC_DBG(1, "invalid vpa offset 0x%x\n", next_vpa);
+ return NULL;
+ }
- carrp++;
- } while (buf_size > 0);
+ adv_dvc->carr_freelist = adv_get_carrier(adv_dvc, next_vpa);
+ /*
+ * insert stopper carrier to terminate list
+ */
+ carrp->next_vpa = cpu_to_le32(ADV_CQ_STOPPER);
+
+ return carrp;
+}
+
+/*
+ * 'offset' is the index in the request pointer array
+ */
+static adv_req_t * adv_get_reqp(struct adv_dvc_var *adv_dvc, u32 offset)
+{
+ struct asc_board *boardp = adv_dvc->drv_ptr;
+
+ BUG_ON(offset > adv_dvc->max_host_qng);
+ return &boardp->adv_reqp[offset];
}
/*
@@ -4944,10 +4239,9 @@ static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc)
*/
static int
AdvSendIdleCmd(ADV_DVC_VAR *asc_dvc,
- ushort idle_cmd, ADV_DCNT idle_cmd_parameter)
+ ushort idle_cmd, u32 idle_cmd_parameter)
{
- int result;
- ADV_DCNT i, j;
+ int result, i, j;
AdvPortAddr iop_base;
iop_base = asc_dvc->iop_base;
@@ -5201,7 +4495,7 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
/*
* Microcode operating variables for WDTR, SDTR, and command tag
- * queuing will be set in slave_configure() based on what a
+ * queuing will be set in sdev_configure() based on what a
* device reports it is capable of in Inquiry byte 7.
*
* If SCSI Bus Resets have been disabled, then directly set
@@ -5414,17 +4708,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
* Set-up the Host->RISC Initiator Command Queue (ICQ).
*/
- if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->icq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
- /*
- * The first command issued will be placed in the stopper carrier.
- */
- asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC ICQ physical address start value.
@@ -5434,21 +4722,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the RISC->Host Initiator Response Queue (IRQ).
*/
- if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->irq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
- /*
- * The first command completed by the RISC will be placed in
- * the stopper.
- *
- * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
- * completed the RISC will set the ASC_RQ_STOPPER bit.
- */
- asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC IRQ physical address start value.
@@ -5734,7 +5012,7 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
/*
* Microcode operating variables for WDTR, SDTR, and command tag
- * queuing will be set in slave_configure() based on what a
+ * queuing will be set in sdev_configure() based on what a
* device reports it is capable of in Inquiry byte 7.
*
* If SCSI Bus Resets have been disabled, then directly set
@@ -5911,17 +5189,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
* Set-up the Host->RISC Initiator Command Queue (ICQ).
*/
- if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->icq_sp) {
+ ASC_DBG(0, "Failed to get ICQ carrier\n");
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
- /*
- * The first command issued will be placed in the stopper carrier.
- */
- asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC ICQ physical address start value.
@@ -5932,21 +5205,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the RISC->Host Initiator Response Queue (IRQ).
*/
- if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->irq_sp) {
+ ASC_DBG(0, "Failed to get IRQ carrier\n");
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
- /*
- * The first command completed by the RISC will be placed in
- * the stopper.
- *
- * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
- * completed the RISC will set the ASC_RQ_STOPPER bit.
- */
- asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC IRQ physical address start value.
@@ -6243,7 +5507,7 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
/*
* Microcode operating variables for WDTR, SDTR, and command tag
- * queuing will be set in slave_configure() based on what a
+ * queuing will be set in sdev_configure() based on what a
* device reports it is capable of in Inquiry byte 7.
*
* If SCSI Bus Resets have been disabled, then directly set
@@ -6421,17 +5685,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the Host->RISC Initiator Command Queue (ICQ).
*/
- if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->icq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
- /*
- * The first command issued will be placed in the stopper carrier.
- */
- asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC ICQ physical address start value. Initialize the
@@ -6445,21 +5703,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the RISC->Host Initiator Response Queue (IRQ).
*/
- if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->irq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
- /*
- * The first command completed by the RISC will be placed in
- * the stopper.
- *
- * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
- * completed the RISC will set the ASC_RQ_STOPPER bit.
- */
- asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC IRQ physical address start value.
@@ -6646,15 +5894,15 @@ static void adv_async_callback(ADV_DVC_VAR *adv_dvc_varp, uchar code)
*/
static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
{
- struct asc_board *boardp;
+ struct asc_board *boardp = adv_dvc_varp->drv_ptr;
adv_req_t *reqp;
adv_sgblk_t *sgblkp;
struct scsi_cmnd *scp;
- struct Scsi_Host *shost;
- ADV_DCNT resid_cnt;
+ u32 resid_cnt;
+ dma_addr_t sense_addr;
- ASC_DBG(1, "adv_dvc_varp 0x%lx, scsiqp 0x%lx\n",
- (ulong)adv_dvc_varp, (ulong)scsiqp);
+ ASC_DBG(1, "adv_dvc_varp 0x%p, scsiqp 0x%p\n",
+ adv_dvc_varp, scsiqp);
ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
/*
@@ -6662,22 +5910,8 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
* completed. The adv_req_t structure actually contains the
* completed ADV_SCSI_REQ_Q structure.
*/
- reqp = (adv_req_t *)ADV_U32_TO_VADDR(scsiqp->srb_ptr);
- ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp);
- if (reqp == NULL) {
- ASC_PRINT("adv_isr_callback: reqp is NULL\n");
- return;
- }
+ scp = scsi_host_find_tag(boardp->shost, scsiqp->srb_tag);
- /*
- * Get the struct scsi_cmnd structure and Scsi_Host structure for the
- * command that has been completed.
- *
- * Note: The adv_req_t request structure and adv_sgblk_t structure,
- * if any, are dropped, because a board structure pointer can not be
- * determined.
- */
- scp = reqp->cmndp;
ASC_DBG(1, "scp 0x%p\n", scp);
if (scp == NULL) {
ASC_PRINT
@@ -6686,20 +5920,33 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
}
ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
- shost = scp->device->host;
- ASC_STATS(shost, callback);
- ASC_DBG(1, "shost 0x%p\n", shost);
+ reqp = (adv_req_t *)scp->host_scribble;
+ ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp);
+ if (reqp == NULL) {
+ ASC_PRINT("adv_isr_callback: reqp is NULL\n");
+ return;
+ }
+ /*
+ * Remove backreferences to avoid duplicate
+ * command completions.
+ */
+ scp->host_scribble = NULL;
+ reqp->cmndp = NULL;
+
+ ASC_STATS(boardp->shost, callback);
+ ASC_DBG(1, "shost 0x%p\n", boardp->shost);
- boardp = shost_priv(shost);
- BUG_ON(adv_dvc_varp != &boardp->dvc_var.adv_dvc_var);
+ sense_addr = le32_to_cpu(scsiqp->sense_addr);
+ dma_unmap_single(boardp->dev, sense_addr,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
/*
* 'done_status' contains the command's ending status.
*/
+ scp->result = 0;
switch (scsiqp->done_status) {
case QD_NO_ERROR:
ASC_DBG(2, "QD_NO_ERROR\n");
- scp->result = 0;
/*
* Check for an underrun condition.
@@ -6720,47 +5967,32 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
ASC_DBG(2, "QD_WITH_ERROR\n");
switch (scsiqp->host_status) {
case QHSTA_NO_ERROR:
+ set_status_byte(scp, scsiqp->scsi_status);
if (scsiqp->scsi_status == SAM_STAT_CHECK_CONDITION) {
ASC_DBG(2, "SAM_STAT_CHECK_CONDITION\n");
ASC_DBG_PRT_SENSE(2, scp->sense_buffer,
SCSI_SENSE_BUFFERSIZE);
- /*
- * Note: The 'status_byte()' macro used by
- * target drivers defined in scsi.h shifts the
- * status byte returned by host drivers right
- * by 1 bit. This is why target drivers also
- * use right shifted status byte definitions.
- * For instance target drivers use
- * CHECK_CONDITION, defined to 0x1, instead of
- * the SCSI defined check condition value of
- * 0x2. Host drivers are supposed to return
- * the status byte as it is defined by SCSI.
- */
- scp->result = DRIVER_BYTE(DRIVER_SENSE) |
- STATUS_BYTE(scsiqp->scsi_status);
- } else {
- scp->result = STATUS_BYTE(scsiqp->scsi_status);
}
break;
default:
/* Some other QHSTA error occurred. */
ASC_DBG(1, "host_status 0x%x\n", scsiqp->host_status);
- scp->result = HOST_BYTE(DID_BAD_TARGET);
+ set_host_byte(scp, DID_BAD_TARGET);
break;
}
break;
case QD_ABORTED_BY_HOST:
ASC_DBG(1, "QD_ABORTED_BY_HOST\n");
- scp->result =
- HOST_BYTE(DID_ABORT) | STATUS_BYTE(scsiqp->scsi_status);
+ set_status_byte(scp, scsiqp->scsi_status);
+ set_host_byte(scp, DID_ABORT);
break;
default:
ASC_DBG(1, "done_status 0x%x\n", scsiqp->done_status);
- scp->result =
- HOST_BYTE(DID_ERROR) | STATUS_BYTE(scsiqp->scsi_status);
+ set_status_byte(scp, scsiqp->scsi_status);
+ set_host_byte(scp, DID_ERROR);
break;
}
@@ -6784,18 +6016,10 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
/* Remove 'sgblkp' from the request list. */
reqp->sgblkp = sgblkp->next_sgblkp;
- /* Add 'sgblkp' to the board free list. */
- sgblkp->next_sgblkp = boardp->adv_sgblkp;
- boardp->adv_sgblkp = sgblkp;
+ dma_pool_free(boardp->adv_sgblk_pool, sgblkp,
+ sgblkp->sg_addr);
}
- /*
- * Free the adv_req_t structure used with the command by adding
- * it back to the board free list.
- */
- reqp->next_reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp;
-
ASC_DBG(1, "done\n");
}
@@ -6822,10 +6046,10 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
{
AdvPortAddr iop_base;
uchar int_stat;
- ushort target_bit;
ADV_CARR_T *free_carrp;
- ADV_VADDR irq_next_vpa;
+ __le32 irq_next_vpa;
ADV_SCSI_REQ_Q *scsiq;
+ adv_req_t *reqp;
iop_base = asc_dvc->iop_base;
@@ -6868,25 +6092,28 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
* Check if the IRQ stopper carrier contains a completed request.
*/
while (((irq_next_vpa =
- le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ASC_RQ_DONE) != 0) {
+ le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ADV_RQ_DONE) != 0) {
/*
* Get a pointer to the newly completed ADV_SCSI_REQ_Q structure.
* The RISC will have set 'areq_vpa' to a virtual address.
*
- * The firmware will have copied the ASC_SCSI_REQ_Q.scsiq_ptr
+ * The firmware will have copied the ADV_SCSI_REQ_Q.scsiq_ptr
* field to the carrier ADV_CARR_T.areq_vpa field. The conversion
- * below complements the conversion of ASC_SCSI_REQ_Q.scsiq_ptr'
+ * below complements the conversion of ADV_SCSI_REQ_Q.scsiq_ptr'
* in AdvExeScsiQueue().
*/
- scsiq = (ADV_SCSI_REQ_Q *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->areq_vpa));
+ u32 pa_offset = le32_to_cpu(asc_dvc->irq_sp->areq_vpa);
+ ASC_DBG(1, "irq_sp %p areq_vpa %u\n",
+ asc_dvc->irq_sp, pa_offset);
+ reqp = adv_get_reqp(asc_dvc, pa_offset);
+ scsiq = &reqp->scsi_req_q;
/*
* Request finished with good status and the queue was not
* DMAed to host memory by the firmware. Set all status fields
* to indicate good status.
*/
- if ((irq_next_vpa & ASC_RQ_GOOD) != 0) {
+ if ((irq_next_vpa & ADV_RQ_GOOD) != 0) {
scsiq->done_status = QD_NO_ERROR;
scsiq->host_status = scsiq->scsi_status = 0;
scsiq->data_cnt = 0L;
@@ -6898,16 +6125,13 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
* stopper carrier.
*/
free_carrp = asc_dvc->irq_sp;
- asc_dvc->irq_sp = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(ASC_GET_CARRP(irq_next_vpa));
+ asc_dvc->irq_sp = adv_get_carrier(asc_dvc,
+ ADV_GET_CARRP(irq_next_vpa));
- free_carrp->next_vpa =
- cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
+ free_carrp->next_vpa = asc_dvc->carr_freelist->carr_va;
asc_dvc->carr_freelist = free_carrp;
asc_dvc->carr_pending_cnt--;
- target_bit = ADV_TID_TO_TIDMASK(scsiq->target_id);
-
/*
* Clear request microcode control flag.
*/
@@ -6917,7 +6141,6 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
* Notify the driver of the completed request by passing
* the ADV_SCSI_REQ_Q pointer to its callback function.
*/
- scsiq->a_flag |= ADV_SCSIQ_DONE;
adv_isr_callback(asc_dvc, scsiq);
/*
* Note: After the driver callback function is called, 'scsiq'
@@ -6994,18 +6217,17 @@ static uchar AscGetSynPeriodIndex(ASC_DVC_VAR *asc_dvc, uchar syn_time)
static uchar
AscMsgOutSDTR(ASC_DVC_VAR *asc_dvc, uchar sdtr_period, uchar sdtr_offset)
{
- EXT_MSG sdtr_buf;
- uchar sdtr_period_index;
- PortAddr iop_base;
-
- iop_base = asc_dvc->iop_base;
- sdtr_buf.msg_type = EXTENDED_MESSAGE;
- sdtr_buf.msg_len = MS_SDTR_LEN;
- sdtr_buf.msg_req = EXTENDED_SDTR;
- sdtr_buf.xfer_period = sdtr_period;
+ PortAddr iop_base = asc_dvc->iop_base;
+ uchar sdtr_period_index = AscGetSynPeriodIndex(asc_dvc, sdtr_period);
+ EXT_MSG sdtr_buf = {
+ .msg_type = EXTENDED_MESSAGE,
+ .msg_len = MS_SDTR_LEN,
+ .msg_req = EXTENDED_SDTR,
+ .xfer_period = sdtr_period,
+ .req_ack_offset = sdtr_offset,
+ };
sdtr_offset &= ASC_SYN_MAX_OFFSET;
- sdtr_buf.req_ack_offset = sdtr_offset;
- sdtr_period_index = AscGetSynPeriodIndex(asc_dvc, sdtr_period);
+
if (sdtr_period_index <= asc_dvc->max_sdtr_index) {
AscMemWordCopyPtrToLram(iop_base, ASCV_MSGOUT_BEG,
(uchar *)&sdtr_buf,
@@ -7033,11 +6255,11 @@ AscCalSDTRData(ASC_DVC_VAR *asc_dvc, uchar sdtr_period, uchar syn_offset)
return byte;
}
-static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
+static bool AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
{
ASC_SCSI_BIT_ID_TYPE org_id;
int i;
- int sta = TRUE;
+ bool sta = true;
AscSetBank(iop_base, 1);
org_id = AscReadChipDvcID(iop_base);
@@ -7051,10 +6273,10 @@ static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
AscSetBank(iop_base, 0);
AscSetChipSyn(iop_base, sdtr_data);
if (AscGetChipSyn(iop_base) != sdtr_data) {
- sta = FALSE;
+ sta = false;
}
} else {
- sta = FALSE;
+ sta = false;
}
AscSetBank(iop_base, 1);
AscWriteChipDvcID(iop_base, org_id);
@@ -7068,12 +6290,12 @@ static void AscSetChipSDTR(PortAddr iop_base, uchar sdtr_data, uchar tid_no)
AscPutMCodeSDTRDoneAtID(iop_base, tid_no, sdtr_data);
}
-static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
+static void AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
{
EXT_MSG ext_msg;
EXT_MSG out_msg;
ushort halt_q_addr;
- int sdtr_accept;
+ bool sdtr_accept;
ushort int_halt_code;
ASC_SCSI_BIT_ID_TYPE scsi_busy;
ASC_SCSI_BIT_ID_TYPE target_id;
@@ -7115,14 +6337,14 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
boardp->sdtr_data[tid_no] = 0;
}
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_ENABLE_ASYN_USE_SYN_FIX) {
if (asc_dvc->pci_fix_asyn_xfer & target_id) {
AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
boardp->sdtr_data[tid_no] = asyn_sdtr;
}
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_EXTMSG_IN) {
AscMemWordCopyPtrFromLram(iop_base,
ASCV_MSGIN_BEG,
@@ -7132,10 +6354,10 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
if (ext_msg.msg_type == EXTENDED_MESSAGE &&
ext_msg.msg_req == EXTENDED_SDTR &&
ext_msg.msg_len == MS_SDTR_LEN) {
- sdtr_accept = TRUE;
+ sdtr_accept = true;
if ((ext_msg.req_ack_offset > ASC_SYN_MAX_OFFSET)) {
- sdtr_accept = FALSE;
+ sdtr_accept = false;
ext_msg.req_ack_offset = ASC_SYN_MAX_OFFSET;
}
if ((ext_msg.xfer_period <
@@ -7143,7 +6365,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
|| (ext_msg.xfer_period >
asc_dvc->sdtr_period_tbl[asc_dvc->
max_sdtr_index])) {
- sdtr_accept = FALSE;
+ sdtr_accept = false;
ext_msg.xfer_period =
asc_dvc->sdtr_period_tbl[asc_dvc->
min_sdtr_index];
@@ -7152,7 +6374,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
sdtr_data =
AscCalSDTRData(asc_dvc, ext_msg.xfer_period,
ext_msg.req_ack_offset);
- if ((sdtr_data == 0xFF)) {
+ if (sdtr_data == 0xFF) {
q_cntl |= QC_MSG_OUT;
asc_dvc->init_sdtr &= ~target_id;
@@ -7208,7 +6430,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)ASC_SCSIQ_B_CNTL),
q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (ext_msg.msg_type == EXTENDED_MESSAGE &&
ext_msg.msg_req == EXTENDED_WDTR &&
ext_msg.msg_len == MS_WDTR_LEN) {
@@ -7224,7 +6446,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)ASC_SCSIQ_B_CNTL),
q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else {
ext_msg.msg_type = MESSAGE_REJECT;
@@ -7238,7 +6460,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)ASC_SCSIQ_B_CNTL),
q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
}
} else if (int_halt_code == ASC_HALT_CHK_CONDITION) {
@@ -7295,7 +6517,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
AscWriteLramByte(iop_base, (ushort)ASCV_SCSIBUSY_B, scsi_busy);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_SDTR_REJECTED) {
AscMemWordCopyPtrFromLram(iop_base,
@@ -7317,7 +6539,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)(halt_q_addr +
(ushort)ASC_SCSIQ_B_CNTL), q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_SS_QUEUE_FULL) {
scsi_status = AscReadLramByte(iop_base,
@@ -7362,166 +6584,9 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
}
}
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
- }
-#if CC_VERY_LONG_SG_LIST
- else if (int_halt_code == ASC_HALT_HOST_COPY_SG_LIST_TO_RISC) {
- uchar q_no;
- ushort q_addr;
- uchar sg_wk_q_no;
- uchar first_sg_wk_q_no;
- ASC_SCSI_Q *scsiq; /* Ptr to driver request. */
- ASC_SG_HEAD *sg_head; /* Ptr to driver SG request. */
- ASC_SG_LIST_Q scsi_sg_q; /* Structure written to queue. */
- ushort sg_list_dwords;
- ushort sg_entry_cnt;
- uchar next_qp;
- int i;
-
- q_no = AscReadLramByte(iop_base, (ushort)ASCV_REQ_SG_LIST_QP);
- if (q_no == ASC_QLINK_END)
- return 0;
-
- q_addr = ASC_QNO_TO_QADDR(q_no);
-
- /*
- * Convert the request's SRB pointer to a host ASC_SCSI_REQ
- * structure pointer using a macro provided by the driver.
- * The ASC_SCSI_REQ pointer provides a pointer to the
- * host ASC_SG_HEAD structure.
- */
- /* Read request's SRB pointer. */
- scsiq = (ASC_SCSI_Q *)
- ASC_SRB2SCSIQ(ASC_U32_TO_VADDR(AscReadLramDWord(iop_base,
- (ushort)
- (q_addr +
- ASC_SCSIQ_D_SRBPTR))));
-
- /*
- * Get request's first and working SG queue.
- */
- sg_wk_q_no = AscReadLramByte(iop_base,
- (ushort)(q_addr +
- ASC_SCSIQ_B_SG_WK_QP));
-
- first_sg_wk_q_no = AscReadLramByte(iop_base,
- (ushort)(q_addr +
- ASC_SCSIQ_B_FIRST_SG_WK_QP));
-
- /*
- * Reset request's working SG queue back to the
- * first SG queue.
- */
- AscWriteLramByte(iop_base,
- (ushort)(q_addr +
- (ushort)ASC_SCSIQ_B_SG_WK_QP),
- first_sg_wk_q_no);
-
- sg_head = scsiq->sg_head;
-
- /*
- * Set sg_entry_cnt to the number of SG elements
- * that will be completed on this interrupt.
- *
- * Note: The allocated SG queues contain ASC_MAX_SG_LIST - 1
- * SG elements. The data_cnt and data_addr fields which
- * add 1 to the SG element capacity are not used when
- * restarting SG handling after a halt.
- */
- if (scsiq->remain_sg_entry_cnt > (ASC_MAX_SG_LIST - 1)) {
- sg_entry_cnt = ASC_MAX_SG_LIST - 1;
-
- /*
- * Keep track of remaining number of SG elements that
- * will need to be handled on the next interrupt.
- */
- scsiq->remain_sg_entry_cnt -= (ASC_MAX_SG_LIST - 1);
- } else {
- sg_entry_cnt = scsiq->remain_sg_entry_cnt;
- scsiq->remain_sg_entry_cnt = 0;
- }
-
- /*
- * Copy SG elements into the list of allocated SG queues.
- *
- * Last index completed is saved in scsiq->next_sg_index.
- */
- next_qp = first_sg_wk_q_no;
- q_addr = ASC_QNO_TO_QADDR(next_qp);
- scsi_sg_q.sg_head_qp = q_no;
- scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
- for (i = 0; i < sg_head->queue_cnt; i++) {
- scsi_sg_q.seq_no = i + 1;
- if (sg_entry_cnt > ASC_SG_LIST_PER_Q) {
- sg_list_dwords = (uchar)(ASC_SG_LIST_PER_Q * 2);
- sg_entry_cnt -= ASC_SG_LIST_PER_Q;
- /*
- * After very first SG queue RISC FW uses next
- * SG queue first element then checks sg_list_cnt
- * against zero and then decrements, so set
- * sg_list_cnt 1 less than number of SG elements
- * in each SG queue.
- */
- scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1;
- scsi_sg_q.sg_cur_list_cnt =
- ASC_SG_LIST_PER_Q - 1;
- } else {
- /*
- * This is the last SG queue in the list of
- * allocated SG queues. If there are more
- * SG elements than will fit in the allocated
- * queues, then set the QCSG_SG_XFER_MORE flag.
- */
- if (scsiq->remain_sg_entry_cnt != 0) {
- scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
- } else {
- scsi_sg_q.cntl |= QCSG_SG_XFER_END;
- }
- /* equals sg_entry_cnt * 2 */
- sg_list_dwords = sg_entry_cnt << 1;
- scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1;
- scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1;
- sg_entry_cnt = 0;
- }
-
- scsi_sg_q.q_no = next_qp;
- AscMemWordCopyPtrToLram(iop_base,
- q_addr + ASC_SCSIQ_SGHD_CPY_BEG,
- (uchar *)&scsi_sg_q,
- sizeof(ASC_SG_LIST_Q) >> 1);
-
- AscMemDWordCopyPtrToLram(iop_base,
- q_addr + ASC_SGQ_LIST_BEG,
- (uchar *)&sg_head->
- sg_list[scsiq->next_sg_index],
- sg_list_dwords);
-
- scsiq->next_sg_index += ASC_SG_LIST_PER_Q;
-
- /*
- * If the just completed SG queue contained the
- * last SG element, then no more SG queues need
- * to be written.
- */
- if (scsi_sg_q.cntl & QCSG_SG_XFER_END) {
- break;
- }
-
- next_qp = AscReadLramByte(iop_base,
- (ushort)(q_addr +
- ASC_SCSIQ_B_FWD));
- q_addr = ASC_QNO_TO_QADDR(next_qp);
- }
-
- /*
- * Clear the halt condition so the RISC will be restarted
- * after the return.
- */
- AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
}
-#endif /* CC_VERY_LONG_SG_LIST */
- return (0);
+ return;
}
/*
@@ -7555,7 +6620,7 @@ DvcGetQinfo(PortAddr iop_base, ushort s_addr, uchar *inbuf, int words)
static uchar
_AscCopyLramScsiDoneQ(PortAddr iop_base,
ushort q_addr,
- ASC_QDONE_INFO *scsiq, ASC_DCNT max_dma_count)
+ ASC_QDONE_INFO *scsiq, unsigned int max_dma_count)
{
ushort _val;
uchar sg_queue_cnt;
@@ -7582,10 +6647,10 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base,
/*
* Read high word of remain bytes from alternate location.
*/
- scsiq->remain_bytes = (((ADV_DCNT)AscReadLramWord(iop_base,
- (ushort)(q_addr +
- (ushort)
- ASC_SCSIQ_W_ALT_DC1)))
+ scsiq->remain_bytes = (((u32)AscReadLramWord(iop_base,
+ (ushort)(q_addr +
+ (ushort)
+ ASC_SCSIQ_W_ALT_DC1)))
<< 16);
/*
* Read low word of remain bytes from original location.
@@ -7605,35 +6670,34 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base,
*/
static void asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
{
- struct asc_board *boardp;
+ struct asc_board *boardp = asc_dvc_varp->drv_ptr;
+ u32 srb_tag;
struct scsi_cmnd *scp;
- struct Scsi_Host *shost;
ASC_DBG(1, "asc_dvc_varp 0x%p, qdonep 0x%p\n", asc_dvc_varp, qdonep);
ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep);
- scp = advansys_srb_to_ptr(asc_dvc_varp, qdonep->d2.srb_ptr);
+ /*
+ * Decrease the srb_tag by 1 to find the SCSI command
+ */
+ srb_tag = qdonep->d2.srb_tag - 1;
+ scp = scsi_host_find_tag(boardp->shost, srb_tag);
if (!scp)
return;
ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
- shost = scp->device->host;
- ASC_STATS(shost, callback);
- ASC_DBG(1, "shost 0x%p\n", shost);
-
- boardp = shost_priv(shost);
- BUG_ON(asc_dvc_varp != &boardp->dvc_var.asc_dvc_var);
+ ASC_STATS(boardp->shost, callback);
- dma_unmap_single(boardp->dev, scp->SCp.dma_handle,
+ dma_unmap_single(boardp->dev, advansys_cmd(scp)->dma_handle,
SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
/*
* 'qdonep' contains the command's ending status.
*/
+ scp->result = 0;
switch (qdonep->d3.done_stat) {
case QD_NO_ERROR:
ASC_DBG(2, "QD_NO_ERROR\n");
- scp->result = 0;
/*
* Check for an underrun condition.
@@ -7653,51 +6717,32 @@ static void asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
ASC_DBG(2, "QD_WITH_ERROR\n");
switch (qdonep->d3.host_stat) {
case QHSTA_NO_ERROR:
+ set_status_byte(scp, qdonep->d3.scsi_stat);
if (qdonep->d3.scsi_stat == SAM_STAT_CHECK_CONDITION) {
ASC_DBG(2, "SAM_STAT_CHECK_CONDITION\n");
ASC_DBG_PRT_SENSE(2, scp->sense_buffer,
SCSI_SENSE_BUFFERSIZE);
- /*
- * Note: The 'status_byte()' macro used by
- * target drivers defined in scsi.h shifts the
- * status byte returned by host drivers right
- * by 1 bit. This is why target drivers also
- * use right shifted status byte definitions.
- * For instance target drivers use
- * CHECK_CONDITION, defined to 0x1, instead of
- * the SCSI defined check condition value of
- * 0x2. Host drivers are supposed to return
- * the status byte as it is defined by SCSI.
- */
- scp->result = DRIVER_BYTE(DRIVER_SENSE) |
- STATUS_BYTE(qdonep->d3.scsi_stat);
- } else {
- scp->result = STATUS_BYTE(qdonep->d3.scsi_stat);
}
break;
default:
/* QHSTA error occurred */
ASC_DBG(1, "host_stat 0x%x\n", qdonep->d3.host_stat);
- scp->result = HOST_BYTE(DID_BAD_TARGET);
+ set_host_byte(scp, DID_BAD_TARGET);
break;
}
break;
case QD_ABORTED_BY_HOST:
ASC_DBG(1, "QD_ABORTED_BY_HOST\n");
- scp->result =
- HOST_BYTE(DID_ABORT) | MSG_BYTE(qdonep->d3.
- scsi_msg) |
- STATUS_BYTE(qdonep->d3.scsi_stat);
+ set_status_byte(scp, qdonep->d3.scsi_stat);
+ set_host_byte(scp, DID_ABORT);
break;
default:
ASC_DBG(1, "done_stat 0x%x\n", qdonep->d3.done_stat);
- scp->result =
- HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.
- scsi_msg) |
- STATUS_BYTE(qdonep->d3.scsi_stat);
+ set_status_byte(scp, qdonep->d3.scsi_stat);
+ set_host_byte(scp, DID_ERROR);
break;
}
@@ -7732,7 +6777,7 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc)
uchar cur_target_qng;
ASC_QDONE_INFO scsiq_buf;
ASC_QDONE_INFO *scsiq;
- int false_overrun;
+ bool false_overrun;
iop_base = asc_dvc->iop_base;
n_q_used = 1;
@@ -7806,14 +6851,17 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc)
scsiq->d3.done_stat = QD_WITH_ERROR;
goto FATAL_ERR_QDONE;
}
- if ((scsiq->d2.srb_ptr == 0UL) ||
+ if ((scsiq->d2.srb_tag == 0UL) ||
((scsiq->q_status & QS_ABORTED) != 0)) {
return (0x11);
} else if (scsiq->q_status == QS_DONE) {
- false_overrun = FALSE;
+ /*
+ * This is also curious.
+ * false_overrun will _always_ be set to 'false'
+ */
+ false_overrun = false;
if (scsiq->extra_bytes != 0) {
- scsiq->remain_bytes +=
- (ADV_DCNT)scsiq->extra_bytes;
+ scsiq->remain_bytes += scsiq->extra_bytes;
}
if (scsiq->d3.done_stat == QD_WITH_ERROR) {
if (scsiq->d3.host_stat ==
@@ -7884,23 +6932,23 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
uchar host_flag;
iop_base = asc_dvc->iop_base;
- int_pending = FALSE;
+ int_pending = ASC_FALSE;
if (AscIsIntPending(iop_base) == 0)
return int_pending;
if ((asc_dvc->init_state & ASC_INIT_STATE_END_LOAD_MC) == 0) {
- return ERR;
+ return ASC_ERROR;
}
if (asc_dvc->in_critical_cnt != 0) {
AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_ON_CRITICAL);
- return ERR;
+ return ASC_ERROR;
}
if (asc_dvc->is_in_int) {
AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_RE_ENTRY);
- return ERR;
+ return ASC_ERROR;
}
- asc_dvc->is_in_int = TRUE;
+ asc_dvc->is_in_int = true;
ctrl_reg = AscGetChipControl(iop_base);
saved_ctrl_reg = ctrl_reg & (~(CC_SCSI_RESET | CC_CHIP_RESET |
CC_SINGLE_STEP | CC_DIAG | CC_TEST));
@@ -7908,7 +6956,7 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
if (chipstat & CSW_SCSI_RESET_LATCH) {
if (!(asc_dvc->bus_type & (ASC_IS_VL | ASC_IS_EISA))) {
int i = 10;
- int_pending = TRUE;
+ int_pending = ASC_TRUE;
asc_dvc->sdtr_done = 0;
saved_ctrl_reg &= (uchar)(~CC_HALT);
while ((AscGetChipStatus(iop_base) &
@@ -7930,15 +6978,11 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
(uchar)(host_flag | (uchar)ASC_HOST_FLAG_IN_ISR));
if ((chipstat & CSW_INT_PENDING) || (int_pending)) {
AscAckInterrupt(iop_base);
- int_pending = TRUE;
+ int_pending = ASC_TRUE;
if ((chipstat & CSW_HALTED) && (ctrl_reg & CC_SINGLE_STEP)) {
- if (AscIsrChipHalted(asc_dvc) == ERR) {
- goto ISR_REPORT_QDONE_FATAL_ERROR;
- } else {
- saved_ctrl_reg &= (uchar)(~CC_HALT);
- }
+ AscIsrChipHalted(asc_dvc);
+ saved_ctrl_reg &= (uchar)(~CC_HALT);
} else {
- ISR_REPORT_QDONE_FATAL_ERROR:
if ((asc_dvc->dvc_cntl & ASC_CNTL_INT_MULTI_Q) != 0) {
while (((status =
AscIsrQDone(asc_dvc)) & 0x01) != 0) {
@@ -7952,20 +6996,20 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
} while (status == 0x11);
}
if ((status & 0x80) != 0)
- int_pending = ERR;
+ int_pending = ASC_ERROR;
}
}
AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
AscSetChipLramAddr(iop_base, saved_ram_addr);
AscSetChipControl(iop_base, saved_ctrl_reg);
- asc_dvc->is_in_int = FALSE;
+ asc_dvc->is_in_int = false;
return int_pending;
}
/*
* advansys_reset()
*
- * Reset the bus associated with the command 'scp'.
+ * Reset the host associated with the command 'scp'.
*
* This function runs its own thread. Interrupts must be blocked but
* sleeping is allowed and no locking other than for host structures is
@@ -7983,7 +7027,7 @@ static int advansys_reset(struct scsi_cmnd *scp)
ASC_STATS(shost, reset);
- scmd_printk(KERN_INFO, scp, "SCSI bus reset started...\n");
+ scmd_printk(KERN_INFO, scp, "SCSI host reset started...\n");
if (ASC_NARROW_BOARD(boardp)) {
ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var;
@@ -7994,20 +7038,19 @@ static int advansys_reset(struct scsi_cmnd *scp)
/* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */
if (asc_dvc->err_code || !asc_dvc->overrun_dma) {
- scmd_printk(KERN_INFO, scp, "SCSI bus reset error: "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset error: "
"0x%x, status: 0x%x\n", asc_dvc->err_code,
status);
ret = FAILED;
} else if (status) {
- scmd_printk(KERN_INFO, scp, "SCSI bus reset warning: "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset warning: "
"0x%x\n", status);
} else {
- scmd_printk(KERN_INFO, scp, "SCSI bus reset "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset "
"successful\n");
}
ASC_DBG(1, "after AscInitAsc1000Driver()\n");
- spin_lock_irqsave(shost->host_lock, flags);
} else {
/*
* If the suggest reset bus flags are set, then reset the bus.
@@ -8016,28 +7059,25 @@ static int advansys_reset(struct scsi_cmnd *scp)
ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var;
/*
- * Reset the target's SCSI bus.
+ * Reset the chip and SCSI bus.
*/
ASC_DBG(1, "before AdvResetChipAndSB()\n");
switch (AdvResetChipAndSB(adv_dvc)) {
case ASC_TRUE:
- scmd_printk(KERN_INFO, scp, "SCSI bus reset "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset "
"successful\n");
break;
case ASC_FALSE:
default:
- scmd_printk(KERN_INFO, scp, "SCSI bus reset error\n");
+ scmd_printk(KERN_INFO, scp, "SCSI host reset error\n");
ret = FAILED;
break;
}
spin_lock_irqsave(shost->host_lock, flags);
AdvISR(adv_dvc);
+ spin_unlock_irqrestore(shost->host_lock, flags);
}
- /* Save the time of the most recently completed reset. */
- boardp->last_reset = jiffies;
- spin_unlock_irqrestore(shost->host_lock, flags);
-
ASC_DBG(1, "ret %d\n", ret);
return ret;
@@ -8055,7 +7095,7 @@ static int advansys_reset(struct scsi_cmnd *scp)
* ip[2]: cylinders
*/
static int
-advansys_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+advansys_biosparam(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int ip[])
{
struct asc_board *boardp = shost_priv(sdev->host);
@@ -8096,9 +7136,10 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id)
struct Scsi_Host *shost = dev_id;
struct asc_board *boardp = shost_priv(shost);
irqreturn_t result = IRQ_NONE;
+ unsigned long flags;
ASC_DBG(2, "boardp 0x%p\n", boardp);
- spin_lock(shost->host_lock);
+ spin_lock_irqsave(shost->host_lock, flags);
if (ASC_NARROW_BOARD(boardp)) {
if (AscIsIntPending(shost->io_port)) {
result = IRQ_HANDLED;
@@ -8113,38 +7154,38 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id)
ASC_STATS(shost, interrupt);
}
}
- spin_unlock(shost->host_lock);
+ spin_unlock_irqrestore(shost->host_lock, flags);
ASC_DBG(1, "end\n");
return result;
}
-static int AscHostReqRiscHalt(PortAddr iop_base)
+static bool AscHostReqRiscHalt(PortAddr iop_base)
{
int count = 0;
- int sta = 0;
+ bool sta = false;
uchar saved_stop_code;
if (AscIsChipHalted(iop_base))
- return (1);
+ return true;
saved_stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
ASC_STOP_HOST_REQ_RISC_HALT | ASC_STOP_REQ_RISC_STOP);
do {
if (AscIsChipHalted(iop_base)) {
- sta = 1;
+ sta = true;
break;
}
mdelay(100);
} while (count++ < 20);
AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, saved_stop_code);
- return (sta);
+ return sta;
}
-static int
+static bool
AscSetRunChipSynRegAtID(PortAddr iop_base, uchar tid_no, uchar sdtr_data)
{
- int sta = FALSE;
+ bool sta = false;
if (AscHostReqRiscHalt(iop_base)) {
sta = AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
@@ -8177,7 +7218,7 @@ static void AscAsyncFix(ASC_DVC_VAR *asc_dvc, struct scsi_device *sdev)
}
static void
-advansys_narrow_slave_configure(struct scsi_device *sdev, ASC_DVC_VAR *asc_dvc)
+advansys_narrow_sdev_configure(struct scsi_device *sdev, ASC_DVC_VAR *asc_dvc)
{
ASC_SCSI_BIT_ID_TYPE tid_bit = 1 << sdev->id;
ASC_SCSI_BIT_ID_TYPE orig_use_tagged_qng = asc_dvc->use_tagged_qng;
@@ -8200,7 +7241,7 @@ advansys_narrow_slave_configure(struct scsi_device *sdev, ASC_DVC_VAR *asc_dvc)
asc_dvc->cfg->can_tagged_qng |= tid_bit;
asc_dvc->use_tagged_qng |= tid_bit;
}
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG,
+ scsi_change_queue_depth(sdev,
asc_dvc->max_dvc_qng[sdev->id]);
}
} else {
@@ -8208,7 +7249,6 @@ advansys_narrow_slave_configure(struct scsi_device *sdev, ASC_DVC_VAR *asc_dvc)
asc_dvc->cfg->can_tagged_qng &= ~tid_bit;
asc_dvc->use_tagged_qng &= ~tid_bit;
}
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
}
if ((sdev->lun == 0) &&
@@ -8304,7 +7344,7 @@ static void advansys_wide_enable_ppr(ADV_DVC_VAR *adv_dvc,
}
static void
-advansys_wide_slave_configure(struct scsi_device *sdev, ADV_DVC_VAR *adv_dvc)
+advansys_wide_sdev_configure(struct scsi_device *sdev, ADV_DVC_VAR *adv_dvc)
{
AdvPortAddr iop_base = adv_dvc->iop_base;
unsigned short tidmask = 1 << sdev->id;
@@ -8342,40 +7382,41 @@ advansys_wide_slave_configure(struct scsi_device *sdev, ADV_DVC_VAR *adv_dvc)
}
}
- if ((adv_dvc->tagqng_able & tidmask) && sdev->tagged_supported) {
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG,
- adv_dvc->max_dvc_qng);
- } else {
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
- }
+ if ((adv_dvc->tagqng_able & tidmask) && sdev->tagged_supported)
+ scsi_change_queue_depth(sdev, adv_dvc->max_dvc_qng);
}
/*
* Set the number of commands to queue per device for the
* specified host adapter.
*/
-static int advansys_slave_configure(struct scsi_device *sdev)
+static int advansys_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct asc_board *boardp = shost_priv(sdev->host);
if (ASC_NARROW_BOARD(boardp))
- advansys_narrow_slave_configure(sdev,
- &boardp->dvc_var.asc_dvc_var);
+ advansys_narrow_sdev_configure(sdev,
+ &boardp->dvc_var.asc_dvc_var);
else
- advansys_wide_slave_configure(sdev,
- &boardp->dvc_var.adv_dvc_var);
+ advansys_wide_sdev_configure(sdev,
+ &boardp->dvc_var.adv_dvc_var);
return 0;
}
-static __le32 advansys_get_sense_buffer_dma(struct scsi_cmnd *scp)
+static __le32 asc_get_sense_buffer_dma(struct scsi_cmnd *scp)
{
struct asc_board *board = shost_priv(scp->device->host);
- scp->SCp.dma_handle = dma_map_single(board->dev, scp->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
- dma_cache_sync(board->dev, scp->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
- return cpu_to_le32(scp->SCp.dma_handle);
+ struct advansys_cmd *acmd = advansys_cmd(scp);
+
+ acmd->dma_handle = dma_map_single(board->dev, scp->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(board->dev, acmd->dma_handle)) {
+ ASC_DBG(1, "failed to map sense buffer\n");
+ return 0;
+ }
+ return cpu_to_le32(acmd->dma_handle);
}
static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
@@ -8383,17 +7424,16 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
{
struct asc_dvc_var *asc_dvc = &boardp->dvc_var.asc_dvc_var;
int use_sg;
+ u32 srb_tag;
memset(asc_scsi_q, 0, sizeof(*asc_scsi_q));
/*
- * Point the ASC_SCSI_Q to the 'struct scsi_cmnd'.
+ * Set the srb_tag to the command tag + 1, as
+ * srb_tag '0' is used internally by the chip.
*/
- asc_scsi_q->q2.srb_ptr = advansys_ptr_to_srb(asc_dvc, scp);
- if (asc_scsi_q->q2.srb_ptr == BAD_SRB) {
- scp->result = HOST_BYTE(DID_SOFT_ERROR);
- return ASC_ERROR;
- }
+ srb_tag = scsi_cmd_to_rq(scp)->tag + 1;
+ asc_scsi_q->q2.srb_tag = srb_tag;
/*
* Build the ASC_SCSI_Q request.
@@ -8404,8 +7444,10 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
asc_scsi_q->q1.target_lun = scp->device->lun;
asc_scsi_q->q2.target_ix =
ASC_TIDLUN_TO_IX(scp->device->id, scp->device->lun);
- asc_scsi_q->q1.sense_addr = advansys_get_sense_buffer_dma(scp);
+ asc_scsi_q->q1.sense_addr = asc_get_sense_buffer_dma(scp);
asc_scsi_q->q1.sense_len = SCSI_SENSE_BUFFERSIZE;
+ if (!asc_scsi_q->q1.sense_addr)
+ return ASC_BUSY;
/*
* If there are any outstanding requests for the current target,
@@ -8420,14 +7462,17 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
*/
if ((asc_dvc->cur_dvc_qng[scp->device->id] > 0) &&
(boardp->reqcnt[scp->device->id] % 255) == 0) {
- asc_scsi_q->q2.tag_code = MSG_ORDERED_TAG;
+ asc_scsi_q->q2.tag_code = ORDERED_QUEUE_TAG;
} else {
- asc_scsi_q->q2.tag_code = MSG_SIMPLE_TAG;
+ asc_scsi_q->q2.tag_code = SIMPLE_QUEUE_TAG;
}
/* Build ASC_SCSI_Q */
use_sg = scsi_dma_map(scp);
- if (use_sg != 0) {
+ if (use_sg < 0) {
+ ASC_DBG(1, "failed to map sglist\n");
+ return ASC_BUSY;
+ } else if (use_sg > 0) {
int sgcnt;
struct scatterlist *slp;
struct asc_sg_head *asc_sg_head;
@@ -8437,15 +7482,15 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
"sg_tablesize %d\n", use_sg,
scp->device->host->sg_tablesize);
scsi_dma_unmap(scp);
- scp->result = HOST_BYTE(DID_ERROR);
+ set_host_byte(scp, DID_ERROR);
return ASC_ERROR;
}
- asc_sg_head = kzalloc(sizeof(asc_scsi_q->sg_head) +
- use_sg * sizeof(struct asc_sg_list), GFP_ATOMIC);
+ asc_sg_head = kzalloc(struct_size(asc_sg_head, sg_list, use_sg),
+ GFP_ATOMIC);
if (!asc_sg_head) {
scsi_dma_unmap(scp);
- scp->result = HOST_BYTE(DID_SOFT_ERROR);
+ set_host_byte(scp, DID_SOFT_ERROR);
return ASC_ERROR;
}
@@ -8492,20 +7537,19 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
* ADV_ERROR(-1) - SG List creation failed
*/
static int
-adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
- int use_sg)
+adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp,
+ ADV_SCSI_REQ_Q *scsiqp, struct scsi_cmnd *scp, int use_sg)
{
- adv_sgblk_t *sgblkp;
- ADV_SCSI_REQ_Q *scsiqp;
+ adv_sgblk_t *sgblkp, *prev_sgblkp;
struct scatterlist *slp;
int sg_elem_cnt;
ADV_SG_BLOCK *sg_block, *prev_sg_block;
- ADV_PADDR sg_block_paddr;
+ dma_addr_t sgblk_paddr;
int i;
- scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q);
slp = scsi_sglist(scp);
sg_elem_cnt = use_sg;
+ prev_sgblkp = NULL;
prev_sg_block = NULL;
reqp->sgblkp = NULL;
@@ -8515,7 +7559,9 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
* list. One 'adv_sgblk_t' structure holds NO_OF_SG_PER_BLOCK
* (15) scatter-gather elements.
*/
- if ((sgblkp = boardp->adv_sgblkp) == NULL) {
+ sgblkp = dma_pool_alloc(boardp->adv_sgblk_pool, GFP_ATOMIC,
+ &sgblk_paddr);
+ if (!sgblkp) {
ASC_DBG(1, "no free adv_sgblk_t\n");
ASC_STATS(scp->device->host, adv_build_nosg);
@@ -8526,24 +7572,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
while ((sgblkp = reqp->sgblkp) != NULL) {
/* Remove 'sgblkp' from the request list. */
reqp->sgblkp = sgblkp->next_sgblkp;
-
- /* Add 'sgblkp' to the board free list. */
- sgblkp->next_sgblkp = boardp->adv_sgblkp;
- boardp->adv_sgblkp = sgblkp;
+ sgblkp->next_sgblkp = NULL;
+ dma_pool_free(boardp->adv_sgblk_pool, sgblkp,
+ sgblkp->sg_addr);
}
return ASC_BUSY;
}
-
/* Complete 'adv_sgblk_t' board allocation. */
- boardp->adv_sgblkp = sgblkp->next_sgblkp;
+ sgblkp->sg_addr = sgblk_paddr;
sgblkp->next_sgblkp = NULL;
-
- /*
- * Get 8 byte aligned virtual and physical addresses
- * for the allocated ADV_SG_BLOCK structure.
- */
- sg_block = (ADV_SG_BLOCK *)ADV_8BALIGN(&sgblkp->sg_block);
- sg_block_paddr = virt_to_bus(sg_block);
+ sg_block = &sgblkp->sg_block;
/*
* Check if this is the first 'adv_sgblk_t' for the
@@ -8558,17 +7596,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
* address pointers.
*/
scsiqp->sg_list_ptr = sg_block;
- scsiqp->sg_real_addr = cpu_to_le32(sg_block_paddr);
+ scsiqp->sg_real_addr = cpu_to_le32(sgblk_paddr);
} else {
/* Request's second or later scatter-gather block. */
- sgblkp->next_sgblkp = reqp->sgblkp;
- reqp->sgblkp = sgblkp;
+ prev_sgblkp->next_sgblkp = sgblkp;
/*
* Point the previous ADV_SG_BLOCK structure to
* the newly allocated ADV_SG_BLOCK structure.
*/
- prev_sg_block->sg_ptr = cpu_to_le32(sg_block_paddr);
+ prev_sg_block->sg_ptr = cpu_to_le32(sgblk_paddr);
}
for (i = 0; i < NO_OF_SG_PER_BLOCK; i++) {
@@ -8579,15 +7616,19 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
ASC_STATS_ADD(scp->device->host, xfer_sect,
DIV_ROUND_UP(sg_dma_len(slp), 512));
- if (--sg_elem_cnt == 0) { /* Last ADV_SG_BLOCK and scatter-gather entry. */
+ if (--sg_elem_cnt == 0) {
+ /*
+ * Last ADV_SG_BLOCK and scatter-gather entry.
+ */
sg_block->sg_cnt = i + 1;
- sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */
+ sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */
return ADV_SUCCESS;
}
- slp++;
+ slp = sg_next(slp);
}
sg_block->sg_cnt = NO_OF_SG_PER_BLOCK;
prev_sg_block = sg_block;
+ prev_sgblkp = sgblkp;
}
}
@@ -8597,38 +7638,35 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
* If an adv_req_t can not be allocated to issue the request,
* then return ASC_BUSY. If an error occurs, then return ASC_ERROR.
*
- * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the
+ * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the
* microcode for DMA addresses or math operations are byte swapped
* to little-endian order.
*/
static int
adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
- ADV_SCSI_REQ_Q **adv_scsiqpp)
+ adv_req_t **adv_reqpp)
{
+ u32 srb_tag = scsi_cmd_to_rq(scp)->tag;
adv_req_t *reqp;
ADV_SCSI_REQ_Q *scsiqp;
- int i;
int ret;
int use_sg;
+ dma_addr_t sense_addr;
/*
* Allocate an adv_req_t structure from the board to execute
* the command.
*/
- if (boardp->adv_reqp == NULL) {
+ reqp = &boardp->adv_reqp[srb_tag];
+ if (reqp->cmndp && reqp->cmndp != scp ) {
ASC_DBG(1, "no free adv_req_t\n");
ASC_STATS(scp->device->host, adv_build_noreq);
return ASC_BUSY;
- } else {
- reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp->next_reqp;
- reqp->next_reqp = NULL;
}
- /*
- * Get 32-byte aligned ADV_SCSI_REQ_Q and ADV_SG_BLOCK pointers.
- */
- scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q);
+ reqp->req_addr = boardp->adv_reqp_addr + (srb_tag * sizeof(adv_req_t));
+
+ scsiqp = &reqp->scsi_req_q;
/*
* Initialize the structure.
@@ -8636,14 +7674,15 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
scsiqp->cntl = scsiqp->scsi_cntl = scsiqp->done_status = 0;
/*
- * Set the ADV_SCSI_REQ_Q 'srb_ptr' to point to the adv_req_t structure.
+ * Set the srb_tag to the command tag.
*/
- scsiqp->srb_ptr = ADV_VADDR_TO_U32(reqp);
+ scsiqp->srb_tag = srb_tag;
/*
- * Set the adv_req_t 'cmndp' to point to the struct scsi_cmnd structure.
+ * Set 'host_scribble' to point to the adv_req_t structure.
*/
reqp->cmndp = scp;
+ scp->host_scribble = (void *)reqp;
/*
* Build the ADV_SCSI_REQ_Q request.
@@ -8652,28 +7691,38 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
/* Set CDB length and copy it to the request structure. */
scsiqp->cdb_len = scp->cmd_len;
/* Copy first 12 CDB bytes to cdb[]. */
- for (i = 0; i < scp->cmd_len && i < 12; i++) {
- scsiqp->cdb[i] = scp->cmnd[i];
- }
+ memcpy(scsiqp->cdb, scp->cmnd, scp->cmd_len < 12 ? scp->cmd_len : 12);
/* Copy last 4 CDB bytes, if present, to cdb16[]. */
- for (; i < scp->cmd_len; i++) {
- scsiqp->cdb16[i - 12] = scp->cmnd[i];
+ if (scp->cmd_len > 12) {
+ int cdb16_len = scp->cmd_len - 12;
+
+ memcpy(scsiqp->cdb16, &scp->cmnd[12], cdb16_len);
}
scsiqp->target_id = scp->device->id;
scsiqp->target_lun = scp->device->lun;
- scsiqp->sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0]));
+ sense_addr = dma_map_single(boardp->dev, scp->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(boardp->dev, sense_addr)) {
+ ASC_DBG(1, "failed to map sense buffer\n");
+ ASC_STATS(scp->device->host, adv_build_noreq);
+ return ASC_BUSY;
+ }
+ scsiqp->sense_addr = cpu_to_le32(sense_addr);
scsiqp->sense_len = SCSI_SENSE_BUFFERSIZE;
/* Build ADV_SCSI_REQ_Q */
use_sg = scsi_dma_map(scp);
- if (use_sg == 0) {
+ if (use_sg < 0) {
+ ASC_DBG(1, "failed to map SG list\n");
+ ASC_STATS(scp->device->host, adv_build_noreq);
+ return ASC_BUSY;
+ } else if (use_sg == 0) {
/* Zero-length transfer */
reqp->sgblkp = NULL;
scsiqp->data_cnt = 0;
- scsiqp->vdata_addr = NULL;
scsiqp->data_addr = 0;
scsiqp->sg_list_ptr = NULL;
@@ -8684,28 +7733,21 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
"ADV_MAX_SG_LIST %d\n", use_sg,
scp->device->host->sg_tablesize);
scsi_dma_unmap(scp);
- scp->result = HOST_BYTE(DID_ERROR);
-
- /*
- * Free the 'adv_req_t' structure by adding it back
- * to the board free list.
- */
- reqp->next_reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp;
+ set_host_byte(scp, DID_ERROR);
+ reqp->cmndp = NULL;
+ scp->host_scribble = NULL;
return ASC_ERROR;
}
scsiqp->data_cnt = cpu_to_le32(scsi_bufflen(scp));
- ret = adv_get_sglist(boardp, reqp, scp, use_sg);
+ ret = adv_get_sglist(boardp, reqp, scsiqp, scp, use_sg);
if (ret != ADV_SUCCESS) {
- /*
- * Free the adv_req_t structure by adding it back to
- * the board free list.
- */
- reqp->next_reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp;
+ scsi_dma_unmap(scp);
+ set_host_byte(scp, DID_ERROR);
+ reqp->cmndp = NULL;
+ scp->host_scribble = NULL;
return ret;
}
@@ -8718,7 +7760,7 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
- *adv_scsiqpp = scsiqp;
+ *adv_reqpp = reqp;
return ASC_NOERROR;
}
@@ -8850,7 +7892,7 @@ static int AscPutReadyQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
}
q_addr = ASC_QNO_TO_QADDR(q_no);
if ((scsiq->q1.target_id & asc_dvc->use_tagged_qng) == 0) {
- scsiq->q2.tag_code &= ~MSG_SIMPLE_TAG;
+ scsiq->q2.tag_code &= ~SIMPLE_QUEUE_TAG;
}
scsiq->q1.status = QS_FREE;
AscMemWordCopyPtrToLram(iop_base,
@@ -8875,8 +7917,8 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
int i;
ASC_SG_HEAD *sg_head;
ASC_SG_LIST_Q scsi_sg_q;
- ASC_DCNT saved_data_addr;
- ASC_DCNT saved_data_cnt;
+ __le32 saved_data_addr;
+ __le32 saved_data_cnt;
PortAddr iop_base;
ushort sg_list_dwords;
ushort sg_index;
@@ -8888,42 +7930,15 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
sg_head = scsiq->sg_head;
saved_data_addr = scsiq->q1.data_addr;
saved_data_cnt = scsiq->q1.data_cnt;
- scsiq->q1.data_addr = (ASC_PADDR) sg_head->sg_list[0].addr;
- scsiq->q1.data_cnt = (ASC_DCNT) sg_head->sg_list[0].bytes;
-#if CC_VERY_LONG_SG_LIST
+ scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr);
+ scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes);
/*
- * If sg_head->entry_cnt is greater than ASC_MAX_SG_LIST
- * then not all SG elements will fit in the allocated queues.
- * The rest of the SG elements will be copied when the RISC
- * completes the SG elements that fit and halts.
+ * Set sg_entry_cnt to be the number of SG elements that
+ * will fit in the allocated SG queues. It is minus 1, because
+ * the first SG element is handled above.
*/
- if (sg_head->entry_cnt > ASC_MAX_SG_LIST) {
- /*
- * Set sg_entry_cnt to be the number of SG elements that
- * will fit in the allocated SG queues. It is minus 1, because
- * the first SG element is handled above. ASC_MAX_SG_LIST is
- * already inflated by 1 to account for this. For example it
- * may be 50 which is 1 + 7 queues * 7 SG elements.
- */
- sg_entry_cnt = ASC_MAX_SG_LIST - 1;
+ sg_entry_cnt = sg_head->entry_cnt - 1;
- /*
- * Keep track of remaining number of SG elements that will
- * need to be handled from a_isr.c.
- */
- scsiq->remain_sg_entry_cnt =
- sg_head->entry_cnt - ASC_MAX_SG_LIST;
- } else {
-#endif /* CC_VERY_LONG_SG_LIST */
- /*
- * Set sg_entry_cnt to be the number of SG elements that
- * will fit in the allocated SG queues. It is minus 1, because
- * the first SG element is handled above.
- */
- sg_entry_cnt = sg_head->entry_cnt - 1;
-#if CC_VERY_LONG_SG_LIST
- }
-#endif /* CC_VERY_LONG_SG_LIST */
if (sg_entry_cnt != 0) {
scsiq->q1.cntl |= QC_SG_HEAD;
q_addr = ASC_QNO_TO_QADDR(q_no);
@@ -8948,21 +7963,7 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
ASC_SG_LIST_PER_Q - 1;
}
} else {
-#if CC_VERY_LONG_SG_LIST
- /*
- * This is the last SG queue in the list of
- * allocated SG queues. If there are more
- * SG elements than will fit in the allocated
- * queues, then set the QCSG_SG_XFER_MORE flag.
- */
- if (sg_head->entry_cnt > ASC_MAX_SG_LIST) {
- scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
- } else {
-#endif /* CC_VERY_LONG_SG_LIST */
- scsi_sg_q.cntl |= QCSG_SG_XFER_END;
-#if CC_VERY_LONG_SG_LIST
- }
-#endif /* CC_VERY_LONG_SG_LIST */
+ scsi_sg_q.cntl |= QCSG_SG_XFER_END;
sg_list_dwords = sg_entry_cnt << 1;
if (i == 0) {
scsi_sg_q.sg_list_cnt = sg_entry_cnt;
@@ -9067,9 +8068,9 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
PortAddr iop_base;
int sta;
int n_q_required;
- int disable_syn_offset_one_fix;
+ bool disable_syn_offset_one_fix;
int i;
- ASC_PADDR addr;
+ u32 addr;
ushort sg_entry_cnt = 0;
ushort sg_entry_cnt_minus_one = 0;
uchar target_ix;
@@ -9079,12 +8080,12 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
uchar scsi_cmd;
uchar disable_cmd;
ASC_SG_HEAD *sg_head;
- ASC_DCNT data_cnt;
+ unsigned long data_cnt;
iop_base = asc_dvc->iop_base;
sg_head = scsiq->sg_head;
if (asc_dvc->err_code != 0)
- return (ERR);
+ return ASC_ERROR;
scsiq->q1.q_no = 0;
if ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0) {
scsiq->q1.extra_bytes = 0;
@@ -9110,46 +8111,41 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
}
if (asc_dvc->in_critical_cnt != 0) {
AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CRITICAL_RE_ENTRY);
- return (ERR);
+ return ASC_ERROR;
}
asc_dvc->in_critical_cnt++;
if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
if ((sg_entry_cnt = sg_head->entry_cnt) == 0) {
asc_dvc->in_critical_cnt--;
- return (ERR);
+ return ASC_ERROR;
}
-#if !CC_VERY_LONG_SG_LIST
if (sg_entry_cnt > ASC_MAX_SG_LIST) {
asc_dvc->in_critical_cnt--;
- return (ERR);
+ return ASC_ERROR;
}
-#endif /* !CC_VERY_LONG_SG_LIST */
if (sg_entry_cnt == 1) {
- scsiq->q1.data_addr =
- (ADV_PADDR)sg_head->sg_list[0].addr;
- scsiq->q1.data_cnt =
- (ADV_DCNT)sg_head->sg_list[0].bytes;
+ scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr);
+ scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes);
scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE);
}
sg_entry_cnt_minus_one = sg_entry_cnt - 1;
}
scsi_cmd = scsiq->cdbptr[0];
- disable_syn_offset_one_fix = FALSE;
+ disable_syn_offset_one_fix = false;
if ((asc_dvc->pci_fix_asyn_xfer & scsiq->q1.target_id) &&
!(asc_dvc->pci_fix_asyn_xfer_always & scsiq->q1.target_id)) {
if (scsiq->q1.cntl & QC_SG_HEAD) {
data_cnt = 0;
for (i = 0; i < sg_entry_cnt; i++) {
- data_cnt +=
- (ADV_DCNT)le32_to_cpu(sg_head->sg_list[i].
- bytes);
+ data_cnt += le32_to_cpu(sg_head->sg_list[i].
+ bytes);
}
} else {
data_cnt = le32_to_cpu(scsiq->q1.data_cnt);
}
if (data_cnt != 0UL) {
if (data_cnt < 512UL) {
- disable_syn_offset_one_fix = TRUE;
+ disable_syn_offset_one_fix = true;
} else {
for (i = 0; i < ASC_SYN_OFFSET_ONE_DISABLE_LIST;
i++) {
@@ -9160,7 +8156,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
}
if (scsi_cmd == disable_cmd) {
disable_syn_offset_one_fix =
- TRUE;
+ true;
break;
}
}
@@ -9168,7 +8164,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
}
}
if (disable_syn_offset_one_fix) {
- scsiq->q2.tag_code &= ~MSG_SIMPLE_TAG;
+ scsiq->q2.tag_code &= ~SIMPLE_QUEUE_TAG;
scsiq->q2.tag_code |= (ASC_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX |
ASC_TAG_FLAG_DISABLE_DISCONNECT);
} else {
@@ -9179,12 +8175,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) {
if ((scsi_cmd == READ_6) ||
(scsi_cmd == READ_10)) {
- addr =
- (ADV_PADDR)le32_to_cpu(sg_head->
+ addr = le32_to_cpu(sg_head->
sg_list
[sg_entry_cnt_minus_one].
addr) +
- (ADV_DCNT)le32_to_cpu(sg_head->
+ le32_to_cpu(sg_head->
sg_list
[sg_entry_cnt_minus_one].
bytes);
@@ -9205,8 +8200,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
sg_list
[sg_entry_cnt_minus_one].
bytes);
- data_cnt -=
- (ASC_DCNT) extra_bytes;
+ data_cnt -= extra_bytes;
sg_head->
sg_list
[sg_entry_cnt_minus_one].
@@ -9217,16 +8211,6 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
}
}
sg_head->entry_to_copy = sg_head->entry_cnt;
-#if CC_VERY_LONG_SG_LIST
- /*
- * Set the sg_entry_cnt to the maximum possible. The rest of
- * the SG elements will be copied when the RISC completes the
- * SG elements that fit and halts.
- */
- if (sg_entry_cnt > ASC_MAX_SG_LIST) {
- sg_entry_cnt = ASC_MAX_SG_LIST;
- }
-#endif /* CC_VERY_LONG_SG_LIST */
n_q_required = AscSgListToQueue(sg_entry_cnt);
if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, n_q_required) >=
(uint) n_q_required)
@@ -9261,8 +8245,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
== 0) {
scsiq->q2.tag_code |=
ASC_TAG_FLAG_EXTRA_BYTES;
- data_cnt -= (ASC_DCNT)
- extra_bytes;
+ data_cnt -= extra_bytes;
scsiq->q1.data_cnt =
cpu_to_le32
(data_cnt);
@@ -9297,7 +8280,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
* If 'done_status' is not set to QD_DO_RETRY, then 'error_retry' will be
* set to SCSI_MAX_RETRY.
*
- * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the microcode
+ * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the microcode
* for DMA addresses or math operations are byte swapped to little-endian
* order.
*
@@ -9308,11 +8291,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
* ADV_ERROR(-1) - Invalid ADV_SCSI_REQ_Q request structure
* host IC error.
*/
-static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
+static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, adv_req_t *reqp)
{
AdvPortAddr iop_base;
- ADV_PADDR req_paddr;
ADV_CARR_T *new_carrp;
+ ADV_SCSI_REQ_Q *scsiq = &reqp->scsi_req_q;
/*
* The ADV_SCSI_REQ_Q 'target_id' field should never exceed ADV_MAX_TID.
@@ -9329,39 +8312,19 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
* Allocate a carrier ensuring at least one carrier always
* remains on the freelist and initialize fields.
*/
- if ((new_carrp = asc_dvc->carr_freelist) == NULL) {
+ new_carrp = adv_get_next_carrier(asc_dvc);
+ if (!new_carrp) {
+ ASC_DBG(1, "No free carriers\n");
return ADV_BUSY;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(new_carrp->next_vpa));
- asc_dvc->carr_pending_cnt++;
- /*
- * Set the carrier to be a stopper by setting 'next_vpa'
- * to the stopper value. The current stopper will be changed
- * below to point to the new stopper.
- */
- new_carrp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
-
- /*
- * Clear the ADV_SCSI_REQ_Q done flag.
- */
- scsiq->a_flag &= ~ADV_SCSIQ_DONE;
-
- req_paddr = virt_to_bus(scsiq);
- BUG_ON(req_paddr & 31);
- /* Wait for assertion before making little-endian */
- req_paddr = cpu_to_le32(req_paddr);
+ asc_dvc->carr_pending_cnt++;
/* Save virtual and physical address of ADV_SCSI_REQ_Q and carrier. */
- scsiq->scsiq_ptr = cpu_to_le32(ADV_VADDR_TO_U32(scsiq));
- scsiq->scsiq_rptr = req_paddr;
+ scsiq->scsiq_ptr = cpu_to_le32(scsiq->srb_tag);
+ scsiq->scsiq_rptr = cpu_to_le32(reqp->req_addr);
- scsiq->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->icq_sp));
- /*
- * Every ADV_CARR_T.carr_pa is byte swapped to little-endian
- * order during initialization.
- */
+ scsiq->carr_va = asc_dvc->icq_sp->carr_va;
scsiq->carr_pa = asc_dvc->icq_sp->carr_pa;
/*
@@ -9369,7 +8332,7 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
* the microcode. The newly allocated stopper will become the new
* stopper.
*/
- asc_dvc->icq_sp->areq_vpa = req_paddr;
+ asc_dvc->icq_sp->areq_vpa = scsiq->scsiq_rptr;
/*
* Set the 'next_vpa' pointer for the old stopper to be the
@@ -9411,7 +8374,7 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
}
/*
- * Execute a single 'Scsi_Cmnd'.
+ * Execute a single 'struct scsi_cmnd'.
*/
static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
{
@@ -9424,11 +8387,10 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var;
struct asc_scsi_q asc_scsi_q;
- /* asc_build_req() can not return ASC_BUSY. */
ret = asc_build_req(boardp, scp, &asc_scsi_q);
- if (ret == ASC_ERROR) {
+ if (ret != ASC_NOERROR) {
ASC_STATS(scp->device->host, build_error);
- return ASC_ERROR;
+ return ret;
}
ret = AscExeScsiQueue(asc_dvc, &asc_scsi_q);
@@ -9436,9 +8398,9 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
err_code = asc_dvc->err_code;
} else {
ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var;
- ADV_SCSI_REQ_Q *adv_scsiqp;
+ adv_req_t *adv_reqp;
- switch (adv_build_req(boardp, scp, &adv_scsiqp)) {
+ switch (adv_build_req(boardp, scp, &adv_reqp)) {
case ASC_NOERROR:
ASC_DBG(3, "adv_build_req ASC_NOERROR\n");
break;
@@ -9458,7 +8420,7 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
return ASC_ERROR;
}
- ret = AdvExeScsiQueue(adv_dvc, adv_scsiqp);
+ ret = AdvExeScsiQueue(adv_dvc, adv_reqp);
err_code = adv_dvc->err_code;
}
@@ -9473,19 +8435,20 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
ASC_DBG(1, "ExeScsiQueue() ASC_NOERROR\n");
break;
case ASC_BUSY:
+ ASC_DBG(1, "ExeScsiQueue() ASC_BUSY\n");
ASC_STATS(scp->device->host, exe_busy);
break;
case ASC_ERROR:
scmd_printk(KERN_ERR, scp, "ExeScsiQueue() ASC_ERROR, "
"err_code 0x%x\n", err_code);
ASC_STATS(scp->device->host, exe_error);
- scp->result = HOST_BYTE(DID_ERROR);
+ set_host_byte(scp, DID_ERROR);
break;
default:
scmd_printk(KERN_ERR, scp, "ExeScsiQueue() unknown, "
"err_code 0x%x\n", err_code);
ASC_STATS(scp->device->host, exe_unknown);
- scp->result = HOST_BYTE(DID_ERROR);
+ set_host_byte(scp, DID_ERROR);
break;
}
@@ -9499,14 +8462,12 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
* This function always returns 0. Command return status is saved
* in the 'scp' result field.
*/
-static int
-advansys_queuecommand_lck(struct scsi_cmnd *scp, void (*done)(struct scsi_cmnd *))
+static int advansys_queuecommand_lck(struct scsi_cmnd *scp)
{
struct Scsi_Host *shost = scp->device->host;
int asc_res, result = 0;
ASC_STATS(shost, queuecommand);
- scp->scsi_done = done;
asc_res = asc_execute_scsi_cmnd(scp);
@@ -9527,7 +8488,7 @@ advansys_queuecommand_lck(struct scsi_cmnd *scp, void (*done)(struct scsi_cmnd *
static DEF_SCSI_QCMD(advansys_queuecommand)
-static ushort __devinit AscGetEisaChipCfg(PortAddr iop_base)
+static ushort AscGetEisaChipCfg(PortAddr iop_base)
{
PortAddr eisa_cfg_iop = (PortAddr) ASC_GET_EISA_SLOT(iop_base) |
(PortAddr) (ASC_EISA_CFG_IOP_MASK);
@@ -9538,8 +8499,8 @@ static ushort __devinit AscGetEisaChipCfg(PortAddr iop_base)
* Return the BIOS address of the adapter at the specified
* I/O port and with the specified bus type.
*/
-static unsigned short __devinit
-AscGetChipBiosAddress(PortAddr iop_base, unsigned short bus_type)
+static unsigned short AscGetChipBiosAddress(PortAddr iop_base,
+ unsigned short bus_type)
{
unsigned short cfg_lsw;
unsigned short bios_addr;
@@ -9560,17 +8521,11 @@ AscGetChipBiosAddress(PortAddr iop_base, unsigned short bus_type)
}
cfg_lsw = AscGetChipCfgLsw(iop_base);
-
- /*
- * ISA PnP uses the top bit as the 32K BIOS flag
- */
- if (bus_type == ASC_IS_ISAPNP)
- cfg_lsw &= 0x7FFF;
bios_addr = ASC_BIOS_MIN_ADDR + (cfg_lsw >> 12) * ASC_BIOS_BANK_SIZE;
return bios_addr;
}
-static uchar __devinit AscSetChipScsiID(PortAddr iop_base, uchar new_host_id)
+static uchar AscSetChipScsiID(PortAddr iop_base, uchar new_host_id)
{
ushort cfg_lsw;
@@ -9584,7 +8539,7 @@ static uchar __devinit AscSetChipScsiID(PortAddr iop_base, uchar new_host_id)
return (AscGetChipScsiID(iop_base));
}
-static unsigned char __devinit AscGetChipScsiCtrl(PortAddr iop_base)
+static unsigned char AscGetChipScsiCtrl(PortAddr iop_base)
{
unsigned char sc;
@@ -9594,8 +8549,8 @@ static unsigned char __devinit AscGetChipScsiCtrl(PortAddr iop_base)
return sc;
}
-static unsigned char __devinit
-AscGetChipVersion(PortAddr iop_base, unsigned short bus_type)
+static unsigned char AscGetChipVersion(PortAddr iop_base,
+ unsigned short bus_type)
{
if (bus_type & ASC_IS_EISA) {
PortAddr eisa_iop;
@@ -9608,19 +8563,6 @@ AscGetChipVersion(PortAddr iop_base, unsigned short bus_type)
return AscGetChipVerNo(iop_base);
}
-#ifdef CONFIG_ISA
-static void __devinit AscEnableIsaDma(uchar dma_channel)
-{
- if (dma_channel < 4) {
- outp(0x000B, (ushort)(0xC0 | dma_channel));
- outp(0x000A, dma_channel);
- } else if (dma_channel < 8) {
- outp(0x00D6, (ushort)(0xC0 | (dma_channel - 4)));
- outp(0x00D4, (ushort)(dma_channel - 4));
- }
-}
-#endif /* CONFIG_ISA */
-
static int AscStopQueueExe(PortAddr iop_base)
{
int count = 0;
@@ -9639,79 +8581,23 @@ static int AscStopQueueExe(PortAddr iop_base)
return (0);
}
-static ASC_DCNT __devinit AscGetMaxDmaCount(ushort bus_type)
+static unsigned int AscGetMaxDmaCount(ushort bus_type)
{
- if (bus_type & ASC_IS_ISA)
- return ASC_MAX_ISA_DMA_COUNT;
- else if (bus_type & (ASC_IS_EISA | ASC_IS_VL))
+ if (bus_type & (ASC_IS_EISA | ASC_IS_VL))
return ASC_MAX_VL_DMA_COUNT;
return ASC_MAX_PCI_DMA_COUNT;
}
-#ifdef CONFIG_ISA
-static ushort __devinit AscGetIsaDmaChannel(PortAddr iop_base)
-{
- ushort channel;
-
- channel = AscGetChipCfgLsw(iop_base) & 0x0003;
- if (channel == 0x03)
- return (0);
- else if (channel == 0x00)
- return (7);
- return (channel + 4);
-}
-
-static ushort __devinit AscSetIsaDmaChannel(PortAddr iop_base, ushort dma_channel)
-{
- ushort cfg_lsw;
- uchar value;
-
- if ((dma_channel >= 5) && (dma_channel <= 7)) {
- if (dma_channel == 7)
- value = 0x00;
- else
- value = dma_channel - 4;
- cfg_lsw = AscGetChipCfgLsw(iop_base) & 0xFFFC;
- cfg_lsw |= value;
- AscSetChipCfgLsw(iop_base, cfg_lsw);
- return (AscGetIsaDmaChannel(iop_base));
- }
- return 0;
-}
-
-static uchar __devinit AscGetIsaDmaSpeed(PortAddr iop_base)
-{
- uchar speed_value;
-
- AscSetBank(iop_base, 1);
- speed_value = AscReadChipDmaSpeed(iop_base);
- speed_value &= 0x07;
- AscSetBank(iop_base, 0);
- return speed_value;
-}
-
-static uchar __devinit AscSetIsaDmaSpeed(PortAddr iop_base, uchar speed_value)
-{
- speed_value &= 0x07;
- AscSetBank(iop_base, 1);
- AscWriteChipDmaSpeed(iop_base, speed_value);
- AscSetBank(iop_base, 0);
- return AscGetIsaDmaSpeed(iop_base);
-}
-#endif /* CONFIG_ISA */
-
-static ushort __devinit AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
+static void AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
{
int i;
PortAddr iop_base;
- ushort warn_code;
uchar chip_version;
iop_base = asc_dvc->iop_base;
- warn_code = 0;
asc_dvc->err_code = 0;
if ((asc_dvc->bus_type &
- (ASC_IS_ISA | ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) {
+ (ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) {
asc_dvc->err_code |= ASC_IERR_NO_BUS_TYPE;
}
AscSetChipControl(iop_base, CC_HALT);
@@ -9722,7 +8608,7 @@ static ushort __devinit AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
/* asc_dvc->init_state initialized in AscInitGetConfig(). */
asc_dvc->sdtr_done = 0;
asc_dvc->cur_total_qng = 0;
- asc_dvc->is_in_int = 0;
+ asc_dvc->is_in_int = false;
asc_dvc->in_critical_cnt = 0;
asc_dvc->last_q_shortage = 0;
asc_dvc->use_tagged_qng = 0;
@@ -9766,17 +8652,6 @@ static ushort __devinit AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
(SEC_ACTIVE_NEGATE | SEC_SLEW_RATE));
}
- asc_dvc->cfg->isa_dma_speed = ASC_DEF_ISA_DMA_SPEED;
-#ifdef CONFIG_ISA
- if ((asc_dvc->bus_type & ASC_IS_ISA) != 0) {
- if (chip_version >= ASC_CHIP_MIN_VER_ISA_PNP) {
- AscSetChipIFC(iop_base, IFC_INIT_DEFAULT);
- asc_dvc->bus_type = ASC_IS_ISAPNP;
- }
- asc_dvc->cfg->isa_dma_channel =
- (uchar)AscGetIsaDmaChannel(iop_base);
- }
-#endif /* CONFIG_ISA */
for (i = 0; i <= ASC_MAX_TID; i++) {
asc_dvc->cur_dvc_qng[i] = 0;
asc_dvc->max_dvc_qng[i] = ASC_MAX_SCSI1_QNG;
@@ -9784,10 +8659,9 @@ static ushort __devinit AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q *)0L;
asc_dvc->cfg->max_tag_qng[i] = ASC_MAX_INRAM_TAG_QNG;
}
- return warn_code;
}
-static int __devinit AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg)
+static int AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg)
{
int retry;
@@ -9802,12 +8676,12 @@ static int __devinit AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg)
return 0;
}
-static void __devinit AscWaitEEPRead(void)
+static void AscWaitEEPRead(void)
{
mdelay(1);
}
-static ushort __devinit AscReadEEPWord(PortAddr iop_base, uchar addr)
+static ushort AscReadEEPWord(PortAddr iop_base, uchar addr)
{
ushort read_wval;
uchar cmd_reg;
@@ -9822,8 +8696,8 @@ static ushort __devinit AscReadEEPWord(PortAddr iop_base, uchar addr)
return read_wval;
}
-static ushort __devinit
-AscGetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, ushort bus_type)
+static ushort AscGetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf,
+ ushort bus_type)
{
ushort wval;
ushort sum;
@@ -9869,7 +8743,7 @@ AscGetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, ushort bus_type)
return sum;
}
-static int __devinit AscTestExternalLram(ASC_DVC_VAR *asc_dvc)
+static int AscTestExternalLram(ASC_DVC_VAR *asc_dvc)
{
PortAddr iop_base;
ushort q_addr;
@@ -9891,18 +8765,18 @@ static int __devinit AscTestExternalLram(ASC_DVC_VAR *asc_dvc)
return (sta);
}
-static void __devinit AscWaitEEPWrite(void)
+static void AscWaitEEPWrite(void)
{
mdelay(20);
}
-static int __devinit AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg)
+static int AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg)
{
ushort read_back;
int retry;
retry = 0;
- while (TRUE) {
+ while (true) {
AscSetChipEEPData(iop_base, data_reg);
mdelay(1);
read_back = AscGetChipEEPData(iop_base);
@@ -9915,8 +8789,7 @@ static int __devinit AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg)
}
}
-static ushort __devinit
-AscWriteEEPWord(PortAddr iop_base, uchar addr, ushort word_val)
+static ushort AscWriteEEPWord(PortAddr iop_base, uchar addr, ushort word_val)
{
ushort read_wval;
@@ -9936,8 +8809,8 @@ AscWriteEEPWord(PortAddr iop_base, uchar addr, ushort word_val)
return (read_wval);
}
-static int __devinit
-AscSetEEPConfigOnce(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, ushort bus_type)
+static int AscSetEEPConfigOnce(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf,
+ ushort bus_type)
{
int n_error;
ushort *wbuf;
@@ -10032,14 +8905,14 @@ AscSetEEPConfigOnce(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, ushort bus_type)
return n_error;
}
-static int __devinit
-AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, ushort bus_type)
+static int AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf,
+ ushort bus_type)
{
int retry;
int n_error;
retry = 0;
- while (TRUE) {
+ while (true) {
if ((n_error = AscSetEEPConfigOnce(iop_base, cfg_buf,
bus_type)) == 0) {
break;
@@ -10051,7 +8924,7 @@ AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, ushort bus_type)
return n_error;
}
-static ushort __devinit AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
+static int AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
{
ASCEEP_CONFIG eep_config_buf;
ASCEEP_CONFIG *eep_config;
@@ -10066,13 +8939,13 @@ static ushort __devinit AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
warn_code = 0;
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0x00FE);
AscStopQueueExe(iop_base);
- if ((AscStopChip(iop_base) == FALSE) ||
+ if ((AscStopChip(iop_base)) ||
(AscGetChipScsiCtrl(iop_base) != 0)) {
asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE;
AscResetChipAndScsiBus(asc_dvc);
mdelay(asc_dvc->scsi_reset_wait * 1000); /* XXX: msleep? */
}
- if (AscIsChipHalted(iop_base) == FALSE) {
+ if (!AscIsChipHalted(iop_base)) {
asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
return (warn_code);
}
@@ -10142,7 +9015,6 @@ static ushort __devinit AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
asc_dvc->cfg->sdtr_enable = eep_config->init_sdtr;
asc_dvc->cfg->disc_enable = eep_config->disc_enable;
asc_dvc->cfg->cmd_qng_enabled = eep_config->use_cmd_qng;
- asc_dvc->cfg->isa_dma_speed = ASC_EEP_GET_DMA_SPD(eep_config);
asc_dvc->start_motor = eep_config->start_motor;
asc_dvc->dvc_cntl = eep_config->cntl;
asc_dvc->no_scam = eep_config->no_scam;
@@ -10216,7 +9088,7 @@ static ushort __devinit AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
return (warn_code);
}
-static int __devinit AscInitGetConfig(struct Scsi_Host *shost)
+static int AscInitGetConfig(struct Scsi_Host *shost)
{
struct asc_board *board = shost_priv(shost);
ASC_DVC_VAR *asc_dvc = &board->dvc_var.asc_dvc_var;
@@ -10227,8 +9099,8 @@ static int __devinit AscInitGetConfig(struct Scsi_Host *shost)
return asc_dvc->err_code;
if (AscFindSignature(asc_dvc->iop_base)) {
- warn_code |= AscInitAscDvcVar(asc_dvc);
- warn_code |= AscInitFromEEP(asc_dvc);
+ AscInitAscDvcVar(asc_dvc);
+ warn_code = AscInitFromEEP(asc_dvc);
asc_dvc->init_state |= ASC_INIT_STATE_END_GET_CFG;
if (asc_dvc->scsi_reset_wait > ASC_MAX_SCSI_RESET_WAIT)
asc_dvc->scsi_reset_wait = ASC_MAX_SCSI_RESET_WAIT;
@@ -10270,7 +9142,7 @@ static int __devinit AscInitGetConfig(struct Scsi_Host *shost)
return asc_dvc->err_code;
}
-static int __devinit AscInitSetConfig(struct pci_dev *pdev, struct Scsi_Host *shost)
+static int AscInitSetConfig(struct pci_dev *pdev, struct Scsi_Host *shost)
{
struct asc_board *board = shost_priv(shost);
ASC_DVC_VAR *asc_dvc = &board->dvc_var.asc_dvc_var;
@@ -10315,22 +9187,10 @@ static int __devinit AscInitSetConfig(struct pci_dev *pdev, struct Scsi_Host *sh
}
} else
#endif /* CONFIG_PCI */
- if (asc_dvc->bus_type == ASC_IS_ISAPNP) {
- if (AscGetChipVersion(iop_base, asc_dvc->bus_type)
- == ASC_CHIP_VER_ASYN_BUG) {
- asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ASYN_USE_SYN;
- }
- }
if (AscSetChipScsiID(iop_base, asc_dvc->cfg->chip_scsi_id) !=
asc_dvc->cfg->chip_scsi_id) {
asc_dvc->err_code |= ASC_IERR_SET_SCSI_ID;
}
-#ifdef CONFIG_ISA
- if (asc_dvc->bus_type & ASC_IS_ISA) {
- AscSetIsaDmaChannel(iop_base, asc_dvc->cfg->isa_dma_channel);
- AscSetIsaDmaSpeed(iop_base, asc_dvc->cfg->isa_dma_speed);
- }
-#endif /* CONFIG_ISA */
asc_dvc->init_state |= ASC_INIT_STATE_END_SET_CFG;
@@ -10384,7 +9244,8 @@ static int __devinit AscInitSetConfig(struct pci_dev *pdev, struct Scsi_Host *sh
* on big-endian platforms so char fields read as words are actually being
* unswapped on big-endian platforms.
*/
-static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config __devinitdata = {
+#ifdef CONFIG_PCI
+static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config = {
ADV_EEPROM_BIOS_ENABLE, /* cfg_lsw */
0x0000, /* cfg_msw */
0xFFFF, /* disc_enable */
@@ -10422,7 +9283,7 @@ static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config __devinitdata = {
0 /* num_of_err */
};
-static ADVEEP_3550_CONFIG ADVEEP_3550_Config_Field_IsChar __devinitdata = {
+static ADVEEP_3550_CONFIG ADVEEP_3550_Config_Field_IsChar = {
0, /* cfg_lsw */
0, /* cfg_msw */
0, /* -disc_enable */
@@ -10460,7 +9321,7 @@ static ADVEEP_3550_CONFIG ADVEEP_3550_Config_Field_IsChar __devinitdata = {
0 /* num_of_err */
};
-static ADVEEP_38C0800_CONFIG Default_38C0800_EEPROM_Config __devinitdata = {
+static ADVEEP_38C0800_CONFIG Default_38C0800_EEPROM_Config = {
ADV_EEPROM_BIOS_ENABLE, /* 00 cfg_lsw */
0x0000, /* 01 cfg_msw */
0xFFFF, /* 02 disc_enable */
@@ -10525,7 +9386,7 @@ static ADVEEP_38C0800_CONFIG Default_38C0800_EEPROM_Config __devinitdata = {
0 /* 63 reserved */
};
-static ADVEEP_38C0800_CONFIG ADVEEP_38C0800_Config_Field_IsChar __devinitdata = {
+static ADVEEP_38C0800_CONFIG ADVEEP_38C0800_Config_Field_IsChar = {
0, /* 00 cfg_lsw */
0, /* 01 cfg_msw */
0, /* 02 disc_enable */
@@ -10590,7 +9451,7 @@ static ADVEEP_38C0800_CONFIG ADVEEP_38C0800_Config_Field_IsChar __devinitdata =
0 /* 63 reserved */
};
-static ADVEEP_38C1600_CONFIG Default_38C1600_EEPROM_Config __devinitdata = {
+static ADVEEP_38C1600_CONFIG Default_38C1600_EEPROM_Config = {
ADV_EEPROM_BIOS_ENABLE, /* 00 cfg_lsw */
0x0000, /* 01 cfg_msw */
0xFFFF, /* 02 disc_enable */
@@ -10655,7 +9516,7 @@ static ADVEEP_38C1600_CONFIG Default_38C1600_EEPROM_Config __devinitdata = {
0 /* 63 reserved */
};
-static ADVEEP_38C1600_CONFIG ADVEEP_38C1600_Config_Field_IsChar __devinitdata = {
+static ADVEEP_38C1600_CONFIG ADVEEP_38C1600_Config_Field_IsChar = {
0, /* 00 cfg_lsw */
0, /* 01 cfg_msw */
0, /* 02 disc_enable */
@@ -10720,11 +9581,10 @@ static ADVEEP_38C1600_CONFIG ADVEEP_38C1600_Config_Field_IsChar __devinitdata =
0 /* 63 reserved */
};
-#ifdef CONFIG_PCI
/*
* Wait for EEPROM command to complete
*/
-static void __devinit AdvWaitEEPCmd(AdvPortAddr iop_base)
+static void AdvWaitEEPCmd(AdvPortAddr iop_base)
{
int eep_delay_ms;
@@ -10743,7 +9603,7 @@ static void __devinit AdvWaitEEPCmd(AdvPortAddr iop_base)
/*
* Read the EEPROM from specified location
*/
-static ushort __devinit AdvReadEEPWord(AdvPortAddr iop_base, int eep_word_addr)
+static ushort AdvReadEEPWord(AdvPortAddr iop_base, int eep_word_addr)
{
AdvWriteWordRegister(iop_base, IOPW_EE_CMD,
ASC_EEP_CMD_READ | eep_word_addr);
@@ -10754,8 +9614,8 @@ static ushort __devinit AdvReadEEPWord(AdvPortAddr iop_base, int eep_word_addr)
/*
* Write the EEPROM from 'cfg_buf'.
*/
-static void __devinit
-AdvSet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf)
+static void AdvSet3550EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_3550_CONFIG *cfg_buf)
{
ushort *wbuf;
ushort addr, chksum;
@@ -10821,8 +9681,8 @@ AdvSet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf)
/*
* Write the EEPROM from 'cfg_buf'.
*/
-static void __devinit
-AdvSet38C0800EEPConfig(AdvPortAddr iop_base, ADVEEP_38C0800_CONFIG *cfg_buf)
+static void AdvSet38C0800EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C0800_CONFIG *cfg_buf)
{
ushort *wbuf;
ushort *charfields;
@@ -10888,8 +9748,8 @@ AdvSet38C0800EEPConfig(AdvPortAddr iop_base, ADVEEP_38C0800_CONFIG *cfg_buf)
/*
* Write the EEPROM from 'cfg_buf'.
*/
-static void __devinit
-AdvSet38C1600EEPConfig(AdvPortAddr iop_base, ADVEEP_38C1600_CONFIG *cfg_buf)
+static void AdvSet38C1600EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C1600_CONFIG *cfg_buf)
{
ushort *wbuf;
ushort *charfields;
@@ -10957,8 +9817,8 @@ AdvSet38C1600EEPConfig(AdvPortAddr iop_base, ADVEEP_38C1600_CONFIG *cfg_buf)
*
* Return a checksum based on the EEPROM configuration read.
*/
-static ushort __devinit
-AdvGet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf)
+static ushort AdvGet3550EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_3550_CONFIG *cfg_buf)
{
ushort wval, chksum;
ushort *wbuf;
@@ -11000,8 +9860,8 @@ AdvGet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf)
*
* Return a checksum based on the EEPROM configuration read.
*/
-static ushort __devinit
-AdvGet38C0800EEPConfig(AdvPortAddr iop_base, ADVEEP_38C0800_CONFIG *cfg_buf)
+static ushort AdvGet38C0800EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C0800_CONFIG *cfg_buf)
{
ushort wval, chksum;
ushort *wbuf;
@@ -11043,8 +9903,8 @@ AdvGet38C0800EEPConfig(AdvPortAddr iop_base, ADVEEP_38C0800_CONFIG *cfg_buf)
*
* Return a checksum based on the EEPROM configuration read.
*/
-static ushort __devinit
-AdvGet38C1600EEPConfig(AdvPortAddr iop_base, ADVEEP_38C1600_CONFIG *cfg_buf)
+static ushort AdvGet38C1600EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C1600_CONFIG *cfg_buf)
{
ushort wval, chksum;
ushort *wbuf;
@@ -11093,7 +9953,7 @@ AdvGet38C1600EEPConfig(AdvPortAddr iop_base, ADVEEP_38C1600_CONFIG *cfg_buf)
*
* Note: Chip is stopped on entry.
*/
-static int __devinit AdvInitFrom3550EEP(ADV_DVC_VAR *asc_dvc)
+static int AdvInitFrom3550EEP(ADV_DVC_VAR *asc_dvc)
{
AdvPortAddr iop_base;
ushort warn_code;
@@ -11243,7 +10103,7 @@ static int __devinit AdvInitFrom3550EEP(ADV_DVC_VAR *asc_dvc)
*
* Note: Chip is stopped on entry.
*/
-static int __devinit AdvInitFrom38C0800EEP(ADV_DVC_VAR *asc_dvc)
+static int AdvInitFrom38C0800EEP(ADV_DVC_VAR *asc_dvc)
{
AdvPortAddr iop_base;
ushort warn_code;
@@ -11442,7 +10302,7 @@ static int __devinit AdvInitFrom38C0800EEP(ADV_DVC_VAR *asc_dvc)
*
* Note: Chip is stopped on entry.
*/
-static int __devinit AdvInitFrom38C1600EEP(ADV_DVC_VAR *asc_dvc)
+static int AdvInitFrom38C1600EEP(ADV_DVC_VAR *asc_dvc)
{
AdvPortAddr iop_base;
ushort warn_code;
@@ -11662,8 +10522,7 @@ static int __devinit AdvInitFrom38C1600EEP(ADV_DVC_VAR *asc_dvc)
* For a non-fatal error return a warning code. If there are no warnings
* then 0 is returned.
*/
-static int __devinit
-AdvInitGetConfig(struct pci_dev *pdev, struct Scsi_Host *shost)
+static int AdvInitGetConfig(struct pci_dev *pdev, struct Scsi_Host *shost)
{
struct asc_board *board = shost_priv(shost);
ADV_DVC_VAR *asc_dvc = &board->dvc_var.adv_dvc_var;
@@ -11743,51 +10602,36 @@ AdvInitGetConfig(struct pci_dev *pdev, struct Scsi_Host *shost)
}
#endif
-static struct scsi_host_template advansys_template = {
+static const struct scsi_host_template advansys_template = {
.proc_name = DRV_NAME,
#ifdef CONFIG_PROC_FS
- .proc_info = advansys_proc_info,
+ .show_info = advansys_show_info,
#endif
.name = DRV_NAME,
.info = advansys_info,
.queuecommand = advansys_queuecommand,
- .eh_bus_reset_handler = advansys_reset,
+ .eh_host_reset_handler = advansys_reset,
.bios_param = advansys_biosparam,
- .slave_configure = advansys_slave_configure,
- /*
- * Because the driver may control an ISA adapter 'unchecked_isa_dma'
- * must be set. The flag will be cleared in advansys_board_found
- * for non-ISA adapters.
- */
- .unchecked_isa_dma = 1,
- /*
- * All adapters controlled by this driver are capable of large
- * scatter-gather lists. According to the mid-level SCSI documentation
- * this obviates any performance gain provided by setting
- * 'use_clustering'. But empirically while CPU utilization is increased
- * by enabling clustering, I/O throughput increases as well.
- */
- .use_clustering = ENABLE_CLUSTERING,
+ .sdev_configure = advansys_sdev_configure,
+ .cmd_size = sizeof(struct advansys_cmd),
};
-static int __devinit advansys_wide_init_chip(struct Scsi_Host *shost)
+static int advansys_wide_init_chip(struct Scsi_Host *shost)
{
struct asc_board *board = shost_priv(shost);
struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var;
- int req_cnt = 0;
- adv_req_t *reqp = NULL;
- int sg_cnt = 0;
- adv_sgblk_t *sgp;
+ size_t sgblk_pool_size;
int warn_code, err_code;
/*
* Allocate buffer carrier structures. The total size
- * is about 4 KB, so allocate all at once.
+ * is about 8 KB, so allocate all at once.
*/
- adv_dvc->carrier_buf = kmalloc(ADV_CARRIER_BUFSIZE, GFP_KERNEL);
- ASC_DBG(1, "carrier_buf 0x%p\n", adv_dvc->carrier_buf);
+ adv_dvc->carrier = dma_alloc_coherent(board->dev,
+ ADV_CARRIER_BUFSIZE, &adv_dvc->carrier_addr, GFP_KERNEL);
+ ASC_DBG(1, "carrier 0x%p\n", adv_dvc->carrier);
- if (!adv_dvc->carrier_buf)
+ if (!adv_dvc->carrier)
goto kmalloc_failed;
/*
@@ -11795,54 +10639,34 @@ static int __devinit advansys_wide_init_chip(struct Scsi_Host *shost)
* board. The total size is about 16 KB, so allocate all at once.
* If the allocation fails decrement and try again.
*/
- for (req_cnt = adv_dvc->max_host_qng; req_cnt > 0; req_cnt--) {
- reqp = kmalloc(sizeof(adv_req_t) * req_cnt, GFP_KERNEL);
-
- ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", reqp, req_cnt,
- (ulong)sizeof(adv_req_t) * req_cnt);
-
- if (reqp)
- break;
+ board->adv_reqp_size = adv_dvc->max_host_qng * sizeof(adv_req_t);
+ if (board->adv_reqp_size & 0x1f) {
+ ASC_DBG(1, "unaligned reqp %lu bytes\n", sizeof(adv_req_t));
+ board->adv_reqp_size = ADV_32BALIGN(board->adv_reqp_size);
}
+ board->adv_reqp = dma_alloc_coherent(board->dev, board->adv_reqp_size,
+ &board->adv_reqp_addr, GFP_KERNEL);
- if (!reqp)
+ if (!board->adv_reqp)
goto kmalloc_failed;
- adv_dvc->orig_reqp = reqp;
+ ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", board->adv_reqp,
+ adv_dvc->max_host_qng, board->adv_reqp_size);
/*
* Allocate up to ADV_TOT_SG_BLOCK request structures for
* the Wide board. Each structure is about 136 bytes.
*/
- board->adv_sgblkp = NULL;
- for (sg_cnt = 0; sg_cnt < ADV_TOT_SG_BLOCK; sg_cnt++) {
- sgp = kmalloc(sizeof(adv_sgblk_t), GFP_KERNEL);
-
- if (!sgp)
- break;
-
- sgp->next_sgblkp = board->adv_sgblkp;
- board->adv_sgblkp = sgp;
-
- }
+ sgblk_pool_size = sizeof(adv_sgblk_t) * ADV_TOT_SG_BLOCK;
+ board->adv_sgblk_pool = dma_pool_create("adv_sgblk", board->dev,
+ sgblk_pool_size, 32, 0);
- ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", sg_cnt, sizeof(adv_sgblk_t),
- sizeof(adv_sgblk_t) * sg_cnt);
+ ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", ADV_TOT_SG_BLOCK,
+ sizeof(adv_sgblk_t), sgblk_pool_size);
- if (!board->adv_sgblkp)
+ if (!board->adv_sgblk_pool)
goto kmalloc_failed;
- /*
- * Point 'adv_reqp' to the request structures and
- * link them together.
- */
- req_cnt--;
- reqp[req_cnt].next_reqp = NULL;
- for (; req_cnt > 0; req_cnt--) {
- reqp[req_cnt - 1].next_reqp = &reqp[req_cnt];
- }
- board->adv_reqp = &reqp[0];
-
if (adv_dvc->chip_type == ADV_CHIP_ASC3550) {
ASC_DBG(2, "AdvInitAsc3550Driver()\n");
warn_code = AdvInitAsc3550Driver(adv_dvc);
@@ -11872,19 +10696,25 @@ static int __devinit advansys_wide_init_chip(struct Scsi_Host *shost)
static void advansys_wide_free_mem(struct asc_board *board)
{
struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var;
- kfree(adv_dvc->carrier_buf);
- adv_dvc->carrier_buf = NULL;
- kfree(adv_dvc->orig_reqp);
- adv_dvc->orig_reqp = board->adv_reqp = NULL;
- while (board->adv_sgblkp) {
- adv_sgblk_t *sgp = board->adv_sgblkp;
- board->adv_sgblkp = sgp->next_sgblkp;
- kfree(sgp);
+
+ if (adv_dvc->carrier) {
+ dma_free_coherent(board->dev, ADV_CARRIER_BUFSIZE,
+ adv_dvc->carrier, adv_dvc->carrier_addr);
+ adv_dvc->carrier = NULL;
+ }
+ if (board->adv_reqp) {
+ dma_free_coherent(board->dev, board->adv_reqp_size,
+ board->adv_reqp, board->adv_reqp_addr);
+ board->adv_reqp = NULL;
+ }
+ if (board->adv_sgblk_pool) {
+ dma_pool_destroy(board->adv_sgblk_pool);
+ board->adv_sgblk_pool = NULL;
}
}
-static int __devinit advansys_board_found(struct Scsi_Host *shost,
- unsigned int iop, int bus_type)
+static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
+ int bus_type)
{
struct pci_dev *pdev;
struct asc_board *boardp = shost_priv(shost);
@@ -11942,20 +10772,6 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
#endif /* CONFIG_PCI */
}
-#ifdef CONFIG_PROC_FS
- /*
- * Allocate buffer for printing information from
- * /proc/scsi/advansys/[0...].
- */
- boardp->prtbuf = kmalloc(ASC_PRTBUF_SIZE, GFP_KERNEL);
- if (!boardp->prtbuf) {
- shost_printk(KERN_ERR, shost, "kmalloc(%d) returned NULL\n",
- ASC_PRTBUF_SIZE);
- ret = -ENOMEM;
- goto err_unmap;
- }
-#endif /* CONFIG_PROC_FS */
-
if (ASC_NARROW_BOARD(boardp)) {
/*
* Set the board bus type and PCI IRQ before
@@ -11963,29 +10779,21 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
*/
switch (asc_dvc_varp->bus_type) {
#ifdef CONFIG_ISA
- case ASC_IS_ISA:
- shost->unchecked_isa_dma = TRUE;
- share_irq = 0;
- break;
case ASC_IS_VL:
- shost->unchecked_isa_dma = FALSE;
share_irq = 0;
break;
case ASC_IS_EISA:
- shost->unchecked_isa_dma = FALSE;
share_irq = IRQF_SHARED;
break;
#endif /* CONFIG_ISA */
#ifdef CONFIG_PCI
case ASC_IS_PCI:
- shost->unchecked_isa_dma = FALSE;
share_irq = IRQF_SHARED;
break;
#endif /* CONFIG_PCI */
default:
shost_printk(KERN_ERR, shost, "unknown adapter type: "
"%d\n", asc_dvc_varp->bus_type);
- shost->unchecked_isa_dma = TRUE;
share_irq = 0;
break;
}
@@ -12004,16 +10812,18 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
* For Wide boards set PCI information before calling
* AdvInitGetConfig().
*/
- shost->unchecked_isa_dma = FALSE;
share_irq = IRQF_SHARED;
ASC_DBG(2, "AdvInitGetConfig()\n");
ret = AdvInitGetConfig(pdev, shost) ? -ENODEV : 0;
+#else
+ share_irq = 0;
+ ret = -ENODEV;
#endif /* CONFIG_PCI */
}
if (ret)
- goto err_free_proc;
+ goto err_unmap;
/*
* Save the EEPROM configuration so that it can be displayed
@@ -12037,7 +10847,7 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
ep->init_sdtr = asc_dvc_varp->cfg->sdtr_enable;
ep->disc_enable = asc_dvc_varp->cfg->disc_enable;
ep->use_cmd_qng = asc_dvc_varp->cfg->cmd_qng_enabled;
- ASC_EEP_SET_DMA_SPD(ep, asc_dvc_varp->cfg->isa_dma_speed);
+ ASC_EEP_SET_DMA_SPD(ep, ASC_DEF_ISA_DMA_SPEED);
ep->start_motor = asc_dvc_varp->start_motor;
ep->cntl = asc_dvc_varp->dvc_cntl;
ep->no_scam = asc_dvc_varp->no_scam;
@@ -12058,7 +10868,7 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
ASC_DBG(2, "AscInitSetConfig()\n");
ret = AscInitSetConfig(pdev, shost) ? -ENODEV : 0;
if (ret)
- goto err_free_proc;
+ goto err_unmap;
} else {
ADVEEP_3550_CONFIG *ep_3550;
ADVEEP_38C0800_CONFIG *ep_38C0800;
@@ -12191,24 +11001,6 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
}
/*
- * Following v1.3.89, 'cmd_per_lun' is no longer needed
- * and should be set to zero.
- *
- * But because of a bug introduced in v1.3.89 if the driver is
- * compiled as a module and 'cmd_per_lun' is zero, the Mid-Level
- * SCSI function 'allocate_device' will panic. To allow the driver
- * to work as a module in these kernels set 'cmd_per_lun' to 1.
- *
- * Note: This is wrong. cmd_per_lun should be set to the depth
- * you want on untagged devices always.
- #ifdef MODULE
- */
- shost->cmd_per_lun = 1;
-/* #else
- shost->cmd_per_lun = 0;
-#endif */
-
- /*
* Set the maximum number of scatter-gather elements the
* adapter can handle.
*/
@@ -12283,22 +11075,6 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
/* Register DMA Channel for Narrow boards. */
shost->dma_channel = NO_ISA_DMA; /* Default to no ISA DMA. */
-#ifdef CONFIG_ISA
- if (ASC_NARROW_BOARD(boardp)) {
- /* Register DMA channel for ISA bus. */
- if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
- shost->dma_channel = asc_dvc_varp->cfg->isa_dma_channel;
- ret = request_dma(shost->dma_channel, DRV_NAME);
- if (ret) {
- shost_printk(KERN_ERR, shost, "request_dma() "
- "%d failed %d\n",
- shost->dma_channel, ret);
- goto err_free_proc;
- }
- AscEnableIsaDma(shost->dma_channel);
- }
- }
-#endif /* CONFIG_ISA */
/* Register IRQ Number. */
ASC_DBG(2, "request_irq(%d, %p)\n", boardp->irq, shost);
@@ -12317,7 +11093,7 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
shost_printk(KERN_ERR, shost, "request_irq(): IRQ 0x%x "
"failed with %d\n", boardp->irq, ret);
}
- goto err_free_dma;
+ goto err_unmap;
}
/*
@@ -12369,17 +11145,12 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
advansys_wide_free_mem(boardp);
err_free_irq:
free_irq(boardp->irq, shost);
- err_free_dma:
-#ifdef CONFIG_ISA
- if (shost->dma_channel != NO_ISA_DMA)
- free_dma(shost->dma_channel);
-#endif
- err_free_proc:
- kfree(boardp->prtbuf);
err_unmap:
if (boardp->ioremap_addr)
iounmap(boardp->ioremap_addr);
+#ifdef CONFIG_PCI
err_shost:
+#endif
return ret;
}
@@ -12394,12 +11165,7 @@ static int advansys_release(struct Scsi_Host *shost)
ASC_DBG(1, "begin\n");
scsi_remove_host(shost);
free_irq(board->irq, shost);
-#ifdef CONFIG_ISA
- if (shost->dma_channel != NO_ISA_DMA) {
- ASC_DBG(1, "free_dma()\n");
- free_dma(shost->dma_channel);
- }
-#endif
+
if (ASC_NARROW_BOARD(board)) {
dma_unmap_single(board->dev,
board->dvc_var.asc_dvc_var.overrun_dma,
@@ -12409,7 +11175,6 @@ static int advansys_release(struct Scsi_Host *shost)
iounmap(board->ioremap_addr);
advansys_wide_free_mem(board);
}
- kfree(board->prtbuf);
scsi_host_put(shost);
ASC_DBG(1, "end\n");
return 0;
@@ -12422,79 +11187,13 @@ static PortAddr _asc_def_iop_base[ASC_IOADR_TABLE_MAX_IX] = {
0x0210, 0x0230, 0x0250, 0x0330
};
-/*
- * The ISA IRQ number is found in bits 2 and 3 of the CfgLsw. It decodes as:
- * 00: 10
- * 01: 11
- * 10: 12
- * 11: 15
- */
-static unsigned int __devinit advansys_isa_irq_no(PortAddr iop_base)
-{
- unsigned short cfg_lsw = AscGetChipCfgLsw(iop_base);
- unsigned int chip_irq = ((cfg_lsw >> 2) & 0x03) + 10;
- if (chip_irq == 13)
- chip_irq = 15;
- return chip_irq;
-}
-
-static int __devinit advansys_isa_probe(struct device *dev, unsigned int id)
-{
- int err = -ENODEV;
- PortAddr iop_base = _asc_def_iop_base[id];
- struct Scsi_Host *shost;
- struct asc_board *board;
-
- if (!request_region(iop_base, ASC_IOADR_GAP, DRV_NAME)) {
- ASC_DBG(1, "I/O port 0x%x busy\n", iop_base);
- return -ENODEV;
- }
- ASC_DBG(1, "probing I/O port 0x%x\n", iop_base);
- if (!AscFindSignature(iop_base))
- goto release_region;
- if (!(AscGetChipVersion(iop_base, ASC_IS_ISA) & ASC_CHIP_VER_ISA_BIT))
- goto release_region;
-
- err = -ENOMEM;
- shost = scsi_host_alloc(&advansys_template, sizeof(*board));
- if (!shost)
- goto release_region;
-
- board = shost_priv(shost);
- board->irq = advansys_isa_irq_no(iop_base);
- board->dev = dev;
-
- err = advansys_board_found(shost, iop_base, ASC_IS_ISA);
- if (err)
- goto free_host;
-
- dev_set_drvdata(dev, shost);
- return 0;
-
- free_host:
- scsi_host_put(shost);
- release_region:
- release_region(iop_base, ASC_IOADR_GAP);
- return err;
-}
-
-static int __devexit advansys_isa_remove(struct device *dev, unsigned int id)
+static void advansys_vlb_remove(struct device *dev, unsigned int id)
{
int ioport = _asc_def_iop_base[id];
advansys_release(dev_get_drvdata(dev));
release_region(ioport, ASC_IOADR_GAP);
- return 0;
}
-static struct isa_driver advansys_isa_driver = {
- .probe = advansys_isa_probe,
- .remove = __devexit_p(advansys_isa_remove),
- .driver = {
- .owner = THIS_MODULE,
- .name = DRV_NAME,
- },
-};
-
/*
* The VLB IRQ number is found in bits 2 to 4 of the CfgLsw. It decodes as:
* 000: invalid
@@ -12506,7 +11205,7 @@ static struct isa_driver advansys_isa_driver = {
* 110: 15
* 111: invalid
*/
-static unsigned int __devinit advansys_vlb_irq_no(PortAddr iop_base)
+static unsigned int advansys_vlb_irq_no(PortAddr iop_base)
{
unsigned short cfg_lsw = AscGetChipCfgLsw(iop_base);
unsigned int chip_irq = ((cfg_lsw >> 2) & 0x07) + 9;
@@ -12515,7 +11214,7 @@ static unsigned int __devinit advansys_vlb_irq_no(PortAddr iop_base)
return chip_irq;
}
-static int __devinit advansys_vlb_probe(struct device *dev, unsigned int id)
+static int advansys_vlb_probe(struct device *dev, unsigned int id)
{
int err = -ENODEV;
PortAddr iop_base = _asc_def_iop_base[id];
@@ -12545,6 +11244,7 @@ static int __devinit advansys_vlb_probe(struct device *dev, unsigned int id)
board = shost_priv(shost);
board->irq = advansys_vlb_irq_no(iop_base);
board->dev = dev;
+ board->shost = shost;
err = advansys_board_found(shost, iop_base, ASC_IS_VL);
if (err)
@@ -12562,14 +11262,14 @@ static int __devinit advansys_vlb_probe(struct device *dev, unsigned int id)
static struct isa_driver advansys_vlb_driver = {
.probe = advansys_vlb_probe,
- .remove = __devexit_p(advansys_isa_remove),
+ .remove = advansys_vlb_remove,
.driver = {
.owner = THIS_MODULE,
.name = "advansys_vlb",
},
};
-static struct eisa_device_id advansys_eisa_table[] __devinitdata = {
+static struct eisa_device_id advansys_eisa_table[] = {
{ "ABP7401" },
{ "ABP7501" },
{ "" }
@@ -12596,7 +11296,7 @@ struct eisa_scsi_data {
* 110: invalid
* 111: invalid
*/
-static unsigned int __devinit advansys_eisa_irq_no(struct eisa_device *edev)
+static unsigned int advansys_eisa_irq_no(struct eisa_device *edev)
{
unsigned short cfg_lsw = inw(edev->base_addr + 0xc86);
unsigned int chip_irq = ((cfg_lsw >> 8) & 0x07) + 10;
@@ -12605,7 +11305,7 @@ static unsigned int __devinit advansys_eisa_irq_no(struct eisa_device *edev)
return chip_irq;
}
-static int __devinit advansys_eisa_probe(struct device *dev)
+static int advansys_eisa_probe(struct device *dev)
{
int i, ioport, irq = 0;
int err;
@@ -12652,6 +11352,7 @@ static int __devinit advansys_eisa_probe(struct device *dev)
board = shost_priv(shost);
board->irq = irq;
board->dev = dev;
+ board->shost = shost;
err = advansys_board_found(shost, ioport, ASC_IS_EISA);
if (!err) {
@@ -12678,7 +11379,7 @@ static int __devinit advansys_eisa_probe(struct device *dev)
return err;
}
-static __devexit int advansys_eisa_remove(struct device *dev)
+static int advansys_eisa_remove(struct device *dev)
{
int i;
struct eisa_scsi_data *data = dev_get_drvdata(dev);
@@ -12702,12 +11403,12 @@ static struct eisa_driver advansys_eisa_driver = {
.driver = {
.name = DRV_NAME,
.probe = advansys_eisa_probe,
- .remove = __devexit_p(advansys_eisa_remove),
+ .remove = advansys_eisa_remove,
}
};
/* PCI Devices supported by this driver */
-static struct pci_device_id advansys_pci_tbl[] __devinitdata = {
+static const struct pci_device_id advansys_pci_tbl[] = {
{PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_1200A,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_ABP940,
@@ -12725,7 +11426,7 @@ static struct pci_device_id advansys_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, advansys_pci_tbl);
-static void __devinit advansys_set_latency(struct pci_dev *pdev)
+static void advansys_set_latency(struct pci_dev *pdev)
{
if ((pdev->device == PCI_DEVICE_ID_ASP_1200A) ||
(pdev->device == PCI_DEVICE_ID_ASP_ABP940)) {
@@ -12738,8 +11439,8 @@ static void __devinit advansys_set_latency(struct pci_dev *pdev)
}
}
-static int __devinit
-advansys_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int advansys_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
int err, ioport;
struct Scsi_Host *shost;
@@ -12768,6 +11469,7 @@ advansys_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
board = shost_priv(shost);
board->irq = pdev->irq;
board->dev = &pdev->dev;
+ board->shost = shost;
if (pdev->device == PCI_DEVICE_ID_ASP_ABP940UW ||
pdev->device == PCI_DEVICE_ID_38C0800_REV1 ||
@@ -12792,7 +11494,7 @@ advansys_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return err;
}
-static void __devexit advansys_pci_remove(struct pci_dev *pdev)
+static void advansys_pci_remove(struct pci_dev *pdev)
{
advansys_release(pci_get_drvdata(pdev));
pci_release_regions(pdev);
@@ -12803,22 +11505,17 @@ static struct pci_driver advansys_pci_driver = {
.name = DRV_NAME,
.id_table = advansys_pci_tbl,
.probe = advansys_pci_probe,
- .remove = __devexit_p(advansys_pci_remove),
+ .remove = advansys_pci_remove,
};
static int __init advansys_init(void)
{
int error;
- error = isa_register_driver(&advansys_isa_driver,
- ASC_IOADR_TABLE_MAX_IX);
- if (error)
- goto fail;
-
error = isa_register_driver(&advansys_vlb_driver,
ASC_IOADR_TABLE_MAX_IX);
if (error)
- goto unregister_isa;
+ goto fail;
error = eisa_driver_register(&advansys_eisa_driver);
if (error)
@@ -12834,8 +11531,6 @@ static int __init advansys_init(void)
eisa_driver_unregister(&advansys_eisa_driver);
unregister_vlb:
isa_unregister_driver(&advansys_vlb_driver);
- unregister_isa:
- isa_unregister_driver(&advansys_isa_driver);
fail:
return error;
}
@@ -12845,12 +11540,12 @@ static void __exit advansys_exit(void)
pci_unregister_driver(&advansys_pci_driver);
eisa_driver_unregister(&advansys_eisa_driver);
isa_unregister_driver(&advansys_vlb_driver);
- isa_unregister_driver(&advansys_isa_driver);
}
module_init(advansys_init);
module_exit(advansys_exit);
+MODULE_DESCRIPTION("AdvanSys SCSI Adapter driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("advansys/mcode.bin");
MODULE_FIRMWARE("advansys/3550.bin");
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
index c5169f01c1cd..182aa80ec4c6 100644
--- a/drivers/scsi/aha152x.c
+++ b/drivers/scsi/aha152x.c
@@ -1,18 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* aha152x.c -- Adaptec AHA-152x driver
* Author: Jürgen E. Fischer, fischer@norbit.de
* Copyright 1993-2004 Jürgen E. Fischer
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- *
* $Id: aha152x.c,v 2.7 2004/01/24 11:42:59 fischer Exp $
*
* $Log: aha152x.c,v $
@@ -228,10 +218,9 @@
* Revision 0.0 1993/08/14 19:54:25 root
* empty function bodies; detect() works.
*
- *
**************************************************************************
-
- see Documentation/scsi/aha152x.txt for configuration details
+
+ see Documentation/scsi/aha152x.rst for configuration details
**************************************************************************/
@@ -239,7 +228,6 @@
#include <asm/irq.h>
#include <linux/io.h>
#include <linux/blkdev.h>
-#include <asm/system.h>
#include <linux/completion.h>
#include <linux/errno.h>
#include <linux/string.h>
@@ -255,13 +243,16 @@
#include <linux/workqueue.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include <scsi/scsicam.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_spi.h>
-#include <scsi/scsi_eh.h>
+#include <scsi/scsicam.h>
#include "aha152x.h"
static LIST_HEAD(aha152x_host_list);
@@ -270,7 +261,7 @@ static LIST_HEAD(aha152x_host_list);
/* DEFINES */
/* For PCMCIA cards, always use AUTOCONF */
-#if defined(PCMCIA) || defined(MODULE)
+#if defined(AHA152X_PCMCIA) || defined(MODULE)
#if !defined(AUTOCONF)
#define AUTOCONF
#endif
@@ -280,49 +271,15 @@ static LIST_HEAD(aha152x_host_list);
#error define AUTOCONF or SETUP0
#endif
-#if defined(AHA152X_DEBUG)
-#define DEBUG_DEFAULT debug_eh
-
-#define DPRINTK(when,msgs...) \
- do { if(HOSTDATA(shpnt)->debug & (when)) printk(msgs); } while(0)
-
-#define DO_LOCK(flags) \
- do { \
- if(spin_is_locked(&QLOCK)) { \
- DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \
- } \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
- spin_lock_irqsave(&QLOCK,flags); \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
- QLOCKER=__func__; \
- QLOCKERL=__LINE__; \
- } while(0)
-
-#define DO_UNLOCK(flags) \
- do { \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \
- spin_unlock_irqrestore(&QLOCK,flags); \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
- QLOCKER="(not locked)"; \
- QLOCKERL=0; \
- } while(0)
-
-#else
-#define DPRINTK(when,msgs...)
#define DO_LOCK(flags) spin_lock_irqsave(&QLOCK,flags)
#define DO_UNLOCK(flags) spin_unlock_irqrestore(&QLOCK,flags)
-#endif
#define LEAD "(scsi%d:%d:%d) "
-#define WARN_LEAD KERN_WARNING LEAD
#define INFO_LEAD KERN_INFO LEAD
-#define NOTE_LEAD KERN_NOTICE LEAD
-#define ERR_LEAD KERN_ERR LEAD
-#define DEBUG_LEAD KERN_DEBUG LEAD
#define CMDINFO(cmd) \
(cmd) ? ((cmd)->device->host->host_no) : -1, \
(cmd) ? ((cmd)->device->id & 0x0f) : -1, \
- (cmd) ? ((cmd)->device->lun & 0x07) : -1
+ (cmd) ? ((u8)(cmd)->device->lun & 0x07) : -1
static inline void
CMD_INC_RESID(struct scsi_cmnd *cmd, int inc)
@@ -332,13 +289,13 @@ CMD_INC_RESID(struct scsi_cmnd *cmd, int inc)
#define DELAY_DEFAULT 1000
-#if defined(PCMCIA)
+#if defined(AHA152X_PCMCIA)
#define IRQ_MIN 0
#define IRQ_MAX 16
#else
#define IRQ_MIN 9
#if defined(__PPC)
-#define IRQ_MAX (nr_irqs-1)
+#define IRQ_MAX (irq_get_nr_irqs()-1)
#else
#define IRQ_MAX 12
#endif
@@ -346,10 +303,10 @@ CMD_INC_RESID(struct scsi_cmnd *cmd, int inc)
enum {
not_issued = 0x0001, /* command not yet issued */
- selecting = 0x0002, /* target is beeing selected */
+ selecting = 0x0002, /* target is being selected */
identified = 0x0004, /* IDENTIFY was sent */
disconnected = 0x0008, /* target disconnected */
- completed = 0x0010, /* target sent COMMAND COMPLETE */
+ completed = 0x0010, /* target sent COMMAND COMPLETE */
aborted = 0x0020, /* ABORT was sent */
resetted = 0x0040, /* BUS DEVICE RESET was sent */
spiordy = 0x0080, /* waiting for SPIORDY to raise */
@@ -359,18 +316,33 @@ enum {
check_condition = 0x0800, /* requesting sense after CHECK CONDITION */
};
+struct aha152x_cmd_priv {
+ char *ptr;
+ int this_residual;
+ struct scatterlist *buffer;
+ int status;
+ int message;
+ int sent_command;
+ int phase;
+};
+
+static struct aha152x_cmd_priv *aha152x_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
MODULE_AUTHOR("Jürgen Fischer");
MODULE_DESCRIPTION(AHA152X_REVID);
MODULE_LICENSE("GPL");
-#if !defined(PCMCIA)
+#if !defined(AHA152X_PCMCIA)
#if defined(MODULE)
static int io[] = {0, 0};
-module_param_array(io, int, NULL, 0);
+module_param_hw_array(io, int, ioport, NULL, 0);
MODULE_PARM_DESC(io,"base io address of controller");
static int irq[] = {0, 0};
-module_param_array(irq, int, NULL, 0);
+module_param_hw_array(irq, int, irq, NULL, 0);
MODULE_PARM_DESC(irq,"interrupt for controller");
static int scsiid[] = {7, 7};
@@ -397,7 +369,6 @@ static int exttrans[] = {0, 0};
module_param_array(exttrans, int, NULL, 0);
MODULE_PARM_DESC(exttrans,"use extended translation");
-#if !defined(AHA152X_DEBUG)
static int aha152x[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
module_param_array(aha152x, int, NULL, 0);
MODULE_PARM_DESC(aha152x, "parameters for first controller");
@@ -405,40 +376,36 @@ MODULE_PARM_DESC(aha152x, "parameters for first controller");
static int aha152x1[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
module_param_array(aha152x1, int, NULL, 0);
MODULE_PARM_DESC(aha152x1, "parameters for second controller");
-#else
-static int debug[] = {DEBUG_DEFAULT, DEBUG_DEFAULT};
-module_param_array(debug, int, NULL, 0);
-MODULE_PARM_DESC(debug, "flags for driver debugging");
-
-static int aha152x[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
-module_param_array(aha152x, int, NULL, 0);
-MODULE_PARM_DESC(aha152x, "parameters for first controller");
-
-static int aha152x1[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
-module_param_array(aha152x1, int, NULL, 0);
-MODULE_PARM_DESC(aha152x1, "parameters for second controller");
-#endif /* !defined(AHA152X_DEBUG) */
#endif /* MODULE */
#ifdef __ISAPNP__
-static struct isapnp_device_id id_table[] __devinitdata = {
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('A','D','P'), ISAPNP_FUNCTION(0x1505), 0 },
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('A','D','P'), ISAPNP_FUNCTION(0x1530), 0 },
+static struct isapnp_device_id id_table[] = {
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1502), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1505), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1510), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1515), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1520), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x2015), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1522), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x2215), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1530), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x3015), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1532), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x3215), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x6360), 0 },
{ ISAPNP_DEVICE_SINGLE_END, }
};
MODULE_DEVICE_TABLE(isapnp, id_table);
#endif /* ISAPNP */
-#endif /* !PCMCIA */
+#endif /* !AHA152X_PCMCIA */
-static struct scsi_host_template aha152x_driver_template;
+static const struct scsi_host_template aha152x_driver_template;
/*
* internal states of the host
*
- */
+ */
enum aha152x_state {
idle=0,
unknown,
@@ -462,39 +429,31 @@ enum aha152x_state {
*
*/
struct aha152x_hostdata {
- Scsi_Cmnd *issue_SC;
+ struct scsi_cmnd *issue_SC;
/* pending commands to issue */
- Scsi_Cmnd *current_SC;
+ struct scsi_cmnd *current_SC;
/* current command on the bus */
- Scsi_Cmnd *disconnected_SC;
+ struct scsi_cmnd *disconnected_SC;
/* commands that disconnected */
- Scsi_Cmnd *done_SC;
+ struct scsi_cmnd *done_SC;
/* command that was completed */
spinlock_t lock;
/* host lock */
-#if defined(AHA152X_DEBUG)
- const char *locker;
- /* which function has the lock */
- int lockerl; /* where did it get it */
-
- int debug; /* current debugging setting */
-#endif
-
#if defined(AHA152X_STAT)
- int total_commands;
+ int total_commands;
int disconnections;
int busfree_without_any_action;
int busfree_without_old_command;
int busfree_without_new_command;
int busfree_without_done_command;
int busfree_with_check_condition;
- int count[maxstate];
- int count_trans[maxstate];
+ int count[maxstate];
+ int count_trans[maxstate];
unsigned long time[maxstate];
#endif
@@ -506,7 +465,7 @@ struct aha152x_hostdata {
int delay; /* reset out delay */
int ext_trans; /* extended translation enabled */
- int swint; /* software-interrupt was fired during detect() */
+ int swint; /* software-interrupt was fired during detect() */
int service; /* bh needs to be run */
int in_intr; /* bh is running */
@@ -535,7 +494,7 @@ struct aha152x_hostdata {
unsigned char msgi[256];
/* received message bytes */
- int msgo_i, msgo_len;
+ int msgo_i, msgo_len;
/* number of sent bytes and length of current messages */
unsigned char msgo[256];
/* pending messages */
@@ -558,7 +517,7 @@ struct aha152x_hostdata {
*
*/
struct aha152x_scdata {
- Scsi_Cmnd *next; /* next sc in queue */
+ struct scsi_cmnd *next; /* next sc in queue */
struct completion *done;/* semaphore to block on */
struct scsi_eh_save ses;
};
@@ -678,11 +637,11 @@ static struct {
static irqreturn_t intr(int irq, void *dev_id);
static void reset_ports(struct Scsi_Host *shpnt);
static void aha152x_error(struct Scsi_Host *shpnt, char *msg);
-static void done(struct Scsi_Host *shpnt, int error);
+static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
+ unsigned char host_byte);
/* diagnostics */
-static void disp_ports(struct Scsi_Host *shpnt);
-static void show_command(Scsi_Cmnd * ptr);
+static void show_command(struct scsi_cmnd * ptr);
static void show_queues(struct Scsi_Host *shpnt);
static void disp_enintr(struct Scsi_Host *shpnt);
@@ -691,9 +650,9 @@ static void disp_enintr(struct Scsi_Host *shpnt);
* queue services:
*
*/
-static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
{
- Scsi_Cmnd *end;
+ struct scsi_cmnd *end;
SCNEXT(new_SC) = NULL;
if (!*SC)
@@ -705,9 +664,9 @@ static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
}
}
-static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd ** SC)
+static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd ** SC)
{
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
ptr = *SC;
if (ptr) {
@@ -717,9 +676,10 @@ static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd ** SC)
return ptr;
}
-static inline Scsi_Cmnd *remove_lun_SC(Scsi_Cmnd ** SC, int target, int lun)
+static inline struct scsi_cmnd *remove_lun_SC(struct scsi_cmnd ** SC,
+ int target, int lun)
{
- Scsi_Cmnd *ptr, *prev;
+ struct scsi_cmnd *ptr, *prev;
for (ptr = *SC, prev = NULL;
ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
@@ -738,9 +698,10 @@ static inline Scsi_Cmnd *remove_lun_SC(Scsi_Cmnd ** SC, int target, int lun)
return ptr;
}
-static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, Scsi_Cmnd *SCp)
+static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC,
+ struct scsi_cmnd *SCp)
{
- Scsi_Cmnd *ptr, *prev;
+ struct scsi_cmnd *ptr, *prev;
for (ptr = *SC, prev = NULL;
ptr && SCp!=ptr;
@@ -804,10 +765,6 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
DELAY = setup->delay;
EXT_TRANS = setup->ext_trans;
-#if defined(AHA152X_DEBUG)
- HOSTDATA(shpnt)->debug = setup->debug;
-#endif
-
SETPORT(SCSIID, setup->scsiid << 4);
shpnt->this_id = setup->scsiid;
@@ -849,7 +806,7 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
SETPORT(SIMODE0, 0);
SETPORT(SIMODE1, 0);
- if( request_irq(shpnt->irq, swintr, IRQF_DISABLED|IRQF_SHARED, "aha152x", shpnt) ) {
+ if (request_irq(shpnt->irq, swintr, IRQF_SHARED, "aha152x", shpnt)) {
printk(KERN_ERR "aha152x%d: irq %d busy.\n", shpnt->host_no, shpnt->irq);
goto out_host_put;
}
@@ -883,7 +840,7 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
SETPORT(SSTAT0, 0x7f);
SETPORT(SSTAT1, 0xef);
- if ( request_irq(shpnt->irq, intr, IRQF_DISABLED|IRQF_SHARED, "aha152x", shpnt) ) {
+ if (request_irq(shpnt->irq, intr, IRQF_SHARED, "aha152x", shpnt)) {
printk(KERN_ERR "aha152x%d: failed to reassign irq %d.\n", shpnt->host_no, shpnt->irq);
goto out_host_put;
}
@@ -914,7 +871,7 @@ void aha152x_release(struct Scsi_Host *shpnt)
if (shpnt->irq)
free_irq(shpnt->irq, shpnt);
-#if !defined(PCMCIA)
+#if !defined(AHA152X_PCMCIA)
if (shpnt->io_port)
release_region(shpnt->io_port, IO_RANGE);
#endif
@@ -933,31 +890,26 @@ void aha152x_release(struct Scsi_Host *shpnt)
* setup controller to generate interrupts depending
* on current state (lock has to be acquired)
*
- */
+ */
static int setup_expected_interrupts(struct Scsi_Host *shpnt)
{
if(CURRENT_SC) {
- CURRENT_SC->SCp.phase |= 1 << 16;
-
- if(CURRENT_SC->SCp.phase & selecting) {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: (seldo) (seltimo) (seldi)\n", CMDINFO(CURRENT_SC));
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
+
+ acp->phase |= 1 << 16;
+
+ if (acp->phase & selecting) {
SETPORT(SSTAT1, SELTO);
SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
SETPORT(SIMODE1, ENSELTIMO);
} else {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (busfree) %s\n", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.phase & spiordy ? "(spiordy)" : "");
- SETPORT(SIMODE0, (CURRENT_SC->SCp.phase & spiordy) ? ENSPIORDY : 0);
- SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
+ SETPORT(SIMODE0, (acp->phase & spiordy) ? ENSPIORDY : 0);
+ SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
}
} else if(STATE==seldi) {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (identify)\n", CMDINFO(CURRENT_SC));
SETPORT(SIMODE0, 0);
- SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
+ SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
} else {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: %s %s\n",
- CMDINFO(CURRENT_SC),
- DISCONNECTED_SC ? "(reselection)" : "",
- ISSUE_SC ? "(busfree)" : "");
SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
SETPORT(SIMODE1, ENSCSIRST | ( (ISSUE_SC||DONE_SC) ? ENBUSFREE : 0));
}
@@ -969,40 +921,30 @@ static int setup_expected_interrupts(struct Scsi_Host *shpnt)
}
-/*
+/*
* Queue a command and setup interrupts for a free bus.
*/
-static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete,
- int phase, void (*done)(Scsi_Cmnd *))
+static int aha152x_internal_queue(struct scsi_cmnd *SCpnt,
+ struct completion *complete, int phase)
{
+ struct aha152x_cmd_priv *acp = aha152x_priv(SCpnt);
struct Scsi_Host *shpnt = SCpnt->device->host;
unsigned long flags;
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_queue) {
- printk(INFO_LEAD "queue: %p; cmd_len=%d pieces=%d size=%u cmnd=",
- CMDINFO(SCpnt), SCpnt, SCpnt->cmd_len,
- scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
- __scsi_print_command(SCpnt->cmnd);
- }
-#endif
-
- SCpnt->scsi_done = done;
- SCpnt->SCp.phase = not_issued | phase;
- SCpnt->SCp.Status = 0x1; /* Ilegal status by SCSI standard */
- SCpnt->SCp.Message = 0;
- SCpnt->SCp.have_data_in = 0;
- SCpnt->SCp.sent_command = 0;
+ acp->phase = not_issued | phase;
+ acp->status = 0x1; /* Illegal status by SCSI standard */
+ acp->message = 0;
+ acp->sent_command = 0;
- if(SCpnt->SCp.phase & (resetting|check_condition)) {
+ if (acp->phase & (resetting | check_condition)) {
if (!SCpnt->host_scribble || SCSEM(SCpnt) || SCNEXT(SCpnt)) {
- printk(ERR_LEAD "cannot reuse command\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt, "cannot reuse command\n");
return FAILED;
}
} else {
SCpnt->host_scribble = kmalloc(sizeof(struct aha152x_scdata), GFP_ATOMIC);
if(!SCpnt->host_scribble) {
- printk(ERR_LEAD "allocation failed\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt, "allocation failed\n");
return FAILED;
}
}
@@ -1014,21 +956,18 @@ static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete,
SCp.ptr : buffer pointer
SCp.this_residual : buffer length
SCp.buffer : next buffer
- SCp.buffers_residual : left buffers in list
SCp.phase : current state of the command */
if ((phase & resetting) || !scsi_sglist(SCpnt)) {
- SCpnt->SCp.ptr = NULL;
- SCpnt->SCp.this_residual = 0;
+ acp->ptr = NULL;
+ acp->this_residual = 0;
scsi_set_resid(SCpnt, 0);
- SCpnt->SCp.buffer = NULL;
- SCpnt->SCp.buffers_residual = 0;
+ acp->buffer = NULL;
} else {
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
- SCpnt->SCp.buffer = scsi_sglist(SCpnt);
- SCpnt->SCp.ptr = SG_ADDRESS(SCpnt->SCp.buffer);
- SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
- SCpnt->SCp.buffers_residual = scsi_sg_count(SCpnt) - 1;
+ acp->buffer = scsi_sglist(SCpnt);
+ acp->ptr = SG_ADDRESS(acp->buffer);
+ acp->this_residual = acp->buffer->length;
}
DO_LOCK(flags);
@@ -1056,33 +995,19 @@ static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete,
* queue a command
*
*/
-static int aha152x_queue_lck(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+static int aha152x_queue_lck(struct scsi_cmnd *SCpnt)
{
-#if 0
- if(*SCpnt->cmnd == REQUEST_SENSE) {
- SCpnt->result = 0;
- done(SCpnt);
-
- return 0;
- }
-#endif
-
- return aha152x_internal_queue(SCpnt, NULL, 0, done);
+ return aha152x_internal_queue(SCpnt, NULL, 0);
}
static DEF_SCSI_QCMD(aha152x_queue)
/*
- *
*
*/
-static void reset_done(Scsi_Cmnd *SCpnt)
+static void reset_done(struct scsi_cmnd *SCpnt)
{
-#if 0
- struct Scsi_Host *shpnt = SCpnt->host;
- DPRINTK(debug_eh, INFO_LEAD "reset_done called\n", CMDINFO(SCpnt));
-#endif
if(SCSEM(SCpnt)) {
complete(SCSEM(SCpnt));
} else {
@@ -1090,30 +1015,29 @@ static void reset_done(Scsi_Cmnd *SCpnt)
}
}
+static void aha152x_scsi_done(struct scsi_cmnd *SCpnt)
+{
+ if (aha152x_priv(SCpnt)->phase & resetting)
+ reset_done(SCpnt);
+ else
+ scsi_done(SCpnt);
+}
+
/*
* Abort a command
*
*/
-static int aha152x_abort(Scsi_Cmnd *SCpnt)
+static int aha152x_abort(struct scsi_cmnd *SCpnt)
{
struct Scsi_Host *shpnt = SCpnt->device->host;
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
unsigned long flags;
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(DEBUG_LEAD "abort(%p)", CMDINFO(SCpnt), SCpnt);
- show_queues(shpnt);
- }
-#endif
-
DO_LOCK(flags);
ptr=remove_SC(&ISSUE_SC, SCpnt);
if(ptr) {
- DPRINTK(debug_eh, DEBUG_LEAD "not yet issued - SUCCESS\n", CMDINFO(SCpnt));
-
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0);
@@ -1123,7 +1047,7 @@ static int aha152x_abort(Scsi_Cmnd *SCpnt)
SCpnt->host_scribble=NULL;
return SUCCESS;
- }
+ }
DO_UNLOCK(flags);
@@ -1134,7 +1058,8 @@ static int aha152x_abort(Scsi_Cmnd *SCpnt)
*
*/
- printk(ERR_LEAD "cannot abort running or disconnected command\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt,
+ "cannot abort running or disconnected command\n");
return FAILED;
}
@@ -1143,24 +1068,17 @@ static int aha152x_abort(Scsi_Cmnd *SCpnt)
* Reset a device
*
*/
-static int aha152x_device_reset(Scsi_Cmnd * SCpnt)
+static int aha152x_device_reset(struct scsi_cmnd * SCpnt)
{
struct Scsi_Host *shpnt = SCpnt->device->host;
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
int ret, issued, disconnected;
unsigned char old_cmd_len = SCpnt->cmd_len;
unsigned long flags;
unsigned long timeleft;
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(INFO_LEAD "aha152x_device_reset(%p)", CMDINFO(SCpnt), SCpnt);
- show_queues(shpnt);
- }
-#endif
-
if(CURRENT_SC==SCpnt) {
- printk(ERR_LEAD "cannot reset current device\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt, "cannot reset current device\n");
return FAILED;
}
@@ -1171,7 +1089,7 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt)
SCpnt->cmd_len = 0;
- aha152x_internal_queue(SCpnt, &done, resetting, reset_done);
+ aha152x_internal_queue(SCpnt, &done, resetting);
timeleft = wait_for_completion_timeout(&done, 100*HZ);
if (!timeleft) {
@@ -1185,7 +1103,7 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt)
DO_LOCK(flags);
- if(SCpnt->SCp.phase & resetted) {
+ if (aha152x_priv(SCpnt)->phase & resetted) {
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0);
@@ -1200,7 +1118,7 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt)
} else if(disconnected) {
append_SC(&DISCONNECTED_SC, SCpnt);
}
-
+
ret = FAILED;
}
@@ -1208,23 +1126,24 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt)
return ret;
}
-static void free_hard_reset_SCs(struct Scsi_Host *shpnt, Scsi_Cmnd **SCs)
+static void free_hard_reset_SCs(struct Scsi_Host *shpnt,
+ struct scsi_cmnd **SCs)
{
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
ptr=*SCs;
while(ptr) {
- Scsi_Cmnd *next;
+ struct scsi_cmnd *next;
if(SCDATA(ptr)) {
next = SCNEXT(ptr);
} else {
- printk(DEBUG_LEAD "queue corrupted at %p\n", CMDINFO(ptr), ptr);
+ scmd_printk(KERN_DEBUG, ptr,
+ "queue corrupted at %p\n", ptr);
next = NULL;
}
if (!ptr->device->soft_reset) {
- DPRINTK(debug_eh, DEBUG_LEAD "disconnected command %p removed\n", CMDINFO(ptr), ptr);
remove_SC(SCs, ptr);
HOSTDATA(shpnt)->commands--;
kfree(ptr->host_scribble);
@@ -1238,6 +1157,9 @@ static void free_hard_reset_SCs(struct Scsi_Host *shpnt, Scsi_Cmnd **SCs)
/*
* Reset the bus
*
+ * AIC-6260 has a hard reset (MRST signal), but apparently
+ * one cannot trigger it via software. So live with
+ * a soft reset; no-one seemed to have cared.
*/
static int aha152x_bus_reset_host(struct Scsi_Host *shpnt)
{
@@ -1245,25 +1167,14 @@ static int aha152x_bus_reset_host(struct Scsi_Host *shpnt)
DO_LOCK(flags);
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(KERN_DEBUG "scsi%d: bus reset", shpnt->host_no);
- show_queues(shpnt);
- }
-#endif
-
free_hard_reset_SCs(shpnt, &ISSUE_SC);
free_hard_reset_SCs(shpnt, &DISCONNECTED_SC);
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: resetting bus\n", shpnt->host_no);
-
SETPORT(SCSISEQ, SCSIRSTO);
mdelay(256);
SETPORT(SCSISEQ, 0);
mdelay(DELAY);
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: bus resetted\n", shpnt->host_no);
-
setup_expected_interrupts(shpnt);
if(HOSTDATA(shpnt)->commands==0)
SETPORT(PORTA, 0);
@@ -1277,7 +1188,7 @@ static int aha152x_bus_reset_host(struct Scsi_Host *shpnt)
* Reset the bus
*
*/
-static int aha152x_bus_reset(Scsi_Cmnd *SCpnt)
+static int aha152x_bus_reset(struct scsi_cmnd *SCpnt)
{
return aha152x_bus_reset_host(SCpnt->device->host);
}
@@ -1325,30 +1236,17 @@ static void reset_ports(struct Scsi_Host *shpnt)
*/
int aha152x_host_reset_host(struct Scsi_Host *shpnt)
{
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: host reset\n", shpnt->host_no);
-
aha152x_bus_reset_host(shpnt);
-
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: resetting ports\n", shpnt->host_no);
reset_ports(shpnt);
return SUCCESS;
}
/*
- * Reset the host (bus and controller)
- *
- */
-static int aha152x_host_reset(Scsi_Cmnd *SCpnt)
-{
- return aha152x_host_reset_host(SCpnt->device->host);
-}
-
-/*
* Return the "logical geometry"
*
*/
-static int aha152x_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+static int aha152x_biosparam(struct scsi_device *sdev, struct gendisk *disk,
sector_t capacity, int *info_array)
{
struct Scsi_Host *shpnt = sdev->host;
@@ -1363,7 +1261,7 @@ static int aha152x_biosparam(struct scsi_device *sdev, struct block_device *bdev
int info[3];
/* try to figure out the geometry from the partition table */
- if (scsicam_bios_param(bdev, capacity, info) < 0 ||
+ if (scsicam_bios_param(disk, capacity, info) < 0 ||
!((info[0] == 64 && info[1] == 32) || (info[0] == 255 && info[1] == 63))) {
if (EXT_TRANS) {
printk(KERN_NOTICE
@@ -1377,7 +1275,7 @@ static int aha152x_biosparam(struct scsi_device *sdev, struct block_device *bdev
"aha152x: unable to verify geometry for disk with >1GB.\n"
" Using default translation. Please verify yourself.\n"
" Perhaps you need to enable extended translation in the driver.\n"
- " See Documentation/scsi/aha152x.txt for details.\n");
+ " See Documentation/scsi/aha152x.rst for details.\n");
}
} else {
info_array[0] = info[0];
@@ -1399,15 +1297,19 @@ static int aha152x_biosparam(struct scsi_device *sdev, struct block_device *bdev
* Internal done function
*
*/
-static void done(struct Scsi_Host *shpnt, int error)
+static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
+ unsigned char host_byte)
{
if (CURRENT_SC) {
if(DONE_SC)
- printk(ERR_LEAD "there's already a completed command %p - will cause abort\n", CMDINFO(CURRENT_SC), DONE_SC);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "there's already a completed command %p "
+ "- will cause abort\n", DONE_SC);
DONE_SC = CURRENT_SC;
CURRENT_SC = NULL;
- DONE_SC->result = error;
+ set_status_byte(DONE_SC, status_byte);
+ set_host_byte(DONE_SC, host_byte);
} else
printk(KERN_ERR "aha152x: done() called outside of command\n");
}
@@ -1458,7 +1360,7 @@ static irqreturn_t intr(int irqno, void *dev_id)
return IRQ_NONE;
if( TESTLO(DMASTAT, INTSTAT) )
- return IRQ_NONE;
+ return IRQ_NONE;
/* no more interrupts from the controller, while we're busy.
INTEN is restored by the BH handler */
@@ -1493,40 +1395,36 @@ static void busfree_run(struct Scsi_Host *shpnt)
SETPORT(SXFRCTL0, CH1);
SETPORT(SSTAT1, CLRBUSFREE);
-
+
if(CURRENT_SC) {
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
+
#if defined(AHA152X_STAT)
action++;
#endif
- CURRENT_SC->SCp.phase &= ~syncneg;
+ acp->phase &= ~syncneg;
- if(CURRENT_SC->SCp.phase & completed) {
+ if (acp->phase & completed) {
/* target sent COMMAND COMPLETE */
- done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16));
+ done(shpnt, acp->status, DID_OK);
- } else if(CURRENT_SC->SCp.phase & aborted) {
- DPRINTK(debug_eh, DEBUG_LEAD "ABORT sent\n", CMDINFO(CURRENT_SC));
- done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_ABORT << 16));
+ } else if (acp->phase & aborted) {
+ done(shpnt, acp->status, DID_ABORT);
- } else if(CURRENT_SC->SCp.phase & resetted) {
- DPRINTK(debug_eh, DEBUG_LEAD "BUS DEVICE RESET sent\n", CMDINFO(CURRENT_SC));
- done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_RESET << 16));
+ } else if (acp->phase & resetted) {
+ done(shpnt, acp->status, DID_RESET);
- } else if(CURRENT_SC->SCp.phase & disconnected) {
+ } else if (acp->phase & disconnected) {
/* target sent DISCONNECT */
- DPRINTK(debug_selection, DEBUG_LEAD "target disconnected at %d/%d\n",
- CMDINFO(CURRENT_SC),
- scsi_get_resid(CURRENT_SC),
- scsi_bufflen(CURRENT_SC));
#if defined(AHA152X_STAT)
HOSTDATA(shpnt)->disconnections++;
#endif
append_SC(&DISCONNECTED_SC, CURRENT_SC);
- CURRENT_SC->SCp.phase |= 1 << 16;
+ acp->phase |= 1 << 16;
CURRENT_SC = NULL;
} else {
- done(shpnt, DID_ERROR << 16);
+ done(shpnt, SAM_STAT_GOOD, DID_ERROR);
}
#if defined(AHA152X_STAT)
} else {
@@ -1541,39 +1439,26 @@ static void busfree_run(struct Scsi_Host *shpnt)
action++;
#endif
- if(DONE_SC->SCp.phase & check_condition) {
+ if (aha152x_priv(DONE_SC)->phase & check_condition) {
struct scsi_cmnd *cmd = HOSTDATA(shpnt)->done_SC;
struct aha152x_scdata *sc = SCDATA(cmd);
-#if 0
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(ERR_LEAD "received sense: ", CMDINFO(DONE_SC));
- scsi_print_sense("bh", DONE_SC);
- }
-#endif
-
scsi_eh_restore_cmnd(cmd, &sc->ses);
- cmd->SCp.Status = SAM_STAT_CHECK_CONDITION;
+ aha152x_priv(cmd)->status = SAM_STAT_CHECK_CONDITION;
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0); /* turn led off */
- } else if(DONE_SC->SCp.Status==SAM_STAT_CHECK_CONDITION) {
+ } else if (aha152x_priv(DONE_SC)->status == SAM_STAT_CHECK_CONDITION) {
#if defined(AHA152X_STAT)
HOSTDATA(shpnt)->busfree_with_check_condition++;
#endif
-#if 0
- DPRINTK(debug_eh, ERR_LEAD "CHECK CONDITION found\n", CMDINFO(DONE_SC));
-#endif
- if(!(DONE_SC->SCp.phase & not_issued)) {
+ if (!(aha152x_priv(DONE_SC)->phase & not_issued)) {
struct aha152x_scdata *sc;
- Scsi_Cmnd *ptr = DONE_SC;
+ struct scsi_cmnd *ptr = DONE_SC;
DONE_SC=NULL;
-#if 0
- DPRINTK(debug_eh, ERR_LEAD "requesting sense\n", CMDINFO(ptr));
-#endif
sc = SCDATA(ptr);
/* It was allocated in aha152x_internal_queue? */
@@ -1581,22 +1466,13 @@ static void busfree_run(struct Scsi_Host *shpnt)
scsi_eh_prep_cmnd(ptr, &sc->ses, NULL, 0, ~0);
DO_UNLOCK(flags);
- aha152x_internal_queue(ptr, NULL, check_condition, ptr->scsi_done);
+ aha152x_internal_queue(ptr, NULL, check_condition);
DO_LOCK(flags);
-#if 0
- } else {
- DPRINTK(debug_eh, ERR_LEAD "command not issued - CHECK CONDITION ignored\n", CMDINFO(DONE_SC));
-#endif
}
}
- if(DONE_SC && DONE_SC->scsi_done) {
-#if defined(AHA152X_DEBUG)
- int hostno=DONE_SC->device->host->host_no;
- int id=DONE_SC->device->id & 0xf;
- int lun=DONE_SC->device->lun & 0x7;
-#endif
- Scsi_Cmnd *ptr = DONE_SC;
+ if (DONE_SC) {
+ struct scsi_cmnd *ptr = DONE_SC;
DONE_SC=NULL;
/* turn led off, when no commands are in the driver */
@@ -1604,15 +1480,13 @@ static void busfree_run(struct Scsi_Host *shpnt)
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0); /* turn led off */
- if(ptr->scsi_done != reset_done) {
+ if (!(aha152x_priv(ptr)->phase & resetting)) {
kfree(ptr->host_scribble);
ptr->host_scribble=NULL;
}
DO_UNLOCK(flags);
- DPRINTK(debug_done, DEBUG_LEAD "calling scsi_done(%p)\n", hostno, id, lun, ptr);
- ptr->scsi_done(ptr);
- DPRINTK(debug_done, DEBUG_LEAD "scsi_done(%p) returned\n", hostno, id, lun, ptr);
+ aha152x_scsi_done(ptr);
DO_LOCK(flags);
}
@@ -1629,12 +1503,12 @@ static void busfree_run(struct Scsi_Host *shpnt)
DO_UNLOCK(flags);
if(CURRENT_SC) {
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
+
#if defined(AHA152X_STAT)
action++;
#endif
- CURRENT_SC->SCp.phase |= selecting;
-
- DPRINTK(debug_selection, DEBUG_LEAD "selecting target\n", CMDINFO(CURRENT_SC));
+ acp->phase |= selecting;
/* clear selection timeout */
SETPORT(SSTAT1, SELTO);
@@ -1662,30 +1536,33 @@ static void busfree_run(struct Scsi_Host *shpnt)
*/
static void seldo_run(struct Scsi_Host *shpnt)
{
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
+
SETPORT(SCSISIG, 0);
SETPORT(SSTAT1, CLRBUSFREE);
SETPORT(SSTAT1, CLRPHASECHG);
- CURRENT_SC->SCp.phase &= ~(selecting|not_issued);
+ acp->phase &= ~(selecting | not_issued);
SETPORT(SCSISEQ, 0);
if (TESTLO(SSTAT0, SELDO)) {
- printk(ERR_LEAD "aha152x: passing bus free condition\n", CMDINFO(CURRENT_SC));
- done(shpnt, DID_NO_CONNECT << 16);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "aha152x: passing bus free condition\n");
+ done(shpnt, SAM_STAT_GOOD, DID_NO_CONNECT);
return;
}
SETPORT(SSTAT0, CLRSELDO);
-
+
ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
- if (CURRENT_SC->SCp.phase & aborting) {
+ if (acp->phase & aborting) {
ADDMSGO(ABORT);
- } else if (CURRENT_SC->SCp.phase & resetting) {
+ } else if (acp->phase & resetting) {
ADDMSGO(BUS_DEVICE_RESET);
} else if (SYNCNEG==0 && SYNCHRONOUS) {
- CURRENT_SC->SCp.phase |= syncneg;
+ acp->phase |= syncneg;
MSGOLEN += spi_populate_sync_msg(&MSGO(MSGOLEN), 50, 8);
SYNCNEG=1; /* negotiation in progress */
}
@@ -1700,29 +1577,24 @@ static void seldo_run(struct Scsi_Host *shpnt)
*/
static void selto_run(struct Scsi_Host *shpnt)
{
- SETPORT(SCSISEQ, 0);
- SETPORT(SSTAT1, CLRSELTIMO);
+ struct aha152x_cmd_priv *acp;
- DPRINTK(debug_selection, DEBUG_LEAD "selection timeout\n", CMDINFO(CURRENT_SC));
+ SETPORT(SCSISEQ, 0);
+ SETPORT(SSTAT1, CLRSELTIMO);
- if(!CURRENT_SC) {
- DPRINTK(debug_selection, DEBUG_LEAD "!CURRENT_SC\n", CMDINFO(CURRENT_SC));
+ if (!CURRENT_SC)
return;
- }
- CURRENT_SC->SCp.phase &= ~selecting;
+ acp = aha152x_priv(CURRENT_SC);
+ acp->phase &= ~selecting;
- if (CURRENT_SC->SCp.phase & aborted) {
- DPRINTK(debug_selection, DEBUG_LEAD "aborted\n", CMDINFO(CURRENT_SC));
- done(shpnt, DID_ABORT << 16);
- } else if (TESTLO(SSTAT0, SELINGO)) {
- DPRINTK(debug_selection, DEBUG_LEAD "arbitration not won\n", CMDINFO(CURRENT_SC));
- done(shpnt, DID_BUS_BUSY << 16);
- } else {
+ if (acp->phase & aborted)
+ done(shpnt, SAM_STAT_GOOD, DID_ABORT);
+ else if (TESTLO(SSTAT0, SELINGO))
+ done(shpnt, SAM_STAT_GOOD, DID_BUS_BUSY);
+ else
/* ARBITRATION won, but SELECTION failed */
- DPRINTK(debug_selection, DEBUG_LEAD "selection failed\n", CMDINFO(CURRENT_SC));
- done(shpnt, DID_NO_CONNECT << 16);
- }
+ done(shpnt, SAM_STAT_GOOD, DID_NO_CONNECT);
}
/*
@@ -1744,10 +1616,11 @@ static void seldi_run(struct Scsi_Host *shpnt)
SETPORT(SSTAT1, CLRPHASECHG);
if(CURRENT_SC) {
- if(!(CURRENT_SC->SCp.phase & not_issued))
- printk(ERR_LEAD "command should not have been issued yet\n", CMDINFO(CURRENT_SC));
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
- DPRINTK(debug_selection, ERR_LEAD "command requeued - reselection\n", CMDINFO(CURRENT_SC));
+ if (!(acp->phase & not_issued))
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "command should not have been issued yet\n");
DO_LOCK(flags);
append_SC(&ISSUE_SC, CURRENT_SC);
@@ -1756,17 +1629,16 @@ static void seldi_run(struct Scsi_Host *shpnt)
CURRENT_SC = NULL;
}
- if(!DISCONNECTED_SC) {
- DPRINTK(debug_selection, DEBUG_LEAD "unexpected SELDI ", CMDINFO(CURRENT_SC));
+ if (!DISCONNECTED_SC)
return;
- }
RECONN_TARGET=-1;
selid = GETPORT(SELID) & ~(1 << shpnt->this_id);
if (selid==0) {
- printk("aha152x%d: target id unknown (%02x)\n", HOSTNO, selid);
+ shost_printk(KERN_INFO, shpnt,
+ "target id unknown (%02x)\n", selid);
return;
}
@@ -1774,8 +1646,8 @@ static void seldi_run(struct Scsi_Host *shpnt)
;
if(selid & ~(1 << target)) {
- printk("aha152x%d: multiple targets reconnected (%02x)\n",
- HOSTNO, selid);
+ shost_printk(KERN_INFO, shpnt,
+ "multiple targets reconnected (%02x)\n", selid);
}
@@ -1785,7 +1657,6 @@ static void seldi_run(struct Scsi_Host *shpnt)
SETRATE(HOSTDATA(shpnt)->syncrate[target]);
RECONN_TARGET=target;
- DPRINTK(debug_selection, DEBUG_LEAD "target %d reselected (%02x).\n", CMDINFO(CURRENT_SC), target, selid);
}
/*
@@ -1804,36 +1675,30 @@ static void seldi_run(struct Scsi_Host *shpnt)
static void msgi_run(struct Scsi_Host *shpnt)
{
for(;;) {
+ struct aha152x_cmd_priv *acp;
int sstat1 = GETPORT(SSTAT1);
if(sstat1 & (PHASECHG|PHASEMIS|BUSFREE) || !(sstat1 & REQINIT))
return;
- if(TESTLO(SSTAT0,SPIORDY)) {
- DPRINTK(debug_msgi, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
ADDMSGI(GETPORT(SCSIDAT));
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_msgi) {
- printk(INFO_LEAD "inbound message %02x ", CMDINFO(CURRENT_SC), MSGI(0));
- spi_print_msg(&MSGI(0));
- printk("\n");
- }
-#endif
-
if(!CURRENT_SC) {
if(LASTSTATE!=seldi) {
- printk(KERN_ERR "aha152x%d: message in w/o current command not after reselection\n", HOSTNO);
+ shost_printk(KERN_ERR, shpnt,
+ "message in w/o current command"
+ " not after reselection\n");
}
/*
- * Handle reselection
- */
+ * Handle reselection
+ */
if(!(MSGI(0) & IDENTIFY_BASE)) {
- printk(KERN_ERR "aha152x%d: target didn't identify after reselection\n", HOSTNO);
+ shost_printk(KERN_ERR, shpnt,
+ "target didn't identify after reselection\n");
continue;
}
@@ -1841,44 +1706,48 @@ static void msgi_run(struct Scsi_Host *shpnt)
if (!CURRENT_SC) {
show_queues(shpnt);
- printk(KERN_ERR "aha152x%d: no disconnected command for target %d/%d\n", HOSTNO, RECONN_TARGET, MSGI(0) & 0x3f);
+ shost_printk(KERN_ERR, shpnt,
+ "no disconnected command"
+ " for target %d/%d\n",
+ RECONN_TARGET, MSGI(0) & 0x3f);
continue;
}
- DPRINTK(debug_msgi, DEBUG_LEAD "target reconnected\n", CMDINFO(CURRENT_SC));
-
- CURRENT_SC->SCp.Message = MSGI(0);
- CURRENT_SC->SCp.phase &= ~disconnected;
+ acp = aha152x_priv(CURRENT_SC);
+ acp->message = MSGI(0);
+ acp->phase &= ~disconnected;
MSGILEN=0;
/* next message if any */
continue;
- }
+ }
- CURRENT_SC->SCp.Message = MSGI(0);
+ acp = aha152x_priv(CURRENT_SC);
+ acp->message = MSGI(0);
switch (MSGI(0)) {
case DISCONNECT:
if (!RECONNECT)
- printk(WARN_LEAD "target was not allowed to disconnect\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_WARNING, CURRENT_SC,
+ "target was not allowed to disconnect\n");
- CURRENT_SC->SCp.phase |= disconnected;
+ acp->phase |= disconnected;
break;
case COMMAND_COMPLETE:
- if(CURRENT_SC->SCp.phase & completed)
- DPRINTK(debug_msgi, DEBUG_LEAD "again COMMAND COMPLETE\n", CMDINFO(CURRENT_SC));
-
- CURRENT_SC->SCp.phase |= completed;
+ acp->phase |= completed;
break;
case MESSAGE_REJECT:
if (SYNCNEG==1) {
- printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "Synchronous Data Transfer Request"
+ " was rejected\n");
SYNCNEG=2; /* negotiation completed */
} else
- printk(INFO_LEAD "inbound message (MESSAGE REJECT)\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "inbound message (MESSAGE REJECT)\n");
break;
case SAVE_POINTERS:
@@ -1899,7 +1768,8 @@ static void msgi_run(struct Scsi_Host *shpnt)
long ticks;
if (MSGI(1) != 3) {
- printk(ERR_LEAD "SDTR message length!=3\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "SDTR message length!=3\n");
break;
}
@@ -1916,10 +1786,12 @@ static void msgi_run(struct Scsi_Host *shpnt)
/* negotiation in progress */
if (ticks > 9 || MSGI(4) < 1 || MSGI(4) > 8) {
ADDMSGO(MESSAGE_REJECT);
- printk(INFO_LEAD "received Synchronous Data Transfer Request invalid - rejected\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO,
+ CURRENT_SC,
+ "received Synchronous Data Transfer Request invalid - rejected\n");
break;
}
-
+
SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
} else if (ticks <= 9 && MSGI(4) >= 1) {
ADDMSGO(EXTENDED_MESSAGE);
@@ -1939,11 +1811,14 @@ static void msgi_run(struct Scsi_Host *shpnt)
SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
} else {
/* requested SDTR is too slow, do it asynchronously */
- printk(INFO_LEAD "Synchronous Data Transfer Request too slow - Rejecting\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO,
+ CURRENT_SC,
+ "Synchronous Data Transfer Request too slow - Rejecting\n");
ADDMSGO(MESSAGE_REJECT);
}
- SYNCNEG=2; /* negotiation completed */
+ /* negotiation completed */
+ SYNCNEG=2;
SETRATE(SYNCRATE);
}
break;
@@ -1977,12 +1852,12 @@ static void msgi_run(struct Scsi_Host *shpnt)
static void msgi_end(struct Scsi_Host *shpnt)
{
if(MSGILEN>0)
- printk(WARN_LEAD "target left before message completed (%d)\n", CMDINFO(CURRENT_SC), MSGILEN);
+ scmd_printk(KERN_WARNING, CURRENT_SC,
+ "target left before message completed (%d)\n",
+ MSGILEN);
- if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE)) {
- DPRINTK(debug_msgi, DEBUG_LEAD "msgo pending\n", CMDINFO(CURRENT_SC));
+ if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE))
SETPORT(SCSISIG, P_MSGI | SIG_ATNO);
- }
}
/*
@@ -1992,24 +1867,16 @@ static void msgi_end(struct Scsi_Host *shpnt)
static void msgo_init(struct Scsi_Host *shpnt)
{
if(MSGOLEN==0) {
- if((CURRENT_SC->SCp.phase & syncneg) && SYNCNEG==2 && SYNCRATE==0) {
+ if ((aha152x_priv(CURRENT_SC)->phase & syncneg) &&
+ SYNCNEG == 2 && SYNCRATE == 0) {
ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
} else {
- printk(INFO_LEAD "unexpected MESSAGE OUT phase; rejecting\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "unexpected MESSAGE OUT phase; rejecting\n");
ADDMSGO(MESSAGE_REJECT);
}
}
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_msgo) {
- int i;
-
- printk(DEBUG_LEAD "messages( ", CMDINFO(CURRENT_SC));
- for (i=0; i<MSGOLEN; i+=spi_print_msg(&MSGO(i)), printk(" "))
- ;
- printk(")\n");
- }
-#endif
}
/*
@@ -2018,16 +1885,11 @@ static void msgo_init(struct Scsi_Host *shpnt)
*/
static void msgo_run(struct Scsi_Host *shpnt)
{
- if(MSGO_I==MSGOLEN)
- DPRINTK(debug_msgo, DEBUG_LEAD "messages all sent (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN);
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
while(MSGO_I<MSGOLEN) {
- DPRINTK(debug_msgo, DEBUG_LEAD "message byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO(MSGO_I), MSGO_I, MSGOLEN);
-
- if(TESTLO(SSTAT0, SPIORDY)) {
- DPRINTK(debug_msgo, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
if (MSGO_I==MSGOLEN-1) {
/* Leave MESSAGE OUT after transfer */
@@ -2036,13 +1898,13 @@ static void msgo_run(struct Scsi_Host *shpnt)
if (MSGO(MSGO_I) & IDENTIFY_BASE)
- CURRENT_SC->SCp.phase |= identified;
+ acp->phase |= identified;
if (MSGO(MSGO_I)==ABORT)
- CURRENT_SC->SCp.phase |= aborted;
+ acp->phase |= aborted;
if (MSGO(MSGO_I)==BUS_DEVICE_RESET)
- CURRENT_SC->SCp.phase |= resetted;
+ acp->phase |= resetted;
SETPORT(SCSIDAT, MSGO(MSGO_I++));
}
@@ -2051,36 +1913,33 @@ static void msgo_run(struct Scsi_Host *shpnt)
static void msgo_end(struct Scsi_Host *shpnt)
{
if(MSGO_I<MSGOLEN) {
- printk(ERR_LEAD "message sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "message sent incompletely (%d/%d)\n",
+ MSGO_I, MSGOLEN);
if(SYNCNEG==1) {
- printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "Synchronous Data Transfer Request was rejected\n");
SYNCNEG=2;
}
}
-
+
MSGO_I = 0;
MSGOLEN = 0;
}
-/*
+/*
* command phase
*
*/
static void cmd_init(struct Scsi_Host *shpnt)
{
- if (CURRENT_SC->SCp.sent_command) {
- printk(ERR_LEAD "command already sent\n", CMDINFO(CURRENT_SC));
- done(shpnt, DID_ERROR << 16);
+ if (aha152x_priv(CURRENT_SC)->sent_command) {
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "command already sent\n");
+ done(shpnt, SAM_STAT_GOOD, DID_ERROR);
return;
}
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_cmd) {
- printk(DEBUG_LEAD "cmd_init: ", CMDINFO(CURRENT_SC));
- __scsi_print_command(CURRENT_SC->cmnd);
- }
-#endif
-
CMD_I=0;
}
@@ -2090,18 +1949,9 @@ static void cmd_init(struct Scsi_Host *shpnt)
*/
static void cmd_run(struct Scsi_Host *shpnt)
{
- if(CMD_I==CURRENT_SC->cmd_len) {
- DPRINTK(debug_cmd, DEBUG_LEAD "command already completely sent (%d/%d)", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len);
- disp_ports(shpnt);
- }
-
while(CMD_I<CURRENT_SC->cmd_len) {
- DPRINTK(debug_cmd, DEBUG_LEAD "command byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), CURRENT_SC->cmnd[CMD_I], CMD_I, CURRENT_SC->cmd_len);
-
- if(TESTLO(SSTAT0, SPIORDY)) {
- DPRINTK(debug_cmd, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
SETPORT(SCSIDAT, CURRENT_SC->cmnd[CMD_I++]);
}
@@ -2110,9 +1960,11 @@ static void cmd_run(struct Scsi_Host *shpnt)
static void cmd_end(struct Scsi_Host *shpnt)
{
if(CMD_I<CURRENT_SC->cmd_len)
- printk(ERR_LEAD "command sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "command sent incompletely (%d/%d)\n",
+ CMD_I, CURRENT_SC->cmd_len);
else
- CURRENT_SC->SCp.sent_command++;
+ aha152x_priv(CURRENT_SC)->sent_command++;
}
/*
@@ -2121,20 +1973,11 @@ static void cmd_end(struct Scsi_Host *shpnt)
*/
static void status_run(struct Scsi_Host *shpnt)
{
- if(TESTLO(SSTAT0,SPIORDY)) {
- DPRINTK(debug_status, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
- CURRENT_SC->SCp.Status = GETPORT(SCSIDAT);
+ aha152x_priv(CURRENT_SC)->status = GETPORT(SCSIDAT);
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_status) {
- printk(DEBUG_LEAD "inbound status %02x ", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.Status);
- scsi_print_status(CURRENT_SC->SCp.Status);
- printk("\n");
- }
-#endif
}
/*
@@ -2153,14 +1996,11 @@ static void datai_init(struct Scsi_Host *shpnt)
SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE);
DATA_LEN=0;
- DPRINTK(debug_datai,
- DEBUG_LEAD "datai_init: request_bufflen=%d resid=%d\n",
- CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC));
}
static void datai_run(struct Scsi_Host *shpnt)
{
+ struct aha152x_cmd_priv *acp;
unsigned long the_time;
int fifodata, data_count;
@@ -2178,8 +2018,7 @@ static void datai_run(struct Scsi_Host *shpnt)
barrier();
if(TESTLO(DMASTAT, DFIFOFULL|INTSTAT)) {
- printk(ERR_LEAD "datai timeout", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC, "datai timeout\n");
break;
}
@@ -2191,58 +2030,57 @@ static void datai_run(struct Scsi_Host *shpnt)
barrier();
if(TESTLO(SSTAT2, SEMPTY)) {
- printk(ERR_LEAD "datai sempty timeout", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "datai sempty timeout");
break;
}
fifodata = GETPORT(FIFOSTAT);
}
- if(CURRENT_SC->SCp.this_residual>0) {
- while(fifodata>0 && CURRENT_SC->SCp.this_residual>0) {
- data_count = fifodata>CURRENT_SC->SCp.this_residual ?
- CURRENT_SC->SCp.this_residual :
- fifodata;
+ acp = aha152x_priv(CURRENT_SC);
+ if (acp->this_residual > 0) {
+ while (fifodata > 0 && acp->this_residual > 0) {
+ data_count = fifodata > acp->this_residual ?
+ acp->this_residual : fifodata;
fifodata -= data_count;
- if(data_count & 1) {
- DPRINTK(debug_datai, DEBUG_LEAD "8bit\n", CMDINFO(CURRENT_SC));
- SETPORT(DMACNTRL0, ENDMA|_8BIT);
- *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT);
- CURRENT_SC->SCp.this_residual--;
- DATA_LEN++;
- SETPORT(DMACNTRL0, ENDMA);
- }
-
- if(data_count > 1) {
- DPRINTK(debug_datai, DEBUG_LEAD "16bit(%d)\n", CMDINFO(CURRENT_SC), data_count);
- data_count >>= 1;
- insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
- CURRENT_SC->SCp.ptr += 2 * data_count;
- CURRENT_SC->SCp.this_residual -= 2 * data_count;
- DATA_LEN += 2 * data_count;
- }
-
- if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) {
- /* advance to next buffer */
- CURRENT_SC->SCp.buffers_residual--;
- CURRENT_SC->SCp.buffer++;
- CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer);
- CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
- }
- }
- } else if(fifodata>0) {
- printk(ERR_LEAD "no buffers left for %d(%d) bytes (data overrun!?)\n", CMDINFO(CURRENT_SC), fifodata, GETPORT(FIFOSTAT));
- SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ if (data_count & 1) {
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ *acp->ptr++ = GETPORT(DATAPORT);
+ acp->this_residual--;
+ DATA_LEN++;
+ SETPORT(DMACNTRL0, ENDMA);
+ }
+
+ if (data_count > 1) {
+ data_count >>= 1;
+ insw(DATAPORT, acp->ptr, data_count);
+ acp->ptr += 2 * data_count;
+ acp->this_residual -= 2 * data_count;
+ DATA_LEN += 2 * data_count;
+ }
+
+ if (acp->this_residual == 0 &&
+ !sg_is_last(acp->buffer)) {
+ /* advance to next buffer */
+ acp->buffer = sg_next(acp->buffer);
+ acp->ptr = SG_ADDRESS(acp->buffer);
+ acp->this_residual = acp->buffer->length;
+ }
+ }
+ } else if (fifodata > 0) {
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "no buffers left for %d(%d) bytes"
+ " (data overrun!?)\n",
+ fifodata, GETPORT(FIFOSTAT));
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
while(fifodata>0) {
- int data;
- data=GETPORT(DATAPORT);
- DPRINTK(debug_datai, DEBUG_LEAD "data=%02x\n", CMDINFO(CURRENT_SC), data);
+ GETPORT(DATAPORT);
fifodata--;
DATA_LEN++;
}
- SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
}
}
@@ -2250,19 +2088,20 @@ static void datai_run(struct Scsi_Host *shpnt)
TESTLO(DMASTAT, DFIFOEMP) ||
TESTLO(SSTAT2, SEMPTY) ||
GETPORT(FIFOSTAT)>0) {
- /*
+ /*
* something went wrong, if there's something left in the fifos
* or the phase didn't change
*/
- printk(ERR_LEAD "fifos should be empty and phase should have changed\n", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "fifos should be empty and phase should have changed\n");
}
if(DATA_LEN!=GETSTCNT()) {
- printk(ERR_LEAD
- "manual transfer count differs from automatic (count=%d;stcnt=%d;diff=%d;fifostat=%d)",
- CMDINFO(CURRENT_SC), DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN, GETPORT(FIFOSTAT));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "manual transfer count differs from automatic "
+ "(count=%d;stcnt=%d;diff=%d;fifostat=%d)",
+ DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN,
+ GETPORT(FIFOSTAT));
mdelay(10000);
}
}
@@ -2271,11 +2110,6 @@ static void datai_end(struct Scsi_Host *shpnt)
{
CMD_INC_RESID(CURRENT_SC, -GETSTCNT());
- DPRINTK(debug_datai,
- DEBUG_LEAD "datai_end: request_bufflen=%d resid=%d stcnt=%d\n",
- CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC), GETSTCNT());
-
SETPORT(SXFRCTL0, CH1|CLRSTCNT);
SETPORT(DMACNTRL0, 0);
}
@@ -2296,52 +2130,48 @@ static void datao_init(struct Scsi_Host *shpnt)
SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE );
DATA_LEN = scsi_get_resid(CURRENT_SC);
-
- DPRINTK(debug_datao,
- DEBUG_LEAD "datao_init: request_bufflen=%d; resid=%d\n",
- CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC));
}
static void datao_run(struct Scsi_Host *shpnt)
{
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
unsigned long the_time;
int data_count;
/* until phase changes or all data sent */
- while(TESTLO(DMASTAT, INTSTAT) && CURRENT_SC->SCp.this_residual>0) {
+ while (TESTLO(DMASTAT, INTSTAT) && acp->this_residual > 0) {
data_count = 128;
- if(data_count > CURRENT_SC->SCp.this_residual)
- data_count=CURRENT_SC->SCp.this_residual;
+ if (data_count > acp->this_residual)
+ data_count = acp->this_residual;
if(TESTLO(DMASTAT, DFIFOEMP)) {
- printk(ERR_LEAD "datao fifo not empty (%d)", CMDINFO(CURRENT_SC), GETPORT(FIFOSTAT));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "datao fifo not empty (%d)",
+ GETPORT(FIFOSTAT));
break;
}
if(data_count & 1) {
SETPORT(DMACNTRL0,WRITE_READ|ENDMA|_8BIT);
- SETPORT(DATAPORT, *CURRENT_SC->SCp.ptr++);
- CURRENT_SC->SCp.this_residual--;
+ SETPORT(DATAPORT, *acp->ptr++);
+ acp->this_residual--;
CMD_INC_RESID(CURRENT_SC, -1);
SETPORT(DMACNTRL0,WRITE_READ|ENDMA);
}
if(data_count > 1) {
data_count >>= 1;
- outsw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
- CURRENT_SC->SCp.ptr += 2 * data_count;
- CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ outsw(DATAPORT, acp->ptr, data_count);
+ acp->ptr += 2 * data_count;
+ acp->this_residual -= 2 * data_count;
CMD_INC_RESID(CURRENT_SC, -2 * data_count);
- }
+ }
- if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) {
+ if (acp->this_residual == 0 && !sg_is_last(acp->buffer)) {
/* advance to next buffer */
- CURRENT_SC->SCp.buffers_residual--;
- CURRENT_SC->SCp.buffer++;
- CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer);
- CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
+ acp->buffer = sg_next(acp->buffer);
+ acp->ptr = SG_ADDRESS(acp->buffer);
+ acp->this_residual = acp->buffer->length;
}
the_time=jiffies + 100*HZ;
@@ -2349,8 +2179,7 @@ static void datao_run(struct Scsi_Host *shpnt)
barrier();
if(TESTLO(DMASTAT, DFIFOEMP|INTSTAT)) {
- printk(ERR_LEAD "dataout timeout", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC, "dataout timeout\n");
break;
}
}
@@ -2358,36 +2187,29 @@ static void datao_run(struct Scsi_Host *shpnt)
static void datao_end(struct Scsi_Host *shpnt)
{
+ struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
+
if(TESTLO(DMASTAT, DFIFOEMP)) {
- int data_count = (DATA_LEN - scsi_get_resid(CURRENT_SC)) -
- GETSTCNT();
-
- DPRINTK(debug_datao, DEBUG_LEAD "datao: %d bytes to resend (%d written, %d transferred)\n",
- CMDINFO(CURRENT_SC),
- data_count,
- DATA_LEN - scsi_get_resid(CURRENT_SC),
- GETSTCNT());
-
- CMD_INC_RESID(CURRENT_SC, data_count);
-
- data_count -= CURRENT_SC->SCp.ptr -
- SG_ADDRESS(CURRENT_SC->SCp.buffer);
- while(data_count>0) {
- CURRENT_SC->SCp.buffer--;
- CURRENT_SC->SCp.buffers_residual++;
- data_count -= CURRENT_SC->SCp.buffer->length;
+ u32 datao_cnt = GETSTCNT();
+ int datao_out = DATA_LEN - scsi_get_resid(CURRENT_SC);
+ int done;
+ struct scatterlist *sg = scsi_sglist(CURRENT_SC);
+
+ CMD_INC_RESID(CURRENT_SC, datao_out - datao_cnt);
+
+ done = scsi_bufflen(CURRENT_SC) - scsi_get_resid(CURRENT_SC);
+ /* Locate the first SG entry not yet sent */
+ while (done > 0 && !sg_is_last(sg)) {
+ if (done < sg->length)
+ break;
+ done -= sg->length;
+ sg = sg_next(sg);
}
- CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer) -
- data_count;
- CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length +
- data_count;
- }
- DPRINTK(debug_datao, DEBUG_LEAD "datao_end: request_bufflen=%d; resid=%d; stcnt=%d\n",
- CMDINFO(CURRENT_SC),
- scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC),
- GETSTCNT());
+ acp->buffer = sg;
+ acp->ptr = SG_ADDRESS(acp->buffer) + done;
+ acp->this_residual = acp->buffer->length - done;
+ }
SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
SETPORT(SXFRCTL0, CH1);
@@ -2412,9 +2234,10 @@ static int update_state(struct Scsi_Host *shpnt)
STATE=rsti;
SETPORT(SCSISEQ,0);
SETPORT(SSTAT1,SCSIRSTI);
- } else if(stat0 & SELDI && PREVSTATE==busfree) {
+ } else if (stat0 & SELDI && PREVSTATE == busfree) {
STATE=seldi;
- } else if(stat0 & SELDO && CURRENT_SC && (CURRENT_SC->SCp.phase & selecting)) {
+ } else if (stat0 & SELDO && CURRENT_SC &&
+ (aha152x_priv(CURRENT_SC)->phase & selecting)) {
STATE=seldo;
} else if(stat1 & SELTO) {
STATE=selto;
@@ -2437,8 +2260,7 @@ static int update_state(struct Scsi_Host *shpnt)
}
if((stat0 & SELDI) && STATE!=seldi && !dataphase) {
- printk(INFO_LEAD "reselection missed?", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_INFO, CURRENT_SC, "reselection missed?");
}
if(STATE!=PREVSTATE) {
@@ -2456,8 +2278,8 @@ static int update_state(struct Scsi_Host *shpnt)
*/
static void parerr_run(struct Scsi_Host *shpnt)
{
- printk(ERR_LEAD "parity error\n", CMDINFO(CURRENT_SC));
- done(shpnt, DID_PARITY << 16);
+ scmd_printk(KERN_ERR, CURRENT_SC, "parity error\n");
+ done(shpnt, SAM_STAT_GOOD, DID_PARITY);
}
/*
@@ -2466,13 +2288,13 @@ static void parerr_run(struct Scsi_Host *shpnt)
*/
static void rsti_run(struct Scsi_Host *shpnt)
{
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
+
+ shost_printk(KERN_NOTICE, shpnt, "scsi reset in\n");
- printk(KERN_NOTICE "aha152x%d: scsi reset in\n", HOSTNO);
-
ptr=DISCONNECTED_SC;
while(ptr) {
- Scsi_Cmnd *next = SCNEXT(ptr);
+ struct scsi_cmnd *next = SCNEXT(ptr);
if (!ptr->device->soft_reset) {
remove_SC(&DISCONNECTED_SC, ptr);
@@ -2480,15 +2302,15 @@ static void rsti_run(struct Scsi_Host *shpnt)
kfree(ptr->host_scribble);
ptr->host_scribble=NULL;
- ptr->result = DID_RESET << 16;
- ptr->scsi_done(ptr);
+ set_host_byte(ptr, DID_RESET);
+ aha152x_scsi_done(ptr);
}
ptr = next;
}
if(CURRENT_SC && !CURRENT_SC->device->soft_reset)
- done(shpnt, DID_RESET << 16 );
+ done(shpnt, SAM_STAT_GOOD, DID_RESET);
}
@@ -2531,8 +2353,6 @@ static void is_complete(struct Scsi_Host *shpnt)
dataphase=update_state(shpnt);
- DPRINTK(debug_phases, LEAD "start %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name);
-
/*
* end previous state
*
@@ -2549,7 +2369,7 @@ static void is_complete(struct Scsi_Host *shpnt)
SETPORT(SXFRCTL0, CH1);
SETPORT(DMACNTRL0, 0);
if(CURRENT_SC)
- CURRENT_SC->SCp.phase &= ~spiordy;
+ aha152x_priv(CURRENT_SC)->phase &= ~spiordy;
}
/*
@@ -2559,9 +2379,9 @@ static void is_complete(struct Scsi_Host *shpnt)
if(dataphase) {
SETPORT(SSTAT0, REQINIT);
SETPORT(SCSISIG, GETPORT(SCSISIG) & P_MASK);
- SETPORT(SSTAT1, PHASECHG);
+ SETPORT(SSTAT1, PHASECHG);
}
-
+
/*
* enable SPIO mode if previous didn't use it
* and this one does
@@ -2571,16 +2391,16 @@ static void is_complete(struct Scsi_Host *shpnt)
SETPORT(DMACNTRL0, 0);
SETPORT(SXFRCTL0, CH1|SPIOEN);
if(CURRENT_SC)
- CURRENT_SC->SCp.phase |= spiordy;
+ aha152x_priv(CURRENT_SC)->phase |= spiordy;
}
-
+
/*
* initialize for new state
*
*/
if(PREVSTATE!=STATE && states[STATE].init)
states[STATE].init(shpnt);
-
+
/*
* handle current state
*
@@ -2588,8 +2408,9 @@ static void is_complete(struct Scsi_Host *shpnt)
if(states[STATE].run)
states[STATE].run(shpnt);
else
- printk(ERR_LEAD "unexpected state (%x)\n", CMDINFO(CURRENT_SC), STATE);
-
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "unexpected state (%x)\n", STATE);
+
/*
* setup controller to interrupt on
* the next expected condition and
@@ -2605,7 +2426,6 @@ static void is_complete(struct Scsi_Host *shpnt)
HOSTDATA(shpnt)->time[STATE] += jiffies-start;
#endif
- DPRINTK(debug_phases, LEAD "end %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name);
} while(pending);
/*
@@ -2618,326 +2438,66 @@ static void is_complete(struct Scsi_Host *shpnt)
}
-/*
+/*
* Dump the current driver status and panic
*/
static void aha152x_error(struct Scsi_Host *shpnt, char *msg)
{
- printk(KERN_EMERG "\naha152x%d: %s\n", HOSTNO, msg);
+ shost_printk(KERN_EMERG, shpnt, "%s\n", msg);
show_queues(shpnt);
panic("aha152x panic\n");
}
/*
- * Display registers of AIC-6260
- */
-static void disp_ports(struct Scsi_Host *shpnt)
-{
-#if defined(AHA152X_DEBUG)
- int s;
-
- printk("\n%s: %s(%s) ",
- CURRENT_SC ? "busy" : "waiting",
- states[STATE].name,
- states[PREVSTATE].name);
-
- s = GETPORT(SCSISEQ);
- printk("SCSISEQ( ");
- if (s & TEMODEO)
- printk("TARGET MODE ");
- if (s & ENSELO)
- printk("SELO ");
- if (s & ENSELI)
- printk("SELI ");
- if (s & ENRESELI)
- printk("RESELI ");
- if (s & ENAUTOATNO)
- printk("AUTOATNO ");
- if (s & ENAUTOATNI)
- printk("AUTOATNI ");
- if (s & ENAUTOATNP)
- printk("AUTOATNP ");
- if (s & SCSIRSTO)
- printk("SCSIRSTO ");
- printk(");");
-
- printk(" SCSISIG(");
- s = GETPORT(SCSISIG);
- switch (s & P_MASK) {
- case P_DATAO:
- printk("DATA OUT");
- break;
- case P_DATAI:
- printk("DATA IN");
- break;
- case P_CMD:
- printk("COMMAND");
- break;
- case P_STATUS:
- printk("STATUS");
- break;
- case P_MSGO:
- printk("MESSAGE OUT");
- break;
- case P_MSGI:
- printk("MESSAGE IN");
- break;
- default:
- printk("*invalid*");
- break;
- }
-
- printk("); ");
-
- printk("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
-
- printk("SSTAT( ");
- s = GETPORT(SSTAT0);
- if (s & TARGET)
- printk("TARGET ");
- if (s & SELDO)
- printk("SELDO ");
- if (s & SELDI)
- printk("SELDI ");
- if (s & SELINGO)
- printk("SELINGO ");
- if (s & SWRAP)
- printk("SWRAP ");
- if (s & SDONE)
- printk("SDONE ");
- if (s & SPIORDY)
- printk("SPIORDY ");
- if (s & DMADONE)
- printk("DMADONE ");
-
- s = GETPORT(SSTAT1);
- if (s & SELTO)
- printk("SELTO ");
- if (s & ATNTARG)
- printk("ATNTARG ");
- if (s & SCSIRSTI)
- printk("SCSIRSTI ");
- if (s & PHASEMIS)
- printk("PHASEMIS ");
- if (s & BUSFREE)
- printk("BUSFREE ");
- if (s & SCSIPERR)
- printk("SCSIPERR ");
- if (s & PHASECHG)
- printk("PHASECHG ");
- if (s & REQINIT)
- printk("REQINIT ");
- printk("); ");
-
-
- printk("SSTAT( ");
-
- s = GETPORT(SSTAT0) & GETPORT(SIMODE0);
-
- if (s & TARGET)
- printk("TARGET ");
- if (s & SELDO)
- printk("SELDO ");
- if (s & SELDI)
- printk("SELDI ");
- if (s & SELINGO)
- printk("SELINGO ");
- if (s & SWRAP)
- printk("SWRAP ");
- if (s & SDONE)
- printk("SDONE ");
- if (s & SPIORDY)
- printk("SPIORDY ");
- if (s & DMADONE)
- printk("DMADONE ");
-
- s = GETPORT(SSTAT1) & GETPORT(SIMODE1);
-
- if (s & SELTO)
- printk("SELTO ");
- if (s & ATNTARG)
- printk("ATNTARG ");
- if (s & SCSIRSTI)
- printk("SCSIRSTI ");
- if (s & PHASEMIS)
- printk("PHASEMIS ");
- if (s & BUSFREE)
- printk("BUSFREE ");
- if (s & SCSIPERR)
- printk("SCSIPERR ");
- if (s & PHASECHG)
- printk("PHASECHG ");
- if (s & REQINIT)
- printk("REQINIT ");
- printk("); ");
-
- printk("SXFRCTL0( ");
-
- s = GETPORT(SXFRCTL0);
- if (s & SCSIEN)
- printk("SCSIEN ");
- if (s & DMAEN)
- printk("DMAEN ");
- if (s & CH1)
- printk("CH1 ");
- if (s & CLRSTCNT)
- printk("CLRSTCNT ");
- if (s & SPIOEN)
- printk("SPIOEN ");
- if (s & CLRCH1)
- printk("CLRCH1 ");
- printk("); ");
-
- printk("SIGNAL( ");
-
- s = GETPORT(SCSISIG);
- if (s & SIG_ATNI)
- printk("ATNI ");
- if (s & SIG_SELI)
- printk("SELI ");
- if (s & SIG_BSYI)
- printk("BSYI ");
- if (s & SIG_REQI)
- printk("REQI ");
- if (s & SIG_ACKI)
- printk("ACKI ");
- printk("); ");
-
- printk("SELID (%02x), ", GETPORT(SELID));
-
- printk("STCNT (%d), ", GETSTCNT());
-
- printk("SSTAT2( ");
-
- s = GETPORT(SSTAT2);
- if (s & SOFFSET)
- printk("SOFFSET ");
- if (s & SEMPTY)
- printk("SEMPTY ");
- if (s & SFULL)
- printk("SFULL ");
- printk("); SFCNT (%d); ", s & (SFULL | SFCNT));
-
- s = GETPORT(SSTAT3);
- printk("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);
-
- printk("SSTAT4( ");
- s = GETPORT(SSTAT4);
- if (s & SYNCERR)
- printk("SYNCERR ");
- if (s & FWERR)
- printk("FWERR ");
- if (s & FRERR)
- printk("FRERR ");
- printk("); ");
-
- printk("DMACNTRL0( ");
- s = GETPORT(DMACNTRL0);
- printk("%s ", s & _8BIT ? "8BIT" : "16BIT");
- printk("%s ", s & DMA ? "DMA" : "PIO");
- printk("%s ", s & WRITE_READ ? "WRITE" : "READ");
- if (s & ENDMA)
- printk("ENDMA ");
- if (s & INTEN)
- printk("INTEN ");
- if (s & RSTFIFO)
- printk("RSTFIFO ");
- if (s & SWINT)
- printk("SWINT ");
- printk("); ");
-
- printk("DMASTAT( ");
- s = GETPORT(DMASTAT);
- if (s & ATDONE)
- printk("ATDONE ");
- if (s & WORDRDY)
- printk("WORDRDY ");
- if (s & DFIFOFULL)
- printk("DFIFOFULL ");
- if (s & DFIFOEMP)
- printk("DFIFOEMP ");
- printk(")\n");
-#endif
-}
-
-/*
* display enabled interrupts
*/
static void disp_enintr(struct Scsi_Host *shpnt)
{
- int s;
-
- printk(KERN_DEBUG "enabled interrupts ( ");
-
- s = GETPORT(SIMODE0);
- if (s & ENSELDO)
- printk("ENSELDO ");
- if (s & ENSELDI)
- printk("ENSELDI ");
- if (s & ENSELINGO)
- printk("ENSELINGO ");
- if (s & ENSWRAP)
- printk("ENSWRAP ");
- if (s & ENSDONE)
- printk("ENSDONE ");
- if (s & ENSPIORDY)
- printk("ENSPIORDY ");
- if (s & ENDMADONE)
- printk("ENDMADONE ");
-
- s = GETPORT(SIMODE1);
- if (s & ENSELTIMO)
- printk("ENSELTIMO ");
- if (s & ENATNTARG)
- printk("ENATNTARG ");
- if (s & ENPHASEMIS)
- printk("ENPHASEMIS ");
- if (s & ENBUSFREE)
- printk("ENBUSFREE ");
- if (s & ENSCSIPERR)
- printk("ENSCSIPERR ");
- if (s & ENPHASECHG)
- printk("ENPHASECHG ");
- if (s & ENREQINIT)
- printk("ENREQINIT ");
- printk(")\n");
+ int s0, s1;
+
+ s0 = GETPORT(SIMODE0);
+ s1 = GETPORT(SIMODE1);
+
+ shost_printk(KERN_DEBUG, shpnt,
+ "enabled interrupts (%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n",
+ (s0 & ENSELDO) ? "ENSELDO " : "",
+ (s0 & ENSELDI) ? "ENSELDI " : "",
+ (s0 & ENSELINGO) ? "ENSELINGO " : "",
+ (s0 & ENSWRAP) ? "ENSWRAP " : "",
+ (s0 & ENSDONE) ? "ENSDONE " : "",
+ (s0 & ENSPIORDY) ? "ENSPIORDY " : "",
+ (s0 & ENDMADONE) ? "ENDMADONE " : "",
+ (s1 & ENSELTIMO) ? "ENSELTIMO " : "",
+ (s1 & ENATNTARG) ? "ENATNTARG " : "",
+ (s1 & ENPHASEMIS) ? "ENPHASEMIS " : "",
+ (s1 & ENBUSFREE) ? "ENBUSFREE " : "",
+ (s1 & ENSCSIPERR) ? "ENSCSIPERR " : "",
+ (s1 & ENPHASECHG) ? "ENPHASECHG " : "",
+ (s1 & ENREQINIT) ? "ENREQINIT " : "");
}
/*
* Show the command data of a command
*/
-static void show_command(Scsi_Cmnd *ptr)
-{
- scmd_printk(KERN_DEBUG, ptr, "%p: cmnd=(", ptr);
-
- __scsi_print_command(ptr->cmnd);
-
- printk(KERN_DEBUG "); request_bufflen=%d; resid=%d; phase |",
- scsi_bufflen(ptr), scsi_get_resid(ptr));
-
- if (ptr->SCp.phase & not_issued)
- printk("not issued|");
- if (ptr->SCp.phase & selecting)
- printk("selecting|");
- if (ptr->SCp.phase & identified)
- printk("identified|");
- if (ptr->SCp.phase & disconnected)
- printk("disconnected|");
- if (ptr->SCp.phase & completed)
- printk("completed|");
- if (ptr->SCp.phase & spiordy)
- printk("spiordy|");
- if (ptr->SCp.phase & syncneg)
- printk("syncneg|");
- if (ptr->SCp.phase & aborted)
- printk("aborted|");
- if (ptr->SCp.phase & resetted)
- printk("resetted|");
- if( SCDATA(ptr) ) {
- printk("; next=0x%p\n", SCNEXT(ptr));
- } else {
- printk("; next=(host scribble NULL)\n");
- }
+static void show_command(struct scsi_cmnd *ptr)
+{
+ const int phase = aha152x_priv(ptr)->phase;
+
+ scsi_print_command(ptr);
+ scmd_printk(KERN_DEBUG, ptr,
+ "request_bufflen=%d; resid=%d; "
+ "phase |%s%s%s%s%s%s%s%s%s; next=0x%p",
+ scsi_bufflen(ptr), scsi_get_resid(ptr),
+ phase & not_issued ? "not issued|" : "",
+ phase & selecting ? "selecting|" : "",
+ phase & identified ? "identified|" : "",
+ phase & disconnected ? "disconnected|" : "",
+ phase & completed ? "completed|" : "",
+ phase & spiordy ? "spiordy|" : "",
+ phase & syncneg ? "syncneg|" : "",
+ phase & aborted ? "aborted|" : "",
+ phase & resetted ? "resetted|" : "",
+ SCDATA(ptr) ? SCNEXT(ptr) : NULL);
}
/*
@@ -2945,7 +2505,7 @@ static void show_command(Scsi_Cmnd *ptr)
*/
static void show_queues(struct Scsi_Host *shpnt)
{
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
unsigned long flags;
DO_LOCK(flags);
@@ -2964,325 +2524,308 @@ static void show_queues(struct Scsi_Host *shpnt)
for (ptr = DISCONNECTED_SC; ptr; ptr = SCDATA(ptr) ? SCNEXT(ptr) : NULL)
show_command(ptr);
- disp_ports(shpnt);
disp_enintr(shpnt);
}
-#undef SPRINTF
-#define SPRINTF(args...) pos += sprintf(pos, ## args)
-
-static int get_command(char *pos, Scsi_Cmnd * ptr)
+static void get_command(struct seq_file *m, struct scsi_cmnd * ptr)
{
- char *start = pos;
+ struct aha152x_cmd_priv *acp = aha152x_priv(ptr);
+ const int phase = acp->phase;
int i;
- SPRINTF("0x%08x: target=%d; lun=%d; cmnd=( ",
- (unsigned int) ptr, ptr->device->id, ptr->device->lun);
+ seq_printf(m, "%p: target=%d; lun=%d; cmnd=( ",
+ ptr, ptr->device->id, (u8)ptr->device->lun);
for (i = 0; i < COMMAND_SIZE(ptr->cmnd[0]); i++)
- SPRINTF("0x%02x ", ptr->cmnd[i]);
-
- SPRINTF("); resid=%d; residual=%d; buffers=%d; phase |",
- scsi_get_resid(ptr), ptr->SCp.this_residual,
- ptr->SCp.buffers_residual);
-
- if (ptr->SCp.phase & not_issued)
- SPRINTF("not issued|");
- if (ptr->SCp.phase & selecting)
- SPRINTF("selecting|");
- if (ptr->SCp.phase & disconnected)
- SPRINTF("disconnected|");
- if (ptr->SCp.phase & aborted)
- SPRINTF("aborted|");
- if (ptr->SCp.phase & identified)
- SPRINTF("identified|");
- if (ptr->SCp.phase & completed)
- SPRINTF("completed|");
- if (ptr->SCp.phase & spiordy)
- SPRINTF("spiordy|");
- if (ptr->SCp.phase & syncneg)
- SPRINTF("syncneg|");
- SPRINTF("; next=0x%p\n", SCNEXT(ptr));
-
- return (pos - start);
+ seq_printf(m, "0x%02x ", ptr->cmnd[i]);
+
+ seq_printf(m, "); resid=%d; residual=%d; buffers=%d; phase |",
+ scsi_get_resid(ptr), acp->this_residual,
+ sg_nents(acp->buffer) - 1);
+
+ if (phase & not_issued)
+ seq_puts(m, "not issued|");
+ if (phase & selecting)
+ seq_puts(m, "selecting|");
+ if (phase & disconnected)
+ seq_puts(m, "disconnected|");
+ if (phase & aborted)
+ seq_puts(m, "aborted|");
+ if (phase & identified)
+ seq_puts(m, "identified|");
+ if (phase & completed)
+ seq_puts(m, "completed|");
+ if (phase & spiordy)
+ seq_puts(m, "spiordy|");
+ if (phase & syncneg)
+ seq_puts(m, "syncneg|");
+ seq_printf(m, "; next=0x%p\n", SCNEXT(ptr));
}
-static int get_ports(struct Scsi_Host *shpnt, char *pos)
+static void get_ports(struct seq_file *m, struct Scsi_Host *shpnt)
{
- char *start = pos;
int s;
- SPRINTF("\n%s: %s(%s) ", CURRENT_SC ? "on bus" : "waiting", states[STATE].name, states[PREVSTATE].name);
+ seq_printf(m, "\n%s: %s(%s) ", CURRENT_SC ? "on bus" : "waiting", states[STATE].name, states[PREVSTATE].name);
s = GETPORT(SCSISEQ);
- SPRINTF("SCSISEQ( ");
+ seq_puts(m, "SCSISEQ( ");
if (s & TEMODEO)
- SPRINTF("TARGET MODE ");
+ seq_puts(m, "TARGET MODE ");
if (s & ENSELO)
- SPRINTF("SELO ");
+ seq_puts(m, "SELO ");
if (s & ENSELI)
- SPRINTF("SELI ");
+ seq_puts(m, "SELI ");
if (s & ENRESELI)
- SPRINTF("RESELI ");
+ seq_puts(m, "RESELI ");
if (s & ENAUTOATNO)
- SPRINTF("AUTOATNO ");
+ seq_puts(m, "AUTOATNO ");
if (s & ENAUTOATNI)
- SPRINTF("AUTOATNI ");
+ seq_puts(m, "AUTOATNI ");
if (s & ENAUTOATNP)
- SPRINTF("AUTOATNP ");
+ seq_puts(m, "AUTOATNP ");
if (s & SCSIRSTO)
- SPRINTF("SCSIRSTO ");
- SPRINTF(");");
+ seq_puts(m, "SCSIRSTO ");
+ seq_puts(m, ");");
- SPRINTF(" SCSISIG(");
+ seq_puts(m, " SCSISIG(");
s = GETPORT(SCSISIG);
switch (s & P_MASK) {
case P_DATAO:
- SPRINTF("DATA OUT");
+ seq_puts(m, "DATA OUT");
break;
case P_DATAI:
- SPRINTF("DATA IN");
+ seq_puts(m, "DATA IN");
break;
case P_CMD:
- SPRINTF("COMMAND");
+ seq_puts(m, "COMMAND");
break;
case P_STATUS:
- SPRINTF("STATUS");
+ seq_puts(m, "STATUS");
break;
case P_MSGO:
- SPRINTF("MESSAGE OUT");
+ seq_puts(m, "MESSAGE OUT");
break;
case P_MSGI:
- SPRINTF("MESSAGE IN");
+ seq_puts(m, "MESSAGE IN");
break;
default:
- SPRINTF("*invalid*");
+ seq_puts(m, "*invalid*");
break;
}
- SPRINTF("); ");
+ seq_puts(m, "); ");
- SPRINTF("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+ seq_printf(m, "INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
- SPRINTF("SSTAT( ");
+ seq_puts(m, "SSTAT( ");
s = GETPORT(SSTAT0);
if (s & TARGET)
- SPRINTF("TARGET ");
+ seq_puts(m, "TARGET ");
if (s & SELDO)
- SPRINTF("SELDO ");
+ seq_puts(m, "SELDO ");
if (s & SELDI)
- SPRINTF("SELDI ");
+ seq_puts(m, "SELDI ");
if (s & SELINGO)
- SPRINTF("SELINGO ");
+ seq_puts(m, "SELINGO ");
if (s & SWRAP)
- SPRINTF("SWRAP ");
+ seq_puts(m, "SWRAP ");
if (s & SDONE)
- SPRINTF("SDONE ");
+ seq_puts(m, "SDONE ");
if (s & SPIORDY)
- SPRINTF("SPIORDY ");
+ seq_puts(m, "SPIORDY ");
if (s & DMADONE)
- SPRINTF("DMADONE ");
+ seq_puts(m, "DMADONE ");
s = GETPORT(SSTAT1);
if (s & SELTO)
- SPRINTF("SELTO ");
+ seq_puts(m, "SELTO ");
if (s & ATNTARG)
- SPRINTF("ATNTARG ");
+ seq_puts(m, "ATNTARG ");
if (s & SCSIRSTI)
- SPRINTF("SCSIRSTI ");
+ seq_puts(m, "SCSIRSTI ");
if (s & PHASEMIS)
- SPRINTF("PHASEMIS ");
+ seq_puts(m, "PHASEMIS ");
if (s & BUSFREE)
- SPRINTF("BUSFREE ");
+ seq_puts(m, "BUSFREE ");
if (s & SCSIPERR)
- SPRINTF("SCSIPERR ");
+ seq_puts(m, "SCSIPERR ");
if (s & PHASECHG)
- SPRINTF("PHASECHG ");
+ seq_puts(m, "PHASECHG ");
if (s & REQINIT)
- SPRINTF("REQINIT ");
- SPRINTF("); ");
+ seq_puts(m, "REQINIT ");
+ seq_puts(m, "); ");
- SPRINTF("SSTAT( ");
+ seq_puts(m, "SSTAT( ");
s = GETPORT(SSTAT0) & GETPORT(SIMODE0);
if (s & TARGET)
- SPRINTF("TARGET ");
+ seq_puts(m, "TARGET ");
if (s & SELDO)
- SPRINTF("SELDO ");
+ seq_puts(m, "SELDO ");
if (s & SELDI)
- SPRINTF("SELDI ");
+ seq_puts(m, "SELDI ");
if (s & SELINGO)
- SPRINTF("SELINGO ");
+ seq_puts(m, "SELINGO ");
if (s & SWRAP)
- SPRINTF("SWRAP ");
+ seq_puts(m, "SWRAP ");
if (s & SDONE)
- SPRINTF("SDONE ");
+ seq_puts(m, "SDONE ");
if (s & SPIORDY)
- SPRINTF("SPIORDY ");
+ seq_puts(m, "SPIORDY ");
if (s & DMADONE)
- SPRINTF("DMADONE ");
+ seq_puts(m, "DMADONE ");
s = GETPORT(SSTAT1) & GETPORT(SIMODE1);
if (s & SELTO)
- SPRINTF("SELTO ");
+ seq_puts(m, "SELTO ");
if (s & ATNTARG)
- SPRINTF("ATNTARG ");
+ seq_puts(m, "ATNTARG ");
if (s & SCSIRSTI)
- SPRINTF("SCSIRSTI ");
+ seq_puts(m, "SCSIRSTI ");
if (s & PHASEMIS)
- SPRINTF("PHASEMIS ");
+ seq_puts(m, "PHASEMIS ");
if (s & BUSFREE)
- SPRINTF("BUSFREE ");
+ seq_puts(m, "BUSFREE ");
if (s & SCSIPERR)
- SPRINTF("SCSIPERR ");
+ seq_puts(m, "SCSIPERR ");
if (s & PHASECHG)
- SPRINTF("PHASECHG ");
+ seq_puts(m, "PHASECHG ");
if (s & REQINIT)
- SPRINTF("REQINIT ");
- SPRINTF("); ");
+ seq_puts(m, "REQINIT ");
+ seq_puts(m, "); ");
- SPRINTF("SXFRCTL0( ");
+ seq_puts(m, "SXFRCTL0( ");
s = GETPORT(SXFRCTL0);
if (s & SCSIEN)
- SPRINTF("SCSIEN ");
+ seq_puts(m, "SCSIEN ");
if (s & DMAEN)
- SPRINTF("DMAEN ");
+ seq_puts(m, "DMAEN ");
if (s & CH1)
- SPRINTF("CH1 ");
+ seq_puts(m, "CH1 ");
if (s & CLRSTCNT)
- SPRINTF("CLRSTCNT ");
+ seq_puts(m, "CLRSTCNT ");
if (s & SPIOEN)
- SPRINTF("SPIOEN ");
+ seq_puts(m, "SPIOEN ");
if (s & CLRCH1)
- SPRINTF("CLRCH1 ");
- SPRINTF("); ");
+ seq_puts(m, "CLRCH1 ");
+ seq_puts(m, "); ");
- SPRINTF("SIGNAL( ");
+ seq_puts(m, "SIGNAL( ");
s = GETPORT(SCSISIG);
if (s & SIG_ATNI)
- SPRINTF("ATNI ");
+ seq_puts(m, "ATNI ");
if (s & SIG_SELI)
- SPRINTF("SELI ");
+ seq_puts(m, "SELI ");
if (s & SIG_BSYI)
- SPRINTF("BSYI ");
+ seq_puts(m, "BSYI ");
if (s & SIG_REQI)
- SPRINTF("REQI ");
+ seq_puts(m, "REQI ");
if (s & SIG_ACKI)
- SPRINTF("ACKI ");
- SPRINTF("); ");
+ seq_puts(m, "ACKI ");
+ seq_puts(m, "); ");
- SPRINTF("SELID(%02x), ", GETPORT(SELID));
+ seq_printf(m, "SELID(%02x), ", GETPORT(SELID));
- SPRINTF("STCNT(%d), ", GETSTCNT());
+ seq_printf(m, "STCNT(%d), ", GETSTCNT());
- SPRINTF("SSTAT2( ");
+ seq_puts(m, "SSTAT2( ");
s = GETPORT(SSTAT2);
if (s & SOFFSET)
- SPRINTF("SOFFSET ");
+ seq_puts(m, "SOFFSET ");
if (s & SEMPTY)
- SPRINTF("SEMPTY ");
+ seq_puts(m, "SEMPTY ");
if (s & SFULL)
- SPRINTF("SFULL ");
- SPRINTF("); SFCNT (%d); ", s & (SFULL | SFCNT));
+ seq_puts(m, "SFULL ");
+ seq_printf(m, "); SFCNT (%d); ", s & (SFULL | SFCNT));
s = GETPORT(SSTAT3);
- SPRINTF("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);
+ seq_printf(m, "SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);
- SPRINTF("SSTAT4( ");
+ seq_puts(m, "SSTAT4( ");
s = GETPORT(SSTAT4);
if (s & SYNCERR)
- SPRINTF("SYNCERR ");
+ seq_puts(m, "SYNCERR ");
if (s & FWERR)
- SPRINTF("FWERR ");
+ seq_puts(m, "FWERR ");
if (s & FRERR)
- SPRINTF("FRERR ");
- SPRINTF("); ");
+ seq_puts(m, "FRERR ");
+ seq_puts(m, "); ");
- SPRINTF("DMACNTRL0( ");
+ seq_puts(m, "DMACNTRL0( ");
s = GETPORT(DMACNTRL0);
- SPRINTF("%s ", s & _8BIT ? "8BIT" : "16BIT");
- SPRINTF("%s ", s & DMA ? "DMA" : "PIO");
- SPRINTF("%s ", s & WRITE_READ ? "WRITE" : "READ");
+ seq_printf(m, "%s ", s & _8BIT ? "8BIT" : "16BIT");
+ seq_printf(m, "%s ", s & DMA ? "DMA" : "PIO");
+ seq_printf(m, "%s ", s & WRITE_READ ? "WRITE" : "READ");
if (s & ENDMA)
- SPRINTF("ENDMA ");
+ seq_puts(m, "ENDMA ");
if (s & INTEN)
- SPRINTF("INTEN ");
+ seq_puts(m, "INTEN ");
if (s & RSTFIFO)
- SPRINTF("RSTFIFO ");
+ seq_puts(m, "RSTFIFO ");
if (s & SWINT)
- SPRINTF("SWINT ");
- SPRINTF("); ");
+ seq_puts(m, "SWINT ");
+ seq_puts(m, "); ");
- SPRINTF("DMASTAT( ");
+ seq_puts(m, "DMASTAT( ");
s = GETPORT(DMASTAT);
if (s & ATDONE)
- SPRINTF("ATDONE ");
+ seq_puts(m, "ATDONE ");
if (s & WORDRDY)
- SPRINTF("WORDRDY ");
+ seq_puts(m, "WORDRDY ");
if (s & DFIFOFULL)
- SPRINTF("DFIFOFULL ");
+ seq_puts(m, "DFIFOFULL ");
if (s & DFIFOEMP)
- SPRINTF("DFIFOEMP ");
- SPRINTF(")\n");
+ seq_puts(m, "DFIFOEMP ");
+ seq_puts(m, ")\n");
- SPRINTF("enabled interrupts( ");
+ seq_puts(m, "enabled interrupts( ");
s = GETPORT(SIMODE0);
if (s & ENSELDO)
- SPRINTF("ENSELDO ");
+ seq_puts(m, "ENSELDO ");
if (s & ENSELDI)
- SPRINTF("ENSELDI ");
+ seq_puts(m, "ENSELDI ");
if (s & ENSELINGO)
- SPRINTF("ENSELINGO ");
+ seq_puts(m, "ENSELINGO ");
if (s & ENSWRAP)
- SPRINTF("ENSWRAP ");
+ seq_puts(m, "ENSWRAP ");
if (s & ENSDONE)
- SPRINTF("ENSDONE ");
+ seq_puts(m, "ENSDONE ");
if (s & ENSPIORDY)
- SPRINTF("ENSPIORDY ");
+ seq_puts(m, "ENSPIORDY ");
if (s & ENDMADONE)
- SPRINTF("ENDMADONE ");
+ seq_puts(m, "ENDMADONE ");
s = GETPORT(SIMODE1);
if (s & ENSELTIMO)
- SPRINTF("ENSELTIMO ");
+ seq_puts(m, "ENSELTIMO ");
if (s & ENATNTARG)
- SPRINTF("ENATNTARG ");
+ seq_puts(m, "ENATNTARG ");
if (s & ENPHASEMIS)
- SPRINTF("ENPHASEMIS ");
+ seq_puts(m, "ENPHASEMIS ");
if (s & ENBUSFREE)
- SPRINTF("ENBUSFREE ");
+ seq_puts(m, "ENBUSFREE ");
if (s & ENSCSIPERR)
- SPRINTF("ENSCSIPERR ");
+ seq_puts(m, "ENSCSIPERR ");
if (s & ENPHASECHG)
- SPRINTF("ENPHASECHG ");
+ seq_puts(m, "ENPHASECHG ");
if (s & ENREQINIT)
- SPRINTF("ENREQINIT ");
- SPRINTF(")\n");
-
- return (pos - start);
+ seq_puts(m, "ENREQINIT ");
+ seq_puts(m, ")\n");
}
-static int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+static int aha152x_set_info(struct Scsi_Host *shpnt, char *buffer, int length)
{
if(!shpnt || !buffer || length<8 || strncmp("aha152x ", buffer, 8)!=0)
return -EINVAL;
-#if defined(AHA152X_DEBUG)
- if(length>14 && strncmp("debug ", buffer+8, 6)==0) {
- int debug = HOSTDATA(shpnt)->debug;
-
- HOSTDATA(shpnt)->debug = simple_strtoul(buffer+14, NULL, 0);
-
- printk(KERN_INFO "aha152x%d: debugging options set to 0x%04x (were 0x%04x)\n", HOSTNO, HOSTDATA(shpnt)->debug, debug);
- } else
-#endif
#if defined(AHA152X_STAT)
if(length>13 && strncmp("reset", buffer+8, 5)==0) {
int i;
@@ -3300,7 +2843,7 @@ static int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
HOSTDATA(shpnt)->time[i]=0;
}
- printk(KERN_INFO "aha152x%d: stats reseted.\n", HOSTNO);
+ shost_printk(KERN_INFO, shpnt, "aha152x: stats reset.\n");
} else
#endif
@@ -3312,102 +2855,64 @@ static int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
return length;
}
-#undef SPRINTF
-#define SPRINTF(args...) \
- do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
-
-static int aha152x_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start,
- off_t offset, int length, int inout)
+static int aha152x_show_info(struct seq_file *m, struct Scsi_Host *shpnt)
{
int i;
- char *pos = buffer;
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
unsigned long flags;
- int thislength;
-
- DPRINTK(debug_procinfo,
- KERN_DEBUG "aha152x_proc_info: buffer=%p offset=%ld length=%d hostno=%d inout=%d\n",
- buffer, offset, length, shpnt->host_no, inout);
-
-
- if (inout)
- return aha152x_set_info(buffer, length, shpnt);
- SPRINTF(AHA152X_REVID "\n");
+ seq_puts(m, AHA152X_REVID "\n");
- SPRINTF("ioports 0x%04lx to 0x%04lx\n",
+ seq_printf(m, "ioports 0x%04lx to 0x%04lx\n",
shpnt->io_port, shpnt->io_port + shpnt->n_io_port - 1);
- SPRINTF("interrupt 0x%02x\n", shpnt->irq);
- SPRINTF("disconnection/reconnection %s\n",
+ seq_printf(m, "interrupt 0x%02x\n", shpnt->irq);
+ seq_printf(m, "disconnection/reconnection %s\n",
RECONNECT ? "enabled" : "disabled");
- SPRINTF("parity checking %s\n",
+ seq_printf(m, "parity checking %s\n",
PARITY ? "enabled" : "disabled");
- SPRINTF("synchronous transfers %s\n",
+ seq_printf(m, "synchronous transfers %s\n",
SYNCHRONOUS ? "enabled" : "disabled");
- SPRINTF("%d commands currently queued\n", HOSTDATA(shpnt)->commands);
+ seq_printf(m, "%d commands currently queued\n", HOSTDATA(shpnt)->commands);
if(SYNCHRONOUS) {
- SPRINTF("synchronously operating targets (tick=50 ns):\n");
+ seq_puts(m, "synchronously operating targets (tick=50 ns):\n");
for (i = 0; i < 8; i++)
if (HOSTDATA(shpnt)->syncrate[i] & 0x7f)
- SPRINTF("target %d: period %dT/%dns; req/ack offset %d\n",
+ seq_printf(m, "target %d: period %dT/%dns; req/ack offset %d\n",
i,
(((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2),
(((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2) * 50,
HOSTDATA(shpnt)->syncrate[i] & 0x0f);
}
-#if defined(AHA152X_DEBUG)
-#define PDEBUG(flags,txt) \
- if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt);
-
- SPRINTF("enabled debugging options: ");
-
- PDEBUG(debug_procinfo, "procinfo");
- PDEBUG(debug_queue, "queue");
- PDEBUG(debug_intr, "interrupt");
- PDEBUG(debug_selection, "selection");
- PDEBUG(debug_msgo, "message out");
- PDEBUG(debug_msgi, "message in");
- PDEBUG(debug_status, "status");
- PDEBUG(debug_cmd, "command");
- PDEBUG(debug_datai, "data in");
- PDEBUG(debug_datao, "data out");
- PDEBUG(debug_eh, "eh");
- PDEBUG(debug_locking, "locks");
- PDEBUG(debug_phases, "phases");
-
- SPRINTF("\n");
-#endif
-
- SPRINTF("\nqueue status:\n");
+ seq_puts(m, "\nqueue status:\n");
DO_LOCK(flags);
if (ISSUE_SC) {
- SPRINTF("not yet issued commands:\n");
+ seq_puts(m, "not yet issued commands:\n");
for (ptr = ISSUE_SC; ptr; ptr = SCNEXT(ptr))
- pos += get_command(pos, ptr);
+ get_command(m, ptr);
} else
- SPRINTF("no not yet issued commands\n");
+ seq_puts(m, "no not yet issued commands\n");
DO_UNLOCK(flags);
if (CURRENT_SC) {
- SPRINTF("current command:\n");
- pos += get_command(pos, CURRENT_SC);
+ seq_puts(m, "current command:\n");
+ get_command(m, CURRENT_SC);
} else
- SPRINTF("no current command\n");
+ seq_puts(m, "no current command\n");
if (DISCONNECTED_SC) {
- SPRINTF("disconnected commands:\n");
+ seq_puts(m, "disconnected commands:\n");
for (ptr = DISCONNECTED_SC; ptr; ptr = SCNEXT(ptr))
- pos += get_command(pos, ptr);
+ get_command(m, ptr);
} else
- SPRINTF("no disconnected commands\n");
+ seq_puts(m, "no disconnected commands\n");
- pos += get_ports(shpnt, pos);
+ get_ports(m, shpnt);
#if defined(AHA152X_STAT)
- SPRINTF("statistics:\n"
- "total commands: %d\n"
- "disconnections: %d\n"
+ seq_printf(m, "statistics:\n"
+ "total commands: %d\n"
+ "disconnections: %d\n"
"busfree with check condition: %d\n"
"busfree without old command: %d\n"
"busfree without new command: %d\n"
@@ -3425,59 +2930,35 @@ static int aha152x_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start
HOSTDATA(shpnt)->busfree_without_done_command,
HOSTDATA(shpnt)->busfree_without_any_action);
for(i=0; i<maxstate; i++) {
- SPRINTF("%-10s %-12d %-12d %-12ld\n",
- states[i].name,
+ seq_printf(m, "%-10s %-12d %-12d %-12ld\n",
+ states[i].name,
HOSTDATA(shpnt)->count_trans[i],
HOSTDATA(shpnt)->count[i],
HOSTDATA(shpnt)->time[i]);
}
#endif
-
- DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: pos=%p\n", pos);
-
- thislength = pos - (buffer + offset);
- DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: length=%d thislength=%d\n", length, thislength);
-
- if(thislength<0) {
- DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: output too short\n");
- *start = NULL;
- return 0;
- }
-
- thislength = thislength<length ? thislength : length;
-
- DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: return %d\n", thislength);
-
- *start = buffer + offset;
- return thislength < length ? thislength : length;
-}
-
-static int aha152x_adjust_queue(struct scsi_device *device)
-{
- blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH);
return 0;
}
-static struct scsi_host_template aha152x_driver_template = {
+static const struct scsi_host_template aha152x_driver_template = {
.module = THIS_MODULE,
.name = AHA152X_REVID,
.proc_name = "aha152x",
- .proc_info = aha152x_proc_info,
+ .show_info = aha152x_show_info,
+ .write_info = aha152x_set_info,
.queuecommand = aha152x_queue,
.eh_abort_handler = aha152x_abort,
.eh_device_reset_handler = aha152x_device_reset,
.eh_bus_reset_handler = aha152x_bus_reset,
- .eh_host_reset_handler = aha152x_host_reset,
.bios_param = aha152x_biosparam,
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
- .slave_alloc = aha152x_adjust_queue,
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct aha152x_cmd_priv),
};
-#if !defined(PCMCIA)
+#if !defined(AHA152X_PCMCIA)
static int setup_count;
static struct aha152x_setup setup[2];
@@ -3700,25 +3181,19 @@ static int __init aha152x_init(void)
setup[setup_count].synchronous = aha152x[5];
setup[setup_count].delay = aha152x[6];
setup[setup_count].ext_trans = aha152x[7];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = aha152x[8];
-#endif
- } else if(io[0]!=0 || irq[0]!=0) {
+ } else if (io[0] != 0 || irq[0] != 0) {
if(io[0]!=0) setup[setup_count].io_port = io[0];
if(irq[0]!=0) setup[setup_count].irq = irq[0];
- setup[setup_count].scsiid = scsiid[0];
- setup[setup_count].reconnect = reconnect[0];
- setup[setup_count].parity = parity[0];
- setup[setup_count].synchronous = sync[0];
- setup[setup_count].delay = delay[0];
- setup[setup_count].ext_trans = exttrans[0];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = debug[0];
-#endif
+ setup[setup_count].scsiid = scsiid[0];
+ setup[setup_count].reconnect = reconnect[0];
+ setup[setup_count].parity = parity[0];
+ setup[setup_count].synchronous = sync[0];
+ setup[setup_count].delay = delay[0];
+ setup[setup_count].ext_trans = exttrans[0];
}
- if (checksetup(&setup[setup_count]))
+ if (checksetup(&setup[setup_count]))
setup_count++;
else
printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%d\n",
@@ -3743,22 +3218,16 @@ static int __init aha152x_init(void)
setup[setup_count].synchronous = aha152x1[5];
setup[setup_count].delay = aha152x1[6];
setup[setup_count].ext_trans = aha152x1[7];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = aha152x1[8];
-#endif
- } else if(io[1]!=0 || irq[1]!=0) {
+ } else if (io[1] != 0 || irq[1] != 0) {
if(io[1]!=0) setup[setup_count].io_port = io[1];
if(irq[1]!=0) setup[setup_count].irq = irq[1];
- setup[setup_count].scsiid = scsiid[1];
- setup[setup_count].reconnect = reconnect[1];
- setup[setup_count].parity = parity[1];
- setup[setup_count].synchronous = sync[1];
- setup[setup_count].delay = delay[1];
- setup[setup_count].ext_trans = exttrans[1];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = debug[1];
-#endif
+ setup[setup_count].scsiid = scsiid[1];
+ setup[setup_count].reconnect = reconnect[1];
+ setup[setup_count].parity = parity[1];
+ setup[setup_count].synchronous = sync[1];
+ setup[setup_count].delay = delay[1];
+ setup[setup_count].ext_trans = exttrans[1];
}
if (checksetup(&setup[setup_count]))
setup_count++;
@@ -3805,9 +3274,6 @@ static int __init aha152x_init(void)
setup[setup_count].synchronous = 1;
setup[setup_count].delay = DELAY_DEFAULT;
setup[setup_count].ext_trans = 0;
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = DEBUG_DEFAULT;
-#endif
#if defined(__ISAPNP__)
pnpdev[setup_count] = dev;
#endif
@@ -3876,9 +3342,6 @@ static int __init aha152x_init(void)
setup[setup_count].synchronous = conf.cf_syncneg;
setup[setup_count].delay = DELAY_DEFAULT;
setup[setup_count].ext_trans = 0;
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = DEBUG_DEFAULT;
-#endif
setup_count++;
}
@@ -3932,11 +3395,8 @@ module_exit(aha152x_exit);
#if !defined(MODULE)
static int __init aha152x_setup(char *str)
{
-#if defined(AHA152X_DEBUG)
- int ints[11];
-#else
int ints[10];
-#endif
+
get_options(str, ARRAY_SIZE(ints), ints);
if(setup_count>=ARRAY_SIZE(setup)) {
@@ -3953,24 +3413,15 @@ static int __init aha152x_setup(char *str)
setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 1;
setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
setup[setup_count].ext_trans = ints[0] >= 8 ? ints[8] : 0;
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = ints[0] >= 9 ? ints[9] : DEBUG_DEFAULT;
- if (ints[0] > 9) {
- printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
- "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>[,<DEBUG>]]]]]]]]\n");
-#else
- if (ints[0] > 8) { /*}*/
+ if (ints[0] > 8)
printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
"[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n");
-#endif
- } else {
+ else
setup_count++;
- return 0;
- }
return 1;
}
__setup("aha152x=", aha152x_setup);
#endif
-#endif /* !PCMCIA */
+#endif /* !AHA152X_PCMCIA */
diff --git a/drivers/scsi/aha152x.h b/drivers/scsi/aha152x.h
index ac4bfa438bf2..efd01877d02b 100644
--- a/drivers/scsi/aha152x.h
+++ b/drivers/scsi/aha152x.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _AHA152X_H
#define _AHA152X_H
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index 195823a51aab..371e8300f029 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -1,28 +1,10 @@
-/* $Id: aha1542.c,v 1.1 1992/07/24 06:27:38 root Exp root $
- * linux/kernel/aha1542.c
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Adaptec AHA-1542 SCSI host adapters
*
* Copyright (C) 1992 Tommy Thorn
* Copyright (C) 1993, 1994, 1995 Eric Youngdale
- *
- * Modified by Eric Youngdale
- * Use request_irq and request_dma to help prevent unexpected conflicts
- * Set up on-board DMA controller, such that we do not have to
- * have the bios enabled to use the aha1542.
- * Modified by David Gentzel
- * Don't call request_dma if dma mask is 0 (for BusLogic BT-445S VL-Bus
- * controller).
- * Modified by Matti Aarnio
- * Accept parameters from LILO cmd-line. -- 1-Oct-94
- * Modified by Mike McLagan <mike.mclagan@linux.org>
- * Recognise extended mode on AHA1542CP, different bit than 1542CF
- * 1-Jan-97
- * Modified by Bjorn L. Thordarson and Einar Thor Einarsson
- * Recognize that DMA0 is valid DMA channel -- 13-Jul-98
- * Modified by Chris Faulhaber <jedgar@fxp.org>
- * Added module command-line options
- * 19-Jul-99
- * Modified by Adam Fritzler
- * Added proper detection of the AHA-1640 (MCA version of AHA-1540)
+ * Copyright (C) 2015 Ondrej Zary
*/
#include <linux/module.h>
@@ -30,99 +12,44 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
-#include <linux/ioport.h>
#include <linux/delay.h>
-#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/spinlock.h>
-#include <linux/isapnp.h>
-#include <linux/blkdev.h>
-#include <linux/mca.h>
-#include <linux/mca-legacy.h>
+#include <linux/isa.h>
+#include <linux/pnp.h>
#include <linux/slab.h>
-
+#include <linux/io.h>
#include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/io.h>
-
-#include "scsi.h"
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include "aha1542.h"
-#define SCSI_BUF_PA(address) isa_virt_to_bus(address)
-#define SCSI_SG_PA(sgent) (isa_page_to_bus(sg_page((sgent))) + (sgent)->offset)
-
-#include<linux/stat.h>
-
-#ifdef DEBUG
-#define DEB(x) x
-#else
-#define DEB(x)
-#endif
-
-/*
- static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha1542.c,v 1.1 1992/07/24 06:27:38 root Exp root $";
- */
-
-/* The adaptec can be configured for quite a number of addresses, but
- I generally do not want the card poking around at random. We allow
- two addresses - this allows people to use the Adaptec with a Midi
- card, which also used 0x330 -- can be overridden with LILO! */
-
-#define MAXBOARDS 4 /* Increase this and the sizes of the
- arrays below, if you need more.. */
+#define MAXBOARDS 4
-/* Boards 3,4 slots are reserved for ISAPnP/MCA scans */
-
-static unsigned int bases[MAXBOARDS] __initdata = {0x330, 0x334, 0, 0};
-
-/* set by aha1542_setup according to the command line; they also may
- be marked __initdata, but require zero initializers then */
-
-static int setup_called[MAXBOARDS];
-static int setup_buson[MAXBOARDS];
-static int setup_busoff[MAXBOARDS];
-static int setup_dmaspeed[MAXBOARDS] __initdata = { -1, -1, -1, -1 };
-
-/*
- * LILO/Module params: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]
- *
- * Where: <PORTBASE> is any of the valid AHA addresses:
- * 0x130, 0x134, 0x230, 0x234, 0x330, 0x334
- * <BUSON> is the time (in microsecs) that AHA spends on the AT-bus
- * when transferring data. 1542A power-on default is 11us,
- * valid values are in range: 2..15 (decimal)
- * <BUSOFF> is the time that AHA spends OFF THE BUS after while
- * it is transferring data (not to monopolize the bus).
- * Power-on default is 4us, valid range: 1..64 microseconds.
- * <DMASPEED> Default is jumper selected (1542A: on the J1),
- * but experimenter can alter it with this.
- * Valid values: 5, 6, 7, 8, 10 (MB/s)
- * Factory default is 5 MB/s.
- */
-
-#if defined(MODULE)
-static int isapnp = 0;
-static int aha1542[] = {0x330, 11, 4, -1};
-module_param_array(aha1542, int, NULL, 0);
+static bool isapnp = 1;
module_param(isapnp, bool, 0);
+MODULE_PARM_DESC(isapnp, "enable PnP support (default=1)");
-static struct isapnp_device_id id_table[] __initdata = {
- {
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1542),
- 0
- },
- {0}
-};
+static int io[MAXBOARDS] = { 0x330, 0x334, 0, 0 };
+module_param_hw_array(io, int, ioport, NULL, 0);
+MODULE_PARM_DESC(io, "base IO address of controller (0x130,0x134,0x230,0x234,0x330,0x334, default=0x330,0x334)");
-MODULE_DEVICE_TABLE(isapnp, id_table);
+/* time AHA spends on the AT-bus during data transfer */
+static int bus_on[MAXBOARDS] = { -1, -1, -1, -1 }; /* power-on default: 11us */
+module_param_array(bus_on, int, NULL, 0);
+MODULE_PARM_DESC(bus_on, "bus on time [us] (2-15, default=-1 [HW default: 11])");
-#else
-static int isapnp = 1;
-#endif
+/* time AHA spends off the bus (not to monopolize it) during data transfer */
+static int bus_off[MAXBOARDS] = { -1, -1, -1, -1 }; /* power-on default: 4us */
+module_param_array(bus_off, int, NULL, 0);
+MODULE_PARM_DESC(bus_off, "bus off time [us] (1-64, default=-1 [HW default: 4])");
+
+/* default is jumper selected (J1 on 1542A), factory default = 5 MB/s */
+static int dma_speed[MAXBOARDS] = { -1, -1, -1, -1 };
+module_param_array(dma_speed, int, NULL, 0);
+MODULE_PARM_DESC(dma_speed, "DMA speed [MB/s] (5,6,7,8,10, default=-1 [by jumper])");
-#define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */
#define BIOS_TRANSLATION_6432 1 /* Default case these days */
#define BIOS_TRANSLATION_25563 2 /* Big disk case */
@@ -131,134 +58,83 @@ struct aha1542_hostdata {
int bios_translation; /* Mapping bios uses - for compatibility */
int aha1542_last_mbi_used;
int aha1542_last_mbo_used;
- Scsi_Cmnd *SCint[AHA1542_MAILBOXES];
- struct mailbox mb[2 * AHA1542_MAILBOXES];
- struct ccb ccb[AHA1542_MAILBOXES];
+ struct scsi_cmnd *int_cmds[AHA1542_MAILBOXES];
+ struct mailbox *mb;
+ dma_addr_t mb_handle;
+ struct ccb *ccb;
+ dma_addr_t ccb_handle;
};
-#define HOSTDATA(host) ((struct aha1542_hostdata *) &host->hostdata)
-
-static DEFINE_SPINLOCK(aha1542_lock);
-
+#define AHA1542_MAX_SECTORS 16
+struct aha1542_cmd {
+ /* bounce buffer */
+ void *data_buffer;
+ dma_addr_t data_buffer_handle;
+};
-#define WAITnexttimeout 3000000
-
-static void setup_mailboxes(int base_io, struct Scsi_Host *shpnt);
-static int aha1542_restart(struct Scsi_Host *shost);
-static void aha1542_intr_handle(struct Scsi_Host *shost);
+static inline void aha1542_intr_reset(u16 base)
+{
+ outb(IRST, CONTROL(base));
+}
-#define aha1542_intr_reset(base) outb(IRST, CONTROL(base))
+static inline bool wait_mask(u16 port, u8 mask, u8 allof, u8 noneof, int timeout)
+{
+ bool delayed = true;
-#define WAIT(port, mask, allof, noneof) \
- { register int WAITbits; \
- register int WAITtimeout = WAITnexttimeout; \
- while (1) { \
- WAITbits = inb(port) & (mask); \
- if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
- break; \
- if (--WAITtimeout == 0) goto fail; \
- } \
- }
+ if (timeout == 0) {
+ timeout = 3000000;
+ delayed = false;
+ }
-/* Similar to WAIT, except we use the udelay call to regulate the
- amount of time we wait. */
-#define WAITd(port, mask, allof, noneof, timeout) \
- { register int WAITbits; \
- register int WAITtimeout = timeout; \
- while (1) { \
- WAITbits = inb(port) & (mask); \
- if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
- break; \
- mdelay(1); \
- if (--WAITtimeout == 0) goto fail; \
- } \
- }
+ while (1) {
+ u8 bits = inb(port) & mask;
+ if ((bits & allof) == allof && ((bits & noneof) == 0))
+ break;
+ if (delayed)
+ mdelay(1);
+ if (--timeout == 0)
+ return false;
+ }
-static void aha1542_stat(void)
-{
-/* int s = inb(STATUS), i = inb(INTRFLAGS);
- printk("status=%x intrflags=%x\n", s, i, WAITnexttimeout-WAITtimeout); */
+ return true;
}
-/* This is a bit complicated, but we need to make sure that an interrupt
- routine does not send something out while we are in the middle of this.
- Fortunately, it is only at boot time that multi-byte messages
- are ever sent. */
-static int aha1542_out(unsigned int base, unchar * cmdp, int len)
+static int aha1542_outb(unsigned int base, u8 val)
{
- unsigned long flags = 0;
- int got_lock;
-
- if (len == 1) {
- got_lock = 0;
- while (1 == 1) {
- WAIT(STATUS(base), CDF, 0, CDF);
- spin_lock_irqsave(&aha1542_lock, flags);
- if (inb(STATUS(base)) & CDF) {
- spin_unlock_irqrestore(&aha1542_lock, flags);
- continue;
- }
- outb(*cmdp, DATA(base));
- spin_unlock_irqrestore(&aha1542_lock, flags);
- return 0;
- }
- } else {
- spin_lock_irqsave(&aha1542_lock, flags);
- got_lock = 1;
- while (len--) {
- WAIT(STATUS(base), CDF, 0, CDF);
- outb(*cmdp++, DATA(base));
- }
- spin_unlock_irqrestore(&aha1542_lock, flags);
- }
+ if (!wait_mask(STATUS(base), CDF, 0, CDF, 0))
+ return 1;
+ outb(val, DATA(base));
+
return 0;
-fail:
- if (got_lock)
- spin_unlock_irqrestore(&aha1542_lock, flags);
- printk(KERN_ERR "aha1542_out failed(%d): ", len + 1);
- aha1542_stat();
- return 1;
}
-/* Only used at boot time, so we do not need to worry about latency as much
- here */
-
-static int __init aha1542_in(unsigned int base, unchar * cmdp, int len)
+static int aha1542_out(unsigned int base, u8 *buf, int len)
{
- unsigned long flags;
-
- spin_lock_irqsave(&aha1542_lock, flags);
while (len--) {
- WAIT(STATUS(base), DF, DF, 0);
- *cmdp++ = inb(DATA(base));
+ if (!wait_mask(STATUS(base), CDF, 0, CDF, 0))
+ return 1;
+ outb(*buf++, DATA(base));
}
- spin_unlock_irqrestore(&aha1542_lock, flags);
+ if (!wait_mask(INTRFLAGS(base), INTRMASK, HACC, 0, 0))
+ return 1;
+
return 0;
-fail:
- spin_unlock_irqrestore(&aha1542_lock, flags);
- printk(KERN_ERR "aha1542_in failed(%d): ", len + 1);
- aha1542_stat();
- return 1;
}
-/* Similar to aha1542_in, except that we wait a very short period of time.
- We use this if we know the board is alive and awake, but we are not sure
- if the board will respond to the command we are about to send or not */
-static int __init aha1542_in1(unsigned int base, unchar * cmdp, int len)
-{
- unsigned long flags;
+/*
+ * Only used at boot time, so we do not need to worry about latency as much
+ * here
+ */
- spin_lock_irqsave(&aha1542_lock, flags);
+static int aha1542_in(unsigned int base, u8 *buf, int len, int timeout)
+{
while (len--) {
- WAITd(STATUS(base), DF, DF, 0, 100);
- *cmdp++ = inb(DATA(base));
+ if (!wait_mask(STATUS(base), DF, DF, 0, timeout))
+ return 1;
+ *buf++ = inb(DATA(base));
}
- spin_unlock_irqrestore(&aha1542_lock, flags);
return 0;
-fail:
- spin_unlock_irqrestore(&aha1542_lock, flags);
- return 1;
}
static int makecode(unsigned hosterr, unsigned scsierr)
@@ -271,43 +147,54 @@ static int makecode(unsigned hosterr, unsigned scsierr)
break;
case 0x11: /* Selection time out-The initiator selection or target
- reselection was not complete within the SCSI Time out period */
+ * reselection was not complete within the SCSI Time out period
+ */
hosterr = DID_TIME_OUT;
break;
case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
- than was allocated by the Data Length field or the sum of the
- Scatter / Gather Data Length fields. */
+ * than was allocated by the Data Length field or the sum of the
+ * Scatter / Gather Data Length fields.
+ */
case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
case 0x15: /* MBO command was not 00, 01 or 02-The first byte of the CB was
- invalid. This usually indicates a software failure. */
+ * invalid. This usually indicates a software failure.
+ */
case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid.
- This usually indicates a software failure. */
+ * This usually indicates a software failure.
+ */
case 0x17: /* Linked CCB does not have the same LUN-A subsequent CCB of a set
- of linked CCB's does not specify the same logical unit number as
- the first. */
+ * of linked CCB's does not specify the same logical unit number as
+ * the first.
+ */
case 0x18: /* Invalid Target Direction received from Host-The direction of a
- Target Mode CCB was invalid. */
+ * Target Mode CCB was invalid.
+ */
case 0x19: /* Duplicate CCB Received in Target Mode-More than once CCB was
- received to service data transfer between the same target LUN
- and initiator SCSI ID in the same direction. */
+ * received to service data transfer between the same target LUN
+ * and initiator SCSI ID in the same direction.
+ */
case 0x1a: /* Invalid CCB or Segment List Parameter-A segment list with a zero
- length segment or invalid segment list boundaries was received.
- A CCB parameter was invalid. */
- DEB(printk("Aha1542: %x %x\n", hosterr, scsierr));
+ * length segment or invalid segment list boundaries was received.
+ * A CCB parameter was invalid.
+ */
+#ifdef DEBUG
+ printk("Aha1542: %x %x\n", hosterr, scsierr);
+#endif
hosterr = DID_ERROR; /* Couldn't find any better */
break;
case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
- phase sequence was requested by the target. The host adapter
- will generate a SCSI Reset Condition, notifying the host with
- a SCRD interrupt */
+ * phase sequence was requested by the target. The host adapter
+ * will generate a SCSI Reset Condition, notifying the host with
+ * a SCRD interrupt
+ */
hosterr = DID_RESET;
break;
default:
@@ -317,106 +204,93 @@ static int makecode(unsigned hosterr, unsigned scsierr)
return scsierr | (hosterr << 16);
}
-static int __init aha1542_test_port(int bse, struct Scsi_Host *shpnt)
+static int aha1542_test_port(struct Scsi_Host *sh)
{
- unchar inquiry_cmd[] = {CMD_INQUIRY};
- unchar inquiry_result[4];
- unchar *cmdp;
- int len;
- volatile int debug = 0;
+ int i;
/* Quick and dirty test for presence of the card. */
- if (inb(STATUS(bse)) == 0xff)
+ if (inb(STATUS(sh->io_port)) == 0xff)
return 0;
/* Reset the adapter. I ought to make a hard reset, but it's not really necessary */
- /* DEB(printk("aha1542_test_port called \n")); */
-
/* In case some other card was probing here, reset interrupts */
- aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
+ aha1542_intr_reset(sh->io_port); /* reset interrupts, so they don't block */
- outb(SRST | IRST /*|SCRST */ , CONTROL(bse));
+ outb(SRST | IRST /*|SCRST */ , CONTROL(sh->io_port));
mdelay(20); /* Wait a little bit for things to settle down. */
- debug = 1;
/* Expect INIT and IDLE, any of the others are bad */
- WAIT(STATUS(bse), STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+ if (!wait_mask(STATUS(sh->io_port), STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF, 0))
+ return 0;
- debug = 2;
/* Shouldn't have generated any interrupts during reset */
- if (inb(INTRFLAGS(bse)) & INTRMASK)
- goto fail;
-
-
- /* Perform a host adapter inquiry instead so we do not need to set
- up the mailboxes ahead of time */
+ if (inb(INTRFLAGS(sh->io_port)) & INTRMASK)
+ return 0;
- aha1542_out(bse, inquiry_cmd, 1);
+ /*
+ * Perform a host adapter inquiry instead so we do not need to set
+ * up the mailboxes ahead of time
+ */
- debug = 3;
- len = 4;
- cmdp = &inquiry_result[0];
+ aha1542_outb(sh->io_port, CMD_INQUIRY);
- while (len--) {
- WAIT(STATUS(bse), DF, DF, 0);
- *cmdp++ = inb(DATA(bse));
+ for (i = 0; i < 4; i++) {
+ if (!wait_mask(STATUS(sh->io_port), DF, DF, 0, 0))
+ return 0;
+ (void)inb(DATA(sh->io_port));
}
- debug = 8;
/* Reading port should reset DF */
- if (inb(STATUS(bse)) & DF)
- goto fail;
+ if (inb(STATUS(sh->io_port)) & DF)
+ return 0;
- debug = 9;
/* When HACC, command is completed, and we're though testing */
- WAIT(INTRFLAGS(bse), HACC, HACC, 0);
- /* now initialize adapter */
+ if (!wait_mask(INTRFLAGS(sh->io_port), HACC, HACC, 0, 0))
+ return 0;
- debug = 10;
/* Clear interrupts */
- outb(IRST, CONTROL(bse));
-
- debug = 11;
+ outb(IRST, CONTROL(sh->io_port));
- return debug; /* 1 = ok */
-fail:
- return 0; /* 0 = not ok */
+ return 1;
}
-/* A quick wrapper for do_aha1542_intr_handle to grab the spin lock */
-static irqreturn_t do_aha1542_intr_handle(int dummy, void *dev_id)
+static void aha1542_free_cmd(struct scsi_cmnd *cmd)
{
- unsigned long flags;
- struct Scsi_Host *shost = dev_id;
+ struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);
+
+ if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ struct request *rq = scsi_cmd_to_rq(cmd);
+ void *buf = acmd->data_buffer;
+ struct req_iterator iter;
+ struct bio_vec bv;
+
+ rq_for_each_segment(bv, rq, iter) {
+ memcpy_to_bvec(&bv, buf);
+ buf += bv.bv_len;
+ }
+ }
- spin_lock_irqsave(shost->host_lock, flags);
- aha1542_intr_handle(shost);
- spin_unlock_irqrestore(shost->host_lock, flags);
- return IRQ_HANDLED;
+ scsi_dma_unmap(cmd);
}
-/* A "high" level interrupt handler */
-static void aha1542_intr_handle(struct Scsi_Host *shost)
+static irqreturn_t aha1542_interrupt(int irq, void *dev_id)
{
- void (*my_done) (Scsi_Cmnd *) = NULL;
+ struct Scsi_Host *sh = dev_id;
+ struct aha1542_hostdata *aha1542 = shost_priv(sh);
int errstatus, mbi, mbo, mbistatus;
int number_serviced;
unsigned long flags;
- Scsi_Cmnd *SCtmp;
+ struct scsi_cmnd *tmp_cmd;
int flag;
- int needs_restart;
- struct mailbox *mb;
- struct ccb *ccb;
-
- mb = HOSTDATA(shost)->mb;
- ccb = HOSTDATA(shost)->ccb;
+ struct mailbox *mb = aha1542->mb;
+ struct ccb *ccb = aha1542->ccb;
#ifdef DEBUG
{
- flag = inb(INTRFLAGS(shost->io_port));
- printk(KERN_DEBUG "aha1542_intr_handle: ");
+ flag = inb(INTRFLAGS(sh->io_port));
+ shost_printk(KERN_DEBUG, sh, "aha1542_intr_handle: ");
if (!(flag & ANYINTR))
printk("no interrupt?");
if (flag & MBIF)
@@ -427,33 +301,32 @@ static void aha1542_intr_handle(struct Scsi_Host *shost)
printk("HACC ");
if (flag & SCRD)
printk("SCRD ");
- printk("status %02x\n", inb(STATUS(shost->io_port)));
- };
+ printk("status %02x\n", inb(STATUS(sh->io_port)));
+ }
#endif
number_serviced = 0;
- needs_restart = 0;
- while (1 == 1) {
- flag = inb(INTRFLAGS(shost->io_port));
+ spin_lock_irqsave(sh->host_lock, flags);
+ while (1) {
+ flag = inb(INTRFLAGS(sh->io_port));
- /* Check for unusual interrupts. If any of these happen, we should
- probably do something special, but for now just printing a message
- is sufficient. A SCSI reset detected is something that we really
- need to deal with in some way. */
+ /*
+ * Check for unusual interrupts. If any of these happen, we should
+ * probably do something special, but for now just printing a message
+ * is sufficient. A SCSI reset detected is something that we really
+ * need to deal with in some way.
+ */
if (flag & ~MBIF) {
if (flag & MBOA)
printk("MBOF ");
if (flag & HACC)
printk("HACC ");
- if (flag & SCRD) {
- needs_restart = 1;
+ if (flag & SCRD)
printk("SCRD ");
- }
}
- aha1542_intr_reset(shost->io_port);
+ aha1542_intr_reset(sh->io_port);
- spin_lock_irqsave(&aha1542_lock, flags);
- mbi = HOSTDATA(shost)->aha1542_last_mbi_used + 1;
+ mbi = aha1542->aha1542_last_mbi_used + 1;
if (mbi >= 2 * AHA1542_MAILBOXES)
mbi = AHA1542_MAILBOXES;
@@ -463,57 +336,51 @@ static void aha1542_intr_handle(struct Scsi_Host *shost)
mbi++;
if (mbi >= 2 * AHA1542_MAILBOXES)
mbi = AHA1542_MAILBOXES;
- } while (mbi != HOSTDATA(shost)->aha1542_last_mbi_used);
+ } while (mbi != aha1542->aha1542_last_mbi_used);
if (mb[mbi].status == 0) {
- spin_unlock_irqrestore(&aha1542_lock, flags);
+ spin_unlock_irqrestore(sh->host_lock, flags);
/* Hmm, no mail. Must have read it the last time around */
- if (!number_serviced && !needs_restart)
- printk(KERN_WARNING "aha1542.c: interrupt received, but no mail.\n");
- /* We detected a reset. Restart all pending commands for
- devices that use the hard reset option */
- if (needs_restart)
- aha1542_restart(shost);
- return;
- };
-
- mbo = (scsi2int(mb[mbi].ccbptr) - (SCSI_BUF_PA(&ccb[0]))) / sizeof(struct ccb);
+ if (!number_serviced)
+ shost_printk(KERN_WARNING, sh, "interrupt received, but no mail.\n");
+ return IRQ_HANDLED;
+ }
+
+ mbo = (scsi2int(mb[mbi].ccbptr) - (unsigned long)aha1542->ccb_handle) / sizeof(struct ccb);
mbistatus = mb[mbi].status;
mb[mbi].status = 0;
- HOSTDATA(shost)->aha1542_last_mbi_used = mbi;
- spin_unlock_irqrestore(&aha1542_lock, flags);
+ aha1542->aha1542_last_mbi_used = mbi;
#ifdef DEBUG
- {
- if (ccb[mbo].tarstat | ccb[mbo].hastat)
- printk(KERN_DEBUG "aha1542_command: returning %x (status %d)\n",
- ccb[mbo].tarstat + ((int) ccb[mbo].hastat << 16), mb[mbi].status);
- };
+ if (ccb[mbo].tarstat | ccb[mbo].hastat)
+ shost_printk(KERN_DEBUG, sh, "aha1542_command: returning %x (status %d)\n",
+ ccb[mbo].tarstat + ((int) ccb[mbo].hastat << 16), mb[mbi].status);
#endif
if (mbistatus == 3)
continue; /* Aborted command not found */
#ifdef DEBUG
- printk(KERN_DEBUG "...done %d %d\n", mbo, mbi);
+ shost_printk(KERN_DEBUG, sh, "...done %d %d\n", mbo, mbi);
#endif
- SCtmp = HOSTDATA(shost)->SCint[mbo];
+ tmp_cmd = aha1542->int_cmds[mbo];
- if (!SCtmp || !SCtmp->scsi_done) {
- printk(KERN_WARNING "aha1542_intr_handle: Unexpected interrupt\n");
- printk(KERN_WARNING "tarstat=%x, hastat=%x idlun=%x ccb#=%d \n", ccb[mbo].tarstat,
+ if (!tmp_cmd) {
+ spin_unlock_irqrestore(sh->host_lock, flags);
+ shost_printk(KERN_WARNING, sh, "Unexpected interrupt\n");
+ shost_printk(KERN_WARNING, sh, "tarstat=%x, hastat=%x idlun=%x ccb#=%d\n", ccb[mbo].tarstat,
ccb[mbo].hastat, ccb[mbo].idlun, mbo);
- return;
+ return IRQ_HANDLED;
}
- my_done = SCtmp->scsi_done;
- kfree(SCtmp->host_scribble);
- SCtmp->host_scribble = NULL;
- /* Fetch the sense data, and tuck it away, in the required slot. The
- Adaptec automatically fetches it, and there is no guarantee that
- we will still have it in the cdb when we come back */
+ aha1542_free_cmd(tmp_cmd);
+ /*
+ * Fetch the sense data, and tuck it away, in the required slot. The
+ * Adaptec automatically fetches it, and there is no guarantee that
+ * we will still have it in the cdb when we come back
+ */
if (ccb[mbo].tarstat == 2)
- memcpy(SCtmp->sense_buffer, &ccb[mbo].cdb[ccb[mbo].cdblen],
+ memcpy(tmp_cmd->sense_buffer, &ccb[mbo].cdb[ccb[mbo].cdblen],
SCSI_SENSE_BUFFERSIZE);
@@ -528,308 +395,236 @@ static void aha1542_intr_handle(struct Scsi_Host *shost)
#ifdef DEBUG
if (errstatus)
- printk(KERN_DEBUG "(aha1542 error:%x %x %x) ", errstatus,
+ shost_printk(KERN_DEBUG, sh, "(aha1542 error:%x %x %x) ", errstatus,
ccb[mbo].hastat, ccb[mbo].tarstat);
+ if (ccb[mbo].tarstat == 2)
+ print_hex_dump_bytes("sense: ", DUMP_PREFIX_NONE, &ccb[mbo].cdb[ccb[mbo].cdblen], 12);
+ if (errstatus)
+ printk("aha1542_intr_handle: returning %6x\n", errstatus);
#endif
-
- if (ccb[mbo].tarstat == 2) {
-#ifdef DEBUG
- int i;
-#endif
- DEB(printk("aha1542_intr_handle: sense:"));
-#ifdef DEBUG
- for (i = 0; i < 12; i++)
- printk("%02x ", ccb[mbo].cdb[ccb[mbo].cdblen + i]);
- printk("\n");
-#endif
- /*
- DEB(printk("aha1542_intr_handle: buf:"));
- for (i = 0; i < bufflen; i++)
- printk("%02x ", ((unchar *)buff)[i]);
- printk("\n");
- */
- }
- DEB(if (errstatus) printk("aha1542_intr_handle: returning %6x\n", errstatus));
- SCtmp->result = errstatus;
- HOSTDATA(shost)->SCint[mbo] = NULL; /* This effectively frees up the mailbox slot, as
- far as queuecommand is concerned */
- my_done(SCtmp);
+ tmp_cmd->result = errstatus;
+ aha1542->int_cmds[mbo] = NULL; /* This effectively frees up the mailbox slot, as
+ * far as queuecommand is concerned
+ */
+ scsi_done(tmp_cmd);
number_serviced++;
- };
+ }
}
-static int aha1542_queuecommand_lck(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+static int aha1542_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
{
- unchar ahacmd = CMD_START_SCSI;
- unchar direction;
- unchar *cmd = (unchar *) SCpnt->cmnd;
- unchar target = SCpnt->device->id;
- unchar lun = SCpnt->device->lun;
+ struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);
+ struct aha1542_hostdata *aha1542 = shost_priv(sh);
+ u8 direction;
+ u8 target = cmd->device->id;
+ u8 lun = cmd->device->lun;
unsigned long flags;
- int bufflen = scsi_bufflen(SCpnt);
+ int bufflen = scsi_bufflen(cmd);
int mbo;
- struct mailbox *mb;
- struct ccb *ccb;
-
- DEB(int i);
+ struct mailbox *mb = aha1542->mb;
+ struct ccb *ccb = aha1542->ccb;
- mb = HOSTDATA(SCpnt->device->host)->mb;
- ccb = HOSTDATA(SCpnt->device->host)->ccb;
-
- DEB(if (target > 1) {
- SCpnt->result = DID_TIME_OUT << 16;
- done(SCpnt); return 0;
- }
- );
-
- if (*cmd == REQUEST_SENSE) {
+ if (*cmd->cmnd == REQUEST_SENSE) {
/* Don't do the command - we have the sense data already */
-#if 0
- /* scsi_request_sense() provides a buffer of size 256,
- so there is no reason to expect equality */
- if (bufflen != SCSI_SENSE_BUFFERSIZE)
- printk(KERN_CRIT "aha1542: Wrong buffer length supplied "
- "for request sense (%d)\n", bufflen);
-#endif
- SCpnt->result = 0;
- done(SCpnt);
+ cmd->result = 0;
+ scsi_done(cmd);
return 0;
}
#ifdef DEBUG
- if (*cmd == READ_10 || *cmd == WRITE_10)
- i = xscsi2int(cmd + 2);
- else if (*cmd == READ_6 || *cmd == WRITE_6)
- i = scsi2int(cmd + 2);
- else
- i = -1;
- if (done)
- printk(KERN_DEBUG "aha1542_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
- else
- printk(KERN_DEBUG "aha1542_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
- aha1542_stat();
- printk(KERN_DEBUG "aha1542_queuecommand: dumping scsi cmd:");
- for (i = 0; i < SCpnt->cmd_len; i++)
- printk("%02x ", cmd[i]);
- printk("\n");
- if (*cmd == WRITE_10 || *cmd == WRITE_6)
- return 0; /* we are still testing, so *don't* write */
+ {
+ int i = -1;
+ if (*cmd->cmnd == READ_10 || *cmd->cmnd == WRITE_10)
+ i = xscsi2int(cmd->cmnd + 2);
+ else if (*cmd->cmnd == READ_6 || *cmd->cmnd == WRITE_6)
+ i = scsi2int(cmd->cmnd + 2);
+ shost_printk(KERN_DEBUG, sh, "aha1542_queuecommand: dev %d cmd %02x pos %d len %d",
+ target, *cmd->cmnd, i, bufflen);
+ print_hex_dump_bytes("command: ", DUMP_PREFIX_NONE, cmd->cmnd, cmd->cmd_len);
+ }
#endif
- /* Use the outgoing mailboxes in a round-robin fashion, because this
- is how the host adapter will scan for them */
- spin_lock_irqsave(&aha1542_lock, flags);
- mbo = HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used + 1;
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ struct request *rq = scsi_cmd_to_rq(cmd);
+ void *buf = acmd->data_buffer;
+ struct req_iterator iter;
+ struct bio_vec bv;
+
+ rq_for_each_segment(bv, rq, iter) {
+ memcpy_from_bvec(buf, &bv);
+ buf += bv.bv_len;
+ }
+ }
+
+ /*
+ * Use the outgoing mailboxes in a round-robin fashion, because this
+ * is how the host adapter will scan for them
+ */
+
+ spin_lock_irqsave(sh->host_lock, flags);
+ mbo = aha1542->aha1542_last_mbo_used + 1;
if (mbo >= AHA1542_MAILBOXES)
mbo = 0;
do {
- if (mb[mbo].status == 0 && HOSTDATA(SCpnt->device->host)->SCint[mbo] == NULL)
+ if (mb[mbo].status == 0 && aha1542->int_cmds[mbo] == NULL)
break;
mbo++;
if (mbo >= AHA1542_MAILBOXES)
mbo = 0;
- } while (mbo != HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used);
+ } while (mbo != aha1542->aha1542_last_mbo_used);
- if (mb[mbo].status || HOSTDATA(SCpnt->device->host)->SCint[mbo])
+ if (mb[mbo].status || aha1542->int_cmds[mbo])
panic("Unable to find empty mailbox for aha1542.\n");
- HOSTDATA(SCpnt->device->host)->SCint[mbo] = SCpnt; /* This will effectively prevent someone else from
- screwing with this cdb. */
+ aha1542->int_cmds[mbo] = cmd; /* This will effectively prevent someone else from
+ * screwing with this cdb.
+ */
- HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used = mbo;
- spin_unlock_irqrestore(&aha1542_lock, flags);
+ aha1542->aha1542_last_mbo_used = mbo;
#ifdef DEBUG
- printk(KERN_DEBUG "Sending command (%d %x)...", mbo, done);
+ shost_printk(KERN_DEBUG, sh, "Sending command (%d)...", mbo);
#endif
- any2scsi(mb[mbo].ccbptr, SCSI_BUF_PA(&ccb[mbo])); /* This gets trashed for some reason */
+ /* This gets trashed for some reason */
+ any2scsi(mb[mbo].ccbptr, aha1542->ccb_handle + mbo * sizeof(*ccb));
memset(&ccb[mbo], 0, sizeof(struct ccb));
- ccb[mbo].cdblen = SCpnt->cmd_len;
+ ccb[mbo].cdblen = cmd->cmd_len;
direction = 0;
- if (*cmd == READ_10 || *cmd == READ_6)
+ if (*cmd->cmnd == READ_10 || *cmd->cmnd == READ_6)
direction = 8;
- else if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ else if (*cmd->cmnd == WRITE_10 || *cmd->cmnd == WRITE_6)
direction = 16;
- memcpy(ccb[mbo].cdb, cmd, ccb[mbo].cdblen);
-
- if (bufflen) {
- struct scatterlist *sg;
- struct chain *cptr;
-#ifdef DEBUG
- unsigned char *ptr;
-#endif
- int i, sg_count = scsi_sg_count(SCpnt);
- ccb[mbo].op = 2; /* SCSI Initiator Command w/scatter-gather */
- SCpnt->host_scribble = kmalloc(sizeof(*cptr)*sg_count,
- GFP_KERNEL | GFP_DMA);
- cptr = (struct chain *) SCpnt->host_scribble;
- if (cptr == NULL) {
- /* free the claimed mailbox slot */
- HOSTDATA(SCpnt->device->host)->SCint[mbo] = NULL;
- return SCSI_MLQUEUE_HOST_BUSY;
- }
- scsi_for_each_sg(SCpnt, sg, sg_count, i) {
- any2scsi(cptr[i].dataptr, SCSI_SG_PA(sg));
- any2scsi(cptr[i].datalen, sg->length);
- };
- any2scsi(ccb[mbo].datalen, sg_count * sizeof(struct chain));
- any2scsi(ccb[mbo].dataptr, SCSI_BUF_PA(cptr));
-#ifdef DEBUG
- printk("cptr %x: ", cptr);
- ptr = (unsigned char *) cptr;
- for (i = 0; i < 18; i++)
- printk("%02x ", ptr[i]);
-#endif
- } else {
- ccb[mbo].op = 0; /* SCSI Initiator Command */
- SCpnt->host_scribble = NULL;
- any2scsi(ccb[mbo].datalen, 0);
+ memcpy(ccb[mbo].cdb, cmd->cmnd, ccb[mbo].cdblen);
+ ccb[mbo].op = 0; /* SCSI Initiator Command */
+ any2scsi(ccb[mbo].datalen, bufflen);
+ if (bufflen)
+ any2scsi(ccb[mbo].dataptr, acmd->data_buffer_handle);
+ else
any2scsi(ccb[mbo].dataptr, 0);
- };
ccb[mbo].idlun = (target & 7) << 5 | direction | (lun & 7); /*SCSI Target Id */
ccb[mbo].rsalen = 16;
ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
ccb[mbo].commlinkid = 0;
#ifdef DEBUG
- {
- int i;
- printk(KERN_DEBUG "aha1542_command: sending.. ");
- for (i = 0; i < sizeof(ccb[mbo]) - 10; i++)
- printk("%02x ", ((unchar *) & ccb[mbo])[i]);
- };
+ print_hex_dump_bytes("sending: ", DUMP_PREFIX_NONE, &ccb[mbo], sizeof(ccb[mbo]) - 10);
+ printk("aha1542_queuecommand: now waiting for interrupt ");
#endif
-
- if (done) {
- DEB(printk("aha1542_queuecommand: now waiting for interrupt ");
- aha1542_stat());
- SCpnt->scsi_done = done;
- mb[mbo].status = 1;
- aha1542_out(SCpnt->device->host->io_port, &ahacmd, 1); /* start scsi command */
- DEB(aha1542_stat());
- } else
- printk("aha1542_queuecommand: done can't be NULL\n");
+ mb[mbo].status = 1;
+ aha1542_outb(cmd->device->host->io_port, CMD_START_SCSI);
+ spin_unlock_irqrestore(sh->host_lock, flags);
return 0;
}
-static DEF_SCSI_QCMD(aha1542_queuecommand)
-
/* Initialize mailboxes */
-static void setup_mailboxes(int bse, struct Scsi_Host *shpnt)
+static void setup_mailboxes(struct Scsi_Host *sh)
{
+ struct aha1542_hostdata *aha1542 = shost_priv(sh);
+ u8 mb_cmd[5] = { CMD_MBINIT, AHA1542_MAILBOXES, 0, 0, 0};
int i;
- struct mailbox *mb;
- struct ccb *ccb;
-
- unchar cmd[5] = { CMD_MBINIT, AHA1542_MAILBOXES, 0, 0, 0};
-
- mb = HOSTDATA(shpnt)->mb;
- ccb = HOSTDATA(shpnt)->ccb;
for (i = 0; i < AHA1542_MAILBOXES; i++) {
- mb[i].status = mb[AHA1542_MAILBOXES + i].status = 0;
- any2scsi(mb[i].ccbptr, SCSI_BUF_PA(&ccb[i]));
- };
- aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
- any2scsi((cmd + 2), SCSI_BUF_PA(mb));
- aha1542_out(bse, cmd, 5);
- WAIT(INTRFLAGS(bse), INTRMASK, HACC, 0);
- while (0) {
-fail:
- printk(KERN_ERR "aha1542_detect: failed setting up mailboxes\n");
+ aha1542->mb[i].status = 0;
+ any2scsi(aha1542->mb[i].ccbptr,
+ aha1542->ccb_handle + i * sizeof(struct ccb));
+ aha1542->mb[AHA1542_MAILBOXES + i].status = 0;
}
- aha1542_intr_reset(bse);
+ aha1542_intr_reset(sh->io_port); /* reset interrupts, so they don't block */
+ any2scsi(mb_cmd + 2, aha1542->mb_handle);
+ if (aha1542_out(sh->io_port, mb_cmd, 5))
+ shost_printk(KERN_ERR, sh, "failed setting up mailboxes\n");
+ aha1542_intr_reset(sh->io_port);
}
-static int __init aha1542_getconfig(int base_io, unsigned char *irq_level, unsigned char *dma_chan, unsigned char *scsi_id)
+static int aha1542_getconfig(struct Scsi_Host *sh)
{
- unchar inquiry_cmd[] = {CMD_RETCONF};
- unchar inquiry_result[3];
+ u8 inquiry_result[3];
int i;
- i = inb(STATUS(base_io));
+ i = inb(STATUS(sh->io_port));
if (i & DF) {
- i = inb(DATA(base_io));
- };
- aha1542_out(base_io, inquiry_cmd, 1);
- aha1542_in(base_io, inquiry_result, 3);
- WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
- while (0) {
-fail:
- printk(KERN_ERR "aha1542_detect: query board settings\n");
+ i = inb(DATA(sh->io_port));
}
- aha1542_intr_reset(base_io);
+ aha1542_outb(sh->io_port, CMD_RETCONF);
+ aha1542_in(sh->io_port, inquiry_result, 3, 0);
+ if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 0))
+ shost_printk(KERN_ERR, sh, "error querying board settings\n");
+ aha1542_intr_reset(sh->io_port);
switch (inquiry_result[0]) {
case 0x80:
- *dma_chan = 7;
+ sh->dma_channel = 7;
break;
case 0x40:
- *dma_chan = 6;
+ sh->dma_channel = 6;
break;
case 0x20:
- *dma_chan = 5;
+ sh->dma_channel = 5;
break;
case 0x01:
- *dma_chan = 0;
+ sh->dma_channel = 0;
break;
case 0:
- /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
- Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
- *dma_chan = 0xFF;
+ /*
+ * This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
+ * Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this.
+ */
+ sh->dma_channel = 0xFF;
break;
default:
- printk(KERN_ERR "Unable to determine Adaptec DMA priority. Disabling board\n");
+ shost_printk(KERN_ERR, sh, "Unable to determine DMA channel.\n");
return -1;
- };
+ }
switch (inquiry_result[1]) {
case 0x40:
- *irq_level = 15;
+ sh->irq = 15;
break;
case 0x20:
- *irq_level = 14;
+ sh->irq = 14;
break;
case 0x8:
- *irq_level = 12;
+ sh->irq = 12;
break;
case 0x4:
- *irq_level = 11;
+ sh->irq = 11;
break;
case 0x2:
- *irq_level = 10;
+ sh->irq = 10;
break;
case 0x1:
- *irq_level = 9;
+ sh->irq = 9;
break;
default:
- printk(KERN_ERR "Unable to determine Adaptec IRQ level. Disabling board\n");
+ shost_printk(KERN_ERR, sh, "Unable to determine IRQ level.\n");
return -1;
- };
- *scsi_id = inquiry_result[2] & 7;
+ }
+ sh->this_id = inquiry_result[2] & 7;
return 0;
}
-/* This function should only be called for 1542C boards - we can detect
- the special firmware settings and unlock the board */
+/*
+ * This function should only be called for 1542C boards - we can detect
+ * the special firmware settings and unlock the board
+ */
-static int __init aha1542_mbenable(int base)
+static int aha1542_mbenable(struct Scsi_Host *sh)
{
- static unchar mbenable_cmd[3];
- static unchar mbenable_result[2];
+ static u8 mbenable_cmd[3];
+ static u8 mbenable_result[2];
int retval;
retval = BIOS_TRANSLATION_6432;
- mbenable_cmd[0] = CMD_EXTBIOS;
- aha1542_out(base, mbenable_cmd, 1);
- if (aha1542_in1(base, mbenable_result, 2))
+ aha1542_outb(sh->io_port, CMD_EXTBIOS);
+ if (aha1542_in(sh->io_port, mbenable_result, 2, 100))
return retval;
- WAITd(INTRFLAGS(base), INTRMASK, HACC, 0, 100);
- aha1542_intr_reset(base);
+ if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 100))
+ goto fail;
+ aha1542_intr_reset(sh->io_port);
if ((mbenable_result[0] & 0x08) || mbenable_result[1]) {
mbenable_cmd[0] = CMD_MBENABLE;
@@ -839,490 +634,269 @@ static int __init aha1542_mbenable(int base)
if ((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03))
retval = BIOS_TRANSLATION_25563;
- aha1542_out(base, mbenable_cmd, 3);
- WAIT(INTRFLAGS(base), INTRMASK, HACC, 0);
- };
+ if (aha1542_out(sh->io_port, mbenable_cmd, 3))
+ goto fail;
+ }
while (0) {
fail:
- printk(KERN_ERR "aha1542_mbenable: Mailbox init failed\n");
+ shost_printk(KERN_ERR, sh, "Mailbox init failed\n");
}
- aha1542_intr_reset(base);
+ aha1542_intr_reset(sh->io_port);
return retval;
}
/* Query the board to find out if it is a 1542 or a 1740, or whatever. */
-static int __init aha1542_query(int base_io, int *transl)
+static int aha1542_query(struct Scsi_Host *sh)
{
- unchar inquiry_cmd[] = {CMD_INQUIRY};
- unchar inquiry_result[4];
+ struct aha1542_hostdata *aha1542 = shost_priv(sh);
+ u8 inquiry_result[4];
int i;
- i = inb(STATUS(base_io));
+ i = inb(STATUS(sh->io_port));
if (i & DF) {
- i = inb(DATA(base_io));
- };
- aha1542_out(base_io, inquiry_cmd, 1);
- aha1542_in(base_io, inquiry_result, 4);
- WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
- while (0) {
-fail:
- printk(KERN_ERR "aha1542_detect: query card type\n");
+ i = inb(DATA(sh->io_port));
}
- aha1542_intr_reset(base_io);
+ aha1542_outb(sh->io_port, CMD_INQUIRY);
+ aha1542_in(sh->io_port, inquiry_result, 4, 0);
+ if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 0))
+ shost_printk(KERN_ERR, sh, "error querying card type\n");
+ aha1542_intr_reset(sh->io_port);
- *transl = BIOS_TRANSLATION_6432; /* Default case */
+ aha1542->bios_translation = BIOS_TRANSLATION_6432; /* Default case */
- /* For an AHA1740 series board, we ignore the board since there is a
- hardware bug which can lead to wrong blocks being returned if the board
- is operating in the 1542 emulation mode. Since there is an extended mode
- driver, we simply ignore the board and let the 1740 driver pick it up.
+ /*
+ * For an AHA1740 series board, we ignore the board since there is a
+ * hardware bug which can lead to wrong blocks being returned if the board
+ * is operating in the 1542 emulation mode. Since there is an extended mode
+ * driver, we simply ignore the board and let the 1740 driver pick it up.
*/
if (inquiry_result[0] == 0x43) {
- printk(KERN_INFO "aha1542.c: Emulation mode not supported for AHA 174N hardware.\n");
+ shost_printk(KERN_INFO, sh, "Emulation mode not supported for AHA-1740 hardware, use aha1740 driver instead.\n");
return 1;
- };
+ }
- /* Always call this - boards that do not support extended bios translation
- will ignore the command, and we will set the proper default */
+ /*
+ * Always call this - boards that do not support extended bios translation
+ * will ignore the command, and we will set the proper default
+ */
- *transl = aha1542_mbenable(base_io);
+ aha1542->bios_translation = aha1542_mbenable(sh);
return 0;
}
-#ifndef MODULE
-static char *setup_str[MAXBOARDS] __initdata;
-static int setup_idx = 0;
-
-static void __init aha1542_setup(char *str, int *ints)
+static u8 dma_speed_hw(int dma_speed)
{
- const char *ahausage = "aha1542: usage: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]\n";
- int setup_portbase;
-
- if (setup_idx >= MAXBOARDS) {
- printk(KERN_ERR "aha1542: aha1542_setup called too many times! Bad LILO params ?\n");
- printk(KERN_ERR " Entryline 1: %s\n", setup_str[0]);
- printk(KERN_ERR " Entryline 2: %s\n", setup_str[1]);
- printk(KERN_ERR " This line: %s\n", str);
- return;
- }
- if (ints[0] < 1 || ints[0] > 4) {
- printk(KERN_ERR "aha1542: %s\n", str);
- printk(ahausage);
- printk(KERN_ERR "aha1542: Wrong parameters may cause system malfunction.. We try anyway..\n");
+ switch (dma_speed) {
+ case 5:
+ return 0x00;
+ case 6:
+ return 0x04;
+ case 7:
+ return 0x01;
+ case 8:
+ return 0x02;
+ case 10:
+ return 0x03;
}
- setup_called[setup_idx] = ints[0];
- setup_str[setup_idx] = str;
- setup_portbase = ints[0] >= 1 ? ints[1] : 0; /* Preserve the default value.. */
- setup_buson[setup_idx] = ints[0] >= 2 ? ints[2] : 7;
- setup_busoff[setup_idx] = ints[0] >= 3 ? ints[3] : 5;
- if (ints[0] >= 4)
- {
- int atbt = -1;
- switch (ints[4]) {
- case 5:
- atbt = 0x00;
- break;
- case 6:
- atbt = 0x04;
- break;
- case 7:
- atbt = 0x01;
- break;
- case 8:
- atbt = 0x02;
- break;
- case 10:
- atbt = 0x03;
- break;
- default:
- printk(KERN_ERR "aha1542: %s\n", str);
- printk(ahausage);
- printk(KERN_ERR "aha1542: Valid values for DMASPEED are 5-8, 10 MB/s. Using jumper defaults.\n");
- break;
- }
- setup_dmaspeed[setup_idx] = atbt;
- }
- if (setup_portbase != 0)
- bases[setup_idx] = setup_portbase;
-
- ++setup_idx;
+ return 0xff; /* invalid */
}
-static int __init do_setup(char *str)
+/* Set the Bus on/off-times as not to ruin floppy performance */
+static void aha1542_set_bus_times(struct Scsi_Host *sh, int bus_on, int bus_off, int dma_speed)
{
- int ints[5];
+ if (bus_on > 0) {
+ u8 oncmd[] = { CMD_BUSON_TIME, clamp(bus_on, 2, 15) };
- int count=setup_idx;
+ aha1542_intr_reset(sh->io_port);
+ if (aha1542_out(sh->io_port, oncmd, 2))
+ goto fail;
+ }
- get_options(str, ARRAY_SIZE(ints), ints);
- aha1542_setup(str,ints);
+ if (bus_off > 0) {
+ u8 offcmd[] = { CMD_BUSOFF_TIME, clamp(bus_off, 1, 64) };
- return count<setup_idx;
-}
+ aha1542_intr_reset(sh->io_port);
+ if (aha1542_out(sh->io_port, offcmd, 2))
+ goto fail;
+ }
-__setup("aha1542=",do_setup);
-#endif
+ if (dma_speed_hw(dma_speed) != 0xff) {
+ u8 dmacmd[] = { CMD_DMASPEED, dma_speed_hw(dma_speed) };
+
+ aha1542_intr_reset(sh->io_port);
+ if (aha1542_out(sh->io_port, dmacmd, 2))
+ goto fail;
+ }
+ aha1542_intr_reset(sh->io_port);
+ return;
+fail:
+ shost_printk(KERN_ERR, sh, "setting bus on/off-time failed\n");
+ aha1542_intr_reset(sh->io_port);
+}
/* return non-zero on detection */
-static int __init aha1542_detect(struct scsi_host_template * tpnt)
+static struct Scsi_Host *aha1542_hw_init(const struct scsi_host_template *tpnt,
+ struct device *pdev, int indx)
{
- unsigned char dma_chan;
- unsigned char irq_level;
- unsigned char scsi_id;
- unsigned long flags;
- unsigned int base_io;
- int trans;
- struct Scsi_Host *shpnt = NULL;
- int count = 0;
- int indx;
-
- DEB(printk("aha1542_detect: \n"));
-
- tpnt->proc_name = "aha1542";
-
-#ifdef MODULE
- bases[0] = aha1542[0];
- setup_buson[0] = aha1542[1];
- setup_busoff[0] = aha1542[2];
- {
- int atbt = -1;
- switch (aha1542[3]) {
- case 5:
- atbt = 0x00;
- break;
- case 6:
- atbt = 0x04;
- break;
- case 7:
- atbt = 0x01;
- break;
- case 8:
- atbt = 0x02;
- break;
- case 10:
- atbt = 0x03;
- break;
- };
- setup_dmaspeed[0] = atbt;
+ unsigned int base_io = io[indx];
+ struct Scsi_Host *sh;
+ struct aha1542_hostdata *aha1542;
+ char dma_info[] = "no DMA";
+
+ if (base_io == 0)
+ return NULL;
+
+ if (!request_region(base_io, AHA1542_REGION_SIZE, "aha1542"))
+ return NULL;
+
+ sh = scsi_host_alloc(tpnt, sizeof(struct aha1542_hostdata));
+ if (!sh)
+ goto release;
+ aha1542 = shost_priv(sh);
+
+ sh->unique_id = base_io;
+ sh->io_port = base_io;
+ sh->n_io_port = AHA1542_REGION_SIZE;
+ aha1542->aha1542_last_mbi_used = 2 * AHA1542_MAILBOXES - 1;
+ aha1542->aha1542_last_mbo_used = AHA1542_MAILBOXES - 1;
+
+ if (!aha1542_test_port(sh))
+ goto unregister;
+
+ aha1542_set_bus_times(sh, bus_on[indx], bus_off[indx], dma_speed[indx]);
+ if (aha1542_query(sh))
+ goto unregister;
+ if (aha1542_getconfig(sh) == -1)
+ goto unregister;
+
+ if (sh->dma_channel != 0xFF)
+ snprintf(dma_info, sizeof(dma_info), "DMA %d", sh->dma_channel);
+ shost_printk(KERN_INFO, sh, "Adaptec AHA-1542 (SCSI-ID %d) at IO 0x%x, IRQ %d, %s\n",
+ sh->this_id, base_io, sh->irq, dma_info);
+ if (aha1542->bios_translation == BIOS_TRANSLATION_25563)
+ shost_printk(KERN_INFO, sh, "Using extended bios translation\n");
+
+ if (dma_set_mask_and_coherent(pdev, DMA_BIT_MASK(24)) < 0)
+ goto unregister;
+
+ aha1542->mb = dma_alloc_coherent(pdev,
+ AHA1542_MAILBOXES * 2 * sizeof(struct mailbox),
+ &aha1542->mb_handle, GFP_KERNEL);
+ if (!aha1542->mb)
+ goto unregister;
+
+ aha1542->ccb = dma_alloc_coherent(pdev,
+ AHA1542_MAILBOXES * sizeof(struct ccb),
+ &aha1542->ccb_handle, GFP_KERNEL);
+ if (!aha1542->ccb)
+ goto free_mb;
+
+ setup_mailboxes(sh);
+
+ if (request_irq(sh->irq, aha1542_interrupt, 0, "aha1542", sh)) {
+ shost_printk(KERN_ERR, sh, "Unable to allocate IRQ.\n");
+ goto free_ccb;
}
-#endif
-
- /*
- * Find MicroChannel cards (AHA1640)
- */
-#ifdef CONFIG_MCA_LEGACY
- if(MCA_bus) {
- int slot = 0;
- int pos = 0;
-
- for (indx = 0; (slot != MCA_NOTFOUND) && (indx < ARRAY_SIZE(bases)); indx++) {
-
- if (bases[indx])
- continue;
-
- /* Detect only AHA-1640 cards -- MCA ID 0F1F */
- slot = mca_find_unused_adapter(0x0f1f, slot);
- if (slot == MCA_NOTFOUND)
- break;
-
- /* Found one */
- pos = mca_read_stored_pos(slot, 3);
-
- /* Decode address */
- if (pos & 0x80) {
- if (pos & 0x02) {
- if (pos & 0x01)
- bases[indx] = 0x334;
- else
- bases[indx] = 0x234;
- } else {
- if (pos & 0x01)
- bases[indx] = 0x134;
- }
- } else {
- if (pos & 0x02) {
- if (pos & 0x01)
- bases[indx] = 0x330;
- else
- bases[indx] = 0x230;
- } else {
- if (pos & 0x01)
- bases[indx] = 0x130;
- }
- }
-
- /* No need to decode IRQ and Arb level -- those are
- * read off the card later.
- */
- printk(KERN_INFO "Found an AHA-1640 in MCA slot %d, I/O 0x%04x\n", slot, bases[indx]);
-
- mca_set_adapter_name(slot, "Adapter AHA-1640");
- mca_set_adapter_procfn(slot, NULL, NULL);
- mca_mark_as_used(slot);
-
- /* Go on */
- slot++;
+ if (sh->dma_channel != 0xFF) {
+ if (request_dma(sh->dma_channel, "aha1542")) {
+ shost_printk(KERN_ERR, sh, "Unable to allocate DMA channel.\n");
+ goto free_irq;
}
-
- }
-#endif
-
- /*
- * Hunt for ISA Plug'n'Pray Adaptecs (AHA1535)
- */
-
- if(isapnp)
- {
- struct pnp_dev *pdev = NULL;
- for(indx = 0; indx < ARRAY_SIZE(bases); indx++) {
- if(bases[indx])
- continue;
- pdev = pnp_find_dev(NULL, ISAPNP_VENDOR('A', 'D', 'P'),
- ISAPNP_FUNCTION(0x1542), pdev);
- if(pdev==NULL)
- break;
- /*
- * Activate the PnP card
- */
-
- if(pnp_device_attach(pdev)<0)
- continue;
-
- if(pnp_activate_dev(pdev)<0) {
- pnp_device_detach(pdev);
- continue;
- }
-
- if(!pnp_port_valid(pdev, 0)) {
- pnp_device_detach(pdev);
- continue;
- }
-
- bases[indx] = pnp_port_start(pdev, 0);
-
- /* The card can be queried for its DMA, we have
- the DMA set up that is enough */
-
- printk(KERN_INFO "ISAPnP found an AHA1535 at I/O 0x%03X\n", bases[indx]);
+ if (sh->dma_channel == 0 || sh->dma_channel >= 5) {
+ set_dma_mode(sh->dma_channel, DMA_MODE_CASCADE);
+ enable_dma(sh->dma_channel);
}
}
- for (indx = 0; indx < ARRAY_SIZE(bases); indx++)
- if (bases[indx] != 0 && request_region(bases[indx], 4, "aha1542")) {
- shpnt = scsi_register(tpnt,
- sizeof(struct aha1542_hostdata));
-
- if(shpnt==NULL) {
- release_region(bases[indx], 4);
- continue;
- }
- if (!aha1542_test_port(bases[indx], shpnt))
- goto unregister;
-
- base_io = bases[indx];
-
- /* Set the Bus on/off-times as not to ruin floppy performance */
- {
- unchar oncmd[] = {CMD_BUSON_TIME, 7};
- unchar offcmd[] = {CMD_BUSOFF_TIME, 5};
-
- if (setup_called[indx]) {
- oncmd[1] = setup_buson[indx];
- offcmd[1] = setup_busoff[indx];
- }
- aha1542_intr_reset(base_io);
- aha1542_out(base_io, oncmd, 2);
- WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
- aha1542_intr_reset(base_io);
- aha1542_out(base_io, offcmd, 2);
- WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
- if (setup_dmaspeed[indx] >= 0) {
- unchar dmacmd[] = {CMD_DMASPEED, 0};
- dmacmd[1] = setup_dmaspeed[indx];
- aha1542_intr_reset(base_io);
- aha1542_out(base_io, dmacmd, 2);
- WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
- }
- while (0) {
-fail:
- printk(KERN_ERR "aha1542_detect: setting bus on/off-time failed\n");
- }
- aha1542_intr_reset(base_io);
- }
- if (aha1542_query(base_io, &trans))
- goto unregister;
- if (aha1542_getconfig(base_io, &irq_level, &dma_chan, &scsi_id) == -1)
- goto unregister;
+ if (scsi_add_host(sh, pdev))
+ goto free_dma;
- printk(KERN_INFO "Configuring Adaptec (SCSI-ID %d) at IO:%x, IRQ %d", scsi_id, base_io, irq_level);
- if (dma_chan != 0xFF)
- printk(", DMA priority %d", dma_chan);
- printk("\n");
+ scsi_scan_host(sh);
- DEB(aha1542_stat());
- setup_mailboxes(base_io, shpnt);
-
- DEB(aha1542_stat());
-
- DEB(printk("aha1542_detect: enable interrupt channel %d\n", irq_level));
- spin_lock_irqsave(&aha1542_lock, flags);
- if (request_irq(irq_level, do_aha1542_intr_handle, 0,
- "aha1542", shpnt)) {
- printk(KERN_ERR "Unable to allocate IRQ for adaptec controller.\n");
- spin_unlock_irqrestore(&aha1542_lock, flags);
- goto unregister;
- }
- if (dma_chan != 0xFF) {
- if (request_dma(dma_chan, "aha1542")) {
- printk(KERN_ERR "Unable to allocate DMA channel for Adaptec.\n");
- free_irq(irq_level, shpnt);
- spin_unlock_irqrestore(&aha1542_lock, flags);
- goto unregister;
- }
- if (dma_chan == 0 || dma_chan >= 5) {
- set_dma_mode(dma_chan, DMA_MODE_CASCADE);
- enable_dma(dma_chan);
- }
- }
-
- shpnt->this_id = scsi_id;
- shpnt->unique_id = base_io;
- shpnt->io_port = base_io;
- shpnt->n_io_port = 4; /* Number of bytes of I/O space used */
- shpnt->dma_channel = dma_chan;
- shpnt->irq = irq_level;
- HOSTDATA(shpnt)->bios_translation = trans;
- if (trans == BIOS_TRANSLATION_25563)
- printk(KERN_INFO "aha1542.c: Using extended bios translation\n");
- HOSTDATA(shpnt)->aha1542_last_mbi_used = (2 * AHA1542_MAILBOXES - 1);
- HOSTDATA(shpnt)->aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1);
- memset(HOSTDATA(shpnt)->SCint, 0, sizeof(HOSTDATA(shpnt)->SCint));
- spin_unlock_irqrestore(&aha1542_lock, flags);
-#if 0
- DEB(printk(" *** READ CAPACITY ***\n"));
-
- {
- unchar buf[8];
- static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int i;
-
- for (i = 0; i < sizeof(buf); ++i)
- buf[i] = 0x87;
- for (i = 0; i < 2; ++i)
- if (!aha1542_command(i, cmd, buf, sizeof(buf))) {
- printk(KERN_DEBUG "aha_detect: LU %d sector_size %d device_size %d\n",
- i, xscsi2int(buf + 4), xscsi2int(buf));
- }
- }
+ return sh;
- DEB(printk(" *** NOW RUNNING MY OWN TEST *** \n"));
-
- for (i = 0; i < 4; ++i) {
- unsigned char cmd[10];
- static buffer[512];
-
- cmd[0] = READ_10;
- cmd[1] = 0;
- xany2scsi(cmd + 2, i);
- cmd[6] = 0;
- cmd[7] = 0;
- cmd[8] = 1;
- cmd[9] = 0;
- aha1542_command(0, cmd, buffer, 512);
- }
-#endif
- count++;
- continue;
+free_dma:
+ if (sh->dma_channel != 0xff)
+ free_dma(sh->dma_channel);
+free_irq:
+ free_irq(sh->irq, sh);
+free_ccb:
+ dma_free_coherent(pdev, AHA1542_MAILBOXES * sizeof(struct ccb),
+ aha1542->ccb, aha1542->ccb_handle);
+free_mb:
+ dma_free_coherent(pdev, AHA1542_MAILBOXES * 2 * sizeof(struct mailbox),
+ aha1542->mb, aha1542->mb_handle);
unregister:
- release_region(bases[indx], 4);
- scsi_unregister(shpnt);
- continue;
+ scsi_host_put(sh);
+release:
+ release_region(base_io, AHA1542_REGION_SIZE);
- };
-
- return count;
+ return NULL;
}
-static int aha1542_release(struct Scsi_Host *shost)
+static int aha1542_release(struct Scsi_Host *sh)
{
- if (shost->irq)
- free_irq(shost->irq, shost);
- if (shost->dma_channel != 0xff)
- free_dma(shost->dma_channel);
- if (shost->io_port && shost->n_io_port)
- release_region(shost->io_port, shost->n_io_port);
- scsi_unregister(shost);
+ struct aha1542_hostdata *aha1542 = shost_priv(sh);
+ struct device *dev = sh->dma_dev;
+
+ scsi_remove_host(sh);
+ if (sh->dma_channel != 0xff)
+ free_dma(sh->dma_channel);
+ dma_free_coherent(dev, AHA1542_MAILBOXES * sizeof(struct ccb),
+ aha1542->ccb, aha1542->ccb_handle);
+ dma_free_coherent(dev, AHA1542_MAILBOXES * 2 * sizeof(struct mailbox),
+ aha1542->mb, aha1542->mb_handle);
+ if (sh->irq)
+ free_irq(sh->irq, sh);
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ scsi_host_put(sh);
return 0;
}
-static int aha1542_restart(struct Scsi_Host *shost)
-{
- int i;
- int count = 0;
-#if 0
- unchar ahacmd = CMD_START_SCSI;
-#endif
-
- for (i = 0; i < AHA1542_MAILBOXES; i++)
- if (HOSTDATA(shost)->SCint[i] &&
- !(HOSTDATA(shost)->SCint[i]->device->soft_reset)) {
-#if 0
- HOSTDATA(shost)->mb[i].status = 1; /* Indicate ready to restart... */
-#endif
- count++;
- }
- printk(KERN_DEBUG "Potential to restart %d stalled commands...\n", count);
-#if 0
- /* start scsi command */
- if (count)
- aha1542_out(shost->io_port, &ahacmd, 1);
-#endif
- return 0;
-}
/*
* This is a device reset. This is handled by sending a special command
* to the device.
*/
-static int aha1542_dev_reset(Scsi_Cmnd * SCpnt)
+static int aha1542_dev_reset(struct scsi_cmnd *cmd)
{
+ struct Scsi_Host *sh = cmd->device->host;
+ struct aha1542_hostdata *aha1542 = shost_priv(sh);
unsigned long flags;
- struct mailbox *mb;
- unchar target = SCpnt->device->id;
- unchar lun = SCpnt->device->lun;
+ struct mailbox *mb = aha1542->mb;
+ u8 target = cmd->device->id;
+ u8 lun = cmd->device->lun;
int mbo;
- struct ccb *ccb;
- unchar ahacmd = CMD_START_SCSI;
-
- ccb = HOSTDATA(SCpnt->device->host)->ccb;
- mb = HOSTDATA(SCpnt->device->host)->mb;
+ struct ccb *ccb = aha1542->ccb;
- spin_lock_irqsave(&aha1542_lock, flags);
- mbo = HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used + 1;
+ spin_lock_irqsave(sh->host_lock, flags);
+ mbo = aha1542->aha1542_last_mbo_used + 1;
if (mbo >= AHA1542_MAILBOXES)
mbo = 0;
do {
- if (mb[mbo].status == 0 && HOSTDATA(SCpnt->device->host)->SCint[mbo] == NULL)
+ if (mb[mbo].status == 0 && aha1542->int_cmds[mbo] == NULL)
break;
mbo++;
if (mbo >= AHA1542_MAILBOXES)
mbo = 0;
- } while (mbo != HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used);
+ } while (mbo != aha1542->aha1542_last_mbo_used);
- if (mb[mbo].status || HOSTDATA(SCpnt->device->host)->SCint[mbo])
+ if (mb[mbo].status || aha1542->int_cmds[mbo])
panic("Unable to find empty mailbox for aha1542.\n");
- HOSTDATA(SCpnt->device->host)->SCint[mbo] = SCpnt; /* This will effectively
- prevent someone else from
- screwing with this cdb. */
+ aha1542->int_cmds[mbo] = cmd; /* This will effectively
+ * prevent someone else from
+ * screwing with this cdb.
+ */
- HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used = mbo;
- spin_unlock_irqrestore(&aha1542_lock, flags);
+ aha1542->aha1542_last_mbo_used = mbo;
- any2scsi(mb[mbo].ccbptr, SCSI_BUF_PA(&ccb[mbo])); /* This gets trashed for some reason */
+ /* This gets trashed for some reason */
+ any2scsi(mb[mbo].ccbptr, aha1542->ccb_handle + mbo * sizeof(*ccb));
memset(&ccb[mbo], 0, sizeof(struct ccb));
@@ -1333,92 +907,62 @@ static int aha1542_dev_reset(Scsi_Cmnd * SCpnt)
ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
ccb[mbo].commlinkid = 0;
- /*
- * Now tell the 1542 to flush all pending commands for this
- * target
+ /*
+ * Now tell the 1542 to flush all pending commands for this
+ * target
*/
- aha1542_out(SCpnt->device->host->io_port, &ahacmd, 1);
+ aha1542_outb(sh->io_port, CMD_START_SCSI);
+ spin_unlock_irqrestore(sh->host_lock, flags);
- scmd_printk(KERN_WARNING, SCpnt,
+ scmd_printk(KERN_WARNING, cmd,
"Trying device reset for target\n");
return SUCCESS;
-
-
-#ifdef ERIC_neverdef
- /*
- * With the 1542 we apparently never get an interrupt to
- * acknowledge a device reset being sent. Then again, Leonard
- * says we are doing this wrong in the first place...
- *
- * Take a wait and see attitude. If we get spurious interrupts,
- * then the device reset is doing something sane and useful, and
- * we will wait for the interrupt to post completion.
- */
- printk(KERN_WARNING "Sent BUS DEVICE RESET to target %d\n", SCpnt->target);
-
- /*
- * Free the command block for all commands running on this
- * target...
- */
- for (i = 0; i < AHA1542_MAILBOXES; i++) {
- if (HOSTDATA(SCpnt->host)->SCint[i] &&
- HOSTDATA(SCpnt->host)->SCint[i]->target == SCpnt->target) {
- Scsi_Cmnd *SCtmp;
- SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
- kfree(SCtmp->host_scribble);
- SCtmp->host_scribble = NULL;
- HOSTDATA(SCpnt->host)->SCint[i] = NULL;
- HOSTDATA(SCpnt->host)->mb[i].status = 0;
- }
- }
- return SUCCESS;
-
- return FAILED;
-#endif /* ERIC_neverdef */
}
-static int aha1542_bus_reset(Scsi_Cmnd * SCpnt)
+static int aha1542_reset(struct scsi_cmnd *cmd, u8 reset_cmd)
{
+ struct Scsi_Host *sh = cmd->device->host;
+ struct aha1542_hostdata *aha1542 = shost_priv(sh);
+ unsigned long flags;
int i;
- /*
+ spin_lock_irqsave(sh->host_lock, flags);
+ /*
* This does a scsi reset for all devices on the bus.
* In principle, we could also reset the 1542 - should
* we do this? Try this first, and we can add that later
* if it turns out to be useful.
*/
- outb(SCRST, CONTROL(SCpnt->device->host->io_port));
+ outb(reset_cmd, CONTROL(cmd->device->host->io_port));
+
+ if (!wait_mask(STATUS(cmd->device->host->io_port),
+ STATMASK, IDLE, STST | DIAGF | INVDCMD | DF | CDF, 0)) {
+ spin_unlock_irqrestore(sh->host_lock, flags);
+ return FAILED;
+ }
/*
- * Wait for the thing to settle down a bit. Unfortunately
- * this is going to basically lock up the machine while we
- * wait for this to complete. To be 100% correct, we need to
- * check for timeout, and if we are doing something like this
- * we are pretty desperate anyways.
+ * We need to do this too before the 1542 can interact with
+ * us again after host reset.
*/
- ssleep(4);
-
- spin_lock_irq(SCpnt->device->host->host_lock);
-
- WAIT(STATUS(SCpnt->device->host->io_port),
- STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+ if (reset_cmd & HRST)
+ setup_mailboxes(cmd->device->host);
/*
* Now try to pick up the pieces. For all pending commands,
* free any internal data structures, and basically clear things
- * out. We do not try and restart any commands or anything -
+ * out. We do not try and restart any commands or anything -
* the strategy handler takes care of that crap.
*/
- printk(KERN_WARNING "Sent BUS RESET to scsi host %d\n", SCpnt->device->host->host_no);
+ shost_printk(KERN_WARNING, cmd->device->host, "Sent BUS RESET to scsi host %d\n", cmd->device->host->host_no);
for (i = 0; i < AHA1542_MAILBOXES; i++) {
- if (HOSTDATA(SCpnt->device->host)->SCint[i] != NULL) {
- Scsi_Cmnd *SCtmp;
- SCtmp = HOSTDATA(SCpnt->device->host)->SCint[i];
+ if (aha1542->int_cmds[i] != NULL) {
+ struct scsi_cmnd *tmp_cmd;
+ tmp_cmd = aha1542->int_cmds[i];
-
- if (SCtmp->device->soft_reset) {
+ if (tmp_cmd->device->soft_reset) {
/*
* If this device implements the soft reset option,
* then it is still holding onto the command, and
@@ -1427,319 +971,200 @@ static int aha1542_bus_reset(Scsi_Cmnd * SCpnt)
*/
continue;
}
- kfree(SCtmp->host_scribble);
- SCtmp->host_scribble = NULL;
- HOSTDATA(SCpnt->device->host)->SCint[i] = NULL;
- HOSTDATA(SCpnt->device->host)->mb[i].status = 0;
+ aha1542_free_cmd(tmp_cmd);
+ aha1542->int_cmds[i] = NULL;
+ aha1542->mb[i].status = 0;
}
}
- spin_unlock_irq(SCpnt->device->host->host_lock);
+ spin_unlock_irqrestore(sh->host_lock, flags);
return SUCCESS;
-
-fail:
- spin_unlock_irq(SCpnt->device->host->host_lock);
- return FAILED;
}
-static int aha1542_host_reset(Scsi_Cmnd * SCpnt)
+static int aha1542_bus_reset(struct scsi_cmnd *cmd)
{
- int i;
-
- /*
- * This does a scsi reset for all devices on the bus.
- * In principle, we could also reset the 1542 - should
- * we do this? Try this first, and we can add that later
- * if it turns out to be useful.
- */
- outb(HRST | SCRST, CONTROL(SCpnt->device->host->io_port));
-
- /*
- * Wait for the thing to settle down a bit. Unfortunately
- * this is going to basically lock up the machine while we
- * wait for this to complete. To be 100% correct, we need to
- * check for timeout, and if we are doing something like this
- * we are pretty desperate anyways.
- */
- ssleep(4);
- spin_lock_irq(SCpnt->device->host->host_lock);
+ return aha1542_reset(cmd, SCRST);
+}
- WAIT(STATUS(SCpnt->device->host->io_port),
- STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+static int aha1542_host_reset(struct scsi_cmnd *cmd)
+{
+ return aha1542_reset(cmd, HRST | SCRST);
+}
- /*
- * We need to do this too before the 1542 can interact with
- * us again.
- */
- setup_mailboxes(SCpnt->device->host->io_port, SCpnt->device->host);
+static int aha1542_biosparam(struct scsi_device *sdev,
+ struct gendisk *unused, sector_t capacity, int geom[])
+{
+ struct aha1542_hostdata *aha1542 = shost_priv(sdev->host);
- /*
- * Now try to pick up the pieces. For all pending commands,
- * free any internal data structures, and basically clear things
- * out. We do not try and restart any commands or anything -
- * the strategy handler takes care of that crap.
- */
- printk(KERN_WARNING "Sent BUS RESET to scsi host %d\n", SCpnt->device->host->host_no);
+ if (capacity >= 0x200000 &&
+ aha1542->bios_translation == BIOS_TRANSLATION_25563) {
+ /* Please verify that this is the same as what DOS returns */
+ geom[0] = 255; /* heads */
+ geom[1] = 63; /* sectors */
+ } else {
+ geom[0] = 64; /* heads */
+ geom[1] = 32; /* sectors */
+ }
+ geom[2] = sector_div(capacity, geom[0] * geom[1]); /* cylinders */
- for (i = 0; i < AHA1542_MAILBOXES; i++) {
- if (HOSTDATA(SCpnt->device->host)->SCint[i] != NULL) {
- Scsi_Cmnd *SCtmp;
- SCtmp = HOSTDATA(SCpnt->device->host)->SCint[i];
+ return 0;
+}
- if (SCtmp->device->soft_reset) {
- /*
- * If this device implements the soft reset option,
- * then it is still holding onto the command, and
- * may yet complete it. In this case, we don't
- * flush the data.
- */
- continue;
- }
- kfree(SCtmp->host_scribble);
- SCtmp->host_scribble = NULL;
- HOSTDATA(SCpnt->device->host)->SCint[i] = NULL;
- HOSTDATA(SCpnt->device->host)->mb[i].status = 0;
- }
- }
+MODULE_DESCRIPTION("Adaptec AHA-1542 SCSI host adapter driver");
+MODULE_LICENSE("GPL");
- spin_unlock_irq(SCpnt->device->host->host_lock);
- return SUCCESS;
+static int aha1542_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
+{
+ struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);
-fail:
- spin_unlock_irq(SCpnt->device->host->host_lock);
- return FAILED;
+ acmd->data_buffer = dma_alloc_coherent(shost->dma_dev,
+ SECTOR_SIZE * AHA1542_MAX_SECTORS,
+ &acmd->data_buffer_handle, GFP_KERNEL);
+ if (!acmd->data_buffer)
+ return -ENOMEM;
+ return 0;
}
-#if 0
-/*
- * These are the old error handling routines. They are only temporarily
- * here while we play with the new error handling code.
- */
-static int aha1542_old_abort(Scsi_Cmnd * SCpnt)
+static int aha1542_exit_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
{
-#if 0
- unchar ahacmd = CMD_START_SCSI;
- unsigned long flags;
- struct mailbox *mb;
- int mbi, mbo, i;
+ struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);
- printk(KERN_DEBUG "In aha1542_abort: %x %x\n",
- inb(STATUS(SCpnt->host->io_port)),
- inb(INTRFLAGS(SCpnt->host->io_port)));
+ dma_free_coherent(shost->dma_dev, SECTOR_SIZE * AHA1542_MAX_SECTORS,
+ acmd->data_buffer, acmd->data_buffer_handle);
+ return 0;
+}
- spin_lock_irqsave(&aha1542_lock, flags);
- mb = HOSTDATA(SCpnt->host)->mb;
- mbi = HOSTDATA(SCpnt->host)->aha1542_last_mbi_used + 1;
- if (mbi >= 2 * AHA1542_MAILBOXES)
- mbi = AHA1542_MAILBOXES;
+static const struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .proc_name = "aha1542",
+ .name = "Adaptec 1542",
+ .cmd_size = sizeof(struct aha1542_cmd),
+ .queuecommand = aha1542_queuecommand,
+ .eh_device_reset_handler= aha1542_dev_reset,
+ .eh_bus_reset_handler = aha1542_bus_reset,
+ .eh_host_reset_handler = aha1542_host_reset,
+ .bios_param = aha1542_biosparam,
+ .init_cmd_priv = aha1542_init_cmd_priv,
+ .exit_cmd_priv = aha1542_exit_cmd_priv,
+ .can_queue = AHA1542_MAILBOXES,
+ .this_id = 7,
+ .max_sectors = AHA1542_MAX_SECTORS,
+ .sg_tablesize = SG_ALL,
+};
- do {
- if (mb[mbi].status != 0)
- break;
- mbi++;
- if (mbi >= 2 * AHA1542_MAILBOXES)
- mbi = AHA1542_MAILBOXES;
- } while (mbi != HOSTDATA(SCpnt->host)->aha1542_last_mbi_used);
- spin_unlock_irqrestore(&aha1542_lock, flags);
+static int aha1542_isa_match(struct device *pdev, unsigned int ndev)
+{
+ struct Scsi_Host *sh = aha1542_hw_init(&driver_template, pdev, ndev);
- if (mb[mbi].status) {
- printk(KERN_ERR "Lost interrupt discovered on irq %d - attempting to recover\n",
- SCpnt->host->irq);
- aha1542_intr_handle(SCpnt->host, NULL);
+ if (!sh)
return 0;
- }
- /* OK, no lost interrupt. Try looking to see how many pending commands
- we think we have. */
-
- for (i = 0; i < AHA1542_MAILBOXES; i++)
- if (HOSTDATA(SCpnt->host)->SCint[i]) {
- if (HOSTDATA(SCpnt->host)->SCint[i] == SCpnt) {
- printk(KERN_ERR "Timed out command pending for %s\n",
- SCpnt->request->rq_disk ?
- SCpnt->request->rq_disk->disk_name : "?"
- );
- if (HOSTDATA(SCpnt->host)->mb[i].status) {
- printk(KERN_ERR "OGMB still full - restarting\n");
- aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
- };
- } else
- printk(KERN_ERR "Other pending command %s\n",
- SCpnt->request->rq_disk ?
- SCpnt->request->rq_disk->disk_name : "?"
- );
- }
-#endif
- DEB(printk("aha1542_abort\n"));
-#if 0
- spin_lock_irqsave(&aha1542_lock, flags);
- for (mbo = 0; mbo < AHA1542_MAILBOXES; mbo++) {
- if (SCpnt == HOSTDATA(SCpnt->host)->SCint[mbo]) {
- mb[mbo].status = 2; /* Abort command */
- aha1542_out(SCpnt->host->io_port, &ahacmd, 1); /* start scsi command */
- spin_unlock_irqrestore(&aha1542_lock, flags);
- break;
- }
- }
- if (AHA1542_MAILBOXES == mbo)
- spin_unlock_irqrestore(&aha1542_lock, flags);
-#endif
- return SCSI_ABORT_SNOOZE;
+ dev_set_drvdata(pdev, sh);
+ return 1;
+}
+
+static void aha1542_isa_remove(struct device *pdev,
+ unsigned int ndev)
+{
+ aha1542_release(dev_get_drvdata(pdev));
+ dev_set_drvdata(pdev, NULL);
}
-/* We do not implement a reset function here, but the upper level code
- assumes that it will get some kind of response for the command in
- SCpnt. We must oblige, or the command will hang the scsi system.
- For a first go, we assume that the 1542 notifies us with all of the
- pending commands (it does implement soft reset, after all). */
+static struct isa_driver aha1542_isa_driver = {
+ .match = aha1542_isa_match,
+ .remove = aha1542_isa_remove,
+ .driver = {
+ .name = "aha1542"
+ },
+};
+static int isa_registered;
-static int aha1542_old_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+#ifdef CONFIG_PNP
+static const struct pnp_device_id aha1542_pnp_ids[] = {
+ { .id = "ADP1542" },
+ { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, aha1542_pnp_ids);
+
+static int aha1542_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
{
- unchar ahacmd = CMD_START_SCSI;
- int i;
+ int indx;
+ struct Scsi_Host *sh;
- /*
- * See if a bus reset was suggested.
- */
- if (reset_flags & SCSI_RESET_SUGGEST_BUS_RESET) {
- /*
- * This does a scsi reset for all devices on the bus.
- * In principle, we could also reset the 1542 - should
- * we do this? Try this first, and we can add that later
- * if it turns out to be useful.
- */
- outb(HRST | SCRST, CONTROL(SCpnt->host->io_port));
+ for (indx = 0; indx < ARRAY_SIZE(io); indx++) {
+ if (io[indx])
+ continue;
- /*
- * Wait for the thing to settle down a bit. Unfortunately
- * this is going to basically lock up the machine while we
- * wait for this to complete. To be 100% correct, we need to
- * check for timeout, and if we are doing something like this
- * we are pretty desperate anyways.
- */
- WAIT(STATUS(SCpnt->host->io_port),
- STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+ if (pnp_activate_dev(pdev) < 0)
+ continue;
- /*
- * We need to do this too before the 1542 can interact with
- * us again.
- */
- setup_mailboxes(SCpnt->host->io_port, SCpnt->host);
+ io[indx] = pnp_port_start(pdev, 0);
/*
- * Now try to pick up the pieces. Restart all commands
- * that are currently active on the bus, and reset all of
- * the datastructures. We have some time to kill while
- * things settle down, so print a nice message.
- */
- printk(KERN_WARNING "Sent BUS RESET to scsi host %d\n", SCpnt->host->host_no);
-
- for (i = 0; i < AHA1542_MAILBOXES; i++)
- if (HOSTDATA(SCpnt->host)->SCint[i] != NULL) {
- Scsi_Cmnd *SCtmp;
- SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
- SCtmp->result = DID_RESET << 16;
- kfree(SCtmp->host_scribble);
- SCtmp->host_scribble = NULL;
- printk(KERN_WARNING "Sending DID_RESET for target %d\n", SCpnt->target);
- SCtmp->scsi_done(SCpnt);
-
- HOSTDATA(SCpnt->host)->SCint[i] = NULL;
- HOSTDATA(SCpnt->host)->mb[i].status = 0;
- }
- /*
- * Now tell the mid-level code what we did here. Since
- * we have restarted all of the outstanding commands,
- * then report SUCCESS.
+ * The card can be queried for its DMA, we have
+ * the DMA set up that is enough
*/
- return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET);
-fail:
- printk(KERN_CRIT "aha1542.c: Unable to perform hard reset.\n");
- printk(KERN_CRIT "Power cycle machine to reset\n");
- return (SCSI_RESET_ERROR | SCSI_RESET_BUS_RESET);
-
- } else {
- /* This does a selective reset of just the one device */
- /* First locate the ccb for this command */
- for (i = 0; i < AHA1542_MAILBOXES; i++)
- if (HOSTDATA(SCpnt->host)->SCint[i] == SCpnt) {
- HOSTDATA(SCpnt->host)->ccb[i].op = 0x81; /* BUS DEVICE RESET */
- /* Now tell the 1542 to flush all pending commands for this target */
- aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
-
- /* Here is the tricky part. What to do next. Do we get an interrupt
- for the commands that we aborted with the specified target, or
- do we generate this on our own? Try it without first and see
- what happens */
- printk(KERN_WARNING "Sent BUS DEVICE RESET to target %d\n", SCpnt->target);
-
- /* If the first does not work, then try the second. I think the
- first option is more likely to be correct. Free the command
- block for all commands running on this target... */
- for (i = 0; i < AHA1542_MAILBOXES; i++)
- if (HOSTDATA(SCpnt->host)->SCint[i] &&
- HOSTDATA(SCpnt->host)->SCint[i]->target == SCpnt->target) {
- Scsi_Cmnd *SCtmp;
- SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
- SCtmp->result = DID_RESET << 16;
- kfree(SCtmp->host_scribble);
- SCtmp->host_scribble = NULL;
- printk(KERN_WARNING "Sending DID_RESET for target %d\n", SCpnt->target);
- SCtmp->scsi_done(SCpnt);
-
- HOSTDATA(SCpnt->host)->SCint[i] = NULL;
- HOSTDATA(SCpnt->host)->mb[i].status = 0;
- }
- return SCSI_RESET_SUCCESS;
- }
+ dev_info(&pdev->dev, "ISAPnP found an AHA1535 at I/O 0x%03X", io[indx]);
}
- /* No active command at this time, so this means that each time we got
- some kind of response the last time through. Tell the mid-level code
- to request sense information in order to decide what to do next. */
- return SCSI_RESET_PUNT;
+
+ sh = aha1542_hw_init(&driver_template, &pdev->dev, indx);
+ if (!sh)
+ return -ENODEV;
+
+ pnp_set_drvdata(pdev, sh);
+ return 0;
}
-#endif /* end of big comment block around old_abort + old_reset */
-static int aha1542_biosparam(struct scsi_device *sdev,
- struct block_device *bdev, sector_t capacity, int *ip)
+static void aha1542_pnp_remove(struct pnp_dev *pdev)
{
- int translation_algorithm;
- int size = capacity;
+ aha1542_release(pnp_get_drvdata(pdev));
+ pnp_set_drvdata(pdev, NULL);
+}
- translation_algorithm = HOSTDATA(sdev->host)->bios_translation;
+static struct pnp_driver aha1542_pnp_driver = {
+ .name = "aha1542",
+ .id_table = aha1542_pnp_ids,
+ .probe = aha1542_pnp_probe,
+ .remove = aha1542_pnp_remove,
+};
+static int pnp_registered;
+#endif /* CONFIG_PNP */
- if ((size >> 11) > 1024 && translation_algorithm == BIOS_TRANSLATION_25563) {
- /* Please verify that this is the same as what DOS returns */
- ip[0] = 255;
- ip[1] = 63;
- ip[2] = size / 255 / 63;
- } else {
- ip[0] = 64;
- ip[1] = 32;
- ip[2] = size >> 11;
+static int __init aha1542_init(void)
+{
+ int ret = 0;
+
+#ifdef CONFIG_PNP
+ if (isapnp) {
+ ret = pnp_register_driver(&aha1542_pnp_driver);
+ if (!ret)
+ pnp_registered = 1;
}
+#endif
+ ret = isa_register_driver(&aha1542_isa_driver, MAXBOARDS);
+ if (!ret)
+ isa_registered = 1;
- return 0;
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ ret = 0;
+#endif
+ if (isa_registered)
+ ret = 0;
+
+ return ret;
}
-MODULE_LICENSE("GPL");
+static void __exit aha1542_exit(void)
+{
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ pnp_unregister_driver(&aha1542_pnp_driver);
+#endif
+ if (isa_registered)
+ isa_unregister_driver(&aha1542_isa_driver);
+}
-static struct scsi_host_template driver_template = {
- .proc_name = "aha1542",
- .name = "Adaptec 1542",
- .detect = aha1542_detect,
- .release = aha1542_release,
- .queuecommand = aha1542_queuecommand,
- .eh_device_reset_handler= aha1542_dev_reset,
- .eh_bus_reset_handler = aha1542_bus_reset,
- .eh_host_reset_handler = aha1542_host_reset,
- .bios_param = aha1542_biosparam,
- .can_queue = AHA1542_MAILBOXES,
- .this_id = 7,
- .sg_tablesize = AHA1542_SCATTER,
- .cmd_per_lun = AHA1542_CMDLUN,
- .unchecked_isa_dma = 1,
- .use_clustering = ENABLE_CLUSTERING,
-};
-#include "scsi_module.c"
+module_init(aha1542_init);
+module_exit(aha1542_exit);
diff --git a/drivers/scsi/aha1542.h b/drivers/scsi/aha1542.h
index b871d2b57f93..92a5f9896ace 100644
--- a/drivers/scsi/aha1542.h
+++ b/drivers/scsi/aha1542.h
@@ -1,64 +1,36 @@
-#ifndef _AHA1542_H
-
-/* $Id: aha1542.h,v 1.1 1992/07/24 06:27:38 root Exp root $
- *
- * Header file for the adaptec 1542 driver for Linux
- *
- * $Log: aha1542.h,v $
- * Revision 1.1 1992/07/24 06:27:38 root
- * Initial revision
- *
- * Revision 1.2 1992/07/04 18:41:49 root
- * Replaced distribution with current drivers
- *
- * Revision 1.3 1992/06/23 23:58:20 root
- * Fixes.
- *
- * Revision 1.2 1992/05/26 22:13:23 root
- * Changed bug that prevented DMA above first 2 mbytes.
- *
- * Revision 1.1 1992/05/22 21:00:29 root
- * Initial revision
- *
- * Revision 1.1 1992/04/24 18:01:50 root
- * Initial revision
- *
- * Revision 1.1 1992/04/02 03:23:13 drew
- * Initial revision
- *
- * Revision 1.3 1992/01/27 14:46:29 tthorn
- * *** empty log message ***
- *
- */
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _AHA1542_H_
+#define _AHA1542_H_
#include <linux/types.h>
/* I/O Port interface 4.2 */
/* READ */
#define STATUS(base) base
-#define STST 0x80 /* Self Test in Progress */
-#define DIAGF 0x40 /* Internal Diagnostic Failure */
-#define INIT 0x20 /* Mailbox Initialization Required */
-#define IDLE 0x10 /* SCSI Host Adapter Idle */
-#define CDF 0x08 /* Command/Data Out Port Full */
-#define DF 0x04 /* Data In Port Full */
-#define INVDCMD 0x01 /* Invalid H A Command */
-#define STATMASK 0xfd /* 0x02 is reserved */
+#define STST BIT(7) /* Self Test in Progress */
+#define DIAGF BIT(6) /* Internal Diagnostic Failure */
+#define INIT BIT(5) /* Mailbox Initialization Required */
+#define IDLE BIT(4) /* SCSI Host Adapter Idle */
+#define CDF BIT(3) /* Command/Data Out Port Full */
+#define DF BIT(2) /* Data In Port Full */
+/* BIT(1) is reserved */
+#define INVDCMD BIT(0) /* Invalid H A Command */
+#define STATMASK (STST | DIAGF | INIT | IDLE | CDF | DF | INVDCMD)
#define INTRFLAGS(base) (STATUS(base)+2)
-#define ANYINTR 0x80 /* Any Interrupt */
-#define SCRD 0x08 /* SCSI Reset Detected */
-#define HACC 0x04 /* HA Command Complete */
-#define MBOA 0x02 /* MBO Empty */
-#define MBIF 0x01 /* MBI Full */
-#define INTRMASK 0x8f
+#define ANYINTR BIT(7) /* Any Interrupt */
+#define SCRD BIT(3) /* SCSI Reset Detected */
+#define HACC BIT(2) /* HA Command Complete */
+#define MBOA BIT(1) /* MBO Empty */
+#define MBIF BIT(0) /* MBI Full */
+#define INTRMASK (ANYINTR | SCRD | HACC | MBOA | MBIF)
/* WRITE */
#define CONTROL(base) STATUS(base)
-#define HRST 0x80 /* Hard Reset */
-#define SRST 0x40 /* Soft Reset */
-#define IRST 0x20 /* Interrupt Reset */
-#define SCRST 0x10 /* SCSI Bus Reset */
+#define HRST BIT(7) /* Hard Reset */
+#define SRST BIT(6) /* Soft Reset */
+#define IRST BIT(5) /* Interrupt Reset */
+#define SCRST BIT(4) /* SCSI Bus Reset */
/* READ/WRITE */
#define DATA(base) (STATUS(base)+1)
@@ -80,14 +52,14 @@
/* Mailbox Definition 5.2.1 and 5.2.2 */
struct mailbox {
- unchar status; /* Command/Status */
- unchar ccbptr[3]; /* msb, .., lsb */
+ u8 status; /* Command/Status */
+ u8 ccbptr[3]; /* msb, .., lsb */
};
/* This is used with scatter-gather */
struct chain {
- unchar datalen[3]; /* Size of this part of chain */
- unchar dataptr[3]; /* Location of data */
+ u8 datalen[3]; /* Size of this part of chain */
+ u8 dataptr[3]; /* Location of data */
};
/* These belong in scsi.h also */
@@ -100,51 +72,37 @@ static inline void any2scsi(u8 *p, u32 v)
#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
-#define xany2scsi(up, p) \
-(up)[0] = ((long)(p)) >> 24; \
-(up)[1] = ((long)(p)) >> 16; \
-(up)[2] = ((long)(p)) >> 8; \
-(up)[3] = ((long)(p));
-
#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ (((long)(up)[2]) << 8) + ((long)(up)[3]) )
#define MAX_CDB 12
#define MAX_SENSE 14
-struct ccb { /* Command Control Block 5.3 */
- unchar op; /* Command Control Block Operation Code */
- unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
- /* Outbound data transfer, length is checked*/
- /* Inbound data transfer, length is checked */
- /* Logical Unit Number */
- unchar cdblen; /* SCSI Command Length */
- unchar rsalen; /* Request Sense Allocation Length/Disable */
- unchar datalen[3]; /* Data Length (msb, .., lsb) */
- unchar dataptr[3]; /* Data Pointer */
- unchar linkptr[3]; /* Link Pointer */
- unchar commlinkid; /* Command Linking Identifier */
- unchar hastat; /* Host Adapter Status (HASTAT) */
- unchar tarstat; /* Target Device Status */
- unchar reserved[2];
- unchar cdb[MAX_CDB+MAX_SENSE];/* SCSI Command Descriptor Block */
- /* REQUEST SENSE */
+/* Command Control Block (CCB), 5.3 */
+struct ccb {
+ u8 op; /* Command Control Block Operation Code: */
+ /* 0x00: SCSI Initiator CCB, 0x01: SCSI Target CCB, */
+ /* 0x02: SCSI Initiator CCB with Scatter/Gather, */
+ /* 0x81: SCSI Bus Device Reset CCB */
+ u8 idlun; /* Address and Direction Control: */
+ /* Bits 7-5: op=0, 2: Target ID, op=1: Initiator ID */
+ /* Bit 4: Outbound data transfer, length is checked */
+ /* Bit 3: Inbound data transfer, length is checked */
+ /* Bits 2-0: Logical Unit Number */
+ u8 cdblen; /* SCSI Command Length */
+ u8 rsalen; /* Request Sense Allocation Length/Disable Auto Sense */
+ u8 datalen[3]; /* Data Length (MSB, ..., LSB) */
+ u8 dataptr[3]; /* Data Pointer (MSB, ..., LSB) */
+ u8 linkptr[3]; /* Link Pointer (MSB, ..., LSB) */
+ u8 commlinkid; /* Command Linking Identifier */
+ u8 hastat; /* Host Adapter Status (HASTAT) */
+ u8 tarstat; /* Target Device Status (TARSTAT) */
+ u8 reserved[2];
+ u8 cdb[MAX_CDB + MAX_SENSE]; /* SCSI Command Descriptor Block */
+ /* followed by the Auto Sense data */
};
-static int aha1542_detect(struct scsi_host_template *);
-static int aha1542_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
-static int aha1542_bus_reset(Scsi_Cmnd * SCpnt);
-static int aha1542_dev_reset(Scsi_Cmnd * SCpnt);
-static int aha1542_host_reset(Scsi_Cmnd * SCpnt);
-#if 0
-static int aha1542_old_abort(Scsi_Cmnd * SCpnt);
-static int aha1542_old_reset(Scsi_Cmnd *, unsigned int);
-#endif
-static int aha1542_biosparam(struct scsi_device *, struct block_device *,
- sector_t, int *);
-
+#define AHA1542_REGION_SIZE 4
#define AHA1542_MAILBOXES 8
-#define AHA1542_SCATTER 16
-#define AHA1542_CMDLUN 1
-#endif
+#endif /* _AHA1542_H_ */
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 1c10b796c1a2..b234621f6b37 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -53,11 +53,14 @@
#include <linux/gfp.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include "aha1740.h"
/* IF YOU ARE HAVING PROBLEMS WITH THIS DRIVER, AND WANT TO WATCH
@@ -107,33 +110,14 @@ static inline dma_addr_t ecb_cpu_to_dma (struct Scsi_Host *host, void *cpu)
return hdata->ecb_dma_addr + offset;
}
-static int aha1740_proc_info(struct Scsi_Host *shpnt, char *buffer,
- char **start, off_t offset,
- int length, int inout)
+static int aha1740_show_info(struct seq_file *m, struct Scsi_Host *shpnt)
{
- int len;
- struct aha1740_hostdata *host;
-
- if (inout)
- return-ENOSYS;
-
- host = HOSTDATA(shpnt);
-
- len = sprintf(buffer, "aha174x at IO:%lx, IRQ %d, SLOT %d.\n"
+ struct aha1740_hostdata *host = HOSTDATA(shpnt);
+ seq_printf(m, "aha174x at IO:%lx, IRQ %d, SLOT %d.\n"
"Extended translation %sabled.\n",
shpnt->io_port, shpnt->irq, host->edev->slot,
host->translation ? "en" : "dis");
-
- if (offset > len) {
- *start = buffer;
- return 0;
- }
-
- *start = buffer + offset;
- len -= offset;
- if (len > length)
- len = length;
- return len;
+ return 0;
}
static int aha1740_makecode(unchar *sense, unchar *status)
@@ -172,6 +156,7 @@ static int aha1740_makecode(unchar *sense, unchar *status)
retval=DID_ERROR; /* It's an Overrun */
/* If not overrun, assume underrun and
* ignore it! */
+ break;
case 0x00: /* No info, assume no error, should
* not occur */
break;
@@ -227,11 +212,11 @@ static int aha1740_test_port(unsigned int base)
static irqreturn_t aha1740_intr_handle(int irq, void *dev_id)
{
struct Scsi_Host *host = (struct Scsi_Host *) dev_id;
- void (*my_done)(Scsi_Cmnd *);
+ void (*my_done)(struct scsi_cmnd *);
int errstatus, adapstat;
int number_serviced;
struct ecb *ecbptr;
- Scsi_Cmnd *SCtmp;
+ struct scsi_cmnd *SCtmp;
unsigned int base;
unsigned long flags;
int handled = 0;
@@ -286,8 +271,11 @@ static irqreturn_t aha1740_intr_handle(int irq, void *dev_id)
guarantee that we will still have it in the
cdb when we come back */
if ( (adapstat & G2INTST_MASK) == G2INTST_CCBERROR ) {
- memcpy(SCtmp->sense_buffer, ecbptr->sense,
- SCSI_SENSE_BUFFERSIZE);
+ memcpy_and_pad(SCtmp->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ ecbptr->sense,
+ sizeof(ecbptr->sense),
+ 0);
errstatus = aha1740_makecode(ecbptr->sense,ecbptr->status);
} else
errstatus = 0;
@@ -331,8 +319,9 @@ static irqreturn_t aha1740_intr_handle(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-static int aha1740_queuecommand_lck(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+static int aha1740_queuecommand_lck(struct scsi_cmnd *SCpnt)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
unchar direction;
unchar *cmd = (unchar *) SCpnt->cmnd;
unchar target = scmd_id(SCpnt);
@@ -521,7 +510,7 @@ static void aha1740_getconfig(unsigned int base, unsigned int *irq_level,
}
static int aha1740_biosparam(struct scsi_device *sdev,
- struct block_device *dev,
+ struct gendisk *unused,
sector_t capacity, int* ip)
{
int size = capacity;
@@ -540,7 +529,7 @@ static int aha1740_biosparam(struct scsi_device *sdev,
return 0;
}
-static int aha1740_eh_abort_handler (Scsi_Cmnd *dummy)
+static int aha1740_eh_abort_handler (struct scsi_cmnd *dummy)
{
/*
* From Alan Cox :
@@ -551,21 +540,19 @@ static int aha1740_eh_abort_handler (Scsi_Cmnd *dummy)
* quiet as possible...
*/
- return 0;
+ return SUCCESS;
}
-static struct scsi_host_template aha1740_template = {
+static const struct scsi_host_template aha1740_template = {
.module = THIS_MODULE,
.proc_name = "aha1740",
- .proc_info = aha1740_proc_info,
+ .show_info = aha1740_show_info,
.name = "Adaptec 174x (EISA)",
.queuecommand = aha1740_queuecommand,
.bios_param = aha1740_biosparam,
.can_queue = AHA1740_ECBS,
.this_id = 7,
.sg_tablesize = AHA1740_SCATTER,
- .cmd_per_lun = AHA1740_CMDLUN,
- .use_clustering = ENABLE_CLUSTERING,
.eh_abort_handler = aha1740_eh_abort_handler,
};
@@ -613,7 +600,6 @@ static int aha1740_probe (struct device *dev)
DMA_BIDIRECTIONAL);
if (!host->ecb_dma_addr) {
printk (KERN_ERR "aha1740_probe: Couldn't map ECB, giving up\n");
- scsi_unregister (shpnt);
goto err_host_put;
}
@@ -647,7 +633,7 @@ static int aha1740_probe (struct device *dev)
return -ENODEV;
}
-static __devexit int aha1740_remove (struct device *dev)
+static int aha1740_remove (struct device *dev)
{
struct Scsi_Host *shpnt = dev_get_drvdata(dev);
struct aha1740_hostdata *host = HOSTDATA (shpnt);
@@ -678,7 +664,7 @@ static struct eisa_driver aha1740_driver = {
.driver = {
.name = "aha1740",
.probe = aha1740_probe,
- .remove = __devexit_p (aha1740_remove),
+ .remove = aha1740_remove,
},
};
@@ -695,4 +681,5 @@ static __exit void aha1740_exit (void)
module_init (aha1740_init);
module_exit (aha1740_exit);
+MODULE_DESCRIPTION("Adaptec AHA1740 SCSI host adapter driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/aha1740.h b/drivers/scsi/aha1740.h
index af23fd6bd795..6eeed6da0b54 100644
--- a/drivers/scsi/aha1740.h
+++ b/drivers/scsi/aha1740.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _AHA1740_H
/* $Id$
@@ -134,8 +135,8 @@ struct ecb { /* Enhanced Control Block 6.1 */
/* Hardware defined portion ends here, rest is driver defined */
u8 sense[MAX_SENSE]; /* Sense area */
u8 status[MAX_STATUS]; /* Status area */
- Scsi_Cmnd *SCpnt; /* Link to the SCSI Command Block */
- void (*done) (Scsi_Cmnd *); /* Completion Function */
+ struct scsi_cmnd *SCpnt; /* Link to the SCSI Command Block */
+ void (*done) (struct scsi_cmnd *); /* Completion Function */
};
#define AHA1740CMD_NOP 0x00 /* No OP */
@@ -149,6 +150,5 @@ struct ecb { /* Enhanced Control Block 6.1 */
#define AHA1740_ECBS 32
#define AHA1740_SCATTER 16
-#define AHA1740_CMDLUN 1
#endif
diff --git a/drivers/scsi/aic7xxx/.gitignore b/drivers/scsi/aic7xxx/.gitignore
index b8ee24d5748a..9aa780221718 100644
--- a/drivers/scsi/aic7xxx/.gitignore
+++ b/drivers/scsi/aic7xxx/.gitignore
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
aic79xx_reg.h
aic79xx_reg_print.c
aic79xx_seq.h
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx
index 5e6620f8dabc..863f0932ef59 100644
--- a/drivers/scsi/aic7xxx/Kconfig.aic79xx
+++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx
@@ -1,86 +1,87 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# AIC79XX 2.5.X Kernel configuration File.
# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic79xx#4 $
#
config SCSI_AIC79XX
tristate "Adaptec AIC79xx U320 support"
- depends on PCI && SCSI
+ depends on PCI && HAS_IOPORT && SCSI
select SCSI_SPI_ATTRS
help
- This driver supports all of Adaptec's Ultra 320 PCI-X
- based SCSI controllers.
+ This driver supports all of Adaptec's Ultra 320 PCI-X
+ based SCSI controllers.
config AIC79XX_CMDS_PER_DEVICE
int "Maximum number of TCQ commands per device"
depends on SCSI_AIC79XX
default "32"
- ---help---
- Specify the number of commands you would like to allocate per SCSI
- device when Tagged Command Queueing (TCQ) is enabled on that device.
+ help
+ Specify the number of commands you would like to allocate per SCSI
+ device when Tagged Command Queueing (TCQ) is enabled on that device.
- This is an upper bound value for the number of tagged transactions
- to be used for any device. The aic7xxx driver will automatically
- vary this number based on device behavior. For devices with a
- fixed maximum, the driver will eventually lock to this maximum
- and display a console message indicating this value.
+ This is an upper bound value for the number of tagged transactions
+ to be used for any device. The aic7xxx driver will automatically
+ vary this number based on device behavior. For devices with a
+ fixed maximum, the driver will eventually lock to this maximum
+ and display a console message indicating this value.
- Due to resource allocation issues in the Linux SCSI mid-layer, using
- a high number of commands per device may result in memory allocation
- failures when many devices are attached to the system. For this reason,
- the default is set to 32. Higher values may result in higher performance
- on some devices. The upper bound is 253. 0 disables tagged queueing.
+ Due to resource allocation issues in the Linux SCSI mid-layer, using
+ a high number of commands per device may result in memory allocation
+ failures when many devices are attached to the system. For this
+ reason, the default is set to 32. Higher values may result in higher
+ performance on some devices. The upper bound is 253. 0 disables
+ tagged queueing.
- Per device tag depth can be controlled via the kernel command line
- "tag_info" option. See drivers/scsi/aic7xxx/README.aic79xx
- for details.
+ Per device tag depth can be controlled via the kernel command line
+ "tag_info" option. See Documentation/scsi/aic79xx.rst for details.
config AIC79XX_RESET_DELAY_MS
int "Initial bus reset delay in milli-seconds"
depends on SCSI_AIC79XX
default "5000"
- ---help---
- The number of milliseconds to delay after an initial bus reset.
- The bus settle delay following all error recovery actions is
- dictated by the SCSI layer and is not affected by this value.
+ help
+ The number of milliseconds to delay after an initial bus reset.
+ The bus settle delay following all error recovery actions is
+ dictated by the SCSI layer and is not affected by this value.
- Default: 5000 (5 seconds)
+ Default: 5000 (5 seconds)
config AIC79XX_BUILD_FIRMWARE
bool "Build Adapter Firmware with Kernel Build"
depends on SCSI_AIC79XX && !PREVENT_FIRMWARE_BUILD
help
- This option should only be enabled if you are modifying the firmware
- source to the aic79xx driver and wish to have the generated firmware
- include files updated during a normal kernel build. The assembler
- for the firmware requires lex and yacc or their equivalents, as well
- as the db v1 library. You may have to install additional packages
- or modify the assembler Makefile or the files it includes if your
- build environment is different than that of the author.
+ This option should only be enabled if you are modifying the firmware
+ source to the aic79xx driver and wish to have the generated firmware
+ include files updated during a normal kernel build. The assembler
+ for the firmware requires lex and yacc or their equivalents, as well
+ as the db v1 library. You may have to install additional packages
+ or modify the assembler Makefile or the files it includes if your
+ build environment is different than that of the author.
config AIC79XX_DEBUG_ENABLE
bool "Compile in Debugging Code"
depends on SCSI_AIC79XX
default y
help
- Compile in aic79xx debugging code that can be useful in diagnosing
- driver errors.
+ Compile in aic79xx debugging code that can be useful in diagnosing
+ driver errors.
config AIC79XX_DEBUG_MASK
int "Debug code enable mask (16383 for all debugging)"
depends on SCSI_AIC79XX
default "0"
help
- Bit mask of debug options that is only valid if the
- CONFIG_AIC79XX_DEBUG_ENBLE option is enabled. The bits in this mask
- are defined in the drivers/scsi/aic7xxx/aic79xx.h - search for the
- variable ahd_debug in that file to find them.
+ Bit mask of debug options that is only valid if the
+ CONFIG_AIC79XX_DEBUG_ENABLE option is enabled. The bits in this mask
+ are defined in the drivers/scsi/aic7xxx/aic79xx.h - search for the
+ variable ahd_debug in that file to find them.
config AIC79XX_REG_PRETTY_PRINT
bool "Decode registers during diagnostics"
depends on SCSI_AIC79XX
default y
help
- Compile in register value tables for the output of expanded register
- contents in diagnostics. This make it much easier to understand debug
- output without having to refer to a data book and/or the aic7xxx.reg
- file.
+ Compile in register value tables for the output of expanded register
+ contents in diagnostics. This make it much easier to understand debug
+ output without having to refer to a data book and/or the aic7xxx.reg
+ file.
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
index 88da670a7915..8f87f2d8ba9f 100644
--- a/drivers/scsi/aic7xxx/Kconfig.aic7xxx
+++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
@@ -1,91 +1,92 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# AIC7XXX and AIC79XX 2.5.X Kernel configuration File.
# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic7xxx#7 $
#
config SCSI_AIC7XXX
- tristate "Adaptec AIC7xxx Fast -> U160 support (New Driver)"
- depends on (PCI || EISA) && SCSI
+ tristate "Adaptec AIC7xxx Fast -> U160 support"
+ depends on (PCI || EISA) && HAS_IOPORT && SCSI
select SCSI_SPI_ATTRS
- ---help---
- This driver supports all of Adaptec's Fast through Ultra 160 PCI
- based SCSI controllers as well as the aic7770 based EISA and VLB
- SCSI controllers (the 274x and 284x series). For AAA and ARO based
- configurations, only SCSI functionality is provided.
+ help
+ This driver supports all of Adaptec's Fast through Ultra 160 PCI
+ based SCSI controllers as well as the aic7770 based EISA and VLB
+ SCSI controllers (the 274x and 284x series). For AAA and ARO based
+ configurations, only SCSI functionality is provided.
- To compile this driver as a module, choose M here: the
- module will be called aic7xxx.
+ To compile this driver as a module, choose M here: the
+ module will be called aic7xxx.
config AIC7XXX_CMDS_PER_DEVICE
int "Maximum number of TCQ commands per device"
depends on SCSI_AIC7XXX
default "32"
- ---help---
- Specify the number of commands you would like to allocate per SCSI
- device when Tagged Command Queueing (TCQ) is enabled on that device.
+ help
+ Specify the number of commands you would like to allocate per SCSI
+ device when Tagged Command Queueing (TCQ) is enabled on that device.
- This is an upper bound value for the number of tagged transactions
- to be used for any device. The aic7xxx driver will automatically
- vary this number based on device behavior. For devices with a
- fixed maximum, the driver will eventually lock to this maximum
- and display a console message indicating this value.
+ This is an upper bound value for the number of tagged transactions
+ to be used for any device. The aic7xxx driver will automatically
+ vary this number based on device behavior. For devices with a
+ fixed maximum, the driver will eventually lock to this maximum
+ and display a console message indicating this value.
- Due to resource allocation issues in the Linux SCSI mid-layer, using
- a high number of commands per device may result in memory allocation
- failures when many devices are attached to the system. For this reason,
- the default is set to 32. Higher values may result in higher performance
- on some devices. The upper bound is 253. 0 disables tagged queueing.
+ Due to resource allocation issues in the Linux SCSI mid-layer, using
+ a high number of commands per device may result in memory allocation
+ failures when many devices are attached to the system. For this
+ reason, the default is set to 32. Higher values may result in higher
+ performance on some devices. The upper bound is 253. 0 disables tagged
+ queueing.
- Per device tag depth can be controlled via the kernel command line
- "tag_info" option. See drivers/scsi/aic7xxx/README.aic7xxx
- for details.
+ Per device tag depth can be controlled via the kernel command line
+ "tag_info" option. See Documentation/scsi/aic7xxx.rst for details.
config AIC7XXX_RESET_DELAY_MS
int "Initial bus reset delay in milli-seconds"
depends on SCSI_AIC7XXX
default "5000"
- ---help---
- The number of milliseconds to delay after an initial bus reset.
- The bus settle delay following all error recovery actions is
- dictated by the SCSI layer and is not affected by this value.
+ help
+ The number of milliseconds to delay after an initial bus reset.
+ The bus settle delay following all error recovery actions is
+ dictated by the SCSI layer and is not affected by this value.
- Default: 5000 (5 seconds)
+ Default: 5000 (5 seconds)
config AIC7XXX_BUILD_FIRMWARE
bool "Build Adapter Firmware with Kernel Build"
depends on SCSI_AIC7XXX && !PREVENT_FIRMWARE_BUILD
help
- This option should only be enabled if you are modifying the firmware
- source to the aic7xxx driver and wish to have the generated firmware
- include files updated during a normal kernel build. The assembler
- for the firmware requires lex and yacc or their equivalents, as well
- as the db v1 library. You may have to install additional packages
- or modify the assembler Makefile or the files it includes if your
- build environment is different than that of the author.
+ This option should only be enabled if you are modifying the firmware
+ source to the aic7xxx driver and wish to have the generated firmware
+ include files updated during a normal kernel build. The assembler
+ for the firmware requires lex and yacc or their equivalents, as well
+ as the db v1 library. You may have to install additional packages
+ or modify the assembler Makefile or the files it includes if your
+ build environment is different than that of the author.
config AIC7XXX_DEBUG_ENABLE
bool "Compile in Debugging Code"
depends on SCSI_AIC7XXX
default y
help
- Compile in aic7xxx debugging code that can be useful in diagnosing
- driver errors.
+ Compile in aic7xxx debugging code that can be useful in diagnosing
+ driver errors.
config AIC7XXX_DEBUG_MASK
- int "Debug code enable mask (2047 for all debugging)"
- depends on SCSI_AIC7XXX
- default "0"
- help
- Bit mask of debug options that is only valid if the
- CONFIG_AIC7XXX_DEBUG_ENABLE option is enabled. The bits in this mask
- are defined in the drivers/scsi/aic7xxx/aic7xxx.h - search for the
- variable ahc_debug in that file to find them.
+ int "Debug code enable mask (2047 for all debugging)"
+ depends on SCSI_AIC7XXX
+ default "0"
+ help
+ Bit mask of debug options that is only valid if the
+ CONFIG_AIC7XXX_DEBUG_ENABLE option is enabled. The bits in this mask
+ are defined in the drivers/scsi/aic7xxx/aic7xxx.h - search for the
+ variable ahc_debug in that file to find them.
config AIC7XXX_REG_PRETTY_PRINT
- bool "Decode registers during diagnostics"
- depends on SCSI_AIC7XXX
+ bool "Decode registers during diagnostics"
+ depends on SCSI_AIC7XXX
default y
- help
- Compile in register value tables for the output of expanded register
- contents in diagnostics. This make it much easier to understand debug
- output without having to refer to a data book and/or the aic7xxx.reg
- file.
+ help
+ Compile in register value tables for the output of expanded register
+ contents in diagnostics. This make it much easier to understand debug
+ output without having to refer to a data book and/or the aic7xxx.reg
+ file.
diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile
index 741d81861d17..853c72a81ae0 100644
--- a/drivers/scsi/aic7xxx/Makefile
+++ b/drivers/scsi/aic7xxx/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Linux aic7xxx SCSI driver.
#
@@ -33,7 +34,6 @@ aic79xx-y += aic79xx_osm.o \
aic79xx_proc.o \
aic79xx_osm_pci.o
-ccflags-y += -Idrivers/scsi
ifdef WARNINGS_BECOME_ERRORS
ccflags-y += -Werror
endif
@@ -55,11 +55,12 @@ aicasm-7xxx-opts-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) := \
ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y)
$(obj)/aic7xxx_seq.h: $(src)/aic7xxx.seq $(src)/aic7xxx.reg $(obj)/aicasm/aicasm
- $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic7xxx_reg.h \
+ $(obj)/aicasm/aicasm -I $(src) -r $(obj)/aic7xxx_reg.h \
$(aicasm-7xxx-opts-y) -o $(obj)/aic7xxx_seq.h \
$(src)/aic7xxx.seq
-$(aic7xxx-gen-y): $(obj)/aic7xxx_seq.h
+$(aic7xxx-gen-y): $(objtree)/$(obj)/aic7xxx_seq.h
+ @true
else
$(obj)/aic7xxx_reg_print.c: $(src)/aic7xxx_reg_print.c_shipped
endif
@@ -72,14 +73,15 @@ aicasm-79xx-opts-$(CONFIG_AIC79XX_REG_PRETTY_PRINT) := \
ifeq ($(CONFIG_AIC79XX_BUILD_FIRMWARE),y)
$(obj)/aic79xx_seq.h: $(src)/aic79xx.seq $(src)/aic79xx.reg $(obj)/aicasm/aicasm
- $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic79xx_reg.h \
+ $(obj)/aicasm/aicasm -I $(src) -r $(obj)/aic79xx_reg.h \
$(aicasm-79xx-opts-y) -o $(obj)/aic79xx_seq.h \
$(src)/aic79xx.seq
-$(aic79xx-gen-y): $(obj)/aic79xx_seq.h
+$(aic79xx-gen-y): $(objtree)/$(obj)/aic79xx_seq.h
+ @true
else
$(obj)/aic79xx_reg_print.c: $(src)/aic79xx_reg_print.c_shipped
endif
$(obj)/aicasm/aicasm: $(src)/aicasm/*.[chyl]
- $(MAKE) -C $(src)/aicasm
+ $(MAKE) -C $(src)/aicasm OUTDIR=$(shell pwd)/$(obj)/aicasm/
diff --git a/drivers/scsi/aic7xxx/aic7770.c b/drivers/scsi/aic7xxx/aic7770.c
index 5000bd69c13f..f1ce02cd569e 100644
--- a/drivers/scsi/aic7xxx/aic7770.c
+++ b/drivers/scsi/aic7xxx/aic7770.c
@@ -42,15 +42,9 @@
* $FreeBSD$
*/
-#ifdef __linux__
#include "aic7xxx_osm.h"
#include "aic7xxx_inline.h"
#include "aic7xxx_93cx6.h"
-#else
-#include <dev/aic7xxx/aic7xxx_osm.h>
-#include <dev/aic7xxx/aic7xxx_inline.h>
-#include <dev/aic7xxx/aic7xxx_93cx6.h>
-#endif
#define ID_AIC7770 0x04907770
#define ID_AHA_274x 0x04907771
@@ -105,21 +99,6 @@ struct aic7770_identity aic7770_ident_table[] =
ahc_aic7770_EISA_setup
}
};
-const int ahc_num_aic7770_devs = ARRAY_SIZE(aic7770_ident_table);
-
-struct aic7770_identity *
-aic7770_find_device(uint32_t id)
-{
- struct aic7770_identity *entry;
- int i;
-
- for (i = 0; i < ahc_num_aic7770_devs; i++) {
- entry = &aic7770_ident_table[i];
- if (entry->full_id == (id & entry->id_mask))
- return (entry);
- }
- return (NULL);
-}
int
aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io)
diff --git a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c
index 0cb8ef64b5ce..a19cdd87c453 100644
--- a/drivers/scsi/aic7xxx/aic7770_osm.c
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -85,19 +85,19 @@ aic7770_probe(struct device *dev)
int error;
sprintf(buf, "ahc_eisa:%d", eisaBase >> 12);
- name = kmalloc(strlen(buf) + 1, GFP_ATOMIC);
+ name = kstrdup(buf, GFP_ATOMIC);
if (name == NULL)
- return (ENOMEM);
- strcpy(name, buf);
+ return -ENOMEM;
ahc = ahc_alloc(&aic7xxx_driver_template, name);
if (ahc == NULL)
- return (ENOMEM);
+ return -ENOMEM;
+ ahc->dev = dev;
error = aic7770_config(ahc, aic7770_ident_table + edev->id.driver_data,
eisaBase);
if (error != 0) {
ahc->bsh.ioport = 0;
ahc_free(ahc);
- return (error);
+ return error < 0 ? error : -error;
}
dev_set_drvdata(dev, ahc);
diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h
index 9b059422aacb..59e9321815c8 100644
--- a/drivers/scsi/aic7xxx/aic79xx.h
+++ b/drivers/scsi/aic7xxx/aic79xx.h
@@ -115,7 +115,7 @@ struct scb_platform_data;
#endif
#define AHD_BUILD_COL_IDX(target, lun) \
- (((lun) << 4) | target)
+ ((((u8)lun) << 4) | target)
#define AHD_GET_SCB_COL_IDX(ahd, scb) \
((SCB_GET_LUN(scb) << 4) | SCB_GET_TARGET(ahd, scb))
@@ -211,7 +211,7 @@ typedef enum {
*/
typedef enum {
AHD_FENONE = 0x00000,
- AHD_WIDE = 0x00001,/* Wide Channel */
+ AHD_WIDE = 0x00001,/* Wide Channel */
AHD_AIC79XXB_SLOWCRC = 0x00002,/* SLOWCRC bit should be set */
AHD_MULTI_FUNC = 0x00100,/* Multi-Function/Channel Device */
AHD_TARGETMODE = 0x01000,/* Has tested target mode support */
@@ -433,7 +433,7 @@ union initiator_data {
* Target mode version of the shared data SCB segment.
*/
struct target_data {
- uint32_t spare[2];
+ uint32_t spare[2];
uint8_t scsi_status; /* SCSI status to give to initiator */
uint8_t target_phases; /* Bitmap of phases to execute */
uint8_t data_phase; /* Data-In or Data-Out */
@@ -607,13 +607,10 @@ struct scb {
ahd_io_ctx_t io_ctx;
struct ahd_softc *ahd_softc;
scb_flag flags;
-#ifndef __linux__
- bus_dmamap_t dmamap;
-#endif
struct scb_platform_data *platform_data;
- struct map_node *hscb_map;
- struct map_node *sg_map;
- struct map_node *sense_map;
+ struct map_node *hscb_map;
+ struct map_node *sg_map;
+ struct map_node *sense_map;
void *sg_list;
uint8_t *sense_data;
dma_addr_t sg_list_busaddr;
@@ -624,7 +621,7 @@ struct scb {
};
TAILQ_HEAD(scb_tailq, scb);
-LIST_HEAD(scb_list, scb);
+BSD_LIST_HEAD(scb_list, scb);
struct scb_data {
/*
@@ -677,7 +674,7 @@ struct scb_data {
struct target_cmd {
uint8_t scsiid; /* Our ID and the initiator's ID */
uint8_t identify; /* Identify message */
- uint8_t bytes[22]; /*
+ uint8_t bytes[22]; /*
* Bytes contains any additional message
* bytes terminated by 0xFF. The remainder
* is the cdb to execute.
@@ -715,7 +712,7 @@ struct ahd_tmode_event {
* structure here so we can store arrays of them, etc. in OS neutral
* data structures.
*/
-#ifdef AHD_TARGET_MODE
+#ifdef AHD_TARGET_MODE
struct ahd_tmode_lstate {
struct cam_path *path;
struct ccb_hdr_slist accept_tios;
@@ -810,11 +807,11 @@ struct ahd_tmode_tstate {
/***************************** Lookup Tables **********************************/
/*
* Phase -> name and message out response
- * to parity errors in each phase table.
+ * to parity errors in each phase table.
*/
struct ahd_phase_table_entry {
- uint8_t phase;
- uint8_t mesg_out; /* Message response to parity errors */
+ uint8_t phase;
+ uint8_t mesg_out; /* Message response to parity errors */
const char *phasemsg;
};
@@ -847,7 +844,7 @@ struct seeprom_config {
#define CFBS_ENABLED 0x04
#define CFBS_DISABLED_SCAN 0x08
#define CFENABLEDV 0x0010 /* Perform Domain Validation */
-#define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */
+#define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */
#define CFSPARITY 0x0040 /* SCSI parity */
#define CFEXTEND 0x0080 /* extended translation enabled */
#define CFBOOTCD 0x0100 /* Support Bootable CD-ROM */
@@ -861,7 +858,7 @@ struct seeprom_config {
/*
* Host Adapter Control Bits
*/
- uint16_t adapter_control; /* word 17 */
+ uint16_t adapter_control; /* word 17 */
#define CFAUTOTERM 0x0001 /* Perform Auto termination */
#define CFSTERM 0x0002 /* SCSI low byte termination */
#define CFWSTERM 0x0004 /* SCSI high byte termination */
@@ -870,7 +867,7 @@ struct seeprom_config {
#define CFSEHIGHTERM 0x0020 /* Ultra2 secondary high term */
#define CFSTPWLEVEL 0x0040 /* Termination level control */
#define CFBIOSAUTOTERM 0x0080 /* Perform Auto termination */
-#define CFTERM_MENU 0x0100 /* BIOS displays termination menu */
+#define CFTERM_MENU 0x0100 /* BIOS displays termination menu */
#define CFCLUSTERENB 0x8000 /* Cluster Enable */
/*
@@ -884,7 +881,7 @@ struct seeprom_config {
/*
* Maximum targets
*/
- uint16_t max_targets; /* word 19 */
+ uint16_t max_targets; /* word 19 */
#define CFMAXTARG 0x00ff /* maximum targets */
#define CFBOOTLUN 0x0f00 /* Lun to boot from */
#define CFBOOTID 0xf000 /* Target to boot from */
@@ -911,7 +908,7 @@ struct vpd_config {
uint8_t length;
uint8_t revision;
uint8_t device_flags;
- uint8_t termnation_menus[2];
+ uint8_t termination_menus[2];
uint8_t fifo_threshold;
uint8_t end_tag;
uint8_t vpd_checksum;
@@ -944,7 +941,7 @@ struct vpd_config {
#define FLX_ROMSTAT_EE_2MBx8 0x2
#define FLX_ROMSTAT_EE_4MBx8 0x3
#define FLX_ROMSTAT_EE_16MBx8 0x4
-#define CURSENSE_ENB 0x1
+#define CURSENSE_ENB 0x1
#define FLXADDR_FLEXSTAT 0x2
#define FLX_FSTAT_BUSY 0x1
#define FLXADDR_CURRENT_STAT 0x4
@@ -1046,8 +1043,6 @@ typedef enum {
typedef uint8_t ahd_mode_state;
-typedef void ahd_callback_t (void *);
-
struct ahd_completion
{
uint16_t tag;
@@ -1056,11 +1051,8 @@ struct ahd_completion
};
struct ahd_softc {
- bus_space_tag_t tags[2];
- bus_space_handle_t bshs[2];
-#ifndef __linux__
- bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */
-#endif
+ bus_space_tag_t tags[2];
+ bus_space_handle_t bshs[2];
struct scb_data scb_data;
struct hardware_scb *next_queued_hscb;
@@ -1069,7 +1061,7 @@ struct ahd_softc {
/*
* SCBs that have been sent to the controller
*/
- LIST_HEAD(, scb) pending_scbs;
+ BSD_LIST_HEAD(, scb) pending_scbs;
/*
* Current register window mode information.
@@ -1122,8 +1114,7 @@ struct ahd_softc {
/*
* Timer handles for timer driven callbacks.
*/
- ahd_timer_t reset_timer;
- ahd_timer_t stat_timer;
+ struct timer_list stat_timer;
/*
* Statistics.
@@ -1184,7 +1175,7 @@ struct ahd_softc {
uint8_t tqinfifonext;
/*
- * Cached verson of the hs_mailbox so we can avoid
+ * Cached version of the hs_mailbox so we can avoid
* pausing the sequencer during mailbox updates.
*/
uint8_t hs_mailbox;
@@ -1252,7 +1243,7 @@ struct ahd_softc {
u_int int_coalescing_threshold;
u_int int_coalescing_stop_threshold;
- uint16_t user_discenable;/* Disconnection allowed */
+ uint16_t user_discenable;/* Disconnection allowed */
uint16_t user_tagenable;/* Tagged Queuing allowed */
};
@@ -1339,10 +1330,8 @@ const struct ahd_pci_identity *ahd_find_pci_device(ahd_dev_softc_t);
int ahd_pci_config(struct ahd_softc *,
const struct ahd_pci_identity *);
int ahd_pci_test_register_access(struct ahd_softc *);
-#ifdef CONFIG_PM
-void ahd_pci_suspend(struct ahd_softc *);
-void ahd_pci_resume(struct ahd_softc *);
-#endif
+void __maybe_unused ahd_pci_suspend(struct ahd_softc *);
+void __maybe_unused ahd_pci_resume(struct ahd_softc *);
/************************** SCB and SCB queue management **********************/
void ahd_qinfifo_requeue_tail(struct ahd_softc *ahd,
@@ -1353,10 +1342,8 @@ struct ahd_softc *ahd_alloc(void *platform_arg, char *name);
int ahd_softc_init(struct ahd_softc *);
void ahd_controller_info(struct ahd_softc *ahd, char *buf);
int ahd_init(struct ahd_softc *ahd);
-#ifdef CONFIG_PM
-int ahd_suspend(struct ahd_softc *ahd);
-void ahd_resume(struct ahd_softc *ahd);
-#endif
+int __maybe_unused ahd_suspend(struct ahd_softc *ahd);
+void __maybe_unused ahd_resume(struct ahd_softc *ahd);
int ahd_default_config(struct ahd_softc *ahd);
int ahd_parse_vpddata(struct ahd_softc *ahd,
struct vpd_config *vpd);
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index 5f8617dd43bb..6b87ea004e53 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -40,16 +40,9 @@
* $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#250 $
*/
-#ifdef __linux__
#include "aic79xx_osm.h"
#include "aic79xx_inline.h"
#include "aicasm/aicasm_insformat.h"
-#else
-#include <dev/aic7xxx/aic79xx_osm.h>
-#include <dev/aic7xxx/aic79xx_inline.h>
-#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
-#endif
-
/***************************** Lookup Tables **********************************/
static const char *const ahd_chip_names[] =
@@ -59,13 +52,12 @@ static const char *const ahd_chip_names[] =
"aic7902",
"aic7901A"
};
-static const u_int num_chip_names = ARRAY_SIZE(ahd_chip_names);
/*
* Hardware error codes.
*/
struct ahd_hard_error_entry {
- uint8_t errno;
+ uint8_t errno;
const char *errmesg;
};
@@ -81,16 +73,16 @@ static const u_int num_errors = ARRAY_SIZE(ahd_hard_errors);
static const struct ahd_phase_table_entry ahd_phase_table[] =
{
- { P_DATAOUT, MSG_NOOP, "in Data-out phase" },
- { P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
- { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
- { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
- { P_COMMAND, MSG_NOOP, "in Command phase" },
- { P_MESGOUT, MSG_NOOP, "in Message-out phase" },
- { P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
+ { P_DATAOUT, NOP, "in Data-out phase" },
+ { P_DATAIN, INITIATOR_ERROR, "in Data-in phase" },
+ { P_DATAOUT_DT, NOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, INITIATOR_ERROR, "in DT Data-in phase" },
+ { P_COMMAND, NOP, "in Command phase" },
+ { P_MESGOUT, NOP, "in Message-out phase" },
+ { P_STATUS, INITIATOR_ERROR, "in Status phase" },
{ P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
- { P_BUSFREE, MSG_NOOP, "while idle" },
- { 0, MSG_NOOP, "in unknown phase" }
+ { P_BUSFREE, NOP, "while idle" },
+ { 0, NOP, "in unknown phase" }
};
/*
@@ -121,7 +113,7 @@ static void ahd_free_tstate(struct ahd_softc *ahd,
u_int scsi_id, char channel, int force);
#endif
static void ahd_devlimited_syncrate(struct ahd_softc *ahd,
- struct ahd_initiator_tinfo *,
+ struct ahd_initiator_tinfo *,
u_int *period,
u_int *ppr_options,
role_t role);
@@ -178,7 +170,7 @@ static void ahd_setup_target_msgin(struct ahd_softc *ahd,
static u_int ahd_sglist_size(struct ahd_softc *ahd);
static u_int ahd_sglist_allocsize(struct ahd_softc *ahd);
static bus_dmamap_callback_t
- ahd_dmamap_cb;
+ ahd_dmamap_cb;
static void ahd_initialize_hscbs(struct ahd_softc *ahd);
static int ahd_init_scbdata(struct ahd_softc *ahd);
static void ahd_fini_scbdata(struct ahd_softc *ahd);
@@ -207,7 +199,7 @@ static void ahd_add_scb_to_free_list(struct ahd_softc *ahd,
static u_int ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
u_int prev, u_int next, u_int tid);
static void ahd_reset_current_bus(struct ahd_softc *ahd);
-static ahd_callback_t ahd_stat_timer;
+static void ahd_stat_timer(struct timer_list *t);
#ifdef AHD_DUMP_SEQ
static void ahd_dumpseq(struct ahd_softc *ahd);
#endif
@@ -276,7 +268,7 @@ static void ahd_run_tqinfifo(struct ahd_softc *ahd, int paused);
static void ahd_handle_hwerrint(struct ahd_softc *ahd);
static void ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat);
static void ahd_handle_scsiint(struct ahd_softc *ahd,
- u_int intstat);
+ u_int intstat);
/************************ Sequencer Execution Control *************************/
void
@@ -1134,7 +1126,7 @@ ahd_restart(struct ahd_softc *ahd)
/* No more pending messages */
ahd_clear_msg_state(ahd);
ahd_outb(ahd, SCSISIGO, 0); /* De-assert BSY */
- ahd_outb(ahd, MSG_OUT, MSG_NOOP); /* No message to send */
+ ahd_outb(ahd, MSG_OUT, NOP); /* No message to send */
ahd_outb(ahd, SXFRCTL1, ahd_inb(ahd, SXFRCTL1) & ~BITBUCKET);
ahd_outb(ahd, SEQINTCTL, 0);
ahd_outb(ahd, LASTPHASE, P_BUSFREE);
@@ -1211,7 +1203,7 @@ ahd_flush_qoutfifo(struct ahd_softc *ahd)
while ((ahd_inb(ahd, LQISTAT2) & LQIGSAVAIL) != 0) {
u_int fifo_mode;
u_int i;
-
+
scbid = ahd_inw(ahd, GSFIFO);
scb = ahd_lookup_scb(ahd, scbid);
if (scb == NULL) {
@@ -1298,7 +1290,7 @@ rescan_fifos:
/*
* Wait for any inprogress DMA to complete and clear DMA state
- * if this if for an SCB in the qinfifo.
+ * if this is for an SCB in the qinfifo.
*/
while (((ccscbctl = ahd_inb(ahd, CCSCBCTL)) & (CCARREN|CCSCBEN)) != 0) {
@@ -1334,7 +1326,7 @@ rescan_fifos:
while (!SCBID_IS_NULL(scbid)) {
uint8_t *hscb_ptr;
u_int i;
-
+
ahd_set_scbptr(ahd, scbid);
next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
scb = ahd_lookup_scb(ahd, scbid);
@@ -1743,10 +1735,8 @@ ahd_dump_sglist(struct scb *scb)
sg_list = (struct ahd_dma64_seg*)scb->sg_list;
for (i = 0; i < scb->sg_count; i++) {
uint64_t addr;
- uint32_t len;
addr = ahd_le64toh(sg_list[i].addr);
- len = ahd_le32toh(sg_list[i].len);
printk("sg[%d] - Addr 0x%x%x : Length %d%s\n",
i,
(uint32_t)((addr >> 32) & 0xFFFFFFFF),
@@ -1914,9 +1904,6 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
{
struct ahd_devinfo devinfo;
struct scb *scb;
- struct ahd_initiator_tinfo *targ_info;
- struct ahd_tmode_tstate *tstate;
- struct ahd_transinfo *tinfo;
u_int scbid;
/*
@@ -1944,12 +1931,6 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
SCB_GET_LUN(scb),
SCB_GET_CHANNEL(ahd, scb),
ROLE_INITIATOR);
- targ_info = ahd_fetch_transinfo(ahd,
- devinfo.channel,
- devinfo.our_scsiid,
- devinfo.target,
- &tstate);
- tinfo = &targ_info->curr;
ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
AHD_TRANS_ACTIVE, /*paused*/TRUE);
ahd_set_syncrate(ahd, &devinfo, /*period*/0,
@@ -2010,7 +1991,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
{
struct scb *scb;
u_int scb_index;
-
+
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
printk("%s: CFG4OVERRUN mode = %x\n", ahd_name(ahd),
@@ -2026,7 +2007,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
*/
ahd_assert_atn(ahd);
ahd_outb(ahd, MSG_OUT, HOST_MSG);
- ahd->msgout_buf[0] = MSG_ABORT_TASK;
+ ahd->msgout_buf[0] = ABORT_TASK;
ahd->msgout_len = 1;
ahd->msgout_index = 0;
ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
@@ -2113,8 +2094,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
ahd->msg_type =
MSG_TYPE_TARGET_MSGOUT;
ahd->msgin_index = 0;
- }
- else
+ } else
ahd_setup_target_msgin(ahd,
&devinfo,
scb);
@@ -2155,7 +2135,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
printk("SXFRCTL0 == 0x%x\n", ahd_inb(ahd, SXFRCTL0));
printk("SEQCTL0 == 0x%x\n", ahd_inb(ahd, SEQCTL0));
ahd_dump_card_state(ahd);
- ahd->msgout_buf[0] = MSG_BUS_DEV_RESET;
+ ahd->msgout_buf[0] = TARGET_RESET;
ahd->msgout_len = 1;
ahd->msgout_index = 0;
ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
@@ -2293,6 +2273,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
switch (scb->hscb->task_management) {
case SIU_TASKMGMT_ABORT_TASK:
tag = SCB_GET_TAG(scb);
+ fallthrough;
case SIU_TASKMGMT_ABORT_TASK_SET:
case SIU_TASKMGMT_CLEAR_TASK_SET:
lun = scb->hscb->lun;
@@ -2303,6 +2284,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
break;
case SIU_TASKMGMT_LUN_RESET:
lun = scb->hscb->lun;
+ fallthrough;
case SIU_TASKMGMT_TARGET_RESET:
{
struct ahd_devinfo devinfo;
@@ -2355,9 +2337,9 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
;
ahd_outb(ahd, SCB_TASK_MANAGEMENT, 0);
ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
- SCB_GET_CHANNEL(ahd, scb),
- SCB_GET_LUN(scb), SCB_GET_TAG(scb),
- ROLE_INITIATOR, /*status*/0,
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+ ROLE_INITIATOR, /*status*/0,
SEARCH_REMOVE);
}
break;
@@ -2675,7 +2657,6 @@ ahd_handle_transmission_error(struct ahd_softc *ahd)
struct scb *scb;
u_int scbid;
u_int lqistat1;
- u_int lqistat2;
u_int msg_out;
u_int curphase;
u_int lastphase;
@@ -2686,7 +2667,7 @@ ahd_handle_transmission_error(struct ahd_softc *ahd)
scb = NULL;
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
lqistat1 = ahd_inb(ahd, LQISTAT1) & ~(LQIPHASE_LQ|LQIPHASE_NLQ);
- lqistat2 = ahd_inb(ahd, LQISTAT2);
+ ahd_inb(ahd, LQISTAT2);
if ((lqistat1 & (LQICRCI_NLQ|LQICRCI_LQ)) == 0
&& (ahd->bugs & AHD_NLQICRC_DELAYED_BUG) != 0) {
u_int lqistate;
@@ -2710,16 +2691,16 @@ ahd_handle_transmission_error(struct ahd_softc *ahd)
lastphase = ahd_inb(ahd, LASTPHASE);
curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
perrdiag = ahd_inb(ahd, PERRDIAG);
- msg_out = MSG_INITIATOR_DET_ERR;
+ msg_out = INITIATOR_ERROR;
ahd_outb(ahd, CLRSINT1, CLRSCSIPERR);
-
+
/*
* Try to find the SCB associated with this error.
*/
silent = FALSE;
if (lqistat1 == 0
|| (lqistat1 & LQICRCI_NLQ) != 0) {
- if ((lqistat1 & (LQICRCI_NLQ|LQIOVERI_NLQ)) != 0)
+ if ((lqistat1 & (LQICRCI_NLQ|LQIOVERI_NLQ)) != 0)
ahd_set_active_fifo(ahd);
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
@@ -2836,20 +2817,20 @@ ahd_handle_transmission_error(struct ahd_softc *ahd)
ahd_lookup_phase_entry(curphase)->phasemsg);
ahd_inb(ahd, SCSIDAT);
}
-
+
if (curphase == P_MESGIN)
msg_out = MSG_PARITY_ERROR;
}
/*
- * We've set the hardware to assert ATN if we
+ * We've set the hardware to assert ATN if we
* get a parity error on "in" phases, so all we
* need to do is stuff the message buffer with
* the appropriate message. "In" phases have set
- * mesg_out to something other than MSG_NOP.
+ * mesg_out to something other than NOP.
*/
ahd->send_msg_perror = msg_out;
- if (scb != NULL && msg_out == MSG_INITIATOR_DET_ERR)
+ if (scb != NULL && msg_out == INITIATOR_ERROR)
scb->flags |= SCB_TRANSMISSION_ERROR;
ahd_outb(ahd, MSG_OUT, HOST_MSG);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
@@ -2888,7 +2869,7 @@ ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
ahd_outb(ahd, CLRINT, CLRSCSIINT);
ahd_unpause(ahd);
} else {
- printk("Reseting Channel for LQI Phase error\n");
+ printk("Resetting Channel for LQI Phase error\n");
ahd_dump_card_state(ahd);
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
}
@@ -3069,8 +3050,8 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
u_int tag;
tag = SCB_LIST_NULL;
- if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT_TAG, TRUE)
- || ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT, TRUE)) {
+ if (ahd_sent_msg(ahd, AHDMSG_1B, ABORT_TASK, TRUE)
+ || ahd_sent_msg(ahd, AHDMSG_1B, ABORT_TASK_SET, TRUE)) {
int found;
int sent_msg;
@@ -3085,9 +3066,9 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
ahd_print_path(ahd, scb);
printk("SCB %d - Abort%s Completed.\n",
SCB_GET_TAG(scb),
- sent_msg == MSG_ABORT_TAG ? "" : " Tag");
+ sent_msg == ABORT_TASK ? "" : " Tag");
- if (sent_msg == MSG_ABORT_TAG)
+ if (sent_msg == ABORT_TASK)
tag = SCB_GET_TAG(scb);
if ((scb->flags & SCB_EXTERNAL_RESET) != 0) {
@@ -3112,25 +3093,12 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
printk("found == 0x%x\n", found);
printerror = 0;
} else if (ahd_sent_msg(ahd, AHDMSG_1B,
- MSG_BUS_DEV_RESET, TRUE)) {
-#ifdef __FreeBSD__
- /*
- * Don't mark the user's request for this BDR
- * as completing with CAM_BDR_SENT. CAM3
- * specifies CAM_REQ_CMP.
- */
- if (scb != NULL
- && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
- && ahd_match_scb(ahd, scb, target, 'A',
- CAM_LUN_WILDCARD, SCB_LIST_NULL,
- ROLE_INITIATOR))
- ahd_set_transaction_status(scb, CAM_REQ_CMP);
-#endif
+ TARGET_RESET, TRUE)) {
ahd_handle_devreset(ahd, &devinfo, CAM_LUN_WILDCARD,
CAM_BDR_SENT, "Bus Device Reset",
/*verbose_level*/0);
printerror = 0;
- } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, FALSE)
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_PPR, FALSE)
&& ppr_busfree == 0) {
struct ahd_initiator_tinfo *tinfo;
struct ahd_tmode_tstate *tstate;
@@ -3183,7 +3151,7 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
}
printerror = 0;
}
- } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, FALSE)
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_WDTR, FALSE)
&& ppr_busfree == 0) {
/*
* Negotiation Rejected. Go-narrow and
@@ -3208,7 +3176,7 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
ahd_qinfifo_requeue_tail(ahd, scb);
}
printerror = 0;
- } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, FALSE)
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_SDTR, FALSE)
&& ppr_busfree == 0) {
/*
* Negotiation Rejected. Go-async and
@@ -3236,7 +3204,7 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
printerror = 0;
} else if ((ahd->msg_flags & MSG_FLAG_EXPECT_IDE_BUSFREE) != 0
&& ahd_sent_msg(ahd, AHDMSG_1B,
- MSG_INITIATOR_DET_ERR, TRUE)) {
+ INITIATOR_ERROR, TRUE)) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
@@ -3245,7 +3213,7 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
printerror = 0;
} else if ((ahd->msg_flags & MSG_FLAG_EXPECT_QASREJ_BUSFREE)
&& ahd_sent_msg(ahd, AHDMSG_1B,
- MSG_MESSAGE_REJECT, TRUE)) {
+ MESSAGE_REJECT, TRUE)) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
@@ -3399,7 +3367,7 @@ proto_violation_reset:
ahd_outb(ahd, MSG_OUT, HOST_MSG);
if (scb == NULL) {
ahd_print_devinfo(ahd, &devinfo);
- ahd->msgout_buf[0] = MSG_ABORT_TASK;
+ ahd->msgout_buf[0] = ABORT_TASK;
ahd->msgout_len = 1;
ahd->msgout_index = 0;
ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
@@ -3477,7 +3445,6 @@ ahd_clear_critical_section(struct ahd_softc *ahd)
cs = ahd->critical_sections;
for (i = 0; i < ahd->num_critical_sections; i++, cs++) {
-
if (cs->begin < seqaddr && cs->end >= seqaddr)
break;
}
@@ -3503,8 +3470,8 @@ ahd_clear_critical_section(struct ahd_softc *ahd)
if (stepping == FALSE) {
first_instr = seqaddr;
- ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
- simode0 = ahd_inb(ahd, SIMODE0);
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ simode0 = ahd_inb(ahd, SIMODE0);
simode3 = ahd_inb(ahd, SIMODE3);
lqimode0 = ahd_inb(ahd, LQIMODE0);
lqimode1 = ahd_inb(ahd, LQIMODE1);
@@ -3546,7 +3513,7 @@ ahd_clear_critical_section(struct ahd_softc *ahd)
ahd_outb(ahd, LQOMODE1, lqomode1);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
- ahd_outb(ahd, SIMODE1, simode1);
+ ahd_outb(ahd, SIMODE1, simode1);
/*
* SCSIINT seems to glitch occasionally when
* the interrupt masks are restored. Clear SCSIINT
@@ -3584,7 +3551,7 @@ ahd_clear_intstat(struct ahd_softc *ahd)
ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
|CLRBUSFREE|CLRSCSIPERR|CLRREQINIT);
ahd_outb(ahd, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO
- |CLRIOERR|CLROVERRUN);
+ |CLRIOERR|CLROVERRUN);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
}
@@ -3681,8 +3648,7 @@ ahd_free_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel, int force)
return;
tstate = ahd->enabled_targets[scsi_id];
- if (tstate != NULL)
- kfree(tstate);
+ kfree(tstate);
ahd->enabled_targets[scsi_id] = NULL;
}
#endif
@@ -3721,7 +3687,7 @@ ahd_devlimited_syncrate(struct ahd_softc *ahd,
*/
if (role == ROLE_TARGET)
transinfo = &tinfo->user;
- else
+ else
transinfo = &tinfo->goal;
*ppr_options &= (transinfo->ppr_options|MSG_EXT_PPR_PCOMP_EN);
if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
@@ -3752,7 +3718,7 @@ ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
if ((*ppr_options & MSG_EXT_PPR_DT_REQ) != 0
&& *period > AHD_SYNCRATE_MIN_DT)
*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
-
+
if (*period > AHD_SYNCRATE_MIN)
*period = 0;
@@ -3823,7 +3789,7 @@ ahd_validate_width(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo,
*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
break;
}
- /* FALLTHROUGH */
+ fallthrough;
case MSG_EXT_WDTR_BUS_8_BIT:
*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
break;
@@ -4115,7 +4081,7 @@ ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
ahd_outb(ahd, NEGOADDR, devinfo->target);
period = tinfo->period;
offset = tinfo->offset;
- memcpy(iocell_opts, ahd->iocell_opts, sizeof(ahd->iocell_opts));
+ memcpy(iocell_opts, ahd->iocell_opts, sizeof(ahd->iocell_opts));
ppr_opts = tinfo->ppr_options & (MSG_EXT_PPR_QAS_REQ|MSG_EXT_PPR_DT_REQ
|MSG_EXT_PPR_IU_REQ|MSG_EXT_PPR_RTI);
con_opts = 0;
@@ -4238,13 +4204,11 @@ ahd_update_pending_scbs(struct ahd_softc *ahd)
pending_scb_count = 0;
LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
struct ahd_devinfo devinfo;
- struct ahd_initiator_tinfo *tinfo;
struct ahd_tmode_tstate *tstate;
ahd_scb_devinfo(ahd, &devinfo, pending_scb);
- tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
- devinfo.our_scsiid,
- devinfo.target, &tstate);
+ ahd_fetch_transinfo(ahd, devinfo.channel, devinfo.our_scsiid,
+ devinfo.target, &tstate);
if ((tstate->auto_negotiate & devinfo.target_mask) == 0
&& (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
@@ -4425,7 +4389,7 @@ ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
} else if (scb == NULL) {
printk("%s: WARNING. No pending message for "
"I_T msgin. Issuing NO-OP\n", ahd_name(ahd));
- ahd->msgout_buf[ahd->msgout_index++] = MSG_NOOP;
+ ahd->msgout_buf[ahd->msgout_index++] = NOP;
ahd->msgout_len++;
ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
return;
@@ -4451,7 +4415,7 @@ ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
}
if (scb->flags & SCB_DEVICE_RESET) {
- ahd->msgout_buf[ahd->msgout_index++] = MSG_BUS_DEV_RESET;
+ ahd->msgout_buf[ahd->msgout_index++] = TARGET_RESET;
ahd->msgout_len++;
ahd_print_path(ahd, scb);
printk("Bus Device Reset Message Sent\n");
@@ -4466,9 +4430,9 @@ ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
} else if ((scb->flags & SCB_ABORT) != 0) {
if ((scb->hscb->control & TAG_ENB) != 0) {
- ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT_TAG;
+ ahd->msgout_buf[ahd->msgout_index++] = ABORT_TASK;
} else {
- ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT;
+ ahd->msgout_buf[ahd->msgout_index++] = ABORT_TASK_SET;
}
ahd->msgout_len++;
ahd_print_path(ahd, scb);
@@ -4700,7 +4664,7 @@ ahd_clear_msg_state(struct ahd_softc *ahd)
*/
ahd_outb(ahd, CLRSINT1, CLRATNO);
}
- ahd_outb(ahd, MSG_OUT, MSG_NOOP);
+ ahd_outb(ahd, MSG_OUT, NOP);
ahd_outb(ahd, SEQ_FLAGS2,
ahd_inb(ahd, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
ahd_restore_modes(ahd, saved_modes);
@@ -4781,7 +4745,7 @@ reswitch:
* with a busfree.
*/
if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0
- && ahd->send_msg_perror == MSG_INITIATOR_DET_ERR)
+ && ahd->send_msg_perror == INITIATOR_ERROR)
ahd->msg_flags |= MSG_FLAG_EXPECT_IDE_BUSFREE;
ahd_outb(ahd, RETURN_2, ahd->send_msg_perror);
@@ -4883,7 +4847,7 @@ reswitch:
#endif
ahd_assert_atn(ahd);
}
- } else
+ } else
ahd->msgin_index++;
if (message_done == MSGLOOP_TERMINATED) {
@@ -4986,7 +4950,7 @@ reswitch:
*/
return;
}
-
+
ahd->msgin_index++;
/*
@@ -5059,7 +5023,7 @@ ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full)
index = 0;
while (index < ahd->msgout_len) {
- if (ahd->msgout_buf[index] == MSG_EXTENDED) {
+ if (ahd->msgout_buf[index] == EXTENDED_MESSAGE) {
u_int end_index;
end_index = index + 1 + ahd->msgout_buf[index + 1];
@@ -5073,8 +5037,8 @@ ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full)
found = TRUE;
}
index = end_index;
- } else if (ahd->msgout_buf[index] >= MSG_SIMPLE_TASK
- && ahd->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+ } else if (ahd->msgout_buf[index] >= SIMPLE_QUEUE_TAG
+ && ahd->msgout_buf[index] <= IGNORE_WIDE_RESIDUE) {
/* Skip tag type and tag id or residue param*/
index += 2;
@@ -5125,36 +5089,36 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
* extended message type.
*/
switch (ahd->msgin_buf[0]) {
- case MSG_DISCONNECT:
- case MSG_SAVEDATAPOINTER:
- case MSG_CMDCOMPLETE:
- case MSG_RESTOREPOINTERS:
- case MSG_IGN_WIDE_RESIDUE:
+ case DISCONNECT:
+ case SAVE_POINTERS:
+ case COMMAND_COMPLETE:
+ case RESTORE_POINTERS:
+ case IGNORE_WIDE_RESIDUE:
/*
* End our message loop as these are messages
* the sequencer handles on its own.
*/
done = MSGLOOP_TERMINATED;
break;
- case MSG_MESSAGE_REJECT:
+ case MESSAGE_REJECT:
response = ahd_handle_msg_reject(ahd, devinfo);
- /* FALLTHROUGH */
- case MSG_NOOP:
+ fallthrough;
+ case NOP:
done = MSGLOOP_MSGCOMPLETE;
break;
- case MSG_EXTENDED:
+ case EXTENDED_MESSAGE:
{
/* Wait for enough of the message to begin validation */
if (ahd->msgin_index < 2)
break;
switch (ahd->msgin_buf[2]) {
- case MSG_EXT_SDTR:
+ case EXTENDED_SDTR:
{
u_int period;
u_int ppr_options;
u_int offset;
u_int saved_offset;
-
+
if (ahd->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
reject = TRUE;
break;
@@ -5196,7 +5160,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
* and didn't have to fall down to async
* transfers.
*/
- if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_SDTR, TRUE)) {
/* We started it */
if (saved_offset != offset) {
/* Went too low - force async */
@@ -5223,7 +5187,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
done = MSGLOOP_MSGCOMPLETE;
break;
}
- case MSG_EXT_WDTR:
+ case EXTENDED_WDTR:
{
u_int bus_width;
u_int saved_width;
@@ -5257,7 +5221,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
saved_width, bus_width);
}
- if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_WDTR, TRUE)) {
/*
* Don't send a WDTR back to the
* target, since we asked first.
@@ -5319,7 +5283,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
done = MSGLOOP_MSGCOMPLETE;
break;
}
- case MSG_EXT_PPR:
+ case EXTENDED_PPR:
{
u_int period;
u_int offset;
@@ -5374,7 +5338,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
ahd_validate_offset(ahd, tinfo, period, &offset,
bus_width, devinfo->role);
- if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, TRUE)) {
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_PPR, TRUE)) {
/*
* If we are unable to do any of the
* requested options (we went too low),
@@ -5437,7 +5401,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
break;
}
#ifdef AHD_TARGET_MODE
- case MSG_BUS_DEV_RESET:
+ case TARGET_RESET:
ahd_handle_devreset(ahd, devinfo, CAM_LUN_WILDCARD,
CAM_BDR_SENT,
"Bus Device Reset Received",
@@ -5445,9 +5409,9 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
ahd_restart(ahd);
done = MSGLOOP_TERMINATED;
break;
- case MSG_ABORT_TAG:
- case MSG_ABORT:
- case MSG_CLEAR_QUEUE:
+ case ABORT_TASK:
+ case ABORT_TASK_SET:
+ case CLEAR_TASK_SET:
{
int tag;
@@ -5457,7 +5421,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
break;
}
tag = SCB_LIST_NULL;
- if (ahd->msgin_buf[0] == MSG_ABORT_TAG)
+ if (ahd->msgin_buf[0] == ABORT_TASK)
tag = ahd_inb(ahd, INITIATOR_TAG);
ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
devinfo->lun, tag, ROLE_TARGET,
@@ -5481,15 +5445,15 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
break;
}
#endif
- case MSG_QAS_REQUEST:
+ case QAS_REQUEST:
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printk("%s: QAS request. SCSISIGI == 0x%x\n",
ahd_name(ahd), ahd_inb(ahd, SCSISIGI));
#endif
ahd->msg_flags |= MSG_FLAG_EXPECT_QASREJ_BUSFREE;
- /* FALLTHROUGH */
- case MSG_TERM_IO_PROC:
+ fallthrough;
+ case TERMINATE_IO_PROC:
default:
reject = TRUE;
break;
@@ -5501,7 +5465,7 @@ ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
*/
ahd->msgout_index = 0;
ahd->msgout_len = 1;
- ahd->msgout_buf[0] = MSG_MESSAGE_REJECT;
+ ahd->msgout_buf[0] = MESSAGE_REJECT;
done = MSGLOOP_MSGCOMPLETE;
response = TRUE;
}
@@ -5540,8 +5504,8 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
/* Might be necessary */
last_msg = ahd_inb(ahd, LAST_MSG);
- if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
- if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/TRUE)
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_PPR, /*full*/FALSE)) {
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_PPR, /*full*/TRUE)
&& tinfo->goal.period <= AHD_SYNCRATE_PACED) {
/*
* Target may not like our SPI-4 PPR Options.
@@ -5578,7 +5542,7 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
ahd_build_transfer_msg(ahd, devinfo);
ahd->msgout_index = 0;
response = 1;
- } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_WDTR, /*full*/FALSE)) {
/* note 8bit xfers */
printk("(%s:%c:%d:%d): refuses WIDE negotiation. Using "
@@ -5603,7 +5567,7 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
ahd->msgout_index = 0;
response = 1;
}
- } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_SDTR, /*full*/FALSE)) {
/* note asynch xfers and clear flag */
ahd_set_syncrate(ahd, devinfo, /*period*/0,
/*offset*/0, /*ppr_options*/0,
@@ -5613,13 +5577,13 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
"Using asynchronous transfers\n",
ahd_name(ahd), devinfo->channel,
devinfo->target, devinfo->lun);
- } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+ } else if ((scb->hscb->control & SIMPLE_QUEUE_TAG) != 0) {
int tag_type;
int mask;
- tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+ tag_type = (scb->hscb->control & SIMPLE_QUEUE_TAG);
- if (tag_type == MSG_SIMPLE_TASK) {
+ if (tag_type == SIMPLE_QUEUE_TAG) {
printk("(%s:%c:%d:%d): refuses tagged commands. "
"Performing non-tagged I/O\n", ahd_name(ahd),
devinfo->channel, devinfo->target, devinfo->lun);
@@ -5629,7 +5593,7 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
printk("(%s:%c:%d:%d): refuses %s tagged commands. "
"Performing simple queue tagged I/O only\n",
ahd_name(ahd), devinfo->channel, devinfo->target,
- devinfo->lun, tag_type == MSG_ORDERED_TASK
+ devinfo->lun, tag_type == ORDERED_QUEUE_TAG
? "ordered" : "head of queue");
ahd_set_tags(ahd, scb->io_ctx, devinfo, AHD_QUEUE_BASIC);
mask = ~0x03;
@@ -5641,9 +5605,9 @@ ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
*/
ahd_outb(ahd, SCB_CONTROL,
ahd_inb_scbram(ahd, SCB_CONTROL) & mask);
- scb->hscb->control &= mask;
+ scb->hscb->control &= mask;
ahd_set_transaction_tag(scb, /*enabled*/FALSE,
- /*type*/MSG_SIMPLE_TASK);
+ /*type*/SIMPLE_QUEUE_TAG);
ahd_outb(ahd, MSG_OUT, MSG_IDENTIFYFLAG);
ahd_assert_atn(ahd);
ahd_busy_tcl(ahd, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
@@ -5850,7 +5814,7 @@ ahd_reinitialize_dataptrs(struct ahd_softc *ahd)
AHD_ASSERT_MODES(ahd, AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK,
AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK);
-
+
scb_index = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scb_index);
@@ -5958,7 +5922,7 @@ ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
continue;
ahd_queue_lstate_event(ahd, lstate, devinfo->our_scsiid,
- MSG_BUS_DEV_RESET, /*arg*/0);
+ TARGET_RESET, /*arg*/0);
ahd_send_lstate_events(ahd, lstate);
}
}
@@ -5972,7 +5936,7 @@ ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
ahd_set_syncrate(ahd, devinfo, /*period*/0, /*offset*/0,
/*ppr_options*/0, AHD_TRANS_CUR,
/*paused*/TRUE);
-
+
if (status != CAM_SEL_TIMEOUT)
ahd_send_async(ahd, devinfo->channel, devinfo->target,
CAM_LUN_WILDCARD, AC_SENT_BDR);
@@ -5988,11 +5952,11 @@ ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
struct scb *scb)
{
- /*
+ /*
* To facilitate adding multiple messages together,
* each routine should increment the index and len
* variables instead of setting them explicitly.
- */
+ */
ahd->msgout_index = 0;
ahd->msgout_len = 0;
@@ -6073,22 +6037,16 @@ ahd_alloc(void *platform_arg, char *name)
{
struct ahd_softc *ahd;
-#ifndef __FreeBSD__
- ahd = kmalloc(sizeof(*ahd), GFP_ATOMIC);
+ ahd = kzalloc(sizeof(*ahd), GFP_ATOMIC);
if (!ahd) {
printk("aic7xxx: cannot malloc softc!\n");
kfree(name);
return NULL;
}
-#else
- ahd = device_get_softc((device_t)platform_arg);
-#endif
- memset(ahd, 0, sizeof(*ahd));
+
ahd->seep_config = kmalloc(sizeof(*ahd->seep_config), GFP_ATOMIC);
if (ahd->seep_config == NULL) {
-#ifndef __FreeBSD__
kfree(ahd);
-#endif
kfree(name);
return (NULL);
}
@@ -6104,8 +6062,7 @@ ahd_alloc(void *platform_arg, char *name)
ahd->bugs = AHD_BUGNONE;
ahd->flags = AHD_SPCHK_ENB_A|AHD_RESET_BUS_A|AHD_TERM_ENB_A
| AHD_EXTENDED_TRANS_A|AHD_STPWLEVEL_A;
- ahd_timer_init(&ahd->reset_timer);
- ahd_timer_init(&ahd->stat_timer);
+ timer_setup(&ahd->stat_timer, ahd_stat_timer, 0);
ahd->int_coalescing_timer = AHD_INT_COALESCING_TIMER_DEFAULT;
ahd->int_coalescing_maxcmds = AHD_INT_COALESCING_MAXCMDS_DEFAULT;
ahd->int_coalescing_mincmds = AHD_INT_COALESCING_MINCMDS_DEFAULT;
@@ -6113,10 +6070,6 @@ ahd_alloc(void *platform_arg, char *name)
ahd->int_coalescing_stop_threshold =
AHD_INT_COALESCING_STOP_THRESHOLD_DEFAULT;
- if (ahd_platform_alloc(ahd, platform_arg) != 0) {
- ahd_free(ahd);
- ahd = NULL;
- }
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MEMORY) != 0) {
printk("%s: scb size = 0x%x, hscb size = 0x%x\n",
@@ -6124,6 +6077,10 @@ ahd_alloc(void *platform_arg, char *name)
(u_int)sizeof(struct hardware_scb));
}
#endif
+ if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+ ahd_free(ahd);
+ ahd = NULL;
+ }
return (ahd);
}
@@ -6132,7 +6089,7 @@ ahd_softc_init(struct ahd_softc *ahd)
{
ahd->unpause = 0;
- ahd->pause = PAUSE;
+ ahd->pause = PAUSE;
return (0);
}
@@ -6145,8 +6102,7 @@ ahd_set_unit(struct ahd_softc *ahd, int unit)
void
ahd_set_name(struct ahd_softc *ahd, char *name)
{
- if (ahd->name != NULL)
- kfree(ahd->name);
+ kfree(ahd->name);
ahd->name = name;
}
@@ -6159,31 +6115,26 @@ ahd_free(struct ahd_softc *ahd)
default:
case 5:
ahd_shutdown(ahd);
- /* FALLTHROUGH */
+ fallthrough;
case 4:
ahd_dmamap_unload(ahd, ahd->shared_data_dmat,
ahd->shared_data_map.dmamap);
- /* FALLTHROUGH */
+ fallthrough;
case 3:
ahd_dmamem_free(ahd, ahd->shared_data_dmat, ahd->qoutfifo,
ahd->shared_data_map.dmamap);
ahd_dmamap_destroy(ahd, ahd->shared_data_dmat,
ahd->shared_data_map.dmamap);
- /* FALLTHROUGH */
+ fallthrough;
case 2:
ahd_dma_tag_destroy(ahd, ahd->shared_data_dmat);
+ break;
case 1:
-#ifndef __linux__
- ahd_dma_tag_destroy(ahd, ahd->buffer_dmat);
-#endif
break;
case 0:
break;
}
-#ifndef __linux__
- ahd_dma_tag_destroy(ahd, ahd->parent_dmat);
-#endif
ahd_platform_free(ahd);
ahd_fini_scbdata(ahd);
for (i = 0; i < AHD_NUM_TARGETS; i++) {
@@ -6213,15 +6164,10 @@ ahd_free(struct ahd_softc *ahd)
kfree(ahd->black_hole);
}
#endif
- if (ahd->name != NULL)
- kfree(ahd->name);
- if (ahd->seep_config != NULL)
- kfree(ahd->seep_config);
- if (ahd->saved_stack != NULL)
- kfree(ahd->saved_stack);
-#ifndef __FreeBSD__
+ kfree(ahd->name);
+ kfree(ahd->seep_config);
+ kfree(ahd->saved_stack);
kfree(ahd);
-#endif
return;
}
@@ -6235,8 +6181,7 @@ ahd_shutdown(void *arg)
/*
* Stop periodic timer callbacks.
*/
- ahd_timer_stop(&ahd->reset_timer);
- ahd_timer_stop(&ahd->stat_timer);
+ timer_delete_sync(&ahd->stat_timer);
/* This will reset most registers to 0, but not all */
ahd_reset(ahd, /*reinit*/FALSE);
@@ -6257,7 +6202,7 @@ ahd_reset(struct ahd_softc *ahd, int reinit)
u_int sxfrctl1;
int wait;
uint32_t cmd;
-
+
/*
* Preserve the value of the SXFRCTL1 register for all channels.
* It contains settings that affect termination and we don't want
@@ -6278,7 +6223,7 @@ ahd_reset(struct ahd_softc *ahd, int reinit)
* does not disable its parity logic prior to
* the start of the reset. This may cause a
* parity error to be detected and thus a
- * spurious SERR or PERR assertion. Disble
+ * spurious SERR or PERR assertion. Disable
* PERR and SERR responses during the CHIPRST.
*/
mod_cmd = cmd & ~(PCIM_CMD_PERRESPEN|PCIM_CMD_SERRESPEN);
@@ -6497,7 +6442,7 @@ ahd_init_scbdata(struct ahd_softc *ahd)
/*
* Note that we were successful
*/
- return (0);
+ return (0);
error_exit:
@@ -6566,8 +6511,8 @@ ahd_fini_scbdata(struct ahd_softc *ahd)
kfree(sns_map);
}
ahd_dma_tag_destroy(ahd, scb_data->sense_dmat);
- /* FALLTHROUGH */
}
+ fallthrough;
case 6:
{
struct map_node *sg_map;
@@ -6581,8 +6526,8 @@ ahd_fini_scbdata(struct ahd_softc *ahd)
kfree(sg_map);
}
ahd_dma_tag_destroy(ahd, scb_data->sg_dmat);
- /* FALLTHROUGH */
}
+ fallthrough;
case 5:
{
struct map_node *hscb_map;
@@ -6596,8 +6541,8 @@ ahd_fini_scbdata(struct ahd_softc *ahd)
kfree(hscb_map);
}
ahd_dma_tag_destroy(ahd, scb_data->hscb_dmat);
- /* FALLTHROUGH */
}
+ fallthrough;
case 4:
case 3:
case 2:
@@ -6936,9 +6881,6 @@ ahd_alloc_scbs(struct ahd_softc *ahd)
for (i = 0; i < newcount; i++) {
struct scb_platform_data *pdata;
u_int col_tag;
-#ifndef __linux__
- int error;
-#endif
next_scb = kmalloc(sizeof(*next_scb), GFP_ATOMIC);
if (next_scb == NULL)
@@ -6972,15 +6914,6 @@ ahd_alloc_scbs(struct ahd_softc *ahd)
next_scb->sg_list_busaddr += sizeof(struct ahd_dma_seg);
next_scb->ahd_softc = ahd;
next_scb->flags = SCB_FLAG_NONE;
-#ifndef __linux__
- error = ahd_dmamap_create(ahd, ahd->buffer_dmat, /*flags*/0,
- &next_scb->dmamap);
- if (error != 0) {
- kfree(next_scb);
- kfree(pdata);
- break;
- }
-#endif
next_scb->hscb->tag = ahd_htole16(scb_data->numscbs);
col_tag = scb_data->numscbs ^ 0x100;
next_scb->col_scb = ahd_find_scb_by_tag(ahd, col_tag);
@@ -7027,7 +6960,7 @@ ahd_controller_info(struct ahd_softc *ahd, char *buf)
static const char *channel_strings[] = {
"Primary Low",
"Primary High",
- "Secondary Low",
+ "Secondary Low",
"Secondary High"
};
@@ -7039,20 +6972,11 @@ static const char *termstat_strings[] = {
};
/***************************** Timer Facilities *******************************/
-#define ahd_timer_init init_timer
-#define ahd_timer_stop del_timer_sync
-typedef void ahd_linux_callback_t (u_long);
-
static void
-ahd_timer_reset(ahd_timer_t *timer, int usec, ahd_callback_t *func, void *arg)
+ahd_timer_reset(struct timer_list *timer, int usec)
{
- struct ahd_softc *ahd;
-
- ahd = (struct ahd_softc *)arg;
- del_timer(timer);
- timer->data = (u_long)arg;
+ timer_delete(timer);
timer->expires = jiffies + (usec * HZ)/1000000;
- timer->function = (ahd_linux_callback_t*)func;
add_timer(timer);
}
@@ -7074,7 +6998,8 @@ ahd_init(struct ahd_softc *ahd)
AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
ahd->stack_size = ahd_probe_stack_size(ahd);
- ahd->saved_stack = kmalloc(ahd->stack_size * sizeof(uint16_t), GFP_ATOMIC);
+ ahd->saved_stack = kmalloc_array(ahd->stack_size, sizeof(uint16_t),
+ GFP_ATOMIC);
if (ahd->saved_stack == NULL)
return (ENOMEM);
@@ -7101,24 +7026,6 @@ ahd_init(struct ahd_softc *ahd)
if ((AHD_TMODE_ENABLE & (0x1 << ahd->unit)) == 0)
ahd->features &= ~AHD_TARGETMODE;
-#ifndef __linux__
- /* DMA tag for mapping buffers into device visible space. */
- if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
- /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
- /*lowaddr*/ahd->flags & AHD_39BIT_ADDRESSING
- ? (dma_addr_t)0x7FFFFFFFFFULL
- : BUS_SPACE_MAXADDR_32BIT,
- /*highaddr*/BUS_SPACE_MAXADDR,
- /*filter*/NULL, /*filterarg*/NULL,
- /*maxsize*/(AHD_NSEG - 1) * PAGE_SIZE,
- /*nsegments*/AHD_NSEG,
- /*maxsegsz*/AHD_MAXTRANSFER_SIZE,
- /*flags*/BUS_DMA_ALLOCNOW,
- &ahd->buffer_dmat) != 0) {
- return (ENOMEM);
- }
-#endif
-
ahd->init_level++;
/*
@@ -7263,6 +7170,7 @@ ahd_init(struct ahd_softc *ahd)
case FLX_CSTAT_OVER:
case FLX_CSTAT_UNDER:
warn_user++;
+ fallthrough;
case FLX_CSTAT_INVALID:
case FLX_CSTAT_OKAY:
if (warn_user == 0 && bootverbose == 0)
@@ -7279,8 +7187,7 @@ ahd_init(struct ahd_softc *ahd)
}
init_done:
ahd_restart(ahd);
- ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
- ahd_stat_timer, ahd);
+ ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US);
return (0);
}
@@ -7325,7 +7232,7 @@ ahd_chip_init(struct ahd_softc *ahd)
} else {
sxfrctl1 |= ahd->seltime;
}
-
+
ahd_outb(ahd, SXFRCTL0, DFON);
ahd_outb(ahd, SXFRCTL1, sxfrctl1|ahd->seltime|ENSTIMER|ACTNEGEN);
ahd_outb(ahd, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
@@ -7581,7 +7488,7 @@ ahd_chip_init(struct ahd_softc *ahd)
ahd_outb(ahd, CMDSIZE_TABLE + 5, 11);
ahd_outb(ahd, CMDSIZE_TABLE + 6, 0);
ahd_outb(ahd, CMDSIZE_TABLE + 7, 0);
-
+
/* Tell the sequencer of our initial queue positions */
ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
ahd_outb(ahd, QOFF_CTLSTA, SCB_QSIZE_512);
@@ -7958,11 +7865,9 @@ ahd_pause_and_flushwork(struct ahd_softc *ahd)
ahd->flags &= ~AHD_ALL_INTERRUPTS;
}
-#ifdef CONFIG_PM
-int
+int __maybe_unused
ahd_suspend(struct ahd_softc *ahd)
{
-
ahd_pause_and_flushwork(ahd);
if (LIST_FIRST(&ahd->pending_scbs) != NULL) {
@@ -7973,15 +7878,13 @@ ahd_suspend(struct ahd_softc *ahd)
return (0);
}
-void
+void __maybe_unused
ahd_resume(struct ahd_softc *ahd)
{
-
ahd_reset(ahd, /*reinit*/TRUE);
- ahd_intr_enable(ahd, TRUE);
+ ahd_intr_enable(ahd, TRUE);
ahd_restart(ahd);
}
-#endif
/************************** Busy Target Table *********************************/
/*
@@ -8020,7 +7923,7 @@ ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl)
u_int scbid;
u_int scb_offset;
u_int saved_scbptr;
-
+
scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
scbid = ahd_inw_scbram(ahd, scb_offset);
ahd_set_scbptr(ahd, saved_scbptr);
@@ -8032,7 +7935,7 @@ ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl, u_int scbid)
{
u_int scb_offset;
u_int saved_scbptr;
-
+
scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
ahd_outw(ahd, scb_offset, scbid);
ahd_set_scbptr(ahd, saved_scbptr);
@@ -8085,7 +7988,7 @@ ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
target = SCB_GET_TARGET(ahd, scb);
lun = SCB_GET_LUN(scb);
channel = SCB_GET_CHANNEL(ahd, scb);
-
+
ahd_search_qinfifo(ahd, target, channel, lun,
/*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
CAM_REQUEUE_REQ, SEARCH_COMPLETE);
@@ -8126,7 +8029,7 @@ ahd_qinfifo_requeue(struct ahd_softc *ahd, struct scb *prev_scb,
ahd_outl(ahd, NEXT_QUEUED_SCB_ADDR, busaddr);
} else {
prev_scb->hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
- ahd_sync_scb(ahd, prev_scb,
+ ahd_sync_scb(ahd, prev_scb,
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
}
ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
@@ -8267,12 +8170,12 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
if ((scb->flags & SCB_ACTIVE) == 0)
printk("Inactive SCB in qinfifo\n");
ahd_done_with_status(ahd, scb, status);
- /* FALLTHROUGH */
+ fallthrough;
case SEARCH_REMOVE:
break;
case SEARCH_PRINT:
printk(" 0x%x", ahd->qinfifo[qinpos]);
- /* FALLTHROUGH */
+ fallthrough;
case SEARCH_COUNT:
ahd_qinfifo_requeue(ahd, prev_scb, scb);
prev_scb = scb;
@@ -8363,7 +8266,7 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
if ((mk_msg_scb->flags & SCB_ACTIVE) == 0)
printk("Inactive SCB pending MK_MSG\n");
ahd_done_with_status(ahd, mk_msg_scb, status);
- /* FALLTHROUGH */
+ fallthrough;
case SEARCH_REMOVE:
{
u_int tail_offset;
@@ -8387,7 +8290,7 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
}
case SEARCH_PRINT:
printk(" 0x%x", SCB_GET_TAG(scb));
- /* FALLTHROUGH */
+ fallthrough;
case SEARCH_COUNT:
break;
}
@@ -8426,7 +8329,7 @@ ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
static int
ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
int lun, u_int tag, role_t role, uint32_t status,
- ahd_search_action action, u_int *list_head,
+ ahd_search_action action, u_int *list_head,
u_int *list_tail, u_int tid)
{
struct scb *scb;
@@ -8468,7 +8371,7 @@ ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
if ((scb->flags & SCB_ACTIVE) == 0)
printk("Inactive SCB in Waiting List\n");
ahd_done_with_status(ahd, scb, status);
- /* FALLTHROUGH */
+ fallthrough;
case SEARCH_REMOVE:
ahd_rem_wscb(ahd, scbid, prev, next, tid);
*list_tail = prev;
@@ -8477,6 +8380,7 @@ ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
break;
case SEARCH_PRINT:
printk("0x%x ", scbid);
+ fallthrough;
case SEARCH_COUNT:
prev = scbid;
break;
@@ -8878,12 +8782,12 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
/**************************** Statistics Processing ***************************/
static void
-ahd_stat_timer(void *arg)
+ahd_stat_timer(struct timer_list *t)
{
- struct ahd_softc *ahd = arg;
+ struct ahd_softc *ahd = timer_container_of(ahd, t, stat_timer);
u_long s;
int enint_coal;
-
+
ahd_lock(ahd, &s);
enint_coal = ahd->hs_mailbox & ENINT_COALESCE;
@@ -8907,8 +8811,7 @@ ahd_stat_timer(void *arg)
ahd->cmdcmplt_bucket = (ahd->cmdcmplt_bucket+1) & (AHD_STAT_BUCKETS-1);
ahd->cmdcmplt_total -= ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket];
ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket] = 0;
- ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
- ahd_stat_timer, ahd);
+ ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US);
ahd_unlock(ahd, &s);
}
@@ -8929,7 +8832,7 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
* operations are on data structures that the sequencer
* is not touching once the queue is frozen.
*/
- hscb = scb->hscb;
+ hscb = scb->hscb;
if (ahd_is_paused(ahd)) {
paused = 1;
@@ -8993,7 +8896,7 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
printk("Invalid Command IU Field\n");
break;
case SIU_PFC_TMF_NOT_SUPPORTED:
- printk("TMF not supportd\n");
+ printk("TMF not supported\n");
break;
case SIU_PFC_TMF_FAILED:
printk("TMF failed\n");
@@ -9003,11 +8906,12 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
break;
case SIU_PFC_ILLEGAL_REQUEST:
printk("Illegal request\n");
+ break;
default:
break;
}
}
- if (siu->status == SCSI_STATUS_OK)
+ if (siu->status == SAM_STAT_GOOD)
ahd_set_transaction_status(scb,
CAM_REQ_CMP_ERR);
}
@@ -9021,8 +8925,8 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
ahd_done(ahd, scb);
break;
}
- case SCSI_STATUS_CMD_TERMINATED:
- case SCSI_STATUS_CHECK_COND:
+ case SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
{
struct ahd_devinfo devinfo;
struct ahd_dma_seg *sg;
@@ -9112,10 +9016,10 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
ahd_queue_scb(ahd, scb);
break;
}
- case SCSI_STATUS_OK:
- printk("%s: Interrupted for staus of 0???\n",
+ case SAM_STAT_GOOD:
+ printk("%s: Interrupted for status of 0???\n",
ahd_name(ahd));
- /* FALLTHROUGH */
+ fallthrough;
default:
ahd_done(ahd, scb);
break;
@@ -9202,7 +9106,7 @@ ahd_calc_residual(struct ahd_softc *ahd, struct scb *scb)
/*
* Remainder of the SG where the transfer
- * stopped.
+ * stopped.
*/
resid = ahd_le32toh(spkt->residual_datacnt) & AHD_SG_LEN_MASK;
sg = ahd_sg_bus_to_virt(ahd, scb, resid_sgptr & SG_PTR_MASK);
@@ -9254,7 +9158,7 @@ ahd_queue_lstate_event(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate,
- (lstate->event_r_idx - lstate->event_w_idx);
if (event_type == EVENT_TYPE_BUS_RESET
- || event_type == MSG_BUS_DEV_RESET) {
+ || event_type == TARGET_RESET) {
/*
* Any earlier events are irrelevant, so reset our buffer.
* This has the effect of allowing us to deal with reset
@@ -9351,9 +9255,9 @@ ahd_dumpseq(struct ahd_softc* ahd)
static void
ahd_loadseq(struct ahd_softc *ahd)
{
- struct cs cs_table[num_critical_sections];
- u_int begin_set[num_critical_sections];
- u_int end_set[num_critical_sections];
+ struct cs cs_table[NUM_CRITICAL_SECTIONS];
+ u_int begin_set[NUM_CRITICAL_SECTIONS];
+ u_int end_set[NUM_CRITICAL_SECTIONS];
const struct patch *cur_patch;
u_int cs_count;
u_int cur_cs;
@@ -9385,7 +9289,7 @@ ahd_loadseq(struct ahd_softc *ahd)
/*
* Setup downloadable constant table.
- *
+ *
* The computation for the S/G prefetch variables is
* a bit complicated. We would like to always fetch
* in terms of cachelined sized increments. However,
@@ -9469,12 +9373,12 @@ ahd_loadseq(struct ahd_softc *ahd)
* Move through the CS table until we find a CS
* that might apply to this instruction.
*/
- for (; cur_cs < num_critical_sections; cur_cs++) {
+ for (; cur_cs < NUM_CRITICAL_SECTIONS; cur_cs++) {
if (critical_sections[cur_cs].end <= i) {
if (begin_set[cs_count] == TRUE
&& end_set[cs_count] == FALSE) {
cs_table[cs_count].end = downloaded;
- end_set[cs_count] = TRUE;
+ end_set[cs_count] = TRUE;
cs_count++;
}
continue;
@@ -9494,10 +9398,9 @@ ahd_loadseq(struct ahd_softc *ahd)
if (cs_count != 0) {
cs_count *= sizeof(struct cs);
- ahd->critical_sections = kmalloc(cs_count, GFP_ATOMIC);
+ ahd->critical_sections = kmemdup(cs_table, cs_count, GFP_ATOMIC);
if (ahd->critical_sections == NULL)
panic("ahd_loadseq: Could not malloc");
- memcpy(ahd->critical_sections, cs_table, cs_count);
}
ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE);
@@ -9603,8 +9506,8 @@ ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
{
fmt3_ins = &instr.format3;
fmt3_ins->address = ahd_resolve_seqaddr(ahd, fmt3_ins->address);
- /* FALLTHROUGH */
}
+ fallthrough;
case AIC_OP_OR:
case AIC_OP_AND:
case AIC_OP_XOR:
@@ -9615,7 +9518,7 @@ ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
}
fmt1_ins->parity = 0;
- /* FALLTHROUGH */
+ fallthrough;
case AIC_OP_ROL:
{
int i, count;
@@ -9710,7 +9613,7 @@ ahd_print_register(const ahd_reg_parse_entry_t *table, u_int num_entries,
printed_mask == 0 ? ":(" : "|",
table[entry].name);
printed_mask |= table[entry].mask;
-
+
break;
}
if (entry >= num_entries)
@@ -9747,7 +9650,7 @@ ahd_dump_card_state(struct ahd_softc *ahd)
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
printk(">>>>>>>>>>>>>>>>>> Dump Card State Begins <<<<<<<<<<<<<<<<<\n"
"%s: Dumping Card State at program address 0x%x Mode 0x%x\n",
- ahd_name(ahd),
+ ahd_name(ahd),
ahd_inw(ahd, CURADDR),
ahd_build_mode_state(ahd, ahd->saved_src_mode,
ahd->saved_dst_mode));
@@ -9863,7 +9766,6 @@ ahd_dump_card_state(struct ahd_softc *ahd)
}
printk("\n");
-
printk("Sequencer DMA-Up and Complete list: ");
scb_index = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
i = 0;
@@ -10041,7 +9943,7 @@ ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
ahd_outb(ahd, SEEADR, cur_addr);
ahd_outb(ahd, SEECTL, SEEOP_READ | SEESTART);
-
+
error = ahd_wait_seeprom(ahd);
if (error)
break;
@@ -10096,7 +9998,7 @@ ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
ahd_outw(ahd, SEEDAT, *buf++);
ahd_outb(ahd, SEEADR, cur_addr);
ahd_outb(ahd, SEECTL, SEEOP_WRITE | SEESTART);
-
+
retval = ahd_wait_seeprom(ahd);
if (retval)
break;
@@ -10201,7 +10103,7 @@ ahd_acquire_seeprom(struct ahd_softc *ahd)
error = ahd_read_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, &seetype);
if (error != 0
- || ((seetype & FLX_ROMSTAT_SEECFG) == FLX_ROMSTAT_SEE_NONE))
+ || ((seetype & FLX_ROMSTAT_SEECFG) == FLX_ROMSTAT_SEE_NONE))
return (0);
return (1);
#endif
@@ -10343,7 +10245,7 @@ ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb)
our_id = ahd->our_id;
if (ccb->ccb_h.target_id != our_id) {
if ((ahd->features & AHD_MULTI_TID) != 0
- && (ahd->flags & AHD_INITIATORROLE) != 0) {
+ && (ahd->flags & AHD_INITIATORROLE) != 0) {
/*
* Only allow additional targets if
* the initiator role is disabled.
@@ -10437,14 +10339,13 @@ ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb)
return;
}
}
- lstate = kmalloc(sizeof(*lstate), GFP_ATOMIC);
+ lstate = kzalloc(sizeof(*lstate), GFP_ATOMIC);
if (lstate == NULL) {
xpt_print_path(ccb->ccb_h.path);
printk("Couldn't allocate lstate\n");
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
return;
}
- memset(lstate, 0, sizeof(*lstate));
status = xpt_create_path(&lstate->path, /*periph*/NULL,
xpt_path_path_id(ccb->ccb_h.path),
xpt_path_target_id(ccb->ccb_h.path),
@@ -10531,7 +10432,7 @@ ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb)
}
ahd_lock(ahd, &s);
-
+
ccb->ccb_h.status = CAM_REQ_CMP;
LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
struct ccb_hdr *ccbh;
@@ -10795,7 +10696,7 @@ ahd_handle_target_cmd(struct ahd_softc *ahd, struct target_cmd *cmd)
printk("Reserved or VU command code type encountered\n");
break;
}
-
+
memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
atio->ccb_h.status |= CAM_CDB_RECVD;
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
index 7d48700257a7..c3d1b9dd24ae 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm.c
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -194,7 +194,7 @@ struct ahd_linux_iocell_opts
#define AIC79XX_PRECOMP_INDEX 0
#define AIC79XX_SLEWRATE_INDEX 1
#define AIC79XX_AMPLITUDE_INDEX 2
-static const struct ahd_linux_iocell_opts aic79xx_iocell_info[] =
+static struct ahd_linux_iocell_opts aic79xx_iocell_info[] __ro_after_init =
{
AIC79XX_DEFAULT_IOOPTS,
AIC79XX_DEFAULT_IOOPTS,
@@ -341,10 +341,10 @@ MODULE_PARM_DESC(aic79xx,
" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
" slowcrc Turn on the SLOWCRC bit (Rev B only)\n"
"\n"
-" Sample /etc/modprobe.conf line:\n"
-" Enable verbose logging\n"
-" Set tag depth on Controller 2/Target 2 to 10 tags\n"
-" Shorten the selection timeout to 128ms\n"
+" Sample modprobe configuration file:\n"
+" # Enable verbose logging\n"
+" # Set tag depth on Controller 2/Target 2 to 10 tags\n"
+" # Shorten the selection timeout to 128ms\n"
"\n"
" options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n"
);
@@ -536,13 +536,18 @@ ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb)
struct scsi_cmnd *cmd;
cmd = scb->io_ctx;
- ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE);
- scsi_dma_unmap(cmd);
+ if (cmd) {
+ ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE);
+ scsi_dma_unmap(cmd);
+ }
}
/******************************** Macros **************************************/
-#define BUILD_SCSIID(ahd, cmd) \
- (((scmd_id(cmd) << TID_SHIFT) & TID) | (ahd)->our_id)
+static inline unsigned int ahd_build_scsiid(struct ahd_softc *ahd,
+ struct scsi_device *sdev)
+{
+ return ((sdev_id(sdev) << TID_SHIFT) & TID) | (ahd)->our_id;
+}
/*
* Return a string describing the driver.
@@ -572,8 +577,7 @@ ahd_linux_info(struct Scsi_Host *host)
/*
* Queue an SCB to the controller.
*/
-static int
-ahd_linux_queue_lck(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *))
+static int ahd_linux_queue_lck(struct scsi_cmnd *cmd)
{
struct ahd_softc *ahd;
struct ahd_linux_device *dev = scsi_transport_device_data(cmd->device);
@@ -581,7 +585,6 @@ ahd_linux_queue_lck(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd
ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
- cmd->scsi_done = scsi_done;
cmd->result = CAM_REQ_INPROG << 16;
rtn = ahd_linux_run_command(ahd, dev, cmd);
@@ -669,7 +672,7 @@ ahd_linux_target_destroy(struct scsi_target *starget)
}
static int
-ahd_linux_slave_alloc(struct scsi_device *sdev)
+ahd_linux_sdev_init(struct scsi_device *sdev)
{
struct ahd_softc *ahd =
*((struct ahd_softc **)sdev->host->hostdata);
@@ -698,11 +701,8 @@ ahd_linux_slave_alloc(struct scsi_device *sdev)
}
static int
-ahd_linux_slave_configure(struct scsi_device *sdev)
+ahd_linux_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
- struct ahd_softc *ahd;
-
- ahd = *((struct ahd_softc **)sdev->host->hostdata);
if (bootverbose)
sdev_printk(KERN_INFO, sdev, "Slave Configure\n");
@@ -720,27 +720,20 @@ ahd_linux_slave_configure(struct scsi_device *sdev)
* Return the disk geometry for the given SCSI device.
*/
static int
-ahd_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ahd_linux_biosparam(struct scsi_device *sdev, struct gendisk *disk,
sector_t capacity, int geom[])
{
- uint8_t *bh;
int heads;
int sectors;
int cylinders;
- int ret;
int extended;
struct ahd_softc *ahd;
ahd = *((struct ahd_softc **)sdev->host->hostdata);
- bh = scsi_bios_ptable(bdev);
- if (bh) {
- ret = scsi_partsize(bh, capacity,
- &geom[2], &geom[0], &geom[1]);
- kfree(bh);
- if (ret != -1)
- return (ret);
- }
+ if (scsi_partsize(disk, capacity, geom))
+ return 0;
+
heads = 64;
sectors = 32;
cylinders = aic_sector_div(capacity, heads, sectors);
@@ -767,11 +760,7 @@ ahd_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
static int
ahd_linux_abort(struct scsi_cmnd *cmd)
{
- int error;
-
- error = ahd_linux_queue_abort_cmd(cmd);
-
- return error;
+ return ahd_linux_queue_abort_cmd(cmd);
}
/*
@@ -785,16 +774,13 @@ ahd_linux_dev_reset(struct scsi_cmnd *cmd)
struct scb *reset_scb;
u_int cdb_byte;
int retval = SUCCESS;
- int paused;
- int wait;
struct ahd_initiator_tinfo *tinfo;
struct ahd_tmode_tstate *tstate;
unsigned long flags;
DECLARE_COMPLETION_ONSTACK(done);
reset_scb = NULL;
- paused = FALSE;
- wait = FALSE;
+
ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
scmd_printk(KERN_INFO, cmd,
@@ -830,14 +816,14 @@ ahd_linux_dev_reset(struct scsi_cmnd *cmd)
tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
cmd->device->id, &tstate);
- reset_scb->io_ctx = cmd;
+ reset_scb->io_ctx = NULL;
reset_scb->platform_data->dev = dev;
reset_scb->sg_count = 0;
ahd_set_residual(reset_scb, 0);
ahd_set_sense_residual(reset_scb, 0);
reset_scb->platform_data->xfer_len = 0;
reset_scb->hscb->control = 0;
- reset_scb->hscb->scsiid = BUILD_SCSIID(ahd,cmd);
+ reset_scb->hscb->scsiid = ahd_build_scsiid(ahd, cmd->device);
reset_scb->hscb->lun = cmd->device->lun;
reset_scb->hscb->cdb_len = 0;
reset_scb->hscb->task_management = SIU_TASKMGMT_LUN_RESET;
@@ -906,7 +892,8 @@ struct scsi_host_template aic79xx_driver_template = {
.module = THIS_MODULE,
.name = "aic79xx",
.proc_name = "aic79xx",
- .proc_info = ahd_linux_proc_info,
+ .show_info = ahd_linux_show_info,
+ .write_info = ahd_proc_write_seeprom,
.info = ahd_linux_info,
.queuecommand = ahd_linux_queue,
.eh_abort_handler = ahd_linux_abort,
@@ -919,9 +906,8 @@ struct scsi_host_template aic79xx_driver_template = {
.this_id = -1,
.max_sectors = 8192,
.cmd_per_lun = 2,
- .use_clustering = ENABLE_CLUSTERING,
- .slave_alloc = ahd_linux_slave_alloc,
- .slave_configure = ahd_linux_slave_configure,
+ .sdev_init = ahd_linux_sdev_init,
+ .sdev_configure = ahd_linux_sdev_configure,
.target_alloc = ahd_linux_target_alloc,
.target_destroy = ahd_linux_target_destroy,
};
@@ -965,8 +951,8 @@ int
ahd_dmamem_alloc(struct ahd_softc *ahd, bus_dma_tag_t dmat, void** vaddr,
int flags, bus_dmamap_t *mapp)
{
- *vaddr = pci_alloc_consistent(ahd->dev_softc,
- dmat->maxsize, mapp);
+ *vaddr = dma_alloc_coherent(&ahd->dev_softc->dev, dmat->maxsize, mapp,
+ GFP_ATOMIC);
if (*vaddr == NULL)
return (ENOMEM);
return(0);
@@ -976,8 +962,7 @@ void
ahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat,
void* vaddr, bus_dmamap_t map)
{
- pci_free_consistent(ahd->dev_softc, dmat->maxsize,
- vaddr, map);
+ dma_free_coherent(&ahd->dev_softc->dev, dmat->maxsize, vaddr, map);
}
int
@@ -1324,10 +1309,9 @@ int
ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg)
{
ahd->platform_data =
- kmalloc(sizeof(struct ahd_platform_data), GFP_ATOMIC);
+ kzalloc(sizeof(struct ahd_platform_data), GFP_ATOMIC);
if (ahd->platform_data == NULL)
return (ENOMEM);
- memset(ahd->platform_data, 0, sizeof(struct ahd_platform_data));
ahd->platform_data->irq = AHD_LINUX_NOIRQ;
ahd_lockinit(ahd);
ahd->seltime = (aic79xx_seltime & 0x3) << 4;
@@ -1467,12 +1451,9 @@ ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev,
switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) {
case AHD_DEV_Q_BASIC:
- scsi_set_tag_type(sdev, MSG_SIMPLE_TASK);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
- break;
case AHD_DEV_Q_TAGGED:
- scsi_set_tag_type(sdev, MSG_ORDERED_TASK);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
+ scsi_change_queue_depth(sdev,
+ dev->openings + dev->active);
break;
default:
/*
@@ -1481,7 +1462,7 @@ ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev,
* serially on the controller/device. This should
* remove some latency.
*/
- scsi_deactivate_tcq(sdev, 1);
+ scsi_change_queue_depth(sdev, 1);
break;
}
}
@@ -1601,7 +1582,7 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev,
* Fill out basics of the HSCB.
*/
hscb->control = 0;
- hscb->scsiid = BUILD_SCSIID(ahd, cmd);
+ hscb->scsiid = ahd_build_scsiid(ahd, cmd->device);
hscb->lun = cmd->device->lun;
scb->hscb->task_management = 0;
mask = SCB_GET_TARGET_MASK(ahd, scb);
@@ -1618,21 +1599,12 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev,
}
if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
- int msg_bytes;
- uint8_t tag_msgs[2];
-
- msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
- if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
- hscb->control |= tag_msgs[0];
- if (tag_msgs[0] == MSG_ORDERED_TASK)
- dev->commands_since_idle_or_otag = 0;
- } else
if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH
&& (dev->flags & AHD_DEV_Q_TAGGED) != 0) {
- hscb->control |= MSG_ORDERED_TASK;
+ hscb->control |= ORDERED_QUEUE_TAG;
dev->commands_since_idle_or_otag = 0;
} else {
- hscb->control |= MSG_SIMPLE_TASK;
+ hscb->control |= SIMPLE_QUEUE_TAG;
}
}
@@ -1702,19 +1674,13 @@ ahd_send_async(struct ahd_softc *ahd, char channel,
switch (code) {
case AC_TRANSFER_NEG:
{
- char buf[80];
struct scsi_target *starget;
- struct info_str info;
struct ahd_initiator_tinfo *tinfo;
struct ahd_tmode_tstate *tstate;
unsigned int target_ppr_options;
BUG_ON(target == CAM_TARGET_WILDCARD);
- info.buffer = buf;
- info.length = sizeof(buf);
- info.offset = 0;
- info.pos = 0;
tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id,
target, &tstate);
@@ -1805,9 +1771,16 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb)
dev = scb->platform_data->dev;
dev->active--;
dev->openings++;
- if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
- cmd->result &= ~(CAM_DEV_QFRZN << 16);
- dev->qfrozen--;
+ if (cmd) {
+ if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
+ cmd->result &= ~(CAM_DEV_QFRZN << 16);
+ dev->qfrozen--;
+ }
+ } else if (scb->flags & SCB_DEVICE_RESET) {
+ if (ahd->platform_data->eh_done)
+ complete(ahd->platform_data->eh_done);
+ ahd_free_scb(ahd, scb);
+ return;
}
ahd_linux_unmap_scb(ahd, scb);
@@ -1819,10 +1792,12 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb)
*/
cmd->sense_buffer[0] = 0;
if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) {
+#ifdef AHD_REPORT_UNDERFLOWS
uint32_t amount_xferred;
amount_xferred =
ahd_get_transfer_length(scb) - ahd_get_residual(scb);
+#endif
if ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MISC) != 0) {
@@ -1859,13 +1834,14 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb)
} else {
ahd_set_transaction_status(scb, CAM_REQ_CMP);
}
- } else if (ahd_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
+ } else if (cmd &&
+ ahd_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
ahd_linux_handle_scsi_status(ahd, cmd->device, scb);
}
if (dev->openings == 1
&& ahd_get_transaction_status(scb) == CAM_REQ_CMP
- && ahd_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+ && ahd_get_scsi_status(scb) != SAM_STAT_TASK_SET_FULL)
dev->tag_success_count++;
/*
* Some devices deal with temporary internal resource
@@ -1893,7 +1869,8 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb)
}
ahd_free_scb(ahd, scb);
- ahd_linux_queue_cmd_complete(ahd, cmd);
+ if (cmd)
+ ahd_linux_queue_cmd_complete(ahd, cmd);
}
static void
@@ -1922,8 +1899,8 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
switch (ahd_get_scsi_status(scb)) {
default:
break;
- case SCSI_STATUS_CHECK_COND:
- case SCSI_STATUS_CMD_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
+ case SAM_STAT_COMMAND_TERMINATED:
{
struct scsi_cmnd *cmd;
@@ -1959,7 +1936,7 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
memcpy(cmd->sense_buffer,
ahd_get_sense_buf(ahd, scb)
+ sense_offset, sense_size);
- cmd->result |= (DRIVER_SENSE << 24);
+ set_status_byte(cmd, SAM_STAT_CHECK_CONDITION);
#ifdef AHD_DEBUG
if (ahd_debug & AHD_SHOW_SENSE) {
@@ -1978,7 +1955,7 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
}
break;
}
- case SCSI_STATUS_QUEUE_FULL:
+ case SAM_STAT_TASK_SET_FULL:
/*
* By the time the core driver has returned this
* command, all other commands that were queued
@@ -2024,7 +2001,7 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
dev->last_queuefull_same_count = 0;
}
ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
- ahd_set_scsi_status(scb, SCSI_STATUS_OK);
+ ahd_set_scsi_status(scb, SAM_STAT_GOOD);
ahd_platform_set_tags(ahd, sdev, &devinfo,
(dev->flags & AHD_DEV_Q_BASIC)
? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
@@ -2038,7 +2015,7 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
ahd_platform_set_tags(ahd, sdev, &devinfo,
(dev->flags & AHD_DEV_Q_BASIC)
? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
- ahd_set_scsi_status(scb, SCSI_STATUS_BUSY);
+ ahd_set_scsi_status(scb, SAM_STAT_BUSY);
}
}
@@ -2049,6 +2026,7 @@ ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd)
int new_status = DID_OK;
int do_fallback = 0;
int scsi_status;
+ struct scsi_sense_data *sense;
/*
* Map CAM error codes into Linux Error codes. We
@@ -2065,25 +2043,19 @@ ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd)
break;
case CAM_AUTOSENSE_FAIL:
new_status = DID_ERROR;
- /* Fallthrough */
+ fallthrough;
case CAM_SCSI_STATUS_ERROR:
scsi_status = ahd_cmd_get_scsi_status(cmd);
switch(scsi_status) {
- case SCSI_STATUS_CMD_TERMINATED:
- case SCSI_STATUS_CHECK_COND:
- if ((cmd->result >> 24) != DRIVER_SENSE) {
+ case SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
+ sense = (struct scsi_sense_data *)
+ cmd->sense_buffer;
+ if (sense->extra_len >= 5 &&
+ (sense->add_sense_code == 0x47
+ || sense->add_sense_code == 0x48))
do_fallback = 1;
- } else {
- struct scsi_sense_data *sense;
-
- sense = (struct scsi_sense_data *)
- cmd->sense_buffer;
- if (sense->extra_len >= 5 &&
- (sense->add_sense_code == 0x47
- || sense->add_sense_code == 0x48))
- do_fallback = 1;
- }
break;
default:
break;
@@ -2142,12 +2114,12 @@ ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd)
if (do_fallback) {
printk("%s: device overrun (status %x) on %d:%d:%d\n",
ahd_name(ahd), status, cmd->device->channel,
- cmd->device->id, cmd->device->lun);
+ cmd->device->id, (u8)cmd->device->lun);
}
ahd_cmd_set_transaction_status(cmd, new_status);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
static void
@@ -2171,9 +2143,8 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
u_int saved_scbptr;
u_int active_scbptr;
u_int last_phase;
- u_int saved_scsiid;
u_int cdb_byte;
- int retval;
+ int retval = SUCCESS;
int was_paused;
int paused;
int wait;
@@ -2211,8 +2182,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
* so we must not still own the command.
*/
scmd_printk(KERN_INFO, cmd, "Is not an active device\n");
- retval = SUCCESS;
- goto no_cmd;
+ goto done;
}
/*
@@ -2225,7 +2195,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
if (pending_scb == NULL) {
scmd_printk(KERN_INFO, cmd, "Command not found\n");
- goto no_cmd;
+ goto done;
}
if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
@@ -2233,7 +2203,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
* We can't queue two recovery actions using the same SCB
*/
retval = FAILED;
- goto done;
+ goto done;
}
/*
@@ -2248,7 +2218,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
if ((pending_scb->flags & SCB_ACTIVE) == 0) {
scmd_printk(KERN_INFO, cmd, "Command already completed\n");
- goto no_cmd;
+ goto done;
}
printk("%s: At time of recovery, card was %spaused\n",
@@ -2258,14 +2228,13 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
disconnected = TRUE;
if (ahd_search_qinfifo(ahd, cmd->device->id,
cmd->device->channel + 'A',
- cmd->device->lun,
+ cmd->device->lun,
pending_scb->hscb->tag,
ROLE_INITIATOR, CAM_REQ_ABORTED,
SEARCH_COMPLETE) > 0) {
printk("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
ahd_name(ahd), cmd->device->channel,
- cmd->device->id, cmd->device->lun);
- retval = SUCCESS;
+ cmd->device->id, (u8)cmd->device->lun);
goto done;
}
@@ -2287,7 +2256,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
* passed in command. That command is currently active on the
* bus or is in the disconnected state.
*/
- saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+ ahd_inb(ahd, SAVED_SCSIID);
if (last_phase != P_BUSFREE
&& SCB_GET_TAG(pending_scb) == active_scbptr) {
@@ -2362,17 +2331,10 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
} else {
scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n");
retval = FAILED;
- goto done;
}
-no_cmd:
- /*
- * Our assumption is that if we don't have the command, no
- * recovery action was required, so we return success. Again,
- * the semantics of the mid-layer recovery engine are not
- * well defined, so this may change in time.
- */
- retval = SUCCESS;
+
+ ahd_restore_modes(ahd, saved_modes);
done:
if (paused)
ahd_unpause(ahd);
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h
index 28e43498cdff..793fe19993a9 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm.h
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.h
@@ -65,11 +65,6 @@
/* Core SCSI definitions */
#define AIC_LIB_PREFIX ahd
-/* Name space conflict with BSD queue macros */
-#ifdef LIST_HEAD
-#undef LIST_HEAD
-#endif
-
#include "cam.h"
#include "queue.h"
#include "scsi_message.h"
@@ -201,16 +196,13 @@ int ahd_dmamap_unload(struct ahd_softc *, bus_dma_tag_t, bus_dmamap_t);
/*
* XXX
* ahd_dmamap_sync is only used on buffers allocated with
- * the pci_alloc_consistent() API. Although I'm not sure how
+ * the dma_alloc_coherent() API. Although I'm not sure how
* this works on architectures with a write buffer, Linux does
* not have an API to sync "coherent" memory. Perhaps we need
* to do an mb()?
*/
#define ahd_dmamap_sync(ahd, dma_tag, dmamap, offset, len, op)
-/************************** Timer DataStructures ******************************/
-typedef struct timer_list ahd_timer_t;
-
/********************************** Includes **********************************/
#ifdef CONFIG_AIC79XX_REG_PRETTY_PRINT
#define AIC_DEBUG_REGISTERS 1
@@ -219,10 +211,6 @@ typedef struct timer_list ahd_timer_t;
#endif
#include "aic79xx.h"
-/***************************** Timer Facilities *******************************/
-#define ahd_timer_init init_timer
-#define ahd_timer_stop del_timer_sync
-
/***************************** SMP support ************************************/
#include <linux/spinlock.h>
@@ -254,7 +242,7 @@ struct ahd_linux_device {
int active;
/*
- * The currently allowed number of
+ * The currently allowed number of
* transactions that can be queued to
* the device. Must be signed for
* conversion from tagged to untagged
@@ -268,7 +256,7 @@ struct ahd_linux_device {
* device's queue is halted.
*/
u_int qfrozen;
-
+
/*
* Cumulative command counter.
*/
@@ -352,11 +340,11 @@ struct ahd_platform_data {
/*
* Fields accessed from interrupt context.
*/
- struct scsi_target *starget[AHD_NUM_TARGETS];
+ struct scsi_target *starget[AHD_NUM_TARGETS];
spinlock_t spin_lock;
struct completion *eh_done;
- struct Scsi_Host *host; /* pointer to scsi host */
+ struct Scsi_Host *host; /* pointer to scsi host */
#define AHD_LINUX_NOIRQ ((uint32_t)~0)
uint32_t irq; /* IRQ for this adapter */
uint32_t bios_address;
@@ -379,14 +367,6 @@ void ahd_insb(struct ahd_softc * ahd, long port,
int ahd_linux_register_host(struct ahd_softc *,
struct scsi_host_template *);
-/*************************** Pretty Printing **********************************/
-struct info_str {
- char *buffer;
- int length;
- off_t offset;
- int pos;
-};
-
/******************************** Locking *************************************/
static inline void
ahd_lockinit(struct ahd_softc *ahd)
@@ -440,8 +420,6 @@ ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
/* config registers for header type 0 devices */
#define PCIR_MAPS 0x10
-#define PCIR_SUBVEND_0 0x2c
-#define PCIR_SUBDEV_0 0x2e
/****************************** PCI-X definitions *****************************/
#define PCIXR_COMMAND 0x96
@@ -513,33 +491,10 @@ ahd_flush_device_writes(struct ahd_softc *ahd)
}
/**************************** Proc FS Support *********************************/
-int ahd_linux_proc_info(struct Scsi_Host *, char *, char **,
- off_t, int, int);
+int ahd_proc_write_seeprom(struct Scsi_Host *, char *, int);
+int ahd_linux_show_info(struct seq_file *,struct Scsi_Host *);
/*********************** Transaction Access Wrappers **************************/
-static inline void ahd_cmd_set_transaction_status(struct scsi_cmnd *, uint32_t);
-static inline void ahd_set_transaction_status(struct scb *, uint32_t);
-static inline void ahd_cmd_set_scsi_status(struct scsi_cmnd *, uint32_t);
-static inline void ahd_set_scsi_status(struct scb *, uint32_t);
-static inline uint32_t ahd_cmd_get_transaction_status(struct scsi_cmnd *cmd);
-static inline uint32_t ahd_get_transaction_status(struct scb *);
-static inline uint32_t ahd_cmd_get_scsi_status(struct scsi_cmnd *cmd);
-static inline uint32_t ahd_get_scsi_status(struct scb *);
-static inline void ahd_set_transaction_tag(struct scb *, int, u_int);
-static inline u_long ahd_get_transfer_length(struct scb *);
-static inline int ahd_get_transfer_dir(struct scb *);
-static inline void ahd_set_residual(struct scb *, u_long);
-static inline void ahd_set_sense_residual(struct scb *scb, u_long resid);
-static inline u_long ahd_get_residual(struct scb *);
-static inline u_long ahd_get_sense_residual(struct scb *);
-static inline int ahd_perform_autosense(struct scb *);
-static inline uint32_t ahd_get_sense_bufsize(struct ahd_softc *,
- struct scb *);
-static inline void ahd_notify_xfer_settings_change(struct ahd_softc *,
- struct ahd_devinfo *);
-static inline void ahd_platform_scb_free(struct ahd_softc *ahd,
- struct scb *scb);
-static inline void ahd_freeze_scb(struct scb *scb);
static inline
void ahd_cmd_set_transaction_status(struct scsi_cmnd *cmd, uint32_t status)
@@ -675,9 +630,9 @@ static inline void
ahd_freeze_scb(struct scb *scb)
{
if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
- scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
- scb->platform_data->dev->qfrozen++;
- }
+ scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+ scb->platform_data->dev->qfrozen++;
+ }
}
void ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev,
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
index 3c85873b14b9..b92e2e3c358a 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
+++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
@@ -45,8 +45,8 @@
/* Define the macro locally since it's different for different class of chips.
*/
-#define ID(x) \
- ID2C(x), \
+#define ID(x) \
+ ID2C(x), \
ID2C(IDIROC(x))
static const struct pci_device_id ahd_linux_pci_id_table[] = {
@@ -74,11 +74,10 @@ static const struct pci_device_id ahd_linux_pci_id_table[] = {
MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table);
-#ifdef CONFIG_PM
-static int
-ahd_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
+static int __maybe_unused
+ahd_linux_pci_dev_suspend(struct device *dev)
{
- struct ahd_softc *ahd = pci_get_drvdata(pdev);
+ struct ahd_softc *ahd = dev_get_drvdata(dev);
int rc;
if ((rc = ahd_suspend(ahd)))
@@ -86,39 +85,20 @@ ahd_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
ahd_pci_suspend(ahd);
- pci_save_state(pdev);
- pci_disable_device(pdev);
-
- if (mesg.event & PM_EVENT_SLEEP)
- pci_set_power_state(pdev, PCI_D3hot);
-
return rc;
}
-static int
-ahd_linux_pci_dev_resume(struct pci_dev *pdev)
+static int __maybe_unused
+ahd_linux_pci_dev_resume(struct device *dev)
{
- struct ahd_softc *ahd = pci_get_drvdata(pdev);
- int rc;
-
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
- if ((rc = pci_enable_device(pdev))) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to enable device after resume (%d)\n", rc);
- return rc;
- }
-
- pci_set_master(pdev);
+ struct ahd_softc *ahd = dev_get_drvdata(dev);
ahd_pci_resume(ahd);
ahd_resume(ahd);
- return rc;
+ return 0;
}
-#endif
static void
ahd_linux_pci_dev_remove(struct pci_dev *pdev)
@@ -178,10 +158,9 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ahd_get_pci_bus(pci),
ahd_get_pci_slot(pci),
ahd_get_pci_function(pci));
- name = kmalloc(strlen(buf) + 1, GFP_ATOMIC);
+ name = kstrdup(buf, GFP_ATOMIC);
if (name == NULL)
return (-ENOMEM);
- strcpy(name, buf);
ahd = ahd_alloc(NULL, name);
if (ahd == NULL)
return (-ENOMEM);
@@ -225,13 +204,14 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return (0);
}
+static SIMPLE_DEV_PM_OPS(ahd_linux_pci_dev_pm_ops,
+ ahd_linux_pci_dev_suspend,
+ ahd_linux_pci_dev_resume);
+
static struct pci_driver aic79xx_pci_driver = {
.name = "aic79xx",
.probe = ahd_linux_pci_dev_probe,
-#ifdef CONFIG_PM
- .suspend = ahd_linux_pci_dev_suspend,
- .resume = ahd_linux_pci_dev_resume,
-#endif
+ .driver.pm = &ahd_linux_pci_dev_pm_ops,
.remove = ahd_linux_pci_dev_remove,
.id_table = ahd_linux_pci_id_table
};
@@ -294,7 +274,7 @@ ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
if (!request_mem_region(start, 0x1000, "aic79xx"))
error = ENOMEM;
if (!error) {
- *maddr = ioremap_nocache(base_page, base_offset + 512);
+ *maddr = ioremap(base_page, base_offset + 512);
if (*maddr == NULL) {
error = ENOMEM;
release_mem_region(start, 0x1000);
@@ -387,7 +367,7 @@ ahd_pci_map_int(struct ahd_softc *ahd)
IRQF_SHARED, "aic79xx", ahd);
if (!error)
ahd->platform_data->irq = ahd->dev_softc->irq;
-
+
return (-error);
}
diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c
index 14b5f8d0e7f4..5fad41b1ab58 100644
--- a/drivers/scsi/aic7xxx/aic79xx_pci.c
+++ b/drivers/scsi/aic7xxx/aic79xx_pci.c
@@ -41,14 +41,8 @@
* $Id: //depot/aic7xxx/aic7xxx/aic79xx_pci.c#92 $
*/
-#ifdef __linux__
#include "aic79xx_osm.h"
#include "aic79xx_inline.h"
-#else
-#include <dev/aic7xxx/aic79xx_osm.h>
-#include <dev/aic7xxx/aic79xx_inline.h>
-#endif
-
#include "aic79xx_pci.h"
static inline uint64_t
@@ -266,8 +260,8 @@ ahd_find_pci_device(ahd_dev_softc_t pci)
vendor = ahd_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
device = ahd_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
- subvendor = ahd_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
- subdevice = ahd_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+ subvendor = ahd_pci_read_config(pci, PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2);
+ subdevice = ahd_pci_read_config(pci, PCI_SUBSYSTEM_ID, /*bytes*/2);
full_id = ahd_compose_id(device,
vendor,
subdevice,
@@ -294,19 +288,17 @@ ahd_find_pci_device(ahd_dev_softc_t pci)
int
ahd_pci_config(struct ahd_softc *ahd, const struct ahd_pci_identity *entry)
{
- struct scb_data *shared_scb_data;
u_int command;
uint32_t devconfig;
uint16_t subvendor;
int error;
- shared_scb_data = NULL;
ahd->description = entry->name;
/*
* Record if this is an HP board.
*/
subvendor = ahd_pci_read_config(ahd->dev_softc,
- PCIR_SUBVEND_0, /*bytes*/2);
+ PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2);
if (subvendor == SUBID_HP)
ahd->flags |= AHD_HP_BOARD;
@@ -385,8 +377,7 @@ ahd_pci_config(struct ahd_softc *ahd, const struct ahd_pci_identity *entry)
return ahd_pci_map_int(ahd);
}
-#ifdef CONFIG_PM
-void
+void __maybe_unused
ahd_pci_suspend(struct ahd_softc *ahd)
{
/*
@@ -402,7 +393,7 @@ ahd_pci_suspend(struct ahd_softc *ahd)
}
-void
+void __maybe_unused
ahd_pci_resume(struct ahd_softc *ahd)
{
ahd_pci_write_config(ahd->dev_softc, DEVCONFIG,
@@ -412,7 +403,6 @@ ahd_pci_resume(struct ahd_softc *ahd)
ahd_pci_write_config(ahd->dev_softc, CSIZE_LATTIME,
ahd->suspend_state.pci_state.csize_lattime, /*bytes*/1);
}
-#endif
/*
* Perform some simple tests that should catch situations where
@@ -827,7 +817,7 @@ ahd_pci_intr(struct ahd_softc *ahd)
for (bit = 0; bit < 8; bit++) {
if ((pci_status[i] & (0x1 << bit)) != 0) {
- static const char *s;
+ const char *s;
s = pci_status_strings[bit];
if (i == 7/*TARG*/ && bit == 3)
@@ -887,23 +877,15 @@ ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat)
for (bit = 0; bit < 8; bit++) {
- if ((split_status[i] & (0x1 << bit)) != 0) {
- static const char *s;
-
- s = split_status_strings[bit];
- printk(s, ahd_name(ahd),
+ if ((split_status[i] & (0x1 << bit)) != 0)
+ printk(split_status_strings[bit], ahd_name(ahd),
split_status_source[i]);
- }
if (i > 1)
continue;
- if ((sg_split_status[i] & (0x1 << bit)) != 0) {
- static const char *s;
-
- s = split_status_strings[bit];
- printk(s, ahd_name(ahd), "SG");
- }
+ if ((sg_split_status[i] & (0x1 << bit)) != 0)
+ printk(split_status_strings[bit], ahd_name(ahd), "SG");
}
}
/*
diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c
index 59c85d5a153a..746d0ca2a657 100644
--- a/drivers/scsi/aic7xxx/aic79xx_proc.c
+++ b/drivers/scsi/aic7xxx/aic79xx_proc.c
@@ -42,16 +42,12 @@
#include "aic79xx_osm.h"
#include "aic79xx_inline.h"
-static void copy_mem_info(struct info_str *info, char *data, int len);
-static int copy_info(struct info_str *info, char *fmt, ...);
static void ahd_dump_target_state(struct ahd_softc *ahd,
- struct info_str *info,
+ struct seq_file *m,
u_int our_id, char channel,
u_int target_id);
-static void ahd_dump_device_state(struct info_str *info,
+static void ahd_dump_device_state(struct seq_file *m,
struct scsi_device *sdev);
-static int ahd_proc_write_seeprom(struct ahd_softc *ahd,
- char *buffer, int length);
/*
* Table of syncrates that don't follow the "divisible by 4"
@@ -93,117 +89,72 @@ ahd_calc_syncsrate(u_int period_factor)
return (10000000 / (period_factor * 4 * 10));
}
-
-static void
-copy_mem_info(struct info_str *info, char *data, int len)
-{
- if (info->pos + len > info->offset + info->length)
- len = info->offset + info->length - info->pos;
-
- if (info->pos + len < info->offset) {
- info->pos += len;
- return;
- }
-
- if (info->pos < info->offset) {
- off_t partial;
-
- partial = info->offset - info->pos;
- data += partial;
- info->pos += partial;
- len -= partial;
- }
-
- if (len > 0) {
- memcpy(info->buffer, data, len);
- info->pos += len;
- info->buffer += len;
- }
-}
-
-static int
-copy_info(struct info_str *info, char *fmt, ...)
-{
- va_list args;
- char buf[256];
- int len;
-
- va_start(args, fmt);
- len = vsprintf(buf, fmt, args);
- va_end(args);
-
- copy_mem_info(info, buf, len);
- return (len);
-}
-
static void
-ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
+ahd_format_transinfo(struct seq_file *m, struct ahd_transinfo *tinfo)
{
u_int speed;
u_int freq;
u_int mb;
if (tinfo->period == AHD_PERIOD_UNKNOWN) {
- copy_info(info, "Renegotiation Pending\n");
+ seq_puts(m, "Renegotiation Pending\n");
return;
}
- speed = 3300;
- freq = 0;
+ speed = 3300;
+ freq = 0;
if (tinfo->offset != 0) {
freq = ahd_calc_syncsrate(tinfo->period);
speed = freq;
}
speed *= (0x01 << tinfo->width);
- mb = speed / 1000;
- if (mb > 0)
- copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
- else
- copy_info(info, "%dKB/s transfers", speed);
+ mb = speed / 1000;
+ if (mb > 0)
+ seq_printf(m, "%d.%03dMB/s transfers", mb, speed % 1000);
+ else
+ seq_printf(m, "%dKB/s transfers", speed);
if (freq != 0) {
int printed_options;
printed_options = 0;
- copy_info(info, " (%d.%03dMHz", freq / 1000, freq % 1000);
+ seq_printf(m, " (%d.%03dMHz", freq / 1000, freq % 1000);
if ((tinfo->ppr_options & MSG_EXT_PPR_RD_STRM) != 0) {
- copy_info(info, " RDSTRM");
+ seq_puts(m, " RDSTRM");
printed_options++;
}
if ((tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
- copy_info(info, "%s", printed_options ? "|DT" : " DT");
+ seq_puts(m, printed_options ? "|DT" : " DT");
printed_options++;
}
if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
- copy_info(info, "%s", printed_options ? "|IU" : " IU");
+ seq_puts(m, printed_options ? "|IU" : " IU");
printed_options++;
}
if ((tinfo->ppr_options & MSG_EXT_PPR_RTI) != 0) {
- copy_info(info, "%s",
- printed_options ? "|RTI" : " RTI");
+ seq_puts(m, printed_options ? "|RTI" : " RTI");
printed_options++;
}
if ((tinfo->ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
- copy_info(info, "%s",
- printed_options ? "|QAS" : " QAS");
+ seq_puts(m, printed_options ? "|QAS" : " QAS");
printed_options++;
}
}
if (tinfo->width > 0) {
if (freq != 0) {
- copy_info(info, ", ");
+ seq_puts(m, ", ");
} else {
- copy_info(info, " (");
+ seq_puts(m, " (");
}
- copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+ seq_printf(m, "%dbit)", 8 * (0x01 << tinfo->width));
} else if (freq != 0) {
- copy_info(info, ")");
+ seq_putc(m, ')');
}
- copy_info(info, "\n");
+ seq_putc(m, '\n');
}
static void
-ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
+ahd_dump_target_state(struct ahd_softc *ahd, struct seq_file *m,
u_int our_id, char channel, u_int target_id)
{
struct scsi_target *starget;
@@ -213,17 +164,17 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
target_id, &tstate);
- copy_info(info, "Target %d Negotiation Settings\n", target_id);
- copy_info(info, "\tUser: ");
- ahd_format_transinfo(info, &tinfo->user);
+ seq_printf(m, "Target %d Negotiation Settings\n", target_id);
+ seq_puts(m, "\tUser: ");
+ ahd_format_transinfo(m, &tinfo->user);
starget = ahd->platform_data->starget[target_id];
if (starget == NULL)
return;
- copy_info(info, "\tGoal: ");
- ahd_format_transinfo(info, &tinfo->goal);
- copy_info(info, "\tCurr: ");
- ahd_format_transinfo(info, &tinfo->curr);
+ seq_puts(m, "\tGoal: ");
+ ahd_format_transinfo(m, &tinfo->goal);
+ seq_puts(m, "\tCurr: ");
+ ahd_format_transinfo(m, &tinfo->curr);
for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
struct scsi_device *dev;
@@ -233,29 +184,30 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
if (dev == NULL)
continue;
- ahd_dump_device_state(info, dev);
+ ahd_dump_device_state(m, dev);
}
}
static void
-ahd_dump_device_state(struct info_str *info, struct scsi_device *sdev)
+ahd_dump_device_state(struct seq_file *m, struct scsi_device *sdev)
{
struct ahd_linux_device *dev = scsi_transport_device_data(sdev);
- copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+ seq_printf(m, "\tChannel %c Target %d Lun %d Settings\n",
sdev->sdev_target->channel + 'A',
- sdev->sdev_target->id, sdev->lun);
+ sdev->sdev_target->id, (u8)sdev->lun);
- copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
- copy_info(info, "\t\tCommands Active %d\n", dev->active);
- copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
- copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
- copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+ seq_printf(m, "\t\tCommands Queued %ld\n", dev->commands_issued);
+ seq_printf(m, "\t\tCommands Active %d\n", dev->active);
+ seq_printf(m, "\t\tCommand Openings %d\n", dev->openings);
+ seq_printf(m, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+ seq_printf(m, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
}
-static int
-ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length)
+int
+ahd_proc_write_seeprom(struct Scsi_Host *shost, char *buffer, int length)
{
+ struct ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata;
ahd_mode_state saved_modes;
int have_seeprom;
u_long s;
@@ -290,7 +242,8 @@ ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length)
u_int start_addr;
if (ahd->seep_config == NULL) {
- ahd->seep_config = kmalloc(sizeof(*ahd->seep_config), GFP_ATOMIC);
+ ahd->seep_config = kmalloc(sizeof(*ahd->seep_config),
+ GFP_ATOMIC);
if (ahd->seep_config == NULL) {
printk("aic79xx: Unable to allocate serial "
"eeprom buffer. Write failing\n");
@@ -319,64 +272,45 @@ done:
* Return information to handle /proc support for the driver.
*/
int
-ahd_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start,
- off_t offset, int length, int inout)
+ahd_linux_show_info(struct seq_file *m, struct Scsi_Host *shost)
{
struct ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata;
- struct info_str info;
char ahd_info[256];
u_int max_targ;
u_int i;
- int retval;
- /* Has data been written to the file? */
- if (inout == TRUE) {
- retval = ahd_proc_write_seeprom(ahd, buffer, length);
- goto done;
- }
-
- if (start)
- *start = buffer;
-
- info.buffer = buffer;
- info.length = length;
- info.offset = offset;
- info.pos = 0;
-
- copy_info(&info, "Adaptec AIC79xx driver version: %s\n",
+ seq_printf(m, "Adaptec AIC79xx driver version: %s\n",
AIC79XX_DRIVER_VERSION);
- copy_info(&info, "%s\n", ahd->description);
+ seq_printf(m, "%s\n", ahd->description);
ahd_controller_info(ahd, ahd_info);
- copy_info(&info, "%s\n", ahd_info);
- copy_info(&info, "Allocated SCBs: %d, SG List Length: %d\n\n",
+ seq_printf(m, "%s\n", ahd_info);
+ seq_printf(m, "Allocated SCBs: %d, SG List Length: %d\n\n",
ahd->scb_data.numscbs, AHD_NSEG);
max_targ = 16;
if (ahd->seep_config == NULL)
- copy_info(&info, "No Serial EEPROM\n");
+ seq_puts(m, "No Serial EEPROM\n");
else {
- copy_info(&info, "Serial EEPROM:\n");
+ seq_puts(m, "Serial EEPROM:\n");
for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) {
if (((i % 8) == 0) && (i != 0)) {
- copy_info(&info, "\n");
+ seq_putc(m, '\n');
}
- copy_info(&info, "0x%.4x ",
+ seq_printf(m, "0x%.4x ",
((uint16_t*)ahd->seep_config)[i]);
}
- copy_info(&info, "\n");
+ seq_putc(m, '\n');
}
- copy_info(&info, "\n");
+ seq_putc(m, '\n');
if ((ahd->features & AHD_WIDE) == 0)
max_targ = 8;
for (i = 0; i < max_targ; i++) {
- ahd_dump_target_state(ahd, &info, ahd->our_id, 'A',
+ ahd_dump_target_state(ahd, m, ahd->our_id, 'A',
/*target_id*/i);
}
- retval = info.pos > info.offset ? info.pos - info.offset : 0;
-done:
- return (retval);
+ return 0;
}
diff --git a/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
index cdcead071ef6..ddcd5a7701ac 100644
--- a/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
+++ b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
@@ -13,13 +13,6 @@ typedef struct ahd_reg_parse_entry {
} ahd_reg_parse_entry_t;
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_mode_ptr_print;
-#else
-#define ahd_mode_ptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "MODE_PTR", 0x00, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_intstat_print;
#else
#define ahd_intstat_print(regvalue, cur_col, wrap) \
@@ -27,27 +20,6 @@ ahd_reg_print_t ahd_intstat_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_seqintcode_print;
-#else
-#define ahd_seqintcode_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SEQINTCODE", 0x02, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_error_print;
-#else
-#define ahd_error_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ERROR", 0x04, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_hescb_qoff_print;
-#else
-#define ahd_hescb_qoff_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "HESCB_QOFF", 0x08, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_hs_mailbox_print;
#else
#define ahd_hs_mailbox_print(regvalue, cur_col, wrap) \
@@ -62,27 +34,6 @@ ahd_reg_print_t ahd_seqintstat_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrseqintstat_print;
-#else
-#define ahd_clrseqintstat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRSEQINTSTAT", 0x0c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_swtimer_print;
-#else
-#define ahd_swtimer_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SWTIMER", 0x0e, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_sescb_qoff_print;
-#else
-#define ahd_sescb_qoff_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SESCB_QOFF", 0x12, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_intctl_print;
#else
#define ahd_intctl_print(regvalue, cur_col, wrap) \
@@ -111,111 +62,6 @@ ahd_reg_print_t ahd_sg_cache_shadow_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqin_print;
-#else
-#define ahd_lqin_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQIN", 0x20, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lunptr_print;
-#else
-#define ahd_lunptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LUNPTR", 0x22, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_cmdlenptr_print;
-#else
-#define ahd_cmdlenptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CMDLENPTR", 0x25, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_attrptr_print;
-#else
-#define ahd_attrptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ATTRPTR", 0x26, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_flagptr_print;
-#else
-#define ahd_flagptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "FLAGPTR", 0x27, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_cmdptr_print;
-#else
-#define ahd_cmdptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CMDPTR", 0x28, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_qnextptr_print;
-#else
-#define ahd_qnextptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "QNEXTPTR", 0x29, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_abrtbyteptr_print;
-#else
-#define ahd_abrtbyteptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ABRTBYTEPTR", 0x2b, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_abrtbitptr_print;
-#else
-#define ahd_abrtbitptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ABRTBITPTR", 0x2c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lunlen_print;
-#else
-#define ahd_lunlen_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LUNLEN", 0x30, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_cdblimit_print;
-#else
-#define ahd_cdblimit_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CDBLIMIT", 0x31, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_maxcmd_print;
-#else
-#define ahd_maxcmd_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "MAXCMD", 0x32, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_maxcmdcnt_print;
-#else
-#define ahd_maxcmdcnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "MAXCMDCNT", 0x33, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqctl1_print;
-#else
-#define ahd_lqctl1_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQCTL1", 0x38, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqctl2_print;
-#else
-#define ahd_lqctl2_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQCTL2", 0x39, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_scsiseq0_print;
#else
#define ahd_scsiseq0_print(regvalue, cur_col, wrap) \
@@ -230,13 +76,6 @@ ahd_reg_print_t ahd_scsiseq1_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_sxfrctl0_print;
-#else
-#define ahd_sxfrctl0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SXFRCTL0", 0x3c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_dffstat_print;
#else
#define ahd_dffstat_print(regvalue, cur_col, wrap) \
@@ -244,13 +83,6 @@ ahd_reg_print_t ahd_dffstat_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_multargid_print;
-#else
-#define ahd_multargid_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "MULTARGID", 0x40, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_scsisigi_print;
#else
#define ahd_scsisigi_print(regvalue, cur_col, wrap) \
@@ -265,13 +97,6 @@ ahd_reg_print_t ahd_scsiphase_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scsidat_print;
-#else
-#define ahd_scsidat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCSIDAT", 0x44, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_scsibus_print;
#else
#define ahd_scsibus_print(regvalue, cur_col, wrap) \
@@ -279,13 +104,6 @@ ahd_reg_print_t ahd_scsibus_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_targidin_print;
-#else
-#define ahd_targidin_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "TARGIDIN", 0x48, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_selid_print;
#else
#define ahd_selid_print(regvalue, cur_col, wrap) \
@@ -293,10 +111,10 @@ ahd_reg_print_t ahd_selid_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_sblkctl_print;
+ahd_reg_print_t ahd_simode0_print;
#else
-#define ahd_sblkctl_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SBLKCTL", 0x4a, regvalue, cur_col, wrap)
+#define ahd_simode0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE0", 0x4b, regvalue, cur_col, wrap)
#endif
#if AIC_DEBUG_REGISTERS
@@ -307,13 +125,6 @@ ahd_reg_print_t ahd_sstat0_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_simode0_print;
-#else
-#define ahd_simode0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SIMODE0", 0x4b, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_sstat1_print;
#else
#define ahd_sstat1_print(regvalue, cur_col, wrap) \
@@ -328,13 +139,6 @@ ahd_reg_print_t ahd_sstat2_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrsint2_print;
-#else
-#define ahd_clrsint2_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRSINT2", 0x4d, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_perrdiag_print;
#else
#define ahd_perrdiag_print(regvalue, cur_col, wrap) \
@@ -342,13 +146,6 @@ ahd_reg_print_t ahd_perrdiag_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqistate_print;
-#else
-#define ahd_lqistate_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQISTATE", 0x4e, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_soffcnt_print;
#else
#define ahd_soffcnt_print(regvalue, cur_col, wrap) \
@@ -356,13 +153,6 @@ ahd_reg_print_t ahd_soffcnt_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqostate_print;
-#else
-#define ahd_lqostate_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQOSTATE", 0x4f, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_lqistat0_print;
#else
#define ahd_lqistat0_print(regvalue, cur_col, wrap) \
@@ -370,27 +160,6 @@ ahd_reg_print_t ahd_lqistat0_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrlqiint0_print;
-#else
-#define ahd_clrlqiint0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRLQIINT0", 0x50, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqimode0_print;
-#else
-#define ahd_lqimode0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQIMODE0", 0x50, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqimode1_print;
-#else
-#define ahd_lqimode1_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQIMODE1", 0x51, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_lqistat1_print;
#else
#define ahd_lqistat1_print(regvalue, cur_col, wrap) \
@@ -398,13 +167,6 @@ ahd_reg_print_t ahd_lqistat1_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrlqiint1_print;
-#else
-#define ahd_clrlqiint1_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRLQIINT1", 0x51, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_lqistat2_print;
#else
#define ahd_lqistat2_print(regvalue, cur_col, wrap) \
@@ -419,20 +181,6 @@ ahd_reg_print_t ahd_sstat3_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_simode3_print;
-#else
-#define ahd_simode3_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SIMODE3", 0x53, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrsint3_print;
-#else
-#define ahd_clrsint3_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRSINT3", 0x53, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_lqostat0_print;
#else
#define ahd_lqostat0_print(regvalue, cur_col, wrap) \
@@ -440,27 +188,6 @@ ahd_reg_print_t ahd_lqostat0_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrlqoint0_print;
-#else
-#define ahd_clrlqoint0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRLQOINT0", 0x54, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqomode0_print;
-#else
-#define ahd_lqomode0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQOMODE0", 0x54, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqomode1_print;
-#else
-#define ahd_lqomode1_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQOMODE1", 0x55, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_lqostat1_print;
#else
#define ahd_lqostat1_print(regvalue, cur_col, wrap) \
@@ -468,13 +195,6 @@ ahd_reg_print_t ahd_lqostat1_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrlqoint1_print;
-#else
-#define ahd_clrlqoint1_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRLQOINT1", 0x55, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_lqostat2_print;
#else
#define ahd_lqostat2_print(regvalue, cur_col, wrap) \
@@ -482,13 +202,6 @@ ahd_reg_print_t ahd_lqostat2_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_os_space_cnt_print;
-#else
-#define ahd_os_space_cnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "OS_SPACE_CNT", 0x56, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_simode1_print;
#else
#define ahd_simode1_print(regvalue, cur_col, wrap) \
@@ -496,13 +209,6 @@ ahd_reg_print_t ahd_simode1_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_gsfifo_print;
-#else
-#define ahd_gsfifo_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "GSFIFO", 0x58, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_dffsxfrctl_print;
#else
#define ahd_dffsxfrctl_print(regvalue, cur_col, wrap) \
@@ -510,27 +216,6 @@ ahd_reg_print_t ahd_dffsxfrctl_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lqoscsctl_print;
-#else
-#define ahd_lqoscsctl_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LQOSCSCTL", 0x5a, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_nextscb_print;
-#else
-#define ahd_nextscb_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NEXTSCB", 0x5a, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_clrseqintsrc_print;
-#else
-#define ahd_clrseqintsrc_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CLRSEQINTSRC", 0x5b, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_seqintsrc_print;
#else
#define ahd_seqintsrc_print(regvalue, cur_col, wrap) \
@@ -538,13 +223,6 @@ ahd_reg_print_t ahd_seqintsrc_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_currscb_print;
-#else
-#define ahd_currscb_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CURRSCB", 0x5c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_seqimode_print;
#else
#define ahd_seqimode_print(regvalue, cur_col, wrap) \
@@ -559,90 +237,6 @@ ahd_reg_print_t ahd_mdffstat_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_lastscb_print;
-#else
-#define ahd_lastscb_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LASTSCB", 0x5e, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_negoaddr_print;
-#else
-#define ahd_negoaddr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NEGOADDR", 0x60, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_negperiod_print;
-#else
-#define ahd_negperiod_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NEGPERIOD", 0x61, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_negoffset_print;
-#else
-#define ahd_negoffset_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NEGOFFSET", 0x62, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_negppropts_print;
-#else
-#define ahd_negppropts_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NEGPPROPTS", 0x63, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_negconopts_print;
-#else
-#define ahd_negconopts_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NEGCONOPTS", 0x64, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_annexcol_print;
-#else
-#define ahd_annexcol_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ANNEXCOL", 0x65, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_annexdat_print;
-#else
-#define ahd_annexdat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ANNEXDAT", 0x66, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scschkn_print;
-#else
-#define ahd_scschkn_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCSCHKN", 0x66, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_iownid_print;
-#else
-#define ahd_iownid_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "IOWNID", 0x67, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_shcnt_print;
-#else
-#define ahd_shcnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SHCNT", 0x68, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_townid_print;
-#else
-#define ahd_townid_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "TOWNID", 0x69, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_seloid_print;
#else
#define ahd_seloid_print(regvalue, cur_col, wrap) \
@@ -650,90 +244,6 @@ ahd_reg_print_t ahd_seloid_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scbhaddr_print;
-#else
-#define ahd_scbhaddr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCBHADDR", 0x7c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_sghaddr_print;
-#else
-#define ahd_sghaddr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SGHADDR", 0x7c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scbhcnt_print;
-#else
-#define ahd_scbhcnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCBHCNT", 0x84, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_sghcnt_print;
-#else
-#define ahd_sghcnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SGHCNT", 0x84, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_pcixctl_print;
-#else
-#define ahd_pcixctl_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "PCIXCTL", 0x93, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_dchspltstat0_print;
-#else
-#define ahd_dchspltstat0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "DCHSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_dchspltstat1_print;
-#else
-#define ahd_dchspltstat1_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "DCHSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_sgspltstat0_print;
-#else
-#define ahd_sgspltstat0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SGSPLTSTAT0", 0x9e, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_sgspltstat1_print;
-#else
-#define ahd_sgspltstat1_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SGSPLTSTAT1", 0x9f, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_df0pcistat_print;
-#else
-#define ahd_df0pcistat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "DF0PCISTAT", 0xa0, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_reg0_print;
-#else
-#define ahd_reg0_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "REG0", 0xa0, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_reg_isr_print;
-#else
-#define ahd_reg_isr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "REG_ISR", 0xa4, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_sg_state_print;
#else
#define ahd_sg_state_print(regvalue, cur_col, wrap) \
@@ -741,27 +251,6 @@ ahd_reg_print_t ahd_sg_state_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_targpcistat_print;
-#else
-#define ahd_targpcistat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "TARGPCISTAT", 0xa7, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scbautoptr_print;
-#else
-#define ahd_scbautoptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCBAUTOPTR", 0xab, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_ccscbaddr_print;
-#else
-#define ahd_ccscbaddr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CCSCBADDR", 0xac, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_ccscbctl_print;
#else
#define ahd_ccscbctl_print(regvalue, cur_col, wrap) \
@@ -776,69 +265,6 @@ ahd_reg_print_t ahd_ccsgctl_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_ccscbram_print;
-#else
-#define ahd_ccscbram_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CCSCBRAM", 0xb0, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_brddat_print;
-#else
-#define ahd_brddat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "BRDDAT", 0xb8, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_seeadr_print;
-#else
-#define ahd_seeadr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SEEADR", 0xba, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_seedat_print;
-#else
-#define ahd_seedat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SEEDAT", 0xbc, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_seectl_print;
-#else
-#define ahd_seectl_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SEECTL", 0xbe, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_seestat_print;
-#else
-#define ahd_seestat_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SEESTAT", 0xbe, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_dspdatactl_print;
-#else
-#define ahd_dspdatactl_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "DSPDATACTL", 0xc1, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_dspselect_print;
-#else
-#define ahd_dspselect_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "DSPSELECT", 0xc4, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_wrtbiasctl_print;
-#else
-#define ahd_wrtbiasctl_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "WRTBIASCTL", 0xc5, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_seqctl0_print;
#else
#define ahd_seqctl0_print(regvalue, cur_col, wrap) \
@@ -853,62 +279,6 @@ ahd_reg_print_t ahd_seqintctl_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_prgmcnt_print;
-#else
-#define ahd_prgmcnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "PRGMCNT", 0xde, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_none_print;
-#else
-#define ahd_none_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NONE", 0xea, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_intvec1_addr_print;
-#else
-#define ahd_intvec1_addr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "INTVEC1_ADDR", 0xf4, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_curaddr_print;
-#else
-#define ahd_curaddr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CURADDR", 0xf4, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_intvec2_addr_print;
-#else
-#define ahd_intvec2_addr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "INTVEC2_ADDR", 0xf6, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_longjmp_addr_print;
-#else
-#define ahd_longjmp_addr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LONGJMP_ADDR", 0xf8, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_accum_save_print;
-#else
-#define ahd_accum_save_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ACCUM_SAVE", 0xfa, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_waiting_scb_tails_print;
-#else
-#define ahd_waiting_scb_tails_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "WAITING_SCB_TAILS", 0x100, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_sram_base_print;
#else
#define ahd_sram_base_print(regvalue, cur_col, wrap) \
@@ -916,62 +286,6 @@ ahd_reg_print_t ahd_sram_base_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_waiting_tid_head_print;
-#else
-#define ahd_waiting_tid_head_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "WAITING_TID_HEAD", 0x120, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_waiting_tid_tail_print;
-#else
-#define ahd_waiting_tid_tail_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "WAITING_TID_TAIL", 0x122, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_next_queued_scb_addr_print;
-#else
-#define ahd_next_queued_scb_addr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "NEXT_QUEUED_SCB_ADDR", 0x124, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_complete_scb_head_print;
-#else
-#define ahd_complete_scb_head_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "COMPLETE_SCB_HEAD", 0x128, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_complete_scb_dmainprog_head_print;
-#else
-#define ahd_complete_scb_dmainprog_head_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "COMPLETE_SCB_DMAINPROG_HEAD", 0x12a, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_complete_dma_scb_head_print;
-#else
-#define ahd_complete_dma_scb_head_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_HEAD", 0x12c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_complete_dma_scb_tail_print;
-#else
-#define ahd_complete_dma_scb_tail_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_TAIL", 0x12e, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_complete_on_qfreeze_head_print;
-#else
-#define ahd_complete_on_qfreeze_head_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "COMPLETE_ON_QFREEZE_HEAD", 0x130, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_qfreeze_count_print;
#else
#define ahd_qfreeze_count_print(regvalue, cur_col, wrap) \
@@ -993,13 +307,6 @@ ahd_reg_print_t ahd_saved_mode_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_msg_out_print;
-#else
-#define ahd_msg_out_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "MSG_OUT", 0x137, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_seq_flags_print;
#else
#define ahd_seq_flags_print(regvalue, cur_col, wrap) \
@@ -1014,48 +321,6 @@ ahd_reg_print_t ahd_lastphase_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_qoutfifo_entry_valid_tag_print;
-#else
-#define ahd_qoutfifo_entry_valid_tag_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "QOUTFIFO_ENTRY_VALID_TAG", 0x13d, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_kernel_tqinpos_print;
-#else
-#define ahd_kernel_tqinpos_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "KERNEL_TQINPOS", 0x13e, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_qoutfifo_next_addr_print;
-#else
-#define ahd_qoutfifo_next_addr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "QOUTFIFO_NEXT_ADDR", 0x144, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_last_msg_print;
-#else
-#define ahd_last_msg_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LAST_MSG", 0x14a, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scsiseq_template_print;
-#else
-#define ahd_scsiseq_template_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCSISEQ_TEMPLATE", 0x14b, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_initiator_tag_print;
-#else
-#define ahd_initiator_tag_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "INITIATOR_TAG", 0x14c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_seq_flags2_print;
#else
#define ahd_seq_flags2_print(regvalue, cur_col, wrap) \
@@ -1063,62 +328,6 @@ ahd_reg_print_t ahd_seq_flags2_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_allocfifo_scbptr_print;
-#else
-#define ahd_allocfifo_scbptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "ALLOCFIFO_SCBPTR", 0x14e, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_int_coalescing_timer_print;
-#else
-#define ahd_int_coalescing_timer_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "INT_COALESCING_TIMER", 0x150, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_int_coalescing_maxcmds_print;
-#else
-#define ahd_int_coalescing_maxcmds_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "INT_COALESCING_MAXCMDS", 0x152, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_int_coalescing_mincmds_print;
-#else
-#define ahd_int_coalescing_mincmds_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "INT_COALESCING_MINCMDS", 0x153, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_cmds_pending_print;
-#else
-#define ahd_cmds_pending_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CMDS_PENDING", 0x154, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_int_coalescing_cmdcount_print;
-#else
-#define ahd_int_coalescing_cmdcount_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "INT_COALESCING_CMDCOUNT", 0x156, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_local_hs_mailbox_print;
-#else
-#define ahd_local_hs_mailbox_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "LOCAL_HS_MAILBOX", 0x157, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_cmdsize_table_print;
-#else
-#define ahd_cmdsize_table_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "CMDSIZE_TABLE", 0x158, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_mk_message_scb_print;
#else
#define ahd_mk_message_scb_print(regvalue, cur_col, wrap) \
@@ -1140,27 +349,6 @@ ahd_reg_print_t ahd_scb_base_print;
#endif
#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_residual_datacnt_print;
-#else
-#define ahd_scb_residual_datacnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_RESIDUAL_DATACNT", 0x180, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_sense_busaddr_print;
-#else
-#define ahd_scb_sense_busaddr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_SENSE_BUSADDR", 0x18c, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_tag_print;
-#else
-#define ahd_scb_tag_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_TAG", 0x190, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
ahd_reg_print_t ahd_scb_control_print;
#else
#define ahd_scb_control_print(regvalue, cur_col, wrap) \
@@ -1174,69 +362,6 @@ ahd_reg_print_t ahd_scb_scsiid_print;
ahd_print_register(NULL, 0, "SCB_SCSIID", 0x193, regvalue, cur_col, wrap)
#endif
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_lun_print;
-#else
-#define ahd_scb_lun_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_LUN", 0x194, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_task_attribute_print;
-#else
-#define ahd_scb_task_attribute_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_TASK_ATTRIBUTE", 0x195, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_task_management_print;
-#else
-#define ahd_scb_task_management_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_TASK_MANAGEMENT", 0x197, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_dataptr_print;
-#else
-#define ahd_scb_dataptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_DATAPTR", 0x198, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_datacnt_print;
-#else
-#define ahd_scb_datacnt_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_DATACNT", 0x1a0, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_sgptr_print;
-#else
-#define ahd_scb_sgptr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_SGPTR", 0x1a4, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_busaddr_print;
-#else
-#define ahd_scb_busaddr_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_BUSADDR", 0x1a8, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_next2_print;
-#else
-#define ahd_scb_next2_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_NEXT2", 0x1ae, regvalue, cur_col, wrap)
-#endif
-
-#if AIC_DEBUG_REGISTERS
-ahd_reg_print_t ahd_scb_disconnected_lists_print;
-#else
-#define ahd_scb_disconnected_lists_print(regvalue, cur_col, wrap) \
- ahd_print_register(NULL, 0, "SCB_DISCONNECTED_LISTS", 0x1b8, regvalue, cur_col, wrap)
-#endif
-
#define MODE_PTR 0x00
#define DST_MODE 0x70
@@ -1292,15 +417,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CLRCMDINT 0x02
#define CLRSPLTINT 0x01
-#define ERROR 0x04
-#define CIOPARERR 0x80
-#define CIOACCESFAIL 0x40
-#define MPARERR 0x20
-#define DPARERR 0x10
-#define SQPARERR 0x08
-#define ILLOPCODE 0x04
-#define DSCTMOUT 0x02
-
#define CLRERR 0x04
#define CLRCIOPARERR 0x80
#define CLRCIOACCESFAIL 0x40
@@ -1310,6 +426,15 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CLRILLOPCODE 0x04
#define CLRDSCTMOUT 0x02
+#define ERROR 0x04
+#define CIOPARERR 0x80
+#define CIOACCESFAIL 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define DSCTMOUT 0x02
+
#define HCNTRL 0x05
#define SEQ_RESET 0x80
#define POWRDN 0x40
@@ -1404,22 +529,22 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define FIFOFULL 0x02
#define FIFOEMP 0x01
-#define SG_CACHE_SHADOW 0x1b
-#define ODD_SEG 0x04
-#define LAST_SEG 0x02
-#define LAST_SEG_DONE 0x01
-
#define ARBCTL 0x1b
#define RESET_HARB 0x80
#define RETRY_SWEN 0x08
#define USE_TIME 0x07
-#define SG_CACHE_PRE 0x1b
+#define SG_CACHE_SHADOW 0x1b
+#define ODD_SEG 0x04
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
-#define LQIN 0x20
+#define SG_CACHE_PRE 0x1b
#define TYPEPTR 0x20
+#define LQIN 0x20
+
#define TAGPTR 0x21
#define LUNPTR 0x22
@@ -1479,14 +604,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define SINGLECMD 0x02
#define ABORTPENDING 0x01
-#define SCSBIST0 0x39
-#define GSBISTERR 0x40
-#define GSBISTDONE 0x20
-#define GSBISTRUN 0x10
-#define OSBISTERR 0x04
-#define OSBISTDONE 0x02
-#define OSBISTRUN 0x01
-
#define LQCTL2 0x39
#define LQIRETRY 0x80
#define LQICONTINUE 0x40
@@ -1497,10 +614,13 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define LQOTOIDLE 0x02
#define LQOPAUSE 0x01
-#define SCSBIST1 0x3a
-#define NTBISTERR 0x04
-#define NTBISTDONE 0x02
-#define NTBISTRUN 0x01
+#define SCSBIST0 0x39
+#define GSBISTERR 0x40
+#define GSBISTDONE 0x20
+#define GSBISTRUN 0x10
+#define OSBISTERR 0x04
+#define OSBISTDONE 0x02
+#define OSBISTRUN 0x01
#define SCSISEQ0 0x3a
#define TEMODEO 0x80
@@ -1509,8 +629,15 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define FORCEBUSFREE 0x10
#define SCSIRSTO 0x01
+#define SCSBIST1 0x3a
+#define NTBISTERR 0x04
+#define NTBISTDONE 0x02
+#define NTBISTRUN 0x01
+
#define SCSISEQ1 0x3b
+#define BUSINITID 0x3c
+
#define SXFRCTL0 0x3c
#define DFON 0x80
#define DFPEXP 0x40
@@ -1519,8 +646,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define DLCOUNT 0x3c
-#define BUSINITID 0x3c
-
#define SXFRCTL1 0x3d
#define BITBUCKET 0x80
#define ENSACHK 0x40
@@ -1545,6 +670,8 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CURRFIFO_1 0x01
#define CURRFIFO_0 0x00
+#define MULTARGID 0x40
+
#define SCSISIGO 0x40
#define CDO 0x80
#define IOO 0x40
@@ -1555,8 +682,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define REQO 0x02
#define ACKO 0x01
-#define MULTARGID 0x40
-
#define SCSISIGI 0x41
#define ATNI 0x10
#define SELI 0x08
@@ -1603,14 +728,14 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define ENAB20 0x04
#define SELWIDE 0x02
-#define CLRSINT0 0x4b
-#define CLRSELDO 0x40
-#define CLRSELDI 0x20
-#define CLRSELINGO 0x10
-#define CLRIOERR 0x08
-#define CLROVERRUN 0x04
-#define CLRSPIORDY 0x02
-#define CLRARBDO 0x01
+#define SIMODE0 0x4b
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENOVERRUN 0x04
+#define ENSPIORDY 0x02
+#define ENARBDO 0x01
#define SSTAT0 0x4b
#define TARGET 0x80
@@ -1622,23 +747,14 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define SPIORDY 0x02
#define ARBDO 0x01
-#define SIMODE0 0x4b
-#define ENSELDO 0x40
-#define ENSELDI 0x20
-#define ENSELINGO 0x10
-#define ENIOERR 0x08
-#define ENOVERRUN 0x04
-#define ENSPIORDY 0x02
-#define ENARBDO 0x01
-
-#define CLRSINT1 0x4c
-#define CLRSELTIMEO 0x80
-#define CLRATNO 0x40
-#define CLRSCSIRSTI 0x20
-#define CLRBUSFREE 0x08
-#define CLRSCSIPERR 0x04
-#define CLRSTRB2FAST 0x02
-#define CLRREQINIT 0x01
+#define CLRSINT0 0x4b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRIOERR 0x08
+#define CLROVERRUN 0x04
+#define CLRSPIORDY 0x02
+#define CLRARBDO 0x01
#define SSTAT1 0x4c
#define SELTO 0x80
@@ -1650,6 +766,20 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define STRB2FAST 0x02
#define REQINIT 0x01
+#define CLRSINT1 0x4c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRSTRB2FAST 0x02
+#define CLRREQINIT 0x01
+
+#define SIMODE2 0x4d
+#define ENWIDE_RES 0x04
+#define ENSDONE 0x02
+#define ENDMADONE 0x01
+
#define SSTAT2 0x4d
#define BUSFREETIME 0xc0
#define NONPACKREQ 0x20
@@ -1662,11 +792,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define BUSFREE_DFF0 0x80
#define BUSFREE_LQO 0x40
-#define SIMODE2 0x4d
-#define ENWIDE_RES 0x04
-#define ENSDONE 0x02
-#define ENDMADONE 0x01
-
#define CLRSINT2 0x4d
#define CLRNONPACKREQ 0x20
#define CLRWIDE_RES 0x04
@@ -1685,10 +810,10 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define LQISTATE 0x4e
-#define SOFFCNT 0x4f
-
#define LQOSTATE 0x4f
+#define SOFFCNT 0x4f
+
#define LQISTAT0 0x50
#define LQIATNQAS 0x20
#define LQICRCT1 0x10
@@ -1697,14 +822,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define LQIATNLQ 0x02
#define LQIATNCMD 0x01
-#define CLRLQIINT0 0x50
-#define CLRLQIATNQAS 0x20
-#define CLRLQICRCT1 0x10
-#define CLRLQICRCT2 0x08
-#define CLRLQIBADLQT 0x04
-#define CLRLQIATNLQ 0x02
-#define CLRLQIATNCMD 0x01
-
#define LQIMODE0 0x50
#define ENLQIATNQASK 0x20
#define ENLQICRCT1 0x10
@@ -1713,6 +830,14 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define ENLQIATNLQ 0x02
#define ENLQIATNCMD 0x01
+#define CLRLQIINT0 0x50
+#define CLRLQIATNQAS 0x20
+#define CLRLQICRCT1 0x10
+#define CLRLQICRCT2 0x08
+#define CLRLQIBADLQT 0x04
+#define CLRLQIATNLQ 0x02
+#define CLRLQIATNCMD 0x01
+
#define LQIMODE1 0x51
#define ENLQIPHASE_LQ 0x80
#define ENLQIPHASE_NLQ 0x40
@@ -1753,25 +878,18 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define LQISTOPCMD 0x02
#define LQIGSAVAIL 0x01
-#define SSTAT3 0x53
-#define NTRAMPERR 0x02
-#define OSRAMPERR 0x01
-
#define SIMODE3 0x53
#define ENNTRAMPERR 0x02
#define ENOSRAMPERR 0x01
+#define SSTAT3 0x53
+#define NTRAMPERR 0x02
+#define OSRAMPERR 0x01
+
#define CLRSINT3 0x53
#define CLRNTRAMPERR 0x02
#define CLROSRAMPERR 0x01
-#define LQOSTAT0 0x54
-#define LQOTARGSCBPERR 0x10
-#define LQOSTOPT2 0x08
-#define LQOATNLQ 0x04
-#define LQOATNPKT 0x02
-#define LQOTCRC 0x01
-
#define CLRLQOINT0 0x54
#define CLRLQOTARGSCBPERR 0x10
#define CLRLQOSTOPT2 0x08
@@ -1779,6 +897,13 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CLRLQOATNPKT 0x02
#define CLRLQOTCRC 0x01
+#define LQOSTAT0 0x54
+#define LQOTARGSCBPERR 0x10
+#define LQOSTOPT2 0x08
+#define LQOATNLQ 0x04
+#define LQOATNPKT 0x02
+#define LQOTCRC 0x01
+
#define LQOMODE0 0x54
#define ENLQOTARGSCBPERR 0x10
#define ENLQOSTOPT2 0x08
@@ -1793,13 +918,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define ENLQOBUSFREE 0x02
#define ENLQOPHACHGINPKT 0x01
-#define LQOSTAT1 0x55
-#define LQOINITSCBPERR 0x10
-#define LQOSTOPI2 0x08
-#define LQOBADQAS 0x04
-#define LQOBUSFREE 0x02
-#define LQOPHACHGINPKT 0x01
-
#define CLRLQOINT1 0x55
#define CLRLQOINITSCBPERR 0x10
#define CLRLQOSTOPI2 0x08
@@ -1807,6 +925,13 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CLRLQOBUSFREE 0x02
#define CLRLQOPHACHGINPKT 0x01
+#define LQOSTAT1 0x55
+#define LQOINITSCBPERR 0x10
+#define LQOSTOPI2 0x08
+#define LQOBADQAS 0x04
+#define LQOBUSFREE 0x02
+#define LQOPHACHGINPKT 0x01
+
#define LQOSTAT2 0x56
#define LQOPKT 0xe0
#define LQOWAITFIFO 0x10
@@ -1859,8 +984,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CFG4ICMD 0x02
#define CFG4TCMD 0x01
-#define CURRSCB 0x5c
-
#define SEQIMODE 0x5c
#define ENCTXTDONE 0x40
#define ENSAVEPTRS 0x20
@@ -1870,6 +993,11 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define ENCFG4ICMD 0x02
#define ENCFG4TCMD 0x01
+#define CURRSCB 0x5c
+
+#define CRCCONTROL 0x5d
+#define CRCVALCHKEN 0x40
+
#define MDFFSTAT 0x5d
#define SHCNTNEGATIVE 0x40
#define SHCNTMINUS1 0x20
@@ -1879,34 +1007,31 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define DATAINFIFO 0x02
#define FIFOFREE 0x01
-#define CRCCONTROL 0x5d
-#define CRCVALCHKEN 0x40
-
#define DFFTAG 0x5e
-#define LASTSCB 0x5e
-
#define SCSITEST 0x5e
#define CNTRTEST 0x08
#define SEL_TXPLL_DEBUG 0x04
+#define LASTSCB 0x5e
+
#define IOPDNCTL 0x5f
#define DISABLE_OE 0x80
#define PDN_IDIST 0x04
#define PDN_DIFFSENSE 0x01
-#define SHADDR 0x60
+#define DGRPCRCI 0x60
#define NEGOADDR 0x60
-#define DGRPCRCI 0x60
+#define SHADDR 0x60
#define NEGPERIOD 0x61
-#define PACKCRCI 0x62
-
#define NEGOFFSET 0x62
+#define PACKCRCI 0x62
+
#define NEGPPROPTS 0x63
#define PPROPT_PACE 0x08
#define PPROPT_QAS 0x04
@@ -1942,16 +1067,18 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define SHCNT 0x68
-#define TOWNID 0x69
-
#define PLL960CTL1 0x69
+#define TOWNID 0x69
+
#define PLL960CNT0 0x6a
#define XSIG 0x6a
#define SELOID 0x6b
+#define FAIRNESS 0x6c
+
#define PLL400CTL0 0x6c
#define PLL_VCOSEL 0x80
#define PLL_PWDN 0x40
@@ -1961,8 +1088,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define PLL_DLPF 0x02
#define PLL_ENFBM 0x01
-#define FAIRNESS 0x6c
-
#define PLL400CTL1 0x6d
#define PLL_CNTEN 0x80
#define PLL_CNTCLR 0x40
@@ -1974,25 +1099,25 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define HADDR 0x70
+#define HODMAADR 0x70
+
#define PLLDELAY 0x70
#define SPLIT_DROP_REQ 0x80
-#define HODMAADR 0x70
+#define HCNT 0x78
#define HODMACNT 0x78
-#define HCNT 0x78
-
#define HODMAEN 0x7a
-#define SCBHADDR 0x7c
-
#define SGHADDR 0x7c
-#define SCBHCNT 0x84
+#define SCBHADDR 0x7c
#define SGHCNT 0x84
+#define SCBHCNT 0x84
+
#define DFF_THRSH 0x88
#define WR_DFTHRSH 0x70
#define RD_DFTHRSH 0x07
@@ -2025,6 +1150,10 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CMCRXMSG0 0x90
+#define OVLYRXMSG0 0x90
+
+#define DCHRXMSG0 0x90
+
#define ROENABLE 0x90
#define MSIROEN 0x20
#define OVLYROEN 0x10
@@ -2033,11 +1162,11 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define DCH1ROEN 0x02
#define DCH0ROEN 0x01
-#define OVLYRXMSG0 0x90
+#define OVLYRXMSG1 0x91
-#define DCHRXMSG0 0x90
+#define CMCRXMSG1 0x91
-#define OVLYRXMSG1 0x91
+#define DCHRXMSG1 0x91
#define NSENABLE 0x91
#define MSINSEN 0x20
@@ -2047,10 +1176,6 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define DCH1NSEN 0x02
#define DCH0NSEN 0x01
-#define CMCRXMSG1 0x91
-
-#define DCHRXMSG1 0x91
-
#define DCHRXMSG2 0x92
#define CMCRXMSG2 0x92
@@ -2074,24 +1199,24 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define TSCSERREN 0x02
#define CMPABCDIS 0x01
+#define CMCSEQBCNT 0x94
+
#define OVLYSEQBCNT 0x94
#define DCHSEQBCNT 0x94
-#define CMCSEQBCNT 0x94
-
-#define CMCSPLTSTAT0 0x96
-
#define DCHSPLTSTAT0 0x96
#define OVLYSPLTSTAT0 0x96
-#define CMCSPLTSTAT1 0x97
+#define CMCSPLTSTAT0 0x96
#define OVLYSPLTSTAT1 0x97
#define DCHSPLTSTAT1 0x97
+#define CMCSPLTSTAT1 0x97
+
#define SGRXMSG0 0x98
#define CDNUM 0xf8
#define CFNUM 0x07
@@ -2119,18 +1244,15 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define TAG_NUM 0x1f
#define RLXORD 0x10
-#define SGSEQBCNT 0x9c
-
#define SLVSPLTOUTATTR0 0x9c
#define LOWER_BCNT 0xff
+#define SGSEQBCNT 0x9c
+
#define SLVSPLTOUTATTR1 0x9d
#define CMPLT_DNUM 0xf8
#define CMPLT_FNUM 0x07
-#define SLVSPLTOUTATTR2 0x9e
-#define CMPLT_BNUM 0xff
-
#define SGSPLTSTAT0 0x9e
#define STAETERM 0x80
#define SCBCERR 0x40
@@ -2141,6 +1263,9 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define RXSCEMSG 0x02
#define RXSPLTRSP 0x01
+#define SLVSPLTOUTATTR2 0x9e
+#define CMPLT_BNUM 0xff
+
#define SGSPLTSTAT1 0x9f
#define RXDATABUCKET 0x01
@@ -2177,14 +1302,14 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CLRPENDMSI 0x08
#define DPR 0x01
+#define DATA_COUNT_ODD 0xa7
+
#define TARGPCISTAT 0xa7
#define DPE 0x80
#define SSE 0x40
#define STA 0x08
#define TWATERR 0x02
-#define DATA_COUNT_ODD 0xa7
-
#define SCBPTR 0xa8
#define CCSCBACNT 0xab
@@ -2196,10 +1321,10 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define CCSGADDR 0xac
-#define CCSCBADR_BK 0xac
-
#define CCSCBADDR 0xac
+#define CCSCBADR_BK 0xac
+
#define CMC_RAMBIST 0xad
#define SG_ELEMENT_SIZE 0x80
#define SCBRAMBIST_FAIL 0x40
@@ -2253,9 +1378,9 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define SEEDAT 0xbc
#define SEECTL 0xbe
+#define SEEOP_EWDS 0x40
#define SEEOP_WALL 0x40
#define SEEOP_EWEN 0x40
-#define SEEOP_EWDS 0x40
#define SEEOPCODE 0x70
#define SEERST 0x02
#define SEESTART 0x01
@@ -2272,25 +1397,25 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define SCBCNT 0xbf
-#define DFWADDR 0xc0
-
#define DSPFLTRCTL 0xc0
#define FLTRDISABLE 0x20
#define EDGESENSE 0x10
#define DSPFCNTSEL 0x0f
+#define DFWADDR 0xc0
+
#define DSPDATACTL 0xc1
#define BYPASSENAB 0x80
#define DESQDIS 0x10
#define RCVROFFSTDIS 0x04
#define XMITOFFSTDIS 0x02
-#define DFRADDR 0xc2
-
#define DSPREQCTL 0xc2
#define MANREQCTL 0xc0
#define MANREQDLY 0x3f
+#define DFRADDR 0xc2
+
#define DSPACKCTL 0xc3
#define MANACKCTL 0xc0
#define MANACKDLY 0x3f
@@ -2311,14 +1436,14 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define WRTBIASCALC 0xc7
-#define RCVRBIASCALC 0xc8
-
#define DFPTRS 0xc8
-#define SKEWCALC 0xc9
+#define RCVRBIASCALC 0xc8
#define DFBKPTR 0xc9
+#define SKEWCALC 0xc9
+
#define DFDBCTL 0xcb
#define DFF_CIO_WR_RDY 0x20
#define DFF_CIO_RD_RDY 0x10
@@ -2403,12 +1528,12 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define ACCUM_SAVE 0xfa
-#define WAITING_SCB_TAILS 0x100
-
#define AHD_PCI_CONFIG_BASE 0x100
#define SRAM_BASE 0x100
+#define WAITING_SCB_TAILS 0x100
+
#define WAITING_TID_HEAD 0x120
#define WAITING_TID_TAIL 0x122
@@ -2437,8 +1562,8 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define PRELOADEN 0x80
#define WIDEODD 0x40
#define SCSIEN 0x20
-#define SDMAEN 0x10
#define SDMAENACK 0x10
+#define SDMAEN 0x10
#define HDMAEN 0x08
#define HDMAENACK 0x08
#define DIRECTION 0x04
@@ -2536,12 +1661,12 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define MK_MESSAGE_SCSIID 0x162
-#define SCB_BASE 0x180
-
#define SCB_RESIDUAL_DATACNT 0x180
#define SCB_CDB_STORE 0x180
#define SCB_HOST_CDB_PTR 0x180
+#define SCB_BASE 0x180
+
#define SCB_RESIDUAL_SGPTR 0x184
#define SG_ADDR_MASK 0xf8
#define SG_OVERRUN_RESID 0x02
@@ -2609,77 +1734,77 @@ ahd_reg_print_t ahd_scb_disconnected_lists_print;
#define SCB_DISCONNECTED_LISTS 0x1b8
+#define STIMESEL_SHIFT 0x03
+#define STIMESEL_MIN 0x18
+#define INVALID_ADDR 0x80
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define AHD_PRECOMP_MASK 0x07
+#define TARGET_DATA_IN 0x01
+#define SEEOP_EWEN_ADDR 0xc0
+#define NUMDSPS 0x14
+#define DST_MODE_SHIFT 0x04
+#define CCSCBADDR_MAX 0x80
+#define AHD_ANNEXCOL_PER_DEV0 0x04
+#define TARGET_CMD_CMPLT 0xfe
+#define SEEOP_WRAL_ADDR 0x40
+#define BUS_8_BIT 0x00
#define AHD_TIMER_MAX_US 0x18ffe7
#define AHD_TIMER_MAX_TICKS 0xffff
#define AHD_SENSE_BUFSIZE 0x100
-#define BUS_8_BIT 0x00
-#define TARGET_CMD_CMPLT 0xfe
-#define SEEOP_WRAL_ADDR 0x40
-#define AHD_AMPLITUDE_DEF 0x07
-#define AHD_PRECOMP_CUTBACK_37 0x07
#define AHD_PRECOMP_SHIFT 0x00
+#define AHD_PRECOMP_CUTBACK_37 0x07
#define AHD_ANNEXCOL_PRECOMP_SLEW 0x04
-#define AHD_TIMER_US_PER_TICK 0x19
-#define SCB_TRANSFER_SIZE_FULL_LUN 0x38
+#define AHD_AMPLITUDE_DEF 0x07
+#define WRTBIASCTL_HP_DEFAULT 0x00
+#define TID_SHIFT 0x04
#define STATUS_QUEUE_FULL 0x28
#define STATUS_BUSY 0x08
-#define MAX_OFFSET_NON_PACED 0x7f
+#define SEEOP_EWDS_ADDR 0x00
+#define SCB_TRANSFER_SIZE_FULL_LUN 0x38
+#define MK_MESSAGE_BIT_OFFSET 0x04
#define MAX_OFFSET_PACED 0xfe
-#define BUS_32_BIT 0x02
+#define MAX_OFFSET_NON_PACED 0x7f
+#define LUNLEN_SINGLE_LEVEL_LUN 0x0f
#define CCSGADDR_MAX 0x80
-#define TID_SHIFT 0x04
-#define MK_MESSAGE_BIT_OFFSET 0x04
-#define WRTBIASCTL_HP_DEFAULT 0x00
-#define SEEOP_EWDS_ADDR 0x00
-#define AHD_AMPLITUDE_SHIFT 0x00
-#define AHD_AMPLITUDE_MASK 0x07
-#define AHD_ANNEXCOL_AMPLITUDE 0x06
-#define AHD_SLEWRATE_DEF_REVA 0x08
+#define B_CURRFIFO_0 0x02
+#define BUS_32_BIT 0x02
+#define AHD_TIMER_US_PER_TICK 0x19
#define AHD_SLEWRATE_SHIFT 0x03
#define AHD_SLEWRATE_MASK 0x78
+#define AHD_SLEWRATE_DEF_REVA 0x08
#define AHD_PRECOMP_CUTBACK_29 0x06
#define AHD_NUM_PER_DEV_ANNEXCOLS 0x04
-#define B_CURRFIFO_0 0x02
-#define LUNLEN_SINGLE_LEVEL_LUN 0x0f
-#define NVRAM_SCB_OFFSET 0x2c
+#define AHD_ANNEXCOL_AMPLITUDE 0x06
+#define AHD_AMPLITUDE_SHIFT 0x00
+#define AHD_AMPLITUDE_MASK 0x07
+#define STIMESEL_BUG_ADJ 0x08
#define STATUS_PKT_SENSE 0xff
-#define CMD_GROUP_CODE_SHIFT 0x05
+#define SRC_MODE_SHIFT 0x00
+#define SEEOP_ERAL_ADDR 0x80
+#define NVRAM_SCB_OFFSET 0x2c
#define MAX_OFFSET_PACED_BUG 0x7f
-#define STIMESEL_BUG_ADJ 0x08
-#define STIMESEL_MIN 0x18
-#define STIMESEL_SHIFT 0x03
#define CCSGRAM_MAXSEGS 0x10
-#define INVALID_ADDR 0x80
-#define SEEOP_ERAL_ADDR 0x80
#define AHD_SLEWRATE_DEF_REVB 0x08
#define AHD_PRECOMP_CUTBACK_17 0x04
-#define AHD_PRECOMP_MASK 0x07
-#define SRC_MODE_SHIFT 0x00
-#define PKT_OVERRUN_BUFSIZE 0x200
#define SCB_TRANSFER_SIZE_1BYTE_LUN 0x30
-#define TARGET_DATA_IN 0x01
-#define HOST_MSG 0xff
+#define PKT_OVERRUN_BUFSIZE 0x200
#define MAX_OFFSET 0xfe
+#define HOST_MSG 0xff
#define BUS_16_BIT 0x01
-#define CCSCBADDR_MAX 0x80
-#define NUMDSPS 0x14
-#define SEEOP_EWEN_ADDR 0xc0
-#define AHD_ANNEXCOL_PER_DEV0 0x04
-#define DST_MODE_SHIFT 0x04
/* Downloaded Constant Definitions */
+#define SG_SIZEOF 0x04
+#define SG_PREFETCH_ALIGN_MASK 0x02
+#define SG_PREFETCH_CNT_LIMIT 0x01
#define CACHELINE_MASK 0x07
#define SCB_TRANSFER_SIZE 0x06
#define PKT_OVERRUN_BUFOFFSET 0x05
-#define SG_SIZEOF 0x04
#define SG_PREFETCH_ADDR_MASK 0x03
-#define SG_PREFETCH_ALIGN_MASK 0x02
-#define SG_PREFETCH_CNT_LIMIT 0x01
#define SG_PREFETCH_CNT 0x00
#define DOWNLOAD_CONST_COUNT 0x08
/* Exported Labels */
-#define LABEL_seq_isr 0x28f
#define LABEL_timer_isr 0x28b
+#define LABEL_seq_isr 0x28f
diff --git a/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
index f5ea715d6ac3..2e0c58905b9e 100644
--- a/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
+++ b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
@@ -234,6 +234,23 @@ ahd_selid_print(u_int regvalue, u_int *cur_col, u_int wrap)
0x49, regvalue, cur_col, wrap));
}
+static const ahd_reg_parse_entry_t SIMODE0_parse_table[] = {
+ { "ENARBDO", 0x01, 0x01 },
+ { "ENSPIORDY", 0x02, 0x02 },
+ { "ENOVERRUN", 0x04, 0x04 },
+ { "ENIOERR", 0x08, 0x08 },
+ { "ENSELINGO", 0x10, 0x10 },
+ { "ENSELDI", 0x20, 0x20 },
+ { "ENSELDO", 0x40, 0x40 }
+};
+
+int
+ahd_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE0_parse_table, 7, "SIMODE0",
+ 0x4b, regvalue, cur_col, wrap));
+}
+
static const ahd_reg_parse_entry_t SSTAT0_parse_table[] = {
{ "ARBDO", 0x01, 0x01 },
{ "SPIORDY", 0x02, 0x02 },
@@ -252,23 +269,6 @@ ahd_sstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
0x4b, regvalue, cur_col, wrap));
}
-static const ahd_reg_parse_entry_t SIMODE0_parse_table[] = {
- { "ENARBDO", 0x01, 0x01 },
- { "ENSPIORDY", 0x02, 0x02 },
- { "ENOVERRUN", 0x04, 0x04 },
- { "ENIOERR", 0x08, 0x08 },
- { "ENSELINGO", 0x10, 0x10 },
- { "ENSELDI", 0x20, 0x20 },
- { "ENSELDO", 0x40, 0x40 }
-};
-
-int
-ahd_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
-{
- return (ahd_print_register(SIMODE0_parse_table, 7, "SIMODE0",
- 0x4b, regvalue, cur_col, wrap));
-}
-
static const ahd_reg_parse_entry_t SSTAT1_parse_table[] = {
{ "REQINIT", 0x01, 0x01 },
{ "STRB2FAST", 0x02, 0x02 },
diff --git a/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
index 4b51e232392f..fd64a950ee44 100644
--- a/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
+++ b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
@@ -1186,5 +1186,4 @@ static const struct cs {
{ 759, 763 }
};
-static const int num_critical_sections = sizeof(critical_sections)
- / sizeof(*critical_sections);
+#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)
diff --git a/drivers/scsi/aic7xxx/aic7xxx.h b/drivers/scsi/aic7xxx/aic7xxx.h
index f695774645c1..20857c213c72 100644
--- a/drivers/scsi/aic7xxx/aic7xxx.h
+++ b/drivers/scsi/aic7xxx/aic7xxx.h
@@ -568,9 +568,6 @@ struct scb {
ahc_io_ctx_t io_ctx;
struct ahc_softc *ahc_softc;
scb_flag flags;
-#ifndef __linux__
- bus_dmamap_t dmamap;
-#endif
struct scb_platform_data *platform_data;
struct sg_map_node *sg_map;
struct ahc_dma_seg *sg_list;
@@ -899,16 +896,11 @@ union ahc_bus_softc {
typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
typedef int (*ahc_bus_chip_init_t)(struct ahc_softc *);
-typedef int (*ahc_bus_suspend_t)(struct ahc_softc *);
-typedef int (*ahc_bus_resume_t)(struct ahc_softc *);
typedef void ahc_callback_t (void *);
struct ahc_softc {
bus_space_tag_t tag;
bus_space_handle_t bsh;
-#ifndef __linux__
- bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */
-#endif
struct scb_data *scb_data;
struct scb *next_queued_scb;
@@ -916,7 +908,7 @@ struct ahc_softc {
/*
* SCBs that have been sent to the controller
*/
- LIST_HEAD(, scb) pending_scbs;
+ BSD_LIST_HEAD(, scb) pending_scbs;
/*
* Counting lock for deferring the release of additional
@@ -949,6 +941,7 @@ struct ahc_softc {
* Platform specific device information.
*/
ahc_dev_softc_t dev_softc;
+ struct device *dev;
/*
* Bus specific device information.
@@ -1126,7 +1119,6 @@ struct aic7770_identity {
ahc_device_setup_t *setup;
};
extern struct aic7770_identity aic7770_ident_table[];
-extern const int ahc_num_aic7770_devs;
#define AHC_EISA_SLOT_OFFSET 0xc00
#define AHC_EISA_IOSIZE 0x100
@@ -1139,12 +1131,9 @@ const struct ahc_pci_identity *ahc_find_pci_device(ahc_dev_softc_t);
int ahc_pci_config(struct ahc_softc *,
const struct ahc_pci_identity *);
int ahc_pci_test_register_access(struct ahc_softc *);
-#ifdef CONFIG_PM
-void ahc_pci_resume(struct ahc_softc *ahc);
-#endif
+void __maybe_unused ahc_pci_resume(struct ahc_softc *ahc);
/*************************** EISA/VL Front End ********************************/
-struct aic7770_identity *aic7770_find_device(uint32_t);
int aic7770_config(struct ahc_softc *ahc,
struct aic7770_identity *,
u_int port);
@@ -1165,10 +1154,8 @@ int ahc_chip_init(struct ahc_softc *ahc);
int ahc_init(struct ahc_softc *ahc);
void ahc_intr_enable(struct ahc_softc *ahc, int enable);
void ahc_pause_and_flushwork(struct ahc_softc *ahc);
-#ifdef CONFIG_PM
-int ahc_suspend(struct ahc_softc *ahc);
-int ahc_resume(struct ahc_softc *ahc);
-#endif
+int __maybe_unused ahc_suspend(struct ahc_softc *ahc);
+int __maybe_unused ahc_resume(struct ahc_softc *ahc);
void ahc_set_unit(struct ahc_softc *, int);
void ahc_set_name(struct ahc_softc *, char *);
void ahc_free(struct ahc_softc *ahc);
diff --git a/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg
index ba0b411d03e2..00fde2243e48 100644
--- a/drivers/scsi/aic7xxx/aic7xxx.reg
+++ b/drivers/scsi/aic7xxx/aic7xxx.reg
@@ -1666,7 +1666,7 @@ scratch_ram {
size 6
/*
* These are reserved registers in the card's scratch ram on the 2742.
- * The EISA configuraiton chip is mapped here. On Rev E. of the
+ * The EISA configuration chip is mapped here. On Rev E. of the
* aic7770, the sequencer can use this area for scratch, but the
* host cannot directly access these registers. On later chips, this
* area can be read and written by both the host and the sequencer.
diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
index 9e85a7ef9c8e..11ddffbcc2f3 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
@@ -64,23 +64,17 @@
* bit to be sent from the chip.
*/
-#ifdef __linux__
#include "aic7xxx_osm.h"
#include "aic7xxx_inline.h"
#include "aic7xxx_93cx6.h"
-#else
-#include <dev/aic7xxx/aic7xxx_osm.h>
-#include <dev/aic7xxx/aic7xxx_inline.h>
-#include <dev/aic7xxx/aic7xxx_93cx6.h>
-#endif
/*
* Right now, we only have to read the SEEPROM. But we make it easier to
* add other 93Cx6 functions.
*/
struct seeprom_cmd {
- uint8_t len;
- uint8_t bits[11];
+ uint8_t len;
+ uint8_t bits[11];
};
/* Short opcodes for the c46 */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c
index dc28b0a91b22..a396f048a031 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_core.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -40,15 +40,9 @@
* $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#155 $
*/
-#ifdef __linux__
#include "aic7xxx_osm.h"
#include "aic7xxx_inline.h"
#include "aicasm/aicasm_insformat.h"
-#else
-#include <dev/aic7xxx/aic7xxx_osm.h>
-#include <dev/aic7xxx/aic7xxx_inline.h>
-#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
-#endif
/***************************** Lookup Tables **********************************/
static const char *const ahc_chip_names[] = {
@@ -67,19 +61,18 @@ static const char *const ahc_chip_names[] = {
"aic7892",
"aic7899"
};
-static const u_int num_chip_names = ARRAY_SIZE(ahc_chip_names);
/*
* Hardware error codes.
*/
struct ahc_hard_error_entry {
- uint8_t errno;
+ uint8_t errno;
const char *errmesg;
};
static const struct ahc_hard_error_entry ahc_hard_errors[] = {
{ ILLHADDR, "Illegal Host Access" },
- { ILLSADDR, "Illegal Sequencer Address referrenced" },
+ { ILLSADDR, "Illegal Sequencer Address referenced" },
{ ILLOPCODE, "Illegal Opcode in sequencer program" },
{ SQPARERR, "Sequencer Parity Error" },
{ DPARERR, "Data-path Parity Error" },
@@ -91,16 +84,16 @@ static const u_int num_errors = ARRAY_SIZE(ahc_hard_errors);
static const struct ahc_phase_table_entry ahc_phase_table[] =
{
- { P_DATAOUT, MSG_NOOP, "in Data-out phase" },
- { P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
- { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
- { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
- { P_COMMAND, MSG_NOOP, "in Command phase" },
- { P_MESGOUT, MSG_NOOP, "in Message-out phase" },
- { P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
+ { P_DATAOUT, NOP, "in Data-out phase" },
+ { P_DATAIN, INITIATOR_ERROR, "in Data-in phase" },
+ { P_DATAOUT_DT, NOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, INITIATOR_ERROR, "in DT Data-in phase" },
+ { P_COMMAND, NOP, "in Command phase" },
+ { P_MESGOUT, NOP, "in Message-out phase" },
+ { P_STATUS, INITIATOR_ERROR, "in Status phase" },
{ P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
- { P_BUSFREE, MSG_NOOP, "while idle" },
- { 0, MSG_NOOP, "in unknown phase" }
+ { P_BUSFREE, NOP, "while idle" },
+ { 0, NOP, "in unknown phase" }
};
/*
@@ -149,7 +142,7 @@ static void ahc_free_tstate(struct ahc_softc *ahc,
#endif
static const struct ahc_syncrate*
ahc_devlimited_syncrate(struct ahc_softc *ahc,
- struct ahc_initiator_tinfo *,
+ struct ahc_initiator_tinfo *,
u_int *period,
u_int *ppr_options,
role_t role);
@@ -202,7 +195,7 @@ static void ahc_setup_target_msgin(struct ahc_softc *ahc,
struct scb *scb);
#endif
-static bus_dmamap_callback_t ahc_dmamap_cb;
+static bus_dmamap_callback_t ahc_dmamap_cb;
static void ahc_build_free_scb_list(struct ahc_softc *ahc);
static int ahc_init_scbdata(struct ahc_softc *ahc);
static void ahc_fini_scbdata(struct ahc_softc *ahc);
@@ -500,7 +493,7 @@ ahc_inq(struct ahc_softc *ahc, u_int port)
return ((ahc_inb(ahc, port))
| (ahc_inb(ahc, port+1) << 8)
| (ahc_inb(ahc, port+2) << 16)
- | (ahc_inb(ahc, port+3) << 24)
+ | (((uint64_t)ahc_inb(ahc, port+3)) << 24)
| (((uint64_t)ahc_inb(ahc, port+4)) << 32)
| (((uint64_t)ahc_inb(ahc, port+5)) << 40)
| (((uint64_t)ahc_inb(ahc, port+6)) << 48)
@@ -822,7 +815,7 @@ ahc_restart(struct ahc_softc *ahc)
ahc_clear_msg_state(ahc);
ahc_outb(ahc, SCSISIGO, 0); /* De-assert BSY */
- ahc_outb(ahc, MSG_OUT, MSG_NOOP); /* No message to send */
+ ahc_outb(ahc, MSG_OUT, NOP); /* No message to send */
ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
ahc_outb(ahc, LASTPHASE, P_BUSFREE);
ahc_outb(ahc, SAVED_SCSIID, 0xFF);
@@ -985,7 +978,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
{
struct scb *scb;
struct ahc_devinfo devinfo;
-
+
ahc_fetch_devinfo(ahc, &devinfo);
/*
@@ -1029,7 +1022,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
goto unpause;
}
- hscb = scb->hscb;
+ hscb = scb->hscb;
/* Don't want to clobber the original sense code */
if ((scb->flags & SCB_SENSE) != 0) {
@@ -1048,12 +1041,12 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc_freeze_scb(scb);
ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
switch (hscb->shared_data.status.scsi_status) {
- case SCSI_STATUS_OK:
- printk("%s: Interrupted for staus of 0???\n",
+ case SAM_STAT_GOOD:
+ printk("%s: Interrupted for status of 0???\n",
ahc_name(ahc));
break;
- case SCSI_STATUS_CMD_TERMINATED:
- case SCSI_STATUS_CHECK_COND:
+ case SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
{
struct ahc_dma_seg *sg;
struct scsi_sense *sc;
@@ -1078,7 +1071,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
&tstate);
tinfo = &targ_info->curr;
sg = scb->sg_list;
- sc = (struct scsi_sense *)(&hscb->shared_data.cdb);
+ sc = (struct scsi_sense *)(&hscb->shared_data.cdb);
/*
* Save off the residual if there is one.
*/
@@ -1124,8 +1117,8 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
* errors will be reported before any data
* phases occur.
*/
- if (ahc_get_residual(scb)
- == ahc_get_transfer_length(scb)) {
+ if (ahc_get_residual(scb)
+ == ahc_get_transfer_length(scb)) {
ahc_update_neg_request(ahc, &devinfo,
tstate, targ_info,
AHC_NEG_IF_NON_ASYNC);
@@ -1136,7 +1129,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
scb->flags |= SCB_AUTO_NEGOTIATE;
}
hscb->cdb_len = sizeof(*sc);
- hscb->dataptr = sg->addr;
+ hscb->dataptr = sg->addr;
hscb->datacnt = sg->len;
hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
hscb->sgptr = ahc_htole32(hscb->sgptr);
@@ -1186,7 +1179,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
printk("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
printk("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
ahc_dump_card_state(ahc);
- ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+ ahc->msgout_buf[0] = TARGET_RESET;
ahc->msgout_len = 1;
ahc->msgout_index = 0;
ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
@@ -1194,13 +1187,13 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc_assert_atn(ahc);
break;
}
- case SEND_REJECT:
+ case SEND_REJECT:
{
u_int rejbyte = ahc_inb(ahc, ACCUM);
printk("%s:%c:%d: Warning - unknown message received from "
- "target (0x%x). Rejecting\n",
+ "target (0x%x). Rejecting\n",
ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
- break;
+ break;
}
case PROTO_VIOLATION:
{
@@ -1293,8 +1286,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc->msg_type =
MSG_TYPE_TARGET_MSGOUT;
ahc->msgin_index = 0;
- }
- else
+ } else
ahc_setup_target_msgin(ahc,
&devinfo,
scb);
@@ -1366,7 +1358,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
if (scb != NULL)
ahc_set_transaction_status(scb,
CAM_UNCOR_PARITY);
- ahc_reset_channel(ahc, devinfo.channel,
+ ahc_reset_channel(ahc, devinfo.channel,
/*init reset*/TRUE);
}
} else {
@@ -1398,7 +1390,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
printk("data overrun detected %s."
" Tag == 0x%x.\n",
ahc_phase_table[i].phasemsg,
- scb->hscb->tag);
+ scb->hscb->tag);
ahc_print_path(ahc, scb);
printk("%s seen Data Phase. Length = %ld. NumSGs = %d.\n",
ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
@@ -1409,7 +1401,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
printk("sg[%d] - Addr 0x%x%x : Length %d\n",
i,
(ahc_le32toh(scb->sg_list[i].len) >> 24
- & SG_HIGH_ADDR_BITS),
+ & SG_HIGH_ADDR_BITS),
ahc_le32toh(scb->sg_list[i].addr),
ahc_le32toh(scb->sg_list[i].len)
& AHC_SG_LEN_MASK);
@@ -1556,7 +1548,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
if (status == 0 && status0 == 0) {
if ((ahc->features & AHC_TWIN) != 0) {
/* Try the other channel */
- ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
status = ahc_inb(ahc, SSTAT1)
& (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
intr_channel = (cur_channel == 'A') ? 'B' : 'A';
@@ -1602,7 +1594,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
printk("%s: Someone reset channel %c\n",
ahc_name(ahc), intr_channel);
if (intr_channel != cur_channel)
- ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
} else if ((status & SCSIPERR) != 0) {
/*
@@ -1673,7 +1665,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
printk("\tCRC Value Mismatch\n");
if ((sstat2 & CRCENDERR) != 0)
printk("\tNo terminal CRC packet "
- "recevied\n");
+ "received\n");
if ((sstat2 & CRCREQERR) != 0)
printk("\tIllegal CRC packet "
"request\n");
@@ -1691,17 +1683,17 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
* data direction, so ignore the value
* in the phase table.
*/
- mesg_out = MSG_INITIATOR_DET_ERR;
+ mesg_out = INITIATOR_ERROR;
}
/*
- * We've set the hardware to assert ATN if we
- * get a parity error on "in" phases, so all we
+ * We've set the hardware to assert ATN if we
+ * get a parity error on "in" phases, so all we
* need to do is stuff the message buffer with
* the appropriate message. "In" phases have set
* mesg_out to something other than MSG_NOP.
*/
- if (mesg_out != MSG_NOOP) {
+ if (mesg_out != NOP) {
if (ahc->msg_type != MSG_TYPE_NONE)
ahc->send_msg_perror = TRUE;
else
@@ -1825,10 +1817,10 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
u_int tag;
tag = SCB_LIST_NULL;
- if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
- || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+ if (ahc_sent_msg(ahc, AHCMSG_1B, ABORT_TASK, TRUE)
+ || ahc_sent_msg(ahc, AHCMSG_1B, ABORT_TASK_SET, TRUE)) {
if (ahc->msgout_buf[ahc->msgout_index - 1]
- == MSG_ABORT_TAG)
+ == ABORT_TASK)
tag = scb->hscb->tag;
ahc_print_path(ahc, scb);
printk("SCB %d - Abort%s Completed.\n",
@@ -1840,22 +1832,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
CAM_REQ_ABORTED);
printerror = 0;
} else if (ahc_sent_msg(ahc, AHCMSG_1B,
- MSG_BUS_DEV_RESET, TRUE)) {
-#ifdef __FreeBSD__
- /*
- * Don't mark the user's request for this BDR
- * as completing with CAM_BDR_SENT. CAM3
- * specifies CAM_REQ_CMP.
- */
- if (scb != NULL
- && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
- && ahc_match_scb(ahc, scb, target, channel,
- CAM_LUN_WILDCARD,
- SCB_LIST_NULL,
- ROLE_INITIATOR)) {
- ahc_set_transaction_status(scb, CAM_REQ_CMP);
- }
-#endif
+ TARGET_RESET, TRUE)) {
ahc_compile_devinfo(&devinfo,
initiator_role_id,
target,
@@ -1868,7 +1845,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
/*verbose_level*/0);
printerror = 0;
} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
- MSG_EXT_PPR, FALSE)) {
+ EXTENDED_PPR, FALSE)) {
struct ahc_initiator_tinfo *tinfo;
struct ahc_tmode_tstate *tstate;
@@ -1887,7 +1864,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
ahc_qinfifo_requeue_tail(ahc, scb);
printerror = 0;
} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
- MSG_EXT_WDTR, FALSE)) {
+ EXTENDED_WDTR, FALSE)) {
/*
* Negotiation Rejected. Go-narrow and
* retry command.
@@ -1899,7 +1876,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
ahc_qinfifo_requeue_tail(ahc, scb);
printerror = 0;
} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
- MSG_EXT_SDTR, FALSE)) {
+ EXTENDED_SDTR, FALSE)) {
/*
* Negotiation Rejected. Go-async and
* retry command.
@@ -2008,7 +1985,7 @@ ahc_clear_critical_section(struct ahc_softc *ahc)
| (ahc_inb(ahc, SEQADDR1) << 8);
/*
- * Seqaddr represents the next instruction to execute,
+ * Seqaddr represents the next instruction to execute,
* so we are really executing the instruction just
* before it.
*/
@@ -2016,7 +1993,6 @@ ahc_clear_critical_section(struct ahc_softc *ahc)
seqaddr -= 1;
cs = ahc->critical_sections;
for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
-
if (cs->begin < seqaddr && cs->end >= seqaddr)
break;
}
@@ -2086,7 +2062,7 @@ ahc_clear_intstat(struct ahc_softc *ahc)
CLRREQINIT);
ahc_flush_device_writes(ahc);
ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
- ahc_flush_device_writes(ahc);
+ ahc_flush_device_writes(ahc);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_flush_device_writes(ahc);
}
@@ -2123,7 +2099,7 @@ ahc_print_scb(struct scb *scb)
printk("sg[%d] - Addr 0x%x%x : Length %d\n",
i,
(ahc_le32toh(scb->sg_list[i].len) >> 24
- & SG_HIGH_ADDR_BITS),
+ & SG_HIGH_ADDR_BITS),
ahc_le32toh(scb->sg_list[i].addr),
ahc_le32toh(scb->sg_list[i].len));
}
@@ -2200,8 +2176,7 @@ ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
if (channel == 'B')
scsi_id += 8;
tstate = ahc->enabled_targets[scsi_id];
- if (tstate != NULL)
- kfree(tstate);
+ kfree(tstate);
ahc->enabled_targets[scsi_id] = NULL;
}
#endif
@@ -2212,7 +2187,7 @@ ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
* by the capabilities of the bus connectivity of and sync settings for
* the target.
*/
-const struct ahc_syncrate *
+static const struct ahc_syncrate *
ahc_devlimited_syncrate(struct ahc_softc *ahc,
struct ahc_initiator_tinfo *tinfo,
u_int *period, u_int *ppr_options, role_t role)
@@ -2246,7 +2221,7 @@ ahc_devlimited_syncrate(struct ahc_softc *ahc,
*/
if (role == ROLE_TARGET)
transinfo = &tinfo->user;
- else
+ else
transinfo = &tinfo->goal;
*ppr_options &= transinfo->ppr_options;
if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
@@ -2321,7 +2296,7 @@ ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
* At some speeds, we only support
* ST transfers.
*/
- if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+ if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
break;
}
@@ -2427,7 +2402,7 @@ ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
break;
}
- /* FALLTHROUGH */
+ fallthrough;
case MSG_EXT_WDTR_BUS_8_BIT:
*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
break;
@@ -2678,9 +2653,9 @@ ahc_set_tags(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
{
struct scsi_device *sdev = cmd->device;
- ahc_platform_set_tags(ahc, sdev, devinfo, alg);
- ahc_send_async(ahc, devinfo->channel, devinfo->target,
- devinfo->lun, AC_TRANSFER_NEG);
+ ahc_platform_set_tags(ahc, sdev, devinfo, alg);
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ devinfo->lun, AC_TRANSFER_NEG);
}
/*
@@ -2779,9 +2754,9 @@ ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
role = ROLE_INITIATOR;
if (role == ROLE_TARGET
- && (ahc->features & AHC_MULTI_TID) != 0
- && (ahc_inb(ahc, SEQ_FLAGS)
- & (CMDPHASE_PENDING|TARG_CMD_PENDING|NO_DISCONNECT)) != 0) {
+ && (ahc->features & AHC_MULTI_TID) != 0
+ && (ahc_inb(ahc, SEQ_FLAGS)
+ & (CMDPHASE_PENDING|TARG_CMD_PENDING|NO_DISCONNECT)) != 0) {
/* We were selected, so pull our id from TARGIDIN */
our_id = ahc_inb(ahc, TARGIDIN) & OID;
} else if ((ahc->features & AHC_ULTRA2) != 0)
@@ -2903,7 +2878,7 @@ ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
}
if (scb->flags & SCB_DEVICE_RESET) {
- ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+ ahc->msgout_buf[ahc->msgout_index++] = TARGET_RESET;
ahc->msgout_len++;
ahc_print_path(ahc, scb);
printk("Bus Device Reset Message Sent\n");
@@ -2917,9 +2892,9 @@ ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
} else if ((scb->flags & SCB_ABORT) != 0) {
if ((scb->hscb->control & TAG_ENB) != 0)
- ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
+ ahc->msgout_buf[ahc->msgout_index++] = ABORT_TASK;
else
- ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
+ ahc->msgout_buf[ahc->msgout_index++] = ABORT_TASK_SET;
ahc->msgout_len++;
ahc_print_path(ahc, scb);
printk("Abort%s Message Sent\n",
@@ -3129,7 +3104,7 @@ ahc_clear_msg_state(struct ahc_softc *ahc)
*/
ahc_outb(ahc, CLRSINT1, CLRATNO);
}
- ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+ ahc_outb(ahc, MSG_OUT, NOP);
ahc_outb(ahc, SEQ_FLAGS2,
ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
}
@@ -3215,7 +3190,7 @@ proto_violation_reset:
ahc_outb(ahc, MSG_OUT, HOST_MSG);
if (scb == NULL) {
ahc_print_devinfo(ahc, &devinfo);
- ahc->msgout_buf[0] = MSG_ABORT_TASK;
+ ahc->msgout_buf[0] = ABORT_TASK;
ahc->msgout_len = 1;
ahc->msgout_index = 0;
ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
@@ -3389,7 +3364,7 @@ reswitch:
#endif
ahc_assert_atn(ahc);
}
- } else
+ } else
ahc->msgin_index++;
if (message_done == MSGLOOP_TERMINATED) {
@@ -3482,7 +3457,7 @@ reswitch:
*/
return;
}
-
+
ahc->msgin_index++;
/*
@@ -3543,7 +3518,7 @@ ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
index = 0;
while (index < ahc->msgout_len) {
- if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+ if (ahc->msgout_buf[index] == EXTENDED_MESSAGE) {
u_int end_index;
end_index = index + 1 + ahc->msgout_buf[index + 1];
@@ -3557,8 +3532,8 @@ ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
found = TRUE;
}
index = end_index;
- } else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
- && ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+ } else if (ahc->msgout_buf[index] >= SIMPLE_QUEUE_TAG
+ && ahc->msgout_buf[index] <= IGNORE_WIDE_RESIDUE) {
/* Skip tag type and tag id or residue param*/
index += 2;
@@ -3609,37 +3584,37 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* extended message type.
*/
switch (ahc->msgin_buf[0]) {
- case MSG_DISCONNECT:
- case MSG_SAVEDATAPOINTER:
- case MSG_CMDCOMPLETE:
- case MSG_RESTOREPOINTERS:
- case MSG_IGN_WIDE_RESIDUE:
+ case DISCONNECT:
+ case SAVE_POINTERS:
+ case COMMAND_COMPLETE:
+ case RESTORE_POINTERS:
+ case IGNORE_WIDE_RESIDUE:
/*
* End our message loop as these are messages
* the sequencer handles on its own.
*/
done = MSGLOOP_TERMINATED;
break;
- case MSG_MESSAGE_REJECT:
+ case MESSAGE_REJECT:
response = ahc_handle_msg_reject(ahc, devinfo);
- /* FALLTHROUGH */
- case MSG_NOOP:
+ fallthrough;
+ case NOP:
done = MSGLOOP_MSGCOMPLETE;
break;
- case MSG_EXTENDED:
+ case EXTENDED_MESSAGE:
{
/* Wait for enough of the message to begin validation */
if (ahc->msgin_index < 2)
break;
switch (ahc->msgin_buf[2]) {
- case MSG_EXT_SDTR:
+ case EXTENDED_SDTR:
{
const struct ahc_syncrate *syncrate;
u_int period;
u_int ppr_options;
u_int offset;
u_int saved_offset;
-
+
if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
reject = TRUE;
break;
@@ -3673,7 +3648,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
ahc->msgin_buf[3], saved_offset,
period, offset);
}
- ahc_set_syncrate(ahc, devinfo,
+ ahc_set_syncrate(ahc, devinfo,
syncrate, period,
offset, ppr_options,
AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
@@ -3684,7 +3659,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* and didn't have to fall down to async
* transfers.
*/
- if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, EXTENDED_SDTR, TRUE)) {
/* We started it */
if (saved_offset != offset) {
/* Went too low - force async */
@@ -3711,7 +3686,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
done = MSGLOOP_MSGCOMPLETE;
break;
}
- case MSG_EXT_WDTR:
+ case EXTENDED_WDTR:
{
u_int bus_width;
u_int saved_width;
@@ -3745,7 +3720,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
saved_width, bus_width);
}
- if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, EXTENDED_WDTR, TRUE)) {
/*
* Don't send a WDTR back to the
* target, since we asked first.
@@ -3807,7 +3782,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
done = MSGLOOP_MSGCOMPLETE;
break;
}
- case MSG_EXT_PPR:
+ case EXTENDED_PPR:
{
const struct ahc_syncrate *syncrate;
u_int period;
@@ -3867,7 +3842,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
&offset, bus_width,
devinfo->role);
- if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, EXTENDED_PPR, TRUE)) {
/*
* If we are unable to do any of the
* requested options (we went too low),
@@ -3931,7 +3906,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
break;
}
#ifdef AHC_TARGET_MODE
- case MSG_BUS_DEV_RESET:
+ case TARGET_RESET:
ahc_handle_devreset(ahc, devinfo,
CAM_BDR_SENT,
"Bus Device Reset Received",
@@ -3939,9 +3914,9 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
ahc_restart(ahc);
done = MSGLOOP_TERMINATED;
break;
- case MSG_ABORT_TAG:
- case MSG_ABORT:
- case MSG_CLEAR_QUEUE:
+ case ABORT_TASK:
+ case ABORT_TASK_SET:
+ case CLEAR_QUEUE_TASK_SET:
{
int tag;
@@ -3951,7 +3926,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
break;
}
tag = SCB_LIST_NULL;
- if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
+ if (ahc->msgin_buf[0] == ABORT_TASK)
tag = ahc_inb(ahc, INITIATOR_TAG);
ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
devinfo->lun, tag, ROLE_TARGET,
@@ -3975,7 +3950,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
break;
}
#endif
- case MSG_TERM_IO_PROC:
+ case TERMINATE_IO_PROC:
default:
reject = TRUE;
break;
@@ -3987,7 +3962,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
*/
ahc->msgout_index = 0;
ahc->msgout_len = 1;
- ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+ ahc->msgout_buf[0] = MESSAGE_REJECT;
done = MSGLOOP_MSGCOMPLETE;
response = TRUE;
}
@@ -4026,7 +4001,7 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
/* Might be necessary */
last_msg = ahc_inb(ahc, LAST_MSG);
- if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, EXTENDED_PPR, /*full*/FALSE)) {
/*
* Target does not support the PPR message.
* Attempt to negotiate SPI-2 style.
@@ -4045,7 +4020,7 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
ahc_build_transfer_msg(ahc, devinfo);
ahc->msgout_index = 0;
response = 1;
- } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, EXTENDED_WDTR, /*full*/FALSE)) {
/* note 8bit xfers */
printk("(%s:%c:%d:%d): refuses WIDE negotiation. Using "
@@ -4070,7 +4045,7 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
ahc->msgout_index = 0;
response = 1;
}
- } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, EXTENDED_SDTR, /*full*/FALSE)) {
/* note asynch xfers and clear flag */
ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
/*offset*/0, /*ppr_options*/0,
@@ -4080,13 +4055,13 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
"Using asynchronous transfers\n",
ahc_name(ahc), devinfo->channel,
devinfo->target, devinfo->lun);
- } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+ } else if ((scb->hscb->control & SIMPLE_QUEUE_TAG) != 0) {
int tag_type;
int mask;
- tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+ tag_type = (scb->hscb->control & SIMPLE_QUEUE_TAG);
- if (tag_type == MSG_SIMPLE_TASK) {
+ if (tag_type == SIMPLE_QUEUE_TAG) {
printk("(%s:%c:%d:%d): refuses tagged commands. "
"Performing non-tagged I/O\n", ahc_name(ahc),
devinfo->channel, devinfo->target, devinfo->lun);
@@ -4096,7 +4071,7 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
printk("(%s:%c:%d:%d): refuses %s tagged commands. "
"Performing simple queue tagged I/O only\n",
ahc_name(ahc), devinfo->channel, devinfo->target,
- devinfo->lun, tag_type == MSG_ORDERED_TASK
+ devinfo->lun, tag_type == ORDERED_QUEUE_TAG
? "ordered" : "head of queue");
ahc_set_tags(ahc, scb->io_ctx, devinfo, AHC_QUEUE_BASIC);
mask = ~0x03;
@@ -4108,9 +4083,9 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
*/
ahc_outb(ahc, SCB_CONTROL,
ahc_inb(ahc, SCB_CONTROL) & mask);
- scb->hscb->control &= mask;
+ scb->hscb->control &= mask;
ahc_set_transaction_tag(scb, /*enabled*/FALSE,
- /*type*/MSG_SIMPLE_TASK);
+ /*type*/SIMPLE_QUEUE_TAG);
ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
ahc_assert_atn(ahc);
@@ -4347,7 +4322,7 @@ ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
continue;
ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
- MSG_BUS_DEV_RESET, /*arg*/0);
+ TARGET_RESET, /*arg*/0);
ahc_send_lstate_events(ahc, lstate);
}
}
@@ -4361,7 +4336,7 @@ ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
/*period*/0, /*offset*/0, /*ppr_options*/0,
AHC_TRANS_CUR, /*paused*/TRUE);
-
+
if (status != CAM_SEL_TIMEOUT)
ahc_send_async(ahc, devinfo->channel, devinfo->target,
CAM_LUN_WILDCARD, AC_SENT_BDR);
@@ -4378,11 +4353,11 @@ ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct scb *scb)
{
- /*
+ /*
* To facilitate adding multiple messages together,
* each routine should increment the index and len
* variables instead of setting them explicitly.
- */
+ */
ahc->msgout_index = 0;
ahc->msgout_len = 0;
@@ -4406,22 +4381,16 @@ ahc_alloc(void *platform_arg, char *name)
struct ahc_softc *ahc;
int i;
-#ifndef __FreeBSD__
- ahc = kmalloc(sizeof(*ahc), GFP_ATOMIC);
+ ahc = kzalloc(sizeof(*ahc), GFP_ATOMIC);
if (!ahc) {
printk("aic7xxx: cannot malloc softc!\n");
kfree(name);
return NULL;
}
-#else
- ahc = device_get_softc((device_t)platform_arg);
-#endif
- memset(ahc, 0, sizeof(*ahc));
+
ahc->seep_config = kmalloc(sizeof(*ahc->seep_config), GFP_ATOMIC);
if (ahc->seep_config == NULL) {
-#ifndef __FreeBSD__
kfree(ahc);
-#endif
kfree(name);
return (NULL);
}
@@ -4461,13 +4430,12 @@ ahc_softc_init(struct ahc_softc *ahc)
ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
else
ahc->unpause = 0;
- ahc->pause = ahc->unpause | PAUSE;
+ ahc->pause = ahc->unpause | PAUSE;
/* XXX The shared scb data stuff should be deprecated */
if (ahc->scb_data == NULL) {
- ahc->scb_data = kmalloc(sizeof(*ahc->scb_data), GFP_ATOMIC);
+ ahc->scb_data = kzalloc(sizeof(*ahc->scb_data), GFP_ATOMIC);
if (ahc->scb_data == NULL)
return (ENOMEM);
- memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
}
return (0);
@@ -4482,8 +4450,7 @@ ahc_set_unit(struct ahc_softc *ahc, int unit)
void
ahc_set_name(struct ahc_softc *ahc, char *name)
{
- if (ahc->name != NULL)
- kfree(ahc->name);
+ kfree(ahc->name);
ahc->name = name;
}
@@ -4496,31 +4463,26 @@ ahc_free(struct ahc_softc *ahc)
default:
case 5:
ahc_shutdown(ahc);
- /* FALLTHROUGH */
+ fallthrough;
case 4:
ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
ahc->shared_data_dmamap);
- /* FALLTHROUGH */
+ fallthrough;
case 3:
ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
ahc->shared_data_dmamap);
ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
ahc->shared_data_dmamap);
- /* FALLTHROUGH */
+ fallthrough;
case 2:
ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
+ fallthrough;
case 1:
-#ifndef __linux__
- ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
-#endif
break;
case 0:
break;
}
-#ifndef __linux__
- ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
-#endif
ahc_platform_free(ahc);
ahc_fini_scbdata(ahc);
for (i = 0; i < AHC_NUM_TARGETS; i++) {
@@ -4550,13 +4512,9 @@ ahc_free(struct ahc_softc *ahc)
kfree(ahc->black_hole);
}
#endif
- if (ahc->name != NULL)
- kfree(ahc->name);
- if (ahc->seep_config != NULL)
- kfree(ahc->seep_config);
-#ifndef __FreeBSD__
+ kfree(ahc->name);
+ kfree(ahc->seep_config);
kfree(ahc);
-#endif
return;
}
@@ -4594,7 +4552,7 @@ ahc_reset(struct ahc_softc *ahc, int reinit)
u_int sxfrctl1_a, sxfrctl1_b;
int error;
int wait;
-
+
/*
* Preserve the value of the SXFRCTL1 register for all channels.
* It contains settings that affect termination and we don't want
@@ -4683,7 +4641,7 @@ ahc_reset(struct ahc_softc *ahc, int reinit)
*/
error = ahc->bus_chip_init(ahc);
#ifdef AHC_DUMP_SEQ
- else
+ else
ahc_dumpseq(ahc);
#endif
@@ -4748,7 +4706,7 @@ ahc_build_free_scb_list(struct ahc_softc *ahc)
/* Set the next pointer */
if ((ahc->flags & AHC_PAGESCBS) != 0)
ahc_outb(ahc, SCB_NEXT, i+1);
- else
+ else
ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
/* Make the tag number, SCSIID, and lun invalid */
@@ -4780,10 +4738,10 @@ ahc_init_scbdata(struct ahc_softc *ahc)
SLIST_INIT(&scb_data->sg_maps);
/* Allocate SCB resources */
- scb_data->scbarray = kmalloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC, GFP_ATOMIC);
+ scb_data->scbarray = kcalloc(AHC_SCB_MAX_ALLOC, sizeof(struct scb),
+ GFP_ATOMIC);
if (scb_data->scbarray == NULL)
return (ENOMEM);
- memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX_ALLOC);
/* Determine the number of hardware SCBs and initialize them */
@@ -4901,7 +4859,7 @@ ahc_init_scbdata(struct ahc_softc *ahc)
/*
* Note that we were successful
*/
- return (0);
+ return (0);
error_exit:
@@ -4934,32 +4892,37 @@ ahc_fini_scbdata(struct ahc_softc *ahc)
}
ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
}
+ fallthrough;
case 6:
ahc_dmamap_unload(ahc, scb_data->sense_dmat,
scb_data->sense_dmamap);
+ fallthrough;
case 5:
ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
scb_data->sense_dmamap);
ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
scb_data->sense_dmamap);
+ fallthrough;
case 4:
ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+ fallthrough;
case 3:
ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
scb_data->hscb_dmamap);
+ fallthrough;
case 2:
ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
scb_data->hscb_dmamap);
ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
scb_data->hscb_dmamap);
+ fallthrough;
case 1:
ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
break;
case 0:
break;
}
- if (scb_data->scbarray != NULL)
- kfree(scb_data->scbarray);
+ kfree(scb_data->scbarray);
}
static void
@@ -5006,9 +4969,7 @@ ahc_alloc_scbs(struct ahc_softc *ahc)
newcount = min(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
for (i = 0; i < newcount; i++) {
struct scb_platform_data *pdata;
-#ifndef __linux__
- int error;
-#endif
+
pdata = kmalloc(sizeof(*pdata), GFP_ATOMIC);
if (pdata == NULL)
break;
@@ -5022,12 +4983,6 @@ ahc_alloc_scbs(struct ahc_softc *ahc)
next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
next_scb->ahc_softc = ahc;
next_scb->flags = SCB_FREE;
-#ifndef __linux__
- error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
- &next_scb->dmamap);
- if (error != 0)
- break;
-#endif
next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
next_scb->hscb->tag = ahc->scb_data->numscbs;
SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
@@ -5047,7 +5002,7 @@ ahc_controller_info(struct ahc_softc *ahc, char *buf)
len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
buf += len;
if ((ahc->features & AHC_TWIN) != 0)
- len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+ len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
"B SCSI Id=%d, primary %c, ",
ahc->our_id, ahc->our_id_b,
(ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
@@ -5183,7 +5138,7 @@ ahc_chip_init(struct ahc_softc *ahc)
ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
-
+
if ((ahc->features & AHC_HS_MAILBOX) != 0)
ahc_outb(ahc, HS_MAILBOX, 0);
@@ -5213,7 +5168,7 @@ ahc_chip_init(struct ahc_softc *ahc)
ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
/* Message out buffer starts empty */
- ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+ ahc_outb(ahc, MSG_OUT, NOP);
/*
* Setup the allowed SCSI Sequences based on operational mode.
@@ -5314,7 +5269,7 @@ ahc_init(struct ahc_softc *ahc)
*/
if ((ahc->flags & AHC_USEDEFAULTS) != 0)
ahc->our_id = ahc->our_id_b = 7;
-
+
/*
* Default to allowing initiator operations.
*/
@@ -5326,31 +5281,13 @@ ahc_init(struct ahc_softc *ahc)
if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
ahc->features &= ~AHC_TARGETMODE;
-#ifndef __linux__
- /* DMA tag for mapping buffers into device visible space. */
- if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
- /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
- /*lowaddr*/ahc->flags & AHC_39BIT_ADDRESSING
- ? (dma_addr_t)0x7FFFFFFFFFULL
- : BUS_SPACE_MAXADDR_32BIT,
- /*highaddr*/BUS_SPACE_MAXADDR,
- /*filter*/NULL, /*filterarg*/NULL,
- /*maxsize*/(AHC_NSEG - 1) * PAGE_SIZE,
- /*nsegments*/AHC_NSEG,
- /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
- /*flags*/BUS_DMA_ALLOCNOW,
- &ahc->buffer_dmat) != 0) {
- return (ENOMEM);
- }
-#endif
-
ahc->init_level++;
/*
* DMA tag for our command fifos and other data in system memory
* the card's sequencer must be able to access. For initiator
* roles, we need to allocate space for the qinfifo and qoutfifo.
- * The qinfifo and qoutfifo are composed of 256 1 byte elements.
+ * The qinfifo and qoutfifo are composed of 256 1 byte elements.
* When providing for the target mode role, we must additionally
* provide space for the incoming target command fifo and an extra
* byte to deal with a dma bug in some chip versions.
@@ -5459,7 +5396,7 @@ ahc_init(struct ahc_softc *ahc)
&& (ahc->flags & AHC_INITIATORROLE) != 0)
ahc->flags |= AHC_RESET_BUS_A;
- ultraenb = 0;
+ ultraenb = 0;
tagenable = ALL_TARGETS_MASK;
/* Grab the disconnection disable table and invert it for our needs */
@@ -5555,9 +5492,9 @@ ahc_init(struct ahc_softc *ahc)
&& (ultraenb & mask) != 0) {
/* Treat 10MHz as a non-ultra speed */
scsirate &= ~SXFR;
- ultraenb &= ~mask;
+ ultraenb &= ~mask;
}
- tinfo->user.period =
+ tinfo->user.period =
ahc_find_period(ahc, scsirate,
(ultraenb & mask)
? AHC_SYNCRATE_ULTRA
@@ -5652,8 +5589,7 @@ ahc_pause_and_flushwork(struct ahc_softc *ahc)
ahc->flags &= ~AHC_ALL_INTERRUPTS;
}
-#ifdef CONFIG_PM
-int
+int __maybe_unused
ahc_suspend(struct ahc_softc *ahc)
{
@@ -5679,16 +5615,15 @@ ahc_suspend(struct ahc_softc *ahc)
return (0);
}
-int
+int __maybe_unused
ahc_resume(struct ahc_softc *ahc)
{
ahc_reset(ahc, /*reinit*/TRUE);
- ahc_intr_enable(ahc, TRUE);
+ ahc_intr_enable(ahc, TRUE);
ahc_restart(ahc);
return (0);
}
-#endif
/************************** Busy Target Table *********************************/
/*
* Return the untagged transaction id for a given target/channel lun.
@@ -5702,7 +5637,7 @@ ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
if ((ahc->flags & AHC_SCB_BTT) != 0) {
u_int saved_scbptr;
-
+
saved_scbptr = ahc_inb(ahc, SCBPTR);
ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
@@ -5722,7 +5657,7 @@ ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
if ((ahc->flags & AHC_SCB_BTT) != 0) {
u_int saved_scbptr;
-
+
saved_scbptr = ahc_inb(ahc, SCBPTR);
ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
@@ -5740,7 +5675,7 @@ ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
if ((ahc->flags & AHC_SCB_BTT) != 0) {
u_int saved_scbptr;
-
+
saved_scbptr = ahc_inb(ahc, SCBPTR);
ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
@@ -5798,7 +5733,7 @@ ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
target = SCB_GET_TARGET(ahc, scb);
lun = SCB_GET_LUN(scb);
channel = SCB_GET_CHANNEL(ahc, scb);
-
+
ahc_search_qinfifo(ahc, target, channel, lun,
/*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
CAM_REQUEUE_REQ, SEARCH_COMPLETE);
@@ -5836,7 +5771,7 @@ ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
} else {
prev_scb->hscb->next = scb->hscb->tag;
- ahc_sync_scb(ahc, prev_scb,
+ ahc_sync_scb(ahc, prev_scb,
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
}
ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
@@ -5929,9 +5864,8 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
if ((scb->flags & SCB_ACTIVE) == 0)
printk("Inactive SCB in qinfifo\n");
ahc_done(ahc, scb);
-
- /* FALLTHROUGH */
}
+ fallthrough;
case SEARCH_REMOVE:
break;
case SEARCH_COUNT:
@@ -6042,8 +5976,8 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
if ((scb->flags & SCB_ACTIVE) == 0)
printk("Inactive SCB in Waiting List\n");
ahc_done(ahc, scb);
- /* FALLTHROUGH */
}
+ fallthrough;
case SEARCH_REMOVE:
next = ahc_rem_wscb(ahc, next, prev);
break;
@@ -6053,7 +5987,6 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
break;
}
} else {
-
prev = next;
next = ahc_inb(ahc, SCB_NEXT);
}
@@ -6299,7 +6232,7 @@ ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
/* update the waiting list */
if (prev == SCB_LIST_NULL) {
/* First in the list */
- ahc_outb(ahc, WAITING_SCBH, next);
+ ahc_outb(ahc, WAITING_SCBH, next);
/*
* Ensure we aren't attempting to perform
@@ -6308,7 +6241,7 @@ ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
} else {
/*
- * Select the scb that pointed to us
+ * Select the scb that pointed to us
* and update its next pointer.
*/
ahc_outb(ahc, SCBPTR, prev);
@@ -6702,7 +6635,7 @@ ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
/*
* Remainder of the SG where the transfer
- * stopped.
+ * stopped.
*/
resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
@@ -6754,7 +6687,7 @@ ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
- (lstate->event_r_idx - lstate->event_w_idx);
if (event_type == EVENT_TYPE_BUS_RESET
- || event_type == MSG_BUS_DEV_RESET) {
+ || event_type == TARGET_RESET) {
/*
* Any earlier events are irrelevant, so reset our buffer.
* This has the effect of allowing us to deal with reset
@@ -6849,9 +6782,9 @@ ahc_dumpseq(struct ahc_softc* ahc)
static int
ahc_loadseq(struct ahc_softc *ahc)
{
- struct cs cs_table[num_critical_sections];
- u_int begin_set[num_critical_sections];
- u_int end_set[num_critical_sections];
+ struct cs cs_table[NUM_CRITICAL_SECTIONS];
+ u_int begin_set[NUM_CRITICAL_SECTIONS];
+ u_int end_set[NUM_CRITICAL_SECTIONS];
const struct patch *cur_patch;
u_int cs_count;
u_int cur_cs;
@@ -6916,12 +6849,12 @@ ahc_loadseq(struct ahc_softc *ahc)
* Move through the CS table until we find a CS
* that might apply to this instruction.
*/
- for (; cur_cs < num_critical_sections; cur_cs++) {
+ for (; cur_cs < NUM_CRITICAL_SECTIONS; cur_cs++) {
if (critical_sections[cur_cs].end <= i) {
if (begin_set[cs_count] == TRUE
&& end_set[cs_count] == FALSE) {
cs_table[cs_count].end = downloaded;
- end_set[cs_count] = TRUE;
+ end_set[cs_count] = TRUE;
cs_count++;
}
continue;
@@ -6941,10 +6874,9 @@ ahc_loadseq(struct ahc_softc *ahc)
if (cs_count != 0) {
cs_count *= sizeof(struct cs);
- ahc->critical_sections = kmalloc(cs_count, GFP_ATOMIC);
+ ahc->critical_sections = kmemdup(cs_table, cs_count, GFP_ATOMIC);
if (ahc->critical_sections == NULL)
panic("ahc_loadseq: Could not malloc");
- memcpy(ahc->critical_sections, cs_table, cs_count);
}
ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
@@ -7048,8 +6980,8 @@ ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
}
address -= address_offset;
fmt3_ins->address = address;
- /* FALLTHROUGH */
}
+ fallthrough;
case AIC_OP_OR:
case AIC_OP_AND:
case AIC_OP_XOR:
@@ -7075,7 +7007,7 @@ ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
fmt1_ins->opcode = AIC_OP_AND;
fmt1_ins->immediate = 0xff;
}
- /* FALLTHROUGH */
+ fallthrough;
case AIC_OP_ROL:
if ((ahc->features & AHC_ULTRA2) != 0) {
int i, count;
@@ -7150,7 +7082,6 @@ ahc_print_register(const ahc_reg_parse_entry_t *table, u_int num_entries,
printed_mask == 0 ? ":(" : "|",
table[entry].name);
printed_mask |= table[entry].mask;
-
break;
}
if (entry >= num_entries)
@@ -7264,7 +7195,7 @@ ahc_dump_card_state(struct ahc_softc *ahc)
scb_index = ahc_inb(ahc, SCB_NEXT);
}
printk("\n");
-
+
ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
printk("QOUTFIFO entries: ");
qoutpos = ahc->qoutfifonext;
@@ -7341,7 +7272,6 @@ ahc_dump_card_state(struct ahc_softc *ahc)
printk("\n");
}
- ahc_platform_dump_card_state(ahc);
printk("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\n");
ahc_outb(ahc, SCBPTR, saved_scbptr);
if (paused == 0)
@@ -7442,7 +7372,7 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
if ((ahc->features & AHC_MULTIROLE) != 0) {
if ((ahc->features & AHC_MULTI_TID) != 0
- && (ahc->flags & AHC_INITIATORROLE) != 0) {
+ && (ahc->flags & AHC_INITIATORROLE) != 0) {
/*
* Only allow additional targets if
* the initiator role is disabled.
@@ -7558,14 +7488,13 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
return;
}
}
- lstate = kmalloc(sizeof(*lstate), GFP_ATOMIC);
+ lstate = kzalloc(sizeof(*lstate), GFP_ATOMIC);
if (lstate == NULL) {
xpt_print_path(ccb->ccb_h.path);
printk("Couldn't allocate lstate\n");
ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
return;
}
- memset(lstate, 0, sizeof(*lstate));
status = xpt_create_path(&lstate->path, /*periph*/NULL,
xpt_path_path_id(ccb->ccb_h.path),
xpt_path_target_id(ccb->ccb_h.path),
@@ -7594,7 +7523,6 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
targid_mask |= target_mask;
ahc_outb(ahc, TARGID, targid_mask);
ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
-
ahc_update_scsiid(ahc, targid_mask);
} else {
u_int our_id;
@@ -7659,7 +7587,7 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
}
ahc_lock(ahc, &s);
-
+
ccb->ccb_h.status = CAM_REQ_CMP;
LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
struct ccb_hdr *ccbh;
@@ -7718,7 +7646,7 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
targid_mask &= ~target_mask;
ahc_outb(ahc, TARGID, targid_mask);
ahc_outb(ahc, TARGID+1,
- (targid_mask >> 8));
+ (targid_mask >> 8));
ahc_update_scsiid(ahc, targid_mask);
}
}
@@ -7847,7 +7775,7 @@ ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
} else {
if (!paused)
- ahc_pause(ahc);
+ ahc_pause(ahc);
ahc_outb(ahc, KERNEL_TQINPOS,
ahc->tqinfifonext & HOST_TQINPOS);
if (!paused)
@@ -7946,7 +7874,7 @@ ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
printk("Reserved or VU command code type encountered\n");
break;
}
-
+
memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
atio->ccb_h.status |= CAM_CDB_RECVD;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index c6251bb4f438..8b2b98666d61 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -1,3 +1,4 @@
+
/*
* Adaptec AIC7xxx device driver for Linux.
*
@@ -145,16 +146,6 @@ static struct scsi_transport_template *ahc_linux_transport_template = NULL;
#endif
/*
- * Control collection of SCSI transfer statistics for the /proc filesystem.
- *
- * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
- * NOTE: This does affect performance since it has to maintain statistics.
- */
-#ifdef CONFIG_AIC7XXX_PROC_STATS
-#define AIC7XXX_PROC_STATS
-#endif
-
-/*
* To change the default number of tagged transactions allowed per-device,
* add a line to the lilo.conf file like:
* append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
@@ -360,10 +351,10 @@ MODULE_PARM_DESC(aic7xxx,
" seltime:<int> Selection Timeout\n"
" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
"\n"
-" Sample /etc/modprobe.conf line:\n"
-" Toggle EISA/VLB probing\n"
-" Set tag depth on Controller 1/Target 1 to 10 tags\n"
-" Shorten the selection timeout to 128ms\n"
+" Sample modprobe configuration file:\n"
+" # Toggle EISA/VLB probing\n"
+" # Set tag depth on Controller 1/Target 1 to 10 tags\n"
+" # Shorten the selection timeout to 128ms\n"
"\n"
" options aic7xxx 'aic7xxx=probe_eisa_vl.tag_info:{{}.{.10}}.seltime:1'\n"
);
@@ -375,7 +366,8 @@ static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
struct scsi_cmnd *cmd);
static void ahc_linux_freeze_simq(struct ahc_softc *ahc);
static void ahc_linux_release_simq(struct ahc_softc *ahc);
-static int ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag);
+static int ahc_linux_queue_recovery_cmd(struct scsi_device *sdev,
+ struct scsi_cmnd *cmd);
static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
@@ -462,7 +454,7 @@ ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
static void ahc_linux_unmap_scb(struct ahc_softc*, struct scb*);
static int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
- struct ahc_dma_seg *sg,
+ struct ahc_dma_seg *sg,
dma_addr_t addr, bus_size_t len);
static void
@@ -527,8 +519,7 @@ ahc_linux_info(struct Scsi_Host *host)
/*
* Queue an SCB to the controller.
*/
-static int
-ahc_linux_queue_lck(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *))
+static int ahc_linux_queue_lck(struct scsi_cmnd *cmd)
{
struct ahc_softc *ahc;
struct ahc_linux_device *dev = scsi_transport_device_data(cmd->device);
@@ -539,7 +530,6 @@ ahc_linux_queue_lck(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd
ahc_lock(ahc, &flags);
if (ahc->platform_data->qfrozen == 0) {
- cmd->scsi_done = scsi_done;
cmd->result = CAM_REQ_INPROG << 16;
rtn = ahc_linux_run_command(ahc, dev, cmd);
}
@@ -574,8 +564,6 @@ ahc_linux_target_alloc(struct scsi_target *starget)
struct scsi_target **ahc_targp = ahc_linux_target_in_softc(starget);
unsigned short scsirate;
struct ahc_devinfo devinfo;
- struct ahc_initiator_tinfo *tinfo;
- struct ahc_tmode_tstate *tstate;
char channel = starget->channel + 'A';
unsigned int our_id = ahc->our_id;
unsigned int target_offset;
@@ -583,7 +571,7 @@ ahc_linux_target_alloc(struct scsi_target *starget)
target_offset = starget->id;
if (starget->channel != 0)
target_offset += 8;
-
+
if (starget->channel)
our_id = ahc->our_id_b;
@@ -609,22 +597,19 @@ ahc_linux_target_alloc(struct scsi_target *starget)
ultra = 0;
flags &= ~CFXFER;
}
-
+
if ((ahc->features & AHC_ULTRA2) != 0) {
scsirate = (flags & CFXFER) | (ultra ? 0x8 : 0);
} else {
scsirate = (flags & CFXFER) << 4;
- maxsync = ultra ? AHC_SYNCRATE_ULTRA :
+ maxsync = ultra ? AHC_SYNCRATE_ULTRA :
AHC_SYNCRATE_FAST;
}
spi_max_width(starget) = (flags & CFWIDEB) ? 1 : 0;
if (!(flags & CFSYNCH))
spi_max_offset(starget) = 0;
- spi_min_period(starget) =
+ spi_min_period(starget) =
ahc_find_period(ahc, scsirate, maxsync);
-
- tinfo = ahc_fetch_transinfo(ahc, channel, ahc->our_id,
- starget->id, &tstate);
}
ahc_compile_devinfo(&devinfo, our_id, starget->id,
CAM_LUN_WILDCARD, channel,
@@ -647,7 +632,7 @@ ahc_linux_target_destroy(struct scsi_target *starget)
}
static int
-ahc_linux_slave_alloc(struct scsi_device *sdev)
+ahc_linux_sdev_init(struct scsi_device *sdev)
{
struct ahc_softc *ahc =
*((struct ahc_softc **)sdev->host->hostdata);
@@ -672,19 +657,15 @@ ahc_linux_slave_alloc(struct scsi_device *sdev)
* a tagged queuing capable device.
*/
dev->maxtags = 0;
-
+
spi_period(starget) = 0;
return 0;
}
static int
-ahc_linux_slave_configure(struct scsi_device *sdev)
+ahc_linux_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
- struct ahc_softc *ahc;
-
- ahc = *((struct ahc_softc **)sdev->host->hostdata);
-
if (bootverbose)
sdev_printk(KERN_INFO, sdev, "Slave Configure\n");
@@ -702,14 +683,12 @@ ahc_linux_slave_configure(struct scsi_device *sdev)
* Return the disk geometry for the given SCSI device.
*/
static int
-ahc_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ahc_linux_biosparam(struct scsi_device *sdev, struct gendisk *disk,
sector_t capacity, int geom[])
{
- uint8_t *bh;
int heads;
int sectors;
int cylinders;
- int ret;
int extended;
struct ahc_softc *ahc;
u_int channel;
@@ -717,14 +696,9 @@ ahc_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
ahc = *((struct ahc_softc **)sdev->host->hostdata);
channel = sdev_channel(sdev);
- bh = scsi_bios_ptable(bdev);
- if (bh) {
- ret = scsi_partsize(bh, capacity,
- &geom[2], &geom[0], &geom[1]);
- kfree(bh);
- if (ret != -1)
- return (ret);
- }
+ if (scsi_partsize(disk, capacity, geom))
+ return 0;
+
heads = 64;
sectors = 32;
cylinders = aic_sector_div(capacity, heads, sectors);
@@ -755,8 +729,8 @@ ahc_linux_abort(struct scsi_cmnd *cmd)
{
int error;
- error = ahc_linux_queue_recovery_cmd(cmd, SCB_ABORT);
- if (error != 0)
+ error = ahc_linux_queue_recovery_cmd(cmd->device, cmd);
+ if (error != SUCCESS)
printk("aic7xxx_abort returns 0x%x\n", error);
return (error);
}
@@ -769,8 +743,8 @@ ahc_linux_dev_reset(struct scsi_cmnd *cmd)
{
int error;
- error = ahc_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
- if (error != 0)
+ error = ahc_linux_queue_recovery_cmd(cmd->device, NULL);
+ if (error != SUCCESS)
printk("aic7xxx_dev_reset returns 0x%x\n", error);
return (error);
}
@@ -803,7 +777,8 @@ struct scsi_host_template aic7xxx_driver_template = {
.module = THIS_MODULE,
.name = "aic7xxx",
.proc_name = "aic7xxx",
- .proc_info = ahc_linux_proc_info,
+ .show_info = ahc_linux_show_info,
+ .write_info = ahc_proc_write_seeprom,
.info = ahc_linux_info,
.queuecommand = ahc_linux_queue,
.eh_abort_handler = ahc_linux_abort,
@@ -816,20 +791,26 @@ struct scsi_host_template aic7xxx_driver_template = {
.this_id = -1,
.max_sectors = 8192,
.cmd_per_lun = 2,
- .use_clustering = ENABLE_CLUSTERING,
- .slave_alloc = ahc_linux_slave_alloc,
- .slave_configure = ahc_linux_slave_configure,
+ .sdev_init = ahc_linux_sdev_init,
+ .sdev_configure = ahc_linux_sdev_configure,
.target_alloc = ahc_linux_target_alloc,
.target_destroy = ahc_linux_target_destroy,
};
/**************************** Tasklet Handler *********************************/
-/******************************** Macros **************************************/
-#define BUILD_SCSIID(ahc, cmd) \
- ((((cmd)->device->id << TID_SHIFT) & TID) \
- | (((cmd)->device->channel == 0) ? (ahc)->our_id : (ahc)->our_id_b) \
- | (((cmd)->device->channel == 0) ? 0 : TWIN_CHNLB))
+
+static inline unsigned int ahc_build_scsiid(struct ahc_softc *ahc,
+ struct scsi_device *sdev)
+{
+ unsigned int scsiid = (sdev->id << TID_SHIFT) & TID;
+
+ if (sdev->channel == 0)
+ scsiid |= ahc->our_id;
+ else
+ scsiid |= ahc->our_id_b | TWIN_CHNLB;
+ return scsiid;
+}
/******************************** Bus DMA *************************************/
int
@@ -870,8 +851,8 @@ int
ahc_dmamem_alloc(struct ahc_softc *ahc, bus_dma_tag_t dmat, void** vaddr,
int flags, bus_dmamap_t *mapp)
{
- *vaddr = pci_alloc_consistent(ahc->dev_softc,
- dmat->maxsize, mapp);
+ /* XXX: check if we really need the GFP_ATOMIC and unwind this mess! */
+ *vaddr = dma_alloc_coherent(ahc->dev, dmat->maxsize, mapp, GFP_ATOMIC);
if (*vaddr == NULL)
return ENOMEM;
return 0;
@@ -881,8 +862,7 @@ void
ahc_dmamem_free(struct ahc_softc *ahc, bus_dma_tag_t dmat,
void* vaddr, bus_dmamap_t map)
{
- pci_free_consistent(ahc->dev_softc, dmat->maxsize,
- vaddr, map);
+ dma_free_coherent(ahc->dev, dmat->maxsize, vaddr, map);
}
int
@@ -1105,7 +1085,7 @@ ahc_linux_register_host(struct ahc_softc *ahc, struct scsi_host_template *templa
template->name = ahc->description;
host = scsi_host_alloc(template, sizeof(struct ahc_softc *));
if (host == NULL)
- return (ENOMEM);
+ return -ENOMEM;
*((struct ahc_softc **)host->hostdata) = ahc;
ahc->platform_data->host = host;
@@ -1133,8 +1113,7 @@ ahc_linux_register_host(struct ahc_softc *ahc, struct scsi_host_template *templa
host->transportt = ahc_linux_transport_template;
- retval = scsi_add_host(host,
- (ahc->dev_softc ? &ahc->dev_softc->dev : NULL));
+ retval = scsi_add_host(host, ahc->dev);
if (retval) {
printk(KERN_WARNING "aic7xxx: scsi_add_host failed\n");
scsi_host_put(host);
@@ -1150,7 +1129,7 @@ ahc_linux_register_host(struct ahc_softc *ahc, struct scsi_host_template *templa
* or forcing transfer negotiations on the next command to any
* target.
*/
-void
+static void
ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc)
{
int i;
@@ -1222,10 +1201,9 @@ ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
{
ahc->platform_data =
- kmalloc(sizeof(struct ahc_platform_data), GFP_ATOMIC);
+ kzalloc(sizeof(struct ahc_platform_data), GFP_ATOMIC);
if (ahc->platform_data == NULL)
return (ENOMEM);
- memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));
ahc->platform_data->irq = AHC_LINUX_NOIRQ;
ahc_lockinit(ahc);
ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
@@ -1248,8 +1226,8 @@ ahc_platform_free(struct ahc_softc *ahc)
starget = ahc->platform_data->starget[i];
if (starget != NULL) {
ahc->platform_data->starget[i] = NULL;
- }
- }
+ }
+ }
if (ahc->platform_data->irq != AHC_LINUX_NOIRQ)
free_irq(ahc->platform_data->irq, ahc);
@@ -1296,7 +1274,7 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,
default:
case AHC_QUEUE_NONE:
now_queuing = 0;
- break;
+ break;
case AHC_QUEUE_BASIC:
now_queuing = AHC_DEV_Q_BASIC;
break;
@@ -1343,12 +1321,9 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,
}
switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) {
case AHC_DEV_Q_BASIC:
- scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
- break;
case AHC_DEV_Q_TAGGED:
- scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
+ scsi_change_queue_depth(sdev,
+ dev->openings + dev->active);
break;
default:
/*
@@ -1357,7 +1332,7 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,
* serially on the controller/device. This should
* remove some latency.
*/
- scsi_deactivate_tcq(sdev, 2);
+ scsi_change_queue_depth(sdev, 2);
break;
}
}
@@ -1456,7 +1431,7 @@ ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev,
* we are storing a full busy target *lun*
* table in SCB space.
*/
- if (!blk_rq_tagged(cmd->request)
+ if (!(cmd->flags & SCMD_TAGGED)
&& (ahc->features & AHC_SCB_BTT) == 0) {
int target_offset;
@@ -1490,7 +1465,7 @@ ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev,
* Fill out basics of the HSCB.
*/
hscb->control = 0;
- hscb->scsiid = BUILD_SCSIID(ahc, cmd);
+ hscb->scsiid = ahc_build_scsiid(ahc, cmd->device);
hscb->lun = cmd->device->lun;
mask = SCB_GET_TARGET_MASK(ahc, scb);
tinfo = ahc_fetch_transinfo(ahc, SCB_GET_CHANNEL(ahc, scb),
@@ -1500,30 +1475,22 @@ ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev,
hscb->scsioffset = tinfo->curr.offset;
if ((tstate->ultraenb & mask) != 0)
hscb->control |= ULTRAENB;
-
+
if ((ahc->user_discenable & mask) != 0)
hscb->control |= DISCENB;
-
+
if ((tstate->auto_negotiate & mask) != 0) {
scb->flags |= SCB_AUTO_NEGOTIATE;
scb->hscb->control |= MK_MESSAGE;
}
if ((dev->flags & (AHC_DEV_Q_TAGGED|AHC_DEV_Q_BASIC)) != 0) {
- int msg_bytes;
- uint8_t tag_msgs[2];
-
- msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
- if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
- hscb->control |= tag_msgs[0];
- if (tag_msgs[0] == MSG_ORDERED_TASK)
- dev->commands_since_idle_or_otag = 0;
- } else if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
+ if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
&& (dev->flags & AHC_DEV_Q_TAGGED) != 0) {
- hscb->control |= MSG_ORDERED_TASK;
+ hscb->control |= ORDERED_QUEUE_TAG;
dev->commands_since_idle_or_otag = 0;
} else {
- hscb->control |= MSG_SIMPLE_TASK;
+ hscb->control |= SIMPLE_QUEUE_TAG;
}
}
@@ -1571,7 +1538,7 @@ ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev,
*/
scb->hscb->sgptr =
ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
-
+
/*
* Copy the first SG into the "current"
* data pointer area.
@@ -1591,7 +1558,7 @@ ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev,
dev->commands_issued++;
if ((dev->flags & AHC_DEV_PERIODIC_OTAG) != 0)
dev->commands_since_idle_or_otag++;
-
+
scb->flags |= SCB_ACTIVE;
if (untagged_q) {
TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
@@ -1612,7 +1579,7 @@ ahc_linux_isr(int irq, void *dev_id)
int ours;
ahc = (struct ahc_softc *) dev_id;
- ahc_lock(ahc, &flags);
+ ahc_lock(ahc, &flags);
ours = ahc_intr(ahc);
ahc_unlock(ahc, &flags);
return IRQ_RETVAL(ours);
@@ -1631,10 +1598,7 @@ ahc_send_async(struct ahc_softc *ahc, char channel,
switch (code) {
case AC_TRANSFER_NEG:
{
- char buf[80];
struct scsi_target *starget;
- struct ahc_linux_target *targ;
- struct info_str info;
struct ahc_initiator_tinfo *tinfo;
struct ahc_tmode_tstate *tstate;
int target_offset;
@@ -1642,10 +1606,6 @@ ahc_send_async(struct ahc_softc *ahc, char channel,
BUG_ON(target == CAM_TARGET_WILDCARD);
- info.buffer = buf;
- info.length = sizeof(buf);
- info.offset = 0;
- info.pos = 0;
tinfo = ahc_fetch_transinfo(ahc, channel,
channel == 'A' ? ahc->our_id
: ahc->our_id_b,
@@ -1672,7 +1632,6 @@ ahc_send_async(struct ahc_softc *ahc, char channel,
starget = ahc->platform_data->starget[target_offset];
if (starget == NULL)
break;
- targ = scsi_transport_target_data(starget);
target_ppr_options =
(spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0)
@@ -1695,22 +1654,22 @@ ahc_send_async(struct ahc_softc *ahc, char channel,
spi_display_xfer_agreement(starget);
break;
}
- case AC_SENT_BDR:
+ case AC_SENT_BDR:
{
WARN_ON(lun != CAM_LUN_WILDCARD);
scsi_report_device_reset(ahc->platform_data->host,
channel - 'A', target);
break;
}
- case AC_BUS_RESET:
+ case AC_BUS_RESET:
if (ahc->platform_data->host != NULL) {
scsi_report_bus_reset(ahc->platform_data->host,
channel - 'A');
}
- break;
- default:
- panic("ahc_send_async: Unexpected async event");
- }
+ break;
+ default:
+ panic("ahc_send_async: Unexpected async event");
+ }
}
/*
@@ -1759,10 +1718,12 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb)
*/
cmd->sense_buffer[0] = 0;
if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
+#ifdef AHC_REPORT_UNDERFLOWS
uint32_t amount_xferred;
amount_xferred =
ahc_get_transfer_length(scb) - ahc_get_residual(scb);
+#endif
if ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
#ifdef AHC_DEBUG
if ((ahc_debug & AHC_SHOW_MISC) != 0) {
@@ -1805,7 +1766,7 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb)
if (dev->openings == 1
&& ahc_get_transaction_status(scb) == CAM_REQ_CMP
- && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+ && ahc_get_scsi_status(scb) != SAM_STAT_TASK_SET_FULL)
dev->tag_success_count++;
/*
* Some devices deal with temporary internal resource
@@ -1848,7 +1809,7 @@ ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
sdev->sdev_target->id, sdev->lun,
sdev->sdev_target->channel == 0 ? 'A' : 'B',
ROLE_INITIATOR);
-
+
/*
* We don't currently trust the mid-layer to
* properly deal with queue full or busy. So,
@@ -1862,8 +1823,8 @@ ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
switch (ahc_get_scsi_status(scb)) {
default:
break;
- case SCSI_STATUS_CHECK_COND:
- case SCSI_STATUS_CMD_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
+ case SAM_STAT_COMMAND_TERMINATED:
{
struct scsi_cmnd *cmd;
@@ -1883,7 +1844,6 @@ ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
if (sense_size < SCSI_SENSE_BUFFERSIZE)
memset(&cmd->sense_buffer[sense_size], 0,
SCSI_SENSE_BUFFERSIZE - sense_size);
- cmd->result |= (DRIVER_SENSE << 24);
#ifdef AHC_DEBUG
if (ahc_debug & AHC_SHOW_SENSE) {
int i;
@@ -1901,7 +1861,7 @@ ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
}
break;
}
- case SCSI_STATUS_QUEUE_FULL:
+ case SAM_STAT_TASK_SET_FULL:
{
/*
* By the time the core driver has returned this
@@ -1945,7 +1905,7 @@ ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
dev->last_queuefull_same_count = 0;
}
ahc_set_transaction_status(scb, CAM_REQUEUE_REQ);
- ahc_set_scsi_status(scb, SCSI_STATUS_OK);
+ ahc_set_scsi_status(scb, SAM_STAT_GOOD);
ahc_platform_set_tags(ahc, sdev, &devinfo,
(dev->flags & AHC_DEV_Q_BASIC)
? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
@@ -1956,7 +1916,7 @@ ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
* as if the target returned BUSY SCSI status.
*/
dev->openings = 1;
- ahc_set_scsi_status(scb, SCSI_STATUS_BUSY);
+ ahc_set_scsi_status(scb, SAM_STAT_BUSY);
ahc_platform_set_tags(ahc, sdev, &devinfo,
(dev->flags & AHC_DEV_Q_BASIC)
? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
@@ -2032,7 +1992,7 @@ ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, struct scsi_cmnd *cmd)
ahc_cmd_set_transaction_status(cmd, new_status);
}
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
static void
@@ -2077,11 +2037,12 @@ ahc_linux_release_simq(struct ahc_softc *ahc)
}
static int
-ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
+ahc_linux_queue_recovery_cmd(struct scsi_device *sdev,
+ struct scsi_cmnd *cmd)
{
struct ahc_softc *ahc;
struct ahc_linux_device *dev;
- struct scb *pending_scb;
+ struct scb *pending_scb = NULL, *scb;
u_int saved_scbptr;
u_int active_scb_index;
u_int last_phase;
@@ -2094,18 +2055,19 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
int disconnected;
unsigned long flags;
- pending_scb = NULL;
paused = FALSE;
wait = FALSE;
- ahc = *(struct ahc_softc **)cmd->device->host->hostdata;
+ ahc = *(struct ahc_softc **)sdev->host->hostdata;
- scmd_printk(KERN_INFO, cmd, "Attempting to queue a%s message\n",
- flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+ sdev_printk(KERN_INFO, sdev, "Attempting to queue a%s message\n",
+ cmd ? "n ABORT" : " TARGET RESET");
- printk("CDB:");
- for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)
- printk(" 0x%x", cmd->cmnd[cdb_byte]);
- printk("\n");
+ if (cmd) {
+ printk("CDB:");
+ for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)
+ printk(" 0x%x", cmd->cmnd[cdb_byte]);
+ printk("\n");
+ }
ahc_lock(ahc, &flags);
@@ -2116,7 +2078,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* at all, and the system wanted us to just abort the
* command, return success.
*/
- dev = scsi_transport_device_data(cmd->device);
+ dev = scsi_transport_device_data(sdev);
if (dev == NULL) {
/*
@@ -2124,20 +2086,19 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* so we must not still own the command.
*/
printk("%s:%d:%d:%d: Is not an active device\n",
- ahc_name(ahc), cmd->device->channel, cmd->device->id,
- cmd->device->lun);
+ ahc_name(ahc), sdev->channel, sdev->id, (u8)sdev->lun);
retval = SUCCESS;
goto no_cmd;
}
- if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0
+ if (cmd && (dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0
&& ahc_search_untagged_queues(ahc, cmd, cmd->device->id,
cmd->device->channel + 'A',
- cmd->device->lun,
+ (u8)cmd->device->lun,
CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) {
printk("%s:%d:%d:%d: Command found on untagged queue\n",
ahc_name(ahc), cmd->device->channel, cmd->device->id,
- cmd->device->lun);
+ (u8)cmd->device->lun);
retval = SUCCESS;
goto done;
}
@@ -2145,25 +2106,28 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
/*
* See if we can find a matching cmd in the pending list.
*/
- LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
- if (pending_scb->io_ctx == cmd)
- break;
- }
-
- if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
-
+ if (cmd) {
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ if (scb->io_ctx == cmd) {
+ pending_scb = scb;
+ break;
+ }
+ }
+ } else {
/* Any SCB for this device will do for a target reset */
- LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
- if (ahc_match_scb(ahc, pending_scb, scmd_id(cmd),
- scmd_channel(cmd) + 'A',
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ if (ahc_match_scb(ahc, scb, sdev->id,
+ sdev->channel + 'A',
CAM_LUN_WILDCARD,
- SCB_LIST_NULL, ROLE_INITIATOR))
+ SCB_LIST_NULL, ROLE_INITIATOR)) {
+ pending_scb = scb;
break;
+ }
}
}
if (pending_scb == NULL) {
- scmd_printk(KERN_INFO, cmd, "Command not found\n");
+ sdev_printk(KERN_INFO, sdev, "Command not found\n");
goto no_cmd;
}
@@ -2194,22 +2158,23 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
ahc_dump_card_state(ahc);
disconnected = TRUE;
- if (flag == SCB_ABORT) {
- if (ahc_search_qinfifo(ahc, cmd->device->id,
- cmd->device->channel + 'A',
- cmd->device->lun,
+ if (cmd) {
+ if (ahc_search_qinfifo(ahc, sdev->id,
+ sdev->channel + 'A',
+ sdev->lun,
pending_scb->hscb->tag,
ROLE_INITIATOR, CAM_REQ_ABORTED,
SEARCH_COMPLETE) > 0) {
printk("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
- ahc_name(ahc), cmd->device->channel,
- cmd->device->id, cmd->device->lun);
+ ahc_name(ahc), sdev->channel,
+ sdev->id, (u8)sdev->lun);
retval = SUCCESS;
goto done;
}
- } else if (ahc_search_qinfifo(ahc, cmd->device->id,
- cmd->device->channel + 'A',
- cmd->device->lun, pending_scb->hscb->tag,
+ } else if (ahc_search_qinfifo(ahc, sdev->id,
+ sdev->channel + 'A',
+ sdev->lun,
+ pending_scb->hscb->tag,
ROLE_INITIATOR, /*status*/0,
SEARCH_COUNT) > 0) {
disconnected = FALSE;
@@ -2221,7 +2186,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG));
if (bus_scb == pending_scb)
disconnected = FALSE;
- else if (flag != SCB_ABORT
+ else if (!cmd
&& ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid
&& ahc_inb(ahc, SAVED_LUN) == SCB_GET_LUN(pending_scb))
disconnected = FALSE;
@@ -2241,18 +2206,18 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
if (last_phase != P_BUSFREE
&& (pending_scb->hscb->tag == active_scb_index
- || (flag == SCB_DEVICE_RESET
- && SCSIID_TARGET(ahc, saved_scsiid) == scmd_id(cmd)))) {
+ || (!cmd && SCSIID_TARGET(ahc, saved_scsiid) == sdev->id))) {
/*
* We're active on the bus, so assert ATN
* and hope that the target responds.
*/
pending_scb = ahc_lookup_scb(ahc, active_scb_index);
- pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+ pending_scb->flags |= SCB_RECOVERY_SCB;
+ pending_scb->flags |= cmd ? SCB_ABORT : SCB_DEVICE_RESET;
ahc_outb(ahc, MSG_OUT, HOST_MSG);
ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
- scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n");
+ sdev_printk(KERN_INFO, sdev, "Device is active, asserting ATN\n");
wait = TRUE;
} else if (disconnected) {
@@ -2273,7 +2238,8 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* an unsolicited reselection occurred.
*/
pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
- pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+ pending_scb->flags |= SCB_RECOVERY_SCB;
+ pending_scb->flags |= cmd ? SCB_ABORT : SCB_DEVICE_RESET;
/*
* Remove any cached copy of this SCB in the
@@ -2282,9 +2248,9 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* same element in the SCB, SCB_NEXT, for
* both the qinfifo and the disconnected list.
*/
- ahc_search_disc_list(ahc, cmd->device->id,
- cmd->device->channel + 'A',
- cmd->device->lun, pending_scb->hscb->tag,
+ ahc_search_disc_list(ahc, sdev->id,
+ sdev->channel + 'A',
+ sdev->lun, pending_scb->hscb->tag,
/*stop_on_first*/TRUE,
/*remove*/TRUE,
/*save_state*/FALSE);
@@ -2307,9 +2273,9 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* so we are the next SCB for this target
* to run.
*/
- ahc_search_qinfifo(ahc, cmd->device->id,
- cmd->device->channel + 'A',
- cmd->device->lun, SCB_LIST_NULL,
+ ahc_search_qinfifo(ahc, sdev->id,
+ sdev->channel + 'A',
+ (u8)sdev->lun, SCB_LIST_NULL,
ROLE_INITIATOR, CAM_REQUEUE_REQ,
SEARCH_COMPLETE);
ahc_qinfifo_requeue_tail(ahc, pending_scb);
@@ -2318,7 +2284,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
printk("Device is disconnected, re-queuing SCB\n");
wait = TRUE;
} else {
- scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n");
+ sdev_printk(KERN_INFO, sdev, "Unable to deliver message\n");
retval = FAILED;
goto done;
}
@@ -2355,11 +2321,6 @@ done:
return (retval);
}
-void
-ahc_platform_dump_card_state(struct ahc_softc *ahc)
-{
-}
-
static void ahc_linux_set_width(struct scsi_target *starget, int width)
{
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
@@ -2379,7 +2340,7 @@ static void ahc_linux_set_period(struct scsi_target *starget, int period)
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
struct ahc_tmode_tstate *tstate;
- struct ahc_initiator_tinfo *tinfo
+ struct ahc_initiator_tinfo *tinfo
= ahc_fetch_transinfo(ahc,
starget->channel + 'A',
shost->this_id, starget->id, &tstate);
@@ -2411,7 +2372,8 @@ static void ahc_linux_set_period(struct scsi_target *starget, int period)
ppr_options &= MSG_EXT_PPR_QAS_REQ;
}
- syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ AHC_SYNCRATE_DT);
ahc_lock(ahc, &flags);
ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset,
ppr_options, AHC_TRANS_GOAL, FALSE);
@@ -2423,7 +2385,7 @@ static void ahc_linux_set_offset(struct scsi_target *starget, int offset)
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
struct ahc_tmode_tstate *tstate;
- struct ahc_initiator_tinfo *tinfo
+ struct ahc_initiator_tinfo *tinfo
= ahc_fetch_transinfo(ahc,
starget->channel + 'A',
shost->this_id, starget->id, &tstate);
@@ -2436,7 +2398,8 @@ static void ahc_linux_set_offset(struct scsi_target *starget, int offset)
ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
starget->channel + 'A', ROLE_INITIATOR);
if (offset != 0) {
- syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ AHC_SYNCRATE_DT);
period = tinfo->goal.period;
ppr_options = tinfo->goal.ppr_options;
}
@@ -2451,7 +2414,7 @@ static void ahc_linux_set_dt(struct scsi_target *starget, int dt)
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
struct ahc_tmode_tstate *tstate;
- struct ahc_initiator_tinfo *tinfo
+ struct ahc_initiator_tinfo *tinfo
= ahc_fetch_transinfo(ahc,
starget->channel + 'A',
shost->this_id, starget->id, &tstate);
@@ -2472,7 +2435,8 @@ static void ahc_linux_set_dt(struct scsi_target *starget, int dt)
ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
starget->channel + 'A', ROLE_INITIATOR);
- syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,AHC_SYNCRATE_DT);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ AHC_SYNCRATE_DT);
ahc_lock(ahc, &flags);
ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->goal.offset,
ppr_options, AHC_TRANS_GOAL, FALSE);
@@ -2489,7 +2453,7 @@ static void ahc_linux_set_qas(struct scsi_target *starget, int qas)
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
struct ahc_tmode_tstate *tstate;
- struct ahc_initiator_tinfo *tinfo
+ struct ahc_initiator_tinfo *tinfo
= ahc_fetch_transinfo(ahc,
starget->channel + 'A',
shost->this_id, starget->id, &tstate);
@@ -2505,7 +2469,8 @@ static void ahc_linux_set_qas(struct scsi_target *starget, int qas)
ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
starget->channel + 'A', ROLE_INITIATOR);
- syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ AHC_SYNCRATE_DT);
ahc_lock(ahc, &flags);
ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->goal.offset,
ppr_options, AHC_TRANS_GOAL, FALSE);
@@ -2517,7 +2482,7 @@ static void ahc_linux_set_iu(struct scsi_target *starget, int iu)
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
struct ahc_tmode_tstate *tstate;
- struct ahc_initiator_tinfo *tinfo
+ struct ahc_initiator_tinfo *tinfo
= ahc_fetch_transinfo(ahc,
starget->channel + 'A',
shost->this_id, starget->id, &tstate);
@@ -2533,7 +2498,8 @@ static void ahc_linux_set_iu(struct scsi_target *starget, int iu)
ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
starget->channel + 'A', ROLE_INITIATOR);
- syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ AHC_SYNCRATE_DT);
ahc_lock(ahc, &flags);
ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->goal.offset,
ppr_options, AHC_TRANS_GOAL, FALSE);
@@ -2549,7 +2515,7 @@ static void ahc_linux_get_signalling(struct Scsi_Host *shost)
if (!(ahc->features & AHC_ULTRA2)) {
/* non-LVD chipset, may not have SBLKCTL reg */
- spi_signalling(shost) =
+ spi_signalling(shost) =
ahc->features & AHC_HVD ?
SPI_SIGNAL_HVD :
SPI_SIGNAL_SE;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h
index bca0fb83f553..51d9f4de0734 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.h
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h
@@ -82,11 +82,6 @@
/* Core SCSI definitions */
#define AIC_LIB_PREFIX ahc
-/* Name space conflict with BSD queue macros */
-#ifdef LIST_HEAD
-#undef LIST_HEAD
-#endif
-
#include "cam.h"
#include "queue.h"
#include "scsi_message.h"
@@ -214,7 +209,7 @@ int ahc_dmamap_unload(struct ahc_softc *, bus_dma_tag_t, bus_dmamap_t);
/*
* XXX
* ahc_dmamap_sync is only used on buffers allocated with
- * the pci_alloc_consistent() API. Although I'm not sure how
+ * the dma_alloc_coherent() API. Although I'm not sure how
* this works on architectures with a write buffer, Linux does
* not have an API to sync "coherent" memory. Perhaps we need
* to do an mb()?
@@ -263,7 +258,7 @@ struct ahc_linux_device {
int active;
/*
- * The currently allowed number of
+ * The currently allowed number of
* transactions that can be queued to
* the device. Must be signed for
* conversion from tagged to untagged
@@ -277,7 +272,7 @@ struct ahc_linux_device {
* device's queue is halted.
*/
u_int qfrozen;
-
+
/*
* Cumulative command counter.
*/
@@ -356,16 +351,16 @@ struct ahc_platform_data {
/*
* Fields accessed from interrupt context.
*/
- struct scsi_target *starget[AHC_NUM_TARGETS];
+ struct scsi_target *starget[AHC_NUM_TARGETS];
spinlock_t spin_lock;
u_int qfrozen;
struct completion *eh_done;
- struct Scsi_Host *host; /* pointer to scsi host */
+ struct Scsi_Host *host; /* pointer to scsi host */
#define AHC_LINUX_NOIRQ ((uint32_t)~0)
uint32_t irq; /* IRQ for this adapter */
uint32_t bios_address;
- resource_size_t mem_busaddr; /* Mem Base Addr */
+ resource_size_t mem_busaddr; /* Mem Base Addr */
};
void ahc_delay(long);
@@ -383,14 +378,6 @@ void ahc_insb(struct ahc_softc * ahc, long port,
int ahc_linux_register_host(struct ahc_softc *,
struct scsi_host_template *);
-/*************************** Pretty Printing **********************************/
-struct info_str {
- char *buffer;
- int length;
- off_t offset;
- int pos;
-};
-
/******************************** Locking *************************************/
/* Lock protecting internal data structures */
@@ -446,8 +433,6 @@ ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
/* config registers for header type 0 devices */
#define PCIR_MAPS 0x10
-#define PCIR_SUBVEND_0 0x2c
-#define PCIR_SUBDEV_0 0x2e
typedef enum
{
@@ -523,34 +508,11 @@ ahc_flush_device_writes(struct ahc_softc *ahc)
}
/**************************** Proc FS Support *********************************/
-int ahc_linux_proc_info(struct Scsi_Host *, char *, char **,
- off_t, int, int);
+int ahc_proc_write_seeprom(struct Scsi_Host *, char *, int);
+int ahc_linux_show_info(struct seq_file *, struct Scsi_Host *);
/*************************** Domain Validation ********************************/
/*********************** Transaction Access Wrappers *************************/
-static inline void ahc_cmd_set_transaction_status(struct scsi_cmnd *, uint32_t);
-static inline void ahc_set_transaction_status(struct scb *, uint32_t);
-static inline void ahc_cmd_set_scsi_status(struct scsi_cmnd *, uint32_t);
-static inline void ahc_set_scsi_status(struct scb *, uint32_t);
-static inline uint32_t ahc_cmd_get_transaction_status(struct scsi_cmnd *cmd);
-static inline uint32_t ahc_get_transaction_status(struct scb *);
-static inline uint32_t ahc_cmd_get_scsi_status(struct scsi_cmnd *cmd);
-static inline uint32_t ahc_get_scsi_status(struct scb *);
-static inline void ahc_set_transaction_tag(struct scb *, int, u_int);
-static inline u_long ahc_get_transfer_length(struct scb *);
-static inline int ahc_get_transfer_dir(struct scb *);
-static inline void ahc_set_residual(struct scb *, u_long);
-static inline void ahc_set_sense_residual(struct scb *scb, u_long resid);
-static inline u_long ahc_get_residual(struct scb *);
-static inline u_long ahc_get_sense_residual(struct scb *);
-static inline int ahc_perform_autosense(struct scb *);
-static inline uint32_t ahc_get_sense_bufsize(struct ahc_softc *,
- struct scb *);
-static inline void ahc_notify_xfer_settings_change(struct ahc_softc *,
- struct ahc_devinfo *);
-static inline void ahc_platform_scb_free(struct ahc_softc *ahc,
- struct scb *scb);
-static inline void ahc_freeze_scb(struct scb *scb);
static inline
void ahc_cmd_set_transaction_status(struct scsi_cmnd *cmd, uint32_t status)
@@ -684,9 +646,9 @@ static inline void
ahc_freeze_scb(struct scb *scb)
{
if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
- scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
- scb->platform_data->dev->qfrozen++;
- }
+ scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+ scb->platform_data->dev->qfrozen++;
+ }
}
void ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,
@@ -701,7 +663,6 @@ void ahc_done(struct ahc_softc*, struct scb*);
void ahc_send_async(struct ahc_softc *, char channel,
u_int target, u_int lun, ac_code);
void ahc_print_path(struct ahc_softc *, struct scb *);
-void ahc_platform_dump_card_state(struct ahc_softc *ahc);
#ifdef CONFIG_PCI
#define AHC_PCI_CONFIG 1
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
index ee05e8410754..198440dc0918 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
@@ -121,47 +121,23 @@ static const struct pci_device_id ahc_linux_pci_id_table[] = {
MODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
-#ifdef CONFIG_PM
-static int
-ahc_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
+static int __maybe_unused
+ahc_linux_pci_dev_suspend(struct device *dev)
{
- struct ahc_softc *ahc = pci_get_drvdata(pdev);
- int rc;
-
- if ((rc = ahc_suspend(ahc)))
- return rc;
-
- pci_save_state(pdev);
- pci_disable_device(pdev);
+ struct ahc_softc *ahc = dev_get_drvdata(dev);
- if (mesg.event & PM_EVENT_SLEEP)
- pci_set_power_state(pdev, PCI_D3hot);
-
- return rc;
+ return ahc_suspend(ahc);
}
-static int
-ahc_linux_pci_dev_resume(struct pci_dev *pdev)
+static int __maybe_unused
+ahc_linux_pci_dev_resume(struct device *dev)
{
- struct ahc_softc *ahc = pci_get_drvdata(pdev);
- int rc;
-
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
- if ((rc = pci_enable_device(pdev))) {
- dev_printk(KERN_ERR, &pdev->dev,
- "failed to enable device after resume (%d)\n", rc);
- return rc;
- }
-
- pci_set_master(pdev);
+ struct ahc_softc *ahc = dev_get_drvdata(dev);
ahc_pci_resume(ahc);
return (ahc_resume(ahc));
}
-#endif
static void
ahc_linux_pci_dev_remove(struct pci_dev *pdev)
@@ -225,10 +201,9 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ahc_get_pci_bus(pci),
ahc_get_pci_slot(pci),
ahc_get_pci_function(pci));
- name = kmalloc(strlen(buf) + 1, GFP_ATOMIC);
+ name = kstrdup(buf, GFP_ATOMIC);
if (name == NULL)
return (-ENOMEM);
- strcpy(name, buf);
ahc = ahc_alloc(NULL, name);
if (ahc == NULL)
return (-ENOMEM);
@@ -251,6 +226,7 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
}
ahc->dev_softc = pci;
+ ahc->dev = &pci->dev;
error = ahc_pci_config(ahc, entry);
if (error != 0) {
ahc_free(ahc);
@@ -265,8 +241,7 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ahc_linux_pci_inherit_flags(ahc);
pci_set_drvdata(pdev, ahc);
- ahc_linux_register_host(ahc, &aic7xxx_driver_template);
- return (0);
+ return ahc_linux_register_host(ahc, &aic7xxx_driver_template);
}
/******************************* PCI Routines *********************************/
@@ -319,14 +294,14 @@ ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
}
}
+static SIMPLE_DEV_PM_OPS(ahc_linux_pci_dev_pm_ops,
+ ahc_linux_pci_dev_suspend,
+ ahc_linux_pci_dev_resume);
static struct pci_driver aic7xxx_pci_driver = {
.name = "aic7xxx",
.probe = ahc_linux_pci_dev_probe,
-#ifdef CONFIG_PM
- .suspend = ahc_linux_pci_dev_suspend,
- .resume = ahc_linux_pci_dev_resume,
-#endif
+ .driver.pm = &ahc_linux_pci_dev_pm_ops,
.remove = ahc_linux_pci_dev_remove,
.id_table = ahc_linux_pci_id_table
};
@@ -372,7 +347,7 @@ ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
if (!request_mem_region(start, 0x1000, "aic7xxx"))
error = ENOMEM;
if (error == 0) {
- *maddr = ioremap_nocache(start, 256);
+ *maddr = ioremap(start, 256);
if (*maddr == NULL) {
error = ENOMEM;
release_mem_region(start, 0x1000);
diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c
index 6917b4f5ac9e..2d4c85426dc3 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_pci.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c
@@ -42,16 +42,9 @@
* $Id: //depot/aic7xxx/aic7xxx/aic7xxx_pci.c#79 $
*/
-#ifdef __linux__
#include "aic7xxx_osm.h"
#include "aic7xxx_inline.h"
#include "aic7xxx_93cx6.h"
-#else
-#include <dev/aic7xxx/aic7xxx_osm.h>
-#include <dev/aic7xxx/aic7xxx_inline.h>
-#include <dev/aic7xxx/aic7xxx_93cx6.h>
-#endif
-
#include "aic7xxx_pci.h"
static inline uint64_t
@@ -601,8 +594,8 @@ static const u_int ahc_num_pci_devs = ARRAY_SIZE(ahc_pci_ident_table);
#define STA 0x08
#define DPR 0x01
-static int ahc_9005_subdevinfo_valid(uint16_t vendor, uint16_t device,
- uint16_t subvendor, uint16_t subdevice);
+static int ahc_9005_subdevinfo_valid(uint16_t device, uint16_t vendor,
+ uint16_t subdevice, uint16_t subvendor);
static int ahc_ext_scbram_present(struct ahc_softc *ahc);
static void ahc_scbram_config(struct ahc_softc *ahc, int enable,
int pcheck, int fast, int large);
@@ -680,8 +673,8 @@ ahc_find_pci_device(ahc_dev_softc_t pci)
vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
- subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
- subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+ subvendor = ahc_pci_read_config(pci, PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2);
+ subdevice = ahc_pci_read_config(pci, PCI_SUBSYSTEM_ID, /*bytes*/2);
full_id = ahc_compose_id(device, vendor, subdevice, subvendor);
/*
@@ -692,7 +685,7 @@ ahc_find_pci_device(ahc_dev_softc_t pci)
* ID as valid.
*/
if (ahc_get_pci_function(pci) > 0
- && ahc_9005_subdevinfo_valid(vendor, device, subvendor, subdevice)
+ && ahc_9005_subdevinfo_valid(device, vendor, subdevice, subvendor)
&& SUBID_9005_MFUNCENB(subdevice) == 0)
return (NULL);
@@ -2015,8 +2008,7 @@ ahc_pci_chip_init(struct ahc_softc *ahc)
return (ahc_chip_init(ahc));
}
-#ifdef CONFIG_PM
-void
+void __maybe_unused
ahc_pci_resume(struct ahc_softc *ahc)
{
/*
@@ -2047,7 +2039,6 @@ ahc_pci_resume(struct ahc_softc *ahc)
ahc_release_seeprom(&sd);
}
}
-#endif
static int
ahc_aic785X_setup(struct ahc_softc *ahc)
diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c
index f2525f8ed1c7..4bc9e2dfccf6 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_proc.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c
@@ -43,16 +43,12 @@
#include "aic7xxx_inline.h"
#include "aic7xxx_93cx6.h"
-static void copy_mem_info(struct info_str *info, char *data, int len);
-static int copy_info(struct info_str *info, char *fmt, ...);
static void ahc_dump_target_state(struct ahc_softc *ahc,
- struct info_str *info,
+ struct seq_file *m,
u_int our_id, char channel,
u_int target_id, u_int target_offset);
-static void ahc_dump_device_state(struct info_str *info,
+static void ahc_dump_device_state(struct seq_file *m,
struct scsi_device *dev);
-static int ahc_proc_write_seeprom(struct ahc_softc *ahc,
- char *buffer, int length);
/*
* Table of syncrates that don't follow the "divisible by 4"
@@ -94,71 +90,28 @@ ahc_calc_syncsrate(u_int period_factor)
return (10000000 / (period_factor * 4 * 10));
}
-
-static void
-copy_mem_info(struct info_str *info, char *data, int len)
-{
- if (info->pos + len > info->offset + info->length)
- len = info->offset + info->length - info->pos;
-
- if (info->pos + len < info->offset) {
- info->pos += len;
- return;
- }
-
- if (info->pos < info->offset) {
- off_t partial;
-
- partial = info->offset - info->pos;
- data += partial;
- info->pos += partial;
- len -= partial;
- }
-
- if (len > 0) {
- memcpy(info->buffer, data, len);
- info->pos += len;
- info->buffer += len;
- }
-}
-
-static int
-copy_info(struct info_str *info, char *fmt, ...)
-{
- va_list args;
- char buf[256];
- int len;
-
- va_start(args, fmt);
- len = vsprintf(buf, fmt, args);
- va_end(args);
-
- copy_mem_info(info, buf, len);
- return (len);
-}
-
static void
-ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
+ahc_format_transinfo(struct seq_file *m, struct ahc_transinfo *tinfo)
{
u_int speed;
u_int freq;
u_int mb;
- speed = 3300;
- freq = 0;
+ speed = 3300;
+ freq = 0;
if (tinfo->offset != 0) {
freq = ahc_calc_syncsrate(tinfo->period);
speed = freq;
}
speed *= (0x01 << tinfo->width);
- mb = speed / 1000;
- if (mb > 0)
- copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
- else
- copy_info(info, "%dKB/s transfers", speed);
+ mb = speed / 1000;
+ if (mb > 0)
+ seq_printf(m, "%d.%03dMB/s transfers", mb, speed % 1000);
+ else
+ seq_printf(m, "%dKB/s transfers", speed);
if (freq != 0) {
- copy_info(info, " (%d.%03dMHz%s, offset %d",
+ seq_printf(m, " (%d.%03dMHz%s, offset %d",
freq / 1000, freq % 1000,
(tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
? " DT" : "", tinfo->offset);
@@ -166,19 +119,19 @@ ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
if (tinfo->width > 0) {
if (freq != 0) {
- copy_info(info, ", ");
+ seq_puts(m, ", ");
} else {
- copy_info(info, " (");
+ seq_puts(m, " (");
}
- copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+ seq_printf(m, "%dbit)", 8 * (0x01 << tinfo->width));
} else if (freq != 0) {
- copy_info(info, ")");
+ seq_putc(m, ')');
}
- copy_info(info, "\n");
+ seq_putc(m, '\n');
}
static void
-ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
+ahc_dump_target_state(struct ahc_softc *ahc, struct seq_file *m,
u_int our_id, char channel, u_int target_id,
u_int target_offset)
{
@@ -190,18 +143,18 @@ ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
target_id, &tstate);
if ((ahc->features & AHC_TWIN) != 0)
- copy_info(info, "Channel %c ", channel);
- copy_info(info, "Target %d Negotiation Settings\n", target_id);
- copy_info(info, "\tUser: ");
- ahc_format_transinfo(info, &tinfo->user);
+ seq_printf(m, "Channel %c ", channel);
+ seq_printf(m, "Target %d Negotiation Settings\n", target_id);
+ seq_puts(m, "\tUser: ");
+ ahc_format_transinfo(m, &tinfo->user);
starget = ahc->platform_data->starget[target_offset];
if (!starget)
return;
- copy_info(info, "\tGoal: ");
- ahc_format_transinfo(info, &tinfo->goal);
- copy_info(info, "\tCurr: ");
- ahc_format_transinfo(info, &tinfo->curr);
+ seq_puts(m, "\tGoal: ");
+ ahc_format_transinfo(m, &tinfo->goal);
+ seq_puts(m, "\tCurr: ");
+ ahc_format_transinfo(m, &tinfo->curr);
for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
struct scsi_device *sdev;
@@ -211,29 +164,30 @@ ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
if (sdev == NULL)
continue;
- ahc_dump_device_state(info, sdev);
+ ahc_dump_device_state(m, sdev);
}
}
static void
-ahc_dump_device_state(struct info_str *info, struct scsi_device *sdev)
+ahc_dump_device_state(struct seq_file *m, struct scsi_device *sdev)
{
struct ahc_linux_device *dev = scsi_transport_device_data(sdev);
- copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+ seq_printf(m, "\tChannel %c Target %d Lun %d Settings\n",
sdev->sdev_target->channel + 'A',
- sdev->sdev_target->id, sdev->lun);
+ sdev->sdev_target->id, (u8)sdev->lun);
- copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
- copy_info(info, "\t\tCommands Active %d\n", dev->active);
- copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
- copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
- copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+ seq_printf(m, "\t\tCommands Queued %ld\n", dev->commands_issued);
+ seq_printf(m, "\t\tCommands Active %d\n", dev->active);
+ seq_printf(m, "\t\tCommand Openings %d\n", dev->openings);
+ seq_printf(m, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+ seq_printf(m, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
}
-static int
-ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length)
+int
+ahc_proc_write_seeprom(struct Scsi_Host *shost, char *buffer, int length)
{
+ struct ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata;
struct seeprom_descriptor sd;
int have_seeprom;
u_long s;
@@ -280,7 +234,7 @@ ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length)
if ((ahc->chip & AHC_VL) != 0) {
sd.sd_control_offset = SEECTL_2840;
sd.sd_status_offset = STATUS_2840;
- sd.sd_dataout_offset = STATUS_2840;
+ sd.sd_dataout_offset = STATUS_2840;
sd.sd_chip = C46;
sd.sd_MS = 0;
sd.sd_RDY = EEPROM_TF;
@@ -301,7 +255,8 @@ ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length)
u_int start_addr;
if (ahc->seep_config == NULL) {
- ahc->seep_config = kmalloc(sizeof(*ahc->seep_config), GFP_ATOMIC);
+ ahc->seep_config = kmalloc(sizeof(*ahc->seep_config),
+ GFP_ATOMIC);
if (ahc->seep_config == NULL) {
printk("aic7xxx: Unable to allocate serial "
"eeprom buffer. Write failing\n");
@@ -332,53 +287,36 @@ done:
* Return information to handle /proc support for the driver.
*/
int
-ahc_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start,
- off_t offset, int length, int inout)
+ahc_linux_show_info(struct seq_file *m, struct Scsi_Host *shost)
{
struct ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata;
- struct info_str info;
char ahc_info[256];
u_int max_targ;
u_int i;
- int retval;
- /* Has data been written to the file? */
- if (inout == TRUE) {
- retval = ahc_proc_write_seeprom(ahc, buffer, length);
- goto done;
- }
-
- if (start)
- *start = buffer;
-
- info.buffer = buffer;
- info.length = length;
- info.offset = offset;
- info.pos = 0;
-
- copy_info(&info, "Adaptec AIC7xxx driver version: %s\n",
+ seq_printf(m, "Adaptec AIC7xxx driver version: %s\n",
AIC7XXX_DRIVER_VERSION);
- copy_info(&info, "%s\n", ahc->description);
+ seq_printf(m, "%s\n", ahc->description);
ahc_controller_info(ahc, ahc_info);
- copy_info(&info, "%s\n", ahc_info);
- copy_info(&info, "Allocated SCBs: %d, SG List Length: %d\n\n",
+ seq_printf(m, "%s\n", ahc_info);
+ seq_printf(m, "Allocated SCBs: %d, SG List Length: %d\n\n",
ahc->scb_data->numscbs, AHC_NSEG);
if (ahc->seep_config == NULL)
- copy_info(&info, "No Serial EEPROM\n");
+ seq_puts(m, "No Serial EEPROM\n");
else {
- copy_info(&info, "Serial EEPROM:\n");
+ seq_puts(m, "Serial EEPROM:\n");
for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) {
if (((i % 8) == 0) && (i != 0)) {
- copy_info(&info, "\n");
+ seq_putc(m, '\n');
}
- copy_info(&info, "0x%.4x ",
+ seq_printf(m, "0x%.4x ",
((uint16_t*)ahc->seep_config)[i]);
}
- copy_info(&info, "\n");
+ seq_putc(m, '\n');
}
- copy_info(&info, "\n");
+ seq_putc(m, '\n');
max_targ = 16;
if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
@@ -398,10 +336,8 @@ ahc_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start,
target_id = i % 8;
}
- ahc_dump_target_state(ahc, &info, our_id,
+ ahc_dump_target_state(ahc, m, our_id,
channel, target_id, i);
}
- retval = info.pos > info.offset ? info.pos - info.offset : 0;
-done:
- return (retval);
+ return 0;
}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
index e821082a4f47..473039df0ed5 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
+++ b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
@@ -244,8 +244,6 @@ ahc_reg_print_t ahc_scb_tag_print;
#define SCSIDATH 0x07
-#define STCNT 0x08
-
#define OPTIONMODE 0x08
#define OPTIONMODE_DEFAULTS 0x03
#define AUTORATEEN 0x80
@@ -257,6 +255,8 @@ ahc_reg_print_t ahc_scb_tag_print;
#define AUTO_MSGOUT_DE 0x02
#define DIS_MSGIN_DUALEDGE 0x01
+#define STCNT 0x08
+
#define TARGCRCCNT 0x0a
#define CLRSINT0 0x0b
@@ -365,8 +365,6 @@ ahc_reg_print_t ahc_scb_tag_print;
#define ALTSTIM 0x20
#define DFLTTID 0x10
-#define TARGID 0x1b
-
#define SPIOCAP 0x1b
#define SOFT1 0x80
#define SOFT0 0x40
@@ -377,12 +375,14 @@ ahc_reg_print_t ahc_scb_tag_print;
#define ROM 0x02
#define SSPIOCPS 0x01
+#define TARGID 0x1b
+
#define BRDCTL 0x1d
#define BRDDAT7 0x80
#define BRDDAT6 0x40
#define BRDDAT5 0x20
-#define BRDDAT4 0x10
#define BRDSTB 0x10
+#define BRDDAT4 0x10
#define BRDDAT3 0x08
#define BRDCS 0x08
#define BRDDAT2 0x04
@@ -406,8 +406,8 @@ ahc_reg_print_t ahc_scb_tag_print;
#define DIAGLEDEN 0x80
#define DIAGLEDON 0x40
#define AUTOFLUSHDIS 0x20
-#define ENAB40 0x08
#define SELBUSB 0x08
+#define ENAB40 0x08
#define ENAB20 0x04
#define SELWIDE 0x02
#define XCVR 0x01
@@ -730,8 +730,8 @@ ahc_reg_print_t ahc_scb_tag_print;
#define SCB_BASE 0xa0
#define SCB_CDB_PTR 0xa0
-#define SCB_RESIDUAL_DATACNT 0xa0
#define SCB_CDB_STORE 0xa0
+#define SCB_RESIDUAL_DATACNT 0xa0
#define SCB_RESIDUAL_SGPTR 0xa4
@@ -756,8 +756,8 @@ ahc_reg_print_t ahc_scb_tag_print;
#define SCB_CONTROL 0xb8
#define SCB_TAG_TYPE 0x03
-#define STATUS_RCVD 0x80
#define TARGET_SCB 0x80
+#define STATUS_RCVD 0x80
#define DISCENB 0x40
#define TAG_ENB 0x20
#define MK_MESSAGE 0x10
@@ -872,40 +872,40 @@ ahc_reg_print_t ahc_scb_tag_print;
#define SG_CACHE_PRE 0xfc
+#define TARGET_CMD_CMPLT 0xfe
#define MAX_OFFSET_ULTRA2 0x7f
#define MAX_OFFSET_16BIT 0x08
#define BUS_8_BIT 0x00
-#define TARGET_CMD_CMPLT 0xfe
+#define TID_SHIFT 0x04
#define STATUS_QUEUE_FULL 0x28
#define STATUS_BUSY 0x08
-#define MAX_OFFSET_8BIT 0x0f
-#define BUS_32_BIT 0x02
-#define CCSGADDR_MAX 0x80
-#define TID_SHIFT 0x04
#define SCB_DOWNLOAD_SIZE_64 0x30
+#define MAX_OFFSET_8BIT 0x0f
#define HOST_MAILBOX_SHIFT 0x04
-#define CMD_GROUP_CODE_SHIFT 0x05
-#define CCSGRAM_MAXSEGS 0x10
-#define SCB_LIST_NULL 0xff
+#define CCSGADDR_MAX 0x80
+#define BUS_32_BIT 0x02
#define SG_SIZEOF 0x08
-#define SCB_DOWNLOAD_SIZE 0x20
#define SEQ_MAILBOX_SHIFT 0x00
+#define SCB_LIST_NULL 0xff
+#define SCB_DOWNLOAD_SIZE 0x20
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define CCSGRAM_MAXSEGS 0x10
#define TARGET_DATA_IN 0x01
-#define HOST_MSG 0xff
+#define STACK_SIZE 0x04
+#define SCB_UPLOAD_SIZE 0x20
#define MAX_OFFSET 0x7f
+#define HOST_MSG 0xff
#define BUS_16_BIT 0x01
-#define SCB_UPLOAD_SIZE 0x20
-#define STACK_SIZE 0x04
/* Downloaded Constant Definitions */
#define INVERTED_CACHESIZE_MASK 0x03
-#define SG_PREFETCH_ADDR_MASK 0x06
#define SG_PREFETCH_ALIGN_MASK 0x05
+#define SG_PREFETCH_ADDR_MASK 0x06
#define QOUTFIFO_OFFSET 0x00
#define SG_PREFETCH_CNT 0x04
-#define CACHESIZE_MASK 0x02
#define QINFIFO_OFFSET 0x01
+#define CACHESIZE_MASK 0x02
#define DOWNLOAD_CONST_COUNT 0x07
diff --git a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
index 07e93fbae706..f37362bc8ece 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
+++ b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
@@ -1304,5 +1304,4 @@ static const struct cs {
{ 875, 877 }
};
-static const int num_critical_sections = sizeof(critical_sections)
- / sizeof(*critical_sections);
+#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)
diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile
index b98c5c1056c3..a3f2357a3f08 100644
--- a/drivers/scsi/aic7xxx/aicasm/Makefile
+++ b/drivers/scsi/aic7xxx/aicasm/Makefile
@@ -1,19 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
PROG= aicasm
+OUTDIR ?= ./
+
.SUFFIXES= .l .y .c .h
CSRCS= aicasm.c aicasm_symbol.c
YSRCS= aicasm_gram.y aicasm_macro_gram.y
LSRCS= aicasm_scan.l aicasm_macro_scan.l
-GENHDRS= aicdb.h $(YSRCS:.y=.h)
-GENSRCS= $(YSRCS:.y=.c) $(LSRCS:.l=.c)
+GENHDRS= $(addprefix ${OUTDIR}/,aicdb.h $(YSRCS:.y=.h))
+GENSRCS= $(addprefix ${OUTDIR}/,$(YSRCS:.y=.c) $(LSRCS:.l=.c))
SRCS= ${CSRCS} ${GENSRCS}
LIBS= -ldb
clean-files:= ${GENSRCS} ${GENHDRS} $(YSRCS:.y=.output) $(PROG)
# Override default kernel CFLAGS. This is a userland app.
-AICASM_CFLAGS:= -I/usr/include -I.
+AICASM_CFLAGS:= -I/usr/include -I. -I$(OUTDIR)
LEX= flex
YACC= bison
YFLAGS= -d
@@ -32,22 +35,25 @@ YFLAGS+= -t -v
LFLAGS= -d
endif
-$(PROG): ${GENHDRS} $(SRCS)
- $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG) $(LIBS)
+$(PROG): $(OUTDIR) ${GENHDRS} $(SRCS)
+ $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(OUTDIR)/$(PROG) $(LIBS)
+
+$(OUTDIR):
+ mkdir -p $(OUTDIR)
-aicdb.h:
+$(OUTDIR)/aicdb.h:
@if [ -e "/usr/include/db4/db_185.h" ]; then \
- echo "#include <db4/db_185.h>" > aicdb.h; \
+ echo "#include <db4/db_185.h>" > $@; \
elif [ -e "/usr/include/db3/db_185.h" ]; then \
- echo "#include <db3/db_185.h>" > aicdb.h; \
+ echo "#include <db3/db_185.h>" > $@; \
elif [ -e "/usr/include/db2/db_185.h" ]; then \
- echo "#include <db2/db_185.h>" > aicdb.h; \
+ echo "#include <db2/db_185.h>" > $@; \
elif [ -e "/usr/include/db1/db_185.h" ]; then \
- echo "#include <db1/db_185.h>" > aicdb.h; \
+ echo "#include <db1/db_185.h>" > $@; \
elif [ -e "/usr/include/db/db_185.h" ]; then \
- echo "#include <db/db_185.h>" > aicdb.h; \
+ echo "#include <db/db_185.h>" > $@; \
elif [ -e "/usr/include/db_185.h" ]; then \
- echo "#include <db_185.h>" > aicdb.h; \
+ echo "#include <db_185.h>" > $@; \
else \
echo "*** Install db development libraries"; \
fi
@@ -55,26 +61,14 @@ aicdb.h:
clean:
rm -f $(clean-files)
-# Create a dependency chain in generated files
-# to avoid concurrent invocations of the single
-# rule that builds them all.
-aicasm_gram.c: aicasm_gram.h
-aicasm_gram.c aicasm_gram.h: aicasm_gram.y
- $(YACC) $(YFLAGS) -b $(<:.y=) $<
- mv $(<:.y=).tab.c $(<:.y=.c)
- mv $(<:.y=).tab.h $(<:.y=.h)
-
-# Create a dependency chain in generated files
-# to avoid concurrent invocations of the single
-# rule that builds them all.
-aicasm_macro_gram.c: aicasm_macro_gram.h
-aicasm_macro_gram.c aicasm_macro_gram.h: aicasm_macro_gram.y
- $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $<
- mv $(<:.y=).tab.c $(<:.y=.c)
- mv $(<:.y=).tab.h $(<:.y=.h)
-
-aicasm_scan.c: aicasm_scan.l
- $(LEX) $(LFLAGS) -o$@ $<
-
-aicasm_macro_scan.c: aicasm_macro_scan.l
- $(LEX) $(LFLAGS) -Pmm -o$@ $<
+$(OUTDIR)/aicasm_gram.c $(OUTDIR)/aicasm_gram.h: aicasm_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) $< -o $(OUTDIR)/$(<:.y=.c)
+
+$(OUTDIR)/aicasm_macro_gram.c $(OUTDIR)/aicasm_macro_gram.h: aicasm_macro_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $< -o $(OUTDIR)/$(<:.y=.c)
+
+$(OUTDIR)/aicasm_scan.c: aicasm_scan.l
+ $(LEX) $(LFLAGS) -o $@ $<
+
+$(OUTDIR)/aicasm_macro_scan.c: aicasm_macro_scan.l
+ $(LEX) $(LFLAGS) -Pmm -o $@ $<
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.c b/drivers/scsi/aic7xxx/aicasm/aicasm.c
index e4a778720301..cd692a4c5f85 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm.c
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.c
@@ -1,5 +1,5 @@
/*
- * Aic7xxx SCSI host adapter firmware asssembler
+ * Aic7xxx SCSI host adapter firmware assembler
*
* Copyright (c) 1997, 1998, 2000, 2001 Justin T. Gibbs.
* Copyright (c) 2001, 2002 Adaptec Inc.
@@ -254,7 +254,7 @@ main(int argc, char *argv[])
argv += optind;
if (argc != 1) {
- fprintf(stderr, "%s: No input file specifiled\n", appname);
+ fprintf(stderr, "%s: No input file specified\n", appname);
usage();
/* NOTREACHED */
}
@@ -283,7 +283,7 @@ main(int argc, char *argv[])
/*
* Decend the tree of scopes and insert/emit
* patches as appropriate. We perform a depth first
- * tranversal, recursively handling each scope.
+ * traversal, recursively handling each scope.
*/
/* start at the root scope */
dump_scope(SLIST_FIRST(&scope_stack));
@@ -451,8 +451,7 @@ output_code()
fprintf(ofile, "\n};\n\n");
fprintf(ofile,
-"static const int num_critical_sections = sizeof(critical_sections)\n"
-" / sizeof(*critical_sections);\n");
+ "#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)\n");
fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.h b/drivers/scsi/aic7xxx/aicasm/aicasm.h
index 51678dd46ff7..716a2aefc925 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm.h
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.h
@@ -42,11 +42,7 @@
* $FreeBSD$
*/
-#ifdef __linux__
#include "../queue.h"
-#else
-#include <sys/queue.h>
-#endif
#ifndef TRUE
#define TRUE 1
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
index f1586a437906..b1c9ce477cbd 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
@@ -52,17 +52,12 @@
#include <string.h>
#include <sysexits.h>
-#ifdef __linux__
#include "../queue.h"
-#else
-#include <sys/queue.h>
-#endif
#include "aicasm.h"
#include "aicasm_symbol.h"
#include "aicasm_insformat.h"
-int yylineno;
char *yyfilename;
char stock_prefix[] = "aic_";
char *prefix = stock_prefix;
@@ -107,6 +102,7 @@ static void add_conditional(symbol_t *symbol);
static void add_version(const char *verstring);
static int is_download_const(expression_t *immed);
static int is_location_address(symbol_t *symbol);
+int yylex();
void yyerror(const char *string);
#define SRAM_SYMNAME "SRAM_BASE"
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
index 9df9e2ce3538..8373447bd7d3 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
@@ -209,7 +209,6 @@ struct instruction {
#define AIC_OP_JC16 0x9105
#define AIC_OP_JNC16 0x9205
#define AIC_OP_CALL16 0x9305
-#define AIC_OP_CALL16 0x9305
/* Page extension is low three bits of second opcode byte. */
#define AIC_OP_JMPF 0xA005
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
index 708326df0766..5c7350eb5b5c 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
@@ -52,11 +52,7 @@
#include <string.h>
#include <sysexits.h>
-#ifdef __linux__
#include "../queue.h"
-#else
-#include <sys/queue.h>
-#endif
#include "aicasm.h"
#include "aicasm_symbol.h"
@@ -65,6 +61,7 @@
static symbol_t *macro_symbol;
static void add_macro_arg(const char *argtext, int position);
+int mmlex();
void mmerror(const char *string);
%}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
index c0457b8c3b77..98e9959c6907 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
@@ -51,11 +51,7 @@
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
-#ifdef __linux__
#include "../queue.h"
-#else
-#include <sys/queue.h>
-#endif
#include "aicasm.h"
#include "aicasm_symbol.h"
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
index 93c8667cd704..fc7e6c58148d 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
@@ -51,11 +51,7 @@
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
-#ifdef __linux__
#include "../queue.h"
-#else
-#include <sys/queue.h>
-#endif
#include "aicasm.h"
#include "aicasm_symbol.h"
@@ -68,6 +64,9 @@ static char *string_buf_ptr;
static int parren_count;
static int quote_count;
static char buf[255];
+void mm_switch_to_buffer(YY_BUFFER_STATE);
+void mmparse();
+void mm_delete_buffer(YY_BUFFER_STATE);
%}
PATH ([/]*[-A-Za-z0-9_.])+
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
index 232aff1fe784..2b44eb5702eb 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
@@ -44,11 +44,7 @@
#include <sys/types.h>
-#ifdef __linux__
#include "aicdb.h"
-#else
-#include <db.h>
-#endif
#include <fcntl.h>
#include <inttypes.h>
#include <regex.h>
@@ -56,6 +52,7 @@
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
+#include <ctype.h>
#include "aicasm_symbol.h"
#include "aicasm.h"
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
index 34bbcad7f83f..ed3bdd43c297 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
@@ -42,11 +42,7 @@
* $FreeBSD$
*/
-#ifdef __linux__
#include "../queue.h"
-#else
-#include <sys/queue.h>
-#endif
typedef enum {
UNINITIALIZED,
@@ -112,7 +108,7 @@ struct macro_arg {
regex_t arg_regex;
char *replacement_text;
};
-STAILQ_HEAD(macro_arg_list, macro_arg) args;
+STAILQ_HEAD(macro_arg_list, macro_arg);
struct macro_info {
struct macro_arg_list args;
diff --git a/drivers/scsi/aic7xxx/aiclib.c b/drivers/scsi/aic7xxx/aiclib.c
deleted file mode 100644
index 828ae3d9a510..000000000000
--- a/drivers/scsi/aic7xxx/aiclib.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Implementation of Utility functions for all SCSI device types.
- *
- * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs.
- * Copyright (c) 1997, 1998 Kenneth D. Merry.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification, immediately at the beginning of the file.
- * 2. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/cam/scsi/scsi_all.c,v 1.38 2002/09/23 04:56:35 mjacob Exp $
- * $Id$
- */
-
-#include "aiclib.h"
-
diff --git a/drivers/scsi/aic7xxx/aiclib.h b/drivers/scsi/aic7xxx/aiclib.h
index f8fd198aafbc..ba08eb3c4e3b 100644
--- a/drivers/scsi/aic7xxx/aiclib.h
+++ b/drivers/scsi/aic7xxx/aiclib.h
@@ -117,21 +117,6 @@ struct scsi_sense_data
#define SSD_FULL_SIZE sizeof(struct scsi_sense_data)
};
-/*
- * Status Byte
- */
-#define SCSI_STATUS_OK 0x00
-#define SCSI_STATUS_CHECK_COND 0x02
-#define SCSI_STATUS_COND_MET 0x04
-#define SCSI_STATUS_BUSY 0x08
-#define SCSI_STATUS_INTERMED 0x10
-#define SCSI_STATUS_INTERMED_COND_MET 0x14
-#define SCSI_STATUS_RESERV_CONFLICT 0x18
-#define SCSI_STATUS_CMD_TERMINATED 0x22 /* Obsolete in SAM-2 */
-#define SCSI_STATUS_QUEUE_FULL 0x28
-#define SCSI_STATUS_ACA_ACTIVE 0x30
-#define SCSI_STATUS_TASK_ABORTED 0x40
-
/************************* Large Disk Handling ********************************/
static inline int
aic_sector_div(sector_t capacity, int heads, int sectors)
diff --git a/drivers/scsi/aic7xxx/queue.h b/drivers/scsi/aic7xxx/queue.h
index 8adf8003a164..ba602981f193 100644
--- a/drivers/scsi/aic7xxx/queue.h
+++ b/drivers/scsi/aic7xxx/queue.h
@@ -246,7 +246,7 @@ struct { \
/*
* List declarations.
*/
-#define LIST_HEAD(name, type) \
+#define BSD_LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
diff --git a/drivers/scsi/aic7xxx/scsi_message.h b/drivers/scsi/aic7xxx/scsi_message.h
index 75811e245ec7..53343a6d8ae1 100644
--- a/drivers/scsi/aic7xxx/scsi_message.h
+++ b/drivers/scsi/aic7xxx/scsi_message.h
@@ -4,42 +4,15 @@
*/
/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
-#define MSG_CMDCOMPLETE 0x00 /* M/M */
-#define MSG_TASK_COMPLETE 0x00 /* M/M */ /* SPI3 Terminology */
-#define MSG_EXTENDED 0x01 /* O/O */
#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
#define MSG_RESTOREPOINTERS 0x03 /* O/O */
#define MSG_DISCONNECT 0x04 /* O/O */
-#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
-#define MSG_ABORT 0x06 /* O/M */
-#define MSG_ABORT_TASK_SET 0x06 /* O/M */ /* SPI3 Terminology */
#define MSG_MESSAGE_REJECT 0x07 /* M/M */
#define MSG_NOOP 0x08 /* M/M */
-#define MSG_PARITY_ERROR 0x09 /* M/M */
-#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
-#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
-#define MSG_BUS_DEV_RESET 0x0c /* O/M */
-#define MSG_TARGET_RESET 0x0c /* O/M */ /* SPI3 Terminology */
-#define MSG_ABORT_TAG 0x0d /* O/O */
-#define MSG_ABORT_TASK 0x0d /* O/O */ /* SPI3 Terminology */
-#define MSG_CLEAR_QUEUE 0x0e /* O/O */
-#define MSG_CLEAR_TASK_SET 0x0e /* O/O */ /* SPI3 Terminology */
-#define MSG_INIT_RECOVERY 0x0f /* O/O */ /* Deprecated in SPI3 */
-#define MSG_REL_RECOVERY 0x10 /* O/O */ /* Deprecated in SPI3 */
-#define MSG_TERM_IO_PROC 0x11 /* O/O */ /* Deprecated in SPI3 */
-#define MSG_CLEAR_ACA 0x16 /* O/O */ /* SPI3 */
-#define MSG_LOGICAL_UNIT_RESET 0x17 /* O/O */ /* SPI3 */
-#define MSG_QAS_REQUEST 0x55 /* O/O */ /* SPI3 */
/* Messages (2 byte) */
#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
-#define MSG_SIMPLE_TASK 0x20 /* O/O */ /* SPI3 Terminology */
-#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
-#define MSG_HEAD_OF_QUEUE_TASK 0x21 /* O/O */ /* SPI3 Terminology */
-#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
-#define MSG_ORDERED_TASK 0x22 /* O/O */ /* SPI3 Terminology */
#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
-#define MSG_ACA_TASK 0x24 /* 0/0 */ /* SPI3 */
/* Identify message */ /* M/M */
#define MSG_IDENTIFYFLAG 0x80
@@ -49,16 +22,13 @@
#define MSG_IDENTIFY_LUNMASK 0x3F
/* Extended messages (opcode and length) */
-#define MSG_EXT_SDTR 0x01
#define MSG_EXT_SDTR_LEN 0x03
-#define MSG_EXT_WDTR 0x03
#define MSG_EXT_WDTR_LEN 0x02
#define MSG_EXT_WDTR_BUS_8_BIT 0x00
#define MSG_EXT_WDTR_BUS_16_BIT 0x01
#define MSG_EXT_WDTR_BUS_32_BIT 0x02 /* Deprecated in SPI3 */
-#define MSG_EXT_PPR 0x04 /* SPI3 */
#define MSG_EXT_PPR_LEN 0x06
#define MSG_EXT_PPR_PCOMP_EN 0x80
#define MSG_EXT_PPR_RTI 0x40
diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c
deleted file mode 100644
index 5b212f0df898..000000000000
--- a/drivers/scsi/aic7xxx_old.c
+++ /dev/null
@@ -1,11149 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux.
- *
- * Copyright (c) 1994 John Aycock
- * The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
- * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
- * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
- * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
- * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
- * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
- * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
- * ANSI SCSI-2 specification (draft 10c), ...
- *
- * --------------------------------------------------------------------------
- *
- * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
- *
- * Substantially modified to include support for wide and twin bus
- * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
- * SCB paging, and other rework of the code.
- *
- * Parts of this driver were also based on the FreeBSD driver by
- * Justin T. Gibbs. His copyright follows:
- *
- * --------------------------------------------------------------------------
- * Copyright (c) 1994-1997 Justin Gibbs.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification, immediately at the beginning of the file.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * Where this Software is combined with software released under the terms of
- * the GNU General Public License ("GPL") and the terms of the GPL would require the
- * combined work to also be released under the terms of the GPL, the terms
- * and conditions of this License will apply in addition to those of the
- * GPL with the exception of any terms or conditions of this License that
- * conflict with, or are expressly prohibited by, the GPL.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
- *---------------------------------------------------------------------------
- *
- * Thanks also go to (in alphabetical order) the following:
- *
- * Rory Bolt - Sequencer bug fixes
- * Jay Estabrook - Initial DEC Alpha support
- * Doug Ledford - Much needed abort/reset bug fixes
- * Kai Makisara - DMAing of SCBs
- *
- * A Boot time option was also added for not resetting the scsi bus.
- *
- * Form: aic7xxx=extended
- * aic7xxx=no_reset
- * aic7xxx=ultra
- * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level
- * aic7xxx=verbose
- *
- * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
- *
- * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
- *-M*************************************************************************/
-
-/*+M**************************************************************************
- *
- * Further driver modifications made by Doug Ledford <dledford@redhat.com>
- *
- * Copyright (c) 1997-1999 Doug Ledford
- *
- * These changes are released under the same licensing terms as the FreeBSD
- * driver written by Justin Gibbs. Please see his Copyright notice above
- * for the exact terms and conditions covering my changes as well as the
- * warranty statement.
- *
- * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
- * but are not limited to:
- *
- * 1: Import of the latest FreeBSD sequencer code for this driver
- * 2: Modification of kernel code to accommodate different sequencer semantics
- * 3: Extensive changes throughout kernel portion of driver to improve
- * abort/reset processing and error hanndling
- * 4: Other work contributed by various people on the Internet
- * 5: Changes to printk information and verbosity selection code
- * 6: General reliability related changes, especially in IRQ management
- * 7: Modifications to the default probe/attach order for supported cards
- * 8: SMP friendliness has been improved
- *
- * Overall, this driver represents a significant departure from the official
- * aic7xxx driver released by Dan Eischen in two ways. First, in the code
- * itself. A diff between the two version of the driver is now a several
- * thousand line diff. Second, in approach to solving the same problem. The
- * problem is importing the FreeBSD aic7xxx driver code to linux can be a
- * difficult and time consuming process, that also can be error prone. Dan
- * Eischen's official driver uses the approach that the linux and FreeBSD
- * drivers should be as identical as possible. To that end, his next version
- * of this driver will be using a mid-layer code library that he is developing
- * to moderate communications between the linux mid-level SCSI code and the
- * low level FreeBSD driver. He intends to be able to essentially drop the
- * FreeBSD driver into the linux kernel with only a few minor tweaks to some
- * include files and the like and get things working, making for fast easy
- * imports of the FreeBSD code into linux.
- *
- * I disagree with Dan's approach. Not that I don't think his way of doing
- * things would be nice, easy to maintain, and create a more uniform driver
- * between FreeBSD and Linux. I have no objection to those issues. My
- * disagreement is on the needed functionality. There simply are certain
- * things that are done differently in FreeBSD than linux that will cause
- * problems for this driver regardless of any middle ware Dan implements.
- * The biggest example of this at the moment is interrupt semantics. Linux
- * doesn't provide the same protection techniques as FreeBSD does, nor can
- * they be easily implemented in any middle ware code since they would truly
- * belong in the kernel proper and would effect all drivers. For the time
- * being, I see issues such as these as major stumbling blocks to the
- * reliability of code based upon such middle ware. Therefore, I choose to
- * use a different approach to importing the FreeBSD code that doesn't
- * involve any middle ware type code. My approach is to import the sequencer
- * code from FreeBSD wholesale. Then, to only make changes in the kernel
- * portion of the driver as they are needed for the new sequencer semantics.
- * In this way, the portion of the driver that speaks to the rest of the
- * linux kernel is fairly static and can be changed/modified to solve
- * any problems one might encounter without concern for the FreeBSD driver.
- *
- * Note: If time and experience should prove me wrong that the middle ware
- * code Dan writes is reliable in its operation, then I'll retract my above
- * statements. But, for those that don't know, I'm from Missouri (in the US)
- * and our state motto is "The Show-Me State". Well, before I will put
- * faith into it, you'll have to show me that it works :)
- *
- *_M*************************************************************************/
-
-/*
- * The next three defines are user configurable. These should be the only
- * defines a user might need to get in here and change. There are other
- * defines buried deeper in the code, but those really shouldn't need touched
- * under normal conditions.
- */
-
-/*
- * AIC7XXX_STRICT_PCI_SETUP
- * Should we assume the PCI config options on our controllers are set with
- * sane and proper values, or should we be anal about our PCI config
- * registers and force them to what we want? The main advantage to
- * defining this option is on non-Intel hardware where the BIOS may not
- * have been run to set things up, or if you have one of the BIOSless
- * Adaptec controllers, such as a 2910, that don't get set up by the
- * BIOS. However, keep in mind that we really do set the most important
- * items in the driver regardless of this setting, this only controls some
- * of the more esoteric PCI options on these cards. In that sense, I
- * would default to leaving this off. However, if people wish to try
- * things both ways, that would also help me to know if there are some
- * machines where it works one way but not another.
- *
- * -- July 7, 17:09
- * OK...I need this on my machine for testing, so the default is to
- * leave it defined.
- *
- * -- July 7, 18:49
- * I needed it for testing, but it didn't make any difference, so back
- * off she goes.
- *
- * -- July 16, 23:04
- * I turned it back on to try and compensate for the 2.1.x PCI code
- * which no longer relies solely on the BIOS and now tries to set
- * things itself.
- */
-
-#define AIC7XXX_STRICT_PCI_SETUP
-
-/*
- * AIC7XXX_VERBOSE_DEBUGGING
- * This option enables a lot of extra printk();s in the code, surrounded
- * by if (aic7xxx_verbose ...) statements. Executing all of those if
- * statements and the extra checks can get to where it actually does have
- * an impact on CPU usage and such, as well as code size. Disabling this
- * define will keep some of those from becoming part of the code.
- *
- * NOTE: Currently, this option has no real effect, I will be adding the
- * various #ifdef's in the code later when I've decided a section is
- * complete and no longer needs debugging. OK...a lot of things are now
- * surrounded by this define, so turning this off does have an impact.
- */
-
-/*
- * #define AIC7XXX_VERBOSE_DEBUGGING
- */
-
-#include <linux/module.h>
-#include <stdarg.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/byteorder.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/blkdev.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/smp.h>
-#include <linux/interrupt.h>
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "aic7xxx_old/aic7xxx.h"
-
-#include "aic7xxx_old/sequencer.h"
-#include "aic7xxx_old/scsi_message.h"
-#include "aic7xxx_old/aic7xxx_reg.h"
-#include <scsi/scsicam.h>
-
-#include <linux/stat.h>
-#include <linux/slab.h> /* for kmalloc() */
-
-#define AIC7XXX_C_VERSION "5.2.6"
-
-#define ALL_TARGETS -1
-#define ALL_CHANNELS -1
-#define ALL_LUNS -1
-#define MAX_TARGETS 16
-#define MAX_LUNS 8
-#ifndef TRUE
-# define TRUE 1
-#endif
-#ifndef FALSE
-# define FALSE 0
-#endif
-
-#if defined(__powerpc__) || defined(__i386__) || defined(__x86_64__)
-# define MMAPIO
-#endif
-
-/*
- * You can try raising me for better performance or lowering me if you have
- * flaky devices that go off the scsi bus when hit with too many tagged
- * commands (like some IBM SCSI-3 LVD drives).
- */
-#define AIC7XXX_CMDS_PER_DEVICE 32
-
-typedef struct
-{
- unsigned char tag_commands[16]; /* Allow for wide/twin adapters. */
-} adapter_tag_info_t;
-
-/*
- * Make a define that will tell the driver not to the default tag depth
- * everywhere.
- */
-#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
- 0, 0, 0, 0, 0, 0, 0, 0}
-
-/*
- * Modify this as you see fit for your system. By setting tag_commands
- * to 0, the driver will use it's own algorithm for determining the
- * number of commands to use (see above). When 255, the driver will
- * not enable tagged queueing for that particular device. When positive
- * (> 0) and (< 255) the values in the array are used for the queue_depth.
- * Note that the maximum value for an entry is 254, but you're insane if
- * you try to use that many commands on one device.
- *
- * In this example, the first line will disable tagged queueing for all
- * the devices on the first probed aic7xxx adapter.
- *
- * The second line enables tagged queueing with 4 commands/LUN for IDs
- * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
- * driver to use its own algorithm for ID 1.
- *
- * The third line is the same as the first line.
- *
- * The fourth line disables tagged queueing for devices 0 and 3. It
- * enables tagged queueing for the other IDs, with 16 commands/LUN
- * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
- * IDs 2, 5-7, and 9-15.
- */
-
-/*
- * NOTE: The below structure is for reference only, the actual structure
- * to modify in order to change things is found after this fake one.
- *
-adapter_tag_info_t aic7xxx_tag_info[] =
-{
- {DEFAULT_TAG_COMMANDS},
- {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255, 4, 4, 4}},
- {DEFAULT_TAG_COMMANDS},
- {{255, 16, 4, 255, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
-};
-*/
-
-static adapter_tag_info_t aic7xxx_tag_info[] =
-{
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS},
- {DEFAULT_TAG_COMMANDS}
-};
-
-
-/*
- * Define an array of board names that can be indexed by aha_type.
- * Don't forget to change this when changing the types!
- */
-static const char *board_names[] = {
- "AIC-7xxx Unknown", /* AIC_NONE */
- "Adaptec AIC-7810 Hardware RAID Controller", /* AIC_7810 */
- "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */
- "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */
- "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */
- "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */
- "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */
- "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */
- "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */
- "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */
- "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */
- "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */
- "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */
- "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */
- "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */
- "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */
- "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */
- "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */
- "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */
- "Adaptec AHA-2940UW Pro Ultra SCSI host adapter", /* AIC_7887 */
- "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */
- "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */
- "Adaptec AHA-293X Ultra2 SCSI host adapter", /* AIC_7890 */
- "Adaptec AHA-294X Ultra2 SCSI host adapter", /* AIC_7890 */
- "Adaptec AIC-7896/7 Ultra2 SCSI host adapter", /* AIC_7896 */
- "Adaptec AHA-394X Ultra2 SCSI host adapter", /* AIC_7897 */
- "Adaptec AHA-395X Ultra2 SCSI host adapter", /* AIC_7897 */
- "Adaptec PCMCIA SCSI controller", /* card bus stuff */
- "Adaptec AIC-7892 Ultra 160/m SCSI host adapter", /* AIC_7892 */
- "Adaptec AIC-7899 Ultra 160/m SCSI host adapter", /* AIC_7899 */
-};
-
-/*
- * There should be a specific return value for this in scsi.h, but
- * it seems that most drivers ignore it.
- */
-#define DID_UNDERFLOW DID_ERROR
-
-/*
- * What we want to do is have the higher level scsi driver requeue
- * the command to us. There is no specific driver status for this
- * condition, but the higher level scsi driver will requeue the
- * command on a DID_BUS_BUSY error.
- *
- * Upon further inspection and testing, it seems that DID_BUS_BUSY
- * will *always* retry the command. We can get into an infinite loop
- * if this happens when we really want some sort of counter that
- * will automatically abort/reset the command after so many retries.
- * Using DID_ERROR will do just that. (Made by a suggestion by
- * Doug Ledford 8/1/96)
- */
-#define DID_RETRY_COMMAND DID_ERROR
-
-#define HSCSIID 0x07
-#define SCSI_RESET 0x040
-
-/*
- * EISA/VL-bus stuff
- */
-#define MINSLOT 1
-#define MAXSLOT 15
-#define SLOTBASE(x) ((x) << 12)
-#define BASE_TO_SLOT(x) ((x) >> 12)
-
-/*
- * Standard EISA Host ID regs (Offset from slot base)
- */
-#define AHC_HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */
-#define AHC_HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */
-#define AHC_HID2 0x82 /* product */
-#define AHC_HID3 0x83 /* firmware revision */
-
-/*
- * AIC-7770 I/O range to reserve for a card
- */
-#define MINREG 0xC00
-#define MAXREG 0xCFF
-
-#define INTDEF 0x5C /* Interrupt Definition Register */
-
-/*
- * AIC-78X0 PCI registers
- */
-#define CLASS_PROGIF_REVID 0x08
-#define DEVREVID 0x000000FFul
-#define PROGINFC 0x0000FF00ul
-#define SUBCLASS 0x00FF0000ul
-#define BASECLASS 0xFF000000ul
-
-#define CSIZE_LATTIME 0x0C
-#define CACHESIZE 0x0000003Ful /* only 5 bits */
-#define LATTIME 0x0000FF00ul
-
-#define DEVCONFIG 0x40
-#define SCBSIZE32 0x00010000ul /* aic789X only */
-#define MPORTMODE 0x00000400ul /* aic7870 only */
-#define RAMPSM 0x00000200ul /* aic7870 only */
-#define RAMPSM_ULTRA2 0x00000004
-#define VOLSENSE 0x00000100ul
-#define SCBRAMSEL 0x00000080ul
-#define SCBRAMSEL_ULTRA2 0x00000008
-#define MRDCEN 0x00000040ul
-#define EXTSCBTIME 0x00000020ul /* aic7870 only */
-#define EXTSCBPEN 0x00000010ul /* aic7870 only */
-#define BERREN 0x00000008ul
-#define DACEN 0x00000004ul
-#define STPWLEVEL 0x00000002ul
-#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
-
-#define SCAMCTL 0x1a /* Ultra2 only */
-#define CCSCBBADDR 0xf0 /* aic7895/6/7 */
-
-/*
- * Define the different types of SEEPROMs on aic7xxx adapters
- * and make it also represent the address size used in accessing
- * its registers. The 93C46 chips have 1024 bits organized into
- * 64 16-bit words, while the 93C56 chips have 2048 bits organized
- * into 128 16-bit words. The C46 chips use 6 bits to address
- * each word, while the C56 and C66 (4096 bits) use 8 bits to
- * address each word.
- */
-typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
-
-/*
- *
- * Define the format of the SEEPROM registers (16 bits).
- *
- */
-struct seeprom_config {
-
-/*
- * SCSI ID Configuration Flags
- */
-#define CFXFER 0x0007 /* synchronous transfer rate */
-#define CFSYNCH 0x0008 /* enable synchronous transfer */
-#define CFDISC 0x0010 /* enable disconnection */
-#define CFWIDEB 0x0020 /* wide bus device (wide card) */
-#define CFSYNCHISULTRA 0x0040 /* CFSYNC is an ultra offset */
-#define CFNEWULTRAFORMAT 0x0080 /* Use the Ultra2 SEEPROM format */
-#define CFSTART 0x0100 /* send start unit SCSI command */
-#define CFINCBIOS 0x0200 /* include in BIOS scan */
-#define CFRNFOUND 0x0400 /* report even if not found */
-#define CFMULTILUN 0x0800 /* probe mult luns in BIOS scan */
-#define CFWBCACHEYES 0x4000 /* Enable W-Behind Cache on drive */
-#define CFWBCACHENC 0xc000 /* Don't change W-Behind Cache */
-/* UNUSED 0x3000 */
- unsigned short device_flags[16]; /* words 0-15 */
-
-/*
- * BIOS Control Bits
- */
-#define CFSUPREM 0x0001 /* support all removable drives */
-#define CFSUPREMB 0x0002 /* support removable drives for boot only */
-#define CFBIOSEN 0x0004 /* BIOS enabled */
-/* UNUSED 0x0008 */
-#define CFSM2DRV 0x0010 /* support more than two drives */
-#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
-/* UNUSED 0x0040 */
-#define CFEXTEND 0x0080 /* extended translation enabled */
-/* UNUSED 0xFF00 */
- unsigned short bios_control; /* word 16 */
-
-/*
- * Host Adapter Control Bits
- */
-#define CFAUTOTERM 0x0001 /* Perform Auto termination */
-#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
-#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
-#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
-#define CFSTERM 0x0004 /* SCSI low byte termination */
-#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
-#define CFSPARITY 0x0010 /* SCSI parity */
-#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
-#define CFRESETB 0x0040 /* reset SCSI bus at boot */
-#define CFBPRIMARY 0x0100 /* Channel B primary on 7895 chipsets */
-#define CFSEAUTOTERM 0x0400 /* aic7890 Perform SE Auto Term */
-#define CFLVDSTERM 0x0800 /* aic7890 LVD Termination */
-/* UNUSED 0xF280 */
- unsigned short adapter_control; /* word 17 */
-
-/*
- * Bus Release, Host Adapter ID
- */
-#define CFSCSIID 0x000F /* host adapter SCSI ID */
-/* UNUSED 0x00F0 */
-#define CFBRTIME 0xFF00 /* bus release time */
- unsigned short brtime_id; /* word 18 */
-
-/*
- * Maximum targets
- */
-#define CFMAXTARG 0x00FF /* maximum targets */
-/* UNUSED 0xFF00 */
- unsigned short max_targets; /* word 19 */
-
- unsigned short res_1[11]; /* words 20-30 */
- unsigned short checksum; /* word 31 */
-};
-
-#define SELBUS_MASK 0x0a
-#define SELNARROW 0x00
-#define SELBUSB 0x08
-#define SINGLE_BUS 0x00
-
-#define SCB_TARGET(scb) \
- (((scb)->hscb->target_channel_lun & TID) >> 4)
-#define SCB_LUN(scb) \
- ((scb)->hscb->target_channel_lun & LID)
-#define SCB_IS_SCSIBUS_B(scb) \
- (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
-
-/*
- * If an error occurs during a data transfer phase, run the command
- * to completion - it's easier that way - making a note of the error
- * condition in this location. This then will modify a DID_OK status
- * into an appropriate error for the higher-level SCSI code.
- */
-#define aic7xxx_error(cmd) ((cmd)->SCp.Status)
-
-/*
- * Keep track of the targets returned status.
- */
-#define aic7xxx_status(cmd) ((cmd)->SCp.sent_command)
-
-/*
- * The position of the SCSI commands scb within the scb array.
- */
-#define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in)
-
-/*
- * The stored DMA mapping for single-buffer data transfers.
- */
-#define aic7xxx_mapping(cmd) ((cmd)->SCp.phase)
-
-/*
- * Get out private data area from a scsi cmd pointer
- */
-#define AIC_DEV(cmd) ((struct aic_dev_data *)(cmd)->device->hostdata)
-
-/*
- * So we can keep track of our host structs
- */
-static struct aic7xxx_host *first_aic7xxx = NULL;
-
-/*
- * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
- * in the scatter-gather lists. We need to convert the virtual
- * addresses to physical addresses.
- */
-struct hw_scatterlist {
- unsigned int address;
- unsigned int length;
-};
-
-/*
- * Maximum number of SG segments these cards can support.
- */
-#define AIC7XXX_MAX_SG 128
-
-/*
- * The maximum number of SCBs we could have for ANY type
- * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
- * SEQUENCER CODE IF THIS IS MODIFIED!
- */
-#define AIC7XXX_MAXSCB 255
-
-
-struct aic7xxx_hwscb {
-/* ------------ Begin hardware supported fields ---------------- */
-/* 0*/ unsigned char control;
-/* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */
-/* 2*/ unsigned char target_status;
-/* 3*/ unsigned char SG_segment_count;
-/* 4*/ unsigned int SG_list_pointer;
-/* 8*/ unsigned char residual_SG_segment_count;
-/* 9*/ unsigned char residual_data_count[3];
-/*12*/ unsigned int data_pointer;
-/*16*/ unsigned int data_count;
-/*20*/ unsigned int SCSI_cmd_pointer;
-/*24*/ unsigned char SCSI_cmd_length;
-/*25*/ unsigned char tag; /* Index into our kernel SCB array.
- * Also used as the tag for tagged I/O
- */
-#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
- * via PIO to initialize a transaction.
- */
-/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
- * or disconnected down in the sequencer.
- */
-/*27*/ unsigned char prev;
-/*28*/ unsigned int pad; /*
- * Unused by the kernel, but we require
- * the padding so that the array of
- * hardware SCBs is aligned on 32 byte
- * boundaries so the sequencer can index
- */
-};
-
-typedef enum {
- SCB_FREE = 0x0000,
- SCB_DTR_SCB = 0x0001,
- SCB_WAITINGQ = 0x0002,
- SCB_ACTIVE = 0x0004,
- SCB_SENSE = 0x0008,
- SCB_ABORT = 0x0010,
- SCB_DEVICE_RESET = 0x0020,
- SCB_RESET = 0x0040,
- SCB_RECOVERY_SCB = 0x0080,
- SCB_MSGOUT_PPR = 0x0100,
- SCB_MSGOUT_SENT = 0x0200,
- SCB_MSGOUT_SDTR = 0x0400,
- SCB_MSGOUT_WDTR = 0x0800,
- SCB_MSGOUT_BITS = SCB_MSGOUT_PPR |
- SCB_MSGOUT_SENT |
- SCB_MSGOUT_SDTR |
- SCB_MSGOUT_WDTR,
- SCB_QUEUED_ABORT = 0x1000,
- SCB_QUEUED_FOR_DONE = 0x2000,
- SCB_WAS_BUSY = 0x4000,
- SCB_QUEUE_FULL = 0x8000
-} scb_flag_type;
-
-typedef enum {
- AHC_FNONE = 0x00000000,
- AHC_PAGESCBS = 0x00000001,
- AHC_CHANNEL_B_PRIMARY = 0x00000002,
- AHC_USEDEFAULTS = 0x00000004,
- AHC_INDIRECT_PAGING = 0x00000008,
- AHC_CHNLB = 0x00000020,
- AHC_CHNLC = 0x00000040,
- AHC_EXTEND_TRANS_A = 0x00000100,
- AHC_EXTEND_TRANS_B = 0x00000200,
- AHC_TERM_ENB_A = 0x00000400,
- AHC_TERM_ENB_SE_LOW = 0x00000400,
- AHC_TERM_ENB_B = 0x00000800,
- AHC_TERM_ENB_SE_HIGH = 0x00000800,
- AHC_HANDLING_REQINITS = 0x00001000,
- AHC_TARGETMODE = 0x00002000,
- AHC_NEWEEPROM_FMT = 0x00004000,
- /*
- * Here ends the FreeBSD defined flags and here begins the linux defined
- * flags. NOTE: I did not preserve the old flag name during this change
- * specifically to force me to evaluate what flags were being used properly
- * and what flags weren't. This way, I could clean up the flag usage on
- * a use by use basis. Doug Ledford
- */
- AHC_MOTHERBOARD = 0x00020000,
- AHC_NO_STPWEN = 0x00040000,
- AHC_RESET_DELAY = 0x00080000,
- AHC_A_SCANNED = 0x00100000,
- AHC_B_SCANNED = 0x00200000,
- AHC_MULTI_CHANNEL = 0x00400000,
- AHC_BIOS_ENABLED = 0x00800000,
- AHC_SEEPROM_FOUND = 0x01000000,
- AHC_TERM_ENB_LVD = 0x02000000,
- AHC_ABORT_PENDING = 0x04000000,
- AHC_RESET_PENDING = 0x08000000,
-#define AHC_IN_ISR_BIT 28
- AHC_IN_ISR = 0x10000000,
- AHC_IN_ABORT = 0x20000000,
- AHC_IN_RESET = 0x40000000,
- AHC_EXTERNAL_SRAM = 0x80000000
-} ahc_flag_type;
-
-typedef enum {
- AHC_NONE = 0x0000,
- AHC_CHIPID_MASK = 0x00ff,
- AHC_AIC7770 = 0x0001,
- AHC_AIC7850 = 0x0002,
- AHC_AIC7860 = 0x0003,
- AHC_AIC7870 = 0x0004,
- AHC_AIC7880 = 0x0005,
- AHC_AIC7890 = 0x0006,
- AHC_AIC7895 = 0x0007,
- AHC_AIC7896 = 0x0008,
- AHC_AIC7892 = 0x0009,
- AHC_AIC7899 = 0x000a,
- AHC_VL = 0x0100,
- AHC_EISA = 0x0200,
- AHC_PCI = 0x0400,
-} ahc_chip;
-
-typedef enum {
- AHC_FENONE = 0x0000,
- AHC_ULTRA = 0x0001,
- AHC_ULTRA2 = 0x0002,
- AHC_WIDE = 0x0004,
- AHC_TWIN = 0x0008,
- AHC_MORE_SRAM = 0x0010,
- AHC_CMD_CHAN = 0x0020,
- AHC_QUEUE_REGS = 0x0040,
- AHC_SG_PRELOAD = 0x0080,
- AHC_SPIOCAP = 0x0100,
- AHC_ULTRA3 = 0x0200,
- AHC_NEW_AUTOTERM = 0x0400,
- AHC_AIC7770_FE = AHC_FENONE,
- AHC_AIC7850_FE = AHC_SPIOCAP,
- AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP,
- AHC_AIC7870_FE = AHC_FENONE,
- AHC_AIC7880_FE = AHC_ULTRA,
- AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|
- AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_NEW_AUTOTERM,
- AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
- AHC_AIC7896_FE = AHC_AIC7890_FE,
- AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_ULTRA3,
- AHC_AIC7899_FE = AHC_AIC7890_FE|AHC_ULTRA3,
-} ahc_feature;
-
-#define SCB_DMA_ADDR(scb, addr) ((unsigned long)(addr) + (scb)->scb_dma->dma_offset)
-
-struct aic7xxx_scb_dma {
- unsigned long dma_offset; /* Correction you have to add
- * to virtual address to get
- * dma handle in this region */
- dma_addr_t dma_address; /* DMA handle of the start,
- * for unmap */
- unsigned int dma_len; /* DMA length */
-};
-
-typedef enum {
- AHC_BUG_NONE = 0x0000,
- AHC_BUG_TMODE_WIDEODD = 0x0001,
- AHC_BUG_AUTOFLUSH = 0x0002,
- AHC_BUG_CACHETHEN = 0x0004,
- AHC_BUG_CACHETHEN_DIS = 0x0008,
- AHC_BUG_PCI_2_1_RETRY = 0x0010,
- AHC_BUG_PCI_MWI = 0x0020,
- AHC_BUG_SCBCHAN_UPLOAD = 0x0040,
-} ahc_bugs;
-
-struct aic7xxx_scb {
- struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */
- struct scsi_cmnd *cmd; /* scsi_cmnd for this scb */
- struct aic7xxx_scb *q_next; /* next scb in queue */
- volatile scb_flag_type flags; /* current state of scb */
- struct hw_scatterlist *sg_list; /* SG list in adapter format */
- unsigned char tag_action;
- unsigned char sg_count;
- unsigned char *sense_cmd; /*
- * Allocate 6 characters for
- * sense command.
- */
- unsigned char *cmnd;
- unsigned int sg_length; /*
- * We init this during
- * buildscb so we don't have
- * to calculate anything during
- * underflow/overflow/stat code
- */
- void *kmalloc_ptr;
- struct aic7xxx_scb_dma *scb_dma;
-};
-
-/*
- * Define a linked list of SCBs.
- */
-typedef struct {
- struct aic7xxx_scb *head;
- struct aic7xxx_scb *tail;
-} scb_queue_type;
-
-static struct {
- unsigned char errno;
- const char *errmesg;
-} hard_error[] = {
- { ILLHADDR, "Illegal Host Access" },
- { ILLSADDR, "Illegal Sequencer Address referenced" },
- { ILLOPCODE, "Illegal Opcode in sequencer program" },
- { SQPARERR, "Sequencer Ram Parity Error" },
- { DPARERR, "Data-Path Ram Parity Error" },
- { MPARERR, "Scratch Ram/SCB Array Ram Parity Error" },
- { PCIERRSTAT,"PCI Error detected" },
- { CIOPARERR, "CIOBUS Parity Error" }
-};
-
-static unsigned char
-generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
-
-typedef struct {
- scb_queue_type free_scbs; /*
- * SCBs assigned to free slot on
- * card (no paging required)
- */
- struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB];
- struct aic7xxx_hwscb *hscbs;
- unsigned char numscbs; /* current number of scbs */
- unsigned char maxhscbs; /* hardware scbs */
- unsigned char maxscbs; /* max scbs including pageable scbs */
- dma_addr_t hscbs_dma; /* DMA handle to hscbs */
- unsigned int hscbs_dma_len; /* length of the above DMA area */
- void *hscb_kmalloc_ptr;
-} scb_data_type;
-
-struct target_cmd {
- unsigned char mesg_bytes[4];
- unsigned char command[28];
-};
-
-#define AHC_TRANS_CUR 0x0001
-#define AHC_TRANS_ACTIVE 0x0002
-#define AHC_TRANS_GOAL 0x0004
-#define AHC_TRANS_USER 0x0008
-#define AHC_TRANS_QUITE 0x0010
-typedef struct {
- unsigned char width;
- unsigned char period;
- unsigned char offset;
- unsigned char options;
-} transinfo_type;
-
-struct aic_dev_data {
- volatile scb_queue_type delayed_scbs;
- volatile unsigned short temp_q_depth;
- unsigned short max_q_depth;
- volatile unsigned char active_cmds;
- /*
- * Statistics Kept:
- *
- * Total Xfers (count for each command that has a data xfer),
- * broken down by reads && writes.
- *
- * Further sorted into a few bins for keeping tabs on how many commands
- * we get of various sizes.
- *
- */
- long w_total; /* total writes */
- long r_total; /* total reads */
- long barrier_total; /* total num of REQ_BARRIER commands */
- long ordered_total; /* How many REQ_BARRIER commands we
- used ordered tags to satisfy */
- long w_bins[6]; /* binned write */
- long r_bins[6]; /* binned reads */
- transinfo_type cur;
- transinfo_type goal;
-#define BUS_DEVICE_RESET_PENDING 0x01
-#define DEVICE_RESET_DELAY 0x02
-#define DEVICE_PRINT_DTR 0x04
-#define DEVICE_WAS_BUSY 0x08
-#define DEVICE_DTR_SCANNED 0x10
-#define DEVICE_SCSI_3 0x20
- volatile unsigned char flags;
- unsigned needppr:1;
- unsigned needppr_copy:1;
- unsigned needsdtr:1;
- unsigned needsdtr_copy:1;
- unsigned needwdtr:1;
- unsigned needwdtr_copy:1;
- unsigned dtr_pending:1;
- struct scsi_device *SDptr;
- struct list_head list;
-};
-
-/*
- * Define a structure used for each host adapter. Note, in order to avoid
- * problems with architectures I can't test on (because I don't have one,
- * such as the Alpha based systems) which happen to give faults for
- * non-aligned memory accesses, care was taken to align this structure
- * in a way that guaranteed all accesses larger than 8 bits were aligned
- * on the appropriate boundary. It's also organized to try and be more
- * cache line efficient. Be careful when changing this lest you might hurt
- * overall performance and bring down the wrath of the masses.
- */
-struct aic7xxx_host {
- /*
- * This is the first 64 bytes in the host struct
- */
-
- /*
- * We are grouping things here....first, items that get either read or
- * written with nearly every interrupt
- */
- volatile long flags;
- ahc_feature features; /* chip features */
- unsigned long base; /* card base address */
- volatile unsigned char __iomem *maddr; /* memory mapped address */
- unsigned long isr_count; /* Interrupt count */
- unsigned long spurious_int;
- scb_data_type *scb_data;
- struct aic7xxx_cmd_queue {
- struct scsi_cmnd *head;
- struct scsi_cmnd *tail;
- } completeq;
-
- /*
- * Things read/written on nearly every entry into aic7xxx_queue()
- */
- volatile scb_queue_type waiting_scbs;
- unsigned char unpause; /* unpause value for HCNTRL */
- unsigned char pause; /* pause value for HCNTRL */
- volatile unsigned char qoutfifonext;
- volatile unsigned char activescbs; /* active scbs */
- volatile unsigned char max_activescbs;
- volatile unsigned char qinfifonext;
- volatile unsigned char *untagged_scbs;
- volatile unsigned char *qoutfifo;
- volatile unsigned char *qinfifo;
-
- unsigned char dev_last_queue_full[MAX_TARGETS];
- unsigned char dev_last_queue_full_count[MAX_TARGETS];
- unsigned short ultraenb; /* Gets downloaded to card as a bitmap */
- unsigned short discenable; /* Gets downloaded to card as a bitmap */
- transinfo_type user[MAX_TARGETS];
-
- unsigned char msg_buf[13]; /* The message for the target */
- unsigned char msg_type;
-#define MSG_TYPE_NONE 0x00
-#define MSG_TYPE_INITIATOR_MSGOUT 0x01
-#define MSG_TYPE_INITIATOR_MSGIN 0x02
- unsigned char msg_len; /* Length of message */
- unsigned char msg_index; /* Index into msg_buf array */
-
-
- /*
- * We put the less frequently used host structure items
- * after the more frequently used items to try and ease
- * the burden on the cache subsystem.
- * These entries are not *commonly* accessed, whereas
- * the preceding entries are accessed very often.
- */
-
- unsigned int irq; /* IRQ for this adapter */
- int instance; /* aic7xxx instance number */
- int scsi_id; /* host adapter SCSI ID */
- int scsi_id_b; /* channel B for twin adapters */
- unsigned int bios_address;
- int board_name_index;
- unsigned short bios_control; /* bios control - SEEPROM */
- unsigned short adapter_control; /* adapter control - SEEPROM */
- struct pci_dev *pdev;
- unsigned char pci_bus;
- unsigned char pci_device_fn;
- struct seeprom_config sc;
- unsigned short sc_type;
- unsigned short sc_size;
- struct aic7xxx_host *next; /* allow for multiple IRQs */
- struct Scsi_Host *host; /* pointer to scsi host */
- struct list_head aic_devs; /* all aic_dev structs on host */
- int host_no; /* SCSI host number */
- unsigned long mbase; /* I/O memory address */
- ahc_chip chip; /* chip type */
- ahc_bugs bugs;
- dma_addr_t fifo_dma; /* DMA handle for fifo arrays */
-};
-
-/*
- * Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns/4 to the proper value to
- * stick in the SCSIRATE reg to use that transfer rate.
- */
-#define AHC_SYNCRATE_ULTRA3 0
-#define AHC_SYNCRATE_ULTRA2 1
-#define AHC_SYNCRATE_ULTRA 3
-#define AHC_SYNCRATE_FAST 6
-#define AHC_SYNCRATE_CRC 0x40
-#define AHC_SYNCRATE_SE 0x10
-static struct aic7xxx_syncrate {
- /* Rates in Ultra mode have bit 8 of sxfr set */
-#define ULTRA_SXFR 0x100
- int sxfr_ultra2;
- int sxfr;
- unsigned char period;
- const char *rate[2];
-} aic7xxx_syncrates[] = {
- { 0x42, 0x000, 9, {"80.0", "160.0"} },
- { 0x13, 0x000, 10, {"40.0", "80.0"} },
- { 0x14, 0x000, 11, {"33.0", "66.6"} },
- { 0x15, 0x100, 12, {"20.0", "40.0"} },
- { 0x16, 0x110, 15, {"16.0", "32.0"} },
- { 0x17, 0x120, 18, {"13.4", "26.8"} },
- { 0x18, 0x000, 25, {"10.0", "20.0"} },
- { 0x19, 0x010, 31, {"8.0", "16.0"} },
- { 0x1a, 0x020, 37, {"6.67", "13.3"} },
- { 0x1b, 0x030, 43, {"5.7", "11.4"} },
- { 0x10, 0x040, 50, {"5.0", "10.0"} },
- { 0x00, 0x050, 56, {"4.4", "8.8" } },
- { 0x00, 0x060, 62, {"4.0", "8.0" } },
- { 0x00, 0x070, 68, {"3.6", "7.2" } },
- { 0x00, 0x000, 0, {NULL, NULL} },
-};
-
-#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1), \
- (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
- ((scb->hscb)->target_channel_lun & 0x07)
-
-#define CTL_OF_CMD(cmd) ((cmd->device->channel) & 0x01), \
- ((cmd->device->id) & 0x0f), \
- ((cmd->device->lun) & 0x07)
-
-#define TARGET_INDEX(cmd) ((cmd)->device->id | ((cmd)->device->channel << 3))
-
-/*
- * A nice little define to make doing our printks a little easier
- */
-
-#define WARN_LEAD KERN_WARNING "(scsi%d:%d:%d:%d) "
-#define INFO_LEAD KERN_INFO "(scsi%d:%d:%d:%d) "
-
-/*
- * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- * cards in the system. This should be fixed. Exceptions to this
- * rule are noted in the comments.
- */
-
-/*
- * Use this as the default queue depth when setting tagged queueing on.
- */
-static unsigned int aic7xxx_default_queue_depth = AIC7XXX_CMDS_PER_DEVICE;
-
-/*
- * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This
- * has no effect on any later resets that might occur due to things like
- * SCSI bus timeouts.
- */
-static unsigned int aic7xxx_no_reset = 0;
-/*
- * Certain PCI motherboards will scan PCI devices from highest to lowest,
- * others scan from lowest to highest, and they tend to do all kinds of
- * strange things when they come into contact with PCI bridge chips. The
- * net result of all this is that the PCI card that is actually used to boot
- * the machine is very hard to detect. Most motherboards go from lowest
- * PCI slot number to highest, and the first SCSI controller found is the
- * one you boot from. The only exceptions to this are when a controller
- * has its BIOS disabled. So, we by default sort all of our SCSI controllers
- * from lowest PCI slot number to highest PCI slot number. We also force
- * all controllers with their BIOS disabled to the end of the list. This
- * works on *almost* all computers. Where it doesn't work, we have this
- * option. Setting this option to non-0 will reverse the order of the sort
- * to highest first, then lowest, but will still leave cards with their BIOS
- * disabled at the very end. That should fix everyone up unless there are
- * really strange cirumstances.
- */
-static int aic7xxx_reverse_scan = 0;
-/*
- * Should we force EXTENDED translation on a controller.
- * 0 == Use whatever is in the SEEPROM or default to off
- * 1 == Use whatever is in the SEEPROM or default to on
- */
-static unsigned int aic7xxx_extended = 0;
-/*
- * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
- * -1 = Use detected settings.
- * 0 = Force Edge triggered mode.
- * 1 = Force Level triggered mode.
- */
-static int aic7xxx_irq_trigger = -1;
-/*
- * This variable is used to override the termination settings on a controller.
- * This should not be used under normal conditions. However, in the case
- * that a controller does not have a readable SEEPROM (so that we can't
- * read the SEEPROM settings directly) and that a controller has a buggered
- * version of the cable detection logic, this can be used to force the
- * correct termination. It is preferable to use the manual termination
- * settings in the BIOS if possible, but some motherboard controllers store
- * those settings in a format we can't read. In other cases, auto term
- * should also work, but the chipset was put together with no auto term
- * logic (common on motherboard controllers). In those cases, we have
- * 32 bits here to work with. That's good for 8 controllers/channels. The
- * bits are organized as 4 bits per channel, with scsi0 getting the lowest
- * 4 bits in the int. A 1 in a bit position indicates the termination setting
- * that corresponds to that bit should be enabled, a 0 is disabled.
- * It looks something like this:
- *
- * 0x0f = 1111-Single Ended Low Byte Termination on/off
- * ||\-Single Ended High Byte Termination on/off
- * |\-LVD Low Byte Termination on/off
- * \-LVD High Byte Termination on/off
- *
- * For non-Ultra2 controllers, the upper 2 bits are not important. So, to
- * enable both high byte and low byte termination on scsi0, I would need to
- * make sure that the override_term variable was set to 0x03 (bits 0011).
- * To make sure that all termination is enabled on an Ultra2 controller at
- * scsi2 and only high byte termination on scsi1 and high and low byte
- * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
- *
- * For the most part, users should never have to use this, that's why I
- * left it fairly cryptic instead of easy to understand. If you need it,
- * most likely someone will be telling you what your's needs to be set to.
- */
-static int aic7xxx_override_term = -1;
-/*
- * Certain motherboard chipset controllers tend to screw
- * up the polarity of the term enable output pin. Use this variable
- * to force the correct polarity for your system. This is a bitfield variable
- * similar to the previous one, but this one has one bit per channel instead
- * of four.
- * 0 = Force the setting to active low.
- * 1 = Force setting to active high.
- * Most Adaptec cards are active high, several motherboards are active low.
- * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
- * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
- * to active high, you would need to set stpwlev=0x9 (bits 1001).
- *
- * People shouldn't need to use this, but if you are experiencing lots of
- * SCSI timeout problems, this may help. There is one sure way to test what
- * this option needs to be. Using a boot floppy to boot the system, configure
- * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
- * if needed then also pass a value to override_term to make sure that the
- * driver is enabling SCSI termination, then set this variable to either 0
- * or 1. When the driver boots, make sure there are *NO* SCSI cables
- * connected to your controller. If it finds and inits the controller
- * without problem, then the setting you passed to stpwlev was correct. If
- * the driver goes into a reset loop and hangs the system, then you need the
- * other setting for this variable. If neither setting lets the machine
- * boot then you have definite termination problems that may not be fixable.
- */
-static int aic7xxx_stpwlev = -1;
-/*
- * Set this to non-0 in order to force the driver to panic the kernel
- * and print out debugging info on a SCSI abort or reset cycle.
- */
-static int aic7xxx_panic_on_abort = 0;
-/*
- * PCI bus parity checking of the Adaptec controllers. This is somewhat
- * dubious at best. To my knowledge, this option has never actually
- * solved a PCI parity problem, but on certain machines with broken PCI
- * chipset configurations, it can generate tons of false error messages.
- * It's included in the driver for completeness.
- * 0 = Shut off PCI parity check
- * -1 = Normal polarity pci parity checking
- * 1 = reverse polarity pci parity checking
- *
- * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this
- * variable to -1 you would actually want to simply pass the variable
- * name without a number. That will invert the 0 which will result in
- * -1.
- */
-static int aic7xxx_pci_parity = 0;
-/*
- * Set this to any non-0 value to cause us to dump the contents of all
- * the card's registers in a hex dump format tailored to each model of
- * controller.
- *
- * NOTE: THE CONTROLLER IS LEFT IN AN UNUSABLE STATE BY THIS OPTION.
- * YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES
- * ONLY
- */
-static int aic7xxx_dump_card = 0;
-/*
- * Set this to a non-0 value to make us dump out the 32 bit instruction
- * registers on the card after completing the sequencer download. This
- * allows the actual sequencer download to be verified. It is possible
- * to use this option and still boot up and run your system. This is
- * only intended for debugging purposes.
- */
-static int aic7xxx_dump_sequencer = 0;
-/*
- * Certain newer motherboards have put new PCI based devices into the
- * IO spaces that used to typically be occupied by VLB or EISA cards.
- * This overlap can cause these newer motherboards to lock up when scanned
- * for older EISA and VLB devices. Setting this option to non-0 will
- * cause the driver to skip scanning for any VLB or EISA controllers and
- * only support the PCI controllers. NOTE: this means that if the kernel
- * os compiled with PCI support disabled, then setting this to non-0
- * would result in never finding any devices :)
- */
-static int aic7xxx_no_probe = 0;
-/*
- * On some machines, enabling the external SCB RAM isn't reliable yet. I
- * haven't had time to make test patches for things like changing the
- * timing mode on that external RAM either. Some of those changes may
- * fix the problem. Until then though, we default to external SCB RAM
- * off and give a command line option to enable it.
- */
-static int aic7xxx_scbram = 0;
-/*
- * So that we can set how long each device is given as a selection timeout.
- * The table of values goes like this:
- * 0 - 256ms
- * 1 - 128ms
- * 2 - 64ms
- * 3 - 32ms
- * We default to 64ms because it's fast. Some old SCSI-I devices need a
- * longer time. The final value has to be left shifted by 3, hence 0x10
- * is the final value.
- */
-static int aic7xxx_seltime = 0x10;
-/*
- * So that insmod can find the variable and make it point to something
- */
-#ifdef MODULE
-static char * aic7xxx = NULL;
-module_param(aic7xxx, charp, 0);
-#endif
-
-#define VERBOSE_NORMAL 0x0000
-#define VERBOSE_NEGOTIATION 0x0001
-#define VERBOSE_SEQINT 0x0002
-#define VERBOSE_SCSIINT 0x0004
-#define VERBOSE_PROBE 0x0008
-#define VERBOSE_PROBE2 0x0010
-#define VERBOSE_NEGOTIATION2 0x0020
-#define VERBOSE_MINOR_ERROR 0x0040
-#define VERBOSE_TRACING 0x0080
-#define VERBOSE_ABORT 0x0f00
-#define VERBOSE_ABORT_MID 0x0100
-#define VERBOSE_ABORT_FIND 0x0200
-#define VERBOSE_ABORT_PROCESS 0x0400
-#define VERBOSE_ABORT_RETURN 0x0800
-#define VERBOSE_RESET 0xf000
-#define VERBOSE_RESET_MID 0x1000
-#define VERBOSE_RESET_FIND 0x2000
-#define VERBOSE_RESET_PROCESS 0x4000
-#define VERBOSE_RESET_RETURN 0x8000
-static int aic7xxx_verbose = VERBOSE_NORMAL | VERBOSE_NEGOTIATION |
- VERBOSE_PROBE; /* verbose messages */
-
-
-/****************************************************************************
- *
- * We're going to start putting in function declarations so that order of
- * functions is no longer important. As needed, they are added here.
- *
- ***************************************************************************/
-
-static int aic7xxx_release(struct Scsi_Host *host);
-static void aic7xxx_set_syncrate(struct aic7xxx_host *p,
- struct aic7xxx_syncrate *syncrate, int target, int channel,
- unsigned int period, unsigned int offset, unsigned char options,
- unsigned int type, struct aic_dev_data *aic_dev);
-static void aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel,
- int lun, unsigned int width, unsigned int type,
- struct aic_dev_data *aic_dev);
-static void aic7xxx_panic_abort(struct aic7xxx_host *p, struct scsi_cmnd *cmd);
-static void aic7xxx_print_card(struct aic7xxx_host *p);
-static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p);
-static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer);
-#endif
-
-/****************************************************************************
- *
- * These functions are now used. They happen to be wrapped in useless
- * inb/outb port read/writes around the real reads and writes because it
- * seems that certain very fast CPUs have a problem dealing with us when
- * going at full speed.
- *
- ***************************************************************************/
-
-static unsigned char
-aic_inb(struct aic7xxx_host *p, long port)
-{
-#ifdef MMAPIO
- unsigned char x;
- if(p->maddr)
- {
- x = readb(p->maddr + port);
- }
- else
- {
- x = inb(p->base + port);
- }
- return(x);
-#else
- return(inb(p->base + port));
-#endif
-}
-
-static void
-aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
-{
-#ifdef MMAPIO
- if(p->maddr)
- {
- writeb(val, p->maddr + port);
- mb(); /* locked operation in order to force CPU ordering */
- readb(p->maddr + HCNTRL); /* dummy read to flush the PCI write */
- }
- else
- {
- outb(val, p->base + port);
- mb(); /* locked operation in order to force CPU ordering */
- }
-#else
- outb(val, p->base + port);
- mb(); /* locked operation in order to force CPU ordering */
-#endif
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_setup
- *
- * Description:
- * Handle Linux boot parameters. This routine allows for assigning a value
- * to a parameter with a ':' between the parameter and the value.
- * ie. aic7xxx=unpause:0x0A,extended
- *-F*************************************************************************/
-static int
-aic7xxx_setup(char *s)
-{
- int i, n;
- char *p;
- char *end;
-
- static struct {
- const char *name;
- unsigned int *flag;
- } options[] = {
- { "extended", &aic7xxx_extended },
- { "no_reset", &aic7xxx_no_reset },
- { "irq_trigger", &aic7xxx_irq_trigger },
- { "verbose", &aic7xxx_verbose },
- { "reverse_scan",&aic7xxx_reverse_scan },
- { "override_term", &aic7xxx_override_term },
- { "stpwlev", &aic7xxx_stpwlev },
- { "no_probe", &aic7xxx_no_probe },
- { "panic_on_abort", &aic7xxx_panic_on_abort },
- { "pci_parity", &aic7xxx_pci_parity },
- { "dump_card", &aic7xxx_dump_card },
- { "dump_sequencer", &aic7xxx_dump_sequencer },
- { "default_queue_depth", &aic7xxx_default_queue_depth },
- { "scbram", &aic7xxx_scbram },
- { "seltime", &aic7xxx_seltime },
- { "tag_info", NULL }
- };
-
- end = strchr(s, '\0');
-
- while ((p = strsep(&s, ",.")) != NULL)
- {
- for (i = 0; i < ARRAY_SIZE(options); i++)
- {
- n = strlen(options[i].name);
- if (!strncmp(options[i].name, p, n))
- {
- if (!strncmp(p, "tag_info", n))
- {
- if (p[n] == ':')
- {
- char *base;
- char *tok, *tok_end, *tok_end2;
- char tok_list[] = { '.', ',', '{', '}', '\0' };
- int i, instance = -1, device = -1;
- unsigned char done = FALSE;
-
- base = p;
- tok = base + n + 1; /* Forward us just past the ':' */
- tok_end = strchr(tok, '\0');
- if (tok_end < end)
- *tok_end = ',';
- while(!done)
- {
- switch(*tok)
- {
- case '{':
- if (instance == -1)
- instance = 0;
- else if (device == -1)
- device = 0;
- tok++;
- break;
- case '}':
- if (device != -1)
- device = -1;
- else if (instance != -1)
- instance = -1;
- tok++;
- break;
- case ',':
- case '.':
- if (instance == -1)
- done = TRUE;
- else if (device >= 0)
- device++;
- else if (instance >= 0)
- instance++;
- if ( (device >= MAX_TARGETS) ||
- (instance >= ARRAY_SIZE(aic7xxx_tag_info)) )
- done = TRUE;
- tok++;
- if (!done)
- {
- base = tok;
- }
- break;
- case '\0':
- done = TRUE;
- break;
- default:
- done = TRUE;
- tok_end = strchr(tok, '\0');
- for(i=0; tok_list[i]; i++)
- {
- tok_end2 = strchr(tok, tok_list[i]);
- if ( (tok_end2) && (tok_end2 < tok_end) )
- {
- tok_end = tok_end2;
- done = FALSE;
- }
- }
- if ( (instance >= 0) && (device >= 0) &&
- (instance < ARRAY_SIZE(aic7xxx_tag_info)) &&
- (device < MAX_TARGETS) )
- aic7xxx_tag_info[instance].tag_commands[device] =
- simple_strtoul(tok, NULL, 0) & 0xff;
- tok = tok_end;
- break;
- }
- }
- while((p != base) && (p != NULL))
- p = strsep(&s, ",.");
- }
- }
- else if (p[n] == ':')
- {
- *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
- if(!strncmp(p, "seltime", n))
- {
- *(options[i].flag) = (*(options[i].flag) % 4) << 3;
- }
- }
- else if (!strncmp(p, "verbose", n))
- {
- *(options[i].flag) = 0xff29;
- }
- else
- {
- *(options[i].flag) = ~(*(options[i].flag));
- if(!strncmp(p, "seltime", n))
- {
- *(options[i].flag) = (*(options[i].flag) % 4) << 3;
- }
- }
- }
- }
- }
- return 1;
-}
-
-__setup("aic7xxx=", aic7xxx_setup);
-
-/*+F*************************************************************************
- * Function:
- * pause_sequencer
- *
- * Description:
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
- *-F*************************************************************************/
-static void
-pause_sequencer(struct aic7xxx_host *p)
-{
- aic_outb(p, p->pause, HCNTRL);
- while ((aic_inb(p, HCNTRL) & PAUSE) == 0)
- {
- ;
- }
- if(p->features & AHC_ULTRA2)
- {
- aic_inb(p, CCSCBCTL);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * unpause_sequencer
- *
- * Description:
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- *-F*************************************************************************/
-static void
-unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
-{
- if (unpause_always ||
- ( !(aic_inb(p, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) &&
- !(p->flags & AHC_HANDLING_REQINITS) ) )
- {
- aic_outb(p, p->unpause, HCNTRL);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * restart_sequencer
- *
- * Description:
- * Restart the sequencer program from address zero. This assumes
- * that the sequencer is already paused.
- *-F*************************************************************************/
-static void
-restart_sequencer(struct aic7xxx_host *p)
-{
- aic_outb(p, 0, SEQADDR0);
- aic_outb(p, 0, SEQADDR1);
- aic_outb(p, FASTMODE, SEQCTL);
-}
-
-/*
- * We include the aic7xxx_seq.c file here so that the other defines have
- * already been made, and so that it comes before the code that actually
- * downloads the instructions (since we don't typically use function
- * prototype, our code has to be ordered that way, it's a left-over from
- * the original driver days.....I should fix it some time DL).
- */
-#include "aic7xxx_old/aic7xxx_seq.c"
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_check_patch
- *
- * Description:
- * See if the next patch to download should be downloaded.
- *-F*************************************************************************/
-static int
-aic7xxx_check_patch(struct aic7xxx_host *p,
- struct sequencer_patch **start_patch, int start_instr, int *skip_addr)
-{
- struct sequencer_patch *cur_patch;
- struct sequencer_patch *last_patch;
- int num_patches;
-
- num_patches = ARRAY_SIZE(sequencer_patches);
- last_patch = &sequencer_patches[num_patches];
- cur_patch = *start_patch;
-
- while ((cur_patch < last_patch) && (start_instr == cur_patch->begin))
- {
- if (cur_patch->patch_func(p) == 0)
- {
- /*
- * Start rejecting code.
- */
- *skip_addr = start_instr + cur_patch->skip_instr;
- cur_patch += cur_patch->skip_patch;
- }
- else
- {
- /*
- * Found an OK patch. Advance the patch pointer to the next patch
- * and wait for our instruction pointer to get here.
- */
- cur_patch++;
- }
- }
-
- *start_patch = cur_patch;
- if (start_instr < *skip_addr)
- /*
- * Still skipping
- */
- return (0);
- return(1);
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_download_instr
- *
- * Description:
- * Find the next patch to download.
- *-F*************************************************************************/
-static void
-aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
- unsigned char *dconsts)
-{
- union ins_formats instr;
- struct ins_format1 *fmt1_ins;
- struct ins_format3 *fmt3_ins;
- unsigned char opcode;
-
- instr = *(union ins_formats*) &seqprog[instrptr * 4];
-
- instr.integer = le32_to_cpu(instr.integer);
-
- fmt1_ins = &instr.format1;
- fmt3_ins = NULL;
-
- /* Pull the opcode */
- opcode = instr.format1.opcode;
- switch (opcode)
- {
- case AIC_OP_JMP:
- case AIC_OP_JC:
- case AIC_OP_JNC:
- case AIC_OP_CALL:
- case AIC_OP_JNE:
- case AIC_OP_JNZ:
- case AIC_OP_JE:
- case AIC_OP_JZ:
- {
- struct sequencer_patch *cur_patch;
- int address_offset;
- unsigned int address;
- int skip_addr;
- int i;
-
- fmt3_ins = &instr.format3;
- address_offset = 0;
- address = fmt3_ins->address;
- cur_patch = sequencer_patches;
- skip_addr = 0;
-
- for (i = 0; i < address;)
- {
- aic7xxx_check_patch(p, &cur_patch, i, &skip_addr);
- if (skip_addr > i)
- {
- int end_addr;
-
- end_addr = min_t(int, address, skip_addr);
- address_offset += end_addr - i;
- i = skip_addr;
- }
- else
- {
- i++;
- }
- }
- address -= address_offset;
- fmt3_ins->address = address;
- /* Fall Through to the next code section */
- }
- case AIC_OP_OR:
- case AIC_OP_AND:
- case AIC_OP_XOR:
- case AIC_OP_ADD:
- case AIC_OP_ADC:
- case AIC_OP_BMOV:
- if (fmt1_ins->parity != 0)
- {
- fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
- }
- fmt1_ins->parity = 0;
- /* Fall Through to the next code section */
- case AIC_OP_ROL:
- if ((p->features & AHC_ULTRA2) != 0)
- {
- int i, count;
-
- /* Calculate odd parity for the instruction */
- for ( i=0, count=0; i < 31; i++)
- {
- unsigned int mask;
-
- mask = 0x01 << i;
- if ((instr.integer & mask) != 0)
- count++;
- }
- if (!(count & 0x01))
- instr.format1.parity = 1;
- }
- else
- {
- if (fmt3_ins != NULL)
- {
- instr.integer = fmt3_ins->immediate |
- (fmt3_ins->source << 8) |
- (fmt3_ins->address << 16) |
- (fmt3_ins->opcode << 25);
- }
- else
- {
- instr.integer = fmt1_ins->immediate |
- (fmt1_ins->source << 8) |
- (fmt1_ins->destination << 16) |
- (fmt1_ins->ret << 24) |
- (fmt1_ins->opcode << 25);
- }
- }
- aic_outb(p, (instr.integer & 0xff), SEQRAM);
- aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
- aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
- aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
- udelay(10);
- break;
-
- default:
- panic("aic7xxx: Unknown opcode encountered in sequencer program.");
- break;
- }
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_loadseq
- *
- * Description:
- * Load the sequencer code into the controller memory.
- *-F*************************************************************************/
-static void
-aic7xxx_loadseq(struct aic7xxx_host *p)
-{
- struct sequencer_patch *cur_patch;
- int i;
- int downloaded;
- int skip_addr;
- unsigned char download_consts[4] = {0, 0, 0, 0};
-
- if (aic7xxx_verbose & VERBOSE_PROBE)
- {
- printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
- }
-#if 0
- download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
-#endif
- download_consts[TMODE_NUMCMDS] = 0;
- cur_patch = &sequencer_patches[0];
- downloaded = 0;
- skip_addr = 0;
-
- aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
- aic_outb(p, 0, SEQADDR0);
- aic_outb(p, 0, SEQADDR1);
-
- for (i = 0; i < sizeof(seqprog) / 4; i++)
- {
- if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0)
- {
- /* Skip this instruction for this configuration. */
- continue;
- }
- aic7xxx_download_instr(p, i, &download_consts[0]);
- downloaded++;
- }
-
- aic_outb(p, 0, SEQADDR0);
- aic_outb(p, 0, SEQADDR1);
- aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
- unpause_sequencer(p, TRUE);
- mdelay(1);
- pause_sequencer(p);
- aic_outb(p, FASTMODE, SEQCTL);
- if (aic7xxx_verbose & VERBOSE_PROBE)
- {
- printk(" %d instructions downloaded\n", downloaded);
- }
- if (aic7xxx_dump_sequencer)
- aic7xxx_print_sequencer(p, downloaded);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_print_sequencer
- *
- * Description:
- * Print the contents of the sequencer memory to the screen.
- *-F*************************************************************************/
-static void
-aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded)
-{
- int i, k, temp;
-
- aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
- aic_outb(p, 0, SEQADDR0);
- aic_outb(p, 0, SEQADDR1);
-
- k = 0;
- for (i=0; i < downloaded; i++)
- {
- if ( k == 0 )
- printk("%03x: ", i);
- temp = aic_inb(p, SEQRAM);
- temp |= (aic_inb(p, SEQRAM) << 8);
- temp |= (aic_inb(p, SEQRAM) << 16);
- temp |= (aic_inb(p, SEQRAM) << 24);
- printk("%08x", temp);
- if ( ++k == 8 )
- {
- printk("\n");
- k = 0;
- }
- else
- printk(" ");
- }
- aic_outb(p, 0, SEQADDR0);
- aic_outb(p, 0, SEQADDR1);
- aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
- unpause_sequencer(p, TRUE);
- mdelay(1);
- pause_sequencer(p);
- aic_outb(p, FASTMODE, SEQCTL);
- printk("\n");
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_info
- *
- * Description:
- * Return a string describing the driver.
- *-F*************************************************************************/
-static const char *
-aic7xxx_info(struct Scsi_Host *dooh)
-{
- static char buffer[256];
- char *bp;
- struct aic7xxx_host *p;
-
- bp = &buffer[0];
- p = (struct aic7xxx_host *)dooh->hostdata;
- memset(bp, 0, sizeof(buffer));
- strcpy(bp, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
- strcat(bp, AIC7XXX_C_VERSION);
- strcat(bp, "/");
- strcat(bp, AIC7XXX_H_VERSION);
- strcat(bp, "\n");
- strcat(bp, " <");
- strcat(bp, board_names[p->board_name_index]);
- strcat(bp, ">");
-
- return(bp);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_find_syncrate
- *
- * Description:
- * Look up the valid period to SCSIRATE conversion in our table
- *-F*************************************************************************/
-static struct aic7xxx_syncrate *
-aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
- unsigned int maxsync, unsigned char *options)
-{
- struct aic7xxx_syncrate *syncrate;
- int done = FALSE;
-
- switch(*options)
- {
- case MSG_EXT_PPR_OPTION_DT_CRC:
- case MSG_EXT_PPR_OPTION_DT_UNITS:
- if(!(p->features & AHC_ULTRA3))
- {
- *options = 0;
- maxsync = max_t(unsigned int, maxsync, AHC_SYNCRATE_ULTRA2);
- }
- break;
- case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
- case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
- if(!(p->features & AHC_ULTRA3))
- {
- *options = 0;
- maxsync = max_t(unsigned int, maxsync, AHC_SYNCRATE_ULTRA2);
- }
- else
- {
- /*
- * we don't support the Quick Arbitration variants of dual edge
- * clocking. As it turns out, we want to send back the
- * same basic option, but without the QA attribute.
- * We know that we are responding because we would never set
- * these options ourself, we would only respond to them.
- */
- switch(*options)
- {
- case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
- *options = MSG_EXT_PPR_OPTION_DT_CRC;
- break;
- case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
- *options = MSG_EXT_PPR_OPTION_DT_UNITS;
- break;
- }
- }
- break;
- default:
- *options = 0;
- maxsync = max_t(unsigned int, maxsync, AHC_SYNCRATE_ULTRA2);
- break;
- }
- syncrate = &aic7xxx_syncrates[maxsync];
- while ( (syncrate->rate[0] != NULL) &&
- (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
- {
- if (*period <= syncrate->period)
- {
- switch(*options)
- {
- case MSG_EXT_PPR_OPTION_DT_CRC:
- case MSG_EXT_PPR_OPTION_DT_UNITS:
- if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
- {
- done = TRUE;
- /*
- * oops, we went too low for the CRC/DualEdge signalling, so
- * clear the options byte
- */
- *options = 0;
- /*
- * We'll be sending a reply to this packet to set the options
- * properly, so unilaterally set the period as well.
- */
- *period = syncrate->period;
- }
- else
- {
- done = TRUE;
- if(syncrate == &aic7xxx_syncrates[maxsync])
- {
- *period = syncrate->period;
- }
- }
- break;
- default:
- if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
- {
- done = TRUE;
- if(syncrate == &aic7xxx_syncrates[maxsync])
- {
- *period = syncrate->period;
- }
- }
- break;
- }
- if(done)
- {
- break;
- }
- }
- syncrate++;
- }
- if ( (*period == 0) || (syncrate->rate[0] == NULL) ||
- ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) )
- {
- /*
- * Use async transfers for this target
- */
- *options = 0;
- *period = 255;
- syncrate = NULL;
- }
- return (syncrate);
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_find_period
- *
- * Description:
- * Look up the valid SCSIRATE to period conversion in our table
- *-F*************************************************************************/
-static unsigned int
-aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
- unsigned int maxsync)
-{
- struct aic7xxx_syncrate *syncrate;
-
- if (p->features & AHC_ULTRA2)
- {
- scsirate &= SXFR_ULTRA2;
- }
- else
- {
- scsirate &= SXFR;
- }
-
- syncrate = &aic7xxx_syncrates[maxsync];
- while (syncrate->rate[0] != NULL)
- {
- if (p->features & AHC_ULTRA2)
- {
- if (syncrate->sxfr_ultra2 == 0)
- break;
- else if (scsirate == syncrate->sxfr_ultra2)
- return (syncrate->period);
- else if (scsirate == (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC))
- return (syncrate->period);
- }
- else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
- {
- return (syncrate->period);
- }
- syncrate++;
- }
- return (0); /* async */
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_validate_offset
- *
- * Description:
- * Set a valid offset value for a particular card in use and transfer
- * settings in use.
- *-F*************************************************************************/
-static void
-aic7xxx_validate_offset(struct aic7xxx_host *p,
- struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide)
-{
- unsigned int maxoffset;
-
- /* Limit offset to what the card (and device) can do */
- if (syncrate == NULL)
- {
- maxoffset = 0;
- }
- else if (p->features & AHC_ULTRA2)
- {
- maxoffset = MAX_OFFSET_ULTRA2;
- }
- else
- {
- if (wide)
- maxoffset = MAX_OFFSET_16BIT;
- else
- maxoffset = MAX_OFFSET_8BIT;
- }
- *offset = min(*offset, maxoffset);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_set_syncrate
- *
- * Description:
- * Set the actual syncrate down in the card and in our host structs
- *-F*************************************************************************/
-static void
-aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
- int target, int channel, unsigned int period, unsigned int offset,
- unsigned char options, unsigned int type, struct aic_dev_data *aic_dev)
-{
- unsigned char tindex;
- unsigned short target_mask;
- unsigned char lun, old_options;
- unsigned int old_period, old_offset;
-
- tindex = target | (channel << 3);
- target_mask = 0x01 << tindex;
- lun = aic_inb(p, SCB_TCL) & 0x07;
-
- if (syncrate == NULL)
- {
- period = 0;
- offset = 0;
- }
-
- old_period = aic_dev->cur.period;
- old_offset = aic_dev->cur.offset;
- old_options = aic_dev->cur.options;
-
-
- if (type & AHC_TRANS_CUR)
- {
- unsigned int scsirate;
-
- scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
- if (p->features & AHC_ULTRA2)
- {
- scsirate &= ~SXFR_ULTRA2;
- if (syncrate != NULL)
- {
- switch(options)
- {
- case MSG_EXT_PPR_OPTION_DT_UNITS:
- /*
- * mask off the CRC bit in the xfer settings
- */
- scsirate |= (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC);
- break;
- default:
- scsirate |= syncrate->sxfr_ultra2;
- break;
- }
- }
- if (type & AHC_TRANS_ACTIVE)
- {
- aic_outb(p, offset, SCSIOFFSET);
- }
- aic_outb(p, offset, TARG_OFFSET + tindex);
- }
- else /* Not an Ultra2 controller */
- {
- scsirate &= ~(SXFR|SOFS);
- p->ultraenb &= ~target_mask;
- if (syncrate != NULL)
- {
- if (syncrate->sxfr & ULTRA_SXFR)
- {
- p->ultraenb |= target_mask;
- }
- scsirate |= (syncrate->sxfr & SXFR);
- scsirate |= (offset & SOFS);
- }
- if (type & AHC_TRANS_ACTIVE)
- {
- unsigned char sxfrctl0;
-
- sxfrctl0 = aic_inb(p, SXFRCTL0);
- sxfrctl0 &= ~FAST20;
- if (p->ultraenb & target_mask)
- sxfrctl0 |= FAST20;
- aic_outb(p, sxfrctl0, SXFRCTL0);
- }
- aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
- aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 );
- }
- if (type & AHC_TRANS_ACTIVE)
- {
- aic_outb(p, scsirate, SCSIRATE);
- }
- aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
- aic_dev->cur.period = period;
- aic_dev->cur.offset = offset;
- aic_dev->cur.options = options;
- if ( !(type & AHC_TRANS_QUITE) &&
- (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
- (aic_dev->flags & DEVICE_PRINT_DTR) )
- {
- if (offset)
- {
- int rate_mod = (scsirate & WIDEXFER) ? 1 : 0;
-
- printk(INFO_LEAD "Synchronous at %s Mbyte/sec, "
- "offset %d.\n", p->host_no, channel, target, lun,
- syncrate->rate[rate_mod], offset);
- }
- else
- {
- printk(INFO_LEAD "Using asynchronous transfers.\n",
- p->host_no, channel, target, lun);
- }
- aic_dev->flags &= ~DEVICE_PRINT_DTR;
- }
- }
-
- if (type & AHC_TRANS_GOAL)
- {
- aic_dev->goal.period = period;
- aic_dev->goal.offset = offset;
- aic_dev->goal.options = options;
- }
-
- if (type & AHC_TRANS_USER)
- {
- p->user[tindex].period = period;
- p->user[tindex].offset = offset;
- p->user[tindex].options = options;
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_set_width
- *
- * Description:
- * Set the actual width down in the card and in our host structs
- *-F*************************************************************************/
-static void
-aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
- unsigned int width, unsigned int type, struct aic_dev_data *aic_dev)
-{
- unsigned char tindex;
- unsigned short target_mask;
- unsigned int old_width;
-
- tindex = target | (channel << 3);
- target_mask = 1 << tindex;
-
- old_width = aic_dev->cur.width;
-
- if (type & AHC_TRANS_CUR)
- {
- unsigned char scsirate;
-
- scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
-
- scsirate &= ~WIDEXFER;
- if (width == MSG_EXT_WDTR_BUS_16_BIT)
- scsirate |= WIDEXFER;
-
- aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
-
- if (type & AHC_TRANS_ACTIVE)
- aic_outb(p, scsirate, SCSIRATE);
-
- aic_dev->cur.width = width;
-
- if ( !(type & AHC_TRANS_QUITE) &&
- (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- (aic_dev->flags & DEVICE_PRINT_DTR) )
- {
- printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
- lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
- }
- }
-
- if (type & AHC_TRANS_GOAL)
- aic_dev->goal.width = width;
- if (type & AHC_TRANS_USER)
- p->user[tindex].width = width;
-
- if (aic_dev->goal.offset)
- {
- if (p->features & AHC_ULTRA2)
- {
- aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
- }
- else if (width == MSG_EXT_WDTR_BUS_16_BIT)
- {
- aic_dev->goal.offset = MAX_OFFSET_16BIT;
- }
- else
- {
- aic_dev->goal.offset = MAX_OFFSET_8BIT;
- }
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * scbq_init
- *
- * Description:
- * SCB queue initialization.
- *
- *-F*************************************************************************/
-static void
-scbq_init(volatile scb_queue_type *queue)
-{
- queue->head = NULL;
- queue->tail = NULL;
-}
-
-/*+F*************************************************************************
- * Function:
- * scbq_insert_head
- *
- * Description:
- * Add an SCB to the head of the list.
- *
- *-F*************************************************************************/
-static inline void
-scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
-{
- scb->q_next = queue->head;
- queue->head = scb;
- if (queue->tail == NULL) /* If list was empty, update tail. */
- queue->tail = queue->head;
-}
-
-/*+F*************************************************************************
- * Function:
- * scbq_remove_head
- *
- * Description:
- * Remove an SCB from the head of the list.
- *
- *-F*************************************************************************/
-static inline struct aic7xxx_scb *
-scbq_remove_head(volatile scb_queue_type *queue)
-{
- struct aic7xxx_scb * scbp;
-
- scbp = queue->head;
- if (queue->head != NULL)
- queue->head = queue->head->q_next;
- if (queue->head == NULL) /* If list is now empty, update tail. */
- queue->tail = NULL;
- return(scbp);
-}
-
-/*+F*************************************************************************
- * Function:
- * scbq_remove
- *
- * Description:
- * Removes an SCB from the list.
- *
- *-F*************************************************************************/
-static inline void
-scbq_remove(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
-{
- if (queue->head == scb)
- {
- /* At beginning of queue, remove from head. */
- scbq_remove_head(queue);
- }
- else
- {
- struct aic7xxx_scb *curscb = queue->head;
-
- /*
- * Search until the next scb is the one we're looking for, or
- * we run out of queue.
- */
- while ((curscb != NULL) && (curscb->q_next != scb))
- {
- curscb = curscb->q_next;
- }
- if (curscb != NULL)
- {
- /* Found it. */
- curscb->q_next = scb->q_next;
- if (scb->q_next == NULL)
- {
- /* Update the tail when removing the tail. */
- queue->tail = curscb;
- }
- }
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * scbq_insert_tail
- *
- * Description:
- * Add an SCB at the tail of the list.
- *
- *-F*************************************************************************/
-static inline void
-scbq_insert_tail(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
-{
- scb->q_next = NULL;
- if (queue->tail != NULL) /* Add the scb at the end of the list. */
- queue->tail->q_next = scb;
- queue->tail = scb; /* Update the tail. */
- if (queue->head == NULL) /* If list was empty, update head. */
- queue->head = queue->tail;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_match_scb
- *
- * Description:
- * Checks to see if an scb matches the target/channel as specified.
- * If target is ALL_TARGETS (-1), then we're looking for any device
- * on the specified channel; this happens when a channel is going
- * to be reset and all devices on that channel must be aborted.
- *-F*************************************************************************/
-static int
-aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
- int target, int channel, int lun, unsigned char tag)
-{
- int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
- int chan = (scb->hscb->target_channel_lun >> 3) & 0x01;
- int slun = scb->hscb->target_channel_lun & 0x07;
- int match;
-
- match = ((chan == channel) || (channel == ALL_CHANNELS));
- if (match != 0)
- match = ((targ == target) || (target == ALL_TARGETS));
- if (match != 0)
- match = ((lun == slun) || (lun == ALL_LUNS));
- if (match != 0)
- match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
-
- return (match);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_add_curscb_to_free_list
- *
- * Description:
- * Adds the current scb (in SCBPTR) to the list of free SCBs.
- *-F*************************************************************************/
-static void
-aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
-{
- /*
- * Invalidate the tag so that aic7xxx_find_scb doesn't think
- * it's active
- */
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic_outb(p, 0, SCB_CONTROL);
-
- aic_outb(p, aic_inb(p, FREE_SCBH), SCB_NEXT);
- aic_outb(p, aic_inb(p, SCBPTR), FREE_SCBH);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_rem_scb_from_disc_list
- *
- * Description:
- * Removes the current SCB from the disconnected list and adds it
- * to the free list.
- *-F*************************************************************************/
-static unsigned char
-aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr,
- unsigned char prev)
-{
- unsigned char next;
-
- aic_outb(p, scbptr, SCBPTR);
- next = aic_inb(p, SCB_NEXT);
- aic7xxx_add_curscb_to_free_list(p);
-
- if (prev != SCB_LIST_NULL)
- {
- aic_outb(p, prev, SCBPTR);
- aic_outb(p, next, SCB_NEXT);
- }
- else
- {
- aic_outb(p, next, DISCONNECTED_SCBH);
- }
-
- return next;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_busy_target
- *
- * Description:
- * Set the specified target busy.
- *-F*************************************************************************/
-static inline void
-aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_index_busy_target
- *
- * Description:
- * Returns the index of the busy target, and optionally sets the
- * target inactive.
- *-F*************************************************************************/
-static inline unsigned char
-aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
- int unbusy)
-{
- unsigned char busy_scbid;
-
- busy_scbid = p->untagged_scbs[tcl];
- if (unbusy)
- {
- p->untagged_scbs[tcl] = SCB_LIST_NULL;
- }
- return (busy_scbid);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_find_scb
- *
- * Description:
- * Look through the SCB array of the card and attempt to find the
- * hardware SCB that corresponds to the passed in SCB. Return
- * SCB_LIST_NULL if unsuccessful. This routine assumes that the
- * card is already paused.
- *-F*************************************************************************/
-static unsigned char
-aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- unsigned char saved_scbptr;
- unsigned char curindex;
-
- saved_scbptr = aic_inb(p, SCBPTR);
- curindex = 0;
- for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
- {
- aic_outb(p, curindex, SCBPTR);
- if (aic_inb(p, SCB_TAG) == scb->hscb->tag)
- {
- break;
- }
- }
- aic_outb(p, saved_scbptr, SCBPTR);
- if (curindex >= p->scb_data->maxhscbs)
- {
- curindex = SCB_LIST_NULL;
- }
-
- return (curindex);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_allocate_scb
- *
- * Description:
- * Get an SCB from the free list or by allocating a new one.
- *-F*************************************************************************/
-static int
-aic7xxx_allocate_scb(struct aic7xxx_host *p)
-{
- struct aic7xxx_scb *scbp = NULL;
- int scb_size = (sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG) + 12 + 6;
- int i;
- int step = PAGE_SIZE / 1024;
- unsigned long scb_count = 0;
- struct hw_scatterlist *hsgp;
- struct aic7xxx_scb *scb_ap;
- struct aic7xxx_scb_dma *scb_dma;
- unsigned char *bufs;
-
- if (p->scb_data->numscbs < p->scb_data->maxscbs)
- {
- /*
- * Calculate the optimal number of SCBs to allocate.
- *
- * NOTE: This formula works because the sizeof(sg_array) is always
- * 1024. Therefore, scb_size * i would always be > PAGE_SIZE *
- * (i/step). The (i-1) allows the left hand side of the equation
- * to grow into the right hand side to a point of near perfect
- * efficiency since scb_size * (i -1) is growing slightly faster
- * than the right hand side. If the number of SG array elements
- * is changed, this function may not be near so efficient any more.
- *
- * Since the DMA'able buffers are now allocated in a separate
- * chunk this algorithm has been modified to match. The '12'
- * and '6' factors in scb_size are for the DMA'able command byte
- * and sensebuffers respectively. -DaveM
- */
- for ( i=step;; i *= 2 )
- {
- if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) )
- {
- i /= 2;
- break;
- }
- }
- scb_count = min( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
- scb_ap = kmalloc(sizeof (struct aic7xxx_scb) * scb_count
- + sizeof(struct aic7xxx_scb_dma), GFP_ATOMIC);
- if (scb_ap == NULL)
- return(0);
- scb_dma = (struct aic7xxx_scb_dma *)&scb_ap[scb_count];
- hsgp = (struct hw_scatterlist *)
- pci_alloc_consistent(p->pdev, scb_size * scb_count,
- &scb_dma->dma_address);
- if (hsgp == NULL)
- {
- kfree(scb_ap);
- return(0);
- }
- bufs = (unsigned char *)&hsgp[scb_count * AIC7XXX_MAX_SG];
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- {
- if (p->scb_data->numscbs == 0)
- printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
- p->host_no, -1, -1, -1, scb_count);
- else
- printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
- p->host_no, -1, -1, -1, scb_count);
- }
-#endif
- memset(scb_ap, 0, sizeof (struct aic7xxx_scb) * scb_count);
- scb_dma->dma_offset = (unsigned long)scb_dma->dma_address
- - (unsigned long)hsgp;
- scb_dma->dma_len = scb_size * scb_count;
- for (i=0; i < scb_count; i++)
- {
- scbp = &scb_ap[i];
- scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs];
- scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG];
- scbp->sense_cmd = bufs;
- scbp->cmnd = bufs + 6;
- bufs += 12 + 6;
- scbp->scb_dma = scb_dma;
- memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb));
- scbp->hscb->tag = p->scb_data->numscbs;
- /*
- * Place in the scb array; never is removed
- */
- p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
- scbq_insert_tail(&p->scb_data->free_scbs, scbp);
- }
- scbp->kmalloc_ptr = scb_ap;
- }
- return(scb_count);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_queue_cmd_complete
- *
- * Description:
- * Due to race conditions present in the SCSI subsystem, it is easier
- * to queue completed commands, then call scsi_done() on them when
- * we're finished. This function queues the completed commands.
- *-F*************************************************************************/
-static void
-aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, struct scsi_cmnd *cmd)
-{
- aic7xxx_position(cmd) = SCB_LIST_NULL;
- cmd->host_scribble = (char *)p->completeq.head;
- p->completeq.head = cmd;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_done_cmds_complete
- *
- * Description:
- * Process the completed command queue.
- *-F*************************************************************************/
-static void aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
-{
- struct scsi_cmnd *cmd;
-
- while (p->completeq.head != NULL) {
- cmd = p->completeq.head;
- p->completeq.head = (struct scsi_cmnd *) cmd->host_scribble;
- cmd->host_scribble = NULL;
- cmd->scsi_done(cmd);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_free_scb
- *
- * Description:
- * Free the scb and insert into the free scb list.
- *-F*************************************************************************/
-static void
-aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-
- scb->flags = SCB_FREE;
- scb->cmd = NULL;
- scb->sg_count = 0;
- scb->sg_length = 0;
- scb->tag_action = 0;
- scb->hscb->control = 0;
- scb->hscb->target_status = 0;
- scb->hscb->target_channel_lun = SCB_LIST_NULL;
-
- scbq_insert_head(&p->scb_data->free_scbs, scb);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_done
- *
- * Description:
- * Calls the higher level scsi done function and frees the scb.
- *-F*************************************************************************/
-static void
-aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- struct scsi_cmnd *cmd = scb->cmd;
- struct aic_dev_data *aic_dev = cmd->device->hostdata;
- int tindex = TARGET_INDEX(cmd);
- struct aic7xxx_scb *scbp;
- unsigned char queue_depth;
-
- scsi_dma_unmap(cmd);
-
- if (scb->flags & SCB_SENSE)
- {
- pci_unmap_single(p->pdev,
- le32_to_cpu(scb->sg_list[0].address),
- SCSI_SENSE_BUFFERSIZE,
- PCI_DMA_FROMDEVICE);
- }
- if (scb->flags & SCB_RECOVERY_SCB)
- {
- p->flags &= ~AHC_ABORT_PENDING;
- }
- if (scb->flags & (SCB_RESET|SCB_ABORT))
- {
- cmd->result |= (DID_RESET << 16);
- }
-
- if ((scb->flags & SCB_MSGOUT_BITS) != 0)
- {
- unsigned short mask;
- int message_error = FALSE;
-
- mask = 0x01 << tindex;
-
- /*
- * Check to see if we get an invalid message or a message error
- * after failing to negotiate a wide or sync transfer message.
- */
- if ((scb->flags & SCB_SENSE) &&
- ((scb->cmd->sense_buffer[12] == 0x43) || /* INVALID_MESSAGE */
- (scb->cmd->sense_buffer[12] == 0x49))) /* MESSAGE_ERROR */
- {
- message_error = TRUE;
- }
-
- if (scb->flags & SCB_MSGOUT_WDTR)
- {
- if (message_error)
- {
- if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- (aic_dev->flags & DEVICE_PRINT_DTR) )
- {
- printk(INFO_LEAD "Device failed to complete Wide Negotiation "
- "processing and\n", p->host_no, CTL_OF_SCB(scb));
- printk(INFO_LEAD "returned a sense error code for invalid message, "
- "disabling future\n", p->host_no, CTL_OF_SCB(scb));
- printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
- CTL_OF_SCB(scb));
- }
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
- }
- }
- if (scb->flags & SCB_MSGOUT_SDTR)
- {
- if (message_error)
- {
- if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- (aic_dev->flags & DEVICE_PRINT_DTR) )
- {
- printk(INFO_LEAD "Device failed to complete Sync Negotiation "
- "processing and\n", p->host_no, CTL_OF_SCB(scb));
- printk(INFO_LEAD "returned a sense error code for invalid message, "
- "disabling future\n", p->host_no, CTL_OF_SCB(scb));
- printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
- CTL_OF_SCB(scb));
- aic_dev->flags &= ~DEVICE_PRINT_DTR;
- }
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
- }
- }
- if (scb->flags & SCB_MSGOUT_PPR)
- {
- if(message_error)
- {
- if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- (aic_dev->flags & DEVICE_PRINT_DTR) )
- {
- printk(INFO_LEAD "Device failed to complete Parallel Protocol "
- "Request processing and\n", p->host_no, CTL_OF_SCB(scb));
- printk(INFO_LEAD "returned a sense error code for invalid message, "
- "disabling future\n", p->host_no, CTL_OF_SCB(scb));
- printk(INFO_LEAD "Parallel Protocol Request negotiation to this "
- "device.\n", p->host_no, CTL_OF_SCB(scb));
- }
- /*
- * Disable PPR negotiation and revert back to WDTR and SDTR setup
- */
- aic_dev->needppr = aic_dev->needppr_copy = 0;
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
- }
- }
- }
-
- queue_depth = aic_dev->temp_q_depth;
- if (queue_depth >= aic_dev->active_cmds)
- {
- scbp = scbq_remove_head(&aic_dev->delayed_scbs);
- if (scbp)
- {
- if (queue_depth == 1)
- {
- /*
- * Give extra preference to untagged devices, such as CD-R devices
- * This makes it more likely that a drive *won't* stuff up while
- * waiting on data at a critical time, such as CD-R writing and
- * audio CD ripping operations. Should also benefit tape drives.
- */
- scbq_insert_head(&p->waiting_scbs, scbp);
- }
- else
- {
- scbq_insert_tail(&p->waiting_scbs, scbp);
- }
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n",
- p->host_no, CTL_OF_SCB(scbp));
-#endif
- if (queue_depth > aic_dev->active_cmds)
- {
- scbp = scbq_remove_head(&aic_dev->delayed_scbs);
- if (scbp)
- scbq_insert_tail(&p->waiting_scbs, scbp);
- }
- }
- }
- if (!(scb->tag_action))
- {
- aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun,
- /* unbusy */ TRUE);
- if (cmd->device->simple_tags)
- {
- aic_dev->temp_q_depth = aic_dev->max_q_depth;
- }
- }
- if(scb->flags & SCB_DTR_SCB)
- {
- aic_dev->dtr_pending = 0;
- }
- aic_dev->active_cmds--;
- p->activescbs--;
-
- if ((scb->sg_length >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
- {
- long *ptr;
- int x, i;
-
-
- if (rq_data_dir(cmd->request) == WRITE)
- {
- aic_dev->w_total++;
- ptr = aic_dev->w_bins;
- }
- else
- {
- aic_dev->r_total++;
- ptr = aic_dev->r_bins;
- }
- x = scb->sg_length;
- x >>= 10;
- for(i=0; i<6; i++)
- {
- x >>= 2;
- if(!x) {
- ptr[i]++;
- break;
- }
- }
- if(i == 6 && x)
- ptr[5]++;
- }
- aic7xxx_free_scb(p, scb);
- aic7xxx_queue_cmd_complete(p, cmd);
-
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_run_done_queue
- *
- * Description:
- * Calls the aic7xxx_done() for the scsi_cmnd of each scb in the
- * aborted list, and adds each scb to the free list. If complete
- * is TRUE, we also process the commands complete list.
- *-F*************************************************************************/
-static void
-aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
-{
- struct aic7xxx_scb *scb;
- int i, found = 0;
-
- for (i = 0; i < p->scb_data->numscbs; i++)
- {
- scb = p->scb_data->scb_array[i];
- if (scb->flags & SCB_QUEUED_FOR_DONE)
- {
- if (scb->flags & SCB_QUEUE_FULL)
- {
- scb->cmd->result = QUEUE_FULL << 1;
- }
- else
- {
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Aborting scb %d\n",
- p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
- /*
- * Clear any residual information since the normal aic7xxx_done() path
- * doesn't touch the residuals.
- */
- scb->hscb->residual_SG_segment_count = 0;
- scb->hscb->residual_data_count[0] = 0;
- scb->hscb->residual_data_count[1] = 0;
- scb->hscb->residual_data_count[2] = 0;
- }
- found++;
- aic7xxx_done(p, scb);
- }
- }
- if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
- {
- printk(INFO_LEAD "%d commands found and queued for "
- "completion.\n", p->host_no, -1, -1, -1, found);
- }
- if (complete)
- {
- aic7xxx_done_cmds_complete(p);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_abort_waiting_scb
- *
- * Description:
- * Manipulate the waiting for selection list and return the
- * scb that follows the one that we remove.
- *-F*************************************************************************/
-static unsigned char
-aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
- unsigned char scbpos, unsigned char prev)
-{
- unsigned char curscb, next;
-
- /*
- * Select the SCB we want to abort and pull the next pointer out of it.
- */
- curscb = aic_inb(p, SCBPTR);
- aic_outb(p, scbpos, SCBPTR);
- next = aic_inb(p, SCB_NEXT);
-
- aic7xxx_add_curscb_to_free_list(p);
-
- /*
- * Update the waiting list
- */
- if (prev == SCB_LIST_NULL)
- {
- /*
- * First in the list
- */
- aic_outb(p, next, WAITING_SCBH);
- }
- else
- {
- /*
- * Select the scb that pointed to us and update its next pointer.
- */
- aic_outb(p, prev, SCBPTR);
- aic_outb(p, next, SCB_NEXT);
- }
- /*
- * Point us back at the original scb position and inform the SCSI
- * system that the command has been aborted.
- */
- aic_outb(p, curscb, SCBPTR);
- return (next);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_search_qinfifo
- *
- * Description:
- * Search the queue-in FIFO for matching SCBs and conditionally
- * requeue. Returns the number of matching SCBs.
- *-F*************************************************************************/
-static int
-aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, int channel,
- int lun, unsigned char tag, int flags, int requeue,
- volatile scb_queue_type *queue)
-{
- int found;
- unsigned char qinpos, qintail;
- struct aic7xxx_scb *scbp;
-
- found = 0;
- qinpos = aic_inb(p, QINPOS);
- qintail = p->qinfifonext;
-
- p->qinfifonext = qinpos;
-
- while (qinpos != qintail)
- {
- scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- /*
- * We found an scb that needs to be removed.
- */
- if (requeue && (queue != NULL))
- {
- if (scbp->flags & SCB_WAITINGQ)
- {
- scbq_remove(queue, scbp);
- scbq_remove(&p->waiting_scbs, scbp);
- scbq_remove(&AIC_DEV(scbp->cmd)->delayed_scbs, scbp);
- AIC_DEV(scbp->cmd)->active_cmds++;
- p->activescbs++;
- }
- scbq_insert_tail(queue, scbp);
- AIC_DEV(scbp->cmd)->active_cmds--;
- p->activescbs--;
- scbp->flags |= SCB_WAITINGQ;
- if ( !(scbp->tag_action & TAG_ENB) )
- {
- aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
- TRUE);
- }
- }
- else if (requeue)
- {
- p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
- }
- else
- {
- /*
- * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
- * flags we were called with, presumeably so aic7xxx_run_done_queue
- * can find this scb
- */
- scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
- if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
- FALSE) == scbp->hscb->tag)
- {
- aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
- TRUE);
- }
- }
- found++;
- }
- else
- {
- p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
- }
- }
- /*
- * Now that we've done the work, clear out any left over commands in the
- * qinfifo and update the KERNEL_QINPOS down on the card.
- *
- * NOTE: This routine expect the sequencer to already be paused when
- * it is run....make sure it's that way!
- */
- qinpos = p->qinfifonext;
- while(qinpos != qintail)
- {
- p->qinfifo[qinpos++] = SCB_LIST_NULL;
- }
- if (p->features & AHC_QUEUE_REGS)
- aic_outb(p, p->qinfifonext, HNSCB_QOFF);
- else
- aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-
- return (found);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_scb_on_qoutfifo
- *
- * Description:
- * Is the scb that was passed to us currently on the qoutfifo?
- *-F*************************************************************************/
-static int
-aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- int i=0;
-
- while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
- {
- if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
- return TRUE;
- else
- i++;
- }
- return FALSE;
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_reset_device
- *
- * Description:
- * The device at the given target/channel has been reset. Abort
- * all active and queued scbs for that target/channel. This function
- * need not worry about linked next pointers because if was a MSG_ABORT_TAG
- * then we had a tagged command (no linked next), if it was MSG_ABORT or
- * MSG_BUS_DEV_RESET then the device won't know about any commands any more
- * and no busy commands will exist, and if it was a bus reset, then nothing
- * knows about any linked next commands any more. In all cases, we don't
- * need to worry about the linked next or busy scb, we just need to clear
- * them.
- *-F*************************************************************************/
-static void
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
- int lun, unsigned char tag)
-{
- struct aic7xxx_scb *scbp, *prev_scbp;
- struct scsi_device *sd;
- unsigned char active_scb, tcl, scb_tag;
- int i = 0, init_lists = FALSE;
- struct aic_dev_data *aic_dev;
-
- /*
- * Restore this when we're done
- */
- active_scb = aic_inb(p, SCBPTR);
- scb_tag = aic_inb(p, SCB_TAG);
-
- if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
- {
- printk(INFO_LEAD "Reset device, hardware_scb %d,\n",
- p->host_no, channel, target, lun, active_scb);
- printk(INFO_LEAD "Current scb %d, SEQADDR 0x%x, LASTPHASE "
- "0x%x\n",
- p->host_no, channel, target, lun, scb_tag,
- aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
- aic_inb(p, LASTPHASE));
- printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%x\n",
- p->host_no, channel, target, lun,
- (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0,
- aic_inb(p, SG_COUNT), aic_inb(p, SCSISIGI));
- printk(INFO_LEAD "SSTAT0 0x%x, SSTAT1 0x%x, SSTAT2 0x%x\n",
- p->host_no, channel, target, lun, aic_inb(p, SSTAT0),
- aic_inb(p, SSTAT1), aic_inb(p, SSTAT2));
- }
-
- /*
- * Deal with the busy target and linked next issues.
- */
- list_for_each_entry(aic_dev, &p->aic_devs, list)
- {
- if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
- printk(INFO_LEAD "processing aic_dev %p\n", p->host_no, channel, target,
- lun, aic_dev);
- sd = aic_dev->SDptr;
-
- if((target != ALL_TARGETS && target != sd->id) ||
- (channel != ALL_CHANNELS && channel != sd->channel))
- continue;
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning up status information "
- "and delayed_scbs.\n", p->host_no, sd->channel, sd->id, sd->lun);
- aic_dev->flags &= ~BUS_DEVICE_RESET_PENDING;
- if ( tag == SCB_LIST_NULL )
- {
- aic_dev->dtr_pending = 0;
- aic_dev->needppr = aic_dev->needppr_copy;
- aic_dev->needsdtr = aic_dev->needsdtr_copy;
- aic_dev->needwdtr = aic_dev->needwdtr_copy;
- aic_dev->flags = DEVICE_PRINT_DTR;
- aic_dev->temp_q_depth = aic_dev->max_q_depth;
- }
- tcl = (sd->id << 4) | (sd->channel << 3) | sd->lun;
- if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
- (tag == SCB_LIST_NULL) )
- aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
- prev_scbp = NULL;
- scbp = aic_dev->delayed_scbs.head;
- while (scbp != NULL)
- {
- prev_scbp = scbp;
- scbp = scbp->q_next;
- if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
- {
- scbq_remove(&aic_dev->delayed_scbs, prev_scbp);
- if (prev_scbp->flags & SCB_WAITINGQ)
- {
- aic_dev->active_cmds++;
- p->activescbs++;
- }
- prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- }
- }
- }
-
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
- aic7xxx_search_qinfifo(p, target, channel, lun, tag,
- SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
-
-/*
- * Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
- * ABORT/RESET commands.
- */
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning waiting_scbs.\n", p->host_no, channel,
- target, lun );
- {
- struct aic7xxx_scb *scbp, *prev_scbp;
-
- prev_scbp = NULL;
- scbp = p->waiting_scbs.head;
- while (scbp != NULL)
- {
- prev_scbp = scbp;
- scbp = scbp->q_next;
- if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
- {
- scbq_remove(&p->waiting_scbs, prev_scbp);
- if (prev_scbp->flags & SCB_WAITINGQ)
- {
- AIC_DEV(prev_scbp->cmd)->active_cmds++;
- p->activescbs++;
- }
- prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- }
- }
- }
-
-
- /*
- * Search waiting for selection list.
- */
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning waiting for selection "
- "list.\n", p->host_no, channel, target, lun);
- {
- unsigned char next, prev, scb_index;
-
- next = aic_inb(p, WAITING_SCBH); /* Start at head of list. */
- prev = SCB_LIST_NULL;
- while (next != SCB_LIST_NULL)
- {
- aic_outb(p, next, SCBPTR);
- scb_index = aic_inb(p, SCB_TAG);
- if (scb_index >= p->scb_data->numscbs)
- {
- /*
- * No aic7xxx_verbose check here.....we want to see this since it
- * means either the kernel driver or the sequencer screwed things up
- */
- printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
- "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
- p->scb_data->numscbs);
- next = aic_inb(p, SCB_NEXT);
- aic7xxx_add_curscb_to_free_list(p);
- }
- else
- {
- scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
- if (scbp->flags & SCB_WAITINGQ)
- {
- AIC_DEV(scbp->cmd)->active_cmds++;
- p->activescbs++;
- }
- scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- if (prev == SCB_LIST_NULL)
- {
- /*
- * This is either the first scb on the waiting list, or we
- * have already yanked the first and haven't left any behind.
- * Either way, we need to turn off the selection hardware if
- * it isn't already off.
- */
- aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
- aic_outb(p, CLRSELTIMEO, CLRSINT1);
- }
- }
- else
- {
- prev = next;
- next = aic_inb(p, SCB_NEXT);
- }
- }
- }
- }
-
- /*
- * Go through disconnected list and remove any entries we have queued
- * for completion, zeroing their control byte too.
- */
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning disconnected scbs "
- "list.\n", p->host_no, channel, target, lun);
- if (p->flags & AHC_PAGESCBS)
- {
- unsigned char next, prev, scb_index;
-
- next = aic_inb(p, DISCONNECTED_SCBH);
- prev = SCB_LIST_NULL;
- while (next != SCB_LIST_NULL)
- {
- aic_outb(p, next, SCBPTR);
- scb_index = aic_inb(p, SCB_TAG);
- if (scb_index > p->scb_data->numscbs)
- {
- printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
- "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
- p->scb_data->numscbs);
- next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
- }
- else
- {
- scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
- if (scbp->flags & SCB_WAITINGQ)
- {
- AIC_DEV(scbp->cmd)->active_cmds++;
- p->activescbs++;
- }
- scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- scbp->hscb->control = 0;
- }
- else
- {
- prev = next;
- next = aic_inb(p, SCB_NEXT);
- }
- }
- }
- }
-
- /*
- * Walk the free list making sure no entries on the free list have
- * a valid SCB_TAG value or SCB_CONTROL byte.
- */
- if (p->flags & AHC_PAGESCBS)
- {
- unsigned char next;
-
- next = aic_inb(p, FREE_SCBH);
- while (next != SCB_LIST_NULL)
- {
- aic_outb(p, next, SCBPTR);
- if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
- {
- printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
- target, lun);
- init_lists = TRUE;
- next = SCB_LIST_NULL;
- }
- else
- {
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic_outb(p, 0, SCB_CONTROL);
- next = aic_inb(p, SCB_NEXT);
- }
- }
- }
-
- /*
- * Go through the hardware SCB array looking for commands that
- * were active but not on any list.
- */
- if (init_lists)
- {
- aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
- aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
- aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
- }
- for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
- {
- unsigned char scbid;
-
- aic_outb(p, i, SCBPTR);
- if (init_lists)
- {
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
- aic_outb(p, 0, SCB_CONTROL);
- aic7xxx_add_curscb_to_free_list(p);
- }
- else
- {
- scbid = aic_inb(p, SCB_TAG);
- if (scbid < p->scb_data->numscbs)
- {
- scbp = p->scb_data->scb_array[scbid];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- aic_outb(p, 0, SCB_CONTROL);
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic7xxx_add_curscb_to_free_list(p);
- }
- }
- }
- }
-
- /*
- * Go through the entire SCB array now and look for commands for
- * for this target that are stillactive. These are other (most likely
- * tagged) commands that were disconnected when the reset occurred.
- * Any commands we find here we know this about, it wasn't on any queue,
- * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
- * lists, so it really must have been a paged out SCB. In that case,
- * we shouldn't need to bother with updating any counters, just mark
- * the correct flags and go on.
- */
- for (i = 0; i < p->scb_data->numscbs; i++)
- {
- scbp = p->scb_data->scb_array[i];
- if ((scbp->flags & SCB_ACTIVE) &&
- aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
- !aic7xxx_scb_on_qoutfifo(p, scbp))
- {
- if (scbp->flags & SCB_WAITINGQ)
- {
- scbq_remove(&p->waiting_scbs, scbp);
- scbq_remove(&AIC_DEV(scbp->cmd)->delayed_scbs, scbp);
- AIC_DEV(scbp->cmd)->active_cmds++;
- p->activescbs++;
- }
- scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- }
- }
-
- aic_outb(p, active_scb, SCBPTR);
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_clear_intstat
- *
- * Description:
- * Clears the interrupt status.
- *-F*************************************************************************/
-static void
-aic7xxx_clear_intstat(struct aic7xxx_host *p)
-{
- /* Clear any interrupt conditions this may have caused. */
- aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
- aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
- CLRPHASECHG | CLRREQINIT, CLRSINT1);
- aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_reset_current_bus
- *
- * Description:
- * Reset the current SCSI bus.
- *-F*************************************************************************/
-static void
-aic7xxx_reset_current_bus(struct aic7xxx_host *p)
-{
-
- /* Disable reset interrupts. */
- aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
-
- /* Turn off the bus' current operations, after all, we shouldn't have any
- * valid commands left to cause a RSELI and SELO once we've tossed the
- * bus away with this reset, so we might as well shut down the sequencer
- * until the bus is restarted as opposed to saving the current settings
- * and restoring them (which makes no sense to me). */
-
- /* Turn on the bus reset. */
- aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
- while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
- mdelay(5);
-
- /*
- * Some of the new Ultra2 chipsets need a longer delay after a chip
- * reset than just the init setup creates, so we have to delay here
- * before we go into a reset in order to make the chips happy.
- */
- if (p->features & AHC_ULTRA2)
- mdelay(250);
- else
- mdelay(50);
-
- /* Turn off the bus reset. */
- aic_outb(p, 0, SCSISEQ);
- mdelay(10);
-
- aic7xxx_clear_intstat(p);
- /* Re-enable reset interrupts. */
- aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
-
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_reset_channel
- *
- * Description:
- * Reset the channel.
- *-F*************************************************************************/
-static void
-aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
-{
- unsigned long offset_min, offset_max;
- unsigned char sblkctl;
- int cur_channel;
-
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Reset channel called, %s initiate reset.\n",
- p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
-
-
- if (channel == 1)
- {
- offset_min = 8;
- offset_max = 16;
- }
- else
- {
- if (p->features & AHC_TWIN)
- {
- /* Channel A */
- offset_min = 0;
- offset_max = 8;
- }
- else
- {
- offset_min = 0;
- if (p->features & AHC_WIDE)
- {
- offset_max = 16;
- }
- else
- {
- offset_max = 8;
- }
- }
- }
-
- while (offset_min < offset_max)
- {
- /*
- * Revert to async/narrow transfers until we renegotiate.
- */
- aic_outb(p, 0, TARG_SCSIRATE + offset_min);
- if (p->features & AHC_ULTRA2)
- {
- aic_outb(p, 0, TARG_OFFSET + offset_min);
- }
- offset_min++;
- }
-
- /*
- * Reset the bus and unpause/restart the controller
- */
- sblkctl = aic_inb(p, SBLKCTL);
- if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
- cur_channel = (sblkctl & SELBUSB) >> 3;
- else
- cur_channel = 0;
- if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
- {
- /*
- * Case 1: Command for another bus is active
- */
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Stealthily resetting idle channel.\n", p->host_no,
- channel, -1, -1);
- /*
- * Stealthily reset the other bus without upsetting the current bus.
- */
- aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
- aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
- if (initiate_reset)
- {
- aic7xxx_reset_current_bus(p);
- }
- aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
- aic7xxx_clear_intstat(p);
- aic_outb(p, sblkctl, SBLKCTL);
- }
- else
- {
- /*
- * Case 2: A command from this bus is active or we're idle.
- */
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Resetting currently active channel.\n", p->host_no,
- channel, -1, -1);
- aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
- SIMODE1);
- p->flags &= ~AHC_HANDLING_REQINITS;
- p->msg_type = MSG_TYPE_NONE;
- p->msg_len = 0;
- if (initiate_reset)
- {
- aic7xxx_reset_current_bus(p);
- }
- aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
- aic7xxx_clear_intstat(p);
- }
- if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
- printk(INFO_LEAD "Channel reset\n", p->host_no, channel, -1, -1);
- /*
- * Clean up all the state information for the pending transactions
- * on this bus.
- */
- aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
-
- if ( !(p->features & AHC_TWIN) )
- {
- restart_sequencer(p);
- }
-
- return;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_run_waiting_queues
- *
- * Description:
- * Scan the awaiting_scbs queue downloading and starting as many
- * scbs as we can.
- *-F*************************************************************************/
-static void
-aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
-{
- struct aic7xxx_scb *scb;
- struct aic_dev_data *aic_dev;
- int sent;
-
-
- if (p->waiting_scbs.head == NULL)
- return;
-
- sent = 0;
-
- /*
- * First handle SCBs that are waiting but have been assigned a slot.
- */
- while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
- {
- aic_dev = scb->cmd->device->hostdata;
- if ( !scb->tag_action )
- {
- aic_dev->temp_q_depth = 1;
- }
- if ( aic_dev->active_cmds >= aic_dev->temp_q_depth)
- {
- scbq_insert_tail(&aic_dev->delayed_scbs, scb);
- }
- else
- {
- scb->flags &= ~SCB_WAITINGQ;
- aic_dev->active_cmds++;
- p->activescbs++;
- if ( !(scb->tag_action) )
- {
- aic7xxx_busy_target(p, scb);
- }
- p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
- sent++;
- }
- }
- if (sent)
- {
- if (p->features & AHC_QUEUE_REGS)
- aic_outb(p, p->qinfifonext, HNSCB_QOFF);
- else
- {
- pause_sequencer(p);
- aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
- unpause_sequencer(p, FALSE);
- }
- if (p->activescbs > p->max_activescbs)
- p->max_activescbs = p->activescbs;
- }
-}
-
-#ifdef CONFIG_PCI
-
-#define DPE 0x80
-#define SSE 0x40
-#define RMA 0x20
-#define RTA 0x10
-#define STA 0x08
-#define DPR 0x01
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_pci_intr
- *
- * Description:
- * Check the scsi card for PCI errors and clear the interrupt
- *
- * NOTE: If you don't have this function and a 2940 card encounters
- * a PCI error condition, the machine will end up locked as the
- * interrupt handler gets slammed with non-stop PCI error interrupts
- *-F*************************************************************************/
-static void
-aic7xxx_pci_intr(struct aic7xxx_host *p)
-{
- unsigned char status1;
-
- pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
-
- if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
- "phase.\n", p->host_no, -1, -1, -1);
- if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
- -1, -1, -1);
- if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
- -1, -1, -1);
- if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
- -1, -1, -1);
- if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
- -1, -1, -1);
- if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
- "PERR#\n", p->host_no, -1, -1, -1);
-
- pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
- if (status1 & (DPR|RMA|RTA))
- aic_outb(p, CLRPARERR, CLRINT);
-
- if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
- aic7xxx_panic_abort(p, NULL);
-
-}
-#endif /* CONFIG_PCI */
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_construct_ppr
- *
- * Description:
- * Build up a Parallel Protocol Request message for use with SCSI-3
- * devices.
- *-F*************************************************************************/
-static void
-aic7xxx_construct_ppr(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- p->msg_buf[p->msg_index++] = MSG_EXTENDED;
- p->msg_buf[p->msg_index++] = MSG_EXT_PPR_LEN;
- p->msg_buf[p->msg_index++] = MSG_EXT_PPR;
- p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.period;
- p->msg_buf[p->msg_index++] = 0;
- p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.offset;
- p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.width;
- p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.options;
- p->msg_len += 8;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_construct_sdtr
- *
- * Description:
- * Constucts a synchronous data transfer message in the message
- * buffer on the sequencer.
- *-F*************************************************************************/
-static void
-aic7xxx_construct_sdtr(struct aic7xxx_host *p, unsigned char period,
- unsigned char offset)
-{
- p->msg_buf[p->msg_index++] = MSG_EXTENDED;
- p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
- p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
- p->msg_buf[p->msg_index++] = period;
- p->msg_buf[p->msg_index++] = offset;
- p->msg_len += 5;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_construct_wdtr
- *
- * Description:
- * Constucts a wide data transfer message in the message buffer
- * on the sequencer.
- *-F*************************************************************************/
-static void
-aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width)
-{
- p->msg_buf[p->msg_index++] = MSG_EXTENDED;
- p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
- p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
- p->msg_buf[p->msg_index++] = bus_width;
- p->msg_len += 4;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_calc_residual
- *
- * Description:
- * Calculate the residual data not yet transferred.
- *-F*************************************************************************/
-static void
-aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- struct aic7xxx_hwscb *hscb;
- struct scsi_cmnd *cmd;
- int actual, i;
-
- cmd = scb->cmd;
- hscb = scb->hscb;
-
- /*
- * Don't destroy valid residual information with
- * residual coming from a check sense operation.
- */
- if (((scb->hscb->control & DISCONNECTED) == 0) &&
- (scb->flags & SCB_SENSE) == 0)
- {
- /*
- * We had an underflow. At this time, there's only
- * one other driver that bothers to check for this,
- * and cmd->underflow seems to be set rather half-
- * heartedly in the higher-level SCSI code.
- */
- actual = scb->sg_length;
- for (i=1; i < hscb->residual_SG_segment_count; i++)
- {
- actual -= scb->sg_list[scb->sg_count - i].length;
- }
- actual -= (hscb->residual_data_count[2] << 16) |
- (hscb->residual_data_count[1] << 8) |
- hscb->residual_data_count[0];
-
- if (actual < cmd->underflow)
- {
- if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
- {
- printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
- "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
- (rq_data_dir(cmd->request) == WRITE) ? "wrote" : "read", actual,
- hscb->residual_SG_segment_count);
- printk(INFO_LEAD "status 0x%x.\n", p->host_no, CTL_OF_SCB(scb),
- hscb->target_status);
- }
- /*
- * In 2.4, only send back the residual information, don't flag this
- * as an error. Before 2.4 we had to flag this as an error because
- * the mid layer didn't check residual data counts to see if the
- * command needs retried.
- */
- scsi_set_resid(cmd, scb->sg_length - actual);
- aic7xxx_status(cmd) = hscb->target_status;
- }
- }
-
- /*
- * Clean out the residual information in the SCB for the
- * next consumer.
- */
- hscb->residual_data_count[2] = 0;
- hscb->residual_data_count[1] = 0;
- hscb->residual_data_count[0] = 0;
- hscb->residual_SG_segment_count = 0;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_handle_device_reset
- *
- * Description:
- * Interrupt handler for sequencer interrupts (SEQINT).
- *-F*************************************************************************/
-static void
-aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
-{
- unsigned char tindex = target;
-
- tindex |= ((channel & 0x01) << 3);
-
- /*
- * Go back to async/narrow transfers and renegotiate.
- */
- aic_outb(p, 0, TARG_SCSIRATE + tindex);
- if (p->features & AHC_ULTRA2)
- aic_outb(p, 0, TARG_OFFSET + tindex);
- aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
- target, -1);
- aic7xxx_run_done_queue(p, /*complete*/ TRUE);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_handle_seqint
- *
- * Description:
- * Interrupt handler for sequencer interrupts (SEQINT).
- *-F*************************************************************************/
-static void
-aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
-{
- struct aic7xxx_scb *scb;
- struct aic_dev_data *aic_dev;
- unsigned short target_mask;
- unsigned char target, lun, tindex;
- unsigned char queue_flag = FALSE;
- char channel;
- int result;
-
- target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
- if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
- channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
- else
- channel = 0;
- tindex = target + (channel << 3);
- lun = aic_inb(p, SAVED_TCL) & 0x07;
- target_mask = (0x01 << tindex);
-
- /*
- * Go ahead and clear the SEQINT now, that avoids any interrupt race
- * conditions later on in case we enable some other interrupt.
- */
- aic_outb(p, CLRSEQINT, CLRINT);
- switch (intstat & SEQINT_MASK)
- {
- case NO_MATCH:
- {
- aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
- SCSISEQ);
- printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
- "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
- printk(WARN_LEAD " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
- p->host_no, channel, target, lun,
- aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
- (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
- if (aic7xxx_panic_on_abort)
- aic7xxx_panic_abort(p, NULL);
- }
- break;
-
- case SEND_REJECT:
- {
- if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
- printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
- "target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
- aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
- }
- break;
-
- case NO_IDENT:
- {
- /*
- * The reconnecting target either did not send an identify
- * message, or did, but we didn't find an SCB to match and
- * before it could respond to our ATN/abort, it hit a dataphase.
- * The only safe thing to do is to blow it away with a bus
- * reset.
- */
- if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
- printk(INFO_LEAD "Target did not send an IDENTIFY message; "
- "LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
- lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
-
- aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
- aic7xxx_run_done_queue(p, TRUE);
-
- }
- break;
-
- case BAD_PHASE:
- if (aic_inb(p, LASTPHASE) == P_BUSFREE)
- {
- if (aic7xxx_verbose & VERBOSE_SEQINT)
- printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
- target, lun);
- restart_sequencer(p);
- }
- else
- {
- if (aic7xxx_verbose & VERBOSE_SEQINT)
- printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
- channel, target, lun);
- }
- break;
-
- case EXTENDED_MSG:
- {
- p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
- p->msg_len = 0;
- p->msg_index = 0;
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
- channel, target, lun);
-#endif
-
- /*
- * To actually receive the message, simply turn on
- * REQINIT interrupts and let our interrupt handler
- * do the rest (REQINIT should already be true).
- */
- p->flags |= AHC_HANDLING_REQINITS;
- aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
-
- /*
- * We don't want the sequencer unpaused yet so we return early
- */
- return;
- }
-
- case REJECT_MSG:
- {
- /*
- * What we care about here is if we had an outstanding SDTR
- * or WDTR message for this target. If we did, this is a
- * signal that the target is refusing negotiation.
- */
- unsigned char scb_index;
- unsigned char last_msg;
-
- scb_index = aic_inb(p, SCB_TAG);
- scb = p->scb_data->scb_array[scb_index];
- aic_dev = AIC_DEV(scb->cmd);
- last_msg = aic_inb(p, LAST_MSG);
-
- if ( (last_msg == MSG_IDENTIFYFLAG) &&
- (scb->tag_action) &&
- !(scb->flags & SCB_MSGOUT_BITS) )
- {
- if (scb->tag_action == MSG_ORDERED_Q_TAG)
- {
- /*
- * OK...the device seems able to accept tagged commands, but
- * not ordered tag commands, only simple tag commands. So, we
- * disable ordered tag commands and go on with life just like
- * normal.
- */
- scsi_adjust_queue_depth(scb->cmd->device, MSG_SIMPLE_TAG,
- scb->cmd->device->queue_depth);
- scb->tag_action = MSG_SIMPLE_Q_TAG;
- scb->hscb->control &= ~SCB_TAG_TYPE;
- scb->hscb->control |= MSG_SIMPLE_Q_TAG;
- aic_outb(p, scb->hscb->control, SCB_CONTROL);
- /*
- * OK..we set the tag type to simple tag command, now we re-assert
- * ATNO and hope this will take us into the identify phase again
- * so we can resend the tag type and info to the device.
- */
- aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
- }
- else if (scb->tag_action == MSG_SIMPLE_Q_TAG)
- {
- unsigned char i;
- struct aic7xxx_scb *scbp;
- int old_verbose;
- /*
- * Hmmmm....the device is flaking out on tagged commands.
- */
- scsi_adjust_queue_depth(scb->cmd->device, 0 /* untagged */,
- p->host->cmd_per_lun);
- aic_dev->max_q_depth = aic_dev->temp_q_depth = 1;
- /*
- * We set this command up as a bus device reset. However, we have
- * to clear the tag type as it's causing us problems. We shouldn't
- * have to worry about any other commands being active, since if
- * the device is refusing tagged commands, this should be the
- * first tagged command sent to the device, however, we do have
- * to worry about any other tagged commands that may already be
- * in the qinfifo. The easiest way to do this, is to issue a BDR,
- * send all the commands back to the mid level code, then let them
- * come back and get rebuilt as untagged commands.
- */
- scb->tag_action = 0;
- scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
- aic_outb(p, scb->hscb->control, SCB_CONTROL);
-
- old_verbose = aic7xxx_verbose;
- aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
- for (i=0; i < p->scb_data->numscbs; i++)
- {
- scbp = p->scb_data->scb_array[i];
- if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
- {
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
- {
- aic7xxx_reset_device(p, target, channel, lun, i);
- }
- }
- }
- aic7xxx_run_done_queue(p, TRUE);
- aic7xxx_verbose = old_verbose;
- /*
- * Wait until after the for loop to set the busy index since
- * aic7xxx_reset_device will clear the busy index during its
- * operation.
- */
- aic7xxx_busy_target(p, scb);
- printk(INFO_LEAD "Device is refusing tagged commands, using "
- "untagged I/O.\n", p->host_no, channel, target, lun);
- aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
- }
- }
- else if (scb->flags & SCB_MSGOUT_PPR)
- {
- /*
- * As per the draft specs, any device capable of supporting any of
- * the option values other than 0 are not allowed to reject the
- * PPR message. Instead, they must negotiate out what they do
- * support instead of rejecting our offering or else they cause
- * a parity error during msg_out phase to signal that they don't
- * like our settings.
- */
- aic_dev->needppr = aic_dev->needppr_copy = 0;
- aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
- (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE), aic_dev);
- aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
- aic_dev);
- aic_dev->goal.options = aic_dev->dtr_pending = 0;
- scb->flags &= ~SCB_MSGOUT_BITS;
- if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Device is rejecting PPR messages, falling "
- "back.\n", p->host_no, channel, target, lun);
- }
- if ( aic_dev->goal.width )
- {
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
- aic_dev->dtr_pending = 1;
- scb->flags |= SCB_MSGOUT_WDTR;
- }
- if ( aic_dev->goal.offset )
- {
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
- if( !aic_dev->dtr_pending )
- {
- aic_dev->dtr_pending = 1;
- scb->flags |= SCB_MSGOUT_SDTR;
- }
- }
- if ( aic_dev->dtr_pending )
- {
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
- }
- }
- else if (scb->flags & SCB_MSGOUT_WDTR)
- {
- /*
- * note 8bit xfers and clear flag
- */
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
- scb->flags &= ~SCB_MSGOUT_BITS;
- aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
- (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR), aic_dev);
- aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
- aic_dev);
- if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Device is rejecting WDTR messages, using "
- "narrow transfers.\n", p->host_no, channel, target, lun);
- }
- aic_dev->needsdtr = aic_dev->needsdtr_copy;
- }
- else if (scb->flags & SCB_MSGOUT_SDTR)
- {
- /*
- * note asynch xfers and clear flag
- */
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
- scb->flags &= ~SCB_MSGOUT_BITS;
- aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
- (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL), aic_dev);
- if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Device is rejecting SDTR messages, using "
- "async transfers.\n", p->host_no, channel, target, lun);
- }
- }
- else if (aic7xxx_verbose & VERBOSE_SEQINT)
- {
- /*
- * Otherwise, we ignore it.
- */
- printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause. "
- "Ignoring.\n", p->host_no, channel, target, lun);
- }
- }
- break;
-
- case BAD_STATUS:
- {
- unsigned char scb_index;
- struct aic7xxx_hwscb *hscb;
- struct scsi_cmnd *cmd;
-
- /* The sequencer will notify us when a command has an error that
- * would be of interest to the kernel. This allows us to leave
- * the sequencer running in the common case of command completes
- * without error. The sequencer will have DMA'd the SCB back
- * up to us, so we can reference the drivers SCB array.
- *
- * Set the default return value to 0 indicating not to send
- * sense. The sense code will change this if needed and this
- * reduces code duplication.
- */
- aic_outb(p, 0, RETURN_1);
- scb_index = aic_inb(p, SCB_TAG);
- if (scb_index > p->scb_data->numscbs)
- {
- printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
- p->host_no, channel, target, lun, intstat, scb_index);
- break;
- }
- scb = p->scb_data->scb_array[scb_index];
- hscb = scb->hscb;
-
- if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
- " cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
- scb_index, scb->flags, (unsigned long) scb->cmd);
- }
- else
- {
- cmd = scb->cmd;
- aic_dev = AIC_DEV(scb->cmd);
- hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
- aic7xxx_status(cmd) = hscb->target_status;
-
- cmd->result = hscb->target_status;
-
- switch (status_byte(hscb->target_status))
- {
- case GOOD:
- if (aic7xxx_verbose & VERBOSE_SEQINT)
- printk(INFO_LEAD "Interrupted for status of GOOD???\n",
- p->host_no, CTL_OF_SCB(scb));
- break;
-
- case COMMAND_TERMINATED:
- case CHECK_CONDITION:
- if ( !(scb->flags & SCB_SENSE) )
- {
- /*
- * Send a sense command to the requesting target.
- * XXX - revisit this and get rid of the memcopys.
- */
- memcpy(scb->sense_cmd, &generic_sense[0],
- sizeof(generic_sense));
-
- scb->sense_cmd[1] = (cmd->device->lun << 5);
- scb->sense_cmd[4] = SCSI_SENSE_BUFFERSIZE;
-
- scb->sg_list[0].length =
- cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
- scb->sg_list[0].address =
- cpu_to_le32(pci_map_single(p->pdev, cmd->sense_buffer,
- SCSI_SENSE_BUFFERSIZE,
- PCI_DMA_FROMDEVICE));
-
- /*
- * XXX - We should allow disconnection, but can't as it
- * might allow overlapped tagged commands.
- */
- /* hscb->control &= DISCENB; */
- hscb->control = 0;
- hscb->target_status = 0;
- hscb->SG_list_pointer =
- cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list));
- hscb->SCSI_cmd_pointer =
- cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd));
- hscb->data_count = scb->sg_list[0].length;
- hscb->data_pointer = scb->sg_list[0].address;
- hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
- hscb->residual_SG_segment_count = 0;
- hscb->residual_data_count[0] = 0;
- hscb->residual_data_count[1] = 0;
- hscb->residual_data_count[2] = 0;
-
- scb->sg_count = hscb->SG_segment_count = 1;
- scb->sg_length = SCSI_SENSE_BUFFERSIZE;
- scb->tag_action = 0;
- scb->flags |= SCB_SENSE;
- /*
- * Ensure the target is busy since this will be an
- * an untagged request.
- */
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- if (scb->flags & SCB_MSGOUT_BITS)
- printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
- CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
- "SDTR" : "WDTR");
- else
- printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
- CTL_OF_SCB(scb));
- }
-#endif
- aic7xxx_busy_target(p, scb);
- aic_outb(p, SEND_SENSE, RETURN_1);
- aic7xxx_error(cmd) = DID_OK;
- break;
- } /* first time sense, no errors */
- printk(INFO_LEAD "CHECK_CONDITION on REQUEST_SENSE, returning "
- "an error.\n", p->host_no, CTL_OF_SCB(scb));
- aic7xxx_error(cmd) = DID_ERROR;
- scb->flags &= ~SCB_SENSE;
- break;
-
- case QUEUE_FULL:
- queue_flag = TRUE; /* Mark that this is a QUEUE_FULL and */
- case BUSY: /* drop through to here */
- {
- struct aic7xxx_scb *next_scbp, *prev_scbp;
- unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
- /*
- * We have to look three places for queued commands:
- * 1: p->waiting_scbs queue
- * 2: QINFIFO
- * 3: WAITING_SCBS list on card (for commands that are started
- * but haven't yet made it to the device)
- *
- * Of special note here is that commands on 2 or 3 above will
- * have already been marked as active, while commands on 1 will
- * not. The aic7xxx_done() function will want to unmark them
- * from active, so any commands we pull off of 1 need to
- * up the active count.
- */
- next_scbp = p->waiting_scbs.head;
- while ( next_scbp != NULL )
- {
- prev_scbp = next_scbp;
- next_scbp = next_scbp->q_next;
- if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
- SCB_LIST_NULL) )
- {
- scbq_remove(&p->waiting_scbs, prev_scbp);
- scb->flags = SCB_QUEUED_FOR_DONE | SCB_QUEUE_FULL;
- p->activescbs++;
- aic_dev->active_cmds++;
- }
- }
- aic7xxx_search_qinfifo(p, target, channel, lun,
- SCB_LIST_NULL, SCB_QUEUED_FOR_DONE | SCB_QUEUE_FULL,
- FALSE, NULL);
- next_scbp = NULL;
- active_hscb = aic_inb(p, SCBPTR);
- prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
- next_hscb = aic_inb(p, WAITING_SCBH);
- while (next_hscb != SCB_LIST_NULL)
- {
- aic_outb(p, next_hscb, SCBPTR);
- scb_index = aic_inb(p, SCB_TAG);
- if (scb_index < p->scb_data->numscbs)
- {
- next_scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
- SCB_LIST_NULL) )
- {
- next_scbp->flags = SCB_QUEUED_FOR_DONE | SCB_QUEUE_FULL;
- next_hscb = aic_inb(p, SCB_NEXT);
- aic_outb(p, 0, SCB_CONTROL);
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic7xxx_add_curscb_to_free_list(p);
- if (prev_hscb == SCB_LIST_NULL)
- {
- /* We were first on the list,
- * so we kill the selection
- * hardware. Let the sequencer
- * re-init the hardware itself
- */
- aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
- aic_outb(p, CLRSELTIMEO, CLRSINT1);
- aic_outb(p, next_hscb, WAITING_SCBH);
- }
- else
- {
- aic_outb(p, prev_hscb, SCBPTR);
- aic_outb(p, next_hscb, SCB_NEXT);
- }
- }
- else
- {
- prev_hscb = next_hscb;
- next_hscb = aic_inb(p, SCB_NEXT);
- }
- } /* scb_index >= p->scb_data->numscbs */
- }
- aic_outb(p, active_hscb, SCBPTR);
- aic7xxx_run_done_queue(p, FALSE);
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if( (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ||
- (aic7xxx_verbose > 0xffff) )
- {
- if (queue_flag)
- printk(INFO_LEAD "Queue full received; queue depth %d, "
- "active %d\n", p->host_no, CTL_OF_SCB(scb),
- aic_dev->max_q_depth, aic_dev->active_cmds);
- else
- printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
- }
-#endif
- if (queue_flag)
- {
- int diff;
- result = scsi_track_queue_full(cmd->device,
- aic_dev->active_cmds);
- if ( result < 0 )
- {
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- printk(INFO_LEAD "Tagged Command Queueing disabled.\n",
- p->host_no, CTL_OF_SCB(scb));
- diff = aic_dev->max_q_depth - p->host->cmd_per_lun;
- aic_dev->temp_q_depth = 1;
- aic_dev->max_q_depth = 1;
- }
- else if ( result > 0 )
- {
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
- CTL_OF_SCB(scb), result);
- diff = aic_dev->max_q_depth - result;
- aic_dev->max_q_depth = result;
- /* temp_q_depth could have been dropped to 1 for an untagged
- * command that might be coming up */
- if(aic_dev->temp_q_depth > result)
- aic_dev->temp_q_depth = result;
- }
- /* We should free up the no unused SCB entries. But, that's
- * a difficult thing to do because we use a direct indexed
- * array, so we can't just take any entries and free them,
- * we *have* to free the ones at the end of the array, and
- * they very well could be in use right now, which means
- * in order to do this right, we have to add a delayed
- * freeing mechanism tied into the scb_free() code area.
- * We'll add that later.
- */
- }
- break;
- }
-
- default:
- if (aic7xxx_verbose & VERBOSE_SEQINT)
- printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
- CTL_OF_SCB(scb), scb->hscb->target_status);
- if (!aic7xxx_error(cmd))
- {
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
- break;
- } /* end switch */
- } /* end else of */
- }
- break;
-
- case AWAITING_MSG:
- {
- unsigned char scb_index, msg_out;
-
- scb_index = aic_inb(p, SCB_TAG);
- msg_out = aic_inb(p, MSG_OUT);
- scb = p->scb_data->scb_array[scb_index];
- aic_dev = AIC_DEV(scb->cmd);
- p->msg_index = p->msg_len = 0;
- /*
- * This SCB had a MK_MESSAGE set in its control byte informing
- * the sequencer that we wanted to send a special message to
- * this target.
- */
-
- if ( !(scb->flags & SCB_DEVICE_RESET) &&
- (msg_out == MSG_IDENTIFYFLAG) &&
- (scb->hscb->control & TAG_ENB) )
- {
- p->msg_buf[p->msg_index++] = scb->tag_action;
- p->msg_buf[p->msg_index++] = scb->hscb->tag;
- p->msg_len += 2;
- }
-
- if (scb->flags & SCB_DEVICE_RESET)
- {
- p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
- p->msg_len++;
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Bus device reset mailed.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
- else if (scb->flags & SCB_ABORT)
- {
- if (scb->tag_action)
- {
- p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
- }
- else
- {
- p->msg_buf[p->msg_index++] = MSG_ABORT;
- }
- p->msg_len++;
- if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
- printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
- CTL_OF_SCB(scb));
- }
- else if (scb->flags & SCB_MSGOUT_PPR)
- {
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
- p->host_no, CTL_OF_SCB(scb),
- aic_dev->goal.period,
- aic_dev->goal.offset,
- aic_dev->goal.width,
- aic_dev->goal.options);
- }
- aic7xxx_construct_ppr(p, scb);
- }
- else if (scb->flags & SCB_MSGOUT_WDTR)
- {
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
- CTL_OF_SCB(scb));
- }
- aic7xxx_construct_wdtr(p, aic_dev->goal.width);
- }
- else if (scb->flags & SCB_MSGOUT_SDTR)
- {
- unsigned int max_sync, period;
- unsigned char options = 0;
- /*
- * Now that the device is selected, use the bits in SBLKCTL and
- * SSTAT2 to determine the max sync rate for this device.
- */
- if (p->features & AHC_ULTRA2)
- {
- if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
- !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
- {
- max_sync = AHC_SYNCRATE_ULTRA2;
- }
- else
- {
- max_sync = AHC_SYNCRATE_ULTRA;
- }
- }
- else if (p->features & AHC_ULTRA)
- {
- max_sync = AHC_SYNCRATE_ULTRA;
- }
- else
- {
- max_sync = AHC_SYNCRATE_FAST;
- }
- period = aic_dev->goal.period;
- aic7xxx_find_syncrate(p, &period, max_sync, &options);
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
- CTL_OF_SCB(scb), period,
- aic_dev->goal.offset);
- }
- aic7xxx_construct_sdtr(p, period, aic_dev->goal.offset);
- }
- else
- {
- panic("aic7xxx: AWAITING_MSG for an SCB that does "
- "not have a waiting message.\n");
- }
- /*
- * We've set everything up to send our message, now to actually do
- * so we need to enable reqinit interrupts and let the interrupt
- * handler do the rest. We don't want to unpause the sequencer yet
- * though so we'll return early. We also have to make sure that
- * we clear the SEQINT *BEFORE* we set the REQINIT handler active
- * or else it's possible on VLB cards to lose the first REQINIT
- * interrupt. Edge triggered EISA cards could also lose this
- * interrupt, although PCI and level triggered cards should not
- * have this problem since they continually interrupt the kernel
- * until we take care of the situation.
- */
- scb->flags |= SCB_MSGOUT_SENT;
- p->msg_index = 0;
- p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
- p->flags |= AHC_HANDLING_REQINITS;
- aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
- return;
- }
- break;
-
- case DATA_OVERRUN:
- {
- unsigned char scb_index = aic_inb(p, SCB_TAG);
- unsigned char lastphase = aic_inb(p, LASTPHASE);
- unsigned int i;
-
- scb = (p->scb_data->scb_array[scb_index]);
- /*
- * XXX - What do we really want to do on an overrun? The
- * mid-level SCSI code should handle this, but for now,
- * we'll just indicate that the command should retried.
- * If we retrieved sense info on this target, then the
- * base SENSE info should have been saved prior to the
- * overrun error. In that case, we return DID_OK and let
- * the mid level code pick up on the sense info. Otherwise
- * we return DID_ERROR so the command will get retried.
- */
- if ( !(scb->flags & SCB_SENSE) )
- {
- printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
- p->host_no, CTL_OF_SCB(scb),
- (lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
- printk(KERN_WARNING " %s seen Data Phase. Length=%d, NumSGs=%d.\n",
- (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
- scb->sg_length, scb->sg_count);
- printk(KERN_WARNING " Raw SCSI Command: 0x");
- for (i = 0; i < scb->hscb->SCSI_cmd_length; i++)
- {
- printk("%02x ", scb->cmd->cmnd[i]);
- }
- printk("\n");
- if(aic7xxx_verbose > 0xffff)
- {
- for (i = 0; i < scb->sg_count; i++)
- {
- printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n",
- i,
- le32_to_cpu(scb->sg_list[i].address),
- le32_to_cpu(scb->sg_list[i].length) );
- }
- }
- aic7xxx_error(scb->cmd) = DID_ERROR;
- }
- else
- printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
- break;
-
- case WIDE_RESIDUE:
- {
- unsigned char resid_sgcnt, index;
- unsigned char scb_index = aic_inb(p, SCB_TAG);
- unsigned int cur_addr, resid_dcnt;
- unsigned int native_addr, native_length, sg_addr;
- int i;
-
- if(scb_index > p->scb_data->numscbs)
- {
- printk(WARN_LEAD "invalid scb_index during WIDE_RESIDUE.\n",
- p->host_no, -1, -1, -1);
- /*
- * XXX: Add error handling here
- */
- break;
- }
- scb = p->scb_data->scb_array[scb_index];
- if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(WARN_LEAD "invalid scb during WIDE_RESIDUE flags:0x%x "
- "scb->cmd:0x%lx\n", p->host_no, CTL_OF_SCB(scb),
- scb->flags, (unsigned long)scb->cmd);
- break;
- }
- if(aic7xxx_verbose & VERBOSE_MINOR_ERROR)
- printk(INFO_LEAD "Got WIDE_RESIDUE message, patching up data "
- "pointer.\n", p->host_no, CTL_OF_SCB(scb));
-
- /*
- * We have a valid scb to use on this WIDE_RESIDUE message, so
- * we need to walk the sg list looking for this particular sg
- * segment, then see if we happen to be at the very beginning of
- * the segment. If we are, then we have to back things up to
- * the previous segment. If not, then we simply need to remove
- * one byte from this segments address and add one to the byte
- * count.
- */
- cur_addr = aic_inb(p, SHADDR) | (aic_inb(p, SHADDR + 1) << 8) |
- (aic_inb(p, SHADDR + 2) << 16) | (aic_inb(p, SHADDR + 3) << 24);
- sg_addr = aic_inb(p, SG_COUNT + 1) | (aic_inb(p, SG_COUNT + 2) << 8) |
- (aic_inb(p, SG_COUNT + 3) << 16) | (aic_inb(p, SG_COUNT + 4) << 24);
- resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT);
- resid_dcnt = aic_inb(p, SCB_RESID_DCNT) |
- (aic_inb(p, SCB_RESID_DCNT + 1) << 8) |
- (aic_inb(p, SCB_RESID_DCNT + 2) << 16);
- index = scb->sg_count - ((resid_sgcnt) ? resid_sgcnt : 1);
- native_addr = le32_to_cpu(scb->sg_list[index].address);
- native_length = le32_to_cpu(scb->sg_list[index].length);
- /*
- * If resid_dcnt == native_length, then we just loaded this SG
- * segment and we need to back it up one...
- */
- if(resid_dcnt == native_length)
- {
- if(index == 0)
- {
- /*
- * Oops, this isn't right, we can't back up to before the
- * beginning. This must be a bogus message, ignore it.
- */
- break;
- }
- resid_dcnt = 1;
- resid_sgcnt += 1;
- native_addr = le32_to_cpu(scb->sg_list[index - 1].address);
- native_length = le32_to_cpu(scb->sg_list[index - 1].length);
- cur_addr = native_addr + (native_length - 1);
- sg_addr -= sizeof(struct hw_scatterlist);
- }
- else
- {
- /*
- * resid_dcnt != native_length, so we are in the middle of a SG
- * element. Back it up one byte and leave the rest alone.
- */
- resid_dcnt += 1;
- cur_addr -= 1;
- }
-
- /*
- * Output the new addresses and counts to the right places on the
- * card.
- */
- aic_outb(p, resid_sgcnt, SG_COUNT);
- aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT);
- aic_outb(p, sg_addr & 0xff, SG_COUNT + 1);
- aic_outb(p, (sg_addr >> 8) & 0xff, SG_COUNT + 2);
- aic_outb(p, (sg_addr >> 16) & 0xff, SG_COUNT + 3);
- aic_outb(p, (sg_addr >> 24) & 0xff, SG_COUNT + 4);
- aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT);
- aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1);
- aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2);
-
- /*
- * The sequencer actually wants to find the new address
- * in the SHADDR register set. On the Ultra2 and later controllers
- * this register set is readonly. In order to get the right number
- * into the register, you actually have to enter it in HADDR and then
- * use the PRELOADEN bit of DFCNTRL to drop it through from the
- * HADDR register to the SHADDR register. On non-Ultra2 controllers,
- * we simply write it direct.
- */
- if(p->features & AHC_ULTRA2)
- {
- /*
- * We might as well be accurate and drop both the resid_dcnt and
- * cur_addr into HCNT and HADDR and have both of them drop
- * through to the shadow layer together.
- */
- aic_outb(p, resid_dcnt & 0xff, HCNT);
- aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1);
- aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2);
- aic_outb(p, cur_addr & 0xff, HADDR);
- aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
- aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
- aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
- aic_outb(p, aic_inb(p, DMAPARAMS) | PRELOADEN, DFCNTRL);
- udelay(1);
- aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL);
- i=0;
- while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000))
- {
- udelay(1);
- }
- }
- else
- {
- aic_outb(p, cur_addr & 0xff, SHADDR);
- aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1);
- aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2);
- aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3);
- }
- }
- break;
-
- case SEQ_SG_FIXUP:
- {
- unsigned char scb_index, tmp;
- int sg_addr, sg_length;
-
- scb_index = aic_inb(p, SCB_TAG);
-
- if(scb_index > p->scb_data->numscbs)
- {
- printk(WARN_LEAD "invalid scb_index during SEQ_SG_FIXUP.\n",
- p->host_no, -1, -1, -1);
- printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
- "0x%x\n", p->host_no, -1, -1, -1,
- aic_inb(p, SCSISIGI),
- aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
- aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
- printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n",
- p->host_no, -1, -1, -1, aic_inb(p, SG_CACHEPTR),
- aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 |
- aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT));
- /*
- * XXX: Add error handling here
- */
- break;
- }
- scb = p->scb_data->scb_array[scb_index];
- if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(WARN_LEAD "invalid scb during SEQ_SG_FIXUP flags:0x%x "
- "scb->cmd:0x%p\n", p->host_no, CTL_OF_SCB(scb),
- scb->flags, scb->cmd);
- printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
- "0x%x\n", p->host_no, CTL_OF_SCB(scb),
- aic_inb(p, SCSISIGI),
- aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
- aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
- printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n",
- p->host_no, CTL_OF_SCB(scb), aic_inb(p, SG_CACHEPTR),
- aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 |
- aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT));
- break;
- }
- if(aic7xxx_verbose & VERBOSE_MINOR_ERROR)
- printk(INFO_LEAD "Fixing up SG address for sequencer.\n", p->host_no,
- CTL_OF_SCB(scb));
- /*
- * Advance the SG pointer to the next element in the list
- */
- tmp = aic_inb(p, SG_NEXT);
- tmp += SG_SIZEOF;
- aic_outb(p, tmp, SG_NEXT);
- if( tmp < SG_SIZEOF )
- aic_outb(p, aic_inb(p, SG_NEXT + 1) + 1, SG_NEXT + 1);
- tmp = aic_inb(p, SG_COUNT) - 1;
- aic_outb(p, tmp, SG_COUNT);
- sg_addr = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].address);
- sg_length = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].length);
- /*
- * Now stuff the element we just advanced past down onto the
- * card so it can be stored in the residual area.
- */
- aic_outb(p, sg_addr & 0xff, HADDR);
- aic_outb(p, (sg_addr >> 8) & 0xff, HADDR + 1);
- aic_outb(p, (sg_addr >> 16) & 0xff, HADDR + 2);
- aic_outb(p, (sg_addr >> 24) & 0xff, HADDR + 3);
- aic_outb(p, sg_length & 0xff, HCNT);
- aic_outb(p, (sg_length >> 8) & 0xff, HCNT + 1);
- aic_outb(p, (sg_length >> 16) & 0xff, HCNT + 2);
- aic_outb(p, (tmp << 2) | ((tmp == 1) ? LAST_SEG : 0), SG_CACHEPTR);
- aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
- while(aic_inb(p, SSTAT0) & SDONE) udelay(1);
- while(aic_inb(p, DFCNTRL) & (HDMAEN|SCSIEN)) aic_outb(p, 0, DFCNTRL);
- }
- break;
-
-#ifdef AIC7XXX_NOT_YET
- case TRACEPOINT2:
- {
- printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no,
- channel, target, lun);
- }
- break;
-
- /* XXX Fill these in later */
- case MSG_BUFFER_BUSY:
- printk("aic7xxx: Message buffer busy.\n");
- break;
- case MSGIN_PHASEMIS:
- printk("aic7xxx: Message-in phasemis.\n");
- break;
-#endif
-
- default: /* unknown */
- printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
- p->host_no, channel, target, lun, intstat,
- aic_inb(p, SCSISIGI));
- break;
- }
-
- /*
- * Clear the sequencer interrupt and unpause the sequencer.
- */
- unpause_sequencer(p, /* unpause always */ TRUE);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_parse_msg
- *
- * Description:
- * Parses incoming messages into actions on behalf of
- * aic7xxx_handle_reqinit
- *_F*************************************************************************/
-static int
-aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- int reject, reply, done;
- unsigned char target_scsirate, tindex;
- unsigned short target_mask;
- unsigned char target, channel, lun;
- unsigned char bus_width, new_bus_width;
- unsigned char trans_options, new_trans_options;
- unsigned int period, new_period, offset, new_offset, maxsync;
- struct aic7xxx_syncrate *syncrate;
- struct aic_dev_data *aic_dev;
-
- target = scb->cmd->device->id;
- channel = scb->cmd->device->channel;
- lun = scb->cmd->device->lun;
- reply = reject = done = FALSE;
- tindex = TARGET_INDEX(scb->cmd);
- aic_dev = AIC_DEV(scb->cmd);
- target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
- target_mask = (0x01 << tindex);
-
- /*
- * Parse as much of the message as is available,
- * rejecting it if we don't support it. When
- * the entire message is available and has been
- * handled, return TRUE indicating that we have
- * parsed an entire message.
- */
-
- if (p->msg_buf[0] != MSG_EXTENDED)
- {
- reject = TRUE;
- }
-
- /*
- * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when
- * using the SDTR messages. We need the PPR messages to enable the
- * higher speeds that include things like Dual Edge clocking.
- */
- if (p->features & AHC_ULTRA2)
- {
- if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
- !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
- {
- if (p->features & AHC_ULTRA3)
- maxsync = AHC_SYNCRATE_ULTRA3;
- else
- maxsync = AHC_SYNCRATE_ULTRA2;
- }
- else
- {
- maxsync = AHC_SYNCRATE_ULTRA;
- }
- }
- else if (p->features & AHC_ULTRA)
- {
- maxsync = AHC_SYNCRATE_ULTRA;
- }
- else
- {
- maxsync = AHC_SYNCRATE_FAST;
- }
-
- /*
- * Just accept the length byte outright and perform
- * more checking once we know the message type.
- */
-
- if ( !reject && (p->msg_len > 2) )
- {
- switch(p->msg_buf[2])
- {
- case MSG_EXT_SDTR:
- {
-
- if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
- {
- reject = TRUE;
- break;
- }
-
- if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
- {
- break;
- }
-
- period = new_period = p->msg_buf[3];
- offset = new_offset = p->msg_buf[4];
- trans_options = new_trans_options = 0;
- bus_width = new_bus_width = target_scsirate & WIDEXFER;
-
- /*
- * If our current max syncrate is in the Ultra3 range, bump it back
- * down to Ultra2 since we can't negotiate DT transfers using SDTR
- */
- if(maxsync == AHC_SYNCRATE_ULTRA3)
- maxsync = AHC_SYNCRATE_ULTRA2;
-
- /*
- * We might have a device that is starting negotiation with us
- * before we can start up negotiation with it....be prepared to
- * have a device ask for a higher speed then we want to give it
- * in that case
- */
- if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
- (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
- {
- if (!(aic_dev->flags & DEVICE_DTR_SCANNED))
- {
- /*
- * We shouldn't get here unless this is a narrow drive, wide
- * devices should trigger this same section of code in the WDTR
- * handler first instead.
- */
- aic_dev->goal.width = MSG_EXT_WDTR_BUS_8_BIT;
- aic_dev->goal.options = 0;
- if(p->user[tindex].offset)
- {
- aic_dev->needsdtr_copy = 1;
- aic_dev->goal.period = max_t(unsigned char, 10,p->user[tindex].period);
- if(p->features & AHC_ULTRA2)
- {
- aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
- }
- else
- {
- aic_dev->goal.offset = MAX_OFFSET_8BIT;
- }
- }
- else
- {
- aic_dev->needsdtr_copy = 0;
- aic_dev->goal.period = 255;
- aic_dev->goal.offset = 0;
- }
- aic_dev->flags |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
- }
- else if (aic_dev->needsdtr_copy == 0)
- {
- /*
- * This is a preemptive message from the target, we've already
- * scanned this target and set our options for it, and we
- * don't need a SDTR with this target (for whatever reason),
- * so reject this incoming SDTR
- */
- reject = TRUE;
- break;
- }
-
- /* The device is sending this message first and we have to reply */
- reply = TRUE;
-
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Received pre-emptive SDTR message from "
- "target.\n", p->host_no, CTL_OF_SCB(scb));
- }
- /*
- * Validate the values the device passed to us against our SEEPROM
- * settings. We don't have to do this if we aren't replying since
- * the device isn't allowed to send values greater than the ones
- * we first sent to it.
- */
- new_period = max_t(unsigned int, period, aic_dev->goal.period);
- new_offset = min_t(unsigned int, offset, aic_dev->goal.offset);
- }
-
- /*
- * Use our new_period, new_offset, bus_width, and card options
- * to determine the actual syncrate settings
- */
- syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
- &trans_options);
- aic7xxx_validate_offset(p, syncrate, &new_offset, bus_width);
-
- /*
- * Did we drop to async? If so, send a reply regardless of whether
- * or not we initiated this negotiation.
- */
- if ((new_offset == 0) && (new_offset != offset))
- {
- aic_dev->needsdtr_copy = 0;
- reply = TRUE;
- }
-
- /*
- * Did we start this, if not, or if we went too low and had to
- * go async, then send an SDTR back to the target
- */
- if(reply)
- {
- /* when sending a reply, make sure that the goal settings are
- * updated along with current and active since the code that
- * will actually build the message for the sequencer uses the
- * goal settings as its guidelines.
- */
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, trans_options,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
- aic_dev);
- scb->flags &= ~SCB_MSGOUT_BITS;
- scb->flags |= SCB_MSGOUT_SDTR;
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
- }
- else
- {
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, trans_options,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
- aic_dev->needsdtr = 0;
- }
- done = TRUE;
- break;
- }
- case MSG_EXT_WDTR:
- {
-
- if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
- {
- reject = TRUE;
- break;
- }
-
- if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
- {
- break;
- }
-
- bus_width = new_bus_width = p->msg_buf[3];
-
- if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
- (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
- {
- switch(bus_width)
- {
- default:
- {
- reject = TRUE;
- if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- ((aic_dev->flags & DEVICE_PRINT_DTR) ||
- (aic7xxx_verbose > 0xffff)) )
- {
- printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
- p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
- }
- } /* We fall through on purpose */
- case MSG_EXT_WDTR_BUS_8_BIT:
- {
- aic_dev->goal.width = MSG_EXT_WDTR_BUS_8_BIT;
- aic_dev->needwdtr_copy &= ~target_mask;
- break;
- }
- case MSG_EXT_WDTR_BUS_16_BIT:
- {
- break;
- }
- }
- aic_dev->needwdtr = 0;
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
- }
- else
- {
- if ( !(aic_dev->flags & DEVICE_DTR_SCANNED) )
- {
- /*
- * Well, we now know the WDTR and SYNC caps of this device since
- * it contacted us first, mark it as such and copy the user stuff
- * over to the goal stuff.
- */
- if( (p->features & AHC_WIDE) && p->user[tindex].width )
- {
- aic_dev->goal.width = MSG_EXT_WDTR_BUS_16_BIT;
- aic_dev->needwdtr_copy = 1;
- }
-
- /*
- * Devices that support DT transfers don't start WDTR requests
- */
- aic_dev->goal.options = 0;
-
- if(p->user[tindex].offset)
- {
- aic_dev->needsdtr_copy = 1;
- aic_dev->goal.period = max_t(unsigned char, 10, p->user[tindex].period);
- if(p->features & AHC_ULTRA2)
- {
- aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
- }
- else if( aic_dev->goal.width )
- {
- aic_dev->goal.offset = MAX_OFFSET_16BIT;
- }
- else
- {
- aic_dev->goal.offset = MAX_OFFSET_8BIT;
- }
- } else {
- aic_dev->needsdtr_copy = 0;
- aic_dev->goal.period = 255;
- aic_dev->goal.offset = 0;
- }
-
- aic_dev->flags |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
- }
- else if (aic_dev->needwdtr_copy == 0)
- {
- /*
- * This is a preemptive message from the target, we've already
- * scanned this target and set our options for it, and we
- * don't need a WDTR with this target (for whatever reason),
- * so reject this incoming WDTR
- */
- reject = TRUE;
- break;
- }
-
- /* The device is sending this message first and we have to reply */
- reply = TRUE;
-
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Received pre-emptive WDTR message from "
- "target.\n", p->host_no, CTL_OF_SCB(scb));
- }
- switch(bus_width)
- {
- case MSG_EXT_WDTR_BUS_16_BIT:
- {
- if ( (p->features & AHC_WIDE) &&
- (aic_dev->goal.width == MSG_EXT_WDTR_BUS_16_BIT) )
- {
- new_bus_width = MSG_EXT_WDTR_BUS_16_BIT;
- break;
- }
- } /* Fall through if we aren't a wide card */
- default:
- case MSG_EXT_WDTR_BUS_8_BIT:
- {
- aic_dev->needwdtr_copy = 0;
- new_bus_width = MSG_EXT_WDTR_BUS_8_BIT;
- break;
- }
- }
- scb->flags &= ~SCB_MSGOUT_BITS;
- scb->flags |= SCB_MSGOUT_WDTR;
- aic_dev->needwdtr = 0;
- if(aic_dev->dtr_pending == 0)
- {
- /* there is no other command with SCB_DTR_SCB already set that will
- * trigger the release of the dtr_pending bit. Both set the bit
- * and set scb->flags |= SCB_DTR_SCB
- */
- aic_dev->dtr_pending = 1;
- scb->flags |= SCB_DTR_SCB;
- }
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
- /* when sending a reply, make sure that the goal settings are
- * updated along with current and active since the code that
- * will actually build the message for the sequencer uses the
- * goal settings as its guidelines.
- */
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
- aic_dev);
- }
-
- /*
- * By virtue of the SCSI spec, a WDTR message negates any existing
- * SDTR negotiations. So, even if needsdtr isn't marked for this
- * device, we still have to do a new SDTR message if the device
- * supports SDTR at all. Therefore, we check needsdtr_copy instead
- * of needstr.
- */
- aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
- aic_dev);
- aic_dev->needsdtr = aic_dev->needsdtr_copy;
- done = TRUE;
- break;
- }
- case MSG_EXT_PPR:
- {
-
- if (p->msg_buf[1] != MSG_EXT_PPR_LEN)
- {
- reject = TRUE;
- break;
- }
-
- if (p->msg_len < (MSG_EXT_PPR_LEN + 2))
- {
- break;
- }
-
- period = new_period = p->msg_buf[3];
- offset = new_offset = p->msg_buf[5];
- bus_width = new_bus_width = p->msg_buf[6];
- trans_options = new_trans_options = p->msg_buf[7] & 0xf;
-
- if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Parsing PPR message (%d/%d/%d/%d)\n",
- p->host_no, CTL_OF_SCB(scb), period, offset, bus_width,
- trans_options);
- }
-
- /*
- * We might have a device that is starting negotiation with us
- * before we can start up negotiation with it....be prepared to
- * have a device ask for a higher speed then we want to give it
- * in that case
- */
- if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
- (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) )
- {
- /* Have we scanned the device yet? */
- if (!(aic_dev->flags & DEVICE_DTR_SCANNED))
- {
- /* The device is electing to use PPR messages, so we will too until
- * we know better */
- aic_dev->needppr = aic_dev->needppr_copy = 1;
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
-
- /* We know the device is SCSI-3 compliant due to PPR */
- aic_dev->flags |= DEVICE_SCSI_3;
-
- /*
- * Not only is the device starting this up, but it also hasn't
- * been scanned yet, so this would likely be our TUR or our
- * INQUIRY command at scan time, so we need to use the
- * settings from the SEEPROM if they existed. Of course, even
- * if we didn't find a SEEPROM, we stuffed default values into
- * the user settings anyway, so use those in all cases.
- */
- aic_dev->goal.width = p->user[tindex].width;
- if(p->user[tindex].offset)
- {
- aic_dev->goal.period = p->user[tindex].period;
- aic_dev->goal.options = p->user[tindex].options;
- if(p->features & AHC_ULTRA2)
- {
- aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
- }
- else if( aic_dev->goal.width &&
- (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
- p->features & AHC_WIDE )
- {
- aic_dev->goal.offset = MAX_OFFSET_16BIT;
- }
- else
- {
- aic_dev->goal.offset = MAX_OFFSET_8BIT;
- }
- }
- else
- {
- aic_dev->goal.period = 255;
- aic_dev->goal.offset = 0;
- aic_dev->goal.options = 0;
- }
- aic_dev->flags |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
- }
- else if (aic_dev->needppr_copy == 0)
- {
- /*
- * This is a preemptive message from the target, we've already
- * scanned this target and set our options for it, and we
- * don't need a PPR with this target (for whatever reason),
- * so reject this incoming PPR
- */
- reject = TRUE;
- break;
- }
-
- /* The device is sending this message first and we have to reply */
- reply = TRUE;
-
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Received pre-emptive PPR message from "
- "target.\n", p->host_no, CTL_OF_SCB(scb));
- }
-
- }
-
- switch(bus_width)
- {
- case MSG_EXT_WDTR_BUS_16_BIT:
- {
- if ( (aic_dev->goal.width == MSG_EXT_WDTR_BUS_16_BIT) &&
- p->features & AHC_WIDE)
- {
- break;
- }
- }
- default:
- {
- if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- ((aic_dev->flags & DEVICE_PRINT_DTR) ||
- (aic7xxx_verbose > 0xffff)) )
- {
- reply = TRUE;
- printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
- p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
- }
- } /* We fall through on purpose */
- case MSG_EXT_WDTR_BUS_8_BIT:
- {
- /*
- * According to the spec, if we aren't wide, we also can't be
- * Dual Edge so clear the options byte
- */
- new_trans_options = 0;
- new_bus_width = MSG_EXT_WDTR_BUS_8_BIT;
- break;
- }
- }
-
- if(reply)
- {
- /* when sending a reply, make sure that the goal settings are
- * updated along with current and active since the code that
- * will actually build the message for the sequencer uses the
- * goal settings as its guidelines.
- */
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
- aic_dev);
- syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
- &new_trans_options);
- aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width);
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, new_trans_options,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
- aic_dev);
- }
- else
- {
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
- syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
- &new_trans_options);
- aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width);
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, new_trans_options,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
- }
-
- /*
- * As it turns out, if we don't *have* to have PPR messages, then
- * configure ourselves not to use them since that makes some
- * external drive chassis work (those chassis can't parse PPR
- * messages and they mangle the SCSI bus until you send a WDTR
- * and SDTR that they can understand).
- */
- if(new_trans_options == 0)
- {
- aic_dev->needppr = aic_dev->needppr_copy = 0;
- if(new_offset)
- {
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
- }
- if (new_bus_width)
- {
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
- }
- }
-
- if((new_offset == 0) && (offset != 0))
- {
- /*
- * Oops, the syncrate went to low for this card and we fell off
- * to async (should never happen with a device that uses PPR
- * messages, but have to be complete)
- */
- reply = TRUE;
- }
-
- if(reply)
- {
- scb->flags &= ~SCB_MSGOUT_BITS;
- scb->flags |= SCB_MSGOUT_PPR;
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
- }
- else
- {
- aic_dev->needppr = 0;
- }
- done = TRUE;
- break;
- }
- default:
- {
- reject = TRUE;
- break;
- }
- } /* end of switch(p->msg_type) */
- } /* end of if (!reject && (p->msg_len > 2)) */
-
- if (!reply && reject)
- {
- aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
- done = TRUE;
- }
- return(done);
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_handle_reqinit
- *
- * Description:
- * Interrupt handler for REQINIT interrupts (used to transfer messages to
- * and from devices).
- *_F*************************************************************************/
-static void
-aic7xxx_handle_reqinit(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- unsigned char lastbyte;
- unsigned char phasemis;
- int done = FALSE;
-
- switch(p->msg_type)
- {
- case MSG_TYPE_INITIATOR_MSGOUT:
- {
- if (p->msg_len == 0)
- panic("aic7xxx: REQINIT with no active message!\n");
-
- lastbyte = (p->msg_index == (p->msg_len - 1));
- phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK) != P_MESGOUT;
-
- if (lastbyte || phasemis)
- {
- /* Time to end the message */
- p->msg_len = 0;
- p->msg_type = MSG_TYPE_NONE;
- /*
- * NOTE-TO-MYSELF: If you clear the REQINIT after you
- * disable REQINITs, then cases of REJECT_MSG stop working
- * and hang the bus
- */
- aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- p->flags &= ~AHC_HANDLING_REQINITS;
-
- if (phasemis == 0)
- {
- aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
- aic_outb(p, 0, RETURN_1);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- printk(INFO_LEAD "Completed sending of REQINIT message.\n",
- p->host_no, CTL_OF_SCB(scb));
-#endif
- }
- else
- {
- aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n",
- p->host_no, CTL_OF_SCB(scb));
-#endif
- }
- unpause_sequencer(p, TRUE);
- }
- else
- {
- /*
- * Present the byte on the bus (clearing REQINIT) but don't
- * unpause the sequencer.
- */
- aic_outb(p, CLRREQINIT, CLRSINT1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- aic_outb(p, p->msg_buf[p->msg_index++], SCSIDATL);
- }
- break;
- }
- case MSG_TYPE_INITIATOR_MSGIN:
- {
- phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK ) != P_MESGIN;
-
- if (phasemis == 0)
- {
- p->msg_len++;
- /* Pull the byte in without acking it */
- p->msg_buf[p->msg_index] = aic_inb(p, SCSIBUSL);
- done = aic7xxx_parse_msg(p, scb);
- /* Ack the byte */
- aic_outb(p, CLRREQINIT, CLRSINT1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- aic_inb(p, SCSIDATL);
- p->msg_index++;
- }
- if (phasemis || done)
- {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- {
- if (phasemis)
- printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n",
- p->host_no, CTL_OF_SCB(scb));
- else
- printk(INFO_LEAD "Completed receipt of REQINIT message.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
-#endif
- /* Time to end our message session */
- p->msg_len = 0;
- p->msg_type = MSG_TYPE_NONE;
- aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- p->flags &= ~AHC_HANDLING_REQINITS;
- unpause_sequencer(p, TRUE);
- }
- break;
- }
- default:
- {
- panic("aic7xxx: Unknown REQINIT message type.\n");
- break;
- }
- } /* End of switch(p->msg_type) */
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_handle_scsiint
- *
- * Description:
- * Interrupt handler for SCSI interrupts (SCSIINT).
- *-F*************************************************************************/
-static void
-aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
-{
- unsigned char scb_index;
- unsigned char status;
- struct aic7xxx_scb *scb;
- struct aic_dev_data *aic_dev;
-
- scb_index = aic_inb(p, SCB_TAG);
- status = aic_inb(p, SSTAT1);
-
- if (scb_index < p->scb_data->numscbs)
- {
- scb = p->scb_data->scb_array[scb_index];
- if ((scb->flags & SCB_ACTIVE) == 0)
- {
- scb = NULL;
- }
- }
- else
- {
- scb = NULL;
- }
-
-
- if ((status & SCSIRSTI) != 0)
- {
- int channel;
-
- if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
- channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
- else
- channel = 0;
-
- if (aic7xxx_verbose & VERBOSE_RESET)
- printk(WARN_LEAD "Someone else reset the channel!!\n",
- p->host_no, channel, -1, -1);
- if (aic7xxx_panic_on_abort)
- aic7xxx_panic_abort(p, NULL);
- /*
- * Go through and abort all commands for the channel, but do not
- * reset the channel again.
- */
- aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
- aic7xxx_run_done_queue(p, TRUE);
- scb = NULL;
- }
- else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
- {
- /*
- * First look at what phase we were last in. If it's message-out,
- * chances are pretty good that the bus free was in response to
- * one of our abort requests.
- */
- unsigned char lastphase = aic_inb(p, LASTPHASE);
- unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
- unsigned char target = (saved_tcl >> 4) & 0x0F;
- int channel;
- int printerror = TRUE;
-
- if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
- channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
- else
- channel = 0;
-
- aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
- SCSISEQ);
- if (lastphase == P_MESGOUT)
- {
- unsigned char message;
-
- message = aic_inb(p, SINDEX);
-
- if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
- {
- if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
- printk(INFO_LEAD "SCB %d abort delivered.\n", p->host_no,
- CTL_OF_SCB(scb), scb->hscb->tag);
- aic7xxx_reset_device(p, target, channel, ALL_LUNS,
- (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
- aic7xxx_run_done_queue(p, TRUE);
- scb = NULL;
- printerror = 0;
- }
- else if (message == MSG_BUS_DEV_RESET)
- {
- aic7xxx_handle_device_reset(p, target, channel);
- scb = NULL;
- printerror = 0;
- }
- }
- if ( (scb != NULL) && (scb->flags & SCB_DTR_SCB) )
- {
- /*
- * Hmmm...error during a negotiation command. Either we have a
- * borken bus, or the device doesn't like our negotiation message.
- * Since we check the INQUIRY data of a device before sending it
- * negotiation messages, assume the bus is borken for whatever
- * reason. Complete the command.
- */
- printerror = 0;
- aic7xxx_reset_device(p, target, channel, ALL_LUNS, scb->hscb->tag);
- aic7xxx_run_done_queue(p, TRUE);
- scb = NULL;
- }
- if (printerror != 0)
- {
- if (scb != NULL)
- {
- unsigned char tag;
-
- if ((scb->hscb->control & TAG_ENB) != 0)
- {
- tag = scb->hscb->tag;
- }
- else
- {
- tag = SCB_LIST_NULL;
- }
- aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
- aic7xxx_run_done_queue(p, TRUE);
- }
- else
- {
- aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
- aic7xxx_run_done_queue(p, TRUE);
- }
- printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
- "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
- (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
- scb = NULL;
- }
- aic_outb(p, MSG_NOOP, MSG_OUT);
- aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
- SIMODE1);
- p->flags &= ~AHC_HANDLING_REQINITS;
- aic_outb(p, CLRBUSFREE, CLRSINT1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- restart_sequencer(p);
- unpause_sequencer(p, TRUE);
- }
- else if ((status & SELTO) != 0)
- {
- unsigned char scbptr;
- unsigned char nextscb;
- struct scsi_cmnd *cmd;
-
- scbptr = aic_inb(p, WAITING_SCBH);
- if (scbptr > p->scb_data->maxhscbs)
- {
- /*
- * I'm still trying to track down exactly how this happens, but until
- * I find it, this code will make sure we aren't passing bogus values
- * into the SCBPTR register, even if that register will just wrap
- * things around, we still don't like having out of range variables.
- *
- * NOTE: Don't check the aic7xxx_verbose variable, I want this message
- * to always be displayed.
- */
- printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n",
- p->host_no, -1, -1, -1, scbptr);
- if (p->scb_data->maxhscbs > 4)
- scbptr &= (p->scb_data->maxhscbs - 1);
- else
- scbptr &= 0x03;
- }
- aic_outb(p, scbptr, SCBPTR);
- scb_index = aic_inb(p, SCB_TAG);
-
- scb = NULL;
- if (scb_index < p->scb_data->numscbs)
- {
- scb = p->scb_data->scb_array[scb_index];
- if ((scb->flags & SCB_ACTIVE) == 0)
- {
- scb = NULL;
- }
- }
- if (scb == NULL)
- {
- printk(WARN_LEAD "Referenced SCB %d not valid during SELTO.\n",
- p->host_no, -1, -1, -1, scb_index);
- printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
- "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
- aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
- aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
- if (aic7xxx_panic_on_abort)
- aic7xxx_panic_abort(p, NULL);
- }
- else
- {
- cmd = scb->cmd;
- cmd->result = (DID_TIME_OUT << 16);
-
- /*
- * Clear out this hardware SCB
- */
- aic_outb(p, 0, SCB_CONTROL);
-
- /*
- * Clear out a few values in the card that are in an undetermined
- * state.
- */
- aic_outb(p, MSG_NOOP, MSG_OUT);
-
- /*
- * Shift the waiting for selection queue forward
- */
- nextscb = aic_inb(p, SCB_NEXT);
- aic_outb(p, nextscb, WAITING_SCBH);
-
- /*
- * Put this SCB back on the free list.
- */
- aic7xxx_add_curscb_to_free_list(p);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
-#endif
- if (scb->flags & SCB_QUEUED_ABORT)
- {
- /*
- * We know that this particular SCB had to be the queued abort since
- * the disconnected SCB would have gotten a reconnect instead.
- * What we need to do then is to let the command timeout again so
- * we get a reset since this abort just failed.
- */
- cmd->result = 0;
- scb = NULL;
- }
- }
- /*
- * Keep the sequencer from trying to restart any selections
- */
- aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
- /*
- * Make sure the data bits on the bus are released
- * Don't do this on 7770 chipsets, it makes them give us
- * a BRKADDRINT and kills the card.
- */
- if( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
- aic_outb(p, 0, SCSIBUSL);
-
- /*
- * Delay for the selection timeout delay period then stop the selection
- */
- udelay(301);
- aic_outb(p, CLRSELINGO, CLRSINT0);
- /*
- * Clear out all the interrupt status bits
- */
- aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
- p->flags &= ~AHC_HANDLING_REQINITS;
- aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- /*
- * Restarting the sequencer will stop the selection and make sure devices
- * are allowed to reselect in.
- */
- restart_sequencer(p);
- unpause_sequencer(p, TRUE);
- }
- else if (scb == NULL)
- {
- printk(WARN_LEAD "aic7xxx_isr - referenced scb not valid "
- "during scsiint 0x%x scb(%d)\n"
- " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
- p->host_no, -1, -1, -1, status, scb_index, aic_inb(p, SIMODE0),
- aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
- (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
- /*
- * Turn off the interrupt and set status to zero, so that it
- * falls through the rest of the SCSIINT code.
- */
- aic_outb(p, status, CLRSINT1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- unpause_sequencer(p, /* unpause always */ TRUE);
- scb = NULL;
- }
- else if (status & SCSIPERR)
- {
- /*
- * Determine the bus phase and queue an appropriate message.
- */
- char *phase;
- struct scsi_cmnd *cmd;
- unsigned char mesg_out = MSG_NOOP;
- unsigned char lastphase = aic_inb(p, LASTPHASE);
- unsigned char sstat2 = aic_inb(p, SSTAT2);
-
- cmd = scb->cmd;
- switch (lastphase)
- {
- case P_DATAOUT:
- phase = "Data-Out";
- break;
- case P_DATAIN:
- phase = "Data-In";
- mesg_out = MSG_INITIATOR_DET_ERR;
- break;
- case P_COMMAND:
- phase = "Command";
- break;
- case P_MESGOUT:
- phase = "Message-Out";
- break;
- case P_STATUS:
- phase = "Status";
- mesg_out = MSG_INITIATOR_DET_ERR;
- break;
- case P_MESGIN:
- phase = "Message-In";
- mesg_out = MSG_PARITY_ERROR;
- break;
- default:
- phase = "unknown";
- break;
- }
-
- /*
- * A parity error has occurred during a data
- * transfer phase. Flag it and continue.
- */
- if( (p->features & AHC_ULTRA3) &&
- (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) &&
- (lastphase == P_DATAIN) )
- {
- printk(WARN_LEAD "CRC error during %s phase.\n",
- p->host_no, CTL_OF_SCB(scb), phase);
- if(sstat2 & CRCVALERR)
- {
- printk(WARN_LEAD " CRC error in intermediate CRC packet.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
- if(sstat2 & CRCENDERR)
- {
- printk(WARN_LEAD " CRC error in ending CRC packet.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
- if(sstat2 & CRCREQERR)
- {
- printk(WARN_LEAD " Target incorrectly requested a CRC packet.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
- if(sstat2 & DUAL_EDGE_ERROR)
- {
- printk(WARN_LEAD " Dual Edge transmission error.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
- }
- else if( (lastphase == P_MESGOUT) &&
- (scb->flags & SCB_MSGOUT_PPR) )
- {
- /*
- * As per the draft specs, any device capable of supporting any of
- * the option values other than 0 are not allowed to reject the
- * PPR message. Instead, they must negotiate out what they do
- * support instead of rejecting our offering or else they cause
- * a parity error during msg_out phase to signal that they don't
- * like our settings.
- */
- aic_dev = AIC_DEV(scb->cmd);
- aic_dev->needppr = aic_dev->needppr_copy = 0;
- aic7xxx_set_width(p, scb->cmd->device->id, scb->cmd->device->channel, scb->cmd->device->lun,
- MSG_EXT_WDTR_BUS_8_BIT,
- (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE),
- aic_dev);
- aic7xxx_set_syncrate(p, NULL, scb->cmd->device->id, scb->cmd->device->channel, 0, 0,
- 0, AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
- aic_dev);
- aic_dev->goal.options = 0;
- scb->flags &= ~SCB_MSGOUT_BITS;
- if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "parity error during PPR message, reverting "
- "to WDTR/SDTR\n", p->host_no, CTL_OF_SCB(scb));
- }
- if ( aic_dev->goal.width )
- {
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
- }
- if ( aic_dev->goal.offset )
- {
- if( aic_dev->goal.period <= 9 )
- {
- aic_dev->goal.period = 10;
- }
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
- }
- scb = NULL;
- }
-
- /*
- * We've set the hardware to assert ATN if we get a parity
- * error on "in" phases, so all we need to do is stuff the
- * message buffer with the appropriate message. "In" phases
- * have set mesg_out to something other than MSG_NOP.
- */
- if (mesg_out != MSG_NOOP)
- {
- aic_outb(p, mesg_out, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
- scb = NULL;
- }
- aic_outb(p, CLRSCSIPERR, CLRSINT1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- unpause_sequencer(p, /* unpause_always */ TRUE);
- }
- else if ( (status & REQINIT) &&
- (p->flags & AHC_HANDLING_REQINITS) )
- {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no,
- CTL_OF_SCB(scb), aic_inb(p, SSTAT1));
-#endif
- aic7xxx_handle_reqinit(p, scb);
- return;
- }
- else
- {
- /*
- * We don't know what's going on. Turn off the
- * interrupt source and try to continue.
- */
- if (aic7xxx_verbose & VERBOSE_SCSIINT)
- printk(INFO_LEAD "Unknown SCSIINT status, SSTAT1(0x%x).\n",
- p->host_no, -1, -1, -1, status);
- aic_outb(p, status, CLRSINT1);
- aic_outb(p, CLRSCSIINT, CLRINT);
- unpause_sequencer(p, /* unpause always */ TRUE);
- scb = NULL;
- }
- if (scb != NULL)
- {
- aic7xxx_done(p, scb);
- }
-}
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-static void
-aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
-{
- unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp;
- int i, bogus, lost;
- static unsigned char scb_status[AIC7XXX_MAXSCB];
-
-#define SCB_NO_LIST 0
-#define SCB_FREE_LIST 1
-#define SCB_WAITING_LIST 2
-#define SCB_DISCONNECTED_LIST 4
-#define SCB_CURRENTLY_ACTIVE 8
-
- /*
- * Note, these checks will fail on a regular basis once the machine moves
- * beyond the bus scan phase. The problem is race conditions concerning
- * the scbs and where they are linked in. When you have 30 or so commands
- * outstanding on the bus, and run this twice with every interrupt, the
- * chances get pretty good that you'll catch the sequencer with an SCB
- * only partially linked in. Therefore, once we pass the scan phase
- * of the bus, we really should disable this function.
- */
- bogus = FALSE;
- memset(&scb_status[0], 0, sizeof(scb_status));
- pause_sequencer(p);
- saved_scbptr = aic_inb(p, SCBPTR);
- if (saved_scbptr >= p->scb_data->maxhscbs)
- {
- printk("Bogus SCBPTR %d\n", saved_scbptr);
- bogus = TRUE;
- }
- scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE;
- free_scbh = aic_inb(p, FREE_SCBH);
- if ( (free_scbh != SCB_LIST_NULL) &&
- (free_scbh >= p->scb_data->maxhscbs) )
- {
- printk("Bogus FREE_SCBH %d\n", free_scbh);
- bogus = TRUE;
- }
- else
- {
- temp = free_scbh;
- while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
- {
- if(scb_status[temp] & 0x07)
- {
- printk("HSCB %d on multiple lists, status 0x%02x", temp,
- scb_status[temp] | SCB_FREE_LIST);
- bogus = TRUE;
- }
- scb_status[temp] |= SCB_FREE_LIST;
- aic_outb(p, temp, SCBPTR);
- temp = aic_inb(p, SCB_NEXT);
- }
- }
-
- dis_scbh = aic_inb(p, DISCONNECTED_SCBH);
- if ( (dis_scbh != SCB_LIST_NULL) &&
- (dis_scbh >= p->scb_data->maxhscbs) )
- {
- printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh);
- bogus = TRUE;
- }
- else
- {
- temp = dis_scbh;
- while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
- {
- if(scb_status[temp] & 0x07)
- {
- printk("HSCB %d on multiple lists, status 0x%02x", temp,
- scb_status[temp] | SCB_DISCONNECTED_LIST);
- bogus = TRUE;
- }
- scb_status[temp] |= SCB_DISCONNECTED_LIST;
- aic_outb(p, temp, SCBPTR);
- temp = aic_inb(p, SCB_NEXT);
- }
- }
-
- wait_scbh = aic_inb(p, WAITING_SCBH);
- if ( (wait_scbh != SCB_LIST_NULL) &&
- (wait_scbh >= p->scb_data->maxhscbs) )
- {
- printk("Bogus WAITING_SCBH %d\n", wait_scbh);
- bogus = TRUE;
- }
- else
- {
- temp = wait_scbh;
- while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
- {
- if(scb_status[temp] & 0x07)
- {
- printk("HSCB %d on multiple lists, status 0x%02x", temp,
- scb_status[temp] | SCB_WAITING_LIST);
- bogus = TRUE;
- }
- scb_status[temp] |= SCB_WAITING_LIST;
- aic_outb(p, temp, SCBPTR);
- temp = aic_inb(p, SCB_NEXT);
- }
- }
-
- lost=0;
- for(i=0; i < p->scb_data->maxhscbs; i++)
- {
- aic_outb(p, i, SCBPTR);
- temp = aic_inb(p, SCB_NEXT);
- if ( ((temp != SCB_LIST_NULL) &&
- (temp >= p->scb_data->maxhscbs)) )
- {
- printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp);
- bogus = TRUE;
- }
- if ( temp == i )
- {
- printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
- bogus = TRUE;
- }
- if (scb_status[i] == 0)
- lost++;
- if (lost > 1)
- {
- printk("Too many lost scbs.\n");
- bogus=TRUE;
- }
- }
- aic_outb(p, saved_scbptr, SCBPTR);
- unpause_sequencer(p, FALSE);
- if (bogus)
- {
- printk("Bogus parameters found in card SCB array structures.\n");
- printk("%s\n", buffer);
- aic7xxx_panic_abort(p, NULL);
- }
- return;
-}
-#endif
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_handle_command_completion_intr
- *
- * Description:
- * SCSI command completion interrupt handler.
- *-F*************************************************************************/
-static void
-aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p)
-{
- struct aic7xxx_scb *scb = NULL;
- struct aic_dev_data *aic_dev;
- struct scsi_cmnd *cmd;
- unsigned char scb_index, tindex;
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) )
- printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
-#endif
-
- /*
- * Read the INTSTAT location after clearing the CMDINT bit. This forces
- * any posted PCI writes to flush to memory. Gerard Roudier suggested
- * this fix to the possible race of clearing the CMDINT bit but not
- * having all command bytes flushed onto the qoutfifo.
- */
- aic_outb(p, CLRCMDINT, CLRINT);
- aic_inb(p, INTSTAT);
- /*
- * The sequencer will continue running when it
- * issues this interrupt. There may be >1 commands
- * finished, so loop until we've processed them all.
- */
-
- while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL)
- {
- scb_index = p->qoutfifo[p->qoutfifonext];
- p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL;
- if ( scb_index >= p->scb_data->numscbs )
- {
- printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no,
- -1, -1, -1, scb_index);
- continue;
- }
- scb = p->scb_data->scb_array[scb_index];
- if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags "
- "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags,
- (unsigned long) scb->cmd);
- continue;
- }
- tindex = TARGET_INDEX(scb->cmd);
- aic_dev = AIC_DEV(scb->cmd);
- if (scb->flags & SCB_QUEUED_ABORT)
- {
- pause_sequencer(p);
- if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) &&
- (aic_inb(p, SCB_TAG) == scb->hscb->tag) )
- {
- unpause_sequencer(p, FALSE);
- continue;
- }
- aic7xxx_reset_device(p, scb->cmd->device->id, scb->cmd->device->channel,
- scb->cmd->device->lun, scb->hscb->tag);
- scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT |
- SCB_QUEUED_ABORT);
- unpause_sequencer(p, FALSE);
- }
- else if (scb->flags & SCB_ABORT)
- {
- /*
- * We started to abort this, but it completed on us, let it
- * through as successful
- */
- scb->flags &= ~(SCB_ABORT|SCB_RESET);
- }
- else if (scb->flags & SCB_SENSE)
- {
- char *buffer = &scb->cmd->sense_buffer[0];
-
- if (buffer[12] == 0x47 || buffer[12] == 0x54)
- {
- /*
- * Signal that we need to re-negotiate things.
- */
- aic_dev->needppr = aic_dev->needppr_copy;
- aic_dev->needsdtr = aic_dev->needsdtr_copy;
- aic_dev->needwdtr = aic_dev->needwdtr_copy;
- }
- }
- cmd = scb->cmd;
- if (scb->hscb->residual_SG_segment_count != 0)
- {
- aic7xxx_calculate_residual(p, scb);
- }
- cmd->result |= (aic7xxx_error(cmd) << 16);
- aic7xxx_done(p, scb);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_isr
- *
- * Description:
- * SCSI controller interrupt handler.
- *-F*************************************************************************/
-static void
-aic7xxx_isr(void *dev_id)
-{
- struct aic7xxx_host *p;
- unsigned char intstat;
-
- p = dev_id;
-
- /*
- * Just a few sanity checks. Make sure that we have an int pending.
- * Also, if PCI, then we are going to check for a PCI bus error status
- * should we get too many spurious interrupts.
- */
- if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
- {
-#ifdef CONFIG_PCI
- if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) &&
- !(p->flags & AHC_HANDLING_REQINITS) )
- {
- if ( aic_inb(p, ERROR) & PCIERRSTAT )
- {
- aic7xxx_pci_intr(p);
- }
- p->spurious_int = 0;
- }
- else if ( !(p->flags & AHC_HANDLING_REQINITS) )
- {
- p->spurious_int++;
- }
-#endif
- return;
- }
-
- p->spurious_int = 0;
-
- /*
- * Keep track of interrupts for /proc/scsi
- */
- p->isr_count++;
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
- (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
- aic7xxx_check_scbs(p, "Bogus settings at start of interrupt.");
-#endif
-
- /*
- * Handle all the interrupt sources - especially for SCSI
- * interrupts, we won't get a second chance at them.
- */
- if (intstat & CMDCMPLT)
- {
- aic7xxx_handle_command_completion_intr(p);
- }
-
- if (intstat & BRKADRINT)
- {
- int i;
- unsigned char errno = aic_inb(p, ERROR);
-
- printk(KERN_ERR "(scsi%d) BRKADRINT error(0x%x):\n", p->host_no, errno);
- for (i = 0; i < ARRAY_SIZE(hard_error); i++)
- {
- if (errno & hard_error[i].errno)
- {
- printk(KERN_ERR " %s\n", hard_error[i].errmesg);
- }
- }
- printk(KERN_ERR "(scsi%d) SEQADDR=0x%x\n", p->host_no,
- (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
- if (aic7xxx_panic_on_abort)
- aic7xxx_panic_abort(p, NULL);
-#ifdef CONFIG_PCI
- if (errno & PCIERRSTAT)
- aic7xxx_pci_intr(p);
-#endif
- if (errno & (SQPARERR | ILLOPCODE | ILLSADDR))
- {
- panic("aic7xxx: unrecoverable BRKADRINT.\n");
- }
- if (errno & ILLHADDR)
- {
- printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first "
- "pausing controller!\n", p->host_no);
- }
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (errno & DPARERR)
- {
- if (aic_inb(p, DMAPARAMS) & DIRECTION)
- printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no);
- else
- printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no);
- }
-#endif
- aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT);
- unpause_sequencer(p, FALSE);
- }
-
- if (intstat & SEQINT)
- {
- /*
- * Read the CCSCBCTL register to work around a bug in the Ultra2 cards
- */
- if(p->features & AHC_ULTRA2)
- {
- aic_inb(p, CCSCBCTL);
- }
- aic7xxx_handle_seqint(p, intstat);
- }
-
- if (intstat & SCSIINT)
- {
- aic7xxx_handle_scsiint(p, intstat);
- }
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
- (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
- aic7xxx_check_scbs(p, "Bogus settings at end of interrupt.");
-#endif
-
-}
-
-/*+F*************************************************************************
- * Function:
- * do_aic7xxx_isr
- *
- * Description:
- * This is a gross hack to solve a problem in linux kernels 2.1.85 and
- * above. Please, children, do not try this at home, and if you ever see
- * anything like it, please inform the Gross Hack Police immediately
- *-F*************************************************************************/
-static irqreturn_t
-do_aic7xxx_isr(int irq, void *dev_id)
-{
- unsigned long cpu_flags;
- struct aic7xxx_host *p;
-
- p = dev_id;
- if(!p)
- return IRQ_NONE;
- spin_lock_irqsave(p->host->host_lock, cpu_flags);
- p->flags |= AHC_IN_ISR;
- do
- {
- aic7xxx_isr(dev_id);
- } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
- aic7xxx_done_cmds_complete(p);
- aic7xxx_run_waiting_queues(p);
- p->flags &= ~AHC_IN_ISR;
- spin_unlock_irqrestore(p->host->host_lock, cpu_flags);
-
- return IRQ_HANDLED;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_init_transinfo
- *
- * Description:
- * Set up the initial aic_dev values from the BIOS settings and from
- * INQUIRY results
- *-F*************************************************************************/
-static void
-aic7xxx_init_transinfo(struct aic7xxx_host *p, struct aic_dev_data *aic_dev)
-{
- struct scsi_device *sdpnt = aic_dev->SDptr;
- unsigned char tindex;
-
- tindex = sdpnt->id | (sdpnt->channel << 3);
- if (!(aic_dev->flags & DEVICE_DTR_SCANNED))
- {
- aic_dev->flags |= DEVICE_DTR_SCANNED;
-
- if ( sdpnt->wdtr && (p->features & AHC_WIDE) )
- {
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
- aic_dev->goal.width = p->user[tindex].width;
- }
- else
- {
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
- pause_sequencer(p);
- aic7xxx_set_width(p, sdpnt->id, sdpnt->channel, sdpnt->lun,
- MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE |
- AHC_TRANS_GOAL |
- AHC_TRANS_CUR), aic_dev );
- unpause_sequencer(p, FALSE);
- }
- if ( sdpnt->sdtr && p->user[tindex].offset )
- {
- aic_dev->goal.period = p->user[tindex].period;
- aic_dev->goal.options = p->user[tindex].options;
- if (p->features & AHC_ULTRA2)
- aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
- else if (aic_dev->goal.width == MSG_EXT_WDTR_BUS_16_BIT)
- aic_dev->goal.offset = MAX_OFFSET_16BIT;
- else
- aic_dev->goal.offset = MAX_OFFSET_8BIT;
- if ( sdpnt->ppr && p->user[tindex].period <= 9 &&
- p->user[tindex].options )
- {
- aic_dev->needppr = aic_dev->needppr_copy = 1;
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
- aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
- aic_dev->flags |= DEVICE_SCSI_3;
- }
- else
- {
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
- aic_dev->goal.period = max_t(unsigned char, 10, aic_dev->goal.period);
- aic_dev->goal.options = 0;
- }
- }
- else
- {
- aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
- aic_dev->goal.period = 255;
- aic_dev->goal.offset = 0;
- aic_dev->goal.options = 0;
- }
- aic_dev->flags |= DEVICE_PRINT_DTR;
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_slave_alloc
- *
- * Description:
- * Set up the initial aic_dev struct pointers
- *-F*************************************************************************/
-static int
-aic7xxx_slave_alloc(struct scsi_device *SDptr)
-{
- struct aic7xxx_host *p = (struct aic7xxx_host *)SDptr->host->hostdata;
- struct aic_dev_data *aic_dev;
-
- aic_dev = kmalloc(sizeof(struct aic_dev_data), GFP_KERNEL);
- if(!aic_dev)
- return 1;
- /*
- * Check to see if channel was scanned.
- */
-
- if (!(p->flags & AHC_A_SCANNED) && (SDptr->channel == 0))
- {
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(INFO_LEAD "Scanning channel for devices.\n",
- p->host_no, 0, -1, -1);
- p->flags |= AHC_A_SCANNED;
- }
- else
- {
- if (!(p->flags & AHC_B_SCANNED) && (SDptr->channel == 1))
- {
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(INFO_LEAD "Scanning channel for devices.\n",
- p->host_no, 1, -1, -1);
- p->flags |= AHC_B_SCANNED;
- }
- }
-
- memset(aic_dev, 0, sizeof(struct aic_dev_data));
- SDptr->hostdata = aic_dev;
- aic_dev->SDptr = SDptr;
- aic_dev->max_q_depth = 1;
- aic_dev->temp_q_depth = 1;
- scbq_init(&aic_dev->delayed_scbs);
- INIT_LIST_HEAD(&aic_dev->list);
- list_add_tail(&aic_dev->list, &p->aic_devs);
- return 0;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_device_queue_depth
- *
- * Description:
- * Determines the queue depth for a given device. There are two ways
- * a queue depth can be obtained for a tagged queueing device. One
- * way is the default queue depth which is determined by whether
- * aic7xxx_default_queue_depth. The other is by the aic7xxx_tag_info
- * array.
- *
- * If tagged queueing isn't supported on the device, then we set the
- * depth to p->host->hostt->cmd_per_lun for internal driver queueing.
- * as the default queue depth. Otherwise, we use either 4 or 8 as the
- * default queue depth (dependent on the number of hardware SCBs).
- * The other way we determine queue depth is through the use of the
- * aic7xxx_tag_info array which is enabled by defining
- * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized
- * with queue depths for individual devices. It also allows tagged
- * queueing to be [en|dis]abled for a specific adapter.
- *-F*************************************************************************/
-static void
-aic7xxx_device_queue_depth(struct aic7xxx_host *p, struct scsi_device *device)
-{
- int tag_enabled = FALSE;
- struct aic_dev_data *aic_dev = device->hostdata;
- unsigned char tindex;
-
- tindex = device->id | (device->channel << 3);
-
- if (device->simple_tags)
- return; // We've already enabled this device
-
- if (device->tagged_supported)
- {
- tag_enabled = TRUE;
-
- if (!(p->discenable & (1 << tindex)))
- {
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- printk(INFO_LEAD "Disconnection disabled, unable to "
- "enable tagged queueing.\n",
- p->host_no, device->channel, device->id, device->lun);
- tag_enabled = FALSE;
- }
- else
- {
- if (p->instance >= ARRAY_SIZE(aic7xxx_tag_info))
- {
- static int print_warning = TRUE;
- if(print_warning)
- {
- printk(KERN_INFO "aic7xxx: WARNING, insufficient tag_info instances for"
- " installed controllers.\n");
- printk(KERN_INFO "aic7xxx: Please update the aic7xxx_tag_info array in"
- " the aic7xxx.c source file.\n");
- print_warning = FALSE;
- }
- aic_dev->max_q_depth = aic_dev->temp_q_depth =
- aic7xxx_default_queue_depth;
- }
- else
- {
-
- if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 255)
- {
- tag_enabled = FALSE;
- }
- else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
- {
- aic_dev->max_q_depth = aic_dev->temp_q_depth =
- aic7xxx_default_queue_depth;
- }
- else
- {
- aic_dev->max_q_depth = aic_dev->temp_q_depth =
- aic7xxx_tag_info[p->instance].tag_commands[tindex];
- }
- }
- }
- }
- if (tag_enabled)
- {
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Tagged queuing enabled, queue depth %d.\n",
- p->host_no, device->channel, device->id,
- device->lun, aic_dev->max_q_depth);
- }
- scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, aic_dev->max_q_depth);
- }
- else
- {
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Tagged queuing disabled, queue depth %d.\n",
- p->host_no, device->channel, device->id,
- device->lun, device->host->cmd_per_lun);
- }
- scsi_adjust_queue_depth(device, 0, device->host->cmd_per_lun);
- }
- return;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_slave_destroy
- *
- * Description:
- * prepare for this device to go away
- *-F*************************************************************************/
-static void
-aic7xxx_slave_destroy(struct scsi_device *SDptr)
-{
- struct aic_dev_data *aic_dev = SDptr->hostdata;
-
- list_del(&aic_dev->list);
- SDptr->hostdata = NULL;
- kfree(aic_dev);
- return;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_slave_configure
- *
- * Description:
- * Configure the device we are attaching to the controller. This is
- * where we get to do things like scan the INQUIRY data, set queue
- * depths, allocate command structs, etc.
- *-F*************************************************************************/
-static int
-aic7xxx_slave_configure(struct scsi_device *SDptr)
-{
- struct aic7xxx_host *p = (struct aic7xxx_host *) SDptr->host->hostdata;
- struct aic_dev_data *aic_dev;
- int scbnum;
-
- aic_dev = (struct aic_dev_data *)SDptr->hostdata;
-
- aic7xxx_init_transinfo(p, aic_dev);
- aic7xxx_device_queue_depth(p, SDptr);
- if(list_empty(&aic_dev->list))
- list_add_tail(&aic_dev->list, &p->aic_devs);
-
- scbnum = 0;
- list_for_each_entry(aic_dev, &p->aic_devs, list) {
- scbnum += aic_dev->max_q_depth;
- }
- while (scbnum > p->scb_data->numscbs)
- {
- /*
- * Pre-allocate the needed SCBs to get around the possibility of having
- * to allocate some when memory is more or less exhausted and we need
- * the SCB in order to perform a swap operation (possible deadlock)
- */
- if ( aic7xxx_allocate_scb(p) == 0 )
- break;
- }
-
-
- return(0);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_probe
- *
- * Description:
- * Probing for EISA boards: it looks like the first two bytes
- * are a manufacturer code - three characters, five bits each:
- *
- * BYTE 0 BYTE 1 BYTE 2 BYTE 3
- * ?1111122 22233333 PPPPPPPP RRRRRRRR
- *
- * The characters are baselined off ASCII '@', so add that value
- * to each to get the real ASCII code for it. The next two bytes
- * appear to be a product and revision number, probably vendor-
- * specific. This is what is being searched for at each port,
- * and what should probably correspond to the ID= field in the
- * ECU's .cfg file for the card - if your card is not detected,
- * make sure your signature is listed in the array.
- *
- * The fourth byte's lowest bit seems to be an enabled/disabled
- * flag (rest of the bits are reserved?).
- *
- * NOTE: This function is only needed on Intel and Alpha platforms,
- * the other platforms we support don't have EISA/VLB busses. So,
- * we #ifdef this entire function to avoid compiler warnings about
- * an unused function.
- *-F*************************************************************************/
-#if defined(__i386__) || defined(__alpha__)
-static int
-aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
-{
- int i;
- unsigned char buf[4];
-
- static struct {
- int n;
- unsigned char signature[sizeof(buf)];
- ahc_chip type;
- int bios_disabled;
- } AIC7xxx[] = {
- { 4, { 0x04, 0x90, 0x77, 0x70 },
- AHC_AIC7770|AHC_EISA, FALSE }, /* mb 7770 */
- { 4, { 0x04, 0x90, 0x77, 0x71 },
- AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */
- { 4, { 0x04, 0x90, 0x77, 0x56 },
- AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */
- { 4, { 0x04, 0x90, 0x77, 0x57 },
- AHC_AIC7770|AHC_VL, TRUE } /* 284x BIOS disabled */
- };
-
- /*
- * The VL-bus cards need to be primed by
- * writing before a signature check.
- */
- for (i = 0; i < sizeof(buf); i++)
- {
- outb(0x80 + i, base);
- buf[i] = inb(base + i);
- }
-
- for (i = 0; i < ARRAY_SIZE(AIC7xxx); i++)
- {
- /*
- * Signature match on enabled card?
- */
- if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
- {
- if (inb(base + 4) & 1)
- {
- if (AIC7xxx[i].bios_disabled)
- {
- *flags |= AHC_USEDEFAULTS;
- }
- else
- {
- *flags |= AHC_BIOS_ENABLED;
- }
- return (i);
- }
-
- printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
- "disabled at slot %d, ignored.\n", slot);
- }
- }
-
- return (-1);
-}
-#endif /* (__i386__) || (__alpha__) */
-
-
-/*+F*************************************************************************
- * Function:
- * read_2840_seeprom
- *
- * Description:
- * Reads the 2840 serial EEPROM and returns 1 if successful and 0 if
- * not successful.
- *
- * See read_seeprom (for the 2940) for the instruction set of the 93C46
- * chip.
- *
- * The 2840 interface to the 93C46 serial EEPROM is through the
- * STATUS_2840 and SEECTL_2840 registers. The CS_2840, CK_2840, and
- * DO_2840 bits of the SEECTL_2840 register are connected to the chip
- * select, clock, and data out lines respectively of the serial EEPROM.
- * The DI_2840 bit of the STATUS_2840 is connected to the data in line
- * of the serial EEPROM. The EEPROM_TF bit of STATUS_2840 register is
- * useful in that it gives us an 800 nsec timer. After a read from the
- * SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
- * later.
- *-F*************************************************************************/
-static int
-read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
-{
- int i = 0, k = 0;
- unsigned char temp;
- unsigned short checksum = 0;
- unsigned short *seeprom = (unsigned short *) sc;
- struct seeprom_cmd {
- unsigned char len;
- unsigned char bits[3];
- };
- struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
-
-#define CLOCK_PULSE(p) \
- while ((aic_inb(p, STATUS_2840) & EEPROM_TF) == 0) \
- { \
- ; /* Do nothing */ \
- } \
- (void) aic_inb(p, SEECTL_2840);
-
- /*
- * Read the first 32 registers of the seeprom. For the 2840,
- * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
- * but only the first 32 are used by Adaptec BIOS. The loop
- * will range from 0 to 31.
- */
- for (k = 0; k < (sizeof(*sc) / 2); k++)
- {
- /*
- * Send chip select for one clock cycle.
- */
- aic_outb(p, CK_2840 | CS_2840, SEECTL_2840);
- CLOCK_PULSE(p);
-
- /*
- * Now we're ready to send the read command followed by the
- * address of the 16-bit register we want to read.
- */
- for (i = 0; i < seeprom_read.len; i++)
- {
- temp = CS_2840 | seeprom_read.bits[i];
- aic_outb(p, temp, SEECTL_2840);
- CLOCK_PULSE(p);
- temp = temp ^ CK_2840;
- aic_outb(p, temp, SEECTL_2840);
- CLOCK_PULSE(p);
- }
- /*
- * Send the 6 bit address (MSB first, LSB last).
- */
- for (i = 5; i >= 0; i--)
- {
- temp = k;
- temp = (temp >> i) & 1; /* Mask out all but lower bit. */
- temp = CS_2840 | temp;
- aic_outb(p, temp, SEECTL_2840);
- CLOCK_PULSE(p);
- temp = temp ^ CK_2840;
- aic_outb(p, temp, SEECTL_2840);
- CLOCK_PULSE(p);
- }
-
- /*
- * Now read the 16 bit register. An initial 0 precedes the
- * register contents which begins with bit 15 (MSB) and ends
- * with bit 0 (LSB). The initial 0 will be shifted off the
- * top of our word as we let the loop run from 0 to 16.
- */
- for (i = 0; i <= 16; i++)
- {
- temp = CS_2840;
- aic_outb(p, temp, SEECTL_2840);
- CLOCK_PULSE(p);
- temp = temp ^ CK_2840;
- seeprom[k] = (seeprom[k] << 1) | (aic_inb(p, STATUS_2840) & DI_2840);
- aic_outb(p, temp, SEECTL_2840);
- CLOCK_PULSE(p);
- }
- /*
- * The serial EEPROM has a checksum in the last word. Keep a
- * running checksum for all words read except for the last
- * word. We'll verify the checksum after all words have been
- * read.
- */
- if (k < (sizeof(*sc) / 2) - 1)
- {
- checksum = checksum + seeprom[k];
- }
-
- /*
- * Reset the chip select for the next command cycle.
- */
- aic_outb(p, 0, SEECTL_2840);
- CLOCK_PULSE(p);
- aic_outb(p, CK_2840, SEECTL_2840);
- CLOCK_PULSE(p);
- aic_outb(p, 0, SEECTL_2840);
- CLOCK_PULSE(p);
- }
-
-#if 0
- printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
- printk("Serial EEPROM:");
- for (k = 0; k < (sizeof(*sc) / 2); k++)
- {
- if (((k % 8) == 0) && (k != 0))
- {
- printk("\n ");
- }
- printk(" 0x%x", seeprom[k]);
- }
- printk("\n");
-#endif
-
- if (checksum != sc->checksum)
- {
- printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
- return (0);
- }
-
- return (1);
-#undef CLOCK_PULSE
-}
-
-#define CLOCK_PULSE(p) \
- do { \
- int limit = 0; \
- do { \
- mb(); \
- pause_sequencer(p); /* This is just to generate some PCI */ \
- /* traffic so the PCI read is flushed */ \
- /* it shouldn't be needed, but some */ \
- /* chipsets do indeed appear to need */ \
- /* something to force PCI reads to get */ \
- /* flushed */ \
- udelay(1); /* Do nothing */ \
- } while (((aic_inb(p, SEECTL) & SEERDY) == 0) && (++limit < 1000)); \
- } while(0)
-
-/*+F*************************************************************************
- * Function:
- * acquire_seeprom
- *
- * Description:
- * Acquires access to the memory port on PCI controllers.
- *-F*************************************************************************/
-static int
-acquire_seeprom(struct aic7xxx_host *p)
-{
-
- /*
- * Request access of the memory port. When access is
- * granted, SEERDY will go high. We use a 1 second
- * timeout which should be near 1 second more than
- * is needed. Reason: after the 7870 chip reset, there
- * should be no contention.
- */
- aic_outb(p, SEEMS, SEECTL);
- CLOCK_PULSE(p);
- if ((aic_inb(p, SEECTL) & SEERDY) == 0)
- {
- aic_outb(p, 0, SEECTL);
- return (0);
- }
- return (1);
-}
-
-/*+F*************************************************************************
- * Function:
- * release_seeprom
- *
- * Description:
- * Releases access to the memory port on PCI controllers.
- *-F*************************************************************************/
-static void
-release_seeprom(struct aic7xxx_host *p)
-{
- /*
- * Make sure the SEEPROM is ready before we release it.
- */
- CLOCK_PULSE(p);
- aic_outb(p, 0, SEECTL);
-}
-
-/*+F*************************************************************************
- * Function:
- * read_seeprom
- *
- * Description:
- * Reads the serial EEPROM and returns 1 if successful and 0 if
- * not successful.
- *
- * The instruction set of the 93C46/56/66 chips is as follows:
- *
- * Start OP
- * Function Bit Code Address Data Description
- * -------------------------------------------------------------------
- * READ 1 10 A5 - A0 Reads data stored in memory,
- * starting at specified address
- * EWEN 1 00 11XXXX Write enable must precede
- * all programming modes
- * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
- * WRITE 1 01 A5 - A0 D15 - D0 Writes register
- * ERAL 1 00 10XXXX Erase all registers
- * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
- * EWDS 1 00 00XXXX Disables all programming
- * instructions
- * *Note: A value of X for address is a don't care condition.
- * *Note: The 93C56 and 93C66 have 8 address bits.
- *
- *
- * The 93C46 has a four wire interface: clock, chip select, data in, and
- * data out. In order to perform one of the above functions, you need
- * to enable the chip select for a clock period (typically a minimum of
- * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
- * respectively. While the chip select remains high, you can clock in
- * the instructions (above) starting with the start bit, followed by the
- * OP code, Address, and Data (if needed). For the READ instruction, the
- * requested 16-bit register contents is read from the data out line but
- * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
- * first). The clock cycling from low to high initiates the next data
- * bit to be sent from the chip.
- *
- * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
- * register. After successful arbitration for the memory port, the
- * SEECS bit of the SEECTL register is connected to the chip select.
- * The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
- * and data in lines respectively. The SEERDY bit of SEECTL is useful
- * in that it gives us an 800 nsec timer. After a write to the SEECTL
- * register, the SEERDY goes high 800 nsec later. The one exception
- * to this is when we first request access to the memory port. The
- * SEERDY goes high to signify that access has been granted and, for
- * this case, has no implied timing.
- *-F*************************************************************************/
-static int
-read_seeprom(struct aic7xxx_host *p, int offset,
- unsigned short *scarray, unsigned int len, seeprom_chip_type chip)
-{
- int i = 0, k;
- unsigned char temp;
- unsigned short checksum = 0;
- struct seeprom_cmd {
- unsigned char len;
- unsigned char bits[3];
- };
- struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
-
- /*
- * Request access of the memory port.
- */
- if (acquire_seeprom(p) == 0)
- {
- return (0);
- }
-
- /*
- * Read 'len' registers of the seeprom. For the 7870, the 93C46
- * SEEPROM is a 1024-bit device with 64 16-bit registers but only
- * the first 32 are used by Adaptec BIOS. Some adapters use the
- * 93C56 SEEPROM which is a 2048-bit device. The loop will range
- * from 0 to 'len' - 1.
- */
- for (k = 0; k < len; k++)
- {
- /*
- * Send chip select for one clock cycle.
- */
- aic_outb(p, SEEMS | SEECK | SEECS, SEECTL);
- CLOCK_PULSE(p);
-
- /*
- * Now we're ready to send the read command followed by the
- * address of the 16-bit register we want to read.
- */
- for (i = 0; i < seeprom_read.len; i++)
- {
- temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
- aic_outb(p, temp, SEECTL);
- CLOCK_PULSE(p);
- temp = temp ^ SEECK;
- aic_outb(p, temp, SEECTL);
- CLOCK_PULSE(p);
- }
- /*
- * Send the 6 or 8 bit address (MSB first, LSB last).
- */
- for (i = ((int) chip - 1); i >= 0; i--)
- {
- temp = k + offset;
- temp = (temp >> i) & 1; /* Mask out all but lower bit. */
- temp = SEEMS | SEECS | (temp << 1);
- aic_outb(p, temp, SEECTL);
- CLOCK_PULSE(p);
- temp = temp ^ SEECK;
- aic_outb(p, temp, SEECTL);
- CLOCK_PULSE(p);
- }
-
- /*
- * Now read the 16 bit register. An initial 0 precedes the
- * register contents which begins with bit 15 (MSB) and ends
- * with bit 0 (LSB). The initial 0 will be shifted off the
- * top of our word as we let the loop run from 0 to 16.
- */
- for (i = 0; i <= 16; i++)
- {
- temp = SEEMS | SEECS;
- aic_outb(p, temp, SEECTL);
- CLOCK_PULSE(p);
- temp = temp ^ SEECK;
- scarray[k] = (scarray[k] << 1) | (aic_inb(p, SEECTL) & SEEDI);
- aic_outb(p, temp, SEECTL);
- CLOCK_PULSE(p);
- }
-
- /*
- * The serial EEPROM should have a checksum in the last word.
- * Keep a running checksum for all words read except for the
- * last word. We'll verify the checksum after all words have
- * been read.
- */
- if (k < (len - 1))
- {
- checksum = checksum + scarray[k];
- }
-
- /*
- * Reset the chip select for the next command cycle.
- */
- aic_outb(p, SEEMS, SEECTL);
- CLOCK_PULSE(p);
- aic_outb(p, SEEMS | SEECK, SEECTL);
- CLOCK_PULSE(p);
- aic_outb(p, SEEMS, SEECTL);
- CLOCK_PULSE(p);
- }
-
- /*
- * Release access to the memory port and the serial EEPROM.
- */
- release_seeprom(p);
-
-#if 0
- printk("Computed checksum 0x%x, checksum read 0x%x\n",
- checksum, scarray[len - 1]);
- printk("Serial EEPROM:");
- for (k = 0; k < len; k++)
- {
- if (((k % 8) == 0) && (k != 0))
- {
- printk("\n ");
- }
- printk(" 0x%x", scarray[k]);
- }
- printk("\n");
-#endif
- if ( (checksum != scarray[len - 1]) || (checksum == 0) )
- {
- return (0);
- }
-
- return (1);
-}
-
-/*+F*************************************************************************
- * Function:
- * read_brdctl
- *
- * Description:
- * Reads the BRDCTL register.
- *-F*************************************************************************/
-static unsigned char
-read_brdctl(struct aic7xxx_host *p)
-{
- unsigned char brdctl, value;
-
- /*
- * Make sure the SEEPROM is ready before we access it
- */
- CLOCK_PULSE(p);
- if (p->features & AHC_ULTRA2)
- {
- brdctl = BRDRW_ULTRA2;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- value = aic_inb(p, BRDCTL);
- CLOCK_PULSE(p);
- return(value);
- }
- brdctl = BRDRW;
- if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
- (p->flags & AHC_CHNLB) )
- {
- brdctl |= BRDCS;
- }
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- value = aic_inb(p, BRDCTL);
- CLOCK_PULSE(p);
- aic_outb(p, 0, BRDCTL);
- CLOCK_PULSE(p);
- return (value);
-}
-
-/*+F*************************************************************************
- * Function:
- * write_brdctl
- *
- * Description:
- * Writes a value to the BRDCTL register.
- *-F*************************************************************************/
-static void
-write_brdctl(struct aic7xxx_host *p, unsigned char value)
-{
- unsigned char brdctl;
-
- /*
- * Make sure the SEEPROM is ready before we access it
- */
- CLOCK_PULSE(p);
- if (p->features & AHC_ULTRA2)
- {
- brdctl = value;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- brdctl |= BRDSTB_ULTRA2;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- brdctl &= ~BRDSTB_ULTRA2;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- read_brdctl(p);
- CLOCK_PULSE(p);
- }
- else
- {
- brdctl = BRDSTB;
- if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
- (p->flags & AHC_CHNLB) )
- {
- brdctl |= BRDCS;
- }
- brdctl = BRDSTB | BRDCS;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- brdctl |= value;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- brdctl &= ~BRDSTB;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- brdctl &= ~BRDCS;
- aic_outb(p, brdctl, BRDCTL);
- CLOCK_PULSE(p);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic785x_cable_detect
- *
- * Description:
- * Detect the cables that are present on aic785x class controller chips
- *-F*************************************************************************/
-static void
-aic785x_cable_detect(struct aic7xxx_host *p, int *int_50,
- int *ext_present, int *eeprom)
-{
- unsigned char brdctl;
-
- aic_outb(p, BRDRW | BRDCS, BRDCTL);
- CLOCK_PULSE(p);
- aic_outb(p, 0, BRDCTL);
- CLOCK_PULSE(p);
- brdctl = aic_inb(p, BRDCTL);
- CLOCK_PULSE(p);
- *int_50 = !(brdctl & BRDDAT5);
- *ext_present = !(brdctl & BRDDAT6);
- *eeprom = (aic_inb(p, SPIOCAP) & EEPROM);
-}
-
-#undef CLOCK_PULSE
-
-/*+F*************************************************************************
- * Function:
- * aic2940_uwpro_cable_detect
- *
- * Description:
- * Detect the cables that are present on the 2940-UWPro cards
- *
- * NOTE: This function assumes the SEEPROM will have already been acquired
- * prior to invocation of this function.
- *-F*************************************************************************/
-static void
-aic2940_uwpro_wide_cable_detect(struct aic7xxx_host *p, int *int_68,
- int *ext_68, int *eeprom)
-{
- unsigned char brdctl;
-
- /*
- * First read the status of our cables. Set the rom bank to
- * 0 since the bank setting serves as a multiplexor for the
- * cable detection logic. BRDDAT5 controls the bank switch.
- */
- write_brdctl(p, 0);
-
- /*
- * Now we read the state of the internal 68 connector. BRDDAT6
- * is don't care, BRDDAT7 is internal 68. The cable is
- * present if the bit is 0
- */
- brdctl = read_brdctl(p);
- *int_68 = !(brdctl & BRDDAT7);
-
- /*
- * Set the bank bit in brdctl and then read the external cable state
- * and the EEPROM status
- */
- write_brdctl(p, BRDDAT5);
- brdctl = read_brdctl(p);
-
- *ext_68 = !(brdctl & BRDDAT6);
- *eeprom = !(brdctl & BRDDAT7);
-
- /*
- * We're done, the calling function will release the SEEPROM for us
- */
-}
-
-/*+F*************************************************************************
- * Function:
- * aic787x_cable_detect
- *
- * Description:
- * Detect the cables that are present on aic787x class controller chips
- *
- * NOTE: This function assumes the SEEPROM will have already been acquired
- * prior to invocation of this function.
- *-F*************************************************************************/
-static void
-aic787x_cable_detect(struct aic7xxx_host *p, int *int_50, int *int_68,
- int *ext_present, int *eeprom)
-{
- unsigned char brdctl;
-
- /*
- * First read the status of our cables. Set the rom bank to
- * 0 since the bank setting serves as a multiplexor for the
- * cable detection logic. BRDDAT5 controls the bank switch.
- */
- write_brdctl(p, 0);
-
- /*
- * Now we read the state of the two internal connectors. BRDDAT6
- * is internal 50, BRDDAT7 is internal 68. For each, the cable is
- * present if the bit is 0
- */
- brdctl = read_brdctl(p);
- *int_50 = !(brdctl & BRDDAT6);
- *int_68 = !(brdctl & BRDDAT7);
-
- /*
- * Set the bank bit in brdctl and then read the external cable state
- * and the EEPROM status
- */
- write_brdctl(p, BRDDAT5);
- brdctl = read_brdctl(p);
-
- *ext_present = !(brdctl & BRDDAT6);
- *eeprom = !(brdctl & BRDDAT7);
-
- /*
- * We're done, the calling function will release the SEEPROM for us
- */
-}
-
-/*+F*************************************************************************
- * Function:
- * aic787x_ultra2_term_detect
- *
- * Description:
- * Detect the termination settings present on ultra2 class controllers
- *
- * NOTE: This function assumes the SEEPROM will have already been acquired
- * prior to invocation of this function.
- *-F*************************************************************************/
-static void
-aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low,
- int *enableSE_high, int *enableLVD_low,
- int *enableLVD_high, int *eprom_present)
-{
- unsigned char brdctl;
-
- brdctl = read_brdctl(p);
-
- *eprom_present = (brdctl & BRDDAT7);
- *enableSE_high = (brdctl & BRDDAT6);
- *enableSE_low = (brdctl & BRDDAT5);
- *enableLVD_high = (brdctl & BRDDAT4);
- *enableLVD_low = (brdctl & BRDDAT3);
-}
-
-/*+F*************************************************************************
- * Function:
- * configure_termination
- *
- * Description:
- * Configures the termination settings on PCI adapters that have
- * SEEPROMs available.
- *-F*************************************************************************/
-static void
-configure_termination(struct aic7xxx_host *p)
-{
- int internal50_present = 0;
- int internal68_present = 0;
- int external_present = 0;
- int eprom_present = 0;
- int enableSE_low = 0;
- int enableSE_high = 0;
- int enableLVD_low = 0;
- int enableLVD_high = 0;
- unsigned char brddat = 0;
- unsigned char max_target = 0;
- unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1);
-
- if (acquire_seeprom(p))
- {
- if (p->features & (AHC_WIDE|AHC_TWIN))
- max_target = 16;
- else
- max_target = 8;
- aic_outb(p, SEEMS | SEECS, SEECTL);
- sxfrctl1 &= ~STPWEN;
- /*
- * The termination/cable detection logic is split into three distinct
- * groups. Ultra2 and later controllers, 2940UW-Pro controllers, and
- * older 7850, 7860, 7870, 7880, and 7895 controllers. Each has its
- * own unique way of detecting their cables and writing the results
- * back to the card.
- */
- if (p->features & AHC_ULTRA2)
- {
- /*
- * As long as user hasn't overridden term settings, always check the
- * cable detection logic
- */
- if (aic7xxx_override_term == -1)
- {
- aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high,
- &enableLVD_low, &enableLVD_high,
- &eprom_present);
- }
-
- /*
- * If the user is overriding settings, then they have been preserved
- * to here as fake adapter_control entries. Parse them and allow
- * them to override the detected settings (if we even did detection).
- */
- if (!(p->adapter_control & CFSEAUTOTERM))
- {
- enableSE_low = (p->adapter_control & CFSTERM);
- enableSE_high = (p->adapter_control & CFWSTERM);
- }
- if (!(p->adapter_control & CFAUTOTERM))
- {
- enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM);
- }
-
- /*
- * Now take those settings that we have and translate them into the
- * values that must be written into the registers.
- *
- * Flash Enable = BRDDAT7
- * Secondary High Term Enable = BRDDAT6
- * Secondary Low Term Enable = BRDDAT5
- * LVD/Primary High Term Enable = BRDDAT4
- * LVD/Primary Low Term Enable = STPWEN bit in SXFRCTL1
- */
- if (enableLVD_low != 0)
- {
- sxfrctl1 |= STPWEN;
- p->flags |= AHC_TERM_ENB_LVD;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) LVD/Primary Low byte termination "
- "Enabled\n", p->host_no);
- }
-
- if (enableLVD_high != 0)
- {
- brddat |= BRDDAT4;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) LVD/Primary High byte termination "
- "Enabled\n", p->host_no);
- }
-
- if (enableSE_low != 0)
- {
- brddat |= BRDDAT5;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) Secondary Low byte termination "
- "Enabled\n", p->host_no);
- }
-
- if (enableSE_high != 0)
- {
- brddat |= BRDDAT6;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) Secondary High byte termination "
- "Enabled\n", p->host_no);
- }
- }
- else if (p->features & AHC_NEW_AUTOTERM)
- {
- /*
- * The 50 pin connector termination is controlled by STPWEN in the
- * SXFRCTL1 register. Since the Adaptec docs typically say the
- * controller is not allowed to be in the middle of a cable and
- * this is the only connection on that stub of the bus, there is
- * no need to even check for narrow termination, it's simply
- * always on.
- */
- sxfrctl1 |= STPWEN;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) Narrow channel termination Enabled\n",
- p->host_no);
-
- if (p->adapter_control & CFAUTOTERM)
- {
- aic2940_uwpro_wide_cable_detect(p, &internal68_present,
- &external_present,
- &eprom_present);
- printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
- "Ext-68 %s)\n", p->host_no,
- "Don't Care",
- internal68_present ? "YES" : "NO",
- external_present ? "YES" : "NO");
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
- eprom_present ? "is" : "is not");
- if (internal68_present && external_present)
- {
- brddat = 0;
- p->flags &= ~AHC_TERM_ENB_SE_HIGH;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) Wide channel termination Disabled\n",
- p->host_no);
- }
- else
- {
- brddat = BRDDAT6;
- p->flags |= AHC_TERM_ENB_SE_HIGH;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
- p->host_no);
- }
- }
- else
- {
- /*
- * The termination of the Wide channel is done more like normal
- * though, and the setting of this termination is done by writing
- * either a 0 or 1 to BRDDAT6 of the BRDDAT register
- */
- if (p->adapter_control & CFWSTERM)
- {
- brddat = BRDDAT6;
- p->flags |= AHC_TERM_ENB_SE_HIGH;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
- p->host_no);
- }
- else
- {
- brddat = 0;
- }
- }
- }
- else
- {
- if (p->adapter_control & CFAUTOTERM)
- {
- if (p->flags & AHC_MOTHERBOARD)
- {
- printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n",
- p->host_no);
- printk(KERN_INFO "(scsi%d) Please verify driver detected settings "
- "are correct.\n", p->host_no);
- printk(KERN_INFO "(scsi%d) If not, then please properly set the "
- "device termination\n", p->host_no);
- printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting "
- "CTRL-A when prompted\n", p->host_no);
- printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no);
- }
- /* Configure auto termination. */
-
- if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 )
- {
- aic787x_cable_detect(p, &internal50_present, &internal68_present,
- &external_present, &eprom_present);
- }
- else
- {
- aic785x_cable_detect(p, &internal50_present, &external_present,
- &eprom_present);
- }
-
- if (max_target <= 8)
- internal68_present = 0;
-
- if (max_target > 8)
- {
- printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
- "Ext-68 %s)\n", p->host_no,
- internal50_present ? "YES" : "NO",
- internal68_present ? "YES" : "NO",
- external_present ? "YES" : "NO");
- }
- else
- {
- printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n",
- p->host_no,
- internal50_present ? "YES" : "NO",
- external_present ? "YES" : "NO");
- }
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
- eprom_present ? "is" : "is not");
-
- /*
- * Now set the termination based on what we found. BRDDAT6
- * controls wide termination enable.
- * Flash Enable = BRDDAT7
- * SE High Term Enable = BRDDAT6
- */
- if (internal50_present && internal68_present && external_present)
- {
- printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n",
- p->host_no);
- printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be "
- "in use at a time!\n", p->host_no);
- /*
- * Force termination (low and high byte) on. This is safer than
- * leaving it completely off, especially since this message comes
- * most often from motherboard controllers that don't even have 3
- * connectors, but instead are failing the cable detection.
- */
- internal50_present = external_present = 0;
- enableSE_high = enableSE_low = 1;
- }
-
- if ((max_target > 8) &&
- ((external_present == 0) || (internal68_present == 0)) )
- {
- brddat |= BRDDAT6;
- p->flags |= AHC_TERM_ENB_SE_HIGH;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
- p->host_no);
- }
-
- if ( ((internal50_present ? 1 : 0) +
- (internal68_present ? 1 : 0) +
- (external_present ? 1 : 0)) <= 1 )
- {
- sxfrctl1 |= STPWEN;
- p->flags |= AHC_TERM_ENB_SE_LOW;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
- p->host_no);
- }
- }
- else /* p->adapter_control & CFAUTOTERM */
- {
- if (p->adapter_control & CFSTERM)
- {
- sxfrctl1 |= STPWEN;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
- p->host_no);
- }
-
- if (p->adapter_control & CFWSTERM)
- {
- brddat |= BRDDAT6;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
- p->host_no);
- }
- }
- }
-
- aic_outb(p, sxfrctl1, SXFRCTL1);
- write_brdctl(p, brddat);
- release_seeprom(p);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * detect_maxscb
- *
- * Description:
- * Detects the maximum number of SCBs for the controller and returns
- * the count and a mask in p (p->maxscbs, p->qcntmask).
- *-F*************************************************************************/
-static void
-detect_maxscb(struct aic7xxx_host *p)
-{
- int i;
-
- /*
- * It's possible that we've already done this for multichannel
- * adapters.
- */
- if (p->scb_data->maxhscbs == 0)
- {
- /*
- * We haven't initialized the SCB settings yet. Walk the SCBs to
- * determince how many there are.
- */
- aic_outb(p, 0, FREE_SCBH);
-
- for (i = 0; i < AIC7XXX_MAXSCB; i++)
- {
- aic_outb(p, i, SCBPTR);
- aic_outb(p, i, SCB_CONTROL);
- if (aic_inb(p, SCB_CONTROL) != i)
- break;
- aic_outb(p, 0, SCBPTR);
- if (aic_inb(p, SCB_CONTROL) != 0)
- break;
-
- aic_outb(p, i, SCBPTR);
- aic_outb(p, 0, SCB_CONTROL); /* Clear the control byte. */
- aic_outb(p, i + 1, SCB_NEXT); /* Set the next pointer. */
- aic_outb(p, SCB_LIST_NULL, SCB_TAG); /* Make the tag invalid. */
- aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS); /* no busy untagged */
- aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
- aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2);
- aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3);
- }
-
- /* Make sure the last SCB terminates the free list. */
- aic_outb(p, i - 1, SCBPTR);
- aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
-
- /* Ensure we clear the first (0) SCBs control byte. */
- aic_outb(p, 0, SCBPTR);
- aic_outb(p, 0, SCB_CONTROL);
-
- p->scb_data->maxhscbs = i;
- /*
- * Use direct indexing instead for speed
- */
- if ( i == AIC7XXX_MAXSCB )
- p->flags &= ~AHC_PAGESCBS;
- }
-
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_register
- *
- * Description:
- * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
- *-F*************************************************************************/
-static int
-aic7xxx_register(struct scsi_host_template *template, struct aic7xxx_host *p,
- int reset_delay)
-{
- int i, result;
- int max_targets;
- int found = 1;
- unsigned char term, scsi_conf;
- struct Scsi_Host *host;
-
- host = p->host;
-
- p->scb_data->maxscbs = AIC7XXX_MAXSCB;
- host->can_queue = AIC7XXX_MAXSCB;
- host->cmd_per_lun = 3;
- host->sg_tablesize = AIC7XXX_MAX_SG;
- host->this_id = p->scsi_id;
- host->io_port = p->base;
- host->n_io_port = 0xFF;
- host->base = p->mbase;
- host->irq = p->irq;
- if (p->features & AHC_WIDE)
- {
- host->max_id = 16;
- }
- if (p->features & AHC_TWIN)
- {
- host->max_channel = 1;
- }
-
- p->host = host;
- p->host_no = host->host_no;
- host->unique_id = p->instance;
- p->isr_count = 0;
- p->next = NULL;
- p->completeq.head = NULL;
- p->completeq.tail = NULL;
- scbq_init(&p->scb_data->free_scbs);
- scbq_init(&p->waiting_scbs);
- INIT_LIST_HEAD(&p->aic_devs);
-
- /*
- * We currently have no commands of any type
- */
- p->qinfifonext = 0;
- p->qoutfifonext = 0;
-
- printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
- board_names[p->board_name_index]);
- switch(p->chip)
- {
- case (AHC_AIC7770|AHC_EISA):
- printk("EISA slot %d\n", p->pci_device_fn);
- break;
- case (AHC_AIC7770|AHC_VL):
- printk("VLB slot %d\n", p->pci_device_fn);
- break;
- default:
- printk("PCI %d/%d/%d\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
- PCI_FUNC(p->pci_device_fn));
- break;
- }
- if (p->features & AHC_TWIN)
- {
- printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
- p->host_no, p->scsi_id, p->scsi_id_b);
- }
- else
- {
- char *channel;
-
- channel = "";
-
- if ((p->flags & AHC_MULTI_CHANNEL) != 0)
- {
- channel = " A";
-
- if ( (p->flags & (AHC_CHNLB|AHC_CHNLC)) != 0 )
- {
- channel = (p->flags & AHC_CHNLB) ? " B" : " C";
- }
- }
- if (p->features & AHC_WIDE)
- {
- printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
- }
- else
- {
- printk(KERN_INFO "(scsi%d) Narrow ", p->host_no);
- }
- printk("Channel%s, SCSI ID=%d, ", channel, p->scsi_id);
- }
- aic_outb(p, 0, SEQ_FLAGS);
-
- detect_maxscb(p);
-
- printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
- p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
- p->base, p->irq);
- printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at %p\n",
- p->host_no, p->mbase, p->maddr);
- }
-
-#ifdef CONFIG_PCI
- /*
- * Now that we know our instance number, we can set the flags we need to
- * force termination if need be.
- */
- if (aic7xxx_stpwlev != -1)
- {
- /*
- * This option only applies to PCI controllers.
- */
- if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
- {
- unsigned char devconfig;
-
- pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig);
- if ( (aic7xxx_stpwlev >> p->instance) & 0x01 )
- {
- devconfig |= STPWLEVEL;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk("(scsi%d) Force setting STPWLEVEL bit\n", p->host_no);
- }
- else
- {
- devconfig &= ~STPWLEVEL;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk("(scsi%d) Force clearing STPWLEVEL bit\n", p->host_no);
- }
- pci_write_config_byte(p->pdev, DEVCONFIG, devconfig);
- }
- }
-#endif
-
- /*
- * That took care of devconfig and stpwlev, now for the actual termination
- * settings.
- */
- if (aic7xxx_override_term != -1)
- {
- /*
- * Again, this only applies to PCI controllers. We don't have problems
- * with the termination on 274x controllers to the best of my knowledge.
- */
- if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
- {
- unsigned char term_override;
-
- term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f);
- p->adapter_control &=
- ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM);
- if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) )
- {
- p->adapter_control |= CFLVDSTERM;
- }
- if (term_override & 0x02)
- {
- p->adapter_control |= CFWSTERM;
- }
- if (term_override & 0x01)
- {
- p->adapter_control |= CFSTERM;
- }
- }
- }
-
- if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) )
- {
- if (p->features & AHC_SPIOCAP)
- {
- if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
- /*
- * Update the settings in sxfrctl1 to match the termination
- * settings.
- */
- configure_termination(p);
- }
- else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
- {
- configure_termination(p);
- }
- }
-
- /*
- * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
- */
- if (p->features & AHC_TWIN)
- {
- /* Select channel B */
- aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
-
- if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
- term = (aic_inb(p, SXFRCTL1) & STPWEN);
- else
- term = ((p->flags & AHC_TERM_ENB_B) ? STPWEN : 0);
-
- aic_outb(p, p->scsi_id_b, SCSIID);
- scsi_conf = aic_inb(p, SCSICONF + 1);
- aic_outb(p, DFON | SPIOEN, SXFRCTL0);
- aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term |
- ENSTIMER | ACTNEGEN, SXFRCTL1);
- aic_outb(p, 0, SIMODE0);
- aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
- aic_outb(p, 0, SCSIRATE);
-
- /* Select channel A */
- aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
- }
-
- if (p->features & AHC_ULTRA2)
- {
- aic_outb(p, p->scsi_id, SCSIID_ULTRA2);
- }
- else
- {
- aic_outb(p, p->scsi_id, SCSIID);
- }
- if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
- term = (aic_inb(p, SXFRCTL1) & STPWEN);
- else
- term = ((p->flags & (AHC_TERM_ENB_A|AHC_TERM_ENB_LVD)) ? STPWEN : 0);
- scsi_conf = aic_inb(p, SCSICONF);
- aic_outb(p, DFON | SPIOEN, SXFRCTL0);
- aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term |
- ENSTIMER | ACTNEGEN, SXFRCTL1);
- aic_outb(p, 0, SIMODE0);
- /*
- * If we are a cardbus adapter then don't enable SCSI reset detection.
- * We shouldn't likely be sharing SCSI busses with someone else, and
- * if we don't have a cable currently plugged into the controller then
- * we won't have a power source for the SCSI termination, which means
- * we'll see infinite incoming bus resets.
- */
- if(p->flags & AHC_NO_STPWEN)
- aic_outb(p, ENSELTIMO | ENSCSIPERR, SIMODE1);
- else
- aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
- aic_outb(p, 0, SCSIRATE);
- if ( p->features & AHC_ULTRA2)
- aic_outb(p, 0, SCSIOFFSET);
-
- /*
- * Look at the information that board initialization or the board
- * BIOS has left us. In the lower four bits of each target's
- * scratch space any value other than 0 indicates that we should
- * initiate synchronous transfers. If it's zero, the user or the
- * BIOS has decided to disable synchronous negotiation to that
- * target so we don't activate the needsdtr flag.
- */
- if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0)
- {
- max_targets = 8;
- }
- else
- {
- max_targets = 16;
- }
-
- if (!(aic7xxx_no_reset))
- {
- /*
- * If we reset the bus, then clear the transfer settings, else leave
- * them be.
- */
- aic_outb(p, 0, ULTRA_ENB);
- aic_outb(p, 0, ULTRA_ENB + 1);
- p->ultraenb = 0;
- }
-
- /*
- * Allocate enough hardware scbs to handle the maximum number of
- * concurrent transactions we can have. We have to make sure that
- * the allocated memory is contiguous memory. The Linux kmalloc
- * routine should only allocate contiguous memory, but note that
- * this could be a problem if kmalloc() is changed.
- */
- {
- size_t array_size;
- unsigned int hscb_physaddr;
-
- array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
- if (p->scb_data->hscbs == NULL)
- {
- /* pci_alloc_consistent enforces the alignment already and
- * clears the area as well.
- */
- p->scb_data->hscbs = pci_alloc_consistent(p->pdev, array_size,
- &p->scb_data->hscbs_dma);
- /* We have to use pci_free_consistent, not kfree */
- p->scb_data->hscb_kmalloc_ptr = NULL;
- p->scb_data->hscbs_dma_len = array_size;
- }
- if (p->scb_data->hscbs == NULL)
- {
- printk("(scsi%d) Unable to allocate hardware SCB array; "
- "failing detection.\n", p->host_no);
- aic_outb(p, 0, SIMODE1);
- p->irq = 0;
- return(0);
- }
-
- hscb_physaddr = p->scb_data->hscbs_dma;
- aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR);
- aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1);
- aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2);
- aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3);
-
- /* Set up the fifo areas at the same time */
- p->untagged_scbs = pci_alloc_consistent(p->pdev, 3*256, &p->fifo_dma);
- if (p->untagged_scbs == NULL)
- {
- printk("(scsi%d) Unable to allocate hardware FIFO arrays; "
- "failing detection.\n", p->host_no);
- p->irq = 0;
- return(0);
- }
-
- p->qoutfifo = p->untagged_scbs + 256;
- p->qinfifo = p->qoutfifo + 256;
- for (i = 0; i < 256; i++)
- {
- p->untagged_scbs[i] = SCB_LIST_NULL;
- p->qinfifo[i] = SCB_LIST_NULL;
- p->qoutfifo[i] = SCB_LIST_NULL;
- }
-
- hscb_physaddr = p->fifo_dma;
- aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR);
- aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
- aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
- aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
- }
-
- /* The Q-FIFOs we just set up are all empty */
- aic_outb(p, 0, QINPOS);
- aic_outb(p, 0, KERNEL_QINPOS);
- aic_outb(p, 0, QOUTPOS);
-
- if(p->features & AHC_QUEUE_REGS)
- {
- aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA);
- aic_outb(p, 0, SDSCB_QOFF);
- aic_outb(p, 0, SNSCB_QOFF);
- aic_outb(p, 0, HNSCB_QOFF);
- }
-
- /*
- * We don't have any waiting selections or disconnected SCBs.
- */
- aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
- aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
-
- /*
- * Message out buffer starts empty
- */
- aic_outb(p, MSG_NOOP, MSG_OUT);
- aic_outb(p, MSG_NOOP, LAST_MSG);
-
- /*
- * Set all the other asundry items that haven't been set yet.
- * This includes just dumping init values to a lot of registers simply
- * to make sure they've been touched and are ready for use parity wise
- * speaking.
- */
- aic_outb(p, 0, TMODE_CMDADDR);
- aic_outb(p, 0, TMODE_CMDADDR + 1);
- aic_outb(p, 0, TMODE_CMDADDR + 2);
- aic_outb(p, 0, TMODE_CMDADDR + 3);
- aic_outb(p, 0, TMODE_CMDADDR_NEXT);
-
- /*
- * Link us into the list of valid hosts
- */
- p->next = first_aic7xxx;
- first_aic7xxx = p;
-
- /*
- * Allocate the first set of scbs for this controller. This is to stream-
- * line code elsewhere in the driver. If we have to check for the existence
- * of scbs in certain code sections, it slows things down. However, as
- * soon as we register the IRQ for this card, we could get an interrupt that
- * includes possibly the SCSI_RSTI interrupt. If we catch that interrupt
- * then we are likely to segfault if we don't have at least one chunk of
- * SCBs allocated or add checks all through the reset code to make sure
- * that the SCBs have been allocated which is an invalid running condition
- * and therefore I think it's preferable to simply pre-allocate the first
- * chunk of SCBs.
- */
- aic7xxx_allocate_scb(p);
-
- /*
- * Load the sequencer program, then re-enable the board -
- * resetting the AIC-7770 disables it, leaving the lights
- * on with nobody home.
- */
- aic7xxx_loadseq(p);
-
- /*
- * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
- */
- aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
-
- if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
- {
- aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */
- }
-
- if ( !(aic7xxx_no_reset) )
- {
- if (p->features & AHC_TWIN)
- {
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
- aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
- aic7xxx_reset_current_bus(p);
- aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
- }
- /* Reset SCSI bus A. */
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- { /* In case we are a 3940, 3985, or 7895, print the right channel */
- char *channel = "";
- if (p->flags & AHC_MULTI_CHANNEL)
- {
- channel = " A";
- if (p->flags & (AHC_CHNLB|AHC_CHNLC))
- channel = (p->flags & AHC_CHNLB) ? " B" : " C";
- }
- printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
- }
-
- aic7xxx_reset_current_bus(p);
-
- }
- else
- {
- if (!reset_delay)
- {
- printk(KERN_INFO "(scsi%d) Not resetting SCSI bus. Note: Don't use "
- "the no_reset\n", p->host_no);
- printk(KERN_INFO "(scsi%d) option unless you have a verifiable need "
- "for it.\n", p->host_no);
- }
- }
-
- /*
- * Register IRQ with the kernel. Only allow sharing IRQs with
- * PCI devices.
- */
- if (!(p->chip & AHC_PCI))
- {
- result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
- }
- else
- {
- result = (request_irq(p->irq, do_aic7xxx_isr, IRQF_SHARED,
- "aic7xxx", p));
- if (result < 0)
- {
- result = (request_irq(p->irq, do_aic7xxx_isr, IRQF_DISABLED | IRQF_SHARED,
- "aic7xxx", p));
- }
- }
- if (result < 0)
- {
- printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring "
- "controller.\n", p->host_no, p->irq);
- aic_outb(p, 0, SIMODE1);
- p->irq = 0;
- return (0);
- }
-
- if(aic_inb(p, INTSTAT) & INT_PEND)
- printk(INFO_LEAD "spurious interrupt during configuration, cleared.\n",
- p->host_no, -1, -1 , -1);
- aic7xxx_clear_intstat(p);
-
- unpause_sequencer(p, /* unpause_always */ TRUE);
-
- return (found);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_chip_reset
- *
- * Description:
- * Perform a chip reset on the aic7xxx SCSI controller. The controller
- * is paused upon return.
- *-F*************************************************************************/
-static int
-aic7xxx_chip_reset(struct aic7xxx_host *p)
-{
- unsigned char sblkctl;
- int wait;
-
- /*
- * For some 274x boards, we must clear the CHIPRST bit and pause
- * the sequencer. For some reason, this makes the driver work.
- */
- aic_outb(p, PAUSE | CHIPRST, HCNTRL);
-
- /*
- * In the future, we may call this function as a last resort for
- * error handling. Let's be nice and not do any unnecessary delays.
- */
- wait = 1000; /* 1 msec (1000 * 1 msec) */
- while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK))
- {
- udelay(1); /* 1 usec */
- }
-
- pause_sequencer(p);
-
- sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE);
- if (p->chip & AHC_PCI)
- sblkctl &= ~SELBUSB;
- switch( sblkctl )
- {
- case 0: /* normal narrow card */
- break;
- case 2: /* Wide card */
- p->features |= AHC_WIDE;
- break;
- case 8: /* Twin card */
- p->features |= AHC_TWIN;
- p->flags |= AHC_MULTI_CHANNEL;
- break;
- default: /* hmmm...we don't know what this is */
- printk(KERN_WARNING "aic7xxx: Unsupported adapter type %d, ignoring.\n",
- aic_inb(p, SBLKCTL) & 0x0a);
- return(-1);
- }
- return(0);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_alloc
- *
- * Description:
- * Allocate and initialize a host structure. Returns NULL upon error
- * and a pointer to a aic7xxx_host struct upon success.
- *-F*************************************************************************/
-static struct aic7xxx_host *
-aic7xxx_alloc(struct scsi_host_template *sht, struct aic7xxx_host *temp)
-{
- struct aic7xxx_host *p = NULL;
- struct Scsi_Host *host;
-
- /*
- * Allocate a storage area by registering us with the mid-level
- * SCSI layer.
- */
- host = scsi_register(sht, sizeof(struct aic7xxx_host));
-
- if (host != NULL)
- {
- p = (struct aic7xxx_host *) host->hostdata;
- memset(p, 0, sizeof(struct aic7xxx_host));
- *p = *temp;
- p->host = host;
-
- p->scb_data = kzalloc(sizeof(scb_data_type), GFP_ATOMIC);
- if (p->scb_data)
- {
- scbq_init (&p->scb_data->free_scbs);
- }
- else
- {
- /*
- * For some reason we don't have enough memory. Free the
- * allocated memory for the aic7xxx_host struct, and return NULL.
- */
- release_region(p->base, MAXREG - MINREG);
- scsi_unregister(host);
- return(NULL);
- }
- p->host_no = host->host_no;
- }
- return (p);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_free
- *
- * Description:
- * Frees and releases all resources associated with an instance of
- * the driver (struct aic7xxx_host *).
- *-F*************************************************************************/
-static void
-aic7xxx_free(struct aic7xxx_host *p)
-{
- int i;
-
- /*
- * Free the allocated hardware SCB space.
- */
- if (p->scb_data != NULL)
- {
- struct aic7xxx_scb_dma *scb_dma = NULL;
- if (p->scb_data->hscbs != NULL)
- {
- pci_free_consistent(p->pdev, p->scb_data->hscbs_dma_len,
- p->scb_data->hscbs, p->scb_data->hscbs_dma);
- p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL;
- }
- /*
- * Free the driver SCBs. These were allocated on an as-need
- * basis. We allocated these in groups depending on how many
- * we could fit into a given amount of RAM. The tail SCB for
- * these allocations has a pointer to the alloced area.
- */
- for (i = 0; i < p->scb_data->numscbs; i++)
- {
- if (p->scb_data->scb_array[i]->scb_dma != scb_dma)
- {
- scb_dma = p->scb_data->scb_array[i]->scb_dma;
- pci_free_consistent(p->pdev, scb_dma->dma_len,
- (void *)((unsigned long)scb_dma->dma_address
- - scb_dma->dma_offset),
- scb_dma->dma_address);
- }
- kfree(p->scb_data->scb_array[i]->kmalloc_ptr);
- p->scb_data->scb_array[i] = NULL;
- }
-
- /*
- * Free the SCB data area.
- */
- kfree(p->scb_data);
- }
-
- pci_free_consistent(p->pdev, 3*256, (void *)p->untagged_scbs, p->fifo_dma);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_load_seeprom
- *
- * Description:
- * Load the seeprom and configure adapter and target settings.
- * Returns 1 if the load was successful and 0 otherwise.
- *-F*************************************************************************/
-static void
-aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
-{
- int have_seeprom = 0;
- int i, max_targets, mask;
- unsigned char scsirate, scsi_conf;
- unsigned short scarray[128];
- struct seeprom_config *sc = (struct seeprom_config *) scarray;
-
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
- }
- switch (p->chip)
- {
- case (AHC_AIC7770|AHC_EISA): /* None of these adapters have seeproms. */
- if (aic_inb(p, SCSICONF) & TERM_ENB)
- p->flags |= AHC_TERM_ENB_A;
- if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
- p->flags |= AHC_TERM_ENB_B;
- break;
-
- case (AHC_AIC7770|AHC_VL):
- have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
- break;
-
- default:
- have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
- scarray, p->sc_size, p->sc_type);
- if (!have_seeprom)
- {
- if(p->sc_type == C46)
- have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
- scarray, p->sc_size, C56_66);
- else
- have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
- scarray, p->sc_size, C46);
- }
- if (!have_seeprom)
- {
- p->sc_size = 128;
- have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
- scarray, p->sc_size, p->sc_type);
- if (!have_seeprom)
- {
- if(p->sc_type == C46)
- have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
- scarray, p->sc_size, C56_66);
- else
- have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
- scarray, p->sc_size, C46);
- }
- }
- break;
- }
-
- if (!have_seeprom)
- {
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk("\naic7xxx: No SEEPROM available.\n");
- }
- p->flags |= AHC_NEWEEPROM_FMT;
- if (aic_inb(p, SCSISEQ) == 0)
- {
- p->flags |= AHC_USEDEFAULTS;
- p->flags &= ~AHC_BIOS_ENABLED;
- p->scsi_id = p->scsi_id_b = 7;
- *sxfrctl1 |= STPWEN;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk("aic7xxx: Using default values.\n");
- }
- }
- else if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk("aic7xxx: Using leftover BIOS values.\n");
- }
- if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
- {
- p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
- sc->adapter_control &= ~CFAUTOTERM;
- sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
- }
- if (aic7xxx_extended)
- p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
- else
- p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
- }
- else
- {
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk("done\n");
- }
-
- /*
- * Note things in our flags
- */
- p->flags |= AHC_SEEPROM_FOUND;
-
- /*
- * Update the settings in sxfrctl1 to match the termination settings.
- */
- *sxfrctl1 = 0;
-
- /*
- * Get our SCSI ID from the SEEPROM setting...
- */
- p->scsi_id = (sc->brtime_id & CFSCSIID);
-
- /*
- * First process the settings that are different between the VLB
- * and PCI adapter seeproms.
- */
- if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770)
- {
- /* VLB adapter seeproms */
- if (sc->bios_control & CF284XEXTEND)
- p->flags |= AHC_EXTEND_TRANS_A;
-
- if (sc->adapter_control & CF284XSTERM)
- {
- *sxfrctl1 |= STPWEN;
- p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
- }
- }
- else
- {
- /* PCI adapter seeproms */
- if (sc->bios_control & CFEXTEND)
- p->flags |= AHC_EXTEND_TRANS_A;
- if (sc->bios_control & CFBIOSEN)
- p->flags |= AHC_BIOS_ENABLED;
- else
- p->flags &= ~AHC_BIOS_ENABLED;
-
- if (sc->adapter_control & CFSTERM)
- {
- *sxfrctl1 |= STPWEN;
- p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
- }
- }
- memcpy(&p->sc, sc, sizeof(struct seeprom_config));
- }
-
- p->discenable = 0;
-
- /*
- * Limit to 16 targets just in case. The 2842 for one is known to
- * blow the max_targets setting, future cards might also.
- */
- max_targets = ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8);
-
- if (have_seeprom)
- {
- for (i = 0; i < max_targets; i++)
- {
- if( ((p->features & AHC_ULTRA) &&
- !(sc->adapter_control & CFULTRAEN) &&
- (sc->device_flags[i] & CFSYNCHISULTRA)) ||
- (sc->device_flags[i] & CFNEWULTRAFORMAT) )
- {
- p->flags |= AHC_NEWEEPROM_FMT;
- break;
- }
- }
- }
-
- for (i = 0; i < max_targets; i++)
- {
- mask = (0x01 << i);
- if (!have_seeprom)
- {
- if (aic_inb(p, SCSISEQ) != 0)
- {
- /*
- * OK...the BIOS set things up and left behind the settings we need.
- * Just make our sc->device_flags[i] entry match what the card has
- * set for this device.
- */
- p->discenable =
- ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) );
- p->ultraenb =
- (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) );
- sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0;
- if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER)
- sc->device_flags[i] |= CFWIDEB;
- if (p->features & AHC_ULTRA2)
- {
- if (aic_inb(p, TARG_OFFSET + i))
- {
- sc->device_flags[i] |= CFSYNCH;
- sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07);
- if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 )
- sc->device_flags[i] |= CFSYNCHISULTRA;
- }
- }
- else
- {
- if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER)
- {
- sc->device_flags[i] |= CFSYNCH;
- if (p->features & AHC_ULTRA)
- sc->device_flags[i] |= ((p->ultraenb & mask) ?
- CFSYNCHISULTRA : 0);
- }
- }
- }
- else
- {
- /*
- * Assume the BIOS has NOT been run on this card and nothing between
- * the card and the devices is configured yet.
- */
- sc->device_flags[i] = CFDISC;
- if (p->features & AHC_WIDE)
- sc->device_flags[i] |= CFWIDEB;
- if (p->features & AHC_ULTRA3)
- sc->device_flags[i] |= 2;
- else if (p->features & AHC_ULTRA2)
- sc->device_flags[i] |= 3;
- else if (p->features & AHC_ULTRA)
- sc->device_flags[i] |= CFSYNCHISULTRA;
- sc->device_flags[i] |= CFSYNCH;
- aic_outb(p, 0, TARG_SCSIRATE + i);
- if (p->features & AHC_ULTRA2)
- aic_outb(p, 0, TARG_OFFSET + i);
- }
- }
- if (sc->device_flags[i] & CFDISC)
- {
- p->discenable |= mask;
- }
- if (p->flags & AHC_NEWEEPROM_FMT)
- {
- if ( !(p->features & AHC_ULTRA2) )
- {
- /*
- * I know of two different Ultra BIOSes that do this differently.
- * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
- * be == to 0x03 and SYNCHISULTRA to be true to mean 40MByte/s
- * while on the IBM Netfinity 5000 they want the same thing
- * to be something else, while flags[i] & CFXFER == 0x03 and
- * SYNCHISULTRA false should be 40MByte/s. So, we set both to
- * 40MByte/s and the lower speeds be damned. People will have
- * to select around the conversely mapped lower speeds in order
- * to select lower speeds on these boards.
- */
- if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
- ((sc->device_flags[i] & CFXFER) == 0x03) )
- {
- sc->device_flags[i] &= ~CFXFER;
- sc->device_flags[i] |= CFSYNCHISULTRA;
- }
- if (sc->device_flags[i] & CFSYNCHISULTRA)
- {
- p->ultraenb |= mask;
- }
- }
- else if ( !(sc->device_flags[i] & CFNEWULTRAFORMAT) &&
- (p->features & AHC_ULTRA2) &&
- (sc->device_flags[i] & CFSYNCHISULTRA) )
- {
- p->ultraenb |= mask;
- }
- }
- else if (sc->adapter_control & CFULTRAEN)
- {
- p->ultraenb |= mask;
- }
- if ( (sc->device_flags[i] & CFSYNCH) == 0)
- {
- sc->device_flags[i] &= ~CFXFER;
- p->ultraenb &= ~mask;
- p->user[i].offset = 0;
- p->user[i].period = 0;
- p->user[i].options = 0;
- }
- else
- {
- if (p->features & AHC_ULTRA3)
- {
- p->user[i].offset = MAX_OFFSET_ULTRA2;
- if( (sc->device_flags[i] & CFXFER) < 0x03 )
- {
- scsirate = (sc->device_flags[i] & CFXFER);
- p->user[i].options = MSG_EXT_PPR_OPTION_DT_CRC;
- }
- else
- {
- scsirate = (sc->device_flags[i] & CFXFER) |
- ((p->ultraenb & mask) ? 0x18 : 0x10);
- p->user[i].options = 0;
- }
- p->user[i].period = aic7xxx_find_period(p, scsirate,
- AHC_SYNCRATE_ULTRA3);
- }
- else if (p->features & AHC_ULTRA2)
- {
- p->user[i].offset = MAX_OFFSET_ULTRA2;
- scsirate = (sc->device_flags[i] & CFXFER) |
- ((p->ultraenb & mask) ? 0x18 : 0x10);
- p->user[i].options = 0;
- p->user[i].period = aic7xxx_find_period(p, scsirate,
- AHC_SYNCRATE_ULTRA2);
- }
- else
- {
- scsirate = (sc->device_flags[i] & CFXFER) << 4;
- p->user[i].options = 0;
- p->user[i].offset = MAX_OFFSET_8BIT;
- if (p->features & AHC_ULTRA)
- {
- short ultraenb;
- ultraenb = aic_inb(p, ULTRA_ENB) |
- (aic_inb(p, ULTRA_ENB + 1) << 8);
- p->user[i].period = aic7xxx_find_period(p, scsirate,
- (p->ultraenb & mask) ?
- AHC_SYNCRATE_ULTRA :
- AHC_SYNCRATE_FAST);
- }
- else
- p->user[i].period = aic7xxx_find_period(p, scsirate,
- AHC_SYNCRATE_FAST);
- }
- }
- if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) )
- {
- p->user[i].width = MSG_EXT_WDTR_BUS_16_BIT;
- }
- else
- {
- p->user[i].width = MSG_EXT_WDTR_BUS_8_BIT;
- }
- }
- aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
- aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
-
- /*
- * We set the p->ultraenb from the SEEPROM to begin with, but now we make
- * it match what is already down in the card. If we are doing a reset
- * on the card then this will get put back to a default state anyway.
- * This allows us to not have to pre-emptively negotiate when using the
- * no_reset option.
- */
- if (p->features & AHC_ULTRA)
- p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8);
-
-
- scsi_conf = (p->scsi_id & HSCSIID);
-
- if(have_seeprom)
- {
- p->adapter_control = sc->adapter_control;
- p->bios_control = sc->bios_control;
-
- switch (p->chip & AHC_CHIPID_MASK)
- {
- case AHC_AIC7895:
- case AHC_AIC7896:
- case AHC_AIC7899:
- if (p->adapter_control & CFBPRIMARY)
- p->flags |= AHC_CHANNEL_B_PRIMARY;
- default:
- break;
- }
-
- if (sc->adapter_control & CFSPARITY)
- scsi_conf |= ENSPCHK;
- }
- else
- {
- scsi_conf |= ENSPCHK | RESET_SCSI;
- }
-
- /*
- * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card.
- * The 2842 and 2742 cards already have these registers set and we don't
- * want to muck with them since we don't set all the bits they do.
- */
- if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
- {
- /* Set the host ID */
- aic_outb(p, scsi_conf, SCSICONF);
- /* In case we are a wide card */
- aic_outb(p, p->scsi_id, SCSICONF + 1);
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_configure_bugs
- *
- * Description:
- * Take the card passed in and set the appropriate bug flags based upon
- * the card model. Also make any changes needed to device registers or
- * PCI registers while we are here.
- *-F*************************************************************************/
-static void
-aic7xxx_configure_bugs(struct aic7xxx_host *p)
-{
- unsigned short tmp_word;
-
- switch(p->chip & AHC_CHIPID_MASK)
- {
- case AHC_AIC7860:
- p->bugs |= AHC_BUG_PCI_2_1_RETRY;
- /* fall through */
- case AHC_AIC7850:
- case AHC_AIC7870:
- p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI;
- break;
- case AHC_AIC7880:
- p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_PCI_2_1_RETRY |
- AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI;
- break;
- case AHC_AIC7890:
- p->bugs |= AHC_BUG_AUTOFLUSH | AHC_BUG_CACHETHEN;
- break;
- case AHC_AIC7892:
- p->bugs |= AHC_BUG_SCBCHAN_UPLOAD;
- break;
- case AHC_AIC7895:
- p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_PCI_2_1_RETRY |
- AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI;
- break;
- case AHC_AIC7896:
- p->bugs |= AHC_BUG_CACHETHEN_DIS;
- break;
- case AHC_AIC7899:
- p->bugs |= AHC_BUG_SCBCHAN_UPLOAD;
- break;
- default:
- /* Nothing to do */
- break;
- }
-
- /*
- * Now handle the bugs that require PCI register or card register tweaks
- */
- pci_read_config_word(p->pdev, PCI_COMMAND, &tmp_word);
- if(p->bugs & AHC_BUG_PCI_MWI)
- {
- tmp_word &= ~PCI_COMMAND_INVALIDATE;
- }
- else
- {
- tmp_word |= PCI_COMMAND_INVALIDATE;
- }
- pci_write_config_word(p->pdev, PCI_COMMAND, tmp_word);
-
- if(p->bugs & AHC_BUG_CACHETHEN)
- {
- aic_outb(p, aic_inb(p, DSCOMMAND0) & ~CACHETHEN, DSCOMMAND0);
- }
- else if (p->bugs & AHC_BUG_CACHETHEN_DIS)
- {
- aic_outb(p, aic_inb(p, DSCOMMAND0) | CACHETHEN, DSCOMMAND0);
- }
-
- return;
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_detect
- *
- * Description:
- * Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
- *
- * XXX - This should really be called aic7xxx_probe(). A sequence of
- * probe(), attach()/detach(), and init() makes more sense than
- * one do-it-all function. This may be useful when (and if) the
- * mid-level SCSI code is overhauled.
- *-F*************************************************************************/
-static int
-aic7xxx_detect(struct scsi_host_template *template)
-{
- struct aic7xxx_host *temp_p = NULL;
- struct aic7xxx_host *current_p = NULL;
- struct aic7xxx_host *list_p = NULL;
- int found = 0;
-#if defined(__i386__) || defined(__alpha__)
- ahc_flag_type flags = 0;
- int type;
-#endif
- unsigned char sxfrctl1;
-#if defined(__i386__) || defined(__alpha__)
- unsigned char hcntrl, hostconf;
- unsigned int slot, base;
-#endif
-
-#ifdef MODULE
- /*
- * If we are called as a module, the aic7xxx pointer may not be null
- * and it would point to our bootup string, just like on the lilo
- * command line. IF not NULL, then process this config string with
- * aic7xxx_setup
- */
- if(aic7xxx)
- aic7xxx_setup(aic7xxx);
-#endif
-
- template->proc_name = "aic7xxx";
- template->sg_tablesize = AIC7XXX_MAX_SG;
-
-
-#ifdef CONFIG_PCI
- /*
- * PCI-bus probe.
- */
- {
- static struct
- {
- unsigned short vendor_id;
- unsigned short device_id;
- ahc_chip chip;
- ahc_flag_type flags;
- ahc_feature features;
- int board_name_index;
- unsigned short seeprom_size;
- unsigned short seeprom_type;
- } const aic_pdevs[] = {
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
- AHC_FNONE, AHC_FENONE, 1,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
- AHC_PAGESCBS, AHC_AIC7850_FE, 5,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
- AHC_PAGESCBS, AHC_AIC7850_FE, 6,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7860_FE, 7,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7860_FE, 7,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7860_FE, 7,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7860_FE, 7,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
- AHC_AIC7860_FE, 7,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7860_FE, 8,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
- AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
- AHC_AIC7870_FE, 9,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 10,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870,
- AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7870_FE, 11,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870,
- AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7870_FE, 12,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 13,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
- AHC_AIC7880_FE, 14,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 15,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7880_FE, 16,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7880_FE, 17,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 19,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
- AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7895_FE, 20,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7890_FE, 21,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7890_FE, 21,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7890_FE, 22,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7890_FE, 23,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7896_FE, 24,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7896_FE, 25,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7896_FE, 26,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWEN,
- AHC_AIC7860_FE, 27,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7892_FE, 28,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7892_FE, 28,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7892_FE, 28,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
- AHC_AIC7892_FE, 28,
- 32, C46 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7899_FE, 29,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7899_FE, 29,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7899_FE, 29,
- 32, C56_66 },
- {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
- AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
- AHC_AIC7899_FE, 29,
- 32, C56_66 },
- };
-
- unsigned short command;
- unsigned int devconfig, i, oldverbose;
- struct pci_dev *pdev = NULL;
-
- for (i = 0; i < ARRAY_SIZE(aic_pdevs); i++)
- {
- pdev = NULL;
- while ((pdev = pci_get_device(aic_pdevs[i].vendor_id,
- aic_pdevs[i].device_id,
- pdev))) {
- if (pci_enable_device(pdev))
- continue;
- if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
- {
- if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
- {
- printk(KERN_INFO "aic7xxx: The 7810 RAID controller is not "
- "supported by\n");
- printk(KERN_INFO " this driver, we are ignoring it.\n");
- }
- }
- else if ( (temp_p = kzalloc(sizeof(struct aic7xxx_host),
- GFP_ATOMIC)) != NULL )
- {
- temp_p->chip = aic_pdevs[i].chip | AHC_PCI;
- temp_p->flags = aic_pdevs[i].flags;
- temp_p->features = aic_pdevs[i].features;
- temp_p->board_name_index = aic_pdevs[i].board_name_index;
- temp_p->sc_size = aic_pdevs[i].seeprom_size;
- temp_p->sc_type = aic_pdevs[i].seeprom_type;
-
- /*
- * Read sundry information from PCI BIOS.
- */
- temp_p->irq = pdev->irq;
- temp_p->pdev = pdev;
- temp_p->pci_bus = pdev->bus->number;
- temp_p->pci_device_fn = pdev->devfn;
- temp_p->base = pci_resource_start(pdev, 0);
- temp_p->mbase = pci_resource_start(pdev, 1);
- current_p = list_p;
- while(current_p && temp_p)
- {
- if ( ((current_p->pci_bus == temp_p->pci_bus) &&
- (current_p->pci_device_fn == temp_p->pci_device_fn)) ||
- (temp_p->base && (current_p->base == temp_p->base)) ||
- (temp_p->mbase && (current_p->mbase == temp_p->mbase)) )
- {
- /* duplicate PCI entry, skip it */
- kfree(temp_p);
- temp_p = NULL;
- continue;
- }
- current_p = current_p->next;
- }
- if(pci_request_regions(temp_p->pdev, "aic7xxx"))
- {
- printk("aic7xxx: <%s> at PCI %d/%d/%d\n",
- board_names[aic_pdevs[i].board_name_index],
- temp_p->pci_bus,
- PCI_SLOT(temp_p->pci_device_fn),
- PCI_FUNC(temp_p->pci_device_fn));
- printk("aic7xxx: I/O ports already in use, ignoring.\n");
- kfree(temp_p);
- continue;
- }
-
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk("aic7xxx: <%s> at PCI %d/%d\n",
- board_names[aic_pdevs[i].board_name_index],
- PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn));
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
- (int)command);
- }
-#ifdef AIC7XXX_STRICT_PCI_SETUP
- command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
- PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
-#else
- command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
-#endif
- command &= ~PCI_COMMAND_INVALIDATE;
- if (aic7xxx_pci_parity == 0)
- command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
- pci_write_config_word(pdev, PCI_COMMAND, command);
-#ifdef AIC7XXX_STRICT_PCI_SETUP
- pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
- }
- devconfig |= 0x80000040;
- pci_write_config_dword(pdev, DEVCONFIG, devconfig);
-#endif /* AIC7XXX_STRICT_PCI_SETUP */
-
- temp_p->unpause = INTEN;
- temp_p->pause = temp_p->unpause | PAUSE;
- if ( ((temp_p->base == 0) &&
- (temp_p->mbase == 0)) ||
- (temp_p->irq == 0) )
- {
- printk("aic7xxx: <%s> at PCI %d/%d/%d\n",
- board_names[aic_pdevs[i].board_name_index],
- temp_p->pci_bus,
- PCI_SLOT(temp_p->pci_device_fn),
- PCI_FUNC(temp_p->pci_device_fn));
- printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
- goto skip_pci_controller;
- }
-
-#ifdef MMAPIO
- if ( !(temp_p->base) || !(temp_p->flags & AHC_MULTI_CHANNEL) ||
- ((temp_p->chip != (AHC_AIC7870 | AHC_PCI)) &&
- (temp_p->chip != (AHC_AIC7880 | AHC_PCI))) )
- {
- temp_p->maddr = ioremap_nocache(temp_p->mbase, 256);
- if(temp_p->maddr)
- {
- /*
- * We need to check the I/O with the MMAPed address. Some machines
- * simply fail to work with MMAPed I/O and certain controllers.
- */
- if(aic_inb(temp_p, HCNTRL) == 0xff)
- {
- /*
- * OK.....we failed our test....go back to programmed I/O
- */
- printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n",
- board_names[aic_pdevs[i].board_name_index],
- temp_p->pci_bus,
- PCI_SLOT(temp_p->pci_device_fn),
- PCI_FUNC(temp_p->pci_device_fn));
- printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
- "Programmed I/O.\n");
- iounmap(temp_p->maddr);
- temp_p->maddr = NULL;
- if(temp_p->base == 0)
- {
- printk("aic7xxx: <%s> at PCI %d/%d/%d\n",
- board_names[aic_pdevs[i].board_name_index],
- temp_p->pci_bus,
- PCI_SLOT(temp_p->pci_device_fn),
- PCI_FUNC(temp_p->pci_device_fn));
- printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
- goto skip_pci_controller;
- }
- }
- }
- }
-#endif
-
- /*
- * We HAVE to make sure the first pause_sequencer() and all other
- * subsequent I/O that isn't PCI config space I/O takes place
- * after the MMAPed I/O region is configured and tested. The
- * problem is the PowerPC architecture that doesn't support
- * programmed I/O at all, so we have to have the MMAP I/O set up
- * for this pause to even work on those machines.
- */
- pause_sequencer(temp_p);
-
- /*
- * Clear out any pending PCI error status messages. Also set
- * verbose to 0 so that we don't emit strange PCI error messages
- * while cleaning out the current status bits.
- */
- oldverbose = aic7xxx_verbose;
- aic7xxx_verbose = 0;
- aic7xxx_pci_intr(temp_p);
- aic7xxx_verbose = oldverbose;
-
- temp_p->bios_address = 0;
-
- /*
- * Remember how the card was setup in case there is no seeprom.
- */
- if (temp_p->features & AHC_ULTRA2)
- temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID;
- else
- temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
- /*
- * Get current termination setting
- */
- sxfrctl1 = aic_inb(temp_p, SXFRCTL1);
-
- if (aic7xxx_chip_reset(temp_p) == -1)
- {
- goto skip_pci_controller;
- }
- /*
- * Very quickly put the term setting back into the register since
- * the chip reset may cause odd things to happen. This is to keep
- * LVD busses with lots of drives from draining the power out of
- * the diffsense line before we get around to running the
- * configure_termination() function. Also restore the STPWLEVEL
- * bit of DEVCONFIG
- */
- aic_outb(temp_p, sxfrctl1, SXFRCTL1);
- pci_write_config_dword(temp_p->pdev, DEVCONFIG, devconfig);
- sxfrctl1 &= STPWEN;
-
- /*
- * We need to set the CHNL? assignments before loading the SEEPROM
- * The 3940 and 3985 cards (original stuff, not any of the later
- * stuff) are 7870 and 7880 class chips. The Ultra2 stuff falls
- * under 7896 and 7897. The 7895 is in a class by itself :)
- */
- switch (temp_p->chip & AHC_CHIPID_MASK)
- {
- case AHC_AIC7870: /* 3840 / 3985 */
- case AHC_AIC7880: /* 3840 UW / 3985 UW */
- if(temp_p->flags & AHC_MULTI_CHANNEL)
- {
- switch(PCI_SLOT(temp_p->pci_device_fn))
- {
- case 5:
- temp_p->flags |= AHC_CHNLB;
- break;
- case 8:
- temp_p->flags |= AHC_CHNLB;
- break;
- case 12:
- temp_p->flags |= AHC_CHNLC;
- break;
- default:
- break;
- }
- }
- break;
-
- case AHC_AIC7895: /* 7895 */
- case AHC_AIC7896: /* 7896/7 */
- case AHC_AIC7899: /* 7899 */
- if (PCI_FUNC(pdev->devfn) != 0)
- {
- temp_p->flags |= AHC_CHNLB;
- }
- /*
- * The 7895 is the only chipset that sets the SCBSIZE32 param
- * in the DEVCONFIG register. The Ultra2 chipsets use
- * the DSCOMMAND0 register instead.
- */
- if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
- {
- pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
- devconfig |= SCBSIZE32;
- pci_write_config_dword(pdev, DEVCONFIG, devconfig);
- }
- break;
- default:
- break;
- }
-
- /*
- * Loading of the SEEPROM needs to come after we've set the flags
- * to indicate possible CHNLB and CHNLC assigments. Otherwise,
- * on 394x and 398x cards we'll end up reading the wrong settings
- * for channels B and C
- */
- switch (temp_p->chip & AHC_CHIPID_MASK)
- {
- case AHC_AIC7892:
- case AHC_AIC7899:
- aic_outb(temp_p, 0, SCAMCTL);
- /*
- * Switch to the alt mode of the chip...
- */
- aic_outb(temp_p, aic_inb(temp_p, SFUNCT) | ALT_MODE, SFUNCT);
- /*
- * Set our options...the last two items set our CRC after x byte
- * count in target mode...
- */
- aic_outb(temp_p, AUTO_MSGOUT_DE | DIS_MSGIN_DUALEDGE, OPTIONMODE);
- aic_outb(temp_p, 0x00, 0x0b);
- aic_outb(temp_p, 0x10, 0x0a);
- /*
- * switch back to normal mode...
- */
- aic_outb(temp_p, aic_inb(temp_p, SFUNCT) & ~ALT_MODE, SFUNCT);
- aic_outb(temp_p, CRCVALCHKEN | CRCENDCHKEN | CRCREQCHKEN |
- TARGCRCENDEN | TARGCRCCNTEN,
- CRCCONTROL1);
- aic_outb(temp_p, ((aic_inb(temp_p, DSCOMMAND0) | USCBSIZE32 |
- MPARCKEN | CIOPARCKEN | CACHETHEN) &
- ~DPARCKEN), DSCOMMAND0);
- aic7xxx_load_seeprom(temp_p, &sxfrctl1);
- break;
- case AHC_AIC7890:
- case AHC_AIC7896:
- aic_outb(temp_p, 0, SCAMCTL);
- aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
- CACHETHEN | MPARCKEN | USCBSIZE32 |
- CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
- aic7xxx_load_seeprom(temp_p, &sxfrctl1);
- break;
- case AHC_AIC7850:
- case AHC_AIC7860:
- /*
- * Set the DSCOMMAND0 register on these cards different from
- * on the 789x cards. Also, read the SEEPROM as well.
- */
- aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
- CACHETHEN | MPARCKEN) & ~DPARCKEN,
- DSCOMMAND0);
- /* FALLTHROUGH */
- default:
- aic7xxx_load_seeprom(temp_p, &sxfrctl1);
- break;
- case AHC_AIC7880:
- /*
- * Check the rev of the chipset before we change DSCOMMAND0
- */
- pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
- if ((devconfig & 0xff) >= 1)
- {
- aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
- CACHETHEN | MPARCKEN) & ~DPARCKEN,
- DSCOMMAND0);
- }
- aic7xxx_load_seeprom(temp_p, &sxfrctl1);
- break;
- }
-
-
- /*
- * and then we need another switch based on the type in order to
- * make sure the channel B primary flag is set properly on 7895
- * controllers....Arrrgggghhh!!! We also have to catch the fact
- * that when you disable the BIOS on the 7895 on the Intel DK440LX
- * motherboard, and possibly others, it only sets the BIOS disabled
- * bit on the A channel...I think I'm starting to lean towards
- * going postal....
- */
- switch(temp_p->chip & AHC_CHIPID_MASK)
- {
- case AHC_AIC7895:
- case AHC_AIC7896:
- case AHC_AIC7899:
- current_p = list_p;
- while(current_p != NULL)
- {
- if ( (current_p->pci_bus == temp_p->pci_bus) &&
- (PCI_SLOT(current_p->pci_device_fn) ==
- PCI_SLOT(temp_p->pci_device_fn)) )
- {
- if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
- {
- temp_p->flags |=
- (current_p->flags & AHC_CHANNEL_B_PRIMARY);
- temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
- temp_p->flags |=
- (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
- }
- else
- {
- current_p->flags |=
- (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
- current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
- current_p->flags |=
- (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
- }
- }
- current_p = current_p->next;
- }
- break;
- default:
- break;
- }
-
- /*
- * We only support external SCB RAM on the 7895/6/7 chipsets.
- * We could support it on the 7890/1 easy enough, but I don't
- * know of any 7890/1 based cards that have it. I do know
- * of 7895/6/7 cards that have it and they work properly.
- */
- switch(temp_p->chip & AHC_CHIPID_MASK)
- {
- default:
- break;
- case AHC_AIC7895:
- case AHC_AIC7896:
- case AHC_AIC7899:
- pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
- if (temp_p->features & AHC_ULTRA2)
- {
- if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) &&
- (aic7xxx_scbram) )
- {
- aic_outb(temp_p,
- aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2,
- DSCOMMAND0);
- temp_p->flags |= AHC_EXTERNAL_SRAM;
- devconfig |= EXTSCBPEN;
- }
- else if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2)
- {
- printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n",
- board_names[aic_pdevs[i].board_name_index],
- temp_p->pci_bus,
- PCI_SLOT(temp_p->pci_device_fn),
- PCI_FUNC(temp_p->pci_device_fn));
- printk("aic7xxx: external SCB RAM detected, "
- "but not enabled\n");
- }
- }
- else
- {
- if ((devconfig & RAMPSM) && (aic7xxx_scbram))
- {
- devconfig &= ~SCBRAMSEL;
- devconfig |= EXTSCBPEN;
- temp_p->flags |= AHC_EXTERNAL_SRAM;
- }
- else if (devconfig & RAMPSM)
- {
- printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n",
- board_names[aic_pdevs[i].board_name_index],
- temp_p->pci_bus,
- PCI_SLOT(temp_p->pci_device_fn),
- PCI_FUNC(temp_p->pci_device_fn));
- printk("aic7xxx: external SCB RAM detected, "
- "but not enabled\n");
- }
- }
- pci_write_config_dword(pdev, DEVCONFIG, devconfig);
- if ( (temp_p->flags & AHC_EXTERNAL_SRAM) &&
- (temp_p->flags & AHC_CHNLB) )
- aic_outb(temp_p, 1, CCSCBBADDR);
- break;
- }
-
- /*
- * Take the LED out of diagnostic mode
- */
- aic_outb(temp_p,
- (aic_inb(temp_p, SBLKCTL) & ~(DIAGLEDEN | DIAGLEDON)),
- SBLKCTL);
-
- /*
- * We don't know where this is set in the SEEPROM or by the
- * BIOS, so we default to 100%. On Ultra2 controllers, use 75%
- * instead.
- */
- if (temp_p->features & AHC_ULTRA2)
- {
- aic_outb(temp_p, RD_DFTHRSH_MAX | WR_DFTHRSH_MAX, DFF_THRSH);
- }
- else
- {
- aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
- }
-
- /*
- * Call our function to fixup any bugs that exist on this chipset.
- * This may muck with PCI settings and other device settings, so
- * make sure it's after all the other PCI and device register
- * tweaks so it can back out bad settings on specific broken cards.
- */
- aic7xxx_configure_bugs(temp_p);
-
- /* Hold a pci device reference */
- pci_dev_get(temp_p->pdev);
-
- if ( list_p == NULL )
- {
- list_p = current_p = temp_p;
- }
- else
- {
- current_p = list_p;
- while(current_p->next != NULL)
- current_p = current_p->next;
- current_p->next = temp_p;
- }
- temp_p->next = NULL;
- found++;
- continue;
-skip_pci_controller:
-#ifdef CONFIG_PCI
- pci_release_regions(temp_p->pdev);
-#endif
- kfree(temp_p);
- } /* Found an Adaptec PCI device. */
- else /* Well, we found one, but we couldn't get any memory */
- {
- printk("aic7xxx: Found <%s>\n",
- board_names[aic_pdevs[i].board_name_index]);
- printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
- "skipping.\n");
- }
- } /* while(pdev=....) */
- } /* for PCI_DEVICES */
- }
-#endif /* CONFIG_PCI */
-
-#if defined(__i386__) || defined(__alpha__)
- /*
- * EISA/VL-bus card signature probe.
- */
- slot = MINSLOT;
- while ( (slot <= MAXSLOT) &&
- !(aic7xxx_no_probe) )
- {
- base = SLOTBASE(slot) + MINREG;
-
- if (!request_region(base, MAXREG - MINREG, "aic7xxx"))
- {
- /*
- * Some other driver has staked a
- * claim to this i/o region already.
- */
- slot++;
- continue; /* back to the beginning of the for loop */
- }
- flags = 0;
- type = aic7xxx_probe(slot, base + AHC_HID0, &flags);
- if (type == -1)
- {
- release_region(base, MAXREG - MINREG);
- slot++;
- continue;
- }
- temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
- if (temp_p == NULL)
- {
- printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
- release_region(base, MAXREG - MINREG);
- slot++;
- continue; /* back to the beginning of the while loop */
- }
-
- /*
- * Pause the card preserving the IRQ type. Allow the operator
- * to override the IRQ trigger.
- */
- if (aic7xxx_irq_trigger == 1)
- hcntrl = IRQMS; /* Level */
- else if (aic7xxx_irq_trigger == 0)
- hcntrl = 0; /* Edge */
- else
- hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
- memset(temp_p, 0, sizeof(struct aic7xxx_host));
- temp_p->unpause = hcntrl | INTEN;
- temp_p->pause = hcntrl | PAUSE | INTEN;
- temp_p->base = base;
- temp_p->mbase = 0;
- temp_p->maddr = NULL;
- temp_p->pci_bus = 0;
- temp_p->pci_device_fn = slot;
- aic_outb(temp_p, hcntrl | PAUSE, HCNTRL);
- while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
- if (aic7xxx_chip_reset(temp_p) == -1)
- temp_p->irq = 0;
- else
- temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
- temp_p->flags |= AHC_PAGESCBS;
-
- switch (temp_p->irq)
- {
- case 9:
- case 10:
- case 11:
- case 12:
- case 14:
- case 15:
- break;
-
- default:
- printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
- "level %d, ignoring.\n", temp_p->irq);
- kfree(temp_p);
- release_region(base, MAXREG - MINREG);
- slot++;
- continue; /* back to the beginning of the while loop */
- }
-
- /*
- * We are committed now, everything has been checked and this card
- * has been found, now we just set it up
- */
-
- /*
- * Insert our new struct into the list at the end
- */
- if (list_p == NULL)
- {
- list_p = current_p = temp_p;
- }
- else
- {
- current_p = list_p;
- while (current_p->next != NULL)
- current_p = current_p->next;
- current_p->next = temp_p;
- }
-
- switch (type)
- {
- case 0:
- temp_p->board_name_index = 2;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk("aic7xxx: <%s> at EISA %d\n",
- board_names[2], slot);
- /* FALLTHROUGH */
- case 1:
- {
- temp_p->chip = AHC_AIC7770 | AHC_EISA;
- temp_p->features |= AHC_AIC7770_FE;
- temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL);
-
- /*
- * Get the primary channel information. Right now we don't
- * do anything with this, but someday we will be able to inform
- * the mid-level SCSI code which channel is primary.
- */
- if (temp_p->board_name_index == 0)
- {
- temp_p->board_name_index = 3;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk("aic7xxx: <%s> at EISA %d\n",
- board_names[3], slot);
- }
- if (temp_p->bios_control & CHANNEL_B_PRIMARY)
- {
- temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
- }
-
- if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
- {
- temp_p->flags &= ~AHC_BIOS_ENABLED;
- }
- else
- {
- temp_p->flags &= ~AHC_USEDEFAULTS;
- temp_p->flags |= AHC_BIOS_ENABLED;
- if ( (temp_p->bios_control & 0x20) == 0 )
- {
- temp_p->bios_address = 0xcc000;
- temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07));
- }
- else
- {
- temp_p->bios_address = 0xd0000;
- temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06));
- }
- }
- temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
- temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
- if (temp_p->features & AHC_WIDE)
- {
- temp_p->scsi_id = temp_p->adapter_control & HWSCSIID;
- temp_p->scsi_id_b = temp_p->scsi_id;
- }
- else
- {
- temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
- temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
- }
- aic7xxx_load_seeprom(temp_p, &sxfrctl1);
- break;
- }
-
- case 2:
- case 3:
- temp_p->chip = AHC_AIC7770 | AHC_VL;
- temp_p->features |= AHC_AIC7770_FE;
- if (type == 2)
- temp_p->flags |= AHC_BIOS_ENABLED;
- else
- temp_p->flags &= ~AHC_BIOS_ENABLED;
- if (aic_inb(temp_p, SCSICONF) & TERM_ENB)
- sxfrctl1 = STPWEN;
- aic7xxx_load_seeprom(temp_p, &sxfrctl1);
- temp_p->board_name_index = 4;
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- printk("aic7xxx: <%s> at VLB %d\n",
- board_names[2], slot);
- switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
- {
- case 0x00:
- temp_p->bios_address = 0xe0000;
- break;
- case 0x20:
- temp_p->bios_address = 0xc8000;
- break;
- case 0x40:
- temp_p->bios_address = 0xd0000;
- break;
- case 0x60:
- temp_p->bios_address = 0xd8000;
- break;
- default:
- break; /* can't get here */
- }
- break;
-
- default: /* Won't get here. */
- break;
- }
- if (aic7xxx_verbose & VERBOSE_PROBE2)
- {
- printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n",
- (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base,
- temp_p->irq,
- (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered");
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis");
- }
-
- /*
- * All the 7770 based chipsets have this bug
- */
- temp_p->bugs |= AHC_BUG_TMODE_WIDEODD;
-
- /*
- * Set the FIFO threshold and the bus off time.
- */
- hostconf = aic_inb(temp_p, HOSTCONF);
- aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD);
- aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME);
- slot++;
- found++;
- }
-
-#endif /* defined(__i386__) || defined(__alpha__) */
-
- /*
- * Now, we re-order the probed devices by BIOS address and BUS class.
- * In general, we follow this algorithm to make the adapters show up
- * in the same order under linux that the computer finds them.
- * 1: All VLB/EISA cards with BIOS_ENABLED first, according to BIOS
- * address, going from lowest to highest.
- * 2: All PCI controllers with BIOS_ENABLED next, according to BIOS
- * address, going from lowest to highest.
- * 3: Remaining VLB/EISA controllers going in slot order.
- * 4: Remaining PCI controllers, going in PCI device order (reversible)
- */
-
- {
- struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL };
- struct aic7xxx_host *vlb, *pci;
- struct aic7xxx_host *prev_p;
- struct aic7xxx_host *p;
- unsigned char left;
-
- prev_p = vlb = pci = NULL;
-
- temp_p = list_p;
- while (temp_p != NULL)
- {
- switch(temp_p->chip & ~AHC_CHIPID_MASK)
- {
- case AHC_EISA:
- case AHC_VL:
- {
- p = temp_p;
- if (p->flags & AHC_BIOS_ENABLED)
- vlb = sort_list[0];
- else
- vlb = sort_list[2];
-
- if (vlb == NULL)
- {
- vlb = temp_p;
- temp_p = temp_p->next;
- vlb->next = NULL;
- }
- else
- {
- current_p = vlb;
- prev_p = NULL;
- while ( (current_p != NULL) &&
- (current_p->bios_address < temp_p->bios_address))
- {
- prev_p = current_p;
- current_p = current_p->next;
- }
- if (prev_p != NULL)
- {
- prev_p->next = temp_p;
- temp_p = temp_p->next;
- prev_p->next->next = current_p;
- }
- else
- {
- vlb = temp_p;
- temp_p = temp_p->next;
- vlb->next = current_p;
- }
- }
-
- if (p->flags & AHC_BIOS_ENABLED)
- sort_list[0] = vlb;
- else
- sort_list[2] = vlb;
-
- break;
- }
- default: /* All PCI controllers fall through to default */
- {
-
- p = temp_p;
- if (p->flags & AHC_BIOS_ENABLED)
- pci = sort_list[1];
- else
- pci = sort_list[3];
-
- if (pci == NULL)
- {
- pci = temp_p;
- temp_p = temp_p->next;
- pci->next = NULL;
- }
- else
- {
- current_p = pci;
- prev_p = NULL;
- if (!aic7xxx_reverse_scan)
- {
- while ( (current_p != NULL) &&
- ( (PCI_SLOT(current_p->pci_device_fn) |
- (current_p->pci_bus << 8)) <
- (PCI_SLOT(temp_p->pci_device_fn) |
- (temp_p->pci_bus << 8)) ) )
- {
- prev_p = current_p;
- current_p = current_p->next;
- }
- }
- else
- {
- while ( (current_p != NULL) &&
- ( (PCI_SLOT(current_p->pci_device_fn) |
- (current_p->pci_bus << 8)) >
- (PCI_SLOT(temp_p->pci_device_fn) |
- (temp_p->pci_bus << 8)) ) )
- {
- prev_p = current_p;
- current_p = current_p->next;
- }
- }
- /*
- * Are we dealing with a 7895/6/7/9 where we need to sort the
- * channels as well, if so, the bios_address values should
- * be the same
- */
- if ( (current_p) && (temp_p->flags & AHC_MULTI_CHANNEL) &&
- (temp_p->pci_bus == current_p->pci_bus) &&
- (PCI_SLOT(temp_p->pci_device_fn) ==
- PCI_SLOT(current_p->pci_device_fn)) )
- {
- if (temp_p->flags & AHC_CHNLB)
- {
- if ( !(temp_p->flags & AHC_CHANNEL_B_PRIMARY) )
- {
- prev_p = current_p;
- current_p = current_p->next;
- }
- }
- else
- {
- if (temp_p->flags & AHC_CHANNEL_B_PRIMARY)
- {
- prev_p = current_p;
- current_p = current_p->next;
- }
- }
- }
- if (prev_p != NULL)
- {
- prev_p->next = temp_p;
- temp_p = temp_p->next;
- prev_p->next->next = current_p;
- }
- else
- {
- pci = temp_p;
- temp_p = temp_p->next;
- pci->next = current_p;
- }
- }
-
- if (p->flags & AHC_BIOS_ENABLED)
- sort_list[1] = pci;
- else
- sort_list[3] = pci;
-
- break;
- }
- } /* End of switch(temp_p->type) */
- } /* End of while (temp_p != NULL) */
- /*
- * At this point, the cards have been broken into 4 sorted lists, now
- * we run through the lists in order and register each controller
- */
- {
- int i;
-
- left = found;
- for (i=0; i<ARRAY_SIZE(sort_list); i++)
- {
- temp_p = sort_list[i];
- while(temp_p != NULL)
- {
- template->name = board_names[temp_p->board_name_index];
- p = aic7xxx_alloc(template, temp_p);
- if (p != NULL)
- {
- p->instance = found - left;
- if (aic7xxx_register(template, p, (--left)) == 0)
- {
- found--;
- aic7xxx_release(p->host);
- scsi_unregister(p->host);
- }
- else if (aic7xxx_dump_card)
- {
- pause_sequencer(p);
- aic7xxx_print_card(p);
- aic7xxx_print_scratch_ram(p);
- unpause_sequencer(p, TRUE);
- }
- }
- current_p = temp_p;
- temp_p = (struct aic7xxx_host *)temp_p->next;
- kfree(current_p);
- }
- }
- }
- }
- return (found);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_buildscb
- *
- * Description:
- * Build a SCB.
- *-F*************************************************************************/
-static void aic7xxx_buildscb(struct aic7xxx_host *p, struct scsi_cmnd *cmd,
- struct aic7xxx_scb *scb)
-{
- unsigned short mask;
- struct aic7xxx_hwscb *hscb;
- struct aic_dev_data *aic_dev = cmd->device->hostdata;
- struct scsi_device *sdptr = cmd->device;
- unsigned char tindex = TARGET_INDEX(cmd);
- int use_sg;
-
- mask = (0x01 << tindex);
- hscb = scb->hscb;
-
- /*
- * Setup the control byte if we need negotiation and have not
- * already requested it.
- */
- hscb->control = 0;
- scb->tag_action = 0;
-
- if (p->discenable & mask)
- {
- hscb->control |= DISCENB;
- /* We always force TEST_UNIT_READY to untagged */
- if (cmd->cmnd[0] != TEST_UNIT_READY && sdptr->simple_tags)
- {
- hscb->control |= MSG_SIMPLE_Q_TAG;
- scb->tag_action = MSG_SIMPLE_Q_TAG;
- }
- }
- if ( !(aic_dev->dtr_pending) &&
- (aic_dev->needppr || aic_dev->needwdtr || aic_dev->needsdtr) &&
- (aic_dev->flags & DEVICE_DTR_SCANNED) )
- {
- aic_dev->dtr_pending = 1;
- scb->tag_action = 0;
- hscb->control &= DISCENB;
- hscb->control |= MK_MESSAGE;
- if(aic_dev->needppr)
- {
- scb->flags |= SCB_MSGOUT_PPR;
- }
- else if(aic_dev->needwdtr)
- {
- scb->flags |= SCB_MSGOUT_WDTR;
- }
- else if(aic_dev->needsdtr)
- {
- scb->flags |= SCB_MSGOUT_SDTR;
- }
- scb->flags |= SCB_DTR_SCB;
- }
- hscb->target_channel_lun = ((cmd->device->id << 4) & 0xF0) |
- ((cmd->device->channel & 0x01) << 3) | (cmd->device->lun & 0x07);
-
- /*
- * The interpretation of request_buffer and request_bufflen
- * changes depending on whether or not use_sg is zero; a
- * non-zero use_sg indicates the number of elements in the
- * scatter-gather array.
- */
-
- /*
- * XXX - this relies on the host data being stored in a
- * little-endian format.
- */
- hscb->SCSI_cmd_length = cmd->cmd_len;
- memcpy(scb->cmnd, cmd->cmnd, cmd->cmd_len);
- hscb->SCSI_cmd_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->cmnd));
-
- use_sg = scsi_dma_map(cmd);
- BUG_ON(use_sg < 0);
-
- if (use_sg) {
- struct scatterlist *sg; /* Must be mid-level SCSI code scatterlist */
-
- /*
- * We must build an SG list in adapter format, as the kernel's SG list
- * cannot be used directly because of data field size (__alpha__)
- * differences and the kernel SG list uses virtual addresses where
- * we need physical addresses.
- */
- int i;
-
- scb->sg_length = 0;
-
-
- /*
- * Copy the segments into the SG array. NOTE!!! - We used to
- * have the first entry both in the data_pointer area and the first
- * SG element. That has changed somewhat. We still have the first
- * entry in both places, but now we download the address of
- * scb->sg_list[1] instead of 0 to the sg pointer in the hscb.
- */
- scsi_for_each_sg(cmd, sg, use_sg, i) {
- unsigned int len = sg_dma_len(sg);
- scb->sg_list[i].address = cpu_to_le32(sg_dma_address(sg));
- scb->sg_list[i].length = cpu_to_le32(len);
- scb->sg_length += len;
- }
- /* Copy the first SG into the data pointer area. */
- hscb->data_pointer = scb->sg_list[0].address;
- hscb->data_count = scb->sg_list[0].length;
- scb->sg_count = i;
- hscb->SG_segment_count = i;
- hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[1]));
- } else {
- scb->sg_count = 0;
- scb->sg_length = 0;
- hscb->SG_segment_count = 0;
- hscb->SG_list_pointer = 0;
- hscb->data_count = 0;
- hscb->data_pointer = 0;
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_queue
- *
- * Description:
- * Queue a SCB to the controller.
- *-F*************************************************************************/
-static int aic7xxx_queue_lck(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *))
-{
- struct aic7xxx_host *p;
- struct aic7xxx_scb *scb;
- struct aic_dev_data *aic_dev;
-
- p = (struct aic7xxx_host *) cmd->device->host->hostdata;
-
- aic_dev = cmd->device->hostdata;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic_dev->active_cmds > aic_dev->max_q_depth)
- {
- printk(WARN_LEAD "Commands queued exceeds queue "
- "depth, active=%d\n",
- p->host_no, CTL_OF_CMD(cmd),
- aic_dev->active_cmds);
- }
-#endif
-
- scb = scbq_remove_head(&p->scb_data->free_scbs);
- if (scb == NULL)
- {
- aic7xxx_allocate_scb(p);
- scb = scbq_remove_head(&p->scb_data->free_scbs);
- if(scb == NULL)
- {
- printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no,
- CTL_OF_CMD(cmd));
- return 1;
- }
- }
- scb->cmd = cmd;
-
- /*
- * Make sure the scsi_cmnd pointer is saved, the struct it points to
- * is set up properly, and the parity error flag is reset, then send
- * the SCB to the sequencer and watch the fun begin.
- */
- aic7xxx_position(cmd) = scb->hscb->tag;
- cmd->scsi_done = fn;
- cmd->result = DID_OK;
- aic7xxx_error(cmd) = DID_OK;
- aic7xxx_status(cmd) = 0;
- cmd->host_scribble = NULL;
-
- /*
- * Construct the SCB beforehand, so the sequencer is
- * paused a minimal amount of time.
- */
- aic7xxx_buildscb(p, cmd, scb);
-
- scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
-
- scbq_insert_tail(&p->waiting_scbs, scb);
- aic7xxx_run_waiting_queues(p);
- return (0);
-}
-
-static DEF_SCSI_QCMD(aic7xxx_queue)
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_bus_device_reset
- *
- * Description:
- * Abort or reset the current SCSI command(s). If the scb has not
- * previously been aborted, then we attempt to send a BUS_DEVICE_RESET
- * message to the target. If the scb has previously been unsuccessfully
- * aborted, then we will reset the channel and have all devices renegotiate.
- * Returns an enumerated type that indicates the status of the operation.
- *-F*************************************************************************/
-static int __aic7xxx_bus_device_reset(struct scsi_cmnd *cmd)
-{
- struct aic7xxx_host *p;
- struct aic7xxx_scb *scb;
- struct aic7xxx_hwscb *hscb;
- int channel;
- unsigned char saved_scbptr, lastphase;
- unsigned char hscb_index;
- int disconnected;
- struct aic_dev_data *aic_dev;
-
- if(cmd == NULL)
- {
- printk(KERN_ERR "aic7xxx_bus_device_reset: called with NULL cmd!\n");
- return FAILED;
- }
- p = (struct aic7xxx_host *)cmd->device->host->hostdata;
- aic_dev = AIC_DEV(cmd);
- if(aic7xxx_position(cmd) < p->scb_data->numscbs)
- scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- else
- return FAILED;
-
- hscb = scb->hscb;
-
- aic7xxx_isr(p);
- aic7xxx_done_cmds_complete(p);
- /* If the command was already complete or just completed, then we didn't
- * do a reset, return FAILED */
- if(!(scb->flags & SCB_ACTIVE))
- return FAILED;
-
- pause_sequencer(p);
- lastphase = aic_inb(p, LASTPHASE);
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- {
- printk(INFO_LEAD "Bus Device reset, scb flags 0x%x, ",
- p->host_no, CTL_OF_SCB(scb), scb->flags);
- switch (lastphase)
- {
- case P_DATAOUT:
- printk("Data-Out phase\n");
- break;
- case P_DATAIN:
- printk("Data-In phase\n");
- break;
- case P_COMMAND:
- printk("Command phase\n");
- break;
- case P_MESGOUT:
- printk("Message-Out phase\n");
- break;
- case P_STATUS:
- printk("Status phase\n");
- break;
- case P_MESGIN:
- printk("Message-In phase\n");
- break;
- default:
- /*
- * We're not in a valid phase, so assume we're idle.
- */
- printk("while idle, LASTPHASE = 0x%x\n", lastphase);
- break;
- }
- printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
- "0x%x\n", p->host_no, CTL_OF_SCB(scb),
- aic_inb(p, SCSISIGI),
- aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
- aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
- printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n", p->host_no,
- CTL_OF_SCB(scb),
- (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0,
- aic_inb(p, SSTAT2),
- aic_inb(p, STCNT + 2) << 16 | aic_inb(p, STCNT + 1) << 8 |
- aic_inb(p, STCNT));
- }
-
- channel = cmd->device->channel;
-
- /*
- * Send a Device Reset Message:
- * The target that is holding up the bus may not be the same as
- * the one that triggered this timeout (different commands have
- * different timeout lengths). Our strategy here is to queue an
- * abort message to the timed out target if it is disconnected.
- * Otherwise, if we have an active target we stuff the message buffer
- * with an abort message and assert ATN in the hopes that the target
- * will let go of the bus and go to the mesgout phase. If this
- * fails, we'll get another timeout a few seconds later which will
- * attempt a bus reset.
- */
- saved_scbptr = aic_inb(p, SCBPTR);
- disconnected = FALSE;
-
- if (lastphase != P_BUSFREE)
- {
- if (aic_inb(p, SCB_TAG) >= p->scb_data->numscbs)
- {
- printk(WARN_LEAD "Invalid SCB ID %d is active, "
- "SCB flags = 0x%x.\n", p->host_no,
- CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
- unpause_sequencer(p, FALSE);
- return FAILED;
- }
- if (scb->hscb->tag == aic_inb(p, SCB_TAG))
- {
- if ( (lastphase == P_MESGOUT) || (lastphase == P_MESGIN) )
- {
- printk(WARN_LEAD "Device reset, Message buffer "
- "in use\n", p->host_no, CTL_OF_SCB(scb));
- unpause_sequencer(p, FALSE);
- return FAILED;
- }
-
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Device reset message in "
- "message buffer\n", p->host_no, CTL_OF_SCB(scb));
- scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
- aic7xxx_error(cmd) = DID_RESET;
- aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
- /* Send the abort message to the active SCB. */
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, lastphase | ATNO, SCSISIGO);
- unpause_sequencer(p, FALSE);
- spin_unlock_irq(p->host->host_lock);
- ssleep(1);
- spin_lock_irq(p->host->host_lock);
- if(aic_dev->flags & BUS_DEVICE_RESET_PENDING)
- return FAILED;
- else
- return SUCCESS;
- }
- } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
- /*
- * Simply set the MK_MESSAGE flag and the SEQINT handler will do
- * the rest on a reconnect/connect.
- */
- scb->hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
- aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
- /*
- * Check to see if the command is on the qinfifo. If it is, then we will
- * not need to queue the command again since the card should start it soon
- */
- if (aic7xxx_search_qinfifo(p, cmd->device->channel, cmd->device->id, cmd->device->lun, hscb->tag,
- 0, TRUE, NULL) == 0)
- {
- disconnected = TRUE;
- if ((hscb_index = aic7xxx_find_scb(p, scb)) != SCB_LIST_NULL)
- {
- unsigned char scb_control;
-
- aic_outb(p, hscb_index, SCBPTR);
- scb_control = aic_inb(p, SCB_CONTROL);
- /*
- * If the DISCONNECTED bit is not set in SCB_CONTROL, then we are
- * actually on the waiting list, not disconnected, and we don't
- * need to requeue the command.
- */
- disconnected = (scb_control & DISCONNECTED);
- aic_outb(p, scb_control | MK_MESSAGE, SCB_CONTROL);
- }
- if (disconnected)
- {
- /*
- * Actually requeue this SCB in case we can select the
- * device before it reconnects. This can result in the command
- * being on the qinfifo twice, but we don't care because it will
- * all get cleaned up if/when the reset takes place.
- */
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Queueing device reset command.\n", p->host_no,
- CTL_OF_SCB(scb));
- p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
- if (p->features & AHC_QUEUE_REGS)
- aic_outb(p, p->qinfifonext, HNSCB_QOFF);
- else
- aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
- scb->flags |= SCB_QUEUED_ABORT;
- }
- }
- aic_outb(p, saved_scbptr, SCBPTR);
- unpause_sequencer(p, FALSE);
- spin_unlock_irq(p->host->host_lock);
- msleep(1000/4);
- spin_lock_irq(p->host->host_lock);
- if(aic_dev->flags & BUS_DEVICE_RESET_PENDING)
- return FAILED;
- else
- return SUCCESS;
-}
-
-static int aic7xxx_bus_device_reset(struct scsi_cmnd *cmd)
-{
- int rc;
-
- spin_lock_irq(cmd->device->host->host_lock);
- rc = __aic7xxx_bus_device_reset(cmd);
- spin_unlock_irq(cmd->device->host->host_lock);
-
- return rc;
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_panic_abort
- *
- * Description:
- * Abort the current SCSI command(s).
- *-F*************************************************************************/
-static void aic7xxx_panic_abort(struct aic7xxx_host *p, struct scsi_cmnd *cmd)
-{
-
- printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION);
- printk("Controller type:\n %s\n", board_names[p->board_name_index]);
- printk("p->flags=0x%lx, p->chip=0x%x, p->features=0x%x, "
- "sequencer %s paused\n",
- p->flags, p->chip, p->features,
- (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
- pause_sequencer(p);
- disable_irq(p->irq);
- aic7xxx_print_card(p);
- aic7xxx_print_scratch_ram(p);
- spin_unlock_irq(p->host->host_lock);
- for(;;) barrier();
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_abort
- *
- * Description:
- * Abort the current SCSI command(s).
- *-F*************************************************************************/
-static int __aic7xxx_abort(struct scsi_cmnd *cmd)
-{
- struct aic7xxx_scb *scb = NULL;
- struct aic7xxx_host *p;
- int found=0, disconnected;
- unsigned char saved_hscbptr, hscbptr, scb_control;
- struct aic_dev_data *aic_dev;
-
- if(cmd == NULL)
- {
- printk(KERN_ERR "aic7xxx_abort: called with NULL cmd!\n");
- return FAILED;
- }
- p = (struct aic7xxx_host *)cmd->device->host->hostdata;
- aic_dev = AIC_DEV(cmd);
- if(aic7xxx_position(cmd) < p->scb_data->numscbs)
- scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- else
- return FAILED;
-
- aic7xxx_isr(p);
- aic7xxx_done_cmds_complete(p);
- /* If the command was already complete or just completed, then we didn't
- * do a reset, return FAILED */
- if(!(scb->flags & SCB_ACTIVE))
- return FAILED;
-
- pause_sequencer(p);
-
- /*
- * I added a new config option to the driver: "panic_on_abort" that will
- * cause the driver to panic and the machine to stop on the first abort
- * or reset call into the driver. At that point, it prints out a lot of
- * useful information for me which I can then use to try and debug the
- * problem. Simply enable the boot time prompt in order to activate this
- * code.
- */
- if (aic7xxx_panic_on_abort)
- aic7xxx_panic_abort(p, cmd);
-
- if (aic7xxx_verbose & VERBOSE_ABORT)
- {
- printk(INFO_LEAD "Aborting scb %d, flags 0x%x, SEQADDR 0x%x, LASTPHASE "
- "0x%x\n",
- p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags,
- aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
- aic_inb(p, LASTPHASE));
- printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%x\n",
- p->host_no, CTL_OF_SCB(scb), (p->features & AHC_ULTRA2) ?
- aic_inb(p, SG_CACHEPTR) : 0, aic_inb(p, SG_COUNT),
- aic_inb(p, SCSISIGI));
- printk(INFO_LEAD "SSTAT0 0x%x, SSTAT1 0x%x, SSTAT2 0x%x\n",
- p->host_no, CTL_OF_SCB(scb), aic_inb(p, SSTAT0),
- aic_inb(p, SSTAT1), aic_inb(p, SSTAT2));
- }
-
- if (scb->flags & SCB_WAITINGQ)
- {
- if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
- printk(INFO_LEAD "SCB found on waiting list and "
- "aborted.\n", p->host_no, CTL_OF_SCB(scb));
- scbq_remove(&p->waiting_scbs, scb);
- scbq_remove(&aic_dev->delayed_scbs, scb);
- aic_dev->active_cmds++;
- p->activescbs++;
- scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
- scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
- goto success;
- }
-
-/*
- * We just checked the waiting_q, now for the QINFIFO
- */
- if ( ((found = aic7xxx_search_qinfifo(p, cmd->device->id, cmd->device->channel,
- cmd->device->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
- FALSE, NULL)) != 0) &&
- (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
- {
- printk(INFO_LEAD "SCB found in QINFIFO and aborted.\n", p->host_no,
- CTL_OF_SCB(scb));
- goto success;
- }
-
-/*
- * QINFIFO, waitingq, completeq done. Next, check WAITING_SCB list in card
- */
-
- saved_hscbptr = aic_inb(p, SCBPTR);
- if ((hscbptr = aic7xxx_find_scb(p, scb)) != SCB_LIST_NULL)
- {
- aic_outb(p, hscbptr, SCBPTR);
- scb_control = aic_inb(p, SCB_CONTROL);
- disconnected = scb_control & DISCONNECTED;
- /*
- * If the DISCONNECTED bit is not set in SCB_CONTROL, then we are
- * either currently active or on the waiting list.
- */
- if(!disconnected && aic_inb(p, LASTPHASE) == P_BUSFREE) {
- if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
- printk(INFO_LEAD "SCB found on hardware waiting"
- " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
- /* If we are the only waiting command, stop the selection engine */
- if (aic_inb(p, WAITING_SCBH) == hscbptr && aic_inb(p, SCB_NEXT) ==
- SCB_LIST_NULL)
- {
- aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
- aic_outb(p, CLRSELTIMEO, CLRSINT1);
- aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
- }
- else
- {
- unsigned char prev, next;
- prev = SCB_LIST_NULL;
- next = aic_inb(p, WAITING_SCBH);
- while(next != SCB_LIST_NULL)
- {
- aic_outb(p, next, SCBPTR);
- if (next == hscbptr)
- {
- next = aic_inb(p, SCB_NEXT);
- if (prev != SCB_LIST_NULL)
- {
- aic_outb(p, prev, SCBPTR);
- aic_outb(p, next, SCB_NEXT);
- }
- else
- aic_outb(p, next, WAITING_SCBH);
- aic_outb(p, hscbptr, SCBPTR);
- next = SCB_LIST_NULL;
- }
- else
- {
- prev = next;
- next = aic_inb(p, SCB_NEXT);
- }
- }
- }
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic_outb(p, 0, SCB_CONTROL);
- aic7xxx_add_curscb_to_free_list(p);
- scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
- goto success;
- }
- else if (!disconnected)
- {
- /*
- * We are the currently active command
- */
- if((aic_inb(p, LASTPHASE) == P_MESGIN) ||
- (aic_inb(p, LASTPHASE) == P_MESGOUT))
- {
- /*
- * Message buffer busy, unable to abort
- */
- printk(INFO_LEAD "message buffer busy, unable to abort.\n",
- p->host_no, CTL_OF_SCB(scb));
- unpause_sequencer(p, FALSE);
- return FAILED;
- }
- /* Fallthrough to below, set ATNO after we set SCB_CONTROL */
- }
- aic_outb(p, scb_control | MK_MESSAGE, SCB_CONTROL);
- if(!disconnected)
- {
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
- }
- aic_outb(p, saved_hscbptr, SCBPTR);
- }
- else
- {
- /*
- * The scb isn't in the card at all and it is active and it isn't in
- * any of the queues, so it must be disconnected and paged out. Fall
- * through to the code below.
- */
- disconnected = 1;
- }
-
- p->flags |= AHC_ABORT_PENDING;
- scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
- scb->hscb->control |= MK_MESSAGE;
- if(disconnected)
- {
- if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
- printk(INFO_LEAD "SCB disconnected. Queueing Abort"
- " SCB.\n", p->host_no, CTL_OF_SCB(scb));
- p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
- if (p->features & AHC_QUEUE_REGS)
- aic_outb(p, p->qinfifonext, HNSCB_QOFF);
- else
- aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
- }
- unpause_sequencer(p, FALSE);
- spin_unlock_irq(p->host->host_lock);
- msleep(1000/4);
- spin_lock_irq(p->host->host_lock);
- if (p->flags & AHC_ABORT_PENDING)
- {
- if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
- printk(INFO_LEAD "Abort never delivered, returning FAILED\n", p->host_no,
- CTL_OF_CMD(cmd));
- p->flags &= ~AHC_ABORT_PENDING;
- return FAILED;
- }
- if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
- printk(INFO_LEAD "Abort successful.\n", p->host_no, CTL_OF_CMD(cmd));
- return SUCCESS;
-
-success:
- if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
- printk(INFO_LEAD "Abort successful.\n", p->host_no, CTL_OF_CMD(cmd));
- aic7xxx_run_done_queue(p, TRUE);
- unpause_sequencer(p, FALSE);
- return SUCCESS;
-}
-
-static int aic7xxx_abort(struct scsi_cmnd *cmd)
-{
- int rc;
-
- spin_lock_irq(cmd->device->host->host_lock);
- rc = __aic7xxx_abort(cmd);
- spin_unlock_irq(cmd->device->host->host_lock);
-
- return rc;
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_reset
- *
- * Description:
- * Resetting the bus always succeeds - is has to, otherwise the
- * kernel will panic! Try a surgical technique - sending a BUS
- * DEVICE RESET message - on the offending target before pulling
- * the SCSI bus reset line.
- *-F*************************************************************************/
-static int aic7xxx_reset(struct scsi_cmnd *cmd)
-{
- struct aic7xxx_scb *scb;
- struct aic7xxx_host *p;
- struct aic_dev_data *aic_dev;
-
- p = (struct aic7xxx_host *) cmd->device->host->hostdata;
- spin_lock_irq(p->host->host_lock);
-
- aic_dev = AIC_DEV(cmd);
- if(aic7xxx_position(cmd) < p->scb_data->numscbs)
- {
- scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- if (scb->cmd != cmd)
- scb = NULL;
- }
- else
- {
- scb = NULL;
- }
-
- /*
- * I added a new config option to the driver: "panic_on_abort" that will
- * cause the driver to panic and the machine to stop on the first abort
- * or reset call into the driver. At that point, it prints out a lot of
- * useful information for me which I can then use to try and debug the
- * problem. Simply enable the boot time prompt in order to activate this
- * code.
- */
- if (aic7xxx_panic_on_abort)
- aic7xxx_panic_abort(p, cmd);
-
- pause_sequencer(p);
-
- while((aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
- {
- aic7xxx_isr(p);
- pause_sequencer(p);
- }
- aic7xxx_done_cmds_complete(p);
-
- if(scb && (scb->cmd == NULL))
- {
- /*
- * We just completed the command when we ran the isr stuff, so we no
- * longer have it.
- */
- unpause_sequencer(p, FALSE);
- spin_unlock_irq(p->host->host_lock);
- return SUCCESS;
- }
-
-/*
- * By this point, we want to already know what we are going to do and
- * only have the following code implement our course of action.
- */
- aic7xxx_reset_channel(p, cmd->device->channel, TRUE);
- if (p->features & AHC_TWIN)
- {
- aic7xxx_reset_channel(p, cmd->device->channel ^ 0x01, TRUE);
- restart_sequencer(p);
- }
- aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
- aic7xxx_clear_intstat(p);
- p->flags &= ~AHC_HANDLING_REQINITS;
- p->msg_type = MSG_TYPE_NONE;
- p->msg_index = 0;
- p->msg_len = 0;
- aic7xxx_run_done_queue(p, TRUE);
- unpause_sequencer(p, FALSE);
- spin_unlock_irq(p->host->host_lock);
- ssleep(2);
- return SUCCESS;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_biosparam
- *
- * Description:
- * Return the disk geometry for the given SCSI device.
- *
- * Note:
- * This function is broken for today's really large drives and needs
- * fixed.
- *-F*************************************************************************/
-static int
-aic7xxx_biosparam(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int geom[])
-{
- sector_t heads, sectors, cylinders;
- int ret;
- struct aic7xxx_host *p;
- unsigned char *buf;
-
- p = (struct aic7xxx_host *) sdev->host->hostdata;
- buf = scsi_bios_ptable(bdev);
-
- if ( buf )
- {
- ret = scsi_partsize(buf, capacity, &geom[2], &geom[0], &geom[1]);
- kfree(buf);
- if ( ret != -1 )
- return(ret);
- }
-
- heads = 64;
- sectors = 32;
- cylinders = capacity >> 11;
-
- if ((p->flags & AHC_EXTEND_TRANS_A) && (cylinders > 1024))
- {
- heads = 255;
- sectors = 63;
- cylinders = capacity >> 14;
- if(capacity > (65535 * heads * sectors))
- cylinders = 65535;
- else
- cylinders = ((unsigned int)capacity) / (unsigned int)(heads * sectors);
- }
-
- geom[0] = (int)heads;
- geom[1] = (int)sectors;
- geom[2] = (int)cylinders;
-
- return (0);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_release
- *
- * Description:
- * Free the passed in Scsi_Host memory structures prior to unloading the
- * module.
- *-F*************************************************************************/
-static int
-aic7xxx_release(struct Scsi_Host *host)
-{
- struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
- struct aic7xxx_host *next, *prev;
-
- if(p->irq)
- free_irq(p->irq, p);
-#ifdef MMAPIO
- if(p->maddr)
- {
- iounmap(p->maddr);
- }
-#endif /* MMAPIO */
- if(!p->pdev)
- release_region(p->base, MAXREG - MINREG);
-#ifdef CONFIG_PCI
- else {
- pci_release_regions(p->pdev);
- pci_dev_put(p->pdev);
- }
-#endif
- prev = NULL;
- next = first_aic7xxx;
- while(next != NULL)
- {
- if(next == p)
- {
- if(prev == NULL)
- first_aic7xxx = next->next;
- else
- prev->next = next->next;
- }
- else
- {
- prev = next;
- }
- next = next->next;
- }
- aic7xxx_free(p);
- return(0);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_print_card
- *
- * Description:
- * Print out all of the control registers on the card
- *
- * NOTE: This function is not yet safe for use on the VLB and EISA
- * controllers, so it isn't used on those controllers at all.
- *-F*************************************************************************/
-static void
-aic7xxx_print_card(struct aic7xxx_host *p)
-{
- int i, j, k, chip;
- static struct register_ranges {
- int num_ranges;
- int range_val[32];
- } cards_ds[] = {
- { 0, {0,} }, /* none */
- {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/
- 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} },
- { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/
- 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
- { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/
- 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
- {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/
- 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
- {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/
- 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
- {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/
- 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
- 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
- 0xfe, 0xff} },
- {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/
- 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a,
- 0x9f, 0x9f, 0xe0, 0xf1} },
- {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/
- 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
- 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
- 0xfe, 0xff} },
- {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7892*/
- 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
- 0xe0, 0xf1, 0xf4, 0xfc} },
- {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7899*/
- 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
- 0xe0, 0xf1, 0xf4, 0xfc} },
- };
- chip = p->chip & AHC_CHIPID_MASK;
- printk("%s at ",
- board_names[p->board_name_index]);
- switch(p->chip & ~AHC_CHIPID_MASK)
- {
- case AHC_VL:
- printk("VLB Slot %d.\n", p->pci_device_fn);
- break;
- case AHC_EISA:
- printk("EISA Slot %d.\n", p->pci_device_fn);
- break;
- case AHC_PCI:
- default:
- printk("PCI %d/%d/%d.\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
- PCI_FUNC(p->pci_device_fn));
- break;
- }
-
- /*
- * the registers on the card....
- */
- printk("Card Dump:\n");
- k = 0;
- for(i=0; i<cards_ds[chip].num_ranges; i++)
- {
- for(j = cards_ds[chip].range_val[ i * 2 ];
- j <= cards_ds[chip].range_val[ i * 2 + 1 ] ;
- j++)
- {
- printk("%02x:%02x ", j, aic_inb(p, j));
- if(++k == 13)
- {
- printk("\n");
- k=0;
- }
- }
- }
- if(k != 0)
- printk("\n");
-
- /*
- * If this was an Ultra2 controller, then we just hosed the card in terms
- * of the QUEUE REGS. This function is only called at init time or by
- * the panic_abort function, so it's safe to assume a generic init time
- * setting here
- */
-
- if(p->features & AHC_QUEUE_REGS)
- {
- aic_outb(p, 0, SDSCB_QOFF);
- aic_outb(p, 0, SNSCB_QOFF);
- aic_outb(p, 0, HNSCB_QOFF);
- }
-
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_print_scratch_ram
- *
- * Description:
- * Print out the scratch RAM values on the card.
- *-F*************************************************************************/
-static void
-aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
-{
- int i, k;
-
- k = 0;
- printk("Scratch RAM:\n");
- for(i = SRAM_BASE; i < SEQCTL; i++)
- {
- printk("%02x:%02x ", i, aic_inb(p, i));
- if(++k == 13)
- {
- printk("\n");
- k=0;
- }
- }
- if (p->features & AHC_MORE_SRAM)
- {
- for(i = TARG_OFFSET; i < 0x80; i++)
- {
- printk("%02x:%02x ", i, aic_inb(p, i));
- if(++k == 13)
- {
- printk("\n");
- k=0;
- }
- }
- }
- printk("\n");
-}
-
-
-#include "aic7xxx_old/aic7xxx_proc.c"
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_VERSION(AIC7XXX_H_VERSION);
-
-
-static struct scsi_host_template driver_template = {
- .proc_info = aic7xxx_proc_info,
- .detect = aic7xxx_detect,
- .release = aic7xxx_release,
- .info = aic7xxx_info,
- .queuecommand = aic7xxx_queue,
- .slave_alloc = aic7xxx_slave_alloc,
- .slave_configure = aic7xxx_slave_configure,
- .slave_destroy = aic7xxx_slave_destroy,
- .bios_param = aic7xxx_biosparam,
- .eh_abort_handler = aic7xxx_abort,
- .eh_device_reset_handler = aic7xxx_bus_device_reset,
- .eh_host_reset_handler = aic7xxx_reset,
- .can_queue = 255,
- .this_id = -1,
- .max_sectors = 2048,
- .cmd_per_lun = 3,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-#include "scsi_module.c"
-
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 2
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -2
- * c-argdecl-indent: 2
- * c-label-offset: -2
- * c-continued-statement-offset: 2
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.h b/drivers/scsi/aic7xxx_old/aic7xxx.h
deleted file mode 100644
index 0116c8128a6b..000000000000
--- a/drivers/scsi/aic7xxx_old/aic7xxx.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux.
- *
- * Copyright (c) 1994 John Aycock
- * The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: aic7xxx.h,v 3.2 1996/07/23 03:37:26 deang Exp $
- *-M*************************************************************************/
-#ifndef _aic7xxx_h
-#define _aic7xxx_h
-
-#define AIC7XXX_H_VERSION "5.2.0"
-
-#endif /* _aic7xxx_h */
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.reg b/drivers/scsi/aic7xxx_old/aic7xxx.reg
deleted file mode 100644
index f67b4bced01c..000000000000
--- a/drivers/scsi/aic7xxx_old/aic7xxx.reg
+++ /dev/null
@@ -1,1401 +0,0 @@
-/*
- * Aic7xxx register and scratch ram definitions.
- *
- * Copyright (c) 1994-1998 Justin Gibbs.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification, immediately at the beginning of the file.
- * 2. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * Where this Software is combined with software released under the terms of
- * the GNU General Public License ("GPL") and the terms of the GPL would require the
- * combined work to also be released under the terms of the GPL, the terms
- * and conditions of this License will apply in addition to those of the
- * GPL with the exception of any terms or conditions of this License that
- * conflict with, or are expressly prohibited by, the GPL.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $
- */
-
-/*
- * This file is processed by the aic7xxx_asm utility for use in assembling
- * firmware for the aic7xxx family of SCSI host adapters as well as to generate
- * a C header file for use in the kernel portion of the Aic7xxx driver.
- *
- * All page numbers refer to the Adaptec AIC-7770 Data Book available from
- * Adaptec's Technical Documents Department 1-800-934-2766
- */
-
-/*
- * SCSI Sequence Control (p. 3-11).
- * Each bit, when set starts a specific SCSI sequence on the bus
- */
-register SCSISEQ {
- address 0x000
- access_mode RW
- bit TEMODE 0x80
- bit ENSELO 0x40
- bit ENSELI 0x20
- bit ENRSELI 0x10
- bit ENAUTOATNO 0x08
- bit ENAUTOATNI 0x04
- bit ENAUTOATNP 0x02
- bit SCSIRSTO 0x01
-}
-
-/*
- * SCSI Transfer Control 0 Register (pp. 3-13).
- * Controls the SCSI module data path.
- */
-register SXFRCTL0 {
- address 0x001
- access_mode RW
- bit DFON 0x80
- bit DFPEXP 0x40
- bit FAST20 0x20
- bit CLRSTCNT 0x10
- bit SPIOEN 0x08
- bit SCAMEN 0x04
- bit CLRCHN 0x02
-}
-
-/*
- * SCSI Transfer Control 1 Register (pp. 3-14,15).
- * Controls the SCSI module data path.
- */
-register SXFRCTL1 {
- address 0x002
- access_mode RW
- bit BITBUCKET 0x80
- bit SWRAPEN 0x40
- bit ENSPCHK 0x20
- mask STIMESEL 0x18
- bit ENSTIMER 0x04
- bit ACTNEGEN 0x02
- bit STPWEN 0x01 /* Powered Termination */
-}
-
-/*
- * SCSI Control Signal Read Register (p. 3-15).
- * Reads the actual state of the SCSI bus pins
- */
-register SCSISIGI {
- address 0x003
- access_mode RO
- bit CDI 0x80
- bit IOI 0x40
- bit MSGI 0x20
- bit ATNI 0x10
- bit SELI 0x08
- bit BSYI 0x04
- bit REQI 0x02
- bit ACKI 0x01
-/*
- * Possible phases in SCSISIGI
- */
- mask PHASE_MASK CDI|IOI|MSGI
- mask P_DATAOUT 0x00
- mask P_DATAIN IOI
- mask P_COMMAND CDI
- mask P_MESGOUT CDI|MSGI
- mask P_STATUS CDI|IOI
- mask P_MESGIN CDI|IOI|MSGI
-}
-
-/*
- * SCSI Control Signal Write Register (p. 3-16).
- * Writing to this register modifies the control signals on the bus. Only
- * those signals that are allowed in the current mode (Initiator/Target) are
- * asserted.
- */
-register SCSISIGO {
- address 0x003
- access_mode WO
- bit CDO 0x80
- bit IOO 0x40
- bit MSGO 0x20
- bit ATNO 0x10
- bit SELO 0x08
- bit BSYO 0x04
- bit REQO 0x02
- bit ACKO 0x01
-/*
- * Possible phases to write into SCSISIG0
- */
- mask PHASE_MASK CDI|IOI|MSGI
- mask P_DATAOUT 0x00
- mask P_DATAIN IOI
- mask P_COMMAND CDI
- mask P_MESGOUT CDI|MSGI
- mask P_STATUS CDI|IOI
- mask P_MESGIN CDI|IOI|MSGI
-}
-
-/*
- * SCSI Rate Control (p. 3-17).
- * Contents of this register determine the Synchronous SCSI data transfer
- * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the
- * SOFS (3:0) bits disables synchronous data transfers. Any offset value
- * greater than 0 enables synchronous transfers.
- */
-register SCSIRATE {
- address 0x004
- access_mode RW
- bit WIDEXFER 0x80 /* Wide transfer control */
- mask SXFR 0x70 /* Sync transfer rate */
- mask SXFR_ULTRA2 0x7f /* Sync transfer rate */
- mask SOFS 0x0f /* Sync offset */
-}
-
-/*
- * SCSI ID (p. 3-18).
- * Contains the ID of the board and the current target on the
- * selected channel.
- */
-register SCSIID {
- address 0x005
- access_mode RW
- mask TID 0xf0 /* Target ID mask */
- mask OID 0x0f /* Our ID mask */
- /*
- * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
- * The aic7890/91 allow an offset of up to 127 transfers in both wide
- * and narrow mode.
- */
- alias SCSIOFFSET
- mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */
-}
-
-/*
- * SCSI Latched Data (p. 3-19).
- * Read/Write latches used to transfer data on the SCSI bus during
- * Automatic or Manual PIO mode. SCSIDATH can be used for the
- * upper byte of a 16bit wide asynchronouse data phase transfer.
- */
-register SCSIDATL {
- address 0x006
- access_mode RW
-}
-
-register SCSIDATH {
- address 0x007
- access_mode RW
-}
-
-/*
- * SCSI Transfer Count (pp. 3-19,20)
- * These registers count down the number of bytes transferred
- * across the SCSI bus. The counter is decremented only once
- * the data has been safely transferred. SDONE in SSTAT0 is
- * set when STCNT goes to 0
- */
-register STCNT {
- address 0x008
- size 3
- access_mode RW
-}
-
-/*
- * Option Mode Register (Alternate Mode) (p. 5-198)
- * This register is used to set certain options on Ultra3 based chips.
- * The chip must be in alternate mode (bit ALT_MODE in SFUNCT must be set)
- */
-register OPTIONMODE {
- address 0x008
- access_mode RW
- bit AUTORATEEN 0x80
- bit AUTOACKEN 0x40
- bit ATNMGMNTEN 0x20
- bit BUSFREEREV 0x10
- bit EXPPHASEDIS 0x08
- bit SCSIDATL_IMGEN 0x04
- bit AUTO_MSGOUT_DE 0x02
- bit DIS_MSGIN_DUALEDGE 0x01
-}
-
-
-/*
- * Clear SCSI Interrupt 0 (p. 3-20)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
- */
-register CLRSINT0 {
- address 0x00b
- access_mode WO
- bit CLRSELDO 0x40
- bit CLRSELDI 0x20
- bit CLRSELINGO 0x10
- bit CLRSWRAP 0x08
- bit CLRSPIORDY 0x02
-}
-
-/*
- * SCSI Status 0 (p. 3-21)
- * Contains one set of SCSI Interrupt codes
- * These are most likely of interest to the sequencer
- */
-register SSTAT0 {
- address 0x00b
- access_mode RO
- bit TARGET 0x80 /* Board acting as target */
- bit SELDO 0x40 /* Selection Done */
- bit SELDI 0x20 /* Board has been selected */
- bit SELINGO 0x10 /* Selection In Progress */
- bit SWRAP 0x08 /* 24bit counter wrap */
- bit IOERR 0x08 /* LVD Tranceiver mode changed */
- bit SDONE 0x04 /* STCNT = 0x000000 */
- bit SPIORDY 0x02 /* SCSI PIO Ready */
- bit DMADONE 0x01 /* DMA transfer completed */
-}
-
-/*
- * Clear SCSI Interrupt 1 (p. 3-23)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
- */
-register CLRSINT1 {
- address 0x00c
- access_mode WO
- bit CLRSELTIMEO 0x80
- bit CLRATNO 0x40
- bit CLRSCSIRSTI 0x20
- bit CLRBUSFREE 0x08
- bit CLRSCSIPERR 0x04
- bit CLRPHASECHG 0x02
- bit CLRREQINIT 0x01
-}
-
-/*
- * SCSI Status 1 (p. 3-24)
- */
-register SSTAT1 {
- address 0x00c
- access_mode RO
- bit SELTO 0x80
- bit ATNTARG 0x40
- bit SCSIRSTI 0x20
- bit PHASEMIS 0x10
- bit BUSFREE 0x08
- bit SCSIPERR 0x04
- bit PHASECHG 0x02
- bit REQINIT 0x01
-}
-
-/*
- * SCSI Status 2 (pp. 3-25,26)
- */
-register SSTAT2 {
- address 0x00d
- access_mode RO
- bit OVERRUN 0x80
- bit SHVALID 0x40
- bit WIDE_RES 0x20
- bit EXP_ACTIVE 0x10 /* SCSI Expander Active */
- bit CRCVALERR 0x08 /* CRC Value Error */
- bit CRCENDERR 0x04 /* CRC End Error */
- bit CRCREQERR 0x02 /* CRC REQ Error */
- bit DUAL_EDGE_ERROR 0x01 /* Invalid pins for Dual Edge phase */
- mask SFCNT 0x1f
-}
-
-/*
- * SCSI Status 3 (p. 3-26)
- */
-register SSTAT3 {
- address 0x00e
- access_mode RO
- mask SCSICNT 0xf0
- mask OFFCNT 0x0f
-}
-
-/*
- * SCSI ID for the aic7890/91 chips
- */
-register SCSIID_ULTRA2 {
- address 0x00f
- access_mode RW
- mask TID 0xf0 /* Target ID mask */
- mask OID 0x0f /* Our ID mask */
-}
-
-/*
- * SCSI Interrupt Mode 1 (p. 3-28)
- * Setting any bit will enable the corresponding function
- * in SIMODE0 to interrupt via the IRQ pin.
- */
-register SIMODE0 {
- address 0x010
- access_mode RW
- bit ENSELDO 0x40
- bit ENSELDI 0x20
- bit ENSELINGO 0x10
- bit ENSWRAP 0x08
- bit ENIOERR 0x08 /* LVD Tranceiver mode changes */
- bit ENSDONE 0x04
- bit ENSPIORDY 0x02
- bit ENDMADONE 0x01
-}
-
-/*
- * SCSI Interrupt Mode 1 (pp. 3-28,29)
- * Setting any bit will enable the corresponding function
- * in SIMODE1 to interrupt via the IRQ pin.
- */
-register SIMODE1 {
- address 0x011
- access_mode RW
- bit ENSELTIMO 0x80
- bit ENATNTARG 0x40
- bit ENSCSIRST 0x20
- bit ENPHASEMIS 0x10
- bit ENBUSFREE 0x08
- bit ENSCSIPERR 0x04
- bit ENPHASECHG 0x02
- bit ENREQINIT 0x01
-}
-
-/*
- * SCSI Data Bus (High) (p. 3-29)
- * This register reads data on the SCSI Data bus directly.
- */
-register SCSIBUSL {
- address 0x012
- access_mode RO
-}
-
-register SCSIBUSH {
- address 0x013
- access_mode RO
-}
-
-/*
- * SCSI/Host Address (p. 3-30)
- * These registers hold the host address for the byte about to be
- * transferred on the SCSI bus. They are counted up in the same
- * manner as STCNT is counted down. SHADDR should always be used
- * to determine the address of the last byte transferred since HADDR
- * can be skewed by write ahead.
- */
-register SHADDR {
- address 0x014
- size 4
- access_mode RO
-}
-
-/*
- * Selection Timeout Timer (p. 3-30)
- */
-register SELTIMER {
- address 0x018
- access_mode RW
- bit STAGE6 0x20
- bit STAGE5 0x10
- bit STAGE4 0x08
- bit STAGE3 0x04
- bit STAGE2 0x02
- bit STAGE1 0x01
-}
-
-/*
- * Selection/Reselection ID (p. 3-31)
- * Upper four bits are the device id. The ONEBIT is set when the re/selecting
- * device did not set its own ID.
- */
-register SELID {
- address 0x019
- access_mode RW
- mask SELID_MASK 0xf0
- bit ONEBIT 0x08
-}
-
-/*
- * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
- * Indicates if external logic has been attached to the chip to
- * perform the tasks of accessing a serial eeprom, testing termination
- * strength, and performing cable detection. On the aic7860, most of
- * these features are handled on chip, but on the aic7855 an attached
- * aic3800 does the grunt work.
- */
-register SPIOCAP {
- address 0x01b
- access_mode RW
- bit SOFT1 0x80
- bit SOFT0 0x40
- bit SOFTCMDEN 0x20
- bit HAS_BRDCTL 0x10 /* External Board control */
- bit SEEPROM 0x08 /* External serial eeprom logic */
- bit EEPROM 0x04 /* Writable external BIOS ROM */
- bit ROM 0x02 /* Logic for accessing external ROM */
- bit SSPIOCPS 0x01 /* Termination and cable detection */
-}
-
-/*
- * SCSI Block Control (p. 3-32)
- * Controls Bus type and channel selection. In a twin channel configuration
- * addresses 0x00-0x1e are gated to the appropriate channel based on this
- * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
- * on a wide bus.
- */
-register SBLKCTL {
- address 0x01f
- access_mode RW
- bit DIAGLEDEN 0x80 /* Aic78X0 only */
- bit DIAGLEDON 0x40 /* Aic78X0 only */
- bit AUTOFLUSHDIS 0x20
- bit SELBUSB 0x08
- bit ENAB40 0x08 /* LVD transceiver active */
- bit ENAB20 0x04 /* SE/HVD transceiver active */
- bit SELWIDE 0x02
- bit XCVR 0x01 /* External transceiver active */
-}
-
-/*
- * Sequencer Control (p. 3-33)
- * Error detection mode and speed configuration
- */
-register SEQCTL {
- address 0x060
- access_mode RW
- bit PERRORDIS 0x80
- bit PAUSEDIS 0x40
- bit FAILDIS 0x20
- bit FASTMODE 0x10
- bit BRKADRINTEN 0x08
- bit STEP 0x04
- bit SEQRESET 0x02
- bit LOADRAM 0x01
-}
-
-/*
- * Sequencer RAM Data (p. 3-34)
- * Single byte window into the Scratch Ram area starting at the address
- * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
- * four bytes in succession. The SEQADDRs will increment after the most
- * significant byte is written
- */
-register SEQRAM {
- address 0x061
- access_mode RW
-}
-
-/*
- * Sequencer Address Registers (p. 3-35)
- * Only the first bit of SEQADDR1 holds addressing information
- */
-register SEQADDR0 {
- address 0x062
- access_mode RW
-}
-
-register SEQADDR1 {
- address 0x063
- access_mode RW
- mask SEQADDR1_MASK 0x01
-}
-
-/*
- * Accumulator
- * We cheat by passing arguments in the Accumulator up to the kernel driver
- */
-register ACCUM {
- address 0x064
- access_mode RW
- accumulator
-}
-
-register SINDEX {
- address 0x065
- access_mode RW
- sindex
-}
-
-register DINDEX {
- address 0x066
- access_mode RW
-}
-
-register ALLONES {
- address 0x069
- access_mode RO
- allones
-}
-
-register ALLZEROS {
- address 0x06a
- access_mode RO
- allzeros
-}
-
-register NONE {
- address 0x06a
- access_mode WO
- none
-}
-
-register FLAGS {
- address 0x06b
- access_mode RO
- bit ZERO 0x02
- bit CARRY 0x01
-}
-
-register SINDIR {
- address 0x06c
- access_mode RO
-}
-
-register DINDIR {
- address 0x06d
- access_mode WO
-}
-
-register FUNCTION1 {
- address 0x06e
- access_mode RW
-}
-
-register STACK {
- address 0x06f
- access_mode RO
-}
-
-/*
- * Board Control (p. 3-43)
- */
-register BCTL {
- address 0x084
- access_mode RW
- bit ACE 0x08
- bit ENABLE 0x01
-}
-
-register DSCOMMAND0 {
- address 0x084
- access_mode RW
- bit CACHETHEN 0x80
- bit DPARCKEN 0x40
- bit MPARCKEN 0x20
- bit EXTREQLCK 0x10
- bit INTSCBRAMSEL 0x08
- bit RAMPS 0x04
- bit USCBSIZE32 0x02
- bit CIOPARCKEN 0x01
-}
-
-/*
- * On the aic78X0 chips, Board Control is replaced by the DSCommand
- * register (p. 4-64)
- */
-register DSCOMMAND {
- address 0x084
- access_mode RW
- bit CACHETHEN 0x80 /* Cache Threshold enable */
- bit DPARCKEN 0x40 /* Data Parity Check Enable */
- bit MPARCKEN 0x20 /* Memory Parity Check Enable */
- bit EXTREQLCK 0x10 /* External Request Lock */
-}
-
-/*
- * Bus On/Off Time (p. 3-44)
- */
-register BUSTIME {
- address 0x085
- access_mode RW
- mask BOFF 0xf0
- mask BON 0x0f
-}
-
-/*
- * Bus Speed (p. 3-45)
- */
-register BUSSPD {
- address 0x086
- access_mode RW
- mask DFTHRSH 0xc0
- mask STBOFF 0x38
- mask STBON 0x07
- mask DFTHRSH_100 0xc0
-}
-
-/*
- * Host Control (p. 3-47) R/W
- * Overall host control of the device.
- */
-register HCNTRL {
- address 0x087
- access_mode RW
- bit POWRDN 0x40
- bit SWINT 0x10
- bit IRQMS 0x08
- bit PAUSE 0x04
- bit INTEN 0x02
- bit CHIPRST 0x01
- bit CHIPRSTACK 0x01
-}
-
-/*
- * Host Address (p. 3-48)
- * This register contains the address of the byte about
- * to be transferred across the host bus.
- */
-register HADDR {
- address 0x088
- size 4
- access_mode RW
-}
-
-register HCNT {
- address 0x08c
- size 3
- access_mode RW
-}
-
-/*
- * SCB Pointer (p. 3-49)
- * Gate one of the four SCBs into the SCBARRAY window.
- */
-register SCBPTR {
- address 0x090
- access_mode RW
-}
-
-/*
- * Interrupt Status (p. 3-50)
- * Status for system interrupts
- */
-register INTSTAT {
- address 0x091
- access_mode RW
- bit BRKADRINT 0x08
- bit SCSIINT 0x04
- bit CMDCMPLT 0x02
- bit SEQINT 0x01
- mask BAD_PHASE SEQINT /* unknown scsi bus phase */
- mask SEND_REJECT 0x10|SEQINT /* sending a message reject */
- mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/
- mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */
- mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */
- mask WIDE_RESIDUE 0x50|SEQINT /* need kernel to back up */
- /* the SG array for us */
- mask REJECT_MSG 0x60|SEQINT /* Reject message received */
- mask BAD_STATUS 0x70|SEQINT /* Bad status from target */
- mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */
- mask AWAITING_MSG 0xa0|SEQINT /*
- * Kernel requested to specify
- * a message to this target
- * (command was null), so tell
- * it that it can fill the
- * message buffer.
- */
- mask SEQ_SG_FIXUP 0xb0|SEQINT /* need help with fixing up
- * the sg array pointer after
- * a phasemis with no valid
- * sg elements in the shadow
- * pipeline.
- */
- mask TRACEPOINT2 0xc0|SEQINT
- mask MSGIN_PHASEMIS 0xd0|SEQINT /*
- * Target changed phase on us
- * when we were expecting
- * another msgin byte.
- */
- mask DATA_OVERRUN 0xe0|SEQINT /*
- * Target attempted to write
- * beyond the bounds of its
- * command.
- */
-
- mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */
- mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
-}
-
-/*
- * Hard Error (p. 3-53)
- * Reporting of catastrophic errors. You usually cannot recover from
- * these without a full board reset.
- */
-register ERROR {
- address 0x092
- access_mode RO
- bit CIOPARERR 0x80 /* Ultra2 only */
- bit PCIERRSTAT 0x40 /* PCI only */
- bit MPARERR 0x20 /* PCI only */
- bit DPARERR 0x10 /* PCI only */
- bit SQPARERR 0x08
- bit ILLOPCODE 0x04
- bit ILLSADDR 0x02
- bit DSCTMOUT 0x02 /* Ultra3 only */
- bit ILLHADDR 0x01
-}
-
-/*
- * Clear Interrupt Status (p. 3-52)
- */
-register CLRINT {
- address 0x092
- access_mode WO
- bit CLRPARERR 0x10 /* PCI only */
- bit CLRBRKADRINT 0x08
- bit CLRSCSIINT 0x04
- bit CLRCMDINT 0x02
- bit CLRSEQINT 0x01
-}
-
-register DFCNTRL {
- address 0x093
- access_mode RW
- bit PRELOADEN 0x80 /* aic7890 only */
- bit WIDEODD 0x40
- bit SCSIEN 0x20
- bit SDMAEN 0x10
- bit SDMAENACK 0x10
- bit HDMAEN 0x08
- bit HDMAENACK 0x08
- bit DIRECTION 0x04
- bit FIFOFLUSH 0x02
- bit FIFORESET 0x01
-}
-
-register DFSTATUS {
- address 0x094
- access_mode RO
- bit PRELOAD_AVAIL 0x80
- bit DWORDEMP 0x20
- bit MREQPEND 0x10
- bit HDONE 0x08
- bit DFTHRESH 0x04
- bit FIFOFULL 0x02
- bit FIFOEMP 0x01
-}
-
-register DFDAT {
- address 0x099
- access_mode RW
-}
-
-/*
- * SCB Auto Increment (p. 3-59)
- * Byte offset into the SCB Array and an optional bit to allow auto
- * incrementing of the address during download and upload operations
- */
-register SCBCNT {
- address 0x09a
- access_mode RW
- bit SCBAUTO 0x80
- mask SCBCNT_MASK 0x1f
-}
-
-/*
- * Queue In FIFO (p. 3-60)
- * Input queue for queued SCBs (commands that the seqencer has yet to start)
- */
-register QINFIFO {
- address 0x09b
- access_mode RW
-}
-
-/*
- * Queue In Count (p. 3-60)
- * Number of queued SCBs
- */
-register QINCNT {
- address 0x09c
- access_mode RO
-}
-
-/*
- * SCSIDATL IMAGE Register (p. 5-104)
- * Write to this register also go to SCSIDATL but this register will preserve
- * the data for later reading as long as the SCSIDATL_IMGEN bit in the
- * OPTIONMODE register is set.
- */
-register SCSIDATL_IMG {
- address 0x09c
- access_mode RW
-}
-
-/*
- * Queue Out FIFO (p. 3-61)
- * Queue of SCBs that have completed and await the host
- */
-register QOUTFIFO {
- address 0x09d
- access_mode WO
-}
-
-/*
- * CRC Control 1 Register (p. 5-105)
- * Control bits for the Ultra 160/m CRC facilities
- */
-register CRCCONTROL1 {
- address 0x09d
- access_mode RW
- bit CRCONSEEN 0x80 /* CRC ON Single Edge ENable */
- bit CRCVALCHKEN 0x40 /* CRC Value Check Enable */
- bit CRCENDCHKEN 0x20 /* CRC End Check Enable */
- bit CRCREQCHKEN 0x10
- bit TARGCRCENDEN 0x08 /* Enable End CRC transfer when target */
- bit TARGCRCCNTEN 0x04 /* Enable CRC transfer when target */
-}
-
-/*
- * Queue Out Count (p. 3-61)
- * Number of queued SCBs in the Out FIFO
- */
-register QOUTCNT {
- address 0x09e
- access_mode RO
-}
-
-/*
- * SCSI Phase Register (p. 5-106)
- * Current bus phase
- */
-register SCSIPHASE {
- address 0x09e
- access_mode RO
- bit SP_STATUS 0x20
- bit SP_COMMAND 0x10
- bit SP_MSG_IN 0x08
- bit SP_MSG_OUT 0x04
- bit SP_DATA_IN 0x02
- bit SP_DATA_OUT 0x01
-}
-
-/*
- * Special Function
- */
-register SFUNCT {
- address 0x09f
- access_mode RW
- bit ALT_MODE 0x80
-}
-
-/*
- * SCB Definition (p. 5-4)
- */
-scb {
- address 0x0a0
- SCB_CONTROL {
- size 1
- bit MK_MESSAGE 0x80
- bit DISCENB 0x40
- bit TAG_ENB 0x20
- bit DISCONNECTED 0x04
- mask SCB_TAG_TYPE 0x03
- }
- SCB_TCL {
- size 1
- bit SELBUSB 0x08
- mask TID 0xf0
- mask LID 0x07
- }
- SCB_TARGET_STATUS {
- size 1
- }
- SCB_SGCOUNT {
- size 1
- }
- SCB_SGPTR {
- size 4
- }
- SCB_RESID_SGCNT {
- size 1
- }
- SCB_RESID_DCNT {
- size 3
- }
- SCB_DATAPTR {
- size 4
- }
- SCB_DATACNT {
- /*
- * Really only 3 bytes, but padded to make
- * the kernel's job easier.
- */
- size 4
- }
- SCB_CMDPTR {
- size 4
- }
- SCB_CMDLEN {
- size 1
- }
- SCB_TAG {
- size 1
- }
- SCB_NEXT {
- size 1
- }
- SCB_PREV {
- size 1
- }
- SCB_BUSYTARGETS {
- size 4
- }
-}
-
-const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
-
-/* --------------------- AHA-2840-only definitions -------------------- */
-
-register SEECTL_2840 {
- address 0x0c0
- access_mode RW
- bit CS_2840 0x04
- bit CK_2840 0x02
- bit DO_2840 0x01
-}
-
-register STATUS_2840 {
- address 0x0c1
- access_mode RW
- bit EEPROM_TF 0x80
- mask BIOS_SEL 0x60
- mask ADSEL 0x1e
- bit DI_2840 0x01
-}
-
-/* --------------------- AIC-7870-only definitions -------------------- */
-
-register DSPCISTATUS {
- address 0x086
- mask DFTHRSH_100 0xc0
-}
-
-register CCHADDR {
- address 0x0E0
- size 8
-}
-
-register CCHCNT {
- address 0x0E8
-}
-
-register CCSGRAM {
- address 0x0E9
-}
-
-register CCSGADDR {
- address 0x0EA
-}
-
-register CCSGCTL {
- address 0x0EB
- bit CCSGDONE 0x80
- bit CCSGEN 0x08
- bit FLAG 0x02
- bit CCSGRESET 0x01
-}
-
-register CCSCBCNT {
- address 0xEF
-}
-
-register CCSCBCTL {
- address 0x0EE
- bit CCSCBDONE 0x80
- bit ARRDONE 0x40 /* SCB Array prefetch done */
- bit CCARREN 0x10
- bit CCSCBEN 0x08
- bit CCSCBDIR 0x04
- bit CCSCBRESET 0x01
-}
-
-register CCSCBADDR {
- address 0x0ED
-}
-
-register CCSCBRAM {
- address 0xEC
-}
-
-register CCSCBPTR {
- address 0x0F1
-}
-
-register HNSCB_QOFF {
- address 0x0F4
-}
-
-register HESCB_QOFF {
- address 0x0F5
-}
-
-register SNSCB_QOFF {
- address 0x0F6
-}
-
-register SESCB_QOFF {
- address 0x0F7
-}
-
-register SDSCB_QOFF {
- address 0x0F8
-}
-
-register QOFF_CTLSTA {
- address 0x0FA
- bit ESTABLISH_SCB_AVAIL 0x80
- bit SCB_AVAIL 0x40
- bit SNSCB_ROLLOVER 0x20
- bit SDSCB_ROLLOVER 0x10
- bit SESCB_ROLLOVER 0x08
- mask SCB_QSIZE 0x07
- mask SCB_QSIZE_256 0x06
-}
-
-register DFF_THRSH {
- address 0x0FB
- mask WR_DFTHRSH 0x70
- mask RD_DFTHRSH 0x07
- mask RD_DFTHRSH_MIN 0x00
- mask RD_DFTHRSH_25 0x01
- mask RD_DFTHRSH_50 0x02
- mask RD_DFTHRSH_63 0x03
- mask RD_DFTHRSH_75 0x04
- mask RD_DFTHRSH_85 0x05
- mask RD_DFTHRSH_90 0x06
- mask RD_DFTHRSH_MAX 0x07
- mask WR_DFTHRSH_MIN 0x00
- mask WR_DFTHRSH_25 0x10
- mask WR_DFTHRSH_50 0x20
- mask WR_DFTHRSH_63 0x30
- mask WR_DFTHRSH_75 0x40
- mask WR_DFTHRSH_85 0x50
- mask WR_DFTHRSH_90 0x60
- mask WR_DFTHRSH_MAX 0x70
-}
-
-register SG_CACHEPTR {
- access_mode RW
- address 0x0fc
- mask SG_USER_DATA 0xfc
- bit LAST_SEG 0x02
- bit LAST_SEG_DONE 0x01
-}
-
-register BRDCTL {
- address 0x01d
- bit BRDDAT7 0x80
- bit BRDDAT6 0x40
- bit BRDDAT5 0x20
- bit BRDSTB 0x10
- bit BRDCS 0x08
- bit BRDRW 0x04
- bit BRDCTL1 0x02
- bit BRDCTL0 0x01
- /* 7890 Definitions */
- bit BRDDAT4 0x10
- bit BRDDAT3 0x08
- bit BRDDAT2 0x04
- bit BRDRW_ULTRA2 0x02
- bit BRDSTB_ULTRA2 0x01
-}
-
-/*
- * Serial EEPROM Control (p. 4-92 in 7870 Databook)
- * Controls the reading and writing of an external serial 1-bit
- * EEPROM Device. In order to access the serial EEPROM, you must
- * first set the SEEMS bit that generates a request to the memory
- * port for access to the serial EEPROM device. When the memory
- * port is not busy servicing another request, it reconfigures
- * to allow access to the serial EEPROM. When this happens, SEERDY
- * gets set high to verify that the memory port access has been
- * granted.
- *
- * After successful arbitration for the memory port, the SEECS bit of
- * the SEECTL register is connected to the chip select. The SEECK,
- * SEEDO, and SEEDI are connected to the clock, data out, and data in
- * lines respectively. The SEERDY bit of SEECTL is useful in that it
- * gives us an 800 nsec timer. After a write to the SEECTL register,
- * the SEERDY goes high 800 nsec later. The one exception to this is
- * when we first request access to the memory port. The SEERDY goes
- * high to signify that access has been granted and, for this case, has
- * no implied timing.
- *
- * See 93cx6.c for detailed information on the protocol necessary to
- * read the serial EEPROM.
- */
-register SEECTL {
- address 0x01e
- bit EXTARBACK 0x80
- bit EXTARBREQ 0x40
- bit SEEMS 0x20
- bit SEERDY 0x10
- bit SEECS 0x08
- bit SEECK 0x04
- bit SEEDO 0x02
- bit SEEDI 0x01
-}
-/* ---------------------- Scratch RAM Offsets ------------------------- */
-/* These offsets are either to values that are initialized by the board's
- * BIOS or are specified by the sequencer code.
- *
- * The host adapter card (at least the BIOS) uses 20-2f for SCSI
- * device information, 32-33 and 5a-5f as well. As it turns out, the
- * BIOS trashes 20-2f, writing the synchronous negotiation results
- * on top of the BIOS values, so we re-use those for our per-target
- * scratchspace (actually a value that can be copied directly into
- * SCSIRATE). The kernel driver will enable synchronous negotiation
- * for all targets that have a value other than 0 in the lower four
- * bits of the target scratch space. This should work regardless of
- * whether the bios has been installed.
- */
-
-scratch_ram {
- address 0x020
-
- /*
- * 1 byte per target starting at this address for configuration values
- */
- TARG_SCSIRATE {
- size 16
- }
- /*
- * Bit vector of targets that have ULTRA enabled.
- */
- ULTRA_ENB {
- size 2
- }
- /*
- * Bit vector of targets that have disconnection disabled.
- */
- DISC_DSB {
- size 2
- }
- /*
- * Single byte buffer used to designate the type or message
- * to send to a target.
- */
- MSG_OUT {
- size 1
- }
- /* Parameters for DMA Logic */
- DMAPARAMS {
- size 1
- bit PRELOADEN 0x80
- bit WIDEODD 0x40
- bit SCSIEN 0x20
- bit SDMAEN 0x10
- bit SDMAENACK 0x10
- bit HDMAEN 0x08
- bit HDMAENACK 0x08
- bit DIRECTION 0x04
- bit FIFOFLUSH 0x02
- bit FIFORESET 0x01
- }
- SEQ_FLAGS {
- size 1
- bit IDENTIFY_SEEN 0x80
- bit SCBPTR_VALID 0x20
- bit DPHASE 0x10
- bit AMTARGET 0x08
- bit WIDE_BUS 0x02
- bit TWIN_BUS 0x01
- }
- /*
- * Temporary storage for the
- * target/channel/lun of a
- * reconnecting target
- */
- SAVED_TCL {
- size 1
- }
- /* Working value of the number of SG segments left */
- SG_COUNT {
- size 1
- }
- /* Working value of SG pointer */
- SG_NEXT {
- size 4
- }
- /*
- * The last bus phase as seen by the sequencer.
- */
- LASTPHASE {
- size 1
- bit CDI 0x80
- bit IOI 0x40
- bit MSGI 0x20
- mask PHASE_MASK CDI|IOI|MSGI
- mask P_DATAOUT 0x00
- mask P_DATAIN IOI
- mask P_COMMAND CDI
- mask P_MESGOUT CDI|MSGI
- mask P_STATUS CDI|IOI
- mask P_MESGIN CDI|IOI|MSGI
- mask P_BUSFREE 0x01
- }
- /*
- * head of list of SCBs awaiting
- * selection
- */
- WAITING_SCBH {
- size 1
- }
- /*
- * head of list of SCBs that are
- * disconnected. Used for SCB
- * paging.
- */
- DISCONNECTED_SCBH {
- size 1
- }
- /*
- * head of list of SCBs that are
- * not in use. Used for SCB paging.
- */
- FREE_SCBH {
- size 1
- }
- /*
- * Address of the hardware scb array in the host.
- */
- HSCB_ADDR {
- size 4
- }
- /*
- * Address of the 256 byte array storing the SCBID of outstanding
- * untagged SCBs indexed by TCL.
- */
- SCBID_ADDR {
- size 4
- }
- /*
- * Address of the array of command descriptors used to store
- * information about incoming selections.
- */
- TMODE_CMDADDR {
- size 4
- }
- KERNEL_QINPOS {
- size 1
- }
- QINPOS {
- size 1
- }
- QOUTPOS {
- size 1
- }
- /*
- * Offset into the command descriptor array for the next
- * available desciptor to use.
- */
- TMODE_CMDADDR_NEXT {
- size 1
- }
- ARG_1 {
- size 1
- mask SEND_MSG 0x80
- mask SEND_SENSE 0x40
- mask SEND_REJ 0x20
- mask MSGOUT_PHASEMIS 0x10
- alias RETURN_1
- }
- ARG_2 {
- size 1
- alias RETURN_2
- }
-
- /*
- * Snapshot of MSG_OUT taken after each message is sent.
- */
- LAST_MSG {
- size 1
- }
-
- /*
- * Number of times we have filled the CCSGRAM with prefetched
- * SG elements.
- */
- PREFETCH_CNT {
- size 1
- }
-
-
- /*
- * These are reserved registers in the card's scratch ram. Some of
- * the values are specified in the AHA2742 technical reference manual
- * and are initialized by the BIOS at boot time.
- */
- SCSICONF {
- address 0x05a
- size 1
- bit TERM_ENB 0x80
- bit RESET_SCSI 0x40
- mask HSCSIID 0x07 /* our SCSI ID */
- mask HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
- }
- HOSTCONF {
- address 0x05d
- size 1
- }
- HA_274_BIOSCTRL {
- address 0x05f
- size 1
- mask BIOSMODE 0x30
- mask BIOSDISABLED 0x30
- bit CHANNEL_B_PRIMARY 0x08
- }
- /*
- * Per target SCSI offset values for Ultra2 controllers.
- */
- TARG_OFFSET {
- address 0x070
- size 16
- }
-}
-
-const SCB_LIST_NULL 0xff
-
-const CCSGADDR_MAX 0x80
-const CCSGRAM_MAXSEGS 16
-
-/* Offsets into the SCBID array where different data is stored */
-const UNTAGGEDSCB_OFFSET 0
-const QOUTFIFO_OFFSET 1
-const QINFIFO_OFFSET 2
-
-/* WDTR Message values */
-const BUS_8_BIT 0x00
-const BUS_16_BIT 0x01
-const BUS_32_BIT 0x02
-
-/* Offset maximums */
-const MAX_OFFSET_8BIT 0x0f
-const MAX_OFFSET_16BIT 0x08
-const MAX_OFFSET_ULTRA2 0x7f
-const HOST_MSG 0xff
-
-/* Target mode command processing constants */
-const CMD_GROUP_CODE_SHIFT 0x05
-const CMD_GROUP0_BYTE_DELTA -4
-const CMD_GROUP2_BYTE_DELTA -6
-const CMD_GROUP4_BYTE_DELTA 4
-const CMD_GROUP5_BYTE_DELTA 11
-
-/*
- * Downloaded (kernel inserted) constants
- */
-
-/*
- * Number of command descriptors in the command descriptor array.
- */
-const TMODE_NUMCMDS download
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.seq b/drivers/scsi/aic7xxx_old/aic7xxx.seq
deleted file mode 100644
index 823ff2873229..000000000000
--- a/drivers/scsi/aic7xxx_old/aic7xxx.seq
+++ /dev/null
@@ -1,1539 +0,0 @@
-/*
- * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
- *
- * Copyright (c) 1994-1999 Justin Gibbs.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification, immediately at the beginning of the file.
- * 2. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * Where this Software is combined with software released under the terms of
- * the GNU General Public License (GPL) and the terms of the GPL would require the
- * combined work to also be released under the terms of the GPL, the terms
- * and conditions of this License will apply in addition to those of the
- * GPL with the exception of any terms or conditions of this License that
- * conflict with, or are expressly prohibited by, the GPL.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
- */
-
-#include "aic7xxx.reg"
-#include "scsi_message.h"
-
-/*
- * A few words on the waiting SCB list:
- * After starting the selection hardware, we check for reconnecting targets
- * as well as for our selection to complete just in case the reselection wins
- * bus arbitration. The problem with this is that we must keep track of the
- * SCB that we've already pulled from the QINFIFO and started the selection
- * on just in case the reselection wins so that we can retry the selection at
- * a later time. This problem cannot be resolved by holding a single entry
- * in scratch ram since a reconnecting target can request sense and this will
- * create yet another SCB waiting for selection. The solution used here is to
- * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
- * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes,
- * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to
- * this list every time a request sense occurs or after completing a non-tagged
- * command for which a second SCB has been queued. The sequencer will
- * automatically consume the entries.
- */
-
-reset:
- clr SCSISIGO; /* De-assert BSY */
- and SXFRCTL1, ~BITBUCKET;
- /* Always allow reselection */
- mvi SCSISEQ, ENRSELI|ENAUTOATNP;
-
- if ((p->features & AHC_CMD_CHAN) != 0) {
- /* Ensure that no DMA operations are in progress */
- clr CCSGCTL;
- clr CCSCBCTL;
- }
-
- call clear_target_state;
-poll_for_work:
- and SXFRCTL0, ~SPIOEN;
- if ((p->features & AHC_QUEUE_REGS) == 0) {
- mov A, QINPOS;
- }
-poll_for_work_loop:
- if ((p->features & AHC_QUEUE_REGS) == 0) {
- and SEQCTL, ~PAUSEDIS;
- }
- test SSTAT0, SELDO|SELDI jnz selection;
- test SCSISEQ, ENSELO jnz poll_for_work;
- if ((p->features & AHC_TWIN) != 0) {
- /*
- * Twin channel devices cannot handle things like SELTO
- * interrupts on the "background" channel. So, if we
- * are selecting, keep polling the current channel util
- * either a selection or reselection occurs.
- */
- xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
- test SSTAT0, SELDO|SELDI jnz selection;
- test SCSISEQ, ENSELO jnz poll_for_work;
- xor SBLKCTL,SELBUSB; /* Toggle back */
- }
- cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
-test_queue:
- /* Has the driver posted any work for us? */
- if ((p->features & AHC_QUEUE_REGS) != 0) {
- test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
- mov NONE, SNSCB_QOFF;
- inc QINPOS;
- } else {
- or SEQCTL, PAUSEDIS;
- cmp KERNEL_QINPOS, A je poll_for_work_loop;
- inc QINPOS;
- and SEQCTL, ~PAUSEDIS;
- }
-
-/*
- * We have at least one queued SCB now and we don't have any
- * SCBs in the list of SCBs awaiting selection. If we have
- * any SCBs available for use, pull the tag from the QINFIFO
- * and get to work on it.
- */
- if ((p->flags & AHC_PAGESCBS) != 0) {
- mov ALLZEROS call get_free_or_disc_scb;
- }
-
-dequeue_scb:
- add A, -1, QINPOS;
- mvi QINFIFO_OFFSET call fetch_byte;
-
- if ((p->flags & AHC_PAGESCBS) == 0) {
- /* In the non-paging case, the SCBID == hardware SCB index */
- mov SCBPTR, RETURN_2;
- }
-dma_queued_scb:
-/*
- * DMA the SCB from host ram into the current SCB location.
- */
- mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov RETURN_2 call dma_scb;
-
-/*
- * Preset the residual fields in case we never go through a data phase.
- * This isn't done by the host so we can avoid a DMA to clear these
- * fields for the normal case of I/O that completes without underrun
- * or overrun conditions.
- */
- if ((p->features & AHC_CMD_CHAN) != 0) {
- bmov SCB_RESID_DCNT, SCB_DATACNT, 3;
- } else {
- mov SCB_RESID_DCNT[0],SCB_DATACNT[0];
- mov SCB_RESID_DCNT[1],SCB_DATACNT[1];
- mov SCB_RESID_DCNT[2],SCB_DATACNT[2];
- }
- mov SCB_RESID_SGCNT, SCB_SGCOUNT;
-
-start_scb:
- /*
- * Place us on the waiting list in case our selection
- * doesn't win during bus arbitration.
- */
- mov SCB_NEXT,WAITING_SCBH;
- mov WAITING_SCBH, SCBPTR;
-start_waiting:
- /*
- * Pull the first entry off of the waiting SCB list.
- */
- mov SCBPTR, WAITING_SCBH;
- call start_selection;
- jmp poll_for_work;
-
-start_selection:
- if ((p->features & AHC_TWIN) != 0) {
- and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
- and A,SELBUSB,SCB_TCL; /* Get new channel bit */
- or SINDEX,A;
- mov SBLKCTL,SINDEX; /* select channel */
- }
-initialize_scsiid:
- if ((p->features & AHC_ULTRA2) != 0) {
- and A, TID, SCB_TCL; /* Get target ID */
- and SCSIID_ULTRA2, OID; /* Clear old target */
- or SCSIID_ULTRA2, A;
- } else {
- and A, TID, SCB_TCL; /* Get target ID */
- and SCSIID, OID; /* Clear old target */
- or SCSIID, A;
- }
- mov SCSIDATL, ALLZEROS; /* clear out the latched */
- /* data register, this */
- /* fixes a bug on some */
- /* controllers where the */
- /* last byte written to */
- /* this register can leak */
- /* onto the data bus at */
- /* bad times, such as during */
- /* selection timeouts */
- mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
-
-/*
- * Initialize Ultra mode setting and clear the SCSI channel.
- * SINDEX should contain any additional bit's the client wants
- * set in SXFRCTL0.
- */
-initialize_channel:
- or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
- if ((p->features & AHC_ULTRA) != 0) {
-ultra:
- mvi SINDEX, ULTRA_ENB+1;
- test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */
- dec SINDEX;
-ultra_2:
- mov FUNCTION1,SAVED_TCL;
- mov A,FUNCTION1;
- test SINDIR, A jz ndx_dtr;
- or SXFRCTL0, FAST20;
- }
-/*
- * Initialize SCSIRATE with the appropriate value for this target.
- * The SCSIRATE settings for each target are stored in an array
- * based at TARG_SCSIRATE.
- */
-ndx_dtr:
- shr A,4,SAVED_TCL;
- if ((p->features & AHC_TWIN) != 0) {
- test SBLKCTL,SELBUSB jz ndx_dtr_2;
- or SAVED_TCL, SELBUSB;
- or A,0x08; /* Channel B entries add 8 */
-ndx_dtr_2:
- }
-
- if ((p->features & AHC_ULTRA2) != 0) {
- add SINDEX, TARG_OFFSET, A;
- mov SCSIOFFSET, SINDIR;
- }
-
- add SINDEX,TARG_SCSIRATE,A;
- mov SCSIRATE,SINDIR ret;
-
-
-selection:
- test SSTAT0,SELDO jnz select_out;
-/*
- * Reselection has been initiated by a target. Make a note that we've been
- * reselected, but haven't seen an IDENTIFY message from the target yet.
- */
-initiator_reselect:
- mvi CLRSINT0, CLRSELDI;
- /* XXX test for and handle ONE BIT condition */
- and SAVED_TCL, SELID_MASK, SELID;
- mvi CLRSINT1,CLRBUSFREE;
- or SIMODE1, ENBUSFREE; /*
- * We aren't expecting a
- * bus free, so interrupt
- * the kernel driver if it
- * happens.
- */
- mvi SPIOEN call initialize_channel;
- mvi MSG_OUT, MSG_NOOP; /* No message to send */
- jmp ITloop;
-
-/*
- * After the selection, remove this SCB from the "waiting SCB"
- * list. This is achieved by simply moving our "next" pointer into
- * WAITING_SCBH. Our next pointer will be set to null the next time this
- * SCB is used, so don't bother with it now.
- */
-select_out:
- /* Turn off the selection hardware */
- mvi SCSISEQ, ENRSELI|ENAUTOATNP; /*
- * ATN on parity errors
- * for "in" phases
- */
- mvi CLRSINT0, CLRSELDO;
- mov SCBPTR, WAITING_SCBH;
- mov WAITING_SCBH,SCB_NEXT;
- mov SAVED_TCL, SCB_TCL;
- mvi CLRSINT1,CLRBUSFREE;
- or SIMODE1, ENBUSFREE; /*
- * We aren't expecting a
- * bus free, so interrupt
- * the kernel driver if it
- * happens.
- */
- mvi SPIOEN call initialize_channel;
-/*
- * As soon as we get a successful selection, the target should go
- * into the message out phase since we have ATN asserted.
- */
- mvi MSG_OUT, MSG_IDENTIFYFLAG;
- or SEQ_FLAGS, IDENTIFY_SEEN;
-
-/*
- * Main loop for information transfer phases. Wait for the target
- * to assert REQ before checking MSG, C/D and I/O for the bus phase.
- */
-ITloop:
- call phase_lock;
-
- mov A, LASTPHASE;
-
- test A, ~P_DATAIN jz p_data;
- cmp A,P_COMMAND je p_command;
- cmp A,P_MESGOUT je p_mesgout;
- cmp A,P_STATUS je p_status;
- cmp A,P_MESGIN je p_mesgin;
-
- mvi INTSTAT,BAD_PHASE; /* unknown phase - signal driver */
- jmp ITloop; /* Try reading the bus again. */
-
-await_busfree:
- and SIMODE1, ~ENBUSFREE;
- call clear_target_state;
- mov NONE, SCSIDATL; /* Ack the last byte */
- and SXFRCTL0, ~SPIOEN;
- test SSTAT1,REQINIT|BUSFREE jz .;
- test SSTAT1, BUSFREE jnz poll_for_work;
- mvi INTSTAT, BAD_PHASE;
-
-clear_target_state:
- /*
- * We assume that the kernel driver may reset us
- * at any time, even in the middle of a DMA, so
- * clear DFCNTRL too.
- */
- clr DFCNTRL;
-
- /*
- * We don't know the target we will connect to,
- * so default to narrow transfers to avoid
- * parity problems.
- */
- if ((p->features & AHC_ULTRA2) != 0) {
- bmov SCSIRATE, ALLZEROS, 2;
- } else {
- clr SCSIRATE;
- and SXFRCTL0, ~(FAST20);
- }
- mvi LASTPHASE, P_BUSFREE;
- /* clear target specific flags */
- clr SEQ_FLAGS ret;
-
-
-data_phase_reinit:
-/*
- * If we re-enter the data phase after going through another phase, the
- * STCNT may have been cleared, so restore it from the residual field.
- * On Ultra2, we have to put it into the HCNT field because we have to
- * drop the data down into the shadow layer via the preload ability.
- */
- if ((p->features & AHC_ULTRA2) != 0) {
- bmov HADDR, SHADDR, 4;
- bmov HCNT, SCB_RESID_DCNT, 3;
- }
- if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
- bmov STCNT, SCB_RESID_DCNT, 3;
- }
- if ((p->features & AHC_CMD_CHAN) == 0) {
- mvi DINDEX, STCNT;
- mvi SCB_RESID_DCNT call bcopy_3;
- }
- jmp data_phase_loop;
-p_data:
- if ((p->features & AHC_ULTRA2) != 0) {
- mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
- } else {
- mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
- }
- test LASTPHASE, IOI jnz . + 2;
- or DMAPARAMS, DIRECTION;
- call assert; /*
- * Ensure entering a data
- * phase is okay - seen identify, etc.
- */
- if ((p->features & AHC_CMD_CHAN) != 0) {
- mvi CCSGADDR, CCSGADDR_MAX;
- }
-
- test SEQ_FLAGS, DPHASE jnz data_phase_reinit;
- or SEQ_FLAGS, DPHASE; /* we've seen a data phase */
- /*
- * Initialize the DMA address and counter from the SCB.
- * Also set SG_COUNT and SG_NEXT in memory since we cannot
- * modify the values in the SCB itself until we see a
- * save data pointers message.
- */
- if ((p->features & AHC_CMD_CHAN) != 0) {
- bmov HADDR, SCB_DATAPTR, 7;
- bmov SG_COUNT, SCB_SGCOUNT, 5;
- if ((p->features & AHC_ULTRA2) == 0) {
- bmov STCNT, HCNT, 3;
- }
- } else {
- mvi DINDEX, HADDR;
- mvi SCB_DATAPTR call bcopy_7;
- call set_stcnt_from_hcnt;
- mvi DINDEX, SG_COUNT;
- mvi SCB_SGCOUNT call bcopy_5;
- }
-data_phase_loop:
- /* Guard against overruns */
- test SG_COUNT, 0xff jnz data_phase_inbounds;
-/*
- * Turn on 'Bit Bucket' mode, set the transfer count to
- * 16meg and let the target run until it changes phase.
- * When the transfer completes, notify the host that we
- * had an overrun.
- */
- or SXFRCTL1,BITBUCKET;
- and DMAPARAMS, ~(HDMAEN|SDMAEN);
- if ((p->features & AHC_ULTRA2) != 0) {
- bmov HCNT, ALLONES, 3;
- }
- if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
- bmov STCNT, ALLONES, 3;
- }
- if ((p->features & AHC_CMD_CHAN) == 0) {
- mvi STCNT[0], 0xFF;
- mvi STCNT[1], 0xFF;
- mvi STCNT[2], 0xFF;
- }
-
-data_phase_inbounds:
-/* If we are the last SG block, tell the hardware. */
- if ((p->features & AHC_ULTRA2) != 0) {
- shl A, 2, SG_COUNT;
- cmp SG_COUNT,0x01 jne data_phase_wideodd;
- or A, LAST_SEG;
- } else {
- cmp SG_COUNT,0x01 jne data_phase_wideodd;
- and DMAPARAMS, ~WIDEODD;
- }
-data_phase_wideodd:
- if ((p->features & AHC_ULTRA2) != 0) {
- mov SG_CACHEPTR, A;
- mov DFCNTRL, DMAPARAMS; /* start the operation */
- test SXFRCTL1, BITBUCKET jnz data_phase_overrun;
-u2_preload_wait:
- test SSTAT1, PHASEMIS jnz u2_phasemis;
- test DFSTATUS, PRELOAD_AVAIL jz u2_preload_wait;
- } else {
- mov DMAPARAMS call dma;
-data_phase_dma_done:
-/* Go tell the host about any overruns */
- test SXFRCTL1,BITBUCKET jnz data_phase_overrun;
-
-/* Exit if we had an underrun. dma clears SINDEX in this case. */
- test SINDEX,0xff jz data_phase_finish;
- }
-/*
- * Advance the scatter-gather pointers
- */
-sg_advance:
- if ((p->features & AHC_ULTRA2) != 0) {
- cmp SG_COUNT, 0x01 je u2_data_phase_finish;
- } else {
- dec SG_COUNT;
- test SG_COUNT, 0xff jz data_phase_finish;
- }
-
- if ((p->features & AHC_CMD_CHAN) != 0) {
-
- /*
- * Do we have any prefetch left???
- */
- cmp CCSGADDR, CCSGADDR_MAX jne prefetch_avail;
-
- /*
- * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
- */
- add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
- mvi A, CCSGADDR_MAX;
- jc . + 2;
- shl A, 3, SG_COUNT;
- mov CCHCNT, A;
- bmov CCHADDR, SG_NEXT, 4;
- mvi CCSGCTL, CCSGEN|CCSGRESET;
- test CCSGCTL, CCSGDONE jz .;
- and CCSGCTL, ~CCSGEN;
- test CCSGCTL, CCSGEN jnz .;
- mvi CCSGCTL, CCSGRESET;
-prefetch_avail:
- bmov HADDR, CCSGRAM, 8;
- if ((p->features & AHC_ULTRA2) == 0) {
- bmov STCNT, HCNT, 3;
- } else {
- dec SG_COUNT;
- }
- } else {
- mvi DINDEX, HADDR;
- mvi SG_NEXT call bcopy_4;
-
- mvi HCNT[0],SG_SIZEOF;
- clr HCNT[1];
- clr HCNT[2];
-
- or DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-
- call dma_finish;
-
-/*
- * Copy data from FIFO into SCB data pointer and data count.
- * This assumes that the SG segments are of the form:
- * struct ahc_dma_seg {
- * u_int32_t addr; four bytes, little-endian order
- * u_int32_t len; four bytes, little endian order
- * };
- */
- mvi DINDEX, HADDR;
- call dfdat_in_7;
- call set_stcnt_from_hcnt;
- }
-/* Advance the SG pointer */
- clr A; /* add sizeof(struct scatter) */
- add SG_NEXT[0],SG_SIZEOF;
- adc SG_NEXT[1],A;
-
- if ((p->features & AHC_ULTRA2) != 0) {
- jmp data_phase_loop;
- } else {
- test SSTAT1, REQINIT jz .;
- test SSTAT1,PHASEMIS jz data_phase_loop;
- }
-
-
-/*
- * We've loaded all of our segments into the preload layer. Now, we simply
- * have to wait for it to finish or for us to get a phasemis. And, since
- * we'll get a phasemis if we do finish, all we really need to do is wait
- * for a phasemis then check if we did actually complete all the segments.
- */
- if ((p->features & AHC_ULTRA2) != 0) {
-u2_data_phase_finish:
- test SSTAT1, PHASEMIS jnz u2_phasemis;
- test SG_CACHEPTR, LAST_SEG_DONE jz u2_data_phase_finish;
- clr SG_COUNT;
- test SSTAT1, REQINIT jz .;
- test SSTAT1, PHASEMIS jz data_phase_loop;
-u2_phasemis:
- call ultra2_dmafinish;
- test SG_CACHEPTR, LAST_SEG_DONE jnz data_phase_finish;
- test SSTAT2, SHVALID jnz u2_fixup_residual;
- mvi INTSTAT, SEQ_SG_FIXUP;
- jmp data_phase_finish;
-u2_fixup_residual:
- shr ARG_1, 2, SG_CACHEPTR;
-u2_phasemis_loop:
- and A, 0x3f, SG_COUNT;
- cmp ARG_1, A je data_phase_finish;
-/*
- * Subtract SG_SIZEOF from the SG_NEXT pointer and add 1 to the SG_COUNT
- */
- clr A;
- add SG_NEXT[0], -SG_SIZEOF;
- adc SG_NEXT[1], 0xff;
- inc SG_COUNT;
- jmp u2_phasemis_loop;
- }
-
-data_phase_finish:
-/*
- * After a DMA finishes, save the SG and STCNT residuals back into the SCB
- * We use STCNT instead of HCNT, since it's a reflection of how many bytes
- * were transferred on the SCSI (as opposed to the host) bus.
- */
- if ((p->features & AHC_CMD_CHAN) != 0) {
- bmov SCB_RESID_DCNT, STCNT, 3;
- mov SCB_RESID_SGCNT, SG_COUNT;
- if ((p->features & AHC_ULTRA2) != 0) {
- or SXFRCTL0, CLRSTCNT|CLRCHN;
- }
- } else {
- mov SCB_RESID_DCNT[0],STCNT[0];
- mov SCB_RESID_DCNT[1],STCNT[1];
- mov SCB_RESID_DCNT[2],STCNT[2];
- mov SCB_RESID_SGCNT, SG_COUNT;
- }
-
- jmp ITloop;
-
-data_phase_overrun:
-/*
- * Turn off BITBUCKET mode and notify the host
- */
- if ((p->features & AHC_ULTRA2) != 0) {
-/*
- * Wait for the target to quit transferring data on the SCSI bus
- */
- test SSTAT1, PHASEMIS jz .;
- call ultra2_dmafinish;
- }
- and SXFRCTL1, ~BITBUCKET;
- mvi INTSTAT,DATA_OVERRUN;
- jmp ITloop;
-
-
-
-
-/*
- * Actually turn off the DMA hardware, save our current position into the
- * proper residual variables, wait for the next REQ signal, then jump to
- * the ITloop. Jumping to the ITloop ensures that if we happen to get
- * brought into the data phase again (or are still in it after our last
- * segment) that we will properly signal an overrun to the kernel.
- */
- if ((p->features & AHC_ULTRA2) != 0) {
-ultra2_dmafinish:
- test DFCNTRL, DIRECTION jnz ultra2_dmahalt;
- and DFCNTRL, ~SCSIEN;
- test DFCNTRL, SCSIEN jnz .;
- if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) {
- or DFCNTRL, FIFOFLUSH;
- }
-ultra2_dmafifoflush:
- if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) {
- /*
- * hardware bug alert! This needless set of jumps
- * works around a glitch in the silicon. When the
- * PCI DMA fifo goes empty, but there is still SCSI
- * data to be flushed into the PCI DMA fifo (and from
- * there on into main memory), the FIFOEMP bit will
- * come on between the time when the PCI DMA buffer
- * went empty and the next bit of data is copied from
- * the SCSI fifo into the PCI fifo. It should only
- * come on when both FIFOs (meaning the entire FIFO
- * chain) are empty. Since it can take up to 4 cycles
- * for new data to be copied from the SCSI fifo into
- * the PCI fifo, testing for FIFOEMP status for 4
- * extra times gives the needed time for any
- * remaining SCSI fifo data to be put in the PCI fifo
- * before we declare it *truly* empty.
- */
- test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
- test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
- test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
- test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
- }
- test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
- test DFSTATUS, MREQPEND jnz .;
-ultra2_dmahalt:
- and DFCNTRL, ~(HDMAEN|SCSIEN);
- test DFCNTRL, (HDMAEN|SCSIEN) jnz .;
- ret;
- }
-
-/*
- * Command phase. Set up the DMA registers and let 'er rip.
- */
-p_command:
- call assert;
-
-/*
- * Load HADDR and HCNT.
- */
- if ((p->features & AHC_CMD_CHAN) != 0) {
- bmov HADDR, SCB_CMDPTR, 5;
- bmov HCNT[1], ALLZEROS, 2;
- if ((p->features & AHC_ULTRA2) == 0) {
- bmov STCNT, HCNT, 3;
- }
- } else {
- mvi DINDEX, HADDR;
- mvi SCB_CMDPTR call bcopy_5;
- clr HCNT[1];
- clr HCNT[2];
- call set_stcnt_from_hcnt;
- }
-
- if ((p->features & AHC_ULTRA2) == 0) {
- mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
- } else {
- mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
- test SSTAT0, SDONE jnz .;
-p_command_dma_loop:
- test SSTAT0, SDONE jnz p_command_ultra2_dma_done;
- test SSTAT1,PHASEMIS jz p_command_dma_loop; /* ie. underrun */
-p_command_ultra2_dma_done:
- test SCSISIGI, REQI jz p_command_ultra2_shutdown;
- test SSTAT1, (PHASEMIS|REQINIT) jz p_command_ultra2_dma_done;
-p_command_ultra2_shutdown:
- and DFCNTRL, ~(HDMAEN|SCSIEN);
- test DFCNTRL, (HDMAEN|SCSIEN) jnz .;
- or SXFRCTL0, CLRSTCNT|CLRCHN;
- }
- jmp ITloop;
-
-/*
- * Status phase. Wait for the data byte to appear, then read it
- * and store it into the SCB.
- */
-p_status:
- call assert;
-
- mov SCB_TARGET_STATUS, SCSIDATL;
- jmp ITloop;
-
-/*
- * Message out phase. If MSG_OUT is 0x80, build I full indentify message
- * sequence and send it to the target. In addition, if the MK_MESSAGE bit
- * is set in the SCB_CONTROL byte, interrupt the host and allow it to send
- * it's own message.
- *
- * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
- * This is done to allow the hsot to send messages outside of an identify
- * sequence while protecting the seqencer from testing the MK_MESSAGE bit
- * on an SCB that might not be for the current nexus. (For example, a
- * BDR message in response to a bad reselection would leave us pointed to
- * an SCB that doesn't have anything to do with the current target).
- * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
- * bus device reset).
- *
- * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
- * in case the target decides to put us in this phase for some strange
- * reason.
- */
-p_mesgout_retry:
- or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
-p_mesgout:
- mov SINDEX, MSG_OUT;
- cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
-p_mesgout_identify:
- if ((p->features & AHC_WIDE) != 0) {
- and SINDEX,0xf,SCB_TCL; /* lun */
- } else {
- and SINDEX,0x7,SCB_TCL; /* lun */
- }
- and A,DISCENB,SCB_CONTROL; /* mask off disconnect privilege */
- or SINDEX,A; /* or in disconnect privilege */
- or SINDEX,MSG_IDENTIFYFLAG;
-p_mesgout_mk_message:
- test SCB_CONTROL,MK_MESSAGE jz p_mesgout_tag;
- mov SCSIDATL, SINDEX; /* Send the last byte */
- jmp p_mesgout_from_host + 1;/* Skip HOST_MSG test */
-/*
- * Send a tag message if TAG_ENB is set in the SCB control block.
- * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
- */
-p_mesgout_tag:
- test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
- mov SCSIDATL, SINDEX; /* Send the identify message */
- call phase_lock;
- cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
- and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
- call phase_lock;
- cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
- mov SCB_TAG jmp p_mesgout_onebyte;
-/*
- * Interrupt the driver, and allow it to send a message
- * if it asks.
- */
-p_mesgout_from_host:
- cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
- mvi INTSTAT,AWAITING_MSG;
- nop;
- /*
- * Did the host detect a phase change?
- */
- cmp RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done;
-
-p_mesgout_onebyte:
- mvi CLRSINT1, CLRATNO;
- mov SCSIDATL, SINDEX;
-
-/*
- * If the next bus phase after ATN drops is a message out, it means
- * that the target is requesting that the last message(s) be resent.
- */
- call phase_lock;
- cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
-
-p_mesgout_done:
- mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
- mov LAST_MSG, MSG_OUT;
- cmp MSG_OUT, MSG_IDENTIFYFLAG jne . + 2;
- and SCB_CONTROL, ~MK_MESSAGE;
- mvi MSG_OUT, MSG_NOOP; /* No message left */
- jmp ITloop;
-
-/*
- * Message in phase. Bytes are read using Automatic PIO mode.
- */
-p_mesgin:
- mvi ACCUM call inb_first; /* read the 1st message byte */
-
- test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
- cmp A,MSG_DISCONNECT je mesgin_disconnect;
- cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
- cmp ALLZEROS,A je mesgin_complete;
- cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
- cmp A,MSG_EXTENDED je mesgin_extended;
- cmp A,MSG_MESSAGE_REJECT je mesgin_reject;
- cmp A,MSG_NOOP je mesgin_done;
- cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_wide_residue;
-
-rej_mesgin:
-/*
- * We have no idea what this message in is, so we issue a message reject
- * and hope for the best. In any case, rejection should be a rare
- * occurrence - signal the driver when it happens.
- */
- mvi INTSTAT,SEND_REJECT; /* let driver know */
-
- mvi MSG_MESSAGE_REJECT call mk_mesg;
-
-mesgin_done:
- mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
- jmp ITloop;
-
-
-mesgin_complete:
-/*
- * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
- * and trigger a completion interrupt. Before doing so, check to see if there
- * is a residual or the status byte is something other than STATUS_GOOD (0).
- * In either of these conditions, we upload the SCB back to the host so it can
- * process this information. In the case of a non zero status byte, we
- * additionally interrupt the kernel driver synchronously, allowing it to
- * decide if sense should be retrieved. If the kernel driver wishes to request
- * sense, it will fill the kernel SCB with a request sense command and set
- * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload
- * the SCB, and process it as the next command by adding it to the waiting list.
- * If the kernel driver does not wish to request sense, it need only clear
- * RETURN_1, and the command is allowed to complete normally. We don't bother
- * to post to the QOUTFIFO in the error cases since it would require extra
- * work in the kernel driver to ensure that the entry was removed before the
- * command complete code tried processing it.
- */
-
-/*
- * First check for residuals
- */
- test SCB_RESID_SGCNT,0xff jnz upload_scb;
- test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */
-upload_scb:
- mvi DMAPARAMS, FIFORESET;
- mov SCB_TAG call dma_scb;
-check_status:
- test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */
- mvi INTSTAT,BAD_STATUS; /* let driver know */
- nop;
- cmp RETURN_1, SEND_SENSE jne complete;
- /* This SCB becomes the next to execute as it will retrieve sense */
- mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov SCB_TAG call dma_scb;
-add_to_waiting_list:
- mov SCB_NEXT,WAITING_SCBH;
- mov WAITING_SCBH, SCBPTR;
- /*
- * Prepare our selection hardware before the busfree so we have a
- * high probability of winning arbitration.
- */
- call start_selection;
- jmp await_busfree;
-
-complete:
- /* If we are untagged, clear our address up in host ram */
- test SCB_CONTROL, TAG_ENB jnz complete_post;
- mov A, SAVED_TCL;
- mvi UNTAGGEDSCB_OFFSET call post_byte_setup;
- mvi SCB_LIST_NULL call post_byte;
-
-complete_post:
- /* Post the SCB and issue an interrupt */
- if ((p->features & AHC_QUEUE_REGS) != 0) {
- mov A, SDSCB_QOFF;
- } else {
- mov A, QOUTPOS;
- }
- mvi QOUTFIFO_OFFSET call post_byte_setup;
- mov SCB_TAG call post_byte;
- if ((p->features & AHC_QUEUE_REGS) == 0) {
- inc QOUTPOS;
- }
- mvi INTSTAT,CMDCMPLT;
-
-add_to_free_list:
- call add_scb_to_free_list;
- jmp await_busfree;
-
-/*
- * Is it an extended message? Copy the message to our message buffer and
- * notify the host. The host will tell us whether to reject this message,
- * respond to it with the message that the host placed in our message buffer,
- * or simply to do nothing.
- */
-mesgin_extended:
- mvi INTSTAT,EXTENDED_MSG; /* let driver know */
- jmp ITloop;
-
-/*
- * Is it a disconnect message? Set a flag in the SCB to remind us
- * and await the bus going free.
- */
-mesgin_disconnect:
- or SCB_CONTROL,DISCONNECTED;
- call add_scb_to_disc_list;
- jmp await_busfree;
-
-/*
- * Save data pointers message:
- * Copying RAM values back to SCB, for Save Data Pointers message, but
- * only if we've actually been into a data phase to change them. This
- * protects against bogus data in scratch ram and the residual counts
- * since they are only initialized when we go into data_in or data_out.
- */
-mesgin_sdptrs:
- test SEQ_FLAGS, DPHASE jz mesgin_done;
- /*
- * The SCB SGPTR becomes the next one we'll download,
- * and the SCB DATAPTR becomes the current SHADDR.
- * Use the residual number since STCNT is corrupted by
- * any message transfer.
- */
- if ((p->features & AHC_CMD_CHAN) != 0) {
- bmov SCB_SGCOUNT, SG_COUNT, 5;
- bmov SCB_DATAPTR, SHADDR, 4;
- bmov SCB_DATACNT, SCB_RESID_DCNT, 3;
- } else {
- mvi DINDEX, SCB_SGCOUNT;
- mvi SG_COUNT call bcopy_5;
- mvi DINDEX, SCB_DATAPTR;
- mvi SHADDR call bcopy_4;
- mvi SCB_RESID_DCNT call bcopy_3;
- }
- jmp mesgin_done;
-
-/*
- * Restore pointers message? Data pointers are recopied from the
- * SCB anytime we enter a data phase for the first time, so all
- * we need to do is clear the DPHASE flag and let the data phase
- * code do the rest.
- */
-mesgin_rdptrs:
- and SEQ_FLAGS, ~DPHASE; /*
- * We'll reload them
- * the next time through
- * the dataphase.
- */
- jmp mesgin_done;
-
-/*
- * Identify message? For a reconnecting target, this tells us the lun
- * that the reconnection is for - find the correct SCB and switch to it,
- * clearing the "disconnected" bit so we don't "find" it by accident later.
- */
-mesgin_identify:
-
- if ((p->features & AHC_WIDE) != 0) {
- and A,0x0f; /* lun in lower four bits */
- } else {
- and A,0x07; /* lun in lower three bits */
- }
- or SAVED_TCL,A; /* SAVED_TCL should be complete now */
-
- mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */
- call get_untagged_SCBID;
- cmp ARG_1, SCB_LIST_NULL je snoop_tag;
- if ((p->flags & AHC_PAGESCBS) != 0) {
- test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB;
- }
- /*
- * If the SCB was found in the disconnected list (as is
- * always the case in non-paging scenarios), SCBPTR is already
- * set to the correct SCB. So, simply setup the SCB and get
- * on with things.
- */
- mov SCBPTR call rem_scb_from_disc_list;
- jmp setup_SCB;
-/*
- * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
- * If we get one, we use the tag returned to find the proper
- * SCB. With SCB paging, this requires using search for both tagged
- * and non-tagged transactions since the SCB may exist in any slot.
- * If we're not using SCB paging, we can use the tag as the direct
- * index to the SCB.
- */
-snoop_tag:
- mov NONE,SCSIDATL; /* ACK Identify MSG */
-snoop_tag_loop:
- call phase_lock;
- cmp LASTPHASE, P_MESGIN jne not_found;
- cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
-get_tag:
- mvi ARG_1 call inb_next; /* tag value */
-
-use_retrieveSCB:
- call retrieveSCB;
-setup_SCB:
- mov A, SAVED_TCL;
- cmp SCB_TCL, A jne not_found_cleanup_scb;
- test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
- and SCB_CONTROL,~DISCONNECTED;
- or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
- /* See if the host wants to send a message upon reconnection */
- test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
- and SCB_CONTROL, ~MK_MESSAGE;
- mvi HOST_MSG call mk_mesg;
- jmp mesgin_done;
-
-not_found_cleanup_scb:
- test SCB_CONTROL, DISCONNECTED jz . + 3;
- call add_scb_to_disc_list;
- jmp not_found;
- call add_scb_to_free_list;
-not_found:
- mvi INTSTAT, NO_MATCH;
- mvi MSG_BUS_DEV_RESET call mk_mesg;
- jmp mesgin_done;
-
-/*
- * Message reject? Let the kernel driver handle this. If we have an
- * outstanding WDTR or SDTR negotiation, assume that it's a response from
- * the target selecting 8bit or asynchronous transfer, otherwise just ignore
- * it since we have no clue what it pertains to.
- */
-mesgin_reject:
- mvi INTSTAT, REJECT_MSG;
- jmp mesgin_done;
-
-/*
- * Wide Residue. We handle the simple cases, but pass of the one hard case
- * to the kernel (when the residue byte happened to cause us to advance our
- * sg element array, so we know have to back that advance out).
- */
-mesgin_wide_residue:
- mvi ARG_1 call inb_next; /* ACK the wide_residue and get */
- /* the size byte */
-/*
- * In order for this to be reliable, we have to do all sorts of horrible
- * magic in terms of resetting the datafifo and reloading the shadow layer
- * with the correct new values (so that a subsequent save data pointers
- * message will do the right thing). We let the kernel do that work.
- */
- mvi INTSTAT, WIDE_RESIDUE;
- jmp mesgin_done;
-
-/*
- * [ ADD MORE MESSAGE HANDLING HERE ]
- */
-
-/*
- * Locking the driver out, build a one-byte message passed in SINDEX
- * if there is no active message already. SINDEX is returned intact.
- */
-mk_mesg:
- or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
- mov MSG_OUT,SINDEX ret;
-
-/*
- * Functions to read data in Automatic PIO mode.
- *
- * According to Adaptec's documentation, an ACK is not sent on input from
- * the target until SCSIDATL is read from. So we wait until SCSIDATL is
- * latched (the usual way), then read the data byte directly off the bus
- * using SCSIBUSL. When we have pulled the ATN line, or we just want to
- * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
- * spec guarantees that the target will hold the data byte on the bus until
- * we send our ACK.
- *
- * The assumption here is that these are called in a particular sequence,
- * and that REQ is already set when inb_first is called. inb_{first,next}
- * use the same calling convention as inb.
- */
-
-inb_next:
- mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
-inb_next_wait:
- /*
- * If there is a parity error, wait for the kernel to
- * see the interrupt and prepare our message response
- * before continuing.
- */
- test SSTAT1, REQINIT jz inb_next_wait;
- test SSTAT1, SCSIPERR jnz .;
- and LASTPHASE, PHASE_MASK, SCSISIGI;
- cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;
-inb_first:
- mov DINDEX,SINDEX;
- mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/
-inb_last:
- mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/
-
-
-mesgin_phasemis:
-/*
- * We expected to receive another byte, but the target changed phase
- */
- mvi INTSTAT, MSGIN_PHASEMIS;
- jmp ITloop;
-
-/*
- * DMA data transfer. HADDR and HCNT must be loaded first, and
- * SINDEX should contain the value to load DFCNTRL with - 0x3d for
- * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
- * during initialization.
- */
-if ((p->features & AHC_ULTRA2) == 0) {
-dma:
- mov DFCNTRL,SINDEX;
-dma_loop:
- test SSTAT0,DMADONE jnz dma_dmadone;
- test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */
-dma_phasemis:
- test SSTAT0,SDONE jnz dma_checkfifo;
- mov SINDEX,ALLZEROS; /* Notify caller of phasemiss */
-
-/*
- * We will be "done" DMAing when the transfer count goes to zero, or
- * the target changes the phase (in light of this, it makes sense that
- * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
- * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
- * magically on STCNT=0 or a phase change, so just wait for FIFO empty
- * status.
- */
-dma_checkfifo:
- test DFCNTRL,DIRECTION jnz dma_fifoempty;
-dma_fifoflush:
- test DFSTATUS,FIFOEMP jz dma_fifoflush;
-
-dma_fifoempty:
- /* Don't clobber an inprogress host data transfer */
- test DFSTATUS, MREQPEND jnz dma_fifoempty;
-/*
- * Now shut the DMA enables off and make sure that the DMA enables are
- * actually off first lest we get an ILLSADDR.
- */
-dma_dmadone:
- cmp LASTPHASE, P_COMMAND je dma_await_nreq;
- test SCSIRATE, 0x0f jnz dma_shutdown;
-dma_await_nreq:
- test SCSISIGI, REQI jz dma_shutdown;
- test SSTAT1, (PHASEMIS|REQINIT) jz dma_await_nreq;
-dma_shutdown:
- and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
-dma_halt:
- /*
- * Some revisions of the aic7880 have a problem where, if the
- * data fifo is full, but the PCI input latch is not empty,
- * HDMAEN cannot be cleared. The fix used here is to attempt
- * to drain the data fifo until there is space for the input
- * latch to drain and HDMAEN de-asserts.
- */
- if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) {
- mov NONE, DFDAT;
- }
- test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
-}
-return:
- ret;
-
-/*
- * Assert that if we've been reselected, then we've seen an IDENTIFY
- * message.
- */
-assert:
- test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */
-
- mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */
-
-/*
- * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
- * or by the SCBID ARG_1. The search begins at the SCB index passed in
- * via SINDEX which is an SCB that must be on the disconnected list. If
- * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
- * is set to the proper SCB.
- */
-findSCB:
- mov SCBPTR,SINDEX; /* Initialize SCBPTR */
- cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID;
- mov A, SAVED_TCL;
- mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */
-findSCB_by_SCBID:
- mov A, ARG_1; /* Tag passed in ARG_1 */
- mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */
-findSCB_next:
- mov ARG_2, SCBPTR;
- cmp SCB_NEXT, SCB_LIST_NULL je notFound;
- mov SCBPTR,SCB_NEXT;
- dec SINDEX; /* Last comparison moved us too far */
-findSCB_loop:
- cmp SINDIR, A jne findSCB_next;
- mov SINDEX, SCBPTR ret;
-notFound:
- mvi SINDEX, SCB_LIST_NULL ret;
-
-/*
- * Retrieve an SCB by SCBID first searching the disconnected list falling
- * back to DMA'ing the SCB down from the host. This routine assumes that
- * ARG_1 is the SCBID of interest and that SINDEX is the position in the
- * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL,
- * we go directly to the host for the SCB.
- */
-retrieveSCB:
- test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host;
- mov SCBPTR call findSCB; /* Continue the search */
- cmp SINDEX, SCB_LIST_NULL je retrieve_from_host;
-
-/*
- * This routine expects SINDEX to contain the index of the SCB to be
- * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
- * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
- * if it is at the head.
- */
-rem_scb_from_disc_list:
-/* Remove this SCB from the disconnection list */
- cmp ARG_2, SCB_LIST_NULL je rHead;
- mov DINDEX, SCB_NEXT;
- mov SCBPTR, ARG_2;
- mov SCB_NEXT, DINDEX;
- mov SCBPTR, SINDEX ret;
-rHead:
- mov DISCONNECTED_SCBH,SCB_NEXT ret;
-
-retrieve_from_host:
-/*
- * We didn't find it. Pull an SCB and DMA down the one we want.
- * We should never get here in the non-paging case.
- */
- mov ALLZEROS call get_free_or_disc_scb;
- mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- /* Jump instead of call as we want to return anyway */
- mov ARG_1 jmp dma_scb;
-
-/*
- * Determine whether a target is using tagged or non-tagged transactions
- * by first looking for a matching transaction based on the TCL and if
- * that fails, looking up this device in the host's untagged SCB array.
- * The TCL to search for is assumed to be in SAVED_TCL. The value is
- * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
- * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
- * in an SCB instead of having to go to the host.
- */
-get_untagged_SCBID:
- cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
- mvi ARG_1, SCB_LIST_NULL;
- mov DISCONNECTED_SCBH call findSCB;
- cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host;
- or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
- test SCB_CONTROL, TAG_ENB jnz . + 2;
- mov ARG_1, SCB_TAG ret;
- mvi ARG_1, SCB_LIST_NULL ret;
-
-/*
- * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
- * and a base address of SCBID_ADDR. The byte is returned in RETURN_2.
- */
-fetch_byte:
- mov ARG_2, SINDEX;
- if ((p->features & AHC_CMD_CHAN) != 0) {
- mvi DINDEX, CCHADDR;
- mvi SCBID_ADDR call set_1byte_addr;
- mvi CCHCNT, 1;
- mvi CCSGCTL, CCSGEN|CCSGRESET;
- test CCSGCTL, CCSGDONE jz .;
- mvi CCSGCTL, CCSGRESET;
- bmov RETURN_2, CCSGRAM, 1 ret;
- } else {
- mvi DINDEX, HADDR;
- mvi SCBID_ADDR call set_1byte_addr;
- mvi HCNT[0], 1;
- clr HCNT[1];
- clr HCNT[2];
- mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
- call dma_finish;
- mov RETURN_2, DFDAT ret;
- }
-
-/*
- * Prepare the hardware to post a byte to host memory given an
- * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
- */
-post_byte_setup:
- mov ARG_2, SINDEX;
- if ((p->features & AHC_CMD_CHAN) != 0) {
- mvi DINDEX, CCHADDR;
- mvi SCBID_ADDR call set_1byte_addr;
- mvi CCHCNT, 1;
- mvi CCSCBCTL, CCSCBRESET ret;
- } else {
- mvi DINDEX, HADDR;
- mvi SCBID_ADDR call set_1byte_addr;
- mvi HCNT[0], 1;
- clr HCNT[1];
- clr HCNT[2];
- mvi DFCNTRL, FIFORESET ret;
- }
-
-post_byte:
- if ((p->features & AHC_CMD_CHAN) != 0) {
- bmov CCSCBRAM, SINDEX, 1;
- or CCSCBCTL, CCSCBEN|CCSCBRESET;
- test CCSCBCTL, CCSCBDONE jz .;
- clr CCSCBCTL ret;
- } else {
- mov DFDAT, SINDEX;
- or DFCNTRL, HDMAEN|FIFOFLUSH;
- jmp dma_finish;
- }
-
-get_SCBID_from_host:
- mov A, SAVED_TCL;
- mvi UNTAGGEDSCB_OFFSET call fetch_byte;
- mov RETURN_1, RETURN_2 ret;
-
-phase_lock:
- test SSTAT1, REQINIT jz phase_lock;
- test SSTAT1, SCSIPERR jnz phase_lock;
- and SCSISIGO, PHASE_MASK, SCSISIGI;
- and LASTPHASE, PHASE_MASK, SCSISIGI ret;
-
-if ((p->features & AHC_CMD_CHAN) == 0) {
-set_stcnt_from_hcnt:
- mov STCNT[0], HCNT[0];
- mov STCNT[1], HCNT[1];
- mov STCNT[2], HCNT[2] ret;
-
-bcopy_7:
- mov DINDIR, SINDIR;
- mov DINDIR, SINDIR;
-bcopy_5:
- mov DINDIR, SINDIR;
-bcopy_4:
- mov DINDIR, SINDIR;
-bcopy_3:
- mov DINDIR, SINDIR;
- mov DINDIR, SINDIR;
- mov DINDIR, SINDIR ret;
-}
-
-/*
- * Setup addr assuming that A is an index into
- * an array of 32byte objects, SINDEX contains
- * the base address of that array, and DINDEX
- * contains the base address of the location
- * to store the indexed address.
- */
-set_32byte_addr:
- shr ARG_2, 3, A;
- shl A, 5;
-/*
- * Setup addr assuming that A + (ARG_1 * 256) is an
- * index into an array of 1byte objects, SINDEX contains
- * the base address of that array, and DINDEX contains
- * the base address of the location to store the computed
- * address.
- */
-set_1byte_addr:
- add DINDIR, A, SINDIR;
- mov A, ARG_2;
- adc DINDIR, A, SINDIR;
- clr A;
- adc DINDIR, A, SINDIR;
- adc DINDIR, A, SINDIR ret;
-
-/*
- * Either post or fetch and SCB from host memory based on the
- * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
- */
-dma_scb:
- mov A, SINDEX;
- if ((p->features & AHC_CMD_CHAN) != 0) {
- mvi DINDEX, CCHADDR;
- mvi HSCB_ADDR call set_32byte_addr;
- mov CCSCBPTR, SCBPTR;
- mvi CCHCNT, 32;
- test DMAPARAMS, DIRECTION jz dma_scb_tohost;
- mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
- cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
- jmp dma_scb_finish;
-dma_scb_tohost:
- if ((p->features & AHC_ULTRA2) == 0) {
- mvi CCSCBCTL, CCSCBRESET;
- bmov CCSCBRAM, SCB_CONTROL, 32;
- or CCSCBCTL, CCSCBEN|CCSCBRESET;
- test CCSCBCTL, CCSCBDONE jz .;
- }
- if ((p->features & AHC_ULTRA2) != 0) {
- if ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0) {
- mvi CCSCBCTL, CCARREN|CCSCBRESET;
- cmp CCSCBCTL, ARRDONE|CCARREN jne .;
- mvi CCHCNT, 32;
- mvi CCSCBCTL, CCSCBEN|CCSCBRESET;
- cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
- } else {
- mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
- cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
- }
- }
-dma_scb_finish:
- clr CCSCBCTL;
- test CCSCBCTL, CCARREN|CCSCBEN jnz .;
- ret;
- }
- if ((p->features & AHC_CMD_CHAN) == 0) {
- mvi DINDEX, HADDR;
- mvi HSCB_ADDR call set_32byte_addr;
- mvi HCNT[0], 32;
- clr HCNT[1];
- clr HCNT[2];
- mov DFCNTRL, DMAPARAMS;
- test DMAPARAMS, DIRECTION jnz dma_scb_fromhost;
- /* Fill it with the SCB data */
-copy_scb_tofifo:
- mvi SINDEX, SCB_CONTROL;
- add A, 32, SINDEX;
-copy_scb_tofifo_loop:
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- cmp SINDEX, A jne copy_scb_tofifo_loop;
- or DFCNTRL, HDMAEN|FIFOFLUSH;
- jmp dma_finish;
-dma_scb_fromhost:
- mvi DINDEX, SCB_CONTROL;
- if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) {
- /*
- * Set the A to -24. It it hits 0, then we let
- * our code fall through to dfdat_in_8 to complete
- * the last of the copy.
- *
- * Also, things happen 8 bytes at a time in this
- * case, so we may need to drain the fifo at most
- * 3 times to keep things flowing
- */
- mvi A, -24;
-dma_scb_hang_fifo:
- /* Wait for the first bit of data to hit the fifo */
- test DFSTATUS, FIFOEMP jnz .;
-dma_scb_hang_wait:
- /* OK, now they've started to transfer into the fifo,
- * so wait for them to stop trying to transfer any
- * more data.
- */
- test DFSTATUS, MREQPEND jnz .;
- /*
- * OK, they started, then they stopped, now see if they
- * managed to complete the job before stopping. Try
- * it multiple times to give the chip a few cycles to
- * set the flag if it did complete.
- */
- test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
- test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
- test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
- /*
- * Too bad, the chip didn't complete the DMA, but there
- * aren't any more memory requests pending, so that
- * means it stopped part way through and hung. That's
- * our bug, so now we drain what data there is in the
- * fifo in order to get things going again.
- */
-dma_scb_hang_empty_fifo:
- call dfdat_in_8;
- add A, 8;
- add SINDEX, A, HCNT;
- /*
- * If there are another 8 bytes of data waiting in the
- * fifo, then the carry bit will be set as a result
- * of the above add command (unless A is non-negative,
- * in which case the carry bit won't be set).
- */
- jc dma_scb_hang_empty_fifo;
- /*
- * We've emptied the fifo now, but we wouldn't have got
- * here if the memory transfer hadn't stopped part way
- * through, so go back up to the beginning of the
- * loop and start over. When it succeeds in getting
- * all the data down, HDONE will be set and we'll
- * jump to the code just below here.
- */
- jmp dma_scb_hang_fifo;
-dma_scb_hang_dma_done:
- and DFCNTRL, ~HDMAEN;
- test DFCNTRL, HDMAEN jnz .;
- call dfdat_in_8;
- add A, 8;
- cmp A, 8 jne . - 2;
- ret;
- } else {
- call dma_finish;
- call dfdat_in_8;
- call dfdat_in_8;
- call dfdat_in_8;
- }
-dfdat_in_8:
- mov DINDIR,DFDAT;
-dfdat_in_7:
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT ret;
- }
-
-
-/*
- * Wait for DMA from host memory to data FIFO to complete, then disable
- * DMA and wait for it to acknowledge that it's off.
- */
-if ((p->features & AHC_CMD_CHAN) == 0) {
-dma_finish:
- test DFSTATUS,HDONE jz dma_finish;
- /* Turn off DMA */
- and DFCNTRL, ~HDMAEN;
- test DFCNTRL, HDMAEN jnz .;
- ret;
-}
-
-add_scb_to_free_list:
- if ((p->flags & AHC_PAGESCBS) != 0) {
- mov SCB_NEXT, FREE_SCBH;
- mov FREE_SCBH, SCBPTR;
- }
- mvi SCB_TAG, SCB_LIST_NULL ret;
-
-if ((p->flags & AHC_PAGESCBS) != 0) {
-get_free_or_disc_scb:
- cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
- cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
-return_error:
- mvi SINDEX, SCB_LIST_NULL ret;
-dequeue_disc_scb:
- mov SCBPTR, DISCONNECTED_SCBH;
-dma_up_scb:
- mvi DMAPARAMS, FIFORESET;
- mov SCB_TAG call dma_scb;
-unlink_disc_scb:
- mov DISCONNECTED_SCBH, SCB_NEXT ret;
-dequeue_free_scb:
- mov SCBPTR, FREE_SCBH;
- mov FREE_SCBH, SCB_NEXT ret;
-}
-
-add_scb_to_disc_list:
-/*
- * Link this SCB into the DISCONNECTED list. This list holds the
- * candidates for paging out an SCB if one is needed for a new command.
- * Modifying the disconnected list is a critical(pause dissabled) section.
- */
- mov SCB_NEXT, DISCONNECTED_SCBH;
- mov DISCONNECTED_SCBH, SCBPTR ret;
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx_proc.c b/drivers/scsi/aic7xxx_old/aic7xxx_proc.c
deleted file mode 100644
index b07e4f04fd00..000000000000
--- a/drivers/scsi/aic7xxx_old/aic7xxx_proc.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver proc support for Linux.
- *
- * Copyright (c) 1995, 1996 Dean W. Gehnert
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * ----------------------------------------------------------------
- * o Modified from the EATA-DMA /proc support.
- * o Additional support for device block statistics provided by
- * Matthew Jacob.
- * o Correction of overflow by Heinz Mauelshagen
- * o Adittional corrections by Doug Ledford
- *
- * Dean W. Gehnert, deang@teleport.com, 05/01/96
- *
- * $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
- *-M*************************************************************************/
-
-
-#define BLS (&aic7xxx_buffer[size])
-#define HDRB \
-" 0 - 4K 4 - 16K 16 - 64K 64 - 256K 256K - 1M 1M+"
-
-#ifdef PROC_DEBUG
-extern int vsprintf(char *, const char *, va_list);
-
-static void
-proc_debug(const char *fmt, ...)
-{
- va_list ap;
- char buf[256];
-
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
- printk(buf);
- va_end(ap);
-}
-#else /* PROC_DEBUG */
-# define proc_debug(fmt, args...)
-#endif /* PROC_DEBUG */
-
-static int aic7xxx_buffer_size = 0;
-static char *aic7xxx_buffer = NULL;
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_set_info
- *
- * Description:
- * Set parameters for the driver from the /proc filesystem.
- *-F*************************************************************************/
-static int
-aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
-{
- proc_debug("aic7xxx_set_info(): %s\n", buffer);
- return (-ENOSYS); /* Currently this is a no-op */
-}
-
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_proc_info
- *
- * Description:
- * Return information to handle /proc support for the driver.
- *-F*************************************************************************/
-int
-aic7xxx_proc_info ( struct Scsi_Host *HBAptr, char *buffer, char **start, off_t offset, int length,
- int inout)
-{
- struct aic7xxx_host *p;
- struct aic_dev_data *aic_dev;
- struct scsi_device *sdptr;
- int size = 0;
- unsigned char i;
- unsigned char tindex;
-
- for(p=first_aic7xxx; p && p->host != HBAptr; p=p->next)
- ;
-
- if (!p)
- {
- size += sprintf(buffer, "Can't find adapter for host number %d\n", HBAptr->host_no);
- if (size > length)
- {
- return (size);
- }
- else
- {
- return (length);
- }
- }
-
- if (inout == TRUE) /* Has data been written to the file? */
- {
- return (aic7xxx_set_info(buffer, length, HBAptr));
- }
-
- p = (struct aic7xxx_host *) HBAptr->hostdata;
-
- /*
- * It takes roughly 1K of space to hold all relevant card info, not
- * counting any proc stats, so we start out with a 1.5k buffer size and
- * if proc_stats is defined, then we sweep the stats structure to see
- * how many drives we will be printing out for and add 384 bytes per
- * device with active stats.
- *
- * Hmmmm...that 1.5k seems to keep growing as items get added so they
- * can be easily viewed for debugging purposes. So, we bumped that
- * 1.5k to 4k so we can quit having to bump it all the time.
- */
-
- size = 4096;
- list_for_each_entry(aic_dev, &p->aic_devs, list)
- size += 512;
- if (aic7xxx_buffer_size != size)
- {
- if (aic7xxx_buffer != NULL)
- {
- kfree(aic7xxx_buffer);
- aic7xxx_buffer_size = 0;
- }
- aic7xxx_buffer = kmalloc(size, GFP_KERNEL);
- }
- if (aic7xxx_buffer == NULL)
- {
- size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n",
- __LINE__);
- return size;
- }
- aic7xxx_buffer_size = size;
-
- size = 0;
- size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
- size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
- size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
- size += sprintf(BLS, "\n");
- size += sprintf(BLS, "Adapter Configuration:\n");
- size += sprintf(BLS, " SCSI Adapter: %s\n",
- board_names[p->board_name_index]);
- if (p->flags & AHC_TWIN)
- size += sprintf(BLS, " Twin Channel Controller ");
- else
- {
- char *channel = "";
- char *ultra = "";
- char *wide = "Narrow ";
- if (p->flags & AHC_MULTI_CHANNEL)
- {
- channel = " Channel A";
- if (p->flags & (AHC_CHNLB|AHC_CHNLC))
- channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
- }
- if (p->features & AHC_WIDE)
- wide = "Wide ";
- if (p->features & AHC_ULTRA3)
- {
- switch(p->chip & AHC_CHIPID_MASK)
- {
- case AHC_AIC7892:
- case AHC_AIC7899:
- ultra = "Ultra-160/m LVD/SE ";
- break;
- default:
- ultra = "Ultra-3 LVD/SE ";
- break;
- }
- }
- else if (p->features & AHC_ULTRA2)
- ultra = "Ultra-2 LVD/SE ";
- else if (p->features & AHC_ULTRA)
- ultra = "Ultra ";
- size += sprintf(BLS, " %s%sController%s ",
- ultra, wide, channel);
- }
- switch(p->chip & ~AHC_CHIPID_MASK)
- {
- case AHC_VL:
- size += sprintf(BLS, "at VLB slot %d\n", p->pci_device_fn);
- break;
- case AHC_EISA:
- size += sprintf(BLS, "at EISA slot %d\n", p->pci_device_fn);
- break;
- default:
- size += sprintf(BLS, "at PCI %d/%d/%d\n", p->pci_bus,
- PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn));
- break;
- }
- if( !(p->maddr) )
- {
- size += sprintf(BLS, " Programmed I/O Base: %lx\n", p->base);
- }
- else
- {
- size += sprintf(BLS, " PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
- }
- if( (p->chip & (AHC_VL | AHC_EISA)) )
- {
- size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address);
- }
- size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
- (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
- ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
- "SEEPROM not found, using leftover BIOS values.") );
- size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n",
- (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
- size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
- size += sprintf(BLS, " SCBs: Active %d, Max Active %d,\n",
- p->activescbs, p->max_activescbs);
- size += sprintf(BLS, " Allocated %d, HW %d, "
- "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
- p->scb_data->maxscbs);
- if (p->flags & AHC_EXTERNAL_SRAM)
- size += sprintf(BLS, " Using External SCB SRAM\n");
- size += sprintf(BLS, " Interrupts: %ld", p->isr_count);
- if (p->chip & AHC_EISA)
- {
- size += sprintf(BLS, " %s\n",
- (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
- }
- else
- {
- size += sprintf(BLS, "\n");
- }
- size += sprintf(BLS, " BIOS Control Word: 0x%04x\n",
- p->bios_control);
- size += sprintf(BLS, " Adapter Control Word: 0x%04x\n",
- p->adapter_control);
- size += sprintf(BLS, " Extended Translation: %sabled\n",
- (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
- size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
- if (p->features & (AHC_ULTRA | AHC_ULTRA2))
- {
- size += sprintf(BLS, " Ultra Enable Flags: 0x%04x\n", p->ultraenb);
- }
- size += sprintf(BLS, "Default Tag Queue Depth: %d\n", aic7xxx_default_queue_depth);
- size += sprintf(BLS, " Tagged Queue By Device array for aic7xxx host "
- "instance %d:\n", p->instance);
- size += sprintf(BLS, " {");
- for(i=0; i < (MAX_TARGETS - 1); i++)
- size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]);
- size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]);
-
- size += sprintf(BLS, "\n");
- size += sprintf(BLS, "Statistics:\n\n");
- list_for_each_entry(aic_dev, &p->aic_devs, list)
- {
- sdptr = aic_dev->SDptr;
- tindex = sdptr->channel << 3 | sdptr->id;
- size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
- p->host_no, sdptr->channel, sdptr->id, sdptr->lun);
- size += sprintf(BLS, " Device using %s/%s",
- (aic_dev->cur.width == MSG_EXT_WDTR_BUS_16_BIT) ?
- "Wide" : "Narrow",
- (aic_dev->cur.offset != 0) ?
- "Sync transfers at " : "Async transfers.\n" );
- if (aic_dev->cur.offset != 0)
- {
- struct aic7xxx_syncrate *sync_rate;
- unsigned char options = aic_dev->cur.options;
- int period = aic_dev->cur.period;
- int rate = (aic_dev->cur.width ==
- MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
-
- sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options);
- if (sync_rate != NULL)
- {
- size += sprintf(BLS, "%s MByte/sec, offset %d\n",
- sync_rate->rate[rate],
- aic_dev->cur.offset );
- }
- else
- {
- size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
- aic_dev->cur.offset );
- }
- }
- size += sprintf(BLS, " Transinfo settings: ");
- size += sprintf(BLS, "current(%d/%d/%d/%d), ",
- aic_dev->cur.period,
- aic_dev->cur.offset,
- aic_dev->cur.width,
- aic_dev->cur.options);
- size += sprintf(BLS, "goal(%d/%d/%d/%d), ",
- aic_dev->goal.period,
- aic_dev->goal.offset,
- aic_dev->goal.width,
- aic_dev->goal.options);
- size += sprintf(BLS, "user(%d/%d/%d/%d)\n",
- p->user[tindex].period,
- p->user[tindex].offset,
- p->user[tindex].width,
- p->user[tindex].options);
- if(sdptr->simple_tags)
- {
- size += sprintf(BLS, " Tagged Command Queueing Enabled, Ordered Tags %s, Depth %d/%d\n", sdptr->ordered_tags ? "Enabled" : "Disabled", sdptr->queue_depth, aic_dev->max_q_depth);
- }
- if(aic_dev->barrier_total)
- size += sprintf(BLS, " Total transfers %ld:\n (%ld/%ld/%ld/%ld reads/writes/REQ_BARRIER/Ordered Tags)\n",
- aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total,
- aic_dev->barrier_total, aic_dev->ordered_total);
- else
- size += sprintf(BLS, " Total transfers %ld:\n (%ld/%ld reads/writes)\n",
- aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total);
- size += sprintf(BLS, "%s\n", HDRB);
- size += sprintf(BLS, " Reads:");
- for (i = 0; i < ARRAY_SIZE(aic_dev->r_bins); i++)
- {
- size += sprintf(BLS, " %10ld", aic_dev->r_bins[i]);
- }
- size += sprintf(BLS, "\n");
- size += sprintf(BLS, " Writes:");
- for (i = 0; i < ARRAY_SIZE(aic_dev->w_bins); i++)
- {
- size += sprintf(BLS, " %10ld", aic_dev->w_bins[i]);
- }
- size += sprintf(BLS, "\n");
- size += sprintf(BLS, "\n\n");
- }
- if (size >= aic7xxx_buffer_size)
- {
- printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");
- }
-
- if (offset > size - 1)
- {
- kfree(aic7xxx_buffer);
- aic7xxx_buffer = NULL;
- aic7xxx_buffer_size = length = 0;
- *start = NULL;
- }
- else
- {
- *start = buffer;
- length = min_t(int, length, size - offset);
- memcpy(buffer, &aic7xxx_buffer[offset], length);
- }
-
- return (length);
-}
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 2
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -2
- * c-argdecl-indent: 2
- * c-label-offset: -2
- * c-continued-statement-offset: 2
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx_reg.h b/drivers/scsi/aic7xxx_old/aic7xxx_reg.h
deleted file mode 100644
index 27f2334abc71..000000000000
--- a/drivers/scsi/aic7xxx_old/aic7xxx_reg.h
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * DO NOT EDIT - This file is automatically generated.
- */
-
-#define SCSISEQ 0x00
-#define TEMODE 0x80
-#define ENSELO 0x40
-#define ENSELI 0x20
-#define ENRSELI 0x10
-#define ENAUTOATNO 0x08
-#define ENAUTOATNI 0x04
-#define ENAUTOATNP 0x02
-#define SCSIRSTO 0x01
-
-#define SXFRCTL0 0x01
-#define DFON 0x80
-#define DFPEXP 0x40
-#define FAST20 0x20
-#define CLRSTCNT 0x10
-#define SPIOEN 0x08
-#define SCAMEN 0x04
-#define CLRCHN 0x02
-
-#define SXFRCTL1 0x02
-#define BITBUCKET 0x80
-#define SWRAPEN 0x40
-#define ENSPCHK 0x20
-#define STIMESEL 0x18
-#define ENSTIMER 0x04
-#define ACTNEGEN 0x02
-#define STPWEN 0x01
-
-#define SCSISIGO 0x03
-#define CDO 0x80
-#define IOO 0x40
-#define MSGO 0x20
-#define ATNO 0x10
-#define SELO 0x08
-#define BSYO 0x04
-#define REQO 0x02
-#define ACKO 0x01
-
-#define SCSISIGI 0x03
-#define ATNI 0x10
-#define SELI 0x08
-#define BSYI 0x04
-#define REQI 0x02
-#define ACKI 0x01
-
-#define SCSIRATE 0x04
-#define WIDEXFER 0x80
-#define SXFR_ULTRA2 0x7f
-#define SXFR 0x70
-#define SOFS 0x0f
-
-#define SCSIID 0x05
-#define SCSIOFFSET 0x05
-#define SOFS_ULTRA2 0x7f
-
-#define SCSIDATL 0x06
-
-#define SCSIDATH 0x07
-
-#define STCNT 0x08
-
-#define OPTIONMODE 0x08
-#define AUTORATEEN 0x80
-#define AUTOACKEN 0x40
-#define ATNMGMNTEN 0x20
-#define BUSFREEREV 0x10
-#define EXPPHASEDIS 0x08
-#define SCSIDATL_IMGEN 0x04
-#define AUTO_MSGOUT_DE 0x02
-#define DIS_MSGIN_DUALEDGE 0x01
-
-#define CLRSINT0 0x0b
-#define CLRSELDO 0x40
-#define CLRSELDI 0x20
-#define CLRSELINGO 0x10
-#define CLRSWRAP 0x08
-#define CLRSPIORDY 0x02
-
-#define SSTAT0 0x0b
-#define TARGET 0x80
-#define SELDO 0x40
-#define SELDI 0x20
-#define SELINGO 0x10
-#define IOERR 0x08
-#define SWRAP 0x08
-#define SDONE 0x04
-#define SPIORDY 0x02
-#define DMADONE 0x01
-
-#define CLRSINT1 0x0c
-#define CLRSELTIMEO 0x80
-#define CLRATNO 0x40
-#define CLRSCSIRSTI 0x20
-#define CLRBUSFREE 0x08
-#define CLRSCSIPERR 0x04
-#define CLRPHASECHG 0x02
-#define CLRREQINIT 0x01
-
-#define SSTAT1 0x0c
-#define SELTO 0x80
-#define ATNTARG 0x40
-#define SCSIRSTI 0x20
-#define PHASEMIS 0x10
-#define BUSFREE 0x08
-#define SCSIPERR 0x04
-#define PHASECHG 0x02
-#define REQINIT 0x01
-
-#define SSTAT2 0x0d
-#define OVERRUN 0x80
-#define SHVALID 0x40
-#define WIDE_RES 0x20
-#define SFCNT 0x1f
-#define EXP_ACTIVE 0x10
-#define CRCVALERR 0x08
-#define CRCENDERR 0x04
-#define CRCREQERR 0x02
-#define DUAL_EDGE_ERROR 0x01
-
-#define SSTAT3 0x0e
-#define SCSICNT 0xf0
-#define OFFCNT 0x0f
-
-#define SCSIID_ULTRA2 0x0f
-#define OID 0x0f
-
-#define SIMODE0 0x10
-#define ENSELDO 0x40
-#define ENSELDI 0x20
-#define ENSELINGO 0x10
-#define ENIOERR 0x08
-#define ENSWRAP 0x08
-#define ENSDONE 0x04
-#define ENSPIORDY 0x02
-#define ENDMADONE 0x01
-
-#define SIMODE1 0x11
-#define ENSELTIMO 0x80
-#define ENATNTARG 0x40
-#define ENSCSIRST 0x20
-#define ENPHASEMIS 0x10
-#define ENBUSFREE 0x08
-#define ENSCSIPERR 0x04
-#define ENPHASECHG 0x02
-#define ENREQINIT 0x01
-
-#define SCSIBUSL 0x12
-
-#define SCSIBUSH 0x13
-
-#define SHADDR 0x14
-
-#define SELTIMER 0x18
-#define STAGE6 0x20
-#define STAGE5 0x10
-#define STAGE4 0x08
-#define STAGE3 0x04
-#define STAGE2 0x02
-#define STAGE1 0x01
-
-#define SELID 0x19
-#define SELID_MASK 0xf0
-#define ONEBIT 0x08
-
-#define SPIOCAP 0x1b
-#define SOFT1 0x80
-#define SOFT0 0x40
-#define SOFTCMDEN 0x20
-#define HAS_BRDCTL 0x10
-#define SEEPROM 0x08
-#define EEPROM 0x04
-#define ROM 0x02
-#define SSPIOCPS 0x01
-
-#define BRDCTL 0x1d
-#define BRDDAT7 0x80
-#define BRDDAT6 0x40
-#define BRDDAT5 0x20
-#define BRDDAT4 0x10
-#define BRDSTB 0x10
-#define BRDCS 0x08
-#define BRDDAT3 0x08
-#define BRDDAT2 0x04
-#define BRDRW 0x04
-#define BRDRW_ULTRA2 0x02
-#define BRDCTL1 0x02
-#define BRDSTB_ULTRA2 0x01
-#define BRDCTL0 0x01
-
-#define SEECTL 0x1e
-#define EXTARBACK 0x80
-#define EXTARBREQ 0x40
-#define SEEMS 0x20
-#define SEERDY 0x10
-#define SEECS 0x08
-#define SEECK 0x04
-#define SEEDO 0x02
-#define SEEDI 0x01
-
-#define SBLKCTL 0x1f
-#define DIAGLEDEN 0x80
-#define DIAGLEDON 0x40
-#define AUTOFLUSHDIS 0x20
-#define ENAB40 0x08
-#define ENAB20 0x04
-#define SELWIDE 0x02
-#define XCVR 0x01
-
-#define SRAM_BASE 0x20
-
-#define TARG_SCSIRATE 0x20
-
-#define ULTRA_ENB 0x30
-
-#define DISC_DSB 0x32
-
-#define MSG_OUT 0x34
-
-#define DMAPARAMS 0x35
-#define PRELOADEN 0x80
-#define WIDEODD 0x40
-#define SCSIEN 0x20
-#define SDMAENACK 0x10
-#define SDMAEN 0x10
-#define HDMAEN 0x08
-#define HDMAENACK 0x08
-#define DIRECTION 0x04
-#define FIFOFLUSH 0x02
-#define FIFORESET 0x01
-
-#define SEQ_FLAGS 0x36
-#define IDENTIFY_SEEN 0x80
-#define SCBPTR_VALID 0x20
-#define DPHASE 0x10
-#define AMTARGET 0x08
-#define WIDE_BUS 0x02
-#define TWIN_BUS 0x01
-
-#define SAVED_TCL 0x37
-
-#define SG_COUNT 0x38
-
-#define SG_NEXT 0x39
-
-#define LASTPHASE 0x3d
-#define P_MESGIN 0xe0
-#define PHASE_MASK 0xe0
-#define P_STATUS 0xc0
-#define P_MESGOUT 0xa0
-#define P_COMMAND 0x80
-#define CDI 0x80
-#define IOI 0x40
-#define P_DATAIN 0x40
-#define MSGI 0x20
-#define P_BUSFREE 0x01
-#define P_DATAOUT 0x00
-
-#define WAITING_SCBH 0x3e
-
-#define DISCONNECTED_SCBH 0x3f
-
-#define FREE_SCBH 0x40
-
-#define HSCB_ADDR 0x41
-
-#define SCBID_ADDR 0x45
-
-#define TMODE_CMDADDR 0x49
-
-#define KERNEL_QINPOS 0x4d
-
-#define QINPOS 0x4e
-
-#define QOUTPOS 0x4f
-
-#define TMODE_CMDADDR_NEXT 0x50
-
-#define ARG_1 0x51
-#define RETURN_1 0x51
-#define SEND_MSG 0x80
-#define SEND_SENSE 0x40
-#define SEND_REJ 0x20
-#define MSGOUT_PHASEMIS 0x10
-
-#define ARG_2 0x52
-#define RETURN_2 0x52
-
-#define LAST_MSG 0x53
-
-#define PREFETCH_CNT 0x54
-
-#define SCSICONF 0x5a
-#define TERM_ENB 0x80
-#define RESET_SCSI 0x40
-#define HWSCSIID 0x0f
-#define HSCSIID 0x07
-
-#define HOSTCONF 0x5d
-
-#define HA_274_BIOSCTRL 0x5f
-#define BIOSMODE 0x30
-#define BIOSDISABLED 0x30
-#define CHANNEL_B_PRIMARY 0x08
-
-#define SEQCTL 0x60
-#define PERRORDIS 0x80
-#define PAUSEDIS 0x40
-#define FAILDIS 0x20
-#define FASTMODE 0x10
-#define BRKADRINTEN 0x08
-#define STEP 0x04
-#define SEQRESET 0x02
-#define LOADRAM 0x01
-
-#define SEQRAM 0x61
-
-#define SEQADDR0 0x62
-
-#define SEQADDR1 0x63
-#define SEQADDR1_MASK 0x01
-
-#define ACCUM 0x64
-
-#define SINDEX 0x65
-
-#define DINDEX 0x66
-
-#define ALLONES 0x69
-
-#define ALLZEROS 0x6a
-
-#define NONE 0x6a
-
-#define FLAGS 0x6b
-#define ZERO 0x02
-#define CARRY 0x01
-
-#define SINDIR 0x6c
-
-#define DINDIR 0x6d
-
-#define FUNCTION1 0x6e
-
-#define STACK 0x6f
-
-#define TARG_OFFSET 0x70
-
-#define BCTL 0x84
-#define ACE 0x08
-#define ENABLE 0x01
-
-#define DSCOMMAND0 0x84
-#define INTSCBRAMSEL 0x08
-#define RAMPS 0x04
-#define USCBSIZE32 0x02
-#define CIOPARCKEN 0x01
-
-#define DSCOMMAND 0x84
-#define CACHETHEN 0x80
-#define DPARCKEN 0x40
-#define MPARCKEN 0x20
-#define EXTREQLCK 0x10
-
-#define BUSTIME 0x85
-#define BOFF 0xf0
-#define BON 0x0f
-
-#define BUSSPD 0x86
-#define DFTHRSH 0xc0
-#define STBOFF 0x38
-#define STBON 0x07
-
-#define DSPCISTATUS 0x86
-#define DFTHRSH_100 0xc0
-
-#define HCNTRL 0x87
-#define POWRDN 0x40
-#define SWINT 0x10
-#define IRQMS 0x08
-#define PAUSE 0x04
-#define INTEN 0x02
-#define CHIPRST 0x01
-#define CHIPRSTACK 0x01
-
-#define HADDR 0x88
-
-#define HCNT 0x8c
-
-#define SCBPTR 0x90
-
-#define INTSTAT 0x91
-#define SEQINT_MASK 0xf1
-#define DATA_OVERRUN 0xe1
-#define MSGIN_PHASEMIS 0xd1
-#define TRACEPOINT2 0xc1
-#define SEQ_SG_FIXUP 0xb1
-#define AWAITING_MSG 0xa1
-#define RESIDUAL 0x81
-#define BAD_STATUS 0x71
-#define REJECT_MSG 0x61
-#define WIDE_RESIDUE 0x51
-#define EXTENDED_MSG 0x41
-#define NO_MATCH 0x31
-#define NO_IDENT 0x21
-#define SEND_REJECT 0x11
-#define INT_PEND 0x0f
-#define BRKADRINT 0x08
-#define SCSIINT 0x04
-#define CMDCMPLT 0x02
-#define BAD_PHASE 0x01
-#define SEQINT 0x01
-
-#define CLRINT 0x92
-#define CLRPARERR 0x10
-#define CLRBRKADRINT 0x08
-#define CLRSCSIINT 0x04
-#define CLRCMDINT 0x02
-#define CLRSEQINT 0x01
-
-#define ERROR 0x92
-#define CIOPARERR 0x80
-#define PCIERRSTAT 0x40
-#define MPARERR 0x20
-#define DPARERR 0x10
-#define SQPARERR 0x08
-#define ILLOPCODE 0x04
-#define DSCTMOUT 0x02
-#define ILLSADDR 0x02
-#define ILLHADDR 0x01
-
-#define DFCNTRL 0x93
-
-#define DFSTATUS 0x94
-#define PRELOAD_AVAIL 0x80
-#define DWORDEMP 0x20
-#define MREQPEND 0x10
-#define HDONE 0x08
-#define DFTHRESH 0x04
-#define FIFOFULL 0x02
-#define FIFOEMP 0x01
-
-#define DFDAT 0x99
-
-#define SCBCNT 0x9a
-#define SCBAUTO 0x80
-#define SCBCNT_MASK 0x1f
-
-#define QINFIFO 0x9b
-
-#define QINCNT 0x9c
-
-#define SCSIDATL_IMG 0x9c
-
-#define QOUTFIFO 0x9d
-
-#define CRCCONTROL1 0x9d
-#define CRCONSEEN 0x80
-#define CRCVALCHKEN 0x40
-#define CRCENDCHKEN 0x20
-#define CRCREQCHKEN 0x10
-#define TARGCRCENDEN 0x08
-#define TARGCRCCNTEN 0x04
-
-#define SCSIPHASE 0x9e
-#define SP_STATUS 0x20
-#define SP_COMMAND 0x10
-#define SP_MSG_IN 0x08
-#define SP_MSG_OUT 0x04
-#define SP_DATA_IN 0x02
-#define SP_DATA_OUT 0x01
-
-#define QOUTCNT 0x9e
-
-#define SFUNCT 0x9f
-#define ALT_MODE 0x80
-
-#define SCB_CONTROL 0xa0
-#define MK_MESSAGE 0x80
-#define DISCENB 0x40
-#define TAG_ENB 0x20
-#define DISCONNECTED 0x04
-#define SCB_TAG_TYPE 0x03
-
-#define SCB_BASE 0xa0
-
-#define SCB_TCL 0xa1
-#define TID 0xf0
-#define SELBUSB 0x08
-#define LID 0x07
-
-#define SCB_TARGET_STATUS 0xa2
-
-#define SCB_SGCOUNT 0xa3
-
-#define SCB_SGPTR 0xa4
-
-#define SCB_RESID_SGCNT 0xa8
-
-#define SCB_RESID_DCNT 0xa9
-
-#define SCB_DATAPTR 0xac
-
-#define SCB_DATACNT 0xb0
-
-#define SCB_CMDPTR 0xb4
-
-#define SCB_CMDLEN 0xb8
-
-#define SCB_TAG 0xb9
-
-#define SCB_NEXT 0xba
-
-#define SCB_PREV 0xbb
-
-#define SCB_BUSYTARGETS 0xbc
-
-#define SEECTL_2840 0xc0
-#define CS_2840 0x04
-#define CK_2840 0x02
-#define DO_2840 0x01
-
-#define STATUS_2840 0xc1
-#define EEPROM_TF 0x80
-#define BIOS_SEL 0x60
-#define ADSEL 0x1e
-#define DI_2840 0x01
-
-#define CCHADDR 0xe0
-
-#define CCHCNT 0xe8
-
-#define CCSGRAM 0xe9
-
-#define CCSGADDR 0xea
-
-#define CCSGCTL 0xeb
-#define CCSGDONE 0x80
-#define CCSGEN 0x08
-#define FLAG 0x02
-#define CCSGRESET 0x01
-
-#define CCSCBRAM 0xec
-
-#define CCSCBADDR 0xed
-
-#define CCSCBCTL 0xee
-#define CCSCBDONE 0x80
-#define ARRDONE 0x40
-#define CCARREN 0x10
-#define CCSCBEN 0x08
-#define CCSCBDIR 0x04
-#define CCSCBRESET 0x01
-
-#define CCSCBCNT 0xef
-
-#define CCSCBPTR 0xf1
-
-#define HNSCB_QOFF 0xf4
-
-#define HESCB_QOFF 0xf5
-
-#define SNSCB_QOFF 0xf6
-
-#define SESCB_QOFF 0xf7
-
-#define SDSCB_QOFF 0xf8
-
-#define QOFF_CTLSTA 0xfa
-#define ESTABLISH_SCB_AVAIL 0x80
-#define SCB_AVAIL 0x40
-#define SNSCB_ROLLOVER 0x20
-#define SDSCB_ROLLOVER 0x10
-#define SESCB_ROLLOVER 0x08
-#define SCB_QSIZE 0x07
-#define SCB_QSIZE_256 0x06
-
-#define DFF_THRSH 0xfb
-#define WR_DFTHRSH 0x70
-#define WR_DFTHRSH_MAX 0x70
-#define WR_DFTHRSH_90 0x60
-#define WR_DFTHRSH_85 0x50
-#define WR_DFTHRSH_75 0x40
-#define WR_DFTHRSH_63 0x30
-#define WR_DFTHRSH_50 0x20
-#define WR_DFTHRSH_25 0x10
-#define RD_DFTHRSH_MAX 0x07
-#define RD_DFTHRSH 0x07
-#define RD_DFTHRSH_90 0x06
-#define RD_DFTHRSH_85 0x05
-#define RD_DFTHRSH_75 0x04
-#define RD_DFTHRSH_63 0x03
-#define RD_DFTHRSH_50 0x02
-#define RD_DFTHRSH_25 0x01
-#define WR_DFTHRSH_MIN 0x00
-#define RD_DFTHRSH_MIN 0x00
-
-#define SG_CACHEPTR 0xfc
-#define SG_USER_DATA 0xfc
-#define LAST_SEG 0x02
-#define LAST_SEG_DONE 0x01
-
-
-#define CMD_GROUP2_BYTE_DELTA 0xfa
-#define MAX_OFFSET_8BIT 0x0f
-#define BUS_16_BIT 0x01
-#define QINFIFO_OFFSET 0x02
-#define CMD_GROUP5_BYTE_DELTA 0x0b
-#define CMD_GROUP_CODE_SHIFT 0x05
-#define MAX_OFFSET_ULTRA2 0x7f
-#define MAX_OFFSET_16BIT 0x08
-#define BUS_8_BIT 0x00
-#define QOUTFIFO_OFFSET 0x01
-#define UNTAGGEDSCB_OFFSET 0x00
-#define CCSGRAM_MAXSEGS 0x10
-#define SCB_LIST_NULL 0xff
-#define SG_SIZEOF 0x08
-#define CMD_GROUP4_BYTE_DELTA 0x04
-#define CMD_GROUP0_BYTE_DELTA 0xfc
-#define HOST_MSG 0xff
-#define BUS_32_BIT 0x02
-#define CCSGADDR_MAX 0x80
-
-
-/* Downloaded Constant Definitions */
-#define TMODE_NUMCMDS 0x00
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx_seq.c b/drivers/scsi/aic7xxx_old/aic7xxx_seq.c
deleted file mode 100644
index e1bc140e9735..000000000000
--- a/drivers/scsi/aic7xxx_old/aic7xxx_seq.c
+++ /dev/null
@@ -1,817 +0,0 @@
-/*
- * DO NOT EDIT - This file is automatically generated.
- */
-static unsigned char seqprog[] = {
- 0xff, 0x6a, 0x06, 0x08,
- 0x7f, 0x02, 0x04, 0x08,
- 0x12, 0x6a, 0x00, 0x00,
- 0xff, 0x6a, 0xd6, 0x09,
- 0xff, 0x6a, 0xdc, 0x09,
- 0x00, 0x65, 0xca, 0x58,
- 0xf7, 0x01, 0x02, 0x08,
- 0xff, 0x4e, 0xc8, 0x08,
- 0xbf, 0x60, 0xc0, 0x08,
- 0x60, 0x0b, 0x86, 0x68,
- 0x40, 0x00, 0x0c, 0x68,
- 0x08, 0x1f, 0x3e, 0x10,
- 0x60, 0x0b, 0x86, 0x68,
- 0x40, 0x00, 0x0c, 0x68,
- 0x08, 0x1f, 0x3e, 0x10,
- 0xff, 0x3e, 0x48, 0x60,
- 0x40, 0xfa, 0x10, 0x78,
- 0xff, 0xf6, 0xd4, 0x08,
- 0x01, 0x4e, 0x9c, 0x18,
- 0x40, 0x60, 0xc0, 0x00,
- 0x00, 0x4d, 0x10, 0x70,
- 0x01, 0x4e, 0x9c, 0x18,
- 0xbf, 0x60, 0xc0, 0x08,
- 0x00, 0x6a, 0x86, 0x5c,
- 0xff, 0x4e, 0xc8, 0x18,
- 0x02, 0x6a, 0x70, 0x5b,
- 0xff, 0x52, 0x20, 0x09,
- 0x0d, 0x6a, 0x6a, 0x00,
- 0x00, 0x52, 0xe6, 0x5b,
- 0x03, 0xb0, 0x52, 0x31,
- 0xff, 0xb0, 0x52, 0x09,
- 0xff, 0xb1, 0x54, 0x09,
- 0xff, 0xb2, 0x56, 0x09,
- 0xff, 0xa3, 0x50, 0x09,
- 0xff, 0x3e, 0x74, 0x09,
- 0xff, 0x90, 0x7c, 0x08,
- 0xff, 0x3e, 0x20, 0x09,
- 0x00, 0x65, 0x4e, 0x58,
- 0x00, 0x65, 0x0c, 0x40,
- 0xf7, 0x1f, 0xca, 0x08,
- 0x08, 0xa1, 0xc8, 0x08,
- 0x00, 0x65, 0xca, 0x00,
- 0xff, 0x65, 0x3e, 0x08,
- 0xf0, 0xa1, 0xc8, 0x08,
- 0x0f, 0x0f, 0x1e, 0x08,
- 0x00, 0x0f, 0x1e, 0x00,
- 0xf0, 0xa1, 0xc8, 0x08,
- 0x0f, 0x05, 0x0a, 0x08,
- 0x00, 0x05, 0x0a, 0x00,
- 0xff, 0x6a, 0x0c, 0x08,
- 0x5a, 0x6a, 0x00, 0x04,
- 0x12, 0x65, 0x02, 0x00,
- 0x31, 0x6a, 0xca, 0x00,
- 0x80, 0x37, 0x6e, 0x68,
- 0xff, 0x65, 0xca, 0x18,
- 0xff, 0x37, 0xdc, 0x08,
- 0xff, 0x6e, 0xc8, 0x08,
- 0x00, 0x6c, 0x76, 0x78,
- 0x20, 0x01, 0x02, 0x00,
- 0x4c, 0x37, 0xc8, 0x28,
- 0x08, 0x1f, 0x7e, 0x78,
- 0x08, 0x37, 0x6e, 0x00,
- 0x08, 0x64, 0xc8, 0x00,
- 0x70, 0x64, 0xca, 0x18,
- 0xff, 0x6c, 0x0a, 0x08,
- 0x20, 0x64, 0xca, 0x18,
- 0xff, 0x6c, 0x08, 0x0c,
- 0x40, 0x0b, 0x96, 0x68,
- 0x20, 0x6a, 0x16, 0x00,
- 0xf0, 0x19, 0x6e, 0x08,
- 0x08, 0x6a, 0x18, 0x00,
- 0x08, 0x11, 0x22, 0x00,
- 0x08, 0x6a, 0x66, 0x58,
- 0x08, 0x6a, 0x68, 0x00,
- 0x00, 0x65, 0xaa, 0x40,
- 0x12, 0x6a, 0x00, 0x00,
- 0x40, 0x6a, 0x16, 0x00,
- 0xff, 0x3e, 0x20, 0x09,
- 0xff, 0xba, 0x7c, 0x08,
- 0xff, 0xa1, 0x6e, 0x08,
- 0x08, 0x6a, 0x18, 0x00,
- 0x08, 0x11, 0x22, 0x00,
- 0x08, 0x6a, 0x66, 0x58,
- 0x80, 0x6a, 0x68, 0x00,
- 0x80, 0x36, 0x6c, 0x00,
- 0x00, 0x65, 0xba, 0x5b,
- 0xff, 0x3d, 0xc8, 0x08,
- 0xbf, 0x64, 0xe2, 0x78,
- 0x80, 0x64, 0xc8, 0x71,
- 0xa0, 0x64, 0xf8, 0x71,
- 0xc0, 0x64, 0xf0, 0x71,
- 0xe0, 0x64, 0x38, 0x72,
- 0x01, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0xaa, 0x40,
- 0xf7, 0x11, 0x22, 0x08,
- 0x00, 0x65, 0xca, 0x58,
- 0xff, 0x06, 0xd4, 0x08,
- 0xf7, 0x01, 0x02, 0x08,
- 0x09, 0x0c, 0xc4, 0x78,
- 0x08, 0x0c, 0x0c, 0x68,
- 0x01, 0x6a, 0x22, 0x01,
- 0xff, 0x6a, 0x26, 0x09,
- 0x02, 0x6a, 0x08, 0x30,
- 0xff, 0x6a, 0x08, 0x08,
- 0xdf, 0x01, 0x02, 0x08,
- 0x01, 0x6a, 0x7a, 0x00,
- 0xff, 0x6a, 0x6c, 0x0c,
- 0x04, 0x14, 0x10, 0x31,
- 0x03, 0xa9, 0x18, 0x31,
- 0x03, 0xa9, 0x10, 0x30,
- 0x08, 0x6a, 0xcc, 0x00,
- 0xa9, 0x6a, 0xd0, 0x5b,
- 0x00, 0x65, 0x02, 0x41,
- 0xa8, 0x6a, 0x6a, 0x00,
- 0x79, 0x6a, 0x6a, 0x00,
- 0x40, 0x3d, 0xea, 0x68,
- 0x04, 0x35, 0x6a, 0x00,
- 0x00, 0x65, 0x2a, 0x5b,
- 0x80, 0x6a, 0xd4, 0x01,
- 0x10, 0x36, 0xd6, 0x68,
- 0x10, 0x36, 0x6c, 0x00,
- 0x07, 0xac, 0x10, 0x31,
- 0x05, 0xa3, 0x70, 0x30,
- 0x03, 0x8c, 0x10, 0x30,
- 0x88, 0x6a, 0xcc, 0x00,
- 0xac, 0x6a, 0xc8, 0x5b,
- 0x00, 0x65, 0xc2, 0x5b,
- 0x38, 0x6a, 0xcc, 0x00,
- 0xa3, 0x6a, 0xcc, 0x5b,
- 0xff, 0x38, 0x12, 0x69,
- 0x80, 0x02, 0x04, 0x00,
- 0xe7, 0x35, 0x6a, 0x08,
- 0x03, 0x69, 0x18, 0x31,
- 0x03, 0x69, 0x10, 0x30,
- 0xff, 0x6a, 0x10, 0x00,
- 0xff, 0x6a, 0x12, 0x00,
- 0xff, 0x6a, 0x14, 0x00,
- 0x22, 0x38, 0xc8, 0x28,
- 0x01, 0x38, 0x1c, 0x61,
- 0x02, 0x64, 0xc8, 0x00,
- 0x01, 0x38, 0x1c, 0x61,
- 0xbf, 0x35, 0x6a, 0x08,
- 0xff, 0x64, 0xf8, 0x09,
- 0xff, 0x35, 0x26, 0x09,
- 0x80, 0x02, 0xa4, 0x69,
- 0x10, 0x0c, 0x7a, 0x69,
- 0x80, 0x94, 0x22, 0x79,
- 0x00, 0x35, 0x0a, 0x5b,
- 0x80, 0x02, 0xa4, 0x69,
- 0xff, 0x65, 0x94, 0x79,
- 0x01, 0x38, 0x70, 0x71,
- 0xff, 0x38, 0x70, 0x18,
- 0xff, 0x38, 0x94, 0x79,
- 0x80, 0xea, 0x4a, 0x61,
- 0xef, 0x38, 0xc8, 0x18,
- 0x80, 0x6a, 0xc8, 0x00,
- 0x00, 0x65, 0x3c, 0x49,
- 0x33, 0x38, 0xc8, 0x28,
- 0xff, 0x64, 0xd0, 0x09,
- 0x04, 0x39, 0xc0, 0x31,
- 0x09, 0x6a, 0xd6, 0x01,
- 0x80, 0xeb, 0x42, 0x79,
- 0xf7, 0xeb, 0xd6, 0x09,
- 0x08, 0xeb, 0x46, 0x69,
- 0x01, 0x6a, 0xd6, 0x01,
- 0x08, 0xe9, 0x10, 0x31,
- 0x03, 0x8c, 0x10, 0x30,
- 0xff, 0x38, 0x70, 0x18,
- 0x88, 0x6a, 0xcc, 0x00,
- 0x39, 0x6a, 0xce, 0x5b,
- 0x08, 0x6a, 0x18, 0x01,
- 0xff, 0x6a, 0x1a, 0x09,
- 0xff, 0x6a, 0x1c, 0x09,
- 0x0d, 0x93, 0x26, 0x01,
- 0x00, 0x65, 0x78, 0x5c,
- 0x88, 0x6a, 0xcc, 0x00,
- 0x00, 0x65, 0x6a, 0x5c,
- 0x00, 0x65, 0xc2, 0x5b,
- 0xff, 0x6a, 0xc8, 0x08,
- 0x08, 0x39, 0x72, 0x18,
- 0x00, 0x3a, 0x74, 0x20,
- 0x00, 0x65, 0x02, 0x41,
- 0x01, 0x0c, 0x6c, 0x79,
- 0x10, 0x0c, 0x02, 0x79,
- 0x10, 0x0c, 0x7a, 0x69,
- 0x01, 0xfc, 0x70, 0x79,
- 0xff, 0x6a, 0x70, 0x08,
- 0x01, 0x0c, 0x76, 0x79,
- 0x10, 0x0c, 0x02, 0x79,
- 0x00, 0x65, 0xae, 0x59,
- 0x01, 0xfc, 0x94, 0x69,
- 0x40, 0x0d, 0x84, 0x69,
- 0xb1, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0x94, 0x41,
- 0x2e, 0xfc, 0xa2, 0x28,
- 0x3f, 0x38, 0xc8, 0x08,
- 0x00, 0x51, 0x94, 0x71,
- 0xff, 0x6a, 0xc8, 0x08,
- 0xf8, 0x39, 0x72, 0x18,
- 0xff, 0x3a, 0x74, 0x20,
- 0x01, 0x38, 0x70, 0x18,
- 0x00, 0x65, 0x86, 0x41,
- 0x03, 0x08, 0x52, 0x31,
- 0xff, 0x38, 0x50, 0x09,
- 0x12, 0x01, 0x02, 0x00,
- 0xff, 0x08, 0x52, 0x09,
- 0xff, 0x09, 0x54, 0x09,
- 0xff, 0x0a, 0x56, 0x09,
- 0xff, 0x38, 0x50, 0x09,
- 0x00, 0x65, 0xaa, 0x40,
- 0x10, 0x0c, 0xa4, 0x79,
- 0x00, 0x65, 0xae, 0x59,
- 0x7f, 0x02, 0x04, 0x08,
- 0xe1, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0xaa, 0x40,
- 0x04, 0x93, 0xc2, 0x69,
- 0xdf, 0x93, 0x26, 0x09,
- 0x20, 0x93, 0xb2, 0x69,
- 0x02, 0x93, 0x26, 0x01,
- 0x01, 0x94, 0xb6, 0x79,
- 0x01, 0x94, 0xb6, 0x79,
- 0x01, 0x94, 0xb6, 0x79,
- 0x01, 0x94, 0xb6, 0x79,
- 0x01, 0x94, 0xb6, 0x79,
- 0x10, 0x94, 0xc0, 0x69,
- 0xd7, 0x93, 0x26, 0x09,
- 0x28, 0x93, 0xc4, 0x69,
- 0xff, 0x6a, 0xd4, 0x0c,
- 0x00, 0x65, 0x2a, 0x5b,
- 0x05, 0xb4, 0x10, 0x31,
- 0x02, 0x6a, 0x1a, 0x31,
- 0x03, 0x8c, 0x10, 0x30,
- 0x88, 0x6a, 0xcc, 0x00,
- 0xb4, 0x6a, 0xcc, 0x5b,
- 0xff, 0x6a, 0x1a, 0x09,
- 0xff, 0x6a, 0x1c, 0x09,
- 0x00, 0x65, 0xc2, 0x5b,
- 0x3d, 0x6a, 0x0a, 0x5b,
- 0xac, 0x6a, 0x26, 0x01,
- 0x04, 0x0b, 0xde, 0x69,
- 0x04, 0x0b, 0xe4, 0x69,
- 0x10, 0x0c, 0xe0, 0x79,
- 0x02, 0x03, 0xe8, 0x79,
- 0x11, 0x0c, 0xe4, 0x79,
- 0xd7, 0x93, 0x26, 0x09,
- 0x28, 0x93, 0xea, 0x69,
- 0x12, 0x01, 0x02, 0x00,
- 0x00, 0x65, 0xaa, 0x40,
- 0x00, 0x65, 0x2a, 0x5b,
- 0xff, 0x06, 0x44, 0x09,
- 0x00, 0x65, 0xaa, 0x40,
- 0x10, 0x3d, 0x06, 0x00,
- 0xff, 0x34, 0xca, 0x08,
- 0x80, 0x65, 0x1c, 0x62,
- 0x0f, 0xa1, 0xca, 0x08,
- 0x07, 0xa1, 0xca, 0x08,
- 0x40, 0xa0, 0xc8, 0x08,
- 0x00, 0x65, 0xca, 0x00,
- 0x80, 0x65, 0xca, 0x00,
- 0x80, 0xa0, 0x0c, 0x7a,
- 0xff, 0x65, 0x0c, 0x08,
- 0x00, 0x65, 0x1e, 0x42,
- 0x20, 0xa0, 0x24, 0x7a,
- 0xff, 0x65, 0x0c, 0x08,
- 0x00, 0x65, 0xba, 0x5b,
- 0xa0, 0x3d, 0x2c, 0x62,
- 0x23, 0xa0, 0x0c, 0x08,
- 0x00, 0x65, 0xba, 0x5b,
- 0xa0, 0x3d, 0x2c, 0x62,
- 0x00, 0xb9, 0x24, 0x42,
- 0xff, 0x65, 0x24, 0x62,
- 0xa1, 0x6a, 0x22, 0x01,
- 0xff, 0x6a, 0xd4, 0x08,
- 0x10, 0x51, 0x2c, 0x72,
- 0x40, 0x6a, 0x18, 0x00,
- 0xff, 0x65, 0x0c, 0x08,
- 0x00, 0x65, 0xba, 0x5b,
- 0xa0, 0x3d, 0xf6, 0x71,
- 0x40, 0x6a, 0x18, 0x00,
- 0xff, 0x34, 0xa6, 0x08,
- 0x80, 0x34, 0x34, 0x62,
- 0x7f, 0xa0, 0x40, 0x09,
- 0x08, 0x6a, 0x68, 0x00,
- 0x00, 0x65, 0xaa, 0x40,
- 0x64, 0x6a, 0x00, 0x5b,
- 0x80, 0x64, 0xaa, 0x6a,
- 0x04, 0x64, 0x8c, 0x72,
- 0x02, 0x64, 0x92, 0x72,
- 0x00, 0x6a, 0x54, 0x72,
- 0x03, 0x64, 0xa6, 0x72,
- 0x01, 0x64, 0x88, 0x72,
- 0x07, 0x64, 0xe8, 0x72,
- 0x08, 0x64, 0x50, 0x72,
- 0x23, 0x64, 0xec, 0x72,
- 0x11, 0x6a, 0x22, 0x01,
- 0x07, 0x6a, 0xf2, 0x5a,
- 0xff, 0x06, 0xd4, 0x08,
- 0x00, 0x65, 0xaa, 0x40,
- 0xff, 0xa8, 0x58, 0x6a,
- 0xff, 0xa2, 0x70, 0x7a,
- 0x01, 0x6a, 0x6a, 0x00,
- 0x00, 0xb9, 0xe6, 0x5b,
- 0xff, 0xa2, 0x70, 0x7a,
- 0x71, 0x6a, 0x22, 0x01,
- 0xff, 0x6a, 0xd4, 0x08,
- 0x40, 0x51, 0x70, 0x62,
- 0x0d, 0x6a, 0x6a, 0x00,
- 0x00, 0xb9, 0xe6, 0x5b,
- 0xff, 0x3e, 0x74, 0x09,
- 0xff, 0x90, 0x7c, 0x08,
- 0x00, 0x65, 0x4e, 0x58,
- 0x00, 0x65, 0xbc, 0x40,
- 0x20, 0xa0, 0x78, 0x6a,
- 0xff, 0x37, 0xc8, 0x08,
- 0x00, 0x6a, 0x90, 0x5b,
- 0xff, 0x6a, 0xa6, 0x5b,
- 0xff, 0xf8, 0xc8, 0x08,
- 0xff, 0x4f, 0xc8, 0x08,
- 0x01, 0x6a, 0x90, 0x5b,
- 0x00, 0xb9, 0xa6, 0x5b,
- 0x01, 0x4f, 0x9e, 0x18,
- 0x02, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0x80, 0x5c,
- 0x00, 0x65, 0xbc, 0x40,
- 0x41, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0xaa, 0x40,
- 0x04, 0xa0, 0x40, 0x01,
- 0x00, 0x65, 0x98, 0x5c,
- 0x00, 0x65, 0xbc, 0x40,
- 0x10, 0x36, 0x50, 0x7a,
- 0x05, 0x38, 0x46, 0x31,
- 0x04, 0x14, 0x58, 0x31,
- 0x03, 0xa9, 0x60, 0x31,
- 0xa3, 0x6a, 0xcc, 0x00,
- 0x38, 0x6a, 0xcc, 0x5b,
- 0xac, 0x6a, 0xcc, 0x00,
- 0x14, 0x6a, 0xce, 0x5b,
- 0xa9, 0x6a, 0xd0, 0x5b,
- 0x00, 0x65, 0x50, 0x42,
- 0xef, 0x36, 0x6c, 0x08,
- 0x00, 0x65, 0x50, 0x42,
- 0x0f, 0x64, 0xc8, 0x08,
- 0x07, 0x64, 0xc8, 0x08,
- 0x00, 0x37, 0x6e, 0x00,
- 0xff, 0x6a, 0xa4, 0x00,
- 0x00, 0x65, 0x60, 0x5b,
- 0xff, 0x51, 0xbc, 0x72,
- 0x20, 0x36, 0xc6, 0x7a,
- 0x00, 0x90, 0x4e, 0x5b,
- 0x00, 0x65, 0xc8, 0x42,
- 0xff, 0x06, 0xd4, 0x08,
- 0x00, 0x65, 0xba, 0x5b,
- 0xe0, 0x3d, 0xe2, 0x62,
- 0x20, 0x12, 0xe2, 0x62,
- 0x51, 0x6a, 0xf6, 0x5a,
- 0x00, 0x65, 0x48, 0x5b,
- 0xff, 0x37, 0xc8, 0x08,
- 0x00, 0xa1, 0xda, 0x62,
- 0x04, 0xa0, 0xda, 0x7a,
- 0xfb, 0xa0, 0x40, 0x09,
- 0x80, 0x36, 0x6c, 0x00,
- 0x80, 0xa0, 0x50, 0x7a,
- 0x7f, 0xa0, 0x40, 0x09,
- 0xff, 0x6a, 0xf2, 0x5a,
- 0x00, 0x65, 0x50, 0x42,
- 0x04, 0xa0, 0xe0, 0x7a,
- 0x00, 0x65, 0x98, 0x5c,
- 0x00, 0x65, 0xe2, 0x42,
- 0x00, 0x65, 0x80, 0x5c,
- 0x31, 0x6a, 0x22, 0x01,
- 0x0c, 0x6a, 0xf2, 0x5a,
- 0x00, 0x65, 0x50, 0x42,
- 0x61, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0x50, 0x42,
- 0x51, 0x6a, 0xf6, 0x5a,
- 0x51, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0x50, 0x42,
- 0x10, 0x3d, 0x06, 0x00,
- 0xff, 0x65, 0x68, 0x0c,
- 0xff, 0x06, 0xd4, 0x08,
- 0x01, 0x0c, 0xf8, 0x7a,
- 0x04, 0x0c, 0xfa, 0x6a,
- 0xe0, 0x03, 0x7a, 0x08,
- 0xe0, 0x3d, 0x06, 0x63,
- 0xff, 0x65, 0xcc, 0x08,
- 0xff, 0x12, 0xda, 0x0c,
- 0xff, 0x06, 0xd4, 0x0c,
- 0xd1, 0x6a, 0x22, 0x01,
- 0x00, 0x65, 0xaa, 0x40,
- 0xff, 0x65, 0x26, 0x09,
- 0x01, 0x0b, 0x1a, 0x6b,
- 0x10, 0x0c, 0x0c, 0x7b,
- 0x04, 0x0b, 0x14, 0x6b,
- 0xff, 0x6a, 0xca, 0x08,
- 0x04, 0x93, 0x18, 0x6b,
- 0x01, 0x94, 0x16, 0x7b,
- 0x10, 0x94, 0x18, 0x6b,
- 0x80, 0x3d, 0x1e, 0x73,
- 0x0f, 0x04, 0x22, 0x6b,
- 0x02, 0x03, 0x22, 0x7b,
- 0x11, 0x0c, 0x1e, 0x7b,
- 0xc7, 0x93, 0x26, 0x09,
- 0xff, 0x99, 0xd4, 0x08,
- 0x38, 0x93, 0x24, 0x6b,
- 0xff, 0x6a, 0xd4, 0x0c,
- 0x80, 0x36, 0x28, 0x6b,
- 0x21, 0x6a, 0x22, 0x05,
- 0xff, 0x65, 0x20, 0x09,
- 0xff, 0x51, 0x36, 0x63,
- 0xff, 0x37, 0xc8, 0x08,
- 0xa1, 0x6a, 0x42, 0x43,
- 0xff, 0x51, 0xc8, 0x08,
- 0xb9, 0x6a, 0x42, 0x43,
- 0xff, 0x90, 0xa4, 0x08,
- 0xff, 0xba, 0x46, 0x73,
- 0xff, 0xba, 0x20, 0x09,
- 0xff, 0x65, 0xca, 0x18,
- 0x00, 0x6c, 0x3a, 0x63,
- 0xff, 0x90, 0xca, 0x0c,
- 0xff, 0x6a, 0xca, 0x04,
- 0x20, 0x36, 0x5a, 0x7b,
- 0x00, 0x90, 0x2e, 0x5b,
- 0xff, 0x65, 0x5a, 0x73,
- 0xff, 0x52, 0x58, 0x73,
- 0xff, 0xba, 0xcc, 0x08,
- 0xff, 0x52, 0x20, 0x09,
- 0xff, 0x66, 0x74, 0x09,
- 0xff, 0x65, 0x20, 0x0d,
- 0xff, 0xba, 0x7e, 0x0c,
- 0x00, 0x6a, 0x86, 0x5c,
- 0x0d, 0x6a, 0x6a, 0x00,
- 0x00, 0x51, 0xe6, 0x43,
- 0xff, 0x3f, 0xb4, 0x73,
- 0xff, 0x6a, 0xa2, 0x00,
- 0x00, 0x3f, 0x2e, 0x5b,
- 0xff, 0x65, 0xb4, 0x73,
- 0x20, 0x36, 0x6c, 0x00,
- 0x20, 0xa0, 0x6e, 0x6b,
- 0xff, 0xb9, 0xa2, 0x0c,
- 0xff, 0x6a, 0xa2, 0x04,
- 0xff, 0x65, 0xa4, 0x08,
- 0xe0, 0x6a, 0xcc, 0x00,
- 0x45, 0x6a, 0xda, 0x5b,
- 0x01, 0x6a, 0xd0, 0x01,
- 0x09, 0x6a, 0xd6, 0x01,
- 0x80, 0xeb, 0x7a, 0x7b,
- 0x01, 0x6a, 0xd6, 0x01,
- 0x01, 0xe9, 0xa4, 0x34,
- 0x88, 0x6a, 0xcc, 0x00,
- 0x45, 0x6a, 0xda, 0x5b,
- 0x01, 0x6a, 0x18, 0x01,
- 0xff, 0x6a, 0x1a, 0x09,
- 0xff, 0x6a, 0x1c, 0x09,
- 0x0d, 0x6a, 0x26, 0x01,
- 0x00, 0x65, 0x78, 0x5c,
- 0xff, 0x99, 0xa4, 0x0c,
- 0xff, 0x65, 0xa4, 0x08,
- 0xe0, 0x6a, 0xcc, 0x00,
- 0x45, 0x6a, 0xda, 0x5b,
- 0x01, 0x6a, 0xd0, 0x01,
- 0x01, 0x6a, 0xdc, 0x05,
- 0x88, 0x6a, 0xcc, 0x00,
- 0x45, 0x6a, 0xda, 0x5b,
- 0x01, 0x6a, 0x18, 0x01,
- 0xff, 0x6a, 0x1a, 0x09,
- 0xff, 0x6a, 0x1c, 0x09,
- 0x01, 0x6a, 0x26, 0x05,
- 0x01, 0x65, 0xd8, 0x31,
- 0x09, 0xee, 0xdc, 0x01,
- 0x80, 0xee, 0xaa, 0x7b,
- 0xff, 0x6a, 0xdc, 0x0d,
- 0xff, 0x65, 0x32, 0x09,
- 0x0a, 0x93, 0x26, 0x01,
- 0x00, 0x65, 0x78, 0x44,
- 0xff, 0x37, 0xc8, 0x08,
- 0x00, 0x6a, 0x70, 0x5b,
- 0xff, 0x52, 0xa2, 0x0c,
- 0x01, 0x0c, 0xba, 0x7b,
- 0x04, 0x0c, 0xba, 0x6b,
- 0xe0, 0x03, 0x06, 0x08,
- 0xe0, 0x03, 0x7a, 0x0c,
- 0xff, 0x8c, 0x10, 0x08,
- 0xff, 0x8d, 0x12, 0x08,
- 0xff, 0x8e, 0x14, 0x0c,
- 0xff, 0x6c, 0xda, 0x08,
- 0xff, 0x6c, 0xda, 0x08,
- 0xff, 0x6c, 0xda, 0x08,
- 0xff, 0x6c, 0xda, 0x08,
- 0xff, 0x6c, 0xda, 0x08,
- 0xff, 0x6c, 0xda, 0x08,
- 0xff, 0x6c, 0xda, 0x0c,
- 0x3d, 0x64, 0xa4, 0x28,
- 0x55, 0x64, 0xc8, 0x28,
- 0x00, 0x6c, 0xda, 0x18,
- 0xff, 0x52, 0xc8, 0x08,
- 0x00, 0x6c, 0xda, 0x20,
- 0xff, 0x6a, 0xc8, 0x08,
- 0x00, 0x6c, 0xda, 0x20,
- 0x00, 0x6c, 0xda, 0x24,
- 0xff, 0x65, 0xc8, 0x08,
- 0xe0, 0x6a, 0xcc, 0x00,
- 0x41, 0x6a, 0xd6, 0x5b,
- 0xff, 0x90, 0xe2, 0x09,
- 0x20, 0x6a, 0xd0, 0x01,
- 0x04, 0x35, 0xf8, 0x7b,
- 0x1d, 0x6a, 0xdc, 0x01,
- 0xdc, 0xee, 0xf4, 0x63,
- 0x00, 0x65, 0x0e, 0x44,
- 0x01, 0x6a, 0xdc, 0x01,
- 0x20, 0xa0, 0xd8, 0x31,
- 0x09, 0xee, 0xdc, 0x01,
- 0x80, 0xee, 0xfe, 0x7b,
- 0x11, 0x6a, 0xdc, 0x01,
- 0x50, 0xee, 0x02, 0x64,
- 0x20, 0x6a, 0xd0, 0x01,
- 0x09, 0x6a, 0xdc, 0x01,
- 0x88, 0xee, 0x08, 0x64,
- 0x19, 0x6a, 0xdc, 0x01,
- 0xd8, 0xee, 0x0c, 0x64,
- 0xff, 0x6a, 0xdc, 0x09,
- 0x18, 0xee, 0x10, 0x6c,
- 0xff, 0x6a, 0xd4, 0x0c,
- 0x88, 0x6a, 0xcc, 0x00,
- 0x41, 0x6a, 0xd6, 0x5b,
- 0x20, 0x6a, 0x18, 0x01,
- 0xff, 0x6a, 0x1a, 0x09,
- 0xff, 0x6a, 0x1c, 0x09,
- 0xff, 0x35, 0x26, 0x09,
- 0x04, 0x35, 0x3c, 0x6c,
- 0xa0, 0x6a, 0xca, 0x00,
- 0x20, 0x65, 0xc8, 0x18,
- 0xff, 0x6c, 0x32, 0x09,
- 0xff, 0x6c, 0x32, 0x09,
- 0xff, 0x6c, 0x32, 0x09,
- 0xff, 0x6c, 0x32, 0x09,
- 0xff, 0x6c, 0x32, 0x09,
- 0xff, 0x6c, 0x32, 0x09,
- 0xff, 0x6c, 0x32, 0x09,
- 0xff, 0x6c, 0x32, 0x09,
- 0x00, 0x65, 0x26, 0x64,
- 0x0a, 0x93, 0x26, 0x01,
- 0x00, 0x65, 0x78, 0x44,
- 0xa0, 0x6a, 0xcc, 0x00,
- 0xe8, 0x6a, 0xc8, 0x00,
- 0x01, 0x94, 0x40, 0x6c,
- 0x10, 0x94, 0x42, 0x6c,
- 0x08, 0x94, 0x54, 0x6c,
- 0x08, 0x94, 0x54, 0x6c,
- 0x08, 0x94, 0x54, 0x6c,
- 0x00, 0x65, 0x68, 0x5c,
- 0x08, 0x64, 0xc8, 0x18,
- 0x00, 0x8c, 0xca, 0x18,
- 0x00, 0x65, 0x4a, 0x4c,
- 0x00, 0x65, 0x40, 0x44,
- 0xf7, 0x93, 0x26, 0x09,
- 0x08, 0x93, 0x56, 0x6c,
- 0x00, 0x65, 0x68, 0x5c,
- 0x08, 0x64, 0xc8, 0x18,
- 0x08, 0x64, 0x58, 0x64,
- 0xff, 0x6a, 0xd4, 0x0c,
- 0x00, 0x65, 0x78, 0x5c,
- 0x00, 0x65, 0x68, 0x5c,
- 0x00, 0x65, 0x68, 0x5c,
- 0x00, 0x65, 0x68, 0x5c,
- 0xff, 0x99, 0xda, 0x08,
- 0xff, 0x99, 0xda, 0x08,
- 0xff, 0x99, 0xda, 0x08,
- 0xff, 0x99, 0xda, 0x08,
- 0xff, 0x99, 0xda, 0x08,
- 0xff, 0x99, 0xda, 0x08,
- 0xff, 0x99, 0xda, 0x08,
- 0xff, 0x99, 0xda, 0x0c,
- 0x08, 0x94, 0x78, 0x7c,
- 0xf7, 0x93, 0x26, 0x09,
- 0x08, 0x93, 0x7c, 0x6c,
- 0xff, 0x6a, 0xd4, 0x0c,
- 0xff, 0x40, 0x74, 0x09,
- 0xff, 0x90, 0x80, 0x08,
- 0xff, 0x6a, 0x72, 0x05,
- 0xff, 0x40, 0x94, 0x64,
- 0xff, 0x3f, 0x8c, 0x64,
- 0xff, 0x6a, 0xca, 0x04,
- 0xff, 0x3f, 0x20, 0x09,
- 0x01, 0x6a, 0x6a, 0x00,
- 0x00, 0xb9, 0xe6, 0x5b,
- 0xff, 0xba, 0x7e, 0x0c,
- 0xff, 0x40, 0x20, 0x09,
- 0xff, 0xba, 0x80, 0x0c,
- 0xff, 0x3f, 0x74, 0x09,
- 0xff, 0x90, 0x7e, 0x0c,
-};
-
-static int aic7xxx_patch15_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch15_func(struct aic7xxx_host *p)
-{
- return ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0);
-}
-
-static int aic7xxx_patch14_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch14_func(struct aic7xxx_host *p)
-{
- return ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0);
-}
-
-static int aic7xxx_patch13_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch13_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_WIDE) != 0);
-}
-
-static int aic7xxx_patch12_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch12_func(struct aic7xxx_host *p)
-{
- return ((p->bugs & AHC_BUG_AUTOFLUSH) != 0);
-}
-
-static int aic7xxx_patch11_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch11_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_ULTRA2) == 0);
-}
-
-static int aic7xxx_patch10_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch10_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_CMD_CHAN) == 0);
-}
-
-static int aic7xxx_patch9_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch9_func(struct aic7xxx_host *p)
-{
- return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
-}
-
-static int aic7xxx_patch8_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch8_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_ULTRA) != 0);
-}
-
-static int aic7xxx_patch7_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch7_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_ULTRA2) != 0);
-}
-
-static int aic7xxx_patch6_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch6_func(struct aic7xxx_host *p)
-{
- return ((p->flags & AHC_PAGESCBS) == 0);
-}
-
-static int aic7xxx_patch5_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch5_func(struct aic7xxx_host *p)
-{
- return ((p->flags & AHC_PAGESCBS) != 0);
-}
-
-static int aic7xxx_patch4_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch4_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_QUEUE_REGS) != 0);
-}
-
-static int aic7xxx_patch3_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch3_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_TWIN) != 0);
-}
-
-static int aic7xxx_patch2_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch2_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_QUEUE_REGS) == 0);
-}
-
-static int aic7xxx_patch1_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch1_func(struct aic7xxx_host *p)
-{
- return ((p->features & AHC_CMD_CHAN) != 0);
-}
-
-static int aic7xxx_patch0_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch0_func(struct aic7xxx_host *p)
-{
- return (0);
-}
-
-struct sequencer_patch {
- int (*patch_func)(struct aic7xxx_host *);
- unsigned int begin :10,
- skip_instr :10,
- skip_patch :12;
-} sequencer_patches[] = {
- { aic7xxx_patch1_func, 3, 2, 1 },
- { aic7xxx_patch2_func, 7, 1, 1 },
- { aic7xxx_patch2_func, 8, 1, 1 },
- { aic7xxx_patch3_func, 11, 4, 1 },
- { aic7xxx_patch4_func, 16, 3, 2 },
- { aic7xxx_patch0_func, 19, 4, 1 },
- { aic7xxx_patch5_func, 23, 1, 1 },
- { aic7xxx_patch6_func, 26, 1, 1 },
- { aic7xxx_patch1_func, 29, 1, 2 },
- { aic7xxx_patch0_func, 30, 3, 1 },
- { aic7xxx_patch3_func, 39, 4, 1 },
- { aic7xxx_patch7_func, 43, 3, 2 },
- { aic7xxx_patch0_func, 46, 3, 1 },
- { aic7xxx_patch8_func, 52, 7, 1 },
- { aic7xxx_patch3_func, 60, 3, 1 },
- { aic7xxx_patch7_func, 63, 2, 1 },
- { aic7xxx_patch7_func, 102, 1, 2 },
- { aic7xxx_patch0_func, 103, 2, 1 },
- { aic7xxx_patch7_func, 107, 2, 1 },
- { aic7xxx_patch9_func, 109, 1, 1 },
- { aic7xxx_patch10_func, 110, 2, 1 },
- { aic7xxx_patch7_func, 113, 1, 2 },
- { aic7xxx_patch0_func, 114, 1, 1 },
- { aic7xxx_patch1_func, 118, 1, 1 },
- { aic7xxx_patch1_func, 121, 3, 3 },
- { aic7xxx_patch11_func, 123, 1, 1 },
- { aic7xxx_patch0_func, 124, 5, 1 },
- { aic7xxx_patch7_func, 132, 1, 1 },
- { aic7xxx_patch9_func, 133, 1, 1 },
- { aic7xxx_patch10_func, 134, 3, 1 },
- { aic7xxx_patch7_func, 137, 3, 2 },
- { aic7xxx_patch0_func, 140, 2, 1 },
- { aic7xxx_patch7_func, 142, 5, 2 },
- { aic7xxx_patch0_func, 147, 3, 1 },
- { aic7xxx_patch7_func, 150, 1, 2 },
- { aic7xxx_patch0_func, 151, 2, 1 },
- { aic7xxx_patch1_func, 153, 15, 4 },
- { aic7xxx_patch11_func, 166, 1, 2 },
- { aic7xxx_patch0_func, 167, 1, 1 },
- { aic7xxx_patch0_func, 168, 10, 1 },
- { aic7xxx_patch7_func, 181, 1, 2 },
- { aic7xxx_patch0_func, 182, 2, 1 },
- { aic7xxx_patch7_func, 184, 18, 1 },
- { aic7xxx_patch1_func, 202, 3, 3 },
- { aic7xxx_patch7_func, 204, 1, 1 },
- { aic7xxx_patch0_func, 205, 4, 1 },
- { aic7xxx_patch7_func, 210, 2, 1 },
- { aic7xxx_patch7_func, 215, 13, 3 },
- { aic7xxx_patch12_func, 218, 1, 1 },
- { aic7xxx_patch12_func, 219, 4, 1 },
- { aic7xxx_patch1_func, 229, 3, 3 },
- { aic7xxx_patch11_func, 231, 1, 1 },
- { aic7xxx_patch0_func, 232, 5, 1 },
- { aic7xxx_patch11_func, 237, 1, 2 },
- { aic7xxx_patch0_func, 238, 9, 1 },
- { aic7xxx_patch13_func, 254, 1, 2 },
- { aic7xxx_patch0_func, 255, 1, 1 },
- { aic7xxx_patch4_func, 316, 1, 2 },
- { aic7xxx_patch0_func, 317, 1, 1 },
- { aic7xxx_patch2_func, 320, 1, 1 },
- { aic7xxx_patch1_func, 330, 3, 2 },
- { aic7xxx_patch0_func, 333, 5, 1 },
- { aic7xxx_patch13_func, 341, 1, 2 },
- { aic7xxx_patch0_func, 342, 1, 1 },
- { aic7xxx_patch5_func, 347, 1, 1 },
- { aic7xxx_patch11_func, 389, 15, 2 },
- { aic7xxx_patch14_func, 402, 1, 1 },
- { aic7xxx_patch1_func, 441, 7, 2 },
- { aic7xxx_patch0_func, 448, 8, 1 },
- { aic7xxx_patch1_func, 457, 4, 2 },
- { aic7xxx_patch0_func, 461, 6, 1 },
- { aic7xxx_patch1_func, 467, 4, 2 },
- { aic7xxx_patch0_func, 471, 3, 1 },
- { aic7xxx_patch10_func, 481, 10, 1 },
- { aic7xxx_patch1_func, 500, 22, 5 },
- { aic7xxx_patch11_func, 508, 4, 1 },
- { aic7xxx_patch7_func, 512, 7, 3 },
- { aic7xxx_patch15_func, 512, 5, 2 },
- { aic7xxx_patch0_func, 517, 2, 1 },
- { aic7xxx_patch10_func, 522, 50, 3 },
- { aic7xxx_patch14_func, 543, 17, 2 },
- { aic7xxx_patch0_func, 560, 4, 1 },
- { aic7xxx_patch10_func, 572, 4, 1 },
- { aic7xxx_patch5_func, 576, 2, 1 },
- { aic7xxx_patch5_func, 579, 9, 1 },
-
-};
diff --git a/drivers/scsi/aic7xxx_old/scsi_message.h b/drivers/scsi/aic7xxx_old/scsi_message.h
deleted file mode 100644
index a79f89c65173..000000000000
--- a/drivers/scsi/aic7xxx_old/scsi_message.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
-#define MSG_CMDCOMPLETE 0x00 /* M/M */
-#define MSG_EXTENDED 0x01 /* O/O */
-#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
-#define MSG_RESTOREPOINTERS 0x03 /* O/O */
-#define MSG_DISCONNECT 0x04 /* O/O */
-#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
-#define MSG_ABORT 0x06 /* O/M */
-#define MSG_MESSAGE_REJECT 0x07 /* M/M */
-#define MSG_NOOP 0x08 /* M/M */
-#define MSG_PARITY_ERROR 0x09 /* M/M */
-#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
-#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
-#define MSG_BUS_DEV_RESET 0x0c /* O/M */
-#define MSG_ABORT_TAG 0x0d /* O/O */
-#define MSG_CLEAR_QUEUE 0x0e /* O/O */
-#define MSG_INIT_RECOVERY 0x0f /* O/O */
-#define MSG_REL_RECOVERY 0x10 /* O/O */
-#define MSG_TERM_IO_PROC 0x11 /* O/O */
-
-/* Messages (2 byte) */
-#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
-#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
-#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
-#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
-
-/* Identify message */ /* M/M */
-#define MSG_IDENTIFYFLAG 0x80
-#define MSG_IDENTIFY_DISCFLAG 0x40
-#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
-#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG)
-
-/* Extended messages (opcode and length) */
-#define MSG_EXT_SDTR 0x01
-#define MSG_EXT_SDTR_LEN 0x03
-
-#define MSG_EXT_WDTR 0x03
-#define MSG_EXT_WDTR_LEN 0x02
-#define MSG_EXT_WDTR_BUS_8_BIT 0x00
-#define MSG_EXT_WDTR_BUS_16_BIT 0x01
-#define MSG_EXT_WDTR_BUS_32_BIT 0x02
-
-#define MSG_EXT_PPR 0x04
-#define MSG_EXT_PPR_LEN 0x06
-#define MSG_EXT_PPR_OPTION_ST 0x00
-#define MSG_EXT_PPR_OPTION_DT_CRC 0x02
-#define MSG_EXT_PPR_OPTION_DT_UNITS 0x03
-#define MSG_EXT_PPR_OPTION_DT_CRC_QUICK 0x04
-#define MSG_EXT_PPR_OPTION_DT_UNITS_QUICK 0x05
diff --git a/drivers/scsi/aic7xxx_old/sequencer.h b/drivers/scsi/aic7xxx_old/sequencer.h
deleted file mode 100644
index ee66855222b1..000000000000
--- a/drivers/scsi/aic7xxx_old/sequencer.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Instruction formats for the sequencer program downloaded to
- * Aic7xxx SCSI host adapters
- *
- * Copyright (c) 1997, 1998 Justin T. Gibbs.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification, immediately at the beginning of the file.
- * 2. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * Where this Software is combined with software released under the terms of
- * the GNU General Public License ("GPL") and the terms of the GPL would require the
- * combined work to also be released under the terms of the GPL, the terms
- * and conditions of this License will apply in addition to those of the
- * GPL with the exception of any terms or conditions of this License that
- * conflict with, or are expressly prohibited by, the GPL.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Id: sequencer.h,v 1.3 1997/09/27 19:37:31 gibbs Exp $
- */
-
-#ifdef __LITTLE_ENDIAN_BITFIELD
-struct ins_format1 {
- unsigned int
- immediate : 8,
- source : 9,
- destination : 9,
- ret : 1,
- opcode : 4,
- parity : 1;
-};
-
-struct ins_format2 {
- unsigned int
- shift_control : 8,
- source : 9,
- destination : 9,
- ret : 1,
- opcode : 4,
- parity : 1;
-};
-
-struct ins_format3 {
- unsigned int
- immediate : 8,
- source : 9,
- address : 10,
- opcode : 4,
- parity : 1;
-};
-#elif defined(__BIG_ENDIAN_BITFIELD)
-struct ins_format1 {
- unsigned int
- parity : 1,
- opcode : 4,
- ret : 1,
- destination : 9,
- source : 9,
- immediate : 8;
-};
-
-struct ins_format2 {
- unsigned int
- parity : 1,
- opcode : 4,
- ret : 1,
- destination : 9,
- source : 9,
- shift_control : 8;
-};
-
-struct ins_format3 {
- unsigned int
- parity : 1,
- opcode : 4,
- address : 10,
- source : 9,
- immediate : 8;
-};
-#endif
-
-union ins_formats {
- struct ins_format1 format1;
- struct ins_format2 format2;
- struct ins_format3 format3;
- unsigned char bytes[4];
- unsigned int integer;
-};
-struct instruction {
- union ins_formats format;
- unsigned int srcline;
- struct symbol *patch_label;
- struct {
- struct instruction *stqe_next;
- } links;
-};
-
-#define AIC_OP_OR 0x0
-#define AIC_OP_AND 0x1
-#define AIC_OP_XOR 0x2
-#define AIC_OP_ADD 0x3
-#define AIC_OP_ADC 0x4
-#define AIC_OP_ROL 0x5
-#define AIC_OP_BMOV 0x6
-
-#define AIC_OP_JMP 0x8
-#define AIC_OP_JC 0x9
-#define AIC_OP_JNC 0xa
-#define AIC_OP_CALL 0xb
-#define AIC_OP_JNE 0xc
-#define AIC_OP_JNZ 0xd
-#define AIC_OP_JE 0xe
-#define AIC_OP_JZ 0xf
-
-/* Pseudo Ops */
-#define AIC_OP_SHL 0x10
-#define AIC_OP_SHR 0x20
-#define AIC_OP_ROR 0x30
diff --git a/drivers/scsi/aic94xx/Kconfig b/drivers/scsi/aic94xx/Kconfig
index c83fe751d0bb..aaa8dadc6e1c 100644
--- a/drivers/scsi/aic94xx/Kconfig
+++ b/drivers/scsi/aic94xx/Kconfig
@@ -1,32 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Kernel configuration file for aic94xx SAS/SATA driver.
#
# Copyright (c) 2005 Adaptec, Inc. All rights reserved.
# Copyright (c) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
#
-# This file is licensed under GPLv2.
-#
-# This file is part of the aic94xx driver.
-#
-# The aic94xx driver is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; version 2 of the
-# License.
-#
-# The aic94xx driver is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Aic94xx Driver; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-#
-#
config SCSI_AIC94XX
tristate "Adaptec AIC94xx SAS/SATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select SCSI_SAS_LIBSAS
select FW_LOADER
help
diff --git a/drivers/scsi/aic94xx/Makefile b/drivers/scsi/aic94xx/Makefile
index c0a15c754585..db9fbe3a8e4c 100644
--- a/drivers/scsi/aic94xx/Makefile
+++ b/drivers/scsi/aic94xx/Makefile
@@ -1,26 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Adaptec aic94xx SAS/SATA driver.
#
# Copyright (C) 2005 Adaptec, Inc. All rights reserved.
# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
#
-# This file is licensed under GPLv2.
-#
-# This file is part of the aic94xx driver.
-#
-# The aic94xx driver is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; version 2 of the
-# License.
-#
-# The aic94xx driver is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with the aic94xx driver; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
ccflags-$(CONFIG_AIC94XX_DEBUG) := -DASD_DEBUG -DASD_ENTER_EXIT
diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h
index 2863a9d22851..f595bc2ee45e 100644
--- a/drivers/scsi/aic94xx/aic94xx.h
+++ b/drivers/scsi/aic94xx/aic94xx.h
@@ -1,27 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver header file.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
*
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
* $Id: //depot/aic94xx/aic94xx.h#31 $
*/
@@ -50,7 +33,7 @@
#ifdef ASD_DEBUG
#define ASD_DPRINTK asd_printk
#else
-#define ASD_DPRINTK(fmt, ...)
+#define ASD_DPRINTK(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif
/* 2*ITNL timeout + 1 second */
@@ -59,14 +42,6 @@
extern struct kmem_cache *asd_dma_token_cache;
extern struct kmem_cache *asd_ascb_cache;
-static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr)
-{
- int i;
- for (i = 0; i < SAS_ADDR_SIZE; i++, p += 2)
- snprintf(p, 3, "%02X", sas_addr[i]);
- *p = '\0';
-}
-
struct asd_ha_struct;
struct asd_ascb;
@@ -78,12 +53,13 @@ void asd_dev_gone(struct domain_device *dev);
void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
-int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags);
+int asd_execute_task(struct sas_task *task, gfp_t gfp_flags);
+
+void asd_set_dmamode(struct domain_device *dev);
/* ---------- TMFs ---------- */
int asd_abort_task(struct sas_task *);
int asd_abort_task_set(struct domain_device *, u8 *lun);
-int asd_clear_aca(struct domain_device *, u8 *lun);
int asd_clear_task_set(struct domain_device *, u8 *lun);
int asd_lu_reset(struct domain_device *, u8 *lun);
int asd_I_T_nexus_reset(struct domain_device *dev);
diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c
index 2e2ddec9c0b6..91d196f26b76 100644
--- a/drivers/scsi/aic94xx/aic94xx_dev.c
+++ b/drivers/scsi/aic94xx/aic94xx_dev.c
@@ -1,27 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA DDB management
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
*
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
* $Id: //depot/aic94xx/aic94xx_dev.c#21 $
*/
@@ -84,7 +67,7 @@ static void asd_set_ddb_type(struct domain_device *dev)
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
int ddb = (int) (unsigned long) dev->lldd_dev;
- if (dev->dev_type == SATA_PM_PORT)
+ if (dev->dev_type == SAS_SATA_PM_PORT)
asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_PM_PORT);
else if (dev->tproto)
asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_TARGET);
@@ -109,36 +92,46 @@ static int asd_init_sata_tag_ddb(struct domain_device *dev)
return 0;
}
-static int asd_init_sata(struct domain_device *dev)
+void asd_set_dmamode(struct domain_device *dev)
{
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+ struct ata_device *ata_dev = sas_to_ata_dev(dev);
int ddb = (int) (unsigned long) dev->lldd_dev;
u32 qdepth = 0;
- int res = 0;
- asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
- if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) &&
- dev->sata_dev.identify_device &&
- dev->sata_dev.identify_device[10] != 0) {
- u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]);
- u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]);
-
- if (w76 & 0x100) /* NCQ? */
- qdepth = (w75 & 0x1F) + 1;
+ if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM_PORT) {
+ if (ata_id_has_ncq(ata_dev->id))
+ qdepth = ata_id_queue_depth(ata_dev->id);
asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK,
(1ULL<<qdepth)-1);
asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth);
}
- if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM ||
- dev->dev_type == SATA_PM_PORT) {
+
+ if (qdepth > 0)
+ if (asd_init_sata_tag_ddb(dev) != 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
+ ata_dev->flags |= ATA_DFLAG_NCQ_OFF;
+ spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags);
+ }
+}
+
+static int asd_init_sata(struct domain_device *dev)
+{
+ struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+ int ddb = (int) (unsigned long) dev->lldd_dev;
+
+ asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
+ if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM ||
+ dev->dev_type == SAS_SATA_PM_PORT) {
struct dev_to_host_fis *fis = (struct dev_to_host_fis *)
dev->frame_rcvd;
asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status);
}
asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF);
- if (qdepth > 0)
- res = asd_init_sata_tag_ddb(dev);
- return res;
+
+ return 0;
}
static int asd_init_target_ddb(struct domain_device *dev)
@@ -164,9 +157,9 @@ static int asd_init_target_ddb(struct domain_device *dev)
asd_ddbsite_write_byte(asd_ha, ddb, CONN_MASK, dev->port->phy_mask);
if (dev->port->oob_mode != SATA_OOB_MODE) {
flags |= OPEN_REQUIRED;
- if ((dev->dev_type == SATA_DEV) ||
+ if ((dev->dev_type == SAS_SATA_DEV) ||
(dev->tproto & SAS_PROTOCOL_STP)) {
- struct smp_resp *rps_resp = &dev->sata_dev.rps_resp;
+ struct smp_rps_resp *rps_resp = &dev->sata_dev.rps_resp;
if (rps_resp->frame_type == SMP_RESPONSE &&
rps_resp->function == SMP_REPORT_PHY_SATA &&
rps_resp->result == SMP_RESP_FUNC_ACC) {
@@ -177,9 +170,7 @@ static int asd_init_target_ddb(struct domain_device *dev)
}
} else {
flags |= CONCURRENT_CONN_SUPP;
- if (!dev->parent &&
- (dev->dev_type == EDGE_DEV ||
- dev->dev_type == FANOUT_DEV))
+ if (!dev->parent && dev_is_expander(dev->dev_type))
asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
4);
else
@@ -188,7 +179,7 @@ static int asd_init_target_ddb(struct domain_device *dev)
asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1);
}
}
- if (dev->dev_type == SATA_PM)
+ if (dev->dev_type == SAS_SATA_PM)
flags |= SATA_MULTIPORT;
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags);
@@ -201,7 +192,7 @@ static int asd_init_target_ddb(struct domain_device *dev)
asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_TAIL, 0xFFFF);
asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
- if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
+ if (dev->dev_type == SAS_SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
i = asd_init_sata(dev);
if (i < 0) {
asd_free_ddb(asd_ha, ddb);
@@ -209,7 +200,7 @@ static int asd_init_target_ddb(struct domain_device *dev)
}
}
- if (dev->dev_type == SAS_END_DEV) {
+ if (dev->dev_type == SAS_END_DEVICE) {
struct sas_end_device *rdev = rphy_to_end_device(dev->rphy);
if (rdev->I_T_nexus_loss_timeout > 0)
asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
@@ -245,7 +236,7 @@ static int asd_init_sata_pm_table_ddb(struct domain_device *dev)
/**
* asd_init_sata_pm_port_ddb -- SATA Port Multiplier Port
- * dev: pointer to domain device
+ * @dev: pointer to domain device
*
* For SATA Port Multiplier Ports we need to allocate one SATA Port
* Multiplier Port DDB and depending on whether the target on it
@@ -290,7 +281,7 @@ static int asd_init_initiator_ddb(struct domain_device *dev)
/**
* asd_init_sata_pm_ddb -- SATA Port Multiplier
- * dev: pointer to domain device
+ * @dev: pointer to domain device
*
* For STP and direct-attached SATA Port Multipliers we need
* one target port DDB entry and one SATA PM table DDB entry.
@@ -318,10 +309,10 @@ int asd_dev_found(struct domain_device *dev)
spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
switch (dev->dev_type) {
- case SATA_PM:
+ case SAS_SATA_PM:
res = asd_init_sata_pm_ddb(dev);
break;
- case SATA_PM_PORT:
+ case SAS_SATA_PM_PORT:
res = asd_init_sata_pm_port_ddb(dev);
break;
default:
diff --git a/drivers/scsi/aic94xx/aic94xx_dump.c b/drivers/scsi/aic94xx/aic94xx_dump.c
index 67eeba3bdb06..552f1913e95e 100644
--- a/drivers/scsi/aic94xx/aic94xx_dump.c
+++ b/drivers/scsi/aic94xx/aic94xx_dump.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA driver dump interface.
*
@@ -5,31 +6,12 @@
* Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
*
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
* 2005/07/14/LT Complete overhaul of this file. Update pages, register
* locations, names, etc. Make use of macros. Print more information.
* Print all cseq and lseq mip and mdp.
- *
*/
-#include "linux/pci.h"
+#include <linux/pci.h>
#include "aic94xx.h"
#include "aic94xx_reg.h"
#include "aic94xx_reg_def.h"
@@ -738,154 +720,8 @@ static void asd_dump_lseq_state(struct asd_ha_struct *asd_ha, int lseq)
PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TIMER_TERM_TS);
}
-#if 0
-
/**
- * asd_dump_ddb_site -- dump a CSEQ DDB site
- * @asd_ha: pointer to host adapter structure
- * @site_no: site number of interest
- */
-void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no)
-{
- if (site_no >= asd_ha->hw_prof.max_ddbs)
- return;
-
-#define DDB_FIELDB(__name) \
- asd_ddbsite_read_byte(asd_ha, site_no, \
- offsetof(struct asd_ddb_ssp_smp_target_port, __name))
-#define DDB2_FIELDB(__name) \
- asd_ddbsite_read_byte(asd_ha, site_no, \
- offsetof(struct asd_ddb_stp_sata_target_port, __name))
-#define DDB_FIELDW(__name) \
- asd_ddbsite_read_word(asd_ha, site_no, \
- offsetof(struct asd_ddb_ssp_smp_target_port, __name))
-
-#define DDB_FIELDD(__name) \
- asd_ddbsite_read_dword(asd_ha, site_no, \
- offsetof(struct asd_ddb_ssp_smp_target_port, __name))
-
- asd_printk("DDB: 0x%02x\n", site_no);
- asd_printk("conn_type: 0x%02x\n", DDB_FIELDB(conn_type));
- asd_printk("conn_rate: 0x%02x\n", DDB_FIELDB(conn_rate));
- asd_printk("init_conn_tag: 0x%04x\n", be16_to_cpu(DDB_FIELDW(init_conn_tag)));
- asd_printk("send_queue_head: 0x%04x\n", be16_to_cpu(DDB_FIELDW(send_queue_head)));
- asd_printk("sq_suspended: 0x%02x\n", DDB_FIELDB(sq_suspended));
- asd_printk("DDB Type: 0x%02x\n", DDB_FIELDB(ddb_type));
- asd_printk("AWT Default: 0x%04x\n", DDB_FIELDW(awt_def));
- asd_printk("compat_features: 0x%02x\n", DDB_FIELDB(compat_features));
- asd_printk("Pathway Blocked Count: 0x%02x\n",
- DDB_FIELDB(pathway_blocked_count));
- asd_printk("arb_wait_time: 0x%04x\n", DDB_FIELDW(arb_wait_time));
- asd_printk("more_compat_features: 0x%08x\n",
- DDB_FIELDD(more_compat_features));
- asd_printk("Conn Mask: 0x%02x\n", DDB_FIELDB(conn_mask));
- asd_printk("flags: 0x%02x\n", DDB_FIELDB(flags));
- asd_printk("flags2: 0x%02x\n", DDB2_FIELDB(flags2));
- asd_printk("ExecQ Tail: 0x%04x\n",DDB_FIELDW(exec_queue_tail));
- asd_printk("SendQ Tail: 0x%04x\n",DDB_FIELDW(send_queue_tail));
- asd_printk("Active Task Count: 0x%04x\n",
- DDB_FIELDW(active_task_count));
- asd_printk("ITNL Reason: 0x%02x\n", DDB_FIELDB(itnl_reason));
- asd_printk("ITNL Timeout Const: 0x%04x\n", DDB_FIELDW(itnl_timeout));
- asd_printk("ITNL timestamp: 0x%08x\n", DDB_FIELDD(itnl_timestamp));
-}
-
-void asd_dump_ddb_0(struct asd_ha_struct *asd_ha)
-{
-#define DDB0_FIELDB(__name) \
- asd_ddbsite_read_byte(asd_ha, 0, \
- offsetof(struct asd_ddb_seq_shared, __name))
-#define DDB0_FIELDW(__name) \
- asd_ddbsite_read_word(asd_ha, 0, \
- offsetof(struct asd_ddb_seq_shared, __name))
-
-#define DDB0_FIELDD(__name) \
- asd_ddbsite_read_dword(asd_ha,0 , \
- offsetof(struct asd_ddb_seq_shared, __name))
-
-#define DDB0_FIELDA(__name, _o) \
- asd_ddbsite_read_byte(asd_ha, 0, \
- offsetof(struct asd_ddb_seq_shared, __name)+_o)
-
-
- asd_printk("DDB: 0\n");
- asd_printk("q_free_ddb_head:%04x\n", DDB0_FIELDW(q_free_ddb_head));
- asd_printk("q_free_ddb_tail:%04x\n", DDB0_FIELDW(q_free_ddb_tail));
- asd_printk("q_free_ddb_cnt:%04x\n", DDB0_FIELDW(q_free_ddb_cnt));
- asd_printk("q_used_ddb_head:%04x\n", DDB0_FIELDW(q_used_ddb_head));
- asd_printk("q_used_ddb_tail:%04x\n", DDB0_FIELDW(q_used_ddb_tail));
- asd_printk("shared_mem_lock:%04x\n", DDB0_FIELDW(shared_mem_lock));
- asd_printk("smp_conn_tag:%04x\n", DDB0_FIELDW(smp_conn_tag));
- asd_printk("est_nexus_buf_cnt:%04x\n", DDB0_FIELDW(est_nexus_buf_cnt));
- asd_printk("est_nexus_buf_thresh:%04x\n",
- DDB0_FIELDW(est_nexus_buf_thresh));
- asd_printk("conn_not_active:%02x\n", DDB0_FIELDB(conn_not_active));
- asd_printk("phy_is_up:%02x\n", DDB0_FIELDB(phy_is_up));
- asd_printk("port_map_by_links:%02x %02x %02x %02x "
- "%02x %02x %02x %02x\n",
- DDB0_FIELDA(port_map_by_links, 0),
- DDB0_FIELDA(port_map_by_links, 1),
- DDB0_FIELDA(port_map_by_links, 2),
- DDB0_FIELDA(port_map_by_links, 3),
- DDB0_FIELDA(port_map_by_links, 4),
- DDB0_FIELDA(port_map_by_links, 5),
- DDB0_FIELDA(port_map_by_links, 6),
- DDB0_FIELDA(port_map_by_links, 7));
-}
-
-static void asd_dump_scb_site(struct asd_ha_struct *asd_ha, u16 site_no)
-{
-
-#define SCB_FIELDB(__name) \
- asd_scbsite_read_byte(asd_ha, site_no, sizeof(struct scb_header) \
- + offsetof(struct initiate_ssp_task, __name))
-#define SCB_FIELDW(__name) \
- asd_scbsite_read_word(asd_ha, site_no, sizeof(struct scb_header) \
- + offsetof(struct initiate_ssp_task, __name))
-#define SCB_FIELDD(__name) \
- asd_scbsite_read_dword(asd_ha, site_no, sizeof(struct scb_header) \
- + offsetof(struct initiate_ssp_task, __name))
-
- asd_printk("Total Xfer Len: 0x%08x.\n", SCB_FIELDD(total_xfer_len));
- asd_printk("Frame Type: 0x%02x.\n", SCB_FIELDB(ssp_frame.frame_type));
- asd_printk("Tag: 0x%04x.\n", SCB_FIELDW(ssp_frame.tag));
- asd_printk("Target Port Xfer Tag: 0x%04x.\n",
- SCB_FIELDW(ssp_frame.tptt));
- asd_printk("Data Offset: 0x%08x.\n", SCB_FIELDW(ssp_frame.data_offs));
- asd_printk("Retry Count: 0x%02x.\n", SCB_FIELDB(retry_count));
-}
-
-/**
- * asd_dump_scb_sites -- dump currently used CSEQ SCB sites
- * @asd_ha: pointer to host adapter struct
- */
-void asd_dump_scb_sites(struct asd_ha_struct *asd_ha)
-{
- u16 site_no;
-
- for (site_no = 0; site_no < asd_ha->hw_prof.max_scbs; site_no++) {
- u8 opcode;
-
- if (!SCB_SITE_VALID(site_no))
- continue;
-
- /* We are only interested in SCB sites currently used.
- */
- opcode = asd_scbsite_read_byte(asd_ha, site_no,
- offsetof(struct scb_header,
- opcode));
- if (opcode == 0xFF)
- continue;
-
- asd_printk("\nSCB: 0x%x\n", site_no);
- asd_dump_scb_site(asd_ha, site_no);
- }
-}
-
-#endif /* 0 */
-
-/**
- * ads_dump_seq_state -- dump CSEQ and LSEQ states
+ * asd_dump_seq_state -- dump CSEQ and LSEQ states
* @asd_ha: pointer to host adapter structure
* @lseq_mask: mask of LSEQs of interest
*/
@@ -926,42 +762,4 @@ void asd_dump_frame_rcvd(struct asd_phy *phy,
spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
}
-#if 0
-
-static void asd_dump_scb(struct asd_ascb *ascb, int ind)
-{
- asd_printk("scb%d: vaddr: 0x%p, dma_handle: 0x%llx, next: 0x%llx, "
- "index:%d, opcode:0x%02x\n",
- ind, ascb->dma_scb.vaddr,
- (unsigned long long)ascb->dma_scb.dma_handle,
- (unsigned long long)
- le64_to_cpu(ascb->scb->header.next_scb),
- le16_to_cpu(ascb->scb->header.index),
- ascb->scb->header.opcode);
-}
-
-void asd_dump_scb_list(struct asd_ascb *ascb, int num)
-{
- int i = 0;
-
- asd_printk("dumping %d scbs:\n", num);
-
- asd_dump_scb(ascb, i++);
- --num;
-
- if (num > 0 && !list_empty(&ascb->list)) {
- struct list_head *el;
-
- list_for_each(el, &ascb->list) {
- struct asd_ascb *s = list_entry(el, struct asd_ascb,
- list);
- asd_dump_scb(s, i++);
- if (--num <= 0)
- break;
- }
- }
-}
-
-#endif /* 0 */
-
#endif /* ASD_DEBUG */
diff --git a/drivers/scsi/aic94xx/aic94xx_dump.h b/drivers/scsi/aic94xx/aic94xx_dump.h
index 191a753d42a7..d8faa5db1998 100644
--- a/drivers/scsi/aic94xx/aic94xx_dump.h
+++ b/drivers/scsi/aic94xx/aic94xx_dump.h
@@ -1,27 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver dump header file.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef _AIC94XX_DUMP_H_
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c
index 81b736c76fff..e74393357025 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.c
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.c
@@ -1,27 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA driver hardware interface.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/pci.h>
@@ -46,7 +28,7 @@ static int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)
if (asd_ha->hw_prof.sas_addr[0])
return 0;
- return sas_request_addr(asd_ha->sas_ha.core.shost,
+ return sas_request_addr(asd_ha->sas_ha.shost,
asd_ha->hw_prof.sas_addr);
}
@@ -74,7 +56,7 @@ static void asd_init_phy_identify(struct asd_phy *phy)
memset(phy->identify_frame, 0, sizeof(*phy->identify_frame));
- phy->identify_frame->dev_type = SAS_END_DEV;
+ phy->identify_frame->dev_type = SAS_END_DEVICE;
if (phy->sas_phy.role & PHY_ROLE_INITIATOR)
phy->identify_frame->initiator_bits = phy->sas_phy.iproto;
if (phy->sas_phy.role & PHY_ROLE_TARGET)
@@ -90,10 +72,8 @@ static int asd_init_phy(struct asd_phy *phy)
struct asd_sas_phy *sas_phy = &phy->sas_phy;
sas_phy->enabled = 1;
- sas_phy->class = SAS;
sas_phy->iproto = SAS_PROTOCOL_ALL;
sas_phy->tproto = 0;
- sas_phy->type = PHY_TYPE_PHYSICAL;
sas_phy->role = PHY_ROLE_INITIATOR;
sas_phy->oob_mode = OOB_NOT_CONNECTED;
sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
@@ -220,16 +200,20 @@ static int asd_init_scbs(struct asd_ha_struct *asd_ha)
/* allocate the index array and bitmap */
asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs;
- asd_ha->seq.tc_index_array = kzalloc(asd_ha->seq.tc_index_bitmap_bits*
- sizeof(void *), GFP_KERNEL);
+ asd_ha->seq.tc_index_array = kcalloc(asd_ha->seq.tc_index_bitmap_bits,
+ sizeof(void *),
+ GFP_KERNEL);
if (!asd_ha->seq.tc_index_array)
return -ENOMEM;
bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8;
bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long);
asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL);
- if (!asd_ha->seq.tc_index_bitmap)
+ if (!asd_ha->seq.tc_index_bitmap) {
+ kfree(asd_ha->seq.tc_index_array);
+ asd_ha->seq.tc_index_array = NULL;
return -ENOMEM;
+ }
spin_lock_init(&seq->tc_index_lock);
@@ -288,7 +272,8 @@ static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, gfp_t gfp_flags)
struct asd_seq_data *seq = &asd_ha->seq;
int i;
- seq->edb_arr = kmalloc(seq->num_edbs*sizeof(*seq->edb_arr), gfp_flags);
+ seq->edb_arr = kmalloc_array(seq->num_edbs, sizeof(*seq->edb_arr),
+ gfp_flags);
if (!seq->edb_arr)
return -ENOMEM;
@@ -320,8 +305,8 @@ static int asd_alloc_escbs(struct asd_ha_struct *asd_ha,
struct asd_ascb *escb;
int i, escbs;
- seq->escb_arr = kmalloc(seq->num_escbs*sizeof(*seq->escb_arr),
- gfp_flags);
+ seq->escb_arr = kmalloc_array(seq->num_escbs, sizeof(*seq->escb_arr),
+ gfp_flags);
if (!seq->escb_arr)
return -ENOMEM;
@@ -477,7 +462,7 @@ static int asd_init_chip(struct asd_ha_struct *asd_ha)
err = asd_start_seqs(asd_ha);
if (err) {
- asd_printk("coudln't start seqs for %s\n",
+ asd_printk("couldn't start seqs for %s\n",
pci_name(asd_ha->pcidev));
goto out;
}
@@ -588,7 +573,7 @@ static int asd_extend_cmdctx(struct asd_ha_struct *asd_ha)
/**
* asd_init_ctxmem -- initialize context memory
- * asd_ha: pointer to host adapter structure
+ * @asd_ha: pointer to host adapter structure
*
* This function sets the maximum number of SCBs and
* DDBs which can be used by the sequencer. This is normally
@@ -632,7 +617,7 @@ int asd_init_hw(struct asd_ha_struct *asd_ha)
pci_name(asd_ha->pcidev));
return err;
}
- pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL,
+ err = pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL,
v | SC_TMR_DIS);
if (err) {
asd_printk("couldn't disable split completion timer of %s\n",
@@ -718,11 +703,8 @@ Out:
*/
static void asd_chip_reset(struct asd_ha_struct *asd_ha)
{
- struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
-
ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev));
asd_chip_hardrst(asd_ha);
- sas_ha->notify_ha_event(sas_ha, HAE_RESET);
}
/* ---------- Done List Routines ---------- */
@@ -749,7 +731,7 @@ static void asd_dl_tasklet_handler(unsigned long data)
goto next_1;
} else if (ascb->scb->header.opcode == EMPTY_SCB) {
goto out;
- } else if (!ascb->uldd_timer && !del_timer(&ascb->timer)) {
+ } else if (!ascb->uldd_timer && !timer_delete(&ascb->timer)) {
goto next_1;
}
spin_lock_irqsave(&seq->pend_q_lock, flags);
@@ -919,7 +901,7 @@ static void asd_dch_sas_isr(struct asd_ha_struct *asd_ha)
}
/**
- * ads_rbi_exsi_isr -- process external system interface interrupt (INITERR)
+ * asd_rbi_exsi_isr -- process external system interface interrupt (INITERR)
* @asd_ha: pointer to host adapter structure
*/
static void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha)
@@ -1055,14 +1037,13 @@ static struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha,
if (ascb) {
ascb->dma_scb.size = sizeof(struct scb);
- ascb->dma_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool,
+ ascb->dma_scb.vaddr = dma_pool_zalloc(asd_ha->scb_pool,
gfp_flags,
&ascb->dma_scb.dma_handle);
if (!ascb->dma_scb.vaddr) {
kmem_cache_free(asd_ascb_cache, ascb);
return NULL;
}
- memset(ascb->dma_scb.vaddr, 0, sizeof(struct scb));
asd_init_ascb(asd_ha, ascb);
spin_lock_irqsave(&seq->tc_index_lock, flags);
@@ -1161,9 +1142,8 @@ static void asd_swap_head_scb(struct asd_ha_struct *asd_ha,
}
/**
- * asd_start_timers -- (add and) start timers of SCBs
+ * asd_start_scb_timers -- (add and) start timers of SCBs
* @list: pointer to struct list_head of the scbs
- * @to: timeout in jiffies
*
* If an SCB in the @list has no timer function, assign the default
* one, then start the timer of the SCB. This function is
@@ -1175,7 +1155,6 @@ static void asd_start_scb_timers(struct list_head *list)
struct asd_ascb *ascb;
list_for_each_entry(ascb, list, list) {
if (!ascb->uldd_timer) {
- ascb->timer.data = (unsigned long) ascb;
ascb->timer.function = asd_ascb_timedout;
ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
add_timer(&ascb->timer);
@@ -1200,8 +1179,7 @@ static void asd_start_scb_timers(struct list_head *list)
* Case A: we can send the whole batch at once. Increment "pending"
* in the beginning of this function, when it is checked, in order to
* eliminate races when this function is called by multiple processes.
- * Case B: should never happen if the managing layer considers
- * lldd_queue_size.
+ * Case B: should never happen.
*/
int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
int num)
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h
index 8c1c28239e93..930e192b1cd4 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.h
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.h
@@ -1,27 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver hardware interface header file.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef _AIC94XX_HWI_H_
@@ -291,8 +273,7 @@ static inline void asd_init_ascb(struct asd_ha_struct *asd_ha,
INIT_LIST_HEAD(&ascb->list);
ascb->scb = ascb->dma_scb.vaddr;
ascb->ha = asd_ha;
- ascb->timer.function = NULL;
- init_timer(&ascb->timer);
+ timer_setup(&ascb->timer, NULL, 0);
ascb->tc_index = -1;
}
@@ -392,7 +373,7 @@ void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask);
-void asd_ascb_timedout(unsigned long data);
+void asd_ascb_timedout(struct timer_list *t);
int asd_chip_hardrst(struct asd_ha_struct *asd_ha);
#endif
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
index d5ff142c93a2..95f3620059f7 100644
--- a/drivers/scsi/aic94xx/aic94xx_init.c
+++ b/drivers/scsi/aic94xx/aic94xx_init.c
@@ -1,27 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA driver initialization.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/module.h>
@@ -32,6 +14,7 @@
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <scsi/sas_ata.h>
#include <scsi/scsi_host.h>
#include "aic94xx.h"
@@ -42,6 +25,7 @@
/* The format is "version.release.patchlevel" */
#define ASD_DRIVER_VERSION "1.0.3"
+#define DRV_NAME "aic94xx"
static int use_msi = 0;
module_param_named(use_msi, use_msi, int, S_IRUGO);
@@ -49,45 +33,22 @@ MODULE_PARM_DESC(use_msi, "\n"
"\tEnable(1) or disable(0) using PCI MSI.\n"
"\tDefault: 0");
-static int lldd_max_execute_num = 0;
-module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
-MODULE_PARM_DESC(collector, "\n"
- "\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
- "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
- "\tThe aic94xx SAS LLDD supports both modes.\n"
- "\tDefault: 0 (Direct Mode).\n");
-
static struct scsi_transport_template *aic94xx_transport_template;
static int asd_scan_finished(struct Scsi_Host *, unsigned long);
static void asd_scan_start(struct Scsi_Host *);
+static const struct attribute_group *asd_sdev_groups[];
-static struct scsi_host_template aic94xx_sht = {
- .module = THIS_MODULE,
- /* .name is initialized */
- .name = "aic94xx",
- .queuecommand = sas_queuecommand,
- .target_alloc = sas_target_alloc,
- .slave_configure = sas_slave_configure,
- .slave_destroy = sas_slave_destroy,
+static const struct scsi_host_template aic94xx_sht = {
+ LIBSAS_SHT_BASE
.scan_finished = asd_scan_finished,
.scan_start = asd_scan_start,
- .change_queue_depth = sas_change_queue_depth,
- .change_queue_type = sas_change_queue_type,
- .bios_param = sas_bios_param,
.can_queue = 1,
- .cmd_per_lun = 1,
- .this_id = -1,
.sg_tablesize = SG_ALL,
- .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
- .use_clustering = ENABLE_CLUSTERING,
- .eh_device_reset_handler = sas_eh_device_reset_handler,
- .eh_bus_reset_handler = sas_eh_bus_reset_handler,
- .slave_alloc = sas_slave_alloc,
- .target_destroy = sas_target_destroy,
- .ioctl = sas_ioctl,
+ .track_queue_depth = 1,
+ .sdev_groups = asd_sdev_groups,
};
-static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha)
+static int asd_map_memio(struct asd_ha_struct *asd_ha)
{
int err, i;
struct asd_ha_addrspace *io_handle;
@@ -110,15 +71,11 @@ static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha)
pci_name(asd_ha->pcidev));
goto Err;
}
- if (io_handle->flags & IORESOURCE_CACHEABLE)
- io_handle->addr = ioremap(io_handle->start,
- io_handle->len);
- else
- io_handle->addr = ioremap_nocache(io_handle->start,
- io_handle->len);
+ io_handle->addr = ioremap(io_handle->start, io_handle->len);
if (!io_handle->addr) {
asd_printk("couldn't map MBAR%d of %s\n", i==0?0:1,
pci_name(asd_ha->pcidev));
+ err = -ENOMEM;
goto Err_unreq;
}
}
@@ -148,7 +105,7 @@ static void asd_unmap_memio(struct asd_ha_struct *asd_ha)
pci_release_region(asd_ha->pcidev, 0);
}
-static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha)
+static int asd_map_ioport(struct asd_ha_struct *asd_ha)
{
int i = PCI_IOBAR_OFFSET, err;
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];
@@ -177,7 +134,7 @@ static void asd_unmap_ioport(struct asd_ha_struct *asd_ha)
pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET);
}
-static int __devinit asd_map_ha(struct asd_ha_struct *asd_ha)
+static int asd_map_ha(struct asd_ha_struct *asd_ha)
{
int err;
u16 cmd_reg;
@@ -223,7 +180,7 @@ static const char *asd_dev_rev[30] = {
[8] = "B0",
};
-static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha)
+static int asd_common_setup(struct asd_ha_struct *asd_ha)
{
int err, i;
@@ -259,7 +216,7 @@ Err:
return err;
}
-static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha)
+static int asd_aic9410_setup(struct asd_ha_struct *asd_ha)
{
int err = asd_common_setup(asd_ha);
@@ -274,7 +231,7 @@ static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha)
return 0;
}
-static int __devinit asd_aic9405_setup(struct asd_ha_struct *asd_ha)
+static int asd_aic9405_setup(struct asd_ha_struct *asd_ha)
{
int err = asd_common_setup(asd_ha);
@@ -296,7 +253,7 @@ static ssize_t asd_show_dev_rev(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%s\n",
asd_dev_rev[asd_ha->revision_id]);
}
-static DEVICE_ATTR(revision, S_IRUGO, asd_show_dev_rev, NULL);
+static DEVICE_ATTR(aic_revision, S_IRUGO, asd_show_dev_rev, NULL);
static ssize_t asd_show_dev_bios_build(struct device *dev,
struct device_attribute *attr,char *buf)
@@ -365,7 +322,7 @@ static ssize_t asd_store_update_bios(struct device *dev,
int flash_command = FLASH_CMD_NONE;
int err = 0;
- cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+ cmd_ptr = kcalloc(count, 2, GFP_KERNEL);
if (!cmd_ptr) {
err = FAIL_OUT_MEMORY;
@@ -493,7 +450,7 @@ static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
{
int err;
- err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+ err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_aic_revision);
if (err)
return err;
@@ -515,13 +472,13 @@ err_update_bios:
err_biosb:
device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
err_rev:
- device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+ device_remove_file(&asd_ha->pcidev->dev, &dev_attr_aic_revision);
return err;
}
static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
{
- device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+ device_remove_file(&asd_ha->pcidev->dev, &dev_attr_aic_revision);
device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
device_remove_file(&asd_ha->pcidev->dev, &dev_attr_update_bios);
@@ -533,7 +490,7 @@ static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
static const struct asd_pcidev_struct {
const char * name;
int (*setup)(struct asd_ha_struct *asd_ha);
-} asd_pcidev_data[] __devinitconst = {
+} asd_pcidev_data[] = {
/* Id 0 is used for dynamic ids. */
{ .name = "Adaptec AIC-94xx SAS/SATA Host Adapter",
.setup = asd_aic9410_setup
@@ -560,7 +517,7 @@ static int asd_create_ha_caches(struct asd_ha_struct *asd_ha)
return 0;
}
-/**
+/*
* asd_free_edbs -- free empty data buffers
* asd_ha: pointer to host adapter structure
*/
@@ -599,8 +556,7 @@ static void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha)
if (asd_ha->hw_prof.scb_ext)
asd_free_coherent(asd_ha, asd_ha->hw_prof.scb_ext);
- if (asd_ha->hw_prof.ddb_bitmap)
- kfree(asd_ha->hw_prof.ddb_bitmap);
+ kfree(asd_ha->hw_prof.ddb_bitmap);
asd_ha->hw_prof.ddb_bitmap = NULL;
for (i = 0; i < ASD_MAX_PHYS; i++) {
@@ -675,12 +631,10 @@ Err:
static void asd_destroy_global_caches(void)
{
- if (asd_dma_token_cache)
- kmem_cache_destroy(asd_dma_token_cache);
+ kmem_cache_destroy(asd_dma_token_cache);
asd_dma_token_cache = NULL;
- if (asd_ascb_cache)
- kmem_cache_destroy(asd_ascb_cache);
+ kmem_cache_destroy(asd_ascb_cache);
asd_ascb_cache = NULL;
}
@@ -699,7 +653,6 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
}
asd_ha->sas_ha.sas_ha_name = (char *) asd_ha->name;
- asd_ha->sas_ha.lldd_module = THIS_MODULE;
asd_ha->sas_ha.sas_addr = &asd_ha->hw_prof.sas_addr[0];
for (i = 0; i < ASD_MAX_PHYS; i++) {
@@ -711,9 +664,6 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
asd_ha->sas_ha.sas_port= sas_ports;
asd_ha->sas_ha.num_phys= ASD_MAX_PHYS;
- asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue;
- asd_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num;
-
return sas_register_ha(&asd_ha->sas_ha);
}
@@ -723,9 +673,8 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha)
err = sas_unregister_ha(&asd_ha->sas_ha);
- sas_remove_host(asd_ha->sas_ha.core.shost);
- scsi_remove_host(asd_ha->sas_ha.core.shost);
- scsi_host_put(asd_ha->sas_ha.core.shost);
+ sas_remove_host(asd_ha->sas_ha.shost);
+ scsi_host_put(asd_ha->sas_ha.shost);
kfree(asd_ha->sas_ha.sas_phy);
kfree(asd_ha->sas_ha.sas_port);
@@ -733,8 +682,7 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha)
return err;
}
-static int __devinit asd_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int asd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
const struct asd_pcidev_struct *asd_dev;
unsigned asd_id = (unsigned) id->driver_data;
@@ -776,7 +724,7 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev));
SHOST_TO_SAS_HA(shost) = &asd_ha->sas_ha;
- asd_ha->sas_ha.core.shost = shost;
+ asd_ha->sas_ha.shost = shost;
shost->transportt = aic94xx_transport_template;
shost->max_id = ~0;
shost->max_lun = ~0;
@@ -790,14 +738,11 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
if (err)
goto Err_remove;
- err = -ENODEV;
- if (!pci_set_dma_mask(dev, DMA_BIT_MASK(64))
- && !pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64)))
- ;
- else if (!pci_set_dma_mask(dev, DMA_BIT_MASK(32))
- && !pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(32)))
- ;
- else {
+ err = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64));
+ if (err)
+ err = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
+ if (err) {
+ err = -ENODEV;
asd_printk("no suitable DMA mask for %s\n", pci_name(dev));
goto Err_remove;
}
@@ -906,7 +851,7 @@ static void asd_free_queues(struct asd_ha_struct *asd_ha)
* times out. Apparently we don't wait for the CONTROL PHY
* to complete, so it doesn't matter if we kill the timer.
*/
- del_timer_sync(&ascb->timer);
+ timer_delete_sync(&ascb->timer);
WARN_ON(ascb->scb->header.opcode != CONTROL_PHY);
list_del_init(pos);
@@ -926,7 +871,7 @@ static void asd_turn_off_leds(struct asd_ha_struct *asd_ha)
}
}
-static void __devexit asd_pci_remove(struct pci_dev *dev)
+static void asd_pci_remove(struct pci_dev *dev)
{
struct asd_ha_struct *asd_ha = pci_get_drvdata(dev);
@@ -937,6 +882,9 @@ static void __devexit asd_pci_remove(struct pci_dev *dev)
asd_disable_ints(asd_ha);
+ /* Ensure all scheduled tasklets complete before freeing resources */
+ tasklet_kill(&asd_ha->seq.dl_tasklet);
+
asd_remove_dev_attrs(asd_ha);
/* XXX more here as needed */
@@ -972,15 +920,15 @@ static int asd_scan_finished(struct Scsi_Host *shost, unsigned long time)
if (time < HZ)
return 0;
/* Wait for discovery to finish */
- scsi_flush_work(shost);
+ sas_drain_work(SHOST_TO_SAS_HA(shost));
return 1;
}
-static ssize_t asd_version_show(struct device_driver *driver, char *buf)
+static ssize_t version_show(struct device_driver *driver, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", ASD_DRIVER_VERSION);
}
-static DRIVER_ATTR(version, S_IRUGO, asd_version_show, NULL);
+static DRIVER_ATTR_RO(version);
static int asd_create_driver_attrs(struct device_driver *driver)
{
@@ -992,6 +940,11 @@ static void asd_remove_driver_attrs(struct device_driver *driver)
driver_remove_file(driver, &driver_attr_version);
}
+static const struct attribute_group *asd_sdev_groups[] = {
+ &sas_ata_sdev_attr_group,
+ NULL
+};
+
static struct sas_domain_function_template aic94xx_transport_functions = {
.lldd_dev_found = asd_dev_found,
.lldd_dev_gone = asd_dev_gone,
@@ -1000,7 +953,6 @@ static struct sas_domain_function_template aic94xx_transport_functions = {
.lldd_abort_task = asd_abort_task,
.lldd_abort_task_set = asd_abort_task_set,
- .lldd_clear_aca = asd_clear_aca,
.lldd_clear_task_set = asd_clear_task_set,
.lldd_I_T_nexus_reset = asd_I_T_nexus_reset,
.lldd_lu_reset = asd_lu_reset,
@@ -1010,9 +962,11 @@ static struct sas_domain_function_template aic94xx_transport_functions = {
.lldd_clear_nexus_ha = asd_clear_nexus_ha,
.lldd_control_phy = asd_control_phy,
+
+ .lldd_ata_set_dmamode = asd_set_dmamode,
};
-static const struct pci_device_id aic94xx_pci_table[] __devinitdata = {
+static const struct pci_device_id aic94xx_pci_table[] = {
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x410),0, 0, 1},
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x412),0, 0, 1},
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x416),0, 0, 1},
@@ -1031,7 +985,7 @@ static struct pci_driver aic94xx_pci_driver = {
.name = ASD_DRIVER_NAME,
.id_table = aic94xx_pci_table,
.probe = asd_pci_probe,
- .remove = __devexit_p(asd_pci_remove),
+ .remove = asd_pci_remove,
};
static int __init aic94xx_init(void)
@@ -1048,8 +1002,10 @@ static int __init aic94xx_init(void)
aic94xx_transport_template =
sas_domain_attach_transport(&aic94xx_transport_functions);
- if (!aic94xx_transport_template)
+ if (!aic94xx_transport_template) {
+ err = -ENOMEM;
goto out_destroy_caches;
+ }
err = pci_register_driver(&aic94xx_pci_driver);
if (err)
diff --git a/drivers/scsi/aic94xx/aic94xx_reg.c b/drivers/scsi/aic94xx/aic94xx_reg.c
index 56b17c22526e..392499e80821 100644
--- a/drivers/scsi/aic94xx/aic94xx_reg.c
+++ b/drivers/scsi/aic94xx/aic94xx_reg.c
@@ -1,27 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA driver register access.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/pci.h>
diff --git a/drivers/scsi/aic94xx/aic94xx_reg.h b/drivers/scsi/aic94xx/aic94xx_reg.h
index 2279307fd27e..d1c0975f8df3 100644
--- a/drivers/scsi/aic94xx/aic94xx_reg.h
+++ b/drivers/scsi/aic94xx/aic94xx_reg.h
@@ -1,27 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver hardware registers definitions.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef _AIC94XX_REG_H_
diff --git a/drivers/scsi/aic94xx/aic94xx_reg_def.h b/drivers/scsi/aic94xx/aic94xx_reg_def.h
index dd6cc8008b16..b96cfc33bb8f 100644
--- a/drivers/scsi/aic94xx/aic94xx_reg_def.h
+++ b/drivers/scsi/aic94xx/aic94xx_reg_def.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver hardware registers definitions.
*
@@ -9,26 +10,7 @@
* agnostic register r/w functions. Some register corrections, sizes,
* etc.
*
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
* $Id: //depot/aic94xx/aic94xx_reg_def.h#27 $
- *
*/
#ifndef _ADP94XX_REG_DEF_H_
diff --git a/drivers/scsi/aic94xx/aic94xx_sas.h b/drivers/scsi/aic94xx/aic94xx_sas.h
index 912e6b755f74..3fe34cb96bb1 100644
--- a/drivers/scsi/aic94xx/aic94xx_sas.h
+++ b/drivers/scsi/aic94xx/aic94xx_sas.h
@@ -1,27 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver SAS definitions and hardware interface header file.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef _AIC94XX_SAS_H_
@@ -327,46 +309,9 @@ struct scb_header {
#define LUN_SIZE 8
-/* See SAS spec, task IU
- */
-struct ssp_task_iu {
- u8 lun[LUN_SIZE]; /* BE */
- u16 _r_a;
- u8 tmf;
- u8 _r_b;
- __be16 tag; /* BE */
- u8 _r_c[14];
-} __attribute__ ((packed));
-
-/* See SAS spec, command IU
- */
-struct ssp_command_iu {
- u8 lun[LUN_SIZE];
- u8 _r_a;
- u8 efb_prio_attr; /* enable first burst, task prio & attr */
-#define EFB_MASK 0x80
-#define TASK_PRIO_MASK 0x78
-#define TASK_ATTR_MASK 0x07
-
- u8 _r_b;
- u8 add_cdb_len; /* in dwords, since bit 0,1 are reserved */
- union {
- u8 cdb[16];
- struct {
- __le64 long_cdb_addr; /* bus address, LE */
- __le32 long_cdb_size; /* LE */
- u8 _r_c[3];
- u8 eol_ds; /* eol:6,6, ds:5,4 */
- } long_cdb; /* sequencer extension */
- };
-} __attribute__ ((packed));
-
-struct xfer_rdy_iu {
- __be32 requested_offset; /* BE */
- __be32 write_data_len; /* BE */
- __be32 _r_a;
-} __attribute__ ((packed));
-
+#define EFB_MASK 0x80
+#define TASK_PRIO_MASK 0x78
+#define TASK_ATTR_MASK 0x07
/* ---------- SCB tasks ---------- */
/* This is both ssp_task and long_ssp_task
@@ -511,7 +456,7 @@ struct abort_task {
u8 proto_conn_rate;
__le32 _r_a;
struct ssp_frame_hdr ssp_frame;
- struct ssp_task_iu ssp_task;
+ struct ssp_tmf_iu ssp_task;
__le16 sister_scb;
__le16 conn_handle;
u8 flags; /* ovrd_itnl_timer:3,3, suspend_data_trans:2,2 */
@@ -549,7 +494,7 @@ struct clear_nexus {
u8 _r_b[3];
u8 conn_mask;
u8 _r_c[19];
- struct ssp_task_iu ssp_task; /* LUN and TAG */
+ struct ssp_tmf_iu ssp_task; /* LUN and TAG */
__le16 _r_d;
__le16 conn_handle;
__le64 _r_e;
@@ -562,7 +507,7 @@ struct initiate_ssp_tmf {
u8 proto_conn_rate;
__le32 _r_a;
struct ssp_frame_hdr ssp_frame;
- struct ssp_task_iu ssp_task;
+ struct ssp_tmf_iu ssp_task;
__le16 sister_scb;
__le16 conn_handle;
u8 flags; /* itnl override and suspend data tx */
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index 29593275201a..08c8dad9ad62 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -1,27 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA driver SCB management.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/gfp.h>
@@ -86,7 +68,6 @@ static void asd_phy_event_tasklet(struct asd_ascb *ascb,
struct done_list_struct *dl)
{
struct asd_ha_struct *asd_ha = ascb->ha;
- struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
int phy_id = dl->status_block[0] & DL_PHY_MASK;
struct asd_phy *phy = &asd_ha->phys[phy_id];
@@ -99,7 +80,8 @@ static void asd_phy_event_tasklet(struct asd_ascb *ascb,
ASD_DPRINTK("phy%d: device unplugged\n", phy_id);
asd_turn_led(asd_ha, phy_id, 0);
sas_phy_disconnected(&phy->sas_phy);
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL,
+ GFP_ATOMIC);
break;
case CURRENT_OOB_DONE:
/* hot plugged device */
@@ -107,12 +89,13 @@ static void asd_phy_event_tasklet(struct asd_ascb *ascb,
get_lrate_mode(phy, oob_mode);
ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n",
phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto);
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE, GFP_ATOMIC);
break;
case CURRENT_SPINUP_HOLD:
/* hot plug SATA, no COMWAKE sent */
asd_turn_led(asd_ha, phy_id, 1);
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD,
+ GFP_ATOMIC);
break;
case CURRENT_GTO_TIMEOUT:
case CURRENT_OOB_ERROR:
@@ -120,7 +103,7 @@ static void asd_phy_event_tasklet(struct asd_ascb *ascb,
dl->status_block[1]);
asd_turn_led(asd_ha, phy_id, 0);
sas_phy_disconnected(&phy->sas_phy);
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR, GFP_ATOMIC);
break;
}
}
@@ -141,8 +124,8 @@ static unsigned ord_phy(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
/**
* asd_get_attached_sas_addr -- extract/generate attached SAS address
- * phy: pointer to asd_phy
- * sas_addr: pointer to buffer where the SAS address is to be written
+ * @phy: pointer to asd_phy
+ * @sas_addr: pointer to buffer where the SAS address is to be written
*
* This function extracts the SAS address from an IDENTIFY frame
* received. If OOB is SATA, then a SAS address is generated from the
@@ -240,7 +223,6 @@ static void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
int edb_el = edb_id + ascb->edb_index;
struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el];
struct asd_phy *phy = &ascb->ha->phys[phy_id];
- struct sas_ha_struct *sas_ha = phy->sas_phy.ha;
u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2];
size = min(size, (u16) sizeof(phy->frame_rcvd));
@@ -252,7 +234,7 @@ static void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
asd_dump_frame_rcvd(phy, dl);
asd_form_port(ascb->ha, phy);
- sas_ha->notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED);
+ sas_notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED, GFP_ATOMIC);
}
static void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
@@ -288,7 +270,7 @@ static void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
asd_turn_led(asd_ha, phy_id, 0);
sas_phy_disconnected(sas_phy);
asd_deform_port(asd_ha, phy);
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR, GFP_ATOMIC);
if (retries_left == 0) {
int num = 1;
@@ -333,7 +315,8 @@ static void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
sas_phy->sas_prim = ffs(cont);
spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
- sas_ha->notify_port_event(sas_phy,PORTE_BROADCAST_RCVD);
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
+ GFP_ATOMIC);
break;
case LmUNKNOWNP:
@@ -354,7 +337,8 @@ static void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
/* The sequencer disables all phys on that port.
* We have to re-enable the phys ourselves. */
asd_deform_port(asd_ha, phy);
- sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
+ sas_notify_port_event(sas_phy, PORTE_HARD_RESET,
+ GFP_ATOMIC);
break;
default:
@@ -585,7 +569,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
/* the device is gone */
sas_phy_disconnected(sas_phy);
asd_deform_port(asd_ha, phy);
- sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT);
+ sas_notify_port_event(sas_phy, PORTE_TIMER_EVENT, GFP_ATOMIC);
break;
default:
ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __func__,
@@ -724,9 +708,11 @@ static void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
switch (pd->max_sas_lrate) {
case SAS_LINK_RATE_6_0_GBPS:
*speed_mask &= ~SAS_SPEED_60_DIS;
+ fallthrough;
default:
case SAS_LINK_RATE_3_0_GBPS:
*speed_mask &= ~SAS_SPEED_30_DIS;
+ fallthrough;
case SAS_LINK_RATE_1_5_GBPS:
*speed_mask &= ~SAS_SPEED_15_DIS;
}
@@ -734,8 +720,10 @@ static void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
switch (pd->min_sas_lrate) {
case SAS_LINK_RATE_6_0_GBPS:
*speed_mask |= SAS_SPEED_30_DIS;
+ fallthrough;
case SAS_LINK_RATE_3_0_GBPS:
*speed_mask |= SAS_SPEED_15_DIS;
+ fallthrough;
default:
case SAS_LINK_RATE_1_5_GBPS:
/* nothing to do */
@@ -745,6 +733,7 @@ static void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
switch (pd->max_sata_lrate) {
case SAS_LINK_RATE_3_0_GBPS:
*speed_mask &= ~SATA_SPEED_30_DIS;
+ fallthrough;
default:
case SAS_LINK_RATE_1_5_GBPS:
*speed_mask &= ~SATA_SPEED_15_DIS;
@@ -753,6 +742,7 @@ static void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
switch (pd->min_sata_lrate) {
case SAS_LINK_RATE_3_0_GBPS:
*speed_mask |= SATA_SPEED_15_DIS;
+ fallthrough;
default:
case SAS_LINK_RATE_1_5_GBPS:
/* nothing to do */
@@ -803,6 +793,7 @@ void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
/* link reset retries, this should be nominal */
control_phy->link_reset_retries = 10;
+ fallthrough;
case RELEASE_SPINUP_HOLD: /* 0x02 */
/* decide the func_mask */
@@ -860,18 +851,18 @@ void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
/**
* asd_ascb_timedout -- called when a pending SCB's timer has expired
- * @data: unsigned long, a pointer to the ascb in question
+ * @t: Timer context used to fetch the SCB
*
* This is the default timeout function which does the most necessary.
* Upper layers can implement their own timeout function, say to free
* resources they have with this SCB, and then call this one at the
* end of their timeout function. To do this, one should initialize
- * the ascb->timer.{function, data, expires} prior to calling the post
+ * the ascb->timer.{function, expires} prior to calling the post
* function. The timer is started by the post function.
*/
-void asd_ascb_timedout(unsigned long data)
+void asd_ascb_timedout(struct timer_list *t)
{
- struct asd_ascb *ascb = (void *) data;
+ struct asd_ascb *ascb = timer_container_of(ascb, t, timer);
struct asd_seq_data *seq = &ascb->ha->seq;
unsigned long flags;
@@ -906,6 +897,7 @@ int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg)
switch (func) {
case PHY_FUNC_CLEAR_ERROR_LOG:
+ case PHY_FUNC_GET_EVENTS:
return -ENOSYS;
case PHY_FUNC_SET_LINK_RATE:
rates = arg;
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c
index edb43fda9f36..5def83c88f13 100644
--- a/drivers/scsi/aic94xx/aic94xx_sds.c
+++ b/drivers/scsi/aic94xx/aic94xx_sds.c
@@ -1,28 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA driver access to shared data structures and memory
* maps.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/pci.h>
@@ -424,7 +406,7 @@ struct asd_manuf_sec {
u8 sas_addr[SAS_ADDR_SIZE];
u8 pcba_sn[ASD_PCBA_SN_SIZE];
/* Here start the other segments */
- u8 linked_list[0];
+ u8 linked_list[];
} __attribute__ ((packed));
struct asd_manuf_phy_desc {
@@ -467,7 +449,7 @@ struct asd_ms_sb_desc {
u8 type;
u8 node_desc_index;
u8 conn_desc_index;
- u8 _recvd[0];
+ u8 _recvd[];
} __attribute__ ((packed));
#if 0
@@ -496,12 +478,12 @@ struct asd_ms_conn_desc {
u8 size_sideband_desc;
u32 _resvd;
u8 name[16];
- struct asd_ms_sb_desc sb_desc[0];
+ struct asd_ms_sb_desc sb_desc[];
} __attribute__ ((packed));
struct asd_nd_phy_desc {
u8 vp_attch_type;
- u8 attch_specific[0];
+ u8 attch_specific[];
} __attribute__ ((packed));
#if 0
@@ -521,7 +503,7 @@ struct asd_ms_node_desc {
u8 size_phy_desc;
u8 _resvd;
u8 name[16];
- struct asd_nd_phy_desc phy_desc[0];
+ struct asd_nd_phy_desc phy_desc[];
} __attribute__ ((packed));
struct asd_ms_conn_map {
@@ -535,8 +517,10 @@ struct asd_ms_conn_map {
u8 num_nodes;
u8 usage_model_id;
u32 _resvd;
- struct asd_ms_conn_desc conn_desc[0];
- struct asd_ms_node_desc node_desc[0];
+ union {
+ DECLARE_FLEX_ARRAY(struct asd_ms_conn_desc, conn_desc);
+ DECLARE_FLEX_ARRAY(struct asd_ms_node_desc, node_desc);
+ };
} __attribute__ ((packed));
struct asd_ctrla_phy_entry {
@@ -560,7 +544,7 @@ struct asd_ll_el {
u8 id0;
u8 id1;
__le16 next;
- u8 something_here[0];
+ u8 something_here[];
} __attribute__ ((packed));
static int asd_poll_flash(struct asd_ha_struct *asd_ha)
@@ -736,10 +720,12 @@ static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1)
do {
switch (id1) {
default:
- if (el->id1 == id1)
+ if (el->id1 == id1) {
+ fallthrough;
case 0xFF:
if (el->id0 == id0)
return el;
+ }
}
el = start + le16_to_cpu(el->next);
} while (el != start);
@@ -983,7 +969,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
{
int err, i;
u32 offs, size;
- struct asd_ll_el *el;
+ struct asd_ll_el *el = NULL;
struct asd_ctrla_phy_settings *ps;
struct asd_ctrla_phy_settings dflt_ps;
@@ -1004,6 +990,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
size = sizeof(struct asd_ctrla_phy_settings);
ps = &dflt_ps;
+ goto out_process;
}
if (size == 0)
@@ -1028,7 +1015,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
ASD_DPRINTK("couldn't find ctrla phy settings struct\n");
goto out2;
}
-
+out_process:
err = asd_process_ctrla_phy_settings(asd_ha, ps);
if (err) {
ASD_DPRINTK("couldn't process ctrla phy settings\n");
@@ -1261,7 +1248,7 @@ int asd_chk_write_status(struct asd_ha_struct *asd_ha,
}
/**
- * asd_hwi_erase_nv_sector - Erase the flash memory sectors.
+ * asd_erase_nv_sector - Erase the flash memory sectors.
* @asd_ha: pointer to the host adapter structure
* @flash_addr: pointer to offset from flash memory
* @size: total bytes to erase.
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.h b/drivers/scsi/aic94xx/aic94xx_sds.h
index a06dc0114b8c..80f3c4782725 100644
--- a/drivers/scsi/aic94xx/aic94xx_sds.h
+++ b/drivers/scsi/aic94xx/aic94xx_sds.h
@@ -1,27 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver hardware interface header file.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Gilbert Wu <gilbert_wu@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef _AIC94XX_SDS_H_
#define _AIC94XX_SDS_H_
diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c
index 390168f62a13..c0f685c86851 100644
--- a/drivers/scsi/aic94xx/aic94xx_seq.c
+++ b/drivers/scsi/aic94xx/aic94xx_seq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA driver sequencer interface.
*
@@ -5,25 +6,6 @@
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
*
* Parts of this code adapted from David Chaw's adp94xx_seq.c.
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/delay.h>
@@ -600,6 +582,7 @@ static void asd_init_cseq_scratch(struct asd_ha_struct *asd_ha)
/**
* asd_init_lseq_mip -- initialize LSEQ Mode independent pages 0-3
* @asd_ha: pointer to host adapter structure
+ * @lseq: link sequencer
*/
static void asd_init_lseq_mip(struct asd_ha_struct *asd_ha, u8 lseq)
{
@@ -687,6 +670,7 @@ static void asd_init_lseq_mip(struct asd_ha_struct *asd_ha, u8 lseq)
/**
* asd_init_lseq_mdp -- initialize LSEQ mode dependent pages.
* @asd_ha: pointer to host adapter structure
+ * @lseq: link sequencer
*/
static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha, int lseq)
{
@@ -971,6 +955,7 @@ static void asd_init_cseq_cio(struct asd_ha_struct *asd_ha)
/**
* asd_init_lseq_cio -- initialize LmSEQ CIO registers
* @asd_ha: pointer to host adapter structure
+ * @lseq: link sequencer
*/
static void asd_init_lseq_cio(struct asd_ha_struct *asd_ha, int lseq)
{
@@ -1228,8 +1213,7 @@ static int asd_seq_start_lseq(struct asd_ha_struct *asd_ha, int lseq)
int asd_release_firmware(void)
{
- if (sequencer_fw)
- release_firmware(sequencer_fw);
+ release_firmware(sequencer_fw);
return 0;
}
@@ -1353,7 +1337,7 @@ int asd_start_seqs(struct asd_ha_struct *asd_ha)
for_each_sequencer(lseq_mask, lseq_mask, lseq) {
err = asd_seq_start_lseq(asd_ha, lseq);
if (err) {
- asd_printk("coudln't start LSEQ %d for %s\n", lseq,
+ asd_printk("couldn't start LSEQ %d for %s\n", lseq,
pci_name(asd_ha->pcidev));
return err;
}
@@ -1364,7 +1348,8 @@ int asd_start_seqs(struct asd_ha_struct *asd_ha)
/**
* asd_update_port_links -- update port_map_by_links and phy_is_up
- * @sas_phy: pointer to the phy which has been added to a port
+ * @asd_ha: pointer to host adapter structure
+ * @phy: pointer to the phy which has been added to a port
*
* 1) When a link reset has completed and we got BYTES DMAED with a
* valid frame we call this function for that phy, to indicate that
diff --git a/drivers/scsi/aic94xx/aic94xx_seq.h b/drivers/scsi/aic94xx/aic94xx_seq.h
index ad787c55525f..5bf9b8ae6a87 100644
--- a/drivers/scsi/aic94xx/aic94xx_seq.h
+++ b/drivers/scsi/aic94xx/aic94xx_seq.h
@@ -1,27 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Aic94xx SAS/SATA driver sequencer interface header file.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef _AIC94XX_SEQ_H_
diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c
index 532d212b6b2c..b26a468ddc98 100644
--- a/drivers/scsi/aic94xx/aic94xx_task.c
+++ b/drivers/scsi/aic94xx/aic94xx_task.c
@@ -1,27 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx SAS/SATA Tasks
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/spinlock.h>
@@ -42,13 +24,13 @@ static void asd_can_dequeue(struct asd_ha_struct *asd_ha, int num)
spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
}
-/* PCI_DMA_... to our direction translation.
+/* DMA_... to our direction translation.
*/
static const u8 data_dir_flags[] = {
- [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT, /* UNSPECIFIED */
- [PCI_DMA_TODEVICE] = DATA_DIR_OUT, /* OUTBOUND */
- [PCI_DMA_FROMDEVICE] = DATA_DIR_IN, /* INBOUND */
- [PCI_DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */
+ [DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT, /* UNSPECIFIED */
+ [DMA_TO_DEVICE] = DATA_DIR_OUT, /* OUTBOUND */
+ [DMA_FROM_DEVICE] = DATA_DIR_IN, /* INBOUND */
+ [DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */
};
static int asd_map_scatterlist(struct sas_task *task,
@@ -60,14 +42,17 @@ static int asd_map_scatterlist(struct sas_task *task,
struct scatterlist *sc;
int num_sg, res;
- if (task->data_dir == PCI_DMA_NONE)
+ if (task->data_dir == DMA_NONE)
return 0;
if (task->num_scatter == 0) {
void *p = task->scatter;
- dma_addr_t dma = pci_map_single(asd_ha->pcidev, p,
+ dma_addr_t dma = dma_map_single(&asd_ha->pcidev->dev, p,
task->total_xfer_len,
task->data_dir);
+ if (dma_mapping_error(&asd_ha->pcidev->dev, dma))
+ return -ENOMEM;
+
sg_arr[0].bus_addr = cpu_to_le64((u64)dma);
sg_arr[0].size = cpu_to_le32(task->total_xfer_len);
sg_arr[0].flags |= ASD_SG_EL_LIST_EOL;
@@ -79,7 +64,7 @@ static int asd_map_scatterlist(struct sas_task *task,
if (sas_protocol_ata(task->task_proto))
num_sg = task->num_scatter;
else
- num_sg = pci_map_sg(asd_ha->pcidev, task->scatter,
+ num_sg = dma_map_sg(&asd_ha->pcidev->dev, task->scatter,
task->num_scatter, task->data_dir);
if (num_sg == 0)
return -ENOMEM;
@@ -126,8 +111,8 @@ static int asd_map_scatterlist(struct sas_task *task,
return 0;
err_unmap:
if (sas_protocol_ata(task->task_proto))
- pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
- task->data_dir);
+ dma_unmap_sg(&asd_ha->pcidev->dev, task->scatter,
+ task->num_scatter, task->data_dir);
return res;
}
@@ -136,21 +121,21 @@ static void asd_unmap_scatterlist(struct asd_ascb *ascb)
struct asd_ha_struct *asd_ha = ascb->ha;
struct sas_task *task = ascb->uldd_task;
- if (task->data_dir == PCI_DMA_NONE)
+ if (task->data_dir == DMA_NONE)
return;
if (task->num_scatter == 0) {
dma_addr_t dma = (dma_addr_t)
le64_to_cpu(ascb->scb->ssp_task.sg_element[0].bus_addr);
- pci_unmap_single(ascb->ha->pcidev, dma, task->total_xfer_len,
- task->data_dir);
+ dma_unmap_single(&ascb->ha->pcidev->dev, dma,
+ task->total_xfer_len, task->data_dir);
return;
}
asd_free_coherent(asd_ha, ascb->sg_arr);
if (task->task_proto != SAS_PROTOCOL_STP)
- pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
- task->data_dir);
+ dma_unmap_sg(&asd_ha->pcidev->dev, task->scatter,
+ task->num_scatter, task->data_dir);
}
/* ---------- Task complete tasklet ---------- */
@@ -201,7 +186,7 @@ static void asd_get_response_tasklet(struct asd_ascb *ascb,
if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
resp->frame_len = le16_to_cpu(*(__le16 *)(r+6));
- memcpy(&resp->ending_fis[0], r+16, 24);
+ memcpy(&resp->ending_fis[0], r+16, ATA_RESP_FIS_SIZE);
ts->buf_valid_size = sizeof(*resp);
}
}
@@ -223,7 +208,7 @@ Again:
switch (opcode) {
case TC_NO_ERROR:
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_GOOD;
+ ts->stat = SAS_SAM_STAT_GOOD;
break;
case TC_UNDERRUN:
ts->resp = SAS_TASK_COMPLETE;
@@ -287,7 +272,6 @@ Again:
case TA_I_T_NEXUS_LOSS:
opcode = dl->status_block[0];
goto Again;
- break;
case TF_INV_CONN_HANDLE:
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_DEVICE_UNKNOWN;
@@ -334,13 +318,13 @@ Again:
break;
case SAS_PROTOCOL_SSP:
asd_unbuild_ssp_ascb(ascb);
+ break;
default:
break;
}
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
- task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
task->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) {
struct completion *completion = ascb->completion;
@@ -373,10 +357,10 @@ static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
if (unlikely(task->ata_task.device_control_reg_update))
scb->header.opcode = CONTROL_ATA_DEV;
- else if (dev->sata_dev.command_set == ATA_COMMAND_SET)
- scb->header.opcode = INITIATE_ATA_TASK;
- else
+ else if (dev->sata_dev.class == ATA_DEV_ATAPI)
scb->header.opcode = INITIATE_ATAPI_TASK;
+ else
+ scb->header.opcode = INITIATE_ATA_TASK;
scb->ata_task.proto_conn_rate = (1 << 5); /* STP */
if (dev->port->oob_mode == SAS_OOB_MODE)
@@ -387,7 +371,7 @@ static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
if (likely(!task->ata_task.device_control_reg_update))
scb->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
scb->ata_task.fis.flags &= 0xF0; /* PM_PORT field shall be 0 */
- if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
+ if (dev->sata_dev.class == ATA_DEV_ATAPI)
memcpy(scb->ata_task.atapi_packet, task->ata_task.atapi_packet,
16);
scb->ata_task.sister_scb = cpu_to_le16(0xFFFF);
@@ -399,19 +383,14 @@ static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
if (task->ata_task.dma_xfer)
flags |= DATA_XFER_MODE_DMA;
if (task->ata_task.use_ncq &&
- dev->sata_dev.command_set != ATAPI_COMMAND_SET)
+ dev->sata_dev.class != ATA_DEV_ATAPI)
flags |= ATA_Q_TYPE_NCQ;
flags |= data_dir_flags[task->data_dir];
scb->ata_task.ata_flags = flags;
- scb->ata_task.retry_count = task->ata_task.retry_count;
+ scb->ata_task.retry_count = 0;
- flags = 0;
- if (task->ata_task.set_affil_pol)
- flags |= SET_AFFIL_POLICY;
- if (task->ata_task.stp_affil_pol)
- flags |= STP_AFFIL_POLICY;
- scb->ata_task.flags = flags;
+ scb->ata_task.flags = 0;
}
ascb->tasklet_complete = asd_task_tasklet_complete;
@@ -436,10 +415,10 @@ static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task,
struct domain_device *dev = task->dev;
struct scb *scb;
- pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
- pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
+ dma_map_sg(&asd_ha->pcidev->dev, &task->smp_task.smp_req, 1,
+ DMA_TO_DEVICE);
+ dma_map_sg(&asd_ha->pcidev->dev, &task->smp_task.smp_resp, 1,
+ DMA_FROM_DEVICE);
scb = ascb->scb;
@@ -471,10 +450,10 @@ static void asd_unbuild_smp_ascb(struct asd_ascb *a)
struct sas_task *task = a->uldd_task;
BUG_ON(!task);
- pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
- pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
+ dma_unmap_sg(&a->ha->pcidev->dev, &task->smp_task.smp_req, 1,
+ DMA_TO_DEVICE);
+ dma_unmap_sg(&a->ha->pcidev->dev, &task->smp_task.smp_resp, 1,
+ DMA_FROM_DEVICE);
}
/* ---------- SSP ---------- */
@@ -501,17 +480,14 @@ static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task,
scb->ssp_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
memcpy(scb->ssp_task.ssp_cmd.lun, task->ssp_task.LUN, 8);
- if (task->ssp_task.enable_first_burst)
- scb->ssp_task.ssp_cmd.efb_prio_attr |= EFB_MASK;
- scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_prio << 3);
scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_attr & 7);
- memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cdb, 16);
+ memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
scb->ssp_task.sister_scb = cpu_to_le16(0xFFFF);
scb->ssp_task.conn_handle = cpu_to_le16(
(u16)(unsigned long)dev->lldd_dev);
scb->ssp_task.data_dir = data_dir_flags[task->data_dir];
- scb->ssp_task.retry_count = scb->ssp_task.retry_count;
ascb->tasklet_complete = asd_task_tasklet_complete;
@@ -542,21 +518,19 @@ static int asd_can_queue(struct asd_ha_struct *asd_ha, int num)
return res;
}
-int asd_execute_task(struct sas_task *task, const int num,
- gfp_t gfp_flags)
+int asd_execute_task(struct sas_task *task, gfp_t gfp_flags)
{
int res = 0;
LIST_HEAD(alist);
struct sas_task *t = task;
struct asd_ascb *ascb = NULL, *a;
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
- unsigned long flags;
- res = asd_can_queue(asd_ha, num);
+ res = asd_can_queue(asd_ha, 1);
if (res)
return res;
- res = num;
+ res = 1;
ascb = asd_ascb_alloc_list(asd_ha, &res, gfp_flags);
if (res) {
res = -ENOMEM;
@@ -567,7 +541,7 @@ int asd_execute_task(struct sas_task *task, const int num,
list_for_each_entry(a, &alist, list) {
a->uldd_task = t;
t->lldd_task = a;
- t = list_entry(t->list.next, struct sas_task, list);
+ break;
}
list_for_each_entry(a, &alist, list) {
t = a->uldd_task;
@@ -593,14 +567,10 @@ int asd_execute_task(struct sas_task *task, const int num,
}
if (res)
goto out_err_unmap;
-
- spin_lock_irqsave(&t->task_state_lock, flags);
- t->task_state_flags |= SAS_TASK_AT_INITIATOR;
- spin_unlock_irqrestore(&t->task_state_lock, flags);
}
list_del_init(&alist);
- res = asd_post_ascb_list(asd_ha, ascb, num);
+ res = asd_post_ascb_list(asd_ha, ascb, 1);
if (unlikely(res)) {
a = NULL;
__list_add(&alist, ascb->list.prev, &ascb->list);
@@ -615,9 +585,6 @@ out_err_unmap:
if (a == b)
break;
t = a->uldd_task;
- spin_lock_irqsave(&t->task_state_lock, flags);
- t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
- spin_unlock_irqrestore(&t->task_state_lock, flags);
switch (t->task_proto) {
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
@@ -628,6 +595,7 @@ out_err_unmap:
break;
case SAS_PROTOCOL_SSP:
asd_unbuild_ssp_ascb(a);
+ break;
default:
break;
}
@@ -638,6 +606,6 @@ out_err_unmap:
out_err:
if (ascb)
asd_ascb_free_list(ascb);
- asd_can_dequeue(asd_ha, num);
+ asd_can_dequeue(asd_ha, 1);
return res;
}
diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c
index 0add73bdf2a4..28ac92b041fe 100644
--- a/drivers/scsi/aic94xx/aic94xx_tmf.c
+++ b/drivers/scsi/aic94xx/aic94xx_tmf.c
@@ -1,27 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aic94xx Task Management Functions
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This file is part of the aic94xx driver.
- *
- * The aic94xx driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * The aic94xx driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the aic94xx driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/spinlock.h>
@@ -35,14 +17,13 @@
static int asd_enqueue_internal(struct asd_ascb *ascb,
void (*tasklet_complete)(struct asd_ascb *,
struct done_list_struct *),
- void (*timed_out)(unsigned long))
+ void (*timed_out)(struct timer_list *t))
{
int res;
ascb->tasklet_complete = tasklet_complete;
ascb->uldd_timer = 1;
- ascb->timer.data = (unsigned long) ascb;
ascb->timer.function = timed_out;
ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
@@ -50,7 +31,7 @@ static int asd_enqueue_internal(struct asd_ascb *ascb,
res = asd_post_ascb_list(ascb->ha, ascb, 1);
if (unlikely(res))
- del_timer(&ascb->timer);
+ timer_delete(&ascb->timer);
return res;
}
@@ -77,7 +58,7 @@ static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
{
struct tasklet_completion_status *tcs = ascb->uldd_task;
ASD_DPRINTK("%s: here\n", __func__);
- if (!del_timer(&ascb->timer)) {
+ if (!timer_delete(&ascb->timer)) {
ASD_DPRINTK("%s: couldn't delete timer\n", __func__);
return;
}
@@ -87,9 +68,9 @@ static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
asd_ascb_free(ascb);
}
-static void asd_clear_nexus_timedout(unsigned long data)
+static void asd_clear_nexus_timedout(struct timer_list *t)
{
- struct asd_ascb *ascb = (void *)data;
+ struct asd_ascb *ascb = timer_container_of(ascb, t, timer);
struct tasklet_completion_status *tcs = ascb->uldd_task;
ASD_DPRINTK("%s: here\n", __func__);
@@ -181,10 +162,10 @@ static int asd_clear_nexus_I_T(struct domain_device *dev,
int asd_I_T_nexus_reset(struct domain_device *dev)
{
int res, tmp_res, i;
- struct sas_phy *phy = sas_find_local_phy(dev);
+ struct sas_phy *phy = sas_get_local_phy(dev);
/* Standard mandates link reset for ATA (type 0) and
* hard reset for SSP (type 1) */
- int reset_type = (dev->dev_type == SATA_DEV ||
+ int reset_type = (dev->dev_type == SAS_SATA_DEV ||
(dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE);
@@ -192,7 +173,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev)
ASD_DPRINTK("sending %s reset to %s\n",
reset_type ? "hard" : "soft", dev_name(&phy->dev));
res = sas_phy_reset(phy, reset_type);
- if (res == TMF_RESP_FUNC_COMPLETE) {
+ if (res == TMF_RESP_FUNC_COMPLETE || res == -ENODEV) {
/* wait for the maximum settle time */
msleep(500);
/* clear all outstanding commands (keep nexus suspended) */
@@ -201,7 +182,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev)
for (i = 0 ; i < 3; i++) {
tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME);
if (tmp_res == TC_RESUME)
- return res;
+ goto out;
msleep(500);
}
@@ -211,7 +192,10 @@ int asd_I_T_nexus_reset(struct domain_device *dev)
dev_printk(KERN_ERR, &phy->dev,
"Failed to resume nexus after reset 0x%x\n", tmp_res);
- return TMF_RESP_FUNC_FAILED;
+ res = TMF_RESP_FUNC_FAILED;
+ out:
+ sas_put_local_phy(phy);
+ return res;
}
static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
@@ -258,9 +242,9 @@ static int asd_clear_nexus_index(struct sas_task *task)
/* ---------- TMFs ---------- */
-static void asd_tmf_timedout(unsigned long data)
+static void asd_tmf_timedout(struct timer_list *t)
{
- struct asd_ascb *ascb = (void *) data;
+ struct asd_ascb *ascb = timer_container_of(ascb, t, timer);
struct tasklet_completion_status *tcs = ascb->uldd_task;
ASD_DPRINTK("tmf timed out\n");
@@ -303,7 +287,7 @@ static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
fh = edb->vaddr + 16;
ru = edb->vaddr + 16 + sizeof(*fh);
res = ru->status;
- if (ru->datapres == 1) /* Response data present */
+ if (ru->datapres == SAS_DATAPRES_RESPONSE_DATA)
res = ru->resp_data[3];
#if 0
ascb->tag = fh->tag;
@@ -319,7 +303,7 @@ static void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
{
struct tasklet_completion_status *tcs;
- if (!del_timer(&ascb->timer))
+ if (!timer_delete(&ascb->timer))
return;
tcs = ascb->uldd_task;
@@ -506,7 +490,7 @@ int asd_abort_task(struct sas_task *task)
switch (tcs.dl_opcode) {
default:
res = asd_clear_nexus(task);
- /* fallthrough */
+ fallthrough;
case TC_NO_ERROR:
break;
/* The task hasn't been sent to the device xor
@@ -660,15 +644,6 @@ int asd_abort_task_set(struct domain_device *dev, u8 *lun)
return res;
}
-int asd_clear_aca(struct domain_device *dev, u8 *lun)
-{
- int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_ACA, 0);
-
- if (res == TMF_RESP_FUNC_COMPLETE)
- asd_clear_nexus_I_T_L(dev, lun);
- return res;
-}
-
int asd_clear_task_set(struct domain_device *dev, u8 *lun)
{
int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_TASK_SET, 0);
@@ -689,7 +664,7 @@ int asd_lu_reset(struct domain_device *dev, u8 *lun)
/**
* asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus
- * task: pointer to sas_task struct of interest
+ * @task: pointer to sas_task struct of interest
*
* Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set,
* or TMF_RESP_FUNC_SUCC if the task is in the task set.
diff --git a/drivers/scsi/am53c974.c b/drivers/scsi/am53c974.c
new file mode 100644
index 000000000000..003e61831e33
--- /dev/null
+++ b/drivers/scsi/am53c974.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD am53c974 driver.
+ * Copyright (c) 2014 Hannes Reinecke, SUSE Linux GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi_host.h>
+
+#include "esp_scsi.h"
+
+#define DRV_MODULE_NAME "am53c974"
+#define DRV_MODULE_VERSION "1.00"
+
+static bool am53c974_debug;
+static bool am53c974_fenab = true;
+
+#define esp_dma_log(f, a...) \
+ do { \
+ if (am53c974_debug) \
+ shost_printk(KERN_DEBUG, esp->host, f, ##a); \
+ } while (0)
+
+#define ESP_DMA_CMD 0x10
+#define ESP_DMA_STC 0x11
+#define ESP_DMA_SPA 0x12
+#define ESP_DMA_WBC 0x13
+#define ESP_DMA_WAC 0x14
+#define ESP_DMA_STATUS 0x15
+#define ESP_DMA_SMDLA 0x16
+#define ESP_DMA_WMAC 0x17
+
+#define ESP_DMA_CMD_IDLE 0x00
+#define ESP_DMA_CMD_BLAST 0x01
+#define ESP_DMA_CMD_ABORT 0x02
+#define ESP_DMA_CMD_START 0x03
+#define ESP_DMA_CMD_MASK 0x03
+#define ESP_DMA_CMD_DIAG 0x04
+#define ESP_DMA_CMD_MDL 0x10
+#define ESP_DMA_CMD_INTE_P 0x20
+#define ESP_DMA_CMD_INTE_D 0x40
+#define ESP_DMA_CMD_DIR 0x80
+
+#define ESP_DMA_STAT_PWDN 0x01
+#define ESP_DMA_STAT_ERROR 0x02
+#define ESP_DMA_STAT_ABORT 0x04
+#define ESP_DMA_STAT_DONE 0x08
+#define ESP_DMA_STAT_SCSIINT 0x10
+#define ESP_DMA_STAT_BCMPLT 0x20
+
+/* EEPROM is accessed with 16-bit values */
+#define DC390_EEPROM_READ 0x80
+#define DC390_EEPROM_LEN 0x40
+
+/*
+ * DC390 EEPROM
+ *
+ * 8 * 4 bytes of per-device options
+ * followed by HBA specific options
+ */
+
+/* Per-device options */
+#define DC390_EE_MODE1 0x00
+#define DC390_EE_SPEED 0x01
+
+/* HBA-specific options */
+#define DC390_EE_ADAPT_SCSI_ID 0x40
+#define DC390_EE_MODE2 0x41
+#define DC390_EE_DELAY 0x42
+#define DC390_EE_TAG_CMD_NUM 0x43
+
+#define DC390_EE_MODE1_PARITY_CHK 0x01
+#define DC390_EE_MODE1_SYNC_NEGO 0x02
+#define DC390_EE_MODE1_EN_DISC 0x04
+#define DC390_EE_MODE1_SEND_START 0x08
+#define DC390_EE_MODE1_TCQ 0x10
+
+#define DC390_EE_MODE2_MORE_2DRV 0x01
+#define DC390_EE_MODE2_GREATER_1G 0x02
+#define DC390_EE_MODE2_RST_SCSI_BUS 0x04
+#define DC390_EE_MODE2_ACTIVE_NEGATION 0x08
+#define DC390_EE_MODE2_NO_SEEK 0x10
+#define DC390_EE_MODE2_LUN_CHECK 0x20
+
+struct pci_esp_priv {
+ struct esp *esp;
+ u8 dma_status;
+};
+
+static void pci_esp_dma_drain(struct esp *esp);
+
+static inline struct pci_esp_priv *pci_esp_get_priv(struct esp *esp)
+{
+ return dev_get_drvdata(esp->dev);
+}
+
+static void pci_esp_write8(struct esp *esp, u8 val, unsigned long reg)
+{
+ iowrite8(val, esp->regs + (reg * 4UL));
+}
+
+static u8 pci_esp_read8(struct esp *esp, unsigned long reg)
+{
+ return ioread8(esp->regs + (reg * 4UL));
+}
+
+static void pci_esp_write32(struct esp *esp, u32 val, unsigned long reg)
+{
+ return iowrite32(val, esp->regs + (reg * 4UL));
+}
+
+static int pci_esp_irq_pending(struct esp *esp)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+
+ pep->dma_status = pci_esp_read8(esp, ESP_DMA_STATUS);
+ esp_dma_log("dma intr dreg[%02x]\n", pep->dma_status);
+
+ if (pep->dma_status & (ESP_DMA_STAT_ERROR |
+ ESP_DMA_STAT_ABORT |
+ ESP_DMA_STAT_DONE |
+ ESP_DMA_STAT_SCSIINT))
+ return 1;
+
+ return 0;
+}
+
+static void pci_esp_reset_dma(struct esp *esp)
+{
+ /* Nothing to do ? */
+}
+
+static void pci_esp_dma_drain(struct esp *esp)
+{
+ u8 resid;
+ int lim = 1000;
+
+
+ if ((esp->sreg & ESP_STAT_PMASK) == ESP_DOP ||
+ (esp->sreg & ESP_STAT_PMASK) == ESP_DIP)
+ /* Data-In or Data-Out, nothing to be done */
+ return;
+
+ while (--lim > 0) {
+ resid = pci_esp_read8(esp, ESP_FFLAGS) & ESP_FF_FBYTES;
+ if (resid <= 1)
+ break;
+ cpu_relax();
+ }
+
+ /*
+ * When there is a residual BCMPLT will never be set
+ * (obviously). But we still have to issue the BLAST
+ * command, otherwise the data will not being transferred.
+ * But we'll never know when the BLAST operation is
+ * finished. So check for some time and give up eventually.
+ */
+ lim = 1000;
+ pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_BLAST, ESP_DMA_CMD);
+ while (pci_esp_read8(esp, ESP_DMA_STATUS) & ESP_DMA_STAT_BCMPLT) {
+ if (--lim == 0)
+ break;
+ cpu_relax();
+ }
+ pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
+ esp_dma_log("DMA blast done (%d tries, %d bytes left)\n", lim, resid);
+ /* BLAST residual handling is currently untested */
+ if (WARN_ON_ONCE(resid == 1)) {
+ struct esp_cmd_entry *ent = esp->active_cmd;
+
+ ent->flags |= ESP_CMD_FLAG_RESIDUAL;
+ }
+}
+
+static void pci_esp_dma_invalidate(struct esp *esp)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+
+ esp_dma_log("invalidate DMA\n");
+
+ pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
+ pep->dma_status = 0;
+}
+
+static int pci_esp_dma_error(struct esp *esp)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+
+ if (pep->dma_status & ESP_DMA_STAT_ERROR) {
+ u8 dma_cmd = pci_esp_read8(esp, ESP_DMA_CMD);
+
+ if ((dma_cmd & ESP_DMA_CMD_MASK) == ESP_DMA_CMD_START)
+ pci_esp_write8(esp, ESP_DMA_CMD_ABORT, ESP_DMA_CMD);
+
+ return 1;
+ }
+ if (pep->dma_status & ESP_DMA_STAT_ABORT) {
+ pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
+ pep->dma_status = pci_esp_read8(esp, ESP_DMA_CMD);
+ return 1;
+ }
+ return 0;
+}
+
+static void pci_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
+ u32 dma_count, int write, u8 cmd)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+ u32 val = 0;
+
+ BUG_ON(!(cmd & ESP_CMD_DMA));
+
+ pep->dma_status = 0;
+
+ /* Set DMA engine to IDLE */
+ if (write)
+ /* DMA write direction logic is inverted */
+ val |= ESP_DMA_CMD_DIR;
+ pci_esp_write8(esp, ESP_DMA_CMD_IDLE | val, ESP_DMA_CMD);
+
+ pci_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
+ pci_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
+ if (esp->config2 & ESP_CONFIG2_FENAB)
+ pci_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
+
+ pci_esp_write32(esp, esp_count, ESP_DMA_STC);
+ pci_esp_write32(esp, addr, ESP_DMA_SPA);
+
+ esp_dma_log("start dma addr[%x] count[%d:%d]\n",
+ addr, esp_count, dma_count);
+
+ scsi_esp_cmd(esp, cmd);
+ /* Send DMA Start command */
+ pci_esp_write8(esp, ESP_DMA_CMD_START | val, ESP_DMA_CMD);
+}
+
+static u32 pci_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
+{
+ int dma_limit = 16;
+ u32 base, end;
+
+ /*
+ * If CONFIG2_FENAB is set we can
+ * handle up to 24 bit addresses
+ */
+ if (esp->config2 & ESP_CONFIG2_FENAB)
+ dma_limit = 24;
+
+ if (dma_len > (1U << dma_limit))
+ dma_len = (1U << dma_limit);
+
+ /*
+ * Prevent crossing a 24-bit address boundary.
+ */
+ base = dma_addr & ((1U << 24) - 1U);
+ end = base + dma_len;
+ if (end > (1U << 24))
+ end = (1U <<24);
+ dma_len = end - base;
+
+ return dma_len;
+}
+
+static const struct esp_driver_ops pci_esp_ops = {
+ .esp_write8 = pci_esp_write8,
+ .esp_read8 = pci_esp_read8,
+ .irq_pending = pci_esp_irq_pending,
+ .reset_dma = pci_esp_reset_dma,
+ .dma_drain = pci_esp_dma_drain,
+ .dma_invalidate = pci_esp_dma_invalidate,
+ .send_dma_cmd = pci_esp_send_dma_cmd,
+ .dma_error = pci_esp_dma_error,
+ .dma_length_limit = pci_esp_dma_length_limit,
+};
+
+/*
+ * Read DC-390 eeprom
+ */
+static void dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd)
+{
+ u8 carry_flag = 1, j = 0x80, bval;
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ if (carry_flag) {
+ pci_write_config_byte(pdev, 0x80, 0x40);
+ bval = 0xc0;
+ } else
+ bval = 0x80;
+
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, bval);
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, 0);
+ udelay(160);
+
+ carry_flag = (cmd & j) ? 1 : 0;
+ j >>= 1;
+ }
+}
+
+static u16 dc390_eeprom_get_data(struct pci_dev *pdev)
+{
+ int i;
+ u16 wval = 0;
+ u8 bval;
+
+ for (i = 0; i < 16; i++) {
+ wval <<= 1;
+
+ pci_write_config_byte(pdev, 0x80, 0x80);
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, 0x40);
+ udelay(160);
+ pci_read_config_byte(pdev, 0x00, &bval);
+
+ if (bval == 0x22)
+ wval |= 1;
+ }
+
+ return wval;
+}
+
+static void dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr)
+{
+ u8 cmd = DC390_EEPROM_READ, i;
+
+ for (i = 0; i < DC390_EEPROM_LEN; i++) {
+ pci_write_config_byte(pdev, 0xc0, 0);
+ udelay(160);
+
+ dc390_eeprom_prepare_read(pdev, cmd++);
+ *ptr++ = dc390_eeprom_get_data(pdev);
+
+ pci_write_config_byte(pdev, 0x80, 0);
+ pci_write_config_byte(pdev, 0x80, 0);
+ udelay(160);
+ }
+}
+
+static void dc390_check_eeprom(struct esp *esp)
+{
+ struct pci_dev *pdev = to_pci_dev(esp->dev);
+ u8 EEbuf[128];
+ u16 *ptr = (u16 *)EEbuf, wval = 0;
+ int i;
+
+ dc390_read_eeprom(pdev, ptr);
+
+ for (i = 0; i < DC390_EEPROM_LEN; i++, ptr++)
+ wval += *ptr;
+
+ /* no Tekram EEprom found */
+ if (wval != 0x1234) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "No valid Tekram EEprom found\n");
+ return;
+ }
+ esp->scsi_id = EEbuf[DC390_EE_ADAPT_SCSI_ID];
+ esp->num_tags = 2 << EEbuf[DC390_EE_TAG_CMD_NUM];
+ if (EEbuf[DC390_EE_MODE2] & DC390_EE_MODE2_ACTIVE_NEGATION)
+ esp->config4 |= ESP_CONFIG4_RADE | ESP_CONFIG4_RAE;
+}
+
+static int pci_esp_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ const struct scsi_host_template *hostt = &scsi_esp_template;
+ int err = -ENODEV;
+ struct Scsi_Host *shost;
+ struct esp *esp;
+ struct pci_esp_priv *pep;
+
+ if (pci_enable_device(pdev)) {
+ dev_printk(KERN_INFO, &pdev->dev, "cannot enable device\n");
+ return -ENODEV;
+ }
+
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "failed to set 32bit DMA mask\n");
+ goto fail_disable_device;
+ }
+
+ shost = scsi_host_alloc(hostt, sizeof(struct esp));
+ if (!shost) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "failed to allocate scsi host\n");
+ err = -ENOMEM;
+ goto fail_disable_device;
+ }
+
+ pep = kzalloc(sizeof(struct pci_esp_priv), GFP_KERNEL);
+ if (!pep) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "failed to allocate esp_priv\n");
+ err = -ENOMEM;
+ goto fail_host_alloc;
+ }
+
+ esp = shost_priv(shost);
+ esp->host = shost;
+ esp->dev = &pdev->dev;
+ esp->ops = &pci_esp_ops;
+ /*
+ * The am53c974 HBA has a design flaw of generating
+ * spurious DMA completion interrupts when using
+ * DMA for command submission.
+ */
+ esp->flags |= ESP_FLAG_USE_FIFO;
+ /*
+ * Enable CONFIG2_FENAB to allow for large DMA transfers
+ */
+ if (am53c974_fenab)
+ esp->config2 |= ESP_CONFIG2_FENAB;
+
+ pep->esp = esp;
+
+ if (pci_request_regions(pdev, DRV_MODULE_NAME)) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "pci memory selection failed\n");
+ goto fail_priv_alloc;
+ }
+
+ esp->regs = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+ if (!esp->regs) {
+ dev_printk(KERN_ERR, &pdev->dev, "pci I/O map failed\n");
+ err = -EINVAL;
+ goto fail_release_regions;
+ }
+ esp->dma_regs = esp->regs;
+
+ pci_set_master(pdev);
+
+ esp->command_block = dma_alloc_coherent(&pdev->dev, 16,
+ &esp->command_block_dma, GFP_KERNEL);
+ if (!esp->command_block) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "failed to allocate command block\n");
+ err = -ENOMEM;
+ goto fail_unmap_regs;
+ }
+
+ pci_set_drvdata(pdev, pep);
+
+ err = request_irq(pdev->irq, scsi_esp_intr, IRQF_SHARED,
+ DRV_MODULE_NAME, esp);
+ if (err < 0) {
+ dev_printk(KERN_ERR, &pdev->dev, "failed to register IRQ\n");
+ goto fail_unmap_command_block;
+ }
+
+ esp->scsi_id = 7;
+ dc390_check_eeprom(esp);
+
+ shost->this_id = esp->scsi_id;
+ shost->max_id = 8;
+ shost->irq = pdev->irq;
+ shost->io_port = pci_resource_start(pdev, 0);
+ shost->n_io_port = pci_resource_len(pdev, 0);
+ shost->unique_id = shost->io_port;
+ esp->scsi_id_mask = (1 << esp->scsi_id);
+ /* Assume 40MHz clock */
+ esp->cfreq = 40000000;
+
+ err = scsi_esp_register(esp);
+ if (err)
+ goto fail_free_irq;
+
+ return 0;
+
+fail_free_irq:
+ free_irq(pdev->irq, esp);
+fail_unmap_command_block:
+ pci_set_drvdata(pdev, NULL);
+ dma_free_coherent(&pdev->dev, 16, esp->command_block,
+ esp->command_block_dma);
+fail_unmap_regs:
+ pci_iounmap(pdev, esp->regs);
+fail_release_regions:
+ pci_release_regions(pdev);
+fail_priv_alloc:
+ kfree(pep);
+fail_host_alloc:
+ scsi_host_put(shost);
+fail_disable_device:
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static void pci_esp_remove_one(struct pci_dev *pdev)
+{
+ struct pci_esp_priv *pep = pci_get_drvdata(pdev);
+ struct esp *esp = pep->esp;
+
+ scsi_esp_unregister(esp);
+ free_irq(pdev->irq, esp);
+ pci_set_drvdata(pdev, NULL);
+ dma_free_coherent(&pdev->dev, 16, esp->command_block,
+ esp->command_block_dma);
+ pci_iounmap(pdev, esp->regs);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ kfree(pep);
+
+ scsi_host_put(esp->host);
+}
+
+static const struct pci_device_id am53c974_pci_tbl[] = {
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, am53c974_pci_tbl);
+
+static struct pci_driver am53c974_driver = {
+ .name = DRV_MODULE_NAME,
+ .id_table = am53c974_pci_tbl,
+ .probe = pci_esp_probe_one,
+ .remove = pci_esp_remove_one,
+};
+
+module_pci_driver(am53c974_driver);
+
+MODULE_DESCRIPTION("AM53C974 SCSI driver");
+MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+MODULE_ALIAS("tmscsim");
+
+module_param(am53c974_debug, bool, 0644);
+MODULE_PARM_DESC(am53c974_debug, "Enable debugging");
+
+module_param(am53c974_fenab, bool, 0444);
+MODULE_PARM_DESC(am53c974_fenab, "Enable 24-bit DMA transfer sizes");
diff --git a/drivers/scsi/arcmsr/Makefile b/drivers/scsi/arcmsr/Makefile
index 721aced39168..9051f66cae36 100644
--- a/drivers/scsi/arcmsr/Makefile
+++ b/drivers/scsi/arcmsr/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# File: drivers/arcmsr/Makefile
# Makefile for the ARECA PCI-X PCI-EXPRESS SATA RAID controllers SCSI driver.
diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h
index 77b26f5b9c33..12c4148fe16f 100644
--- a/drivers/scsi/arcmsr/arcmsr.h
+++ b/drivers/scsi/arcmsr/arcmsr.h
@@ -45,42 +45,64 @@
#include <linux/interrupt.h>
struct device_attribute;
/*The limit of outstanding scsi command that firmware can handle*/
-#define ARCMSR_MAX_OUTSTANDING_CMD 256
-#ifdef CONFIG_XEN
- #define ARCMSR_MAX_FREECCB_NUM 160
-#else
- #define ARCMSR_MAX_FREECCB_NUM 320
-#endif
-#define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.15 2010/08/05"
-#define ARCMSR_SCSI_INITIATOR_ID 255
-#define ARCMSR_MAX_XFER_SECTORS 512
-#define ARCMSR_MAX_XFER_SECTORS_B 4096
-#define ARCMSR_MAX_XFER_SECTORS_C 304
-#define ARCMSR_MAX_TARGETID 17
-#define ARCMSR_MAX_TARGETLUN 8
-#define ARCMSR_MAX_CMD_PERLUN ARCMSR_MAX_OUTSTANDING_CMD
-#define ARCMSR_MAX_QBUFFER 4096
-#define ARCMSR_DEFAULT_SG_ENTRIES 38
-#define ARCMSR_MAX_HBB_POSTQUEUE 264
-#define ARCMSR_MAX_XFER_LEN 0x26000 /* 152K */
-#define ARCMSR_CDB_SG_PAGE_LENGTH 256
+#define ARCMSR_NAME "arcmsr"
+#define ARCMSR_MAX_FREECCB_NUM 1024
+#define ARCMSR_MAX_OUTSTANDING_CMD 1024
+#define ARCMSR_DEFAULT_OUTSTANDING_CMD 128
+#define ARCMSR_MIN_OUTSTANDING_CMD 32
+#define ARCMSR_DRIVER_VERSION "v1.51.00.14-20230915"
+#define ARCMSR_SCSI_INITIATOR_ID 255
+#define ARCMSR_MAX_XFER_SECTORS 512
+#define ARCMSR_MAX_XFER_SECTORS_B 4096
+#define ARCMSR_MAX_XFER_SECTORS_C 304
+#define ARCMSR_MAX_TARGETID 17
+#define ARCMSR_MAX_TARGETLUN 8
+#define ARCMSR_MAX_CMD_PERLUN 128
+#define ARCMSR_DEFAULT_CMD_PERLUN 32
+#define ARCMSR_MIN_CMD_PERLUN 1
+#define ARCMSR_MAX_QBUFFER 4096
+#define ARCMSR_DEFAULT_SG_ENTRIES 38
+#define ARCMSR_MAX_HBB_POSTQUEUE 264
+#define ARCMSR_MAX_ARC1214_POSTQUEUE 256
+#define ARCMSR_MAX_ARC1214_DONEQUEUE 257
+#define ARCMSR_MAX_HBE_DONEQUEUE 512
+#define ARCMSR_MAX_XFER_LEN 0x26000 /* 152K */
+#define ARCMSR_CDB_SG_PAGE_LENGTH 256
+#define ARCMST_NUM_MSIX_VECTORS 4
#ifndef PCI_DEVICE_ID_ARECA_1880
-#define PCI_DEVICE_ID_ARECA_1880 0x1880
- #endif
+#define PCI_DEVICE_ID_ARECA_1880 0x1880
+#endif
+#ifndef PCI_DEVICE_ID_ARECA_1214
+#define PCI_DEVICE_ID_ARECA_1214 0x1214
+#endif
+#ifndef PCI_DEVICE_ID_ARECA_1203
+#define PCI_DEVICE_ID_ARECA_1203 0x1203
+#endif
+#ifndef PCI_DEVICE_ID_ARECA_1883
+#define PCI_DEVICE_ID_ARECA_1883 0x1883
+#endif
+#ifndef PCI_DEVICE_ID_ARECA_1884
+#define PCI_DEVICE_ID_ARECA_1884 0x1884
+#endif
+#define PCI_DEVICE_ID_ARECA_1886_0 0x1886
+#define PCI_DEVICE_ID_ARECA_1886 0x188A
+#define ARCMSR_HOURS (1000 * 60 * 60 * 4)
+#define ARCMSR_MINUTES (1000 * 60 * 60)
+#define ARCMSR_DEFAULT_TIMEOUT 90
/*
**********************************************************************************
**
**********************************************************************************
*/
-#define ARC_SUCCESS 0
-#define ARC_FAILURE 1
+#define ARC_SUCCESS 0
+#define ARC_FAILURE 1
/*
*******************************************************************************
** split 64bits dma addressing
*******************************************************************************
*/
-#define dma_addr_hi32(addr) (uint32_t) ((addr>>16)>>16)
-#define dma_addr_lo32(addr) (uint32_t) (addr & 0xffffffff)
+#define dma_addr_hi32(addr) (uint32_t) ((addr>>16)>>16)
+#define dma_addr_lo32(addr) (uint32_t) (addr & 0xffffffff)
/*
*******************************************************************************
** MESSAGE CONTROL CODE
@@ -100,10 +122,11 @@ struct CMD_MESSAGE
** IOP Message Transfer Data for user space
*******************************************************************************
*/
+#define ARCMSR_API_DATA_BUFLEN 1032
struct CMD_MESSAGE_FIELD
{
struct CMD_MESSAGE cmdmessage;
- uint8_t messagedatabuffer[1032];
+ uint8_t messagedatabuffer[ARCMSR_API_DATA_BUFLEN];
};
/* IOP message transfer */
#define ARCMSR_MESSAGE_FAIL 0x0001
@@ -119,7 +142,7 @@ struct CMD_MESSAGE_FIELD
#define FUNCTION_SAY_HELLO 0x0807
#define FUNCTION_SAY_GOODBYE 0x0808
#define FUNCTION_FLUSH_ADAPTER_CACHE 0x0809
-#define FUNCTION_GET_FIRMWARE_STATUS 0x080A
+#define FUNCTION_GET_FIRMWARE_STATUS 0x080A
#define FUNCTION_HARDWARE_RESET 0x080B
/* ARECA IO CONTROL CODE*/
#define ARCMSR_MESSAGE_READ_RQBUFFER \
@@ -150,18 +173,18 @@ struct CMD_MESSAGE_FIELD
** structure for holding DMA address data
*************************************************************
*/
-#define IS_DMA64 (sizeof(dma_addr_t) == 8)
-#define IS_SG64_ADDR 0x01000000 /* bit24 */
+#define IS_DMA64 (sizeof(dma_addr_t) == 8)
+#define IS_SG64_ADDR 0x01000000 /* bit24 */
struct SG32ENTRY
{
- __le32 length;
- __le32 address;
+ __le32 length;
+ __le32 address;
}__attribute__ ((packed));
struct SG64ENTRY
{
- __le32 length;
- __le32 address;
- __le32 addresshigh;
+ __le32 length;
+ __le32 address;
+ __le32 addresshigh;
}__attribute__ ((packed));
/*
********************************************************************
@@ -180,50 +203,50 @@ struct QBUFFER
*/
struct FIRMWARE_INFO
{
- uint32_t signature; /*0, 00-03*/
- uint32_t request_len; /*1, 04-07*/
- uint32_t numbers_queue; /*2, 08-11*/
- uint32_t sdram_size; /*3, 12-15*/
- uint32_t ide_channels; /*4, 16-19*/
- char vendor[40]; /*5, 20-59*/
- char model[8]; /*15, 60-67*/
- char firmware_ver[16]; /*17, 68-83*/
- char device_map[16]; /*21, 84-99*/
- uint32_t cfgVersion; /*25,100-103 Added for checking of new firmware capability*/
- uint8_t cfgSerial[16]; /*26,104-119*/
- uint32_t cfgPicStatus; /*30,120-123*/
+ uint32_t signature; /*0, 00-03*/
+ uint32_t request_len; /*1, 04-07*/
+ uint32_t numbers_queue; /*2, 08-11*/
+ uint32_t sdram_size; /*3, 12-15*/
+ uint32_t ide_channels; /*4, 16-19*/
+ char vendor[40]; /*5, 20-59*/
+ char model[8]; /*15, 60-67*/
+ char firmware_ver[16]; /*17, 68-83*/
+ char device_map[16]; /*21, 84-99*/
+ uint32_t cfgVersion; /*25,100-103 Added for checking of new firmware capability*/
+ uint8_t cfgSerial[16]; /*26,104-119*/
+ uint32_t cfgPicStatus; /*30,120-123*/
};
/* signature of set and get firmware config */
-#define ARCMSR_SIGNATURE_GET_CONFIG 0x87974060
-#define ARCMSR_SIGNATURE_SET_CONFIG 0x87974063
+#define ARCMSR_SIGNATURE_GET_CONFIG 0x87974060
+#define ARCMSR_SIGNATURE_SET_CONFIG 0x87974063
/* message code of inbound message register */
-#define ARCMSR_INBOUND_MESG0_NOP 0x00000000
-#define ARCMSR_INBOUND_MESG0_GET_CONFIG 0x00000001
-#define ARCMSR_INBOUND_MESG0_SET_CONFIG 0x00000002
-#define ARCMSR_INBOUND_MESG0_ABORT_CMD 0x00000003
-#define ARCMSR_INBOUND_MESG0_STOP_BGRB 0x00000004
-#define ARCMSR_INBOUND_MESG0_FLUSH_CACHE 0x00000005
-#define ARCMSR_INBOUND_MESG0_START_BGRB 0x00000006
-#define ARCMSR_INBOUND_MESG0_CHK331PENDING 0x00000007
-#define ARCMSR_INBOUND_MESG0_SYNC_TIMER 0x00000008
+#define ARCMSR_INBOUND_MESG0_NOP 0x00000000
+#define ARCMSR_INBOUND_MESG0_GET_CONFIG 0x00000001
+#define ARCMSR_INBOUND_MESG0_SET_CONFIG 0x00000002
+#define ARCMSR_INBOUND_MESG0_ABORT_CMD 0x00000003
+#define ARCMSR_INBOUND_MESG0_STOP_BGRB 0x00000004
+#define ARCMSR_INBOUND_MESG0_FLUSH_CACHE 0x00000005
+#define ARCMSR_INBOUND_MESG0_START_BGRB 0x00000006
+#define ARCMSR_INBOUND_MESG0_CHK331PENDING 0x00000007
+#define ARCMSR_INBOUND_MESG0_SYNC_TIMER 0x00000008
/* doorbell interrupt generator */
-#define ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK 0x00000001
-#define ARCMSR_INBOUND_DRIVER_DATA_READ_OK 0x00000002
-#define ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK 0x00000001
-#define ARCMSR_OUTBOUND_IOP331_DATA_READ_OK 0x00000002
+#define ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK 0x00000001
+#define ARCMSR_INBOUND_DRIVER_DATA_READ_OK 0x00000002
+#define ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK 0x00000001
+#define ARCMSR_OUTBOUND_IOP331_DATA_READ_OK 0x00000002
/* ccb areca cdb flag */
-#define ARCMSR_CCBPOST_FLAG_SGL_BSIZE 0x80000000
-#define ARCMSR_CCBPOST_FLAG_IAM_BIOS 0x40000000
-#define ARCMSR_CCBREPLY_FLAG_IAM_BIOS 0x40000000
-#define ARCMSR_CCBREPLY_FLAG_ERROR_MODE0 0x10000000
-#define ARCMSR_CCBREPLY_FLAG_ERROR_MODE1 0x00000001
+#define ARCMSR_CCBPOST_FLAG_SGL_BSIZE 0x80000000
+#define ARCMSR_CCBPOST_FLAG_IAM_BIOS 0x40000000
+#define ARCMSR_CCBREPLY_FLAG_IAM_BIOS 0x40000000
+#define ARCMSR_CCBREPLY_FLAG_ERROR_MODE0 0x10000000
+#define ARCMSR_CCBREPLY_FLAG_ERROR_MODE1 0x00000001
/* outbound firmware ok */
-#define ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK 0x80000000
+#define ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK 0x80000000
/* ARC-1680 Bus Reset*/
-#define ARCMSR_ARC1680_BUS_RESET 0x00000003
+#define ARCMSR_ARC1680_BUS_RESET 0x00000003
/* ARC-1880 Bus Reset*/
-#define ARCMSR_ARC1880_RESET_ADAPTER 0x00000024
-#define ARCMSR_ARC1880_DiagWrite_ENABLE 0x00000080
+#define ARCMSR_ARC1880_RESET_ADAPTER 0x00000024
+#define ARCMSR_ARC1880_DiagWrite_ENABLE 0x00000080
/*
************************************************************************
@@ -237,6 +260,12 @@ struct FIRMWARE_INFO
/* window of "instruction flags" from iop to driver */
#define ARCMSR_IOP2DRV_DOORBELL 0x00020408
#define ARCMSR_IOP2DRV_DOORBELL_MASK 0x0002040C
+/* window of "instruction flags" from iop to driver */
+#define ARCMSR_IOP2DRV_DOORBELL_1203 0x00021870
+#define ARCMSR_IOP2DRV_DOORBELL_MASK_1203 0x00021874
+/* window of "instruction flags" from driver to iop */
+#define ARCMSR_DRV2IOP_DOORBELL_1203 0x00021878
+#define ARCMSR_DRV2IOP_DOORBELL_MASK_1203 0x0002187C
/* ARECA FLAG LANGUAGE */
/* ioctl transfer */
#define ARCMSR_IOP2DRV_DATA_WRITE_OK 0x00000001
@@ -260,9 +289,10 @@ struct FIRMWARE_INFO
#define ARCMSR_MESSAGE_FLUSH_CACHE 0x00050008
/* (ARCMSR_INBOUND_MESG0_START_BGRB<<16)|ARCMSR_DRV2IOP_MESSAGE_CMD_POSTED) */
#define ARCMSR_MESSAGE_START_BGRB 0x00060008
+#define ARCMSR_MESSAGE_SYNC_TIMER 0x00080008
#define ARCMSR_MESSAGE_START_DRIVER_MODE 0x000E0008
#define ARCMSR_MESSAGE_SET_POST_WINDOW 0x000F0008
-#define ARCMSR_MESSAGE_ACTIVE_EOI_MODE 0x00100008
+#define ARCMSR_MESSAGE_ACTIVE_EOI_MODE 0x00100008
/* ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK */
#define ARCMSR_MESSAGE_FIRMWARE_OK 0x80000000
/* ioctl transfer */
@@ -271,7 +301,7 @@ struct FIRMWARE_INFO
#define ARCMSR_DRV2IOP_DATA_READ_OK 0x00000002
#define ARCMSR_DRV2IOP_CDB_POSTED 0x00000004
#define ARCMSR_DRV2IOP_MESSAGE_CMD_POSTED 0x00000008
-#define ARCMSR_DRV2IOP_END_OF_INTERRUPT 0x00000010
+#define ARCMSR_DRV2IOP_END_OF_INTERRUPT 0x00000010
/* data tunnel buffer between user space program and its firmware */
/* user space data to iop 128bytes */
@@ -280,6 +310,9 @@ struct FIRMWARE_INFO
#define ARCMSR_MESSAGE_RBUFFER 0x0000ff00
/* iop message_rwbuffer for message command */
#define ARCMSR_MESSAGE_RWBUFFER 0x0000fa00
+
+#define MEM_BASE0(x) (u32 __iomem *)((unsigned long)acb->mem_base0 + x)
+#define MEM_BASE1(x) (u32 __iomem *)((unsigned long)acb->mem_base1 + x)
/*
************************************************************************
** SPEC. for Areca HBC adapter
@@ -293,12 +326,12 @@ struct FIRMWARE_INFO
#define ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR_MASK 0x00000008 /* When clear, the Outbound Post List FIFO Not Empty interrupt routes to the host.*/
#define ARCMSR_HBCMU_ALL_INTMASKENABLE 0x0000000D /* disable all ISR */
/* Host Interrupt Status */
-#define ARCMSR_HBCMU_UTILITY_A_ISR 0x00000001
+#define ARCMSR_HBCMU_UTILITY_A_ISR 0x00000001
/*
** Set when the Utility_A Interrupt bit is set in the Outbound Doorbell Register.
** It clears by writing a 1 to the Utility_A bit in the Outbound Doorbell Clear Register or through automatic clearing (if enabled).
*/
-#define ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR 0x00000004
+#define ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR 0x00000004
/*
** Set if Outbound Doorbell register bits 30:1 have a non-zero
** value. This bit clears only when Outbound Doorbell bits
@@ -311,7 +344,7 @@ struct FIRMWARE_INFO
** Register (FIFO) is not empty. It clears when the Outbound
** Post List FIFO is empty.
*/
-#define ARCMSR_HBCMU_SAS_ALL_INT 0x00000010
+#define ARCMSR_HBCMU_SAS_ALL_INT 0x00000010
/*
** This bit indicates a SAS interrupt from a source external to
** the PCIe core. This bit is not maskable.
@@ -320,35 +353,125 @@ struct FIRMWARE_INFO
#define ARCMSR_HBCMU_DRV2IOP_DATA_WRITE_OK 0x00000002
#define ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK 0x00000004
/*inbound message 0 ready*/
-#define ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE 0x00000008
+#define ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE 0x00000008
/*more than 12 request completed in a time*/
#define ARCMSR_HBCMU_DRV2IOP_POSTQUEUE_THROTTLING 0x00000010
#define ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_OK 0x00000002
/*outbound DATA WRITE isr door bell clear*/
-#define ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_DOORBELL_CLEAR 0x00000002
+#define ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_DOORBELL_CLEAR 0x00000002
#define ARCMSR_HBCMU_IOP2DRV_DATA_READ_OK 0x00000004
/*outbound DATA READ isr door bell clear*/
-#define ARCMSR_HBCMU_IOP2DRV_DATA_READ_DOORBELL_CLEAR 0x00000004
+#define ARCMSR_HBCMU_IOP2DRV_DATA_READ_DOORBELL_CLEAR 0x00000004
/*outbound message 0 ready*/
-#define ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE 0x00000008
+#define ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE 0x00000008
/*outbound message cmd isr door bell clear*/
#define ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR 0x00000008
/*ARCMSR_HBAMU_MESSAGE_FIRMWARE_OK*/
#define ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK 0x80000000
/*
*******************************************************************************
+** SPEC. for Areca Type D adapter
+*******************************************************************************
+*/
+#define ARCMSR_ARC1214_CHIP_ID 0x00004
+#define ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION 0x00008
+#define ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK 0x00034
+#define ARCMSR_ARC1214_SAMPLE_RESET 0x00100
+#define ARCMSR_ARC1214_RESET_REQUEST 0x00108
+#define ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS 0x00200
+#define ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE 0x0020C
+#define ARCMSR_ARC1214_INBOUND_MESSAGE0 0x00400
+#define ARCMSR_ARC1214_INBOUND_MESSAGE1 0x00404
+#define ARCMSR_ARC1214_OUTBOUND_MESSAGE0 0x00420
+#define ARCMSR_ARC1214_OUTBOUND_MESSAGE1 0x00424
+#define ARCMSR_ARC1214_INBOUND_DOORBELL 0x00460
+#define ARCMSR_ARC1214_OUTBOUND_DOORBELL 0x00480
+#define ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE 0x00484
+#define ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW 0x01000
+#define ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH 0x01004
+#define ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER 0x01018
+#define ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW 0x01060
+#define ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH 0x01064
+#define ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER 0x0106C
+#define ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER 0x01070
+#define ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE 0x01088
+#define ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE 0x0108C
+#define ARCMSR_ARC1214_MESSAGE_WBUFFER 0x02000
+#define ARCMSR_ARC1214_MESSAGE_RBUFFER 0x02100
+#define ARCMSR_ARC1214_MESSAGE_RWBUFFER 0x02200
+/* Host Interrupt Mask */
+#define ARCMSR_ARC1214_ALL_INT_ENABLE 0x00001010
+#define ARCMSR_ARC1214_ALL_INT_DISABLE 0x00000000
+/* Host Interrupt Status */
+#define ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR 0x00001000
+#define ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR 0x00000010
+/* DoorBell*/
+#define ARCMSR_ARC1214_DRV2IOP_DATA_IN_READY 0x00000001
+#define ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ 0x00000002
+/*inbound message 0 ready*/
+#define ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK 0x00000001
+/*outbound DATA WRITE isr door bell clear*/
+#define ARCMSR_ARC1214_IOP2DRV_DATA_READ_OK 0x00000002
+/*outbound message 0 ready*/
+#define ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE 0x02000000
+/*outbound message cmd isr door bell clear*/
+/*ARCMSR_HBAMU_MESSAGE_FIRMWARE_OK*/
+#define ARCMSR_ARC1214_MESSAGE_FIRMWARE_OK 0x80000000
+#define ARCMSR_ARC1214_OUTBOUND_LIST_INTERRUPT_CLEAR 0x00000001
+/*
+*******************************************************************************
+** SPEC. for Areca Type E adapter
+*******************************************************************************
+*/
+#define ARCMSR_SIGNATURE_1884 0x188417D3
+
+#define ARCMSR_HBEMU_DRV2IOP_DATA_WRITE_OK 0x00000002
+#define ARCMSR_HBEMU_DRV2IOP_DATA_READ_OK 0x00000004
+#define ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE 0x00000008
+
+#define ARCMSR_HBEMU_IOP2DRV_DATA_WRITE_OK 0x00000002
+#define ARCMSR_HBEMU_IOP2DRV_DATA_READ_OK 0x00000004
+#define ARCMSR_HBEMU_IOP2DRV_MESSAGE_CMD_DONE 0x00000008
+
+#define ARCMSR_HBEMU_MESSAGE_FIRMWARE_OK 0x80000000
+
+#define ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR 0x00000001
+#define ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR 0x00000008
+#define ARCMSR_HBEMU_ALL_INTMASKENABLE 0x00000009
+
+/* ARC-1884 doorbell sync */
+#define ARCMSR_HBEMU_DOORBELL_SYNC 0x100
+#define ARCMSR_ARC188X_RESET_ADAPTER 0x00000004
+#define ARCMSR_ARC1884_DiagWrite_ENABLE 0x00000080
+
+/*
+*******************************************************************************
+** SPEC. for Areca Type F adapter
+*******************************************************************************
+*/
+#define ARCMSR_SIGNATURE_1886 0x188617D3
+// Doorbell and interrupt definition are same as Type E adapter
+/* ARC-1886 doorbell sync */
+#define ARCMSR_HBFMU_DOORBELL_SYNC 0x100
+//set host rw buffer physical address at inbound message 0, 1 (low,high)
+#define ARCMSR_HBFMU_DOORBELL_SYNC1 0x300
+#define ARCMSR_HBFMU_MESSAGE_FIRMWARE_OK 0x80000000
+#define ARCMSR_HBFMU_MESSAGE_NO_VOLUME_CHANGE 0x20000000
+
+/*
+*******************************************************************************
** ARECA SCSI COMMAND DESCRIPTOR BLOCK size 0x1F8 (504)
*******************************************************************************
*/
struct ARCMSR_CDB
{
- uint8_t Bus;
- uint8_t TargetID;
- uint8_t LUN;
- uint8_t Function;
- uint8_t CdbLength;
- uint8_t sgcount;
- uint8_t Flags;
+ uint8_t Bus;
+ uint8_t TargetID;
+ uint8_t LUN;
+ uint8_t Function;
+ uint8_t CdbLength;
+ uint8_t sgcount;
+ uint8_t Flags;
#define ARCMSR_CDB_FLAG_SGL_BSIZE 0x01
#define ARCMSR_CDB_FLAG_BIOS 0x02
#define ARCMSR_CDB_FLAG_WRITE 0x04
@@ -356,21 +479,21 @@ struct ARCMSR_CDB
#define ARCMSR_CDB_FLAG_HEADQ 0x08
#define ARCMSR_CDB_FLAG_ORDEREDQ 0x10
- uint8_t msgPages;
- uint32_t Context;
- uint32_t DataLength;
- uint8_t Cdb[16];
- uint8_t DeviceStatus;
+ uint8_t msgPages;
+ uint32_t msgContext;
+ uint32_t DataLength;
+ uint8_t Cdb[16];
+ uint8_t DeviceStatus;
#define ARCMSR_DEV_CHECK_CONDITION 0x02
#define ARCMSR_DEV_SELECT_TIMEOUT 0xF0
#define ARCMSR_DEV_ABORTED 0xF1
#define ARCMSR_DEV_INIT_FAIL 0xF2
- uint8_t SenseData[15];
+ uint8_t SenseData[15];
union
{
- struct SG32ENTRY sg32entry[1];
- struct SG64ENTRY sg64entry[1];
+ struct SG32ENTRY sg32entry[1];
+ struct SG64ENTRY sg64entry[1];
} u;
};
/*
@@ -410,13 +533,13 @@ struct MessageUnit_B
uint32_t done_qbuffer[ARCMSR_MAX_HBB_POSTQUEUE];
uint32_t postq_index;
uint32_t doneq_index;
- uint32_t __iomem *drv2iop_doorbell;
- uint32_t __iomem *drv2iop_doorbell_mask;
- uint32_t __iomem *iop2drv_doorbell;
- uint32_t __iomem *iop2drv_doorbell_mask;
- uint32_t __iomem *message_rwbuffer;
- uint32_t __iomem *message_wbuffer;
- uint32_t __iomem *message_rbuffer;
+ uint32_t __iomem *drv2iop_doorbell;
+ uint32_t __iomem *drv2iop_doorbell_mask;
+ uint32_t __iomem *iop2drv_doorbell;
+ uint32_t __iomem *iop2drv_doorbell_mask;
+ uint32_t __iomem *message_rwbuffer;
+ uint32_t __iomem *message_wbuffer;
+ uint32_t __iomem *message_rbuffer;
};
/*
*********************************************************************
@@ -436,7 +559,132 @@ struct MessageUnit_C{
uint32_t diagnostic_rw_data; /*0024 0027*/
uint32_t diagnostic_rw_address_low; /*0028 002B*/
uint32_t diagnostic_rw_address_high; /*002C 002F*/
- uint32_t host_int_status; /*0030 0033*/
+ uint32_t host_int_status; /*0030 0033*/
+ uint32_t host_int_mask; /*0034 0037*/
+ uint32_t dcr_data; /*0038 003B*/
+ uint32_t dcr_address; /*003C 003F*/
+ uint32_t inbound_queueport; /*0040 0043*/
+ uint32_t outbound_queueport; /*0044 0047*/
+ uint32_t hcb_pci_address_low; /*0048 004B*/
+ uint32_t hcb_pci_address_high; /*004C 004F*/
+ uint32_t iop_int_status; /*0050 0053*/
+ uint32_t iop_int_mask; /*0054 0057*/
+ uint32_t iop_inbound_queue_port; /*0058 005B*/
+ uint32_t iop_outbound_queue_port; /*005C 005F*/
+ uint32_t inbound_free_list_index; /*0060 0063*/
+ uint32_t inbound_post_list_index; /*0064 0067*/
+ uint32_t outbound_free_list_index; /*0068 006B*/
+ uint32_t outbound_post_list_index; /*006C 006F*/
+ uint32_t inbound_doorbell_clear; /*0070 0073*/
+ uint32_t i2o_message_unit_control; /*0074 0077*/
+ uint32_t last_used_message_source_address_low; /*0078 007B*/
+ uint32_t last_used_message_source_address_high; /*007C 007F*/
+ uint32_t pull_mode_data_byte_count[4]; /*0080 008F*/
+ uint32_t message_dest_address_index; /*0090 0093*/
+ uint32_t done_queue_not_empty_int_counter_timer; /*0094 0097*/
+ uint32_t utility_A_int_counter_timer; /*0098 009B*/
+ uint32_t outbound_doorbell; /*009C 009F*/
+ uint32_t outbound_doorbell_clear; /*00A0 00A3*/
+ uint32_t message_source_address_index; /*00A4 00A7*/
+ uint32_t message_done_queue_index; /*00A8 00AB*/
+ uint32_t reserved0; /*00AC 00AF*/
+ uint32_t inbound_msgaddr0; /*00B0 00B3*/
+ uint32_t inbound_msgaddr1; /*00B4 00B7*/
+ uint32_t outbound_msgaddr0; /*00B8 00BB*/
+ uint32_t outbound_msgaddr1; /*00BC 00BF*/
+ uint32_t inbound_queueport_low; /*00C0 00C3*/
+ uint32_t inbound_queueport_high; /*00C4 00C7*/
+ uint32_t outbound_queueport_low; /*00C8 00CB*/
+ uint32_t outbound_queueport_high; /*00CC 00CF*/
+ uint32_t iop_inbound_queue_port_low; /*00D0 00D3*/
+ uint32_t iop_inbound_queue_port_high; /*00D4 00D7*/
+ uint32_t iop_outbound_queue_port_low; /*00D8 00DB*/
+ uint32_t iop_outbound_queue_port_high; /*00DC 00DF*/
+ uint32_t message_dest_queue_port_low; /*00E0 00E3*/
+ uint32_t message_dest_queue_port_high; /*00E4 00E7*/
+ uint32_t last_used_message_dest_address_low; /*00E8 00EB*/
+ uint32_t last_used_message_dest_address_high; /*00EC 00EF*/
+ uint32_t message_done_queue_base_address_low; /*00F0 00F3*/
+ uint32_t message_done_queue_base_address_high; /*00F4 00F7*/
+ uint32_t host_diagnostic; /*00F8 00FB*/
+ uint32_t write_sequence; /*00FC 00FF*/
+ uint32_t reserved1[34]; /*0100 0187*/
+ uint32_t reserved2[1950]; /*0188 1FFF*/
+ uint32_t message_wbuffer[32]; /*2000 207F*/
+ uint32_t reserved3[32]; /*2080 20FF*/
+ uint32_t message_rbuffer[32]; /*2100 217F*/
+ uint32_t reserved4[32]; /*2180 21FF*/
+ uint32_t msgcode_rwbuffer[256]; /*2200 23FF*/
+};
+/*
+*********************************************************************
+** Messaging Unit (MU) of Type D processor
+*********************************************************************
+*/
+struct InBound_SRB {
+ uint32_t addressLow; /* pointer to SRB block */
+ uint32_t addressHigh;
+ uint32_t length; /* in DWORDs */
+ uint32_t reserved0;
+};
+
+struct OutBound_SRB {
+ uint32_t addressLow; /* pointer to SRB block */
+ uint32_t addressHigh;
+};
+
+struct MessageUnit_D {
+ struct InBound_SRB post_qbuffer[ARCMSR_MAX_ARC1214_POSTQUEUE];
+ volatile struct OutBound_SRB
+ done_qbuffer[ARCMSR_MAX_ARC1214_DONEQUEUE];
+ u16 postq_index;
+ volatile u16 doneq_index;
+ u32 __iomem *chip_id; /* 0x00004 */
+ u32 __iomem *cpu_mem_config; /* 0x00008 */
+ u32 __iomem *i2o_host_interrupt_mask; /* 0x00034 */
+ u32 __iomem *sample_at_reset; /* 0x00100 */
+ u32 __iomem *reset_request; /* 0x00108 */
+ u32 __iomem *host_int_status; /* 0x00200 */
+ u32 __iomem *pcief0_int_enable; /* 0x0020C */
+ u32 __iomem *inbound_msgaddr0; /* 0x00400 */
+ u32 __iomem *inbound_msgaddr1; /* 0x00404 */
+ u32 __iomem *outbound_msgaddr0; /* 0x00420 */
+ u32 __iomem *outbound_msgaddr1; /* 0x00424 */
+ u32 __iomem *inbound_doorbell; /* 0x00460 */
+ u32 __iomem *outbound_doorbell; /* 0x00480 */
+ u32 __iomem *outbound_doorbell_enable; /* 0x00484 */
+ u32 __iomem *inboundlist_base_low; /* 0x01000 */
+ u32 __iomem *inboundlist_base_high; /* 0x01004 */
+ u32 __iomem *inboundlist_write_pointer; /* 0x01018 */
+ u32 __iomem *outboundlist_base_low; /* 0x01060 */
+ u32 __iomem *outboundlist_base_high; /* 0x01064 */
+ u32 __iomem *outboundlist_copy_pointer; /* 0x0106C */
+ u32 __iomem *outboundlist_read_pointer; /* 0x01070 0x01072 */
+ u32 __iomem *outboundlist_interrupt_cause; /* 0x1088 */
+ u32 __iomem *outboundlist_interrupt_enable; /* 0x108C */
+ u32 __iomem *message_wbuffer; /* 0x2000 */
+ u32 __iomem *message_rbuffer; /* 0x2100 */
+ u32 __iomem *msgcode_rwbuffer; /* 0x2200 */
+};
+/*
+*********************************************************************
+** Messaging Unit (MU) of Type E processor(LSI)
+*********************************************************************
+*/
+struct MessageUnit_E{
+ uint32_t iobound_doorbell; /*0000 0003*/
+ uint32_t write_sequence_3xxx; /*0004 0007*/
+ uint32_t host_diagnostic_3xxx; /*0008 000B*/
+ uint32_t posted_outbound_doorbell; /*000C 000F*/
+ uint32_t master_error_attribute; /*0010 0013*/
+ uint32_t master_error_address_low; /*0014 0017*/
+ uint32_t master_error_address_high; /*0018 001B*/
+ uint32_t hcb_size; /*001C 001F*/
+ uint32_t inbound_doorbell; /*0020 0023*/
+ uint32_t diagnostic_rw_data; /*0024 0027*/
+ uint32_t diagnostic_rw_address_low; /*0028 002B*/
+ uint32_t diagnostic_rw_address_high; /*002C 002F*/
+ uint32_t host_int_status; /*0030 0033*/
uint32_t host_int_mask; /*0034 0037*/
uint32_t dcr_data; /*0038 003B*/
uint32_t dcr_address; /*003C 003F*/
@@ -448,12 +696,12 @@ struct MessageUnit_C{
uint32_t iop_int_mask; /*0054 0057*/
uint32_t iop_inbound_queue_port; /*0058 005B*/
uint32_t iop_outbound_queue_port; /*005C 005F*/
- uint32_t inbound_free_list_index; /*0060 0063*/
- uint32_t inbound_post_list_index; /*0064 0067*/
- uint32_t outbound_free_list_index; /*0068 006B*/
- uint32_t outbound_post_list_index; /*006C 006F*/
+ uint32_t inbound_free_list_index; /*0060 0063*/
+ uint32_t inbound_post_list_index; /*0064 0067*/
+ uint32_t reply_post_producer_index; /*0068 006B*/
+ uint32_t reply_post_consumer_index; /*006C 006F*/
uint32_t inbound_doorbell_clear; /*0070 0073*/
- uint32_t i2o_message_unit_control; /*0074 0077*/
+ uint32_t i2o_message_unit_control; /*0074 0077*/
uint32_t last_used_message_source_address_low; /*0078 007B*/
uint32_t last_used_message_source_address_high; /*007C 007F*/
uint32_t pull_mode_data_byte_count[4]; /*0080 008F*/
@@ -461,7 +709,7 @@ struct MessageUnit_C{
uint32_t done_queue_not_empty_int_counter_timer; /*0094 0097*/
uint32_t utility_A_int_counter_timer; /*0098 009B*/
uint32_t outbound_doorbell; /*009C 009F*/
- uint32_t outbound_doorbell_clear; /*00A0 00A3*/
+ uint32_t outbound_doorbell_clear; /*00A0 00A3*/
uint32_t message_source_address_index; /*00A4 00A7*/
uint32_t message_done_queue_index; /*00A8 00AB*/
uint32_t reserved0; /*00AC 00AF*/
@@ -483,16 +731,114 @@ struct MessageUnit_C{
uint32_t last_used_message_dest_address_high; /*00EC 00EF*/
uint32_t message_done_queue_base_address_low; /*00F0 00F3*/
uint32_t message_done_queue_base_address_high; /*00F4 00F7*/
- uint32_t host_diagnostic; /*00F8 00FB*/
+ uint32_t host_diagnostic; /*00F8 00FB*/
uint32_t write_sequence; /*00FC 00FF*/
uint32_t reserved1[34]; /*0100 0187*/
- uint32_t reserved2[1950]; /*0188 1FFF*/
+ uint32_t reserved2[1950]; /*0188 1FFF*/
uint32_t message_wbuffer[32]; /*2000 207F*/
uint32_t reserved3[32]; /*2080 20FF*/
uint32_t message_rbuffer[32]; /*2100 217F*/
uint32_t reserved4[32]; /*2180 21FF*/
uint32_t msgcode_rwbuffer[256]; /*2200 23FF*/
};
+
+/*
+*********************************************************************
+** Messaging Unit (MU) of Type F processor(LSI)
+*********************************************************************
+*/
+struct MessageUnit_F {
+ uint32_t iobound_doorbell; /*0000 0003*/
+ uint32_t write_sequence_3xxx; /*0004 0007*/
+ uint32_t host_diagnostic_3xxx; /*0008 000B*/
+ uint32_t posted_outbound_doorbell; /*000C 000F*/
+ uint32_t master_error_attribute; /*0010 0013*/
+ uint32_t master_error_address_low; /*0014 0017*/
+ uint32_t master_error_address_high; /*0018 001B*/
+ uint32_t hcb_size; /*001C 001F*/
+ uint32_t inbound_doorbell; /*0020 0023*/
+ uint32_t diagnostic_rw_data; /*0024 0027*/
+ uint32_t diagnostic_rw_address_low; /*0028 002B*/
+ uint32_t diagnostic_rw_address_high; /*002C 002F*/
+ uint32_t host_int_status; /*0030 0033*/
+ uint32_t host_int_mask; /*0034 0037*/
+ uint32_t dcr_data; /*0038 003B*/
+ uint32_t dcr_address; /*003C 003F*/
+ uint32_t inbound_queueport; /*0040 0043*/
+ uint32_t outbound_queueport; /*0044 0047*/
+ uint32_t hcb_pci_address_low; /*0048 004B*/
+ uint32_t hcb_pci_address_high; /*004C 004F*/
+ uint32_t iop_int_status; /*0050 0053*/
+ uint32_t iop_int_mask; /*0054 0057*/
+ uint32_t iop_inbound_queue_port; /*0058 005B*/
+ uint32_t iop_outbound_queue_port; /*005C 005F*/
+ uint32_t inbound_free_list_index; /*0060 0063*/
+ uint32_t inbound_post_list_index; /*0064 0067*/
+ uint32_t reply_post_producer_index; /*0068 006B*/
+ uint32_t reply_post_consumer_index; /*006C 006F*/
+ uint32_t inbound_doorbell_clear; /*0070 0073*/
+ uint32_t i2o_message_unit_control; /*0074 0077*/
+ uint32_t last_used_message_source_address_low; /*0078 007B*/
+ uint32_t last_used_message_source_address_high; /*007C 007F*/
+ uint32_t pull_mode_data_byte_count[4]; /*0080 008F*/
+ uint32_t message_dest_address_index; /*0090 0093*/
+ uint32_t done_queue_not_empty_int_counter_timer; /*0094 0097*/
+ uint32_t utility_A_int_counter_timer; /*0098 009B*/
+ uint32_t outbound_doorbell; /*009C 009F*/
+ uint32_t outbound_doorbell_clear; /*00A0 00A3*/
+ uint32_t message_source_address_index; /*00A4 00A7*/
+ uint32_t message_done_queue_index; /*00A8 00AB*/
+ uint32_t reserved0; /*00AC 00AF*/
+ uint32_t inbound_msgaddr0; /*00B0 00B3*/
+ uint32_t inbound_msgaddr1; /*00B4 00B7*/
+ uint32_t outbound_msgaddr0; /*00B8 00BB*/
+ uint32_t outbound_msgaddr1; /*00BC 00BF*/
+ uint32_t inbound_queueport_low; /*00C0 00C3*/
+ uint32_t inbound_queueport_high; /*00C4 00C7*/
+ uint32_t outbound_queueport_low; /*00C8 00CB*/
+ uint32_t outbound_queueport_high; /*00CC 00CF*/
+ uint32_t iop_inbound_queue_port_low; /*00D0 00D3*/
+ uint32_t iop_inbound_queue_port_high; /*00D4 00D7*/
+ uint32_t iop_outbound_queue_port_low; /*00D8 00DB*/
+ uint32_t iop_outbound_queue_port_high; /*00DC 00DF*/
+ uint32_t message_dest_queue_port_low; /*00E0 00E3*/
+ uint32_t message_dest_queue_port_high; /*00E4 00E7*/
+ uint32_t last_used_message_dest_address_low; /*00E8 00EB*/
+ uint32_t last_used_message_dest_address_high; /*00EC 00EF*/
+ uint32_t message_done_queue_base_address_low; /*00F0 00F3*/
+ uint32_t message_done_queue_base_address_high; /*00F4 00F7*/
+ uint32_t host_diagnostic; /*00F8 00FB*/
+ uint32_t write_sequence; /*00FC 00FF*/
+ uint32_t reserved1[46]; /*0100 01B7*/
+ uint32_t reply_post_producer_index1; /*01B8 01BB*/
+ uint32_t reply_post_consumer_index1; /*01BC 01BF*/
+};
+
+#define MESG_RW_BUFFER_SIZE (256 * 3)
+
+typedef struct deliver_completeQ {
+ uint16_t cmdFlag;
+ uint16_t cmdSMID;
+ uint16_t cmdLMID; // reserved (0)
+ uint16_t cmdFlag2; // reserved (0)
+} DeliverQ, CompletionQ, *pDeliver_Q, *pCompletion_Q;
+
+#define ARCMSR_XOR_SEG_SIZE (1024 * 1024)
+struct HostRamBuf {
+ uint32_t hrbSignature; // must be "HRBS"
+ uint32_t hrbSize; // total sg size, be multiples of MB
+ uint32_t hrbRes[2]; // reserved, must be set to 0
+};
+struct Xor_sg {
+ dma_addr_t xorPhys;
+ uint64_t xorBufLen;
+};
+struct XorHandle {
+ dma_addr_t xorPhys;
+ uint64_t xorBufLen;
+ void *xorVirt;
+};
+
/*
*******************************************************************************
** Adapter Control Block
@@ -500,100 +846,133 @@ struct MessageUnit_C{
*/
struct AdapterControlBlock
{
- uint32_t adapter_type; /* adapter A,B..... */
- #define ACB_ADAPTER_TYPE_A 0x00000001 /* hba I IOP */
- #define ACB_ADAPTER_TYPE_B 0x00000002 /* hbb M IOP */
- #define ACB_ADAPTER_TYPE_C 0x00000004 /* hbc P IOP */
- #define ACB_ADAPTER_TYPE_D 0x00000008 /* hbd A IOP */
- struct pci_dev * pdev;
- struct Scsi_Host * host;
- unsigned long vir2phy_offset;
+ uint32_t adapter_type; /* adapter A,B..... */
+#define ACB_ADAPTER_TYPE_A 0x00000000 /* hba I IOP */
+#define ACB_ADAPTER_TYPE_B 0x00000001 /* hbb M IOP */
+#define ACB_ADAPTER_TYPE_C 0x00000002 /* hbc L IOP */
+#define ACB_ADAPTER_TYPE_D 0x00000003 /* hbd M IOP */
+#define ACB_ADAPTER_TYPE_E 0x00000004 /* hba L IOP */
+#define ACB_ADAPTER_TYPE_F 0x00000005 /* hba L IOP */
+ u32 ioqueue_size;
+ struct pci_dev * pdev;
+ struct Scsi_Host * host;
+ unsigned long vir2phy_offset;
/* Offset is used in making arc cdb physical to virtual calculations */
- uint32_t outbound_int_enable;
- uint32_t cdb_phyaddr_hi32;
- uint32_t reg_mu_acc_handle0;
- spinlock_t eh_lock;
- spinlock_t ccblist_lock;
+ uint32_t outbound_int_enable;
+ uint32_t cdb_phyaddr_hi32;
+ uint32_t reg_mu_acc_handle0;
+ uint64_t cdb_phyadd_hipart;
+ spinlock_t eh_lock;
+ spinlock_t ccblist_lock;
+ spinlock_t postq_lock;
+ spinlock_t doneq_lock;
+ spinlock_t rqbuffer_lock;
+ spinlock_t wqbuffer_lock;
union {
struct MessageUnit_A __iomem *pmuA;
struct MessageUnit_B *pmuB;
struct MessageUnit_C __iomem *pmuC;
+ struct MessageUnit_D *pmuD;
+ struct MessageUnit_E __iomem *pmuE;
+ struct MessageUnit_F __iomem *pmuF;
};
/* message unit ATU inbound base address0 */
- void __iomem *mem_base0;
- void __iomem *mem_base1;
- uint32_t acb_flags;
+ void __iomem *mem_base0;
+ void __iomem *mem_base1;
+ //0x000 - COMPORT_IN (Host sent to ROC)
+ uint32_t *message_wbuffer;
+ //0x100 - COMPORT_OUT (ROC sent to Host)
+ uint32_t *message_rbuffer;
+ uint32_t *msgcode_rwbuffer; //0x200 - BIOS_AREA
+ uint32_t acb_flags;
u16 dev_id;
- uint8_t adapter_index;
- #define ACB_F_SCSISTOPADAPTER 0x0001
- #define ACB_F_MSG_STOP_BGRB 0x0002
- /* stop RAID background rebuild */
- #define ACB_F_MSG_START_BGRB 0x0004
- /* stop RAID background rebuild */
- #define ACB_F_IOPDATA_OVERFLOW 0x0008
- /* iop message data rqbuffer overflow */
- #define ACB_F_MESSAGE_WQBUFFER_CLEARED 0x0010
- /* message clear wqbuffer */
- #define ACB_F_MESSAGE_RQBUFFER_CLEARED 0x0020
- /* message clear rqbuffer */
- #define ACB_F_MESSAGE_WQBUFFER_READED 0x0040
- #define ACB_F_BUS_RESET 0x0080
- #define ACB_F_BUS_HANG_ON 0x0800/* need hardware reset bus */
+ uint8_t adapter_index;
+#define ACB_F_SCSISTOPADAPTER 0x0001
+#define ACB_F_MSG_STOP_BGRB 0x0002
+/* stop RAID background rebuild */
+#define ACB_F_MSG_START_BGRB 0x0004
+/* stop RAID background rebuild */
+#define ACB_F_IOPDATA_OVERFLOW 0x0008
+/* iop message data rqbuffer overflow */
+#define ACB_F_MESSAGE_WQBUFFER_CLEARED 0x0010
+/* message clear wqbuffer */
+#define ACB_F_MESSAGE_RQBUFFER_CLEARED 0x0020
+/* message clear rqbuffer */
+#define ACB_F_MESSAGE_WQBUFFER_READED 0x0040
+#define ACB_F_BUS_RESET 0x0080
- #define ACB_F_IOP_INITED 0x0100
- /* iop init */
- #define ACB_F_ABORT 0x0200
- #define ACB_F_FIRMWARE_TRAP 0x0400
- struct CommandControlBlock * pccb_pool[ARCMSR_MAX_FREECCB_NUM];
+#define ACB_F_IOP_INITED 0x0100
+/* iop init */
+#define ACB_F_ABORT 0x0200
+#define ACB_F_FIRMWARE_TRAP 0x0400
+#define ACB_F_ADAPTER_REMOVED 0x0800
+#define ACB_F_MSG_GET_CONFIG 0x1000
+ struct CommandControlBlock * pccb_pool[ARCMSR_MAX_FREECCB_NUM];
/* used for memory free */
- struct list_head ccb_free_list;
+ struct list_head ccb_free_list;
/* head of free ccb list */
- atomic_t ccboutstandingcount;
+ atomic_t ccboutstandingcount;
/*The present outstanding command number that in the IOP that
waiting for being handled by FW*/
- void * dma_coherent;
+ void * dma_coherent;
/* dma_coherent used for memory free */
- dma_addr_t dma_coherent_handle;
+ dma_addr_t dma_coherent_handle;
/* dma_coherent_handle used for memory free */
- dma_addr_t dma_coherent_handle_hbb_mu;
- unsigned int uncache_size;
- uint8_t rqbuffer[ARCMSR_MAX_QBUFFER];
+ dma_addr_t dma_coherent_handle2;
+ void *dma_coherent2;
+ unsigned int uncache_size;
+ uint8_t rqbuffer[ARCMSR_MAX_QBUFFER];
/* data collection buffer for read from 80331 */
- int32_t rqbuf_firstindex;
+ int32_t rqbuf_getIndex;
/* first of read buffer */
- int32_t rqbuf_lastindex;
+ int32_t rqbuf_putIndex;
/* last of read buffer */
- uint8_t wqbuffer[ARCMSR_MAX_QBUFFER];
+ uint8_t wqbuffer[ARCMSR_MAX_QBUFFER];
/* data collection buffer for write to 80331 */
- int32_t wqbuf_firstindex;
+ int32_t wqbuf_getIndex;
/* first of write buffer */
- int32_t wqbuf_lastindex;
+ int32_t wqbuf_putIndex;
/* last of write buffer */
- uint8_t devstate[ARCMSR_MAX_TARGETID][ARCMSR_MAX_TARGETLUN];
+ uint8_t devstate[ARCMSR_MAX_TARGETID][ARCMSR_MAX_TARGETLUN];
/* id0 ..... id15, lun0...lun7 */
-#define ARECA_RAID_GONE 0x55
-#define ARECA_RAID_GOOD 0xaa
- uint32_t num_resets;
- uint32_t num_aborts;
- uint32_t signature;
- uint32_t firm_request_len;
- uint32_t firm_numbers_queue;
- uint32_t firm_sdram_size;
- uint32_t firm_hd_channels;
- uint32_t firm_cfg_version;
+#define ARECA_RAID_GONE 0x55
+#define ARECA_RAID_GOOD 0xaa
+ uint32_t num_resets;
+ uint32_t num_aborts;
+ uint32_t signature;
+ uint32_t firm_request_len;
+ uint32_t firm_numbers_queue;
+ uint32_t firm_sdram_size;
+ uint32_t firm_hd_channels;
+ uint32_t firm_cfg_version;
char firm_model[12];
char firm_version[20];
char device_map[20]; /*21,84-99*/
- struct work_struct arcmsr_do_message_isr_bh;
- struct timer_list eternal_timer;
+ uint32_t firm_PicStatus;
+ struct work_struct arcmsr_do_message_isr_bh;
+ struct timer_list eternal_timer;
unsigned short fw_flag;
- #define FW_NORMAL 0x0000
- #define FW_BOG 0x0001
- #define FW_DEADLOCK 0x0010
- atomic_t rq_map_token;
- atomic_t ante_token_value;
+#define FW_NORMAL 0x0000
+#define FW_BOG 0x0001
+#define FW_DEADLOCK 0x0010
+ uint32_t maxOutstanding;
+ int vector_count;
+ int xor_mega;
+ uint32_t maxFreeCCB;
+ struct timer_list refresh_timer;
+ uint32_t doneq_index;
+ uint32_t ccbsize;
+ uint32_t in_doorbell;
+ uint32_t out_doorbell;
+ uint32_t completionQ_entry;
+ pCompletion_Q pCompletionQ;
+ uint32_t completeQ_size;
+ void *xorVirt;
+ dma_addr_t xorPhys;
+ unsigned int init2cfg_size;
+ unsigned int xorVirtOffset;
};/* HW_DEVICE_EXTENSION */
/*
*******************************************************************************
@@ -602,30 +981,31 @@ struct AdapterControlBlock
*******************************************************************************
*/
struct CommandControlBlock{
- /*x32:sizeof struct_CCB=(32+60)byte, x64:sizeof struct_CCB=(64+60)byte*/
- struct list_head list; /*x32: 8byte, x64: 16byte*/
- struct scsi_cmnd *pcmd; /*8 bytes pointer of linux scsi command */
- struct AdapterControlBlock *acb; /*x32: 4byte, x64: 8byte*/
- uint32_t cdb_phyaddr_pattern; /*x32: 4byte, x64: 4byte*/
- uint32_t arc_cdb_size; /*x32:4byte,x64:4byte*/
- uint16_t ccb_flags; /*x32: 2byte, x64: 2byte*/
- #define CCB_FLAG_READ 0x0000
- #define CCB_FLAG_WRITE 0x0001
- #define CCB_FLAG_ERROR 0x0002
- #define CCB_FLAG_FLUSHCACHE 0x0004
- #define CCB_FLAG_MASTER_ABORTED 0x0008
- uint16_t startdone; /*x32:2byte,x32:2byte*/
- #define ARCMSR_CCB_DONE 0x0000
- #define ARCMSR_CCB_START 0x55AA
- #define ARCMSR_CCB_ABORTED 0xAA55
- #define ARCMSR_CCB_ILLEGAL 0xFFFF
- #if BITS_PER_LONG == 64
+ /*x32:sizeof struct_CCB=(64+60)byte, x64:sizeof struct_CCB=(64+60)byte*/
+ struct list_head list; /*x32: 8byte, x64: 16byte*/
+ struct scsi_cmnd *pcmd; /*8 bytes pointer of linux scsi command */
+ struct AdapterControlBlock *acb; /*x32: 4byte, x64: 8byte*/
+ unsigned long cdb_phyaddr; /*x32: 4byte, x64: 8byte*/
+ uint32_t arc_cdb_size; /*x32:4byte,x64:4byte*/
+ uint16_t ccb_flags; /*x32: 2byte, x64: 2byte*/
+#define CCB_FLAG_READ 0x0000
+#define CCB_FLAG_WRITE 0x0001
+#define CCB_FLAG_ERROR 0x0002
+#define CCB_FLAG_FLUSHCACHE 0x0004
+#define CCB_FLAG_MASTER_ABORTED 0x0008
+ uint16_t startdone; /*x32:2byte,x32:2byte*/
+#define ARCMSR_CCB_DONE 0x0000
+#define ARCMSR_CCB_START 0x55AA
+#define ARCMSR_CCB_ABORTED 0xAA55
+#define ARCMSR_CCB_ILLEGAL 0xFFFF
+ uint32_t smid;
+#if BITS_PER_LONG == 64
/* ======================512+64 bytes======================== */
- uint32_t reserved[5]; /*24 byte*/
- #else
+ uint32_t reserved[3]; /*12 byte*/
+#else
/* ======================512+32 bytes======================== */
- uint32_t reserved; /*8 byte*/
- #endif
+ uint32_t reserved[8]; /*32 byte*/
+#endif
/* ======================================================= */
struct ARCMSR_CDB arcmsr_cdb;
};
@@ -659,13 +1039,13 @@ struct SENSE_DATA
** Outbound Interrupt Status Register - OISR
*******************************************************************************
*/
-#define ARCMSR_MU_OUTBOUND_INTERRUPT_STATUS_REG 0x30
-#define ARCMSR_MU_OUTBOUND_PCI_INT 0x10
-#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INT 0x08
-#define ARCMSR_MU_OUTBOUND_DOORBELL_INT 0x04
-#define ARCMSR_MU_OUTBOUND_MESSAGE1_INT 0x02
-#define ARCMSR_MU_OUTBOUND_MESSAGE0_INT 0x01
-#define ARCMSR_MU_OUTBOUND_HANDLE_INT \
+#define ARCMSR_MU_OUTBOUND_INTERRUPT_STATUS_REG 0x30
+#define ARCMSR_MU_OUTBOUND_PCI_INT 0x10
+#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INT 0x08
+#define ARCMSR_MU_OUTBOUND_DOORBELL_INT 0x04
+#define ARCMSR_MU_OUTBOUND_MESSAGE1_INT 0x02
+#define ARCMSR_MU_OUTBOUND_MESSAGE0_INT 0x01
+#define ARCMSR_MU_OUTBOUND_HANDLE_INT \
(ARCMSR_MU_OUTBOUND_MESSAGE0_INT \
|ARCMSR_MU_OUTBOUND_MESSAGE1_INT \
|ARCMSR_MU_OUTBOUND_DOORBELL_INT \
@@ -676,17 +1056,19 @@ struct SENSE_DATA
** Outbound Interrupt Mask Register - OIMR
*******************************************************************************
*/
-#define ARCMSR_MU_OUTBOUND_INTERRUPT_MASK_REG 0x34
-#define ARCMSR_MU_OUTBOUND_PCI_INTMASKENABLE 0x10
-#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE 0x08
-#define ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE 0x04
-#define ARCMSR_MU_OUTBOUND_MESSAGE1_INTMASKENABLE 0x02
-#define ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE 0x01
-#define ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE 0x1F
+#define ARCMSR_MU_OUTBOUND_INTERRUPT_MASK_REG 0x34
+#define ARCMSR_MU_OUTBOUND_PCI_INTMASKENABLE 0x10
+#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE 0x08
+#define ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE 0x04
+#define ARCMSR_MU_OUTBOUND_MESSAGE1_INTMASKENABLE 0x02
+#define ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE 0x01
+#define ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE 0x1F
-extern void arcmsr_post_ioctldata2iop(struct AdapterControlBlock *);
-extern void arcmsr_iop_message_read(struct AdapterControlBlock *);
+extern void arcmsr_write_ioctldata2iop(struct AdapterControlBlock *);
+extern uint32_t arcmsr_Read_iop_rqbuffer_data(struct AdapterControlBlock *,
+ struct QBUFFER __iomem *);
+extern void arcmsr_clear_iop2drv_rqueue_buffer(struct AdapterControlBlock *);
extern struct QBUFFER __iomem *arcmsr_get_iop_rqbuffer(struct AdapterControlBlock *);
-extern struct device_attribute *arcmsr_host_attrs[];
+extern const struct attribute_group *arcmsr_host_groups[];
extern int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *);
void arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb);
diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c
index acdae33de521..1990af2bef95 100644
--- a/drivers/scsi/arcmsr/arcmsr_attr.c
+++ b/drivers/scsi/arcmsr/arcmsr_attr.c
@@ -41,7 +41,7 @@
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************
** For history of changes, see Documentation/scsi/ChangeLog.arcmsr
-** Firmware Specification, see Documentation/scsi/arcmsr_spec.txt
+** Firmware Specification, see Documentation/scsi/arcmsr_spec.rst
*******************************************************************************
*/
#include <linux/module.h>
@@ -50,6 +50,7 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pci.h>
+#include <linux/circ_buf.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -57,107 +58,104 @@
#include <scsi/scsi_transport.h>
#include "arcmsr.h"
-struct device_attribute *arcmsr_host_attrs[];
-
static ssize_t arcmsr_sysfs_iop_message_read(struct file *filp,
struct kobject *kobj,
- struct bin_attribute *bin,
+ const struct bin_attribute *bin,
char *buf, loff_t off,
size_t count)
{
struct device *dev = container_of(kobj,struct device,kobj);
struct Scsi_Host *host = class_to_shost(dev);
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
- uint8_t *pQbuffer,*ptmpQbuffer;
+ uint8_t *ptmpQbuffer;
int32_t allxfer_len = 0;
+ unsigned long flags;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
/* do message unit read. */
ptmpQbuffer = (uint8_t *)buf;
- while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex)
- && (allxfer_len < 1031)) {
- pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex];
- memcpy(ptmpQbuffer, pQbuffer, 1);
- acb->rqbuf_firstindex++;
- acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER;
- ptmpQbuffer++;
- allxfer_len++;
+ spin_lock_irqsave(&acb->rqbuffer_lock, flags);
+ if (acb->rqbuf_getIndex != acb->rqbuf_putIndex) {
+ unsigned int tail = acb->rqbuf_getIndex;
+ unsigned int head = acb->rqbuf_putIndex;
+ unsigned int cnt_to_end = CIRC_CNT_TO_END(head, tail, ARCMSR_MAX_QBUFFER);
+
+ allxfer_len = CIRC_CNT(head, tail, ARCMSR_MAX_QBUFFER);
+ if (allxfer_len > ARCMSR_API_DATA_BUFLEN)
+ allxfer_len = ARCMSR_API_DATA_BUFLEN;
+
+ if (allxfer_len <= cnt_to_end)
+ memcpy(ptmpQbuffer, acb->rqbuffer + tail, allxfer_len);
+ else {
+ memcpy(ptmpQbuffer, acb->rqbuffer + tail, cnt_to_end);
+ memcpy(ptmpQbuffer + cnt_to_end, acb->rqbuffer, allxfer_len - cnt_to_end);
+ }
+ acb->rqbuf_getIndex = (acb->rqbuf_getIndex + allxfer_len) % ARCMSR_MAX_QBUFFER;
}
if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
struct QBUFFER __iomem *prbuffer;
- uint8_t __iomem *iop_data;
- int32_t iop_len;
-
acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
prbuffer = arcmsr_get_iop_rqbuffer(acb);
- iop_data = prbuffer->data;
- iop_len = readl(&prbuffer->data_len);
- while (iop_len > 0) {
- acb->rqbuffer[acb->rqbuf_lastindex] = readb(iop_data);
- acb->rqbuf_lastindex++;
- acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER;
- iop_data++;
- iop_len--;
- }
- arcmsr_iop_message_read(acb);
+ if (arcmsr_Read_iop_rqbuffer_data(acb, prbuffer) == 0)
+ acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
}
- return (allxfer_len);
+ spin_unlock_irqrestore(&acb->rqbuffer_lock, flags);
+ return allxfer_len;
}
static ssize_t arcmsr_sysfs_iop_message_write(struct file *filp,
struct kobject *kobj,
- struct bin_attribute *bin,
+ const struct bin_attribute *bin,
char *buf, loff_t off,
size_t count)
{
struct device *dev = container_of(kobj,struct device,kobj);
struct Scsi_Host *host = class_to_shost(dev);
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
- int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex;
+ int32_t user_len, cnt2end;
uint8_t *pQbuffer, *ptmpuserbuffer;
+ unsigned long flags;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (count > 1032)
+ if (count > ARCMSR_API_DATA_BUFLEN)
return -EINVAL;
/* do message unit write. */
ptmpuserbuffer = (uint8_t *)buf;
user_len = (int32_t)count;
- wqbuf_lastindex = acb->wqbuf_lastindex;
- wqbuf_firstindex = acb->wqbuf_firstindex;
- if (wqbuf_lastindex != wqbuf_firstindex) {
- arcmsr_post_ioctldata2iop(acb);
+ spin_lock_irqsave(&acb->wqbuffer_lock, flags);
+ if (acb->wqbuf_putIndex != acb->wqbuf_getIndex) {
+ arcmsr_write_ioctldata2iop(acb);
+ spin_unlock_irqrestore(&acb->wqbuffer_lock, flags);
return 0; /*need retry*/
} else {
- my_empty_len = (wqbuf_firstindex-wqbuf_lastindex - 1)
- &(ARCMSR_MAX_QBUFFER - 1);
- if (my_empty_len >= user_len) {
- while (user_len > 0) {
- pQbuffer =
- &acb->wqbuffer[acb->wqbuf_lastindex];
- memcpy(pQbuffer, ptmpuserbuffer, 1);
- acb->wqbuf_lastindex++;
- acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER;
- ptmpuserbuffer++;
- user_len--;
- }
- if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) {
- acb->acb_flags &=
- ~ACB_F_MESSAGE_WQBUFFER_CLEARED;
- arcmsr_post_ioctldata2iop(acb);
- }
- return count;
- } else {
- return 0; /*need retry*/
+ pQbuffer = &acb->wqbuffer[acb->wqbuf_putIndex];
+ cnt2end = ARCMSR_MAX_QBUFFER - acb->wqbuf_putIndex;
+ if (user_len > cnt2end) {
+ memcpy(pQbuffer, ptmpuserbuffer, cnt2end);
+ ptmpuserbuffer += cnt2end;
+ user_len -= cnt2end;
+ acb->wqbuf_putIndex = 0;
+ pQbuffer = acb->wqbuffer;
+ }
+ memcpy(pQbuffer, ptmpuserbuffer, user_len);
+ acb->wqbuf_putIndex += user_len;
+ acb->wqbuf_putIndex %= ARCMSR_MAX_QBUFFER;
+ if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) {
+ acb->acb_flags &=
+ ~ACB_F_MESSAGE_WQBUFFER_CLEARED;
+ arcmsr_write_ioctldata2iop(acb);
}
+ spin_unlock_irqrestore(&acb->wqbuffer_lock, flags);
+ return count;
}
}
static ssize_t arcmsr_sysfs_iop_message_clear(struct file *filp,
struct kobject *kobj,
- struct bin_attribute *bin,
+ const struct bin_attribute *bin,
char *buf, loff_t off,
size_t count)
{
@@ -165,22 +163,24 @@ static ssize_t arcmsr_sysfs_iop_message_clear(struct file *filp,
struct Scsi_Host *host = class_to_shost(dev);
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
uint8_t *pQbuffer;
+ unsigned long flags;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
- acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
- arcmsr_iop_message_read(acb);
- }
+ arcmsr_clear_iop2drv_rqueue_buffer(acb);
acb->acb_flags |=
(ACB_F_MESSAGE_WQBUFFER_CLEARED
| ACB_F_MESSAGE_RQBUFFER_CLEARED
| ACB_F_MESSAGE_WQBUFFER_READED);
- acb->rqbuf_firstindex = 0;
- acb->rqbuf_lastindex = 0;
- acb->wqbuf_firstindex = 0;
- acb->wqbuf_lastindex = 0;
+ spin_lock_irqsave(&acb->rqbuffer_lock, flags);
+ acb->rqbuf_getIndex = 0;
+ acb->rqbuf_putIndex = 0;
+ spin_unlock_irqrestore(&acb->rqbuffer_lock, flags);
+ spin_lock_irqsave(&acb->wqbuffer_lock, flags);
+ acb->wqbuf_getIndex = 0;
+ acb->wqbuf_putIndex = 0;
+ spin_unlock_irqrestore(&acb->wqbuffer_lock, flags);
pQbuffer = acb->rqbuffer;
memset(pQbuffer, 0, sizeof (struct QBUFFER));
pQbuffer = acb->wqbuffer;
@@ -188,25 +188,25 @@ static ssize_t arcmsr_sysfs_iop_message_clear(struct file *filp,
return 1;
}
-static struct bin_attribute arcmsr_sysfs_message_read_attr = {
+static const struct bin_attribute arcmsr_sysfs_message_read_attr = {
.attr = {
.name = "mu_read",
.mode = S_IRUSR ,
},
- .size = 1032,
+ .size = ARCMSR_API_DATA_BUFLEN,
.read = arcmsr_sysfs_iop_message_read,
};
-static struct bin_attribute arcmsr_sysfs_message_write_attr = {
+static const struct bin_attribute arcmsr_sysfs_message_write_attr = {
.attr = {
.name = "mu_write",
.mode = S_IWUSR,
},
- .size = 1032,
+ .size = ARCMSR_API_DATA_BUFLEN,
.write = arcmsr_sysfs_iop_message_write,
};
-static struct bin_attribute arcmsr_sysfs_message_clear_attr = {
+static const struct bin_attribute arcmsr_sysfs_message_clear_attr = {
.attr = {
.name = "mu_clear",
.mode = S_IWUSR,
@@ -387,16 +387,25 @@ static DEVICE_ATTR(host_fw_numbers_queue, S_IRUGO, arcmsr_attr_host_fw_numbers_q
static DEVICE_ATTR(host_fw_sdram_size, S_IRUGO, arcmsr_attr_host_fw_sdram_size, NULL);
static DEVICE_ATTR(host_fw_hd_channels, S_IRUGO, arcmsr_attr_host_fw_hd_channels, NULL);
-struct device_attribute *arcmsr_host_attrs[] = {
- &dev_attr_host_driver_version,
- &dev_attr_host_driver_posted_cmd,
- &dev_attr_host_driver_reset,
- &dev_attr_host_driver_abort,
- &dev_attr_host_fw_model,
- &dev_attr_host_fw_version,
- &dev_attr_host_fw_request_len,
- &dev_attr_host_fw_numbers_queue,
- &dev_attr_host_fw_sdram_size,
- &dev_attr_host_fw_hd_channels,
+static struct attribute *arcmsr_host_attrs[] = {
+ &dev_attr_host_driver_version.attr,
+ &dev_attr_host_driver_posted_cmd.attr,
+ &dev_attr_host_driver_reset.attr,
+ &dev_attr_host_driver_abort.attr,
+ &dev_attr_host_fw_model.attr,
+ &dev_attr_host_fw_version.attr,
+ &dev_attr_host_fw_request_len.attr,
+ &dev_attr_host_fw_numbers_queue.attr,
+ &dev_attr_host_fw_sdram_size.attr,
+ &dev_attr_host_fw_hd_channels.attr,
NULL,
};
+
+static const struct attribute_group arcmsr_host_attr_group = {
+ .attrs = arcmsr_host_attrs,
+};
+
+const struct attribute_group *arcmsr_host_groups[] = {
+ &arcmsr_host_attr_group,
+ NULL
+};
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index f980600f78a8..f0c5a30ce51b 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -2,11 +2,10 @@
*******************************************************************************
** O.S : Linux
** FILE NAME : arcmsr_hba.c
-** BY : Nick Cheng
-** Description: SCSI RAID Device Driver for
-** ARECA RAID Host adapter
+** BY : Nick Cheng, C.L. Huang
+** Description: SCSI RAID Device Driver for Areca RAID Controller
*******************************************************************************
-** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved
+** Copyright (C) 2002 - 2014, Areca Technology Corporation All rights reserved
**
** Web site: www.areca.com.tw
** E-mail: support@areca.com.tw
@@ -42,7 +41,7 @@
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************
** For history of changes, see Documentation/scsi/ChangeLog.arcmsr
-** Firmware Specification, see Documentation/scsi/arcmsr_spec.txt
+** Firmware Specification, see Documentation/scsi/arcmsr_spec.rst
*******************************************************************************
*/
#include <linux/module.h>
@@ -58,11 +57,10 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/aer.h>
+#include <linux/circ_buf.h>
#include <asm/dma.h>
#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -71,102 +69,172 @@
#include <scsi/scsi_transport.h>
#include <scsi/scsicam.h>
#include "arcmsr.h"
-MODULE_AUTHOR("Nick Cheng <support@areca.com.tw>");
-MODULE_DESCRIPTION("ARECA (ARC11xx/12xx/16xx/1880) SATA/SAS RAID Host Bus Adapter");
+MODULE_AUTHOR("Nick Cheng, C.L. Huang <support@areca.com.tw>");
+MODULE_DESCRIPTION("Areca ARC11xx/12xx/16xx/188x SAS/SATA RAID Controller Driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(ARCMSR_DRIVER_VERSION);
+static int msix_enable = 1;
+module_param(msix_enable, int, S_IRUGO);
+MODULE_PARM_DESC(msix_enable, "Enable MSI-X interrupt(0 ~ 1), msix_enable=1(enable), =0(disable)");
+
+static int msi_enable = 1;
+module_param(msi_enable, int, S_IRUGO);
+MODULE_PARM_DESC(msi_enable, "Enable MSI interrupt(0 ~ 1), msi_enable=1(enable), =0(disable)");
+
+static int host_can_queue = ARCMSR_DEFAULT_OUTSTANDING_CMD;
+module_param(host_can_queue, int, S_IRUGO);
+MODULE_PARM_DESC(host_can_queue, " adapter queue depth(32 ~ 1024), default is 128");
+
+static int cmd_per_lun = ARCMSR_DEFAULT_CMD_PERLUN;
+module_param(cmd_per_lun, int, S_IRUGO);
+MODULE_PARM_DESC(cmd_per_lun, " device queue depth(1 ~ 128), default is 32");
+
+static int dma_mask_64 = 0;
+module_param(dma_mask_64, int, S_IRUGO);
+MODULE_PARM_DESC(dma_mask_64, " set DMA mask to 64 bits(0 ~ 1), dma_mask_64=1(64 bits), =0(32 bits)");
+
+static int set_date_time = 0;
+module_param(set_date_time, int, S_IRUGO);
+MODULE_PARM_DESC(set_date_time, " send date, time to iop(0 ~ 1), set_date_time=1(enable), default(=0) is disable");
+
+static int cmd_timeout = ARCMSR_DEFAULT_TIMEOUT;
+module_param(cmd_timeout, int, S_IRUGO);
+MODULE_PARM_DESC(cmd_timeout, " scsi cmd timeout(0 ~ 120 sec.), default is 90");
+
#define ARCMSR_SLEEPTIME 10
#define ARCMSR_RETRYCOUNT 12
-wait_queue_head_t wait_q;
+static wait_queue_head_t wait_q;
static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb,
struct scsi_cmnd *cmd);
static int arcmsr_iop_confirm(struct AdapterControlBlock *acb);
static int arcmsr_abort(struct scsi_cmnd *);
static int arcmsr_bus_reset(struct scsi_cmnd *);
static int arcmsr_bios_param(struct scsi_device *sdev,
- struct block_device *bdev, sector_t capacity, int *info);
+ struct gendisk *disk, sector_t capacity, int *info);
static int arcmsr_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd);
static int arcmsr_probe(struct pci_dev *pdev,
const struct pci_device_id *id);
+static int __maybe_unused arcmsr_suspend(struct device *dev);
+static int __maybe_unused arcmsr_resume(struct device *dev);
static void arcmsr_remove(struct pci_dev *pdev);
static void arcmsr_shutdown(struct pci_dev *pdev);
static void arcmsr_iop_init(struct AdapterControlBlock *acb);
static void arcmsr_free_ccb_pool(struct AdapterControlBlock *acb);
static u32 arcmsr_disable_outbound_ints(struct AdapterControlBlock *acb);
+static void arcmsr_enable_outbound_ints(struct AdapterControlBlock *acb,
+ u32 intmask_org);
static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb);
-static void arcmsr_flush_hba_cache(struct AdapterControlBlock *acb);
-static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb);
-static void arcmsr_request_device_map(unsigned long pacb);
-static void arcmsr_request_hba_device_map(struct AdapterControlBlock *acb);
-static void arcmsr_request_hbb_device_map(struct AdapterControlBlock *acb);
-static void arcmsr_request_hbc_device_map(struct AdapterControlBlock *acb);
+static void arcmsr_hbaA_flush_cache(struct AdapterControlBlock *acb);
+static void arcmsr_hbaB_flush_cache(struct AdapterControlBlock *acb);
+static void arcmsr_request_device_map(struct timer_list *t);
static void arcmsr_message_isr_bh_fn(struct work_struct *work);
static bool arcmsr_get_firmware_spec(struct AdapterControlBlock *acb);
static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb);
-static void arcmsr_hbc_message_isr(struct AdapterControlBlock *pACB);
+static void arcmsr_hbaC_message_isr(struct AdapterControlBlock *pACB);
+static void arcmsr_hbaD_message_isr(struct AdapterControlBlock *acb);
+static void arcmsr_hbaE_message_isr(struct AdapterControlBlock *acb);
+static void arcmsr_hbaE_postqueue_isr(struct AdapterControlBlock *acb);
+static void arcmsr_hbaF_postqueue_isr(struct AdapterControlBlock *acb);
static void arcmsr_hardware_reset(struct AdapterControlBlock *acb);
static const char *arcmsr_info(struct Scsi_Host *);
static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
-static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth, int reason)
+static void arcmsr_free_irq(struct pci_dev *, struct AdapterControlBlock *);
+static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb);
+static void arcmsr_set_iop_datetime(struct timer_list *);
+static int arcmsr_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim);
+static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
queue_depth = ARCMSR_MAX_CMD_PERLUN;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
+ return scsi_change_queue_depth(sdev, queue_depth);
}
-static struct scsi_host_template arcmsr_scsi_host_template = {
+static const struct scsi_host_template arcmsr_scsi_host_template = {
.module = THIS_MODULE,
- .name = "ARCMSR ARECA SATA/SAS RAID Controller"
- ARCMSR_DRIVER_VERSION,
+ .proc_name = ARCMSR_NAME,
+ .name = "Areca SAS/SATA RAID driver",
.info = arcmsr_info,
.queuecommand = arcmsr_queue_command,
- .eh_abort_handler = arcmsr_abort,
+ .eh_abort_handler = arcmsr_abort,
.eh_bus_reset_handler = arcmsr_bus_reset,
.bios_param = arcmsr_bios_param,
+ .sdev_configure = arcmsr_sdev_configure,
.change_queue_depth = arcmsr_adjust_disk_queue_depth,
- .can_queue = ARCMSR_MAX_FREECCB_NUM,
- .this_id = ARCMSR_SCSI_INITIATOR_ID,
- .sg_tablesize = ARCMSR_DEFAULT_SG_ENTRIES,
- .max_sectors = ARCMSR_MAX_XFER_SECTORS_C,
- .cmd_per_lun = ARCMSR_MAX_CMD_PERLUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = arcmsr_host_attrs,
+ .can_queue = ARCMSR_DEFAULT_OUTSTANDING_CMD,
+ .this_id = ARCMSR_SCSI_INITIATOR_ID,
+ .sg_tablesize = ARCMSR_DEFAULT_SG_ENTRIES,
+ .max_sectors = ARCMSR_MAX_XFER_SECTORS_C,
+ .cmd_per_lun = ARCMSR_DEFAULT_CMD_PERLUN,
+ .shost_groups = arcmsr_host_groups,
+ .no_write_same = 1,
};
-static struct pci_device_id arcmsr_device_id_table[] = {
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1110)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1120)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1130)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1160)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1170)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1200)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1201)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1202)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1210)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1220)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1230)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1260)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1270)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1280)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1380)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1381)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1680)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1681)},
- {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1880)},
+
+static const struct pci_device_id arcmsr_device_id_table[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1110),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1120),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1130),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1160),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1170),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1200),
+ .driver_data = ACB_ADAPTER_TYPE_B},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1201),
+ .driver_data = ACB_ADAPTER_TYPE_B},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1202),
+ .driver_data = ACB_ADAPTER_TYPE_B},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1203),
+ .driver_data = ACB_ADAPTER_TYPE_B},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1210),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1214),
+ .driver_data = ACB_ADAPTER_TYPE_D},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1220),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1230),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1260),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1270),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1280),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1380),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1381),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1680),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1681),
+ .driver_data = ACB_ADAPTER_TYPE_A},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1880),
+ .driver_data = ACB_ADAPTER_TYPE_C},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1883),
+ .driver_data = ACB_ADAPTER_TYPE_C},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1884),
+ .driver_data = ACB_ADAPTER_TYPE_E},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1886_0),
+ .driver_data = ACB_ADAPTER_TYPE_F},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1886),
+ .driver_data = ACB_ADAPTER_TYPE_F},
{0, 0}, /* Terminating entry */
};
MODULE_DEVICE_TABLE(pci, arcmsr_device_id_table);
+
+static SIMPLE_DEV_PM_OPS(arcmsr_pm_ops, arcmsr_suspend, arcmsr_resume);
+
static struct pci_driver arcmsr_pci_driver = {
.name = "arcmsr",
- .id_table = arcmsr_device_id_table,
+ .id_table = arcmsr_device_id_table,
.probe = arcmsr_probe,
.remove = arcmsr_remove,
+ .driver.pm = &arcmsr_pm_ops,
.shutdown = arcmsr_shutdown,
};
/*
@@ -174,17 +242,16 @@ static struct pci_driver arcmsr_pci_driver = {
****************************************************************************
*/
-static void arcmsr_free_hbb_mu(struct AdapterControlBlock *acb)
+static void arcmsr_free_io_queue(struct AdapterControlBlock *acb)
{
switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A:
- case ACB_ADAPTER_TYPE_C:
+ case ACB_ADAPTER_TYPE_B:
+ case ACB_ADAPTER_TYPE_D:
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ dma_free_coherent(&acb->pdev->dev, acb->ioqueue_size,
+ acb->dma_coherent2, acb->dma_coherent_handle2);
break;
- case ACB_ADAPTER_TYPE_B:{
- dma_free_coherent(&acb->pdev->dev,
- sizeof(struct MessageUnit_B),
- acb->pmuB, acb->dma_coherent_handle_hbb_mu);
- }
}
}
@@ -218,7 +285,7 @@ static bool arcmsr_remap_pciregion(struct AdapterControlBlock *acb)
break;
}
case ACB_ADAPTER_TYPE_C:{
- acb->pmuC = ioremap_nocache(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
+ acb->pmuC = ioremap(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
if (!acb->pmuC) {
printk(KERN_NOTICE "arcmsr%d: memory mapping region fail \n", acb->host->host_no);
return false;
@@ -229,6 +296,48 @@ static bool arcmsr_remap_pciregion(struct AdapterControlBlock *acb)
}
break;
}
+ case ACB_ADAPTER_TYPE_D: {
+ void __iomem *mem_base0;
+ unsigned long addr, range;
+
+ addr = (unsigned long)pci_resource_start(pdev, 0);
+ range = pci_resource_len(pdev, 0);
+ mem_base0 = ioremap(addr, range);
+ if (!mem_base0) {
+ pr_notice("arcmsr%d: memory mapping region fail\n",
+ acb->host->host_no);
+ return false;
+ }
+ acb->mem_base0 = mem_base0;
+ break;
+ }
+ case ACB_ADAPTER_TYPE_E: {
+ acb->pmuE = ioremap(pci_resource_start(pdev, 1),
+ pci_resource_len(pdev, 1));
+ if (!acb->pmuE) {
+ pr_notice("arcmsr%d: memory mapping region fail \n",
+ acb->host->host_no);
+ return false;
+ }
+ writel(0, &acb->pmuE->host_int_status); /*clear interrupt*/
+ writel(ARCMSR_HBEMU_DOORBELL_SYNC, &acb->pmuE->iobound_doorbell); /* synchronize doorbell to 0 */
+ acb->in_doorbell = 0;
+ acb->out_doorbell = 0;
+ break;
+ }
+ case ACB_ADAPTER_TYPE_F: {
+ acb->pmuF = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ if (!acb->pmuF) {
+ pr_notice("arcmsr%d: memory mapping region fail\n",
+ acb->host->host_no);
+ return false;
+ }
+ writel(0, &acb->pmuF->host_int_status); /* clear interrupt */
+ writel(ARCMSR_HBFMU_DOORBELL_SYNC, &acb->pmuF->iobound_doorbell);
+ acb->in_doorbell = 0;
+ acb->out_doorbell = 0;
+ break;
+ }
}
return true;
}
@@ -236,19 +345,25 @@ static bool arcmsr_remap_pciregion(struct AdapterControlBlock *acb)
static void arcmsr_unmap_pciregion(struct AdapterControlBlock *acb)
{
switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A:{
+ case ACB_ADAPTER_TYPE_A:
iounmap(acb->pmuA);
- }
- break;
- case ACB_ADAPTER_TYPE_B:{
+ break;
+ case ACB_ADAPTER_TYPE_B:
iounmap(acb->mem_base0);
iounmap(acb->mem_base1);
- }
-
- break;
- case ACB_ADAPTER_TYPE_C:{
+ break;
+ case ACB_ADAPTER_TYPE_C:
iounmap(acb->pmuC);
- }
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ iounmap(acb->mem_base0);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ iounmap(acb->pmuE);
+ break;
+ case ACB_ADAPTER_TYPE_F:
+ iounmap(acb->pmuF);
+ break;
}
}
@@ -262,18 +377,13 @@ static irqreturn_t arcmsr_do_interrupt(int irq, void *dev_id)
}
static int arcmsr_bios_param(struct scsi_device *sdev,
- struct block_device *bdev, sector_t capacity, int *geom)
+ struct gendisk *disk, sector_t capacity, int *geom)
{
- int ret, heads, sectors, cylinders, total_capacity;
- unsigned char *buffer;/* return copy of block device's partition table */
+ int heads, sectors, cylinders, total_capacity;
+
+ if (scsi_partsize(disk, capacity, geom))
+ return 0;
- buffer = scsi_bios_ptable(bdev);
- if (buffer) {
- ret = scsi_partsize(buffer, capacity, &geom[2], &geom[0], &geom[1]);
- kfree(buffer);
- if (ret != -1)
- return ret;
- }
total_capacity = capacity;
heads = 64;
sectors = 32;
@@ -289,27 +399,7 @@ static int arcmsr_bios_param(struct scsi_device *sdev,
return 0;
}
-static void arcmsr_define_adapter_type(struct AdapterControlBlock *acb)
-{
- struct pci_dev *pdev = acb->pdev;
- u16 dev_id;
- pci_read_config_word(pdev, PCI_DEVICE_ID, &dev_id);
- acb->dev_id = dev_id;
- switch (dev_id) {
- case 0x1880: {
- acb->adapter_type = ACB_ADAPTER_TYPE_C;
- }
- break;
- case 0x1201: {
- acb->adapter_type = ACB_ADAPTER_TYPE_B;
- }
- break;
-
- default: acb->adapter_type = ACB_ADAPTER_TYPE_A;
- }
-}
-
-static uint8_t arcmsr_hba_wait_msgint_ready(struct AdapterControlBlock *acb)
+static uint8_t arcmsr_hbaA_wait_msgint_ready(struct AdapterControlBlock *acb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
int i;
@@ -327,7 +417,7 @@ static uint8_t arcmsr_hba_wait_msgint_ready(struct AdapterControlBlock *acb)
return false;
}
-static uint8_t arcmsr_hbb_wait_msgint_ready(struct AdapterControlBlock *acb)
+static uint8_t arcmsr_hbaB_wait_msgint_ready(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
int i;
@@ -347,9 +437,9 @@ static uint8_t arcmsr_hbb_wait_msgint_ready(struct AdapterControlBlock *acb)
return false;
}
-static uint8_t arcmsr_hbc_wait_msgint_ready(struct AdapterControlBlock *pACB)
+static uint8_t arcmsr_hbaC_wait_msgint_ready(struct AdapterControlBlock *pACB)
{
- struct MessageUnit_C *phbcmu = (struct MessageUnit_C *)pACB->pmuC;
+ struct MessageUnit_C __iomem *phbcmu = pACB->pmuC;
int i;
for (i = 0; i < 2000; i++) {
@@ -365,13 +455,48 @@ static uint8_t arcmsr_hbc_wait_msgint_ready(struct AdapterControlBlock *pACB)
return false;
}
-static void arcmsr_flush_hba_cache(struct AdapterControlBlock *acb)
+static bool arcmsr_hbaD_wait_msgint_ready(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_D *reg = pACB->pmuD;
+ int i;
+
+ for (i = 0; i < 2000; i++) {
+ if (readl(reg->outbound_doorbell)
+ & ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE) {
+ writel(ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE,
+ reg->outbound_doorbell);
+ return true;
+ }
+ msleep(10);
+ } /* max 20 seconds */
+ return false;
+}
+
+static bool arcmsr_hbaE_wait_msgint_ready(struct AdapterControlBlock *pACB)
+{
+ int i;
+ uint32_t read_doorbell;
+ struct MessageUnit_E __iomem *phbcmu = pACB->pmuE;
+
+ for (i = 0; i < 2000; i++) {
+ read_doorbell = readl(&phbcmu->iobound_doorbell);
+ if ((read_doorbell ^ pACB->in_doorbell) & ARCMSR_HBEMU_IOP2DRV_MESSAGE_CMD_DONE) {
+ writel(0, &phbcmu->host_int_status); /*clear interrupt*/
+ pACB->in_doorbell = read_doorbell;
+ return true;
+ }
+ msleep(10);
+ } /* max 20 seconds */
+ return false;
+}
+
+static void arcmsr_hbaA_flush_cache(struct AdapterControlBlock *acb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
int retry_count = 30;
writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, &reg->inbound_msgaddr0);
do {
- if (arcmsr_hba_wait_msgint_ready(acb))
+ if (arcmsr_hbaA_wait_msgint_ready(acb))
break;
else {
retry_count--;
@@ -381,13 +506,13 @@ static void arcmsr_flush_hba_cache(struct AdapterControlBlock *acb)
} while (retry_count != 0);
}
-static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb)
+static void arcmsr_hbaB_flush_cache(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
int retry_count = 30;
writel(ARCMSR_MESSAGE_FLUSH_CACHE, reg->drv2iop_doorbell);
do {
- if (arcmsr_hbb_wait_msgint_ready(acb))
+ if (arcmsr_hbaB_wait_msgint_ready(acb))
break;
else {
retry_count--;
@@ -397,14 +522,14 @@ static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb)
} while (retry_count != 0);
}
-static void arcmsr_flush_hbc_cache(struct AdapterControlBlock *pACB)
+static void arcmsr_hbaC_flush_cache(struct AdapterControlBlock *pACB)
{
- struct MessageUnit_C *reg = (struct MessageUnit_C *)pACB->pmuC;
+ struct MessageUnit_C __iomem *reg = pACB->pmuC;
int retry_count = 30;/* enlarge wait flush adapter cache time: 10 minute */
writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, &reg->inbound_msgaddr0);
writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
do {
- if (arcmsr_hbc_wait_msgint_ready(pACB)) {
+ if (arcmsr_hbaC_wait_msgint_ready(pACB)) {
break;
} else {
retry_count--;
@@ -414,23 +539,268 @@ static void arcmsr_flush_hbc_cache(struct AdapterControlBlock *pACB)
} while (retry_count != 0);
return;
}
+
+static void arcmsr_hbaD_flush_cache(struct AdapterControlBlock *pACB)
+{
+ int retry_count = 15;
+ struct MessageUnit_D *reg = pACB->pmuD;
+
+ writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, reg->inbound_msgaddr0);
+ do {
+ if (arcmsr_hbaD_wait_msgint_ready(pACB))
+ break;
+
+ retry_count--;
+ pr_notice("arcmsr%d: wait 'flush adapter "
+ "cache' timeout, retry count down = %d\n",
+ pACB->host->host_no, retry_count);
+ } while (retry_count != 0);
+}
+
+static void arcmsr_hbaE_flush_cache(struct AdapterControlBlock *pACB)
+{
+ int retry_count = 30;
+ struct MessageUnit_E __iomem *reg = pACB->pmuE;
+
+ writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, &reg->inbound_msgaddr0);
+ pACB->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pACB->out_doorbell, &reg->iobound_doorbell);
+ do {
+ if (arcmsr_hbaE_wait_msgint_ready(pACB))
+ break;
+ retry_count--;
+ pr_notice("arcmsr%d: wait 'flush adapter "
+ "cache' timeout, retry count down = %d\n",
+ pACB->host->host_no, retry_count);
+ } while (retry_count != 0);
+}
+
static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb)
{
switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A: {
- arcmsr_flush_hba_cache(acb);
- }
+ case ACB_ADAPTER_TYPE_A:
+ arcmsr_hbaA_flush_cache(acb);
break;
+ case ACB_ADAPTER_TYPE_B:
+ arcmsr_hbaB_flush_cache(acb);
+ break;
+ case ACB_ADAPTER_TYPE_C:
+ arcmsr_hbaC_flush_cache(acb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ arcmsr_hbaD_flush_cache(acb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ arcmsr_hbaE_flush_cache(acb);
+ break;
+ }
+}
+static void arcmsr_hbaB_assign_regAddr(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_B *reg = acb->pmuB;
+
+ if (acb->pdev->device == PCI_DEVICE_ID_ARECA_1203) {
+ reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_1203);
+ reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK_1203);
+ reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_1203);
+ reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK_1203);
+ } else {
+ reg->drv2iop_doorbell= MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL);
+ reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK);
+ reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL);
+ reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK);
+ }
+ reg->message_wbuffer = MEM_BASE1(ARCMSR_MESSAGE_WBUFFER);
+ reg->message_rbuffer = MEM_BASE1(ARCMSR_MESSAGE_RBUFFER);
+ reg->message_rwbuffer = MEM_BASE1(ARCMSR_MESSAGE_RWBUFFER);
+}
+
+static void arcmsr_hbaD_assign_regAddr(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_D *reg = acb->pmuD;
+
+ reg->chip_id = MEM_BASE0(ARCMSR_ARC1214_CHIP_ID);
+ reg->cpu_mem_config = MEM_BASE0(ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION);
+ reg->i2o_host_interrupt_mask = MEM_BASE0(ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK);
+ reg->sample_at_reset = MEM_BASE0(ARCMSR_ARC1214_SAMPLE_RESET);
+ reg->reset_request = MEM_BASE0(ARCMSR_ARC1214_RESET_REQUEST);
+ reg->host_int_status = MEM_BASE0(ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS);
+ reg->pcief0_int_enable = MEM_BASE0(ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE);
+ reg->inbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE0);
+ reg->inbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE1);
+ reg->outbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE0);
+ reg->outbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE1);
+ reg->inbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_INBOUND_DOORBELL);
+ reg->outbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL);
+ reg->outbound_doorbell_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE);
+ reg->inboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW);
+ reg->inboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH);
+ reg->inboundlist_write_pointer = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER);
+ reg->outboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW);
+ reg->outboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH);
+ reg->outboundlist_copy_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER);
+ reg->outboundlist_read_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER);
+ reg->outboundlist_interrupt_cause = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE);
+ reg->outboundlist_interrupt_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE);
+ reg->message_wbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_WBUFFER);
+ reg->message_rbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RBUFFER);
+ reg->msgcode_rwbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RWBUFFER);
+}
+
+static void arcmsr_hbaF_assign_regAddr(struct AdapterControlBlock *acb)
+{
+ dma_addr_t host_buffer_dma;
+ struct MessageUnit_F __iomem *pmuF;
+
+ memset(acb->dma_coherent2, 0xff, acb->completeQ_size);
+ acb->message_wbuffer = (uint32_t *)round_up((unsigned long)acb->dma_coherent2 +
+ acb->completeQ_size, 4);
+ acb->message_rbuffer = ((void *)acb->message_wbuffer) + 0x100;
+ acb->msgcode_rwbuffer = ((void *)acb->message_wbuffer) + 0x200;
+ memset((void *)acb->message_wbuffer, 0, MESG_RW_BUFFER_SIZE);
+ host_buffer_dma = round_up(acb->dma_coherent_handle2 + acb->completeQ_size, 4);
+ pmuF = acb->pmuF;
+ /* host buffer low address, bit0:1 all buffer active */
+ writel(lower_32_bits(host_buffer_dma | 1), &pmuF->inbound_msgaddr0);
+ /* host buffer high address */
+ writel(upper_32_bits(host_buffer_dma), &pmuF->inbound_msgaddr1);
+ /* set host buffer physical address */
+ writel(ARCMSR_HBFMU_DOORBELL_SYNC1, &pmuF->iobound_doorbell);
+}
+
+static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb)
+{
+ bool rtn = true;
+ void *dma_coherent;
+ dma_addr_t dma_coherent_handle;
+ struct pci_dev *pdev = acb->pdev;
+
+ switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_B: {
- arcmsr_flush_hbb_cache(acb);
+ acb->ioqueue_size = roundup(sizeof(struct MessageUnit_B), 32);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size,
+ &dma_coherent_handle, GFP_KERNEL);
+ if (!dma_coherent) {
+ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
+ return false;
+ }
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = dma_coherent;
+ acb->pmuB = (struct MessageUnit_B *)dma_coherent;
+ arcmsr_hbaB_assign_regAddr(acb);
}
break;
- case ACB_ADAPTER_TYPE_C: {
- arcmsr_flush_hbc_cache(acb);
+ case ACB_ADAPTER_TYPE_D: {
+ acb->ioqueue_size = roundup(sizeof(struct MessageUnit_D), 32);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size,
+ &dma_coherent_handle, GFP_KERNEL);
+ if (!dma_coherent) {
+ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
+ return false;
+ }
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = dma_coherent;
+ acb->pmuD = (struct MessageUnit_D *)dma_coherent;
+ arcmsr_hbaD_assign_regAddr(acb);
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E: {
+ uint32_t completeQ_size;
+ completeQ_size = sizeof(struct deliver_completeQ) * ARCMSR_MAX_HBE_DONEQUEUE + 128;
+ acb->ioqueue_size = roundup(completeQ_size, 32);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size,
+ &dma_coherent_handle, GFP_KERNEL);
+ if (!dma_coherent){
+ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
+ return false;
+ }
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = dma_coherent;
+ acb->pCompletionQ = dma_coherent;
+ acb->completionQ_entry = acb->ioqueue_size / sizeof(struct deliver_completeQ);
+ acb->doneq_index = 0;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_F: {
+ uint32_t QueueDepth;
+ uint32_t depthTbl[] = {256, 512, 1024, 128, 64, 32};
+
+ arcmsr_wait_firmware_ready(acb);
+ QueueDepth = depthTbl[readl(&acb->pmuF->outbound_msgaddr1) & 7];
+ acb->completeQ_size = sizeof(struct deliver_completeQ) * QueueDepth + 128;
+ acb->ioqueue_size = roundup(acb->completeQ_size + MESG_RW_BUFFER_SIZE, 32);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size,
+ &dma_coherent_handle, GFP_KERNEL);
+ if (!dma_coherent) {
+ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
+ return false;
+ }
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = dma_coherent;
+ acb->pCompletionQ = dma_coherent;
+ acb->completionQ_entry = acb->completeQ_size / sizeof(struct deliver_completeQ);
+ acb->doneq_index = 0;
+ arcmsr_hbaF_assign_regAddr(acb);
}
+ break;
+ default:
+ break;
}
+ return rtn;
+}
+
+static int arcmsr_alloc_xor_buffer(struct AdapterControlBlock *acb)
+{
+ int rc = 0;
+ struct pci_dev *pdev = acb->pdev;
+ void *dma_coherent;
+ dma_addr_t dma_coherent_handle;
+ int i, xor_ram;
+ struct Xor_sg *pXorPhys;
+ void **pXorVirt;
+ struct HostRamBuf *pRamBuf;
+
+ // allocate 1 MB * N physically continuous memory for XOR engine.
+ xor_ram = (acb->firm_PicStatus >> 24) & 0x0f;
+ acb->xor_mega = (xor_ram - 1) * 32 + 128 + 3;
+ acb->init2cfg_size = sizeof(struct HostRamBuf) +
+ (sizeof(struct XorHandle) * acb->xor_mega);
+ dma_coherent = dma_alloc_coherent(&pdev->dev, acb->init2cfg_size,
+ &dma_coherent_handle, GFP_KERNEL);
+ acb->xorVirt = dma_coherent;
+ acb->xorPhys = dma_coherent_handle;
+ pXorPhys = (struct Xor_sg *)((unsigned long)dma_coherent +
+ sizeof(struct HostRamBuf));
+ acb->xorVirtOffset = sizeof(struct HostRamBuf) +
+ (sizeof(struct Xor_sg) * acb->xor_mega);
+ pXorVirt = (void **)((unsigned long)dma_coherent +
+ (unsigned long)acb->xorVirtOffset);
+ for (i = 0; i < acb->xor_mega; i++) {
+ dma_coherent = dma_alloc_coherent(&pdev->dev,
+ ARCMSR_XOR_SEG_SIZE,
+ &dma_coherent_handle, GFP_KERNEL);
+ if (dma_coherent) {
+ pXorPhys->xorPhys = dma_coherent_handle;
+ pXorPhys->xorBufLen = ARCMSR_XOR_SEG_SIZE;
+ *pXorVirt = dma_coherent;
+ pXorPhys++;
+ pXorVirt++;
+ } else {
+ pr_info("arcmsr%d: alloc max XOR buffer = 0x%x MB\n",
+ acb->host->host_no, i);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+ pRamBuf = (struct HostRamBuf *)acb->xorVirt;
+ pRamBuf->hrbSignature = 0x53425248; //HRBS
+ pRamBuf->hrbSize = i * ARCMSR_XOR_SEG_SIZE;
+ pRamBuf->hrbRes[0] = 0;
+ pRamBuf->hrbRes[1] = 0;
+ return rc;
}
static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
@@ -440,11 +810,11 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
dma_addr_t dma_coherent_handle;
struct CommandControlBlock *ccb_tmp;
int i = 0, j = 0;
- dma_addr_t cdb_phyaddr;
+ unsigned long cdb_phyaddr, next_ccb_phy;
unsigned long roundup_ccbsize;
unsigned long max_xfer_len;
unsigned long max_sg_entrys;
- uint32_t firm_config_version;
+ uint32_t firm_config_version, curr_phy_upper32;
for (i = 0; i < ARCMSR_MAX_TARGETID; i++)
for (j = 0; j < ARCMSR_MAX_TARGETLUN; j++)
@@ -460,7 +830,9 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
acb->host->max_sectors = max_xfer_len/512;
acb->host->sg_tablesize = max_sg_entrys;
roundup_ccbsize = roundup(sizeof(struct CommandControlBlock) + (max_sg_entrys - 1) * sizeof(struct SG64ENTRY), 32);
- acb->uncache_size = roundup_ccbsize * ARCMSR_MAX_FREECCB_NUM;
+ acb->uncache_size = roundup_ccbsize * acb->maxFreeCCB;
+ if (acb->adapter_type != ACB_ADAPTER_TYPE_F)
+ acb->uncache_size += acb->ioqueue_size;
dma_coherent = dma_alloc_coherent(&pdev->dev, acb->uncache_size, &dma_coherent_handle, GFP_KERNEL);
if(!dma_coherent){
printk(KERN_NOTICE "arcmsr%d: dma_alloc_coherent got error\n", acb->host->host_no);
@@ -469,138 +841,239 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
acb->dma_coherent = dma_coherent;
acb->dma_coherent_handle = dma_coherent_handle;
memset(dma_coherent, 0, acb->uncache_size);
+ acb->ccbsize = roundup_ccbsize;
ccb_tmp = dma_coherent;
+ curr_phy_upper32 = upper_32_bits(dma_coherent_handle);
acb->vir2phy_offset = (unsigned long)dma_coherent - (unsigned long)dma_coherent_handle;
- for(i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++){
- cdb_phyaddr = dma_coherent_handle + offsetof(struct CommandControlBlock, arcmsr_cdb);
- ccb_tmp->cdb_phyaddr_pattern = ((acb->adapter_type == ACB_ADAPTER_TYPE_C) ? cdb_phyaddr : (cdb_phyaddr >> 5));
+ for(i = 0; i < acb->maxFreeCCB; i++){
+ cdb_phyaddr = (unsigned long)dma_coherent_handle + offsetof(struct CommandControlBlock, arcmsr_cdb);
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_A:
+ case ACB_ADAPTER_TYPE_B:
+ ccb_tmp->cdb_phyaddr = cdb_phyaddr >> 5;
+ break;
+ case ACB_ADAPTER_TYPE_C:
+ case ACB_ADAPTER_TYPE_D:
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ ccb_tmp->cdb_phyaddr = cdb_phyaddr;
+ break;
+ }
acb->pccb_pool[i] = ccb_tmp;
ccb_tmp->acb = acb;
+ ccb_tmp->smid = (u32)i << 16;
INIT_LIST_HEAD(&ccb_tmp->list);
- list_add_tail(&ccb_tmp->list, &acb->ccb_free_list);
+ next_ccb_phy = dma_coherent_handle + roundup_ccbsize;
+ if (upper_32_bits(next_ccb_phy) != curr_phy_upper32) {
+ acb->maxFreeCCB = i;
+ acb->host->can_queue = i;
+ break;
+ }
+ else
+ list_add_tail(&ccb_tmp->list, &acb->ccb_free_list);
ccb_tmp = (struct CommandControlBlock *)((unsigned long)ccb_tmp + roundup_ccbsize);
- dma_coherent_handle = dma_coherent_handle + roundup_ccbsize;
+ dma_coherent_handle = next_ccb_phy;
+ }
+ if (acb->adapter_type != ACB_ADAPTER_TYPE_F) {
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = ccb_tmp;
+ }
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_B:
+ acb->pmuB = (struct MessageUnit_B *)acb->dma_coherent2;
+ arcmsr_hbaB_assign_regAddr(acb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ acb->pmuD = (struct MessageUnit_D *)acb->dma_coherent2;
+ arcmsr_hbaD_assign_regAddr(acb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ acb->pCompletionQ = acb->dma_coherent2;
+ acb->completionQ_entry = acb->ioqueue_size / sizeof(struct deliver_completeQ);
+ acb->doneq_index = 0;
+ break;
+ }
+ if ((acb->firm_PicStatus >> 24) & 0x0f) {
+ if (arcmsr_alloc_xor_buffer(acb))
+ return -ENOMEM;
}
return 0;
}
static void arcmsr_message_isr_bh_fn(struct work_struct *work)
{
- struct AdapterControlBlock *acb = container_of(work,struct AdapterControlBlock, arcmsr_do_message_isr_bh);
+ struct AdapterControlBlock *acb = container_of(work,
+ struct AdapterControlBlock, arcmsr_do_message_isr_bh);
+ char *acb_dev_map = (char *)acb->device_map;
+ uint32_t __iomem *signature = NULL;
+ char __iomem *devicemap = NULL;
+ int target, lun;
+ struct scsi_device *psdev;
+ char diff, temp;
+
switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A: {
+ case ACB_ADAPTER_TYPE_A: {
+ struct MessageUnit_A __iomem *reg = acb->pmuA;
+
+ signature = (uint32_t __iomem *)(&reg->message_rwbuffer[0]);
+ devicemap = (char __iomem *)(&reg->message_rwbuffer[21]);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_B: {
+ struct MessageUnit_B *reg = acb->pmuB;
- struct MessageUnit_A __iomem *reg = acb->pmuA;
- char *acb_dev_map = (char *)acb->device_map;
- uint32_t __iomem *signature = (uint32_t __iomem*) (&reg->message_rwbuffer[0]);
- char __iomem *devicemap = (char __iomem*) (&reg->message_rwbuffer[21]);
- int target, lun;
- struct scsi_device *psdev;
- char diff;
-
- atomic_inc(&acb->rq_map_token);
- if (readl(signature) == ARCMSR_SIGNATURE_GET_CONFIG) {
- for(target = 0; target < ARCMSR_MAX_TARGETID -1; target++) {
- diff = (*acb_dev_map)^readb(devicemap);
- if (diff != 0) {
- char temp;
- *acb_dev_map = readb(devicemap);
- temp =*acb_dev_map;
- for(lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
- if((temp & 0x01)==1 && (diff & 0x01) == 1) {
- scsi_add_device(acb->host, 0, target, lun);
- }else if((temp & 0x01) == 0 && (diff & 0x01) == 1) {
- psdev = scsi_device_lookup(acb->host, 0, target, lun);
- if (psdev != NULL ) {
- scsi_remove_device(psdev);
- scsi_device_put(psdev);
- }
- }
- temp >>= 1;
- diff >>= 1;
- }
+ signature = (uint32_t __iomem *)(&reg->message_rwbuffer[0]);
+ devicemap = (char __iomem *)(&reg->message_rwbuffer[21]);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_C: {
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
+
+ signature = (uint32_t __iomem *)(&reg->msgcode_rwbuffer[0]);
+ devicemap = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+
+ signature = (uint32_t __iomem *)(&reg->msgcode_rwbuffer[0]);
+ devicemap = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_E: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+
+ signature = (uint32_t __iomem *)(&reg->msgcode_rwbuffer[0]);
+ devicemap = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_F: {
+ signature = (uint32_t __iomem *)(&acb->msgcode_rwbuffer[0]);
+ devicemap = (char __iomem *)(&acb->msgcode_rwbuffer[21]);
+ break;
+ }
+ }
+ if (readl(signature) != ARCMSR_SIGNATURE_GET_CONFIG)
+ return;
+ for (target = 0; target < ARCMSR_MAX_TARGETID - 1;
+ target++) {
+ temp = readb(devicemap);
+ diff = (*acb_dev_map) ^ temp;
+ if (diff != 0) {
+ *acb_dev_map = temp;
+ for (lun = 0; lun < ARCMSR_MAX_TARGETLUN;
+ lun++) {
+ if ((diff & 0x01) == 1 &&
+ (temp & 0x01) == 1) {
+ scsi_add_device(acb->host,
+ 0, target, lun);
+ } else if ((diff & 0x01) == 1
+ && (temp & 0x01) == 0) {
+ psdev = scsi_device_lookup(acb->host,
+ 0, target, lun);
+ if (psdev != NULL) {
+ scsi_remove_device(psdev);
+ scsi_device_put(psdev);
}
- devicemap++;
- acb_dev_map++;
}
+ temp >>= 1;
+ diff >>= 1;
}
- break;
}
+ devicemap++;
+ acb_dev_map++;
+ }
+ acb->acb_flags &= ~ACB_F_MSG_GET_CONFIG;
+}
- case ACB_ADAPTER_TYPE_B: {
- struct MessageUnit_B *reg = acb->pmuB;
- char *acb_dev_map = (char *)acb->device_map;
- uint32_t __iomem *signature = (uint32_t __iomem*)(&reg->message_rwbuffer[0]);
- char __iomem *devicemap = (char __iomem*)(&reg->message_rwbuffer[21]);
- int target, lun;
- struct scsi_device *psdev;
- char diff;
-
- atomic_inc(&acb->rq_map_token);
- if (readl(signature) == ARCMSR_SIGNATURE_GET_CONFIG) {
- for(target = 0; target < ARCMSR_MAX_TARGETID -1; target++) {
- diff = (*acb_dev_map)^readb(devicemap);
- if (diff != 0) {
- char temp;
- *acb_dev_map = readb(devicemap);
- temp =*acb_dev_map;
- for(lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
- if((temp & 0x01)==1 && (diff & 0x01) == 1) {
- scsi_add_device(acb->host, 0, target, lun);
- }else if((temp & 0x01) == 0 && (diff & 0x01) == 1) {
- psdev = scsi_device_lookup(acb->host, 0, target, lun);
- if (psdev != NULL ) {
- scsi_remove_device(psdev);
- scsi_device_put(psdev);
- }
- }
- temp >>= 1;
- diff >>= 1;
- }
- }
- devicemap++;
- acb_dev_map++;
- }
+static int
+arcmsr_request_irq(struct pci_dev *pdev, struct AdapterControlBlock *acb)
+{
+ unsigned long flags;
+ int nvec, i;
+
+ if (msix_enable == 0)
+ goto msi_int0;
+ nvec = pci_alloc_irq_vectors(pdev, 1, ARCMST_NUM_MSIX_VECTORS,
+ PCI_IRQ_MSIX);
+ if (nvec > 0) {
+ pr_info("arcmsr%d: msi-x enabled\n", acb->host->host_no);
+ flags = 0;
+ } else {
+msi_int0:
+ if (msi_enable == 1) {
+ nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
+ if (nvec == 1) {
+ dev_info(&pdev->dev, "msi enabled\n");
+ goto msi_int1;
}
}
- break;
- case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *reg = acb->pmuC;
- char *acb_dev_map = (char *)acb->device_map;
- uint32_t __iomem *signature = (uint32_t __iomem *)(&reg->msgcode_rwbuffer[0]);
- char __iomem *devicemap = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
- int target, lun;
- struct scsi_device *psdev;
- char diff;
-
- atomic_inc(&acb->rq_map_token);
- if (readl(signature) == ARCMSR_SIGNATURE_GET_CONFIG) {
- for (target = 0; target < ARCMSR_MAX_TARGETID - 1; target++) {
- diff = (*acb_dev_map)^readb(devicemap);
- if (diff != 0) {
- char temp;
- *acb_dev_map = readb(devicemap);
- temp = *acb_dev_map;
- for (lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
- if ((temp & 0x01) == 1 && (diff & 0x01) == 1) {
- scsi_add_device(acb->host, 0, target, lun);
- } else if ((temp & 0x01) == 0 && (diff & 0x01) == 1) {
- psdev = scsi_device_lookup(acb->host, 0, target, lun);
- if (psdev != NULL) {
- scsi_remove_device(psdev);
- scsi_device_put(psdev);
- }
- }
- temp >>= 1;
- diff >>= 1;
- }
- }
- devicemap++;
- acb_dev_map++;
- }
- }
+ nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX);
+ if (nvec < 1)
+ return FAILED;
+msi_int1:
+ flags = IRQF_SHARED;
+ }
+
+ acb->vector_count = nvec;
+ for (i = 0; i < nvec; i++) {
+ if (request_irq(pci_irq_vector(pdev, i), arcmsr_do_interrupt,
+ flags, "arcmsr", acb)) {
+ pr_warn("arcmsr%d: request_irq =%d failed!\n",
+ acb->host->host_no, pci_irq_vector(pdev, i));
+ goto out_free_irq;
+ }
+ }
+
+ return SUCCESS;
+out_free_irq:
+ while (--i >= 0)
+ free_irq(pci_irq_vector(pdev, i), acb);
+ pci_free_irq_vectors(pdev);
+ return FAILED;
+}
+
+static void arcmsr_init_get_devmap_timer(struct AdapterControlBlock *pacb)
+{
+ INIT_WORK(&pacb->arcmsr_do_message_isr_bh, arcmsr_message_isr_bh_fn);
+ pacb->fw_flag = FW_NORMAL;
+ timer_setup(&pacb->eternal_timer, arcmsr_request_device_map, 0);
+ pacb->eternal_timer.expires = jiffies + msecs_to_jiffies(6 * HZ);
+ add_timer(&pacb->eternal_timer);
+}
+
+static void arcmsr_init_set_datetime_timer(struct AdapterControlBlock *pacb)
+{
+ timer_setup(&pacb->refresh_timer, arcmsr_set_iop_datetime, 0);
+ pacb->refresh_timer.expires = jiffies + secs_to_jiffies(60);
+ add_timer(&pacb->refresh_timer);
+}
+
+static int arcmsr_set_dma_mask(struct AdapterControlBlock *acb)
+{
+ struct pci_dev *pcidev = acb->pdev;
+
+ if (IS_DMA64) {
+ if (((acb->adapter_type == ACB_ADAPTER_TYPE_A) && !dma_mask_64) ||
+ dma_set_mask(&pcidev->dev, DMA_BIT_MASK(64)))
+ goto dma32;
+ if (acb->adapter_type <= ACB_ADAPTER_TYPE_B)
+ return 0;
+ if (dma_set_coherent_mask(&pcidev->dev, DMA_BIT_MASK(64)) ||
+ dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(64))) {
+ printk("arcmsr: set DMA 64 mask failed\n");
+ return -ENXIO;
+ }
+ } else {
+dma32:
+ if (dma_set_mask(&pcidev->dev, DMA_BIT_MASK(32)) ||
+ dma_set_coherent_mask(&pcidev->dev, DMA_BIT_MASK(32)) ||
+ dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32))) {
+ printk("arcmsr: set DMA 32-bit mask failed\n");
+ return -ENXIO;
}
}
+ return 0;
}
static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -617,28 +1090,25 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if(!host){
goto pci_disable_dev;
}
- error = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
- if(error){
- error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if(error){
- printk(KERN_WARNING
- "scsi%d: No suitable DMA mask available\n",
- host->host_no);
- goto scsi_host_release;
- }
- }
init_waitqueue_head(&wait_q);
bus = pdev->bus->number;
dev_fun = pdev->devfn;
acb = (struct AdapterControlBlock *) host->hostdata;
memset(acb,0,sizeof(struct AdapterControlBlock));
acb->pdev = pdev;
+ acb->adapter_type = id->driver_data;
+ if (arcmsr_set_dma_mask(acb))
+ goto scsi_host_release;
acb->host = host;
host->max_lun = ARCMSR_MAX_TARGETLUN;
host->max_id = ARCMSR_MAX_TARGETID; /*16:8*/
host->max_cmd_len = 16; /*this is issue of 64bit LBA ,over 2T byte*/
- host->can_queue = ARCMSR_MAX_FREECCB_NUM; /* max simultaneous cmds */
- host->cmd_per_lun = ARCMSR_MAX_CMD_PERLUN;
+ if ((host_can_queue < ARCMSR_MIN_OUTSTANDING_CMD) || (host_can_queue > ARCMSR_MAX_OUTSTANDING_CMD))
+ host_can_queue = ARCMSR_DEFAULT_OUTSTANDING_CMD;
+ host->can_queue = host_can_queue; /* max simultaneous cmds */
+ if ((cmd_per_lun < ARCMSR_MIN_CMD_PERLUN) || (cmd_per_lun > ARCMSR_MAX_CMD_PERLUN))
+ cmd_per_lun = ARCMSR_DEFAULT_CMD_PERLUN;
+ host->cmd_per_lun = cmd_per_lun;
host->this_id = ARCMSR_SCSI_INITIATOR_ID;
host->unique_id = (bus << 8) | dev_fun;
pci_set_drvdata(pdev, host);
@@ -649,56 +1119,61 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
spin_lock_init(&acb->eh_lock);
spin_lock_init(&acb->ccblist_lock);
+ spin_lock_init(&acb->postq_lock);
+ spin_lock_init(&acb->doneq_lock);
+ spin_lock_init(&acb->rqbuffer_lock);
+ spin_lock_init(&acb->wqbuffer_lock);
acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
ACB_F_MESSAGE_RQBUFFER_CLEARED |
ACB_F_MESSAGE_WQBUFFER_READED);
acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER;
INIT_LIST_HEAD(&acb->ccb_free_list);
- arcmsr_define_adapter_type(acb);
error = arcmsr_remap_pciregion(acb);
if(!error){
goto pci_release_regs;
}
+ error = arcmsr_alloc_io_queue(acb);
+ if (!error)
+ goto unmap_pci_region;
error = arcmsr_get_firmware_spec(acb);
if(!error){
- goto unmap_pci_region;
+ goto free_hbb_mu;
}
+ if (acb->adapter_type != ACB_ADAPTER_TYPE_F)
+ arcmsr_free_io_queue(acb);
error = arcmsr_alloc_ccb_pool(acb);
if(error){
- goto free_hbb_mu;
+ goto unmap_pci_region;
}
- arcmsr_iop_init(acb);
error = scsi_add_host(host, &pdev->dev);
if(error){
- goto RAID_controller_stop;
+ goto free_ccb_pool;
}
- error = request_irq(pdev->irq, arcmsr_do_interrupt, IRQF_SHARED, "arcmsr", acb);
- if(error){
+ if (arcmsr_request_irq(pdev, acb) == FAILED)
goto scsi_host_remove;
- }
- host->irq = pdev->irq;
- scsi_scan_host(host);
- INIT_WORK(&acb->arcmsr_do_message_isr_bh, arcmsr_message_isr_bh_fn);
- atomic_set(&acb->rq_map_token, 16);
- atomic_set(&acb->ante_token_value, 16);
- acb->fw_flag = FW_NORMAL;
- init_timer(&acb->eternal_timer);
- acb->eternal_timer.expires = jiffies + msecs_to_jiffies(6 * HZ);
- acb->eternal_timer.data = (unsigned long) acb;
- acb->eternal_timer.function = &arcmsr_request_device_map;
- add_timer(&acb->eternal_timer);
+ arcmsr_iop_init(acb);
+ arcmsr_init_get_devmap_timer(acb);
+ if (set_date_time)
+ arcmsr_init_set_datetime_timer(acb);
if(arcmsr_alloc_sysfs_attr(acb))
goto out_free_sysfs;
+ scsi_scan_host(host);
return 0;
out_free_sysfs:
-scsi_host_remove:
- scsi_remove_host(host);
-RAID_controller_stop:
+ if (set_date_time)
+ timer_delete_sync(&acb->refresh_timer);
+ timer_delete_sync(&acb->eternal_timer);
+ flush_work(&acb->arcmsr_do_message_isr_bh);
arcmsr_stop_adapter_bgrb(acb);
arcmsr_flush_adapter_cache(acb);
+ arcmsr_free_irq(pdev, acb);
+scsi_host_remove:
+ scsi_remove_host(host);
+free_ccb_pool:
arcmsr_free_ccb_pool(acb);
+ goto unmap_pci_region;
free_hbb_mu:
- arcmsr_free_hbb_mu(acb);
+ arcmsr_free_io_queue(acb);
unmap_pci_region:
arcmsr_unmap_pciregion(acb);
pci_release_regs:
@@ -710,82 +1185,181 @@ pci_disable_dev:
return -ENODEV;
}
-static uint8_t arcmsr_abort_hba_allcmd(struct AdapterControlBlock *acb)
+static void arcmsr_free_irq(struct pci_dev *pdev,
+ struct AdapterControlBlock *acb)
+{
+ int i;
+
+ for (i = 0; i < acb->vector_count; i++)
+ free_irq(pci_irq_vector(pdev, i), acb);
+ pci_free_irq_vectors(pdev);
+}
+
+static int __maybe_unused arcmsr_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct AdapterControlBlock *acb =
+ (struct AdapterControlBlock *)host->hostdata;
+
+ arcmsr_disable_outbound_ints(acb);
+ arcmsr_free_irq(pdev, acb);
+ timer_delete_sync(&acb->eternal_timer);
+ if (set_date_time)
+ timer_delete_sync(&acb->refresh_timer);
+ flush_work(&acb->arcmsr_do_message_isr_bh);
+ arcmsr_stop_adapter_bgrb(acb);
+ arcmsr_flush_adapter_cache(acb);
+ return 0;
+}
+
+static int __maybe_unused arcmsr_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct AdapterControlBlock *acb =
+ (struct AdapterControlBlock *)host->hostdata;
+
+ if (arcmsr_set_dma_mask(acb))
+ goto controller_unregister;
+ if (arcmsr_request_irq(pdev, acb) == FAILED)
+ goto controller_stop;
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_B: {
+ struct MessageUnit_B *reg = acb->pmuB;
+ uint32_t i;
+ for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) {
+ reg->post_qbuffer[i] = 0;
+ reg->done_qbuffer[i] = 0;
+ }
+ reg->postq_index = 0;
+ reg->doneq_index = 0;
+ break;
+ }
+ case ACB_ADAPTER_TYPE_E:
+ writel(0, &acb->pmuE->host_int_status);
+ writel(ARCMSR_HBEMU_DOORBELL_SYNC, &acb->pmuE->iobound_doorbell);
+ acb->in_doorbell = 0;
+ acb->out_doorbell = 0;
+ acb->doneq_index = 0;
+ break;
+ case ACB_ADAPTER_TYPE_F:
+ writel(0, &acb->pmuF->host_int_status);
+ writel(ARCMSR_HBFMU_DOORBELL_SYNC, &acb->pmuF->iobound_doorbell);
+ acb->in_doorbell = 0;
+ acb->out_doorbell = 0;
+ acb->doneq_index = 0;
+ arcmsr_hbaF_assign_regAddr(acb);
+ break;
+ }
+ arcmsr_iop_init(acb);
+ arcmsr_init_get_devmap_timer(acb);
+ if (set_date_time)
+ arcmsr_init_set_datetime_timer(acb);
+ return 0;
+controller_stop:
+ arcmsr_stop_adapter_bgrb(acb);
+ arcmsr_flush_adapter_cache(acb);
+controller_unregister:
+ scsi_remove_host(host);
+ arcmsr_free_ccb_pool(acb);
+ if (acb->adapter_type == ACB_ADAPTER_TYPE_F)
+ arcmsr_free_io_queue(acb);
+ arcmsr_unmap_pciregion(acb);
+ scsi_host_put(host);
+ return -ENODEV;
+}
+
+static uint8_t arcmsr_hbaA_abort_allcmd(struct AdapterControlBlock *acb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, &reg->inbound_msgaddr0);
- if (!arcmsr_hba_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaA_wait_msgint_ready(acb)) {
printk(KERN_NOTICE
- "arcmsr%d: wait 'abort all outstanding command' timeout \n"
+ "arcmsr%d: wait 'abort all outstanding command' timeout\n"
, acb->host->host_no);
return false;
}
return true;
}
-static uint8_t arcmsr_abort_hbb_allcmd(struct AdapterControlBlock *acb)
+static uint8_t arcmsr_hbaB_abort_allcmd(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
writel(ARCMSR_MESSAGE_ABORT_CMD, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
printk(KERN_NOTICE
- "arcmsr%d: wait 'abort all outstanding command' timeout \n"
+ "arcmsr%d: wait 'abort all outstanding command' timeout\n"
, acb->host->host_no);
return false;
}
return true;
}
-static uint8_t arcmsr_abort_hbc_allcmd(struct AdapterControlBlock *pACB)
+static uint8_t arcmsr_hbaC_abort_allcmd(struct AdapterControlBlock *pACB)
{
- struct MessageUnit_C *reg = (struct MessageUnit_C *)pACB->pmuC;
+ struct MessageUnit_C __iomem *reg = pACB->pmuC;
writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, &reg->inbound_msgaddr0);
writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
- if (!arcmsr_hbc_wait_msgint_ready(pACB)) {
+ if (!arcmsr_hbaC_wait_msgint_ready(pACB)) {
printk(KERN_NOTICE
- "arcmsr%d: wait 'abort all outstanding command' timeout \n"
+ "arcmsr%d: wait 'abort all outstanding command' timeout\n"
, pACB->host->host_no);
return false;
}
return true;
}
-static uint8_t arcmsr_abort_allcmd(struct AdapterControlBlock *acb)
-{
- uint8_t rtnval = 0;
- switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A: {
- rtnval = arcmsr_abort_hba_allcmd(acb);
- }
- break;
- case ACB_ADAPTER_TYPE_B: {
- rtnval = arcmsr_abort_hbb_allcmd(acb);
- }
- break;
+static uint8_t arcmsr_hbaD_abort_allcmd(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_D *reg = pACB->pmuD;
- case ACB_ADAPTER_TYPE_C: {
- rtnval = arcmsr_abort_hbc_allcmd(acb);
- }
+ writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, reg->inbound_msgaddr0);
+ if (!arcmsr_hbaD_wait_msgint_ready(pACB)) {
+ pr_notice("arcmsr%d: wait 'abort all outstanding "
+ "command' timeout\n", pACB->host->host_no);
+ return false;
}
- return rtnval;
+ return true;
}
-static bool arcmsr_hbb_enable_driver_mode(struct AdapterControlBlock *pacb)
+static uint8_t arcmsr_hbaE_abort_allcmd(struct AdapterControlBlock *pACB)
{
- struct MessageUnit_B *reg = pacb->pmuB;
- writel(ARCMSR_MESSAGE_START_DRIVER_MODE, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(pacb)) {
- printk(KERN_ERR "arcmsr%d: can't set driver mode. \n", pacb->host->host_no);
+ struct MessageUnit_E __iomem *reg = pACB->pmuE;
+
+ writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, &reg->inbound_msgaddr0);
+ pACB->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pACB->out_doorbell, &reg->iobound_doorbell);
+ if (!arcmsr_hbaE_wait_msgint_ready(pACB)) {
+ pr_notice("arcmsr%d: wait 'abort all outstanding "
+ "command' timeout\n", pACB->host->host_no);
return false;
}
- return true;
+ return true;
}
-static void arcmsr_pci_unmap_dma(struct CommandControlBlock *ccb)
+static uint8_t arcmsr_abort_allcmd(struct AdapterControlBlock *acb)
{
- struct scsi_cmnd *pcmd = ccb->pcmd;
-
- scsi_dma_unmap(pcmd);
+ uint8_t rtnval = 0;
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_A:
+ rtnval = arcmsr_hbaA_abort_allcmd(acb);
+ break;
+ case ACB_ADAPTER_TYPE_B:
+ rtnval = arcmsr_hbaB_abort_allcmd(acb);
+ break;
+ case ACB_ADAPTER_TYPE_C:
+ rtnval = arcmsr_hbaC_abort_allcmd(acb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ rtnval = arcmsr_hbaD_abort_allcmd(acb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ rtnval = arcmsr_hbaE_abort_allcmd(acb);
+ break;
+ }
+ return rtnval;
}
static void arcmsr_ccb_complete(struct CommandControlBlock *ccb)
@@ -794,26 +1368,29 @@ static void arcmsr_ccb_complete(struct CommandControlBlock *ccb)
struct scsi_cmnd *pcmd = ccb->pcmd;
unsigned long flags;
atomic_dec(&acb->ccboutstandingcount);
- arcmsr_pci_unmap_dma(ccb);
+ scsi_dma_unmap(ccb->pcmd);
ccb->startdone = ARCMSR_CCB_DONE;
spin_lock_irqsave(&acb->ccblist_lock, flags);
list_add_tail(&ccb->list, &acb->ccb_free_list);
spin_unlock_irqrestore(&acb->ccblist_lock, flags);
- pcmd->scsi_done(pcmd);
+ scsi_done(pcmd);
}
static void arcmsr_report_sense_info(struct CommandControlBlock *ccb)
{
-
struct scsi_cmnd *pcmd = ccb->pcmd;
- struct SENSE_DATA *sensebuffer = (struct SENSE_DATA *)pcmd->sense_buffer;
- pcmd->result = DID_OK << 16;
- if (sensebuffer) {
- int sense_data_length =
- sizeof(struct SENSE_DATA) < SCSI_SENSE_BUFFERSIZE
- ? sizeof(struct SENSE_DATA) : SCSI_SENSE_BUFFERSIZE;
- memset(sensebuffer, 0, SCSI_SENSE_BUFFERSIZE);
- memcpy(sensebuffer, ccb->arcmsr_cdb.SenseData, sense_data_length);
+
+ pcmd->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION;
+ if (pcmd->sense_buffer) {
+ struct SENSE_DATA *sensebuffer;
+
+ memcpy_and_pad(pcmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ ccb->arcmsr_cdb.SenseData,
+ sizeof(ccb->arcmsr_cdb.SenseData),
+ 0);
+
+ sensebuffer = (struct SENSE_DATA *)pcmd->sense_buffer;
sensebuffer->ErrorCode = SCSI_SENSE_CURRENT_ERRORS;
sensebuffer->Valid = 1;
}
@@ -837,12 +1414,26 @@ static u32 arcmsr_disable_outbound_ints(struct AdapterControlBlock *acb)
}
break;
case ACB_ADAPTER_TYPE_C:{
- struct MessageUnit_C *reg = (struct MessageUnit_C *)acb->pmuC;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
/* disable all outbound interrupt */
orig_mask = readl(&reg->host_int_mask); /* disable outbound message0 int */
writel(orig_mask|ARCMSR_HBCMU_ALL_INTMASKENABLE, &reg->host_int_mask);
}
break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ /* disable all outbound interrupt */
+ writel(ARCMSR_ARC1214_ALL_INT_DISABLE, reg->pcief0_int_enable);
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ orig_mask = readl(&reg->host_int_mask);
+ writel(orig_mask | ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR | ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR, &reg->host_int_mask);
+ readl(&reg->host_int_mask); /* Dummy readl to force pci flush */
+ }
+ break;
}
return orig_mask;
}
@@ -901,13 +1492,10 @@ static void arcmsr_report_ccb_state(struct AdapterControlBlock *acb,
static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, struct CommandControlBlock *pCCB, bool error)
{
- int id, lun;
if ((pCCB->acb != acb) || (pCCB->startdone != ARCMSR_CCB_START)) {
if (pCCB->startdone == ARCMSR_CCB_ABORTED) {
struct scsi_cmnd *abortcmd = pCCB->pcmd;
if (abortcmd) {
- id = abortcmd->device->id;
- lun = abortcmd->device->lun;
abortcmd->result |= DID_ABORT << 16;
arcmsr_ccb_complete(pCCB);
printk(KERN_NOTICE "arcmsr%d: pCCB ='0x%p' isr got aborted command \n",
@@ -925,7 +1513,7 @@ static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, struct Comma
, pCCB->acb
, pCCB->startdone
, atomic_read(&acb->ccboutstandingcount));
- return;
+ return;
}
arcmsr_report_ccb_state(acb, pCCB, error);
}
@@ -937,6 +1525,8 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
struct ARCMSR_CDB *pARCMSR_CDB;
bool error;
struct CommandControlBlock *pCCB;
+ unsigned long ccb_cdb_phy;
+
switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_A: {
@@ -947,8 +1537,11 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
/*clear and abort all outbound posted Q*/
writel(outbound_intstatus, &reg->outbound_intstatus);/*clear interrupt*/
while(((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF)
- && (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) {
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ && (i++ < acb->maxOutstanding)) {
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
@@ -961,9 +1554,13 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
/*clear all outbound posted Q*/
writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell); /* clear doorbell interrupt */
for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) {
- if ((flag_ccb = readl(&reg->done_qbuffer[i])) != 0) {
- writel(0, &reg->done_qbuffer[i]);
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ flag_ccb = reg->done_qbuffer[i];
+ if (flag_ccb != 0) {
+ reg->done_qbuffer[i] = 0;
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
@@ -975,40 +1572,162 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
}
break;
case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *reg = acb->pmuC;
- struct ARCMSR_CDB *pARCMSR_CDB;
- uint32_t flag_ccb, ccb_cdb_phy;
- bool error;
- struct CommandControlBlock *pCCB;
- while ((readl(&reg->host_int_status) & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) && (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) {
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
+ while ((readl(&reg->host_int_status) & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) && (i++ < acb->maxOutstanding)) {
/*need to do*/
flag_ccb = readl(&reg->outbound_queueport_low);
ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+ccb_cdb_phy);/*frame must be 32 bytes aligned*/
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
}
+ }
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *pmu = acb->pmuD;
+ uint32_t outbound_write_pointer;
+ uint32_t doneq_index, index_stripped, addressLow, residual, toggle;
+ unsigned long flags;
+
+ residual = atomic_read(&acb->ccboutstandingcount);
+ for (i = 0; i < residual; i++) {
+ spin_lock_irqsave(&acb->doneq_lock, flags);
+ outbound_write_pointer =
+ pmu->done_qbuffer[0].addressLow + 1;
+ doneq_index = pmu->doneq_index;
+ if ((doneq_index & 0xFFF) !=
+ (outbound_write_pointer & 0xFFF)) {
+ toggle = doneq_index & 0x4000;
+ index_stripped = (doneq_index & 0xFFF) + 1;
+ index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
+ pmu->doneq_index = index_stripped ? (index_stripped | toggle) :
+ ((toggle ^ 0x4000) + 1);
+ doneq_index = pmu->doneq_index;
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ addressLow = pmu->done_qbuffer[doneq_index &
+ 0xFFF].addressLow;
+ ccb_cdb_phy = (addressLow & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)
+ (acb->vir2phy_offset + ccb_cdb_phy);
+ pCCB = container_of(pARCMSR_CDB,
+ struct CommandControlBlock, arcmsr_cdb);
+ error = (addressLow &
+ ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ?
+ true : false;
+ arcmsr_drain_donequeue(acb, pCCB, error);
+ writel(doneq_index,
+ pmu->outboundlist_read_pointer);
+ } else {
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ mdelay(10);
+ }
+ }
+ pmu->postq_index = 0;
+ pmu->doneq_index = 0x40FF;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ arcmsr_hbaE_postqueue_isr(acb);
+ break;
+ case ACB_ADAPTER_TYPE_F:
+ arcmsr_hbaF_postqueue_isr(acb);
+ break;
+ }
+}
+
+static void arcmsr_remove_scsi_devices(struct AdapterControlBlock *acb)
+{
+ char *acb_dev_map = (char *)acb->device_map;
+ int target, lun, i;
+ struct scsi_device *psdev;
+ struct CommandControlBlock *ccb;
+ char temp;
+
+ for (i = 0; i < acb->maxFreeCCB; i++) {
+ ccb = acb->pccb_pool[i];
+ if (ccb->startdone == ARCMSR_CCB_START) {
+ ccb->pcmd->result = DID_NO_CONNECT << 16;
+ scsi_dma_unmap(ccb->pcmd);
+ scsi_done(ccb->pcmd);
+ }
}
+ for (target = 0; target < ARCMSR_MAX_TARGETID; target++) {
+ temp = *acb_dev_map;
+ if (temp) {
+ for (lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
+ if (temp & 1) {
+ psdev = scsi_device_lookup(acb->host,
+ 0, target, lun);
+ if (psdev != NULL) {
+ scsi_remove_device(psdev);
+ scsi_device_put(psdev);
+ }
+ }
+ temp >>= 1;
+ }
+ *acb_dev_map = 0;
+ }
+ acb_dev_map++;
}
}
+
+static void arcmsr_free_pcidev(struct AdapterControlBlock *acb)
+{
+ struct pci_dev *pdev;
+ struct Scsi_Host *host;
+
+ host = acb->host;
+ arcmsr_free_sysfs_attr(acb);
+ scsi_remove_host(host);
+ flush_work(&acb->arcmsr_do_message_isr_bh);
+ timer_delete_sync(&acb->eternal_timer);
+ if (set_date_time)
+ timer_delete_sync(&acb->refresh_timer);
+ pdev = acb->pdev;
+ arcmsr_free_irq(pdev, acb);
+ arcmsr_free_ccb_pool(acb);
+ if (acb->adapter_type == ACB_ADAPTER_TYPE_F)
+ arcmsr_free_io_queue(acb);
+ arcmsr_unmap_pciregion(acb);
+ pci_release_regions(pdev);
+ scsi_host_put(host);
+ pci_disable_device(pdev);
+}
+
static void arcmsr_remove(struct pci_dev *pdev)
{
struct Scsi_Host *host = pci_get_drvdata(pdev);
struct AdapterControlBlock *acb =
(struct AdapterControlBlock *) host->hostdata;
int poll_count = 0;
+ uint16_t dev_id;
+
+ pci_read_config_word(pdev, PCI_DEVICE_ID, &dev_id);
+ if (dev_id == 0xffff) {
+ acb->acb_flags &= ~ACB_F_IOP_INITED;
+ acb->acb_flags |= ACB_F_ADAPTER_REMOVED;
+ arcmsr_remove_scsi_devices(acb);
+ arcmsr_free_pcidev(acb);
+ return;
+ }
arcmsr_free_sysfs_attr(acb);
scsi_remove_host(host);
- flush_work_sync(&acb->arcmsr_do_message_isr_bh);
- del_timer_sync(&acb->eternal_timer);
+ flush_work(&acb->arcmsr_do_message_isr_bh);
+ timer_delete_sync(&acb->eternal_timer);
+ if (set_date_time)
+ timer_delete_sync(&acb->refresh_timer);
arcmsr_disable_outbound_ints(acb);
arcmsr_stop_adapter_bgrb(acb);
arcmsr_flush_adapter_cache(acb);
acb->acb_flags |= ACB_F_SCSISTOPADAPTER;
acb->acb_flags &= ~ACB_F_IOP_INITED;
- for (poll_count = 0; poll_count < ARCMSR_MAX_OUTSTANDING_CMD; poll_count++){
+ for (poll_count = 0; poll_count < acb->maxOutstanding; poll_count++){
if (!atomic_read(&acb->ccboutstandingcount))
break;
arcmsr_interrupt(acb);/* FIXME: need spinlock */
@@ -1020,7 +1739,7 @@ static void arcmsr_remove(struct pci_dev *pdev)
arcmsr_abort_allcmd(acb);
arcmsr_done4abort_postqueue(acb);
- for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
+ for (i = 0; i < acb->maxFreeCCB; i++) {
struct CommandControlBlock *ccb = acb->pccb_pool[i];
if (ccb->startdone == ARCMSR_CCB_START) {
ccb->startdone = ARCMSR_CCB_ABORTED;
@@ -1029,14 +1748,14 @@ static void arcmsr_remove(struct pci_dev *pdev)
}
}
}
- free_irq(pdev->irq, acb);
+ arcmsr_free_irq(pdev, acb);
arcmsr_free_ccb_pool(acb);
- arcmsr_free_hbb_mu(acb);
+ if (acb->adapter_type == ACB_ADAPTER_TYPE_F)
+ arcmsr_free_io_queue(acb);
arcmsr_unmap_pciregion(acb);
pci_release_regions(pdev);
scsi_host_put(host);
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
}
static void arcmsr_shutdown(struct pci_dev *pdev)
@@ -1044,21 +1763,26 @@ static void arcmsr_shutdown(struct pci_dev *pdev)
struct Scsi_Host *host = pci_get_drvdata(pdev);
struct AdapterControlBlock *acb =
(struct AdapterControlBlock *)host->hostdata;
- del_timer_sync(&acb->eternal_timer);
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+ return;
+ timer_delete_sync(&acb->eternal_timer);
+ if (set_date_time)
+ timer_delete_sync(&acb->refresh_timer);
arcmsr_disable_outbound_ints(acb);
- flush_work_sync(&acb->arcmsr_do_message_isr_bh);
+ arcmsr_free_irq(pdev, acb);
+ flush_work(&acb->arcmsr_do_message_isr_bh);
arcmsr_stop_adapter_bgrb(acb);
arcmsr_flush_adapter_cache(acb);
}
-static int arcmsr_module_init(void)
+static int __init arcmsr_module_init(void)
{
int error = 0;
error = pci_register_driver(&arcmsr_pci_driver);
return error;
}
-static void arcmsr_module_exit(void)
+static void __exit arcmsr_module_exit(void)
{
pci_unregister_driver(&arcmsr_pci_driver);
}
@@ -1092,11 +1816,27 @@ static void arcmsr_enable_outbound_ints(struct AdapterControlBlock *acb,
}
break;
case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *reg = acb->pmuC;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
mask = ~(ARCMSR_HBCMU_UTILITY_A_ISR_MASK | ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR_MASK|ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR_MASK);
writel(intmask_org & mask, &reg->host_int_mask);
acb->outbound_int_enable = ~(intmask_org & mask) & 0x0000000f;
}
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+
+ mask = ARCMSR_ARC1214_ALL_INT_ENABLE;
+ writel(intmask_org | mask, reg->pcief0_int_enable);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+
+ mask = ~(ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR | ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR);
+ writel(intmask_org & mask, &reg->host_int_mask);
+ break;
+ }
}
}
@@ -1116,7 +1856,7 @@ static int arcmsr_build_ccb(struct AdapterControlBlock *acb,
arcmsr_cdb->TargetID = pcmd->device->id;
arcmsr_cdb->LUN = pcmd->device->lun;
arcmsr_cdb->Function = 1;
- arcmsr_cdb->Context = 0;
+ arcmsr_cdb->msgContext = 0;
memcpy(arcmsr_cdb->Cdb, pcmd->cmnd, pcmd->cmd_len);
nseg = scsi_dma_map(pcmd);
@@ -1157,7 +1897,7 @@ static int arcmsr_build_ccb(struct AdapterControlBlock *acb,
static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandControlBlock *ccb)
{
- uint32_t cdb_phyaddr_pattern = ccb->cdb_phyaddr_pattern;
+ uint32_t cdb_phyaddr = ccb->cdb_phyaddr;
struct ARCMSR_CDB *arcmsr_cdb = (struct ARCMSR_CDB *)&ccb->arcmsr_cdb;
atomic_inc(&acb->ccboutstandingcount);
ccb->startdone = ARCMSR_CCB_START;
@@ -1166,25 +1906,24 @@ static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandContr
struct MessageUnit_A __iomem *reg = acb->pmuA;
if (arcmsr_cdb->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE)
- writel(cdb_phyaddr_pattern | ARCMSR_CCBPOST_FLAG_SGL_BSIZE,
+ writel(cdb_phyaddr | ARCMSR_CCBPOST_FLAG_SGL_BSIZE,
&reg->inbound_queueport);
- else {
- writel(cdb_phyaddr_pattern, &reg->inbound_queueport);
- }
- }
+ else
+ writel(cdb_phyaddr, &reg->inbound_queueport);
break;
+ }
case ACB_ADAPTER_TYPE_B: {
struct MessageUnit_B *reg = acb->pmuB;
uint32_t ending_index, index = reg->postq_index;
ending_index = ((index + 1) % ARCMSR_MAX_HBB_POSTQUEUE);
- writel(0, &reg->post_qbuffer[ending_index]);
+ reg->post_qbuffer[ending_index] = 0;
if (arcmsr_cdb->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE) {
- writel(cdb_phyaddr_pattern | ARCMSR_CCBPOST_FLAG_SGL_BSIZE,\
- &reg->post_qbuffer[index]);
+ reg->post_qbuffer[index] =
+ cdb_phyaddr | ARCMSR_CCBPOST_FLAG_SGL_BSIZE;
} else {
- writel(cdb_phyaddr_pattern, &reg->post_qbuffer[index]);
+ reg->post_qbuffer[index] = cdb_phyaddr;
}
index++;
index %= ARCMSR_MAX_HBB_POSTQUEUE;/*if last index number set it to 0 */
@@ -1193,83 +1932,183 @@ static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandContr
}
break;
case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *phbcmu = (struct MessageUnit_C *)acb->pmuC;
+ struct MessageUnit_C __iomem *phbcmu = acb->pmuC;
uint32_t ccb_post_stamp, arc_cdb_size;
arc_cdb_size = (ccb->arc_cdb_size > 0x300) ? 0x300 : ccb->arc_cdb_size;
- ccb_post_stamp = (cdb_phyaddr_pattern | ((arc_cdb_size - 1) >> 6) | 1);
- if (acb->cdb_phyaddr_hi32) {
- writel(acb->cdb_phyaddr_hi32, &phbcmu->inbound_queueport_high);
- writel(ccb_post_stamp, &phbcmu->inbound_queueport_low);
- } else {
- writel(ccb_post_stamp, &phbcmu->inbound_queueport_low);
+ ccb_post_stamp = (cdb_phyaddr | ((arc_cdb_size - 1) >> 6) | 1);
+ writel(upper_32_bits(ccb->cdb_phyaddr), &phbcmu->inbound_queueport_high);
+ writel(ccb_post_stamp, &phbcmu->inbound_queueport_low);
+ }
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *pmu = acb->pmuD;
+ u16 index_stripped;
+ u16 postq_index, toggle;
+ unsigned long flags;
+ struct InBound_SRB *pinbound_srb;
+
+ spin_lock_irqsave(&acb->postq_lock, flags);
+ postq_index = pmu->postq_index;
+ pinbound_srb = (struct InBound_SRB *)&(pmu->post_qbuffer[postq_index & 0xFF]);
+ pinbound_srb->addressHigh = upper_32_bits(ccb->cdb_phyaddr);
+ pinbound_srb->addressLow = cdb_phyaddr;
+ pinbound_srb->length = ccb->arc_cdb_size >> 2;
+ arcmsr_cdb->msgContext = dma_addr_lo32(cdb_phyaddr);
+ toggle = postq_index & 0x4000;
+ index_stripped = postq_index + 1;
+ index_stripped &= (ARCMSR_MAX_ARC1214_POSTQUEUE - 1);
+ pmu->postq_index = index_stripped ? (index_stripped | toggle) :
+ (toggle ^ 0x4000);
+ writel(postq_index, pmu->inboundlist_write_pointer);
+ spin_unlock_irqrestore(&acb->postq_lock, flags);
+ break;
}
+ case ACB_ADAPTER_TYPE_E: {
+ struct MessageUnit_E __iomem *pmu = acb->pmuE;
+ u32 ccb_post_stamp, arc_cdb_size;
+
+ arc_cdb_size = (ccb->arc_cdb_size > 0x300) ? 0x300 : ccb->arc_cdb_size;
+ ccb_post_stamp = (ccb->smid | ((arc_cdb_size - 1) >> 6));
+ writel(0, &pmu->inbound_queueport_high);
+ writel(ccb_post_stamp, &pmu->inbound_queueport_low);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_F __iomem *pmu = acb->pmuF;
+ u32 ccb_post_stamp, arc_cdb_size;
+
+ if (ccb->arc_cdb_size <= 0x300)
+ arc_cdb_size = (ccb->arc_cdb_size - 1) >> 6 | 1;
+ else {
+ arc_cdb_size = ((ccb->arc_cdb_size + 0xff) >> 8) + 2;
+ if (arc_cdb_size > 0xF)
+ arc_cdb_size = 0xF;
+ arc_cdb_size = (arc_cdb_size << 1) | 1;
+ }
+ ccb_post_stamp = (ccb->smid | arc_cdb_size);
+ writel(0, &pmu->inbound_queueport_high);
+ writel(ccb_post_stamp, &pmu->inbound_queueport_low);
+ break;
}
}
}
-static void arcmsr_stop_hba_bgrb(struct AdapterControlBlock *acb)
+static void arcmsr_hbaA_stop_bgrb(struct AdapterControlBlock *acb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
acb->acb_flags &= ~ACB_F_MSG_START_BGRB;
writel(ARCMSR_INBOUND_MESG0_STOP_BGRB, &reg->inbound_msgaddr0);
- if (!arcmsr_hba_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaA_wait_msgint_ready(acb)) {
printk(KERN_NOTICE
- "arcmsr%d: wait 'stop adapter background rebulid' timeout \n"
+ "arcmsr%d: wait 'stop adapter background rebuild' timeout\n"
, acb->host->host_no);
}
}
-static void arcmsr_stop_hbb_bgrb(struct AdapterControlBlock *acb)
+static void arcmsr_hbaB_stop_bgrb(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
acb->acb_flags &= ~ACB_F_MSG_START_BGRB;
writel(ARCMSR_MESSAGE_STOP_BGRB, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
printk(KERN_NOTICE
- "arcmsr%d: wait 'stop adapter background rebulid' timeout \n"
+ "arcmsr%d: wait 'stop adapter background rebuild' timeout\n"
, acb->host->host_no);
}
}
-static void arcmsr_stop_hbc_bgrb(struct AdapterControlBlock *pACB)
+static void arcmsr_hbaC_stop_bgrb(struct AdapterControlBlock *pACB)
{
- struct MessageUnit_C *reg = (struct MessageUnit_C *)pACB->pmuC;
+ struct MessageUnit_C __iomem *reg = pACB->pmuC;
pACB->acb_flags &= ~ACB_F_MSG_START_BGRB;
writel(ARCMSR_INBOUND_MESG0_STOP_BGRB, &reg->inbound_msgaddr0);
writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
- if (!arcmsr_hbc_wait_msgint_ready(pACB)) {
+ if (!arcmsr_hbaC_wait_msgint_ready(pACB)) {
printk(KERN_NOTICE
- "arcmsr%d: wait 'stop adapter background rebulid' timeout \n"
+ "arcmsr%d: wait 'stop adapter background rebuild' timeout\n"
, pACB->host->host_no);
}
return;
}
+
+static void arcmsr_hbaD_stop_bgrb(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_D *reg = pACB->pmuD;
+
+ pACB->acb_flags &= ~ACB_F_MSG_START_BGRB;
+ writel(ARCMSR_INBOUND_MESG0_STOP_BGRB, reg->inbound_msgaddr0);
+ if (!arcmsr_hbaD_wait_msgint_ready(pACB))
+ pr_notice("arcmsr%d: wait 'stop adapter background rebuild' "
+ "timeout\n", pACB->host->host_no);
+}
+
+static void arcmsr_hbaE_stop_bgrb(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_E __iomem *reg = pACB->pmuE;
+
+ pACB->acb_flags &= ~ACB_F_MSG_START_BGRB;
+ writel(ARCMSR_INBOUND_MESG0_STOP_BGRB, &reg->inbound_msgaddr0);
+ pACB->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pACB->out_doorbell, &reg->iobound_doorbell);
+ if (!arcmsr_hbaE_wait_msgint_ready(pACB)) {
+ pr_notice("arcmsr%d: wait 'stop adapter background rebuild' "
+ "timeout\n", pACB->host->host_no);
+ }
+}
+
static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb)
{
switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A: {
- arcmsr_stop_hba_bgrb(acb);
- }
+ case ACB_ADAPTER_TYPE_A:
+ arcmsr_hbaA_stop_bgrb(acb);
break;
-
- case ACB_ADAPTER_TYPE_B: {
- arcmsr_stop_hbb_bgrb(acb);
- }
+ case ACB_ADAPTER_TYPE_B:
+ arcmsr_hbaB_stop_bgrb(acb);
+ break;
+ case ACB_ADAPTER_TYPE_C:
+ arcmsr_hbaC_stop_bgrb(acb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ arcmsr_hbaD_stop_bgrb(acb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ arcmsr_hbaE_stop_bgrb(acb);
break;
- case ACB_ADAPTER_TYPE_C: {
- arcmsr_stop_hbc_bgrb(acb);
- }
}
}
static void arcmsr_free_ccb_pool(struct AdapterControlBlock *acb)
{
+ if (acb->xor_mega) {
+ struct Xor_sg *pXorPhys;
+ void **pXorVirt;
+ int i;
+
+ pXorPhys = (struct Xor_sg *)(acb->xorVirt +
+ sizeof(struct HostRamBuf));
+ pXorVirt = (void **)((unsigned long)acb->xorVirt +
+ (unsigned long)acb->xorVirtOffset);
+ for (i = 0; i < acb->xor_mega; i++) {
+ if (pXorPhys->xorPhys) {
+ dma_free_coherent(&acb->pdev->dev,
+ ARCMSR_XOR_SEG_SIZE,
+ *pXorVirt, pXorPhys->xorPhys);
+ pXorPhys->xorPhys = 0;
+ *pXorVirt = NULL;
+ }
+ pXorPhys++;
+ pXorVirt++;
+ }
+ dma_free_coherent(&acb->pdev->dev, acb->init2cfg_size,
+ acb->xorVirt, acb->xorPhys);
+ }
dma_free_coherent(&acb->pdev->dev, acb->uncache_size, acb->dma_coherent, acb->dma_coherent_handle);
}
-void arcmsr_iop_message_read(struct AdapterControlBlock *acb)
+static void arcmsr_iop_message_read(struct AdapterControlBlock *acb)
{
switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_A: {
@@ -1277,7 +2116,6 @@ void arcmsr_iop_message_read(struct AdapterControlBlock *acb)
writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, &reg->inbound_doorbell);
}
break;
-
case ACB_ADAPTER_TYPE_B: {
struct MessageUnit_B *reg = acb->pmuB;
writel(ARCMSR_DRV2IOP_DATA_READ_OK, reg->drv2iop_doorbell);
@@ -1285,8 +2123,23 @@ void arcmsr_iop_message_read(struct AdapterControlBlock *acb)
break;
case ACB_ADAPTER_TYPE_C: {
struct MessageUnit_C __iomem *reg = acb->pmuC;
+
writel(ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK, &reg->inbound_doorbell);
}
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ writel(ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ,
+ reg->inbound_doorbell);
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_DATA_READ_OK;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ }
+ break;
}
}
@@ -1321,6 +2174,19 @@ static void arcmsr_iop_message_wrote(struct AdapterControlBlock *acb)
writel(ARCMSR_HBCMU_DRV2IOP_DATA_WRITE_OK, &reg->inbound_doorbell);
}
break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ writel(ARCMSR_ARC1214_DRV2IOP_DATA_IN_READY,
+ reg->inbound_doorbell);
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_DATA_WRITE_OK;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ }
+ break;
}
}
@@ -1334,16 +2200,30 @@ struct QBUFFER __iomem *arcmsr_get_iop_rqbuffer(struct AdapterControlBlock *acb)
qbuffer = (struct QBUFFER __iomem *)&reg->message_rbuffer;
}
break;
-
case ACB_ADAPTER_TYPE_B: {
struct MessageUnit_B *reg = acb->pmuB;
qbuffer = (struct QBUFFER __iomem *)reg->message_rbuffer;
}
break;
case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *phbcmu = (struct MessageUnit_C *)acb->pmuC;
+ struct MessageUnit_C __iomem *phbcmu = acb->pmuC;
qbuffer = (struct QBUFFER __iomem *)&phbcmu->message_rbuffer;
}
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ qbuffer = (struct QBUFFER __iomem *)reg->message_rbuffer;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ qbuffer = (struct QBUFFER __iomem *)&reg->message_rbuffer;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_F: {
+ qbuffer = (struct QBUFFER __iomem *)acb->message_rbuffer;
+ }
+ break;
}
return qbuffer;
}
@@ -1358,103 +2238,225 @@ static struct QBUFFER __iomem *arcmsr_get_iop_wqbuffer(struct AdapterControlBloc
pqbuffer = (struct QBUFFER __iomem *) &reg->message_wbuffer;
}
break;
-
case ACB_ADAPTER_TYPE_B: {
struct MessageUnit_B *reg = acb->pmuB;
pqbuffer = (struct QBUFFER __iomem *)reg->message_wbuffer;
}
break;
case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *reg = (struct MessageUnit_C *)acb->pmuC;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
pqbuffer = (struct QBUFFER __iomem *)&reg->message_wbuffer;
- }
-
+ }
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ pqbuffer = (struct QBUFFER __iomem *)reg->message_wbuffer;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ pqbuffer = (struct QBUFFER __iomem *)&reg->message_wbuffer;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_F:
+ pqbuffer = (struct QBUFFER __iomem *)acb->message_wbuffer;
+ break;
}
return pqbuffer;
}
-static void arcmsr_iop2drv_data_wrote_handle(struct AdapterControlBlock *acb)
+static uint32_t
+arcmsr_Read_iop_rqbuffer_in_DWORD(struct AdapterControlBlock *acb,
+ struct QBUFFER __iomem *prbuffer)
{
- struct QBUFFER __iomem *prbuffer;
- struct QBUFFER *pQbuffer;
- uint8_t __iomem *iop_data;
- int32_t my_empty_len, iop_len, rqbuf_firstindex, rqbuf_lastindex;
- rqbuf_lastindex = acb->rqbuf_lastindex;
- rqbuf_firstindex = acb->rqbuf_firstindex;
- prbuffer = arcmsr_get_iop_rqbuffer(acb);
- iop_data = (uint8_t __iomem *)prbuffer->data;
- iop_len = prbuffer->data_len;
- my_empty_len = (rqbuf_firstindex - rqbuf_lastindex - 1) & (ARCMSR_MAX_QBUFFER - 1);
-
- if (my_empty_len >= iop_len)
- {
- while (iop_len > 0) {
- pQbuffer = (struct QBUFFER *)&acb->rqbuffer[rqbuf_lastindex];
- memcpy(pQbuffer, iop_data, 1);
- rqbuf_lastindex++;
- rqbuf_lastindex %= ARCMSR_MAX_QBUFFER;
+ uint8_t *pQbuffer;
+ uint8_t *buf1 = NULL;
+ uint32_t __iomem *iop_data;
+ uint32_t iop_len, data_len, *buf2 = NULL;
+
+ iop_data = (uint32_t __iomem *)prbuffer->data;
+ iop_len = readl(&prbuffer->data_len);
+ if (iop_len > 0) {
+ buf1 = kmalloc(128, GFP_ATOMIC);
+ buf2 = (uint32_t *)buf1;
+ if (buf1 == NULL)
+ return 0;
+ data_len = iop_len;
+ while (data_len >= 4) {
+ *buf2++ = readl(iop_data);
iop_data++;
- iop_len--;
+ data_len -= 4;
}
- acb->rqbuf_lastindex = rqbuf_lastindex;
- arcmsr_iop_message_read(acb);
+ if (data_len)
+ *buf2 = readl(iop_data);
+ buf2 = (uint32_t *)buf1;
+ }
+ while (iop_len > 0) {
+ pQbuffer = &acb->rqbuffer[acb->rqbuf_putIndex];
+ *pQbuffer = *buf1;
+ acb->rqbuf_putIndex++;
+ /* if last, index number set it to 0 */
+ acb->rqbuf_putIndex %= ARCMSR_MAX_QBUFFER;
+ buf1++;
+ iop_len--;
+ }
+ kfree(buf2);
+ /* let IOP know data has been read */
+ arcmsr_iop_message_read(acb);
+ return 1;
+}
+
+uint32_t
+arcmsr_Read_iop_rqbuffer_data(struct AdapterControlBlock *acb,
+ struct QBUFFER __iomem *prbuffer) {
+
+ uint8_t *pQbuffer;
+ uint8_t __iomem *iop_data;
+ uint32_t iop_len;
+
+ if (acb->adapter_type > ACB_ADAPTER_TYPE_B)
+ return arcmsr_Read_iop_rqbuffer_in_DWORD(acb, prbuffer);
+ iop_data = (uint8_t __iomem *)prbuffer->data;
+ iop_len = readl(&prbuffer->data_len);
+ while (iop_len > 0) {
+ pQbuffer = &acb->rqbuffer[acb->rqbuf_putIndex];
+ *pQbuffer = readb(iop_data);
+ acb->rqbuf_putIndex++;
+ acb->rqbuf_putIndex %= ARCMSR_MAX_QBUFFER;
+ iop_data++;
+ iop_len--;
}
+ arcmsr_iop_message_read(acb);
+ return 1;
+}
+
+static void arcmsr_iop2drv_data_wrote_handle(struct AdapterControlBlock *acb)
+{
+ unsigned long flags;
+ struct QBUFFER __iomem *prbuffer;
+ int32_t buf_empty_len;
- else {
+ spin_lock_irqsave(&acb->rqbuffer_lock, flags);
+ prbuffer = arcmsr_get_iop_rqbuffer(acb);
+ if (acb->rqbuf_putIndex >= acb->rqbuf_getIndex) {
+ buf_empty_len = (ARCMSR_MAX_QBUFFER - 1) -
+ (acb->rqbuf_putIndex - acb->rqbuf_getIndex);
+ } else
+ buf_empty_len = acb->rqbuf_getIndex - acb->rqbuf_putIndex - 1;
+ if (buf_empty_len >= readl(&prbuffer->data_len)) {
+ if (arcmsr_Read_iop_rqbuffer_data(acb, prbuffer) == 0)
+ acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
+ } else
acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
+ spin_unlock_irqrestore(&acb->rqbuffer_lock, flags);
+}
+
+static void arcmsr_write_ioctldata2iop_in_DWORD(struct AdapterControlBlock *acb)
+{
+ uint8_t *pQbuffer;
+ struct QBUFFER __iomem *pwbuffer;
+ uint8_t *buf1 = NULL;
+ uint32_t __iomem *iop_data;
+ uint32_t allxfer_len = 0, data_len, *buf2 = NULL, data;
+
+ if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_READED) {
+ buf1 = kmalloc(128, GFP_ATOMIC);
+ buf2 = (uint32_t *)buf1;
+ if (buf1 == NULL)
+ return;
+
+ acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READED);
+ pwbuffer = arcmsr_get_iop_wqbuffer(acb);
+ iop_data = (uint32_t __iomem *)pwbuffer->data;
+ while ((acb->wqbuf_getIndex != acb->wqbuf_putIndex)
+ && (allxfer_len < 124)) {
+ pQbuffer = &acb->wqbuffer[acb->wqbuf_getIndex];
+ *buf1 = *pQbuffer;
+ acb->wqbuf_getIndex++;
+ acb->wqbuf_getIndex %= ARCMSR_MAX_QBUFFER;
+ buf1++;
+ allxfer_len++;
+ }
+ data_len = allxfer_len;
+ buf1 = (uint8_t *)buf2;
+ while (data_len >= 4) {
+ data = *buf2++;
+ writel(data, iop_data);
+ iop_data++;
+ data_len -= 4;
+ }
+ if (data_len) {
+ data = *buf2;
+ writel(data, iop_data);
+ }
+ writel(allxfer_len, &pwbuffer->data_len);
+ kfree(buf1);
+ arcmsr_iop_message_wrote(acb);
}
}
-static void arcmsr_iop2drv_data_read_handle(struct AdapterControlBlock *acb)
+void
+arcmsr_write_ioctldata2iop(struct AdapterControlBlock *acb)
{
- acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_READED;
- if (acb->wqbuf_firstindex != acb->wqbuf_lastindex) {
- uint8_t *pQbuffer;
- struct QBUFFER __iomem *pwbuffer;
- uint8_t __iomem *iop_data;
- int32_t allxfer_len = 0;
+ uint8_t *pQbuffer;
+ struct QBUFFER __iomem *pwbuffer;
+ uint8_t __iomem *iop_data;
+ int32_t allxfer_len = 0;
+ if (acb->adapter_type > ACB_ADAPTER_TYPE_B) {
+ arcmsr_write_ioctldata2iop_in_DWORD(acb);
+ return;
+ }
+ if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_READED) {
acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READED);
pwbuffer = arcmsr_get_iop_wqbuffer(acb);
iop_data = (uint8_t __iomem *)pwbuffer->data;
-
- while ((acb->wqbuf_firstindex != acb->wqbuf_lastindex) && \
- (allxfer_len < 124)) {
- pQbuffer = &acb->wqbuffer[acb->wqbuf_firstindex];
- memcpy(iop_data, pQbuffer, 1);
- acb->wqbuf_firstindex++;
- acb->wqbuf_firstindex %= ARCMSR_MAX_QBUFFER;
+ while ((acb->wqbuf_getIndex != acb->wqbuf_putIndex)
+ && (allxfer_len < 124)) {
+ pQbuffer = &acb->wqbuffer[acb->wqbuf_getIndex];
+ writeb(*pQbuffer, iop_data);
+ acb->wqbuf_getIndex++;
+ acb->wqbuf_getIndex %= ARCMSR_MAX_QBUFFER;
iop_data++;
allxfer_len++;
}
- pwbuffer->data_len = allxfer_len;
-
+ writel(allxfer_len, &pwbuffer->data_len);
arcmsr_iop_message_wrote(acb);
}
+}
+
+static void arcmsr_iop2drv_data_read_handle(struct AdapterControlBlock *acb)
+{
+ unsigned long flags;
- if (acb->wqbuf_firstindex == acb->wqbuf_lastindex) {
+ spin_lock_irqsave(&acb->wqbuffer_lock, flags);
+ acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_READED;
+ if (acb->wqbuf_getIndex != acb->wqbuf_putIndex)
+ arcmsr_write_ioctldata2iop(acb);
+ if (acb->wqbuf_getIndex == acb->wqbuf_putIndex)
acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_CLEARED;
- }
+ spin_unlock_irqrestore(&acb->wqbuffer_lock, flags);
}
-static void arcmsr_hba_doorbell_isr(struct AdapterControlBlock *acb)
+static void arcmsr_hbaA_doorbell_isr(struct AdapterControlBlock *acb)
{
uint32_t outbound_doorbell;
struct MessageUnit_A __iomem *reg = acb->pmuA;
outbound_doorbell = readl(&reg->outbound_doorbell);
- writel(outbound_doorbell, &reg->outbound_doorbell);
- if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK) {
- arcmsr_iop2drv_data_wrote_handle(acb);
- }
-
- if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_READ_OK) {
- arcmsr_iop2drv_data_read_handle(acb);
- }
+ do {
+ writel(outbound_doorbell, &reg->outbound_doorbell);
+ if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK)
+ arcmsr_iop2drv_data_wrote_handle(acb);
+ if (outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_READ_OK)
+ arcmsr_iop2drv_data_read_handle(acb);
+ outbound_doorbell = readl(&reg->outbound_doorbell);
+ } while (outbound_doorbell & (ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK
+ | ARCMSR_OUTBOUND_IOP331_DATA_READ_OK));
}
-static void arcmsr_hbc_doorbell_isr(struct AdapterControlBlock *pACB)
+static void arcmsr_hbaC_doorbell_isr(struct AdapterControlBlock *pACB)
{
uint32_t outbound_doorbell;
- struct MessageUnit_C *reg = (struct MessageUnit_C *)pACB->pmuC;
+ struct MessageUnit_C __iomem *reg = pACB->pmuC;
/*
*******************************************************************
** Maybe here we need to check wrqbuffer_lock is lock or not
@@ -1463,33 +2465,95 @@ static void arcmsr_hbc_doorbell_isr(struct AdapterControlBlock *pACB)
*******************************************************************
*/
outbound_doorbell = readl(&reg->outbound_doorbell);
- writel(outbound_doorbell, &reg->outbound_doorbell_clear);/*clear interrupt*/
- if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_OK) {
- arcmsr_iop2drv_data_wrote_handle(pACB);
- }
- if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_READ_OK) {
- arcmsr_iop2drv_data_read_handle(pACB);
- }
- if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) {
- arcmsr_hbc_message_isr(pACB); /* messenger of "driver to iop commands" */
- }
- return;
+ do {
+ writel(outbound_doorbell, &reg->outbound_doorbell_clear);
+ readl(&reg->outbound_doorbell_clear);
+ if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_OK)
+ arcmsr_iop2drv_data_wrote_handle(pACB);
+ if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_READ_OK)
+ arcmsr_iop2drv_data_read_handle(pACB);
+ if (outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE)
+ arcmsr_hbaC_message_isr(pACB);
+ outbound_doorbell = readl(&reg->outbound_doorbell);
+ } while (outbound_doorbell & (ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_OK
+ | ARCMSR_HBCMU_IOP2DRV_DATA_READ_OK
+ | ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE));
+}
+
+static void arcmsr_hbaD_doorbell_isr(struct AdapterControlBlock *pACB)
+{
+ uint32_t outbound_doorbell;
+ struct MessageUnit_D *pmu = pACB->pmuD;
+
+ outbound_doorbell = readl(pmu->outbound_doorbell);
+ do {
+ writel(outbound_doorbell, pmu->outbound_doorbell);
+ if (outbound_doorbell & ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE)
+ arcmsr_hbaD_message_isr(pACB);
+ if (outbound_doorbell & ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK)
+ arcmsr_iop2drv_data_wrote_handle(pACB);
+ if (outbound_doorbell & ARCMSR_ARC1214_IOP2DRV_DATA_READ_OK)
+ arcmsr_iop2drv_data_read_handle(pACB);
+ outbound_doorbell = readl(pmu->outbound_doorbell);
+ } while (outbound_doorbell & (ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK
+ | ARCMSR_ARC1214_IOP2DRV_DATA_READ_OK
+ | ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE));
}
-static void arcmsr_hba_postqueue_isr(struct AdapterControlBlock *acb)
+
+static void arcmsr_hbaE_doorbell_isr(struct AdapterControlBlock *pACB)
+{
+ uint32_t outbound_doorbell, in_doorbell, tmp, i;
+ struct MessageUnit_E __iomem *reg = pACB->pmuE;
+
+ if (pACB->adapter_type == ACB_ADAPTER_TYPE_F) {
+ for (i = 0; i < 5; i++) {
+ in_doorbell = readl(&reg->iobound_doorbell);
+ if (in_doorbell != 0)
+ break;
+ }
+ } else
+ in_doorbell = readl(&reg->iobound_doorbell);
+ outbound_doorbell = in_doorbell ^ pACB->in_doorbell;
+ do {
+ writel(0, &reg->host_int_status); /* clear interrupt */
+ if (outbound_doorbell & ARCMSR_HBEMU_IOP2DRV_DATA_WRITE_OK) {
+ arcmsr_iop2drv_data_wrote_handle(pACB);
+ }
+ if (outbound_doorbell & ARCMSR_HBEMU_IOP2DRV_DATA_READ_OK) {
+ arcmsr_iop2drv_data_read_handle(pACB);
+ }
+ if (outbound_doorbell & ARCMSR_HBEMU_IOP2DRV_MESSAGE_CMD_DONE) {
+ arcmsr_hbaE_message_isr(pACB);
+ }
+ tmp = in_doorbell;
+ in_doorbell = readl(&reg->iobound_doorbell);
+ outbound_doorbell = tmp ^ in_doorbell;
+ } while (outbound_doorbell & (ARCMSR_HBEMU_IOP2DRV_DATA_WRITE_OK
+ | ARCMSR_HBEMU_IOP2DRV_DATA_READ_OK
+ | ARCMSR_HBEMU_IOP2DRV_MESSAGE_CMD_DONE));
+ pACB->in_doorbell = in_doorbell;
+}
+
+static void arcmsr_hbaA_postqueue_isr(struct AdapterControlBlock *acb)
{
uint32_t flag_ccb;
struct MessageUnit_A __iomem *reg = acb->pmuA;
struct ARCMSR_CDB *pARCMSR_CDB;
struct CommandControlBlock *pCCB;
bool error;
+ unsigned long cdb_phy_addr;
+
while ((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF) {
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ cdb_phy_addr = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ cdb_phy_addr = cdb_phy_addr | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + cdb_phy_addr);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
}
}
-static void arcmsr_hbb_postqueue_isr(struct AdapterControlBlock *acb)
+static void arcmsr_hbaB_postqueue_isr(struct AdapterControlBlock *acb)
{
uint32_t index;
uint32_t flag_ccb;
@@ -1497,48 +2561,160 @@ static void arcmsr_hbb_postqueue_isr(struct AdapterControlBlock *acb)
struct ARCMSR_CDB *pARCMSR_CDB;
struct CommandControlBlock *pCCB;
bool error;
+ unsigned long cdb_phy_addr;
+
index = reg->doneq_index;
- while ((flag_ccb = readl(&reg->done_qbuffer[index])) != 0) {
- writel(0, &reg->done_qbuffer[index]);
- pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/
+ while ((flag_ccb = reg->done_qbuffer[index]) != 0) {
+ cdb_phy_addr = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ cdb_phy_addr = cdb_phy_addr | acb->cdb_phyadd_hipart;
+ pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + cdb_phy_addr);
pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb);
error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false;
arcmsr_drain_donequeue(acb, pCCB, error);
+ reg->done_qbuffer[index] = 0;
index++;
index %= ARCMSR_MAX_HBB_POSTQUEUE;
reg->doneq_index = index;
}
}
-static void arcmsr_hbc_postqueue_isr(struct AdapterControlBlock *acb)
+static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb)
{
- struct MessageUnit_C *phbcmu;
+ struct MessageUnit_C __iomem *phbcmu;
struct ARCMSR_CDB *arcmsr_cdb;
struct CommandControlBlock *ccb;
- uint32_t flag_ccb, ccb_cdb_phy, throttling = 0;
+ uint32_t flag_ccb, throttling = 0;
+ unsigned long ccb_cdb_phy;
int error;
- phbcmu = (struct MessageUnit_C *)acb->pmuC;
+ phbcmu = acb->pmuC;
/* areca cdb command done */
/* Use correct offset and size for syncing */
- while (readl(&phbcmu->host_int_status) &
- ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR){
- /* check if command done with no error*/
- flag_ccb = readl(&phbcmu->outbound_queueport_low);
- ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);/*frame must be 32 bytes aligned*/
- arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
- ccb = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb);
- error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
- /* check if command done with no error */
- arcmsr_drain_donequeue(acb, ccb, error);
- if (throttling == ARCMSR_HBC_ISR_THROTTLING_LEVEL) {
- writel(ARCMSR_HBCMU_DRV2IOP_POSTQUEUE_THROTTLING, &phbcmu->inbound_doorbell);
- break;
+ while ((flag_ccb = readl(&phbcmu->outbound_queueport_low)) !=
+ 0xFFFFFFFF) {
+ ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset
+ + ccb_cdb_phy);
+ ccb = container_of(arcmsr_cdb, struct CommandControlBlock,
+ arcmsr_cdb);
+ error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1)
+ ? true : false;
+ /* check if command done with no error */
+ arcmsr_drain_donequeue(acb, ccb, error);
+ throttling++;
+ if (throttling == ARCMSR_HBC_ISR_THROTTLING_LEVEL) {
+ writel(ARCMSR_HBCMU_DRV2IOP_POSTQUEUE_THROTTLING,
+ &phbcmu->inbound_doorbell);
+ throttling = 0;
+ }
+ }
+}
+
+static void arcmsr_hbaD_postqueue_isr(struct AdapterControlBlock *acb)
+{
+ u32 outbound_write_pointer, doneq_index, index_stripped, toggle;
+ uint32_t addressLow;
+ int error;
+ struct MessageUnit_D *pmu;
+ struct ARCMSR_CDB *arcmsr_cdb;
+ struct CommandControlBlock *ccb;
+ unsigned long flags, ccb_cdb_phy;
+
+ spin_lock_irqsave(&acb->doneq_lock, flags);
+ pmu = acb->pmuD;
+ outbound_write_pointer = pmu->done_qbuffer[0].addressLow + 1;
+ doneq_index = pmu->doneq_index;
+ if ((doneq_index & 0xFFF) != (outbound_write_pointer & 0xFFF)) {
+ do {
+ toggle = doneq_index & 0x4000;
+ index_stripped = (doneq_index & 0xFFF) + 1;
+ index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
+ pmu->doneq_index = index_stripped ? (index_stripped | toggle) :
+ ((toggle ^ 0x4000) + 1);
+ doneq_index = pmu->doneq_index;
+ addressLow = pmu->done_qbuffer[doneq_index &
+ 0xFFF].addressLow;
+ ccb_cdb_phy = (addressLow & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset
+ + ccb_cdb_phy);
+ ccb = container_of(arcmsr_cdb,
+ struct CommandControlBlock, arcmsr_cdb);
+ error = (addressLow & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1)
+ ? true : false;
+ arcmsr_drain_donequeue(acb, ccb, error);
+ writel(doneq_index, pmu->outboundlist_read_pointer);
+ } while ((doneq_index & 0xFFF) !=
+ (outbound_write_pointer & 0xFFF));
+ }
+ writel(ARCMSR_ARC1214_OUTBOUND_LIST_INTERRUPT_CLEAR,
+ pmu->outboundlist_interrupt_cause);
+ readl(pmu->outboundlist_interrupt_cause);
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+}
+
+static void arcmsr_hbaE_postqueue_isr(struct AdapterControlBlock *acb)
+{
+ uint32_t doneq_index;
+ uint16_t cmdSMID;
+ int error;
+ struct MessageUnit_E __iomem *pmu;
+ struct CommandControlBlock *ccb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&acb->doneq_lock, flags);
+ doneq_index = acb->doneq_index;
+ pmu = acb->pmuE;
+ while ((readl(&pmu->reply_post_producer_index) & 0xFFFF) != doneq_index) {
+ cmdSMID = acb->pCompletionQ[doneq_index].cmdSMID;
+ ccb = acb->pccb_pool[cmdSMID];
+ error = (acb->pCompletionQ[doneq_index].cmdFlag
+ & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
+ arcmsr_drain_donequeue(acb, ccb, error);
+ doneq_index++;
+ if (doneq_index >= acb->completionQ_entry)
+ doneq_index = 0;
}
- throttling++;
+ acb->doneq_index = doneq_index;
+ writel(doneq_index, &pmu->reply_post_consumer_index);
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+}
+
+static void arcmsr_hbaF_postqueue_isr(struct AdapterControlBlock *acb)
+{
+ uint32_t doneq_index;
+ uint16_t cmdSMID;
+ int error;
+ struct MessageUnit_F __iomem *phbcmu;
+ struct CommandControlBlock *ccb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&acb->doneq_lock, flags);
+ doneq_index = acb->doneq_index;
+ phbcmu = acb->pmuF;
+ while (1) {
+ cmdSMID = acb->pCompletionQ[doneq_index].cmdSMID;
+ if (cmdSMID == 0xffff)
+ break;
+ ccb = acb->pccb_pool[cmdSMID];
+ error = (acb->pCompletionQ[doneq_index].cmdFlag &
+ ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
+ arcmsr_drain_donequeue(acb, ccb, error);
+ acb->pCompletionQ[doneq_index].cmdSMID = 0xffff;
+ doneq_index++;
+ if (doneq_index >= acb->completionQ_entry)
+ doneq_index = 0;
}
+ acb->doneq_index = doneq_index;
+ writel(doneq_index, &phbcmu->reply_post_consumer_index);
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
}
+
/*
**********************************************************************************
** Handle a message interrupt
@@ -1547,20 +2723,22 @@ static void arcmsr_hbc_postqueue_isr(struct AdapterControlBlock *acb)
** We want this in order to compare the drivemap so that we can detect newly-attached drives.
**********************************************************************************
*/
-static void arcmsr_hba_message_isr(struct AdapterControlBlock *acb)
+static void arcmsr_hbaA_message_isr(struct AdapterControlBlock *acb)
{
- struct MessageUnit_A *reg = acb->pmuA;
+ struct MessageUnit_A __iomem *reg = acb->pmuA;
/*clear interrupt and message state*/
writel(ARCMSR_MU_OUTBOUND_MESSAGE0_INT, &reg->outbound_intstatus);
- schedule_work(&acb->arcmsr_do_message_isr_bh);
+ if (acb->acb_flags & ACB_F_MSG_GET_CONFIG)
+ schedule_work(&acb->arcmsr_do_message_isr_bh);
}
-static void arcmsr_hbb_message_isr(struct AdapterControlBlock *acb)
+static void arcmsr_hbaB_message_isr(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
/*clear interrupt and message state*/
writel(ARCMSR_MESSAGE_INT_CLEAR_PATTERN, reg->iop2drv_doorbell);
- schedule_work(&acb->arcmsr_do_message_isr_bh);
+ if (acb->acb_flags & ACB_F_MSG_GET_CONFIG)
+ schedule_work(&acb->arcmsr_do_message_isr_bh);
}
/*
**********************************************************************************
@@ -1571,116 +2749,207 @@ static void arcmsr_hbb_message_isr(struct AdapterControlBlock *acb)
** We want this in order to compare the drivemap so that we can detect newly-attached drives.
**********************************************************************************
*/
-static void arcmsr_hbc_message_isr(struct AdapterControlBlock *acb)
+static void arcmsr_hbaC_message_isr(struct AdapterControlBlock *acb)
{
- struct MessageUnit_C *reg = acb->pmuC;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
/*clear interrupt and message state*/
writel(ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR, &reg->outbound_doorbell_clear);
- schedule_work(&acb->arcmsr_do_message_isr_bh);
+ if (acb->acb_flags & ACB_F_MSG_GET_CONFIG)
+ schedule_work(&acb->arcmsr_do_message_isr_bh);
+}
+
+static void arcmsr_hbaD_message_isr(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_D *reg = acb->pmuD;
+
+ writel(ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE, reg->outbound_doorbell);
+ readl(reg->outbound_doorbell);
+ if (acb->acb_flags & ACB_F_MSG_GET_CONFIG)
+ schedule_work(&acb->arcmsr_do_message_isr_bh);
}
-static int arcmsr_handle_hba_isr(struct AdapterControlBlock *acb)
+static void arcmsr_hbaE_message_isr(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+
+ writel(0, &reg->host_int_status);
+ if (acb->acb_flags & ACB_F_MSG_GET_CONFIG)
+ schedule_work(&acb->arcmsr_do_message_isr_bh);
+}
+
+static int arcmsr_hbaA_handle_isr(struct AdapterControlBlock *acb)
{
uint32_t outbound_intstatus;
struct MessageUnit_A __iomem *reg = acb->pmuA;
outbound_intstatus = readl(&reg->outbound_intstatus) &
acb->outbound_int_enable;
- if (!(outbound_intstatus & ARCMSR_MU_OUTBOUND_HANDLE_INT)) {
- return 1;
- }
- writel(outbound_intstatus, &reg->outbound_intstatus);
- if (outbound_intstatus & ARCMSR_MU_OUTBOUND_DOORBELL_INT) {
- arcmsr_hba_doorbell_isr(acb);
- }
- if (outbound_intstatus & ARCMSR_MU_OUTBOUND_POSTQUEUE_INT) {
- arcmsr_hba_postqueue_isr(acb);
- }
- if(outbound_intstatus & ARCMSR_MU_OUTBOUND_MESSAGE0_INT) {
- /* messenger of "driver to iop commands" */
- arcmsr_hba_message_isr(acb);
- }
- return 0;
+ if (!(outbound_intstatus & ARCMSR_MU_OUTBOUND_HANDLE_INT))
+ return IRQ_NONE;
+ do {
+ writel(outbound_intstatus, &reg->outbound_intstatus);
+ if (outbound_intstatus & ARCMSR_MU_OUTBOUND_DOORBELL_INT)
+ arcmsr_hbaA_doorbell_isr(acb);
+ if (outbound_intstatus & ARCMSR_MU_OUTBOUND_POSTQUEUE_INT)
+ arcmsr_hbaA_postqueue_isr(acb);
+ if (outbound_intstatus & ARCMSR_MU_OUTBOUND_MESSAGE0_INT)
+ arcmsr_hbaA_message_isr(acb);
+ outbound_intstatus = readl(&reg->outbound_intstatus) &
+ acb->outbound_int_enable;
+ } while (outbound_intstatus & (ARCMSR_MU_OUTBOUND_DOORBELL_INT
+ | ARCMSR_MU_OUTBOUND_POSTQUEUE_INT
+ | ARCMSR_MU_OUTBOUND_MESSAGE0_INT));
+ return IRQ_HANDLED;
}
-static int arcmsr_handle_hbb_isr(struct AdapterControlBlock *acb)
+static int arcmsr_hbaB_handle_isr(struct AdapterControlBlock *acb)
{
uint32_t outbound_doorbell;
struct MessageUnit_B *reg = acb->pmuB;
outbound_doorbell = readl(reg->iop2drv_doorbell) &
acb->outbound_int_enable;
if (!outbound_doorbell)
- return 1;
-
- writel(~outbound_doorbell, reg->iop2drv_doorbell);
- /*in case the last action of doorbell interrupt clearance is cached,
- this action can push HW to write down the clear bit*/
- readl(reg->iop2drv_doorbell);
- writel(ARCMSR_DRV2IOP_END_OF_INTERRUPT, reg->drv2iop_doorbell);
- if (outbound_doorbell & ARCMSR_IOP2DRV_DATA_WRITE_OK) {
- arcmsr_iop2drv_data_wrote_handle(acb);
- }
- if (outbound_doorbell & ARCMSR_IOP2DRV_DATA_READ_OK) {
- arcmsr_iop2drv_data_read_handle(acb);
- }
- if (outbound_doorbell & ARCMSR_IOP2DRV_CDB_DONE) {
- arcmsr_hbb_postqueue_isr(acb);
- }
- if(outbound_doorbell & ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) {
- /* messenger of "driver to iop commands" */
- arcmsr_hbb_message_isr(acb);
- }
- return 0;
+ return IRQ_NONE;
+ do {
+ writel(~outbound_doorbell, reg->iop2drv_doorbell);
+ writel(ARCMSR_DRV2IOP_END_OF_INTERRUPT, reg->drv2iop_doorbell);
+ if (outbound_doorbell & ARCMSR_IOP2DRV_DATA_WRITE_OK)
+ arcmsr_iop2drv_data_wrote_handle(acb);
+ if (outbound_doorbell & ARCMSR_IOP2DRV_DATA_READ_OK)
+ arcmsr_iop2drv_data_read_handle(acb);
+ if (outbound_doorbell & ARCMSR_IOP2DRV_CDB_DONE)
+ arcmsr_hbaB_postqueue_isr(acb);
+ if (outbound_doorbell & ARCMSR_IOP2DRV_MESSAGE_CMD_DONE)
+ arcmsr_hbaB_message_isr(acb);
+ outbound_doorbell = readl(reg->iop2drv_doorbell) &
+ acb->outbound_int_enable;
+ } while (outbound_doorbell & (ARCMSR_IOP2DRV_DATA_WRITE_OK
+ | ARCMSR_IOP2DRV_DATA_READ_OK
+ | ARCMSR_IOP2DRV_CDB_DONE
+ | ARCMSR_IOP2DRV_MESSAGE_CMD_DONE));
+ return IRQ_HANDLED;
}
-static int arcmsr_handle_hbc_isr(struct AdapterControlBlock *pACB)
+static int arcmsr_hbaC_handle_isr(struct AdapterControlBlock *pACB)
{
uint32_t host_interrupt_status;
- struct MessageUnit_C *phbcmu = (struct MessageUnit_C *)pACB->pmuC;
+ struct MessageUnit_C __iomem *phbcmu = pACB->pmuC;
/*
*********************************************
** check outbound intstatus
*********************************************
*/
- host_interrupt_status = readl(&phbcmu->host_int_status);
- if (!host_interrupt_status) {
- /*it must be share irq*/
- return 1;
- }
- /* MU ioctl transfer doorbell interrupts*/
- if (host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR) {
- arcmsr_hbc_doorbell_isr(pACB); /* messenger of "ioctl message read write" */
- }
- /* MU post queue interrupts*/
- if (host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) {
- arcmsr_hbc_postqueue_isr(pACB); /* messenger of "scsi commands" */
- }
- return 0;
+ host_interrupt_status = readl(&phbcmu->host_int_status) &
+ (ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR);
+ if (!host_interrupt_status)
+ return IRQ_NONE;
+ do {
+ if (host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR)
+ arcmsr_hbaC_doorbell_isr(pACB);
+ /* MU post queue interrupts*/
+ if (host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR)
+ arcmsr_hbaC_postqueue_isr(pACB);
+ host_interrupt_status = readl(&phbcmu->host_int_status);
+ } while (host_interrupt_status & (ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR));
+ return IRQ_HANDLED;
}
-static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
+
+static irqreturn_t arcmsr_hbaD_handle_isr(struct AdapterControlBlock *pACB)
{
- switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A: {
- if (arcmsr_handle_hba_isr(acb)) {
- return IRQ_NONE;
- }
- }
- break;
+ u32 host_interrupt_status;
+ struct MessageUnit_D *pmu = pACB->pmuD;
+
+ host_interrupt_status = readl(pmu->host_int_status) &
+ (ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR);
+ if (!host_interrupt_status)
+ return IRQ_NONE;
+ do {
+ /* MU post queue interrupts*/
+ if (host_interrupt_status &
+ ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR)
+ arcmsr_hbaD_postqueue_isr(pACB);
+ if (host_interrupt_status &
+ ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR)
+ arcmsr_hbaD_doorbell_isr(pACB);
+ host_interrupt_status = readl(pmu->host_int_status);
+ } while (host_interrupt_status &
+ (ARCMSR_ARC1214_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_ARC1214_OUTBOUND_DOORBELL_ISR));
+ return IRQ_HANDLED;
+}
- case ACB_ADAPTER_TYPE_B: {
- if (arcmsr_handle_hbb_isr(acb)) {
- return IRQ_NONE;
- }
- }
- break;
- case ACB_ADAPTER_TYPE_C: {
- if (arcmsr_handle_hbc_isr(acb)) {
- return IRQ_NONE;
+static irqreturn_t arcmsr_hbaE_handle_isr(struct AdapterControlBlock *pACB)
+{
+ uint32_t host_interrupt_status;
+ struct MessageUnit_E __iomem *pmu = pACB->pmuE;
+
+ host_interrupt_status = readl(&pmu->host_int_status) &
+ (ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR);
+ if (!host_interrupt_status)
+ return IRQ_NONE;
+ do {
+ /* MU ioctl transfer doorbell interrupts*/
+ if (host_interrupt_status & ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR) {
+ arcmsr_hbaE_doorbell_isr(pACB);
}
+ /* MU post queue interrupts*/
+ if (host_interrupt_status & ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR) {
+ arcmsr_hbaE_postqueue_isr(pACB);
}
- }
+ host_interrupt_status = readl(&pmu->host_int_status);
+ } while (host_interrupt_status & (ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR));
return IRQ_HANDLED;
}
+static irqreturn_t arcmsr_hbaF_handle_isr(struct AdapterControlBlock *pACB)
+{
+ uint32_t host_interrupt_status;
+ struct MessageUnit_F __iomem *phbcmu = pACB->pmuF;
+
+ host_interrupt_status = readl(&phbcmu->host_int_status) &
+ (ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR);
+ if (!host_interrupt_status)
+ return IRQ_NONE;
+ do {
+ /* MU post queue interrupts*/
+ if (host_interrupt_status & ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR)
+ arcmsr_hbaF_postqueue_isr(pACB);
+
+ /* MU ioctl transfer doorbell interrupts*/
+ if (host_interrupt_status & ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR)
+ arcmsr_hbaE_doorbell_isr(pACB);
+
+ host_interrupt_status = readl(&phbcmu->host_int_status);
+ } while (host_interrupt_status & (ARCMSR_HBEMU_OUTBOUND_POSTQUEUE_ISR |
+ ARCMSR_HBEMU_OUTBOUND_DOORBELL_ISR));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
+{
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_A:
+ return arcmsr_hbaA_handle_isr(acb);
+ case ACB_ADAPTER_TYPE_B:
+ return arcmsr_hbaB_handle_isr(acb);
+ case ACB_ADAPTER_TYPE_C:
+ return arcmsr_hbaC_handle_isr(acb);
+ case ACB_ADAPTER_TYPE_D:
+ return arcmsr_hbaD_handle_isr(acb);
+ case ACB_ADAPTER_TYPE_E:
+ return arcmsr_hbaE_handle_isr(acb);
+ case ACB_ADAPTER_TYPE_F:
+ return arcmsr_hbaF_handle_isr(acb);
+ default:
+ return IRQ_NONE;
+ }
+}
+
static void arcmsr_iop_parking(struct AdapterControlBlock *acb)
{
if (acb) {
@@ -1696,311 +2965,298 @@ static void arcmsr_iop_parking(struct AdapterControlBlock *acb)
}
}
-void arcmsr_post_ioctldata2iop(struct AdapterControlBlock *acb)
+
+void arcmsr_clear_iop2drv_rqueue_buffer(struct AdapterControlBlock *acb)
{
- int32_t wqbuf_firstindex, wqbuf_lastindex;
- uint8_t *pQbuffer;
- struct QBUFFER __iomem *pwbuffer;
- uint8_t __iomem *iop_data;
- int32_t allxfer_len = 0;
- pwbuffer = arcmsr_get_iop_wqbuffer(acb);
- iop_data = (uint8_t __iomem *)pwbuffer->data;
- if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_READED) {
- acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READED);
- wqbuf_firstindex = acb->wqbuf_firstindex;
- wqbuf_lastindex = acb->wqbuf_lastindex;
- while ((wqbuf_firstindex != wqbuf_lastindex) && (allxfer_len < 124)) {
- pQbuffer = &acb->wqbuffer[wqbuf_firstindex];
- memcpy(iop_data, pQbuffer, 1);
- wqbuf_firstindex++;
- wqbuf_firstindex %= ARCMSR_MAX_QBUFFER;
- iop_data++;
- allxfer_len++;
+ uint32_t i;
+
+ if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
+ for (i = 0; i < 15; i++) {
+ if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
+ acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
+ acb->rqbuf_getIndex = 0;
+ acb->rqbuf_putIndex = 0;
+ arcmsr_iop_message_read(acb);
+ mdelay(30);
+ } else if (acb->rqbuf_getIndex !=
+ acb->rqbuf_putIndex) {
+ acb->rqbuf_getIndex = 0;
+ acb->rqbuf_putIndex = 0;
+ mdelay(30);
+ } else
+ break;
}
- acb->wqbuf_firstindex = wqbuf_firstindex;
- pwbuffer->data_len = allxfer_len;
- arcmsr_iop_message_wrote(acb);
}
}
static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb,
- struct scsi_cmnd *cmd)
+ struct scsi_cmnd *cmd)
{
- struct CMD_MESSAGE_FIELD *pcmdmessagefld;
- int retvalue = 0, transfer_len = 0;
char *buffer;
+ unsigned short use_sg;
+ int retvalue = 0, transfer_len = 0;
+ unsigned long flags;
+ struct CMD_MESSAGE_FIELD *pcmdmessagefld;
+ uint32_t controlcode = (uint32_t)cmd->cmnd[5] << 24 |
+ (uint32_t)cmd->cmnd[6] << 16 |
+ (uint32_t)cmd->cmnd[7] << 8 |
+ (uint32_t)cmd->cmnd[8];
struct scatterlist *sg;
- uint32_t controlcode = (uint32_t ) cmd->cmnd[5] << 24 |
- (uint32_t ) cmd->cmnd[6] << 16 |
- (uint32_t ) cmd->cmnd[7] << 8 |
- (uint32_t ) cmd->cmnd[8];
- /* 4 bytes: Areca io control code */
+
+ use_sg = scsi_sg_count(cmd);
sg = scsi_sglist(cmd);
- buffer = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset;
- if (scsi_sg_count(cmd) > 1) {
+ buffer = kmap_atomic(sg_page(sg)) + sg->offset;
+ if (use_sg > 1) {
retvalue = ARCMSR_MESSAGE_FAIL;
goto message_out;
}
transfer_len += sg->length;
-
if (transfer_len > sizeof(struct CMD_MESSAGE_FIELD)) {
retvalue = ARCMSR_MESSAGE_FAIL;
+ pr_info("%s: ARCMSR_MESSAGE_FAIL!\n", __func__);
goto message_out;
}
- pcmdmessagefld = (struct CMD_MESSAGE_FIELD *) buffer;
- switch(controlcode) {
-
+ pcmdmessagefld = (struct CMD_MESSAGE_FIELD *)buffer;
+ switch (controlcode) {
case ARCMSR_MESSAGE_READ_RQBUFFER: {
unsigned char *ver_addr;
- uint8_t *pQbuffer, *ptmpQbuffer;
- int32_t allxfer_len = 0;
-
- ver_addr = kmalloc(1032, GFP_ATOMIC);
+ uint8_t *ptmpQbuffer;
+ uint32_t allxfer_len = 0;
+ ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC);
if (!ver_addr) {
retvalue = ARCMSR_MESSAGE_FAIL;
+ pr_info("%s: memory not enough!\n", __func__);
goto message_out;
}
-
ptmpQbuffer = ver_addr;
- while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex)
- && (allxfer_len < 1031)) {
- pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex];
- memcpy(ptmpQbuffer, pQbuffer, 1);
- acb->rqbuf_firstindex++;
- acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER;
- ptmpQbuffer++;
- allxfer_len++;
+ spin_lock_irqsave(&acb->rqbuffer_lock, flags);
+ if (acb->rqbuf_getIndex != acb->rqbuf_putIndex) {
+ unsigned int tail = acb->rqbuf_getIndex;
+ unsigned int head = acb->rqbuf_putIndex;
+ unsigned int cnt_to_end = CIRC_CNT_TO_END(head, tail, ARCMSR_MAX_QBUFFER);
+
+ allxfer_len = CIRC_CNT(head, tail, ARCMSR_MAX_QBUFFER);
+ if (allxfer_len > ARCMSR_API_DATA_BUFLEN)
+ allxfer_len = ARCMSR_API_DATA_BUFLEN;
+
+ if (allxfer_len <= cnt_to_end)
+ memcpy(ptmpQbuffer, acb->rqbuffer + tail, allxfer_len);
+ else {
+ memcpy(ptmpQbuffer, acb->rqbuffer + tail, cnt_to_end);
+ memcpy(ptmpQbuffer + cnt_to_end, acb->rqbuffer, allxfer_len - cnt_to_end);
+ }
+ acb->rqbuf_getIndex = (acb->rqbuf_getIndex + allxfer_len) % ARCMSR_MAX_QBUFFER;
}
+ memcpy(pcmdmessagefld->messagedatabuffer, ver_addr,
+ allxfer_len);
if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
-
struct QBUFFER __iomem *prbuffer;
- uint8_t __iomem *iop_data;
- int32_t iop_len;
-
acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
prbuffer = arcmsr_get_iop_rqbuffer(acb);
- iop_data = prbuffer->data;
- iop_len = readl(&prbuffer->data_len);
- while (iop_len > 0) {
- acb->rqbuffer[acb->rqbuf_lastindex] = readb(iop_data);
- acb->rqbuf_lastindex++;
- acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER;
- iop_data++;
- iop_len--;
- }
- arcmsr_iop_message_read(acb);
- }
- memcpy(pcmdmessagefld->messagedatabuffer, ver_addr, allxfer_len);
- pcmdmessagefld->cmdmessage.Length = allxfer_len;
- if(acb->fw_flag == FW_DEADLOCK) {
- pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }else{
- pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK;
+ if (arcmsr_Read_iop_rqbuffer_data(acb, prbuffer) == 0)
+ acb->acb_flags |= ACB_F_IOPDATA_OVERFLOW;
}
+ spin_unlock_irqrestore(&acb->rqbuffer_lock, flags);
kfree(ver_addr);
- }
+ pcmdmessagefld->cmdmessage.Length = allxfer_len;
+ if (acb->fw_flag == FW_DEADLOCK)
+ pcmdmessagefld->cmdmessage.ReturnCode =
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
+ pcmdmessagefld->cmdmessage.ReturnCode =
+ ARCMSR_MESSAGE_RETURNCODE_OK;
break;
-
+ }
case ARCMSR_MESSAGE_WRITE_WQBUFFER: {
unsigned char *ver_addr;
- int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex;
+ uint32_t user_len;
+ int32_t cnt2end;
uint8_t *pQbuffer, *ptmpuserbuffer;
- ver_addr = kmalloc(1032, GFP_ATOMIC);
- if (!ver_addr) {
+ user_len = pcmdmessagefld->cmdmessage.Length;
+ if (user_len > ARCMSR_API_DATA_BUFLEN) {
retvalue = ARCMSR_MESSAGE_FAIL;
goto message_out;
}
- if(acb->fw_flag == FW_DEADLOCK) {
- pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }else{
- pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_OK;
+
+ ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC);
+ if (!ver_addr) {
+ retvalue = ARCMSR_MESSAGE_FAIL;
+ goto message_out;
}
ptmpuserbuffer = ver_addr;
- user_len = pcmdmessagefld->cmdmessage.Length;
- memcpy(ptmpuserbuffer, pcmdmessagefld->messagedatabuffer, user_len);
- wqbuf_lastindex = acb->wqbuf_lastindex;
- wqbuf_firstindex = acb->wqbuf_firstindex;
- if (wqbuf_lastindex != wqbuf_firstindex) {
+
+ memcpy(ptmpuserbuffer,
+ pcmdmessagefld->messagedatabuffer, user_len);
+ spin_lock_irqsave(&acb->wqbuffer_lock, flags);
+ if (acb->wqbuf_putIndex != acb->wqbuf_getIndex) {
struct SENSE_DATA *sensebuffer =
(struct SENSE_DATA *)cmd->sense_buffer;
- arcmsr_post_ioctldata2iop(acb);
+ arcmsr_write_ioctldata2iop(acb);
/* has error report sensedata */
- sensebuffer->ErrorCode = 0x70;
+ sensebuffer->ErrorCode = SCSI_SENSE_CURRENT_ERRORS;
sensebuffer->SenseKey = ILLEGAL_REQUEST;
sensebuffer->AdditionalSenseLength = 0x0A;
sensebuffer->AdditionalSenseCode = 0x20;
sensebuffer->Valid = 1;
retvalue = ARCMSR_MESSAGE_FAIL;
} else {
- my_empty_len = (wqbuf_firstindex-wqbuf_lastindex - 1)
- &(ARCMSR_MAX_QBUFFER - 1);
- if (my_empty_len >= user_len) {
- while (user_len > 0) {
- pQbuffer =
- &acb->wqbuffer[acb->wqbuf_lastindex];
- memcpy(pQbuffer, ptmpuserbuffer, 1);
- acb->wqbuf_lastindex++;
- acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER;
- ptmpuserbuffer++;
- user_len--;
- }
- if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) {
- acb->acb_flags &=
- ~ACB_F_MESSAGE_WQBUFFER_CLEARED;
- arcmsr_post_ioctldata2iop(acb);
- }
- } else {
- /* has error report sensedata */
- struct SENSE_DATA *sensebuffer =
- (struct SENSE_DATA *)cmd->sense_buffer;
- sensebuffer->ErrorCode = 0x70;
- sensebuffer->SenseKey = ILLEGAL_REQUEST;
- sensebuffer->AdditionalSenseLength = 0x0A;
- sensebuffer->AdditionalSenseCode = 0x20;
- sensebuffer->Valid = 1;
- retvalue = ARCMSR_MESSAGE_FAIL;
+ pQbuffer = &acb->wqbuffer[acb->wqbuf_putIndex];
+ cnt2end = ARCMSR_MAX_QBUFFER - acb->wqbuf_putIndex;
+ if (user_len > cnt2end) {
+ memcpy(pQbuffer, ptmpuserbuffer, cnt2end);
+ ptmpuserbuffer += cnt2end;
+ user_len -= cnt2end;
+ acb->wqbuf_putIndex = 0;
+ pQbuffer = acb->wqbuffer;
}
+ memcpy(pQbuffer, ptmpuserbuffer, user_len);
+ acb->wqbuf_putIndex += user_len;
+ acb->wqbuf_putIndex %= ARCMSR_MAX_QBUFFER;
+ if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) {
+ acb->acb_flags &=
+ ~ACB_F_MESSAGE_WQBUFFER_CLEARED;
+ arcmsr_write_ioctldata2iop(acb);
}
- kfree(ver_addr);
}
+ spin_unlock_irqrestore(&acb->wqbuffer_lock, flags);
+ kfree(ver_addr);
+ if (acb->fw_flag == FW_DEADLOCK)
+ pcmdmessagefld->cmdmessage.ReturnCode =
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
+ pcmdmessagefld->cmdmessage.ReturnCode =
+ ARCMSR_MESSAGE_RETURNCODE_OK;
break;
-
+ }
case ARCMSR_MESSAGE_CLEAR_RQBUFFER: {
uint8_t *pQbuffer = acb->rqbuffer;
- if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
- acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
- arcmsr_iop_message_read(acb);
- }
+
+ arcmsr_clear_iop2drv_rqueue_buffer(acb);
+ spin_lock_irqsave(&acb->rqbuffer_lock, flags);
acb->acb_flags |= ACB_F_MESSAGE_RQBUFFER_CLEARED;
- acb->rqbuf_firstindex = 0;
- acb->rqbuf_lastindex = 0;
+ acb->rqbuf_getIndex = 0;
+ acb->rqbuf_putIndex = 0;
memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER);
- if(acb->fw_flag == FW_DEADLOCK) {
+ spin_unlock_irqrestore(&acb->rqbuffer_lock, flags);
+ if (acb->fw_flag == FW_DEADLOCK)
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }else{
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_OK;
- }
- }
+ ARCMSR_MESSAGE_RETURNCODE_OK;
break;
-
+ }
case ARCMSR_MESSAGE_CLEAR_WQBUFFER: {
uint8_t *pQbuffer = acb->wqbuffer;
- if(acb->fw_flag == FW_DEADLOCK) {
+ spin_lock_irqsave(&acb->wqbuffer_lock, flags);
+ acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
+ ACB_F_MESSAGE_WQBUFFER_READED);
+ acb->wqbuf_getIndex = 0;
+ acb->wqbuf_putIndex = 0;
+ memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER);
+ spin_unlock_irqrestore(&acb->wqbuffer_lock, flags);
+ if (acb->fw_flag == FW_DEADLOCK)
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }else{
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_OK;
- }
-
- if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
- acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
- arcmsr_iop_message_read(acb);
- }
- acb->acb_flags |=
- (ACB_F_MESSAGE_WQBUFFER_CLEARED |
- ACB_F_MESSAGE_WQBUFFER_READED);
- acb->wqbuf_firstindex = 0;
- acb->wqbuf_lastindex = 0;
- memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER);
- }
+ ARCMSR_MESSAGE_RETURNCODE_OK;
break;
-
+ }
case ARCMSR_MESSAGE_CLEAR_ALLQBUFFER: {
uint8_t *pQbuffer;
-
- if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
- acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
- arcmsr_iop_message_read(acb);
- }
- acb->acb_flags |=
- (ACB_F_MESSAGE_WQBUFFER_CLEARED
- | ACB_F_MESSAGE_RQBUFFER_CLEARED
- | ACB_F_MESSAGE_WQBUFFER_READED);
- acb->rqbuf_firstindex = 0;
- acb->rqbuf_lastindex = 0;
- acb->wqbuf_firstindex = 0;
- acb->wqbuf_lastindex = 0;
+ arcmsr_clear_iop2drv_rqueue_buffer(acb);
+ spin_lock_irqsave(&acb->rqbuffer_lock, flags);
+ acb->acb_flags |= ACB_F_MESSAGE_RQBUFFER_CLEARED;
+ acb->rqbuf_getIndex = 0;
+ acb->rqbuf_putIndex = 0;
pQbuffer = acb->rqbuffer;
memset(pQbuffer, 0, sizeof(struct QBUFFER));
+ spin_unlock_irqrestore(&acb->rqbuffer_lock, flags);
+ spin_lock_irqsave(&acb->wqbuffer_lock, flags);
+ acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
+ ACB_F_MESSAGE_WQBUFFER_READED);
+ acb->wqbuf_getIndex = 0;
+ acb->wqbuf_putIndex = 0;
pQbuffer = acb->wqbuffer;
memset(pQbuffer, 0, sizeof(struct QBUFFER));
- if(acb->fw_flag == FW_DEADLOCK) {
+ spin_unlock_irqrestore(&acb->wqbuffer_lock, flags);
+ if (acb->fw_flag == FW_DEADLOCK)
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }else{
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_OK;
- }
- }
+ ARCMSR_MESSAGE_RETURNCODE_OK;
break;
-
+ }
case ARCMSR_MESSAGE_RETURN_CODE_3F: {
- if(acb->fw_flag == FW_DEADLOCK) {
+ if (acb->fw_flag == FW_DEADLOCK)
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }else{
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_3F;
- }
+ ARCMSR_MESSAGE_RETURNCODE_3F;
break;
- }
+ }
case ARCMSR_MESSAGE_SAY_HELLO: {
int8_t *hello_string = "Hello! I am ARCMSR";
- if(acb->fw_flag == FW_DEADLOCK) {
+ if (acb->fw_flag == FW_DEADLOCK)
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }else{
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_OK;
- }
- memcpy(pcmdmessagefld->messagedatabuffer, hello_string
- , (int16_t)strlen(hello_string));
- }
+ ARCMSR_MESSAGE_RETURNCODE_OK;
+ memcpy(pcmdmessagefld->messagedatabuffer,
+ hello_string, (int16_t)strlen(hello_string));
break;
-
- case ARCMSR_MESSAGE_SAY_GOODBYE:
- if(acb->fw_flag == FW_DEADLOCK) {
+ }
+ case ARCMSR_MESSAGE_SAY_GOODBYE: {
+ if (acb->fw_flag == FW_DEADLOCK)
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
+ pcmdmessagefld->cmdmessage.ReturnCode =
+ ARCMSR_MESSAGE_RETURNCODE_OK;
arcmsr_iop_parking(acb);
break;
-
- case ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE:
- if(acb->fw_flag == FW_DEADLOCK) {
+ }
+ case ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE: {
+ if (acb->fw_flag == FW_DEADLOCK)
pcmdmessagefld->cmdmessage.ReturnCode =
- ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
- }
+ ARCMSR_MESSAGE_RETURNCODE_BUS_HANG_ON;
+ else
+ pcmdmessagefld->cmdmessage.ReturnCode =
+ ARCMSR_MESSAGE_RETURNCODE_OK;
arcmsr_flush_adapter_cache(acb);
break;
-
+ }
default:
retvalue = ARCMSR_MESSAGE_FAIL;
+ pr_info("%s: unknown controlcode!\n", __func__);
+ }
+message_out:
+ if (use_sg) {
+ struct scatterlist *sg = scsi_sglist(cmd);
+ kunmap_atomic(buffer - sg->offset);
}
- message_out:
- sg = scsi_sglist(cmd);
- kunmap_atomic(buffer - sg->offset, KM_IRQ0);
return retvalue;
}
static struct CommandControlBlock *arcmsr_get_freeccb(struct AdapterControlBlock *acb)
{
- struct list_head *head = &acb->ccb_free_list;
+ struct list_head *head;
struct CommandControlBlock *ccb = NULL;
unsigned long flags;
+
spin_lock_irqsave(&acb->ccblist_lock, flags);
+ head = &acb->ccb_free_list;
if (!list_empty(head)) {
ccb = list_entry(head->next, struct CommandControlBlock, list);
list_del_init(&ccb->list);
}else{
spin_unlock_irqrestore(&acb->ccblist_lock, flags);
- return 0;
+ return NULL;
}
spin_unlock_irqrestore(&acb->ccblist_lock, flags);
return ccb;
@@ -2017,7 +3273,7 @@ static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb,
if (cmd->device->lun) {
cmd->result = (DID_TIME_OUT << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return;
}
inqdata[0] = TYPE_PROCESSOR;
@@ -2028,67 +3284,59 @@ static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb,
/* ISO, ECMA, & ANSI versions */
inqdata[4] = 31;
/* length of additional data */
- strncpy(&inqdata[8], "Areca ", 8);
+ memcpy(&inqdata[8], "Areca ", 8);
/* Vendor Identification */
- strncpy(&inqdata[16], "RAID controller ", 16);
+ memcpy(&inqdata[16], "RAID controller ", 16);
/* Product Identification */
- strncpy(&inqdata[32], "R001", 4); /* Product Revision */
+ memcpy(&inqdata[32], "R001", 4); /* Product Revision */
sg = scsi_sglist(cmd);
- buffer = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset;
+ buffer = kmap_atomic(sg_page(sg)) + sg->offset;
memcpy(buffer, inqdata, sizeof(inqdata));
sg = scsi_sglist(cmd);
- kunmap_atomic(buffer - sg->offset, KM_IRQ0);
+ kunmap_atomic(buffer - sg->offset);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
break;
case WRITE_BUFFER:
case READ_BUFFER: {
if (arcmsr_iop_message_xfer(acb, cmd))
cmd->result = (DID_ERROR << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
break;
default:
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
}
-static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd,
- void (* done)(struct scsi_cmnd *))
+static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd)
{
struct Scsi_Host *host = cmd->device->host;
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
struct CommandControlBlock *ccb;
int target = cmd->device->id;
- int lun = cmd->device->lun;
- uint8_t scsicmd = cmd->cmnd[0];
- cmd->scsi_done = done;
- cmd->host_scribble = NULL;
- cmd->result = 0;
- if ((scsicmd == SYNCHRONIZE_CACHE) ||(scsicmd == SEND_DIAGNOSTIC)){
- if(acb->devstate[target][lun] == ARECA_RAID_GONE) {
- cmd->result = (DID_NO_CONNECT << 16);
- }
- cmd->scsi_done(cmd);
+
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED) {
+ cmd->result = (DID_NO_CONNECT << 16);
+ scsi_done(cmd);
return 0;
}
+ cmd->host_scribble = NULL;
+ cmd->result = 0;
if (target == 16) {
/* virtual device for iop message transfer */
arcmsr_handle_virtual_command(acb, cmd);
return 0;
}
- if (atomic_read(&acb->ccboutstandingcount) >=
- ARCMSR_MAX_OUTSTANDING_CMD)
- return SCSI_MLQUEUE_HOST_BUSY;
ccb = arcmsr_get_freeccb(acb);
if (!ccb)
return SCSI_MLQUEUE_HOST_BUSY;
if (arcmsr_build_ccb( acb, ccb, cmd ) == FAILED) {
- cmd->result = (DID_ERROR << 16) | (RESERVATION_CONFLICT << 1);
- cmd->scsi_done(cmd);
+ cmd->result = (DID_ERROR << 16) | SAM_STAT_RESERVATION_CONFLICT;
+ scsi_done(cmd);
return 0;
}
arcmsr_post_ccb(acb, ccb);
@@ -2097,210 +3345,232 @@ static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd,
static DEF_SCSI_QCMD(arcmsr_queue_command)
-static bool arcmsr_get_hba_config(struct AdapterControlBlock *acb)
+static int arcmsr_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ unsigned int dev_timeout;
+
+ dev_timeout = sdev->request_queue->rq_timeout;
+ if ((cmd_timeout > 0) && ((cmd_timeout * HZ) > dev_timeout))
+ blk_queue_rq_timeout(sdev->request_queue, cmd_timeout * HZ);
+ return 0;
+}
+
+static void arcmsr_get_adapter_config(struct AdapterControlBlock *pACB, uint32_t *rwbuffer)
{
- struct MessageUnit_A __iomem *reg = acb->pmuA;
- char *acb_firm_model = acb->firm_model;
- char *acb_firm_version = acb->firm_version;
- char *acb_device_map = acb->device_map;
- char __iomem *iop_firm_model = (char __iomem *)(&reg->message_rwbuffer[15]);
- char __iomem *iop_firm_version = (char __iomem *)(&reg->message_rwbuffer[17]);
- char __iomem *iop_device_map = (char __iomem *)(&reg->message_rwbuffer[21]);
int count;
- writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
- if (!arcmsr_hba_wait_msgint_ready(acb)) {
- printk(KERN_NOTICE "arcmsr%d: wait 'get adapter firmware \
- miscellaneous data' timeout \n", acb->host->host_no);
- return false;
- }
- count = 8;
- while (count){
- *acb_firm_model = readb(iop_firm_model);
+ uint32_t *acb_firm_model = (uint32_t *)pACB->firm_model;
+ uint32_t *acb_firm_version = (uint32_t *)pACB->firm_version;
+ uint32_t *acb_device_map = (uint32_t *)pACB->device_map;
+ uint32_t *firm_model = &rwbuffer[15];
+ uint32_t *firm_version = &rwbuffer[17];
+ uint32_t *device_map = &rwbuffer[21];
+
+ count = 2;
+ while (count) {
+ *acb_firm_model = readl(firm_model);
acb_firm_model++;
- iop_firm_model++;
+ firm_model++;
count--;
}
-
- count = 16;
- while (count){
- *acb_firm_version = readb(iop_firm_version);
+ count = 4;
+ while (count) {
+ *acb_firm_version = readl(firm_version);
acb_firm_version++;
- iop_firm_version++;
+ firm_version++;
count--;
}
-
- count=16;
- while(count){
- *acb_device_map = readb(iop_device_map);
+ count = 4;
+ while (count) {
+ *acb_device_map = readl(device_map);
acb_device_map++;
- iop_device_map++;
+ device_map++;
count--;
}
- printk(KERN_NOTICE "Areca RAID Controller%d: F/W %s & Model %s\n",
- acb->host->host_no,
- acb->firm_version,
- acb->firm_model);
- acb->signature = readl(&reg->message_rwbuffer[0]);
- acb->firm_request_len = readl(&reg->message_rwbuffer[1]);
- acb->firm_numbers_queue = readl(&reg->message_rwbuffer[2]);
- acb->firm_sdram_size = readl(&reg->message_rwbuffer[3]);
- acb->firm_hd_channels = readl(&reg->message_rwbuffer[4]);
- acb->firm_cfg_version = readl(&reg->message_rwbuffer[25]); /*firm_cfg_version,25,100-103*/
+ pACB->signature = readl(&rwbuffer[0]);
+ pACB->firm_request_len = readl(&rwbuffer[1]);
+ pACB->firm_numbers_queue = readl(&rwbuffer[2]);
+ pACB->firm_sdram_size = readl(&rwbuffer[3]);
+ pACB->firm_hd_channels = readl(&rwbuffer[4]);
+ pACB->firm_cfg_version = readl(&rwbuffer[25]);
+ if (pACB->adapter_type == ACB_ADAPTER_TYPE_F)
+ pACB->firm_PicStatus = readl(&rwbuffer[30]);
+ else
+ pACB->firm_PicStatus = 0;
+ pr_notice("Areca RAID Controller%d: Model %s, F/W %s\n",
+ pACB->host->host_no,
+ pACB->firm_model,
+ pACB->firm_version);
+}
+
+static bool arcmsr_hbaA_get_config(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_A __iomem *reg = acb->pmuA;
+
+ arcmsr_wait_firmware_ready(acb);
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
+ if (!arcmsr_hbaA_wait_msgint_ready(acb)) {
+ printk(KERN_NOTICE "arcmsr%d: wait 'get adapter firmware \
+ miscellaneous data' timeout \n", acb->host->host_no);
+ return false;
+ }
+ arcmsr_get_adapter_config(acb, reg->message_rwbuffer);
return true;
}
-static bool arcmsr_get_hbb_config(struct AdapterControlBlock *acb)
+static bool arcmsr_hbaB_get_config(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
- struct pci_dev *pdev = acb->pdev;
- void *dma_coherent;
- dma_addr_t dma_coherent_handle;
- char *acb_firm_model = acb->firm_model;
- char *acb_firm_version = acb->firm_version;
- char *acb_device_map = acb->device_map;
- char __iomem *iop_firm_model;
- /*firm_model,15,60-67*/
- char __iomem *iop_firm_version;
- /*firm_version,17,68-83*/
- char __iomem *iop_device_map;
- /*firm_version,21,84-99*/
- int count;
- dma_coherent = dma_alloc_coherent(&pdev->dev, sizeof(struct MessageUnit_B), &dma_coherent_handle, GFP_KERNEL);
- if (!dma_coherent){
- printk(KERN_NOTICE "arcmsr%d: dma_alloc_coherent got error for hbb mu\n", acb->host->host_no);
+
+ arcmsr_wait_firmware_ready(acb);
+ writel(ARCMSR_MESSAGE_START_DRIVER_MODE, reg->drv2iop_doorbell);
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
+ printk(KERN_ERR "arcmsr%d: can't set driver mode.\n", acb->host->host_no);
return false;
}
- acb->dma_coherent_handle_hbb_mu = dma_coherent_handle;
- reg = (struct MessageUnit_B *)dma_coherent;
- acb->pmuB = reg;
- reg->drv2iop_doorbell= (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_DRV2IOP_DOORBELL);
- reg->drv2iop_doorbell_mask = (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_DRV2IOP_DOORBELL_MASK);
- reg->iop2drv_doorbell = (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_IOP2DRV_DOORBELL);
- reg->iop2drv_doorbell_mask = (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_IOP2DRV_DOORBELL_MASK);
- reg->message_wbuffer = (uint32_t __iomem *)((unsigned long)acb->mem_base1 + ARCMSR_MESSAGE_WBUFFER);
- reg->message_rbuffer = (uint32_t __iomem *)((unsigned long)acb->mem_base1 + ARCMSR_MESSAGE_RBUFFER);
- reg->message_rwbuffer = (uint32_t __iomem *)((unsigned long)acb->mem_base1 + ARCMSR_MESSAGE_RWBUFFER);
- iop_firm_model = (char __iomem *)(&reg->message_rwbuffer[15]); /*firm_model,15,60-67*/
- iop_firm_version = (char __iomem *)(&reg->message_rwbuffer[17]); /*firm_version,17,68-83*/
- iop_device_map = (char __iomem *)(&reg->message_rwbuffer[21]); /*firm_version,21,84-99*/
-
writel(ARCMSR_MESSAGE_GET_CONFIG, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "arcmsr%d: wait 'get adapter firmware \
miscellaneous data' timeout \n", acb->host->host_no);
return false;
}
- count = 8;
- while (count){
- *acb_firm_model = readb(iop_firm_model);
- acb_firm_model++;
- iop_firm_model++;
- count--;
- }
- count = 16;
- while (count){
- *acb_firm_version = readb(iop_firm_version);
- acb_firm_version++;
- iop_firm_version++;
- count--;
- }
-
- count = 16;
- while(count){
- *acb_device_map = readb(iop_device_map);
- acb_device_map++;
- iop_device_map++;
- count--;
- }
-
- printk(KERN_NOTICE "Areca RAID Controller%d: F/W %s & Model %s\n",
- acb->host->host_no,
- acb->firm_version,
- acb->firm_model);
-
- acb->signature = readl(&reg->message_rwbuffer[1]);
- /*firm_signature,1,00-03*/
- acb->firm_request_len = readl(&reg->message_rwbuffer[2]);
- /*firm_request_len,1,04-07*/
- acb->firm_numbers_queue = readl(&reg->message_rwbuffer[3]);
- /*firm_numbers_queue,2,08-11*/
- acb->firm_sdram_size = readl(&reg->message_rwbuffer[4]);
- /*firm_sdram_size,3,12-15*/
- acb->firm_hd_channels = readl(&reg->message_rwbuffer[5]);
- /*firm_ide_channels,4,16-19*/
- acb->firm_cfg_version = readl(&reg->message_rwbuffer[25]); /*firm_cfg_version,25,100-103*/
- /*firm_ide_channels,4,16-19*/
+ arcmsr_get_adapter_config(acb, reg->message_rwbuffer);
return true;
}
-static bool arcmsr_get_hbc_config(struct AdapterControlBlock *pACB)
+static bool arcmsr_hbaC_get_config(struct AdapterControlBlock *pACB)
{
- uint32_t intmask_org, Index, firmware_state = 0;
- struct MessageUnit_C *reg = pACB->pmuC;
- char *acb_firm_model = pACB->firm_model;
- char *acb_firm_version = pACB->firm_version;
- char *iop_firm_model = (char *)(&reg->msgcode_rwbuffer[15]); /*firm_model,15,60-67*/
- char *iop_firm_version = (char *)(&reg->msgcode_rwbuffer[17]); /*firm_version,17,68-83*/
- int count;
+ uint32_t intmask_org;
+ struct MessageUnit_C __iomem *reg = pACB->pmuC;
+
/* disable all outbound interrupt */
intmask_org = readl(&reg->host_int_mask); /* disable outbound message0 int */
writel(intmask_org|ARCMSR_HBCMU_ALL_INTMASKENABLE, &reg->host_int_mask);
/* wait firmware ready */
- do {
- firmware_state = readl(&reg->outbound_msgaddr1);
- } while ((firmware_state & ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK) == 0);
+ arcmsr_wait_firmware_ready(pACB);
/* post "get config" instruction */
writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
/* wait message ready */
- for (Index = 0; Index < 2000; Index++) {
- if (readl(&reg->outbound_doorbell) & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) {
- writel(ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR, &reg->outbound_doorbell_clear);/*clear interrupt*/
- break;
- }
- udelay(10);
- } /*max 1 seconds*/
- if (Index >= 2000) {
+ if (!arcmsr_hbaC_wait_msgint_ready(pACB)) {
printk(KERN_NOTICE "arcmsr%d: wait 'get adapter firmware \
miscellaneous data' timeout \n", pACB->host->host_no);
return false;
}
- count = 8;
- while (count) {
- *acb_firm_model = readb(iop_firm_model);
- acb_firm_model++;
- iop_firm_model++;
- count--;
+ arcmsr_get_adapter_config(pACB, reg->msgcode_rwbuffer);
+ return true;
+}
+
+static bool arcmsr_hbaD_get_config(struct AdapterControlBlock *acb)
+{
+ struct MessageUnit_D *reg = acb->pmuD;
+
+ if (readl(acb->pmuD->outbound_doorbell) &
+ ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE) {
+ writel(ARCMSR_ARC1214_IOP2DRV_MESSAGE_CMD_DONE,
+ acb->pmuD->outbound_doorbell);/*clear interrupt*/
}
- count = 16;
- while (count) {
- *acb_firm_version = readb(iop_firm_version);
- acb_firm_version++;
- iop_firm_version++;
- count--;
+ arcmsr_wait_firmware_ready(acb);
+ /* post "get config" instruction */
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, reg->inbound_msgaddr0);
+ /* wait message ready */
+ if (!arcmsr_hbaD_wait_msgint_ready(acb)) {
+ pr_notice("arcmsr%d: wait get adapter firmware "
+ "miscellaneous data timeout\n", acb->host->host_no);
+ return false;
}
- printk(KERN_NOTICE "Areca RAID Controller%d: F/W %s & Model %s\n",
- pACB->host->host_no,
- pACB->firm_version,
- pACB->firm_model);
- pACB->firm_request_len = readl(&reg->msgcode_rwbuffer[1]); /*firm_request_len,1,04-07*/
- pACB->firm_numbers_queue = readl(&reg->msgcode_rwbuffer[2]); /*firm_numbers_queue,2,08-11*/
- pACB->firm_sdram_size = readl(&reg->msgcode_rwbuffer[3]); /*firm_sdram_size,3,12-15*/
- pACB->firm_hd_channels = readl(&reg->msgcode_rwbuffer[4]); /*firm_ide_channels,4,16-19*/
- pACB->firm_cfg_version = readl(&reg->msgcode_rwbuffer[25]); /*firm_cfg_version,25,100-103*/
- /*all interrupt service will be enable at arcmsr_iop_init*/
+ arcmsr_get_adapter_config(acb, reg->msgcode_rwbuffer);
+ return true;
+}
+
+static bool arcmsr_hbaE_get_config(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_E __iomem *reg = pACB->pmuE;
+ uint32_t intmask_org;
+
+ /* disable all outbound interrupt */
+ intmask_org = readl(&reg->host_int_mask); /* disable outbound message0 int */
+ writel(intmask_org | ARCMSR_HBEMU_ALL_INTMASKENABLE, &reg->host_int_mask);
+ /* wait firmware ready */
+ arcmsr_wait_firmware_ready(pACB);
+ mdelay(20);
+ /* post "get config" instruction */
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
+
+ pACB->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pACB->out_doorbell, &reg->iobound_doorbell);
+ /* wait message ready */
+ if (!arcmsr_hbaE_wait_msgint_ready(pACB)) {
+ pr_notice("arcmsr%d: wait get adapter firmware "
+ "miscellaneous data timeout\n", pACB->host->host_no);
+ return false;
+ }
+ arcmsr_get_adapter_config(pACB, reg->msgcode_rwbuffer);
+ return true;
+}
+
+static bool arcmsr_hbaF_get_config(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_F __iomem *reg = pACB->pmuF;
+ uint32_t intmask_org;
+
+ /* disable all outbound interrupt */
+ intmask_org = readl(&reg->host_int_mask); /* disable outbound message0 int */
+ writel(intmask_org | ARCMSR_HBEMU_ALL_INTMASKENABLE, &reg->host_int_mask);
+ /* wait firmware ready */
+ arcmsr_wait_firmware_ready(pACB);
+ /* post "get config" instruction */
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
+
+ pACB->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pACB->out_doorbell, &reg->iobound_doorbell);
+ /* wait message ready */
+ if (!arcmsr_hbaE_wait_msgint_ready(pACB)) {
+ pr_notice("arcmsr%d: wait get adapter firmware miscellaneous data timeout\n",
+ pACB->host->host_no);
+ return false;
+ }
+ arcmsr_get_adapter_config(pACB, pACB->msgcode_rwbuffer);
return true;
}
+
static bool arcmsr_get_firmware_spec(struct AdapterControlBlock *acb)
{
- if (acb->adapter_type == ACB_ADAPTER_TYPE_A)
- return arcmsr_get_hba_config(acb);
- else if (acb->adapter_type == ACB_ADAPTER_TYPE_B)
- return arcmsr_get_hbb_config(acb);
+ bool rtn = false;
+
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_A:
+ rtn = arcmsr_hbaA_get_config(acb);
+ break;
+ case ACB_ADAPTER_TYPE_B:
+ rtn = arcmsr_hbaB_get_config(acb);
+ break;
+ case ACB_ADAPTER_TYPE_C:
+ rtn = arcmsr_hbaC_get_config(acb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ rtn = arcmsr_hbaD_get_config(acb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ rtn = arcmsr_hbaE_get_config(acb);
+ break;
+ case ACB_ADAPTER_TYPE_F:
+ rtn = arcmsr_hbaF_get_config(acb);
+ break;
+ default:
+ break;
+ }
+ acb->maxOutstanding = acb->firm_numbers_queue - 1;
+ if (acb->host->can_queue >= acb->firm_numbers_queue)
+ acb->host->can_queue = acb->maxOutstanding;
else
- return arcmsr_get_hbc_config(acb);
+ acb->maxOutstanding = acb->host->can_queue;
+ acb->maxFreeCCB = acb->host->can_queue;
+ if (acb->maxFreeCCB < ARCMSR_MAX_FREECCB_NUM)
+ acb->maxFreeCCB += 64;
+ return rtn;
}
-static int arcmsr_polling_hba_ccbdone(struct AdapterControlBlock *acb,
+static int arcmsr_hbaA_polling_ccbdone(struct AdapterControlBlock *acb,
struct CommandControlBlock *poll_ccb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
@@ -2309,7 +3579,9 @@ static int arcmsr_polling_hba_ccbdone(struct AdapterControlBlock *acb,
uint32_t flag_ccb, outbound_intstatus, poll_ccb_done = 0, poll_count = 0;
int rtn;
bool error;
- polling_hba_ccb_retry:
+ unsigned long ccb_cdb_phy;
+
+polling_hba_ccb_retry:
poll_count++;
outbound_intstatus = readl(&reg->outbound_intstatus) & acb->outbound_int_enable;
writel(outbound_intstatus, &reg->outbound_intstatus);/*clear interrupt*/
@@ -2327,16 +3599,19 @@ static int arcmsr_polling_hba_ccbdone(struct AdapterControlBlock *acb,
goto polling_hba_ccb_retry;
}
}
- arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
ccb = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb);
- poll_ccb_done = (ccb == poll_ccb) ? 1:0;
+ poll_ccb_done |= (ccb == poll_ccb) ? 1 : 0;
if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) {
if ((ccb->startdone == ARCMSR_CCB_ABORTED) || (ccb == poll_ccb)) {
printk(KERN_NOTICE "arcmsr%d: scsi id = %d lun = %d ccb = '0x%p'"
" poll command abort successfully \n"
, acb->host->host_no
, ccb->pcmd->device->id
- , ccb->pcmd->device->lun
+ , (u32)ccb->pcmd->device->lun
, ccb);
ccb->pcmd->result = DID_ABORT << 16;
arcmsr_ccb_complete(ccb);
@@ -2356,7 +3631,7 @@ static int arcmsr_polling_hba_ccbdone(struct AdapterControlBlock *acb,
return rtn;
}
-static int arcmsr_polling_hbb_ccbdone(struct AdapterControlBlock *acb,
+static int arcmsr_hbaB_polling_ccbdone(struct AdapterControlBlock *acb,
struct CommandControlBlock *poll_ccb)
{
struct MessageUnit_B *reg = acb->pmuB;
@@ -2365,14 +3640,16 @@ static int arcmsr_polling_hbb_ccbdone(struct AdapterControlBlock *acb,
uint32_t flag_ccb, poll_ccb_done = 0, poll_count = 0;
int index, rtn;
bool error;
- polling_hbb_ccb_retry:
+ unsigned long ccb_cdb_phy;
+polling_hbb_ccb_retry:
poll_count++;
/* clear doorbell interrupt */
writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell);
while(1){
index = reg->doneq_index;
- if ((flag_ccb = readl(&reg->done_qbuffer[index])) == 0) {
+ flag_ccb = reg->done_qbuffer[index];
+ if (flag_ccb == 0) {
if (poll_ccb_done){
rtn = SUCCESS;
break;
@@ -2385,22 +3662,25 @@ static int arcmsr_polling_hbb_ccbdone(struct AdapterControlBlock *acb,
goto polling_hbb_ccb_retry;
}
}
- writel(0, &reg->done_qbuffer[index]);
+ reg->done_qbuffer[index] = 0;
index++;
/*if last index number set it to 0 */
index %= ARCMSR_MAX_HBB_POSTQUEUE;
reg->doneq_index = index;
/* check if command done with no error*/
- arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));
+ ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff;
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
ccb = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb);
- poll_ccb_done = (ccb == poll_ccb) ? 1:0;
+ poll_ccb_done |= (ccb == poll_ccb) ? 1 : 0;
if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) {
if ((ccb->startdone == ARCMSR_CCB_ABORTED) || (ccb == poll_ccb)) {
printk(KERN_NOTICE "arcmsr%d: scsi id = %d lun = %d ccb = '0x%p'"
" poll command abort successfully \n"
,acb->host->host_no
,ccb->pcmd->device->id
- ,ccb->pcmd->device->lun
+ ,(u32)ccb->pcmd->device->lun
,ccb);
ccb->pcmd->result = DID_ABORT << 16;
arcmsr_ccb_complete(ccb);
@@ -2420,15 +3700,18 @@ static int arcmsr_polling_hbb_ccbdone(struct AdapterControlBlock *acb,
return rtn;
}
-static int arcmsr_polling_hbc_ccbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_ccb)
+static int arcmsr_hbaC_polling_ccbdone(struct AdapterControlBlock *acb,
+ struct CommandControlBlock *poll_ccb)
{
- struct MessageUnit_C *reg = (struct MessageUnit_C *)acb->pmuC;
- uint32_t flag_ccb, ccb_cdb_phy;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
+ uint32_t flag_ccb;
struct ARCMSR_CDB *arcmsr_cdb;
bool error;
struct CommandControlBlock *pCCB;
uint32_t poll_ccb_done = 0, poll_count = 0;
int rtn;
+ unsigned long ccb_cdb_phy;
+
polling_hbc_ccb_retry:
poll_count++;
while (1) {
@@ -2447,9 +3730,11 @@ polling_hbc_ccb_retry:
}
flag_ccb = readl(&reg->outbound_queueport_low);
ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
- arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);/*frame must be 32 bytes aligned*/
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);
pCCB = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb);
- poll_ccb_done = (pCCB == poll_ccb) ? 1 : 0;
+ poll_ccb_done |= (pCCB == poll_ccb) ? 1 : 0;
/* check ifcommand done with no error*/
if ((pCCB->acb != acb) || (pCCB->startdone != ARCMSR_CCB_START)) {
if (pCCB->startdone == ARCMSR_CCB_ABORTED) {
@@ -2457,10 +3742,10 @@ polling_hbc_ccb_retry:
" poll command abort successfully \n"
, acb->host->host_no
, pCCB->pcmd->device->id
- , pCCB->pcmd->device->lun
+ , (u32)pCCB->pcmd->device->lun
, pCCB);
- pCCB->pcmd->result = DID_ABORT << 16;
- arcmsr_ccb_complete(pCCB);
+ pCCB->pcmd->result = DID_ABORT << 16;
+ arcmsr_ccb_complete(pCCB);
continue;
}
printk(KERN_NOTICE "arcmsr%d: polling get an illegal ccb"
@@ -2476,42 +3761,300 @@ polling_hbc_ccb_retry:
}
return rtn;
}
+
+static int arcmsr_hbaD_polling_ccbdone(struct AdapterControlBlock *acb,
+ struct CommandControlBlock *poll_ccb)
+{
+ bool error;
+ uint32_t poll_ccb_done = 0, poll_count = 0, flag_ccb;
+ int rtn, doneq_index, index_stripped, outbound_write_pointer, toggle;
+ unsigned long flags, ccb_cdb_phy;
+ struct ARCMSR_CDB *arcmsr_cdb;
+ struct CommandControlBlock *pCCB;
+ struct MessageUnit_D *pmu = acb->pmuD;
+
+polling_hbaD_ccb_retry:
+ poll_count++;
+ while (1) {
+ spin_lock_irqsave(&acb->doneq_lock, flags);
+ outbound_write_pointer = pmu->done_qbuffer[0].addressLow + 1;
+ doneq_index = pmu->doneq_index;
+ if ((outbound_write_pointer & 0xFFF) == (doneq_index & 0xFFF)) {
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ if (poll_ccb_done) {
+ rtn = SUCCESS;
+ break;
+ } else {
+ msleep(25);
+ if (poll_count > 40) {
+ rtn = FAILED;
+ break;
+ }
+ goto polling_hbaD_ccb_retry;
+ }
+ }
+ toggle = doneq_index & 0x4000;
+ index_stripped = (doneq_index & 0xFFF) + 1;
+ index_stripped %= ARCMSR_MAX_ARC1214_DONEQUEUE;
+ pmu->doneq_index = index_stripped ? (index_stripped | toggle) :
+ ((toggle ^ 0x4000) + 1);
+ doneq_index = pmu->doneq_index;
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ flag_ccb = pmu->done_qbuffer[doneq_index & 0xFFF].addressLow;
+ ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0);
+ if (acb->cdb_phyadd_hipart)
+ ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart;
+ arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset +
+ ccb_cdb_phy);
+ pCCB = container_of(arcmsr_cdb, struct CommandControlBlock,
+ arcmsr_cdb);
+ poll_ccb_done |= (pCCB == poll_ccb) ? 1 : 0;
+ if ((pCCB->acb != acb) ||
+ (pCCB->startdone != ARCMSR_CCB_START)) {
+ if (pCCB->startdone == ARCMSR_CCB_ABORTED) {
+ pr_notice("arcmsr%d: scsi id = %d "
+ "lun = %d ccb = '0x%p' poll command "
+ "abort successfully\n"
+ , acb->host->host_no
+ , pCCB->pcmd->device->id
+ , (u32)pCCB->pcmd->device->lun
+ , pCCB);
+ pCCB->pcmd->result = DID_ABORT << 16;
+ arcmsr_ccb_complete(pCCB);
+ continue;
+ }
+ pr_notice("arcmsr%d: polling an illegal "
+ "ccb command done ccb = '0x%p' "
+ "ccboutstandingcount = %d\n"
+ , acb->host->host_no
+ , pCCB
+ , atomic_read(&acb->ccboutstandingcount));
+ continue;
+ }
+ error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1)
+ ? true : false;
+ arcmsr_report_ccb_state(acb, pCCB, error);
+ }
+ return rtn;
+}
+
+static int arcmsr_hbaE_polling_ccbdone(struct AdapterControlBlock *acb,
+ struct CommandControlBlock *poll_ccb)
+{
+ bool error;
+ uint32_t poll_ccb_done = 0, poll_count = 0, doneq_index;
+ uint16_t cmdSMID;
+ unsigned long flags;
+ int rtn;
+ struct CommandControlBlock *pCCB;
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+
+ polling_hbaC_ccb_retry:
+ poll_count++;
+ while (1) {
+ spin_lock_irqsave(&acb->doneq_lock, flags);
+ doneq_index = acb->doneq_index;
+ if ((readl(&reg->reply_post_producer_index) & 0xFFFF) ==
+ doneq_index) {
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ if (poll_ccb_done) {
+ rtn = SUCCESS;
+ break;
+ } else {
+ msleep(25);
+ if (poll_count > 40) {
+ rtn = FAILED;
+ break;
+ }
+ goto polling_hbaC_ccb_retry;
+ }
+ }
+ cmdSMID = acb->pCompletionQ[doneq_index].cmdSMID;
+ doneq_index++;
+ if (doneq_index >= acb->completionQ_entry)
+ doneq_index = 0;
+ acb->doneq_index = doneq_index;
+ spin_unlock_irqrestore(&acb->doneq_lock, flags);
+ pCCB = acb->pccb_pool[cmdSMID];
+ poll_ccb_done |= (pCCB == poll_ccb) ? 1 : 0;
+ /* check if command done with no error*/
+ if ((pCCB->acb != acb) || (pCCB->startdone != ARCMSR_CCB_START)) {
+ if (pCCB->startdone == ARCMSR_CCB_ABORTED) {
+ pr_notice("arcmsr%d: scsi id = %d "
+ "lun = %d ccb = '0x%p' poll command "
+ "abort successfully\n"
+ , acb->host->host_no
+ , pCCB->pcmd->device->id
+ , (u32)pCCB->pcmd->device->lun
+ , pCCB);
+ pCCB->pcmd->result = DID_ABORT << 16;
+ arcmsr_ccb_complete(pCCB);
+ continue;
+ }
+ pr_notice("arcmsr%d: polling an illegal "
+ "ccb command done ccb = '0x%p' "
+ "ccboutstandingcount = %d\n"
+ , acb->host->host_no
+ , pCCB
+ , atomic_read(&acb->ccboutstandingcount));
+ continue;
+ }
+ error = (acb->pCompletionQ[doneq_index].cmdFlag &
+ ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false;
+ arcmsr_report_ccb_state(acb, pCCB, error);
+ }
+ writel(doneq_index, &reg->reply_post_consumer_index);
+ return rtn;
+}
+
static int arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
struct CommandControlBlock *poll_ccb)
{
int rtn = 0;
switch (acb->adapter_type) {
- case ACB_ADAPTER_TYPE_A: {
- rtn = arcmsr_polling_hba_ccbdone(acb, poll_ccb);
- }
+ case ACB_ADAPTER_TYPE_A:
+ rtn = arcmsr_hbaA_polling_ccbdone(acb, poll_ccb);
+ break;
+ case ACB_ADAPTER_TYPE_B:
+ rtn = arcmsr_hbaB_polling_ccbdone(acb, poll_ccb);
+ break;
+ case ACB_ADAPTER_TYPE_C:
+ rtn = arcmsr_hbaC_polling_ccbdone(acb, poll_ccb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ rtn = arcmsr_hbaD_polling_ccbdone(acb, poll_ccb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ rtn = arcmsr_hbaE_polling_ccbdone(acb, poll_ccb);
break;
+ }
+ return rtn;
+}
- case ACB_ADAPTER_TYPE_B: {
- rtn = arcmsr_polling_hbb_ccbdone(acb, poll_ccb);
+static void arcmsr_set_iop_datetime(struct timer_list *t)
+{
+ struct AdapterControlBlock *pacb = timer_container_of(pacb, t,
+ refresh_timer);
+ unsigned int next_time;
+ struct tm tm;
+
+ union {
+ struct {
+ uint16_t signature;
+ uint8_t year;
+ uint8_t month;
+ uint8_t date;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ } a;
+ struct {
+ uint32_t msg_time[2];
+ } b;
+ } datetime;
+
+ time64_to_tm(ktime_get_real_seconds(), -sys_tz.tz_minuteswest * 60, &tm);
+
+ datetime.a.signature = 0x55AA;
+ datetime.a.year = tm.tm_year - 100; /* base 2000 instead of 1900 */
+ datetime.a.month = tm.tm_mon;
+ datetime.a.date = tm.tm_mday;
+ datetime.a.hour = tm.tm_hour;
+ datetime.a.minute = tm.tm_min;
+ datetime.a.second = tm.tm_sec;
+
+ switch (pacb->adapter_type) {
+ case ACB_ADAPTER_TYPE_A: {
+ struct MessageUnit_A __iomem *reg = pacb->pmuA;
+ writel(datetime.b.msg_time[0], &reg->message_rwbuffer[0]);
+ writel(datetime.b.msg_time[1], &reg->message_rwbuffer[1]);
+ writel(ARCMSR_INBOUND_MESG0_SYNC_TIMER, &reg->inbound_msgaddr0);
+ break;
}
- break;
- case ACB_ADAPTER_TYPE_C: {
- rtn = arcmsr_polling_hbc_ccbdone(acb, poll_ccb);
+ case ACB_ADAPTER_TYPE_B: {
+ uint32_t __iomem *rwbuffer;
+ struct MessageUnit_B *reg = pacb->pmuB;
+ rwbuffer = reg->message_rwbuffer;
+ writel(datetime.b.msg_time[0], rwbuffer++);
+ writel(datetime.b.msg_time[1], rwbuffer++);
+ writel(ARCMSR_MESSAGE_SYNC_TIMER, reg->drv2iop_doorbell);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_C: {
+ struct MessageUnit_C __iomem *reg = pacb->pmuC;
+ writel(datetime.b.msg_time[0], &reg->msgcode_rwbuffer[0]);
+ writel(datetime.b.msg_time[1], &reg->msgcode_rwbuffer[1]);
+ writel(ARCMSR_INBOUND_MESG0_SYNC_TIMER, &reg->inbound_msgaddr0);
+ writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_D: {
+ uint32_t __iomem *rwbuffer;
+ struct MessageUnit_D *reg = pacb->pmuD;
+ rwbuffer = reg->msgcode_rwbuffer;
+ writel(datetime.b.msg_time[0], rwbuffer++);
+ writel(datetime.b.msg_time[1], rwbuffer++);
+ writel(ARCMSR_INBOUND_MESG0_SYNC_TIMER, reg->inbound_msgaddr0);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_E: {
+ struct MessageUnit_E __iomem *reg = pacb->pmuE;
+ writel(datetime.b.msg_time[0], &reg->msgcode_rwbuffer[0]);
+ writel(datetime.b.msg_time[1], &reg->msgcode_rwbuffer[1]);
+ writel(ARCMSR_INBOUND_MESG0_SYNC_TIMER, &reg->inbound_msgaddr0);
+ pacb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pacb->out_doorbell, &reg->iobound_doorbell);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_F __iomem *reg = pacb->pmuF;
+
+ pacb->msgcode_rwbuffer[0] = datetime.b.msg_time[0];
+ pacb->msgcode_rwbuffer[1] = datetime.b.msg_time[1];
+ writel(ARCMSR_INBOUND_MESG0_SYNC_TIMER, &reg->inbound_msgaddr0);
+ pacb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pacb->out_doorbell, &reg->iobound_doorbell);
+ break;
}
}
- return rtn;
+ if (sys_tz.tz_minuteswest)
+ next_time = ARCMSR_HOURS;
+ else
+ next_time = ARCMSR_MINUTES;
+ mod_timer(&pacb->refresh_timer, jiffies + msecs_to_jiffies(next_time));
}
static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
{
uint32_t cdb_phyaddr, cdb_phyaddr_hi32;
dma_addr_t dma_coherent_handle;
+
/*
********************************************************************
** here we need to tell iop 331 our freeccb.HighPart
** if freeccb.HighPart is not zero
********************************************************************
*/
- dma_coherent_handle = acb->dma_coherent_handle;
- cdb_phyaddr = (uint32_t)(dma_coherent_handle);
- cdb_phyaddr_hi32 = (uint32_t)((cdb_phyaddr >> 16) >> 16);
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_B:
+ case ACB_ADAPTER_TYPE_D:
+ dma_coherent_handle = acb->dma_coherent_handle2;
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ dma_coherent_handle = acb->dma_coherent_handle +
+ offsetof(struct CommandControlBlock, arcmsr_cdb);
+ break;
+ default:
+ dma_coherent_handle = acb->dma_coherent_handle;
+ break;
+ }
+ cdb_phyaddr = lower_32_bits(dma_coherent_handle);
+ cdb_phyaddr_hi32 = upper_32_bits(dma_coherent_handle);
acb->cdb_phyaddr_hi32 = cdb_phyaddr_hi32;
+ acb->cdb_phyadd_hipart = ((uint64_t)cdb_phyaddr_hi32) << 32;
/*
***********************************************************************
** if adapter type B, set window of "post command Q"
@@ -2522,65 +4065,61 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_A: {
if (cdb_phyaddr_hi32 != 0) {
struct MessageUnit_A __iomem *reg = acb->pmuA;
- uint32_t intmask_org;
- intmask_org = arcmsr_disable_outbound_ints(acb);
writel(ARCMSR_SIGNATURE_SET_CONFIG, \
&reg->message_rwbuffer[0]);
writel(cdb_phyaddr_hi32, &reg->message_rwbuffer[1]);
writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, \
&reg->inbound_msgaddr0);
- if (!arcmsr_hba_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaA_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "arcmsr%d: ""set ccb high \
part physical address timeout\n",
acb->host->host_no);
return 1;
}
- arcmsr_enable_outbound_ints(acb, intmask_org);
}
}
break;
case ACB_ADAPTER_TYPE_B: {
- unsigned long post_queue_phyaddr;
uint32_t __iomem *rwbuffer;
struct MessageUnit_B *reg = acb->pmuB;
- uint32_t intmask_org;
- intmask_org = arcmsr_disable_outbound_ints(acb);
reg->postq_index = 0;
reg->doneq_index = 0;
writel(ARCMSR_MESSAGE_SET_POST_WINDOW, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(acb)) {
- printk(KERN_NOTICE "arcmsr%d:can not set diver mode\n", \
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
+ printk(KERN_NOTICE "arcmsr%d: cannot set driver mode\n", \
acb->host->host_no);
return 1;
}
- post_queue_phyaddr = acb->dma_coherent_handle_hbb_mu;
rwbuffer = reg->message_rwbuffer;
/* driver "set config" signature */
writel(ARCMSR_SIGNATURE_SET_CONFIG, rwbuffer++);
/* normal should be zero */
writel(cdb_phyaddr_hi32, rwbuffer++);
/* postQ size (256 + 8)*4 */
- writel(post_queue_phyaddr, rwbuffer++);
+ writel(cdb_phyaddr, rwbuffer++);
/* doneQ size (256 + 8)*4 */
- writel(post_queue_phyaddr + 1056, rwbuffer++);
+ writel(cdb_phyaddr + 1056, rwbuffer++);
/* ccb maxQ size must be --> [(256 + 8)*4]*/
writel(1056, rwbuffer);
writel(ARCMSR_MESSAGE_SET_CONFIG, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "arcmsr%d: 'set command Q window' \
timeout \n",acb->host->host_no);
return 1;
}
- arcmsr_hbb_enable_driver_mode(acb);
- arcmsr_enable_outbound_ints(acb, intmask_org);
+ writel(ARCMSR_MESSAGE_START_DRIVER_MODE, reg->drv2iop_doorbell);
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
+ pr_err("arcmsr%d: can't set driver mode.\n",
+ acb->host->host_no);
+ return 1;
+ }
}
break;
case ACB_ADAPTER_TYPE_C: {
- if (cdb_phyaddr_hi32 != 0) {
- struct MessageUnit_C *reg = (struct MessageUnit_C *)acb->pmuC;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
printk(KERN_NOTICE "arcmsr%d: cdb_phyaddr_hi32=0x%x\n",
acb->adapter_index, cdb_phyaddr_hi32);
@@ -2588,13 +4127,80 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
writel(cdb_phyaddr_hi32, &reg->msgcode_rwbuffer[1]);
writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, &reg->inbound_msgaddr0);
writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
- if (!arcmsr_hbc_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaC_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "arcmsr%d: 'set command Q window' \
timeout \n", acb->host->host_no);
return 1;
}
}
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ uint32_t __iomem *rwbuffer;
+ struct MessageUnit_D *reg = acb->pmuD;
+ reg->postq_index = 0;
+ reg->doneq_index = 0;
+ rwbuffer = reg->msgcode_rwbuffer;
+ writel(ARCMSR_SIGNATURE_SET_CONFIG, rwbuffer++);
+ writel(cdb_phyaddr_hi32, rwbuffer++);
+ writel(cdb_phyaddr, rwbuffer++);
+ writel(cdb_phyaddr + (ARCMSR_MAX_ARC1214_POSTQUEUE *
+ sizeof(struct InBound_SRB)), rwbuffer++);
+ writel(0x100, rwbuffer);
+ writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, reg->inbound_msgaddr0);
+ if (!arcmsr_hbaD_wait_msgint_ready(acb)) {
+ pr_notice("arcmsr%d: 'set command Q window' timeout\n",
+ acb->host->host_no);
+ return 1;
}
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ writel(ARCMSR_SIGNATURE_SET_CONFIG, &reg->msgcode_rwbuffer[0]);
+ writel(ARCMSR_SIGNATURE_1884, &reg->msgcode_rwbuffer[1]);
+ writel(cdb_phyaddr, &reg->msgcode_rwbuffer[2]);
+ writel(cdb_phyaddr_hi32, &reg->msgcode_rwbuffer[3]);
+ writel(acb->ccbsize, &reg->msgcode_rwbuffer[4]);
+ writel(lower_32_bits(acb->dma_coherent_handle2), &reg->msgcode_rwbuffer[5]);
+ writel(upper_32_bits(acb->dma_coherent_handle2), &reg->msgcode_rwbuffer[6]);
+ writel(acb->ioqueue_size, &reg->msgcode_rwbuffer[7]);
+ writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, &reg->inbound_msgaddr0);
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ if (!arcmsr_hbaE_wait_msgint_ready(acb)) {
+ pr_notice("arcmsr%d: 'set command Q window' timeout \n",
+ acb->host->host_no);
+ return 1;
+ }
+ }
+ break;
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_F __iomem *reg = acb->pmuF;
+
+ acb->msgcode_rwbuffer[0] = ARCMSR_SIGNATURE_SET_CONFIG;
+ acb->msgcode_rwbuffer[1] = ARCMSR_SIGNATURE_1886;
+ acb->msgcode_rwbuffer[2] = cdb_phyaddr;
+ acb->msgcode_rwbuffer[3] = cdb_phyaddr_hi32;
+ acb->msgcode_rwbuffer[4] = acb->ccbsize;
+ acb->msgcode_rwbuffer[5] = lower_32_bits(acb->dma_coherent_handle2);
+ acb->msgcode_rwbuffer[6] = upper_32_bits(acb->dma_coherent_handle2);
+ acb->msgcode_rwbuffer[7] = acb->completeQ_size;
+ if (acb->xor_mega) {
+ acb->msgcode_rwbuffer[8] = 0x455AA; //Linux init 2
+ acb->msgcode_rwbuffer[9] = 0;
+ acb->msgcode_rwbuffer[10] = lower_32_bits(acb->xorPhys);
+ acb->msgcode_rwbuffer[11] = upper_32_bits(acb->xorPhys);
+ }
+ writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, &reg->inbound_msgaddr0);
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ if (!arcmsr_hbaE_wait_msgint_ready(acb)) {
+ pr_notice("arcmsr%d: 'set command Q window' timeout\n",
+ acb->host->host_no);
+ return 1;
+ }
+ }
+ break;
}
return 0;
}
@@ -2607,6 +4213,8 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_A: {
struct MessageUnit_A __iomem *reg = acb->pmuA;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(&reg->outbound_msgaddr1);
} while ((firmware_state & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0);
}
@@ -2615,150 +4223,183 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_B: {
struct MessageUnit_B *reg = acb->pmuB;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(reg->iop2drv_doorbell);
} while ((firmware_state & ARCMSR_MESSAGE_FIRMWARE_OK) == 0);
writel(ARCMSR_DRV2IOP_END_OF_INTERRUPT, reg->drv2iop_doorbell);
}
break;
case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *reg = (struct MessageUnit_C *)acb->pmuC;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(&reg->outbound_msgaddr1);
} while ((firmware_state & ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK) == 0);
}
- }
-}
-
-static void arcmsr_request_hba_device_map(struct AdapterControlBlock *acb)
-{
- struct MessageUnit_A __iomem *reg = acb->pmuA;
- if (unlikely(atomic_read(&acb->rq_map_token) == 0) || ((acb->acb_flags & ACB_F_BUS_RESET) != 0 ) || ((acb->acb_flags & ACB_F_ABORT) != 0 )){
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- return;
- } else {
- acb->fw_flag = FW_NORMAL;
- if (atomic_read(&acb->ante_token_value) == atomic_read(&acb->rq_map_token)){
- atomic_set(&acb->rq_map_token, 16);
- }
- atomic_set(&acb->ante_token_value, atomic_read(&acb->rq_map_token));
- if (atomic_dec_and_test(&acb->rq_map_token)) {
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- return;
- }
- writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- }
- return;
-}
-
-static void arcmsr_request_hbb_device_map(struct AdapterControlBlock *acb)
-{
- struct MessageUnit_B __iomem *reg = acb->pmuB;
- if (unlikely(atomic_read(&acb->rq_map_token) == 0) || ((acb->acb_flags & ACB_F_BUS_RESET) != 0 ) || ((acb->acb_flags & ACB_F_ABORT) != 0 )){
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- return;
- } else {
- acb->fw_flag = FW_NORMAL;
- if (atomic_read(&acb->ante_token_value) == atomic_read(&acb->rq_map_token)) {
- atomic_set(&acb->rq_map_token, 16);
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
+ firmware_state = readl(reg->outbound_msgaddr1);
+ } while ((firmware_state &
+ ARCMSR_ARC1214_MESSAGE_FIRMWARE_OK) == 0);
}
- atomic_set(&acb->ante_token_value, atomic_read(&acb->rq_map_token));
- if (atomic_dec_and_test(&acb->rq_map_token)) {
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- return;
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
+ firmware_state = readl(&reg->outbound_msgaddr1);
+ } while ((firmware_state & ARCMSR_HBEMU_MESSAGE_FIRMWARE_OK) == 0);
}
- writel(ARCMSR_MESSAGE_GET_CONFIG, reg->drv2iop_doorbell);
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
+ break;
}
- return;
}
-static void arcmsr_request_hbc_device_map(struct AdapterControlBlock *acb)
+static void arcmsr_request_device_map(struct timer_list *t)
{
- struct MessageUnit_C __iomem *reg = acb->pmuC;
- if (unlikely(atomic_read(&acb->rq_map_token) == 0) || ((acb->acb_flags & ACB_F_BUS_RESET) != 0) || ((acb->acb_flags & ACB_F_ABORT) != 0)) {
+ struct AdapterControlBlock *acb = timer_container_of(acb, t,
+ eternal_timer);
+ if (acb->acb_flags & (ACB_F_MSG_GET_CONFIG | ACB_F_BUS_RESET | ACB_F_ABORT)) {
mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- return;
} else {
acb->fw_flag = FW_NORMAL;
- if (atomic_read(&acb->ante_token_value) == atomic_read(&acb->rq_map_token)) {
- atomic_set(&acb->rq_map_token, 16);
- }
- atomic_set(&acb->ante_token_value, atomic_read(&acb->rq_map_token));
- if (atomic_dec_and_test(&acb->rq_map_token)) {
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- return;
- }
- writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
- writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- }
- return;
-}
-
-static void arcmsr_request_device_map(unsigned long pacb)
-{
- struct AdapterControlBlock *acb = (struct AdapterControlBlock *)pacb;
- switch (acb->adapter_type) {
+ switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_A: {
- arcmsr_request_hba_device_map(acb);
- }
- break;
+ struct MessageUnit_A __iomem *reg = acb->pmuA;
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
+ break;
+ }
case ACB_ADAPTER_TYPE_B: {
- arcmsr_request_hbb_device_map(acb);
- }
- break;
+ struct MessageUnit_B *reg = acb->pmuB;
+ writel(ARCMSR_MESSAGE_GET_CONFIG, reg->drv2iop_doorbell);
+ break;
+ }
case ACB_ADAPTER_TYPE_C: {
- arcmsr_request_hbc_device_map(acb);
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
+ writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &reg->inbound_doorbell);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, reg->inbound_msgaddr0);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_E: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ break;
+ }
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_F __iomem *reg = acb->pmuF;
+ uint32_t outMsg1 = readl(&reg->outbound_msgaddr1);
+
+ if (!(outMsg1 & ARCMSR_HBFMU_MESSAGE_FIRMWARE_OK) ||
+ (outMsg1 & ARCMSR_HBFMU_MESSAGE_NO_VOLUME_CHANGE))
+ goto nxt6s;
+ writel(ARCMSR_INBOUND_MESG0_GET_CONFIG, &reg->inbound_msgaddr0);
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ break;
+ }
+ default:
+ return;
}
+ acb->acb_flags |= ACB_F_MSG_GET_CONFIG;
+nxt6s:
+ mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
}
}
-static void arcmsr_start_hba_bgrb(struct AdapterControlBlock *acb)
+static void arcmsr_hbaA_start_bgrb(struct AdapterControlBlock *acb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
acb->acb_flags |= ACB_F_MSG_START_BGRB;
writel(ARCMSR_INBOUND_MESG0_START_BGRB, &reg->inbound_msgaddr0);
- if (!arcmsr_hba_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaA_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "arcmsr%d: wait 'start adapter background \
- rebulid' timeout \n", acb->host->host_no);
+ rebuild' timeout \n", acb->host->host_no);
}
}
-static void arcmsr_start_hbb_bgrb(struct AdapterControlBlock *acb)
+static void arcmsr_hbaB_start_bgrb(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
acb->acb_flags |= ACB_F_MSG_START_BGRB;
writel(ARCMSR_MESSAGE_START_BGRB, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "arcmsr%d: wait 'start adapter background \
- rebulid' timeout \n",acb->host->host_no);
+ rebuild' timeout \n",acb->host->host_no);
}
}
-static void arcmsr_start_hbc_bgrb(struct AdapterControlBlock *pACB)
+static void arcmsr_hbaC_start_bgrb(struct AdapterControlBlock *pACB)
{
- struct MessageUnit_C *phbcmu = (struct MessageUnit_C *)pACB->pmuC;
+ struct MessageUnit_C __iomem *phbcmu = pACB->pmuC;
pACB->acb_flags |= ACB_F_MSG_START_BGRB;
writel(ARCMSR_INBOUND_MESG0_START_BGRB, &phbcmu->inbound_msgaddr0);
writel(ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE, &phbcmu->inbound_doorbell);
- if (!arcmsr_hbc_wait_msgint_ready(pACB)) {
+ if (!arcmsr_hbaC_wait_msgint_ready(pACB)) {
printk(KERN_NOTICE "arcmsr%d: wait 'start adapter background \
- rebulid' timeout \n", pACB->host->host_no);
+ rebuild' timeout \n", pACB->host->host_no);
}
return;
}
+
+static void arcmsr_hbaD_start_bgrb(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_D *pmu = pACB->pmuD;
+
+ pACB->acb_flags |= ACB_F_MSG_START_BGRB;
+ writel(ARCMSR_INBOUND_MESG0_START_BGRB, pmu->inbound_msgaddr0);
+ if (!arcmsr_hbaD_wait_msgint_ready(pACB)) {
+ pr_notice("arcmsr%d: wait 'start adapter "
+ "background rebuild' timeout\n", pACB->host->host_no);
+ }
+}
+
+static void arcmsr_hbaE_start_bgrb(struct AdapterControlBlock *pACB)
+{
+ struct MessageUnit_E __iomem *pmu = pACB->pmuE;
+
+ pACB->acb_flags |= ACB_F_MSG_START_BGRB;
+ writel(ARCMSR_INBOUND_MESG0_START_BGRB, &pmu->inbound_msgaddr0);
+ pACB->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE;
+ writel(pACB->out_doorbell, &pmu->iobound_doorbell);
+ if (!arcmsr_hbaE_wait_msgint_ready(pACB)) {
+ pr_notice("arcmsr%d: wait 'start adapter "
+ "background rebuild' timeout \n", pACB->host->host_no);
+ }
+}
+
static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb)
{
switch (acb->adapter_type) {
case ACB_ADAPTER_TYPE_A:
- arcmsr_start_hba_bgrb(acb);
+ arcmsr_hbaA_start_bgrb(acb);
break;
case ACB_ADAPTER_TYPE_B:
- arcmsr_start_hbb_bgrb(acb);
+ arcmsr_hbaB_start_bgrb(acb);
break;
case ACB_ADAPTER_TYPE_C:
- arcmsr_start_hbc_bgrb(acb);
+ arcmsr_hbaC_start_bgrb(acb);
+ break;
+ case ACB_ADAPTER_TYPE_D:
+ arcmsr_hbaD_start_bgrb(acb);
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:
+ arcmsr_hbaE_start_bgrb(acb);
+ break;
}
}
@@ -2778,20 +4419,86 @@ static void arcmsr_clear_doorbell_queue_buffer(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_B: {
struct MessageUnit_B *reg = acb->pmuB;
- /*clear interrupt and message state*/
- writel(ARCMSR_MESSAGE_INT_CLEAR_PATTERN, reg->iop2drv_doorbell);
+ uint32_t outbound_doorbell, i;
+ writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell);
writel(ARCMSR_DRV2IOP_DATA_READ_OK, reg->drv2iop_doorbell);
/* let IOP know data has been read */
+ for(i=0; i < 200; i++) {
+ msleep(20);
+ outbound_doorbell = readl(reg->iop2drv_doorbell);
+ if( outbound_doorbell & ARCMSR_IOP2DRV_DATA_WRITE_OK) {
+ writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell);
+ writel(ARCMSR_DRV2IOP_DATA_READ_OK, reg->drv2iop_doorbell);
+ } else
+ break;
+ }
}
break;
case ACB_ADAPTER_TYPE_C: {
- struct MessageUnit_C *reg = (struct MessageUnit_C *)acb->pmuC;
- uint32_t outbound_doorbell;
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
+ uint32_t outbound_doorbell, i;
/* empty doorbell Qbuffer if door bell ringed */
outbound_doorbell = readl(&reg->outbound_doorbell);
writel(outbound_doorbell, &reg->outbound_doorbell_clear);
writel(ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK, &reg->inbound_doorbell);
+ for (i = 0; i < 200; i++) {
+ msleep(20);
+ outbound_doorbell = readl(&reg->outbound_doorbell);
+ if (outbound_doorbell &
+ ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_OK) {
+ writel(outbound_doorbell,
+ &reg->outbound_doorbell_clear);
+ writel(ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK,
+ &reg->inbound_doorbell);
+ } else
+ break;
+ }
+ }
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg = acb->pmuD;
+ uint32_t outbound_doorbell, i;
+ /* empty doorbell Qbuffer if door bell ringed */
+ outbound_doorbell = readl(reg->outbound_doorbell);
+ writel(outbound_doorbell, reg->outbound_doorbell);
+ writel(ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ,
+ reg->inbound_doorbell);
+ for (i = 0; i < 200; i++) {
+ msleep(20);
+ outbound_doorbell = readl(reg->outbound_doorbell);
+ if (outbound_doorbell &
+ ARCMSR_ARC1214_IOP2DRV_DATA_WRITE_OK) {
+ writel(outbound_doorbell,
+ reg->outbound_doorbell);
+ writel(ARCMSR_ARC1214_DRV2IOP_DATA_OUT_READ,
+ reg->inbound_doorbell);
+ } else
+ break;
}
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F: {
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ uint32_t i, tmp;
+
+ acb->in_doorbell = readl(&reg->iobound_doorbell);
+ writel(0, &reg->host_int_status); /*clear interrupt*/
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_DATA_READ_OK;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ for(i=0; i < 200; i++) {
+ msleep(20);
+ tmp = acb->in_doorbell;
+ acb->in_doorbell = readl(&reg->iobound_doorbell);
+ if((tmp ^ acb->in_doorbell) & ARCMSR_HBEMU_IOP2DRV_DATA_WRITE_OK) {
+ writel(0, &reg->host_int_status); /*clear interrupt*/
+ acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_DATA_READ_OK;
+ writel(acb->out_doorbell, &reg->iobound_doorbell);
+ } else
+ break;
+ }
+ }
+ break;
}
}
@@ -2804,7 +4511,7 @@ static void arcmsr_enable_eoi_mode(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
writel(ARCMSR_MESSAGE_ACTIVE_EOI_MODE, reg->drv2iop_doorbell);
- if (!arcmsr_hbb_wait_msgint_ready(acb)) {
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "ARCMSR IOP enables EOI_MODE TIMEOUT");
return;
}
@@ -2822,16 +4529,17 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb)
int i, count = 0;
struct MessageUnit_A __iomem *pmuA = acb->pmuA;
struct MessageUnit_C __iomem *pmuC = acb->pmuC;
- u32 temp = 0;
+ struct MessageUnit_D *pmuD = acb->pmuD;
+
/* backup pci config data */
printk(KERN_NOTICE "arcmsr%d: executing hw bus reset .....\n", acb->host->host_no);
for (i = 0; i < 64; i++) {
pci_read_config_byte(acb->pdev, i, &value[i]);
}
/* hardware reset signal */
- if ((acb->dev_id == 0x1680)) {
+ if (acb->dev_id == 0x1680) {
writel(ARCMSR_ARC1680_BUS_RESET, &pmuA->reserved1[0]);
- } else if ((acb->dev_id == 0x1880)) {
+ } else if (acb->dev_id == 0x1880) {
do {
count++;
writel(0xF, &pmuC->write_sequence);
@@ -2840,8 +4548,23 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb)
writel(0x2, &pmuC->write_sequence);
writel(0x7, &pmuC->write_sequence);
writel(0xD, &pmuC->write_sequence);
- } while ((((temp = readl(&pmuC->host_diagnostic)) | ARCMSR_ARC1880_DiagWrite_ENABLE) == 0) && (count < 5));
+ } while (((readl(&pmuC->host_diagnostic) & ARCMSR_ARC1880_DiagWrite_ENABLE) == 0) && (count < 5));
writel(ARCMSR_ARC1880_RESET_ADAPTER, &pmuC->host_diagnostic);
+ } else if (acb->dev_id == 0x1884) {
+ struct MessageUnit_E __iomem *pmuE = acb->pmuE;
+ do {
+ count++;
+ writel(0x4, &pmuE->write_sequence_3xxx);
+ writel(0xB, &pmuE->write_sequence_3xxx);
+ writel(0x2, &pmuE->write_sequence_3xxx);
+ writel(0x7, &pmuE->write_sequence_3xxx);
+ writel(0xD, &pmuE->write_sequence_3xxx);
+ mdelay(10);
+ } while (((readl(&pmuE->host_diagnostic_3xxx) &
+ ARCMSR_ARC1884_DiagWrite_ENABLE) == 0) && (count < 5));
+ writel(ARCMSR_ARC188X_RESET_ADAPTER, &pmuE->host_diagnostic_3xxx);
+ } else if (acb->dev_id == 0x1214) {
+ writel(0x20, pmuD->reset_request);
} else {
pci_write_config_byte(acb->pdev, 0x84, 0x20);
}
@@ -2853,6 +4576,46 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb)
msleep(1000);
return;
}
+
+static bool arcmsr_reset_in_progress(struct AdapterControlBlock *acb)
+{
+ bool rtn = true;
+
+ switch(acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_A:{
+ struct MessageUnit_A __iomem *reg = acb->pmuA;
+ rtn = ((readl(&reg->outbound_msgaddr1) &
+ ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0) ? true : false;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_B:{
+ struct MessageUnit_B *reg = acb->pmuB;
+ rtn = ((readl(reg->iop2drv_doorbell) &
+ ARCMSR_MESSAGE_FIRMWARE_OK) == 0) ? true : false;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_C:{
+ struct MessageUnit_C __iomem *reg = acb->pmuC;
+ rtn = (readl(&reg->host_diagnostic) & 0x04) ? true : false;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_D:{
+ struct MessageUnit_D *reg = acb->pmuD;
+ rtn = ((readl(reg->sample_at_reset) & 0x80) == 0) ?
+ true : false;
+ }
+ break;
+ case ACB_ADAPTER_TYPE_E:
+ case ACB_ADAPTER_TYPE_F:{
+ struct MessageUnit_E __iomem *reg = acb->pmuE;
+ rtn = (readl(&reg->host_diagnostic_3xxx) &
+ ARCMSR_ARC188X_RESET_ADAPTER) ? true : false;
+ }
+ break;
+ }
+ return rtn;
+}
+
static void arcmsr_iop_init(struct AdapterControlBlock *acb)
{
uint32_t intmask_org;
@@ -2885,7 +4648,7 @@ static uint8_t arcmsr_iop_reset(struct AdapterControlBlock *acb)
rtnval = arcmsr_abort_allcmd(acb);
/* clear all outbound posted Q */
arcmsr_done4abort_postqueue(acb);
- for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
+ for (i = 0; i < acb->maxFreeCCB; i++) {
ccb = acb->pccb_pool[i];
if (ccb->startdone == ARCMSR_CCB_START) {
scsi_dma_unmap(ccb->pcmd);
@@ -2907,139 +4670,53 @@ static uint8_t arcmsr_iop_reset(struct AdapterControlBlock *acb)
static int arcmsr_bus_reset(struct scsi_cmnd *cmd)
{
struct AdapterControlBlock *acb;
- uint32_t intmask_org, outbound_doorbell;
int retry_count = 0;
int rtn = FAILED;
acb = (struct AdapterControlBlock *) cmd->device->host->hostdata;
- printk(KERN_ERR "arcmsr: executing bus reset eh.....num_resets = %d, num_aborts = %d \n", acb->num_resets, acb->num_aborts);
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+ return SUCCESS;
+ pr_notice("arcmsr: executing bus reset eh.....num_resets = %d,"
+ " num_aborts = %d \n", acb->num_resets, acb->num_aborts);
acb->num_resets++;
- switch(acb->adapter_type){
- case ACB_ADAPTER_TYPE_A:{
- if (acb->acb_flags & ACB_F_BUS_RESET){
- long timeout;
- printk(KERN_ERR "arcmsr: there is an bus reset eh proceeding.......\n");
- timeout = wait_event_timeout(wait_q, (acb->acb_flags & ACB_F_BUS_RESET) == 0, 220*HZ);
- if (timeout) {
- return SUCCESS;
- }
- }
- acb->acb_flags |= ACB_F_BUS_RESET;
- if (!arcmsr_iop_reset(acb)) {
- struct MessageUnit_A __iomem *reg;
- reg = acb->pmuA;
- arcmsr_hardware_reset(acb);
- acb->acb_flags &= ~ACB_F_IOP_INITED;
-sleep_again:
- ssleep(ARCMSR_SLEEPTIME);
- if ((readl(&reg->outbound_msgaddr1) & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0) {
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, retry=%d\n", acb->host->host_no, retry_count);
- if (retry_count > ARCMSR_RETRYCOUNT) {
- acb->fw_flag = FW_DEADLOCK;
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, RETRY TERMINATED!!\n", acb->host->host_no);
- return FAILED;
- }
- retry_count++;
- goto sleep_again;
- }
- acb->acb_flags |= ACB_F_IOP_INITED;
- /* disable all outbound interrupt */
- intmask_org = arcmsr_disable_outbound_ints(acb);
- arcmsr_get_firmware_spec(acb);
- arcmsr_start_adapter_bgrb(acb);
- /* clear Qbuffer if door bell ringed */
- outbound_doorbell = readl(&reg->outbound_doorbell);
- writel(outbound_doorbell, &reg->outbound_doorbell); /*clear interrupt */
- writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK, &reg->inbound_doorbell);
- /* enable outbound Post Queue,outbound doorbell Interrupt */
- arcmsr_enable_outbound_ints(acb, intmask_org);
- atomic_set(&acb->rq_map_token, 16);
- atomic_set(&acb->ante_token_value, 16);
- acb->fw_flag = FW_NORMAL;
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- acb->acb_flags &= ~ACB_F_BUS_RESET;
- rtn = SUCCESS;
- printk(KERN_ERR "arcmsr: scsi bus reset eh returns with success\n");
- } else {
- acb->acb_flags &= ~ACB_F_BUS_RESET;
- atomic_set(&acb->rq_map_token, 16);
- atomic_set(&acb->ante_token_value, 16);
- acb->fw_flag = FW_NORMAL;
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6*HZ));
- rtn = SUCCESS;
- }
- break;
- }
- case ACB_ADAPTER_TYPE_B:{
- acb->acb_flags |= ACB_F_BUS_RESET;
- if (!arcmsr_iop_reset(acb)) {
- acb->acb_flags &= ~ACB_F_BUS_RESET;
- rtn = FAILED;
- } else {
- acb->acb_flags &= ~ACB_F_BUS_RESET;
- atomic_set(&acb->rq_map_token, 16);
- atomic_set(&acb->ante_token_value, 16);
- acb->fw_flag = FW_NORMAL;
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- rtn = SUCCESS;
- }
- break;
- }
- case ACB_ADAPTER_TYPE_C:{
- if (acb->acb_flags & ACB_F_BUS_RESET) {
- long timeout;
- printk(KERN_ERR "arcmsr: there is an bus reset eh proceeding.......\n");
- timeout = wait_event_timeout(wait_q, (acb->acb_flags & ACB_F_BUS_RESET) == 0, 220*HZ);
- if (timeout) {
- return SUCCESS;
- }
- }
- acb->acb_flags |= ACB_F_BUS_RESET;
- if (!arcmsr_iop_reset(acb)) {
- struct MessageUnit_C __iomem *reg;
- reg = acb->pmuC;
- arcmsr_hardware_reset(acb);
- acb->acb_flags &= ~ACB_F_IOP_INITED;
-sleep:
- ssleep(ARCMSR_SLEEPTIME);
- if ((readl(&reg->host_diagnostic) & 0x04) != 0) {
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, retry=%d\n", acb->host->host_no, retry_count);
- if (retry_count > ARCMSR_RETRYCOUNT) {
- acb->fw_flag = FW_DEADLOCK;
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, RETRY TERMINATED!!\n", acb->host->host_no);
- return FAILED;
- }
- retry_count++;
- goto sleep;
- }
- acb->acb_flags |= ACB_F_IOP_INITED;
- /* disable all outbound interrupt */
- intmask_org = arcmsr_disable_outbound_ints(acb);
- arcmsr_get_firmware_spec(acb);
- arcmsr_start_adapter_bgrb(acb);
- /* clear Qbuffer if door bell ringed */
- outbound_doorbell = readl(&reg->outbound_doorbell);
- writel(outbound_doorbell, &reg->outbound_doorbell_clear); /*clear interrupt */
- writel(ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK, &reg->inbound_doorbell);
- /* enable outbound Post Queue,outbound doorbell Interrupt */
- arcmsr_enable_outbound_ints(acb, intmask_org);
- atomic_set(&acb->rq_map_token, 16);
- atomic_set(&acb->ante_token_value, 16);
- acb->fw_flag = FW_NORMAL;
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6 * HZ));
- acb->acb_flags &= ~ACB_F_BUS_RESET;
- rtn = SUCCESS;
- printk(KERN_ERR "arcmsr: scsi bus reset eh returns with success\n");
- } else {
- acb->acb_flags &= ~ACB_F_BUS_RESET;
- atomic_set(&acb->rq_map_token, 16);
- atomic_set(&acb->ante_token_value, 16);
- acb->fw_flag = FW_NORMAL;
- mod_timer(&acb->eternal_timer, jiffies + msecs_to_jiffies(6*HZ));
- rtn = SUCCESS;
+ if (acb->acb_flags & ACB_F_BUS_RESET) {
+ long timeout;
+ pr_notice("arcmsr: there is a bus reset eh proceeding...\n");
+ timeout = wait_event_timeout(wait_q, (acb->acb_flags
+ & ACB_F_BUS_RESET) == 0, 220 * HZ);
+ if (timeout)
+ return SUCCESS;
+ }
+ acb->acb_flags |= ACB_F_BUS_RESET;
+ if (!arcmsr_iop_reset(acb)) {
+ arcmsr_hardware_reset(acb);
+ acb->acb_flags &= ~ACB_F_IOP_INITED;
+wait_reset_done:
+ ssleep(ARCMSR_SLEEPTIME);
+ if (arcmsr_reset_in_progress(acb)) {
+ if (retry_count > ARCMSR_RETRYCOUNT) {
+ acb->fw_flag = FW_DEADLOCK;
+ pr_notice("arcmsr%d: waiting for hw bus reset"
+ " return, RETRY TERMINATED!!\n",
+ acb->host->host_no);
+ return FAILED;
}
- break;
+ retry_count++;
+ goto wait_reset_done;
}
+ arcmsr_iop_init(acb);
+ acb->fw_flag = FW_NORMAL;
+ mod_timer(&acb->eternal_timer, jiffies +
+ msecs_to_jiffies(6 * HZ));
+ acb->acb_flags &= ~ACB_F_BUS_RESET;
+ rtn = SUCCESS;
+ pr_notice("arcmsr: scsi bus reset eh returns with success\n");
+ } else {
+ acb->acb_flags &= ~ACB_F_BUS_RESET;
+ acb->fw_flag = FW_NORMAL;
+ mod_timer(&acb->eternal_timer, jiffies +
+ msecs_to_jiffies(6 * HZ));
+ rtn = SUCCESS;
}
return rtn;
}
@@ -3058,9 +4735,13 @@ static int arcmsr_abort(struct scsi_cmnd *cmd)
(struct AdapterControlBlock *)cmd->device->host->hostdata;
int i = 0;
int rtn = FAILED;
+ uint32_t intmask_org;
+
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+ return SUCCESS;
printk(KERN_NOTICE
- "arcmsr%d: abort device command of scsi id = %d lun = %d \n",
- acb->host->host_no, cmd->device->id, cmd->device->lun);
+ "arcmsr%d: abort device command of scsi id = %d lun = %d\n",
+ acb->host->host_no, cmd->device->id, (u32)cmd->device->lun);
acb->acb_flags |= ACB_F_ABORT;
acb->num_aborts++;
/*
@@ -3069,10 +4750,13 @@ static int arcmsr_abort(struct scsi_cmnd *cmd)
** we need to handle it as soon as possible and exit
************************************************
*/
- if (!atomic_read(&acb->ccboutstandingcount))
+ if (!atomic_read(&acb->ccboutstandingcount)) {
+ acb->acb_flags &= ~ACB_F_ABORT;
return rtn;
+ }
- for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
+ intmask_org = arcmsr_disable_outbound_ints(acb);
+ for (i = 0; i < acb->maxFreeCCB; i++) {
struct CommandControlBlock *ccb = acb->pccb_pool[i];
if (ccb->startdone == ARCMSR_CCB_START && ccb->pcmd == cmd) {
ccb->startdone = ARCMSR_CCB_ABORTED;
@@ -3081,6 +4765,7 @@ static int arcmsr_abort(struct scsi_cmnd *cmd)
}
}
acb->acb_flags &= ~ACB_F_ABORT;
+ arcmsr_enable_outbound_ints(acb, intmask_org);
return rtn;
}
@@ -3097,12 +4782,13 @@ static const char *arcmsr_info(struct Scsi_Host *host)
case PCI_DEVICE_ID_ARECA_1202:
case PCI_DEVICE_ID_ARECA_1210:
raid6 = 0;
- /*FALLTHRU*/
+ fallthrough;
case PCI_DEVICE_ID_ARECA_1120:
case PCI_DEVICE_ID_ARECA_1130:
case PCI_DEVICE_ID_ARECA_1160:
case PCI_DEVICE_ID_ARECA_1170:
case PCI_DEVICE_ID_ARECA_1201:
+ case PCI_DEVICE_ID_ARECA_1203:
case PCI_DEVICE_ID_ARECA_1220:
case PCI_DEVICE_ID_ARECA_1230:
case PCI_DEVICE_ID_ARECA_1260:
@@ -3110,19 +4796,26 @@ static const char *arcmsr_info(struct Scsi_Host *host)
case PCI_DEVICE_ID_ARECA_1280:
type = "SATA";
break;
+ case PCI_DEVICE_ID_ARECA_1214:
case PCI_DEVICE_ID_ARECA_1380:
case PCI_DEVICE_ID_ARECA_1381:
case PCI_DEVICE_ID_ARECA_1680:
case PCI_DEVICE_ID_ARECA_1681:
case PCI_DEVICE_ID_ARECA_1880:
- type = "SAS";
+ case PCI_DEVICE_ID_ARECA_1883:
+ case PCI_DEVICE_ID_ARECA_1884:
+ type = "SAS/SATA";
+ break;
+ case PCI_DEVICE_ID_ARECA_1886_0:
+ case PCI_DEVICE_ID_ARECA_1886:
+ type = "NVMe/SAS/SATA";
break;
default:
- type = "X-TYPE";
+ type = "unknown";
+ raid6 = 0;
break;
}
- sprintf(buf, "Areca %s Host Adapter RAID Controller%s\n %s",
- type, raid6 ? "( RAID6 capable)" : "",
- ARCMSR_DRIVER_VERSION);
+ sprintf(buf, "Areca %s RAID Controller %s\narcmsr version %s\n",
+ type, raid6 ? "(RAID6 capable)" : "", ARCMSR_DRIVER_VERSION);
return buf;
}
diff --git a/drivers/scsi/arm/Kconfig b/drivers/scsi/arm/Kconfig
index a8587f1f5e7e..9f64133f976a 100644
--- a/drivers/scsi/arm/Kconfig
+++ b/drivers/scsi/arm/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# SCSI driver configuration for Acorn
#
@@ -9,17 +10,6 @@ config SCSI_ACORNSCSI_3
This enables support for the Acorn SCSI card (aka30). If you have an
Acorn system with one of these, say Y. If unsure, say N.
-config SCSI_ACORNSCSI_TAGGED_QUEUE
- bool "Support SCSI 2 Tagged queueing"
- depends on SCSI_ACORNSCSI_3
- help
- Say Y here to enable tagged queuing support on the Acorn SCSI card.
-
- This is a feature of SCSI-2 which improves performance: the host
- adapter can send several SCSI commands to a device's queue even if
- previous commands haven't finished yet. Some SCSI devices don't
- implement this properly, so the safe answer is N.
-
config SCSI_ACORNSCSI_SYNC
bool "Support SCSI 2 Synchronous Transfers"
depends on SCSI_ACORNSCSI_3
@@ -64,19 +54,19 @@ config SCSI_POWERTECSCSI
you have one of these, say Y. If unsure, say N.
comment "The following drivers are not fully supported"
- depends on ARCH_ACORN && EXPERIMENTAL
+ depends on ARCH_ACORN
config SCSI_CUMANA_1
- tristate "CumanaSCSI I support (EXPERIMENTAL)"
- depends on ARCH_ACORN && EXPERIMENTAL && SCSI
+ tristate "CumanaSCSI I support"
+ depends on ARCH_ACORN && SCSI
select SCSI_SPI_ATTRS
help
This enables support for the Cumana SCSI I card. If you have an
Acorn system with one of these, say Y. If unsure, say N.
config SCSI_OAK1
- tristate "Oak SCSI support (EXPERIMENTAL)"
- depends on ARCH_ACORN && EXPERIMENTAL && SCSI
+ tristate "Oak SCSI support"
+ depends on ARCH_ACORN && SCSI
select SCSI_SPI_ATTRS
help
This enables support for the Oak SCSI card. If you have an Acorn
diff --git a/drivers/scsi/arm/Makefile b/drivers/scsi/arm/Makefile
index 16c3e86a6b1b..b576d9276f71 100644
--- a/drivers/scsi/arm/Makefile
+++ b/drivers/scsi/arm/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for drivers/scsi/arm
#
diff --git a/drivers/scsi/arm/acornscsi-io.S b/drivers/scsi/arm/acornscsi-io.S
index 22171b2110a8..fdd7237bb829 100644
--- a/drivers/scsi/arm/acornscsi-io.S
+++ b/drivers/scsi/arm/acornscsi-io.S
@@ -1,9 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/linkage.h>
diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
index c454e44cf51c..ef21b85cf014 100644
--- a/drivers/scsi/arm/acornscsi.c
+++ b/drivers/scsi/arm/acornscsi.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/scsi/acornscsi.c
*
* Acorn SCSI 3 driver
* By R.M.King.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Abandoned using the Select and Transfer command since there were
* some nasty races between our software and the target devices that
* were not easy to solve, and the device errata had a lot of entries
@@ -55,19 +52,8 @@
* You can tell if you have a device that supports tagged queueing my
* cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported
* as '2 TAG'.
- *
- * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config
- * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug,
- * comment out the undef.
- */
-#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
-/*
- * SCSI-II Linked command support.
- *
- * The higher level code doesn't support linked commands yet, and so the option
- * is undef'd here.
*/
-#undef CONFIG_SCSI_ACORNSCSI_LINK
+
/*
* SCSI-II Synchronous transfer support.
*
@@ -138,16 +124,19 @@
#include <linux/stringify.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <asm/ecard.h>
-#include "../scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_spi.h>
#include "acornscsi.h"
#include "msgqueue.h"
-#include "scsi.h"
+#include "arm_scsi.h"
#include <scsi/scsicam.h>
@@ -155,16 +144,6 @@
#define VER_MINOR 0
#define VER_PATCH 6
-#ifndef ABORT_TAG
-#define ABORT_TAG 0xd
-#else
-#error "Yippee! ABORT TAG is now defined! Remove this error!"
-#endif
-
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
-#error SCSI2 LINKed commands not supported (yet)!
-#endif
-
#ifdef USE_DMAC
/*
* DMAC setup parameters
@@ -192,7 +171,7 @@ static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,
unsigned int result);
static int acornscsi_reconnect_finish(AS_Host *host);
static void acornscsi_dma_cleanup(AS_Host *host);
-static void acornscsi_abortcmd(AS_Host *host, unsigned char tag);
+static void acornscsi_abortcmd(AS_Host *host);
/* ====================================================================================
* Miscellaneous
@@ -612,7 +591,7 @@ datadir_t acornscsi_datadirection(int command)
case CHANGE_DEFINITION: case COMPARE: case COPY:
case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
- case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE_6:
case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
case WRITE_6: case WRITE_10: case WRITE_VERIFY:
case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
@@ -689,7 +668,8 @@ int round_period(unsigned int period)
* Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
*/
static
-unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
+unsigned char __maybe_unused calc_sync_xfer(unsigned int period,
+ unsigned int offset)
{
return sync_xfer_table[round_period(period)].reg_value |
((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
@@ -749,7 +729,7 @@ intr_ret_t acornscsi_kick(AS_Host *host)
*/
host->scsi.phase = PHASE_CONNECTING;
host->SCpnt = SCpnt;
- host->scsi.SCp = SCpnt->SCp;
+ host->scsi.SCp = *arm_scsi_pointer(SCpnt);
host->dma.xfer_setup = 0;
host->dma.xfer_required = 0;
host->dma.xfer_done = 0;
@@ -761,18 +741,8 @@ intr_ret_t acornscsi_kick(AS_Host *host)
#endif
if (from_queue) {
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- /*
- * tagged queueing - allocate a new tag to this command
- */
- if (SCpnt->device->simple_tags) {
- SCpnt->device->current_tag += 1;
- if (SCpnt->device->current_tag == 0)
- SCpnt->device->current_tag = 1;
- SCpnt->tag = SCpnt->device->current_tag;
- } else
-#endif
- set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
+ set_bit(SCpnt->device->id * 8 +
+ (u8)(SCpnt->device->lun & 0x07), host->busyluns);
host->stats.removes += 1;
@@ -813,7 +783,10 @@ static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,
acornscsi_dma_cleanup(host);
- SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
+ set_host_byte(SCpnt, result);
+ if (result == DID_OK)
+ scsi_msg_to_host_byte(SCpnt, host->scsi.SCp.Message);
+ set_status_byte(SCpnt, host->scsi.SCp.Status);
/*
* In theory, this should not happen. In practice, it seems to.
@@ -852,32 +825,30 @@ static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,
xfer_warn = 0;
if (xfer_warn) {
- switch (status_byte(SCpnt->result)) {
- case CHECK_CONDITION:
- case COMMAND_TERMINATED:
- case BUSY:
- case QUEUE_FULL:
- case RESERVATION_CONFLICT:
+ switch (get_status_byte(SCpnt)) {
+ case SAM_STAT_CHECK_CONDITION:
+ case SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_BUSY:
+ case SAM_STAT_TASK_SET_FULL:
+ case SAM_STAT_RESERVATION_CONFLICT:
break;
default:
- printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
- host->host->host_no, SCpnt->result);
- __scsi_print_command(SCpnt->cmnd);
+ scmd_printk(KERN_ERR, SCpnt,
+ "incomplete data transfer detected: "
+ "result=%08X", SCpnt->result);
+ scsi_print_command(SCpnt);
acornscsi_dumpdma(host, "done");
- acornscsi_dumplog(host, SCpnt->device->id);
- SCpnt->result &= 0xffff;
- SCpnt->result |= DID_ERROR << 16;
+ acornscsi_dumplog(host, SCpnt->device->id);
+ set_host_byte(SCpnt, DID_ERROR);
}
}
}
- if (!SCpnt->scsi_done)
- panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
-
- clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
+ clear_bit(SCpnt->device->id * 8 +
+ (u8)(SCpnt->device->lun & 0x7), host->busyluns);
- SCpnt->scsi_done(SCpnt);
+ scsi_done(SCpnt);
} else
printk("scsi%d: null command in acornscsi_done", host->host->host_no);
@@ -1079,7 +1050,7 @@ void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
* Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
* Params : host - host to finish
* Notes : This is called when a command is:
- * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT
+ * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONNECT
* : This must not return until all transfers are completed.
*/
static
@@ -1207,7 +1178,7 @@ void acornscsi_dma_intr(AS_Host *host)
* the device recognises the attention.
*/
if (dmac_read(host, DMAC_STATUS) & STATUS_RQ0) {
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
dmac_write(host, DMAC_TXCNTLO, 0);
dmac_write(host, DMAC_TXCNTHI, 0);
@@ -1453,6 +1424,7 @@ unsigned char acornscsi_readmessagebyte(AS_Host *host)
static
void acornscsi_message(AS_Host *host)
{
+ struct scsi_pointer *scsi_pointer;
unsigned char message[16];
unsigned int msgidx = 0, msglen = 1;
@@ -1502,8 +1474,8 @@ void acornscsi_message(AS_Host *host)
}
switch (message[0]) {
- case ABORT:
- case ABORT_TAG:
+ case ABORT_TASK_SET:
+ case ABORT_TASK:
case COMMAND_COMPLETE:
if (host->scsi.phase != PHASE_STATUSIN) {
printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
@@ -1522,8 +1494,9 @@ void acornscsi_message(AS_Host *host)
* the saved data pointer for the current I/O process.
*/
acornscsi_dma_cleanup(host);
- host->SCpnt->SCp = host->scsi.SCp;
- host->SCpnt->SCp.sent_command = 0;
+ scsi_pointer = arm_scsi_pointer(host->SCpnt);
+ *scsi_pointer = host->scsi.SCp;
+ scsi_pointer->sent_command = 0;
host->scsi.phase = PHASE_MSGIN;
break;
@@ -1538,7 +1511,7 @@ void acornscsi_message(AS_Host *host)
* the present command and status areas.'
*/
acornscsi_dma_cleanup(host);
- host->scsi.SCp = host->SCpnt->SCp;
+ host->scsi.SCp = *arm_scsi_pointer(host->SCpnt);
host->scsi.phase = PHASE_MSGIN;
break;
@@ -1575,22 +1548,6 @@ void acornscsi_message(AS_Host *host)
acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
switch (host->scsi.last_message) {
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- case HEAD_OF_QUEUE_TAG:
- case ORDERED_QUEUE_TAG:
- case SIMPLE_QUEUE_TAG:
- /*
- * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
- * If a target does not implement tagged queuing and a queue tag
- * message is received, it shall respond with a MESSAGE REJECT
- * message and accept the I/O process as if it were untagged.
- */
- printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
- host->host->host_no, acornscsi_target(host));
- host->SCpnt->device->simple_tags = 0;
- set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns);
- break;
-#endif
case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
/*
* Target can't handle synchronous transfers
@@ -1607,10 +1564,6 @@ void acornscsi_message(AS_Host *host)
}
break;
- case QUEUE_FULL:
- /* TODO: target queue is full */
- break;
-
case SIMPLE_QUEUE_TAG:
/* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */
printk("scsi%d.%c: reconnect queue tag %02X\n",
@@ -1669,42 +1622,6 @@ void acornscsi_message(AS_Host *host)
}
break;
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
- case LINKED_CMD_COMPLETE:
- case LINKED_FLG_CMD_COMPLETE:
- /*
- * We don't support linked commands yet
- */
- if (0) {
-#if (DEBUG & DEBUG_LINK)
- printk("scsi%d.%c: lun %d tag %d linked command complete\n",
- host->host->host_no, acornscsi_target(host), host->SCpnt->tag);
-#endif
- /*
- * A linked command should only terminate with one of these messages
- * if there are more linked commands available.
- */
- if (!host->SCpnt->next_link) {
- printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
- instance->host_no, acornscsi_target(host), host->SCpnt->tag);
- acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
- msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
- } else {
- struct scsi_cmnd *SCpnt = host->SCpnt;
-
- acornscsi_dma_cleanup(host);
-
- host->SCpnt = host->SCpnt->next_link;
- host->SCpnt->tag = SCpnt->tag;
- SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status;
- SCpnt->done(SCpnt);
-
- /* initialise host->SCpnt->SCp */
- }
- break;
- }
-#endif
-
default: /* reject message */
printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
host->host->host_no, acornscsi_target(host),
@@ -1741,24 +1658,11 @@ void acornscsi_buildmessages(AS_Host *host)
#if 0
/* does the device need the current command aborted */
if (cmd_aborted) {
- acornscsi_abortcmd(host->SCpnt->tag);
+ acornscsi_abortcmd(host);
return;
}
#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- if (host->SCpnt->tag) {
- unsigned int tag_type;
-
- if (host->SCpnt->cmnd[0] == REQUEST_SENSE ||
- host->SCpnt->cmnd[0] == TEST_UNIT_READY ||
- host->SCpnt->cmnd[0] == INQUIRY)
- tag_type = HEAD_OF_QUEUE_TAG;
- else
- tag_type = SIMPLE_QUEUE_TAG;
- msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
- }
-#endif
#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) {
@@ -1852,7 +1756,7 @@ int acornscsi_reconnect(AS_Host *host)
"to reconnect with\n",
host->host->host_no, '0' + target);
acornscsi_dumplog(host, target);
- acornscsi_abortcmd(host, 0);
+ acornscsi_abortcmd(host);
if (host->SCpnt) {
queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
host->SCpnt = NULL;
@@ -1863,7 +1767,7 @@ int acornscsi_reconnect(AS_Host *host)
}
/*
- * Function: int acornscsi_reconect_finish(AS_Host *host)
+ * Function: int acornscsi_reconnect_finish(AS_Host *host)
* Purpose : finish reconnecting a command
* Params : host - host to complete
* Returns : 0 if failed
@@ -1875,7 +1779,7 @@ int acornscsi_reconnect_finish(AS_Host *host)
host->scsi.disconnectable = 0;
if (host->SCpnt->device->id == host->scsi.reconnected.target &&
host->SCpnt->device->lun == host->scsi.reconnected.lun &&
- host->SCpnt->tag == host->scsi.reconnected.tag) {
+ scsi_cmd_to_rq(host->SCpnt)->tag == host->scsi.reconnected.tag) {
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
DBG(host->SCpnt, printk("scsi%d.%c: reconnected",
host->host->host_no, acornscsi_target(host)));
@@ -1902,12 +1806,12 @@ int acornscsi_reconnect_finish(AS_Host *host)
}
if (!host->SCpnt)
- acornscsi_abortcmd(host, host->scsi.reconnected.tag);
+ acornscsi_abortcmd(host);
else {
/*
* Restore data pointer from SAVED pointers.
*/
- host->scsi.SCp = host->SCpnt->SCp;
+ host->scsi.SCp = *arm_scsi_pointer(host->SCpnt);
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
printk(", data pointers: [%p, %X]",
host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
@@ -1943,21 +1847,15 @@ void acornscsi_disconnect_unexpected(AS_Host *host)
* Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)
* Purpose : abort a currently executing command
* Params : host - host with connected command to abort
- * tag - tag to abort
*/
static
-void acornscsi_abortcmd(AS_Host *host, unsigned char tag)
+void acornscsi_abortcmd(AS_Host *host)
{
host->scsi.phase = PHASE_ABORTED;
sbic_arm_write(host, SBIC_CMND, CMND_ASSERTATN);
msgqueue_flush(&host->scsi.msgs);
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- if (tag)
- msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag);
- else
-#endif
- msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
+ msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
}
/* ==========================================================================================
@@ -2047,7 +1945,7 @@ intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
}
return INTR_PROCESSING;
@@ -2083,7 +1981,7 @@ intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
host->host->host_no, acornscsi_target(host), ssr);
acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
}
return INTR_PROCESSING;
@@ -2129,20 +2027,20 @@ intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
case 0x18: /* -> PHASE_DATAOUT */
/* COMMAND -> DATA OUT */
if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
acornscsi_dma_setup(host, DMA_OUT);
if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
host->scsi.phase = PHASE_DATAOUT;
return INTR_IDLE;
case 0x19: /* -> PHASE_DATAIN */
/* COMMAND -> DATA IN */
if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
acornscsi_dma_setup(host, DMA_IN);
if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
host->scsi.phase = PHASE_DATAIN;
return INTR_IDLE;
@@ -2210,7 +2108,7 @@ intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
/* MESSAGE IN -> DATA OUT */
acornscsi_dma_setup(host, DMA_OUT);
if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
host->scsi.phase = PHASE_DATAOUT;
return INTR_IDLE;
@@ -2219,7 +2117,7 @@ intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
/* MESSAGE IN -> DATA IN */
acornscsi_dma_setup(host, DMA_IN);
if (!acornscsi_starttransfer(host))
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
host->scsi.phase = PHASE_DATAIN;
return INTR_IDLE;
@@ -2260,7 +2158,7 @@ intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
switch (ssr) {
case 0x19: /* -> PHASE_DATAIN */
case 0x89: /* -> PHASE_DATAIN */
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
return INTR_IDLE;
case 0x1b: /* -> PHASE_STATUSIN */
@@ -2309,7 +2207,7 @@ intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
switch (ssr) {
case 0x18: /* -> PHASE_DATAOUT */
case 0x88: /* -> PHASE_DATAOUT */
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
return INTR_IDLE;
case 0x1b: /* -> PHASE_STATUSIN */
@@ -2505,41 +2403,32 @@ acornscsi_intr(int irq, void *dev_id)
*/
/*
- * Function : acornscsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+ * Function : acornscsi_queuecmd(struct scsi_cmnd *cmd)
* Purpose : queues a SCSI command
* Params : cmd - SCSI command
- * done - function called on completion, with pointer to command descriptor
* Returns : 0, or < 0 on error.
*/
-static int acornscsi_queuecmd_lck(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
+static int acornscsi_queuecmd_lck(struct scsi_cmnd *SCpnt)
{
+ struct scsi_pointer *scsi_pointer = arm_scsi_pointer(SCpnt);
+ void (*done)(struct scsi_cmnd *) = scsi_done;
AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
- if (!done) {
- /* there should be some way of rejecting errors like this without panicing... */
- panic("scsi%d: queuecommand called with NULL done function [cmd=%p]",
- host->host->host_no, SCpnt);
- return -EINVAL;
- }
-
#if (DEBUG & DEBUG_NO_WRITE)
if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) {
printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
host->host->host_no, '0' + SCpnt->device->id);
- SCpnt->result = DID_NO_CONNECT << 16;
+ set_host_byte(SCpnt, DID_NO_CONNECT);
done(SCpnt);
return 0;
}
#endif
- SCpnt->scsi_done = done;
SCpnt->host_scribble = NULL;
SCpnt->result = 0;
- SCpnt->tag = 0;
- SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
- SCpnt->SCp.sent_command = 0;
- SCpnt->SCp.scsi_xferred = 0;
+ scsi_pointer->phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
+ scsi_pointer->sent_command = 0;
+ scsi_pointer->scsi_xferred = 0;
init_SCp(SCpnt);
@@ -2549,7 +2438,7 @@ static int acornscsi_queuecmd_lck(struct scsi_cmnd *SCpnt,
unsigned long flags;
if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {
- SCpnt->result = DID_ERROR << 16;
+ set_host_byte(SCpnt, DID_ERROR);
done(SCpnt);
return 0;
}
@@ -2561,32 +2450,7 @@ static int acornscsi_queuecmd_lck(struct scsi_cmnd *SCpnt,
return 0;
}
-DEF_SCSI_QCMD(acornscsi_queuecmd)
-
-/*
- * Prototype: void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1, struct scsi_cmnd **SCpntp2, int result)
- * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
- * Params : SCpntp1 - pointer to command to return
- * SCpntp2 - pointer to command to check
- * result - result to pass back to mid-level done function
- * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
- */
-static inline void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1,
- struct scsi_cmnd **SCpntp2,
- int result)
-{
- struct scsi_cmnd *SCpnt = *SCpntp1;
-
- if (SCpnt) {
- *SCpntp1 = NULL;
-
- SCpnt->result = result;
- SCpnt->scsi_done(SCpnt);
- }
-
- if (SCpnt == *SCpntp2)
- *SCpntp2 = NULL;
-}
+static DEF_SCSI_QCMD(acornscsi_queuecmd)
enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
@@ -2660,7 +2524,7 @@ static enum res_abort acornscsi_do_abort(AS_Host *host, struct scsi_cmnd *SCpnt)
break;
default:
- acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_abortcmd(host);
res = res_snooze;
}
local_irq_restore(flags);
@@ -2688,7 +2552,7 @@ static enum res_abort acornscsi_do_abort(AS_Host *host, struct scsi_cmnd *SCpnt)
* Params : SCpnt - command to abort
* Returns : one of SCSI_ABORT_ macros
*/
-int acornscsi_abort(struct scsi_cmnd *SCpnt)
+static int acornscsi_abort(struct scsi_cmnd *SCpnt)
{
AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata;
int result;
@@ -2719,7 +2583,9 @@ int acornscsi_abort(struct scsi_cmnd *SCpnt)
//#if (DEBUG & DEBUG_ABORT)
printk("clear ");
//#endif
- clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
+ clear_bit(SCpnt->device->id * 8 +
+ (u8)(SCpnt->device->lun & 0x7), host->busyluns);
+ fallthrough;
/*
* We found the command, and cleared it out. Either
@@ -2768,7 +2634,7 @@ int acornscsi_abort(struct scsi_cmnd *SCpnt)
* Params : SCpnt - command causing reset
* Returns : one of SCSI_RESET_ macros
*/
-int acornscsi_bus_reset(struct scsi_cmnd *SCpnt)
+static int acornscsi_host_reset(struct scsi_cmnd *SCpnt)
{
AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
struct scsi_cmnd *SCptr;
@@ -2777,14 +2643,15 @@ int acornscsi_bus_reset(struct scsi_cmnd *SCpnt)
#if (DEBUG & DEBUG_RESET)
{
- int asr, ssr;
+ int asr, ssr, devidx;
asr = sbic_arm_read(host, SBIC_ASR);
ssr = sbic_arm_read(host, SBIC_SSR);
printk(KERN_WARNING "acornscsi_reset: ");
print_sbic_status(asr, ssr, host->scsi.phase);
- acornscsi_dumplog(host, SCpnt->device->id);
+ for (devidx = 0; devidx < 9; devidx++)
+ acornscsi_dumplog(host, devidx);
}
#endif
@@ -2812,8 +2679,7 @@ int acornscsi_bus_reset(struct scsi_cmnd *SCpnt)
* Params : host - host to give information on
* Returns : a constant string
*/
-const
-char *acornscsi_info(struct Scsi_Host *host)
+static const char *acornscsi_info(struct Scsi_Host *host)
{
static char string[100], *p;
@@ -2823,12 +2689,6 @@ char *acornscsi_info(struct Scsi_Host *host)
#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
" SYNC"
#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- " TAG"
-#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
- " LINK"
-#endif
#if (DEBUG & DEBUG_NO_WRITE)
" NOWRITE (" __stringify(NO_WRITE) ")"
#endif
@@ -2837,42 +2697,31 @@ char *acornscsi_info(struct Scsi_Host *host)
return string;
}
-int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start, off_t offset,
- int length, int inout)
+static int acornscsi_show_info(struct seq_file *m, struct Scsi_Host *instance)
{
- int pos, begin = 0, devidx;
+ int devidx;
struct scsi_device *scd;
AS_Host *host;
- char *p = buffer;
-
- if (inout == 1)
- return -EINVAL;
host = (AS_Host *)instance->hostdata;
- p += sprintf(p, "AcornSCSI driver v%d.%d.%d"
+ seq_printf(m, "AcornSCSI driver v%d.%d.%d"
#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
" SYNC"
#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
- " TAG"
-#endif
-#ifdef CONFIG_SCSI_ACORNSCSI_LINK
- " LINK"
-#endif
#if (DEBUG & DEBUG_NO_WRITE)
" NOWRITE (" __stringify(NO_WRITE) ")"
#endif
"\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
- p += sprintf(p, "SBIC: WD33C93A Address: %p IRQ : %d\n",
+ seq_printf(m, "SBIC: WD33C93A Address: %p IRQ : %d\n",
host->base + SBIC_REGIDX, host->scsi.irq);
#ifdef USE_DMAC
- p += sprintf(p, "DMAC: uPC71071 Address: %p IRQ : %d\n\n",
+ seq_printf(m, "DMAC: uPC71071 Address: %p IRQ : %d\n\n",
host->base + DMAC_OFFSET, host->scsi.irq);
#endif
- p += sprintf(p, "Statistics:\n"
+ seq_printf(m, "Statistics:\n"
"Queued commands: %-10u Issued commands: %-10u\n"
"Done commands : %-10u Reads : %-10u\n"
"Writes : %-10u Others : %-10u\n"
@@ -2887,7 +2736,7 @@ int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start,
for (devidx = 0; devidx < 9; devidx ++) {
unsigned int statptr, prev;
- p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
+ seq_printf(m, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
statptr = host->status_ptr[devidx] - 10;
if ((signed int)statptr < 0)
@@ -2897,7 +2746,7 @@ int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start,
for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
if (host->status[devidx][statptr].when) {
- p += sprintf(p, "%c%02X:%02X+%2ld",
+ seq_printf(m, "%c%02X:%02X+%2ld",
host->status[devidx][statptr].irq ? '-' : ' ',
host->status[devidx][statptr].ph,
host->status[devidx][statptr].ssr,
@@ -2908,66 +2757,46 @@ int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start,
}
}
- p += sprintf(p, "\nAttached devices:\n");
+ seq_printf(m, "\nAttached devices:\n");
shost_for_each_device(scd, instance) {
- p += sprintf(p, "Device/Lun TaggedQ Sync\n");
- p += sprintf(p, " %d/%d ", scd->id, scd->lun);
+ seq_printf(m, "Device/Lun TaggedQ Sync\n");
+ seq_printf(m, " %d/%llu ", scd->id, scd->lun);
if (scd->tagged_supported)
- p += sprintf(p, "%3sabled(%3d) ",
- scd->simple_tags ? "en" : "dis",
- scd->current_tag);
+ seq_printf(m, "%3sabled ",
+ scd->simple_tags ? "en" : "dis");
else
- p += sprintf(p, "unsupported ");
+ seq_printf(m, "unsupported ");
if (host->device[scd->id].sync_xfer & 15)
- p += sprintf(p, "offset %d, %d ns\n",
+ seq_printf(m, "offset %d, %d ns\n",
host->device[scd->id].sync_xfer & 15,
acornscsi_getperiod(host->device[scd->id].sync_xfer));
else
- p += sprintf(p, "async\n");
+ seq_printf(m, "async\n");
- pos = p - buffer;
- if (pos + begin < offset) {
- begin += pos;
- p = buffer;
- }
- pos = p - buffer;
- if (pos + begin > offset + length) {
- scsi_device_put(scd);
- break;
- }
}
-
- pos = p - buffer;
-
- *start = buffer + (offset - begin);
- pos -= offset - begin;
-
- if (pos > length)
- pos = length;
-
- return pos;
+ return 0;
}
-static struct scsi_host_template acornscsi_template = {
+static const struct scsi_host_template acornscsi_template = {
.module = THIS_MODULE,
- .proc_info = acornscsi_proc_info,
+ .show_info = acornscsi_show_info,
.name = "AcornSCSI",
.info = acornscsi_info,
.queuecommand = acornscsi_queuecmd,
.eh_abort_handler = acornscsi_abort,
- .eh_bus_reset_handler = acornscsi_bus_reset,
+ .eh_host_reset_handler = acornscsi_host_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
.proc_name = "acornscsi",
+ .cmd_size = sizeof(struct arm_cmd_priv),
};
-static int __devinit
-acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+static int acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
AS_Host *ashost;
@@ -2987,8 +2816,10 @@ acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
ashost->base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
ashost->fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
- if (!ashost->base || !ashost->fast)
+ if (!ashost->base || !ashost->fast) {
+ ret = -ENOMEM;
goto out_put;
+ }
host->irq = ec->irq;
ashost->host = host;
@@ -2997,7 +2828,7 @@ acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
ec->irqaddr = ashost->fast + INT_REG;
ec->irqmask = 0x0a;
- ret = request_irq(host->irq, acornscsi_intr, IRQF_DISABLED, "acornscsi", ashost);
+ ret = request_irq(host->irq, acornscsi_intr, 0, "acornscsi", ashost);
if (ret) {
printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n",
host->host_no, ashost->scsi.irq, ret);
@@ -3033,7 +2864,7 @@ acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
return ret;
}
-static void __devexit acornscsi_remove(struct expansion_card *ec)
+static void acornscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
AS_Host *ashost = (AS_Host *)host->hostdata;
@@ -3064,7 +2895,7 @@ static const struct ecard_id acornscsi_cids[] = {
static struct ecard_driver acornscsi_driver = {
.probe = acornscsi_probe,
- .remove = __devexit_p(acornscsi_remove),
+ .remove = acornscsi_remove,
.id_table = acornscsi_cids,
.drv = {
.name = "acornscsi",
diff --git a/drivers/scsi/arm/acornscsi.h b/drivers/scsi/arm/acornscsi.h
index 01bc715a3aec..376c76bc2aca 100644
--- a/drivers/scsi/arm/acornscsi.h
+++ b/drivers/scsi/arm/acornscsi.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/drivers/acorn/scsi/acornscsi.h
*
* Copyright (C) 1997 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Acorn SCSI driver
*/
#ifndef ACORNSCSI_H
diff --git a/drivers/scsi/arm/scsi.h b/drivers/scsi/arm/arm_scsi.h
index 138a521ba1a8..ea9fcd92c6de 100644
--- a/drivers/scsi/arm/scsi.h
+++ b/drivers/scsi/arm/arm_scsi.h
@@ -1,19 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * linux/drivers/acorn/scsi/scsi.h
- *
* Copyright (C) 2002 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Commonly used scsi driver functions.
+ * Commonly used functions by the ARM SCSI-II drivers.
*/
#include <linux/scatterlist.h>
#define BELT_AND_BRACES
+struct arm_cmd_priv {
+ struct scsi_pointer scsi_pointer;
+};
+
+static inline struct scsi_pointer *arm_scsi_pointer(struct scsi_cmnd *cmd)
+{
+ struct arm_cmd_priv *acmd = scsi_cmd_priv(cmd);
+
+ return &acmd->scsi_pointer;
+}
+
/*
* The scatter-gather list handling. This contains all
* the yucky stuff that needs to be fixed properly.
@@ -81,16 +87,18 @@ static inline void put_next_SCp_byte(struct scsi_pointer *SCp, unsigned char c)
static inline void init_SCp(struct scsi_cmnd *SCpnt)
{
- memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer));
+ struct scsi_pointer *scsi_pointer = arm_scsi_pointer(SCpnt);
+
+ memset(scsi_pointer, 0, sizeof(struct scsi_pointer));
if (scsi_bufflen(SCpnt)) {
unsigned long len = 0;
- SCpnt->SCp.buffer = scsi_sglist(SCpnt);
- SCpnt->SCp.buffers_residual = scsi_sg_count(SCpnt) - 1;
- SCpnt->SCp.ptr = sg_virt(SCpnt->SCp.buffer);
- SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
- SCpnt->SCp.phase = scsi_bufflen(SCpnt);
+ scsi_pointer->buffer = scsi_sglist(SCpnt);
+ scsi_pointer->buffers_residual = scsi_sg_count(SCpnt) - 1;
+ scsi_pointer->ptr = sg_virt(scsi_pointer->buffer);
+ scsi_pointer->this_residual = scsi_pointer->buffer->length;
+ scsi_pointer->phase = scsi_bufflen(SCpnt);
#ifdef BELT_AND_BRACES
{ /*
@@ -114,15 +122,15 @@ static inline void init_SCp(struct scsi_cmnd *SCpnt)
* FIXME: Totaly naive fixup. We should abort
* with error
*/
- SCpnt->SCp.phase =
+ scsi_pointer->phase =
min_t(unsigned long, len,
scsi_bufflen(SCpnt));
}
}
#endif
} else {
- SCpnt->SCp.ptr = NULL;
- SCpnt->SCp.this_residual = 0;
- SCpnt->SCp.phase = 0;
+ scsi_pointer->ptr = NULL;
+ scsi_pointer->this_residual = 0;
+ scsi_pointer->phase = 0;
}
}
diff --git a/drivers/scsi/arm/arxescsi.c b/drivers/scsi/arm/arxescsi.c
index a750aa72b8ef..925d0bd68aa5 100644
--- a/drivers/scsi/arm/arxescsi.c
+++ b/drivers/scsi/arm/arxescsi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/scsi/arm/arxescsi.c
*
@@ -34,8 +35,12 @@
#include <asm/io.h>
#include <asm/ecard.h>
-#include "../scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include "fas216.h"
struct arxescsi_info {
@@ -220,47 +225,21 @@ static const char *arxescsi_info(struct Scsi_Host *host)
return string;
}
-/*
- * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset,
- * int length, int host_no, int inout)
- * Purpose : Return information about the driver to a user process accessing
- * the /proc filesystem.
- * Params : buffer - a buffer to write information to
- * start - a pointer into this buffer set by this routine to the start
- * of the required information.
- * offset - offset into information that we have read up to.
- * length - length of buffer
- * host_no - host number to return information for
- * inout - 0 for reading, 1 for writing.
- * Returns : length of data written to buffer.
- */
static int
-arxescsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length,
- int inout)
+arxescsi_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct arxescsi_info *info;
- char *p = buffer;
- int pos;
-
info = (struct arxescsi_info *)host->hostdata;
- if (inout == 1)
- return -EINVAL;
-
- p += sprintf(p, "ARXE 16-bit SCSI driver v%s\n", VERSION);
- p += fas216_print_host(&info->info, p);
- p += fas216_print_stats(&info->info, p);
- p += fas216_print_devices(&info->info, p);
-
- *start = buffer + offset;
- pos = p - buffer - offset;
- if (pos > length)
- pos = length;
- return pos;
+ seq_printf(m, "ARXE 16-bit SCSI driver v%s\n", VERSION);
+ fas216_print_host(&info->info, m);
+ fas216_print_stats(&info->info, m);
+ fas216_print_devices(&info->info, m);
+ return 0;
}
-static struct scsi_host_template arxescsi_template = {
- .proc_info = arxescsi_proc_info,
+static const struct scsi_host_template arxescsi_template = {
+ .show_info = arxescsi_show_info,
.name = "ARXE SCSI card",
.info = arxescsi_info,
.queuecommand = fas216_noqueue_command,
@@ -268,16 +247,15 @@ static struct scsi_host_template arxescsi_template = {
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
+ .cmd_size = sizeof(struct fas216_cmd_priv),
.can_queue = 0,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
.proc_name = "arxescsi",
};
-static int __devinit
-arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+static int arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct arxescsi_info *info;
@@ -305,7 +283,7 @@ arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
info->base = base;
info->info.scsi.io_base = base + 0x2000;
- info->info.scsi.irq = NO_IRQ;
+ info->info.scsi.irq = 0;
info->info.scsi.dma = NO_DMA;
info->info.scsi.io_shift = 5;
info->info.ifcfg.clockrate = 24; /* MHz */
@@ -340,7 +318,7 @@ arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
return ret;
}
-static void __devexit arxescsi_remove(struct expansion_card *ec)
+static void arxescsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
@@ -359,7 +337,7 @@ static const struct ecard_id arxescsi_cids[] = {
static struct ecard_driver arxescsi_driver = {
.probe = arxescsi_probe,
- .remove = __devexit_p(arxescsi_remove),
+ .remove = arxescsi_remove,
.id_table = arxescsi_cids,
.drv = {
.name = "arxescsi",
diff --git a/drivers/scsi/arm/cumana_1.c b/drivers/scsi/arm/cumana_1.c
index a3398fe70a9c..d1a2a22ffe8c 100644
--- a/drivers/scsi/arm/cumana_1.c
+++ b/drivers/scsi/arm/cumana_1.c
@@ -1,77 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Generic Generic NCR5380 driver
*
* Copyright 1995-2002, Russell King
*/
#include <linux/module.h>
-#include <linux/signal.h>
#include <linux/ioport.h>
-#include <linux/delay.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <asm/ecard.h>
#include <asm/io.h>
-#include <asm/system.h>
-#include "../scsi.h"
#include <scsi/scsi_host.h>
-#include <scsi/scsicam.h>
-
-#define AUTOSENSE
-#define PSEUDO_DMA
+#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
+#define NCR5380_read(reg) cumanascsi_read(hostdata, reg)
+#define NCR5380_write(reg, value) cumanascsi_write(hostdata, reg, value)
-#define CUMANASCSI_PUBLIC_RELEASE 1
+#define NCR5380_dma_xfer_len cumanascsi_dma_xfer_len
+#define NCR5380_dma_recv_setup cumanascsi_pread
+#define NCR5380_dma_send_setup cumanascsi_pwrite
+#define NCR5380_dma_residual NCR5380_dma_residual_none
-#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
-#define NCR5380_local_declare() struct Scsi_Host *_instance
-#define NCR5380_setup(instance) _instance = instance
-#define NCR5380_read(reg) cumanascsi_read(_instance, reg)
-#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value)
#define NCR5380_intr cumanascsi_intr
#define NCR5380_queue_command cumanascsi_queue_command
-#define NCR5380_proc_info cumanascsi_proc_info
+#define NCR5380_info cumanascsi_info
#define NCR5380_implementation_fields \
- unsigned ctrl; \
- void __iomem *base; \
- void __iomem *dma
+ unsigned ctrl
-#define BOARD_NORMAL 0
-#define BOARD_NCR53C400 1
+struct NCR5380_hostdata;
+static u8 cumanascsi_read(struct NCR5380_hostdata *, unsigned int);
+static void cumanascsi_write(struct NCR5380_hostdata *, unsigned int, u8);
#include "../NCR5380.h"
-void cumanascsi_setup(char *str, int *ints)
-{
-}
-
-const char *cumanascsi_info(struct Scsi_Host *spnt)
-{
- return "";
-}
-
#define CTRL 0x16fc
#define STAT 0x2004
#define L(v) (((v)<<16)|((v) & 0x0000ffff))
#define H(v) (((v)>>16)|((v) & 0xffff0000))
-static inline int
-NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, int len)
+static inline int cumanascsi_pwrite(struct NCR5380_hostdata *hostdata,
+ unsigned char *addr, int len)
{
unsigned long *laddr;
- void __iomem *dma = priv(host)->dma + 0x2000;
+ u8 __iomem *base = hostdata->io;
+ u8 __iomem *dma = hostdata->pdma_io + 0x2000;
if(!len) return 0;
- writeb(0x02, priv(host)->base + CTRL);
+ writeb(0x02, base + CTRL);
laddr = (unsigned long *)addr;
while(len >= 32)
{
unsigned int status;
unsigned long v;
- status = readb(priv(host)->base + STAT);
+ status = readb(base + STAT);
if(status & 0x80)
goto end;
if(!(status & 0x40))
@@ -90,12 +75,12 @@ NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, int len)
}
addr = (unsigned char *)laddr;
- writeb(0x12, priv(host)->base + CTRL);
+ writeb(0x12, base + CTRL);
while(len > 0)
{
unsigned int status;
- status = readb(priv(host)->base + STAT);
+ status = readb(base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
@@ -105,7 +90,7 @@ NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, int len)
break;
}
- status = readb(priv(host)->base + STAT);
+ status = readb(base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
@@ -116,24 +101,28 @@ NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, int len)
}
}
end:
- writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL);
- return len;
+ writeb(hostdata->ctrl | 0x40, base + CTRL);
+
+ if (len)
+ return -1;
+ return 0;
}
-static inline int
-NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, int len)
+static inline int cumanascsi_pread(struct NCR5380_hostdata *hostdata,
+ unsigned char *addr, int len)
{
unsigned long *laddr;
- void __iomem *dma = priv(host)->dma + 0x2000;
+ u8 __iomem *base = hostdata->io;
+ u8 __iomem *dma = hostdata->pdma_io + 0x2000;
if(!len) return 0;
- writeb(0x00, priv(host)->base + CTRL);
+ writeb(0x00, base + CTRL);
laddr = (unsigned long *)addr;
while(len >= 32)
{
unsigned int status;
- status = readb(priv(host)->base + STAT);
+ status = readb(base + STAT);
if(status & 0x80)
goto end;
if(!(status & 0x40))
@@ -152,12 +141,12 @@ NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, int len)
}
addr = (unsigned char *)laddr;
- writeb(0x10, priv(host)->base + CTRL);
+ writeb(0x10, base + CTRL);
while(len > 0)
{
unsigned int status;
- status = readb(priv(host)->base + STAT);
+ status = readb(base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
@@ -167,7 +156,7 @@ NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, int len)
break;
}
- status = readb(priv(host)->base + STAT);
+ status = readb(base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
@@ -178,56 +167,69 @@ NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, int len)
}
}
end:
- writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL);
- return len;
+ writeb(hostdata->ctrl | 0x40, base + CTRL);
+
+ if (len)
+ return -1;
+ return 0;
}
-static unsigned char cumanascsi_read(struct Scsi_Host *host, unsigned int reg)
+static int cumanascsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
+ struct scsi_cmnd *cmd)
{
- void __iomem *base = priv(host)->base;
- unsigned char val;
+ return cmd->transfersize;
+}
+
+static u8 cumanascsi_read(struct NCR5380_hostdata *hostdata,
+ unsigned int reg)
+{
+ u8 __iomem *base = hostdata->io;
+ u8 val;
writeb(0, base + CTRL);
val = readb(base + 0x2100 + (reg << 2));
- priv(host)->ctrl = 0x40;
+ hostdata->ctrl = 0x40;
writeb(0x40, base + CTRL);
return val;
}
-static void cumanascsi_write(struct Scsi_Host *host, unsigned int reg, unsigned int value)
+static void cumanascsi_write(struct NCR5380_hostdata *hostdata,
+ unsigned int reg, u8 value)
{
- void __iomem *base = priv(host)->base;
+ u8 __iomem *base = hostdata->io;
writeb(0, base + CTRL);
writeb(value, base + 0x2100 + (reg << 2));
- priv(host)->ctrl = 0x40;
+ hostdata->ctrl = 0x40;
writeb(0x40, base + CTRL);
}
#include "../NCR5380.c"
-static struct scsi_host_template cumanascsi_template = {
+static const struct scsi_host_template cumanascsi_template = {
.module = THIS_MODULE,
.name = "Cumana 16-bit SCSI",
.info = cumanascsi_info,
.queuecommand = cumanascsi_queue_command,
.eh_abort_handler = NCR5380_abort,
- .eh_bus_reset_handler = NCR5380_bus_reset,
+ .eh_host_reset_handler = NCR5380_host_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
- .use_clustering = DISABLE_CLUSTERING,
.proc_name = "CumanaSCSI-1",
+ .cmd_size = sizeof(struct NCR5380_cmd),
+ .max_sectors = 128,
+ .dma_boundary = PAGE_SIZE - 1,
};
-static int __devinit
-cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id)
+static int cumanascsi1_probe(struct expansion_card *ec,
+ const struct ecard_id *id)
{
struct Scsi_Host *host;
int ret;
@@ -242,44 +244,34 @@ cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id)
goto out_release;
}
- priv(host)->base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW),
- ecard_resource_len(ec, ECARD_RES_IOCSLOW));
- priv(host)->dma = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
- ecard_resource_len(ec, ECARD_RES_MEMC));
- if (!priv(host)->base || !priv(host)->dma) {
+ priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW),
+ ecard_resource_len(ec, ECARD_RES_IOCSLOW));
+ priv(host)->pdma_io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
+ ecard_resource_len(ec, ECARD_RES_MEMC));
+ if (!priv(host)->io || !priv(host)->pdma_io) {
ret = -ENOMEM;
goto out_unmap;
}
host->irq = ec->irq;
- NCR5380_init(host, 0);
+ ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP);
+ if (ret)
+ goto out_unmap;
- priv(host)->ctrl = 0;
- writeb(0, priv(host)->base + CTRL);
+ NCR5380_maybe_reset_bus(host);
- host->n_io_port = 255;
- if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) {
- ret = -EBUSY;
- goto out_unmap;
- }
+ priv(host)->ctrl = 0;
+ writeb(0, priv(host)->io + CTRL);
- ret = request_irq(host->irq, cumanascsi_intr, IRQF_DISABLED,
+ ret = request_irq(host->irq, cumanascsi_intr, 0,
"CumanaSCSI-1", host);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, host->irq, ret);
- goto out_unmap;
+ goto out_exit;
}
- printk("scsi%d: at port 0x%08lx irq %d",
- host->host_no, host->io_port, host->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE);
- printk("\nscsi%d:", host->host_no);
- NCR5380_print_options(host);
- printk("\n");
-
ret = scsi_add_host(host, &ec->dev);
if (ret)
goto out_free_irq;
@@ -289,9 +281,11 @@ cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id)
out_free_irq:
free_irq(host->irq, host);
+ out_exit:
+ NCR5380_exit(host);
out_unmap:
- iounmap(priv(host)->base);
- iounmap(priv(host)->dma);
+ iounmap(priv(host)->io);
+ iounmap(priv(host)->pdma_io);
scsi_host_put(host);
out_release:
ecard_release_resources(ec);
@@ -299,18 +293,20 @@ cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id)
return ret;
}
-static void __devexit cumanascsi1_remove(struct expansion_card *ec)
+static void cumanascsi1_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
+ void __iomem *base = priv(host)->io;
+ void __iomem *dma = priv(host)->pdma_io;
ecard_set_drvdata(ec, NULL);
scsi_remove_host(host);
free_irq(host->irq, host);
NCR5380_exit(host);
- iounmap(priv(host)->base);
- iounmap(priv(host)->dma);
scsi_host_put(host);
+ iounmap(base);
+ iounmap(dma);
ecard_release_resources(ec);
}
@@ -321,7 +317,7 @@ static const struct ecard_id cumanascsi1_cids[] = {
static struct ecard_driver cumanascsi1_driver = {
.probe = cumanascsi1_probe,
- .remove = __devexit_p(cumanascsi1_remove),
+ .remove = cumanascsi1_remove,
.id_table = cumanascsi1_cids,
.drv = {
.name = "cumanascsi1",
diff --git a/drivers/scsi/arm/cumana_2.c b/drivers/scsi/arm/cumana_2.c
index 547987b86384..e460068f6834 100644
--- a/drivers/scsi/arm/cumana_2.c
+++ b/drivers/scsi/arm/cumana_2.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/scsi/cumana_2.c
*
* Copyright (C) 1997-2005 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Changelog:
* 30-08-1997 RMK 0.0.0 Created, READONLY version.
* 22-01-1998 RMK 0.0.1 Updated to 2.1.80.
@@ -26,16 +23,20 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
+#include <linux/pgtable.h>
#include <asm/dma.h>
#include <asm/ecard.h>
#include <asm/io.h>
-#include <asm/pgtable.h>
-#include "../scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include "fas216.h"
-#include "scsi.h"
+#include "arm_scsi.h"
#include <scsi/scsicam.h>
@@ -169,14 +170,15 @@ cumanascsi_2_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,
bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
- if (direction == DMA_OUT)
- map_dir = DMA_TO_DEVICE,
- dma_dir = DMA_MODE_WRITE,
+ if (direction == DMA_OUT) {
+ map_dir = DMA_TO_DEVICE;
+ dma_dir = DMA_MODE_WRITE;
alatch_dir = ALATCH_DMA_OUT;
- else
- map_dir = DMA_FROM_DEVICE,
- dma_dir = DMA_MODE_READ,
+ } else {
+ map_dir = DMA_FROM_DEVICE;
+ dma_dir = DMA_MODE_READ;
alatch_dir = ALATCH_DMA_IN;
+ }
dma_map_sg(dev, info->sg, bufs, map_dir);
@@ -294,7 +296,7 @@ cumanascsi_2_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)
* Params : host - driver host structure to return info for.
* Returns : pointer to a static buffer containing null terminated string.
*/
-const char *cumanascsi_2_info(struct Scsi_Host *host)
+static const char *cumanascsi_2_info(struct Scsi_Host *host)
{
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
static char string[150];
@@ -329,58 +331,35 @@ cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
cumanascsi_2_terminator_ctl(host, 0);
else
ret = -EINVAL;
- } else
+ } else {
ret = -EINVAL;
- } else
+ }
+ } else {
ret = -EINVAL;
+ }
return ret;
}
-/* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset,
- * int length, int host_no, int inout)
- * Purpose : Return information about the driver to a user process accessing
- * the /proc filesystem.
- * Params : buffer - a buffer to write information to
- * start - a pointer into this buffer set by this routine to the start
- * of the required information.
- * offset - offset into information that we have read up to.
- * length - length of buffer
- * host_no - host number to return information for
- * inout - 0 for reading, 1 for writing.
- * Returns : length of data written to buffer.
- */
-int cumanascsi_2_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t offset,
- int length, int inout)
+static int cumanascsi_2_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct cumanascsi2_info *info;
- char *p = buffer;
- int pos;
-
- if (inout == 1)
- return cumanascsi_2_set_proc_info(host, buffer, length);
-
info = (struct cumanascsi2_info *)host->hostdata;
- p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION);
- p += fas216_print_host(&info->info, p);
- p += sprintf(p, "Term : o%s\n",
+ seq_printf(m, "Cumana SCSI II driver v%s\n", VERSION);
+ fas216_print_host(&info->info, m);
+ seq_printf(m, "Term : o%s\n",
info->terms ? "n" : "ff");
- p += fas216_print_stats(&info->info, p);
- p += fas216_print_devices(&info->info, p);
-
- *start = buffer + offset;
- pos = p - buffer - offset;
- if (pos > length)
- pos = length;
-
- return pos;
+ fas216_print_stats(&info->info, m);
+ fas216_print_devices(&info->info, m);
+ return 0;
}
-static struct scsi_host_template cumanascsi2_template = {
+static const struct scsi_host_template cumanascsi2_template = {
.module = THIS_MODULE,
- .proc_info = cumanascsi_2_proc_info,
+ .show_info = cumanascsi_2_show_info,
+ .write_info = cumanascsi_2_set_proc_info,
.name = "Cumana SCSI II",
.info = cumanascsi_2_info,
.queuecommand = fas216_queue_command,
@@ -388,17 +367,16 @@ static struct scsi_host_template cumanascsi2_template = {
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
+ .cmd_size = sizeof(struct fas216_cmd_priv),
.can_queue = 1,
.this_id = 7,
- .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
+ .sg_tablesize = SG_MAX_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
.proc_name = "cumanascsi2",
};
-static int __devinit
-cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id)
+static int cumanascsi2_probe(struct expansion_card *ec,
+ const struct ecard_id *id)
{
struct Scsi_Host *host;
struct cumanascsi2_info *info;
@@ -456,7 +434,7 @@ cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id)
goto out_free;
ret = request_irq(ec->irq, cumanascsi_2_intr,
- IRQF_DISABLED, "cumanascsi2", info);
+ 0, "cumanascsi2", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, ec->irq, ret);
@@ -480,7 +458,7 @@ cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id)
if (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
- free_irq(ec->irq, host);
+ free_irq(ec->irq, info);
out_release:
fas216_release(host);
@@ -495,7 +473,7 @@ cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id)
return ret;
}
-static void __devexit cumanascsi2_remove(struct expansion_card *ec)
+static void cumanascsi2_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
@@ -519,7 +497,7 @@ static const struct ecard_id cumanascsi2_cids[] = {
static struct ecard_driver cumanascsi2_driver = {
.probe = cumanascsi2_probe,
- .remove = __devexit_p(cumanascsi2_remove),
+ .remove = cumanascsi2_remove,
.id_table = cumanascsi2_cids,
.drv = {
.name = "cumanascsi2",
diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c
index edfd12b48c28..99be9da8757f 100644
--- a/drivers/scsi/arm/eesox.c
+++ b/drivers/scsi/arm/eesox.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/scsi/eesox.c
*
* Copyright (C) 1997-2005 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* This driver is based on experimentation. Hence, it may have made
* assumptions about the particular card that I have available, and
* may not be reliable!
@@ -32,16 +29,20 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
+#include <linux/pgtable.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ecard.h>
-#include <asm/pgtable.h>
-#include "../scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include "fas216.h"
-#include "scsi.h"
+#include "arm_scsi.h"
#include <scsi/scsicam.h>
@@ -168,12 +169,13 @@ eesoxscsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,
bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
- if (direction == DMA_OUT)
- map_dir = DMA_TO_DEVICE,
+ if (direction == DMA_OUT) {
+ map_dir = DMA_TO_DEVICE;
dma_dir = DMA_MODE_WRITE;
- else
- map_dir = DMA_FROM_DEVICE,
+ } else {
+ map_dir = DMA_FROM_DEVICE;
dma_dir = DMA_MODE_READ;
+ }
dma_map_sg(dev, info->sg, bufs, map_dir);
@@ -273,7 +275,7 @@ static void eesoxscsi_buffer_out(void *buf, int length, void __iomem *base)
{
const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;
const void __iomem *reg_dmastat = base + EESOX_DMASTAT;
- const void __iomem *reg_dmadata = base + EESOX_DMADATA;
+ void __iomem *reg_dmadata = base + EESOX_DMADATA;
do {
unsigned int status;
@@ -379,7 +381,7 @@ eesoxscsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)
* Params : host - driver host structure to return info for.
* Returns : pointer to a static buffer containing null terminated string.
*/
-const char *eesoxscsi_info(struct Scsi_Host *host)
+static const char *eesoxscsi_info(struct Scsi_Host *host)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
static char string[150];
@@ -422,45 +424,20 @@ eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
return ret;
}
-/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
- * int length, int host_no, int inout)
- * Purpose : Return information about the driver to a user process accessing
- * the /proc filesystem.
- * Params : buffer - a buffer to write information to
- * start - a pointer into this buffer set by this routine to the start
- * of the required information.
- * offset - offset into information that we have read up to.
- * length - length of buffer
- * host_no - host number to return information for
- * inout - 0 for reading, 1 for writing.
- * Returns : length of data written to buffer.
- */
-int eesoxscsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
- int length, int inout)
+static int eesoxscsi_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct eesoxscsi_info *info;
- char *p = buffer;
- int pos;
-
- if (inout == 1)
- return eesoxscsi_set_proc_info(host, buffer, length);
info = (struct eesoxscsi_info *)host->hostdata;
- p += sprintf(p, "EESOX SCSI driver v%s\n", VERSION);
- p += fas216_print_host(&info->info, p);
- p += sprintf(p, "Term : o%s\n",
+ seq_printf(m, "EESOX SCSI driver v%s\n", VERSION);
+ fas216_print_host(&info->info, m);
+ seq_printf(m, "Term : o%s\n",
info->control & EESOX_TERM_ENABLE ? "n" : "ff");
- p += fas216_print_stats(&info->info, p);
- p += fas216_print_devices(&info->info, p);
-
- *start = buffer + offset;
- pos = p - buffer - offset;
- if (pos > length)
- pos = length;
-
- return pos;
+ fas216_print_stats(&info->info, m);
+ fas216_print_devices(&info->info, m);
+ return 0;
}
static ssize_t eesoxscsi_show_term(struct device *dev, struct device_attribute *attr, char *buf)
@@ -496,9 +473,10 @@ static ssize_t eesoxscsi_store_term(struct device *dev, struct device_attribute
static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,
eesoxscsi_show_term, eesoxscsi_store_term);
-static struct scsi_host_template eesox_template = {
+static const struct scsi_host_template eesox_template = {
.module = THIS_MODULE,
- .proc_info = eesoxscsi_proc_info,
+ .show_info = eesoxscsi_show_info,
+ .write_info = eesoxscsi_set_proc_info,
.name = "EESOX SCSI",
.info = eesoxscsi_info,
.queuecommand = fas216_queue_command,
@@ -506,17 +484,15 @@ static struct scsi_host_template eesox_template = {
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
+ .cmd_size = sizeof(struct fas216_cmd_priv),
.can_queue = 1,
.this_id = 7,
- .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
+ .sg_tablesize = SG_MAX_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
.proc_name = "eesox",
};
-static int __devinit
-eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+static int eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct eesoxscsi_info *info;
@@ -601,7 +577,7 @@ eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
if (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
- free_irq(ec->irq, host);
+ free_irq(ec->irq, info);
out_remove:
fas216_remove(host);
@@ -617,7 +593,7 @@ eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
return ret;
}
-static void __devexit eesoxscsi_remove(struct expansion_card *ec)
+static void eesoxscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
@@ -643,7 +619,7 @@ static const struct ecard_id eesoxscsi_cids[] = {
static struct ecard_driver eesoxscsi_driver = {
.probe = eesoxscsi_probe,
- .remove = __devexit_p(eesoxscsi_remove),
+ .remove = eesoxscsi_remove,
.id_table = eesoxscsi_cids,
.drv = {
.name = "eesoxscsi",
diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c
index e85c40b6e19b..b1a749ab18f8 100644
--- a/drivers/scsi/arm/fas216.c
+++ b/drivers/scsi/arm/fas216.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/scsi/fas216.c
*
* Copyright (C) 1997-2003 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
* other sources, including:
* the AMD Am53CF94 data sheet
@@ -50,11 +47,15 @@
#include <asm/irq.h>
#include <asm/ecard.h>
-#include "../scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include "fas216.h"
-#include "scsi.h"
+#include "arm_scsi.h"
/* NOTE: SCSI2 Synchronous transfers *require* DMA according to
* the data sheet. This restriction is crazy, especially when
@@ -80,7 +81,6 @@
* I was thinking that this was a good chip until I found this restriction ;(
*/
#define SCSI2_SYNC
-#undef SCSI2_TAG
#undef DEBUG_CONNECT
#undef DEBUG_MESSAGES
@@ -98,6 +98,7 @@ static int level_mask = LOG_ERROR;
module_param(level_mask, int, 0644);
+#ifndef MODULE
static int __init fas216_log_setup(char *str)
{
char *s;
@@ -138,6 +139,7 @@ static int __init fas216_log_setup(char *str)
}
__setup("fas216_logging=", fas216_log_setup);
+#endif
static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg)
{
@@ -179,6 +181,7 @@ static void print_SCp(struct scsi_pointer *SCp, const char *prefix, const char *
SCp->buffers_residual, suffix);
}
+#ifdef CHECK_STRUCTURE
static void fas216_dumpinfo(FAS216_Info *info)
{
static int used = 0;
@@ -223,7 +226,6 @@ static void fas216_dumpinfo(FAS216_Info *info)
info->internal_done, info->magic_end);
}
-#ifdef CHECK_STRUCTURE
static void __fas216_checkmagic(FAS216_Info *info, const char *func)
{
int corruption = 0;
@@ -308,8 +310,7 @@ static void fas216_log_command(FAS216_Info *info, int level,
fas216_do_log(info, '0' + SCpnt->device->id, fmt, args);
va_end(args);
- printk(" CDB: ");
- __scsi_print_command(SCpnt->cmnd);
+ scsi_print_command(SCpnt);
}
static void
@@ -605,6 +606,7 @@ static void fas216_handlesync(FAS216_Info *info, char *msg)
msgqueue_flush(&info->scsi.msgs);
msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ fallthrough;
case async:
dev->period = info->ifcfg.asyncperiod / 4;
@@ -759,7 +761,7 @@ static void fas216_transfer(FAS216_Info *info)
fas216_log(info, LOG_ERROR, "null buffer passed to "
"fas216_starttransfer");
print_SCp(&info->scsi.SCp, "SCp: ", "\n");
- print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n");
+ print_SCp(arm_scsi_pointer(info->SCpnt), "Cmnd SCp: ", "\n");
return;
}
@@ -917,6 +919,7 @@ static void fas216_disconnect_intr(FAS216_Info *info)
fas216_done(info, DID_ABORT);
break;
}
+ fallthrough;
default: /* huh? */
printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n",
@@ -990,7 +993,7 @@ fas216_reselected_intr(FAS216_Info *info)
info->scsi.disconnectable = 0;
if (info->SCpnt->device->id == target &&
info->SCpnt->device->lun == lun &&
- info->SCpnt->tag == tag) {
+ scsi_cmd_to_rq(info->SCpnt)->tag == tag) {
fas216_log(info, LOG_CONNECT, "reconnected previously executing command");
} else {
queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
@@ -1008,7 +1011,7 @@ fas216_reselected_intr(FAS216_Info *info)
/*
* Restore data pointer from SAVED data pointer
*/
- info->scsi.SCp = info->SCpnt->SCp;
+ info->scsi.SCp = *arm_scsi_pointer(info->SCpnt);
fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]",
info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
@@ -1051,6 +1054,7 @@ fas216_reselected_intr(FAS216_Info *info)
static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen)
{
+ struct scsi_pointer *scsi_pointer;
int i;
switch (message[0]) {
@@ -1075,8 +1079,9 @@ static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int
* as required by the SCSI II standard. These always
* point to the start of their respective areas.
*/
- info->SCpnt->SCp = info->scsi.SCp;
- info->SCpnt->SCp.sent_command = 0;
+ scsi_pointer = arm_scsi_pointer(info->SCpnt);
+ *scsi_pointer = info->scsi.SCp;
+ scsi_pointer->sent_command = 0;
fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER,
"save data pointers: [%p, %X]",
info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
@@ -1089,7 +1094,7 @@ static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int
/*
* Restore current data pointer from SAVED data pointer
*/
- info->scsi.SCp = info->SCpnt->SCp;
+ info->scsi.SCp = *arm_scsi_pointer(info->SCpnt);
fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER,
"restore data pointers: [%p, 0x%x]",
info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
@@ -1375,6 +1380,7 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne
case IS_COMPLETE:
break;
}
+ break;
default:
break;
@@ -1413,6 +1419,8 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne
case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */
case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */
fas216_stoptransfer(info);
+ fallthrough;
+
case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */
case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */
case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */
@@ -1424,6 +1432,8 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne
case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */
case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */
fas216_stoptransfer(info);
+ fallthrough;
+
case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */
case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */
case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */
@@ -1475,7 +1485,7 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne
if (msgqueue_msglength(&info->scsi.msgs) > 1)
fas216_cmd(info, CMD_SETATN);
- /*FALLTHROUGH*/
+ fallthrough;
/*
* Any -> Message Out
@@ -1577,6 +1587,7 @@ static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned
fas216_message(info);
break;
}
+ fallthrough;
default:
fas216_log(info, 0, "internal phase %s for function done?"
@@ -1761,7 +1772,7 @@ static void fas216_start_command(FAS216_Info *info, struct scsi_cmnd *SCpnt)
* claim host busy
*/
info->scsi.phase = PHASE_SELECTION;
- info->scsi.SCp = SCpnt->SCp;
+ info->scsi.SCp = *arm_scsi_pointer(SCpnt);
info->SCpnt = SCpnt;
info->dma.transfer_type = fasdma_none;
@@ -1785,8 +1796,9 @@ static void fas216_start_command(FAS216_Info *info, struct scsi_cmnd *SCpnt)
/*
* add tag message if required
*/
- if (SCpnt->tag)
- msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
+ if (SCpnt->device->simple_tags)
+ msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG,
+ scsi_cmd_to_rq(SCpnt)->tag);
do {
#ifdef SCSI2_SYNC
@@ -1809,19 +1821,8 @@ static void fas216_start_command(FAS216_Info *info, struct scsi_cmnd *SCpnt)
static void fas216_allocate_tag(FAS216_Info *info, struct scsi_cmnd *SCpnt)
{
-#ifdef SCSI2_TAG
- /*
- * tagged queuing - allocate a new tag to this command
- */
- if (SCpnt->device->simple_tags && SCpnt->cmnd[0] != REQUEST_SENSE &&
- SCpnt->cmnd[0] != INQUIRY) {
- SCpnt->device->current_tag += 1;
- if (SCpnt->device->current_tag == 0)
- SCpnt->device->current_tag = 1;
- SCpnt->tag = SCpnt->device->current_tag;
- } else
-#endif
- set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns);
+ set_bit(SCpnt->device->id * 8 +
+ (u8)(SCpnt->device->lun & 0x7), info->busyluns);
info->stats.removes += 1;
switch (SCpnt->cmnd[0]) {
@@ -1850,7 +1851,7 @@ static void fas216_do_bus_device_reset(FAS216_Info *info,
* claim host busy
*/
info->scsi.phase = PHASE_SELECTION;
- info->scsi.SCp = SCpnt->SCp;
+ info->scsi.SCp = *arm_scsi_pointer(SCpnt);
info->SCpnt = SCpnt;
info->dma.transfer_type = fasdma_none;
@@ -1958,6 +1959,7 @@ static void fas216_kick(FAS216_Info *info)
switch (where_from) {
case TYPE_QUEUE:
fas216_allocate_tag(info, SCpnt);
+ fallthrough;
case TYPE_OTHER:
fas216_start_command(info, SCpnt);
break;
@@ -1999,17 +2001,19 @@ static void fas216_devicereset_done(FAS216_Info *info, struct scsi_cmnd *SCpnt,
static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt,
unsigned int result)
{
+ struct scsi_pointer *scsi_pointer = arm_scsi_pointer(SCpnt);
+
fas216_log_target(info, LOG_CONNECT, SCpnt->device->id,
"request sense complete, result=0x%04x%02x%02x",
- result, SCpnt->SCp.Message, SCpnt->SCp.Status);
+ result, scsi_pointer->Message, scsi_pointer->Status);
- if (result != DID_OK || SCpnt->SCp.Status != GOOD)
+ if (result != DID_OK || scsi_pointer->Status != SAM_STAT_GOOD)
/*
* Something went wrong. Make sure that we don't
* have valid data in the sense buffer that could
* confuse the higher levels.
*/
- memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
+ memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id);
//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); }
/*
@@ -2019,7 +2023,7 @@ static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt,
* correctly by fas216_std_done.
*/
scsi_eh_restore_cmnd(SCpnt, &info->ses);
- SCpnt->scsi_done(SCpnt);
+ fas216_cmd_priv(SCpnt)->scsi_done(SCpnt);
}
/**
@@ -2033,10 +2037,14 @@ static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt,
static void
fas216_std_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, unsigned int result)
{
+ struct scsi_pointer *scsi_pointer = arm_scsi_pointer(SCpnt);
+
info->stats.fins += 1;
- SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
- info->scsi.SCp.Status;
+ set_host_byte(SCpnt, result);
+ if (result == DID_OK)
+ scsi_msg_to_host_byte(SCpnt, info->scsi.SCp.Message);
+ set_status_byte(SCpnt, info->scsi.SCp.Status);
fas216_log_command(info, LOG_CONNECT, SCpnt,
"command complete, result=0x%08x", SCpnt->result);
@@ -2044,23 +2052,22 @@ fas216_std_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, unsigned int result)
/*
* If the driver detected an error, we're all done.
*/
- if (host_byte(SCpnt->result) != DID_OK ||
- msg_byte(SCpnt->result) != COMMAND_COMPLETE)
+ if (get_host_byte(SCpnt) != DID_OK)
goto done;
/*
* If the command returned CHECK_CONDITION or COMMAND_TERMINATED
* status, request the sense information.
*/
- if (status_byte(SCpnt->result) == CHECK_CONDITION ||
- status_byte(SCpnt->result) == COMMAND_TERMINATED)
+ if (get_status_byte(SCpnt) == SAM_STAT_CHECK_CONDITION ||
+ get_status_byte(SCpnt) == SAM_STAT_COMMAND_TERMINATED)
goto request_sense;
/*
* If the command did not complete with GOOD status,
* we are all done here.
*/
- if (status_byte(SCpnt->result) != GOOD)
+ if (get_status_byte(SCpnt) != SAM_STAT_GOOD)
goto done;
/*
@@ -2078,21 +2085,19 @@ fas216_std_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, unsigned int result)
break;
default:
- printk(KERN_ERR "scsi%d.%c: incomplete data transfer "
- "detected: res=%08X ptr=%p len=%X CDB: ",
- info->host->host_no, '0' + SCpnt->device->id,
- SCpnt->result, info->scsi.SCp.ptr,
- info->scsi.SCp.this_residual);
- __scsi_print_command(SCpnt->cmnd);
- SCpnt->result &= ~(255 << 16);
- SCpnt->result |= DID_BAD_TARGET << 16;
+ scmd_printk(KERN_ERR, SCpnt,
+ "incomplete data transfer detected: res=%08X ptr=%p len=%X\n",
+ SCpnt->result, info->scsi.SCp.ptr,
+ info->scsi.SCp.this_residual);
+ scsi_print_command(SCpnt);
+ set_host_byte(SCpnt, DID_ERROR);
goto request_sense;
}
}
done:
- if (SCpnt->scsi_done) {
- SCpnt->scsi_done(SCpnt);
+ if (fas216_cmd_priv(SCpnt)->scsi_done) {
+ fas216_cmd_priv(SCpnt)->scsi_done(SCpnt);
return;
}
@@ -2108,9 +2113,8 @@ request_sense:
fas216_log_target(info, LOG_CONNECT, SCpnt->device->id,
"requesting sense");
init_SCp(SCpnt);
- SCpnt->SCp.Message = 0;
- SCpnt->SCp.Status = 0;
- SCpnt->tag = 0;
+ scsi_pointer->Message = 0;
+ scsi_pointer->Status = 0;
SCpnt->host_scribble = (void *)fas216_rq_sns_done;
/*
@@ -2157,12 +2161,11 @@ static void fas216_done(FAS216_Info *info, unsigned int result)
* to transfer, we should not have a valid pointer.
*/
if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) {
- printk("scsi%d.%c: zero bytes left to transfer, but "
- "buffer pointer still valid: ptr=%p len=%08x CDB: ",
- info->host->host_no, '0' + SCpnt->device->id,
- info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ scmd_printk(KERN_INFO, SCpnt,
+ "zero bytes left to transfer, but buffer pointer still valid: ptr=%p len=%08x\n",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
info->scsi.SCp.ptr = NULL;
- __scsi_print_command(SCpnt->cmnd);
+ scsi_print_command(SCpnt);
}
/*
@@ -2171,12 +2174,13 @@ static void fas216_done(FAS216_Info *info, unsigned int result)
* status.
*/
info->device[SCpnt->device->id].parity_check = 0;
- clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns);
+ clear_bit(SCpnt->device->id * 8 +
+ (u8)(SCpnt->device->lun & 0x7), info->busyluns);
fn = (void (*)(FAS216_Info *, struct scsi_cmnd *, unsigned int))SCpnt->host_scribble;
fn(info, SCpnt, result);
- if (info->scsi.irq != NO_IRQ) {
+ if (info->scsi.irq) {
spin_lock_irqsave(&info->host_lock, flags);
if (info->scsi.phase == PHASE_IDLE)
fas216_kick(info);
@@ -2190,7 +2194,7 @@ no_command:
}
/**
- * fas216_queue_command - queue a command for adapter to process.
+ * fas216_queue_command_internal - queue a command for the adapter to process
* @SCpnt: Command to queue
* @done: done function to call once command is complete
*
@@ -2198,8 +2202,8 @@ no_command:
* Returns: 0 on success, else error.
* Notes: io_request_lock is held, interrupts are disabled.
*/
-static int fas216_queue_command_lck(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
+static int fas216_queue_command_internal(struct scsi_cmnd *SCpnt,
+ void (*done)(struct scsi_cmnd *))
{
FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
int result;
@@ -2209,14 +2213,13 @@ static int fas216_queue_command_lck(struct scsi_cmnd *SCpnt,
fas216_log_command(info, LOG_CONNECT, SCpnt,
"received command (%p)", SCpnt);
- SCpnt->scsi_done = done;
+ fas216_cmd_priv(SCpnt)->scsi_done = done;
SCpnt->host_scribble = (void *)fas216_std_done;
SCpnt->result = 0;
init_SCp(SCpnt);
info->stats.queues += 1;
- SCpnt->tag = 0;
spin_lock(&info->host_lock);
@@ -2240,6 +2243,11 @@ static int fas216_queue_command_lck(struct scsi_cmnd *SCpnt,
return result;
}
+static int fas216_queue_command_lck(struct scsi_cmnd *SCpnt)
+{
+ return fas216_queue_command_internal(SCpnt, scsi_done);
+}
+
DEF_SCSI_QCMD(fas216_queue_command)
/**
@@ -2265,8 +2273,7 @@ static void fas216_internal_done(struct scsi_cmnd *SCpnt)
* Returns: scsi result code.
* Notes: io_request_lock is held, interrupts are disabled.
*/
-static int fas216_noqueue_command_lck(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
+static int fas216_noqueue_command_lck(struct scsi_cmnd *SCpnt)
{
FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
@@ -2276,10 +2283,10 @@ static int fas216_noqueue_command_lck(struct scsi_cmnd *SCpnt,
* We should only be using this if we don't have an interrupt.
* Provide some "incentive" to use the queueing code.
*/
- BUG_ON(info->scsi.irq != NO_IRQ);
+ BUG_ON(info->scsi.irq);
info->internal_done = 0;
- fas216_queue_command_lck(SCpnt, fas216_internal_done);
+ fas216_queue_command_internal(SCpnt, fas216_internal_done);
/*
* This wastes time, since we can't return until the command is
@@ -2307,7 +2314,7 @@ static int fas216_noqueue_command_lck(struct scsi_cmnd *SCpnt,
spin_lock_irq(info->host->host_lock);
- done(SCpnt);
+ scsi_done(SCpnt);
return 0;
}
@@ -2318,13 +2325,13 @@ DEF_SCSI_QCMD(fas216_noqueue_command)
* Error handler timeout function. Indicate that we timed out,
* and wake up any error handler process so it can continue.
*/
-static void fas216_eh_timer(unsigned long data)
+static void fas216_eh_timer(struct timer_list *t)
{
- FAS216_Info *info = (FAS216_Info *)data;
+ FAS216_Info *info = timer_container_of(info, t, eh_timer);
fas216_log(info, LOG_ERROR, "error handling timed out\n");
- del_timer(&info->eh_timer);
+ timer_delete(&info->eh_timer);
if (info->rst_bus_status == 0)
info->rst_bus_status = -1;
@@ -2398,7 +2405,8 @@ static enum res_find fas216_find_command(FAS216_Info *info,
* been set.
*/
info->origSCpnt = NULL;
- clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns);
+ clear_bit(SCpnt->device->id * 8 +
+ (u8)(SCpnt->device->lun & 0x7), info->busyluns);
printk("waiting for execution ");
res = res_success;
} else
@@ -2424,14 +2432,11 @@ int fas216_eh_abort(struct scsi_cmnd *SCpnt)
info->stats.aborts += 1;
- printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no);
- __scsi_print_command(SCpnt->cmnd);
+ scmd_printk(KERN_WARNING, SCpnt, "abort command\n");
print_debug_list();
fas216_dumpstate(info);
- printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt);
-
switch (fas216_find_command(info, SCpnt)) {
/*
* We found the command, and cleared it out. Either
@@ -2439,7 +2444,7 @@ int fas216_eh_abort(struct scsi_cmnd *SCpnt)
* target, or the busylun bit is not set.
*/
case res_success:
- printk("success\n");
+ scmd_printk(KERN_WARNING, SCpnt, "abort %p success\n", SCpnt);
result = SUCCESS;
break;
@@ -2449,14 +2454,13 @@ int fas216_eh_abort(struct scsi_cmnd *SCpnt)
* if the bus is free.
*/
case res_hw_abort:
-
/*
* We are unable to abort the command for some reason.
*/
default:
case res_failed:
- printk("failed\n");
+ scmd_printk(KERN_WARNING, SCpnt, "abort %p failed\n", SCpnt);
break;
}
@@ -2528,7 +2532,7 @@ int fas216_eh_device_reset(struct scsi_cmnd *SCpnt)
*/
wait_event(info->eh_wait, info->rst_dev_status);
- del_timer_sync(&info->eh_timer);
+ timer_delete_sync(&info->eh_timer);
spin_lock_irqsave(&info->host_lock, flags);
info->rstSCpnt = NULL;
@@ -2618,7 +2622,7 @@ int fas216_eh_bus_reset(struct scsi_cmnd *SCpnt)
* Wait one second for the interrupt.
*/
wait_event(info->eh_wait, info->rst_bus_status);
- del_timer_sync(&info->eh_timer);
+ timer_delete_sync(&info->eh_timer);
fas216_log(info, LOG_ERROR, "bus reset complete: %s\n",
info->rst_bus_status == 1 ? "success" : "failed");
@@ -2661,8 +2665,7 @@ int fas216_eh_host_reset(struct scsi_cmnd *SCpnt)
fas216_checkmagic(info);
- printk("scsi%d.%c: %s: resetting host\n",
- info->host->host_no, '0' + SCpnt->device->id, __func__);
+ fas216_log(info, LOG_ERROR, "resetting host");
/*
* Reset the SCSI chip.
@@ -2853,9 +2856,7 @@ int fas216_init(struct Scsi_Host *host)
info->rst_dev_status = -1;
info->rst_bus_status = -1;
init_waitqueue_head(&info->eh_wait);
- init_timer(&info->eh_timer);
- info->eh_timer.data = (unsigned long)info;
- info->eh_timer.function = fas216_eh_timer;
+ timer_setup(&info->eh_timer, fas216_eh_timer, 0);
spin_lock_init(&info->host_lock);
@@ -2958,9 +2959,9 @@ void fas216_release(struct Scsi_Host *host)
queue_free(&info->queues.issue);
}
-int fas216_print_host(FAS216_Info *info, char *buffer)
+void fas216_print_host(FAS216_Info *info, struct seq_file *m)
{
- return sprintf(buffer,
+ seq_printf(m,
"\n"
"Chip : %s\n"
" Address: 0x%p\n"
@@ -2970,11 +2971,9 @@ int fas216_print_host(FAS216_Info *info, char *buffer)
info->scsi.irq, info->scsi.dma);
}
-int fas216_print_stats(FAS216_Info *info, char *buffer)
+void fas216_print_stats(FAS216_Info *info, struct seq_file *m)
{
- char *p = buffer;
-
- p += sprintf(p, "\n"
+ seq_printf(m, "\n"
"Command Statistics:\n"
" Queued : %u\n"
" Issued : %u\n"
@@ -2991,38 +2990,32 @@ int fas216_print_stats(FAS216_Info *info, char *buffer)
info->stats.writes, info->stats.miscs,
info->stats.disconnects, info->stats.aborts,
info->stats.bus_resets, info->stats.host_resets);
-
- return p - buffer;
}
-int fas216_print_devices(FAS216_Info *info, char *buffer)
+void fas216_print_devices(FAS216_Info *info, struct seq_file *m)
{
struct fas216_device *dev;
struct scsi_device *scd;
- char *p = buffer;
- p += sprintf(p, "Device/Lun TaggedQ Parity Sync\n");
+ seq_puts(m, "Device/Lun TaggedQ Parity Sync\n");
shost_for_each_device(scd, info->host) {
dev = &info->device[scd->id];
- p += sprintf(p, " %d/%d ", scd->id, scd->lun);
+ seq_printf(m, " %d/%llu ", scd->id, scd->lun);
if (scd->tagged_supported)
- p += sprintf(p, "%3sabled(%3d) ",
- scd->simple_tags ? "en" : "dis",
- scd->current_tag);
+ seq_printf(m, "%3sabled ",
+ scd->simple_tags ? "en" : "dis");
else
- p += sprintf(p, "unsupported ");
+ seq_puts(m, "unsupported ");
- p += sprintf(p, "%3sabled ", dev->parity_enabled ? "en" : "dis");
+ seq_printf(m, "%3sabled ", dev->parity_enabled ? "en" : "dis");
if (dev->sof)
- p += sprintf(p, "offset %d, %d ns\n",
+ seq_printf(m, "offset %d, %d ns\n",
dev->sof, dev->period * 4);
else
- p += sprintf(p, "async\n");
+ seq_puts(m, "async\n");
}
-
- return p - buffer;
}
EXPORT_SYMBOL(fas216_init);
diff --git a/drivers/scsi/arm/fas216.h b/drivers/scsi/arm/fas216.h
index 84b7127c0121..08113277a2a9 100644
--- a/drivers/scsi/arm/fas216.h
+++ b/drivers/scsi/arm/fas216.h
@@ -1,21 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/drivers/acorn/scsi/fas216.h
*
* Copyright (C) 1997-2000 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* FAS216 generic driver
*/
#ifndef FAS216_H
#define FAS216_H
-#ifndef NO_IRQ
-#define NO_IRQ 255
-#endif
-
#include <scsi/scsi_eh.h>
#include "queue.h"
@@ -317,6 +310,20 @@ typedef struct {
unsigned long magic_end;
} FAS216_Info;
+/* driver-private data per SCSI command. */
+struct fas216_cmd_priv {
+ /*
+ * @scsi_pointer must be the first member. See also arm_scsi_pointer().
+ */
+ struct scsi_pointer scsi_pointer;
+ void (*scsi_done)(struct scsi_cmnd *cmd);
+};
+
+static inline struct fas216_cmd_priv *fas216_cmd_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
/* Function: int fas216_init (struct Scsi_Host *instance)
* Purpose : initialise FAS/NCR/AMD SCSI structures.
* Params : instance - a driver-specific filled-out structure
@@ -362,9 +369,9 @@ extern void fas216_remove (struct Scsi_Host *instance);
*/
extern void fas216_release (struct Scsi_Host *instance);
-extern int fas216_print_host(FAS216_Info *info, char *buffer);
-extern int fas216_print_stats(FAS216_Info *info, char *buffer);
-extern int fas216_print_devices(FAS216_Info *info, char *buffer);
+extern void fas216_print_host(FAS216_Info *info, struct seq_file *m);
+extern void fas216_print_stats(FAS216_Info *info, struct seq_file *m);
+extern void fas216_print_devices(FAS216_Info *info, struct seq_file *m);
/* Function: int fas216_eh_abort(struct scsi_cmnd *SCpnt)
* Purpose : abort this command
diff --git a/drivers/scsi/arm/msgqueue.c b/drivers/scsi/arm/msgqueue.c
index 7c95c7582b29..58115831362f 100644
--- a/drivers/scsi/arm/msgqueue.c
+++ b/drivers/scsi/arm/msgqueue.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/scsi/msgqueue.c
*
* Copyright (C) 1997-1998 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* message queue handling
*/
#include <linux/module.h>
diff --git a/drivers/scsi/arm/msgqueue.h b/drivers/scsi/arm/msgqueue.h
index 41c7333df3e3..4bcc400f556b 100644
--- a/drivers/scsi/arm/msgqueue.h
+++ b/drivers/scsi/arm/msgqueue.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/drivers/acorn/scsi/msgqueue.h
*
* Copyright (C) 1997 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* message queue handling
*/
#ifndef MSGQUEUE_H
diff --git a/drivers/scsi/arm/oak.c b/drivers/scsi/arm/oak.c
index 849cdf89f7bb..d69245007096 100644
--- a/drivers/scsi/arm/oak.c
+++ b/drivers/scsi/arm/oak.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Oak Generic NCR5380 driver
*
@@ -5,72 +6,58 @@
*/
#include <linux/module.h>
-#include <linux/signal.h>
#include <linux/ioport.h>
-#include <linux/delay.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <asm/ecard.h>
#include <asm/io.h>
-#include <asm/system.h>
-#include "../scsi.h"
#include <scsi/scsi_host.h>
-#define AUTOSENSE
-/*#define PSEUDO_DMA*/
+#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
-#define OAKSCSI_PUBLIC_RELEASE 1
+#define NCR5380_read(reg) readb(hostdata->io + ((reg) << 2))
+#define NCR5380_write(reg, value) writeb(value, hostdata->io + ((reg) << 2))
-#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
-#define NCR5380_local_declare() void __iomem *_base
-#define NCR5380_setup(host) _base = priv(host)->base
+#define NCR5380_dma_xfer_len NCR5380_dma_xfer_none
+#define NCR5380_dma_recv_setup oakscsi_pread
+#define NCR5380_dma_send_setup oakscsi_pwrite
+#define NCR5380_dma_residual NCR5380_dma_residual_none
-#define NCR5380_read(reg) readb(_base + ((reg) << 2))
-#define NCR5380_write(reg, value) writeb(value, _base + ((reg) << 2))
-#define NCR5380_intr oakscsi_intr
#define NCR5380_queue_command oakscsi_queue_command
-#define NCR5380_proc_info oakscsi_proc_info
-
-#define NCR5380_implementation_fields \
- void __iomem *base
+#define NCR5380_info oakscsi_info
-#define BOARD_NORMAL 0
-#define BOARD_NCR53C400 1
+#define NCR5380_implementation_fields /* none */
#include "../NCR5380.h"
#undef START_DMA_INITIATOR_RECEIVE_REG
#define START_DMA_INITIATOR_RECEIVE_REG (128 + 7)
-const char * oakscsi_info (struct Scsi_Host *spnt)
-{
- return "";
-}
-
#define STAT ((128 + 16) << 2)
#define DATA ((128 + 8) << 2)
-static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
- int len)
+static inline int oakscsi_pwrite(struct NCR5380_hostdata *hostdata,
+ unsigned char *addr, int len)
{
- void __iomem *base = priv(instance)->base;
+ u8 __iomem *base = hostdata->io;
printk("writing %p len %d\n",addr, len);
- if(!len) return -1;
while(1)
{
int status;
while (((status = readw(base + STAT)) & 0x100)==0);
}
+ return 0;
}
-static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
- int len)
+static inline int oakscsi_pread(struct NCR5380_hostdata *hostdata,
+ unsigned char *addr, int len)
{
- void __iomem *base = priv(instance)->base;
+ u8 __iomem *base = hostdata->io;
+
printk("reading %p len %d\n", addr, len);
while(len > 0)
{
@@ -85,7 +72,7 @@ printk("reading %p len %d\n", addr, len);
if(status & 0x200 || !timeout)
{
printk("status = %08X\n", status);
- return 1;
+ return -1;
}
}
@@ -113,27 +100,27 @@ printk("reading %p len %d\n", addr, len);
#include "../NCR5380.c"
-static struct scsi_host_template oakscsi_template = {
+static const struct scsi_host_template oakscsi_template = {
.module = THIS_MODULE,
- .proc_info = oakscsi_proc_info,
.name = "Oak 16-bit SCSI",
.info = oakscsi_info,
.queuecommand = oakscsi_queue_command,
.eh_abort_handler = NCR5380_abort,
- .eh_bus_reset_handler = NCR5380_bus_reset,
+ .eh_host_reset_handler = NCR5380_host_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
.proc_name = "oakscsi",
+ .cmd_size = sizeof(struct NCR5380_cmd),
+ .max_sectors = 128,
};
-static int __devinit
-oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+static int oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
- int ret = -ENOMEM;
+ int ret;
ret = ecard_request_resources(ec);
if (ret)
@@ -145,35 +132,32 @@ oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
goto release;
}
- priv(host)->base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
- ecard_resource_len(ec, ECARD_RES_MEMC));
- if (!priv(host)->base) {
+ priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
+ ecard_resource_len(ec, ECARD_RES_MEMC));
+ if (!priv(host)->io) {
ret = -ENOMEM;
goto unreg;
}
- host->irq = IRQ_NONE;
- host->n_io_port = 255;
+ host->irq = NO_IRQ;
- NCR5380_init(host, 0);
+ ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP);
+ if (ret)
+ goto out_unmap;
- printk("scsi%d: at port 0x%08lx irqs disabled",
- host->host_no, host->io_port);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE);
- printk("\nscsi%d:", host->host_no);
- NCR5380_print_options(host);
- printk("\n");
+ NCR5380_maybe_reset_bus(host);
ret = scsi_add_host(host, &ec->dev);
if (ret)
- goto out_unmap;
+ goto out_exit;
scsi_scan_host(host);
goto out;
+ out_exit:
+ NCR5380_exit(host);
out_unmap:
- iounmap(priv(host)->base);
+ iounmap(priv(host)->io);
unreg:
scsi_host_put(host);
release:
@@ -182,16 +166,17 @@ oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
return ret;
}
-static void __devexit oakscsi_remove(struct expansion_card *ec)
+static void oakscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
+ void __iomem *base = priv(host)->io;
ecard_set_drvdata(ec, NULL);
scsi_remove_host(host);
NCR5380_exit(host);
- iounmap(priv(host)->base);
scsi_host_put(host);
+ iounmap(base);
ecard_release_resources(ec);
}
@@ -202,7 +187,7 @@ static const struct ecard_id oakscsi_cids[] = {
static struct ecard_driver oakscsi_driver = {
.probe = oakscsi_probe,
- .remove = __devexit_p(oakscsi_remove),
+ .remove = oakscsi_remove,
.id_table = oakscsi_cids,
.drv = {
.name = "oakscsi",
diff --git a/drivers/scsi/arm/powertec.c b/drivers/scsi/arm/powertec.c
index 9274c0677b9c..823c65ff6c12 100644
--- a/drivers/scsi/arm/powertec.c
+++ b/drivers/scsi/arm/powertec.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/scsi/powertec.c
*
* Copyright (C) 1997-2005 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/blkdev.h>
@@ -17,16 +14,20 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
+#include <linux/pgtable.h>
#include <asm/dma.h>
#include <asm/ecard.h>
#include <asm/io.h>
-#include <asm/pgtable.h>
-#include "../scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include "fas216.h"
-#include "scsi.h"
+#include "arm_scsi.h"
#include <scsi/scsicam.h>
@@ -141,12 +142,13 @@ powertecscsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp,
bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
- if (direction == DMA_OUT)
- map_dir = DMA_TO_DEVICE,
+ if (direction == DMA_OUT) {
+ map_dir = DMA_TO_DEVICE;
dma_dir = DMA_MODE_WRITE;
- else
- map_dir = DMA_FROM_DEVICE,
+ } else {
+ map_dir = DMA_FROM_DEVICE;
dma_dir = DMA_MODE_READ;
+ }
dma_map_sg(dev, info->sg, bufs, map_dir);
@@ -182,7 +184,7 @@ powertecscsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp)
* Params : host - driver host structure to return info for.
* Returns : pointer to a static buffer containing null terminated string.
*/
-const char *powertecscsi_info(struct Scsi_Host *host)
+static const char *powertecscsi_info(struct Scsi_Host *host)
{
struct powertec_info *info = (struct powertec_info *)host->hostdata;
static char string[150];
@@ -237,32 +239,20 @@ powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
* inout - 0 for reading, 1 for writing.
* Returns : length of data written to buffer.
*/
-int powertecscsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
- int length, int inout)
+static int powertecscsi_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct powertec_info *info;
- char *p = buffer;
- int pos;
-
- if (inout == 1)
- return powertecscsi_set_proc_info(host, buffer, length);
info = (struct powertec_info *)host->hostdata;
- p += sprintf(p, "PowerTec SCSI driver v%s\n", VERSION);
- p += fas216_print_host(&info->info, p);
- p += sprintf(p, "Term : o%s\n",
+ seq_printf(m, "PowerTec SCSI driver v%s\n", VERSION);
+ fas216_print_host(&info->info, m);
+ seq_printf(m, "Term : o%s\n",
info->term_ctl ? "n" : "ff");
- p += fas216_print_stats(&info->info, p);
- p += fas216_print_devices(&info->info, p);
-
- *start = buffer + offset;
- pos = p - buffer - offset;
- if (pos > length)
- pos = length;
-
- return pos;
+ fas216_print_stats(&info->info, m);
+ fas216_print_devices(&info->info, m);
+ return 0;
}
static ssize_t powertecscsi_show_term(struct device *dev, struct device_attribute *attr, char *buf)
@@ -289,9 +279,10 @@ powertecscsi_store_term(struct device *dev, struct device_attribute *attr, const
static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,
powertecscsi_show_term, powertecscsi_store_term);
-static struct scsi_host_template powertecscsi_template = {
+static const struct scsi_host_template powertecscsi_template = {
.module = THIS_MODULE,
- .proc_info = powertecscsi_proc_info,
+ .show_info = powertecscsi_show_info,
+ .write_info = powertecscsi_set_proc_info,
.name = "PowerTec SCSI",
.info = powertecscsi_info,
.queuecommand = fas216_queue_command,
@@ -299,18 +290,17 @@ static struct scsi_host_template powertecscsi_template = {
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
-
+ .cmd_size = sizeof(struct fas216_cmd_priv),
.can_queue = 8,
.this_id = 7,
- .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
+ .sg_tablesize = SG_MAX_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
.cmd_per_lun = 2,
- .use_clustering = ENABLE_CLUSTERING,
.proc_name = "powertec",
};
-static int __devinit
-powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+static int powertecscsi_probe(struct expansion_card *ec,
+ const struct ecard_id *id)
{
struct Scsi_Host *host;
struct powertec_info *info;
@@ -369,7 +359,7 @@ powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
goto out_free;
ret = request_irq(ec->irq, powertecscsi_intr,
- IRQF_DISABLED, "powertec", info);
+ 0, "powertec", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, ec->irq, ret);
@@ -393,7 +383,7 @@ powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
if (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
- free_irq(ec->irq, host);
+ free_irq(ec->irq, info);
out_release:
fas216_release(host);
@@ -409,7 +399,7 @@ powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
return ret;
}
-static void __devexit powertecscsi_remove(struct expansion_card *ec)
+static void powertecscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct powertec_info *info = (struct powertec_info *)host->hostdata;
@@ -435,7 +425,7 @@ static const struct ecard_id powertecscsi_cids[] = {
static struct ecard_driver powertecscsi_driver = {
.probe = powertecscsi_probe,
- .remove = __devexit_p(powertecscsi_remove),
+ .remove = powertecscsi_remove,
.id_table = powertecscsi_cids,
.drv = {
.name = "powertecscsi",
diff --git a/drivers/scsi/arm/queue.c b/drivers/scsi/arm/queue.c
index cb11ccef54e5..978df23ce188 100644
--- a/drivers/scsi/arm/queue.c
+++ b/drivers/scsi/arm/queue.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/scsi/queue.c: queue handling primitives
*
* Copyright (C) 1997-2000 Russell King
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Changelog:
* 15-Sep-1997 RMK Created.
* 11-Oct-1997 RMK Corrected problem with queue_remove_exclude
@@ -23,7 +20,11 @@
#include <linux/list.h>
#include <linux/init.h>
-#include "../scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
#define DEBUG
@@ -70,7 +71,7 @@ int queue_initialise (Queue_t *queue)
* need to keep free lists or allocate this
* memory.
*/
- queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL);
+ queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);
if (q) {
for (; nqueues; q++, nqueues--) {
SET_MAGIC(q, QUEUE_MAGIC_FREE);
@@ -167,7 +168,8 @@ struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
spin_lock_irqsave(&queue->queue_lock, flags);
list_for_each(l, &queue->head) {
QE_t *q = list_entry(l, QE_t, list);
- if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) {
+ if (!test_bit(q->SCpnt->device->id * 8 +
+ (u8)(q->SCpnt->device->lun & 0x7), exclude)) {
SCpnt = __queue_remove(queue, l);
break;
}
@@ -216,7 +218,7 @@ struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,
list_for_each(l, &queue->head) {
QE_t *q = list_entry(l, QE_t, list);
if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
- q->SCpnt->tag == tag) {
+ scsi_cmd_to_rq(q->SCpnt)->tag == tag) {
SCpnt = __queue_remove(queue, l);
break;
}
diff --git a/drivers/scsi/arm/queue.h b/drivers/scsi/arm/queue.h
index 3c519c9237b2..cb51379dce94 100644
--- a/drivers/scsi/arm/queue.h
+++ b/drivers/scsi/arm/queue.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/drivers/acorn/scsi/queue.h: queue handling
*
* Copyright (C) 1997 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef QUEUE_H
#define QUEUE_H
diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c
deleted file mode 100644
index ea439f93ed81..000000000000
--- a/drivers/scsi/atari_NCR5380.c
+++ /dev/null
@@ -1,2989 +0,0 @@
-/*
- * NCR 5380 generic driver routines. These should make it *trivial*
- * to implement 5380 SCSI drivers under Linux with a non-trantor
- * architecture.
- *
- * Note that these routines also work with NR53c400 family chips.
- *
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 666-5836
- *
- * DISTRIBUTION RELEASE 6.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
- * this file, too:
- *
- * - Some of the debug statements were incorrect (undefined variables and the
- * like). I fixed that.
- *
- * - In information_transfer(), I think a #ifdef was wrong. Looking at the
- * possible DMA transfer size should also happen for REAL_DMA. I added this
- * in the #if statement.
- *
- * - When using real DMA, information_transfer() should return in a DATAOUT
- * phase after starting the DMA. It has nothing more to do.
- *
- * - The interrupt service routine should run main after end of DMA, too (not
- * only after RESELECTION interrupts). Additionally, it should _not_ test
- * for more interrupts after running main, since a DMA process may have
- * been started and interrupts are turned on now. The new int could happen
- * inside the execution of NCR5380_intr(), leading to recursive
- * calls.
- *
- * - I've added a function merge_contiguous_buffers() that tries to
- * merge scatter-gather buffers that are located at contiguous
- * physical addresses and can be processed with the same DMA setup.
- * Since most scatter-gather operations work on a page (4K) of
- * 4 buffers (1K), in more than 90% of all cases three interrupts and
- * DMA setup actions are saved.
- *
- * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
- * and USLEEP, because these were messing up readability and will never be
- * needed for Atari SCSI.
- *
- * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
- * stuff), and 'main' is executed in a bottom half if awoken by an
- * interrupt.
- *
- * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
- * constructs. In my eyes, this made the source rather unreadable, so I
- * finally replaced that by the *_PRINTK() macros.
- *
- */
-
-/*
- * Further development / testing that should be done :
- * 1. Test linked command handling code after Eric is ready with
- * the high level code.
- */
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_transport_spi.h>
-
-#if (NDEBUG & NDEBUG_LISTS)
-#define LIST(x, y) \
- do { \
- printk("LINE:%d Adding %p to %p\n", \
- __LINE__, (void*)(x), (void*)(y)); \
- if ((x) == (y)) \
- udelay(5); \
- } while (0)
-#define REMOVE(w, x, y, z) \
- do { \
- printk("LINE:%d Removing: %p->%p %p->%p \n", \
- __LINE__, (void*)(w), (void*)(x), \
- (void*)(y), (void*)(z)); \
- if ((x) == (y)) \
- udelay(5); \
- } while (0)
-#else
-#define LIST(x,y)
-#define REMOVE(w,x,y,z)
-#endif
-
-#ifndef notyet
-#undef LINKED
-#endif
-
-/*
- * Design
- * Issues :
- *
- * The other Linux SCSI drivers were written when Linux was Intel PC-only,
- * and specifically for each board rather than each chip. This makes their
- * adaptation to platforms like the Mac (Some of which use NCR5380's)
- * more difficult than it has to be.
- *
- * Also, many of the SCSI drivers were written before the command queuing
- * routines were implemented, meaning their implementations of queued
- * commands were hacked on rather than designed in from the start.
- *
- * When I designed the Linux SCSI drivers I figured that
- * while having two different SCSI boards in a system might be useful
- * for debugging things, two of the same type wouldn't be used.
- * Well, I was wrong and a number of users have mailed me about running
- * multiple high-performance SCSI boards in a server.
- *
- * Finally, when I get questions from users, I have no idea what
- * revision of my driver they are running.
- *
- * This driver attempts to address these problems :
- * This is a generic 5380 driver. To use it on a different platform,
- * one simply writes appropriate system specific macros (ie, data
- * transfer - some PC's will use the I/O bus, 68K's must use
- * memory mapped) and drops this file in their 'C' wrapper.
- *
- * As far as command queueing, two queues are maintained for
- * each 5380 in the system - commands that haven't been issued yet,
- * and commands that are currently executing. This means that an
- * unlimited number of commands may be queued, letting
- * more commands propagate from the higher driver levels giving higher
- * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
- * allowing multiple commands to propagate all the way to a SCSI-II device
- * while a command is already executing.
- *
- * To solve the multiple-boards-in-the-same-system problem,
- * there is a separate instance structure for each instance
- * of a 5380 in the system. So, multiple NCR5380 drivers will
- * be able to coexist with appropriate changes to the high level
- * SCSI code.
- *
- * A NCR5380_PUBLIC_REVISION macro is provided, with the release
- * number (updated for each public release) printed by the
- * NCR5380_print_options command, which should be called from the
- * wrapper detect function, so that I know what release of the driver
- * users are using.
- *
- * Issues specific to the NCR5380 :
- *
- * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
- * piece of hardware that requires you to sit in a loop polling for
- * the REQ signal as long as you are connected. Some devices are
- * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
- * while doing long seek operations.
- *
- * The workaround for this is to keep track of devices that have
- * disconnected. If the device hasn't disconnected, for commands that
- * should disconnect, we do something like
- *
- * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
- *
- * Some tweaking of N and M needs to be done. An algorithm based
- * on "time to data" would give the best results as long as short time
- * to datas (ie, on the same track) were considered, however these
- * broken devices are the exception rather than the rule and I'd rather
- * spend my time optimizing for the normal case.
- *
- * Architecture :
- *
- * At the heart of the design is a coroutine, NCR5380_main,
- * which is started when not running by the interrupt handler,
- * timer, and queue command function. It attempts to establish
- * I_T_L or I_T_L_Q nexuses by removing the commands from the
- * issue queue and calling NCR5380_select() if a nexus
- * is not established.
- *
- * Once a nexus is established, the NCR5380_information_transfer()
- * phase goes through the various phases as instructed by the target.
- * if the target goes into MSG IN and sends a DISCONNECT message,
- * the command structure is placed into the per instance disconnected
- * queue, and NCR5380_main tries to find more work. If USLEEP
- * was defined, and the target is idle for too long, the system
- * will try to sleep.
- *
- * If a command has disconnected, eventually an interrupt will trigger,
- * calling NCR5380_intr() which will in turn call NCR5380_reselect
- * to reestablish a nexus. This will run main if necessary.
- *
- * On command termination, the done function will be called as
- * appropriate.
- *
- * SCSI pointers are maintained in the SCp field of SCSI command
- * structures, being initialized after the command is connected
- * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
- * Note that in violation of the standard, an implicit SAVE POINTERS operation
- * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
- */
-
-/*
- * Using this file :
- * This file a skeleton Linux SCSI driver for the NCR 5380 series
- * of chips. To use it, you write an architecture specific functions
- * and macros and include this file in your driver.
- *
- * These macros control options :
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * LINKED - if defined, linked commands are supported.
- *
- * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
- *
- * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
- *
- * These macros MUST be defined :
- *
- * NCR5380_read(register) - read from the specified register
- *
- * NCR5380_write(register, value) - write to the specific register
- *
- * Either real DMA *or* pseudo DMA may be implemented
- * REAL functions :
- * NCR5380_REAL_DMA should be defined if real DMA is to be used.
- * Note that the DMA setup functions should return the number of bytes
- * that they were able to program the controller for.
- *
- * Also note that generic i386/PC versions of these macros are
- * available as NCR5380_i386_dma_write_setup,
- * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
- *
- * NCR5380_dma_write_setup(instance, src, count) - initialize
- * NCR5380_dma_read_setup(instance, dst, count) - initialize
- * NCR5380_dma_residual(instance); - residual count
- *
- * PSEUDO functions :
- * NCR5380_pwrite(instance, src, count)
- * NCR5380_pread(instance, dst, count);
- *
- * If nothing specific to this implementation needs doing (ie, with external
- * hardware), you must also define
- *
- * NCR5380_queue_command
- * NCR5380_reset
- * NCR5380_abort
- * NCR5380_proc_info
- *
- * to be the global entry points into the specific driver, ie
- * #define NCR5380_queue_command t128_queue_command.
- *
- * If this is not done, the routines will be defined as static functions
- * with the NCR5380* names and the user must provide a globally
- * accessible wrapper function.
- *
- * The generic driver is initialized by calling NCR5380_init(instance),
- * after setting the appropriate host specific fields and ID. If the
- * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
- * possible) function may be used. Before the specific driver initialization
- * code finishes, NCR5380_print_options should be called.
- */
-
-static struct Scsi_Host *first_instance = NULL;
-static struct scsi_host_template *the_template = NULL;
-
-/* Macros ease life... :-) */
-#define SETUP_HOSTDATA(in) \
- struct NCR5380_hostdata *hostdata = \
- (struct NCR5380_hostdata *)(in)->hostdata
-#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
-
-#define NEXT(cmd) ((Scsi_Cmnd *)(cmd)->host_scribble)
-#define SET_NEXT(cmd,next) ((cmd)->host_scribble = (void *)(next))
-#define NEXTADDR(cmd) ((Scsi_Cmnd **)&(cmd)->host_scribble)
-
-#define HOSTNO instance->host_no
-#define H_NO(cmd) (cmd)->device->host->host_no
-
-#ifdef SUPPORT_TAGS
-
-/*
- * Functions for handling tagged queuing
- * =====================================
- *
- * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
- *
- * Using consecutive numbers for the tags is no good idea in my eyes. There
- * could be wrong re-usings if the counter (8 bit!) wraps and some early
- * command has been preempted for a long time. My solution: a bitfield for
- * remembering used tags.
- *
- * There's also the problem that each target has a certain queue size, but we
- * cannot know it in advance :-( We just see a QUEUE_FULL status being
- * returned. So, in this case, the driver internal queue size assumption is
- * reduced to the number of active tags if QUEUE_FULL is returned by the
- * target. The command is returned to the mid-level, but with status changed
- * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
- * correctly.
- *
- * We're also not allowed running tagged commands as long as an untagged
- * command is active. And REQUEST SENSE commands after a contingent allegiance
- * condition _must_ be untagged. To keep track whether an untagged command has
- * been issued, the host->busy array is still employed, as it is without
- * support for tagged queuing.
- *
- * One could suspect that there are possible race conditions between
- * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
- * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
- * which already guaranteed to be running at most once. It is also the only
- * place where tags/LUNs are allocated. So no other allocation can slip
- * between that pair, there could only happen a reselection, which can free a
- * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
- * important: the tag bit must be cleared before 'nr_allocated' is decreased.
- */
-
-/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
-#undef TAG_NONE
-#define TAG_NONE 0xff
-
-typedef struct {
- DECLARE_BITMAP(allocated, MAX_TAGS);
- int nr_allocated;
- int queue_size;
-} TAG_ALLOC;
-
-static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
-
-
-static void __init init_tags(void)
-{
- int target, lun;
- TAG_ALLOC *ta;
-
- if (!setup_use_tagged_queuing)
- return;
-
- for (target = 0; target < 8; ++target) {
- for (lun = 0; lun < 8; ++lun) {
- ta = &TagAlloc[target][lun];
- bitmap_zero(ta->allocated, MAX_TAGS);
- ta->nr_allocated = 0;
- /* At the beginning, assume the maximum queue size we could
- * support (MAX_TAGS). This value will be decreased if the target
- * returns QUEUE_FULL status.
- */
- ta->queue_size = MAX_TAGS;
- }
- }
-}
-
-
-/* Check if we can issue a command to this LUN: First see if the LUN is marked
- * busy by an untagged command. If the command should use tagged queuing, also
- * check that there is a free tag and the target's queue won't overflow. This
- * function should be called with interrupts disabled to avoid race
- * conditions.
- */
-
-static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged)
-{
- SETUP_HOSTDATA(cmd->device->host);
-
- if (hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))
- return 1;
- if (!should_be_tagged ||
- !setup_use_tagged_queuing || !cmd->device->tagged_supported)
- return 0;
- if (TagAlloc[cmd->device->id][cmd->device->lun].nr_allocated >=
- TagAlloc[cmd->device->id][cmd->device->lun].queue_size) {
- TAG_PRINTK("scsi%d: target %d lun %d: no free tags\n",
- H_NO(cmd), cmd->device->id, cmd->device->lun);
- return 1;
- }
- return 0;
-}
-
-
-/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
- * must be called before!), or reserve the LUN in 'busy' if the command is
- * untagged.
- */
-
-static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged)
-{
- SETUP_HOSTDATA(cmd->device->host);
-
- /* If we or the target don't support tagged queuing, allocate the LUN for
- * an untagged command.
- */
- if (!should_be_tagged ||
- !setup_use_tagged_queuing || !cmd->device->tagged_supported) {
- cmd->tag = TAG_NONE;
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
- TAG_PRINTK("scsi%d: target %d lun %d now allocated by untagged "
- "command\n", H_NO(cmd), cmd->device->id, cmd->device->lun);
- } else {
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
-
- cmd->tag = find_first_zero_bit(ta->allocated, MAX_TAGS);
- set_bit(cmd->tag, ta->allocated);
- ta->nr_allocated++;
- TAG_PRINTK("scsi%d: using tag %d for target %d lun %d "
- "(now %d tags in use)\n",
- H_NO(cmd), cmd->tag, cmd->device->id,
- cmd->device->lun, ta->nr_allocated);
- }
-}
-
-
-/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
- * unlock the LUN.
- */
-
-static void cmd_free_tag(Scsi_Cmnd *cmd)
-{
- SETUP_HOSTDATA(cmd->device->host);
-
- if (cmd->tag == TAG_NONE) {
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- TAG_PRINTK("scsi%d: target %d lun %d untagged cmd finished\n",
- H_NO(cmd), cmd->device->id, cmd->device->lun);
- } else if (cmd->tag >= MAX_TAGS) {
- printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
- H_NO(cmd), cmd->tag);
- } else {
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
- clear_bit(cmd->tag, ta->allocated);
- ta->nr_allocated--;
- TAG_PRINTK("scsi%d: freed tag %d for target %d lun %d\n",
- H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun);
- }
-}
-
-
-static void free_all_tags(void)
-{
- int target, lun;
- TAG_ALLOC *ta;
-
- if (!setup_use_tagged_queuing)
- return;
-
- for (target = 0; target < 8; ++target) {
- for (lun = 0; lun < 8; ++lun) {
- ta = &TagAlloc[target][lun];
- bitmap_zero(ta->allocated, MAX_TAGS);
- ta->nr_allocated = 0;
- }
- }
-}
-
-#endif /* SUPPORT_TAGS */
-
-
-/*
- * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
- *
- * Purpose: Try to merge several scatter-gather requests into one DMA
- * transfer. This is possible if the scatter buffers lie on
- * physical contiguous addresses.
- *
- * Parameters: Scsi_Cmnd *cmd
- * The command to work on. The first scatter buffer's data are
- * assumed to be already transferred into ptr/this_residual.
- */
-
-static void merge_contiguous_buffers(Scsi_Cmnd *cmd)
-{
- unsigned long endaddr;
-#if (NDEBUG & NDEBUG_MERGING)
- unsigned long oldlen = cmd->SCp.this_residual;
- int cnt = 1;
-#endif
-
- for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
- cmd->SCp.buffers_residual &&
- virt_to_phys(sg_virt(&cmd->SCp.buffer[1])) == endaddr;) {
- MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
- page_address(sg_page(&cmd->SCp.buffer[1])), endaddr);
-#if (NDEBUG & NDEBUG_MERGING)
- ++cnt;
-#endif
- ++cmd->SCp.buffer;
- --cmd->SCp.buffers_residual;
- cmd->SCp.this_residual += cmd->SCp.buffer->length;
- endaddr += cmd->SCp.buffer->length;
- }
-#if (NDEBUG & NDEBUG_MERGING)
- if (oldlen != cmd->SCp.this_residual)
- MER_PRINTK("merged %d buffers from %p, new length %08x\n",
- cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
-#endif
-}
-
-/*
- * Function : void initialize_SCp(Scsi_Cmnd *cmd)
- *
- * Purpose : initialize the saved data pointers for cmd to point to the
- * start of the buffer.
- *
- * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
- */
-
-static inline void initialize_SCp(Scsi_Cmnd *cmd)
-{
- /*
- * Initialize the Scsi Pointer field so that all of the commands in the
- * various queues are valid.
- */
-
- if (scsi_bufflen(cmd)) {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- /* ++roman: Try to merge some scatter-buffers if they are at
- * contiguous physical addresses.
- */
- merge_contiguous_buffers(cmd);
- } else {
- cmd->SCp.buffer = NULL;
- cmd->SCp.buffers_residual = 0;
- cmd->SCp.ptr = NULL;
- cmd->SCp.this_residual = 0;
- }
-}
-
-#include <linux/delay.h>
-
-#if NDEBUG
-static struct {
- unsigned char mask;
- const char *name;
-} signals[] = {
- { SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
- { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
- { SR_SEL, "SEL" }, {0, NULL}
-}, basrs[] = {
- {BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}
-}, icrs[] = {
- {ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
- {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
- {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
- {0, NULL}
-}, mrs[] = {
- {MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
- {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
- "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
- {MR_MONITOR_BSY, "MODE MONITOR BSY"},
- {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
- {0, NULL}
-};
-
-/*
- * Function : void NCR5380_print(struct Scsi_Host *instance)
- *
- * Purpose : print the SCSI bus signals for debugging purposes
- *
- * Input : instance - which NCR5380
- */
-
-static void NCR5380_print(struct Scsi_Host *instance)
-{
- unsigned char status, data, basr, mr, icr, i;
- unsigned long flags;
-
- local_irq_save(flags);
- data = NCR5380_read(CURRENT_SCSI_DATA_REG);
- status = NCR5380_read(STATUS_REG);
- mr = NCR5380_read(MODE_REG);
- icr = NCR5380_read(INITIATOR_COMMAND_REG);
- basr = NCR5380_read(BUS_AND_STATUS_REG);
- local_irq_restore(flags);
- printk("STATUS_REG: %02x ", status);
- for (i = 0; signals[i].mask; ++i)
- if (status & signals[i].mask)
- printk(",%s", signals[i].name);
- printk("\nBASR: %02x ", basr);
- for (i = 0; basrs[i].mask; ++i)
- if (basr & basrs[i].mask)
- printk(",%s", basrs[i].name);
- printk("\nICR: %02x ", icr);
- for (i = 0; icrs[i].mask; ++i)
- if (icr & icrs[i].mask)
- printk(",%s", icrs[i].name);
- printk("\nMODE: %02x ", mr);
- for (i = 0; mrs[i].mask; ++i)
- if (mr & mrs[i].mask)
- printk(",%s", mrs[i].name);
- printk("\n");
-}
-
-static struct {
- unsigned char value;
- const char *name;
-} phases[] = {
- {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
- {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
- {PHASE_UNKNOWN, "UNKNOWN"}
-};
-
-/*
- * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
- *
- * Purpose : print the current SCSI phase for debugging purposes
- *
- * Input : instance - which NCR5380
- */
-
-static void NCR5380_print_phase(struct Scsi_Host *instance)
-{
- unsigned char status;
- int i;
-
- status = NCR5380_read(STATUS_REG);
- if (!(status & SR_REQ))
- printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
- else {
- for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
- (phases[i].value != (status & PHASE_MASK)); ++i)
- ;
- printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
- }
-}
-
-#else /* !NDEBUG */
-
-/* dummies... */
-static inline void NCR5380_print(struct Scsi_Host *instance)
-{
-};
-static inline void NCR5380_print_phase(struct Scsi_Host *instance)
-{
-};
-
-#endif
-
-/*
- * ++roman: New scheme of calling NCR5380_main()
- *
- * If we're not in an interrupt, we can call our main directly, it cannot be
- * already running. Else, we queue it on a task queue, if not 'main_running'
- * tells us that a lower level is already executing it. This way,
- * 'main_running' needs not be protected in a special way.
- *
- * queue_main() is a utility function for putting our main onto the task
- * queue, if main_running is false. It should be called only from a
- * interrupt or bottom half.
- */
-
-#include <linux/gfp.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-
-static volatile int main_running;
-static DECLARE_WORK(NCR5380_tqueue, NCR5380_main);
-
-static inline void queue_main(void)
-{
- if (!main_running) {
- /* If in interrupt and NCR5380_main() not already running,
- queue it on the 'immediate' task queue, to be processed
- immediately after the current interrupt processing has
- finished. */
- schedule_work(&NCR5380_tqueue);
- }
- /* else: nothing to do: the running NCR5380_main() will pick up
- any newly queued command. */
-}
-
-
-static inline void NCR5380_all_init(void)
-{
- static int done = 0;
- if (!done) {
- INI_PRINTK("scsi : NCR5380_all_init()\n");
- done = 1;
- }
-}
-
-
-/*
- * Function : void NCR58380_print_options (struct Scsi_Host *instance)
- *
- * Purpose : called by probe code indicating the NCR5380 driver
- * options that were selected.
- *
- * Inputs : instance, pointer to this instance. Unused.
- */
-
-static void __init NCR5380_print_options(struct Scsi_Host *instance)
-{
- printk(" generic options"
-#ifdef AUTOSENSE
- " AUTOSENSE"
-#endif
-#ifdef REAL_DMA
- " REAL DMA"
-#endif
-#ifdef PARITY
- " PARITY"
-#endif
-#ifdef SUPPORT_TAGS
- " SCSI-2 TAGGED QUEUING"
-#endif
- );
- printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
-}
-
-/*
- * Function : void NCR5380_print_status (struct Scsi_Host *instance)
- *
- * Purpose : print commands in the various queues, called from
- * NCR5380_abort and NCR5380_debug to aid debugging.
- *
- * Inputs : instance, pointer to this instance.
- */
-
-static void NCR5380_print_status(struct Scsi_Host *instance)
-{
- char *pr_bfr;
- char *start;
- int len;
-
- NCR_PRINT(NDEBUG_ANY);
- NCR_PRINT_PHASE(NDEBUG_ANY);
-
- pr_bfr = (char *)__get_free_page(GFP_ATOMIC);
- if (!pr_bfr) {
- printk("NCR5380_print_status: no memory for print buffer\n");
- return;
- }
- len = NCR5380_proc_info(instance, pr_bfr, &start, 0, PAGE_SIZE, 0);
- pr_bfr[len] = 0;
- printk("\n%s\n", pr_bfr);
- free_page((unsigned long)pr_bfr);
-}
-
-
-/******************************************/
-/*
- * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
- *
- * *buffer: I/O buffer
- * **start: if inout == FALSE pointer into buffer where user read should start
- * offset: current offset
- * length: length of buffer
- * hostno: Scsi_Host host_no
- * inout: TRUE - user is writing; FALSE - user is reading
- *
- * Return the number of bytes read from or written
-*/
-
-#undef SPRINTF
-#define SPRINTF(fmt,args...) \
- do { \
- if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
- pos += sprintf(pos, fmt , ## args); \
- } while(0)
-static char *lprint_Scsi_Cmnd(Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
-
-static int NCR5380_proc_info(struct Scsi_Host *instance, char *buffer,
- char **start, off_t offset, int length, int inout)
-{
- char *pos = buffer;
- struct NCR5380_hostdata *hostdata;
- Scsi_Cmnd *ptr;
- unsigned long flags;
- off_t begin = 0;
-#define check_offset() \
- do { \
- if (pos - buffer < offset - begin) { \
- begin += pos - buffer; \
- pos = buffer; \
- } \
- } while (0)
-
- hostdata = (struct NCR5380_hostdata *)instance->hostdata;
-
- if (inout) /* Has data been written to the file ? */
- return -ENOSYS; /* Currently this is a no-op */
- SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
- check_offset();
- local_irq_save(flags);
- SPRINTF("NCR5380: coroutine is%s running.\n",
- main_running ? "" : "n't");
- check_offset();
- if (!hostdata->connected)
- SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
- else
- pos = lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected,
- pos, buffer, length);
- SPRINTF("scsi%d: issue_queue\n", HOSTNO);
- check_offset();
- for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
- pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
- check_offset();
- }
-
- SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
- check_offset();
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
- ptr = NEXT(ptr)) {
- pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
- check_offset();
- }
-
- local_irq_restore(flags);
- *start = buffer + (offset - begin);
- if (pos - buffer < offset - begin)
- return 0;
- else if (pos - buffer - (offset - begin) < length)
- return pos - buffer - (offset - begin);
- return length;
-}
-
-static char *lprint_Scsi_Cmnd(Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
-{
- int i, s;
- unsigned char *command;
- SPRINTF("scsi%d: destination target %d, lun %d\n",
- H_NO(cmd), cmd->device->id, cmd->device->lun);
- SPRINTF(" command = ");
- command = cmd->cmnd;
- SPRINTF("%2d (0x%02x)", command[0], command[0]);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- SPRINTF(" %02x", command[i]);
- SPRINTF("\n");
- return pos;
-}
-
-
-/*
- * Function : void NCR5380_init (struct Scsi_Host *instance)
- *
- * Purpose : initializes *instance and corresponding 5380 chip.
- *
- * Inputs : instance - instantiation of the 5380 driver.
- *
- * Notes : I assume that the host, hostno, and id bits have been
- * set correctly. I don't care about the irq and other fields.
- *
- */
-
-static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
-{
- int i;
- SETUP_HOSTDATA(instance);
-
- NCR5380_all_init();
-
- hostdata->aborted = 0;
- hostdata->id_mask = 1 << instance->this_id;
- hostdata->id_higher_mask = 0;
- for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
- if (i > hostdata->id_mask)
- hostdata->id_higher_mask |= i;
- for (i = 0; i < 8; ++i)
- hostdata->busy[i] = 0;
-#ifdef SUPPORT_TAGS
- init_tags();
-#endif
-#if defined (REAL_DMA)
- hostdata->dma_len = 0;
-#endif
- hostdata->targets_present = 0;
- hostdata->connected = NULL;
- hostdata->issue_queue = NULL;
- hostdata->disconnected_queue = NULL;
- hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
-
- if (!the_template) {
- the_template = instance->hostt;
- first_instance = instance;
- }
-
-#ifndef AUTOSENSE
- if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
- printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
- " without AUTOSENSE option, contingent allegiance conditions may\n"
- " be incorrectly cleared.\n", HOSTNO);
-#endif /* def AUTOSENSE */
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(TARGET_COMMAND_REG, 0);
- NCR5380_write(SELECT_ENABLE_REG, 0);
-
- return 0;
-}
-
-/*
- * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
- * void (*done)(Scsi_Cmnd *))
- *
- * Purpose : enqueues a SCSI command
- *
- * Inputs : cmd - SCSI command, done - function called on completion, with
- * a pointer to the command descriptor.
- *
- * Returns : 0
- *
- * Side effects :
- * cmd is added to the per instance issue_queue, with minor
- * twiddling done to the host specific fields of cmd. If the
- * main coroutine is not running, it is restarted.
- *
- */
-
-static int NCR5380_queue_command_lck(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
-{
- SETUP_HOSTDATA(cmd->device->host);
- Scsi_Cmnd *tmp;
- int oldto;
- unsigned long flags;
-
-#if (NDEBUG & NDEBUG_NO_WRITE)
- switch (cmd->cmnd[0]) {
- case WRITE_6:
- case WRITE_10:
- printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
- H_NO(cmd));
- cmd->result = (DID_ERROR << 16);
- done(cmd);
- return 0;
- }
-#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
-
-#ifdef NCR5380_STATS
-# if 0
- if (!hostdata->connected && !hostdata->issue_queue &&
- !hostdata->disconnected_queue) {
- hostdata->timebase = jiffies;
- }
-# endif
-# ifdef NCR5380_STAT_LIMIT
- if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT)
-# endif
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingw++;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingr++;
- break;
- }
-#endif
-
- /*
- * We use the host_scribble field as a pointer to the next command
- * in a queue
- */
-
- SET_NEXT(cmd, NULL);
- cmd->scsi_done = done;
-
- cmd->result = 0;
-
- /*
- * Insert the cmd into the issue queue. Note that REQUEST SENSE
- * commands are added to the head of the queue since any command will
- * clear the contingent allegiance condition that exists and the
- * sense data is only guaranteed to be valid while the condition exists.
- */
-
- local_irq_save(flags);
- /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
- * Otherwise a running NCR5380_main may steal the lock.
- * Lock before actually inserting due to fairness reasons explained in
- * atari_scsi.c. If we insert first, then it's impossible for this driver
- * to release the lock.
- * Stop timer for this command while waiting for the lock, or timeouts
- * may happen (and they really do), and it's no good if the command doesn't
- * appear in any of the queues.
- * ++roman: Just disabling the NCR interrupt isn't sufficient here,
- * because also a timer int can trigger an abort or reset, which would
- * alter queues and touch the lock.
- */
- if (!IS_A_TT()) {
- /* perhaps stop command timer here */
- falcon_get_lock();
- /* perhaps restart command timer here */
- }
- if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
- LIST(cmd, hostdata->issue_queue);
- SET_NEXT(cmd, hostdata->issue_queue);
- hostdata->issue_queue = cmd;
- } else {
- for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
- NEXT(tmp); tmp = NEXT(tmp))
- ;
- LIST(cmd, tmp);
- SET_NEXT(tmp, cmd);
- }
- local_irq_restore(flags);
-
- QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd),
- (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
-
- /* If queue_command() is called from an interrupt (real one or bottom
- * half), we let queue_main() do the job of taking care about main. If it
- * is already running, this is a no-op, else main will be queued.
- *
- * If we're not in an interrupt, we can call NCR5380_main()
- * unconditionally, because it cannot be already running.
- */
- if (in_interrupt() || ((flags >> 8) & 7) >= 6)
- queue_main();
- else
- NCR5380_main(NULL);
- return 0;
-}
-
-static DEF_SCSI_QCMD(NCR5380_queue_command)
-
-/*
- * Function : NCR5380_main (void)
- *
- * Purpose : NCR5380_main is a coroutine that runs as long as more work can
- * be done on the NCR5380 host adapters in a system. Both
- * NCR5380_queue_command() and NCR5380_intr() will try to start it
- * in case it is not running.
- *
- * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
- * reenable them. This prevents reentrancy and kernel stack overflow.
- */
-
-static void NCR5380_main(struct work_struct *work)
-{
- Scsi_Cmnd *tmp, *prev;
- struct Scsi_Host *instance = first_instance;
- struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
- int done;
- unsigned long flags;
-
- /*
- * We run (with interrupts disabled) until we're sure that none of
- * the host adapters have anything that can be done, at which point
- * we set main_running to 0 and exit.
- *
- * Interrupts are enabled before doing various other internal
- * instructions, after we've decided that we need to run through
- * the loop again.
- *
- * this should prevent any race conditions.
- *
- * ++roman: Just disabling the NCR interrupt isn't sufficient here,
- * because also a timer int can trigger an abort or reset, which can
- * alter queues and touch the Falcon lock.
- */
-
- /* Tell int handlers main() is now already executing. Note that
- no races are possible here. If an int comes in before
- 'main_running' is set here, and queues/executes main via the
- task queue, it doesn't do any harm, just this instance of main
- won't find any work left to do. */
- if (main_running)
- return;
- main_running = 1;
-
- local_save_flags(flags);
- do {
- local_irq_disable(); /* Freeze request queues */
- done = 1;
-
- if (!hostdata->connected) {
- MAIN_PRINTK("scsi%d: not connected\n", HOSTNO);
- /*
- * Search through the issue_queue for a command destined
- * for a target that's not busy.
- */
-#if (NDEBUG & NDEBUG_LISTS)
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
- tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
- ;
- /*printk("%p ", tmp);*/
- if ((tmp == prev) && tmp)
- printk(" LOOP\n");
- /* else printk("\n"); */
-#endif
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
- prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp)) {
-
-#if (NDEBUG & NDEBUG_LISTS)
- if (prev != tmp)
- printk("MAIN tmp=%p target=%d busy=%d lun=%d\n",
- tmp, tmp->device->id, hostdata->busy[tmp->device->id],
- tmp->device->lun);
-#endif
- /* When we find one, remove it from the issue queue. */
- /* ++guenther: possible race with Falcon locking */
- if (
-#ifdef SUPPORT_TAGS
- !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
-#else
- !(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))
-#endif
- ) {
- /* ++guenther: just to be sure, this must be atomic */
- local_irq_disable();
- if (prev) {
- REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
- SET_NEXT(prev, NEXT(tmp));
- } else {
- REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
- hostdata->issue_queue = NEXT(tmp);
- }
- SET_NEXT(tmp, NULL);
- falcon_dont_release++;
-
- /* reenable interrupts after finding one */
- local_irq_restore(flags);
-
- /*
- * Attempt to establish an I_T_L nexus here.
- * On success, instance->hostdata->connected is set.
- * On failure, we must add the command back to the
- * issue queue so we can keep trying.
- */
- MAIN_PRINTK("scsi%d: main(): command for target %d "
- "lun %d removed from issue_queue\n",
- HOSTNO, tmp->device->id, tmp->device->lun);
- /*
- * REQUEST SENSE commands are issued without tagged
- * queueing, even on SCSI-II devices because the
- * contingent allegiance condition exists for the
- * entire unit.
- */
- /* ++roman: ...and the standard also requires that
- * REQUEST SENSE command are untagged.
- */
-
-#ifdef SUPPORT_TAGS
- cmd_get_tag(tmp, tmp->cmnd[0] != REQUEST_SENSE);
-#endif
- if (!NCR5380_select(instance, tmp,
- (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
- TAG_NEXT)) {
- falcon_dont_release--;
- /* release if target did not response! */
- falcon_release_lock_if_possible(hostdata);
- break;
- } else {
- local_irq_disable();
- LIST(tmp, hostdata->issue_queue);
- SET_NEXT(tmp, hostdata->issue_queue);
- hostdata->issue_queue = tmp;
-#ifdef SUPPORT_TAGS
- cmd_free_tag(tmp);
-#endif
- falcon_dont_release--;
- local_irq_restore(flags);
- MAIN_PRINTK("scsi%d: main(): select() failed, "
- "returned to issue_queue\n", HOSTNO);
- if (hostdata->connected)
- break;
- }
- } /* if target/lun/target queue is not busy */
- } /* for issue_queue */
- } /* if (!hostdata->connected) */
-
- if (hostdata->connected
-#ifdef REAL_DMA
- && !hostdata->dma_len
-#endif
- ) {
- local_irq_restore(flags);
- MAIN_PRINTK("scsi%d: main: performing information transfer\n",
- HOSTNO);
- NCR5380_information_transfer(instance);
- MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO);
- done = 0;
- }
- } while (!done);
-
- /* Better allow ints _after_ 'main_running' has been cleared, else
- an interrupt could believe we'll pick up the work it left for
- us, but we won't see it anymore here... */
- main_running = 0;
- local_irq_restore(flags);
-}
-
-
-#ifdef REAL_DMA
-/*
- * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
- *
- * Purpose : Called by interrupt handler when DMA finishes or a phase
- * mismatch occurs (which would finish the DMA transfer).
- *
- * Inputs : instance - this instance of the NCR5380.
- *
- */
-
-static void NCR5380_dma_complete(struct Scsi_Host *instance)
-{
- SETUP_HOSTDATA(instance);
- int transfered, saved_data = 0, overrun = 0, cnt, toPIO;
- unsigned char **data, p;
- volatile int *count;
-
- if (!hostdata->connected) {
- printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
- "no connected cmd\n", HOSTNO);
- return;
- }
-
- if (atari_read_overruns) {
- p = hostdata->connected->SCp.phase;
- if (p & SR_IO) {
- udelay(10);
- if ((NCR5380_read(BUS_AND_STATUS_REG) &
- (BASR_PHASE_MATCH|BASR_ACK)) ==
- (BASR_PHASE_MATCH|BASR_ACK)) {
- saved_data = NCR5380_read(INPUT_DATA_REG);
- overrun = 1;
- DMA_PRINTK("scsi%d: read overrun handled\n", HOSTNO);
- }
- }
- }
-
- DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
- HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
- NCR5380_read(STATUS_REG));
-
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
- hostdata->dma_len = 0;
-
- data = (unsigned char **)&hostdata->connected->SCp.ptr;
- count = &hostdata->connected->SCp.this_residual;
- *data += transfered;
- *count -= transfered;
-
- if (atari_read_overruns) {
- if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) {
- cnt = toPIO = atari_read_overruns;
- if (overrun) {
- DMA_PRINTK("Got an input overrun, using saved byte\n");
- *(*data)++ = saved_data;
- (*count)--;
- cnt--;
- toPIO--;
- }
- DMA_PRINTK("Doing %d-byte PIO to 0x%08lx\n", cnt, (long)*data);
- NCR5380_transfer_pio(instance, &p, &cnt, data);
- *count -= toPIO - cnt;
- }
- }
-}
-#endif /* REAL_DMA */
-
-
-/*
- * Function : void NCR5380_intr (int irq)
- *
- * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
- * from the disconnected queue, and restarting NCR5380_main()
- * as required.
- *
- * Inputs : int irq, irq that caused this interrupt.
- *
- */
-
-static irqreturn_t NCR5380_intr(int irq, void *dev_id)
-{
- struct Scsi_Host *instance = first_instance;
- int done = 1, handled = 0;
- unsigned char basr;
-
- INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO);
-
- /* Look for pending interrupts */
- basr = NCR5380_read(BUS_AND_STATUS_REG);
- INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr);
- /* dispatch to appropriate routine if found and done=0 */
- if (basr & BASR_IRQ) {
- NCR_PRINT(NDEBUG_INTR);
- if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
- done = 0;
- ENABLE_IRQ();
- INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO);
- NCR5380_reselect(instance);
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- } else if (basr & BASR_PARITY_ERROR) {
- INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO);
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
- INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO);
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- } else {
- /*
- * The rest of the interrupt conditions can occur only during a
- * DMA transfer
- */
-
-#if defined(REAL_DMA)
- /*
- * We should only get PHASE MISMATCH and EOP interrupts if we have
- * DMA enabled, so do a sanity check based on the current setting
- * of the MODE register.
- */
-
- if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
- ((basr & BASR_END_DMA_TRANSFER) ||
- !(basr & BASR_PHASE_MATCH))) {
-
- INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
- NCR5380_dma_complete( instance );
- done = 0;
- ENABLE_IRQ();
- } else
-#endif /* REAL_DMA */
- {
-/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
- if (basr & BASR_PHASE_MATCH)
- printk(KERN_NOTICE "scsi%d: unknown interrupt, "
- "BASR 0x%x, MR 0x%x, SR 0x%x\n",
- HOSTNO, basr, NCR5380_read(MODE_REG),
- NCR5380_read(STATUS_REG));
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- }
- } /* if !(SELECTION || PARITY) */
- handled = 1;
- } /* BASR & IRQ */ else {
- printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
- "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
- NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- }
-
- if (!done) {
- INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO);
- /* Put a call to NCR5380_main() on the queue... */
- queue_main();
- }
- return IRQ_RETVAL(handled);
-}
-
-#ifdef NCR5380_STATS
-static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd *cmd)
-{
-# ifdef NCR5380_STAT_LIMIT
- if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT)
-# endif
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase);
- /*hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);*/
- hostdata->pendingw--;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase);
- /*hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);*/
- hostdata->pendingr--;
- break;
- }
-}
-#endif
-
-/*
- * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
- * int tag);
- *
- * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
- * including ARBITRATION, SELECTION, and initial message out for
- * IDENTIFY and queue messages.
- *
- * Inputs : instance - instantiation of the 5380 driver on which this
- * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
- * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
- * the command that is presently connected.
- *
- * Returns : -1 if selection could not execute for some reason,
- * 0 if selection succeeded or failed because the target
- * did not respond.
- *
- * Side effects :
- * If bus busy, arbitration failed, etc, NCR5380_select() will exit
- * with registers as they should have been on entry - ie
- * SELECT_ENABLE will be set appropriately, the NCR5380
- * will cease to drive any SCSI bus signals.
- *
- * If successful : I_T_L or I_T_L_Q nexus will be established,
- * instance->connected will be set to cmd.
- * SELECT interrupt will be disabled.
- *
- * If failed (no target) : cmd->scsi_done() will be called, and the
- * cmd->result host byte set to DID_BAD_TARGET.
- */
-
-static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
-{
- SETUP_HOSTDATA(instance);
- unsigned char tmp[3], phase;
- unsigned char *data;
- int len;
- unsigned long timeout;
- unsigned long flags;
-
- hostdata->restart_select = 0;
- NCR_PRINT(NDEBUG_ARBITRATION);
- ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO,
- instance->this_id);
-
- /*
- * Set the phase bits to 0, otherwise the NCR5380 won't drive the
- * data bus during SELECTION.
- */
-
- local_irq_save(flags);
- if (hostdata->connected) {
- local_irq_restore(flags);
- return -1;
- }
- NCR5380_write(TARGET_COMMAND_REG, 0);
-
- /*
- * Start arbitration.
- */
-
- NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
- NCR5380_write(MODE_REG, MR_ARBITRATE);
-
- local_irq_restore(flags);
-
- /* Wait for arbitration logic to complete */
-#if defined(NCR_TIMEOUT)
- {
- unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
-
- while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) &&
- time_before(jiffies, timeout) && !hostdata->connected)
- ;
- if (time_after_eq(jiffies, timeout)) {
- printk("scsi : arbitration timeout at %d\n", __LINE__);
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
- }
- }
-#else /* NCR_TIMEOUT */
- while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) &&
- !hostdata->connected)
- ;
-#endif
-
- ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO);
-
- if (hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- return -1;
- }
- /*
- * The arbitration delay is 2.2us, but this is a minimum and there is
- * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
- * the integral nature of udelay().
- *
- */
-
- udelay(3);
-
- /* Check for lost arbitration */
- if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
- (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
- (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
- hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
- HOSTNO);
- return -1;
- }
-
- /* after/during arbitration, BSY should be asserted.
- IBM DPES-31080 Version S31Q works now */
- /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */
- NCR5380_write(INITIATOR_COMMAND_REG,
- ICR_BASE | ICR_ASSERT_SEL | ICR_ASSERT_BSY);
-
- if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
- hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
- HOSTNO);
- return -1;
- }
-
- /*
- * Again, bus clear + bus settle time is 1.2us, however, this is
- * a minimum so we'll udelay ceil(1.2)
- */
-
-#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
- /* ++roman: But some targets (see above :-) seem to need a bit more... */
- udelay(15);
-#else
- udelay(2);
-#endif
-
- if (hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- return -1;
- }
-
- ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO);
-
- /*
- * Now that we have won arbitration, start Selection process, asserting
- * the host and target ID's on the SCSI bus.
- */
-
- NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->device->id)));
-
- /*
- * Raise ATN while SEL is true before BSY goes false from arbitration,
- * since this is the only way to guarantee that we'll get a MESSAGE OUT
- * phase immediately after selection.
- */
-
- NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
- ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
- NCR5380_write(MODE_REG, MR_BASE);
-
- /*
- * Reselect interrupts must be turned off prior to the dropping of BSY,
- * otherwise we will trigger an interrupt.
- */
-
- if (hostdata->connected) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- return -1;
- }
-
- NCR5380_write(SELECT_ENABLE_REG, 0);
-
- /*
- * The initiator shall then wait at least two deskew delays and release
- * the BSY signal.
- */
- udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
-
- /* Reset BSY */
- NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
- ICR_ASSERT_ATN | ICR_ASSERT_SEL));
-
- /*
- * Something weird happens when we cease to drive BSY - looks
- * like the board/chip is letting us do another read before the
- * appropriate propagation delay has expired, and we're confusing
- * a BSY signal from ourselves as the target's response to SELECTION.
- *
- * A small delay (the 'C++' frontend breaks the pipeline with an
- * unnecessary jump, making it work on my 386-33/Trantor T128, the
- * tighter 'C' code breaks and requires this) solves the problem -
- * the 1 us delay is arbitrary, and only used because this delay will
- * be the same on other platforms and since it works here, it should
- * work there.
- *
- * wingel suggests that this could be due to failing to wait
- * one deskew delay.
- */
-
- udelay(1);
-
- SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->device->id);
-
- /*
- * The SCSI specification calls for a 250 ms timeout for the actual
- * selection.
- */
-
- timeout = jiffies + 25;
-
- /*
- * XXX very interesting - we're seeing a bounce where the BSY we
- * asserted is being reflected / still asserted (propagation delay?)
- * and it's detecting as true. Sigh.
- */
-
-#if 0
- /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert
- * IO while SEL is true. But again, there are some disks out the in the
- * world that do that nevertheless. (Somebody claimed that this announces
- * reselection capability of the target.) So we better skip that test and
- * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
- */
-
- while (time_before(jiffies, timeout) &&
- !(NCR5380_read(STATUS_REG) & (SR_BSY | SR_IO)))
- ;
-
- if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- NCR5380_reselect(instance);
- printk(KERN_ERR "scsi%d: reselection after won arbitration?\n",
- HOSTNO);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
- }
-#else
- while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY))
- ;
-#endif
-
- /*
- * No less than two deskew delays after the initiator detects the
- * BSY signal is true, it shall release the SEL signal and may
- * change the DATA BUS. -wingel
- */
-
- udelay(1);
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-
- if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- if (hostdata->targets_present & (1 << cmd->device->id)) {
- printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO);
- if (hostdata->restart_select)
- printk(KERN_NOTICE "\trestart select\n");
- NCR_PRINT(NDEBUG_ANY);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
- }
- cmd->result = DID_BAD_TARGET << 16;
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
-#ifdef SUPPORT_TAGS
- cmd_free_tag(cmd);
-#endif
- cmd->scsi_done(cmd);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return 0;
- }
-
- hostdata->targets_present |= (1 << cmd->device->id);
-
- /*
- * Since we followed the SCSI spec, and raised ATN while SEL
- * was true but before BSY was false during selection, the information
- * transfer phase should be a MESSAGE OUT phase so that we can send the
- * IDENTIFY message.
- *
- * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
- * message (2 bytes) with a tag ID that we increment with every command
- * until it wraps back to 0.
- *
- * XXX - it turns out that there are some broken SCSI-II devices,
- * which claim to support tagged queuing but fail when more than
- * some number of commands are issued at once.
- */
-
- /* Wait for start of REQ/ACK handshake */
- while (!(NCR5380_read(STATUS_REG) & SR_REQ))
- ;
-
- SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
- HOSTNO, cmd->device->id);
- tmp[0] = IDENTIFY(1, cmd->device->lun);
-
-#ifdef SUPPORT_TAGS
- if (cmd->tag != TAG_NONE) {
- tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
- tmp[2] = cmd->tag;
- len = 3;
- } else
- len = 1;
-#else
- len = 1;
- cmd->tag = 0;
-#endif /* SUPPORT_TAGS */
-
- /* Send message(s) */
- data = tmp;
- phase = PHASE_MSGOUT;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO);
- /* XXX need to handle errors here */
- hostdata->connected = cmd;
-#ifndef SUPPORT_TAGS
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
-#endif
-
- initialize_SCp(cmd);
-
- return 0;
-}
-
-/*
- * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
- * unsigned char *phase, int *count, unsigned char **data)
- *
- * Purpose : transfers data in given phase using polled I/O
- *
- * Inputs : instance - instance of driver, *phase - pointer to
- * what phase is expected, *count - pointer to number of
- * bytes to transfer, **data - pointer to data pointer.
- *
- * Returns : -1 when different phase is entered without transferring
- * maximum number of bytes, 0 if all bytes are transferred or exit
- * is in same phase.
- *
- * Also, *phase, *count, *data are modified in place.
- *
- * XXX Note : handling for bus free may be useful.
- */
-
-/*
- * Note : this code is not as quick as it could be, however it
- * IS 100% reliable, and for the actual data transfer where speed
- * counts, we will always do a pseudo DMA or DMA transfer.
- */
-
-static int NCR5380_transfer_pio(struct Scsi_Host *instance,
- unsigned char *phase, int *count,
- unsigned char **data)
-{
- register unsigned char p = *phase, tmp;
- register int c = *count;
- register unsigned char *d = *data;
-
- /*
- * The NCR5380 chip will only drive the SCSI bus when the
- * phase specified in the appropriate bits of the TARGET COMMAND
- * REGISTER match the STATUS REGISTER
- */
-
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-
- do {
- /*
- * Wait for assertion of REQ, after which the phase bits will be
- * valid
- */
- while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ))
- ;
-
- HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO);
-
- /* Check for phase mismatch */
- if ((tmp & PHASE_MASK) != p) {
- PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO);
- NCR_PRINT_PHASE(NDEBUG_PIO);
- break;
- }
-
- /* Do actual transfer from SCSI bus to / from memory */
- if (!(p & SR_IO))
- NCR5380_write(OUTPUT_DATA_REG, *d);
- else
- *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
-
- ++d;
-
- /*
- * The SCSI standard suggests that in MSGOUT phase, the initiator
- * should drop ATN on the last byte of the message phase
- * after REQ has been asserted for the handshake but before
- * the initiator raises ACK.
- */
-
- if (!(p & SR_IO)) {
- if (!((p & SR_MSG) && c > 1)) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
- NCR_PRINT(NDEBUG_PIO);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_DATA | ICR_ASSERT_ACK);
- } else {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_DATA | ICR_ASSERT_ATN);
- NCR_PRINT(NDEBUG_PIO);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
- }
- } else {
- NCR_PRINT(NDEBUG_PIO);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
- }
-
- while (NCR5380_read(STATUS_REG) & SR_REQ)
- ;
-
- HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO);
-
- /*
- * We have several special cases to consider during REQ/ACK handshaking :
- * 1. We were in MSGOUT phase, and we are on the last byte of the
- * message. ATN must be dropped as ACK is dropped.
- *
- * 2. We are in a MSGIN phase, and we are on the last byte of the
- * message. We must exit with ACK asserted, so that the calling
- * code may raise ATN before dropping ACK to reject the message.
- *
- * 3. ACK and ATN are clear and the target may proceed as normal.
- */
- if (!(p == PHASE_MSGIN && c == 1)) {
- if (p == PHASE_MSGOUT && c > 1)
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- else
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- }
- } while (--c);
-
- PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c);
-
- *count = c;
- *data = d;
- tmp = NCR5380_read(STATUS_REG);
- /* The phase read from the bus is valid if either REQ is (already)
- * asserted or if ACK hasn't been released yet. The latter is the case if
- * we're in MSGIN and all wanted bytes have been received.
- */
- if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0))
- *phase = tmp & PHASE_MASK;
- else
- *phase = PHASE_UNKNOWN;
-
- if (!c || (*phase == p))
- return 0;
- else
- return -1;
-}
-
-/*
- * Function : do_abort (Scsi_Host *host)
- *
- * Purpose : abort the currently established nexus. Should only be
- * called from a routine which can drop into a
- *
- * Returns : 0 on success, -1 on failure.
- */
-
-static int do_abort(struct Scsi_Host *host)
-{
- unsigned char tmp, *msgptr, phase;
- int len;
-
- /* Request message out phase */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-
- /*
- * Wait for the target to indicate a valid phase by asserting
- * REQ. Once this happens, we'll have either a MSGOUT phase
- * and can immediately send the ABORT message, or we'll have some
- * other phase and will have to source/sink data.
- *
- * We really don't care what value was on the bus or what value
- * the target sees, so we just handshake.
- */
-
- while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ))
- ;
-
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
-
- if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
- ICR_ASSERT_ACK);
- while (NCR5380_read(STATUS_REG) & SR_REQ)
- ;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- }
-
- tmp = ABORT;
- msgptr = &tmp;
- len = 1;
- phase = PHASE_MSGOUT;
- NCR5380_transfer_pio(host, &phase, &len, &msgptr);
-
- /*
- * If we got here, and the command completed successfully,
- * we're about to go into bus free state.
- */
-
- return len ? -1 : 0;
-}
-
-#if defined(REAL_DMA)
-/*
- * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
- * unsigned char *phase, int *count, unsigned char **data)
- *
- * Purpose : transfers data in given phase using either real
- * or pseudo DMA.
- *
- * Inputs : instance - instance of driver, *phase - pointer to
- * what phase is expected, *count - pointer to number of
- * bytes to transfer, **data - pointer to data pointer.
- *
- * Returns : -1 when different phase is entered without transferring
- * maximum number of bytes, 0 if all bytes or transferred or exit
- * is in same phase.
- *
- * Also, *phase, *count, *data are modified in place.
- *
- */
-
-
-static int NCR5380_transfer_dma(struct Scsi_Host *instance,
- unsigned char *phase, int *count,
- unsigned char **data)
-{
- SETUP_HOSTDATA(instance);
- register int c = *count;
- register unsigned char p = *phase;
- register unsigned char *d = *data;
- unsigned char tmp;
- unsigned long flags;
-
- if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
- *phase = tmp;
- return -1;
- }
-
- if (atari_read_overruns && (p & SR_IO))
- c -= atari_read_overruns;
-
- DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n",
- HOSTNO, (p & SR_IO) ? "reading" : "writing",
- c, (p & SR_IO) ? "to" : "from", d);
-
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-
-#ifdef REAL_DMA
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
-#endif /* def REAL_DMA */
-
- if (IS_A_TT()) {
- /* On the Medusa, it is a must to initialize the DMA before
- * starting the NCR. This is also the cleaner way for the TT.
- */
- local_irq_save(flags);
- hostdata->dma_len = (p & SR_IO) ?
- NCR5380_dma_read_setup(instance, d, c) :
- NCR5380_dma_write_setup(instance, d, c);
- local_irq_restore(flags);
- }
-
- if (p & SR_IO)
- NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
- else {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
- NCR5380_write(START_DMA_SEND_REG, 0);
- }
-
- if (!IS_A_TT()) {
- /* On the Falcon, the DMA setup must be done after the last */
- /* NCR access, else the DMA setup gets trashed!
- */
- local_irq_save(flags);
- hostdata->dma_len = (p & SR_IO) ?
- NCR5380_dma_read_setup(instance, d, c) :
- NCR5380_dma_write_setup(instance, d, c);
- local_irq_restore(flags);
- }
- return 0;
-}
-#endif /* defined(REAL_DMA) */
-
-/*
- * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
- *
- * Purpose : run through the various SCSI phases and do as the target
- * directs us to. Operates on the currently connected command,
- * instance->connected.
- *
- * Inputs : instance, instance for which we are doing commands
- *
- * Side effects : SCSI things happen, the disconnected queue will be
- * modified if a command disconnects, *instance->connected will
- * change.
- *
- * XXX Note : we need to watch for bus free or a reset condition here
- * to recover from an unexpected bus free condition.
- */
-
-static void NCR5380_information_transfer(struct Scsi_Host *instance)
-{
- SETUP_HOSTDATA(instance);
- unsigned long flags;
- unsigned char msgout = NOP;
- int sink = 0;
- int len;
-#if defined(REAL_DMA)
- int transfersize;
-#endif
- unsigned char *data;
- unsigned char phase, tmp, extended_msg[10], old_phase = 0xff;
- Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
-
- while (1) {
- tmp = NCR5380_read(STATUS_REG);
- /* We only have a valid SCSI phase when REQ is asserted */
- if (tmp & SR_REQ) {
- phase = (tmp & PHASE_MASK);
- if (phase != old_phase) {
- old_phase = phase;
- NCR_PRINT_PHASE(NDEBUG_INFORMATION);
- }
-
- if (sink && (phase != PHASE_MSGOUT)) {
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
- ICR_ASSERT_ACK);
- while (NCR5380_read(STATUS_REG) & SR_REQ)
- ;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_ATN);
- sink = 0;
- continue;
- }
-
- switch (phase) {
- case PHASE_DATAOUT:
-#if (NDEBUG & NDEBUG_NO_DATAOUT)
- printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT "
- "aborted\n", HOSTNO);
- sink = 1;
- do_abort(instance);
- cmd->result = DID_ERROR << 16;
- cmd->scsi_done(cmd);
- return;
-#endif
- case PHASE_DATAIN:
- /*
- * If there is no room left in the current buffer in the
- * scatter-gather list, move onto the next one.
- */
-
- if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
- ++cmd->SCp.buffer;
- --cmd->SCp.buffers_residual;
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- /* ++roman: Try to merge some scatter-buffers if
- * they are at contiguous physical addresses.
- */
- merge_contiguous_buffers(cmd);
- INF_PRINTK("scsi%d: %d bytes and %d buffers left\n",
- HOSTNO, cmd->SCp.this_residual,
- cmd->SCp.buffers_residual);
- }
-
- /*
- * The preferred transfer method is going to be
- * PSEUDO-DMA for systems that are strictly PIO,
- * since we can let the hardware do the handshaking.
- *
- * For this to work, we need to know the transfersize
- * ahead of time, since the pseudo-DMA code will sit
- * in an unconditional loop.
- */
-
- /* ++roman: I suggest, this should be
- * #if def(REAL_DMA)
- * instead of leaving REAL_DMA out.
- */
-
-#if defined(REAL_DMA)
- if (!cmd->device->borken &&
- (transfersize = NCR5380_dma_xfer_len(instance,cmd,phase)) > 31) {
- len = transfersize;
- cmd->SCp.phase = phase;
- if (NCR5380_transfer_dma(instance, &phase,
- &len, (unsigned char **)&cmd->SCp.ptr)) {
- /*
- * If the watchdog timer fires, all future
- * accesses to this device will use the
- * polled-IO. */
- printk(KERN_NOTICE "scsi%d: switching target %d "
- "lun %d to slow handshake\n", HOSTNO,
- cmd->device->id, cmd->device->lun);
- cmd->device->borken = 1;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_ATN);
- sink = 1;
- do_abort(instance);
- cmd->result = DID_ERROR << 16;
- cmd->scsi_done(cmd);
- /* XXX - need to source or sink data here, as appropriate */
- } else {
-#ifdef REAL_DMA
- /* ++roman: When using real DMA,
- * information_transfer() should return after
- * starting DMA since it has nothing more to
- * do.
- */
- return;
-#else
- cmd->SCp.this_residual -= transfersize - len;
-#endif
- }
- } else
-#endif /* defined(REAL_DMA) */
- NCR5380_transfer_pio(instance, &phase,
- (int *)&cmd->SCp.this_residual,
- (unsigned char **)&cmd->SCp.ptr);
- break;
- case PHASE_MSGIN:
- len = 1;
- data = &tmp;
- NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- cmd->SCp.Message = tmp;
-
- switch (tmp) {
- /*
- * Linking lets us reduce the time required to get the
- * next command out to the device, hopefully this will
- * mean we don't waste another revolution due to the delays
- * required by ARBITRATION and another SELECTION.
- *
- * In the current implementation proposal, low level drivers
- * merely have to start the next command, pointed to by
- * next_link, done() is called as with unlinked commands.
- */
-#ifdef LINKED
- case LINKED_CMD_COMPLETE:
- case LINKED_FLG_CMD_COMPLETE:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- LNK_PRINTK("scsi%d: target %d lun %d linked command "
- "complete.\n", HOSTNO, cmd->device->id, cmd->device->lun);
-
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /*
- * Sanity check : A linked command should only terminate
- * with one of these messages if there are more linked
- * commands available.
- */
-
- if (!cmd->next_link) {
- printk(KERN_NOTICE "scsi%d: target %d lun %d "
- "linked command complete, no next_link\n",
- HOSTNO, cmd->device->id, cmd->device->lun);
- sink = 1;
- do_abort(instance);
- return;
- }
-
- initialize_SCp(cmd->next_link);
- /* The next command is still part of this process; copy it
- * and don't free it! */
- cmd->next_link->tag = cmd->tag;
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- LNK_PRINTK("scsi%d: target %d lun %d linked request "
- "done, calling scsi_done().\n",
- HOSTNO, cmd->device->id, cmd->device->lun);
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
- cmd->scsi_done(cmd);
- cmd = hostdata->connected;
- break;
-#endif /* def LINKED */
- case ABORT:
- case COMMAND_COMPLETE:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- /* ++guenther: possible race with Falcon locking */
- falcon_dont_release++;
- hostdata->connected = NULL;
- QU_PRINTK("scsi%d: command for target %d, lun %d "
- "completed\n", HOSTNO, cmd->device->id, cmd->device->lun);
-#ifdef SUPPORT_TAGS
- cmd_free_tag(cmd);
- if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
- /* Turn a QUEUE FULL status into BUSY, I think the
- * mid level cannot handle QUEUE FULL :-( (The
- * command is retried after BUSY). Also update our
- * queue size to the number of currently issued
- * commands now.
- */
- /* ++Andreas: the mid level code knows about
- QUEUE_FULL now. */
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
- TAG_PRINTK("scsi%d: target %d lun %d returned "
- "QUEUE_FULL after %d commands\n",
- HOSTNO, cmd->device->id, cmd->device->lun,
- ta->nr_allocated);
- if (ta->queue_size > ta->nr_allocated)
- ta->nr_allocated = ta->queue_size;
- }
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-
- /*
- * I'm not sure what the correct thing to do here is :
- *
- * If the command that just executed is NOT a request
- * sense, the obvious thing to do is to set the result
- * code to the values of the stored parameters.
- *
- * If it was a REQUEST SENSE command, we need some way to
- * differentiate between the failure code of the original
- * and the failure code of the REQUEST sense - the obvious
- * case is success, where we fall through and leave the
- * result code unchanged.
- *
- * The non-obvious place is where the REQUEST SENSE failed
- */
-
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- else if (status_byte(cmd->SCp.Status) != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
-
-#ifdef AUTOSENSE
- if ((cmd->cmnd[0] == REQUEST_SENSE) &&
- hostdata->ses.cmd_len) {
- scsi_eh_restore_cmnd(cmd, &hostdata->ses);
- hostdata->ses.cmd_len = 0 ;
- }
-
- if ((cmd->cmnd[0] != REQUEST_SENSE) &&
- (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
- scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0);
-
- ASEN_PRINTK("scsi%d: performing request sense\n", HOSTNO);
-
- local_irq_save(flags);
- LIST(cmd,hostdata->issue_queue);
- SET_NEXT(cmd, hostdata->issue_queue);
- hostdata->issue_queue = (Scsi_Cmnd *) cmd;
- local_irq_restore(flags);
- QU_PRINTK("scsi%d: REQUEST SENSE added to head of "
- "issue queue\n", H_NO(cmd));
- } else
-#endif /* def AUTOSENSE */
- {
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
- cmd->scsi_done(cmd);
- }
-
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /*
- * Restore phase bits to 0 so an interrupted selection,
- * arbitration can resume.
- */
- NCR5380_write(TARGET_COMMAND_REG, 0);
-
- while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
- barrier();
-
- falcon_dont_release--;
- /* ++roman: For Falcon SCSI, release the lock on the
- * ST-DMA here if no other commands are waiting on the
- * disconnected queue.
- */
- falcon_release_lock_if_possible(hostdata);
- return;
- case MESSAGE_REJECT:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- switch (hostdata->last_message) {
- case HEAD_OF_QUEUE_TAG:
- case ORDERED_QUEUE_TAG:
- case SIMPLE_QUEUE_TAG:
- /* The target obviously doesn't support tagged
- * queuing, even though it announced this ability in
- * its INQUIRY data ?!? (maybe only this LUN?) Ok,
- * clear 'tagged_supported' and lock the LUN, since
- * the command is treated as untagged further on.
- */
- cmd->device->tagged_supported = 0;
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
- cmd->tag = TAG_NONE;
- TAG_PRINTK("scsi%d: target %d lun %d rejected "
- "QUEUE_TAG message; tagged queuing "
- "disabled\n",
- HOSTNO, cmd->device->id, cmd->device->lun);
- break;
- }
- break;
- case DISCONNECT:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- local_irq_save(flags);
- cmd->device->disconnect = 1;
- LIST(cmd,hostdata->disconnected_queue);
- SET_NEXT(cmd, hostdata->disconnected_queue);
- hostdata->connected = NULL;
- hostdata->disconnected_queue = cmd;
- local_irq_restore(flags);
- QU_PRINTK("scsi%d: command for target %d lun %d was "
- "moved from connected to the "
- "disconnected_queue\n", HOSTNO,
- cmd->device->id, cmd->device->lun);
- /*
- * Restore phase bits to 0 so an interrupted selection,
- * arbitration can resume.
- */
- NCR5380_write(TARGET_COMMAND_REG, 0);
-
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /* Wait for bus free to avoid nasty timeouts */
- while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
- barrier();
- return;
- /*
- * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
- * operation, in violation of the SCSI spec so we can safely
- * ignore SAVE/RESTORE pointers calls.
- *
- * Unfortunately, some disks violate the SCSI spec and
- * don't issue the required SAVE_POINTERS message before
- * disconnecting, and we have to break spec to remain
- * compatible.
- */
- case SAVE_POINTERS:
- case RESTORE_POINTERS:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- break;
- case EXTENDED_MESSAGE:
- /*
- * Extended messages are sent in the following format :
- * Byte
- * 0 EXTENDED_MESSAGE == 1
- * 1 length (includes one byte for code, doesn't
- * include first two bytes)
- * 2 code
- * 3..length+1 arguments
- *
- * Start the extended message buffer with the EXTENDED_MESSAGE
- * byte, since spi_print_msg() wants the whole thing.
- */
- extended_msg[0] = EXTENDED_MESSAGE;
- /* Accept first byte by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO);
-
- len = 2;
- data = extended_msg + 1;
- phase = PHASE_MSGIN;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO,
- (int)extended_msg[1], (int)extended_msg[2]);
-
- if (!len && extended_msg[1] <=
- (sizeof(extended_msg) - 1)) {
- /* Accept third byte by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- len = extended_msg[1] - 1;
- data = extended_msg + 3;
- phase = PHASE_MSGIN;
-
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- EXT_PRINTK("scsi%d: message received, residual %d\n",
- HOSTNO, len);
-
- switch (extended_msg[2]) {
- case EXTENDED_SDTR:
- case EXTENDED_WDTR:
- case EXTENDED_MODIFY_DATA_POINTER:
- case EXTENDED_EXTENDED_IDENTIFY:
- tmp = 0;
- }
- } else if (len) {
- printk(KERN_NOTICE "scsi%d: error receiving "
- "extended message\n", HOSTNO);
- tmp = 0;
- } else {
- printk(KERN_NOTICE "scsi%d: extended message "
- "code %02x length %d is too long\n",
- HOSTNO, extended_msg[2], extended_msg[1]);
- tmp = 0;
- }
- /* Fall through to reject message */
-
- /*
- * If we get something weird that we aren't expecting,
- * reject it.
- */
- default:
- if (!tmp) {
- printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
- spi_print_msg(extended_msg);
- printk("\n");
- } else if (tmp != EXTENDED_MESSAGE)
- printk(KERN_DEBUG "scsi%d: rejecting unknown "
- "message %02x from target %d, lun %d\n",
- HOSTNO, tmp, cmd->device->id, cmd->device->lun);
- else
- printk(KERN_DEBUG "scsi%d: rejecting unknown "
- "extended message "
- "code %02x, length %d from target %d, lun %d\n",
- HOSTNO, extended_msg[1], extended_msg[0],
- cmd->device->id, cmd->device->lun);
-
-
- msgout = MESSAGE_REJECT;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- break;
- } /* switch (tmp) */
- break;
- case PHASE_MSGOUT:
- len = 1;
- data = &msgout;
- hostdata->last_message = msgout;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- if (msgout == ABORT) {
-#ifdef SUPPORT_TAGS
- cmd_free_tag(cmd);
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- hostdata->connected = NULL;
- cmd->result = DID_ERROR << 16;
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
- cmd->scsi_done(cmd);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- falcon_release_lock_if_possible(hostdata);
- return;
- }
- msgout = NOP;
- break;
- case PHASE_CMDOUT:
- len = cmd->cmd_len;
- data = cmd->cmnd;
- /*
- * XXX for performance reasons, on machines with a
- * PSEUDO-DMA architecture we should probably
- * use the dma transfer function.
- */
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- break;
- case PHASE_STATIN:
- len = 1;
- data = &tmp;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- cmd->SCp.Status = tmp;
- break;
- default:
- printk("scsi%d: unknown phase\n", HOSTNO);
- NCR_PRINT(NDEBUG_ANY);
- } /* switch(phase) */
- } /* if (tmp * SR_REQ) */
- } /* while (1) */
-}
-
-/*
- * Function : void NCR5380_reselect (struct Scsi_Host *instance)
- *
- * Purpose : does reselection, initializing the instance->connected
- * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
- * nexus has been reestablished,
- *
- * Inputs : instance - this instance of the NCR5380.
- *
- */
-
-
-static void NCR5380_reselect(struct Scsi_Host *instance)
-{
- SETUP_HOSTDATA(instance);
- unsigned char target_mask;
- unsigned char lun, phase;
- int len;
-#ifdef SUPPORT_TAGS
- unsigned char tag;
-#endif
- unsigned char msg[3];
- unsigned char *data;
- Scsi_Cmnd *tmp = NULL, *prev;
-/* unsigned long flags; */
-
- /*
- * Disable arbitration, etc. since the host adapter obviously
- * lost, and tell an interrupted NCR5380_select() to restart.
- */
-
- NCR5380_write(MODE_REG, MR_BASE);
- hostdata->restart_select = 1;
-
- target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
-
- RSL_PRINTK("scsi%d: reselect\n", HOSTNO);
-
- /*
- * At this point, we have detected that our SCSI ID is on the bus,
- * SEL is true and BSY was false for at least one bus settle delay
- * (400 ns).
- *
- * We must assert BSY ourselves, until the target drops the SEL
- * signal.
- */
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
-
- while (NCR5380_read(STATUS_REG) & SR_SEL)
- ;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- /*
- * Wait for target to go into MSGIN.
- */
-
- while (!(NCR5380_read(STATUS_REG) & SR_REQ))
- ;
-
- len = 1;
- data = msg;
- phase = PHASE_MSGIN;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
-
- if (!(msg[0] & 0x80)) {
- printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
- spi_print_msg(msg);
- do_abort(instance);
- return;
- }
- lun = (msg[0] & 0x07);
-
-#ifdef SUPPORT_TAGS
- /* If the phase is still MSGIN, the target wants to send some more
- * messages. In case it supports tagged queuing, this is probably a
- * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
- */
- tag = TAG_NONE;
- if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
- /* Accept previous IDENTIFY message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- len = 2;
- data = msg + 1;
- if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
- msg[1] == SIMPLE_QUEUE_TAG)
- tag = msg[2];
- TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at "
- "reselection\n", HOSTNO, target_mask, lun, tag);
- }
-#endif
-
- /*
- * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
- * just reestablished, and remove it from the disconnected queue.
- */
-
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
- tmp; prev = tmp, tmp = NEXT(tmp)) {
- if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun)
-#ifdef SUPPORT_TAGS
- && (tag == tmp->tag)
-#endif
- ) {
- /* ++guenther: prevent race with falcon_release_lock */
- falcon_dont_release++;
- if (prev) {
- REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
- SET_NEXT(prev, NEXT(tmp));
- } else {
- REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp));
- hostdata->disconnected_queue = NEXT(tmp);
- }
- SET_NEXT(tmp, NULL);
- break;
- }
- }
-
- if (!tmp) {
- printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d "
-#ifdef SUPPORT_TAGS
- "tag %d "
-#endif
- "not in disconnected_queue.\n",
- HOSTNO, target_mask, lun
-#ifdef SUPPORT_TAGS
- , tag
-#endif
- );
- /*
- * Since we have an established nexus that we can't do anything
- * with, we must abort it.
- */
- do_abort(instance);
- return;
- }
-
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- hostdata->connected = tmp;
- RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
- HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag);
- falcon_dont_release--;
-}
-
-
-/*
- * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
- *
- * Purpose : abort a command
- *
- * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
- * host byte of the result field to, if zero DID_ABORTED is
- * used.
- *
- * Returns : 0 - success, -1 on failure.
- *
- * XXX - there is no way to abort the command that is currently
- * connected, you have to wait for it to complete. If this is
- * a problem, we could implement longjmp() / setjmp(), setjmp()
- * called where the loop started in NCR5380_main().
- */
-
-static
-int NCR5380_abort(Scsi_Cmnd *cmd)
-{
- struct Scsi_Host *instance = cmd->device->host;
- SETUP_HOSTDATA(instance);
- Scsi_Cmnd *tmp, **prev;
- unsigned long flags;
-
- printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
- scsi_print_command(cmd);
-
- NCR5380_print_status(instance);
-
- local_irq_save(flags);
-
- if (!IS_A_TT() && !falcon_got_lock)
- printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_abort\n",
- HOSTNO);
-
- ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
- NCR5380_read(BUS_AND_STATUS_REG),
- NCR5380_read(STATUS_REG));
-
-#if 1
- /*
- * Case 1 : If the command is the currently executing command,
- * we'll set the aborted flag and return control so that
- * information transfer routine can exit cleanly.
- */
-
- if (hostdata->connected == cmd) {
-
- ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO);
- /*
- * We should perform BSY checking, and make sure we haven't slipped
- * into BUS FREE.
- */
-
- /* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */
- /*
- * Since we can't change phases until we've completed the current
- * handshake, we have to source or sink a byte of data if the current
- * phase is not MSGOUT.
- */
-
- /*
- * Return control to the executing NCR drive so we can clear the
- * aborted flag and get back into our main loop.
- */
-
- if (do_abort(instance) == 0) {
- hostdata->aborted = 1;
- hostdata->connected = NULL;
- cmd->result = DID_ABORT << 16;
-#ifdef SUPPORT_TAGS
- cmd_free_tag(cmd);
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- local_irq_restore(flags);
- cmd->scsi_done(cmd);
- falcon_release_lock_if_possible(hostdata);
- return SCSI_ABORT_SUCCESS;
- } else {
-/* local_irq_restore(flags); */
- printk("scsi%d: abort of connected command failed!\n", HOSTNO);
- return SCSI_ABORT_ERROR;
- }
- }
-#endif
-
- /*
- * Case 2 : If the command hasn't been issued yet, we simply remove it
- * from the issue queue.
- */
- for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue),
- tmp = (Scsi_Cmnd *)hostdata->issue_queue;
- tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) {
- if (cmd == tmp) {
- REMOVE(5, *prev, tmp, NEXT(tmp));
- (*prev) = NEXT(tmp);
- SET_NEXT(tmp, NULL);
- tmp->result = DID_ABORT << 16;
- local_irq_restore(flags);
- ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n",
- HOSTNO);
- /* Tagged queuing note: no tag to free here, hasn't been assigned
- * yet... */
- tmp->scsi_done(tmp);
- falcon_release_lock_if_possible(hostdata);
- return SCSI_ABORT_SUCCESS;
- }
- }
-
- /*
- * Case 3 : If any commands are connected, we're going to fail the abort
- * and let the high level SCSI driver retry at a later time or
- * issue a reset.
- *
- * Timeouts, and therefore aborted commands, will be highly unlikely
- * and handling them cleanly in this situation would make the common
- * case of noresets less efficient, and would pollute our code. So,
- * we fail.
- */
-
- if (hostdata->connected) {
- local_irq_restore(flags);
- ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO);
- return SCSI_ABORT_SNOOZE;
- }
-
- /*
- * Case 4: If the command is currently disconnected from the bus, and
- * there are no connected commands, we reconnect the I_T_L or
- * I_T_L_Q nexus associated with it, go into message out, and send
- * an abort message.
- *
- * This case is especially ugly. In order to reestablish the nexus, we
- * need to call NCR5380_select(). The easiest way to implement this
- * function was to abort if the bus was busy, and let the interrupt
- * handler triggered on the SEL for reselect take care of lost arbitrations
- * where necessary, meaning interrupts need to be enabled.
- *
- * When interrupts are enabled, the queues may change - so we
- * can't remove it from the disconnected queue before selecting it
- * because that could cause a failure in hashing the nexus if that
- * device reselected.
- *
- * Since the queues may change, we can't use the pointers from when we
- * first locate it.
- *
- * So, we must first locate the command, and if NCR5380_select()
- * succeeds, then issue the abort, relocate the command and remove
- * it from the disconnected queue.
- */
-
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
- tmp = NEXT(tmp)) {
- if (cmd == tmp) {
- local_irq_restore(flags);
- ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO);
-
- if (NCR5380_select(instance, cmd, (int)cmd->tag))
- return SCSI_ABORT_BUSY;
-
- ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO);
-
- do_abort(instance);
-
- local_irq_save(flags);
- for (prev = (Scsi_Cmnd **)&(hostdata->disconnected_queue),
- tmp = (Scsi_Cmnd *)hostdata->disconnected_queue;
- tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) {
- if (cmd == tmp) {
- REMOVE(5, *prev, tmp, NEXT(tmp));
- *prev = NEXT(tmp);
- SET_NEXT(tmp, NULL);
- tmp->result = DID_ABORT << 16;
- /* We must unlock the tag/LUN immediately here, since the
- * target goes to BUS FREE and doesn't send us another
- * message (COMMAND_COMPLETE or the like)
- */
-#ifdef SUPPORT_TAGS
- cmd_free_tag(tmp);
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- local_irq_restore(flags);
- tmp->scsi_done(tmp);
- falcon_release_lock_if_possible(hostdata);
- return SCSI_ABORT_SUCCESS;
- }
- }
- }
- }
-
- /*
- * Case 5 : If we reached this point, the command was not found in any of
- * the queues.
- *
- * We probably reached this point because of an unlikely race condition
- * between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case something really
- * broke.
- */
-
- local_irq_restore(flags);
- printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO);
-
- /* Maybe it is sufficient just to release the ST-DMA lock... (if
- * possible at all) At least, we should check if the lock could be
- * released after the abort, in case it is kept due to some bug.
- */
- falcon_release_lock_if_possible(hostdata);
-
- return SCSI_ABORT_NOT_RUNNING;
-}
-
-
-/*
- * Function : int NCR5380_reset (Scsi_Cmnd *cmd)
- *
- * Purpose : reset the SCSI bus.
- *
- * Returns : SCSI_RESET_WAKEUP
- *
- */
-
-static int NCR5380_bus_reset(Scsi_Cmnd *cmd)
-{
- SETUP_HOSTDATA(cmd->device->host);
- int i;
- unsigned long flags;
-#if 1
- Scsi_Cmnd *connected, *disconnected_queue;
-#endif
-
- if (!IS_A_TT() && !falcon_got_lock)
- printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_reset\n",
- H_NO(cmd));
-
- NCR5380_print_status(cmd->device->host);
-
- /* get in phase */
- NCR5380_write(TARGET_COMMAND_REG,
- PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG)));
- /* assert RST */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
- udelay(40);
- /* reset NCR registers */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(TARGET_COMMAND_REG, 0);
- NCR5380_write(SELECT_ENABLE_REG, 0);
- /* ++roman: reset interrupt condition! otherwise no interrupts don't get
- * through anymore ... */
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-
-#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */
- /* XXX see below XXX */
-
- /* MSch: old-style reset: actually abort all command processing here */
-
- /* After the reset, there are no more connected or disconnected commands
- * and no busy units; to avoid problems with re-inserting the commands
- * into the issue_queue (via scsi_done()), the aborted commands are
- * remembered in local variables first.
- */
- local_irq_save(flags);
- connected = (Scsi_Cmnd *)hostdata->connected;
- hostdata->connected = NULL;
- disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue;
- hostdata->disconnected_queue = NULL;
-#ifdef SUPPORT_TAGS
- free_all_tags();
-#endif
- for (i = 0; i < 8; ++i)
- hostdata->busy[i] = 0;
-#ifdef REAL_DMA
- hostdata->dma_len = 0;
-#endif
- local_irq_restore(flags);
-
- /* In order to tell the mid-level code which commands were aborted,
- * set the command status to DID_RESET and call scsi_done() !!!
- * This ultimately aborts processing of these commands in the mid-level.
- */
-
- if ((cmd = connected)) {
- ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
- cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
- cmd->scsi_done(cmd);
- }
-
- for (i = 0; (cmd = disconnected_queue); ++i) {
- disconnected_queue = NEXT(cmd);
- SET_NEXT(cmd, NULL);
- cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
- cmd->scsi_done(cmd);
- }
- if (i > 0)
- ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i);
-
- /* The Falcon lock should be released after a reset...
- */
- /* ++guenther: moved to atari_scsi_reset(), to prevent a race between
- * unlocking and enabling dma interrupt.
- */
-/* falcon_release_lock_if_possible( hostdata );*/
-
- /* since all commands have been explicitly terminated, we need to tell
- * the midlevel code that the reset was SUCCESSFUL, and there is no
- * need to 'wake up' the commands by a request_sense
- */
- return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
-#else /* 1 */
-
- /* MSch: new-style reset handling: let the mid-level do what it can */
-
- /* ++guenther: MID-LEVEL IS STILL BROKEN.
- * Mid-level is supposed to requeue all commands that were active on the
- * various low-level queues. In fact it does this, but that's not enough
- * because all these commands are subject to timeout. And if a timeout
- * happens for any removed command, *_abort() is called but all queues
- * are now empty. Abort then gives up the falcon lock, which is fatal,
- * since the mid-level will queue more commands and must have the lock
- * (it's all happening inside timer interrupt handler!!).
- * Even worse, abort will return NOT_RUNNING for all those commands not
- * on any queue, so they won't be retried ...
- *
- * Conclusion: either scsi.c disables timeout for all resetted commands
- * immediately, or we lose! As of linux-2.0.20 it doesn't.
- */
-
- /* After the reset, there are no more connected or disconnected commands
- * and no busy units; so clear the low-level status here to avoid
- * conflicts when the mid-level code tries to wake up the affected
- * commands!
- */
-
- if (hostdata->issue_queue)
- ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd));
- if (hostdata->connected)
- ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
- if (hostdata->disconnected_queue)
- ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd));
-
- local_irq_save(flags);
- hostdata->issue_queue = NULL;
- hostdata->connected = NULL;
- hostdata->disconnected_queue = NULL;
-#ifdef SUPPORT_TAGS
- free_all_tags();
-#endif
- for (i = 0; i < 8; ++i)
- hostdata->busy[i] = 0;
-#ifdef REAL_DMA
- hostdata->dma_len = 0;
-#endif
- local_irq_restore(flags);
-
- /* we did no complete reset of all commands, so a wakeup is required */
- return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET;
-#endif /* 1 */
-}
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c
index 3e8658e2f154..85055677666c 100644
--- a/drivers/scsi/atari_scsi.c
+++ b/drivers/scsi/atari_scsi.c
@@ -14,100 +14,69 @@
*
*/
-
-/**************************************************************************/
-/* */
-/* Notes for Falcon SCSI: */
-/* ---------------------- */
-/* */
-/* Since the Falcon SCSI uses the ST-DMA chip, that is shared among */
-/* several device drivers, locking and unlocking the access to this */
-/* chip is required. But locking is not possible from an interrupt, */
-/* since it puts the process to sleep if the lock is not available. */
-/* This prevents "late" locking of the DMA chip, i.e. locking it just */
-/* before using it, since in case of disconnection-reconnection */
-/* commands, the DMA is started from the reselection interrupt. */
-/* */
-/* Two possible schemes for ST-DMA-locking would be: */
-/* 1) The lock is taken for each command separately and disconnecting */
-/* is forbidden (i.e. can_queue = 1). */
-/* 2) The DMA chip is locked when the first command comes in and */
-/* released when the last command is finished and all queues are */
-/* empty. */
-/* The first alternative would result in bad performance, since the */
-/* interleaving of commands would not be used. The second is unfair to */
-/* other drivers using the ST-DMA, because the queues will seldom be */
-/* totally empty if there is a lot of disk traffic. */
-/* */
-/* For this reasons I decided to employ a more elaborate scheme: */
-/* - First, we give up the lock every time we can (for fairness), this */
-/* means every time a command finishes and there are no other commands */
-/* on the disconnected queue. */
-/* - If there are others waiting to lock the DMA chip, we stop */
-/* issuing commands, i.e. moving them onto the issue queue. */
-/* Because of that, the disconnected queue will run empty in a */
-/* while. Instead we go to sleep on a 'fairness_queue'. */
-/* - If the lock is released, all processes waiting on the fairness */
-/* queue will be woken. The first of them tries to re-lock the DMA, */
-/* the others wait for the first to finish this task. After that, */
-/* they can all run on and do their commands... */
-/* This sounds complicated (and it is it :-(), but it seems to be a */
-/* good compromise between fairness and performance: As long as no one */
-/* else wants to work with the ST-DMA chip, SCSI can go along as */
-/* usual. If now someone else comes, this behaviour is changed to a */
-/* "fairness mode": just already initiated commands are finished and */
-/* then the lock is released. The other one waiting will probably win */
-/* the race for locking the DMA, since it was waiting for longer. And */
-/* after it has finished, SCSI can go ahead again. Finally: I hope I */
-/* have not produced any deadlock possibilities! */
-/* */
-/**************************************************************************/
-
-
+/*
+ * Notes for Falcon SCSI DMA
+ *
+ * The 5380 device is one of several that all share the DMA chip. Hence
+ * "locking" and "unlocking" access to this chip is required.
+ *
+ * Two possible schemes for ST DMA acquisition by atari_scsi are:
+ * 1) The lock is taken for each command separately (i.e. can_queue == 1).
+ * 2) The lock is taken when the first command arrives and released
+ * when the last command is finished (i.e. can_queue > 1).
+ *
+ * The first alternative limits SCSI bus utilization, since interleaving
+ * commands is not possible. The second gives better performance but is
+ * unfair to other drivers needing to use the ST DMA chip. In order to
+ * allow the IDE and floppy drivers equal access to the ST DMA chip
+ * the default is can_queue == 1.
+ */
#include <linux/module.h>
-
-#define NDEBUG (0)
-
-#define NDEBUG_ABORT 0x00100000
-#define NDEBUG_TAGS 0x00200000
-#define NDEBUG_MERGING 0x00400000
-
-#define AUTOSENSE
-/* For the Atari version, use only polled IO or REAL_DMA */
-#define REAL_DMA
-/* Support tagged queuing? (on devices that are able to... :-) */
-#define SUPPORT_TAGS
-#define MAX_TAGS 32
-
#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/ctype.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/nvram.h>
#include <linux/bitops.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
#include <asm/setup.h>
#include <asm/atarihw.h>
#include <asm/atariints.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/irq.h>
-#include <asm/traps.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "atari_scsi.h"
-#include "NCR5380.h"
#include <asm/atari_stdma.h>
#include <asm/atari_stram.h>
#include <asm/io.h>
-#include <linux/stat.h>
+#include <scsi/scsi_host.h>
+
+#define DMA_MIN_SIZE 32
+
+/* Definitions for the core NCR5380 driver. */
+
+#define NCR5380_implementation_fields /* none */
+
+static u8 (*atari_scsi_reg_read)(unsigned int);
+static void (*atari_scsi_reg_write)(unsigned int, u8);
+
+#define NCR5380_read(reg) atari_scsi_reg_read(reg)
+#define NCR5380_write(reg, value) atari_scsi_reg_write(reg, value)
+
+#define NCR5380_queue_command atari_scsi_queue_command
+#define NCR5380_abort atari_scsi_abort
+#define NCR5380_info atari_scsi_info
+
+#define NCR5380_dma_xfer_len atari_scsi_dma_xfer_len
+#define NCR5380_dma_recv_setup atari_scsi_dma_recv_setup
+#define NCR5380_dma_send_setup atari_scsi_dma_send_setup
+#define NCR5380_dma_residual atari_scsi_dma_residual
+
+#define NCR5380_acquire_dma_irq(instance) falcon_get_lock(instance)
+#define NCR5380_release_dma_irq(instance) falcon_release_lock()
+
+#include "NCR5380.h"
+
#define IS_A_TT() ATARIHW_PRESENT(TT_SCSI)
@@ -154,65 +123,8 @@ static inline unsigned long SCSI_DMA_GETADR(void)
return adr;
}
-static inline void ENABLE_IRQ(void)
-{
- if (IS_A_TT())
- atari_enable_irq(IRQ_TT_MFP_SCSI);
- else
- atari_enable_irq(IRQ_MFP_FSCSI);
-}
-
-static inline void DISABLE_IRQ(void)
-{
- if (IS_A_TT())
- atari_disable_irq(IRQ_TT_MFP_SCSI);
- else
- atari_disable_irq(IRQ_MFP_FSCSI);
-}
-
-
-#define HOSTDATA_DMALEN (((struct NCR5380_hostdata *) \
- (atari_scsi_host->hostdata))->dma_len)
-
-/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
- * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more
- * need ten times the standard value... */
-#ifndef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
-#define AFTER_RESET_DELAY (HZ/2)
-#else
-#define AFTER_RESET_DELAY (5*HZ/2)
-#endif
-
-/***************************** Prototypes *****************************/
-
-#ifdef REAL_DMA
-static int scsi_dma_is_ignored_buserr(unsigned char dma_stat);
static void atari_scsi_fetch_restbytes(void);
-static long atari_scsi_dma_residual(struct Scsi_Host *instance);
-static int falcon_classify_cmd(Scsi_Cmnd *cmd);
-static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
- Scsi_Cmnd *cmd, int write_flag);
-#endif
-static irqreturn_t scsi_tt_intr(int irq, void *dummy);
-static irqreturn_t scsi_falcon_intr(int irq, void *dummy);
-static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata);
-static void falcon_get_lock(void);
-#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
-static void atari_scsi_reset_boot(void);
-#endif
-static unsigned char atari_scsi_tt_reg_read(unsigned char reg);
-static void atari_scsi_tt_reg_write(unsigned char reg, unsigned char value);
-static unsigned char atari_scsi_falcon_reg_read(unsigned char reg);
-static void atari_scsi_falcon_reg_write(unsigned char reg, unsigned char value);
-
-/************************* End of Prototypes **************************/
-
-
-static struct Scsi_Host *atari_scsi_host;
-static unsigned char (*atari_scsi_reg_read)(unsigned char reg);
-static void (*atari_scsi_reg_write)(unsigned char reg, unsigned char value);
-
-#ifdef REAL_DMA
+
static unsigned long atari_dma_residual, atari_dma_startaddr;
static short atari_dma_active;
/* pointer to the dribble buffer */
@@ -231,9 +143,6 @@ static char *atari_dma_orig_addr;
/* mask for address bits that can't be used with the ST-DMA */
static unsigned long atari_dma_stram_mask;
#define STRAM_ADDR(a) (((a) & atari_dma_stram_mask) == 0)
-/* number of bytes to cut from a transfer to handle NCR overruns */
-static int atari_read_overruns;
-#endif
static int setup_can_queue = -1;
module_param(setup_can_queue, int, 0);
@@ -241,16 +150,12 @@ static int setup_cmd_per_lun = -1;
module_param(setup_cmd_per_lun, int, 0);
static int setup_sg_tablesize = -1;
module_param(setup_sg_tablesize, int, 0);
-#ifdef SUPPORT_TAGS
-static int setup_use_tagged_queuing = -1;
-module_param(setup_use_tagged_queuing, int, 0);
-#endif
static int setup_hostid = -1;
module_param(setup_hostid, int, 0);
+static int setup_toshiba_delay = -1;
+module_param(setup_toshiba_delay, int, 0);
-#if defined(REAL_DMA)
-
static int scsi_dma_is_ignored_buserr(unsigned char dma_stat)
{
int i;
@@ -273,48 +178,16 @@ static int scsi_dma_is_ignored_buserr(unsigned char dma_stat)
}
-#if 0
-/* Dead code... wasn't called anyway :-) and causes some trouble, because at
- * end-of-DMA, both SCSI ints are triggered simultaneously, so the NCR int has
- * to clear the DMA int pending bit before it allows other level 6 interrupts.
- */
-static void scsi_dma_buserr(int irq, void *dummy)
-{
- unsigned char dma_stat = tt_scsi_dma.dma_ctrl;
-
- /* Don't do anything if a NCR interrupt is pending. Probably it's just
- * masked... */
- if (atari_irq_pending(IRQ_TT_MFP_SCSI))
- return;
-
- printk("Bad SCSI DMA interrupt! dma_addr=0x%08lx dma_stat=%02x dma_cnt=%08lx\n",
- SCSI_DMA_READ_P(dma_addr), dma_stat, SCSI_DMA_READ_P(dma_cnt));
- if (dma_stat & 0x80) {
- if (!scsi_dma_is_ignored_buserr(dma_stat))
- printk("SCSI DMA bus error -- bad DMA programming!\n");
- } else {
- /* Under normal circumstances we never should get to this point,
- * since both interrupts are triggered simultaneously and the 5380
- * int has higher priority. When this irq is handled, that DMA
- * interrupt is cleared. So a warning message is printed here.
- */
- printk("SCSI DMA intr ?? -- this shouldn't happen!\n");
- }
-}
-#endif
-
-#endif
-
-
-static irqreturn_t scsi_tt_intr(int irq, void *dummy)
+static irqreturn_t scsi_tt_intr(int irq, void *dev)
{
-#ifdef REAL_DMA
+ struct Scsi_Host *instance = dev;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
int dma_stat;
dma_stat = tt_scsi_dma.dma_ctrl;
- INT_PRINTK("scsi%d: NCR5380 interrupt, DMA status = %02x\n",
- atari_scsi_host->host_no, dma_stat & 0xff);
+ dsprintk(NDEBUG_INTR, instance, "NCR5380 interrupt, DMA status = %02x\n",
+ dma_stat & 0xff);
/* Look if it was the DMA that has interrupted: First possibility
* is that a bus error occurred...
@@ -337,9 +210,10 @@ static irqreturn_t scsi_tt_intr(int irq, void *dummy)
* data reg!
*/
if ((dma_stat & 0x02) && !(dma_stat & 0x40)) {
- atari_dma_residual = HOSTDATA_DMALEN - (SCSI_DMA_READ_P(dma_addr) - atari_dma_startaddr);
+ atari_dma_residual = hostdata->dma_len -
+ (SCSI_DMA_READ_P(dma_addr) - atari_dma_startaddr);
- DMA_PRINTK("SCSI DMA: There are %ld residual bytes.\n",
+ dprintk(NDEBUG_DMA, "SCSI DMA: There are %ld residual bytes.\n",
atari_dma_residual);
if ((signed int)atari_dma_residual < 0)
@@ -370,7 +244,7 @@ static irqreturn_t scsi_tt_intr(int irq, void *dummy)
* other command. These shouldn't disconnect anyway.
*/
if (atari_dma_residual & 0x1ff) {
- DMA_PRINTK("SCSI DMA: DMA bug corrected, "
+ dprintk(NDEBUG_DMA, "SCSI DMA: DMA bug corrected, "
"difference %ld bytes\n",
512 - (atari_dma_residual & 0x1ff));
atari_dma_residual = (atari_dma_residual + 511) & ~0x1ff;
@@ -387,21 +261,16 @@ static irqreturn_t scsi_tt_intr(int irq, void *dummy)
tt_scsi_dma.dma_ctrl = 0;
}
-#endif /* REAL_DMA */
+ NCR5380_intr(irq, dev);
- NCR5380_intr(irq, dummy);
-
-#if 0
- /* To be sure the int is not masked */
- atari_enable_irq(IRQ_TT_MFP_SCSI);
-#endif
return IRQ_HANDLED;
}
-static irqreturn_t scsi_falcon_intr(int irq, void *dummy)
+static irqreturn_t scsi_falcon_intr(int irq, void *dev)
{
-#ifdef REAL_DMA
+ struct Scsi_Host *instance = dev;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
int dma_stat;
/* Turn off DMA and select sector counter register before
@@ -436,8 +305,8 @@ static irqreturn_t scsi_falcon_intr(int irq, void *dummy)
printk(KERN_ERR "SCSI DMA error: %ld bytes lost in "
"ST-DMA fifo\n", transferred & 15);
- atari_dma_residual = HOSTDATA_DMALEN - transferred;
- DMA_PRINTK("SCSI DMA: There are %ld residual bytes.\n",
+ atari_dma_residual = hostdata->dma_len - transferred;
+ dprintk(NDEBUG_DMA, "SCSI DMA: There are %ld residual bytes.\n",
atari_dma_residual);
} else
atari_dma_residual = 0;
@@ -448,18 +317,16 @@ static irqreturn_t scsi_falcon_intr(int irq, void *dummy)
* data to the original destination address.
*/
memcpy(atari_dma_orig_addr, phys_to_virt(atari_dma_startaddr),
- HOSTDATA_DMALEN - atari_dma_residual);
+ hostdata->dma_len - atari_dma_residual);
atari_dma_orig_addr = NULL;
}
-#endif /* REAL_DMA */
+ NCR5380_intr(irq, dev);
- NCR5380_intr(irq, dummy);
return IRQ_HANDLED;
}
-#ifdef REAL_DMA
static void atari_scsi_fetch_restbytes(void)
{
int nr;
@@ -473,401 +340,92 @@ static void atari_scsi_fetch_restbytes(void)
/* there are 'nr' bytes left for the last long address
before the DMA pointer */
phys_dst ^= nr;
- DMA_PRINTK("SCSI DMA: there are %d rest bytes for phys addr 0x%08lx",
+ dprintk(NDEBUG_DMA, "SCSI DMA: there are %d rest bytes for phys addr 0x%08lx",
nr, phys_dst);
/* The content of the DMA pointer is a physical address! */
dst = phys_to_virt(phys_dst);
- DMA_PRINTK(" = virt addr %p\n", dst);
+ dprintk(NDEBUG_DMA, " = virt addr %p\n", dst);
for (src = (char *)&tt_scsi_dma.dma_restdata; nr != 0; --nr)
*dst++ = *src++;
}
}
-#endif /* REAL_DMA */
-
-static int falcon_got_lock = 0;
-static DECLARE_WAIT_QUEUE_HEAD(falcon_fairness_wait);
-static int falcon_trying_lock = 0;
-static DECLARE_WAIT_QUEUE_HEAD(falcon_try_wait);
-static int falcon_dont_release = 0;
/* This function releases the lock on the DMA chip if there is no
- * connected command and the disconnected queue is empty. On
- * releasing, instances of falcon_get_lock are awoken, that put
- * themselves to sleep for fairness. They can now try to get the lock
- * again (but others waiting longer more probably will win).
+ * connected command and the disconnected queue is empty.
*/
-static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata)
+static void falcon_release_lock(void)
{
- unsigned long flags;
-
if (IS_A_TT())
return;
- local_irq_save(flags);
-
- if (falcon_got_lock && !hostdata->disconnected_queue &&
- !hostdata->issue_queue && !hostdata->connected) {
-
- if (falcon_dont_release) {
-#if 0
- printk("WARNING: Lock release not allowed. Ignored\n");
-#endif
- local_irq_restore(flags);
- return;
- }
- falcon_got_lock = 0;
+ if (stdma_is_locked_by(scsi_falcon_intr))
stdma_release();
- wake_up(&falcon_fairness_wait);
- }
-
- local_irq_restore(flags);
}
/* This function manages the locking of the ST-DMA.
* If the DMA isn't locked already for SCSI, it tries to lock it by
* calling stdma_lock(). But if the DMA is locked by the SCSI code and
* there are other drivers waiting for the chip, we do not issue the
- * command immediately but wait on 'falcon_fairness_queue'. We will be
- * waked up when the DMA is unlocked by some SCSI interrupt. After that
- * we try to get the lock again.
- * But we must be prepared that more than one instance of
- * falcon_get_lock() is waiting on the fairness queue. They should not
- * try all at once to call stdma_lock(), one is enough! For that, the
- * first one sets 'falcon_trying_lock', others that see that variable
- * set wait on the queue 'falcon_try_wait'.
- * Complicated, complicated.... Sigh...
+ * command immediately but tell the SCSI mid-layer to defer.
*/
-static void falcon_get_lock(void)
+static int falcon_get_lock(struct Scsi_Host *instance)
{
- unsigned long flags;
-
if (IS_A_TT())
- return;
+ return 1;
- local_irq_save(flags);
+ if (stdma_is_locked_by(scsi_falcon_intr))
+ return 1;
- while (!in_irq() && falcon_got_lock && stdma_others_waiting())
- sleep_on(&falcon_fairness_wait);
-
- while (!falcon_got_lock) {
- if (in_irq())
- panic("Falcon SCSI hasn't ST-DMA lock in interrupt");
- if (!falcon_trying_lock) {
- falcon_trying_lock = 1;
- stdma_lock(scsi_falcon_intr, NULL);
- falcon_got_lock = 1;
- falcon_trying_lock = 0;
- wake_up(&falcon_try_wait);
- } else {
- sleep_on(&falcon_try_wait);
- }
- }
-
- local_irq_restore(flags);
- if (!falcon_got_lock)
- panic("Falcon SCSI: someone stole the lock :-(\n");
-}
-
-
-int __init atari_scsi_detect(struct scsi_host_template *host)
-{
- static int called = 0;
- struct Scsi_Host *instance;
-
- if (!MACH_IS_ATARI ||
- (!ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(TT_SCSI)) ||
- called)
- return 0;
-
- host->proc_name = "Atari";
-
- atari_scsi_reg_read = IS_A_TT() ? atari_scsi_tt_reg_read :
- atari_scsi_falcon_reg_read;
- atari_scsi_reg_write = IS_A_TT() ? atari_scsi_tt_reg_write :
- atari_scsi_falcon_reg_write;
-
- /* setup variables */
- host->can_queue =
- (setup_can_queue > 0) ? setup_can_queue :
- IS_A_TT() ? ATARI_TT_CAN_QUEUE : ATARI_FALCON_CAN_QUEUE;
- host->cmd_per_lun =
- (setup_cmd_per_lun > 0) ? setup_cmd_per_lun :
- IS_A_TT() ? ATARI_TT_CMD_PER_LUN : ATARI_FALCON_CMD_PER_LUN;
- /* Force sg_tablesize to 0 on a Falcon! */
- host->sg_tablesize =
- !IS_A_TT() ? ATARI_FALCON_SG_TABLESIZE :
- (setup_sg_tablesize >= 0) ? setup_sg_tablesize : ATARI_TT_SG_TABLESIZE;
-
- if (setup_hostid >= 0)
- host->this_id = setup_hostid;
- else {
- /* use 7 as default */
- host->this_id = 7;
- /* Test if a host id is set in the NVRam */
- if (ATARIHW_PRESENT(TT_CLK) && nvram_check_checksum()) {
- unsigned char b = nvram_read_byte( 14 );
- /* Arbitration enabled? (for TOS) If yes, use configured host ID */
- if (b & 0x80)
- host->this_id = b & 7;
- }
- }
-
-#ifdef SUPPORT_TAGS
- if (setup_use_tagged_queuing < 0)
- setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING;
-#endif
-#ifdef REAL_DMA
- /* If running on a Falcon and if there's TT-Ram (i.e., more than one
- * memory block, since there's always ST-Ram in a Falcon), then allocate a
- * STRAM_BUFFER_SIZE byte dribble buffer for transfers from/to alternative
- * Ram.
- */
- if (MACH_IS_ATARI && ATARIHW_PRESENT(ST_SCSI) &&
- !ATARIHW_PRESENT(EXTD_DMA) && m68k_num_memory > 1) {
- atari_dma_buffer = atari_stram_alloc(STRAM_BUFFER_SIZE, "SCSI");
- if (!atari_dma_buffer) {
- printk(KERN_ERR "atari_scsi_detect: can't allocate ST-RAM "
- "double buffer\n");
- return 0;
- }
- atari_dma_phys_buffer = virt_to_phys(atari_dma_buffer);
- atari_dma_orig_addr = 0;
- }
-#endif
- instance = scsi_register(host, sizeof(struct NCR5380_hostdata));
- if (instance == NULL) {
- atari_stram_free(atari_dma_buffer);
- atari_dma_buffer = 0;
- return 0;
- }
- atari_scsi_host = instance;
- /*
- * Set irq to 0, to avoid that the mid-level code disables our interrupt
- * during queue_command calls. This is completely unnecessary, and even
- * worse causes bad problems on the Falcon, where the int is shared with
- * IDE and floppy!
- */
- instance->irq = 0;
-
-#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
- atari_scsi_reset_boot();
-#endif
- NCR5380_init(instance, 0);
-
- if (IS_A_TT()) {
-
- /* This int is actually "pseudo-slow", i.e. it acts like a slow
- * interrupt after having cleared the pending flag for the DMA
- * interrupt. */
- if (request_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr, IRQ_TYPE_SLOW,
- "SCSI NCR5380", instance)) {
- printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting",IRQ_TT_MFP_SCSI);
- scsi_unregister(atari_scsi_host);
- atari_stram_free(atari_dma_buffer);
- atari_dma_buffer = 0;
- return 0;
- }
- tt_mfp.active_edge |= 0x80; /* SCSI int on L->H */
-#ifdef REAL_DMA
- tt_scsi_dma.dma_ctrl = 0;
- atari_dma_residual = 0;
-
- if (MACH_IS_MEDUSA) {
- /* While the read overruns (described by Drew Eckhardt in
- * NCR5380.c) never happened on TTs, they do in fact on the Medusa
- * (This was the cause why SCSI didn't work right for so long
- * there.) Since handling the overruns slows down a bit, I turned
- * the #ifdef's into a runtime condition.
- *
- * In principle it should be sufficient to do max. 1 byte with
- * PIO, but there is another problem on the Medusa with the DMA
- * rest data register. So 'atari_read_overruns' is currently set
- * to 4 to avoid having transfers that aren't a multiple of 4. If
- * the rest data bug is fixed, this can be lowered to 1.
- */
- atari_read_overruns = 4;
- }
-#endif /*REAL_DMA*/
- } else { /* ! IS_A_TT */
-
- /* Nothing to do for the interrupt: the ST-DMA is initialized
- * already by atari_init_INTS()
- */
-
-#ifdef REAL_DMA
- atari_dma_residual = 0;
- atari_dma_active = 0;
- atari_dma_stram_mask = (ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000
- : 0xff000000);
-#endif
- }
-
- printk(KERN_INFO "scsi%d: options CAN_QUEUE=%d CMD_PER_LUN=%d SCAT-GAT=%d "
-#ifdef SUPPORT_TAGS
- "TAGGED-QUEUING=%s "
-#endif
- "HOSTID=%d",
- instance->host_no, instance->hostt->can_queue,
- instance->hostt->cmd_per_lun,
- instance->hostt->sg_tablesize,
-#ifdef SUPPORT_TAGS
- setup_use_tagged_queuing ? "yes" : "no",
-#endif
- instance->hostt->this_id );
- NCR5380_print_options(instance);
- printk("\n");
-
- called = 1;
- return 1;
+ /* stdma_lock() may sleep which means it can't be used here */
+ return stdma_try_lock(scsi_falcon_intr, instance);
}
-int atari_scsi_release(struct Scsi_Host *sh)
-{
- if (IS_A_TT())
- free_irq(IRQ_TT_MFP_SCSI, sh);
- if (atari_dma_buffer)
- atari_stram_free(atari_dma_buffer);
- return 1;
-}
-
-void __init atari_scsi_setup(char *str, int *ints)
+#ifndef MODULE
+static int __init atari_scsi_setup(char *str)
{
/* Format of atascsi parameter is:
* atascsi=<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>
- * Defaults depend on TT or Falcon, hostid determined at run time.
+ * Defaults depend on TT or Falcon, determined at run time.
* Negative values mean don't change.
*/
+ int ints[8];
+
+ get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] < 1) {
printk("atari_scsi_setup: no arguments!\n");
- return;
- }
-
- if (ints[0] >= 1) {
- if (ints[1] > 0)
- /* no limits on this, just > 0 */
- setup_can_queue = ints[1];
- }
- if (ints[0] >= 2) {
- if (ints[2] > 0)
- setup_cmd_per_lun = ints[2];
- }
- if (ints[0] >= 3) {
- if (ints[3] >= 0) {
- setup_sg_tablesize = ints[3];
- /* Must be <= SG_ALL (255) */
- if (setup_sg_tablesize > SG_ALL)
- setup_sg_tablesize = SG_ALL;
- }
- }
- if (ints[0] >= 4) {
- /* Must be between 0 and 7 */
- if (ints[4] >= 0 && ints[4] <= 7)
- setup_hostid = ints[4];
- else if (ints[4] > 7)
- printk("atari_scsi_setup: invalid host ID %d !\n", ints[4]);
- }
-#ifdef SUPPORT_TAGS
- if (ints[0] >= 5) {
- if (ints[5] >= 0)
- setup_use_tagged_queuing = !!ints[5];
- }
-#endif
-}
-
-int atari_scsi_bus_reset(Scsi_Cmnd *cmd)
-{
- int rv;
- struct NCR5380_hostdata *hostdata =
- (struct NCR5380_hostdata *)cmd->device->host->hostdata;
-
- /* For doing the reset, SCSI interrupts must be disabled first,
- * since the 5380 raises its IRQ line while _RST is active and we
- * can't disable interrupts completely, since we need the timer.
- */
- /* And abort a maybe active DMA transfer */
- if (IS_A_TT()) {
- atari_turnoff_irq(IRQ_TT_MFP_SCSI);
-#ifdef REAL_DMA
- tt_scsi_dma.dma_ctrl = 0;
-#endif /* REAL_DMA */
- } else {
- atari_turnoff_irq(IRQ_MFP_FSCSI);
-#ifdef REAL_DMA
- st_dma.dma_mode_status = 0x90;
- atari_dma_active = 0;
- atari_dma_orig_addr = NULL;
-#endif /* REAL_DMA */
- }
-
- rv = NCR5380_bus_reset(cmd);
-
- /* Re-enable ints */
- if (IS_A_TT()) {
- atari_turnon_irq(IRQ_TT_MFP_SCSI);
- } else {
- atari_turnon_irq(IRQ_MFP_FSCSI);
+ return 0;
}
- if ((rv & SCSI_RESET_ACTION) == SCSI_RESET_SUCCESS)
- falcon_release_lock_if_possible(hostdata);
-
- return rv;
-}
-
-
-#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
-static void __init atari_scsi_reset_boot(void)
-{
- unsigned long end;
+ if (ints[0] >= 1)
+ setup_can_queue = ints[1];
+ if (ints[0] >= 2)
+ setup_cmd_per_lun = ints[2];
+ if (ints[0] >= 3)
+ setup_sg_tablesize = ints[3];
+ if (ints[0] >= 4)
+ setup_hostid = ints[4];
+ /* ints[5] (use_tagged_queuing) is ignored */
+ /* ints[6] (use_pdma) is ignored */
+ if (ints[0] >= 7)
+ setup_toshiba_delay = ints[7];
- /*
- * Do a SCSI reset to clean up the bus during initialization. No messing
- * with the queues, interrupts, or locks necessary here.
- */
-
- printk("Atari SCSI: resetting the SCSI bus...");
-
- /* get in phase */
- NCR5380_write(TARGET_COMMAND_REG,
- PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG)));
-
- /* assert RST */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
- /* The min. reset hold time is 25us, so 40us should be enough */
- udelay(50);
- /* reset RST and interrupt */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-
- end = jiffies + AFTER_RESET_DELAY;
- while (time_before(jiffies, end))
- barrier();
-
- printk(" done\n");
-}
-#endif
-
-
-const char *atari_scsi_info(struct Scsi_Host *host)
-{
- /* atari_scsi_detect() is verbose enough... */
- static const char string[] = "Atari native SCSI";
- return string;
+ return 1;
}
+__setup("atascsi=", atari_scsi_setup);
+#endif /* !MODULE */
-#if defined(REAL_DMA)
-
-unsigned long atari_scsi_dma_setup(struct Scsi_Host *instance, void *data,
- unsigned long count, int dir)
+static unsigned long atari_scsi_dma_setup(struct NCR5380_hostdata *hostdata,
+ void *data, unsigned long count,
+ int dir)
{
unsigned long addr = virt_to_phys(data);
- DMA_PRINTK("scsi%d: setting up dma, data = %p, phys = %lx, count = %ld, "
- "dir = %d\n", instance->host_no, data, addr, count, dir);
+ dprintk(NDEBUG_DMA, "scsi%d: setting up dma, data = %p, phys = %lx, count = %ld, dir = %d\n",
+ hostdata->host->host_no, data, addr, count, dir);
if (!IS_A_TT() && !STRAM_ADDR(addr)) {
/* If we have a non-DMAable address on a Falcon, use the dribble
@@ -895,9 +453,6 @@ unsigned long atari_scsi_dma_setup(struct Scsi_Host *instance, void *data,
*/
dma_cache_maintenance(addr, count, dir);
- if (count == 0)
- printk(KERN_NOTICE "SCSI warning: DMA programmed for 0 bytes !\n");
-
if (IS_A_TT()) {
tt_scsi_dma.dma_ctrl = dir;
SCSI_DMA_WRITE_P(dma_addr, addr);
@@ -927,8 +482,19 @@ unsigned long atari_scsi_dma_setup(struct Scsi_Host *instance, void *data,
return count;
}
+static inline int atari_scsi_dma_recv_setup(struct NCR5380_hostdata *hostdata,
+ unsigned char *data, int count)
+{
+ return atari_scsi_dma_setup(hostdata, data, count, 0);
+}
+
+static inline int atari_scsi_dma_send_setup(struct NCR5380_hostdata *hostdata,
+ unsigned char *data, int count)
+{
+ return atari_scsi_dma_setup(hostdata, data, count, 1);
+}
-static long atari_scsi_dma_residual(struct Scsi_Host *instance)
+static int atari_scsi_dma_residual(struct NCR5380_hostdata *hostdata)
{
return atari_dma_residual;
}
@@ -938,7 +504,7 @@ static long atari_scsi_dma_residual(struct Scsi_Host *instance)
#define CMD_SURELY_BYTE_MODE 1
#define CMD_MODE_UNKNOWN 2
-static int falcon_classify_cmd(Scsi_Cmnd *cmd)
+static int falcon_classify_cmd(struct scsi_cmnd *cmd)
{
unsigned char opcode = cmd->cmnd[0];
@@ -969,10 +535,14 @@ static int falcon_classify_cmd(Scsi_Cmnd *cmd)
* the overrun problem, so this question is academic :-)
*/
-static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
- Scsi_Cmnd *cmd, int write_flag)
+static int atari_scsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
+ struct scsi_cmnd *cmd)
{
- unsigned long possible_len, limit;
+ int wanted_len = NCR5380_to_ncmd(cmd)->this_residual;
+ int possible_len, limit;
+
+ if (wanted_len < DMA_MIN_SIZE)
+ return 0;
if (IS_A_TT())
/* TT SCSI DMA can transfer arbitrary #bytes */
@@ -1006,7 +576,7 @@ static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
* use the dribble buffer and thus can do only STRAM_BUFFER_SIZE bytes.
*/
- if (write_flag) {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
/* Write operation can always use the DMA, but the transfer size must
* be rounded up to the next multiple of 512 (atari_dma_setup() does
* this).
@@ -1040,22 +610,19 @@ static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
}
/* Last step: apply the hard limit on DMA transfers */
- limit = (atari_dma_buffer && !STRAM_ADDR(virt_to_phys(cmd->SCp.ptr))) ?
+ limit = (atari_dma_buffer && !STRAM_ADDR(virt_to_phys(NCR5380_to_ncmd(cmd)->ptr))) ?
STRAM_BUFFER_SIZE : 255*512;
if (possible_len > limit)
possible_len = limit;
if (possible_len != wanted_len)
- DMA_PRINTK("Sorry, must cut DMA transfer size to %ld bytes "
- "instead of %ld\n", possible_len, wanted_len);
+ dprintk(NDEBUG_DMA, "DMA transfer now %d bytes instead of %d\n",
+ possible_len, wanted_len);
return possible_len;
}
-#endif /* REAL_DMA */
-
-
/* NCR5380 register access functions
*
* There are separate functions for TT and Falcon, because the access
@@ -1063,48 +630,269 @@ static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
* NCR5380_write call these functions via function pointers.
*/
-static unsigned char atari_scsi_tt_reg_read(unsigned char reg)
+static u8 atari_scsi_tt_reg_read(unsigned int reg)
{
return tt_scsi_regp[reg * 2];
}
-static void atari_scsi_tt_reg_write(unsigned char reg, unsigned char value)
+static void atari_scsi_tt_reg_write(unsigned int reg, u8 value)
{
tt_scsi_regp[reg * 2] = value;
}
-static unsigned char atari_scsi_falcon_reg_read(unsigned char reg)
+static u8 atari_scsi_falcon_reg_read(unsigned int reg)
{
- dma_wd.dma_mode_status= (u_short)(0x88 + reg);
- return (u_char)dma_wd.fdc_acces_seccount;
+ unsigned long flags;
+ u8 result;
+
+ reg += 0x88;
+ local_irq_save(flags);
+ dma_wd.dma_mode_status = (u_short)reg;
+ result = (u8)dma_wd.fdc_acces_seccount;
+ local_irq_restore(flags);
+ return result;
}
-static void atari_scsi_falcon_reg_write(unsigned char reg, unsigned char value)
+static void atari_scsi_falcon_reg_write(unsigned int reg, u8 value)
{
- dma_wd.dma_mode_status = (u_short)(0x88 + reg);
+ unsigned long flags;
+
+ reg += 0x88;
+ local_irq_save(flags);
+ dma_wd.dma_mode_status = (u_short)reg;
dma_wd.fdc_acces_seccount = (u_short)value;
+ local_irq_restore(flags);
}
-#include "atari_NCR5380.c"
+#include "NCR5380.c"
+
+static int atari_scsi_host_reset(struct scsi_cmnd *cmd)
+{
+ int rv;
+ unsigned long flags;
+
+ local_irq_save(flags);
-static struct scsi_host_template driver_template = {
- .proc_info = atari_scsi_proc_info,
+ /* Abort a maybe active DMA transfer */
+ if (IS_A_TT()) {
+ tt_scsi_dma.dma_ctrl = 0;
+ } else {
+ if (stdma_is_locked_by(scsi_falcon_intr))
+ st_dma.dma_mode_status = 0x90;
+ atari_dma_active = 0;
+ atari_dma_orig_addr = NULL;
+ }
+
+ rv = NCR5380_host_reset(cmd);
+
+ /* The 5380 raises its IRQ line while _RST is active but the ST DMA
+ * "lock" has been released so this interrupt may end up handled by
+ * floppy or IDE driver (if one of them holds the lock). The NCR5380
+ * interrupt flag has been cleared already.
+ */
+
+ local_irq_restore(flags);
+
+ return rv;
+}
+
+#define DRV_MODULE_NAME "atari_scsi"
+#define PFX DRV_MODULE_NAME ": "
+
+static struct scsi_host_template atari_scsi_template = {
+ .module = THIS_MODULE,
+ .proc_name = DRV_MODULE_NAME,
.name = "Atari native SCSI",
- .detect = atari_scsi_detect,
- .release = atari_scsi_release,
.info = atari_scsi_info,
.queuecommand = atari_scsi_queue_command,
.eh_abort_handler = atari_scsi_abort,
- .eh_bus_reset_handler = atari_scsi_bus_reset,
- .can_queue = 0, /* initialized at run-time */
- .this_id = 0, /* initialized at run-time */
- .sg_tablesize = 0, /* initialized at run-time */
- .cmd_per_lun = 0, /* initialized at run-time */
- .use_clustering = DISABLE_CLUSTERING
+ .eh_host_reset_handler = atari_scsi_host_reset,
+ .this_id = 7,
+ .cmd_per_lun = 2,
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct NCR5380_cmd),
};
+static int __init atari_scsi_probe(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance;
+ int error;
+ struct resource *irq;
+ int host_flags = 0;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq)
+ return -ENODEV;
+
+ if (ATARIHW_PRESENT(TT_SCSI)) {
+ atari_scsi_reg_read = atari_scsi_tt_reg_read;
+ atari_scsi_reg_write = atari_scsi_tt_reg_write;
+ } else {
+ atari_scsi_reg_read = atari_scsi_falcon_reg_read;
+ atari_scsi_reg_write = atari_scsi_falcon_reg_write;
+ }
+
+ if (ATARIHW_PRESENT(TT_SCSI)) {
+ atari_scsi_template.can_queue = 16;
+ atari_scsi_template.sg_tablesize = SG_ALL;
+ } else {
+ atari_scsi_template.can_queue = 1;
+ atari_scsi_template.sg_tablesize = 1;
+ }
+
+ if (setup_can_queue > 0)
+ atari_scsi_template.can_queue = setup_can_queue;
+
+ if (setup_cmd_per_lun > 0)
+ atari_scsi_template.cmd_per_lun = setup_cmd_per_lun;
+
+ /* Don't increase sg_tablesize on Falcon! */
+ if (ATARIHW_PRESENT(TT_SCSI) && setup_sg_tablesize > 0)
+ atari_scsi_template.sg_tablesize = setup_sg_tablesize;
+
+ if (setup_hostid >= 0) {
+ atari_scsi_template.this_id = setup_hostid & 7;
+ } else if (IS_REACHABLE(CONFIG_NVRAM)) {
+ /* Test if a host id is set in the NVRam */
+ if (ATARIHW_PRESENT(TT_CLK)) {
+ unsigned char b;
+ loff_t offset = 16;
+ ssize_t count = nvram_read(&b, 1, &offset);
+
+ /* Arbitration enabled? (for TOS)
+ * If yes, use configured host ID
+ */
+ if ((count == 1) && (b & 0x80))
+ atari_scsi_template.this_id = b & 7;
+ }
+ }
+
+ /* If running on a Falcon and if there's TT-Ram (i.e., more than one
+ * memory block, since there's always ST-Ram in a Falcon), then
+ * allocate a STRAM_BUFFER_SIZE byte dribble buffer for transfers
+ * from/to alternative Ram.
+ */
+ if (ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(EXTD_DMA) &&
+ m68k_realnum_memory > 1) {
+ atari_dma_buffer = atari_stram_alloc(STRAM_BUFFER_SIZE, "SCSI");
+ if (!atari_dma_buffer) {
+ pr_err(PFX "can't allocate ST-RAM double buffer\n");
+ return -ENOMEM;
+ }
+ atari_dma_phys_buffer = atari_stram_to_phys(atari_dma_buffer);
+ atari_dma_orig_addr = NULL;
+ }
+
+ instance = scsi_host_alloc(&atari_scsi_template,
+ sizeof(struct NCR5380_hostdata));
+ if (!instance) {
+ error = -ENOMEM;
+ goto fail_alloc;
+ }
+
+ instance->irq = irq->start;
+
+ host_flags |= IS_A_TT() ? 0 : FLAG_LATE_DMA_SETUP;
+ host_flags |= setup_toshiba_delay > 0 ? FLAG_TOSHIBA_DELAY : 0;
+
+ error = NCR5380_init(instance, host_flags);
+ if (error)
+ goto fail_init;
+
+ if (IS_A_TT()) {
+ error = request_irq(instance->irq, scsi_tt_intr, 0,
+ "NCR5380", instance);
+ if (error) {
+ pr_err(PFX "request irq %d failed, aborting\n",
+ instance->irq);
+ goto fail_irq;
+ }
+ tt_mfp.active_edge |= 0x80; /* SCSI int on L->H */
+
+ tt_scsi_dma.dma_ctrl = 0;
+ atari_dma_residual = 0;
+
+ /* While the read overruns (described by Drew Eckhardt in
+ * NCR5380.c) never happened on TTs, they do in fact on the
+ * Medusa (This was the cause why SCSI didn't work right for
+ * so long there.) Since handling the overruns slows down
+ * a bit, I turned the #ifdef's into a runtime condition.
+ *
+ * In principle it should be sufficient to do max. 1 byte with
+ * PIO, but there is another problem on the Medusa with the DMA
+ * rest data register. So read_overruns is currently set
+ * to 4 to avoid having transfers that aren't a multiple of 4.
+ * If the rest data bug is fixed, this can be lowered to 1.
+ */
+ if (MACH_IS_MEDUSA) {
+ struct NCR5380_hostdata *hostdata =
+ shost_priv(instance);
+
+ hostdata->read_overruns = 4;
+ }
+ } else {
+ /* Nothing to do for the interrupt: the ST-DMA is initialized
+ * already.
+ */
+ atari_dma_residual = 0;
+ atari_dma_active = 0;
+ atari_dma_stram_mask = (ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000
+ : 0xff000000);
+ }
+
+ NCR5380_maybe_reset_bus(instance);
+
+ error = scsi_add_host(instance, NULL);
+ if (error)
+ goto fail_host;
+
+ platform_set_drvdata(pdev, instance);
+
+ scsi_scan_host(instance);
+ return 0;
+
+fail_host:
+ if (IS_A_TT())
+ free_irq(instance->irq, instance);
+fail_irq:
+ NCR5380_exit(instance);
+fail_init:
+ scsi_host_put(instance);
+fail_alloc:
+ if (atari_dma_buffer)
+ atari_stram_free(atari_dma_buffer);
+ return error;
+}
+
+static void __exit atari_scsi_remove(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance = platform_get_drvdata(pdev);
+
+ scsi_remove_host(instance);
+ if (IS_A_TT())
+ free_irq(instance->irq, instance);
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+ if (atari_dma_buffer)
+ atari_stram_free(atari_dma_buffer);
+}
+
+/*
+ * atari_scsi_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver atari_scsi_driver __refdata = {
+ .remove = __exit_p(atari_scsi_remove),
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ },
+};
-#include "scsi_module.c"
+module_platform_driver_probe(atari_scsi_driver, atari_scsi_probe);
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
+MODULE_DESCRIPTION("Atari TT/Falcon NCR5380 SCSI driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/atari_scsi.h b/drivers/scsi/atari_scsi.h
deleted file mode 100644
index efadb8d567c2..000000000000
--- a/drivers/scsi/atari_scsi.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * atari_scsi.h -- Header file for the Atari native SCSI driver
- *
- * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
- *
- * (Loosely based on the work of Robert De Vries' team)
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- */
-
-
-#ifndef ATARI_SCSI_H
-#define ATARI_SCSI_H
-
-/* (I_HAVE_OVERRUNS stuff removed) */
-
-#ifndef ASM
-int atari_scsi_detect (struct scsi_host_template *);
-const char *atari_scsi_info (struct Scsi_Host *);
-int atari_scsi_reset (Scsi_Cmnd *, unsigned int);
-int atari_scsi_release (struct Scsi_Host *);
-
-/* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. Higher
- * values should work, too; try it! (but cmd_per_lun costs memory!) */
-
-/* But there seems to be a bug somewhere that requires CAN_QUEUE to be
- * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since
- * changed CMD_PER_LUN... */
-
-/* Note: The Falcon currently uses 8/1 setting due to unsolved problems with
- * cmd_per_lun != 1 */
-
-#define ATARI_TT_CAN_QUEUE 16
-#define ATARI_TT_CMD_PER_LUN 8
-#define ATARI_TT_SG_TABLESIZE SG_ALL
-
-#define ATARI_FALCON_CAN_QUEUE 8
-#define ATARI_FALCON_CMD_PER_LUN 1
-#define ATARI_FALCON_SG_TABLESIZE SG_NONE
-
-#define DEFAULT_USE_TAGGED_QUEUING 0
-
-
-#define NCR5380_implementation_fields /* none */
-
-#define NCR5380_read(reg) atari_scsi_reg_read( reg )
-#define NCR5380_write(reg, value) atari_scsi_reg_write( reg, value )
-
-#define NCR5380_intr atari_scsi_intr
-#define NCR5380_queue_command atari_scsi_queue_command
-#define NCR5380_abort atari_scsi_abort
-#define NCR5380_proc_info atari_scsi_proc_info
-#define NCR5380_dma_read_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 0)
-#define NCR5380_dma_write_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 1)
-#define NCR5380_dma_residual(inst) atari_scsi_dma_residual( inst )
-#define NCR5380_dma_xfer_len(i,cmd,phase) \
- atari_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
-
-/* former generic SCSI error handling stuff */
-
-#define SCSI_ABORT_SNOOZE 0
-#define SCSI_ABORT_SUCCESS 1
-#define SCSI_ABORT_PENDING 2
-#define SCSI_ABORT_BUSY 3
-#define SCSI_ABORT_NOT_RUNNING 4
-#define SCSI_ABORT_ERROR 5
-
-#define SCSI_RESET_SNOOZE 0
-#define SCSI_RESET_PUNT 1
-#define SCSI_RESET_SUCCESS 2
-#define SCSI_RESET_PENDING 3
-#define SCSI_RESET_WAKEUP 4
-#define SCSI_RESET_NOT_RUNNING 5
-#define SCSI_RESET_ERROR 6
-
-#define SCSI_RESET_SYNCHRONOUS 0x01
-#define SCSI_RESET_ASYNCHRONOUS 0x02
-#define SCSI_RESET_SUGGEST_BUS_RESET 0x04
-#define SCSI_RESET_SUGGEST_HOST_RESET 0x08
-
-#define SCSI_RESET_BUS_RESET 0x100
-#define SCSI_RESET_HOST_RESET 0x200
-#define SCSI_RESET_ACTION 0xff
-
-/* Debugging printk definitions:
- *
- * ARB -> arbitration
- * ASEN -> auto-sense
- * DMA -> DMA
- * HSH -> PIO handshake
- * INF -> information transfer
- * INI -> initialization
- * INT -> interrupt
- * LNK -> linked commands
- * MAIN -> NCR5380_main() control flow
- * NDAT -> no data-out phase
- * NWR -> no write commands
- * PIO -> PIO transfers
- * PDMA -> pseudo DMA (unused on Atari)
- * QU -> queues
- * RSL -> reselections
- * SEL -> selections
- * USL -> usleep cpde (unused on Atari)
- * LBS -> last byte sent (unused on Atari)
- * RSS -> restarting of selections
- * EXT -> extended messages
- * ABRT -> aborting and resetting
- * TAG -> queue tag handling
- * MER -> merging of consec. buffers
- *
- */
-
-#define dprint(flg, format...) \
-({ \
- if (NDEBUG & (flg)) \
- printk(KERN_DEBUG format); \
-})
-
-#define ARB_PRINTK(format, args...) \
- dprint(NDEBUG_ARBITRATION, format , ## args)
-#define ASEN_PRINTK(format, args...) \
- dprint(NDEBUG_AUTOSENSE, format , ## args)
-#define DMA_PRINTK(format, args...) \
- dprint(NDEBUG_DMA, format , ## args)
-#define HSH_PRINTK(format, args...) \
- dprint(NDEBUG_HANDSHAKE, format , ## args)
-#define INF_PRINTK(format, args...) \
- dprint(NDEBUG_INFORMATION, format , ## args)
-#define INI_PRINTK(format, args...) \
- dprint(NDEBUG_INIT, format , ## args)
-#define INT_PRINTK(format, args...) \
- dprint(NDEBUG_INTR, format , ## args)
-#define LNK_PRINTK(format, args...) \
- dprint(NDEBUG_LINKED, format , ## args)
-#define MAIN_PRINTK(format, args...) \
- dprint(NDEBUG_MAIN, format , ## args)
-#define NDAT_PRINTK(format, args...) \
- dprint(NDEBUG_NO_DATAOUT, format , ## args)
-#define NWR_PRINTK(format, args...) \
- dprint(NDEBUG_NO_WRITE, format , ## args)
-#define PIO_PRINTK(format, args...) \
- dprint(NDEBUG_PIO, format , ## args)
-#define PDMA_PRINTK(format, args...) \
- dprint(NDEBUG_PSEUDO_DMA, format , ## args)
-#define QU_PRINTK(format, args...) \
- dprint(NDEBUG_QUEUES, format , ## args)
-#define RSL_PRINTK(format, args...) \
- dprint(NDEBUG_RESELECTION, format , ## args)
-#define SEL_PRINTK(format, args...) \
- dprint(NDEBUG_SELECTION, format , ## args)
-#define USL_PRINTK(format, args...) \
- dprint(NDEBUG_USLEEP, format , ## args)
-#define LBS_PRINTK(format, args...) \
- dprint(NDEBUG_LAST_BYTE_SENT, format , ## args)
-#define RSS_PRINTK(format, args...) \
- dprint(NDEBUG_RESTART_SELECT, format , ## args)
-#define EXT_PRINTK(format, args...) \
- dprint(NDEBUG_EXTENDED, format , ## args)
-#define ABRT_PRINTK(format, args...) \
- dprint(NDEBUG_ABORT, format , ## args)
-#define TAG_PRINTK(format, args...) \
- dprint(NDEBUG_TAGS, format , ## args)
-#define MER_PRINTK(format, args...) \
- dprint(NDEBUG_MERGING, format , ## args)
-
-/* conditional macros for NCR5380_print_{,phase,status} */
-
-#define NCR_PRINT(mask) \
- ((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0)
-
-#define NCR_PRINT_PHASE(mask) \
- ((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0)
-
-#define NCR_PRINT_STATUS(mask) \
- ((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0)
-
-
-#endif /* ndef ASM */
-#endif /* ATARI_SCSI_H */
-
-
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 7e6eca4a125e..df6f40b51deb 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 1997 Wu Ching Chen
* 2.1.x update (C) 1998 Krzysztof G. Baranowski
@@ -30,7 +31,6 @@
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <scsi/scsi.h>
@@ -40,79 +40,132 @@
#include "atp870u.h"
-static struct scsi_host_template atp870u_template;
+static const struct scsi_host_template atp870u_template;
static void send_s870(struct atp_unit *dev,unsigned char c);
-static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c);
-static void tscam_885(void);
+static void atp_is(struct atp_unit *dev, unsigned char c, bool wide_chip,
+ unsigned char lvdmode);
+
+static inline void atp_writeb_base(struct atp_unit *atp, u8 reg, u8 val)
+{
+ outb(val, atp->baseport + reg);
+}
+
+static inline void atp_writew_base(struct atp_unit *atp, u8 reg, u16 val)
+{
+ outw(val, atp->baseport + reg);
+}
+
+static inline void atp_writeb_io(struct atp_unit *atp, u8 channel, u8 reg, u8 val)
+{
+ outb(val, atp->ioport[channel] + reg);
+}
+
+static inline void atp_writew_io(struct atp_unit *atp, u8 channel, u8 reg, u16 val)
+{
+ outw(val, atp->ioport[channel] + reg);
+}
+
+static inline void atp_writeb_pci(struct atp_unit *atp, u8 channel, u8 reg, u8 val)
+{
+ outb(val, atp->pciport[channel] + reg);
+}
+
+static inline void atp_writel_pci(struct atp_unit *atp, u8 channel, u8 reg, u32 val)
+{
+ outl(val, atp->pciport[channel] + reg);
+}
+
+static inline u8 atp_readb_base(struct atp_unit *atp, u8 reg)
+{
+ return inb(atp->baseport + reg);
+}
+
+static inline u16 atp_readw_base(struct atp_unit *atp, u8 reg)
+{
+ return inw(atp->baseport + reg);
+}
+
+static inline u32 atp_readl_base(struct atp_unit *atp, u8 reg)
+{
+ return inl(atp->baseport + reg);
+}
+
+static inline u8 atp_readb_io(struct atp_unit *atp, u8 channel, u8 reg)
+{
+ return inb(atp->ioport[channel] + reg);
+}
+
+static inline u16 atp_readw_io(struct atp_unit *atp, u8 channel, u8 reg)
+{
+ return inw(atp->ioport[channel] + reg);
+}
+
+static inline u8 atp_readb_pci(struct atp_unit *atp, u8 channel, u8 reg)
+{
+ return inb(atp->pciport[channel] + reg);
+}
+
+static inline bool is880(struct atp_unit *atp)
+{
+ return atp->pdev->device == ATP880_DEVID1 ||
+ atp->pdev->device == ATP880_DEVID2;
+}
+
+static inline bool is885(struct atp_unit *atp)
+{
+ return atp->pdev->device == ATP885_DEVID;
+}
static irqreturn_t atp870u_intr_handle(int irq, void *dev_id)
{
unsigned long flags;
- unsigned short int tmpcip, id;
+ unsigned short int id;
unsigned char i, j, c, target_id, lun,cmdp;
unsigned char *prd;
struct scsi_cmnd *workreq;
- unsigned int workport, tmport, tmport1;
unsigned long adrcnt, k;
#ifdef ED_DBGP
unsigned long l;
#endif
- int errstus;
struct Scsi_Host *host = dev_id;
struct atp_unit *dev = (struct atp_unit *)&host->hostdata;
for (c = 0; c < 2; c++) {
- tmport = dev->ioport[c] + 0x1f;
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x80) != 0)
- {
- goto ch_sel;
- }
+ break;
dev->in_int[c] = 0;
}
- return IRQ_NONE;
-ch_sel:
-#ifdef ED_DBGP
+ if ((j & 0x80) == 0)
+ return IRQ_NONE;
+#ifdef ED_DBGP
printk("atp870u_intr_handle enter\n");
-#endif
+#endif
dev->in_int[c] = 1;
- cmdp = inb(dev->ioport[c] + 0x10);
- workport = dev->ioport[c];
+ cmdp = atp_readb_io(dev, c, 0x10);
if (dev->working[c] != 0) {
- if (dev->dev_id == ATP885_DEVID) {
- tmport1 = workport + 0x16;
- if ((inb(tmport1) & 0x80) == 0)
- outb((inb(tmport1) | 0x80), tmport1);
- }
- tmpcip = dev->pciport[c];
- if ((inb(tmpcip) & 0x08) != 0)
+ if (is885(dev)) {
+ if ((atp_readb_io(dev, c, 0x16) & 0x80) == 0)
+ atp_writeb_io(dev, c, 0x16,
+ (atp_readb_io(dev, c, 0x16) | 0x80));
+ }
+ if ((atp_readb_pci(dev, c, 0x00) & 0x08) != 0)
{
- tmpcip += 0x2;
for (k=0; k < 1000; k++) {
- if ((inb(tmpcip) & 0x08) == 0) {
- goto stop_dma;
- }
- if ((inb(tmpcip) & 0x01) == 0) {
- goto stop_dma;
- }
+ if ((atp_readb_pci(dev, c, 2) & 0x08) == 0)
+ break;
+ if ((atp_readb_pci(dev, c, 2) & 0x01) == 0)
+ break;
}
}
-stop_dma:
- tmpcip = dev->pciport[c];
- outb(0x00, tmpcip);
- tmport -= 0x08;
-
- i = inb(tmport);
-
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip += 2;
- outb(0x06, tmpcip);
- tmpcip -= 2;
- }
+ atp_writeb_pci(dev, c, 0, 0x00);
+
+ i = atp_readb_io(dev, c, 0x17);
- tmport -= 0x02;
- target_id = inb(tmport);
- tmport += 0x02;
+ if (is885(dev))
+ atp_writeb_pci(dev, c, 2, 0x06);
+
+ target_id = atp_readb_io(dev, c, 0x15);
/*
* Remap wide devices onto id numbers
@@ -130,52 +183,55 @@ stop_dma:
}
dev->last_cmd[c] |= 0x40;
}
- if (dev->dev_id == ATP885_DEVID)
+ if (is885(dev))
dev->r1f[c][target_id] |= j;
#ifdef ED_DBGP
printk("atp870u_intr_handle status = %x\n",i);
-#endif
+#endif
if (i == 0x85) {
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
- if (dev->dev_id == ATP885_DEVID) {
- tmport -= 0x05;
+ if (is885(dev)) {
adrcnt = 0;
- ((unsigned char *) &adrcnt)[2] = inb(tmport++);
- ((unsigned char *) &adrcnt)[1] = inb(tmport++);
- ((unsigned char *) &adrcnt)[0] = inb(tmport);
- if (dev->id[c][target_id].last_len != adrcnt)
- {
- k = dev->id[c][target_id].last_len;
- k -= adrcnt;
- dev->id[c][target_id].tran_len = k;
- dev->id[c][target_id].last_len = adrcnt;
+ ((unsigned char *) &adrcnt)[2] =
+ atp_readb_io(dev, c, 0x12);
+ ((unsigned char *) &adrcnt)[1] =
+ atp_readb_io(dev, c, 0x13);
+ ((unsigned char *) &adrcnt)[0] =
+ atp_readb_io(dev, c, 0x14);
+ if (dev->id[c][target_id].last_len != adrcnt) {
+ k = dev->id[c][target_id].last_len;
+ k -= adrcnt;
+ dev->id[c][target_id].tran_len = k;
+ dev->id[c][target_id].last_len = adrcnt;
}
#ifdef ED_DBGP
- printk("tmport = %x dev->id[c][target_id].last_len = %d dev->id[c][target_id].tran_len = %d\n",tmport,dev->id[c][target_id].last_len,dev->id[c][target_id].tran_len);
-#endif
+ printk("dev->id[c][target_id].last_len = %d "
+ "dev->id[c][target_id].tran_len = %d\n",
+ dev->id[c][target_id].last_len,
+ dev->id[c][target_id].tran_len);
+#endif
}
/*
* Flip wide
- */
+ */
if (dev->wide_id[c] != 0) {
- tmport = workport + 0x1b;
- outb(0x01, tmport);
- while ((inb(tmport) & 0x01) != 0x01) {
- outb(0x01, tmport);
- }
- }
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != 0x01)
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ }
/*
* Issue more commands
*/
- spin_lock_irqsave(dev->host->host_lock, flags);
- if (((dev->quhd[c] != dev->quend[c]) || (dev->last_cmd[c] != 0xff)) &&
+ spin_lock_irqsave(dev->host->host_lock, flags);
+ if (((dev->quhd[c] != dev->quend[c]) ||
+ (dev->last_cmd[c] != 0xff)) &&
(dev->in_snd[c] == 0)) {
#ifdef ED_DBGP
printk("Call sent_s870\n");
-#endif
+#endif
send_s870(dev,c);
}
spin_unlock_irqrestore(dev->host->host_lock, flags);
@@ -185,106 +241,98 @@ stop_dma:
dev->in_int[c] = 0;
#ifdef ED_DBGP
printk("Status 0x85 return\n");
-#endif
- goto handled;
+#endif
+ return IRQ_HANDLED;
}
if (i == 0x40) {
dev->last_cmd[c] |= 0x40;
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
if (i == 0x21) {
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
- tmport -= 0x05;
adrcnt = 0;
- ((unsigned char *) &adrcnt)[2] = inb(tmport++);
- ((unsigned char *) &adrcnt)[1] = inb(tmport++);
- ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ ((unsigned char *) &adrcnt)[2] =
+ atp_readb_io(dev, c, 0x12);
+ ((unsigned char *) &adrcnt)[1] =
+ atp_readb_io(dev, c, 0x13);
+ ((unsigned char *) &adrcnt)[0] =
+ atp_readb_io(dev, c, 0x14);
k = dev->id[c][target_id].last_len;
k -= adrcnt;
dev->id[c][target_id].tran_len = k;
dev->id[c][target_id].last_len = adrcnt;
- tmport -= 0x04;
- outb(0x41, tmport);
- tmport += 0x08;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x41);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
- if (dev->dev_id == ATP885_DEVID) {
+ if (is885(dev)) {
if ((i == 0x4c) || (i == 0x4d) || (i == 0x8c) || (i == 0x8d)) {
- if ((i == 0x4c) || (i == 0x8c))
- i=0x48;
- else
- i=0x49;
- }
-
+ if ((i == 0x4c) || (i == 0x8c))
+ i=0x48;
+ else
+ i=0x49;
+ }
}
if ((i == 0x80) || (i == 0x8f)) {
#ifdef ED_DBGP
printk(KERN_DEBUG "Device reselect\n");
-#endif
+#endif
lun = 0;
- tmport -= 0x07;
- if (cmdp == 0x44 || i==0x80) {
- tmport += 0x0d;
- lun = inb(tmport) & 0x07;
- } else {
+ if (cmdp == 0x44 || i == 0x80)
+ lun = atp_readb_io(dev, c, 0x1d) & 0x07;
+ else {
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
if (cmdp == 0x41) {
#ifdef ED_DBGP
printk("cmdp = 0x41\n");
-#endif
- tmport += 0x02;
+#endif
adrcnt = 0;
- ((unsigned char *) &adrcnt)[2] = inb(tmport++);
- ((unsigned char *) &adrcnt)[1] = inb(tmport++);
- ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ ((unsigned char *) &adrcnt)[2] =
+ atp_readb_io(dev, c, 0x12);
+ ((unsigned char *) &adrcnt)[1] =
+ atp_readb_io(dev, c, 0x13);
+ ((unsigned char *) &adrcnt)[0] =
+ atp_readb_io(dev, c, 0x14);
k = dev->id[c][target_id].last_len;
k -= adrcnt;
dev->id[c][target_id].tran_len = k;
dev->id[c][target_id].last_len = adrcnt;
- tmport += 0x04;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
} else {
#ifdef ED_DBGP
printk("cmdp != 0x41\n");
-#endif
- outb(0x46, tmport);
+#endif
+ atp_writeb_io(dev, c, 0x10, 0x46);
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x02;
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x12, 0x00);
+ atp_writeb_io(dev, c, 0x13, 0x00);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
}
if (dev->last_cmd[c] != 0xff) {
dev->last_cmd[c] |= 0x40;
}
- if (dev->dev_id == ATP885_DEVID) {
- j = inb(dev->baseport + 0x29) & 0xfe;
- outb(j, dev->baseport + 0x29);
- tmport = workport + 0x16;
- } else {
- tmport = workport + 0x10;
- outb(0x45, tmport);
- tmport += 0x06;
- }
-
- target_id = inb(tmport);
+ if (is885(dev)) {
+ j = atp_readb_base(dev, 0x29) & 0xfe;
+ atp_writeb_base(dev, 0x29, j);
+ } else
+ atp_writeb_io(dev, c, 0x10, 0x45);
+
+ target_id = atp_readb_io(dev, c, 0x16);
/*
* Remap wide identifiers
*/
@@ -293,31 +341,30 @@ stop_dma:
} else {
target_id &= 0x07;
}
- if (dev->dev_id == ATP885_DEVID) {
- tmport = workport + 0x10;
- outb(0x45, tmport);
- }
+ if (is885(dev))
+ atp_writeb_io(dev, c, 0x10, 0x45);
workreq = dev->id[c][target_id].curr_req;
-#ifdef ED_DBGP
+#ifdef ED_DBGP
scmd_printk(KERN_DEBUG, workreq, "CDB");
for (l = 0; l < workreq->cmd_len; l++)
printk(KERN_DEBUG " %x",workreq->cmnd[l]);
printk("\n");
-#endif
-
- tmport = workport + 0x0f;
- outb(lun, tmport);
- tmport += 0x02;
- outb(dev->id[c][target_id].devsp, tmport++);
+#endif
+
+ atp_writeb_io(dev, c, 0x0f, lun);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][target_id].devsp);
adrcnt = dev->id[c][target_id].tran_len;
k = dev->id[c][target_id].last_len;
- outb(((unsigned char *) &k)[2], tmport++);
- outb(((unsigned char *) &k)[1], tmport++);
- outb(((unsigned char *) &k)[0], tmport++);
-#ifdef ED_DBGP
- printk("k %x, k[0] 0x%x k[1] 0x%x k[2] 0x%x\n", k, inb(tmport-1), inb(tmport-2), inb(tmport-3));
-#endif
+ atp_writeb_io(dev, c, 0x12, ((unsigned char *) &k)[2]);
+ atp_writeb_io(dev, c, 0x13, ((unsigned char *) &k)[1]);
+ atp_writeb_io(dev, c, 0x14, ((unsigned char *) &k)[0]);
+#ifdef ED_DBGP
+ printk("k %x, k[0] 0x%x k[1] 0x%x k[2] 0x%x\n", k,
+ atp_readb_io(dev, c, 0x14),
+ atp_readb_io(dev, c, 0x13),
+ atp_readb_io(dev, c, 0x12));
+#endif
/* Remap wide */
j = target_id;
if (target_id > 7) {
@@ -325,35 +372,41 @@ stop_dma:
}
/* Add direction */
j |= dev->id[c][target_id].dirct;
- outb(j, tmport++);
- outb(0x80,tmport);
-
- /* enable 32 bit fifo transfer */
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip = dev->pciport[c] + 1;
- i=inb(tmpcip) & 0xf3;
- //j=workreq->cmnd[0];
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ atp_writeb_io(dev, c, 0x15, j);
+ atp_writeb_io(dev, c, 0x16, 0x80);
+
+ /* enable 32 bit fifo transfer */
+ if (is885(dev)) {
+ i = atp_readb_pci(dev, c, 1) & 0xf3;
+ //j=workreq->cmnd[0];
+ if ((workreq->cmnd[0] == READ_6) ||
+ (workreq->cmnd[0] == READ_10) ||
+ (workreq->cmnd[0] == WRITE_6) ||
+ (workreq->cmnd[0] == WRITE_10)) {
i |= 0x0c;
}
- outb(i,tmpcip);
- } else if ((dev->dev_id == ATP880_DEVID1) ||
- (dev->dev_id == ATP880_DEVID2) ) {
- tmport = workport - 0x05;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);
- } else {
- outb((unsigned char) (inb(tmport) & 0x3f), tmport);
- }
- } else {
- tmport = workport + 0x3a;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((unsigned char) ((inb(tmport) & 0xf3) | 0x08), tmport);
- } else {
- outb((unsigned char) (inb(tmport) & 0xf3), tmport);
- }
- }
- tmport = workport + 0x1b;
+ atp_writeb_pci(dev, c, 1, i);
+ } else if (is880(dev)) {
+ if ((workreq->cmnd[0] == READ_6) ||
+ (workreq->cmnd[0] == READ_10) ||
+ (workreq->cmnd[0] == WRITE_6) ||
+ (workreq->cmnd[0] == WRITE_10))
+ atp_writeb_base(dev, 0x3b,
+ (atp_readb_base(dev, 0x3b) & 0x3f) | 0xc0);
+ else
+ atp_writeb_base(dev, 0x3b,
+ atp_readb_base(dev, 0x3b) & 0x3f);
+ } else {
+ if ((workreq->cmnd[0] == READ_6) ||
+ (workreq->cmnd[0] == READ_10) ||
+ (workreq->cmnd[0] == WRITE_6) ||
+ (workreq->cmnd[0] == WRITE_10))
+ atp_writeb_base(dev, 0x3a,
+ (atp_readb_base(dev, 0x3a) & 0xf3) | 0x08);
+ else
+ atp_writeb_base(dev, 0x3a,
+ atp_readb_base(dev, 0x3a) & 0xf3);
+ }
j = 0;
id = 1;
id = id << target_id;
@@ -363,22 +416,20 @@ stop_dma:
if ((id & dev->wide_id[c]) != 0) {
j |= 0x01;
}
- outb(j, tmport);
- while ((inb(tmport) & 0x01) != j) {
- outb(j,tmport);
- }
+ atp_writeb_io(dev, c, 0x1b, j);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != j)
+ atp_writeb_io(dev, c, 0x1b, j);
if (dev->id[c][target_id].last_len == 0) {
- tmport = workport + 0x18;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
#ifdef ED_DBGP
printk("dev->id[c][target_id].last_len = 0\n");
-#endif
- goto handled;
+#endif
+ return IRQ_HANDLED;
}
#ifdef ED_DBGP
printk("target_id = %d adrcnt = %d\n",target_id,adrcnt);
-#endif
+#endif
prd = dev->id[c][target_id].prd_pos;
while (adrcnt != 0) {
id = ((unsigned short int *)prd)[2];
@@ -388,8 +439,8 @@ stop_dma:
k = id;
}
if (k > adrcnt) {
- ((unsigned short int *)prd)[2] = (unsigned short int)
- (k - adrcnt);
+ ((unsigned short int *)prd)[2] =
+ (unsigned short int)(k - adrcnt);
((unsigned long *)prd)[0] += adrcnt;
adrcnt = 0;
dev->id[c][target_id].prd_pos = prd;
@@ -400,41 +451,36 @@ stop_dma:
if (adrcnt == 0) {
dev->id[c][target_id].prd_pos = prd;
}
- }
+ }
}
- tmpcip = dev->pciport[c] + 0x04;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
+ atp_writel_pci(dev, c, 0x04, dev->id[c][target_id].prdaddr);
#ifdef ED_DBGP
- printk("dev->id[%d][%d].prdaddr 0x%8x\n", c, target_id, dev->id[c][target_id].prdaddr);
+ printk("dev->id[%d][%d].prdaddr 0x%8x\n",
+ c, target_id, dev->id[c][target_id].prdaddr);
#endif
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip -= 0x04;
- } else {
- tmpcip -= 0x02;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- tmpcip -= 0x02;
+ if (!is885(dev)) {
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
}
- tmport = workport + 0x18;
/*
* Check transfer direction
*/
if (dev->id[c][target_id].dirct != 0) {
- outb(0x08, tmport);
- outb(0x01, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x01);
dev->in_int[c] = 0;
#ifdef ED_DBGP
printk("status 0x80 return dirct != 0\n");
-#endif
- goto handled;
+#endif
+ return IRQ_HANDLED;
}
- outb(0x08, tmport);
- outb(0x09, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x09);
dev->in_int[c] = 0;
#ifdef ED_DBGP
printk("status 0x80 return dirct = 0\n");
-#endif
- goto handled;
+#endif
+ return IRQ_HANDLED;
}
/*
@@ -443,31 +489,22 @@ stop_dma:
workreq = dev->id[c][target_id].curr_req;
- if (i == 0x42) {
- if ((dev->last_cmd[c] & 0xf0) != 0x40)
- {
- dev->last_cmd[c] = 0xff;
- }
- errstus = 0x02;
- workreq->result = errstus;
- goto go_42;
- }
- if (i == 0x16) {
+ if (i == 0x42 || i == 0x16) {
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
- errstus = 0;
- tmport -= 0x08;
- errstus = inb(tmport);
- if (((dev->r1f[c][target_id] & 0x10) != 0)&&(dev->dev_id==ATP885_DEVID)) {
- printk(KERN_WARNING "AEC67162 CRC ERROR !\n");
- errstus = 0x02;
- }
- workreq->result = errstus;
-go_42:
- if (dev->dev_id == ATP885_DEVID) {
- j = inb(dev->baseport + 0x29) | 0x01;
- outb(j, dev->baseport + 0x29);
+ if (i == 0x16) {
+ workreq->result = atp_readb_io(dev, c, 0x0f);
+ if (((dev->r1f[c][target_id] & 0x10) != 0) && is885(dev)) {
+ printk(KERN_WARNING "AEC67162 CRC ERROR !\n");
+ workreq->result = SAM_STAT_CHECK_CONDITION;
+ }
+ } else
+ workreq->result = SAM_STAT_CHECK_CONDITION;
+
+ if (is885(dev)) {
+ j = atp_readb_base(dev, 0x29) | 0x01;
+ atp_writeb_base(dev, 0x29, j);
}
/*
* Complete the command
@@ -475,10 +512,10 @@ go_42:
scsi_dma_unmap(workreq);
spin_lock_irqsave(dev->host->host_lock, flags);
- (*workreq->scsi_done) (workreq);
+ scsi_done(workreq);
#ifdef ED_DBGP
printk("workreq->scsi_done\n");
-#endif
+#endif
/*
* Clear it off the queue
*/
@@ -489,26 +526,25 @@ go_42:
* Take it back wide
*/
if (dev->wide_id[c] != 0) {
- tmport = workport + 0x1b;
- outb(0x01, tmport);
- while ((inb(tmport) & 0x01) != 0x01) {
- outb(0x01, tmport);
- }
- }
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != 0x01)
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ }
/*
* If there is stuff to send and nothing going then send it
*/
spin_lock_irqsave(dev->host->host_lock, flags);
- if (((dev->last_cmd[c] != 0xff) || (dev->quhd[c] != dev->quend[c])) &&
+ if (((dev->last_cmd[c] != 0xff) ||
+ (dev->quhd[c] != dev->quend[c])) &&
(dev->in_snd[c] == 0)) {
#ifdef ED_DBGP
printk("Call sent_s870(scsi_done)\n");
-#endif
+#endif
send_s870(dev,c);
}
spin_unlock_irqrestore(dev->host->host_lock, flags);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
@@ -518,98 +554,74 @@ go_42:
}
i &= 0x0f;
if (i == 0x09) {
- tmpcip += 4;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
- tmpcip = tmpcip - 2;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- tmpcip = tmpcip - 2;
- tmport = workport + 0x10;
- outb(0x41, tmport);
- if (dev->dev_id == ATP885_DEVID) {
- tmport += 2;
+ atp_writel_pci(dev, c, 4, dev->id[c][target_id].prdaddr);
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
+ atp_writeb_io(dev, c, 0x10, 0x41);
+ if (is885(dev)) {
k = dev->id[c][target_id].last_len;
- outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[0]), tmport);
+ atp_writeb_io(dev, c, 0x12,
+ ((unsigned char *) (&k))[2]);
+ atp_writeb_io(dev, c, 0x13,
+ ((unsigned char *) (&k))[1]);
+ atp_writeb_io(dev, c, 0x14,
+ ((unsigned char *) (&k))[0]);
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x04;
} else {
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x08;
}
- outb(0x08, tmport);
- outb(0x09, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x09);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
if (i == 0x08) {
- tmpcip += 4;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
- tmpcip = tmpcip - 2;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- tmpcip = tmpcip - 2;
- tmport = workport + 0x10;
- outb(0x41, tmport);
- if (dev->dev_id == ATP885_DEVID) {
- tmport += 2;
+ atp_writel_pci(dev, c, 4, dev->id[c][target_id].prdaddr);
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
+ atp_writeb_io(dev, c, 0x10, 0x41);
+ if (is885(dev)) {
k = dev->id[c][target_id].last_len;
- outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[0]), tmport++);
- } else {
- tmport += 5;
+ atp_writeb_io(dev, c, 0x12,
+ ((unsigned char *) (&k))[2]);
+ atp_writeb_io(dev, c, 0x13,
+ ((unsigned char *) (&k))[1]);
+ atp_writeb_io(dev, c, 0x14,
+ ((unsigned char *) (&k))[0]);
}
- outb((unsigned char) (inb(tmport) | 0x20), tmport);
+ atp_writeb_io(dev, c, 0x15,
+ atp_readb_io(dev, c, 0x15) | 0x20);
dev->id[c][target_id].dirct = 0x20;
- tmport += 0x03;
- outb(0x08, tmport);
- outb(0x01, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x01);
dev->in_int[c] = 0;
- goto handled;
- }
- tmport -= 0x07;
- if (i == 0x0a) {
- outb(0x30, tmport);
- } else {
- outb(0x46, tmport);
+ return IRQ_HANDLED;
}
+ if (i == 0x0a)
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ else
+ atp_writeb_io(dev, c, 0x10, 0x46);
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x02;
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- dev->in_int[c] = 0;
- goto handled;
- } else {
-// tmport = workport + 0x17;
-// inb(tmport);
-// dev->working[c] = 0;
- dev->in_int[c] = 0;
- goto handled;
+ atp_writeb_io(dev, c, 0x12, 0x00);
+ atp_writeb_io(dev, c, 0x13, 0x00);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
}
-
-handled:
-#ifdef ED_DBGP
- printk("atp870u_intr_handle exit\n");
-#endif
+ dev->in_int[c] = 0;
+
return IRQ_HANDLED;
}
/**
- * atp870u_queuecommand - Queue SCSI command
+ * atp870u_queuecommand_lck - Queue SCSI command
* @req_p: request block
- * @done: completion function
*
* Queue a command to the ATP queue. Called with the host lock held.
*/
-static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p,
- void (*done) (struct scsi_cmnd *))
+static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
unsigned char c;
- unsigned int tmport,m;
+ unsigned int m;
struct atp_unit *dev;
struct Scsi_Host *host;
@@ -617,19 +629,17 @@ static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p,
req_p->sense_buffer[0]=0;
scsi_set_resid(req_p, 0);
if (scmd_channel(req_p) > 1) {
- req_p->result = 0x00040000;
+ req_p->result = DID_BAD_TARGET << 16;
done(req_p);
-#ifdef ED_DBGP
- printk("atp870u_queuecommand : req_p->device->channel > 1\n");
-#endif
+#ifdef ED_DBGP
+ printk("atp870u_queuecommand : req_p->device->channel > 1\n");
+#endif
return 0;
}
host = req_p->device->host;
dev = (struct atp_unit *)&host->hostdata;
-
-
m = 1;
m = m << scmd_id(req_p);
@@ -638,22 +648,11 @@ static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p,
*/
if ((m & dev->active_id[c]) == 0) {
- req_p->result = 0x00040000;
+ req_p->result = DID_BAD_TARGET << 16;
done(req_p);
return 0;
}
- if (done) {
- req_p->scsi_done = done;
- } else {
-#ifdef ED_DBGP
- printk( "atp870u_queuecommand: done can't be NULL\n");
-#endif
- req_p->result = 0;
- done(req_p);
- return 0;
- }
-
/*
* Count new command
*/
@@ -661,7 +660,7 @@ static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p,
if (dev->quend[c] >= qcnt) {
dev->quend[c] = 0;
}
-
+
/*
* Check queue state
*/
@@ -669,56 +668,57 @@ static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p,
if (dev->quend[c] == 0) {
dev->quend[c] = qcnt;
}
-#ifdef ED_DBGP
+#ifdef ED_DBGP
printk("atp870u_queuecommand : dev->quhd[c] == dev->quend[c]\n");
-#endif
+#endif
dev->quend[c]--;
- req_p->result = 0x00020000;
- done(req_p);
+ req_p->result = DID_BUS_BUSY << 16;
+ done(req_p);
return 0;
}
dev->quereq[c][dev->quend[c]] = req_p;
- tmport = dev->ioport[c] + 0x1c;
-#ifdef ED_DBGP
- printk("dev->ioport[c] = %x inb(tmport) = %x dev->in_int[%d] = %d dev->in_snd[%d] = %d\n",dev->ioport[c],inb(tmport),c,dev->in_int[c],c,dev->in_snd[c]);
+#ifdef ED_DBGP
+ printk("dev->ioport[c] = %x atp_readb_io(dev, c, 0x1c) = %x "
+ "dev->in_int[%d] = %d dev->in_snd[%d] = %d\n",
+ dev->ioport[c], atp_readb_io(dev, c, 0x1c), c,
+ dev->in_int[c],c,dev->in_snd[c]);
#endif
- if ((inb(tmport) == 0) && (dev->in_int[c] == 0) && (dev->in_snd[c] == 0)) {
+ if ((atp_readb_io(dev, c, 0x1c) == 0) &&
+ (dev->in_int[c] == 0) &&
+ (dev->in_snd[c] == 0)) {
#ifdef ED_DBGP
printk("Call sent_s870(atp870u_queuecommand)\n");
-#endif
+#endif
send_s870(dev,c);
}
-#ifdef ED_DBGP
+#ifdef ED_DBGP
printk("atp870u_queuecommand : exit\n");
-#endif
+#endif
return 0;
}
static DEF_SCSI_QCMD(atp870u_queuecommand)
-/**
+/*
* send_s870 - send a command to the controller
- * @host: host
*
* On entry there is work queued to be done. We move some of that work to the
- * controller itself.
+ * controller itself.
*
* Caller holds the host lock.
*/
-static void send_s870(struct atp_unit *dev,unsigned char c)
+static void send_s870(struct atp_unit *dev, unsigned char c)
{
- unsigned int tmport;
- struct scsi_cmnd *workreq;
+ struct scsi_cmnd *workreq = NULL;
unsigned int i;//,k;
unsigned char j, target_id;
unsigned char *prd;
- unsigned short int tmpcip, w;
+ unsigned short int w;
unsigned long l, bttl = 0;
- unsigned int workport;
unsigned long sg_count;
if (dev->in_snd[c] != 0) {
-#ifdef ED_DBGP
+#ifdef ED_DBGP
printk("cmnd in_snd\n");
#endif
return;
@@ -730,53 +730,43 @@ static void send_s870(struct atp_unit *dev,unsigned char c)
if ((dev->last_cmd[c] != 0xff) && ((dev->last_cmd[c] & 0x40) != 0)) {
dev->last_cmd[c] &= 0x0f;
workreq = dev->id[c][dev->last_cmd[c]].curr_req;
- if (workreq != NULL) { /* check NULL pointer */
- goto cmd_subp;
- }
- dev->last_cmd[c] = 0xff;
- if (dev->quhd[c] == dev->quend[c]) {
- dev->in_snd[c] = 0;
- return ;
+ if (!workreq) {
+ dev->last_cmd[c] = 0xff;
+ if (dev->quhd[c] == dev->quend[c]) {
+ dev->in_snd[c] = 0;
+ return;
+ }
}
}
- if ((dev->last_cmd[c] != 0xff) && (dev->working[c] != 0)) {
- dev->in_snd[c] = 0;
- return ;
- }
- dev->working[c]++;
- j = dev->quhd[c];
- dev->quhd[c]++;
- if (dev->quhd[c] >= qcnt) {
- dev->quhd[c] = 0;
- }
- workreq = dev->quereq[c][dev->quhd[c]];
- if (dev->id[c][scmd_id(workreq)].curr_req == NULL) {
+ if (!workreq) {
+ if ((dev->last_cmd[c] != 0xff) && (dev->working[c] != 0)) {
+ dev->in_snd[c] = 0;
+ return;
+ }
+ dev->working[c]++;
+ j = dev->quhd[c];
+ dev->quhd[c]++;
+ if (dev->quhd[c] >= qcnt)
+ dev->quhd[c] = 0;
+ workreq = dev->quereq[c][dev->quhd[c]];
+ if (dev->id[c][scmd_id(workreq)].curr_req != NULL) {
+ dev->quhd[c] = j;
+ dev->working[c]--;
+ dev->in_snd[c] = 0;
+ return;
+ }
dev->id[c][scmd_id(workreq)].curr_req = workreq;
dev->last_cmd[c] = scmd_id(workreq);
- goto cmd_subp;
- }
- dev->quhd[c] = j;
- dev->working[c]--;
- dev->in_snd[c] = 0;
- return;
-cmd_subp:
- workport = dev->ioport[c];
- tmport = workport + 0x1f;
- if ((inb(tmport) & 0xb0) != 0) {
- goto abortsnd;
}
- tmport = workport + 0x1c;
- if (inb(tmport) == 0) {
- goto oktosend;
- }
-abortsnd:
+ if ((atp_readb_io(dev, c, 0x1f) & 0xb0) != 0 ||
+ atp_readb_io(dev, c, 0x1c) != 0) {
#ifdef ED_DBGP
- printk("Abort to Send\n");
+ printk("Abort to Send\n");
#endif
- dev->last_cmd[c] |= 0x40;
- dev->in_snd[c] = 0;
- return;
-oktosend:
+ dev->last_cmd[c] |= 0x40;
+ dev->in_snd[c] = 0;
+ return;
+ }
#ifdef ED_DBGP
printk("OK to Send\n");
scmd_printk(KERN_DEBUG, workreq, "CDB");
@@ -784,24 +774,23 @@ oktosend:
printk(" %x",workreq->cmnd[i]);
}
printk("\n");
-#endif
+#endif
l = scsi_bufflen(workreq);
- if (dev->dev_id == ATP885_DEVID) {
- j = inb(dev->baseport + 0x29) & 0xfe;
- outb(j, dev->baseport + 0x29);
+ if (is885(dev)) {
+ j = atp_readb_base(dev, 0x29) & 0xfe;
+ atp_writeb_base(dev, 0x29, j);
dev->r1f[c][scmd_id(workreq)] = 0;
}
-
+
if (workreq->cmnd[0] == READ_CAPACITY) {
if (l > 8)
l = 8;
}
- if (workreq->cmnd[0] == 0x00) {
+ if (workreq->cmnd[0] == TEST_UNIT_READY) {
l = 0;
}
- tmport = workport + 0x1b;
j = 0;
target_id = scmd_id(workreq);
@@ -813,9 +802,9 @@ oktosend:
if ((w & dev->wide_id[c]) != 0) {
j |= 0x01;
}
- outb(j, tmport);
- while ((inb(tmport) & 0x01) != j) {
- outb(j,tmport);
+ atp_writeb_io(dev, c, 0x1b, j);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != j) {
+ atp_writeb_pci(dev, c, 0x1b, j);
#ifdef ED_DBGP
printk("send_s870 while loop 1\n");
#endif
@@ -824,41 +813,37 @@ oktosend:
* Write the command
*/
- tmport = workport;
- outb(workreq->cmd_len, tmport++);
- outb(0x2c, tmport++);
- if (dev->dev_id == ATP885_DEVID) {
- outb(0x7f, tmport++);
- } else {
- outb(0xcf, tmport++);
- }
- for (i = 0; i < workreq->cmd_len; i++) {
- outb(workreq->cmnd[i], tmport++);
- }
- tmport = workport + 0x0f;
- outb(workreq->device->lun, tmport);
- tmport += 0x02;
+ atp_writeb_io(dev, c, 0x00, workreq->cmd_len);
+ atp_writeb_io(dev, c, 0x01, 0x2c);
+ if (is885(dev))
+ atp_writeb_io(dev, c, 0x02, 0x7f);
+ else
+ atp_writeb_io(dev, c, 0x02, 0xcf);
+ for (i = 0; i < workreq->cmd_len; i++)
+ atp_writeb_io(dev, c, 0x03 + i, workreq->cmnd[i]);
+ atp_writeb_io(dev, c, 0x0f, workreq->device->lun);
/*
* Write the target
*/
- outb(dev->id[c][target_id].devsp, tmport++);
-#ifdef ED_DBGP
- printk("dev->id[%d][%d].devsp = %2x\n",c,target_id,dev->id[c][target_id].devsp);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][target_id].devsp);
+#ifdef ED_DBGP
+ printk("dev->id[%d][%d].devsp = %2x\n",c,target_id,
+ dev->id[c][target_id].devsp);
#endif
sg_count = scsi_dma_map(workreq);
/*
* Write transfer size
*/
- outb((unsigned char) (((unsigned char *) (&l))[2]), tmport++);
- outb((unsigned char) (((unsigned char *) (&l))[1]), tmport++);
- outb((unsigned char) (((unsigned char *) (&l))[0]), tmport++);
- j = target_id;
+ atp_writeb_io(dev, c, 0x12, ((unsigned char *) (&l))[2]);
+ atp_writeb_io(dev, c, 0x13, ((unsigned char *) (&l))[1]);
+ atp_writeb_io(dev, c, 0x14, ((unsigned char *) (&l))[0]);
+ j = target_id;
dev->id[c][j].last_len = l;
dev->id[c][j].tran_len = 0;
-#ifdef ED_DBGP
+#ifdef ED_DBGP
printk("dev->id[%2d][%2d].last_len = %d\n",c,j,dev->id[c][j].last_len);
-#endif
+#endif
/*
* Flip the wide bits
*/
@@ -868,29 +853,24 @@ oktosend:
/*
* Check transfer direction
*/
- if (workreq->sc_data_direction == DMA_TO_DEVICE) {
- outb((unsigned char) (j | 0x20), tmport++);
- } else {
- outb(j, tmport++);
- }
- outb((unsigned char) (inb(tmport) | 0x80), tmport);
- outb(0x80, tmport);
- tmport = workport + 0x1c;
+ if (workreq->sc_data_direction == DMA_TO_DEVICE)
+ atp_writeb_io(dev, c, 0x15, j | 0x20);
+ else
+ atp_writeb_io(dev, c, 0x15, j);
+ atp_writeb_io(dev, c, 0x16, atp_readb_io(dev, c, 0x16) | 0x80);
+ atp_writeb_io(dev, c, 0x16, 0x80);
dev->id[c][target_id].dirct = 0;
if (l == 0) {
- if (inb(tmport) == 0) {
- tmport = workport + 0x18;
+ if (atp_readb_io(dev, c, 0x1c) == 0) {
#ifdef ED_DBGP
- printk("change SCSI_CMD_REG 0x08\n");
-#endif
- outb(0x08, tmport);
- } else {
+ printk("change SCSI_CMD_REG 0x08\n");
+#endif
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ } else
dev->last_cmd[c] |= 0x40;
- }
dev->in_snd[c] = 0;
return;
}
- tmpcip = dev->pciport[c];
prd = dev->id[c][target_id].prd_table;
dev->id[c][target_id].prd_pos = prd;
@@ -905,9 +885,9 @@ oktosend:
scsi_for_each_sg(workreq, sgpnt, sg_count, j) {
bttl = sg_dma_address(sgpnt);
l=sg_dma_len(sgpnt);
-#ifdef ED_DBGP
+#ifdef ED_DBGP
printk("1. bttl %x, l %x\n",bttl, l);
-#endif
+#endif
while (l > 0x10000) {
(((u16 *) (prd))[i + 3]) = 0x0000;
(((u16 *) (prd))[i + 2]) = 0x0000;
@@ -919,74 +899,77 @@ oktosend:
(((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
(((u16 *) (prd))[i + 2]) = cpu_to_le16(l);
(((u16 *) (prd))[i + 3]) = 0;
- i += 0x04;
+ i += 0x04;
}
- (((u16 *) (prd))[i - 1]) = cpu_to_le16(0x8000);
-#ifdef ED_DBGP
- printk("prd %4x %4x %4x %4x\n",(((unsigned short int *)prd)[0]),(((unsigned short int *)prd)[1]),(((unsigned short int *)prd)[2]),(((unsigned short int *)prd)[3]));
+ (((u16 *) (prd))[i - 1]) = cpu_to_le16(0x8000);
+#ifdef ED_DBGP
+ printk("prd %4x %4x %4x %4x\n",
+ (((unsigned short int *)prd)[0]),
+ (((unsigned short int *)prd)[1]),
+ (((unsigned short int *)prd)[2]),
+ (((unsigned short int *)prd)[3]));
printk("2. bttl %x, l %x\n",bttl, l);
-#endif
+#endif
}
- tmpcip += 4;
-#ifdef ED_DBGP
- printk("send_s870: prdaddr_2 0x%8x tmpcip %x target_id %d\n", dev->id[c][target_id].prdaddr,tmpcip,target_id);
-#endif
+#ifdef ED_DBGP
+ printk("send_s870: prdaddr_2 0x%8x target_id %d\n",
+ dev->id[c][target_id].prdaddr,target_id);
+#endif
dev->id[c][target_id].prdaddr = dev->id[c][target_id].prd_bus;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
- tmpcip = tmpcip - 2;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip--;
- j=inb(tmpcip) & 0xf3;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) ||
- (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- j |= 0x0c;
- }
- outb(j,tmpcip);
- tmpcip--;
- } else if ((dev->dev_id == ATP880_DEVID1) ||
- (dev->dev_id == ATP880_DEVID2)) {
- tmpcip =tmpcip -2;
- tmport = workport - 0x05;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);
- } else {
- outb((unsigned char) (inb(tmport) & 0x3f), tmport);
- }
- } else {
- tmpcip =tmpcip -2;
- tmport = workport + 0x3a;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((inb(tmport) & 0xf3) | 0x08, tmport);
- } else {
- outb(inb(tmport) & 0xf3, tmport);
- }
- }
- tmport = workport + 0x1c;
+ atp_writel_pci(dev, c, 4, dev->id[c][target_id].prdaddr);
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
+ if (is885(dev)) {
+ j = atp_readb_pci(dev, c, 1) & 0xf3;
+ if ((workreq->cmnd[0] == READ_6) ||
+ (workreq->cmnd[0] == READ_10) ||
+ (workreq->cmnd[0] == WRITE_6) ||
+ (workreq->cmnd[0] == WRITE_10)) {
+ j |= 0x0c;
+ }
+ atp_writeb_pci(dev, c, 1, j);
+ } else if (is880(dev)) {
+ if ((workreq->cmnd[0] == READ_6) ||
+ (workreq->cmnd[0] == READ_10) ||
+ (workreq->cmnd[0] == WRITE_6) ||
+ (workreq->cmnd[0] == WRITE_10))
+ atp_writeb_base(dev, 0x3b,
+ (atp_readb_base(dev, 0x3b) & 0x3f) | 0xc0);
+ else
+ atp_writeb_base(dev, 0x3b,
+ atp_readb_base(dev, 0x3b) & 0x3f);
+ } else {
+ if ((workreq->cmnd[0] == READ_6) ||
+ (workreq->cmnd[0] == READ_10) ||
+ (workreq->cmnd[0] == WRITE_6) ||
+ (workreq->cmnd[0] == WRITE_10))
+ atp_writeb_base(dev, 0x3a,
+ (atp_readb_base(dev, 0x3a) & 0xf3) | 0x08);
+ else
+ atp_writeb_base(dev, 0x3a,
+ atp_readb_base(dev, 0x3a) & 0xf3);
+ }
if(workreq->sc_data_direction == DMA_TO_DEVICE) {
dev->id[c][target_id].dirct = 0x20;
- if (inb(tmport) == 0) {
- tmport = workport + 0x18;
- outb(0x08, tmport);
- outb(0x01, tmpcip);
-#ifdef ED_DBGP
+ if (atp_readb_io(dev, c, 0x1c) == 0) {
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x01);
+#ifdef ED_DBGP
printk( "start DMA(to target)\n");
-#endif
+#endif
} else {
dev->last_cmd[c] |= 0x40;
}
dev->in_snd[c] = 0;
return;
}
- if (inb(tmport) == 0) {
- tmport = workport + 0x18;
- outb(0x08, tmport);
- outb(0x09, tmpcip);
-#ifdef ED_DBGP
+ if (atp_readb_io(dev, c, 0x1c) == 0) {
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x09);
+#ifdef ED_DBGP
printk( "start DMA(to host)\n");
-#endif
+#endif
} else {
dev->last_cmd[c] |= 0x40;
}
@@ -997,49 +980,40 @@ oktosend:
static unsigned char fun_scam(struct atp_unit *dev, unsigned short int *val)
{
- unsigned int tmport;
unsigned short int i, k;
unsigned char j;
- tmport = dev->ioport[0] + 0x1c;
- outw(*val, tmport);
-FUN_D7:
+ atp_writew_io(dev, 0, 0x1c, *val);
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
- k = inw(tmport);
+ k = atp_readw_io(dev, 0, 0x1c);
j = (unsigned char) (k >> 8);
- if ((k & 0x8000) != 0) { /* DB7 all release? */
- goto FUN_D7;
- }
+ if ((k & 0x8000) != 0) /* DB7 all release? */
+ i = 0;
}
*val |= 0x4000; /* assert DB6 */
- outw(*val, tmport);
+ atp_writew_io(dev, 0, 0x1c, *val);
*val &= 0xdfff; /* assert DB5 */
- outw(*val, tmport);
-FUN_D5:
+ atp_writew_io(dev, 0, 0x1c, *val);
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
- if ((inw(tmport) & 0x2000) != 0) { /* DB5 all release? */
- goto FUN_D5;
- }
+ if ((atp_readw_io(dev, 0, 0x1c) & 0x2000) != 0) /* DB5 all release? */
+ i = 0;
}
*val |= 0x8000; /* no DB4-0, assert DB7 */
*val &= 0xe0ff;
- outw(*val, tmport);
+ atp_writew_io(dev, 0, 0x1c, *val);
*val &= 0xbfff; /* release DB6 */
- outw(*val, tmport);
-FUN_D6:
+ atp_writew_io(dev, 0, 0x1c, *val);
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
- if ((inw(tmport) & 0x4000) != 0) { /* DB6 all release? */
- goto FUN_D6;
- }
+ if ((atp_readw_io(dev, 0, 0x1c) & 0x4000) != 0) /* DB6 all release? */
+ i = 0;
}
return j;
}
-static void tscam(struct Scsi_Host *host)
+static void tscam(struct Scsi_Host *host, bool wide_chip, u8 scam_on)
{
- unsigned int tmport;
unsigned char i, j, k;
unsigned long n;
unsigned short int m, assignid_map, val;
@@ -1056,31 +1030,28 @@ static void tscam(struct Scsi_Host *host)
}
*/
- tmport = dev->ioport[0] + 1;
- outb(0x08, tmport++);
- outb(0x7f, tmport);
- tmport = dev->ioport[0] + 0x11;
- outb(0x20, tmport);
+ atp_writeb_io(dev, 0, 1, 0x08);
+ atp_writeb_io(dev, 0, 2, 0x7f);
+ atp_writeb_io(dev, 0, 0x11, 0x20);
- if ((dev->scam_on & 0x40) == 0) {
+ if ((scam_on & 0x40) == 0) {
return;
}
m = 1;
m <<= dev->host_id[0];
j = 16;
- if (dev->chip_ver < 4) {
+ if (!wide_chip) {
m |= 0xff00;
j = 8;
}
assignid_map = m;
- tmport = dev->ioport[0] + 0x02;
- outb(0x02, tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
+ atp_writeb_io(dev, 0, 0x02, 0x02); /* 2*2=4ms,3EH 2/32*3E=3.9ms */
+ atp_writeb_io(dev, 0, 0x03, 0);
+ atp_writeb_io(dev, 0, 0x04, 0);
+ atp_writeb_io(dev, 0, 0x05, 0);
+ atp_writeb_io(dev, 0, 0x06, 0);
+ atp_writeb_io(dev, 0, 0x07, 0);
+ atp_writeb_io(dev, 0, 0x08, 0);
for (i = 0; i < j; i++) {
m = 1;
@@ -1088,142 +1059,125 @@ static void tscam(struct Scsi_Host *host)
if ((m & assignid_map) != 0) {
continue;
}
- tmport = dev->ioport[0] + 0x0f;
- outb(0, tmport++);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
+ atp_writeb_io(dev, 0, 0x0f, 0);
+ atp_writeb_io(dev, 0, 0x12, 0);
+ atp_writeb_io(dev, 0, 0x13, 0);
+ atp_writeb_io(dev, 0, 0x14, 0);
if (i > 7) {
k = (i & 0x07) | 0x40;
} else {
k = i;
}
- outb(k, tmport++);
- tmport = dev->ioport[0] + 0x1b;
- if (dev->chip_ver == 4) {
- outb(0x01, tmport);
- } else {
- outb(0x00, tmport);
- }
-wait_rdyok:
- tmport = dev->ioport[0] + 0x18;
- outb(0x09, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
- tmport -= 0x08;
- k = inb(tmport);
- if (k != 0x16) {
- if ((k == 0x85) || (k == 0x42)) {
- continue;
- }
- tmport = dev->ioport[0] + 0x10;
- outb(0x41, tmport);
- goto wait_rdyok;
- }
+ atp_writeb_io(dev, 0, 0x15, k);
+ if (wide_chip)
+ atp_writeb_io(dev, 0, 0x1b, 0x01);
+ else
+ atp_writeb_io(dev, 0, 0x1b, 0x00);
+ do {
+ atp_writeb_io(dev, 0, 0x18, 0x09);
+
+ while ((atp_readb_io(dev, 0, 0x1f) & 0x80) == 0x00)
+ cpu_relax();
+ k = atp_readb_io(dev, 0, 0x17);
+ if ((k == 0x85) || (k == 0x42))
+ break;
+ if (k != 0x16)
+ atp_writeb_io(dev, 0, 0x10, 0x41);
+ } while (k != 0x16);
+ if ((k == 0x85) || (k == 0x42))
+ continue;
assignid_map |= m;
}
- tmport = dev->ioport[0] + 0x02;
- outb(0x7f, tmport);
- tmport = dev->ioport[0] + 0x1b;
- outb(0x02, tmport);
+ atp_writeb_io(dev, 0, 0x02, 0x7f);
+ atp_writeb_io(dev, 0, 0x1b, 0x02);
- outb(0, 0x80);
+ udelay(2);
val = 0x0080; /* bsy */
- tmport = dev->ioport[0] + 0x1c;
- outw(val, tmport);
+ atp_writew_io(dev, 0, 0x1c, val);
val |= 0x0040; /* sel */
- outw(val, tmport);
+ atp_writew_io(dev, 0, 0x1c, val);
val |= 0x0004; /* msg */
- outw(val, tmport);
- inb(0x80); /* 2 deskew delay(45ns*2=90ns) */
+ atp_writew_io(dev, 0, 0x1c, val);
+ udelay(2); /* 2 deskew delay(45ns*2=90ns) */
val &= 0x007f; /* no bsy */
- outw(val, tmport);
- mdelay(128);
+ atp_writew_io(dev, 0, 0x1c, val);
+ msleep(128);
val &= 0x00fb; /* after 1ms no msg */
- outw(val, tmport);
-wait_nomsg:
- if ((inb(tmport) & 0x04) != 0) {
- goto wait_nomsg;
- }
- outb(1, 0x80);
+ atp_writew_io(dev, 0, 0x1c, val);
+ while ((atp_readb_io(dev, 0, 0x1c) & 0x04) != 0)
+ ;
+ udelay(2);
udelay(100);
- for (n = 0; n < 0x30000; n++) {
- if ((inb(tmport) & 0x80) != 0) { /* bsy ? */
- goto wait_io;
- }
- }
- goto TCM_SYNC;
-wait_io:
- for (n = 0; n < 0x30000; n++) {
- if ((inb(tmport) & 0x81) == 0x0081) {
- goto wait_io1;
- }
- }
- goto TCM_SYNC;
-wait_io1:
- inb(0x80);
- val |= 0x8003; /* io,cd,db7 */
- outw(val, tmport);
- inb(0x80);
- val &= 0x00bf; /* no sel */
- outw(val, tmport);
- outb(2, 0x80);
-TCM_SYNC:
- udelay(0x800);
- if ((inb(tmport) & 0x80) == 0x00) { /* bsy ? */
- outw(0, tmport--);
- outb(0, tmport);
- tmport = dev->ioport[0] + 0x15;
- outb(0, tmport);
- tmport += 0x03;
- outb(0x09, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0)
+ for (n = 0; n < 0x30000; n++)
+ if ((atp_readb_io(dev, 0, 0x1c) & 0x80) != 0) /* bsy ? */
+ break;
+ if (n < 0x30000)
+ for (n = 0; n < 0x30000; n++)
+ if ((atp_readb_io(dev, 0, 0x1c) & 0x81) == 0x0081) {
+ udelay(2);
+ val |= 0x8003; /* io,cd,db7 */
+ atp_writew_io(dev, 0, 0x1c, val);
+ udelay(2);
+ val &= 0x00bf; /* no sel */
+ atp_writew_io(dev, 0, 0x1c, val);
+ udelay(2);
+ break;
+ }
+ while (1) {
+ /*
+ * The funny division into multiple delays is to accomodate
+ * arches like ARM where udelay() multiplies its argument by
+ * a large number to initialize a loop counter. To avoid
+ * overflow, the maximum supported udelay is 2000 microseconds.
+ *
+ * XXX it would be more polite to find a way to use msleep()
+ */
+ mdelay(2);
+ udelay(48);
+ if ((atp_readb_io(dev, 0, 0x1c) & 0x80) == 0x00) { /* bsy ? */
+ atp_writew_io(dev, 0, 0x1c, 0);
+ atp_writeb_io(dev, 0, 0x1b, 0);
+ atp_writeb_io(dev, 0, 0x15, 0);
+ atp_writeb_io(dev, 0, 0x18, 0x09);
+ while ((atp_readb_io(dev, 0, 0x1f) & 0x80) == 0)
cpu_relax();
- tmport -= 0x08;
- inb(tmport);
+ atp_readb_io(dev, 0, 0x17);
return;
}
val &= 0x00ff; /* synchronization */
val |= 0x3f00;
fun_scam(dev, &val);
- outb(3, 0x80);
+ udelay(2);
val &= 0x00ff; /* isolation */
val |= 0x2000;
fun_scam(dev, &val);
- outb(4, 0x80);
+ udelay(2);
i = 8;
j = 0;
-TCM_ID:
- if ((inw(tmport) & 0x2000) == 0) {
- goto TCM_ID;
- }
- outb(5, 0x80);
- val &= 0x00ff; /* get ID_STRING */
- val |= 0x2000;
- k = fun_scam(dev, &val);
- if ((k & 0x03) == 0) {
- goto TCM_5;
- }
- mbuf[j] <<= 0x01;
- mbuf[j] &= 0xfe;
- if ((k & 0x02) != 0) {
- mbuf[j] |= 0x01;
- }
- i--;
- if (i > 0) {
- goto TCM_ID;
+
+ while (1) {
+ if ((atp_readw_io(dev, 0, 0x1c) & 0x2000) == 0)
+ continue;
+ udelay(2);
+ val &= 0x00ff; /* get ID_STRING */
+ val |= 0x2000;
+ k = fun_scam(dev, &val);
+ if ((k & 0x03) == 0)
+ break;
+ mbuf[j] <<= 0x01;
+ mbuf[j] &= 0xfe;
+ if ((k & 0x02) != 0)
+ mbuf[j] |= 0x01;
+ i--;
+ if (i > 0)
+ continue;
+ j++;
+ i = 8;
}
- j++;
- i = 8;
- goto TCM_ID;
-TCM_5: /* isolation complete.. */
+ /* isolation complete.. */
/* mbuf[32]=0;
printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */
i = 15;
@@ -1231,33 +1185,33 @@ TCM_5: /* isolation complete.. */
if ((j & 0x20) != 0) { /* bit5=1:ID up to 7 */
i = 7;
}
- if ((j & 0x06) == 0) { /* IDvalid? */
- goto G2Q5;
- }
- k = mbuf[1];
-small_id:
- m = 1;
- m <<= k;
- if ((m & assignid_map) == 0) {
- goto G2Q_QUIN;
- }
- if (k > 0) {
- k--;
- goto small_id;
- }
-G2Q5: /* srch from max acceptable ID# */
- k = i; /* max acceptable ID# */
-G2Q_LP:
- m = 1;
- m <<= k;
- if ((m & assignid_map) == 0) {
- goto G2Q_QUIN;
+ if ((j & 0x06) != 0) { /* IDvalid? */
+ k = mbuf[1];
+ while (1) {
+ m = 1;
+ m <<= k;
+ if ((m & assignid_map) == 0)
+ break;
+ if (k > 0)
+ k--;
+ else
+ break;
+ }
}
- if (k > 0) {
- k--;
- goto G2Q_LP;
+ if ((m & assignid_map) != 0) { /* srch from max acceptable ID# */
+ k = i; /* max acceptable ID# */
+ while (1) {
+ m = 1;
+ m <<= k;
+ if ((m & assignid_map) == 0)
+ break;
+ if (k > 0)
+ k--;
+ else
+ break;
+ }
}
-G2Q_QUIN: /* k=binID#, */
+ /* k=binID#, */
assignid_map |= m;
if (k < 8) {
quintet[0] = 0x38; /* 1st dft ID<8 */
@@ -1276,1767 +1230,410 @@ G2Q_QUIN: /* k=binID#, */
val |= m;
fun_scam(dev, &val);
- goto TCM_SYNC;
-
+ }
}
-static void is870(struct atp_unit *dev, unsigned int wkport)
+static void atp870u_free_tables(struct Scsi_Host *host)
{
- unsigned int tmport;
- unsigned char i, j, k, rmb, n;
- unsigned short int m;
- static unsigned char mbuf[512];
- static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
- static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
- static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
- static unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0c, 0x0e };
- static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x0c, 0x07 };
- static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
-
- tmport = wkport + 0x3a;
- outb((unsigned char) (inb(tmport) | 0x10), tmport);
-
- for (i = 0; i < 16; i++) {
- if ((dev->chip_ver != 4) && (i > 7)) {
- break;
- }
- m = 1;
- m = m << i;
- if ((m & dev->active_id[0]) != 0) {
- continue;
- }
- if (i == dev->host_id[0]) {
- printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[0]);
- continue;
- }
- tmport = wkport + 0x1b;
- if (dev->chip_ver == 4) {
- outb(0x01, tmport);
- } else {
- outb(0x00, tmport);
- }
- tmport = wkport + 1;
- outb(0x08, tmport++);
- outb(0x7f, tmport++);
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- j = i;
- if ((j & 0x08) != 0) {
- j = (j & 0x07) | 0x40;
- }
- outb(j, tmport);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- dev->active_id[0] |= m;
-
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x04;
- outb(0x00, tmport);
-
-phase_cmd:
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- tmport = wkport + 0x10;
- outb(0x41, tmport);
- goto phase_cmd;
- }
-sel_ok:
- tmport = wkport + 3;
- outb(inqd[0], tmport++);
- outb(inqd[1], tmport++);
- outb(inqd[2], tmport++);
- outb(inqd[3], tmport++);
- outb(inqd[4], tmport++);
- outb(inqd[5], tmport);
- tmport += 0x07;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(inqd[6], tmport++);
- outb(inqd[7], tmport++);
- tmport += 0x03;
- outb(inqd[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- tmport = wkport + 0x1b;
- if (dev->chip_ver == 4)
- outb(0x00, tmport);
-
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
- j = 0;
-rd_inq_data:
- k = inb(tmport);
- if ((k & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[j++] = inb(tmport);
- tmport += 0x06;
- goto rd_inq_data;
- }
- if ((k & 0x80) == 0) {
- goto rd_inq_data;
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x16) {
- goto inq_ok;
- }
- tmport = wkport + 0x10;
- outb(0x46, tmport);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x16) {
- goto sel_ok;
- }
-inq_ok:
- mbuf[36] = 0;
- printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
- dev->id[0][i].devtype = mbuf[0];
- rmb = mbuf[1];
- n = mbuf[7];
- if (dev->chip_ver != 4) {
- goto not_wide;
- }
- if ((mbuf[7] & 0x60) == 0) {
- goto not_wide;
- }
- if ((dev->global_map[0] & 0x20) == 0) {
- goto not_wide;
- }
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 3;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_wide:
- j = 0;
- tmport = wkport + 0x14;
- outb(0x05, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(wide[j++], tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto try_wide;
- }
- continue;
-widep_out:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
- }
- continue;
-widep_in:
- tmport = wkport + 0x14;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-widep_in1:
- j = inb(tmport);
- if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto widep_in1;
- }
- if ((j & 0x80) == 0x00) {
- goto widep_in1;
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
+ struct atp_unit *atp_dev = (struct atp_unit *)&host->hostdata;
+ int j, k;
+ for (j=0; j < 2; j++) {
+ for (k = 0; k < 16; k++) {
+ if (!atp_dev->id[j][k].prd_table)
+ continue;
+ dma_free_coherent(&atp_dev->pdev->dev, 1024,
+ atp_dev->id[j][k].prd_table,
+ atp_dev->id[j][k].prd_bus);
+ atp_dev->id[j][k].prd_table = NULL;
}
- continue;
-widep_cmd:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
+ }
+}
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- if (j == 0x4e) {
- goto widep_out;
+static int atp870u_init_tables(struct Scsi_Host *host)
+{
+ struct atp_unit *atp_dev = (struct atp_unit *)&host->hostdata;
+ int c,k;
+ for(c=0;c < 2;c++) {
+ for(k=0;k<16;k++) {
+ atp_dev->id[c][k].prd_table =
+ dma_alloc_coherent(&atp_dev->pdev->dev, 1024,
+ &(atp_dev->id[c][k].prd_bus),
+ GFP_KERNEL);
+ if (!atp_dev->id[c][k].prd_table) {
+ printk("atp870u_init_tables fail\n");
+ atp870u_free_tables(host);
+ return -ENOMEM;
}
- continue;
- }
- if (mbuf[0] != 0x01) {
- goto not_wide;
- }
- if (mbuf[1] != 0x02) {
- goto not_wide;
- }
- if (mbuf[2] != 0x03) {
- goto not_wide;
- }
- if (mbuf[3] != 0x01) {
- goto not_wide;
- }
- m = 1;
- m = m << i;
- dev->wide_id[0] |= m;
-not_wide:
- if ((dev->id[0][i].devtype == 0x00) || (dev->id[0][i].devtype == 0x07) || ((dev->id[0][i].devtype == 0x05) && ((n & 0x10) != 0))) {
- goto set_sync;
- }
- continue;
-set_sync:
- tmport = wkport + 0x1b;
- j = 0;
- if ((m & dev->wide_id[0]) != 0) {
- j |= 0x01;
+ atp_dev->id[c][k].prdaddr = atp_dev->id[c][k].prd_bus;
+ atp_dev->id[c][k].devsp=0x20;
+ atp_dev->id[c][k].devtype = 0x7f;
+ atp_dev->id[c][k].curr_req = NULL;
}
- outb(j, tmport);
- tmport = wkport + 3;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_sync:
- j = 0;
- tmport = wkport + 0x14;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- if ((m & dev->wide_id[0]) != 0) {
- outb(synw[j++], tmport);
- } else {
- if ((m & dev->ultra_map[0]) != 0) {
- outb(synu[j++], tmport);
- } else {
- outb(synn[j++], tmport);
- }
- }
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto try_sync;
- }
- continue;
-phase_outs:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00) {
- if ((inb(tmport) & 0x01) != 0x00) {
- tmport -= 0x06;
- outb(0x00, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_ins:
- tmport = wkport + 0x14;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-phase_ins1:
- j = inb(tmport);
- if ((j & 0x01) != 0x00) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto phase_ins1;
- }
- if ((j & 0x80) == 0x00) {
- goto phase_ins1;
- }
- tmport -= 0x08;
+ atp_dev->active_id[c] = 0;
+ atp_dev->wide_id[c] = 0;
+ atp_dev->host_id[c] = 0x07;
+ atp_dev->quhd[c] = 0;
+ atp_dev->quend[c] = 0;
+ atp_dev->last_cmd[c] = 0xff;
+ atp_dev->in_snd[c] = 0;
+ atp_dev->in_int[c] = 0;
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_cmds:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
-tar_dcons:
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- continue;
- }
- if (mbuf[0] != 0x01) {
- continue;
- }
- if (mbuf[1] != 0x03) {
- continue;
- }
- if (mbuf[4] == 0x00) {
- continue;
- }
- if (mbuf[3] > 0x64) {
- continue;
- }
- if (mbuf[4] > 0x0c) {
- mbuf[4] = 0x0c;
+ for (k = 0; k < qcnt; k++) {
+ atp_dev->quereq[c][k] = NULL;
}
- dev->id[0][i].devsp = mbuf[4];
- if ((mbuf[3] < 0x0d) && (rmb == 0)) {
- j = 0xa0;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x1a) {
- j = 0x20;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x33) {
- j = 0x40;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x4c) {
- j = 0x50;
- goto set_syn_ok;
+ for (k = 0; k < 16; k++) {
+ atp_dev->id[c][k].curr_req = NULL;
+ atp_dev->sp[c][k] = 0x04;
}
- j = 0x60;
-set_syn_ok:
- dev->id[0][i].devsp = (dev->id[0][i].devsp & 0x0f) | j;
}
- tmport = wkport + 0x3a;
- outb((unsigned char) (inb(tmport) & 0xef), tmport);
+ return 0;
}
-static void is880(struct atp_unit *dev, unsigned int wkport)
+static void atp_set_host_id(struct atp_unit *atp, u8 c, u8 host_id)
{
- unsigned int tmport;
- unsigned char i, j, k, rmb, n, lvdmode;
- unsigned short int m;
- static unsigned char mbuf[512];
- static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
- static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
- static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
- unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
- static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
- unsigned char synuw[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
- static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
- static unsigned char u3[9] = { 0x80, 1, 6, 4, 0x09, 00, 0x0e, 0x01, 0x02 };
-
- lvdmode = inb(wkport + 0x3f) & 0x40;
-
- for (i = 0; i < 16; i++) {
- m = 1;
- m = m << i;
- if ((m & dev->active_id[0]) != 0) {
- continue;
- }
- if (i == dev->host_id[0]) {
- printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[0]);
- continue;
- }
- tmport = wkport + 0x5b;
- outb(0x01, tmport);
- tmport = wkport + 0x41;
- outb(0x08, tmport++);
- outb(0x7f, tmport++);
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- j = i;
- if ((j & 0x08) != 0) {
- j = (j & 0x07) | 0x40;
- }
- outb(j, tmport);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
+ atp_writeb_io(atp, c, 0, host_id | 0x08);
+ atp_writeb_io(atp, c, 0x18, 0);
+ while ((atp_readb_io(atp, c, 0x1f) & 0x80) == 0)
+ mdelay(1);
+ atp_readb_io(atp, c, 0x17);
+ atp_writeb_io(atp, c, 1, 8);
+ atp_writeb_io(atp, c, 2, 0x7f);
+ atp_writeb_io(atp, c, 0x11, 0x20);
+}
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
+static void atp870_init(struct Scsi_Host *shpnt)
+{
+ struct atp_unit *atpdev = shost_priv(shpnt);
+ struct pci_dev *pdev = atpdev->pdev;
+ unsigned char k, host_id;
+ u8 scam_on;
+ bool wide_chip =
+ (pdev->device == PCI_DEVICE_ID_ARTOP_AEC7610 &&
+ pdev->revision == 4) ||
+ (pdev->device == PCI_DEVICE_ID_ARTOP_AEC7612UW) ||
+ (pdev->device == PCI_DEVICE_ID_ARTOP_AEC7612SUW);
+
+ pci_read_config_byte(pdev, 0x49, &host_id);
+
+ dev_info(&pdev->dev, "ACARD AEC-671X PCI Ultra/W SCSI-2/3 "
+ "Host Adapter: IO:%lx, IRQ:%d.\n",
+ shpnt->io_port, shpnt->irq);
+
+ atpdev->ioport[0] = shpnt->io_port;
+ atpdev->pciport[0] = shpnt->io_port + 0x20;
+ host_id &= 0x07;
+ atpdev->host_id[0] = host_id;
+ scam_on = atp_readb_pci(atpdev, 0, 2);
+ atpdev->global_map[0] = atp_readb_base(atpdev, 0x2d);
+ atpdev->ultra_map[0] = atp_readw_base(atpdev, 0x2e);
+
+ if (atpdev->ultra_map[0] == 0) {
+ scam_on = 0x00;
+ atpdev->global_map[0] = 0x20;
+ atpdev->ultra_map[0] = 0xffff;
+ }
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- dev->active_id[0] |= m;
+ if (pdev->revision > 0x07) /* check if atp876 chip */
+ atp_writeb_base(atpdev, 0x3e, 0x00); /* enable terminator */
+
+ k = (atp_readb_base(atpdev, 0x3a) & 0xf3) | 0x10;
+ atp_writeb_base(atpdev, 0x3a, k);
+ atp_writeb_base(atpdev, 0x3a, k & 0xdf);
+ msleep(32);
+ atp_writeb_base(atpdev, 0x3a, k);
+ msleep(32);
+ atp_set_host_id(atpdev, 0, host_id);
+
+ tscam(shpnt, wide_chip, scam_on);
+ atp_writeb_base(atpdev, 0x3a, atp_readb_base(atpdev, 0x3a) | 0x10);
+ atp_is(atpdev, 0, wide_chip, 0);
+ atp_writeb_base(atpdev, 0x3a, atp_readb_base(atpdev, 0x3a) & 0xef);
+ atp_writeb_base(atpdev, 0x3b, atp_readb_base(atpdev, 0x3b) | 0x20);
+ shpnt->max_id = wide_chip ? 16 : 8;
+ shpnt->this_id = host_id;
+}
- tmport = wkport + 0x50;
- outb(0x30, tmport);
- tmport = wkport + 0x54;
- outb(0x00, tmport);
+static void atp880_init(struct Scsi_Host *shpnt)
+{
+ struct atp_unit *atpdev = shost_priv(shpnt);
+ struct pci_dev *pdev = atpdev->pdev;
+ unsigned char k, m, host_id;
+ unsigned int n;
-phase_cmd:
- tmport = wkport + 0x58;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80);
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- tmport = wkport + 0x50;
- outb(0x41, tmport);
- goto phase_cmd;
- }
-sel_ok:
- tmport = wkport + 0x43;
- outb(inqd[0], tmport++);
- outb(inqd[1], tmport++);
- outb(inqd[2], tmport++);
- outb(inqd[3], tmport++);
- outb(inqd[4], tmport++);
- outb(inqd[5], tmport);
- tmport += 0x07;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(inqd[6], tmport++);
- outb(inqd[7], tmport++);
- tmport += 0x03;
- outb(inqd[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
+ atpdev->ioport[0] = shpnt->io_port + 0x40;
+ atpdev->pciport[0] = shpnt->io_port + 0x28;
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- tmport = wkport + 0x5b;
- outb(0x00, tmport);
- tmport = wkport + 0x58;
- outb(0x08, tmport);
- tmport += 0x07;
- j = 0;
-rd_inq_data:
- k = inb(tmport);
- if ((k & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[j++] = inb(tmport);
- tmport += 0x06;
- goto rd_inq_data;
- }
- if ((k & 0x80) == 0) {
- goto rd_inq_data;
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x16) {
- goto inq_ok;
- }
- tmport = wkport + 0x50;
- outb(0x46, tmport);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x16)
- goto sel_ok;
+ host_id = atp_readb_base(atpdev, 0x39) >> 4;
-inq_ok:
- mbuf[36] = 0;
- printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
- dev->id[0][i].devtype = mbuf[0];
- rmb = mbuf[1];
- n = mbuf[7];
- if ((mbuf[7] & 0x60) == 0) {
- goto not_wide;
- }
- if ((i < 8) && ((dev->global_map[0] & 0x20) == 0)) {
- goto not_wide;
- }
- if (lvdmode == 0) {
- goto chg_wide;
- }
- if (dev->sp[0][i] != 0x04) // force u2
- {
- goto chg_wide;
- }
+ dev_info(&pdev->dev, "ACARD AEC-67160 PCI Ultra3 LVD "
+ "Host Adapter: IO:%lx, IRQ:%d.\n",
+ shpnt->io_port, shpnt->irq);
+ atpdev->host_id[0] = host_id;
- tmport = wkport + 0x5b;
- outb(0x01, tmport);
- tmport = wkport + 0x43;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
+ atpdev->global_map[0] = atp_readb_base(atpdev, 0x35);
+ atpdev->ultra_map[0] = atp_readw_base(atpdev, 0x3c);
- tmport -= 0x08;
+ n = 0x3f09;
+ while (n < 0x4000) {
+ m = 0;
+ atp_writew_base(atpdev, 0x34, n);
+ n += 0x0002;
+ if (atp_readb_base(atpdev, 0x30) == 0xff)
+ break;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
+ atp_writew_base(atpdev, 0x34, n);
+ n += 0x0002;
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
+ atp_writew_base(atpdev, 0x34, n);
+ n += 0x0002;
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
+ atp_writew_base(atpdev, 0x34, n);
+ n += 0x0002;
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
+ n += 0x0018;
+ }
+ atp_writew_base(atpdev, 0x34, 0);
+ atpdev->ultra_map[0] = 0;
+ atpdev->async[0] = 0;
+ for (k = 0; k < 16; k++) {
+ n = 1 << k;
+ if (atpdev->sp[0][k] > 1)
+ atpdev->ultra_map[0] |= n;
+ else
+ if (atpdev->sp[0][k] == 0)
+ atpdev->async[0] |= n;
+ }
+ atpdev->async[0] = ~(atpdev->async[0]);
+ atp_writeb_base(atpdev, 0x35, atpdev->global_map[0]);
+
+ k = atp_readb_base(atpdev, 0x38) & 0x80;
+ atp_writeb_base(atpdev, 0x38, k);
+ atp_writeb_base(atpdev, 0x3b, 0x20);
+ msleep(32);
+ atp_writeb_base(atpdev, 0x3b, 0);
+ msleep(32);
+ atp_readb_io(atpdev, 0, 0x1b);
+ atp_readb_io(atpdev, 0, 0x17);
+
+ atp_set_host_id(atpdev, 0, host_id);
+
+ tscam(shpnt, true, atp_readb_base(atpdev, 0x22));
+ atp_is(atpdev, 0, true, atp_readb_base(atpdev, 0x3f) & 0x40);
+ atp_writeb_base(atpdev, 0x38, 0xb0);
+ shpnt->max_id = 16;
+ shpnt->this_id = host_id;
+}
- while (inb(tmport) != 0x8e)
- cpu_relax();
+static void atp885_init(struct Scsi_Host *shpnt)
+{
+ struct atp_unit *atpdev = shost_priv(shpnt);
+ struct pci_dev *pdev = atpdev->pdev;
+ unsigned char k, m, c;
+ unsigned int n;
+ unsigned char setupdata[2][16];
-try_u3:
- j = 0;
- tmport = wkport + 0x54;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(u3[j++], tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
+ dev_info(&pdev->dev, "ACARD AEC-67162 PCI Ultra3 LVD "
+ "Host Adapter: IO:%lx, IRQ:%d.\n",
+ shpnt->io_port, shpnt->irq);
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto u3p_in;
- }
- if (j == 0x0a) {
- goto u3p_cmd;
- }
- if (j == 0x0e) {
- goto try_u3;
- }
- continue;
-u3p_out:
- tmport = wkport + 0x58;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto u3p_in;
- }
- if (j == 0x0a) {
- goto u3p_cmd;
- }
- if (j == 0x0e) {
- goto u3p_out;
- }
- continue;
-u3p_in:
- tmport = wkport + 0x54;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-u3p_in1:
- j = inb(tmport);
- if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto u3p_in1;
- }
- if ((j & 0x80) == 0x00) {
- goto u3p_in1;
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto u3p_in;
- }
- if (j == 0x0a) {
- goto u3p_cmd;
- }
- if (j == 0x0e) {
- goto u3p_out;
- }
- continue;
-u3p_cmd:
- tmport = wkport + 0x50;
- outb(0x30, tmport);
- tmport = wkport + 0x54;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- if (j == 0x4e) {
- goto u3p_out;
- }
- continue;
- }
- if (mbuf[0] != 0x01) {
- goto chg_wide;
- }
- if (mbuf[1] != 0x06) {
- goto chg_wide;
- }
- if (mbuf[2] != 0x04) {
- goto chg_wide;
- }
- if (mbuf[3] == 0x09) {
- m = 1;
- m = m << i;
- dev->wide_id[0] |= m;
- dev->id[0][i].devsp = 0xce;
- continue;
- }
-chg_wide:
- tmport = wkport + 0x5b;
- outb(0x01, tmport);
- tmport = wkport + 0x43;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
+ atpdev->ioport[0] = shpnt->io_port + 0x80;
+ atpdev->ioport[1] = shpnt->io_port + 0xc0;
+ atpdev->pciport[0] = shpnt->io_port + 0x40;
+ atpdev->pciport[1] = shpnt->io_port + 0x50;
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_wide:
- j = 0;
- tmport = wkport + 0x54;
- outb(0x05, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(wide[j++], tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto try_wide;
- }
- continue;
-widep_out:
- tmport = wkport + 0x58;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
- }
- continue;
-widep_in:
- tmport = wkport + 0x54;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-widep_in1:
- j = inb(tmport);
- if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto widep_in1;
- }
- if ((j & 0x80) == 0x00) {
- goto widep_in1;
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
- }
- continue;
-widep_cmd:
- tmport = wkport + 0x50;
- outb(0x30, tmport);
- tmport = wkport + 0x54;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
+ c = atp_readb_base(atpdev, 0x29);
+ atp_writeb_base(atpdev, 0x29, c | 0x04);
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- if (j == 0x4e) {
- goto widep_out;
- }
- continue;
- }
- if (mbuf[0] != 0x01) {
- goto not_wide;
- }
- if (mbuf[1] != 0x02) {
- goto not_wide;
- }
- if (mbuf[2] != 0x03) {
- goto not_wide;
- }
- if (mbuf[3] != 0x01) {
- goto not_wide;
- }
- m = 1;
- m = m << i;
- dev->wide_id[0] |= m;
-not_wide:
- if ((dev->id[0][i].devtype == 0x00) || (dev->id[0][i].devtype == 0x07) || ((dev->id[0][i].devtype == 0x05) && ((n & 0x10) != 0))) {
- m = 1;
- m = m << i;
- if ((dev->async[0] & m) != 0) {
- goto set_sync;
- }
- }
- continue;
-set_sync:
- if (dev->sp[0][i] == 0x02) {
- synu[4] = 0x0c;
- synuw[4] = 0x0c;
- } else {
- if (dev->sp[0][i] >= 0x03) {
- synu[4] = 0x0a;
- synuw[4] = 0x0a;
+ n = 0x1f80;
+ while (n < 0x2000) {
+ atp_writew_base(atpdev, 0x3c, n);
+ if (atp_readl_base(atpdev, 0x38) == 0xffffffff)
+ break;
+ for (m = 0; m < 2; m++) {
+ atpdev->global_map[m] = 0;
+ for (k = 0; k < 4; k++) {
+ atp_writew_base(atpdev, 0x3c, n++);
+ ((u32 *)&setupdata[m][0])[k] =
+ atp_readl_base(atpdev, 0x38);
}
- }
- tmport = wkport + 0x5b;
- j = 0;
- if ((m & dev->wide_id[0]) != 0) {
- j |= 0x01;
- }
- outb(j, tmport);
- tmport = wkport + 0x43;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
- continue;
- }
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_sync:
- j = 0;
- tmport = wkport + 0x54;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- if ((m & dev->wide_id[0]) != 0) {
- if ((m & dev->ultra_map[0]) != 0) {
- outb(synuw[j++], tmport);
- } else {
- outb(synw[j++], tmport);
- }
- } else {
- if ((m & dev->ultra_map[0]) != 0) {
- outb(synu[j++], tmport);
- } else {
- outb(synn[j++], tmport);
- }
- }
- tmport += 0x06;
+ for (k = 0; k < 4; k++) {
+ atp_writew_base(atpdev, 0x3c, n++);
+ ((u32 *)&atpdev->sp[m][0])[k] =
+ atp_readl_base(atpdev, 0x38);
}
+ n += 8;
}
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto try_sync;
- }
- continue;
-phase_outs:
- tmport = wkport + 0x58;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00) {
- if ((inb(tmport) & 0x01) != 0x00) {
- tmport -= 0x06;
- outb(0x00, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_ins:
- tmport = wkport + 0x54;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-phase_ins1:
- j = inb(tmport);
- if ((j & 0x01) != 0x00) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto phase_ins1;
- }
- if ((j & 0x80) == 0x00) {
- goto phase_ins1;
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_cmds:
- tmport = wkport + 0x50;
- outb(0x30, tmport);
-tar_dcons:
- tmport = wkport + 0x54;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- continue;
- }
- if (mbuf[0] != 0x01) {
- continue;
- }
- if (mbuf[1] != 0x03) {
- continue;
- }
- if (mbuf[4] == 0x00) {
- continue;
- }
- if (mbuf[3] > 0x64) {
- continue;
- }
- if (mbuf[4] > 0x0e) {
- mbuf[4] = 0x0e;
- }
- dev->id[0][i].devsp = mbuf[4];
- if (mbuf[3] < 0x0c) {
- j = 0xb0;
- goto set_syn_ok;
- }
- if ((mbuf[3] < 0x0d) && (rmb == 0)) {
- j = 0xa0;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x1a) {
- j = 0x20;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x33) {
- j = 0x40;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x4c) {
- j = 0x50;
- goto set_syn_ok;
- }
- j = 0x60;
-set_syn_ok:
- dev->id[0][i].devsp = (dev->id[0][i].devsp & 0x0f) | j;
}
-}
-
-static void atp870u_free_tables(struct Scsi_Host *host)
-{
- struct atp_unit *atp_dev = (struct atp_unit *)&host->hostdata;
- int j, k;
- for (j=0; j < 2; j++) {
+ c = atp_readb_base(atpdev, 0x29);
+ atp_writeb_base(atpdev, 0x29, c & 0xfb);
+ for (c = 0; c < 2; c++) {
+ atpdev->ultra_map[c] = 0;
+ atpdev->async[c] = 0;
for (k = 0; k < 16; k++) {
- if (!atp_dev->id[j][k].prd_table)
- continue;
- pci_free_consistent(atp_dev->pdev, 1024, atp_dev->id[j][k].prd_table, atp_dev->id[j][k].prd_bus);
- atp_dev->id[j][k].prd_table = NULL;
+ n = 1 << k;
+ if (atpdev->sp[c][k] > 1)
+ atpdev->ultra_map[c] |= n;
+ else
+ if (atpdev->sp[c][k] == 0)
+ atpdev->async[c] |= n;
+ }
+ atpdev->async[c] = ~(atpdev->async[c]);
+
+ if (atpdev->global_map[c] == 0) {
+ k = setupdata[c][1];
+ if ((k & 0x40) != 0)
+ atpdev->global_map[c] |= 0x20;
+ k &= 0x07;
+ atpdev->global_map[c] |= k;
+ if ((setupdata[c][2] & 0x04) != 0)
+ atpdev->global_map[c] |= 0x08;
+ atpdev->host_id[c] = setupdata[c][0] & 0x07;
}
}
-}
-static int atp870u_init_tables(struct Scsi_Host *host)
-{
- struct atp_unit *atp_dev = (struct atp_unit *)&host->hostdata;
- int c,k;
- for(c=0;c < 2;c++) {
- for(k=0;k<16;k++) {
- atp_dev->id[c][k].prd_table = pci_alloc_consistent(atp_dev->pdev, 1024, &(atp_dev->id[c][k].prd_bus));
- if (!atp_dev->id[c][k].prd_table) {
- printk("atp870u_init_tables fail\n");
- atp870u_free_tables(host);
- return -ENOMEM;
- }
- atp_dev->id[c][k].prdaddr = atp_dev->id[c][k].prd_bus;
- atp_dev->id[c][k].devsp=0x20;
- atp_dev->id[c][k].devtype = 0x7f;
- atp_dev->id[c][k].curr_req = NULL;
- }
-
- atp_dev->active_id[c] = 0;
- atp_dev->wide_id[c] = 0;
- atp_dev->host_id[c] = 0x07;
- atp_dev->quhd[c] = 0;
- atp_dev->quend[c] = 0;
- atp_dev->last_cmd[c] = 0xff;
- atp_dev->in_snd[c] = 0;
- atp_dev->in_int[c] = 0;
-
- for (k = 0; k < qcnt; k++) {
- atp_dev->quereq[c][k] = NULL;
- }
- for (k = 0; k < 16; k++) {
- atp_dev->id[c][k].curr_req = NULL;
- atp_dev->sp[c][k] = 0x04;
- }
- }
- return 0;
+ k = atp_readb_base(atpdev, 0x28) & 0x8f;
+ k |= 0x10;
+ atp_writeb_base(atpdev, 0x28, k);
+ atp_writeb_pci(atpdev, 0, 1, 0x80);
+ atp_writeb_pci(atpdev, 1, 1, 0x80);
+ msleep(100);
+ atp_writeb_pci(atpdev, 0, 1, 0);
+ atp_writeb_pci(atpdev, 1, 1, 0);
+ msleep(1000);
+ atp_readb_io(atpdev, 0, 0x1b);
+ atp_readb_io(atpdev, 0, 0x17);
+ atp_readb_io(atpdev, 1, 0x1b);
+ atp_readb_io(atpdev, 1, 0x17);
+
+ k = atpdev->host_id[0];
+ if (k > 7)
+ k = (k & 0x07) | 0x40;
+ atp_set_host_id(atpdev, 0, k);
+
+ k = atpdev->host_id[1];
+ if (k > 7)
+ k = (k & 0x07) | 0x40;
+ atp_set_host_id(atpdev, 1, k);
+
+ msleep(600); /* this delay used to be called tscam_885() */
+ dev_info(&pdev->dev, "Scanning Channel A SCSI Device ...\n");
+ atp_is(atpdev, 0, true, atp_readb_io(atpdev, 0, 0x1b) >> 7);
+ atp_writeb_io(atpdev, 0, 0x16, 0x80);
+ dev_info(&pdev->dev, "Scanning Channel B SCSI Device ...\n");
+ atp_is(atpdev, 1, true, atp_readb_io(atpdev, 1, 0x1b) >> 7);
+ atp_writeb_io(atpdev, 1, 0x16, 0x80);
+ k = atp_readb_base(atpdev, 0x28) & 0xcf;
+ k |= 0xc0;
+ atp_writeb_base(atpdev, 0x28, k);
+ k = atp_readb_base(atpdev, 0x1f) | 0x80;
+ atp_writeb_base(atpdev, 0x1f, k);
+ k = atp_readb_base(atpdev, 0x29) | 0x01;
+ atp_writeb_base(atpdev, 0x29, k);
+ shpnt->max_id = 16;
+ shpnt->max_lun = (atpdev->global_map[0] & 0x07) + 1;
+ shpnt->max_channel = 1;
+ shpnt->this_id = atpdev->host_id[0];
}
/* return non-zero on detection */
static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- unsigned char k, m, c;
- unsigned long flags;
- unsigned int base_io, tmport, error,n;
- unsigned char host_id;
struct Scsi_Host *shpnt = NULL;
- struct atp_unit *atpdev, *p;
- unsigned char setupdata[2][16];
- int count = 0;
+ struct atp_unit *atpdev;
+ int err;
- atpdev = kzalloc(sizeof(*atpdev), GFP_KERNEL);
- if (!atpdev)
- return -ENOMEM;
+ if (ent->device == PCI_DEVICE_ID_ARTOP_AEC7610 && pdev->revision < 2) {
+ dev_err(&pdev->dev, "ATP850S chips (AEC6710L/F cards) are not supported.\n");
+ return -ENODEV;
+ }
- if (pci_enable_device(pdev))
- goto err_eio;
+ err = pci_enable_device(pdev);
+ if (err)
+ goto fail;
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
- printk(KERN_INFO "atp870u: use 32bit DMA mask.\n");
- } else {
- printk(KERN_ERR "atp870u: DMA mask required but not available.\n");
- goto err_eio;
- }
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
+ printk(KERN_ERR "atp870u: DMA mask required but not available.\n");
+ err = -EIO;
+ goto disable_device;
+ }
- /*
- * It's probably easier to weed out some revisions like
- * this than via the PCI device table
- */
- if (ent->device == PCI_DEVICE_ID_ARTOP_AEC7610) {
- error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atpdev->chip_ver);
- if (atpdev->chip_ver < 2)
- goto err_eio;
+ err = pci_request_regions(pdev, "atp870u");
+ if (err)
+ goto disable_device;
+ pci_set_master(pdev);
+
+ err = -ENOMEM;
+ shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
+ if (!shpnt)
+ goto release_region;
+
+ atpdev = shost_priv(shpnt);
+
+ atpdev->host = shpnt;
+ atpdev->pdev = pdev;
+ pci_set_drvdata(pdev, atpdev);
+
+ shpnt->io_port = pci_resource_start(pdev, 0);
+ shpnt->io_port &= 0xfffffff8;
+ shpnt->n_io_port = pci_resource_len(pdev, 0);
+ atpdev->baseport = shpnt->io_port;
+ shpnt->unique_id = shpnt->io_port;
+ shpnt->irq = pdev->irq;
+
+ err = atp870u_init_tables(shpnt);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate tables for Acard controller\n");
+ goto unregister;
}
- switch (ent->device) {
- case PCI_DEVICE_ID_ARTOP_AEC7612UW:
- case PCI_DEVICE_ID_ARTOP_AEC7612SUW:
- case ATP880_DEVID1:
- case ATP880_DEVID2:
- case ATP885_DEVID:
- atpdev->chip_ver = 0x04;
- default:
- break;
+ if (is880(atpdev))
+ atp880_init(shpnt);
+ else if (is885(atpdev))
+ atp885_init(shpnt);
+ else
+ atp870_init(shpnt);
+
+ err = request_irq(shpnt->irq, atp870u_intr_handle, IRQF_SHARED, "atp870u", shpnt);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate IRQ %d.\n", shpnt->irq);
+ goto free_tables;
}
- base_io = pci_resource_start(pdev, 0);
- base_io &= 0xfffffff8;
-
- if ((ent->device == ATP880_DEVID1)||(ent->device == ATP880_DEVID2)) {
- error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atpdev->chip_ver);
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80);//JCC082803
-
- host_id = inb(base_io + 0x39);
- host_id >>= 0x04;
-
- printk(KERN_INFO " ACARD AEC-67160 PCI Ultra3 LVD Host Adapter: %d"
- " IO:%x, IRQ:%d.\n", count, base_io, pdev->irq);
- atpdev->ioport[0] = base_io + 0x40;
- atpdev->pciport[0] = base_io + 0x28;
- atpdev->dev_id = ent->device;
- atpdev->host_id[0] = host_id;
-
- tmport = base_io + 0x22;
- atpdev->scam_on = inb(tmport);
- tmport += 0x13;
- atpdev->global_map[0] = inb(tmport);
- tmport += 0x07;
- atpdev->ultra_map[0] = inw(tmport);
-
- n = 0x3f09;
-next_fblk_880:
- if (n >= 0x4000)
- goto flash_ok_880;
- m = 0;
- outw(n, base_io + 0x34);
- n += 0x0002;
- if (inb(base_io + 0x30) == 0xff)
- goto flash_ok_880;
-
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
- outw(n, base_io + 0x34);
- n += 0x0002;
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
- outw(n, base_io + 0x34);
- n += 0x0002;
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
- outw(n, base_io + 0x34);
- n += 0x0002;
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
- n += 0x0018;
- goto next_fblk_880;
-flash_ok_880:
- outw(0, base_io + 0x34);
- atpdev->ultra_map[0] = 0;
- atpdev->async[0] = 0;
- for (k = 0; k < 16; k++) {
- n = 1;
- n = n << k;
- if (atpdev->sp[0][k] > 1) {
- atpdev->ultra_map[0] |= n;
- } else {
- if (atpdev->sp[0][k] == 0)
- atpdev->async[0] |= n;
- }
- }
- atpdev->async[0] = ~(atpdev->async[0]);
- outb(atpdev->global_map[0], base_io + 0x35);
-
- shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
- if (!shpnt)
- goto err_nomem;
-
- p = (struct atp_unit *)&shpnt->hostdata;
-
- atpdev->host = shpnt;
- atpdev->pdev = pdev;
- pci_set_drvdata(pdev, p);
- memcpy(p, atpdev, sizeof(*atpdev));
- if (atp870u_init_tables(shpnt) < 0) {
- printk(KERN_ERR "Unable to allocate tables for Acard controller\n");
- goto unregister;
- }
-
- if (request_irq(pdev->irq, atp870u_intr_handle, IRQF_SHARED, "atp880i", shpnt)) {
- printk(KERN_ERR "Unable to allocate IRQ%d for Acard controller.\n", pdev->irq);
- goto free_tables;
- }
-
- spin_lock_irqsave(shpnt->host_lock, flags);
- tmport = base_io + 0x38;
- k = inb(tmport) & 0x80;
- outb(k, tmport);
- tmport += 0x03;
- outb(0x20, tmport);
- mdelay(32);
- outb(0, tmport);
- mdelay(32);
- tmport = base_io + 0x5b;
- inb(tmport);
- tmport -= 0x04;
- inb(tmport);
- tmport = base_io + 0x40;
- outb((host_id | 0x08), tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0)
- mdelay(1);
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 0x41;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0x51;
- outb(0x20, tmport);
-
- tscam(shpnt);
- is880(p, base_io);
- tmport = base_io + 0x38;
- outb(0xb0, tmport);
- shpnt->max_id = 16;
- shpnt->this_id = host_id;
- shpnt->unique_id = base_io;
- shpnt->io_port = base_io;
- shpnt->n_io_port = 0x60; /* Number of bytes of I/O space used */
- shpnt->irq = pdev->irq;
- } else if (ent->device == ATP885_DEVID) {
- printk(KERN_INFO " ACARD AEC-67162 PCI Ultra3 LVD Host Adapter: IO:%x, IRQ:%d.\n"
- , base_io, pdev->irq);
-
- atpdev->pdev = pdev;
- atpdev->dev_id = ent->device;
- atpdev->baseport = base_io;
- atpdev->ioport[0] = base_io + 0x80;
- atpdev->ioport[1] = base_io + 0xc0;
- atpdev->pciport[0] = base_io + 0x40;
- atpdev->pciport[1] = base_io + 0x50;
-
- shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
- if (!shpnt)
- goto err_nomem;
-
- p = (struct atp_unit *)&shpnt->hostdata;
-
- atpdev->host = shpnt;
- atpdev->pdev = pdev;
- pci_set_drvdata(pdev, p);
- memcpy(p, atpdev, sizeof(struct atp_unit));
- if (atp870u_init_tables(shpnt) < 0)
- goto unregister;
-
-#ifdef ED_DBGP
- printk("request_irq() shpnt %p hostdata %p\n", shpnt, p);
-#endif
- if (request_irq(pdev->irq, atp870u_intr_handle, IRQF_SHARED, "atp870u", shpnt)) {
- printk(KERN_ERR "Unable to allocate IRQ for Acard controller.\n");
- goto free_tables;
- }
-
- spin_lock_irqsave(shpnt->host_lock, flags);
-
- c=inb(base_io + 0x29);
- outb((c | 0x04),base_io + 0x29);
-
- n=0x1f80;
-next_fblk_885:
- if (n >= 0x2000) {
- goto flash_ok_885;
- }
- outw(n,base_io + 0x3c);
- if (inl(base_io + 0x38) == 0xffffffff) {
- goto flash_ok_885;
- }
- for (m=0; m < 2; m++) {
- p->global_map[m]= 0;
- for (k=0; k < 4; k++) {
- outw(n++,base_io + 0x3c);
- ((unsigned long *)&setupdata[m][0])[k]=inl(base_io + 0x38);
- }
- for (k=0; k < 4; k++) {
- outw(n++,base_io + 0x3c);
- ((unsigned long *)&p->sp[m][0])[k]=inl(base_io + 0x38);
- }
- n += 8;
- }
- goto next_fblk_885;
-flash_ok_885:
-#ifdef ED_DBGP
- printk( "Flash Read OK\n");
-#endif
- c=inb(base_io + 0x29);
- outb((c & 0xfb),base_io + 0x29);
- for (c=0;c < 2;c++) {
- p->ultra_map[c]=0;
- p->async[c] = 0;
- for (k=0; k < 16; k++) {
- n=1;
- n = n << k;
- if (p->sp[c][k] > 1) {
- p->ultra_map[c] |= n;
- } else {
- if (p->sp[c][k] == 0) {
- p->async[c] |= n;
- }
- }
- }
- p->async[c] = ~(p->async[c]);
-
- if (p->global_map[c] == 0) {
- k=setupdata[c][1];
- if ((k & 0x40) != 0)
- p->global_map[c] |= 0x20;
- k &= 0x07;
- p->global_map[c] |= k;
- if ((setupdata[c][2] & 0x04) != 0)
- p->global_map[c] |= 0x08;
- p->host_id[c] = setupdata[c][0] & 0x07;
- }
- }
-
- k = inb(base_io + 0x28) & 0x8f;
- k |= 0x10;
- outb(k, base_io + 0x28);
- outb(0x80, base_io + 0x41);
- outb(0x80, base_io + 0x51);
- mdelay(100);
- outb(0, base_io + 0x41);
- outb(0, base_io + 0x51);
- mdelay(1000);
- inb(base_io + 0x9b);
- inb(base_io + 0x97);
- inb(base_io + 0xdb);
- inb(base_io + 0xd7);
- tmport = base_io + 0x80;
- k=p->host_id[0];
- if (k > 7)
- k = (k & 0x07) | 0x40;
- k |= 0x08;
- outb(k, tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0)
- cpu_relax();
-
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 0x81;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0x91;
- outb(0x20, tmport);
-
- tmport = base_io + 0xc0;
- k=p->host_id[1];
- if (k > 7)
- k = (k & 0x07) | 0x40;
- k |= 0x08;
- outb(k, tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0)
- cpu_relax();
+ err = scsi_add_host(shpnt, &pdev->dev);
+ if (err)
+ goto scsi_add_fail;
+ scsi_scan_host(shpnt);
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 0xc1;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0xd1;
- outb(0x20, tmport);
-
- tscam_885();
- printk(KERN_INFO " Scanning Channel A SCSI Device ...\n");
- is885(p, base_io + 0x80, 0);
- printk(KERN_INFO " Scanning Channel B SCSI Device ...\n");
- is885(p, base_io + 0xc0, 1);
-
- k = inb(base_io + 0x28) & 0xcf;
- k |= 0xc0;
- outb(k, base_io + 0x28);
- k = inb(base_io + 0x1f) | 0x80;
- outb(k, base_io + 0x1f);
- k = inb(base_io + 0x29) | 0x01;
- outb(k, base_io + 0x29);
-#ifdef ED_DBGP
- //printk("atp885: atp_host[0] 0x%p\n", atp_host[0]);
-#endif
- shpnt->max_id = 16;
- shpnt->max_lun = (p->global_map[0] & 0x07) + 1;
- shpnt->max_channel = 1;
- shpnt->this_id = p->host_id[0];
- shpnt->unique_id = base_io;
- shpnt->io_port = base_io;
- shpnt->n_io_port = 0xff; /* Number of bytes of I/O space used */
- shpnt->irq = pdev->irq;
-
- } else {
- error = pci_read_config_byte(pdev, 0x49, &host_id);
-
- printk(KERN_INFO " ACARD AEC-671X PCI Ultra/W SCSI-2/3 Host Adapter: %d "
- "IO:%x, IRQ:%d.\n", count, base_io, pdev->irq);
-
- atpdev->ioport[0] = base_io;
- atpdev->pciport[0] = base_io + 0x20;
- atpdev->dev_id = ent->device;
- host_id &= 0x07;
- atpdev->host_id[0] = host_id;
- tmport = base_io + 0x22;
- atpdev->scam_on = inb(tmport);
- tmport += 0x0b;
- atpdev->global_map[0] = inb(tmport++);
- atpdev->ultra_map[0] = inw(tmport);
-
- if (atpdev->ultra_map[0] == 0) {
- atpdev->scam_on = 0x00;
- atpdev->global_map[0] = 0x20;
- atpdev->ultra_map[0] = 0xffff;
- }
-
- shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
- if (!shpnt)
- goto err_nomem;
-
- p = (struct atp_unit *)&shpnt->hostdata;
-
- atpdev->host = shpnt;
- atpdev->pdev = pdev;
- pci_set_drvdata(pdev, p);
- memcpy(p, atpdev, sizeof(*atpdev));
- if (atp870u_init_tables(shpnt) < 0)
- goto unregister;
-
- if (request_irq(pdev->irq, atp870u_intr_handle, IRQF_SHARED, "atp870i", shpnt)) {
- printk(KERN_ERR "Unable to allocate IRQ%d for Acard controller.\n", pdev->irq);
- goto free_tables;
- }
-
- spin_lock_irqsave(shpnt->host_lock, flags);
- if (atpdev->chip_ver > 0x07) { /* check if atp876 chip then enable terminator */
- tmport = base_io + 0x3e;
- outb(0x00, tmport);
- }
-
- tmport = base_io + 0x3a;
- k = (inb(tmport) & 0xf3) | 0x10;
- outb(k, tmport);
- outb((k & 0xdf), tmport);
- mdelay(32);
- outb(k, tmport);
- mdelay(32);
- tmport = base_io;
- outb((host_id | 0x08), tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0)
- mdelay(1);
-
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 1;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0x11;
- outb(0x20, tmport);
-
- tscam(shpnt);
- is870(p, base_io);
- tmport = base_io + 0x3a;
- outb((inb(tmport) & 0xef), tmport);
- tmport++;
- outb((inb(tmport) | 0x20), tmport);
- if (atpdev->chip_ver == 4)
- shpnt->max_id = 16;
- else
- shpnt->max_id = 8;
- shpnt->this_id = host_id;
- shpnt->unique_id = base_io;
- shpnt->io_port = base_io;
- shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */
- shpnt->irq = pdev->irq;
- }
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- if(ent->device==ATP885_DEVID) {
- if(!request_region(base_io, 0xff, "atp870u")) /* Register the IO ports that we use */
- goto request_io_fail;
- } else if((ent->device==ATP880_DEVID1)||(ent->device==ATP880_DEVID2)) {
- if(!request_region(base_io, 0x60, "atp870u")) /* Register the IO ports that we use */
- goto request_io_fail;
- } else {
- if(!request_region(base_io, 0x40, "atp870u")) /* Register the IO ports that we use */
- goto request_io_fail;
- }
- count++;
- if (scsi_add_host(shpnt, &pdev->dev))
- goto scsi_add_fail;
- scsi_scan_host(shpnt);
-#ifdef ED_DBGP
- printk("atp870u_prob : exit\n");
-#endif
- return 0;
+ return 0;
scsi_add_fail:
- printk("atp870u_prob:scsi_add_fail\n");
- if(ent->device==ATP885_DEVID) {
- release_region(base_io, 0xff);
- } else if((ent->device==ATP880_DEVID1)||(ent->device==ATP880_DEVID2)) {
- release_region(base_io, 0x60);
- } else {
- release_region(base_io, 0x40);
- }
-request_io_fail:
- printk("atp870u_prob:request_io_fail\n");
- free_irq(pdev->irq, shpnt);
+ free_irq(shpnt->irq, shpnt);
free_tables:
- printk("atp870u_prob:free_table\n");
atp870u_free_tables(shpnt);
unregister:
- printk("atp870u_prob:unregister\n");
scsi_host_put(shpnt);
- return -1;
-err_eio:
- kfree(atpdev);
- return -EIO;
-err_nomem:
- kfree(atpdev);
- return -ENOMEM;
+release_region:
+ pci_release_regions(pdev);
+disable_device:
+ pci_disable_device(pdev);
+fail:
+ return err;
}
/* The abort command does not leave the device in a clean state where
@@ -3047,8 +1644,7 @@ static int atp870u_abort(struct scsi_cmnd * SCpnt)
{
unsigned char j, k, c;
struct scsi_cmnd *workrequ;
- unsigned int tmport;
- struct atp_unit *dev;
+ struct atp_unit *dev;
struct Scsi_Host *host;
host = SCpnt->device->host;
@@ -3057,18 +1653,13 @@ static int atp870u_abort(struct scsi_cmnd * SCpnt)
printk(" atp870u: abort Channel = %x \n", c);
printk("working=%x last_cmd=%x ", dev->working[c], dev->last_cmd[c]);
printk(" quhdu=%x quendu=%x ", dev->quhd[c], dev->quend[c]);
- tmport = dev->ioport[c];
for (j = 0; j < 0x18; j++) {
- printk(" r%2x=%2x", j, inb(tmport++));
+ printk(" r%2x=%2x", j, atp_readb_io(dev, c, j));
}
- tmport += 0x04;
- printk(" r1c=%2x", inb(tmport));
- tmport += 0x03;
- printk(" r1f=%2x in_snd=%2x ", inb(tmport), dev->in_snd[c]);
- tmport= dev->pciport[c];
- printk(" d00=%2x", inb(tmport));
- tmport += 0x02;
- printk(" d02=%2x", inb(tmport));
+ printk(" r1c=%2x", atp_readb_io(dev, c, 0x1c));
+ printk(" r1f=%2x in_snd=%2x ", atp_readb_io(dev, c, 0x1f), dev->in_snd[c]);
+ printk(" d00=%2x", atp_readb_pci(dev, c, 0x00));
+ printk(" d02=%2x", atp_readb_pci(dev, c, 0x02));
for(j=0;j<16;j++) {
if (dev->id[c][j].curr_req != NULL) {
workrequ = dev->id[c][j].curr_req;
@@ -3091,42 +1682,17 @@ static const char *atp870u_info(struct Scsi_Host *notused)
return buffer;
}
-#define BLS buffer + len + size
-static int atp870u_proc_info(struct Scsi_Host *HBAptr, char *buffer,
- char **start, off_t offset, int length, int inout)
+static int atp870u_show_info(struct seq_file *m, struct Scsi_Host *HBAptr)
{
- static u8 buff[512];
- int size = 0;
- int len = 0;
- off_t begin = 0;
- off_t pos = 0;
-
- if (inout)
- return -EINVAL;
- if (offset == 0)
- memset(buff, 0, sizeof(buff));
- size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.6+ac\n");
- len += size;
- pos = begin + len;
- size = 0;
-
- size += sprintf(BLS, "\n");
- size += sprintf(BLS, "Adapter Configuration:\n");
- size += sprintf(BLS, " Base IO: %#.4lx\n", HBAptr->io_port);
- size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
- len += size;
- pos = begin + len;
-
- *start = buffer + (offset - begin); /* Start of wanted data */
- len -= (offset - begin); /* Start slop */
- if (len > length) {
- len = length; /* Ending slop */
- }
- return (len);
+ seq_puts(m, "ACARD AEC-671X Driver Version: 2.6+ac\n\n"
+ "Adapter Configuration:\n");
+ seq_printf(m, " Base IO: %#.4lx\n", HBAptr->io_port);
+ seq_printf(m, " IRQ: %d\n", HBAptr->irq);
+ return 0;
}
-static int atp870u_biosparam(struct scsi_device *disk, struct block_device *dev,
+static int atp870u_biosparam(struct scsi_device *disk, struct gendisk *unused,
sector_t capacity, int *ip)
{
int heads, sectors, cylinders;
@@ -3147,45 +1713,40 @@ static int atp870u_biosparam(struct scsi_device *disk, struct block_device *dev,
}
static void atp870u_remove (struct pci_dev *pdev)
-{
+{
struct atp_unit *devext = pci_get_drvdata(pdev);
struct Scsi_Host *pshost = devext->host;
-
-
+
scsi_remove_host(pshost);
- printk(KERN_INFO "free_irq : %d\n",pshost->irq);
free_irq(pshost->irq, pshost);
- release_region(pshost->io_port, pshost->n_io_port);
- printk(KERN_INFO "atp870u_free_tables : %p\n",pshost);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
atp870u_free_tables(pshost);
- printk(KERN_INFO "scsi_host_put : %p\n",pshost);
scsi_host_put(pshost);
- printk(KERN_INFO "pci_set_drvdata : %p\n",pdev);
- pci_set_drvdata(pdev, NULL);
}
+
+MODULE_DESCRIPTION("ACARD SCSI host adapter driver");
MODULE_LICENSE("GPL");
-static struct scsi_host_template atp870u_template = {
+static const struct scsi_host_template atp870u_template = {
.module = THIS_MODULE,
- .name = "atp870u" /* name */,
+ .name = "atp870u" /* name */,
.proc_name = "atp870u",
- .proc_info = atp870u_proc_info,
- .info = atp870u_info /* info */,
- .queuecommand = atp870u_queuecommand /* queuecommand */,
- .eh_abort_handler = atp870u_abort /* abort */,
- .bios_param = atp870u_biosparam /* biosparm */,
- .can_queue = qcnt /* can_queue */,
- .this_id = 7 /* SCSI ID */,
- .sg_tablesize = ATP870U_SCATTER /*SG_ALL*/ /*SG_NONE*/,
- .cmd_per_lun = ATP870U_CMDLUN /* commands per lun */,
- .use_clustering = ENABLE_CLUSTERING,
+ .show_info = atp870u_show_info,
+ .info = atp870u_info /* info */,
+ .queuecommand = atp870u_queuecommand /* queuecommand */,
+ .eh_abort_handler = atp870u_abort /* abort */,
+ .bios_param = atp870u_biosparam /* biosparm */,
+ .can_queue = qcnt /* can_queue */,
+ .this_id = 7 /* SCSI ID */,
+ .sg_tablesize = ATP870U_SCATTER /*SG_ALL*/,
.max_sectors = ATP870U_MAX_SECTORS,
};
-static struct pci_device_id atp870u_id_table[] = {
+static const struct pci_device_id atp870u_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP885_DEVID) },
- { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP880_DEVID1) },
- { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP880_DEVID2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP880_DEVID1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP880_DEVID2) },
{ PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7610) },
{ PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7612UW) },
{ PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7612U) },
@@ -3202,55 +1763,30 @@ static struct pci_driver atp870u_driver = {
.id_table = atp870u_id_table,
.name = "atp870u",
.probe = atp870u_probe,
- .remove = __devexit_p(atp870u_remove),
+ .remove = atp870u_remove,
};
-static int __init atp870u_init(void)
-{
-#ifdef ED_DBGP
- printk("atp870u_init: Entry\n");
-#endif
- return pci_register_driver(&atp870u_driver);
-}
-
-static void __exit atp870u_exit(void)
-{
-#ifdef ED_DBGP
- printk("atp870u_exit: Entry\n");
-#endif
- pci_unregister_driver(&atp870u_driver);
-}
-
-static void tscam_885(void)
-{
- unsigned char i;
-
- for (i = 0; i < 0x2; i++) {
- mdelay(300);
- }
- return;
-}
-
+module_pci_driver(atp870u_driver);
-
-static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c)
+static void atp_is(struct atp_unit *dev, unsigned char c, bool wide_chip,
+ unsigned char lvdmode)
{
- unsigned int tmport;
- unsigned char i, j, k, rmb, n, lvdmode;
+ unsigned char i, j, k, rmb, n;
unsigned short int m;
static unsigned char mbuf[512];
- static unsigned char satn[9] = {0, 0, 0, 0, 0, 0, 0, 6, 6};
- static unsigned char inqd[9] = {0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6};
- static unsigned char synn[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
- unsigned char synu[6] = {0x80, 1, 3, 1, 0x0a, 0x0e};
- static unsigned char synw[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
- unsigned char synuw[6] = {0x80, 1, 3, 1, 0x0a, 0x0e};
- static unsigned char wide[6] = {0x80, 1, 2, 3, 1, 0};
- static unsigned char u3[9] = { 0x80,1,6,4,0x09,00,0x0e,0x01,0x02 };
-
- lvdmode=inb(wkport + 0x1b) >> 7;
+ static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
+ static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
+ static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
+ unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
+ static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
+ static unsigned char synw_870[6] = { 0x80, 1, 3, 1, 0x0c, 0x07 };
+ unsigned char synuw[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
+ static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
+ static unsigned char u3[9] = { 0x80, 1, 6, 4, 0x09, 00, 0x0e, 0x01, 0x02 };
for (i = 0; i < 16; i++) {
+ if (!wide_chip && (i > 7))
+ break;
m = 1;
m = m << i;
if ((m & dev->active_id[c]) != 0) {
@@ -3260,192 +1796,172 @@ static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c)
printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[c]);
continue;
}
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 0x01;
- outb(0x08, tmport++);
- outb(0x7f, tmport++);
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
-
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
+ atp_writeb_io(dev, c, 0x1b, wide_chip ? 0x01 : 0x00);
+ atp_writeb_io(dev, c, 1, 0x08);
+ atp_writeb_io(dev, c, 2, 0x7f);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
j = i;
if ((j & 0x08) != 0) {
j = (j & 0x07) | 0x40;
}
- outb(j, tmport);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
+ atp_writeb_io(dev, c, 0x15, j);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
- while ((inb(tmport) & 0x80) == 0x00)
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
dev->active_id[c] |= m;
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ if (is885(dev) || is880(dev))
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ else /* result of is870() merge - is this a bug? */
+ atp_writeb_io(dev, c, 0x04, 0x00);
phase_cmd:
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
- tmport = wkport + 0x10;
- outb(0x41, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x41);
goto phase_cmd;
}
sel_ok:
- tmport = wkport + 0x03;
- outb(inqd[0], tmport++);
- outb(inqd[1], tmport++);
- outb(inqd[2], tmport++);
- outb(inqd[3], tmport++);
- outb(inqd[4], tmport++);
- outb(inqd[5], tmport);
- tmport += 0x07;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(inqd[6], tmport++);
- outb(inqd[7], tmport++);
- tmport += 0x03;
- outb(inqd[8], tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 3, inqd[0]);
+ atp_writeb_io(dev, c, 4, inqd[1]);
+ atp_writeb_io(dev, c, 5, inqd[2]);
+ atp_writeb_io(dev, c, 6, inqd[3]);
+ atp_writeb_io(dev, c, 7, inqd[4]);
+ atp_writeb_io(dev, c, 8, inqd[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, inqd[6]);
+ atp_writeb_io(dev, c, 0x14, inqd[7]);
+ atp_writeb_io(dev, c, 0x18, inqd[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
- tmport = wkport + 0x1b;
- outb(0x00, tmport);
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
+
+ if (wide_chip)
+ atp_writeb_io(dev, c, 0x1b, 0x00);
+
+ atp_writeb_io(dev, c, 0x18, 0x08);
j = 0;
rd_inq_data:
- k = inb(tmport);
+ k = atp_readb_io(dev, c, 0x1f);
if ((k & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[j++] = inb(tmport);
- tmport += 0x06;
+ mbuf[j++] = atp_readb_io(dev, c, 0x19);
goto rd_inq_data;
}
if ((k & 0x80) == 0) {
goto rd_inq_data;
}
- tmport -= 0x08;
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x17);
if (j == 0x16) {
goto inq_ok;
}
- tmport = wkport + 0x10;
- outb(0x46, tmport);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x10, 0x46);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, 0);
+ atp_writeb_io(dev, c, 0x14, 0);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if (inb(tmport) != 0x16) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x16)
goto sel_ok;
- }
+
inq_ok:
mbuf[36] = 0;
- printk( KERN_INFO" ID: %2d %s\n", i, &mbuf[8]);
+ printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
dev->id[c][i].devtype = mbuf[0];
rmb = mbuf[1];
n = mbuf[7];
+ if (!wide_chip)
+ goto not_wide;
if ((mbuf[7] & 0x60) == 0) {
goto not_wide;
}
- if ((i < 8) && ((dev->global_map[c] & 0x20) == 0)) {
- goto not_wide;
+ if (is885(dev) || is880(dev)) {
+ if ((i < 8) && ((dev->global_map[c] & 0x20) == 0))
+ goto not_wide;
+ } else { /* result of is870() merge - is this a bug? */
+ if ((dev->global_map[c] & 0x20) == 0)
+ goto not_wide;
}
if (lvdmode == 0) {
- goto chg_wide;
- }
- if (dev->sp[c][i] != 0x04) { // force u2
- goto chg_wide;
- }
-
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 0x03;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
+ goto chg_wide;
+ }
+ if (dev->sp[c][i] != 0x04) // force u2
+ {
+ goto chg_wide;
+ }
+
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
try_u3:
j = 0;
- tmport = wkport + 0x14;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(u3[j++], tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x14, 0x09);
+ atp_writeb_io(dev, c, 0x18, 0x20);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, u3[j++]);
cpu_relax();
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00)
cpu_relax();
- j = inb(tmport) & 0x0f;
+
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto u3p_in;
}
@@ -3457,19 +1973,13 @@ try_u3:
}
continue;
u3p_out:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x18, 0x20);
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, 0);
cpu_relax();
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto u3p_in;
}
@@ -3481,25 +1991,19 @@ u3p_out:
}
continue;
u3p_in:
- tmport = wkport + 0x14;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
+ atp_writeb_io(dev, c, 0x14, 0x09);
+ atp_writeb_io(dev, c, 0x18, 0x20);
k = 0;
u3p_in1:
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
+ mbuf[k++] = atp_readb_io(dev, c, 0x19);
goto u3p_in1;
}
if ((j & 0x80) == 0x00) {
goto u3p_in1;
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto u3p_in;
}
@@ -3511,16 +2015,13 @@ u3p_in1:
}
continue;
u3p_cmd:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00);
- tmport -= 0x08;
- j = inb(tmport);
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
if (j == 0x4e) {
goto u3p_out;
@@ -3541,60 +2042,52 @@ u3p_cmd:
m = m << i;
dev->wide_id[c] |= m;
dev->id[c][i].devsp = 0xce;
-#ifdef ED_DBGP
- printk("dev->id[%2d][%2d].devsp = %2x\n",c,i,dev->id[c][i].devsp);
+#ifdef ED_DBGP
+ printk("dev->id[%2d][%2d].devsp = %2x\n",
+ c, i, dev->id[c][i].devsp);
#endif
continue;
}
chg_wide:
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 0x03;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 &&
+ atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
try_wide:
j = 0;
- tmport = wkport + 0x14;
- outb(0x05, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(wide[j++], tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x14, 0x05);
+ atp_writeb_io(dev, c, 0x18, 0x20);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, wide[j++]);
cpu_relax();
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00)
cpu_relax();
- j = inb(tmport) & 0x0f;
+
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto widep_in;
}
@@ -3606,19 +2099,13 @@ try_wide:
}
continue;
widep_out:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x18, 0x20);
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, 0);
cpu_relax();
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto widep_in;
}
@@ -3630,25 +2117,19 @@ widep_out:
}
continue;
widep_in:
- tmport = wkport + 0x14;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
+ atp_writeb_io(dev, c, 0x14, 0xff);
+ atp_writeb_io(dev, c, 0x18, 0x20);
k = 0;
widep_in1:
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
+ mbuf[k++] = atp_readb_io(dev, c, 0x19);
goto widep_in1;
}
if ((j & 0x80) == 0x00) {
goto widep_in1;
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto widep_in;
}
@@ -3660,17 +2141,14 @@ widep_in1:
}
continue;
widep_cmd:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
if (j == 0x4e) {
goto widep_out;
@@ -3693,88 +2171,84 @@ widep_cmd:
m = m << i;
dev->wide_id[c] |= m;
not_wide:
- if ((dev->id[c][i].devtype == 0x00) || (dev->id[c][i].devtype == 0x07) ||
+ if ((dev->id[c][i].devtype == 0x00) ||
+ (dev->id[c][i].devtype == 0x07) ||
((dev->id[c][i].devtype == 0x05) && ((n & 0x10) != 0))) {
m = 1;
m = m << i;
if ((dev->async[c] & m) != 0) {
- goto set_sync;
+ goto set_sync;
}
}
continue;
set_sync:
- if (dev->sp[c][i] == 0x02) {
- synu[4]=0x0c;
- synuw[4]=0x0c;
+ if ((!is885(dev) && !is880(dev)) || (dev->sp[c][i] == 0x02)) {
+ synu[4] = 0x0c;
+ synuw[4] = 0x0c;
} else {
- if (dev->sp[c][i] >= 0x03) {
- synu[4]=0x0a;
- synuw[4]=0x0a;
- }
+ if (dev->sp[c][i] >= 0x03) {
+ synu[4] = 0x0a;
+ synuw[4] = 0x0a;
+ }
}
- tmport = wkport + 0x1b;
j = 0;
if ((m & dev->wide_id[c]) != 0) {
j |= 0x01;
}
- outb(j, tmport);
- tmport = wkport + 0x03;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x1b, j);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 &&
+ atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
try_sync:
j = 0;
- tmport = wkport + 0x14;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
+ atp_writeb_io(dev, c, 0x14, 0x06);
+ atp_writeb_io(dev, c, 0x18, 0x20);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0) {
if ((m & dev->wide_id[c]) != 0) {
- if ((m & dev->ultra_map[c]) != 0) {
- outb(synuw[j++], tmport);
- } else {
- outb(synw[j++], tmport);
- }
+ if (is885(dev) || is880(dev)) {
+ if ((m & dev->ultra_map[c]) != 0) {
+ atp_writeb_io(dev, c, 0x19, synuw[j++]);
+ } else {
+ atp_writeb_io(dev, c, 0x19, synw[j++]);
+ }
+ } else
+ atp_writeb_io(dev, c, 0x19, synw_870[j++]);
} else {
if ((m & dev->ultra_map[c]) != 0) {
- outb(synu[j++], tmport);
+ atp_writeb_io(dev, c, 0x19, synu[j++]);
} else {
- outb(synn[j++], tmport);
+ atp_writeb_io(dev, c, 0x19, synn[j++]);
}
}
- tmport += 0x06;
}
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00)
cpu_relax();
- j = inb(tmport) & 0x0f;
+
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto phase_ins;
}
@@ -3786,19 +2260,13 @@ try_sync:
}
continue;
phase_outs:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00) {
- if ((inb(tmport) & 0x01) != 0x00) {
- tmport -= 0x06;
- outb(0x00, tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x18, 0x20);
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0x00)
+ atp_writeb_io(dev, c, 0x19, 0x00);
cpu_relax();
}
- tmport -= 0x08;
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x17);
if (j == 0x85) {
goto tar_dcons;
}
@@ -3814,26 +2282,25 @@ phase_outs:
}
continue;
phase_ins:
- tmport = wkport + 0x14;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
+ if (is885(dev) || is880(dev))
+ atp_writeb_io(dev, c, 0x14, 0x06);
+ else
+ atp_writeb_io(dev, c, 0x14, 0xff);
+ atp_writeb_io(dev, c, 0x18, 0x20);
k = 0;
phase_ins1:
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x01) != 0x00) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
+ mbuf[k++] = atp_readb_io(dev, c, 0x19);
goto phase_ins1;
}
if ((j & 0x80) == 0x00) {
goto phase_ins1;
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00);
- j = inb(tmport);
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j == 0x85) {
goto tar_dcons;
}
@@ -3849,18 +2316,15 @@ phase_ins1:
}
continue;
phase_cmds:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x30);
tar_dcons:
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
continue;
}
@@ -3876,14 +2340,21 @@ tar_dcons:
if (mbuf[3] > 0x64) {
continue;
}
- if (mbuf[4] > 0x0e) {
- mbuf[4] = 0x0e;
+ if (is885(dev) || is880(dev)) {
+ if (mbuf[4] > 0x0e) {
+ mbuf[4] = 0x0e;
+ }
+ } else {
+ if (mbuf[4] > 0x0c) {
+ mbuf[4] = 0x0c;
+ }
}
dev->id[c][i].devsp = mbuf[4];
- if (mbuf[3] < 0x0c){
- j = 0xb0;
- goto set_syn_ok;
- }
+ if (is885(dev) || is880(dev))
+ if (mbuf[3] < 0x0c) {
+ j = 0xb0;
+ goto set_syn_ok;
+ }
if ((mbuf[3] < 0x0d) && (rmb == 0)) {
j = 0xa0;
goto set_syn_ok;
@@ -3901,16 +2372,11 @@ tar_dcons:
goto set_syn_ok;
}
j = 0x60;
- set_syn_ok:
+set_syn_ok:
dev->id[c][i].devsp = (dev->id[c][i].devsp & 0x0f) | j;
-#ifdef ED_DBGP
- printk("dev->id[%2d][%2d].devsp = %2x\n",c,i,dev->id[c][i].devsp);
+#ifdef ED_DBGP
+ printk("dev->id[%2d][%2d].devsp = %2x\n",
+ c,i,dev->id[c][i].devsp);
#endif
}
- tmport = wkport + 0x16;
- outb(0x80, tmport);
}
-
-module_init(atp870u_init);
-module_exit(atp870u_exit);
-
diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h
index 62bae64a01c1..31f6ab24b5cb 100644
--- a/drivers/scsi/atp870u.h
+++ b/drivers/scsi/atp870u.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ATP870U_H
#define _ATP870U_H
@@ -6,11 +7,10 @@
/* I/O Port */
-#define MAX_CDB 12
-#define MAX_SENSE 14
-#define qcnt 32
-#define ATP870U_SCATTER 128
-#define ATP870U_CMDLUN 1
+#define MAX_CDB 12
+#define MAX_SENSE 14
+#define qcnt 32
+#define ATP870U_SCATTER 128
#define MAX_ADAPTER 8
#define MAX_SCSI_ID 16
@@ -27,24 +27,20 @@ struct atp_unit
unsigned long baseport;
unsigned long ioport[2];
unsigned long pciport[2];
- unsigned long irq;
unsigned char last_cmd[2];
unsigned char in_snd[2];
unsigned char in_int[2];
unsigned char quhd[2];
unsigned char quend[2];
unsigned char global_map[2];
- unsigned char chip_ver;
- unsigned char scam_on;
unsigned char host_id[2];
unsigned int working[2];
unsigned short wide_id[2];
unsigned short active_id[2];
unsigned short ultra_map[2];
unsigned short async[2];
- unsigned short dev_id;
unsigned char sp[2][16];
- unsigned char r1f[2][16];
+ unsigned char r1f[2][16];
struct scsi_cmnd *quereq[2][qcnt];
struct atp_id
{
@@ -59,8 +55,8 @@ struct atp_unit
dma_addr_t prdaddr; /* Dynamically updated in driver */
struct scsi_cmnd *curr_req;
} id[2][16];
- struct Scsi_Host *host;
- struct pci_dev *pdev;
+ struct Scsi_Host *host;
+ struct pci_dev *pdev;
unsigned int unit;
};
diff --git a/drivers/scsi/be2iscsi/Kconfig b/drivers/scsi/be2iscsi/Kconfig
index ceaca32e788d..958c9b46ec78 100644
--- a/drivers/scsi/be2iscsi/Kconfig
+++ b/drivers/scsi/be2iscsi/Kconfig
@@ -1,9 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
config BE2ISCSI
- tristate "ServerEngines' 10Gbps iSCSI - BladeEngine 2"
+ tristate "Emulex 10Gbps iSCSI - BladeEngine 2"
depends on PCI && SCSI && NET
select SCSI_ISCSI_ATTRS
select ISCSI_BOOT_SYSFS
+ select IRQ_POLL
help
- This driver implements the iSCSI functionality for ServerEngines'
+ This driver implements the iSCSI functionality for Emulex
10Gbps Storage adapter - BladeEngine 2.
diff --git a/drivers/scsi/be2iscsi/Makefile b/drivers/scsi/be2iscsi/Makefile
index c11f443e3f83..910885343a75 100644
--- a/drivers/scsi/be2iscsi/Makefile
+++ b/drivers/scsi/be2iscsi/Makefile
@@ -1,5 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
-# Makefile to build the iSCSI driver for ServerEngine's BladeEngine.
+# Makefile to build the iSCSI driver for Emulex OneConnect.
#
#
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index 1d7b976c850f..4c58a02590c7 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -1,18 +1,10 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2017 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
*
* Contact Information:
- * linux-drivers@emulex.com
- *
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
+ * linux-drivers@broadcom.com
*/
#ifndef BEISCSI_H
@@ -20,7 +12,7 @@
#include <linux/pci.h>
#include <linux/if_vlan.h>
-#include <linux/blk-iopoll.h>
+#include <linux/irq_poll.h>
#define FW_VER_LEN 32
#define MCC_Q_LEN 128
#define MCC_CQ_LEN 256
@@ -28,7 +20,7 @@
/* BladeEngine Generation numbers */
#define BE_GEN2 2
#define BE_GEN3 3
-
+#define BE_GEN4 4
struct be_dma_mem {
void *va;
dma_addr_t dma;
@@ -42,7 +34,7 @@ struct be_queue_info {
u16 id;
u16 tail, head;
bool created;
- atomic_t used; /* Number of valid elements in the queue */
+ u16 used; /* Number of valid elements in the queue */
};
static inline u32 MODULO(u16 val, u16 limit)
@@ -83,11 +75,22 @@ static inline void queue_tail_inc(struct be_queue_info *q)
/*ISCSI */
+struct be_aic_obj { /* Adaptive interrupt coalescing (AIC) info */
+ unsigned long jiffies;
+ u32 eq_prev; /* Used to calculate eqe */
+ u32 prev_eqd;
+#define BEISCSI_EQ_DELAY_MIN 0
+#define BEISCSI_EQ_DELAY_DEF 32
+#define BEISCSI_EQ_DELAY_MAX 128
+};
+
struct be_eq_obj {
+ u32 cq_count;
struct be_queue_info q;
struct beiscsi_hba *phba;
struct be_queue_info *cq;
- struct blk_iopoll iopoll;
+ struct work_struct mcc_work; /* Work Item */
+ struct irq_poll iopoll;
};
struct be_mcc_obj {
@@ -95,6 +98,16 @@ struct be_mcc_obj {
struct be_queue_info cq;
};
+struct beiscsi_mcc_tag_state {
+ unsigned long tag_state;
+#define MCC_TAG_STATE_RUNNING 0
+#define MCC_TAG_STATE_TIMEOUT 1
+#define MCC_TAG_STATE_ASYNC 2
+#define MCC_TAG_STATE_IGNORE 3
+ void (*cbfn)(struct beiscsi_hba *, unsigned int);
+ struct be_dma_mem tag_mem_state;
+};
+
struct be_ctrl_info {
u8 __iomem *csr;
u8 __iomem *db; /* Door Bell */
@@ -102,7 +115,7 @@ struct be_ctrl_info {
struct pci_dev *pdev;
/* Mbox used for cmd request/response */
- spinlock_t mbox_lock; /* For serializing mbox cmds to BE card */
+ struct mutex mbox_lock; /* For serializing mbox cmds to BE card */
struct be_dma_mem mbox_mem;
/* Mbox mem is adjusted to align to 16 bytes. The allocated addr
* is stored for freeing purpose */
@@ -111,31 +124,33 @@ struct be_ctrl_info {
/* MCC Rings */
struct be_mcc_obj mcc_obj;
spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */
- spinlock_t mcc_cq_lock;
wait_queue_head_t mcc_wait[MAX_MCC_CMD + 1];
unsigned int mcc_tag[MAX_MCC_CMD];
- unsigned int mcc_numtag[MAX_MCC_CMD + 1];
+ unsigned int mcc_tag_status[MAX_MCC_CMD + 1];
unsigned short mcc_alloc_index;
unsigned short mcc_free_index;
unsigned int mcc_tag_available;
+
+ struct beiscsi_mcc_tag_state ptag_state[MAX_MCC_CMD + 1];
};
#include "be_cmds.h"
-#define PAGE_SHIFT_4K 12
-#define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K)
-#define mcc_timeout 120000 /* 5s timeout */
+/* WRB index mask for MCC_Q_LEN queue entries */
+#define MCC_Q_WRB_IDX_MASK CQE_STATUS_WRB_MASK
+#define MCC_Q_WRB_IDX_SHIFT CQE_STATUS_WRB_SHIFT
+/* TAG is from 1...MAX_MCC_CMD, MASK includes MAX_MCC_CMD */
+#define MCC_Q_CMD_TAG_MASK ((MAX_MCC_CMD << 1) - 1)
+
+#define PAGE_SHIFT_4K 12
+#define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K)
/* Returns number of pages spanned by the data starting at the given addr */
#define PAGES_4K_SPANNED(_address, size) \
((u32)((((size_t)(_address) & (PAGE_SIZE_4K - 1)) + \
(size) + (PAGE_SIZE_4K - 1)) >> PAGE_SHIFT_4K))
-/* Byte offset into the page corresponding to given address */
-#define OFFSET_IN_PAGE(addr) \
- ((size_t)(addr) & (PAGE_SIZE_4K-1))
-
/* Returns bit offset within a DWORD of a bitfield */
#define AMAP_BIT_OFFSET(_struct, field) \
(((size_t)&(((_struct *)0)->field))%32)
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index b8a82f2c62c8..0b59b63bce79 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -1,464 +1,694 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2017 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
*
* Contact Information:
- * linux-drivers@emulex.com
- *
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
+ * linux-drivers@broadcom.com
*/
+#include <scsi/iscsi_proto.h>
+
+#include "be_main.h"
#include "be.h"
#include "be_mgmt.h"
-#include "be_main.h"
-
-int beiscsi_pci_soft_reset(struct beiscsi_hba *phba)
-{
- u32 sreset;
- u8 *pci_reset_offset = 0;
- u8 *pci_online0_offset = 0;
- u8 *pci_online1_offset = 0;
- u32 pconline0 = 0;
- u32 pconline1 = 0;
- u32 i;
-
- pci_reset_offset = (u8 *)phba->pci_va + BE2_SOFT_RESET;
- pci_online0_offset = (u8 *)phba->pci_va + BE2_PCI_ONLINE0;
- pci_online1_offset = (u8 *)phba->pci_va + BE2_PCI_ONLINE1;
- sreset = readl((void *)pci_reset_offset);
- sreset |= BE2_SET_RESET;
- writel(sreset, (void *)pci_reset_offset);
-
- i = 0;
- while (sreset & BE2_SET_RESET) {
- if (i > 64)
- break;
- msleep(100);
- sreset = readl((void *)pci_reset_offset);
- i++;
- }
-
- if (sreset & BE2_SET_RESET) {
- printk(KERN_ERR "Soft Reset did not deassert\n");
- return -EIO;
- }
- pconline1 = BE2_MPU_IRAM_ONLINE;
- writel(pconline0, (void *)pci_online0_offset);
- writel(pconline1, (void *)pci_online1_offset);
-
- sreset = BE2_SET_RESET;
- writel(sreset, (void *)pci_reset_offset);
- i = 0;
- while (sreset & BE2_SET_RESET) {
- if (i > 64)
- break;
- msleep(1);
- sreset = readl((void *)pci_reset_offset);
- i++;
- }
- if (sreset & BE2_SET_RESET) {
- printk(KERN_ERR "MPU Online Soft Reset did not deassert\n");
- return -EIO;
- }
- return 0;
-}
-
-int be_chk_reset_complete(struct beiscsi_hba *phba)
+/* UE Status Low CSR */
+static const char * const desc_ue_status_low[] = {
+ "CEV",
+ "CTX",
+ "DBUF",
+ "ERX",
+ "Host",
+ "MPU",
+ "NDMA",
+ "PTC ",
+ "RDMA ",
+ "RXF ",
+ "RXIPS ",
+ "RXULP0 ",
+ "RXULP1 ",
+ "RXULP2 ",
+ "TIM ",
+ "TPOST ",
+ "TPRE ",
+ "TXIPS ",
+ "TXULP0 ",
+ "TXULP1 ",
+ "UC ",
+ "WDMA ",
+ "TXULP2 ",
+ "HOST1 ",
+ "P0_OB_LINK ",
+ "P1_OB_LINK ",
+ "HOST_GPIO ",
+ "MBOX ",
+ "AXGMAC0",
+ "AXGMAC1",
+ "JTAG",
+ "MPU_INTPEND"
+};
+
+/* UE Status High CSR */
+static const char * const desc_ue_status_hi[] = {
+ "LPCMEMHOST",
+ "MGMT_MAC",
+ "PCS0ONLINE",
+ "MPU_IRAM",
+ "PCS1ONLINE",
+ "PCTL0",
+ "PCTL1",
+ "PMEM",
+ "RR",
+ "TXPB",
+ "RXPP",
+ "XAUI",
+ "TXP",
+ "ARM",
+ "IPC",
+ "HOST2",
+ "HOST3",
+ "HOST4",
+ "HOST5",
+ "HOST6",
+ "HOST7",
+ "HOST8",
+ "HOST9",
+ "NETC",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown"
+};
+
+struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba,
+ unsigned int *ref_tag)
{
- unsigned int num_loop;
- u8 *mpu_sem = 0;
- u32 status;
-
- num_loop = 1000;
- mpu_sem = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
- msleep(5000);
-
- while (num_loop) {
- status = readl((void *)mpu_sem);
-
- if ((status & 0x80000000) || (status & 0x0000FFFF) == 0xC000)
- break;
- msleep(60);
- num_loop--;
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ struct be_mcc_wrb *wrb = NULL;
+ unsigned int tag;
+
+ spin_lock(&phba->ctrl.mcc_lock);
+ if (mccq->used == mccq->len) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT |
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BC_%d : MCC queue full: WRB used %u tag avail %u\n",
+ mccq->used, phba->ctrl.mcc_tag_available);
+ goto alloc_failed;
}
- if ((status & 0x80000000) || (!num_loop)) {
- printk(KERN_ERR "Failed in be_chk_reset_complete"
- "status = 0x%x\n", status);
- return -EIO;
+ if (!phba->ctrl.mcc_tag_available)
+ goto alloc_failed;
+
+ tag = phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index];
+ if (!tag) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT |
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BC_%d : MCC tag 0 allocated: tag avail %u alloc index %u\n",
+ phba->ctrl.mcc_tag_available,
+ phba->ctrl.mcc_alloc_index);
+ goto alloc_failed;
}
- return 0;
-}
+ /* return this tag for further reference */
+ *ref_tag = tag;
+ phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index] = 0;
+ phba->ctrl.mcc_tag_status[tag] = 0;
+ phba->ctrl.ptag_state[tag].tag_state = 0;
+ phba->ctrl.ptag_state[tag].cbfn = NULL;
+ phba->ctrl.mcc_tag_available--;
+ if (phba->ctrl.mcc_alloc_index == (MAX_MCC_CMD - 1))
+ phba->ctrl.mcc_alloc_index = 0;
+ else
+ phba->ctrl.mcc_alloc_index++;
-void be_mcc_notify(struct beiscsi_hba *phba)
-{
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
- u32 val = 0;
+ wrb = queue_head_node(mccq);
+ memset(wrb, 0, sizeof(*wrb));
+ wrb->tag0 = tag;
+ wrb->tag0 |= (mccq->head << MCC_Q_WRB_IDX_SHIFT) & MCC_Q_WRB_IDX_MASK;
+ queue_head_inc(mccq);
+ mccq->used++;
- val |= mccq->id & DB_MCCQ_RING_ID_MASK;
- val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT;
- iowrite32(val, phba->db_va + DB_MCCQ_OFFSET);
+alloc_failed:
+ spin_unlock(&phba->ctrl.mcc_lock);
+ return wrb;
}
-unsigned int alloc_mcc_tag(struct beiscsi_hba *phba)
+void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag)
{
- unsigned int tag = 0;
-
- if (phba->ctrl.mcc_tag_available) {
- tag = phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index];
- phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index] = 0;
- phba->ctrl.mcc_numtag[tag] = 0;
- }
- if (tag) {
- phba->ctrl.mcc_tag_available--;
- if (phba->ctrl.mcc_alloc_index == (MAX_MCC_CMD - 1))
- phba->ctrl.mcc_alloc_index = 0;
- else
- phba->ctrl.mcc_alloc_index++;
- }
- return tag;
-}
+ struct be_queue_info *mccq = &ctrl->mcc_obj.q;
-void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag)
-{
- spin_lock(&ctrl->mbox_lock);
- tag = tag & 0x000000FF;
+ spin_lock(&ctrl->mcc_lock);
+ tag = tag & MCC_Q_CMD_TAG_MASK;
ctrl->mcc_tag[ctrl->mcc_free_index] = tag;
if (ctrl->mcc_free_index == (MAX_MCC_CMD - 1))
ctrl->mcc_free_index = 0;
else
ctrl->mcc_free_index++;
ctrl->mcc_tag_available++;
- spin_unlock(&ctrl->mbox_lock);
+ mccq->used--;
+ spin_unlock(&ctrl->mcc_lock);
}
-bool is_link_state_evt(u32 trailer)
+/*
+ * beiscsi_mcc_compl_status - Return the status of MCC completion
+ * @phba: Driver private structure
+ * @tag: Tag for the MBX Command
+ * @wrb: the WRB used for the MBX Command
+ * @mbx_cmd_mem: ptr to memory allocated for MBX Cmd
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ */
+int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba,
+ unsigned int tag,
+ struct be_mcc_wrb **wrb,
+ struct be_dma_mem *mbx_cmd_mem)
{
- return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
- ASYNC_TRAILER_EVENT_CODE_MASK) ==
- ASYNC_EVENT_CODE_LINK_STATE);
-}
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ uint16_t status = 0, addl_status = 0, wrb_num = 0;
+ struct be_cmd_resp_hdr *mbx_resp_hdr;
+ struct be_cmd_req_hdr *mbx_hdr;
+ struct be_mcc_wrb *temp_wrb;
+ uint32_t mcc_tag_status;
+ int rc = 0;
+
+ mcc_tag_status = phba->ctrl.mcc_tag_status[tag];
+ status = (mcc_tag_status & CQE_STATUS_MASK);
+ addl_status = ((mcc_tag_status & CQE_STATUS_ADDL_MASK) >>
+ CQE_STATUS_ADDL_SHIFT);
+
+ if (mbx_cmd_mem) {
+ mbx_hdr = (struct be_cmd_req_hdr *)mbx_cmd_mem->va;
+ } else {
+ wrb_num = (mcc_tag_status & CQE_STATUS_WRB_MASK) >>
+ CQE_STATUS_WRB_SHIFT;
+ temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num);
+ mbx_hdr = embedded_payload(temp_wrb);
-static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl)
-{
- if (compl->flags != 0) {
- compl->flags = le32_to_cpu(compl->flags);
- WARN_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0);
- return true;
- } else
- return false;
-}
+ if (wrb)
+ *wrb = temp_wrb;
+ }
-static inline void be_mcc_compl_use(struct be_mcc_compl *compl)
-{
- compl->flags = 0;
+ if (status || addl_status) {
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
+ BEISCSI_LOG_CONFIG,
+ "BC_%d : MBX Cmd Failed for Subsys : %d Opcode : %d with Status : %d and Extd_Status : %d\n",
+ mbx_hdr->subsystem, mbx_hdr->opcode,
+ status, addl_status);
+ rc = -EIO;
+ if (status == MCC_STATUS_INSUFFICIENT_BUFFER) {
+ mbx_resp_hdr = (struct be_cmd_resp_hdr *)mbx_hdr;
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
+ BEISCSI_LOG_CONFIG,
+ "BC_%d : Insufficient Buffer Error Resp_Len : %d Actual_Resp_Len : %d\n",
+ mbx_resp_hdr->response_length,
+ mbx_resp_hdr->actual_resp_len);
+ rc = -EAGAIN;
+ }
+ }
+
+ return rc;
}
-static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
- struct be_mcc_compl *compl)
+/*
+ * beiscsi_mccq_compl_wait()- Process completion in MCC CQ
+ * @phba: Driver private structure
+ * @tag: Tag for the MBX Command
+ * @wrb: the WRB used for the MBX Command
+ * @mbx_cmd_mem: ptr to memory allocated for MBX Cmd
+ *
+ * Waits for MBX completion with the passed TAG.
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ **/
+int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba,
+ unsigned int tag,
+ struct be_mcc_wrb **wrb,
+ struct be_dma_mem *mbx_cmd_mem)
{
- u16 compl_status, extd_status;
+ int rc = 0;
- be_dws_le_to_cpu(compl, 4);
+ if (!tag || tag > MAX_MCC_CMD) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : invalid tag %u\n", tag);
+ return -EINVAL;
+ }
- compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
- CQE_STATUS_COMPL_MASK;
- if (compl_status != MCC_STATUS_SUCCESS) {
- extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
- CQE_STATUS_EXTD_MASK;
- dev_err(&ctrl->pdev->dev,
- "error in cmd completion: status(compl/extd)=%d/%d\n",
- compl_status, extd_status);
+ if (beiscsi_hba_in_error(phba)) {
+ clear_bit(MCC_TAG_STATE_RUNNING,
+ &phba->ctrl.ptag_state[tag].tag_state);
+ return -EIO;
+ }
+
+ /* wait for the mccq completion */
+ rc = wait_event_interruptible_timeout(phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_tag_status[tag],
+ msecs_to_jiffies(
+ BEISCSI_HOST_MBX_TIMEOUT));
+ /**
+ * Return EIO if port is being disabled. Associated DMA memory, if any,
+ * is freed by the caller. When port goes offline, MCCQ is cleaned up
+ * so does WRB.
+ */
+ if (!test_bit(BEISCSI_HBA_ONLINE, &phba->state)) {
+ clear_bit(MCC_TAG_STATE_RUNNING,
+ &phba->ctrl.ptag_state[tag].tag_state);
+ return -EIO;
+ }
+
+ /**
+ * If MBOX cmd timeout expired, tag and resource allocated
+ * for cmd is not freed until FW returns completion.
+ */
+ if (rc <= 0) {
+ struct be_dma_mem *tag_mem;
+
+ /**
+ * PCI/DMA memory allocated and posted in non-embedded mode
+ * will have mbx_cmd_mem != NULL.
+ * Save virtual and bus addresses for the command so that it
+ * can be freed later.
+ **/
+ tag_mem = &phba->ctrl.ptag_state[tag].tag_mem_state;
+ if (mbx_cmd_mem) {
+ tag_mem->size = mbx_cmd_mem->size;
+ tag_mem->va = mbx_cmd_mem->va;
+ tag_mem->dma = mbx_cmd_mem->dma;
+ } else
+ tag_mem->size = 0;
+
+ /* first make tag_mem_state visible to all */
+ wmb();
+ set_bit(MCC_TAG_STATE_TIMEOUT,
+ &phba->ctrl.ptag_state[tag].tag_state);
+
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
+ BEISCSI_LOG_CONFIG,
+ "BC_%d : MBX Cmd Completion timed out\n");
return -EBUSY;
}
- return 0;
+
+ rc = __beiscsi_mcc_compl_status(phba, tag, wrb, mbx_cmd_mem);
+
+ free_mcc_wrb(&phba->ctrl, tag);
+ return rc;
}
-int be_mcc_compl_process_isr(struct be_ctrl_info *ctrl,
- struct be_mcc_compl *compl)
+/*
+ * beiscsi_process_mbox_compl()- Check the MBX completion status
+ * @ctrl: Function specific MBX data structure
+ * @compl: Completion status of MBX Command
+ *
+ * Check for the MBX completion status when BMBX method used
+ *
+ * return
+ * Success: Zero
+ * Failure: Non-Zero
+ **/
+static int beiscsi_process_mbox_compl(struct be_ctrl_info *ctrl,
+ struct be_mcc_compl *compl)
{
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ struct be_cmd_req_hdr *hdr = embedded_payload(wrb);
u16 compl_status, extd_status;
- unsigned short tag;
- be_dws_le_to_cpu(compl, 4);
+ /**
+ * To check if valid bit is set, check the entire word as we don't know
+ * the endianness of the data (old entry is host endian while a new
+ * entry is little endian)
+ */
+ if (!compl->flags) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BC_%d : BMBX busy, no completion\n");
+ return -EBUSY;
+ }
+ compl->flags = le32_to_cpu(compl->flags);
+ WARN_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0);
- compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
- CQE_STATUS_COMPL_MASK;
- /* The ctrl.mcc_numtag[tag] is filled with
- * [31] = valid, [30:24] = Rsvd, [23:16] = wrb, [15:8] = extd_status,
- * [7:0] = compl_status
+ /**
+ * Just swap the status to host endian;
+ * mcc tag is opaquely copied from mcc_wrb.
*/
- tag = (compl->tag0 & 0x000000FF);
+ be_dws_le_to_cpu(compl, 4);
+ compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
+ CQE_STATUS_COMPL_MASK;
extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
- CQE_STATUS_EXTD_MASK;
+ CQE_STATUS_EXTD_MASK;
+ /* Need to reset the entire word that houses the valid bit */
+ compl->flags = 0;
- ctrl->mcc_numtag[tag] = 0x80000000;
- ctrl->mcc_numtag[tag] |= (compl->tag0 & 0x00FF0000);
- ctrl->mcc_numtag[tag] |= (extd_status & 0x000000FF) << 8;
- ctrl->mcc_numtag[tag] |= (compl_status & 0x000000FF);
- wake_up_interruptible(&ctrl->mcc_wait[tag]);
- return 0;
+ if (compl_status == MCC_STATUS_SUCCESS)
+ return 0;
+
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BC_%d : error in cmd completion: Subsystem : %d Opcode : %d status(compl/extd)=%d/%d\n",
+ hdr->subsystem, hdr->opcode, compl_status, extd_status);
+ return compl_status;
}
-static struct be_mcc_compl *be_mcc_compl_get(struct beiscsi_hba *phba)
+static void beiscsi_process_async_link(struct beiscsi_hba *phba,
+ struct be_mcc_compl *compl)
{
- struct be_queue_info *mcc_cq = &phba->ctrl.mcc_obj.cq;
- struct be_mcc_compl *compl = queue_tail_node(mcc_cq);
-
- if (be_mcc_compl_is_new(compl)) {
- queue_tail_inc(mcc_cq);
- return compl;
+ struct be_async_event_link_state *evt;
+
+ evt = (struct be_async_event_link_state *)compl;
+
+ phba->port_speed = evt->port_speed;
+ /**
+ * Check logical link status in ASYNC event.
+ * This has been newly introduced in SKH-R Firmware 10.0.338.45.
+ **/
+ if (evt->port_link_status & BE_ASYNC_LINK_UP_MASK) {
+ set_bit(BEISCSI_HBA_LINK_UP, &phba->state);
+ if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state))
+ beiscsi_start_boot_work(phba, BE_BOOT_INVALID_SHANDLE);
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : Link Up on Port %d tag 0x%x\n",
+ evt->physical_port, evt->event_tag);
+ } else {
+ clear_bit(BEISCSI_HBA_LINK_UP, &phba->state);
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : Link Down on Port %d tag 0x%x\n",
+ evt->physical_port, evt->event_tag);
+ iscsi_host_for_each_session(phba->shost,
+ beiscsi_session_fail);
}
- return NULL;
}
-static void be2iscsi_fail_session(struct iscsi_cls_session *cls_session)
+static char *beiscsi_port_misconf_event_msg[] = {
+ "Physical Link is functional.",
+ "Optics faulted/incorrectly installed/not installed - Reseat optics, if issue not resolved, replace.",
+ "Optics of two types installed - Remove one optic or install matching pair of optics.",
+ "Incompatible optics - Replace with compatible optics for card to function.",
+ "Unqualified optics - Replace with Avago optics for Warranty and Technical Support.",
+ "Uncertified optics - Replace with Avago Certified optics to enable link operation."
+};
+
+static void beiscsi_process_async_sli(struct beiscsi_hba *phba,
+ struct be_mcc_compl *compl)
{
- iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED);
+ struct be_async_event_sli *async_sli;
+ u8 evt_type, state, old_state, le;
+ char *sev = KERN_WARNING;
+ char *msg = NULL;
+
+ evt_type = compl->flags >> ASYNC_TRAILER_EVENT_TYPE_SHIFT;
+ evt_type &= ASYNC_TRAILER_EVENT_TYPE_MASK;
+
+ /* processing only MISCONFIGURED physical port event */
+ if (evt_type != ASYNC_SLI_EVENT_TYPE_MISCONFIGURED)
+ return;
+
+ async_sli = (struct be_async_event_sli *)compl;
+ state = async_sli->event_data1 >>
+ (phba->fw_config.phys_port * 8) & 0xff;
+ le = async_sli->event_data2 >>
+ (phba->fw_config.phys_port * 8) & 0xff;
+
+ old_state = phba->optic_state;
+ phba->optic_state = state;
+
+ if (state >= ARRAY_SIZE(beiscsi_port_misconf_event_msg)) {
+ /* fw is reporting a state we don't know, log and return */
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : Port %c: Unrecognized optic state 0x%x\n",
+ phba->port_name, async_sli->event_data1);
+ return;
+ }
+
+ if (ASYNC_SLI_LINK_EFFECT_VALID(le)) {
+ /* log link effect for unqualified-4, uncertified-5 optics */
+ if (state > 3)
+ msg = (ASYNC_SLI_LINK_EFFECT_STATE(le)) ?
+ " Link is non-operational." :
+ " Link is operational.";
+ /* 1 - info */
+ if (ASYNC_SLI_LINK_EFFECT_SEV(le) == 1)
+ sev = KERN_INFO;
+ /* 2 - error */
+ if (ASYNC_SLI_LINK_EFFECT_SEV(le) == 2)
+ sev = KERN_ERR;
+ }
+
+ if (old_state != phba->optic_state)
+ __beiscsi_log(phba, sev, "BC_%d : Port %c: %s%s\n",
+ phba->port_name,
+ beiscsi_port_misconf_event_msg[state],
+ !msg ? "" : msg);
}
-void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
- struct be_async_event_link_state *evt)
+void beiscsi_process_async_event(struct beiscsi_hba *phba,
+ struct be_mcc_compl *compl)
{
- switch (evt->port_link_status) {
- case ASYNC_EVENT_LINK_DOWN:
- SE_DEBUG(DBG_LVL_1, "Link Down on Physical Port %d\n",
- evt->physical_port);
- phba->state |= BE_ADAPTER_LINK_DOWN;
- iscsi_host_for_each_session(phba->shost,
- be2iscsi_fail_session);
+ char *sev = KERN_INFO;
+ u8 evt_code;
+
+ /* interpret flags as an async trailer */
+ evt_code = compl->flags >> ASYNC_TRAILER_EVENT_CODE_SHIFT;
+ evt_code &= ASYNC_TRAILER_EVENT_CODE_MASK;
+ switch (evt_code) {
+ case ASYNC_EVENT_CODE_LINK_STATE:
+ beiscsi_process_async_link(phba, compl);
+ break;
+ case ASYNC_EVENT_CODE_ISCSI:
+ if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state))
+ beiscsi_start_boot_work(phba, BE_BOOT_INVALID_SHANDLE);
+ sev = KERN_ERR;
break;
- case ASYNC_EVENT_LINK_UP:
- phba->state = BE_ADAPTER_UP;
- SE_DEBUG(DBG_LVL_1, "Link UP on Physical Port %d\n",
- evt->physical_port);
+ case ASYNC_EVENT_CODE_SLI:
+ beiscsi_process_async_sli(phba, compl);
break;
default:
- SE_DEBUG(DBG_LVL_1, "Unexpected Async Notification %d on"
- "Physical Port %d\n",
- evt->port_link_status,
- evt->physical_port);
+ /* event not registered */
+ sev = KERN_ERR;
}
-}
-static void beiscsi_cq_notify(struct beiscsi_hba *phba, u16 qid, bool arm,
- u16 num_popped)
-{
- u32 val = 0;
- val |= qid & DB_CQ_RING_ID_MASK;
- if (arm)
- val |= 1 << DB_CQ_REARM_SHIFT;
- val |= num_popped << DB_CQ_NUM_POPPED_SHIFT;
- iowrite32(val, phba->db_va + DB_CQ_OFFSET);
+ beiscsi_log(phba, sev, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BC_%d : ASYNC Event %x: status 0x%08x flags 0x%08x\n",
+ evt_code, compl->status, compl->flags);
}
-
-int beiscsi_process_mcc(struct beiscsi_hba *phba)
+int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl,
+ struct be_mcc_compl *compl)
{
- struct be_mcc_compl *compl;
- int num = 0, status = 0;
- struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ u16 compl_status, extd_status;
+ struct be_dma_mem *tag_mem;
+ unsigned int tag, wrb_idx;
- spin_lock_bh(&phba->ctrl.mcc_cq_lock);
- while ((compl = be_mcc_compl_get(phba))) {
- if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
- /* Interpret flags as an async trailer */
- if (is_link_state_evt(compl->flags))
- /* Interpret compl as a async link evt */
- beiscsi_async_link_state_process(phba,
- (struct be_async_event_link_state *) compl);
- else
- SE_DEBUG(DBG_LVL_1,
- " Unsupported Async Event, flags"
- " = 0x%08x\n", compl->flags);
-
- } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) {
- status = be_mcc_compl_process(ctrl, compl);
- atomic_dec(&phba->ctrl.mcc_obj.q.used);
- }
- be_mcc_compl_use(compl);
- num++;
+ be_dws_le_to_cpu(compl, 4);
+ tag = (compl->tag0 & MCC_Q_CMD_TAG_MASK);
+ wrb_idx = (compl->tag0 & CQE_STATUS_WRB_MASK) >> CQE_STATUS_WRB_SHIFT;
+
+ if (!test_bit(MCC_TAG_STATE_RUNNING,
+ &ctrl->ptag_state[tag].tag_state)) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_MBOX |
+ BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
+ "BC_%d : MBX cmd completed but not posted\n");
+ return 0;
}
- if (num)
- beiscsi_cq_notify(phba, phba->ctrl.mcc_obj.cq.id, true, num);
-
- spin_unlock_bh(&phba->ctrl.mcc_cq_lock);
- return status;
-}
-
-/* Wait till no more pending mcc requests are present */
-static int be_mcc_wait_compl(struct beiscsi_hba *phba)
-{
- int i, status;
- for (i = 0; i < mcc_timeout; i++) {
- status = beiscsi_process_mcc(phba);
- if (status)
- return status;
+ /* end MCC with this tag */
+ clear_bit(MCC_TAG_STATE_RUNNING, &ctrl->ptag_state[tag].tag_state);
+
+ if (test_bit(MCC_TAG_STATE_TIMEOUT, &ctrl->ptag_state[tag].tag_state)) {
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_MBOX | BEISCSI_LOG_INIT |
+ BEISCSI_LOG_CONFIG,
+ "BC_%d : MBX Completion for timeout Command from FW\n");
+ /**
+ * Check for the size before freeing resource.
+ * Only for non-embedded cmd, PCI resource is allocated.
+ **/
+ tag_mem = &ctrl->ptag_state[tag].tag_mem_state;
+ if (tag_mem->size) {
+ dma_free_coherent(&ctrl->pdev->dev, tag_mem->size,
+ tag_mem->va, tag_mem->dma);
+ tag_mem->size = 0;
+ }
+ free_mcc_wrb(ctrl, tag);
+ return 0;
+ }
- if (atomic_read(&phba->ctrl.mcc_obj.q.used) == 0)
- break;
- udelay(100);
+ compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
+ CQE_STATUS_COMPL_MASK;
+ extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
+ CQE_STATUS_EXTD_MASK;
+ /* The ctrl.mcc_tag_status[tag] is filled with
+ * [31] = valid, [30:24] = Rsvd, [23:16] = wrb, [15:8] = extd_status,
+ * [7:0] = compl_status
+ */
+ ctrl->mcc_tag_status[tag] = CQE_VALID_MASK;
+ ctrl->mcc_tag_status[tag] |= (wrb_idx << CQE_STATUS_WRB_SHIFT);
+ ctrl->mcc_tag_status[tag] |= (extd_status << CQE_STATUS_ADDL_SHIFT) &
+ CQE_STATUS_ADDL_MASK;
+ ctrl->mcc_tag_status[tag] |= (compl_status & CQE_STATUS_MASK);
+
+ if (test_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state)) {
+ if (ctrl->ptag_state[tag].cbfn)
+ ctrl->ptag_state[tag].cbfn(phba, tag);
+ else
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : MBX ASYNC command with no callback\n");
+ free_mcc_wrb(ctrl, tag);
+ return 0;
}
- if (i == mcc_timeout) {
- dev_err(&phba->pcidev->dev, "mccq poll timed out\n");
- return -EBUSY;
+
+ if (test_bit(MCC_TAG_STATE_IGNORE, &ctrl->ptag_state[tag].tag_state)) {
+ /* just check completion status and free wrb */
+ __beiscsi_mcc_compl_status(phba, tag, NULL, NULL);
+ free_mcc_wrb(ctrl, tag);
+ return 0;
}
+
+ wake_up_interruptible(&ctrl->mcc_wait[tag]);
return 0;
}
-/* Notify MCC requests and wait for completion */
-int be_mcc_notify_wait(struct beiscsi_hba *phba)
+void be_mcc_notify(struct beiscsi_hba *phba, unsigned int tag)
{
- be_mcc_notify(phba);
- return be_mcc_wait_compl(phba);
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ u32 val = 0;
+
+ set_bit(MCC_TAG_STATE_RUNNING, &phba->ctrl.ptag_state[tag].tag_state);
+ val |= mccq->id & DB_MCCQ_RING_ID_MASK;
+ val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT;
+ /* make request available for DMA */
+ wmb();
+ iowrite32(val, phba->db_va + DB_MCCQ_OFFSET);
}
-static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl)
+/*
+ * be_mbox_db_ready_poll()- Check ready status
+ * @ctrl: Function specific MBX data structure
+ *
+ * Check for the ready status of FW to send BMBX
+ * commands to adapter.
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ **/
+static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl)
{
-#define long_delay 2000
+ /* wait 30s for generic non-flash MBOX operation */
+#define BEISCSI_MBX_RDY_BIT_TIMEOUT 30000
void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET;
- int cnt = 0, wait = 5; /* in usecs */
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ unsigned long timeout;
u32 ready;
+ /*
+ * This BMBX busy wait path is used during init only.
+ * For the commands executed during init, 5s should suffice.
+ */
+ timeout = jiffies + msecs_to_jiffies(BEISCSI_MBX_RDY_BIT_TIMEOUT);
do {
- ready = ioread32(db) & MPU_MAILBOX_DB_RDY_MASK;
- if (ready)
- break;
+ if (beiscsi_hba_in_error(phba))
+ return -EIO;
- if (cnt > 12000000) {
- dev_err(&ctrl->pdev->dev, "mbox_db poll timed out\n");
- return -EBUSY;
- }
+ ready = ioread32(db);
+ if (ready == 0xffffffff)
+ return -EIO;
- if (cnt > 50) {
- wait = long_delay;
- mdelay(long_delay / 1000);
- } else
- udelay(wait);
- cnt += wait;
- } while (true);
- return 0;
+ ready &= MPU_MAILBOX_DB_RDY_MASK;
+ if (ready)
+ return 0;
+
+ if (time_after(jiffies, timeout))
+ break;
+ /* 1ms sleep is enough in most cases */
+ schedule_timeout_uninterruptible(msecs_to_jiffies(1));
+ } while (!ready);
+
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BC_%d : FW Timed Out\n");
+ set_bit(BEISCSI_HBA_FW_TIMEOUT, &phba->state);
+ return -EBUSY;
}
-int be_mbox_notify(struct be_ctrl_info *ctrl)
+/*
+ * be_mbox_notify: Notify adapter of new BMBX command
+ * @ctrl: Function specific MBX data structure
+ *
+ * Ring doorbell to inform adapter of a BMBX command
+ * to process
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ **/
+static int be_mbox_notify(struct be_ctrl_info *ctrl)
{
int status;
u32 val = 0;
void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET;
struct be_dma_mem *mbox_mem = &ctrl->mbox_mem;
struct be_mcc_mailbox *mbox = mbox_mem->va;
- struct be_mcc_compl *compl = &mbox->compl;
+
+ status = be_mbox_db_ready_poll(ctrl);
+ if (status)
+ return status;
val &= ~MPU_MAILBOX_DB_RDY_MASK;
val |= MPU_MAILBOX_DB_HI_MASK;
val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2;
iowrite32(val, db);
- status = be_mbox_db_ready_wait(ctrl);
- if (status != 0) {
- SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed\n");
+ status = be_mbox_db_ready_poll(ctrl);
+ if (status)
return status;
- }
+
val = 0;
val &= ~MPU_MAILBOX_DB_RDY_MASK;
val &= ~MPU_MAILBOX_DB_HI_MASK;
val |= (u32) (mbox_mem->dma >> 4) << 2;
iowrite32(val, db);
- status = be_mbox_db_ready_wait(ctrl);
- if (status != 0) {
- SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed\n");
- return status;
- }
- if (be_mcc_compl_is_new(compl)) {
- status = be_mcc_compl_process(ctrl, &mbox->compl);
- be_mcc_compl_use(compl);
- if (status) {
- SE_DEBUG(DBG_LVL_1, "After be_mcc_compl_process\n");
- return status;
- }
- } else {
- dev_err(&ctrl->pdev->dev, "invalid mailbox completion\n");
- return -EBUSY;
- }
- return 0;
-}
-
-/*
- * Insert the mailbox address into the doorbell in two steps
- * Polls on the mbox doorbell till a command completion (or a timeout) occurs
- */
-static int be_mbox_notify_wait(struct beiscsi_hba *phba)
-{
- int status;
- u32 val = 0;
- void __iomem *db = phba->ctrl.db + MPU_MAILBOX_DB_OFFSET;
- struct be_dma_mem *mbox_mem = &phba->ctrl.mbox_mem;
- struct be_mcc_mailbox *mbox = mbox_mem->va;
- struct be_mcc_compl *compl = &mbox->compl;
- struct be_ctrl_info *ctrl = &phba->ctrl;
-
- val |= MPU_MAILBOX_DB_HI_MASK;
- /* at bits 2 - 31 place mbox dma addr msb bits 34 - 63 */
- val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2;
- iowrite32(val, db);
-
- /* wait for ready to be set */
- status = be_mbox_db_ready_wait(ctrl);
- if (status != 0)
+ status = be_mbox_db_ready_poll(ctrl);
+ if (status)
return status;
- val = 0;
- /* at bits 2 - 31 place mbox dma addr lsb bits 4 - 33 */
- val |= (u32)(mbox_mem->dma >> 4) << 2;
- iowrite32(val, db);
-
- status = be_mbox_db_ready_wait(ctrl);
- if (status != 0)
- return status;
+ /* RDY is set; small delay before CQE read. */
+ udelay(1);
- /* A cq entry has been made now */
- if (be_mcc_compl_is_new(compl)) {
- status = be_mcc_compl_process(ctrl, &mbox->compl);
- be_mcc_compl_use(compl);
- if (status)
- return status;
- } else {
- dev_err(&phba->pcidev->dev, "invalid mailbox completion\n");
- return -EBUSY;
- }
- return 0;
+ status = beiscsi_process_mbox_compl(ctrl, &mbox->compl);
+ return status;
}
-void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
- bool embedded, u8 sge_cnt)
+void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, u32 payload_len,
+ bool embedded, u8 sge_cnt)
{
if (embedded)
- wrb->embedded |= MCC_WRB_EMBEDDED_MASK;
+ wrb->emb_sgecnt_special |= MCC_WRB_EMBEDDED_MASK;
else
- wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) <<
- MCC_WRB_SGE_CNT_SHIFT;
+ wrb->emb_sgecnt_special |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) <<
+ MCC_WRB_SGE_CNT_SHIFT;
wrb->payload_length = payload_len;
be_dws_cpu_to_le(wrb, 8);
}
void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
- u8 subsystem, u8 opcode, int cmd_len)
+ u8 subsystem, u8 opcode, u32 cmd_len)
{
req_hdr->opcode = opcode;
req_hdr->subsystem = subsystem;
req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr));
- req_hdr->timeout = 120;
+ req_hdr->timeout = BEISCSI_FW_MBX_TIMEOUT;
}
static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
@@ -502,21 +732,6 @@ struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem)
return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb;
}
-struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba)
-{
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
- struct be_mcc_wrb *wrb;
-
- BUG_ON(atomic_read(&mccq->used) >= mccq->len);
- wrb = queue_head_node(mccq);
- memset(wrb, 0, sizeof(*wrb));
- wrb->tag0 = (mccq->head & 0x000000FF) << 16;
- queue_head_inc(mccq);
- atomic_inc(&mccq->used);
- return wrb;
-}
-
-
int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *eq, int eq_delay)
{
@@ -526,8 +741,7 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_dma_mem *q_mem = &eq->dma_mem;
int status;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_eq_create\n");
- spin_lock(&ctrl->mbox_lock);
+ mutex_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -554,36 +768,7 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
eq->id = le16_to_cpu(resp->eq_id);
eq->created = true;
}
- spin_unlock(&ctrl->mbox_lock);
- return status;
-}
-
-int be_cmd_fw_initialize(struct be_ctrl_info *ctrl)
-{
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
- int status;
- u8 *endian_check;
-
- SE_DEBUG(DBG_LVL_8, "In be_cmd_fw_initialize\n");
- spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
-
- endian_check = (u8 *) wrb;
- *endian_check++ = 0xFF;
- *endian_check++ = 0x12;
- *endian_check++ = 0x34;
- *endian_check++ = 0xFF;
- *endian_check++ = 0xFF;
- *endian_check++ = 0x56;
- *endian_check++ = 0x78;
- *endian_check++ = 0xFF;
- be_dws_cpu_to_le(wrb, sizeof(*wrb));
-
- status = be_mbox_notify(ctrl);
- if (status)
- SE_DEBUG(DBG_LVL_1, "be_cmd_fw_initialize Failed\n");
-
- spin_unlock(&ctrl->mbox_lock);
+ mutex_unlock(&ctrl->mbox_lock);
return status;
}
@@ -594,34 +779,48 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_cmd_req_cq_create *req = embedded_payload(wrb);
struct be_cmd_resp_cq_create *resp = embedded_payload(wrb);
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
struct be_dma_mem *q_mem = &cq->dma_mem;
void *ctxt = &req->context;
int status;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_cq_create\n");
- spin_lock(&ctrl->mbox_lock);
+ mutex_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_CQ_CREATE, sizeof(*req));
- if (!q_mem->va)
- SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n");
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
+ if (is_chip_be2_be3r(phba)) {
+ AMAP_SET_BITS(struct amap_cq_context, coalescwm,
+ ctxt, coalesce_wm);
+ AMAP_SET_BITS(struct amap_cq_context, nodelay, ctxt, no_delay);
+ AMAP_SET_BITS(struct amap_cq_context, count, ctxt,
+ __ilog2_u32(cq->len / 256));
+ AMAP_SET_BITS(struct amap_cq_context, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts);
+ AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id);
+ AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context, func, ctxt,
+ PCI_FUNC(ctrl->pdev->devfn));
+ } else {
+ req->hdr.version = MBX_CMD_VER2;
+ req->page_size = 1;
+ AMAP_SET_BITS(struct amap_cq_context_v2, coalescwm,
+ ctxt, coalesce_wm);
+ AMAP_SET_BITS(struct amap_cq_context_v2, nodelay,
+ ctxt, no_delay);
+ AMAP_SET_BITS(struct amap_cq_context_v2, count, ctxt,
+ __ilog2_u32(cq->len / 256));
+ AMAP_SET_BITS(struct amap_cq_context_v2, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context_v2, eventable, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context_v2, eqid, ctxt, eq->id);
+ AMAP_SET_BITS(struct amap_cq_context_v2, armed, ctxt, 1);
+ }
- AMAP_SET_BITS(struct amap_cq_context, coalescwm, ctxt, coalesce_wm);
- AMAP_SET_BITS(struct amap_cq_context, nodelay, ctxt, no_delay);
- AMAP_SET_BITS(struct amap_cq_context, count, ctxt,
- __ilog2_u32(cq->len / 256));
- AMAP_SET_BITS(struct amap_cq_context, valid, ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts);
- AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id);
- AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context, func, ctxt,
- PCI_FUNC(ctrl->pdev->devfn));
be_dws_cpu_to_le(ctxt, sizeof(req->context));
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
@@ -631,9 +830,11 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
cq->id = le16_to_cpu(resp->cq_id);
cq->created = true;
} else
- SE_DEBUG(DBG_LVL_1, "In be_cmd_cq_create, status=ox%08x\n",
- status);
- spin_unlock(&ctrl->mbox_lock);
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BC_%d : In be_cmd_cq_create, status=ox%08x\n",
+ status);
+
+ mutex_unlock(&ctrl->mbox_lock);
return status;
}
@@ -651,24 +852,28 @@ int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
struct be_queue_info *cq)
{
struct be_mcc_wrb *wrb;
- struct be_cmd_req_mcc_create *req;
+ struct be_cmd_req_mcc_create_ext *req;
struct be_dma_mem *q_mem = &mccq->dma_mem;
struct be_ctrl_info *ctrl;
void *ctxt;
int status;
- spin_lock(&phba->ctrl.mbox_lock);
+ mutex_lock(&phba->ctrl.mbox_lock);
ctrl = &phba->ctrl;
wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ memset(wrb, 0, sizeof(*wrb));
req = embedded_payload(wrb);
ctxt = &req->context;
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MCC_CREATE, sizeof(*req));
+ OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req));
req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
+ req->async_evt_bitmap = 1 << ASYNC_EVENT_CODE_LINK_STATE;
+ req->async_evt_bitmap |= 1 << ASYNC_EVENT_CODE_ISCSI;
+ req->async_evt_bitmap |= 1 << ASYNC_EVENT_CODE_SLI;
AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt,
PCI_FUNC(phba->pcidev->devfn));
@@ -681,13 +886,13 @@ int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
- status = be_mbox_notify_wait(phba);
+ status = be_mbox_notify(ctrl);
if (!status) {
struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb);
mccq->id = le16_to_cpu(resp->id);
mccq->created = true;
}
- spin_unlock(&phba->ctrl.mbox_lock);
+ mutex_unlock(&phba->ctrl.mbox_lock);
return status;
}
@@ -697,11 +902,15 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_cmd_req_q_destroy *req = embedded_payload(wrb);
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
u8 subsys = 0, opcode = 0;
int status;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_q_destroy\n");
- spin_lock(&ctrl->mbox_lock);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BC_%d : In beiscsi_cmd_q_destroy "
+ "queue_type : %d\n", queue_type);
+
+ mutex_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -731,9 +940,8 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
opcode = OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES;
break;
default:
- spin_unlock(&ctrl->mbox_lock);
+ mutex_unlock(&ctrl->mbox_lock);
BUG();
- return -ENXIO;
}
be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req));
if (queue_type != QTYPE_SGL)
@@ -741,23 +949,42 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
status = be_mbox_notify(ctrl);
- spin_unlock(&ctrl->mbox_lock);
+ mutex_unlock(&ctrl->mbox_lock);
return status;
}
+/**
+ * be_cmd_create_default_pdu_queue()- Create DEFQ for the adapter
+ * @ctrl: ptr to ctrl_info
+ * @cq: Completion Queue
+ * @dq: Default Queue
+ * @length: ring size
+ * @entry_size: size of each entry in DEFQ
+ * @is_header: Header or Data DEFQ
+ * @ulp_num: Bind to which ULP
+ *
+ * Create HDR/Data DEFQ for the passed ULP. Unsol PDU are posted
+ * on this queue by the FW
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero Value
+ *
+ **/
int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
struct be_queue_info *cq,
struct be_queue_info *dq, int length,
- int entry_size)
+ int entry_size, uint8_t is_header,
+ uint8_t ulp_num)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_defq_create_req *req = embedded_payload(wrb);
struct be_dma_mem *q_mem = &dq->dma_mem;
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
void *ctxt = &req->context;
int status;
- SE_DEBUG(DBG_LVL_8, "In be_cmd_create_default_pdu_queue\n");
- spin_lock(&ctrl->mbox_lock);
+ mutex_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -766,17 +993,41 @@ int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
OPCODE_COMMON_ISCSI_DEFQ_CREATE, sizeof(*req));
req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
- AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid, ctxt, 0);
- AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid_valid, ctxt,
- 1);
- AMAP_SET_BITS(struct amap_be_default_pdu_context, pci_func_id, ctxt,
- PCI_FUNC(ctrl->pdev->devfn));
- AMAP_SET_BITS(struct amap_be_default_pdu_context, ring_size, ctxt,
- be_encoded_q_len(length / sizeof(struct phys_addr)));
- AMAP_SET_BITS(struct amap_be_default_pdu_context, default_buffer_size,
- ctxt, entry_size);
- AMAP_SET_BITS(struct amap_be_default_pdu_context, cq_id_recv, ctxt,
- cq->id);
+ if (phba->fw_config.dual_ulp_aware) {
+ req->ulp_num = ulp_num;
+ req->dua_feature |= (1 << BEISCSI_DUAL_ULP_AWARE_BIT);
+ req->dua_feature |= (1 << BEISCSI_BIND_Q_TO_ULP_BIT);
+ }
+
+ if (is_chip_be2_be3r(phba)) {
+ AMAP_SET_BITS(struct amap_be_default_pdu_context,
+ rx_pdid, ctxt, 0);
+ AMAP_SET_BITS(struct amap_be_default_pdu_context,
+ rx_pdid_valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_be_default_pdu_context,
+ pci_func_id, ctxt, PCI_FUNC(ctrl->pdev->devfn));
+ AMAP_SET_BITS(struct amap_be_default_pdu_context,
+ ring_size, ctxt,
+ be_encoded_q_len(length /
+ sizeof(struct phys_addr)));
+ AMAP_SET_BITS(struct amap_be_default_pdu_context,
+ default_buffer_size, ctxt, entry_size);
+ AMAP_SET_BITS(struct amap_be_default_pdu_context,
+ cq_id_recv, ctxt, cq->id);
+ } else {
+ AMAP_SET_BITS(struct amap_default_pdu_context_ext,
+ rx_pdid, ctxt, 0);
+ AMAP_SET_BITS(struct amap_default_pdu_context_ext,
+ rx_pdid_valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_default_pdu_context_ext,
+ ring_size, ctxt,
+ be_encoded_q_len(length /
+ sizeof(struct phys_addr)));
+ AMAP_SET_BITS(struct amap_default_pdu_context_ext,
+ default_buffer_size, ctxt, entry_size);
+ AMAP_SET_BITS(struct amap_default_pdu_context_ext,
+ cq_id_recv, ctxt, cq->id);
+ }
be_dws_cpu_to_le(ctxt, sizeof(req->context));
@@ -784,25 +1035,56 @@ int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
status = be_mbox_notify(ctrl);
if (!status) {
+ struct be_ring *defq_ring;
struct be_defq_create_resp *resp = embedded_payload(wrb);
dq->id = le16_to_cpu(resp->id);
dq->created = true;
+ if (is_header)
+ defq_ring = &phba->phwi_ctrlr->default_pdu_hdr[ulp_num];
+ else
+ defq_ring = &phba->phwi_ctrlr->
+ default_pdu_data[ulp_num];
+
+ defq_ring->id = dq->id;
+
+ if (!phba->fw_config.dual_ulp_aware) {
+ defq_ring->ulp_num = BEISCSI_ULP0;
+ defq_ring->doorbell_offset = DB_RXULP0_OFFSET;
+ } else {
+ defq_ring->ulp_num = resp->ulp_num;
+ defq_ring->doorbell_offset = resp->doorbell_offset;
+ }
}
- spin_unlock(&ctrl->mbox_lock);
+ mutex_unlock(&ctrl->mbox_lock);
return status;
}
-int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
- struct be_queue_info *wrbq)
+/**
+ * be_cmd_wrbq_create()- Create WRBQ
+ * @ctrl: ptr to ctrl_info
+ * @q_mem: memory details for the queue
+ * @wrbq: queue info
+ * @pwrb_context: ptr to wrb_context
+ * @ulp_num: ULP on which the WRBQ is to be created
+ *
+ * Create WRBQ on the passed ULP_NUM.
+ *
+ **/
+int be_cmd_wrbq_create(struct be_ctrl_info *ctrl,
+ struct be_dma_mem *q_mem,
+ struct be_queue_info *wrbq,
+ struct hwi_wrb_context *pwrb_context,
+ uint8_t ulp_num)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_wrbq_create_req *req = embedded_payload(wrb);
struct be_wrbq_create_resp *resp = embedded_payload(wrb);
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
int status;
- spin_lock(&ctrl->mbox_lock);
+ mutex_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -810,14 +1092,75 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_WRBQ_CREATE, sizeof(*req));
req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
+
+ if (phba->fw_config.dual_ulp_aware) {
+ req->ulp_num = ulp_num;
+ req->dua_feature |= (1 << BEISCSI_DUAL_ULP_AWARE_BIT);
+ req->dua_feature |= (1 << BEISCSI_BIND_Q_TO_ULP_BIT);
+ }
+
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
status = be_mbox_notify(ctrl);
if (!status) {
wrbq->id = le16_to_cpu(resp->cid);
wrbq->created = true;
+
+ pwrb_context->cid = wrbq->id;
+ if (!phba->fw_config.dual_ulp_aware) {
+ pwrb_context->doorbell_offset = DB_TXULP0_OFFSET;
+ pwrb_context->ulp_num = BEISCSI_ULP0;
+ } else {
+ pwrb_context->ulp_num = resp->ulp_num;
+ pwrb_context->doorbell_offset = resp->doorbell_offset;
+ }
}
- spin_unlock(&ctrl->mbox_lock);
+ mutex_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
+int be_cmd_iscsi_post_template_hdr(struct be_ctrl_info *ctrl,
+ struct be_dma_mem *q_mem)
+{
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_post_template_pages_req *req = embedded_payload(wrb);
+ int status;
+
+ mutex_lock(&ctrl->mbox_lock);
+
+ memset(wrb, 0, sizeof(*wrb));
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_ADD_TEMPLATE_HEADER_BUFFERS,
+ sizeof(*req));
+
+ req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
+ req->type = BEISCSI_TEMPLATE_HDR_TYPE_ISCSI;
+ be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
+
+ status = be_mbox_notify(ctrl);
+ mutex_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
+int be_cmd_iscsi_remove_template_hdr(struct be_ctrl_info *ctrl)
+{
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_remove_template_pages_req *req = embedded_payload(wrb);
+ int status;
+
+ mutex_lock(&ctrl->mbox_lock);
+
+ memset(wrb, 0, sizeof(*wrb));
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_REMOVE_TEMPLATE_HEADER_BUFFERS,
+ sizeof(*req));
+
+ req->type = BEISCSI_TEMPLATE_HDR_TYPE_ISCSI;
+
+ status = be_mbox_notify(ctrl);
+ mutex_unlock(&ctrl->mbox_lock);
return status;
}
@@ -827,15 +1170,15 @@ int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl,
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_post_sgl_pages_req *req = embedded_payload(wrb);
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
int status;
unsigned int curr_pages;
- u32 internal_page_offset = 0;
u32 temp_num_pages = num_pages;
if (num_pages == 0xff)
num_pages = 1;
- spin_lock(&ctrl->mbox_lock);
+ mutex_lock(&ctrl->mbox_lock);
do {
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -848,7 +1191,6 @@ int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl,
req->page_offset = page_offset;
be_cmd_page_addrs_prepare(req->pages, req->num_pages, q_mem);
q_mem->dma = q_mem->dma + (req->num_pages * PAGE_SIZE);
- internal_page_offset += req->num_pages;
page_offset += req->num_pages;
num_pages -= req->num_pages;
@@ -857,14 +1199,666 @@ int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl,
status = be_mbox_notify(ctrl);
if (status) {
- SE_DEBUG(DBG_LVL_1,
- "FW CMD to map iscsi frags failed.\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BC_%d : FW CMD to map iscsi frags failed.\n");
+
goto error;
}
} while (num_pages > 0);
error:
- spin_unlock(&ctrl->mbox_lock);
+ mutex_unlock(&ctrl->mbox_lock);
if (status != 0)
beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
return status;
}
+
+/**
+ * be_cmd_set_vlan()- Configure VLAN paramters on the adapter
+ * @phba: device priv structure instance
+ * @vlan_tag: TAG to be set
+ *
+ * Set the VLAN_TAG for the adapter or Disable VLAN on adapter
+ *
+ * returns
+ * TAG for the MBX Cmd
+ * **/
+int be_cmd_set_vlan(struct beiscsi_hba *phba,
+ uint16_t vlan_tag)
+{
+ unsigned int tag;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_set_vlan_req *req;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ if (mutex_lock_interruptible(&ctrl->mbox_lock))
+ return 0;
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
+ }
+
+ req = embedded_payload(wrb);
+ be_wrb_hdr_prepare(wrb, sizeof(*wrb), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_SET_VLAN,
+ sizeof(*req));
+
+ req->interface_hndl = phba->interface_handle;
+ req->vlan_priority = vlan_tag;
+
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
+
+ return tag;
+}
+
+int beiscsi_check_supported_fw(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba)
+{
+ struct be_dma_mem nonemb_cmd;
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mgmt_controller_attributes *req;
+ struct be_sge *sge = nonembedded_sgl(wrb);
+ int status = 0;
+
+ nonemb_cmd.va = dma_alloc_coherent(&ctrl->pdev->dev,
+ sizeof(struct be_mgmt_controller_attributes),
+ &nonemb_cmd.dma, GFP_KERNEL);
+ if (nonemb_cmd.va == NULL) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d : dma_alloc_coherent failed in %s\n",
+ __func__);
+ return -ENOMEM;
+ }
+ nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes);
+ req = nonemb_cmd.va;
+ memset(req, 0, sizeof(*req));
+ mutex_lock(&ctrl->mbox_lock);
+ memset(wrb, 0, sizeof(*wrb));
+ be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req));
+ sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
+ sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
+ sge->len = cpu_to_le32(nonemb_cmd.size);
+ status = be_mbox_notify(ctrl);
+ if (!status) {
+ struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va;
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BG_%d : Firmware Version of CMD : %s\n"
+ "Firmware Version is : %s\n"
+ "Developer Build, not performing version check...\n",
+ resp->params.hba_attribs
+ .flashrom_version_string,
+ resp->params.hba_attribs.
+ firmware_version_string);
+
+ phba->fw_config.iscsi_features =
+ resp->params.hba_attribs.iscsi_features;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : phba->fw_config.iscsi_features = %d\n",
+ phba->fw_config.iscsi_features);
+ memcpy(phba->fw_ver_str, resp->params.hba_attribs.
+ firmware_version_string, BEISCSI_VER_STRLEN);
+ } else
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d : Failed in beiscsi_check_supported_fw\n");
+ mutex_unlock(&ctrl->mbox_lock);
+ if (nonemb_cmd.va)
+ dma_free_coherent(&ctrl->pdev->dev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+
+ return status;
+}
+
+/**
+ * beiscsi_get_fw_config()- Get the FW config for the function
+ * @ctrl: ptr to Ctrl Info
+ * @phba: ptr to the dev priv structure
+ *
+ * Get the FW config and resources available for the function.
+ * The resources are created based on the count received here.
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero Value
+ **/
+int beiscsi_get_fw_config(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba)
+{
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_fw_cfg *pfw_cfg = embedded_payload(wrb);
+ uint32_t cid_count, icd_count;
+ int status = -EINVAL;
+ uint8_t ulp_num = 0;
+
+ mutex_lock(&ctrl->mbox_lock);
+ memset(wrb, 0, sizeof(*wrb));
+ be_wrb_hdr_prepare(wrb, sizeof(*pfw_cfg), true, 0);
+
+ be_cmd_hdr_prepare(&pfw_cfg->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_QUERY_FIRMWARE_CONFIG,
+ EMBED_MBX_MAX_PAYLOAD_SIZE);
+
+ if (be_mbox_notify(ctrl)) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d : Failed in beiscsi_get_fw_config\n");
+ goto fail_init;
+ }
+
+ /* FW response formats depend on port id */
+ phba->fw_config.phys_port = pfw_cfg->phys_port;
+ if (phba->fw_config.phys_port >= BEISCSI_PHYS_PORT_MAX) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d : invalid physical port id %d\n",
+ phba->fw_config.phys_port);
+ goto fail_init;
+ }
+
+ /* populate and check FW config against min and max values */
+ if (!is_chip_be2_be3r(phba)) {
+ phba->fw_config.eqid_count = pfw_cfg->eqid_count;
+ phba->fw_config.cqid_count = pfw_cfg->cqid_count;
+ if (phba->fw_config.eqid_count == 0 ||
+ phba->fw_config.eqid_count > 2048) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d : invalid EQ count %d\n",
+ phba->fw_config.eqid_count);
+ goto fail_init;
+ }
+ if (phba->fw_config.cqid_count == 0 ||
+ phba->fw_config.cqid_count > 4096) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d : invalid CQ count %d\n",
+ phba->fw_config.cqid_count);
+ goto fail_init;
+ }
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BG_%d : EQ_Count : %d CQ_Count : %d\n",
+ phba->fw_config.eqid_count,
+ phba->fw_config.cqid_count);
+ }
+
+ /**
+ * Check on which all ULP iSCSI Protocol is loaded.
+ * Set the Bit for those ULP. This set flag is used
+ * at all places in the code to check on which ULP
+ * iSCSi Protocol is loaded
+ **/
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (pfw_cfg->ulp[ulp_num].ulp_mode &
+ BEISCSI_ULP_ISCSI_INI_MODE) {
+ set_bit(ulp_num, &phba->fw_config.ulp_supported);
+
+ /* Get the CID, ICD and Chain count for each ULP */
+ phba->fw_config.iscsi_cid_start[ulp_num] =
+ pfw_cfg->ulp[ulp_num].sq_base;
+ phba->fw_config.iscsi_cid_count[ulp_num] =
+ pfw_cfg->ulp[ulp_num].sq_count;
+
+ phba->fw_config.iscsi_icd_start[ulp_num] =
+ pfw_cfg->ulp[ulp_num].icd_base;
+ phba->fw_config.iscsi_icd_count[ulp_num] =
+ pfw_cfg->ulp[ulp_num].icd_count;
+
+ phba->fw_config.iscsi_chain_start[ulp_num] =
+ pfw_cfg->chain_icd[ulp_num].chain_base;
+ phba->fw_config.iscsi_chain_count[ulp_num] =
+ pfw_cfg->chain_icd[ulp_num].chain_count;
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BG_%d : Function loaded on ULP : %d\n"
+ "\tiscsi_cid_count : %d\n"
+ "\tiscsi_cid_start : %d\n"
+ "\t iscsi_icd_count : %d\n"
+ "\t iscsi_icd_start : %d\n",
+ ulp_num,
+ phba->fw_config.
+ iscsi_cid_count[ulp_num],
+ phba->fw_config.
+ iscsi_cid_start[ulp_num],
+ phba->fw_config.
+ iscsi_icd_count[ulp_num],
+ phba->fw_config.
+ iscsi_icd_start[ulp_num]);
+ }
+ }
+
+ if (phba->fw_config.ulp_supported == 0) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d : iSCSI initiator mode not set: ULP0 %x ULP1 %x\n",
+ pfw_cfg->ulp[BEISCSI_ULP0].ulp_mode,
+ pfw_cfg->ulp[BEISCSI_ULP1].ulp_mode);
+ goto fail_init;
+ }
+
+ /**
+ * ICD is shared among ULPs. Use icd_count of any one loaded ULP
+ **/
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++)
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported))
+ break;
+ icd_count = phba->fw_config.iscsi_icd_count[ulp_num];
+ if (icd_count == 0 || icd_count > 65536) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d: invalid ICD count %d\n", icd_count);
+ goto fail_init;
+ }
+
+ cid_count = BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP0) +
+ BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP1);
+ if (cid_count == 0 || cid_count > 4096) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BG_%d: invalid CID count %d\n", cid_count);
+ goto fail_init;
+ }
+
+ /**
+ * Check FW is dual ULP aware i.e. can handle either
+ * of the protocols.
+ */
+ phba->fw_config.dual_ulp_aware = (pfw_cfg->function_mode &
+ BEISCSI_FUNC_DUA_MODE);
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BG_%d : DUA Mode : 0x%x\n",
+ phba->fw_config.dual_ulp_aware);
+
+ /* all set, continue using this FW config */
+ status = 0;
+fail_init:
+ mutex_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
+/**
+ * beiscsi_get_port_name()- Get port name for the function
+ * @ctrl: ptr to Ctrl Info
+ * @phba: ptr to the dev priv structure
+ *
+ * Get the alphanumeric character for port
+ *
+ **/
+int beiscsi_get_port_name(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba)
+{
+ int ret = 0;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_get_port_name *ioctl;
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ memset(wrb, 0, sizeof(*wrb));
+ ioctl = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0);
+ be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_PORT_NAME,
+ EMBED_MBX_MAX_PAYLOAD_SIZE);
+ ret = be_mbox_notify(ctrl);
+ phba->port_name = 0;
+ if (!ret) {
+ phba->port_name = ioctl->p.resp.port_names >>
+ (phba->fw_config.phys_port * 8) & 0xff;
+ } else {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BG_%d : GET_PORT_NAME ret 0x%x status 0x%x\n",
+ ret, ioctl->h.resp_hdr.status);
+ }
+
+ if (phba->port_name == 0)
+ phba->port_name = '?';
+
+ mutex_unlock(&ctrl->mbox_lock);
+ return ret;
+}
+
+int beiscsi_set_host_data(struct beiscsi_hba *phba)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_cmd_set_host_data *ioctl;
+ struct be_mcc_wrb *wrb;
+ int ret = 0;
+
+ if (is_chip_be2_be3r(phba))
+ return ret;
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ memset(wrb, 0, sizeof(*wrb));
+ ioctl = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0);
+ be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_HOST_DATA,
+ EMBED_MBX_MAX_PAYLOAD_SIZE);
+ ioctl->param.req.param_id = BE_CMD_SET_HOST_PARAM_ID;
+ ioctl->param.req.param_len =
+ snprintf((char *)ioctl->param.req.param_data,
+ sizeof(ioctl->param.req.param_data),
+ "Linux iSCSI v%s", BUILD_STR);
+ ioctl->param.req.param_len = ALIGN(ioctl->param.req.param_len + 1, 4);
+ if (ioctl->param.req.param_len > BE_CMD_MAX_DRV_VERSION)
+ ioctl->param.req.param_len = BE_CMD_MAX_DRV_VERSION;
+ ret = be_mbox_notify(ctrl);
+ if (!ret) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BG_%d : HBA set host driver version\n");
+ } else {
+ /**
+ * Check "MCC_STATUS_INVALID_LENGTH" for SKH.
+ * Older FW versions return this error.
+ */
+ if (ret == MCC_STATUS_ILLEGAL_REQUEST ||
+ ret == MCC_STATUS_INVALID_LENGTH)
+ __beiscsi_log(phba, KERN_INFO,
+ "BG_%d : HBA failed to set host driver version\n");
+ }
+
+ mutex_unlock(&ctrl->mbox_lock);
+ return ret;
+}
+
+int beiscsi_set_uer_feature(struct beiscsi_hba *phba)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_cmd_set_features *ioctl;
+ struct be_mcc_wrb *wrb;
+ int ret = 0;
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ memset(wrb, 0, sizeof(*wrb));
+ ioctl = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0);
+ be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_FEATURES,
+ EMBED_MBX_MAX_PAYLOAD_SIZE);
+ ioctl->feature = BE_CMD_SET_FEATURE_UER;
+ ioctl->param_len = sizeof(ioctl->param.req);
+ ioctl->param.req.uer = BE_CMD_UER_SUPP_BIT;
+ ret = be_mbox_notify(ctrl);
+ if (!ret) {
+ phba->ue2rp = ioctl->param.resp.ue2rp;
+ set_bit(BEISCSI_HBA_UER_SUPP, &phba->state);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BG_%d : HBA error recovery supported\n");
+ } else {
+ /**
+ * Check "MCC_STATUS_INVALID_LENGTH" for SKH.
+ * Older FW versions return this error.
+ */
+ if (ret == MCC_STATUS_ILLEGAL_REQUEST ||
+ ret == MCC_STATUS_INVALID_LENGTH)
+ __beiscsi_log(phba, KERN_INFO,
+ "BG_%d : HBA error recovery not supported\n");
+ }
+
+ mutex_unlock(&ctrl->mbox_lock);
+ return ret;
+}
+
+static u32 beiscsi_get_post_stage(struct beiscsi_hba *phba)
+{
+ u32 sem;
+
+ if (is_chip_be2_be3r(phba))
+ sem = ioread32(phba->csr_va + SLIPORT_SEMAPHORE_OFFSET_BEx);
+ else
+ pci_read_config_dword(phba->pcidev,
+ SLIPORT_SEMAPHORE_OFFSET_SH, &sem);
+ return sem;
+}
+
+int beiscsi_check_fw_rdy(struct beiscsi_hba *phba)
+{
+ u32 loop, post, rdy = 0;
+
+ loop = 1000;
+ while (loop--) {
+ post = beiscsi_get_post_stage(phba);
+ if (post & POST_ERROR_BIT)
+ break;
+ if ((post & POST_STAGE_MASK) == POST_STAGE_ARMFW_RDY) {
+ rdy = 1;
+ break;
+ }
+ msleep(60);
+ }
+
+ if (!rdy) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : FW not ready 0x%x\n", post);
+ }
+
+ return rdy;
+}
+
+int beiscsi_cmd_function_reset(struct beiscsi_hba *phba)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_post_sgl_pages_req *req;
+ int status;
+
+ mutex_lock(&ctrl->mbox_lock);
+
+ req = embedded_payload(wrb);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_FUNCTION_RESET, sizeof(*req));
+ status = be_mbox_notify(ctrl);
+
+ mutex_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
+int beiscsi_cmd_special_wrb(struct be_ctrl_info *ctrl, u32 load)
+{
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ u8 *endian_check;
+ int status;
+
+ mutex_lock(&ctrl->mbox_lock);
+ memset(wrb, 0, sizeof(*wrb));
+
+ endian_check = (u8 *) wrb;
+ if (load) {
+ /* to start communicating */
+ *endian_check++ = 0xFF;
+ *endian_check++ = 0x12;
+ *endian_check++ = 0x34;
+ *endian_check++ = 0xFF;
+ *endian_check++ = 0xFF;
+ *endian_check++ = 0x56;
+ *endian_check++ = 0x78;
+ *endian_check++ = 0xFF;
+ } else {
+ /* to stop communicating */
+ *endian_check++ = 0xFF;
+ *endian_check++ = 0xAA;
+ *endian_check++ = 0xBB;
+ *endian_check++ = 0xFF;
+ *endian_check++ = 0xFF;
+ *endian_check++ = 0xCC;
+ *endian_check++ = 0xDD;
+ *endian_check = 0xFF;
+ }
+ be_dws_cpu_to_le(wrb, sizeof(*wrb));
+
+ status = be_mbox_notify(ctrl);
+ if (status)
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BC_%d : special WRB message failed\n");
+ mutex_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
+int beiscsi_init_sliport(struct beiscsi_hba *phba)
+{
+ int status;
+
+ /* check POST stage before talking to FW */
+ status = beiscsi_check_fw_rdy(phba);
+ if (!status)
+ return -EIO;
+
+ /* clear all error states after checking FW rdy */
+ phba->state &= ~BEISCSI_HBA_IN_ERR;
+
+ /* check again UER support */
+ phba->state &= ~BEISCSI_HBA_UER_SUPP;
+
+ /*
+ * SLI COMMON_FUNCTION_RESET completion is indicated by BMBX RDY bit.
+ * It should clean up any stale info in FW for this fn.
+ */
+ status = beiscsi_cmd_function_reset(phba);
+ if (status) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BC_%d : SLI Function Reset failed\n");
+ return status;
+ }
+
+ /* indicate driver is loading */
+ return beiscsi_cmd_special_wrb(&phba->ctrl, 1);
+}
+
+/**
+ * beiscsi_cmd_iscsi_cleanup()- Inform FW to cleanup EP data structures.
+ * @phba: pointer to dev priv structure
+ * @ulp: ULP number.
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero Value
+ **/
+int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct iscsi_cleanup_req_v1 *req_v1;
+ struct iscsi_cleanup_req *req;
+ u16 hdr_ring_id, data_ring_id;
+ struct be_mcc_wrb *wrb;
+ int status;
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = wrb_from_mbox(&ctrl->mbox_mem);
+
+ hdr_ring_id = HWI_GET_DEF_HDRQ_ID(phba, ulp);
+ data_ring_id = HWI_GET_DEF_BUFQ_ID(phba, ulp);
+ if (is_chip_be2_be3r(phba)) {
+ req = embedded_payload(wrb);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req));
+ req->chute = (1 << ulp);
+ /* BE2/BE3 FW creates 8-bit ring id */
+ req->hdr_ring_id = hdr_ring_id;
+ req->data_ring_id = data_ring_id;
+ } else {
+ req_v1 = embedded_payload(wrb);
+ be_wrb_hdr_prepare(wrb, sizeof(*req_v1), true, 0);
+ be_cmd_hdr_prepare(&req_v1->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_CLEANUP,
+ sizeof(*req_v1));
+ req_v1->hdr.version = 1;
+ req_v1->chute = (1 << ulp);
+ req_v1->hdr_ring_id = cpu_to_le16(hdr_ring_id);
+ req_v1->data_ring_id = cpu_to_le16(data_ring_id);
+ }
+
+ status = be_mbox_notify(ctrl);
+ if (status)
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT,
+ "BG_%d : %s failed %d\n", __func__, ulp);
+ mutex_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
+/*
+ * beiscsi_detect_ue()- Detect Unrecoverable Error on adapter
+ * @phba: Driver priv structure
+ *
+ * Read registers linked to UE and check for the UE status
+ **/
+int beiscsi_detect_ue(struct beiscsi_hba *phba)
+{
+ uint32_t ue_mask_hi = 0, ue_mask_lo = 0;
+ uint32_t ue_hi = 0, ue_lo = 0;
+ uint8_t i = 0;
+ int ret = 0;
+
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_LOW, &ue_lo);
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_MASK_LOW,
+ &ue_mask_lo);
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_HIGH,
+ &ue_hi);
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_MASK_HI,
+ &ue_mask_hi);
+
+ ue_lo = (ue_lo & ~ue_mask_lo);
+ ue_hi = (ue_hi & ~ue_mask_hi);
+
+
+ if (ue_lo || ue_hi) {
+ set_bit(BEISCSI_HBA_IN_UE, &phba->state);
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : HBA error detected\n");
+ ret = 1;
+ }
+
+ if (ue_lo) {
+ for (i = 0; ue_lo; ue_lo >>= 1, i++) {
+ if (ue_lo & 1)
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : UE_LOW %s bit set\n",
+ desc_ue_status_low[i]);
+ }
+ }
+
+ if (ue_hi) {
+ for (i = 0; ue_hi; ue_hi >>= 1, i++) {
+ if (ue_hi & 1)
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : UE_HIGH %s bit set\n",
+ desc_ue_status_hi[i]);
+ }
+ }
+ return ret;
+}
+
+/*
+ * beiscsi_detect_tpe()- Detect Transient Parity Error on adapter
+ * @phba: Driver priv structure
+ *
+ * Read SLIPORT SEMAPHORE register to check for UER
+ *
+ **/
+int beiscsi_detect_tpe(struct beiscsi_hba *phba)
+{
+ u32 post, status;
+ int ret = 0;
+
+ post = beiscsi_get_post_stage(phba);
+ status = post & POST_STAGE_MASK;
+ if ((status & POST_ERR_RECOVERY_CODE_MASK) ==
+ POST_STAGE_RECOVERABLE_ERR) {
+ set_bit(BEISCSI_HBA_IN_TPE, &phba->state);
+ __beiscsi_log(phba, KERN_INFO,
+ "BC_%d : HBA error recoverable: 0x%x\n", post);
+ ret = 1;
+ } else {
+ __beiscsi_log(phba, KERN_INFO,
+ "BC_%d : HBA in UE: 0x%x\n", post);
+ }
+
+ return ret;
+}
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index 497eb29e5c9e..5f9f0b18ddf3 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -1,18 +1,10 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2017 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
*
* Contact Information:
- * linux-drivers@emulex.com
- *
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
+ * linux-drivers@broadcom.com
*/
#ifndef BEISCSI_CMDS_H
@@ -23,23 +15,30 @@
* firmware in the BE. These requests are communicated to the processor
* using Work Request Blocks (WRBs) submitted to the MCC-WRB ring or via one
* WRB inside a MAILBOX.
- * The commands are serviced by the ARM processor in the BladeEngine's MPU.
+ * The commands are serviced by the ARM processor in the OneConnect's MPU.
*/
struct be_sge {
- u32 pa_lo;
- u32 pa_hi;
- u32 len;
+ __le32 pa_lo;
+ __le32 pa_hi;
+ __le32 len;
};
-#define MCC_WRB_SGE_CNT_SHIFT 3 /* bits 3 - 7 of dword 0 */
-#define MCC_WRB_SGE_CNT_MASK 0x1F /* bits 3 - 7 of dword 0 */
struct be_mcc_wrb {
- u32 embedded; /* dword 0 */
+ u32 emb_sgecnt_special; /* dword 0 */
+ /* bits 0 - embedded */
+ /* bits 1 - 2 reserved */
+ /* bits 3 - 7 sge count */
+ /* bits 8 - 23 reserved */
+ /* bits 24 - 31 special */
+#define MCC_WRB_EMBEDDED_MASK 1
+#define MCC_WRB_SGE_CNT_SHIFT 3
+#define MCC_WRB_SGE_CNT_MASK 0x1F
u32 payload_length; /* dword 1 */
u32 tag0; /* dword 2 */
u32 tag1; /* dword 3 */
u32 rsvd; /* dword 4 */
union {
+#define EMBED_MBX_MAX_PAYLOAD_SIZE 220
u8 embedded_payload[236]; /* used by embedded cmds */
struct be_sge sgl[19]; /* used by non-embedded cmds */
} payload;
@@ -52,11 +51,28 @@ struct be_mcc_wrb {
/* Completion Status */
#define MCC_STATUS_SUCCESS 0x0
-
-#define CQE_STATUS_COMPL_MASK 0xFFFF
-#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */
-#define CQE_STATUS_EXTD_MASK 0xFFFF
-#define CQE_STATUS_EXTD_SHIFT 16 /* bits 0 - 15 */
+#define MCC_STATUS_FAILED 0x1
+#define MCC_STATUS_ILLEGAL_REQUEST 0x2
+#define MCC_STATUS_ILLEGAL_FIELD 0x3
+#define MCC_STATUS_INSUFFICIENT_BUFFER 0x4
+#define MCC_STATUS_INVALID_LENGTH 0x74
+
+#define CQE_STATUS_COMPL_MASK 0xFFFF
+#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */
+#define CQE_STATUS_EXTD_MASK 0xFFFF
+#define CQE_STATUS_EXTD_SHIFT 16 /* bits 31 - 16 */
+#define CQE_STATUS_ADDL_MASK 0xFF00
+#define CQE_STATUS_ADDL_SHIFT 8
+#define CQE_STATUS_MASK 0xFF
+#define CQE_STATUS_WRB_MASK 0xFF0000
+#define CQE_STATUS_WRB_SHIFT 16
+
+#define BEISCSI_HOST_MBX_TIMEOUT (110 * 1000)
+#define BEISCSI_FW_MBX_TIMEOUT 100
+
+/* MBOX Command VER */
+#define MBX_CMD_VER1 0x01
+#define MBX_CMD_VER2 0x02
struct be_mcc_compl {
u32 status; /* dword 0 */
@@ -80,21 +96,30 @@ struct be_mcc_compl {
#define MPU_MAILBOX_DB_RDY_MASK 0x1 /* bit 0 */
#define MPU_MAILBOX_DB_HI_MASK 0x2 /* bit 1 */
-/********** MPU semphore ******************/
-#define MPU_EP_SEMAPHORE_OFFSET 0xac
-#define EP_SEMAPHORE_POST_STAGE_MASK 0x0000FFFF
-#define EP_SEMAPHORE_POST_ERR_MASK 0x1
-#define EP_SEMAPHORE_POST_ERR_SHIFT 31
+/********** MPU semphore: used for SH & BE ******************/
+#define SLIPORT_SOFTRESET_OFFSET 0x5c /* CSR BAR offset */
+#define SLIPORT_SEMAPHORE_OFFSET_BEx 0xac /* CSR BAR offset */
+#define SLIPORT_SEMAPHORE_OFFSET_SH 0x94 /* PCI-CFG offset */
+#define POST_STAGE_MASK 0x0000FFFF
+#define POST_ERROR_BIT 0x80000000
+#define POST_ERR_RECOVERY_CODE_MASK 0xF000
+
+/* Soft Reset register masks */
+#define SLIPORT_SOFTRESET_SR_MASK 0x00000080 /* SR bit */
+
+/* MPU semphore POST stage values */
+#define POST_STAGE_AWAITING_HOST_RDY 0x1 /* FW awaiting goahead from host */
+#define POST_STAGE_HOST_RDY 0x2 /* Host has given go-ahed to FW */
+#define POST_STAGE_BE_RESET 0x3 /* Host wants to reset chip */
+#define POST_STAGE_ARMFW_RDY 0xC000 /* FW is done with POST */
+#define POST_STAGE_RECOVERABLE_ERR 0xE000 /* Recoverable err detected */
/********** MCC door bell ************/
#define DB_MCCQ_OFFSET 0x140
-#define DB_MCCQ_RING_ID_MASK 0x7FF /* bits 0 - 10 */
+#define DB_MCCQ_RING_ID_MASK 0xFFFF /* bits 0 - 15 */
/* Number of entries posted */
#define DB_MCCQ_NUM_POSTED_SHIFT 16 /* bits 16 - 29 */
-/* MPU semphore POST stage values */
-#define POST_STAGE_ARMFW_RDY 0xc000 /* FW is done with POST */
-
/**
* When the async bit of mcc_compl is set, the last 4 bytes of
* mcc_compl is interpreted as follows:
@@ -102,13 +127,30 @@ struct be_mcc_compl {
#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8 /* bits 8 - 15 */
#define ASYNC_TRAILER_EVENT_CODE_MASK 0xFF
#define ASYNC_EVENT_CODE_LINK_STATE 0x1
+#define ASYNC_EVENT_CODE_ISCSI 0x4
+#define ASYNC_EVENT_CODE_SLI 0x11
+
+#define ASYNC_TRAILER_EVENT_TYPE_SHIFT 16 /* bits 16 - 23 */
+#define ASYNC_TRAILER_EVENT_TYPE_MASK 0xFF
+
+/* iSCSI events */
+#define ASYNC_EVENT_NEW_ISCSI_TGT_DISC 0x4
+#define ASYNC_EVENT_NEW_ISCSI_CONN 0x5
+#define ASYNC_EVENT_NEW_TCP_CONN 0x7
+
+/* SLI events */
+#define ASYNC_SLI_EVENT_TYPE_MISCONFIGURED 0x9
+#define ASYNC_SLI_LINK_EFFECT_VALID(le) (le & 0x80)
+#define ASYNC_SLI_LINK_EFFECT_SEV(le) ((le >> 1) & 0x03)
+#define ASYNC_SLI_LINK_EFFECT_STATE(le) (le & 0x01)
+
struct be_async_event_trailer {
u32 code;
};
enum {
ASYNC_EVENT_LINK_DOWN = 0x0,
- ASYNC_EVENT_LINK_UP = 0x1
+ ASYNC_EVENT_LINK_UP = 0x1,
};
/**
@@ -118,13 +160,39 @@ enum {
struct be_async_event_link_state {
u8 physical_port;
u8 port_link_status;
+/**
+ * ASYNC_EVENT_LINK_DOWN 0x0
+ * ASYNC_EVENT_LINK_UP 0x1
+ * ASYNC_EVENT_LINK_LOGICAL_DOWN 0x2
+ * ASYNC_EVENT_LINK_LOGICAL_UP 0x3
+ */
+#define BE_ASYNC_LINK_UP_MASK 0x01
u8 port_duplex;
u8 port_speed;
+/* BE2ISCSI_LINK_SPEED_ZERO 0x00 - no link */
+#define BE2ISCSI_LINK_SPEED_10MBPS 0x01
+#define BE2ISCSI_LINK_SPEED_100MBPS 0x02
+#define BE2ISCSI_LINK_SPEED_1GBPS 0x03
+#define BE2ISCSI_LINK_SPEED_10GBPS 0x04
+#define BE2ISCSI_LINK_SPEED_25GBPS 0x06
+#define BE2ISCSI_LINK_SPEED_40GBPS 0x07
u8 port_fault;
- u8 rsvd0[7];
+ u8 event_reason;
+ u16 qos_link_speed;
+ u32 event_tag;
struct be_async_event_trailer trailer;
} __packed;
+/**
+ * When async-trailer is SLI event, mcc_compl is interpreted as
+ */
+struct be_async_event_sli {
+ u32 event_data1;
+ u32 event_data2;
+ u32 reserved;
+ u32 trailer;
+} __packed;
+
struct be_mcc_mailbox {
struct be_mcc_wrb wrb;
struct be_mcc_compl compl;
@@ -144,6 +212,9 @@ struct be_mcc_mailbox {
#define OPCODE_COMMON_CQ_CREATE 12
#define OPCODE_COMMON_EQ_CREATE 13
#define OPCODE_COMMON_MCC_CREATE 21
+#define OPCODE_COMMON_MCC_CREATE_EXT 90
+#define OPCODE_COMMON_ADD_TEMPLATE_HEADER_BUFFERS 24
+#define OPCODE_COMMON_REMOVE_TEMPLATE_HEADER_BUFFERS 25
#define OPCODE_COMMON_GET_CNTL_ATTRIBUTES 32
#define OPCODE_COMMON_GET_FW_VERSION 35
#define OPCODE_COMMON_MODIFY_EQ_DELAY 41
@@ -153,6 +224,9 @@ struct be_mcc_mailbox {
#define OPCODE_COMMON_EQ_DESTROY 55
#define OPCODE_COMMON_QUERY_FIRMWARE_CONFIG 58
#define OPCODE_COMMON_FUNCTION_RESET 61
+#define OPCODE_COMMON_GET_PORT_NAME 77
+#define OPCODE_COMMON_SET_HOST_DATA 93
+#define OPCODE_COMMON_SET_FEATURES 191
/**
* LIST of opcodes that are common between Initiator and Target
@@ -163,7 +237,8 @@ struct be_mcc_mailbox {
#define OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES 3
#define OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG 7
#define OPCODE_COMMON_ISCSI_NTWK_SET_VLAN 14
-#define OPCODE_COMMON_ISCSI_NTWK_CONFIGURE_STATELESS_IP_ADDR 17
+#define OPCODE_COMMON_ISCSI_NTWK_CONFIG_STATELESS_IP_ADDR 17
+#define OPCODE_COMMON_ISCSI_NTWK_REL_STATELESS_IP_ADDR 18
#define OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR 21
#define OPCODE_COMMON_ISCSI_NTWK_GET_DEFAULT_GATEWAY 22
#define OPCODE_COMMON_ISCSI_NTWK_MODIFY_DEFAULT_GATEWAY 23
@@ -182,7 +257,8 @@ struct be_cmd_req_hdr {
u8 domain; /* dword 0 */
u32 timeout; /* dword 1 */
u32 request_length; /* dword 2 */
- u32 rsvd0; /* dword 3 */
+ u8 version; /* dword 3 */
+ u8 rsvd0[3]; /* dword 3 */
};
struct be_cmd_resp_hdr {
@@ -197,6 +273,10 @@ struct phys_addr {
u32 hi;
};
+struct virt_addr {
+ u32 lo;
+ u32 hi;
+};
/**************************
* BE Command definitions *
**************************/
@@ -244,6 +324,12 @@ struct be_cmd_resp_eq_create {
u16 rsvd0; /* sword */
} __packed;
+struct be_set_eqd {
+ u32 eq_id;
+ u32 phase;
+ u32 delay_multiplier;
+} __packed;
+
struct mgmt_chap_format {
u32 flags;
u8 intr_chap_name[256];
@@ -262,6 +348,17 @@ struct mgmt_auth_method_format {
struct mgmt_chap_format chap;
} __packed;
+struct be_cmd_req_logout_fw_sess {
+ struct be_cmd_req_hdr hdr; /* dw[4] */
+ uint32_t session_handle;
+} __packed;
+
+struct be_cmd_resp_logout_fw_sess {
+ struct be_cmd_resp_hdr hdr; /* dw[4] */
+ uint32_t session_status;
+#define BE_SESS_STATUS_CLOSE 0x20
+} __packed;
+
struct mgmt_conn_login_options {
u8 flags;
u8 header_digest;
@@ -274,15 +371,23 @@ struct mgmt_conn_login_options {
struct mgmt_auth_method_format auth_data;
} __packed;
-struct ip_address_format {
+struct ip_addr_format {
u16 size_of_structure;
u8 reserved;
u8 ip_type;
- u8 ip_address[16];
+#define BEISCSI_IP_TYPE_V4 0x1
+#define BEISCSI_IP_TYPE_STATIC_V4 0x3
+#define BEISCSI_IP_TYPE_DHCP_V4 0x5
+/* type v4 values < type v6 values */
+#define BEISCSI_IP_TYPE_V6 0x10
+#define BEISCSI_IP_TYPE_ROUTABLE_V6 0x30
+#define BEISCSI_IP_TYPE_LINK_LOCAL_V6 0x50
+#define BEISCSI_IP_TYPE_AUTO_V6 0x90
+ u8 addr[16];
u32 rsvd0;
} __packed;
-struct mgmt_conn_info {
+struct mgmt_conn_info {
u32 connection_handle;
u32 connection_status;
u16 src_port;
@@ -290,9 +395,9 @@ struct mgmt_conn_info {
u16 dest_port_redirected;
u16 cid;
u32 estimated_throughput;
- struct ip_address_format src_ipaddr;
- struct ip_address_format dest_ipaddr;
- struct ip_address_format dest_ipaddr_redirected;
+ struct ip_addr_format src_ipaddr;
+ struct ip_addr_format dest_ipaddr;
+ struct ip_addr_format dest_ipaddr_redirected;
struct mgmt_conn_login_options negotiated_login_options;
} __packed;
@@ -322,43 +427,143 @@ struct mgmt_session_info {
struct mgmt_conn_info conn_list[1];
} __packed;
-struct be_cmd_req_get_session {
+struct be_cmd_get_session_req {
struct be_cmd_req_hdr hdr;
u32 session_handle;
} __packed;
-struct be_cmd_resp_get_session {
+struct be_cmd_get_session_resp {
struct be_cmd_resp_hdr hdr;
struct mgmt_session_info session_info;
} __packed;
struct mac_addr {
- u16 size_of_struct;
+ u16 size_of_structure;
u8 addr[ETH_ALEN];
} __packed;
-struct be_cmd_req_get_boot_target {
+struct be_cmd_get_boot_target_req {
struct be_cmd_req_hdr hdr;
} __packed;
-struct be_cmd_resp_get_boot_target {
+struct be_cmd_get_boot_target_resp {
struct be_cmd_resp_hdr hdr;
- u32 boot_session_count;
- int boot_session_handle;
+ u32 boot_session_count;
+ u32 boot_session_handle;
+/**
+ * FW returns 0xffffffff if it couldn't establish connection with
+ * configured boot target.
+ */
+#define BE_BOOT_INVALID_SHANDLE 0xffffffff
};
-struct be_cmd_req_mac_query {
+struct be_cmd_reopen_session_req {
+ struct be_cmd_req_hdr hdr;
+#define BE_REOPEN_ALL_SESSIONS 0x00
+#define BE_REOPEN_BOOT_SESSIONS 0x01
+#define BE_REOPEN_A_SESSION 0x02
+ u16 reopen_type;
+ u16 rsvd;
+ u32 session_handle;
+} __packed;
+
+struct be_cmd_reopen_session_resp {
+ struct be_cmd_resp_hdr hdr;
+ u32 rsvd;
+ u32 session_handle;
+} __packed;
+
+
+struct be_cmd_mac_query_req {
struct be_cmd_req_hdr hdr;
u8 type;
u8 permanent;
u16 if_id;
} __packed;
-struct be_cmd_resp_mac_query {
+struct be_cmd_get_mac_resp {
struct be_cmd_resp_hdr hdr;
struct mac_addr mac;
};
+struct be_ip_addr_subnet_format {
+ u16 size_of_structure;
+ u8 ip_type;
+ u8 ipv6_prefix_length;
+ u8 addr[16];
+ u8 subnet_mask[16];
+ u32 rsvd0;
+} __packed;
+
+struct be_cmd_get_if_info_req {
+ struct be_cmd_req_hdr hdr;
+ u32 interface_hndl;
+ u32 ip_type;
+} __packed;
+
+struct be_cmd_get_if_info_resp {
+ struct be_cmd_req_hdr hdr;
+ u32 interface_hndl;
+ u32 vlan_priority;
+ u32 ip_addr_count;
+ u32 dhcp_state;
+ struct be_ip_addr_subnet_format ip_addr;
+} __packed;
+
+struct be_ip_addr_record {
+ u32 action;
+ u32 interface_hndl;
+ struct be_ip_addr_subnet_format ip_addr;
+ u32 status;
+} __packed;
+
+struct be_ip_addr_record_params {
+ u32 record_entry_count;
+ struct be_ip_addr_record ip_record;
+} __packed;
+
+struct be_cmd_set_ip_addr_req {
+ struct be_cmd_req_hdr hdr;
+ struct be_ip_addr_record_params ip_params;
+} __packed;
+
+
+struct be_cmd_set_dhcp_req {
+ struct be_cmd_req_hdr hdr;
+ u32 interface_hndl;
+ u32 ip_type;
+ u32 flags;
+ u32 retry_count;
+} __packed;
+
+struct be_cmd_rel_dhcp_req {
+ struct be_cmd_req_hdr hdr;
+ u32 interface_hndl;
+ u32 ip_type;
+} __packed;
+
+struct be_cmd_set_def_gateway_req {
+ struct be_cmd_req_hdr hdr;
+ u32 action;
+ struct ip_addr_format ip_addr;
+} __packed;
+
+struct be_cmd_get_def_gateway_req {
+ struct be_cmd_req_hdr hdr;
+ u32 ip_type;
+} __packed;
+
+struct be_cmd_get_def_gateway_resp {
+ struct be_cmd_req_hdr hdr;
+ struct ip_addr_format ip_addr;
+} __packed;
+
+#define BEISCSI_VLAN_DISABLE 0xFFFF
+struct be_cmd_set_vlan_req {
+ struct be_cmd_req_hdr hdr;
+ u32 interface_hndl;
+ u32 vlan_priority;
+} __packed;
/******************** Create CQ ***************************/
/**
* Pseudo amap definition in which each bit of the actual structure is defined
@@ -387,10 +592,28 @@ struct amap_cq_context {
u8 rsvd5[32]; /* dword 3 */
} __packed;
+struct amap_cq_context_v2 {
+ u8 rsvd0[12]; /* dword 0 */
+ u8 coalescwm[2]; /* dword 0 */
+ u8 nodelay; /* dword 0 */
+ u8 rsvd1[12]; /* dword 0 */
+ u8 count[2]; /* dword 0 */
+ u8 valid; /* dword 0 */
+ u8 rsvd2; /* dword 0 */
+ u8 eventable; /* dword 0 */
+ u8 eqid[16]; /* dword 1 */
+ u8 rsvd3[15]; /* dword 1 */
+ u8 armed; /* dword 1 */
+ u8 cqecount[16];/* dword 2 */
+ u8 rsvd4[16]; /* dword 2 */
+ u8 rsvd5[32]; /* dword 3 */
+};
+
struct be_cmd_req_cq_create {
struct be_cmd_req_hdr hdr;
u16 num_pages;
- u16 rsvd0;
+ u8 page_size;
+ u8 rsvd0;
u8 context[sizeof(struct amap_cq_context) / 8];
struct phys_addr pages[4];
} __packed;
@@ -421,10 +644,11 @@ struct amap_mcc_context {
u8 rsvd2[32];
} __packed;
-struct be_cmd_req_mcc_create {
+struct be_cmd_req_mcc_create_ext {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u16 rsvd0;
+ u32 async_evt_bitmap;
u8 context[sizeof(struct amap_mcc_context) / 8];
struct phys_addr pages[8];
} __packed;
@@ -477,20 +701,18 @@ static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb)
/******************** Modify EQ Delay *******************/
struct be_cmd_req_modify_eq_delay {
struct be_cmd_req_hdr hdr;
- u32 num_eq;
+ __le32 num_eq;
struct {
- u32 eq_id;
- u32 phase;
- u32 delay_multiplier;
- } delay[8];
+ __le32 eq_id;
+ __le32 phase;
+ __le32 delay_multiplier;
+ } delay[MAX_CPUS];
} __packed;
/******************** Get MAC ADDR *******************/
-#define ETH_ALEN 6
-
-struct be_cmd_req_get_mac_addr {
- struct be_cmd_req_hdr hdr;
+struct be_cmd_get_nic_conf_resp {
+ struct be_cmd_resp_hdr hdr;
u32 nic_port_count;
u32 speed;
u32 max_speed;
@@ -498,21 +720,85 @@ struct be_cmd_req_get_mac_addr {
u32 max_frame_size;
u16 size_of_structure;
u8 mac_address[ETH_ALEN];
- u32 rsvd[23];
+} __packed;
+
+/******************** Get HBA NAME *******************/
+
+struct be_cmd_hba_name {
+ struct be_cmd_req_hdr hdr;
+ u16 flags;
+ u16 rsvd0;
+ u8 initiator_name[ISCSI_NAME_LEN];
+#define BE_INI_ALIAS_LEN 32
+ u8 initiator_alias[BE_INI_ALIAS_LEN];
+} __packed;
+
+/******************** COMMON SET HOST DATA *******************/
+#define BE_CMD_SET_HOST_PARAM_ID 0x2
+#define BE_CMD_MAX_DRV_VERSION 0x30
+struct be_sethost_req {
+ u32 param_id;
+ u32 param_len;
+ u32 param_data[32];
};
-struct be_cmd_resp_get_mac_addr {
- struct be_cmd_resp_hdr hdr;
- u32 nic_port_count;
- u32 speed;
- u32 max_speed;
- u32 link_state;
- u32 max_frame_size;
- u16 size_of_structure;
- u8 mac_address[6];
- u32 rsvd[23];
+struct be_sethost_resp {
+ u32 rsvd0;
};
+struct be_cmd_set_host_data {
+ union {
+ struct be_cmd_req_hdr req_hdr;
+ struct be_cmd_resp_hdr resp_hdr;
+ } h;
+ union {
+ struct be_sethost_req req;
+ struct be_sethost_resp resp;
+ } param;
+} __packed;
+
+/******************** COMMON SET Features *******************/
+#define BE_CMD_SET_FEATURE_UER 0x10
+#define BE_CMD_UER_SUPP_BIT 0x1
+struct be_uer_req {
+ u32 uer;
+ u32 rsvd;
+};
+
+struct be_uer_resp {
+ u32 uer;
+ u16 ue2rp;
+ u16 ue2sr;
+};
+
+struct be_cmd_set_features {
+ union {
+ struct be_cmd_req_hdr req_hdr;
+ struct be_cmd_resp_hdr resp_hdr;
+ } h;
+ u32 feature;
+ u32 param_len;
+ union {
+ struct be_uer_req req;
+ struct be_uer_resp resp;
+ u32 rsvd[2];
+ } param;
+} __packed;
+
+int beiscsi_cmd_function_reset(struct beiscsi_hba *phba);
+
+int beiscsi_cmd_special_wrb(struct be_ctrl_info *ctrl, u32 load);
+
+int beiscsi_check_fw_rdy(struct beiscsi_hba *phba);
+
+int beiscsi_init_sliport(struct beiscsi_hba *phba);
+
+int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp_num);
+
+int beiscsi_detect_ue(struct beiscsi_hba *phba);
+
+int beiscsi_detect_tpe(struct beiscsi_hba *phba);
+
int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *eq, int eq_delay);
@@ -527,44 +813,59 @@ int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
struct be_queue_info *mccq,
struct be_queue_info *cq);
-int be_poll_mcc(struct be_ctrl_info *ctrl);
-int mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
- struct beiscsi_hba *phba);
-unsigned int be_cmd_get_mac_addr(struct beiscsi_hba *phba);
-unsigned int beiscsi_get_boot_target(struct beiscsi_hba *phba);
-unsigned int beiscsi_get_session_info(struct beiscsi_hba *phba,
- u32 boot_session_handle,
- struct be_dma_mem *nonemb_cmd);
-
-void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag);
-/*ISCSI Functuions */
-int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
-
+void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag);
+
+int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *,
+ int num);
+int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba,
+ unsigned int tag,
+ struct be_mcc_wrb **wrb,
+ struct be_dma_mem *mbx_cmd_mem);
+int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba,
+ unsigned int tag,
+ struct be_mcc_wrb **wrb,
+ struct be_dma_mem *mbx_cmd_mem);
struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem);
-struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba);
-int be_mcc_notify_wait(struct beiscsi_hba *phba);
-void be_mcc_notify(struct beiscsi_hba *phba);
-unsigned int alloc_mcc_tag(struct beiscsi_hba *phba);
-void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
- struct be_async_event_link_state *evt);
-int be_mcc_compl_process_isr(struct be_ctrl_info *ctrl,
- struct be_mcc_compl *compl);
-
-int be_mbox_notify(struct be_ctrl_info *ctrl);
+void be_mcc_notify(struct beiscsi_hba *phba, unsigned int tag);
+struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba,
+ unsigned int *ref_tag);
+void beiscsi_process_async_event(struct beiscsi_hba *phba,
+ struct be_mcc_compl *compl);
+int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl,
+ struct be_mcc_compl *compl);
int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
struct be_queue_info *cq,
struct be_queue_info *dq, int length,
- int entry_size);
+ int entry_size, uint8_t is_header,
+ uint8_t ulp_num);
+
+int be_cmd_iscsi_post_template_hdr(struct be_ctrl_info *ctrl,
+ struct be_dma_mem *q_mem);
+
+int be_cmd_iscsi_remove_template_hdr(struct be_ctrl_info *ctrl);
int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl,
struct be_dma_mem *q_mem, u32 page_offset,
u32 num_pages);
int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
- struct be_queue_info *wrbq);
+ struct be_queue_info *wrbq,
+ struct hwi_wrb_context *pwrb_context,
+ uint8_t ulp_num);
+
+/* Configuration Functions */
+int be_cmd_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag);
+
+int beiscsi_check_supported_fw(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba);
+
+int beiscsi_get_fw_config(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba);
-bool is_link_state_evt(u32 trailer);
+int beiscsi_get_port_name(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba);
+
+int beiscsi_set_uer_feature(struct beiscsi_hba *phba);
+int beiscsi_set_host_data(struct beiscsi_hba *phba);
struct be_default_pdu_context {
u32 dw[4];
@@ -588,11 +889,25 @@ struct amap_be_default_pdu_context {
u8 rsvd4[32]; /* dword 3 */
} __packed;
+struct amap_default_pdu_context_ext {
+ u8 rsvd0[16]; /* dword 0 */
+ u8 ring_size[4]; /* dword 0 */
+ u8 rsvd1[12]; /* dword 0 */
+ u8 rsvd2[22]; /* dword 1 */
+ u8 rx_pdid[9]; /* dword 1 */
+ u8 rx_pdid_valid; /* dword 1 */
+ u8 default_buffer_size[16]; /* dword 2 */
+ u8 cq_id_recv[16]; /* dword 2 */
+ u8 rsvd3[32]; /* dword 3 */
+} __packed;
+
struct be_defq_create_req {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u8 ulp_num;
- u8 rsvd0;
+#define BEISCSI_DUAL_ULP_AWARE_BIT 0 /* Byte 3 - Bit 0 */
+#define BEISCSI_BIND_Q_TO_ULP_BIT 1 /* Byte 3 - Bit 1 */
+ u8 dua_feature;
struct be_default_pdu_context context;
struct phys_addr pages[8];
} __packed;
@@ -600,6 +915,27 @@ struct be_defq_create_req {
struct be_defq_create_resp {
struct be_cmd_req_hdr hdr;
u16 id;
+ u8 rsvd0;
+ u8 ulp_num;
+ u32 doorbell_offset;
+ u16 register_set;
+ u16 doorbell_format;
+} __packed;
+
+struct be_post_template_pages_req {
+ struct be_cmd_req_hdr hdr;
+ u16 num_pages;
+#define BEISCSI_TEMPLATE_HDR_TYPE_ISCSI 0x1
+ u16 type;
+ struct phys_addr scratch_pa;
+ struct virt_addr scratch_va;
+ struct virt_addr pages_va;
+ struct phys_addr pages[16];
+} __packed;
+
+struct be_remove_template_pages_req {
+ struct be_cmd_req_hdr hdr;
+ u16 type;
u16 rsvd0;
} __packed;
@@ -616,14 +952,18 @@ struct be_wrbq_create_req {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u8 ulp_num;
- u8 rsvd0;
+ u8 dua_feature;
struct phys_addr pages[8];
} __packed;
struct be_wrbq_create_resp {
struct be_cmd_resp_hdr resp_hdr;
u16 cid;
- u16 rsvd0;
+ u8 rsvd0;
+ u8 ulp_num;
+ u32 doorbell_offset;
+ u16 register_set;
+ u16 doorbell_format;
} __packed;
#define SOL_CID_MASK 0x0000FFC0
@@ -673,6 +1013,59 @@ struct amap_sol_cqe_ring {
u8 valid; /* dword 3 */
} __packed;
+struct amap_sol_cqe_v2 {
+ u8 hw_sts[8]; /* dword 0 */
+ u8 i_sts[8]; /* dword 0 */
+ u8 wrb_index[16]; /* dword 0 */
+ u8 i_exp_cmd_sn[32]; /* dword 1 */
+ u8 code[6]; /* dword 2 */
+ u8 cmd_cmpl; /* dword 2 */
+ u8 rsvd0; /* dword 2 */
+ u8 i_cmd_wnd[8]; /* dword 2 */
+ u8 cid[13]; /* dword 2 */
+ u8 u; /* dword 2 */
+ u8 o; /* dword 2 */
+ u8 s; /* dword 2 */
+ u8 i_res_cnt[31]; /* dword 3 */
+ u8 valid; /* dword 3 */
+} __packed;
+
+struct common_sol_cqe {
+ u32 exp_cmdsn;
+ u32 res_cnt;
+ u16 wrb_index;
+ u16 cid;
+ u8 hw_sts;
+ u8 cmd_wnd;
+ u8 res_flag; /* the s feild of structure */
+ u8 i_resp; /* for skh if cmd_complete is set then i_sts is response */
+ u8 i_flags; /* for skh or the u and o feilds */
+ u8 i_sts; /* for skh if cmd_complete is not-set then i_sts is status */
+};
+
+/*** iSCSI ack/driver message completions ***/
+struct amap_it_dmsg_cqe {
+ u8 ack_num[32]; /* DWORD 0 */
+ u8 pdu_bytes_rcvd[32]; /* DWORD 1 */
+ u8 code[6]; /* DWORD 2 */
+ u8 cid[10]; /* DWORD 2 */
+ u8 wrb_idx[8]; /* DWORD 2 */
+ u8 rsvd0[8]; /* DWORD 2*/
+ u8 rsvd1[31]; /* DWORD 3*/
+ u8 valid; /* DWORD 3 */
+} __packed;
+
+struct amap_it_dmsg_cqe_v2 {
+ u8 ack_num[32]; /* DWORD 0 */
+ u8 pdu_bytes_rcvd[32]; /* DWORD 1 */
+ u8 code[6]; /* DWORD 2 */
+ u8 rsvd0[10]; /* DWORD 2 */
+ u8 wrb_idx[16]; /* DWORD 2 */
+ u8 rsvd1[16]; /* DWORD 3 */
+ u8 cid[13]; /* DWORD 3 */
+ u8 rsvd2[2]; /* DWORD 3 */
+ u8 valid; /* DWORD 3 */
+} __packed;
/**
@@ -680,7 +1073,7 @@ struct amap_sol_cqe_ring {
* stack to notify the
* controller of a posted Work Request Block
*/
-#define DB_WRB_POST_CID_MASK 0x3FF /* bits 0 - 9 */
+#define DB_WRB_POST_CID_MASK 0xFFFF /* bits 0 - 16 */
#define DB_DEF_PDU_WRB_INDEX_MASK 0xFF /* bits 0 - 9 */
#define DB_DEF_PDU_WRB_INDEX_SHIFT 16
@@ -696,7 +1089,16 @@ struct iscsi_cleanup_req {
u16 chute;
u8 hdr_ring_id;
u8 data_ring_id;
+} __packed;
+struct iscsi_cleanup_req_v1 {
+ struct be_cmd_req_hdr hdr;
+ u16 chute;
+ u16 rsvd1;
+ u16 hdr_ring_id;
+ u16 rsvd2;
+ u16 data_ring_id;
+ u16 rsvd3;
} __packed;
struct eq_delay {
@@ -713,7 +1115,7 @@ struct be_eq_delay_params_in {
struct tcp_connect_and_offload_in {
struct be_cmd_req_hdr hdr;
- struct ip_address_format ip_address;
+ struct ip_addr_format ip_address;
u16 tcp_port;
u16 cid;
u16 cq_id;
@@ -725,6 +1127,26 @@ struct tcp_connect_and_offload_in {
u8 rsvd0[3];
} __packed;
+struct tcp_connect_and_offload_in_v1 {
+ struct be_cmd_req_hdr hdr;
+ struct ip_addr_format ip_address;
+ u16 tcp_port;
+ u16 cid;
+ u16 cq_id;
+ u16 defq_id;
+ struct phys_addr dataout_template_pa;
+ u16 hdr_ring_id;
+ u16 data_ring_id;
+ u8 do_offload;
+ u8 ifd_state;
+ u8 rsvd0[2];
+ u16 tcp_window_size;
+ u8 tcp_window_scale_count;
+ u8 rsvd1;
+ u32 tcp_mss:24;
+ u8 rsvd2;
+} __packed;
+
struct tcp_connect_and_offload_out {
struct be_cmd_resp_hdr hdr;
u32 connection_handle;
@@ -733,38 +1155,59 @@ struct tcp_connect_and_offload_out {
} __packed;
-struct be_mcc_wrb_context {
- struct MCC_WRB *wrb;
- int *users_final_status;
-} __packed;
-
-#define DB_DEF_PDU_RING_ID_MASK 0x3FF /* bits 0 - 9 */
-#define DB_DEF_PDU_CQPROC_MASK 0x3FFF /* bits 0 - 9 */
+#define DB_DEF_PDU_RING_ID_MASK 0x3FFF /* bits 0 - 13 */
+#define DB_DEF_PDU_CQPROC_MASK 0x3FFF /* bits 16 - 29 */
#define DB_DEF_PDU_REARM_SHIFT 14
#define DB_DEF_PDU_EVENT_SHIFT 15
#define DB_DEF_PDU_CQPROC_SHIFT 16
-struct dmsg_cqe {
- u32 dw[4];
+struct be_invalidate_connection_params_in {
+ struct be_cmd_req_hdr hdr;
+ u32 session_handle;
+ u16 cid;
+ u16 unused;
+#define BE_CLEANUP_TYPE_INVALIDATE 0x8001
+#define BE_CLEANUP_TYPE_ISSUE_TCP_RST 0x8002
+ u16 cleanup_type;
+ u16 save_cfg;
} __packed;
-struct tcp_upload_params_in {
+struct be_invalidate_connection_params_out {
+ u32 session_handle;
+ u16 cid;
+ u16 unused;
+} __packed;
+
+union be_invalidate_connection_params {
+ struct be_invalidate_connection_params_in req;
+ struct be_invalidate_connection_params_out resp;
+} __packed;
+
+struct be_tcp_upload_params_in {
struct be_cmd_req_hdr hdr;
u16 id;
+#define BE_UPLOAD_TYPE_GRACEFUL 1
+/* abortive upload with reset */
+#define BE_UPLOAD_TYPE_ABORT_RESET 2
+/* abortive upload without reset */
+#define BE_UPLOAD_TYPE_ABORT 3
+/* abortive upload with reset, sequence number by driver */
+#define BE_UPLOAD_TYPE_ABORT_WITH_SEQ 4
u16 upload_type;
u32 reset_seq;
} __packed;
-struct tcp_upload_params_out {
+struct be_tcp_upload_params_out {
u32 dw[32];
} __packed;
-union tcp_upload_params {
- struct tcp_upload_params_in request;
- struct tcp_upload_params_out response;
+union be_tcp_upload_params {
+ struct be_tcp_upload_params_in request;
+ struct be_tcp_upload_params_out response;
} __packed;
struct be_ulp_fw_cfg {
+#define BEISCSI_ULP_ISCSI_INI_MODE 0x10
u32 ulp_mode;
u32 etx_base;
u32 etx_count;
@@ -780,23 +1223,51 @@ struct be_ulp_fw_cfg {
u32 icd_count;
};
+struct be_ulp_chain_icd {
+ u32 chain_base;
+ u32 chain_count;
+};
+
struct be_fw_cfg {
struct be_cmd_req_hdr hdr;
u32 be_config_number;
u32 asic_revision;
u32 phys_port;
+#define BEISCSI_FUNC_ISCSI_INI_MODE 0x10
+#define BEISCSI_FUNC_DUA_MODE 0x800
u32 function_mode;
struct be_ulp_fw_cfg ulp[2];
u32 function_caps;
+ u32 cqid_base;
+ u32 cqid_count;
+ u32 eqid_base;
+ u32 eqid_count;
+ struct be_ulp_chain_icd chain_icd[2];
} __packed;
-struct be_all_if_id {
+struct be_cmd_get_all_if_id_req {
struct be_cmd_req_hdr hdr;
u32 if_count;
u32 if_hndl_list[1];
} __packed;
+struct be_cmd_get_port_name {
+ union {
+ struct be_cmd_req_hdr req_hdr;
+ struct be_cmd_resp_hdr resp_hdr;
+ } h;
+ union {
+ struct {
+ u32 reserved;
+ } req;
+ struct {
+ u32 port_names;
+ } resp;
+ } p;
+} __packed;
+
#define ISCSI_OPCODE_SCSI_DATA_OUT 5
+#define OPCODE_COMMON_NTWK_LINK_STATUS_QUERY 5
#define OPCODE_COMMON_MODIFY_EQ_DELAY 41
#define OPCODE_COMMON_ISCSI_CLEANUP 59
#define OPCODE_COMMON_TCP_UPLOAD 56
@@ -805,14 +1276,15 @@ struct be_all_if_id {
#define OPCODE_ISCSI_INI_CFG_GET_HBA_NAME 6
#define OPCODE_ISCSI_INI_CFG_SET_HBA_NAME 7
#define OPCODE_ISCSI_INI_SESSION_GET_A_SESSION 14
+#define OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET 24
+#define OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS 36
#define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41
#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
#define OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET 52
+#define OPCODE_COMMON_WRITE_FLASH 96
+#define OPCODE_COMMON_READ_FLASH 97
-/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */
#define CMD_ISCSI_COMMAND_INVALIDATE 1
-#define CMD_ISCSI_CONNECTION_INVALIDATE 0x8001
-#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 0x8002
#define INI_WR_CMD 1 /* Initiator write command */
#define INI_TMF_CMD 2 /* Initiator TMF command */
@@ -821,47 +1293,14 @@ struct be_all_if_id {
* a read command
*/
#define TGT_CTX_UPDT_CMD 7 /* Target context update */
-#define TGT_STS_CMD 8 /* Target R2T and other BHS
- * where only the status number
- * need to be updated
- */
-#define TGT_DATAIN_CMD 9 /* Target Data-Ins in response
- * to read command
- */
-#define TGT_SOS_PDU 10 /* Target:standalone status
- * response
- */
#define TGT_DM_CMD 11 /* Indicates that the bhs
- * preparedby
- * driver should not be touched
- */
-/* --- CMD_CHUTE_TYPE --- */
-#define CMD_CONNECTION_CHUTE_0 1
-#define CMD_CONNECTION_CHUTE_1 2
-#define CMD_CONNECTION_CHUTE_2 3
-
-#define EQ_MAJOR_CODE_COMPLETION 0
-
-#define CMD_ISCSI_SESSION_DEL_CFG_FROM_FLASH 0
-#define CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH 1
-
-/* --- CONNECTION_UPLOAD_PARAMS --- */
-/* These parameters are used to define the type of upload desired. */
-#define CONNECTION_UPLOAD_GRACEFUL 1 /* Graceful upload */
-#define CONNECTION_UPLOAD_ABORT_RESET 2 /* Abortive upload with
- * reset
- */
-#define CONNECTION_UPLOAD_ABORT 3 /* Abortive upload without
- * reset
+ * prepared by driver should not
+ * be touched.
*/
-#define CONNECTION_UPLOAD_ABORT_WITH_SEQ 4 /* Abortive upload with reset,
- * sequence number by driver */
-
-/* Returns byte size of given field with a structure. */
/* Returns the number of items in the field array. */
#define BE_NUMBER_OF_FIELD(_type_, _field_) \
- (FIELD_SIZEOF(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\
+ (sizeof_field(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\
/**
* Different types of iSCSI completions to host driver for both initiator
@@ -1014,13 +1453,9 @@ struct be_all_if_id {
* the cxn
*/
-int beiscsi_pci_soft_reset(struct beiscsi_hba *phba);
-int be_chk_reset_complete(struct beiscsi_hba *phba);
-
-void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
+void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, u32 payload_len,
bool embedded, u8 sge_cnt);
void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
- u8 subsystem, u8 opcode, int cmd_len);
-
+ u8 subsystem, u8 opcode, u32 cmd_len);
#endif /* !BEISCSI_CMDS_H */
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index 3cad10605023..8d374ae863ba 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -1,20 +1,14 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file is part of the Emulex Linux Device Driver for Enterprise iSCSI
+ * Host Bus Adapters. Refer to the README file included with this package
+ * for driver version and adapter compatibility.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * Copyright (c) 2018 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*
* Contact Information:
- * linux-drivers@emulex.com
- *
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
+ * linux-drivers@broadcom.com
*/
#include <scsi/libiscsi.h>
@@ -23,6 +17,8 @@
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_netlink.h>
+#include <net/netlink.h>
#include <scsi/scsi.h>
#include "be_iscsi.h"
@@ -31,6 +27,7 @@ extern struct iscsi_transport beiscsi_iscsi_transport;
/**
* beiscsi_session_create - creates a new iscsi session
+ * @ep: pointer to iscsi ep
* @cmds_max: max commands supported
* @qdepth: max queue depth supported
* @initial_cmdsn: initial iscsi CMDSN
@@ -48,24 +45,34 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
struct beiscsi_session *beiscsi_sess;
struct beiscsi_io_task *io_task;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n");
if (!ep) {
- SE_DEBUG(DBG_LVL_1, "beiscsi_session_create: invalid ep\n");
+ pr_err("beiscsi_session_create: invalid ep\n");
return NULL;
}
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
- shost = phba->shost;
+
+ if (!beiscsi_hba_is_online(phba)) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : HBA in error 0x%lx\n", phba->state);
+ return NULL;
+ }
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_session_create\n");
if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) {
- shost_printk(KERN_ERR, shost, "Cannot handle %d cmds."
- "Max cmds per session supported is %d. Using %d. "
- "\n", cmds_max,
- beiscsi_ep->phba->params.wrbs_per_cxn,
- beiscsi_ep->phba->params.wrbs_per_cxn);
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Cannot handle %d cmds."
+ "Max cmds per session supported is %d. Using %d."
+ "\n", cmds_max,
+ beiscsi_ep->phba->params.wrbs_per_cxn,
+ beiscsi_ep->phba->params.wrbs_per_cxn);
+
cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn;
}
+ shost = phba->shost;
cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
shost, cmds_max,
sizeof(*beiscsi_sess),
@@ -75,8 +82,8 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
return NULL;
sess = cls_session->dd_data;
beiscsi_sess = sess->dd_data;
- beiscsi_sess->bhs_pool = pci_pool_create("beiscsi_bhs_pool",
- phba->pcidev,
+ beiscsi_sess->bhs_pool = dma_pool_create("beiscsi_bhs_pool",
+ &phba->pcidev->dev,
sizeof(struct be_cmd_bhs),
64, 0);
if (!beiscsi_sess->bhs_pool)
@@ -100,12 +107,22 @@ void beiscsi_session_destroy(struct iscsi_cls_session *cls_session)
struct iscsi_session *sess = cls_session->dd_data;
struct beiscsi_session *beiscsi_sess = sess->dd_data;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_session_destroy\n");
- pci_pool_destroy(beiscsi_sess->bhs_pool);
+ printk(KERN_INFO "In beiscsi_session_destroy\n");
+ dma_pool_destroy(beiscsi_sess->bhs_pool);
iscsi_session_teardown(cls_session);
}
/**
+ * beiscsi_session_fail(): Closing session with appropriate error
+ * @cls_session: ptr to session
+ **/
+void beiscsi_session_fail(struct iscsi_cls_session *cls_session)
+{
+ iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED);
+}
+
+
+/**
* beiscsi_conn_create - create an instance of iscsi connection
* @cls_session: ptr to iscsi_cls_session
* @cid: iscsi cid
@@ -121,11 +138,13 @@ beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid)
struct iscsi_session *sess;
struct beiscsi_session *beiscsi_sess;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid"
- "from iscsi layer=%d\n", cid);
shost = iscsi_session_to_shost(cls_session);
phba = iscsi_host_priv(shost);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_conn_create ,cid"
+ "from iscsi layer=%d\n", cid);
+
cls_conn = iscsi_conn_setup(cls_session, sizeof(*beiscsi_conn), cid);
if (!cls_conn)
return NULL;
@@ -142,32 +161,11 @@ beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid)
}
/**
- * beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table
- * @beiscsi_conn: The pointer to beiscsi_conn structure
- * @phba: The phba instance
- * @cid: The cid to free
- */
-static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
- struct beiscsi_conn *beiscsi_conn,
- unsigned int cid)
-{
- if (phba->conn_table[cid]) {
- SE_DEBUG(DBG_LVL_1,
- "Connection table already occupied. Detected clash\n");
- return -EINVAL;
- } else {
- SE_DEBUG(DBG_LVL_8, "phba->conn_table[%d]=%p(beiscsi_conn)\n",
- cid, beiscsi_conn);
- phba->conn_table[cid] = beiscsi_conn;
- }
- return 0;
-}
-
-/**
* beiscsi_conn_bind - Binds iscsi session/connection with TCP connection
* @cls_session: pointer to iscsi cls session
* @cls_conn: pointer to iscsi cls conn
* @transport_fd: EP handle(64 bit)
+ * @is_leading: indicate if this is the session leading connection (MCS)
*
* This function binds the TCP Conn with iSCSI Connection and Session.
*/
@@ -177,35 +175,446 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
- struct Scsi_Host *shost =
- (struct Scsi_Host *)iscsi_session_to_shost(cls_session);
- struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost);
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ struct hwi_controller *phwi_ctrlr = phba->phwi_ctrlr;
+ struct hwi_wrb_context *pwrb_context;
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_endpoint *ep;
+ uint16_t cri_index;
+ int rc = 0;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_bind\n");
ep = iscsi_lookup_endpoint(transport_fd);
if (!ep)
return -EINVAL;
beiscsi_ep = ep->dd_data;
- if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
- return -EINVAL;
+ if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) {
+ rc = -EINVAL;
+ goto put_ep;
+ }
if (beiscsi_ep->phba != phba) {
- SE_DEBUG(DBG_LVL_8,
- "beiscsi_ep->hba=%p not equal to phba=%p\n",
- beiscsi_ep->phba, phba);
- return -EEXIST;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : beiscsi_ep->hba=%p not equal to phba=%p\n",
+ beiscsi_ep->phba, phba);
+ rc = -EEXIST;
+ goto put_ep;
+ }
+ cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid);
+ if (phba->conn_table[cri_index]) {
+ if (beiscsi_conn != phba->conn_table[cri_index] ||
+ beiscsi_ep != phba->conn_table[cri_index]->ep) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : conn_table not empty at %u: cid %u conn %p:%p\n",
+ cri_index,
+ beiscsi_ep->ep_cid,
+ beiscsi_conn,
+ phba->conn_table[cri_index]);
+ rc = -EINVAL;
+ goto put_ep;
+ }
}
beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid;
beiscsi_conn->ep = beiscsi_ep;
beiscsi_ep->conn = beiscsi_conn;
- SE_DEBUG(DBG_LVL_8, "beiscsi_conn=%p conn=%p ep_cid=%d\n",
- beiscsi_conn, conn, beiscsi_ep->ep_cid);
- return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid);
+ /**
+ * Each connection is associated with a WRBQ kept in wrb_context.
+ * Store doorbell offset for transmit path.
+ */
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
+ beiscsi_conn->doorbell_offset = pwrb_context->doorbell_offset;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : cid %d phba->conn_table[%u]=%p\n",
+ beiscsi_ep->ep_cid, cri_index, beiscsi_conn);
+ phba->conn_table[cri_index] = beiscsi_conn;
+
+put_ep:
+ iscsi_put_endpoint(ep);
+ return rc;
+}
+
+static int beiscsi_iface_create_ipv4(struct beiscsi_hba *phba)
+{
+ if (phba->ipv4_iface)
+ return 0;
+
+ phba->ipv4_iface = iscsi_create_iface(phba->shost,
+ &beiscsi_iscsi_transport,
+ ISCSI_IFACE_TYPE_IPV4,
+ 0, 0);
+ if (!phba->ipv4_iface) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Could not "
+ "create default IPv4 address.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int beiscsi_iface_create_ipv6(struct beiscsi_hba *phba)
+{
+ if (phba->ipv6_iface)
+ return 0;
+
+ phba->ipv6_iface = iscsi_create_iface(phba->shost,
+ &beiscsi_iscsi_transport,
+ ISCSI_IFACE_TYPE_IPV6,
+ 0, 0);
+ if (!phba->ipv6_iface) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Could not "
+ "create default IPv6 address.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void beiscsi_iface_create_default(struct beiscsi_hba *phba)
+{
+ struct be_cmd_get_if_info_resp *if_info;
+
+ if (!beiscsi_if_get_info(phba, BEISCSI_IP_TYPE_V4, &if_info)) {
+ beiscsi_iface_create_ipv4(phba);
+ kfree(if_info);
+ }
+
+ if (!beiscsi_if_get_info(phba, BEISCSI_IP_TYPE_V6, &if_info)) {
+ beiscsi_iface_create_ipv6(phba);
+ kfree(if_info);
+ }
+}
+
+void beiscsi_iface_destroy_default(struct beiscsi_hba *phba)
+{
+ if (phba->ipv6_iface) {
+ iscsi_destroy_iface(phba->ipv6_iface);
+ phba->ipv6_iface = NULL;
+ }
+ if (phba->ipv4_iface) {
+ iscsi_destroy_iface(phba->ipv4_iface);
+ phba->ipv4_iface = NULL;
+ }
+}
+
+/**
+ * beiscsi_iface_config_vlan()- Set the VLAN TAG
+ * @shost: Scsi Host for the driver instance
+ * @iface_param: Interface paramters
+ *
+ * Set the VLAN TAG for the adapter or disable
+ * the VLAN config
+ *
+ * returns
+ * Success: 0
+ * Failure: Non-Zero Value
+ **/
+static int
+beiscsi_iface_config_vlan(struct Scsi_Host *shost,
+ struct iscsi_iface_param_info *iface_param)
+{
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ int ret = -EPERM;
+
+ switch (iface_param->param) {
+ case ISCSI_NET_PARAM_VLAN_ENABLED:
+ ret = 0;
+ if (iface_param->value[0] != ISCSI_VLAN_ENABLE)
+ ret = beiscsi_if_set_vlan(phba, BEISCSI_VLAN_DISABLE);
+ break;
+ case ISCSI_NET_PARAM_VLAN_TAG:
+ ret = beiscsi_if_set_vlan(phba,
+ *((uint16_t *)iface_param->value));
+ break;
+ }
+ return ret;
+}
+
+
+static int
+beiscsi_iface_config_ipv4(struct Scsi_Host *shost,
+ struct iscsi_iface_param_info *info,
+ void *data, uint32_t dt_len)
+{
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ u8 *ip = NULL, *subnet = NULL, *gw;
+ struct nlattr *nla;
+ int ret = -EPERM;
+
+ /* Check the param */
+ switch (info->param) {
+ case ISCSI_NET_PARAM_IFACE_ENABLE:
+ if (info->value[0] == ISCSI_IFACE_ENABLE)
+ ret = beiscsi_iface_create_ipv4(phba);
+ else {
+ iscsi_destroy_iface(phba->ipv4_iface);
+ phba->ipv4_iface = NULL;
+ }
+ break;
+ case ISCSI_NET_PARAM_IPV4_GW:
+ gw = info->value;
+ ret = beiscsi_if_set_gw(phba, BEISCSI_IP_TYPE_V4, gw);
+ break;
+ case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
+ if (info->value[0] == ISCSI_BOOTPROTO_DHCP)
+ ret = beiscsi_if_en_dhcp(phba, BEISCSI_IP_TYPE_V4);
+ else if (info->value[0] == ISCSI_BOOTPROTO_STATIC)
+ /* release DHCP IP address */
+ ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4,
+ NULL, NULL);
+ else
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Invalid BOOTPROTO: %d\n",
+ info->value[0]);
+ break;
+ case ISCSI_NET_PARAM_IPV4_ADDR:
+ ip = info->value;
+ nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET);
+ if (nla) {
+ info = nla_data(nla);
+ subnet = info->value;
+ }
+ ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4,
+ ip, subnet);
+ break;
+ case ISCSI_NET_PARAM_IPV4_SUBNET:
+ /*
+ * OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR ioctl needs IP
+ * and subnet both. Find IP to be applied for this subnet.
+ */
+ subnet = info->value;
+ nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR);
+ if (nla) {
+ info = nla_data(nla);
+ ip = info->value;
+ }
+ ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4,
+ ip, subnet);
+ break;
+ }
+
+ return ret;
+}
+
+static int
+beiscsi_iface_config_ipv6(struct Scsi_Host *shost,
+ struct iscsi_iface_param_info *iface_param,
+ void *data, uint32_t dt_len)
+{
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ int ret = -EPERM;
+
+ switch (iface_param->param) {
+ case ISCSI_NET_PARAM_IFACE_ENABLE:
+ if (iface_param->value[0] == ISCSI_IFACE_ENABLE)
+ ret = beiscsi_iface_create_ipv6(phba);
+ else {
+ iscsi_destroy_iface(phba->ipv6_iface);
+ phba->ipv6_iface = NULL;
+ }
+ break;
+ case ISCSI_NET_PARAM_IPV6_ADDR:
+ ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V6,
+ iface_param->value, NULL);
+ break;
+ }
+
+ return ret;
+}
+
+int beiscsi_iface_set_param(struct Scsi_Host *shost,
+ void *data, uint32_t dt_len)
+{
+ struct iscsi_iface_param_info *iface_param = NULL;
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ struct nlattr *attrib;
+ uint32_t rm_len = dt_len;
+ int ret;
+
+ if (!beiscsi_hba_is_online(phba)) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : HBA in error 0x%lx\n", phba->state);
+ return -EBUSY;
+ }
+
+ /* update interface_handle */
+ ret = beiscsi_if_get_handle(phba);
+ if (ret) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Getting Interface Handle Failed\n");
+ return ret;
+ }
+
+ nla_for_each_attr(attrib, data, dt_len, rm_len) {
+ /* ignore nla_type as it is never used */
+ if (nla_len(attrib) < sizeof(*iface_param))
+ return -EINVAL;
+
+ iface_param = nla_data(attrib);
+
+ if (iface_param->param_type != ISCSI_NET_PARAM)
+ continue;
+
+ /*
+ * BE2ISCSI only supports 1 interface
+ */
+ if (iface_param->iface_num) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Invalid iface_num %d."
+ "Only iface_num 0 is supported.\n",
+ iface_param->iface_num);
+
+ return -EINVAL;
+ }
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : %s.0 set param %d",
+ (iface_param->iface_type == ISCSI_IFACE_TYPE_IPV4) ?
+ "ipv4" : "ipv6", iface_param->param);
+
+ ret = -EPERM;
+ switch (iface_param->param) {
+ case ISCSI_NET_PARAM_VLAN_ENABLED:
+ case ISCSI_NET_PARAM_VLAN_TAG:
+ ret = beiscsi_iface_config_vlan(shost, iface_param);
+ break;
+ default:
+ switch (iface_param->iface_type) {
+ case ISCSI_IFACE_TYPE_IPV4:
+ ret = beiscsi_iface_config_ipv4(shost,
+ iface_param,
+ data, dt_len);
+ break;
+ case ISCSI_IFACE_TYPE_IPV6:
+ ret = beiscsi_iface_config_ipv6(shost,
+ iface_param,
+ data, dt_len);
+ break;
+ }
+ }
+
+ if (ret == -EPERM) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : %s.0 set param %d not permitted",
+ (iface_param->iface_type ==
+ ISCSI_IFACE_TYPE_IPV4) ? "ipv4" : "ipv6",
+ iface_param->param);
+ ret = 0;
+ }
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int __beiscsi_iface_get_param(struct beiscsi_hba *phba,
+ struct iscsi_iface *iface,
+ int param, char *buf)
+{
+ struct be_cmd_get_if_info_resp *if_info;
+ int len, ip_type = BEISCSI_IP_TYPE_V4;
+
+ if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6)
+ ip_type = BEISCSI_IP_TYPE_V6;
+
+ len = beiscsi_if_get_info(phba, ip_type, &if_info);
+ if (len)
+ return len;
+
+ switch (param) {
+ case ISCSI_NET_PARAM_IPV4_ADDR:
+ len = sprintf(buf, "%pI4\n", if_info->ip_addr.addr);
+ break;
+ case ISCSI_NET_PARAM_IPV6_ADDR:
+ len = sprintf(buf, "%pI6\n", if_info->ip_addr.addr);
+ break;
+ case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
+ if (!if_info->dhcp_state)
+ len = sprintf(buf, "static\n");
+ else
+ len = sprintf(buf, "dhcp\n");
+ break;
+ case ISCSI_NET_PARAM_IPV4_SUBNET:
+ len = sprintf(buf, "%pI4\n", if_info->ip_addr.subnet_mask);
+ break;
+ case ISCSI_NET_PARAM_VLAN_ENABLED:
+ len = sprintf(buf, "%s\n",
+ (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) ?
+ "disable" : "enable");
+ break;
+ case ISCSI_NET_PARAM_VLAN_ID:
+ if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE)
+ len = -EINVAL;
+ else
+ len = sprintf(buf, "%d\n",
+ (if_info->vlan_priority &
+ ISCSI_MAX_VLAN_ID));
+ break;
+ case ISCSI_NET_PARAM_VLAN_PRIORITY:
+ if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE)
+ len = -EINVAL;
+ else
+ len = sprintf(buf, "%d\n",
+ ((if_info->vlan_priority >> 13) &
+ ISCSI_MAX_VLAN_PRIORITY));
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ kfree(if_info);
+ return len;
+}
+
+int beiscsi_iface_get_param(struct iscsi_iface *iface,
+ enum iscsi_param_type param_type,
+ int param, char *buf)
+{
+ struct Scsi_Host *shost = iscsi_iface_to_shost(iface);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ struct be_cmd_get_def_gateway_resp gateway;
+ int len = -EPERM;
+
+ if (param_type != ISCSI_NET_PARAM)
+ return 0;
+ if (!beiscsi_hba_is_online(phba)) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : HBA in error 0x%lx\n", phba->state);
+ return -EBUSY;
+ }
+
+ switch (param) {
+ case ISCSI_NET_PARAM_IPV4_ADDR:
+ case ISCSI_NET_PARAM_IPV4_SUBNET:
+ case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
+ case ISCSI_NET_PARAM_IPV6_ADDR:
+ case ISCSI_NET_PARAM_VLAN_ENABLED:
+ case ISCSI_NET_PARAM_VLAN_ID:
+ case ISCSI_NET_PARAM_VLAN_PRIORITY:
+ len = __beiscsi_iface_get_param(phba, iface, param, buf);
+ break;
+ case ISCSI_NET_PARAM_IFACE_ENABLE:
+ if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4)
+ len = sprintf(buf, "%s\n",
+ phba->ipv4_iface ? "enable" : "disable");
+ else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6)
+ len = sprintf(buf, "%s\n",
+ phba->ipv6_iface ? "enable" : "disable");
+ break;
+ case ISCSI_NET_PARAM_IPV4_GW:
+ memset(&gateway, 0, sizeof(gateway));
+ len = beiscsi_if_get_gw(phba, BEISCSI_IP_TYPE_V4, &gateway);
+ if (!len)
+ len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr);
+ break;
+ }
+
+ return len;
}
/**
@@ -220,22 +629,25 @@ int beiscsi_ep_get_param(struct iscsi_endpoint *ep,
enum iscsi_param param, char *buf)
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
- int len = 0;
+ int len;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_param, param= %d\n", param);
+ beiscsi_log(beiscsi_ep->phba, KERN_INFO,
+ BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_ep_get_param,"
+ " param= %d\n", param);
switch (param) {
case ISCSI_PARAM_CONN_PORT:
len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport);
break;
case ISCSI_PARAM_CONN_ADDRESS:
- if (beiscsi_ep->ip_type == BE2_IPV4)
+ if (beiscsi_ep->ip_type == BEISCSI_IP_TYPE_V4)
len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr);
else
len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr);
break;
default:
- return -ENOSYS;
+ len = -EPERM;
}
return len;
}
@@ -245,9 +657,14 @@ int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
+ struct beiscsi_hba *phba = NULL;
int ret;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_set_param, param= %d\n", param);
+ phba = ((struct beiscsi_conn *)conn->dd_data)->phba;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_conn_set_param,"
+ " param= %d\n", param);
+
ret = iscsi_set_param(cls_conn, param, buf, buflen);
if (ret)
return ret;
@@ -269,9 +686,9 @@ int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
session->max_burst = 262144;
break;
case ISCSI_PARAM_MAX_XMIT_DLENGTH:
- if ((conn->max_xmit_dlength > 65536) ||
- (conn->max_xmit_dlength == 0))
+ if (conn->max_xmit_dlength > 65536)
conn->max_xmit_dlength = 65536;
+ fallthrough;
default:
return 0;
}
@@ -280,28 +697,103 @@ int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
}
/**
+ * beiscsi_get_port_state - Get the Port State
+ * @shost : pointer to scsi_host structure
+ *
+ */
+static void beiscsi_get_port_state(struct Scsi_Host *shost)
+{
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ struct iscsi_cls_host *ihost = shost->shost_data;
+
+ ihost->port_state = test_bit(BEISCSI_HBA_LINK_UP, &phba->state) ?
+ ISCSI_PORT_STATE_UP : ISCSI_PORT_STATE_DOWN;
+}
+
+/**
+ * beiscsi_get_port_speed - Get the Port Speed from Adapter
+ * @shost : pointer to scsi_host structure
+ *
+ */
+static void beiscsi_get_port_speed(struct Scsi_Host *shost)
+{
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ struct iscsi_cls_host *ihost = shost->shost_data;
+
+ switch (phba->port_speed) {
+ case BE2ISCSI_LINK_SPEED_10MBPS:
+ ihost->port_speed = ISCSI_PORT_SPEED_10MBPS;
+ break;
+ case BE2ISCSI_LINK_SPEED_100MBPS:
+ ihost->port_speed = ISCSI_PORT_SPEED_100MBPS;
+ break;
+ case BE2ISCSI_LINK_SPEED_1GBPS:
+ ihost->port_speed = ISCSI_PORT_SPEED_1GBPS;
+ break;
+ case BE2ISCSI_LINK_SPEED_10GBPS:
+ ihost->port_speed = ISCSI_PORT_SPEED_10GBPS;
+ break;
+ case BE2ISCSI_LINK_SPEED_25GBPS:
+ ihost->port_speed = ISCSI_PORT_SPEED_25GBPS;
+ break;
+ case BE2ISCSI_LINK_SPEED_40GBPS:
+ ihost->port_speed = ISCSI_PORT_SPEED_40GBPS;
+ break;
+ default:
+ ihost->port_speed = ISCSI_PORT_SPEED_UNKNOWN;
+ }
+}
+
+/**
* beiscsi_get_host_param - get the iscsi parameter
* @shost: pointer to scsi_host structure
* @param: parameter type identifier
* @buf: buffer pointer
*
- * returns host parameter
*/
int beiscsi_get_host_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
- struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
int status = 0;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_get_host_param, param= %d\n", param);
+ if (!beiscsi_hba_is_online(phba)) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : HBA in error 0x%lx\n", phba->state);
+ return 0;
+ }
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_get_host_param, param = %d\n", param);
+
switch (param) {
case ISCSI_HOST_PARAM_HWADDRESS:
status = beiscsi_get_macaddr(buf, phba);
if (status < 0) {
- SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n");
- return status;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : beiscsi_get_macaddr Failed\n");
+ return 0;
}
break;
+ case ISCSI_HOST_PARAM_INITIATOR_NAME:
+ /* try fetching user configured name first */
+ status = beiscsi_get_initiator_name(phba, buf, true);
+ if (status < 0) {
+ status = beiscsi_get_initiator_name(phba, buf, false);
+ if (status < 0) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Retrieving Initiator Name Failed\n");
+ status = 0;
+ }
+ }
+ break;
+ case ISCSI_HOST_PARAM_PORT_STATE:
+ beiscsi_get_port_state(shost);
+ status = sprintf(buf, "%s\n", iscsi_get_port_state_name(shost));
+ break;
+ case ISCSI_HOST_PARAM_PORT_SPEED:
+ beiscsi_get_port_speed(shost);
+ status = sprintf(buf, "%s\n", iscsi_get_port_speed_name(shost));
+ break;
default:
return iscsi_host_get_param(shost, param, buf);
}
@@ -310,45 +802,21 @@ int beiscsi_get_host_param(struct Scsi_Host *shost,
int beiscsi_get_macaddr(char *buf, struct beiscsi_hba *phba)
{
- struct be_cmd_resp_get_mac_addr *resp;
- struct be_mcc_wrb *wrb;
- unsigned int tag, wrb_num;
- unsigned short status, extd_status;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ struct be_cmd_get_nic_conf_resp resp;
int rc;
- if (phba->read_mac_address)
- return sysfs_format_mac(buf, phba->mac_address,
- ETH_ALEN);
+ if (phba->mac_addr_set)
+ return sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
- tag = be_cmd_get_mac_addr(phba);
- if (!tag) {
- SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed\n");
- return -EBUSY;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
- SE_DEBUG(DBG_LVL_1, "Failed to get be_cmd_get_mac_addr"
- " status = %d extd_status = %d\n",
- status, extd_status);
- free_mcc_tag(&phba->ctrl, tag);
- return -EAGAIN;
- }
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
- resp = embedded_payload(wrb);
- memcpy(phba->mac_address, resp->mac_address, ETH_ALEN);
- rc = sysfs_format_mac(buf, phba->mac_address,
- ETH_ALEN);
- phba->read_mac_address = 1;
- return rc;
-}
+ memset(&resp, 0, sizeof(resp));
+ rc = mgmt_get_nic_conf(phba, &resp);
+ if (rc)
+ return rc;
+ phba->mac_addr_set = true;
+ memcpy(phba->mac_address, resp.mac_address, ETH_ALEN);
+ return sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
+}
/**
* beiscsi_conn_get_stats - get the iscsi stats
@@ -361,8 +829,12 @@ void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn,
struct iscsi_stats *stats)
{
struct iscsi_conn *conn = cls_conn->dd_data;
+ struct beiscsi_hba *phba = NULL;
+
+ phba = ((struct beiscsi_conn *)conn->dd_data)->phba;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_conn_get_stats\n");
- SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_stats\n");
stats->txdata_octets = conn->txdata_octets;
stats->rxdata_octets = conn->rxdata_octets;
stats->dataout_pdus = conn->dataout_pdus_cnt;
@@ -374,7 +846,7 @@ void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn,
stats->r2t_pdus = conn->r2t_pdus_cnt;
stats->digest_err = 0;
stats->timeout_err = 0;
- stats->custom_length = 0;
+ stats->custom_length = 1;
strcpy(stats->custom[0].desc, "eh_abort_cnt");
stats->custom[0].value = conn->eh_abort_cnt;
}
@@ -407,8 +879,20 @@ static void beiscsi_set_params_for_offld(struct beiscsi_conn *beiscsi_conn,
session->initial_r2t_en);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, imd, params,
session->imm_data_en);
+ AMAP_SET_BITS(struct amap_beiscsi_offload_params,
+ data_seq_inorder, params,
+ session->dataseq_inorder_en);
+ AMAP_SET_BITS(struct amap_beiscsi_offload_params,
+ pdu_seq_inorder, params,
+ session->pdu_inorder_en);
+ AMAP_SET_BITS(struct amap_beiscsi_offload_params, max_r2t, params,
+ session->max_r2t);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, exp_statsn, params,
(conn->exp_statsn - 1));
+ AMAP_SET_BITS(struct amap_beiscsi_offload_params,
+ max_recv_data_segment_length, params,
+ conn->max_recv_dlength);
+
}
/**
@@ -421,12 +905,24 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_offload_params params;
+ struct beiscsi_hba *phba;
+
+ phba = ((struct beiscsi_conn *)conn->dd_data)->phba;
+
+ if (!beiscsi_hba_is_online(phba)) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : HBA in error 0x%lx\n", phba->state);
+ return -EBUSY;
+ }
+ beiscsi_log(beiscsi_conn->phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_conn_start\n");
- SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_start\n");
memset(&params, 0, sizeof(struct beiscsi_offload_params));
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep)
- SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n");
+ beiscsi_log(beiscsi_conn->phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_conn_start , no beiscsi_ep\n");
beiscsi_conn->login_in_progress = 0;
beiscsi_set_params_for_offld(beiscsi_conn, &params);
@@ -441,15 +937,38 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
*/
static int beiscsi_get_cid(struct beiscsi_hba *phba)
{
- unsigned short cid = 0xFFFF;
-
- if (!phba->avlbl_cids)
- return cid;
-
- cid = phba->cid_array[phba->cid_alloc++];
- if (phba->cid_alloc == phba->params.cxns_per_ctrl)
- phba->cid_alloc = 0;
- phba->avlbl_cids--;
+ uint16_t cid_avlbl_ulp0, cid_avlbl_ulp1;
+ unsigned short cid, cid_from_ulp;
+ struct ulp_cid_info *cid_info;
+
+ /* Find the ULP which has more CID available */
+ cid_avlbl_ulp0 = (phba->cid_array_info[BEISCSI_ULP0]) ?
+ BEISCSI_ULP0_AVLBL_CID(phba) : 0;
+ cid_avlbl_ulp1 = (phba->cid_array_info[BEISCSI_ULP1]) ?
+ BEISCSI_ULP1_AVLBL_CID(phba) : 0;
+ cid_from_ulp = (cid_avlbl_ulp0 > cid_avlbl_ulp1) ?
+ BEISCSI_ULP0 : BEISCSI_ULP1;
+ /**
+ * If iSCSI protocol is loaded only on ULP 0, and when cid_avlbl_ulp
+ * is ZERO for both, ULP 1 is returned.
+ * Check if ULP is loaded before getting new CID.
+ */
+ if (!test_bit(cid_from_ulp, (void *)&phba->fw_config.ulp_supported))
+ return BE_INVALID_CID;
+
+ cid_info = phba->cid_array_info[cid_from_ulp];
+ cid = cid_info->cid_array[cid_info->cid_alloc];
+ if (!cid_info->avlbl_cids || cid == BE_INVALID_CID) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : failed to get cid: available %u:%u\n",
+ cid_info->avlbl_cids, cid_info->cid_free);
+ return BE_INVALID_CID;
+ }
+ /* empty the slot */
+ cid_info->cid_array[cid_info->cid_alloc++] = BE_INVALID_CID;
+ if (cid_info->cid_alloc == BEISCSI_GET_CID_COUNT(phba, cid_from_ulp))
+ cid_info->cid_alloc = 0;
+ cid_info->avlbl_cids--;
return cid;
}
@@ -460,29 +979,71 @@ static int beiscsi_get_cid(struct beiscsi_hba *phba)
*/
static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid)
{
- phba->avlbl_cids++;
- phba->cid_array[phba->cid_free++] = cid;
- if (phba->cid_free == phba->params.cxns_per_ctrl)
- phba->cid_free = 0;
+ uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
+ struct hwi_wrb_context *pwrb_context;
+ struct hwi_controller *phwi_ctrlr;
+ struct ulp_cid_info *cid_info;
+ uint16_t cid_post_ulp;
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
+ cid_post_ulp = pwrb_context->ulp_num;
+
+ cid_info = phba->cid_array_info[cid_post_ulp];
+ /* fill only in empty slot */
+ if (cid_info->cid_array[cid_info->cid_free] != BE_INVALID_CID) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : failed to put cid %u: available %u:%u\n",
+ cid, cid_info->avlbl_cids, cid_info->cid_free);
+ return;
+ }
+ cid_info->cid_array[cid_info->cid_free++] = cid;
+ if (cid_info->cid_free == BEISCSI_GET_CID_COUNT(phba, cid_post_ulp))
+ cid_info->cid_free = 0;
+ cid_info->avlbl_cids++;
}
/**
* beiscsi_free_ep - free endpoint
- * @ep: pointer to iscsi endpoint structure
+ * @beiscsi_ep: pointer to device endpoint struct
*/
static void beiscsi_free_ep(struct beiscsi_endpoint *beiscsi_ep)
{
struct beiscsi_hba *phba = beiscsi_ep->phba;
+ struct beiscsi_conn *beiscsi_conn;
beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
beiscsi_ep->phba = NULL;
+ /* clear this to track freeing in beiscsi_ep_disconnect */
+ phba->ep_array[BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid)] = NULL;
+
+ /**
+ * Check if any connection resource allocated by driver
+ * is to be freed.This case occurs when target redirection
+ * or connection retry is done.
+ **/
+ if (!beiscsi_ep->conn)
+ return;
+
+ beiscsi_conn = beiscsi_ep->conn;
+ /**
+ * Break ep->conn link here so that completions after
+ * this are ignored.
+ */
+ beiscsi_ep->conn = NULL;
+ if (beiscsi_conn->login_in_progress) {
+ beiscsi_free_mgmt_task_handles(beiscsi_conn,
+ beiscsi_conn->task);
+ beiscsi_conn->login_in_progress = 0;
+ }
}
/**
* beiscsi_open_conn - Ask FW to open a TCP connection
- * @ep: endpoint to be used
+ * @ep: pointer to device endpoint struct
* @src_addr: The source IP address
* @dst_addr: The Destination IP address
+ * @non_blocking: blocking or non-blocking call
*
* Asks the FW to open a TCP connection
*/
@@ -492,89 +1053,91 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
struct beiscsi_hba *phba = beiscsi_ep->phba;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
- struct be_mcc_wrb *wrb;
struct tcp_connect_and_offload_out *ptcpcnct_out;
- unsigned short status, extd_status;
struct be_dma_mem nonemb_cmd;
- unsigned int tag, wrb_num;
+ unsigned int tag, req_memsize;
int ret = -ENOMEM;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn\n");
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_open_conn\n");
+
beiscsi_ep->ep_cid = beiscsi_get_cid(phba);
- if (beiscsi_ep->ep_cid == 0xFFFF) {
- SE_DEBUG(DBG_LVL_1, "No free cid available\n");
+ if (beiscsi_ep->ep_cid == BE_INVALID_CID) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : No free cid available\n");
return ret;
}
- SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn, ep_cid=%d\n",
- beiscsi_ep->ep_cid);
- phba->ep_array[beiscsi_ep->ep_cid -
- phba->fw_config.iscsi_cid_start] = ep;
- if (beiscsi_ep->ep_cid > (phba->fw_config.iscsi_cid_start +
- phba->params.cxns_per_ctrl * 2)) {
- SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n");
- goto free_ep;
- }
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_open_conn, ep_cid=%d\n",
+ beiscsi_ep->ep_cid);
+
+ phba->ep_array[BE_GET_CRI_FROM_CID
+ (beiscsi_ep->ep_cid)] = ep;
beiscsi_ep->cid_vld = 0;
- nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
- sizeof(struct tcp_connect_and_offload_in),
- &nonemb_cmd.dma);
+
+ if (is_chip_be2_be3r(phba))
+ req_memsize = sizeof(struct tcp_connect_and_offload_in);
+ else
+ req_memsize = sizeof(struct tcp_connect_and_offload_in_v1);
+
+ nonemb_cmd.va = dma_alloc_coherent(&phba->ctrl.pdev->dev,
+ req_memsize,
+ &nonemb_cmd.dma, GFP_KERNEL);
if (nonemb_cmd.va == NULL) {
- SE_DEBUG(DBG_LVL_1,
- "Failed to allocate memory for mgmt_open_connection"
- "\n");
- beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
+
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Failed to allocate memory for"
+ " mgmt_open_connection\n");
+
+ beiscsi_free_ep(beiscsi_ep);
return -ENOMEM;
}
- nonemb_cmd.size = sizeof(struct tcp_connect_and_offload_in);
+ nonemb_cmd.size = req_memsize;
memset(nonemb_cmd.va, 0, nonemb_cmd.size);
tag = mgmt_open_connection(phba, dst_addr, beiscsi_ep, &nonemb_cmd);
if (!tag) {
- SE_DEBUG(DBG_LVL_1,
- "mgmt_open_connection Failed for cid=%d\n",
- beiscsi_ep->ep_cid);
- beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : mgmt_open_connection Failed for cid=%d\n",
+ beiscsi_ep->ep_cid);
+
+ dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
+ beiscsi_free_ep(beiscsi_ep);
return -EAGAIN;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- }
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
- SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed"
- " status = %d extd_status = %d\n",
- status, extd_status);
- free_mcc_tag(&phba->ctrl, tag);
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
- goto free_ep;
- } else {
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
+ }
- ptcpcnct_out = embedded_payload(wrb);
- beiscsi_ep = ep->dd_data;
- beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
- beiscsi_ep->cid_vld = 1;
- SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
+ ret = beiscsi_mccq_compl_wait(phba, tag, NULL, &nonemb_cmd);
+ if (ret) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BS_%d : mgmt_open_connection Failed");
+
+ if (ret != -EBUSY)
+ dma_free_coherent(&phba->ctrl.pdev->dev,
+ nonemb_cmd.size, nonemb_cmd.va,
+ nonemb_cmd.dma);
+
+ beiscsi_free_ep(beiscsi_ep);
+ return ret;
}
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+
+ ptcpcnct_out = (struct tcp_connect_and_offload_out *)nonemb_cmd.va;
+ beiscsi_ep = ep->dd_data;
+ beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
+ beiscsi_ep->cid_vld = 1;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : mgmt_open_connection Success\n");
+
+ dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return 0;
-
-free_ep:
- beiscsi_free_ep(beiscsi_ep);
- return -EBUSY;
}
/**
* beiscsi_ep_connect - Ask chip to create TCP Conn
- * @scsi_host: Pointer to scsi_host structure
+ * @shost: Pointer to scsi_host structure
* @dst_addr: The IP address of Target
* @non_blocking: blocking or non-blocking call
*
@@ -589,18 +1152,23 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
struct iscsi_endpoint *ep;
int ret;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_connect\n");
- if (shost)
- phba = iscsi_host_priv(shost);
- else {
+ if (!shost) {
ret = -ENXIO;
- SE_DEBUG(DBG_LVL_1, "shost is NULL\n");
+ pr_err("beiscsi_ep_connect shost is NULL\n");
return ERR_PTR(ret);
}
- if (phba->state != BE_ADAPTER_UP) {
+ phba = iscsi_host_priv(shost);
+ if (!beiscsi_hba_is_online(phba)) {
+ ret = -EIO;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : HBA in error 0x%lx\n", phba->state);
+ return ERR_PTR(ret);
+ }
+ if (!test_bit(BEISCSI_HBA_LINK_UP, &phba->state)) {
ret = -EBUSY;
- SE_DEBUG(DBG_LVL_1, "The Adapter state is Not UP\n");
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BS_%d : The Adapter Port state is Down!!!\n");
return ERR_PTR(ret);
}
@@ -615,7 +1183,8 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
beiscsi_ep->openiscsi_ep = ep;
ret = beiscsi_open_conn(ep, NULL, dst_addr, non_blocking);
if (ret) {
- SE_DEBUG(DBG_LVL_1, "Failed in beiscsi_open_conn\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BS_%d : Failed in beiscsi_open_conn\n");
goto free_ep;
}
@@ -637,7 +1206,9 @@ int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_poll\n");
+ beiscsi_log(beiscsi_ep->phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_ep_poll\n");
+
if (beiscsi_ep->cid_vld == 1)
return 1;
else
@@ -645,43 +1216,82 @@ int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
}
/**
- * beiscsi_close_conn - Upload the connection
- * @ep: The iscsi endpoint
- * @flag: The type of connection closure
- */
-static int beiscsi_close_conn(struct beiscsi_endpoint *beiscsi_ep, int flag)
+ * beiscsi_flush_cq()- Flush the CQ created.
+ * @phba: ptr device priv structure.
+ *
+ * Before the connection resource are freed flush
+ * all the CQ enteries
+ **/
+static void beiscsi_flush_cq(struct beiscsi_hba *phba)
{
- int ret = 0;
- unsigned int tag;
- struct beiscsi_hba *phba = beiscsi_ep->phba;
-
- tag = mgmt_upload_connection(phba, beiscsi_ep->ep_cid, flag);
- if (!tag) {
- SE_DEBUG(DBG_LVL_8, "upload failed for cid 0x%x\n",
- beiscsi_ep->ep_cid);
- ret = -EAGAIN;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
+ uint16_t i;
+ struct be_eq_obj *pbe_eq;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ irq_poll_disable(&pbe_eq->iopoll);
+ beiscsi_process_cq(pbe_eq, BE2_MAX_NUM_CQ_PROC);
+ irq_poll_enable(&pbe_eq->iopoll);
}
- return ret;
}
/**
- * beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table
- * @phba: The phba instance
- * @cid: The cid to free
+ * beiscsi_conn_close - Invalidate and upload connection
+ * @beiscsi_ep: pointer to device endpoint struct
+ *
+ * Returns 0 on success, -1 on failure.
*/
-static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba,
- unsigned int cid)
+static int beiscsi_conn_close(struct beiscsi_endpoint *beiscsi_ep)
{
- if (phba->conn_table[cid])
- phba->conn_table[cid] = NULL;
- else {
- SE_DEBUG(DBG_LVL_8, "Connection table Not occupied.\n");
- return -EINVAL;
+ struct beiscsi_hba *phba = beiscsi_ep->phba;
+ unsigned int tag, attempts;
+ int ret;
+
+ /**
+ * Without successfully invalidating and uploading connection
+ * driver can't reuse the CID so attempt more than once.
+ */
+ attempts = 0;
+ while (attempts++ < 3) {
+ tag = beiscsi_invalidate_cxn(phba, beiscsi_ep);
+ if (tag) {
+ ret = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL);
+ if (!ret)
+ break;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : invalidate conn failed cid %d\n",
+ beiscsi_ep->ep_cid);
+ }
}
+
+ /* wait for all completions to arrive, then process them */
+ msleep(250);
+ /* flush CQ entries */
+ beiscsi_flush_cq(phba);
+
+ if (attempts > 3)
+ return -1;
+
+ attempts = 0;
+ while (attempts++ < 3) {
+ tag = beiscsi_upload_cxn(phba, beiscsi_ep);
+ if (tag) {
+ ret = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL);
+ if (!ret)
+ break;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : upload conn failed cid %d\n",
+ beiscsi_ep->ep_cid);
+ }
+ }
+ if (attempts > 3)
+ return -1;
+
return 0;
}
@@ -693,43 +1303,113 @@ static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba,
*/
void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
{
- struct beiscsi_conn *beiscsi_conn;
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_hba *phba;
- unsigned int tag;
- unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH;
+ uint16_t cri_index;
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect for ep_cid = %d\n",
- beiscsi_ep->ep_cid);
-
- if (!beiscsi_ep->conn) {
- SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect, no "
- "beiscsi_ep\n");
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : In beiscsi_ep_disconnect for ep_cid = %u\n",
+ beiscsi_ep->ep_cid);
+
+ cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid);
+ if (!phba->ep_array[cri_index]) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : ep_array at %u cid %u empty\n",
+ cri_index,
+ beiscsi_ep->ep_cid);
return;
}
- beiscsi_conn = beiscsi_ep->conn;
- iscsi_suspend_queue(beiscsi_conn->conn);
-
- SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect ep_cid = %d\n",
- beiscsi_ep->ep_cid);
- tag = mgmt_invalidate_connection(phba, beiscsi_ep,
- beiscsi_ep->ep_cid, 1,
- savecfg_flag);
- if (!tag) {
- SE_DEBUG(DBG_LVL_1,
- "mgmt_invalidate_connection Failed for cid=%d\n",
- beiscsi_ep->ep_cid);
+ if (!beiscsi_hba_is_online(phba)) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : HBA in error 0x%lx\n", phba->state);
} else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
+ /**
+ * Make CID available even if close fails.
+ * If not freed, FW might fail open using the CID.
+ */
+ if (beiscsi_conn_close(beiscsi_ep) < 0)
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : close conn failed cid %d\n",
+ beiscsi_ep->ep_cid);
}
- beiscsi_close_conn(beiscsi_ep, CONNECTION_UPLOAD_GRACEFUL);
beiscsi_free_ep(beiscsi_ep);
- beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid);
+ if (!phba->conn_table[cri_index])
+ __beiscsi_log(phba, KERN_ERR,
+ "BS_%d : conn_table empty at %u: cid %u\n",
+ cri_index, beiscsi_ep->ep_cid);
+ phba->conn_table[cri_index] = NULL;
iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep);
}
+
+umode_t beiscsi_attr_is_visible(int param_type, int param)
+{
+ switch (param_type) {
+ case ISCSI_NET_PARAM:
+ switch (param) {
+ case ISCSI_NET_PARAM_IFACE_ENABLE:
+ case ISCSI_NET_PARAM_IPV4_ADDR:
+ case ISCSI_NET_PARAM_IPV4_SUBNET:
+ case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
+ case ISCSI_NET_PARAM_IPV4_GW:
+ case ISCSI_NET_PARAM_IPV6_ADDR:
+ case ISCSI_NET_PARAM_VLAN_ID:
+ case ISCSI_NET_PARAM_VLAN_PRIORITY:
+ case ISCSI_NET_PARAM_VLAN_ENABLED:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case ISCSI_HOST_PARAM:
+ switch (param) {
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ case ISCSI_HOST_PARAM_INITIATOR_NAME:
+ case ISCSI_HOST_PARAM_PORT_STATE:
+ case ISCSI_HOST_PARAM_PORT_SPEED:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case ISCSI_PARAM:
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ case ISCSI_PARAM_HDRDGST_EN:
+ case ISCSI_PARAM_DATADGST_EN:
+ case ISCSI_PARAM_CONN_ADDRESS:
+ case ISCSI_PARAM_CONN_PORT:
+ case ISCSI_PARAM_EXP_STATSN:
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ case ISCSI_PARAM_PING_TMO:
+ case ISCSI_PARAM_RECV_TMO:
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ case ISCSI_PARAM_MAX_R2T:
+ case ISCSI_PARAM_IMM_DATA_EN:
+ case ISCSI_PARAM_FIRST_BURST:
+ case ISCSI_PARAM_MAX_BURST:
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ case ISCSI_PARAM_ERL:
+ case ISCSI_PARAM_TARGET_NAME:
+ case ISCSI_PARAM_TPGT:
+ case ISCSI_PARAM_USERNAME:
+ case ISCSI_PARAM_PASSWORD:
+ case ISCSI_PARAM_USERNAME_IN:
+ case ISCSI_PARAM_PASSWORD_IN:
+ case ISCSI_PARAM_FAST_ABORT:
+ case ISCSI_PARAM_ABORT_TMO:
+ case ISCSI_PARAM_LU_RESET_TMO:
+ case ISCSI_PARAM_IFACE_NAME:
+ case ISCSI_PARAM_INITIATOR_NAME:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index ff60b7fd92d6..8c88657eed0d 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -1,20 +1,10 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
- *
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2017 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
*
* Contact Information:
- * linux-drivers@emulex.com
- *
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
+ * linux-drivers@broadcom.com
*/
#ifndef _BE_ISCSI_
@@ -23,8 +13,18 @@
#include "be_main.h"
#include "be_mgmt.h"
-#define BE2_IPV4 0x1
-#define BE2_IPV6 0x10
+void beiscsi_iface_create_default(struct beiscsi_hba *phba);
+
+void beiscsi_iface_destroy_default(struct beiscsi_hba *phba);
+
+int beiscsi_iface_get_param(struct iscsi_iface *iface,
+ enum iscsi_param_type param_type,
+ int param, char *buf);
+
+int beiscsi_iface_set_param(struct Scsi_Host *shost,
+ void *data, uint32_t count);
+
+umode_t beiscsi_attr_is_visible(int param_type, int param);
void beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_offload_params *params);
@@ -40,6 +40,8 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
void beiscsi_session_destroy(struct iscsi_cls_session *cls_session);
+void beiscsi_session_fail(struct iscsi_cls_session *cls_session);
+
struct iscsi_cls_conn *beiscsi_conn_create(struct iscsi_cls_session
*cls_session, uint32_t cid);
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 94b9a07845d5..a0e794ffc980 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -1,20 +1,26 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
+/*
+ * This file is part of the Emulex Linux Device Driver for Enterprise iSCSI
+ * Host Bus Adapters. Refer to the README file included with this package
+ * for driver version and adapter compatibility.
+ *
+ * Copyright (c) 2018 Broadcom. All Rights Reserved.
+ * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as published
+ * by the Free Software Foundation.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * This program is distributed in the hope that it will be useful. ALL EXPRESS
+ * OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+ * OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH
+ * DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
+ * See the GNU General Public License for more details, a copy of which
+ * can be found in the file COPYING included with this package.
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@broadcom.com
*
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
*/
#include <linux/reboot.h>
@@ -27,8 +33,13 @@
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/iscsi_boot_sysfs.h>
+#include <linux/module.h>
+#include <linux/bsg-lib.h>
+#include <linux/irq_poll.h>
#include <scsi/libiscsi.h>
+#include <scsi/scsi_bsg_iscsi.h>
+#include <scsi/scsi_netlink.h>
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_cmnd.h>
@@ -38,421 +49,376 @@
#include "be_main.h"
#include "be_iscsi.h"
#include "be_mgmt.h"
+#include "be_cmds.h"
static unsigned int be_iopoll_budget = 10;
static unsigned int be_max_phys_size = 64;
static unsigned int enable_msix = 1;
-static unsigned int gcrashmode = 0;
-static unsigned int num_hba = 0;
-MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
-MODULE_AUTHOR("ServerEngines Corporation");
+MODULE_VERSION(BUILD_STR);
+MODULE_AUTHOR("Emulex Corporation");
MODULE_LICENSE("GPL");
module_param(be_iopoll_budget, int, 0);
module_param(enable_msix, int, 0);
module_param(be_max_phys_size, uint, S_IRUGO);
-MODULE_PARM_DESC(be_max_phys_size, "Maximum Size (In Kilobytes) of physically"
- "contiguous memory that can be allocated."
- "Range is 16 - 128");
+MODULE_PARM_DESC(be_max_phys_size,
+ "Maximum Size (In Kilobytes) of physically contiguous "
+ "memory that can be allocated. Range is 16 - 128");
+
+#define beiscsi_disp_param(_name)\
+static ssize_t \
+beiscsi_##_name##_disp(struct device *dev,\
+ struct device_attribute *attrib, char *buf) \
+{ \
+ struct Scsi_Host *shost = class_to_shost(dev);\
+ struct beiscsi_hba *phba = iscsi_host_priv(shost); \
+ return snprintf(buf, PAGE_SIZE, "%d\n",\
+ phba->attr_##_name);\
+}
-static int beiscsi_slave_configure(struct scsi_device *sdev)
-{
- blk_queue_max_segment_size(sdev->request_queue, 65536);
- return 0;
+#define beiscsi_change_param(_name, _minval, _maxval, _defaval)\
+static int \
+beiscsi_##_name##_change(struct beiscsi_hba *phba, uint32_t val)\
+{\
+ if (val >= _minval && val <= _maxval) {\
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,\
+ "BA_%d : beiscsi_"#_name" updated "\
+ "from 0x%x ==> 0x%x\n",\
+ phba->attr_##_name, val); \
+ phba->attr_##_name = val;\
+ return 0;\
+ } \
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, \
+ "BA_%d beiscsi_"#_name" attribute "\
+ "cannot be updated to 0x%x, "\
+ "range allowed is ["#_minval" - "#_maxval"]\n", val);\
+ return -EINVAL;\
}
+#define beiscsi_store_param(_name) \
+static ssize_t \
+beiscsi_##_name##_store(struct device *dev,\
+ struct device_attribute *attr, const char *buf,\
+ size_t count) \
+{ \
+ struct Scsi_Host *shost = class_to_shost(dev);\
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);\
+ uint32_t param_val = 0;\
+ if (!isdigit(buf[0]))\
+ return -EINVAL;\
+ if (sscanf(buf, "%i", &param_val) != 1)\
+ return -EINVAL;\
+ if (beiscsi_##_name##_change(phba, param_val) == 0) \
+ return strlen(buf);\
+ else \
+ return -EINVAL;\
+}
+
+#define beiscsi_init_param(_name, _minval, _maxval, _defval) \
+static int \
+beiscsi_##_name##_init(struct beiscsi_hba *phba, uint32_t val) \
+{ \
+ if (val >= _minval && val <= _maxval) {\
+ phba->attr_##_name = val;\
+ return 0;\
+ } \
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,\
+ "BA_%d beiscsi_"#_name" attribute " \
+ "cannot be updated to 0x%x, "\
+ "range allowed is ["#_minval" - "#_maxval"]\n", val);\
+ phba->attr_##_name = _defval;\
+ return -EINVAL;\
+}
+
+#define BEISCSI_RW_ATTR(_name, _minval, _maxval, _defval, _descp) \
+static uint beiscsi_##_name = _defval;\
+module_param(beiscsi_##_name, uint, S_IRUGO);\
+MODULE_PARM_DESC(beiscsi_##_name, _descp);\
+beiscsi_disp_param(_name)\
+beiscsi_change_param(_name, _minval, _maxval, _defval)\
+beiscsi_store_param(_name)\
+beiscsi_init_param(_name, _minval, _maxval, _defval)\
+static DEVICE_ATTR(beiscsi_##_name, S_IRUGO | S_IWUSR,\
+ beiscsi_##_name##_disp, beiscsi_##_name##_store)
+
+/*
+ * When new log level added update MAX allowed value for log_enable
+ */
+BEISCSI_RW_ATTR(log_enable, 0x00,
+ 0xFF, 0x00, "Enable logging Bit Mask\n"
+ "\t\t\t\tInitialization Events : 0x01\n"
+ "\t\t\t\tMailbox Events : 0x02\n"
+ "\t\t\t\tMiscellaneous Events : 0x04\n"
+ "\t\t\t\tError Handling : 0x08\n"
+ "\t\t\t\tIO Path Events : 0x10\n"
+ "\t\t\t\tConfiguration Path : 0x20\n"
+ "\t\t\t\tiSCSI Protocol : 0x40\n");
+
+static DEVICE_ATTR(beiscsi_drvr_ver, S_IRUGO, beiscsi_drvr_ver_disp, NULL);
+static DEVICE_ATTR(beiscsi_adapter_family, S_IRUGO, beiscsi_adap_family_disp, NULL);
+static DEVICE_ATTR(beiscsi_fw_ver, S_IRUGO, beiscsi_fw_ver_disp, NULL);
+static DEVICE_ATTR(beiscsi_phys_port, S_IRUGO, beiscsi_phys_port_disp, NULL);
+static DEVICE_ATTR(beiscsi_active_session_count, S_IRUGO,
+ beiscsi_active_session_disp, NULL);
+static DEVICE_ATTR(beiscsi_free_session_count, S_IRUGO,
+ beiscsi_free_session_disp, NULL);
+
+static struct attribute *beiscsi_attrs[] = {
+ &dev_attr_beiscsi_log_enable.attr,
+ &dev_attr_beiscsi_drvr_ver.attr,
+ &dev_attr_beiscsi_adapter_family.attr,
+ &dev_attr_beiscsi_fw_ver.attr,
+ &dev_attr_beiscsi_active_session_count.attr,
+ &dev_attr_beiscsi_free_session_count.attr,
+ &dev_attr_beiscsi_phys_port.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(beiscsi);
+
+static char const *cqe_desc[] = {
+ "RESERVED_DESC",
+ "SOL_CMD_COMPLETE",
+ "SOL_CMD_KILLED_DATA_DIGEST_ERR",
+ "CXN_KILLED_PDU_SIZE_EXCEEDS_DSL",
+ "CXN_KILLED_BURST_LEN_MISMATCH",
+ "CXN_KILLED_AHS_RCVD",
+ "CXN_KILLED_HDR_DIGEST_ERR",
+ "CXN_KILLED_UNKNOWN_HDR",
+ "CXN_KILLED_STALE_ITT_TTT_RCVD",
+ "CXN_KILLED_INVALID_ITT_TTT_RCVD",
+ "CXN_KILLED_RST_RCVD",
+ "CXN_KILLED_TIMED_OUT",
+ "CXN_KILLED_RST_SENT",
+ "CXN_KILLED_FIN_RCVD",
+ "CXN_KILLED_BAD_UNSOL_PDU_RCVD",
+ "CXN_KILLED_BAD_WRB_INDEX_ERROR",
+ "CXN_KILLED_OVER_RUN_RESIDUAL",
+ "CXN_KILLED_UNDER_RUN_RESIDUAL",
+ "CMD_KILLED_INVALID_STATSN_RCVD",
+ "CMD_KILLED_INVALID_R2T_RCVD",
+ "CMD_CXN_KILLED_LUN_INVALID",
+ "CMD_CXN_KILLED_ICD_INVALID",
+ "CMD_CXN_KILLED_ITT_INVALID",
+ "CMD_CXN_KILLED_SEQ_OUTOFORDER",
+ "CMD_CXN_KILLED_INVALID_DATASN_RCVD",
+ "CXN_INVALIDATE_NOTIFY",
+ "CXN_INVALIDATE_INDEX_NOTIFY",
+ "CMD_INVALIDATED_NOTIFY",
+ "UNSOL_HDR_NOTIFY",
+ "UNSOL_DATA_NOTIFY",
+ "UNSOL_DATA_DIGEST_ERROR_NOTIFY",
+ "DRIVERMSG_NOTIFY",
+ "CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN",
+ "SOL_CMD_KILLED_DIF_ERR",
+ "CXN_KILLED_SYN_RCVD",
+ "CXN_KILLED_IMM_DATA_RCVD"
+};
+
static int beiscsi_eh_abort(struct scsi_cmnd *sc)
{
+ struct iscsi_task *abrt_task = iscsi_cmd(sc)->task;
struct iscsi_cls_session *cls_session;
- struct iscsi_task *aborted_task = (struct iscsi_task *)sc->SCp.ptr;
- struct beiscsi_io_task *aborted_io_task;
- struct iscsi_conn *conn;
+ struct beiscsi_io_task *abrt_io_task;
struct beiscsi_conn *beiscsi_conn;
- struct beiscsi_hba *phba;
struct iscsi_session *session;
- struct invalidate_command_table *inv_tbl;
- struct be_dma_mem nonemb_cmd;
- unsigned int cid, tag, num_invalidate;
+ struct invldt_cmd_tbl inv_tbl;
+ struct beiscsi_hba *phba;
+ struct iscsi_conn *conn;
+ int rc;
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
- spin_lock_bh(&session->lock);
- if (!aborted_task || !aborted_task->sc) {
- /* we raced */
- spin_unlock_bh(&session->lock);
+completion_check:
+ /* check if we raced, task just got cleaned up under us */
+ spin_lock_bh(&session->back_lock);
+ if (!abrt_task || !abrt_task->sc) {
+ spin_unlock_bh(&session->back_lock);
return SUCCESS;
}
-
- aborted_io_task = aborted_task->dd_data;
- if (!aborted_io_task->scsi_cmnd) {
- /* raced or invalid command */
- spin_unlock_bh(&session->lock);
- return SUCCESS;
+ /* get a task ref till FW processes the req for the ICD used */
+ if (!iscsi_get_task(abrt_task)) {
+ spin_unlock(&session->back_lock);
+ /* We are just about to call iscsi_free_task so wait for it. */
+ udelay(5);
+ goto completion_check;
}
- spin_unlock_bh(&session->lock);
- conn = aborted_task->conn;
+
+ abrt_io_task = abrt_task->dd_data;
+ conn = abrt_task->conn;
beiscsi_conn = conn->dd_data;
phba = beiscsi_conn->phba;
-
- /* invalidate iocb */
- cid = beiscsi_conn->beiscsi_conn_cid;
- inv_tbl = phba->inv_tbl;
- memset(inv_tbl, 0x0, sizeof(*inv_tbl));
- inv_tbl->cid = cid;
- inv_tbl->icd = aborted_io_task->psgl_handle->sgl_index;
- num_invalidate = 1;
- nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
- sizeof(struct invalidate_commands_params_in),
- &nonemb_cmd.dma);
- if (nonemb_cmd.va == NULL) {
- SE_DEBUG(DBG_LVL_1,
- "Failed to allocate memory for"
- "mgmt_invalidate_icds\n");
+ /* mark WRB invalid which have been not processed by FW yet */
+ if (is_chip_be2_be3r(phba)) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, invld,
+ abrt_io_task->pwrb_handle->pwrb, 1);
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, invld,
+ abrt_io_task->pwrb_handle->pwrb, 1);
+ }
+ inv_tbl.cid = beiscsi_conn->beiscsi_conn_cid;
+ inv_tbl.icd = abrt_io_task->psgl_handle->sgl_index;
+ spin_unlock_bh(&session->back_lock);
+
+ rc = beiscsi_mgmt_invalidate_icds(phba, &inv_tbl, 1);
+ iscsi_put_task(abrt_task);
+ if (rc) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_EH,
+ "BM_%d : sc %p invalidation failed %d\n",
+ sc, rc);
return FAILED;
}
- nonemb_cmd.size = sizeof(struct invalidate_commands_params_in);
- tag = mgmt_invalidate_icds(phba, inv_tbl, num_invalidate,
- cid, &nonemb_cmd);
- if (!tag) {
- shost_printk(KERN_WARNING, phba->shost,
- "mgmt_invalidate_icds could not be"
- " submitted\n");
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
-
- return FAILED;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
- }
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
return iscsi_eh_abort(sc);
}
static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
{
- struct iscsi_task *abrt_task;
- struct beiscsi_io_task *abrt_io_task;
- struct iscsi_conn *conn;
+ struct beiscsi_invldt_cmd_tbl {
+ struct invldt_cmd_tbl tbl[BE_INVLDT_CMD_TBL_SZ];
+ struct iscsi_task *task[BE_INVLDT_CMD_TBL_SZ];
+ } *inv_tbl;
+ struct iscsi_cls_session *cls_session;
struct beiscsi_conn *beiscsi_conn;
- struct beiscsi_hba *phba;
+ struct beiscsi_io_task *io_task;
struct iscsi_session *session;
- struct iscsi_cls_session *cls_session;
- struct invalidate_command_table *inv_tbl;
- struct be_dma_mem nonemb_cmd;
- unsigned int cid, tag, i, num_invalidate;
- int rc = FAILED;
+ struct beiscsi_hba *phba;
+ struct iscsi_conn *conn;
+ struct iscsi_task *task;
+ unsigned int i, nents;
+ int rc, more = 0;
- /* invalidate iocbs */
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
- spin_lock_bh(&session->lock);
- if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
- goto unlock;
+
+ spin_lock_bh(&session->frwd_lock);
+ if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) {
+ spin_unlock_bh(&session->frwd_lock);
+ return FAILED;
+ }
conn = session->leadconn;
beiscsi_conn = conn->dd_data;
phba = beiscsi_conn->phba;
- cid = beiscsi_conn->beiscsi_conn_cid;
- inv_tbl = phba->inv_tbl;
- memset(inv_tbl, 0x0, sizeof(*inv_tbl) * BE2_CMDS_PER_CXN);
- num_invalidate = 0;
- for (i = 0; i < conn->session->cmds_max; i++) {
- abrt_task = conn->session->cmds[i];
- abrt_io_task = abrt_task->dd_data;
- if (!abrt_task->sc || abrt_task->state == ISCSI_TASK_FREE)
- continue;
-
- if (abrt_task->sc->device->lun != abrt_task->sc->device->lun)
- continue;
- inv_tbl->cid = cid;
- inv_tbl->icd = abrt_io_task->psgl_handle->sgl_index;
- num_invalidate++;
- inv_tbl++;
- }
- spin_unlock_bh(&session->lock);
- inv_tbl = phba->inv_tbl;
-
- nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
- sizeof(struct invalidate_commands_params_in),
- &nonemb_cmd.dma);
- if (nonemb_cmd.va == NULL) {
- SE_DEBUG(DBG_LVL_1,
- "Failed to allocate memory for"
- "mgmt_invalidate_icds\n");
+ inv_tbl = kzalloc(sizeof(*inv_tbl), GFP_ATOMIC);
+ if (!inv_tbl) {
+ spin_unlock_bh(&session->frwd_lock);
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_EH,
+ "BM_%d : invldt_cmd_tbl alloc failed\n");
return FAILED;
}
- nonemb_cmd.size = sizeof(struct invalidate_commands_params_in);
- memset(nonemb_cmd.va, 0, nonemb_cmd.size);
- tag = mgmt_invalidate_icds(phba, inv_tbl, num_invalidate,
- cid, &nonemb_cmd);
- if (!tag) {
- shost_printk(KERN_WARNING, phba->shost,
- "mgmt_invalidate_icds could not be"
- " submitted\n");
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
- return FAILED;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
- }
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
- return iscsi_eh_device_reset(sc);
-unlock:
- spin_unlock_bh(&session->lock);
- return rc;
-}
-
-static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf)
-{
- struct beiscsi_hba *phba = data;
- char *str = buf;
- int rc;
-
- switch (type) {
- case ISCSI_BOOT_TGT_NAME:
- rc = sprintf(buf, "%.*s\n",
- (int)strlen(phba->boot_sess.target_name),
- (char *)&phba->boot_sess.target_name);
- break;
- case ISCSI_BOOT_TGT_IP_ADDR:
- if (phba->boot_sess.conn_list[0].dest_ipaddr.ip_type == 0x1)
- rc = sprintf(buf, "%pI4\n",
- (char *)&phba->boot_sess.conn_list[0].
- dest_ipaddr.ip_address);
- else
- rc = sprintf(str, "%pI6\n",
- (char *)&phba->boot_sess.conn_list[0].
- dest_ipaddr.ip_address);
- break;
- case ISCSI_BOOT_TGT_PORT:
- rc = sprintf(str, "%d\n", phba->boot_sess.conn_list[0].
- dest_port);
- break;
-
- case ISCSI_BOOT_TGT_CHAP_NAME:
- rc = sprintf(str, "%.*s\n",
- phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- target_chap_name_length,
- (char *)&phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- target_chap_name);
- break;
- case ISCSI_BOOT_TGT_CHAP_SECRET:
- rc = sprintf(str, "%.*s\n",
- phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- target_secret_length,
- (char *)&phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- target_secret);
-
- break;
- case ISCSI_BOOT_TGT_REV_CHAP_NAME:
- rc = sprintf(str, "%.*s\n",
- phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- intr_chap_name_length,
- (char *)&phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- intr_chap_name);
-
- break;
- case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
- rc = sprintf(str, "%.*s\n",
- phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- intr_secret_length,
- (char *)&phba->boot_sess.conn_list[0].
- negotiated_login_options.auth_data.chap.
- intr_secret);
- break;
- case ISCSI_BOOT_TGT_FLAGS:
- rc = sprintf(str, "2\n");
- break;
- case ISCSI_BOOT_TGT_NIC_ASSOC:
- rc = sprintf(str, "0\n");
- break;
- default:
- rc = -ENOSYS;
- break;
- }
- return rc;
-}
-
-static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf)
-{
- struct beiscsi_hba *phba = data;
- char *str = buf;
- int rc;
-
- switch (type) {
- case ISCSI_BOOT_INI_INITIATOR_NAME:
- rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname);
- break;
- default:
- rc = -ENOSYS;
- break;
- }
- return rc;
-}
-
-static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf)
-{
- struct beiscsi_hba *phba = data;
- char *str = buf;
- int rc;
+ nents = 0;
+ /* take back_lock to prevent task from getting cleaned up under us */
+ spin_lock(&session->back_lock);
+ for (i = 0; i < conn->session->cmds_max; i++) {
+ task = conn->session->cmds[i];
+ if (!task->sc)
+ continue;
- switch (type) {
- case ISCSI_BOOT_ETH_FLAGS:
- rc = sprintf(str, "2\n");
- break;
- case ISCSI_BOOT_ETH_INDEX:
- rc = sprintf(str, "0\n");
- break;
- case ISCSI_BOOT_ETH_MAC:
- rc = beiscsi_get_macaddr(buf, phba);
- if (rc < 0) {
- SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n");
- return rc;
+ if (sc->device->lun != task->sc->device->lun)
+ continue;
+ /**
+ * Can't fit in more cmds? Normally this won't happen b'coz
+ * BEISCSI_CMD_PER_LUN is same as BE_INVLDT_CMD_TBL_SZ.
+ */
+ if (nents == BE_INVLDT_CMD_TBL_SZ) {
+ more = 1;
+ break;
}
- break;
- default:
- rc = -ENOSYS;
- break;
- }
- return rc;
-}
+ /* get a task ref till FW processes the req for the ICD used */
+ if (!iscsi_get_task(task)) {
+ /*
+ * The task has completed in the driver and is
+ * completing in libiscsi. Just ignore it here. When we
+ * call iscsi_eh_device_reset, it will wait for us.
+ */
+ continue;
+ }
-static mode_t beiscsi_tgt_get_attr_visibility(void *data, int type)
-{
- int rc;
+ io_task = task->dd_data;
+ /* mark WRB invalid which have been not processed by FW yet */
+ if (is_chip_be2_be3r(phba)) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, invld,
+ io_task->pwrb_handle->pwrb, 1);
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, invld,
+ io_task->pwrb_handle->pwrb, 1);
+ }
- switch (type) {
- case ISCSI_BOOT_TGT_NAME:
- case ISCSI_BOOT_TGT_IP_ADDR:
- case ISCSI_BOOT_TGT_PORT:
- case ISCSI_BOOT_TGT_CHAP_NAME:
- case ISCSI_BOOT_TGT_CHAP_SECRET:
- case ISCSI_BOOT_TGT_REV_CHAP_NAME:
- case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
- case ISCSI_BOOT_TGT_NIC_ASSOC:
- case ISCSI_BOOT_TGT_FLAGS:
- rc = S_IRUGO;
- break;
- default:
- rc = 0;
- break;
+ inv_tbl->tbl[nents].cid = beiscsi_conn->beiscsi_conn_cid;
+ inv_tbl->tbl[nents].icd = io_task->psgl_handle->sgl_index;
+ inv_tbl->task[nents] = task;
+ nents++;
}
- return rc;
-}
+ spin_unlock(&session->back_lock);
+ spin_unlock_bh(&session->frwd_lock);
-static mode_t beiscsi_ini_get_attr_visibility(void *data, int type)
-{
- int rc;
+ rc = SUCCESS;
+ if (!nents)
+ goto end_reset;
- switch (type) {
- case ISCSI_BOOT_INI_INITIATOR_NAME:
- rc = S_IRUGO;
- break;
- default:
- rc = 0;
- break;
+ if (more) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_EH,
+ "BM_%d : number of cmds exceeds size of invalidation table\n");
+ rc = FAILED;
+ goto end_reset;
}
- return rc;
-}
-
-static mode_t beiscsi_eth_get_attr_visibility(void *data, int type)
-{
- int rc;
-
- switch (type) {
- case ISCSI_BOOT_ETH_FLAGS:
- case ISCSI_BOOT_ETH_MAC:
- case ISCSI_BOOT_ETH_INDEX:
- rc = S_IRUGO;
- break;
- default:
- rc = 0;
- break;
+ if (beiscsi_mgmt_invalidate_icds(phba, &inv_tbl->tbl[0], nents)) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_EH,
+ "BM_%d : cid %u scmds invalidation failed\n",
+ beiscsi_conn->beiscsi_conn_cid);
+ rc = FAILED;
}
- return rc;
-}
-static int beiscsi_setup_boot_info(struct beiscsi_hba *phba)
-{
- struct iscsi_boot_kobj *boot_kobj;
-
- phba->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no);
- if (!phba->boot_kset)
- return -ENOMEM;
+end_reset:
+ for (i = 0; i < nents; i++)
+ iscsi_put_task(inv_tbl->task[i]);
+ kfree(inv_tbl);
- /* get boot info using mgmt cmd */
- boot_kobj = iscsi_boot_create_target(phba->boot_kset, 0, phba,
- beiscsi_show_boot_tgt_info,
- beiscsi_tgt_get_attr_visibility);
- if (!boot_kobj)
- goto free_kset;
-
- boot_kobj = iscsi_boot_create_initiator(phba->boot_kset, 0, phba,
- beiscsi_show_boot_ini_info,
- beiscsi_ini_get_attr_visibility);
- if (!boot_kobj)
- goto free_kset;
-
- boot_kobj = iscsi_boot_create_ethernet(phba->boot_kset, 0, phba,
- beiscsi_show_boot_eth_info,
- beiscsi_eth_get_attr_visibility);
- if (!boot_kobj)
- goto free_kset;
- return 0;
-
-free_kset:
- if (phba->boot_kset)
- iscsi_boot_destroy_kset(phba->boot_kset);
- return -ENOMEM;
+ if (rc == SUCCESS)
+ rc = iscsi_eh_device_reset(sc);
+ return rc;
}
/*------------------- PCI Driver operations and data ----------------- */
-static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
+static const struct pci_device_id beiscsi_pci_id_table[] = {
{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) },
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) },
+ { PCI_DEVICE(ELX_VENDOR_ID, OC_SKH_ID1) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
-static struct scsi_host_template beiscsi_sht = {
+
+static const struct scsi_host_template beiscsi_sht = {
.module = THIS_MODULE,
- .name = "ServerEngines 10Gbe open-iscsi Initiator Driver",
+ .name = "Emulex 10Gbe open-iscsi Initiator Driver",
.proc_name = DRV_NAME,
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
- .slave_configure = beiscsi_slave_configure,
+ .change_queue_depth = scsi_change_queue_depth,
.target_alloc = iscsi_target_alloc,
+ .eh_timed_out = iscsi_eh_cmd_timed_out,
.eh_abort_handler = beiscsi_eh_abort,
.eh_device_reset_handler = beiscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_session_reset,
+ .shost_groups = beiscsi_groups,
.sg_tablesize = BEISCSI_SGLIST_ELEMENTS,
.can_queue = BE2_IO_DEPTH,
.this_id = -1,
.max_sectors = BEISCSI_MAX_SECTORS,
+ .max_segment_size = 65536,
.cmd_per_lun = BEISCSI_CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
+ .vendor_id = SCSI_NL_VID_TYPE_PCI | BE_VENDOR_ID,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct iscsi_cmd),
};
static struct scsi_transport_template *beiscsi_scsi_transport;
@@ -464,12 +430,11 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
shost = iscsi_host_alloc(&beiscsi_sht, sizeof(*phba), 0);
if (!shost) {
- dev_err(&pcidev->dev, "beiscsi_hba_alloc -"
- "iscsi_host_alloc failed\n");
+ dev_err(&pcidev->dev,
+ "beiscsi_hba_alloc - iscsi_host_alloc failed\n");
return NULL;
}
- shost->dma_boundary = pcidev->dma_mask;
- shost->max_id = BE2_MAX_SESSIONS;
+ shost->max_id = BE2_MAX_SESSIONS - 1;
shost->max_channel = 0;
shost->max_cmd_len = BEISCSI_MAX_CMD_LEN;
shost->max_lun = BEISCSI_NUM_MAX_LUN;
@@ -479,24 +444,9 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
phba->shost = shost;
phba->pcidev = pci_dev_get(pcidev);
pci_set_drvdata(pcidev, phba);
-
- if (iscsi_host_add(shost, &phba->pcidev->dev))
- goto free_devices;
-
- if (beiscsi_setup_boot_info(phba))
- /*
- * log error but continue, because we may not be using
- * iscsi boot.
- */
- shost_printk(KERN_ERR, phba->shost, "Could not set up "
- "iSCSI boot info.");
+ phba->interface_handle = 0xFFFFFFFF;
return phba;
-
-free_devices:
- pci_dev_put(phba->pcidev);
- iscsi_host_free(phba->shost);
- return NULL;
}
static void beiscsi_unmap_pci_function(struct beiscsi_hba *phba)
@@ -521,34 +471,31 @@ static int beiscsi_map_pci_bars(struct beiscsi_hba *phba,
u8 __iomem *addr;
int pcicfg_reg;
- addr = ioremap_nocache(pci_resource_start(pcidev, 2),
+ addr = ioremap(pci_resource_start(pcidev, 2),
pci_resource_len(pcidev, 2));
if (addr == NULL)
return -ENOMEM;
phba->ctrl.csr = addr;
phba->csr_va = addr;
- phba->csr_pa.u.a64.address = pci_resource_start(pcidev, 2);
- addr = ioremap_nocache(pci_resource_start(pcidev, 4), 128 * 1024);
+ addr = ioremap(pci_resource_start(pcidev, 4), 128 * 1024);
if (addr == NULL)
goto pci_map_err;
phba->ctrl.db = addr;
phba->db_va = addr;
- phba->db_pa.u.a64.address = pci_resource_start(pcidev, 4);
if (phba->generation == BE_GEN2)
pcicfg_reg = 1;
else
pcicfg_reg = 0;
- addr = ioremap_nocache(pci_resource_start(pcidev, pcicfg_reg),
+ addr = ioremap(pci_resource_start(pcidev, pcicfg_reg),
pci_resource_len(pcidev, pcicfg_reg));
if (addr == NULL)
goto pci_map_err;
phba->ctrl.pcicfg = addr;
phba->pci_va = addr;
- phba->pci_pa.u.a64.address = pci_resource_start(pcidev, pcicfg_reg);
return 0;
pci_map_err:
@@ -562,21 +509,35 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
ret = pci_enable_device(pcidev);
if (ret) {
- dev_err(&pcidev->dev, "beiscsi_enable_pci - enable device "
- "failed. Returning -ENODEV\n");
+ dev_err(&pcidev->dev,
+ "beiscsi_enable_pci - enable device failed\n");
return ret;
}
+ ret = pci_request_regions(pcidev, DRV_NAME);
+ if (ret) {
+ dev_err(&pcidev->dev,
+ "beiscsi_enable_pci - request region failed\n");
+ goto pci_dev_disable;
+ }
+
pci_set_master(pcidev);
- if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) {
- ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
+ ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(64));
+ if (ret) {
+ ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n");
- pci_disable_device(pcidev);
- return ret;
+ goto pci_region_release;
}
}
return 0;
+
+pci_region_release:
+ pci_release_regions(pcidev);
+pci_dev_disable:
+ pci_disable_device(pcidev);
+
+ return ret;
}
static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
@@ -591,49 +552,100 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
if (status)
return status;
mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
- mbox_mem_alloc->va = pci_alloc_consistent(pdev,
- mbox_mem_alloc->size,
- &mbox_mem_alloc->dma);
+ mbox_mem_alloc->va = dma_alloc_coherent(&pdev->dev,
+ mbox_mem_alloc->size, &mbox_mem_alloc->dma, GFP_KERNEL);
if (!mbox_mem_alloc->va) {
beiscsi_unmap_pci_function(phba);
- status = -ENOMEM;
- return status;
+ return -ENOMEM;
}
mbox_mem_align->size = sizeof(struct be_mcc_mailbox);
mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16);
mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
- spin_lock_init(&ctrl->mbox_lock);
+ mutex_init(&ctrl->mbox_lock);
spin_lock_init(&phba->ctrl.mcc_lock);
- spin_lock_init(&phba->ctrl.mcc_cq_lock);
return status;
}
+/**
+ * beiscsi_get_params()- Set the config paramters
+ * @phba: ptr device priv structure
+ **/
static void beiscsi_get_params(struct beiscsi_hba *phba)
{
- phba->params.ios_per_ctrl = (phba->fw_config.iscsi_icd_count
- - (phba->fw_config.iscsi_cid_count
- + BE2_TMFS
- + BE2_NOPOUT_REQ));
- phba->params.cxns_per_ctrl = phba->fw_config.iscsi_cid_count;
- phba->params.asyncpdus_per_ctrl = phba->fw_config.iscsi_cid_count * 2;
- phba->params.icds_per_ctrl = phba->fw_config.iscsi_icd_count;
+ uint32_t total_cid_count = 0;
+ uint32_t total_icd_count = 0;
+ uint8_t ulp_num = 0;
+
+ total_cid_count = BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP0) +
+ BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP1);
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ uint32_t align_mask = 0;
+ uint32_t icd_post_per_page = 0;
+ uint32_t icd_count_unavailable = 0;
+ uint32_t icd_start = 0, icd_count = 0;
+ uint32_t icd_start_align = 0, icd_count_align = 0;
+
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+ icd_start = phba->fw_config.iscsi_icd_start[ulp_num];
+ icd_count = phba->fw_config.iscsi_icd_count[ulp_num];
+
+ /* Get ICD count that can be posted on each page */
+ icd_post_per_page = (PAGE_SIZE / (BE2_SGE *
+ sizeof(struct iscsi_sge)));
+ align_mask = (icd_post_per_page - 1);
+
+ /* Check if icd_start is aligned ICD per page posting */
+ if (icd_start % icd_post_per_page) {
+ icd_start_align = ((icd_start +
+ icd_post_per_page) &
+ ~(align_mask));
+ phba->fw_config.
+ iscsi_icd_start[ulp_num] =
+ icd_start_align;
+ }
+
+ icd_count_align = (icd_count & ~align_mask);
+
+ /* ICD discarded in the process of alignment */
+ if (icd_start_align)
+ icd_count_unavailable = ((icd_start_align -
+ icd_start) +
+ (icd_count -
+ icd_count_align));
+
+ /* Updated ICD count available */
+ phba->fw_config.iscsi_icd_count[ulp_num] = (icd_count -
+ icd_count_unavailable);
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : Aligned ICD values\n"
+ "\t ICD Start : %d\n"
+ "\t ICD Count : %d\n"
+ "\t ICD Discarded : %d\n",
+ phba->fw_config.
+ iscsi_icd_start[ulp_num],
+ phba->fw_config.
+ iscsi_icd_count[ulp_num],
+ icd_count_unavailable);
+ break;
+ }
+ }
+
+ total_icd_count = phba->fw_config.iscsi_icd_count[ulp_num];
+ phba->params.ios_per_ctrl = (total_icd_count -
+ (total_cid_count +
+ BE2_TMFS + BE2_NOPOUT_REQ));
+ phba->params.cxns_per_ctrl = total_cid_count;
+ phba->params.icds_per_ctrl = total_icd_count;
phba->params.num_sge_per_io = BE2_SGE;
phba->params.defpdu_hdr_sz = BE2_DEFPDU_HDR_SZ;
phba->params.defpdu_data_sz = BE2_DEFPDU_DATA_SZ;
- phba->params.eq_timer = 64;
- phba->params.num_eq_entries =
- (((BE2_CMDS_PER_CXN * 2 + phba->fw_config.iscsi_cid_count * 2
- + BE2_TMFS) / 512) + 1) * 512;
- phba->params.num_eq_entries = (phba->params.num_eq_entries < 1024)
- ? 1024 : phba->params.num_eq_entries;
- SE_DEBUG(DBG_LVL_8, "phba->params.num_eq_entries=%d\n",
- phba->params.num_eq_entries);
- phba->params.num_cq_entries =
- (((BE2_CMDS_PER_CXN * 2 + phba->fw_config.iscsi_cid_count * 2
- + BE2_TMFS) / 512) + 1) * 512;
+ phba->params.num_eq_entries = 1024;
+ phba->params.num_cq_entries = 1024;
phba->params.wrbs_per_cxn = 256;
}
@@ -643,14 +655,23 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba,
unsigned char rearm, unsigned char event)
{
u32 val = 0;
- val |= id & DB_EQ_RING_ID_MASK;
+
if (rearm)
val |= 1 << DB_EQ_REARM_SHIFT;
if (clr_interrupt)
val |= 1 << DB_EQ_CLR_SHIFT;
if (event)
val |= 1 << DB_EQ_EVNT_SHIFT;
+
val |= num_processed << DB_EQ_NUM_POPPED_SHIFT;
+ /* Setting lower order EQ_ID Bits */
+ val |= (id & DB_EQ_RING_ID_LOW_MASK);
+
+ /* Setting Higher order EQ_ID Bits */
+ val |= (((id >> DB_EQ_HIGH_FEILD_SHIFT) &
+ DB_EQ_RING_ID_HIGH_MASK)
+ << DB_EQ_HIGH_SET_SHIFT);
+
iowrite32(val, phba->db_va + DB_EQ_OFFSET);
}
@@ -662,42 +683,35 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba,
static irqreturn_t be_isr_mcc(int irq, void *dev_id)
{
struct beiscsi_hba *phba;
- struct be_eq_entry *eqe = NULL;
+ struct be_eq_entry *eqe;
struct be_queue_info *eq;
struct be_queue_info *mcc;
- unsigned int num_eq_processed;
+ unsigned int mcc_events;
struct be_eq_obj *pbe_eq;
- unsigned long flags;
pbe_eq = dev_id;
eq = &pbe_eq->q;
phba = pbe_eq->phba;
mcc = &phba->ctrl.mcc_obj.cq;
eqe = queue_tail_node(eq);
- if (!eqe)
- SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
-
- num_eq_processed = 0;
+ mcc_events = 0;
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
& EQE_VALID_MASK) {
if (((eqe->dw[offsetof(struct amap_eq_entry,
resource_id) / 32] &
EQE_RESID_MASK) >> 16) == mcc->id) {
- spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 1;
- spin_unlock_irqrestore(&phba->isr_lock, flags);
+ mcc_events++;
}
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
queue_tail_inc(eq);
eqe = queue_tail_node(eq);
- num_eq_processed++;
}
- if (phba->todo_mcc_cq)
- queue_work(phba->wq, &phba->work_cqs);
- if (num_eq_processed)
- hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
+ if (mcc_events) {
+ queue_work(phba->wq, &pbe_eq->mcc_work);
+ hwi_ring_eq_db(phba, eq->id, 1, mcc_events, 1, 1);
+ }
return IRQ_HANDLED;
}
@@ -709,56 +723,18 @@ static irqreturn_t be_isr_mcc(int irq, void *dev_id)
static irqreturn_t be_isr_msix(int irq, void *dev_id)
{
struct beiscsi_hba *phba;
- struct be_eq_entry *eqe = NULL;
struct be_queue_info *eq;
- struct be_queue_info *cq;
- unsigned int num_eq_processed;
struct be_eq_obj *pbe_eq;
- unsigned long flags;
pbe_eq = dev_id;
eq = &pbe_eq->q;
- cq = pbe_eq->cq;
- eqe = queue_tail_node(eq);
- if (!eqe)
- SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
phba = pbe_eq->phba;
- num_eq_processed = 0;
- if (blk_iopoll_enabled) {
- while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
- & EQE_VALID_MASK) {
- if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
- blk_iopoll_sched(&pbe_eq->iopoll);
+ /* disable interrupt till iopoll completes */
+ hwi_ring_eq_db(phba, eq->id, 1, 0, 0, 1);
+ irq_poll_sched(&pbe_eq->iopoll);
- AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
- queue_tail_inc(eq);
- eqe = queue_tail_node(eq);
- num_eq_processed++;
- }
- if (num_eq_processed)
- hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1);
-
- return IRQ_HANDLED;
- } else {
- while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
- & EQE_VALID_MASK) {
- spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_cq = 1;
- spin_unlock_irqrestore(&phba->isr_lock, flags);
- AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
- queue_tail_inc(eq);
- eqe = queue_tail_node(eq);
- num_eq_processed++;
- }
- if (phba->todo_cq)
- queue_work(phba->wq, &phba->work_cqs);
-
- if (num_eq_processed)
- hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
-
- return IRQ_HANDLED;
- }
+ return IRQ_HANDLED;
}
/**
@@ -771,15 +747,13 @@ static irqreturn_t be_isr(int irq, void *dev_id)
struct beiscsi_hba *phba;
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
- struct be_eq_entry *eqe = NULL;
+ struct be_eq_entry *eqe;
struct be_queue_info *eq;
- struct be_queue_info *cq;
struct be_queue_info *mcc;
- unsigned long flags, index;
- unsigned int num_mcceq_processed, num_ioeq_processed;
+ unsigned int mcc_events, io_events;
struct be_ctrl_info *ctrl;
struct be_eq_obj *pbe_eq;
- int isr;
+ int isr, rearm;
phba = dev_id;
ctrl = &phba->ctrl;
@@ -794,78 +768,53 @@ static irqreturn_t be_isr(int irq, void *dev_id)
eq = &phwi_context->be_eq[0].q;
mcc = &phba->ctrl.mcc_obj.cq;
- index = 0;
eqe = queue_tail_node(eq);
- if (!eqe)
- SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
- num_ioeq_processed = 0;
- num_mcceq_processed = 0;
- if (blk_iopoll_enabled) {
- while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
- & EQE_VALID_MASK) {
- if (((eqe->dw[offsetof(struct amap_eq_entry,
- resource_id) / 32] &
- EQE_RESID_MASK) >> 16) == mcc->id) {
- spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 1;
- spin_unlock_irqrestore(&phba->isr_lock, flags);
- num_mcceq_processed++;
- } else {
- if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
- blk_iopoll_sched(&pbe_eq->iopoll);
- num_ioeq_processed++;
- }
- AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
- queue_tail_inc(eq);
- eqe = queue_tail_node(eq);
- }
- if (num_ioeq_processed || num_mcceq_processed) {
- if (phba->todo_mcc_cq)
- queue_work(phba->wq, &phba->work_cqs);
-
- if ((num_mcceq_processed) && (!num_ioeq_processed))
- hwi_ring_eq_db(phba, eq->id, 0,
- (num_ioeq_processed +
- num_mcceq_processed) , 1, 1);
- else
- hwi_ring_eq_db(phba, eq->id, 0,
- (num_ioeq_processed +
- num_mcceq_processed), 0, 1);
+ io_events = 0;
+ mcc_events = 0;
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ if (((eqe->dw[offsetof(struct amap_eq_entry,
+ resource_id) / 32] & EQE_RESID_MASK) >> 16) == mcc->id)
+ mcc_events++;
+ else
+ io_events++;
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ }
+ if (!io_events && !mcc_events)
+ return IRQ_NONE;
- return IRQ_HANDLED;
- } else
- return IRQ_NONE;
- } else {
- cq = &phwi_context->be_cq[0];
- while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
- & EQE_VALID_MASK) {
-
- if (((eqe->dw[offsetof(struct amap_eq_entry,
- resource_id) / 32] &
- EQE_RESID_MASK) >> 16) != cq->id) {
- spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 1;
- spin_unlock_irqrestore(&phba->isr_lock, flags);
- } else {
- spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_cq = 1;
- spin_unlock_irqrestore(&phba->isr_lock, flags);
- }
- AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
- queue_tail_inc(eq);
- eqe = queue_tail_node(eq);
- num_ioeq_processed++;
- }
- if (phba->todo_cq || phba->todo_mcc_cq)
- queue_work(phba->wq, &phba->work_cqs);
+ /* no need to rearm if interrupt is only for IOs */
+ rearm = 0;
+ if (mcc_events) {
+ queue_work(phba->wq, &pbe_eq->mcc_work);
+ /* rearm for MCCQ */
+ rearm = 1;
+ }
+ if (io_events)
+ irq_poll_sched(&pbe_eq->iopoll);
+ hwi_ring_eq_db(phba, eq->id, 0, (io_events + mcc_events), rearm, 1);
+ return IRQ_HANDLED;
+}
- if (num_ioeq_processed) {
- hwi_ring_eq_db(phba, eq->id, 0,
- num_ioeq_processed, 1, 1);
- return IRQ_HANDLED;
- } else
- return IRQ_NONE;
+static void beiscsi_free_irqs(struct beiscsi_hba *phba)
+{
+ struct hwi_context_memory *phwi_context;
+ int i;
+
+ if (!phba->pcidev->msix_enabled) {
+ if (phba->pcidev->irq)
+ free_irq(phba->pcidev->irq, phba);
+ return;
+ }
+
+ phwi_context = phba->phwi_ctrlr->phwi_ctxt;
+ for (i = 0; i <= phba->num_cpus; i++) {
+ free_irq(pci_irq_vector(phba->pcidev, i),
+ &phwi_context->be_eq[i]);
+ kfree(phba->msi_name[i]);
}
}
@@ -874,34 +823,45 @@ static int beiscsi_init_irqs(struct beiscsi_hba *phba)
struct pci_dev *pcidev = phba->pcidev;
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
- int ret, msix_vec, i, j;
- char desc[32];
+ int ret, i, j;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- if (phba->msix_enabled) {
+ if (pcidev->msix_enabled) {
for (i = 0; i < phba->num_cpus; i++) {
- sprintf(desc, "beiscsi_msix_%04x", i);
- msix_vec = phba->msix_entries[i].vector;
- ret = request_irq(msix_vec, be_isr_msix, 0, desc,
+ phba->msi_name[i] = kasprintf(GFP_KERNEL,
+ "beiscsi_%02x_%02x",
+ phba->shost->host_no, i);
+ if (!phba->msi_name[i]) {
+ ret = -ENOMEM;
+ goto free_msix_irqs;
+ }
+
+ ret = request_irq(pci_irq_vector(pcidev, i),
+ be_isr_msix, 0, phba->msi_name[i],
&phwi_context->be_eq[i]);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "beiscsi_init_irqs-Failed to"
- "register msix for i = %d\n", i);
- if (!i)
- return ret;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : %s-Failed to register msix for i = %d\n",
+ __func__, i);
+ kfree(phba->msi_name[i]);
goto free_msix_irqs;
}
}
- msix_vec = phba->msix_entries[i].vector;
- ret = request_irq(msix_vec, be_isr_mcc, 0, "beiscsi_msix_mcc",
- &phwi_context->be_eq[i]);
+ phba->msi_name[i] = kasprintf(GFP_KERNEL, "beiscsi_mcc_%02x",
+ phba->shost->host_no);
+ if (!phba->msi_name[i]) {
+ ret = -ENOMEM;
+ goto free_msix_irqs;
+ }
+ ret = request_irq(pci_irq_vector(pcidev, i), be_isr_mcc, 0,
+ phba->msi_name[i], &phwi_context->be_eq[i]);
if (ret) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
- "Failed to register beiscsi_msix_mcc\n");
- i++;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : %s-Failed to register beiscsi_msix_mcc\n",
+ __func__);
+ kfree(phba->msi_name[i]);
goto free_msix_irqs;
}
@@ -909,87 +869,55 @@ static int beiscsi_init_irqs(struct beiscsi_hba *phba)
ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED,
"beiscsi", phba);
if (ret) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
- "Failed to register irq\\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : %s-Failed to register irq\n",
+ __func__);
return ret;
}
}
return 0;
free_msix_irqs:
- for (j = i - 1; j == 0; j++)
- free_irq(msix_vec, &phwi_context->be_eq[j]);
+ for (j = i - 1; j >= 0; j--) {
+ free_irq(pci_irq_vector(pcidev, i), &phwi_context->be_eq[j]);
+ kfree(phba->msi_name[j]);
+ }
return ret;
}
-static void hwi_ring_cq_db(struct beiscsi_hba *phba,
+void hwi_ring_cq_db(struct beiscsi_hba *phba,
unsigned int id, unsigned int num_processed,
- unsigned char rearm, unsigned char event)
+ unsigned char rearm)
{
u32 val = 0;
- val |= id & DB_CQ_RING_ID_MASK;
+
if (rearm)
val |= 1 << DB_CQ_REARM_SHIFT;
+
val |= num_processed << DB_CQ_NUM_POPPED_SHIFT;
- iowrite32(val, phba->db_va + DB_CQ_OFFSET);
-}
-static unsigned int
-beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
- struct beiscsi_hba *phba,
- unsigned short cid,
- struct pdu_base *ppdu,
- unsigned long pdu_len,
- void *pbuffer, unsigned long buf_len)
-{
- struct iscsi_conn *conn = beiscsi_conn->conn;
- struct iscsi_session *session = conn->session;
- struct iscsi_task *task;
- struct beiscsi_io_task *io_task;
- struct iscsi_hdr *login_hdr;
+ /* Setting lower order CQ_ID Bits */
+ val |= (id & DB_CQ_RING_ID_LOW_MASK);
- switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] &
- PDUBASE_OPCODE_MASK) {
- case ISCSI_OP_NOOP_IN:
- pbuffer = NULL;
- buf_len = 0;
- break;
- case ISCSI_OP_ASYNC_EVENT:
- break;
- case ISCSI_OP_REJECT:
- WARN_ON(!pbuffer);
- WARN_ON(!(buf_len == 48));
- SE_DEBUG(DBG_LVL_1, "In ISCSI_OP_REJECT\n");
- break;
- case ISCSI_OP_LOGIN_RSP:
- case ISCSI_OP_TEXT_RSP:
- task = conn->login_task;
- io_task = task->dd_data;
- login_hdr = (struct iscsi_hdr *)ppdu;
- login_hdr->itt = io_task->libiscsi_itt;
- break;
- default:
- shost_printk(KERN_WARNING, phba->shost,
- "Unrecognized opcode 0x%x in async msg\n",
- (ppdu->
- dw[offsetof(struct amap_pdu_base, opcode) / 32]
- & PDUBASE_OPCODE_MASK));
- return 1;
- }
+ /* Setting Higher order CQ_ID Bits */
+ val |= (((id >> DB_CQ_HIGH_FEILD_SHIFT) &
+ DB_CQ_RING_ID_HIGH_MASK)
+ << DB_CQ_HIGH_SET_SHIFT);
- spin_lock_bh(&session->lock);
- __iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len);
- spin_unlock_bh(&session->lock);
- return 0;
+ iowrite32(val, phba->db_va + DB_CQ_OFFSET);
}
static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
{
struct sgl_handle *psgl_handle;
+ unsigned long flags;
+ spin_lock_irqsave(&phba->io_sgl_lock, flags);
if (phba->io_sgl_hndl_avbl) {
- SE_DEBUG(DBG_LVL_8,
- "In alloc_io_sgl_handle,io_sgl_alloc_index=%d\n",
- phba->io_sgl_alloc_index);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_IO,
+ "BM_%d : In alloc_io_sgl_handle,"
+ " io_sgl_alloc_index=%d\n",
+ phba->io_sgl_alloc_index);
+
psgl_handle = phba->io_sgl_hndl_base[phba->
io_sgl_alloc_index];
phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL;
@@ -1001,23 +929,30 @@ static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
phba->io_sgl_alloc_index++;
} else
psgl_handle = NULL;
+ spin_unlock_irqrestore(&phba->io_sgl_lock, flags);
return psgl_handle;
}
static void
free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
{
- SE_DEBUG(DBG_LVL_8, "In free_,io_sgl_free_index=%d\n",
- phba->io_sgl_free_index);
+ unsigned long flags;
+
+ spin_lock_irqsave(&phba->io_sgl_lock, flags);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_IO,
+ "BM_%d : In free_,io_sgl_free_index=%d\n",
+ phba->io_sgl_free_index);
+
if (phba->io_sgl_hndl_base[phba->io_sgl_free_index]) {
/*
* this can happen if clean_task is called on a task that
* failed in xmit_task or alloc_pdu.
*/
- SE_DEBUG(DBG_LVL_8,
- "Double Free in IO SGL io_sgl_free_index=%d,"
- "value there=%p\n", phba->io_sgl_free_index,
- phba->io_sgl_hndl_base[phba->io_sgl_free_index]);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_IO,
+ "BM_%d : Double Free in IO SGL io_sgl_free_index=%d, value there=%p\n",
+ phba->io_sgl_free_index,
+ phba->io_sgl_hndl_base[phba->io_sgl_free_index]);
+ spin_unlock_irqrestore(&phba->io_sgl_lock, flags);
return;
}
phba->io_sgl_hndl_base[phba->io_sgl_free_index] = psgl_handle;
@@ -1026,38 +961,73 @@ free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
phba->io_sgl_free_index = 0;
else
phba->io_sgl_free_index++;
+ spin_unlock_irqrestore(&phba->io_sgl_lock, flags);
+}
+
+static inline struct wrb_handle *
+beiscsi_get_wrb_handle(struct hwi_wrb_context *pwrb_context,
+ unsigned int wrbs_per_cxn)
+{
+ struct wrb_handle *pwrb_handle;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pwrb_context->wrb_lock, flags);
+ if (!pwrb_context->wrb_handles_available) {
+ spin_unlock_irqrestore(&pwrb_context->wrb_lock, flags);
+ return NULL;
+ }
+ pwrb_handle = pwrb_context->pwrb_handle_base[pwrb_context->alloc_index];
+ pwrb_context->wrb_handles_available--;
+ if (pwrb_context->alloc_index == (wrbs_per_cxn - 1))
+ pwrb_context->alloc_index = 0;
+ else
+ pwrb_context->alloc_index++;
+ spin_unlock_irqrestore(&pwrb_context->wrb_lock, flags);
+
+ if (pwrb_handle)
+ memset(pwrb_handle->pwrb, 0, sizeof(*pwrb_handle->pwrb));
+
+ return pwrb_handle;
}
/**
* alloc_wrb_handle - To allocate a wrb handle
* @phba: The hba pointer
* @cid: The cid to use for allocation
+ * @pcontext: ptr to ptr to wrb context
*
* This happens under session_lock until submission to chip
*/
-struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid)
+struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
+ struct hwi_wrb_context **pcontext)
{
struct hwi_wrb_context *pwrb_context;
struct hwi_controller *phwi_ctrlr;
- struct wrb_handle *pwrb_handle, *pwrb_handle_tmp;
+ uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[cid];
- if (pwrb_context->wrb_handles_available >= 2) {
- pwrb_handle = pwrb_context->pwrb_handle_base[
- pwrb_context->alloc_index];
- pwrb_context->wrb_handles_available--;
- if (pwrb_context->alloc_index ==
- (phba->params.wrbs_per_cxn - 1))
- pwrb_context->alloc_index = 0;
- else
- pwrb_context->alloc_index++;
- pwrb_handle_tmp = pwrb_context->pwrb_handle_base[
- pwrb_context->alloc_index];
- pwrb_handle->nxt_wrb_index = pwrb_handle_tmp->wrb_index;
- } else
- pwrb_handle = NULL;
- return pwrb_handle;
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
+ /* return the context address */
+ *pcontext = pwrb_context;
+ return beiscsi_get_wrb_handle(pwrb_context, phba->params.wrbs_per_cxn);
+}
+
+static inline void
+beiscsi_put_wrb_handle(struct hwi_wrb_context *pwrb_context,
+ struct wrb_handle *pwrb_handle,
+ unsigned int wrbs_per_cxn)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pwrb_context->wrb_lock, flags);
+ pwrb_context->pwrb_handle_base[pwrb_context->free_index] = pwrb_handle;
+ pwrb_context->wrb_handles_available++;
+ if (pwrb_context->free_index == (wrbs_per_cxn - 1))
+ pwrb_context->free_index = 0;
+ else
+ pwrb_context->free_index++;
+ pwrb_handle->pio_handle = NULL;
+ spin_unlock_irqrestore(&pwrb_context->wrb_lock, flags);
}
/**
@@ -1072,29 +1042,31 @@ static void
free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context,
struct wrb_handle *pwrb_handle)
{
- pwrb_context->pwrb_handle_base[pwrb_context->free_index] = pwrb_handle;
- pwrb_context->wrb_handles_available++;
- if (pwrb_context->free_index == (phba->params.wrbs_per_cxn - 1))
- pwrb_context->free_index = 0;
- else
- pwrb_context->free_index++;
-
- SE_DEBUG(DBG_LVL_8,
- "FREE WRB: pwrb_handle=%p free_index=0x%x"
- "wrb_handles_available=%d\n",
- pwrb_handle, pwrb_context->free_index,
- pwrb_context->wrb_handles_available);
+ beiscsi_put_wrb_handle(pwrb_context,
+ pwrb_handle,
+ phba->params.wrbs_per_cxn);
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : FREE WRB: pwrb_handle=%p free_index=0x%x "
+ "wrb_handles_available=%d\n",
+ pwrb_handle, pwrb_context->free_index,
+ pwrb_context->wrb_handles_available);
}
static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba)
{
struct sgl_handle *psgl_handle;
+ unsigned long flags;
+ spin_lock_irqsave(&phba->mgmt_sgl_lock, flags);
if (phba->eh_sgl_hndl_avbl) {
psgl_handle = phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index];
phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index] = NULL;
- SE_DEBUG(DBG_LVL_8, "mgmt_sgl_alloc_index=%d=0x%x\n",
- phba->eh_sgl_alloc_index, phba->eh_sgl_alloc_index);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BM_%d : mgmt_sgl_alloc_index=%d=0x%x\n",
+ phba->eh_sgl_alloc_index,
+ phba->eh_sgl_alloc_index);
+
phba->eh_sgl_hndl_avbl--;
if (phba->eh_sgl_alloc_index ==
(phba->params.icds_per_ctrl - phba->params.ios_per_ctrl -
@@ -1104,23 +1076,31 @@ static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba)
phba->eh_sgl_alloc_index++;
} else
psgl_handle = NULL;
+ spin_unlock_irqrestore(&phba->mgmt_sgl_lock, flags);
return psgl_handle;
}
void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&phba->mgmt_sgl_lock, flags);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BM_%d : In free_mgmt_sgl_handle,"
+ "eh_sgl_free_index=%d\n",
+ phba->eh_sgl_free_index);
- SE_DEBUG(DBG_LVL_8, "In free_mgmt_sgl_handle,eh_sgl_free_index=%d\n",
- phba->eh_sgl_free_index);
if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) {
/*
* this can happen if clean_task is called on a task that
* failed in xmit_task or alloc_pdu.
*/
- SE_DEBUG(DBG_LVL_8,
- "Double Free in eh SGL ,eh_sgl_free_index=%d\n",
- phba->eh_sgl_free_index);
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BM_%d : Double Free in eh SGL ,"
+ "eh_sgl_free_index=%d\n",
+ phba->eh_sgl_free_index);
+ spin_unlock_irqrestore(&phba->mgmt_sgl_lock, flags);
return;
}
phba->eh_sgl_hndl_base[phba->eh_sgl_free_index] = psgl_handle;
@@ -1130,36 +1110,38 @@ free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
phba->eh_sgl_free_index = 0;
else
phba->eh_sgl_free_index++;
+ spin_unlock_irqrestore(&phba->mgmt_sgl_lock, flags);
}
static void
be_complete_io(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct beiscsi_io_task *io_task = task->dd_data;
struct be_status_bhs *sts_bhs =
(struct be_status_bhs *)io_task->cmd_bhs;
struct iscsi_conn *conn = beiscsi_conn->conn;
- unsigned int sense_len;
unsigned char *sense;
u32 resid = 0, exp_cmdsn, max_cmdsn;
u8 rsp, status, flags;
- exp_cmdsn = (psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK);
- max_cmdsn = ((psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
- rsp = ((psol->dw[offsetof(struct amap_sol_cqe, i_resp) / 32]
- & SOL_RESP_MASK) >> 16);
- status = ((psol->dw[offsetof(struct amap_sol_cqe, i_sts) / 32]
- & SOL_STS_MASK) >> 8);
- flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
+ exp_cmdsn = csol_cqe->exp_cmdsn;
+ max_cmdsn = (csol_cqe->exp_cmdsn +
+ csol_cqe->cmd_wnd - 1);
+ rsp = csol_cqe->i_resp;
+ status = csol_cqe->i_sts;
+ flags = csol_cqe->i_flags;
+ resid = csol_cqe->res_cnt;
+
+ if (!task->sc) {
+ if (io_task->scsi_cmnd) {
+ scsi_dma_unmap(io_task->scsi_cmnd);
+ io_task->scsi_cmnd = NULL;
+ }
+ return;
+ }
task->sc->result = (DID_OK << 16) | status;
if (rsp != ISCSI_STATUS_CMD_COMPLETED) {
task->sc->result = DID_ERROR << 16;
@@ -1168,9 +1150,6 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
/* bidi not initially supported */
if (flags & (ISCSI_FLAG_CMD_UNDERFLOW | ISCSI_FLAG_CMD_OVERFLOW)) {
- resid = (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) /
- 32] & SOL_RES_CNT_MASK);
-
if (!status && (flags & ISCSI_FLAG_CMD_OVERFLOW))
task->sc->result = DID_ERROR << 16;
@@ -1183,28 +1162,29 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
}
if (status == SAM_STAT_CHECK_CONDITION) {
+ u16 sense_len;
unsigned short *slen = (unsigned short *)sts_bhs->sense_info;
+
sense = sts_bhs->sense_info + sizeof(unsigned short);
- sense_len = cpu_to_be16(*slen);
+ sense_len = be16_to_cpu(*slen);
memcpy(task->sc->sense_buffer, sense,
min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE));
}
- if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ) {
- if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
- & SOL_RES_CNT_MASK)
- conn->rxdata_octets += (psol->
- dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
- & SOL_RES_CNT_MASK);
- }
+ if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ)
+ conn->rxdata_octets += resid;
unmap:
- scsi_dma_unmap(io_task->scsi_cmnd);
+ if (io_task->scsi_cmnd) {
+ scsi_dma_unmap(io_task->scsi_cmnd);
+ io_task->scsi_cmnd = NULL;
+ }
iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn);
}
static void
be_complete_logout(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct iscsi_logout_rsp *hdr;
struct beiscsi_io_task *io_task = task->dd_data;
@@ -1214,18 +1194,12 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
hdr->opcode = ISCSI_OP_LOGOUT_RSP;
hdr->t2wait = 5;
hdr->t2retain = 0;
- hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
- hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
- 32] & SOL_RESP_MASK);
- hdr->exp_cmdsn = cpu_to_be32(psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK);
- hdr->max_cmdsn = be32_to_cpu((psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->flags = csol_cqe->i_flags;
+ hdr->response = csol_cqe->i_resp;
+ hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
+ hdr->max_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn +
+ csol_cqe->cmd_wnd - 1);
+
hdr->dlength[0] = 0;
hdr->dlength[1] = 0;
hdr->dlength[2] = 0;
@@ -1236,7 +1210,8 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
static void
be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct iscsi_tm_rsp *hdr;
struct iscsi_conn *conn = beiscsi_conn->conn;
@@ -1244,16 +1219,12 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
hdr = (struct iscsi_tm_rsp *)task->hdr;
hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP;
- hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
- hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
- 32] & SOL_RESP_MASK);
- hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
- hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->flags = csol_cqe->i_flags;
+ hdr->response = csol_cqe->i_resp;
+ hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
+ hdr->max_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn +
+ csol_cqe->cmd_wnd - 1);
+
hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -1263,548 +1234,580 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba, struct sol_cqe *psol)
{
struct hwi_wrb_context *pwrb_context;
- struct wrb_handle *pwrb_handle = NULL;
+ uint16_t wrb_index, cid, cri_index;
struct hwi_controller *phwi_ctrlr;
+ struct wrb_handle *pwrb_handle;
+ struct iscsi_session *session;
struct iscsi_task *task;
- struct beiscsi_io_task *io_task;
- struct iscsi_conn *conn = beiscsi_conn->conn;
- struct iscsi_session *session = conn->session;
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[((psol->
- dw[offsetof(struct amap_sol_cqe, cid) / 32] &
- SOL_CID_MASK) >> 6) -
- phba->fw_config.iscsi_cid_start];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
- dw[offsetof(struct amap_sol_cqe, wrb_index) /
- 32] & SOL_WRB_INDEX_MASK) >> 16)];
- task = pwrb_handle->pio_handle;
+ if (is_chip_be2_be3r(phba)) {
+ wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
+ wrb_idx, psol);
+ cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
+ cid, psol);
+ } else {
+ wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
+ wrb_idx, psol);
+ cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
+ cid, psol);
+ }
- io_task = task->dd_data;
- spin_lock(&phba->mgmt_sgl_lock);
- free_mgmt_sgl_handle(phba, io_task->psgl_handle);
- spin_unlock(&phba->mgmt_sgl_lock);
- spin_lock_bh(&session->lock);
- free_wrb_handle(phba, pwrb_context, pwrb_handle);
- spin_unlock_bh(&session->lock);
+ cri_index = BE_GET_CRI_FROM_CID(cid);
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[wrb_index];
+ session = beiscsi_conn->conn->session;
+ spin_lock_bh(&session->back_lock);
+ task = pwrb_handle->pio_handle;
+ if (task)
+ __iscsi_put_task(task);
+ spin_unlock_bh(&session->back_lock);
}
static void
be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct iscsi_nopin *hdr;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct beiscsi_io_task *io_task = task->dd_data;
hdr = (struct iscsi_nopin *)task->hdr;
- hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
- hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
- hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->flags = csol_cqe->i_flags;
+ hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
+ hdr->max_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn +
+ csol_cqe->cmd_wnd - 1);
+
hdr->opcode = ISCSI_OP_NOOP_IN;
hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
+static void adapter_get_sol_cqe(struct beiscsi_hba *phba,
+ struct sol_cqe *psol,
+ struct common_sol_cqe *csol_cqe)
+{
+ if (is_chip_be2_be3r(phba)) {
+ csol_cqe->exp_cmdsn = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_exp_cmd_sn, psol);
+ csol_cqe->res_cnt = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_res_cnt, psol);
+ csol_cqe->cmd_wnd = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_cmd_wnd, psol);
+ csol_cqe->wrb_index = AMAP_GET_BITS(struct amap_sol_cqe,
+ wrb_index, psol);
+ csol_cqe->cid = AMAP_GET_BITS(struct amap_sol_cqe,
+ cid, psol);
+ csol_cqe->hw_sts = AMAP_GET_BITS(struct amap_sol_cqe,
+ hw_sts, psol);
+ csol_cqe->i_resp = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_resp, psol);
+ csol_cqe->i_sts = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_sts, psol);
+ csol_cqe->i_flags = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_flags, psol);
+ } else {
+ csol_cqe->exp_cmdsn = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_exp_cmd_sn, psol);
+ csol_cqe->res_cnt = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_res_cnt, psol);
+ csol_cqe->wrb_index = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ wrb_index, psol);
+ csol_cqe->cid = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ cid, psol);
+ csol_cqe->hw_sts = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ hw_sts, psol);
+ csol_cqe->cmd_wnd = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_cmd_wnd, psol);
+ if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ cmd_cmpl, psol))
+ csol_cqe->i_sts = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_sts, psol);
+ else
+ csol_cqe->i_resp = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_sts, psol);
+ if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ u, psol))
+ csol_cqe->i_flags = ISCSI_FLAG_CMD_UNDERFLOW;
+
+ if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ o, psol))
+ csol_cqe->i_flags |= ISCSI_FLAG_CMD_OVERFLOW;
+ }
+}
+
+
static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba, struct sol_cqe *psol)
{
+ struct iscsi_conn *conn = beiscsi_conn->conn;
+ struct iscsi_session *session = conn->session;
+ struct common_sol_cqe csol_cqe = {0};
struct hwi_wrb_context *pwrb_context;
- struct wrb_handle *pwrb_handle;
- struct iscsi_wrb *pwrb = NULL;
struct hwi_controller *phwi_ctrlr;
+ struct wrb_handle *pwrb_handle;
struct iscsi_task *task;
- unsigned int type;
- struct iscsi_conn *conn = beiscsi_conn->conn;
- struct iscsi_session *session = conn->session;
+ uint16_t cri_index = 0;
+ uint8_t type;
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[((psol->dw[offsetof
- (struct amap_sol_cqe, cid) / 32]
- & SOL_CID_MASK) >> 6) -
- phba->fw_config.iscsi_cid_start];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
- dw[offsetof(struct amap_sol_cqe, wrb_index) /
- 32] & SOL_WRB_INDEX_MASK) >> 16)];
+
+ /* Copy the elements to a common structure */
+ adapter_get_sol_cqe(phba, psol, &csol_cqe);
+
+ cri_index = BE_GET_CRI_FROM_CID(csol_cqe.cid);
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
+
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[
+ csol_cqe.wrb_index];
+
+ spin_lock_bh(&session->back_lock);
task = pwrb_handle->pio_handle;
- pwrb = pwrb_handle->pwrb;
- type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
- WRB_TYPE_MASK) >> 28;
+ if (!task) {
+ spin_unlock_bh(&session->back_lock);
+ return;
+ }
+ type = ((struct beiscsi_io_task *)task->dd_data)->wrb_type;
- spin_lock_bh(&session->lock);
switch (type) {
case HWH_TYPE_IO:
case HWH_TYPE_IO_RD:
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) ==
ISCSI_OP_NOOP_OUT)
- be_complete_nopin_resp(beiscsi_conn, task, psol);
+ be_complete_nopin_resp(beiscsi_conn, task, &csol_cqe);
else
- be_complete_io(beiscsi_conn, task, psol);
+ be_complete_io(beiscsi_conn, task, &csol_cqe);
break;
case HWH_TYPE_LOGOUT:
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
- be_complete_logout(beiscsi_conn, task, psol);
+ be_complete_logout(beiscsi_conn, task, &csol_cqe);
else
- be_complete_tmf(beiscsi_conn, task, psol);
-
+ be_complete_tmf(beiscsi_conn, task, &csol_cqe);
break;
case HWH_TYPE_LOGIN:
- SE_DEBUG(DBG_LVL_1,
- "\t\t No HWH_TYPE_LOGIN Expected in hwi_complete_cmd"
- "- Solicited path\n");
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+ "BM_%d :\t\t No HWH_TYPE_LOGIN Expected in"
+ " %s- Solicited path\n", __func__);
break;
case HWH_TYPE_NOP:
- be_complete_nopin_resp(beiscsi_conn, task, psol);
+ be_complete_nopin_resp(beiscsi_conn, task, &csol_cqe);
break;
default:
- shost_printk(KERN_WARNING, phba->shost,
- "In hwi_complete_cmd, unknown type = %d"
- "wrb_index 0x%x CID 0x%x\n", type,
- ((psol->dw[offsetof(struct amap_iscsi_wrb,
- type) / 32] & SOL_WRB_INDEX_MASK) >> 16),
- ((psol->dw[offsetof(struct amap_sol_cqe,
- cid) / 32] & SOL_CID_MASK) >> 6));
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+ "BM_%d : In %s, unknown type = %d "
+ "wrb_index 0x%x CID 0x%x\n", __func__, type,
+ csol_cqe.wrb_index,
+ csol_cqe.cid);
break;
}
- spin_unlock_bh(&session->lock);
-}
-
-static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context
- *pasync_ctx, unsigned int is_header,
- unsigned int host_write_ptr)
-{
- if (is_header)
- return &pasync_ctx->async_entry[host_write_ptr].
- header_busy_list;
- else
- return &pasync_ctx->async_entry[host_write_ptr].data_busy_list;
+ spin_unlock_bh(&session->back_lock);
}
-static struct async_pdu_handle *
-hwi_get_async_handle(struct beiscsi_hba *phba,
- struct beiscsi_conn *beiscsi_conn,
- struct hwi_async_pdu_context *pasync_ctx,
- struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index)
+/*
+ * ASYNC PDUs include
+ * a. Unsolicited NOP-In (target initiated NOP-In)
+ * b. ASYNC Messages
+ * c. Reject PDU
+ * d. Login response
+ * These headers arrive unprocessed by the EP firmware.
+ * iSCSI layer processes them.
+ */
+static unsigned int
+beiscsi_complete_pdu(struct beiscsi_conn *beiscsi_conn,
+ struct pdu_base *phdr, void *pdata, unsigned int dlen)
{
- struct be_bus_address phys_addr;
- struct list_head *pbusy_list;
- struct async_pdu_handle *pasync_handle = NULL;
- int buffer_len = 0;
- unsigned char buffer_index = -1;
- unsigned char is_header = 0;
-
- phys_addr.u.a32.address_lo =
- pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_lo) / 32] -
- ((pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32]
- & PDUCQE_DPL_MASK) >> 16);
- phys_addr.u.a32.address_hi =
- pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_hi) / 32];
-
- phys_addr.u.a64.address =
- *((unsigned long long *)(&phys_addr.u.a64.address));
-
- switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32]
- & PDUCQE_CODE_MASK) {
- case UNSOL_HDR_NOTIFY:
- is_header = 1;
-
- pbusy_list = hwi_get_async_busy_list(pasync_ctx, 1,
- (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
- index) / 32] & PDUCQE_INDEX_MASK));
-
- buffer_len = (unsigned int)(phys_addr.u.a64.address -
- pasync_ctx->async_header.pa_base.u.a64.address);
-
- buffer_index = buffer_len /
- pasync_ctx->async_header.buffer_size;
+ struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct iscsi_conn *conn = beiscsi_conn->conn;
+ struct beiscsi_io_task *io_task;
+ struct iscsi_hdr *login_hdr;
+ struct iscsi_task *task;
+ u8 code;
+ code = AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr);
+ switch (code) {
+ case ISCSI_OP_NOOP_IN:
+ pdata = NULL;
+ dlen = 0;
break;
- case UNSOL_DATA_NOTIFY:
- pbusy_list = hwi_get_async_busy_list(pasync_ctx, 0, (pdpdu_cqe->
- dw[offsetof(struct amap_i_t_dpdu_cqe,
- index) / 32] & PDUCQE_INDEX_MASK));
- buffer_len = (unsigned long)(phys_addr.u.a64.address -
- pasync_ctx->async_data.pa_base.u.
- a64.address);
- buffer_index = buffer_len / pasync_ctx->async_data.buffer_size;
+ case ISCSI_OP_ASYNC_EVENT:
+ break;
+ case ISCSI_OP_REJECT:
+ WARN_ON(!pdata);
+ WARN_ON(!(dlen == 48));
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+ "BM_%d : In ISCSI_OP_REJECT\n");
+ break;
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ task = conn->login_task;
+ io_task = task->dd_data;
+ login_hdr = (struct iscsi_hdr *)phdr;
+ login_hdr->itt = io_task->libiscsi_itt;
break;
default:
- pbusy_list = NULL;
- shost_printk(KERN_WARNING, phba->shost,
- "Unexpected code=%d\n",
- pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
- code) / 32] & PDUCQE_CODE_MASK);
- return NULL;
- }
-
- WARN_ON(!(buffer_index <= pasync_ctx->async_data.num_entries));
- WARN_ON(list_empty(pbusy_list));
- list_for_each_entry(pasync_handle, pbusy_list, link) {
- WARN_ON(pasync_handle->consumed);
- if (pasync_handle->index == buffer_index)
- break;
- }
-
- WARN_ON(!pasync_handle);
-
- pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start;
- pasync_handle->is_header = is_header;
- pasync_handle->buffer_len = ((pdpdu_cqe->
- dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32]
- & PDUCQE_DPL_MASK) >> 16);
-
- *pcq_index = (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
- index) / 32] & PDUCQE_INDEX_MASK);
- return pasync_handle;
-}
-
-static unsigned int
-hwi_update_async_writables(struct hwi_async_pdu_context *pasync_ctx,
- unsigned int is_header, unsigned int cq_index)
-{
- struct list_head *pbusy_list;
- struct async_pdu_handle *pasync_handle;
- unsigned int num_entries, writables = 0;
- unsigned int *pep_read_ptr, *pwritables;
-
-
- if (is_header) {
- pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr;
- pwritables = &pasync_ctx->async_header.writables;
- num_entries = pasync_ctx->async_header.num_entries;
- } else {
- pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr;
- pwritables = &pasync_ctx->async_data.writables;
- num_entries = pasync_ctx->async_data.num_entries;
- }
-
- while ((*pep_read_ptr) != cq_index) {
- (*pep_read_ptr)++;
- *pep_read_ptr = (*pep_read_ptr) % num_entries;
-
- pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header,
- *pep_read_ptr);
- if (writables == 0)
- WARN_ON(list_empty(pbusy_list));
-
- if (!list_empty(pbusy_list)) {
- pasync_handle = list_entry(pbusy_list->next,
- struct async_pdu_handle,
- link);
- WARN_ON(!pasync_handle);
- pasync_handle->consumed = 1;
- }
-
- writables++;
- }
-
- if (!writables) {
- SE_DEBUG(DBG_LVL_1,
- "Duplicate notification received - index 0x%x!!\n",
- cq_index);
- WARN_ON(1);
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : unrecognized async PDU opcode 0x%x\n",
+ code);
+ return 1;
}
-
- *pwritables = *pwritables + writables;
+ __iscsi_complete_pdu(conn, (struct iscsi_hdr *)phdr, pdata, dlen);
return 0;
}
-static unsigned int hwi_free_async_msg(struct beiscsi_hba *phba,
- unsigned int cri)
+static inline void
+beiscsi_hdl_put_handle(struct hd_async_context *pasync_ctx,
+ struct hd_async_handle *pasync_handle)
{
- struct hwi_controller *phwi_ctrlr;
- struct hwi_async_pdu_context *pasync_ctx;
- struct async_pdu_handle *pasync_handle, *tmp_handle;
- struct list_head *plist;
- unsigned int i = 0;
-
- phwi_ctrlr = phba->phwi_ctrlr;
- pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
-
- plist = &pasync_ctx->async_entry[cri].wait_queue.list;
-
- list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) {
- list_del(&pasync_handle->link);
-
- if (i == 0) {
- list_add_tail(&pasync_handle->link,
- &pasync_ctx->async_header.free_list);
- pasync_ctx->async_header.free_entries++;
- i++;
- } else {
- list_add_tail(&pasync_handle->link,
- &pasync_ctx->async_data.free_list);
- pasync_ctx->async_data.free_entries++;
- i++;
- }
- }
-
- INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list);
- pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0;
- pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0;
- return 0;
+ pasync_handle->is_final = 0;
+ pasync_handle->buffer_len = 0;
+ pasync_handle->in_use = 0;
+ list_del_init(&pasync_handle->link);
}
-static struct phys_addr *
-hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx,
- unsigned int is_header, unsigned int host_write_ptr)
+static void
+beiscsi_hdl_purge_handles(struct beiscsi_hba *phba,
+ struct hd_async_context *pasync_ctx,
+ u16 cri)
{
- struct phys_addr *pasync_sge = NULL;
+ struct hd_async_handle *pasync_handle, *tmp_handle;
+ struct list_head *plist;
- if (is_header)
- pasync_sge = pasync_ctx->async_header.ring_base;
- else
- pasync_sge = pasync_ctx->async_data.ring_base;
+ plist = &pasync_ctx->async_entry[cri].wq.list;
+ list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link)
+ beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
- return pasync_sge + host_write_ptr;
+ INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wq.list);
+ pasync_ctx->async_entry[cri].wq.hdr_len = 0;
+ pasync_ctx->async_entry[cri].wq.bytes_received = 0;
+ pasync_ctx->async_entry[cri].wq.bytes_needed = 0;
}
-static void hwi_post_async_buffers(struct beiscsi_hba *phba,
- unsigned int is_header)
+static struct hd_async_handle *
+beiscsi_hdl_get_handle(struct beiscsi_conn *beiscsi_conn,
+ struct hd_async_context *pasync_ctx,
+ struct i_t_dpdu_cqe *pdpdu_cqe,
+ u8 *header)
{
- struct hwi_controller *phwi_ctrlr;
- struct hwi_async_pdu_context *pasync_ctx;
- struct async_pdu_handle *pasync_handle;
- struct list_head *pfree_link, *pbusy_list;
- struct phys_addr *pasync_sge;
- unsigned int ring_id, num_entries;
- unsigned int host_write_num;
- unsigned int writables;
- unsigned int i = 0;
- u32 doorbell = 0;
+ struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct hd_async_handle *pasync_handle;
+ struct be_bus_address phys_addr;
+ u16 cid, code, ci, cri;
+ u8 final, error = 0;
+ u32 dpl;
- phwi_ctrlr = phba->phwi_ctrlr;
- pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
-
- if (is_header) {
- num_entries = pasync_ctx->async_header.num_entries;
- writables = min(pasync_ctx->async_header.writables,
- pasync_ctx->async_header.free_entries);
- pfree_link = pasync_ctx->async_header.free_list.next;
- host_write_num = pasync_ctx->async_header.host_write_ptr;
- ring_id = phwi_ctrlr->default_pdu_hdr.id;
+ cid = beiscsi_conn->beiscsi_conn_cid;
+ cri = BE_GET_ASYNC_CRI_FROM_CID(cid);
+ /**
+ * This function is invoked to get the right async_handle structure
+ * from a given DEF PDU CQ entry.
+ *
+ * - index in CQ entry gives the vertical index
+ * - address in CQ entry is the offset where the DMA last ended
+ * - final - no more notifications for this PDU
+ */
+ if (is_chip_be2_be3r(phba)) {
+ dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ dpl, pdpdu_cqe);
+ ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ index, pdpdu_cqe);
+ final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ final, pdpdu_cqe);
} else {
- num_entries = pasync_ctx->async_data.num_entries;
- writables = min(pasync_ctx->async_data.writables,
- pasync_ctx->async_data.free_entries);
- pfree_link = pasync_ctx->async_data.free_list.next;
- host_write_num = pasync_ctx->async_data.host_write_ptr;
- ring_id = phwi_ctrlr->default_pdu_data.id;
- }
-
- writables = (writables / 8) * 8;
- if (writables) {
- for (i = 0; i < writables; i++) {
- pbusy_list =
- hwi_get_async_busy_list(pasync_ctx, is_header,
- host_write_num);
- pasync_handle =
- list_entry(pfree_link, struct async_pdu_handle,
- link);
- WARN_ON(!pasync_handle);
- pasync_handle->consumed = 0;
-
- pfree_link = pfree_link->next;
-
- pasync_sge = hwi_get_ring_address(pasync_ctx,
- is_header, host_write_num);
-
- pasync_sge->hi = pasync_handle->pa.u.a32.address_lo;
- pasync_sge->lo = pasync_handle->pa.u.a32.address_hi;
-
- list_move(&pasync_handle->link, pbusy_list);
-
- host_write_num++;
- host_write_num = host_write_num % num_entries;
- }
-
- if (is_header) {
- pasync_ctx->async_header.host_write_ptr =
- host_write_num;
- pasync_ctx->async_header.free_entries -= writables;
- pasync_ctx->async_header.writables -= writables;
- pasync_ctx->async_header.busy_entries += writables;
- } else {
- pasync_ctx->async_data.host_write_ptr = host_write_num;
- pasync_ctx->async_data.free_entries -= writables;
- pasync_ctx->async_data.writables -= writables;
- pasync_ctx->async_data.busy_entries += writables;
- }
+ dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ dpl, pdpdu_cqe);
+ ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ index, pdpdu_cqe);
+ final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ final, pdpdu_cqe);
+ }
- doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK;
- doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT;
- doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT;
- doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK)
- << DB_DEF_PDU_CQPROC_SHIFT;
+ /**
+ * DB addr Hi/Lo is same for BE and SKH.
+ * Subtract the dataplacementlength to get to the base.
+ */
+ phys_addr.u.a32.address_lo = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ db_addr_lo, pdpdu_cqe);
+ phys_addr.u.a32.address_lo -= dpl;
+ phys_addr.u.a32.address_hi = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ db_addr_hi, pdpdu_cqe);
+
+ code = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, code, pdpdu_cqe);
+ switch (code) {
+ case UNSOL_HDR_NOTIFY:
+ pasync_handle = pasync_ctx->async_entry[ci].header;
+ *header = 1;
+ break;
+ case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
+ error = 1;
+ fallthrough;
+ case UNSOL_DATA_NOTIFY:
+ pasync_handle = pasync_ctx->async_entry[ci].data;
+ break;
+ /* called only for above codes */
+ default:
+ return NULL;
+ }
- iowrite32(doorbell, phba->db_va + DB_RXULP0_OFFSET);
+ if (pasync_handle->pa.u.a64.address != phys_addr.u.a64.address ||
+ pasync_handle->index != ci) {
+ /* driver bug - if ci does not match async handle index */
+ error = 1;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+ "BM_%d : cid %u async PDU handle mismatch - addr in %cQE %llx at %u:addr in CQE %llx ci %u\n",
+ cid, pasync_handle->is_header ? 'H' : 'D',
+ pasync_handle->pa.u.a64.address,
+ pasync_handle->index,
+ phys_addr.u.a64.address, ci);
+ /* FW has stale address - attempt continuing by dropping */
}
-}
-static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba,
- struct beiscsi_conn *beiscsi_conn,
- struct i_t_dpdu_cqe *pdpdu_cqe)
-{
- struct hwi_controller *phwi_ctrlr;
- struct hwi_async_pdu_context *pasync_ctx;
- struct async_pdu_handle *pasync_handle = NULL;
- unsigned int cq_index = -1;
+ /**
+ * DEF PDU header and data buffers with errors should be simply
+ * dropped as there are no consumers for it.
+ */
+ if (error) {
+ beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
+ return NULL;
+ }
- phwi_ctrlr = phba->phwi_ctrlr;
- pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
+ if (pasync_handle->in_use || !list_empty(&pasync_handle->link)) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+ "BM_%d : cid %d async PDU handle in use - code %d ci %d addr %llx\n",
+ cid, code, ci, phys_addr.u.a64.address);
+ beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
+ }
- pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx,
- pdpdu_cqe, &cq_index);
- BUG_ON(pasync_handle->is_header != 0);
- if (pasync_handle->consumed == 0)
- hwi_update_async_writables(pasync_ctx, pasync_handle->is_header,
- cq_index);
+ list_del_init(&pasync_handle->link);
+ /**
+ * Each CID is associated with unique CRI.
+ * ASYNC_CRI_FROM_CID mapping and CRI_FROM_CID are totaly different.
+ **/
+ pasync_handle->cri = cri;
+ pasync_handle->is_final = final;
+ pasync_handle->buffer_len = dpl;
+ pasync_handle->in_use = 1;
- hwi_free_async_msg(phba, pasync_handle->cri);
- hwi_post_async_buffers(phba, pasync_handle->is_header);
+ return pasync_handle;
}
static unsigned int
-hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn,
- struct beiscsi_hba *phba,
- struct hwi_async_pdu_context *pasync_ctx, unsigned short cri)
+beiscsi_hdl_fwd_pdu(struct beiscsi_conn *beiscsi_conn,
+ struct hd_async_context *pasync_ctx,
+ u16 cri)
{
+ struct iscsi_session *session = beiscsi_conn->conn->session;
+ struct hd_async_handle *pasync_handle, *plast_handle;
+ struct beiscsi_hba *phba = beiscsi_conn->phba;
+ void *phdr = NULL, *pdata = NULL;
+ u32 dlen = 0, status = 0;
struct list_head *plist;
- struct async_pdu_handle *pasync_handle;
- void *phdr = NULL;
- unsigned int hdr_len = 0, buf_len = 0;
- unsigned int status, index = 0, offset = 0;
- void *pfirst_buffer = NULL;
- unsigned int num_buf = 0;
-
- plist = &pasync_ctx->async_entry[cri].wait_queue.list;
+ plist = &pasync_ctx->async_entry[cri].wq.list;
+ plast_handle = NULL;
list_for_each_entry(pasync_handle, plist, link) {
- if (index == 0) {
+ plast_handle = pasync_handle;
+ /* get the header, the first entry */
+ if (!phdr) {
phdr = pasync_handle->pbuffer;
- hdr_len = pasync_handle->buffer_len;
- } else {
- buf_len = pasync_handle->buffer_len;
- if (!num_buf) {
- pfirst_buffer = pasync_handle->pbuffer;
- num_buf++;
- }
- memcpy(pfirst_buffer + offset,
- pasync_handle->pbuffer, buf_len);
- offset = buf_len;
+ continue;
}
- index++;
- }
-
- status = beiscsi_process_async_pdu(beiscsi_conn, phba,
- (beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start),
- phdr, hdr_len, pfirst_buffer,
- buf_len);
-
- if (status == 0)
- hwi_free_async_msg(phba, cri);
- return 0;
+ /* use first buffer to collect all the data */
+ if (!pdata) {
+ pdata = pasync_handle->pbuffer;
+ dlen = pasync_handle->buffer_len;
+ continue;
+ }
+ if (!pasync_handle->buffer_len ||
+ (dlen + pasync_handle->buffer_len) >
+ pasync_ctx->async_data.buffer_size)
+ break;
+ memcpy(pdata + dlen, pasync_handle->pbuffer,
+ pasync_handle->buffer_len);
+ dlen += pasync_handle->buffer_len;
+ }
+
+ if (!plast_handle->is_final) {
+ /* last handle should have final PDU notification from FW */
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+ "BM_%d : cid %u %p fwd async PDU opcode %x with last handle missing - HL%u:DN%u:DR%u\n",
+ beiscsi_conn->beiscsi_conn_cid, plast_handle,
+ AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr),
+ pasync_ctx->async_entry[cri].wq.hdr_len,
+ pasync_ctx->async_entry[cri].wq.bytes_needed,
+ pasync_ctx->async_entry[cri].wq.bytes_received);
+ }
+ spin_lock_bh(&session->back_lock);
+ status = beiscsi_complete_pdu(beiscsi_conn, phdr, pdata, dlen);
+ spin_unlock_bh(&session->back_lock);
+ beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
+ return status;
}
static unsigned int
-hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn,
- struct beiscsi_hba *phba,
- struct async_pdu_handle *pasync_handle)
+beiscsi_hdl_gather_pdu(struct beiscsi_conn *beiscsi_conn,
+ struct hd_async_context *pasync_ctx,
+ struct hd_async_handle *pasync_handle)
{
- struct hwi_async_pdu_context *pasync_ctx;
- struct hwi_controller *phwi_ctrlr;
unsigned int bytes_needed = 0, status = 0;
- unsigned short cri = pasync_handle->cri;
+ u16 cri = pasync_handle->cri;
+ struct cri_wait_queue *wq;
+ struct beiscsi_hba *phba;
struct pdu_base *ppdu;
+ char *err = "";
- phwi_ctrlr = phba->phwi_ctrlr;
- pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
-
- list_del(&pasync_handle->link);
+ phba = beiscsi_conn->phba;
+ wq = &pasync_ctx->async_entry[cri].wq;
if (pasync_handle->is_header) {
- pasync_ctx->async_header.busy_entries--;
- if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
- hwi_free_async_msg(phba, cri);
- BUG();
+ /* check if PDU hdr is rcv'd when old hdr not completed */
+ if (wq->hdr_len) {
+ err = "incomplete";
+ goto drop_pdu;
}
-
- pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0;
- pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1;
- pasync_ctx->async_entry[cri].wait_queue.hdr_len =
- (unsigned short)pasync_handle->buffer_len;
- list_add_tail(&pasync_handle->link,
- &pasync_ctx->async_entry[cri].wait_queue.list);
-
ppdu = pasync_handle->pbuffer;
- bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base,
- data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) &
- 0xFFFF0000) | ((be16_to_cpu((ppdu->
- dw[offsetof(struct amap_pdu_base, data_len_lo) / 32]
- & PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF));
-
- if (status == 0) {
- pasync_ctx->async_entry[cri].wait_queue.bytes_needed =
- bytes_needed;
-
- if (bytes_needed == 0)
- status = hwi_fwd_async_msg(beiscsi_conn, phba,
- pasync_ctx, cri);
- }
+ bytes_needed = AMAP_GET_BITS(struct amap_pdu_base,
+ data_len_hi, ppdu);
+ bytes_needed <<= 16;
+ bytes_needed |= be16_to_cpu(AMAP_GET_BITS(struct amap_pdu_base,
+ data_len_lo, ppdu));
+ wq->hdr_len = pasync_handle->buffer_len;
+ wq->bytes_received = 0;
+ wq->bytes_needed = bytes_needed;
+ list_add_tail(&pasync_handle->link, &wq->list);
+ if (!bytes_needed)
+ status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
+ pasync_ctx, cri);
} else {
- pasync_ctx->async_data.busy_entries--;
- if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
- list_add_tail(&pasync_handle->link,
- &pasync_ctx->async_entry[cri].wait_queue.
- list);
- pasync_ctx->async_entry[cri].wait_queue.
- bytes_received +=
- (unsigned short)pasync_handle->buffer_len;
-
- if (pasync_ctx->async_entry[cri].wait_queue.
- bytes_received >=
- pasync_ctx->async_entry[cri].wait_queue.
- bytes_needed)
- status = hwi_fwd_async_msg(beiscsi_conn, phba,
- pasync_ctx, cri);
+ /* check if data received has header and is needed */
+ if (!wq->hdr_len || !wq->bytes_needed) {
+ err = "header less";
+ goto drop_pdu;
+ }
+ wq->bytes_received += pasync_handle->buffer_len;
+ /* Something got overwritten? Better catch it here. */
+ if (wq->bytes_received > wq->bytes_needed) {
+ err = "overflow";
+ goto drop_pdu;
}
+ list_add_tail(&pasync_handle->link, &wq->list);
+ if (wq->bytes_received == wq->bytes_needed)
+ status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
+ pasync_ctx, cri);
}
return status;
+
+drop_pdu:
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+ "BM_%d : cid %u async PDU %s - def-%c:HL%u:DN%u:DR%u\n",
+ beiscsi_conn->beiscsi_conn_cid, err,
+ pasync_handle->is_header ? 'H' : 'D',
+ wq->hdr_len, wq->bytes_needed,
+ pasync_handle->buffer_len);
+ /* discard this handle */
+ beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
+ /* free all the other handles in cri_wait_queue */
+ beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
+ /* try continuing */
+ return status;
}
-static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
- struct beiscsi_hba *phba,
- struct i_t_dpdu_cqe *pdpdu_cqe)
+static void
+beiscsi_hdq_post_handles(struct beiscsi_hba *phba,
+ u8 header, u8 ulp_num, u16 nbuf)
{
+ struct hd_async_handle *pasync_handle;
+ struct hd_async_context *pasync_ctx;
struct hwi_controller *phwi_ctrlr;
- struct hwi_async_pdu_context *pasync_ctx;
- struct async_pdu_handle *pasync_handle = NULL;
- unsigned int cq_index = -1;
+ struct phys_addr *pasync_sge;
+ u32 ring_id, doorbell = 0;
+ u32 doorbell_offset;
+ u16 prod, pi;
phwi_ctrlr = phba->phwi_ctrlr;
- pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
- pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx,
- pdpdu_cqe, &cq_index);
-
- if (pasync_handle->consumed == 0)
- hwi_update_async_writables(pasync_ctx, pasync_handle->is_header,
- cq_index);
- hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle);
- hwi_post_async_buffers(phba, pasync_handle->is_header);
+ pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
+ if (header) {
+ pasync_sge = pasync_ctx->async_header.ring_base;
+ pi = pasync_ctx->async_header.pi;
+ ring_id = phwi_ctrlr->default_pdu_hdr[ulp_num].id;
+ doorbell_offset = phwi_ctrlr->default_pdu_hdr[ulp_num].
+ doorbell_offset;
+ } else {
+ pasync_sge = pasync_ctx->async_data.ring_base;
+ pi = pasync_ctx->async_data.pi;
+ ring_id = phwi_ctrlr->default_pdu_data[ulp_num].id;
+ doorbell_offset = phwi_ctrlr->default_pdu_data[ulp_num].
+ doorbell_offset;
+ }
+
+ for (prod = 0; prod < nbuf; prod++) {
+ if (header)
+ pasync_handle = pasync_ctx->async_entry[pi].header;
+ else
+ pasync_handle = pasync_ctx->async_entry[pi].data;
+ WARN_ON(pasync_handle->is_header != header);
+ WARN_ON(pasync_handle->index != pi);
+ /* setup the ring only once */
+ if (nbuf == pasync_ctx->num_entries) {
+ /* note hi is lo */
+ pasync_sge[pi].hi = pasync_handle->pa.u.a32.address_lo;
+ pasync_sge[pi].lo = pasync_handle->pa.u.a32.address_hi;
+ }
+ if (++pi == pasync_ctx->num_entries)
+ pi = 0;
+ }
+
+ if (header)
+ pasync_ctx->async_header.pi = pi;
+ else
+ pasync_ctx->async_data.pi = pi;
+
+ doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK;
+ doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT;
+ doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT;
+ doorbell |= (prod & DB_DEF_PDU_CQPROC_MASK) << DB_DEF_PDU_CQPROC_SHIFT;
+ iowrite32(doorbell, phba->db_va + doorbell_offset);
+}
+
+static void
+beiscsi_hdq_process_compl(struct beiscsi_conn *beiscsi_conn,
+ struct i_t_dpdu_cqe *pdpdu_cqe)
+{
+ struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct hd_async_handle *pasync_handle = NULL;
+ struct hd_async_context *pasync_ctx;
+ struct hwi_controller *phwi_ctrlr;
+ u8 ulp_num, consumed, header = 0;
+ u16 cid_cri;
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ cid_cri = BE_GET_CRI_FROM_CID(beiscsi_conn->beiscsi_conn_cid);
+ ulp_num = BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, cid_cri);
+ pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
+ pasync_handle = beiscsi_hdl_get_handle(beiscsi_conn, pasync_ctx,
+ pdpdu_cqe, &header);
+ if (is_chip_be2_be3r(phba))
+ consumed = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ num_cons, pdpdu_cqe);
+ else
+ consumed = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ num_cons, pdpdu_cqe);
+ if (pasync_handle)
+ beiscsi_hdl_gather_pdu(beiscsi_conn, pasync_ctx, pasync_handle);
+ /* num_cons indicates number of 8 RQEs consumed */
+ if (consumed)
+ beiscsi_hdq_post_handles(phba, header, ulp_num, 8 * consumed);
}
-static void beiscsi_process_mcc_isr(struct beiscsi_hba *phba)
+void beiscsi_process_mcc_cq(struct beiscsi_hba *phba)
{
struct be_queue_info *mcc_cq;
struct be_mcc_compl *mcc_compl;
@@ -1814,25 +1817,18 @@ static void beiscsi_process_mcc_isr(struct beiscsi_hba *phba)
mcc_compl = queue_tail_node(mcc_cq);
mcc_compl->flags = le32_to_cpu(mcc_compl->flags);
while (mcc_compl->flags & CQE_FLAGS_VALID_MASK) {
+ if (beiscsi_hba_in_error(phba))
+ return;
if (num_processed >= 32) {
hwi_ring_cq_db(phba, mcc_cq->id,
- num_processed, 0, 0);
+ num_processed, 0);
num_processed = 0;
}
if (mcc_compl->flags & CQE_FLAGS_ASYNC_MASK) {
- /* Interpret flags as an async trailer */
- if (is_link_state_evt(mcc_compl->flags))
- /* Interpret compl as a async link evt */
- beiscsi_async_link_state_process(phba,
- (struct be_async_event_link_state *) mcc_compl);
- else
- SE_DEBUG(DBG_LVL_1,
- " Unsupported Async Event, flags"
- " = 0x%08x\n", mcc_compl->flags);
+ beiscsi_process_async_event(phba, mcc_compl);
} else if (mcc_compl->flags & CQE_FLAGS_COMPLETED_MASK) {
- be_mcc_compl_process_isr(&phba->ctrl, mcc_compl);
- atomic_dec(&phba->ctrl.mcc_obj.q.used);
+ beiscsi_process_mcc_compl(&phba->ctrl, mcc_compl);
}
mcc_compl->flags = 0;
@@ -1843,17 +1839,38 @@ static void beiscsi_process_mcc_isr(struct beiscsi_hba *phba)
}
if (num_processed > 0)
- hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 1, 0);
+ hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 1);
+}
+static void beiscsi_mcc_work(struct work_struct *work)
+{
+ struct be_eq_obj *pbe_eq;
+ struct beiscsi_hba *phba;
+
+ pbe_eq = container_of(work, struct be_eq_obj, mcc_work);
+ phba = pbe_eq->phba;
+ beiscsi_process_mcc_cq(phba);
+ /* rearm EQ for further interrupts */
+ if (!beiscsi_hba_in_error(phba))
+ hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
}
-static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
+/**
+ * beiscsi_process_cq()- Process the Completion Queue
+ * @pbe_eq: Event Q on which the Completion has come
+ * @budget: Max number of events to processed
+ *
+ * return
+ * Number of Completion Entries processed.
+ **/
+unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
{
struct be_queue_info *cq;
struct sol_cqe *sol;
- struct dmsg_cqe *dmsg;
+ unsigned int total = 0;
unsigned int num_processed = 0;
- unsigned int tot_nump = 0;
+ unsigned short code = 0, cid = 0;
+ uint16_t cri_index = 0;
struct beiscsi_conn *beiscsi_conn;
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_endpoint *ep;
@@ -1865,51 +1882,102 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] &
CQE_VALID_MASK) {
+ if (beiscsi_hba_in_error(phba))
+ return 0;
+
be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
- ep = phba->ep_array[(u32) ((sol->
- dw[offsetof(struct amap_sol_cqe, cid) / 32] &
- SOL_CID_MASK) >> 6) -
- phba->fw_config.iscsi_cid_start];
+ code = (sol->dw[offsetof(struct amap_sol_cqe, code) / 32] &
+ CQE_CODE_MASK);
+
+ /* Get the CID */
+ if (is_chip_be2_be3r(phba)) {
+ cid = AMAP_GET_BITS(struct amap_sol_cqe, cid, sol);
+ } else {
+ if ((code == DRIVERMSG_NOTIFY) ||
+ (code == UNSOL_HDR_NOTIFY) ||
+ (code == UNSOL_DATA_NOTIFY))
+ cid = AMAP_GET_BITS(
+ struct amap_i_t_dpdu_cqe_v2,
+ cid, sol);
+ else
+ cid = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ cid, sol);
+ }
+
+ cri_index = BE_GET_CRI_FROM_CID(cid);
+ ep = phba->ep_array[cri_index];
+
+ if (ep == NULL) {
+ /* connection has already been freed
+ * just move on to next one
+ */
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT,
+ "BM_%d : proc cqe of disconn ep: cid %d\n",
+ cid);
+ goto proc_next_cqe;
+ }
beiscsi_ep = ep->dd_data;
beiscsi_conn = beiscsi_ep->conn;
- if (num_processed >= 32) {
- hwi_ring_cq_db(phba, cq->id,
- num_processed, 0, 0);
- tot_nump += num_processed;
+ /* replenish cq */
+ if (num_processed == 32) {
+ hwi_ring_cq_db(phba, cq->id, 32, 0);
num_processed = 0;
}
+ total++;
- switch ((u32) sol->dw[offsetof(struct amap_sol_cqe, code) /
- 32] & CQE_CODE_MASK) {
+ switch (code) {
case SOL_CMD_COMPLETE:
hwi_complete_cmd(beiscsi_conn, phba, sol);
break;
case DRIVERMSG_NOTIFY:
- SE_DEBUG(DBG_LVL_8, "Received DRIVERMSG_NOTIFY\n");
- dmsg = (struct dmsg_cqe *)sol;
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Received %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
+
hwi_complete_drvr_msgs(beiscsi_conn, phba, sol);
break;
case UNSOL_HDR_NOTIFY:
- SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR_ NOTIFY\n");
- hwi_process_default_pdu_ring(beiscsi_conn, phba,
- (struct i_t_dpdu_cqe *)sol);
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Received %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
+
+ spin_lock_bh(&phba->async_pdu_lock);
+ beiscsi_hdq_process_compl(beiscsi_conn,
+ (struct i_t_dpdu_cqe *)sol);
+ spin_unlock_bh(&phba->async_pdu_lock);
break;
case UNSOL_DATA_NOTIFY:
- SE_DEBUG(DBG_LVL_8, "Received UNSOL_DATA_NOTIFY\n");
- hwi_process_default_pdu_ring(beiscsi_conn, phba,
- (struct i_t_dpdu_cqe *)sol);
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+ "BM_%d : Received %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
+
+ spin_lock_bh(&phba->async_pdu_lock);
+ beiscsi_hdq_process_compl(beiscsi_conn,
+ (struct i_t_dpdu_cqe *)sol);
+ spin_unlock_bh(&phba->async_pdu_lock);
break;
case CXN_INVALIDATE_INDEX_NOTIFY:
case CMD_INVALIDATED_NOTIFY:
case CXN_INVALIDATE_NOTIFY:
- SE_DEBUG(DBG_LVL_1,
- "Ignoring CQ Error notification for cmd/cxn"
- "invalidate\n");
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Ignoring %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
break;
+ case CXN_KILLED_HDR_DIGEST_ERR:
case SOL_CMD_KILLED_DATA_DIGEST_ERR:
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+ "BM_%d : Cmd Notification %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
+ break;
case CMD_KILLED_INVALID_STATSN_RCVD:
case CMD_KILLED_INVALID_R2T_RCVD:
case CMD_CXN_KILLED_LUN_INVALID:
@@ -1917,123 +1985,200 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case CMD_CXN_KILLED_ITT_INVALID:
case CMD_CXN_KILLED_SEQ_OUTOFORDER:
case CMD_CXN_KILLED_INVALID_DATASN_RCVD:
- SE_DEBUG(DBG_LVL_1,
- "CQ Error notification for cmd.. "
- "code %d cid 0x%x\n",
- sol->dw[offsetof(struct amap_sol_cqe, code) /
- 32] & CQE_CODE_MASK,
- (sol->dw[offsetof(struct amap_sol_cqe, cid) /
- 32] & SOL_CID_MASK));
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+ "BM_%d : Cmd Notification %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
break;
case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
- SE_DEBUG(DBG_LVL_1,
- "Digest error on def pdu ring, dropping..\n");
- hwi_flush_default_pdu_buffer(phba, beiscsi_conn,
- (struct i_t_dpdu_cqe *) sol);
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Dropping %s[%d] on DPDU ring on CID : %d\n",
+ cqe_desc[code], code, cid);
+ spin_lock_bh(&phba->async_pdu_lock);
+ /* driver consumes the entry and drops the contents */
+ beiscsi_hdq_process_compl(beiscsi_conn,
+ (struct i_t_dpdu_cqe *)sol);
+ spin_unlock_bh(&phba->async_pdu_lock);
break;
case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL:
case CXN_KILLED_BURST_LEN_MISMATCH:
case CXN_KILLED_AHS_RCVD:
- case CXN_KILLED_HDR_DIGEST_ERR:
case CXN_KILLED_UNKNOWN_HDR:
case CXN_KILLED_STALE_ITT_TTT_RCVD:
case CXN_KILLED_INVALID_ITT_TTT_RCVD:
case CXN_KILLED_TIMED_OUT:
case CXN_KILLED_FIN_RCVD:
+ case CXN_KILLED_RST_SENT:
+ case CXN_KILLED_RST_RCVD:
case CXN_KILLED_BAD_UNSOL_PDU_RCVD:
case CXN_KILLED_BAD_WRB_INDEX_ERROR:
case CXN_KILLED_OVER_RUN_RESIDUAL:
case CXN_KILLED_UNDER_RUN_RESIDUAL:
case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN:
- SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
- "0x%x...\n",
- sol->dw[offsetof(struct amap_sol_cqe, code) /
- 32] & CQE_CODE_MASK,
- (sol->dw[offsetof(struct amap_sol_cqe, cid) /
- 32] & CQE_CID_MASK));
- iscsi_conn_failure(beiscsi_conn->conn,
- ISCSI_ERR_CONN_FAILED);
- break;
- case CXN_KILLED_RST_SENT:
- case CXN_KILLED_RST_RCVD:
- SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
- "received/sent on CID 0x%x...\n",
- sol->dw[offsetof(struct amap_sol_cqe, code) /
- 32] & CQE_CODE_MASK,
- (sol->dw[offsetof(struct amap_sol_cqe, cid) /
- 32] & CQE_CID_MASK));
- iscsi_conn_failure(beiscsi_conn->conn,
- ISCSI_ERR_CONN_FAILED);
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Event %s[%d] received on CID : %d\n",
+ cqe_desc[code], code, cid);
+ if (beiscsi_conn)
+ iscsi_conn_failure(beiscsi_conn->conn,
+ ISCSI_ERR_CONN_FAILED);
break;
default:
- SE_DEBUG(DBG_LVL_1, "CQ Error Invalid code= %d "
- "received on CID 0x%x...\n",
- sol->dw[offsetof(struct amap_sol_cqe, code) /
- 32] & CQE_CODE_MASK,
- (sol->dw[offsetof(struct amap_sol_cqe, cid) /
- 32] & CQE_CID_MASK));
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Invalid CQE Event Received Code : %d CID 0x%x...\n",
+ code, cid);
break;
}
+proc_next_cqe:
AMAP_SET_BITS(struct amap_sol_cqe, valid, sol, 0);
queue_tail_inc(cq);
sol = queue_tail_node(cq);
num_processed++;
+ if (total == budget)
+ break;
}
- if (num_processed > 0) {
- tot_nump += num_processed;
- hwi_ring_cq_db(phba, cq->id, num_processed, 1, 0);
- }
- return tot_nump;
+ hwi_ring_cq_db(phba, cq->id, num_processed, 1);
+ return total;
}
-void beiscsi_process_all_cqs(struct work_struct *work)
+static int be_iopoll(struct irq_poll *iop, int budget)
{
- unsigned long flags;
- struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
+ unsigned int ret, io_events;
+ struct beiscsi_hba *phba;
struct be_eq_obj *pbe_eq;
- struct beiscsi_hba *phba =
- container_of(work, struct beiscsi_hba, work_cqs);
+ struct be_eq_entry *eqe = NULL;
+ struct be_queue_info *eq;
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
- if (phba->msix_enabled)
- pbe_eq = &phwi_context->be_eq[phba->num_cpus];
- else
- pbe_eq = &phwi_context->be_eq[0];
+ pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
+ phba = pbe_eq->phba;
+ if (beiscsi_hba_in_error(phba)) {
+ irq_poll_complete(iop);
+ return 0;
+ }
- if (phba->todo_mcc_cq) {
- spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 0;
- spin_unlock_irqrestore(&phba->isr_lock, flags);
- beiscsi_process_mcc_isr(phba);
+ io_events = 0;
+ eq = &pbe_eq->q;
+ eqe = queue_tail_node(eq);
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] &
+ EQE_VALID_MASK) {
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ io_events++;
}
+ hwi_ring_eq_db(phba, eq->id, 1, io_events, 0, 1);
- if (phba->todo_cq) {
- spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_cq = 0;
- spin_unlock_irqrestore(&phba->isr_lock, flags);
- beiscsi_process_cq(pbe_eq);
+ ret = beiscsi_process_cq(pbe_eq, budget);
+ pbe_eq->cq_count += ret;
+ if (ret < budget) {
+ irq_poll_complete(iop);
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+ "BM_%d : rearm pbe_eq->q.id =%d ret %d\n",
+ pbe_eq->q.id, ret);
+ if (!beiscsi_hba_in_error(phba))
+ hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
}
+ return ret;
}
-static int be_iopoll(struct blk_iopoll *iop, int budget)
+static void
+hwi_write_sgl_v2(struct iscsi_wrb *pwrb, struct scatterlist *sg,
+ unsigned int num_sg, struct beiscsi_io_task *io_task)
{
- static unsigned int ret;
- struct beiscsi_hba *phba;
- struct be_eq_obj *pbe_eq;
+ struct iscsi_sge *psgl;
+ unsigned int sg_len, index;
+ unsigned int sge_len = 0;
+ unsigned long long addr;
+ struct scatterlist *l_sg;
+ unsigned int offset;
- pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
- ret = beiscsi_process_cq(pbe_eq);
- if (ret < budget) {
- phba = pbe_eq->phba;
- blk_iopoll_complete(iop);
- SE_DEBUG(DBG_LVL_8, "rearm pbe_eq->q.id =%d\n", pbe_eq->q.id);
- hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, iscsi_bhs_addr_lo, pwrb,
+ io_task->bhs_pa.u.a32.address_lo);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, iscsi_bhs_addr_hi, pwrb,
+ io_task->bhs_pa.u.a32.address_hi);
+
+ l_sg = sg;
+ for (index = 0; (index < num_sg) && (index < 2); index++,
+ sg = sg_next(sg)) {
+ if (index == 0) {
+ sg_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge0_addr_lo, pwrb,
+ lower_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge0_addr_hi, pwrb,
+ upper_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge0_len, pwrb,
+ sg_len);
+ sge_len = sg_len;
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_r2t_offset,
+ pwrb, sge_len);
+ sg_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge1_addr_lo, pwrb,
+ lower_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge1_addr_hi, pwrb,
+ upper_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge1_len, pwrb,
+ sg_len);
+ }
}
- return ret;
+ psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag;
+ memset(psgl, 0, sizeof(*psgl) * BE2_SGE);
+
+ AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len - 2);
+
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
+ io_task->bhs_pa.u.a32.address_hi);
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
+ io_task->bhs_pa.u.a32.address_lo);
+
+ if (num_sg == 1) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
+ 1);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
+ 0);
+ } else if (num_sg == 2) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
+ 0);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
+ 1);
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
+ 0);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
+ 0);
+ }
+
+ sg = l_sg;
+ psgl++;
+ psgl++;
+ offset = 0;
+ for (index = 0; index < num_sg; index++, sg = sg_next(sg), psgl++) {
+ sg_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
+ lower_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
+ upper_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, sg_len);
+ AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, offset);
+ AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0);
+ offset += sg_len;
+ }
+ psgl--;
+ AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1);
}
static void
@@ -2124,13 +2269,18 @@ hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg,
AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1);
}
-static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
+/**
+ * hwi_write_buffer()- Populate the WRB with task info
+ * @pwrb: ptr to the WRB entry
+ * @task: iscsi task which is to be executed
+ **/
+static int hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
{
struct iscsi_sge *psgl;
- unsigned long long addr;
struct beiscsi_io_task *io_task = task->dd_data;
struct beiscsi_conn *beiscsi_conn = io_task->conn;
struct beiscsi_hba *phba = beiscsi_conn->phba;
+ uint8_t dsp_value = 0;
io_task->bhs_len = sizeof(struct be_nonio_bhs) - 2;
AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_lo, pwrb,
@@ -2139,26 +2289,41 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
io_task->bhs_pa.u.a32.address_hi);
if (task->data) {
- if (task->data_count) {
- AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
- addr = (u64) pci_map_single(phba->pcidev,
- task->data,
- task->data_count, 1);
- } else {
- AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
- addr = 0;
- }
+
+ /* Check for the data_count */
+ dsp_value = (task->data_count) ? 1 : 0;
+
+ if (is_chip_be2_be3r(phba))
+ AMAP_SET_BITS(struct amap_iscsi_wrb, dsp,
+ pwrb, dsp_value);
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp,
+ pwrb, dsp_value);
+
+ /* Map addr only if there is data_count */
+ if (dsp_value) {
+ io_task->mtask_addr = dma_map_single(&phba->pcidev->dev,
+ task->data,
+ task->data_count,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&phba->pcidev->dev,
+ io_task->mtask_addr))
+ return -ENOMEM;
+ io_task->mtask_data_count = task->data_count;
+ } else
+ io_task->mtask_addr = 0;
+
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_lo, pwrb,
- ((u32)(addr & 0xFFFFFFFF)));
+ lower_32_bits(io_task->mtask_addr));
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_hi, pwrb,
- ((u32)(addr >> 32)));
+ upper_32_bits(io_task->mtask_addr));
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb,
task->data_count);
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, 1);
} else {
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
- addr = 0;
+ io_task->mtask_addr = 0;
}
psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag;
@@ -2181,36 +2346,27 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
psgl++;
if (task->data) {
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
- ((u32)(addr & 0xFFFFFFFF)));
+ lower_32_bits(io_task->mtask_addr));
AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
- ((u32)(addr >> 32)));
+ upper_32_bits(io_task->mtask_addr));
}
AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0x106);
}
AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1);
+ return 0;
}
+/**
+ * beiscsi_find_mem_req()- Find mem needed
+ * @phba: ptr to HBA struct
+ **/
static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
{
- unsigned int num_cq_pages, num_async_pdu_buf_pages;
+ uint8_t mem_descr_index, ulp_num;
+ unsigned int num_async_pdu_buf_pages;
unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn;
unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages;
- num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
- sizeof(struct sol_cqe));
- num_async_pdu_buf_pages =
- PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
- phba->params.defpdu_hdr_sz);
- num_async_pdu_buf_sgl_pages =
- PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
- sizeof(struct phys_addr));
- num_async_pdu_data_pages =
- PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
- phba->params.defpdu_data_sz);
- num_async_pdu_data_sgl_pages =
- PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
- sizeof(struct phys_addr));
-
phba->params.hwi_ws_sz = sizeof(struct hwi_controller);
phba->mem_req[ISCSI_MEM_GLOBAL_HEADER] = 2 *
@@ -2231,54 +2387,129 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
phba->params.icds_per_ctrl;
phba->mem_req[HWI_MEM_SGE] = sizeof(struct iscsi_sge) *
phba->params.num_sge_per_io * phba->params.icds_per_ctrl;
-
- phba->mem_req[HWI_MEM_ASYNC_HEADER_BUF] =
- num_async_pdu_buf_pages * PAGE_SIZE;
- phba->mem_req[HWI_MEM_ASYNC_DATA_BUF] =
- num_async_pdu_data_pages * PAGE_SIZE;
- phba->mem_req[HWI_MEM_ASYNC_HEADER_RING] =
- num_async_pdu_buf_sgl_pages * PAGE_SIZE;
- phba->mem_req[HWI_MEM_ASYNC_DATA_RING] =
- num_async_pdu_data_sgl_pages * PAGE_SIZE;
- phba->mem_req[HWI_MEM_ASYNC_HEADER_HANDLE] =
- phba->params.asyncpdus_per_ctrl *
- sizeof(struct async_pdu_handle);
- phba->mem_req[HWI_MEM_ASYNC_DATA_HANDLE] =
- phba->params.asyncpdus_per_ctrl *
- sizeof(struct async_pdu_handle);
- phba->mem_req[HWI_MEM_ASYNC_PDU_CONTEXT] =
- sizeof(struct hwi_async_pdu_context) +
- (phba->params.cxns_per_ctrl * sizeof(struct hwi_async_entry));
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+
+ num_async_pdu_buf_sgl_pages =
+ PAGES_REQUIRED(BEISCSI_ASYNC_HDQ_SIZE(
+ phba, ulp_num) *
+ sizeof(struct phys_addr));
+
+ num_async_pdu_buf_pages =
+ PAGES_REQUIRED(BEISCSI_ASYNC_HDQ_SIZE(
+ phba, ulp_num) *
+ phba->params.defpdu_hdr_sz);
+
+ num_async_pdu_data_pages =
+ PAGES_REQUIRED(BEISCSI_ASYNC_HDQ_SIZE(
+ phba, ulp_num) *
+ phba->params.defpdu_data_sz);
+
+ num_async_pdu_data_sgl_pages =
+ PAGES_REQUIRED(BEISCSI_ASYNC_HDQ_SIZE(
+ phba, ulp_num) *
+ sizeof(struct phys_addr));
+
+ mem_descr_index = (HWI_MEM_TEMPLATE_HDR_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ BEISCSI_GET_CID_COUNT(phba, ulp_num) *
+ BEISCSI_TEMPLATE_HDR_PER_CXN_SIZE;
+
+ mem_descr_index = (HWI_MEM_ASYNC_HEADER_BUF_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ num_async_pdu_buf_pages *
+ PAGE_SIZE;
+
+ mem_descr_index = (HWI_MEM_ASYNC_DATA_BUF_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ num_async_pdu_data_pages *
+ PAGE_SIZE;
+
+ mem_descr_index = (HWI_MEM_ASYNC_HEADER_RING_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ num_async_pdu_buf_sgl_pages *
+ PAGE_SIZE;
+
+ mem_descr_index = (HWI_MEM_ASYNC_DATA_RING_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ num_async_pdu_data_sgl_pages *
+ PAGE_SIZE;
+
+ mem_descr_index = (HWI_MEM_ASYNC_HEADER_HANDLE_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ BEISCSI_ASYNC_HDQ_SIZE(phba, ulp_num) *
+ sizeof(struct hd_async_handle);
+
+ mem_descr_index = (HWI_MEM_ASYNC_DATA_HANDLE_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ BEISCSI_ASYNC_HDQ_SIZE(phba, ulp_num) *
+ sizeof(struct hd_async_handle);
+
+ mem_descr_index = (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+ phba->mem_req[mem_descr_index] =
+ sizeof(struct hd_async_context) +
+ (BEISCSI_ASYNC_HDQ_SIZE(phba, ulp_num) *
+ sizeof(struct hd_async_entry));
+ }
+ }
}
static int beiscsi_alloc_mem(struct beiscsi_hba *phba)
{
- struct be_mem_descriptor *mem_descr;
dma_addr_t bus_add;
+ struct hwi_controller *phwi_ctrlr;
+ struct be_mem_descriptor *mem_descr;
struct mem_array *mem_arr, *mem_arr_orig;
unsigned int i, j, alloc_size, curr_alloc_size;
- phba->phwi_ctrlr = kmalloc(phba->params.hwi_ws_sz, GFP_KERNEL);
+ phba->phwi_ctrlr = kzalloc(phba->params.hwi_ws_sz, GFP_KERNEL);
if (!phba->phwi_ctrlr)
return -ENOMEM;
+ /* Allocate memory for wrb_context */
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_ctrlr->wrb_context = kcalloc(phba->params.cxns_per_ctrl,
+ sizeof(struct hwi_wrb_context),
+ GFP_KERNEL);
+ if (!phwi_ctrlr->wrb_context) {
+ kfree(phba->phwi_ctrlr);
+ return -ENOMEM;
+ }
+
phba->init_mem = kcalloc(SE_MEM_MAX, sizeof(*mem_descr),
GFP_KERNEL);
if (!phba->init_mem) {
+ kfree(phwi_ctrlr->wrb_context);
kfree(phba->phwi_ctrlr);
return -ENOMEM;
}
- mem_arr_orig = kmalloc(sizeof(*mem_arr_orig) * BEISCSI_MAX_FRAGS_INIT,
- GFP_KERNEL);
+ mem_arr_orig = kmalloc_array(BEISCSI_MAX_FRAGS_INIT,
+ sizeof(*mem_arr_orig),
+ GFP_KERNEL);
if (!mem_arr_orig) {
kfree(phba->init_mem);
+ kfree(phwi_ctrlr->wrb_context);
kfree(phba->phwi_ctrlr);
return -ENOMEM;
}
mem_descr = phba->init_mem;
for (i = 0; i < SE_MEM_MAX; i++) {
+ if (!phba->mem_req[i]) {
+ mem_descr->mem_array = NULL;
+ mem_descr++;
+ continue;
+ }
+
j = 0;
mem_arr = mem_arr_orig;
alloc_size = phba->mem_req[i];
@@ -2286,10 +2517,9 @@ static int beiscsi_alloc_mem(struct beiscsi_hba *phba)
BEISCSI_MAX_FRAGS_INIT);
curr_alloc_size = min(be_max_phys_size * 1024, alloc_size);
do {
- mem_arr->virtual_address = pci_alloc_consistent(
- phba->pcidev,
- curr_alloc_size,
- &bus_add);
+ mem_arr->virtual_address =
+ dma_alloc_coherent(&phba->pcidev->dev,
+ curr_alloc_size, &bus_add, GFP_KERNEL);
if (!mem_arr->virtual_address) {
if (curr_alloc_size <= BE_MIN_MEM_SIZE)
goto free_mem;
@@ -2312,8 +2542,8 @@ static int beiscsi_alloc_mem(struct beiscsi_hba *phba)
} while (alloc_size);
mem_descr->num_elements = j;
mem_descr->size_in_bytes = phba->mem_req[i];
- mem_descr->mem_array = kmalloc(sizeof(*mem_arr) * j,
- GFP_KERNEL);
+ mem_descr->mem_array = kmalloc_array(j, sizeof(*mem_arr),
+ GFP_KERNEL);
if (!mem_descr->mem_array)
goto free_mem;
@@ -2327,7 +2557,7 @@ free_mem:
mem_descr->num_elements = j;
while ((i) || (j)) {
for (j = mem_descr->num_elements; j > 0; j--) {
- pci_free_consistent(phba->pcidev,
+ dma_free_coherent(&phba->pcidev->dev,
mem_descr->mem_array[j - 1].size,
mem_descr->mem_array[j - 1].
virtual_address,
@@ -2343,6 +2573,7 @@ free_mem:
}
kfree(mem_arr_orig);
kfree(phba->init_mem);
+ kfree(phba->phwi_ctrlr->wrb_context);
kfree(phba->phwi_ctrlr);
return -ENOMEM;
}
@@ -2378,40 +2609,68 @@ static void iscsi_init_global_templates(struct beiscsi_hba *phba)
AMAP_SET_BITS(struct amap_pdu_nop_out, i_bit, pnop_out, 0);
}
-static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
+static int beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
{
struct be_mem_descriptor *mem_descr_wrbh, *mem_descr_wrb;
- struct wrb_handle *pwrb_handle;
+ struct hwi_context_memory *phwi_ctxt;
+ struct wrb_handle *pwrb_handle = NULL;
struct hwi_controller *phwi_ctrlr;
struct hwi_wrb_context *pwrb_context;
- struct iscsi_wrb *pwrb;
- unsigned int num_cxn_wrbh;
- unsigned int num_cxn_wrb, j, idx, index;
+ struct iscsi_wrb *pwrb = NULL;
+ unsigned int num_cxn_wrbh = 0;
+ unsigned int num_cxn_wrb = 0, j, idx = 0, index;
mem_descr_wrbh = phba->init_mem;
mem_descr_wrbh += HWI_MEM_WRBH;
mem_descr_wrb = phba->init_mem;
mem_descr_wrb += HWI_MEM_WRB;
-
- idx = 0;
- pwrb_handle = mem_descr_wrbh->mem_array[idx].virtual_address;
- num_cxn_wrbh = ((mem_descr_wrbh->mem_array[idx].size) /
- ((sizeof(struct wrb_handle)) *
- phba->params.wrbs_per_cxn));
phwi_ctrlr = phba->phwi_ctrlr;
- for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
+ /* Allocate memory for WRBQ */
+ phwi_ctxt = phwi_ctrlr->phwi_ctxt;
+ phwi_ctxt->be_wrbq = kcalloc(phba->params.cxns_per_ctrl,
+ sizeof(struct be_queue_info),
+ GFP_KERNEL);
+ if (!phwi_ctxt->be_wrbq) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : WRBQ Mem Alloc Failed\n");
+ return -ENOMEM;
+ }
+
+ for (index = 0; index < phba->params.cxns_per_ctrl; index++) {
pwrb_context = &phwi_ctrlr->wrb_context[index];
pwrb_context->pwrb_handle_base =
- kzalloc(sizeof(struct wrb_handle *) *
- phba->params.wrbs_per_cxn, GFP_KERNEL);
+ kcalloc(phba->params.wrbs_per_cxn,
+ sizeof(struct wrb_handle *),
+ GFP_KERNEL);
+ if (!pwrb_context->pwrb_handle_base) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Mem Alloc Failed. Failing to load\n");
+ goto init_wrb_hndl_failed;
+ }
pwrb_context->pwrb_handle_basestd =
- kzalloc(sizeof(struct wrb_handle *) *
- phba->params.wrbs_per_cxn, GFP_KERNEL);
+ kcalloc(phba->params.wrbs_per_cxn,
+ sizeof(struct wrb_handle *),
+ GFP_KERNEL);
+ if (!pwrb_context->pwrb_handle_basestd) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Mem Alloc Failed. Failing to load\n");
+ goto init_wrb_hndl_failed;
+ }
+ if (!num_cxn_wrbh) {
+ pwrb_handle =
+ mem_descr_wrbh->mem_array[idx].virtual_address;
+ num_cxn_wrbh = ((mem_descr_wrbh->mem_array[idx].size) /
+ ((sizeof(struct wrb_handle)) *
+ phba->params.wrbs_per_cxn));
+ idx++;
+ }
+ pwrb_context->alloc_index = 0;
+ pwrb_context->wrb_handles_available = 0;
+ pwrb_context->free_index = 0;
+
if (num_cxn_wrbh) {
- pwrb_context->alloc_index = 0;
- pwrb_context->wrb_handles_available = 0;
for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
pwrb_context->pwrb_handle_base[j] = pwrb_handle;
pwrb_context->pwrb_handle_basestd[j] =
@@ -2420,49 +2679,22 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
pwrb_handle->wrb_index = j;
pwrb_handle++;
}
- pwrb_context->free_index = 0;
- num_cxn_wrbh--;
- } else {
- idx++;
- pwrb_handle =
- mem_descr_wrbh->mem_array[idx].virtual_address;
- num_cxn_wrbh =
- ((mem_descr_wrbh->mem_array[idx].size) /
- ((sizeof(struct wrb_handle)) *
- phba->params.wrbs_per_cxn));
- pwrb_context->alloc_index = 0;
- for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
- pwrb_context->pwrb_handle_base[j] = pwrb_handle;
- pwrb_context->pwrb_handle_basestd[j] =
- pwrb_handle;
- pwrb_context->wrb_handles_available++;
- pwrb_handle->wrb_index = j;
- pwrb_handle++;
- }
- pwrb_context->free_index = 0;
num_cxn_wrbh--;
}
+ spin_lock_init(&pwrb_context->wrb_lock);
}
idx = 0;
- pwrb = mem_descr_wrb->mem_array[idx].virtual_address;
- num_cxn_wrb = (mem_descr_wrb->mem_array[idx].size) /
- ((sizeof(struct iscsi_wrb) *
- phba->params.wrbs_per_cxn));
- for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
+ for (index = 0; index < phba->params.cxns_per_ctrl; index++) {
pwrb_context = &phwi_ctrlr->wrb_context[index];
- if (num_cxn_wrb) {
- for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
- pwrb_handle = pwrb_context->pwrb_handle_base[j];
- pwrb_handle->pwrb = pwrb;
- pwrb++;
- }
- num_cxn_wrb--;
- } else {
- idx++;
+ if (!num_cxn_wrb) {
pwrb = mem_descr_wrb->mem_array[idx].virtual_address;
num_cxn_wrb = (mem_descr_wrb->mem_array[idx].size) /
- ((sizeof(struct iscsi_wrb) *
- phba->params.wrbs_per_cxn));
+ ((sizeof(struct iscsi_wrb) *
+ phba->params.wrbs_per_cxn));
+ idx++;
+ }
+
+ if (num_cxn_wrb) {
for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
pwrb_handle = pwrb_context->pwrb_handle_base[j];
pwrb_handle->pwrb = pwrb;
@@ -2471,164 +2703,247 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
num_cxn_wrb--;
}
}
+ return 0;
+init_wrb_hndl_failed:
+ for (j = index; j > 0; j--) {
+ pwrb_context = &phwi_ctrlr->wrb_context[j];
+ kfree(pwrb_context->pwrb_handle_base);
+ kfree(pwrb_context->pwrb_handle_basestd);
+ }
+ kfree(phwi_ctxt->be_wrbq);
+ return -ENOMEM;
}
-static void hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
+static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
{
+ uint8_t ulp_num;
struct hwi_controller *phwi_ctrlr;
struct hba_parameters *p = &phba->params;
- struct hwi_async_pdu_context *pasync_ctx;
- struct async_pdu_handle *pasync_header_h, *pasync_data_h;
- unsigned int index;
+ struct hd_async_context *pasync_ctx;
+ struct hd_async_handle *pasync_header_h, *pasync_data_h;
+ unsigned int index, idx, num_per_mem, num_async_data;
struct be_mem_descriptor *mem_descr;
- mem_descr = (struct be_mem_descriptor *)phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_PDU_CONTEXT;
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+ /* get async_ctx for each ULP */
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET));
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num] =
+ (struct hd_async_context *)
+ mem_descr->mem_array[0].virtual_address;
+
+ pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num];
+ memset(pasync_ctx, 0, sizeof(*pasync_ctx));
+
+ pasync_ctx->async_entry =
+ (struct hd_async_entry *)
+ ((long unsigned int)pasync_ctx +
+ sizeof(struct hd_async_context));
+
+ pasync_ctx->num_entries = BEISCSI_ASYNC_HDQ_SIZE(phba,
+ ulp_num);
+ /* setup header buffers */
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += HWI_MEM_ASYNC_HEADER_BUF_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
+ if (mem_descr->mem_array[0].virtual_address) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_async_pdu_ctx"
+ " HWI_MEM_ASYNC_HEADER_BUF_ULP%d va=%p\n",
+ ulp_num,
+ mem_descr->mem_array[0].
+ virtual_address);
+ } else
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT,
+ "BM_%d : No Virtual address for ULP : %d\n",
+ ulp_num);
+
+ pasync_ctx->async_header.pi = 0;
+ pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz;
+ pasync_ctx->async_header.va_base =
+ mem_descr->mem_array[0].virtual_address;
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_ctrlr->phwi_ctxt->pasync_ctx = (struct hwi_async_pdu_context *)
+ pasync_ctx->async_header.pa_base.u.a64.address =
+ mem_descr->mem_array[0].
+ bus_address.u.a64.address;
+
+ /* setup header buffer sgls */
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += HWI_MEM_ASYNC_HEADER_RING_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
+ if (mem_descr->mem_array[0].virtual_address) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_async_pdu_ctx"
+ " HWI_MEM_ASYNC_HEADER_RING_ULP%d va=%p\n",
+ ulp_num,
+ mem_descr->mem_array[0].
+ virtual_address);
+ } else
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT,
+ "BM_%d : No Virtual address for ULP : %d\n",
+ ulp_num);
+
+ pasync_ctx->async_header.ring_base =
mem_descr->mem_array[0].virtual_address;
- pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx;
- memset(pasync_ctx, 0, sizeof(*pasync_ctx));
-
- pasync_ctx->async_header.num_entries = p->asyncpdus_per_ctrl;
- pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz;
- pasync_ctx->async_data.buffer_size = p->defpdu_data_sz;
- pasync_ctx->async_data.num_entries = p->asyncpdus_per_ctrl;
-
- mem_descr = (struct be_mem_descriptor *)phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_HEADER_BUF;
- if (mem_descr->mem_array[0].virtual_address) {
- SE_DEBUG(DBG_LVL_8,
- "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_BUF"
- "va=%p\n", mem_descr->mem_array[0].virtual_address);
- } else
- shost_printk(KERN_WARNING, phba->shost,
- "No Virtual address\n");
- pasync_ctx->async_header.va_base =
- mem_descr->mem_array[0].virtual_address;
+ /* setup header buffer handles */
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
+ if (mem_descr->mem_array[0].virtual_address) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_async_pdu_ctx"
+ " HWI_MEM_ASYNC_HEADER_HANDLE_ULP%d va=%p\n",
+ ulp_num,
+ mem_descr->mem_array[0].
+ virtual_address);
+ } else
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT,
+ "BM_%d : No Virtual address for ULP : %d\n",
+ ulp_num);
+
+ pasync_ctx->async_header.handle_base =
+ mem_descr->mem_array[0].virtual_address;
- pasync_ctx->async_header.pa_base.u.a64.address =
- mem_descr->mem_array[0].bus_address.u.a64.address;
+ /* setup data buffer sgls */
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += HWI_MEM_ASYNC_DATA_RING_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
+ if (mem_descr->mem_array[0].virtual_address) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_async_pdu_ctx"
+ " HWI_MEM_ASYNC_DATA_RING_ULP%d va=%p\n",
+ ulp_num,
+ mem_descr->mem_array[0].
+ virtual_address);
+ } else
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT,
+ "BM_%d : No Virtual address for ULP : %d\n",
+ ulp_num);
+
+ pasync_ctx->async_data.ring_base =
+ mem_descr->mem_array[0].virtual_address;
- mem_descr = (struct be_mem_descriptor *)phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_HEADER_RING;
- if (mem_descr->mem_array[0].virtual_address) {
- SE_DEBUG(DBG_LVL_8,
- "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_RING"
- "va=%p\n", mem_descr->mem_array[0].virtual_address);
- } else
- shost_printk(KERN_WARNING, phba->shost,
- "No Virtual address\n");
- pasync_ctx->async_header.ring_base =
- mem_descr->mem_array[0].virtual_address;
-
- mem_descr = (struct be_mem_descriptor *)phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE;
- if (mem_descr->mem_array[0].virtual_address) {
- SE_DEBUG(DBG_LVL_8,
- "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_HANDLE"
- "va=%p\n", mem_descr->mem_array[0].virtual_address);
- } else
- shost_printk(KERN_WARNING, phba->shost,
- "No Virtual address\n");
-
- pasync_ctx->async_header.handle_base =
- mem_descr->mem_array[0].virtual_address;
- pasync_ctx->async_header.writables = 0;
- INIT_LIST_HEAD(&pasync_ctx->async_header.free_list);
-
- mem_descr = (struct be_mem_descriptor *)phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_DATA_BUF;
- if (mem_descr->mem_array[0].virtual_address) {
- SE_DEBUG(DBG_LVL_8,
- "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_BUF"
- "va=%p\n", mem_descr->mem_array[0].virtual_address);
- } else
- shost_printk(KERN_WARNING, phba->shost,
- "No Virtual address\n");
- pasync_ctx->async_data.va_base =
- mem_descr->mem_array[0].virtual_address;
- pasync_ctx->async_data.pa_base.u.a64.address =
- mem_descr->mem_array[0].bus_address.u.a64.address;
-
- mem_descr = (struct be_mem_descriptor *)phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_DATA_RING;
- if (mem_descr->mem_array[0].virtual_address) {
- SE_DEBUG(DBG_LVL_8,
- "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_RING"
- "va=%p\n", mem_descr->mem_array[0].virtual_address);
- } else
- shost_printk(KERN_WARNING, phba->shost,
- "No Virtual address\n");
-
- pasync_ctx->async_data.ring_base =
- mem_descr->mem_array[0].virtual_address;
-
- mem_descr = (struct be_mem_descriptor *)phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_DATA_HANDLE;
- if (!mem_descr->mem_array[0].virtual_address)
- shost_printk(KERN_WARNING, phba->shost,
- "No Virtual address\n");
-
- pasync_ctx->async_data.handle_base =
- mem_descr->mem_array[0].virtual_address;
- pasync_ctx->async_data.writables = 0;
- INIT_LIST_HEAD(&pasync_ctx->async_data.free_list);
-
- pasync_header_h =
- (struct async_pdu_handle *)pasync_ctx->async_header.handle_base;
- pasync_data_h =
- (struct async_pdu_handle *)pasync_ctx->async_data.handle_base;
-
- for (index = 0; index < p->asyncpdus_per_ctrl; index++) {
- pasync_header_h->cri = -1;
- pasync_header_h->index = (char)index;
- INIT_LIST_HEAD(&pasync_header_h->link);
- pasync_header_h->pbuffer =
- (void *)((unsigned long)
- (pasync_ctx->async_header.va_base) +
- (p->defpdu_hdr_sz * index));
-
- pasync_header_h->pa.u.a64.address =
- pasync_ctx->async_header.pa_base.u.a64.address +
- (p->defpdu_hdr_sz * index);
-
- list_add_tail(&pasync_header_h->link,
- &pasync_ctx->async_header.free_list);
- pasync_header_h++;
- pasync_ctx->async_header.free_entries++;
- pasync_ctx->async_header.writables++;
-
- INIT_LIST_HEAD(&pasync_ctx->async_entry[index].wait_queue.list);
- INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
- header_busy_list);
- pasync_data_h->cri = -1;
- pasync_data_h->index = (char)index;
- INIT_LIST_HEAD(&pasync_data_h->link);
- pasync_data_h->pbuffer =
- (void *)((unsigned long)
- (pasync_ctx->async_data.va_base) +
- (p->defpdu_data_sz * index));
-
- pasync_data_h->pa.u.a64.address =
- pasync_ctx->async_data.pa_base.u.a64.address +
- (p->defpdu_data_sz * index);
-
- list_add_tail(&pasync_data_h->link,
- &pasync_ctx->async_data.free_list);
- pasync_data_h++;
- pasync_ctx->async_data.free_entries++;
- pasync_ctx->async_data.writables++;
-
- INIT_LIST_HEAD(&pasync_ctx->async_entry[index].data_busy_list);
- }
-
- pasync_ctx->async_header.host_write_ptr = 0;
- pasync_ctx->async_header.ep_read_ptr = -1;
- pasync_ctx->async_data.host_write_ptr = 0;
- pasync_ctx->async_data.ep_read_ptr = -1;
+ /* setup data buffer handles */
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += HWI_MEM_ASYNC_DATA_HANDLE_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
+ if (!mem_descr->mem_array[0].virtual_address)
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT,
+ "BM_%d : No Virtual address for ULP : %d\n",
+ ulp_num);
+
+ pasync_ctx->async_data.handle_base =
+ mem_descr->mem_array[0].virtual_address;
+
+ pasync_header_h =
+ (struct hd_async_handle *)
+ pasync_ctx->async_header.handle_base;
+ pasync_data_h =
+ (struct hd_async_handle *)
+ pasync_ctx->async_data.handle_base;
+
+ /* setup data buffers */
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += HWI_MEM_ASYNC_DATA_BUF_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
+ if (mem_descr->mem_array[0].virtual_address) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_async_pdu_ctx"
+ " HWI_MEM_ASYNC_DATA_BUF_ULP%d va=%p\n",
+ ulp_num,
+ mem_descr->mem_array[0].
+ virtual_address);
+ } else
+ beiscsi_log(phba, KERN_WARNING,
+ BEISCSI_LOG_INIT,
+ "BM_%d : No Virtual address for ULP : %d\n",
+ ulp_num);
+
+ idx = 0;
+ pasync_ctx->async_data.pi = 0;
+ pasync_ctx->async_data.buffer_size = p->defpdu_data_sz;
+ pasync_ctx->async_data.va_base =
+ mem_descr->mem_array[idx].virtual_address;
+ pasync_ctx->async_data.pa_base.u.a64.address =
+ mem_descr->mem_array[idx].
+ bus_address.u.a64.address;
+
+ num_async_data = ((mem_descr->mem_array[idx].size) /
+ phba->params.defpdu_data_sz);
+ num_per_mem = 0;
+
+ for (index = 0; index < BEISCSI_ASYNC_HDQ_SIZE
+ (phba, ulp_num); index++) {
+ pasync_header_h->cri = -1;
+ pasync_header_h->is_header = 1;
+ pasync_header_h->index = index;
+ INIT_LIST_HEAD(&pasync_header_h->link);
+ pasync_header_h->pbuffer =
+ (void *)((unsigned long)
+ (pasync_ctx->
+ async_header.va_base) +
+ (p->defpdu_hdr_sz * index));
+
+ pasync_header_h->pa.u.a64.address =
+ pasync_ctx->async_header.pa_base.u.a64.
+ address + (p->defpdu_hdr_sz * index);
+
+ pasync_ctx->async_entry[index].header =
+ pasync_header_h;
+ pasync_header_h++;
+ INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
+ wq.list);
+
+ pasync_data_h->cri = -1;
+ pasync_data_h->is_header = 0;
+ pasync_data_h->index = index;
+ INIT_LIST_HEAD(&pasync_data_h->link);
+
+ if (!num_async_data) {
+ num_per_mem = 0;
+ idx++;
+ pasync_ctx->async_data.va_base =
+ mem_descr->mem_array[idx].
+ virtual_address;
+ pasync_ctx->async_data.pa_base.u.
+ a64.address =
+ mem_descr->mem_array[idx].
+ bus_address.u.a64.address;
+ num_async_data =
+ ((mem_descr->mem_array[idx].
+ size) /
+ phba->params.defpdu_data_sz);
+ }
+ pasync_data_h->pbuffer =
+ (void *)((unsigned long)
+ (pasync_ctx->async_data.va_base) +
+ (p->defpdu_data_sz * num_per_mem));
+
+ pasync_data_h->pa.u.a64.address =
+ pasync_ctx->async_data.pa_base.u.a64.
+ address + (p->defpdu_data_sz *
+ num_per_mem);
+ num_per_mem++;
+ num_async_data--;
+
+ pasync_ctx->async_entry[index].data =
+ pasync_data_h;
+ pasync_data_h++;
+ }
+ }
+ }
+
+ return 0;
}
static int
@@ -2638,7 +2953,7 @@ be_sgl_create_contiguous(void *virtual_address,
{
WARN_ON(!virtual_address);
WARN_ON(!physical_address);
- WARN_ON(!length > 0);
+ WARN_ON(!length);
WARN_ON(!sgl);
sgl->va = virtual_address;
@@ -2696,17 +3011,17 @@ static int be_fill_queue(struct be_queue_info *q,
static int beiscsi_create_eqs(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context)
{
+ int ret = -ENOMEM, eq_for_mcc;
unsigned int i, num_eq_pages;
- int ret, eq_for_mcc;
struct be_queue_info *eq;
struct be_dma_mem *mem;
void *eq_vaddress;
dma_addr_t paddr;
- num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
+ num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries *
sizeof(struct be_eq_entry));
- if (phba->msix_enabled)
+ if (phba->pcidev->msix_enabled)
eq_for_mcc = 1;
else
eq_for_mcc = 0;
@@ -2714,39 +3029,44 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba,
eq = &phwi_context->be_eq[i].q;
mem = &eq->dma_mem;
phwi_context->be_eq[i].phba = phba;
- eq_vaddress = pci_alloc_consistent(phba->pcidev,
- num_eq_pages * PAGE_SIZE,
- &paddr);
- if (!eq_vaddress)
+ eq_vaddress = dma_alloc_coherent(&phba->pcidev->dev,
+ num_eq_pages * PAGE_SIZE,
+ &paddr, GFP_KERNEL);
+ if (!eq_vaddress) {
+ ret = -ENOMEM;
goto create_eq_error;
+ }
mem->va = eq_vaddress;
ret = be_fill_queue(eq, phba->params.num_eq_entries,
sizeof(struct be_eq_entry), eq_vaddress);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for EQ\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : be_fill_queue Failed for EQ\n");
goto create_eq_error;
}
mem->dma = paddr;
ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
- phwi_context->cur_eqd);
+ BEISCSI_EQ_DELAY_DEF);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "beiscsi_cmd_eq_create"
- "Failedfor EQ\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : beiscsi_cmd_eq_create Failed for EQ\n");
goto create_eq_error;
}
- SE_DEBUG(DBG_LVL_8, "eqid = %d\n", phwi_context->be_eq[i].q.id);
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : eqid = %d\n",
+ phwi_context->be_eq[i].q.id);
}
return 0;
+
create_eq_error:
- for (i = 0; i < (phba->num_cpus + 1); i++) {
+ for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
eq = &phwi_context->be_eq[i].q;
mem = &eq->dma_mem;
if (mem->va)
- pci_free_consistent(phba->pcidev, num_eq_pages
+ dma_free_coherent(&phba->pcidev->dev, num_eq_pages
* PAGE_SIZE,
mem->va, mem->dma);
}
@@ -2757,14 +3077,14 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context)
{
unsigned int i, num_cq_pages;
- int ret;
struct be_queue_info *cq, *eq;
struct be_dma_mem *mem;
struct be_eq_obj *pbe_eq;
void *cq_vaddress;
+ int ret = -ENOMEM;
dma_addr_t paddr;
- num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
+ num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries *
sizeof(struct sol_cqe));
for (i = 0; i < phba->num_cpus; i++) {
@@ -2774,16 +3094,19 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba,
pbe_eq->cq = cq;
pbe_eq->phba = phba;
mem = &cq->dma_mem;
- cq_vaddress = pci_alloc_consistent(phba->pcidev,
- num_cq_pages * PAGE_SIZE,
- &paddr);
- if (!cq_vaddress)
+ cq_vaddress = dma_alloc_coherent(&phba->pcidev->dev,
+ num_cq_pages * PAGE_SIZE,
+ &paddr, GFP_KERNEL);
+ if (!cq_vaddress) {
+ ret = -ENOMEM;
goto create_cq_error;
+ }
+
ret = be_fill_queue(cq, phba->params.num_cq_entries,
sizeof(struct sol_cqe), cq_vaddress);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for ISCSI CQ\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : be_fill_queue Failed for ISCSI CQ\n");
goto create_cq_error;
}
@@ -2791,14 +3114,13 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba,
ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false,
false, 0);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "beiscsi_cmd_eq_create"
- "Failed for ISCSI CQ\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : beiscsi_cmd_eq_create Failed for ISCSI CQ\n");
goto create_cq_error;
}
- SE_DEBUG(DBG_LVL_8, "iscsi cq_id is %d for eq_id %d\n",
- cq->id, eq->id);
- SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : iscsi cq_id is %d for eq_id %d\n"
+ "iSCSI CQ CREATED\n", cq->id, eq->id);
}
return 0;
@@ -2807,19 +3129,18 @@ create_cq_error:
cq = &phwi_context->be_cq[i];
mem = &cq->dma_mem;
if (mem->va)
- pci_free_consistent(phba->pcidev, num_cq_pages
+ dma_free_coherent(&phba->pcidev->dev, num_cq_pages
* PAGE_SIZE,
mem->va, mem->dma);
}
return ret;
-
}
static int
beiscsi_create_def_hdr(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context,
struct hwi_controller *phwi_ctrlr,
- unsigned int def_pdu_ring_sz)
+ unsigned int def_pdu_ring_sz, uint8_t ulp_num)
{
unsigned int idx;
int ret;
@@ -2829,34 +3150,41 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba,
void *dq_vaddress;
idx = 0;
- dq = &phwi_context->be_def_hdrq;
+ dq = &phwi_context->be_def_hdrq[ulp_num];
cq = &phwi_context->be_cq[0];
mem = &dq->dma_mem;
mem_descr = phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_HEADER_RING;
+ mem_descr += HWI_MEM_ASYNC_HEADER_RING_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
dq_vaddress = mem_descr->mem_array[idx].virtual_address;
ret = be_fill_queue(dq, mem_descr->mem_array[0].size /
sizeof(struct phys_addr),
sizeof(struct phys_addr), dq_vaddress);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for DEF PDU HDR\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : be_fill_queue Failed for DEF PDU HDR on ULP : %d\n",
+ ulp_num);
+
return ret;
}
mem->dma = (unsigned long)mem_descr->mem_array[idx].
bus_address.u.a64.address;
ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dq,
def_pdu_ring_sz,
- phba->params.defpdu_hdr_sz);
+ phba->params.defpdu_hdr_sz,
+ BEISCSI_DEFQ_HDR, ulp_num);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_cmd_create_default_pdu_queue Failed DEFHDR\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : be_cmd_create_default_pdu_queue Failed DEFHDR on ULP : %d\n",
+ ulp_num);
+
return ret;
}
- phwi_ctrlr->default_pdu_hdr.id = phwi_context->be_def_hdrq.id;
- SE_DEBUG(DBG_LVL_8, "iscsi def pdu id is %d\n",
- phwi_context->be_def_hdrq.id);
- hwi_post_async_buffers(phba, 1);
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : iscsi hdr def pdu id for ULP : %d is %d\n",
+ ulp_num,
+ phwi_context->be_def_hdrq[ulp_num].id);
return 0;
}
@@ -2864,7 +3192,7 @@ static int
beiscsi_create_def_data(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context,
struct hwi_controller *phwi_ctrlr,
- unsigned int def_pdu_ring_sz)
+ unsigned int def_pdu_ring_sz, uint8_t ulp_num)
{
unsigned int idx;
int ret;
@@ -2874,36 +3202,81 @@ beiscsi_create_def_data(struct beiscsi_hba *phba,
void *dq_vaddress;
idx = 0;
- dataq = &phwi_context->be_def_dataq;
+ dataq = &phwi_context->be_def_dataq[ulp_num];
cq = &phwi_context->be_cq[0];
mem = &dataq->dma_mem;
mem_descr = phba->init_mem;
- mem_descr += HWI_MEM_ASYNC_DATA_RING;
+ mem_descr += HWI_MEM_ASYNC_DATA_RING_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
dq_vaddress = mem_descr->mem_array[idx].virtual_address;
ret = be_fill_queue(dataq, mem_descr->mem_array[0].size /
sizeof(struct phys_addr),
sizeof(struct phys_addr), dq_vaddress);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for DEF PDU DATA\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : be_fill_queue Failed for DEF PDU "
+ "DATA on ULP : %d\n",
+ ulp_num);
+
return ret;
}
mem->dma = (unsigned long)mem_descr->mem_array[idx].
bus_address.u.a64.address;
ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dataq,
def_pdu_ring_sz,
- phba->params.defpdu_data_sz);
+ phba->params.defpdu_data_sz,
+ BEISCSI_DEFQ_DATA, ulp_num);
if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_cmd_create_default_pdu_queue Failed"
- " for DEF PDU DATA\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d be_cmd_create_default_pdu_queue"
+ " Failed for DEF PDU DATA on ULP : %d\n",
+ ulp_num);
return ret;
}
- phwi_ctrlr->default_pdu_data.id = phwi_context->be_def_dataq.id;
- SE_DEBUG(DBG_LVL_8, "iscsi def data id is %d\n",
- phwi_context->be_def_dataq.id);
- hwi_post_async_buffers(phba, 0);
- SE_DEBUG(DBG_LVL_8, "DEFAULT PDU DATA RING CREATED\n");
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : iscsi def data id on ULP : %d is %d\n",
+ ulp_num,
+ phwi_context->be_def_dataq[ulp_num].id);
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : DEFAULT PDU DATA RING CREATED on ULP : %d\n",
+ ulp_num);
+ return 0;
+}
+
+
+static int
+beiscsi_post_template_hdr(struct beiscsi_hba *phba)
+{
+ struct be_mem_descriptor *mem_descr;
+ struct mem_array *pm_arr;
+ struct be_dma_mem sgl;
+ int status, ulp_num;
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+ mem_descr = (struct be_mem_descriptor *)phba->init_mem;
+ mem_descr += HWI_MEM_TEMPLATE_HDR_ULP0 +
+ (ulp_num * MEM_DESCR_OFFSET);
+ pm_arr = mem_descr->mem_array;
+
+ hwi_build_be_sgl_arr(phba, pm_arr, &sgl);
+ status = be_cmd_iscsi_post_template_hdr(
+ &phba->ctrl, &sgl);
+
+ if (status != 0) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Post Template HDR Failed for "
+ "ULP_%d\n", ulp_num);
+ return status;
+ }
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : Template HDR Pages Posted for "
+ "ULP_%d\n", ulp_num);
+ }
+ }
return 0;
}
@@ -2914,14 +3287,18 @@ beiscsi_post_pages(struct beiscsi_hba *phba)
struct mem_array *pm_arr;
unsigned int page_offset, i;
struct be_dma_mem sgl;
- int status;
+ int status, ulp_num = 0;
mem_descr = phba->init_mem;
mem_descr += HWI_MEM_SGE;
pm_arr = mem_descr->mem_array;
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++)
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported))
+ break;
+
page_offset = (sizeof(struct iscsi_sge) * phba->params.num_sge_per_io *
- phba->fw_config.iscsi_icd_start) / PAGE_SIZE;
+ phba->fw_config.iscsi_icd_start[ulp_num]) / PAGE_SIZE;
for (i = 0; i < mem_descr->num_elements; i++) {
hwi_build_be_sgl_arr(phba, pm_arr, &sgl);
status = be_cmd_iscsi_post_sgl_pages(&phba->ctrl, &sgl,
@@ -2929,22 +3306,25 @@ beiscsi_post_pages(struct beiscsi_hba *phba)
(pm_arr->size / PAGE_SIZE));
page_offset += pm_arr->size / PAGE_SIZE;
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "post sgl failed.\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : post sgl failed.\n");
return status;
}
pm_arr++;
}
- SE_DEBUG(DBG_LVL_8, "POSTED PAGES\n");
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : POSTED PAGES\n");
return 0;
}
static void be_queue_free(struct beiscsi_hba *phba, struct be_queue_info *q)
{
struct be_dma_mem *mem = &q->dma_mem;
- if (mem->va)
- pci_free_consistent(phba->pcidev, mem->size,
+ if (mem->va) {
+ dma_free_coherent(&phba->pcidev->dev, mem->size,
mem->va, mem->dma);
+ mem->va = NULL;
+ }
}
static int be_queue_alloc(struct beiscsi_hba *phba, struct be_queue_info *q,
@@ -2956,10 +3336,10 @@ static int be_queue_alloc(struct beiscsi_hba *phba, struct be_queue_info *q,
q->len = len;
q->entry_size = entry_size;
mem->size = len * entry_size;
- mem->va = pci_alloc_consistent(phba->pcidev, mem->size, &mem->dma);
+ mem->va = dma_alloc_coherent(&phba->pcidev->dev, mem->size, &mem->dma,
+ GFP_KERNEL);
if (!mem->va)
return -ENOMEM;
- memset(mem->va, 0, mem->size);
return 0;
}
@@ -2968,23 +3348,27 @@ beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context,
struct hwi_controller *phwi_ctrlr)
{
- unsigned int wrb_mem_index, offset, size, num_wrb_rings;
+ unsigned int num_wrb_rings;
u64 pa_addr_lo;
- unsigned int idx, num, i;
+ unsigned int idx, num, i, ulp_num;
struct mem_array *pwrb_arr;
void *wrb_vaddr;
struct be_dma_mem sgl;
struct be_mem_descriptor *mem_descr;
+ struct hwi_wrb_context *pwrb_context;
int status;
+ uint8_t ulp_count = 0, ulp_base_num = 0;
+ uint16_t cid_count_ulp[BEISCSI_ULP_COUNT] = { 0 };
idx = 0;
mem_descr = phba->init_mem;
mem_descr += HWI_MEM_WRB;
- pwrb_arr = kmalloc(sizeof(*pwrb_arr) * phba->params.cxns_per_ctrl,
- GFP_KERNEL);
+ pwrb_arr = kmalloc_array(phba->params.cxns_per_ctrl,
+ sizeof(*pwrb_arr),
+ GFP_KERNEL);
if (!pwrb_arr) {
- shost_printk(KERN_ERR, phba->shost,
- "Memory alloc failed in create wrb ring.\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Memory alloc failed in create wrb ring.\n");
return -ENOMEM;
}
wrb_vaddr = mem_descr->mem_array[idx].virtual_address;
@@ -3004,37 +3388,55 @@ beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
} else {
idx++;
wrb_vaddr = mem_descr->mem_array[idx].virtual_address;
- pa_addr_lo = mem_descr->mem_array[idx].\
+ pa_addr_lo = mem_descr->mem_array[idx].
bus_address.u.a64.address;
num_wrb_rings = mem_descr->mem_array[idx].size /
(phba->params.wrbs_per_cxn *
sizeof(struct iscsi_wrb));
pwrb_arr[num].virtual_address = wrb_vaddr;
- pwrb_arr[num].bus_address.u.a64.address\
- = pa_addr_lo;
+ pwrb_arr[num].bus_address.u.a64.address = pa_addr_lo;
pwrb_arr[num].size = phba->params.wrbs_per_cxn *
sizeof(struct iscsi_wrb);
wrb_vaddr += pwrb_arr[num].size;
- pa_addr_lo += pwrb_arr[num].size;
+ pa_addr_lo += pwrb_arr[num].size;
num_wrb_rings--;
}
}
+
+ /* Get the ULP Count */
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++)
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+ ulp_count++;
+ ulp_base_num = ulp_num;
+ cid_count_ulp[ulp_num] =
+ BEISCSI_GET_CID_COUNT(phba, ulp_num);
+ }
+
for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
- wrb_mem_index = 0;
- offset = 0;
- size = 0;
+ if (ulp_count > 1) {
+ ulp_base_num = (ulp_base_num + 1) % BEISCSI_ULP_COUNT;
+
+ if (!cid_count_ulp[ulp_base_num])
+ ulp_base_num = (ulp_base_num + 1) %
+ BEISCSI_ULP_COUNT;
+
+ cid_count_ulp[ulp_base_num]--;
+ }
+
hwi_build_be_sgl_by_offset(phba, &pwrb_arr[i], &sgl);
status = be_cmd_wrbq_create(&phba->ctrl, &sgl,
- &phwi_context->be_wrbq[i]);
+ &phwi_context->be_wrbq[i],
+ &phwi_ctrlr->wrb_context[i],
+ ulp_base_num);
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "wrbq create failed.");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : wrbq create failed.");
kfree(pwrb_arr);
return status;
}
- phwi_ctrlr->wrb_context[i * 2].cid = phwi_context->be_wrbq[i].
- id;
+ pwrb_context = &phwi_ctrlr->wrb_context[i];
+ BE_SET_CID_TO_CRI(i, pwrb_context->cid);
}
kfree(pwrb_arr);
return 0;
@@ -3047,7 +3449,7 @@ static void free_wrb_handles(struct beiscsi_hba *phba)
struct hwi_wrb_context *pwrb_context;
phwi_ctrlr = phba->phwi_ctrlr;
- for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
+ for (index = 0; index < phba->params.cxns_per_ctrl; index++) {
pwrb_context = &phwi_ctrlr->wrb_context[index];
kfree(pwrb_context->pwrb_handle_base);
kfree(pwrb_context->pwrb_handle_basestd);
@@ -3056,62 +3458,63 @@ static void free_wrb_handles(struct beiscsi_hba *phba)
static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
{
- struct be_queue_info *q;
struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_dma_mem *ptag_mem;
+ struct be_queue_info *q;
+ int i, tag;
q = &phba->ctrl.mcc_obj.q;
- if (q->created)
+ for (i = 0; i < MAX_MCC_CMD; i++) {
+ tag = i + 1;
+ if (!test_bit(MCC_TAG_STATE_RUNNING,
+ &ctrl->ptag_state[tag].tag_state))
+ continue;
+
+ if (test_bit(MCC_TAG_STATE_TIMEOUT,
+ &ctrl->ptag_state[tag].tag_state)) {
+ ptag_mem = &ctrl->ptag_state[tag].tag_mem_state;
+ if (ptag_mem->size) {
+ dma_free_coherent(&ctrl->pdev->dev,
+ ptag_mem->size,
+ ptag_mem->va,
+ ptag_mem->dma);
+ ptag_mem->size = 0;
+ }
+ continue;
+ }
+ /**
+ * If MCC is still active and waiting then wake up the process.
+ * We are here only because port is going offline. The process
+ * sees that (BEISCSI_HBA_ONLINE is cleared) and EIO error is
+ * returned for the operation and allocated memory cleaned up.
+ */
+ if (waitqueue_active(&ctrl->mcc_wait[tag])) {
+ ctrl->mcc_tag_status[tag] = MCC_STATUS_FAILED;
+ ctrl->mcc_tag_status[tag] |= CQE_VALID_MASK;
+ wake_up_interruptible(&ctrl->mcc_wait[tag]);
+ /*
+ * Control tag info gets reinitialized in enable
+ * so wait for the process to clear running state.
+ */
+ while (test_bit(MCC_TAG_STATE_RUNNING,
+ &ctrl->ptag_state[tag].tag_state))
+ schedule_timeout_uninterruptible(HZ);
+ }
+ /**
+ * For MCC with tag_states MCC_TAG_STATE_ASYNC and
+ * MCC_TAG_STATE_IGNORE nothing needs to done.
+ */
+ }
+ if (q->created) {
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
- be_queue_free(phba, q);
+ be_queue_free(phba, q);
+ }
q = &phba->ctrl.mcc_obj.cq;
- if (q->created)
+ if (q->created) {
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
- be_queue_free(phba, q);
-}
-
-static void hwi_cleanup(struct beiscsi_hba *phba)
-{
- struct be_queue_info *q;
- struct be_ctrl_info *ctrl = &phba->ctrl;
- struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
- int i, eq_num;
-
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
- for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
- q = &phwi_context->be_wrbq[i];
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
- }
- free_wrb_handles(phba);
-
- q = &phwi_context->be_def_hdrq;
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
-
- q = &phwi_context->be_def_dataq;
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
-
- beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
-
- for (i = 0; i < (phba->num_cpus); i++) {
- q = &phwi_context->be_cq[i];
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
- }
- if (phba->msix_enabled)
- eq_num = 1;
- else
- eq_num = 0;
- for (i = 0; i < (phba->num_cpus + eq_num); i++) {
- q = &phwi_context->be_eq[i].q;
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+ be_queue_free(phba, q);
}
- be_mcc_queues_destroy(phba);
}
static int be_mcc_queues_create(struct beiscsi_hba *phba,
@@ -3126,14 +3529,15 @@ static int be_mcc_queues_create(struct beiscsi_hba *phba,
sizeof(struct be_mcc_compl)))
goto err;
/* Ask BE to create MCC compl queue; */
- if (phba->msix_enabled) {
- if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq
- [phba->num_cpus].q, false, true, 0))
- goto mcc_cq_free;
+ if (phba->pcidev->msix_enabled) {
+ if (beiscsi_cmd_cq_create(ctrl, cq,
+ &phwi_context->be_eq[phba->num_cpus].q,
+ false, true, 0))
+ goto mcc_cq_free;
} else {
if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq[0].q,
false, true, 0))
- goto mcc_cq_free;
+ goto mcc_cq_free;
}
/* Alloc MCC queue */
@@ -3157,16 +3561,144 @@ err:
return -ENOMEM;
}
-static int find_num_cpus(void)
+static void be2iscsi_enable_msix(struct beiscsi_hba *phba)
{
- int num_cpus = 0;
+ int nvec = 1;
- num_cpus = num_online_cpus();
- if (num_cpus >= MAX_CPUS)
- num_cpus = MAX_CPUS - 1;
+ switch (phba->generation) {
+ case BE_GEN2:
+ case BE_GEN3:
+ nvec = BEISCSI_MAX_NUM_CPUS + 1;
+ break;
+ case BE_GEN4:
+ nvec = phba->fw_config.eqid_count;
+ break;
+ default:
+ nvec = 2;
+ break;
+ }
+
+ /* if eqid_count == 1 fall back to INTX */
+ if (enable_msix && nvec > 1) {
+ struct irq_affinity desc = { .post_vectors = 1 };
+
+ if (pci_alloc_irq_vectors_affinity(phba->pcidev, 2, nvec,
+ PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, &desc) < 0) {
+ phba->num_cpus = nvec - 1;
+ return;
+ }
+ }
- SE_DEBUG(DBG_LVL_8, "num_cpus = %d\n", num_cpus);
- return num_cpus;
+ phba->num_cpus = 1;
+}
+
+static void hwi_purge_eq(struct beiscsi_hba *phba)
+{
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ struct be_queue_info *eq;
+ struct be_eq_entry *eqe = NULL;
+ int i, eq_msix;
+ unsigned int num_processed;
+
+ if (beiscsi_hba_in_error(phba))
+ return;
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+ if (phba->pcidev->msix_enabled)
+ eq_msix = 1;
+ else
+ eq_msix = 0;
+
+ for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
+ eq = &phwi_context->be_eq[i].q;
+ eqe = queue_tail_node(eq);
+ num_processed = 0;
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ num_processed++;
+ }
+
+ if (num_processed)
+ hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1);
+ }
+}
+
+static void hwi_cleanup_port(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *q;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ int i, eq_for_mcc, ulp_num;
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++)
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported))
+ beiscsi_cmd_iscsi_cleanup(phba, ulp_num);
+
+ /**
+ * Purge all EQ entries that may have been left out. This is to
+ * workaround a problem we've seen occasionally where driver gets an
+ * interrupt with EQ entry bit set after stopping the controller.
+ */
+ hwi_purge_eq(phba);
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+
+ be_cmd_iscsi_remove_template_hdr(ctrl);
+
+ for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
+ q = &phwi_context->be_wrbq[i];
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
+ }
+ kfree(phwi_context->be_wrbq);
+ free_wrb_handles(phba);
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+
+ q = &phwi_context->be_def_hdrq[ulp_num];
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
+
+ q = &phwi_context->be_def_dataq[ulp_num];
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
+ }
+ }
+
+ beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
+
+ for (i = 0; i < (phba->num_cpus); i++) {
+ q = &phwi_context->be_cq[i];
+ if (q->created) {
+ be_queue_free(phba, q);
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ }
+ }
+
+ be_mcc_queues_destroy(phba);
+ if (phba->pcidev->msix_enabled)
+ eq_for_mcc = 1;
+ else
+ eq_for_mcc = 0;
+ for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
+ q = &phwi_context->be_eq[i].q;
+ if (q->created) {
+ be_queue_free(phba, q);
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+ }
+ }
+ /* this ensures complete FW cleanup */
+ beiscsi_cmd_function_reset(phba);
+ /* last communication, indicate driver is unloading */
+ beiscsi_cmd_special_wrb(&phba->ctrl, 0);
}
static int hwi_init_port(struct beiscsi_hba *phba)
@@ -3175,20 +3707,18 @@ static int hwi_init_port(struct beiscsi_hba *phba)
struct hwi_context_memory *phwi_context;
unsigned int def_pdu_ring_sz;
struct be_ctrl_info *ctrl = &phba->ctrl;
- int status;
+ int status, ulp_num;
+ u16 nbufs;
- def_pdu_ring_sz =
- phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr);
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- phwi_context->max_eqd = 0;
- phwi_context->min_eqd = 0;
- phwi_context->cur_eqd = 64;
- be_cmd_fw_initialize(&phba->ctrl);
+ /* set port optic state to unknown */
+ phba->optic_state = 0xff;
status = beiscsi_create_eqs(phba, phwi_context);
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost, "EQ not created\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : EQ not created\n");
goto error;
}
@@ -3196,55 +3726,106 @@ static int hwi_init_port(struct beiscsi_hba *phba)
if (status != 0)
goto error;
- status = mgmt_check_supported_fw(ctrl, phba);
+ status = beiscsi_check_supported_fw(ctrl, phba);
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "Unsupported fw version\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Unsupported fw version\n");
goto error;
}
status = beiscsi_create_cqs(phba, phwi_context);
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost, "CQ not created\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : CQ not created\n");
goto error;
}
- status = beiscsi_create_def_hdr(phba, phwi_context, phwi_ctrlr,
- def_pdu_ring_sz);
- if (status != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "Default Header not created\n");
- goto error;
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+ nbufs = phwi_context->pasync_ctx[ulp_num]->num_entries;
+ def_pdu_ring_sz = nbufs * sizeof(struct phys_addr);
+
+ status = beiscsi_create_def_hdr(phba, phwi_context,
+ phwi_ctrlr,
+ def_pdu_ring_sz,
+ ulp_num);
+ if (status != 0) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Default Header not created for ULP : %d\n",
+ ulp_num);
+ goto error;
+ }
+
+ status = beiscsi_create_def_data(phba, phwi_context,
+ phwi_ctrlr,
+ def_pdu_ring_sz,
+ ulp_num);
+ if (status != 0) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Default Data not created for ULP : %d\n",
+ ulp_num);
+ goto error;
+ }
+ /**
+ * Now that the default PDU rings have been created,
+ * let EP know about it.
+ */
+ beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR,
+ ulp_num, nbufs);
+ beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA,
+ ulp_num, nbufs);
+ }
}
- status = beiscsi_create_def_data(phba, phwi_context,
- phwi_ctrlr, def_pdu_ring_sz);
+ status = beiscsi_post_pages(phba);
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "Default Data not created\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Post SGL Pages Failed\n");
goto error;
}
- status = beiscsi_post_pages(phba);
+ status = beiscsi_post_template_hdr(phba);
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost, "Post SGL Pages Failed\n");
- goto error;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Template HDR Posting for CXN Failed\n");
}
status = beiscsi_create_wrb_rings(phba, phwi_context, phwi_ctrlr);
if (status != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "WRB Rings not created\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : WRB Rings not created\n");
goto error;
}
- SE_DEBUG(DBG_LVL_8, "hwi_init_port success\n");
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ uint16_t async_arr_idx = 0;
+
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+ uint16_t cri = 0;
+ struct hd_async_context *pasync_ctx;
+
+ pasync_ctx = HWI_GET_ASYNC_PDU_CTX(
+ phwi_ctrlr, ulp_num);
+ for (cri = 0; cri <
+ phba->params.cxns_per_ctrl; cri++) {
+ if (ulp_num == BEISCSI_GET_ULP_FROM_CRI
+ (phwi_ctrlr, cri))
+ pasync_ctx->cid_to_async_cri_map[
+ phwi_ctrlr->wrb_context[cri].cid] =
+ async_arr_idx++;
+ }
+ }
+ }
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_port success\n");
return 0;
error:
- shost_printk(KERN_ERR, phba->shost, "hwi_init_port failed");
- hwi_cleanup(phba);
- return -ENOMEM;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_port failed");
+ hwi_cleanup_port(phba);
+ return status;
}
static int hwi_init_controller(struct beiscsi_hba *phba)
@@ -3255,21 +3836,30 @@ static int hwi_init_controller(struct beiscsi_hba *phba)
if (1 == phba->init_mem[HWI_MEM_ADDN_CONTEXT].num_elements) {
phwi_ctrlr->phwi_ctxt = (struct hwi_context_memory *)phba->
init_mem[HWI_MEM_ADDN_CONTEXT].mem_array[0].virtual_address;
- SE_DEBUG(DBG_LVL_8, " phwi_ctrlr->phwi_ctxt=%p\n",
- phwi_ctrlr->phwi_ctxt);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : phwi_ctrlr->phwi_ctxt=%p\n",
+ phwi_ctrlr->phwi_ctxt);
} else {
- shost_printk(KERN_ERR, phba->shost,
- "HWI_MEM_ADDN_CONTEXT is more than one element."
- "Failing to load\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : HWI_MEM_ADDN_CONTEXT is more "
+ "than one element.Failing to load\n");
return -ENOMEM;
}
iscsi_init_global_templates(phba);
- beiscsi_init_wrb_handle(phba);
- hwi_init_async_pdu_ctx(phba);
+ if (beiscsi_init_wrb_handle(phba))
+ return -ENOMEM;
+
+ if (hwi_init_async_pdu_ctx(phba)) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_async_pdu_ctx failed\n");
+ return -ENOMEM;
+ }
+
if (hwi_init_port(phba) != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "hwi_init_controller failed\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : hwi_init_controller failed\n");
+
return -ENOMEM;
}
return 0;
@@ -3281,51 +3871,30 @@ static void beiscsi_free_mem(struct beiscsi_hba *phba)
int i, j;
mem_descr = phba->init_mem;
- i = 0;
- j = 0;
for (i = 0; i < SE_MEM_MAX; i++) {
for (j = mem_descr->num_elements; j > 0; j--) {
- pci_free_consistent(phba->pcidev,
+ dma_free_coherent(&phba->pcidev->dev,
mem_descr->mem_array[j - 1].size,
mem_descr->mem_array[j - 1].virtual_address,
(unsigned long)mem_descr->mem_array[j - 1].
bus_address.u.a64.address);
}
+
kfree(mem_descr->mem_array);
mem_descr++;
}
kfree(phba->init_mem);
+ kfree(phba->phwi_ctrlr->wrb_context);
kfree(phba->phwi_ctrlr);
}
-static int beiscsi_init_controller(struct beiscsi_hba *phba)
-{
- int ret = -ENOMEM;
-
- ret = beiscsi_get_memory(phba);
- if (ret < 0) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe -"
- "Failed in beiscsi_alloc_memory\n");
- return ret;
- }
-
- ret = hwi_init_controller(phba);
- if (ret)
- goto free_init;
- SE_DEBUG(DBG_LVL_8, "Return success from beiscsi_init_controller");
- return 0;
-
-free_init:
- beiscsi_free_mem(phba);
- return -ENOMEM;
-}
-
static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
{
struct be_mem_descriptor *mem_descr_sglh, *mem_descr_sg;
struct sgl_handle *psgl_handle;
struct iscsi_sge *pfrag;
unsigned int arr_index, i, idx;
+ unsigned int ulp_icd_start, ulp_num = 0;
phba->io_sgl_hndl_avbl = 0;
phba->eh_sgl_hndl_avbl = 0;
@@ -3333,28 +3902,28 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
mem_descr_sglh = phba->init_mem;
mem_descr_sglh += HWI_MEM_SGLH;
if (1 == mem_descr_sglh->num_elements) {
- phba->io_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) *
- phba->params.ios_per_ctrl,
+ phba->io_sgl_hndl_base = kcalloc(phba->params.ios_per_ctrl,
+ sizeof(struct sgl_handle *),
GFP_KERNEL);
if (!phba->io_sgl_hndl_base) {
- shost_printk(KERN_ERR, phba->shost,
- "Mem Alloc Failed. Failing to load\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Mem Alloc Failed. Failing to load\n");
return -ENOMEM;
}
- phba->eh_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) *
- (phba->params.icds_per_ctrl -
- phba->params.ios_per_ctrl),
- GFP_KERNEL);
+ phba->eh_sgl_hndl_base =
+ kcalloc(phba->params.icds_per_ctrl -
+ phba->params.ios_per_ctrl,
+ sizeof(struct sgl_handle *), GFP_KERNEL);
if (!phba->eh_sgl_hndl_base) {
kfree(phba->io_sgl_hndl_base);
- shost_printk(KERN_ERR, phba->shost,
- "Mem Alloc Failed. Failing to load\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Mem Alloc Failed. Failing to load\n");
return -ENOMEM;
}
} else {
- shost_printk(KERN_ERR, phba->shost,
- "HWI_MEM_SGLH is more than one element."
- "Failing to load\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : HWI_MEM_SGLH is more than one element."
+ "Failing to load\n");
return -ENOMEM;
}
@@ -3380,15 +3949,24 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
}
idx++;
}
- SE_DEBUG(DBG_LVL_8,
- "phba->io_sgl_hndl_avbl=%d"
- "phba->eh_sgl_hndl_avbl=%d\n",
- phba->io_sgl_hndl_avbl,
- phba->eh_sgl_hndl_avbl);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : phba->io_sgl_hndl_avbl=%d "
+ "phba->eh_sgl_hndl_avbl=%d\n",
+ phba->io_sgl_hndl_avbl,
+ phba->eh_sgl_hndl_avbl);
+
mem_descr_sg = phba->init_mem;
mem_descr_sg += HWI_MEM_SGE;
- SE_DEBUG(DBG_LVL_8, "\n mem_descr_sg->num_elements=%d\n",
- mem_descr_sg->num_elements);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "\n BM_%d : mem_descr_sg->num_elements=%d\n",
+ mem_descr_sg->num_elements);
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++)
+ if (test_bit(ulp_num, &phba->fw_config.ulp_supported))
+ break;
+
+ ulp_icd_start = phba->fw_config.iscsi_icd_start[ulp_num];
+
arr_index = 0;
idx = 0;
while (idx < mem_descr_sg->num_elements) {
@@ -3407,8 +3985,7 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, pfrag, 0);
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, pfrag, 0);
pfrag += phba->params.num_sge_per_io;
- psgl_handle->sgl_index =
- phba->fw_config.iscsi_icd_start + arr_index++;
+ psgl_handle->sgl_index = ulp_icd_start + arr_index++;
}
idx++;
}
@@ -3421,32 +3998,92 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
static int hba_setup_cid_tbls(struct beiscsi_hba *phba)
{
- int i, new_cid;
-
- phba->cid_array = kzalloc(sizeof(void *) * phba->params.cxns_per_ctrl,
- GFP_KERNEL);
- if (!phba->cid_array) {
- shost_printk(KERN_ERR, phba->shost,
- "Failed to allocate memory in "
- "hba_setup_cid_tbls\n");
- return -ENOMEM;
+ int ret;
+ uint16_t i, ulp_num;
+ struct ulp_cid_info *ptr_cid_info = NULL;
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) {
+ ptr_cid_info = kzalloc(sizeof(struct ulp_cid_info),
+ GFP_KERNEL);
+
+ if (!ptr_cid_info) {
+ ret = -ENOMEM;
+ goto free_memory;
+ }
+
+ /* Allocate memory for CID array */
+ ptr_cid_info->cid_array =
+ kcalloc(BEISCSI_GET_CID_COUNT(phba, ulp_num),
+ sizeof(*ptr_cid_info->cid_array),
+ GFP_KERNEL);
+ if (!ptr_cid_info->cid_array) {
+ kfree(ptr_cid_info);
+ ptr_cid_info = NULL;
+ ret = -ENOMEM;
+
+ goto free_memory;
+ }
+ ptr_cid_info->avlbl_cids = BEISCSI_GET_CID_COUNT(
+ phba, ulp_num);
+
+ /* Save the cid_info_array ptr */
+ phba->cid_array_info[ulp_num] = ptr_cid_info;
+ }
}
- phba->ep_array = kzalloc(sizeof(struct iscsi_endpoint *) *
- phba->params.cxns_per_ctrl * 2, GFP_KERNEL);
+ phba->ep_array = kcalloc(phba->params.cxns_per_ctrl,
+ sizeof(struct iscsi_endpoint *),
+ GFP_KERNEL);
if (!phba->ep_array) {
- shost_printk(KERN_ERR, phba->shost,
- "Failed to allocate memory in "
- "hba_setup_cid_tbls\n");
- kfree(phba->cid_array);
- return -ENOMEM;
+ ret = -ENOMEM;
+
+ goto free_memory;
}
- new_cid = phba->fw_config.iscsi_cid_start;
+
+ phba->conn_table = kcalloc(phba->params.cxns_per_ctrl,
+ sizeof(struct beiscsi_conn *),
+ GFP_KERNEL);
+ if (!phba->conn_table) {
+ kfree(phba->ep_array);
+ phba->ep_array = NULL;
+ ret = -ENOMEM;
+
+ goto free_memory;
+ }
+
for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
- phba->cid_array[i] = new_cid;
- new_cid += 2;
+ ulp_num = phba->phwi_ctrlr->wrb_context[i].ulp_num;
+
+ ptr_cid_info = phba->cid_array_info[ulp_num];
+ ptr_cid_info->cid_array[ptr_cid_info->cid_alloc++] =
+ phba->phwi_ctrlr->wrb_context[i].cid;
+
+ }
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) {
+ ptr_cid_info = phba->cid_array_info[ulp_num];
+
+ ptr_cid_info->cid_alloc = 0;
+ ptr_cid_info->cid_free = 0;
+ }
}
- phba->avlbl_cids = phba->params.cxns_per_ctrl;
return 0;
+
+free_memory:
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) {
+ ptr_cid_info = phba->cid_array_info[ulp_num];
+
+ if (ptr_cid_info) {
+ kfree(ptr_cid_info->cid_array);
+ kfree(ptr_cid_info);
+ phba->cid_array_info[ulp_num] = NULL;
+ }
+ }
+ }
+
+ return ret;
}
static void hwi_enable_intr(struct beiscsi_hba *phba)
@@ -3469,18 +4106,22 @@ static void hwi_enable_intr(struct beiscsi_hba *phba)
enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
if (!enabled) {
reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
- SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p\n", reg, addr);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : reg =x%08x addr=%p\n", reg, addr);
iowrite32(reg, addr);
}
- if (!phba->msix_enabled) {
+ if (!phba->pcidev->msix_enabled) {
eq = &phwi_context->be_eq[0].q;
- SE_DEBUG(DBG_LVL_8, "eq->id=%d\n", eq->id);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : eq->id=%d\n", eq->id);
+
hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
} else {
for (i = 0; i <= phba->num_cpus; i++) {
eq = &phwi_context->be_eq[i].q;
- SE_DEBUG(DBG_LVL_8, "eq->id=%d\n", eq->id);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : eq->id=%d\n", eq->id);
hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
}
}
@@ -3498,175 +4139,155 @@ static void hwi_disable_intr(struct beiscsi_hba *phba)
reg &= ~MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
iowrite32(reg, addr);
} else
- shost_printk(KERN_WARNING, phba->shost,
- "In hwi_disable_intr, Already Disabled\n");
-}
-
-static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
-{
- struct be_cmd_resp_get_boot_target *boot_resp;
- struct be_cmd_resp_get_session *session_resp;
- struct be_mcc_wrb *wrb;
- struct be_dma_mem nonemb_cmd;
- unsigned int tag, wrb_num;
- unsigned short status, extd_status;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
-
- tag = beiscsi_get_boot_target(phba);
- if (!tag) {
- SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed\n");
- return -EAGAIN;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
- SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed"
- " status = %d extd_status = %d\n",
- status, extd_status);
- free_mcc_tag(&phba->ctrl, tag);
- return -EBUSY;
- }
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
- boot_resp = embedded_payload(wrb);
-
- if (boot_resp->boot_session_handle < 0) {
- printk(KERN_ERR "No Boot Session for this pci_func,"
- "session Hndl = %d\n", boot_resp->boot_session_handle);
- return -ENXIO;
- }
-
- nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
- sizeof(*session_resp),
- &nonemb_cmd.dma);
- if (nonemb_cmd.va == NULL) {
- SE_DEBUG(DBG_LVL_1,
- "Failed to allocate memory for"
- "beiscsi_get_session_info\n");
- return -ENOMEM;
- }
-
- memset(nonemb_cmd.va, 0, sizeof(*session_resp));
- tag = beiscsi_get_session_info(phba,
- boot_resp->boot_session_handle, &nonemb_cmd);
- if (!tag) {
- SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info"
- " Failed\n");
- goto boot_freemem;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
- SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info Failed"
- " status = %d extd_status = %d\n",
- status, extd_status);
- free_mcc_tag(&phba->ctrl, tag);
- goto boot_freemem;
- }
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
- session_resp = nonemb_cmd.va ;
- memcpy(&phba->boot_sess, &session_resp->session_info,
- sizeof(struct mgmt_session_info));
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
- return 0;
-boot_freemem:
- pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
- return -ENOMEM;
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT,
+ "BM_%d : In hwi_disable_intr, Already Disabled\n");
}
static int beiscsi_init_port(struct beiscsi_hba *phba)
{
int ret;
- ret = beiscsi_init_controller(phba);
+ ret = hwi_init_controller(phba);
if (ret < 0) {
- shost_printk(KERN_ERR, phba->shost,
- "beiscsi_dev_probe - Failed in"
- "beiscsi_init_controller\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : init controller failed\n");
return ret;
}
ret = beiscsi_init_sgl_handle(phba);
if (ret < 0) {
- shost_printk(KERN_ERR, phba->shost,
- "beiscsi_dev_probe - Failed in"
- "beiscsi_init_sgl_handle\n");
- goto do_cleanup_ctrlr;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : init sgl handles failed\n");
+ goto cleanup_port;
}
- if (hba_setup_cid_tbls(phba)) {
- shost_printk(KERN_ERR, phba->shost,
- "Failed in hba_setup_cid_tbls\n");
+ ret = hba_setup_cid_tbls(phba);
+ if (ret < 0) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : setup CID table failed\n");
kfree(phba->io_sgl_hndl_base);
kfree(phba->eh_sgl_hndl_base);
- goto do_cleanup_ctrlr;
+ goto cleanup_port;
}
-
return ret;
-do_cleanup_ctrlr:
- hwi_cleanup(phba);
+cleanup_port:
+ hwi_cleanup_port(phba);
return ret;
}
-static void hwi_purge_eq(struct beiscsi_hba *phba)
+static void beiscsi_cleanup_port(struct beiscsi_hba *phba)
{
+ struct ulp_cid_info *ptr_cid_info = NULL;
+ int ulp_num;
+
+ kfree(phba->io_sgl_hndl_base);
+ kfree(phba->eh_sgl_hndl_base);
+ kfree(phba->ep_array);
+ kfree(phba->conn_table);
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) {
+ ptr_cid_info = phba->cid_array_info[ulp_num];
+
+ if (ptr_cid_info) {
+ kfree(ptr_cid_info->cid_array);
+ kfree(ptr_cid_info);
+ phba->cid_array_info[ulp_num] = NULL;
+ }
+ }
+ }
+}
+
+/**
+ * beiscsi_free_mgmt_task_handles()- Free driver CXN resources
+ * @beiscsi_conn: ptr to the conn to be cleaned up
+ * @task: ptr to iscsi_task resource to be freed.
+ *
+ * Free driver mgmt resources binded to CXN.
+ **/
+void
+beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn,
+ struct iscsi_task *task)
+{
+ struct beiscsi_io_task *io_task;
+ struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct hwi_wrb_context *pwrb_context;
struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
- struct be_queue_info *eq;
- struct be_eq_entry *eqe = NULL;
- int i, eq_msix;
- unsigned int num_processed;
+ uint16_t cri_index = BE_GET_CRI_FROM_CID(
+ beiscsi_conn->beiscsi_conn_cid);
phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
- if (phba->msix_enabled)
- eq_msix = 1;
- else
- eq_msix = 0;
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
- for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
- eq = &phwi_context->be_eq[i].q;
- eqe = queue_tail_node(eq);
- num_processed = 0;
- while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
- & EQE_VALID_MASK) {
- AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
- queue_tail_inc(eq);
- eqe = queue_tail_node(eq);
- num_processed++;
- }
+ io_task = task->dd_data;
- if (num_processed)
- hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1);
+ if (io_task->pwrb_handle) {
+ free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
+ io_task->pwrb_handle = NULL;
+ }
+
+ if (io_task->psgl_handle) {
+ free_mgmt_sgl_handle(phba, io_task->psgl_handle);
+ io_task->psgl_handle = NULL;
+ }
+
+ if (io_task->mtask_addr) {
+ dma_unmap_single(&phba->pcidev->dev,
+ io_task->mtask_addr,
+ io_task->mtask_data_count,
+ DMA_TO_DEVICE);
+ io_task->mtask_addr = 0;
}
}
-static void beiscsi_clean_port(struct beiscsi_hba *phba)
+/**
+ * beiscsi_cleanup_task()- Free driver resources of the task
+ * @task: ptr to the iscsi task
+ *
+ **/
+static void beiscsi_cleanup_task(struct iscsi_task *task)
{
- int mgmt_status;
+ struct beiscsi_io_task *io_task = task->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct beiscsi_conn *beiscsi_conn = conn->dd_data;
+ struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess;
+ struct hwi_wrb_context *pwrb_context;
+ struct hwi_controller *phwi_ctrlr;
+ uint16_t cri_index = BE_GET_CRI_FROM_CID(
+ beiscsi_conn->beiscsi_conn_cid);
- mgmt_status = mgmt_epfw_cleanup(phba, CMD_CONNECTION_CHUTE_0);
- if (mgmt_status)
- shost_printk(KERN_WARNING, phba->shost,
- "mgmt_epfw_cleanup FAILED\n");
+ phwi_ctrlr = phba->phwi_ctrlr;
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
- hwi_purge_eq(phba);
- hwi_cleanup(phba);
- kfree(phba->io_sgl_hndl_base);
- kfree(phba->eh_sgl_hndl_base);
- kfree(phba->cid_array);
- kfree(phba->ep_array);
+ if (io_task->cmd_bhs) {
+ dma_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
+ io_task->bhs_pa.u.a64.address);
+ io_task->cmd_bhs = NULL;
+ task->hdr = NULL;
+ }
+
+ if (task->sc) {
+ if (io_task->pwrb_handle) {
+ free_wrb_handle(phba, pwrb_context,
+ io_task->pwrb_handle);
+ io_task->pwrb_handle = NULL;
+ }
+
+ if (io_task->psgl_handle) {
+ free_io_sgl_handle(phba, io_task->psgl_handle);
+ io_task->psgl_handle = NULL;
+ }
+
+ if (io_task->scsi_cmnd) {
+ if (io_task->num_sg)
+ scsi_dma_unmap(io_task->scsi_cmnd);
+ io_task->scsi_cmnd = NULL;
+ }
+ } else {
+ if (!beiscsi_conn->login_in_progress)
+ beiscsi_free_mgmt_task_handles(beiscsi_conn, task);
+ }
}
void
@@ -3674,85 +4295,56 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_offload_params *params)
{
struct wrb_handle *pwrb_handle;
- struct iscsi_target_context_update_wrb *pwrb = NULL;
- struct be_mem_descriptor *mem_descr;
+ struct hwi_wrb_context *pwrb_context = NULL;
struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct iscsi_task *task = beiscsi_conn->task;
+ struct iscsi_session *session = task->conn->session;
u32 doorbell = 0;
/*
* We can always use 0 here because it is reserved by libiscsi for
* login/startup related tasks.
*/
- pwrb_handle = alloc_wrb_handle(phba, (beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start));
- pwrb = (struct iscsi_target_context_update_wrb *)pwrb_handle->pwrb;
- memset(pwrb, 0, sizeof(*pwrb));
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- max_burst_length, pwrb, params->dw[offsetof
- (struct amap_beiscsi_offload_params,
- max_burst_length) / 32]);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- max_send_data_segment_length, pwrb,
- params->dw[offsetof(struct amap_beiscsi_offload_params,
- max_send_data_segment_length) / 32]);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- first_burst_length,
- pwrb,
- params->dw[offsetof(struct amap_beiscsi_offload_params,
- first_burst_length) / 32]);
-
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, erl, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- erl) / 32] & OFFLD_PARAMS_ERL));
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, dde, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- dde) / 32] & OFFLD_PARAMS_DDE) >> 2);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, hde, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- hde) / 32] & OFFLD_PARAMS_HDE) >> 3);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ir2t, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, imd, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- imd) / 32] & OFFLD_PARAMS_IMD) >> 5);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, stat_sn,
- pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- exp_statsn) / 32] + 1));
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, type, pwrb,
- 0x7);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, wrb_idx,
- pwrb, pwrb_handle->wrb_index);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb,
- pwrb, pwrb_handle->nxt_wrb_index);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- session_state, pwrb, 0);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack,
- pwrb, 1);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, notpredblq,
- pwrb, 0);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, mode, pwrb,
- 0);
-
- mem_descr = phba->init_mem;
- mem_descr += ISCSI_MEM_GLOBAL_HEADER;
-
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- pad_buffer_addr_hi, pwrb,
- mem_descr->mem_array[0].bus_address.u.a32.address_hi);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- pad_buffer_addr_lo, pwrb,
- mem_descr->mem_array[0].bus_address.u.a32.address_lo);
+ beiscsi_conn->login_in_progress = 0;
+ spin_lock_bh(&session->back_lock);
+ beiscsi_cleanup_task(task);
+ spin_unlock_bh(&session->back_lock);
+
+ pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid,
+ &pwrb_context);
+
+ /* Check for the adapter family */
+ if (is_chip_be2_be3r(phba))
+ beiscsi_offload_cxn_v0(params, pwrb_handle,
+ phba->init_mem,
+ pwrb_context);
+ else
+ beiscsi_offload_cxn_v2(params, pwrb_handle,
+ pwrb_context);
- be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb));
+ be_dws_le_to_cpu(pwrb_handle->pwrb,
+ sizeof(struct iscsi_target_context_update_wrb));
doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK)
<< DB_DEF_PDU_WRB_INDEX_SHIFT;
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
+ iowrite32(doorbell, phba->db_va +
+ beiscsi_conn->doorbell_offset);
- iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
+ /*
+ * There is no completion for CONTEXT_UPDATE. The completion of next
+ * WRB posted guarantees FW's processing and DMA'ing of it.
+ * Use beiscsi_put_wrb_handle to put it back in the pool which makes
+ * sure zero'ing or reuse of the WRB only after wrbs_per_cxn.
+ */
+ beiscsi_put_wrb_handle(pwrb_context, pwrb_handle,
+ phba->params.wrbs_per_cxn);
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : put CONTEXT_UPDATE pwrb_handle=%p free_index=0x%x wrb_handles_available=%d\n",
+ pwrb_handle, pwrb_context->free_index,
+ pwrb_context->wrb_handles_available);
}
static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt,
@@ -3782,10 +4374,11 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
struct hwi_wrb_context *pwrb_context;
struct hwi_controller *phwi_ctrlr;
itt_t itt;
+ uint16_t cri_index = 0;
struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess;
dma_addr_t paddr;
- io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool,
+ io_task->cmd_bhs = dma_pool_alloc(beiscsi_sess->bhs_pool,
GFP_ATOMIC, &paddr);
if (!io_task->cmd_bhs)
return -ENOMEM;
@@ -3796,39 +4389,62 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr;
task->hdr_max = sizeof(struct be_cmd_bhs);
io_task->psgl_handle = NULL;
- io_task->psgl_handle = NULL;
+ io_task->pwrb_handle = NULL;
if (task->sc) {
- spin_lock(&phba->io_sgl_lock);
io_task->psgl_handle = alloc_io_sgl_handle(phba);
- spin_unlock(&phba->io_sgl_lock);
- if (!io_task->psgl_handle)
+ if (!io_task->psgl_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of IO_SGL_ICD Failed "
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_hndls;
+ }
io_task->pwrb_handle = alloc_wrb_handle(phba,
- beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start);
- if (!io_task->pwrb_handle)
+ beiscsi_conn->beiscsi_conn_cid,
+ &io_task->pwrb_context);
+ if (!io_task->pwrb_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of WRB_HANDLE Failed "
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_io_hndls;
+ }
} else {
io_task->scsi_cmnd = NULL;
if ((opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
+ beiscsi_conn->task = task;
if (!beiscsi_conn->login_in_progress) {
- spin_lock(&phba->mgmt_sgl_lock);
io_task->psgl_handle = (struct sgl_handle *)
alloc_mgmt_sgl_handle(phba);
- spin_unlock(&phba->mgmt_sgl_lock);
- if (!io_task->psgl_handle)
+ if (!io_task->psgl_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO |
+ BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of MGMT_SGL_ICD Failed "
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_hndls;
+ }
beiscsi_conn->login_in_progress = 1;
beiscsi_conn->plogin_sgl_handle =
io_task->psgl_handle;
io_task->pwrb_handle =
alloc_wrb_handle(phba,
- beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start);
- if (!io_task->pwrb_handle)
- goto free_io_hndls;
+ beiscsi_conn->beiscsi_conn_cid,
+ &io_task->pwrb_context);
+ if (!io_task->pwrb_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO |
+ BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of WRB_HANDLE Failed "
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
+ goto free_mgmt_hndls;
+ }
beiscsi_conn->plogin_wrb_handle =
io_task->pwrb_handle;
@@ -3839,17 +4455,28 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
beiscsi_conn->plogin_wrb_handle;
}
} else {
- spin_lock(&phba->mgmt_sgl_lock);
io_task->psgl_handle = alloc_mgmt_sgl_handle(phba);
- spin_unlock(&phba->mgmt_sgl_lock);
- if (!io_task->psgl_handle)
+ if (!io_task->psgl_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO |
+ BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of MGMT_SGL_ICD Failed "
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_hndls;
+ }
io_task->pwrb_handle =
alloc_wrb_handle(phba,
- beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start);
- if (!io_task->pwrb_handle)
+ beiscsi_conn->beiscsi_conn_cid,
+ &io_task->pwrb_context);
+ if (!io_task->pwrb_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of WRB_HANDLE Failed "
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_mgmt_hndls;
+ }
}
}
@@ -3862,69 +4489,83 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
return 0;
free_io_hndls:
- spin_lock(&phba->io_sgl_lock);
free_io_sgl_handle(phba, io_task->psgl_handle);
- spin_unlock(&phba->io_sgl_lock);
goto free_hndls;
free_mgmt_hndls:
- spin_lock(&phba->mgmt_sgl_lock);
free_mgmt_sgl_handle(phba, io_task->psgl_handle);
- spin_unlock(&phba->mgmt_sgl_lock);
+ io_task->psgl_handle = NULL;
free_hndls:
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[
- beiscsi_conn->beiscsi_conn_cid -
- phba->fw_config.iscsi_cid_start];
+ cri_index = BE_GET_CRI_FROM_CID(
+ beiscsi_conn->beiscsi_conn_cid);
+ pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
if (io_task->pwrb_handle)
free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
io_task->pwrb_handle = NULL;
- pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
+ dma_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
io_task->bhs_pa.u.a64.address);
- SE_DEBUG(DBG_LVL_1, "Alloc of SGL_ICD Failed\n");
+ io_task->cmd_bhs = NULL;
return -ENOMEM;
}
-
-static void beiscsi_cleanup_task(struct iscsi_task *task)
+static int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg,
+ unsigned int num_sg, unsigned int xferlen,
+ unsigned int writedir)
{
+
struct beiscsi_io_task *io_task = task->dd_data;
struct iscsi_conn *conn = task->conn;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_hba *phba = beiscsi_conn->phba;
- struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess;
- struct hwi_wrb_context *pwrb_context;
- struct hwi_controller *phwi_ctrlr;
+ struct iscsi_wrb *pwrb = NULL;
+ unsigned int doorbell = 0;
- phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid
- - phba->fw_config.iscsi_cid_start];
- if (io_task->pwrb_handle) {
- free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
- io_task->pwrb_handle = NULL;
- }
+ pwrb = io_task->pwrb_handle->pwrb;
- if (io_task->cmd_bhs) {
- pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
- io_task->bhs_pa.u.a64.address);
- }
+ io_task->bhs_len = sizeof(struct be_cmd_bhs);
- if (task->sc) {
- if (io_task->psgl_handle) {
- spin_lock(&phba->io_sgl_lock);
- free_io_sgl_handle(phba, io_task->psgl_handle);
- spin_unlock(&phba->io_sgl_lock);
- io_task->psgl_handle = NULL;
- }
+ if (writedir) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, type, pwrb,
+ INI_WR_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp, pwrb, 1);
} else {
- if (task->hdr &&
- ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN))
- return;
- if (io_task->psgl_handle) {
- spin_lock(&phba->mgmt_sgl_lock);
- free_mgmt_sgl_handle(phba, io_task->psgl_handle);
- spin_unlock(&phba->mgmt_sgl_lock);
- io_task->psgl_handle = NULL;
- }
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, type, pwrb,
+ INI_RD_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp, pwrb, 0);
}
+
+ io_task->wrb_type = AMAP_GET_BITS(struct amap_iscsi_wrb_v2,
+ type, pwrb);
+
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, lun, pwrb,
+ cpu_to_be16(*(unsigned short *)
+ &io_task->cmd_bhs->iscsi_hdr.lun));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, r2t_exp_dtl, pwrb, xferlen);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, wrb_idx, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, cmdsn_itt, pwrb,
+ be32_to_cpu(task->cmdsn));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sgl_idx, pwrb,
+ io_task->psgl_handle->sgl_index);
+
+ hwi_write_sgl_v2(pwrb, sg, num_sg, io_task);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ if (io_task->pwrb_context->plast_wrb)
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb,
+ io_task->pwrb_context->plast_wrb,
+ io_task->pwrb_handle->wrb_index);
+ io_task->pwrb_context->plast_wrb = pwrb;
+
+ be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
+
+ doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
+ doorbell |= (io_task->pwrb_handle->wrb_index &
+ DB_DEF_PDU_WRB_INDEX_MASK) <<
+ DB_DEF_PDU_WRB_INDEX_SHIFT;
+ doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
+ iowrite32(doorbell, phba->db_va +
+ beiscsi_conn->doorbell_offset);
+ return 0;
}
static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
@@ -3940,19 +4581,9 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
unsigned int doorbell = 0;
pwrb = io_task->pwrb_handle->pwrb;
- io_task->cmd_bhs->iscsi_hdr.exp_statsn = 0;
io_task->bhs_len = sizeof(struct be_cmd_bhs);
if (writedir) {
- memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48);
- AMAP_SET_BITS(struct amap_pdu_data_out, itt,
- &io_task->cmd_bhs->iscsi_data_pdu,
- (unsigned int)io_task->cmd_bhs->iscsi_hdr.itt);
- AMAP_SET_BITS(struct amap_pdu_data_out, opcode,
- &io_task->cmd_bhs->iscsi_data_pdu,
- ISCSI_OPCODE_SCSI_DATA_OUT);
- AMAP_SET_BITS(struct amap_pdu_data_out, final_bit,
- &io_task->cmd_bhs->iscsi_data_pdu, 1);
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
INI_WR_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
@@ -3961,13 +4592,13 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
INI_RD_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
}
- memcpy(&io_task->cmd_bhs->iscsi_data_pdu.
- dw[offsetof(struct amap_pdu_data_out, lun) / 32],
- io_task->cmd_bhs->iscsi_hdr.lun, sizeof(struct scsi_lun));
+
+ io_task->wrb_type = AMAP_GET_BITS(struct amap_iscsi_wrb,
+ type, pwrb);
AMAP_SET_BITS(struct amap_iscsi_wrb, lun, pwrb,
- cpu_to_be16((unsigned short)io_task->cmd_bhs->iscsi_hdr.
- lun[0]));
+ cpu_to_be16(*(unsigned short *)
+ &io_task->cmd_bhs->iscsi_hdr.lun));
AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb, xferlen);
AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
io_task->pwrb_handle->wrb_index);
@@ -3979,7 +4610,13 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
hwi_write_sgl(pwrb, sg, num_sg, io_task);
AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
- io_task->pwrb_handle->nxt_wrb_index);
+ io_task->pwrb_handle->wrb_index);
+ if (io_task->pwrb_context->plast_wrb)
+ AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb,
+ io_task->pwrb_context->plast_wrb,
+ io_task->pwrb_handle->wrb_index);
+ io_task->pwrb_context->plast_wrb = pwrb;
+
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
@@ -3987,7 +4624,8 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
- iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
+ iowrite32(doorbell, phba->db_va +
+ beiscsi_conn->doorbell_offset);
return 0;
}
@@ -4000,75 +4638,112 @@ static int beiscsi_mtask(struct iscsi_task *task)
struct iscsi_wrb *pwrb = NULL;
unsigned int doorbell = 0;
unsigned int cid;
+ unsigned int pwrb_typeoffset = 0;
+ int ret = 0;
cid = beiscsi_conn->beiscsi_conn_cid;
pwrb = io_task->pwrb_handle->pwrb;
- memset(pwrb, 0, sizeof(*pwrb));
- AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
- be32_to_cpu(task->cmdsn));
- AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
- io_task->pwrb_handle->wrb_index);
- AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb,
- io_task->psgl_handle->sgl_index);
+
+ if (is_chip_be2_be3r(phba)) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
+ be32_to_cpu(task->cmdsn));
+ AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb,
+ io_task->psgl_handle->sgl_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb,
+ task->data_count);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ if (io_task->pwrb_context->plast_wrb)
+ AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb,
+ io_task->pwrb_context->plast_wrb,
+ io_task->pwrb_handle->wrb_index);
+ io_task->pwrb_context->plast_wrb = pwrb;
+
+ pwrb_typeoffset = BE_WRB_TYPE_OFFSET;
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, cmdsn_itt, pwrb,
+ be32_to_cpu(task->cmdsn));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, wrb_idx, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sgl_idx, pwrb,
+ io_task->psgl_handle->sgl_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, r2t_exp_dtl, pwrb,
+ task->data_count);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ if (io_task->pwrb_context->plast_wrb)
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb,
+ io_task->pwrb_context->plast_wrb,
+ io_task->pwrb_handle->wrb_index);
+ io_task->pwrb_context->plast_wrb = pwrb;
+
+ pwrb_typeoffset = SKH_WRB_TYPE_OFFSET;
+ }
+
switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_LOGIN:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- TGT_DM_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1);
- hwi_write_buffer(pwrb, task);
+ ADAPTER_SET_WRB_TYPE(pwrb, TGT_DM_CMD, pwrb_typeoffset);
+ ret = hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_NOOP_OUT:
if (task->hdr->ttt != ISCSI_RESERVED_TAG) {
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- TGT_DM_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt,
- pwrb, 0);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
+ ADAPTER_SET_WRB_TYPE(pwrb, TGT_DM_CMD, pwrb_typeoffset);
+ if (is_chip_be2_be3r(phba))
+ AMAP_SET_BITS(struct amap_iscsi_wrb,
+ dmsg, pwrb, 1);
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ dmsg, pwrb, 1);
} else {
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- INI_RD_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 1);
+ ADAPTER_SET_WRB_TYPE(pwrb, INI_RD_CMD, pwrb_typeoffset);
+ if (is_chip_be2_be3r(phba))
+ AMAP_SET_BITS(struct amap_iscsi_wrb,
+ dmsg, pwrb, 0);
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ dmsg, pwrb, 0);
}
- hwi_write_buffer(pwrb, task);
+ ret = hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_TEXT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- TGT_DM_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
- hwi_write_buffer(pwrb, task);
+ ADAPTER_SET_WRB_TYPE(pwrb, TGT_DM_CMD, pwrb_typeoffset);
+ ret = hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_SCSI_TMFUNC:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- INI_TMF_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
- hwi_write_buffer(pwrb, task);
+ ADAPTER_SET_WRB_TYPE(pwrb, INI_TMF_CMD, pwrb_typeoffset);
+ ret = hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_LOGOUT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- HWH_TYPE_LOGOUT);
- hwi_write_buffer(pwrb, task);
+ ADAPTER_SET_WRB_TYPE(pwrb, HWH_TYPE_LOGOUT, pwrb_typeoffset);
+ ret = hwi_write_buffer(pwrb, task);
break;
default:
- SE_DEBUG(DBG_LVL_1, "opcode =%d Not supported\n",
- task->hdr->opcode & ISCSI_OPCODE_MASK);
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BM_%d : opcode =%d Not supported\n",
+ task->hdr->opcode & ISCSI_OPCODE_MASK);
+
return -EINVAL;
}
- AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb,
- task->data_count);
- AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
- io_task->pwrb_handle->nxt_wrb_index);
- be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
+ if (ret)
+ return ret;
+
+ /* Set the task type */
+ io_task->wrb_type = (is_chip_be2_be3r(phba)) ?
+ AMAP_GET_BITS(struct amap_iscsi_wrb, type, pwrb) :
+ AMAP_GET_BITS(struct amap_iscsi_wrb_v2, type, pwrb);
doorbell |= cid & DB_WRB_POST_CID_MASK;
doorbell |= (io_task->pwrb_handle->wrb_index &
DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
- iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
+ iowrite32(doorbell, phba->db_va +
+ beiscsi_conn->doorbell_offset);
return 0;
}
@@ -4076,320 +4751,1052 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
{
struct beiscsi_io_task *io_task = task->dd_data;
struct scsi_cmnd *sc = task->sc;
+ struct beiscsi_hba *phba;
struct scatterlist *sg;
int num_sg;
unsigned int writedir = 0, xferlen = 0;
+ phba = io_task->conn->phba;
+ /**
+ * HBA in error includes BEISCSI_HBA_FW_TIMEOUT. IO path might be
+ * operational if FW still gets heartbeat from EP FW. Is management
+ * path really needed to continue further?
+ */
+ if (!beiscsi_hba_is_online(phba))
+ return -EIO;
+
+ if (!io_task->conn->login_in_progress)
+ task->hdr->exp_statsn = 0;
+
if (!sc)
return beiscsi_mtask(task);
io_task->scsi_cmnd = sc;
+ io_task->num_sg = 0;
num_sg = scsi_dma_map(sc);
if (num_sg < 0) {
- SE_DEBUG(DBG_LVL_1, " scsi_dma_map Failed\n")
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_ISCSI,
+ "BM_%d : scsi_dma_map Failed "
+ "Driver_ITT : 0x%x ITT : 0x%x Xferlen : 0x%x\n",
+ be32_to_cpu(io_task->cmd_bhs->iscsi_hdr.itt),
+ io_task->libiscsi_itt, scsi_bufflen(sc));
+
return num_sg;
}
+ /**
+ * For scsi cmd task, check num_sg before unmapping in cleanup_task.
+ * For management task, cleanup_task checks mtask_addr before unmapping.
+ */
+ io_task->num_sg = num_sg;
xferlen = scsi_bufflen(sc);
sg = scsi_sglist(sc);
- if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ if (sc->sc_data_direction == DMA_TO_DEVICE)
writedir = 1;
- SE_DEBUG(DBG_LVL_4, "task->imm_count=0x%08x\n",
- task->imm_count);
- } else
+ else
writedir = 0;
- return beiscsi_iotask(task, sg, num_sg, xferlen, writedir);
+
+ return phba->iotask_fn(task, sg, num_sg, xferlen, writedir);
}
-static void beiscsi_remove(struct pci_dev *pcidev)
+/**
+ * beiscsi_bsg_request - handle bsg request from ISCSI transport
+ * @job: job to handle
+ */
+static int beiscsi_bsg_request(struct bsg_job *job)
+{
+ struct Scsi_Host *shost;
+ struct beiscsi_hba *phba;
+ struct iscsi_bsg_request *bsg_req = job->request;
+ int rc = -EINVAL;
+ unsigned int tag;
+ struct be_dma_mem nonemb_cmd;
+ struct be_cmd_resp_hdr *resp;
+ struct iscsi_bsg_reply *bsg_reply = job->reply;
+ unsigned short status, extd_status;
+
+ shost = iscsi_job_to_shost(job);
+ phba = iscsi_host_priv(shost);
+
+ if (!beiscsi_hba_is_online(phba)) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BM_%d : HBA in error 0x%lx\n", phba->state);
+ return -ENXIO;
+ }
+
+ switch (bsg_req->msgcode) {
+ case ISCSI_BSG_HST_VENDOR:
+ nonemb_cmd.va = dma_alloc_coherent(&phba->ctrl.pdev->dev,
+ job->request_payload.payload_len,
+ &nonemb_cmd.dma, GFP_KERNEL);
+ if (nonemb_cmd.va == NULL) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BM_%d : Failed to allocate memory for "
+ "beiscsi_bsg_request\n");
+ return -ENOMEM;
+ }
+ tag = mgmt_vendor_specific_fw_cmd(&phba->ctrl, phba, job,
+ &nonemb_cmd);
+ if (!tag) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BM_%d : MBX Tag Allocation Failed\n");
+
+ dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+ return -EAGAIN;
+ }
+
+ rc = wait_event_interruptible_timeout(
+ phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_tag_status[tag],
+ msecs_to_jiffies(
+ BEISCSI_HOST_MBX_TIMEOUT));
+
+ if (!test_bit(BEISCSI_HBA_ONLINE, &phba->state)) {
+ clear_bit(MCC_TAG_STATE_RUNNING,
+ &phba->ctrl.ptag_state[tag].tag_state);
+ dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+ return -EIO;
+ }
+ extd_status = (phba->ctrl.mcc_tag_status[tag] &
+ CQE_STATUS_ADDL_MASK) >> CQE_STATUS_ADDL_SHIFT;
+ status = phba->ctrl.mcc_tag_status[tag] & CQE_STATUS_MASK;
+ free_mcc_wrb(&phba->ctrl, tag);
+ resp = (struct be_cmd_resp_hdr *)nonemb_cmd.va;
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ nonemb_cmd.va, (resp->response_length
+ + sizeof(*resp)));
+ bsg_reply->reply_payload_rcv_len = resp->response_length;
+ bsg_reply->result = status;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+ if (status || extd_status) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BM_%d : MBX Cmd Failed"
+ " status = %d extd_status = %d\n",
+ status, extd_status);
+
+ return -EIO;
+ } else {
+ rc = 0;
+ }
+ break;
+
+ default:
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BM_%d : Unsupported bsg command: 0x%x\n",
+ bsg_req->msgcode);
+ break;
+ }
+
+ return rc;
+}
+
+static void beiscsi_hba_attrs_init(struct beiscsi_hba *phba)
+{
+ /* Set the logging parameter */
+ beiscsi_log_enable_init(phba, beiscsi_log_enable);
+}
+
+void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle)
+{
+ if (phba->boot_struct.boot_kset)
+ return;
+
+ /* skip if boot work is already in progress */
+ if (test_and_set_bit(BEISCSI_HBA_BOOT_WORK, &phba->state))
+ return;
+
+ phba->boot_struct.retry = 3;
+ phba->boot_struct.tag = 0;
+ phba->boot_struct.s_handle = s_handle;
+ phba->boot_struct.action = BEISCSI_BOOT_GET_SHANDLE;
+ schedule_work(&phba->boot_work);
+}
+
+#define BEISCSI_SYSFS_ISCSI_BOOT_FLAGS 3
+/*
+ * beiscsi_show_boot_tgt_info()
+ * Boot flag info for iscsi-utilities
+ * Bit 0 Block valid flag
+ * Bit 1 Firmware booting selected
+ */
+static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf)
+{
+ struct beiscsi_hba *phba = data;
+ struct mgmt_session_info *boot_sess = &phba->boot_struct.boot_sess;
+ struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0];
+ char *str = buf;
+ int rc = -EPERM;
+
+ switch (type) {
+ case ISCSI_BOOT_TGT_NAME:
+ rc = sprintf(buf, "%.*s\n",
+ (int)strlen(boot_sess->target_name),
+ (char *)&boot_sess->target_name);
+ break;
+ case ISCSI_BOOT_TGT_IP_ADDR:
+ if (boot_conn->dest_ipaddr.ip_type == BEISCSI_IP_TYPE_V4)
+ rc = sprintf(buf, "%pI4\n",
+ (char *)&boot_conn->dest_ipaddr.addr);
+ else
+ rc = sprintf(str, "%pI6\n",
+ (char *)&boot_conn->dest_ipaddr.addr);
+ break;
+ case ISCSI_BOOT_TGT_PORT:
+ rc = sprintf(str, "%d\n", boot_conn->dest_port);
+ break;
+
+ case ISCSI_BOOT_TGT_CHAP_NAME:
+ rc = sprintf(str, "%.*s\n",
+ boot_conn->negotiated_login_options.auth_data.chap.
+ target_chap_name_length,
+ (char *)&boot_conn->negotiated_login_options.
+ auth_data.chap.target_chap_name);
+ break;
+ case ISCSI_BOOT_TGT_CHAP_SECRET:
+ rc = sprintf(str, "%.*s\n",
+ boot_conn->negotiated_login_options.auth_data.chap.
+ target_secret_length,
+ (char *)&boot_conn->negotiated_login_options.
+ auth_data.chap.target_secret);
+ break;
+ case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+ rc = sprintf(str, "%.*s\n",
+ boot_conn->negotiated_login_options.auth_data.chap.
+ intr_chap_name_length,
+ (char *)&boot_conn->negotiated_login_options.
+ auth_data.chap.intr_chap_name);
+ break;
+ case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+ rc = sprintf(str, "%.*s\n",
+ boot_conn->negotiated_login_options.auth_data.chap.
+ intr_secret_length,
+ (char *)&boot_conn->negotiated_login_options.
+ auth_data.chap.intr_secret);
+ break;
+ case ISCSI_BOOT_TGT_FLAGS:
+ rc = sprintf(str, "%d\n", BEISCSI_SYSFS_ISCSI_BOOT_FLAGS);
+ break;
+ case ISCSI_BOOT_TGT_NIC_ASSOC:
+ rc = sprintf(str, "0\n");
+ break;
+ }
+ return rc;
+}
+
+static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf)
+{
+ struct beiscsi_hba *phba = data;
+ char *str = buf;
+ int rc = -EPERM;
+
+ switch (type) {
+ case ISCSI_BOOT_INI_INITIATOR_NAME:
+ rc = sprintf(str, "%s\n",
+ phba->boot_struct.boot_sess.initiator_iscsiname);
+ break;
+ }
+ return rc;
+}
+
+static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf)
+{
+ struct beiscsi_hba *phba = data;
+ char *str = buf;
+ int rc = -EPERM;
+
+ switch (type) {
+ case ISCSI_BOOT_ETH_FLAGS:
+ rc = sprintf(str, "%d\n", BEISCSI_SYSFS_ISCSI_BOOT_FLAGS);
+ break;
+ case ISCSI_BOOT_ETH_INDEX:
+ rc = sprintf(str, "0\n");
+ break;
+ case ISCSI_BOOT_ETH_MAC:
+ rc = beiscsi_get_macaddr(str, phba);
+ break;
+ }
+ return rc;
+}
+
+static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type)
+{
+ umode_t rc = 0;
+
+ switch (type) {
+ case ISCSI_BOOT_TGT_NAME:
+ case ISCSI_BOOT_TGT_IP_ADDR:
+ case ISCSI_BOOT_TGT_PORT:
+ case ISCSI_BOOT_TGT_CHAP_NAME:
+ case ISCSI_BOOT_TGT_CHAP_SECRET:
+ case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+ case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+ case ISCSI_BOOT_TGT_NIC_ASSOC:
+ case ISCSI_BOOT_TGT_FLAGS:
+ rc = S_IRUGO;
+ break;
+ }
+ return rc;
+}
+
+static umode_t beiscsi_ini_get_attr_visibility(void *data, int type)
+{
+ umode_t rc = 0;
+
+ switch (type) {
+ case ISCSI_BOOT_INI_INITIATOR_NAME:
+ rc = S_IRUGO;
+ break;
+ }
+ return rc;
+}
+
+static umode_t beiscsi_eth_get_attr_visibility(void *data, int type)
+{
+ umode_t rc = 0;
+
+ switch (type) {
+ case ISCSI_BOOT_ETH_FLAGS:
+ case ISCSI_BOOT_ETH_MAC:
+ case ISCSI_BOOT_ETH_INDEX:
+ rc = S_IRUGO;
+ break;
+ }
+ return rc;
+}
+
+static void beiscsi_boot_kobj_release(void *data)
+{
+ struct beiscsi_hba *phba = data;
+
+ scsi_host_put(phba->shost);
+}
+
+static int beiscsi_boot_create_kset(struct beiscsi_hba *phba)
+{
+ struct boot_struct *bs = &phba->boot_struct;
+ struct iscsi_boot_kobj *boot_kobj;
+
+ if (bs->boot_kset) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BM_%d: boot_kset already created\n");
+ return 0;
+ }
+
+ bs->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no);
+ if (!bs->boot_kset) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BM_%d: boot_kset alloc failed\n");
+ return -ENOMEM;
+ }
+
+ /* get shost ref because the show function will refer phba */
+ if (!scsi_host_get(phba->shost))
+ goto free_kset;
+
+ boot_kobj = iscsi_boot_create_target(bs->boot_kset, 0, phba,
+ beiscsi_show_boot_tgt_info,
+ beiscsi_tgt_get_attr_visibility,
+ beiscsi_boot_kobj_release);
+ if (!boot_kobj)
+ goto put_shost;
+
+ if (!scsi_host_get(phba->shost))
+ goto free_kset;
+
+ boot_kobj = iscsi_boot_create_initiator(bs->boot_kset, 0, phba,
+ beiscsi_show_boot_ini_info,
+ beiscsi_ini_get_attr_visibility,
+ beiscsi_boot_kobj_release);
+ if (!boot_kobj)
+ goto put_shost;
+
+ if (!scsi_host_get(phba->shost))
+ goto free_kset;
+
+ boot_kobj = iscsi_boot_create_ethernet(bs->boot_kset, 0, phba,
+ beiscsi_show_boot_eth_info,
+ beiscsi_eth_get_attr_visibility,
+ beiscsi_boot_kobj_release);
+ if (!boot_kobj)
+ goto put_shost;
+
+ return 0;
+
+put_shost:
+ scsi_host_put(phba->shost);
+free_kset:
+ iscsi_boot_destroy_kset(bs->boot_kset);
+ bs->boot_kset = NULL;
+ return -ENOMEM;
+}
+
+static void beiscsi_boot_work(struct work_struct *work)
+{
+ struct beiscsi_hba *phba =
+ container_of(work, struct beiscsi_hba, boot_work);
+ struct boot_struct *bs = &phba->boot_struct;
+ unsigned int tag = 0;
+
+ if (!beiscsi_hba_is_online(phba))
+ return;
+
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BM_%d : %s action %d\n",
+ __func__, phba->boot_struct.action);
+
+ switch (phba->boot_struct.action) {
+ case BEISCSI_BOOT_REOPEN_SESS:
+ tag = beiscsi_boot_reopen_sess(phba);
+ break;
+ case BEISCSI_BOOT_GET_SHANDLE:
+ tag = __beiscsi_boot_get_shandle(phba, 1);
+ break;
+ case BEISCSI_BOOT_GET_SINFO:
+ tag = beiscsi_boot_get_sinfo(phba);
+ break;
+ case BEISCSI_BOOT_LOGOUT_SESS:
+ tag = beiscsi_boot_logout_sess(phba);
+ break;
+ case BEISCSI_BOOT_CREATE_KSET:
+ beiscsi_boot_create_kset(phba);
+ /**
+ * updated boot_kset is made visible to all before
+ * ending the boot work.
+ */
+ mb();
+ clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state);
+ return;
+ }
+ if (!tag) {
+ if (bs->retry--)
+ schedule_work(&phba->boot_work);
+ else
+ clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state);
+ }
+}
+
+static void beiscsi_eqd_update_work(struct work_struct *work)
{
- struct beiscsi_hba *phba = NULL;
- struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
+ struct be_set_eqd set_eqd[MAX_CPUS];
+ struct hwi_controller *phwi_ctrlr;
struct be_eq_obj *pbe_eq;
- unsigned int i, msix_vec;
- u8 *real_offset = 0;
- u32 value = 0;
+ struct beiscsi_hba *phba;
+ unsigned int pps, delta;
+ struct be_aic_obj *aic;
+ int eqd, i, num = 0;
+ unsigned long now;
- phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev);
- if (!phba) {
- dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n");
+ phba = container_of(work, struct beiscsi_hba, eqd_update.work);
+ if (!beiscsi_hba_is_online(phba))
return;
- }
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- hwi_disable_intr(phba);
- if (phba->msix_enabled) {
- for (i = 0; i <= phba->num_cpus; i++) {
- msix_vec = phba->msix_entries[i].vector;
- free_irq(msix_vec, &phwi_context->be_eq[i]);
+
+ for (i = 0; i <= phba->num_cpus; i++) {
+ aic = &phba->aic_obj[i];
+ pbe_eq = &phwi_context->be_eq[i];
+ now = jiffies;
+ if (!aic->jiffies || time_before(now, aic->jiffies) ||
+ pbe_eq->cq_count < aic->eq_prev) {
+ aic->jiffies = now;
+ aic->eq_prev = pbe_eq->cq_count;
+ continue;
}
- } else
- if (phba->pcidev->irq)
- free_irq(phba->pcidev->irq, phba);
- pci_disable_msix(phba->pcidev);
- destroy_workqueue(phba->wq);
- if (blk_iopoll_enabled)
- for (i = 0; i < phba->num_cpus; i++) {
- pbe_eq = &phwi_context->be_eq[i];
- blk_iopoll_disable(&pbe_eq->iopoll);
+ delta = jiffies_to_msecs(now - aic->jiffies);
+ pps = (((u32)(pbe_eq->cq_count - aic->eq_prev) * 1000) / delta);
+ eqd = (pps / 1500) << 2;
+
+ if (eqd < 8)
+ eqd = 0;
+ eqd = min_t(u32, eqd, BEISCSI_EQ_DELAY_MAX);
+ eqd = max_t(u32, eqd, BEISCSI_EQ_DELAY_MIN);
+
+ aic->jiffies = now;
+ aic->eq_prev = pbe_eq->cq_count;
+
+ if (eqd != aic->prev_eqd) {
+ set_eqd[num].delay_multiplier = (eqd * 65)/100;
+ set_eqd[num].eq_id = pbe_eq->q.id;
+ aic->prev_eqd = eqd;
+ num++;
}
+ }
+ if (num)
+ /* completion of this is ignored */
+ beiscsi_modify_eq_delay(phba, set_eqd, num);
- beiscsi_clean_port(phba);
- beiscsi_free_mem(phba);
- real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
+ schedule_delayed_work(&phba->eqd_update,
+ msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL));
+}
+
+static void beiscsi_hw_tpe_check(struct timer_list *t)
+{
+ struct beiscsi_hba *phba = timer_container_of(phba, t, hw_check);
+ u32 wait;
+
+ /* if not TPE, do nothing */
+ if (!beiscsi_detect_tpe(phba))
+ return;
+
+ /* wait default 4000ms before recovering */
+ wait = 4000;
+ if (phba->ue2rp > BEISCSI_UE_DETECT_INTERVAL)
+ wait = phba->ue2rp - BEISCSI_UE_DETECT_INTERVAL;
+ queue_delayed_work(phba->wq, &phba->recover_port,
+ msecs_to_jiffies(wait));
+}
+
+static void beiscsi_hw_health_check(struct timer_list *t)
+{
+ struct beiscsi_hba *phba = timer_container_of(phba, t, hw_check);
- value = readl((void *)real_offset);
+ beiscsi_detect_ue(phba);
+ if (beiscsi_detect_ue(phba)) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BM_%d : port in error: %lx\n", phba->state);
+ /* sessions are no longer valid, so first fail the sessions */
+ queue_work(phba->wq, &phba->sess_work);
- if (value & 0x00010000) {
- value &= 0xfffeffff;
- writel(value, (void *)real_offset);
+ /* detect UER supported */
+ if (!test_bit(BEISCSI_HBA_UER_SUPP, &phba->state))
+ return;
+ /* modify this timer to check TPE */
+ phba->hw_check.function = beiscsi_hw_tpe_check;
}
- beiscsi_unmap_pci_function(phba);
- pci_free_consistent(phba->pcidev,
- phba->ctrl.mbox_mem_alloced.size,
- phba->ctrl.mbox_mem_alloced.va,
- phba->ctrl.mbox_mem_alloced.dma);
- if (phba->boot_kset)
- iscsi_boot_destroy_kset(phba->boot_kset);
- iscsi_host_remove(phba->shost);
- pci_dev_put(phba->pcidev);
- iscsi_host_free(phba->shost);
+
+ mod_timer(&phba->hw_check,
+ jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL));
+}
+
+/*
+ * beiscsi_enable_port()- Enables the disabled port.
+ * Only port resources freed in disable function are reallocated.
+ * This is called in HBA error handling path.
+ *
+ * @phba: Instance of driver private structure
+ *
+ **/
+static int beiscsi_enable_port(struct beiscsi_hba *phba)
+{
+ struct hwi_context_memory *phwi_context;
+ struct hwi_controller *phwi_ctrlr;
+ struct be_eq_obj *pbe_eq;
+ int ret, i;
+
+ if (test_bit(BEISCSI_HBA_ONLINE, &phba->state)) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BM_%d : %s : port is online %lx\n",
+ __func__, phba->state);
+ return 0;
+ }
+
+ ret = beiscsi_init_sliport(phba);
+ if (ret)
+ return ret;
+
+ be2iscsi_enable_msix(phba);
+
+ beiscsi_get_params(phba);
+ beiscsi_set_host_data(phba);
+ /* Re-enable UER. If different TPE occurs then it is recoverable. */
+ beiscsi_set_uer_feature(phba);
+
+ phba->shost->max_id = phba->params.cxns_per_ctrl - 1;
+ phba->shost->can_queue = phba->params.ios_per_ctrl;
+ ret = beiscsi_init_port(phba);
+ if (ret < 0) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BM_%d : init port failed\n");
+ goto disable_msix;
+ }
+
+ for (i = 0; i < MAX_MCC_CMD; i++) {
+ init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]);
+ phba->ctrl.mcc_tag[i] = i + 1;
+ phba->ctrl.mcc_tag_status[i + 1] = 0;
+ phba->ctrl.mcc_tag_available++;
+ }
+
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll);
+ }
+
+ i = (phba->pcidev->msix_enabled) ? i : 0;
+ /* Work item for MCC handling */
+ pbe_eq = &phwi_context->be_eq[i];
+ INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work);
+
+ ret = beiscsi_init_irqs(phba);
+ if (ret < 0) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BM_%d : setup IRQs failed %d\n", ret);
+ goto cleanup_port;
+ }
+ hwi_enable_intr(phba);
+ /* port operational: clear all error bits */
+ set_bit(BEISCSI_HBA_ONLINE, &phba->state);
+ __beiscsi_log(phba, KERN_INFO,
+ "BM_%d : port online: 0x%lx\n", phba->state);
+
+ /* start hw_check timer and eqd_update work */
+ schedule_delayed_work(&phba->eqd_update,
+ msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL));
+
+ /**
+ * Timer function gets modified for TPE detection.
+ * Always reinit to do health check first.
+ */
+ phba->hw_check.function = beiscsi_hw_health_check;
+ mod_timer(&phba->hw_check,
+ jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL));
+ return 0;
+
+cleanup_port:
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ irq_poll_disable(&pbe_eq->iopoll);
+ }
+ hwi_cleanup_port(phba);
+
+disable_msix:
+ pci_free_irq_vectors(phba->pcidev);
+ return ret;
}
-static void beiscsi_msix_enable(struct beiscsi_hba *phba)
+/*
+ * beiscsi_disable_port()- Disable port and cleanup driver resources.
+ * This is called in HBA error handling and driver removal.
+ * @phba: Instance Priv structure
+ * @unload: indicate driver is unloading
+ *
+ * Free the OS and HW resources held by the driver
+ **/
+static void beiscsi_disable_port(struct beiscsi_hba *phba, int unload)
{
- int i, status;
+ struct hwi_context_memory *phwi_context;
+ struct hwi_controller *phwi_ctrlr;
+ struct be_eq_obj *pbe_eq;
+ unsigned int i;
- for (i = 0; i <= phba->num_cpus; i++)
- phba->msix_entries[i].entry = i;
+ if (!test_and_clear_bit(BEISCSI_HBA_ONLINE, &phba->state))
+ return;
- status = pci_enable_msix(phba->pcidev, phba->msix_entries,
- (phba->num_cpus + 1));
- if (!status)
- phba->msix_enabled = true;
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+ hwi_disable_intr(phba);
+ beiscsi_free_irqs(phba);
+ pci_free_irq_vectors(phba->pcidev);
- return;
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ irq_poll_disable(&pbe_eq->iopoll);
+ }
+ cancel_delayed_work_sync(&phba->eqd_update);
+ cancel_work_sync(&phba->boot_work);
+ /* WQ might be running cancel queued mcc_work if we are not exiting */
+ if (!unload && beiscsi_hba_in_error(phba)) {
+ pbe_eq = &phwi_context->be_eq[i];
+ cancel_work_sync(&pbe_eq->mcc_work);
+ }
+ hwi_cleanup_port(phba);
+ beiscsi_cleanup_port(phba);
}
-static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
- const struct pci_device_id *id)
+static void beiscsi_sess_work(struct work_struct *work)
+{
+ struct beiscsi_hba *phba;
+
+ phba = container_of(work, struct beiscsi_hba, sess_work);
+ /*
+ * This work gets scheduled only in case of HBA error.
+ * Old sessions are gone so need to be re-established.
+ * iscsi_session_failure needs process context hence this work.
+ */
+ iscsi_host_for_each_session(phba->shost, beiscsi_session_fail);
+}
+
+static void beiscsi_recover_port(struct work_struct *work)
+{
+ struct beiscsi_hba *phba;
+
+ phba = container_of(work, struct beiscsi_hba, recover_port.work);
+ beiscsi_disable_port(phba, 0);
+ beiscsi_enable_port(phba);
+}
+
+static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
{
struct beiscsi_hba *phba = NULL;
- struct hwi_controller *phwi_ctrlr;
+
+ phba = (struct beiscsi_hba *)pci_get_drvdata(pdev);
+ set_bit(BEISCSI_HBA_PCI_ERR, &phba->state);
+
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : EEH error detected\n");
+
+ /* first stop UE detection when PCI error detected */
+ timer_delete_sync(&phba->hw_check);
+ cancel_delayed_work_sync(&phba->recover_port);
+
+ /* sessions are no longer valid, so first fail the sessions */
+ iscsi_host_for_each_session(phba->shost, beiscsi_session_fail);
+ beiscsi_disable_port(phba, 0);
+
+ if (state == pci_channel_io_perm_failure) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : EEH : State PERM Failure");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_disable_device(pdev);
+
+ /* The error could cause the FW to trigger a flash debug dump.
+ * Resetting the card while flash dump is in progress
+ * can cause it not to recover; wait for it to finish.
+ * Wait only for first function as it is needed only once per
+ * adapter.
+ **/
+ if (pdev->devfn == 0)
+ ssleep(30);
+
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev)
+{
+ struct beiscsi_hba *phba = NULL;
+ int status = 0;
+
+ phba = (struct beiscsi_hba *)pci_get_drvdata(pdev);
+
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : EEH Reset\n");
+
+ status = pci_enable_device(pdev);
+ if (status)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ pci_set_master(pdev);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ status = beiscsi_check_fw_rdy(phba);
+ if (status) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT,
+ "BM_%d : EEH Reset Completed\n");
+ } else {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT,
+ "BM_%d : EEH Reset Completion Failure\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void beiscsi_eeh_resume(struct pci_dev *pdev)
+{
+ struct beiscsi_hba *phba;
+ int ret;
+
+ phba = (struct beiscsi_hba *)pci_get_drvdata(pdev);
+ pci_save_state(pdev);
+
+ ret = beiscsi_enable_port(phba);
+ if (ret)
+ __beiscsi_log(phba, KERN_ERR,
+ "BM_%d : AER EEH resume failed\n");
+}
+
+static int beiscsi_dev_probe(struct pci_dev *pcidev,
+ const struct pci_device_id *id)
+{
struct hwi_context_memory *phwi_context;
+ struct hwi_controller *phwi_ctrlr;
+ struct beiscsi_hba *phba = NULL;
struct be_eq_obj *pbe_eq;
- int ret, num_cpus, i;
- u8 *real_offset = 0;
- u32 value = 0;
+ unsigned int s_handle;
+ int ret, i;
ret = beiscsi_enable_pci(pcidev);
if (ret < 0) {
- dev_err(&pcidev->dev, "beiscsi_dev_probe-"
- " Failed to enable pci device\n");
+ dev_err(&pcidev->dev,
+ "beiscsi_dev_probe - Failed to enable pci device\n");
return ret;
}
phba = beiscsi_hba_alloc(pcidev);
if (!phba) {
- dev_err(&pcidev->dev, "beiscsi_dev_probe-"
- " Failed in beiscsi_hba_alloc\n");
+ dev_err(&pcidev->dev,
+ "beiscsi_dev_probe - Failed in beiscsi_hba_alloc\n");
+ ret = -ENOMEM;
goto disable_pci;
}
+ pci_save_state(pcidev);
+
+ /* Initialize Driver configuration Paramters */
+ beiscsi_hba_attrs_init(phba);
+
+ phba->mac_addr_set = false;
+
switch (pcidev->device) {
case BE_DEVICE_ID1:
case OC_DEVICE_ID1:
case OC_DEVICE_ID2:
phba->generation = BE_GEN2;
+ phba->iotask_fn = beiscsi_iotask;
+ dev_warn(&pcidev->dev,
+ "Obsolete/Unsupported BE2 Adapter Family\n");
break;
case BE_DEVICE_ID2:
case OC_DEVICE_ID3:
phba->generation = BE_GEN3;
+ phba->iotask_fn = beiscsi_iotask;
+ break;
+ case OC_SKH_ID1:
+ phba->generation = BE_GEN4;
+ phba->iotask_fn = beiscsi_iotask_v2;
break;
default:
phba->generation = 0;
}
- if (enable_msix)
- num_cpus = find_num_cpus();
- else
- num_cpus = 1;
- phba->num_cpus = num_cpus;
- SE_DEBUG(DBG_LVL_8, "num_cpus = %d\n", phba->num_cpus);
-
- if (enable_msix)
- beiscsi_msix_enable(phba);
ret = be_ctrl_init(phba, pcidev);
if (ret) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
- "Failed in be_ctrl_init\n");
- goto hba_free;
- }
-
- if (!num_hba) {
- real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
- value = readl((void *)real_offset);
- if (value & 0x00010000) {
- gcrashmode++;
- shost_printk(KERN_ERR, phba->shost,
- "Loading Driver in crashdump mode\n");
- ret = beiscsi_pci_soft_reset(phba);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "Reset Failed. Aborting Crashdump\n");
- goto hba_free;
- }
- ret = be_chk_reset_complete(phba);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "Failed to get out of reset."
- "Aborting Crashdump\n");
- goto hba_free;
- }
- } else {
- value |= 0x00010000;
- writel(value, (void *)real_offset);
- num_hba++;
- }
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : be_ctrl_init failed\n");
+ goto free_hba;
}
+ ret = beiscsi_init_sliport(phba);
+ if (ret)
+ goto free_hba;
+
spin_lock_init(&phba->io_sgl_lock);
spin_lock_init(&phba->mgmt_sgl_lock);
- spin_lock_init(&phba->isr_lock);
- ret = mgmt_get_fw_config(&phba->ctrl, phba);
+ spin_lock_init(&phba->async_pdu_lock);
+ ret = beiscsi_get_fw_config(&phba->ctrl, phba);
if (ret != 0) {
- shost_printk(KERN_ERR, phba->shost,
- "Error getting fw config\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Error getting fw config\n");
goto free_port;
}
- phba->shost->max_id = phba->fw_config.iscsi_cid_count;
+ beiscsi_get_port_name(&phba->ctrl, phba);
beiscsi_get_params(phba);
+ beiscsi_set_host_data(phba);
+ beiscsi_set_uer_feature(phba);
+
+ be2iscsi_enable_msix(phba);
+
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "BM_%d : num_cpus = %d\n",
+ phba->num_cpus);
+
+ phba->shost->max_id = phba->params.cxns_per_ctrl;
phba->shost->can_queue = phba->params.ios_per_ctrl;
+ ret = beiscsi_get_memory(phba);
+ if (ret < 0) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : alloc host mem failed\n");
+ goto free_port;
+ }
+
ret = beiscsi_init_port(phba);
if (ret < 0) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
- "Failed in beiscsi_init_port\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : init port failed\n");
+ beiscsi_free_mem(phba);
goto free_port;
}
- for (i = 0; i < MAX_MCC_CMD ; i++) {
+ for (i = 0; i < MAX_MCC_CMD; i++) {
init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]);
phba->ctrl.mcc_tag[i] = i + 1;
- phba->ctrl.mcc_numtag[i + 1] = 0;
+ phba->ctrl.mcc_tag_status[i + 1] = 0;
phba->ctrl.mcc_tag_available++;
+ memset(&phba->ctrl.ptag_state[i].tag_mem_state, 0,
+ sizeof(struct be_dma_mem));
}
phba->ctrl.mcc_alloc_index = phba->ctrl.mcc_free_index = 0;
- snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u",
- phba->shost->host_no);
- phba->wq = alloc_workqueue(phba->wq_name, WQ_MEM_RECLAIM, 1);
+ phba->wq = alloc_workqueue("beiscsi_%02x_wq",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 1,
+ phba->shost->host_no);
if (!phba->wq) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
- "Failed to allocate work queue\n");
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : beiscsi_dev_probe-"
+ "Failed to allocate work queue\n");
+ ret = -ENOMEM;
goto free_twq;
}
- INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs);
+ INIT_DELAYED_WORK(&phba->eqd_update, beiscsi_eqd_update_work);
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- if (blk_iopoll_enabled) {
- for (i = 0; i < phba->num_cpus; i++) {
- pbe_eq = &phwi_context->be_eq[i];
- blk_iopoll_init(&pbe_eq->iopoll, be_iopoll_budget,
- be_iopoll);
- blk_iopoll_enable(&pbe_eq->iopoll);
- }
+
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll);
}
+
+ i = (phba->pcidev->msix_enabled) ? i : 0;
+ /* Work item for MCC handling */
+ pbe_eq = &phwi_context->be_eq[i];
+ INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work);
+
ret = beiscsi_init_irqs(phba);
if (ret < 0) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
- "Failed to beiscsi_init_irqs\n");
- goto free_blkenbld;
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : beiscsi_dev_probe-"
+ "Failed to beiscsi_init_irqs\n");
+ goto disable_iopoll;
}
hwi_enable_intr(phba);
- ret = beiscsi_get_boot_info(phba);
- if (ret < 0) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
- "No Boot Devices !!!!!\n");
+
+ ret = iscsi_host_add(phba->shost, &phba->pcidev->dev);
+ if (ret)
+ goto free_irqs;
+
+ /* set online bit after port is operational */
+ set_bit(BEISCSI_HBA_ONLINE, &phba->state);
+ __beiscsi_log(phba, KERN_INFO,
+ "BM_%d : port online: 0x%lx\n", phba->state);
+
+ INIT_WORK(&phba->boot_work, beiscsi_boot_work);
+ ret = beiscsi_boot_get_shandle(phba, &s_handle);
+ if (ret > 0) {
+ beiscsi_start_boot_work(phba, s_handle);
+ /**
+ * Set this bit after starting the work to let
+ * probe handle it first.
+ * ASYNC event can too schedule this work.
+ */
+ set_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state);
}
- SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED\n\n\n");
+
+ beiscsi_iface_create_default(phba);
+ schedule_delayed_work(&phba->eqd_update,
+ msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL));
+
+ INIT_WORK(&phba->sess_work, beiscsi_sess_work);
+ INIT_DELAYED_WORK(&phba->recover_port, beiscsi_recover_port);
+ /**
+ * Start UE detection here. UE before this will cause stall in probe
+ * and eventually fail the probe.
+ */
+ timer_setup(&phba->hw_check, beiscsi_hw_health_check, 0);
+ mod_timer(&phba->hw_check,
+ jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL));
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
+ "\n\n\n BM_%d : SUCCESS - DRIVER LOADED\n\n\n");
return 0;
-free_blkenbld:
+free_irqs:
+ hwi_disable_intr(phba);
+ beiscsi_free_irqs(phba);
+disable_iopoll:
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ irq_poll_disable(&pbe_eq->iopoll);
+ }
destroy_workqueue(phba->wq);
- if (blk_iopoll_enabled)
- for (i = 0; i < phba->num_cpus; i++) {
- pbe_eq = &phwi_context->be_eq[i];
- blk_iopoll_disable(&pbe_eq->iopoll);
- }
free_twq:
- beiscsi_clean_port(phba);
+ hwi_cleanup_port(phba);
+ beiscsi_cleanup_port(phba);
beiscsi_free_mem(phba);
free_port:
- real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
+ dma_free_coherent(&phba->pcidev->dev,
+ phba->ctrl.mbox_mem_alloced.size,
+ phba->ctrl.mbox_mem_alloced.va,
+ phba->ctrl.mbox_mem_alloced.dma);
+ beiscsi_unmap_pci_function(phba);
+free_hba:
+ pci_disable_msix(phba->pcidev);
+ pci_dev_put(phba->pcidev);
+ iscsi_host_free(phba->shost);
+ pci_set_drvdata(pcidev, NULL);
+disable_pci:
+ pci_release_regions(pcidev);
+ pci_disable_device(pcidev);
+ return ret;
+}
- value = readl((void *)real_offset);
+static void beiscsi_remove(struct pci_dev *pcidev)
+{
+ struct beiscsi_hba *phba = NULL;
- if (value & 0x00010000) {
- value &= 0xfffeffff;
- writel(value, (void *)real_offset);
+ phba = pci_get_drvdata(pcidev);
+ if (!phba) {
+ dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n");
+ return;
}
- pci_free_consistent(phba->pcidev,
+ /* first stop UE detection before unloading */
+ timer_delete_sync(&phba->hw_check);
+ cancel_delayed_work_sync(&phba->recover_port);
+ cancel_work_sync(&phba->sess_work);
+
+ beiscsi_iface_destroy_default(phba);
+ iscsi_host_remove(phba->shost, false);
+ beiscsi_disable_port(phba, 1);
+
+ /* after cancelling boot_work */
+ iscsi_boot_destroy_kset(phba->boot_struct.boot_kset);
+
+ /* free all resources */
+ destroy_workqueue(phba->wq);
+ beiscsi_free_mem(phba);
+
+ /* ctrl uninit */
+ beiscsi_unmap_pci_function(phba);
+ dma_free_coherent(&phba->pcidev->dev,
phba->ctrl.mbox_mem_alloced.size,
phba->ctrl.mbox_mem_alloced.va,
- phba->ctrl.mbox_mem_alloced.dma);
- beiscsi_unmap_pci_function(phba);
-hba_free:
- if (phba->msix_enabled)
- pci_disable_msix(phba->pcidev);
- iscsi_host_remove(phba->shost);
+ phba->ctrl.mbox_mem_alloced.dma);
+
pci_dev_put(phba->pcidev);
iscsi_host_free(phba->shost);
-disable_pci:
+ pci_set_drvdata(pcidev, NULL);
+ pci_release_regions(pcidev);
pci_disable_device(pcidev);
- return ret;
}
+
+static const struct pci_error_handlers beiscsi_eeh_handlers = {
+ .error_detected = beiscsi_eeh_err_detected,
+ .slot_reset = beiscsi_eeh_reset,
+ .resume = beiscsi_eeh_resume,
+};
+
struct iscsi_transport beiscsi_iscsi_transport = {
.owner = THIS_MODULE,
.name = DRV_NAME,
.caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_TEXT_NEGO |
CAP_MULTI_R2T | CAP_DATADGST | CAP_DATA_PATH_OFFLOAD,
- .param_mask = ISCSI_MAX_RECV_DLENGTH |
- ISCSI_MAX_XMIT_DLENGTH |
- ISCSI_HDRDGST_EN |
- ISCSI_DATADGST_EN |
- ISCSI_INITIAL_R2T_EN |
- ISCSI_MAX_R2T |
- ISCSI_IMM_DATA_EN |
- ISCSI_FIRST_BURST |
- ISCSI_MAX_BURST |
- ISCSI_PDU_INORDER_EN |
- ISCSI_DATASEQ_INORDER_EN |
- ISCSI_ERL |
- ISCSI_CONN_PORT |
- ISCSI_CONN_ADDRESS |
- ISCSI_EXP_STATSN |
- ISCSI_PERSISTENT_PORT |
- ISCSI_PERSISTENT_ADDRESS |
- ISCSI_TARGET_NAME | ISCSI_TPGT |
- ISCSI_USERNAME | ISCSI_PASSWORD |
- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO |
- ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
- ISCSI_HOST_INITIATOR_NAME,
.create_session = beiscsi_session_create,
.destroy_session = beiscsi_session_destroy,
.create_conn = beiscsi_conn_create,
.bind_conn = beiscsi_conn_bind,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = iscsi_conn_teardown,
+ .attr_is_visible = beiscsi_attr_is_visible,
+ .set_iface_param = beiscsi_iface_set_param,
+ .get_iface_param = beiscsi_iface_get_param,
.set_param = beiscsi_set_param,
.get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param,
@@ -4407,16 +5814,17 @@ struct iscsi_transport beiscsi_iscsi_transport = {
.ep_poll = beiscsi_ep_poll,
.ep_disconnect = beiscsi_ep_disconnect,
.session_recovery_timedout = iscsi_session_recovery_timedout,
+ .bsg_request = beiscsi_bsg_request,
};
static struct pci_driver beiscsi_pci_driver = {
.name = DRV_NAME,
.probe = beiscsi_dev_probe,
.remove = beiscsi_remove,
- .id_table = beiscsi_pci_id_table
+ .id_table = beiscsi_pci_id_table,
+ .err_handler = &beiscsi_eeh_handlers
};
-
static int __init beiscsi_module_init(void)
{
int ret;
@@ -4424,19 +5832,17 @@ static int __init beiscsi_module_init(void)
beiscsi_scsi_transport =
iscsi_register_transport(&beiscsi_iscsi_transport);
if (!beiscsi_scsi_transport) {
- SE_DEBUG(DBG_LVL_1,
- "beiscsi_module_init - Unable to register beiscsi"
- "transport.\n");
+ printk(KERN_ERR
+ "beiscsi_module_init - Unable to register beiscsi transport.\n");
return -ENOMEM;
}
- SE_DEBUG(DBG_LVL_8, "In beiscsi_module_init, tt=%p\n",
- &beiscsi_iscsi_transport);
+ printk(KERN_INFO "In beiscsi_module_init, tt=%p\n",
+ &beiscsi_iscsi_transport);
ret = pci_register_driver(&beiscsi_pci_driver);
if (ret) {
- SE_DEBUG(DBG_LVL_1,
- "beiscsi_module_init - Unable to register"
- "beiscsi pci driver.\n");
+ printk(KERN_ERR
+ "beiscsi_module_init - Unable to register beiscsi pci driver.\n");
goto unregister_iscsi_transport;
}
return 0;
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 081c171a1ed6..71c95d144560 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -1,20 +1,10 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
- *
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2017 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
*
* Contact Information:
- * linux-drivers@emulex.com
- *
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
+ * linux-drivers@broadcom.com
*/
#ifndef _BEISCSI_MAIN_
@@ -24,6 +14,8 @@
#include <linux/pci.h>
#include <linux/if_ether.h>
#include <linux/in.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -32,14 +24,14 @@
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
-#include "be.h"
#define DRV_NAME "be2iscsi"
-#define BUILD_STR "2.103.298.0"
-#define BE_NAME "ServerEngines BladeEngine2" \
- "Linux iSCSI Driver version" BUILD_STR
+#define BUILD_STR "11.4.0.1"
+#define BE_NAME "Emulex OneConnect" \
+ "Open-iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
#define BE_VENDOR_ID 0x19A2
+#define ELX_VENDOR_ID 0x10DF
/* DEVICE ID's for BE2 */
#define BE_DEVICE_ID1 0x212
#define OC_DEVICE_ID1 0x702
@@ -49,34 +41,37 @@
#define BE_DEVICE_ID2 0x222
#define OC_DEVICE_ID3 0x712
+/* DEVICE ID for SKH */
+#define OC_SKH_ID1 0x722
+
#define BE2_IO_DEPTH 1024
#define BE2_MAX_SESSIONS 256
-#define BE2_CMDS_PER_CXN 128
#define BE2_TMFS 16
#define BE2_NOPOUT_REQ 16
#define BE2_SGE 32
#define BE2_DEFPDU_HDR_SZ 64
#define BE2_DEFPDU_DATA_SZ 8192
+#define BE2_MAX_NUM_CQ_PROC 512
+
+#define MAX_CPUS 64U
+#define BEISCSI_MAX_NUM_CPUS 7
+
+#define BEISCSI_VER_STRLEN 32
-#define MAX_CPUS 31
#define BEISCSI_SGLIST_ELEMENTS 30
+/**
+ * BE_INVLDT_CMD_TBL_SZ is 128 which is total number commands that can
+ * be invalidated at a time, consider it before changing the value of
+ * BEISCSI_CMD_PER_LUN.
+ */
#define BEISCSI_CMD_PER_LUN 128 /* scsi_host->cmd_per_lun */
-#define BEISCSI_MAX_SECTORS 2048 /* scsi_host->max_sectors */
+#define BEISCSI_MAX_SECTORS 1024 /* scsi_host->max_sectors */
+#define BEISCSI_TEMPLATE_HDR_PER_CXN_SIZE 128 /* Template size per cxn */
#define BEISCSI_MAX_CMD_LEN 16 /* scsi_host->max_cmd_len */
#define BEISCSI_NUM_MAX_LUN 256 /* scsi_host->max_lun */
-#define BEISCSI_NUM_DEVICES_SUPPORTED 0x01
#define BEISCSI_MAX_FRAGS_INIT 192
-#define BE_NUM_MSIX_ENTRIES 1
-
-#define MPU_EP_CONTROL 0
-#define MPU_EP_SEMAPHORE 0xac
-#define BE2_SOFT_RESET 0x5c
-#define BE2_PCI_ONLINE0 0xb0
-#define BE2_PCI_ONLINE1 0xb4
-#define BE2_SET_RESET 0x80
-#define BE2_MPU_IRAM_ONLINE 0x00000080
#define BE_SENSE_INFO_SIZE 258
#define BE_ISCSI_PDU_HEADER_SIZE 64
@@ -84,32 +79,13 @@
#define MAX_CMD_SZ 65536
#define IIOC_SCSI_DATA 0x05 /* Write Operation */
-#define DBG_LVL 0x00000001
-#define DBG_LVL_1 0x00000001
-#define DBG_LVL_2 0x00000002
-#define DBG_LVL_3 0x00000004
-#define DBG_LVL_4 0x00000008
-#define DBG_LVL_5 0x00000010
-#define DBG_LVL_6 0x00000020
-#define DBG_LVL_7 0x00000040
-#define DBG_LVL_8 0x00000080
-
-#define SE_DEBUG(debug_mask, fmt, args...) \
-do { \
- if (debug_mask & DBG_LVL) { \
- printk(KERN_ERR "(%s():%d):", __func__, __LINE__);\
- printk(fmt, ##args); \
- } \
-} while (0);
-
-#define BE_ADAPTER_UP 0x00000000
-#define BE_ADAPTER_LINK_DOWN 0x00000001
/**
* hardware needs the async PDU buffers to be posted in multiples of 8
* So have atleast 8 of them by default
*/
-#define HWI_GET_ASYNC_PDU_CTX(phwi) (phwi->phwi_ctxt->pasync_ctx)
+#define HWI_GET_ASYNC_PDU_CTX(phwi, ulp_num) \
+ (phwi->phwi_ctxt->pasync_ctx[ulp_num])
/********* Memory BAR register ************/
#define PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET 0xfc
@@ -135,11 +111,15 @@ do { \
#define DB_RXULP0_OFFSET 0xA0
/********* Event Q door bell *************/
#define DB_EQ_OFFSET DB_CQ_OFFSET
-#define DB_EQ_RING_ID_MASK 0x1FF /* bits 0 - 8 */
+#define DB_EQ_RING_ID_LOW_MASK 0x1FF /* bits 0 - 8 */
/* Clear the interrupt for this eq */
#define DB_EQ_CLR_SHIFT (9) /* bit 9 */
/* Must be 1 */
#define DB_EQ_EVNT_SHIFT (10) /* bit 10 */
+/* Higher Order EQ_ID bit */
+#define DB_EQ_RING_ID_HIGH_MASK 0x1F /* bits 11 - 15 */
+#define DB_EQ_HIGH_SET_SHIFT 11
+#define DB_EQ_HIGH_FEILD_SHIFT 9
/* Number of event entries processed */
#define DB_EQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */
/* Rearm bit */
@@ -147,34 +127,51 @@ do { \
/********* Compl Q door bell *************/
#define DB_CQ_OFFSET 0x120
-#define DB_CQ_RING_ID_MASK 0x3FF /* bits 0 - 9 */
+#define DB_CQ_RING_ID_LOW_MASK 0x3FF /* bits 0 - 9 */
+/* Higher Order CQ_ID bit */
+#define DB_CQ_RING_ID_HIGH_MASK 0x1F /* bits 11 - 15 */
+#define DB_CQ_HIGH_SET_SHIFT 11
+#define DB_CQ_HIGH_FEILD_SHIFT 10
+
/* Number of event entries processed */
#define DB_CQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */
/* Rearm bit */
#define DB_CQ_REARM_SHIFT (29) /* bit 29 */
#define GET_HWI_CONTROLLER_WS(pc) (pc->phwi_ctrlr)
-#define HWI_GET_DEF_BUFQ_ID(pc) (((struct hwi_controller *)\
- (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_data.id)
-#define HWI_GET_DEF_HDRQ_ID(pc) (((struct hwi_controller *)\
- (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_hdr.id)
+#define HWI_GET_DEF_BUFQ_ID(pc, ulp_num) (((struct hwi_controller *)\
+ (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_data[ulp_num].id)
+#define HWI_GET_DEF_HDRQ_ID(pc, ulp_num) (((struct hwi_controller *)\
+ (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_hdr[ulp_num].id)
#define PAGES_REQUIRED(x) \
((x < PAGE_SIZE) ? 1 : ((x + PAGE_SIZE - 1) / PAGE_SIZE))
+#define MEM_DESCR_OFFSET 8
+#define BEISCSI_DEFQ_HDR 1
+#define BEISCSI_DEFQ_DATA 0
enum be_mem_enum {
HWI_MEM_ADDN_CONTEXT,
HWI_MEM_WRB,
HWI_MEM_WRBH,
HWI_MEM_SGLH,
HWI_MEM_SGE,
- HWI_MEM_ASYNC_HEADER_BUF, /* 5 */
- HWI_MEM_ASYNC_DATA_BUF,
- HWI_MEM_ASYNC_HEADER_RING,
- HWI_MEM_ASYNC_DATA_RING,
- HWI_MEM_ASYNC_HEADER_HANDLE,
- HWI_MEM_ASYNC_DATA_HANDLE, /* 10 */
- HWI_MEM_ASYNC_PDU_CONTEXT,
+ HWI_MEM_TEMPLATE_HDR_ULP0,
+ HWI_MEM_ASYNC_HEADER_BUF_ULP0, /* 6 */
+ HWI_MEM_ASYNC_DATA_BUF_ULP0,
+ HWI_MEM_ASYNC_HEADER_RING_ULP0,
+ HWI_MEM_ASYNC_DATA_RING_ULP0,
+ HWI_MEM_ASYNC_HEADER_HANDLE_ULP0,
+ HWI_MEM_ASYNC_DATA_HANDLE_ULP0, /* 11 */
+ HWI_MEM_ASYNC_PDU_CONTEXT_ULP0,
+ HWI_MEM_TEMPLATE_HDR_ULP1,
+ HWI_MEM_ASYNC_HEADER_BUF_ULP1, /* 14 */
+ HWI_MEM_ASYNC_DATA_BUF_ULP1,
+ HWI_MEM_ASYNC_HEADER_RING_ULP1,
+ HWI_MEM_ASYNC_DATA_RING_ULP1,
+ HWI_MEM_ASYNC_HEADER_HANDLE_ULP1,
+ HWI_MEM_ASYNC_DATA_HANDLE_ULP1, /* 19 */
+ HWI_MEM_ASYNC_PDU_CONTEXT_ULP1,
ISCSI_MEM_GLOBAL_HEADER,
SE_MEM_MAX
};
@@ -202,13 +199,8 @@ struct mem_array {
};
struct be_mem_descriptor {
- unsigned int index; /* Index of this memory parameter */
- unsigned int category; /* type indicates cached/non-cached */
- unsigned int num_elements; /* number of elements in this
- * descriptor
- */
- unsigned int alignment_mask; /* Alignment mask for this block */
unsigned int size_in_bytes; /* Size required by memory block */
+ unsigned int num_elements;
struct mem_array *mem_array;
};
@@ -223,7 +215,6 @@ struct sgl_handle {
struct hba_parameters {
unsigned int ios_per_ctrl;
unsigned int cxns_per_ctrl;
- unsigned int asyncpdus_per_ctrl;
unsigned int icds_per_ctrl;
unsigned int num_sge_per_io;
unsigned int defpdu_hdr_sz;
@@ -231,43 +222,48 @@ struct hba_parameters {
unsigned int num_cq_entries;
unsigned int num_eq_entries;
unsigned int wrbs_per_cxn;
- unsigned int crashmode;
- unsigned int hba_num;
-
- unsigned int mgmt_ws_sz;
unsigned int hwi_ws_sz;
+};
- unsigned int eto;
- unsigned int ldto;
-
- unsigned int dbg_flags;
- unsigned int num_cxn;
-
- unsigned int eq_timer;
- /**
- * These are calculated from other params. They're here
- * for debug purposes
- */
- unsigned int num_mcc_pages;
- unsigned int num_mcc_cq_pages;
- unsigned int num_cq_pages;
- unsigned int num_eq_pages;
-
- unsigned int num_async_pdu_buf_pages;
- unsigned int num_async_pdu_buf_sgl_pages;
- unsigned int num_async_pdu_buf_cq_pages;
-
- unsigned int num_async_pdu_hdr_pages;
- unsigned int num_async_pdu_hdr_sgl_pages;
- unsigned int num_async_pdu_hdr_cq_pages;
+#define BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, cri) \
+ (phwi_ctrlr->wrb_context[cri].ulp_num)
+struct hwi_wrb_context {
+ spinlock_t wrb_lock;
+ struct wrb_handle **pwrb_handle_base;
+ struct wrb_handle **pwrb_handle_basestd;
+ struct iscsi_wrb *plast_wrb;
+ unsigned short alloc_index;
+ unsigned short free_index;
+ unsigned short wrb_handles_available;
+ unsigned short cid;
+ uint8_t ulp_num; /* ULP to which CID binded */
+ uint32_t doorbell_offset;
+};
- unsigned int num_sge;
+struct ulp_cid_info {
+ unsigned short *cid_array;
+ unsigned short avlbl_cids;
+ unsigned short cid_alloc;
+ unsigned short cid_free;
};
-struct invalidate_command_table {
- unsigned short icd;
- unsigned short cid;
-} __packed;
+#include "be.h"
+#define chip_be2(phba) (phba->generation == BE_GEN2)
+#define chip_be3_r(phba) (phba->generation == BE_GEN3)
+#define is_chip_be2_be3r(phba) (chip_be3_r(phba) || (chip_be2(phba)))
+
+#define BEISCSI_ULP0 0
+#define BEISCSI_ULP1 1
+#define BEISCSI_ULP_COUNT 2
+#define BEISCSI_ULP0_LOADED 0x01
+#define BEISCSI_ULP1_LOADED 0x02
+
+#define BEISCSI_ULP_AVLBL_CID(phba, ulp_num) \
+ (((struct ulp_cid_info *)phba->cid_array_info[ulp_num])->avlbl_cids)
+#define BEISCSI_ULP0_AVLBL_CID(phba) \
+ BEISCSI_ULP_AVLBL_CID(phba, BEISCSI_ULP0)
+#define BEISCSI_ULP1_AVLBL_CID(phba) \
+ BEISCSI_ULP_AVLBL_CID(phba, BEISCSI_ULP1)
struct beiscsi_hba {
struct hba_parameters params;
@@ -277,24 +273,17 @@ struct beiscsi_hba {
u8 __iomem *csr_va; /* CSR */
u8 __iomem *db_va; /* Door Bell */
u8 __iomem *pci_va; /* PCI Config */
- struct be_bus_address csr_pa; /* CSR */
- struct be_bus_address db_pa; /* CSR */
- struct be_bus_address pci_pa; /* CSR */
/* PCI representation of our HBA */
struct pci_dev *pcidev;
- unsigned int state;
- unsigned short asic_revision;
unsigned int num_cpus;
unsigned int nxt_cqid;
- struct msix_entry msix_entries[MAX_CPUS + 1];
- bool msix_enabled;
+ char *msi_name[MAX_CPUS];
struct be_mem_descriptor *init_mem;
unsigned short io_sgl_alloc_index;
unsigned short io_sgl_free_index;
unsigned short io_sgl_hndl_avbl;
struct sgl_handle **io_sgl_hndl_base;
- struct sgl_handle **sgl_hndl_array;
unsigned short eh_sgl_alloc_index;
unsigned short eh_sgl_free_index;
@@ -302,54 +291,113 @@ struct beiscsi_hba {
struct sgl_handle **eh_sgl_hndl_base;
spinlock_t io_sgl_lock;
spinlock_t mgmt_sgl_lock;
- spinlock_t isr_lock;
- unsigned int age;
- unsigned short avlbl_cids;
- unsigned short cid_alloc;
- unsigned short cid_free;
- struct beiscsi_conn *conn_table[BE2_MAX_SESSIONS * 2];
+ spinlock_t async_pdu_lock;
struct list_head hba_queue;
- unsigned short *cid_array;
+#define BE_MAX_SESSION 2048
+#define BE_INVALID_CID 0xffff
+#define BE_SET_CID_TO_CRI(cri_index, cid) \
+ (phba->cid_to_cri_map[cid] = cri_index)
+#define BE_GET_CRI_FROM_CID(cid) (phba->cid_to_cri_map[cid])
+ unsigned short cid_to_cri_map[BE_MAX_SESSION];
+ struct ulp_cid_info *cid_array_info[BEISCSI_ULP_COUNT];
struct iscsi_endpoint **ep_array;
- struct iscsi_boot_kset *boot_kset;
+ struct beiscsi_conn **conn_table;
struct Scsi_Host *shost;
+ struct iscsi_iface *ipv4_iface;
+ struct iscsi_iface *ipv6_iface;
struct {
/**
* group together since they are used most frequently
* for cid to cri conversion
*/
- unsigned int iscsi_cid_start;
+#define BEISCSI_PHYS_PORT_MAX 4
unsigned int phys_port;
+ /* valid values of phys_port id are 0, 1, 2, 3 */
+ unsigned int eqid_count;
+ unsigned int cqid_count;
+ unsigned int iscsi_cid_start[BEISCSI_ULP_COUNT];
+#define BEISCSI_GET_CID_COUNT(phba, ulp_num) \
+ (phba->fw_config.iscsi_cid_count[ulp_num])
+ unsigned int iscsi_cid_count[BEISCSI_ULP_COUNT];
+ unsigned int iscsi_icd_count[BEISCSI_ULP_COUNT];
+ unsigned int iscsi_icd_start[BEISCSI_ULP_COUNT];
+ unsigned int iscsi_chain_start[BEISCSI_ULP_COUNT];
+ unsigned int iscsi_chain_count[BEISCSI_ULP_COUNT];
- unsigned int isr_offset;
- unsigned int iscsi_icd_start;
- unsigned int iscsi_cid_count;
- unsigned int iscsi_icd_count;
- unsigned int pci_function;
-
- unsigned short cid_alloc;
- unsigned short cid_free;
- unsigned short avlbl_cids;
unsigned short iscsi_features;
- spinlock_t cid_lock;
+ uint16_t dual_ulp_aware;
+ unsigned long ulp_supported;
} fw_config;
+ unsigned long state;
+#define BEISCSI_HBA_ONLINE 0
+#define BEISCSI_HBA_LINK_UP 1
+#define BEISCSI_HBA_BOOT_FOUND 2
+#define BEISCSI_HBA_BOOT_WORK 3
+#define BEISCSI_HBA_UER_SUPP 4
+#define BEISCSI_HBA_PCI_ERR 5
+#define BEISCSI_HBA_FW_TIMEOUT 6
+#define BEISCSI_HBA_IN_UE 7
+#define BEISCSI_HBA_IN_TPE 8
+
+/* error bits */
+#define BEISCSI_HBA_IN_ERR ((1 << BEISCSI_HBA_PCI_ERR) | \
+ (1 << BEISCSI_HBA_FW_TIMEOUT) | \
+ (1 << BEISCSI_HBA_IN_UE) | \
+ (1 << BEISCSI_HBA_IN_TPE))
+
+ u8 optic_state;
+ struct delayed_work eqd_update;
+ /* update EQ delay timer every 1000ms */
+#define BEISCSI_EQD_UPDATE_INTERVAL 1000
+ struct timer_list hw_check;
+ /* check for UE every 1000ms */
+#define BEISCSI_UE_DETECT_INTERVAL 1000
+ u32 ue2rp;
+ struct delayed_work recover_port;
+ struct work_struct sess_work;
+
+ bool mac_addr_set;
u8 mac_address[ETH_ALEN];
- unsigned short todo_cq;
- unsigned short todo_mcc_cq;
- char wq_name[20];
+ u8 port_name;
+ u8 port_speed;
+ char fw_ver_str[BEISCSI_VER_STRLEN];
struct workqueue_struct *wq; /* The actuak work queue */
- struct work_struct work_cqs; /* The work being queued */
struct be_ctrl_info ctrl;
unsigned int generation;
- unsigned int read_mac_address;
- struct mgmt_session_info boot_sess;
- struct invalidate_command_table inv_tbl[128];
-
+ unsigned int interface_handle;
+
+ struct be_aic_obj aic_obj[MAX_CPUS];
+ unsigned int attr_log_enable;
+ int (*iotask_fn)(struct iscsi_task *,
+ struct scatterlist *sg,
+ uint32_t num_sg, uint32_t xferlen,
+ uint32_t writedir);
+ struct boot_struct {
+ int retry;
+ unsigned int tag;
+ unsigned int s_handle;
+ struct be_dma_mem nonemb_cmd;
+ enum {
+ BEISCSI_BOOT_REOPEN_SESS = 1,
+ BEISCSI_BOOT_GET_SHANDLE,
+ BEISCSI_BOOT_GET_SINFO,
+ BEISCSI_BOOT_LOGOUT_SESS,
+ BEISCSI_BOOT_CREATE_KSET,
+ } action;
+ struct mgmt_session_info boot_sess;
+ struct iscsi_boot_kset *boot_kset;
+ } boot_struct;
+ struct work_struct boot_work;
};
+#define beiscsi_hba_in_error(phba) ((phba)->state & BEISCSI_HBA_IN_ERR)
+#define beiscsi_hba_is_online(phba) \
+ (!beiscsi_hba_in_error((phba)) && \
+ test_bit(BEISCSI_HBA_ONLINE, &phba->state))
+
struct beiscsi_session {
- struct pci_pool *bhs_pool;
+ struct dma_pool *bhs_pool;
};
/**
@@ -359,6 +407,7 @@ struct beiscsi_conn {
struct iscsi_conn *conn;
struct beiscsi_hba *phba;
u32 exp_statsn;
+ u32 doorbell_offset;
u32 beiscsi_conn_cid;
struct beiscsi_endpoint *ep;
unsigned short login_in_progress;
@@ -397,7 +446,7 @@ struct amap_pdu_data_out {
};
struct be_cmd_bhs {
- struct iscsi_cmd iscsi_hdr;
+ struct iscsi_scsi_req iscsi_hdr;
unsigned char pad1[16];
struct pdu_data_out iscsi_data_pdu;
unsigned char pad2[BE_SENSE_INFO_SIZE -
@@ -409,14 +458,15 @@ struct beiscsi_io_task {
struct sgl_handle *psgl_handle;
struct beiscsi_conn *conn;
struct scsi_cmnd *scsi_cmnd;
- unsigned int cmd_sn;
- unsigned int flags;
- unsigned short cid;
- unsigned short header_len;
+ int num_sg;
+ struct hwi_wrb_context *pwrb_context;
itt_t libiscsi_itt;
struct be_cmd_bhs *cmd_bhs;
struct be_bus_address bhs_pa;
unsigned short bhs_len;
+ dma_addr_t mtask_addr;
+ uint32_t mtask_data_count;
+ uint8_t wrb_type;
};
struct be_nonio_bhs {
@@ -428,7 +478,7 @@ struct be_nonio_bhs {
};
struct be_status_bhs {
- struct iscsi_cmd iscsi_hdr;
+ struct iscsi_scsi_req iscsi_hdr;
unsigned char pad1[16];
/**
* The plus 2 below is to hold the sense info length that gets
@@ -456,7 +506,7 @@ struct amap_iscsi_sge {
};
struct beiscsi_offload_params {
- u32 dw[5];
+ u32 dw[6];
};
#define OFFLD_PARAMS_ERL 0x00000003
@@ -464,6 +514,9 @@ struct beiscsi_offload_params {
#define OFFLD_PARAMS_HDE 0x00000008
#define OFFLD_PARAMS_IR2T 0x00000010
#define OFFLD_PARAMS_IMD 0x00000020
+#define OFFLD_PARAMS_DATA_SEQ_INORDER 0x00000040
+#define OFFLD_PARAMS_PDU_SEQ_INORDER 0x00000080
+#define OFFLD_PARAMS_MAX_R2T 0x00FFFF00
/**
* Pseudo amap definition in which each bit of the actual structure is defined
@@ -478,84 +531,87 @@ struct amap_beiscsi_offload_params {
u8 hde[1];
u8 ir2t[1];
u8 imd[1];
- u8 pad[26];
+ u8 data_seq_inorder[1];
+ u8 pdu_seq_inorder[1];
+ u8 max_r2t[16];
+ u8 pad[8];
u8 exp_statsn[32];
+ u8 max_recv_data_segment_length[32];
};
-/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
- struct beiscsi_hba *phba, struct sol_cqe *psol);*/
-
-struct async_pdu_handle {
+struct hd_async_handle {
struct list_head link;
struct be_bus_address pa;
void *pbuffer;
- unsigned int consumed;
- unsigned char index;
- unsigned char is_header;
- unsigned short cri;
- unsigned long buffer_len;
+ u32 buffer_len;
+ u16 index;
+ u16 cri;
+ u8 is_header;
+ u8 is_final;
+ u8 in_use;
};
-struct hwi_async_entry {
- struct {
- unsigned char hdr_received;
- unsigned char hdr_len;
- unsigned short bytes_received;
+#define BEISCSI_ASYNC_HDQ_SIZE(phba, ulp) \
+ (BEISCSI_GET_CID_COUNT((phba), (ulp)) * 2)
+
+/**
+ * This has list of async PDUs that are waiting to be processed.
+ * Buffers live in this list for a brief duration before they get
+ * processed and posted back to hardware.
+ * Note that we don't really need one cri_wait_queue per async_entry.
+ * We need one cri_wait_queue per CRI. Its easier to manage if this
+ * is tagged along with the async_entry.
+ */
+struct hd_async_entry {
+ struct cri_wait_queue {
+ unsigned short hdr_len;
+ unsigned int bytes_received;
unsigned int bytes_needed;
struct list_head list;
- } wait_queue;
-
- struct list_head header_busy_list;
- struct list_head data_busy_list;
+ } wq;
+ /* handles posted to FW resides here */
+ struct hd_async_handle *header;
+ struct hd_async_handle *data;
};
-struct hwi_async_pdu_context {
- struct {
- struct be_bus_address pa_base;
- void *va_base;
- void *ring_base;
- struct async_pdu_handle *handle_base;
-
- unsigned int host_write_ptr;
- unsigned int ep_read_ptr;
- unsigned int writables;
-
- unsigned int free_entries;
- unsigned int busy_entries;
- unsigned int buffer_size;
- unsigned int num_entries;
-
- struct list_head free_list;
- } async_header;
-
- struct {
- struct be_bus_address pa_base;
- void *va_base;
- void *ring_base;
- struct async_pdu_handle *handle_base;
-
- unsigned int host_write_ptr;
- unsigned int ep_read_ptr;
- unsigned int writables;
-
- unsigned int free_entries;
- unsigned int busy_entries;
- unsigned int buffer_size;
- struct list_head free_list;
- unsigned int num_entries;
- } async_data;
+struct hd_async_buf_context {
+ struct be_bus_address pa_base;
+ void *va_base;
+ void *ring_base;
+ struct hd_async_handle *handle_base;
+ u32 buffer_size;
+ u16 pi;
+};
+/**
+ * hd_async_context is declared for each ULP supporting iSCSI function.
+ */
+struct hd_async_context {
+ struct hd_async_buf_context async_header;
+ struct hd_async_buf_context async_data;
+ u16 num_entries;
/**
- * This is a varying size list! Do not add anything
- * after this entry!!
+ * When unsol PDU is in, it needs to be chained till all the bytes are
+ * received and then processing is done. hd_async_entry is created
+ * based on the cid_count for each ULP. When unsol PDU comes in based
+ * on the conn_id it needs to be added to the correct async_entry wq.
+ * Below defined cid_to_async_cri_map is used to reterive the
+ * async_cri_map for a particular connection.
+ *
+ * This array is initialized after beiscsi_create_wrb_rings returns.
+ *
+ * - this method takes more memory space, fixed to 2K
+ * - any support for connections greater than this the array size needs
+ * to be incremented
*/
- struct hwi_async_entry async_entry[BE2_MAX_SESSIONS * 2];
+#define BE_GET_ASYNC_CRI_FROM_CID(cid) (pasync_ctx->cid_to_async_cri_map[cid])
+ unsigned short cid_to_async_cri_map[BE_MAX_SESSION];
+ /**
+ * This is a variable size array. Don`t add anything after this field!!
+ */
+ struct hd_async_entry *async_entry;
};
-#define PDUCQE_CODE_MASK 0x0000003F
-#define PDUCQE_DPL_MASK 0xFFFF0000
-#define PDUCQE_INDEX_MASK 0x0000FFFF
-
struct i_t_dpdu_cqe {
u32 dw[4];
} __packed;
@@ -577,6 +633,20 @@ struct amap_i_t_dpdu_cqe {
u8 valid;
} __packed;
+struct amap_i_t_dpdu_cqe_v2 {
+ u8 db_addr_hi[32]; /* DWORD 0 */
+ u8 db_addr_lo[32]; /* DWORD 1 */
+ u8 code[6]; /* DWORD 2 */
+ u8 num_cons; /* DWORD 2*/
+ u8 rsvd0[8]; /* DWORD 2 */
+ u8 dpl[17]; /* DWORD 2 */
+ u8 index[16]; /* DWORD 3 */
+ u8 cid[13]; /* DWORD 3 */
+ u8 rsvd1; /* DWORD 3 */
+ u8 final; /* DWORD 3 */
+ u8 valid; /* DWORD 3 */
+} __packed;
+
#define CQE_VALID_MASK 0x80000000
#define CQE_CODE_MASK 0x0000003F
#define CQE_CID_MASK 0x0000FFC0
@@ -625,6 +695,11 @@ struct iscsi_wrb {
} __packed;
#define WRB_TYPE_MASK 0xF0000000
+#define SKH_WRB_TYPE_OFFSET 27
+#define BE_WRB_TYPE_OFFSET 28
+
+#define ADAPTER_SET_WRB_TYPE(pwrb, wrb_type, type_offset) \
+ (pwrb->dw[0] |= (wrb_type << type_offset))
/**
* Pseudo amap definition in which each bit of the actual structure is defined
@@ -671,11 +746,60 @@ struct amap_iscsi_wrb {
} __packed;
-struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid);
+struct amap_iscsi_wrb_v2 {
+ u8 r2t_exp_dtl[25]; /* DWORD 0 */
+ u8 rsvd0[2]; /* DWORD 0*/
+ u8 type[5]; /* DWORD 0 */
+ u8 ptr2nextwrb[8]; /* DWORD 1 */
+ u8 wrb_idx[8]; /* DWORD 1 */
+ u8 lun[16]; /* DWORD 1 */
+ u8 sgl_idx[16]; /* DWORD 2 */
+ u8 ref_sgl_icd_idx[16]; /* DWORD 2 */
+ u8 exp_data_sn[32]; /* DWORD 3 */
+ u8 iscsi_bhs_addr_hi[32]; /* DWORD 4 */
+ u8 iscsi_bhs_addr_lo[32]; /* DWORD 5 */
+ u8 cq_id[16]; /* DWORD 6 */
+ u8 rsvd1[16]; /* DWORD 6 */
+ u8 cmdsn_itt[32]; /* DWORD 7 */
+ u8 sge0_addr_hi[32]; /* DWORD 8 */
+ u8 sge0_addr_lo[32]; /* DWORD 9 */
+ u8 sge0_offset[24]; /* DWORD 10 */
+ u8 rsvd2[7]; /* DWORD 10 */
+ u8 sge0_last; /* DWORD 10 */
+ u8 sge0_len[17]; /* DWORD 11 */
+ u8 rsvd3[7]; /* DWORD 11 */
+ u8 diff_enbl; /* DWORD 11 */
+ u8 u_run; /* DWORD 11 */
+ u8 o_run; /* DWORD 11 */
+ u8 invld; /* DWORD 11 */
+ u8 dsp; /* DWORD 11 */
+ u8 dmsg; /* DWORD 11 */
+ u8 rsvd4; /* DWORD 11 */
+ u8 lt; /* DWORD 11 */
+ u8 sge1_addr_hi[32]; /* DWORD 12 */
+ u8 sge1_addr_lo[32]; /* DWORD 13 */
+ u8 sge1_r2t_offset[24]; /* DWORD 14 */
+ u8 rsvd5[7]; /* DWORD 14 */
+ u8 sge1_last; /* DWORD 14 */
+ u8 sge1_len[17]; /* DWORD 15 */
+ u8 rsvd6[15]; /* DWORD 15 */
+} __packed;
+
+
+struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
+ struct hwi_wrb_context **pcontext);
void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle);
-void beiscsi_process_all_cqs(struct work_struct *work);
+void beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn,
+ struct iscsi_task *task);
+
+void hwi_ring_cq_db(struct beiscsi_hba *phba,
+ unsigned int id, unsigned int num_processed,
+ unsigned char rearm);
+
+unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget);
+void beiscsi_process_mcc_cq(struct beiscsi_hba *phba);
struct pdu_nop_out {
u32 dw[12];
@@ -736,6 +860,7 @@ struct iscsi_target_context_update_wrb {
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
+#define BE_TGT_CTX_UPDT_CMD 0x07
struct amap_iscsi_target_context_update_wrb {
u8 lun[14]; /* DWORD 0 */
u8 lt; /* DWORD 0 */
@@ -781,6 +906,49 @@ struct amap_iscsi_target_context_update_wrb {
} __packed;
+#define BEISCSI_MAX_RECV_DATASEG_LEN (64 * 1024)
+#define BEISCSI_MAX_CXNS 1
+struct amap_iscsi_target_context_update_wrb_v2 {
+ u8 max_burst_length[24]; /* DWORD 0 */
+ u8 rsvd0[3]; /* DWORD 0 */
+ u8 type[5]; /* DWORD 0 */
+ u8 ptr2nextwrb[8]; /* DWORD 1 */
+ u8 wrb_idx[8]; /* DWORD 1 */
+ u8 rsvd1[16]; /* DWORD 1 */
+ u8 max_send_data_segment_length[24]; /* DWORD 2 */
+ u8 rsvd2[8]; /* DWORD 2 */
+ u8 first_burst_length[24]; /* DWORD 3 */
+ u8 rsvd3[8]; /* DOWRD 3 */
+ u8 max_r2t[16]; /* DWORD 4 */
+ u8 rsvd4; /* DWORD 4 */
+ u8 hde; /* DWORD 4 */
+ u8 dde; /* DWORD 4 */
+ u8 erl[2]; /* DWORD 4 */
+ u8 rsvd5[6]; /* DWORD 4 */
+ u8 imd; /* DWORD 4 */
+ u8 ir2t; /* DWORD 4 */
+ u8 rsvd6[3]; /* DWORD 4 */
+ u8 stat_sn[32]; /* DWORD 5 */
+ u8 rsvd7[32]; /* DWORD 6 */
+ u8 rsvd8[32]; /* DWORD 7 */
+ u8 max_recv_dataseg_len[24]; /* DWORD 8 */
+ u8 rsvd9[8]; /* DWORD 8 */
+ u8 rsvd10[32]; /* DWORD 9 */
+ u8 rsvd11[32]; /* DWORD 10 */
+ u8 max_cxns[16]; /* DWORD 11 */
+ u8 rsvd12[11]; /* DWORD 11*/
+ u8 invld; /* DWORD 11 */
+ u8 rsvd13;/* DWORD 11*/
+ u8 dmsg; /* DWORD 11 */
+ u8 data_seq_inorder; /* DWORD 11 */
+ u8 pdu_seq_inorder; /* DWORD 11 */
+ u8 rsvd14[32]; /*DWORD 12 */
+ u8 rsvd15[32]; /* DWORD 13 */
+ u8 rsvd16[32]; /* DWORD 14 */
+ u8 rsvd17[32]; /* DWORD 15 */
+} __packed;
+
+
struct be_ring {
u32 pages; /* queue size in pages */
u32 id; /* queue id assigned by beklib */
@@ -788,6 +956,10 @@ struct be_ring {
u32 cidx; /* consumer index */
u32 pidx; /* producer index -- not used by most rings */
u32 item_size; /* size in bytes of one object */
+ u8 ulp_num; /* ULP to which CID binded */
+ u16 register_set;
+ u16 doorbell_format;
+ u32 doorbell_offset;
void *va; /* The virtual address of the ring. This
* should be last to allow 32 & 64 bit debugger
@@ -795,28 +967,10 @@ struct be_ring {
*/
};
-struct hwi_wrb_context {
- struct list_head wrb_handle_list;
- struct list_head wrb_handle_drvr_list;
- struct wrb_handle **pwrb_handle_base;
- struct wrb_handle **pwrb_handle_basestd;
- struct iscsi_wrb *plast_wrb;
- unsigned short alloc_index;
- unsigned short free_index;
- unsigned short wrb_handles_available;
- unsigned short cid;
-};
-
struct hwi_controller {
- struct list_head io_sgl_list;
- struct list_head eh_sgl_list;
- struct sgl_handle *psgl_handle_base;
- unsigned int wrb_mem_index;
-
- struct hwi_wrb_context wrb_context[BE2_MAX_SESSIONS * 2];
- struct mcc_wrb *pmcc_wrb_base;
- struct be_ring default_pdu_hdr;
- struct be_ring default_pdu_data;
+ struct hwi_wrb_context *wrb_context;
+ struct be_ring default_pdu_hdr[BEISCSI_ULP_COUNT];
+ struct be_ring default_pdu_data[BEISCSI_ULP_COUNT];
struct hwi_context_memory *phwi_ctxt;
};
@@ -831,29 +985,45 @@ enum hwh_type_enum {
};
struct wrb_handle {
- enum hwh_type_enum type;
unsigned short wrb_index;
- unsigned short nxt_wrb_index;
-
struct iscsi_task *pio_handle;
struct iscsi_wrb *pwrb;
};
struct hwi_context_memory {
- /* Adaptive interrupt coalescing (AIC) info */
- u16 min_eqd; /* in usecs */
- u16 max_eqd; /* in usecs */
- u16 cur_eqd; /* in usecs */
struct be_eq_obj be_eq[MAX_CPUS];
- struct be_queue_info be_cq[MAX_CPUS];
-
- struct be_queue_info be_def_hdrq;
- struct be_queue_info be_def_dataq;
+ struct be_queue_info be_cq[MAX_CPUS - 1];
- struct be_queue_info be_wrbq[BE2_MAX_SESSIONS];
- struct be_mcc_wrb_context *pbe_mcc_context;
-
- struct hwi_async_pdu_context *pasync_ctx;
+ struct be_queue_info *be_wrbq;
+ /**
+ * Create array of ULP number for below entries as DEFQ
+ * will be created for both ULP if iSCSI Protocol is
+ * loaded on both ULP.
+ */
+ struct be_queue_info be_def_hdrq[BEISCSI_ULP_COUNT];
+ struct be_queue_info be_def_dataq[BEISCSI_ULP_COUNT];
+ struct hd_async_context *pasync_ctx[BEISCSI_ULP_COUNT];
};
+void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle);
+
+/* Logging related definitions */
+#define BEISCSI_LOG_INIT 0x0001 /* Initialization events */
+#define BEISCSI_LOG_MBOX 0x0002 /* Mailbox Events */
+#define BEISCSI_LOG_MISC 0x0004 /* Miscllaneous Events */
+#define BEISCSI_LOG_EH 0x0008 /* Error Handler */
+#define BEISCSI_LOG_IO 0x0010 /* IO Code Path */
+#define BEISCSI_LOG_CONFIG 0x0020 /* CONFIG Code Path */
+#define BEISCSI_LOG_ISCSI 0x0040 /* SCSI/iSCSI Protocol related Logs */
+
+#define __beiscsi_log(phba, level, fmt, arg...) \
+ shost_printk(level, phba->shost, fmt, __LINE__, ##arg)
+
+#define beiscsi_log(phba, level, mask, fmt, arg...) \
+do { \
+ uint32_t log_value = phba->attr_log_enable; \
+ if (((mask) & log_value) || (level[1] <= '3')) \
+ __beiscsi_log(phba, level, fmt, ##arg); \
+} while (0);
+
#endif
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 44762cfa3e12..4e899ec1477d 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1,449 +1,1559 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
+/*
+ * This file is part of the Emulex Linux Device Driver for Enterprise iSCSI
+ * Host Bus Adapters. Refer to the README file included with this package
+ * for driver version and adapter compatibility.
+ *
+ * Copyright (c) 2018 Broadcom. All Rights Reserved.
+ * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as published
+ * by the Free Software Foundation.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * This program is distributed in the hope that it will be useful. ALL EXPRESS
+ * OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+ * OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH
+ * DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
+ * See the GNU General Public License for more details, a copy of which
+ * can be found in the file COPYING included with this package.
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@broadcom.com
*
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
*/
+#include <linux/bsg-lib.h>
+#include <scsi/scsi_transport_iscsi.h>
+#include <scsi/scsi_bsg_iscsi.h>
#include "be_mgmt.h"
#include "be_iscsi.h"
-#include <scsi/scsi_transport_iscsi.h>
+#include "be_main.h"
-unsigned int beiscsi_get_boot_target(struct beiscsi_hba *phba)
+unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba,
+ struct bsg_job *job,
+ struct be_dma_mem *nonemb_cmd)
{
- struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb;
- struct be_cmd_req_get_mac_addr *req;
+ struct be_sge *mcc_sge;
unsigned int tag = 0;
+ struct iscsi_bsg_request *bsg_req = job->request;
+ struct be_bsg_vendor_cmd *req = nonemb_cmd->va;
+ unsigned short region, sector_size, sector, offset;
- SE_DEBUG(DBG_LVL_8, "In bescsi_get_boot_target\n");
- spin_lock(&ctrl->mbox_lock);
- tag = alloc_mcc_tag(phba);
- if (!tag) {
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+ nonemb_cmd->size = job->request_payload.payload_len;
+ memset(nonemb_cmd->va, 0, nonemb_cmd->size);
+ region = bsg_req->rqst_data.h_vendor.vendor_cmd[1];
+ sector_size = bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+ sector = bsg_req->rqst_data.h_vendor.vendor_cmd[3];
+ offset = bsg_req->rqst_data.h_vendor.vendor_cmd[4];
+ req->region = region;
+ req->sector = sector;
+ req->offset = offset;
+
+ if (mutex_lock_interruptible(&ctrl->mbox_lock))
+ return 0;
+ switch (bsg_req->rqst_data.h_vendor.vendor_cmd[0]) {
+ case BEISCSI_WRITE_FLASH:
+ offset = sector * sector_size + offset;
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_WRITE_FLASH, sizeof(*req));
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ nonemb_cmd->va + offset, job->request_len);
+ break;
+ case BEISCSI_READ_FLASH:
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_READ_FLASH, sizeof(*req));
+ break;
+ default:
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : Unsupported cmd = 0x%x\n\n",
+ bsg_req->rqst_data.h_vendor.vendor_cmd[0]);
+
+ mutex_unlock(&ctrl->mbox_lock);
+ return -EPERM;
}
- wrb = wrb_from_mccq(phba);
- req = embedded_payload(wrb);
- wrb->tag0 |= tag;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
- OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET,
- sizeof(*req));
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
+ }
+
+ mcc_sge = nonembedded_sgl(wrb);
+ be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false,
+ job->request_payload.sg_cnt);
+ mcc_sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
+ mcc_sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
+ mcc_sge->len = cpu_to_le32(nonemb_cmd->size);
+
+ be_mcc_notify(phba, tag);
- be_mcc_notify(phba);
- spin_unlock(&ctrl->mbox_lock);
+ mutex_unlock(&ctrl->mbox_lock);
return tag;
}
-unsigned int beiscsi_get_session_info(struct beiscsi_hba *phba,
- u32 boot_session_handle,
- struct be_dma_mem *nonemb_cmd)
+/**
+ * mgmt_open_connection()- Establish a TCP CXN
+ * @phba: driver priv structure
+ * @dst_addr: Destination Address
+ * @beiscsi_ep: ptr to device endpoint struct
+ * @nonemb_cmd: ptr to memory allocated for command
+ *
+ * return
+ * Success: Tag number of the MBX Command issued
+ * Failure: Error code
+ **/
+int mgmt_open_connection(struct beiscsi_hba *phba,
+ struct sockaddr *dst_addr,
+ struct beiscsi_endpoint *beiscsi_ep,
+ struct be_dma_mem *nonemb_cmd)
{
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr;
+ struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr;
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb;
+ struct tcp_connect_and_offload_in_v1 *req;
+ unsigned short def_hdr_id;
+ unsigned short def_data_id;
+ struct phys_addr template_address = { 0, 0 };
+ struct phys_addr *ptemplate_address;
unsigned int tag = 0;
- struct be_cmd_req_get_session *req;
- struct be_cmd_resp_get_session *resp;
+ unsigned int i, ulp_num;
+ unsigned short cid = beiscsi_ep->ep_cid;
struct be_sge *sge;
- SE_DEBUG(DBG_LVL_8, "In beiscsi_get_session_info\n");
- spin_lock(&ctrl->mbox_lock);
- tag = alloc_mcc_tag(phba);
- if (!tag) {
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+ if (dst_addr->sa_family != PF_INET && dst_addr->sa_family != PF_INET6) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BG_%d : unknown addr family %d\n",
+ dst_addr->sa_family);
+ return 0;
}
- nonemb_cmd->size = sizeof(*resp);
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+
+ ulp_num = phwi_ctrlr->wrb_context[BE_GET_CRI_FROM_CID(cid)].ulp_num;
+
+ def_hdr_id = (unsigned short)HWI_GET_DEF_HDRQ_ID(phba, ulp_num);
+ def_data_id = (unsigned short)HWI_GET_DEF_BUFQ_ID(phba, ulp_num);
+
+ ptemplate_address = &template_address;
+ ISCSI_GET_PDU_TEMPLATE_ADDRESS(phba, ptemplate_address);
+ if (mutex_lock_interruptible(&ctrl->mbox_lock))
+ return 0;
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
+ }
+
+ sge = nonembedded_sgl(wrb);
req = nonemb_cmd->va;
memset(req, 0, sizeof(*req));
- wrb = wrb_from_mccq(phba);
- sge = nonembedded_sgl(wrb);
- wrb->tag0 |= tag;
-
- wrb->tag0 |= tag;
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
- OPCODE_ISCSI_INI_SESSION_GET_A_SESSION,
- sizeof(*resp));
- req->session_handle = boot_session_handle;
+ be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false, 1);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD,
+ nonemb_cmd->size);
+ if (dst_addr->sa_family == PF_INET) {
+ __be32 s_addr = daddr_in->sin_addr.s_addr;
+ req->ip_address.ip_type = BEISCSI_IP_TYPE_V4;
+ req->ip_address.addr[0] = s_addr & 0x000000ff;
+ req->ip_address.addr[1] = (s_addr & 0x0000ff00) >> 8;
+ req->ip_address.addr[2] = (s_addr & 0x00ff0000) >> 16;
+ req->ip_address.addr[3] = (s_addr & 0xff000000) >> 24;
+ req->tcp_port = ntohs(daddr_in->sin_port);
+ beiscsi_ep->dst_addr = daddr_in->sin_addr.s_addr;
+ beiscsi_ep->dst_tcpport = ntohs(daddr_in->sin_port);
+ beiscsi_ep->ip_type = BEISCSI_IP_TYPE_V4;
+ } else {
+ /* else its PF_INET6 family */
+ req->ip_address.ip_type = BEISCSI_IP_TYPE_V6;
+ memcpy(&req->ip_address.addr,
+ &daddr_in6->sin6_addr.in6_u.u6_addr8, 16);
+ req->tcp_port = ntohs(daddr_in6->sin6_port);
+ beiscsi_ep->dst_tcpport = ntohs(daddr_in6->sin6_port);
+ memcpy(&beiscsi_ep->dst6_addr,
+ &daddr_in6->sin6_addr.in6_u.u6_addr8, 16);
+ beiscsi_ep->ip_type = BEISCSI_IP_TYPE_V6;
+ }
+ req->cid = cid;
+ i = phba->nxt_cqid++;
+ if (phba->nxt_cqid == phba->num_cpus)
+ phba->nxt_cqid = 0;
+ req->cq_id = phwi_context->be_cq[i].id;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BG_%d : i=%d cq_id=%d\n", i, req->cq_id);
+ req->defq_id = def_hdr_id;
+ req->hdr_ring_id = def_hdr_id;
+ req->data_ring_id = def_data_id;
+ req->do_offload = 1;
+ req->dataout_template_pa.lo = ptemplate_address->lo;
+ req->dataout_template_pa.hi = ptemplate_address->hi;
sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd->size);
- be_mcc_notify(phba);
- spin_unlock(&ctrl->mbox_lock);
+ if (!is_chip_be2_be3r(phba)) {
+ req->hdr.version = MBX_CMD_VER1;
+ req->tcp_window_size = 0x8000;
+ req->tcp_window_scale_count = 2;
+ }
+
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
return tag;
}
-int mgmt_get_fw_config(struct be_ctrl_info *ctrl,
- struct beiscsi_hba *phba)
+/**
+ * beiscsi_exec_nemb_cmd()- execute non-embedded MBX cmd
+ * @phba: driver priv structure
+ * @nonemb_cmd: DMA address of the MBX command to be issued
+ * @cbfn: callback func on MCC completion
+ * @resp_buf: buffer to copy the MBX cmd response
+ * @resp_buf_len: response length to be copied
+ *
+ **/
+static int beiscsi_exec_nemb_cmd(struct beiscsi_hba *phba,
+ struct be_dma_mem *nonemb_cmd,
+ void (*cbfn)(struct beiscsi_hba *,
+ unsigned int),
+ void *resp_buf, u32 resp_buf_len)
{
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
- struct be_fw_cfg *req = embedded_payload(wrb);
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb;
+ struct be_sge *sge;
+ unsigned int tag;
+ int rc = 0;
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return -ENOMEM;
+ }
+
+ sge = nonembedded_sgl(wrb);
+ be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false, 1);
+ sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
+ sge->pa_lo = cpu_to_le32(lower_32_bits(nonemb_cmd->dma));
+ sge->len = cpu_to_le32(nonemb_cmd->size);
+
+ if (cbfn) {
+ struct be_dma_mem *tag_mem;
+
+ set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state);
+ ctrl->ptag_state[tag].cbfn = cbfn;
+ tag_mem = &phba->ctrl.ptag_state[tag].tag_mem_state;
+
+ /* store DMA mem to be freed in callback */
+ tag_mem->size = nonemb_cmd->size;
+ tag_mem->va = nonemb_cmd->va;
+ tag_mem->dma = nonemb_cmd->dma;
+ }
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
+
+ /* with cbfn set, its async cmd, don't wait */
+ if (cbfn)
+ return 0;
+
+ rc = beiscsi_mccq_compl_wait(phba, tag, NULL, nonemb_cmd);
+
+ /* copy the response, if any */
+ if (resp_buf)
+ memcpy(resp_buf, nonemb_cmd->va, resp_buf_len);
+ return rc;
+}
+
+static int beiscsi_prep_nemb_cmd(struct beiscsi_hba *phba,
+ struct be_dma_mem *cmd,
+ u8 subsystem, u8 opcode, u32 size)
+{
+ cmd->va = dma_alloc_coherent(&phba->ctrl.pdev->dev, size, &cmd->dma,
+ GFP_KERNEL);
+ if (!cmd->va) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
+ "BG_%d : Failed to allocate memory for if info\n");
+ return -ENOMEM;
+ }
+ cmd->size = size;
+ be_cmd_hdr_prepare(cmd->va, subsystem, opcode, size);
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BG_%d : subsystem %u cmd %u size %u\n",
+ subsystem, opcode, size);
+ return 0;
+}
+
+static void beiscsi_free_nemb_cmd(struct beiscsi_hba *phba,
+ struct be_dma_mem *cmd, int rc)
+{
+ /*
+ * If FW is busy the DMA buffer is saved with the tag. When the cmd
+ * completes this buffer is freed.
+ */
+ if (rc == -EBUSY)
+ return;
+
+ dma_free_coherent(&phba->ctrl.pdev->dev, cmd->size, cmd->va, cmd->dma);
+}
+
+static void __beiscsi_eq_delay_compl(struct beiscsi_hba *phba, unsigned int tag)
+{
+ struct be_dma_mem *tag_mem;
+
+ /* status is ignored */
+ __beiscsi_mcc_compl_status(phba, tag, NULL, NULL);
+ tag_mem = &phba->ctrl.ptag_state[tag].tag_mem_state;
+ if (tag_mem->size) {
+ dma_free_coherent(&phba->pcidev->dev, tag_mem->size,
+ tag_mem->va, tag_mem->dma);
+ tag_mem->size = 0;
+ }
+}
+
+int beiscsi_modify_eq_delay(struct beiscsi_hba *phba,
+ struct be_set_eqd *set_eqd, int num)
+{
+ struct be_cmd_req_modify_eq_delay *req;
+ struct be_dma_mem nonemb_cmd;
+ int i, rc;
+
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req));
+ if (rc)
+ return rc;
+
+ req = nonemb_cmd.va;
+ req->num_eq = cpu_to_le32(num);
+ for (i = 0; i < num; i++) {
+ req->delay[i].eq_id = cpu_to_le32(set_eqd[i].eq_id);
+ req->delay[i].phase = 0;
+ req->delay[i].delay_multiplier =
+ cpu_to_le32(set_eqd[i].delay_multiplier);
+ }
+
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, __beiscsi_eq_delay_compl,
+ NULL, 0);
+ if (rc) {
+ /*
+ * Only free on failure. Async cmds are handled like -EBUSY
+ * where it's handled for us.
+ */
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ }
+ return rc;
+}
+
+/**
+ * beiscsi_get_initiator_name - read initiator name from flash
+ * @phba: device priv structure
+ * @name: buffer pointer
+ * @cfg: fetch user configured
+ *
+ */
+int beiscsi_get_initiator_name(struct beiscsi_hba *phba, char *name, bool cfg)
+{
+ struct be_dma_mem nonemb_cmd;
+ struct be_cmd_hba_name resp;
+ struct be_cmd_hba_name *req;
+ int rc;
+
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_ISCSI_INI,
+ OPCODE_ISCSI_INI_CFG_GET_HBA_NAME, sizeof(resp));
+ if (rc)
+ return rc;
+
+ req = nonemb_cmd.va;
+ if (cfg)
+ req->hdr.version = 1;
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL,
+ &resp, sizeof(resp));
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ if (rc) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BS_%d : Initiator Name MBX Failed\n");
+ return rc;
+ }
+ rc = sprintf(name, "%s\n", resp.initiator_name);
+ return rc;
+}
+
+unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_get_all_if_id_req *req;
+ struct be_cmd_get_all_if_id_req *pbe_allid;
+ unsigned int tag;
int status = 0;
- spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
+ if (mutex_lock_interruptible(&ctrl->mbox_lock))
+ return -EINTR;
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return -ENOMEM;
+ }
+ req = embedded_payload(wrb);
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_GET_ALL_IF_ID,
+ sizeof(*req));
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
- status = be_mbox_notify(ctrl);
- if (!status) {
- struct be_fw_cfg *pfw_cfg;
- pfw_cfg = req;
- phba->fw_config.phys_port = pfw_cfg->phys_port;
- phba->fw_config.iscsi_icd_start =
- pfw_cfg->ulp[0].icd_base;
- phba->fw_config.iscsi_icd_count =
- pfw_cfg->ulp[0].icd_count;
- phba->fw_config.iscsi_cid_start =
- pfw_cfg->ulp[0].sq_base;
- phba->fw_config.iscsi_cid_count =
- pfw_cfg->ulp[0].sq_count;
- if (phba->fw_config.iscsi_cid_count > (BE2_MAX_SESSIONS / 2)) {
- SE_DEBUG(DBG_LVL_8,
- "FW reported MAX CXNS as %d\t"
- "Max Supported = %d.\n",
- phba->fw_config.iscsi_cid_count,
- BE2_MAX_SESSIONS);
- phba->fw_config.iscsi_cid_count = BE2_MAX_SESSIONS / 2;
- }
- } else {
- shost_printk(KERN_WARNING, phba->shost,
- "Failed in mgmt_get_fw_config\n");
+ status = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL);
+ if (status) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : %s failed: %d\n", __func__, status);
+ return -EBUSY;
}
- spin_unlock(&ctrl->mbox_lock);
+ pbe_allid = embedded_payload(wrb);
+ /* we now support only one interface per function */
+ phba->interface_handle = pbe_allid->if_hndl_list[0];
+
return status;
}
-int mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
- struct beiscsi_hba *phba)
+static inline bool beiscsi_if_zero_ip(u8 *ip, u32 ip_type)
+{
+ u32 len;
+
+ len = (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN;
+ while (len && !ip[len - 1])
+ len--;
+ return (len == 0);
+}
+
+static int beiscsi_if_mod_gw(struct beiscsi_hba *phba,
+ u32 action, u32 ip_type, u8 *gw)
{
+ struct be_cmd_set_def_gateway_req *req;
struct be_dma_mem nonemb_cmd;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
- struct be_mgmt_controller_attributes *req;
- struct be_sge *sge = nonembedded_sgl(wrb);
- int status = 0;
+ int rt_val;
- nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev,
- sizeof(struct be_mgmt_controller_attributes),
- &nonemb_cmd.dma);
- if (nonemb_cmd.va == NULL) {
- SE_DEBUG(DBG_LVL_1,
- "Failed to allocate memory for mgmt_check_supported_fw"
- "\n");
- return -ENOMEM;
+ rt_val = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_MODIFY_DEFAULT_GATEWAY,
+ sizeof(*req));
+ if (rt_val)
+ return rt_val;
+
+ req = nonemb_cmd.va;
+ req->action = action;
+ req->ip_addr.ip_type = ip_type;
+ memcpy(req->ip_addr.addr, gw,
+ (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN);
+ rt_val = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rt_val);
+ return rt_val;
+}
+
+int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw)
+{
+ struct be_cmd_get_def_gateway_resp gw_resp;
+ int rt_val;
+
+ memset(&gw_resp, 0, sizeof(gw_resp));
+ rt_val = beiscsi_if_get_gw(phba, ip_type, &gw_resp);
+ if (rt_val) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : Failed to Get Gateway Addr\n");
+ return rt_val;
+ }
+
+ if (!beiscsi_if_zero_ip(gw_resp.ip_addr.addr, ip_type)) {
+ rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, ip_type,
+ gw_resp.ip_addr.addr);
+ if (rt_val) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : Failed to clear Gateway Addr Set\n");
+ return rt_val;
+ }
}
- nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes);
+
+ rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_ADD, ip_type, gw);
+ if (rt_val)
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : Failed to Set Gateway Addr\n");
+
+ return rt_val;
+}
+
+int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type,
+ struct be_cmd_get_def_gateway_resp *resp)
+{
+ struct be_cmd_get_def_gateway_req *req;
+ struct be_dma_mem nonemb_cmd;
+ int rc;
+
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_GET_DEFAULT_GATEWAY,
+ sizeof(*resp));
+ if (rc)
+ return rc;
+
req = nonemb_cmd.va;
- memset(req, 0, sizeof(*req));
- spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req));
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd.size);
- status = be_mbox_notify(ctrl);
- if (!status) {
- struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va;
- SE_DEBUG(DBG_LVL_8, "Firmware version of CMD: %s\n",
- resp->params.hba_attribs.flashrom_version_string);
- SE_DEBUG(DBG_LVL_8, "Firmware version is : %s\n",
- resp->params.hba_attribs.firmware_version_string);
- SE_DEBUG(DBG_LVL_8,
- "Developer Build, not performing version check...\n");
- phba->fw_config.iscsi_features =
- resp->params.hba_attribs.iscsi_features;
- SE_DEBUG(DBG_LVL_8, " phba->fw_config.iscsi_features = %d\n",
- phba->fw_config.iscsi_features);
- } else
- SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n");
- spin_unlock(&ctrl->mbox_lock);
- if (nonemb_cmd.va)
- pci_free_consistent(ctrl->pdev, nonemb_cmd.size,
- nonemb_cmd.va, nonemb_cmd.dma);
+ req->ip_type = ip_type;
- return status;
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, resp,
+ sizeof(*resp));
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ return rc;
}
-int mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
+static int
+beiscsi_if_clr_ip(struct beiscsi_hba *phba,
+ struct be_cmd_get_if_info_resp *if_info)
+{
+ struct be_cmd_set_ip_addr_req *req;
+ struct be_dma_mem nonemb_cmd;
+ int rc;
+
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR,
+ sizeof(*req));
+ if (rc)
+ return rc;
+
+ req = nonemb_cmd.va;
+ req->ip_params.record_entry_count = 1;
+ req->ip_params.ip_record.action = IP_ACTION_DEL;
+ req->ip_params.ip_record.interface_hndl =
+ phba->interface_handle;
+ req->ip_params.ip_record.ip_addr.size_of_structure =
+ sizeof(struct be_ip_addr_subnet_format);
+ req->ip_params.ip_record.ip_addr.ip_type = if_info->ip_addr.ip_type;
+ memcpy(req->ip_params.ip_record.ip_addr.addr,
+ if_info->ip_addr.addr,
+ sizeof(if_info->ip_addr.addr));
+ memcpy(req->ip_params.ip_record.ip_addr.subnet_mask,
+ if_info->ip_addr.subnet_mask,
+ sizeof(if_info->ip_addr.subnet_mask));
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
+ if (rc < 0 || req->ip_params.ip_record.status) {
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BG_%d : failed to clear IP: rc %d status %d\n",
+ rc, req->ip_params.ip_record.status);
+ }
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ return rc;
+}
+
+static int
+beiscsi_if_set_ip(struct beiscsi_hba *phba, u8 *ip,
+ u8 *subnet, u32 ip_type)
+{
+ struct be_cmd_set_ip_addr_req *req;
+ struct be_dma_mem nonemb_cmd;
+ uint32_t ip_len;
+ int rc;
+
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR,
+ sizeof(*req));
+ if (rc)
+ return rc;
+
+ req = nonemb_cmd.va;
+ req->ip_params.record_entry_count = 1;
+ req->ip_params.ip_record.action = IP_ACTION_ADD;
+ req->ip_params.ip_record.interface_hndl =
+ phba->interface_handle;
+ req->ip_params.ip_record.ip_addr.size_of_structure =
+ sizeof(struct be_ip_addr_subnet_format);
+ req->ip_params.ip_record.ip_addr.ip_type = ip_type;
+ ip_len = (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN;
+ memcpy(req->ip_params.ip_record.ip_addr.addr, ip, ip_len);
+ if (subnet)
+ memcpy(req->ip_params.ip_record.ip_addr.subnet_mask,
+ subnet, ip_len);
+
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
+ /**
+ * In some cases, host needs to look into individual record status
+ * even though FW reported success for that IOCTL.
+ */
+ if (rc < 0 || req->ip_params.ip_record.status) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BG_%d : failed to set IP: rc %d status %d\n",
+ rc, req->ip_params.ip_record.status);
+ if (req->ip_params.ip_record.status)
+ rc = -EINVAL;
+ }
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ return rc;
+}
+
+int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type,
+ u8 *ip, u8 *subnet)
+{
+ struct be_cmd_get_if_info_resp *if_info;
+ struct be_cmd_rel_dhcp_req *reldhcp;
+ struct be_dma_mem nonemb_cmd;
+ int rc;
+
+ rc = beiscsi_if_get_info(phba, ip_type, &if_info);
+ if (rc)
+ return rc;
+
+ if (if_info->dhcp_state) {
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd,
+ CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_REL_STATELESS_IP_ADDR,
+ sizeof(*reldhcp));
+ if (rc)
+ goto exit;
+
+ reldhcp = nonemb_cmd.va;
+ reldhcp->interface_hndl = phba->interface_handle;
+ reldhcp->ip_type = ip_type;
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ if (rc < 0) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : failed to release existing DHCP: %d\n",
+ rc);
+ goto exit;
+ }
+ }
+
+ /* first delete any IP set */
+ if (!beiscsi_if_zero_ip(if_info->ip_addr.addr, ip_type)) {
+ rc = beiscsi_if_clr_ip(phba, if_info);
+ if (rc)
+ goto exit;
+ }
+
+ /* if ip == NULL then this is called just to release DHCP IP */
+ if (ip)
+ rc = beiscsi_if_set_ip(phba, ip, subnet, ip_type);
+exit:
+ kfree(if_info);
+ return rc;
+}
+
+int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type)
+{
+ struct be_cmd_get_def_gateway_resp gw_resp;
+ struct be_cmd_get_if_info_resp *if_info;
+ struct be_cmd_set_dhcp_req *dhcpreq;
+ struct be_dma_mem nonemb_cmd;
+ u8 *gw;
+ int rc;
+
+ rc = beiscsi_if_get_info(phba, ip_type, &if_info);
+ if (rc)
+ return rc;
+
+ if (if_info->dhcp_state) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : DHCP Already Enabled\n");
+ goto exit;
+ }
+
+ /* first delete any IP set */
+ if (!beiscsi_if_zero_ip(if_info->ip_addr.addr, ip_type)) {
+ rc = beiscsi_if_clr_ip(phba, if_info);
+ if (rc)
+ goto exit;
+ }
+
+ /* delete gateway settings if mode change is to DHCP */
+ memset(&gw_resp, 0, sizeof(gw_resp));
+ /* use ip_type provided in if_info */
+ rc = beiscsi_if_get_gw(phba, if_info->ip_addr.ip_type, &gw_resp);
+ if (rc) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : Failed to Get Gateway Addr\n");
+ goto exit;
+ }
+ gw = (u8 *)&gw_resp.ip_addr.addr;
+ if (!beiscsi_if_zero_ip(gw, if_info->ip_addr.ip_type)) {
+ rc = beiscsi_if_mod_gw(phba, IP_ACTION_DEL,
+ if_info->ip_addr.ip_type, gw);
+ if (rc) {
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BG_%d : Failed to clear Gateway Addr Set\n");
+ goto exit;
+ }
+ }
+
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_CONFIG_STATELESS_IP_ADDR,
+ sizeof(*dhcpreq));
+ if (rc)
+ goto exit;
+
+ dhcpreq = nonemb_cmd.va;
+ dhcpreq->flags = 1; /* 1 - blocking; 0 - non-blocking */
+ dhcpreq->retry_count = 1;
+ dhcpreq->interface_hndl = phba->interface_handle;
+ dhcpreq->ip_type = ip_type;
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+exit:
+ kfree(if_info);
+ return rc;
+}
+
+/**
+ * beiscsi_if_set_vlan()- Issue and wait for CMD completion
+ * @phba: device private structure instance
+ * @vlan_tag: VLAN tag
+ *
+ * Issue the MBX Cmd and wait for the completion of the
+ * command.
+ *
+ * returns
+ * Success: 0
+ * Failure: Non-Xero Value
+ **/
+int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag)
+{
+ int rc;
+ unsigned int tag;
+
+ tag = be_cmd_set_vlan(phba, vlan_tag);
+ if (!tag) {
+ beiscsi_log(phba, KERN_ERR,
+ (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX),
+ "BG_%d : VLAN Setting Failed\n");
+ return -EBUSY;
+ }
+
+ rc = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL);
+ if (rc) {
+ beiscsi_log(phba, KERN_ERR,
+ (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX),
+ "BS_%d : VLAN MBX Cmd Failed\n");
+ return rc;
+ }
+ return rc;
+}
+
+
+int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type,
+ struct be_cmd_get_if_info_resp **if_info)
+{
+ struct be_cmd_get_if_info_req *req;
+ struct be_dma_mem nonemb_cmd;
+ uint32_t ioctl_size = sizeof(struct be_cmd_get_if_info_resp);
+ int rc;
+
+ rc = beiscsi_if_get_handle(phba);
+ if (rc)
+ return rc;
+
+ do {
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd,
+ CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_GET_IF_INFO,
+ ioctl_size);
+ if (rc)
+ return rc;
+
+ req = nonemb_cmd.va;
+ req->interface_hndl = phba->interface_handle;
+ req->ip_type = ip_type;
+
+ /* Allocate memory for if_info */
+ *if_info = kzalloc(ioctl_size, GFP_KERNEL);
+ if (!*if_info) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
+ "BG_%d : Memory Allocation Failure\n");
+
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd,
+ -ENOMEM);
+ return -ENOMEM;
+ }
+
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, *if_info,
+ ioctl_size);
+
+ /* Check if the error is because of Insufficent_Buffer */
+ if (rc == -EAGAIN) {
+
+ /* Get the new memory size */
+ ioctl_size = ((struct be_cmd_resp_hdr *)
+ nonemb_cmd.va)->actual_resp_len;
+ ioctl_size += sizeof(struct be_cmd_req_hdr);
+
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ /* Free the virtual memory */
+ kfree(*if_info);
+ } else {
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ break;
+ }
+ } while (true);
+ return rc;
+}
+
+int mgmt_get_nic_conf(struct beiscsi_hba *phba,
+ struct be_cmd_get_nic_conf_resp *nic)
+{
+ struct be_dma_mem nonemb_cmd;
+ int rc;
+
+ rc = beiscsi_prep_nemb_cmd(phba, &nonemb_cmd, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
+ sizeof(*nic));
+ if (rc)
+ return rc;
+
+ rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, nic, sizeof(*nic));
+ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
+ return rc;
+}
+
+static void beiscsi_boot_process_compl(struct beiscsi_hba *phba,
+ unsigned int tag)
+{
+ struct be_cmd_get_boot_target_resp *boot_resp;
+ struct be_cmd_resp_logout_fw_sess *logo_resp;
+ struct be_cmd_get_session_resp *sess_resp;
+ struct be_mcc_wrb *wrb;
+ struct boot_struct *bs;
+ int boot_work, status;
+
+ if (!test_bit(BEISCSI_HBA_BOOT_WORK, &phba->state)) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BG_%d : %s no boot work %lx\n",
+ __func__, phba->state);
+ return;
+ }
+
+ if (phba->boot_struct.tag != tag) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BG_%d : %s tag mismatch %d:%d\n",
+ __func__, tag, phba->boot_struct.tag);
+ return;
+ }
+ bs = &phba->boot_struct;
+ boot_work = 1;
+ status = 0;
+ switch (bs->action) {
+ case BEISCSI_BOOT_REOPEN_SESS:
+ status = __beiscsi_mcc_compl_status(phba, tag, NULL, NULL);
+ if (!status)
+ bs->action = BEISCSI_BOOT_GET_SHANDLE;
+ else
+ bs->retry--;
+ break;
+ case BEISCSI_BOOT_GET_SHANDLE:
+ status = __beiscsi_mcc_compl_status(phba, tag, &wrb, NULL);
+ if (!status) {
+ boot_resp = embedded_payload(wrb);
+ bs->s_handle = boot_resp->boot_session_handle;
+ }
+ if (bs->s_handle == BE_BOOT_INVALID_SHANDLE) {
+ bs->action = BEISCSI_BOOT_REOPEN_SESS;
+ bs->retry--;
+ } else {
+ bs->action = BEISCSI_BOOT_GET_SINFO;
+ }
+ break;
+ case BEISCSI_BOOT_GET_SINFO:
+ status = __beiscsi_mcc_compl_status(phba, tag, NULL,
+ &bs->nonemb_cmd);
+ if (!status) {
+ sess_resp = bs->nonemb_cmd.va;
+ memcpy(&bs->boot_sess, &sess_resp->session_info,
+ sizeof(struct mgmt_session_info));
+ bs->action = BEISCSI_BOOT_LOGOUT_SESS;
+ } else {
+ __beiscsi_log(phba, KERN_ERR,
+ "BG_%d : get boot session info error : 0x%x\n",
+ status);
+ boot_work = 0;
+ }
+ dma_free_coherent(&phba->ctrl.pdev->dev, bs->nonemb_cmd.size,
+ bs->nonemb_cmd.va, bs->nonemb_cmd.dma);
+ bs->nonemb_cmd.va = NULL;
+ break;
+ case BEISCSI_BOOT_LOGOUT_SESS:
+ status = __beiscsi_mcc_compl_status(phba, tag, &wrb, NULL);
+ if (!status) {
+ logo_resp = embedded_payload(wrb);
+ if (logo_resp->session_status != BE_SESS_STATUS_CLOSE) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BG_%d : FW boot session logout error : 0x%x\n",
+ logo_resp->session_status);
+ }
+ }
+ /* continue to create boot_kset even if logout failed? */
+ bs->action = BEISCSI_BOOT_CREATE_KSET;
+ break;
+ default:
+ break;
+ }
+
+ /* clear the tag so no other completion matches this tag */
+ bs->tag = 0;
+ if (!bs->retry) {
+ boot_work = 0;
+ __beiscsi_log(phba, KERN_ERR,
+ "BG_%d : failed to setup boot target: status %d action %d\n",
+ status, bs->action);
+ }
+ if (!boot_work) {
+ /* wait for next event to start boot_work */
+ clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state);
+ return;
+ }
+ schedule_work(&phba->boot_work);
+}
+
+/**
+ * beiscsi_boot_logout_sess()- Logout from boot FW session
+ * @phba: Device priv structure instance
+ *
+ * return
+ * the TAG used for MBOX Command
+ *
+ */
+unsigned int beiscsi_boot_logout_sess(struct beiscsi_hba *phba)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
- struct iscsi_cleanup_req *req = embedded_payload(wrb);
- int status = 0;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_logout_fw_sess *req;
+ unsigned int tag;
- spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
+ }
+ req = embedded_payload(wrb);
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
- OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req));
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
+ OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET,
+ sizeof(struct be_cmd_req_logout_fw_sess));
+ /* Use the session handle copied into boot_sess */
+ req->session_handle = phba->boot_struct.boot_sess.session_handle;
- req->chute = chute;
- req->hdr_ring_id = cpu_to_le16(HWI_GET_DEF_HDRQ_ID(phba));
- req->data_ring_id = cpu_to_le16(HWI_GET_DEF_BUFQ_ID(phba));
+ phba->boot_struct.tag = tag;
+ set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state);
+ ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl;
- status = be_mcc_notify_wait(phba);
- if (status)
- shost_printk(KERN_WARNING, phba->shost,
- " mgmt_epfw_cleanup , FAILED\n");
- spin_unlock(&ctrl->mbox_lock);
- return status;
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
+
+ return tag;
+}
+/**
+ * beiscsi_boot_reopen_sess()- Reopen boot session
+ * @phba: Device priv structure instance
+ *
+ * return
+ * the TAG used for MBOX Command
+ *
+ **/
+unsigned int beiscsi_boot_reopen_sess(struct beiscsi_hba *phba)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_reopen_session_req *req;
+ unsigned int tag;
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
+ }
+
+ req = embedded_payload(wrb);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
+ OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS,
+ sizeof(struct be_cmd_reopen_session_resp));
+ req->reopen_type = BE_REOPEN_BOOT_SESSIONS;
+ req->session_handle = BE_BOOT_INVALID_SHANDLE;
+
+ phba->boot_struct.tag = tag;
+ set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state);
+ ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl;
+
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
+ return tag;
}
-unsigned int mgmt_invalidate_icds(struct beiscsi_hba *phba,
- struct invalidate_command_table *inv_tbl,
- unsigned int num_invalidate, unsigned int cid,
- struct be_dma_mem *nonemb_cmd)
+/**
+ * beiscsi_boot_get_sinfo()- Get boot session info
+ * @phba: device priv structure instance
+ *
+ * Fetches the boot_struct.s_handle info from FW.
+ * return
+ * the TAG used for MBOX Command
+ *
+ **/
+unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_cmd_get_session_req *req;
+ struct be_dma_mem *nonemb_cmd;
struct be_mcc_wrb *wrb;
struct be_sge *sge;
- struct invalidate_commands_params_in *req;
- unsigned int i, tag = 0;
+ unsigned int tag;
- spin_lock(&ctrl->mbox_lock);
- tag = alloc_mcc_tag(phba);
- if (!tag) {
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
+ }
+
+ nonemb_cmd = &phba->boot_struct.nonemb_cmd;
+ nonemb_cmd->size = sizeof(struct be_cmd_get_session_resp);
+ nonemb_cmd->va = dma_alloc_coherent(&phba->ctrl.pdev->dev,
+ nonemb_cmd->size,
+ &nonemb_cmd->dma,
+ GFP_KERNEL);
+ if (!nonemb_cmd->va) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
}
req = nonemb_cmd->va;
memset(req, 0, sizeof(*req));
- wrb = wrb_from_mccq(phba);
sge = nonembedded_sgl(wrb);
- wrb->tag0 |= tag;
-
be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
- OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS,
- sizeof(*req));
- req->ref_handle = 0;
- req->cleanup_type = CMD_ISCSI_COMMAND_INVALIDATE;
- for (i = 0; i < num_invalidate; i++) {
- req->table[i].icd = inv_tbl->icd;
- req->table[i].cid = inv_tbl->cid;
- req->icd_count++;
- inv_tbl++;
- }
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
+ OPCODE_ISCSI_INI_SESSION_GET_A_SESSION,
+ sizeof(struct be_cmd_get_session_resp));
+ req->session_handle = phba->boot_struct.s_handle;
sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd->size);
- be_mcc_notify(phba);
- spin_unlock(&ctrl->mbox_lock);
+ phba->boot_struct.tag = tag;
+ set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state);
+ ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl;
+
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
return tag;
}
-unsigned int mgmt_invalidate_connection(struct beiscsi_hba *phba,
- struct beiscsi_endpoint *beiscsi_ep,
- unsigned short cid,
- unsigned short issue_reset,
- unsigned short savecfg_flag)
+unsigned int __beiscsi_boot_get_shandle(struct beiscsi_hba *phba, int async)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb;
- struct iscsi_invalidate_connection_params_in *req;
- unsigned int tag = 0;
+ struct be_cmd_get_boot_target_req *req;
+ unsigned int tag;
- spin_lock(&ctrl->mbox_lock);
- tag = alloc_mcc_tag(phba);
- if (!tag) {
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
}
- wrb = wrb_from_mccq(phba);
- wrb->tag0 |= tag;
- req = embedded_payload(wrb);
+ req = embedded_payload(wrb);
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
- OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION,
- sizeof(*req));
- req->session_handle = beiscsi_ep->fw_handle;
- req->cid = cid;
- if (issue_reset)
- req->cleanup_type = CMD_ISCSI_CONNECTION_ISSUE_TCP_RST;
- else
- req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE;
- req->save_cfg = savecfg_flag;
- be_mcc_notify(phba);
- spin_unlock(&ctrl->mbox_lock);
+ OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET,
+ sizeof(struct be_cmd_get_boot_target_resp));
+
+ if (async) {
+ phba->boot_struct.tag = tag;
+ set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state);
+ ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl;
+ }
+
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
return tag;
}
-unsigned int mgmt_upload_connection(struct beiscsi_hba *phba,
- unsigned short cid, unsigned int upload_flag)
+/**
+ * beiscsi_boot_get_shandle()- Get boot session handle
+ * @phba: device priv structure instance
+ * @s_handle: session handle returned for boot session.
+ *
+ * return
+ * Success: 1
+ * Failure: negative
+ *
+ **/
+int beiscsi_boot_get_shandle(struct beiscsi_hba *phba, unsigned int *s_handle)
{
- struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_cmd_get_boot_target_resp *boot_resp;
struct be_mcc_wrb *wrb;
- struct tcp_upload_params_in *req;
- unsigned int tag = 0;
+ unsigned int tag;
+ int rc;
- spin_lock(&ctrl->mbox_lock);
- tag = alloc_mcc_tag(phba);
+ *s_handle = BE_BOOT_INVALID_SHANDLE;
+ /* get configured boot session count and handle */
+ tag = __beiscsi_boot_get_shandle(phba, 0);
if (!tag) {
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT,
+ "BG_%d : Getting Boot Target Info Failed\n");
+ return -EAGAIN;
}
- wrb = wrb_from_mccq(phba);
- req = embedded_payload(wrb);
- wrb->tag0 |= tag;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
- be_cmd_hdr_prepare(&req->hdr, CMD_COMMON_TCP_UPLOAD,
- OPCODE_COMMON_TCP_UPLOAD, sizeof(*req));
- req->id = (unsigned short)cid;
- req->upload_type = (unsigned char)upload_flag;
- be_mcc_notify(phba);
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+ rc = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL);
+ if (rc) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
+ "BG_%d : MBX CMD get_boot_target Failed\n");
+ return -EBUSY;
+ }
+
+ boot_resp = embedded_payload(wrb);
+ /* check if there are any boot targets configured */
+ if (!boot_resp->boot_session_count) {
+ __beiscsi_log(phba, KERN_INFO,
+ "BG_%d : No boot targets configured\n");
+ return -ENXIO;
+ }
+
+ /* only if FW has logged in to the boot target, s_handle is valid */
+ *s_handle = boot_resp->boot_session_handle;
+ return 1;
}
-int mgmt_open_connection(struct beiscsi_hba *phba,
- struct sockaddr *dst_addr,
- struct beiscsi_endpoint *beiscsi_ep,
- struct be_dma_mem *nonemb_cmd)
+/**
+ * beiscsi_drvr_ver_disp()- Display the driver Name and Version
+ * @dev: ptr to device not used.
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text driver name and version
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_drvr_ver_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, BE_NAME "\n");
+}
+/**
+ * beiscsi_fw_ver_disp()- Display Firmware Version
+ * @dev: ptr to device not used.
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text Firmware version
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_fw_ver_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
- struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr;
- struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr;
- struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb;
- struct tcp_connect_and_offload_in *req;
- unsigned short def_hdr_id;
- unsigned short def_data_id;
- struct phys_addr template_address = { 0, 0 };
- struct phys_addr *ptemplate_address;
- unsigned int tag = 0;
- unsigned int i;
- unsigned short cid = beiscsi_ep->ep_cid;
- struct be_sge *sge;
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
- def_hdr_id = (unsigned short)HWI_GET_DEF_HDRQ_ID(phba);
- def_data_id = (unsigned short)HWI_GET_DEF_BUFQ_ID(phba);
+ return snprintf(buf, PAGE_SIZE, "%s\n", phba->fw_ver_str);
+}
- ptemplate_address = &template_address;
- ISCSI_GET_PDU_TEMPLATE_ADDRESS(phba, ptemplate_address);
- spin_lock(&ctrl->mbox_lock);
- tag = alloc_mcc_tag(phba);
- if (!tag) {
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+/**
+ * beiscsi_active_session_disp()- Display Sessions Active
+ * @dev: ptr to device not used.
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text Session Count
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_active_session_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ uint16_t avlbl_cids = 0, ulp_num, len = 0, total_cids = 0;
+
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) {
+ avlbl_cids = BEISCSI_ULP_AVLBL_CID(phba, ulp_num);
+ total_cids = BEISCSI_GET_CID_COUNT(phba, ulp_num);
+ len += scnprintf(buf+len, PAGE_SIZE - len,
+ "ULP%d : %d\n", ulp_num,
+ (total_cids - avlbl_cids));
+ } else
+ len += scnprintf(buf+len, PAGE_SIZE - len,
+ "ULP%d : %d\n", ulp_num, 0);
}
- wrb = wrb_from_mccq(phba);
- memset(wrb, 0, sizeof(*wrb));
- sge = nonembedded_sgl(wrb);
- req = nonemb_cmd->va;
- memset(req, 0, sizeof(*req));
- wrb->tag0 |= tag;
+ return len;
+}
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
- OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD,
- sizeof(*req));
- if (dst_addr->sa_family == PF_INET) {
- __be32 s_addr = daddr_in->sin_addr.s_addr;
- req->ip_address.ip_type = BE2_IPV4;
- req->ip_address.ip_address[0] = s_addr & 0x000000ff;
- req->ip_address.ip_address[1] = (s_addr & 0x0000ff00) >> 8;
- req->ip_address.ip_address[2] = (s_addr & 0x00ff0000) >> 16;
- req->ip_address.ip_address[3] = (s_addr & 0xff000000) >> 24;
- req->tcp_port = ntohs(daddr_in->sin_port);
- beiscsi_ep->dst_addr = daddr_in->sin_addr.s_addr;
- beiscsi_ep->dst_tcpport = ntohs(daddr_in->sin_port);
- beiscsi_ep->ip_type = BE2_IPV4;
- } else if (dst_addr->sa_family == PF_INET6) {
- req->ip_address.ip_type = BE2_IPV6;
- memcpy(&req->ip_address.ip_address,
- &daddr_in6->sin6_addr.in6_u.u6_addr8, 16);
- req->tcp_port = ntohs(daddr_in6->sin6_port);
- beiscsi_ep->dst_tcpport = ntohs(daddr_in6->sin6_port);
- memcpy(&beiscsi_ep->dst6_addr,
- &daddr_in6->sin6_addr.in6_u.u6_addr8, 16);
- beiscsi_ep->ip_type = BE2_IPV6;
- } else{
- shost_printk(KERN_ERR, phba->shost, "unknown addr family %d\n",
- dst_addr->sa_family);
- spin_unlock(&ctrl->mbox_lock);
- free_mcc_tag(&phba->ctrl, tag);
- return -EINVAL;
+/**
+ * beiscsi_free_session_disp()- Display Avaliable Session
+ * @dev: ptr to device not used.
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text Session Count
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_free_session_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+ uint16_t ulp_num, len = 0;
+ for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+ if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported))
+ len += scnprintf(buf+len, PAGE_SIZE - len,
+ "ULP%d : %d\n", ulp_num,
+ BEISCSI_ULP_AVLBL_CID(phba, ulp_num));
+ else
+ len += scnprintf(buf+len, PAGE_SIZE - len,
+ "ULP%d : %d\n", ulp_num, 0);
}
- req->cid = cid;
- i = phba->nxt_cqid++;
- if (phba->nxt_cqid == phba->num_cpus)
- phba->nxt_cqid = 0;
- req->cq_id = phwi_context->be_cq[i].id;
- SE_DEBUG(DBG_LVL_8, "i=%d cq_id=%d\n", i, req->cq_id);
- req->defq_id = def_hdr_id;
- req->hdr_ring_id = def_hdr_id;
- req->data_ring_id = def_data_id;
- req->do_offload = 1;
- req->dataout_template_pa.lo = ptemplate_address->lo;
- req->dataout_template_pa.hi = ptemplate_address->hi;
- sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
- sge->len = cpu_to_le32(nonemb_cmd->size);
- be_mcc_notify(phba);
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+
+ return len;
}
-unsigned int be_cmd_get_mac_addr(struct beiscsi_hba *phba)
+/**
+ * beiscsi_adap_family_disp()- Display adapter family.
+ * @dev: ptr to device to get priv structure
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text driver name and version
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_adap_family_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
+ uint16_t dev_id = 0;
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+
+ dev_id = phba->pcidev->device;
+ switch (dev_id) {
+ case BE_DEVICE_ID1:
+ case OC_DEVICE_ID1:
+ case OC_DEVICE_ID2:
+ return snprintf(buf, PAGE_SIZE,
+ "Obsolete/Unsupported BE2 Adapter Family\n");
+ case BE_DEVICE_ID2:
+ case OC_DEVICE_ID3:
+ return snprintf(buf, PAGE_SIZE, "BE3-R Adapter Family\n");
+ case OC_SKH_ID1:
+ return snprintf(buf, PAGE_SIZE, "Skyhawk-R Adapter Family\n");
+ default:
+ return snprintf(buf, PAGE_SIZE,
+ "Unknown Adapter Family: 0x%x\n", dev_id);
+ }
+}
+
+/**
+ * beiscsi_phys_port_disp()- Display Physical Port Identifier
+ * @dev: ptr to device not used.
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text port identifier
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_phys_port_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "Port Identifier : %u\n",
+ phba->fw_config.phys_port);
+}
+
+void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle,
+ struct be_mem_descriptor *mem_descr,
+ struct hwi_wrb_context *pwrb_context)
+{
+ struct iscsi_wrb *pwrb = pwrb_handle->pwrb;
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ max_send_data_segment_length, pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ max_send_data_segment_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, type, pwrb,
+ BE_TGT_CTX_UPDT_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ first_burst_length,
+ pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ first_burst_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, erl, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ erl) / 32] & OFFLD_PARAMS_ERL));
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, dde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ dde) / 32] & OFFLD_PARAMS_DDE) >> 2);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, hde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ hde) / 32] & OFFLD_PARAMS_HDE) >> 3);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ir2t, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, imd, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ imd) / 32] & OFFLD_PARAMS_IMD) >> 5);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, stat_sn,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ exp_statsn) / 32] + 1));
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, wrb_idx,
+ pwrb, pwrb_handle->wrb_index);
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ max_burst_length, pwrb, params->dw[offsetof
+ (struct amap_beiscsi_offload_params,
+ max_burst_length) / 32]);
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb,
+ pwrb, pwrb_handle->wrb_index);
+ if (pwrb_context->plast_wrb)
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ ptr2nextwrb,
+ pwrb_context->plast_wrb,
+ pwrb_handle->wrb_index);
+ pwrb_context->plast_wrb = pwrb;
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ session_state, pwrb, 0);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack,
+ pwrb, 1);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, notpredblq,
+ pwrb, 0);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, mode, pwrb,
+ 0);
+
+ mem_descr += ISCSI_MEM_GLOBAL_HEADER;
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ pad_buffer_addr_hi, pwrb,
+ mem_descr->mem_array[0].bus_address.u.a32.address_hi);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ pad_buffer_addr_lo, pwrb,
+ mem_descr->mem_array[0].bus_address.u.a32.address_lo);
+}
+
+void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle,
+ struct hwi_wrb_context *pwrb_context)
+{
+ struct iscsi_wrb *pwrb = pwrb_handle->pwrb;
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_burst_length, pwrb, params->dw[offsetof
+ (struct amap_beiscsi_offload_params,
+ max_burst_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ type, pwrb,
+ BE_TGT_CTX_UPDT_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ ptr2nextwrb,
+ pwrb, pwrb_handle->wrb_index);
+ if (pwrb_context->plast_wrb)
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ ptr2nextwrb,
+ pwrb_context->plast_wrb,
+ pwrb_handle->wrb_index);
+ pwrb_context->plast_wrb = pwrb;
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, wrb_idx,
+ pwrb, pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_send_data_segment_length, pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ max_send_data_segment_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ first_burst_length, pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ first_burst_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_recv_dataseg_len, pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ max_recv_data_segment_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_cxns, pwrb, BEISCSI_MAX_CXNS);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, erl, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ erl) / 32] & OFFLD_PARAMS_ERL));
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, dde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ dde) / 32] & OFFLD_PARAMS_DDE) >> 2);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, hde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ hde) / 32] & OFFLD_PARAMS_HDE) >> 3);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ ir2t, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, imd, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ imd) / 32] & OFFLD_PARAMS_IMD) >> 5);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ data_seq_inorder,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ data_seq_inorder) / 32] &
+ OFFLD_PARAMS_DATA_SEQ_INORDER) >> 6);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ pdu_seq_inorder,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ pdu_seq_inorder) / 32] &
+ OFFLD_PARAMS_PDU_SEQ_INORDER) >> 7);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, max_r2t,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ max_r2t) / 32] &
+ OFFLD_PARAMS_MAX_R2T) >> 8);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, stat_sn,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ exp_statsn) / 32] + 1));
+}
+
+unsigned int beiscsi_invalidate_cxn(struct beiscsi_hba *phba,
+ struct beiscsi_endpoint *beiscsi_ep)
+{
+ struct be_invalidate_connection_params_in *req;
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb;
- struct be_cmd_req_get_mac_addr *req;
unsigned int tag = 0;
- SE_DEBUG(DBG_LVL_8, "In be_cmd_get_mac_addr\n");
- spin_lock(&ctrl->mbox_lock);
- tag = alloc_mcc_tag(phba);
- if (!tag) {
- spin_unlock(&ctrl->mbox_lock);
- return tag;
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
}
- wrb = wrb_from_mccq(phba);
req = embedded_payload(wrb);
- wrb->tag0 |= tag;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
- OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
+ be_wrb_hdr_prepare(wrb, sizeof(union be_invalidate_connection_params),
+ true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
+ OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION,
sizeof(*req));
+ req->session_handle = beiscsi_ep->fw_handle;
+ req->cid = beiscsi_ep->ep_cid;
+ if (beiscsi_ep->conn)
+ req->cleanup_type = BE_CLEANUP_TYPE_INVALIDATE;
+ else
+ req->cleanup_type = BE_CLEANUP_TYPE_ISSUE_TCP_RST;
+ /**
+ * 0 - non-persistent targets
+ * 1 - save session info on flash
+ */
+ req->save_cfg = 0;
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
+ return tag;
+}
- be_mcc_notify(phba);
- spin_unlock(&ctrl->mbox_lock);
+unsigned int beiscsi_upload_cxn(struct beiscsi_hba *phba,
+ struct beiscsi_endpoint *beiscsi_ep)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb;
+ struct be_tcp_upload_params_in *req;
+ unsigned int tag;
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ return 0;
+ }
+
+ req = embedded_payload(wrb);
+ be_wrb_hdr_prepare(wrb, sizeof(union be_tcp_upload_params), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_COMMON_TCP_UPLOAD,
+ OPCODE_COMMON_TCP_UPLOAD, sizeof(*req));
+ req->id = beiscsi_ep->ep_cid;
+ if (beiscsi_ep->conn)
+ req->upload_type = BE_UPLOAD_TYPE_GRACEFUL;
+ else
+ req->upload_type = BE_UPLOAD_TYPE_ABORT;
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
return tag;
}
+int beiscsi_mgmt_invalidate_icds(struct beiscsi_hba *phba,
+ struct invldt_cmd_tbl *inv_tbl,
+ unsigned int nents)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct invldt_cmds_params_in *req;
+ struct be_dma_mem nonemb_cmd;
+ struct be_mcc_wrb *wrb;
+ unsigned int i, tag;
+ struct be_sge *sge;
+ int rc;
+
+ if (!nents || nents > BE_INVLDT_CMD_TBL_SZ)
+ return -EINVAL;
+
+ nonemb_cmd.size = sizeof(union be_invldt_cmds_params);
+ nonemb_cmd.va = dma_alloc_coherent(&phba->ctrl.pdev->dev,
+ nonemb_cmd.size, &nonemb_cmd.dma,
+ GFP_KERNEL);
+ if (!nonemb_cmd.va) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_EH,
+ "BM_%d : invldt_cmds_params alloc failed\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&ctrl->mbox_lock);
+ wrb = alloc_mcc_wrb(phba, &tag);
+ if (!wrb) {
+ mutex_unlock(&ctrl->mbox_lock);
+ dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+ return -ENOMEM;
+ }
+
+ req = nonemb_cmd.va;
+ be_wrb_hdr_prepare(wrb, nonemb_cmd.size, false, 1);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS,
+ sizeof(*req));
+ req->ref_handle = 0;
+ req->cleanup_type = CMD_ISCSI_COMMAND_INVALIDATE;
+ for (i = 0; i < nents; i++) {
+ req->table[i].icd = inv_tbl[i].icd;
+ req->table[i].cid = inv_tbl[i].cid;
+ req->icd_count++;
+ }
+ sge = nonembedded_sgl(wrb);
+ sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
+ sge->pa_lo = cpu_to_le32(lower_32_bits(nonemb_cmd.dma));
+ sge->len = cpu_to_le32(nonemb_cmd.size);
+
+ be_mcc_notify(phba, tag);
+ mutex_unlock(&ctrl->mbox_lock);
+
+ rc = beiscsi_mccq_compl_wait(phba, tag, NULL, &nonemb_cmd);
+ if (rc != -EBUSY)
+ dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
+ nonemb_cmd.va, nonemb_cmd.dma);
+ return rc;
+}
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index 08428824ace2..d10858828701 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -1,164 +1,88 @@
-/**
- * Copyright (C) 2005 - 2011 Emulex
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation. The full GNU General
- * Public License is included in this distribution in the file called COPYING.
- *
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2017 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
*
* Contact Information:
- * linux-drivers@emulex.com
- *
- * Emulex
- * 3333 Susan Street
- * Costa Mesa, CA 92626
+ * linux-drivers@broadcom.com
*/
#ifndef _BEISCSI_MGMT_
#define _BEISCSI_MGMT_
-#include <linux/types.h>
-#include <linux/list.h>
+#include <scsi/scsi_bsg_iscsi.h>
#include "be_iscsi.h"
#include "be_main.h"
-/**
- * Pseudo amap definition in which each bit of the actual structure is defined
- * as a byte: used to calculate offset/shift/mask of each field
- */
-struct amap_mcc_sge {
- u8 pa_lo[32]; /* dword 0 */
- u8 pa_hi[32]; /* dword 1 */
- u8 length[32]; /* DWORD 2 */
-} __packed;
-
-/**
- * Pseudo amap definition in which each bit of the actual structure is defined
- * as a byte: used to calculate offset/shift/mask of each field
- */
-struct amap_mcc_wrb_payload {
- union {
- struct amap_mcc_sge sgl[19];
- u8 embedded[59 * 32]; /* DWORDS 57 to 115 */
- } u;
-} __packed;
-
-/**
- * Pseudo amap definition in which each bit of the actual structure is defined
- * as a byte: used to calculate offset/shift/mask of each field
- */
-struct amap_mcc_wrb {
- u8 embedded; /* DWORD 0 */
- u8 rsvd0[2]; /* DWORD 0 */
- u8 sge_count[5]; /* DWORD 0 */
- u8 rsvd1[16]; /* DWORD 0 */
- u8 special[8]; /* DWORD 0 */
- u8 payload_length[32];
- u8 tag[64]; /* DWORD 2 */
- u8 rsvd2[32]; /* DWORD 4 */
- struct amap_mcc_wrb_payload payload;
-};
-
-struct mcc_sge {
- u32 pa_lo; /* dword 0 */
- u32 pa_hi; /* dword 1 */
- u32 length; /* DWORD 2 */
-} __packed;
-
-struct mcc_wrb_payload {
- union {
- struct mcc_sge sgl[19];
- u32 embedded[59]; /* DWORDS 57 to 115 */
- } u;
-} __packed;
+#define IP_ACTION_ADD 0x01
+#define IP_ACTION_DEL 0x02
-#define MCC_WRB_EMBEDDED_MASK 0x00000001
+#define IP_V6_LEN 16
+#define IP_V4_LEN 4
-struct mcc_wrb {
- u32 dw[0]; /* DWORD 0 */
- u32 payload_length;
- u32 tag[2]; /* DWORD 2 */
- u32 rsvd2[1]; /* DWORD 4 */
- struct mcc_wrb_payload payload;
-};
+/* UE Status and Mask register */
+#define PCICFG_UE_STATUS_LOW 0xA0
+#define PCICFG_UE_STATUS_HIGH 0xA4
+#define PCICFG_UE_STATUS_MASK_LOW 0xA8
+#define PCICFG_UE_STATUS_MASK_HI 0xAC
-int mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute);
int mgmt_open_connection(struct beiscsi_hba *phba,
struct sockaddr *dst_addr,
struct beiscsi_endpoint *beiscsi_ep,
struct be_dma_mem *nonemb_cmd);
-unsigned int mgmt_upload_connection(struct beiscsi_hba *phba,
- unsigned short cid,
- unsigned int upload_flag);
-unsigned int mgmt_invalidate_icds(struct beiscsi_hba *phba,
- struct invalidate_command_table *inv_tbl,
- unsigned int num_invalidate, unsigned int cid,
- struct be_dma_mem *nonemb_cmd);
-
-struct iscsi_invalidate_connection_params_in {
- struct be_cmd_req_hdr hdr;
- unsigned int session_handle;
- unsigned short cid;
- unsigned short unused;
- unsigned short cleanup_type;
- unsigned short save_cfg;
-} __packed;
+unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba,
+ struct bsg_job *job,
+ struct be_dma_mem *nonemb_cmd);
-struct iscsi_invalidate_connection_params_out {
- unsigned int session_handle;
+#define BE_INVLDT_CMD_TBL_SZ 128
+struct invldt_cmd_tbl {
+ unsigned short icd;
unsigned short cid;
- unsigned short unused;
} __packed;
-union iscsi_invalidate_connection_params {
- struct iscsi_invalidate_connection_params_in request;
- struct iscsi_invalidate_connection_params_out response;
-} __packed;
-
-struct invalidate_commands_params_in {
+struct invldt_cmds_params_in {
struct be_cmd_req_hdr hdr;
unsigned int ref_handle;
unsigned int icd_count;
- struct invalidate_command_table table[128];
+ struct invldt_cmd_tbl table[BE_INVLDT_CMD_TBL_SZ];
unsigned short cleanup_type;
unsigned short unused;
} __packed;
-struct invalidate_commands_params_out {
+struct invldt_cmds_params_out {
+ struct be_cmd_resp_hdr hdr;
unsigned int ref_handle;
unsigned int icd_count;
- unsigned int icd_status[128];
+ unsigned int icd_status[BE_INVLDT_CMD_TBL_SZ];
} __packed;
-union invalidate_commands_params {
- struct invalidate_commands_params_in request;
- struct invalidate_commands_params_out response;
+union be_invldt_cmds_params {
+ struct invldt_cmds_params_in request;
+ struct invldt_cmds_params_out response;
} __packed;
struct mgmt_hba_attributes {
- u8 flashrom_version_string[32];
- u8 manufacturer_name[32];
+ u8 flashrom_version_string[BEISCSI_VER_STRLEN];
+ u8 manufacturer_name[BEISCSI_VER_STRLEN];
u32 supported_modes;
u8 seeprom_version_lo;
u8 seeprom_version_hi;
u8 rsvd0[2];
u32 fw_cmd_data_struct_version;
u32 ep_fw_data_struct_version;
- u32 future_reserved[12];
+ u8 ncsi_version_string[12];
u32 default_extended_timeout;
- u8 controller_model_number[32];
+ u8 controller_model_number[BEISCSI_VER_STRLEN];
u8 controller_description[64];
- u8 controller_serial_number[32];
- u8 ip_version_string[32];
- u8 firmware_version_string[32];
- u8 bios_version_string[32];
- u8 redboot_version_string[32];
- u8 driver_version_string[32];
- u8 fw_on_flash_version_string[32];
+ u8 controller_serial_number[BEISCSI_VER_STRLEN];
+ u8 ip_version_string[BEISCSI_VER_STRLEN];
+ u8 firmware_version_string[BEISCSI_VER_STRLEN];
+ u8 bios_version_string[BEISCSI_VER_STRLEN];
+ u8 redboot_version_string[BEISCSI_VER_STRLEN];
+ u8 driver_version_string[BEISCSI_VER_STRLEN];
+ u8 fw_on_flash_version_string[BEISCSI_VER_STRLEN];
u32 functionalities_supported;
u16 max_cdblength;
u8 asic_revision;
@@ -174,7 +98,8 @@ struct mgmt_hba_attributes {
u32 firmware_post_status;
u32 hba_mtu[8];
u8 iscsi_features;
- u8 future_u8[3];
+ u8 asic_generation;
+ u8 future_u8[2];
u32 future_u32[3];
} __packed;
@@ -191,7 +116,7 @@ struct mgmt_controller_attributes {
u64 unique_identifier;
u8 netfilters;
u8 rsvd0[3];
- u8 future_u32[4];
+ u32 future_u32[4];
} __packed;
struct be_mgmt_controller_attributes {
@@ -204,30 +129,29 @@ struct be_mgmt_controller_attributes_resp {
struct mgmt_controller_attributes params;
} __packed;
+struct be_bsg_vendor_cmd {
+ struct be_cmd_req_hdr hdr;
+ unsigned short region;
+ unsigned short offset;
+ unsigned short sector;
+} __packed;
+
/* configuration management */
#define GET_MGMT_CONTROLLER_WS(phba) (phba->pmgmt_ws)
-/* MGMT CMD flags */
-
-#define MGMT_CMDH_FREE (1<<0)
-
-/* --- MGMT_ERROR_CODES --- */
-/* Error Codes returned in the status field of the CMD response header */
-#define MGMT_STATUS_SUCCESS 0 /* The CMD completed without errors */
-#define MGMT_STATUS_FAILED 1 /* Error status in the Status field of */
- /* the CMD_RESPONSE_HEADER */
-
#define ISCSI_GET_PDU_TEMPLATE_ADDRESS(pc, pa) {\
- pa->lo = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\
+ pa->lo = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\
bus_address.u.a32.address_lo; \
- pa->hi = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\
+ pa->hi = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\
bus_address.u.a32.address_hi; \
}
+#define BEISCSI_WRITE_FLASH 0
+#define BEISCSI_READ_FLASH 1
+
struct beiscsi_endpoint {
struct beiscsi_hba *phba;
- struct beiscsi_sess *sess;
struct beiscsi_conn *conn;
struct iscsi_endpoint *openiscsi_ep;
unsigned short ip_type;
@@ -239,13 +163,80 @@ struct beiscsi_endpoint {
u16 cid_vld;
};
-int mgmt_get_fw_config(struct be_ctrl_info *ctrl,
- struct beiscsi_hba *phba);
+int beiscsi_mgmt_invalidate_icds(struct beiscsi_hba *phba,
+ struct invldt_cmd_tbl *inv_tbl,
+ unsigned int nents);
+
+int beiscsi_get_initiator_name(struct beiscsi_hba *phba, char *name, bool cfg);
+
+int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type);
+
+int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type,
+ u8 *ip, u8 *subnet);
+
+int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw);
+
+int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type,
+ struct be_cmd_get_def_gateway_resp *resp);
+
+int mgmt_get_nic_conf(struct beiscsi_hba *phba,
+ struct be_cmd_get_nic_conf_resp *mac);
+
+int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type,
+ struct be_cmd_get_if_info_resp **if_info);
+
+unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba);
+
+int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag);
+
+unsigned int beiscsi_boot_logout_sess(struct beiscsi_hba *phba);
+
+unsigned int beiscsi_boot_reopen_sess(struct beiscsi_hba *phba);
+
+unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba);
+
+unsigned int __beiscsi_boot_get_shandle(struct beiscsi_hba *phba, int async);
+
+int beiscsi_boot_get_shandle(struct beiscsi_hba *phba, unsigned int *s_handle);
+
+ssize_t beiscsi_drvr_ver_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+ssize_t beiscsi_fw_ver_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+ssize_t beiscsi_active_session_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+ssize_t beiscsi_adap_family_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+ssize_t beiscsi_free_session_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+ssize_t beiscsi_phys_port_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle,
+ struct be_mem_descriptor *mem_descr,
+ struct hwi_wrb_context *pwrb_context);
+
+void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle,
+ struct hwi_wrb_context *pwrb_context);
+
+unsigned int beiscsi_invalidate_cxn(struct beiscsi_hba *phba,
+ struct beiscsi_endpoint *beiscsi_ep);
+
+unsigned int beiscsi_upload_cxn(struct beiscsi_hba *phba,
+ struct beiscsi_endpoint *beiscsi_ep);
+
+int be_cmd_modify_eq_delay(struct beiscsi_hba *phba,
+ struct be_set_eqd *, int num);
-unsigned int mgmt_invalidate_connection(struct beiscsi_hba *phba,
- struct beiscsi_endpoint *beiscsi_ep,
- unsigned short cid,
- unsigned short issue_reset,
- unsigned short savecfg_flag);
+int beiscsi_logout_fw_sess(struct beiscsi_hba *phba,
+ uint32_t fw_sess_handle);
#endif
diff --git a/drivers/scsi/bfa/Makefile b/drivers/scsi/bfa/Makefile
index 4ce6f4942327..442fc3db8f1f 100644
--- a/drivers/scsi/bfa/Makefile
+++ b/drivers/scsi/bfa/Makefile
@@ -1,6 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SCSI_BFA_FC) := bfa.o
-bfa-y := bfad.o bfad_im.o bfad_attr.o bfad_debugfs.o
+bfa-y := bfad.o bfad_im.o bfad_attr.o bfad_debugfs.o bfad_bsg.o
bfa-y += bfa_ioc.o bfa_ioc_cb.o bfa_ioc_ct.o bfa_hw_cb.o bfa_hw_ct.o
bfa-y += bfa_fcs.o bfa_fcs_lport.o bfa_fcs_rport.o bfa_fcs_fcpim.o bfa_fcbuild.o
bfa-y += bfa_port.o bfa_fcpim.o bfa_core.o bfa_svc.o
diff --git a/drivers/scsi/bfa/bfa.h b/drivers/scsi/bfa/bfa.h
index 7be6b5a8114b..a6b8c4ddea19 100644
--- a/drivers/scsi/bfa/bfa.h
+++ b/drivers/scsi/bfa/bfa.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_H__
#define __BFA_H__
@@ -27,7 +20,6 @@
struct bfa_s;
typedef void (*bfa_isr_func_t) (struct bfa_s *bfa, struct bfi_msg_s *m);
-typedef void (*bfa_cb_cbfn_t) (void *cbarg, bfa_boolean_t complete);
/*
* Interrupt message handlers
@@ -54,14 +46,14 @@ void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m);
((void *)((struct bfi_msg_s *)((__bfa)->iocfc.req_cq_ba[__reqq].kva) \
+ bfa_reqq_pi((__bfa), (__reqq)))))
-#define bfa_reqq_produce(__bfa, __reqq) do { \
+#define bfa_reqq_produce(__bfa, __reqq, __mh) do { \
+ (__mh).mtag.h2i.qid = (__bfa)->iocfc.hw_qid[__reqq];\
(__bfa)->iocfc.req_cq_pi[__reqq]++; \
(__bfa)->iocfc.req_cq_pi[__reqq] &= \
((__bfa)->iocfc.cfg.drvcfg.num_reqq_elems - 1); \
writel((__bfa)->iocfc.req_cq_pi[__reqq], \
(__bfa)->iocfc.bfa_regs.cpe_q_pi[__reqq]); \
- mmiowb(); \
- } while (0)
+ } while (0)
#define bfa_rspq_pi(__bfa, __rspq) \
(*(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva))
@@ -76,16 +68,6 @@ void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m);
} while (0)
/*
- * Queue element to wait for room in request queue. FIFO order is
- * maintained when fullfilling requests.
- */
-struct bfa_reqq_wait_s {
- struct list_head qe;
- void (*qresume) (void *cbarg);
- void *cbarg;
-};
-
-/*
* Circular queue usage assignments
*/
enum {
@@ -128,21 +110,10 @@ bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg),
#define bfa_reqq_wcancel(__wqe) list_del(&(__wqe)->qe)
-
-/*
- * Generic BFA callback element.
- */
-struct bfa_cb_qe_s {
- struct list_head qe;
- bfa_cb_cbfn_t cbfn;
- bfa_boolean_t once;
- u32 rsvd;
- void *cbarg;
-};
-
#define bfa_cb_queue(__bfa, __hcb_qe, __cbfn, __cbarg) do { \
(__hcb_qe)->cbfn = (__cbfn); \
(__hcb_qe)->cbarg = (__cbarg); \
+ (__hcb_qe)->pre_rmv = BFA_FALSE; \
list_add_tail(&(__hcb_qe)->qe, &(__bfa)->comp_q); \
} while (0)
@@ -157,59 +128,26 @@ struct bfa_cb_qe_s {
} \
} while (0)
+#define bfa_cb_queue_status(__bfa, __hcb_qe, __status) do { \
+ (__hcb_qe)->fw_status = (__status); \
+ list_add_tail(&(__hcb_qe)->qe, &(__bfa)->comp_q); \
+} while (0)
+
#define bfa_cb_queue_done(__hcb_qe) do { \
(__hcb_qe)->once = BFA_FALSE; \
} while (0)
-/*
- * PCI devices supported by the current BFA
- */
-struct bfa_pciid_s {
- u16 device_id;
- u16 vendor_id;
-};
-
extern char bfa_version[];
-/*
- * BFA memory resources
- */
-enum bfa_mem_type {
- BFA_MEM_TYPE_KVA = 1, /* Kernel Virtual Memory *(non-dma-able) */
- BFA_MEM_TYPE_DMA = 2, /* DMA-able memory */
- BFA_MEM_TYPE_MAX = BFA_MEM_TYPE_DMA,
-};
-
-struct bfa_mem_elem_s {
- enum bfa_mem_type mem_type; /* see enum bfa_mem_type */
- u32 mem_len; /* Total Length in Bytes */
- u8 *kva; /* kernel virtual address */
- u64 dma; /* dma address if DMA memory */
- u8 *kva_curp; /* kva allocation cursor */
- u64 dma_curp; /* dma allocation cursor */
-};
-
-struct bfa_meminfo_s {
- struct bfa_mem_elem_s meminfo[BFA_MEM_TYPE_MAX];
-};
-#define bfa_meminfo_kva(_m) \
- ((_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp)
-#define bfa_meminfo_dma_virt(_m) \
- ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp)
-#define bfa_meminfo_dma_phys(_m) \
- ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp)
-
struct bfa_iocfc_regs_s {
void __iomem *intr_status;
void __iomem *intr_mask;
void __iomem *cpe_q_pi[BFI_IOC_MAX_CQS];
void __iomem *cpe_q_ci[BFI_IOC_MAX_CQS];
- void __iomem *cpe_q_depth[BFI_IOC_MAX_CQS];
void __iomem *cpe_q_ctrl[BFI_IOC_MAX_CQS];
void __iomem *rme_q_ci[BFI_IOC_MAX_CQS];
void __iomem *rme_q_pi[BFI_IOC_MAX_CQS];
- void __iomem *rme_q_depth[BFI_IOC_MAX_CQS];
void __iomem *rme_q_ctrl[BFI_IOC_MAX_CQS];
};
@@ -229,35 +167,85 @@ struct bfa_msix_s {
struct bfa_hwif_s {
void (*hw_reginit)(struct bfa_s *bfa);
void (*hw_reqq_ack)(struct bfa_s *bfa, int reqq);
- void (*hw_rspq_ack)(struct bfa_s *bfa, int rspq);
+ void (*hw_rspq_ack)(struct bfa_s *bfa, int rspq, u32 ci);
void (*hw_msix_init)(struct bfa_s *bfa, int nvecs);
- void (*hw_msix_install)(struct bfa_s *bfa);
+ void (*hw_msix_ctrl_install)(struct bfa_s *bfa);
+ void (*hw_msix_queue_install)(struct bfa_s *bfa);
void (*hw_msix_uninstall)(struct bfa_s *bfa);
void (*hw_isr_mode_set)(struct bfa_s *bfa, bfa_boolean_t msix);
void (*hw_msix_getvecs)(struct bfa_s *bfa, u32 *vecmap,
u32 *nvecs, u32 *maxvec);
void (*hw_msix_get_rme_range) (struct bfa_s *bfa, u32 *start,
u32 *end);
+ int cpe_vec_q0;
+ int rme_vec_q0;
};
typedef void (*bfa_cb_iocfc_t) (void *cbarg, enum bfa_status status);
+struct bfa_faa_cbfn_s {
+ bfa_cb_iocfc_t faa_cbfn;
+ void *faa_cbarg;
+};
+
+#define BFA_FAA_ENABLED 1
+#define BFA_FAA_DISABLED 2
+
+/*
+ * FAA attributes
+ */
+struct bfa_faa_attr_s {
+ wwn_t faa;
+ u8 faa_state;
+ u8 pwwn_source;
+ u8 rsvd[6];
+};
+
+struct bfa_faa_args_s {
+ struct bfa_faa_attr_s *faa_attr;
+ struct bfa_faa_cbfn_s faa_cb;
+ u8 faa_state;
+ bfa_boolean_t busy;
+};
+
+/*
+ * IOCFC state machine definitions/declarations
+ */
+enum iocfc_event {
+ IOCFC_E_INIT = 1, /* IOCFC init request */
+ IOCFC_E_START = 2, /* IOCFC mod start request */
+ IOCFC_E_STOP = 3, /* IOCFC stop request */
+ IOCFC_E_ENABLE = 4, /* IOCFC enable request */
+ IOCFC_E_DISABLE = 5, /* IOCFC disable request */
+ IOCFC_E_IOC_ENABLED = 6, /* IOC enabled message */
+ IOCFC_E_IOC_DISABLED = 7, /* IOC disabled message */
+ IOCFC_E_IOC_FAILED = 8, /* failure notice by IOC sm */
+ IOCFC_E_DCONF_DONE = 9, /* dconf read/write done */
+ IOCFC_E_CFG_DONE = 10, /* IOCFC config complete */
+};
+
+struct bfa_iocfc_s;
+typedef void (*bfa_iocfs_fsm_t)(struct bfa_iocfc_s *, enum iocfc_event);
+
struct bfa_iocfc_s {
+ bfa_iocfs_fsm_t fsm;
struct bfa_s *bfa;
struct bfa_iocfc_cfg_s cfg;
- int action;
u32 req_cq_pi[BFI_IOC_MAX_CQS];
u32 rsp_cq_ci[BFI_IOC_MAX_CQS];
+ u8 hw_qid[BFI_IOC_MAX_CQS];
struct bfa_cb_qe_s init_hcb_qe;
struct bfa_cb_qe_s stop_hcb_qe;
struct bfa_cb_qe_s dis_hcb_qe;
+ struct bfa_cb_qe_s en_hcb_qe;
struct bfa_cb_qe_s stats_hcb_qe;
- bfa_boolean_t cfgdone;
+ bfa_boolean_t submod_enabled;
+ bfa_boolean_t cb_reqd; /* Driver call back reqd */
+ bfa_status_t op_status; /* Status of bfa iocfc op */
struct bfa_dma_s cfg_info;
struct bfi_iocfc_cfg_s *cfginfo;
struct bfa_dma_s cfgrsp_dma;
struct bfi_iocfc_cfgrsp_s *cfgrsp;
- struct bfi_iocfc_cfg_reply_s *cfg_reply;
struct bfa_dma_s req_cq_ba[BFI_IOC_MAX_CQS];
struct bfa_dma_s req_cq_shadow_ci[BFI_IOC_MAX_CQS];
struct bfa_dma_s rsp_cq_ba[BFI_IOC_MAX_CQS];
@@ -267,18 +255,40 @@ struct bfa_iocfc_s {
bfa_cb_iocfc_t updateq_cbfn; /* bios callback function */
void *updateq_cbarg; /* bios callback arg */
u32 intr_mask;
+ struct bfa_faa_args_s faa_args;
+ struct bfa_mem_dma_s ioc_dma;
+ struct bfa_mem_dma_s iocfc_dma;
+ struct bfa_mem_dma_s reqq_dma[BFI_IOC_MAX_CQS];
+ struct bfa_mem_dma_s rspq_dma[BFI_IOC_MAX_CQS];
+ struct bfa_mem_kva_s kva_seg;
};
-#define bfa_lpuid(__bfa) \
- bfa_ioc_portid(&(__bfa)->ioc)
+#define BFA_MEM_IOC_DMA(_bfa) (&((_bfa)->iocfc.ioc_dma))
+#define BFA_MEM_IOCFC_DMA(_bfa) (&((_bfa)->iocfc.iocfc_dma))
+#define BFA_MEM_REQQ_DMA(_bfa, _qno) (&((_bfa)->iocfc.reqq_dma[(_qno)]))
+#define BFA_MEM_RSPQ_DMA(_bfa, _qno) (&((_bfa)->iocfc.rspq_dma[(_qno)]))
+#define BFA_MEM_IOCFC_KVA(_bfa) (&((_bfa)->iocfc.kva_seg))
+
+#define bfa_fn_lpu(__bfa) \
+ bfi_fn_lpu(bfa_ioc_pcifn(&(__bfa)->ioc), bfa_ioc_portid(&(__bfa)->ioc))
#define bfa_msix_init(__bfa, __nvecs) \
((__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs))
-#define bfa_msix_install(__bfa) \
- ((__bfa)->iocfc.hwif.hw_msix_install(__bfa))
+#define bfa_msix_ctrl_install(__bfa) \
+ ((__bfa)->iocfc.hwif.hw_msix_ctrl_install(__bfa))
+#define bfa_msix_queue_install(__bfa) \
+ ((__bfa)->iocfc.hwif.hw_msix_queue_install(__bfa))
#define bfa_msix_uninstall(__bfa) \
((__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa))
-#define bfa_isr_mode_set(__bfa, __msix) \
- ((__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix))
+#define bfa_isr_rspq_ack(__bfa, __queue, __ci) \
+ ((__bfa)->iocfc.hwif.hw_rspq_ack(__bfa, __queue, __ci))
+#define bfa_isr_reqq_ack(__bfa, __queue) do { \
+ if ((__bfa)->iocfc.hwif.hw_reqq_ack) \
+ (__bfa)->iocfc.hwif.hw_reqq_ack(__bfa, __queue); \
+} while (0)
+#define bfa_isr_mode_set(__bfa, __msix) do { \
+ if ((__bfa)->iocfc.hwif.hw_isr_mode_set) \
+ (__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix); \
+} while (0)
#define bfa_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) \
((__bfa)->iocfc.hwif.hw_msix_getvecs(__bfa, __vecmap, \
__nvecs, __maxvec))
@@ -290,17 +300,17 @@ struct bfa_iocfc_s {
/*
* FC specific IOC functions.
*/
-void bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len);
+void bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg,
+ struct bfa_meminfo_s *meminfo,
+ struct bfa_s *bfa);
void bfa_iocfc_attach(struct bfa_s *bfa, void *bfad,
struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo,
struct bfa_pcidev_s *pcidev);
void bfa_iocfc_init(struct bfa_s *bfa);
void bfa_iocfc_start(struct bfa_s *bfa);
void bfa_iocfc_stop(struct bfa_s *bfa);
void bfa_iocfc_isr(void *bfa, struct bfi_mbmsg_s *msg);
-void bfa_iocfc_set_snsbase(struct bfa_s *bfa, u64 snsbase_pa);
+void bfa_iocfc_set_snsbase(struct bfa_s *bfa, int seg_no, u64 snsbase_pa);
bfa_boolean_t bfa_iocfc_is_operational(struct bfa_s *bfa);
void bfa_iocfc_reset_queues(struct bfa_s *bfa);
@@ -310,10 +320,10 @@ void bfa_msix_rspq(struct bfa_s *bfa, int vec);
void bfa_msix_lpu_err(struct bfa_s *bfa, int vec);
void bfa_hwcb_reginit(struct bfa_s *bfa);
-void bfa_hwcb_reqq_ack(struct bfa_s *bfa, int rspq);
-void bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq);
+void bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci);
void bfa_hwcb_msix_init(struct bfa_s *bfa, int nvecs);
-void bfa_hwcb_msix_install(struct bfa_s *bfa);
+void bfa_hwcb_msix_ctrl_install(struct bfa_s *bfa);
+void bfa_hwcb_msix_queue_install(struct bfa_s *bfa);
void bfa_hwcb_msix_uninstall(struct bfa_s *bfa);
void bfa_hwcb_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix);
void bfa_hwcb_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, u32 *nvecs,
@@ -321,10 +331,13 @@ void bfa_hwcb_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, u32 *nvecs,
void bfa_hwcb_msix_get_rme_range(struct bfa_s *bfa, u32 *start,
u32 *end);
void bfa_hwct_reginit(struct bfa_s *bfa);
+void bfa_hwct2_reginit(struct bfa_s *bfa);
void bfa_hwct_reqq_ack(struct bfa_s *bfa, int rspq);
-void bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq);
+void bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci);
+void bfa_hwct2_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci);
void bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs);
-void bfa_hwct_msix_install(struct bfa_s *bfa);
+void bfa_hwct_msix_ctrl_install(struct bfa_s *bfa);
+void bfa_hwct_msix_queue_install(struct bfa_s *bfa);
void bfa_hwct_msix_uninstall(struct bfa_s *bfa);
void bfa_hwct_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix);
void bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, u32 *nvecs,
@@ -332,8 +345,6 @@ void bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, u32 *nvecs,
void bfa_hwct_msix_get_rme_range(struct bfa_s *bfa, u32 *start,
u32 *end);
void bfa_iocfc_get_bootwwns(struct bfa_s *bfa, u8 *nwwns, wwn_t *wwns);
-wwn_t bfa_iocfc_get_pwwn(struct bfa_s *bfa);
-wwn_t bfa_iocfc_get_nwwn(struct bfa_s *bfa);
int bfa_iocfc_get_pbc_vports(struct bfa_s *bfa,
struct bfi_pbc_vport_s *pbc_vport);
@@ -373,11 +384,26 @@ int bfa_iocfc_get_pbc_vports(struct bfa_s *bfa,
#define bfa_get_fw_clock_res(__bfa) \
((__bfa)->iocfc.cfgrsp->fwcfg.fw_tick_res)
-void bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids);
+/*
+ * lun mask macros return NULL when min cfg is enabled and there is
+ * no memory allocated for lunmask.
+ */
+#define bfa_get_lun_mask(__bfa) \
+ ((&(__bfa)->modules.dconf_mod)->min_cfg) ? NULL : \
+ (&(BFA_DCONF_MOD(__bfa)->dconf->lun_mask))
+
+#define bfa_get_lun_mask_list(_bfa) \
+ ((&(_bfa)->modules.dconf_mod)->min_cfg) ? NULL : \
+ (bfa_get_lun_mask(_bfa)->lun_list)
+
+#define bfa_get_lun_mask_status(_bfa) \
+ (((&(_bfa)->modules.dconf_mod)->min_cfg) \
+ ? BFA_LUNMASK_MINCFG : ((bfa_get_lun_mask(_bfa))->status))
+
void bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg);
-void bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg);
void bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo);
+ struct bfa_meminfo_s *meminfo,
+ struct bfa_s *bfa);
void bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
struct bfa_meminfo_s *meminfo,
struct bfa_pcidev_s *pcidev);
@@ -405,4 +431,26 @@ void bfa_iocfc_disable(struct bfa_s *bfa);
#define bfa_timer_start(_bfa, _timer, _timercb, _arg, _timeout) \
bfa_timer_begin(&(_bfa)->timer_mod, _timer, _timercb, _arg, _timeout)
+struct bfa_cb_pending_q_s {
+ struct bfa_cb_qe_s hcb_qe;
+ void *data; /* Driver buffer */
+};
+
+/* Common macros to operate on pending stats/attr apis */
+#define bfa_pending_q_init(__qe, __cbfn, __cbarg, __data) do { \
+ bfa_q_qe_init(&((__qe)->hcb_qe.qe)); \
+ (__qe)->hcb_qe.cbfn = (__cbfn); \
+ (__qe)->hcb_qe.cbarg = (__cbarg); \
+ (__qe)->hcb_qe.pre_rmv = BFA_TRUE; \
+ (__qe)->data = (__data); \
+} while (0)
+
+#define bfa_pending_q_init_status(__qe, __cbfn, __cbarg, __data) do { \
+ bfa_q_qe_init(&((__qe)->hcb_qe.qe)); \
+ (__qe)->hcb_qe.cbfn_status = (__cbfn); \
+ (__qe)->hcb_qe.cbarg = (__cbarg); \
+ (__qe)->hcb_qe.pre_rmv = BFA_TRUE; \
+ (__qe)->data = (__data); \
+} while (0)
+
#endif /* __BFA_H__ */
diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c
index 91838c51fb76..2559df8baa05 100644
--- a/drivers/scsi/bfa/bfa_core.c
+++ b/drivers/scsi/bfa/bfa_core.c
@@ -1,47 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
#include "bfa_modules.h"
-#include "bfi_ctreg.h"
+#include "bfi_reg.h"
BFA_TRC_FILE(HAL, CORE);
/*
- * BFA module list terminated by NULL
- */
-static struct bfa_module_s *hal_mods[] = {
- &hal_mod_sgpg,
- &hal_mod_fcport,
- &hal_mod_fcxp,
- &hal_mod_lps,
- &hal_mod_uf,
- &hal_mod_rport,
- &hal_mod_fcpim,
- NULL
-};
-
-/*
* Message handlers for various modules.
*/
static bfa_isr_func_t bfa_isrs[BFI_MC_MAX] = {
bfa_isr_unhandled, /* NONE */
bfa_isr_unhandled, /* BFI_MC_IOC */
- bfa_isr_unhandled, /* BFI_MC_DIAG */
+ bfa_fcdiag_intr, /* BFI_MC_DIAG */
bfa_isr_unhandled, /* BFI_MC_FLASH */
bfa_isr_unhandled, /* BFI_MC_CEE */
bfa_fcport_isr, /* BFI_MC_FCPORT */
@@ -51,7 +30,7 @@ static bfa_isr_func_t bfa_isrs[BFI_MC_MAX] = {
bfa_fcxp_isr, /* BFI_MC_FCXP */
bfa_lps_isr, /* BFI_MC_LPS */
bfa_rport_isr, /* BFI_MC_RPORT */
- bfa_itnim_isr, /* BFI_MC_ITNIM */
+ bfa_itn_isr, /* BFI_MC_ITN */
bfa_isr_unhandled, /* BFI_MC_IOIM_READ */
bfa_isr_unhandled, /* BFI_MC_IOIM_WRITE */
bfa_isr_unhandled, /* BFI_MC_IOIM_IO */
@@ -88,24 +67,108 @@ static bfa_ioc_mbox_mcfunc_t bfa_mbox_isrs[BFI_MC_MAX] = {
+void
+__bfa_trc(struct bfa_trc_mod_s *trcm, int fileno, int line, u64 data)
+{
+ int tail = trcm->tail;
+ struct bfa_trc_s *trc = &trcm->trc[tail];
+
+ if (trcm->stopped)
+ return;
+
+ trc->fileno = (u16) fileno;
+ trc->line = (u16) line;
+ trc->data.u64 = data;
+ trc->timestamp = BFA_TRC_TS(trcm);
+
+ trcm->tail = (trcm->tail + 1) & (BFA_TRC_MAX - 1);
+ if (trcm->tail == trcm->head)
+ trcm->head = (trcm->head + 1) & (BFA_TRC_MAX - 1);
+}
+
static void
-bfa_com_port_attach(struct bfa_s *bfa, struct bfa_meminfo_s *mi)
+bfa_com_port_attach(struct bfa_s *bfa)
{
struct bfa_port_s *port = &bfa->modules.port;
- u32 dm_len;
- u8 *dm_kva;
- u64 dm_pa;
+ struct bfa_mem_dma_s *port_dma = BFA_MEM_PORT_DMA(bfa);
- dm_len = bfa_port_meminfo();
- dm_kva = bfa_meminfo_dma_virt(mi);
- dm_pa = bfa_meminfo_dma_phys(mi);
-
- memset(port, 0, sizeof(struct bfa_port_s));
bfa_port_attach(port, &bfa->ioc, bfa, bfa->trcmod);
- bfa_port_mem_claim(port, dm_kva, dm_pa);
+ bfa_port_mem_claim(port, port_dma->kva_curp, port_dma->dma_curp);
+}
+
+/*
+ * ablk module attach
+ */
+static void
+bfa_com_ablk_attach(struct bfa_s *bfa)
+{
+ struct bfa_ablk_s *ablk = &bfa->modules.ablk;
+ struct bfa_mem_dma_s *ablk_dma = BFA_MEM_ABLK_DMA(bfa);
+
+ bfa_ablk_attach(ablk, &bfa->ioc);
+ bfa_ablk_memclaim(ablk, ablk_dma->kva_curp, ablk_dma->dma_curp);
+}
+
+static void
+bfa_com_cee_attach(struct bfa_s *bfa)
+{
+ struct bfa_cee_s *cee = &bfa->modules.cee;
+ struct bfa_mem_dma_s *cee_dma = BFA_MEM_CEE_DMA(bfa);
+
+ cee->trcmod = bfa->trcmod;
+ bfa_cee_attach(cee, &bfa->ioc, bfa);
+ bfa_cee_mem_claim(cee, cee_dma->kva_curp, cee_dma->dma_curp);
+}
+
+static void
+bfa_com_sfp_attach(struct bfa_s *bfa)
+{
+ struct bfa_sfp_s *sfp = BFA_SFP_MOD(bfa);
+ struct bfa_mem_dma_s *sfp_dma = BFA_MEM_SFP_DMA(bfa);
+
+ bfa_sfp_attach(sfp, &bfa->ioc, bfa, bfa->trcmod);
+ bfa_sfp_memclaim(sfp, sfp_dma->kva_curp, sfp_dma->dma_curp);
+}
+
+static void
+bfa_com_flash_attach(struct bfa_s *bfa, bfa_boolean_t mincfg)
+{
+ struct bfa_flash_s *flash = BFA_FLASH(bfa);
+ struct bfa_mem_dma_s *flash_dma = BFA_MEM_FLASH_DMA(bfa);
- bfa_meminfo_dma_virt(mi) = dm_kva + dm_len;
- bfa_meminfo_dma_phys(mi) = dm_pa + dm_len;
+ bfa_flash_attach(flash, &bfa->ioc, bfa, bfa->trcmod, mincfg);
+ bfa_flash_memclaim(flash, flash_dma->kva_curp,
+ flash_dma->dma_curp, mincfg);
+}
+
+static void
+bfa_com_diag_attach(struct bfa_s *bfa)
+{
+ struct bfa_diag_s *diag = BFA_DIAG_MOD(bfa);
+ struct bfa_mem_dma_s *diag_dma = BFA_MEM_DIAG_DMA(bfa);
+
+ bfa_diag_attach(diag, &bfa->ioc, bfa, bfa_fcport_beacon, bfa->trcmod);
+ bfa_diag_memclaim(diag, diag_dma->kva_curp, diag_dma->dma_curp);
+}
+
+static void
+bfa_com_phy_attach(struct bfa_s *bfa, bfa_boolean_t mincfg)
+{
+ struct bfa_phy_s *phy = BFA_PHY(bfa);
+ struct bfa_mem_dma_s *phy_dma = BFA_MEM_PHY_DMA(bfa);
+
+ bfa_phy_attach(phy, &bfa->ioc, bfa, bfa->trcmod, mincfg);
+ bfa_phy_memclaim(phy, phy_dma->kva_curp, phy_dma->dma_curp, mincfg);
+}
+
+static void
+bfa_com_fru_attach(struct bfa_s *bfa, bfa_boolean_t mincfg)
+{
+ struct bfa_fru_s *fru = BFA_FRU(bfa);
+ struct bfa_mem_dma_s *fru_dma = BFA_MEM_FRU_DMA(bfa);
+
+ bfa_fru_attach(fru, &bfa->ioc, bfa, bfa->trcmod, mincfg);
+ bfa_fru_memclaim(fru, fru_dma->kva_curp, fru_dma->dma_curp, mincfg);
}
/*
@@ -122,6 +185,7 @@ enum {
BFA_IOCFC_ACT_INIT = 1,
BFA_IOCFC_ACT_STOP = 2,
BFA_IOCFC_ACT_DISABLE = 3,
+ BFA_IOCFC_ACT_ENABLE = 4,
};
#define DEF_CFG_NUM_FABRICS 1
@@ -142,13 +206,484 @@ enum {
#define DEF_CFG_NUM_SBOOT_LUNS 16
/*
+ * IOCFC state machine definitions/declarations
+ */
+bfa_fsm_state_decl(bfa_iocfc, stopped, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, initing, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, dconf_read, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_cfg_wait,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_cfg_done,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, operational,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, dconf_write,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, stopping, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, enabling, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, cfg_wait, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, disabling, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, disabled, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, failed, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_failed,
+ struct bfa_iocfc_s, enum iocfc_event);
+
+/*
* forward declaration for IOC FC functions
*/
+static void bfa_iocfc_start_submod(struct bfa_s *bfa);
+static void bfa_iocfc_disable_submod(struct bfa_s *bfa);
+static void bfa_iocfc_send_cfg(void *bfa_arg);
static void bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status);
static void bfa_iocfc_disable_cbfn(void *bfa_arg);
static void bfa_iocfc_hbfail_cbfn(void *bfa_arg);
static void bfa_iocfc_reset_cbfn(void *bfa_arg);
static struct bfa_ioc_cbfn_s bfa_iocfc_cbfn;
+static void bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete);
+static void bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl);
+static void bfa_iocfc_enable_cb(void *bfa_arg, bfa_boolean_t compl);
+static void bfa_iocfc_disable_cb(void *bfa_arg, bfa_boolean_t compl);
+
+static void
+bfa_iocfc_sm_stopped_entry(struct bfa_iocfc_s *iocfc)
+{
+}
+
+static void
+bfa_iocfc_sm_stopped(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_INIT:
+ case IOCFC_E_ENABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_initing);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_initing_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_enable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_initing(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_read);
+ break;
+
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_dconf_read_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_dconf_modinit(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_dconf_read(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_DCONF_DONE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_wait);
+ break;
+
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_init_cfg_wait_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_iocfc_send_cfg(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_CFG_DONE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_done);
+ break;
+
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_init_cfg_done_entry(struct bfa_iocfc_s *iocfc)
+{
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.init_hcb_qe,
+ bfa_iocfc_init_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_cfg_done(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_START:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_operational);
+ break;
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_operational_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_fcport_init(iocfc->bfa);
+ bfa_iocfc_start_submod(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_operational(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_dconf_write_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_dconf_modexit(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_dconf_write(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_DCONF_DONE:
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_stopping_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_disable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_stopping(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_DISABLED:
+ bfa_isr_disable(iocfc->bfa);
+ bfa_iocfc_disable_submod(iocfc->bfa);
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopped);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.stop_hcb_qe,
+ bfa_iocfc_stop_cb, iocfc->bfa);
+ break;
+
+ case IOCFC_E_IOC_ENABLED:
+ case IOCFC_E_DCONF_DONE:
+ case IOCFC_E_CFG_DONE:
+ break;
+
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_enabling_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_enable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_enabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_cfg_wait);
+ break;
+
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+
+ if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+ break;
+
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+ bfa_iocfc_enable_cb, iocfc->bfa);
+ iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_cfg_wait_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_iocfc_send_cfg(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_CFG_DONE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_operational);
+ if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+ break;
+
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+ bfa_iocfc_enable_cb, iocfc->bfa);
+ iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+ if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+ break;
+
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+ bfa_iocfc_enable_cb, iocfc->bfa);
+ iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_disabling_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_disable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_disabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_DISABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabled);
+ break;
+ case IOCFC_E_IOC_ENABLED:
+ case IOCFC_E_DCONF_DONE:
+ case IOCFC_E_CFG_DONE:
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_disabled_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_isr_disable(iocfc->bfa);
+ bfa_iocfc_disable_submod(iocfc->bfa);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.dis_hcb_qe,
+ bfa_iocfc_disable_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_disabled(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+ case IOCFC_E_ENABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_enabling);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_failed_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_isr_disable(iocfc->bfa);
+ bfa_iocfc_disable_submod(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_failed(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_cfg_wait);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_init_failed_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_isr_disable(iocfc->bfa);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.init_hcb_qe,
+ bfa_iocfc_init_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_failed(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_ioc_disable(&iocfc->bfa->ioc);
+ break;
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_read);
+ break;
+ case IOCFC_E_IOC_DISABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopped);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.dis_hcb_qe,
+ bfa_iocfc_disable_cb, iocfc->bfa);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
/*
* BFA Interrupt handling functions
@@ -173,10 +708,93 @@ bfa_reqq_resume(struct bfa_s *bfa, int qid)
}
}
+static bfa_boolean_t
+bfa_isr_rspq(struct bfa_s *bfa, int qid)
+{
+ struct bfi_msg_s *m;
+ u32 pi, ci;
+ struct list_head *waitq;
+ bfa_boolean_t ret;
+
+ ci = bfa_rspq_ci(bfa, qid);
+ pi = bfa_rspq_pi(bfa, qid);
+
+ ret = (ci != pi);
+
+ while (ci != pi) {
+ m = bfa_rspq_elem(bfa, qid, ci);
+ WARN_ON(m->mhdr.msg_class >= BFI_MC_MAX);
+
+ bfa_isrs[m->mhdr.msg_class] (bfa, m);
+ CQ_INCR(ci, bfa->iocfc.cfg.drvcfg.num_rspq_elems);
+ }
+
+ /*
+ * acknowledge RME completions and update CI
+ */
+ bfa_isr_rspq_ack(bfa, qid, ci);
+
+ /*
+ * Resume any pending requests in the corresponding reqq.
+ */
+ waitq = bfa_reqq(bfa, qid);
+ if (!list_empty(waitq))
+ bfa_reqq_resume(bfa, qid);
+
+ return ret;
+}
+
+static inline void
+bfa_isr_reqq(struct bfa_s *bfa, int qid)
+{
+ struct list_head *waitq;
+
+ bfa_isr_reqq_ack(bfa, qid);
+
+ /*
+ * Resume any pending requests in the corresponding reqq.
+ */
+ waitq = bfa_reqq(bfa, qid);
+ if (!list_empty(waitq))
+ bfa_reqq_resume(bfa, qid);
+}
+
void
bfa_msix_all(struct bfa_s *bfa, int vec)
{
- bfa_intx(bfa);
+ u32 intr, qintr;
+ int queue;
+
+ intr = readl(bfa->iocfc.bfa_regs.intr_status);
+ if (!intr)
+ return;
+
+ /*
+ * RME completion queue interrupt
+ */
+ qintr = intr & __HFN_INT_RME_MASK;
+ if (qintr && bfa->queue_process) {
+ for (queue = 0; queue < BFI_IOC_MAX_CQS; queue++)
+ bfa_isr_rspq(bfa, queue);
+ }
+
+ intr &= ~qintr;
+ if (!intr)
+ return;
+
+ /*
+ * CPE completion queue interrupt
+ */
+ qintr = intr & __HFN_INT_CPE_MASK;
+ if (qintr && bfa->queue_process) {
+ for (queue = 0; queue < BFI_IOC_MAX_CQS; queue++)
+ bfa_isr_reqq(bfa, queue);
+ }
+ intr &= ~qintr;
+ if (!intr)
+ return;
+
+ bfa_msix_lpu_err(bfa, intr);
}
bfa_boolean_t
@@ -184,40 +802,40 @@ bfa_intx(struct bfa_s *bfa)
{
u32 intr, qintr;
int queue;
+ bfa_boolean_t rspq_comp = BFA_FALSE;
intr = readl(bfa->iocfc.bfa_regs.intr_status);
- if (!intr)
- return BFA_FALSE;
+
+ qintr = intr & (__HFN_INT_RME_MASK | __HFN_INT_CPE_MASK);
+ if (qintr)
+ writel(qintr, bfa->iocfc.bfa_regs.intr_status);
/*
- * RME completion queue interrupt
+ * Unconditional RME completion queue interrupt
*/
- qintr = intr & __HFN_INT_RME_MASK;
- writel(qintr, bfa->iocfc.bfa_regs.intr_status);
-
- for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) {
- if (intr & (__HFN_INT_RME_Q0 << queue))
- bfa_msix_rspq(bfa, queue & (BFI_IOC_MAX_CQS - 1));
+ if (bfa->queue_process) {
+ for (queue = 0; queue < BFI_IOC_MAX_CQS; queue++)
+ if (bfa_isr_rspq(bfa, queue))
+ rspq_comp = BFA_TRUE;
}
- intr &= ~qintr;
+
if (!intr)
- return BFA_TRUE;
+ return (qintr | rspq_comp) ? BFA_TRUE : BFA_FALSE;
/*
* CPE completion queue interrupt
*/
qintr = intr & __HFN_INT_CPE_MASK;
- writel(qintr, bfa->iocfc.bfa_regs.intr_status);
-
- for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) {
- if (intr & (__HFN_INT_CPE_Q0 << queue))
- bfa_msix_reqq(bfa, queue & (BFI_IOC_MAX_CQS - 1));
+ if (qintr && bfa->queue_process) {
+ for (queue = 0; queue < BFI_IOC_MAX_CQS; queue++)
+ bfa_isr_reqq(bfa, queue);
}
intr &= ~qintr;
if (!intr)
return BFA_TRUE;
- bfa_msix_lpu_err(bfa, intr);
+ if (bfa->intr_enabled)
+ bfa_msix_lpu_err(bfa, intr);
return BFA_TRUE;
}
@@ -225,58 +843,47 @@ bfa_intx(struct bfa_s *bfa)
void
bfa_isr_enable(struct bfa_s *bfa)
{
- u32 intr_unmask;
- int pci_func = bfa_ioc_pcifn(&bfa->ioc);
+ u32 umsk;
+ int port_id = bfa_ioc_portid(&bfa->ioc);
- bfa_trc(bfa, pci_func);
+ bfa_trc(bfa, bfa_ioc_pcifn(&bfa->ioc));
+ bfa_trc(bfa, port_id);
- bfa_msix_install(bfa);
- intr_unmask = (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 |
- __HFN_INT_ERR_LPU1 | __HFN_INT_ERR_PSS |
- __HFN_INT_LL_HALT);
+ bfa_msix_ctrl_install(bfa);
- if (pci_func == 0)
- intr_unmask |= (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 |
- __HFN_INT_CPE_Q2 | __HFN_INT_CPE_Q3 |
- __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 |
- __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 |
- __HFN_INT_MBOX_LPU0);
- else
- intr_unmask |= (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 |
- __HFN_INT_CPE_Q6 | __HFN_INT_CPE_Q7 |
- __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 |
- __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 |
- __HFN_INT_MBOX_LPU1);
-
- writel(intr_unmask, bfa->iocfc.bfa_regs.intr_status);
- writel(~intr_unmask, bfa->iocfc.bfa_regs.intr_mask);
- bfa->iocfc.intr_mask = ~intr_unmask;
+ if (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)) {
+ umsk = __HFN_INT_ERR_MASK_CT2;
+ umsk |= port_id == 0 ?
+ __HFN_INT_FN0_MASK_CT2 : __HFN_INT_FN1_MASK_CT2;
+ } else {
+ umsk = __HFN_INT_ERR_MASK;
+ umsk |= port_id == 0 ? __HFN_INT_FN0_MASK : __HFN_INT_FN1_MASK;
+ }
+
+ writel(umsk, bfa->iocfc.bfa_regs.intr_status);
+ writel(~umsk, bfa->iocfc.bfa_regs.intr_mask);
+ bfa->iocfc.intr_mask = ~umsk;
bfa_isr_mode_set(bfa, bfa->msix.nvecs != 0);
+
+ /*
+ * Set the flag indicating successful enabling of interrupts
+ */
+ bfa->intr_enabled = BFA_TRUE;
}
void
bfa_isr_disable(struct bfa_s *bfa)
{
+ bfa->intr_enabled = BFA_FALSE;
bfa_isr_mode_set(bfa, BFA_FALSE);
writel(-1L, bfa->iocfc.bfa_regs.intr_mask);
bfa_msix_uninstall(bfa);
}
void
-bfa_msix_reqq(struct bfa_s *bfa, int qid)
+bfa_msix_reqq(struct bfa_s *bfa, int vec)
{
- struct list_head *waitq;
-
- qid &= (BFI_IOC_MAX_CQS - 1);
-
- bfa->iocfc.hwif.hw_reqq_ack(bfa, qid);
-
- /*
- * Resume any pending requests in the corresponding reqq.
- */
- waitq = bfa_reqq(bfa, qid);
- if (!list_empty(waitq))
- bfa_reqq_resume(bfa, qid);
+ bfa_isr_reqq(bfa, vec - bfa->iocfc.hwif.cpe_vec_q0);
}
void
@@ -290,57 +897,38 @@ bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m)
}
void
-bfa_msix_rspq(struct bfa_s *bfa, int qid)
+bfa_msix_rspq(struct bfa_s *bfa, int vec)
{
- struct bfi_msg_s *m;
- u32 pi, ci;
- struct list_head *waitq;
-
- qid &= (BFI_IOC_MAX_CQS - 1);
-
- bfa->iocfc.hwif.hw_rspq_ack(bfa, qid);
-
- ci = bfa_rspq_ci(bfa, qid);
- pi = bfa_rspq_pi(bfa, qid);
-
- if (bfa->rme_process) {
- while (ci != pi) {
- m = bfa_rspq_elem(bfa, qid, ci);
- bfa_isrs[m->mhdr.msg_class] (bfa, m);
- CQ_INCR(ci, bfa->iocfc.cfg.drvcfg.num_rspq_elems);
- }
- }
-
- /*
- * update CI
- */
- bfa_rspq_ci(bfa, qid) = pi;
- writel(pi, bfa->iocfc.bfa_regs.rme_q_ci[qid]);
- mmiowb();
-
- /*
- * Resume any pending requests in the corresponding reqq.
- */
- waitq = bfa_reqq(bfa, qid);
- if (!list_empty(waitq))
- bfa_reqq_resume(bfa, qid);
+ bfa_isr_rspq(bfa, vec - bfa->iocfc.hwif.rme_vec_q0);
}
void
bfa_msix_lpu_err(struct bfa_s *bfa, int vec)
{
u32 intr, curr_value;
+ bfa_boolean_t lpu_isr, halt_isr, pss_isr;
intr = readl(bfa->iocfc.bfa_regs.intr_status);
- if (intr & (__HFN_INT_MBOX_LPU0 | __HFN_INT_MBOX_LPU1))
- bfa_ioc_mbox_isr(&bfa->ioc);
+ if (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)) {
+ halt_isr = intr & __HFN_INT_CPQ_HALT_CT2;
+ pss_isr = intr & __HFN_INT_ERR_PSS_CT2;
+ lpu_isr = intr & (__HFN_INT_MBOX_LPU0_CT2 |
+ __HFN_INT_MBOX_LPU1_CT2);
+ intr &= __HFN_INT_ERR_MASK_CT2;
+ } else {
+ halt_isr = bfa_asic_id_ct(bfa->ioc.pcidev.device_id) ?
+ (intr & __HFN_INT_LL_HALT) : 0;
+ pss_isr = intr & __HFN_INT_ERR_PSS;
+ lpu_isr = intr & (__HFN_INT_MBOX_LPU0 | __HFN_INT_MBOX_LPU1);
+ intr &= __HFN_INT_ERR_MASK;
+ }
- intr &= (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 |
- __HFN_INT_ERR_LPU1 | __HFN_INT_ERR_PSS | __HFN_INT_LL_HALT);
+ if (lpu_isr)
+ bfa_ioc_mbox_isr(&bfa->ioc);
if (intr) {
- if (intr & __HFN_INT_LL_HALT) {
+ if (halt_isr) {
/*
* If LL_HALT bit is set then FW Init Halt LL Port
* Register needs to be cleared as well so Interrupt
@@ -351,7 +939,7 @@ bfa_msix_lpu_err(struct bfa_s *bfa, int vec)
writel(curr_value, bfa->ioc.ioc_regs.ll_halt);
}
- if (intr & __HFN_INT_ERR_PSS) {
+ if (pss_isr) {
/*
* ERR_PSS bit needs to be cleared as well in case
* interrups are shared so driver's interrupt handler is
@@ -359,7 +947,6 @@ bfa_msix_lpu_err(struct bfa_s *bfa, int vec)
*/
curr_value = readl(
bfa->ioc.ioc_regs.pss_err_status_reg);
- curr_value &= __PSS_ERR_STATUS_SET;
writel(curr_value,
bfa->ioc.ioc_regs.pss_err_status_reg);
}
@@ -377,41 +964,6 @@ bfa_msix_lpu_err(struct bfa_s *bfa, int vec)
* BFA IOC private functions
*/
-static void
-bfa_iocfc_cqs_sz(struct bfa_iocfc_cfg_s *cfg, u32 *dm_len)
-{
- int i, per_reqq_sz, per_rspq_sz;
-
- per_reqq_sz = BFA_ROUNDUP((cfg->drvcfg.num_reqq_elems * BFI_LMSG_SZ),
- BFA_DMA_ALIGN_SZ);
- per_rspq_sz = BFA_ROUNDUP((cfg->drvcfg.num_rspq_elems * BFI_LMSG_SZ),
- BFA_DMA_ALIGN_SZ);
-
- /*
- * Calculate CQ size
- */
- for (i = 0; i < cfg->fwcfg.num_cqs; i++) {
- *dm_len = *dm_len + per_reqq_sz;
- *dm_len = *dm_len + per_rspq_sz;
- }
-
- /*
- * Calculate Shadow CI/PI size
- */
- for (i = 0; i < cfg->fwcfg.num_cqs; i++)
- *dm_len += (2 * BFA_CACHELINE_SZ);
-}
-
-static void
-bfa_iocfc_fw_cfg_sz(struct bfa_iocfc_cfg_s *cfg, u32 *dm_len)
-{
- *dm_len +=
- BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ);
- *dm_len +=
- BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s),
- BFA_CACHELINE_SZ);
-}
-
/*
* Use the Mailbox interface to send BFI_IOCFC_H2I_CFG_REQ
*/
@@ -433,8 +985,14 @@ bfa_iocfc_send_cfg(void *bfa_arg)
/*
* initialize IOC configuration info
*/
+ cfg_info->single_msix_vec = 0;
+ if (bfa->msix.nvecs == 1)
+ cfg_info->single_msix_vec = 1;
cfg_info->endian_sig = BFI_IOC_ENDIAN_SIG;
cfg_info->num_cqs = cfg->fwcfg.num_cqs;
+ cfg_info->num_ioim_reqs = cpu_to_be16(bfa_fcpim_get_throttle_cfg(bfa,
+ cfg->fwcfg.num_ioim_reqs));
+ cfg_info->num_fwtio_reqs = cpu_to_be16(cfg->fwcfg.num_fwtio_reqs);
bfa_dma_be_addr_set(cfg_info->cfgrsp_addr, iocfc->cfgrsp_dma.pa);
/*
@@ -460,16 +1018,14 @@ bfa_iocfc_send_cfg(void *bfa_arg)
* Enable interrupt coalescing if it is driver init path
* and not ioc disable/enable path.
*/
- if (!iocfc->cfgdone)
+ if (bfa_fsm_cmp_state(iocfc, bfa_iocfc_sm_init_cfg_wait))
cfg_info->intr_attr.coalesce = BFA_TRUE;
- iocfc->cfgdone = BFA_FALSE;
-
/*
* dma map IOC configuration itself
*/
bfi_h2i_set(cfg_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_CFG_REQ,
- bfa_lpuid(bfa));
+ bfa_fn_lpu(bfa));
bfa_dma_be_addr_set(cfg_req.ioc_cfg_dma_addr, iocfc->cfg_info.pa);
bfa_ioc_mbox_send(&bfa->ioc, &cfg_req,
@@ -484,33 +1040,45 @@ bfa_iocfc_init_mem(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
bfa->bfad = bfad;
iocfc->bfa = bfa;
- iocfc->action = BFA_IOCFC_ACT_NONE;
-
iocfc->cfg = *cfg;
/*
* Initialize chip specific handlers.
*/
- if (bfa_asic_id_ct(bfa_ioc_devid(&bfa->ioc))) {
+ if (bfa_asic_id_ctc(bfa_ioc_devid(&bfa->ioc))) {
iocfc->hwif.hw_reginit = bfa_hwct_reginit;
iocfc->hwif.hw_reqq_ack = bfa_hwct_reqq_ack;
iocfc->hwif.hw_rspq_ack = bfa_hwct_rspq_ack;
iocfc->hwif.hw_msix_init = bfa_hwct_msix_init;
- iocfc->hwif.hw_msix_install = bfa_hwct_msix_install;
+ iocfc->hwif.hw_msix_ctrl_install = bfa_hwct_msix_ctrl_install;
+ iocfc->hwif.hw_msix_queue_install = bfa_hwct_msix_queue_install;
iocfc->hwif.hw_msix_uninstall = bfa_hwct_msix_uninstall;
iocfc->hwif.hw_isr_mode_set = bfa_hwct_isr_mode_set;
iocfc->hwif.hw_msix_getvecs = bfa_hwct_msix_getvecs;
iocfc->hwif.hw_msix_get_rme_range = bfa_hwct_msix_get_rme_range;
+ iocfc->hwif.rme_vec_q0 = BFI_MSIX_RME_QMIN_CT;
+ iocfc->hwif.cpe_vec_q0 = BFI_MSIX_CPE_QMIN_CT;
} else {
iocfc->hwif.hw_reginit = bfa_hwcb_reginit;
- iocfc->hwif.hw_reqq_ack = bfa_hwcb_reqq_ack;
+ iocfc->hwif.hw_reqq_ack = NULL;
iocfc->hwif.hw_rspq_ack = bfa_hwcb_rspq_ack;
iocfc->hwif.hw_msix_init = bfa_hwcb_msix_init;
- iocfc->hwif.hw_msix_install = bfa_hwcb_msix_install;
+ iocfc->hwif.hw_msix_ctrl_install = bfa_hwcb_msix_ctrl_install;
+ iocfc->hwif.hw_msix_queue_install = bfa_hwcb_msix_queue_install;
iocfc->hwif.hw_msix_uninstall = bfa_hwcb_msix_uninstall;
iocfc->hwif.hw_isr_mode_set = bfa_hwcb_isr_mode_set;
iocfc->hwif.hw_msix_getvecs = bfa_hwcb_msix_getvecs;
iocfc->hwif.hw_msix_get_rme_range = bfa_hwcb_msix_get_rme_range;
+ iocfc->hwif.rme_vec_q0 = BFI_MSIX_RME_QMIN_CB +
+ bfa_ioc_pcifn(&bfa->ioc) * BFI_IOC_MAX_CQS;
+ iocfc->hwif.cpe_vec_q0 = BFI_MSIX_CPE_QMIN_CB +
+ bfa_ioc_pcifn(&bfa->ioc) * BFI_IOC_MAX_CQS;
+ }
+
+ if (bfa_asic_id_ct2(bfa_ioc_devid(&bfa->ioc))) {
+ iocfc->hwif.hw_reginit = bfa_hwct2_reginit;
+ iocfc->hwif.hw_isr_mode_set = NULL;
+ iocfc->hwif.hw_rspq_ack = bfa_hwct2_rspq_ack;
}
iocfc->hwif.hw_reginit(bfa);
@@ -518,48 +1086,42 @@ bfa_iocfc_init_mem(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
}
static void
-bfa_iocfc_mem_claim(struct bfa_s *bfa, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo)
+bfa_iocfc_mem_claim(struct bfa_s *bfa, struct bfa_iocfc_cfg_s *cfg)
{
- u8 *dm_kva;
- u64 dm_pa;
- int i, per_reqq_sz, per_rspq_sz;
+ u8 *dm_kva = NULL;
+ u64 dm_pa = 0;
+ int i, per_reqq_sz, per_rspq_sz;
struct bfa_iocfc_s *iocfc = &bfa->iocfc;
- int dbgsz;
+ struct bfa_mem_dma_s *ioc_dma = BFA_MEM_IOC_DMA(bfa);
+ struct bfa_mem_dma_s *iocfc_dma = BFA_MEM_IOCFC_DMA(bfa);
+ struct bfa_mem_dma_s *reqq_dma, *rspq_dma;
- dm_kva = bfa_meminfo_dma_virt(meminfo);
- dm_pa = bfa_meminfo_dma_phys(meminfo);
+ /* First allocate dma memory for IOC */
+ bfa_ioc_mem_claim(&bfa->ioc, bfa_mem_dma_virt(ioc_dma),
+ bfa_mem_dma_phys(ioc_dma));
- /*
- * First allocate dma memory for IOC.
- */
- bfa_ioc_mem_claim(&bfa->ioc, dm_kva, dm_pa);
- dm_kva += BFA_ROUNDUP(sizeof(struct bfi_ioc_attr_s), BFA_DMA_ALIGN_SZ);
- dm_pa += BFA_ROUNDUP(sizeof(struct bfi_ioc_attr_s), BFA_DMA_ALIGN_SZ);
-
- /*
- * Claim DMA-able memory for the request/response queues and for shadow
- * ci/pi registers
- */
+ /* Claim DMA-able memory for the request/response queues */
per_reqq_sz = BFA_ROUNDUP((cfg->drvcfg.num_reqq_elems * BFI_LMSG_SZ),
- BFA_DMA_ALIGN_SZ);
+ BFA_DMA_ALIGN_SZ);
per_rspq_sz = BFA_ROUNDUP((cfg->drvcfg.num_rspq_elems * BFI_LMSG_SZ),
- BFA_DMA_ALIGN_SZ);
+ BFA_DMA_ALIGN_SZ);
for (i = 0; i < cfg->fwcfg.num_cqs; i++) {
- iocfc->req_cq_ba[i].kva = dm_kva;
- iocfc->req_cq_ba[i].pa = dm_pa;
- memset(dm_kva, 0, per_reqq_sz);
- dm_kva += per_reqq_sz;
- dm_pa += per_reqq_sz;
-
- iocfc->rsp_cq_ba[i].kva = dm_kva;
- iocfc->rsp_cq_ba[i].pa = dm_pa;
- memset(dm_kva, 0, per_rspq_sz);
- dm_kva += per_rspq_sz;
- dm_pa += per_rspq_sz;
+ reqq_dma = BFA_MEM_REQQ_DMA(bfa, i);
+ iocfc->req_cq_ba[i].kva = bfa_mem_dma_virt(reqq_dma);
+ iocfc->req_cq_ba[i].pa = bfa_mem_dma_phys(reqq_dma);
+ memset(iocfc->req_cq_ba[i].kva, 0, per_reqq_sz);
+
+ rspq_dma = BFA_MEM_RSPQ_DMA(bfa, i);
+ iocfc->rsp_cq_ba[i].kva = bfa_mem_dma_virt(rspq_dma);
+ iocfc->rsp_cq_ba[i].pa = bfa_mem_dma_phys(rspq_dma);
+ memset(iocfc->rsp_cq_ba[i].kva, 0, per_rspq_sz);
}
+ /* Claim IOCFC dma memory - for shadow CI/PI */
+ dm_kva = bfa_mem_dma_virt(iocfc_dma);
+ dm_pa = bfa_mem_dma_phys(iocfc_dma);
+
for (i = 0; i < cfg->fwcfg.num_cqs; i++) {
iocfc->req_cq_shadow_ci[i].kva = dm_kva;
iocfc->req_cq_shadow_ci[i].pa = dm_pa;
@@ -572,37 +1134,25 @@ bfa_iocfc_mem_claim(struct bfa_s *bfa, struct bfa_iocfc_cfg_s *cfg,
dm_pa += BFA_CACHELINE_SZ;
}
- /*
- * Claim DMA-able memory for the config info page
- */
+ /* Claim IOCFC dma memory - for the config info page */
bfa->iocfc.cfg_info.kva = dm_kva;
bfa->iocfc.cfg_info.pa = dm_pa;
bfa->iocfc.cfginfo = (struct bfi_iocfc_cfg_s *) dm_kva;
dm_kva += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ);
dm_pa += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ);
- /*
- * Claim DMA-able memory for the config response
- */
+ /* Claim IOCFC dma memory - for the config response */
bfa->iocfc.cfgrsp_dma.kva = dm_kva;
bfa->iocfc.cfgrsp_dma.pa = dm_pa;
bfa->iocfc.cfgrsp = (struct bfi_iocfc_cfgrsp_s *) dm_kva;
-
- dm_kva +=
- BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s),
- BFA_CACHELINE_SZ);
+ dm_kva += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s),
+ BFA_CACHELINE_SZ);
dm_pa += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s),
- BFA_CACHELINE_SZ);
+ BFA_CACHELINE_SZ);
-
- bfa_meminfo_dma_virt(meminfo) = dm_kva;
- bfa_meminfo_dma_phys(meminfo) = dm_pa;
-
- dbgsz = (bfa_auto_recover) ? BFA_DBG_FWTRC_LEN : 0;
- if (dbgsz > 0) {
- bfa_ioc_debug_memclaim(&bfa->ioc, bfa_meminfo_kva(meminfo));
- bfa_meminfo_kva(meminfo) += dbgsz;
- }
+ /* Claim IOCFC kva memory */
+ bfa_ioc_debug_memclaim(&bfa->ioc, bfa_mem_kva_curp(iocfc));
+ bfa_mem_kva_curp(iocfc) += BFA_DBG_FWTRC_LEN;
}
/*
@@ -613,10 +1163,19 @@ bfa_iocfc_start_submod(struct bfa_s *bfa)
{
int i;
- bfa->rme_process = BFA_TRUE;
+ bfa->queue_process = BFA_TRUE;
+ for (i = 0; i < BFI_IOC_MAX_CQS; i++)
+ bfa_isr_rspq_ack(bfa, i, bfa_rspq_ci(bfa, i));
- for (i = 0; hal_mods[i]; i++)
- hal_mods[i]->start(bfa);
+ bfa_fcport_start(bfa);
+ bfa_uf_start(bfa);
+ /*
+ * bfa_init() with flash read is complete. now invalidate the stale
+ * content of lun mask like unit attention, rp tag and lp tag.
+ */
+ bfa_ioim_lm_init(BFA_FCP_MOD(bfa)->bfa);
+
+ bfa->iocfc.submod_enabled = BFA_TRUE;
}
/*
@@ -625,10 +1184,18 @@ bfa_iocfc_start_submod(struct bfa_s *bfa)
static void
bfa_iocfc_disable_submod(struct bfa_s *bfa)
{
- int i;
+ if (bfa->iocfc.submod_enabled == BFA_FALSE)
+ return;
- for (i = 0; hal_mods[i]; i++)
- hal_mods[i]->iocdisable(bfa);
+ bfa_fcdiag_iocdisable(bfa);
+ bfa_fcport_iocdisable(bfa);
+ bfa_fcxp_iocdisable(bfa);
+ bfa_lps_iocdisable(bfa);
+ bfa_rport_iocdisable(bfa);
+ bfa_fcp_iocdisable(bfa);
+ bfa_dconf_iocdisable(bfa);
+
+ bfa->iocfc.submod_enabled = BFA_FALSE;
}
static void
@@ -636,15 +1203,8 @@ bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete)
{
struct bfa_s *bfa = bfa_arg;
- if (complete) {
- if (bfa->iocfc.cfgdone)
- bfa_cb_init(bfa->bfad, BFA_STATUS_OK);
- else
- bfa_cb_init(bfa->bfad, BFA_STATUS_FAILED);
- } else {
- if (bfa->iocfc.cfgdone)
- bfa->iocfc.action = BFA_IOCFC_ACT_NONE;
- }
+ if (complete)
+ bfa_cb_init(bfa->bfad, bfa->iocfc.op_status);
}
static void
@@ -655,8 +1215,16 @@ bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl)
if (compl)
complete(&bfad->comp);
- else
- bfa->iocfc.action = BFA_IOCFC_ACT_NONE;
+}
+
+static void
+bfa_iocfc_enable_cb(void *bfa_arg, bfa_boolean_t compl)
+{
+ struct bfa_s *bfa = bfa_arg;
+ struct bfad_s *bfad = bfa->bfad;
+
+ if (compl)
+ complete(&bfad->enable_comp);
}
static void
@@ -670,6 +1238,41 @@ bfa_iocfc_disable_cb(void *bfa_arg, bfa_boolean_t compl)
}
/*
+ * configure queue registers from firmware response
+ */
+static void
+bfa_iocfc_qreg(struct bfa_s *bfa, struct bfi_iocfc_qreg_s *qreg)
+{
+ int i;
+ struct bfa_iocfc_regs_s *r = &bfa->iocfc.bfa_regs;
+ void __iomem *kva = bfa_ioc_bar0(&bfa->ioc);
+
+ for (i = 0; i < BFI_IOC_MAX_CQS; i++) {
+ bfa->iocfc.hw_qid[i] = qreg->hw_qid[i];
+ r->cpe_q_ci[i] = kva + be32_to_cpu(qreg->cpe_q_ci_off[i]);
+ r->cpe_q_pi[i] = kva + be32_to_cpu(qreg->cpe_q_pi_off[i]);
+ r->cpe_q_ctrl[i] = kva + be32_to_cpu(qreg->cpe_qctl_off[i]);
+ r->rme_q_ci[i] = kva + be32_to_cpu(qreg->rme_q_ci_off[i]);
+ r->rme_q_pi[i] = kva + be32_to_cpu(qreg->rme_q_pi_off[i]);
+ r->rme_q_ctrl[i] = kva + be32_to_cpu(qreg->rme_qctl_off[i]);
+ }
+}
+
+static void
+bfa_iocfc_res_recfg(struct bfa_s *bfa, struct bfa_iocfc_fwcfg_s *fwcfg)
+{
+ struct bfa_iocfc_s *iocfc = &bfa->iocfc;
+ struct bfi_iocfc_cfg_s *cfg_info = iocfc->cfginfo;
+
+ bfa_fcxp_res_recfg(bfa, fwcfg->num_fcxp_reqs);
+ bfa_uf_res_recfg(bfa, fwcfg->num_uf_bufs);
+ bfa_rport_res_recfg(bfa, fwcfg->num_rports);
+ bfa_fcp_res_recfg(bfa, cpu_to_be16(cfg_info->num_ioim_reqs),
+ fwcfg->num_ioim_reqs);
+ bfa_tskim_res_recfg(bfa, fwcfg->num_tskim_reqs);
+}
+
+/*
* Update BFA configuration from firmware configuration.
*/
static void
@@ -679,25 +1282,35 @@ bfa_iocfc_cfgrsp(struct bfa_s *bfa)
struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp;
struct bfa_iocfc_fwcfg_s *fwcfg = &cfgrsp->fwcfg;
- fwcfg->num_cqs = fwcfg->num_cqs;
fwcfg->num_ioim_reqs = be16_to_cpu(fwcfg->num_ioim_reqs);
+ fwcfg->num_fwtio_reqs = be16_to_cpu(fwcfg->num_fwtio_reqs);
fwcfg->num_tskim_reqs = be16_to_cpu(fwcfg->num_tskim_reqs);
fwcfg->num_fcxp_reqs = be16_to_cpu(fwcfg->num_fcxp_reqs);
fwcfg->num_uf_bufs = be16_to_cpu(fwcfg->num_uf_bufs);
fwcfg->num_rports = be16_to_cpu(fwcfg->num_rports);
- iocfc->cfgdone = BFA_TRUE;
+ /*
+ * configure queue register offsets as learnt from firmware
+ */
+ bfa_iocfc_qreg(bfa, &cfgrsp->qreg);
/*
- * Configuration is complete - initialize/start submodules
+ * Re-configure resources as learnt from Firmware
*/
- bfa_fcport_init(bfa);
+ bfa_iocfc_res_recfg(bfa, fwcfg);
- if (iocfc->action == BFA_IOCFC_ACT_INIT)
- bfa_cb_queue(bfa, &iocfc->init_hcb_qe, bfa_iocfc_init_cb, bfa);
- else
- bfa_iocfc_start_submod(bfa);
+ /*
+ * Install MSIX queue handlers
+ */
+ bfa_msix_queue_install(bfa);
+
+ if (bfa->iocfc.cfgrsp->pbc_cfg.pbc_pwwn != 0) {
+ bfa->ioc.attr->pwwn = bfa->iocfc.cfgrsp->pbc_cfg.pbc_pwwn;
+ bfa->ioc.attr->nwwn = bfa->iocfc.cfgrsp->pbc_cfg.pbc_nwwn;
+ bfa_fsm_send_event(iocfc, IOCFC_E_CFG_DONE);
+ }
}
+
void
bfa_iocfc_reset_queues(struct bfa_s *bfa)
{
@@ -712,6 +1325,95 @@ bfa_iocfc_reset_queues(struct bfa_s *bfa)
}
/*
+ * Process FAA pwwn msg from fw.
+ */
+static void
+bfa_iocfc_process_faa_addr(struct bfa_s *bfa, struct bfi_faa_addr_msg_s *msg)
+{
+ struct bfa_iocfc_s *iocfc = &bfa->iocfc;
+ struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp;
+
+ cfgrsp->pbc_cfg.pbc_pwwn = msg->pwwn;
+ cfgrsp->pbc_cfg.pbc_nwwn = msg->nwwn;
+
+ bfa->ioc.attr->pwwn = msg->pwwn;
+ bfa->ioc.attr->nwwn = msg->nwwn;
+ bfa_fsm_send_event(iocfc, IOCFC_E_CFG_DONE);
+}
+
+/* Fabric Assigned Address specific functions */
+
+/*
+ * Check whether IOC is ready before sending command down
+ */
+static bfa_status_t
+bfa_faa_validate_request(struct bfa_s *bfa)
+{
+ enum bfa_ioc_type_e ioc_type = bfa_get_type(bfa);
+ u32 card_type = bfa->ioc.attr->card_type;
+
+ if (bfa_ioc_is_operational(&bfa->ioc)) {
+ if ((ioc_type != BFA_IOC_TYPE_FC) || bfa_mfg_is_mezz(card_type))
+ return BFA_STATUS_FEATURE_NOT_SUPPORTED;
+ } else {
+ return BFA_STATUS_IOC_NON_OP;
+ }
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
+ bfa_cb_iocfc_t cbfn, void *cbarg)
+{
+ struct bfi_faa_query_s faa_attr_req;
+ struct bfa_iocfc_s *iocfc = &bfa->iocfc;
+ bfa_status_t status;
+
+ status = bfa_faa_validate_request(bfa);
+ if (status != BFA_STATUS_OK)
+ return status;
+
+ if (iocfc->faa_args.busy == BFA_TRUE)
+ return BFA_STATUS_DEVBUSY;
+
+ iocfc->faa_args.faa_attr = attr;
+ iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
+ iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
+
+ iocfc->faa_args.busy = BFA_TRUE;
+ memset(&faa_attr_req, 0, sizeof(struct bfi_faa_query_s));
+ bfi_h2i_set(faa_attr_req.mh, BFI_MC_IOCFC,
+ BFI_IOCFC_H2I_FAA_QUERY_REQ, bfa_fn_lpu(bfa));
+
+ bfa_ioc_mbox_send(&bfa->ioc, &faa_attr_req,
+ sizeof(struct bfi_faa_query_s));
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * FAA query response
+ */
+static void
+bfa_faa_query_reply(struct bfa_iocfc_s *iocfc,
+ bfi_faa_query_rsp_t *rsp)
+{
+ void *cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
+
+ if (iocfc->faa_args.faa_attr) {
+ iocfc->faa_args.faa_attr->faa = rsp->faa;
+ iocfc->faa_args.faa_attr->faa_state = rsp->faa_status;
+ iocfc->faa_args.faa_attr->pwwn_source = rsp->addr_source;
+ }
+
+ WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
+
+ iocfc->faa_args.faa_cb.faa_cbfn(cbarg, BFA_STATUS_OK);
+ iocfc->faa_args.busy = BFA_FALSE;
+}
+
+/*
* IOC enable request is complete
*/
static void
@@ -719,15 +1421,10 @@ bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status)
{
struct bfa_s *bfa = bfa_arg;
- if (status != BFA_STATUS_OK) {
- bfa_isr_disable(bfa);
- if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT)
- bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
- bfa_iocfc_init_cb, bfa);
- return;
- }
-
- bfa_iocfc_send_cfg(bfa);
+ if (status == BFA_STATUS_OK)
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_ENABLED);
+ else
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_FAILED);
}
/*
@@ -738,17 +1435,8 @@ bfa_iocfc_disable_cbfn(void *bfa_arg)
{
struct bfa_s *bfa = bfa_arg;
- bfa_isr_disable(bfa);
- bfa_iocfc_disable_submod(bfa);
-
- if (bfa->iocfc.action == BFA_IOCFC_ACT_STOP)
- bfa_cb_queue(bfa, &bfa->iocfc.stop_hcb_qe, bfa_iocfc_stop_cb,
- bfa);
- else {
- WARN_ON(bfa->iocfc.action != BFA_IOCFC_ACT_DISABLE);
- bfa_cb_queue(bfa, &bfa->iocfc.dis_hcb_qe, bfa_iocfc_disable_cb,
- bfa);
- }
+ bfa->queue_process = BFA_FALSE;
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_DISABLED);
}
/*
@@ -759,14 +1447,8 @@ bfa_iocfc_hbfail_cbfn(void *bfa_arg)
{
struct bfa_s *bfa = bfa_arg;
- bfa->rme_process = BFA_FALSE;
-
- bfa_isr_disable(bfa);
- bfa_iocfc_disable_submod(bfa);
-
- if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT)
- bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe, bfa_iocfc_init_cb,
- bfa);
+ bfa->queue_process = BFA_FALSE;
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_FAILED);
}
/*
@@ -781,20 +1463,50 @@ bfa_iocfc_reset_cbfn(void *bfa_arg)
bfa_isr_enable(bfa);
}
-
/*
* Query IOC memory requirement information.
*/
void
-bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len)
+bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo,
+ struct bfa_s *bfa)
{
- /* dma memory for IOC */
- *dm_len += BFA_ROUNDUP(sizeof(struct bfi_ioc_attr_s), BFA_DMA_ALIGN_SZ);
+ int q, per_reqq_sz, per_rspq_sz;
+ struct bfa_mem_dma_s *ioc_dma = BFA_MEM_IOC_DMA(bfa);
+ struct bfa_mem_dma_s *iocfc_dma = BFA_MEM_IOCFC_DMA(bfa);
+ struct bfa_mem_kva_s *iocfc_kva = BFA_MEM_IOCFC_KVA(bfa);
+ u32 dm_len = 0;
+
+ /* dma memory setup for IOC */
+ bfa_mem_dma_setup(meminfo, ioc_dma,
+ BFA_ROUNDUP(sizeof(struct bfi_ioc_attr_s), BFA_DMA_ALIGN_SZ));
+
+ /* dma memory setup for REQ/RSP queues */
+ per_reqq_sz = BFA_ROUNDUP((cfg->drvcfg.num_reqq_elems * BFI_LMSG_SZ),
+ BFA_DMA_ALIGN_SZ);
+ per_rspq_sz = BFA_ROUNDUP((cfg->drvcfg.num_rspq_elems * BFI_LMSG_SZ),
+ BFA_DMA_ALIGN_SZ);
+
+ for (q = 0; q < cfg->fwcfg.num_cqs; q++) {
+ bfa_mem_dma_setup(meminfo, BFA_MEM_REQQ_DMA(bfa, q),
+ per_reqq_sz);
+ bfa_mem_dma_setup(meminfo, BFA_MEM_RSPQ_DMA(bfa, q),
+ per_rspq_sz);
+ }
- bfa_iocfc_fw_cfg_sz(cfg, dm_len);
- bfa_iocfc_cqs_sz(cfg, dm_len);
- *km_len += (bfa_auto_recover) ? BFA_DBG_FWTRC_LEN : 0;
+ /* IOCFC dma memory - calculate Shadow CI/PI size */
+ for (q = 0; q < cfg->fwcfg.num_cqs; q++)
+ dm_len += (2 * BFA_CACHELINE_SZ);
+
+ /* IOCFC dma memory - calculate config info / rsp size */
+ dm_len += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ);
+ dm_len += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s),
+ BFA_CACHELINE_SZ);
+
+ /* dma memory setup for IOCFC */
+ bfa_mem_dma_setup(meminfo, iocfc_dma, dm_len);
+
+ /* kva memory setup for IOCFC */
+ bfa_mem_kva_setup(meminfo, iocfc_kva, BFA_DBG_FWTRC_LEN);
}
/*
@@ -802,7 +1514,7 @@ bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
*/
void
bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
+ struct bfa_pcidev_s *pcidev)
{
int i;
struct bfa_ioc_s *ioc = &bfa->ioc;
@@ -815,22 +1527,22 @@ bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
ioc->trcmod = bfa->trcmod;
bfa_ioc_attach(&bfa->ioc, bfa, &bfa_iocfc_cbfn, &bfa->timer_mod);
- /*
- * Set FC mode for BFA_PCI_DEVICE_ID_CT_FC.
- */
- if (pcidev->device_id == BFA_PCI_DEVICE_ID_CT_FC)
- bfa_ioc_set_fcmode(&bfa->ioc);
-
- bfa_ioc_pci_init(&bfa->ioc, pcidev, BFI_MC_IOCFC);
+ bfa_ioc_pci_init(&bfa->ioc, pcidev, BFI_PCIFN_CLASS_FC);
bfa_ioc_mbox_register(&bfa->ioc, bfa_mbox_isrs);
bfa_iocfc_init_mem(bfa, bfad, cfg, pcidev);
- bfa_iocfc_mem_claim(bfa, cfg, meminfo);
+ bfa_iocfc_mem_claim(bfa, cfg);
INIT_LIST_HEAD(&bfa->timer_mod.timer_q);
INIT_LIST_HEAD(&bfa->comp_q);
for (i = 0; i < BFI_IOC_MAX_CQS; i++)
INIT_LIST_HEAD(&bfa->reqq_waitq[i]);
+
+ bfa->iocfc.cb_reqd = BFA_FALSE;
+ bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa->iocfc.submod_enabled = BFA_FALSE;
+
+ bfa_fsm_set_state(&bfa->iocfc, bfa_iocfc_sm_stopped);
}
/*
@@ -839,8 +1551,7 @@ bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
void
bfa_iocfc_init(struct bfa_s *bfa)
{
- bfa->iocfc.action = BFA_IOCFC_ACT_INIT;
- bfa_ioc_enable(&bfa->ioc);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_INIT);
}
/*
@@ -850,8 +1561,7 @@ bfa_iocfc_init(struct bfa_s *bfa)
void
bfa_iocfc_start(struct bfa_s *bfa)
{
- if (bfa->iocfc.cfgdone)
- bfa_iocfc_start_submod(bfa);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_START);
}
/*
@@ -861,10 +1571,7 @@ bfa_iocfc_start(struct bfa_s *bfa)
void
bfa_iocfc_stop(struct bfa_s *bfa)
{
- bfa->iocfc.action = BFA_IOCFC_ACT_STOP;
-
- bfa->rme_process = BFA_FALSE;
- bfa_ioc_disable(&bfa->ioc);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_STOP);
}
void
@@ -879,12 +1586,18 @@ bfa_iocfc_isr(void *bfaarg, struct bfi_mbmsg_s *m)
switch (msg->mh.msg_id) {
case BFI_IOCFC_I2H_CFG_REPLY:
- iocfc->cfg_reply = &msg->cfg_reply;
bfa_iocfc_cfgrsp(bfa);
break;
case BFI_IOCFC_I2H_UPDATEQ_RSP:
iocfc->updateq_cbfn(iocfc->updateq_cbarg, BFA_STATUS_OK);
break;
+ case BFI_IOCFC_I2H_ADDR_MSG:
+ bfa_iocfc_process_faa_addr(bfa,
+ (struct bfi_faa_addr_msg_s *)msg);
+ break;
+ case BFI_IOCFC_I2H_FAA_QUERY_RSP:
+ bfa_faa_query_reply(iocfc, (bfi_faa_query_rsp_t *)msg);
+ break;
default:
WARN_ON(1);
}
@@ -926,7 +1639,7 @@ bfa_iocfc_israttr_set(struct bfa_s *bfa, struct bfa_iocfc_intr_attr_s *attr)
return BFA_STATUS_DEVBUSY;
bfi_h2i_set(m->mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_SET_INTR_REQ,
- bfa_lpuid(bfa));
+ bfa_fn_lpu(bfa));
m->coalesce = iocfc->cfginfo->intr_attr.coalesce;
m->delay = iocfc->cfginfo->intr_attr.delay;
m->latency = iocfc->cfginfo->intr_attr.latency;
@@ -934,17 +1647,17 @@ bfa_iocfc_israttr_set(struct bfa_s *bfa, struct bfa_iocfc_intr_attr_s *attr)
bfa_trc(bfa, attr->delay);
bfa_trc(bfa, attr->latency);
- bfa_reqq_produce(bfa, BFA_REQQ_IOC);
+ bfa_reqq_produce(bfa, BFA_REQQ_IOC, m->mh);
return BFA_STATUS_OK;
}
void
-bfa_iocfc_set_snsbase(struct bfa_s *bfa, u64 snsbase_pa)
+bfa_iocfc_set_snsbase(struct bfa_s *bfa, int seg_no, u64 snsbase_pa)
{
struct bfa_iocfc_s *iocfc = &bfa->iocfc;
iocfc->cfginfo->sense_buf_len = (BFI_IOIM_SNSLEN - 1);
- bfa_dma_be_addr_set(iocfc->cfginfo->ioim_snsbase, snsbase_pa);
+ bfa_dma_be_addr_set(iocfc->cfginfo->ioim_snsbase[seg_no], snsbase_pa);
}
/*
* Enable IOC after it is disabled.
@@ -954,7 +1667,8 @@ bfa_iocfc_enable(struct bfa_s *bfa)
{
bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0,
"IOC Enable");
- bfa_ioc_enable(&bfa->ioc);
+ bfa->iocfc.cb_reqd = BFA_TRUE;
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_ENABLE);
}
void
@@ -962,17 +1676,15 @@ bfa_iocfc_disable(struct bfa_s *bfa)
{
bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0,
"IOC Disable");
- bfa->iocfc.action = BFA_IOCFC_ACT_DISABLE;
- bfa->rme_process = BFA_FALSE;
- bfa_ioc_disable(&bfa->ioc);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_DISABLE);
}
-
bfa_boolean_t
bfa_iocfc_is_operational(struct bfa_s *bfa)
{
- return bfa_ioc_is_operational(&bfa->ioc) && bfa->iocfc.cfgdone;
+ return bfa_ioc_is_operational(&bfa->ioc) &&
+ bfa_fsm_cmp_state(&bfa->iocfc, bfa_iocfc_sm_operational);
}
/*
@@ -1033,33 +1745,56 @@ bfa_iocfc_get_pbc_vports(struct bfa_s *bfa, struct bfi_pbc_vport_s *pbc_vport)
* starting address for each block and provide the same
* structure as input parameter to bfa_attach() call.
*
+ * @param[in] bfa - pointer to the bfa structure, used while fetching the
+ * dma, kva memory information of the bfa sub-modules.
+ *
* @return void
*
* Special Considerations: @note
*/
void
-bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo)
+bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo,
+ struct bfa_s *bfa)
{
- int i;
- u32 km_len = 0, dm_len = 0;
+ struct bfa_mem_dma_s *port_dma = BFA_MEM_PORT_DMA(bfa);
+ struct bfa_mem_dma_s *ablk_dma = BFA_MEM_ABLK_DMA(bfa);
+ struct bfa_mem_dma_s *cee_dma = BFA_MEM_CEE_DMA(bfa);
+ struct bfa_mem_dma_s *sfp_dma = BFA_MEM_SFP_DMA(bfa);
+ struct bfa_mem_dma_s *flash_dma = BFA_MEM_FLASH_DMA(bfa);
+ struct bfa_mem_dma_s *diag_dma = BFA_MEM_DIAG_DMA(bfa);
+ struct bfa_mem_dma_s *phy_dma = BFA_MEM_PHY_DMA(bfa);
+ struct bfa_mem_dma_s *fru_dma = BFA_MEM_FRU_DMA(bfa);
WARN_ON((cfg == NULL) || (meminfo == NULL));
memset((void *)meminfo, 0, sizeof(struct bfa_meminfo_s));
- meminfo->meminfo[BFA_MEM_TYPE_KVA - 1].mem_type =
- BFA_MEM_TYPE_KVA;
- meminfo->meminfo[BFA_MEM_TYPE_DMA - 1].mem_type =
- BFA_MEM_TYPE_DMA;
- bfa_iocfc_meminfo(cfg, &km_len, &dm_len);
-
- for (i = 0; hal_mods[i]; i++)
- hal_mods[i]->meminfo(cfg, &km_len, &dm_len);
-
- dm_len += bfa_port_meminfo();
-
- meminfo->meminfo[BFA_MEM_TYPE_KVA - 1].mem_len = km_len;
- meminfo->meminfo[BFA_MEM_TYPE_DMA - 1].mem_len = dm_len;
+ /* Initialize the DMA & KVA meminfo queues */
+ INIT_LIST_HEAD(&meminfo->dma_info.qe);
+ INIT_LIST_HEAD(&meminfo->kva_info.qe);
+
+ bfa_iocfc_meminfo(cfg, meminfo, bfa);
+ bfa_sgpg_meminfo(cfg, meminfo, bfa);
+ bfa_fcport_meminfo(cfg, meminfo, bfa);
+ bfa_fcxp_meminfo(cfg, meminfo, bfa);
+ bfa_lps_meminfo(cfg, meminfo, bfa);
+ bfa_uf_meminfo(cfg, meminfo, bfa);
+ bfa_rport_meminfo(cfg, meminfo, bfa);
+ bfa_fcp_meminfo(cfg, meminfo, bfa);
+ bfa_dconf_meminfo(cfg, meminfo, bfa);
+
+ /* dma info setup */
+ bfa_mem_dma_setup(meminfo, port_dma, bfa_port_meminfo());
+ bfa_mem_dma_setup(meminfo, ablk_dma, bfa_ablk_meminfo());
+ bfa_mem_dma_setup(meminfo, cee_dma, bfa_cee_meminfo());
+ bfa_mem_dma_setup(meminfo, sfp_dma, bfa_sfp_meminfo());
+ bfa_mem_dma_setup(meminfo, flash_dma,
+ bfa_flash_meminfo(cfg->drvcfg.min_cfg));
+ bfa_mem_dma_setup(meminfo, diag_dma, bfa_diag_meminfo());
+ bfa_mem_dma_setup(meminfo, phy_dma,
+ bfa_phy_meminfo(cfg->drvcfg.min_cfg));
+ bfa_mem_dma_setup(meminfo, fru_dma,
+ bfa_fru_meminfo(cfg->drvcfg.min_cfg));
}
/*
@@ -1092,28 +1827,51 @@ void
bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
{
- int i;
- struct bfa_mem_elem_s *melem;
+ struct bfa_mem_dma_s *dma_info, *dma_elem;
+ struct bfa_mem_kva_s *kva_info, *kva_elem;
+ struct list_head *dm_qe, *km_qe;
bfa->fcs = BFA_FALSE;
WARN_ON((cfg == NULL) || (meminfo == NULL));
- /*
- * initialize all memory pointers for iterative allocation
- */
- for (i = 0; i < BFA_MEM_TYPE_MAX; i++) {
- melem = meminfo->meminfo + i;
- melem->kva_curp = melem->kva;
- melem->dma_curp = melem->dma;
- }
+ /* Initialize memory pointers for iterative allocation */
+ dma_info = &meminfo->dma_info;
+ dma_info->kva_curp = dma_info->kva;
+ dma_info->dma_curp = dma_info->dma;
- bfa_iocfc_attach(bfa, bfad, cfg, meminfo, pcidev);
+ kva_info = &meminfo->kva_info;
+ kva_info->kva_curp = kva_info->kva;
- for (i = 0; hal_mods[i]; i++)
- hal_mods[i]->attach(bfa, bfad, cfg, meminfo, pcidev);
+ list_for_each(dm_qe, &dma_info->qe) {
+ dma_elem = (struct bfa_mem_dma_s *) dm_qe;
+ dma_elem->kva_curp = dma_elem->kva;
+ dma_elem->dma_curp = dma_elem->dma;
+ }
+
+ list_for_each(km_qe, &kva_info->qe) {
+ kva_elem = (struct bfa_mem_kva_s *) km_qe;
+ kva_elem->kva_curp = kva_elem->kva;
+ }
- bfa_com_port_attach(bfa, meminfo);
+ bfa_iocfc_attach(bfa, bfad, cfg, pcidev);
+ bfa_fcdiag_attach(bfa, bfad, cfg, pcidev);
+ bfa_sgpg_attach(bfa, bfad, cfg, pcidev);
+ bfa_fcport_attach(bfa, bfad, cfg, pcidev);
+ bfa_fcxp_attach(bfa, bfad, cfg, pcidev);
+ bfa_lps_attach(bfa, bfad, cfg, pcidev);
+ bfa_uf_attach(bfa, bfad, cfg, pcidev);
+ bfa_rport_attach(bfa, bfad, cfg, pcidev);
+ bfa_fcp_attach(bfa, bfad, cfg, pcidev);
+ bfa_dconf_attach(bfa, bfad, cfg);
+ bfa_com_port_attach(bfa);
+ bfa_com_ablk_attach(bfa);
+ bfa_com_cee_attach(bfa);
+ bfa_com_sfp_attach(bfa);
+ bfa_com_flash_attach(bfa, cfg->drvcfg.min_cfg);
+ bfa_com_diag_attach(bfa);
+ bfa_com_phy_attach(bfa, cfg->drvcfg.min_cfg);
+ bfa_com_fru_attach(bfa, cfg->drvcfg.min_cfg);
}
/*
@@ -1132,10 +1890,6 @@ bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
void
bfa_detach(struct bfa_s *bfa)
{
- int i;
-
- for (i = 0; hal_mods[i]; i++)
- hal_mods[i]->detach(bfa);
bfa_ioc_detach(&bfa->ioc);
}
@@ -1155,7 +1909,12 @@ bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q)
list_for_each_safe(qe, qen, comp_q) {
hcb_qe = (struct bfa_cb_qe_s *) qe;
- hcb_qe->cbfn(hcb_qe->cbarg, BFA_TRUE);
+ if (hcb_qe->pre_rmv) {
+ /* qe is invalid after return, dequeue before cbfn() */
+ list_del(qe);
+ hcb_qe->cbfn_status(hcb_qe->cbarg, hcb_qe->fw_status);
+ } else
+ hcb_qe->cbfn(hcb_qe->cbarg, BFA_TRUE);
}
}
@@ -1168,29 +1927,11 @@ bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q)
while (!list_empty(comp_q)) {
bfa_q_deq(comp_q, &qe);
hcb_qe = (struct bfa_cb_qe_s *) qe;
+ WARN_ON(hcb_qe->pre_rmv);
hcb_qe->cbfn(hcb_qe->cbarg, BFA_FALSE);
}
}
-
-/*
- * Return the list of PCI vendor/device id lists supported by this
- * BFA instance.
- */
-void
-bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids)
-{
- static struct bfa_pciid_s __pciids[] = {
- {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G2P},
- {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G1P},
- {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_CT},
- {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_CT_FC},
- };
-
- *npciids = sizeof(__pciids) / sizeof(__pciids[0]);
- *pciids = __pciids;
-}
-
/*
* Use this function query the default struct bfa_iocfc_cfg_s value (compiled
* into BFA layer). The OS driver can then turn back and overwrite entries that
@@ -1215,6 +1956,7 @@ bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg)
cfg->fwcfg.num_fcxp_reqs = DEF_CFG_NUM_FCXP_REQS;
cfg->fwcfg.num_uf_bufs = DEF_CFG_NUM_UF_BUFS;
cfg->fwcfg.num_cqs = DEF_CFG_NUM_CQS;
+ cfg->fwcfg.num_fwtio_reqs = 0;
cfg->drvcfg.num_reqq_elems = DEF_CFG_NUM_REQQ_ELEMS;
cfg->drvcfg.num_rspq_elems = DEF_CFG_NUM_RSPQ_ELEMS;
@@ -1226,19 +1968,3 @@ bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg)
cfg->drvcfg.delay_comp = BFA_FALSE;
}
-
-void
-bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg)
-{
- bfa_cfg_get_default(cfg);
- cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN;
- cfg->fwcfg.num_tskim_reqs = BFA_TSKIM_MIN;
- cfg->fwcfg.num_fcxp_reqs = BFA_FCXP_MIN;
- cfg->fwcfg.num_uf_bufs = BFA_UF_MIN;
- cfg->fwcfg.num_rports = BFA_RPORT_MIN;
-
- cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN;
- cfg->drvcfg.num_reqq_elems = BFA_REQQ_NELEMS_MIN;
- cfg->drvcfg.num_rspq_elems = BFA_RSPQ_NELEMS_MIN;
- cfg->drvcfg.min_cfg = BFA_TRUE;
-}
diff --git a/drivers/scsi/bfa/bfa_cs.h b/drivers/scsi/bfa/bfa_cs.h
index 12bfeed268eb..6650b1dbb1ed 100644
--- a/drivers/scsi/bfa/bfa_cs.h
+++ b/drivers/scsi/bfa/bfa_cs.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -34,10 +27,10 @@
#define BFA_TRC_TS(_trcm) \
({ \
- struct timeval tv; \
+ struct timespec64 ts; \
\
- do_gettimeofday(&tv); \
- (tv.tv_sec*1000000+tv.tv_usec); \
+ ktime_get_ts64(&ts); \
+ (ts.tv_sec*1000000+ts.tv_nsec / 1000); \
})
#ifndef BFA_TRC_TS
@@ -107,44 +100,11 @@ bfa_trc_stop(struct bfa_trc_mod_s *trcm)
trcm->stopped = 1;
}
-static inline void
-__bfa_trc(struct bfa_trc_mod_s *trcm, int fileno, int line, u64 data)
-{
- int tail = trcm->tail;
- struct bfa_trc_s *trc = &trcm->trc[tail];
-
- if (trcm->stopped)
- return;
-
- trc->fileno = (u16) fileno;
- trc->line = (u16) line;
- trc->data.u64 = data;
- trc->timestamp = BFA_TRC_TS(trcm);
-
- trcm->tail = (trcm->tail + 1) & (BFA_TRC_MAX - 1);
- if (trcm->tail == trcm->head)
- trcm->head = (trcm->head + 1) & (BFA_TRC_MAX - 1);
-}
-
-
-static inline void
-__bfa_trc32(struct bfa_trc_mod_s *trcm, int fileno, int line, u32 data)
-{
- int tail = trcm->tail;
- struct bfa_trc_s *trc = &trcm->trc[tail];
-
- if (trcm->stopped)
- return;
-
- trc->fileno = (u16) fileno;
- trc->line = (u16) line;
- trc->data.u32.u32 = data;
- trc->timestamp = BFA_TRC_TS(trcm);
+void
+__bfa_trc(struct bfa_trc_mod_s *trcm, int fileno, int line, u64 data);
- trcm->tail = (trcm->tail + 1) & (BFA_TRC_MAX - 1);
- if (trcm->tail == trcm->head)
- trcm->head = (trcm->head + 1) & (BFA_TRC_MAX - 1);
-}
+void
+__bfa_trc32(struct bfa_trc_mod_s *trcm, int fileno, int line, u32 data);
#define bfa_sm_fault(__mod, __event) do { \
bfa_trc(__mod, (((u32)0xDEAD << 16) | __event)); \
@@ -168,7 +128,7 @@ __bfa_trc32(struct bfa_trc_mod_s *trcm, int fileno, int line, u32 data)
/*
* bfa_q_deq - dequeue an element from head of the queue
*/
-#define bfa_q_deq(_q, _qe) { \
+#define bfa_q_deq(_q, _qe) do { \
if (!list_empty(_q)) { \
(*((struct list_head **) (_qe))) = bfa_q_next(_q); \
bfa_q_prev(bfa_q_next(*((struct list_head **) _qe))) = \
@@ -177,7 +137,7 @@ __bfa_trc32(struct bfa_trc_mod_s *trcm, int fileno, int line, u32 data)
} else { \
*((struct list_head **) (_qe)) = (struct list_head *) NULL;\
} \
-}
+} while (0)
/*
* bfa_q_deq_tail - dequeue an element from tail of the queue
@@ -227,10 +187,10 @@ typedef void (*bfa_sm_t)(void *sm, int event);
#define bfa_sm_state_decl(oc, st, otype, etype) \
static void oc ## _sm_ ## st(otype * fsm, etype event)
-#define bfa_sm_set_state(_sm, _state) ((_sm)->sm = (bfa_sm_t)(_state))
+#define bfa_sm_set_state(_sm, _state) ((_sm)->sm = (_state))
#define bfa_sm_send_event(_sm, _event) ((_sm)->sm((_sm), (_event)))
#define bfa_sm_get_state(_sm) ((_sm)->sm)
-#define bfa_sm_cmp_state(_sm, _state) ((_sm)->sm == (bfa_sm_t)(_state))
+#define bfa_sm_cmp_state(_sm, _state) ((_sm)->sm == (_state))
/*
* For converting from state machine function to state encoding.
@@ -240,7 +200,7 @@ struct bfa_sm_table_s {
int state; /* state machine encoding */
char *name; /* state name for display */
};
-#define BFA_SM(_sm) ((bfa_sm_t)(_sm))
+#define BFA_SM(_sm) (_sm)
/*
* State machine with entry actions.
@@ -258,24 +218,13 @@ typedef void (*bfa_fsm_t)(void *fsm, int event);
static void oc ## _sm_ ## st ## _entry(otype * fsm)
#define bfa_fsm_set_state(_fsm, _state) do { \
- (_fsm)->fsm = (bfa_fsm_t)(_state); \
+ (_fsm)->fsm = (_state); \
_state ## _entry(_fsm); \
} while (0)
#define bfa_fsm_send_event(_fsm, _event) ((_fsm)->fsm((_fsm), (_event)))
#define bfa_fsm_get_state(_fsm) ((_fsm)->fsm)
-#define bfa_fsm_cmp_state(_fsm, _state) \
- ((_fsm)->fsm == (bfa_fsm_t)(_state))
-
-static inline int
-bfa_sm_to_state(struct bfa_sm_table_s *smt, bfa_sm_t sm)
-{
- int i = 0;
-
- while (smt[i].sm && smt[i].sm != sm)
- i++;
- return smt[i].state;
-}
+#define bfa_fsm_cmp_state(_fsm, _state) ((_fsm)->fsm == (_state))
/*
* @ Generic wait counter.
diff --git a/drivers/scsi/bfa/bfa_defs.h b/drivers/scsi/bfa/bfa_defs.h
index d85f93aea465..6abd9f42a3fc 100644
--- a/drivers/scsi/bfa/bfa_defs.h
+++ b/drivers/scsi/bfa/bfa_defs.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_DEFS_H__
@@ -40,7 +33,13 @@ enum {
BFA_MFG_TYPE_ASTRA = 807, /* Astra mezz card */
BFA_MFG_TYPE_LIGHTNING_P0 = 902, /* Lightning mezz card - old */
BFA_MFG_TYPE_LIGHTNING = 1741, /* Lightning mezz card */
- BFA_MFG_TYPE_INVALID = 0, /* Invalid card type */
+ BFA_MFG_TYPE_PROWLER_F = 1560, /* Prowler FC only cards */
+ BFA_MFG_TYPE_PROWLER_N = 1410, /* Prowler NIC only cards */
+ BFA_MFG_TYPE_PROWLER_C = 1710, /* Prowler CNA only cards */
+ BFA_MFG_TYPE_PROWLER_D = 1860, /* Prowler Dual cards */
+ BFA_MFG_TYPE_CHINOOK = 1867, /* Chinook cards */
+ BFA_MFG_TYPE_CHINOOK2 = 1869, /*!< Chinook2 cards */
+ BFA_MFG_TYPE_INVALID = 0, /* Invalid card type */
};
#pragma pack(1)
@@ -53,7 +52,9 @@ enum {
(type) == BFA_MFG_TYPE_WANCHESE || \
(type) == BFA_MFG_TYPE_ASTRA || \
(type) == BFA_MFG_TYPE_LIGHTNING_P0 || \
- (type) == BFA_MFG_TYPE_LIGHTNING))
+ (type) == BFA_MFG_TYPE_LIGHTNING || \
+ (type) == BFA_MFG_TYPE_CHINOOK || \
+ (type) == BFA_MFG_TYPE_CHINOOK2))
/*
* Check if the card having old wwn/mac handling
@@ -124,30 +125,104 @@ enum bfa_status {
BFA_STATUS_ETIMER = 5, /* Timer expired - Retry, if persists,
* contact support */
BFA_STATUS_EPROTOCOL = 6, /* Protocol error */
+ BFA_STATUS_BADFLASH = 9, /* Flash is bad */
+ BFA_STATUS_SFP_UNSUPP = 10, /* Unsupported SFP - Replace SFP */
+ BFA_STATUS_UNKNOWN_VFID = 11, /* VF_ID not found */
+ BFA_STATUS_DATACORRUPTED = 12, /* Diag returned data corrupted */
BFA_STATUS_DEVBUSY = 13, /* Device busy - Retry operation */
+ BFA_STATUS_HDMA_FAILED = 16, /* Host dma failed contact support */
+ BFA_STATUS_FLASH_BAD_LEN = 17, /* Flash bad length */
BFA_STATUS_UNKNOWN_LWWN = 18, /* LPORT PWWN not found */
BFA_STATUS_UNKNOWN_RWWN = 19, /* RPORT PWWN not found */
BFA_STATUS_VPORT_EXISTS = 21, /* VPORT already exists */
BFA_STATUS_VPORT_MAX = 22, /* Reached max VPORT supported limit */
BFA_STATUS_UNSUPP_SPEED = 23, /* Invalid Speed Check speed setting */
BFA_STATUS_INVLD_DFSZ = 24, /* Invalid Max data field size */
+ BFA_STATUS_CMD_NOTSUPP = 26, /* Command/API not supported */
BFA_STATUS_FABRIC_RJT = 29, /* Reject from attached fabric */
+ BFA_STATUS_UNKNOWN_VWWN = 30, /* VPORT PWWN not found */
+ BFA_STATUS_PORT_OFFLINE = 34, /* Port is not online */
BFA_STATUS_VPORT_WWN_BP = 46, /* WWN is same as base port's WWN */
+ BFA_STATUS_PORT_NOT_DISABLED = 47, /* Port not disabled disable port */
BFA_STATUS_NO_FCPIM_NEXUS = 52, /* No FCP Nexus exists with the rport */
BFA_STATUS_IOC_FAILURE = 56, /* IOC failure - Retry, if persists
* contact support */
BFA_STATUS_INVALID_WWN = 57, /* Invalid WWN */
+ BFA_STATUS_ADAPTER_ENABLED = 60, /* Adapter is not disabled */
+ BFA_STATUS_IOC_NON_OP = 61, /* IOC is not operational */
+ BFA_STATUS_VERSION_FAIL = 70, /* Application/Driver version mismatch */
BFA_STATUS_DIAG_BUSY = 71, /* diag busy */
+ BFA_STATUS_BEACON_ON = 72, /* Port Beacon already on */
BFA_STATUS_ENOFSAVE = 78, /* No saved firmware trace */
BFA_STATUS_IOC_DISABLED = 82, /* IOC is already disabled */
+ BFA_STATUS_ERROR_TRL_ENABLED = 87, /* TRL is enabled */
+ BFA_STATUS_ERROR_QOS_ENABLED = 88, /* QoS is enabled */
+ BFA_STATUS_NO_SFP_DEV = 89, /* No SFP device check or replace SFP */
+ BFA_STATUS_MEMTEST_FAILED = 90, /* Memory test failed contact support */
+ BFA_STATUS_LEDTEST_OP = 109, /* LED test is operating */
BFA_STATUS_INVALID_MAC = 134, /* Invalid MAC address */
+ BFA_STATUS_CMD_NOTSUPP_CNA = 146, /* Command not supported for CNA */
BFA_STATUS_PBC = 154, /* Operation not allowed for pre-boot
* configuration */
+ BFA_STATUS_BAD_FWCFG = 156, /* Bad firmware configuration */
+ BFA_STATUS_INVALID_VENDOR = 158, /* Invalid switch vendor */
+ BFA_STATUS_SFP_NOT_READY = 159, /* SFP info is not ready. Retry */
BFA_STATUS_TRUNK_ENABLED = 164, /* Trunk is already enabled on
* this adapter */
BFA_STATUS_TRUNK_DISABLED = 165, /* Trunking is disabled on
* the adapter */
BFA_STATUS_IOPROFILE_OFF = 175, /* IO profile OFF */
+ BFA_STATUS_PHY_NOT_PRESENT = 183, /* PHY module not present */
+ BFA_STATUS_FEATURE_NOT_SUPPORTED = 192, /* Feature not supported */
+ BFA_STATUS_ENTRY_EXISTS = 193, /* Entry already exists */
+ BFA_STATUS_ENTRY_NOT_EXISTS = 194, /* Entry does not exist */
+ BFA_STATUS_NO_CHANGE = 195, /* Feature already in that state */
+ BFA_STATUS_FAA_ENABLED = 197, /* FAA is already enabled */
+ BFA_STATUS_FAA_DISABLED = 198, /* FAA is already disabled */
+ BFA_STATUS_FAA_ACQUIRED = 199, /* FAA is already acquired */
+ BFA_STATUS_FAA_ACQ_ADDR = 200, /* Acquiring addr */
+ BFA_STATUS_BBCR_FC_ONLY = 201, /*!< BBCredit Recovery is supported for *
+ * FC mode only */
+ BFA_STATUS_ERROR_TRUNK_ENABLED = 203, /* Trunk enabled on adapter */
+ BFA_STATUS_MAX_ENTRY_REACHED = 212, /* MAX entry reached */
+ BFA_STATUS_TOPOLOGY_LOOP = 230, /* Topology is set to Loop */
+ BFA_STATUS_LOOP_UNSUPP_MEZZ = 231, /* Loop topology is not supported
+ * on mezz cards */
+ BFA_STATUS_INVALID_BW = 233, /* Invalid bandwidth value */
+ BFA_STATUS_QOS_BW_INVALID = 234, /* Invalid QOS bandwidth
+ * configuration */
+ BFA_STATUS_DPORT_ENABLED = 235, /* D-port mode is already enabled */
+ BFA_STATUS_DPORT_DISABLED = 236, /* D-port mode is already disabled */
+ BFA_STATUS_CMD_NOTSUPP_MEZZ = 239, /* Cmd not supported for MEZZ card */
+ BFA_STATUS_FRU_NOT_PRESENT = 240, /* fru module not present */
+ BFA_STATUS_DPORT_NO_SFP = 243, /* SFP is not present.\n D-port will be
+ * enabled but it will be operational
+ * only after inserting a valid SFP. */
+ BFA_STATUS_DPORT_ERR = 245, /* D-port mode is enabled */
+ BFA_STATUS_DPORT_ENOSYS = 254, /* Switch has no D_Port functionality */
+ BFA_STATUS_DPORT_CANT_PERF = 255, /* Switch port is not D_Port capable
+ * or D_Port is disabled */
+ BFA_STATUS_DPORT_LOGICALERR = 256, /* Switch D_Port fail */
+ BFA_STATUS_DPORT_SWBUSY = 257, /* Switch port busy */
+ BFA_STATUS_ERR_BBCR_SPEED_UNSUPPORT = 258, /*!< BB credit recovery is
+ * supported at max port speed alone */
+ BFA_STATUS_ERROR_BBCR_ENABLED = 259, /*!< BB credit recovery
+ * is enabled */
+ BFA_STATUS_INVALID_BBSCN = 260, /*!< Invalid BBSCN value.
+ * Valid range is [1-15] */
+ BFA_STATUS_DDPORT_ERR = 261, /* Dynamic D_Port mode is active.\n To
+ * exit dynamic mode, disable D_Port on
+ * the remote port */
+ BFA_STATUS_DPORT_SFPWRAP_ERR = 262, /* Clear e/o_wrap fail, check or
+ * replace SFP */
+ BFA_STATUS_BBCR_CFG_NO_CHANGE = 265, /*!< BBCR is operational.
+ * Disable BBCR and try this operation again. */
+ BFA_STATUS_DPORT_SW_NOTREADY = 268, /* Remote port is not ready to
+ * start dport test. Check remote
+ * port status. */
+ BFA_STATUS_DPORT_INV_SFP = 271, /* Invalid SFP for D-PORT mode. */
+ BFA_STATUS_DPORT_CMD_NOTSUPP = 273, /* Dport is not supported by
+ * remote port */
BFA_STATUS_MAX_VAL /* Unknown error code */
};
#define bfa_status_t enum bfa_status
@@ -184,6 +259,7 @@ enum {
BFA_ADAPTER_MFG_NAME_LEN = 8, /* manufacturer name length */
BFA_ADAPTER_SYM_NAME_LEN = 64, /* adapter symbolic name length */
BFA_ADAPTER_OS_TYPE_LEN = 64, /* adapter os type length */
+ BFA_ADAPTER_UUID_LEN = 16, /* adapter uuid length */
};
struct bfa_adapter_attr_s {
@@ -213,6 +289,11 @@ struct bfa_adapter_attr_s {
u8 is_mezz;
u8 trunk_capable;
+ u8 mfg_day; /* manufacturing day */
+ u8 mfg_month; /* manufacturing month */
+ u16 mfg_year; /* manufacturing year */
+ u16 rsvd;
+ u8 uuid[BFA_ADAPTER_UUID_LEN];
};
/*
@@ -265,6 +346,8 @@ enum bfa_ioc_state {
BFA_IOC_DISABLED = 10, /* IOC is disabled */
BFA_IOC_FWMISMATCH = 11, /* IOC f/w different from drivers */
BFA_IOC_ENABLING = 12, /* IOC is being enabled */
+ BFA_IOC_HWFAIL = 13, /* PCI mapping doesn't exist */
+ BFA_IOC_ACQ_ADDR = 14, /* Acquiring addr from fabric */
};
/*
@@ -294,6 +377,7 @@ struct bfa_ioc_drv_stats_s {
u32 enable_reqs;
u32 disable_replies;
u32 enable_replies;
+ u32 rsvd;
};
/*
@@ -320,7 +404,144 @@ struct bfa_ioc_attr_s {
struct bfa_ioc_driver_attr_s driver_attr; /* driver attr */
struct bfa_ioc_pci_attr_s pci_attr;
u8 port_id; /* port number */
- u8 rsvd[7]; /* 64bit align */
+ u8 port_mode; /* bfa_mode_s */
+ u8 cap_bm; /* capability */
+ u8 port_mode_cfg; /* bfa_mode_s */
+ u8 def_fn; /* 1 if default fn */
+ u8 rsvd[3]; /* 64bit align */
+};
+
+/*
+ * AEN related definitions
+ */
+enum bfa_aen_category {
+ BFA_AEN_CAT_ADAPTER = 1,
+ BFA_AEN_CAT_PORT = 2,
+ BFA_AEN_CAT_LPORT = 3,
+ BFA_AEN_CAT_RPORT = 4,
+ BFA_AEN_CAT_ITNIM = 5,
+ BFA_AEN_CAT_AUDIT = 8,
+ BFA_AEN_CAT_IOC = 9,
+};
+
+/* BFA adapter level events */
+enum bfa_adapter_aen_event {
+ BFA_ADAPTER_AEN_ADD = 1, /* New Adapter found event */
+ BFA_ADAPTER_AEN_REMOVE = 2, /* Adapter removed event */
+};
+
+struct bfa_adapter_aen_data_s {
+ char serial_num[BFA_ADAPTER_SERIAL_NUM_LEN];
+ u32 nports; /* Number of NPorts */
+ wwn_t pwwn; /* WWN of one of its physical port */
+};
+
+/* BFA physical port Level events */
+enum bfa_port_aen_event {
+ BFA_PORT_AEN_ONLINE = 1, /* Physical Port online event */
+ BFA_PORT_AEN_OFFLINE = 2, /* Physical Port offline event */
+ BFA_PORT_AEN_RLIR = 3, /* RLIR event, not supported */
+ BFA_PORT_AEN_SFP_INSERT = 4, /* SFP inserted event */
+ BFA_PORT_AEN_SFP_REMOVE = 5, /* SFP removed event */
+ BFA_PORT_AEN_SFP_POM = 6, /* SFP POM event */
+ BFA_PORT_AEN_ENABLE = 7, /* Physical Port enable event */
+ BFA_PORT_AEN_DISABLE = 8, /* Physical Port disable event */
+ BFA_PORT_AEN_AUTH_ON = 9, /* Physical Port auth success event */
+ BFA_PORT_AEN_AUTH_OFF = 10, /* Physical Port auth fail event */
+ BFA_PORT_AEN_DISCONNECT = 11, /* Physical Port disconnect event */
+ BFA_PORT_AEN_QOS_NEG = 12, /* Base Port QOS negotiation event */
+ BFA_PORT_AEN_FABRIC_NAME_CHANGE = 13, /* Fabric Name/WWN change */
+ BFA_PORT_AEN_SFP_ACCESS_ERROR = 14, /* SFP read error event */
+ BFA_PORT_AEN_SFP_UNSUPPORT = 15, /* Unsupported SFP event */
+};
+
+enum bfa_port_aen_sfp_pom {
+ BFA_PORT_AEN_SFP_POM_GREEN = 1, /* Normal */
+ BFA_PORT_AEN_SFP_POM_AMBER = 2, /* Warning */
+ BFA_PORT_AEN_SFP_POM_RED = 3, /* Critical */
+ BFA_PORT_AEN_SFP_POM_MAX = BFA_PORT_AEN_SFP_POM_RED
+};
+
+struct bfa_port_aen_data_s {
+ wwn_t pwwn; /* WWN of the physical port */
+ wwn_t fwwn; /* WWN of the fabric port */
+ u32 phy_port_num; /* For SFP related events */
+ u16 ioc_type;
+ u16 level; /* Only transitions will be informed */
+ mac_t mac; /* MAC address of the ethernet port */
+ u16 rsvd;
+};
+
+/* BFA AEN logical port events */
+enum bfa_lport_aen_event {
+ BFA_LPORT_AEN_NEW = 1, /* LPort created event */
+ BFA_LPORT_AEN_DELETE = 2, /* LPort deleted event */
+ BFA_LPORT_AEN_ONLINE = 3, /* LPort online event */
+ BFA_LPORT_AEN_OFFLINE = 4, /* LPort offline event */
+ BFA_LPORT_AEN_DISCONNECT = 5, /* LPort disconnect event */
+ BFA_LPORT_AEN_NEW_PROP = 6, /* VPort created event */
+ BFA_LPORT_AEN_DELETE_PROP = 7, /* VPort deleted event */
+ BFA_LPORT_AEN_NEW_STANDARD = 8, /* VPort created event */
+ BFA_LPORT_AEN_DELETE_STANDARD = 9, /* VPort deleted event */
+ BFA_LPORT_AEN_NPIV_DUP_WWN = 10, /* VPort with duplicate WWN */
+ BFA_LPORT_AEN_NPIV_FABRIC_MAX = 11, /* Max NPIV in fabric/fport */
+ BFA_LPORT_AEN_NPIV_UNKNOWN = 12, /* Unknown NPIV Error code */
+};
+
+struct bfa_lport_aen_data_s {
+ u16 vf_id; /* vf_id of this logical port */
+ u16 roles; /* Logical port mode,IM/TM/IP etc */
+ u32 rsvd;
+ wwn_t ppwwn; /* WWN of its physical port */
+ wwn_t lpwwn; /* WWN of this logical port */
+};
+
+/* BFA ITNIM events */
+enum bfa_itnim_aen_event {
+ BFA_ITNIM_AEN_ONLINE = 1, /* Target online */
+ BFA_ITNIM_AEN_OFFLINE = 2, /* Target offline */
+ BFA_ITNIM_AEN_DISCONNECT = 3, /* Target disconnected */
+};
+
+struct bfa_itnim_aen_data_s {
+ u16 vf_id; /* vf_id of the IT nexus */
+ u16 rsvd[3];
+ wwn_t ppwwn; /* WWN of its physical port */
+ wwn_t lpwwn; /* WWN of logical port */
+ wwn_t rpwwn; /* WWN of remote(target) port */
+};
+
+/* BFA audit events */
+enum bfa_audit_aen_event {
+ BFA_AUDIT_AEN_AUTH_ENABLE = 1,
+ BFA_AUDIT_AEN_AUTH_DISABLE = 2,
+ BFA_AUDIT_AEN_FLASH_ERASE = 3,
+ BFA_AUDIT_AEN_FLASH_UPDATE = 4,
+};
+
+struct bfa_audit_aen_data_s {
+ wwn_t pwwn;
+ int partition_inst;
+ int partition_type;
+};
+
+/* BFA IOC level events */
+enum bfa_ioc_aen_event {
+ BFA_IOC_AEN_HBGOOD = 1, /* Heart Beat restore event */
+ BFA_IOC_AEN_HBFAIL = 2, /* Heart Beat failure event */
+ BFA_IOC_AEN_ENABLE = 3, /* IOC enabled event */
+ BFA_IOC_AEN_DISABLE = 4, /* IOC disabled event */
+ BFA_IOC_AEN_FWMISMATCH = 5, /* IOC firmware mismatch */
+ BFA_IOC_AEN_FWCFG_ERROR = 6, /* IOC firmware config error */
+ BFA_IOC_AEN_INVALID_VENDOR = 7,
+ BFA_IOC_AEN_INVALID_NWWN = 8, /* Zero NWWN */
+ BFA_IOC_AEN_INVALID_PWWN = 9 /* Zero PWWN */
+};
+
+struct bfa_ioc_aen_data_s {
+ wwn_t pwwn;
+ u16 ioc_type;
+ mac_t mac;
};
/*
@@ -337,6 +558,21 @@ struct bfa_ioc_attr_s {
#define BFA_MFG_SUPPLIER_PARTNUM_SIZE 20
#define BFA_MFG_SUPPLIER_SERIALNUM_SIZE 20
#define BFA_MFG_SUPPLIER_REVISION_SIZE 4
+/*
+ * Initial capability definition
+ */
+#define BFA_MFG_IC_FC 0x01
+#define BFA_MFG_IC_ETH 0x02
+
+/*
+ * Adapter capability mask definition
+ */
+#define BFA_CM_HBA 0x01
+#define BFA_CM_CNA 0x02
+#define BFA_CM_NIC 0x04
+#define BFA_CM_FC16G 0x08
+#define BFA_CM_SRIOV 0x10
+#define BFA_CM_MEZZ 0x20
#pragma pack(1)
@@ -344,31 +580,39 @@ struct bfa_ioc_attr_s {
* All numerical fields are in big-endian format.
*/
struct bfa_mfg_block_s {
- u8 version; /* manufacturing block version */
- u8 mfg_sig[3]; /* characters 'M', 'F', 'G' */
- u16 mfgsize; /* mfg block size */
- u16 u16_chksum; /* old u16 checksum */
- char brcd_serialnum[STRSZ(BFA_MFG_SERIALNUM_SIZE)];
- char brcd_partnum[STRSZ(BFA_MFG_PARTNUM_SIZE)];
- u8 mfg_day; /* manufacturing day */
- u8 mfg_month; /* manufacturing month */
- u16 mfg_year; /* manufacturing year */
- wwn_t mfg_wwn; /* wwn base for this adapter */
- u8 num_wwn; /* number of wwns assigned */
- u8 mfg_speeds; /* speeds allowed for this adapter */
- u8 rsv[2];
- char supplier_id[STRSZ(BFA_MFG_SUPPLIER_ID_SIZE)];
- char supplier_partnum[STRSZ(BFA_MFG_SUPPLIER_PARTNUM_SIZE)];
- char
- supplier_serialnum[STRSZ(BFA_MFG_SUPPLIER_SERIALNUM_SIZE)];
- char
- supplier_revision[STRSZ(BFA_MFG_SUPPLIER_REVISION_SIZE)];
- mac_t mfg_mac; /* mac address */
- u8 num_mac; /* number of mac addresses */
- u8 rsv2;
- u32 mfg_type; /* card type */
- u8 rsv3[108];
- u8 md5_chksum[BFA_MFG_CHKSUM_SIZE]; /* md5 checksum */
+ u8 version; /*!< manufacturing block version */
+ u8 mfg_sig[3]; /*!< characters 'M', 'F', 'G' */
+ u16 mfgsize; /*!< mfg block size */
+ u16 u16_chksum; /*!< old u16 checksum */
+ char brcd_serialnum[STRSZ(BFA_MFG_SERIALNUM_SIZE)];
+ char brcd_partnum[STRSZ(BFA_MFG_PARTNUM_SIZE)];
+ u8 mfg_day; /*!< manufacturing day */
+ u8 mfg_month; /*!< manufacturing month */
+ u16 mfg_year; /*!< manufacturing year */
+ wwn_t mfg_wwn; /*!< wwn base for this adapter */
+ u8 num_wwn; /*!< number of wwns assigned */
+ u8 mfg_speeds; /*!< speeds allowed for this adapter */
+ u8 rsv[2];
+ char supplier_id[STRSZ(BFA_MFG_SUPPLIER_ID_SIZE)];
+ char supplier_partnum[STRSZ(BFA_MFG_SUPPLIER_PARTNUM_SIZE)];
+ char supplier_serialnum[STRSZ(BFA_MFG_SUPPLIER_SERIALNUM_SIZE)];
+ char supplier_revision[STRSZ(BFA_MFG_SUPPLIER_REVISION_SIZE)];
+ mac_t mfg_mac; /*!< base mac address */
+ u8 num_mac; /*!< number of mac addresses */
+ u8 rsv2;
+ u32 card_type; /*!< card type */
+ char cap_nic; /*!< capability nic */
+ char cap_cna; /*!< capability cna */
+ char cap_hba; /*!< capability hba */
+ char cap_fc16g; /*!< capability fc 16g */
+ char cap_sriov; /*!< capability sriov */
+ char cap_mezz; /*!< capability mezz */
+ u8 rsv3;
+ u8 mfg_nports; /*!< number of ports */
+ char media[8]; /*!< xfi/xaui */
+ char initial_mode[8]; /*!< initial mode: hba/cna/nic */
+ u8 rsv4[84];
+ u8 md5_chksum[BFA_MFG_CHKSUM_SIZE]; /*!< md5 checksum */
};
#pragma pack()
@@ -386,17 +630,30 @@ enum {
BFA_PCI_DEVICE_ID_FC_8G1P = 0x17,
BFA_PCI_DEVICE_ID_CT = 0x14,
BFA_PCI_DEVICE_ID_CT_FC = 0x21,
+ BFA_PCI_DEVICE_ID_CT2 = 0x22,
+ BFA_PCI_DEVICE_ID_CT2_QUAD = 0x23,
};
-#define bfa_asic_id_ct(devid) \
- ((devid) == BFA_PCI_DEVICE_ID_CT || \
- (devid) == BFA_PCI_DEVICE_ID_CT_FC)
+#define bfa_asic_id_cb(__d) \
+ ((__d) == BFA_PCI_DEVICE_ID_FC_8G2P || \
+ (__d) == BFA_PCI_DEVICE_ID_FC_8G1P)
+#define bfa_asic_id_ct(__d) \
+ ((__d) == BFA_PCI_DEVICE_ID_CT || \
+ (__d) == BFA_PCI_DEVICE_ID_CT_FC)
+#define bfa_asic_id_ct2(__d) \
+ ((__d) == BFA_PCI_DEVICE_ID_CT2 || \
+ (__d) == BFA_PCI_DEVICE_ID_CT2_QUAD)
+#define bfa_asic_id_ctc(__d) \
+ (bfa_asic_id_ct(__d) || bfa_asic_id_ct2(__d))
/*
* PCI sub-system device and vendor ID information
*/
enum {
BFA_PCI_FCOE_SSDEVICE_ID = 0x14,
+ BFA_PCI_CT2_SSID_FCoE = 0x22,
+ BFA_PCI_CT2_SSID_ETH = 0x23,
+ BFA_PCI_CT2_SSID_FC = 0x24,
};
/*
@@ -416,9 +673,7 @@ enum bfa_port_speed {
BFA_PORT_SPEED_8GBPS = 8,
BFA_PORT_SPEED_10GBPS = 10,
BFA_PORT_SPEED_16GBPS = 16,
- BFA_PORT_SPEED_AUTO =
- (BFA_PORT_SPEED_1GBPS | BFA_PORT_SPEED_2GBPS |
- BFA_PORT_SPEED_4GBPS | BFA_PORT_SPEED_8GBPS),
+ BFA_PORT_SPEED_AUTO = 0xf,
};
#define bfa_port_speed_t enum bfa_port_speed
@@ -454,6 +709,20 @@ struct bfa_boot_bootlun_s {
/*
* BOOT boot configuraton
*/
+struct bfa_boot_cfg_s {
+ u8 version;
+ u8 rsvd1;
+ u16 chksum;
+ u8 enable; /* enable/disable SAN boot */
+ u8 speed; /* boot speed settings */
+ u8 topology; /* boot topology setting */
+ u8 bootopt; /* bfa_boot_bootopt_t */
+ u32 nbluns; /* number of boot luns */
+ u32 rsvd2;
+ struct bfa_boot_bootlun_s blun[BFA_BOOT_BOOTLUN_MAX];
+ struct bfa_boot_bootlun_s blun_disc[BFA_BOOT_BOOTLUN_MAX];
+};
+
struct bfa_boot_pbc_s {
u8 enable; /* enable/disable SAN boot */
u8 speed; /* boot speed settings */
@@ -463,4 +732,549 @@ struct bfa_boot_pbc_s {
struct bfa_boot_bootlun_s pblun[BFA_PREBOOT_BOOTLUN_MAX];
};
+struct bfa_ethboot_cfg_s {
+ u8 version;
+ u8 rsvd1;
+ u16 chksum;
+ u8 enable; /* enable/disable Eth/PXE boot */
+ u8 rsvd2;
+ u16 vlan;
+};
+
+/*
+ * ASIC block configuration related structures
+ */
+#define BFA_ABLK_MAX_PORTS 2
+#define BFA_ABLK_MAX_PFS 16
+#define BFA_ABLK_MAX 2
+
+#pragma pack(1)
+enum bfa_mode_s {
+ BFA_MODE_HBA = 1,
+ BFA_MODE_CNA = 2,
+ BFA_MODE_NIC = 3
+};
+
+struct bfa_adapter_cfg_mode_s {
+ u16 max_pf;
+ u16 max_vf;
+ enum bfa_mode_s mode;
+};
+
+struct bfa_ablk_cfg_pf_s {
+ u16 pers;
+ u8 port_id;
+ u8 optrom;
+ u8 valid;
+ u8 sriov;
+ u8 max_vfs;
+ u8 rsvd[1];
+ u16 num_qpairs;
+ u16 num_vectors;
+ u16 bw_min;
+ u16 bw_max;
+};
+
+struct bfa_ablk_cfg_port_s {
+ u8 mode;
+ u8 type;
+ u8 max_pfs;
+ u8 rsvd[5];
+};
+
+struct bfa_ablk_cfg_inst_s {
+ u8 nports;
+ u8 max_pfs;
+ u8 rsvd[6];
+ struct bfa_ablk_cfg_pf_s pf_cfg[BFA_ABLK_MAX_PFS];
+ struct bfa_ablk_cfg_port_s port_cfg[BFA_ABLK_MAX_PORTS];
+};
+
+struct bfa_ablk_cfg_s {
+ struct bfa_ablk_cfg_inst_s inst[BFA_ABLK_MAX];
+};
+
+
+/*
+ * SFP module specific
+ */
+#define SFP_DIAGMON_SIZE 10 /* num bytes of diag monitor data */
+
+/* SFP state change notification event */
+#define BFA_SFP_SCN_REMOVED 0
+#define BFA_SFP_SCN_INSERTED 1
+#define BFA_SFP_SCN_POM 2
+#define BFA_SFP_SCN_FAILED 3
+#define BFA_SFP_SCN_UNSUPPORT 4
+#define BFA_SFP_SCN_VALID 5
+
+enum bfa_defs_sfp_media_e {
+ BFA_SFP_MEDIA_UNKNOWN = 0x00,
+ BFA_SFP_MEDIA_CU = 0x01,
+ BFA_SFP_MEDIA_LW = 0x02,
+ BFA_SFP_MEDIA_SW = 0x03,
+ BFA_SFP_MEDIA_EL = 0x04,
+ BFA_SFP_MEDIA_UNSUPPORT = 0x05,
+};
+
+/*
+ * values for xmtr_tech above
+ */
+enum {
+ SFP_XMTR_TECH_CU = (1 << 0), /* copper FC-BaseT */
+ SFP_XMTR_TECH_CP = (1 << 1), /* copper passive */
+ SFP_XMTR_TECH_CA = (1 << 2), /* copper active */
+ SFP_XMTR_TECH_LL = (1 << 3), /* longwave laser */
+ SFP_XMTR_TECH_SL = (1 << 4), /* shortwave laser w/ OFC */
+ SFP_XMTR_TECH_SN = (1 << 5), /* shortwave laser w/o OFC */
+ SFP_XMTR_TECH_EL_INTRA = (1 << 6), /* elec intra-enclosure */
+ SFP_XMTR_TECH_EL_INTER = (1 << 7), /* elec inter-enclosure */
+ SFP_XMTR_TECH_LC = (1 << 8), /* longwave laser */
+ SFP_XMTR_TECH_SA = (1 << 9)
+};
+
+/*
+ * Serial ID: Data Fields -- Address A0h
+ * Basic ID field total 64 bytes
+ */
+struct sfp_srlid_base_s {
+ u8 id; /* 00: Identifier */
+ u8 extid; /* 01: Extended Identifier */
+ u8 connector; /* 02: Connector */
+ u8 xcvr[8]; /* 03-10: Transceiver */
+ u8 encoding; /* 11: Encoding */
+ u8 br_norm; /* 12: BR, Nominal */
+ u8 rate_id; /* 13: Rate Identifier */
+ u8 len_km; /* 14: Length single mode km */
+ u8 len_100m; /* 15: Length single mode 100m */
+ u8 len_om2; /* 16: Length om2 fiber 10m */
+ u8 len_om1; /* 17: Length om1 fiber 10m */
+ u8 len_cu; /* 18: Length copper 1m */
+ u8 len_om3; /* 19: Length om3 fiber 10m */
+ u8 vendor_name[16];/* 20-35 */
+ u8 unalloc1;
+ u8 vendor_oui[3]; /* 37-39 */
+ u8 vendor_pn[16]; /* 40-55 */
+ u8 vendor_rev[4]; /* 56-59 */
+ u8 wavelen[2]; /* 60-61 */
+ u8 unalloc2;
+ u8 cc_base; /* 63: check code for base id field */
+};
+
+/*
+ * Serial ID: Data Fields -- Address A0h
+ * Extended id field total 32 bytes
+ */
+struct sfp_srlid_ext_s {
+ u8 options[2];
+ u8 br_max;
+ u8 br_min;
+ u8 vendor_sn[16];
+ u8 date_code[8];
+ u8 diag_mon_type; /* 92: Diagnostic Monitoring type */
+ u8 en_options;
+ u8 sff_8472;
+ u8 cc_ext;
+};
+
+/*
+ * Diagnostic: Data Fields -- Address A2h
+ * Diagnostic and control/status base field total 96 bytes
+ */
+struct sfp_diag_base_s {
+ /*
+ * Alarm and warning Thresholds 40 bytes
+ */
+ u8 temp_high_alarm[2]; /* 00-01 */
+ u8 temp_low_alarm[2]; /* 02-03 */
+ u8 temp_high_warning[2]; /* 04-05 */
+ u8 temp_low_warning[2]; /* 06-07 */
+
+ u8 volt_high_alarm[2]; /* 08-09 */
+ u8 volt_low_alarm[2]; /* 10-11 */
+ u8 volt_high_warning[2]; /* 12-13 */
+ u8 volt_low_warning[2]; /* 14-15 */
+
+ u8 bias_high_alarm[2]; /* 16-17 */
+ u8 bias_low_alarm[2]; /* 18-19 */
+ u8 bias_high_warning[2]; /* 20-21 */
+ u8 bias_low_warning[2]; /* 22-23 */
+
+ u8 tx_pwr_high_alarm[2]; /* 24-25 */
+ u8 tx_pwr_low_alarm[2]; /* 26-27 */
+ u8 tx_pwr_high_warning[2]; /* 28-29 */
+ u8 tx_pwr_low_warning[2]; /* 30-31 */
+
+ u8 rx_pwr_high_alarm[2]; /* 32-33 */
+ u8 rx_pwr_low_alarm[2]; /* 34-35 */
+ u8 rx_pwr_high_warning[2]; /* 36-37 */
+ u8 rx_pwr_low_warning[2]; /* 38-39 */
+
+ u8 unallocate_1[16];
+
+ /*
+ * ext_cal_const[36]
+ */
+ u8 rx_pwr[20];
+ u8 tx_i[4];
+ u8 tx_pwr[4];
+ u8 temp[4];
+ u8 volt[4];
+ u8 unallocate_2[3];
+ u8 cc_dmi;
+};
+
+/*
+ * Diagnostic: Data Fields -- Address A2h
+ * Diagnostic and control/status extended field total 24 bytes
+ */
+struct sfp_diag_ext_s {
+ u8 diag[SFP_DIAGMON_SIZE];
+ u8 unalloc1[4];
+ u8 status_ctl;
+ u8 rsvd;
+ u8 alarm_flags[2];
+ u8 unalloc2[2];
+ u8 warning_flags[2];
+ u8 ext_status_ctl[2];
+};
+
+/*
+ * Diagnostic: Data Fields -- Address A2h
+ * General Use Fields: User Writable Table - Features's Control Registers
+ * Total 32 bytes
+ */
+struct sfp_usr_eeprom_s {
+ u8 rsvd1[2]; /* 128-129 */
+ u8 ewrap; /* 130 */
+ u8 rsvd2[2]; /* */
+ u8 owrap; /* 133 */
+ u8 rsvd3[2]; /* */
+ u8 prbs; /* 136: PRBS 7 generator */
+ u8 rsvd4[2]; /* */
+ u8 tx_eqz_16; /* 139: TX Equalizer (16xFC) */
+ u8 tx_eqz_8; /* 140: TX Equalizer (8xFC) */
+ u8 rsvd5[2]; /* */
+ u8 rx_emp_16; /* 143: RX Emphasis (16xFC) */
+ u8 rx_emp_8; /* 144: RX Emphasis (8xFC) */
+ u8 rsvd6[2]; /* */
+ u8 tx_eye_adj; /* 147: TX eye Threshold Adjust */
+ u8 rsvd7[3]; /* */
+ u8 tx_eye_qctl; /* 151: TX eye Quality Control */
+ u8 tx_eye_qres; /* 152: TX eye Quality Result */
+ u8 rsvd8[2]; /* */
+ u8 poh[3]; /* 155-157: Power On Hours */
+ u8 rsvd9[2]; /* */
+};
+
+struct sfp_mem_s {
+ struct sfp_srlid_base_s srlid_base;
+ struct sfp_srlid_ext_s srlid_ext;
+ struct sfp_diag_base_s diag_base;
+ struct sfp_diag_ext_s diag_ext;
+ struct sfp_usr_eeprom_s usr_eeprom;
+};
+
+/*
+ * transceiver codes (SFF-8472 Rev 10.2 Table 3.5)
+ */
+union sfp_xcvr_e10g_code_u {
+ u8 b;
+ struct {
+#ifdef __BIG_ENDIAN
+ u8 e10g_unall:1; /* 10G Ethernet compliance */
+ u8 e10g_lrm:1;
+ u8 e10g_lr:1;
+ u8 e10g_sr:1;
+ u8 ib_sx:1; /* Infiniband compliance */
+ u8 ib_lx:1;
+ u8 ib_cu_a:1;
+ u8 ib_cu_p:1;
+#else
+ u8 ib_cu_p:1;
+ u8 ib_cu_a:1;
+ u8 ib_lx:1;
+ u8 ib_sx:1; /* Infiniband compliance */
+ u8 e10g_sr:1;
+ u8 e10g_lr:1;
+ u8 e10g_lrm:1;
+ u8 e10g_unall:1; /* 10G Ethernet compliance */
+#endif
+ } r;
+};
+
+union sfp_xcvr_so1_code_u {
+ u8 b;
+ struct {
+ u8 escon:2; /* ESCON compliance code */
+ u8 oc192_reach:1; /* SONET compliance code */
+ u8 so_reach:2;
+ u8 oc48_reach:3;
+ } r;
+};
+
+union sfp_xcvr_so2_code_u {
+ u8 b;
+ struct {
+ u8 reserved:1;
+ u8 oc12_reach:3; /* OC12 reach */
+ u8 reserved1:1;
+ u8 oc3_reach:3; /* OC3 reach */
+ } r;
+};
+
+union sfp_xcvr_eth_code_u {
+ u8 b;
+ struct {
+ u8 base_px:1;
+ u8 base_bx10:1;
+ u8 e100base_fx:1;
+ u8 e100base_lx:1;
+ u8 e1000base_t:1;
+ u8 e1000base_cx:1;
+ u8 e1000base_lx:1;
+ u8 e1000base_sx:1;
+ } r;
+};
+
+struct sfp_xcvr_fc1_code_s {
+ u8 link_len:5; /* FC link length */
+ u8 xmtr_tech2:3;
+ u8 xmtr_tech1:7; /* FC transmitter technology */
+ u8 reserved1:1;
+};
+
+union sfp_xcvr_fc2_code_u {
+ u8 b;
+ struct {
+ u8 tw_media:1; /* twin axial pair (tw) */
+ u8 tp_media:1; /* shielded twisted pair (sp) */
+ u8 mi_media:1; /* miniature coax (mi) */
+ u8 tv_media:1; /* video coax (tv) */
+ u8 m6_media:1; /* multimode, 62.5m (m6) */
+ u8 m5_media:1; /* multimode, 50m (m5) */
+ u8 reserved:1;
+ u8 sm_media:1; /* single mode (sm) */
+ } r;
+};
+
+union sfp_xcvr_fc3_code_u {
+ u8 b;
+ struct {
+#ifdef __BIG_ENDIAN
+ u8 rsv4:1;
+ u8 mb800:1; /* 800 Mbytes/sec */
+ u8 mb1600:1; /* 1600 Mbytes/sec */
+ u8 mb400:1; /* 400 Mbytes/sec */
+ u8 rsv2:1;
+ u8 mb200:1; /* 200 Mbytes/sec */
+ u8 rsv1:1;
+ u8 mb100:1; /* 100 Mbytes/sec */
+#else
+ u8 mb100:1; /* 100 Mbytes/sec */
+ u8 rsv1:1;
+ u8 mb200:1; /* 200 Mbytes/sec */
+ u8 rsv2:1;
+ u8 mb400:1; /* 400 Mbytes/sec */
+ u8 mb1600:1; /* 1600 Mbytes/sec */
+ u8 mb800:1; /* 800 Mbytes/sec */
+ u8 rsv4:1;
+#endif
+ } r;
+};
+
+struct sfp_xcvr_s {
+ union sfp_xcvr_e10g_code_u e10g;
+ union sfp_xcvr_so1_code_u so1;
+ union sfp_xcvr_so2_code_u so2;
+ union sfp_xcvr_eth_code_u eth;
+ struct sfp_xcvr_fc1_code_s fc1;
+ union sfp_xcvr_fc2_code_u fc2;
+ union sfp_xcvr_fc3_code_u fc3;
+};
+
+/*
+ * Flash module specific
+ */
+#define BFA_FLASH_PART_ENTRY_SIZE 32 /* partition entry size */
+#define BFA_FLASH_PART_MAX 32 /* maximal # of partitions */
+
+enum bfa_flash_part_type {
+ BFA_FLASH_PART_OPTROM = 1, /* option rom partition */
+ BFA_FLASH_PART_FWIMG = 2, /* firmware image partition */
+ BFA_FLASH_PART_FWCFG = 3, /* firmware tuneable config */
+ BFA_FLASH_PART_DRV = 4, /* IOC driver config */
+ BFA_FLASH_PART_BOOT = 5, /* boot config */
+ BFA_FLASH_PART_ASIC = 6, /* asic bootstrap configuration */
+ BFA_FLASH_PART_MFG = 7, /* manufacturing block partition */
+ BFA_FLASH_PART_OPTROM2 = 8, /* 2nd option rom partition */
+ BFA_FLASH_PART_VPD = 9, /* vpd data of OEM info */
+ BFA_FLASH_PART_PBC = 10, /* pre-boot config */
+ BFA_FLASH_PART_BOOTOVL = 11, /* boot overlay partition */
+ BFA_FLASH_PART_LOG = 12, /* firmware log partition */
+ BFA_FLASH_PART_PXECFG = 13, /* pxe boot config partition */
+ BFA_FLASH_PART_PXEOVL = 14, /* pxe boot overlay partition */
+ BFA_FLASH_PART_PORTCFG = 15, /* port cfg partition */
+ BFA_FLASH_PART_ASICBK = 16, /* asic backup partition */
+};
+
+/*
+ * flash partition attributes
+ */
+struct bfa_flash_part_attr_s {
+ u32 part_type; /* partition type */
+ u32 part_instance; /* partition instance */
+ u32 part_off; /* partition offset */
+ u32 part_size; /* partition size */
+ u32 part_len; /* partition content length */
+ u32 part_status; /* partition status */
+ char rsv[BFA_FLASH_PART_ENTRY_SIZE - 24];
+};
+
+/*
+ * flash attributes
+ */
+struct bfa_flash_attr_s {
+ u32 status; /* flash overall status */
+ u32 npart; /* num of partitions */
+ struct bfa_flash_part_attr_s part[BFA_FLASH_PART_MAX];
+};
+
+/*
+ * DIAG module specific
+ */
+#define LB_PATTERN_DEFAULT 0xB5B5B5B5
+#define QTEST_CNT_DEFAULT 10
+#define QTEST_PAT_DEFAULT LB_PATTERN_DEFAULT
+#define DPORT_ENABLE_LOOPCNT_DEFAULT (1024 * 1024)
+
+struct bfa_diag_memtest_s {
+ u8 algo;
+ u8 rsvd[7];
+};
+
+struct bfa_diag_memtest_result {
+ u32 status;
+ u32 addr;
+ u32 exp; /* expect value read from reg */
+ u32 act; /* actually value read */
+ u32 err_status; /* error status reg */
+ u32 err_status1; /* extra error info reg */
+ u32 err_addr; /* error address reg */
+ u8 algo;
+ u8 rsv[3];
+};
+
+struct bfa_diag_loopback_result_s {
+ u32 numtxmfrm; /* no. of transmit frame */
+ u32 numosffrm; /* no. of outstanding frame */
+ u32 numrcvfrm; /* no. of received good frame */
+ u32 badfrminf; /* mis-match info */
+ u32 badfrmnum; /* mis-match fram number */
+ u8 status; /* loopback test result */
+ u8 rsvd[3];
+};
+
+enum bfa_diag_dport_test_status {
+ DPORT_TEST_ST_IDLE = 0, /* the test has not started yet. */
+ DPORT_TEST_ST_FINAL = 1, /* the test done successfully */
+ DPORT_TEST_ST_SKIP = 2, /* the test skipped */
+ DPORT_TEST_ST_FAIL = 3, /* the test failed */
+ DPORT_TEST_ST_INPRG = 4, /* the testing is in progress */
+ DPORT_TEST_ST_RESPONDER = 5, /* test triggered from remote port */
+ DPORT_TEST_ST_STOPPED = 6, /* the test stopped by user. */
+ DPORT_TEST_ST_MAX
+};
+
+enum bfa_diag_dport_test_type {
+ DPORT_TEST_ELOOP = 0,
+ DPORT_TEST_OLOOP = 1,
+ DPORT_TEST_ROLOOP = 2,
+ DPORT_TEST_LINK = 3,
+ DPORT_TEST_MAX
+};
+
+enum bfa_diag_dport_test_opmode {
+ BFA_DPORT_OPMODE_AUTO = 0,
+ BFA_DPORT_OPMODE_MANU = 1,
+};
+
+struct bfa_diag_dport_subtest_result_s {
+ u8 status; /* bfa_diag_dport_test_status */
+ u8 rsvd[7]; /* 64bit align */
+ u64 start_time; /* timestamp */
+};
+
+struct bfa_diag_dport_result_s {
+ wwn_t rp_pwwn; /* switch port wwn */
+ wwn_t rp_nwwn; /* switch node wwn */
+ u64 start_time; /* user/sw start time */
+ u64 end_time; /* timestamp */
+ u8 status; /* bfa_diag_dport_test_status */
+ u8 mode; /* bfa_diag_dport_test_opmode */
+ u8 rsvd; /* 64bit align */
+ u8 speed; /* link speed for buf_reqd */
+ u16 buffer_required;
+ u16 frmsz; /* frame size for buf_reqd */
+ u32 lpcnt; /* Frame count */
+ u32 pat; /* Pattern */
+ u32 roundtrip_latency; /* in nano sec */
+ u32 est_cable_distance; /* in meter */
+ struct bfa_diag_dport_subtest_result_s subtest[DPORT_TEST_MAX];
+};
+
+struct bfa_diag_ledtest_s {
+ u32 cmd; /* bfa_led_op_t */
+ u32 color; /* bfa_led_color_t */
+ u16 freq; /* no. of blinks every 10 secs */
+ u8 led; /* bitmap of LEDs to be tested */
+ u8 rsvd[5];
+};
+
+struct bfa_diag_loopback_s {
+ u32 loopcnt;
+ u32 pattern;
+ u8 lb_mode; /* bfa_port_opmode_t */
+ u8 speed; /* bfa_port_speed_t */
+ u8 rsvd[2];
+};
+
+/*
+ * PHY module specific
+ */
+enum bfa_phy_status_e {
+ BFA_PHY_STATUS_GOOD = 0, /* phy is good */
+ BFA_PHY_STATUS_NOT_PRESENT = 1, /* phy does not exist */
+ BFA_PHY_STATUS_BAD = 2, /* phy is bad */
+};
+
+/*
+ * phy attributes for phy query
+ */
+struct bfa_phy_attr_s {
+ u32 status; /* phy present/absent status */
+ u32 length; /* firmware length */
+ u32 fw_ver; /* firmware version */
+ u32 an_status; /* AN status */
+ u32 pma_pmd_status; /* PMA/PMD link status */
+ u32 pma_pmd_signal; /* PMA/PMD signal detect */
+ u32 pcs_status; /* PCS link status */
+};
+
+/*
+ * phy stats
+ */
+struct bfa_phy_stats_s {
+ u32 status; /* phy stats status */
+ u32 link_breaks; /* Num of link breaks after linkup */
+ u32 pma_pmd_fault; /* NPMA/PMD fault */
+ u32 pcs_fault; /* PCS fault */
+ u32 speed_neg; /* Num of speed negotiation */
+ u32 tx_eq_training; /* Num of TX EQ training */
+ u32 tx_eq_timeout; /* Num of TX EQ timeout */
+ u32 crc_error; /* Num of CRC errors */
+};
+
+#pragma pack()
+
#endif /* __BFA_DEFS_H__ */
diff --git a/drivers/scsi/bfa/bfa_defs_fcs.h b/drivers/scsi/bfa/bfa_defs_fcs.h
index 191d34a58b9c..760606062c66 100644
--- a/drivers/scsi/bfa/bfa_defs_fcs.h
+++ b/drivers/scsi/bfa/bfa_defs_fcs.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_DEFS_FCS_H__
@@ -90,12 +83,15 @@ enum bfa_lport_role {
* FCS port configuration.
*/
struct bfa_lport_cfg_s {
- wwn_t pwwn; /* port wwn */
- wwn_t nwwn; /* node wwn */
- struct bfa_lport_symname_s sym_name; /* vm port symbolic name */
- bfa_boolean_t preboot_vp; /* vport created from PBC */
- enum bfa_lport_role roles; /* FCS port roles */
- u8 tag[16]; /* opaque tag from application */
+ wwn_t pwwn; /* port wwn */
+ wwn_t nwwn; /* node wwn */
+ struct bfa_lport_symname_s sym_name; /* vm port symbolic name */
+ struct bfa_lport_symname_s node_sym_name; /* Node symbolic name */
+ enum bfa_lport_role roles; /* FCS port roles */
+ u32 rsvd;
+ bfa_boolean_t preboot_vp; /* vport created from PBC */
+ u8 tag[16]; /* opaque tag from application */
+ u8 padding[4];
};
/*
@@ -129,28 +125,6 @@ enum bfa_lport_offline_reason {
};
/*
- * FCS lport info.
- */
-struct bfa_lport_info_s {
- u8 port_type; /* bfa_lport_type_t : physical or
- * virtual */
- u8 port_state; /* one of bfa_lport_state values */
- u8 offline_reason; /* one of bfa_lport_offline_reason_t
- * values */
- wwn_t port_wwn;
- wwn_t node_wwn;
-
- /*
- * following 4 feilds are valid for Physical Ports only
- */
- u32 max_vports_supp; /* Max supported vports */
- u32 num_vports_inuse; /* Num of in use vports */
- u32 max_rports_supp; /* Max supported rports */
- u32 num_rports_inuse; /* Num of doscovered rports */
-
-};
-
-/*
* FCS port statistics
*/
struct bfa_lport_stats_s {
@@ -190,6 +164,18 @@ struct bfa_lport_stats_s {
u32 ns_gidft_unknown_rsp;
u32 ns_gidft_alloc_wait;
+ u32 ns_rnnid_sent;
+ u32 ns_rnnid_accepts;
+ u32 ns_rnnid_rsp_err;
+ u32 ns_rnnid_rejects;
+ u32 ns_rnnid_alloc_wait;
+
+ u32 ns_rsnn_nn_sent;
+ u32 ns_rsnn_nn_accepts;
+ u32 ns_rsnn_nn_rsp_err;
+ u32 ns_rsnn_nn_rejects;
+ u32 ns_rsnn_nn_alloc_wait;
+
/*
* Mgmt Server stats
*/
@@ -249,12 +235,13 @@ enum bfa_vport_state {
BFA_FCS_VPORT_FDISC_SEND = 2,
BFA_FCS_VPORT_FDISC = 3,
BFA_FCS_VPORT_FDISC_RETRY = 4,
- BFA_FCS_VPORT_ONLINE = 5,
- BFA_FCS_VPORT_DELETING = 6,
- BFA_FCS_VPORT_CLEANUP = 6,
- BFA_FCS_VPORT_LOGO_SEND = 7,
- BFA_FCS_VPORT_LOGO = 8,
- BFA_FCS_VPORT_ERROR = 9,
+ BFA_FCS_VPORT_FDISC_RSP_WAIT = 5,
+ BFA_FCS_VPORT_ONLINE = 6,
+ BFA_FCS_VPORT_DELETING = 7,
+ BFA_FCS_VPORT_CLEANUP = 8,
+ BFA_FCS_VPORT_LOGO_SEND = 9,
+ BFA_FCS_VPORT_LOGO = 10,
+ BFA_FCS_VPORT_ERROR = 11,
BFA_FCS_VPORT_MAX_STATE,
};
@@ -407,6 +394,11 @@ struct bfa_rport_remote_link_stats_s {
u32 icc; /* Invalid CRC Count */
};
+struct bfa_rport_qualifier_s {
+ wwn_t pwwn; /* Port WWN */
+ u32 pid; /* port ID */
+ u32 rsvd;
+};
#define BFA_MAX_IO_INDEX 7
#define BFA_NO_IO_INDEX 9
diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h
index 207f598877c7..f2c49f0e5c8b 100644
--- a/drivers/scsi/bfa/bfa_defs_svc.h
+++ b/drivers/scsi/bfa/bfa_defs_svc.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_DEFS_SVC_H__
@@ -34,40 +27,43 @@
struct bfa_iocfc_intr_attr_s {
u8 coalesce; /* enable/disable coalescing */
u8 rsvd[3];
- __be16 latency; /* latency in microseconds */
- __be16 delay; /* delay in microseconds */
+ __be16 latency; /* latency in microseconds */
+ __be16 delay; /* delay in microseconds */
};
/*
* IOC firmware configuraton
*/
struct bfa_iocfc_fwcfg_s {
- u16 num_fabrics; /* number of fabrics */
- u16 num_lports; /* number of local lports */
- u16 num_rports; /* number of remote ports */
- u16 num_ioim_reqs; /* number of IO reqs */
- u16 num_tskim_reqs; /* task management requests */
- u16 num_iotm_reqs; /* number of TM IO reqs */
- u16 num_tsktm_reqs; /* TM task management requests*/
- u16 num_fcxp_reqs; /* unassisted FC exchanges */
- u16 num_uf_bufs; /* unsolicited recv buffers */
+ u16 num_fabrics; /* number of fabrics */
+ u16 num_lports; /* number of local lports */
+ u16 num_rports; /* number of remote ports */
+ u16 num_ioim_reqs; /* number of IO reqs */
+ u16 num_tskim_reqs; /* task management requests */
+ u16 num_fwtio_reqs; /* number of TM IO reqs in FW */
+ u16 num_fcxp_reqs; /* unassisted FC exchanges */
+ u16 num_uf_bufs; /* unsolicited recv buffers */
u8 num_cqs;
u8 fw_tick_res; /* FW clock resolution in ms */
- u8 rsvd[4];
+ u8 rsvd[6];
};
#pragma pack()
struct bfa_iocfc_drvcfg_s {
- u16 num_reqq_elems; /* number of req queue elements */
- u16 num_rspq_elems; /* number of rsp queue elements */
- u16 num_sgpgs; /* number of total SG pages */
- u16 num_sboot_tgts; /* number of SAN boot targets */
- u16 num_sboot_luns; /* number of SAN boot luns */
- u16 ioc_recover; /* IOC recovery mode */
- u16 min_cfg; /* minimum configuration */
- u16 path_tov; /* device path timeout */
- bfa_boolean_t delay_comp; /* delay completion of
- failed inflight IOs */
+ u16 num_reqq_elems; /* number of req queue elements */
+ u16 num_rspq_elems; /* number of rsp queue elements */
+ u16 num_sgpgs; /* number of total SG pages */
+ u16 num_sboot_tgts; /* number of SAN boot targets */
+ u16 num_sboot_luns; /* number of SAN boot luns */
+ u16 ioc_recover; /* IOC recovery mode */
+ u16 min_cfg; /* minimum configuration */
+ u16 path_tov; /* device path timeout */
+ u16 num_tio_reqs; /* number of TM IO reqs */
+ u8 port_mode;
+ u8 rsvd_a;
+ bfa_boolean_t delay_comp; /* delay completion of failed
+ * inflight IOs */
+ u16 num_ttsk_reqs; /* TM task management requests */
u32 rsvd;
};
@@ -82,7 +78,7 @@ struct bfa_iocfc_cfg_s {
/*
* IOC firmware IO stats
*/
-struct bfa_fw_io_stats_s {
+struct bfa_fw_ioim_stats_s {
u32 host_abort; /* IO aborted by host driver*/
u32 host_cleanup; /* IO clean up by host driver */
@@ -98,10 +94,13 @@ struct bfa_fw_io_stats_s {
u32 fw_frm_drop; /* f/w drop the frame */
u32 rec_timeout; /* FW rec timed out */
- u32 error_rec; /* FW sending rec on
- * an error condition*/
+ u32 error_rec; /* FW sending rec on
+ * an error condition*/
u32 wait_for_si; /* FW wait for SI */
u32 rec_rsp_inval; /* REC rsp invalid */
+ u32 rec_rsp_xchg_comp; /* REC rsp xchg complete */
+ u32 rec_rsp_rd_si_ownd; /* REC rsp read si owned */
+
u32 seqr_io_abort; /* target does not know cmd so abort */
u32 seqr_io_retry; /* SEQR failed so retry IO */
@@ -121,9 +120,9 @@ struct bfa_fw_io_stats_s {
u32 unexp_fcp_rsp; /* fcp response in wrong state */
u32 fcp_rsp_under_run; /* fcp rsp IO underrun */
- u32 fcp_rsp_under_run_wr; /* fcp rsp IO underrun for write */
+ u32 fcp_rsp_under_run_wr; /* fcp rsp IO underrun for write */
u32 fcp_rsp_under_run_err; /* fcp rsp IO underrun error */
- u32 fcp_rsp_resid_inval; /* invalid residue */
+ u32 fcp_rsp_resid_inval; /* invalid residue */
u32 fcp_rsp_over_run; /* fcp rsp IO overrun */
u32 fcp_rsp_over_run_err; /* fcp rsp IO overrun error */
u32 fcp_rsp_proto_err; /* protocol error in fcp rsp */
@@ -139,17 +138,66 @@ struct bfa_fw_io_stats_s {
u32 ioh_hit_class2_event; /* IOH hit class2 */
u32 ioh_miss_other_event; /* IOH miss other */
u32 ioh_seq_cnt_err_event; /* IOH seq cnt error */
- u32 ioh_len_err_event; /* IOH len error - fcp_dl !=
- * bytes xfered */
+ u32 ioh_len_err_event; /* IOH len error - fcp_dl !=
+ * bytes xfered */
u32 ioh_seq_len_err_event; /* IOH seq len error */
u32 ioh_data_oor_event; /* Data out of range */
u32 ioh_ro_ooo_event; /* Relative offset out of range */
u32 ioh_cpu_owned_event; /* IOH hit -iost owned by f/w */
u32 ioh_unexp_frame_event; /* unexpected frame received
- * count */
+ * count */
u32 ioh_err_int; /* IOH error int during data-phase
- * for scsi write
- */
+ * for scsi write */
+};
+
+struct bfa_fw_tio_stats_s {
+ u32 tio_conf_proc; /* TIO CONF processed */
+ u32 tio_conf_drop; /* TIO CONF dropped */
+ u32 tio_cleanup_req; /* TIO cleanup requested */
+ u32 tio_cleanup_comp; /* TIO cleanup completed */
+ u32 tio_abort_rsp; /* TIO abort response */
+ u32 tio_abort_rsp_comp; /* TIO abort rsp completed */
+ u32 tio_abts_req; /* TIO ABTS requested */
+ u32 tio_abts_ack; /* TIO ABTS ack-ed */
+ u32 tio_abts_ack_nocomp;/* TIO ABTS ack-ed but not completed */
+ u32 tio_abts_tmo; /* TIO ABTS timeout */
+ u32 tio_snsdata_dma; /* TIO sense data DMA */
+ u32 tio_rxwchan_wait; /* TIO waiting for RX wait channel */
+ u32 tio_rxwchan_avail; /* TIO RX wait channel available */
+ u32 tio_hit_bls; /* TIO IOH BLS event */
+ u32 tio_uf_recv; /* TIO received UF */
+ u32 tio_rd_invalid_sm; /* TIO read reqst in wrong state machine */
+ u32 tio_wr_invalid_sm; /* TIO write reqst in wrong state machine */
+
+ u32 ds_rxwchan_wait; /* DS waiting for RX wait channel */
+ u32 ds_rxwchan_avail; /* DS RX wait channel available */
+ u32 ds_unaligned_rd; /* DS unaligned read */
+ u32 ds_rdcomp_invalid_sm; /* DS read completed in wrong state
+ * machine */
+ u32 ds_wrcomp_invalid_sm; /* DS write completed in wrong state
+ * machine */
+ u32 ds_flush_req; /* DS flush requested */
+ u32 ds_flush_comp; /* DS flush completed */
+ u32 ds_xfrdy_exp; /* DS XFER_RDY expired */
+ u32 ds_seq_cnt_err; /* DS seq cnt error */
+ u32 ds_seq_len_err; /* DS seq len error */
+ u32 ds_data_oor; /* DS data out of order */
+ u32 ds_hit_bls; /* DS hit BLS */
+ u32 ds_edtov_timer_exp; /* DS edtov expired */
+ u32 ds_cpu_owned; /* DS cpu owned */
+ u32 ds_hit_class2; /* DS hit class2 */
+ u32 ds_length_err; /* DS length error */
+ u32 ds_ro_ooo_err; /* DS relative offset out-of-order error */
+ u32 ds_rectov_timer_exp;/* DS rectov expired */
+ u32 ds_unexp_fr_err; /* DS unexp frame error */
+};
+
+/*
+ * IOC firmware IO stats
+ */
+struct bfa_fw_io_stats_s {
+ struct bfa_fw_ioim_stats_s ioim_stats;
+ struct bfa_fw_tio_stats_s tio_stats;
};
/*
@@ -157,117 +205,154 @@ struct bfa_fw_io_stats_s {
*/
struct bfa_fw_port_fpg_stats_s {
- u32 intr_evt;
- u32 intr;
- u32 intr_excess;
- u32 intr_cause0;
- u32 intr_other;
- u32 intr_other_ign;
- u32 sig_lost;
- u32 sig_regained;
- u32 sync_lost;
- u32 sync_to;
- u32 sync_regained;
- u32 div2_overflow;
- u32 div2_underflow;
- u32 efifo_overflow;
- u32 efifo_underflow;
- u32 idle_rx;
- u32 lrr_rx;
- u32 lr_rx;
- u32 ols_rx;
- u32 nos_rx;
- u32 lip_rx;
- u32 arbf0_rx;
- u32 arb_rx;
- u32 mrk_rx;
- u32 const_mrk_rx;
- u32 prim_unknown;
+ u32 intr_evt;
+ u32 intr;
+ u32 intr_excess;
+ u32 intr_cause0;
+ u32 intr_other;
+ u32 intr_other_ign;
+ u32 sig_lost;
+ u32 sig_regained;
+ u32 sync_lost;
+ u32 sync_to;
+ u32 sync_regained;
+ u32 div2_overflow;
+ u32 div2_underflow;
+ u32 efifo_overflow;
+ u32 efifo_underflow;
+ u32 idle_rx;
+ u32 lrr_rx;
+ u32 lr_rx;
+ u32 ols_rx;
+ u32 nos_rx;
+ u32 lip_rx;
+ u32 arbf0_rx;
+ u32 arb_rx;
+ u32 mrk_rx;
+ u32 const_mrk_rx;
+ u32 prim_unknown;
};
struct bfa_fw_port_lksm_stats_s {
- u32 hwsm_success; /* hwsm state machine success */
- u32 hwsm_fails; /* hwsm fails */
- u32 hwsm_wdtov; /* hwsm timed out */
- u32 swsm_success; /* swsm success */
- u32 swsm_fails; /* swsm fails */
- u32 swsm_wdtov; /* swsm timed out */
- u32 busybufs; /* link init failed due to busybuf */
- u32 buf_waits; /* bufwait state entries */
- u32 link_fails; /* link failures */
- u32 psp_errors; /* primitive sequence protocol errors */
- u32 lr_unexp; /* No. of times LR rx-ed unexpectedly */
- u32 lrr_unexp; /* No. of times LRR rx-ed unexpectedly */
- u32 lr_tx; /* No. of times LR tx started */
- u32 lrr_tx; /* No. of times LRR tx started */
- u32 ols_tx; /* No. of times OLS tx started */
- u32 nos_tx; /* No. of times NOS tx started */
- u32 hwsm_lrr_rx; /* No. of times LRR rx-ed by HWSM */
- u32 hwsm_lr_rx; /* No. of times LR rx-ed by HWSM */
+ u32 hwsm_success; /* hwsm state machine success */
+ u32 hwsm_fails; /* hwsm fails */
+ u32 hwsm_wdtov; /* hwsm timed out */
+ u32 swsm_success; /* swsm success */
+ u32 swsm_fails; /* swsm fails */
+ u32 swsm_wdtov; /* swsm timed out */
+ u32 busybufs; /* link init failed due to busybuf */
+ u32 buf_waits; /* bufwait state entries */
+ u32 link_fails; /* link failures */
+ u32 psp_errors; /* primitive sequence protocol errors */
+ u32 lr_unexp; /* No. of times LR rx-ed unexpectedly */
+ u32 lrr_unexp; /* No. of times LRR rx-ed unexpectedly */
+ u32 lr_tx; /* No. of times LR tx started */
+ u32 lrr_tx; /* No. of times LRR tx started */
+ u32 ols_tx; /* No. of times OLS tx started */
+ u32 nos_tx; /* No. of times NOS tx started */
+ u32 hwsm_lrr_rx; /* No. of times LRR rx-ed by HWSM */
+ u32 hwsm_lr_rx; /* No. of times LR rx-ed by HWSM */
};
struct bfa_fw_port_snsm_stats_s {
- u32 hwsm_success; /* Successful hwsm terminations */
- u32 hwsm_fails; /* hwsm fail count */
- u32 hwsm_wdtov; /* hwsm timed out */
- u32 swsm_success; /* swsm success */
- u32 swsm_wdtov; /* swsm timed out */
- u32 error_resets; /* error resets initiated by upsm */
- u32 sync_lost; /* Sync loss count */
- u32 sig_lost; /* Signal loss count */
+ u32 hwsm_success; /* Successful hwsm terminations */
+ u32 hwsm_fails; /* hwsm fail count */
+ u32 hwsm_wdtov; /* hwsm timed out */
+ u32 swsm_success; /* swsm success */
+ u32 swsm_wdtov; /* swsm timed out */
+ u32 error_resets; /* error resets initiated by upsm */
+ u32 sync_lost; /* Sync loss count */
+ u32 sig_lost; /* Signal loss count */
+ u32 asn8g_attempts; /* SNSM HWSM at 8Gbps attempts */
+ u32 adapt_success; /* SNSM adaptation success */
+ u32 adapt_fails; /* SNSM adaptation failures */
+ u32 adapt_ign_fails; /* SNSM adaptation failures ignored */
};
struct bfa_fw_port_physm_stats_s {
- u32 module_inserts; /* Module insert count */
- u32 module_xtracts; /* Module extracts count */
- u32 module_invalids; /* Invalid module inserted count */
- u32 module_read_ign; /* Module validation status ignored */
- u32 laser_faults; /* Laser fault count */
- u32 rsvd;
+ u32 module_inserts; /* Module insert count */
+ u32 module_xtracts; /* Module extracts count */
+ u32 module_invalids; /* Invalid module inserted count */
+ u32 module_read_ign; /* Module validation status ignored */
+ u32 laser_faults; /* Laser fault count */
+ u32 rsvd;
};
struct bfa_fw_fip_stats_s {
- u32 vlan_req; /* vlan discovery requests */
- u32 vlan_notify; /* vlan notifications */
- u32 vlan_err; /* vlan response error */
- u32 vlan_timeouts; /* vlan disvoery timeouts */
- u32 vlan_invalids; /* invalid vlan in discovery advert. */
- u32 disc_req; /* Discovery solicit requests */
- u32 disc_rsp; /* Discovery solicit response */
- u32 disc_err; /* Discovery advt. parse errors */
- u32 disc_unsol; /* Discovery unsolicited */
- u32 disc_timeouts; /* Discovery timeouts */
- u32 disc_fcf_unavail; /* Discovery FCF Not Avail. */
- u32 linksvc_unsupp; /* Unsupported link service req */
- u32 linksvc_err; /* Parse error in link service req */
- u32 logo_req; /* FIP logos received */
- u32 clrvlink_req; /* Clear virtual link req */
- u32 op_unsupp; /* Unsupported FIP operation */
- u32 untagged; /* Untagged frames (ignored) */
- u32 invalid_version; /* Invalid FIP version */
+ u32 vlan_req; /* vlan discovery requests */
+ u32 vlan_notify; /* vlan notifications */
+ u32 vlan_err; /* vlan response error */
+ u32 vlan_timeouts; /* vlan disvoery timeouts */
+ u32 vlan_invalids; /* invalid vlan in discovery advert. */
+ u32 disc_req; /* Discovery solicit requests */
+ u32 disc_rsp; /* Discovery solicit response */
+ u32 disc_err; /* Discovery advt. parse errors */
+ u32 disc_unsol; /* Discovery unsolicited */
+ u32 disc_timeouts; /* Discovery timeouts */
+ u32 disc_fcf_unavail; /* Discovery FCF Not Avail. */
+ u32 linksvc_unsupp; /* Unsupported link service req */
+ u32 linksvc_err; /* Parse error in link service req */
+ u32 logo_req; /* FIP logos received */
+ u32 clrvlink_req; /* Clear virtual link req */
+ u32 op_unsupp; /* Unsupported FIP operation */
+ u32 untagged; /* Untagged frames (ignored) */
+ u32 invalid_version; /* Invalid FIP version */
};
struct bfa_fw_lps_stats_s {
- u32 mac_invalids; /* Invalid mac assigned */
- u32 rsvd;
+ u32 mac_invalids; /* Invalid mac assigned */
+ u32 rsvd;
};
struct bfa_fw_fcoe_stats_s {
- u32 cee_linkups; /* CEE link up count */
- u32 cee_linkdns; /* CEE link down count */
- u32 fip_linkups; /* FIP link up count */
- u32 fip_linkdns; /* FIP link up count */
- u32 fip_fails; /* FIP fail count */
- u32 mac_invalids; /* Invalid mac assigned */
+ u32 cee_linkups; /* CEE link up count */
+ u32 cee_linkdns; /* CEE link down count */
+ u32 fip_linkups; /* FIP link up count */
+ u32 fip_linkdns; /* FIP link up count */
+ u32 fip_fails; /* FIP fail count */
+ u32 mac_invalids; /* Invalid mac assigned */
};
/*
* IOC firmware FCoE port stats
*/
struct bfa_fw_fcoe_port_stats_s {
- struct bfa_fw_fcoe_stats_s fcoe_stats;
- struct bfa_fw_fip_stats_s fip_stats;
+ struct bfa_fw_fcoe_stats_s fcoe_stats;
+ struct bfa_fw_fip_stats_s fip_stats;
+};
+
+/**
+ * @brief LPSM statistics
+ */
+struct bfa_fw_lpsm_stats_s {
+ u32 cls_rx; /* LPSM cls_rx */
+ u32 cls_tx; /* LPSM cls_tx */
+ u32 arbf0_rx; /* LPSM abrf0 rcvd */
+ u32 arbf0_tx; /* LPSM abrf0 xmit */
+ u32 init_rx; /* LPSM loop init start */
+ u32 unexp_hwst; /* LPSM unknown hw state */
+ u32 unexp_frame; /* LPSM unknown_frame */
+ u32 unexp_prim; /* LPSM unexpected primitive */
+ u32 prev_alpa_unavail; /* LPSM prev alpa unavailable */
+ u32 alpa_unavail; /* LPSM alpa not available */
+ u32 lip_rx; /* LPSM lip rcvd */
+ u32 lip_f7f7_rx; /* LPSM lip f7f7 rcvd */
+ u32 lip_f8_rx; /* LPSM lip f8 rcvd */
+ u32 lip_f8f7_rx; /* LPSM lip f8f7 rcvd */
+ u32 lip_other_rx; /* LPSM lip other rcvd */
+ u32 lip_tx; /* LPSM lip xmit */
+ u32 retry_tov; /* LPSM retry TOV */
+ u32 lip_tov; /* LPSM LIP wait TOV */
+ u32 idle_tov; /* LPSM idle wait TOV */
+ u32 arbf0_tov; /* LPSM arbfo wait TOV */
+ u32 stop_loop_tov; /* LPSM stop loop wait TOV */
+ u32 lixa_tov; /* LPSM lisa wait TOV */
+ u32 lixx_tov; /* LPSM lilp/lirp wait TOV */
+ u32 cls_tov; /* LPSM cls wait TOV */
+ u32 sler; /* LPSM SLER recvd */
+ u32 failed; /* LPSM failed */
+ u32 success; /* LPSM online */
};
/*
@@ -276,14 +361,15 @@ struct bfa_fw_fcoe_port_stats_s {
struct bfa_fw_fc_uport_stats_s {
struct bfa_fw_port_snsm_stats_s snsm_stats;
struct bfa_fw_port_lksm_stats_s lksm_stats;
+ struct bfa_fw_lpsm_stats_s lpsm_stats;
};
/*
* IOC firmware FC port stats
*/
union bfa_fw_fc_port_stats_s {
- struct bfa_fw_fc_uport_stats_s fc_stats;
- struct bfa_fw_fcoe_port_stats_s fcoe_stats;
+ struct bfa_fw_fc_uport_stats_s fc_stats;
+ struct bfa_fw_fcoe_port_stats_s fcoe_stats;
};
/*
@@ -303,35 +389,36 @@ struct bfa_fw_fcxchg_stats_s {
u32 ua_state_inv;
};
-struct bfa_fw_lpsm_stats_s {
- u32 cls_rx;
- u32 cls_tx;
-};
-
/*
* Trunk statistics
*/
struct bfa_fw_trunk_stats_s {
u32 emt_recvd; /* Trunk EMT received */
- u32 emt_accepted; /* Trunk EMT Accepted */
- u32 emt_rejected; /* Trunk EMT rejected */
+ u32 emt_accepted; /* Trunk EMT Accepted */
+ u32 emt_rejected; /* Trunk EMT rejected */
u32 etp_recvd; /* Trunk ETP received */
- u32 etp_accepted; /* Trunk ETP Accepted */
- u32 etp_rejected; /* Trunk ETP rejected */
+ u32 etp_accepted; /* Trunk ETP Accepted */
+ u32 etp_rejected; /* Trunk ETP rejected */
u32 lr_recvd; /* Trunk LR received */
- u32 rsvd; /* padding for 64 bit alignment */
+ u32 rsvd; /* padding for 64 bit alignment */
};
-struct bfa_fw_advsm_stats_s {
+struct bfa_fw_aport_stats_s {
u32 flogi_sent; /* Flogi sent */
u32 flogi_acc_recvd; /* Flogi Acc received */
u32 flogi_rjt_recvd; /* Flogi rejects received */
- u32 flogi_retries; /* Flogi retries */
+ u32 flogi_retries; /* Flogi retries */
u32 elp_recvd; /* ELP received */
- u32 elp_accepted; /* ELP Accepted */
- u32 elp_rejected; /* ELP rejected */
- u32 elp_dropped; /* ELP dropped */
+ u32 elp_accepted; /* ELP Accepted */
+ u32 elp_rejected; /* ELP rejected */
+ u32 elp_dropped; /* ELP dropped */
+
+ u32 bbcr_lr_count; /*!< BBCR Link Resets */
+ u32 frame_lost_intrs; /*!< BBCR Frame loss intrs */
+ u32 rrdy_lost_intrs; /*!< BBCR Rrdy loss intrs */
+
+ u32 rsvd;
};
/*
@@ -392,6 +479,14 @@ struct bfa_fw_ct_mod_stats_s {
};
/*
+ * RDS mod stats
+ */
+struct bfa_fw_rds_stats_s {
+ u32 no_fid_drop_err; /* RDS no fid drop error */
+ u32 rsvd; /* 64bit align */
+};
+
+/*
* IOC firmware stats
*/
struct bfa_fw_stats_s {
@@ -400,13 +495,13 @@ struct bfa_fw_stats_s {
struct bfa_fw_io_stats_s io_stats;
struct bfa_fw_port_stats_s port_stats;
struct bfa_fw_fcxchg_stats_s fcxchg_stats;
- struct bfa_fw_lpsm_stats_s lpsm_stats;
struct bfa_fw_lps_stats_s lps_stats;
struct bfa_fw_trunk_stats_s trunk_stats;
- struct bfa_fw_advsm_stats_s advsm_stats;
+ struct bfa_fw_aport_stats_s aport_stats;
struct bfa_fw_mac_mod_stats_s macmod_stats;
struct bfa_fw_ct_mod_stats_s ctmod_stats;
struct bfa_fw_eth_sndrcv_stats_s ethsndrcv_stats;
+ struct bfa_fw_rds_stats_s rds_stats;
};
#define BFA_IOCFC_PATHTOV_MAX 60
@@ -416,6 +511,7 @@ struct bfa_fw_stats_s {
* QoS states
*/
enum bfa_qos_state {
+ BFA_QOS_DISABLED = 0, /* QoS is disabled */
BFA_QOS_ONLINE = 1, /* QoS is online */
BFA_QOS_OFFLINE = 2, /* QoS is offline */
};
@@ -439,13 +535,44 @@ enum bfa_qos_bw_alloc {
BFA_QOS_BW_LOW = 10, /* bandwidth allocation for Low */
};
#pragma pack(1)
+
+struct bfa_qos_bw_s {
+ u8 qos_bw_set;
+ u8 high;
+ u8 med;
+ u8 low;
+};
+
/*
* QoS attribute returned in QoS Query
*/
struct bfa_qos_attr_s {
- u8 state; /* QoS current state */
- u8 rsvd[3];
- u32 total_bb_cr; /* Total BB Credits */
+ u8 state; /* QoS current state */
+ u8 rsvd1[3];
+ u32 total_bb_cr; /* Total BB Credits */
+ struct bfa_qos_bw_s qos_bw; /* QOS bw cfg */
+ struct bfa_qos_bw_s qos_bw_op; /* QOS bw operational */
+};
+
+enum bfa_bbcr_state {
+ BFA_BBCR_DISABLED, /*!< BBCR is disable */
+ BFA_BBCR_ONLINE, /*!< BBCR is online */
+ BFA_BBCR_OFFLINE, /*!< BBCR is offline */
+};
+
+enum bfa_bbcr_err_reason {
+ BFA_BBCR_ERR_REASON_NONE, /*!< Unknown */
+ BFA_BBCR_ERR_REASON_SPEED_UNSUP, /*!< Port speed < max sup_speed */
+ BFA_BBCR_ERR_REASON_PEER_UNSUP, /*!< BBCR is disable on peer port */
+ BFA_BBCR_ERR_REASON_NON_BRCD_SW, /*!< Connected to non BRCD switch */
+ BFA_BBCR_ERR_REASON_FLOGI_RJT, /*!< Login rejected by the switch */
+};
+
+struct bfa_bbcr_attr_s {
+ u8 state;
+ u8 peer_bb_scn;
+ u8 reason;
+ u8 rsvd;
};
/*
@@ -467,7 +594,7 @@ struct bfa_qos_vc_attr_s {
u16 total_vc_count; /* Total VC Count */
u16 shared_credit;
u32 elp_opmode_flags;
- struct bfa_qos_vc_info_s vc_info[BFA_QOS_MAX_VC]; /* as many as
+ struct bfa_qos_vc_info_s vc_info[BFA_QOS_MAX_VC]; /* as many as
* total_vc_count */
};
@@ -477,16 +604,16 @@ struct bfa_qos_vc_attr_s {
struct bfa_qos_stats_s {
u32 flogi_sent; /* QoS Flogi sent */
u32 flogi_acc_recvd; /* QoS Flogi Acc received */
- u32 flogi_rjt_recvd; /* QoS Flogi rejects received */
+ u32 flogi_rjt_recvd; /* QoS Flogi rejects received */
u32 flogi_retries; /* QoS Flogi retries */
u32 elp_recvd; /* QoS ELP received */
u32 elp_accepted; /* QoS ELP Accepted */
- u32 elp_rejected; /* QoS ELP rejected */
- u32 elp_dropped; /* QoS ELP dropped */
+ u32 elp_rejected; /* QoS ELP rejected */
+ u32 elp_dropped; /* QoS ELP dropped */
- u32 qos_rscn_recvd; /* QoS RSCN received */
- u32 rsvd; /* padding for 64 bit alignment */
+ u32 qos_rscn_recvd; /* QoS RSCN received */
+ u32 rsvd; /* padding for 64 bit alignment */
};
/*
@@ -494,9 +621,9 @@ struct bfa_qos_stats_s {
*/
struct bfa_fcoe_stats_s {
u64 secs_reset; /* Seconds since stats reset */
- u64 cee_linkups; /* CEE link up */
+ u64 cee_linkups; /* CEE link up */
u64 cee_linkdns; /* CEE link down */
- u64 fip_linkups; /* FIP link up */
+ u64 fip_linkups; /* FIP link up */
u64 fip_linkdns; /* FIP link down */
u64 fip_fails; /* FIP failures */
u64 mac_invalids; /* Invalid mac assignments */
@@ -506,38 +633,38 @@ struct bfa_fcoe_stats_s {
u64 vlan_timeouts; /* Vlan request timeouts */
u64 vlan_invalids; /* Vlan invalids */
u64 disc_req; /* Discovery requests */
- u64 disc_rsp; /* Discovery responses */
+ u64 disc_rsp; /* Discovery responses */
u64 disc_err; /* Discovery error frames */
u64 disc_unsol; /* Discovery unsolicited */
u64 disc_timeouts; /* Discovery timeouts */
u64 disc_fcf_unavail; /* Discovery FCF not avail */
- u64 linksvc_unsupp; /* FIP link service req unsupp. */
- u64 linksvc_err; /* FIP link service req errors */
+ u64 linksvc_unsupp; /* FIP link service req unsupp */
+ u64 linksvc_err; /* FIP link service req errors */
u64 logo_req; /* FIP logos received */
- u64 clrvlink_req; /* Clear virtual link requests */
+ u64 clrvlink_req; /* Clear virtual link requests */
u64 op_unsupp; /* FIP operation unsupp. */
- u64 untagged; /* FIP untagged frames */
+ u64 untagged; /* FIP untagged frames */
u64 txf_ucast; /* Tx FCoE unicast frames */
- u64 txf_ucast_vlan; /* Tx FCoE unicast vlan frames */
+ u64 txf_ucast_vlan; /* Tx FCoE unicast vlan frames */
u64 txf_ucast_octets; /* Tx FCoE unicast octets */
u64 txf_mcast; /* Tx FCoE multicast frames */
- u64 txf_mcast_vlan; /* Tx FCoE multicast vlan frames */
+ u64 txf_mcast_vlan; /* Tx FCoE multicast vlan frames */
u64 txf_mcast_octets; /* Tx FCoE multicast octets */
u64 txf_bcast; /* Tx FCoE broadcast frames */
- u64 txf_bcast_vlan; /* Tx FCoE broadcast vlan frames */
+ u64 txf_bcast_vlan; /* Tx FCoE broadcast vlan frames */
u64 txf_bcast_octets; /* Tx FCoE broadcast octets */
- u64 txf_timeout; /* Tx timeouts */
+ u64 txf_timeout; /* Tx timeouts */
u64 txf_parity_errors; /* Transmit parity err */
- u64 txf_fid_parity_errors; /* Transmit FID parity err */
+ u64 txf_fid_parity_errors; /* Transmit FID parity err */
u64 rxf_ucast_octets; /* Rx FCoE unicast octets */
u64 rxf_ucast; /* Rx FCoE unicast frames */
- u64 rxf_ucast_vlan; /* Rx FCoE unicast vlan frames */
+ u64 rxf_ucast_vlan; /* Rx FCoE unicast vlan frames */
u64 rxf_mcast_octets; /* Rx FCoE multicast octets */
u64 rxf_mcast; /* Rx FCoE multicast frames */
- u64 rxf_mcast_vlan; /* Rx FCoE multicast vlan frames */
+ u64 rxf_mcast_vlan; /* Rx FCoE multicast vlan frames */
u64 rxf_bcast_octets; /* Rx FCoE broadcast octets */
u64 rxf_bcast; /* Rx FCoE broadcast frames */
- u64 rxf_bcast_vlan; /* Rx FCoE broadcast vlan frames */
+ u64 rxf_bcast_vlan; /* Rx FCoE broadcast vlan frames */
};
/*
@@ -618,6 +745,7 @@ struct bfa_itnim_iostats_s {
u32 tm_iocdowns; /* TM cleaned-up due to IOC down */
u32 tm_cleanups; /* TM cleanup requests */
u32 tm_cleanup_comps; /* TM cleanup completions */
+ u32 rsvd[6];
};
/* Modify char* port_stt[] in bfal_port.c if a new state was added */
@@ -636,6 +764,9 @@ enum bfa_port_states {
BFA_PORT_ST_FWMISMATCH = 12,
BFA_PORT_ST_PREBOOT_DISABLED = 13,
BFA_PORT_ST_TOGGLING_QWAIT = 14,
+ BFA_PORT_ST_FAA_MISCONFIG = 15,
+ BFA_PORT_ST_DPORT = 16,
+ BFA_PORT_ST_DDPORT = 17,
BFA_PORT_ST_MAX_STATE,
};
@@ -657,9 +788,11 @@ enum bfa_port_type {
*/
enum bfa_port_topology {
BFA_PORT_TOPOLOGY_NONE = 0, /* No valid topology */
- BFA_PORT_TOPOLOGY_P2P = 1, /* P2P only */
- BFA_PORT_TOPOLOGY_LOOP = 2, /* LOOP topology */
- BFA_PORT_TOPOLOGY_AUTO = 3, /* auto topology selection */
+ BFA_PORT_TOPOLOGY_P2P_OLD_VER = 1, /* P2P def for older ver */
+ BFA_PORT_TOPOLOGY_LOOP = 2, /* LOOP topology */
+ BFA_PORT_TOPOLOGY_AUTO_OLD_VER = 3, /* auto def for older ver */
+ BFA_PORT_TOPOLOGY_AUTO = 4, /* auto topology selection */
+ BFA_PORT_TOPOLOGY_P2P = 5, /* P2P only */
};
/*
@@ -703,6 +836,7 @@ enum bfa_port_linkstate_rsn {
BFA_PORT_LINKSTATE_RSN_LOCAL_FAULT = 9,
BFA_PORT_LINKSTATE_RSN_REMOTE_FAULT = 10,
BFA_PORT_LINKSTATE_RSN_TIMEOUT = 11,
+ BFA_PORT_LINKSTATE_RSN_FAA_MISCONFIG = 12,
@@ -732,8 +866,76 @@ enum bfa_port_linkstate_rsn {
CEE_ISCSI_PRI_PFC_OFF = 42,
CEE_ISCSI_PRI_OVERLAP_FCOE_PRI = 43
};
+
+#define MAX_LUN_MASK_CFG 16
+
+/*
+ * Initially flash content may be fff. On making LUN mask enable and disable
+ * state change. when report lun command is being processed it goes from
+ * BFA_LUN_MASK_ACTIVE to BFA_LUN_MASK_FETCH and comes back to
+ * BFA_LUN_MASK_ACTIVE.
+ */
+enum bfa_ioim_lun_mask_state_s {
+ BFA_IOIM_LUN_MASK_INACTIVE = 0,
+ BFA_IOIM_LUN_MASK_ACTIVE = 1,
+ BFA_IOIM_LUN_MASK_FETCHED = 2,
+};
+
+enum bfa_lunmask_state_s {
+ BFA_LUNMASK_DISABLED = 0x00,
+ BFA_LUNMASK_ENABLED = 0x01,
+ BFA_LUNMASK_MINCFG = 0x02,
+ BFA_LUNMASK_UNINITIALIZED = 0xff,
+};
+
+/**
+ * FEC states
+ */
+enum bfa_fec_state_s {
+ BFA_FEC_ONLINE = 1, /*!< FEC is online */
+ BFA_FEC_OFFLINE = 2, /*!< FEC is offline */
+ BFA_FEC_OFFLINE_NOT_16G = 3, /*!< FEC is offline (speed not 16Gig) */
+};
+
#pragma pack(1)
/*
+ * LUN mask configuration
+ */
+struct bfa_lun_mask_s {
+ wwn_t lp_wwn;
+ wwn_t rp_wwn;
+ struct scsi_lun lun;
+ u8 ua;
+ u8 rsvd[3];
+ u16 rp_tag;
+ u8 lp_tag;
+ u8 state;
+};
+
+#define MAX_LUN_MASK_CFG 16
+struct bfa_lunmask_cfg_s {
+ u32 status;
+ u32 rsvd;
+ struct bfa_lun_mask_s lun_list[MAX_LUN_MASK_CFG];
+};
+
+struct bfa_throttle_cfg_s {
+ u16 is_valid;
+ u16 value;
+ u32 rsvd;
+};
+
+struct bfa_defs_fcpim_throttle_s {
+ u16 max_value;
+ u16 cur_value;
+ u16 cfg_value;
+ u16 rsvd;
+};
+
+#define BFA_BB_SCN_DEF 3
+#define BFA_BB_SCN_MAX 0x0F
+
+/*
* Physical port configuration
*/
struct bfa_port_cfg_s {
@@ -748,8 +950,13 @@ struct bfa_port_cfg_s {
u8 tx_bbcredit; /* transmit buffer credits */
u8 ratelimit; /* ratelimit enabled or not */
u8 trl_def_speed; /* ratelimit default speed */
- u16 path_tov; /* device path timeout */
- u16 q_depth; /* SCSI Queue depth */
+ u8 bb_cr_enabled; /*!< Config state of BB_SCN */
+ u8 bb_scn; /*!< BB_SCN value for FLOGI Exchg */
+ u8 faa_state; /* FAA enabled/disabled */
+ u8 rsvd1;
+ u16 path_tov; /* device path timeout */
+ u16 q_depth; /* SCSI Queue depth */
+ struct bfa_qos_bw_s qos_bw; /* QOS bandwidth */
};
#pragma pack()
@@ -760,20 +967,21 @@ struct bfa_port_attr_s {
/*
* Static fields
*/
- wwn_t nwwn; /* node wwn */
- wwn_t pwwn; /* port wwn */
- wwn_t factorynwwn; /* factory node wwn */
- wwn_t factorypwwn; /* factory port wwn */
- enum fc_cos cos_supported; /* supported class of services */
- u32 rsvd;
+ wwn_t nwwn; /* node wwn */
+ wwn_t pwwn; /* port wwn */
+ wwn_t factorynwwn; /* factory node wwn */
+ wwn_t factorypwwn; /* factory port wwn */
+ enum fc_cos cos_supported; /* supported class of
+ * services */
+ u32 rsvd;
struct fc_symname_s port_symname; /* port symbolic name */
- enum bfa_port_speed speed_supported; /* supported speeds */
- bfa_boolean_t pbind_enabled;
+ enum bfa_port_speed speed_supported; /* supported speeds */
+ bfa_boolean_t pbind_enabled;
/*
* Configured values
*/
- struct bfa_port_cfg_s pport_cfg; /* pport cfg */
+ struct bfa_port_cfg_s pport_cfg; /* pport cfg */
/*
* Dynamic field - info from BFA
@@ -782,70 +990,70 @@ struct bfa_port_attr_s {
enum bfa_port_speed speed; /* current speed */
enum bfa_port_topology topology; /* current topology */
bfa_boolean_t beacon; /* current beacon status */
- bfa_boolean_t link_e2e_beacon; /* link beacon is on */
- bfa_boolean_t plog_enabled; /* portlog is enabled */
+ bfa_boolean_t link_e2e_beacon; /* link beacon is on */
+ bfa_boolean_t bbsc_op_status; /* fc credit recovery oper
+ * state */
+ enum bfa_fec_state_s fec_state; /*!< current FEC state */
/*
* Dynamic field - info from FCS
*/
- u32 pid; /* port ID */
+ u32 pid; /* port ID */
enum bfa_port_type port_type; /* current topology */
- u32 loopback; /* external loopback */
- u32 authfail; /* auth fail state */
- bfa_boolean_t io_profile; /* get it from fcpim mod */
- u8 pad[4]; /* for 64-bit alignement */
+ u32 loopback; /* external loopback */
+ u32 authfail; /* auth fail state */
/* FCoE specific */
- u16 fcoe_vlan;
- u8 rsvd1[6];
+ u16 fcoe_vlan;
+ u8 rsvd1[2];
};
/*
* Port FCP mappings.
*/
struct bfa_port_fcpmap_s {
- char osdevname[256];
+ char osdevname[256];
u32 bus;
u32 target;
u32 oslun;
u32 fcid;
- wwn_t nwwn;
- wwn_t pwwn;
+ wwn_t nwwn;
+ wwn_t pwwn;
u64 fcplun;
- char luid[256];
+ char luid[256];
};
/*
* Port RNID info.
*/
struct bfa_port_rnid_s {
- wwn_t wwn;
+ wwn_t wwn;
u32 unittype;
u32 portid;
u32 attached_nodes_num;
u16 ip_version;
u16 udp_port;
- u8 ipaddr[16];
+ u8 ipaddr[16];
u16 rsvd;
u16 topologydiscoveryflags;
};
#pragma pack(1)
struct bfa_fcport_fcf_s {
- wwn_t name; /* FCF name */
- wwn_t fabric_name; /* Fabric Name */
- u8 fipenabled; /* FIP enabled or not */
- u8 fipfailed; /* FIP failed or not */
- u8 resv[2];
- u8 pri; /* FCF priority */
- u8 version; /* FIP version used */
- u8 available; /* Available for login */
- u8 fka_disabled; /* FKA is disabled */
- u8 maxsz_verified; /* FCoE max size verified */
- u8 fc_map[3]; /* FC map */
- __be16 vlan; /* FCoE vlan tag/priority */
- u32 fka_adv_per; /* FIP ka advert. period */
- mac_t mac; /* FCF mac */
+ wwn_t name; /* FCF name */
+ wwn_t fabric_name; /* Fabric Name */
+ u8 fipenabled; /* FIP enabled or not */
+ u8 fipfailed; /* FIP failed or not */
+ u8 resv[2];
+ u8 pri; /* FCF priority */
+ u8 version; /* FIP version used */
+ u8 available; /* Available for login */
+ u8 fka_disabled; /* FKA is disabled */
+ u8 maxsz_verified; /* FCoE max size verified */
+ u8 fc_map[3]; /* FC map */
+ __be16 vlan; /* FCoE vlan tag/priority */
+ u32 fka_adv_per; /* FIP ka advert. period */
+ mac_t mac; /* FCF mac */
};
/*
@@ -867,6 +1075,13 @@ struct bfa_trunk_vc_attr_s {
u16 vc_credits[8];
};
+struct bfa_fcport_loop_info_s {
+ u8 myalpa; /* alpa claimed */
+ u8 alpabm_val; /* alpa bitmap valid or not (1 or 0) */
+ u8 resvd[6];
+ struct fc_alpabm_s alpabm; /* alpa bitmap */
+};
+
/*
* Link state information
*/
@@ -875,15 +1090,22 @@ struct bfa_port_link_s {
u8 linkstate_rsn; /* bfa_port_linkstate_rsn_t */
u8 topology; /* P2P/LOOP bfa_port_topology */
u8 speed; /* Link speed (1/2/4/8 G) */
- u32 linkstate_opt; /* Linkstate optional data (debug) */
+ u32 linkstate_opt; /* Linkstate optional data (debug) */
u8 trunked; /* Trunked or not (1 or 0) */
- u8 resvd[3];
+ u8 fec_state; /*!< State of FEC */
+ u8 resvd[6];
struct bfa_qos_attr_s qos_attr; /* QoS Attributes */
union {
- struct bfa_qos_vc_attr_s qos_vc_attr; /* VC info from ELP */
- struct bfa_trunk_vc_attr_s trunk_vc_attr;
- struct bfa_fcport_fcf_s fcf; /* FCF information (for FCoE) */
- } vc_fcf;
+ struct bfa_fcport_loop_info_s loop_info;
+ struct bfa_bbcr_attr_s bbcr_attr;
+ union {
+ struct bfa_qos_vc_attr_s qos_vc_attr;
+ /* VC info from ELP */
+ struct bfa_trunk_vc_attr_s trunk_vc_attr;
+ struct bfa_fcport_fcf_s fcf;
+ /* FCF information (for FCoE) */
+ } vc_fcf;
+ } attr;
};
#pragma pack()
@@ -929,7 +1151,7 @@ struct bfa_rport_hal_stats_s {
u32 sm_fwc_del; /* fw create: delete events */
u32 sm_fwc_off; /* fw create: offline events */
u32 sm_fwc_hwf; /* fw create: IOC down */
- u32 sm_fwc_unexp; /* fw create: exception events*/
+ u32 sm_fwc_unexp; /* fw create: exception events*/
u32 sm_on_off; /* online: offline events */
u32 sm_on_del; /* online: delete events */
u32 sm_on_hwf; /* online: IOC down events */
@@ -937,25 +1159,25 @@ struct bfa_rport_hal_stats_s {
u32 sm_fwd_rsp; /* fw delete: fw responses */
u32 sm_fwd_del; /* fw delete: delete events */
u32 sm_fwd_hwf; /* fw delete: IOC down events */
- u32 sm_fwd_unexp; /* fw delete: exception events*/
+ u32 sm_fwd_unexp; /* fw delete: exception events*/
u32 sm_off_del; /* offline: delete events */
u32 sm_off_on; /* offline: online events */
u32 sm_off_hwf; /* offline: IOC down events */
- u32 sm_off_unexp; /* offline: exception events */
- u32 sm_del_fwrsp; /* delete: fw responses */
+ u32 sm_off_unexp; /* offline: exception events */
+ u32 sm_del_fwrsp; /* delete: fw responses */
u32 sm_del_hwf; /* delete: IOC down events */
- u32 sm_del_unexp; /* delete: exception events */
- u32 sm_delp_fwrsp; /* delete pend: fw responses */
+ u32 sm_del_unexp; /* delete: exception events */
+ u32 sm_delp_fwrsp; /* delete pend: fw responses */
u32 sm_delp_hwf; /* delete pend: IOC downs */
- u32 sm_delp_unexp; /* delete pend: exceptions */
- u32 sm_offp_fwrsp; /* off-pending: fw responses */
+ u32 sm_delp_unexp; /* delete pend: exceptions */
+ u32 sm_offp_fwrsp; /* off-pending: fw responses */
u32 sm_offp_del; /* off-pending: deletes */
u32 sm_offp_hwf; /* off-pending: IOC downs */
- u32 sm_offp_unexp; /* off-pending: exceptions */
+ u32 sm_offp_unexp; /* off-pending: exceptions */
u32 sm_iocd_off; /* IOC down: offline events */
u32 sm_iocd_del; /* IOC down: delete events */
u32 sm_iocd_on; /* IOC down: online events */
- u32 sm_iocd_unexp; /* IOC down: exceptions */
+ u32 sm_iocd_unexp; /* IOC down: exceptions */
u32 rsvd;
};
#pragma pack(1)
@@ -963,9 +1185,9 @@ struct bfa_rport_hal_stats_s {
* Rport's QoS attributes
*/
struct bfa_rport_qos_attr_s {
- u8 qos_priority; /* rport's QoS priority */
- u8 rsvd[3];
- u32 qos_flow_id; /* QoS flow Id */
+ u8 qos_priority; /* rport's QoS priority */
+ u8 rsvd[3];
+ u32 qos_flow_id; /* QoS flow Id */
};
#pragma pack()
@@ -988,6 +1210,19 @@ struct bfa_itnim_ioprofile_s {
};
/*
+ * vHBA port attribute values.
+ */
+struct bfa_vhba_attr_s {
+ wwn_t nwwn; /* node wwn */
+ wwn_t pwwn; /* port wwn */
+ u32 pid; /* port ID */
+ bfa_boolean_t io_profile; /* get it from fcpim mod */
+ bfa_boolean_t plog_enabled; /* portlog is enabled */
+ u16 path_tov;
+ u8 rsvd[2];
+};
+
+/*
* FC physical port statistics.
*/
struct bfa_port_fc_stats_s {
@@ -995,6 +1230,9 @@ struct bfa_port_fc_stats_s {
u64 tx_frames; /* Tx frames */
u64 tx_words; /* Tx words */
u64 tx_lip; /* Tx LIP */
+ u64 tx_lip_f7f7; /* Tx LIP_F7F7 */
+ u64 tx_lip_f8f7; /* Tx LIP_F8F7 */
+ u64 tx_arbf0; /* Tx ARB F0 */
u64 tx_nos; /* Tx NOS */
u64 tx_ols; /* Tx OLS */
u64 tx_lr; /* Tx LR */
@@ -1002,6 +1240,9 @@ struct bfa_port_fc_stats_s {
u64 rx_frames; /* Rx frames */
u64 rx_words; /* Rx words */
u64 lip_count; /* Rx LIP */
+ u64 rx_lip_f7f7; /* Rx LIP_F7F7 */
+ u64 rx_lip_f8f7; /* Rx LIP_F8F7 */
+ u64 rx_arbf0; /* Rx ARB F0 */
u64 nos_count; /* Rx NOS */
u64 ols_count; /* Rx OLS */
u64 lr_count; /* Rx LR */
@@ -1020,6 +1261,12 @@ struct bfa_port_fc_stats_s {
u64 bad_os_count; /* Invalid ordered sets */
u64 err_enc_out; /* Encoding err nonframe_8b10b */
u64 err_enc; /* Encoding err frame_8b10b */
+ u64 bbcr_frames_lost; /*!< BBCR Frames Lost */
+ u64 bbcr_rrdys_lost; /*!< BBCR RRDYs Lost */
+ u64 bbcr_link_resets; /*!< BBCR Link Resets */
+ u64 bbcr_frame_lost_intrs; /*!< BBCR Frame loss intrs */
+ u64 bbcr_rrdy_lost_intrs; /*!< BBCR Rrdy loss intrs */
+ u64 loop_timeouts; /* Loop timeouts */
};
/*
@@ -1078,4 +1325,132 @@ union bfa_port_stats_u {
struct bfa_port_eth_stats_s eth;
};
+struct bfa_port_cfg_mode_s {
+ u16 max_pf;
+ u16 max_vf;
+ enum bfa_mode_s mode;
+};
+
+#pragma pack(1)
+
+#define BFA_CEE_LLDP_MAX_STRING_LEN (128)
+#define BFA_CEE_DCBX_MAX_PRIORITY (8)
+#define BFA_CEE_DCBX_MAX_PGID (8)
+
+struct bfa_cee_lldp_str_s {
+ u8 sub_type;
+ u8 len;
+ u8 rsvd[2];
+ u8 value[BFA_CEE_LLDP_MAX_STRING_LEN];
+};
+
+struct bfa_cee_lldp_cfg_s {
+ struct bfa_cee_lldp_str_s chassis_id;
+ struct bfa_cee_lldp_str_s port_id;
+ struct bfa_cee_lldp_str_s port_desc;
+ struct bfa_cee_lldp_str_s sys_name;
+ struct bfa_cee_lldp_str_s sys_desc;
+ struct bfa_cee_lldp_str_s mgmt_addr;
+ u16 time_to_live;
+ u16 enabled_system_cap;
+};
+
+/* CEE/DCBX parameters */
+struct bfa_cee_dcbx_cfg_s {
+ u8 pgid[BFA_CEE_DCBX_MAX_PRIORITY];
+ u8 pg_percentage[BFA_CEE_DCBX_MAX_PGID];
+ u8 pfc_primap; /* bitmap of priorties with PFC enabled */
+ u8 fcoe_primap; /* bitmap of priorities used for FcoE traffic */
+ u8 iscsi_primap; /* bitmap of priorities used for iSCSI traffic */
+ u8 dcbx_version; /* operating version:CEE or preCEE */
+ u8 lls_fcoe; /* FCoE Logical Link Status */
+ u8 lls_lan; /* LAN Logical Link Status */
+ u8 rsvd[2];
+};
+
+/* CEE Query */
+struct bfa_cee_attr_s {
+ u8 cee_status;
+ u8 error_reason;
+ struct bfa_cee_lldp_cfg_s lldp_remote;
+ struct bfa_cee_dcbx_cfg_s dcbx_remote;
+ mac_t src_mac;
+ u8 link_speed;
+ u8 nw_priority;
+ u8 filler[2];
+};
+
+/* LLDP/DCBX/CEE Statistics */
+struct bfa_cee_stats_s {
+ u32 lldp_tx_frames; /* LLDP Tx Frames */
+ u32 lldp_rx_frames; /* LLDP Rx Frames */
+ u32 lldp_rx_frames_invalid; /* LLDP Rx Frames invalid */
+ u32 lldp_rx_frames_new; /* LLDP Rx Frames new */
+ u32 lldp_tlvs_unrecognized; /* LLDP Rx unrecog. TLVs */
+ u32 lldp_rx_shutdown_tlvs; /* LLDP Rx shutdown TLVs */
+ u32 lldp_info_aged_out; /* LLDP remote info aged */
+ u32 dcbx_phylink_ups; /* DCBX phy link ups */
+ u32 dcbx_phylink_downs; /* DCBX phy link downs */
+ u32 dcbx_rx_tlvs; /* DCBX Rx TLVs */
+ u32 dcbx_rx_tlvs_invalid; /* DCBX Rx TLVs invalid */
+ u32 dcbx_control_tlv_error; /* DCBX control TLV errors */
+ u32 dcbx_feature_tlv_error; /* DCBX feature TLV errors */
+ u32 dcbx_cee_cfg_new; /* DCBX new CEE cfg rcvd */
+ u32 cee_status_down; /* DCB status down */
+ u32 cee_status_up; /* DCB status up */
+ u32 cee_hw_cfg_changed; /* DCB hw cfg changed */
+ u32 cee_rx_invalid_cfg; /* DCB invalid cfg */
+};
+
+#pragma pack()
+
+/*
+ * AEN related definitions
+ */
+#define BFAD_NL_VENDOR_ID (((u64)0x01 << SCSI_NL_VID_TYPE_SHIFT) \
+ | BFA_PCI_VENDOR_ID_BROCADE)
+
+/* BFA remote port events */
+enum bfa_rport_aen_event {
+ BFA_RPORT_AEN_ONLINE = 1, /* RPort online event */
+ BFA_RPORT_AEN_OFFLINE = 2, /* RPort offline event */
+ BFA_RPORT_AEN_DISCONNECT = 3, /* RPort disconnect event */
+ BFA_RPORT_AEN_QOS_PRIO = 4, /* QOS priority change event */
+ BFA_RPORT_AEN_QOS_FLOWID = 5, /* QOS flow Id change event */
+};
+
+struct bfa_rport_aen_data_s {
+ u16 vf_id; /* vf_id of this logical port */
+ u16 rsvd[3];
+ wwn_t ppwwn; /* WWN of its physical port */
+ wwn_t lpwwn; /* WWN of this logical port */
+ wwn_t rpwwn; /* WWN of this remote port */
+ union {
+ struct bfa_rport_qos_attr_s qos;
+ } priv;
+};
+
+union bfa_aen_data_u {
+ struct bfa_adapter_aen_data_s adapter;
+ struct bfa_port_aen_data_s port;
+ struct bfa_lport_aen_data_s lport;
+ struct bfa_rport_aen_data_s rport;
+ struct bfa_itnim_aen_data_s itnim;
+ struct bfa_audit_aen_data_s audit;
+ struct bfa_ioc_aen_data_s ioc;
+};
+
+#define BFA_AEN_MAX_ENTRY 512
+
+struct bfa_aen_entry_s {
+ struct list_head qe;
+ enum bfa_aen_category aen_category;
+ int aen_type;
+ union bfa_aen_data_u aen_data;
+ u64 aen_tv_sec;
+ u64 aen_tv_usec;
+ u32 seq_num;
+ u32 bfad_num;
+};
+
#endif /* __BFA_DEFS_SVC_H__ */
diff --git a/drivers/scsi/bfa/bfa_fc.h b/drivers/scsi/bfa/bfa_fc.h
index bf0067e0fd0d..1091aa428533 100644
--- a/drivers/scsi/bfa/bfa_fc.h
+++ b/drivers/scsi/bfa/bfa_fc.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_FC_H__
@@ -24,6 +17,7 @@ typedef u64 wwn_t;
#define WWN_NULL (0)
#define FC_SYMNAME_MAX 256 /* max name server symbolic name size */
+#define FC_ALPA_MAX 128
#pragma pack(1)
@@ -39,21 +33,6 @@ struct scsi_cdb_s {
u8 scsi_cdb[SCSI_MAX_CDBLEN];
};
-/* ------------------------------------------------------------
- * SCSI status byte values
- * ------------------------------------------------------------
- */
-#define SCSI_STATUS_GOOD 0x00
-#define SCSI_STATUS_CHECK_CONDITION 0x02
-#define SCSI_STATUS_CONDITION_MET 0x04
-#define SCSI_STATUS_BUSY 0x08
-#define SCSI_STATUS_INTERMEDIATE 0x10
-#define SCSI_STATUS_ICM 0x14 /* intermediate condition met */
-#define SCSI_STATUS_RESERVATION_CONFLICT 0x18
-#define SCSI_STATUS_COMMAND_TERMINATED 0x22
-#define SCSI_STATUS_QUEUE_FULL 0x28
-#define SCSI_STATUS_ACA_ACTIVE 0x30
-
#define SCSI_MAX_ALLOC_LEN 0xFF /* maximum allocarion length */
/*
@@ -821,7 +800,7 @@ struct fc_rscn_pl_s {
u8 command;
u8 pagelen;
__be16 payldlen;
- struct fc_rscn_event_s event[1];
+ struct fc_rscn_event_s event[];
};
/*
@@ -1015,6 +994,10 @@ struct fc_symname_s {
u8 symname[FC_SYMNAME_MAX];
};
+struct fc_alpabm_s {
+ u8 alpa_bm[FC_ALPA_MAX / 8];
+};
+
/*
* protocol default timeout values
*/
@@ -1049,15 +1032,6 @@ struct fc_vft_s {
};
/*
- * FCP
- */
-enum {
- FCP_RJT = 0x01000000, /* SRR reject */
- FCP_SRR_ACCEPT = 0x02000000, /* SRR accept */
- FCP_SRR = 0x14000000, /* Sequence Retransmission Request */
-};
-
-/*
* FCP_CMND definitions
*/
#define FCP_CMND_CDB_LEN 16
@@ -1219,7 +1193,7 @@ enum {
};
/*
- * defintions for CT reason code
+ * definitions for CT reason code
*/
enum {
CT_RSN_INV_CMD = 0x01,
@@ -1266,7 +1240,7 @@ enum {
};
/*
- * defintions for the explanation code for all servers
+ * definitions for the explanation code for all servers
*/
enum {
CT_EXP_AUTH_EXCEPTION = 0xF1,
@@ -1288,6 +1262,7 @@ enum {
GS_GSPN_ID = 0x0118, /* Get symbolic PN on ID */
GS_RFT_ID = 0x0217, /* Register fc4type on ID */
GS_RSPN_ID = 0x0218, /* Register symbolic PN on ID */
+ GS_RSNN_NN = 0x0239, /* Register symbolic NN on NN */
GS_RPN_ID = 0x0212, /* Register port name */
GS_RNN_ID = 0x0213, /* Register node name */
GS_RCS_ID = 0x0214, /* Register class of service */
@@ -1366,6 +1341,15 @@ struct fcgs_rspnid_req_s {
};
/*
+ * RSNN_NN
+ */
+struct fcgs_rsnn_nn_req_s {
+ wwn_t node_name; /* Node name */
+ u8 snn_len; /* symbolic node name length */
+ u8 snn[256]; /* symbolic node name */
+};
+
+/*
* RPN_ID
*/
struct fcgs_rpnid_req_s {
@@ -1525,6 +1509,12 @@ enum fdmi_hba_attribute_type {
FDMI_HBA_ATTRIB_FW_VERSION, /* 0x0009 */
FDMI_HBA_ATTRIB_OS_NAME, /* 0x000A */
FDMI_HBA_ATTRIB_MAX_CT, /* 0x000B */
+ FDMI_HBA_ATTRIB_NODE_SYM_NAME, /* 0x000C */
+ FDMI_HBA_ATTRIB_VENDOR_INFO, /* 0x000D */
+ FDMI_HBA_ATTRIB_NUM_PORTS, /* 0x000E */
+ FDMI_HBA_ATTRIB_FABRIC_NAME, /* 0x000F */
+ FDMI_HBA_ATTRIB_BIOS_VER, /* 0x0010 */
+ FDMI_HBA_ATTRIB_VENDOR_ID = 0x00E0,
FDMI_HBA_ATTRIB_MAX_TYPE
};
@@ -1539,6 +1529,15 @@ enum fdmi_port_attribute_type {
FDMI_PORT_ATTRIB_FRAME_SIZE, /* 0x0004 */
FDMI_PORT_ATTRIB_DEV_NAME, /* 0x0005 */
FDMI_PORT_ATTRIB_HOST_NAME, /* 0x0006 */
+ FDMI_PORT_ATTRIB_NODE_NAME, /* 0x0007 */
+ FDMI_PORT_ATTRIB_PORT_NAME, /* 0x0008 */
+ FDMI_PORT_ATTRIB_PORT_SYM_NAME, /* 0x0009 */
+ FDMI_PORT_ATTRIB_PORT_TYPE, /* 0x000A */
+ FDMI_PORT_ATTRIB_SUPP_COS, /* 0x000B */
+ FDMI_PORT_ATTRIB_PORT_FAB_NAME, /* 0x000C */
+ FDMI_PORT_ATTRIB_PORT_FC4_TYPE, /* 0x000D */
+ FDMI_PORT_ATTRIB_PORT_STATE = 0x101, /* 0x0101 */
+ FDMI_PORT_ATTRIB_PORT_NUM_RPRT = 0x102, /* 0x0102 */
FDMI_PORT_ATTR_MAX_TYPE
};
@@ -1549,7 +1548,7 @@ enum fdmi_port_attribute_type {
struct fdmi_attr_s {
__be16 type;
__be16 len;
- u8 value[1];
+ u8 value[];
};
/*
diff --git a/drivers/scsi/bfa/bfa_fcbuild.c b/drivers/scsi/bfa/bfa_fcbuild.c
index b7e253451654..c44fd096ee68 100644
--- a/drivers/scsi/bfa/bfa_fcbuild.c
+++ b/drivers/scsi/bfa/bfa_fcbuild.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
* fcbuild.c - FC link service frame building and parsing routines
@@ -94,7 +87,6 @@ fcbuild_init(void)
*/
plogi_tmpl.csp.verhi = FC_PH_VER_PH_3;
plogi_tmpl.csp.verlo = FC_PH_VER_4_3;
- plogi_tmpl.csp.bbcred = cpu_to_be16(0x0004);
plogi_tmpl.csp.ciro = 0x1;
plogi_tmpl.csp.cisc = 0x0;
plogi_tmpl.csp.altbbcred = 0x0;
@@ -156,6 +148,22 @@ fc_gs_fchdr_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u32 ox_id)
*/
}
+static void
+fc_gsresp_fchdr_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id)
+{
+ memset(fchs, 0, sizeof(struct fchs_s));
+
+ fchs->routing = FC_RTG_FC4_DEV_DATA;
+ fchs->cat_info = FC_CAT_SOLICIT_CTRL;
+ fchs->type = FC_TYPE_SERVICES;
+ fchs->f_ctl =
+ bfa_hton3b(FCTL_EC_RESP | FCTL_SEQ_INI | FCTL_LS_EXCH |
+ FCTL_END_SEQ | FCTL_SI_XFER);
+ fchs->d_id = d_id;
+ fchs->s_id = s_id;
+ fchs->ox_id = ox_id;
+}
+
void
fc_els_req_build(struct fchs_s *fchs, u32 d_id, u32 s_id, __be16 ox_id)
{
@@ -174,27 +182,6 @@ fc_els_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, __be16 ox_id)
fchs->ox_id = ox_id;
}
-enum fc_parse_status
-fc_els_rsp_parse(struct fchs_s *fchs, int len)
-{
- struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
- struct fc_ls_rjt_s *ls_rjt = (struct fc_ls_rjt_s *) els_cmd;
-
- len = len;
-
- switch (els_cmd->els_code) {
- case FC_ELS_LS_RJT:
- if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
- return FC_PARSE_BUSY;
- else
- return FC_PARSE_FAILURE;
-
- case FC_ELS_ACC:
- return FC_PARSE_OK;
- }
- return FC_PARSE_OK;
-}
-
static void
fc_bls_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, __be16 ox_id)
{
@@ -207,12 +194,16 @@ fc_bls_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, __be16 ox_id)
static u16
fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
__be16 ox_id, wwn_t port_name, wwn_t node_name,
- u16 pdu_size, u8 els_code)
+ u16 pdu_size, u16 bb_cr, u8 els_code)
{
struct fc_logi_s *plogi = (struct fc_logi_s *) (pld);
memcpy(plogi, &plogi_tmpl, sizeof(struct fc_logi_s));
+ /* For FC AL bb_cr is 0 and altbbcred is 1 */
+ if (!bb_cr)
+ plogi->csp.altbbcred = 1;
+
plogi->els_cmd.els_code = els_code;
if (els_code == FC_ELS_PLOGI)
fc_els_req_build(fchs, d_id, s_id, ox_id);
@@ -220,6 +211,7 @@ fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
fc_els_rsp_build(fchs, d_id, s_id, ox_id);
plogi->csp.rxsz = plogi->class3.rxsz = cpu_to_be16(pdu_size);
+ plogi->csp.bbcred = cpu_to_be16(bb_cr);
memcpy(&plogi->port_name, &port_name, sizeof(wwn_t));
memcpy(&plogi->node_name, &node_name, sizeof(wwn_t));
@@ -228,55 +220,19 @@ fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
}
u16
-fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
- u16 ox_id, wwn_t port_name, wwn_t node_name, u16 pdu_size,
- u8 set_npiv, u8 set_auth, u16 local_bb_credits)
-{
- u32 d_id = bfa_hton3b(FC_FABRIC_PORT);
- __be32 *vvl_info;
-
- memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s));
-
- flogi->els_cmd.els_code = FC_ELS_FLOGI;
- fc_els_req_build(fchs, d_id, s_id, ox_id);
-
- flogi->csp.rxsz = flogi->class3.rxsz = cpu_to_be16(pdu_size);
- flogi->port_name = port_name;
- flogi->node_name = node_name;
-
- /*
- * Set the NPIV Capability Bit ( word 1, bit 31) of Common
- * Service Parameters.
- */
- flogi->csp.ciro = set_npiv;
-
- /* set AUTH capability */
- flogi->csp.security = set_auth;
-
- flogi->csp.bbcred = cpu_to_be16(local_bb_credits);
-
- /* Set brcd token in VVL */
- vvl_info = (u32 *)&flogi->vvl[0];
-
- /* set the flag to indicate the presence of VVL */
- flogi->csp.npiv_supp = 1; /* @todo. field name is not correct */
- vvl_info[0] = cpu_to_be32(FLOGI_VVL_BRCD);
-
- return sizeof(struct fc_logi_s);
-}
-
-u16
fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
__be16 ox_id, wwn_t port_name, wwn_t node_name,
- u16 pdu_size, u16 local_bb_credits)
+ u16 pdu_size, u16 local_bb_credits, u8 bb_scn)
{
u32 d_id = 0;
+ u16 bbscn_rxsz = (bb_scn << 12) | pdu_size;
memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s));
fc_els_rsp_build(fchs, d_id, s_id, ox_id);
flogi->els_cmd.els_code = FC_ELS_ACC;
- flogi->csp.rxsz = flogi->class3.rxsz = cpu_to_be16(pdu_size);
+ flogi->class3.rxsz = cpu_to_be16(pdu_size);
+ flogi->csp.rxsz = cpu_to_be16(bbscn_rxsz); /* bb_scn/rxsz */
flogi->port_name = port_name;
flogi->node_name = node_name;
@@ -286,73 +242,21 @@ fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
}
u16
-fc_fdisc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
- u16 ox_id, wwn_t port_name, wwn_t node_name, u16 pdu_size)
-{
- u32 d_id = bfa_hton3b(FC_FABRIC_PORT);
-
- memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s));
-
- flogi->els_cmd.els_code = FC_ELS_FDISC;
- fc_els_req_build(fchs, d_id, s_id, ox_id);
-
- flogi->csp.rxsz = flogi->class3.rxsz = cpu_to_be16(pdu_size);
- flogi->port_name = port_name;
- flogi->node_name = node_name;
-
- return sizeof(struct fc_logi_s);
-}
-
-u16
fc_plogi_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
u16 ox_id, wwn_t port_name, wwn_t node_name,
- u16 pdu_size)
+ u16 pdu_size, u16 bb_cr)
{
return fc_plogi_x_build(fchs, pld, d_id, s_id, ox_id, port_name,
- node_name, pdu_size, FC_ELS_PLOGI);
+ node_name, pdu_size, bb_cr, FC_ELS_PLOGI);
}
u16
fc_plogi_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
u16 ox_id, wwn_t port_name, wwn_t node_name,
- u16 pdu_size)
+ u16 pdu_size, u16 bb_cr)
{
return fc_plogi_x_build(fchs, pld, d_id, s_id, ox_id, port_name,
- node_name, pdu_size, FC_ELS_ACC);
-}
-
-enum fc_parse_status
-fc_plogi_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
-{
- struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
- struct fc_logi_s *plogi;
- struct fc_ls_rjt_s *ls_rjt;
-
- switch (els_cmd->els_code) {
- case FC_ELS_LS_RJT:
- ls_rjt = (struct fc_ls_rjt_s *) (fchs + 1);
- if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
- return FC_PARSE_BUSY;
- else
- return FC_PARSE_FAILURE;
- case FC_ELS_ACC:
- plogi = (struct fc_logi_s *) (fchs + 1);
- if (len < sizeof(struct fc_logi_s))
- return FC_PARSE_FAILURE;
-
- if (!wwn_is_equal(plogi->port_name, port_name))
- return FC_PARSE_FAILURE;
-
- if (!plogi->class3.class_valid)
- return FC_PARSE_FAILURE;
-
- if (be16_to_cpu(plogi->class3.rxsz) < (FC_MIN_PDUSZ))
- return FC_PARSE_FAILURE;
-
- return FC_PARSE_OK;
- default:
- return FC_PARSE_FAILURE;
- }
+ node_name, pdu_size, bb_cr, FC_ELS_ACC);
}
enum fc_parse_status
@@ -427,21 +331,6 @@ fc_prli_rsp_parse(struct fc_prli_s *prli, int len)
return FC_PARSE_OK;
}
-enum fc_parse_status
-fc_prli_parse(struct fc_prli_s *prli)
-{
- if (prli->parampage.type != FC_TYPE_FCP)
- return FC_PARSE_FAILURE;
-
- if (!prli->parampage.imagepair)
- return FC_PARSE_FAILURE;
-
- if (!prli->parampage.servparams.initiator)
- return FC_PARSE_FAILURE;
-
- return FC_PARSE_OK;
-}
-
u16
fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id, u32 s_id,
u16 ox_id, wwn_t port_name)
@@ -512,84 +401,6 @@ fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, wwn_t port_name,
return FC_PARSE_OK;
}
-enum fc_parse_status
-fc_adisc_parse(struct fchs_s *fchs, void *pld, u32 host_dap, wwn_t node_name,
- wwn_t port_name)
-{
- struct fc_adisc_s *adisc = (struct fc_adisc_s *) pld;
-
- if (adisc->els_cmd.els_code != FC_ELS_ACC)
- return FC_PARSE_FAILURE;
-
- if ((adisc->nport_id == (host_dap))
- && wwn_is_equal(adisc->orig_port_name, port_name)
- && wwn_is_equal(adisc->orig_node_name, node_name))
- return FC_PARSE_OK;
-
- return FC_PARSE_FAILURE;
-}
-
-enum fc_parse_status
-fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, wwn_t port_name)
-{
- struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1);
-
- if (pdisc->class3.class_valid != 1)
- return FC_PARSE_FAILURE;
-
- if ((be16_to_cpu(pdisc->class3.rxsz) <
- (FC_MIN_PDUSZ - sizeof(struct fchs_s)))
- || (pdisc->class3.rxsz == 0))
- return FC_PARSE_FAILURE;
-
- if (!wwn_is_equal(pdisc->port_name, port_name))
- return FC_PARSE_FAILURE;
-
- if (!wwn_is_equal(pdisc->node_name, node_name))
- return FC_PARSE_FAILURE;
-
- return FC_PARSE_OK;
-}
-
-u16
-fc_abts_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id)
-{
- memcpy(fchs, &fc_bls_req_tmpl, sizeof(struct fchs_s));
- fchs->cat_info = FC_CAT_ABTS;
- fchs->d_id = (d_id);
- fchs->s_id = (s_id);
- fchs->ox_id = cpu_to_be16(ox_id);
-
- return sizeof(struct fchs_s);
-}
-
-enum fc_parse_status
-fc_abts_rsp_parse(struct fchs_s *fchs, int len)
-{
- if ((fchs->cat_info == FC_CAT_BA_ACC)
- || (fchs->cat_info == FC_CAT_BA_RJT))
- return FC_PARSE_OK;
-
- return FC_PARSE_FAILURE;
-}
-
-u16
-fc_rrq_build(struct fchs_s *fchs, struct fc_rrq_s *rrq, u32 d_id, u32 s_id,
- u16 ox_id, u16 rrq_oxid)
-{
- fc_els_req_build(fchs, d_id, s_id, ox_id);
-
- /*
- * build rrq payload
- */
- memcpy(rrq, &rrq_tmpl, sizeof(struct fc_rrq_s));
- rrq->s_id = (s_id);
- rrq->ox_id = cpu_to_be16(rrq_oxid);
- rrq->rx_id = FC_RXID_ANY;
-
- return sizeof(struct fc_rrq_s);
-}
-
u16
fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
__be16 ox_id)
@@ -665,30 +476,6 @@ fc_logout_params_pages(struct fchs_s *fc_frame, u8 els_code)
}
u16
-fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc,
- u32 d_id, u32 s_id, __be16 ox_id, int num_pages)
-{
- int page;
-
- fc_els_rsp_build(fchs, d_id, s_id, ox_id);
-
- memset(tprlo_acc, 0, (num_pages * 16) + 4);
- tprlo_acc->command = FC_ELS_ACC;
-
- tprlo_acc->page_len = 0x10;
- tprlo_acc->payload_len = cpu_to_be16((num_pages * 16) + 4);
-
- for (page = 0; page < num_pages; page++) {
- tprlo_acc->tprlo_acc_params[page].opa_valid = 0;
- tprlo_acc->tprlo_acc_params[page].rpa_valid = 0;
- tprlo_acc->tprlo_acc_params[page].fc4type_csp = FC_TYPE_FCP;
- tprlo_acc->tprlo_acc_params[page].orig_process_assc = 0;
- tprlo_acc->tprlo_acc_params[page].resp_process_assc = 0;
- }
- return be16_to_cpu(tprlo_acc->payload_len);
-}
-
-u16
fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc, u32 d_id,
u32 s_id, __be16 ox_id, int num_pages)
{
@@ -713,20 +500,6 @@ fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc, u32 d_id,
}
u16
-fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, u32 d_id,
- u32 s_id, u16 ox_id, u32 data_format)
-{
- fc_els_req_build(fchs, d_id, s_id, ox_id);
-
- memset(rnid, 0, sizeof(struct fc_rnid_cmd_s));
-
- rnid->els_cmd.els_code = FC_ELS_RNID;
- rnid->node_id_data_format = data_format;
-
- return sizeof(struct fc_rnid_cmd_s);
-}
-
-u16
fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc, u32 d_id,
u32 s_id, __be16 ox_id, u32 data_format,
struct fc_rnid_common_id_data_s *common_id_data,
@@ -755,18 +528,6 @@ fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc, u32 d_id,
}
u16
-fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, u32 d_id,
- u32 s_id, u16 ox_id)
-{
- fc_els_req_build(fchs, d_id, s_id, ox_id);
-
- memset(rpsc, 0, sizeof(struct fc_rpsc_cmd_s));
-
- rpsc->els_cmd.els_code = FC_ELS_RPSC;
- return sizeof(struct fc_rpsc_cmd_s);
-}
-
-u16
fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rpsc2, u32 d_id,
u32 s_id, u32 *pid_list, u16 npids)
{
@@ -807,202 +568,6 @@ fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc,
return sizeof(struct fc_rpsc_acc_s);
}
-u16
-fc_logo_rsp_parse(struct fchs_s *fchs, int len)
-{
- struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
-
- len = len;
- if (els_cmd->els_code != FC_ELS_ACC)
- return FC_PARSE_FAILURE;
-
- return FC_PARSE_OK;
-}
-
-u16
-fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id,
- wwn_t port_name, wwn_t node_name, u16 pdu_size)
-{
- struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1);
-
- memcpy(pdisc, &plogi_tmpl, sizeof(struct fc_logi_s));
-
- pdisc->els_cmd.els_code = FC_ELS_PDISC;
- fc_els_req_build(fchs, d_id, s_id, ox_id);
-
- pdisc->csp.rxsz = pdisc->class3.rxsz = cpu_to_be16(pdu_size);
- pdisc->port_name = port_name;
- pdisc->node_name = node_name;
-
- return sizeof(struct fc_logi_s);
-}
-
-u16
-fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
-{
- struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1);
-
- if (len < sizeof(struct fc_logi_s))
- return FC_PARSE_LEN_INVAL;
-
- if (pdisc->els_cmd.els_code != FC_ELS_ACC)
- return FC_PARSE_ACC_INVAL;
-
- if (!wwn_is_equal(pdisc->port_name, port_name))
- return FC_PARSE_PWWN_NOT_EQUAL;
-
- if (!pdisc->class3.class_valid)
- return FC_PARSE_NWWN_NOT_EQUAL;
-
- if (be16_to_cpu(pdisc->class3.rxsz) < (FC_MIN_PDUSZ))
- return FC_PARSE_RXSZ_INVAL;
-
- return FC_PARSE_OK;
-}
-
-u16
-fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id,
- int num_pages)
-{
- struct fc_prlo_s *prlo = (struct fc_prlo_s *) (fchs + 1);
- int page;
-
- fc_els_req_build(fchs, d_id, s_id, ox_id);
- memset(prlo, 0, (num_pages * 16) + 4);
- prlo->command = FC_ELS_PRLO;
- prlo->page_len = 0x10;
- prlo->payload_len = cpu_to_be16((num_pages * 16) + 4);
-
- for (page = 0; page < num_pages; page++) {
- prlo->prlo_params[page].type = FC_TYPE_FCP;
- prlo->prlo_params[page].opa_valid = 0;
- prlo->prlo_params[page].rpa_valid = 0;
- prlo->prlo_params[page].orig_process_assc = 0;
- prlo->prlo_params[page].resp_process_assc = 0;
- }
-
- return be16_to_cpu(prlo->payload_len);
-}
-
-u16
-fc_prlo_rsp_parse(struct fchs_s *fchs, int len)
-{
- struct fc_prlo_acc_s *prlo = (struct fc_prlo_acc_s *) (fchs + 1);
- int num_pages = 0;
- int page = 0;
-
- len = len;
-
- if (prlo->command != FC_ELS_ACC)
- return FC_PARSE_FAILURE;
-
- num_pages = ((be16_to_cpu(prlo->payload_len)) - 4) / 16;
-
- for (page = 0; page < num_pages; page++) {
- if (prlo->prlo_acc_params[page].type != FC_TYPE_FCP)
- return FC_PARSE_FAILURE;
-
- if (prlo->prlo_acc_params[page].opa_valid != 0)
- return FC_PARSE_FAILURE;
-
- if (prlo->prlo_acc_params[page].rpa_valid != 0)
- return FC_PARSE_FAILURE;
-
- if (prlo->prlo_acc_params[page].orig_process_assc != 0)
- return FC_PARSE_FAILURE;
-
- if (prlo->prlo_acc_params[page].resp_process_assc != 0)
- return FC_PARSE_FAILURE;
- }
- return FC_PARSE_OK;
-
-}
-
-u16
-fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id,
- int num_pages, enum fc_tprlo_type tprlo_type, u32 tpr_id)
-{
- struct fc_tprlo_s *tprlo = (struct fc_tprlo_s *) (fchs + 1);
- int page;
-
- fc_els_req_build(fchs, d_id, s_id, ox_id);
- memset(tprlo, 0, (num_pages * 16) + 4);
- tprlo->command = FC_ELS_TPRLO;
- tprlo->page_len = 0x10;
- tprlo->payload_len = cpu_to_be16((num_pages * 16) + 4);
-
- for (page = 0; page < num_pages; page++) {
- tprlo->tprlo_params[page].type = FC_TYPE_FCP;
- tprlo->tprlo_params[page].opa_valid = 0;
- tprlo->tprlo_params[page].rpa_valid = 0;
- tprlo->tprlo_params[page].orig_process_assc = 0;
- tprlo->tprlo_params[page].resp_process_assc = 0;
- if (tprlo_type == FC_GLOBAL_LOGO) {
- tprlo->tprlo_params[page].global_process_logout = 1;
- } else if (tprlo_type == FC_TPR_LOGO) {
- tprlo->tprlo_params[page].tpo_nport_valid = 1;
- tprlo->tprlo_params[page].tpo_nport_id = (tpr_id);
- }
- }
-
- return be16_to_cpu(tprlo->payload_len);
-}
-
-u16
-fc_tprlo_rsp_parse(struct fchs_s *fchs, int len)
-{
- struct fc_tprlo_acc_s *tprlo = (struct fc_tprlo_acc_s *) (fchs + 1);
- int num_pages = 0;
- int page = 0;
-
- len = len;
-
- if (tprlo->command != FC_ELS_ACC)
- return FC_PARSE_ACC_INVAL;
-
- num_pages = (be16_to_cpu(tprlo->payload_len) - 4) / 16;
-
- for (page = 0; page < num_pages; page++) {
- if (tprlo->tprlo_acc_params[page].type != FC_TYPE_FCP)
- return FC_PARSE_NOT_FCP;
- if (tprlo->tprlo_acc_params[page].opa_valid != 0)
- return FC_PARSE_OPAFLAG_INVAL;
- if (tprlo->tprlo_acc_params[page].rpa_valid != 0)
- return FC_PARSE_RPAFLAG_INVAL;
- if (tprlo->tprlo_acc_params[page].orig_process_assc != 0)
- return FC_PARSE_OPA_INVAL;
- if (tprlo->tprlo_acc_params[page].resp_process_assc != 0)
- return FC_PARSE_RPA_INVAL;
- }
- return FC_PARSE_OK;
-}
-
-enum fc_parse_status
-fc_rrq_rsp_parse(struct fchs_s *fchs, int len)
-{
- struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
-
- len = len;
- if (els_cmd->els_code != FC_ELS_ACC)
- return FC_PARSE_FAILURE;
-
- return FC_PARSE_OK;
-}
-
-u16
-fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id, __be16 ox_id,
- u32 reason_code, u32 reason_expl)
-{
- struct fc_ba_rjt_s *ba_rjt = (struct fc_ba_rjt_s *) (fchs + 1);
-
- fc_bls_rsp_build(fchs, d_id, s_id, ox_id);
-
- fchs->cat_info = FC_CAT_BA_RJT;
- ba_rjt->reason_code = reason_code;
- ba_rjt->reason_expl = reason_expl;
- return sizeof(struct fc_ba_rjt_s);
-}
-
static void
fc_gs_cthdr_build(struct ct_hdr_s *cthdr, u32 s_id, u16 cmd_code)
{
@@ -1067,32 +632,18 @@ fc_gpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
}
u16
-fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
- u32 port_id)
+fc_gs_rjt_build(struct fchs_s *fchs, struct ct_hdr_s *cthdr,
+ u32 d_id, u32 s_id, u16 ox_id, u8 reason_code,
+ u8 reason_code_expl)
{
- struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld;
- fcgs_gnnid_req_t *gnnid = (fcgs_gnnid_req_t *) (cthdr + 1);
- u32 d_id = bfa_hton3b(FC_NAME_SERVER);
-
- fc_gs_fchdr_build(fchs, d_id, s_id, ox_id);
- fc_gs_cthdr_build(cthdr, s_id, GS_GNN_ID);
-
- memset(gnnid, 0, sizeof(fcgs_gnnid_req_t));
- gnnid->dap = port_id;
- return sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s);
-}
+ fc_gsresp_fchdr_build(fchs, d_id, s_id, ox_id);
-u16
-fc_ct_rsp_parse(struct ct_hdr_s *cthdr)
-{
- if (be16_to_cpu(cthdr->cmd_rsp_code) != CT_RSP_ACCEPT) {
- if (cthdr->reason_code == CT_RSN_LOGICAL_BUSY)
- return FC_PARSE_BUSY;
- else
- return FC_PARSE_FAILURE;
- }
+ cthdr->cmd_rsp_code = cpu_to_be16(CT_RSP_REJECT);
+ cthdr->rev_id = CT_GS3_REVISION;
- return FC_PARSE_OK;
+ cthdr->reason_code = reason_code;
+ cthdr->exp_code = reason_code_expl;
+ return sizeof(struct ct_hdr_s);
}
u16
@@ -1113,26 +664,6 @@ fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr,
}
u16
-fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn,
- u32 s_id, u16 ox_id)
-{
- u32 d_id = bfa_hton3b(FC_FABRIC_CONTROLLER);
- u16 payldlen;
-
- fc_els_req_build(fchs, d_id, s_id, ox_id);
- rscn->command = FC_ELS_RSCN;
- rscn->pagelen = sizeof(rscn->event[0]);
-
- payldlen = sizeof(u32) + rscn->pagelen;
- rscn->payldlen = cpu_to_be16(payldlen);
-
- rscn->event[0].format = FC_RSCN_FORMAT_PORTID;
- rscn->event[0].portid = s_id;
-
- return sizeof(struct fc_rscn_pl_s);
-}
-
-u16
fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
enum bfa_lport_role roles)
{
@@ -1157,26 +688,6 @@ fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
}
u16
-fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
- u8 *fc4_bitmap, u32 bitmap_size)
-{
- struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld;
- struct fcgs_rftid_req_s *rftid = (struct fcgs_rftid_req_s *)(cthdr + 1);
- u32 d_id = bfa_hton3b(FC_NAME_SERVER);
-
- fc_gs_fchdr_build(fchs, d_id, s_id, ox_id);
- fc_gs_cthdr_build(cthdr, s_id, GS_RFT_ID);
-
- memset(rftid, 0, sizeof(struct fcgs_rftid_req_s));
-
- rftid->dap = s_id;
- memcpy((void *)rftid->fc4_type, (void *)fc4_bitmap,
- (bitmap_size < 32 ? bitmap_size : 32));
-
- return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s);
-}
-
-u16
fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
u8 fc4_type, u8 fc4_ftrs)
{
@@ -1212,48 +723,51 @@ fc_rspnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
memset(rspnid, 0, sizeof(struct fcgs_rspnid_req_s));
rspnid->dap = s_id;
- rspnid->spn_len = (u8) strlen((char *)name);
- strncpy((char *)rspnid->spn, (char *)name, rspnid->spn_len);
+ strscpy(rspnid->spn, name, sizeof(rspnid->spn));
+ rspnid->spn_len = (u8) strlen(rspnid->spn);
return sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s);
}
u16
-fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id, u8 fc4_type)
+fc_rsnn_nn_build(struct fchs_s *fchs, void *pyld, u32 s_id,
+ wwn_t node_name, u8 *name)
{
-
struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld;
- struct fcgs_gidft_req_s *gidft = (struct fcgs_gidft_req_s *)(cthdr + 1);
- u32 d_id = bfa_hton3b(FC_NAME_SERVER);
+ struct fcgs_rsnn_nn_req_s *rsnn_nn =
+ (struct fcgs_rsnn_nn_req_s *) (cthdr + 1);
+ u32 d_id = bfa_hton3b(FC_NAME_SERVER);
fc_gs_fchdr_build(fchs, d_id, s_id, 0);
+ fc_gs_cthdr_build(cthdr, s_id, GS_RSNN_NN);
- fc_gs_cthdr_build(cthdr, s_id, GS_GID_FT);
+ memset(rsnn_nn, 0, sizeof(struct fcgs_rsnn_nn_req_s));
- memset(gidft, 0, sizeof(struct fcgs_gidft_req_s));
- gidft->fc4_type = fc4_type;
- gidft->domain_id = 0;
- gidft->area_id = 0;
+ rsnn_nn->node_name = node_name;
+ strscpy(rsnn_nn->snn, name, sizeof(rsnn_nn->snn));
+ rsnn_nn->snn_len = (u8) strlen(rsnn_nn->snn);
- return sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s);
+ return sizeof(struct fcgs_rsnn_nn_req_s) + sizeof(struct ct_hdr_s);
}
u16
-fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
- wwn_t port_name)
+fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id, u8 fc4_type)
{
+
struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld;
- struct fcgs_rpnid_req_s *rpnid = (struct fcgs_rpnid_req_s *)(cthdr + 1);
+ struct fcgs_gidft_req_s *gidft = (struct fcgs_gidft_req_s *)(cthdr + 1);
u32 d_id = bfa_hton3b(FC_NAME_SERVER);
fc_gs_fchdr_build(fchs, d_id, s_id, 0);
- fc_gs_cthdr_build(cthdr, s_id, GS_RPN_ID);
- memset(rpnid, 0, sizeof(struct fcgs_rpnid_req_s));
- rpnid->port_id = port_id;
- rpnid->port_name = port_name;
+ fc_gs_cthdr_build(cthdr, s_id, GS_GID_FT);
+
+ memset(gidft, 0, sizeof(struct fcgs_gidft_req_s));
+ gidft->fc4_type = fc4_type;
+ gidft->domain_id = 0;
+ gidft->area_id = 0;
- return sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s);
+ return sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1274,59 +788,6 @@ fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
return sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s);
}
-u16
-fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
- u32 cos)
-{
- struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld;
- struct fcgs_rcsid_req_s *rcsid =
- (struct fcgs_rcsid_req_s *) (cthdr + 1);
- u32 d_id = bfa_hton3b(FC_NAME_SERVER);
-
- fc_gs_fchdr_build(fchs, d_id, s_id, 0);
- fc_gs_cthdr_build(cthdr, s_id, GS_RCS_ID);
-
- memset(rcsid, 0, sizeof(struct fcgs_rcsid_req_s));
- rcsid->port_id = port_id;
- rcsid->cos = cos;
-
- return sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s);
-}
-
-u16
-fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
- u8 port_type)
-{
- struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld;
- struct fcgs_rptid_req_s *rptid = (struct fcgs_rptid_req_s *)(cthdr + 1);
- u32 d_id = bfa_hton3b(FC_NAME_SERVER);
-
- fc_gs_fchdr_build(fchs, d_id, s_id, 0);
- fc_gs_cthdr_build(cthdr, s_id, GS_RPT_ID);
-
- memset(rptid, 0, sizeof(struct fcgs_rptid_req_s));
- rptid->port_id = port_id;
- rptid->port_type = port_type;
-
- return sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s);
-}
-
-u16
-fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id)
-{
- struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld;
- struct fcgs_ganxt_req_s *ganxt = (struct fcgs_ganxt_req_s *)(cthdr + 1);
- u32 d_id = bfa_hton3b(FC_NAME_SERVER);
-
- fc_gs_fchdr_build(fchs, d_id, s_id, 0);
- fc_gs_cthdr_build(cthdr, s_id, GS_GA_NXT);
-
- memset(ganxt, 0, sizeof(struct fcgs_ganxt_req_s));
- ganxt->port_id = port_id;
-
- return sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s);
-}
-
/*
* Builds fc hdr and ct hdr for FDMI requests.
*/
diff --git a/drivers/scsi/bfa/bfa_fcbuild.h b/drivers/scsi/bfa/bfa_fcbuild.h
index ece51ec7620b..51da37b2ae6b 100644
--- a/drivers/scsi/bfa/bfa_fcbuild.h
+++ b/drivers/scsi/bfa/bfa_fcbuild.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
* fcbuild.h - FC link service frame building and parsing routines
@@ -66,6 +59,9 @@ fc_rpsc_operspeed_to_bfa_speed(enum fc_rpsc_op_speed speed)
case RPSC_OP_SPEED_8G:
return BFA_PORT_SPEED_8GBPS;
+ case RPSC_OP_SPEED_16G:
+ return BFA_PORT_SPEED_16GBPS;
+
case RPSC_OP_SPEED_10G:
return BFA_PORT_SPEED_10GBPS;
@@ -94,6 +90,9 @@ fc_bfa_speed_to_rpsc_operspeed(enum bfa_port_speed op_speed)
case BFA_PORT_SPEED_8GBPS:
return RPSC_OP_SPEED_8G;
+ case BFA_PORT_SPEED_16GBPS:
+ return RPSC_OP_SPEED_16G;
+
case BFA_PORT_SPEED_10GBPS:
return RPSC_OP_SPEED_10G;
@@ -128,46 +127,26 @@ struct fc_templates_s {
void fcbuild_init(void);
-u16 fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi,
- u32 s_id, u16 ox_id, wwn_t port_name, wwn_t node_name,
- u16 pdu_size, u8 set_npiv, u8 set_auth,
- u16 local_bb_credits);
-
-u16 fc_fdisc_build(struct fchs_s *buf, struct fc_logi_s *flogi, u32 s_id,
- u16 ox_id, wwn_t port_name, wwn_t node_name,
- u16 pdu_size);
-
u16 fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi,
u32 s_id, __be16 ox_id,
wwn_t port_name, wwn_t node_name,
u16 pdu_size,
- u16 local_bb_credits);
+ u16 local_bb_credits, u8 bb_scn);
u16 fc_plogi_build(struct fchs_s *fchs, void *pld, u32 d_id,
u32 s_id, u16 ox_id, wwn_t port_name,
- wwn_t node_name, u16 pdu_size);
+ wwn_t node_name, u16 pdu_size, u16 bb_cr);
enum fc_parse_status fc_plogi_parse(struct fchs_s *fchs);
-u16 fc_abts_build(struct fchs_s *buf, u32 d_id, u32 s_id,
- u16 ox_id);
-
-enum fc_parse_status fc_abts_rsp_parse(struct fchs_s *buf, int len);
-
-u16 fc_rrq_build(struct fchs_s *buf, struct fc_rrq_s *rrq, u32 d_id,
- u32 s_id, u16 ox_id, u16 rrq_oxid);
-enum fc_parse_status fc_rrq_rsp_parse(struct fchs_s *buf, int len);
-
u16 fc_rspnid_build(struct fchs_s *fchs, void *pld, u32 s_id,
u16 ox_id, u8 *name);
+u16 fc_rsnn_nn_build(struct fchs_s *fchs, void *pld, u32 s_id,
+ wwn_t node_name, u8 *name);
u16 fc_rftid_build(struct fchs_s *fchs, void *pld, u32 s_id,
u16 ox_id, enum bfa_lport_role role);
-u16 fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id,
- u16 ox_id, u8 *fc4_bitmap,
- u32 bitmap_size);
-
u16 fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id,
u16 ox_id, u8 fc4_type, u8 fc4_ftrs);
@@ -177,21 +156,22 @@ u16 fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id,
u16 fc_gpnid_build(struct fchs_s *fchs, void *pld, u32 s_id,
u16 ox_id, u32 port_id);
+u16 fc_gs_rjt_build(struct fchs_s *fchs, struct ct_hdr_s *cthdr,
+ u32 d_id, u32 s_id, u16 ox_id,
+ u8 reason_code, u8 reason_code_expl);
+
u16 fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr,
u8 set_br_reg, u32 s_id, u16 ox_id);
u16 fc_plogi_acc_build(struct fchs_s *fchs, void *pld, u32 d_id,
u32 s_id, u16 ox_id,
wwn_t port_name, wwn_t node_name,
- u16 pdu_size);
+ u16 pdu_size, u16 bb_cr);
u16 fc_adisc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc,
u32 d_id, u32 s_id, __be16 ox_id, wwn_t port_name,
wwn_t node_name);
-enum fc_parse_status fc_adisc_parse(struct fchs_s *fchs, void *pld,
- u32 host_dap, wwn_t node_name, wwn_t port_name);
-
enum fc_parse_status fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len,
wwn_t port_name, wwn_t node_name);
@@ -212,10 +192,6 @@ u16 fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id,
u32 s_id, __be16 ox_id,
enum bfa_lport_role role);
-u16 fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid,
- u32 d_id, u32 s_id, u16 ox_id,
- u32 data_format);
-
u16 fc_rnid_acc_build(struct fchs_s *fchs,
struct fc_rnid_acc_s *rnid_acc, u32 d_id, u32 s_id,
__be16 ox_id, u32 data_format,
@@ -224,29 +200,15 @@ u16 fc_rnid_acc_build(struct fchs_s *fchs,
u16 fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rps2c,
u32 d_id, u32 s_id, u32 *pid_list, u16 npids);
-u16 fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc,
- u32 d_id, u32 s_id, u16 ox_id);
u16 fc_rpsc_acc_build(struct fchs_s *fchs,
struct fc_rpsc_acc_s *rpsc_acc, u32 d_id, u32 s_id,
__be16 ox_id, struct fc_rpsc_speed_info_s *oper_speed);
u16 fc_gid_ft_build(struct fchs_s *fchs, void *pld, u32 s_id,
u8 fc4_type);
-u16 fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id,
- u32 port_id, wwn_t port_name);
-
u16 fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id,
u32 port_id, wwn_t node_name);
-u16 fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id,
- u32 port_id, u32 cos);
-
-u16 fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id,
- u32 port_id, u8 port_type);
-
-u16 fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id,
- u32 port_id);
-
u16 fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id,
u32 s_id, u16 ox_id, wwn_t port_name);
@@ -263,54 +225,16 @@ void fc_get_fc4type_bitmask(u8 fc4_type, u8 *bit_mask);
void fc_els_req_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
__be16 ox_id);
-enum fc_parse_status fc_els_rsp_parse(struct fchs_s *fchs, int len);
-
-enum fc_parse_status fc_plogi_rsp_parse(struct fchs_s *fchs, int len,
- wwn_t port_name);
-
-enum fc_parse_status fc_prli_parse(struct fc_prli_s *prli);
-
-enum fc_parse_status fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name,
- wwn_t port_name);
-
u16 fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, u32 d_id,
u32 s_id, __be16 ox_id, u16 rx_id);
int fc_logout_params_pages(struct fchs_s *fc_frame, u8 els_code);
-u16 fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc,
- u32 d_id, u32 s_id, __be16 ox_id, int num_pages);
-
u16 fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc,
u32 d_id, u32 s_id, __be16 ox_id, int num_pages);
-u16 fc_logo_rsp_parse(struct fchs_s *fchs, int len);
-
-u16 fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
- u16 ox_id, wwn_t port_name, wwn_t node_name,
- u16 pdu_size);
-
-u16 fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name);
-
-u16 fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
- u16 ox_id, int num_pages);
-
-u16 fc_prlo_rsp_parse(struct fchs_s *fchs, int len);
-
u16 fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
u16 ox_id, int num_pages, enum fc_tprlo_type tprlo_type,
u32 tpr_id);
-u16 fc_tprlo_rsp_parse(struct fchs_s *fchs, int len);
-
-u16 fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
- __be16 ox_id, u32 reason_code, u32 reason_expl);
-
-u16 fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
- u32 port_id);
-
-u16 fc_ct_rsp_parse(struct ct_hdr_s *cthdr);
-
-u16 fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, u32 s_id,
- u16 ox_id);
#endif
diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c
index c0353cdca929..2518e36d70d3 100644
--- a/drivers/scsi/bfa/bfa_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcpim.c
@@ -1,25 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
#include "bfa_modules.h"
BFA_TRC_FILE(HAL, FCPIM);
-BFA_MODULE(fcpim);
/*
* BFA ITNIM Related definitions
@@ -67,19 +59,9 @@ static void bfa_itnim_update_del_itn_stats(struct bfa_itnim_s *itnim);
} \
} while (0)
-/*
- * itnim state machine event
- */
-enum bfa_itnim_event {
- BFA_ITNIM_SM_CREATE = 1, /* itnim is created */
- BFA_ITNIM_SM_ONLINE = 2, /* itnim is online */
- BFA_ITNIM_SM_OFFLINE = 3, /* itnim is offline */
- BFA_ITNIM_SM_FWRSP = 4, /* firmware response */
- BFA_ITNIM_SM_DELETE = 5, /* deleting an existing itnim */
- BFA_ITNIM_SM_CLEANUP = 6, /* IO cleanup completion */
- BFA_ITNIM_SM_SLER = 7, /* second level error recovery */
- BFA_ITNIM_SM_HWFAIL = 8, /* IOC h/w failure event */
- BFA_ITNIM_SM_QRESUME = 9, /* queue space available */
+enum bfa_ioim_lm_ua_status {
+ BFA_IOIM_LM_UA_RESET = 0,
+ BFA_IOIM_LM_UA_SET = 1,
};
/*
@@ -101,30 +83,6 @@ enum bfa_itnim_event {
(__fcpim)->profile_start(__ioim); \
} while (0)
-/*
- * IO state machine events
- */
-enum bfa_ioim_event {
- BFA_IOIM_SM_START = 1, /* io start request from host */
- BFA_IOIM_SM_COMP_GOOD = 2, /* io good comp, resource free */
- BFA_IOIM_SM_COMP = 3, /* io comp, resource is free */
- BFA_IOIM_SM_COMP_UTAG = 4, /* io comp, resource is free */
- BFA_IOIM_SM_DONE = 5, /* io comp, resource not free */
- BFA_IOIM_SM_FREE = 6, /* io resource is freed */
- BFA_IOIM_SM_ABORT = 7, /* abort request from scsi stack */
- BFA_IOIM_SM_ABORT_COMP = 8, /* abort from f/w */
- BFA_IOIM_SM_ABORT_DONE = 9, /* abort completion from f/w */
- BFA_IOIM_SM_QRESUME = 10, /* CQ space available to queue IO */
- BFA_IOIM_SM_SGALLOCED = 11, /* SG page allocation successful */
- BFA_IOIM_SM_SQRETRY = 12, /* sequence recovery retry */
- BFA_IOIM_SM_HCB = 13, /* bfa callback complete */
- BFA_IOIM_SM_CLEANUP = 14, /* IO cleanup from itnim */
- BFA_IOIM_SM_TMSTART = 15, /* IO cleanup from tskim */
- BFA_IOIM_SM_TMDONE = 16, /* IO cleanup from tskim */
- BFA_IOIM_SM_HWFAIL = 17, /* IOC h/w failure event */
- BFA_IOIM_SM_IOTOV = 18, /* ITN offline TOV */
-};
-
/*
* BFA TSKIM related definitions
@@ -144,17 +102,6 @@ enum bfa_ioim_event {
} while (0)
-enum bfa_tskim_event {
- BFA_TSKIM_SM_START = 1, /* TM command start */
- BFA_TSKIM_SM_DONE = 2, /* TM completion */
- BFA_TSKIM_SM_QRESUME = 3, /* resume after qfull */
- BFA_TSKIM_SM_HWFAIL = 5, /* IOC h/w failure event */
- BFA_TSKIM_SM_HCB = 6, /* BFA callback completion */
- BFA_TSKIM_SM_IOS_DONE = 7, /* IO and sub TM completions */
- BFA_TSKIM_SM_CLEANUP = 8, /* TM cleanup on ITN offline */
- BFA_TSKIM_SM_CLEANUP_DONE = 9, /* TM abort completion */
-};
-
/*
* forward declaration for BFA ITNIM functions
*/
@@ -287,24 +234,16 @@ static void bfa_tskim_sm_hcb(struct bfa_tskim_s *tskim,
* Compute and return memory needed by FCP(im) module.
*/
static void
-bfa_fcpim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len)
+bfa_fcpim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len)
{
- bfa_itnim_meminfo(cfg, km_len, dm_len);
+ bfa_itnim_meminfo(cfg, km_len);
/*
* IO memory
*/
- if (cfg->fwcfg.num_ioim_reqs < BFA_IOIM_MIN)
- cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN;
- else if (cfg->fwcfg.num_ioim_reqs > BFA_IOIM_MAX)
- cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MAX;
-
*km_len += cfg->fwcfg.num_ioim_reqs *
(sizeof(struct bfa_ioim_s) + sizeof(struct bfa_ioim_sp_s));
- *dm_len += cfg->fwcfg.num_ioim_reqs * BFI_IOIM_SNSLEN;
-
/*
* task management command memory
*/
@@ -315,52 +254,41 @@ bfa_fcpim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
static void
-bfa_fcpim_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
+bfa_fcpim_attach(struct bfa_fcp_mod_s *fcp, void *bfad,
+ struct bfa_iocfc_cfg_s *cfg, struct bfa_pcidev_s *pcidev)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = &fcp->fcpim;
+ struct bfa_s *bfa = fcp->bfa;
bfa_trc(bfa, cfg->drvcfg.path_tov);
bfa_trc(bfa, cfg->fwcfg.num_rports);
bfa_trc(bfa, cfg->fwcfg.num_ioim_reqs);
bfa_trc(bfa, cfg->fwcfg.num_tskim_reqs);
+ fcpim->fcp = fcp;
fcpim->bfa = bfa;
fcpim->num_itnims = cfg->fwcfg.num_rports;
- fcpim->num_ioim_reqs = cfg->fwcfg.num_ioim_reqs;
fcpim->num_tskim_reqs = cfg->fwcfg.num_tskim_reqs;
fcpim->path_tov = cfg->drvcfg.path_tov;
fcpim->delay_comp = cfg->drvcfg.delay_comp;
fcpim->profile_comp = NULL;
fcpim->profile_start = NULL;
- bfa_itnim_attach(fcpim, meminfo);
- bfa_tskim_attach(fcpim, meminfo);
- bfa_ioim_attach(fcpim, meminfo);
-}
-
-static void
-bfa_fcpim_detach(struct bfa_s *bfa)
-{
+ bfa_itnim_attach(fcpim);
+ bfa_tskim_attach(fcpim);
+ bfa_ioim_attach(fcpim);
}
-static void
-bfa_fcpim_start(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_fcpim_stop(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_fcpim_iocdisable(struct bfa_s *bfa)
+void
+bfa_fcpim_iocdisable(struct bfa_fcp_mod_s *fcp)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = &fcp->fcpim;
struct bfa_itnim_s *itnim;
struct list_head *qe, *qen;
+ /* Enqueue unused ioim resources to free_q */
+ list_splice_tail_init(&fcpim->tskim_unused_q, &fcpim->tskim_free_q);
+
list_for_each_safe(qe, qen, &fcpim->itnim_q) {
itnim = (struct bfa_itnim_s *) qe;
bfa_itnim_iocdisable(itnim);
@@ -370,7 +298,7 @@ bfa_fcpim_iocdisable(struct bfa_s *bfa)
void
bfa_fcpim_path_tov_set(struct bfa_s *bfa, u16 path_tov)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
fcpim->path_tov = path_tov * 1000;
if (fcpim->path_tov > BFA_FCPIM_PATHTOV_MAX)
@@ -380,15 +308,140 @@ bfa_fcpim_path_tov_set(struct bfa_s *bfa, u16 path_tov)
u16
bfa_fcpim_path_tov_get(struct bfa_s *bfa)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
return fcpim->path_tov / 1000;
}
+#define bfa_fcpim_add_iostats(__l, __r, __stats) \
+ (__l->__stats += __r->__stats)
+
+void
+bfa_fcpim_add_stats(struct bfa_itnim_iostats_s *lstats,
+ struct bfa_itnim_iostats_s *rstats)
+{
+ bfa_fcpim_add_iostats(lstats, rstats, total_ios);
+ bfa_fcpim_add_iostats(lstats, rstats, qresumes);
+ bfa_fcpim_add_iostats(lstats, rstats, no_iotags);
+ bfa_fcpim_add_iostats(lstats, rstats, io_aborts);
+ bfa_fcpim_add_iostats(lstats, rstats, no_tskims);
+ bfa_fcpim_add_iostats(lstats, rstats, iocomp_ok);
+ bfa_fcpim_add_iostats(lstats, rstats, iocomp_underrun);
+ bfa_fcpim_add_iostats(lstats, rstats, iocomp_overrun);
+ bfa_fcpim_add_iostats(lstats, rstats, iocomp_aborted);
+ bfa_fcpim_add_iostats(lstats, rstats, iocomp_timedout);
+ bfa_fcpim_add_iostats(lstats, rstats, iocom_nexus_abort);
+ bfa_fcpim_add_iostats(lstats, rstats, iocom_proto_err);
+ bfa_fcpim_add_iostats(lstats, rstats, iocom_dif_err);
+ bfa_fcpim_add_iostats(lstats, rstats, iocom_sqer_needed);
+ bfa_fcpim_add_iostats(lstats, rstats, iocom_res_free);
+ bfa_fcpim_add_iostats(lstats, rstats, iocom_hostabrts);
+ bfa_fcpim_add_iostats(lstats, rstats, iocom_utags);
+ bfa_fcpim_add_iostats(lstats, rstats, io_cleanups);
+ bfa_fcpim_add_iostats(lstats, rstats, io_tmaborts);
+ bfa_fcpim_add_iostats(lstats, rstats, onlines);
+ bfa_fcpim_add_iostats(lstats, rstats, offlines);
+ bfa_fcpim_add_iostats(lstats, rstats, creates);
+ bfa_fcpim_add_iostats(lstats, rstats, deletes);
+ bfa_fcpim_add_iostats(lstats, rstats, create_comps);
+ bfa_fcpim_add_iostats(lstats, rstats, delete_comps);
+ bfa_fcpim_add_iostats(lstats, rstats, sler_events);
+ bfa_fcpim_add_iostats(lstats, rstats, fw_create);
+ bfa_fcpim_add_iostats(lstats, rstats, fw_delete);
+ bfa_fcpim_add_iostats(lstats, rstats, ioc_disabled);
+ bfa_fcpim_add_iostats(lstats, rstats, cleanup_comps);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_cmnds);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_fw_rsps);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_success);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_failures);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_io_comps);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_qresumes);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_iocdowns);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_cleanups);
+ bfa_fcpim_add_iostats(lstats, rstats, tm_cleanup_comps);
+ bfa_fcpim_add_iostats(lstats, rstats, io_comps);
+ bfa_fcpim_add_iostats(lstats, rstats, input_reqs);
+ bfa_fcpim_add_iostats(lstats, rstats, output_reqs);
+ bfa_fcpim_add_iostats(lstats, rstats, rd_throughput);
+ bfa_fcpim_add_iostats(lstats, rstats, wr_throughput);
+}
+
+bfa_status_t
+bfa_fcpim_port_iostats(struct bfa_s *bfa,
+ struct bfa_itnim_iostats_s *stats, u8 lp_tag)
+{
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+ struct list_head *qe, *qen;
+ struct bfa_itnim_s *itnim;
+
+ /* accumulate IO stats from itnim */
+ memset(stats, 0, sizeof(struct bfa_itnim_iostats_s));
+ list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+ itnim = (struct bfa_itnim_s *) qe;
+ if (itnim->rport->rport_info.lp_tag != lp_tag)
+ continue;
+ bfa_fcpim_add_stats(stats, &(itnim->stats));
+ }
+ return BFA_STATUS_OK;
+}
+
+static void
+bfa_ioim_profile_comp(struct bfa_ioim_s *ioim)
+{
+ struct bfa_itnim_latency_s *io_lat =
+ &(ioim->itnim->ioprofile.io_latency);
+ u32 val, idx;
+
+ val = (u32)(jiffies - ioim->start_time);
+ idx = bfa_ioim_get_index(scsi_bufflen((struct scsi_cmnd *)ioim->dio));
+ bfa_itnim_ioprofile_update(ioim->itnim, idx);
+
+ io_lat->count[idx]++;
+ io_lat->min[idx] = (io_lat->min[idx] < val) ? io_lat->min[idx] : val;
+ io_lat->max[idx] = (io_lat->max[idx] > val) ? io_lat->max[idx] : val;
+ io_lat->avg[idx] += val;
+}
+
+static void
+bfa_ioim_profile_start(struct bfa_ioim_s *ioim)
+{
+ ioim->start_time = jiffies;
+}
+
+bfa_status_t
+bfa_fcpim_profile_on(struct bfa_s *bfa, time64_t time)
+{
+ struct bfa_itnim_s *itnim;
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+ struct list_head *qe, *qen;
+
+ /* accumulate IO stats from itnim */
+ list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+ itnim = (struct bfa_itnim_s *) qe;
+ bfa_itnim_clear_stats(itnim);
+ }
+ fcpim->io_profile = BFA_TRUE;
+ fcpim->io_profile_start_time = time;
+ fcpim->profile_comp = bfa_ioim_profile_comp;
+ fcpim->profile_start = bfa_ioim_profile_start;
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_fcpim_profile_off(struct bfa_s *bfa)
+{
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+ fcpim->io_profile = BFA_FALSE;
+ fcpim->io_profile_start_time = 0;
+ fcpim->profile_comp = NULL;
+ fcpim->profile_start = NULL;
+ return BFA_STATUS_OK;
+}
+
u16
bfa_fcpim_qdepth_get(struct bfa_s *bfa)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
return fcpim->q_depth;
}
@@ -990,8 +1043,7 @@ bfa_itnim_tskdone(struct bfa_itnim_s *itnim)
}
void
-bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len)
+bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len)
{
/*
* ITN memory
@@ -1000,15 +1052,16 @@ bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
}
void
-bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo)
+bfa_itnim_attach(struct bfa_fcpim_s *fcpim)
{
struct bfa_s *bfa = fcpim->bfa;
+ struct bfa_fcp_mod_s *fcp = fcpim->fcp;
struct bfa_itnim_s *itnim;
int i, j;
INIT_LIST_HEAD(&fcpim->itnim_q);
- itnim = (struct bfa_itnim_s *) bfa_meminfo_kva(minfo);
+ itnim = (struct bfa_itnim_s *) bfa_mem_kva_curp(fcp);
fcpim->itnim_arr = itnim;
for (i = 0; i < fcpim->num_itnims; i++, itnim++) {
@@ -1030,7 +1083,7 @@ bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo)
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
}
- bfa_meminfo_kva(minfo) = (u8 *) itnim;
+ bfa_mem_kva_curp(fcp) = (u8 *) itnim;
}
void
@@ -1043,7 +1096,7 @@ bfa_itnim_iocdisable(struct bfa_itnim_s *itnim)
static bfa_boolean_t
bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim)
{
- struct bfi_itnim_create_req_s *m;
+ struct bfi_itn_create_req_s *m;
itnim->msg_no++;
@@ -1056,8 +1109,8 @@ bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim)
return BFA_FALSE;
}
- bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_CREATE_REQ,
- bfa_lpuid(itnim->bfa));
+ bfi_h2i_set(m->mh, BFI_MC_ITN, BFI_ITN_H2I_CREATE_REQ,
+ bfa_fn_lpu(itnim->bfa));
m->fw_handle = itnim->rport->fw_handle;
m->class = FC_CLASS_3;
m->seq_rec = itnim->seq_rec;
@@ -1067,14 +1120,14 @@ bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim)
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(itnim->bfa, itnim->reqq);
+ bfa_reqq_produce(itnim->bfa, itnim->reqq, m->mh);
return BFA_TRUE;
}
static bfa_boolean_t
bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim)
{
- struct bfi_itnim_delete_req_s *m;
+ struct bfi_itn_delete_req_s *m;
/*
* check for room in queue to send request now
@@ -1085,15 +1138,15 @@ bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim)
return BFA_FALSE;
}
- bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_DELETE_REQ,
- bfa_lpuid(itnim->bfa));
+ bfi_h2i_set(m->mh, BFI_MC_ITN, BFI_ITN_H2I_DELETE_REQ,
+ bfa_fn_lpu(itnim->bfa));
m->fw_handle = itnim->rport->fw_handle;
bfa_stats(itnim, fw_delete);
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(itnim->bfa, itnim->reqq);
+ bfa_reqq_produce(itnim->bfa, itnim->reqq, m->mh);
return BFA_TRUE;
}
@@ -1224,7 +1277,7 @@ bfa_itnim_iotov_delete(struct bfa_itnim_s *itnim)
static void
bfa_itnim_update_del_itn_stats(struct bfa_itnim_s *itnim)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(itnim->bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(itnim->bfa);
fcpim->del_itn_stats.del_itn_iocomp_aborted +=
itnim->stats.iocomp_aborted;
fcpim->del_itn_stats.del_itn_iocomp_timedout +=
@@ -1250,8 +1303,8 @@ bfa_itnim_update_del_itn_stats(struct bfa_itnim_s *itnim)
void
bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
- union bfi_itnim_i2h_msg_u msg;
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+ union bfi_itn_i2h_msg_u msg;
struct bfa_itnim_s *itnim;
bfa_trc(bfa, m->mhdr.msg_id);
@@ -1259,7 +1312,7 @@ bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
msg.msg = m;
switch (m->mhdr.msg_id) {
- case BFI_ITNIM_I2H_CREATE_RSP:
+ case BFI_ITN_I2H_CREATE_RSP:
itnim = BFA_ITNIM_FROM_TAG(fcpim,
msg.create_rsp->bfa_handle);
WARN_ON(msg.create_rsp->status != BFA_STATUS_OK);
@@ -1267,7 +1320,7 @@ bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP);
break;
- case BFI_ITNIM_I2H_DELETE_RSP:
+ case BFI_ITN_I2H_DELETE_RSP:
itnim = BFA_ITNIM_FROM_TAG(fcpim,
msg.delete_rsp->bfa_handle);
WARN_ON(msg.delete_rsp->status != BFA_STATUS_OK);
@@ -1275,7 +1328,7 @@ bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP);
break;
- case BFI_ITNIM_I2H_SLER_EVENT:
+ case BFI_ITN_I2H_SLER_EVENT:
itnim = BFA_ITNIM_FROM_TAG(fcpim,
msg.sler_event->bfa_handle);
bfa_stats(itnim, sler_events);
@@ -1295,9 +1348,11 @@ bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
struct bfa_itnim_s *
bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
struct bfa_itnim_s *itnim;
+ bfa_itn_create(bfa, rport, bfa_itnim_isr);
+
itnim = BFA_ITNIM_FROM_TAG(fcpim, rport->rport_tag);
WARN_ON(itnim->rport != rport);
@@ -1347,10 +1402,41 @@ bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable));
}
+#define bfa_io_lat_clock_res_div HZ
+#define bfa_io_lat_clock_res_mul 1000
+bfa_status_t
+bfa_itnim_get_ioprofile(struct bfa_itnim_s *itnim,
+ struct bfa_itnim_ioprofile_s *ioprofile)
+{
+ struct bfa_fcpim_s *fcpim;
+
+ if (!itnim)
+ return BFA_STATUS_NO_FCPIM_NEXUS;
+
+ fcpim = BFA_FCPIM(itnim->bfa);
+
+ if (!fcpim->io_profile)
+ return BFA_STATUS_IOPROFILE_OFF;
+
+ itnim->ioprofile.index = BFA_IOBUCKET_MAX;
+ /* unsigned 32-bit time_t overflow here in y2106 */
+ itnim->ioprofile.io_profile_start_time =
+ bfa_io_profile_start_time(itnim->bfa);
+ itnim->ioprofile.clock_res_mul = bfa_io_lat_clock_res_mul;
+ itnim->ioprofile.clock_res_div = bfa_io_lat_clock_res_div;
+ *ioprofile = itnim->ioprofile;
+
+ return BFA_STATUS_OK;
+}
+
void
bfa_itnim_clear_stats(struct bfa_itnim_s *itnim)
{
int j;
+
+ if (!itnim)
+ return;
+
memset(&itnim->stats, 0, sizeof(itnim->stats));
memset(&itnim->ioprofile, 0, sizeof(itnim->ioprofile));
for (j = 0; j < BFA_IOBUCKET_MAX; j++)
@@ -1415,7 +1501,7 @@ bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event)
bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
WARN_ON(!bfa_q_is_on_q(&ioim->itnim->pending_q, ioim));
bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
- __bfa_cb_ioim_abort, ioim);
+ __bfa_cb_ioim_abort, ioim);
break;
default:
@@ -1955,6 +2041,27 @@ bfa_ioim_sm_resfree(struct bfa_ioim_s *ioim, enum bfa_ioim_event event)
}
}
+/*
+ * This is called from bfa_fcpim_start after the bfa_init() with flash read
+ * is complete by driver. now invalidate the stale content of lun mask
+ * like unit attention, rp tag and lp tag.
+ */
+void
+bfa_ioim_lm_init(struct bfa_s *bfa)
+{
+ struct bfa_lun_mask_s *lunm_list;
+ int i;
+
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+ return;
+
+ lunm_list = bfa_get_lun_mask_list(bfa);
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ lunm_list[i].ua = BFA_IOIM_LM_UA_RESET;
+ lunm_list[i].lp_tag = BFA_LP_TAG_INVALID;
+ lunm_list[i].rp_tag = BFA_RPORT_TAG_INVALID;
+ }
+}
static void
__bfa_cb_ioim_good_comp(void *cbarg, bfa_boolean_t complete)
@@ -1988,10 +2095,11 @@ __bfa_cb_ioim_comp(void *cbarg, bfa_boolean_t complete)
/*
* setup sense information, if present
*/
- if ((m->scsi_status == SCSI_STATUS_CHECK_CONDITION) &&
+ if ((m->scsi_status == SAM_STAT_CHECK_CONDITION) &&
m->sns_len) {
sns_len = m->sns_len;
- snsinfo = ioim->iosp->snsinfo;
+ snsinfo = BFA_SNSINFO_FROM_TAG(ioim->fcpim->fcp,
+ ioim->iotag);
}
/*
@@ -2012,6 +2120,218 @@ __bfa_cb_ioim_comp(void *cbarg, bfa_boolean_t complete)
m->scsi_status, sns_len, snsinfo, residue);
}
+void
+bfa_fcpim_lunmask_rp_update(struct bfa_s *bfa, wwn_t lp_wwn, wwn_t rp_wwn,
+ u16 rp_tag, u8 lp_tag)
+{
+ struct bfa_lun_mask_s *lun_list;
+ u8 i;
+
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+ return;
+
+ lun_list = bfa_get_lun_mask_list(bfa);
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if (lun_list[i].state == BFA_IOIM_LUN_MASK_ACTIVE) {
+ if ((lun_list[i].lp_wwn == lp_wwn) &&
+ (lun_list[i].rp_wwn == rp_wwn)) {
+ lun_list[i].rp_tag = rp_tag;
+ lun_list[i].lp_tag = lp_tag;
+ }
+ }
+ }
+}
+
+/*
+ * set UA for all active luns in LM DB
+ */
+static void
+bfa_ioim_lm_set_ua(struct bfa_s *bfa)
+{
+ struct bfa_lun_mask_s *lunm_list;
+ int i;
+
+ lunm_list = bfa_get_lun_mask_list(bfa);
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if (lunm_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE)
+ continue;
+ lunm_list[i].ua = BFA_IOIM_LM_UA_SET;
+ }
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_update(struct bfa_s *bfa, u32 update)
+{
+ struct bfa_lunmask_cfg_s *lun_mask;
+
+ bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+ return BFA_STATUS_FAILED;
+
+ if (bfa_get_lun_mask_status(bfa) == update)
+ return BFA_STATUS_NO_CHANGE;
+
+ lun_mask = bfa_get_lun_mask(bfa);
+ lun_mask->status = update;
+
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_ENABLED)
+ bfa_ioim_lm_set_ua(bfa);
+
+ return bfa_dconf_update(bfa);
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_clear(struct bfa_s *bfa)
+{
+ int i;
+ struct bfa_lun_mask_s *lunm_list;
+
+ bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+ return BFA_STATUS_FAILED;
+
+ lunm_list = bfa_get_lun_mask_list(bfa);
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if (lunm_list[i].state == BFA_IOIM_LUN_MASK_ACTIVE) {
+ if (lunm_list[i].rp_tag != BFA_RPORT_TAG_INVALID)
+ bfa_rport_unset_lunmask(bfa,
+ BFA_RPORT_FROM_TAG(bfa, lunm_list[i].rp_tag));
+ }
+ }
+
+ memset(lunm_list, 0, sizeof(struct bfa_lun_mask_s) * MAX_LUN_MASK_CFG);
+ return bfa_dconf_update(bfa);
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_query(struct bfa_s *bfa, void *buf)
+{
+ struct bfa_lunmask_cfg_s *lun_mask;
+
+ bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+ return BFA_STATUS_FAILED;
+
+ lun_mask = bfa_get_lun_mask(bfa);
+ memcpy(buf, lun_mask, sizeof(struct bfa_lunmask_cfg_s));
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_add(struct bfa_s *bfa, u16 vf_id, wwn_t *pwwn,
+ wwn_t rpwwn, struct scsi_lun lun)
+{
+ struct bfa_lun_mask_s *lunm_list;
+ struct bfa_rport_s *rp = NULL;
+ int i, free_index = MAX_LUN_MASK_CFG + 1;
+ struct bfa_fcs_lport_s *port = NULL;
+ struct bfa_fcs_rport_s *rp_fcs;
+
+ bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+ return BFA_STATUS_FAILED;
+
+ port = bfa_fcs_lookup_port(&((struct bfad_s *)bfa->bfad)->bfa_fcs,
+ vf_id, *pwwn);
+ if (port) {
+ *pwwn = port->port_cfg.pwwn;
+ rp_fcs = bfa_fcs_lport_get_rport_by_pwwn(port, rpwwn);
+ if (rp_fcs)
+ rp = rp_fcs->bfa_rport;
+ }
+
+ lunm_list = bfa_get_lun_mask_list(bfa);
+ /* if entry exists */
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if (lunm_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE)
+ free_index = i;
+ if ((lunm_list[i].lp_wwn == *pwwn) &&
+ (lunm_list[i].rp_wwn == rpwwn) &&
+ (scsilun_to_int((struct scsi_lun *)&lunm_list[i].lun) ==
+ scsilun_to_int((struct scsi_lun *)&lun)))
+ return BFA_STATUS_ENTRY_EXISTS;
+ }
+
+ if (free_index > MAX_LUN_MASK_CFG)
+ return BFA_STATUS_MAX_ENTRY_REACHED;
+
+ if (rp) {
+ lunm_list[free_index].lp_tag = bfa_lps_get_tag_from_pid(bfa,
+ rp->rport_info.local_pid);
+ lunm_list[free_index].rp_tag = rp->rport_tag;
+ } else {
+ lunm_list[free_index].lp_tag = BFA_LP_TAG_INVALID;
+ lunm_list[free_index].rp_tag = BFA_RPORT_TAG_INVALID;
+ }
+
+ lunm_list[free_index].lp_wwn = *pwwn;
+ lunm_list[free_index].rp_wwn = rpwwn;
+ lunm_list[free_index].lun = lun;
+ lunm_list[free_index].state = BFA_IOIM_LUN_MASK_ACTIVE;
+
+ /* set for all luns in this rp */
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if ((lunm_list[i].lp_wwn == *pwwn) &&
+ (lunm_list[i].rp_wwn == rpwwn))
+ lunm_list[i].ua = BFA_IOIM_LM_UA_SET;
+ }
+
+ return bfa_dconf_update(bfa);
+}
+
+bfa_status_t
+bfa_fcpim_lunmask_delete(struct bfa_s *bfa, u16 vf_id, wwn_t *pwwn,
+ wwn_t rpwwn, struct scsi_lun lun)
+{
+ struct bfa_lun_mask_s *lunm_list;
+ struct bfa_fcs_lport_s *port = NULL;
+ int i;
+
+ /* in min cfg lunm_list could be NULL but no commands should run. */
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_MINCFG)
+ return BFA_STATUS_FAILED;
+
+ bfa_trc(bfa, bfa_get_lun_mask_status(bfa));
+ bfa_trc(bfa, *pwwn);
+ bfa_trc(bfa, rpwwn);
+ bfa_trc(bfa, scsilun_to_int((struct scsi_lun *)&lun));
+
+ if (*pwwn == 0) {
+ port = bfa_fcs_lookup_port(
+ &((struct bfad_s *)bfa->bfad)->bfa_fcs,
+ vf_id, *pwwn);
+ if (port)
+ *pwwn = port->port_cfg.pwwn;
+ }
+
+ lunm_list = bfa_get_lun_mask_list(bfa);
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if ((lunm_list[i].lp_wwn == *pwwn) &&
+ (lunm_list[i].rp_wwn == rpwwn) &&
+ (scsilun_to_int((struct scsi_lun *)&lunm_list[i].lun) ==
+ scsilun_to_int((struct scsi_lun *)&lun))) {
+ lunm_list[i].lp_wwn = 0;
+ lunm_list[i].rp_wwn = 0;
+ int_to_scsilun(0, &lunm_list[i].lun);
+ lunm_list[i].state = BFA_IOIM_LUN_MASK_INACTIVE;
+ if (lunm_list[i].rp_tag != BFA_RPORT_TAG_INVALID) {
+ lunm_list[i].rp_tag = BFA_RPORT_TAG_INVALID;
+ lunm_list[i].lp_tag = BFA_LP_TAG_INVALID;
+ }
+ return bfa_dconf_update(bfa);
+ }
+ }
+
+ /* set for all luns in this rp */
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if ((lunm_list[i].lp_wwn == *pwwn) &&
+ (lunm_list[i].rp_wwn == rpwwn))
+ lunm_list[i].ua = BFA_IOIM_LM_UA_SET;
+ }
+
+ return BFA_STATUS_ENTRY_NOT_EXISTS;
+}
+
static void
__bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete)
{
@@ -2189,29 +2509,30 @@ bfa_ioim_send_ioreq(struct bfa_ioim_s *ioim)
*/
switch (m->cmnd.iodir) {
case FCP_IODIR_READ:
- bfi_h2i_set(m->mh, BFI_MC_IOIM_READ, 0, bfa_lpuid(ioim->bfa));
+ bfi_h2i_set(m->mh, BFI_MC_IOIM_READ, 0, bfa_fn_lpu(ioim->bfa));
bfa_stats(itnim, input_reqs);
ioim->itnim->stats.rd_throughput += fcp_dl;
break;
case FCP_IODIR_WRITE:
- bfi_h2i_set(m->mh, BFI_MC_IOIM_WRITE, 0, bfa_lpuid(ioim->bfa));
+ bfi_h2i_set(m->mh, BFI_MC_IOIM_WRITE, 0, bfa_fn_lpu(ioim->bfa));
bfa_stats(itnim, output_reqs);
ioim->itnim->stats.wr_throughput += fcp_dl;
break;
case FCP_IODIR_RW:
bfa_stats(itnim, input_reqs);
bfa_stats(itnim, output_reqs);
+ fallthrough;
default:
- bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_lpuid(ioim->bfa));
+ bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_fn_lpu(ioim->bfa));
}
if (itnim->seq_rec ||
(scsi_bufflen(cmnd) & (sizeof(u32) - 1)))
- bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_lpuid(ioim->bfa));
+ bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_fn_lpu(ioim->bfa));
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(ioim->bfa, ioim->reqq);
+ bfa_reqq_produce(ioim->bfa, ioim->reqq, m->mh);
return BFA_TRUE;
}
@@ -2269,14 +2590,14 @@ bfa_ioim_send_abort(struct bfa_ioim_s *ioim)
else
msgop = BFI_IOIM_H2I_IOCLEANUP_REQ;
- bfi_h2i_set(m->mh, BFI_MC_IOIM, msgop, bfa_lpuid(ioim->bfa));
+ bfi_h2i_set(m->mh, BFI_MC_IOIM, msgop, bfa_fn_lpu(ioim->bfa));
m->io_tag = cpu_to_be16(ioim->iotag);
m->abort_tag = ++ioim->abort_tag;
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(ioim->bfa, ioim->reqq);
+ bfa_reqq_produce(ioim->bfa, ioim->reqq, m->mh);
return BFA_TRUE;
}
@@ -2360,46 +2681,32 @@ bfa_ioim_delayed_comp(struct bfa_ioim_s *ioim, bfa_boolean_t iotov)
* Memory allocation and initialization.
*/
void
-bfa_ioim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo)
+bfa_ioim_attach(struct bfa_fcpim_s *fcpim)
{
struct bfa_ioim_s *ioim;
+ struct bfa_fcp_mod_s *fcp = fcpim->fcp;
struct bfa_ioim_sp_s *iosp;
u16 i;
- u8 *snsinfo;
- u32 snsbufsz;
/*
* claim memory first
*/
- ioim = (struct bfa_ioim_s *) bfa_meminfo_kva(minfo);
+ ioim = (struct bfa_ioim_s *) bfa_mem_kva_curp(fcp);
fcpim->ioim_arr = ioim;
- bfa_meminfo_kva(minfo) = (u8 *) (ioim + fcpim->num_ioim_reqs);
+ bfa_mem_kva_curp(fcp) = (u8 *) (ioim + fcpim->fcp->num_ioim_reqs);
- iosp = (struct bfa_ioim_sp_s *) bfa_meminfo_kva(minfo);
+ iosp = (struct bfa_ioim_sp_s *) bfa_mem_kva_curp(fcp);
fcpim->ioim_sp_arr = iosp;
- bfa_meminfo_kva(minfo) = (u8 *) (iosp + fcpim->num_ioim_reqs);
-
- /*
- * Claim DMA memory for per IO sense data.
- */
- snsbufsz = fcpim->num_ioim_reqs * BFI_IOIM_SNSLEN;
- fcpim->snsbase.pa = bfa_meminfo_dma_phys(minfo);
- bfa_meminfo_dma_phys(minfo) += snsbufsz;
-
- fcpim->snsbase.kva = bfa_meminfo_dma_virt(minfo);
- bfa_meminfo_dma_virt(minfo) += snsbufsz;
- snsinfo = fcpim->snsbase.kva;
- bfa_iocfc_set_snsbase(fcpim->bfa, fcpim->snsbase.pa);
+ bfa_mem_kva_curp(fcp) = (u8 *) (iosp + fcpim->fcp->num_ioim_reqs);
/*
* Initialize ioim free queues
*/
- INIT_LIST_HEAD(&fcpim->ioim_free_q);
INIT_LIST_HEAD(&fcpim->ioim_resfree_q);
INIT_LIST_HEAD(&fcpim->ioim_comp_q);
- for (i = 0; i < fcpim->num_ioim_reqs;
- i++, ioim++, iosp++, snsinfo += BFI_IOIM_SNSLEN) {
+ for (i = 0; i < fcpim->fcp->num_ioim_reqs;
+ i++, ioim++, iosp++) {
/*
* initialize IOIM
*/
@@ -2408,22 +2715,19 @@ bfa_ioim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo)
ioim->bfa = fcpim->bfa;
ioim->fcpim = fcpim;
ioim->iosp = iosp;
- iosp->snsinfo = snsinfo;
INIT_LIST_HEAD(&ioim->sgpg_q);
bfa_reqq_winit(&ioim->iosp->reqq_wait,
bfa_ioim_qresume, ioim);
bfa_sgpg_winit(&ioim->iosp->sgpg_wqe,
bfa_ioim_sgpg_alloced, ioim);
bfa_sm_set_state(ioim, bfa_ioim_sm_uninit);
-
- list_add_tail(&ioim->qe, &fcpim->ioim_free_q);
}
}
void
bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
struct bfi_ioim_rsp_s *rsp = (struct bfi_ioim_rsp_s *) m;
struct bfa_ioim_s *ioim;
u16 iotag;
@@ -2452,6 +2756,7 @@ bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
case BFI_IOIM_STS_TIMEDOUT:
bfa_stats(ioim->itnim, iocomp_timedout);
+ fallthrough;
case BFI_IOIM_STS_ABORTED:
rsp->io_status = BFI_IOIM_STS_ABORTED;
bfa_stats(ioim->itnim, iocomp_aborted);
@@ -2507,7 +2812,7 @@ bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
void
bfa_ioim_good_comp_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
struct bfi_ioim_rsp_s *rsp = (struct bfi_ioim_rsp_s *) m;
struct bfa_ioim_s *ioim;
u16 iotag;
@@ -2515,9 +2820,10 @@ bfa_ioim_good_comp_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
iotag = be16_to_cpu(rsp->io_tag);
ioim = BFA_IOIM_FROM_TAG(fcpim, iotag);
- WARN_ON(BFA_IOIM_TAG_2_ID(ioim->iotag) != iotag);
+ WARN_ON(ioim->iotag != iotag);
bfa_ioim_cb_profile_comp(fcpim, ioim);
+
bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD);
}
@@ -2573,18 +2879,21 @@ struct bfa_ioim_s *
bfa_ioim_alloc(struct bfa_s *bfa, struct bfad_ioim_s *dio,
struct bfa_itnim_s *itnim, u16 nsges)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
struct bfa_ioim_s *ioim;
+ struct bfa_iotag_s *iotag = NULL;
/*
* alocate IOIM resource
*/
- bfa_q_deq(&fcpim->ioim_free_q, &ioim);
- if (!ioim) {
+ bfa_q_deq(&fcpim->fcp->iotag_ioim_free_q, &iotag);
+ if (!iotag) {
bfa_stats(itnim, no_iotags);
return NULL;
}
+ ioim = BFA_IOIM_FROM_TAG(fcpim, iotag->tag);
+
ioim->dio = dio;
ioim->itnim = itnim;
ioim->nsges = nsges;
@@ -2601,7 +2910,8 @@ bfa_ioim_alloc(struct bfa_s *bfa, struct bfad_ioim_s *dio,
void
bfa_ioim_free(struct bfa_ioim_s *ioim)
{
- struct bfa_fcpim_mod_s *fcpim = ioim->fcpim;
+ struct bfa_fcpim_s *fcpim = ioim->fcpim;
+ struct bfa_iotag_s *iotag;
if (ioim->nsgpgs > 0)
bfa_sgpg_mfree(ioim->bfa, &ioim->sgpg_q, ioim->nsgpgs);
@@ -2610,8 +2920,17 @@ bfa_ioim_free(struct bfa_ioim_s *ioim)
fcpim->ios_active--;
ioim->iotag &= BFA_IOIM_IOTAG_MASK;
+
+ WARN_ON(!(ioim->iotag <
+ (fcpim->fcp->num_ioim_reqs + fcpim->fcp->num_fwtio_reqs)));
+ iotag = BFA_IOTAG_FROM_TAG(fcpim->fcp, ioim->iotag);
+
+ if (ioim->iotag < fcpim->fcp->num_ioim_reqs)
+ list_add_tail(&iotag->qe, &fcpim->fcp->iotag_ioim_free_q);
+ else
+ list_add_tail(&iotag->qe, &fcpim->fcp->iotag_tio_free_q);
+
list_del(&ioim->qe);
- list_add_tail(&ioim->qe, &fcpim->ioim_free_q);
}
void
@@ -2656,7 +2975,7 @@ bfa_ioim_abort(struct bfa_ioim_s *ioim)
static void
bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
{
- bfa_trc(tskim->bfa, event);
+ bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event);
switch (event) {
case BFA_TSKIM_SM_START:
@@ -2694,7 +3013,7 @@ bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
static void
bfa_tskim_sm_active(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
{
- bfa_trc(tskim->bfa, event);
+ bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event);
switch (event) {
case BFA_TSKIM_SM_DONE:
@@ -2730,7 +3049,7 @@ bfa_tskim_sm_active(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
static void
bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
{
- bfa_trc(tskim->bfa, event);
+ bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event);
switch (event) {
case BFA_TSKIM_SM_DONE:
@@ -2739,6 +3058,7 @@ bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
*/
break;
+ case BFA_TSKIM_SM_UTAG:
case BFA_TSKIM_SM_CLEANUP_DONE:
bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup);
bfa_tskim_cleanup_ios(tskim);
@@ -2758,7 +3078,7 @@ bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
static void
bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
{
- bfa_trc(tskim->bfa, event);
+ bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event);
switch (event) {
case BFA_TSKIM_SM_IOS_DONE:
@@ -2790,7 +3110,7 @@ bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
static void
bfa_tskim_sm_qfull(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
{
- bfa_trc(tskim->bfa, event);
+ bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event);
switch (event) {
case BFA_TSKIM_SM_QRESUME:
@@ -2827,14 +3147,12 @@ static void
bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim,
enum bfa_tskim_event event)
{
- bfa_trc(tskim->bfa, event);
+ bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event);
switch (event) {
case BFA_TSKIM_SM_DONE:
bfa_reqq_wcancel(&tskim->reqq_wait);
- /*
- * Fall through !!!
- */
+ fallthrough;
case BFA_TSKIM_SM_QRESUME:
bfa_sm_set_state(tskim, bfa_tskim_sm_cleanup);
bfa_tskim_send_abort(tskim);
@@ -2858,7 +3176,7 @@ bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim,
static void
bfa_tskim_sm_hcb(struct bfa_tskim_s *tskim, enum bfa_tskim_event event)
{
- bfa_trc(tskim->bfa, event);
+ bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event);
switch (event) {
case BFA_TSKIM_SM_HCB:
@@ -3021,7 +3339,7 @@ bfa_tskim_send(struct bfa_tskim_s *tskim)
* build i/o request message next
*/
bfi_h2i_set(m->mh, BFI_MC_TSKIM, BFI_TSKIM_H2I_TM_REQ,
- bfa_lpuid(tskim->bfa));
+ bfa_fn_lpu(tskim->bfa));
m->tsk_tag = cpu_to_be16(tskim->tsk_tag);
m->itn_fhdl = tskim->itnim->rport->fw_handle;
@@ -3032,7 +3350,7 @@ bfa_tskim_send(struct bfa_tskim_s *tskim)
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(tskim->bfa, itnim->reqq);
+ bfa_reqq_produce(tskim->bfa, itnim->reqq, m->mh);
return BFA_TRUE;
}
@@ -3056,14 +3374,14 @@ bfa_tskim_send_abort(struct bfa_tskim_s *tskim)
* build i/o request message next
*/
bfi_h2i_set(m->mh, BFI_MC_TSKIM, BFI_TSKIM_H2I_ABORT_REQ,
- bfa_lpuid(tskim->bfa));
+ bfa_fn_lpu(tskim->bfa));
m->tsk_tag = cpu_to_be16(tskim->tsk_tag);
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(tskim->bfa, itnim->reqq);
+ bfa_reqq_produce(tskim->bfa, itnim->reqq, m->mh);
return BFA_TRUE;
}
@@ -3095,15 +3413,6 @@ bfa_tskim_iocdisable_ios(struct bfa_tskim_s *tskim)
}
/*
- * Notification on completions from related ioim.
- */
-void
-bfa_tskim_iodone(struct bfa_tskim_s *tskim)
-{
- bfa_wc_down(&tskim->wc);
-}
-
-/*
* Handle IOC h/w failure notification from itnim.
*/
void
@@ -3129,14 +3438,16 @@ bfa_tskim_cleanup(struct bfa_tskim_s *tskim)
* Memory allocation and initialization.
*/
void
-bfa_tskim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo)
+bfa_tskim_attach(struct bfa_fcpim_s *fcpim)
{
struct bfa_tskim_s *tskim;
+ struct bfa_fcp_mod_s *fcp = fcpim->fcp;
u16 i;
INIT_LIST_HEAD(&fcpim->tskim_free_q);
+ INIT_LIST_HEAD(&fcpim->tskim_unused_q);
- tskim = (struct bfa_tskim_s *) bfa_meminfo_kva(minfo);
+ tskim = (struct bfa_tskim_s *) bfa_mem_kva_curp(fcp);
fcpim->tskim_arr = tskim;
for (i = 0; i < fcpim->num_tskim_reqs; i++, tskim++) {
@@ -3155,13 +3466,13 @@ bfa_tskim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo)
list_add_tail(&tskim->qe, &fcpim->tskim_free_q);
}
- bfa_meminfo_kva(minfo) = (u8 *) tskim;
+ bfa_mem_kva_curp(fcp) = (u8 *) tskim;
}
void
bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
struct bfi_tskim_rsp_s *rsp = (struct bfi_tskim_rsp_s *) m;
struct bfa_tskim_s *tskim;
u16 tsk_tag = be16_to_cpu(rsp->tsk_tag);
@@ -3178,6 +3489,8 @@ bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
if (rsp->tsk_status == BFI_TSKIM_STS_ABORTED) {
bfa_stats(tskim->itnim, tm_cleanup_comps);
bfa_sm_send_event(tskim, BFA_TSKIM_SM_CLEANUP_DONE);
+ } else if (rsp->tsk_status == BFI_TSKIM_STS_UTAG) {
+ bfa_sm_send_event(tskim, BFA_TSKIM_SM_UTAG);
} else {
bfa_stats(tskim->itnim, tm_fw_rsps);
bfa_sm_send_event(tskim, BFA_TSKIM_SM_DONE);
@@ -3188,7 +3501,7 @@ bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
struct bfa_tskim_s *
bfa_tskim_alloc(struct bfa_s *bfa, struct bfad_tskim_s *dtsk)
{
- struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
struct bfa_tskim_s *tskim;
bfa_q_deq(&fcpim->tskim_free_q, &tskim);
@@ -3233,3 +3546,292 @@ bfa_tskim_start(struct bfa_tskim_s *tskim, struct bfa_itnim_s *itnim,
list_add_tail(&tskim->qe, &itnim->tsk_q);
bfa_sm_send_event(tskim, BFA_TSKIM_SM_START);
}
+
+void
+bfa_tskim_res_recfg(struct bfa_s *bfa, u16 num_tskim_fw)
+{
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+ struct list_head *qe;
+ int i;
+
+ for (i = 0; i < (fcpim->num_tskim_reqs - num_tskim_fw); i++) {
+ bfa_q_deq_tail(&fcpim->tskim_free_q, &qe);
+ list_add_tail(qe, &fcpim->tskim_unused_q);
+ }
+}
+
+void
+bfa_fcp_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo,
+ struct bfa_s *bfa)
+{
+ struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa);
+ struct bfa_mem_kva_s *fcp_kva = BFA_MEM_FCP_KVA(bfa);
+ struct bfa_mem_dma_s *seg_ptr;
+ u16 nsegs, idx, per_seg_ios, num_io_req;
+ u32 km_len = 0;
+
+ /*
+ * ZERO for num_ioim_reqs and num_fwtio_reqs is allowed config value.
+ * So if the values are non zero, adjust them appropriately.
+ */
+ if (cfg->fwcfg.num_ioim_reqs &&
+ cfg->fwcfg.num_ioim_reqs < BFA_IOIM_MIN)
+ cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN;
+ else if (cfg->fwcfg.num_ioim_reqs > BFA_IOIM_MAX)
+ cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MAX;
+
+ if (cfg->fwcfg.num_fwtio_reqs > BFA_FWTIO_MAX)
+ cfg->fwcfg.num_fwtio_reqs = BFA_FWTIO_MAX;
+
+ num_io_req = (cfg->fwcfg.num_ioim_reqs + cfg->fwcfg.num_fwtio_reqs);
+ if (num_io_req > BFA_IO_MAX) {
+ if (cfg->fwcfg.num_ioim_reqs && cfg->fwcfg.num_fwtio_reqs) {
+ cfg->fwcfg.num_ioim_reqs = BFA_IO_MAX/2;
+ cfg->fwcfg.num_fwtio_reqs = BFA_IO_MAX/2;
+ } else if (cfg->fwcfg.num_fwtio_reqs)
+ cfg->fwcfg.num_fwtio_reqs = BFA_FWTIO_MAX;
+ else
+ cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MAX;
+ }
+
+ bfa_fcpim_meminfo(cfg, &km_len);
+
+ num_io_req = (cfg->fwcfg.num_ioim_reqs + cfg->fwcfg.num_fwtio_reqs);
+ km_len += num_io_req * sizeof(struct bfa_iotag_s);
+ km_len += cfg->fwcfg.num_rports * sizeof(struct bfa_itn_s);
+
+ /* dma memory */
+ nsegs = BFI_MEM_DMA_NSEGS(num_io_req, BFI_IOIM_SNSLEN);
+ per_seg_ios = BFI_MEM_NREQS_SEG(BFI_IOIM_SNSLEN);
+
+ bfa_mem_dma_seg_iter(fcp, seg_ptr, nsegs, idx) {
+ if (num_io_req >= per_seg_ios) {
+ num_io_req -= per_seg_ios;
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ per_seg_ios * BFI_IOIM_SNSLEN);
+ } else
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ num_io_req * BFI_IOIM_SNSLEN);
+ }
+
+ /* kva memory */
+ bfa_mem_kva_setup(minfo, fcp_kva, km_len);
+}
+
+void
+bfa_fcp_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
+ struct bfa_pcidev_s *pcidev)
+{
+ struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa);
+ struct bfa_mem_dma_s *seg_ptr;
+ u16 idx, nsegs, num_io_req;
+
+ fcp->max_ioim_reqs = cfg->fwcfg.num_ioim_reqs;
+ fcp->num_ioim_reqs = cfg->fwcfg.num_ioim_reqs;
+ fcp->num_fwtio_reqs = cfg->fwcfg.num_fwtio_reqs;
+ fcp->num_itns = cfg->fwcfg.num_rports;
+ fcp->bfa = bfa;
+
+ /*
+ * Setup the pool of snsbase addr's, that is passed to fw as
+ * part of bfi_iocfc_cfg_s.
+ */
+ num_io_req = (cfg->fwcfg.num_ioim_reqs + cfg->fwcfg.num_fwtio_reqs);
+ nsegs = BFI_MEM_DMA_NSEGS(num_io_req, BFI_IOIM_SNSLEN);
+
+ bfa_mem_dma_seg_iter(fcp, seg_ptr, nsegs, idx) {
+
+ if (!bfa_mem_dma_virt(seg_ptr))
+ break;
+
+ fcp->snsbase[idx].pa = bfa_mem_dma_phys(seg_ptr);
+ fcp->snsbase[idx].kva = bfa_mem_dma_virt(seg_ptr);
+ bfa_iocfc_set_snsbase(bfa, idx, fcp->snsbase[idx].pa);
+ }
+
+ fcp->throttle_update_required = 1;
+ bfa_fcpim_attach(fcp, bfad, cfg, pcidev);
+
+ bfa_iotag_attach(fcp);
+
+ fcp->itn_arr = (struct bfa_itn_s *) bfa_mem_kva_curp(fcp);
+ bfa_mem_kva_curp(fcp) = (u8 *)fcp->itn_arr +
+ (fcp->num_itns * sizeof(struct bfa_itn_s));
+ memset(fcp->itn_arr, 0,
+ (fcp->num_itns * sizeof(struct bfa_itn_s)));
+}
+
+void
+bfa_fcp_iocdisable(struct bfa_s *bfa)
+{
+ struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa);
+
+ bfa_fcpim_iocdisable(fcp);
+}
+
+void
+bfa_fcp_res_recfg(struct bfa_s *bfa, u16 num_ioim_fw, u16 max_ioim_fw)
+{
+ struct bfa_fcp_mod_s *mod = BFA_FCP_MOD(bfa);
+ struct list_head *qe;
+ int i;
+
+ /* Update io throttle value only once during driver load time */
+ if (!mod->throttle_update_required)
+ return;
+
+ for (i = 0; i < (mod->num_ioim_reqs - num_ioim_fw); i++) {
+ bfa_q_deq_tail(&mod->iotag_ioim_free_q, &qe);
+ list_add_tail(qe, &mod->iotag_unused_q);
+ }
+
+ if (mod->num_ioim_reqs != num_ioim_fw) {
+ bfa_trc(bfa, mod->num_ioim_reqs);
+ bfa_trc(bfa, num_ioim_fw);
+ }
+
+ mod->max_ioim_reqs = max_ioim_fw;
+ mod->num_ioim_reqs = num_ioim_fw;
+ mod->throttle_update_required = 0;
+}
+
+void
+bfa_itn_create(struct bfa_s *bfa, struct bfa_rport_s *rport,
+ void (*isr)(struct bfa_s *bfa, struct bfi_msg_s *m))
+{
+ struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa);
+ struct bfa_itn_s *itn;
+
+ itn = BFA_ITN_FROM_TAG(fcp, rport->rport_tag);
+ itn->isr = isr;
+}
+
+/*
+ * Itn interrupt processing.
+ */
+void
+bfa_itn_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
+{
+ struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa);
+ union bfi_itn_i2h_msg_u msg;
+ struct bfa_itn_s *itn;
+
+ msg.msg = m;
+ itn = BFA_ITN_FROM_TAG(fcp, msg.create_rsp->bfa_handle);
+
+ if (itn->isr)
+ itn->isr(bfa, m);
+ else
+ WARN_ON(1);
+}
+
+void
+bfa_iotag_attach(struct bfa_fcp_mod_s *fcp)
+{
+ struct bfa_iotag_s *iotag;
+ u16 num_io_req, i;
+
+ iotag = (struct bfa_iotag_s *) bfa_mem_kva_curp(fcp);
+ fcp->iotag_arr = iotag;
+
+ INIT_LIST_HEAD(&fcp->iotag_ioim_free_q);
+ INIT_LIST_HEAD(&fcp->iotag_tio_free_q);
+ INIT_LIST_HEAD(&fcp->iotag_unused_q);
+
+ num_io_req = fcp->num_ioim_reqs + fcp->num_fwtio_reqs;
+ for (i = 0; i < num_io_req; i++, iotag++) {
+ memset(iotag, 0, sizeof(struct bfa_iotag_s));
+ iotag->tag = i;
+ if (i < fcp->num_ioim_reqs)
+ list_add_tail(&iotag->qe, &fcp->iotag_ioim_free_q);
+ else
+ list_add_tail(&iotag->qe, &fcp->iotag_tio_free_q);
+ }
+
+ bfa_mem_kva_curp(fcp) = (u8 *) iotag;
+}
+
+
+/*
+ * To send config req, first try to use throttle value from flash
+ * If 0, then use driver parameter
+ * We need to use min(flash_val, drv_val) because
+ * memory allocation was done based on this cfg'd value
+ */
+u16
+bfa_fcpim_get_throttle_cfg(struct bfa_s *bfa, u16 drv_cfg_param)
+{
+ u16 tmp;
+ struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa);
+
+ /*
+ * If throttle value from flash is already in effect after driver is
+ * loaded then until next load, always return current value instead
+ * of actual flash value
+ */
+ if (!fcp->throttle_update_required)
+ return (u16)fcp->num_ioim_reqs;
+
+ tmp = bfa_dconf_read_data_valid(bfa) ? bfa_fcpim_read_throttle(bfa) : 0;
+ if (!tmp || (tmp > drv_cfg_param))
+ tmp = drv_cfg_param;
+
+ return tmp;
+}
+
+bfa_status_t
+bfa_fcpim_write_throttle(struct bfa_s *bfa, u16 value)
+{
+ if (!bfa_dconf_get_min_cfg(bfa)) {
+ BFA_DCONF_MOD(bfa)->dconf->throttle_cfg.value = value;
+ BFA_DCONF_MOD(bfa)->dconf->throttle_cfg.is_valid = 1;
+ return BFA_STATUS_OK;
+ }
+
+ return BFA_STATUS_FAILED;
+}
+
+u16
+bfa_fcpim_read_throttle(struct bfa_s *bfa)
+{
+ struct bfa_throttle_cfg_s *throttle_cfg =
+ &(BFA_DCONF_MOD(bfa)->dconf->throttle_cfg);
+
+ return ((!bfa_dconf_get_min_cfg(bfa)) ?
+ ((throttle_cfg->is_valid == 1) ? (throttle_cfg->value) : 0) : 0);
+}
+
+bfa_status_t
+bfa_fcpim_throttle_set(struct bfa_s *bfa, u16 value)
+{
+ /* in min cfg no commands should run. */
+ if ((bfa_dconf_get_min_cfg(bfa) == BFA_TRUE) ||
+ (!bfa_dconf_read_data_valid(bfa)))
+ return BFA_STATUS_FAILED;
+
+ bfa_fcpim_write_throttle(bfa, value);
+
+ return bfa_dconf_update(bfa);
+}
+
+bfa_status_t
+bfa_fcpim_throttle_get(struct bfa_s *bfa, void *buf)
+{
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa);
+ struct bfa_defs_fcpim_throttle_s throttle;
+
+ if ((bfa_dconf_get_min_cfg(bfa) == BFA_TRUE) ||
+ (!bfa_dconf_read_data_valid(bfa)))
+ return BFA_STATUS_FAILED;
+
+ memset(&throttle, 0, sizeof(struct bfa_defs_fcpim_throttle_s));
+
+ throttle.cur_value = (u16)(fcpim->fcp->num_ioim_reqs);
+ throttle.cfg_value = bfa_fcpim_read_throttle(bfa);
+ if (!throttle.cfg_value)
+ throttle.cfg_value = throttle.cur_value;
+ throttle.max_value = (u16)(fcpim->fcp->max_ioim_reqs);
+ memcpy(buf, &throttle, sizeof(struct bfa_defs_fcpim_throttle_s));
+
+ return BFA_STATUS_OK;
+}
diff --git a/drivers/scsi/bfa/bfa_fcpim.h b/drivers/scsi/bfa/bfa_fcpim.h
index 1e38dade8423..64e4485b226e 100644
--- a/drivers/scsi/bfa/bfa_fcpim.h
+++ b/drivers/scsi/bfa/bfa_fcpim.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_FCPIM_H__
@@ -24,6 +17,36 @@
#include "bfa_defs_svc.h"
#include "bfa_cs.h"
+/* FCP module related definitions */
+#define BFA_IO_MAX BFI_IO_MAX
+#define BFA_FWTIO_MAX 2000
+
+struct bfa_fcp_mod_s;
+struct bfa_iotag_s {
+ struct list_head qe; /* queue element */
+ u16 tag; /* FW IO tag */
+};
+
+struct bfa_itn_s {
+ bfa_isr_func_t isr;
+};
+
+void bfa_itn_create(struct bfa_s *bfa, struct bfa_rport_s *rport,
+ void (*isr)(struct bfa_s *bfa, struct bfi_msg_s *m));
+void bfa_itn_isr(struct bfa_s *bfa, struct bfi_msg_s *m);
+void bfa_iotag_attach(struct bfa_fcp_mod_s *fcp);
+void bfa_fcp_res_recfg(struct bfa_s *bfa, u16 num_ioim_fw, u16 max_ioim_fw);
+
+#define BFA_FCP_MOD(_hal) (&(_hal)->modules.fcp_mod)
+#define BFA_MEM_FCP_KVA(__bfa) (&(BFA_FCP_MOD(__bfa)->kva_seg))
+#define BFA_IOTAG_FROM_TAG(_fcp, _tag) \
+ (&(_fcp)->iotag_arr[(_tag & BFA_IOIM_IOTAG_MASK)])
+#define BFA_ITN_FROM_TAG(_fcp, _tag) \
+ ((_fcp)->itn_arr + ((_tag) & ((_fcp)->num_itns - 1)))
+#define BFA_SNSINFO_FROM_TAG(_fcp, _tag) \
+ bfa_mem_get_dmabuf_kva(_fcp, (_tag & BFA_IOIM_IOTAG_MASK), \
+ BFI_IOIM_SNSLEN)
+
#define BFA_ITNIM_MIN 32
#define BFA_ITNIM_MAX 1024
@@ -51,14 +74,22 @@ bfa_ioim_get_index(u32 n) {
if (n >= (1UL)<<22)
return BFA_IOBUCKET_MAX - 1;
n >>= 8;
- if (n >= (1UL)<<16)
- n >>= 16; pos += 16;
- if (n >= 1 << 8)
- n >>= 8; pos += 8;
- if (n >= 1 << 4)
- n >>= 4; pos += 4;
- if (n >= 1 << 2)
- n >>= 2; pos += 2;
+ if (n >= (1UL)<<16) {
+ n >>= 16;
+ pos += 16;
+ }
+ if (n >= 1 << 8) {
+ n >>= 8;
+ pos += 8;
+ }
+ if (n >= 1 << 4) {
+ n >>= 4;
+ pos += 4;
+ }
+ if (n >= 1 << 2) {
+ n >>= 2;
+ pos += 2;
+ }
if (n >= 1 << 1)
pos += 1;
@@ -75,43 +106,89 @@ struct bfad_tskim_s;
typedef void (*bfa_fcpim_profile_t) (struct bfa_ioim_s *ioim);
-struct bfa_fcpim_mod_s {
+struct bfa_fcpim_s {
struct bfa_s *bfa;
+ struct bfa_fcp_mod_s *fcp;
struct bfa_itnim_s *itnim_arr;
struct bfa_ioim_s *ioim_arr;
struct bfa_ioim_sp_s *ioim_sp_arr;
struct bfa_tskim_s *tskim_arr;
- struct bfa_dma_s snsbase;
int num_itnims;
- int num_ioim_reqs;
int num_tskim_reqs;
u32 path_tov;
u16 q_depth;
u8 reqq; /* Request queue to be used */
- u8 rsvd;
struct list_head itnim_q; /* queue of active itnim */
- struct list_head ioim_free_q; /* free IO resources */
struct list_head ioim_resfree_q; /* IOs waiting for f/w */
struct list_head ioim_comp_q; /* IO global comp Q */
struct list_head tskim_free_q;
+ struct list_head tskim_unused_q; /* Unused tskim Q */
u32 ios_active; /* current active IOs */
u32 delay_comp;
struct bfa_fcpim_del_itn_stats_s del_itn_stats;
bfa_boolean_t ioredirect;
bfa_boolean_t io_profile;
- u32 io_profile_start_time;
+ time64_t io_profile_start_time;
bfa_fcpim_profile_t profile_comp;
bfa_fcpim_profile_t profile_start;
};
+/* Max FCP dma segs required */
+#define BFA_FCP_DMA_SEGS BFI_IOIM_SNSBUF_SEGS
+
+struct bfa_fcp_mod_s {
+ struct bfa_s *bfa;
+ struct list_head iotag_ioim_free_q; /* free IO resources */
+ struct list_head iotag_tio_free_q; /* free IO resources */
+ struct list_head iotag_unused_q; /* unused IO resources*/
+ struct bfa_iotag_s *iotag_arr;
+ struct bfa_itn_s *itn_arr;
+ int max_ioim_reqs;
+ int num_ioim_reqs;
+ int num_fwtio_reqs;
+ int num_itns;
+ struct bfa_dma_s snsbase[BFA_FCP_DMA_SEGS];
+ struct bfa_fcpim_s fcpim;
+ struct bfa_mem_dma_s dma_seg[BFA_FCP_DMA_SEGS];
+ struct bfa_mem_kva_s kva_seg;
+ int throttle_update_required;
+};
+
+/*
+ * IO state machine events
+ */
+enum bfa_ioim_event {
+ BFA_IOIM_SM_START = 1, /* io start request from host */
+ BFA_IOIM_SM_COMP_GOOD = 2, /* io good comp, resource free */
+ BFA_IOIM_SM_COMP = 3, /* io comp, resource is free */
+ BFA_IOIM_SM_COMP_UTAG = 4, /* io comp, resource is free */
+ BFA_IOIM_SM_DONE = 5, /* io comp, resource not free */
+ BFA_IOIM_SM_FREE = 6, /* io resource is freed */
+ BFA_IOIM_SM_ABORT = 7, /* abort request from scsi stack */
+ BFA_IOIM_SM_ABORT_COMP = 8, /* abort from f/w */
+ BFA_IOIM_SM_ABORT_DONE = 9, /* abort completion from f/w */
+ BFA_IOIM_SM_QRESUME = 10, /* CQ space available to queue IO */
+ BFA_IOIM_SM_SGALLOCED = 11, /* SG page allocation successful */
+ BFA_IOIM_SM_SQRETRY = 12, /* sequence recovery retry */
+ BFA_IOIM_SM_HCB = 13, /* bfa callback complete */
+ BFA_IOIM_SM_CLEANUP = 14, /* IO cleanup from itnim */
+ BFA_IOIM_SM_TMSTART = 15, /* IO cleanup from tskim */
+ BFA_IOIM_SM_TMDONE = 16, /* IO cleanup from tskim */
+ BFA_IOIM_SM_HWFAIL = 17, /* IOC h/w failure event */
+ BFA_IOIM_SM_IOTOV = 18, /* ITN offline TOV */
+};
+
+struct bfa_ioim_s;
+typedef void (*bfa_ioim_sm_t)(struct bfa_ioim_s *, enum bfa_ioim_event);
+
/*
* BFA IO (initiator mode)
*/
struct bfa_ioim_s {
struct list_head qe; /* queue elememt */
- bfa_sm_t sm; /* BFA ioim state machine */
+ bfa_ioim_sm_t sm; /* BFA ioim state machine */
struct bfa_s *bfa; /* BFA module */
- struct bfa_fcpim_mod_s *fcpim; /* parent fcpim module */
+ struct bfa_fcpim_s *fcpim; /* parent fcpim module */
struct bfa_itnim_s *itnim; /* i-t-n nexus for this IO */
struct bfad_ioim_s *dio; /* driver IO handle */
u16 iotag; /* FWI IO tag */
@@ -124,26 +201,41 @@ struct bfa_ioim_s {
bfa_cb_cbfn_t io_cbfn; /* IO completion handler */
struct bfa_ioim_sp_s *iosp; /* slow-path IO handling */
u8 reqq; /* Request queue for I/O */
+ u8 mode; /* IO is passthrough or not */
u64 start_time; /* IO's Profile start val */
};
struct bfa_ioim_sp_s {
struct bfi_msg_s comp_rspmsg; /* IO comp f/w response */
- u8 *snsinfo; /* sense info for this IO */
struct bfa_sgpg_wqe_s sgpg_wqe; /* waitq elem for sgpg */
struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */
bfa_boolean_t abort_explicit; /* aborted by OS */
struct bfa_tskim_s *tskim; /* Relevant TM cmd */
};
+enum bfa_tskim_event {
+ BFA_TSKIM_SM_START = 1, /* TM command start */
+ BFA_TSKIM_SM_DONE = 2, /* TM completion */
+ BFA_TSKIM_SM_QRESUME = 3, /* resume after qfull */
+ BFA_TSKIM_SM_HWFAIL = 5, /* IOC h/w failure event */
+ BFA_TSKIM_SM_HCB = 6, /* BFA callback completion */
+ BFA_TSKIM_SM_IOS_DONE = 7, /* IO and sub TM completions */
+ BFA_TSKIM_SM_CLEANUP = 8, /* TM cleanup on ITN offline */
+ BFA_TSKIM_SM_CLEANUP_DONE = 9, /* TM abort completion */
+ BFA_TSKIM_SM_UTAG = 10, /* TM completion unknown tag */
+};
+
+struct bfa_tskim_s;
+typedef void (*bfa_tskim_sm_t)(struct bfa_tskim_s *, enum bfa_tskim_event);
+
/*
* BFA Task management command (initiator mode)
*/
struct bfa_tskim_s {
struct list_head qe;
- bfa_sm_t sm;
+ bfa_tskim_sm_t sm;
struct bfa_s *bfa; /* BFA module */
- struct bfa_fcpim_mod_s *fcpim; /* parent fcpim module */
+ struct bfa_fcpim_s *fcpim; /* parent fcpim module */
struct bfa_itnim_s *itnim; /* i-t-n nexus for this IO */
struct bfad_tskim_s *dtsk; /* driver task mgmt cmnd */
bfa_boolean_t notify; /* notify itnim on TM comp */
@@ -159,11 +251,29 @@ struct bfa_tskim_s {
};
/*
+ * itnim state machine event
+ */
+enum bfa_itnim_event {
+ BFA_ITNIM_SM_CREATE = 1, /* itnim is created */
+ BFA_ITNIM_SM_ONLINE = 2, /* itnim is online */
+ BFA_ITNIM_SM_OFFLINE = 3, /* itnim is offline */
+ BFA_ITNIM_SM_FWRSP = 4, /* firmware response */
+ BFA_ITNIM_SM_DELETE = 5, /* deleting an existing itnim */
+ BFA_ITNIM_SM_CLEANUP = 6, /* IO cleanup completion */
+ BFA_ITNIM_SM_SLER = 7, /* second level error recovery */
+ BFA_ITNIM_SM_HWFAIL = 8, /* IOC h/w failure event */
+ BFA_ITNIM_SM_QRESUME = 9, /* queue space available */
+};
+
+struct bfa_itnim_s;
+typedef void (*bfa_itnim_sm_t)(struct bfa_itnim_s *, enum bfa_itnim_event);
+
+/*
* BFA i-t-n (initiator mode)
*/
struct bfa_itnim_s {
struct list_head qe; /* queue element */
- bfa_sm_t sm; /* i-t-n im BFA state machine */
+ bfa_itnim_sm_t sm; /* i-t-n im BFA state machine */
struct bfa_s *bfa; /* bfa instance */
struct bfa_rport_s *rport; /* bfa rport */
void *ditn; /* driver i-t-n structure */
@@ -182,13 +292,13 @@ struct bfa_itnim_s {
struct bfa_wc_s wc; /* waiting counter */
struct bfa_timer_s timer; /* pending IO TOV */
struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */
- struct bfa_fcpim_mod_s *fcpim; /* fcpim module */
+ struct bfa_fcpim_s *fcpim; /* fcpim module */
struct bfa_itnim_iostats_s stats;
struct bfa_itnim_ioprofile_s ioprofile;
};
#define bfa_itnim_is_online(_itnim) ((_itnim)->is_online)
-#define BFA_FCPIM_MOD(_hal) (&(_hal)->modules.fcpim_mod)
+#define BFA_FCPIM(_hal) (&(_hal)->modules.fcp_mod.fcpim)
#define BFA_IOIM_TAG_2_ID(_iotag) ((_iotag) & BFA_IOIM_IOTAG_MASK)
#define BFA_IOIM_FROM_TAG(_fcpim, _iotag) \
(&fcpim->ioim_arr[(_iotag & BFA_IOIM_IOTAG_MASK)])
@@ -196,9 +306,9 @@ struct bfa_itnim_s {
(&fcpim->tskim_arr[_tmtag & (fcpim->num_tskim_reqs - 1)])
#define bfa_io_profile_start_time(_bfa) \
- (_bfa->modules.fcpim_mod.io_profile_start_time)
+ ((_bfa)->modules.fcp_mod.fcpim.io_profile_start_time)
#define bfa_fcpim_get_io_profile(_bfa) \
- (_bfa->modules.fcpim_mod.io_profile)
+ ((_bfa)->modules.fcp_mod.fcpim.io_profile)
#define bfa_ioim_update_iotag(__ioim) do { \
uint16_t k = (__ioim)->iotag >> BFA_IOIM_RETRY_TAG_OFFSET; \
k++; (__ioim)->iotag &= BFA_IOIM_IOTAG_MASK; \
@@ -217,8 +327,7 @@ bfa_ioim_maxretry_reached(struct bfa_ioim_s *ioim)
/*
* function prototypes
*/
-void bfa_ioim_attach(struct bfa_fcpim_mod_s *fcpim,
- struct bfa_meminfo_s *minfo);
+void bfa_ioim_attach(struct bfa_fcpim_s *fcpim);
void bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg);
void bfa_ioim_good_comp_isr(struct bfa_s *bfa,
struct bfi_msg_s *msg);
@@ -228,18 +337,14 @@ void bfa_ioim_cleanup_tm(struct bfa_ioim_s *ioim,
void bfa_ioim_iocdisable(struct bfa_ioim_s *ioim);
void bfa_ioim_tov(struct bfa_ioim_s *ioim);
-void bfa_tskim_attach(struct bfa_fcpim_mod_s *fcpim,
- struct bfa_meminfo_s *minfo);
+void bfa_tskim_attach(struct bfa_fcpim_s *fcpim);
void bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg);
-void bfa_tskim_iodone(struct bfa_tskim_s *tskim);
void bfa_tskim_iocdisable(struct bfa_tskim_s *tskim);
void bfa_tskim_cleanup(struct bfa_tskim_s *tskim);
+void bfa_tskim_res_recfg(struct bfa_s *bfa, u16 num_tskim_fw);
-void bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len);
-void bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim,
- struct bfa_meminfo_s *minfo);
-void bfa_itnim_detach(struct bfa_fcpim_mod_s *fcpim);
+void bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len);
+void bfa_itnim_attach(struct bfa_fcpim_s *fcpim);
void bfa_itnim_iocdisable(struct bfa_itnim_s *itnim);
void bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg);
void bfa_itnim_iodone(struct bfa_itnim_s *itnim);
@@ -252,13 +357,19 @@ bfa_boolean_t bfa_itnim_hold_io(struct bfa_itnim_s *itnim);
void bfa_fcpim_path_tov_set(struct bfa_s *bfa, u16 path_tov);
u16 bfa_fcpim_path_tov_get(struct bfa_s *bfa);
u16 bfa_fcpim_qdepth_get(struct bfa_s *bfa);
+bfa_status_t bfa_fcpim_port_iostats(struct bfa_s *bfa,
+ struct bfa_itnim_iostats_s *stats, u8 lp_tag);
+void bfa_fcpim_add_stats(struct bfa_itnim_iostats_s *fcpim_stats,
+ struct bfa_itnim_iostats_s *itnim_stats);
+bfa_status_t bfa_fcpim_profile_on(struct bfa_s *bfa, time64_t time);
+bfa_status_t bfa_fcpim_profile_off(struct bfa_s *bfa);
#define bfa_fcpim_ioredirect_enabled(__bfa) \
- (((struct bfa_fcpim_mod_s *)(BFA_FCPIM_MOD(__bfa)))->ioredirect)
+ (((struct bfa_fcpim_s *)(BFA_FCPIM(__bfa)))->ioredirect)
#define bfa_fcpim_get_next_reqq(__bfa, __qid) \
{ \
- struct bfa_fcpim_mod_s *__fcpim = BFA_FCPIM_MOD(__bfa); \
+ struct bfa_fcpim_s *__fcpim = BFA_FCPIM(__bfa); \
__fcpim->reqq++; \
__fcpim->reqq &= (BFI_IOC_MAX_CQS - 1); \
*(__qid) = __fcpim->reqq; \
@@ -352,4 +463,19 @@ void bfa_tskim_start(struct bfa_tskim_s *tskim,
void bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk,
enum bfi_tskim_status tsk_status);
+void bfa_fcpim_lunmask_rp_update(struct bfa_s *bfa, wwn_t lp_wwn,
+ wwn_t rp_wwn, u16 rp_tag, u8 lp_tag);
+bfa_status_t bfa_fcpim_lunmask_update(struct bfa_s *bfa, u32 on_off);
+bfa_status_t bfa_fcpim_lunmask_query(struct bfa_s *bfa, void *buf);
+bfa_status_t bfa_fcpim_lunmask_delete(struct bfa_s *bfa, u16 vf_id,
+ wwn_t *pwwn, wwn_t rpwwn, struct scsi_lun lun);
+bfa_status_t bfa_fcpim_lunmask_add(struct bfa_s *bfa, u16 vf_id,
+ wwn_t *pwwn, wwn_t rpwwn, struct scsi_lun lun);
+bfa_status_t bfa_fcpim_lunmask_clear(struct bfa_s *bfa);
+u16 bfa_fcpim_read_throttle(struct bfa_s *bfa);
+bfa_status_t bfa_fcpim_write_throttle(struct bfa_s *bfa, u16 value);
+bfa_status_t bfa_fcpim_throttle_set(struct bfa_s *bfa, u16 value);
+bfa_status_t bfa_fcpim_throttle_get(struct bfa_s *bfa, void *buf);
+u16 bfa_fcpim_get_throttle_cfg(struct bfa_s *bfa, u16 drv_cfg_param);
+
#endif /* __BFA_FCPIM_H__ */
diff --git a/drivers/scsi/bfa/bfa_fcs.c b/drivers/scsi/bfa/bfa_fcs.c
index 9b43ca4b6778..e52ce9b01f49 100644
--- a/drivers/scsi/bfa/bfa_fcs.c
+++ b/drivers/scsi/bfa/bfa_fcs.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -20,30 +13,13 @@
*/
#include "bfad_drv.h"
+#include "bfad_im.h"
#include "bfa_fcs.h"
#include "bfa_fcbuild.h"
BFA_TRC_FILE(FCS, FCS);
/*
- * FCS sub-modules
- */
-struct bfa_fcs_mod_s {
- void (*attach) (struct bfa_fcs_s *fcs);
- void (*modinit) (struct bfa_fcs_s *fcs);
- void (*modexit) (struct bfa_fcs_s *fcs);
-};
-
-#define BFA_FCS_MODULE(_mod) { _mod ## _modinit, _mod ## _modexit }
-
-static struct bfa_fcs_mod_s fcs_modules[] = {
- { bfa_fcs_port_attach, NULL, NULL },
- { bfa_fcs_uf_attach, NULL, NULL },
- { bfa_fcs_fabric_attach, bfa_fcs_fabric_modinit,
- bfa_fcs_fabric_modexit },
-};
-
-/*
* fcs_api BFA FCS API
*/
@@ -56,61 +32,65 @@ bfa_fcs_exit_comp(void *fcs_cbarg)
complete(&bfad->comp);
}
-
+/*
+ * fcs initialization, called once after bfa initialization is complete
+ */
+void
+bfa_fcs_init(struct bfa_fcs_s *fcs)
+{
+ bfa_sm_send_event(&fcs->fabric, BFA_FCS_FABRIC_SM_CREATE);
+ bfa_trc(fcs, 0);
+}
/*
* fcs_api BFA FCS API
*/
/*
- * fcs attach -- called once to initialize data structures at driver attach time
+ * FCS update cfg - reset the pwwn/nwwn of fabric base logical port
+ * with values learned during bfa_init firmware GETATTR REQ.
*/
void
-bfa_fcs_attach(struct bfa_fcs_s *fcs, struct bfa_s *bfa, struct bfad_s *bfad,
- bfa_boolean_t min_cfg)
+bfa_fcs_update_cfg(struct bfa_fcs_s *fcs)
{
- int i;
- struct bfa_fcs_mod_s *mod;
-
- fcs->bfa = bfa;
- fcs->bfad = bfad;
- fcs->min_cfg = min_cfg;
+ struct bfa_fcs_fabric_s *fabric = &fcs->fabric;
+ struct bfa_lport_cfg_s *port_cfg = &fabric->bport.port_cfg;
+ struct bfa_ioc_s *ioc = &fabric->fcs->bfa->ioc;
- bfa->fcs = BFA_TRUE;
- fcbuild_init();
+ port_cfg->nwwn = ioc->attr->nwwn;
+ port_cfg->pwwn = ioc->attr->pwwn;
+}
- for (i = 0; i < sizeof(fcs_modules) / sizeof(fcs_modules[0]); i++) {
- mod = &fcs_modules[i];
- if (mod->attach)
- mod->attach(fcs);
- }
+/*
+ * Stop FCS operations.
+ */
+void
+bfa_fcs_stop(struct bfa_fcs_s *fcs)
+{
+ bfa_wc_init(&fcs->wc, bfa_fcs_exit_comp, fcs);
+ bfa_wc_up(&fcs->wc);
+ bfa_fcs_fabric_modstop(fcs);
+ bfa_wc_wait(&fcs->wc);
}
/*
- * fcs initialization, called once after bfa initialization is complete
+ * fcs pbc vport initialization
*/
void
-bfa_fcs_init(struct bfa_fcs_s *fcs)
+bfa_fcs_pbc_vport_init(struct bfa_fcs_s *fcs)
{
- int i, npbc_vports;
- struct bfa_fcs_mod_s *mod;
+ int i, npbc_vports;
struct bfi_pbc_vport_s pbc_vports[BFI_PBC_MAX_VPORTS];
- for (i = 0; i < sizeof(fcs_modules) / sizeof(fcs_modules[0]); i++) {
- mod = &fcs_modules[i];
- if (mod->modinit)
- mod->modinit(fcs);
- }
/* Initialize pbc vports */
if (!fcs->min_cfg) {
npbc_vports =
- bfa_iocfc_get_pbc_vports(fcs->bfa, pbc_vports);
+ bfa_iocfc_get_pbc_vports(fcs->bfa, pbc_vports);
for (i = 0; i < npbc_vports; i++)
bfa_fcb_pbc_vport_create(fcs->bfa->bfad, pbc_vports[i]);
}
}
-
/*
* brief
* FCS driver details initialization.
@@ -128,6 +108,7 @@ bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs,
fcs->driver_info = *driver_info;
bfa_fcs_fabric_psymb_init(&fcs->fabric);
+ bfa_fcs_fabric_nsymb_init(&fcs->fabric);
}
/*
@@ -140,26 +121,14 @@ bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs,
void
bfa_fcs_exit(struct bfa_fcs_s *fcs)
{
- struct bfa_fcs_mod_s *mod;
- int nmods, i;
-
bfa_wc_init(&fcs->wc, bfa_fcs_exit_comp, fcs);
-
- nmods = sizeof(fcs_modules) / sizeof(fcs_modules[0]);
-
- for (i = 0; i < nmods; i++) {
-
- mod = &fcs_modules[i];
- if (mod->modexit) {
- bfa_wc_up(&fcs->wc);
- mod->modexit(fcs);
- }
- }
-
+ bfa_wc_up(&fcs->wc);
+ bfa_trc(fcs, 0);
+ bfa_lps_delete(fcs->fabric.lps);
+ bfa_sm_send_event(&fcs->fabric, BFA_FCS_FABRIC_SM_DELETE);
bfa_wc_wait(&fcs->wc);
}
-
/*
* Fabric module implementation.
*/
@@ -168,11 +137,14 @@ bfa_fcs_exit(struct bfa_fcs_s *fcs)
#define BFA_FCS_FABRIC_CLEANUP_DELAY (10000) /* Milliseconds */
#define bfa_fcs_fabric_set_opertype(__fabric) do { \
- if (bfa_fcport_get_topology((__fabric)->fcs->bfa) \
- == BFA_PORT_TOPOLOGY_P2P) \
+ if (bfa_fcport_get_topology((__fabric)->fcs->bfa) \
+ == BFA_PORT_TOPOLOGY_P2P) { \
+ if (fabric->fab_type == BFA_FCS_FABRIC_SWITCHED) \
(__fabric)->oper_type = BFA_PORT_TYPE_NPORT; \
else \
- (__fabric)->oper_type = BFA_PORT_TYPE_NLPORT; \
+ (__fabric)->oper_type = BFA_PORT_TYPE_P2P; \
+ } else \
+ (__fabric)->oper_type = BFA_PORT_TYPE_NLPORT; \
} while (0)
/*
@@ -185,6 +157,8 @@ static void bfa_fcs_fabric_notify_offline(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_delay(void *cbarg);
static void bfa_fcs_fabric_delete(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_delete_comp(void *cbarg);
+static void bfa_fcs_fabric_stop(struct bfa_fcs_fabric_s *fabric);
+static void bfa_fcs_fabric_stop_comp(void *cbarg);
static void bfa_fcs_fabric_process_uf(struct bfa_fcs_fabric_s *fabric,
struct fchs_s *fchs, u16 len);
static void bfa_fcs_fabric_process_flogi(struct bfa_fcs_fabric_s *fabric,
@@ -219,6 +193,10 @@ static void bfa_fcs_fabric_sm_isolated(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
+static void bfa_fcs_fabric_sm_stopping(struct bfa_fcs_fabric_s *fabric,
+ enum bfa_fcs_fabric_event event);
+static void bfa_fcs_fabric_sm_cleanup(struct bfa_fcs_fabric_s *fabric,
+ enum bfa_fcs_fabric_event event);
/*
* Beginning state before fabric creation.
*/
@@ -252,16 +230,30 @@ static void
bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
+ struct bfa_s *bfa = fabric->fcs->bfa;
+
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_START:
- if (bfa_fcport_is_linkup(fabric->fcs->bfa)) {
+ if (!bfa_fcport_is_linkup(fabric->fcs->bfa)) {
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
+ break;
+ }
+ if (bfa_fcport_get_topology(bfa) ==
+ BFA_PORT_TOPOLOGY_LOOP) {
+ fabric->fab_type = BFA_FCS_FABRIC_LOOP;
+ fabric->bport.pid = bfa_fcport_get_myalpa(bfa);
+ fabric->bport.pid = bfa_hton3b(fabric->bport.pid);
+ bfa_sm_set_state(fabric,
+ bfa_fcs_fabric_sm_online);
+ bfa_fcs_fabric_set_opertype(fabric);
+ bfa_fcs_lport_online(&fabric->bport);
+ } else {
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
bfa_fcs_fabric_login(fabric);
- } else
- bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
+ }
break;
case BFA_FCS_FABRIC_SM_LINK_UP:
@@ -269,8 +261,8 @@ bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric,
break;
case BFA_FCS_FABRIC_SM_DELETE:
- bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit);
- bfa_wc_down(&fabric->fcs->wc);
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
+ bfa_fcs_fabric_delete(fabric);
break;
default:
@@ -286,16 +278,28 @@ static void
bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
+ struct bfa_s *bfa = fabric->fcs->bfa;
+
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_LINK_UP:
- bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
- bfa_fcs_fabric_login(fabric);
+ if (bfa_fcport_get_topology(bfa) != BFA_PORT_TOPOLOGY_LOOP) {
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
+ bfa_fcs_fabric_login(fabric);
+ break;
+ }
+ fabric->fab_type = BFA_FCS_FABRIC_LOOP;
+ fabric->bport.pid = bfa_fcport_get_myalpa(bfa);
+ fabric->bport.pid = bfa_hton3b(fabric->bport.pid);
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online);
+ bfa_fcs_fabric_set_opertype(fabric);
+ bfa_fcs_lport_online(&fabric->bport);
break;
case BFA_FCS_FABRIC_SM_RETRY_OP:
+ case BFA_FCS_FABRIC_SM_LOOPBACK:
break;
case BFA_FCS_FABRIC_SM_DELETE:
@@ -303,6 +307,11 @@ bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric,
bfa_fcs_fabric_delete(fabric);
break;
+ case BFA_FCS_FABRIC_SM_STOP:
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup);
+ bfa_fcs_fabric_stop(fabric);
+ break;
+
default:
bfa_sm_fault(fabric->fcs, event);
}
@@ -521,6 +530,9 @@ bfa_fcs_fabric_sm_nofabric(struct bfa_fcs_fabric_s *fabric,
fabric->bb_credit);
break;
+ case BFA_FCS_FABRIC_SM_RETRY_OP:
+ break;
+
default:
bfa_sm_fault(fabric->fcs, event);
}
@@ -533,14 +545,20 @@ void
bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
+ struct bfa_s *bfa = fabric->fcs->bfa;
+
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
- bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
- bfa_fcs_fabric_notify_offline(fabric);
+ if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) {
+ bfa_fcs_lport_offline(&fabric->bport);
+ } else {
+ bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
+ bfa_fcs_fabric_notify_offline(fabric);
+ }
break;
case BFA_FCS_FABRIC_SM_DELETE:
@@ -548,6 +566,11 @@ bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric,
bfa_fcs_fabric_delete(fabric);
break;
+ case BFA_FCS_FABRIC_SM_STOP:
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_stopping);
+ bfa_fcs_fabric_stop(fabric);
+ break;
+
case BFA_FCS_FABRIC_SM_AUTH_FAILED:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed);
bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
@@ -645,7 +668,71 @@ bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric,
}
}
+/*
+ * Fabric is being stopped, awaiting vport stop completions.
+ */
+static void
+bfa_fcs_fabric_sm_stopping(struct bfa_fcs_fabric_s *fabric,
+ enum bfa_fcs_fabric_event event)
+{
+ struct bfa_s *bfa = fabric->fcs->bfa;
+
+ bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
+ bfa_trc(fabric->fcs, event);
+
+ switch (event) {
+ case BFA_FCS_FABRIC_SM_STOPCOMP:
+ if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) {
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created);
+ } else {
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup);
+ bfa_sm_send_event(fabric->lps, BFA_LPS_SM_LOGOUT);
+ }
+ break;
+
+ case BFA_FCS_FABRIC_SM_LINK_UP:
+ break;
+
+ case BFA_FCS_FABRIC_SM_LINK_DOWN:
+ if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP)
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created);
+ else
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup);
+ break;
+ default:
+ bfa_sm_fault(fabric->fcs, event);
+ }
+}
+
+/*
+ * Fabric is being stopped, cleanup without FLOGO
+ */
+static void
+bfa_fcs_fabric_sm_cleanup(struct bfa_fcs_fabric_s *fabric,
+ enum bfa_fcs_fabric_event event)
+{
+ bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
+ bfa_trc(fabric->fcs, event);
+
+ switch (event) {
+ case BFA_FCS_FABRIC_SM_STOPCOMP:
+ case BFA_FCS_FABRIC_SM_LOGOCOMP:
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created);
+ bfa_wc_down(&(fabric->fcs)->wc);
+ break;
+
+ case BFA_FCS_FABRIC_SM_LINK_DOWN:
+ /*
+ * Ignore - can get this event if we get notified about IOC down
+ * before the fabric completion callbk is done.
+ */
+ break;
+
+ default:
+ bfa_sm_fault(fabric->fcs, event);
+ }
+}
/*
* fcs_fabric_private fabric private functions
@@ -674,23 +761,23 @@ bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric)
bfa_ioc_get_adapter_model(&fabric->fcs->bfa->ioc, model);
/* Model name/number */
- strncpy((char *)&port_cfg->sym_name, model,
- BFA_FCS_PORT_SYMBNAME_MODEL_SZ);
- strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
- sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
+ strscpy(port_cfg->sym_name.symname, model,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->sym_name.symname, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
+ BFA_SYMNAME_MAXLEN);
/* Driver Version */
- strncat((char *)&port_cfg->sym_name, (char *)driver_info->version,
- BFA_FCS_PORT_SYMBNAME_VERSION_SZ);
- strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
- sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
+ strlcat(port_cfg->sym_name.symname, driver_info->version,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->sym_name.symname, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
+ BFA_SYMNAME_MAXLEN);
/* Host machine name */
- strncat((char *)&port_cfg->sym_name,
- (char *)driver_info->host_machine_name,
- BFA_FCS_PORT_SYMBNAME_MACHINENAME_SZ);
- strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
- sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
+ strlcat(port_cfg->sym_name.symname,
+ driver_info->host_machine_name,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->sym_name.symname, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
+ BFA_SYMNAME_MAXLEN);
/*
* Host OS Info :
@@ -698,24 +785,24 @@ bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric)
* OS name string and instead copy the entire OS info string (64 bytes).
*/
if (driver_info->host_os_patch[0] == '\0') {
- strncat((char *)&port_cfg->sym_name,
- (char *)driver_info->host_os_name,
- BFA_FCS_OS_STR_LEN);
- strncat((char *)&port_cfg->sym_name,
+ strlcat(port_cfg->sym_name.symname,
+ driver_info->host_os_name,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->sym_name.symname,
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
- sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
+ BFA_SYMNAME_MAXLEN);
} else {
- strncat((char *)&port_cfg->sym_name,
- (char *)driver_info->host_os_name,
- BFA_FCS_PORT_SYMBNAME_OSINFO_SZ);
- strncat((char *)&port_cfg->sym_name,
+ strlcat(port_cfg->sym_name.symname,
+ driver_info->host_os_name,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->sym_name.symname,
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
- sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
+ BFA_SYMNAME_MAXLEN);
/* Append host OS Patch Info */
- strncat((char *)&port_cfg->sym_name,
- (char *)driver_info->host_os_patch,
- BFA_FCS_PORT_SYMBNAME_OSPATCH_SZ);
+ strlcat(port_cfg->sym_name.symname,
+ driver_info->host_os_patch,
+ BFA_SYMNAME_MAXLEN);
}
/* null terminate */
@@ -723,6 +810,44 @@ bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric)
}
/*
+ * Node Symbolic Name Creation for base port and all vports
+ */
+void
+bfa_fcs_fabric_nsymb_init(struct bfa_fcs_fabric_s *fabric)
+{
+ struct bfa_lport_cfg_s *port_cfg = &fabric->bport.port_cfg;
+ char model[BFA_ADAPTER_MODEL_NAME_LEN] = {0};
+ struct bfa_fcs_driver_info_s *driver_info = &fabric->fcs->driver_info;
+
+ bfa_ioc_get_adapter_model(&fabric->fcs->bfa->ioc, model);
+
+ /* Model name/number */
+ strscpy(port_cfg->node_sym_name.symname, model,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->node_sym_name.symname,
+ BFA_FCS_PORT_SYMBNAME_SEPARATOR,
+ BFA_SYMNAME_MAXLEN);
+
+ /* Driver Version */
+ strlcat(port_cfg->node_sym_name.symname, (char *)driver_info->version,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->node_sym_name.symname,
+ BFA_FCS_PORT_SYMBNAME_SEPARATOR,
+ BFA_SYMNAME_MAXLEN);
+
+ /* Host machine name */
+ strlcat(port_cfg->node_sym_name.symname,
+ driver_info->host_machine_name,
+ BFA_SYMNAME_MAXLEN);
+ strlcat(port_cfg->node_sym_name.symname,
+ BFA_FCS_PORT_SYMBNAME_SEPARATOR,
+ BFA_SYMNAME_MAXLEN);
+
+ /* null terminate */
+ port_cfg->node_sym_name.symname[BFA_SYMNAME_MAXLEN - 1] = 0;
+}
+
+/*
* bfa lps login completion callback
*/
void
@@ -793,6 +918,7 @@ bfa_cb_lps_flogi_comp(void *bfad, void *uarg, bfa_status_t status)
*/
fabric->bport.port_topo.pn2n.rem_port_wwn =
fabric->lps->pr_pwwn;
+ fabric->fab_type = BFA_FCS_FABRIC_N2N;
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_NO_FABRIC);
}
@@ -810,8 +936,6 @@ bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric)
struct bfa_lport_cfg_s *pcfg = &fabric->bport.port_cfg;
u8 alpa = 0;
- if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP)
- alpa = bfa_fcport_get_myalpa(bfa);
bfa_lps_flogi(fabric->lps, fabric, alpa, bfa_fcport_get_maxfrsize(bfa),
pcfg->pwwn, pcfg->nwwn, fabric->auth_reqd);
@@ -873,6 +997,28 @@ bfa_fcs_fabric_delay(void *cbarg)
}
/*
+ * Stop all vports and wait for vport stop completions.
+ */
+static void
+bfa_fcs_fabric_stop(struct bfa_fcs_fabric_s *fabric)
+{
+ struct bfa_fcs_vport_s *vport;
+ struct list_head *qe, *qen;
+
+ bfa_wc_init(&fabric->stop_wc, bfa_fcs_fabric_stop_comp, fabric);
+
+ list_for_each_safe(qe, qen, &fabric->vport_q) {
+ vport = (struct bfa_fcs_vport_s *) qe;
+ bfa_wc_up(&fabric->stop_wc);
+ bfa_fcs_vport_fcs_stop(vport);
+ }
+
+ bfa_wc_up(&fabric->stop_wc);
+ bfa_fcs_lport_stop(&fabric->bport);
+ bfa_wc_wait(&fabric->stop_wc);
+}
+
+/*
* Delete all vports and wait for vport delete completions.
*/
static void
@@ -898,64 +1044,29 @@ bfa_fcs_fabric_delete_comp(void *cbarg)
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELCOMP);
}
-/*
- * fcs_fabric_public fabric public functions
- */
-
-/*
- * Attach time initialization.
- */
-void
-bfa_fcs_fabric_attach(struct bfa_fcs_s *fcs)
+static void
+bfa_fcs_fabric_stop_comp(void *cbarg)
{
- struct bfa_fcs_fabric_s *fabric;
-
- fabric = &fcs->fabric;
- memset(fabric, 0, sizeof(struct bfa_fcs_fabric_s));
-
- /*
- * Initialize base fabric.
- */
- fabric->fcs = fcs;
- INIT_LIST_HEAD(&fabric->vport_q);
- INIT_LIST_HEAD(&fabric->vf_q);
- fabric->lps = bfa_lps_alloc(fcs->bfa);
- WARN_ON(!fabric->lps);
-
- /*
- * Initialize fabric delete completion handler. Fabric deletion is
- * complete when the last vport delete is complete.
- */
- bfa_wc_init(&fabric->wc, bfa_fcs_fabric_delete_comp, fabric);
- bfa_wc_up(&fabric->wc); /* For the base port */
+ struct bfa_fcs_fabric_s *fabric = cbarg;
- bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit);
- bfa_fcs_lport_attach(&fabric->bport, fabric->fcs, FC_VF_ID_NULL, NULL);
+ bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_STOPCOMP);
}
-void
-bfa_fcs_fabric_modinit(struct bfa_fcs_s *fcs)
-{
- bfa_sm_send_event(&fcs->fabric, BFA_FCS_FABRIC_SM_CREATE);
- bfa_trc(fcs, 0);
-}
+/*
+ * fcs_fabric_public fabric public functions
+ */
/*
- * Module cleanup
+ * Fabric module stop -- stop FCS actions
*/
void
-bfa_fcs_fabric_modexit(struct bfa_fcs_s *fcs)
+bfa_fcs_fabric_modstop(struct bfa_fcs_s *fcs)
{
struct bfa_fcs_fabric_s *fabric;
bfa_trc(fcs, 0);
-
- /*
- * Cleanup base fabric.
- */
fabric = &fcs->fabric;
- bfa_lps_delete(fabric->lps);
- bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELETE);
+ bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_STOP);
}
/*
@@ -1138,8 +1249,11 @@ bfa_fcs_fabric_uf_recv(struct bfa_fcs_fabric_s *fabric, struct fchs_s *fchs,
return;
}
}
- bfa_trc(fabric->fcs, els_cmd->els_code);
- bfa_fcs_lport_uf_recv(&fabric->bport, fchs, len);
+
+ if (!bfa_fcs_fabric_is_switched(fabric))
+ bfa_fcs_lport_uf_recv(&fabric->bport, fchs, len);
+
+ bfa_trc(fabric->fcs, fchs->type);
}
/*
@@ -1212,7 +1326,7 @@ bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric)
u16 reqlen;
struct fchs_s fchs;
- fcxp = bfa_fcs_fcxp_alloc(fabric->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(fabric->fcs, BFA_FALSE);
/*
* Do not expect this failure -- expect remote node to retry
*/
@@ -1224,9 +1338,9 @@ bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric)
n2n_port->reply_oxid, pcfg->pwwn,
pcfg->nwwn,
bfa_fcport_get_maxfrsize(bfa),
- bfa_fcport_get_rx_bbcredit(bfa));
+ bfa_fcport_get_rx_bbcredit(bfa), 0);
- bfa_fcxp_send(fcxp, NULL, fabric->vf_id, fabric->lps->lp_tag,
+ bfa_fcxp_send(fcxp, NULL, fabric->vf_id, fabric->lps->bfa_tag,
BFA_FALSE, FC_CLASS_3,
reqlen, &fchs, bfa_fcs_fabric_flogiacc_comp, fabric,
FC_MAX_PDUSZ, 0);
@@ -1245,6 +1359,29 @@ bfa_fcs_fabric_flogiacc_comp(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
bfa_trc(fabric->fcs, status);
}
+
+/*
+ * Send AEN notification
+ */
+static void
+bfa_fcs_fabric_aen_post(struct bfa_fcs_lport_s *port,
+ enum bfa_port_aen_event event)
+{
+ struct bfad_s *bfad = (struct bfad_s *)port->fabric->fcs->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ aen_entry->aen_data.port.pwwn = bfa_fcs_lport_get_pwwn(port);
+ aen_entry->aen_data.port.fwwn = bfa_fcs_lport_get_fabric_name(port);
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++port->fcs->fcs_aen_seq,
+ BFA_AEN_CAT_PORT, event);
+}
+
/*
*
* @param[in] fabric - fabric
@@ -1276,16 +1413,25 @@ bfa_fcs_fabric_set_fabric_name(struct bfa_fcs_fabric_s *fabric,
BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
"Base port WWN = %s Fabric WWN = %s\n",
pwwn_ptr, fwwn_ptr);
+ bfa_fcs_fabric_aen_post(&fabric->bport,
+ BFA_PORT_AEN_FABRIC_NAME_CHANGE);
}
}
+void
+bfa_cb_lps_flogo_comp(void *bfad, void *uarg)
+{
+ struct bfa_fcs_fabric_s *fabric = uarg;
+ bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LOGOCOMP);
+}
+
/*
* Returns FCS vf structure for a given vf_id.
*
* param[in] vf_id - VF_ID
*
* return
- * If lookup succeeds, retuns fcs vf object, otherwise returns NULL
+ * If lookup succeeds, returns fcs vf object, otherwise returns NULL
*/
bfa_fcs_vf_t *
bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id)
@@ -1298,6 +1444,45 @@ bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id)
}
/*
+ * Return the list of local logical ports present in the given VF.
+ *
+ * @param[in] vf vf for which logical ports are returned
+ * @param[out] lpwwn returned logical port wwn list
+ * @param[in,out] nlports in:size of lpwwn list;
+ * out:total elements present,
+ * actual elements returned is limited by the size
+ */
+void
+bfa_fcs_vf_get_ports(bfa_fcs_vf_t *vf, wwn_t lpwwn[], int *nlports)
+{
+ struct list_head *qe;
+ struct bfa_fcs_vport_s *vport;
+ int i = 0;
+ struct bfa_fcs_s *fcs;
+
+ if (vf == NULL || lpwwn == NULL || *nlports == 0)
+ return;
+
+ fcs = vf->fcs;
+
+ bfa_trc(fcs, vf->vf_id);
+ bfa_trc(fcs, (uint32_t) *nlports);
+
+ lpwwn[i++] = vf->bport.port_cfg.pwwn;
+
+ list_for_each(qe, &vf->vport_q) {
+ if (i >= *nlports)
+ break;
+
+ vport = (struct bfa_fcs_vport_s *) qe;
+ lpwwn[i++] = vport->lport.port_cfg.pwwn;
+ }
+
+ bfa_trc(fcs, i);
+ *nlports = i;
+}
+
+/*
* BFA FCS PPORT ( physical port)
*/
static void
@@ -1321,12 +1506,6 @@ bfa_fcs_port_event_handler(void *cbarg, enum bfa_port_linkstate event)
}
}
-void
-bfa_fcs_port_attach(struct bfa_fcs_s *fcs)
-{
- bfa_fcport_event_register(fcs->bfa, bfa_fcs_port_event_handler, fcs);
-}
-
/*
* BFA FCS UF ( Unsolicited Frames)
*/
@@ -1394,8 +1573,44 @@ bfa_fcs_uf_recv(void *cbarg, struct bfa_uf_s *uf)
bfa_uf_free(uf);
}
+/*
+ * fcs attach -- called once to initialize data structures at driver attach time
+ */
void
-bfa_fcs_uf_attach(struct bfa_fcs_s *fcs)
+bfa_fcs_attach(struct bfa_fcs_s *fcs, struct bfa_s *bfa, struct bfad_s *bfad,
+ bfa_boolean_t min_cfg)
{
+ struct bfa_fcs_fabric_s *fabric = &fcs->fabric;
+
+ fcs->bfa = bfa;
+ fcs->bfad = bfad;
+ fcs->min_cfg = min_cfg;
+ fcs->num_rport_logins = 0;
+
+ bfa->fcs = BFA_TRUE;
+ fcbuild_init();
+
+ bfa_fcport_event_register(fcs->bfa, bfa_fcs_port_event_handler, fcs);
bfa_uf_recv_register(fcs->bfa, bfa_fcs_uf_recv, fcs);
+
+ memset(fabric, 0, sizeof(struct bfa_fcs_fabric_s));
+
+ /*
+ * Initialize base fabric.
+ */
+ fabric->fcs = fcs;
+ INIT_LIST_HEAD(&fabric->vport_q);
+ INIT_LIST_HEAD(&fabric->vf_q);
+ fabric->lps = bfa_lps_alloc(fcs->bfa);
+ WARN_ON(!fabric->lps);
+
+ /*
+ * Initialize fabric delete completion handler. Fabric deletion is
+ * complete when the last vport delete is complete.
+ */
+ bfa_wc_init(&fabric->wc, bfa_fcs_fabric_delete_comp, fabric);
+ bfa_wc_up(&fabric->wc); /* For the base port */
+
+ bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit);
+ bfa_fcs_lport_attach(&fabric->bport, fabric->fcs, FC_VF_ID_NULL, NULL);
}
diff --git a/drivers/scsi/bfa/bfa_fcs.h b/drivers/scsi/bfa/bfa_fcs.h
index 61cdce4bd913..19903539a08d 100644
--- a/drivers/scsi/bfa/bfa_fcs.h
+++ b/drivers/scsi/bfa/bfa_fcs.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_FCS_H__
@@ -27,22 +20,6 @@
#define BFA_FCS_OS_STR_LEN 64
/*
- * lps_pvt BFA LPS private functions
- */
-
-enum bfa_lps_event {
- BFA_LPS_SM_LOGIN = 1, /* login request from user */
- BFA_LPS_SM_LOGOUT = 2, /* logout request from user */
- BFA_LPS_SM_FWRSP = 3, /* f/w response to login/logout */
- BFA_LPS_SM_RESUME = 4, /* space present in reqq queue */
- BFA_LPS_SM_DELETE = 5, /* lps delete from user */
- BFA_LPS_SM_OFFLINE = 6, /* Link is offline */
- BFA_LPS_SM_RX_CVL = 7, /* Rx clear virtual link */
- BFA_LPS_SM_SET_N2N_PID = 8, /* Set assigned PID for n2n */
-};
-
-
-/*
* !!! Only append to the enums defined here to avoid any versioning
* !!! needed between trace utility and driver version
*/
@@ -62,21 +39,59 @@ struct bfa_fcs_s;
#define N2N_LOCAL_PID 0x010000
#define N2N_REMOTE_PID 0x020000
#define BFA_FCS_RETRY_TIMEOUT 2000
+#define BFA_FCS_MAX_NS_RETRIES 5
#define BFA_FCS_PID_IS_WKA(pid) ((bfa_ntoh3b(pid) > 0xFFF000) ? 1 : 0)
+#define BFA_FCS_MAX_RPORT_LOGINS 1024
+/*
+ * VPort NS State Machine events
+ */
+enum vport_ns_event {
+ NSSM_EVENT_PORT_ONLINE = 1,
+ NSSM_EVENT_PORT_OFFLINE = 2,
+ NSSM_EVENT_PLOGI_SENT = 3,
+ NSSM_EVENT_RSP_OK = 4,
+ NSSM_EVENT_RSP_ERROR = 5,
+ NSSM_EVENT_TIMEOUT = 6,
+ NSSM_EVENT_NS_QUERY = 7,
+ NSSM_EVENT_RSPNID_SENT = 8,
+ NSSM_EVENT_RFTID_SENT = 9,
+ NSSM_EVENT_RFFID_SENT = 10,
+ NSSM_EVENT_GIDFT_SENT = 11,
+ NSSM_EVENT_RNNID_SENT = 12,
+ NSSM_EVENT_RSNN_NN_SENT = 13,
+};
+struct bfa_fcs_lport_ns_s;
+typedef void (*bfa_fcs_lport_ns_sm_t)(struct bfa_fcs_lport_ns_s *fsm, enum vport_ns_event);
struct bfa_fcs_lport_ns_s {
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_lport_ns_sm_t sm; /* state machine */
struct bfa_timer_s timer;
struct bfa_fcs_lport_s *port; /* parent port */
struct bfa_fcxp_s *fcxp;
struct bfa_fcxp_wqe_s fcxp_wqe;
+ u8 num_rnnid_retries;
+ u8 num_rsnn_nn_retries;
+};
+
+/*
+ * VPort SCN State Machine events
+ */
+enum port_scn_event {
+ SCNSM_EVENT_PORT_ONLINE = 1,
+ SCNSM_EVENT_PORT_OFFLINE = 2,
+ SCNSM_EVENT_RSP_OK = 3,
+ SCNSM_EVENT_RSP_ERROR = 4,
+ SCNSM_EVENT_TIMEOUT = 5,
+ SCNSM_EVENT_SCR_SENT = 6,
};
+struct bfa_fcs_lport_scn_s;
+typedef void (*bfa_fcs_lport_scn_sm_t)(struct bfa_fcs_lport_scn_s *fsm, enum port_scn_event);
struct bfa_fcs_lport_scn_s {
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_lport_scn_sm_t sm; /* state machine */
struct bfa_timer_s timer;
struct bfa_fcs_lport_s *port; /* parent port */
struct bfa_fcxp_s *fcxp;
@@ -84,8 +99,25 @@ struct bfa_fcs_lport_scn_s {
};
+/*
+ * FDMI State Machine events
+ */
+enum port_fdmi_event {
+ FDMISM_EVENT_PORT_ONLINE = 1,
+ FDMISM_EVENT_PORT_OFFLINE = 2,
+ FDMISM_EVENT_RSP_OK = 4,
+ FDMISM_EVENT_RSP_ERROR = 5,
+ FDMISM_EVENT_TIMEOUT = 6,
+ FDMISM_EVENT_RHBA_SENT = 7,
+ FDMISM_EVENT_RPRT_SENT = 8,
+ FDMISM_EVENT_RPA_SENT = 9,
+};
+
+struct bfa_fcs_lport_fdmi_s;
+typedef void (*bfa_fcs_lport_fdmi_sm_t)(struct bfa_fcs_lport_fdmi_s *fsm, enum port_fdmi_event);
+
struct bfa_fcs_lport_fdmi_s {
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_lport_fdmi_sm_t sm; /* state machine */
struct bfa_timer_s timer;
struct bfa_fcs_lport_ms_s *ms; /* parent ms */
struct bfa_fcxp_s *fcxp;
@@ -93,10 +125,24 @@ struct bfa_fcs_lport_fdmi_s {
u8 retry_cnt; /* retry count */
u8 rsvd[3];
};
+/*
+ * MS State Machine events
+ */
+enum port_ms_event {
+ MSSM_EVENT_PORT_ONLINE = 1,
+ MSSM_EVENT_PORT_OFFLINE = 2,
+ MSSM_EVENT_RSP_OK = 3,
+ MSSM_EVENT_RSP_ERROR = 4,
+ MSSM_EVENT_TIMEOUT = 5,
+ MSSM_EVENT_FCXP_SENT = 6,
+ MSSM_EVENT_PORT_FABRIC_RSCN = 7
+};
+struct bfa_fcs_lport_ms_s;
+typedef void (*bfa_fcs_lport_ms_sm_t)(struct bfa_fcs_lport_ms_s *fsm, enum port_ms_event);
struct bfa_fcs_lport_ms_s {
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_lport_ms_sm_t sm; /* state machine */
struct bfa_timer_s timer;
struct bfa_fcs_lport_s *port; /* parent port */
struct bfa_fcxp_s *fcxp;
@@ -116,9 +162,9 @@ struct bfa_fcs_lport_fab_s {
#define MAX_ALPA_COUNT 127
struct bfa_fcs_lport_loop_s {
- u8 num_alpa; /* Num of ALPA entries in the map */
- u8 alpa_pos_map[MAX_ALPA_COUNT]; /* ALPA Positional
- *Map */
+ u8 num_alpa; /* Num of ALPA entries in the map */
+ u8 alpabm_valid; /* alpa bitmap valid or not (1 or 0) */
+ u8 alpa_pos_map[MAX_ALPA_COUNT]; /* ALPA Positional Map */
struct bfa_fcs_lport_s *port; /* parent port */
};
@@ -136,10 +182,25 @@ union bfa_fcs_lport_topo_u {
struct bfa_fcs_lport_n2n_s pn2n;
};
+/*
+ * fcs_port_sm FCS logical port state machine
+ */
+
+enum bfa_fcs_lport_event {
+ BFA_FCS_PORT_SM_CREATE = 1,
+ BFA_FCS_PORT_SM_ONLINE = 2,
+ BFA_FCS_PORT_SM_OFFLINE = 3,
+ BFA_FCS_PORT_SM_DELETE = 4,
+ BFA_FCS_PORT_SM_DELRPORT = 5,
+ BFA_FCS_PORT_SM_STOP = 6,
+};
+
+struct bfa_fcs_lport_s;
+typedef void (*bfa_fcs_lport_sm_t)(struct bfa_fcs_lport_s *fsm, enum bfa_fcs_lport_event);
struct bfa_fcs_lport_s {
struct list_head qe; /* used by port/vport */
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_lport_sm_t sm; /* state machine */
struct bfa_fcs_fabric_s *fabric; /* parent fabric */
struct bfa_lport_cfg_s port_cfg; /* port configuration */
struct bfa_timer_s link_timer; /* timer for link offline */
@@ -173,12 +234,40 @@ enum bfa_fcs_fabric_type {
BFA_FCS_FABRIC_UNKNOWN = 0,
BFA_FCS_FABRIC_SWITCHED = 1,
BFA_FCS_FABRIC_N2N = 2,
+ BFA_FCS_FABRIC_LOOP = 3,
+};
+
+/*
+ * Fabric state machine events
+ */
+enum bfa_fcs_fabric_event {
+ BFA_FCS_FABRIC_SM_CREATE = 1, /* create from driver */
+ BFA_FCS_FABRIC_SM_DELETE = 2, /* delete from driver */
+ BFA_FCS_FABRIC_SM_LINK_DOWN = 3, /* link down from port */
+ BFA_FCS_FABRIC_SM_LINK_UP = 4, /* link up from port */
+ BFA_FCS_FABRIC_SM_CONT_OP = 5, /* flogi/auth continue op */
+ BFA_FCS_FABRIC_SM_RETRY_OP = 6, /* flogi/auth retry op */
+ BFA_FCS_FABRIC_SM_NO_FABRIC = 7, /* from flogi/auth */
+ BFA_FCS_FABRIC_SM_PERF_EVFP = 8, /* from flogi/auth */
+ BFA_FCS_FABRIC_SM_ISOLATE = 9, /* from EVFP processing */
+ BFA_FCS_FABRIC_SM_NO_TAGGING = 10, /* no VFT tagging from EVFP */
+ BFA_FCS_FABRIC_SM_DELAYED = 11, /* timeout delay event */
+ BFA_FCS_FABRIC_SM_AUTH_FAILED = 12, /* auth failed */
+ BFA_FCS_FABRIC_SM_AUTH_SUCCESS = 13, /* auth successful */
+ BFA_FCS_FABRIC_SM_DELCOMP = 14, /* all vports deleted event */
+ BFA_FCS_FABRIC_SM_LOOPBACK = 15, /* Received our own FLOGI */
+ BFA_FCS_FABRIC_SM_START = 16, /* from driver */
+ BFA_FCS_FABRIC_SM_STOP = 17, /* Stop from driver */
+ BFA_FCS_FABRIC_SM_STOPCOMP = 18, /* Stop completion */
+ BFA_FCS_FABRIC_SM_LOGOCOMP = 19, /* FLOGO completion */
};
+struct bfa_fcs_fabric_s;
+typedef void (*bfa_fcs_fabric_sm_t)(struct bfa_fcs_fabric_s *fsm, enum bfa_fcs_fabric_event);
struct bfa_fcs_fabric_s {
struct list_head qe; /* queue element */
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_fabric_sm_t sm; /* state machine */
struct bfa_fcs_s *fcs; /* FCS instance */
struct bfa_fcs_lport_s bport; /* base logical port */
enum bfa_fcs_fabric_type fab_type; /* fabric type */
@@ -205,6 +294,7 @@ struct bfa_fcs_fabric_s {
struct bfa_lps_s *lps; /* lport login services */
u8 fabric_ip_addr[BFA_FCS_FABRIC_IPADDR_SZ];
/* attached fabric's ip addr */
+ struct bfa_wc_s stop_wc; /* wait counter for stop */
};
#define bfa_fcs_fabric_npiv_capable(__f) ((__f)->is_npiv)
@@ -220,9 +310,6 @@ struct bfa_vf_event_s {
u32 undefined;
};
-struct bfa_fcs_s;
-struct bfa_fcs_fabric_s;
-
/*
* @todo : need to move to a global config file.
* Maximum Rports supported per port (physical/logical).
@@ -239,19 +326,19 @@ struct bfa_fcs_fabric_s;
* Symbolic Name.
*
* Physical Port's symbolic name Format : (Total 128 bytes)
- * Adapter Model number/name : 12 bytes
+ * Adapter Model number/name : 16 bytes
* Driver Version : 10 bytes
* Host Machine Name : 30 bytes
- * Host OS Info : 48 bytes
+ * Host OS Info : 44 bytes
* Host OS PATCH Info : 16 bytes
* ( remaining 12 bytes reserved to be used for separator)
*/
#define BFA_FCS_PORT_SYMBNAME_SEPARATOR " | "
-#define BFA_FCS_PORT_SYMBNAME_MODEL_SZ 12
+#define BFA_FCS_PORT_SYMBNAME_MODEL_SZ 16
#define BFA_FCS_PORT_SYMBNAME_VERSION_SZ 10
#define BFA_FCS_PORT_SYMBNAME_MACHINENAME_SZ 30
-#define BFA_FCS_PORT_SYMBNAME_OSINFO_SZ 48
+#define BFA_FCS_PORT_SYMBNAME_OSINFO_SZ 44
#define BFA_FCS_PORT_SYMBNAME_OSPATCH_SZ 16
/*
@@ -261,6 +348,7 @@ struct bfa_fcs_fabric_s;
#define bfa_fcs_lport_get_pwwn(_lport) ((_lport)->port_cfg.pwwn)
#define bfa_fcs_lport_get_nwwn(_lport) ((_lport)->port_cfg.nwwn)
#define bfa_fcs_lport_get_psym_name(_lport) ((_lport)->port_cfg.sym_name)
+#define bfa_fcs_lport_get_nsym_name(_lport) ((_lport)->port_cfg.node_sym_name)
#define bfa_fcs_lport_is_initiator(_lport) \
((_lport)->port_cfg.roles & BFA_LPORT_ROLE_FCP_IM)
#define bfa_fcs_lport_get_nrports(_lport) \
@@ -283,17 +371,13 @@ bfa_fcs_lport_get_drvport(struct bfa_fcs_lport_s *port)
bfa_boolean_t bfa_fcs_lport_is_online(struct bfa_fcs_lport_s *port);
struct bfa_fcs_lport_s *bfa_fcs_get_base_port(struct bfa_fcs_s *fcs);
-void bfa_fcs_lport_get_rports(struct bfa_fcs_lport_s *port,
- wwn_t rport_wwns[], int *nrports);
-
-wwn_t bfa_fcs_lport_get_rport(struct bfa_fcs_lport_s *port, wwn_t wwn,
- int index, int nrports, bfa_boolean_t bwwn);
+void bfa_fcs_lport_get_rport_quals(struct bfa_fcs_lport_s *port,
+ struct bfa_rport_qualifier_s rport[], int *nrports);
struct bfa_fcs_lport_s *bfa_fcs_lookup_port(struct bfa_fcs_s *fcs,
u16 vf_id, wwn_t lpwwn);
-void bfa_fcs_lport_get_info(struct bfa_fcs_lport_s *port,
- struct bfa_lport_info_s *port_info);
+void bfa_fcs_lport_set_symname(struct bfa_fcs_lport_s *port, char *symname);
void bfa_fcs_lport_get_attr(struct bfa_fcs_lport_s *port,
struct bfa_lport_attr_s *port_attr);
void bfa_fcs_lport_get_stats(struct bfa_fcs_lport_s *fcs_port,
@@ -321,12 +405,15 @@ void bfa_fcs_lport_init(struct bfa_fcs_lport_s *lport,
void bfa_fcs_lport_online(struct bfa_fcs_lport_s *port);
void bfa_fcs_lport_offline(struct bfa_fcs_lport_s *port);
void bfa_fcs_lport_delete(struct bfa_fcs_lport_s *port);
+void bfa_fcs_lport_stop(struct bfa_fcs_lport_s *port);
struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_pid(
struct bfa_fcs_lport_s *port, u32 pid);
+struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_old_pid(
+ struct bfa_fcs_lport_s *port, u32 pid);
struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_pwwn(
struct bfa_fcs_lport_s *port, wwn_t pwwn);
-struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_nwwn(
- struct bfa_fcs_lport_s *port, wwn_t nwwn);
+struct bfa_fcs_rport_s *bfa_fcs_lport_get_rport_by_qualifier(
+ struct bfa_fcs_lport_s *port, wwn_t pwwn, u32 pid);
void bfa_fcs_lport_add_rport(struct bfa_fcs_lport_s *port,
struct bfa_fcs_rport_s *rport);
void bfa_fcs_lport_del_rport(struct bfa_fcs_lport_s *port,
@@ -335,15 +422,42 @@ void bfa_fcs_lport_ns_init(struct bfa_fcs_lport_s *vport);
void bfa_fcs_lport_ns_offline(struct bfa_fcs_lport_s *vport);
void bfa_fcs_lport_ns_online(struct bfa_fcs_lport_s *vport);
void bfa_fcs_lport_ns_query(struct bfa_fcs_lport_s *port);
+void bfa_fcs_lport_ns_util_send_rspn_id(void *cbarg,
+ struct bfa_fcxp_s *fcxp_alloced);
void bfa_fcs_lport_scn_init(struct bfa_fcs_lport_s *vport);
void bfa_fcs_lport_scn_offline(struct bfa_fcs_lport_s *vport);
-void bfa_fcs_lport_scn_online(struct bfa_fcs_lport_s *vport);
+void bfa_fcs_lport_fab_scn_online(struct bfa_fcs_lport_s *vport);
void bfa_fcs_lport_scn_process_rscn(struct bfa_fcs_lport_s *port,
struct fchs_s *rx_frame, u32 len);
+void bfa_fcs_lport_lip_scn_online(bfa_fcs_lport_t *port);
+
+/*
+ * VPort State Machine events
+ */
+enum bfa_fcs_vport_event {
+ BFA_FCS_VPORT_SM_CREATE = 1, /* vport create event */
+ BFA_FCS_VPORT_SM_DELETE = 2, /* vport delete event */
+ BFA_FCS_VPORT_SM_START = 3, /* vport start request */
+ BFA_FCS_VPORT_SM_STOP = 4, /* stop: unsupported */
+ BFA_FCS_VPORT_SM_ONLINE = 5, /* fabric online */
+ BFA_FCS_VPORT_SM_OFFLINE = 6, /* fabric offline event */
+ BFA_FCS_VPORT_SM_FRMSENT = 7, /* fdisc/logo sent events */
+ BFA_FCS_VPORT_SM_RSP_OK = 8, /* good response */
+ BFA_FCS_VPORT_SM_RSP_ERROR = 9, /* error/bad response */
+ BFA_FCS_VPORT_SM_TIMEOUT = 10, /* delay timer event */
+ BFA_FCS_VPORT_SM_DELCOMP = 11, /* lport delete completion */
+ BFA_FCS_VPORT_SM_RSP_DUP_WWN = 12, /* Dup wnn error*/
+ BFA_FCS_VPORT_SM_RSP_FAILED = 13, /* non-retryable failure */
+ BFA_FCS_VPORT_SM_STOPCOMP = 14, /* vport delete completion */
+ BFA_FCS_VPORT_SM_FABRIC_MAX = 15, /* max vports on fabric */
+};
+
+struct bfa_fcs_vport_s;
+typedef void (*bfa_fcs_vport_sm_t)(struct bfa_fcs_vport_s *fsm, enum bfa_fcs_vport_event);
struct bfa_fcs_vport_s {
struct list_head qe; /* queue elem */
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_vport_sm_t sm; /* state machine */
bfa_fcs_lport_t lport; /* logical port */
struct bfa_timer_s timer;
struct bfad_vport_s *vport_drv; /* Driver private */
@@ -366,7 +480,6 @@ bfa_status_t bfa_fcs_pbc_vport_create(struct bfa_fcs_vport_s *vport,
struct bfa_fcs_s *fcs, u16 vf_id,
struct bfa_lport_cfg_s *port_cfg,
struct bfad_vport_s *vport_drv);
-bfa_boolean_t bfa_fcs_is_pbc_vport(struct bfa_fcs_vport_s *vport);
bfa_status_t bfa_fcs_vport_delete(struct bfa_fcs_vport_s *vport);
bfa_status_t bfa_fcs_vport_start(struct bfa_fcs_vport_s *vport);
bfa_status_t bfa_fcs_vport_stop(struct bfa_fcs_vport_s *vport);
@@ -374,11 +487,12 @@ void bfa_fcs_vport_get_attr(struct bfa_fcs_vport_s *vport,
struct bfa_vport_attr_s *vport_attr);
struct bfa_fcs_vport_s *bfa_fcs_vport_lookup(struct bfa_fcs_s *fcs,
u16 vf_id, wwn_t vpwwn);
-void bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport);
void bfa_fcs_vport_online(struct bfa_fcs_vport_s *vport);
void bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport);
void bfa_fcs_vport_delete_comp(struct bfa_fcs_vport_s *vport);
void bfa_fcs_vport_fcs_delete(struct bfa_fcs_vport_s *vport);
+void bfa_fcs_vport_fcs_stop(struct bfa_fcs_vport_s *vport);
+void bfa_fcs_vport_stop_comp(struct bfa_fcs_vport_s *vport);
#define BFA_FCS_RPORT_DEF_DEL_TIMEOUT 90 /* in secs */
#define BFA_FCS_RPORT_MAX_RETRIES (5)
@@ -392,9 +506,26 @@ struct bfa_fcs_itnim_s;
struct bfa_fcs_tin_s;
struct bfa_fcs_iprp_s;
+/*
+ * fcs_rport_ftrs_sm FCS rport state machine events
+ */
+
+enum rpf_event {
+ RPFSM_EVENT_RPORT_OFFLINE = 1, /* Rport offline */
+ RPFSM_EVENT_RPORT_ONLINE = 2, /* Rport online */
+ RPFSM_EVENT_FCXP_SENT = 3, /* Frame from has been sent */
+ RPFSM_EVENT_TIMEOUT = 4, /* Rport SM timeout event */
+ RPFSM_EVENT_RPSC_COMP = 5,
+ RPFSM_EVENT_RPSC_FAIL = 6,
+ RPFSM_EVENT_RPSC_ERROR = 7,
+};
+
+struct bfa_fcs_rpf_s;
+typedef void (*bfa_fcs_rpf_sm_t)(struct bfa_fcs_rpf_s *, enum rpf_event);
+
/* Rport Features (RPF) */
struct bfa_fcs_rpf_s {
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_rpf_sm_t sm; /* state machine */
struct bfa_fcs_rport_s *rport; /* parent rport */
struct bfa_timer_s timer; /* general purpose timer */
struct bfa_fcxp_s *fcxp; /* FCXP needed for discarding */
@@ -409,21 +540,53 @@ struct bfa_fcs_rpf_s {
*/
};
+/*
+ * fcs_rport_sm FCS rport state machine events
+ */
+enum rport_event {
+ RPSM_EVENT_PLOGI_SEND = 1, /* new rport; start with PLOGI */
+ RPSM_EVENT_PLOGI_RCVD = 2, /* Inbound PLOGI from remote port */
+ RPSM_EVENT_PLOGI_COMP = 3, /* PLOGI completed to rport */
+ RPSM_EVENT_LOGO_RCVD = 4, /* LOGO from remote device */
+ RPSM_EVENT_LOGO_IMP = 5, /* implicit logo for SLER */
+ RPSM_EVENT_FCXP_SENT = 6, /* Frame from has been sent */
+ RPSM_EVENT_DELETE = 7, /* RPORT delete request */
+ RPSM_EVENT_FAB_SCN = 8, /* state change notification */
+ RPSM_EVENT_ACCEPTED = 9, /* Good response from remote device */
+ RPSM_EVENT_FAILED = 10, /* Request to rport failed. */
+ RPSM_EVENT_TIMEOUT = 11, /* Rport SM timeout event */
+ RPSM_EVENT_HCB_ONLINE = 12, /* BFA rport online callback */
+ RPSM_EVENT_HCB_OFFLINE = 13, /* BFA rport offline callback */
+ RPSM_EVENT_FC4_OFFLINE = 14, /* FC-4 offline complete */
+ RPSM_EVENT_ADDRESS_CHANGE = 15, /* Rport's PID has changed */
+ RPSM_EVENT_ADDRESS_DISC = 16, /* Need to Discover rport's PID */
+ RPSM_EVENT_PRLO_RCVD = 17, /* PRLO from remote device */
+ RPSM_EVENT_PLOGI_RETRY = 18, /* Retry PLOGI continuously */
+ RPSM_EVENT_SCN_OFFLINE = 19, /* loop scn offline */
+ RPSM_EVENT_SCN_ONLINE = 20, /* loop scn online */
+ RPSM_EVENT_FC4_FCS_ONLINE = 21, /* FC-4 FCS online complete */
+};
+
+struct bfa_fcs_rport_s;
+typedef void (*bfa_fcs_rport_sm_t)(struct bfa_fcs_rport_s *, enum rport_event);
+
struct bfa_fcs_rport_s {
struct list_head qe; /* used by port/vport */
struct bfa_fcs_lport_s *port; /* parent FCS port */
struct bfa_fcs_s *fcs; /* fcs instance */
struct bfad_rport_s *rp_drv; /* driver peer instance */
u32 pid; /* port ID of rport */
+ u32 old_pid; /* PID before rport goes offline */
u16 maxfrsize; /* maximum frame size */
__be16 reply_oxid; /* OX_ID of inbound requests */
enum fc_cos fc_cos; /* FC classes of service supp */
bfa_boolean_t cisc; /* CISC capable device */
bfa_boolean_t prlo; /* processing prlo or LOGO */
+ bfa_boolean_t plogi_pending; /* Rx Plogi Pending */
wwn_t pwwn; /* port wwn of rport */
wwn_t nwwn; /* node wwn of rport */
struct bfa_rport_symname_s psym_name; /* port symbolic name */
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_rport_sm_t sm; /* state machine */
struct bfa_timer_s timer; /* general purpose timer */
struct bfa_fcs_itnim_s *itnim; /* ITN initiator mode role */
struct bfa_fcs_tin_s *tin; /* ITN initiator mode role */
@@ -436,6 +599,7 @@ struct bfa_fcs_rport_s {
struct bfa_rport_stats_s stats; /* rport stats */
enum bfa_rport_function scsi_function; /* Initiator/Target */
struct bfa_fcs_rpf_s rpf; /* Rport features module */
+ bfa_boolean_t scn_online; /* SCN online flag */
};
static inline struct bfa_rport_s *
@@ -447,20 +611,18 @@ bfa_fcs_rport_get_halrport(struct bfa_fcs_rport_s *rport)
/*
* bfa fcs rport API functions
*/
+void bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport,
+ struct bfa_rport_attr_s *attr);
struct bfa_fcs_rport_s *bfa_fcs_rport_lookup(struct bfa_fcs_lport_s *port,
wwn_t rpwwn);
-struct bfa_fcs_rport_s *bfa_fcs_rport_lookup_by_nwwn(
- struct bfa_fcs_lport_s *port, wwn_t rnwwn);
void bfa_fcs_rport_set_del_timeout(u8 rport_tmo);
-
+void bfa_fcs_rport_set_max_logins(u32 max_logins);
void bfa_fcs_rport_uf_recv(struct bfa_fcs_rport_s *rport,
struct fchs_s *fchs, u16 len);
void bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport);
struct bfa_fcs_rport_s *bfa_fcs_rport_create(struct bfa_fcs_lport_s *port,
u32 pid);
-void bfa_fcs_rport_start(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs,
- struct fc_logi_s *plogi_rsp);
void bfa_fcs_rport_plogi_create(struct bfa_fcs_lport_s *port,
struct fchs_s *rx_fchs,
struct fc_logi_s *plogi);
@@ -478,12 +640,34 @@ void bfa_fcs_rpf_rport_online(struct bfa_fcs_rport_s *rport);
void bfa_fcs_rpf_rport_offline(struct bfa_fcs_rport_s *rport);
/*
+ * fcs_itnim_sm FCS itnim state machine events
+ */
+enum bfa_fcs_itnim_event {
+ BFA_FCS_ITNIM_SM_FCS_ONLINE = 1, /* rport online event */
+ BFA_FCS_ITNIM_SM_OFFLINE = 2, /* rport offline */
+ BFA_FCS_ITNIM_SM_FRMSENT = 3, /* prli frame is sent */
+ BFA_FCS_ITNIM_SM_RSP_OK = 4, /* good response */
+ BFA_FCS_ITNIM_SM_RSP_ERROR = 5, /* error response */
+ BFA_FCS_ITNIM_SM_TIMEOUT = 6, /* delay timeout */
+ BFA_FCS_ITNIM_SM_HCB_OFFLINE = 7, /* BFA online callback */
+ BFA_FCS_ITNIM_SM_HCB_ONLINE = 8, /* BFA offline callback */
+ BFA_FCS_ITNIM_SM_INITIATOR = 9, /* rport is initiator */
+ BFA_FCS_ITNIM_SM_DELETE = 10, /* delete event from rport */
+ BFA_FCS_ITNIM_SM_PRLO = 11, /* delete event from rport */
+ BFA_FCS_ITNIM_SM_RSP_NOT_SUPP = 12, /* cmd not supported rsp */
+ BFA_FCS_ITNIM_SM_HAL_ONLINE = 13, /* bfa rport online event */
+};
+
+struct bfa_fcs_itnim_s;
+typedef void (*bfa_fcs_itnim_sm_t)(struct bfa_fcs_itnim_s *, enum bfa_fcs_itnim_event);
+
+/*
* forward declarations
*/
struct bfad_itnim_s;
struct bfa_fcs_itnim_s {
- bfa_sm_t sm; /* state machine */
+ bfa_fcs_itnim_sm_t sm; /* state machine */
struct bfa_fcs_rport_s *rport; /* parent remote rport */
struct bfad_itnim_s *itnim_drv; /* driver peer instance */
struct bfa_fcs_s *fcs; /* fcs instance */
@@ -498,12 +682,13 @@ struct bfa_fcs_itnim_s {
struct bfa_fcxp_s *fcxp; /* FCXP in use */
struct bfa_itnim_stats_s stats; /* itn statistics */
};
-#define bfa_fcs_fcxp_alloc(__fcs) \
- bfa_fcxp_alloc(NULL, (__fcs)->bfa, 0, 0, NULL, NULL, NULL, NULL)
-
-#define bfa_fcs_fcxp_alloc_wait(__bfa, __wqe, __alloc_cbfn, __alloc_cbarg) \
- bfa_fcxp_alloc_wait(__bfa, __wqe, __alloc_cbfn, __alloc_cbarg, \
- NULL, 0, 0, NULL, NULL, NULL, NULL)
+#define bfa_fcs_fcxp_alloc(__fcs, __req) \
+ bfa_fcxp_req_rsp_alloc(NULL, (__fcs)->bfa, 0, 0, \
+ NULL, NULL, NULL, NULL, __req)
+#define bfa_fcs_fcxp_alloc_wait(__bfa, __wqe, __alloc_cbfn, \
+ __alloc_cbarg, __req) \
+ bfa_fcxp_req_rsp_alloc_wait(__bfa, __wqe, __alloc_cbfn, \
+ __alloc_cbarg, NULL, 0, 0, NULL, NULL, NULL, NULL, __req)
static inline struct bfad_port_s *
bfa_fcs_itnim_get_drvport(struct bfa_fcs_itnim_s *itnim)
@@ -585,20 +770,34 @@ bfa_status_t bfa_fcs_itnim_stats_clear(struct bfa_fcs_lport_s *port,
struct bfa_fcs_itnim_s *bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport);
void bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim);
void bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim);
-void bfa_fcs_itnim_rport_online(struct bfa_fcs_itnim_s *itnim);
+void bfa_fcs_itnim_brp_online(struct bfa_fcs_itnim_s *itnim);
bfa_status_t bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim);
void bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim);
void bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim,
struct fchs_s *fchs, u16 len);
-#define BFA_FCS_FDMI_SUPORTED_SPEEDS (FDMI_TRANS_SPEED_1G | \
- FDMI_TRANS_SPEED_2G | \
- FDMI_TRANS_SPEED_4G | \
- FDMI_TRANS_SPEED_8G)
+#define BFA_FCS_FDMI_SUPP_SPEEDS_4G (FDMI_TRANS_SPEED_1G | \
+ FDMI_TRANS_SPEED_2G | \
+ FDMI_TRANS_SPEED_4G)
+
+#define BFA_FCS_FDMI_SUPP_SPEEDS_8G (FDMI_TRANS_SPEED_1G | \
+ FDMI_TRANS_SPEED_2G | \
+ FDMI_TRANS_SPEED_4G | \
+ FDMI_TRANS_SPEED_8G)
+
+#define BFA_FCS_FDMI_SUPP_SPEEDS_16G (FDMI_TRANS_SPEED_2G | \
+ FDMI_TRANS_SPEED_4G | \
+ FDMI_TRANS_SPEED_8G | \
+ FDMI_TRANS_SPEED_16G)
+
+#define BFA_FCS_FDMI_SUPP_SPEEDS_10G FDMI_TRANS_SPEED_10G
+
+#define BFA_FCS_FDMI_VENDOR_INFO_LEN 8
+#define BFA_FCS_FDMI_FC4_TYPE_LEN 32
/*
* HBA Attribute Block : BFA internal representation. Note : Some variable
- * sizes have been trimmed to suit BFA For Ex : Model will be "Brocade". Based
+ * sizes have been trimmed to suit BFA For Ex : Model will be "QLogic ". Based
* on this the size has been reduced to 16 bytes from the standard's 64 bytes.
*/
struct bfa_fcs_fdmi_hba_attr_s {
@@ -606,25 +805,39 @@ struct bfa_fcs_fdmi_hba_attr_s {
u8 manufacturer[64];
u8 serial_num[64];
u8 model[16];
- u8 model_desc[256];
+ u8 model_desc[128];
u8 hw_version[8];
- u8 driver_version[8];
+ u8 driver_version[BFA_VERSION_LEN];
u8 option_rom_ver[BFA_VERSION_LEN];
- u8 fw_version[8];
+ u8 fw_version[BFA_VERSION_LEN];
u8 os_name[256];
__be32 max_ct_pyld;
+ struct bfa_lport_symname_s node_sym_name;
+ u8 vendor_info[BFA_FCS_FDMI_VENDOR_INFO_LEN];
+ __be32 num_ports;
+ wwn_t fabric_name;
+ u8 bios_ver[BFA_VERSION_LEN];
};
/*
* Port Attribute Block
*/
struct bfa_fcs_fdmi_port_attr_s {
- u8 supp_fc4_types[32]; /* supported FC4 types */
+ u8 supp_fc4_types[BFA_FCS_FDMI_FC4_TYPE_LEN];
__be32 supp_speed; /* supported speed */
__be32 curr_speed; /* current Speed */
__be32 max_frm_size; /* max frame size */
u8 os_device_name[256]; /* OS device Name */
u8 host_name[256]; /* host name */
+ wwn_t port_name;
+ wwn_t node_name;
+ struct bfa_lport_symname_s port_sym_name;
+ __be32 port_type;
+ enum fc_cos scos;
+ wwn_t port_fabric_name;
+ u8 port_act_fc4_type[BFA_FCS_FDMI_FC4_TYPE_LEN];
+ __be32 port_state;
+ __be32 num_ports;
};
struct bfa_fcs_stats_s {
@@ -655,6 +868,8 @@ struct bfa_fcs_s {
struct bfa_fcs_fabric_s fabric; /* base fabric state machine */
struct bfa_fcs_stats_s stats; /* FCS statistics */
struct bfa_wc_s wc; /* waiting counter */
+ int fcs_aen_seq;
+ u32 num_rport_logins;
};
/*
@@ -662,74 +877,29 @@ struct bfa_fcs_s {
*/
/*
- * Fabric state machine events
- */
-enum bfa_fcs_fabric_event {
- BFA_FCS_FABRIC_SM_CREATE = 1, /* create from driver */
- BFA_FCS_FABRIC_SM_DELETE = 2, /* delete from driver */
- BFA_FCS_FABRIC_SM_LINK_DOWN = 3, /* link down from port */
- BFA_FCS_FABRIC_SM_LINK_UP = 4, /* link up from port */
- BFA_FCS_FABRIC_SM_CONT_OP = 5, /* flogi/auth continue op */
- BFA_FCS_FABRIC_SM_RETRY_OP = 6, /* flogi/auth retry op */
- BFA_FCS_FABRIC_SM_NO_FABRIC = 7, /* from flogi/auth */
- BFA_FCS_FABRIC_SM_PERF_EVFP = 8, /* from flogi/auth */
- BFA_FCS_FABRIC_SM_ISOLATE = 9, /* from EVFP processing */
- BFA_FCS_FABRIC_SM_NO_TAGGING = 10, /* no VFT tagging from EVFP */
- BFA_FCS_FABRIC_SM_DELAYED = 11, /* timeout delay event */
- BFA_FCS_FABRIC_SM_AUTH_FAILED = 12, /* auth failed */
- BFA_FCS_FABRIC_SM_AUTH_SUCCESS = 13, /* auth successful */
- BFA_FCS_FABRIC_SM_DELCOMP = 14, /* all vports deleted event */
- BFA_FCS_FABRIC_SM_LOOPBACK = 15, /* Received our own FLOGI */
- BFA_FCS_FABRIC_SM_START = 16, /* from driver */
-};
-
-/*
- * fcs_rport_sm FCS rport state machine events
- */
-
-enum rport_event {
- RPSM_EVENT_PLOGI_SEND = 1, /* new rport; start with PLOGI */
- RPSM_EVENT_PLOGI_RCVD = 2, /* Inbound PLOGI from remote port */
- RPSM_EVENT_PLOGI_COMP = 3, /* PLOGI completed to rport */
- RPSM_EVENT_LOGO_RCVD = 4, /* LOGO from remote device */
- RPSM_EVENT_LOGO_IMP = 5, /* implicit logo for SLER */
- RPSM_EVENT_FCXP_SENT = 6, /* Frame from has been sent */
- RPSM_EVENT_DELETE = 7, /* RPORT delete request */
- RPSM_EVENT_SCN = 8, /* state change notification */
- RPSM_EVENT_ACCEPTED = 9, /* Good response from remote device */
- RPSM_EVENT_FAILED = 10, /* Request to rport failed. */
- RPSM_EVENT_TIMEOUT = 11, /* Rport SM timeout event */
- RPSM_EVENT_HCB_ONLINE = 12, /* BFA rport online callback */
- RPSM_EVENT_HCB_OFFLINE = 13, /* BFA rport offline callback */
- RPSM_EVENT_FC4_OFFLINE = 14, /* FC-4 offline complete */
- RPSM_EVENT_ADDRESS_CHANGE = 15, /* Rport's PID has changed */
- RPSM_EVENT_ADDRESS_DISC = 16, /* Need to Discover rport's PID */
- RPSM_EVENT_PRLO_RCVD = 17, /* PRLO from remote device */
- RPSM_EVENT_PLOGI_RETRY = 18, /* Retry PLOGI continuously */
-};
-
-/*
* bfa fcs API functions
*/
void bfa_fcs_attach(struct bfa_fcs_s *fcs, struct bfa_s *bfa,
struct bfad_s *bfad,
bfa_boolean_t min_cfg);
void bfa_fcs_init(struct bfa_fcs_s *fcs);
+void bfa_fcs_pbc_vport_init(struct bfa_fcs_s *fcs);
+void bfa_fcs_update_cfg(struct bfa_fcs_s *fcs);
void bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs,
struct bfa_fcs_driver_info_s *driver_info);
void bfa_fcs_exit(struct bfa_fcs_s *fcs);
+void bfa_fcs_stop(struct bfa_fcs_s *fcs);
/*
* bfa fcs vf public functions
*/
bfa_fcs_vf_t *bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id);
+void bfa_fcs_vf_get_ports(bfa_fcs_vf_t *vf, wwn_t vpwwn[], int *nports);
/*
* fabric protected interface functions
*/
-void bfa_fcs_fabric_attach(struct bfa_fcs_s *fcs);
void bfa_fcs_fabric_modinit(struct bfa_fcs_s *fcs);
-void bfa_fcs_fabric_modexit(struct bfa_fcs_s *fcs);
void bfa_fcs_fabric_link_up(struct bfa_fcs_fabric_s *fabric);
void bfa_fcs_fabric_link_down(struct bfa_fcs_fabric_s *fabric);
void bfa_fcs_fabric_addvport(struct bfa_fcs_fabric_s *fabric,
@@ -742,11 +912,11 @@ void bfa_fcs_fabric_modstart(struct bfa_fcs_s *fcs);
void bfa_fcs_fabric_uf_recv(struct bfa_fcs_fabric_s *fabric,
struct fchs_s *fchs, u16 len);
void bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric);
+void bfa_fcs_fabric_nsymb_init(struct bfa_fcs_fabric_s *fabric);
void bfa_fcs_fabric_set_fabric_name(struct bfa_fcs_fabric_s *fabric,
wwn_t fabric_name);
u16 bfa_fcs_fabric_get_switch_oui(struct bfa_fcs_fabric_s *fabric);
-void bfa_fcs_uf_attach(struct bfa_fcs_s *fcs);
-void bfa_fcs_port_attach(struct bfa_fcs_s *fcs);
+void bfa_fcs_fabric_modstop(struct bfa_fcs_s *fcs);
void bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
void bfa_fcs_fabric_sm_loopback(struct bfa_fcs_fabric_s *fabric,
@@ -763,9 +933,7 @@ void bfa_fcs_fabric_sm_auth_failed(struct bfa_fcs_fabric_s *fabric,
*/
struct bfad_port_s;
-struct bfad_vf_s;
struct bfad_vport_s;
-struct bfad_rport_s;
/*
* lport callbacks
@@ -775,9 +943,6 @@ struct bfad_port_s *bfa_fcb_lport_new(struct bfad_s *bfad,
enum bfa_lport_role roles,
struct bfad_vf_s *vf_drv,
struct bfad_vport_s *vp_drv);
-void bfa_fcb_lport_delete(struct bfad_s *bfad, enum bfa_lport_role roles,
- struct bfad_vf_s *vf_drv,
- struct bfad_vport_s *vp_drv);
/*
* vport callbacks
@@ -794,8 +959,8 @@ bfa_status_t bfa_fcb_rport_alloc(struct bfad_s *bfad,
/*
* itnim callbacks
*/
-void bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim,
- struct bfad_itnim_s **itnim_drv);
+int bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim,
+ struct bfad_itnim_s **itnim_drv);
void bfa_fcb_itnim_free(struct bfad_s *bfad,
struct bfad_itnim_s *itnim_drv);
void bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv);
diff --git a/drivers/scsi/bfa/bfa_fcs_fcpim.c b/drivers/scsi/bfa/bfa_fcs_fcpim.c
index e7b49f4cb51f..40e65ab28504 100644
--- a/drivers/scsi/bfa/bfa_fcs_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcs_fcpim.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -23,6 +16,7 @@
#include "bfa_fcs.h"
#include "bfa_fcbuild.h"
#include "bfad_im.h"
+#include "bfa_fcpim.h"
BFA_TRC_FILE(FCS, FCPIM);
@@ -37,24 +31,8 @@ static void bfa_fcs_itnim_prli_response(void *fcsarg,
struct bfa_fcxp_s *fcxp, void *cbarg,
bfa_status_t req_status, u32 rsp_len,
u32 resid_len, struct fchs_s *rsp_fchs);
-
-/*
- * fcs_itnim_sm FCS itnim state machine events
- */
-
-enum bfa_fcs_itnim_event {
- BFA_FCS_ITNIM_SM_ONLINE = 1, /* rport online event */
- BFA_FCS_ITNIM_SM_OFFLINE = 2, /* rport offline */
- BFA_FCS_ITNIM_SM_FRMSENT = 3, /* prli frame is sent */
- BFA_FCS_ITNIM_SM_RSP_OK = 4, /* good response */
- BFA_FCS_ITNIM_SM_RSP_ERROR = 5, /* error response */
- BFA_FCS_ITNIM_SM_TIMEOUT = 6, /* delay timeout */
- BFA_FCS_ITNIM_SM_HCB_OFFLINE = 7, /* BFA online callback */
- BFA_FCS_ITNIM_SM_HCB_ONLINE = 8, /* BFA offline callback */
- BFA_FCS_ITNIM_SM_INITIATOR = 9, /* rport is initiator */
- BFA_FCS_ITNIM_SM_DELETE = 10, /* delete event from rport */
- BFA_FCS_ITNIM_SM_PRLO = 11, /* delete event from rport */
-};
+static void bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
+ enum bfa_itnim_aen_event event);
static void bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim,
enum bfa_fcs_itnim_event event);
@@ -66,6 +44,8 @@ static void bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim,
enum bfa_fcs_itnim_event event);
static void bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim,
enum bfa_fcs_itnim_event event);
+static void bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim,
+ enum bfa_fcs_itnim_event event);
static void bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
enum bfa_fcs_itnim_event event);
static void bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim,
@@ -73,7 +53,23 @@ static void bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim,
static void bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim,
enum bfa_fcs_itnim_event event);
-static struct bfa_sm_table_s itnim_sm_table[] = {
+struct bfa_fcs_itnim_sm_table_s {
+ bfa_fcs_itnim_sm_t sm; /* state machine function */
+ enum bfa_itnim_state state; /* state machine encoding */
+ char *name; /* state name for display */
+};
+
+static inline enum bfa_itnim_state
+bfa_fcs_itnim_sm_to_state(struct bfa_fcs_itnim_sm_table_s *smt, bfa_fcs_itnim_sm_t sm)
+{
+ int i = 0;
+
+ while (smt[i].sm && smt[i].sm != sm)
+ i++;
+ return smt[i].state;
+}
+
+static struct bfa_fcs_itnim_sm_table_s itnim_sm_table[] = {
{BFA_SM(bfa_fcs_itnim_sm_offline), BFA_ITNIM_OFFLINE},
{BFA_SM(bfa_fcs_itnim_sm_prli_send), BFA_ITNIM_PRLI_SEND},
{BFA_SM(bfa_fcs_itnim_sm_prli), BFA_ITNIM_PRLI_SENT},
@@ -96,7 +92,7 @@ bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim,
bfa_trc(itnim->fcs, event);
switch (event) {
- case BFA_FCS_ITNIM_SM_ONLINE:
+ case BFA_FCS_ITNIM_SM_FCS_ONLINE:
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send);
itnim->prli_retries = 0;
bfa_fcs_itnim_send_prli(itnim, NULL);
@@ -135,6 +131,7 @@ bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim,
case BFA_FCS_ITNIM_SM_INITIATOR:
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe);
+ bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
break;
case BFA_FCS_ITNIM_SM_OFFLINE:
@@ -163,12 +160,13 @@ bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
switch (event) {
case BFA_FCS_ITNIM_SM_RSP_OK:
- if (itnim->rport->scsi_function == BFA_RPORT_INITIATOR) {
+ if (itnim->rport->scsi_function == BFA_RPORT_INITIATOR)
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
- } else {
- bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online);
- bfa_itnim_online(itnim->bfa_itnim, itnim->seq_rec);
- }
+ else
+ bfa_sm_set_state(itnim,
+ bfa_fcs_itnim_sm_hal_rport_online);
+
+ bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
break;
case BFA_FCS_ITNIM_SM_RSP_ERROR:
@@ -178,6 +176,10 @@ bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
BFA_FCS_RETRY_TIMEOUT);
break;
+ case BFA_FCS_ITNIM_SM_RSP_NOT_SUPP:
+ bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
+ break;
+
case BFA_FCS_ITNIM_SM_OFFLINE:
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
bfa_fcxp_discard(itnim->fcxp);
@@ -187,6 +189,7 @@ bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
case BFA_FCS_ITNIM_SM_INITIATOR:
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
bfa_fcxp_discard(itnim->fcxp);
+ bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
break;
case BFA_FCS_ITNIM_SM_DELETE:
@@ -201,6 +204,44 @@ bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
}
static void
+bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim,
+ enum bfa_fcs_itnim_event event)
+{
+ bfa_trc(itnim->fcs, itnim->rport->pwwn);
+ bfa_trc(itnim->fcs, event);
+
+ switch (event) {
+ case BFA_FCS_ITNIM_SM_HAL_ONLINE:
+ if (!itnim->bfa_itnim)
+ itnim->bfa_itnim = bfa_itnim_create(itnim->fcs->bfa,
+ itnim->rport->bfa_rport, itnim);
+
+ if (itnim->bfa_itnim) {
+ bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online);
+ bfa_itnim_online(itnim->bfa_itnim, itnim->seq_rec);
+ } else {
+ bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
+ bfa_sm_send_event(itnim->rport, RPSM_EVENT_DELETE);
+ }
+
+ break;
+
+ case BFA_FCS_ITNIM_SM_OFFLINE:
+ bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
+ bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
+ break;
+
+ case BFA_FCS_ITNIM_SM_DELETE:
+ bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
+ bfa_fcs_itnim_free(itnim);
+ break;
+
+ default:
+ bfa_sm_fault(itnim->fcs, event);
+ }
+}
+
+static void
bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim,
enum bfa_fcs_itnim_event event)
{
@@ -231,6 +272,7 @@ bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim,
case BFA_FCS_ITNIM_SM_INITIATOR:
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
bfa_timer_stop(&itnim->timer);
+ bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
break;
case BFA_FCS_ITNIM_SM_DELETE:
@@ -264,12 +306,12 @@ bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim,
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Target (WWN = %s) is online for initiator (WWN = %s)\n",
rpwwn_buf, lpwwn_buf);
+ bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_ONLINE);
break;
case BFA_FCS_ITNIM_SM_OFFLINE:
- bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
+ bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
bfa_itnim_offline(itnim->bfa_itnim);
- bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
break;
case BFA_FCS_ITNIM_SM_DELETE:
@@ -300,14 +342,17 @@ bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
bfa_itnim_offline(itnim->bfa_itnim);
wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port));
wwn2str(rpwwn_buf, itnim->rport->pwwn);
- if (bfa_fcs_lport_is_online(itnim->rport->port) == BFA_TRUE)
+ if (bfa_fcs_lport_is_online(itnim->rport->port) == BFA_TRUE) {
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
"Target (WWN = %s) connectivity lost for "
"initiator (WWN = %s)\n", rpwwn_buf, lpwwn_buf);
- else
+ bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT);
+ } else {
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Target (WWN = %s) offlined by initiator (WWN = %s)\n",
rpwwn_buf, lpwwn_buf);
+ bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE);
+ }
break;
case BFA_FCS_ITNIM_SM_DELETE:
@@ -361,8 +406,14 @@ bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim,
bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
break;
+ /*
+ * fcs_online is expected here for well known initiator ports
+ */
+ case BFA_FCS_ITNIM_SM_FCS_ONLINE:
+ bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
+ break;
+
case BFA_FCS_ITNIM_SM_RSP_ERROR:
- case BFA_FCS_ITNIM_SM_ONLINE:
case BFA_FCS_ITNIM_SM_INITIATOR:
break;
@@ -377,6 +428,33 @@ bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim,
}
static void
+bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
+ enum bfa_itnim_aen_event event)
+{
+ struct bfa_fcs_rport_s *rport = itnim->rport;
+ struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+
+ /* Don't post events for well known addresses */
+ if (BFA_FCS_PID_IS_WKA(rport->pid))
+ return;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ aen_entry->aen_data.itnim.vf_id = rport->port->fabric->vf_id;
+ aen_entry->aen_data.itnim.ppwwn = bfa_fcs_lport_get_pwwn(
+ bfa_fcs_get_base_port(itnim->fcs));
+ aen_entry->aen_data.itnim.lpwwn = bfa_fcs_lport_get_pwwn(rport->port);
+ aen_entry->aen_data.itnim.rpwwn = rport->pwwn;
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++rport->fcs->fcs_aen_seq,
+ BFA_AEN_CAT_ITNIM, event);
+}
+
+static void
bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced)
{
struct bfa_fcs_itnim_s *itnim = itnim_cbarg;
@@ -388,11 +466,12 @@ bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(itnim->fcs, itnim->rport->pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
itnim->stats.fcxp_alloc_wait++;
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &itnim->fcxp_wqe,
- bfa_fcs_itnim_send_prli, itnim);
+ bfa_fcs_itnim_send_prli, itnim, BFA_TRUE);
return;
}
itnim->fcxp = fcxp;
@@ -445,8 +524,9 @@ bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
if (prli_resp->parampage.servparams.initiator) {
bfa_trc(itnim->fcs, prli_resp->parampage.type);
itnim->rport->scsi_function =
- BFA_RPORT_INITIATOR;
+ BFA_RPORT_INITIATOR;
itnim->stats.prli_rsp_acc++;
+ itnim->stats.initiator++;
bfa_sm_send_event(itnim,
BFA_FCS_ITNIM_SM_RSP_OK);
return;
@@ -472,6 +552,10 @@ bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
bfa_trc(itnim->fcs, ls_rjt->reason_code_expl);
itnim->stats.prli_rsp_rjt++;
+ if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) {
+ bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_NOT_SUPP);
+ return;
+ }
bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR);
}
}
@@ -488,7 +572,11 @@ bfa_fcs_itnim_timeout(void *arg)
static void
bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim)
{
- bfa_itnim_delete(itnim->bfa_itnim);
+ if (itnim->bfa_itnim) {
+ bfa_itnim_delete(itnim->bfa_itnim);
+ itnim->bfa_itnim = NULL;
+ }
+
bfa_fcb_itnim_free(itnim->fcs->bfad, itnim->itnim_drv);
}
@@ -509,13 +597,13 @@ bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport)
struct bfa_fcs_lport_s *port = rport->port;
struct bfa_fcs_itnim_s *itnim;
struct bfad_itnim_s *itnim_drv;
- struct bfa_itnim_s *bfa_itnim;
+ int ret;
/*
* call bfad to allocate the itnim
*/
- bfa_fcb_itnim_alloc(port->fcs->bfad, &itnim, &itnim_drv);
- if (itnim == NULL) {
+ ret = bfa_fcb_itnim_alloc(port->fcs->bfad, &itnim, &itnim_drv);
+ if (ret) {
bfa_trc(port->fcs, rport->pwwn);
return NULL;
}
@@ -527,20 +615,7 @@ bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport)
itnim->fcs = rport->fcs;
itnim->itnim_drv = itnim_drv;
- /*
- * call BFA to create the itnim
- */
- bfa_itnim =
- bfa_itnim_create(port->fcs->bfa, rport->bfa_rport, itnim);
-
- if (bfa_itnim == NULL) {
- bfa_trc(port->fcs, rport->pwwn);
- bfa_fcb_itnim_free(port->fcs->bfad, itnim_drv);
- WARN_ON(1);
- return NULL;
- }
-
- itnim->bfa_itnim = bfa_itnim;
+ itnim->bfa_itnim = NULL;
itnim->seq_rec = BFA_FALSE;
itnim->rec_support = BFA_FALSE;
itnim->conf_comp = BFA_FALSE;
@@ -570,20 +645,12 @@ bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim)
* Notification from rport that PLOGI is complete to initiate FC-4 session.
*/
void
-bfa_fcs_itnim_rport_online(struct bfa_fcs_itnim_s *itnim)
+bfa_fcs_itnim_brp_online(struct bfa_fcs_itnim_s *itnim)
{
itnim->stats.onlines++;
- if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid)) {
- bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_ONLINE);
- } else {
- /*
- * For well known addresses, we set the itnim to initiator
- * state
- */
- itnim->stats.initiator++;
- bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR);
- }
+ if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid))
+ bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HAL_ONLINE);
}
/*
@@ -615,7 +682,7 @@ bfa_status_t
bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim)
{
bfa_trc(itnim->fcs, itnim->rport->pid);
- switch (bfa_sm_to_state(itnim_sm_table, itnim->sm)) {
+ switch (bfa_fcs_itnim_sm_to_state(itnim_sm_table, itnim->sm)) {
case BFA_ITNIM_ONLINE:
case BFA_ITNIM_INITIATIOR:
return BFA_STATUS_OK;
@@ -715,7 +782,7 @@ bfa_fcs_itnim_attr_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn,
if (itnim == NULL)
return BFA_STATUS_NO_FCPIM_NEXUS;
- attr->state = bfa_sm_to_state(itnim_sm_table, itnim->sm);
+ attr->state = bfa_fcs_itnim_sm_to_state(itnim_sm_table, itnim->sm);
attr->retry = itnim->seq_rec;
attr->rec_support = itnim->rec_support;
attr->conf_comp = itnim->conf_comp;
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index 1d6be8c14473..9a85f417018f 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -1,27 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
+#include "bfad_im.h"
#include "bfa_fcs.h"
#include "bfa_fcbuild.h"
#include "bfa_fc.h"
BFA_TRC_FILE(FCS, PORT);
+/*
+ * ALPA to LIXA bitmap mapping
+ *
+ * ALPA 0x00 (Word 0, Bit 30) is invalid for N_Ports. Also Word 0 Bit 31
+ * is for L_bit (login required) and is filled as ALPA 0x00 here.
+ */
+static const u8 loop_alpa_map[] = {
+ 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x0F, 0x10, /* Word 0 Bits 31..24 */
+ 0x17, 0x18, 0x1B, 0x1D, 0x1E, 0x1F, 0x23, 0x25, /* Word 0 Bits 23..16 */
+ 0x26, 0x27, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, /* Word 0 Bits 15..08 */
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x39, 0x3A, /* Word 0 Bits 07..00 */
+
+ 0x3C, 0x43, 0x45, 0x46, 0x47, 0x49, 0x4A, 0x4B, /* Word 1 Bits 31..24 */
+ 0x4C, 0x4D, 0x4E, 0x51, 0x52, 0x53, 0x54, 0x55, /* Word 1 Bits 23..16 */
+ 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x67, /* Word 1 Bits 15..08 */
+ 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x71, 0x72, /* Word 1 Bits 07..00 */
+
+ 0x73, 0x74, 0x75, 0x76, 0x79, 0x7A, 0x7C, 0x80, /* Word 2 Bits 31..24 */
+ 0x81, 0x82, 0x84, 0x88, 0x8F, 0x90, 0x97, 0x98, /* Word 2 Bits 23..16 */
+ 0x9B, 0x9D, 0x9E, 0x9F, 0xA3, 0xA5, 0xA6, 0xA7, /* Word 2 Bits 15..08 */
+ 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xB1, 0xB2, /* Word 2 Bits 07..00 */
+
+ 0xB3, 0xB4, 0xB5, 0xB6, 0xB9, 0xBA, 0xBC, 0xC3, /* Word 3 Bits 31..24 */
+ 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, /* Word 3 Bits 23..16 */
+ 0xCE, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD9, /* Word 3 Bits 15..08 */
+ 0xDA, 0xDC, 0xE0, 0xE1, 0xE2, 0xE4, 0xE8, 0xEF, /* Word 3 Bits 07..00 */
+};
+
static void bfa_fcs_lport_send_ls_rjt(struct bfa_fcs_lport_s *port,
struct fchs_s *rx_fchs, u8 reason_code,
u8 reason_code_expl);
@@ -50,30 +72,35 @@ static void bfa_fcs_lport_n2n_init(struct bfa_fcs_lport_s *port);
static void bfa_fcs_lport_n2n_online(struct bfa_fcs_lport_s *port);
static void bfa_fcs_lport_n2n_offline(struct bfa_fcs_lport_s *port);
+static void bfa_fcs_lport_loop_init(struct bfa_fcs_lport_s *port);
+static void bfa_fcs_lport_loop_online(struct bfa_fcs_lport_s *port);
+static void bfa_fcs_lport_loop_offline(struct bfa_fcs_lport_s *port);
+
static struct {
void (*init) (struct bfa_fcs_lport_s *port);
void (*online) (struct bfa_fcs_lport_s *port);
void (*offline) (struct bfa_fcs_lport_s *port);
} __port_action[] = {
- {
- bfa_fcs_lport_unknown_init, bfa_fcs_lport_unknown_online,
- bfa_fcs_lport_unknown_offline}, {
- bfa_fcs_lport_fab_init, bfa_fcs_lport_fab_online,
- bfa_fcs_lport_fab_offline}, {
- bfa_fcs_lport_n2n_init, bfa_fcs_lport_n2n_online,
- bfa_fcs_lport_n2n_offline},
- };
-
-/*
- * fcs_port_sm FCS logical port state machine
- */
-
-enum bfa_fcs_lport_event {
- BFA_FCS_PORT_SM_CREATE = 1,
- BFA_FCS_PORT_SM_ONLINE = 2,
- BFA_FCS_PORT_SM_OFFLINE = 3,
- BFA_FCS_PORT_SM_DELETE = 4,
- BFA_FCS_PORT_SM_DELRPORT = 5,
+ [BFA_FCS_FABRIC_UNKNOWN] = {
+ .init = bfa_fcs_lport_unknown_init,
+ .online = bfa_fcs_lport_unknown_online,
+ .offline = bfa_fcs_lport_unknown_offline
+ },
+ [BFA_FCS_FABRIC_SWITCHED] = {
+ .init = bfa_fcs_lport_fab_init,
+ .online = bfa_fcs_lport_fab_online,
+ .offline = bfa_fcs_lport_fab_offline
+ },
+ [BFA_FCS_FABRIC_N2N] = {
+ .init = bfa_fcs_lport_n2n_init,
+ .online = bfa_fcs_lport_n2n_online,
+ .offline = bfa_fcs_lport_n2n_offline
+ },
+ [BFA_FCS_FABRIC_LOOP] = {
+ .init = bfa_fcs_lport_loop_init,
+ .online = bfa_fcs_lport_loop_online,
+ .offline = bfa_fcs_lport_loop_offline
+ },
};
static void bfa_fcs_lport_sm_uninit(struct bfa_fcs_lport_s *port,
@@ -86,6 +113,8 @@ static void bfa_fcs_lport_sm_offline(struct bfa_fcs_lport_s *port,
enum bfa_fcs_lport_event event);
static void bfa_fcs_lport_sm_deleting(struct bfa_fcs_lport_s *port,
enum bfa_fcs_lport_event event);
+static void bfa_fcs_lport_sm_stopping(struct bfa_fcs_lport_s *port,
+ enum bfa_fcs_lport_event event);
static void
bfa_fcs_lport_sm_uninit(
@@ -123,6 +152,14 @@ bfa_fcs_lport_sm_init(struct bfa_fcs_lport_s *port,
bfa_fcs_lport_deleted(port);
break;
+ case BFA_FCS_PORT_SM_STOP:
+ /* If vport - send completion call back */
+ if (port->vport)
+ bfa_fcs_vport_stop_comp(port->vport);
+ else
+ bfa_wc_down(&(port->fabric->stop_wc));
+ break;
+
case BFA_FCS_PORT_SM_OFFLINE:
break;
@@ -148,6 +185,25 @@ bfa_fcs_lport_sm_online(
bfa_fcs_lport_offline_actions(port);
break;
+ case BFA_FCS_PORT_SM_STOP:
+ __port_action[port->fabric->fab_type].offline(port);
+
+ if (port->num_rports == 0) {
+ bfa_sm_set_state(port, bfa_fcs_lport_sm_init);
+ /* If vport - send completion call back */
+ if (port->vport)
+ bfa_fcs_vport_stop_comp(port->vport);
+ else
+ bfa_wc_down(&(port->fabric->stop_wc));
+ } else {
+ bfa_sm_set_state(port, bfa_fcs_lport_sm_stopping);
+ list_for_each_safe(qe, qen, &port->rport_q) {
+ rport = (struct bfa_fcs_rport_s *) qe;
+ bfa_sm_send_event(rport, RPSM_EVENT_DELETE);
+ }
+ }
+ break;
+
case BFA_FCS_PORT_SM_DELETE:
__port_action[port->fabric->fab_type].offline(port);
@@ -189,6 +245,23 @@ bfa_fcs_lport_sm_offline(
bfa_fcs_lport_online_actions(port);
break;
+ case BFA_FCS_PORT_SM_STOP:
+ if (port->num_rports == 0) {
+ bfa_sm_set_state(port, bfa_fcs_lport_sm_init);
+ /* If vport - send completion call back */
+ if (port->vport)
+ bfa_fcs_vport_stop_comp(port->vport);
+ else
+ bfa_wc_down(&(port->fabric->stop_wc));
+ } else {
+ bfa_sm_set_state(port, bfa_fcs_lport_sm_stopping);
+ list_for_each_safe(qe, qen, &port->rport_q) {
+ rport = (struct bfa_fcs_rport_s *) qe;
+ bfa_sm_send_event(rport, RPSM_EVENT_DELETE);
+ }
+ }
+ break;
+
case BFA_FCS_PORT_SM_DELETE:
if (port->num_rports == 0) {
bfa_sm_set_state(port, bfa_fcs_lport_sm_uninit);
@@ -212,6 +285,30 @@ bfa_fcs_lport_sm_offline(
}
static void
+bfa_fcs_lport_sm_stopping(struct bfa_fcs_lport_s *port,
+ enum bfa_fcs_lport_event event)
+{
+ bfa_trc(port->fcs, port->port_cfg.pwwn);
+ bfa_trc(port->fcs, event);
+
+ switch (event) {
+ case BFA_FCS_PORT_SM_DELRPORT:
+ if (port->num_rports == 0) {
+ bfa_sm_set_state(port, bfa_fcs_lport_sm_init);
+ /* If vport - send completion call back */
+ if (port->vport)
+ bfa_fcs_vport_stop_comp(port->vport);
+ else
+ bfa_wc_down(&(port->fabric->stop_wc));
+ }
+ break;
+
+ default:
+ bfa_sm_fault(port->fcs, event);
+ }
+}
+
+static void
bfa_fcs_lport_sm_deleting(
struct bfa_fcs_lport_s *port,
enum bfa_fcs_lport_event event)
@@ -237,6 +334,31 @@ bfa_fcs_lport_sm_deleting(
*/
/*
+ * Send AEN notification
+ */
+static void
+bfa_fcs_lport_aen_post(struct bfa_fcs_lport_s *port,
+ enum bfa_lport_aen_event event)
+{
+ struct bfad_s *bfad = (struct bfad_s *)port->fabric->fcs->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ aen_entry->aen_data.lport.vf_id = port->fabric->vf_id;
+ aen_entry->aen_data.lport.roles = port->port_cfg.roles;
+ aen_entry->aen_data.lport.ppwwn = bfa_fcs_lport_get_pwwn(
+ bfa_fcs_get_base_port(port->fcs));
+ aen_entry->aen_data.lport.lpwwn = bfa_fcs_lport_get_pwwn(port);
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++port->fcs->fcs_aen_seq,
+ BFA_AEN_CAT_LPORT, event);
+}
+
+/*
* Send a LS reject
*/
static void
@@ -251,7 +373,7 @@ bfa_fcs_lport_send_ls_rjt(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs,
bfa_trc(port->fcs, rx_fchs->d_id);
bfa_trc(port->fcs, rx_fchs->s_id);
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -265,6 +387,40 @@ bfa_fcs_lport_send_ls_rjt(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs,
}
/*
+ * Send a FCCT Reject
+ */
+static void
+bfa_fcs_lport_send_fcgs_rjt(struct bfa_fcs_lport_s *port,
+ struct fchs_s *rx_fchs, u8 reason_code, u8 reason_code_expl)
+{
+ struct fchs_s fchs;
+ struct bfa_fcxp_s *fcxp;
+ struct bfa_rport_s *bfa_rport = NULL;
+ int len;
+ struct ct_hdr_s *rx_cthdr = (struct ct_hdr_s *)(rx_fchs + 1);
+ struct ct_hdr_s *ct_hdr;
+
+ bfa_trc(port->fcs, rx_fchs->d_id);
+ bfa_trc(port->fcs, rx_fchs->s_id);
+
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
+ if (!fcxp)
+ return;
+
+ ct_hdr = bfa_fcxp_get_reqbuf(fcxp);
+ ct_hdr->gs_type = rx_cthdr->gs_type;
+ ct_hdr->gs_sub_type = rx_cthdr->gs_sub_type;
+
+ len = fc_gs_rjt_build(&fchs, ct_hdr, rx_fchs->s_id,
+ bfa_fcs_lport_get_fcid(port),
+ rx_fchs->ox_id, reason_code, reason_code_expl);
+
+ bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag,
+ BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL,
+ FC_MAX_PDUSZ, 0);
+}
+
+/*
* Process incoming plogi from a remote port.
*/
static void
@@ -384,7 +540,7 @@ bfa_fcs_lport_echo(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs,
bfa_trc(port->fcs, rx_fchs->s_id);
bfa_trc(port->fcs, rx_fchs->d_id);
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -429,7 +585,7 @@ bfa_fcs_lport_rnid(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs,
bfa_trc(port->fcs, rx_fchs->d_id);
bfa_trc(port->fcs, rx_len);
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -493,9 +649,10 @@ bfa_fcs_lport_online_actions(struct bfa_fcs_lport_s *port)
__port_action[port->fabric->fab_type].online(port);
wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(port));
- BFA_LOG(KERN_INFO, bfad, bfa_log_level,
+ BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
"Logical port online: WWN = %s Role = %s\n",
lpwwn_buf, "Initiator");
+ bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_ONLINE);
bfad->bfad_flags |= BFAD_PORT_ONLINE;
}
@@ -514,14 +671,17 @@ bfa_fcs_lport_offline_actions(struct bfa_fcs_lport_s *port)
wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(port));
if (bfa_sm_cmp_state(port->fabric,
- bfa_fcs_fabric_sm_online) == BFA_TRUE)
- BFA_LOG(KERN_ERR, bfad, bfa_log_level,
+ bfa_fcs_fabric_sm_online) == BFA_TRUE) {
+ BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
"Logical port lost fabric connectivity: WWN = %s Role = %s\n",
lpwwn_buf, "Initiator");
- else
- BFA_LOG(KERN_INFO, bfad, bfa_log_level,
+ bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_DISCONNECT);
+ } else {
+ BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
"Logical port taken offline: WWN = %s Role = %s\n",
lpwwn_buf, "Initiator");
+ bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_OFFLINE);
+ }
list_for_each_safe(qe, qen, &port->rport_q) {
rport = (struct bfa_fcs_rport_s *) qe;
@@ -557,7 +717,7 @@ bfa_fcs_lport_abts_acc(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs)
bfa_trc(port->fcs, rx_fchs->d_id);
bfa_trc(port->fcs, rx_fchs->s_id);
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -579,16 +739,13 @@ bfa_fcs_lport_deleted(struct bfa_fcs_lport_s *port)
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Logical port deleted: WWN = %s Role = %s\n",
lpwwn_buf, "Initiator");
+ bfa_fcs_lport_aen_post(port, BFA_LPORT_AEN_DELETE);
/* Base port will be deleted by the OS driver */
- if (port->vport) {
- bfa_fcb_lport_delete(port->fcs->bfad, port->port_cfg.roles,
- port->fabric->vf_drv,
- port->vport ? port->vport->vport_drv : NULL);
+ if (port->vport)
bfa_fcs_vport_delete_comp(port->vport);
- } else {
+ else
bfa_wc_down(&port->fabric->wc);
- }
}
@@ -607,7 +764,20 @@ bfa_fcs_lport_uf_recv(struct bfa_fcs_lport_s *lport,
bfa_trc(lport->fcs, fchs->type);
if (!bfa_fcs_lport_is_online(lport)) {
- bfa_stats(lport, uf_recv_drops);
+ /*
+ * In direct attach topology, it is possible to get a PLOGI
+ * before the lport is online due to port feature
+ * (QoS/Trunk/FEC/CR), so send a rjt
+ */
+ if ((fchs->type == FC_TYPE_ELS) &&
+ (els_cmd->els_code == FC_ELS_PLOGI)) {
+ bfa_fcs_lport_send_ls_rjt(lport, fchs,
+ FC_LS_RJT_RSN_UNABLE_TO_PERF_CMD,
+ FC_LS_RJT_EXP_NO_ADDL_INFO);
+ bfa_stats(lport, plogi_rcvd);
+ } else
+ bfa_stats(lport, uf_recv_drops);
+
return;
}
@@ -647,6 +817,16 @@ bfa_fcs_lport_uf_recv(struct bfa_fcs_lport_s *lport,
bfa_fcs_lport_abts_acc(lport, fchs);
return;
}
+
+ if (fchs->type == FC_TYPE_SERVICES) {
+ /*
+ * Unhandled FC-GS frames. Send a FC-CT Reject
+ */
+ bfa_fcs_lport_send_fcgs_rjt(lport, fchs, CT_RSN_NOT_SUPP,
+ CT_NS_EXP_NOADDITIONAL);
+ return;
+ }
+
/*
* look for a matching remote port ID
*/
@@ -720,6 +900,25 @@ bfa_fcs_lport_get_rport_by_pid(struct bfa_fcs_lport_s *port, u32 pid)
}
/*
+ * OLD_PID based Lookup for a R-Port in the Port R-Port Queue
+ */
+struct bfa_fcs_rport_s *
+bfa_fcs_lport_get_rport_by_old_pid(struct bfa_fcs_lport_s *port, u32 pid)
+{
+ struct bfa_fcs_rport_s *rport;
+ struct list_head *qe;
+
+ list_for_each(qe, &port->rport_q) {
+ rport = (struct bfa_fcs_rport_s *) qe;
+ if (rport->old_pid == pid)
+ return rport;
+ }
+
+ bfa_trc(port->fcs, pid);
+ return NULL;
+}
+
+/*
* PWWN based Lookup for a R-Port in the Port R-Port Queue
*/
struct bfa_fcs_rport_s *
@@ -739,21 +938,22 @@ bfa_fcs_lport_get_rport_by_pwwn(struct bfa_fcs_lport_s *port, wwn_t pwwn)
}
/*
- * NWWN based Lookup for a R-Port in the Port R-Port Queue
+ * PWWN & PID based Lookup for a R-Port in the Port R-Port Queue
*/
struct bfa_fcs_rport_s *
-bfa_fcs_lport_get_rport_by_nwwn(struct bfa_fcs_lport_s *port, wwn_t nwwn)
+bfa_fcs_lport_get_rport_by_qualifier(struct bfa_fcs_lport_s *port,
+ wwn_t pwwn, u32 pid)
{
struct bfa_fcs_rport_s *rport;
struct list_head *qe;
list_for_each(qe, &port->rport_q) {
rport = (struct bfa_fcs_rport_s *) qe;
- if (wwn_is_equal(rport->nwwn, nwwn))
+ if (wwn_is_equal(rport->pwwn, pwwn) && rport->pid == pid)
return rport;
}
- bfa_trc(port->fcs, nwwn);
+ bfa_trc(port->fcs, pwwn);
return NULL;
}
@@ -805,6 +1005,16 @@ bfa_fcs_lport_offline(struct bfa_fcs_lport_s *port)
}
/*
+ * Called by fabric for base port and by vport for virtual ports
+ * when target mode driver is unloaded.
+ */
+void
+bfa_fcs_lport_stop(struct bfa_fcs_lport_s *port)
+{
+ bfa_sm_send_event(port, BFA_FCS_PORT_SM_STOP);
+}
+
+/*
* Called by fabric to delete base lport and associated resources.
*
* Called by vport to delete lport and associated resources. Should call
@@ -835,8 +1045,8 @@ bfa_fcs_lport_attach(struct bfa_fcs_lport_s *lport, struct bfa_fcs_s *fcs,
lport->fcs = fcs;
lport->fabric = bfa_fcs_vf_lookup(fcs, vf_id);
lport->vport = vport;
- lport->lp_tag = (vport) ? vport->lps->lp_tag :
- lport->fabric->lps->lp_tag;
+ lport->lp_tag = (vport) ? vport->lps->bfa_tag :
+ lport->fabric->lps->bfa_tag;
INIT_LIST_HEAD(&lport->rport_q);
lport->num_rports = 0;
@@ -866,11 +1076,23 @@ bfa_fcs_lport_init(struct bfa_fcs_lport_s *lport,
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"New logical port created: WWN = %s Role = %s\n",
lpwwn_buf, "Initiator");
+ bfa_fcs_lport_aen_post(lport, BFA_LPORT_AEN_NEW);
bfa_sm_set_state(lport, bfa_fcs_lport_sm_uninit);
bfa_sm_send_event(lport, BFA_FCS_PORT_SM_CREATE);
}
+void
+bfa_fcs_lport_set_symname(struct bfa_fcs_lport_s *port,
+ char *symname)
+{
+ strcpy(port->port_cfg.sym_name.symname, symname);
+
+ if (bfa_sm_cmp_state(port, bfa_fcs_lport_sm_online))
+ bfa_fcs_lport_ns_util_send_rspn_id(
+ BFA_FCS_GET_NS_FROM_PORT(port), NULL);
+}
+
/*
* fcs_lport_api
*/
@@ -935,7 +1157,7 @@ static void
bfa_fcs_lport_fab_online(struct bfa_fcs_lport_s *port)
{
bfa_fcs_lport_ns_online(port);
- bfa_fcs_lport_scn_online(port);
+ bfa_fcs_lport_fab_scn_online(port);
}
/*
@@ -1029,6 +1251,98 @@ bfa_fcs_lport_n2n_offline(struct bfa_fcs_lport_s *port)
n2n_port->reply_oxid = 0;
}
+static void
+bfa_fcport_get_loop_attr(struct bfa_fcs_lport_s *port)
+{
+ int i = 0, j = 0, bit = 0, alpa_bit = 0;
+ u8 k = 0;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(port->fcs->bfa);
+
+ port->port_topo.ploop.alpabm_valid = fcport->alpabm_valid;
+ port->pid = fcport->myalpa;
+ port->pid = bfa_hton3b(port->pid);
+
+ for (i = 0; i < (FC_ALPA_MAX / 8); i++) {
+ for (j = 0, alpa_bit = 0; j < 8; j++, alpa_bit++) {
+ bfa_trc(port->fcs->bfa, fcport->alpabm.alpa_bm[i]);
+ bit = (fcport->alpabm.alpa_bm[i] & (1 << (7 - j)));
+ if (bit) {
+ port->port_topo.ploop.alpa_pos_map[k] =
+ loop_alpa_map[(i * 8) + alpa_bit];
+ k++;
+ bfa_trc(port->fcs->bfa, k);
+ bfa_trc(port->fcs->bfa,
+ port->port_topo.ploop.alpa_pos_map[k]);
+ }
+ }
+ }
+ port->port_topo.ploop.num_alpa = k;
+}
+
+/*
+ * Called by fcs/port to initialize Loop topology.
+ */
+static void
+bfa_fcs_lport_loop_init(struct bfa_fcs_lport_s *port)
+{
+}
+
+/*
+ * Called by fcs/port to notify transition to online state.
+ */
+static void
+bfa_fcs_lport_loop_online(struct bfa_fcs_lport_s *port)
+{
+ u8 num_alpa = 0, alpabm_valid = 0;
+ struct bfa_fcs_rport_s *rport;
+ u8 *alpa_map = NULL;
+ int i = 0;
+ u32 pid;
+
+ bfa_fcport_get_loop_attr(port);
+
+ num_alpa = port->port_topo.ploop.num_alpa;
+ alpabm_valid = port->port_topo.ploop.alpabm_valid;
+ alpa_map = port->port_topo.ploop.alpa_pos_map;
+
+ bfa_trc(port->fcs->bfa, port->pid);
+ bfa_trc(port->fcs->bfa, num_alpa);
+ if (alpabm_valid == 1) {
+ for (i = 0; i < num_alpa; i++) {
+ bfa_trc(port->fcs->bfa, alpa_map[i]);
+ if (alpa_map[i] != bfa_hton3b(port->pid)) {
+ pid = alpa_map[i];
+ bfa_trc(port->fcs->bfa, pid);
+ rport = bfa_fcs_lport_get_rport_by_pid(port,
+ bfa_hton3b(pid));
+ if (!rport)
+ rport = bfa_fcs_rport_create(port,
+ bfa_hton3b(pid));
+ }
+ }
+ } else {
+ for (i = 0; i < MAX_ALPA_COUNT; i++) {
+ if (alpa_map[i] != port->pid) {
+ pid = loop_alpa_map[i];
+ bfa_trc(port->fcs->bfa, pid);
+ rport = bfa_fcs_lport_get_rport_by_pid(port,
+ bfa_hton3b(pid));
+ if (!rport)
+ rport = bfa_fcs_rport_create(port,
+ bfa_hton3b(pid));
+ }
+ }
+ }
+}
+
+/*
+ * Called by fcs/port to notify transition to offline state.
+ */
+static void
+bfa_fcs_lport_loop_offline(struct bfa_fcs_lport_s *port)
+{
+}
+
#define BFA_FCS_FDMI_CMD_MAX_RETRIES 2
/*
@@ -1062,7 +1376,7 @@ static void bfa_fcs_lport_fdmi_rpa_response(void *fcsarg,
u32 resid_len,
struct fchs_s *rsp_fchs);
static void bfa_fcs_lport_fdmi_timeout(void *arg);
-static u16 bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi,
+static int bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi,
u8 *pyld);
static u16 bfa_fcs_lport_fdmi_build_rprt_pyld(struct bfa_fcs_lport_fdmi_s *fdmi,
u8 *pyld);
@@ -1074,23 +1388,11 @@ static void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi,
struct bfa_fcs_fdmi_hba_attr_s *hba_attr);
static void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi,
struct bfa_fcs_fdmi_port_attr_s *port_attr);
-/*
- * fcs_fdmi_sm FCS FDMI state machine
- */
+u32 bfa_fcs_fdmi_convert_speed(enum bfa_port_speed pport_speed);
/*
- * FDMI State Machine events
+ * fcs_fdmi_sm FCS FDMI state machine
*/
-enum port_fdmi_event {
- FDMISM_EVENT_PORT_ONLINE = 1,
- FDMISM_EVENT_PORT_OFFLINE = 2,
- FDMISM_EVENT_RSP_OK = 4,
- FDMISM_EVENT_RSP_ERROR = 5,
- FDMISM_EVENT_TIMEOUT = 6,
- FDMISM_EVENT_RHBA_SENT = 7,
- FDMISM_EVENT_RPRT_SENT = 8,
- FDMISM_EVENT_RPA_SENT = 9,
-};
static void bfa_fcs_lport_fdmi_sm_offline(struct bfa_fcs_lport_fdmi_s *fdmi,
enum port_fdmi_event event);
@@ -1520,10 +1822,11 @@ bfa_fcs_lport_fdmi_send_rhba(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->port_cfg.pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe,
- bfa_fcs_lport_fdmi_send_rhba, fdmi);
+ bfa_fcs_lport_fdmi_send_rhba, fdmi, BFA_TRUE);
return;
}
fdmi->fcxp = fcxp;
@@ -1538,6 +1841,8 @@ bfa_fcs_lport_fdmi_send_rhba(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_fcs_lport_fdmi_build_rhba_pyld(fdmi,
(u8 *) ((struct ct_hdr_s *) pyld
+ 1));
+ if (attr_len < 0)
+ return;
bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
FC_CLASS_3, (len + attr_len), &fchs,
@@ -1547,17 +1852,20 @@ bfa_fcs_lport_fdmi_send_rhba(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_sm_send_event(fdmi, FDMISM_EVENT_RHBA_SENT);
}
-static u16
+static int
bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi, u8 *pyld)
{
struct bfa_fcs_lport_s *port = fdmi->ms->port;
- struct bfa_fcs_fdmi_hba_attr_s hba_attr;
- struct bfa_fcs_fdmi_hba_attr_s *fcs_hba_attr = &hba_attr;
+ struct bfa_fcs_fdmi_hba_attr_s *fcs_hba_attr;
struct fdmi_rhba_s *rhba = (struct fdmi_rhba_s *) pyld;
struct fdmi_attr_s *attr;
+ int len;
u8 *curr_ptr;
- u16 len, count;
- u16 templen;
+ u16 templen, count;
+
+ fcs_hba_attr = kzalloc(sizeof(*fcs_hba_attr), GFP_KERNEL);
+ if (!fcs_hba_attr)
+ return -ENOMEM;
/*
* get hba attributes
@@ -1672,7 +1980,7 @@ bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi, u8 *pyld)
memcpy(attr->value, fcs_hba_attr->driver_version, templen);
templen = fc_roundup(templen, sizeof(u32));
curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
- len += templen;;
+ len += templen;
count++;
attr->len = cpu_to_be16(templen + sizeof(attr->type) +
sizeof(templen));
@@ -1693,13 +2001,10 @@ bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi, u8 *pyld)
sizeof(templen));
}
- /*
- * f/w Version = driver version
- */
attr = (struct fdmi_attr_s *) curr_ptr;
attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_FW_VERSION);
- templen = (u16) strlen(fcs_hba_attr->driver_version);
- memcpy(attr->value, fcs_hba_attr->driver_version, templen);
+ templen = (u16) strlen(fcs_hba_attr->fw_version);
+ memcpy(attr->value, fcs_hba_attr->fw_version, templen);
templen = fc_roundup(templen, sizeof(u32));
curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
len += templen;
@@ -1730,10 +2035,71 @@ bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi, u8 *pyld)
attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_MAX_CT);
templen = sizeof(fcs_hba_attr->max_ct_pyld);
memcpy(attr->value, &fcs_hba_attr->max_ct_pyld, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
len += templen;
count++;
attr->len = cpu_to_be16(templen + sizeof(attr->type) +
sizeof(templen));
+ /*
+ * Send extended attributes ( FOS 7.1 support )
+ */
+ if (fdmi->retry_cnt == 0) {
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_NODE_SYM_NAME);
+ templen = sizeof(fcs_hba_attr->node_sym_name);
+ memcpy(attr->value, &fcs_hba_attr->node_sym_name, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ count++;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_VENDOR_ID);
+ templen = sizeof(fcs_hba_attr->vendor_info);
+ memcpy(attr->value, &fcs_hba_attr->vendor_info, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ count++;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_NUM_PORTS);
+ templen = sizeof(fcs_hba_attr->num_ports);
+ memcpy(attr->value, &fcs_hba_attr->num_ports, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ count++;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_FABRIC_NAME);
+ templen = sizeof(fcs_hba_attr->fabric_name);
+ memcpy(attr->value, &fcs_hba_attr->fabric_name, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ count++;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_BIOS_VER);
+ templen = sizeof(fcs_hba_attr->bios_ver);
+ memcpy(attr->value, &fcs_hba_attr->bios_ver, templen);
+ templen = fc_roundup(attr->len, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ count++;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+ }
/*
* Update size of payload
@@ -1741,6 +2107,9 @@ bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi, u8 *pyld)
len += ((sizeof(attr->type) + sizeof(attr->len)) * count);
rhba->hba_attr_blk.attr_count = cpu_to_be32(count);
+
+ kfree(fcs_hba_attr);
+
return len;
}
@@ -1794,10 +2163,11 @@ bfa_fcs_lport_fdmi_send_rprt(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->port_cfg.pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe,
- bfa_fcs_lport_fdmi_send_rprt, fdmi);
+ bfa_fcs_lport_fdmi_send_rprt, fdmi, BFA_TRUE);
return;
}
fdmi->fcxp = fcxp;
@@ -1933,6 +2303,113 @@ bfa_fcs_lport_fdmi_build_portattr_block(struct bfa_fcs_lport_fdmi_s *fdmi,
sizeof(templen));
}
+ if (fdmi->retry_cnt == 0) {
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_NODE_NAME);
+ templen = sizeof(fcs_port_attr.node_name);
+ memcpy(attr->value, &fcs_port_attr.node_name, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_PORT_NAME);
+ templen = sizeof(fcs_port_attr.port_name);
+ memcpy(attr->value, &fcs_port_attr.port_name, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(attr->len) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ if (fcs_port_attr.port_sym_name.symname[0] != '\0') {
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type =
+ cpu_to_be16(FDMI_PORT_ATTRIB_PORT_SYM_NAME);
+ templen = sizeof(fcs_port_attr.port_sym_name);
+ memcpy(attr->value,
+ &fcs_port_attr.port_sym_name, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) +
+ sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen +
+ sizeof(attr->type) + sizeof(templen));
+ }
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_PORT_TYPE);
+ templen = sizeof(fcs_port_attr.port_type);
+ memcpy(attr->value, &fcs_port_attr.port_type, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_SUPP_COS);
+ templen = sizeof(fcs_port_attr.scos);
+ memcpy(attr->value, &fcs_port_attr.scos, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_PORT_FAB_NAME);
+ templen = sizeof(fcs_port_attr.port_fabric_name);
+ memcpy(attr->value, &fcs_port_attr.port_fabric_name, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_PORT_FC4_TYPE);
+ templen = sizeof(fcs_port_attr.port_act_fc4_type);
+ memcpy(attr->value, fcs_port_attr.port_act_fc4_type,
+ templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_PORT_STATE);
+ templen = sizeof(fcs_port_attr.port_state);
+ memcpy(attr->value, &fcs_port_attr.port_state, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+
+ attr = (struct fdmi_attr_s *) curr_ptr;
+ attr->type = cpu_to_be16(FDMI_PORT_ATTRIB_PORT_NUM_RPRT);
+ templen = sizeof(fcs_port_attr.num_ports);
+ memcpy(attr->value, &fcs_port_attr.num_ports, templen);
+ templen = fc_roundup(templen, sizeof(u32));
+ curr_ptr += sizeof(attr->type) + sizeof(templen) + templen;
+ len += templen;
+ ++count;
+ attr->len = cpu_to_be16(templen + sizeof(attr->type) +
+ sizeof(templen));
+ }
+
/*
* Update size of payload
*/
@@ -2009,10 +2486,11 @@ bfa_fcs_lport_fdmi_send_rpa(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->port_cfg.pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe,
- bfa_fcs_lport_fdmi_send_rpa, fdmi);
+ bfa_fcs_lport_fdmi_send_rpa, fdmi, BFA_TRUE);
return;
}
fdmi->fcxp = fcxp;
@@ -2099,6 +2577,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi,
{
struct bfa_fcs_lport_s *port = fdmi->ms->port;
struct bfa_fcs_driver_info_s *driver_info = &port->fcs->driver_info;
+ struct bfa_fcs_fdmi_port_attr_s fcs_port_attr;
memset(hba_attr, 0, sizeof(struct bfa_fcs_fdmi_hba_attr_s));
@@ -2117,10 +2596,10 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi,
bfa_ioc_get_adapter_fw_ver(&port->fcs->bfa->ioc,
hba_attr->fw_version);
- strncpy(hba_attr->driver_version, (char *)driver_info->version,
+ strscpy(hba_attr->driver_version, (char *)driver_info->version,
sizeof(hba_attr->driver_version));
- strncpy(hba_attr->os_name, driver_info->host_os_name,
+ strscpy(hba_attr->os_name, driver_info->host_os_name,
sizeof(hba_attr->os_name));
/*
@@ -2128,13 +2607,24 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi,
* to the os name along with a separator
*/
if (driver_info->host_os_patch[0] != '\0') {
- strncat(hba_attr->os_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
- sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
- strncat(hba_attr->os_name, driver_info->host_os_patch,
- sizeof(driver_info->host_os_patch));
+ strlcat(hba_attr->os_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
+ sizeof(hba_attr->os_name));
+ strlcat(hba_attr->os_name, driver_info->host_os_patch,
+ sizeof(hba_attr->os_name));
}
- hba_attr->max_ct_pyld = cpu_to_be32(FC_MAX_PDUSZ);
+ /* Retrieve the max frame size from the port attr */
+ bfa_fcs_fdmi_get_portattr(fdmi, &fcs_port_attr);
+ hba_attr->max_ct_pyld = fcs_port_attr.max_frm_size;
+
+ strscpy(hba_attr->node_sym_name.symname,
+ port->port_cfg.node_sym_name.symname, BFA_SYMNAME_MAXLEN);
+ strcpy(hba_attr->vendor_info, "QLogic");
+ hba_attr->num_ports =
+ cpu_to_be32(bfa_ioc_get_nports(&port->fcs->bfa->ioc));
+ hba_attr->fabric_name = port->fabric->lps->pr_nwwn;
+ strscpy(hba_attr->bios_ver, hba_attr->option_rom_ver, BFA_VERSION_LEN);
+
}
static void
@@ -2144,6 +2634,7 @@ bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi,
struct bfa_fcs_lport_s *port = fdmi->ms->port;
struct bfa_fcs_driver_info_s *driver_info = &port->fcs->driver_info;
struct bfa_port_attr_s pport_attr;
+ struct bfa_lport_attr_s lport_attr;
memset(port_attr, 0, sizeof(struct bfa_fcs_fdmi_port_attr_s));
@@ -2160,32 +2651,103 @@ bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi,
/*
* Supported Speeds
*/
- port_attr->supp_speed = cpu_to_be32(BFA_FCS_FDMI_SUPORTED_SPEEDS);
+ switch (pport_attr.speed_supported) {
+ case BFA_PORT_SPEED_16GBPS:
+ port_attr->supp_speed =
+ cpu_to_be32(BFA_FCS_FDMI_SUPP_SPEEDS_16G);
+ break;
+
+ case BFA_PORT_SPEED_10GBPS:
+ port_attr->supp_speed =
+ cpu_to_be32(BFA_FCS_FDMI_SUPP_SPEEDS_10G);
+ break;
+
+ case BFA_PORT_SPEED_8GBPS:
+ port_attr->supp_speed =
+ cpu_to_be32(BFA_FCS_FDMI_SUPP_SPEEDS_8G);
+ break;
+
+ case BFA_PORT_SPEED_4GBPS:
+ port_attr->supp_speed =
+ cpu_to_be32(BFA_FCS_FDMI_SUPP_SPEEDS_4G);
+ break;
+
+ default:
+ bfa_sm_fault(port->fcs, pport_attr.speed_supported);
+ }
/*
* Current Speed
*/
- port_attr->curr_speed = cpu_to_be32(pport_attr.speed);
+ port_attr->curr_speed = cpu_to_be32(
+ bfa_fcs_fdmi_convert_speed(pport_attr.speed));
/*
* Max PDU Size.
*/
- port_attr->max_frm_size = cpu_to_be32(FC_MAX_PDUSZ);
+ port_attr->max_frm_size = cpu_to_be32(pport_attr.pport_cfg.maxfrsize);
/*
* OS device Name
*/
- strncpy(port_attr->os_device_name, (char *)driver_info->os_device_name,
+ strscpy(port_attr->os_device_name, driver_info->os_device_name,
sizeof(port_attr->os_device_name));
/*
* Host name
*/
- strncpy(port_attr->host_name, (char *)driver_info->host_machine_name,
+ strscpy(port_attr->host_name, driver_info->host_machine_name,
sizeof(port_attr->host_name));
+ port_attr->node_name = bfa_fcs_lport_get_nwwn(port);
+ port_attr->port_name = bfa_fcs_lport_get_pwwn(port);
+
+ strscpy(port_attr->port_sym_name.symname,
+ bfa_fcs_lport_get_psym_name(port).symname, BFA_SYMNAME_MAXLEN);
+ bfa_fcs_lport_get_attr(port, &lport_attr);
+ port_attr->port_type = cpu_to_be32(lport_attr.port_type);
+ port_attr->scos = pport_attr.cos_supported;
+ port_attr->port_fabric_name = port->fabric->lps->pr_nwwn;
+ fc_get_fc4type_bitmask(FC_TYPE_FCP, port_attr->port_act_fc4_type);
+ port_attr->port_state = cpu_to_be32(pport_attr.port_state);
+ port_attr->num_ports = cpu_to_be32(port->num_rports);
}
+/*
+ * Convert BFA speed to FDMI format.
+ */
+u32
+bfa_fcs_fdmi_convert_speed(bfa_port_speed_t pport_speed)
+{
+ u32 ret;
+
+ switch (pport_speed) {
+ case BFA_PORT_SPEED_1GBPS:
+ case BFA_PORT_SPEED_2GBPS:
+ ret = pport_speed;
+ break;
+
+ case BFA_PORT_SPEED_4GBPS:
+ ret = FDMI_TRANS_SPEED_4G;
+ break;
+
+ case BFA_PORT_SPEED_8GBPS:
+ ret = FDMI_TRANS_SPEED_8G;
+ break;
+
+ case BFA_PORT_SPEED_10GBPS:
+ ret = FDMI_TRANS_SPEED_10G;
+ break;
+
+ case BFA_PORT_SPEED_16GBPS:
+ ret = FDMI_TRANS_SPEED_16G;
+ break;
+
+ default:
+ ret = FDMI_TRANS_SPEED_UNKNOWN;
+ }
+ return ret;
+}
void
bfa_fcs_lport_fdmi_init(struct bfa_fcs_lport_ms_s *ms)
@@ -2255,19 +2817,6 @@ static void bfa_fcs_lport_ms_gfn_response(void *fcsarg,
* fcs_ms_sm FCS MS state machine
*/
-/*
- * MS State Machine events
- */
-enum port_ms_event {
- MSSM_EVENT_PORT_ONLINE = 1,
- MSSM_EVENT_PORT_OFFLINE = 2,
- MSSM_EVENT_RSP_OK = 3,
- MSSM_EVENT_RSP_ERROR = 4,
- MSSM_EVENT_TIMEOUT = 5,
- MSSM_EVENT_FCXP_SENT = 6,
- MSSM_EVENT_PORT_FABRIC_RSCN = 7
-};
-
static void bfa_fcs_lport_ms_sm_offline(struct bfa_fcs_lport_ms_s *ms,
enum port_ms_event event);
static void bfa_fcs_lport_ms_sm_plogi_sending(struct bfa_fcs_lport_ms_s *ms,
@@ -2540,10 +3089,11 @@ bfa_fcs_lport_ms_send_gmal(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->pid);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe,
- bfa_fcs_lport_ms_send_gmal, ms);
+ bfa_fcs_lport_ms_send_gmal, ms, BFA_TRUE);
return;
}
ms->fcxp = fcxp;
@@ -2620,7 +3170,7 @@ bfa_fcs_lport_ms_gmal_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
rsp_str[gmal_entry->len-1] = 0;
/* copy IP Address to fabric */
- strncpy(bfa_fcs_lport_get_fabric_ipaddr(port),
+ strscpy(bfa_fcs_lport_get_fabric_ipaddr(port),
gmal_entry->ip_addr,
BFA_FCS_FABRIC_IPADDR_SZ);
break;
@@ -2740,10 +3290,11 @@ bfa_fcs_lport_ms_send_gfn(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->pid);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe,
- bfa_fcs_lport_ms_send_gfn, ms);
+ bfa_fcs_lport_ms_send_gfn, ms, BFA_TRUE);
return;
}
ms->fcxp = fcxp;
@@ -2816,11 +3367,12 @@ bfa_fcs_lport_ms_send_plogi(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->pid);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
port->stats.ms_plogi_alloc_wait++;
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe,
- bfa_fcs_lport_ms_send_plogi, ms);
+ bfa_fcs_lport_ms_send_plogi, ms, BFA_TRUE);
return;
}
ms->fcxp = fcxp;
@@ -2829,7 +3381,8 @@ bfa_fcs_lport_ms_send_plogi(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_hton3b(FC_MGMT_SERVER),
bfa_fcs_lport_get_fcid(port), 0,
port->port_cfg.pwwn, port->port_cfg.nwwn,
- bfa_fcport_get_maxfrsize(port->fcs->bfa));
+ bfa_fcport_get_maxfrsize(port->fcs->bfa),
+ bfa_fcport_get_rx_bbcredit(port->fcs->bfa));
bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
FC_CLASS_3, len, &fchs,
@@ -2969,6 +3522,10 @@ static void bfa_fcs_lport_ns_send_rff_id(void *ns_cbarg,
struct bfa_fcxp_s *fcxp_alloced);
static void bfa_fcs_lport_ns_send_gid_ft(void *ns_cbarg,
struct bfa_fcxp_s *fcxp_alloced);
+static void bfa_fcs_lport_ns_send_rnn_id(void *ns_cbarg,
+ struct bfa_fcxp_s *fcxp_alloced);
+static void bfa_fcs_lport_ns_send_rsnn_nn(void *ns_cbarg,
+ struct bfa_fcxp_s *fcxp_alloced);
static void bfa_fcs_lport_ns_timeout(void *arg);
static void bfa_fcs_lport_ns_plogi_response(void *fcsarg,
struct bfa_fcxp_s *fcxp,
@@ -3005,6 +3562,20 @@ static void bfa_fcs_lport_ns_gid_ft_response(void *fcsarg,
u32 rsp_len,
u32 resid_len,
struct fchs_s *rsp_fchs);
+static void bfa_fcs_lport_ns_rnn_id_response(void *fcsarg,
+ struct bfa_fcxp_s *fcxp,
+ void *cbarg,
+ bfa_status_t req_status,
+ u32 rsp_len,
+ u32 resid_len,
+ struct fchs_s *rsp_fchs);
+static void bfa_fcs_lport_ns_rsnn_nn_response(void *fcsarg,
+ struct bfa_fcxp_s *fcxp,
+ void *cbarg,
+ bfa_status_t req_status,
+ u32 rsp_len,
+ u32 resid_len,
+ struct fchs_s *rsp_fchs);
static void bfa_fcs_lport_ns_process_gidft_pids(
struct bfa_fcs_lport_s *port,
u32 *pid_buf, u32 n_pids);
@@ -3014,23 +3585,6 @@ static void bfa_fcs_lport_ns_boot_target_disc(bfa_fcs_lport_t *port);
* fcs_ns_sm FCS nameserver interface state machine
*/
-/*
- * VPort NS State Machine events
- */
-enum vport_ns_event {
- NSSM_EVENT_PORT_ONLINE = 1,
- NSSM_EVENT_PORT_OFFLINE = 2,
- NSSM_EVENT_PLOGI_SENT = 3,
- NSSM_EVENT_RSP_OK = 4,
- NSSM_EVENT_RSP_ERROR = 5,
- NSSM_EVENT_TIMEOUT = 6,
- NSSM_EVENT_NS_QUERY = 7,
- NSSM_EVENT_RSPNID_SENT = 8,
- NSSM_EVENT_RFTID_SENT = 9,
- NSSM_EVENT_RFFID_SENT = 10,
- NSSM_EVENT_GIDFT_SENT = 11,
-};
-
static void bfa_fcs_lport_ns_sm_offline(struct bfa_fcs_lport_ns_s *ns,
enum vport_ns_event event);
static void bfa_fcs_lport_ns_sm_plogi_sending(struct bfa_fcs_lport_ns_s *ns,
@@ -3069,6 +3623,21 @@ static void bfa_fcs_lport_ns_sm_gid_ft_retry(struct bfa_fcs_lport_ns_s *ns,
enum vport_ns_event event);
static void bfa_fcs_lport_ns_sm_online(struct bfa_fcs_lport_ns_s *ns,
enum vport_ns_event event);
+static void bfa_fcs_lport_ns_sm_sending_rnn_id(
+ struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event);
+static void bfa_fcs_lport_ns_sm_rnn_id(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event);
+static void bfa_fcs_lport_ns_sm_rnn_id_retry(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event);
+static void bfa_fcs_lport_ns_sm_sending_rsnn_nn(
+ struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event);
+static void bfa_fcs_lport_ns_sm_rsnn_nn(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event);
+static void bfa_fcs_lport_ns_sm_rsnn_nn_retry(
+ struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event);
/*
* Start in offline state - awaiting linkup
*/
@@ -3136,8 +3705,9 @@ bfa_fcs_lport_ns_sm_plogi(struct bfa_fcs_lport_ns_s *ns,
break;
case NSSM_EVENT_RSP_OK:
- bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_sending_rspn_id);
- bfa_fcs_lport_ns_send_rspn_id(ns, NULL);
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_sending_rnn_id);
+ ns->num_rnnid_retries = 0;
+ bfa_fcs_lport_ns_send_rnn_id(ns, NULL);
break;
case NSSM_EVENT_PORT_OFFLINE:
@@ -3177,6 +3747,176 @@ bfa_fcs_lport_ns_sm_plogi_retry(struct bfa_fcs_lport_ns_s *ns,
}
static void
+bfa_fcs_lport_ns_sm_sending_rnn_id(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event)
+{
+ bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn);
+ bfa_trc(ns->port->fcs, event);
+
+ switch (event) {
+ case NSSM_EVENT_RNNID_SENT:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_rnn_id);
+ break;
+
+ case NSSM_EVENT_PORT_OFFLINE:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_offline);
+ bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port),
+ &ns->fcxp_wqe);
+ break;
+ default:
+ bfa_sm_fault(ns->port->fcs, event);
+ }
+}
+
+static void
+bfa_fcs_lport_ns_sm_rnn_id(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event)
+{
+ bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn);
+ bfa_trc(ns->port->fcs, event);
+
+ switch (event) {
+ case NSSM_EVENT_RSP_OK:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_sending_rsnn_nn);
+ ns->num_rnnid_retries = 0;
+ ns->num_rsnn_nn_retries = 0;
+ bfa_fcs_lport_ns_send_rsnn_nn(ns, NULL);
+ break;
+
+ case NSSM_EVENT_RSP_ERROR:
+ if (ns->num_rnnid_retries < BFA_FCS_MAX_NS_RETRIES) {
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_rnn_id_retry);
+ ns->port->stats.ns_retries++;
+ ns->num_rnnid_retries++;
+ bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port),
+ &ns->timer, bfa_fcs_lport_ns_timeout, ns,
+ BFA_FCS_RETRY_TIMEOUT);
+ } else {
+ bfa_sm_set_state(ns,
+ bfa_fcs_lport_ns_sm_sending_rspn_id);
+ bfa_fcs_lport_ns_send_rspn_id(ns, NULL);
+ }
+ break;
+
+ case NSSM_EVENT_PORT_OFFLINE:
+ bfa_fcxp_discard(ns->fcxp);
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_offline);
+ break;
+
+ default:
+ bfa_sm_fault(ns->port->fcs, event);
+ }
+}
+
+static void
+bfa_fcs_lport_ns_sm_rnn_id_retry(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event)
+{
+ bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn);
+ bfa_trc(ns->port->fcs, event);
+
+ switch (event) {
+ case NSSM_EVENT_TIMEOUT:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_sending_rnn_id);
+ bfa_fcs_lport_ns_send_rnn_id(ns, NULL);
+ break;
+
+ case NSSM_EVENT_PORT_OFFLINE:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_offline);
+ bfa_timer_stop(&ns->timer);
+ break;
+
+ default:
+ bfa_sm_fault(ns->port->fcs, event);
+ }
+}
+
+static void
+bfa_fcs_lport_ns_sm_sending_rsnn_nn(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event)
+{
+ bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn);
+ bfa_trc(ns->port->fcs, event);
+
+ switch (event) {
+ case NSSM_EVENT_RSNN_NN_SENT:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_rsnn_nn);
+ break;
+
+ case NSSM_EVENT_PORT_OFFLINE:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_offline);
+ bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port),
+ &ns->fcxp_wqe);
+ break;
+
+ default:
+ bfa_sm_fault(ns->port->fcs, event);
+ }
+}
+
+static void
+bfa_fcs_lport_ns_sm_rsnn_nn(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event)
+{
+ bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn);
+ bfa_trc(ns->port->fcs, event);
+
+ switch (event) {
+ case NSSM_EVENT_RSP_OK:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_sending_rspn_id);
+ ns->num_rsnn_nn_retries = 0;
+ bfa_fcs_lport_ns_send_rspn_id(ns, NULL);
+ break;
+
+ case NSSM_EVENT_RSP_ERROR:
+ if (ns->num_rsnn_nn_retries < BFA_FCS_MAX_NS_RETRIES) {
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_rsnn_nn_retry);
+ ns->port->stats.ns_retries++;
+ ns->num_rsnn_nn_retries++;
+ bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port),
+ &ns->timer, bfa_fcs_lport_ns_timeout,
+ ns, BFA_FCS_RETRY_TIMEOUT);
+ } else {
+ bfa_sm_set_state(ns,
+ bfa_fcs_lport_ns_sm_sending_rspn_id);
+ bfa_fcs_lport_ns_send_rspn_id(ns, NULL);
+ }
+ break;
+
+ case NSSM_EVENT_PORT_OFFLINE:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_offline);
+ bfa_fcxp_discard(ns->fcxp);
+ break;
+
+ default:
+ bfa_sm_fault(ns->port->fcs, event);
+ }
+}
+
+static void
+bfa_fcs_lport_ns_sm_rsnn_nn_retry(struct bfa_fcs_lport_ns_s *ns,
+ enum vport_ns_event event)
+{
+ bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn);
+ bfa_trc(ns->port->fcs, event);
+
+ switch (event) {
+ case NSSM_EVENT_TIMEOUT:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_sending_rsnn_nn);
+ bfa_fcs_lport_ns_send_rsnn_nn(ns, NULL);
+ break;
+
+ case NSSM_EVENT_PORT_OFFLINE:
+ bfa_sm_set_state(ns, bfa_fcs_lport_ns_sm_offline);
+ bfa_timer_stop(&ns->timer);
+ break;
+
+ default:
+ bfa_sm_fault(ns->port->fcs, event);
+ }
+}
+
+static void
bfa_fcs_lport_ns_sm_sending_rspn_id(struct bfa_fcs_lport_ns_s *ns,
enum vport_ns_event event)
{
@@ -3548,7 +4288,7 @@ bfa_fcs_lport_ns_sm_online(struct bfa_fcs_lport_ns_s *ns,
bfa_sm_set_state(ns,
bfa_fcs_lport_ns_sm_sending_gid_ft);
bfa_fcs_lport_ns_send_gid_ft(ns, NULL);
- };
+ }
break;
default:
@@ -3573,11 +4313,12 @@ bfa_fcs_lport_ns_send_plogi(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->pid);
-fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
port->stats.ns_plogi_alloc_wait++;
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
- bfa_fcs_lport_ns_send_plogi, ns);
+ bfa_fcs_lport_ns_send_plogi, ns, BFA_TRUE);
return;
}
ns->fcxp = fcxp;
@@ -3586,7 +4327,8 @@ fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
bfa_hton3b(FC_NAME_SERVER),
bfa_fcs_lport_get_fcid(port), 0,
port->port_cfg.pwwn, port->port_cfg.nwwn,
- bfa_fcport_get_maxfrsize(port->fcs->bfa));
+ bfa_fcport_get_maxfrsize(port->fcs->bfa),
+ bfa_fcport_get_rx_bbcredit(port->fcs->bfa));
bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
FC_CLASS_3, len, &fchs,
@@ -3655,6 +4397,162 @@ bfa_fcs_lport_ns_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
}
/*
+ * Register node name for port_id
+ */
+static void
+bfa_fcs_lport_ns_send_rnn_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
+{
+ struct bfa_fcs_lport_ns_s *ns = ns_cbarg;
+ struct bfa_fcs_lport_s *port = ns->port;
+ struct fchs_s fchs;
+ int len;
+ struct bfa_fcxp_s *fcxp;
+
+ bfa_trc(port->fcs, port->port_cfg.pwwn);
+
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
+ if (!fcxp) {
+ port->stats.ns_rnnid_alloc_wait++;
+ bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
+ bfa_fcs_lport_ns_send_rnn_id, ns, BFA_TRUE);
+ return;
+ }
+
+ ns->fcxp = fcxp;
+
+ len = fc_rnnid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
+ bfa_fcs_lport_get_fcid(port),
+ bfa_fcs_lport_get_fcid(port),
+ bfa_fcs_lport_get_nwwn(port));
+
+ bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
+ FC_CLASS_3, len, &fchs,
+ bfa_fcs_lport_ns_rnn_id_response, (void *)ns,
+ FC_MAX_PDUSZ, FC_FCCT_TOV);
+
+ port->stats.ns_rnnid_sent++;
+ bfa_sm_send_event(ns, NSSM_EVENT_RNNID_SENT);
+}
+
+static void
+bfa_fcs_lport_ns_rnn_id_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
+ void *cbarg, bfa_status_t req_status,
+ u32 rsp_len, u32 resid_len,
+ struct fchs_s *rsp_fchs)
+
+{
+ struct bfa_fcs_lport_ns_s *ns = (struct bfa_fcs_lport_ns_s *) cbarg;
+ struct bfa_fcs_lport_s *port = ns->port;
+ struct ct_hdr_s *cthdr = NULL;
+
+ bfa_trc(port->fcs, port->port_cfg.pwwn);
+
+ /*
+ * Sanity Checks
+ */
+ if (req_status != BFA_STATUS_OK) {
+ bfa_trc(port->fcs, req_status);
+ port->stats.ns_rnnid_rsp_err++;
+ bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR);
+ return;
+ }
+
+ cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp);
+ cthdr->cmd_rsp_code = be16_to_cpu(cthdr->cmd_rsp_code);
+
+ if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) {
+ port->stats.ns_rnnid_accepts++;
+ bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK);
+ return;
+ }
+
+ port->stats.ns_rnnid_rejects++;
+ bfa_trc(port->fcs, cthdr->reason_code);
+ bfa_trc(port->fcs, cthdr->exp_code);
+ bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR);
+}
+
+/*
+ * Register the symbolic node name for a given node name.
+ */
+static void
+bfa_fcs_lport_ns_send_rsnn_nn(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
+{
+ struct bfa_fcs_lport_ns_s *ns = ns_cbarg;
+ struct bfa_fcs_lport_s *port = ns->port;
+ struct fchs_s fchs;
+ int len;
+ struct bfa_fcxp_s *fcxp;
+ u8 *nsymbl;
+
+ bfa_trc(port->fcs, port->port_cfg.pwwn);
+
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
+ if (!fcxp) {
+ port->stats.ns_rsnn_nn_alloc_wait++;
+ bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
+ bfa_fcs_lport_ns_send_rsnn_nn, ns, BFA_TRUE);
+ return;
+ }
+ ns->fcxp = fcxp;
+
+ nsymbl = (u8 *) &(bfa_fcs_lport_get_nsym_name(
+ bfa_fcs_get_base_port(port->fcs)));
+
+ len = fc_rsnn_nn_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
+ bfa_fcs_lport_get_fcid(port),
+ bfa_fcs_lport_get_nwwn(port), nsymbl);
+
+ bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
+ FC_CLASS_3, len, &fchs,
+ bfa_fcs_lport_ns_rsnn_nn_response, (void *)ns,
+ FC_MAX_PDUSZ, FC_FCCT_TOV);
+
+ port->stats.ns_rsnn_nn_sent++;
+
+ bfa_sm_send_event(ns, NSSM_EVENT_RSNN_NN_SENT);
+}
+
+static void
+bfa_fcs_lport_ns_rsnn_nn_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
+ void *cbarg, bfa_status_t req_status,
+ u32 rsp_len, u32 resid_len,
+ struct fchs_s *rsp_fchs)
+{
+ struct bfa_fcs_lport_ns_s *ns = (struct bfa_fcs_lport_ns_s *) cbarg;
+ struct bfa_fcs_lport_s *port = ns->port;
+ struct ct_hdr_s *cthdr = NULL;
+
+ bfa_trc(port->fcs, port->port_cfg.pwwn);
+
+ /*
+ * Sanity Checks
+ */
+ if (req_status != BFA_STATUS_OK) {
+ bfa_trc(port->fcs, req_status);
+ port->stats.ns_rsnn_nn_rsp_err++;
+ bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR);
+ return;
+ }
+
+ cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp);
+ cthdr->cmd_rsp_code = be16_to_cpu(cthdr->cmd_rsp_code);
+
+ if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) {
+ port->stats.ns_rsnn_nn_accepts++;
+ bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK);
+ return;
+ }
+
+ port->stats.ns_rsnn_nn_rejects++;
+ bfa_trc(port->fcs, cthdr->reason_code);
+ bfa_trc(port->fcs, cthdr->exp_code);
+ bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR);
+}
+
+/*
* Register the symbolic port name.
*/
static void
@@ -3672,11 +4570,12 @@ bfa_fcs_lport_ns_send_rspn_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->port_cfg.pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
port->stats.ns_rspnid_alloc_wait++;
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
- bfa_fcs_lport_ns_send_rspn_id, ns);
+ bfa_fcs_lport_ns_send_rspn_id, ns, BFA_TRUE);
return;
}
ns->fcxp = fcxp;
@@ -3690,21 +4589,13 @@ bfa_fcs_lport_ns_send_rspn_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
* to that of the base port.
*/
- strncpy((char *)psymbl,
- (char *) &
- (bfa_fcs_lport_get_psym_name
+ strscpy(symbl,
+ (char *)&(bfa_fcs_lport_get_psym_name
(bfa_fcs_get_base_port(port->fcs))),
- strlen((char *) &
- bfa_fcs_lport_get_psym_name(bfa_fcs_get_base_port
- (port->fcs))));
-
- /* Ensure we have a null terminating string. */
- ((char *)psymbl)[strlen((char *) &
- bfa_fcs_lport_get_psym_name(bfa_fcs_get_base_port
- (port->fcs)))] = 0;
- strncat((char *)psymbl,
- (char *) &(bfa_fcs_lport_get_psym_name(port)),
- strlen((char *) &bfa_fcs_lport_get_psym_name(port)));
+ sizeof(symbl));
+
+ strlcat(symbl, (char *)&(bfa_fcs_lport_get_psym_name(port)),
+ sizeof(symbl));
} else {
psymbl = (u8 *) &(bfa_fcs_lport_get_psym_name(port));
}
@@ -3773,11 +4664,12 @@ bfa_fcs_lport_ns_send_rft_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->port_cfg.pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
port->stats.ns_rftid_alloc_wait++;
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
- bfa_fcs_lport_ns_send_rft_id, ns);
+ bfa_fcs_lport_ns_send_rft_id, ns, BFA_TRUE);
return;
}
ns->fcxp = fcxp;
@@ -3846,11 +4738,12 @@ bfa_fcs_lport_ns_send_rff_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->port_cfg.pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
port->stats.ns_rffid_alloc_wait++;
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
- bfa_fcs_lport_ns_send_rff_id, ns);
+ bfa_fcs_lport_ns_send_rff_id, ns, BFA_TRUE);
return;
}
ns->fcxp = fcxp;
@@ -3929,11 +4822,12 @@ bfa_fcs_lport_ns_send_gid_ft(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->pid);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
port->stats.ns_gidft_alloc_wait++;
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
- bfa_fcs_lport_ns_send_gid_ft, ns);
+ bfa_fcs_lport_ns_send_gid_ft, ns, BFA_TRUE);
return;
}
ns->fcxp = fcxp;
@@ -4063,6 +4957,10 @@ bfa_fcs_lport_ns_process_gidft_pids(struct bfa_fcs_lport_s *port, u32 *pid_buf,
struct fcgs_gidft_resp_s *gidft_entry;
struct bfa_fcs_rport_s *rport;
u32 ii;
+ struct bfa_fcs_fabric_s *fabric = port->fabric;
+ struct bfa_fcs_vport_s *vport;
+ struct list_head *qe;
+ u8 found = 0;
for (ii = 0; ii < n_pids; ii++) {
gidft_entry = (struct fcgs_gidft_resp_s *) &pid_buf[ii];
@@ -4071,6 +4969,29 @@ bfa_fcs_lport_ns_process_gidft_pids(struct bfa_fcs_lport_s *port, u32 *pid_buf,
continue;
/*
+ * Ignore PID if it is of base port
+ * (Avoid vports discovering base port as remote port)
+ */
+ if (gidft_entry->pid == fabric->bport.pid)
+ continue;
+
+ /*
+ * Ignore PID if it is of vport created on the same base port
+ * (Avoid vport discovering every other vport created on the
+ * same port as remote port)
+ */
+ list_for_each(qe, &fabric->vport_q) {
+ vport = (struct bfa_fcs_vport_s *) qe;
+ if (vport->lport.pid == gidft_entry->pid)
+ found = 1;
+ }
+
+ if (found) {
+ found = 0;
+ continue;
+ }
+
+ /*
* Check if this rport already exists
*/
rport = bfa_fcs_lport_get_rport_by_pid(port, gidft_entry->pid);
@@ -4137,7 +5058,8 @@ bfa_fcs_lport_ns_query(struct bfa_fcs_lport_s *port)
struct bfa_fcs_lport_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port);
bfa_trc(port->fcs, port->pid);
- bfa_sm_send_event(ns, NSSM_EVENT_NS_QUERY);
+ if (bfa_sm_cmp_state(ns, bfa_fcs_lport_ns_sm_online))
+ bfa_sm_send_event(ns, NSSM_EVENT_NS_QUERY);
}
static void
@@ -4157,6 +5079,61 @@ bfa_fcs_lport_ns_boot_target_disc(bfa_fcs_lport_t *port)
}
}
+void
+bfa_fcs_lport_ns_util_send_rspn_id(void *cbarg, struct bfa_fcxp_s *fcxp_alloced)
+{
+ struct bfa_fcs_lport_ns_s *ns = cbarg;
+ struct bfa_fcs_lport_s *port = ns->port;
+ struct fchs_s fchs;
+ struct bfa_fcxp_s *fcxp;
+ u8 symbl[256];
+ int len;
+
+ /* Avoid sending RSPN in the following states. */
+ if (bfa_sm_cmp_state(ns, bfa_fcs_lport_ns_sm_offline) ||
+ bfa_sm_cmp_state(ns, bfa_fcs_lport_ns_sm_plogi_sending) ||
+ bfa_sm_cmp_state(ns, bfa_fcs_lport_ns_sm_plogi) ||
+ bfa_sm_cmp_state(ns, bfa_fcs_lport_ns_sm_plogi_retry) ||
+ bfa_sm_cmp_state(ns, bfa_fcs_lport_ns_sm_rspn_id_retry))
+ return;
+
+ memset(symbl, 0, sizeof(symbl));
+ bfa_trc(port->fcs, port->port_cfg.pwwn);
+
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
+ if (!fcxp) {
+ port->stats.ns_rspnid_alloc_wait++;
+ bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe,
+ bfa_fcs_lport_ns_util_send_rspn_id, ns, BFA_FALSE);
+ return;
+ }
+
+ ns->fcxp = fcxp;
+
+ if (port->vport) {
+ /*
+ * For Vports, we append the vport's port symbolic name
+ * to that of the base port.
+ */
+ strscpy(symbl, (char *)&(bfa_fcs_lport_get_psym_name
+ (bfa_fcs_get_base_port(port->fcs))),
+ sizeof(symbl));
+
+ strlcat(symbl,
+ (char *)&(bfa_fcs_lport_get_psym_name(port)),
+ sizeof(symbl));
+ }
+
+ len = fc_rspnid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
+ bfa_fcs_lport_get_fcid(port), 0, symbl);
+
+ bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
+ FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0);
+
+ port->stats.ns_rspnid_sent++;
+}
+
/*
* FCS SCN
*/
@@ -4184,18 +5161,6 @@ static void bfa_fcs_lport_scn_timeout(void *arg);
* fcs_scm_sm FCS SCN state machine
*/
-/*
- * VPort SCN State Machine events
- */
-enum port_scn_event {
- SCNSM_EVENT_PORT_ONLINE = 1,
- SCNSM_EVENT_PORT_OFFLINE = 2,
- SCNSM_EVENT_RSP_OK = 3,
- SCNSM_EVENT_RSP_ERROR = 4,
- SCNSM_EVENT_TIMEOUT = 5,
- SCNSM_EVENT_SCR_SENT = 6,
-};
-
static void bfa_fcs_lport_scn_sm_offline(struct bfa_fcs_lport_scn_s *scn,
enum port_scn_event event);
static void bfa_fcs_lport_scn_sm_sending_scr(
@@ -4331,10 +5296,11 @@ bfa_fcs_lport_scn_send_scr(void *scn_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(port->fcs, port->pid);
bfa_trc(port->fcs, port->port_cfg.pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &scn->fcxp_wqe,
- bfa_fcs_lport_scn_send_scr, scn);
+ bfa_fcs_lport_scn_send_scr, scn, BFA_TRUE);
return;
}
scn->fcxp = fcxp;
@@ -4416,7 +5382,7 @@ bfa_fcs_lport_scn_send_ls_acc(struct bfa_fcs_lport_s *port,
bfa_trc(port->fcs, rx_fchs->s_id);
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -4478,7 +5444,7 @@ bfa_fcs_lport_scn_offline(struct bfa_fcs_lport_s *port)
}
void
-bfa_fcs_lport_scn_online(struct bfa_fcs_lport_s *port)
+bfa_fcs_lport_fab_scn_online(struct bfa_fcs_lport_s *port)
{
struct bfa_fcs_lport_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port);
@@ -4490,14 +5456,33 @@ static void
bfa_fcs_lport_scn_portid_rscn(struct bfa_fcs_lport_s *port, u32 rpid)
{
struct bfa_fcs_rport_s *rport;
+ struct bfa_fcs_fabric_s *fabric = port->fabric;
+ struct bfa_fcs_vport_s *vport;
+ struct list_head *qe;
bfa_trc(port->fcs, rpid);
/*
+ * Ignore PID if it is of base port or of vports created on the
+ * same base port. It is to avoid vports discovering base port or
+ * other vports created on same base port as remote port
+ */
+ if (rpid == fabric->bport.pid)
+ return;
+
+ list_for_each(qe, &fabric->vport_q) {
+ vport = (struct bfa_fcs_vport_s *) qe;
+ if (vport->lport.pid == rpid)
+ return;
+ }
+ /*
* If this is an unknown device, then it just came online.
* Otherwise let rport handle the RSCN event.
*/
rport = bfa_fcs_lport_get_rport_by_pid(port, rpid);
+ if (!rport)
+ rport = bfa_fcs_lport_get_rport_by_old_pid(port, rpid);
+
if (rport == NULL) {
/*
* If min cfg mode is enabled, we donot need to
@@ -4604,7 +5589,7 @@ bfa_fcs_lport_scn_process_rscn(struct bfa_fcs_lport_s *port,
bfa_fcs_lport_ms_fabric_rscn(port);
break;
}
- /* !!!!!!!!! Fall Through !!!!!!!!!!!!! */
+ fallthrough;
case FC_RSCN_FORMAT_AREA:
case FC_RSCN_FORMAT_DOMAIN:
@@ -4641,64 +5626,16 @@ bfa_fcs_get_base_port(struct bfa_fcs_s *fcs)
return &fcs->fabric.bport;
}
-wwn_t
-bfa_fcs_lport_get_rport(struct bfa_fcs_lport_s *port, wwn_t wwn, int index,
- int nrports, bfa_boolean_t bwwn)
-{
- struct list_head *qh, *qe;
- struct bfa_fcs_rport_s *rport = NULL;
- int i;
- struct bfa_fcs_s *fcs;
-
- if (port == NULL || nrports == 0)
- return (wwn_t) 0;
-
- fcs = port->fcs;
- bfa_trc(fcs, (u32) nrports);
-
- i = 0;
- qh = &port->rport_q;
- qe = bfa_q_first(qh);
-
- while ((qe != qh) && (i < nrports)) {
- rport = (struct bfa_fcs_rport_s *) qe;
- if (bfa_ntoh3b(rport->pid) > 0xFFF000) {
- qe = bfa_q_next(qe);
- bfa_trc(fcs, (u32) rport->pwwn);
- bfa_trc(fcs, rport->pid);
- bfa_trc(fcs, i);
- continue;
- }
-
- if (bwwn) {
- if (!memcmp(&wwn, &rport->pwwn, 8))
- break;
- } else {
- if (i == index)
- break;
- }
-
- i++;
- qe = bfa_q_next(qe);
- }
-
- bfa_trc(fcs, i);
- if (rport)
- return rport->pwwn;
- else
- return (wwn_t) 0;
-}
-
void
-bfa_fcs_lport_get_rports(struct bfa_fcs_lport_s *port,
- wwn_t rport_wwns[], int *nrports)
+bfa_fcs_lport_get_rport_quals(struct bfa_fcs_lport_s *port,
+ struct bfa_rport_qualifier_s rports[], int *nrports)
{
struct list_head *qh, *qe;
struct bfa_fcs_rport_s *rport = NULL;
int i;
struct bfa_fcs_s *fcs;
- if (port == NULL || rport_wwns == NULL || *nrports == 0)
+ if (port == NULL || rports == NULL || *nrports == 0)
return;
fcs = port->fcs;
@@ -4718,7 +5655,13 @@ bfa_fcs_lport_get_rports(struct bfa_fcs_lport_s *port,
continue;
}
- rport_wwns[i] = rport->pwwn;
+ if (!rport->pwwn && !rport->pid) {
+ qe = bfa_q_next(qe);
+ continue;
+ }
+
+ rports[i].pwwn = rport->pwwn;
+ rports[i].pid = rport->pid;
i++;
qe = bfa_q_next(qe);
@@ -4743,13 +5686,13 @@ bfa_fcs_lport_get_rport_max_speed(bfa_fcs_lport_t *port)
bfa_port_speed_t max_speed = 0;
struct bfa_port_attr_s port_attr;
bfa_port_speed_t port_speed, rport_speed;
- bfa_boolean_t trl_enabled = bfa_fcport_is_ratelim(port->fcs->bfa);
-
+ bfa_boolean_t trl_enabled;
if (port == NULL)
return 0;
fcs = port->fcs;
+ trl_enabled = bfa_fcport_is_ratelim(port->fcs->bfa);
/* Get Physical port's current speed */
bfa_fcport_get_attr(port->fcs->bfa, &port_attr);
@@ -4762,8 +5705,8 @@ bfa_fcs_lport_get_rport_max_speed(bfa_fcs_lport_t *port)
while (qe != qh) {
rport = (struct bfa_fcs_rport_s *) qe;
if ((bfa_ntoh3b(rport->pid) > 0xFFF000) ||
- (bfa_fcs_rport_get_state(rport) ==
- BFA_RPORT_OFFLINE)) {
+ (bfa_fcs_rport_get_state(rport) == BFA_RPORT_OFFLINE) ||
+ (rport->scsi_function != BFA_RPORT_TARGET)) {
qe = bfa_q_next(qe);
continue;
}
@@ -4776,17 +5719,15 @@ bfa_fcs_lport_get_rport_max_speed(bfa_fcs_lport_t *port)
bfa_fcport_get_ratelim_speed(port->fcs->bfa);
}
- if ((rport_speed == BFA_PORT_SPEED_8GBPS) ||
- (rport_speed > port_speed)) {
- max_speed = rport_speed;
- break;
- } else if (rport_speed > max_speed) {
+ if (rport_speed > max_speed)
max_speed = rport_speed;
- }
qe = bfa_q_next(qe);
}
+ if (max_speed > port_speed)
+ max_speed = port_speed;
+
bfa_trc(fcs, max_speed);
return max_speed;
}
@@ -4815,54 +5756,6 @@ bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn)
return NULL;
}
-/*
- * API corresponding to NPIV_VPORT_GETINFO.
- */
-void
-bfa_fcs_lport_get_info(struct bfa_fcs_lport_s *port,
- struct bfa_lport_info_s *port_info)
-{
-
- bfa_trc(port->fcs, port->fabric->fabric_name);
-
- if (port->vport == NULL) {
- /*
- * This is a Physical port
- */
- port_info->port_type = BFA_LPORT_TYPE_PHYSICAL;
-
- /*
- * @todo : need to fix the state & reason
- */
- port_info->port_state = 0;
- port_info->offline_reason = 0;
-
- port_info->port_wwn = bfa_fcs_lport_get_pwwn(port);
- port_info->node_wwn = bfa_fcs_lport_get_nwwn(port);
-
- port_info->max_vports_supp =
- bfa_lps_get_max_vport(port->fcs->bfa);
- port_info->num_vports_inuse =
- port->fabric->num_vports;
- port_info->max_rports_supp = BFA_FCS_MAX_RPORTS_SUPP;
- port_info->num_rports_inuse = port->num_rports;
- } else {
- /*
- * This is a virtual port
- */
- port_info->port_type = BFA_LPORT_TYPE_VIRTUAL;
-
- /*
- * @todo : need to fix the state & reason
- */
- port_info->port_state = 0;
- port_info->offline_reason = 0;
-
- port_info->port_wwn = bfa_fcs_lport_get_pwwn(port);
- port_info->node_wwn = bfa_fcs_lport_get_nwwn(port);
- }
-}
-
void
bfa_fcs_lport_get_stats(struct bfa_fcs_lport_s *fcs_port,
struct bfa_lport_stats_s *port_stats)
@@ -4877,6 +5770,15 @@ bfa_fcs_lport_clear_stats(struct bfa_fcs_lport_s *fcs_port)
}
/*
+ * Let new loop map create missing rports
+ */
+void
+bfa_fcs_lport_lip_scn_online(struct bfa_fcs_lport_s *port)
+{
+ bfa_fcs_lport_loop_online(port);
+}
+
+/*
* FCS virtual port state machine
*/
@@ -4901,25 +5803,6 @@ static void bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport);
* fcs_vport_sm FCS virtual port state machine
*/
-/*
- * VPort State Machine events
- */
-enum bfa_fcs_vport_event {
- BFA_FCS_VPORT_SM_CREATE = 1, /* vport create event */
- BFA_FCS_VPORT_SM_DELETE = 2, /* vport delete event */
- BFA_FCS_VPORT_SM_START = 3, /* vport start request */
- BFA_FCS_VPORT_SM_STOP = 4, /* stop: unsupported */
- BFA_FCS_VPORT_SM_ONLINE = 5, /* fabric online */
- BFA_FCS_VPORT_SM_OFFLINE = 6, /* fabric offline event */
- BFA_FCS_VPORT_SM_FRMSENT = 7, /* fdisc/logo sent events */
- BFA_FCS_VPORT_SM_RSP_OK = 8, /* good response */
- BFA_FCS_VPORT_SM_RSP_ERROR = 9, /* error/bad response */
- BFA_FCS_VPORT_SM_TIMEOUT = 10, /* delay timer event */
- BFA_FCS_VPORT_SM_DELCOMP = 11, /* lport delete completion */
- BFA_FCS_VPORT_SM_RSP_DUP_WWN = 12, /* Dup wnn error*/
- BFA_FCS_VPORT_SM_RSP_FAILED = 13, /* non-retryable failure */
-};
-
static void bfa_fcs_vport_sm_uninit(struct bfa_fcs_vport_s *vport,
enum bfa_fcs_vport_event event);
static void bfa_fcs_vport_sm_created(struct bfa_fcs_vport_s *vport,
@@ -4930,6 +5813,8 @@ static void bfa_fcs_vport_sm_fdisc(struct bfa_fcs_vport_s *vport,
enum bfa_fcs_vport_event event);
static void bfa_fcs_vport_sm_fdisc_retry(struct bfa_fcs_vport_s *vport,
enum bfa_fcs_vport_event event);
+static void bfa_fcs_vport_sm_fdisc_rsp_wait(struct bfa_fcs_vport_s *vport,
+ enum bfa_fcs_vport_event event);
static void bfa_fcs_vport_sm_online(struct bfa_fcs_vport_s *vport,
enum bfa_fcs_vport_event event);
static void bfa_fcs_vport_sm_deleting(struct bfa_fcs_vport_s *vport,
@@ -4940,13 +5825,34 @@ static void bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport,
enum bfa_fcs_vport_event event);
static void bfa_fcs_vport_sm_error(struct bfa_fcs_vport_s *vport,
enum bfa_fcs_vport_event event);
+static void bfa_fcs_vport_sm_stopping(struct bfa_fcs_vport_s *vport,
+ enum bfa_fcs_vport_event event);
+static void bfa_fcs_vport_sm_logo_for_stop(struct bfa_fcs_vport_s *vport,
+ enum bfa_fcs_vport_event event);
+
+struct bfa_fcs_vport_sm_table_s {
+ bfa_fcs_vport_sm_t sm; /* state machine function */
+ enum bfa_vport_state state; /* state machine encoding */
+ char *name; /* state name for display */
+};
+
+static inline enum bfa_vport_state
+bfa_vport_sm_to_state(struct bfa_fcs_vport_sm_table_s *smt, bfa_fcs_vport_sm_t sm)
+{
+ int i = 0;
-static struct bfa_sm_table_s vport_sm_table[] = {
+ while (smt[i].sm && smt[i].sm != sm)
+ i++;
+ return smt[i].state;
+}
+
+static struct bfa_fcs_vport_sm_table_s vport_sm_table[] = {
{BFA_SM(bfa_fcs_vport_sm_uninit), BFA_FCS_VPORT_UNINIT},
{BFA_SM(bfa_fcs_vport_sm_created), BFA_FCS_VPORT_CREATED},
{BFA_SM(bfa_fcs_vport_sm_offline), BFA_FCS_VPORT_OFFLINE},
{BFA_SM(bfa_fcs_vport_sm_fdisc), BFA_FCS_VPORT_FDISC},
{BFA_SM(bfa_fcs_vport_sm_fdisc_retry), BFA_FCS_VPORT_FDISC_RETRY},
+ {BFA_SM(bfa_fcs_vport_sm_fdisc_rsp_wait), BFA_FCS_VPORT_FDISC_RSP_WAIT},
{BFA_SM(bfa_fcs_vport_sm_online), BFA_FCS_VPORT_ONLINE},
{BFA_SM(bfa_fcs_vport_sm_deleting), BFA_FCS_VPORT_DELETING},
{BFA_SM(bfa_fcs_vport_sm_cleanup), BFA_FCS_VPORT_CLEANUP},
@@ -5042,6 +5948,11 @@ bfa_fcs_vport_sm_offline(struct bfa_fcs_vport_s *vport,
bfa_fcs_vport_do_fdisc(vport);
break;
+ case BFA_FCS_VPORT_SM_STOP:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup);
+ bfa_sm_send_event(&vport->lport, BFA_FCS_PORT_SM_STOP);
+ break;
+
case BFA_FCS_VPORT_SM_OFFLINE:
/*
* This can happen if the vport couldn't be initialzied
@@ -5070,9 +5981,7 @@ bfa_fcs_vport_sm_fdisc(struct bfa_fcs_vport_s *vport,
switch (event) {
case BFA_FCS_VPORT_SM_DELETE:
- bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup);
- bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE);
- bfa_fcs_lport_delete(&vport->lport);
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc_rsp_wait);
break;
case BFA_FCS_VPORT_SM_OFFLINE:
@@ -5093,6 +6002,7 @@ bfa_fcs_vport_sm_fdisc(struct bfa_fcs_vport_s *vport,
break;
case BFA_FCS_VPORT_SM_RSP_FAILED:
+ case BFA_FCS_VPORT_SM_FABRIC_MAX:
bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline);
break;
@@ -5140,6 +6050,42 @@ bfa_fcs_vport_sm_fdisc_retry(struct bfa_fcs_vport_s *vport,
}
/*
+ * FDISC is in progress and we got a vport delete request -
+ * this is a wait state while we wait for fdisc response and
+ * we will transition to the appropriate state - on rsp status.
+ */
+static void
+bfa_fcs_vport_sm_fdisc_rsp_wait(struct bfa_fcs_vport_s *vport,
+ enum bfa_fcs_vport_event event)
+{
+ bfa_trc(__vport_fcs(vport), __vport_pwwn(vport));
+ bfa_trc(__vport_fcs(vport), event);
+
+ switch (event) {
+ case BFA_FCS_VPORT_SM_RSP_OK:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_deleting);
+ bfa_fcs_lport_delete(&vport->lport);
+ break;
+
+ case BFA_FCS_VPORT_SM_DELETE:
+ break;
+
+ case BFA_FCS_VPORT_SM_OFFLINE:
+ case BFA_FCS_VPORT_SM_RSP_ERROR:
+ case BFA_FCS_VPORT_SM_RSP_FAILED:
+ case BFA_FCS_VPORT_SM_FABRIC_MAX:
+ case BFA_FCS_VPORT_SM_RSP_DUP_WWN:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup);
+ bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE);
+ bfa_fcs_lport_delete(&vport->lport);
+ break;
+
+ default:
+ bfa_sm_fault(__vport_fcs(vport), event);
+ }
+}
+
+/*
* Vport is online (FDISC is complete).
*/
static void
@@ -5155,6 +6101,11 @@ bfa_fcs_vport_sm_online(struct bfa_fcs_vport_s *vport,
bfa_fcs_lport_delete(&vport->lport);
break;
+ case BFA_FCS_VPORT_SM_STOP:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_stopping);
+ bfa_sm_send_event(&vport->lport, BFA_FCS_PORT_SM_STOP);
+ break;
+
case BFA_FCS_VPORT_SM_OFFLINE:
bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline);
bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE);
@@ -5167,6 +6118,32 @@ bfa_fcs_vport_sm_online(struct bfa_fcs_vport_s *vport,
}
/*
+ * Vport is being stopped - awaiting lport stop completion to send
+ * LOGO to fabric.
+ */
+static void
+bfa_fcs_vport_sm_stopping(struct bfa_fcs_vport_s *vport,
+ enum bfa_fcs_vport_event event)
+{
+ bfa_trc(__vport_fcs(vport), __vport_pwwn(vport));
+ bfa_trc(__vport_fcs(vport), event);
+
+ switch (event) {
+ case BFA_FCS_VPORT_SM_STOPCOMP:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_logo_for_stop);
+ bfa_fcs_vport_do_logo(vport);
+ break;
+
+ case BFA_FCS_VPORT_SM_OFFLINE:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup);
+ break;
+
+ default:
+ bfa_sm_fault(__vport_fcs(vport), event);
+ }
+}
+
+/*
* Vport is being deleted - awaiting lport delete completion to send
* LOGO to fabric.
*/
@@ -5236,6 +6213,10 @@ bfa_fcs_vport_sm_cleanup(struct bfa_fcs_vport_s *vport,
bfa_fcs_vport_free(vport);
break;
+ case BFA_FCS_VPORT_SM_STOPCOMP:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_created);
+ break;
+
case BFA_FCS_VPORT_SM_DELETE:
break;
@@ -5245,6 +6226,32 @@ bfa_fcs_vport_sm_cleanup(struct bfa_fcs_vport_s *vport,
}
/*
+ * LOGO is sent to fabric. Vport stop is in progress. Lport stop cleanup
+ * is done.
+ */
+static void
+bfa_fcs_vport_sm_logo_for_stop(struct bfa_fcs_vport_s *vport,
+ enum bfa_fcs_vport_event event)
+{
+ bfa_trc(__vport_fcs(vport), __vport_pwwn(vport));
+ bfa_trc(__vport_fcs(vport), event);
+
+ switch (event) {
+ case BFA_FCS_VPORT_SM_OFFLINE:
+ bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE);
+ fallthrough;
+
+ case BFA_FCS_VPORT_SM_RSP_OK:
+ case BFA_FCS_VPORT_SM_RSP_ERROR:
+ bfa_sm_set_state(vport, bfa_fcs_vport_sm_created);
+ break;
+
+ default:
+ bfa_sm_fault(__vport_fcs(vport), event);
+ }
+}
+
+/*
* LOGO is sent to fabric. Vport delete is in progress. Lport delete cleanup
* is done.
*/
@@ -5258,9 +6265,7 @@ bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport,
switch (event) {
case BFA_FCS_VPORT_SM_OFFLINE:
bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE);
- /*
- * !!! fall through !!!
- */
+ fallthrough;
case BFA_FCS_VPORT_SM_RSP_OK:
case BFA_FCS_VPORT_SM_RSP_ERROR:
@@ -5282,6 +6287,31 @@ bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport,
* fcs_vport_private FCS virtual port private functions
*/
/*
+ * Send AEN notification
+ */
+static void
+bfa_fcs_vport_aen_post(struct bfa_fcs_lport_s *port,
+ enum bfa_lport_aen_event event)
+{
+ struct bfad_s *bfad = (struct bfad_s *)port->fabric->fcs->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ aen_entry->aen_data.lport.vf_id = port->fabric->vf_id;
+ aen_entry->aen_data.lport.roles = port->port_cfg.roles;
+ aen_entry->aen_data.lport.ppwwn = bfa_fcs_lport_get_pwwn(
+ bfa_fcs_get_base_port(port->fcs));
+ aen_entry->aen_data.lport.lpwwn = bfa_fcs_lport_get_pwwn(port);
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++port->fcs->fcs_aen_seq,
+ BFA_AEN_CAT_LPORT, event);
+}
+
+/*
* This routine will be called to send a FDISC command.
*/
static void
@@ -5308,8 +6338,11 @@ bfa_fcs_vport_fdisc_rejected(struct bfa_fcs_vport_s *vport)
case FC_LS_RJT_EXP_INVALID_NPORT_ID: /* by Cisco */
if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES)
bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
- else
+ else {
+ bfa_fcs_vport_aen_post(&vport->lport,
+ BFA_LPORT_AEN_NPIV_DUP_WWN);
bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_DUP_WWN);
+ }
break;
case FC_LS_RJT_EXP_INSUFF_RES:
@@ -5319,11 +6352,17 @@ bfa_fcs_vport_fdisc_rejected(struct bfa_fcs_vport_s *vport)
*/
if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES)
bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
- else
- bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_FAILED);
+ else {
+ bfa_fcs_vport_aen_post(&vport->lport,
+ BFA_LPORT_AEN_NPIV_FABRIC_MAX);
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_FABRIC_MAX);
+ }
break;
default:
+ if (vport->fdisc_retries == 0)
+ bfa_fcs_vport_aen_post(&vport->lport,
+ BFA_LPORT_AEN_NPIV_UNKNOWN);
bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
}
}
@@ -5371,15 +6410,21 @@ bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport)
(struct bfad_vport_s *)vport->vport_drv;
bfa_fcs_fabric_delvport(__vport_fabric(vport), vport);
+ bfa_lps_delete(vport->lps);
- if (vport_drv->comp_del)
+ if (vport_drv->comp_del) {
complete(vport_drv->comp_del);
+ return;
+ }
- bfa_lps_delete(vport->lps);
+ /*
+ * We queue the vport delete work to the IM work_q from here.
+ * The memory for the bfad_vport_s is freed from the FC function
+ * template vport_delete entry point.
+ */
+ bfad_im_port_delete(vport_drv->drv_port.bfad, &vport_drv->drv_port);
}
-
-
/*
* fcs_vport_public FCS virtual port public interfaces
*/
@@ -5391,7 +6436,10 @@ void
bfa_fcs_vport_online(struct bfa_fcs_vport_s *vport)
{
vport->vport_stats.fab_online++;
- bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_ONLINE);
+ if (bfa_fcs_fabric_npiv_capable(__vport_fabric(vport)))
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_ONLINE);
+ else
+ vport->vport_stats.fab_no_npiv++;
}
/*
@@ -5405,13 +6453,14 @@ bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport)
}
/*
- * Cleanup notification from fabric SM on link timer expiry.
+ * Stop notification from fabric SM. To be invoked from within FCS.
*/
void
-bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport)
+bfa_fcs_vport_fcs_stop(struct bfa_fcs_vport_s *vport)
{
- vport->vport_stats.fab_cleanup++;
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_STOP);
}
+
/*
* delete notification from fabric SM. To be invoked from within FCS.
*/
@@ -5422,6 +6471,15 @@ bfa_fcs_vport_fcs_delete(struct bfa_fcs_vport_s *vport)
}
/*
+ * Stop completion callback from associated lport
+ */
+void
+bfa_fcs_vport_stop_comp(struct bfa_fcs_vport_s *vport)
+{
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_STOPCOMP);
+}
+
+/*
* Delete completion callback from associated lport
*/
void
@@ -5517,24 +6575,6 @@ bfa_fcs_pbc_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs,
}
/*
- * Use this function to findout if this is a pbc vport or not.
- *
- * @param[in] vport - pointer to bfa_fcs_vport_t.
- *
- * @returns None
- */
-bfa_boolean_t
-bfa_fcs_is_pbc_vport(struct bfa_fcs_vport_s *vport)
-{
-
- if (vport && (vport->lport.port_cfg.preboot_vp == BFA_TRUE))
- return BFA_TRUE;
- else
- return BFA_FALSE;
-
-}
-
-/*
* Use this function initialize the vport.
*
* @param[in] vport - pointer to bfa_fcs_vport_t.
@@ -5606,7 +6646,7 @@ bfa_fcs_vport_get_attr(struct bfa_fcs_vport_s *vport,
memset(attr, 0, sizeof(struct bfa_vport_attr_s));
bfa_fcs_lport_get_attr(&vport->lport, &attr->port_attr);
- attr->vport_state = bfa_sm_to_state(vport_sm_table, vport->sm);
+ attr->vport_state = bfa_vport_sm_to_state(vport_sm_table, vport->sm);
}
@@ -5674,7 +6714,19 @@ bfa_cb_lps_fdisc_comp(void *bfad, void *uarg, bfa_status_t status)
break;
}
- bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
+ if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES)
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
+ else
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_FAILED);
+
+ break;
+
+ case BFA_STATUS_ETIMER:
+ vport->vport_stats.fdisc_timeouts++;
+ if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES)
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR);
+ else
+ bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_FAILED);
break;
case BFA_STATUS_FABRIC_RJT:
diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c
index caaee6f06937..d4bde9bbe75b 100644
--- a/drivers/scsi/bfa/bfa_fcs_rport.c
+++ b/drivers/scsi/bfa/bfa_fcs_rport.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -20,6 +13,7 @@
*/
#include "bfad_drv.h"
+#include "bfad_im.h"
#include "bfa_fcs.h"
#include "bfa_fcbuild.h"
@@ -29,14 +23,22 @@ static u32
bfa_fcs_rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT * 1000;
/* In millisecs */
/*
+ * bfa_fcs_rport_max_logins is max count of bfa_fcs_rports
+ * whereas DEF_CFG_NUM_RPORTS is max count of bfa_rports
+ */
+static u32 bfa_fcs_rport_max_logins = BFA_FCS_MAX_RPORT_LOGINS;
+
+/*
* forward declarations
*/
static struct bfa_fcs_rport_s *bfa_fcs_rport_alloc(
struct bfa_fcs_lport_s *port, wwn_t pwwn, u32 rpid);
static void bfa_fcs_rport_free(struct bfa_fcs_rport_s *rport);
static void bfa_fcs_rport_hal_online(struct bfa_fcs_rport_s *rport);
-static void bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport);
-static void bfa_fcs_rport_offline_action(struct bfa_fcs_rport_s *rport);
+static void bfa_fcs_rport_fcs_online_action(struct bfa_fcs_rport_s *rport);
+static void bfa_fcs_rport_hal_online_action(struct bfa_fcs_rport_s *rport);
+static void bfa_fcs_rport_fcs_offline_action(struct bfa_fcs_rport_s *rport);
+static void bfa_fcs_rport_hal_offline_action(struct bfa_fcs_rport_s *rport);
static void bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport,
struct fc_logi_s *plogi);
static void bfa_fcs_rport_timeout(void *arg);
@@ -75,6 +77,7 @@ static void bfa_fcs_rport_send_ls_rjt(struct bfa_fcs_rport_s *rport,
static void bfa_fcs_rport_process_adisc(struct bfa_fcs_rport_s *rport,
struct fchs_s *rx_fchs, u16 len);
static void bfa_fcs_rport_send_prlo_acc(struct bfa_fcs_rport_s *rport);
+static void bfa_fcs_rport_hal_offline(struct bfa_fcs_rport_s *rport);
static void bfa_fcs_rport_sm_uninit(struct bfa_fcs_rport_s *rport,
enum rport_event event);
@@ -86,6 +89,8 @@ static void bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport,
enum rport_event event);
static void bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport,
enum rport_event event);
+static void bfa_fcs_rport_sm_fc4_fcs_online(struct bfa_fcs_rport_s *rport,
+ enum rport_event event);
static void bfa_fcs_rport_sm_hal_online(struct bfa_fcs_rport_s *rport,
enum rport_event event);
static void bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport,
@@ -94,9 +99,13 @@ static void bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport,
enum rport_event event);
static void bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport,
enum rport_event event);
-static void bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport,
- enum rport_event event);
-static void bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport,
+static void bfa_fcs_rport_sm_adisc_online_sending(
+ struct bfa_fcs_rport_s *rport, enum rport_event event);
+static void bfa_fcs_rport_sm_adisc_online(struct bfa_fcs_rport_s *rport,
+ enum rport_event event);
+static void bfa_fcs_rport_sm_adisc_offline_sending(struct bfa_fcs_rport_s
+ *rport, enum rport_event event);
+static void bfa_fcs_rport_sm_adisc_offline(struct bfa_fcs_rport_s *rport,
enum rport_event event);
static void bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport,
enum rport_event event);
@@ -122,19 +131,42 @@ static void bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport,
enum rport_event event);
static void bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport,
enum rport_event event);
+static void bfa_fcs_rport_sm_fc4_off_delete(struct bfa_fcs_rport_s *rport,
+ enum rport_event event);
+static void bfa_fcs_rport_sm_delete_pending(struct bfa_fcs_rport_s *rport,
+ enum rport_event event);
+
+struct bfa_fcs_rport_sm_table_s {
+ bfa_fcs_rport_sm_t sm; /* state machine function */
+ enum bfa_rport_state state; /* state machine encoding */
+ char *name; /* state name for display */
+};
+
+static inline enum bfa_rport_state
+bfa_rport_sm_to_state(struct bfa_fcs_rport_sm_table_s *smt, bfa_fcs_rport_sm_t sm)
+{
+ int i = 0;
+
+ while (smt[i].sm && smt[i].sm != sm)
+ i++;
+ return smt[i].state;
+}
-static struct bfa_sm_table_s rport_sm_table[] = {
+static struct bfa_fcs_rport_sm_table_s rport_sm_table[] = {
{BFA_SM(bfa_fcs_rport_sm_uninit), BFA_RPORT_UNINIT},
{BFA_SM(bfa_fcs_rport_sm_plogi_sending), BFA_RPORT_PLOGI},
{BFA_SM(bfa_fcs_rport_sm_plogiacc_sending), BFA_RPORT_ONLINE},
{BFA_SM(bfa_fcs_rport_sm_plogi_retry), BFA_RPORT_PLOGI_RETRY},
{BFA_SM(bfa_fcs_rport_sm_plogi), BFA_RPORT_PLOGI},
+ {BFA_SM(bfa_fcs_rport_sm_fc4_fcs_online), BFA_RPORT_ONLINE},
{BFA_SM(bfa_fcs_rport_sm_hal_online), BFA_RPORT_ONLINE},
{BFA_SM(bfa_fcs_rport_sm_online), BFA_RPORT_ONLINE},
{BFA_SM(bfa_fcs_rport_sm_nsquery_sending), BFA_RPORT_NSQUERY},
{BFA_SM(bfa_fcs_rport_sm_nsquery), BFA_RPORT_NSQUERY},
- {BFA_SM(bfa_fcs_rport_sm_adisc_sending), BFA_RPORT_ADISC},
- {BFA_SM(bfa_fcs_rport_sm_adisc), BFA_RPORT_ADISC},
+ {BFA_SM(bfa_fcs_rport_sm_adisc_online_sending), BFA_RPORT_ADISC},
+ {BFA_SM(bfa_fcs_rport_sm_adisc_online), BFA_RPORT_ADISC},
+ {BFA_SM(bfa_fcs_rport_sm_adisc_offline_sending), BFA_RPORT_ADISC},
+ {BFA_SM(bfa_fcs_rport_sm_adisc_offline), BFA_RPORT_ADISC},
{BFA_SM(bfa_fcs_rport_sm_fc4_logorcv), BFA_RPORT_LOGORCV},
{BFA_SM(bfa_fcs_rport_sm_fc4_logosend), BFA_RPORT_LOGO},
{BFA_SM(bfa_fcs_rport_sm_fc4_offline), BFA_RPORT_OFFLINE},
@@ -214,10 +246,19 @@ bfa_fcs_rport_sm_plogi_sending(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_send_plogiacc(rport, NULL);
break;
+ case RPSM_EVENT_SCN_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
+ bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ break;
case RPSM_EVENT_ADDRESS_CHANGE:
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
/* query the NS */
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
+ WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) !=
+ BFA_PORT_TOPOLOGY_LOOP));
bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending);
rport->ns_retries = 0;
bfa_fcs_rport_send_nsdisc(rport, NULL);
@@ -251,8 +292,8 @@ bfa_fcs_rport_sm_plogiacc_sending(struct bfa_fcs_rport_s *rport,
switch (event) {
case RPSM_EVENT_FCXP_SENT:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
- bfa_fcs_rport_hal_online(rport);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
+ bfa_fcs_rport_fcs_online_action(rport);
break;
case RPSM_EVENT_DELETE:
@@ -262,12 +303,21 @@ bfa_fcs_rport_sm_plogiacc_sending(struct bfa_fcs_rport_s *rport,
break;
case RPSM_EVENT_PLOGI_RCVD:
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_PLOGI_COMP:
+ case RPSM_EVENT_FAB_SCN:
/*
* Ignore, SCN is possibly online notification.
*/
break;
+ case RPSM_EVENT_SCN_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
+ bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ break;
+
case RPSM_EVENT_ADDRESS_CHANGE:
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending);
@@ -328,9 +378,19 @@ bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_send_plogiacc(rport, NULL);
break;
+ case RPSM_EVENT_SCN_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
+ bfa_timer_stop(&rport->timer);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ break;
+
case RPSM_EVENT_ADDRESS_CHANGE:
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
bfa_timer_stop(&rport->timer);
+ WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) !=
+ BFA_PORT_TOPOLOGY_LOOP));
bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending);
rport->ns_retries = 0;
bfa_fcs_rport_send_nsdisc(rport, NULL);
@@ -346,9 +406,9 @@ bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport,
break;
case RPSM_EVENT_PLOGI_COMP:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
bfa_timer_stop(&rport->timer);
- bfa_fcs_rport_hal_online(rport);
+ bfa_fcs_rport_fcs_online_action(rport);
break;
default:
@@ -368,24 +428,20 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event)
switch (event) {
case RPSM_EVENT_ACCEPTED:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
rport->plogi_retries = 0;
- bfa_fcs_rport_hal_online(rport);
+ bfa_fcs_rport_fcs_online_action(rport);
break;
case RPSM_EVENT_LOGO_RCVD:
bfa_fcs_rport_send_logo_acc(rport);
- /*
- * !! fall through !!
- */
+ fallthrough;
case RPSM_EVENT_PRLO_RCVD:
if (rport->prlo == BFA_TRUE)
bfa_fcs_rport_send_prlo_acc(rport);
bfa_fcxp_discard(rport->fcxp);
- /*
- * !! fall through !!
- */
+ fallthrough;
case RPSM_EVENT_FAILED:
if (rport->plogi_retries < BFA_FCS_RPORT_MAX_RETRIES) {
rport->plogi_retries++;
@@ -395,6 +451,7 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event)
BFA_FCS_RETRY_TIMEOUT);
} else {
bfa_stats(rport->port, rport_del_max_plogi_retry);
+ rport->old_pid = rport->pid;
rport->pid = 0;
bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
bfa_timer_start(rport->fcs->bfa, &rport->timer,
@@ -403,7 +460,18 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event)
}
break;
- case RPSM_EVENT_PLOGI_RETRY:
+ case RPSM_EVENT_SCN_ONLINE:
+ break;
+
+ case RPSM_EVENT_SCN_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
+ bfa_fcxp_discard(rport->fcxp);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ break;
+
+ case RPSM_EVENT_PLOGI_RETRY:
rport->plogi_retries = 0;
bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_retry);
bfa_timer_start(rport->fcs->bfa, &rport->timer,
@@ -421,8 +489,10 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event)
break;
case RPSM_EVENT_ADDRESS_CHANGE:
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
bfa_fcxp_discard(rport->fcxp);
+ WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) !=
+ BFA_PORT_TOPOLOGY_LOOP));
bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending);
rport->ns_retries = 0;
bfa_fcs_rport_send_nsdisc(rport, NULL);
@@ -441,13 +511,78 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event)
break;
case RPSM_EVENT_PLOGI_COMP:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_hal_online(rport);
+ bfa_fcs_rport_fcs_online_action(rport);
+ break;
+
+ default:
+ bfa_sm_fault(rport->fcs, event);
+ }
+}
+
+/*
+ * PLOGI is done. Await bfa_fcs_itnim to ascertain the scsi function
+ */
+static void
+bfa_fcs_rport_sm_fc4_fcs_online(struct bfa_fcs_rport_s *rport,
+ enum rport_event event)
+{
+ bfa_trc(rport->fcs, rport->pwwn);
+ bfa_trc(rport->fcs, rport->pid);
+ bfa_trc(rport->fcs, event);
+
+ switch (event) {
+ case RPSM_EVENT_FC4_FCS_ONLINE:
+ if (rport->scsi_function == BFA_RPORT_INITIATOR) {
+ if (!BFA_FCS_PID_IS_WKA(rport->pid))
+ bfa_fcs_rpf_rport_online(rport);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_online);
+ break;
+ }
+
+ if (!rport->bfa_rport)
+ rport->bfa_rport =
+ bfa_rport_create(rport->fcs->bfa, rport);
+
+ if (rport->bfa_rport) {
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_fcs_rport_hal_online(rport);
+ } else {
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
+ bfa_fcs_rport_fcs_offline_action(rport);
+ }
+ break;
+
+ case RPSM_EVENT_PLOGI_RCVD:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
+ rport->plogi_pending = BFA_TRUE;
+ bfa_fcs_rport_fcs_offline_action(rport);
+ break;
+
+ case RPSM_EVENT_PLOGI_COMP:
+ case RPSM_EVENT_LOGO_IMP:
+ case RPSM_EVENT_ADDRESS_CHANGE:
+ case RPSM_EVENT_FAB_SCN:
+ case RPSM_EVENT_SCN_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
+ bfa_fcs_rport_fcs_offline_action(rport);
+ break;
+
+ case RPSM_EVENT_LOGO_RCVD:
+ case RPSM_EVENT_PRLO_RCVD:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv);
+ bfa_fcs_rport_fcs_offline_action(rport);
+ break;
+
+ case RPSM_EVENT_DELETE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
+ bfa_fcs_rport_fcs_offline_action(rport);
break;
default:
bfa_sm_fault(rport->fcs, event);
+ break;
}
}
@@ -466,40 +601,35 @@ bfa_fcs_rport_sm_hal_online(struct bfa_fcs_rport_s *rport,
switch (event) {
case RPSM_EVENT_HCB_ONLINE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_online);
- bfa_fcs_rport_online_action(rport);
+ bfa_fcs_rport_hal_online_action(rport);
break;
- case RPSM_EVENT_PRLO_RCVD:
+ case RPSM_EVENT_PLOGI_COMP:
break;
+ case RPSM_EVENT_PRLO_RCVD:
case RPSM_EVENT_LOGO_RCVD:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logorcv);
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv);
+ bfa_fcs_rport_fcs_offline_action(rport);
break;
+ case RPSM_EVENT_FAB_SCN:
case RPSM_EVENT_LOGO_IMP:
case RPSM_EVENT_ADDRESS_CHANGE:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline);
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
+ case RPSM_EVENT_SCN_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
+ bfa_fcs_rport_fcs_offline_action(rport);
break;
case RPSM_EVENT_PLOGI_RCVD:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending);
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
- bfa_fcs_rport_send_plogiacc(rport, NULL);
+ rport->plogi_pending = BFA_TRUE;
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
+ bfa_fcs_rport_fcs_offline_action(rport);
break;
case RPSM_EVENT_DELETE:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend);
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
- break;
-
- case RPSM_EVENT_SCN:
- /*
- * @todo
- * Ignore SCN - PLOGI just completed, FC-4 login should detect
- * device failures.
- */
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
+ bfa_fcs_rport_fcs_offline_action(rport);
break;
default:
@@ -518,14 +648,15 @@ bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, enum rport_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
if (bfa_fcs_fabric_is_switched(rport->port->fabric)) {
bfa_sm_set_state(rport,
bfa_fcs_rport_sm_nsquery_sending);
rport->ns_retries = 0;
bfa_fcs_rport_send_nsdisc(rport, NULL);
} else {
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending);
+ bfa_sm_set_state(rport,
+ bfa_fcs_rport_sm_adisc_online_sending);
bfa_fcs_rport_send_adisc(rport, NULL);
}
break;
@@ -533,21 +664,23 @@ bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, enum rport_event event)
case RPSM_EVENT_PLOGI_RCVD:
case RPSM_EVENT_LOGO_IMP:
case RPSM_EVENT_ADDRESS_CHANGE:
+ case RPSM_EVENT_SCN_OFFLINE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_DELETE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
+ case RPSM_EVENT_SCN_ONLINE:
case RPSM_EVENT_PLOGI_COMP:
break;
@@ -576,10 +709,10 @@ bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport,
case RPSM_EVENT_DELETE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
/*
* ignore SCN, wait for response to query itself
*/
@@ -589,24 +722,16 @@ bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport,
case RPSM_EVENT_PRLO_RCVD:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_LOGO_IMP:
- rport->pid = 0;
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
- bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_timer_start(rport->fcs->bfa, &rport->timer,
- bfa_fcs_rport_timeout, rport,
- bfa_fcs_rport_del_timeout);
- break;
-
case RPSM_EVENT_PLOGI_RCVD:
case RPSM_EVENT_ADDRESS_CHANGE:
case RPSM_EVENT_PLOGI_COMP:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
default:
@@ -627,7 +752,7 @@ bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event)
switch (event) {
case RPSM_EVENT_ACCEPTED:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_online_sending);
bfa_fcs_rport_send_adisc(rport, NULL);
break;
@@ -639,24 +764,24 @@ bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event)
bfa_fcs_rport_send_nsdisc(rport, NULL);
} else {
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
}
break;
case RPSM_EVENT_DELETE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
break;
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_PLOGI_COMP:
@@ -665,7 +790,7 @@ bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event)
case RPSM_EVENT_LOGO_IMP:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
default:
@@ -678,7 +803,7 @@ bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event)
* authenticating with rport. FC-4s are paused.
*/
static void
-bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport,
+bfa_fcs_rport_sm_adisc_online_sending(struct bfa_fcs_rport_s *rport,
enum rport_event event)
{
bfa_trc(rport->fcs, rport->pwwn);
@@ -687,36 +812,36 @@ bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport,
switch (event) {
case RPSM_EVENT_FCXP_SENT:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_online);
break;
case RPSM_EVENT_DELETE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_LOGO_IMP:
case RPSM_EVENT_ADDRESS_CHANGE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
break;
case RPSM_EVENT_PLOGI_RCVD:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
default:
@@ -729,7 +854,8 @@ bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport,
* FC-4s are paused.
*/
static void
-bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event)
+bfa_fcs_rport_sm_adisc_online(struct bfa_fcs_rport_s *rport,
+ enum rport_event event)
{
bfa_trc(rport->fcs, rport->pwwn);
bfa_trc(rport->fcs, rport->pid);
@@ -746,23 +872,21 @@ bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event)
* At least go offline when a PLOGI is received.
*/
bfa_fcxp_discard(rport->fcxp);
- /*
- * !!! fall through !!!
- */
+ fallthrough;
case RPSM_EVENT_FAILED:
case RPSM_EVENT_ADDRESS_CHANGE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_DELETE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
/*
* already processing RSCN
*/
@@ -771,14 +895,55 @@ bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event)
case RPSM_EVENT_LOGO_IMP:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
break;
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_offline_action(rport);
+ bfa_fcs_rport_hal_offline_action(rport);
+ break;
+
+ default:
+ bfa_sm_fault(rport->fcs, event);
+ }
+}
+
+/*
+ * ADISC is being sent for authenticating with rport
+ * Already did offline actions.
+ */
+static void
+bfa_fcs_rport_sm_adisc_offline_sending(struct bfa_fcs_rport_s *rport,
+ enum rport_event event)
+{
+ bfa_trc(rport->fcs, rport->pwwn);
+ bfa_trc(rport->fcs, rport->pid);
+ bfa_trc(rport->fcs, event);
+
+ switch (event) {
+ case RPSM_EVENT_FCXP_SENT:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_offline);
+ break;
+
+ case RPSM_EVENT_DELETE:
+ case RPSM_EVENT_SCN_OFFLINE:
+ case RPSM_EVENT_LOGO_IMP:
+ case RPSM_EVENT_LOGO_RCVD:
+ case RPSM_EVENT_PRLO_RCVD:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
+ bfa_fcxp_walloc_cancel(rport->fcs->bfa,
+ &rport->fcxp_wqe);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ break;
+
+ case RPSM_EVENT_PLOGI_RCVD:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending);
+ bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
+ bfa_fcs_rport_send_plogiacc(rport, NULL);
break;
default:
@@ -787,7 +952,55 @@ bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event)
}
/*
- * Rport has sent LOGO. Awaiting FC-4 offline completion callback.
+ * ADISC to rport
+ * Already did offline actions
+ */
+static void
+bfa_fcs_rport_sm_adisc_offline(struct bfa_fcs_rport_s *rport,
+ enum rport_event event)
+{
+ bfa_trc(rport->fcs, rport->pwwn);
+ bfa_trc(rport->fcs, rport->pid);
+ bfa_trc(rport->fcs, event);
+
+ switch (event) {
+ case RPSM_EVENT_ACCEPTED:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_fcs_rport_hal_online(rport);
+ break;
+
+ case RPSM_EVENT_PLOGI_RCVD:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending);
+ bfa_fcxp_discard(rport->fcxp);
+ bfa_fcs_rport_send_plogiacc(rport, NULL);
+ break;
+
+ case RPSM_EVENT_FAILED:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ break;
+
+ case RPSM_EVENT_DELETE:
+ case RPSM_EVENT_SCN_OFFLINE:
+ case RPSM_EVENT_LOGO_IMP:
+ case RPSM_EVENT_LOGO_RCVD:
+ case RPSM_EVENT_PRLO_RCVD:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
+ bfa_fcxp_discard(rport->fcxp);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ break;
+
+ default:
+ bfa_sm_fault(rport->fcs, event);
+ }
+}
+
+/*
+ * Rport has sent LOGO. Awaiting FC-4 offline completion callback.
*/
static void
bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport,
@@ -800,13 +1013,21 @@ bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport,
switch (event) {
case RPSM_EVENT_FC4_OFFLINE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logorcv);
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
+ bfa_fcs_rport_hal_offline(rport);
break;
case RPSM_EVENT_DELETE:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend);
+ if (rport->pid && (rport->prlo == BFA_TRUE))
+ bfa_fcs_rport_send_prlo_acc(rport);
+ if (rport->pid && (rport->prlo == BFA_FALSE))
+ bfa_fcs_rport_send_logo_acc(rport);
+
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_off_delete);
break;
+ case RPSM_EVENT_SCN_ONLINE:
+ case RPSM_EVENT_SCN_OFFLINE:
+ case RPSM_EVENT_HCB_ONLINE:
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
case RPSM_EVENT_ADDRESS_CHANGE:
@@ -832,7 +1053,21 @@ bfa_fcs_rport_sm_fc4_logosend(struct bfa_fcs_rport_s *rport,
switch (event) {
case RPSM_EVENT_FC4_OFFLINE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend);
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
+ bfa_fcs_rport_hal_offline(rport);
+ break;
+
+ case RPSM_EVENT_LOGO_RCVD:
+ bfa_fcs_rport_send_logo_acc(rport);
+ fallthrough;
+ case RPSM_EVENT_PRLO_RCVD:
+ if (rport->prlo == BFA_TRUE)
+ bfa_fcs_rport_send_prlo_acc(rport);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_off_delete);
+ break;
+
+ case RPSM_EVENT_HCB_ONLINE:
+ case RPSM_EVENT_DELETE:
+ /* Rport is being deleted */
break;
default:
@@ -854,13 +1089,26 @@ bfa_fcs_rport_sm_fc4_offline(struct bfa_fcs_rport_s *rport,
switch (event) {
case RPSM_EVENT_FC4_OFFLINE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline);
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
+ bfa_fcs_rport_hal_offline(rport);
break;
- case RPSM_EVENT_SCN:
- case RPSM_EVENT_LOGO_IMP:
+ case RPSM_EVENT_SCN_ONLINE:
+ break;
case RPSM_EVENT_LOGO_RCVD:
+ /*
+ * Rport is going offline. Just ack the logo
+ */
+ bfa_fcs_rport_send_logo_acc(rport);
+ break;
+
case RPSM_EVENT_PRLO_RCVD:
+ bfa_fcs_rport_send_prlo_acc(rport);
+ break;
+
+ case RPSM_EVENT_SCN_OFFLINE:
+ case RPSM_EVENT_HCB_ONLINE:
+ case RPSM_EVENT_FAB_SCN:
+ case RPSM_EVENT_LOGO_IMP:
case RPSM_EVENT_ADDRESS_CHANGE:
/*
* rport is already going offline.
@@ -891,25 +1139,47 @@ bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport,
switch (event) {
case RPSM_EVENT_HCB_OFFLINE:
+ if (bfa_fcs_lport_is_online(rport->port) &&
+ (rport->plogi_pending)) {
+ rport->plogi_pending = BFA_FALSE;
+ bfa_sm_set_state(rport,
+ bfa_fcs_rport_sm_plogiacc_sending);
+ bfa_fcs_rport_send_plogiacc(rport, NULL);
+ break;
+ }
+ fallthrough;
+
case RPSM_EVENT_ADDRESS_CHANGE:
- if (bfa_fcs_lport_is_online(rport->port)) {
- if (bfa_fcs_fabric_is_switched(rport->port->fabric)) {
- bfa_sm_set_state(rport,
- bfa_fcs_rport_sm_nsdisc_sending);
- rport->ns_retries = 0;
- bfa_fcs_rport_send_nsdisc(rport, NULL);
- } else {
- bfa_sm_set_state(rport,
- bfa_fcs_rport_sm_plogi_sending);
- rport->plogi_retries = 0;
- bfa_fcs_rport_send_plogi(rport, NULL);
- }
- } else {
+ if (!bfa_fcs_lport_is_online(rport->port)) {
rport->pid = 0;
bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
bfa_timer_start(rport->fcs->bfa, &rport->timer,
bfa_fcs_rport_timeout, rport,
bfa_fcs_rport_del_timeout);
+ break;
+ }
+ if (bfa_fcs_fabric_is_switched(rport->port->fabric)) {
+ bfa_sm_set_state(rport,
+ bfa_fcs_rport_sm_nsdisc_sending);
+ rport->ns_retries = 0;
+ bfa_fcs_rport_send_nsdisc(rport, NULL);
+ } else if (bfa_fcport_get_topology(rport->port->fcs->bfa) ==
+ BFA_PORT_TOPOLOGY_LOOP) {
+ if (rport->scn_online) {
+ bfa_sm_set_state(rport,
+ bfa_fcs_rport_sm_adisc_offline_sending);
+ bfa_fcs_rport_send_adisc(rport, NULL);
+ } else {
+ bfa_sm_set_state(rport,
+ bfa_fcs_rport_sm_offline);
+ bfa_timer_start(rport->fcs->bfa, &rport->timer,
+ bfa_fcs_rport_timeout, rport,
+ bfa_fcs_rport_del_timeout);
+ }
+ } else {
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending);
+ rport->plogi_retries = 0;
+ bfa_fcs_rport_send_plogi(rport, NULL);
}
break;
@@ -918,9 +1188,13 @@ bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_free(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_SCN_ONLINE:
+ case RPSM_EVENT_SCN_OFFLINE:
+ case RPSM_EVENT_FAB_SCN:
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
+ case RPSM_EVENT_PLOGI_RCVD:
+ case RPSM_EVENT_LOGO_IMP:
/*
* Ignore, already offline.
*/
@@ -957,10 +1231,18 @@ bfa_fcs_rport_sm_hcb_logorcv(struct bfa_fcs_rport_s *rport,
*/
if (bfa_fcs_lport_is_online(rport->port) &&
(!BFA_FCS_PID_IS_WKA(rport->pid))) {
- bfa_sm_set_state(rport,
- bfa_fcs_rport_sm_nsdisc_sending);
- rport->ns_retries = 0;
- bfa_fcs_rport_send_nsdisc(rport, NULL);
+ if (bfa_fcs_fabric_is_switched(rport->port->fabric)) {
+ bfa_sm_set_state(rport,
+ bfa_fcs_rport_sm_nsdisc_sending);
+ rport->ns_retries = 0;
+ bfa_fcs_rport_send_nsdisc(rport, NULL);
+ } else {
+ /* For N2N Direct Attach, try to re-login */
+ bfa_sm_set_state(rport,
+ bfa_fcs_rport_sm_plogi_sending);
+ rport->plogi_retries = 0;
+ bfa_fcs_rport_send_plogi(rport, NULL);
+ }
} else {
/*
* if it is not a well known address, reset the
@@ -976,13 +1258,19 @@ bfa_fcs_rport_sm_hcb_logorcv(struct bfa_fcs_rport_s *rport,
break;
case RPSM_EVENT_DELETE:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_delete_pending);
+ if (rport->pid && (rport->prlo == BFA_TRUE))
+ bfa_fcs_rport_send_prlo_acc(rport);
+ if (rport->pid && (rport->prlo == BFA_FALSE))
+ bfa_fcs_rport_send_logo_acc(rport);
break;
case RPSM_EVENT_LOGO_IMP:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline);
break;
+ case RPSM_EVENT_SCN_ONLINE:
+ case RPSM_EVENT_SCN_OFFLINE:
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
/*
@@ -1015,7 +1303,17 @@ bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport,
break;
case RPSM_EVENT_LOGO_RCVD:
+ bfa_fcs_rport_send_logo_acc(rport);
+ fallthrough;
case RPSM_EVENT_PRLO_RCVD:
+ if (rport->prlo == BFA_TRUE)
+ bfa_fcs_rport_send_prlo_acc(rport);
+
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_delete_pending);
+ break;
+
+ case RPSM_EVENT_SCN_ONLINE:
+ case RPSM_EVENT_SCN_OFFLINE:
case RPSM_EVENT_ADDRESS_CHANGE:
break;
@@ -1042,12 +1340,19 @@ bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_free(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_SCN_ONLINE:
+ case RPSM_EVENT_SCN_OFFLINE:
+ case RPSM_EVENT_FAB_SCN:
case RPSM_EVENT_ADDRESS_CHANGE:
break;
case RPSM_EVENT_LOGO_RCVD:
+ bfa_fcs_rport_send_logo_acc(rport);
+ fallthrough;
case RPSM_EVENT_PRLO_RCVD:
+ if (rport->prlo == BFA_TRUE)
+ bfa_fcs_rport_send_prlo_acc(rport);
+
bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
bfa_fcs_rport_free(rport);
@@ -1075,10 +1380,12 @@ bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, enum rport_event event)
bfa_fcs_rport_free(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
case RPSM_EVENT_ADDRESS_CHANGE:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending);
bfa_timer_stop(&rport->timer);
+ WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) !=
+ BFA_PORT_TOPOLOGY_LOOP));
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending);
rport->ns_retries = 0;
bfa_fcs_rport_send_nsdisc(rport, NULL);
break;
@@ -1098,12 +1405,19 @@ bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, enum rport_event event)
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
case RPSM_EVENT_LOGO_IMP:
+ case RPSM_EVENT_SCN_OFFLINE:
break;
case RPSM_EVENT_PLOGI_COMP:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
bfa_timer_stop(&rport->timer);
- bfa_fcs_rport_hal_online(rport);
+ bfa_fcs_rport_fcs_online_action(rport);
+ break;
+
+ case RPSM_EVENT_SCN_ONLINE:
+ bfa_timer_stop(&rport->timer);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending);
+ bfa_fcs_rport_send_plogi(rport, NULL);
break;
case RPSM_EVENT_PLOGI_SEND:
@@ -1146,7 +1460,7 @@ bfa_fcs_rport_sm_nsdisc_sending(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_send_plogiacc(rport, NULL);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
case RPSM_EVENT_LOGO_RCVD:
case RPSM_EVENT_PRLO_RCVD:
case RPSM_EVENT_PLOGI_SEND:
@@ -1165,9 +1479,9 @@ bfa_fcs_rport_sm_nsdisc_sending(struct bfa_fcs_rport_s *rport,
break;
case RPSM_EVENT_PLOGI_COMP:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe);
- bfa_fcs_rport_hal_online(rport);
+ bfa_fcs_rport_fcs_online_action(rport);
break;
default:
@@ -1192,7 +1506,7 @@ bfa_fcs_rport_sm_nsdisc_retry(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_send_nsdisc(rport, NULL);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
case RPSM_EVENT_ADDRESS_CHANGE:
bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending);
bfa_timer_stop(&rport->timer);
@@ -1229,9 +1543,9 @@ bfa_fcs_rport_sm_nsdisc_retry(struct bfa_fcs_rport_s *rport,
break;
case RPSM_EVENT_PLOGI_COMP:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
bfa_timer_stop(&rport->timer);
- bfa_fcs_rport_hal_online(rport);
+ bfa_fcs_rport_fcs_online_action(rport);
break;
default:
@@ -1271,12 +1585,13 @@ bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport,
bfa_fcs_rport_sm_nsdisc_sending);
bfa_fcs_rport_send_nsdisc(rport, NULL);
} else {
+ rport->old_pid = rport->pid;
rport->pid = 0;
bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline);
bfa_timer_start(rport->fcs->bfa, &rport->timer,
bfa_fcs_rport_timeout, rport,
bfa_fcs_rport_del_timeout);
- };
+ }
break;
case RPSM_EVENT_DELETE:
@@ -1304,7 +1619,7 @@ bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport,
case RPSM_EVENT_PRLO_RCVD:
bfa_fcs_rport_send_prlo_acc(rport);
break;
- case RPSM_EVENT_SCN:
+ case RPSM_EVENT_FAB_SCN:
/*
* ignore, wait for NS query response
*/
@@ -1318,9 +1633,9 @@ bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport,
break;
case RPSM_EVENT_PLOGI_COMP:
- bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online);
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_fcs_online);
bfa_fcxp_discard(rport->fcxp);
- bfa_fcs_rport_hal_online(rport);
+ bfa_fcs_rport_fcs_online_action(rport);
break;
default:
@@ -1328,7 +1643,63 @@ bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport,
}
}
+/*
+ * Rport needs to be deleted
+ * waiting for ITNIM clean up to finish
+ */
+static void
+bfa_fcs_rport_sm_fc4_off_delete(struct bfa_fcs_rport_s *rport,
+ enum rport_event event)
+{
+ bfa_trc(rport->fcs, rport->pwwn);
+ bfa_trc(rport->fcs, rport->pid);
+ bfa_trc(rport->fcs, event);
+ switch (event) {
+ case RPSM_EVENT_FC4_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_delete_pending);
+ bfa_fcs_rport_hal_offline(rport);
+ break;
+
+ case RPSM_EVENT_DELETE:
+ case RPSM_EVENT_PLOGI_RCVD:
+ /* Ignore these events */
+ break;
+
+ default:
+ bfa_sm_fault(rport->fcs, event);
+ break;
+ }
+}
+
+/*
+ * RPort needs to be deleted
+ * waiting for BFA/FW to finish current processing
+ */
+static void
+bfa_fcs_rport_sm_delete_pending(struct bfa_fcs_rport_s *rport,
+ enum rport_event event)
+{
+ bfa_trc(rport->fcs, rport->pwwn);
+ bfa_trc(rport->fcs, rport->pid);
+ bfa_trc(rport->fcs, event);
+
+ switch (event) {
+ case RPSM_EVENT_HCB_OFFLINE:
+ bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit);
+ bfa_fcs_rport_free(rport);
+ break;
+
+ case RPSM_EVENT_DELETE:
+ case RPSM_EVENT_LOGO_IMP:
+ case RPSM_EVENT_PLOGI_RCVD:
+ /* Ignore these events */
+ break;
+
+ default:
+ bfa_sm_fault(rport->fcs, event);
+ }
+}
/*
* fcs_rport_private FCS RPORT provate functions
@@ -1345,10 +1716,11 @@ bfa_fcs_rport_send_plogi(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(rport->fcs, rport->pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe,
- bfa_fcs_rport_send_plogi, rport);
+ bfa_fcs_rport_send_plogi, rport, BFA_TRUE);
return;
}
rport->fcxp = fcxp;
@@ -1356,7 +1728,8 @@ bfa_fcs_rport_send_plogi(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced)
len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid,
bfa_fcs_lport_get_fcid(port), 0,
port->port_cfg.pwwn, port->port_cfg.nwwn,
- bfa_fcport_get_maxfrsize(port->fcs->bfa));
+ bfa_fcport_get_maxfrsize(port->fcs->bfa),
+ bfa_fcport_get_rx_bbcredit(port->fcs->bfa));
bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
FC_CLASS_3, len, &fchs, bfa_fcs_rport_plogi_response,
@@ -1464,10 +1837,11 @@ bfa_fcs_rport_send_plogiacc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(rport->fcs, rport->pwwn);
bfa_trc(rport->fcs, rport->reply_oxid);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe,
- bfa_fcs_rport_send_plogiacc, rport);
+ bfa_fcs_rport_send_plogiacc, rport, BFA_FALSE);
return;
}
rport->fcxp = fcxp;
@@ -1476,7 +1850,8 @@ bfa_fcs_rport_send_plogiacc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced)
rport->pid, bfa_fcs_lport_get_fcid(port),
rport->reply_oxid, port->port_cfg.pwwn,
port->port_cfg.nwwn,
- bfa_fcport_get_maxfrsize(port->fcs->bfa));
+ bfa_fcport_get_maxfrsize(port->fcs->bfa),
+ bfa_fcport_get_rx_bbcredit(port->fcs->bfa));
bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0);
@@ -1495,10 +1870,11 @@ bfa_fcs_rport_send_adisc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(rport->fcs, rport->pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe,
- bfa_fcs_rport_send_adisc, rport);
+ bfa_fcs_rport_send_adisc, rport, BFA_TRUE);
return;
}
rport->fcxp = fcxp;
@@ -1558,10 +1934,11 @@ bfa_fcs_rport_send_nsdisc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(rport->fcs, rport->pid);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe,
- bfa_fcs_rport_send_nsdisc, rport);
+ bfa_fcs_rport_send_nsdisc, rport, BFA_TRUE);
return;
}
rport->fcxp = fcxp;
@@ -1714,10 +2091,11 @@ bfa_fcs_rport_send_logo(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced)
port = rport->port;
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe,
- bfa_fcs_rport_send_logo, rport);
+ bfa_fcs_rport_send_logo, rport, BFA_FALSE);
return;
}
rport->fcxp = fcxp;
@@ -1751,7 +2129,7 @@ bfa_fcs_rport_send_logo_acc(void *rport_cbarg)
port = rport->port;
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -1822,7 +2200,7 @@ bfa_fcs_rport_process_prli(struct bfa_fcs_rport_s *rport,
bfa_fcs_itnim_is_initiator(rport->itnim);
}
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -1859,7 +2237,7 @@ bfa_fcs_rport_process_rpsc(struct bfa_fcs_rport_s *rport,
speeds.port_op_speed = fc_bfa_speed_to_rpsc_operspeed(pport_attr.speed);
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -1878,22 +2256,19 @@ bfa_fcs_rport_process_adisc(struct bfa_fcs_rport_s *rport,
struct bfa_fcxp_s *fcxp;
struct fchs_s fchs;
struct bfa_fcs_lport_s *port = rport->port;
- struct fc_adisc_s *adisc;
bfa_trc(port->fcs, rx_fchs->s_id);
bfa_trc(port->fcs, rx_fchs->d_id);
rport->stats.adisc_rcvd++;
- adisc = (struct fc_adisc_s *) (rx_fchs + 1);
-
/*
* Accept if the itnim for this rport is online.
* Else reject the ADISC.
*/
if (bfa_fcs_itnim_get_online_state(rport->itnim) == BFA_STATUS_OK) {
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -1930,6 +2305,15 @@ bfa_fcs_rport_hal_online(struct bfa_fcs_rport_s *rport)
bfa_rport_online(rport->bfa_rport, &rport_info);
}
+static void
+bfa_fcs_rport_hal_offline(struct bfa_fcs_rport_s *rport)
+{
+ if (rport->bfa_rport)
+ bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_OFFLINE);
+ else
+ bfa_cb_rport_offline(rport);
+}
+
static struct bfa_fcs_rport_s *
bfa_fcs_rport_alloc(struct bfa_fcs_lport_s *port, wwn_t pwwn, u32 rpid)
{
@@ -1940,6 +2324,11 @@ bfa_fcs_rport_alloc(struct bfa_fcs_lport_s *port, wwn_t pwwn, u32 rpid)
/*
* allocate rport
*/
+ if (fcs->num_rport_logins >= bfa_fcs_rport_max_logins) {
+ bfa_trc(fcs, rpid);
+ return NULL;
+ }
+
if (bfa_fcb_rport_alloc(fcs->bfad, &rport, &rport_drv)
!= BFA_STATUS_OK) {
bfa_trc(fcs, rpid);
@@ -1954,16 +2343,9 @@ bfa_fcs_rport_alloc(struct bfa_fcs_lport_s *port, wwn_t pwwn, u32 rpid)
rport->rp_drv = rport_drv;
rport->pid = rpid;
rport->pwwn = pwwn;
+ rport->old_pid = 0;
- /*
- * allocate BFA rport
- */
- rport->bfa_rport = bfa_rport_create(port->fcs->bfa, rport);
- if (!rport->bfa_rport) {
- bfa_trc(fcs, rpid);
- kfree(rport_drv);
- return NULL;
- }
+ rport->bfa_rport = NULL;
/*
* allocate FC-4s
@@ -1974,14 +2356,13 @@ bfa_fcs_rport_alloc(struct bfa_fcs_lport_s *port, wwn_t pwwn, u32 rpid)
rport->itnim = bfa_fcs_itnim_create(rport);
if (!rport->itnim) {
bfa_trc(fcs, rpid);
- bfa_sm_send_event(rport->bfa_rport,
- BFA_RPORT_SM_DELETE);
kfree(rport_drv);
return NULL;
}
}
bfa_fcs_lport_add_rport(port, rport);
+ fcs->num_rport_logins++;
bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit);
@@ -1997,25 +2378,73 @@ static void
bfa_fcs_rport_free(struct bfa_fcs_rport_s *rport)
{
struct bfa_fcs_lport_s *port = rport->port;
+ struct bfa_fcs_s *fcs = port->fcs;
/*
* - delete FC-4s
* - delete BFA rport
* - remove from queue of rports
*/
+ rport->plogi_pending = BFA_FALSE;
+
if (bfa_fcs_lport_is_initiator(port)) {
bfa_fcs_itnim_delete(rport->itnim);
if (rport->pid != 0 && !BFA_FCS_PID_IS_WKA(rport->pid))
bfa_fcs_rpf_rport_offline(rport);
}
- bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_DELETE);
+ if (rport->bfa_rport) {
+ bfa_sm_send_event(rport->bfa_rport, BFA_RPORT_SM_DELETE);
+ rport->bfa_rport = NULL;
+ }
+
bfa_fcs_lport_del_rport(port, rport);
+ fcs->num_rport_logins--;
kfree(rport->rp_drv);
}
static void
-bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport)
+bfa_fcs_rport_aen_post(struct bfa_fcs_rport_s *rport,
+ enum bfa_rport_aen_event event,
+ struct bfa_rport_aen_data_s *data)
+{
+ struct bfa_fcs_lport_s *port = rport->port;
+ struct bfad_s *bfad = (struct bfad_s *)port->fcs->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ if (event == BFA_RPORT_AEN_QOS_PRIO)
+ aen_entry->aen_data.rport.priv.qos = data->priv.qos;
+ else if (event == BFA_RPORT_AEN_QOS_FLOWID)
+ aen_entry->aen_data.rport.priv.qos = data->priv.qos;
+
+ aen_entry->aen_data.rport.vf_id = rport->port->fabric->vf_id;
+ aen_entry->aen_data.rport.ppwwn = bfa_fcs_lport_get_pwwn(
+ bfa_fcs_get_base_port(rport->fcs));
+ aen_entry->aen_data.rport.lpwwn = bfa_fcs_lport_get_pwwn(rport->port);
+ aen_entry->aen_data.rport.rpwwn = rport->pwwn;
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++rport->fcs->fcs_aen_seq,
+ BFA_AEN_CAT_RPORT, event);
+}
+
+static void
+bfa_fcs_rport_fcs_online_action(struct bfa_fcs_rport_s *rport)
+{
+ if ((!rport->pid) || (!rport->pwwn)) {
+ bfa_trc(rport->fcs, rport->pid);
+ bfa_sm_fault(rport->fcs, rport->pid);
+ }
+
+ bfa_sm_send_event(rport->itnim, BFA_FCS_ITNIM_SM_FCS_ONLINE);
+}
+
+static void
+bfa_fcs_rport_hal_online_action(struct bfa_fcs_rport_s *rport)
{
struct bfa_fcs_lport_s *port = rport->port;
struct bfad_s *bfad = (struct bfad_s *)port->fcs->bfad;
@@ -2024,43 +2453,69 @@ bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport)
rport->stats.onlines++;
+ if ((!rport->pid) || (!rport->pwwn)) {
+ bfa_trc(rport->fcs, rport->pid);
+ bfa_sm_fault(rport->fcs, rport->pid);
+ }
+
if (bfa_fcs_lport_is_initiator(port)) {
- bfa_fcs_itnim_rport_online(rport->itnim);
+ bfa_fcs_itnim_brp_online(rport->itnim);
if (!BFA_FCS_PID_IS_WKA(rport->pid))
bfa_fcs_rpf_rport_online(rport);
- };
+ }
wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(port));
wwn2str(rpwwn_buf, rport->pwwn);
- if (!BFA_FCS_PID_IS_WKA(rport->pid))
+ if (!BFA_FCS_PID_IS_WKA(rport->pid)) {
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Remote port (WWN = %s) online for logical port (WWN = %s)\n",
rpwwn_buf, lpwwn_buf);
+ bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_ONLINE, NULL);
+ }
+}
+
+static void
+bfa_fcs_rport_fcs_offline_action(struct bfa_fcs_rport_s *rport)
+{
+ if (!BFA_FCS_PID_IS_WKA(rport->pid))
+ bfa_fcs_rpf_rport_offline(rport);
+
+ bfa_fcs_itnim_rport_offline(rport->itnim);
}
static void
-bfa_fcs_rport_offline_action(struct bfa_fcs_rport_s *rport)
+bfa_fcs_rport_hal_offline_action(struct bfa_fcs_rport_s *rport)
{
struct bfa_fcs_lport_s *port = rport->port;
struct bfad_s *bfad = (struct bfad_s *)port->fcs->bfad;
char lpwwn_buf[BFA_STRING_32];
char rpwwn_buf[BFA_STRING_32];
+ if (!rport->bfa_rport) {
+ bfa_fcs_rport_fcs_offline_action(rport);
+ return;
+ }
+
rport->stats.offlines++;
wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(port));
wwn2str(rpwwn_buf, rport->pwwn);
if (!BFA_FCS_PID_IS_WKA(rport->pid)) {
- if (bfa_fcs_lport_is_online(rport->port) == BFA_TRUE)
+ if (bfa_fcs_lport_is_online(rport->port) == BFA_TRUE) {
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
"Remote port (WWN = %s) connectivity lost for "
"logical port (WWN = %s)\n",
rpwwn_buf, lpwwn_buf);
- else
+ bfa_fcs_rport_aen_post(rport,
+ BFA_RPORT_AEN_DISCONNECT, NULL);
+ } else {
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Remote port (WWN = %s) offlined by "
"logical port (WWN = %s)\n",
rpwwn_buf, lpwwn_buf);
+ bfa_fcs_rport_aen_post(rport,
+ BFA_RPORT_AEN_OFFLINE, NULL);
+ }
}
if (bfa_fcs_lport_is_initiator(port)) {
@@ -2100,7 +2555,10 @@ bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport, struct fc_logi_s *plogi)
* - MAX receive frame size
*/
rport->cisc = plogi->csp.cisc;
- rport->maxfrsize = be16_to_cpu(plogi->class3.rxsz);
+ if (be16_to_cpu(plogi->class3.rxsz) < be16_to_cpu(plogi->csp.rxsz))
+ rport->maxfrsize = be16_to_cpu(plogi->class3.rxsz);
+ else
+ rport->maxfrsize = be16_to_cpu(plogi->csp.rxsz);
bfa_trc(port->fcs, be16_to_cpu(plogi->csp.bbcred));
bfa_trc(port->fcs, port->fabric->bb_credit);
@@ -2188,27 +2646,6 @@ bfa_fcs_rport_create_by_wwn(struct bfa_fcs_lport_s *port, wwn_t rpwwn)
bfa_sm_send_event(rport, RPSM_EVENT_ADDRESS_DISC);
return rport;
}
-/*
- * Called by bport in private loop topology to indicate that a
- * rport has been discovered and plogi has been completed.
- *
- * @param[in] port - base port or vport
- * @param[in] rpid - remote port ID
- */
-void
-bfa_fcs_rport_start(struct bfa_fcs_lport_s *port, struct fchs_s *fchs,
- struct fc_logi_s *plogi)
-{
- struct bfa_fcs_rport_s *rport;
-
- rport = bfa_fcs_rport_alloc(port, WWN_NULL, fchs->s_id);
- if (!rport)
- return;
-
- bfa_fcs_rport_update(rport, plogi);
-
- bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_COMP);
-}
/*
* Called by bport/vport to handle PLOGI received from a new remote port.
@@ -2233,22 +2670,6 @@ bfa_fcs_rport_plogi_create(struct bfa_fcs_lport_s *port, struct fchs_s *fchs,
bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_RCVD);
}
-static int
-wwn_compare(wwn_t wwn1, wwn_t wwn2)
-{
- u8 *b1 = (u8 *) &wwn1;
- u8 *b2 = (u8 *) &wwn2;
- int i;
-
- for (i = 0; i < sizeof(wwn_t); i++) {
- if (b1[i] < b2[i])
- return -1;
- if (b1[i] > b2[i])
- return 1;
- }
- return 0;
-}
-
/*
* Called by bport/vport to handle PLOGI received from an existing
* remote port.
@@ -2266,19 +2687,8 @@ bfa_fcs_rport_plogi(struct bfa_fcs_rport_s *rport, struct fchs_s *rx_fchs,
rport->reply_oxid = rx_fchs->ox_id;
bfa_trc(rport->fcs, rport->reply_oxid);
- /*
- * In Switched fabric topology,
- * PLOGI to each other. If our pwwn is smaller, ignore it,
- * if it is not a well known address.
- * If the link topology is N2N,
- * this Plogi should be accepted.
- */
- if ((wwn_compare(rport->port->port_cfg.pwwn, rport->pwwn) == -1) &&
- (bfa_fcs_fabric_is_switched(rport->port->fabric)) &&
- (!BFA_FCS_PID_IS_WKA(rport->pid))) {
- bfa_trc(rport->fcs, rport->pid);
- return;
- }
+ rport->pid = rx_fchs->s_id;
+ bfa_trc(rport->fcs, rport->pid);
rport->stats.plogi_rcvd++;
bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_RCVD);
@@ -2292,10 +2702,9 @@ void
bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport)
{
rport->stats.rscns++;
- bfa_sm_send_event(rport, RPSM_EVENT_SCN);
+ bfa_sm_send_event(rport, RPSM_EVENT_FAB_SCN);
}
-
/*
* brief
* This routine BFA callback for bfa_rport_online() call.
@@ -2361,8 +2770,53 @@ bfa_cb_rport_qos_scn_flowid(void *cbarg,
struct bfa_rport_qos_attr_s new_qos_attr)
{
struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *) cbarg;
+ struct bfa_rport_aen_data_s aen_data;
bfa_trc(rport->fcs, rport->pwwn);
+ aen_data.priv.qos = new_qos_attr;
+ bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_FLOWID, &aen_data);
+}
+
+void
+bfa_cb_rport_scn_online(struct bfa_s *bfa)
+{
+ struct bfa_fcs_s *fcs = &((struct bfad_s *)bfa->bfad)->bfa_fcs;
+ struct bfa_fcs_lport_s *port = bfa_fcs_get_base_port(fcs);
+ struct bfa_fcs_rport_s *rp;
+ struct list_head *qe;
+
+ list_for_each(qe, &port->rport_q) {
+ rp = (struct bfa_fcs_rport_s *) qe;
+ bfa_sm_send_event(rp, RPSM_EVENT_SCN_ONLINE);
+ rp->scn_online = BFA_TRUE;
+ }
+
+ if (bfa_fcs_lport_is_online(port))
+ bfa_fcs_lport_lip_scn_online(port);
+}
+
+void
+bfa_cb_rport_scn_no_dev(void *rport)
+{
+ struct bfa_fcs_rport_s *rp = rport;
+
+ bfa_sm_send_event(rp, RPSM_EVENT_SCN_OFFLINE);
+ rp->scn_online = BFA_FALSE;
+}
+
+void
+bfa_cb_rport_scn_offline(struct bfa_s *bfa)
+{
+ struct bfa_fcs_s *fcs = &((struct bfad_s *)bfa->bfad)->bfa_fcs;
+ struct bfa_fcs_lport_s *port = bfa_fcs_get_base_port(fcs);
+ struct bfa_fcs_rport_s *rp;
+ struct list_head *qe;
+
+ list_for_each(qe, &port->rport_q) {
+ rp = (struct bfa_fcs_rport_s *) qe;
+ bfa_sm_send_event(rp, RPSM_EVENT_SCN_OFFLINE);
+ rp->scn_online = BFA_FALSE;
+ }
}
/*
@@ -2385,8 +2839,11 @@ bfa_cb_rport_qos_scn_prio(void *cbarg,
struct bfa_rport_qos_attr_s new_qos_attr)
{
struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *) cbarg;
+ struct bfa_rport_aen_data_s aen_data;
bfa_trc(rport->fcs, rport->pwwn);
+ aen_data.priv.qos = new_qos_attr;
+ bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_PRIO, &aen_data);
}
/*
@@ -2457,7 +2914,7 @@ bfa_fcs_rport_send_prlo_acc(struct bfa_fcs_rport_s *rport)
bfa_trc(rport->fcs, rport->pid);
- fcxp = bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(port->fcs, BFA_FALSE);
if (!fcxp)
return;
len = fc_prlo_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
@@ -2483,7 +2940,7 @@ bfa_fcs_rport_send_ls_rjt(struct bfa_fcs_rport_s *rport, struct fchs_s *rx_fchs,
bfa_trc(rport->fcs, rx_fchs->s_id);
- fcxp = bfa_fcs_fcxp_alloc(rport->fcs);
+ fcxp = bfa_fcs_fcxp_alloc(rport->fcs, BFA_FALSE);
if (!fcxp)
return;
@@ -2502,7 +2959,7 @@ bfa_fcs_rport_send_ls_rjt(struct bfa_fcs_rport_s *rport, struct fchs_s *rx_fchs,
int
bfa_fcs_rport_get_state(struct bfa_fcs_rport_s *rport)
{
- return bfa_sm_to_state(rport_sm_table, rport->sm);
+ return bfa_rport_sm_to_state(rport_sm_table, rport->sm);
}
@@ -2531,7 +2988,62 @@ bfa_fcs_rport_prlo(struct bfa_fcs_rport_s *rport, __be16 ox_id)
bfa_sm_send_event(rport, RPSM_EVENT_PRLO_RCVD);
}
+/*
+ * Called by BFAD to set the max limit on number of bfa_fcs_rport allocation
+ * which limits number of concurrent logins to remote ports
+ */
+void
+bfa_fcs_rport_set_max_logins(u32 max_logins)
+{
+ if (max_logins > 0)
+ bfa_fcs_rport_max_logins = max_logins;
+}
+void
+bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport,
+ struct bfa_rport_attr_s *rport_attr)
+{
+ struct bfa_rport_qos_attr_s qos_attr;
+ struct bfa_fcs_lport_s *port = rport->port;
+ bfa_port_speed_t rport_speed = rport->rpf.rpsc_speed;
+ struct bfa_port_attr_s port_attr;
+
+ bfa_fcport_get_attr(rport->fcs->bfa, &port_attr);
+
+ memset(rport_attr, 0, sizeof(struct bfa_rport_attr_s));
+ memset(&qos_attr, 0, sizeof(struct bfa_rport_qos_attr_s));
+
+ rport_attr->pid = rport->pid;
+ rport_attr->pwwn = rport->pwwn;
+ rport_attr->nwwn = rport->nwwn;
+ rport_attr->cos_supported = rport->fc_cos;
+ rport_attr->df_sz = rport->maxfrsize;
+ rport_attr->state = bfa_fcs_rport_get_state(rport);
+ rport_attr->fc_cos = rport->fc_cos;
+ rport_attr->cisc = rport->cisc;
+ rport_attr->scsi_function = rport->scsi_function;
+ rport_attr->curr_speed = rport->rpf.rpsc_speed;
+ rport_attr->assigned_speed = rport->rpf.assigned_speed;
+
+ if (rport->bfa_rport) {
+ qos_attr.qos_priority = rport->bfa_rport->qos_attr.qos_priority;
+ qos_attr.qos_flow_id =
+ cpu_to_be32(rport->bfa_rport->qos_attr.qos_flow_id);
+ }
+ rport_attr->qos_attr = qos_attr;
+
+ rport_attr->trl_enforced = BFA_FALSE;
+ if (bfa_fcport_is_ratelim(port->fcs->bfa) &&
+ (rport->scsi_function == BFA_RPORT_TARGET)) {
+ if (rport_speed == BFA_PORT_SPEED_UNKNOWN)
+ rport_speed =
+ bfa_fcport_get_ratelim_speed(rport->fcs->bfa);
+
+ if ((bfa_fcs_lport_get_rport_max_speed(port) !=
+ BFA_PORT_SPEED_UNKNOWN) && (rport_speed < port_attr.speed))
+ rport_attr->trl_enforced = BFA_TRUE;
+ }
+}
/*
* Remote port implementation.
@@ -2556,21 +3068,6 @@ bfa_fcs_rport_lookup(struct bfa_fcs_lport_s *port, wwn_t rpwwn)
return rport;
}
-struct bfa_fcs_rport_s *
-bfa_fcs_rport_lookup_by_nwwn(struct bfa_fcs_lport_s *port, wwn_t rnwwn)
-{
- struct bfa_fcs_rport_s *rport;
-
- rport = bfa_fcs_lport_get_rport_by_nwwn(port, rnwwn);
- if (rport == NULL) {
- /*
- * TBD Error handling
- */
- }
-
- return rport;
-}
-
/*
* Remote port features (RPF) implementation.
*/
@@ -2590,20 +3087,6 @@ static void bfa_fcs_rpf_rpsc2_response(void *fcsarg,
static void bfa_fcs_rpf_timeout(void *arg);
-/*
- * fcs_rport_ftrs_sm FCS rport state machine events
- */
-
-enum rpf_event {
- RPFSM_EVENT_RPORT_OFFLINE = 1, /* Rport offline */
- RPFSM_EVENT_RPORT_ONLINE = 2, /* Rport online */
- RPFSM_EVENT_FCXP_SENT = 3, /* Frame from has been sent */
- RPFSM_EVENT_TIMEOUT = 4, /* Rport SM timeout event */
- RPFSM_EVENT_RPSC_COMP = 5,
- RPFSM_EVENT_RPSC_FAIL = 6,
- RPFSM_EVENT_RPSC_ERROR = 7,
-};
-
static void bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf,
enum rpf_event event);
static void bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf,
@@ -2851,10 +3334,11 @@ bfa_fcs_rpf_send_rpsc2(void *rpf_cbarg, struct bfa_fcxp_s *fcxp_alloced)
bfa_trc(rport->fcs, rport->pwwn);
- fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
+ fcxp = fcxp_alloced ? fcxp_alloced :
+ bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
if (!fcxp) {
bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &rpf->fcxp_wqe,
- bfa_fcs_rpf_send_rpsc2, rpf);
+ bfa_fcs_rpf_send_rpsc2, rpf, BFA_TRUE);
return;
}
rpf->fcxp = fcxp;
@@ -2897,9 +3381,10 @@ bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
num_ents = be16_to_cpu(rpsc2_acc->num_pids);
bfa_trc(rport->fcs, num_ents);
if (num_ents > 0) {
- WARN_ON(rpsc2_acc->port_info[0].pid == rport->pid);
+ WARN_ON(be32_to_cpu(rpsc2_acc->port_info[0].pid) !=
+ bfa_ntoh3b(rport->pid));
bfa_trc(rport->fcs,
- be16_to_cpu(rpsc2_acc->port_info[0].pid));
+ be32_to_cpu(rpsc2_acc->port_info[0].pid));
bfa_trc(rport->fcs,
be16_to_cpu(rpsc2_acc->port_info[0].speed));
bfa_trc(rport->fcs,
diff --git a/drivers/scsi/bfa/bfa_hw_cb.c b/drivers/scsi/bfa/bfa_hw_cb.c
index 977e681ec803..6cc2f7290d7e 100644
--- a/drivers/scsi/bfa/bfa_hw_cb.c
+++ b/drivers/scsi/bfa/bfa_hw_cb.c
@@ -1,30 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
#include "bfa_modules.h"
-#include "bfi_cbreg.h"
+#include "bfi_reg.h"
void
bfa_hwcb_reginit(struct bfa_s *bfa)
{
struct bfa_iocfc_regs_s *bfa_regs = &bfa->iocfc.bfa_regs;
void __iomem *kva = bfa_ioc_bar0(&bfa->ioc);
- int i, q, fn = bfa_ioc_pcifn(&bfa->ioc);
+ int fn = bfa_ioc_pcifn(&bfa->ioc);
if (fn == 0) {
bfa_regs->intr_status = (kva + HOSTFN0_INT_STATUS);
@@ -33,29 +26,6 @@ bfa_hwcb_reginit(struct bfa_s *bfa)
bfa_regs->intr_status = (kva + HOSTFN1_INT_STATUS);
bfa_regs->intr_mask = (kva + HOSTFN1_INT_MSK);
}
-
- for (i = 0; i < BFI_IOC_MAX_CQS; i++) {
- /*
- * CPE registers
- */
- q = CPE_Q_NUM(fn, i);
- bfa_regs->cpe_q_pi[i] = (kva + CPE_Q_PI(q));
- bfa_regs->cpe_q_ci[i] = (kva + CPE_Q_CI(q));
- bfa_regs->cpe_q_depth[i] = (kva + CPE_Q_DEPTH(q));
-
- /*
- * RME registers
- */
- q = CPE_Q_NUM(fn, i);
- bfa_regs->rme_q_pi[i] = (kva + RME_Q_PI(q));
- bfa_regs->rme_q_ci[i] = (kva + RME_Q_CI(q));
- bfa_regs->rme_q_depth[i] = (kva + RME_Q_DEPTH(q));
- }
-}
-
-void
-bfa_hwcb_reqq_ack(struct bfa_s *bfa, int reqq)
-{
}
static void
@@ -65,16 +35,34 @@ bfa_hwcb_reqq_ack_msix(struct bfa_s *bfa, int reqq)
bfa->iocfc.bfa_regs.intr_status);
}
-void
-bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq)
+/*
+ * Actions to respond RME Interrupt for Crossbow ASIC:
+ * - Write 1 to Interrupt Status register
+ * INTX - done in bfa_intx()
+ * MSIX - done in bfa_hwcb_rspq_ack_msix()
+ * - Update CI (only if new CI)
+ */
+static void
+bfa_hwcb_rspq_ack_msix(struct bfa_s *bfa, int rspq, u32 ci)
{
+ writel(__HFN_INT_RME_Q0 << RME_Q_NUM(bfa_ioc_pcifn(&bfa->ioc), rspq),
+ bfa->iocfc.bfa_regs.intr_status);
+
+ if (bfa_rspq_ci(bfa, rspq) == ci)
+ return;
+
+ bfa_rspq_ci(bfa, rspq) = ci;
+ writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
}
-static void
-bfa_hwcb_rspq_ack_msix(struct bfa_s *bfa, int rspq)
+void
+bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci)
{
- writel(__HFN_INT_RME_Q0 << RME_Q_NUM(bfa_ioc_pcifn(&bfa->ioc), rspq),
- bfa->iocfc.bfa_regs.intr_status);
+ if (bfa_rspq_ci(bfa, rspq) == ci)
+ return;
+
+ bfa_rspq_ci(bfa, rspq) = ci;
+ writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
}
void
@@ -104,43 +92,71 @@ bfa_hwcb_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap,
}
/*
+ * Dummy interrupt handler for handling spurious interrupts.
+ */
+static void
+bfa_hwcb_msix_dummy(struct bfa_s *bfa, int vec)
+{
+}
+
+/*
* No special setup required for crossbow -- vector assignments are implicit.
*/
void
bfa_hwcb_msix_init(struct bfa_s *bfa, int nvecs)
{
- int i;
-
WARN_ON((nvecs != 1) && (nvecs != __HFN_NUMINTS));
bfa->msix.nvecs = nvecs;
- if (nvecs == 1) {
- for (i = 0; i < BFA_MSIX_CB_MAX; i++)
+ bfa_hwcb_msix_uninstall(bfa);
+}
+
+void
+bfa_hwcb_msix_ctrl_install(struct bfa_s *bfa)
+{
+ int i;
+
+ if (bfa->msix.nvecs == 0)
+ return;
+
+ if (bfa->msix.nvecs == 1) {
+ for (i = BFI_MSIX_CPE_QMIN_CB; i < BFI_MSIX_CB_MAX; i++)
bfa->msix.handler[i] = bfa_msix_all;
return;
}
- for (i = BFA_MSIX_CPE_Q0; i <= BFA_MSIX_CPE_Q7; i++)
- bfa->msix.handler[i] = bfa_msix_reqq;
-
- for (i = BFA_MSIX_RME_Q0; i <= BFA_MSIX_RME_Q7; i++)
- bfa->msix.handler[i] = bfa_msix_rspq;
-
- for (; i < BFA_MSIX_CB_MAX; i++)
+ for (i = BFI_MSIX_RME_QMAX_CB+1; i < BFI_MSIX_CB_MAX; i++)
bfa->msix.handler[i] = bfa_msix_lpu_err;
}
-/*
- * Crossbow -- dummy, interrupts are masked
- */
void
-bfa_hwcb_msix_install(struct bfa_s *bfa)
+bfa_hwcb_msix_queue_install(struct bfa_s *bfa)
{
+ int i;
+
+ if (bfa->msix.nvecs == 0)
+ return;
+
+ if (bfa->msix.nvecs == 1) {
+ for (i = BFI_MSIX_CPE_QMIN_CB; i <= BFI_MSIX_RME_QMAX_CB; i++)
+ bfa->msix.handler[i] = bfa_msix_all;
+ return;
+ }
+
+ for (i = BFI_MSIX_CPE_QMIN_CB; i <= BFI_MSIX_CPE_QMAX_CB; i++)
+ bfa->msix.handler[i] = bfa_msix_reqq;
+
+ for (i = BFI_MSIX_RME_QMIN_CB; i <= BFI_MSIX_RME_QMAX_CB; i++)
+ bfa->msix.handler[i] = bfa_msix_rspq;
}
void
bfa_hwcb_msix_uninstall(struct bfa_s *bfa)
{
+ int i;
+
+ for (i = 0; i < BFI_MSIX_CB_MAX; i++)
+ bfa->msix.handler[i] = bfa_hwcb_msix_dummy;
}
/*
@@ -149,13 +165,18 @@ bfa_hwcb_msix_uninstall(struct bfa_s *bfa)
void
bfa_hwcb_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix)
{
- bfa->iocfc.hwif.hw_reqq_ack = bfa_hwcb_reqq_ack_msix;
- bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack_msix;
+ if (msix) {
+ bfa->iocfc.hwif.hw_reqq_ack = bfa_hwcb_reqq_ack_msix;
+ bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack_msix;
+ } else {
+ bfa->iocfc.hwif.hw_reqq_ack = NULL;
+ bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack;
+ }
}
void
bfa_hwcb_msix_get_rme_range(struct bfa_s *bfa, u32 *start, u32 *end)
{
- *start = BFA_MSIX_RME_Q0;
- *end = BFA_MSIX_RME_Q7;
+ *start = BFI_MSIX_RME_QMIN_CB;
+ *end = BFI_MSIX_RME_QMAX_CB;
}
diff --git a/drivers/scsi/bfa/bfa_hw_ct.c b/drivers/scsi/bfa/bfa_hw_ct.c
index 21018d98a07b..4b1c0a568a22 100644
--- a/drivers/scsi/bfa/bfa_hw_ct.c
+++ b/drivers/scsi/bfa/bfa_hw_ct.c
@@ -1,45 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
#include "bfa_modules.h"
-#include "bfi_ctreg.h"
+#include "bfi_reg.h"
BFA_TRC_FILE(HAL, IOCFC_CT);
-static u32 __ct_msix_err_vec_reg[] = {
- HOST_MSIX_ERR_INDEX_FN0,
- HOST_MSIX_ERR_INDEX_FN1,
- HOST_MSIX_ERR_INDEX_FN2,
- HOST_MSIX_ERR_INDEX_FN3,
-};
-
-static void
-bfa_hwct_msix_lpu_err_set(struct bfa_s *bfa, bfa_boolean_t msix, int vec)
-{
- int fn = bfa_ioc_pcifn(&bfa->ioc);
- void __iomem *kva = bfa_ioc_bar0(&bfa->ioc);
-
- if (msix)
- writel(vec, kva + __ct_msix_err_vec_reg[fn]);
- else
- writel(0, kva + __ct_msix_err_vec_reg[fn]);
-}
-
/*
* Dummy interrupt handler for handling spurious interrupt during chip-reinit.
*/
@@ -53,7 +27,7 @@ bfa_hwct_reginit(struct bfa_s *bfa)
{
struct bfa_iocfc_regs_s *bfa_regs = &bfa->iocfc.bfa_regs;
void __iomem *kva = bfa_ioc_bar0(&bfa->ioc);
- int i, q, fn = bfa_ioc_pcifn(&bfa->ioc);
+ int fn = bfa_ioc_pcifn(&bfa->ioc);
if (fn == 0) {
bfa_regs->intr_status = (kva + HOSTFN0_INT_STATUS);
@@ -62,26 +36,16 @@ bfa_hwct_reginit(struct bfa_s *bfa)
bfa_regs->intr_status = (kva + HOSTFN1_INT_STATUS);
bfa_regs->intr_mask = (kva + HOSTFN1_INT_MSK);
}
+}
- for (i = 0; i < BFI_IOC_MAX_CQS; i++) {
- /*
- * CPE registers
- */
- q = CPE_Q_NUM(fn, i);
- bfa_regs->cpe_q_pi[i] = (kva + CPE_PI_PTR_Q(q << 5));
- bfa_regs->cpe_q_ci[i] = (kva + CPE_CI_PTR_Q(q << 5));
- bfa_regs->cpe_q_depth[i] = (kva + CPE_DEPTH_Q(q << 5));
- bfa_regs->cpe_q_ctrl[i] = (kva + CPE_QCTRL_Q(q << 5));
-
- /*
- * RME registers
- */
- q = CPE_Q_NUM(fn, i);
- bfa_regs->rme_q_pi[i] = (kva + RME_PI_PTR_Q(q << 5));
- bfa_regs->rme_q_ci[i] = (kva + RME_CI_PTR_Q(q << 5));
- bfa_regs->rme_q_depth[i] = (kva + RME_DEPTH_Q(q << 5));
- bfa_regs->rme_q_ctrl[i] = (kva + RME_QCTRL_Q(q << 5));
- }
+void
+bfa_hwct2_reginit(struct bfa_s *bfa)
+{
+ struct bfa_iocfc_regs_s *bfa_regs = &bfa->iocfc.bfa_regs;
+ void __iomem *kva = bfa_ioc_bar0(&bfa->ioc);
+
+ bfa_regs->intr_status = (kva + CT2_HOSTFN_INT_STATUS);
+ bfa_regs->intr_mask = (kva + CT2_HOSTFN_INTR_MASK);
}
void
@@ -93,22 +57,43 @@ bfa_hwct_reqq_ack(struct bfa_s *bfa, int reqq)
writel(r32, bfa->iocfc.bfa_regs.cpe_q_ctrl[reqq]);
}
+/*
+ * Actions to respond RME Interrupt for Catapult ASIC:
+ * - Write 1 to Interrupt Status register (INTx only - done in bfa_intx())
+ * - Acknowledge by writing to RME Queue Control register
+ * - Update CI
+ */
void
-bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq)
+bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci)
{
u32 r32;
r32 = readl(bfa->iocfc.bfa_regs.rme_q_ctrl[rspq]);
writel(r32, bfa->iocfc.bfa_regs.rme_q_ctrl[rspq]);
+
+ bfa_rspq_ci(bfa, rspq) = ci;
+ writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
+}
+
+/*
+ * Actions to respond RME Interrupt for Catapult2 ASIC:
+ * - Write 1 to Interrupt Status register (INTx only - done in bfa_intx())
+ * - Update CI
+ */
+void
+bfa_hwct2_rspq_ack(struct bfa_s *bfa, int rspq, u32 ci)
+{
+ bfa_rspq_ci(bfa, rspq) = ci;
+ writel(ci, bfa->iocfc.bfa_regs.rme_q_ci[rspq]);
}
void
bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap,
u32 *num_vecs, u32 *max_vec_bit)
{
- *msix_vecs_bmap = (1 << BFA_MSIX_CT_MAX) - 1;
- *max_vec_bit = (1 << (BFA_MSIX_CT_MAX - 1));
- *num_vecs = BFA_MSIX_CT_MAX;
+ *msix_vecs_bmap = (1 << BFI_MSIX_CT_MAX) - 1;
+ *max_vec_bit = (1 << (BFI_MSIX_CT_MAX - 1));
+ *num_vecs = BFI_MSIX_CT_MAX;
}
/*
@@ -117,7 +102,7 @@ bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap,
void
bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs)
{
- WARN_ON((nvecs != 1) && (nvecs != BFA_MSIX_CT_MAX));
+ WARN_ON((nvecs != 1) && (nvecs != BFI_MSIX_CT_MAX));
bfa_trc(bfa, nvecs);
bfa->msix.nvecs = nvecs;
@@ -125,7 +110,19 @@ bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs)
}
void
-bfa_hwct_msix_install(struct bfa_s *bfa)
+bfa_hwct_msix_ctrl_install(struct bfa_s *bfa)
+{
+ if (bfa->msix.nvecs == 0)
+ return;
+
+ if (bfa->msix.nvecs == 1)
+ bfa->msix.handler[BFI_MSIX_LPU_ERR_CT] = bfa_msix_all;
+ else
+ bfa->msix.handler[BFI_MSIX_LPU_ERR_CT] = bfa_msix_lpu_err;
+}
+
+void
+bfa_hwct_msix_queue_install(struct bfa_s *bfa)
{
int i;
@@ -133,19 +130,16 @@ bfa_hwct_msix_install(struct bfa_s *bfa)
return;
if (bfa->msix.nvecs == 1) {
- for (i = 0; i < BFA_MSIX_CT_MAX; i++)
+ for (i = BFI_MSIX_CPE_QMIN_CT; i < BFI_MSIX_CT_MAX; i++)
bfa->msix.handler[i] = bfa_msix_all;
return;
}
- for (i = BFA_MSIX_CPE_Q0; i <= BFA_MSIX_CPE_Q3; i++)
+ for (i = BFI_MSIX_CPE_QMIN_CT; i <= BFI_MSIX_CPE_QMAX_CT; i++)
bfa->msix.handler[i] = bfa_msix_reqq;
- for (; i <= BFA_MSIX_RME_Q3; i++)
+ for (i = BFI_MSIX_RME_QMIN_CT; i <= BFI_MSIX_RME_QMAX_CT; i++)
bfa->msix.handler[i] = bfa_msix_rspq;
-
- WARN_ON(i != BFA_MSIX_LPU_ERR);
- bfa->msix.handler[BFA_MSIX_LPU_ERR] = bfa_msix_lpu_err;
}
void
@@ -153,7 +147,7 @@ bfa_hwct_msix_uninstall(struct bfa_s *bfa)
{
int i;
- for (i = 0; i < BFA_MSIX_CT_MAX; i++)
+ for (i = 0; i < BFI_MSIX_CT_MAX; i++)
bfa->msix.handler[i] = bfa_hwct_msix_dummy;
}
@@ -164,13 +158,12 @@ void
bfa_hwct_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix)
{
bfa_trc(bfa, 0);
- bfa_hwct_msix_lpu_err_set(bfa, msix, BFA_MSIX_LPU_ERR);
bfa_ioc_isr_mode_set(&bfa->ioc, msix);
}
void
bfa_hwct_msix_get_rme_range(struct bfa_s *bfa, u32 *start, u32 *end)
{
- *start = BFA_MSIX_RME_Q0;
- *end = BFA_MSIX_RME_Q3;
+ *start = BFI_MSIX_RME_QMIN_CT;
+ *end = BFI_MSIX_RME_QMAX_CT;
}
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 6c7e0339dda4..aa68d61a2d0d 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -1,25 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
+#include "bfad_im.h"
#include "bfa_ioc.h"
-#include "bfi_ctreg.h"
+#include "bfi_reg.h"
#include "bfa_defs.h"
#include "bfa_defs_svc.h"
+#include "bfi.h"
BFA_TRC_FILE(CNA, IOC);
@@ -29,8 +24,8 @@ BFA_TRC_FILE(CNA, IOC);
#define BFA_IOC_TOV 3000 /* msecs */
#define BFA_IOC_HWSEM_TOV 500 /* msecs */
#define BFA_IOC_HB_TOV 500 /* msecs */
-#define BFA_IOC_HWINIT_MAX 5
#define BFA_IOC_TOV_RECOVER BFA_IOC_HB_TOV
+#define BFA_IOC_POLL_TOV BFA_TIMER_FREQ
#define bfa_ioc_timer_start(__ioc) \
bfa_timer_begin((__ioc)->timer_mod, &(__ioc)->ioc_timer, \
@@ -44,6 +39,14 @@ BFA_TRC_FILE(CNA, IOC);
#define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn))
+#define bfa_ioc_state_disabled(__sm) \
+ (((__sm) == BFI_IOC_UNINIT) || \
+ ((__sm) == BFI_IOC_INITING) || \
+ ((__sm) == BFI_IOC_HWINIT) || \
+ ((__sm) == BFI_IOC_DISABLED) || \
+ ((__sm) == BFI_IOC_FAIL) || \
+ ((__sm) == BFI_IOC_CFG_DISABLED))
+
/*
* Asic specific macros : see bfa_hw_cb.c and bfa_hw_ct.c for details.
*/
@@ -66,6 +69,14 @@ BFA_TRC_FILE(CNA, IOC);
((__ioc)->ioc_hwif->ioc_sync_ack(__ioc))
#define bfa_ioc_sync_complete(__ioc) \
((__ioc)->ioc_hwif->ioc_sync_complete(__ioc))
+#define bfa_ioc_set_cur_ioc_fwstate(__ioc, __fwstate) \
+ ((__ioc)->ioc_hwif->ioc_set_fwstate(__ioc, __fwstate))
+#define bfa_ioc_get_cur_ioc_fwstate(__ioc) \
+ ((__ioc)->ioc_hwif->ioc_get_fwstate(__ioc))
+#define bfa_ioc_set_alt_ioc_fwstate(__ioc, __fwstate) \
+ ((__ioc)->ioc_hwif->ioc_set_alt_fwstate(__ioc, __fwstate))
+#define bfa_ioc_get_alt_ioc_fwstate(__ioc) \
+ ((__ioc)->ioc_hwif->ioc_get_alt_fwstate(__ioc))
#define bfa_ioc_mbox_cmd_pending(__ioc) \
(!list_empty(&((__ioc)->mbox_mod.cmd_q)) || \
@@ -79,39 +90,30 @@ bfa_boolean_t bfa_auto_recover = BFA_TRUE;
static void bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc);
static void bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force);
static void bfa_ioc_timeout(void *ioc);
+static void bfa_ioc_poll_fwinit(struct bfa_ioc_s *ioc);
static void bfa_ioc_send_enable(struct bfa_ioc_s *ioc);
static void bfa_ioc_send_disable(struct bfa_ioc_s *ioc);
static void bfa_ioc_send_getattr(struct bfa_ioc_s *ioc);
static void bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc);
static void bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc);
-static void bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc);
+static void bfa_ioc_mbox_flush(struct bfa_ioc_s *ioc);
static void bfa_ioc_recover(struct bfa_ioc_s *ioc);
-static void bfa_ioc_check_attr_wwns(struct bfa_ioc_s *ioc);
+static void bfa_ioc_event_notify(struct bfa_ioc_s *ioc ,
+ enum bfa_ioc_event_e event);
static void bfa_ioc_disable_comp(struct bfa_ioc_s *ioc);
static void bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc);
-static void bfa_ioc_debug_save_ftrc(struct bfa_ioc_s *ioc);
static void bfa_ioc_fail_notify(struct bfa_ioc_s *ioc);
static void bfa_ioc_pf_fwmismatch(struct bfa_ioc_s *ioc);
-
+static enum bfi_ioc_img_ver_cmp_e bfa_ioc_fw_ver_patch_cmp(
+ struct bfi_ioc_image_hdr_s *base_fwhdr,
+ struct bfi_ioc_image_hdr_s *fwhdr_to_cmp);
+static enum bfi_ioc_img_ver_cmp_e bfa_ioc_flash_fwver_cmp(
+ struct bfa_ioc_s *ioc,
+ struct bfi_ioc_image_hdr_s *base_fwhdr);
/*
* IOC state machine definitions/declarations
*/
-enum ioc_event {
- IOC_E_RESET = 1, /* IOC reset request */
- IOC_E_ENABLE = 2, /* IOC enable request */
- IOC_E_DISABLE = 3, /* IOC disable request */
- IOC_E_DETACH = 4, /* driver detach cleanup */
- IOC_E_ENABLED = 5, /* f/w enabled */
- IOC_E_FWRSP_GETATTR = 6, /* IOC get attribute response */
- IOC_E_DISABLED = 7, /* f/w disabled */
- IOC_E_INITFAILED = 8, /* failure notice by iocpf sm */
- IOC_E_PFFAILED = 9, /* failure notice by iocpf sm */
- IOC_E_HBFAIL = 10, /* heartbeat failure */
- IOC_E_HWERROR = 11, /* hardware error interrupt */
- IOC_E_TIMEOUT = 12, /* timeout */
-};
-
bfa_fsm_state_decl(bfa_ioc, uninit, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, reset, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, enabling, struct bfa_ioc_s, enum ioc_event);
@@ -121,8 +123,15 @@ bfa_fsm_state_decl(bfa_ioc, fail_retry, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, fail, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, hwfail, struct bfa_ioc_s, enum ioc_event);
-static struct bfa_sm_table_s ioc_sm_table[] = {
+struct bfa_ioc_sm_table {
+ bfa_ioc_sm_t sm; /* state machine function */
+ enum bfa_ioc_state state; /* state machine encoding */
+ char *name; /* state name for display */
+};
+
+static struct bfa_ioc_sm_table ioc_sm_table[] = {
{BFA_SM(bfa_ioc_sm_uninit), BFA_IOC_UNINIT},
{BFA_SM(bfa_ioc_sm_reset), BFA_IOC_RESET},
{BFA_SM(bfa_ioc_sm_enabling), BFA_IOC_ENABLING},
@@ -132,8 +141,19 @@ static struct bfa_sm_table_s ioc_sm_table[] = {
{BFA_SM(bfa_ioc_sm_fail), BFA_IOC_FAIL},
{BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING},
{BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED},
+ {BFA_SM(bfa_ioc_sm_hwfail), BFA_IOC_HWFAIL},
};
+static inline enum bfa_ioc_state
+bfa_ioc_sm_to_state(struct bfa_ioc_sm_table *smt, bfa_ioc_sm_t sm)
+{
+ int i = 0;
+
+ while (smt[i].sm && smt[i].sm != sm)
+ i++;
+ return smt[i].state;
+}
+
/*
* IOCPF state machine definitions/declarations
*/
@@ -143,9 +163,9 @@ static struct bfa_sm_table_s ioc_sm_table[] = {
bfa_iocpf_timeout, (__ioc), BFA_IOC_TOV)
#define bfa_iocpf_timer_stop(__ioc) bfa_timer_stop(&(__ioc)->ioc_timer)
-#define bfa_iocpf_recovery_timer_start(__ioc) \
+#define bfa_iocpf_poll_timer_start(__ioc) \
bfa_timer_begin((__ioc)->timer_mod, &(__ioc)->ioc_timer, \
- bfa_iocpf_timeout, (__ioc), BFA_IOC_TOV_RECOVER)
+ bfa_iocpf_poll_timeout, (__ioc), BFA_IOC_POLL_TOV)
#define bfa_sem_timer_start(__ioc) \
bfa_timer_begin((__ioc)->timer_mod, &(__ioc)->sem_timer, \
@@ -157,23 +177,7 @@ static struct bfa_sm_table_s ioc_sm_table[] = {
*/
static void bfa_iocpf_timeout(void *ioc_arg);
static void bfa_iocpf_sem_timeout(void *ioc_arg);
-
-/*
- * IOCPF state machine events
- */
-enum iocpf_event {
- IOCPF_E_ENABLE = 1, /* IOCPF enable request */
- IOCPF_E_DISABLE = 2, /* IOCPF disable request */
- IOCPF_E_STOP = 3, /* stop on driver detach */
- IOCPF_E_FWREADY = 4, /* f/w initialization done */
- IOCPF_E_FWRSP_ENABLE = 5, /* enable f/w response */
- IOCPF_E_FWRSP_DISABLE = 6, /* disable f/w response */
- IOCPF_E_FAIL = 7, /* failure notice by ioc sm */
- IOCPF_E_INITFAIL = 8, /* init fail notice by ioc sm */
- IOCPF_E_GETATTRFAIL = 9, /* init fail notice by ioc sm */
- IOCPF_E_SEMLOCKED = 10, /* h/w semaphore is locked */
- IOCPF_E_TIMEOUT = 11, /* f/w response timeout */
-};
+static void bfa_iocpf_poll_timeout(void *ioc_arg);
/*
* IOCPF states
@@ -207,7 +211,23 @@ bfa_fsm_state_decl(bfa_iocpf, disabling_sync, struct bfa_iocpf_s,
enum iocpf_event);
bfa_fsm_state_decl(bfa_iocpf, disabled, struct bfa_iocpf_s, enum iocpf_event);
-static struct bfa_sm_table_s iocpf_sm_table[] = {
+struct bfa_iocpf_sm_table {
+ bfa_iocpf_sm_t sm; /* state machine function */
+ enum bfa_iocpf_state state; /* state machine encoding */
+ char *name; /* state name for display */
+};
+
+static inline enum bfa_iocpf_state
+bfa_iocpf_sm_to_state(struct bfa_iocpf_sm_table *smt, bfa_iocpf_sm_t sm)
+{
+ int i = 0;
+
+ while (smt[i].sm && smt[i].sm != sm)
+ i++;
+ return smt[i].state;
+}
+
+static struct bfa_iocpf_sm_table iocpf_sm_table[] = {
{BFA_SM(bfa_iocpf_sm_reset), BFA_IOCPF_RESET},
{BFA_SM(bfa_iocpf_sm_fwcheck), BFA_IOCPF_FWMISMATCH},
{BFA_SM(bfa_iocpf_sm_mismatch), BFA_IOCPF_FWMISMATCH},
@@ -314,11 +334,16 @@ bfa_ioc_sm_enabling(struct bfa_ioc_s *ioc, enum ioc_event event)
/* !!! fall through !!! */
case IOC_E_HWERROR:
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_fail_retry);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
if (event != IOC_E_PFFAILED)
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_INITFAIL);
break;
+ case IOC_E_HWFAILED:
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwfail);
+ break;
+
case IOC_E_DISABLE:
bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling);
break;
@@ -355,18 +380,16 @@ bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event)
switch (event) {
case IOC_E_FWRSP_GETATTR:
bfa_ioc_timer_stop(ioc);
- bfa_ioc_check_attr_wwns(ioc);
bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
break;
- break;
case IOC_E_PFFAILED:
case IOC_E_HWERROR:
bfa_ioc_timer_stop(ioc);
- /* !!! fall through !!! */
+ fallthrough;
case IOC_E_TIMEOUT:
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_fail_retry);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
if (event != IOC_E_PFFAILED)
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_GETATTRFAIL);
break;
@@ -384,15 +407,16 @@ bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event)
}
}
-
static void
bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc)
{
struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK);
+ bfa_ioc_event_notify(ioc, BFA_IOC_E_ENABLED);
bfa_ioc_hb_monitor(ioc);
BFA_LOG(KERN_INFO, bfad, bfa_log_level, "IOC enabled\n");
+ bfa_ioc_aen_post(ioc, BFA_IOC_AEN_ENABLE);
}
static void
@@ -412,15 +436,15 @@ bfa_ioc_sm_op(struct bfa_ioc_s *ioc, enum ioc_event event)
case IOC_E_PFFAILED:
case IOC_E_HWERROR:
bfa_hb_timer_stop(ioc);
- /* !!! fall through !!! */
+ fallthrough;
case IOC_E_HBFAIL:
- bfa_ioc_fail_notify(ioc);
-
if (ioc->iocpf.auto_recover)
bfa_fsm_set_state(ioc, bfa_ioc_sm_fail_retry);
else
bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
+ bfa_ioc_fail_notify(ioc);
+
if (event != IOC_E_PFFAILED)
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_FAIL);
break;
@@ -437,6 +461,7 @@ bfa_ioc_sm_disabling_entry(struct bfa_ioc_s *ioc)
struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_DISABLE);
BFA_LOG(KERN_INFO, bfad, bfa_log_level, "IOC disabled\n");
+ bfa_ioc_aen_post(ioc, BFA_IOC_AEN_DISABLE);
}
/*
@@ -461,6 +486,11 @@ bfa_ioc_sm_disabling(struct bfa_ioc_s *ioc, enum ioc_event event)
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_FAIL);
break;
+ case IOC_E_HWFAILED:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwfail);
+ bfa_ioc_disable_comp(ioc);
+ break;
+
default:
bfa_sm_fault(ioc, event);
}
@@ -525,12 +555,14 @@ bfa_ioc_sm_fail_retry(struct bfa_ioc_s *ioc, enum ioc_event event)
* Initialization retry failed.
*/
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
if (event != IOC_E_PFFAILED)
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_INITFAIL);
break;
- case IOC_E_INITFAILED:
- bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
+ case IOC_E_HWFAILED:
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwfail);
break;
case IOC_E_ENABLE:
@@ -581,8 +613,9 @@ bfa_ioc_sm_fail(struct bfa_ioc_s *ioc, enum ioc_event event)
break;
case IOC_E_HWERROR:
+ case IOC_E_HWFAILED:
/*
- * HB failure notification, ignore.
+ * HB failure / HW error notification, ignore.
*/
break;
default:
@@ -590,6 +623,39 @@ bfa_ioc_sm_fail(struct bfa_ioc_s *ioc, enum ioc_event event)
}
}
+static void
+bfa_ioc_sm_hwfail_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_trc(ioc, 0);
+}
+
+static void
+bfa_ioc_sm_hwfail(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+ bfa_trc(ioc, event);
+
+ switch (event) {
+ case IOC_E_ENABLE:
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ break;
+
+ case IOC_E_DISABLE:
+ ioc->cbfn->disable_cbfn(ioc->bfa);
+ break;
+
+ case IOC_E_DETACH:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_uninit);
+ break;
+
+ case IOC_E_HWERROR:
+ /* Ignore - already in hwfail state */
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
/*
* IOCPF State Machine
*/
@@ -600,7 +666,7 @@ bfa_ioc_sm_fail(struct bfa_ioc_s *ioc, enum ioc_event event)
static void
bfa_iocpf_sm_reset_entry(struct bfa_iocpf_s *iocpf)
{
- iocpf->retry_count = 0;
+ iocpf->fw_mismatch_notified = BFA_FALSE;
iocpf->auto_recover = bfa_auto_recover;
}
@@ -633,6 +699,60 @@ bfa_iocpf_sm_reset(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_fwcheck_entry(struct bfa_iocpf_s *iocpf)
{
+ struct bfi_ioc_image_hdr_s fwhdr;
+ u32 r32, fwstate, pgnum, loff = 0;
+ int i;
+
+ /*
+ * Spin on init semaphore to serialize.
+ */
+ r32 = readl(iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+ while (r32 & 0x1) {
+ udelay(20);
+ r32 = readl(iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+ }
+
+ /* h/w sem init */
+ fwstate = bfa_ioc_get_cur_ioc_fwstate(iocpf->ioc);
+ if (fwstate == BFI_IOC_UNINIT) {
+ writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+ goto sem_get;
+ }
+
+ bfa_ioc_fwver_get(iocpf->ioc, &fwhdr);
+
+ if (swab32(fwhdr.exec) == BFI_FWBOOT_TYPE_NORMAL) {
+ writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+ goto sem_get;
+ }
+
+ /*
+ * Clear fwver hdr
+ */
+ pgnum = PSS_SMEM_PGNUM(iocpf->ioc->ioc_regs.smem_pg0, loff);
+ writel(pgnum, iocpf->ioc->ioc_regs.host_page_num_fn);
+
+ for (i = 0; i < sizeof(struct bfi_ioc_image_hdr_s) / sizeof(u32); i++) {
+ bfa_mem_write(iocpf->ioc->ioc_regs.smem_page_start, loff, 0);
+ loff += sizeof(u32);
+ }
+
+ bfa_trc(iocpf->ioc, fwstate);
+ bfa_trc(iocpf->ioc, swab32(fwhdr.exec));
+ bfa_ioc_set_cur_ioc_fwstate(iocpf->ioc, BFI_IOC_UNINIT);
+ bfa_ioc_set_alt_ioc_fwstate(iocpf->ioc, BFI_IOC_UNINIT);
+
+ /*
+ * Unlock the hw semaphore. Should be here only once per boot.
+ */
+ bfa_ioc_ownership_reset(iocpf->ioc);
+
+ /*
+ * unlock init semaphore.
+ */
+ writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+
+sem_get:
bfa_ioc_hw_sem_get(iocpf->ioc);
}
@@ -650,7 +770,6 @@ bfa_iocpf_sm_fwcheck(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
case IOCPF_E_SEMLOCKED:
if (bfa_ioc_firmware_lock(ioc)) {
if (bfa_ioc_sync_start(ioc)) {
- iocpf->retry_count = 0;
bfa_ioc_sync_join(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_hwinit);
} else {
@@ -664,6 +783,11 @@ bfa_iocpf_sm_fwcheck(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
}
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_fsm_send_event(ioc, IOC_E_HWFAILED);
+ break;
+
case IOCPF_E_DISABLE:
bfa_sem_timer_stop(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_reset);
@@ -689,10 +813,10 @@ bfa_iocpf_sm_mismatch_entry(struct bfa_iocpf_s *iocpf)
/*
* Call only the first time sm enters fwmismatch state.
*/
- if (iocpf->retry_count == 0)
+ if (iocpf->fw_mismatch_notified == BFA_FALSE)
bfa_ioc_pf_fwmismatch(iocpf->ioc);
- iocpf->retry_count++;
+ iocpf->fw_mismatch_notified = BFA_TRUE;
bfa_iocpf_timer_start(iocpf->ioc);
}
@@ -757,6 +881,11 @@ bfa_iocpf_sm_semwait(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
}
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_fsm_send_event(ioc, IOC_E_HWFAILED);
+ break;
+
case IOCPF_E_DISABLE:
bfa_sem_timer_stop(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
@@ -770,7 +899,7 @@ bfa_iocpf_sm_semwait(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_hwinit_entry(struct bfa_iocpf_s *iocpf)
{
- bfa_iocpf_timer_start(iocpf->ioc);
+ iocpf->poll_time = 0;
bfa_ioc_hwinit(iocpf->ioc, BFA_FALSE);
}
@@ -787,20 +916,12 @@ bfa_iocpf_sm_hwinit(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_FWREADY:
- bfa_iocpf_timer_stop(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_enabling);
break;
- case IOCPF_E_INITFAIL:
- bfa_iocpf_timer_stop(ioc);
- /*
- * !!! fall through !!!
- */
-
case IOCPF_E_TIMEOUT:
writel(1, ioc->ioc_regs.ioc_sem_reg);
- if (event == IOCPF_E_TIMEOUT)
- bfa_fsm_send_event(ioc, IOC_E_PFFAILED);
+ bfa_fsm_send_event(ioc, IOC_E_PFFAILED);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail_sync);
break;
@@ -820,6 +941,10 @@ static void
bfa_iocpf_sm_enabling_entry(struct bfa_iocpf_s *iocpf)
{
bfa_iocpf_timer_start(iocpf->ioc);
+ /*
+ * Enable Interrupts before sending fw IOC ENABLE cmd.
+ */
+ iocpf->ioc->cbfn->reset_cbfn(iocpf->ioc->bfa);
bfa_ioc_send_enable(iocpf->ioc);
}
@@ -843,9 +968,7 @@ bfa_iocpf_sm_enabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
case IOCPF_E_INITFAIL:
bfa_iocpf_timer_stop(ioc);
- /*
- * !!! fall through !!!
- */
+ fallthrough;
case IOCPF_E_TIMEOUT:
writel(1, ioc->ioc_regs.ioc_sem_reg);
@@ -860,10 +983,6 @@ bfa_iocpf_sm_enabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling);
break;
- case IOCPF_E_FWREADY:
- bfa_ioc_send_enable(ioc);
- break;
-
default:
bfa_sm_fault(ioc, event);
}
@@ -895,16 +1014,6 @@ bfa_iocpf_sm_ready(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail_sync);
break;
- case IOCPF_E_FWREADY:
- if (bfa_ioc_is_operational(ioc)) {
- bfa_fsm_send_event(ioc, IOC_E_PFFAILED);
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail_sync);
- } else {
- bfa_fsm_send_event(ioc, IOC_E_PFFAILED);
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail_sync);
- }
- break;
-
default:
bfa_sm_fault(ioc, event);
}
@@ -929,19 +1038,16 @@ bfa_iocpf_sm_disabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_FWRSP_DISABLE:
- case IOCPF_E_FWREADY:
bfa_iocpf_timer_stop(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
break;
case IOCPF_E_FAIL:
bfa_iocpf_timer_stop(ioc);
- /*
- * !!! fall through !!!
- */
+ fallthrough;
case IOCPF_E_TIMEOUT:
- writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
+ bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
break;
@@ -976,6 +1082,11 @@ bfa_iocpf_sm_disabling_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabled);
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_fsm_send_event(ioc, IOC_E_HWFAILED);
+ break;
+
case IOCPF_E_FAIL:
break;
@@ -990,6 +1101,7 @@ bfa_iocpf_sm_disabling_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_disabled_entry(struct bfa_iocpf_s *iocpf)
{
+ bfa_ioc_mbox_flush(iocpf->ioc);
bfa_fsm_send_event(iocpf->ioc, IOC_E_DISABLED);
}
@@ -1002,7 +1114,6 @@ bfa_iocpf_sm_disabled(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_ENABLE:
- iocpf->retry_count = 0;
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_semwait);
break;
@@ -1019,6 +1130,7 @@ bfa_iocpf_sm_disabled(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_initfail_sync_entry(struct bfa_iocpf_s *iocpf)
{
+ bfa_ioc_debug_save_ftrc(iocpf->ioc);
bfa_ioc_hw_sem_get(iocpf->ioc);
}
@@ -1035,20 +1147,15 @@ bfa_iocpf_sm_initfail_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_SEMLOCKED:
bfa_ioc_notify_fail(ioc);
- bfa_ioc_sync_ack(ioc);
- iocpf->retry_count++;
- if (iocpf->retry_count >= BFA_IOC_HWINIT_MAX) {
- bfa_ioc_sync_leave(ioc);
- writel(1, ioc->ioc_regs.ioc_sem_reg);
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail);
- } else {
- if (bfa_ioc_sync_complete(ioc))
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_hwinit);
- else {
- writel(1, ioc->ioc_regs.ioc_sem_reg);
- bfa_fsm_set_state(iocpf, bfa_iocpf_sm_semwait);
- }
- }
+ bfa_ioc_sync_leave(ioc);
+ bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
+ writel(1, ioc->ioc_regs.ioc_sem_reg);
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail);
+ break;
+
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_fsm_send_event(ioc, IOC_E_HWFAILED);
break;
case IOCPF_E_DISABLE:
@@ -1073,7 +1180,7 @@ bfa_iocpf_sm_initfail_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_initfail_entry(struct bfa_iocpf_s *iocpf)
{
- bfa_fsm_send_event(iocpf->ioc, IOC_E_INITFAILED);
+ bfa_trc(iocpf->ioc, 0);
}
/*
@@ -1112,7 +1219,7 @@ bfa_iocpf_sm_fail_sync_entry(struct bfa_iocpf_s *iocpf)
/*
* Flush any queued up mailbox requests.
*/
- bfa_ioc_mbox_hbfail(iocpf->ioc);
+ bfa_ioc_mbox_flush(iocpf->ioc);
bfa_ioc_hw_sem_get(iocpf->ioc);
}
@@ -1126,11 +1233,11 @@ bfa_iocpf_sm_fail_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
switch (event) {
case IOCPF_E_SEMLOCKED:
- iocpf->retry_count = 0;
bfa_ioc_sync_ack(ioc);
bfa_ioc_notify_fail(ioc);
if (!iocpf->auto_recover) {
bfa_ioc_sync_leave(ioc);
+ bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
writel(1, ioc->ioc_regs.ioc_sem_reg);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
} else {
@@ -1143,6 +1250,11 @@ bfa_iocpf_sm_fail_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
}
break;
+ case IOCPF_E_SEM_ERROR:
+ bfa_fsm_set_state(iocpf, bfa_iocpf_sm_fail);
+ bfa_fsm_send_event(ioc, IOC_E_HWFAILED);
+ break;
+
case IOCPF_E_DISABLE:
bfa_sem_timer_stop(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_disabling_sync);
@@ -1159,6 +1271,7 @@ bfa_iocpf_sm_fail_sync(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
static void
bfa_iocpf_sm_fail_entry(struct bfa_iocpf_s *iocpf)
{
+ bfa_trc(iocpf->ioc, 0);
}
/*
@@ -1185,23 +1298,28 @@ bfa_iocpf_sm_fail(struct bfa_iocpf_s *iocpf, enum iocpf_event event)
* BFA IOC private functions
*/
+/*
+ * Notify common modules registered for notification.
+ */
static void
-bfa_ioc_disable_comp(struct bfa_ioc_s *ioc)
+bfa_ioc_event_notify(struct bfa_ioc_s *ioc, enum bfa_ioc_event_e event)
{
- struct list_head *qe;
- struct bfa_ioc_hbfail_notify_s *notify;
-
- ioc->cbfn->disable_cbfn(ioc->bfa);
+ struct bfa_ioc_notify_s *notify;
+ struct list_head *qe;
- /*
- * Notify common modules registered for notification.
- */
- list_for_each(qe, &ioc->hb_notify_q) {
- notify = (struct bfa_ioc_hbfail_notify_s *) qe;
- notify->cbfn(notify->cbarg);
+ list_for_each(qe, &ioc->notify_q) {
+ notify = (struct bfa_ioc_notify_s *)qe;
+ notify->cbfn(notify->cbarg, event);
}
}
+static void
+bfa_ioc_disable_comp(struct bfa_ioc_s *ioc)
+{
+ ioc->cbfn->disable_cbfn(ioc->bfa);
+ bfa_ioc_event_notify(ioc, BFA_IOC_E_DISABLED);
+}
+
bfa_boolean_t
bfa_ioc_sem_get(void __iomem *sem_reg)
{
@@ -1211,16 +1329,15 @@ bfa_ioc_sem_get(void __iomem *sem_reg)
r32 = readl(sem_reg);
- while (r32 && (cnt < BFA_SEM_SPINCNT)) {
+ while ((r32 & 1) && (cnt < BFA_SEM_SPINCNT)) {
cnt++;
udelay(2);
r32 = readl(sem_reg);
}
- if (r32 == 0)
+ if (!(r32 & 1))
return BFA_TRUE;
- WARN_ON(cnt >= BFA_SEM_SPINCNT);
return BFA_FALSE;
}
@@ -1234,7 +1351,12 @@ bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc)
* will return 1. Semaphore is released by writing 1 to the register
*/
r32 = readl(ioc->ioc_regs.ioc_sem_reg);
- if (r32 == 0) {
+ if (r32 == ~0) {
+ WARN_ON(r32 == ~0);
+ bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_SEM_ERROR);
+ return;
+ }
+ if (!(r32 & 1)) {
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_SEMLOCKED);
return;
}
@@ -1316,13 +1438,12 @@ bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc)
void
bfa_ioc_fwver_get(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
{
- u32 pgnum, pgoff;
+ u32 pgnum;
u32 loff = 0;
int i;
u32 *fwsig = (u32 *) fwhdr;
pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, loff);
- pgoff = PSS_SMEM_PGOFF(loff);
writel(pgnum, ioc->ioc_regs.host_page_num_fn);
for (i = 0; i < (sizeof(struct bfi_ioc_image_hdr_s) / sizeof(u32));
@@ -1334,28 +1455,42 @@ bfa_ioc_fwver_get(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
}
/*
- * Returns TRUE if same.
+ * Returns TRUE if driver is willing to work with current smem f/w version.
*/
bfa_boolean_t
-bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
+bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc,
+ struct bfi_ioc_image_hdr_s *smem_fwhdr)
{
struct bfi_ioc_image_hdr_s *drv_fwhdr;
- int i;
+ enum bfi_ioc_img_ver_cmp_e smem_flash_cmp, drv_smem_cmp;
drv_fwhdr = (struct bfi_ioc_image_hdr_s *)
- bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc), 0);
+ bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), 0);
- for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++) {
- if (fwhdr->md5sum[i] != drv_fwhdr->md5sum[i]) {
- bfa_trc(ioc, i);
- bfa_trc(ioc, fwhdr->md5sum[i]);
- bfa_trc(ioc, drv_fwhdr->md5sum[i]);
- return BFA_FALSE;
- }
+ /*
+ * If smem is incompatible or old, driver should not work with it.
+ */
+ drv_smem_cmp = bfa_ioc_fw_ver_patch_cmp(drv_fwhdr, smem_fwhdr);
+ if (drv_smem_cmp == BFI_IOC_IMG_VER_INCOMP ||
+ drv_smem_cmp == BFI_IOC_IMG_VER_OLD) {
+ return BFA_FALSE;
}
- bfa_trc(ioc, fwhdr->md5sum[0]);
- return BFA_TRUE;
+ /*
+ * IF Flash has a better F/W than smem do not work with smem.
+ * If smem f/w == flash f/w, as smem f/w not old | incmp, work with it.
+ * If Flash is old or incomp work with smem iff smem f/w == drv f/w.
+ */
+ smem_flash_cmp = bfa_ioc_flash_fwver_cmp(ioc, smem_fwhdr);
+
+ if (smem_flash_cmp == BFI_IOC_IMG_VER_BETTER) {
+ return BFA_FALSE;
+ } else if (smem_flash_cmp == BFI_IOC_IMG_VER_SAME) {
+ return BFA_TRUE;
+ } else {
+ return (drv_smem_cmp == BFI_IOC_IMG_VER_SAME) ?
+ BFA_TRUE : BFA_FALSE;
+ }
}
/*
@@ -1365,25 +1500,178 @@ bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
static bfa_boolean_t
bfa_ioc_fwver_valid(struct bfa_ioc_s *ioc, u32 boot_env)
{
- struct bfi_ioc_image_hdr_s fwhdr, *drv_fwhdr;
+ struct bfi_ioc_image_hdr_s fwhdr;
bfa_ioc_fwver_get(ioc, &fwhdr);
- drv_fwhdr = (struct bfi_ioc_image_hdr_s *)
- bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc), 0);
- if (fwhdr.signature != drv_fwhdr->signature) {
- bfa_trc(ioc, fwhdr.signature);
- bfa_trc(ioc, drv_fwhdr->signature);
+ if (swab32(fwhdr.bootenv) != boot_env) {
+ bfa_trc(ioc, fwhdr.bootenv);
+ bfa_trc(ioc, boot_env);
return BFA_FALSE;
}
- if (swab32(fwhdr.param) != boot_env) {
- bfa_trc(ioc, fwhdr.param);
- bfa_trc(ioc, boot_env);
+ return bfa_ioc_fwver_cmp(ioc, &fwhdr);
+}
+
+static bfa_boolean_t
+bfa_ioc_fwver_md5_check(struct bfi_ioc_image_hdr_s *fwhdr_1,
+ struct bfi_ioc_image_hdr_s *fwhdr_2)
+{
+ int i;
+
+ for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++)
+ if (fwhdr_1->md5sum[i] != fwhdr_2->md5sum[i])
+ return BFA_FALSE;
+
+ return BFA_TRUE;
+}
+
+/*
+ * Returns TRUE if major minor and maintainence are same.
+ * If patch versions are same, check for MD5 Checksum to be same.
+ */
+static bfa_boolean_t
+bfa_ioc_fw_ver_compatible(struct bfi_ioc_image_hdr_s *drv_fwhdr,
+ struct bfi_ioc_image_hdr_s *fwhdr_to_cmp)
+{
+ if (drv_fwhdr->signature != fwhdr_to_cmp->signature)
+ return BFA_FALSE;
+
+ if (drv_fwhdr->fwver.major != fwhdr_to_cmp->fwver.major)
+ return BFA_FALSE;
+
+ if (drv_fwhdr->fwver.minor != fwhdr_to_cmp->fwver.minor)
return BFA_FALSE;
+
+ if (drv_fwhdr->fwver.maint != fwhdr_to_cmp->fwver.maint)
+ return BFA_FALSE;
+
+ if (drv_fwhdr->fwver.patch == fwhdr_to_cmp->fwver.patch &&
+ drv_fwhdr->fwver.phase == fwhdr_to_cmp->fwver.phase &&
+ drv_fwhdr->fwver.build == fwhdr_to_cmp->fwver.build) {
+ return bfa_ioc_fwver_md5_check(drv_fwhdr, fwhdr_to_cmp);
}
- return bfa_ioc_fwver_cmp(ioc, &fwhdr);
+ return BFA_TRUE;
+}
+
+static bfa_boolean_t
+bfa_ioc_flash_fwver_valid(struct bfi_ioc_image_hdr_s *flash_fwhdr)
+{
+ if (flash_fwhdr->fwver.major == 0 || flash_fwhdr->fwver.major == 0xFF)
+ return BFA_FALSE;
+
+ return BFA_TRUE;
+}
+
+static bfa_boolean_t fwhdr_is_ga(struct bfi_ioc_image_hdr_s *fwhdr)
+{
+ if (fwhdr->fwver.phase == 0 &&
+ fwhdr->fwver.build == 0)
+ return BFA_TRUE;
+
+ return BFA_FALSE;
+}
+
+/*
+ * Returns TRUE if both are compatible and patch of fwhdr_to_cmp is better.
+ */
+static enum bfi_ioc_img_ver_cmp_e
+bfa_ioc_fw_ver_patch_cmp(struct bfi_ioc_image_hdr_s *base_fwhdr,
+ struct bfi_ioc_image_hdr_s *fwhdr_to_cmp)
+{
+ if (bfa_ioc_fw_ver_compatible(base_fwhdr, fwhdr_to_cmp) == BFA_FALSE)
+ return BFI_IOC_IMG_VER_INCOMP;
+
+ if (fwhdr_to_cmp->fwver.patch > base_fwhdr->fwver.patch)
+ return BFI_IOC_IMG_VER_BETTER;
+
+ else if (fwhdr_to_cmp->fwver.patch < base_fwhdr->fwver.patch)
+ return BFI_IOC_IMG_VER_OLD;
+
+ /*
+ * GA takes priority over internal builds of the same patch stream.
+ * At this point major minor maint and patch numbers are same.
+ */
+
+ if (fwhdr_is_ga(base_fwhdr) == BFA_TRUE) {
+ if (fwhdr_is_ga(fwhdr_to_cmp))
+ return BFI_IOC_IMG_VER_SAME;
+ else
+ return BFI_IOC_IMG_VER_OLD;
+ } else {
+ if (fwhdr_is_ga(fwhdr_to_cmp))
+ return BFI_IOC_IMG_VER_BETTER;
+ }
+
+ if (fwhdr_to_cmp->fwver.phase > base_fwhdr->fwver.phase)
+ return BFI_IOC_IMG_VER_BETTER;
+ else if (fwhdr_to_cmp->fwver.phase < base_fwhdr->fwver.phase)
+ return BFI_IOC_IMG_VER_OLD;
+
+ if (fwhdr_to_cmp->fwver.build > base_fwhdr->fwver.build)
+ return BFI_IOC_IMG_VER_BETTER;
+ else if (fwhdr_to_cmp->fwver.build < base_fwhdr->fwver.build)
+ return BFI_IOC_IMG_VER_OLD;
+
+ /*
+ * All Version Numbers are equal.
+ * Md5 check to be done as a part of compatibility check.
+ */
+ return BFI_IOC_IMG_VER_SAME;
+}
+
+#define BFA_FLASH_PART_FWIMG_ADDR 0x100000 /* fw image address */
+
+bfa_status_t
+bfa_ioc_flash_img_get_chnk(struct bfa_ioc_s *ioc, u32 off,
+ u32 *fwimg)
+{
+ return bfa_flash_raw_read(ioc->pcidev.pci_bar_kva,
+ BFA_FLASH_PART_FWIMG_ADDR + (off * sizeof(u32)),
+ (char *)fwimg, BFI_FLASH_CHUNK_SZ);
+}
+
+static enum bfi_ioc_img_ver_cmp_e
+bfa_ioc_flash_fwver_cmp(struct bfa_ioc_s *ioc,
+ struct bfi_ioc_image_hdr_s *base_fwhdr)
+{
+ struct bfi_ioc_image_hdr_s *flash_fwhdr;
+ bfa_status_t status;
+ u32 fwimg[BFI_FLASH_CHUNK_SZ_WORDS];
+
+ status = bfa_ioc_flash_img_get_chnk(ioc, 0, fwimg);
+ if (status != BFA_STATUS_OK)
+ return BFI_IOC_IMG_VER_INCOMP;
+
+ flash_fwhdr = (struct bfi_ioc_image_hdr_s *) fwimg;
+ if (bfa_ioc_flash_fwver_valid(flash_fwhdr) == BFA_TRUE)
+ return bfa_ioc_fw_ver_patch_cmp(base_fwhdr, flash_fwhdr);
+ else
+ return BFI_IOC_IMG_VER_INCOMP;
+}
+
+
+/*
+ * Invalidate fwver signature
+ */
+bfa_status_t
+bfa_ioc_fwsig_invalidate(struct bfa_ioc_s *ioc)
+{
+
+ u32 pgnum;
+ u32 loff = 0;
+ enum bfi_ioc_state ioc_fwstate;
+
+ ioc_fwstate = bfa_ioc_get_cur_ioc_fwstate(ioc);
+ if (!bfa_ioc_state_disabled(ioc_fwstate))
+ return BFA_STATUS_ADAPTER_ENABLED;
+
+ pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, loff);
+ writel(pgnum, ioc->ioc_regs.host_page_num_fn);
+ bfa_mem_write(ioc->ioc_regs.smem_page_start, loff, BFA_IOC_FW_INV_SIGN);
+
+ return BFA_STATUS_OK;
}
/*
@@ -1407,15 +1695,15 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
u32 boot_type;
u32 boot_env;
- ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);
+ ioc_fwstate = bfa_ioc_get_cur_ioc_fwstate(ioc);
if (force)
ioc_fwstate = BFI_IOC_UNINIT;
bfa_trc(ioc, ioc_fwstate);
- boot_type = BFI_BOOT_TYPE_NORMAL;
- boot_env = BFI_BOOT_LOADER_OS;
+ boot_type = BFI_FWBOOT_TYPE_NORMAL;
+ boot_env = BFI_FWBOOT_ENV_OS;
/*
* check if firmware is valid
@@ -1424,7 +1712,8 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
BFA_FALSE : bfa_ioc_fwver_valid(ioc, boot_env);
if (!fwvalid) {
- bfa_ioc_boot(ioc, boot_type, boot_env);
+ if (bfa_ioc_boot(ioc, boot_type, boot_env) == BFA_STATUS_OK)
+ bfa_ioc_poll_fwinit(ioc);
return;
}
@@ -1433,7 +1722,7 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
* just wait for an initialization completion interrupt.
*/
if (ioc_fwstate == BFI_IOC_INITING) {
- ioc->cbfn->reset_cbfn(ioc->bfa);
+ bfa_ioc_poll_fwinit(ioc);
return;
}
@@ -1452,7 +1741,6 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
* be flushed. Otherwise MSI-X interrupts are not delivered.
*/
bfa_ioc_msgflush(ioc);
- ioc->cbfn->reset_cbfn(ioc->bfa);
bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_FWREADY);
return;
}
@@ -1460,7 +1748,8 @@ bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
/*
* Initialize the h/w for any other states.
*/
- bfa_ioc_boot(ioc, boot_type, boot_env);
+ if (bfa_ioc_boot(ioc, boot_type, boot_env) == BFA_STATUS_OK)
+ bfa_ioc_poll_fwinit(ioc);
}
static void
@@ -1504,13 +1793,12 @@ static void
bfa_ioc_send_enable(struct bfa_ioc_s *ioc)
{
struct bfi_ioc_ctrl_req_s enable_req;
- struct timeval tv;
bfi_h2i_set(enable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_ENABLE_REQ,
bfa_ioc_portid(ioc));
- enable_req.ioc_class = ioc->ioc_mc;
- do_gettimeofday(&tv);
- enable_req.tv_sec = be32_to_cpu(tv.tv_sec);
+ enable_req.clscode = cpu_to_be16(ioc->clscode);
+ /* unsigned 32-bit time_t overflow in y2106 */
+ enable_req.tv_sec = be32_to_cpu(ktime_get_real_seconds());
bfa_ioc_mbox_send(ioc, &enable_req, sizeof(struct bfi_ioc_ctrl_req_s));
}
@@ -1521,6 +1809,9 @@ bfa_ioc_send_disable(struct bfa_ioc_s *ioc)
bfi_h2i_set(disable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_DISABLE_REQ,
bfa_ioc_portid(ioc));
+ disable_req.clscode = cpu_to_be16(ioc->clscode);
+ /* unsigned 32-bit time_t overflow in y2106 */
+ disable_req.tv_sec = be32_to_cpu(ktime_get_real_seconds());
bfa_ioc_mbox_send(ioc, &disable_req, sizeof(struct bfi_ioc_ctrl_req_s));
}
@@ -1563,35 +1854,61 @@ bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc)
/*
* Initiate a full firmware download.
*/
-static void
+static bfa_status_t
bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
u32 boot_env)
{
u32 *fwimg;
- u32 pgnum, pgoff;
+ u32 pgnum;
u32 loff = 0;
u32 chunkno = 0;
u32 i;
+ u32 asicmode;
+ u32 fwimg_size;
+ u32 fwimg_buf[BFI_FLASH_CHUNK_SZ_WORDS];
+ bfa_status_t status;
- /*
- * Initialize LMEM first before code download
- */
- bfa_ioc_lmem_init(ioc);
+ if (boot_env == BFI_FWBOOT_ENV_OS &&
+ boot_type == BFI_FWBOOT_TYPE_FLASH) {
+ fwimg_size = BFI_FLASH_IMAGE_SZ/sizeof(u32);
- bfa_trc(ioc, bfa_cb_image_get_size(BFA_IOC_FWIMG_TYPE(ioc)));
- fwimg = bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc), chunkno);
+ status = bfa_ioc_flash_img_get_chnk(ioc,
+ BFA_IOC_FLASH_CHUNK_ADDR(chunkno), fwimg_buf);
+ if (status != BFA_STATUS_OK)
+ return status;
- pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, loff);
- pgoff = PSS_SMEM_PGOFF(loff);
+ fwimg = fwimg_buf;
+ } else {
+ fwimg_size = bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc));
+ fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc),
+ BFA_IOC_FLASH_CHUNK_ADDR(chunkno));
+ }
+
+ bfa_trc(ioc, fwimg_size);
+
+ pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, loff);
writel(pgnum, ioc->ioc_regs.host_page_num_fn);
- for (i = 0; i < bfa_cb_image_get_size(BFA_IOC_FWIMG_TYPE(ioc)); i++) {
+ for (i = 0; i < fwimg_size; i++) {
if (BFA_IOC_FLASH_CHUNK_NO(i) != chunkno) {
chunkno = BFA_IOC_FLASH_CHUNK_NO(i);
- fwimg = bfa_cb_image_get_chunk(BFA_IOC_FWIMG_TYPE(ioc),
+
+ if (boot_env == BFI_FWBOOT_ENV_OS &&
+ boot_type == BFI_FWBOOT_TYPE_FLASH) {
+ status = bfa_ioc_flash_img_get_chnk(ioc,
+ BFA_IOC_FLASH_CHUNK_ADDR(chunkno),
+ fwimg_buf);
+ if (status != BFA_STATUS_OK)
+ return status;
+
+ fwimg = fwimg_buf;
+ } else {
+ fwimg = bfa_cb_image_get_chunk(
+ bfa_ioc_asic_gen(ioc),
BFA_IOC_FLASH_CHUNK_ADDR(chunkno));
+ }
}
/*
@@ -1616,12 +1933,21 @@ bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
ioc->ioc_regs.host_page_num_fn);
/*
- * Set boot type and boot param at the end.
- */
- bfa_mem_write(ioc->ioc_regs.smem_page_start, BFI_BOOT_TYPE_OFF,
+ * Set boot type, env and device mode at the end.
+ */
+ if (boot_env == BFI_FWBOOT_ENV_OS &&
+ boot_type == BFI_FWBOOT_TYPE_FLASH) {
+ boot_type = BFI_FWBOOT_TYPE_NORMAL;
+ }
+ asicmode = BFI_FWBOOT_DEVMODE(ioc->asic_gen, ioc->asic_mode,
+ ioc->port0_mode, ioc->port1_mode);
+ bfa_mem_write(ioc->ioc_regs.smem_page_start, BFI_FWBOOT_DEVMODE_OFF,
+ swab32(asicmode));
+ bfa_mem_write(ioc->ioc_regs.smem_page_start, BFI_FWBOOT_TYPE_OFF,
swab32(boot_type));
- bfa_mem_write(ioc->ioc_regs.smem_page_start, BFI_BOOT_LOADER_OFF,
+ bfa_mem_write(ioc->ioc_regs.smem_page_start, BFI_FWBOOT_ENV_OFF,
swab32(boot_env));
+ return BFA_STATUS_OK;
}
@@ -1636,6 +1962,8 @@ bfa_ioc_getattr_reply(struct bfa_ioc_s *ioc)
attr->adapter_prop = be32_to_cpu(attr->adapter_prop);
attr->card_type = be32_to_cpu(attr->card_type);
attr->maxfrsize = be16_to_cpu(attr->maxfrsize);
+ ioc->fcmode = (attr->port_mode == BFI_PORT_MODE_FC);
+ attr->mfg_year = be16_to_cpu(attr->mfg_year);
bfa_fsm_send_event(ioc, IOC_E_FWRSP_GETATTR);
}
@@ -1690,7 +2018,7 @@ bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc)
* Cleanup any pending requests.
*/
static void
-bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc)
+bfa_ioc_mbox_flush(struct bfa_ioc_s *ioc)
{
struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
struct bfa_mbox_cmd_s *cmd;
@@ -1735,7 +2063,7 @@ bfa_ioc_smem_read(struct bfa_ioc_s *ioc, void *tbuf, u32 soff, u32 sz)
bfa_trc(ioc, len);
for (i = 0; i < len; i++) {
r32 = bfa_mem_read(ioc->ioc_regs.smem_page_start, loff);
- buf[i] = be32_to_cpu(r32);
+ buf[i] = swab32(r32);
loff += sizeof(u32);
/*
@@ -1752,6 +2080,7 @@ bfa_ioc_smem_read(struct bfa_ioc_s *ioc, void *tbuf, u32 soff, u32 sz)
/*
* release semaphore.
*/
+ readl(ioc->ioc_regs.ioc_init_sem_reg);
writel(1, ioc->ioc_regs.ioc_init_sem_reg);
bfa_trc(ioc, pgnum);
@@ -1808,6 +2137,7 @@ bfa_ioc_smem_clr(struct bfa_ioc_s *ioc, u32 soff, u32 sz)
/*
* release semaphore.
*/
+ readl(ioc->ioc_regs.ioc_init_sem_reg);
writel(1, ioc->ioc_regs.ioc_init_sem_reg);
bfa_trc(ioc, pgnum);
return BFA_STATUS_OK;
@@ -1816,23 +2146,19 @@ bfa_ioc_smem_clr(struct bfa_ioc_s *ioc, u32 soff, u32 sz)
static void
bfa_ioc_fail_notify(struct bfa_ioc_s *ioc)
{
- struct list_head *qe;
- struct bfa_ioc_hbfail_notify_s *notify;
struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
/*
* Notify driver and common modules registered for notification.
*/
ioc->cbfn->hbfail_cbfn(ioc->bfa);
- list_for_each(qe, &ioc->hb_notify_q) {
- notify = (struct bfa_ioc_hbfail_notify_s *) qe;
- notify->cbfn(notify->cbarg);
- }
+ bfa_ioc_event_notify(ioc, BFA_IOC_E_FAILED);
bfa_ioc_debug_save_ftrc(ioc);
BFA_LOG(KERN_CRIT, bfad, bfa_log_level,
"Heart Beat of IOC has failed\n");
+ bfa_ioc_aen_post(ioc, BFA_IOC_AEN_HBFAIL);
}
@@ -1847,6 +2173,7 @@ bfa_ioc_pf_fwmismatch(struct bfa_ioc_s *ioc)
BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
"Running firmware version is incompatible "
"with the driver version\n");
+ bfa_ioc_aen_post(ioc, BFA_IOC_AEN_FWMISMATCH);
}
bfa_status_t
@@ -1861,9 +2188,16 @@ bfa_ioc_pll_init(struct bfa_ioc_s *ioc)
bfa_ioc_pll_init_asic(ioc);
ioc->pllinit = BFA_TRUE;
+
+ /*
+ * Initialize LMEM
+ */
+ bfa_ioc_lmem_init(ioc);
+
/*
* release semaphore.
*/
+ readl(ioc->ioc_regs.ioc_init_sem_reg);
writel(1, ioc->ioc_regs.ioc_init_sem_reg);
return BFA_STATUS_OK;
@@ -1873,49 +2207,53 @@ bfa_ioc_pll_init(struct bfa_ioc_s *ioc)
* Interface used by diag module to do firmware boot with memory test
* as the entry vector.
*/
-void
+bfa_status_t
bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_env)
{
- void __iomem *rb;
-
+ struct bfi_ioc_image_hdr_s *drv_fwhdr;
+ bfa_status_t status;
bfa_ioc_stats(ioc, ioc_boots);
if (bfa_ioc_pll_init(ioc) != BFA_STATUS_OK)
- return;
+ return BFA_STATUS_FAILED;
+
+ if (boot_env == BFI_FWBOOT_ENV_OS &&
+ boot_type == BFI_FWBOOT_TYPE_NORMAL) {
+
+ drv_fwhdr = (struct bfi_ioc_image_hdr_s *)
+ bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), 0);
+
+ /*
+ * Work with Flash iff flash f/w is better than driver f/w.
+ * Otherwise push drivers firmware.
+ */
+ if (bfa_ioc_flash_fwver_cmp(ioc, drv_fwhdr) ==
+ BFI_IOC_IMG_VER_BETTER)
+ boot_type = BFI_FWBOOT_TYPE_FLASH;
+ }
/*
* Initialize IOC state of all functions on a chip reset.
*/
- rb = ioc->pcidev.pci_bar_kva;
- if (boot_type == BFI_BOOT_TYPE_MEMTEST) {
- writel(BFI_IOC_MEMTEST, (rb + BFA_IOC0_STATE_REG));
- writel(BFI_IOC_MEMTEST, (rb + BFA_IOC1_STATE_REG));
+ if (boot_type == BFI_FWBOOT_TYPE_MEMTEST) {
+ bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_MEMTEST);
+ bfa_ioc_set_alt_ioc_fwstate(ioc, BFI_IOC_MEMTEST);
} else {
- writel(BFI_IOC_INITING, (rb + BFA_IOC0_STATE_REG));
- writel(BFI_IOC_INITING, (rb + BFA_IOC1_STATE_REG));
+ bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_INITING);
+ bfa_ioc_set_alt_ioc_fwstate(ioc, BFI_IOC_INITING);
}
bfa_ioc_msgflush(ioc);
- bfa_ioc_download_fw(ioc, boot_type, boot_env);
-
- /*
- * Enable interrupts just before starting LPU
- */
- ioc->cbfn->reset_cbfn(ioc->bfa);
- bfa_ioc_lpu_start(ioc);
-}
-
-/*
- * Enable/disable IOC failure auto recovery.
- */
-void
-bfa_ioc_auto_recover(bfa_boolean_t auto_recover)
-{
- bfa_auto_recover = auto_recover;
+ status = bfa_ioc_download_fw(ioc, boot_type, boot_env);
+ if (status == BFA_STATUS_OK)
+ bfa_ioc_lpu_start(ioc);
+ else {
+ WARN_ON(boot_type == BFI_FWBOOT_TYPE_MEMTEST);
+ bfa_iocpf_timeout(ioc);
+ }
+ return status;
}
-
-
bfa_boolean_t
bfa_ioc_is_operational(struct bfa_ioc_s *ioc)
{
@@ -1923,22 +2261,16 @@ bfa_ioc_is_operational(struct bfa_ioc_s *ioc)
}
bfa_boolean_t
-bfa_ioc_is_initialized(struct bfa_ioc_s *ioc)
-{
- u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);
-
- return ((r32 != BFI_IOC_UNINIT) &&
- (r32 != BFI_IOC_INITING) &&
- (r32 != BFI_IOC_MEMTEST));
-}
-
-void
bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg)
{
__be32 *msgp = mbmsg;
u32 r32;
int i;
+ r32 = readl(ioc->ioc_regs.lpu_mbox_cmd);
+ if ((r32 & 1) == 0)
+ return BFA_FALSE;
+
/*
* read the MBOX msg
*/
@@ -1954,6 +2286,8 @@ bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg)
*/
writel(1, ioc->ioc_regs.lpu_mbox_cmd);
readl(ioc->ioc_regs.lpu_mbox_cmd);
+
+ return BFA_TRUE;
}
void
@@ -1970,11 +2304,10 @@ bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *m)
case BFI_IOC_I2H_HBEAT:
break;
- case BFI_IOC_I2H_READY_EVENT:
- bfa_fsm_send_event(iocpf, IOCPF_E_FWREADY);
- break;
-
case BFI_IOC_I2H_ENABLE_REPLY:
+ ioc->port_mode = ioc->port_mode_cfg =
+ (enum bfa_mode_s)msg->fw_event.port_mode;
+ ioc->ad_cap_bm = msg->fw_event.cap_bm;
bfa_fsm_send_event(iocpf, IOCPF_E_FWRSP_ENABLE);
break;
@@ -2011,7 +2344,7 @@ bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa, struct bfa_ioc_cbfn_s *cbfn,
ioc->iocpf.ioc = ioc;
bfa_ioc_mbox_attach(ioc);
- INIT_LIST_HEAD(&ioc->hb_notify_q);
+ INIT_LIST_HEAD(&ioc->notify_q);
bfa_fsm_set_state(ioc, bfa_ioc_sm_uninit);
bfa_fsm_send_event(ioc, IOC_E_RESET);
@@ -2024,6 +2357,7 @@ void
bfa_ioc_detach(struct bfa_ioc_s *ioc)
{
bfa_fsm_send_event(ioc, IOC_E_DETACH);
+ INIT_LIST_HEAD(&ioc->notify_q);
}
/*
@@ -2033,20 +2367,81 @@ bfa_ioc_detach(struct bfa_ioc_s *ioc)
*/
void
bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev,
- enum bfi_mclass mc)
+ enum bfi_pcifn_class clscode)
{
- ioc->ioc_mc = mc;
+ ioc->clscode = clscode;
ioc->pcidev = *pcidev;
- ioc->ctdev = bfa_asic_id_ct(ioc->pcidev.device_id);
- ioc->cna = ioc->ctdev && !ioc->fcmode;
+
+ /*
+ * Initialize IOC and device personality
+ */
+ ioc->port0_mode = ioc->port1_mode = BFI_PORT_MODE_FC;
+ ioc->asic_mode = BFI_ASIC_MODE_FC;
+
+ switch (pcidev->device_id) {
+ case BFA_PCI_DEVICE_ID_FC_8G1P:
+ case BFA_PCI_DEVICE_ID_FC_8G2P:
+ ioc->asic_gen = BFI_ASIC_GEN_CB;
+ ioc->fcmode = BFA_TRUE;
+ ioc->port_mode = ioc->port_mode_cfg = BFA_MODE_HBA;
+ ioc->ad_cap_bm = BFA_CM_HBA;
+ break;
+
+ case BFA_PCI_DEVICE_ID_CT:
+ ioc->asic_gen = BFI_ASIC_GEN_CT;
+ ioc->port0_mode = ioc->port1_mode = BFI_PORT_MODE_ETH;
+ ioc->asic_mode = BFI_ASIC_MODE_ETH;
+ ioc->port_mode = ioc->port_mode_cfg = BFA_MODE_CNA;
+ ioc->ad_cap_bm = BFA_CM_CNA;
+ break;
+
+ case BFA_PCI_DEVICE_ID_CT_FC:
+ ioc->asic_gen = BFI_ASIC_GEN_CT;
+ ioc->fcmode = BFA_TRUE;
+ ioc->port_mode = ioc->port_mode_cfg = BFA_MODE_HBA;
+ ioc->ad_cap_bm = BFA_CM_HBA;
+ break;
+
+ case BFA_PCI_DEVICE_ID_CT2:
+ case BFA_PCI_DEVICE_ID_CT2_QUAD:
+ ioc->asic_gen = BFI_ASIC_GEN_CT2;
+ if (clscode == BFI_PCIFN_CLASS_FC &&
+ pcidev->ssid == BFA_PCI_CT2_SSID_FC) {
+ ioc->asic_mode = BFI_ASIC_MODE_FC16;
+ ioc->fcmode = BFA_TRUE;
+ ioc->port_mode = ioc->port_mode_cfg = BFA_MODE_HBA;
+ ioc->ad_cap_bm = BFA_CM_HBA;
+ } else {
+ ioc->port0_mode = ioc->port1_mode = BFI_PORT_MODE_ETH;
+ ioc->asic_mode = BFI_ASIC_MODE_ETH;
+ if (pcidev->ssid == BFA_PCI_CT2_SSID_FCoE) {
+ ioc->port_mode =
+ ioc->port_mode_cfg = BFA_MODE_CNA;
+ ioc->ad_cap_bm = BFA_CM_CNA;
+ } else {
+ ioc->port_mode =
+ ioc->port_mode_cfg = BFA_MODE_NIC;
+ ioc->ad_cap_bm = BFA_CM_NIC;
+ }
+ }
+ break;
+
+ default:
+ WARN_ON(1);
+ }
/*
* Set asic specific interfaces. See bfa_ioc_cb.c and bfa_ioc_ct.c
*/
- if (ioc->ctdev)
- bfa_ioc_set_ct_hwif(ioc);
- else
+ if (ioc->asic_gen == BFI_ASIC_GEN_CB)
bfa_ioc_set_cb_hwif(ioc);
+ else if (ioc->asic_gen == BFI_ASIC_GEN_CT)
+ bfa_ioc_set_ct_hwif(ioc);
+ else {
+ WARN_ON(ioc->asic_gen != BFI_ASIC_GEN_CT2);
+ bfa_ioc_set_ct2_hwif(ioc);
+ bfa_ioc_ct2_poweron(ioc);
+ }
bfa_ioc_map_port(ioc);
bfa_ioc_reg_init(ioc);
@@ -2085,6 +2480,12 @@ bfa_ioc_disable(struct bfa_ioc_s *ioc)
bfa_fsm_send_event(ioc, IOC_E_DISABLE);
}
+void
+bfa_ioc_suspend(struct bfa_ioc_s *ioc)
+{
+ ioc->dbg_fwsave_once = BFA_TRUE;
+ bfa_fsm_send_event(ioc, IOC_E_HWERROR);
+}
/*
* Initialize memory for saving firmware trace. Driver must initialize
@@ -2094,7 +2495,7 @@ void
bfa_ioc_debug_memclaim(struct bfa_ioc_s *ioc, void *dbg_fwsave)
{
ioc->dbg_fwsave = dbg_fwsave;
- ioc->dbg_fwsave_len = (ioc->iocpf.auto_recover) ? BFA_DBG_FWTRC_LEN : 0;
+ ioc->dbg_fwsave_len = BFA_DBG_FWTRC_LEN;
}
/*
@@ -2172,36 +2573,38 @@ bfa_ioc_mbox_isr(struct bfa_ioc_s *ioc)
struct bfi_mbmsg_s m;
int mc;
- bfa_ioc_msgget(ioc, &m);
+ if (bfa_ioc_msgget(ioc, &m)) {
+ /*
+ * Treat IOC message class as special.
+ */
+ mc = m.mh.msg_class;
+ if (mc == BFI_MC_IOC) {
+ bfa_ioc_isr(ioc, &m);
+ return;
+ }
- /*
- * Treat IOC message class as special.
- */
- mc = m.mh.msg_class;
- if (mc == BFI_MC_IOC) {
- bfa_ioc_isr(ioc, &m);
- return;
+ if ((mc >= BFI_MC_MAX) || (mod->mbhdlr[mc].cbfn == NULL))
+ return;
+
+ mod->mbhdlr[mc].cbfn(mod->mbhdlr[mc].cbarg, &m);
}
- if ((mc > BFI_MC_MAX) || (mod->mbhdlr[mc].cbfn == NULL))
- return;
+ bfa_ioc_lpu_read_stat(ioc);
- mod->mbhdlr[mc].cbfn(mod->mbhdlr[mc].cbarg, &m);
+ /*
+ * Try to send pending mailbox commands
+ */
+ bfa_ioc_mbox_poll(ioc);
}
void
bfa_ioc_error_isr(struct bfa_ioc_s *ioc)
{
+ bfa_ioc_stats(ioc, ioc_hbfails);
+ ioc->stats.hb_count = ioc->hb_count;
bfa_fsm_send_event(ioc, IOC_E_HWERROR);
}
-void
-bfa_ioc_set_fcmode(struct bfa_ioc_s *ioc)
-{
- ioc->fcmode = BFA_TRUE;
- ioc->port_id = bfa_ioc_pcifn(ioc);
-}
-
/*
* return true if IOC is disabled
*/
@@ -2223,14 +2626,6 @@ bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc)
bfa_fsm_cmp_state(&ioc->iocpf, bfa_iocpf_sm_mismatch);
}
-#define bfa_ioc_state_disabled(__sm) \
- (((__sm) == BFI_IOC_UNINIT) || \
- ((__sm) == BFI_IOC_INITING) || \
- ((__sm) == BFI_IOC_HWINIT) || \
- ((__sm) == BFI_IOC_DISABLED) || \
- ((__sm) == BFI_IOC_FAIL) || \
- ((__sm) == BFI_IOC_CFG_DISABLED))
-
/*
* Check if adapter is disabled -- both IOCs should be in a disabled
* state.
@@ -2239,17 +2634,16 @@ bfa_boolean_t
bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc)
{
u32 ioc_state;
- void __iomem *rb = ioc->pcidev.pci_bar_kva;
if (!bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled))
return BFA_FALSE;
- ioc_state = readl(rb + BFA_IOC0_STATE_REG);
+ ioc_state = bfa_ioc_get_cur_ioc_fwstate(ioc);
if (!bfa_ioc_state_disabled(ioc_state))
return BFA_FALSE;
if (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_FC_8G1P) {
- ioc_state = readl(rb + BFA_IOC1_STATE_REG);
+ ioc_state = bfa_ioc_get_cur_ioc_fwstate(ioc);
if (!bfa_ioc_state_disabled(ioc_state))
return BFA_FALSE;
}
@@ -2263,11 +2657,11 @@ bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc)
void
bfa_ioc_reset_fwstate(struct bfa_ioc_s *ioc)
{
- writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
- writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate);
+ bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_UNINIT);
+ bfa_ioc_set_alt_ioc_fwstate(ioc, BFI_IOC_UNINIT);
}
-#define BFA_MFG_NAME "Brocade"
+#define BFA_MFG_NAME "QLogic"
void
bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc,
struct bfa_adapter_attr_s *ad_attr)
@@ -2308,24 +2702,25 @@ bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc,
bfa_ioc_get_pci_chip_rev(ioc, ad_attr->hw_ver);
- ad_attr->cna_capable = ioc->cna;
- ad_attr->trunk_capable = (ad_attr->nports > 1) && !ioc->cna &&
- !ad_attr->is_mezz;
+ ad_attr->cna_capable = bfa_ioc_is_cna(ioc);
+ ad_attr->trunk_capable = (ad_attr->nports > 1) &&
+ !bfa_ioc_is_cna(ioc) && !ad_attr->is_mezz;
+ ad_attr->mfg_day = ioc_attr->mfg_day;
+ ad_attr->mfg_month = ioc_attr->mfg_month;
+ ad_attr->mfg_year = ioc_attr->mfg_year;
+ memcpy(ad_attr->uuid, ioc_attr->uuid, BFA_ADAPTER_UUID_LEN);
}
enum bfa_ioc_type_e
bfa_ioc_get_type(struct bfa_ioc_s *ioc)
{
- if (!ioc->ctdev || ioc->fcmode)
- return BFA_IOC_TYPE_FC;
- else if (ioc->ioc_mc == BFI_MC_IOCFC)
- return BFA_IOC_TYPE_FCoE;
- else if (ioc->ioc_mc == BFI_MC_LL)
- return BFA_IOC_TYPE_LL;
- else {
- WARN_ON(ioc->ioc_mc != BFI_MC_LL);
+ if (ioc->clscode == BFI_PCIFN_CLASS_ETH)
return BFA_IOC_TYPE_LL;
- }
+
+ WARN_ON(ioc->clscode != BFI_PCIFN_CLASS_FC);
+
+ return (ioc->attr->port_mode == BFI_PORT_MODE_FC)
+ ? BFA_IOC_TYPE_FC : BFA_IOC_TYPE_FCoE;
}
void
@@ -2371,36 +2766,39 @@ void
bfa_ioc_get_adapter_manufacturer(struct bfa_ioc_s *ioc, char *manufacturer)
{
memset((void *)manufacturer, 0, BFA_ADAPTER_MFG_NAME_LEN);
- memcpy(manufacturer, BFA_MFG_NAME, BFA_ADAPTER_MFG_NAME_LEN);
+ strscpy(manufacturer, BFA_MFG_NAME, BFA_ADAPTER_MFG_NAME_LEN);
}
void
bfa_ioc_get_adapter_model(struct bfa_ioc_s *ioc, char *model)
{
struct bfi_ioc_attr_s *ioc_attr;
+ u8 nports = bfa_ioc_get_nports(ioc);
WARN_ON(!model);
memset((void *)model, 0, BFA_ADAPTER_MODEL_NAME_LEN);
ioc_attr = ioc->attr;
- /*
- * model name
- */
- snprintf(model, BFA_ADAPTER_MODEL_NAME_LEN, "%s-%u",
- BFA_MFG_NAME, ioc_attr->card_type);
+ if (bfa_asic_id_ct2(ioc->pcidev.device_id) &&
+ (!bfa_mfg_is_mezz(ioc_attr->card_type)))
+ snprintf(model, BFA_ADAPTER_MODEL_NAME_LEN, "%s-%u-%u%s",
+ BFA_MFG_NAME, ioc_attr->card_type, nports, "p");
+ else
+ snprintf(model, BFA_ADAPTER_MODEL_NAME_LEN, "%s-%u",
+ BFA_MFG_NAME, ioc_attr->card_type);
}
enum bfa_ioc_state
bfa_ioc_get_state(struct bfa_ioc_s *ioc)
{
enum bfa_iocpf_state iocpf_st;
- enum bfa_ioc_state ioc_st = bfa_sm_to_state(ioc_sm_table, ioc->fsm);
+ enum bfa_ioc_state ioc_st = bfa_ioc_sm_to_state(ioc_sm_table, ioc->fsm);
if (ioc_st == BFA_IOC_ENABLING ||
ioc_st == BFA_IOC_FAIL || ioc_st == BFA_IOC_INITFAIL) {
- iocpf_st = bfa_sm_to_state(iocpf_sm_table, ioc->iocpf.fsm);
+ iocpf_st = bfa_iocpf_sm_to_state(iocpf_sm_table, ioc->iocpf.fsm);
switch (iocpf_st) {
case BFA_IOCPF_SEMWAIT:
@@ -2437,14 +2835,18 @@ bfa_ioc_get_attr(struct bfa_ioc_s *ioc, struct bfa_ioc_attr_s *ioc_attr)
memset((void *)ioc_attr, 0, sizeof(struct bfa_ioc_attr_s));
ioc_attr->state = bfa_ioc_get_state(ioc);
- ioc_attr->port_id = ioc->port_id;
+ ioc_attr->port_id = bfa_ioc_portid(ioc);
+ ioc_attr->port_mode = ioc->port_mode;
+ ioc_attr->port_mode_cfg = ioc->port_mode_cfg;
+ ioc_attr->cap_bm = ioc->ad_cap_bm;
ioc_attr->ioc_type = bfa_ioc_get_type(ioc);
bfa_ioc_get_adapter_attr(ioc, &ioc_attr->adapter_attr);
- ioc_attr->pci_attr.device_id = ioc->pcidev.device_id;
- ioc_attr->pci_attr.pcifn = ioc->pcidev.pci_func;
+ ioc_attr->pci_attr.device_id = bfa_ioc_devid(ioc);
+ ioc_attr->pci_attr.pcifn = bfa_ioc_pcifn(ioc);
+ ioc_attr->def_fn = (bfa_ioc_pcifn(ioc) == bfa_ioc_portid(ioc));
bfa_ioc_get_pci_chip_rev(ioc, ioc_attr->pci_attr.chip_rev);
}
@@ -2475,10 +2877,41 @@ bfa_ioc_get_mfg_mac(struct bfa_ioc_s *ioc)
return m;
}
-bfa_boolean_t
-bfa_ioc_get_fcmode(struct bfa_ioc_s *ioc)
+/*
+ * Send AEN notification
+ */
+void
+bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event)
{
- return ioc->fcmode || !bfa_asic_id_ct(ioc->pcidev.device_id);
+ struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+ enum bfa_ioc_type_e ioc_type;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ ioc_type = bfa_ioc_get_type(ioc);
+ switch (ioc_type) {
+ case BFA_IOC_TYPE_FC:
+ aen_entry->aen_data.ioc.pwwn = ioc->attr->pwwn;
+ break;
+ case BFA_IOC_TYPE_FCoE:
+ aen_entry->aen_data.ioc.pwwn = ioc->attr->pwwn;
+ aen_entry->aen_data.ioc.mac = bfa_ioc_get_mac(ioc);
+ break;
+ case BFA_IOC_TYPE_LL:
+ aen_entry->aen_data.ioc.mac = bfa_ioc_get_mac(ioc);
+ break;
+ default:
+ WARN_ON(ioc_type != BFA_IOC_TYPE_FC);
+ break;
+ }
+
+ /* Send the AEN notification */
+ aen_entry->aen_data.ioc.ioc_type = ioc_type;
+ bfad_im_post_vendor_event(aen_entry, bfad, ++ioc->ioc_aen_seq,
+ BFA_AEN_CAT_IOC, event);
}
/*
@@ -2531,7 +2964,7 @@ bfa_ioc_send_fwsync(struct bfa_ioc_s *ioc)
bfi_h2i_set(req->mh, BFI_MC_IOC, BFI_IOC_H2I_DBG_SYNC,
bfa_ioc_portid(ioc));
- req->ioc_class = ioc->ioc_mc;
+ req->clscode = cpu_to_be16(ioc->clscode);
bfa_ioc_mbox_queue(ioc, &cmd);
}
@@ -2652,7 +3085,7 @@ bfa_ioc_fw_stats_clear(struct bfa_ioc_s *ioc)
/*
* Save firmware trace if configured.
*/
-static void
+void
bfa_ioc_debug_save_ftrc(struct bfa_ioc_s *ioc)
{
int tlen;
@@ -2673,16 +3106,10 @@ static void
bfa_ioc_recover(struct bfa_ioc_s *ioc)
{
bfa_ioc_stats(ioc, ioc_hbfails);
+ ioc->stats.hb_count = ioc->hb_count;
bfa_fsm_send_event(ioc, IOC_E_HBFAIL);
}
-static void
-bfa_ioc_check_attr_wwns(struct bfa_ioc_s *ioc)
-{
- if (bfa_ioc_get_type(ioc) == BFA_IOC_TYPE_LL)
- return;
-}
-
/*
* BFA IOC PF private functions
*/
@@ -2703,6 +3130,34 @@ bfa_iocpf_sem_timeout(void *ioc_arg)
bfa_ioc_hw_sem_get(ioc);
}
+static void
+bfa_ioc_poll_fwinit(struct bfa_ioc_s *ioc)
+{
+ u32 fwstate = bfa_ioc_get_cur_ioc_fwstate(ioc);
+
+ bfa_trc(ioc, fwstate);
+
+ if (fwstate == BFI_IOC_DISABLED) {
+ bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_FWREADY);
+ return;
+ }
+
+ if (ioc->iocpf.poll_time >= (3 * BFA_IOC_TOV))
+ bfa_iocpf_timeout(ioc);
+ else {
+ ioc->iocpf.poll_time += BFA_IOC_POLL_TOV;
+ bfa_iocpf_poll_timer_start(ioc);
+ }
+}
+
+static void
+bfa_iocpf_poll_timeout(void *ioc_arg)
+{
+ struct bfa_ioc_s *ioc = (struct bfa_ioc_s *) ioc_arg;
+
+ bfa_ioc_poll_fwinit(ioc);
+}
+
/*
* bfa timer function
*/
@@ -2770,3 +3225,3774 @@ bfa_timer_stop(struct bfa_timer_s *timer)
list_del(&timer->qe);
}
+
+/*
+ * ASIC block related
+ */
+static void
+bfa_ablk_config_swap(struct bfa_ablk_cfg_s *cfg)
+{
+ struct bfa_ablk_cfg_inst_s *cfg_inst;
+ int i, j;
+ u16 be16;
+
+ for (i = 0; i < BFA_ABLK_MAX; i++) {
+ cfg_inst = &cfg->inst[i];
+ for (j = 0; j < BFA_ABLK_MAX_PFS; j++) {
+ be16 = cfg_inst->pf_cfg[j].pers;
+ cfg_inst->pf_cfg[j].pers = be16_to_cpu(be16);
+ be16 = cfg_inst->pf_cfg[j].num_qpairs;
+ cfg_inst->pf_cfg[j].num_qpairs = be16_to_cpu(be16);
+ be16 = cfg_inst->pf_cfg[j].num_vectors;
+ cfg_inst->pf_cfg[j].num_vectors = be16_to_cpu(be16);
+ be16 = cfg_inst->pf_cfg[j].bw_min;
+ cfg_inst->pf_cfg[j].bw_min = be16_to_cpu(be16);
+ be16 = cfg_inst->pf_cfg[j].bw_max;
+ cfg_inst->pf_cfg[j].bw_max = be16_to_cpu(be16);
+ }
+ }
+}
+
+static void
+bfa_ablk_isr(void *cbarg, struct bfi_mbmsg_s *msg)
+{
+ struct bfa_ablk_s *ablk = (struct bfa_ablk_s *)cbarg;
+ struct bfi_ablk_i2h_rsp_s *rsp = (struct bfi_ablk_i2h_rsp_s *)msg;
+ bfa_ablk_cbfn_t cbfn;
+
+ WARN_ON(msg->mh.msg_class != BFI_MC_ABLK);
+ bfa_trc(ablk->ioc, msg->mh.msg_id);
+
+ switch (msg->mh.msg_id) {
+ case BFI_ABLK_I2H_QUERY:
+ if (rsp->status == BFA_STATUS_OK) {
+ memcpy(ablk->cfg, ablk->dma_addr.kva,
+ sizeof(struct bfa_ablk_cfg_s));
+ bfa_ablk_config_swap(ablk->cfg);
+ ablk->cfg = NULL;
+ }
+ break;
+
+ case BFI_ABLK_I2H_ADPT_CONFIG:
+ case BFI_ABLK_I2H_PORT_CONFIG:
+ /* update config port mode */
+ ablk->ioc->port_mode_cfg = rsp->port_mode;
+ break;
+
+ case BFI_ABLK_I2H_PF_DELETE:
+ case BFI_ABLK_I2H_PF_UPDATE:
+ case BFI_ABLK_I2H_OPTROM_ENABLE:
+ case BFI_ABLK_I2H_OPTROM_DISABLE:
+ /* No-op */
+ break;
+
+ case BFI_ABLK_I2H_PF_CREATE:
+ *(ablk->pcifn) = rsp->pcifn;
+ ablk->pcifn = NULL;
+ break;
+
+ default:
+ WARN_ON(1);
+ }
+
+ ablk->busy = BFA_FALSE;
+ if (ablk->cbfn) {
+ cbfn = ablk->cbfn;
+ ablk->cbfn = NULL;
+ cbfn(ablk->cbarg, rsp->status);
+ }
+}
+
+static void
+bfa_ablk_notify(void *cbarg, enum bfa_ioc_event_e event)
+{
+ struct bfa_ablk_s *ablk = (struct bfa_ablk_s *)cbarg;
+
+ bfa_trc(ablk->ioc, event);
+
+ switch (event) {
+ case BFA_IOC_E_ENABLED:
+ WARN_ON(ablk->busy != BFA_FALSE);
+ break;
+
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ /* Fail any pending requests */
+ ablk->pcifn = NULL;
+ if (ablk->busy) {
+ if (ablk->cbfn)
+ ablk->cbfn(ablk->cbarg, BFA_STATUS_FAILED);
+ ablk->cbfn = NULL;
+ ablk->busy = BFA_FALSE;
+ }
+ break;
+
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+u32
+bfa_ablk_meminfo(void)
+{
+ return BFA_ROUNDUP(sizeof(struct bfa_ablk_cfg_s), BFA_DMA_ALIGN_SZ);
+}
+
+void
+bfa_ablk_memclaim(struct bfa_ablk_s *ablk, u8 *dma_kva, u64 dma_pa)
+{
+ ablk->dma_addr.kva = dma_kva;
+ ablk->dma_addr.pa = dma_pa;
+}
+
+void
+bfa_ablk_attach(struct bfa_ablk_s *ablk, struct bfa_ioc_s *ioc)
+{
+ ablk->ioc = ioc;
+
+ bfa_ioc_mbox_regisr(ablk->ioc, BFI_MC_ABLK, bfa_ablk_isr, ablk);
+ bfa_q_qe_init(&ablk->ioc_notify);
+ bfa_ioc_notify_init(&ablk->ioc_notify, bfa_ablk_notify, ablk);
+ list_add_tail(&ablk->ioc_notify.qe, &ablk->ioc->notify_q);
+}
+
+bfa_status_t
+bfa_ablk_query(struct bfa_ablk_s *ablk, struct bfa_ablk_cfg_s *ablk_cfg,
+ bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_query_s *m;
+
+ WARN_ON(!ablk_cfg);
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->cfg = ablk_cfg;
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_query_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_QUERY,
+ bfa_ioc_portid(ablk->ioc));
+ bfa_dma_be_addr_set(m->addr, ablk->dma_addr.pa);
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_ablk_pf_create(struct bfa_ablk_s *ablk, u16 *pcifn,
+ u8 port, enum bfi_pcifn_class personality,
+ u16 bw_min, u16 bw_max,
+ bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_pf_req_s *m;
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->pcifn = pcifn;
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_pf_req_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_PF_CREATE,
+ bfa_ioc_portid(ablk->ioc));
+ m->pers = cpu_to_be16((u16)personality);
+ m->bw_min = cpu_to_be16(bw_min);
+ m->bw_max = cpu_to_be16(bw_max);
+ m->port = port;
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_ablk_pf_delete(struct bfa_ablk_s *ablk, int pcifn,
+ bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_pf_req_s *m;
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_pf_req_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_PF_DELETE,
+ bfa_ioc_portid(ablk->ioc));
+ m->pcifn = (u8)pcifn;
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_ablk_adapter_config(struct bfa_ablk_s *ablk, enum bfa_mode_s mode,
+ int max_pf, int max_vf, bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_cfg_req_s *m;
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_cfg_req_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_ADPT_CONFIG,
+ bfa_ioc_portid(ablk->ioc));
+ m->mode = (u8)mode;
+ m->max_pf = (u8)max_pf;
+ m->max_vf = (u8)max_vf;
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_ablk_port_config(struct bfa_ablk_s *ablk, int port, enum bfa_mode_s mode,
+ int max_pf, int max_vf, bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_cfg_req_s *m;
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_cfg_req_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_PORT_CONFIG,
+ bfa_ioc_portid(ablk->ioc));
+ m->port = (u8)port;
+ m->mode = (u8)mode;
+ m->max_pf = (u8)max_pf;
+ m->max_vf = (u8)max_vf;
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_ablk_pf_update(struct bfa_ablk_s *ablk, int pcifn, u16 bw_min,
+ u16 bw_max, bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_pf_req_s *m;
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_pf_req_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_PF_UPDATE,
+ bfa_ioc_portid(ablk->ioc));
+ m->pcifn = (u8)pcifn;
+ m->bw_min = cpu_to_be16(bw_min);
+ m->bw_max = cpu_to_be16(bw_max);
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_ablk_optrom_en(struct bfa_ablk_s *ablk, bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_optrom_s *m;
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_optrom_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_OPTROM_ENABLE,
+ bfa_ioc_portid(ablk->ioc));
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_ablk_optrom_dis(struct bfa_ablk_s *ablk, bfa_ablk_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_ablk_h2i_optrom_s *m;
+
+ if (!bfa_ioc_is_operational(ablk->ioc)) {
+ bfa_trc(ablk->ioc, BFA_STATUS_IOC_FAILURE);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+
+ if (ablk->busy) {
+ bfa_trc(ablk->ioc, BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ ablk->cbfn = cbfn;
+ ablk->cbarg = cbarg;
+ ablk->busy = BFA_TRUE;
+
+ m = (struct bfi_ablk_h2i_optrom_s *)ablk->mb.msg;
+ bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_OPTROM_DISABLE,
+ bfa_ioc_portid(ablk->ioc));
+ bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * SFP module specific
+ */
+
+/* forward declarations */
+static void bfa_sfp_getdata_send(struct bfa_sfp_s *sfp);
+static void bfa_sfp_media_get(struct bfa_sfp_s *sfp);
+static bfa_status_t bfa_sfp_speed_valid(struct bfa_sfp_s *sfp,
+ enum bfa_port_speed portspeed);
+
+static void
+bfa_cb_sfp_show(struct bfa_sfp_s *sfp)
+{
+ bfa_trc(sfp, sfp->lock);
+ if (sfp->cbfn)
+ sfp->cbfn(sfp->cbarg, sfp->status);
+ sfp->lock = 0;
+ sfp->cbfn = NULL;
+}
+
+static void
+bfa_cb_sfp_state_query(struct bfa_sfp_s *sfp)
+{
+ bfa_trc(sfp, sfp->portspeed);
+ if (sfp->media) {
+ bfa_sfp_media_get(sfp);
+ if (sfp->state_query_cbfn)
+ sfp->state_query_cbfn(sfp->state_query_cbarg,
+ sfp->status);
+ sfp->media = NULL;
+ }
+
+ if (sfp->portspeed) {
+ sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed);
+ if (sfp->state_query_cbfn)
+ sfp->state_query_cbfn(sfp->state_query_cbarg,
+ sfp->status);
+ sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
+ }
+
+ sfp->state_query_lock = 0;
+ sfp->state_query_cbfn = NULL;
+}
+
+/*
+ * IOC event handler.
+ */
+static void
+bfa_sfp_notify(void *sfp_arg, enum bfa_ioc_event_e event)
+{
+ struct bfa_sfp_s *sfp = sfp_arg;
+
+ bfa_trc(sfp, event);
+ bfa_trc(sfp, sfp->lock);
+ bfa_trc(sfp, sfp->state_query_lock);
+
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (sfp->lock) {
+ sfp->status = BFA_STATUS_IOC_FAILURE;
+ bfa_cb_sfp_show(sfp);
+ }
+
+ if (sfp->state_query_lock) {
+ sfp->status = BFA_STATUS_IOC_FAILURE;
+ bfa_cb_sfp_state_query(sfp);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * SFP's State Change Notification post to AEN
+ */
+static void
+bfa_sfp_scn_aen_post(struct bfa_sfp_s *sfp, struct bfi_sfp_scn_s *rsp)
+{
+ struct bfad_s *bfad = (struct bfad_s *)sfp->ioc->bfa->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+ enum bfa_port_aen_event aen_evt = 0;
+
+ bfa_trc(sfp, (((u64)rsp->pomlvl) << 16) | (((u64)rsp->sfpid) << 8) |
+ ((u64)rsp->event));
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ aen_entry->aen_data.port.ioc_type = bfa_ioc_get_type(sfp->ioc);
+ aen_entry->aen_data.port.pwwn = sfp->ioc->attr->pwwn;
+ aen_entry->aen_data.port.mac = bfa_ioc_get_mac(sfp->ioc);
+
+ switch (rsp->event) {
+ case BFA_SFP_SCN_INSERTED:
+ aen_evt = BFA_PORT_AEN_SFP_INSERT;
+ break;
+ case BFA_SFP_SCN_REMOVED:
+ aen_evt = BFA_PORT_AEN_SFP_REMOVE;
+ break;
+ case BFA_SFP_SCN_FAILED:
+ aen_evt = BFA_PORT_AEN_SFP_ACCESS_ERROR;
+ break;
+ case BFA_SFP_SCN_UNSUPPORT:
+ aen_evt = BFA_PORT_AEN_SFP_UNSUPPORT;
+ break;
+ case BFA_SFP_SCN_POM:
+ aen_evt = BFA_PORT_AEN_SFP_POM;
+ aen_entry->aen_data.port.level = rsp->pomlvl;
+ break;
+ default:
+ bfa_trc(sfp, rsp->event);
+ WARN_ON(1);
+ }
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++sfp->ioc->ioc_aen_seq,
+ BFA_AEN_CAT_PORT, aen_evt);
+}
+
+/*
+ * SFP get data send
+ */
+static void
+bfa_sfp_getdata_send(struct bfa_sfp_s *sfp)
+{
+ struct bfi_sfp_req_s *req = (struct bfi_sfp_req_s *)sfp->mbcmd.msg;
+
+ bfa_trc(sfp, req->memtype);
+
+ /* build host command */
+ bfi_h2i_set(req->mh, BFI_MC_SFP, BFI_SFP_H2I_SHOW,
+ bfa_ioc_portid(sfp->ioc));
+
+ /* send mbox cmd */
+ bfa_ioc_mbox_queue(sfp->ioc, &sfp->mbcmd);
+}
+
+/*
+ * SFP is valid, read sfp data
+ */
+static void
+bfa_sfp_getdata(struct bfa_sfp_s *sfp, enum bfi_sfp_mem_e memtype)
+{
+ struct bfi_sfp_req_s *req = (struct bfi_sfp_req_s *)sfp->mbcmd.msg;
+
+ WARN_ON(sfp->lock != 0);
+ bfa_trc(sfp, sfp->state);
+
+ sfp->lock = 1;
+ sfp->memtype = memtype;
+ req->memtype = memtype;
+
+ /* Setup SG list */
+ bfa_alen_set(&req->alen, sizeof(struct sfp_mem_s), sfp->dbuf_pa);
+
+ bfa_sfp_getdata_send(sfp);
+}
+
+/*
+ * SFP scn handler
+ */
+static void
+bfa_sfp_scn(struct bfa_sfp_s *sfp, struct bfi_mbmsg_s *msg)
+{
+ struct bfi_sfp_scn_s *rsp = (struct bfi_sfp_scn_s *) msg;
+
+ switch (rsp->event) {
+ case BFA_SFP_SCN_INSERTED:
+ sfp->state = BFA_SFP_STATE_INSERTED;
+ sfp->data_valid = 0;
+ bfa_sfp_scn_aen_post(sfp, rsp);
+ break;
+ case BFA_SFP_SCN_REMOVED:
+ sfp->state = BFA_SFP_STATE_REMOVED;
+ sfp->data_valid = 0;
+ bfa_sfp_scn_aen_post(sfp, rsp);
+ break;
+ case BFA_SFP_SCN_FAILED:
+ sfp->state = BFA_SFP_STATE_FAILED;
+ sfp->data_valid = 0;
+ bfa_sfp_scn_aen_post(sfp, rsp);
+ break;
+ case BFA_SFP_SCN_UNSUPPORT:
+ sfp->state = BFA_SFP_STATE_UNSUPPORT;
+ bfa_sfp_scn_aen_post(sfp, rsp);
+ if (!sfp->lock)
+ bfa_sfp_getdata(sfp, BFI_SFP_MEM_ALL);
+ break;
+ case BFA_SFP_SCN_POM:
+ bfa_sfp_scn_aen_post(sfp, rsp);
+ break;
+ case BFA_SFP_SCN_VALID:
+ sfp->state = BFA_SFP_STATE_VALID;
+ if (!sfp->lock)
+ bfa_sfp_getdata(sfp, BFI_SFP_MEM_ALL);
+ break;
+ default:
+ bfa_trc(sfp, rsp->event);
+ WARN_ON(1);
+ }
+}
+
+/*
+ * SFP show complete
+ */
+static void
+bfa_sfp_show_comp(struct bfa_sfp_s *sfp, struct bfi_mbmsg_s *msg)
+{
+ struct bfi_sfp_rsp_s *rsp = (struct bfi_sfp_rsp_s *) msg;
+
+ if (!sfp->lock) {
+ /*
+ * receiving response after ioc failure
+ */
+ bfa_trc(sfp, sfp->lock);
+ return;
+ }
+
+ bfa_trc(sfp, rsp->status);
+ if (rsp->status == BFA_STATUS_OK) {
+ sfp->data_valid = 1;
+ if (sfp->state == BFA_SFP_STATE_VALID)
+ sfp->status = BFA_STATUS_OK;
+ else if (sfp->state == BFA_SFP_STATE_UNSUPPORT)
+ sfp->status = BFA_STATUS_SFP_UNSUPP;
+ else
+ bfa_trc(sfp, sfp->state);
+ } else {
+ sfp->data_valid = 0;
+ sfp->status = rsp->status;
+ /* sfpshow shouldn't change sfp state */
+ }
+
+ bfa_trc(sfp, sfp->memtype);
+ if (sfp->memtype == BFI_SFP_MEM_DIAGEXT) {
+ bfa_trc(sfp, sfp->data_valid);
+ if (sfp->data_valid) {
+ u32 size = sizeof(struct sfp_mem_s);
+ u8 *des = (u8 *)(sfp->sfpmem);
+ memcpy(des, sfp->dbuf_kva, size);
+ }
+ /*
+ * Queue completion callback.
+ */
+ bfa_cb_sfp_show(sfp);
+ } else
+ sfp->lock = 0;
+
+ bfa_trc(sfp, sfp->state_query_lock);
+ if (sfp->state_query_lock) {
+ sfp->state = rsp->state;
+ /* Complete callback */
+ bfa_cb_sfp_state_query(sfp);
+ }
+}
+
+/*
+ * SFP query fw sfp state
+ */
+static void
+bfa_sfp_state_query(struct bfa_sfp_s *sfp)
+{
+ struct bfi_sfp_req_s *req = (struct bfi_sfp_req_s *)sfp->mbcmd.msg;
+
+ /* Should not be doing query if not in _INIT state */
+ WARN_ON(sfp->state != BFA_SFP_STATE_INIT);
+ WARN_ON(sfp->state_query_lock != 0);
+ bfa_trc(sfp, sfp->state);
+
+ sfp->state_query_lock = 1;
+ req->memtype = 0;
+
+ if (!sfp->lock)
+ bfa_sfp_getdata(sfp, BFI_SFP_MEM_ALL);
+}
+
+static void
+bfa_sfp_media_get(struct bfa_sfp_s *sfp)
+{
+ enum bfa_defs_sfp_media_e *media = sfp->media;
+
+ *media = BFA_SFP_MEDIA_UNKNOWN;
+
+ if (sfp->state == BFA_SFP_STATE_UNSUPPORT)
+ *media = BFA_SFP_MEDIA_UNSUPPORT;
+ else if (sfp->state == BFA_SFP_STATE_VALID) {
+ union sfp_xcvr_e10g_code_u e10g;
+ struct sfp_mem_s *sfpmem = (struct sfp_mem_s *)sfp->dbuf_kva;
+ u16 xmtr_tech = (sfpmem->srlid_base.xcvr[4] & 0x3) << 7 |
+ (sfpmem->srlid_base.xcvr[5] >> 1);
+
+ e10g.b = sfpmem->srlid_base.xcvr[0];
+ bfa_trc(sfp, e10g.b);
+ bfa_trc(sfp, xmtr_tech);
+ /* check fc transmitter tech */
+ if ((xmtr_tech & SFP_XMTR_TECH_CU) ||
+ (xmtr_tech & SFP_XMTR_TECH_CP) ||
+ (xmtr_tech & SFP_XMTR_TECH_CA))
+ *media = BFA_SFP_MEDIA_CU;
+ else if ((xmtr_tech & SFP_XMTR_TECH_EL_INTRA) ||
+ (xmtr_tech & SFP_XMTR_TECH_EL_INTER))
+ *media = BFA_SFP_MEDIA_EL;
+ else if ((xmtr_tech & SFP_XMTR_TECH_LL) ||
+ (xmtr_tech & SFP_XMTR_TECH_LC))
+ *media = BFA_SFP_MEDIA_LW;
+ else if ((xmtr_tech & SFP_XMTR_TECH_SL) ||
+ (xmtr_tech & SFP_XMTR_TECH_SN) ||
+ (xmtr_tech & SFP_XMTR_TECH_SA))
+ *media = BFA_SFP_MEDIA_SW;
+ /* Check 10G Ethernet Compilance code */
+ else if (e10g.r.e10g_sr)
+ *media = BFA_SFP_MEDIA_SW;
+ else if (e10g.r.e10g_lrm && e10g.r.e10g_lr)
+ *media = BFA_SFP_MEDIA_LW;
+ else if (e10g.r.e10g_unall)
+ *media = BFA_SFP_MEDIA_UNKNOWN;
+ else
+ bfa_trc(sfp, 0);
+ } else
+ bfa_trc(sfp, sfp->state);
+}
+
+static bfa_status_t
+bfa_sfp_speed_valid(struct bfa_sfp_s *sfp, enum bfa_port_speed portspeed)
+{
+ struct sfp_mem_s *sfpmem = (struct sfp_mem_s *)sfp->dbuf_kva;
+ struct sfp_xcvr_s *xcvr = (struct sfp_xcvr_s *) sfpmem->srlid_base.xcvr;
+ union sfp_xcvr_fc3_code_u fc3 = xcvr->fc3;
+ union sfp_xcvr_e10g_code_u e10g = xcvr->e10g;
+
+ if (portspeed == BFA_PORT_SPEED_10GBPS) {
+ if (e10g.r.e10g_sr || e10g.r.e10g_lr)
+ return BFA_STATUS_OK;
+ else {
+ bfa_trc(sfp, e10g.b);
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+ }
+ if (((portspeed & BFA_PORT_SPEED_16GBPS) && fc3.r.mb1600) ||
+ ((portspeed & BFA_PORT_SPEED_8GBPS) && fc3.r.mb800) ||
+ ((portspeed & BFA_PORT_SPEED_4GBPS) && fc3.r.mb400) ||
+ ((portspeed & BFA_PORT_SPEED_2GBPS) && fc3.r.mb200) ||
+ ((portspeed & BFA_PORT_SPEED_1GBPS) && fc3.r.mb100))
+ return BFA_STATUS_OK;
+ else {
+ bfa_trc(sfp, portspeed);
+ bfa_trc(sfp, fc3.b);
+ bfa_trc(sfp, e10g.b);
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+}
+
+/*
+ * SFP hmbox handler
+ */
+void
+bfa_sfp_intr(void *sfparg, struct bfi_mbmsg_s *msg)
+{
+ struct bfa_sfp_s *sfp = sfparg;
+
+ switch (msg->mh.msg_id) {
+ case BFI_SFP_I2H_SHOW:
+ bfa_sfp_show_comp(sfp, msg);
+ break;
+
+ case BFI_SFP_I2H_SCN:
+ bfa_sfp_scn(sfp, msg);
+ break;
+
+ default:
+ bfa_trc(sfp, msg->mh.msg_id);
+ WARN_ON(1);
+ }
+}
+
+/*
+ * Return DMA memory needed by sfp module.
+ */
+u32
+bfa_sfp_meminfo(void)
+{
+ return BFA_ROUNDUP(sizeof(struct sfp_mem_s), BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Attach virtual and physical memory for SFP.
+ */
+void
+bfa_sfp_attach(struct bfa_sfp_s *sfp, struct bfa_ioc_s *ioc, void *dev,
+ struct bfa_trc_mod_s *trcmod)
+{
+ sfp->dev = dev;
+ sfp->ioc = ioc;
+ sfp->trcmod = trcmod;
+
+ sfp->cbfn = NULL;
+ sfp->cbarg = NULL;
+ sfp->sfpmem = NULL;
+ sfp->lock = 0;
+ sfp->data_valid = 0;
+ sfp->state = BFA_SFP_STATE_INIT;
+ sfp->state_query_lock = 0;
+ sfp->state_query_cbfn = NULL;
+ sfp->state_query_cbarg = NULL;
+ sfp->media = NULL;
+ sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
+ sfp->is_elb = BFA_FALSE;
+
+ bfa_ioc_mbox_regisr(sfp->ioc, BFI_MC_SFP, bfa_sfp_intr, sfp);
+ bfa_q_qe_init(&sfp->ioc_notify);
+ bfa_ioc_notify_init(&sfp->ioc_notify, bfa_sfp_notify, sfp);
+ list_add_tail(&sfp->ioc_notify.qe, &sfp->ioc->notify_q);
+}
+
+/*
+ * Claim Memory for SFP
+ */
+void
+bfa_sfp_memclaim(struct bfa_sfp_s *sfp, u8 *dm_kva, u64 dm_pa)
+{
+ sfp->dbuf_kva = dm_kva;
+ sfp->dbuf_pa = dm_pa;
+ memset(sfp->dbuf_kva, 0, sizeof(struct sfp_mem_s));
+
+ dm_kva += BFA_ROUNDUP(sizeof(struct sfp_mem_s), BFA_DMA_ALIGN_SZ);
+ dm_pa += BFA_ROUNDUP(sizeof(struct sfp_mem_s), BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Show SFP eeprom content
+ *
+ * @param[in] sfp - bfa sfp module
+ *
+ * @param[out] sfpmem - sfp eeprom data
+ *
+ */
+bfa_status_t
+bfa_sfp_show(struct bfa_sfp_s *sfp, struct sfp_mem_s *sfpmem,
+ bfa_cb_sfp_t cbfn, void *cbarg)
+{
+
+ if (!bfa_ioc_is_operational(sfp->ioc)) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_IOC_NON_OP;
+ }
+
+ if (sfp->lock) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ sfp->cbfn = cbfn;
+ sfp->cbarg = cbarg;
+ sfp->sfpmem = sfpmem;
+
+ bfa_sfp_getdata(sfp, BFI_SFP_MEM_DIAGEXT);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Return SFP Media type
+ *
+ * @param[in] sfp - bfa sfp module
+ *
+ * @param[out] media - port speed from user
+ *
+ */
+bfa_status_t
+bfa_sfp_media(struct bfa_sfp_s *sfp, enum bfa_defs_sfp_media_e *media,
+ bfa_cb_sfp_t cbfn, void *cbarg)
+{
+ if (!bfa_ioc_is_operational(sfp->ioc)) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_IOC_NON_OP;
+ }
+
+ sfp->media = media;
+ if (sfp->state == BFA_SFP_STATE_INIT) {
+ if (sfp->state_query_lock) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_DEVBUSY;
+ } else {
+ sfp->state_query_cbfn = cbfn;
+ sfp->state_query_cbarg = cbarg;
+ bfa_sfp_state_query(sfp);
+ return BFA_STATUS_SFP_NOT_READY;
+ }
+ }
+
+ bfa_sfp_media_get(sfp);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Check if user set port speed is allowed by the SFP
+ *
+ * @param[in] sfp - bfa sfp module
+ * @param[in] portspeed - port speed from user
+ *
+ */
+bfa_status_t
+bfa_sfp_speed(struct bfa_sfp_s *sfp, enum bfa_port_speed portspeed,
+ bfa_cb_sfp_t cbfn, void *cbarg)
+{
+ WARN_ON(portspeed == BFA_PORT_SPEED_UNKNOWN);
+
+ if (!bfa_ioc_is_operational(sfp->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* For Mezz card, all speed is allowed */
+ if (bfa_mfg_is_mezz(sfp->ioc->attr->card_type))
+ return BFA_STATUS_OK;
+
+ /* Check SFP state */
+ sfp->portspeed = portspeed;
+ if (sfp->state == BFA_SFP_STATE_INIT) {
+ if (sfp->state_query_lock) {
+ bfa_trc(sfp, 0);
+ return BFA_STATUS_DEVBUSY;
+ } else {
+ sfp->state_query_cbfn = cbfn;
+ sfp->state_query_cbarg = cbarg;
+ bfa_sfp_state_query(sfp);
+ return BFA_STATUS_SFP_NOT_READY;
+ }
+ }
+
+ if (sfp->state == BFA_SFP_STATE_REMOVED ||
+ sfp->state == BFA_SFP_STATE_FAILED) {
+ bfa_trc(sfp, sfp->state);
+ return BFA_STATUS_NO_SFP_DEV;
+ }
+
+ if (sfp->state == BFA_SFP_STATE_INSERTED) {
+ bfa_trc(sfp, sfp->state);
+ return BFA_STATUS_DEVBUSY; /* sfp is reading data */
+ }
+
+ /* For eloopback, all speed is allowed */
+ if (sfp->is_elb)
+ return BFA_STATUS_OK;
+
+ return bfa_sfp_speed_valid(sfp, portspeed);
+}
+
+/*
+ * Flash module specific
+ */
+
+/*
+ * FLASH DMA buffer should be big enough to hold both MFG block and
+ * asic block(64k) at the same time and also should be 2k aligned to
+ * avoid write segement to cross sector boundary.
+ */
+#define BFA_FLASH_SEG_SZ 2048
+#define BFA_FLASH_DMA_BUF_SZ \
+ BFA_ROUNDUP(0x010000 + sizeof(struct bfa_mfg_block_s), BFA_FLASH_SEG_SZ)
+
+static void
+bfa_flash_aen_audit_post(struct bfa_ioc_s *ioc, enum bfa_audit_aen_event event,
+ int inst, int type)
+{
+ struct bfad_s *bfad = (struct bfad_s *)ioc->bfa->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ aen_entry->aen_data.audit.pwwn = ioc->attr->pwwn;
+ aen_entry->aen_data.audit.partition_inst = inst;
+ aen_entry->aen_data.audit.partition_type = type;
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++ioc->ioc_aen_seq,
+ BFA_AEN_CAT_AUDIT, event);
+}
+
+static void
+bfa_flash_cb(struct bfa_flash_s *flash)
+{
+ flash->op_busy = 0;
+ if (flash->cbfn)
+ flash->cbfn(flash->cbarg, flash->status);
+}
+
+static void
+bfa_flash_notify(void *cbarg, enum bfa_ioc_event_e event)
+{
+ struct bfa_flash_s *flash = cbarg;
+
+ bfa_trc(flash, event);
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (flash->op_busy) {
+ flash->status = BFA_STATUS_IOC_FAILURE;
+ flash->cbfn(flash->cbarg, flash->status);
+ flash->op_busy = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Send flash attribute query request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_flash_query_send(void *cbarg)
+{
+ struct bfa_flash_s *flash = cbarg;
+ struct bfi_flash_query_req_s *msg =
+ (struct bfi_flash_query_req_s *) flash->mb.msg;
+
+ bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_QUERY_REQ,
+ bfa_ioc_portid(flash->ioc));
+ bfa_alen_set(&msg->alen, sizeof(struct bfa_flash_attr_s),
+ flash->dbuf_pa);
+ bfa_ioc_mbox_queue(flash->ioc, &flash->mb);
+}
+
+/*
+ * Send flash write request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_flash_write_send(struct bfa_flash_s *flash)
+{
+ struct bfi_flash_write_req_s *msg =
+ (struct bfi_flash_write_req_s *) flash->mb.msg;
+ u32 len;
+
+ msg->type = be32_to_cpu(flash->type);
+ msg->instance = flash->instance;
+ msg->offset = be32_to_cpu(flash->addr_off + flash->offset);
+ len = (flash->residue < BFA_FLASH_DMA_BUF_SZ) ?
+ flash->residue : BFA_FLASH_DMA_BUF_SZ;
+ msg->length = be32_to_cpu(len);
+
+ /* indicate if it's the last msg of the whole write operation */
+ msg->last = (len == flash->residue) ? 1 : 0;
+
+ bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_WRITE_REQ,
+ bfa_ioc_portid(flash->ioc));
+ bfa_alen_set(&msg->alen, len, flash->dbuf_pa);
+ memcpy(flash->dbuf_kva, flash->ubuf + flash->offset, len);
+ bfa_ioc_mbox_queue(flash->ioc, &flash->mb);
+
+ flash->residue -= len;
+ flash->offset += len;
+}
+
+/*
+ * Send flash read request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_flash_read_send(void *cbarg)
+{
+ struct bfa_flash_s *flash = cbarg;
+ struct bfi_flash_read_req_s *msg =
+ (struct bfi_flash_read_req_s *) flash->mb.msg;
+ u32 len;
+
+ msg->type = be32_to_cpu(flash->type);
+ msg->instance = flash->instance;
+ msg->offset = be32_to_cpu(flash->addr_off + flash->offset);
+ len = (flash->residue < BFA_FLASH_DMA_BUF_SZ) ?
+ flash->residue : BFA_FLASH_DMA_BUF_SZ;
+ msg->length = be32_to_cpu(len);
+ bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_READ_REQ,
+ bfa_ioc_portid(flash->ioc));
+ bfa_alen_set(&msg->alen, len, flash->dbuf_pa);
+ bfa_ioc_mbox_queue(flash->ioc, &flash->mb);
+}
+
+/*
+ * Send flash erase request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_flash_erase_send(void *cbarg)
+{
+ struct bfa_flash_s *flash = cbarg;
+ struct bfi_flash_erase_req_s *msg =
+ (struct bfi_flash_erase_req_s *) flash->mb.msg;
+
+ msg->type = be32_to_cpu(flash->type);
+ msg->instance = flash->instance;
+ bfi_h2i_set(msg->mh, BFI_MC_FLASH, BFI_FLASH_H2I_ERASE_REQ,
+ bfa_ioc_portid(flash->ioc));
+ bfa_ioc_mbox_queue(flash->ioc, &flash->mb);
+}
+
+/*
+ * Process flash response messages upon receiving interrupts.
+ *
+ * @param[in] flasharg - flash structure
+ * @param[in] msg - message structure
+ */
+static void
+bfa_flash_intr(void *flasharg, struct bfi_mbmsg_s *msg)
+{
+ struct bfa_flash_s *flash = flasharg;
+ u32 status;
+
+ union {
+ struct bfi_flash_query_rsp_s *query;
+ struct bfi_flash_erase_rsp_s *erase;
+ struct bfi_flash_write_rsp_s *write;
+ struct bfi_flash_read_rsp_s *read;
+ struct bfi_flash_event_s *event;
+ struct bfi_mbmsg_s *msg;
+ } m;
+
+ m.msg = msg;
+ bfa_trc(flash, msg->mh.msg_id);
+
+ if (!flash->op_busy && msg->mh.msg_id != BFI_FLASH_I2H_EVENT) {
+ /* receiving response after ioc failure */
+ bfa_trc(flash, 0x9999);
+ return;
+ }
+
+ switch (msg->mh.msg_id) {
+ case BFI_FLASH_I2H_QUERY_RSP:
+ status = be32_to_cpu(m.query->status);
+ bfa_trc(flash, status);
+ if (status == BFA_STATUS_OK) {
+ u32 i;
+ struct bfa_flash_attr_s *attr, *f;
+
+ attr = (struct bfa_flash_attr_s *) flash->ubuf;
+ f = (struct bfa_flash_attr_s *) flash->dbuf_kva;
+ attr->status = be32_to_cpu(f->status);
+ attr->npart = be32_to_cpu(f->npart);
+ bfa_trc(flash, attr->status);
+ bfa_trc(flash, attr->npart);
+ for (i = 0; i < attr->npart; i++) {
+ attr->part[i].part_type =
+ be32_to_cpu(f->part[i].part_type);
+ attr->part[i].part_instance =
+ be32_to_cpu(f->part[i].part_instance);
+ attr->part[i].part_off =
+ be32_to_cpu(f->part[i].part_off);
+ attr->part[i].part_size =
+ be32_to_cpu(f->part[i].part_size);
+ attr->part[i].part_len =
+ be32_to_cpu(f->part[i].part_len);
+ attr->part[i].part_status =
+ be32_to_cpu(f->part[i].part_status);
+ }
+ }
+ flash->status = status;
+ bfa_flash_cb(flash);
+ break;
+ case BFI_FLASH_I2H_ERASE_RSP:
+ status = be32_to_cpu(m.erase->status);
+ bfa_trc(flash, status);
+ flash->status = status;
+ bfa_flash_cb(flash);
+ break;
+ case BFI_FLASH_I2H_WRITE_RSP:
+ status = be32_to_cpu(m.write->status);
+ bfa_trc(flash, status);
+ if (status != BFA_STATUS_OK || flash->residue == 0) {
+ flash->status = status;
+ bfa_flash_cb(flash);
+ } else {
+ bfa_trc(flash, flash->offset);
+ bfa_flash_write_send(flash);
+ }
+ break;
+ case BFI_FLASH_I2H_READ_RSP:
+ status = be32_to_cpu(m.read->status);
+ bfa_trc(flash, status);
+ if (status != BFA_STATUS_OK) {
+ flash->status = status;
+ bfa_flash_cb(flash);
+ } else {
+ u32 len = be32_to_cpu(m.read->length);
+ bfa_trc(flash, flash->offset);
+ bfa_trc(flash, len);
+ memcpy(flash->ubuf + flash->offset,
+ flash->dbuf_kva, len);
+ flash->residue -= len;
+ flash->offset += len;
+ if (flash->residue == 0) {
+ flash->status = status;
+ bfa_flash_cb(flash);
+ } else
+ bfa_flash_read_send(flash);
+ }
+ break;
+ case BFI_FLASH_I2H_BOOT_VER_RSP:
+ break;
+ case BFI_FLASH_I2H_EVENT:
+ status = be32_to_cpu(m.event->status);
+ bfa_trc(flash, status);
+ if (status == BFA_STATUS_BAD_FWCFG)
+ bfa_ioc_aen_post(flash->ioc, BFA_IOC_AEN_FWCFG_ERROR);
+ else if (status == BFA_STATUS_INVALID_VENDOR) {
+ u32 param;
+ param = be32_to_cpu(m.event->param);
+ bfa_trc(flash, param);
+ bfa_ioc_aen_post(flash->ioc,
+ BFA_IOC_AEN_INVALID_VENDOR);
+ }
+ break;
+
+ default:
+ WARN_ON(1);
+ }
+}
+
+/*
+ * Flash memory info API.
+ *
+ * @param[in] mincfg - minimal cfg variable
+ */
+u32
+bfa_flash_meminfo(bfa_boolean_t mincfg)
+{
+ /* min driver doesn't need flash */
+ if (mincfg)
+ return 0;
+ return BFA_ROUNDUP(BFA_FLASH_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Flash attach API.
+ *
+ * @param[in] flash - flash structure
+ * @param[in] ioc - ioc structure
+ * @param[in] dev - device structure
+ * @param[in] trcmod - trace module
+ * @param[in] logmod - log module
+ */
+void
+bfa_flash_attach(struct bfa_flash_s *flash, struct bfa_ioc_s *ioc, void *dev,
+ struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg)
+{
+ flash->ioc = ioc;
+ flash->trcmod = trcmod;
+ flash->cbfn = NULL;
+ flash->cbarg = NULL;
+ flash->op_busy = 0;
+
+ bfa_ioc_mbox_regisr(flash->ioc, BFI_MC_FLASH, bfa_flash_intr, flash);
+ bfa_q_qe_init(&flash->ioc_notify);
+ bfa_ioc_notify_init(&flash->ioc_notify, bfa_flash_notify, flash);
+ list_add_tail(&flash->ioc_notify.qe, &flash->ioc->notify_q);
+
+ /* min driver doesn't need flash */
+ if (mincfg) {
+ flash->dbuf_kva = NULL;
+ flash->dbuf_pa = 0;
+ }
+}
+
+/*
+ * Claim memory for flash
+ *
+ * @param[in] flash - flash structure
+ * @param[in] dm_kva - pointer to virtual memory address
+ * @param[in] dm_pa - physical memory address
+ * @param[in] mincfg - minimal cfg variable
+ */
+void
+bfa_flash_memclaim(struct bfa_flash_s *flash, u8 *dm_kva, u64 dm_pa,
+ bfa_boolean_t mincfg)
+{
+ if (mincfg)
+ return;
+
+ flash->dbuf_kva = dm_kva;
+ flash->dbuf_pa = dm_pa;
+ memset(flash->dbuf_kva, 0, BFA_FLASH_DMA_BUF_SZ);
+ dm_kva += BFA_ROUNDUP(BFA_FLASH_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+ dm_pa += BFA_ROUNDUP(BFA_FLASH_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Get flash attribute.
+ *
+ * @param[in] flash - flash structure
+ * @param[in] attr - flash attribute structure
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_flash_get_attr(struct bfa_flash_s *flash, struct bfa_flash_attr_s *attr,
+ bfa_cb_flash_t cbfn, void *cbarg)
+{
+ bfa_trc(flash, BFI_FLASH_H2I_QUERY_REQ);
+
+ if (!bfa_ioc_is_operational(flash->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (flash->op_busy) {
+ bfa_trc(flash, flash->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ flash->op_busy = 1;
+ flash->cbfn = cbfn;
+ flash->cbarg = cbarg;
+ flash->ubuf = (u8 *) attr;
+ bfa_flash_query_send(flash);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Erase flash partition.
+ *
+ * @param[in] flash - flash structure
+ * @param[in] type - flash partition type
+ * @param[in] instance - flash partition instance
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_flash_erase_part(struct bfa_flash_s *flash, enum bfa_flash_part_type type,
+ u8 instance, bfa_cb_flash_t cbfn, void *cbarg)
+{
+ bfa_trc(flash, BFI_FLASH_H2I_ERASE_REQ);
+ bfa_trc(flash, type);
+ bfa_trc(flash, instance);
+
+ if (!bfa_ioc_is_operational(flash->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (flash->op_busy) {
+ bfa_trc(flash, flash->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ flash->op_busy = 1;
+ flash->cbfn = cbfn;
+ flash->cbarg = cbarg;
+ flash->type = type;
+ flash->instance = instance;
+
+ bfa_flash_erase_send(flash);
+ bfa_flash_aen_audit_post(flash->ioc, BFA_AUDIT_AEN_FLASH_ERASE,
+ instance, type);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Update flash partition.
+ *
+ * @param[in] flash - flash structure
+ * @param[in] type - flash partition type
+ * @param[in] instance - flash partition instance
+ * @param[in] buf - update data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to the partition starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_flash_update_part(struct bfa_flash_s *flash, enum bfa_flash_part_type type,
+ u8 instance, void *buf, u32 len, u32 offset,
+ bfa_cb_flash_t cbfn, void *cbarg)
+{
+ bfa_trc(flash, BFI_FLASH_H2I_WRITE_REQ);
+ bfa_trc(flash, type);
+ bfa_trc(flash, instance);
+ bfa_trc(flash, len);
+ bfa_trc(flash, offset);
+
+ if (!bfa_ioc_is_operational(flash->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /*
+ * 'len' must be in word (4-byte) boundary
+ * 'offset' must be in sector (16kb) boundary
+ */
+ if (!len || (len & 0x03) || (offset & 0x00003FFF))
+ return BFA_STATUS_FLASH_BAD_LEN;
+
+ if (type == BFA_FLASH_PART_MFG)
+ return BFA_STATUS_EINVAL;
+
+ if (flash->op_busy) {
+ bfa_trc(flash, flash->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ flash->op_busy = 1;
+ flash->cbfn = cbfn;
+ flash->cbarg = cbarg;
+ flash->type = type;
+ flash->instance = instance;
+ flash->residue = len;
+ flash->offset = 0;
+ flash->addr_off = offset;
+ flash->ubuf = buf;
+
+ bfa_flash_write_send(flash);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Read flash partition.
+ *
+ * @param[in] flash - flash structure
+ * @param[in] type - flash partition type
+ * @param[in] instance - flash partition instance
+ * @param[in] buf - read data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to the partition starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_flash_read_part(struct bfa_flash_s *flash, enum bfa_flash_part_type type,
+ u8 instance, void *buf, u32 len, u32 offset,
+ bfa_cb_flash_t cbfn, void *cbarg)
+{
+ bfa_trc(flash, BFI_FLASH_H2I_READ_REQ);
+ bfa_trc(flash, type);
+ bfa_trc(flash, instance);
+ bfa_trc(flash, len);
+ bfa_trc(flash, offset);
+
+ if (!bfa_ioc_is_operational(flash->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /*
+ * 'len' must be in word (4-byte) boundary
+ * 'offset' must be in sector (16kb) boundary
+ */
+ if (!len || (len & 0x03) || (offset & 0x00003FFF))
+ return BFA_STATUS_FLASH_BAD_LEN;
+
+ if (flash->op_busy) {
+ bfa_trc(flash, flash->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ flash->op_busy = 1;
+ flash->cbfn = cbfn;
+ flash->cbarg = cbarg;
+ flash->type = type;
+ flash->instance = instance;
+ flash->residue = len;
+ flash->offset = 0;
+ flash->addr_off = offset;
+ flash->ubuf = buf;
+ bfa_flash_read_send(flash);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * DIAG module specific
+ */
+
+#define BFA_DIAG_MEMTEST_TOV 50000 /* memtest timeout in msec */
+#define CT2_BFA_DIAG_MEMTEST_TOV (9*30*1000) /* 4.5 min */
+
+/* IOC event handler */
+static void
+bfa_diag_notify(void *diag_arg, enum bfa_ioc_event_e event)
+{
+ struct bfa_diag_s *diag = diag_arg;
+
+ bfa_trc(diag, event);
+ bfa_trc(diag, diag->block);
+ bfa_trc(diag, diag->fwping.lock);
+ bfa_trc(diag, diag->tsensor.lock);
+
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (diag->fwping.lock) {
+ diag->fwping.status = BFA_STATUS_IOC_FAILURE;
+ diag->fwping.cbfn(diag->fwping.cbarg,
+ diag->fwping.status);
+ diag->fwping.lock = 0;
+ }
+
+ if (diag->tsensor.lock) {
+ diag->tsensor.status = BFA_STATUS_IOC_FAILURE;
+ diag->tsensor.cbfn(diag->tsensor.cbarg,
+ diag->tsensor.status);
+ diag->tsensor.lock = 0;
+ }
+
+ if (diag->block) {
+ if (diag->timer_active) {
+ bfa_timer_stop(&diag->timer);
+ diag->timer_active = 0;
+ }
+
+ diag->status = BFA_STATUS_IOC_FAILURE;
+ diag->cbfn(diag->cbarg, diag->status);
+ diag->block = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+bfa_diag_memtest_done(void *cbarg)
+{
+ struct bfa_diag_s *diag = cbarg;
+ struct bfa_ioc_s *ioc = diag->ioc;
+ struct bfa_diag_memtest_result *res = diag->result;
+ u32 loff = BFI_BOOT_MEMTEST_RES_ADDR;
+ u32 pgnum, i;
+
+ pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, loff);
+ writel(pgnum, ioc->ioc_regs.host_page_num_fn);
+
+ for (i = 0; i < (sizeof(struct bfa_diag_memtest_result) /
+ sizeof(u32)); i++) {
+ /* read test result from smem */
+ *((u32 *) res + i) =
+ bfa_mem_read(ioc->ioc_regs.smem_page_start, loff);
+ loff += sizeof(u32);
+ }
+
+ /* Reset IOC fwstates to BFI_IOC_UNINIT */
+ bfa_ioc_reset_fwstate(ioc);
+
+ res->status = swab32(res->status);
+ bfa_trc(diag, res->status);
+
+ if (res->status == BFI_BOOT_MEMTEST_RES_SIG)
+ diag->status = BFA_STATUS_OK;
+ else {
+ diag->status = BFA_STATUS_MEMTEST_FAILED;
+ res->addr = swab32(res->addr);
+ res->exp = swab32(res->exp);
+ res->act = swab32(res->act);
+ res->err_status = swab32(res->err_status);
+ res->err_status1 = swab32(res->err_status1);
+ res->err_addr = swab32(res->err_addr);
+ bfa_trc(diag, res->addr);
+ bfa_trc(diag, res->exp);
+ bfa_trc(diag, res->act);
+ bfa_trc(diag, res->err_status);
+ bfa_trc(diag, res->err_status1);
+ bfa_trc(diag, res->err_addr);
+ }
+ diag->timer_active = 0;
+ diag->cbfn(diag->cbarg, diag->status);
+ diag->block = 0;
+}
+
+/*
+ * Firmware ping
+ */
+
+/*
+ * Perform DMA test directly
+ */
+static void
+diag_fwping_send(struct bfa_diag_s *diag)
+{
+ struct bfi_diag_fwping_req_s *fwping_req;
+ u32 i;
+
+ bfa_trc(diag, diag->fwping.dbuf_pa);
+
+ /* fill DMA area with pattern */
+ for (i = 0; i < (BFI_DIAG_DMA_BUF_SZ >> 2); i++)
+ *((u32 *)diag->fwping.dbuf_kva + i) = diag->fwping.data;
+
+ /* Fill mbox msg */
+ fwping_req = (struct bfi_diag_fwping_req_s *)diag->fwping.mbcmd.msg;
+
+ /* Setup SG list */
+ bfa_alen_set(&fwping_req->alen, BFI_DIAG_DMA_BUF_SZ,
+ diag->fwping.dbuf_pa);
+ /* Set up dma count */
+ fwping_req->count = cpu_to_be32(diag->fwping.count);
+ /* Set up data pattern */
+ fwping_req->data = diag->fwping.data;
+
+ /* build host command */
+ bfi_h2i_set(fwping_req->mh, BFI_MC_DIAG, BFI_DIAG_H2I_FWPING,
+ bfa_ioc_portid(diag->ioc));
+
+ /* send mbox cmd */
+ bfa_ioc_mbox_queue(diag->ioc, &diag->fwping.mbcmd);
+}
+
+static void
+diag_fwping_comp(struct bfa_diag_s *diag,
+ struct bfi_diag_fwping_rsp_s *diag_rsp)
+{
+ u32 rsp_data = diag_rsp->data;
+ u8 rsp_dma_status = diag_rsp->dma_status;
+
+ bfa_trc(diag, rsp_data);
+ bfa_trc(diag, rsp_dma_status);
+
+ if (rsp_dma_status == BFA_STATUS_OK) {
+ u32 i, pat;
+ pat = (diag->fwping.count & 0x1) ? ~(diag->fwping.data) :
+ diag->fwping.data;
+ /* Check mbox data */
+ if (diag->fwping.data != rsp_data) {
+ bfa_trc(diag, rsp_data);
+ diag->fwping.result->dmastatus =
+ BFA_STATUS_DATACORRUPTED;
+ diag->fwping.status = BFA_STATUS_DATACORRUPTED;
+ diag->fwping.cbfn(diag->fwping.cbarg,
+ diag->fwping.status);
+ diag->fwping.lock = 0;
+ return;
+ }
+ /* Check dma pattern */
+ for (i = 0; i < (BFI_DIAG_DMA_BUF_SZ >> 2); i++) {
+ if (*((u32 *)diag->fwping.dbuf_kva + i) != pat) {
+ bfa_trc(diag, i);
+ bfa_trc(diag, pat);
+ bfa_trc(diag,
+ *((u32 *)diag->fwping.dbuf_kva + i));
+ diag->fwping.result->dmastatus =
+ BFA_STATUS_DATACORRUPTED;
+ diag->fwping.status = BFA_STATUS_DATACORRUPTED;
+ diag->fwping.cbfn(diag->fwping.cbarg,
+ diag->fwping.status);
+ diag->fwping.lock = 0;
+ return;
+ }
+ }
+ diag->fwping.result->dmastatus = BFA_STATUS_OK;
+ diag->fwping.status = BFA_STATUS_OK;
+ diag->fwping.cbfn(diag->fwping.cbarg, diag->fwping.status);
+ diag->fwping.lock = 0;
+ } else {
+ diag->fwping.status = BFA_STATUS_HDMA_FAILED;
+ diag->fwping.cbfn(diag->fwping.cbarg, diag->fwping.status);
+ diag->fwping.lock = 0;
+ }
+}
+
+/*
+ * Temperature Sensor
+ */
+
+static void
+diag_tempsensor_send(struct bfa_diag_s *diag)
+{
+ struct bfi_diag_ts_req_s *msg;
+
+ msg = (struct bfi_diag_ts_req_s *)diag->tsensor.mbcmd.msg;
+ bfa_trc(diag, msg->temp);
+ /* build host command */
+ bfi_h2i_set(msg->mh, BFI_MC_DIAG, BFI_DIAG_H2I_TEMPSENSOR,
+ bfa_ioc_portid(diag->ioc));
+ /* send mbox cmd */
+ bfa_ioc_mbox_queue(diag->ioc, &diag->tsensor.mbcmd);
+}
+
+static void
+diag_tempsensor_comp(struct bfa_diag_s *diag, bfi_diag_ts_rsp_t *rsp)
+{
+ if (!diag->tsensor.lock) {
+ /* receiving response after ioc failure */
+ bfa_trc(diag, diag->tsensor.lock);
+ return;
+ }
+
+ /*
+ * ASIC junction tempsensor is a reg read operation
+ * it will always return OK
+ */
+ diag->tsensor.temp->temp = be16_to_cpu(rsp->temp);
+ diag->tsensor.temp->ts_junc = rsp->ts_junc;
+ diag->tsensor.temp->ts_brd = rsp->ts_brd;
+
+ if (rsp->ts_brd) {
+ /* tsensor.temp->status is brd_temp status */
+ diag->tsensor.temp->status = rsp->status;
+ if (rsp->status == BFA_STATUS_OK) {
+ diag->tsensor.temp->brd_temp =
+ be16_to_cpu(rsp->brd_temp);
+ } else
+ diag->tsensor.temp->brd_temp = 0;
+ }
+
+ bfa_trc(diag, rsp->status);
+ bfa_trc(diag, rsp->ts_junc);
+ bfa_trc(diag, rsp->temp);
+ bfa_trc(diag, rsp->ts_brd);
+ bfa_trc(diag, rsp->brd_temp);
+
+ /* tsensor status is always good bcos we always have junction temp */
+ diag->tsensor.status = BFA_STATUS_OK;
+ diag->tsensor.cbfn(diag->tsensor.cbarg, diag->tsensor.status);
+ diag->tsensor.lock = 0;
+}
+
+/*
+ * LED Test command
+ */
+static void
+diag_ledtest_send(struct bfa_diag_s *diag, struct bfa_diag_ledtest_s *ledtest)
+{
+ struct bfi_diag_ledtest_req_s *msg;
+
+ msg = (struct bfi_diag_ledtest_req_s *)diag->ledtest.mbcmd.msg;
+ /* build host command */
+ bfi_h2i_set(msg->mh, BFI_MC_DIAG, BFI_DIAG_H2I_LEDTEST,
+ bfa_ioc_portid(diag->ioc));
+
+ /*
+ * convert the freq from N blinks per 10 sec to
+ * crossbow ontime value. We do it here because division is need
+ */
+ if (ledtest->freq)
+ ledtest->freq = 500 / ledtest->freq;
+
+ if (ledtest->freq == 0)
+ ledtest->freq = 1;
+
+ bfa_trc(diag, ledtest->freq);
+ /* mcpy(&ledtest_req->req, ledtest, sizeof(bfa_diag_ledtest_t)); */
+ msg->cmd = (u8) ledtest->cmd;
+ msg->color = (u8) ledtest->color;
+ msg->portid = bfa_ioc_portid(diag->ioc);
+ msg->led = ledtest->led;
+ msg->freq = cpu_to_be16(ledtest->freq);
+
+ /* send mbox cmd */
+ bfa_ioc_mbox_queue(diag->ioc, &diag->ledtest.mbcmd);
+}
+
+static void
+diag_ledtest_comp(struct bfa_diag_s *diag, struct bfi_diag_ledtest_rsp_s *msg)
+{
+ bfa_trc(diag, diag->ledtest.lock);
+ diag->ledtest.lock = BFA_FALSE;
+ /* no bfa_cb_queue is needed because driver is not waiting */
+}
+
+/*
+ * Port beaconing
+ */
+static void
+diag_portbeacon_send(struct bfa_diag_s *diag, bfa_boolean_t beacon, u32 sec)
+{
+ struct bfi_diag_portbeacon_req_s *msg;
+
+ msg = (struct bfi_diag_portbeacon_req_s *)diag->beacon.mbcmd.msg;
+ /* build host command */
+ bfi_h2i_set(msg->mh, BFI_MC_DIAG, BFI_DIAG_H2I_PORTBEACON,
+ bfa_ioc_portid(diag->ioc));
+ msg->beacon = beacon;
+ msg->period = cpu_to_be32(sec);
+ /* send mbox cmd */
+ bfa_ioc_mbox_queue(diag->ioc, &diag->beacon.mbcmd);
+}
+
+static void
+diag_portbeacon_comp(struct bfa_diag_s *diag)
+{
+ bfa_trc(diag, diag->beacon.state);
+ diag->beacon.state = BFA_FALSE;
+ if (diag->cbfn_beacon)
+ diag->cbfn_beacon(diag->dev, BFA_FALSE, diag->beacon.link_e2e);
+}
+
+/*
+ * Diag hmbox handler
+ */
+static void
+bfa_diag_intr(void *diagarg, struct bfi_mbmsg_s *msg)
+{
+ struct bfa_diag_s *diag = diagarg;
+
+ switch (msg->mh.msg_id) {
+ case BFI_DIAG_I2H_PORTBEACON:
+ diag_portbeacon_comp(diag);
+ break;
+ case BFI_DIAG_I2H_FWPING:
+ diag_fwping_comp(diag, (struct bfi_diag_fwping_rsp_s *) msg);
+ break;
+ case BFI_DIAG_I2H_TEMPSENSOR:
+ diag_tempsensor_comp(diag, (bfi_diag_ts_rsp_t *) msg);
+ break;
+ case BFI_DIAG_I2H_LEDTEST:
+ diag_ledtest_comp(diag, (struct bfi_diag_ledtest_rsp_s *) msg);
+ break;
+ default:
+ bfa_trc(diag, msg->mh.msg_id);
+ WARN_ON(1);
+ }
+}
+
+/*
+ * Gen RAM Test
+ *
+ * @param[in] *diag - diag data struct
+ * @param[in] *memtest - mem test params input from upper layer,
+ * @param[in] pattern - mem test pattern
+ * @param[in] *result - mem test result
+ * @param[in] cbfn - mem test callback functioin
+ * @param[in] cbarg - callback functioin arg
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_diag_memtest(struct bfa_diag_s *diag, struct bfa_diag_memtest_s *memtest,
+ u32 pattern, struct bfa_diag_memtest_result *result,
+ bfa_cb_diag_t cbfn, void *cbarg)
+{
+ u32 memtest_tov;
+
+ bfa_trc(diag, pattern);
+
+ if (!bfa_ioc_adapter_is_disabled(diag->ioc))
+ return BFA_STATUS_ADAPTER_ENABLED;
+
+ /* check to see if there is another destructive diag cmd running */
+ if (diag->block) {
+ bfa_trc(diag, diag->block);
+ return BFA_STATUS_DEVBUSY;
+ } else
+ diag->block = 1;
+
+ diag->result = result;
+ diag->cbfn = cbfn;
+ diag->cbarg = cbarg;
+
+ /* download memtest code and take LPU0 out of reset */
+ bfa_ioc_boot(diag->ioc, BFI_FWBOOT_TYPE_MEMTEST, BFI_FWBOOT_ENV_OS);
+
+ memtest_tov = (bfa_ioc_asic_gen(diag->ioc) == BFI_ASIC_GEN_CT2) ?
+ CT2_BFA_DIAG_MEMTEST_TOV : BFA_DIAG_MEMTEST_TOV;
+ bfa_timer_begin(diag->ioc->timer_mod, &diag->timer,
+ bfa_diag_memtest_done, diag, memtest_tov);
+ diag->timer_active = 1;
+ return BFA_STATUS_OK;
+}
+
+/*
+ * DIAG firmware ping command
+ *
+ * @param[in] *diag - diag data struct
+ * @param[in] cnt - dma loop count for testing PCIE
+ * @param[in] data - data pattern to pass in fw
+ * @param[in] *result - pt to bfa_diag_fwping_result_t data struct
+ * @param[in] cbfn - callback function
+ * @param[in] *cbarg - callback functioin arg
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_diag_fwping(struct bfa_diag_s *diag, u32 cnt, u32 data,
+ struct bfa_diag_results_fwping *result, bfa_cb_diag_t cbfn,
+ void *cbarg)
+{
+ bfa_trc(diag, cnt);
+ bfa_trc(diag, data);
+
+ if (!bfa_ioc_is_operational(diag->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (bfa_asic_id_ct2(bfa_ioc_devid((diag->ioc))) &&
+ ((diag->ioc)->clscode == BFI_PCIFN_CLASS_ETH))
+ return BFA_STATUS_CMD_NOTSUPP;
+
+ /* check to see if there is another destructive diag cmd running */
+ if (diag->block || diag->fwping.lock) {
+ bfa_trc(diag, diag->block);
+ bfa_trc(diag, diag->fwping.lock);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ /* Initialization */
+ diag->fwping.lock = 1;
+ diag->fwping.cbfn = cbfn;
+ diag->fwping.cbarg = cbarg;
+ diag->fwping.result = result;
+ diag->fwping.data = data;
+ diag->fwping.count = cnt;
+
+ /* Init test results */
+ diag->fwping.result->data = 0;
+ diag->fwping.result->status = BFA_STATUS_OK;
+
+ /* kick off the first ping */
+ diag_fwping_send(diag);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Read Temperature Sensor
+ *
+ * @param[in] *diag - diag data struct
+ * @param[in] *result - pt to bfa_diag_temp_t data struct
+ * @param[in] cbfn - callback function
+ * @param[in] *cbarg - callback functioin arg
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_diag_tsensor_query(struct bfa_diag_s *diag,
+ struct bfa_diag_results_tempsensor_s *result,
+ bfa_cb_diag_t cbfn, void *cbarg)
+{
+ /* check to see if there is a destructive diag cmd running */
+ if (diag->block || diag->tsensor.lock) {
+ bfa_trc(diag, diag->block);
+ bfa_trc(diag, diag->tsensor.lock);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ if (!bfa_ioc_is_operational(diag->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* Init diag mod params */
+ diag->tsensor.lock = 1;
+ diag->tsensor.temp = result;
+ diag->tsensor.cbfn = cbfn;
+ diag->tsensor.cbarg = cbarg;
+ diag->tsensor.status = BFA_STATUS_OK;
+
+ /* Send msg to fw */
+ diag_tempsensor_send(diag);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * LED Test command
+ *
+ * @param[in] *diag - diag data struct
+ * @param[in] *ledtest - pt to ledtest data structure
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_diag_ledtest(struct bfa_diag_s *diag, struct bfa_diag_ledtest_s *ledtest)
+{
+ bfa_trc(diag, ledtest->cmd);
+
+ if (!bfa_ioc_is_operational(diag->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (diag->beacon.state)
+ return BFA_STATUS_BEACON_ON;
+
+ if (diag->ledtest.lock)
+ return BFA_STATUS_LEDTEST_OP;
+
+ /* Send msg to fw */
+ diag->ledtest.lock = BFA_TRUE;
+ diag_ledtest_send(diag, ledtest);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Port beaconing command
+ *
+ * @param[in] *diag - diag data struct
+ * @param[in] beacon - port beaconing 1:ON 0:OFF
+ * @param[in] link_e2e_beacon - link beaconing 1:ON 0:OFF
+ * @param[in] sec - beaconing duration in seconds
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_diag_beacon_port(struct bfa_diag_s *diag, bfa_boolean_t beacon,
+ bfa_boolean_t link_e2e_beacon, uint32_t sec)
+{
+ bfa_trc(diag, beacon);
+ bfa_trc(diag, link_e2e_beacon);
+ bfa_trc(diag, sec);
+
+ if (!bfa_ioc_is_operational(diag->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (diag->ledtest.lock)
+ return BFA_STATUS_LEDTEST_OP;
+
+ if (diag->beacon.state && beacon) /* beacon alread on */
+ return BFA_STATUS_BEACON_ON;
+
+ diag->beacon.state = beacon;
+ diag->beacon.link_e2e = link_e2e_beacon;
+ if (diag->cbfn_beacon)
+ diag->cbfn_beacon(diag->dev, beacon, link_e2e_beacon);
+
+ /* Send msg to fw */
+ diag_portbeacon_send(diag, beacon, sec);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Return DMA memory needed by diag module.
+ */
+u32
+bfa_diag_meminfo(void)
+{
+ return BFA_ROUNDUP(BFI_DIAG_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Attach virtual and physical memory for Diag.
+ */
+void
+bfa_diag_attach(struct bfa_diag_s *diag, struct bfa_ioc_s *ioc, void *dev,
+ bfa_cb_diag_beacon_t cbfn_beacon, struct bfa_trc_mod_s *trcmod)
+{
+ diag->dev = dev;
+ diag->ioc = ioc;
+ diag->trcmod = trcmod;
+
+ diag->block = 0;
+ diag->cbfn = NULL;
+ diag->cbarg = NULL;
+ diag->result = NULL;
+ diag->cbfn_beacon = cbfn_beacon;
+
+ bfa_ioc_mbox_regisr(diag->ioc, BFI_MC_DIAG, bfa_diag_intr, diag);
+ bfa_q_qe_init(&diag->ioc_notify);
+ bfa_ioc_notify_init(&diag->ioc_notify, bfa_diag_notify, diag);
+ list_add_tail(&diag->ioc_notify.qe, &diag->ioc->notify_q);
+}
+
+void
+bfa_diag_memclaim(struct bfa_diag_s *diag, u8 *dm_kva, u64 dm_pa)
+{
+ diag->fwping.dbuf_kva = dm_kva;
+ diag->fwping.dbuf_pa = dm_pa;
+ memset(diag->fwping.dbuf_kva, 0, BFI_DIAG_DMA_BUF_SZ);
+}
+
+/*
+ * PHY module specific
+ */
+#define BFA_PHY_DMA_BUF_SZ 0x02000 /* 8k dma buffer */
+#define BFA_PHY_LOCK_STATUS 0x018878 /* phy semaphore status reg */
+
+static void
+bfa_phy_ntoh32(u32 *obuf, u32 *ibuf, int sz)
+{
+ int i, m = sz >> 2;
+
+ for (i = 0; i < m; i++)
+ obuf[i] = be32_to_cpu(ibuf[i]);
+}
+
+static bfa_boolean_t
+bfa_phy_present(struct bfa_phy_s *phy)
+{
+ return (phy->ioc->attr->card_type == BFA_MFG_TYPE_LIGHTNING);
+}
+
+static void
+bfa_phy_notify(void *cbarg, enum bfa_ioc_event_e event)
+{
+ struct bfa_phy_s *phy = cbarg;
+
+ bfa_trc(phy, event);
+
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (phy->op_busy) {
+ phy->status = BFA_STATUS_IOC_FAILURE;
+ phy->cbfn(phy->cbarg, phy->status);
+ phy->op_busy = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Send phy attribute query request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_phy_query_send(void *cbarg)
+{
+ struct bfa_phy_s *phy = cbarg;
+ struct bfi_phy_query_req_s *msg =
+ (struct bfi_phy_query_req_s *) phy->mb.msg;
+
+ msg->instance = phy->instance;
+ bfi_h2i_set(msg->mh, BFI_MC_PHY, BFI_PHY_H2I_QUERY_REQ,
+ bfa_ioc_portid(phy->ioc));
+ bfa_alen_set(&msg->alen, sizeof(struct bfa_phy_attr_s), phy->dbuf_pa);
+ bfa_ioc_mbox_queue(phy->ioc, &phy->mb);
+}
+
+/*
+ * Send phy write request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_phy_write_send(void *cbarg)
+{
+ struct bfa_phy_s *phy = cbarg;
+ struct bfi_phy_write_req_s *msg =
+ (struct bfi_phy_write_req_s *) phy->mb.msg;
+ u32 len;
+ u16 *buf, *dbuf;
+ int i, sz;
+
+ msg->instance = phy->instance;
+ msg->offset = cpu_to_be32(phy->addr_off + phy->offset);
+ len = (phy->residue < BFA_PHY_DMA_BUF_SZ) ?
+ phy->residue : BFA_PHY_DMA_BUF_SZ;
+ msg->length = cpu_to_be32(len);
+
+ /* indicate if it's the last msg of the whole write operation */
+ msg->last = (len == phy->residue) ? 1 : 0;
+
+ bfi_h2i_set(msg->mh, BFI_MC_PHY, BFI_PHY_H2I_WRITE_REQ,
+ bfa_ioc_portid(phy->ioc));
+ bfa_alen_set(&msg->alen, len, phy->dbuf_pa);
+
+ buf = (u16 *) (phy->ubuf + phy->offset);
+ dbuf = (u16 *)phy->dbuf_kva;
+ sz = len >> 1;
+ for (i = 0; i < sz; i++)
+ buf[i] = cpu_to_be16(dbuf[i]);
+
+ bfa_ioc_mbox_queue(phy->ioc, &phy->mb);
+
+ phy->residue -= len;
+ phy->offset += len;
+}
+
+/*
+ * Send phy read request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_phy_read_send(void *cbarg)
+{
+ struct bfa_phy_s *phy = cbarg;
+ struct bfi_phy_read_req_s *msg =
+ (struct bfi_phy_read_req_s *) phy->mb.msg;
+ u32 len;
+
+ msg->instance = phy->instance;
+ msg->offset = cpu_to_be32(phy->addr_off + phy->offset);
+ len = (phy->residue < BFA_PHY_DMA_BUF_SZ) ?
+ phy->residue : BFA_PHY_DMA_BUF_SZ;
+ msg->length = cpu_to_be32(len);
+ bfi_h2i_set(msg->mh, BFI_MC_PHY, BFI_PHY_H2I_READ_REQ,
+ bfa_ioc_portid(phy->ioc));
+ bfa_alen_set(&msg->alen, len, phy->dbuf_pa);
+ bfa_ioc_mbox_queue(phy->ioc, &phy->mb);
+}
+
+/*
+ * Send phy stats request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_phy_stats_send(void *cbarg)
+{
+ struct bfa_phy_s *phy = cbarg;
+ struct bfi_phy_stats_req_s *msg =
+ (struct bfi_phy_stats_req_s *) phy->mb.msg;
+
+ msg->instance = phy->instance;
+ bfi_h2i_set(msg->mh, BFI_MC_PHY, BFI_PHY_H2I_STATS_REQ,
+ bfa_ioc_portid(phy->ioc));
+ bfa_alen_set(&msg->alen, sizeof(struct bfa_phy_stats_s), phy->dbuf_pa);
+ bfa_ioc_mbox_queue(phy->ioc, &phy->mb);
+}
+
+/*
+ * Flash memory info API.
+ *
+ * @param[in] mincfg - minimal cfg variable
+ */
+u32
+bfa_phy_meminfo(bfa_boolean_t mincfg)
+{
+ /* min driver doesn't need phy */
+ if (mincfg)
+ return 0;
+
+ return BFA_ROUNDUP(BFA_PHY_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Flash attach API.
+ *
+ * @param[in] phy - phy structure
+ * @param[in] ioc - ioc structure
+ * @param[in] dev - device structure
+ * @param[in] trcmod - trace module
+ * @param[in] logmod - log module
+ */
+void
+bfa_phy_attach(struct bfa_phy_s *phy, struct bfa_ioc_s *ioc, void *dev,
+ struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg)
+{
+ phy->ioc = ioc;
+ phy->trcmod = trcmod;
+ phy->cbfn = NULL;
+ phy->cbarg = NULL;
+ phy->op_busy = 0;
+
+ bfa_ioc_mbox_regisr(phy->ioc, BFI_MC_PHY, bfa_phy_intr, phy);
+ bfa_q_qe_init(&phy->ioc_notify);
+ bfa_ioc_notify_init(&phy->ioc_notify, bfa_phy_notify, phy);
+ list_add_tail(&phy->ioc_notify.qe, &phy->ioc->notify_q);
+
+ /* min driver doesn't need phy */
+ if (mincfg) {
+ phy->dbuf_kva = NULL;
+ phy->dbuf_pa = 0;
+ }
+}
+
+/*
+ * Claim memory for phy
+ *
+ * @param[in] phy - phy structure
+ * @param[in] dm_kva - pointer to virtual memory address
+ * @param[in] dm_pa - physical memory address
+ * @param[in] mincfg - minimal cfg variable
+ */
+void
+bfa_phy_memclaim(struct bfa_phy_s *phy, u8 *dm_kva, u64 dm_pa,
+ bfa_boolean_t mincfg)
+{
+ if (mincfg)
+ return;
+
+ phy->dbuf_kva = dm_kva;
+ phy->dbuf_pa = dm_pa;
+ memset(phy->dbuf_kva, 0, BFA_PHY_DMA_BUF_SZ);
+ dm_kva += BFA_ROUNDUP(BFA_PHY_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+ dm_pa += BFA_ROUNDUP(BFA_PHY_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+}
+
+bfa_boolean_t
+bfa_phy_busy(struct bfa_ioc_s *ioc)
+{
+ void __iomem *rb;
+
+ rb = bfa_ioc_bar0(ioc);
+ return readl(rb + BFA_PHY_LOCK_STATUS);
+}
+
+/*
+ * Get phy attribute.
+ *
+ * @param[in] phy - phy structure
+ * @param[in] attr - phy attribute structure
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_phy_get_attr(struct bfa_phy_s *phy, u8 instance,
+ struct bfa_phy_attr_s *attr, bfa_cb_phy_t cbfn, void *cbarg)
+{
+ bfa_trc(phy, BFI_PHY_H2I_QUERY_REQ);
+ bfa_trc(phy, instance);
+
+ if (!bfa_phy_present(phy))
+ return BFA_STATUS_PHY_NOT_PRESENT;
+
+ if (!bfa_ioc_is_operational(phy->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (phy->op_busy || bfa_phy_busy(phy->ioc)) {
+ bfa_trc(phy, phy->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ phy->op_busy = 1;
+ phy->cbfn = cbfn;
+ phy->cbarg = cbarg;
+ phy->instance = instance;
+ phy->ubuf = (uint8_t *) attr;
+ bfa_phy_query_send(phy);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Get phy stats.
+ *
+ * @param[in] phy - phy structure
+ * @param[in] instance - phy image instance
+ * @param[in] stats - pointer to phy stats
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_phy_get_stats(struct bfa_phy_s *phy, u8 instance,
+ struct bfa_phy_stats_s *stats,
+ bfa_cb_phy_t cbfn, void *cbarg)
+{
+ bfa_trc(phy, BFI_PHY_H2I_STATS_REQ);
+ bfa_trc(phy, instance);
+
+ if (!bfa_phy_present(phy))
+ return BFA_STATUS_PHY_NOT_PRESENT;
+
+ if (!bfa_ioc_is_operational(phy->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (phy->op_busy || bfa_phy_busy(phy->ioc)) {
+ bfa_trc(phy, phy->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ phy->op_busy = 1;
+ phy->cbfn = cbfn;
+ phy->cbarg = cbarg;
+ phy->instance = instance;
+ phy->ubuf = (u8 *) stats;
+ bfa_phy_stats_send(phy);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Update phy image.
+ *
+ * @param[in] phy - phy structure
+ * @param[in] instance - phy image instance
+ * @param[in] buf - update data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_phy_update(struct bfa_phy_s *phy, u8 instance,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_phy_t cbfn, void *cbarg)
+{
+ bfa_trc(phy, BFI_PHY_H2I_WRITE_REQ);
+ bfa_trc(phy, instance);
+ bfa_trc(phy, len);
+ bfa_trc(phy, offset);
+
+ if (!bfa_phy_present(phy))
+ return BFA_STATUS_PHY_NOT_PRESENT;
+
+ if (!bfa_ioc_is_operational(phy->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* 'len' must be in word (4-byte) boundary */
+ if (!len || (len & 0x03))
+ return BFA_STATUS_FAILED;
+
+ if (phy->op_busy || bfa_phy_busy(phy->ioc)) {
+ bfa_trc(phy, phy->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ phy->op_busy = 1;
+ phy->cbfn = cbfn;
+ phy->cbarg = cbarg;
+ phy->instance = instance;
+ phy->residue = len;
+ phy->offset = 0;
+ phy->addr_off = offset;
+ phy->ubuf = buf;
+
+ bfa_phy_write_send(phy);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Read phy image.
+ *
+ * @param[in] phy - phy structure
+ * @param[in] instance - phy image instance
+ * @param[in] buf - read data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_phy_read(struct bfa_phy_s *phy, u8 instance,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_phy_t cbfn, void *cbarg)
+{
+ bfa_trc(phy, BFI_PHY_H2I_READ_REQ);
+ bfa_trc(phy, instance);
+ bfa_trc(phy, len);
+ bfa_trc(phy, offset);
+
+ if (!bfa_phy_present(phy))
+ return BFA_STATUS_PHY_NOT_PRESENT;
+
+ if (!bfa_ioc_is_operational(phy->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* 'len' must be in word (4-byte) boundary */
+ if (!len || (len & 0x03))
+ return BFA_STATUS_FAILED;
+
+ if (phy->op_busy || bfa_phy_busy(phy->ioc)) {
+ bfa_trc(phy, phy->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ phy->op_busy = 1;
+ phy->cbfn = cbfn;
+ phy->cbarg = cbarg;
+ phy->instance = instance;
+ phy->residue = len;
+ phy->offset = 0;
+ phy->addr_off = offset;
+ phy->ubuf = buf;
+ bfa_phy_read_send(phy);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Process phy response messages upon receiving interrupts.
+ *
+ * @param[in] phyarg - phy structure
+ * @param[in] msg - message structure
+ */
+void
+bfa_phy_intr(void *phyarg, struct bfi_mbmsg_s *msg)
+{
+ struct bfa_phy_s *phy = phyarg;
+ u32 status;
+
+ union {
+ struct bfi_phy_query_rsp_s *query;
+ struct bfi_phy_stats_rsp_s *stats;
+ struct bfi_phy_write_rsp_s *write;
+ struct bfi_phy_read_rsp_s *read;
+ struct bfi_mbmsg_s *msg;
+ } m;
+
+ m.msg = msg;
+ bfa_trc(phy, msg->mh.msg_id);
+
+ if (!phy->op_busy) {
+ /* receiving response after ioc failure */
+ bfa_trc(phy, 0x9999);
+ return;
+ }
+
+ switch (msg->mh.msg_id) {
+ case BFI_PHY_I2H_QUERY_RSP:
+ status = be32_to_cpu(m.query->status);
+ bfa_trc(phy, status);
+
+ if (status == BFA_STATUS_OK) {
+ struct bfa_phy_attr_s *attr =
+ (struct bfa_phy_attr_s *) phy->ubuf;
+ bfa_phy_ntoh32((u32 *)attr, (u32 *)phy->dbuf_kva,
+ sizeof(struct bfa_phy_attr_s));
+ bfa_trc(phy, attr->status);
+ bfa_trc(phy, attr->length);
+ }
+
+ phy->status = status;
+ phy->op_busy = 0;
+ if (phy->cbfn)
+ phy->cbfn(phy->cbarg, phy->status);
+ break;
+ case BFI_PHY_I2H_STATS_RSP:
+ status = be32_to_cpu(m.stats->status);
+ bfa_trc(phy, status);
+
+ if (status == BFA_STATUS_OK) {
+ struct bfa_phy_stats_s *stats =
+ (struct bfa_phy_stats_s *) phy->ubuf;
+ bfa_phy_ntoh32((u32 *)stats, (u32 *)phy->dbuf_kva,
+ sizeof(struct bfa_phy_stats_s));
+ bfa_trc(phy, stats->status);
+ }
+
+ phy->status = status;
+ phy->op_busy = 0;
+ if (phy->cbfn)
+ phy->cbfn(phy->cbarg, phy->status);
+ break;
+ case BFI_PHY_I2H_WRITE_RSP:
+ status = be32_to_cpu(m.write->status);
+ bfa_trc(phy, status);
+
+ if (status != BFA_STATUS_OK || phy->residue == 0) {
+ phy->status = status;
+ phy->op_busy = 0;
+ if (phy->cbfn)
+ phy->cbfn(phy->cbarg, phy->status);
+ } else {
+ bfa_trc(phy, phy->offset);
+ bfa_phy_write_send(phy);
+ }
+ break;
+ case BFI_PHY_I2H_READ_RSP:
+ status = be32_to_cpu(m.read->status);
+ bfa_trc(phy, status);
+
+ if (status != BFA_STATUS_OK) {
+ phy->status = status;
+ phy->op_busy = 0;
+ if (phy->cbfn)
+ phy->cbfn(phy->cbarg, phy->status);
+ } else {
+ u32 len = be32_to_cpu(m.read->length);
+ u16 *buf = (u16 *)(phy->ubuf + phy->offset);
+ u16 *dbuf = (u16 *)phy->dbuf_kva;
+ int i, sz = len >> 1;
+
+ bfa_trc(phy, phy->offset);
+ bfa_trc(phy, len);
+
+ for (i = 0; i < sz; i++)
+ buf[i] = be16_to_cpu(dbuf[i]);
+
+ phy->residue -= len;
+ phy->offset += len;
+
+ if (phy->residue == 0) {
+ phy->status = status;
+ phy->op_busy = 0;
+ if (phy->cbfn)
+ phy->cbfn(phy->cbarg, phy->status);
+ } else
+ bfa_phy_read_send(phy);
+ }
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+/* forward declaration of DCONF state machine */
+static void bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_dirty(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_sync(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+static void bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event);
+
+static void bfa_dconf_cbfn(void *dconf, bfa_status_t status);
+static void bfa_dconf_timer(void *cbarg);
+static bfa_status_t bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf);
+static void bfa_dconf_init_cb(void *arg, bfa_status_t status);
+
+/*
+ * Beginning state of dconf module. Waiting for an event to start.
+ */
+static void
+bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_status_t bfa_status;
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_INIT:
+ if (dconf->min_cfg) {
+ bfa_trc(dconf->bfa, dconf->min_cfg);
+ bfa_fsm_send_event(&dconf->bfa->iocfc,
+ IOCFC_E_DCONF_DONE);
+ return;
+ }
+ bfa_sm_set_state(dconf, bfa_dconf_sm_flash_read);
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, 2 * BFA_DCONF_UPDATE_TOV);
+ bfa_status = bfa_flash_read_part(BFA_FLASH(dconf->bfa),
+ BFA_FLASH_PART_DRV, dconf->instance,
+ dconf->dconf,
+ sizeof(struct bfa_dconf_s), 0,
+ bfa_dconf_init_cb, dconf->bfa);
+ if (bfa_status != BFA_STATUS_OK) {
+ bfa_timer_stop(&dconf->timer);
+ bfa_dconf_init_cb(dconf->bfa, BFA_STATUS_FAILED);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ return;
+ }
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ case BFA_DCONF_SM_WR:
+ case BFA_DCONF_SM_FLASH_COMP:
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * Read flash for dconf entries and make a call back to the driver once done.
+ */
+static void
+bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_FLASH_COMP:
+ bfa_timer_stop(&dconf->timer);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+ break;
+ case BFA_DCONF_SM_TIMEOUT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+ bfa_ioc_suspend(&dconf->bfa->ioc);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_timer_stop(&dconf->timer);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ bfa_timer_stop(&dconf->timer);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * DCONF Module is in ready state. Has completed the initialization.
+ */
+static void
+bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_WR:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
+ break;
+ case BFA_DCONF_SM_INIT:
+ case BFA_DCONF_SM_IOCDISABLE:
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * entries are dirty, write back to the flash.
+ */
+
+static void
+bfa_dconf_sm_dirty(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_TIMEOUT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_sync);
+ bfa_dconf_flash_write(dconf);
+ break;
+ case BFA_DCONF_SM_WR:
+ bfa_timer_stop(&dconf->timer);
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_timer_stop(&dconf->timer);
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_final_sync);
+ bfa_dconf_flash_write(dconf);
+ break;
+ case BFA_DCONF_SM_FLASH_COMP:
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ bfa_timer_stop(&dconf->timer);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_iocdown_dirty);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * Sync the dconf entries to the flash.
+ */
+static void
+bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_IOCDISABLE:
+ case BFA_DCONF_SM_FLASH_COMP:
+ bfa_timer_stop(&dconf->timer);
+ fallthrough;
+ case BFA_DCONF_SM_TIMEOUT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+static void
+bfa_dconf_sm_sync(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_FLASH_COMP:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+ break;
+ case BFA_DCONF_SM_WR:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_final_sync);
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_iocdown_dirty);
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+static void
+bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
+ enum bfa_dconf_event event)
+{
+ bfa_trc(dconf->bfa, event);
+
+ switch (event) {
+ case BFA_DCONF_SM_INIT:
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
+ break;
+ case BFA_DCONF_SM_EXIT:
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
+ break;
+ case BFA_DCONF_SM_IOCDISABLE:
+ break;
+ default:
+ bfa_sm_fault(dconf->bfa, event);
+ }
+}
+
+/*
+ * Compute and return memory needed by DRV_CFG module.
+ */
+void
+bfa_dconf_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo,
+ struct bfa_s *bfa)
+{
+ struct bfa_mem_kva_s *dconf_kva = BFA_MEM_DCONF_KVA(bfa);
+
+ if (cfg->drvcfg.min_cfg)
+ bfa_mem_kva_setup(meminfo, dconf_kva,
+ sizeof(struct bfa_dconf_hdr_s));
+ else
+ bfa_mem_kva_setup(meminfo, dconf_kva,
+ sizeof(struct bfa_dconf_s));
+}
+
+void
+bfa_dconf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+
+ dconf->bfad = bfad;
+ dconf->bfa = bfa;
+ dconf->instance = bfa->ioc.port_id;
+ bfa_trc(bfa, dconf->instance);
+
+ dconf->dconf = (struct bfa_dconf_s *) bfa_mem_kva_curp(dconf);
+ if (cfg->drvcfg.min_cfg) {
+ bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_hdr_s);
+ dconf->min_cfg = BFA_TRUE;
+ } else {
+ dconf->min_cfg = BFA_FALSE;
+ bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_s);
+ }
+
+ bfa_dconf_read_data_valid(bfa) = BFA_FALSE;
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+}
+
+static void
+bfa_dconf_init_cb(void *arg, bfa_status_t status)
+{
+ struct bfa_s *bfa = arg;
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+
+ if (status == BFA_STATUS_OK) {
+ bfa_dconf_read_data_valid(bfa) = BFA_TRUE;
+ if (dconf->dconf->hdr.signature != BFI_DCONF_SIGNATURE)
+ dconf->dconf->hdr.signature = BFI_DCONF_SIGNATURE;
+ if (dconf->dconf->hdr.version != BFI_DCONF_VERSION)
+ dconf->dconf->hdr.version = BFI_DCONF_VERSION;
+ }
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_DCONF_DONE);
+}
+
+void
+bfa_dconf_modinit(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_INIT);
+}
+
+static void bfa_dconf_timer(void *cbarg)
+{
+ struct bfa_dconf_mod_s *dconf = cbarg;
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_TIMEOUT);
+}
+
+void
+bfa_dconf_iocdisable(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_IOCDISABLE);
+}
+
+static bfa_status_t
+bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf)
+{
+ bfa_status_t bfa_status;
+ bfa_trc(dconf->bfa, 0);
+
+ bfa_status = bfa_flash_update_part(BFA_FLASH(dconf->bfa),
+ BFA_FLASH_PART_DRV, dconf->instance,
+ dconf->dconf, sizeof(struct bfa_dconf_s), 0,
+ bfa_dconf_cbfn, dconf);
+ if (bfa_status != BFA_STATUS_OK)
+ WARN_ON(bfa_status);
+ bfa_trc(dconf->bfa, bfa_status);
+
+ return bfa_status;
+}
+
+bfa_status_t
+bfa_dconf_update(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ bfa_trc(dconf->bfa, 0);
+ if (bfa_sm_cmp_state(dconf, bfa_dconf_sm_iocdown_dirty))
+ return BFA_STATUS_FAILED;
+
+ if (dconf->min_cfg) {
+ bfa_trc(dconf->bfa, dconf->min_cfg);
+ return BFA_STATUS_FAILED;
+ }
+
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_WR);
+ return BFA_STATUS_OK;
+}
+
+static void
+bfa_dconf_cbfn(void *arg, bfa_status_t status)
+{
+ struct bfa_dconf_mod_s *dconf = arg;
+ WARN_ON(status);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+}
+
+void
+bfa_dconf_modexit(struct bfa_s *bfa)
+{
+ struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_EXIT);
+}
+
+/*
+ * FRU specific functions
+ */
+
+#define BFA_FRU_DMA_BUF_SZ 0x02000 /* 8k dma buffer */
+#define BFA_FRU_CHINOOK_MAX_SIZE 0x10000
+#define BFA_FRU_LIGHTNING_MAX_SIZE 0x200
+
+static void
+bfa_fru_notify(void *cbarg, enum bfa_ioc_event_e event)
+{
+ struct bfa_fru_s *fru = cbarg;
+
+ bfa_trc(fru, event);
+
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (fru->op_busy) {
+ fru->status = BFA_STATUS_IOC_FAILURE;
+ fru->cbfn(fru->cbarg, fru->status);
+ fru->op_busy = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Send fru write request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_fru_write_send(void *cbarg, enum bfi_fru_h2i_msgs msg_type)
+{
+ struct bfa_fru_s *fru = cbarg;
+ struct bfi_fru_write_req_s *msg =
+ (struct bfi_fru_write_req_s *) fru->mb.msg;
+ u32 len;
+
+ msg->offset = cpu_to_be32(fru->addr_off + fru->offset);
+ len = (fru->residue < BFA_FRU_DMA_BUF_SZ) ?
+ fru->residue : BFA_FRU_DMA_BUF_SZ;
+ msg->length = cpu_to_be32(len);
+
+ /*
+ * indicate if it's the last msg of the whole write operation
+ */
+ msg->last = (len == fru->residue) ? 1 : 0;
+
+ msg->trfr_cmpl = (len == fru->residue) ? fru->trfr_cmpl : 0;
+ bfi_h2i_set(msg->mh, BFI_MC_FRU, msg_type, bfa_ioc_portid(fru->ioc));
+ bfa_alen_set(&msg->alen, len, fru->dbuf_pa);
+
+ memcpy(fru->dbuf_kva, fru->ubuf + fru->offset, len);
+ bfa_ioc_mbox_queue(fru->ioc, &fru->mb);
+
+ fru->residue -= len;
+ fru->offset += len;
+}
+
+/*
+ * Send fru read request.
+ *
+ * @param[in] cbarg - callback argument
+ */
+static void
+bfa_fru_read_send(void *cbarg, enum bfi_fru_h2i_msgs msg_type)
+{
+ struct bfa_fru_s *fru = cbarg;
+ struct bfi_fru_read_req_s *msg =
+ (struct bfi_fru_read_req_s *) fru->mb.msg;
+ u32 len;
+
+ msg->offset = cpu_to_be32(fru->addr_off + fru->offset);
+ len = (fru->residue < BFA_FRU_DMA_BUF_SZ) ?
+ fru->residue : BFA_FRU_DMA_BUF_SZ;
+ msg->length = cpu_to_be32(len);
+ bfi_h2i_set(msg->mh, BFI_MC_FRU, msg_type, bfa_ioc_portid(fru->ioc));
+ bfa_alen_set(&msg->alen, len, fru->dbuf_pa);
+ bfa_ioc_mbox_queue(fru->ioc, &fru->mb);
+}
+
+/*
+ * Flash memory info API.
+ *
+ * @param[in] mincfg - minimal cfg variable
+ */
+u32
+bfa_fru_meminfo(bfa_boolean_t mincfg)
+{
+ /* min driver doesn't need fru */
+ if (mincfg)
+ return 0;
+
+ return BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Flash attach API.
+ *
+ * @param[in] fru - fru structure
+ * @param[in] ioc - ioc structure
+ * @param[in] dev - device structure
+ * @param[in] trcmod - trace module
+ * @param[in] logmod - log module
+ */
+void
+bfa_fru_attach(struct bfa_fru_s *fru, struct bfa_ioc_s *ioc, void *dev,
+ struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg)
+{
+ fru->ioc = ioc;
+ fru->trcmod = trcmod;
+ fru->cbfn = NULL;
+ fru->cbarg = NULL;
+ fru->op_busy = 0;
+
+ bfa_ioc_mbox_regisr(fru->ioc, BFI_MC_FRU, bfa_fru_intr, fru);
+ bfa_q_qe_init(&fru->ioc_notify);
+ bfa_ioc_notify_init(&fru->ioc_notify, bfa_fru_notify, fru);
+ list_add_tail(&fru->ioc_notify.qe, &fru->ioc->notify_q);
+
+ /* min driver doesn't need fru */
+ if (mincfg) {
+ fru->dbuf_kva = NULL;
+ fru->dbuf_pa = 0;
+ }
+}
+
+/*
+ * Claim memory for fru
+ *
+ * @param[in] fru - fru structure
+ * @param[in] dm_kva - pointer to virtual memory address
+ * @param[in] dm_pa - frusical memory address
+ * @param[in] mincfg - minimal cfg variable
+ */
+void
+bfa_fru_memclaim(struct bfa_fru_s *fru, u8 *dm_kva, u64 dm_pa,
+ bfa_boolean_t mincfg)
+{
+ if (mincfg)
+ return;
+
+ fru->dbuf_kva = dm_kva;
+ fru->dbuf_pa = dm_pa;
+ memset(fru->dbuf_kva, 0, BFA_FRU_DMA_BUF_SZ);
+ dm_kva += BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+ dm_pa += BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * Update fru vpd image.
+ *
+ * @param[in] fru - fru structure
+ * @param[in] buf - update data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_fruvpd_update(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg, u8 trfr_cmpl)
+{
+ bfa_trc(fru, BFI_FRUVPD_H2I_WRITE_REQ);
+ bfa_trc(fru, len);
+ bfa_trc(fru, offset);
+
+ if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2 &&
+ fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK2)
+ return BFA_STATUS_FRU_NOT_PRESENT;
+
+ if (fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK)
+ return BFA_STATUS_CMD_NOTSUPP;
+
+ if (!bfa_ioc_is_operational(fru->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (fru->op_busy) {
+ bfa_trc(fru, fru->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ fru->op_busy = 1;
+
+ fru->cbfn = cbfn;
+ fru->cbarg = cbarg;
+ fru->residue = len;
+ fru->offset = 0;
+ fru->addr_off = offset;
+ fru->ubuf = buf;
+ fru->trfr_cmpl = trfr_cmpl;
+
+ bfa_fru_write_send(fru, BFI_FRUVPD_H2I_WRITE_REQ);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Read fru vpd image.
+ *
+ * @param[in] fru - fru structure
+ * @param[in] buf - read data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_fruvpd_read(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg)
+{
+ bfa_trc(fru, BFI_FRUVPD_H2I_READ_REQ);
+ bfa_trc(fru, len);
+ bfa_trc(fru, offset);
+
+ if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
+ return BFA_STATUS_FRU_NOT_PRESENT;
+
+ if (fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK &&
+ fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK2)
+ return BFA_STATUS_CMD_NOTSUPP;
+
+ if (!bfa_ioc_is_operational(fru->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (fru->op_busy) {
+ bfa_trc(fru, fru->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ fru->op_busy = 1;
+
+ fru->cbfn = cbfn;
+ fru->cbarg = cbarg;
+ fru->residue = len;
+ fru->offset = 0;
+ fru->addr_off = offset;
+ fru->ubuf = buf;
+ bfa_fru_read_send(fru, BFI_FRUVPD_H2I_READ_REQ);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Get maximum size fru vpd image.
+ *
+ * @param[in] fru - fru structure
+ * @param[out] size - maximum size of fru vpd data
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_fruvpd_get_max_size(struct bfa_fru_s *fru, u32 *max_size)
+{
+ if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
+ return BFA_STATUS_FRU_NOT_PRESENT;
+
+ if (!bfa_ioc_is_operational(fru->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (fru->ioc->attr->card_type == BFA_MFG_TYPE_CHINOOK ||
+ fru->ioc->attr->card_type == BFA_MFG_TYPE_CHINOOK2)
+ *max_size = BFA_FRU_CHINOOK_MAX_SIZE;
+ else
+ return BFA_STATUS_CMD_NOTSUPP;
+ return BFA_STATUS_OK;
+}
+/*
+ * tfru write.
+ *
+ * @param[in] fru - fru structure
+ * @param[in] buf - update data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_tfru_write(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg)
+{
+ bfa_trc(fru, BFI_TFRU_H2I_WRITE_REQ);
+ bfa_trc(fru, len);
+ bfa_trc(fru, offset);
+ bfa_trc(fru, *((u8 *) buf));
+
+ if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
+ return BFA_STATUS_FRU_NOT_PRESENT;
+
+ if (!bfa_ioc_is_operational(fru->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (fru->op_busy) {
+ bfa_trc(fru, fru->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ fru->op_busy = 1;
+
+ fru->cbfn = cbfn;
+ fru->cbarg = cbarg;
+ fru->residue = len;
+ fru->offset = 0;
+ fru->addr_off = offset;
+ fru->ubuf = buf;
+
+ bfa_fru_write_send(fru, BFI_TFRU_H2I_WRITE_REQ);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * tfru read.
+ *
+ * @param[in] fru - fru structure
+ * @param[in] buf - read data buffer
+ * @param[in] len - data buffer length
+ * @param[in] offset - offset relative to starting address
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback argument
+ *
+ * Return status.
+ */
+bfa_status_t
+bfa_tfru_read(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg)
+{
+ bfa_trc(fru, BFI_TFRU_H2I_READ_REQ);
+ bfa_trc(fru, len);
+ bfa_trc(fru, offset);
+
+ if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2)
+ return BFA_STATUS_FRU_NOT_PRESENT;
+
+ if (!bfa_ioc_is_operational(fru->ioc))
+ return BFA_STATUS_IOC_NON_OP;
+
+ if (fru->op_busy) {
+ bfa_trc(fru, fru->op_busy);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ fru->op_busy = 1;
+
+ fru->cbfn = cbfn;
+ fru->cbarg = cbarg;
+ fru->residue = len;
+ fru->offset = 0;
+ fru->addr_off = offset;
+ fru->ubuf = buf;
+ bfa_fru_read_send(fru, BFI_TFRU_H2I_READ_REQ);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Process fru response messages upon receiving interrupts.
+ *
+ * @param[in] fruarg - fru structure
+ * @param[in] msg - message structure
+ */
+void
+bfa_fru_intr(void *fruarg, struct bfi_mbmsg_s *msg)
+{
+ struct bfa_fru_s *fru = fruarg;
+ struct bfi_fru_rsp_s *rsp = (struct bfi_fru_rsp_s *)msg;
+ u32 status;
+
+ bfa_trc(fru, msg->mh.msg_id);
+
+ if (!fru->op_busy) {
+ /*
+ * receiving response after ioc failure
+ */
+ bfa_trc(fru, 0x9999);
+ return;
+ }
+
+ switch (msg->mh.msg_id) {
+ case BFI_FRUVPD_I2H_WRITE_RSP:
+ case BFI_TFRU_I2H_WRITE_RSP:
+ status = be32_to_cpu(rsp->status);
+ bfa_trc(fru, status);
+
+ if (status != BFA_STATUS_OK || fru->residue == 0) {
+ fru->status = status;
+ fru->op_busy = 0;
+ if (fru->cbfn)
+ fru->cbfn(fru->cbarg, fru->status);
+ } else {
+ bfa_trc(fru, fru->offset);
+ if (msg->mh.msg_id == BFI_FRUVPD_I2H_WRITE_RSP)
+ bfa_fru_write_send(fru,
+ BFI_FRUVPD_H2I_WRITE_REQ);
+ else
+ bfa_fru_write_send(fru,
+ BFI_TFRU_H2I_WRITE_REQ);
+ }
+ break;
+ case BFI_FRUVPD_I2H_READ_RSP:
+ case BFI_TFRU_I2H_READ_RSP:
+ status = be32_to_cpu(rsp->status);
+ bfa_trc(fru, status);
+
+ if (status != BFA_STATUS_OK) {
+ fru->status = status;
+ fru->op_busy = 0;
+ if (fru->cbfn)
+ fru->cbfn(fru->cbarg, fru->status);
+ } else {
+ u32 len = be32_to_cpu(rsp->length);
+
+ bfa_trc(fru, fru->offset);
+ bfa_trc(fru, len);
+
+ memcpy(fru->ubuf + fru->offset, fru->dbuf_kva, len);
+ fru->residue -= len;
+ fru->offset += len;
+
+ if (fru->residue == 0) {
+ fru->status = status;
+ fru->op_busy = 0;
+ if (fru->cbfn)
+ fru->cbfn(fru->cbarg, fru->status);
+ } else {
+ if (msg->mh.msg_id == BFI_FRUVPD_I2H_READ_RSP)
+ bfa_fru_read_send(fru,
+ BFI_FRUVPD_H2I_READ_REQ);
+ else
+ bfa_fru_read_send(fru,
+ BFI_TFRU_H2I_READ_REQ);
+ }
+ }
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+/*
+ * register definitions
+ */
+#define FLI_CMD_REG 0x0001d000
+#define FLI_RDDATA_REG 0x0001d010
+#define FLI_ADDR_REG 0x0001d004
+#define FLI_DEV_STATUS_REG 0x0001d014
+
+#define BFA_FLASH_FIFO_SIZE 128 /* fifo size */
+#define BFA_FLASH_CHECK_MAX 10000 /* max # of status check */
+#define BFA_FLASH_BLOCKING_OP_MAX 1000000 /* max # of blocking op check */
+#define BFA_FLASH_WIP_MASK 0x01 /* write in progress bit mask */
+
+enum bfa_flash_cmd {
+ BFA_FLASH_FAST_READ = 0x0b, /* fast read */
+ BFA_FLASH_READ_STATUS = 0x05, /* read status */
+};
+
+/*
+ * Hardware error definition
+ */
+enum bfa_flash_err {
+ BFA_FLASH_NOT_PRESENT = -1, /*!< flash not present */
+ BFA_FLASH_UNINIT = -2, /*!< flash not initialized */
+ BFA_FLASH_BAD = -3, /*!< flash bad */
+ BFA_FLASH_BUSY = -4, /*!< flash busy */
+ BFA_FLASH_ERR_CMD_ACT = -5, /*!< command active never cleared */
+ BFA_FLASH_ERR_FIFO_CNT = -6, /*!< fifo count never cleared */
+ BFA_FLASH_ERR_WIP = -7, /*!< write-in-progress never cleared */
+ BFA_FLASH_ERR_TIMEOUT = -8, /*!< fli timeout */
+ BFA_FLASH_ERR_LEN = -9, /*!< invalid length */
+};
+
+/*
+ * Flash command register data structure
+ */
+union bfa_flash_cmd_reg_u {
+ struct {
+#ifdef __BIG_ENDIAN
+ u32 act:1;
+ u32 rsv:1;
+ u32 write_cnt:9;
+ u32 read_cnt:9;
+ u32 addr_cnt:4;
+ u32 cmd:8;
+#else
+ u32 cmd:8;
+ u32 addr_cnt:4;
+ u32 read_cnt:9;
+ u32 write_cnt:9;
+ u32 rsv:1;
+ u32 act:1;
+#endif
+ } r;
+ u32 i;
+};
+
+/*
+ * Flash device status register data structure
+ */
+union bfa_flash_dev_status_reg_u {
+ struct {
+#ifdef __BIG_ENDIAN
+ u32 rsv:21;
+ u32 fifo_cnt:6;
+ u32 busy:1;
+ u32 init_status:1;
+ u32 present:1;
+ u32 bad:1;
+ u32 good:1;
+#else
+ u32 good:1;
+ u32 bad:1;
+ u32 present:1;
+ u32 init_status:1;
+ u32 busy:1;
+ u32 fifo_cnt:6;
+ u32 rsv:21;
+#endif
+ } r;
+ u32 i;
+};
+
+/*
+ * Flash address register data structure
+ */
+union bfa_flash_addr_reg_u {
+ struct {
+#ifdef __BIG_ENDIAN
+ u32 addr:24;
+ u32 dummy:8;
+#else
+ u32 dummy:8;
+ u32 addr:24;
+#endif
+ } r;
+ u32 i;
+};
+
+/*
+ * dg flash_raw_private Flash raw private functions
+ */
+static void
+bfa_flash_set_cmd(void __iomem *pci_bar, u8 wr_cnt,
+ u8 rd_cnt, u8 ad_cnt, u8 op)
+{
+ union bfa_flash_cmd_reg_u cmd;
+
+ cmd.i = 0;
+ cmd.r.act = 1;
+ cmd.r.write_cnt = wr_cnt;
+ cmd.r.read_cnt = rd_cnt;
+ cmd.r.addr_cnt = ad_cnt;
+ cmd.r.cmd = op;
+ writel(cmd.i, (pci_bar + FLI_CMD_REG));
+}
+
+static void
+bfa_flash_set_addr(void __iomem *pci_bar, u32 address)
+{
+ union bfa_flash_addr_reg_u addr;
+
+ addr.r.addr = address & 0x00ffffff;
+ addr.r.dummy = 0;
+ writel(addr.i, (pci_bar + FLI_ADDR_REG));
+}
+
+static int
+bfa_flash_cmd_act_check(void __iomem *pci_bar)
+{
+ union bfa_flash_cmd_reg_u cmd;
+
+ cmd.i = readl(pci_bar + FLI_CMD_REG);
+
+ if (cmd.r.act)
+ return BFA_FLASH_ERR_CMD_ACT;
+
+ return 0;
+}
+
+/*
+ * @brief
+ * Flush FLI data fifo.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] dev_status - device status
+ *
+ * Return 0 on success, negative error number on error.
+ */
+static u32
+bfa_flash_fifo_flush(void __iomem *pci_bar)
+{
+ u32 i;
+ union bfa_flash_dev_status_reg_u dev_status;
+
+ dev_status.i = readl(pci_bar + FLI_DEV_STATUS_REG);
+
+ if (!dev_status.r.fifo_cnt)
+ return 0;
+
+ /* fifo counter in terms of words */
+ for (i = 0; i < dev_status.r.fifo_cnt; i++)
+ readl(pci_bar + FLI_RDDATA_REG);
+
+ /*
+ * Check the device status. It may take some time.
+ */
+ for (i = 0; i < BFA_FLASH_CHECK_MAX; i++) {
+ dev_status.i = readl(pci_bar + FLI_DEV_STATUS_REG);
+ if (!dev_status.r.fifo_cnt)
+ break;
+ }
+
+ if (dev_status.r.fifo_cnt)
+ return BFA_FLASH_ERR_FIFO_CNT;
+
+ return 0;
+}
+
+/*
+ * @brief
+ * Read flash status.
+ *
+ * @param[in] pci_bar - pci bar address
+ *
+ * Return 0 on success, negative error number on error.
+*/
+static u32
+bfa_flash_status_read(void __iomem *pci_bar)
+{
+ union bfa_flash_dev_status_reg_u dev_status;
+ int status;
+ u32 ret_status;
+ int i;
+
+ status = bfa_flash_fifo_flush(pci_bar);
+ if (status < 0)
+ return status;
+
+ bfa_flash_set_cmd(pci_bar, 0, 4, 0, BFA_FLASH_READ_STATUS);
+
+ for (i = 0; i < BFA_FLASH_CHECK_MAX; i++) {
+ status = bfa_flash_cmd_act_check(pci_bar);
+ if (!status)
+ break;
+ }
+
+ if (status)
+ return status;
+
+ dev_status.i = readl(pci_bar + FLI_DEV_STATUS_REG);
+ if (!dev_status.r.fifo_cnt)
+ return BFA_FLASH_BUSY;
+
+ ret_status = readl(pci_bar + FLI_RDDATA_REG);
+ ret_status >>= 24;
+
+ status = bfa_flash_fifo_flush(pci_bar);
+ if (status < 0)
+ return status;
+
+ return ret_status;
+}
+
+/*
+ * @brief
+ * Start flash read operation.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] offset - flash address offset
+ * @param[in] len - read data length
+ * @param[in] buf - read data buffer
+ *
+ * Return 0 on success, negative error number on error.
+ */
+static u32
+bfa_flash_read_start(void __iomem *pci_bar, u32 offset, u32 len,
+ char *buf)
+{
+ int status;
+
+ /*
+ * len must be mutiple of 4 and not exceeding fifo size
+ */
+ if (len == 0 || len > BFA_FLASH_FIFO_SIZE || (len & 0x03) != 0)
+ return BFA_FLASH_ERR_LEN;
+
+ /*
+ * check status
+ */
+ status = bfa_flash_status_read(pci_bar);
+ if (status == BFA_FLASH_BUSY)
+ status = bfa_flash_status_read(pci_bar);
+
+ if (status < 0)
+ return status;
+
+ /*
+ * check if write-in-progress bit is cleared
+ */
+ if (status & BFA_FLASH_WIP_MASK)
+ return BFA_FLASH_ERR_WIP;
+
+ bfa_flash_set_addr(pci_bar, offset);
+
+ bfa_flash_set_cmd(pci_bar, 0, (u8)len, 4, BFA_FLASH_FAST_READ);
+
+ return 0;
+}
+
+/*
+ * @brief
+ * Check flash read operation.
+ *
+ * @param[in] pci_bar - pci bar address
+ *
+ * Return flash device status, 1 if busy, 0 if not.
+ */
+static u32
+bfa_flash_read_check(void __iomem *pci_bar)
+{
+ if (bfa_flash_cmd_act_check(pci_bar))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * @brief
+ * End flash read operation.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] len - read data length
+ * @param[in] buf - read data buffer
+ *
+ */
+static void
+bfa_flash_read_end(void __iomem *pci_bar, u32 len, char *buf)
+{
+
+ u32 i;
+
+ /*
+ * read data fifo up to 32 words
+ */
+ for (i = 0; i < len; i += 4) {
+ u32 w = readl(pci_bar + FLI_RDDATA_REG);
+ *((u32 *) (buf + i)) = swab32(w);
+ }
+
+ bfa_flash_fifo_flush(pci_bar);
+}
+
+/*
+ * @brief
+ * Perform flash raw read.
+ *
+ * @param[in] pci_bar - pci bar address
+ * @param[in] offset - flash partition address offset
+ * @param[in] buf - read data buffer
+ * @param[in] len - read data length
+ *
+ * Return status.
+ */
+
+
+#define FLASH_BLOCKING_OP_MAX 500
+#define FLASH_SEM_LOCK_REG 0x18820
+
+static int
+bfa_raw_sem_get(void __iomem *bar)
+{
+ int locked;
+
+ locked = readl((bar + FLASH_SEM_LOCK_REG));
+ return !locked;
+
+}
+
+static bfa_status_t
+bfa_flash_sem_get(void __iomem *bar)
+{
+ u32 n = FLASH_BLOCKING_OP_MAX;
+
+ while (!bfa_raw_sem_get(bar)) {
+ if (--n <= 0)
+ return BFA_STATUS_BADFLASH;
+ mdelay(10);
+ }
+ return BFA_STATUS_OK;
+}
+
+static void
+bfa_flash_sem_put(void __iomem *bar)
+{
+ writel(0, (bar + FLASH_SEM_LOCK_REG));
+}
+
+bfa_status_t
+bfa_flash_raw_read(void __iomem *pci_bar, u32 offset, char *buf,
+ u32 len)
+{
+ u32 n;
+ int status;
+ u32 off, l, s, residue, fifo_sz;
+
+ residue = len;
+ off = 0;
+ fifo_sz = BFA_FLASH_FIFO_SIZE;
+ status = bfa_flash_sem_get(pci_bar);
+ if (status != BFA_STATUS_OK)
+ return status;
+
+ while (residue) {
+ s = offset + off;
+ n = s / fifo_sz;
+ l = (n + 1) * fifo_sz - s;
+ if (l > residue)
+ l = residue;
+
+ status = bfa_flash_read_start(pci_bar, offset + off, l,
+ &buf[off]);
+ if (status < 0) {
+ bfa_flash_sem_put(pci_bar);
+ return BFA_STATUS_FAILED;
+ }
+
+ n = BFA_FLASH_BLOCKING_OP_MAX;
+ while (bfa_flash_read_check(pci_bar)) {
+ if (--n <= 0) {
+ bfa_flash_sem_put(pci_bar);
+ return BFA_STATUS_FAILED;
+ }
+ }
+
+ bfa_flash_read_end(pci_bar, l, &buf[off]);
+
+ residue -= l;
+ off += l;
+ }
+ bfa_flash_sem_put(pci_bar);
+
+ return BFA_STATUS_OK;
+}
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index c85182a704fb..d70332e9ad6d 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_IOC_H__
@@ -72,7 +65,7 @@ struct bfa_sge_s {
} while (0)
#define bfa_swap_words(_x) ( \
- ((_x) << 32) | ((_x) >> 32))
+ ((u64)(_x) << 32) | ((u64)(_x) >> 32))
#ifdef __BIG_ENDIAN
#define bfa_sge_to_be(_x)
@@ -85,12 +78,79 @@ struct bfa_sge_s {
#endif
/*
+ * BFA memory resources
+ */
+struct bfa_mem_dma_s {
+ struct list_head qe; /* Queue of DMA elements */
+ u32 mem_len; /* Total Length in Bytes */
+ u8 *kva; /* kernel virtual address */
+ u64 dma; /* dma address if DMA memory */
+ u8 *kva_curp; /* kva allocation cursor */
+ u64 dma_curp; /* dma allocation cursor */
+};
+#define bfa_mem_dma_t struct bfa_mem_dma_s
+
+struct bfa_mem_kva_s {
+ struct list_head qe; /* Queue of KVA elements */
+ u32 mem_len; /* Total Length in Bytes */
+ u8 *kva; /* kernel virtual address */
+ u8 *kva_curp; /* kva allocation cursor */
+};
+#define bfa_mem_kva_t struct bfa_mem_kva_s
+
+struct bfa_meminfo_s {
+ struct bfa_mem_dma_s dma_info;
+ struct bfa_mem_kva_s kva_info;
+};
+
+/* BFA memory segment setup helpers */
+static inline void bfa_mem_dma_setup(struct bfa_meminfo_s *meminfo,
+ struct bfa_mem_dma_s *dm_ptr,
+ size_t seg_sz)
+{
+ dm_ptr->mem_len = seg_sz;
+ if (seg_sz)
+ list_add_tail(&dm_ptr->qe, &meminfo->dma_info.qe);
+}
+
+static inline void bfa_mem_kva_setup(struct bfa_meminfo_s *meminfo,
+ struct bfa_mem_kva_s *kva_ptr,
+ size_t seg_sz)
+{
+ kva_ptr->mem_len = seg_sz;
+ if (seg_sz)
+ list_add_tail(&kva_ptr->qe, &meminfo->kva_info.qe);
+}
+
+/* BFA dma memory segments iterator */
+#define bfa_mem_dma_sptr(_mod, _i) (&(_mod)->dma_seg[(_i)])
+#define bfa_mem_dma_seg_iter(_mod, _sptr, _nr, _i) \
+ for (_i = 0, _sptr = bfa_mem_dma_sptr(_mod, _i); _i < (_nr); \
+ _i++, _sptr = bfa_mem_dma_sptr(_mod, _i))
+
+#define bfa_mem_kva_curp(_mod) ((_mod)->kva_seg.kva_curp)
+#define bfa_mem_dma_virt(_sptr) ((_sptr)->kva_curp)
+#define bfa_mem_dma_phys(_sptr) ((_sptr)->dma_curp)
+#define bfa_mem_dma_len(_sptr) ((_sptr)->mem_len)
+
+/* Get the corresponding dma buf kva for a req - from the tag */
+#define bfa_mem_get_dmabuf_kva(_mod, _tag, _rqsz) \
+ (((u8 *)(_mod)->dma_seg[BFI_MEM_SEG_FROM_TAG(_tag, _rqsz)].kva_curp) +\
+ BFI_MEM_SEG_REQ_OFFSET(_tag, _rqsz) * (_rqsz))
+
+/* Get the corresponding dma buf pa for a req - from the tag */
+#define bfa_mem_get_dmabuf_pa(_mod, _tag, _rqsz) \
+ ((_mod)->dma_seg[BFI_MEM_SEG_FROM_TAG(_tag, _rqsz)].dma_curp + \
+ BFI_MEM_SEG_REQ_OFFSET(_tag, _rqsz) * (_rqsz))
+
+/*
* PCI device information required by IOC
*/
struct bfa_pcidev_s {
int pci_slot;
u8 pci_func;
u16 device_id;
+ u16 ssid;
void __iomem *pci_bar_kva;
};
@@ -112,18 +172,6 @@ struct bfa_dma_s {
#define BFI_SMEM_CB_SIZE 0x200000U /* ! 2MB for crossbow */
#define BFI_SMEM_CT_SIZE 0x280000U /* ! 2.5MB for catapult */
-
-#define bfa_dma_addr_set(dma_addr, pa) \
- __bfa_dma_addr_set(&dma_addr, (u64)pa)
-
-static inline void
-__bfa_dma_addr_set(union bfi_addr_u *dma_addr, u64 pa)
-{
- dma_addr->a32.addr_lo = (__be32) pa;
- dma_addr->a32.addr_hi = (__be32) (pa >> 32);
-}
-
-
#define bfa_dma_be_addr_set(dma_addr, pa) \
__bfa_dma_be_addr_set(&dma_addr, (u64)pa)
static inline void
@@ -133,11 +181,22 @@ __bfa_dma_be_addr_set(union bfi_addr_u *dma_addr, u64 pa)
dma_addr->a32.addr_hi = cpu_to_be32(pa >> 32);
}
+#define bfa_alen_set(__alen, __len, __pa) \
+ __bfa_alen_set(__alen, __len, (u64)__pa)
+
+static inline void
+__bfa_alen_set(struct bfi_alen_s *alen, u32 len, u64 pa)
+{
+ alen->al_len = cpu_to_be32(len);
+ bfa_dma_be_addr_set(alen->al_addr, pa);
+}
+
struct bfa_ioc_regs_s {
void __iomem *hfn_mbox_cmd;
void __iomem *hfn_mbox;
void __iomem *lpu_mbox_cmd;
void __iomem *lpu_mbox;
+ void __iomem *lpu_read_stat;
void __iomem *pss_ctl_reg;
void __iomem *pss_err_status_reg;
void __iomem *app_pll_fast_ctl_reg;
@@ -199,31 +258,79 @@ struct bfa_ioc_cbfn_s {
};
/*
- * Heartbeat failure notification queue element.
+ * IOC event notification mechanism.
*/
-struct bfa_ioc_hbfail_notify_s {
+enum ioc_event {
+ IOC_E_RESET = 1, /* IOC reset request */
+ IOC_E_ENABLE = 2, /* IOC enable request */
+ IOC_E_DISABLE = 3, /* IOC disable request */
+ IOC_E_DETACH = 4, /* driver detach cleanup */
+ IOC_E_ENABLED = 5, /* f/w enabled */
+ IOC_E_FWRSP_GETATTR = 6, /* IOC get attribute response */
+ IOC_E_DISABLED = 7, /* f/w disabled */
+ IOC_E_PFFAILED = 8, /* failure notice by iocpf sm */
+ IOC_E_HBFAIL = 9, /* heartbeat failure */
+ IOC_E_HWERROR = 10, /* hardware error interrupt */
+ IOC_E_TIMEOUT = 11, /* timeout */
+ IOC_E_HWFAILED = 12, /* PCI mapping failure notice */
+};
+
+struct bfa_ioc_s;
+typedef void (*bfa_ioc_sm_t)(struct bfa_ioc_s *fsm, enum ioc_event);
+
+enum bfa_ioc_event_e {
+ BFA_IOC_E_ENABLED = 1,
+ BFA_IOC_E_DISABLED = 2,
+ BFA_IOC_E_FAILED = 3,
+};
+
+typedef void (*bfa_ioc_notify_cbfn_t)(void *, enum bfa_ioc_event_e);
+
+struct bfa_ioc_notify_s {
struct list_head qe;
- bfa_ioc_hbfail_cbfn_t cbfn;
+ bfa_ioc_notify_cbfn_t cbfn;
void *cbarg;
};
/*
- * Initialize a heartbeat failure notification structure
+ * Initialize a IOC event notification structure
*/
-#define bfa_ioc_hbfail_init(__notify, __cbfn, __cbarg) do { \
+#define bfa_ioc_notify_init(__notify, __cbfn, __cbarg) do { \
(__notify)->cbfn = (__cbfn); \
(__notify)->cbarg = (__cbarg); \
} while (0)
+/*
+ * IOCPF state machine events
+ */
+enum iocpf_event {
+ IOCPF_E_ENABLE = 1, /* IOCPF enable request */
+ IOCPF_E_DISABLE = 2, /* IOCPF disable request */
+ IOCPF_E_STOP = 3, /* stop on driver detach */
+ IOCPF_E_FWREADY = 4, /* f/w initialization done */
+ IOCPF_E_FWRSP_ENABLE = 5, /* enable f/w response */
+ IOCPF_E_FWRSP_DISABLE = 6, /* disable f/w response */
+ IOCPF_E_FAIL = 7, /* failure notice by ioc sm */
+ IOCPF_E_INITFAIL = 8, /* init fail notice by ioc sm */
+ IOCPF_E_GETATTRFAIL = 9, /* init fail notice by ioc sm */
+ IOCPF_E_SEMLOCKED = 10, /* h/w semaphore is locked */
+ IOCPF_E_TIMEOUT = 11, /* f/w response timeout */
+ IOCPF_E_SEM_ERROR = 12, /* h/w sem mapping error */
+};
+
+struct bfa_iocpf_s;
+typedef void (*bfa_iocpf_sm_t)(struct bfa_iocpf_s *fsm, enum iocpf_event);
+
struct bfa_iocpf_s {
- bfa_fsm_t fsm;
+ bfa_iocpf_sm_t fsm;
struct bfa_ioc_s *ioc;
- u32 retry_count;
+ bfa_boolean_t fw_mismatch_notified;
bfa_boolean_t auto_recover;
+ u32 poll_time;
};
struct bfa_ioc_s {
- bfa_fsm_t fsm;
+ bfa_ioc_sm_t fsm;
struct bfa_s *bfa;
struct bfa_pcidev_s pcidev;
struct bfa_timer_mod_s *timer_mod;
@@ -231,17 +338,15 @@ struct bfa_ioc_s {
struct bfa_timer_s sem_timer;
struct bfa_timer_s hb_timer;
u32 hb_count;
- struct list_head hb_notify_q;
+ struct list_head notify_q;
void *dbg_fwsave;
int dbg_fwsave_len;
bfa_boolean_t dbg_fwsave_once;
- enum bfi_mclass ioc_mc;
+ enum bfi_pcifn_class clscode;
struct bfa_ioc_regs_s ioc_regs;
struct bfa_trc_mod_s *trcmod;
struct bfa_ioc_drv_stats_s stats;
bfa_boolean_t fcmode;
- bfa_boolean_t ctdev;
- bfa_boolean_t cna;
bfa_boolean_t pllinit;
bfa_boolean_t stats_busy; /* outstanding stats */
u8 port_id;
@@ -251,10 +356,18 @@ struct bfa_ioc_s {
struct bfa_ioc_mbox_mod_s mbox_mod;
struct bfa_ioc_hwif_s *ioc_hwif;
struct bfa_iocpf_s iocpf;
+ enum bfi_asic_gen asic_gen;
+ enum bfi_asic_mode asic_mode;
+ enum bfi_port_mode port0_mode;
+ enum bfi_port_mode port1_mode;
+ enum bfa_mode_s port_mode;
+ u8 ad_cap_bm; /* adapter cap bit mask */
+ u8 port_mode_cfg; /* config port mode */
+ int ioc_aen_seq;
};
struct bfa_ioc_hwif_s {
- bfa_status_t (*ioc_pll_init) (void __iomem *rb, bfa_boolean_t fcmode);
+ bfa_status_t (*ioc_pll_init) (void __iomem *rb, enum bfi_asic_mode m);
bfa_boolean_t (*ioc_firmware_lock) (struct bfa_ioc_s *ioc);
void (*ioc_firmware_unlock) (struct bfa_ioc_s *ioc);
void (*ioc_reg_init) (struct bfa_ioc_s *ioc);
@@ -268,12 +381,479 @@ struct bfa_ioc_hwif_s {
void (*ioc_sync_leave) (struct bfa_ioc_s *ioc);
void (*ioc_sync_ack) (struct bfa_ioc_s *ioc);
bfa_boolean_t (*ioc_sync_complete) (struct bfa_ioc_s *ioc);
+ bfa_boolean_t (*ioc_lpu_read_stat) (struct bfa_ioc_s *ioc);
+ void (*ioc_set_fwstate) (struct bfa_ioc_s *ioc,
+ enum bfi_ioc_state fwstate);
+ enum bfi_ioc_state (*ioc_get_fwstate) (struct bfa_ioc_s *ioc);
+ void (*ioc_set_alt_fwstate) (struct bfa_ioc_s *ioc,
+ enum bfi_ioc_state fwstate);
+ enum bfi_ioc_state (*ioc_get_alt_fwstate) (struct bfa_ioc_s *ioc);
+};
+
+/*
+ * Queue element to wait for room in request queue. FIFO order is
+ * maintained when fullfilling requests.
+ */
+struct bfa_reqq_wait_s {
+ struct list_head qe;
+ void (*qresume) (void *cbarg);
+ void *cbarg;
+};
+
+typedef void (*bfa_cb_cbfn_t) (void *cbarg, bfa_boolean_t complete);
+typedef void (*bfa_cb_cbfn_status_t) (void *cbarg, bfa_status_t status);
+
+/*
+ * Generic BFA callback element.
+ */
+struct bfa_cb_qe_s {
+ struct list_head qe;
+ union {
+ bfa_cb_cbfn_status_t cbfn_status;
+ bfa_cb_cbfn_t cbfn;
+ };
+ bfa_boolean_t once;
+ bfa_boolean_t pre_rmv; /* set for stack based qe(s) */
+ bfa_status_t fw_status; /* to access fw status in comp proc */
+ void *cbarg;
+};
+
+/*
+ * ASIC block configurtion related
+ */
+
+typedef void (*bfa_ablk_cbfn_t)(void *, enum bfa_status);
+
+struct bfa_ablk_s {
+ struct bfa_ioc_s *ioc;
+ struct bfa_ablk_cfg_s *cfg;
+ u16 *pcifn;
+ struct bfa_dma_s dma_addr;
+ bfa_boolean_t busy;
+ struct bfa_mbox_cmd_s mb;
+ bfa_ablk_cbfn_t cbfn;
+ void *cbarg;
+ struct bfa_ioc_notify_s ioc_notify;
+ struct bfa_mem_dma_s ablk_dma;
+};
+#define BFA_MEM_ABLK_DMA(__bfa) (&((__bfa)->modules.ablk.ablk_dma))
+
+/*
+ * SFP module specific
+ */
+typedef void (*bfa_cb_sfp_t) (void *cbarg, bfa_status_t status);
+
+struct bfa_sfp_s {
+ void *dev;
+ struct bfa_ioc_s *ioc;
+ struct bfa_trc_mod_s *trcmod;
+ struct sfp_mem_s *sfpmem;
+ bfa_cb_sfp_t cbfn;
+ void *cbarg;
+ enum bfi_sfp_mem_e memtype; /* mem access type */
+ u32 status;
+ struct bfa_mbox_cmd_s mbcmd;
+ u8 *dbuf_kva; /* dma buf virtual address */
+ u64 dbuf_pa; /* dma buf physical address */
+ struct bfa_ioc_notify_s ioc_notify;
+ enum bfa_defs_sfp_media_e *media;
+ enum bfa_port_speed portspeed;
+ bfa_cb_sfp_t state_query_cbfn;
+ void *state_query_cbarg;
+ u8 lock;
+ u8 data_valid; /* data in dbuf is valid */
+ u8 state; /* sfp state */
+ u8 state_query_lock;
+ struct bfa_mem_dma_s sfp_dma;
+ u8 is_elb; /* eloopback */
};
+#define BFA_SFP_MOD(__bfa) (&(__bfa)->modules.sfp)
+#define BFA_MEM_SFP_DMA(__bfa) (&(BFA_SFP_MOD(__bfa)->sfp_dma))
+
+u32 bfa_sfp_meminfo(void);
+
+void bfa_sfp_attach(struct bfa_sfp_s *sfp, struct bfa_ioc_s *ioc,
+ void *dev, struct bfa_trc_mod_s *trcmod);
+
+void bfa_sfp_memclaim(struct bfa_sfp_s *diag, u8 *dm_kva, u64 dm_pa);
+void bfa_sfp_intr(void *bfaarg, struct bfi_mbmsg_s *msg);
+
+bfa_status_t bfa_sfp_show(struct bfa_sfp_s *sfp, struct sfp_mem_s *sfpmem,
+ bfa_cb_sfp_t cbfn, void *cbarg);
+
+bfa_status_t bfa_sfp_media(struct bfa_sfp_s *sfp,
+ enum bfa_defs_sfp_media_e *media,
+ bfa_cb_sfp_t cbfn, void *cbarg);
+
+bfa_status_t bfa_sfp_speed(struct bfa_sfp_s *sfp,
+ enum bfa_port_speed portspeed,
+ bfa_cb_sfp_t cbfn, void *cbarg);
+
+/*
+ * Flash module specific
+ */
+typedef void (*bfa_cb_flash_t) (void *cbarg, bfa_status_t status);
+
+struct bfa_flash_s {
+ struct bfa_ioc_s *ioc; /* back pointer to ioc */
+ struct bfa_trc_mod_s *trcmod;
+ u32 type; /* partition type */
+ u8 instance; /* partition instance */
+ u8 rsv[3];
+ u32 op_busy; /* operation busy flag */
+ u32 residue; /* residual length */
+ u32 offset; /* offset */
+ bfa_status_t status; /* status */
+ u8 *dbuf_kva; /* dma buf virtual address */
+ u64 dbuf_pa; /* dma buf physical address */
+ struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */
+ bfa_cb_flash_t cbfn; /* user callback function */
+ void *cbarg; /* user callback arg */
+ u8 *ubuf; /* user supplied buffer */
+ struct bfa_cb_qe_s hcb_qe; /* comp: BFA callback qelem */
+ u32 addr_off; /* partition address offset */
+ struct bfa_mbox_cmd_s mb; /* mailbox */
+ struct bfa_ioc_notify_s ioc_notify; /* ioc event notify */
+ struct bfa_mem_dma_s flash_dma;
+};
+
+#define BFA_FLASH(__bfa) (&(__bfa)->modules.flash)
+#define BFA_MEM_FLASH_DMA(__bfa) (&(BFA_FLASH(__bfa)->flash_dma))
+
+bfa_status_t bfa_flash_get_attr(struct bfa_flash_s *flash,
+ struct bfa_flash_attr_s *attr,
+ bfa_cb_flash_t cbfn, void *cbarg);
+bfa_status_t bfa_flash_erase_part(struct bfa_flash_s *flash,
+ enum bfa_flash_part_type type, u8 instance,
+ bfa_cb_flash_t cbfn, void *cbarg);
+bfa_status_t bfa_flash_update_part(struct bfa_flash_s *flash,
+ enum bfa_flash_part_type type, u8 instance,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_flash_t cbfn, void *cbarg);
+bfa_status_t bfa_flash_read_part(struct bfa_flash_s *flash,
+ enum bfa_flash_part_type type, u8 instance, void *buf,
+ u32 len, u32 offset, bfa_cb_flash_t cbfn, void *cbarg);
+u32 bfa_flash_meminfo(bfa_boolean_t mincfg);
+void bfa_flash_attach(struct bfa_flash_s *flash, struct bfa_ioc_s *ioc,
+ void *dev, struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg);
+void bfa_flash_memclaim(struct bfa_flash_s *flash,
+ u8 *dm_kva, u64 dm_pa, bfa_boolean_t mincfg);
+bfa_status_t bfa_flash_raw_read(void __iomem *pci_bar_kva,
+ u32 offset, char *buf, u32 len);
+
+/*
+ * DIAG module specific
+ */
+
+typedef void (*bfa_cb_diag_t) (void *cbarg, bfa_status_t status);
+typedef void (*bfa_cb_diag_beacon_t) (void *dev, bfa_boolean_t beacon,
+ bfa_boolean_t link_e2e_beacon);
+
+/*
+ * Firmware ping test results
+ */
+struct bfa_diag_results_fwping {
+ u32 data; /* store the corrupted data */
+ u32 status;
+ u32 dmastatus;
+ u8 rsvd[4];
+};
+
+struct bfa_diag_qtest_result_s {
+ u32 status;
+ u16 count; /* successful queue test count */
+ u8 queue;
+ u8 rsvd; /* 64-bit align */
+};
+
+/*
+ * Firmware ping test results
+ */
+struct bfa_diag_fwping_s {
+ struct bfa_diag_results_fwping *result;
+ bfa_cb_diag_t cbfn;
+ void *cbarg;
+ u32 data;
+ u8 lock;
+ u8 rsv[3];
+ u32 status;
+ u32 count;
+ struct bfa_mbox_cmd_s mbcmd;
+ u8 *dbuf_kva; /* dma buf virtual address */
+ u64 dbuf_pa; /* dma buf physical address */
+};
+
+/*
+ * Temperature sensor query results
+ */
+struct bfa_diag_results_tempsensor_s {
+ u32 status;
+ u16 temp; /* 10-bit A/D value */
+ u16 brd_temp; /* 9-bit board temp */
+ u8 ts_junc; /* show junction tempsensor */
+ u8 ts_brd; /* show board tempsensor */
+ u8 rsvd[6]; /* keep 8 bytes alignment */
+};
+
+struct bfa_diag_tsensor_s {
+ bfa_cb_diag_t cbfn;
+ void *cbarg;
+ struct bfa_diag_results_tempsensor_s *temp;
+ u8 lock;
+ u8 rsv[3];
+ u32 status;
+ struct bfa_mbox_cmd_s mbcmd;
+};
+
+struct bfa_diag_sfpshow_s {
+ struct sfp_mem_s *sfpmem;
+ bfa_cb_diag_t cbfn;
+ void *cbarg;
+ u8 lock;
+ u8 static_data;
+ u8 rsv[2];
+ u32 status;
+ struct bfa_mbox_cmd_s mbcmd;
+ u8 *dbuf_kva; /* dma buf virtual address */
+ u64 dbuf_pa; /* dma buf physical address */
+};
+
+struct bfa_diag_led_s {
+ struct bfa_mbox_cmd_s mbcmd;
+ bfa_boolean_t lock; /* 1: ledtest is operating */
+};
+
+struct bfa_diag_beacon_s {
+ struct bfa_mbox_cmd_s mbcmd;
+ bfa_boolean_t state; /* port beacon state */
+ bfa_boolean_t link_e2e; /* link beacon state */
+};
+
+struct bfa_diag_s {
+ void *dev;
+ struct bfa_ioc_s *ioc;
+ struct bfa_trc_mod_s *trcmod;
+ struct bfa_diag_fwping_s fwping;
+ struct bfa_diag_tsensor_s tsensor;
+ struct bfa_diag_sfpshow_s sfpshow;
+ struct bfa_diag_led_s ledtest;
+ struct bfa_diag_beacon_s beacon;
+ void *result;
+ struct bfa_timer_s timer;
+ bfa_cb_diag_beacon_t cbfn_beacon;
+ bfa_cb_diag_t cbfn;
+ void *cbarg;
+ u8 block;
+ u8 timer_active;
+ u8 rsvd[2];
+ u32 status;
+ struct bfa_ioc_notify_s ioc_notify;
+ struct bfa_mem_dma_s diag_dma;
+};
+
+#define BFA_DIAG_MOD(__bfa) (&(__bfa)->modules.diag_mod)
+#define BFA_MEM_DIAG_DMA(__bfa) (&(BFA_DIAG_MOD(__bfa)->diag_dma))
+
+u32 bfa_diag_meminfo(void);
+void bfa_diag_memclaim(struct bfa_diag_s *diag, u8 *dm_kva, u64 dm_pa);
+void bfa_diag_attach(struct bfa_diag_s *diag, struct bfa_ioc_s *ioc, void *dev,
+ bfa_cb_diag_beacon_t cbfn_beacon,
+ struct bfa_trc_mod_s *trcmod);
+bfa_status_t bfa_diag_reg_read(struct bfa_diag_s *diag, u32 offset,
+ u32 len, u32 *buf, u32 force);
+bfa_status_t bfa_diag_reg_write(struct bfa_diag_s *diag, u32 offset,
+ u32 len, u32 value, u32 force);
+bfa_status_t bfa_diag_tsensor_query(struct bfa_diag_s *diag,
+ struct bfa_diag_results_tempsensor_s *result,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_diag_fwping(struct bfa_diag_s *diag, u32 cnt,
+ u32 pattern, struct bfa_diag_results_fwping *result,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_diag_sfpshow(struct bfa_diag_s *diag,
+ struct sfp_mem_s *sfpmem, u8 static_data,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_diag_memtest(struct bfa_diag_s *diag,
+ struct bfa_diag_memtest_s *memtest, u32 pattern,
+ struct bfa_diag_memtest_result *result,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_diag_ledtest(struct bfa_diag_s *diag,
+ struct bfa_diag_ledtest_s *ledtest);
+bfa_status_t bfa_diag_beacon_port(struct bfa_diag_s *diag,
+ bfa_boolean_t beacon, bfa_boolean_t link_e2e_beacon,
+ u32 sec);
+
+/*
+ * PHY module specific
+ */
+typedef void (*bfa_cb_phy_t) (void *cbarg, bfa_status_t status);
+
+struct bfa_phy_s {
+ struct bfa_ioc_s *ioc; /* back pointer to ioc */
+ struct bfa_trc_mod_s *trcmod; /* trace module */
+ u8 instance; /* port instance */
+ u8 op_busy; /* operation busy flag */
+ u8 rsv[2];
+ u32 residue; /* residual length */
+ u32 offset; /* offset */
+ bfa_status_t status; /* status */
+ u8 *dbuf_kva; /* dma buf virtual address */
+ u64 dbuf_pa; /* dma buf physical address */
+ struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */
+ bfa_cb_phy_t cbfn; /* user callback function */
+ void *cbarg; /* user callback arg */
+ u8 *ubuf; /* user supplied buffer */
+ struct bfa_cb_qe_s hcb_qe; /* comp: BFA callback qelem */
+ u32 addr_off; /* phy address offset */
+ struct bfa_mbox_cmd_s mb; /* mailbox */
+ struct bfa_ioc_notify_s ioc_notify; /* ioc event notify */
+ struct bfa_mem_dma_s phy_dma;
+};
+#define BFA_PHY(__bfa) (&(__bfa)->modules.phy)
+#define BFA_MEM_PHY_DMA(__bfa) (&(BFA_PHY(__bfa)->phy_dma))
+
+bfa_boolean_t bfa_phy_busy(struct bfa_ioc_s *ioc);
+bfa_status_t bfa_phy_get_attr(struct bfa_phy_s *phy, u8 instance,
+ struct bfa_phy_attr_s *attr,
+ bfa_cb_phy_t cbfn, void *cbarg);
+bfa_status_t bfa_phy_get_stats(struct bfa_phy_s *phy, u8 instance,
+ struct bfa_phy_stats_s *stats,
+ bfa_cb_phy_t cbfn, void *cbarg);
+bfa_status_t bfa_phy_update(struct bfa_phy_s *phy, u8 instance,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_phy_t cbfn, void *cbarg);
+bfa_status_t bfa_phy_read(struct bfa_phy_s *phy, u8 instance,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_phy_t cbfn, void *cbarg);
+
+u32 bfa_phy_meminfo(bfa_boolean_t mincfg);
+void bfa_phy_attach(struct bfa_phy_s *phy, struct bfa_ioc_s *ioc,
+ void *dev, struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg);
+void bfa_phy_memclaim(struct bfa_phy_s *phy,
+ u8 *dm_kva, u64 dm_pa, bfa_boolean_t mincfg);
+void bfa_phy_intr(void *phyarg, struct bfi_mbmsg_s *msg);
+
+/*
+ * FRU module specific
+ */
+typedef void (*bfa_cb_fru_t) (void *cbarg, bfa_status_t status);
+
+struct bfa_fru_s {
+ struct bfa_ioc_s *ioc; /* back pointer to ioc */
+ struct bfa_trc_mod_s *trcmod; /* trace module */
+ u8 op_busy; /* operation busy flag */
+ u8 rsv[3];
+ u32 residue; /* residual length */
+ u32 offset; /* offset */
+ bfa_status_t status; /* status */
+ u8 *dbuf_kva; /* dma buf virtual address */
+ u64 dbuf_pa; /* dma buf physical address */
+ struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */
+ bfa_cb_fru_t cbfn; /* user callback function */
+ void *cbarg; /* user callback arg */
+ u8 *ubuf; /* user supplied buffer */
+ struct bfa_cb_qe_s hcb_qe; /* comp: BFA callback qelem */
+ u32 addr_off; /* fru address offset */
+ struct bfa_mbox_cmd_s mb; /* mailbox */
+ struct bfa_ioc_notify_s ioc_notify; /* ioc event notify */
+ struct bfa_mem_dma_s fru_dma;
+ u8 trfr_cmpl;
+};
+
+#define BFA_FRU(__bfa) (&(__bfa)->modules.fru)
+#define BFA_MEM_FRU_DMA(__bfa) (&(BFA_FRU(__bfa)->fru_dma))
+
+bfa_status_t bfa_fruvpd_update(struct bfa_fru_s *fru,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg, u8 trfr_cmpl);
+bfa_status_t bfa_fruvpd_read(struct bfa_fru_s *fru,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg);
+bfa_status_t bfa_fruvpd_get_max_size(struct bfa_fru_s *fru, u32 *max_size);
+bfa_status_t bfa_tfru_write(struct bfa_fru_s *fru,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg);
+bfa_status_t bfa_tfru_read(struct bfa_fru_s *fru,
+ void *buf, u32 len, u32 offset,
+ bfa_cb_fru_t cbfn, void *cbarg);
+u32 bfa_fru_meminfo(bfa_boolean_t mincfg);
+void bfa_fru_attach(struct bfa_fru_s *fru, struct bfa_ioc_s *ioc,
+ void *dev, struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg);
+void bfa_fru_memclaim(struct bfa_fru_s *fru,
+ u8 *dm_kva, u64 dm_pa, bfa_boolean_t mincfg);
+void bfa_fru_intr(void *fruarg, struct bfi_mbmsg_s *msg);
+
+/*
+ * Driver Config( dconf) specific
+ */
+#define BFI_DCONF_SIGNATURE 0xabcdabcd
+#define BFI_DCONF_VERSION 1
+
+#pragma pack(1)
+struct bfa_dconf_hdr_s {
+ u32 signature;
+ u32 version;
+};
+
+struct bfa_dconf_s {
+ struct bfa_dconf_hdr_s hdr;
+ struct bfa_lunmask_cfg_s lun_mask;
+ struct bfa_throttle_cfg_s throttle_cfg;
+};
+#pragma pack()
+
+/*
+ * DCONF state machine events
+ */
+enum bfa_dconf_event {
+ BFA_DCONF_SM_INIT = 1, /* dconf Init */
+ BFA_DCONF_SM_FLASH_COMP = 2, /* read/write to flash */
+ BFA_DCONF_SM_WR = 3, /* binding change, map */
+ BFA_DCONF_SM_TIMEOUT = 4, /* Start timer */
+ BFA_DCONF_SM_EXIT = 5, /* exit dconf module */
+ BFA_DCONF_SM_IOCDISABLE = 6, /* IOC disable event */
+};
+
+struct bfa_dconf_mod_s;
+typedef void (*bfa_dconf_sm_t)(struct bfa_dconf_mod_s *fsm, enum bfa_dconf_event);
+
+struct bfa_dconf_mod_s {
+ bfa_dconf_sm_t sm;
+ u8 instance;
+ bfa_boolean_t read_data_valid;
+ bfa_boolean_t min_cfg;
+ struct bfa_timer_s timer;
+ struct bfa_s *bfa;
+ void *bfad;
+ void *trcmod;
+ struct bfa_dconf_s *dconf;
+ struct bfa_mem_kva_s kva_seg;
+};
+
+#define BFA_DCONF_MOD(__bfa) \
+ (&(__bfa)->modules.dconf_mod)
+#define BFA_MEM_DCONF_KVA(__bfa) (&(BFA_DCONF_MOD(__bfa)->kva_seg))
+#define bfa_dconf_read_data_valid(__bfa) \
+ (BFA_DCONF_MOD(__bfa)->read_data_valid)
+#define BFA_DCONF_UPDATE_TOV 5000 /* memtest timeout in msec */
+#define bfa_dconf_get_min_cfg(__bfa) \
+ (BFA_DCONF_MOD(__bfa)->min_cfg)
+
+void bfa_dconf_modinit(struct bfa_s *bfa);
+void bfa_dconf_modexit(struct bfa_s *bfa);
+bfa_status_t bfa_dconf_update(struct bfa_s *bfa);
+
+/*
+ * IOC specfic macros
+ */
#define bfa_ioc_pcifn(__ioc) ((__ioc)->pcidev.pci_func)
#define bfa_ioc_devid(__ioc) ((__ioc)->pcidev.device_id)
#define bfa_ioc_bar0(__ioc) ((__ioc)->pcidev.pci_bar_kva)
#define bfa_ioc_portid(__ioc) ((__ioc)->port_id)
+#define bfa_ioc_asic_gen(__ioc) ((__ioc)->asic_gen)
+#define bfa_ioc_is_cna(__ioc) \
+ ((bfa_ioc_get_type(__ioc) == BFA_IOC_TYPE_FCoE) || \
+ (bfa_ioc_get_type(__ioc) == BFA_IOC_TYPE_LL))
#define bfa_ioc_fetch_stats(__ioc, __stats) \
(((__stats)->drv_stats) = (__ioc)->stats)
#define bfa_ioc_clr_stats(__ioc) \
@@ -281,18 +861,16 @@ struct bfa_ioc_hwif_s {
#define bfa_ioc_maxfrsize(__ioc) ((__ioc)->attr->maxfrsize)
#define bfa_ioc_rx_bbcredit(__ioc) ((__ioc)->attr->rx_bbcredit)
#define bfa_ioc_speed_sup(__ioc) \
- BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop)
+ ((bfa_ioc_is_cna(__ioc)) ? BFA_PORT_SPEED_10GBPS : \
+ BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop))
#define bfa_ioc_get_nports(__ioc) \
BFI_ADAPTER_GETP(NPORTS, (__ioc)->attr->adapter_prop)
#define bfa_ioc_stats(_ioc, _stats) ((_ioc)->stats._stats++)
#define BFA_IOC_FWIMG_MINSZ (16 * 1024)
-#define BFA_IOC_FWIMG_TYPE(__ioc) \
- (((__ioc)->ctdev) ? \
- (((__ioc)->fcmode) ? BFI_IMAGE_CT_FC : BFI_IMAGE_CT_CNA) : \
- BFI_IMAGE_CB_FC)
-#define BFA_IOC_FW_SMEM_SIZE(__ioc) \
- (((__ioc)->ctdev) ? BFI_SMEM_CT_SIZE : BFI_SMEM_CB_SIZE)
+#define BFA_IOC_FW_SMEM_SIZE(__ioc) \
+ ((bfa_ioc_asic_gen(__ioc) == BFI_ASIC_GEN_CB) \
+ ? BFI_SMEM_CB_SIZE : BFI_SMEM_CT_SIZE)
#define BFA_IOC_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS)
#define BFA_IOC_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS)
#define BFA_IOC_FLASH_CHUNK_ADDR(chunkno) (chunkno * BFI_FLASH_CHUNK_SZ_WORDS)
@@ -305,7 +883,7 @@ void bfa_ioc_mbox_register(struct bfa_ioc_s *ioc,
bfa_ioc_mbox_mcfunc_t *mcfuncs);
void bfa_ioc_mbox_isr(struct bfa_ioc_s *ioc);
void bfa_ioc_mbox_send(struct bfa_ioc_s *ioc, void *ioc_msg, int len);
-void bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg);
+bfa_boolean_t bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg);
void bfa_ioc_mbox_regisr(struct bfa_ioc_s *ioc, enum bfi_mclass mc,
bfa_ioc_mbox_mcfunc_t cbfn, void *cbarg);
@@ -315,40 +893,48 @@ void bfa_ioc_mbox_regisr(struct bfa_ioc_s *ioc, enum bfi_mclass mc,
#define bfa_ioc_pll_init_asic(__ioc) \
((__ioc)->ioc_hwif->ioc_pll_init((__ioc)->pcidev.pci_bar_kva, \
- (__ioc)->fcmode))
+ (__ioc)->asic_mode))
bfa_status_t bfa_ioc_pll_init(struct bfa_ioc_s *ioc);
-bfa_status_t bfa_ioc_cb_pll_init(void __iomem *rb, bfa_boolean_t fcmode);
-bfa_boolean_t bfa_ioc_ct_pll_init_complete(void __iomem *rb);
-bfa_status_t bfa_ioc_ct_pll_init(void __iomem *rb, bfa_boolean_t fcmode);
+bfa_status_t bfa_ioc_cb_pll_init(void __iomem *rb, enum bfi_asic_mode mode);
+bfa_status_t bfa_ioc_ct_pll_init(void __iomem *rb, enum bfi_asic_mode mode);
+bfa_status_t bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode);
-#define bfa_ioc_isr_mode_set(__ioc, __msix) \
- ((__ioc)->ioc_hwif->ioc_isr_mode_set(__ioc, __msix))
+#define bfa_ioc_isr_mode_set(__ioc, __msix) do { \
+ if ((__ioc)->ioc_hwif->ioc_isr_mode_set) \
+ ((__ioc)->ioc_hwif->ioc_isr_mode_set(__ioc, __msix)); \
+} while (0)
#define bfa_ioc_ownership_reset(__ioc) \
((__ioc)->ioc_hwif->ioc_ownership_reset(__ioc))
+#define bfa_ioc_get_fcmode(__ioc) ((__ioc)->fcmode)
+#define bfa_ioc_lpu_read_stat(__ioc) do { \
+ if ((__ioc)->ioc_hwif->ioc_lpu_read_stat) \
+ ((__ioc)->ioc_hwif->ioc_lpu_read_stat(__ioc)); \
+} while (0)
-
-void bfa_ioc_set_ct_hwif(struct bfa_ioc_s *ioc);
void bfa_ioc_set_cb_hwif(struct bfa_ioc_s *ioc);
+void bfa_ioc_set_ct_hwif(struct bfa_ioc_s *ioc);
+void bfa_ioc_set_ct2_hwif(struct bfa_ioc_s *ioc);
+void bfa_ioc_ct2_poweron(struct bfa_ioc_s *ioc);
void bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa,
struct bfa_ioc_cbfn_s *cbfn, struct bfa_timer_mod_s *timer_mod);
-void bfa_ioc_auto_recover(bfa_boolean_t auto_recover);
void bfa_ioc_detach(struct bfa_ioc_s *ioc);
+void bfa_ioc_suspend(struct bfa_ioc_s *ioc);
void bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev,
- enum bfi_mclass mc);
+ enum bfi_pcifn_class clscode);
void bfa_ioc_mem_claim(struct bfa_ioc_s *ioc, u8 *dm_kva, u64 dm_pa);
void bfa_ioc_enable(struct bfa_ioc_s *ioc);
void bfa_ioc_disable(struct bfa_ioc_s *ioc);
bfa_boolean_t bfa_ioc_intx_claim(struct bfa_ioc_s *ioc);
-void bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type,
- u32 boot_param);
+bfa_status_t bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type,
+ u32 boot_env);
void bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *msg);
void bfa_ioc_error_isr(struct bfa_ioc_s *ioc);
bfa_boolean_t bfa_ioc_is_operational(struct bfa_ioc_s *ioc);
-bfa_boolean_t bfa_ioc_is_initialized(struct bfa_ioc_s *ioc);
bfa_boolean_t bfa_ioc_is_disabled(struct bfa_ioc_s *ioc);
+bfa_boolean_t bfa_ioc_is_acq_addr(struct bfa_ioc_s *ioc);
bfa_boolean_t bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc);
bfa_boolean_t bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc);
void bfa_ioc_reset_fwstate(struct bfa_ioc_s *ioc);
@@ -372,17 +958,47 @@ bfa_status_t bfa_ioc_debug_fwtrc(struct bfa_ioc_s *ioc, void *trcdata,
int *trclen);
bfa_status_t bfa_ioc_debug_fwcore(struct bfa_ioc_s *ioc, void *buf,
u32 *offset, int *buflen);
-void bfa_ioc_set_fcmode(struct bfa_ioc_s *ioc);
-bfa_boolean_t bfa_ioc_get_fcmode(struct bfa_ioc_s *ioc);
+bfa_status_t bfa_ioc_fwsig_invalidate(struct bfa_ioc_s *ioc);
bfa_boolean_t bfa_ioc_sem_get(void __iomem *sem_reg);
void bfa_ioc_fwver_get(struct bfa_ioc_s *ioc,
struct bfi_ioc_image_hdr_s *fwhdr);
bfa_boolean_t bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc,
struct bfi_ioc_image_hdr_s *fwhdr);
+void bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event);
bfa_status_t bfa_ioc_fw_stats_get(struct bfa_ioc_s *ioc, void *stats);
bfa_status_t bfa_ioc_fw_stats_clear(struct bfa_ioc_s *ioc);
+void bfa_ioc_debug_save_ftrc(struct bfa_ioc_s *ioc);
/*
+ * asic block configuration related APIs
+ */
+u32 bfa_ablk_meminfo(void);
+void bfa_ablk_memclaim(struct bfa_ablk_s *ablk, u8 *dma_kva, u64 dma_pa);
+void bfa_ablk_attach(struct bfa_ablk_s *ablk, struct bfa_ioc_s *ioc);
+bfa_status_t bfa_ablk_query(struct bfa_ablk_s *ablk,
+ struct bfa_ablk_cfg_s *ablk_cfg,
+ bfa_ablk_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_ablk_adapter_config(struct bfa_ablk_s *ablk,
+ enum bfa_mode_s mode, int max_pf, int max_vf,
+ bfa_ablk_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_ablk_port_config(struct bfa_ablk_s *ablk, int port,
+ enum bfa_mode_s mode, int max_pf, int max_vf,
+ bfa_ablk_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_ablk_pf_create(struct bfa_ablk_s *ablk, u16 *pcifn,
+ u8 port, enum bfi_pcifn_class personality,
+ u16 bw_min, u16 bw_max, bfa_ablk_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_ablk_pf_delete(struct bfa_ablk_s *ablk, int pcifn,
+ bfa_ablk_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_ablk_pf_update(struct bfa_ablk_s *ablk, int pcifn,
+ u16 bw_min, u16 bw_max, bfa_ablk_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_ablk_optrom_en(struct bfa_ablk_s *ablk,
+ bfa_ablk_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_ablk_optrom_dis(struct bfa_ablk_s *ablk,
+ bfa_ablk_cbfn_t cbfn, void *cbarg);
+
+bfa_status_t bfa_ioc_flash_img_get_chnk(struct bfa_ioc_s *ioc, u32 off,
+ u32 *fwimg);
+/*
* bfa mfg wwn API functions
*/
mac_t bfa_ioc_get_mac(struct bfa_ioc_s *ioc);
@@ -391,50 +1007,64 @@ mac_t bfa_ioc_get_mfg_mac(struct bfa_ioc_s *ioc);
/*
* F/W Image Size & Chunk
*/
-extern u32 bfi_image_ct_fc_size;
-extern u32 bfi_image_ct_cna_size;
-extern u32 bfi_image_cb_fc_size;
-extern u32 *bfi_image_ct_fc;
-extern u32 *bfi_image_ct_cna;
-extern u32 *bfi_image_cb_fc;
+extern u32 bfi_image_cb_size;
+extern u32 bfi_image_ct_size;
+extern u32 bfi_image_ct2_size;
+extern u32 *bfi_image_cb;
+extern u32 *bfi_image_ct;
+extern u32 *bfi_image_ct2;
static inline u32 *
-bfi_image_ct_fc_get_chunk(u32 off)
-{ return (u32 *)(bfi_image_ct_fc + off); }
+bfi_image_cb_get_chunk(u32 off)
+{
+ return (u32 *)(bfi_image_cb + off);
+}
static inline u32 *
-bfi_image_ct_cna_get_chunk(u32 off)
-{ return (u32 *)(bfi_image_ct_cna + off); }
+bfi_image_ct_get_chunk(u32 off)
+{
+ return (u32 *)(bfi_image_ct + off);
+}
static inline u32 *
-bfi_image_cb_fc_get_chunk(u32 off)
-{ return (u32 *)(bfi_image_cb_fc + off); }
+bfi_image_ct2_get_chunk(u32 off)
+{
+ return (u32 *)(bfi_image_ct2 + off);
+}
static inline u32*
-bfa_cb_image_get_chunk(int type, u32 off)
+bfa_cb_image_get_chunk(enum bfi_asic_gen asic_gen, u32 off)
{
- switch (type) {
- case BFI_IMAGE_CT_FC:
- return bfi_image_ct_fc_get_chunk(off); break;
- case BFI_IMAGE_CT_CNA:
- return bfi_image_ct_cna_get_chunk(off); break;
- case BFI_IMAGE_CB_FC:
- return bfi_image_cb_fc_get_chunk(off); break;
- default: return NULL;
+ switch (asic_gen) {
+ case BFI_ASIC_GEN_CB:
+ return bfi_image_cb_get_chunk(off);
+ break;
+ case BFI_ASIC_GEN_CT:
+ return bfi_image_ct_get_chunk(off);
+ break;
+ case BFI_ASIC_GEN_CT2:
+ return bfi_image_ct2_get_chunk(off);
+ break;
+ default:
+ return NULL;
}
}
static inline u32
-bfa_cb_image_get_size(int type)
+bfa_cb_image_get_size(enum bfi_asic_gen asic_gen)
{
- switch (type) {
- case BFI_IMAGE_CT_FC:
- return bfi_image_ct_fc_size; break;
- case BFI_IMAGE_CT_CNA:
- return bfi_image_ct_cna_size; break;
- case BFI_IMAGE_CB_FC:
- return bfi_image_cb_fc_size; break;
- default: return 0;
+ switch (asic_gen) {
+ case BFI_ASIC_GEN_CB:
+ return bfi_image_cb_size;
+ break;
+ case BFI_ASIC_GEN_CT:
+ return bfi_image_ct_size;
+ break;
+ case BFI_ASIC_GEN_CT2:
+ return bfi_image_ct2_size;
+ break;
+ default:
+ return 0;
}
}
diff --git a/drivers/scsi/bfa/bfa_ioc_cb.c b/drivers/scsi/bfa/bfa_ioc_cb.c
index 89ae4c8f95a2..2fc6215c2b2b 100644
--- a/drivers/scsi/bfa/bfa_ioc_cb.c
+++ b/drivers/scsi/bfa/bfa_ioc_cb.c
@@ -1,27 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
#include "bfa_ioc.h"
-#include "bfi_cbreg.h"
+#include "bfi_reg.h"
#include "bfa_defs.h"
BFA_TRC_FILE(CNA, IOC_CB);
+#define bfa_ioc_cb_join_pos(__ioc) ((u32) (1 << BFA_IOC_CB_JOIN_SH))
+
/*
* forward declarations
*/
@@ -37,6 +32,12 @@ static void bfa_ioc_cb_sync_join(struct bfa_ioc_s *ioc);
static void bfa_ioc_cb_sync_leave(struct bfa_ioc_s *ioc);
static void bfa_ioc_cb_sync_ack(struct bfa_ioc_s *ioc);
static bfa_boolean_t bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc);
+static void bfa_ioc_cb_set_cur_ioc_fwstate(
+ struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
+static enum bfi_ioc_state bfa_ioc_cb_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc);
+static void bfa_ioc_cb_set_alt_ioc_fwstate(
+ struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
+static enum bfi_ioc_state bfa_ioc_cb_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc);
static struct bfa_ioc_hwif_s hwif_cb;
@@ -59,6 +60,10 @@ bfa_ioc_set_cb_hwif(struct bfa_ioc_s *ioc)
hwif_cb.ioc_sync_leave = bfa_ioc_cb_sync_leave;
hwif_cb.ioc_sync_ack = bfa_ioc_cb_sync_ack;
hwif_cb.ioc_sync_complete = bfa_ioc_cb_sync_complete;
+ hwif_cb.ioc_set_fwstate = bfa_ioc_cb_set_cur_ioc_fwstate;
+ hwif_cb.ioc_get_fwstate = bfa_ioc_cb_get_cur_ioc_fwstate;
+ hwif_cb.ioc_set_alt_fwstate = bfa_ioc_cb_set_alt_ioc_fwstate;
+ hwif_cb.ioc_get_alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate;
ioc->ioc_hwif = &hwif_cb;
}
@@ -69,20 +74,28 @@ bfa_ioc_set_cb_hwif(struct bfa_ioc_s *ioc)
static bfa_boolean_t
bfa_ioc_cb_firmware_lock(struct bfa_ioc_s *ioc)
{
+ enum bfi_ioc_state alt_fwstate, cur_fwstate;
struct bfi_ioc_image_hdr_s fwhdr;
- uint32_t fwstate = readl(ioc->ioc_regs.ioc_fwstate);
- if (fwstate == BFI_IOC_UNINIT)
- return BFA_TRUE;
+ cur_fwstate = bfa_ioc_cb_get_cur_ioc_fwstate(ioc);
+ bfa_trc(ioc, cur_fwstate);
+ alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate(ioc);
+ bfa_trc(ioc, alt_fwstate);
- bfa_ioc_fwver_get(ioc, &fwhdr);
-
- if (swab32(fwhdr.exec) == BFI_BOOT_TYPE_NORMAL)
+ /*
+ * Uninit implies this is the only driver as of now.
+ */
+ if (cur_fwstate == BFI_IOC_UNINIT)
return BFA_TRUE;
-
- bfa_trc(ioc, fwstate);
- bfa_trc(ioc, fwhdr.exec);
- writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
+ /*
+ * Check if another driver with a different firmware is active
+ */
+ bfa_ioc_fwver_get(ioc, &fwhdr);
+ if (!bfa_ioc_fwver_cmp(ioc, &fwhdr) &&
+ alt_fwstate != BFI_IOC_DISABLED) {
+ bfa_trc(ioc, alt_fwstate);
+ return BFA_FALSE;
+ }
return BFA_TRUE;
}
@@ -98,7 +111,7 @@ bfa_ioc_cb_firmware_unlock(struct bfa_ioc_s *ioc)
static void
bfa_ioc_cb_notify_fail(struct bfa_ioc_s *ioc)
{
- writel(__PSS_ERR_STATUS_SET, ioc->ioc_regs.err_set);
+ writel(~0U, ioc->ioc_regs.err_set);
readl(ioc->ioc_regs.err_set);
}
@@ -152,8 +165,8 @@ bfa_ioc_cb_reg_init(struct bfa_ioc_s *ioc)
*/
ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG);
ioc->ioc_regs.pss_err_status_reg = (rb + PSS_ERR_STATUS_REG);
- ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_400_CTL_REG);
- ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_212_CTL_REG);
+ ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_LCLK_CTL_REG);
+ ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_SCLK_CTL_REG);
/*
* IOC semaphore registers and serialization
@@ -202,6 +215,20 @@ bfa_ioc_cb_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix)
static bfa_boolean_t
bfa_ioc_cb_sync_start(struct bfa_ioc_s *ioc)
{
+ u32 ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);
+
+ /**
+ * Driver load time. If the join bit is set,
+ * it is due to an unclean exit by the driver for this
+ * PCI fn in the previous incarnation. Whoever comes here first
+ * should clean it up, no matter which PCI fn.
+ */
+ if (ioc_fwstate & BFA_IOC_CB_JOIN_MASK) {
+ writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
+ writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate);
+ return BFA_TRUE;
+ }
+
return bfa_ioc_cb_sync_complete(ioc);
}
@@ -227,24 +254,66 @@ bfa_ioc_cb_ownership_reset(struct bfa_ioc_s *ioc)
static void
bfa_ioc_cb_sync_join(struct bfa_ioc_s *ioc)
{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);
+ u32 join_pos = bfa_ioc_cb_join_pos(ioc);
+
+ writel((r32 | join_pos), ioc->ioc_regs.ioc_fwstate);
}
static void
bfa_ioc_cb_sync_leave(struct bfa_ioc_s *ioc)
{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);
+ u32 join_pos = bfa_ioc_cb_join_pos(ioc);
+
+ writel((r32 & ~join_pos), ioc->ioc_regs.ioc_fwstate);
+}
+
+static void
+bfa_ioc_cb_set_cur_ioc_fwstate(struct bfa_ioc_s *ioc,
+ enum bfi_ioc_state fwstate)
+{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fwstate);
+
+ writel((fwstate | (r32 & BFA_IOC_CB_JOIN_MASK)),
+ ioc->ioc_regs.ioc_fwstate);
+}
+
+static enum bfi_ioc_state
+bfa_ioc_cb_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc)
+{
+ return (enum bfi_ioc_state)(readl(ioc->ioc_regs.ioc_fwstate) &
+ BFA_IOC_CB_FWSTATE_MASK);
+}
+
+static void
+bfa_ioc_cb_set_alt_ioc_fwstate(struct bfa_ioc_s *ioc,
+ enum bfi_ioc_state fwstate)
+{
+ u32 r32 = readl(ioc->ioc_regs.alt_ioc_fwstate);
+
+ writel((fwstate | (r32 & BFA_IOC_CB_JOIN_MASK)),
+ ioc->ioc_regs.alt_ioc_fwstate);
+}
+
+static enum bfi_ioc_state
+bfa_ioc_cb_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc)
+{
+ return (enum bfi_ioc_state)(readl(ioc->ioc_regs.alt_ioc_fwstate) &
+ BFA_IOC_CB_FWSTATE_MASK);
}
static void
bfa_ioc_cb_sync_ack(struct bfa_ioc_s *ioc)
{
- writel(BFI_IOC_FAIL, ioc->ioc_regs.ioc_fwstate);
+ bfa_ioc_cb_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL);
}
static bfa_boolean_t
bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc)
{
- uint32_t fwstate, alt_fwstate;
- fwstate = readl(ioc->ioc_regs.ioc_fwstate);
+ u32 fwstate, alt_fwstate;
+ fwstate = bfa_ioc_cb_get_cur_ioc_fwstate(ioc);
/*
* At this point, this IOC is hoding the hw sem in the
@@ -272,7 +341,7 @@ bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc)
fwstate == BFI_IOC_OP)
return BFA_TRUE;
else {
- alt_fwstate = readl(ioc->ioc_regs.alt_ioc_fwstate);
+ alt_fwstate = bfa_ioc_cb_get_alt_ioc_fwstate(ioc);
if (alt_fwstate == BFI_IOC_FAIL ||
alt_fwstate == BFI_IOC_DISABLED ||
alt_fwstate == BFI_IOC_UNINIT ||
@@ -285,44 +354,48 @@ bfa_ioc_cb_sync_complete(struct bfa_ioc_s *ioc)
}
bfa_status_t
-bfa_ioc_cb_pll_init(void __iomem *rb, bfa_boolean_t fcmode)
+bfa_ioc_cb_pll_init(void __iomem *rb, enum bfi_asic_mode fcmode)
{
- u32 pll_sclk, pll_fclk;
-
- pll_sclk = __APP_PLL_212_ENABLE | __APP_PLL_212_LRESETN |
- __APP_PLL_212_P0_1(3U) |
- __APP_PLL_212_JITLMT0_1(3U) |
- __APP_PLL_212_CNTLMT0_1(3U);
- pll_fclk = __APP_PLL_400_ENABLE | __APP_PLL_400_LRESETN |
- __APP_PLL_400_RSEL200500 | __APP_PLL_400_P0_1(3U) |
- __APP_PLL_400_JITLMT0_1(3U) |
- __APP_PLL_400_CNTLMT0_1(3U);
- writel(BFI_IOC_UNINIT, (rb + BFA_IOC0_STATE_REG));
- writel(BFI_IOC_UNINIT, (rb + BFA_IOC1_STATE_REG));
+ u32 pll_sclk, pll_fclk, join_bits;
+
+ pll_sclk = __APP_PLL_SCLK_ENABLE | __APP_PLL_SCLK_LRESETN |
+ __APP_PLL_SCLK_P0_1(3U) |
+ __APP_PLL_SCLK_JITLMT0_1(3U) |
+ __APP_PLL_SCLK_CNTLMT0_1(3U);
+ pll_fclk = __APP_PLL_LCLK_ENABLE | __APP_PLL_LCLK_LRESETN |
+ __APP_PLL_LCLK_RSEL200500 | __APP_PLL_LCLK_P0_1(3U) |
+ __APP_PLL_LCLK_JITLMT0_1(3U) |
+ __APP_PLL_LCLK_CNTLMT0_1(3U);
+ join_bits = readl(rb + BFA_IOC0_STATE_REG) &
+ BFA_IOC_CB_JOIN_MASK;
+ writel((BFI_IOC_UNINIT | join_bits), (rb + BFA_IOC0_STATE_REG));
+ join_bits = readl(rb + BFA_IOC1_STATE_REG) &
+ BFA_IOC_CB_JOIN_MASK;
+ writel((BFI_IOC_UNINIT | join_bits), (rb + BFA_IOC1_STATE_REG));
writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
- writel(__APP_PLL_212_LOGIC_SOFT_RESET, rb + APP_PLL_212_CTL_REG);
- writel(__APP_PLL_212_BYPASS | __APP_PLL_212_LOGIC_SOFT_RESET,
- rb + APP_PLL_212_CTL_REG);
- writel(__APP_PLL_400_LOGIC_SOFT_RESET, rb + APP_PLL_400_CTL_REG);
- writel(__APP_PLL_400_BYPASS | __APP_PLL_400_LOGIC_SOFT_RESET,
- rb + APP_PLL_400_CTL_REG);
+ writel(__APP_PLL_SCLK_LOGIC_SOFT_RESET, rb + APP_PLL_SCLK_CTL_REG);
+ writel(__APP_PLL_SCLK_BYPASS | __APP_PLL_SCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_SCLK_CTL_REG);
+ writel(__APP_PLL_LCLK_LOGIC_SOFT_RESET, rb + APP_PLL_LCLK_CTL_REG);
+ writel(__APP_PLL_LCLK_BYPASS | __APP_PLL_LCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_LCLK_CTL_REG);
udelay(2);
- writel(__APP_PLL_212_LOGIC_SOFT_RESET, rb + APP_PLL_212_CTL_REG);
- writel(__APP_PLL_400_LOGIC_SOFT_RESET, rb + APP_PLL_400_CTL_REG);
- writel(pll_sclk | __APP_PLL_212_LOGIC_SOFT_RESET,
- rb + APP_PLL_212_CTL_REG);
- writel(pll_fclk | __APP_PLL_400_LOGIC_SOFT_RESET,
- rb + APP_PLL_400_CTL_REG);
+ writel(__APP_PLL_SCLK_LOGIC_SOFT_RESET, rb + APP_PLL_SCLK_CTL_REG);
+ writel(__APP_PLL_LCLK_LOGIC_SOFT_RESET, rb + APP_PLL_LCLK_CTL_REG);
+ writel(pll_sclk | __APP_PLL_SCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_SCLK_CTL_REG);
+ writel(pll_fclk | __APP_PLL_LCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_LCLK_CTL_REG);
udelay(2000);
writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
- writel(pll_sclk, (rb + APP_PLL_212_CTL_REG));
- writel(pll_fclk, (rb + APP_PLL_400_CTL_REG));
+ writel(pll_sclk, (rb + APP_PLL_SCLK_CTL_REG));
+ writel(pll_fclk, (rb + APP_PLL_LCLK_CTL_REG));
return BFA_STATUS_OK;
}
diff --git a/drivers/scsi/bfa/bfa_ioc_ct.c b/drivers/scsi/bfa/bfa_ioc_ct.c
index 93612520f0d2..fb748291676b 100644
--- a/drivers/scsi/bfa/bfa_ioc_ct.c
+++ b/drivers/scsi/bfa/bfa_ioc_ct.c
@@ -1,23 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
#include "bfa_ioc.h"
-#include "bfi_ctreg.h"
+#include "bfi_reg.h"
#include "bfa_defs.h"
BFA_TRC_FILE(CNA, IOC_CT);
@@ -36,9 +29,6 @@ BFA_TRC_FILE(CNA, IOC_CT);
*/
static bfa_boolean_t bfa_ioc_ct_firmware_lock(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_firmware_unlock(struct bfa_ioc_s *ioc);
-static void bfa_ioc_ct_reg_init(struct bfa_ioc_s *ioc);
-static void bfa_ioc_ct_map_port(struct bfa_ioc_s *ioc);
-static void bfa_ioc_ct_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix);
static void bfa_ioc_ct_notify_fail(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_ownership_reset(struct bfa_ioc_s *ioc);
static bfa_boolean_t bfa_ioc_ct_sync_start(struct bfa_ioc_s *ioc);
@@ -46,31 +36,15 @@ static void bfa_ioc_ct_sync_join(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_sync_leave(struct bfa_ioc_s *ioc);
static void bfa_ioc_ct_sync_ack(struct bfa_ioc_s *ioc);
static bfa_boolean_t bfa_ioc_ct_sync_complete(struct bfa_ioc_s *ioc);
+static void bfa_ioc_ct_set_cur_ioc_fwstate(
+ struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
+static enum bfi_ioc_state bfa_ioc_ct_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc);
+static void bfa_ioc_ct_set_alt_ioc_fwstate(
+ struct bfa_ioc_s *ioc, enum bfi_ioc_state fwstate);
+static enum bfi_ioc_state bfa_ioc_ct_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc);
static struct bfa_ioc_hwif_s hwif_ct;
-
-/*
- * Called from bfa_ioc_attach() to map asic specific calls.
- */
-void
-bfa_ioc_set_ct_hwif(struct bfa_ioc_s *ioc)
-{
- hwif_ct.ioc_pll_init = bfa_ioc_ct_pll_init;
- hwif_ct.ioc_firmware_lock = bfa_ioc_ct_firmware_lock;
- hwif_ct.ioc_firmware_unlock = bfa_ioc_ct_firmware_unlock;
- hwif_ct.ioc_reg_init = bfa_ioc_ct_reg_init;
- hwif_ct.ioc_map_port = bfa_ioc_ct_map_port;
- hwif_ct.ioc_isr_mode_set = bfa_ioc_ct_isr_mode_set;
- hwif_ct.ioc_notify_fail = bfa_ioc_ct_notify_fail;
- hwif_ct.ioc_ownership_reset = bfa_ioc_ct_ownership_reset;
- hwif_ct.ioc_sync_start = bfa_ioc_ct_sync_start;
- hwif_ct.ioc_sync_join = bfa_ioc_ct_sync_join;
- hwif_ct.ioc_sync_leave = bfa_ioc_ct_sync_leave;
- hwif_ct.ioc_sync_ack = bfa_ioc_ct_sync_ack;
- hwif_ct.ioc_sync_complete = bfa_ioc_ct_sync_complete;
-
- ioc->ioc_hwif = &hwif_ct;
-}
+static struct bfa_ioc_hwif_s hwif_ct2;
/*
* Return true if firmware of current driver matches the running firmware.
@@ -82,19 +56,6 @@ bfa_ioc_ct_firmware_lock(struct bfa_ioc_s *ioc)
u32 usecnt;
struct bfi_ioc_image_hdr_s fwhdr;
- /*
- * Firmware match check is relevant only for CNA.
- */
- if (!ioc->cna)
- return BFA_TRUE;
-
- /*
- * If bios boot (flash based) -- do not increment usage count
- */
- if (bfa_cb_image_get_size(BFA_IOC_FWIMG_TYPE(ioc)) <
- BFA_IOC_FWIMG_MINSZ)
- return BFA_TRUE;
-
bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
usecnt = readl(ioc->ioc_regs.ioc_usage_reg);
@@ -103,6 +64,7 @@ bfa_ioc_ct_firmware_lock(struct bfa_ioc_s *ioc)
*/
if (usecnt == 0) {
writel(1, ioc->ioc_regs.ioc_usage_reg);
+ readl(ioc->ioc_regs.ioc_usage_sem_reg);
writel(1, ioc->ioc_regs.ioc_usage_sem_reg);
writel(0, ioc->ioc_regs.ioc_fail_sync);
bfa_trc(ioc, usecnt);
@@ -122,6 +84,7 @@ bfa_ioc_ct_firmware_lock(struct bfa_ioc_s *ioc)
*/
bfa_ioc_fwver_get(ioc, &fwhdr);
if (!bfa_ioc_fwver_cmp(ioc, &fwhdr)) {
+ readl(ioc->ioc_regs.ioc_usage_sem_reg);
writel(1, ioc->ioc_regs.ioc_usage_sem_reg);
bfa_trc(ioc, usecnt);
return BFA_FALSE;
@@ -132,6 +95,7 @@ bfa_ioc_ct_firmware_lock(struct bfa_ioc_s *ioc)
*/
usecnt++;
writel(usecnt, ioc->ioc_regs.ioc_usage_reg);
+ readl(ioc->ioc_regs.ioc_usage_sem_reg);
writel(1, ioc->ioc_regs.ioc_usage_sem_reg);
bfa_trc(ioc, usecnt);
return BFA_TRUE;
@@ -143,19 +107,6 @@ bfa_ioc_ct_firmware_unlock(struct bfa_ioc_s *ioc)
u32 usecnt;
/*
- * Firmware lock is relevant only for CNA.
- */
- if (!ioc->cna)
- return;
-
- /*
- * If bios boot (flash based) -- do not decrement usage count
- */
- if (bfa_cb_image_get_size(BFA_IOC_FWIMG_TYPE(ioc)) <
- BFA_IOC_FWIMG_MINSZ)
- return;
-
- /*
* decrement usage count
*/
bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
@@ -166,6 +117,7 @@ bfa_ioc_ct_firmware_unlock(struct bfa_ioc_s *ioc)
writel(usecnt, ioc->ioc_regs.ioc_usage_reg);
bfa_trc(ioc, usecnt);
+ readl(ioc->ioc_regs.ioc_usage_sem_reg);
writel(1, ioc->ioc_regs.ioc_usage_sem_reg);
}
@@ -175,14 +127,14 @@ bfa_ioc_ct_firmware_unlock(struct bfa_ioc_s *ioc)
static void
bfa_ioc_ct_notify_fail(struct bfa_ioc_s *ioc)
{
- if (ioc->cna) {
+ if (bfa_ioc_is_cna(ioc)) {
writel(__FW_INIT_HALT_P, ioc->ioc_regs.ll_halt);
writel(__FW_INIT_HALT_P, ioc->ioc_regs.alt_ll_halt);
/* Wait for halt to take effect */
readl(ioc->ioc_regs.ll_halt);
readl(ioc->ioc_regs.alt_ll_halt);
} else {
- writel(__PSS_ERR_STATUS_SET, ioc->ioc_regs.err_set);
+ writel(~0U, ioc->ioc_regs.err_set);
readl(ioc->ioc_regs.err_set);
}
}
@@ -190,7 +142,7 @@ bfa_ioc_ct_notify_fail(struct bfa_ioc_s *ioc)
/*
* Host to LPU mailbox message addresses
*/
-static struct { u32 hfn_mbox, lpu_mbox, hfn_pgn; } iocreg_fnreg[] = {
+static struct { u32 hfn_mbox, lpu_mbox, hfn_pgn; } ct_fnreg[] = {
{ HOSTFN0_LPU_MBOX0_0, LPU_HOSTFN0_MBOX0_0, HOST_PAGE_NUM_FN0 },
{ HOSTFN1_LPU_MBOX0_8, LPU_HOSTFN1_MBOX0_8, HOST_PAGE_NUM_FN1 },
{ HOSTFN2_LPU_MBOX0_0, LPU_HOSTFN2_MBOX0_0, HOST_PAGE_NUM_FN2 },
@@ -200,21 +152,31 @@ static struct { u32 hfn_mbox, lpu_mbox, hfn_pgn; } iocreg_fnreg[] = {
/*
* Host <-> LPU mailbox command/status registers - port 0
*/
-static struct { u32 hfn, lpu; } iocreg_mbcmd_p0[] = {
- { HOSTFN0_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN0_MBOX0_CMD_STAT },
- { HOSTFN1_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN1_MBOX0_CMD_STAT },
- { HOSTFN2_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN2_MBOX0_CMD_STAT },
- { HOSTFN3_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN3_MBOX0_CMD_STAT }
+static struct { u32 hfn, lpu; } ct_p0reg[] = {
+ { HOSTFN0_LPU0_CMD_STAT, LPU0_HOSTFN0_CMD_STAT },
+ { HOSTFN1_LPU0_CMD_STAT, LPU0_HOSTFN1_CMD_STAT },
+ { HOSTFN2_LPU0_CMD_STAT, LPU0_HOSTFN2_CMD_STAT },
+ { HOSTFN3_LPU0_CMD_STAT, LPU0_HOSTFN3_CMD_STAT }
};
/*
* Host <-> LPU mailbox command/status registers - port 1
*/
-static struct { u32 hfn, lpu; } iocreg_mbcmd_p1[] = {
- { HOSTFN0_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN0_MBOX0_CMD_STAT },
- { HOSTFN1_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN1_MBOX0_CMD_STAT },
- { HOSTFN2_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN2_MBOX0_CMD_STAT },
- { HOSTFN3_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN3_MBOX0_CMD_STAT }
+static struct { u32 hfn, lpu; } ct_p1reg[] = {
+ { HOSTFN0_LPU1_CMD_STAT, LPU1_HOSTFN0_CMD_STAT },
+ { HOSTFN1_LPU1_CMD_STAT, LPU1_HOSTFN1_CMD_STAT },
+ { HOSTFN2_LPU1_CMD_STAT, LPU1_HOSTFN2_CMD_STAT },
+ { HOSTFN3_LPU1_CMD_STAT, LPU1_HOSTFN3_CMD_STAT }
+};
+
+static struct { uint32_t hfn_mbox, lpu_mbox, hfn_pgn, hfn, lpu, lpu_read; }
+ ct2_reg[] = {
+ { CT2_HOSTFN_LPU0_MBOX0, CT2_LPU0_HOSTFN_MBOX0, CT2_HOSTFN_PAGE_NUM,
+ CT2_HOSTFN_LPU0_CMD_STAT, CT2_LPU0_HOSTFN_CMD_STAT,
+ CT2_HOSTFN_LPU0_READ_STAT},
+ { CT2_HOSTFN_LPU1_MBOX0, CT2_LPU1_HOSTFN_MBOX0, CT2_HOSTFN_PAGE_NUM,
+ CT2_HOSTFN_LPU1_CMD_STAT, CT2_LPU1_HOSTFN_CMD_STAT,
+ CT2_HOSTFN_LPU1_READ_STAT},
};
static void
@@ -225,24 +187,24 @@ bfa_ioc_ct_reg_init(struct bfa_ioc_s *ioc)
rb = bfa_ioc_bar0(ioc);
- ioc->ioc_regs.hfn_mbox = rb + iocreg_fnreg[pcifn].hfn_mbox;
- ioc->ioc_regs.lpu_mbox = rb + iocreg_fnreg[pcifn].lpu_mbox;
- ioc->ioc_regs.host_page_num_fn = rb + iocreg_fnreg[pcifn].hfn_pgn;
+ ioc->ioc_regs.hfn_mbox = rb + ct_fnreg[pcifn].hfn_mbox;
+ ioc->ioc_regs.lpu_mbox = rb + ct_fnreg[pcifn].lpu_mbox;
+ ioc->ioc_regs.host_page_num_fn = rb + ct_fnreg[pcifn].hfn_pgn;
if (ioc->port_id == 0) {
ioc->ioc_regs.heartbeat = rb + BFA_IOC0_HBEAT_REG;
ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC0_STATE_REG;
ioc->ioc_regs.alt_ioc_fwstate = rb + BFA_IOC1_STATE_REG;
- ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].hfn;
- ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].lpu;
+ ioc->ioc_regs.hfn_mbox_cmd = rb + ct_p0reg[pcifn].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + ct_p0reg[pcifn].lpu;
ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0;
ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P1;
} else {
ioc->ioc_regs.heartbeat = (rb + BFA_IOC1_HBEAT_REG);
ioc->ioc_regs.ioc_fwstate = (rb + BFA_IOC1_STATE_REG);
ioc->ioc_regs.alt_ioc_fwstate = rb + BFA_IOC0_STATE_REG;
- ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].hfn;
- ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].lpu;
+ ioc->ioc_regs.hfn_mbox_cmd = rb + ct_p1reg[pcifn].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + ct_p1reg[pcifn].lpu;
ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1;
ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P0;
}
@@ -252,8 +214,8 @@ bfa_ioc_ct_reg_init(struct bfa_ioc_s *ioc)
*/
ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG);
ioc->ioc_regs.pss_err_status_reg = (rb + PSS_ERR_STATUS_REG);
- ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_425_CTL_REG);
- ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_312_CTL_REG);
+ ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_LCLK_CTL_REG);
+ ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_SCLK_CTL_REG);
/*
* IOC semaphore registers and serialization
@@ -276,6 +238,64 @@ bfa_ioc_ct_reg_init(struct bfa_ioc_s *ioc)
ioc->ioc_regs.err_set = (rb + ERR_SET_REG);
}
+static void
+bfa_ioc_ct2_reg_init(struct bfa_ioc_s *ioc)
+{
+ void __iomem *rb;
+ int port = bfa_ioc_portid(ioc);
+
+ rb = bfa_ioc_bar0(ioc);
+
+ ioc->ioc_regs.hfn_mbox = rb + ct2_reg[port].hfn_mbox;
+ ioc->ioc_regs.lpu_mbox = rb + ct2_reg[port].lpu_mbox;
+ ioc->ioc_regs.host_page_num_fn = rb + ct2_reg[port].hfn_pgn;
+ ioc->ioc_regs.hfn_mbox_cmd = rb + ct2_reg[port].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + ct2_reg[port].lpu;
+ ioc->ioc_regs.lpu_read_stat = rb + ct2_reg[port].lpu_read;
+
+ if (port == 0) {
+ ioc->ioc_regs.heartbeat = rb + CT2_BFA_IOC0_HBEAT_REG;
+ ioc->ioc_regs.ioc_fwstate = rb + CT2_BFA_IOC0_STATE_REG;
+ ioc->ioc_regs.alt_ioc_fwstate = rb + CT2_BFA_IOC1_STATE_REG;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0;
+ ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P1;
+ } else {
+ ioc->ioc_regs.heartbeat = (rb + CT2_BFA_IOC1_HBEAT_REG);
+ ioc->ioc_regs.ioc_fwstate = (rb + CT2_BFA_IOC1_STATE_REG);
+ ioc->ioc_regs.alt_ioc_fwstate = rb + CT2_BFA_IOC0_STATE_REG;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1;
+ ioc->ioc_regs.alt_ll_halt = rb + FW_INIT_HALT_P0;
+ }
+
+ /*
+ * PSS control registers
+ */
+ ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG);
+ ioc->ioc_regs.pss_err_status_reg = (rb + PSS_ERR_STATUS_REG);
+ ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + CT2_APP_PLL_LCLK_CTL_REG);
+ ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + CT2_APP_PLL_SCLK_CTL_REG);
+
+ /*
+ * IOC semaphore registers and serialization
+ */
+ ioc->ioc_regs.ioc_sem_reg = (rb + CT2_HOST_SEM0_REG);
+ ioc->ioc_regs.ioc_usage_sem_reg = (rb + CT2_HOST_SEM1_REG);
+ ioc->ioc_regs.ioc_init_sem_reg = (rb + CT2_HOST_SEM2_REG);
+ ioc->ioc_regs.ioc_usage_reg = (rb + CT2_BFA_FW_USE_COUNT);
+ ioc->ioc_regs.ioc_fail_sync = (rb + CT2_BFA_IOC_FAIL_SYNC);
+
+ /*
+ * sram memory access
+ */
+ ioc->ioc_regs.smem_page_start = (rb + PSS_SMEM_PAGE_START);
+ ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CT;
+
+ /*
+ * err set reg : for notification of hb failure in fcmode
+ */
+ ioc->ioc_regs.err_set = (rb + ERR_SET_REG);
+}
+
/*
* Initialize IOC to port mapping.
*/
@@ -298,6 +318,19 @@ bfa_ioc_ct_map_port(struct bfa_ioc_s *ioc)
bfa_trc(ioc, ioc->port_id);
}
+static void
+bfa_ioc_ct2_map_port(struct bfa_ioc_s *ioc)
+{
+ void __iomem *rb = ioc->pcidev.pci_bar_kva;
+ u32 r32;
+
+ r32 = readl(rb + CT2_HOSTFN_PERSONALITY0);
+ ioc->port_id = ((r32 & __FC_LL_PORT_MAP__MK) >> __FC_LL_PORT_MAP__SH);
+
+ bfa_trc(ioc, bfa_ioc_pcifn(ioc));
+ bfa_trc(ioc, ioc->port_id);
+}
+
/*
* Set interrupt mode for a function: INTX or MSIX
*/
@@ -316,7 +349,7 @@ bfa_ioc_ct_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix)
/*
* If already in desired mode, do not change anything
*/
- if (!msix && mode)
+ if ((!msix && mode) || (msix && !mode))
return;
if (msix)
@@ -331,6 +364,20 @@ bfa_ioc_ct_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix)
writel(r32, rb + FNC_PERS_REG);
}
+static bfa_boolean_t
+bfa_ioc_ct2_lpu_read_stat(struct bfa_ioc_s *ioc)
+{
+ u32 r32;
+
+ r32 = readl(ioc->ioc_regs.lpu_read_stat);
+ if (r32) {
+ writel(1, ioc->ioc_regs.lpu_read_stat);
+ return BFA_TRUE;
+ }
+
+ return BFA_FALSE;
+}
+
/*
* Cleanup hw semaphore and usecnt registers
*/
@@ -338,12 +385,12 @@ static void
bfa_ioc_ct_ownership_reset(struct bfa_ioc_s *ioc)
{
- if (ioc->cna) {
- bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
- writel(0, ioc->ioc_regs.ioc_usage_reg);
- writel(1, ioc->ioc_regs.ioc_usage_sem_reg);
- }
+ bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg);
+ writel(0, ioc->ioc_regs.ioc_usage_reg);
+ readl(ioc->ioc_regs.ioc_usage_sem_reg);
+ writel(1, ioc->ioc_regs.ioc_usage_sem_reg);
+ writel(0, ioc->ioc_regs.ioc_fail_sync);
/*
* Read the hw sem reg to make sure that it is locked
* before we clear it. If it is not locked, writing 1
@@ -450,31 +497,102 @@ bfa_ioc_ct_sync_complete(struct bfa_ioc_s *ioc)
}
/*
- * Check the firmware state to know if pll_init has been completed already
+ * Called from bfa_ioc_attach() to map asic specific calls.
*/
-bfa_boolean_t
-bfa_ioc_ct_pll_init_complete(void __iomem *rb)
+static void
+bfa_ioc_set_ctx_hwif(struct bfa_ioc_s *ioc, struct bfa_ioc_hwif_s *hwif)
{
- if ((readl(rb + BFA_IOC0_STATE_REG) == BFI_IOC_OP) ||
- (readl(rb + BFA_IOC1_STATE_REG) == BFI_IOC_OP))
- return BFA_TRUE;
+ hwif->ioc_firmware_lock = bfa_ioc_ct_firmware_lock;
+ hwif->ioc_firmware_unlock = bfa_ioc_ct_firmware_unlock;
+ hwif->ioc_notify_fail = bfa_ioc_ct_notify_fail;
+ hwif->ioc_ownership_reset = bfa_ioc_ct_ownership_reset;
+ hwif->ioc_sync_start = bfa_ioc_ct_sync_start;
+ hwif->ioc_sync_join = bfa_ioc_ct_sync_join;
+ hwif->ioc_sync_leave = bfa_ioc_ct_sync_leave;
+ hwif->ioc_sync_ack = bfa_ioc_ct_sync_ack;
+ hwif->ioc_sync_complete = bfa_ioc_ct_sync_complete;
+ hwif->ioc_set_fwstate = bfa_ioc_ct_set_cur_ioc_fwstate;
+ hwif->ioc_get_fwstate = bfa_ioc_ct_get_cur_ioc_fwstate;
+ hwif->ioc_set_alt_fwstate = bfa_ioc_ct_set_alt_ioc_fwstate;
+ hwif->ioc_get_alt_fwstate = bfa_ioc_ct_get_alt_ioc_fwstate;
+}
- return BFA_FALSE;
+/*
+ * Called from bfa_ioc_attach() to map asic specific calls.
+ */
+void
+bfa_ioc_set_ct_hwif(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_set_ctx_hwif(ioc, &hwif_ct);
+
+ hwif_ct.ioc_pll_init = bfa_ioc_ct_pll_init;
+ hwif_ct.ioc_reg_init = bfa_ioc_ct_reg_init;
+ hwif_ct.ioc_map_port = bfa_ioc_ct_map_port;
+ hwif_ct.ioc_isr_mode_set = bfa_ioc_ct_isr_mode_set;
+ ioc->ioc_hwif = &hwif_ct;
+}
+
+/*
+ * Called from bfa_ioc_attach() to map asic specific calls.
+ */
+void
+bfa_ioc_set_ct2_hwif(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_set_ctx_hwif(ioc, &hwif_ct2);
+
+ hwif_ct2.ioc_pll_init = bfa_ioc_ct2_pll_init;
+ hwif_ct2.ioc_reg_init = bfa_ioc_ct2_reg_init;
+ hwif_ct2.ioc_map_port = bfa_ioc_ct2_map_port;
+ hwif_ct2.ioc_lpu_read_stat = bfa_ioc_ct2_lpu_read_stat;
+ hwif_ct2.ioc_isr_mode_set = NULL;
+ ioc->ioc_hwif = &hwif_ct2;
+}
+
+/*
+ * Workaround for MSI-X resource allocation for catapult-2 with no asic block
+ */
+#define HOSTFN_MSIX_DEFAULT 64
+#define HOSTFN_MSIX_VT_INDEX_MBOX_ERR 0x30138
+#define HOSTFN_MSIX_VT_OFST_NUMVT 0x3013c
+#define __MSIX_VT_NUMVT__MK 0x003ff800
+#define __MSIX_VT_NUMVT__SH 11
+#define __MSIX_VT_NUMVT_(_v) ((_v) << __MSIX_VT_NUMVT__SH)
+#define __MSIX_VT_OFST_ 0x000007ff
+void
+bfa_ioc_ct2_poweron(struct bfa_ioc_s *ioc)
+{
+ void __iomem *rb = ioc->pcidev.pci_bar_kva;
+ u32 r32;
+
+ r32 = readl(rb + HOSTFN_MSIX_VT_OFST_NUMVT);
+ if (r32 & __MSIX_VT_NUMVT__MK) {
+ writel(r32 & __MSIX_VT_OFST_,
+ rb + HOSTFN_MSIX_VT_INDEX_MBOX_ERR);
+ return;
+ }
+
+ writel(__MSIX_VT_NUMVT_(HOSTFN_MSIX_DEFAULT - 1) |
+ HOSTFN_MSIX_DEFAULT * bfa_ioc_pcifn(ioc),
+ rb + HOSTFN_MSIX_VT_OFST_NUMVT);
+ writel(HOSTFN_MSIX_DEFAULT * bfa_ioc_pcifn(ioc),
+ rb + HOSTFN_MSIX_VT_INDEX_MBOX_ERR);
}
bfa_status_t
-bfa_ioc_ct_pll_init(void __iomem *rb, bfa_boolean_t fcmode)
+bfa_ioc_ct_pll_init(void __iomem *rb, enum bfi_asic_mode mode)
{
u32 pll_sclk, pll_fclk, r32;
+ bfa_boolean_t fcmode = (mode == BFI_ASIC_MODE_FC);
+
+ pll_sclk = __APP_PLL_SCLK_LRESETN | __APP_PLL_SCLK_ENARST |
+ __APP_PLL_SCLK_RSEL200500 | __APP_PLL_SCLK_P0_1(3U) |
+ __APP_PLL_SCLK_JITLMT0_1(3U) |
+ __APP_PLL_SCLK_CNTLMT0_1(1U);
+ pll_fclk = __APP_PLL_LCLK_LRESETN | __APP_PLL_LCLK_ENARST |
+ __APP_PLL_LCLK_RSEL200500 | __APP_PLL_LCLK_P0_1(3U) |
+ __APP_PLL_LCLK_JITLMT0_1(3U) |
+ __APP_PLL_LCLK_CNTLMT0_1(1U);
- pll_sclk = __APP_PLL_312_LRESETN | __APP_PLL_312_ENARST |
- __APP_PLL_312_RSEL200500 | __APP_PLL_312_P0_1(3U) |
- __APP_PLL_312_JITLMT0_1(3U) |
- __APP_PLL_312_CNTLMT0_1(1U);
- pll_fclk = __APP_PLL_425_LRESETN | __APP_PLL_425_ENARST |
- __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(3U) |
- __APP_PLL_425_JITLMT0_1(3U) |
- __APP_PLL_425_CNTLMT0_1(1U);
if (fcmode) {
writel(0, (rb + OP_MODE));
writel(__APP_EMS_CMLCKSEL | __APP_EMS_REFCKBUFEN2 |
@@ -491,20 +609,21 @@ bfa_ioc_ct_pll_init(void __iomem *rb, bfa_boolean_t fcmode)
writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
writel(0xffffffffU, (rb + HOSTFN0_INT_MSK));
writel(0xffffffffU, (rb + HOSTFN1_INT_MSK));
- writel(pll_sclk | __APP_PLL_312_LOGIC_SOFT_RESET,
- rb + APP_PLL_312_CTL_REG);
- writel(pll_fclk | __APP_PLL_425_LOGIC_SOFT_RESET,
- rb + APP_PLL_425_CTL_REG);
- writel(pll_sclk | __APP_PLL_312_LOGIC_SOFT_RESET | __APP_PLL_312_ENABLE,
- rb + APP_PLL_312_CTL_REG);
- writel(pll_fclk | __APP_PLL_425_LOGIC_SOFT_RESET | __APP_PLL_425_ENABLE,
- rb + APP_PLL_425_CTL_REG);
+ writel(pll_sclk | __APP_PLL_SCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_SCLK_CTL_REG);
+ writel(pll_fclk | __APP_PLL_LCLK_LOGIC_SOFT_RESET,
+ rb + APP_PLL_LCLK_CTL_REG);
+ writel(pll_sclk | __APP_PLL_SCLK_LOGIC_SOFT_RESET |
+ __APP_PLL_SCLK_ENABLE, rb + APP_PLL_SCLK_CTL_REG);
+ writel(pll_fclk | __APP_PLL_LCLK_LOGIC_SOFT_RESET |
+ __APP_PLL_LCLK_ENABLE, rb + APP_PLL_LCLK_CTL_REG);
readl(rb + HOSTFN0_INT_MSK);
udelay(2000);
writel(0xffffffffU, (rb + HOSTFN0_INT_STATUS));
writel(0xffffffffU, (rb + HOSTFN1_INT_STATUS));
- writel(pll_sclk | __APP_PLL_312_ENABLE, rb + APP_PLL_312_CTL_REG);
- writel(pll_fclk | __APP_PLL_425_ENABLE, rb + APP_PLL_425_CTL_REG);
+ writel(pll_sclk | __APP_PLL_SCLK_ENABLE, rb + APP_PLL_SCLK_CTL_REG);
+ writel(pll_fclk | __APP_PLL_LCLK_ENABLE, rb + APP_PLL_LCLK_CTL_REG);
+
if (!fcmode) {
writel(__PMM_1T_RESET_P, (rb + PMM_1T_RESET_REG_P0));
writel(__PMM_1T_RESET_P, (rb + PMM_1T_RESET_REG_P1));
@@ -524,3 +643,348 @@ bfa_ioc_ct_pll_init(void __iomem *rb, bfa_boolean_t fcmode)
writel(0, (rb + MBIST_CTL_REG));
return BFA_STATUS_OK;
}
+
+static void
+bfa_ioc_ct2_sclk_init(void __iomem *rb)
+{
+ u32 r32;
+
+ /*
+ * put s_clk PLL and PLL FSM in reset
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ r32 &= ~(__APP_PLL_SCLK_ENABLE | __APP_PLL_SCLK_LRESETN);
+ r32 |= (__APP_PLL_SCLK_ENARST | __APP_PLL_SCLK_BYPASS |
+ __APP_PLL_SCLK_LOGIC_SOFT_RESET);
+ writel(r32, (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * Ignore mode and program for the max clock (which is FC16)
+ * Firmware/NFC will do the PLL init appropiately
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ r32 &= ~(__APP_PLL_SCLK_REFCLK_SEL | __APP_PLL_SCLK_CLK_DIV2);
+ writel(r32, (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * while doing PLL init dont clock gate ethernet subsystem
+ */
+ r32 = readl((rb + CT2_CHIP_MISC_PRG));
+ writel(r32 | __ETH_CLK_ENABLE_PORT0, (rb + CT2_CHIP_MISC_PRG));
+
+ r32 = readl((rb + CT2_PCIE_MISC_REG));
+ writel(r32 | __ETH_CLK_ENABLE_PORT1, (rb + CT2_PCIE_MISC_REG));
+
+ /*
+ * set sclk value
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ r32 &= (__P_SCLK_PLL_LOCK | __APP_PLL_SCLK_REFCLK_SEL |
+ __APP_PLL_SCLK_CLK_DIV2);
+ writel(r32 | 0x1061731b, (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * poll for s_clk lock or delay 1ms
+ */
+ udelay(1000);
+}
+
+static void
+bfa_ioc_ct2_lclk_init(void __iomem *rb)
+{
+ u32 r32;
+
+ /*
+ * put l_clk PLL and PLL FSM in reset
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ r32 &= ~(__APP_PLL_LCLK_ENABLE | __APP_PLL_LCLK_LRESETN);
+ r32 |= (__APP_PLL_LCLK_ENARST | __APP_PLL_LCLK_BYPASS |
+ __APP_PLL_LCLK_LOGIC_SOFT_RESET);
+ writel(r32, (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /*
+ * set LPU speed (set for FC16 which will work for other modes)
+ */
+ r32 = readl((rb + CT2_CHIP_MISC_PRG));
+ writel(r32, (rb + CT2_CHIP_MISC_PRG));
+
+ /*
+ * set LPU half speed (set for FC16 which will work for other modes)
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ writel(r32, (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /*
+ * set lclk for mode (set for FC16)
+ */
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ r32 &= (__P_LCLK_PLL_LOCK | __APP_LPUCLK_HALFSPEED);
+ r32 |= 0x20c1731b;
+ writel(r32, (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+ /*
+ * poll for s_clk lock or delay 1ms
+ */
+ udelay(1000);
+}
+
+static void
+bfa_ioc_ct2_mem_init(void __iomem *rb)
+{
+ u32 r32;
+
+ r32 = readl((rb + PSS_CTL_REG));
+ r32 &= ~__PSS_LMEM_RESET;
+ writel(r32, (rb + PSS_CTL_REG));
+ udelay(1000);
+
+ writel(__EDRAM_BISTR_START, (rb + CT2_MBIST_CTL_REG));
+ udelay(1000);
+ writel(0, (rb + CT2_MBIST_CTL_REG));
+}
+
+static void
+bfa_ioc_ct2_mac_reset(void __iomem *rb)
+{
+ /* put port0, port1 MAC & AHB in reset */
+ writel((__CSI_MAC_RESET | __CSI_MAC_AHB_RESET),
+ rb + CT2_CSI_MAC_CONTROL_REG(0));
+ writel((__CSI_MAC_RESET | __CSI_MAC_AHB_RESET),
+ rb + CT2_CSI_MAC_CONTROL_REG(1));
+}
+
+static void
+bfa_ioc_ct2_enable_flash(void __iomem *rb)
+{
+ u32 r32;
+
+ r32 = readl((rb + PSS_GPIO_OUT_REG));
+ writel(r32 & ~1, (rb + PSS_GPIO_OUT_REG));
+ r32 = readl((rb + PSS_GPIO_OE_REG));
+ writel(r32 | 1, (rb + PSS_GPIO_OE_REG));
+}
+
+#define CT2_NFC_MAX_DELAY 1000
+#define CT2_NFC_PAUSE_MAX_DELAY 4000
+#define CT2_NFC_VER_VALID 0x147
+#define CT2_NFC_STATE_RUNNING 0x20000001
+#define BFA_IOC_PLL_POLL 1000000
+
+static bfa_boolean_t
+bfa_ioc_ct2_nfc_halted(void __iomem *rb)
+{
+ u32 r32;
+
+ r32 = readl(rb + CT2_NFC_CSR_SET_REG);
+ if (r32 & __NFC_CONTROLLER_HALTED)
+ return BFA_TRUE;
+
+ return BFA_FALSE;
+}
+
+static void
+bfa_ioc_ct2_nfc_halt(void __iomem *rb)
+{
+ int i;
+
+ writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_SET_REG);
+ for (i = 0; i < CT2_NFC_MAX_DELAY; i++) {
+ if (bfa_ioc_ct2_nfc_halted(rb))
+ break;
+ udelay(1000);
+ }
+ WARN_ON(!bfa_ioc_ct2_nfc_halted(rb));
+}
+
+static void
+bfa_ioc_ct2_nfc_resume(void __iomem *rb)
+{
+ u32 r32;
+ int i;
+
+ writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_CLR_REG);
+ for (i = 0; i < CT2_NFC_MAX_DELAY; i++) {
+ r32 = readl(rb + CT2_NFC_CSR_SET_REG);
+ if (!(r32 & __NFC_CONTROLLER_HALTED))
+ return;
+ udelay(1000);
+ }
+ WARN_ON(1);
+}
+
+static void
+bfa_ioc_ct2_clk_reset(void __iomem *rb)
+{
+ u32 r32;
+
+ bfa_ioc_ct2_sclk_init(rb);
+ bfa_ioc_ct2_lclk_init(rb);
+
+ /*
+ * release soft reset on s_clk & l_clk
+ */
+ r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
+ writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET,
+ (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
+ writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET,
+ (rb + CT2_APP_PLL_LCLK_CTL_REG));
+
+}
+
+static void
+bfa_ioc_ct2_nfc_clk_reset(void __iomem *rb)
+{
+ u32 r32, i;
+
+ r32 = readl((rb + PSS_CTL_REG));
+ r32 |= (__PSS_LPU0_RESET | __PSS_LPU1_RESET);
+ writel(r32, (rb + PSS_CTL_REG));
+
+ writel(__RESET_AND_START_SCLK_LCLK_PLLS, rb + CT2_CSI_FW_CTL_SET_REG);
+
+ for (i = 0; i < BFA_IOC_PLL_POLL; i++) {
+ r32 = readl(rb + CT2_NFC_FLASH_STS_REG);
+
+ if ((r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS))
+ break;
+ }
+ WARN_ON(!(r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS));
+
+ for (i = 0; i < BFA_IOC_PLL_POLL; i++) {
+ r32 = readl(rb + CT2_NFC_FLASH_STS_REG);
+
+ if (!(r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS))
+ break;
+ }
+ WARN_ON((r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS));
+
+ r32 = readl(rb + CT2_CSI_FW_CTL_REG);
+ WARN_ON((r32 & __RESET_AND_START_SCLK_LCLK_PLLS));
+}
+
+static void
+bfa_ioc_ct2_wait_till_nfc_running(void __iomem *rb)
+{
+ u32 r32;
+ int i;
+
+ if (bfa_ioc_ct2_nfc_halted(rb))
+ bfa_ioc_ct2_nfc_resume(rb);
+ for (i = 0; i < CT2_NFC_PAUSE_MAX_DELAY; i++) {
+ r32 = readl(rb + CT2_NFC_STS_REG);
+ if (r32 == CT2_NFC_STATE_RUNNING)
+ return;
+ udelay(1000);
+ }
+
+ r32 = readl(rb + CT2_NFC_STS_REG);
+ WARN_ON(!(r32 == CT2_NFC_STATE_RUNNING));
+}
+
+bfa_status_t
+bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode)
+{
+ u32 wgn, r32, nfc_ver;
+
+ wgn = readl(rb + CT2_WGN_STATUS);
+
+ if (wgn == (__WGN_READY | __GLBL_PF_VF_CFG_RDY)) {
+ /*
+ * If flash is corrupted, enable flash explicitly
+ */
+ bfa_ioc_ct2_clk_reset(rb);
+ bfa_ioc_ct2_enable_flash(rb);
+
+ bfa_ioc_ct2_mac_reset(rb);
+
+ bfa_ioc_ct2_clk_reset(rb);
+ bfa_ioc_ct2_enable_flash(rb);
+
+ } else {
+ nfc_ver = readl(rb + CT2_RSC_GPR15_REG);
+
+ if ((nfc_ver >= CT2_NFC_VER_VALID) &&
+ (wgn == (__A2T_AHB_LOAD | __WGN_READY))) {
+
+ bfa_ioc_ct2_wait_till_nfc_running(rb);
+
+ bfa_ioc_ct2_nfc_clk_reset(rb);
+ } else {
+ bfa_ioc_ct2_nfc_halt(rb);
+
+ bfa_ioc_ct2_clk_reset(rb);
+ bfa_ioc_ct2_mac_reset(rb);
+ bfa_ioc_ct2_clk_reset(rb);
+
+ }
+ }
+ /*
+ * The very first PCIe DMA Read done by LPU fails with a fatal error,
+ * when Address Translation Cache (ATC) has been enabled by system BIOS.
+ *
+ * Workaround:
+ * Disable Invalidated Tag Match Enable capability by setting the bit 26
+ * of CHIP_MISC_PRG to 0, by default it is set to 1.
+ */
+ r32 = readl(rb + CT2_CHIP_MISC_PRG);
+ writel((r32 & 0xfbffffff), (rb + CT2_CHIP_MISC_PRG));
+
+ /*
+ * Mask the interrupts and clear any
+ * pending interrupts left by BIOS/EFI
+ */
+
+ writel(1, (rb + CT2_LPU0_HOSTFN_MBOX0_MSK));
+ writel(1, (rb + CT2_LPU1_HOSTFN_MBOX0_MSK));
+
+ /* For first time initialization, no need to clear interrupts */
+ r32 = readl(rb + HOST_SEM5_REG);
+ if (r32 & 0x1) {
+ r32 = readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ if (r32 == 1) {
+ writel(1, (rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ }
+ r32 = readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
+ if (r32 == 1) {
+ writel(1, (rb + CT2_LPU1_HOSTFN_CMD_STAT));
+ readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
+ }
+ }
+
+ bfa_ioc_ct2_mem_init(rb);
+
+ writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC0_STATE_REG));
+ writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC1_STATE_REG));
+
+ return BFA_STATUS_OK;
+}
+
+static void
+bfa_ioc_ct_set_cur_ioc_fwstate(struct bfa_ioc_s *ioc,
+ enum bfi_ioc_state fwstate)
+{
+ writel(fwstate, ioc->ioc_regs.ioc_fwstate);
+}
+
+static enum bfi_ioc_state
+bfa_ioc_ct_get_cur_ioc_fwstate(struct bfa_ioc_s *ioc)
+{
+ return (enum bfi_ioc_state)readl(ioc->ioc_regs.ioc_fwstate);
+}
+
+static void
+bfa_ioc_ct_set_alt_ioc_fwstate(struct bfa_ioc_s *ioc,
+ enum bfi_ioc_state fwstate)
+{
+ writel(fwstate, ioc->ioc_regs.alt_ioc_fwstate);
+}
+
+static enum bfi_ioc_state
+bfa_ioc_ct_get_alt_ioc_fwstate(struct bfa_ioc_s *ioc)
+{
+ return (enum bfi_ioc_state) readl(ioc->ioc_regs.alt_ioc_fwstate);
+}
diff --git a/drivers/scsi/bfa/bfa_modules.h b/drivers/scsi/bfa/bfa_modules.h
index ab79ff6fdeea..ed29ebda30da 100644
--- a/drivers/scsi/bfa/bfa_modules.h
+++ b/drivers/scsi/bfa/bfa_modules.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -29,14 +22,23 @@
#include "bfa_port.h"
struct bfa_modules_s {
+ struct bfa_fcdiag_s fcdiag; /* fcdiag module */
struct bfa_fcport_s fcport; /* fc port module */
struct bfa_fcxp_mod_s fcxp_mod; /* fcxp module */
struct bfa_lps_mod_s lps_mod; /* fcxp module */
struct bfa_uf_mod_s uf_mod; /* unsolicited frame module */
struct bfa_rport_mod_s rport_mod; /* remote port module */
- struct bfa_fcpim_mod_s fcpim_mod; /* FCP initiator module */
+ struct bfa_fcp_mod_s fcp_mod; /* FCP initiator module */
struct bfa_sgpg_mod_s sgpg_mod; /* SG page module */
struct bfa_port_s port; /* Physical port module */
+ struct bfa_ablk_s ablk; /* ASIC block config module */
+ struct bfa_cee_s cee; /* CEE Module */
+ struct bfa_sfp_s sfp; /* SFP module */
+ struct bfa_flash_s flash; /* flash module */
+ struct bfa_diag_s diag_mod; /* diagnostics module */
+ struct bfa_phy_s phy; /* phy module */
+ struct bfa_dconf_mod_s dconf_mod; /* DCONF common module */
+ struct bfa_fru_s fru; /* fru module */
};
/*
@@ -51,55 +53,8 @@ enum {
BFA_TRC_HAL_IOCFC_CB = 5,
};
-
-/*
- * Macro to define a new BFA module
- */
-#define BFA_MODULE(__mod) \
- static void bfa_ ## __mod ## _meminfo( \
- struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, \
- u32 *dm_len); \
- static void bfa_ ## __mod ## _attach(struct bfa_s *bfa, \
- void *bfad, struct bfa_iocfc_cfg_s *cfg, \
- struct bfa_meminfo_s *meminfo, \
- struct bfa_pcidev_s *pcidev); \
- static void bfa_ ## __mod ## _detach(struct bfa_s *bfa); \
- static void bfa_ ## __mod ## _start(struct bfa_s *bfa); \
- static void bfa_ ## __mod ## _stop(struct bfa_s *bfa); \
- static void bfa_ ## __mod ## _iocdisable(struct bfa_s *bfa); \
- \
- extern struct bfa_module_s hal_mod_ ## __mod; \
- struct bfa_module_s hal_mod_ ## __mod = { \
- bfa_ ## __mod ## _meminfo, \
- bfa_ ## __mod ## _attach, \
- bfa_ ## __mod ## _detach, \
- bfa_ ## __mod ## _start, \
- bfa_ ## __mod ## _stop, \
- bfa_ ## __mod ## _iocdisable, \
- }
-
#define BFA_CACHELINE_SZ (256)
-/*
- * Structure used to interact between different BFA sub modules
- *
- * Each sub module needs to implement only the entry points relevant to it (and
- * can leave entry points as NULL)
- */
-struct bfa_module_s {
- void (*meminfo) (struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len);
- void (*attach) (struct bfa_s *bfa, void *bfad,
- struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo,
- struct bfa_pcidev_s *pcidev);
- void (*detach) (struct bfa_s *bfa);
- void (*start) (struct bfa_s *bfa);
- void (*stop) (struct bfa_s *bfa);
- void (*iocdisable) (struct bfa_s *bfa);
-};
-
-
struct bfa_s {
void *bfad; /* BFA driver instance */
struct bfa_plog_s *plog; /* portlog buffer */
@@ -109,19 +64,59 @@ struct bfa_s {
struct bfa_timer_mod_s timer_mod; /* timer module */
struct bfa_modules_s modules; /* BFA modules */
struct list_head comp_q; /* pending completions */
- bfa_boolean_t rme_process; /* RME processing enabled */
+ bfa_boolean_t queue_process; /* queue processing enabled */
struct list_head reqq_waitq[BFI_IOC_MAX_CQS];
bfa_boolean_t fcs; /* FCS is attached to BFA */
struct bfa_msix_s msix;
+ int bfa_aen_seq;
+ bfa_boolean_t intr_enabled; /* Status of interrupts */
};
extern bfa_boolean_t bfa_auto_recover;
-extern struct bfa_module_s hal_mod_sgpg;
-extern struct bfa_module_s hal_mod_fcport;
-extern struct bfa_module_s hal_mod_fcxp;
-extern struct bfa_module_s hal_mod_lps;
-extern struct bfa_module_s hal_mod_uf;
-extern struct bfa_module_s hal_mod_rport;
-extern struct bfa_module_s hal_mod_fcpim;
+
+void bfa_dconf_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *);
+void bfa_dconf_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_dconf_iocdisable(struct bfa_s *);
+void bfa_fcp_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_fcp_iocdisable(struct bfa_s *bfa);
+void bfa_fcp_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_fcpim_iocdisable(struct bfa_fcp_mod_s *);
+void bfa_fcport_start(struct bfa_s *);
+void bfa_fcport_iocdisable(struct bfa_s *);
+void bfa_fcport_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_fcport_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_fcxp_iocdisable(struct bfa_s *);
+void bfa_fcxp_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_fcxp_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_fcdiag_iocdisable(struct bfa_s *);
+void bfa_fcdiag_attach(struct bfa_s *bfa, void *, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_ioim_lm_init(struct bfa_s *);
+void bfa_lps_iocdisable(struct bfa_s *bfa);
+void bfa_lps_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_lps_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_rport_iocdisable(struct bfa_s *bfa);
+void bfa_rport_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_rport_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_sgpg_attach(struct bfa_s *, void *bfad, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_uf_meminfo(struct bfa_iocfc_cfg_s *, struct bfa_meminfo_s *,
+ struct bfa_s *);
+void bfa_uf_attach(struct bfa_s *, void *, struct bfa_iocfc_cfg_s *,
+ struct bfa_pcidev_s *);
+void bfa_uf_start(struct bfa_s *);
#endif /* __BFA_MODULES_H__ */
diff --git a/drivers/scsi/bfa/bfa_plog.h b/drivers/scsi/bfa/bfa_plog.h
index 1c9baa68339b..0ed67339ed90 100644
--- a/drivers/scsi/bfa/bfa_plog.h
+++ b/drivers/scsi/bfa/bfa_plog.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_PORTLOG_H__
#define __BFA_PORTLOG_H__
diff --git a/drivers/scsi/bfa/bfa_port.c b/drivers/scsi/bfa/bfa_port.c
index 3f8e9d6066ec..cfe2c9c336bf 100644
--- a/drivers/scsi/bfa/bfa_port.c
+++ b/drivers/scsi/bfa/bfa_port.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
@@ -24,8 +17,6 @@
BFA_TRC_FILE(CNA, PORT);
-#define bfa_ioc_portid(__ioc) ((__ioc)->port_id)
-
static void
bfa_port_stats_swap(struct bfa_port_s *port, union bfa_port_stats_u *stats)
{
@@ -97,14 +88,11 @@ bfa_port_get_stats_isr(struct bfa_port_s *port, bfa_status_t status)
port->stats_busy = BFA_FALSE;
if (status == BFA_STATUS_OK) {
- struct timeval tv;
-
memcpy(port->stats, port->stats_dma.kva,
sizeof(union bfa_port_stats_u));
bfa_port_stats_swap(port, port->stats);
- do_gettimeofday(&tv);
- port->stats->fc.secs_reset = tv.tv_sec - port->stats_reset_time;
+ port->stats->fc.secs_reset = ktime_get_seconds() - port->stats_reset_time;
}
if (port->stats_cbfn) {
@@ -125,16 +113,13 @@ bfa_port_get_stats_isr(struct bfa_port_s *port, bfa_status_t status)
static void
bfa_port_clear_stats_isr(struct bfa_port_s *port, bfa_status_t status)
{
- struct timeval tv;
-
port->stats_status = status;
port->stats_busy = BFA_FALSE;
/*
* re-initialize time stamp for stats reset
*/
- do_gettimeofday(&tv);
- port->stats_reset_time = tv.tv_sec;
+ port->stats_reset_time = ktime_get_seconds();
if (port->stats_cbfn) {
port->stats_cbfn(port->stats_cbarg, status);
@@ -236,6 +221,12 @@ bfa_port_enable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn,
{
struct bfi_port_generic_req_s *m;
+ /* If port is PBC disabled, return error */
+ if (port->pbc_disabled) {
+ bfa_trc(port, BFA_STATUS_PBC);
+ return BFA_STATUS_PBC;
+ }
+
if (bfa_ioc_is_disabled(port->ioc)) {
bfa_trc(port, BFA_STATUS_IOC_DISABLED);
return BFA_STATUS_IOC_DISABLED;
@@ -246,6 +237,12 @@ bfa_port_enable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn,
return BFA_STATUS_IOC_FAILURE;
}
+ /* if port is d-port enabled, return error */
+ if (port->dport_enabled) {
+ bfa_trc(port, BFA_STATUS_DPORT_ERR);
+ return BFA_STATUS_DPORT_ERR;
+ }
+
if (port->endis_pending) {
bfa_trc(port, BFA_STATUS_DEVBUSY);
return BFA_STATUS_DEVBUSY;
@@ -280,6 +277,12 @@ bfa_port_disable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn,
{
struct bfi_port_generic_req_s *m;
+ /* If port is PBC disabled, return error */
+ if (port->pbc_disabled) {
+ bfa_trc(port, BFA_STATUS_PBC);
+ return BFA_STATUS_PBC;
+ }
+
if (bfa_ioc_is_disabled(port->ioc)) {
bfa_trc(port, BFA_STATUS_IOC_DISABLED);
return BFA_STATUS_IOC_DISABLED;
@@ -290,6 +293,12 @@ bfa_port_disable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn,
return BFA_STATUS_IOC_FAILURE;
}
+ /* if port is d-port enabled, return error */
+ if (port->dport_enabled) {
+ bfa_trc(port, BFA_STATUS_DPORT_ERR);
+ return BFA_STATUS_DPORT_ERR;
+ }
+
if (port->endis_pending) {
bfa_trc(port, BFA_STATUS_DEVBUSY);
return BFA_STATUS_DEVBUSY;
@@ -387,32 +396,47 @@ bfa_port_clear_stats(struct bfa_port_s *port, bfa_port_stats_cbfn_t cbfn,
}
/*
- * bfa_port_hbfail()
+ * bfa_port_notify()
*
+ * Port module IOC event handler
*
* @param[in] Pointer to the Port module data structure.
+ * @param[in] IOC event structure
*
* @return void
*/
void
-bfa_port_hbfail(void *arg)
+bfa_port_notify(void *arg, enum bfa_ioc_event_e event)
{
struct bfa_port_s *port = (struct bfa_port_s *) arg;
- /* Fail any pending get_stats/clear_stats requests */
- if (port->stats_busy) {
- if (port->stats_cbfn)
- port->stats_cbfn(port->stats_cbarg, BFA_STATUS_FAILED);
- port->stats_cbfn = NULL;
- port->stats_busy = BFA_FALSE;
- }
-
- /* Clear any enable/disable is pending */
- if (port->endis_pending) {
- if (port->endis_cbfn)
- port->endis_cbfn(port->endis_cbarg, BFA_STATUS_FAILED);
- port->endis_cbfn = NULL;
- port->endis_pending = BFA_FALSE;
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ /* Fail any pending get_stats/clear_stats requests */
+ if (port->stats_busy) {
+ if (port->stats_cbfn)
+ port->stats_cbfn(port->stats_cbarg,
+ BFA_STATUS_FAILED);
+ port->stats_cbfn = NULL;
+ port->stats_busy = BFA_FALSE;
+ }
+
+ /* Clear any enable/disable is pending */
+ if (port->endis_pending) {
+ if (port->endis_cbfn)
+ port->endis_cbfn(port->endis_cbarg,
+ BFA_STATUS_FAILED);
+ port->endis_cbfn = NULL;
+ port->endis_pending = BFA_FALSE;
+ }
+
+ /* clear D-port mode */
+ if (port->dport_enabled)
+ bfa_port_set_dportenabled(port, BFA_FALSE);
+ break;
+ default:
+ break;
}
}
@@ -433,8 +457,6 @@ void
bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc,
void *dev, struct bfa_trc_mod_s *trcmod)
{
- struct timeval tv;
-
WARN_ON(!port);
port->dev = dev;
@@ -445,16 +467,398 @@ bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc,
port->endis_pending = BFA_FALSE;
port->stats_cbfn = NULL;
port->endis_cbfn = NULL;
+ port->pbc_disabled = BFA_FALSE;
+ port->dport_enabled = BFA_FALSE;
bfa_ioc_mbox_regisr(port->ioc, BFI_MC_PORT, bfa_port_isr, port);
- bfa_ioc_hbfail_init(&port->hbfail, bfa_port_hbfail, port);
- list_add_tail(&port->hbfail.qe, &port->ioc->hb_notify_q);
+ bfa_q_qe_init(&port->ioc_notify);
+ bfa_ioc_notify_init(&port->ioc_notify, bfa_port_notify, port);
+ list_add_tail(&port->ioc_notify.qe, &port->ioc->notify_q);
/*
* initialize time stamp for stats reset
*/
- do_gettimeofday(&tv);
- port->stats_reset_time = tv.tv_sec;
+ port->stats_reset_time = ktime_get_seconds();
bfa_trc(port, 0);
}
+
+/*
+ * bfa_port_set_dportenabled();
+ *
+ * Port module- set pbc disabled flag
+ *
+ * @param[in] port - Pointer to the Port module data structure
+ *
+ * @return void
+ */
+void
+bfa_port_set_dportenabled(struct bfa_port_s *port, bfa_boolean_t enabled)
+{
+ port->dport_enabled = enabled;
+}
+
+/*
+ * CEE module specific definitions
+ */
+
+/*
+ * bfa_cee_get_attr_isr()
+ *
+ * @brief CEE ISR for get-attributes responses from f/w
+ *
+ * @param[in] cee - Pointer to the CEE module
+ * status - Return status from the f/w
+ *
+ * @return void
+ */
+static void
+bfa_cee_get_attr_isr(struct bfa_cee_s *cee, bfa_status_t status)
+{
+ struct bfa_cee_lldp_cfg_s *lldp_cfg = &cee->attr->lldp_remote;
+
+ cee->get_attr_status = status;
+ bfa_trc(cee, 0);
+ if (status == BFA_STATUS_OK) {
+ bfa_trc(cee, 0);
+ memcpy(cee->attr, cee->attr_dma.kva,
+ sizeof(struct bfa_cee_attr_s));
+ lldp_cfg->time_to_live = be16_to_cpu(lldp_cfg->time_to_live);
+ lldp_cfg->enabled_system_cap =
+ be16_to_cpu(lldp_cfg->enabled_system_cap);
+ }
+ cee->get_attr_pending = BFA_FALSE;
+ if (cee->cbfn.get_attr_cbfn) {
+ bfa_trc(cee, 0);
+ cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg, status);
+ }
+}
+
+/*
+ * bfa_cee_get_stats_isr()
+ *
+ * @brief CEE ISR for get-stats responses from f/w
+ *
+ * @param[in] cee - Pointer to the CEE module
+ * status - Return status from the f/w
+ *
+ * @return void
+ */
+static void
+bfa_cee_get_stats_isr(struct bfa_cee_s *cee, bfa_status_t status)
+{
+ u32 *buffer;
+ int i;
+
+ cee->get_stats_status = status;
+ bfa_trc(cee, 0);
+ if (status == BFA_STATUS_OK) {
+ bfa_trc(cee, 0);
+ memcpy(cee->stats, cee->stats_dma.kva,
+ sizeof(struct bfa_cee_stats_s));
+ /* swap the cee stats */
+ buffer = (u32 *)cee->stats;
+ for (i = 0; i < (sizeof(struct bfa_cee_stats_s) /
+ sizeof(u32)); i++)
+ buffer[i] = cpu_to_be32(buffer[i]);
+ }
+ cee->get_stats_pending = BFA_FALSE;
+ bfa_trc(cee, 0);
+ if (cee->cbfn.get_stats_cbfn) {
+ bfa_trc(cee, 0);
+ cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg, status);
+ }
+}
+
+/*
+ * bfa_cee_reset_stats_isr()
+ *
+ * @brief CEE ISR for reset-stats responses from f/w
+ *
+ * @param[in] cee - Pointer to the CEE module
+ * status - Return status from the f/w
+ *
+ * @return void
+ */
+static void
+bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status)
+{
+ cee->reset_stats_status = status;
+ cee->reset_stats_pending = BFA_FALSE;
+ if (cee->cbfn.reset_stats_cbfn)
+ cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg, status);
+}
+
+/*
+ * bfa_cee_meminfo()
+ *
+ * @brief Returns the size of the DMA memory needed by CEE module
+ *
+ * @param[in] void
+ *
+ * @return Size of DMA region
+ */
+u32
+bfa_cee_meminfo(void)
+{
+ return BFA_ROUNDUP(sizeof(struct bfa_cee_attr_s), BFA_DMA_ALIGN_SZ) +
+ BFA_ROUNDUP(sizeof(struct bfa_cee_stats_s), BFA_DMA_ALIGN_SZ);
+}
+
+/*
+ * bfa_cee_mem_claim()
+ *
+ * @brief Initialized CEE DMA Memory
+ *
+ * @param[in] cee CEE module pointer
+ * dma_kva Kernel Virtual Address of CEE DMA Memory
+ * dma_pa Physical Address of CEE DMA Memory
+ *
+ * @return void
+ */
+void
+bfa_cee_mem_claim(struct bfa_cee_s *cee, u8 *dma_kva, u64 dma_pa)
+{
+ cee->attr_dma.kva = dma_kva;
+ cee->attr_dma.pa = dma_pa;
+ cee->stats_dma.kva = dma_kva + BFA_ROUNDUP(
+ sizeof(struct bfa_cee_attr_s), BFA_DMA_ALIGN_SZ);
+ cee->stats_dma.pa = dma_pa + BFA_ROUNDUP(
+ sizeof(struct bfa_cee_attr_s), BFA_DMA_ALIGN_SZ);
+ cee->attr = (struct bfa_cee_attr_s *) dma_kva;
+ cee->stats = (struct bfa_cee_stats_s *) (dma_kva + BFA_ROUNDUP(
+ sizeof(struct bfa_cee_attr_s), BFA_DMA_ALIGN_SZ));
+}
+
+/*
+ * bfa_cee_get_attr()
+ *
+ * @brief
+ * Send the request to the f/w to fetch CEE attributes.
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return Status
+ */
+
+bfa_status_t
+bfa_cee_get_attr(struct bfa_cee_s *cee, struct bfa_cee_attr_s *attr,
+ bfa_cee_get_attr_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_cee_get_req_s *cmd;
+
+ WARN_ON((cee == NULL) || (cee->ioc == NULL));
+ bfa_trc(cee, 0);
+ if (!bfa_ioc_is_operational(cee->ioc)) {
+ bfa_trc(cee, 0);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+ if (cee->get_attr_pending == BFA_TRUE) {
+ bfa_trc(cee, 0);
+ return BFA_STATUS_DEVBUSY;
+ }
+ cee->get_attr_pending = BFA_TRUE;
+ cmd = (struct bfi_cee_get_req_s *) cee->get_cfg_mb.msg;
+ cee->attr = attr;
+ cee->cbfn.get_attr_cbfn = cbfn;
+ cee->cbfn.get_attr_cbarg = cbarg;
+ bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_CFG_REQ,
+ bfa_ioc_portid(cee->ioc));
+ bfa_dma_be_addr_set(cmd->dma_addr, cee->attr_dma.pa);
+ bfa_ioc_mbox_queue(cee->ioc, &cee->get_cfg_mb);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * bfa_cee_get_stats()
+ *
+ * @brief
+ * Send the request to the f/w to fetch CEE statistics.
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return Status
+ */
+
+bfa_status_t
+bfa_cee_get_stats(struct bfa_cee_s *cee, struct bfa_cee_stats_s *stats,
+ bfa_cee_get_stats_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_cee_get_req_s *cmd;
+
+ WARN_ON((cee == NULL) || (cee->ioc == NULL));
+
+ if (!bfa_ioc_is_operational(cee->ioc)) {
+ bfa_trc(cee, 0);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+ if (cee->get_stats_pending == BFA_TRUE) {
+ bfa_trc(cee, 0);
+ return BFA_STATUS_DEVBUSY;
+ }
+ cee->get_stats_pending = BFA_TRUE;
+ cmd = (struct bfi_cee_get_req_s *) cee->get_stats_mb.msg;
+ cee->stats = stats;
+ cee->cbfn.get_stats_cbfn = cbfn;
+ cee->cbfn.get_stats_cbarg = cbarg;
+ bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_STATS_REQ,
+ bfa_ioc_portid(cee->ioc));
+ bfa_dma_be_addr_set(cmd->dma_addr, cee->stats_dma.pa);
+ bfa_ioc_mbox_queue(cee->ioc, &cee->get_stats_mb);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * bfa_cee_reset_stats()
+ *
+ * @brief Clears CEE Stats in the f/w.
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return Status
+ */
+
+bfa_status_t
+bfa_cee_reset_stats(struct bfa_cee_s *cee,
+ bfa_cee_reset_stats_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_cee_reset_stats_s *cmd;
+
+ WARN_ON((cee == NULL) || (cee->ioc == NULL));
+ if (!bfa_ioc_is_operational(cee->ioc)) {
+ bfa_trc(cee, 0);
+ return BFA_STATUS_IOC_FAILURE;
+ }
+ if (cee->reset_stats_pending == BFA_TRUE) {
+ bfa_trc(cee, 0);
+ return BFA_STATUS_DEVBUSY;
+ }
+ cee->reset_stats_pending = BFA_TRUE;
+ cmd = (struct bfi_cee_reset_stats_s *) cee->reset_stats_mb.msg;
+ cee->cbfn.reset_stats_cbfn = cbfn;
+ cee->cbfn.reset_stats_cbarg = cbarg;
+ bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_RESET_STATS,
+ bfa_ioc_portid(cee->ioc));
+ bfa_ioc_mbox_queue(cee->ioc, &cee->reset_stats_mb);
+
+ return BFA_STATUS_OK;
+}
+
+/*
+ * bfa_cee_isrs()
+ *
+ * @brief Handles Mail-box interrupts for CEE module.
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return void
+ */
+
+static void
+bfa_cee_isr(void *cbarg, struct bfi_mbmsg_s *m)
+{
+ union bfi_cee_i2h_msg_u *msg;
+ struct bfi_cee_get_rsp_s *get_rsp;
+ struct bfa_cee_s *cee = (struct bfa_cee_s *) cbarg;
+ msg = (union bfi_cee_i2h_msg_u *) m;
+ get_rsp = (struct bfi_cee_get_rsp_s *) m;
+ bfa_trc(cee, msg->mh.msg_id);
+ switch (msg->mh.msg_id) {
+ case BFI_CEE_I2H_GET_CFG_RSP:
+ bfa_trc(cee, get_rsp->cmd_status);
+ bfa_cee_get_attr_isr(cee, get_rsp->cmd_status);
+ break;
+ case BFI_CEE_I2H_GET_STATS_RSP:
+ bfa_cee_get_stats_isr(cee, get_rsp->cmd_status);
+ break;
+ case BFI_CEE_I2H_RESET_STATS_RSP:
+ bfa_cee_reset_stats_isr(cee, get_rsp->cmd_status);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+/*
+ * bfa_cee_notify()
+ *
+ * @brief CEE module IOC event handler.
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ * @param[in] IOC event type
+ *
+ * @return void
+ */
+
+static void
+bfa_cee_notify(void *arg, enum bfa_ioc_event_e event)
+{
+ struct bfa_cee_s *cee = (struct bfa_cee_s *) arg;
+
+ bfa_trc(cee, event);
+
+ switch (event) {
+ case BFA_IOC_E_DISABLED:
+ case BFA_IOC_E_FAILED:
+ if (cee->get_attr_pending == BFA_TRUE) {
+ cee->get_attr_status = BFA_STATUS_FAILED;
+ cee->get_attr_pending = BFA_FALSE;
+ if (cee->cbfn.get_attr_cbfn) {
+ cee->cbfn.get_attr_cbfn(
+ cee->cbfn.get_attr_cbarg,
+ BFA_STATUS_FAILED);
+ }
+ }
+ if (cee->get_stats_pending == BFA_TRUE) {
+ cee->get_stats_status = BFA_STATUS_FAILED;
+ cee->get_stats_pending = BFA_FALSE;
+ if (cee->cbfn.get_stats_cbfn) {
+ cee->cbfn.get_stats_cbfn(
+ cee->cbfn.get_stats_cbarg,
+ BFA_STATUS_FAILED);
+ }
+ }
+ if (cee->reset_stats_pending == BFA_TRUE) {
+ cee->reset_stats_status = BFA_STATUS_FAILED;
+ cee->reset_stats_pending = BFA_FALSE;
+ if (cee->cbfn.reset_stats_cbfn) {
+ cee->cbfn.reset_stats_cbfn(
+ cee->cbfn.reset_stats_cbarg,
+ BFA_STATUS_FAILED);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * bfa_cee_attach()
+ *
+ * @brief CEE module-attach API
+ *
+ * @param[in] cee - Pointer to the CEE module data structure
+ * ioc - Pointer to the ioc module data structure
+ * dev - Pointer to the device driver module data structure
+ * The device driver specific mbox ISR functions have
+ * this pointer as one of the parameters.
+ *
+ * @return void
+ */
+void
+bfa_cee_attach(struct bfa_cee_s *cee, struct bfa_ioc_s *ioc,
+ void *dev)
+{
+ WARN_ON(cee == NULL);
+ cee->dev = dev;
+ cee->ioc = ioc;
+
+ bfa_ioc_mbox_regisr(cee->ioc, BFI_MC_CEE, bfa_cee_isr, cee);
+ bfa_q_qe_init(&cee->ioc_notify);
+ bfa_ioc_notify_init(&cee->ioc_notify, bfa_cee_notify, cee);
+ list_add_tail(&cee->ioc_notify.qe, &cee->ioc->notify_q);
+}
diff --git a/drivers/scsi/bfa/bfa_port.h b/drivers/scsi/bfa/bfa_port.h
index c4ee9db6b470..7e569d4b5214 100644
--- a/drivers/scsi/bfa/bfa_port.h
+++ b/drivers/scsi/bfa/bfa_port.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_PORT_H__
@@ -35,7 +28,7 @@ struct bfa_port_s {
bfa_port_stats_cbfn_t stats_cbfn;
void *stats_cbarg;
bfa_status_t stats_status;
- u32 stats_reset_time;
+ time64_t stats_reset_time;
union bfa_port_stats_u *stats;
struct bfa_dma_s stats_dma;
bfa_boolean_t endis_pending;
@@ -43,12 +36,17 @@ struct bfa_port_s {
bfa_port_endis_cbfn_t endis_cbfn;
void *endis_cbarg;
bfa_status_t endis_status;
- struct bfa_ioc_hbfail_notify_s hbfail;
+ struct bfa_ioc_notify_s ioc_notify;
+ bfa_boolean_t pbc_disabled;
+ bfa_boolean_t dport_enabled;
+ struct bfa_mem_dma_s port_dma;
};
+#define BFA_MEM_PORT_DMA(__bfa) (&((__bfa)->modules.port.port_dma))
+
void bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc,
void *dev, struct bfa_trc_mod_s *trcmod);
-void bfa_port_hbfail(void *arg);
+void bfa_port_notify(void *arg, enum bfa_ioc_event_e event);
bfa_status_t bfa_port_get_stats(struct bfa_port_s *port,
union bfa_port_stats_u *stats,
@@ -62,4 +60,60 @@ bfa_status_t bfa_port_disable(struct bfa_port_s *port,
u32 bfa_port_meminfo(void);
void bfa_port_mem_claim(struct bfa_port_s *port,
u8 *dma_kva, u64 dma_pa);
+void bfa_port_set_dportenabled(struct bfa_port_s *port,
+ bfa_boolean_t enabled);
+
+/*
+ * CEE declaration
+ */
+typedef void (*bfa_cee_get_attr_cbfn_t) (void *dev, bfa_status_t status);
+typedef void (*bfa_cee_get_stats_cbfn_t) (void *dev, bfa_status_t status);
+typedef void (*bfa_cee_reset_stats_cbfn_t) (void *dev, bfa_status_t status);
+
+struct bfa_cee_cbfn_s {
+ bfa_cee_get_attr_cbfn_t get_attr_cbfn;
+ void *get_attr_cbarg;
+ bfa_cee_get_stats_cbfn_t get_stats_cbfn;
+ void *get_stats_cbarg;
+ bfa_cee_reset_stats_cbfn_t reset_stats_cbfn;
+ void *reset_stats_cbarg;
+};
+
+struct bfa_cee_s {
+ void *dev;
+ bfa_boolean_t get_attr_pending;
+ bfa_boolean_t get_stats_pending;
+ bfa_boolean_t reset_stats_pending;
+ bfa_status_t get_attr_status;
+ bfa_status_t get_stats_status;
+ bfa_status_t reset_stats_status;
+ struct bfa_cee_cbfn_s cbfn;
+ struct bfa_ioc_notify_s ioc_notify;
+ struct bfa_trc_mod_s *trcmod;
+ struct bfa_cee_attr_s *attr;
+ struct bfa_cee_stats_s *stats;
+ struct bfa_dma_s attr_dma;
+ struct bfa_dma_s stats_dma;
+ struct bfa_ioc_s *ioc;
+ struct bfa_mbox_cmd_s get_cfg_mb;
+ struct bfa_mbox_cmd_s get_stats_mb;
+ struct bfa_mbox_cmd_s reset_stats_mb;
+ struct bfa_mem_dma_s cee_dma;
+};
+
+#define BFA_MEM_CEE_DMA(__bfa) (&((__bfa)->modules.cee.cee_dma))
+
+u32 bfa_cee_meminfo(void);
+void bfa_cee_mem_claim(struct bfa_cee_s *cee, u8 *dma_kva, u64 dma_pa);
+void bfa_cee_attach(struct bfa_cee_s *cee,
+ struct bfa_ioc_s *ioc, void *dev);
+bfa_status_t bfa_cee_get_attr(struct bfa_cee_s *cee,
+ struct bfa_cee_attr_s *attr,
+ bfa_cee_get_attr_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_cee_get_stats(struct bfa_cee_s *cee,
+ struct bfa_cee_stats_s *stats,
+ bfa_cee_get_stats_cbfn_t cbfn, void *cbarg);
+bfa_status_t bfa_cee_reset_stats(struct bfa_cee_s *cee,
+ bfa_cee_reset_stats_cbfn_t cbfn, void *cbarg);
+
#endif /* __BFA_PORT_H__ */
diff --git a/drivers/scsi/bfa/bfa_svc.c b/drivers/scsi/bfa/bfa_svc.c
index 16d9a5f61c18..df33afaaa673 100644
--- a/drivers/scsi/bfa/bfa_svc.c
+++ b/drivers/scsi/bfa/bfa_svc.c
@@ -1,32 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include "bfad_drv.h"
+#include "bfad_im.h"
#include "bfa_plog.h"
#include "bfa_cs.h"
#include "bfa_modules.h"
BFA_TRC_FILE(HAL, FCXP);
-BFA_MODULE(fcxp);
-BFA_MODULE(sgpg);
-BFA_MODULE(lps);
-BFA_MODULE(fcport);
-BFA_MODULE(rport);
-BFA_MODULE(uf);
/*
* LPS related definitions
@@ -53,31 +41,6 @@ BFA_MODULE(uf);
(bfa_ioc_is_disabled(&bfa->ioc) == BFA_TRUE))
/*
- * BFA port state machine events
- */
-enum bfa_fcport_sm_event {
- BFA_FCPORT_SM_START = 1, /* start port state machine */
- BFA_FCPORT_SM_STOP = 2, /* stop port state machine */
- BFA_FCPORT_SM_ENABLE = 3, /* enable port */
- BFA_FCPORT_SM_DISABLE = 4, /* disable port state machine */
- BFA_FCPORT_SM_FWRSP = 5, /* firmware enable/disable rsp */
- BFA_FCPORT_SM_LINKUP = 6, /* firmware linkup event */
- BFA_FCPORT_SM_LINKDOWN = 7, /* firmware linkup down */
- BFA_FCPORT_SM_QRESUME = 8, /* CQ space available */
- BFA_FCPORT_SM_HWFAIL = 9, /* IOC h/w failure */
-};
-
-/*
- * BFA port link notification state machine events
- */
-
-enum bfa_fcport_ln_sm_event {
- BFA_FCPORT_LN_SM_LINKUP = 1, /* linkup event */
- BFA_FCPORT_LN_SM_LINKDOWN = 2, /* linkdown event */
- BFA_FCPORT_LN_SM_NOTIFICATION = 3 /* done notification */
-};
-
-/*
* RPORT related definitions
*/
#define bfa_rport_offline_cb(__rp) do { \
@@ -113,18 +76,9 @@ static void bfa_fcxp_queue(struct bfa_fcxp_s *fcxp,
/*
* forward declarations for LPS functions
*/
-static void bfa_lps_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len,
- u32 *dm_len);
-static void bfa_lps_attach(struct bfa_s *bfa, void *bfad,
- struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo,
- struct bfa_pcidev_s *pcidev);
-static void bfa_lps_detach(struct bfa_s *bfa);
-static void bfa_lps_start(struct bfa_s *bfa);
-static void bfa_lps_stop(struct bfa_s *bfa);
-static void bfa_lps_iocdisable(struct bfa_s *bfa);
static void bfa_lps_login_rsp(struct bfa_s *bfa,
struct bfi_lps_login_rsp_s *rsp);
+static void bfa_lps_no_res(struct bfa_lps_s *first_lps, u8 count);
static void bfa_lps_logout_rsp(struct bfa_s *bfa,
struct bfi_lps_logout_rsp_s *rsp);
static void bfa_lps_reqq_resume(void *lps_arg);
@@ -195,6 +149,12 @@ static void bfa_fcport_sm_iocdown(struct bfa_fcport_s *fcport,
enum bfa_fcport_sm_event event);
static void bfa_fcport_sm_iocfail(struct bfa_fcport_s *fcport,
enum bfa_fcport_sm_event event);
+static void bfa_fcport_sm_dport(struct bfa_fcport_s *fcport,
+ enum bfa_fcport_sm_event event);
+static void bfa_fcport_sm_ddport(struct bfa_fcport_s *fcport,
+ enum bfa_fcport_sm_event event);
+static void bfa_fcport_sm_faa_misconfig(struct bfa_fcport_s *fcport,
+ enum bfa_fcport_sm_event event);
static void bfa_fcport_ln_sm_dn(struct bfa_fcport_ln_s *ln,
enum bfa_fcport_ln_sm_event event);
@@ -211,7 +171,23 @@ static void bfa_fcport_ln_sm_up_dn_nf(struct bfa_fcport_ln_s *ln,
static void bfa_fcport_ln_sm_up_dn_up_nf(struct bfa_fcport_ln_s *ln,
enum bfa_fcport_ln_sm_event event);
-static struct bfa_sm_table_s hal_port_sm_table[] = {
+struct bfa_fcport_sm_table_s {
+ bfa_fcport_sm_t sm; /* state machine function */
+ enum bfa_port_states state; /* state machine encoding */
+ char *name; /* state name for display */
+};
+
+static inline enum bfa_port_states
+bfa_fcport_sm_to_state(struct bfa_fcport_sm_table_s *smt, bfa_fcport_sm_t sm)
+{
+ int i = 0;
+
+ while (smt[i].sm && smt[i].sm != sm)
+ i++;
+ return smt[i].state;
+}
+
+static struct bfa_fcport_sm_table_s hal_port_sm_table[] = {
{BFA_SM(bfa_fcport_sm_uninit), BFA_PORT_ST_UNINIT},
{BFA_SM(bfa_fcport_sm_enabling_qwait), BFA_PORT_ST_ENABLING_QWAIT},
{BFA_SM(bfa_fcport_sm_enabling), BFA_PORT_ST_ENABLING},
@@ -224,6 +200,9 @@ static struct bfa_sm_table_s hal_port_sm_table[] = {
{BFA_SM(bfa_fcport_sm_stopped), BFA_PORT_ST_STOPPED},
{BFA_SM(bfa_fcport_sm_iocdown), BFA_PORT_ST_IOCDOWN},
{BFA_SM(bfa_fcport_sm_iocfail), BFA_PORT_ST_IOCDOWN},
+ {BFA_SM(bfa_fcport_sm_dport), BFA_PORT_ST_DPORT},
+ {BFA_SM(bfa_fcport_sm_ddport), BFA_PORT_ST_DDPORT},
+ {BFA_SM(bfa_fcport_sm_faa_misconfig), BFA_PORT_ST_FAA_MISCONFIG},
};
@@ -287,18 +266,6 @@ plkd_validate_logrec(struct bfa_plog_rec_s *pl_rec)
return 0;
}
-static u64
-bfa_get_log_time(void)
-{
- u64 system_time = 0;
- struct timeval tv;
- do_gettimeofday(&tv);
-
- /* We are interested in seconds only. */
- system_time = tv.tv_sec;
- return system_time;
-}
-
static void
bfa_plog_add(struct bfa_plog_s *plog, struct bfa_plog_rec_s *pl_rec)
{
@@ -319,7 +286,7 @@ bfa_plog_add(struct bfa_plog_s *plog, struct bfa_plog_rec_s *pl_rec)
memcpy(pl_recp, pl_rec, sizeof(struct bfa_plog_rec_s));
- pl_recp->tv = bfa_get_log_time();
+ pl_recp->tv = ktime_get_real_seconds();
BFA_PL_LOG_REC_INCR(plog->tail);
if (plog->head == plog->tail)
@@ -349,8 +316,8 @@ bfa_plog_str(struct bfa_plog_s *plog, enum bfa_plog_mid mid,
lp.eid = event;
lp.log_type = BFA_PL_LOG_TYPE_STRING;
lp.misc = misc;
- strncpy(lp.log_entry.string_log, log_str,
- BFA_PL_STRING_LOG_SZ - 1);
+ strscpy(lp.log_entry.string_log, log_str,
+ BFA_PL_STRING_LOG_SZ);
lp.log_entry.string_log[BFA_PL_STRING_LOG_SZ - 1] = '\0';
bfa_plog_add(plog, &lp);
}
@@ -388,13 +355,10 @@ bfa_plog_fchdr(struct bfa_plog_s *plog, enum bfa_plog_mid mid,
enum bfa_plog_eid event,
u16 misc, struct fchs_s *fchdr)
{
- struct bfa_plog_rec_s lp;
u32 *tmp_int = (u32 *) fchdr;
u32 ints[BFA_PL_INT_LOG_SZ];
if (plog->plog_enabled) {
- memset(&lp, 0, sizeof(struct bfa_plog_rec_s));
-
ints[0] = tmp_int[0];
ints[1] = tmp_int[1];
ints[2] = tmp_int[4];
@@ -408,13 +372,10 @@ bfa_plog_fchdr_and_pl(struct bfa_plog_s *plog, enum bfa_plog_mid mid,
enum bfa_plog_eid event, u16 misc, struct fchs_s *fchdr,
u32 pld_w0)
{
- struct bfa_plog_rec_s lp;
u32 *tmp_int = (u32 *) fchdr;
u32 ints[BFA_PL_INT_LOG_SZ];
if (plog->plog_enabled) {
- memset(&lp, 0, sizeof(struct bfa_plog_rec_s));
-
ints[0] = tmp_int[0];
ints[1] = tmp_int[1];
ints[2] = tmp_int[4];
@@ -430,51 +391,19 @@ bfa_plog_fchdr_and_pl(struct bfa_plog_s *plog, enum bfa_plog_mid mid,
*/
static void
-claim_fcxp_req_rsp_mem(struct bfa_fcxp_mod_s *mod, struct bfa_meminfo_s *mi)
-{
- u8 *dm_kva = NULL;
- u64 dm_pa;
- u32 buf_pool_sz;
-
- dm_kva = bfa_meminfo_dma_virt(mi);
- dm_pa = bfa_meminfo_dma_phys(mi);
-
- buf_pool_sz = mod->req_pld_sz * mod->num_fcxps;
-
- /*
- * Initialize the fcxp req payload list
- */
- mod->req_pld_list_kva = dm_kva;
- mod->req_pld_list_pa = dm_pa;
- dm_kva += buf_pool_sz;
- dm_pa += buf_pool_sz;
- memset(mod->req_pld_list_kva, 0, buf_pool_sz);
-
- /*
- * Initialize the fcxp rsp payload list
- */
- buf_pool_sz = mod->rsp_pld_sz * mod->num_fcxps;
- mod->rsp_pld_list_kva = dm_kva;
- mod->rsp_pld_list_pa = dm_pa;
- dm_kva += buf_pool_sz;
- dm_pa += buf_pool_sz;
- memset(mod->rsp_pld_list_kva, 0, buf_pool_sz);
-
- bfa_meminfo_dma_virt(mi) = dm_kva;
- bfa_meminfo_dma_phys(mi) = dm_pa;
-}
-
-static void
-claim_fcxps_mem(struct bfa_fcxp_mod_s *mod, struct bfa_meminfo_s *mi)
+claim_fcxps_mem(struct bfa_fcxp_mod_s *mod)
{
u16 i;
struct bfa_fcxp_s *fcxp;
- fcxp = (struct bfa_fcxp_s *) bfa_meminfo_kva(mi);
+ fcxp = (struct bfa_fcxp_s *) bfa_mem_kva_curp(mod);
memset(fcxp, 0, sizeof(struct bfa_fcxp_s) * mod->num_fcxps);
- INIT_LIST_HEAD(&mod->fcxp_free_q);
+ INIT_LIST_HEAD(&mod->fcxp_req_free_q);
+ INIT_LIST_HEAD(&mod->fcxp_rsp_free_q);
INIT_LIST_HEAD(&mod->fcxp_active_q);
+ INIT_LIST_HEAD(&mod->fcxp_req_unused_q);
+ INIT_LIST_HEAD(&mod->fcxp_rsp_unused_q);
mod->fcxp_list = fcxp;
@@ -482,47 +411,67 @@ claim_fcxps_mem(struct bfa_fcxp_mod_s *mod, struct bfa_meminfo_s *mi)
fcxp->fcxp_mod = mod;
fcxp->fcxp_tag = i;
- list_add_tail(&fcxp->qe, &mod->fcxp_free_q);
+ if (i < (mod->num_fcxps / 2)) {
+ list_add_tail(&fcxp->qe, &mod->fcxp_req_free_q);
+ fcxp->req_rsp = BFA_TRUE;
+ } else {
+ list_add_tail(&fcxp->qe, &mod->fcxp_rsp_free_q);
+ fcxp->req_rsp = BFA_FALSE;
+ }
+
bfa_reqq_winit(&fcxp->reqq_wqe, bfa_fcxp_qresume, fcxp);
fcxp->reqq_waiting = BFA_FALSE;
fcxp = fcxp + 1;
}
- bfa_meminfo_kva(mi) = (void *)fcxp;
+ bfa_mem_kva_curp(mod) = (void *)fcxp;
}
-static void
-bfa_fcxp_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len,
- u32 *dm_len)
+void
+bfa_fcxp_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo,
+ struct bfa_s *bfa)
{
- u16 num_fcxp_reqs = cfg->fwcfg.num_fcxp_reqs;
+ struct bfa_fcxp_mod_s *fcxp_mod = BFA_FCXP_MOD(bfa);
+ struct bfa_mem_kva_s *fcxp_kva = BFA_MEM_FCXP_KVA(bfa);
+ struct bfa_mem_dma_s *seg_ptr;
+ u16 nsegs, idx, per_seg_fcxp;
+ u16 num_fcxps = cfg->fwcfg.num_fcxp_reqs;
+ u32 per_fcxp_sz;
- if (num_fcxp_reqs == 0)
+ if (num_fcxps == 0)
return;
- /*
- * Account for req/rsp payload
- */
- *dm_len += BFA_FCXP_MAX_IBUF_SZ * num_fcxp_reqs;
if (cfg->drvcfg.min_cfg)
- *dm_len += BFA_FCXP_MAX_IBUF_SZ * num_fcxp_reqs;
+ per_fcxp_sz = 2 * BFA_FCXP_MAX_IBUF_SZ;
else
- *dm_len += BFA_FCXP_MAX_LBUF_SZ * num_fcxp_reqs;
+ per_fcxp_sz = BFA_FCXP_MAX_IBUF_SZ + BFA_FCXP_MAX_LBUF_SZ;
- /*
- * Account for fcxp structs
- */
- *ndm_len += sizeof(struct bfa_fcxp_s) * num_fcxp_reqs;
+ /* dma memory */
+ nsegs = BFI_MEM_DMA_NSEGS(num_fcxps, per_fcxp_sz);
+ per_seg_fcxp = BFI_MEM_NREQS_SEG(per_fcxp_sz);
+
+ bfa_mem_dma_seg_iter(fcxp_mod, seg_ptr, nsegs, idx) {
+ if (num_fcxps >= per_seg_fcxp) {
+ num_fcxps -= per_seg_fcxp;
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ per_seg_fcxp * per_fcxp_sz);
+ } else
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ num_fcxps * per_fcxp_sz);
+ }
+
+ /* kva memory */
+ bfa_mem_kva_setup(minfo, fcxp_kva,
+ cfg->fwcfg.num_fcxp_reqs * sizeof(struct bfa_fcxp_s));
}
-static void
+void
bfa_fcxp_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
+ struct bfa_pcidev_s *pcidev)
{
struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa);
- memset(mod, 0, sizeof(struct bfa_fcxp_mod_s));
mod->bfa = bfa;
mod->num_fcxps = cfg->fwcfg.num_fcxp_reqs;
@@ -533,34 +482,23 @@ bfa_fcxp_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
if (!cfg->drvcfg.min_cfg)
mod->rsp_pld_sz = BFA_FCXP_MAX_LBUF_SZ;
- INIT_LIST_HEAD(&mod->wait_q);
-
- claim_fcxp_req_rsp_mem(mod, meminfo);
- claim_fcxps_mem(mod, meminfo);
-}
-
-static void
-bfa_fcxp_detach(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_fcxp_start(struct bfa_s *bfa)
-{
-}
+ INIT_LIST_HEAD(&mod->req_wait_q);
+ INIT_LIST_HEAD(&mod->rsp_wait_q);
-static void
-bfa_fcxp_stop(struct bfa_s *bfa)
-{
+ claim_fcxps_mem(mod);
}
-static void
+void
bfa_fcxp_iocdisable(struct bfa_s *bfa)
{
struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa);
struct bfa_fcxp_s *fcxp;
struct list_head *qe, *qen;
+ /* Enqueue unused fcxp resources to free_q */
+ list_splice_tail_init(&mod->fcxp_req_unused_q, &mod->fcxp_req_free_q);
+ list_splice_tail_init(&mod->fcxp_rsp_unused_q, &mod->fcxp_rsp_free_q);
+
list_for_each_safe(qe, qen, &mod->fcxp_active_q) {
fcxp = (struct bfa_fcxp_s *) qe;
if (fcxp->caller == NULL) {
@@ -576,11 +514,14 @@ bfa_fcxp_iocdisable(struct bfa_s *bfa)
}
static struct bfa_fcxp_s *
-bfa_fcxp_get(struct bfa_fcxp_mod_s *fm)
+bfa_fcxp_get(struct bfa_fcxp_mod_s *fm, bfa_boolean_t req)
{
struct bfa_fcxp_s *fcxp;
- bfa_q_deq(&fm->fcxp_free_q, &fcxp);
+ if (req)
+ bfa_q_deq(&fm->fcxp_req_free_q, &fcxp);
+ else
+ bfa_q_deq(&fm->fcxp_rsp_free_q, &fcxp);
if (fcxp)
list_add_tail(&fcxp->qe, &fm->fcxp_active_q);
@@ -659,7 +600,11 @@ bfa_fcxp_put(struct bfa_fcxp_s *fcxp)
struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod;
struct bfa_fcxp_wqe_s *wqe;
- bfa_q_deq(&mod->wait_q, &wqe);
+ if (fcxp->req_rsp)
+ bfa_q_deq(&mod->req_wait_q, &wqe);
+ else
+ bfa_q_deq(&mod->rsp_wait_q, &wqe);
+
if (wqe) {
bfa_trc(mod->bfa, fcxp->fcxp_tag);
@@ -674,7 +619,11 @@ bfa_fcxp_put(struct bfa_fcxp_s *fcxp)
WARN_ON(!bfa_q_is_on_q(&mod->fcxp_active_q, fcxp));
list_del(&fcxp->qe);
- list_add_tail(&fcxp->qe, &mod->fcxp_free_q);
+
+ if (fcxp->req_rsp)
+ list_add_tail(&fcxp->qe, &mod->fcxp_req_free_q);
+ else
+ list_add_tail(&fcxp->qe, &mod->fcxp_rsp_free_q);
}
static void
@@ -750,23 +699,6 @@ hal_fcxp_send_comp(struct bfa_s *bfa, struct bfi_fcxp_send_rsp_s *fcxp_rsp)
}
static void
-hal_fcxp_set_local_sges(struct bfi_sge_s *sge, u32 reqlen, u64 req_pa)
-{
- union bfi_addr_u sga_zero = { {0} };
-
- sge->sg_len = reqlen;
- sge->flags = BFI_SGE_DATA_LAST;
- bfa_dma_addr_set(sge[0].sga, req_pa);
- bfa_sge_to_be(sge);
- sge++;
-
- sge->sga = sga_zero;
- sge->sg_len = reqlen;
- sge->flags = BFI_SGE_PGDLEN;
- bfa_sge_to_be(sge);
-}
-
-static void
hal_fcxp_tx_plog(struct bfa_s *bfa, u32 reqlen, struct bfa_fcxp_s *fcxp,
struct fchs_s *fchs)
{
@@ -846,7 +778,7 @@ bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, struct bfi_fcxp_send_req_s *send_req)
struct bfa_rport_s *rport = reqi->bfa_rport;
bfi_h2i_set(send_req->mh, BFI_MC_FCXP, BFI_FCXP_H2I_SEND_REQ,
- bfa_lpuid(bfa));
+ bfa_fn_lpu(bfa));
send_req->fcxp_tag = cpu_to_be16(fcxp->fcxp_tag);
if (rport) {
@@ -860,7 +792,7 @@ bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, struct bfi_fcxp_send_req_s *send_req)
}
send_req->vf_id = cpu_to_be16(reqi->vf_id);
- send_req->lp_tag = reqi->lp_tag;
+ send_req->lp_fwtag = bfa_lps_get_fwtag(bfa, reqi->lp_tag);
send_req->class = reqi->class;
send_req->rsp_timeout = rspi->rsp_timeout;
send_req->cts = reqi->cts;
@@ -873,18 +805,16 @@ bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, struct bfi_fcxp_send_req_s *send_req)
* setup req sgles
*/
if (fcxp->use_ireqbuf == 1) {
- hal_fcxp_set_local_sges(send_req->req_sge, reqi->req_tot_len,
+ bfa_alen_set(&send_req->req_alen, reqi->req_tot_len,
BFA_FCXP_REQ_PLD_PA(fcxp));
} else {
if (fcxp->nreq_sgles > 0) {
WARN_ON(fcxp->nreq_sgles != 1);
- hal_fcxp_set_local_sges(send_req->req_sge,
- reqi->req_tot_len,
- fcxp->req_sga_cbfn(fcxp->caller,
- 0));
+ bfa_alen_set(&send_req->req_alen, reqi->req_tot_len,
+ fcxp->req_sga_cbfn(fcxp->caller, 0));
} else {
WARN_ON(reqi->req_tot_len != 0);
- hal_fcxp_set_local_sges(send_req->rsp_sge, 0, 0);
+ bfa_alen_set(&send_req->rsp_alen, 0, 0);
}
}
@@ -894,25 +824,23 @@ bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, struct bfi_fcxp_send_req_s *send_req)
if (fcxp->use_irspbuf == 1) {
WARN_ON(rspi->rsp_maxlen > BFA_FCXP_MAX_LBUF_SZ);
- hal_fcxp_set_local_sges(send_req->rsp_sge, rspi->rsp_maxlen,
+ bfa_alen_set(&send_req->rsp_alen, rspi->rsp_maxlen,
BFA_FCXP_RSP_PLD_PA(fcxp));
-
} else {
if (fcxp->nrsp_sgles > 0) {
WARN_ON(fcxp->nrsp_sgles != 1);
- hal_fcxp_set_local_sges(send_req->rsp_sge,
- rspi->rsp_maxlen,
- fcxp->rsp_sga_cbfn(fcxp->caller,
- 0));
+ bfa_alen_set(&send_req->rsp_alen, rspi->rsp_maxlen,
+ fcxp->rsp_sga_cbfn(fcxp->caller, 0));
+
} else {
WARN_ON(rspi->rsp_maxlen != 0);
- hal_fcxp_set_local_sges(send_req->rsp_sge, 0, 0);
+ bfa_alen_set(&send_req->rsp_alen, 0, 0);
}
}
hal_fcxp_tx_plog(bfa, reqi->req_tot_len, fcxp, &reqi->fchs);
- bfa_reqq_produce(bfa, BFA_REQQ_FCXP);
+ bfa_reqq_produce(bfa, BFA_REQQ_FCXP, send_req->mh);
bfa_trc(bfa, bfa_reqq_pi(bfa, BFA_REQQ_FCXP));
bfa_trc(bfa, bfa_reqq_ci(bfa, BFA_REQQ_FCXP));
@@ -938,21 +866,23 @@ bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, struct bfi_fcxp_send_req_s *send_req)
* Address (given the sge index).
* @param[in] get_rsp_sglen function ptr to be called to get a response SG
* len (given the sge index).
+ * @param[in] req Allocated FCXP is used to send req or rsp?
+ * request - BFA_TRUE, response - BFA_FALSE
*
* @return FCXP instance. NULL on failure.
*/
struct bfa_fcxp_s *
-bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
- int nrsp_sgles, bfa_fcxp_get_sgaddr_t req_sga_cbfn,
- bfa_fcxp_get_sglen_t req_sglen_cbfn,
- bfa_fcxp_get_sgaddr_t rsp_sga_cbfn,
- bfa_fcxp_get_sglen_t rsp_sglen_cbfn)
+bfa_fcxp_req_rsp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
+ int nrsp_sgles, bfa_fcxp_get_sgaddr_t req_sga_cbfn,
+ bfa_fcxp_get_sglen_t req_sglen_cbfn,
+ bfa_fcxp_get_sgaddr_t rsp_sga_cbfn,
+ bfa_fcxp_get_sglen_t rsp_sglen_cbfn, bfa_boolean_t req)
{
struct bfa_fcxp_s *fcxp = NULL;
WARN_ON(bfa == NULL);
- fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa));
+ fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa), req);
if (fcxp == NULL)
return NULL;
@@ -978,19 +908,11 @@ bfa_fcxp_get_reqbuf(struct bfa_fcxp_s *fcxp)
void *reqbuf;
WARN_ON(fcxp->use_ireqbuf != 1);
- reqbuf = ((u8 *)mod->req_pld_list_kva) +
- fcxp->fcxp_tag * mod->req_pld_sz;
+ reqbuf = bfa_mem_get_dmabuf_kva(mod, fcxp->fcxp_tag,
+ mod->req_pld_sz + mod->rsp_pld_sz);
return reqbuf;
}
-u32
-bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp)
-{
- struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod;
-
- return mod->req_pld_sz;
-}
-
/*
* Get the internal response buffer pointer
*
@@ -1002,13 +924,15 @@ void *
bfa_fcxp_get_rspbuf(struct bfa_fcxp_s *fcxp)
{
struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod;
- void *rspbuf;
+ void *fcxp_buf;
WARN_ON(fcxp->use_irspbuf != 1);
- rspbuf = ((u8 *)mod->rsp_pld_list_kva) +
- fcxp->fcxp_tag * mod->rsp_pld_sz;
- return rspbuf;
+ fcxp_buf = bfa_mem_get_dmabuf_kva(mod, fcxp->fcxp_tag,
+ mod->req_pld_sz + mod->rsp_pld_sz);
+
+ /* fcxp_buf = req_buf + rsp_buf :- add req_buf_sz to get to rsp_buf */
+ return ((u8 *) fcxp_buf) + mod->req_pld_sz;
}
/*
@@ -1091,33 +1015,21 @@ bfa_fcxp_send(struct bfa_fcxp_s *fcxp, struct bfa_rport_s *rport,
bfa_fcxp_queue(fcxp, send_req);
}
-/*
- * Abort a BFA FCXP
- *
- * @param[in] fcxp BFA fcxp pointer
- *
- * @return void
- */
-bfa_status_t
-bfa_fcxp_abort(struct bfa_fcxp_s *fcxp)
-{
- bfa_trc(fcxp->fcxp_mod->bfa, fcxp->fcxp_tag);
- WARN_ON(1);
- return BFA_STATUS_OK;
-}
-
void
-bfa_fcxp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe,
+bfa_fcxp_req_rsp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe,
bfa_fcxp_alloc_cbfn_t alloc_cbfn, void *alloc_cbarg,
void *caller, int nreq_sgles,
int nrsp_sgles, bfa_fcxp_get_sgaddr_t req_sga_cbfn,
bfa_fcxp_get_sglen_t req_sglen_cbfn,
bfa_fcxp_get_sgaddr_t rsp_sga_cbfn,
- bfa_fcxp_get_sglen_t rsp_sglen_cbfn)
+ bfa_fcxp_get_sglen_t rsp_sglen_cbfn, bfa_boolean_t req)
{
struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa);
- WARN_ON(!list_empty(&mod->fcxp_free_q));
+ if (req)
+ WARN_ON(!list_empty(&mod->fcxp_req_free_q));
+ else
+ WARN_ON(!list_empty(&mod->fcxp_rsp_free_q));
wqe->alloc_cbfn = alloc_cbfn;
wqe->alloc_cbarg = alloc_cbarg;
@@ -1130,7 +1042,10 @@ bfa_fcxp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe,
wqe->rsp_sga_cbfn = rsp_sga_cbfn;
wqe->rsp_sglen_cbfn = rsp_sglen_cbfn;
- list_add_tail(&wqe->qe, &mod->wait_q);
+ if (req)
+ list_add_tail(&wqe->qe, &mod->req_wait_q);
+ else
+ list_add_tail(&wqe->qe, &mod->rsp_wait_q);
}
void
@@ -1138,7 +1053,8 @@ bfa_fcxp_walloc_cancel(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe)
{
struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa);
- WARN_ON(!bfa_q_is_on_q(&mod->wait_q, wqe));
+ WARN_ON(!bfa_q_is_on_q(&mod->req_wait_q, wqe) ||
+ !bfa_q_is_on_q(&mod->rsp_wait_q, wqe));
list_del(&wqe->qe);
}
@@ -1181,6 +1097,23 @@ bfa_fcxp_get_maxrsp(struct bfa_s *bfa)
return mod->rsp_pld_sz;
}
+void
+bfa_fcxp_res_recfg(struct bfa_s *bfa, u16 num_fcxp_fw)
+{
+ struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa);
+ struct list_head *qe;
+ int i;
+
+ for (i = 0; i < (mod->num_fcxps - num_fcxp_fw); i++) {
+ if (i < ((mod->num_fcxps - num_fcxp_fw) / 2)) {
+ bfa_q_deq_tail(&mod->fcxp_req_free_q, &qe);
+ list_add_tail(qe, &mod->fcxp_req_unused_q);
+ } else {
+ bfa_q_deq_tail(&mod->fcxp_rsp_free_q, &qe);
+ list_add_tail(qe, &mod->fcxp_rsp_unused_q);
+ }
+ }
+}
/*
* BFA LPS state machine functions
@@ -1192,7 +1125,7 @@ bfa_fcxp_get_maxrsp(struct bfa_s *bfa)
static void
bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, event);
switch (event) {
@@ -1232,6 +1165,12 @@ bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event)
* Just ignore
*/
break;
+ case BFA_LPS_SM_SET_N2N_PID:
+ /*
+ * When topology is set to loop, bfa_lps_set_n2n_pid() sends
+ * this event. Ignore this event.
+ */
+ break;
default:
bfa_sm_fault(lps->bfa, event);
@@ -1244,7 +1183,7 @@ bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event)
static void
bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, event);
switch (event) {
@@ -1278,6 +1217,7 @@ bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event)
break;
case BFA_LPS_SM_OFFLINE:
+ case BFA_LPS_SM_DELETE:
bfa_sm_set_state(lps, bfa_lps_sm_init);
break;
@@ -1297,15 +1237,17 @@ bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event)
static void
bfa_lps_sm_loginwait(struct bfa_lps_s *lps, enum bfa_lps_event event)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, event);
switch (event) {
case BFA_LPS_SM_RESUME:
bfa_sm_set_state(lps, bfa_lps_sm_login);
+ bfa_lps_send_login(lps);
break;
case BFA_LPS_SM_OFFLINE:
+ case BFA_LPS_SM_DELETE:
bfa_sm_set_state(lps, bfa_lps_sm_init);
bfa_reqq_wcancel(&lps->wqe);
break;
@@ -1329,7 +1271,7 @@ bfa_lps_sm_loginwait(struct bfa_lps_s *lps, enum bfa_lps_event event)
static void
bfa_lps_sm_online(struct bfa_lps_s *lps, enum bfa_lps_event event)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, event);
switch (event) {
@@ -1378,7 +1320,7 @@ bfa_lps_sm_online(struct bfa_lps_s *lps, enum bfa_lps_event event)
static void
bfa_lps_sm_online_n2n_pid_wait(struct bfa_lps_s *lps, enum bfa_lps_event event)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, event);
switch (event) {
@@ -1420,16 +1362,17 @@ bfa_lps_sm_online_n2n_pid_wait(struct bfa_lps_s *lps, enum bfa_lps_event event)
static void
bfa_lps_sm_logout(struct bfa_lps_s *lps, enum bfa_lps_event event)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, event);
switch (event) {
case BFA_LPS_SM_FWRSP:
+ case BFA_LPS_SM_OFFLINE:
bfa_sm_set_state(lps, bfa_lps_sm_init);
bfa_lps_logout_comp(lps);
break;
- case BFA_LPS_SM_OFFLINE:
+ case BFA_LPS_SM_DELETE:
bfa_sm_set_state(lps, bfa_lps_sm_init);
break;
@@ -1444,7 +1387,7 @@ bfa_lps_sm_logout(struct bfa_lps_s *lps, enum bfa_lps_event event)
static void
bfa_lps_sm_logowait(struct bfa_lps_s *lps, enum bfa_lps_event event)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, event);
switch (event) {
@@ -1454,6 +1397,7 @@ bfa_lps_sm_logowait(struct bfa_lps_s *lps, enum bfa_lps_event event)
break;
case BFA_LPS_SM_OFFLINE:
+ case BFA_LPS_SM_DELETE:
bfa_sm_set_state(lps, bfa_lps_sm_init);
bfa_reqq_wcancel(&lps->wqe);
break;
@@ -1472,68 +1416,57 @@ bfa_lps_sm_logowait(struct bfa_lps_s *lps, enum bfa_lps_event event)
/*
* return memory requirement
*/
-static void
-bfa_lps_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len,
- u32 *dm_len)
+void
+bfa_lps_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo,
+ struct bfa_s *bfa)
{
+ struct bfa_mem_kva_s *lps_kva = BFA_MEM_LPS_KVA(bfa);
+
if (cfg->drvcfg.min_cfg)
- *ndm_len += sizeof(struct bfa_lps_s) * BFA_LPS_MIN_LPORTS;
+ bfa_mem_kva_setup(minfo, lps_kva,
+ sizeof(struct bfa_lps_s) * BFA_LPS_MIN_LPORTS);
else
- *ndm_len += sizeof(struct bfa_lps_s) * BFA_LPS_MAX_LPORTS;
+ bfa_mem_kva_setup(minfo, lps_kva,
+ sizeof(struct bfa_lps_s) * BFA_LPS_MAX_LPORTS);
}
/*
* bfa module attach at initialization time
*/
-static void
+void
bfa_lps_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
+ struct bfa_pcidev_s *pcidev)
{
struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa);
struct bfa_lps_s *lps;
int i;
- memset(mod, 0, sizeof(struct bfa_lps_mod_s));
mod->num_lps = BFA_LPS_MAX_LPORTS;
if (cfg->drvcfg.min_cfg)
mod->num_lps = BFA_LPS_MIN_LPORTS;
else
mod->num_lps = BFA_LPS_MAX_LPORTS;
- mod->lps_arr = lps = (struct bfa_lps_s *) bfa_meminfo_kva(meminfo);
+ mod->lps_arr = lps = (struct bfa_lps_s *) bfa_mem_kva_curp(mod);
- bfa_meminfo_kva(meminfo) += mod->num_lps * sizeof(struct bfa_lps_s);
+ bfa_mem_kva_curp(mod) += mod->num_lps * sizeof(struct bfa_lps_s);
INIT_LIST_HEAD(&mod->lps_free_q);
INIT_LIST_HEAD(&mod->lps_active_q);
+ INIT_LIST_HEAD(&mod->lps_login_q);
for (i = 0; i < mod->num_lps; i++, lps++) {
lps->bfa = bfa;
- lps->lp_tag = (u8) i;
+ lps->bfa_tag = (u8) i;
lps->reqq = BFA_REQQ_LPS;
bfa_reqq_winit(&lps->wqe, bfa_lps_reqq_resume, lps);
list_add_tail(&lps->qe, &mod->lps_free_q);
}
}
-static void
-bfa_lps_detach(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_lps_start(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_lps_stop(struct bfa_s *bfa)
-{
-}
-
/*
* IOC in disabled state -- consider all lps offline
*/
-static void
+void
bfa_lps_iocdisable(struct bfa_s *bfa)
{
struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa);
@@ -1544,6 +1477,11 @@ bfa_lps_iocdisable(struct bfa_s *bfa)
lps = (struct bfa_lps_s *) qe;
bfa_sm_send_event(lps, BFA_LPS_SM_OFFLINE);
}
+ list_for_each_safe(qe, qen, &mod->lps_login_q) {
+ lps = (struct bfa_lps_s *) qe;
+ bfa_sm_send_event(lps, BFA_LPS_SM_OFFLINE);
+ }
+ list_splice_tail_init(&mod->lps_login_q, &mod->lps_active_q);
}
/*
@@ -1555,12 +1493,13 @@ bfa_lps_login_rsp(struct bfa_s *bfa, struct bfi_lps_login_rsp_s *rsp)
struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa);
struct bfa_lps_s *lps;
- WARN_ON(rsp->lp_tag >= mod->num_lps);
- lps = BFA_LPS_FROM_TAG(mod, rsp->lp_tag);
+ WARN_ON(rsp->bfa_tag >= mod->num_lps);
+ lps = BFA_LPS_FROM_TAG(mod, rsp->bfa_tag);
lps->status = rsp->status;
switch (rsp->status) {
case BFA_STATUS_OK:
+ lps->fw_tag = rsp->fw_tag;
lps->fport = rsp->f_port;
if (lps->fport)
lps->lp_pid = rsp->lp_pid;
@@ -1586,14 +1525,46 @@ bfa_lps_login_rsp(struct bfa_s *bfa, struct bfi_lps_login_rsp_s *rsp)
break;
+ case BFA_STATUS_VPORT_MAX:
+ if (rsp->ext_status)
+ bfa_lps_no_res(lps, rsp->ext_status);
+ break;
+
default:
/* Nothing to do with other status */
break;
}
+ list_del(&lps->qe);
+ list_add_tail(&lps->qe, &mod->lps_active_q);
bfa_sm_send_event(lps, BFA_LPS_SM_FWRSP);
}
+static void
+bfa_lps_no_res(struct bfa_lps_s *first_lps, u8 count)
+{
+ struct bfa_s *bfa = first_lps->bfa;
+ struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa);
+ struct list_head *qe, *qe_next;
+ struct bfa_lps_s *lps;
+
+ bfa_trc(bfa, count);
+
+ qe = bfa_q_next(first_lps);
+
+ while (count && qe) {
+ qe_next = bfa_q_next(qe);
+ lps = (struct bfa_lps_s *)qe;
+ bfa_trc(bfa, lps->bfa_tag);
+ lps->status = first_lps->status;
+ list_del(&lps->qe);
+ list_add_tail(&lps->qe, &mod->lps_active_q);
+ bfa_sm_send_event(lps, BFA_LPS_SM_FWRSP);
+ qe = qe_next;
+ count--;
+ }
+}
+
/*
* Firmware logout response
*/
@@ -1603,8 +1574,8 @@ bfa_lps_logout_rsp(struct bfa_s *bfa, struct bfi_lps_logout_rsp_s *rsp)
struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa);
struct bfa_lps_s *lps;
- WARN_ON(rsp->lp_tag >= mod->num_lps);
- lps = BFA_LPS_FROM_TAG(mod, rsp->lp_tag);
+ WARN_ON(rsp->bfa_tag >= mod->num_lps);
+ lps = BFA_LPS_FROM_TAG(mod, rsp->bfa_tag);
bfa_sm_send_event(lps, BFA_LPS_SM_FWRSP);
}
@@ -1618,7 +1589,7 @@ bfa_lps_rx_cvl_event(struct bfa_s *bfa, struct bfi_lps_cvl_event_s *cvl)
struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa);
struct bfa_lps_s *lps;
- lps = BFA_LPS_FROM_TAG(mod, cvl->lp_tag);
+ lps = BFA_LPS_FROM_TAG(mod, cvl->bfa_tag);
bfa_sm_send_event(lps, BFA_LPS_SM_RX_CVL);
}
@@ -1653,15 +1624,16 @@ bfa_lps_free(struct bfa_lps_s *lps)
static void
bfa_lps_send_login(struct bfa_lps_s *lps)
{
+ struct bfa_lps_mod_s *mod = BFA_LPS_MOD(lps->bfa);
struct bfi_lps_login_req_s *m;
m = bfa_reqq_next(lps->bfa, lps->reqq);
WARN_ON(!m);
bfi_h2i_set(m->mh, BFI_MC_LPS, BFI_LPS_H2I_LOGIN_REQ,
- bfa_lpuid(lps->bfa));
+ bfa_fn_lpu(lps->bfa));
- m->lp_tag = lps->lp_tag;
+ m->bfa_tag = lps->bfa_tag;
m->alpa = lps->alpa;
m->pdu_size = cpu_to_be16(lps->pdusz);
m->pwwn = lps->pwwn;
@@ -1669,7 +1641,9 @@ bfa_lps_send_login(struct bfa_lps_s *lps)
m->fdisc = lps->fdisc;
m->auth_en = lps->auth_en;
- bfa_reqq_produce(lps->bfa, lps->reqq);
+ bfa_reqq_produce(lps->bfa, lps->reqq, m->mh);
+ list_del(&lps->qe);
+ list_add_tail(&lps->qe, &mod->lps_login_q);
}
/*
@@ -1684,11 +1658,11 @@ bfa_lps_send_logout(struct bfa_lps_s *lps)
WARN_ON(!m);
bfi_h2i_set(m->mh, BFI_MC_LPS, BFI_LPS_H2I_LOGOUT_REQ,
- bfa_lpuid(lps->bfa));
+ bfa_fn_lpu(lps->bfa));
- m->lp_tag = lps->lp_tag;
+ m->fw_tag = lps->fw_tag;
m->port_name = lps->pwwn;
- bfa_reqq_produce(lps->bfa, lps->reqq);
+ bfa_reqq_produce(lps->bfa, lps->reqq, m->mh);
}
/*
@@ -1703,11 +1677,11 @@ bfa_lps_send_set_n2n_pid(struct bfa_lps_s *lps)
WARN_ON(!m);
bfi_h2i_set(m->mh, BFI_MC_LPS, BFI_LPS_H2I_N2N_PID_REQ,
- bfa_lpuid(lps->bfa));
+ bfa_fn_lpu(lps->bfa));
- m->lp_tag = lps->lp_tag;
+ m->fw_tag = lps->fw_tag;
m->lp_pid = lps->lp_pid;
- bfa_reqq_produce(lps->bfa, lps->reqq);
+ bfa_reqq_produce(lps->bfa, lps->reqq, m->mh);
}
/*
@@ -1758,6 +1732,8 @@ bfa_lps_logout_comp_cb(void *arg, bfa_boolean_t complete)
if (lps->fdisc)
bfa_cb_lps_fdisclogo_comp(lps->bfa->bfad, lps->uarg);
+ else
+ bfa_cb_lps_flogo_comp(lps->bfa->bfad, lps->uarg);
}
/*
@@ -1898,6 +1874,13 @@ bfa_lps_fdisclogo(struct bfa_lps_s *lps)
bfa_sm_send_event(lps, BFA_LPS_SM_LOGOUT);
}
+u8
+bfa_lps_get_fwtag(struct bfa_s *bfa, u8 lp_tag)
+{
+ struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa);
+
+ return BFA_LPS_FROM_TAG(mod, lp_tag)->fw_tag;
+}
/*
* Return lport services tag given the pid
@@ -1911,7 +1894,7 @@ bfa_lps_get_tag_from_pid(struct bfa_s *bfa, u32 pid)
for (i = 0, lps = mod->lps_arr; i < mod->num_lps; i++, lps++) {
if (lps->lp_pid == pid)
- return lps->lp_tag;
+ return lps->bfa_tag;
}
/* Return base port tag anyway */
@@ -1936,7 +1919,7 @@ bfa_lps_get_base_pid(struct bfa_s *bfa)
void
bfa_lps_set_n2n_pid(struct bfa_lps_s *lps, uint32_t n2n_pid)
{
- bfa_trc(lps->bfa, lps->lp_tag);
+ bfa_trc(lps->bfa, lps->bfa_tag);
bfa_trc(lps->bfa, n2n_pid);
lps->lp_pid = n2n_pid;
@@ -1955,15 +1938,15 @@ bfa_lps_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
msg.msg = m;
switch (m->mhdr.msg_id) {
- case BFI_LPS_H2I_LOGIN_RSP:
+ case BFI_LPS_I2H_LOGIN_RSP:
bfa_lps_login_rsp(bfa, msg.login_rsp);
break;
- case BFI_LPS_H2I_LOGOUT_RSP:
+ case BFI_LPS_I2H_LOGOUT_RSP:
bfa_lps_logout_rsp(bfa, msg.logout_rsp);
break;
- case BFI_LPS_H2I_CVL_EVENT:
+ case BFI_LPS_I2H_CVL_EVENT:
bfa_lps_rx_cvl_event(bfa, msg.cvl_event);
break;
@@ -1973,6 +1956,24 @@ bfa_lps_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
}
}
+static void
+bfa_fcport_aen_post(struct bfa_fcport_s *fcport, enum bfa_port_aen_event event)
+{
+ struct bfad_s *bfad = (struct bfad_s *)fcport->bfa->bfad;
+ struct bfa_aen_entry_s *aen_entry;
+
+ bfad_get_aen_entry(bfad, aen_entry);
+ if (!aen_entry)
+ return;
+
+ aen_entry->aen_data.port.ioc_type = bfa_get_type(fcport->bfa);
+ aen_entry->aen_data.port.pwwn = fcport->pwwn;
+
+ /* Send the AEN notification */
+ bfad_im_post_vendor_event(aen_entry, bfad, ++fcport->bfa->bfa_aen_seq,
+ BFA_AEN_CAT_PORT, event);
+}
+
/*
* FC PORT state machine functions
*/
@@ -2061,6 +2062,7 @@ bfa_fcport_sm_enabling_qwait(struct bfa_fcport_s *fcport,
wwn2str(pwwn_buf, fcport->pwwn);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port disabled: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
break;
case BFA_FCPORT_SM_LINKUP:
@@ -2076,6 +2078,12 @@ bfa_fcport_sm_enabling_qwait(struct bfa_fcport_s *fcport,
bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown);
break;
+ case BFA_FCPORT_SM_FAA_MISCONFIG:
+ bfa_fcport_reset_linkinfo(fcport);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig);
+ break;
+
default:
bfa_sm_fault(fcport->bfa, event);
}
@@ -2121,6 +2129,7 @@ bfa_fcport_sm_enabling(struct bfa_fcport_s *fcport,
wwn2str(pwwn_buf, fcport->pwwn);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port disabled: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
break;
case BFA_FCPORT_SM_STOP:
@@ -2131,6 +2140,12 @@ bfa_fcport_sm_enabling(struct bfa_fcport_s *fcport,
bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown);
break;
+ case BFA_FCPORT_SM_FAA_MISCONFIG:
+ bfa_fcport_reset_linkinfo(fcport);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig);
+ break;
+
default:
bfa_sm_fault(fcport->bfa, event);
}
@@ -2156,11 +2171,11 @@ bfa_fcport_sm_linkdown(struct bfa_fcport_s *fcport,
if (!bfa_ioc_get_fcmode(&fcport->bfa->ioc)) {
bfa_trc(fcport->bfa,
- pevent->link_state.vc_fcf.fcf.fipenabled);
+ pevent->link_state.attr.vc_fcf.fcf.fipenabled);
bfa_trc(fcport->bfa,
- pevent->link_state.vc_fcf.fcf.fipfailed);
+ pevent->link_state.attr.vc_fcf.fcf.fipfailed);
- if (pevent->link_state.vc_fcf.fcf.fipfailed)
+ if (pevent->link_state.attr.vc_fcf.fcf.fipfailed)
bfa_plog_str(fcport->bfa->plog, BFA_PL_MID_HAL,
BFA_PL_EID_FIP_FCF_DISC, 0,
"FIP FCF Discovery Failed");
@@ -2174,6 +2189,12 @@ bfa_fcport_sm_linkdown(struct bfa_fcport_s *fcport,
wwn2str(pwwn_buf, fcport->pwwn);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port online: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_ONLINE);
+
+ /* If QoS is enabled and it is not online, send AEN */
+ if (fcport->cfg.qos_enabled &&
+ fcport->qos_attr.state != BFA_QOS_ONLINE)
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_QOS_NEG);
break;
case BFA_FCPORT_SM_LINKDOWN:
@@ -2200,6 +2221,7 @@ bfa_fcport_sm_linkdown(struct bfa_fcport_s *fcport,
wwn2str(pwwn_buf, fcport->pwwn);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port disabled: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
break;
case BFA_FCPORT_SM_STOP:
@@ -2210,6 +2232,12 @@ bfa_fcport_sm_linkdown(struct bfa_fcport_s *fcport,
bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown);
break;
+ case BFA_FCPORT_SM_FAA_MISCONFIG:
+ bfa_fcport_reset_linkinfo(fcport);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig);
+ break;
+
default:
bfa_sm_fault(fcport->bfa, event);
}
@@ -2245,8 +2273,10 @@ bfa_fcport_sm_linkup(struct bfa_fcport_s *fcport,
wwn2str(pwwn_buf, fcport->pwwn);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port offline: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port disabled: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
break;
case BFA_FCPORT_SM_LINKDOWN:
@@ -2256,26 +2286,32 @@ bfa_fcport_sm_linkup(struct bfa_fcport_s *fcport,
bfa_plog_str(fcport->bfa->plog, BFA_PL_MID_HAL,
BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown");
wwn2str(pwwn_buf, fcport->pwwn);
- if (BFA_PORT_IS_DISABLED(fcport->bfa))
+ if (BFA_PORT_IS_DISABLED(fcport->bfa)) {
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port offline: WWN = %s\n", pwwn_buf);
- else
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
+ } else {
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
"Base port (WWN = %s) "
"lost fabric connectivity\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ }
break;
case BFA_FCPORT_SM_STOP:
bfa_sm_set_state(fcport, bfa_fcport_sm_stopped);
bfa_fcport_reset_linkinfo(fcport);
wwn2str(pwwn_buf, fcport->pwwn);
- if (BFA_PORT_IS_DISABLED(fcport->bfa))
+ if (BFA_PORT_IS_DISABLED(fcport->bfa)) {
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port offline: WWN = %s\n", pwwn_buf);
- else
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
+ } else {
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
"Base port (WWN = %s) "
"lost fabric connectivity\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ }
break;
case BFA_FCPORT_SM_HWFAIL:
@@ -2283,13 +2319,22 @@ bfa_fcport_sm_linkup(struct bfa_fcport_s *fcport,
bfa_fcport_reset_linkinfo(fcport);
bfa_fcport_scn(fcport, BFA_PORT_LINKDOWN, BFA_FALSE);
wwn2str(pwwn_buf, fcport->pwwn);
- if (BFA_PORT_IS_DISABLED(fcport->bfa))
+ if (BFA_PORT_IS_DISABLED(fcport->bfa)) {
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port offline: WWN = %s\n", pwwn_buf);
- else
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_OFFLINE);
+ } else {
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
"Base port (WWN = %s) "
"lost fabric connectivity\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ }
+ break;
+
+ case BFA_FCPORT_SM_FAA_MISCONFIG:
+ bfa_fcport_reset_linkinfo(fcport);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig);
break;
default:
@@ -2337,6 +2382,12 @@ bfa_fcport_sm_disabling_qwait(struct bfa_fcport_s *fcport,
bfa_reqq_wcancel(&fcport->reqq_wait);
break;
+ case BFA_FCPORT_SM_FAA_MISCONFIG:
+ bfa_fcport_reset_linkinfo(fcport);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT);
+ bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig);
+ break;
+
default:
bfa_sm_fault(fcport->bfa, event);
}
@@ -2420,6 +2471,7 @@ bfa_fcport_sm_disabling(struct bfa_fcport_s *fcport,
wwn2str(pwwn_buf, fcport->pwwn);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port enabled: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_ENABLE);
break;
case BFA_FCPORT_SM_STOP:
@@ -2474,6 +2526,7 @@ bfa_fcport_sm_disabled(struct bfa_fcport_s *fcport,
wwn2str(pwwn_buf, fcport->pwwn);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"Base port enabled: WWN = %s\n", pwwn_buf);
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_ENABLE);
break;
case BFA_FCPORT_SM_DISABLE:
@@ -2486,6 +2539,14 @@ bfa_fcport_sm_disabled(struct bfa_fcport_s *fcport,
bfa_sm_set_state(fcport, bfa_fcport_sm_iocfail);
break;
+ case BFA_FCPORT_SM_DPORTENABLE:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_dport);
+ break;
+
+ case BFA_FCPORT_SM_DDPORTENABLE:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_ddport);
+ break;
+
default:
bfa_sm_fault(fcport->bfa, event);
}
@@ -2566,6 +2627,115 @@ bfa_fcport_sm_iocfail(struct bfa_fcport_s *fcport,
}
}
+static void
+bfa_fcport_sm_dport(struct bfa_fcport_s *fcport, enum bfa_fcport_sm_event event)
+{
+ bfa_trc(fcport->bfa, event);
+
+ switch (event) {
+ case BFA_FCPORT_SM_DPORTENABLE:
+ case BFA_FCPORT_SM_DISABLE:
+ case BFA_FCPORT_SM_ENABLE:
+ case BFA_FCPORT_SM_START:
+ /*
+ * Ignore event for a port that is dport
+ */
+ break;
+
+ case BFA_FCPORT_SM_STOP:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_stopped);
+ break;
+
+ case BFA_FCPORT_SM_HWFAIL:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_iocfail);
+ break;
+
+ case BFA_FCPORT_SM_DPORTDISABLE:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_disabled);
+ break;
+
+ default:
+ bfa_sm_fault(fcport->bfa, event);
+ }
+}
+
+static void
+bfa_fcport_sm_ddport(struct bfa_fcport_s *fcport,
+ enum bfa_fcport_sm_event event)
+{
+ bfa_trc(fcport->bfa, event);
+
+ switch (event) {
+ case BFA_FCPORT_SM_DISABLE:
+ case BFA_FCPORT_SM_DDPORTDISABLE:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_disabled);
+ break;
+
+ case BFA_FCPORT_SM_DPORTENABLE:
+ case BFA_FCPORT_SM_DPORTDISABLE:
+ case BFA_FCPORT_SM_ENABLE:
+ case BFA_FCPORT_SM_START:
+ /*
+ * Ignore event for a port that is ddport
+ */
+ break;
+
+ case BFA_FCPORT_SM_STOP:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_stopped);
+ break;
+
+ case BFA_FCPORT_SM_HWFAIL:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_iocfail);
+ break;
+
+ default:
+ bfa_sm_fault(fcport->bfa, event);
+ }
+}
+
+static void
+bfa_fcport_sm_faa_misconfig(struct bfa_fcport_s *fcport,
+ enum bfa_fcport_sm_event event)
+{
+ bfa_trc(fcport->bfa, event);
+
+ switch (event) {
+ case BFA_FCPORT_SM_DPORTENABLE:
+ case BFA_FCPORT_SM_ENABLE:
+ case BFA_FCPORT_SM_START:
+ /*
+ * Ignore event for a port as there is FAA misconfig
+ */
+ break;
+
+ case BFA_FCPORT_SM_DISABLE:
+ if (bfa_fcport_send_disable(fcport))
+ bfa_sm_set_state(fcport, bfa_fcport_sm_disabling);
+ else
+ bfa_sm_set_state(fcport, bfa_fcport_sm_disabling_qwait);
+
+ bfa_fcport_reset_linkinfo(fcport);
+ bfa_fcport_scn(fcport, BFA_PORT_LINKDOWN, BFA_FALSE);
+ bfa_plog_str(fcport->bfa->plog, BFA_PL_MID_HAL,
+ BFA_PL_EID_PORT_DISABLE, 0, "Port Disable");
+ bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE);
+ break;
+
+ case BFA_FCPORT_SM_STOP:
+ bfa_sm_set_state(fcport, bfa_fcport_sm_stopped);
+ break;
+
+ case BFA_FCPORT_SM_HWFAIL:
+ bfa_fcport_reset_linkinfo(fcport);
+ bfa_fcport_scn(fcport, BFA_PORT_LINKDOWN, BFA_FALSE);
+ bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown);
+ break;
+
+ default:
+ bfa_sm_fault(fcport->bfa, event);
+ }
+}
+
/*
* Link state is down
*/
@@ -2776,11 +2946,13 @@ bfa_fcport_queue_cb(struct bfa_fcport_ln_s *ln, enum bfa_port_linkstate event)
#define FCPORT_STATS_DMA_SZ (BFA_ROUNDUP(sizeof(union bfa_fcport_stats_u), \
BFA_CACHELINE_SZ))
-static void
-bfa_fcport_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len,
- u32 *dm_len)
+void
+bfa_fcport_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo,
+ struct bfa_s *bfa)
{
- *dm_len += FCPORT_STATS_DMA_SZ;
+ struct bfa_mem_dma_s *fcport_dma = BFA_MEM_FCPORT_DMA(bfa);
+
+ bfa_mem_dma_setup(minfo, fcport_dma, FCPORT_STATS_DMA_SZ);
}
static void
@@ -2792,42 +2964,31 @@ bfa_fcport_qresume(void *cbarg)
}
static void
-bfa_fcport_mem_claim(struct bfa_fcport_s *fcport, struct bfa_meminfo_s *meminfo)
+bfa_fcport_mem_claim(struct bfa_fcport_s *fcport)
{
- u8 *dm_kva;
- u64 dm_pa;
+ struct bfa_mem_dma_s *fcport_dma = &fcport->fcport_dma;
- dm_kva = bfa_meminfo_dma_virt(meminfo);
- dm_pa = bfa_meminfo_dma_phys(meminfo);
-
- fcport->stats_kva = dm_kva;
- fcport->stats_pa = dm_pa;
- fcport->stats = (union bfa_fcport_stats_u *) dm_kva;
-
- dm_kva += FCPORT_STATS_DMA_SZ;
- dm_pa += FCPORT_STATS_DMA_SZ;
-
- bfa_meminfo_dma_virt(meminfo) = dm_kva;
- bfa_meminfo_dma_phys(meminfo) = dm_pa;
+ fcport->stats_kva = bfa_mem_dma_virt(fcport_dma);
+ fcport->stats_pa = bfa_mem_dma_phys(fcport_dma);
+ fcport->stats = (union bfa_fcport_stats_u *)
+ bfa_mem_dma_virt(fcport_dma);
}
/*
* Memory initialization.
*/
-static void
+void
bfa_fcport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
+ struct bfa_pcidev_s *pcidev)
{
struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
struct bfa_port_cfg_s *port_cfg = &fcport->cfg;
struct bfa_fcport_ln_s *ln = &fcport->ln;
- struct timeval tv;
- memset(fcport, 0, sizeof(struct bfa_fcport_s));
fcport->bfa = bfa;
ln->fcport = fcport;
- bfa_fcport_mem_claim(fcport, meminfo);
+ bfa_fcport_mem_claim(fcport);
bfa_sm_set_state(fcport, bfa_fcport_sm_uninit);
bfa_sm_set_state(ln, bfa_fcport_ln_sm_dn);
@@ -2835,8 +2996,8 @@ bfa_fcport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
/*
* initialize time stamp for stats reset
*/
- do_gettimeofday(&tv);
- fcport->stats_reset_time = tv.tv_sec;
+ fcport->stats_reset_time = ktime_get_seconds();
+ fcport->stats_dma_ready = BFA_FALSE;
/*
* initialize and set default configuration
@@ -2847,44 +3008,49 @@ bfa_fcport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
port_cfg->maxfrsize = 0;
port_cfg->trl_def_speed = BFA_PORT_SPEED_1GBPS;
+ port_cfg->qos_bw.high = BFA_QOS_BW_HIGH;
+ port_cfg->qos_bw.med = BFA_QOS_BW_MED;
+ port_cfg->qos_bw.low = BFA_QOS_BW_LOW;
- bfa_reqq_winit(&fcport->reqq_wait, bfa_fcport_qresume, fcport);
-}
+ fcport->fec_state = BFA_FEC_OFFLINE;
-static void
-bfa_fcport_detach(struct bfa_s *bfa)
-{
+ INIT_LIST_HEAD(&fcport->stats_pending_q);
+ INIT_LIST_HEAD(&fcport->statsclr_pending_q);
+
+ bfa_reqq_winit(&fcport->reqq_wait, bfa_fcport_qresume, fcport);
}
-/*
- * Called when IOC is ready.
- */
-static void
+void
bfa_fcport_start(struct bfa_s *bfa)
{
bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_START);
}
/*
- * Called before IOC is stopped.
+ * Called when IOC failure is detected.
*/
-static void
-bfa_fcport_stop(struct bfa_s *bfa)
+void
+bfa_fcport_iocdisable(struct bfa_s *bfa)
{
- bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_STOP);
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ bfa_sm_send_event(fcport, BFA_FCPORT_SM_HWFAIL);
bfa_trunk_iocdisable(bfa);
}
/*
- * Called when IOC failure is detected.
+ * Update loop info in fcport for SCN online
*/
static void
-bfa_fcport_iocdisable(struct bfa_s *bfa)
+bfa_fcport_update_loop_info(struct bfa_fcport_s *fcport,
+ struct bfa_fcport_loop_info_s *loop_info)
{
- struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
-
- bfa_sm_send_event(fcport, BFA_FCPORT_SM_HWFAIL);
- bfa_trunk_iocdisable(bfa);
+ fcport->myalpa = loop_info->myalpa;
+ fcport->alpabm_valid =
+ loop_info->alpabm_val;
+ memcpy(fcport->alpabm.alpa_bm,
+ loop_info->alpabm.alpa_bm,
+ sizeof(struct fc_alpabm_s));
}
static void
@@ -2896,12 +3062,20 @@ bfa_fcport_update_linkinfo(struct bfa_fcport_s *fcport)
fcport->speed = pevent->link_state.speed;
fcport->topology = pevent->link_state.topology;
- if (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)
- fcport->myalpa = 0;
+ if (fcport->topology == BFA_PORT_TOPOLOGY_LOOP) {
+ bfa_fcport_update_loop_info(fcport,
+ &pevent->link_state.attr.loop_info);
+ return;
+ }
/* QoS Details */
fcport->qos_attr = pevent->link_state.qos_attr;
- fcport->qos_vc_attr = pevent->link_state.vc_fcf.qos_vc_attr;
+ fcport->qos_vc_attr = pevent->link_state.attr.vc_fcf.qos_vc_attr;
+
+ if (fcport->cfg.bb_cr_enabled)
+ fcport->bbcr_attr = pevent->link_state.attr.bbcr_attr;
+
+ fcport->fec_state = pevent->link_state.fec_state;
/*
* update trunk state if applicable
@@ -2910,7 +3084,8 @@ bfa_fcport_update_linkinfo(struct bfa_fcport_s *fcport)
trunk->attr.state = BFA_TRUNK_DISABLED;
/* update FCoE specific */
- fcport->fcoe_vlan = be16_to_cpu(pevent->link_state.vc_fcf.fcf.vlan);
+ fcport->fcoe_vlan =
+ be16_to_cpu(pevent->link_state.attr.vc_fcf.fcf.vlan);
bfa_trc(fcport->bfa, fcport->speed);
bfa_trc(fcport->bfa, fcport->topology);
@@ -2921,6 +3096,7 @@ bfa_fcport_reset_linkinfo(struct bfa_fcport_s *fcport)
{
fcport->speed = BFA_PORT_SPEED_UNKNOWN;
fcport->topology = BFA_PORT_TOPOLOGY_NONE;
+ fcport->fec_state = BFA_FEC_OFFLINE;
}
/*
@@ -2948,13 +3124,13 @@ bfa_fcport_send_enable(struct bfa_fcport_s *fcport)
}
bfi_h2i_set(m->mh, BFI_MC_FCPORT, BFI_FCPORT_H2I_ENABLE_REQ,
- bfa_lpuid(fcport->bfa));
+ bfa_fn_lpu(fcport->bfa));
m->nwwn = fcport->nwwn;
m->pwwn = fcport->pwwn;
m->port_cfg = fcport->cfg;
m->msgtag = fcport->msgtag;
m->port_cfg.maxfrsize = cpu_to_be16(fcport->cfg.maxfrsize);
- m->use_flash_cfg = fcport->use_flash_cfg;
+ m->use_flash_cfg = fcport->use_flash_cfg;
bfa_dma_be_addr_set(m->stats_dma_addr, fcport->stats_pa);
bfa_trc(fcport->bfa, m->stats_dma_addr.a32.addr_lo);
bfa_trc(fcport->bfa, m->stats_dma_addr.a32.addr_hi);
@@ -2962,7 +3138,7 @@ bfa_fcport_send_enable(struct bfa_fcport_s *fcport)
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT);
+ bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT, m->mh);
return BFA_TRUE;
}
@@ -2991,13 +3167,13 @@ bfa_fcport_send_disable(struct bfa_fcport_s *fcport)
}
bfi_h2i_set(m->mh, BFI_MC_FCPORT, BFI_FCPORT_H2I_DISABLE_REQ,
- bfa_lpuid(fcport->bfa));
+ bfa_fn_lpu(fcport->bfa));
m->msgtag = fcport->msgtag;
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT);
+ bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT, m->mh);
return BFA_TRUE;
}
@@ -3013,32 +3189,6 @@ bfa_fcport_set_wwns(struct bfa_fcport_s *fcport)
}
static void
-bfa_fcport_send_txcredit(void *port_cbarg)
-{
-
- struct bfa_fcport_s *fcport = port_cbarg;
- struct bfi_fcport_set_svc_params_req_s *m;
-
- /*
- * check for room in queue to send request now
- */
- m = bfa_reqq_next(fcport->bfa, BFA_REQQ_PORT);
- if (!m) {
- bfa_trc(fcport->bfa, fcport->cfg.tx_bbcredit);
- return;
- }
-
- bfi_h2i_set(m->mh, BFI_MC_FCPORT, BFI_FCPORT_H2I_SET_SVC_PARAMS_REQ,
- bfa_lpuid(fcport->bfa));
- m->tx_bbcredit = cpu_to_be16((u16)fcport->cfg.tx_bbcredit);
-
- /*
- * queue I/O message to firmware
- */
- bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT);
-}
-
-static void
bfa_fcport_qos_stats_swap(struct bfa_qos_stats_s *d,
struct bfa_qos_stats_s *s)
{
@@ -3074,30 +3224,36 @@ bfa_fcport_fcoe_stats_swap(struct bfa_fcoe_stats_s *d,
static void
__bfa_cb_fcport_stats_get(void *cbarg, bfa_boolean_t complete)
{
- struct bfa_fcport_s *fcport = cbarg;
+ struct bfa_fcport_s *fcport = (struct bfa_fcport_s *)cbarg;
+ struct bfa_cb_pending_q_s *cb;
+ struct list_head *qe, *qen;
+ union bfa_fcport_stats_u *ret;
if (complete) {
- if (fcport->stats_status == BFA_STATUS_OK) {
- struct timeval tv;
-
- /* Swap FC QoS or FCoE stats */
- if (bfa_ioc_get_fcmode(&fcport->bfa->ioc)) {
- bfa_fcport_qos_stats_swap(
- &fcport->stats_ret->fcqos,
- &fcport->stats->fcqos);
- } else {
- bfa_fcport_fcoe_stats_swap(
- &fcport->stats_ret->fcoe,
- &fcport->stats->fcoe);
-
- do_gettimeofday(&tv);
- fcport->stats_ret->fcoe.secs_reset =
- tv.tv_sec - fcport->stats_reset_time;
+ time64_t time = ktime_get_seconds();
+
+ list_for_each_safe(qe, qen, &fcport->stats_pending_q) {
+ bfa_q_deq(&fcport->stats_pending_q, &qe);
+ cb = (struct bfa_cb_pending_q_s *)qe;
+ if (fcport->stats_status == BFA_STATUS_OK) {
+ ret = (union bfa_fcport_stats_u *)cb->data;
+ /* Swap FC QoS or FCoE stats */
+ if (bfa_ioc_get_fcmode(&fcport->bfa->ioc))
+ bfa_fcport_qos_stats_swap(&ret->fcqos,
+ &fcport->stats->fcqos);
+ else {
+ bfa_fcport_fcoe_stats_swap(&ret->fcoe,
+ &fcport->stats->fcoe);
+ ret->fcoe.secs_reset =
+ time - fcport->stats_reset_time;
+ }
}
+ bfa_cb_queue_status(fcport->bfa, &cb->hcb_qe,
+ fcport->stats_status);
}
- fcport->stats_cbfn(fcport->stats_cbarg, fcport->stats_status);
+ fcport->stats_status = BFA_STATUS_OK;
} else {
- fcport->stats_busy = BFA_FALSE;
+ INIT_LIST_HEAD(&fcport->stats_pending_q);
fcport->stats_status = BFA_STATUS_OK;
}
}
@@ -3115,8 +3271,7 @@ bfa_fcport_stats_get_timeout(void *cbarg)
}
fcport->stats_status = BFA_STATUS_ETIMER;
- bfa_cb_queue(fcport->bfa, &fcport->hcb_qe, __bfa_cb_fcport_stats_get,
- fcport);
+ __bfa_cb_fcport_stats_get(fcport, BFA_TRUE);
}
static void
@@ -3139,27 +3294,31 @@ bfa_fcport_send_stats_get(void *cbarg)
memset(msg, 0, sizeof(struct bfi_fcport_req_s));
bfi_h2i_set(msg->mh, BFI_MC_FCPORT, BFI_FCPORT_H2I_STATS_GET_REQ,
- bfa_lpuid(fcport->bfa));
- bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT);
+ bfa_fn_lpu(fcport->bfa));
+ bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT, msg->mh);
}
static void
__bfa_cb_fcport_stats_clr(void *cbarg, bfa_boolean_t complete)
{
- struct bfa_fcport_s *fcport = cbarg;
+ struct bfa_fcport_s *fcport = (struct bfa_fcport_s *) cbarg;
+ struct bfa_cb_pending_q_s *cb;
+ struct list_head *qe, *qen;
if (complete) {
- struct timeval tv;
-
/*
* re-initialize time stamp for stats reset
*/
- do_gettimeofday(&tv);
- fcport->stats_reset_time = tv.tv_sec;
-
- fcport->stats_cbfn(fcport->stats_cbarg, fcport->stats_status);
+ fcport->stats_reset_time = ktime_get_seconds();
+ list_for_each_safe(qe, qen, &fcport->statsclr_pending_q) {
+ bfa_q_deq(&fcport->statsclr_pending_q, &qe);
+ cb = (struct bfa_cb_pending_q_s *)qe;
+ bfa_cb_queue_status(fcport->bfa, &cb->hcb_qe,
+ fcport->stats_status);
+ }
+ fcport->stats_status = BFA_STATUS_OK;
} else {
- fcport->stats_busy = BFA_FALSE;
+ INIT_LIST_HEAD(&fcport->statsclr_pending_q);
fcport->stats_status = BFA_STATUS_OK;
}
}
@@ -3177,8 +3336,7 @@ bfa_fcport_stats_clr_timeout(void *cbarg)
}
fcport->stats_status = BFA_STATUS_ETIMER;
- bfa_cb_queue(fcport->bfa, &fcport->hcb_qe,
- __bfa_cb_fcport_stats_clr, fcport);
+ __bfa_cb_fcport_stats_clr(fcport, BFA_TRUE);
}
static void
@@ -3201,8 +3359,8 @@ bfa_fcport_send_stats_clear(void *cbarg)
memset(msg, 0, sizeof(struct bfi_fcport_req_s));
bfi_h2i_set(msg->mh, BFI_MC_FCPORT, BFI_FCPORT_H2I_STATS_CLEAR_REQ,
- bfa_lpuid(fcport->bfa));
- bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT);
+ bfa_fn_lpu(fcport->bfa));
+ bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT, msg->mh);
}
/*
@@ -3329,6 +3487,9 @@ bfa_fcport_init(struct bfa_s *bfa)
fcport->cfg.rx_bbcredit = bfa_ioc_rx_bbcredit(&bfa->ioc);
fcport->speed_sup = bfa_ioc_speed_sup(&bfa->ioc);
+ if (bfa_fcport_is_pbcdisabled(bfa))
+ bfa->modules.port.pbc_disabled = BFA_TRUE;
+
WARN_ON(!fcport->cfg.maxfrsize);
WARN_ON(!fcport->cfg.rx_bbcredit);
WARN_ON(!fcport->speed_sup);
@@ -3347,12 +3508,13 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg)
fcport->event_arg.i2hmsg = i2hmsg;
bfa_trc(bfa, msg->mhdr.msg_id);
- bfa_trc(bfa, bfa_sm_to_state(hal_port_sm_table, fcport->sm));
+ bfa_trc(bfa, bfa_fcport_sm_to_state(hal_port_sm_table, fcport->sm));
switch (msg->mhdr.msg_id) {
case BFI_FCPORT_I2H_ENABLE_RSP:
if (fcport->msgtag == i2hmsg.penable_rsp->msgtag) {
+ fcport->stats_dma_ready = BFA_TRUE;
if (fcport->use_flash_cfg) {
fcport->cfg = i2hmsg.penable_rsp->port_cfg;
fcport->cfg.maxfrsize =
@@ -3368,9 +3530,24 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg)
else
fcport->trunk.attr.state =
BFA_TRUNK_DISABLED;
+ fcport->qos_attr.qos_bw =
+ i2hmsg.penable_rsp->port_cfg.qos_bw;
fcport->use_flash_cfg = BFA_FALSE;
}
+ if (fcport->cfg.qos_enabled)
+ fcport->qos_attr.state = BFA_QOS_OFFLINE;
+ else
+ fcport->qos_attr.state = BFA_QOS_DISABLED;
+
+ fcport->qos_attr.qos_bw_op =
+ i2hmsg.penable_rsp->port_cfg.qos_bw;
+
+ if (fcport->cfg.bb_cr_enabled)
+ fcport->bbcr_attr.state = BFA_BBCR_OFFLINE;
+ else
+ fcport->bbcr_attr.state = BFA_BBCR_DISABLED;
+
bfa_sm_send_event(fcport, BFA_FCPORT_SM_FWRSP);
}
break;
@@ -3381,10 +3558,24 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg)
break;
case BFI_FCPORT_I2H_EVENT:
+ if (fcport->cfg.bb_cr_enabled)
+ fcport->bbcr_attr.state = BFA_BBCR_OFFLINE;
+ else
+ fcport->bbcr_attr.state = BFA_BBCR_DISABLED;
+
if (i2hmsg.event->link_state.linkstate == BFA_PORT_LINKUP)
bfa_sm_send_event(fcport, BFA_FCPORT_SM_LINKUP);
- else
- bfa_sm_send_event(fcport, BFA_FCPORT_SM_LINKDOWN);
+ else {
+ if (i2hmsg.event->link_state.linkstate_rsn ==
+ BFA_PORT_LINKSTATE_RSN_FAA_MISCONFIG)
+ bfa_sm_send_event(fcport,
+ BFA_FCPORT_SM_FAA_MISCONFIG);
+ else
+ bfa_sm_send_event(fcport,
+ BFA_FCPORT_SM_LINKDOWN);
+ }
+ fcport->qos_attr.qos_bw_op =
+ i2hmsg.event->link_state.qos_attr.qos_bw_op;
break;
case BFI_FCPORT_I2H_TRUNK_SCN:
@@ -3395,28 +3586,26 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg)
/*
* check for timer pop before processing the rsp
*/
- if (fcport->stats_busy == BFA_FALSE ||
- fcport->stats_status == BFA_STATUS_ETIMER)
+ if (list_empty(&fcport->stats_pending_q) ||
+ (fcport->stats_status == BFA_STATUS_ETIMER))
break;
bfa_timer_stop(&fcport->timer);
fcport->stats_status = i2hmsg.pstatsget_rsp->status;
- bfa_cb_queue(fcport->bfa, &fcport->hcb_qe,
- __bfa_cb_fcport_stats_get, fcport);
+ __bfa_cb_fcport_stats_get(fcport, BFA_TRUE);
break;
case BFI_FCPORT_I2H_STATS_CLEAR_RSP:
/*
* check for timer pop before processing the rsp
*/
- if (fcport->stats_busy == BFA_FALSE ||
- fcport->stats_status == BFA_STATUS_ETIMER)
+ if (list_empty(&fcport->statsclr_pending_q) ||
+ (fcport->stats_status == BFA_STATUS_ETIMER))
break;
bfa_timer_stop(&fcport->timer);
fcport->stats_status = BFA_STATUS_OK;
- bfa_cb_queue(fcport->bfa, &fcport->hcb_qe,
- __bfa_cb_fcport_stats_clr, fcport);
+ __bfa_cb_fcport_stats_clr(fcport, BFA_TRUE);
break;
case BFI_FCPORT_I2H_ENABLE_AEN:
@@ -3453,6 +3642,9 @@ bfa_fcport_enable(struct bfa_s *bfa)
{
struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+ if (bfa_fcport_is_pbcdisabled(bfa))
+ return BFA_STATUS_PBC;
+
if (bfa_ioc_is_disabled(&bfa->ioc))
return BFA_STATUS_IOC_DISABLED;
@@ -3466,6 +3658,8 @@ bfa_fcport_enable(struct bfa_s *bfa)
bfa_status_t
bfa_fcport_disable(struct bfa_s *bfa)
{
+ if (bfa_fcport_is_pbcdisabled(bfa))
+ return BFA_STATUS_PBC;
if (bfa_ioc_is_disabled(&bfa->ioc))
return BFA_STATUS_IOC_DISABLED;
@@ -3474,6 +3668,21 @@ bfa_fcport_disable(struct bfa_s *bfa)
return BFA_STATUS_OK;
}
+/* If PBC is disabled on port, return error */
+bfa_status_t
+bfa_fcport_is_pbcdisabled(struct bfa_s *bfa)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+ struct bfa_iocfc_s *iocfc = &bfa->iocfc;
+ struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp;
+
+ if (cfgrsp->pbc_cfg.port_enabled == BFI_PBC_PORT_DISABLED) {
+ bfa_trc(bfa, fcport->pwwn);
+ return BFA_STATUS_PBC;
+ }
+ return BFA_STATUS_OK;
+}
+
/*
* Configure port speed.
*/
@@ -3486,11 +3695,34 @@ bfa_fcport_cfg_speed(struct bfa_s *bfa, enum bfa_port_speed speed)
if (fcport->cfg.trunked == BFA_TRUE)
return BFA_STATUS_TRUNK_ENABLED;
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) &&
+ (speed == BFA_PORT_SPEED_16GBPS))
+ return BFA_STATUS_UNSUPP_SPEED;
if ((speed != BFA_PORT_SPEED_AUTO) && (speed > fcport->speed_sup)) {
bfa_trc(bfa, fcport->speed_sup);
return BFA_STATUS_UNSUPP_SPEED;
}
+ /* Port speed entered needs to be checked */
+ if (bfa_ioc_get_type(&fcport->bfa->ioc) == BFA_IOC_TYPE_FC) {
+ /* For CT2, 1G is not supported */
+ if ((speed == BFA_PORT_SPEED_1GBPS) &&
+ (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)))
+ return BFA_STATUS_UNSUPP_SPEED;
+
+ /* Already checked for Auto Speed and Max Speed supp */
+ if (!(speed == BFA_PORT_SPEED_1GBPS ||
+ speed == BFA_PORT_SPEED_2GBPS ||
+ speed == BFA_PORT_SPEED_4GBPS ||
+ speed == BFA_PORT_SPEED_8GBPS ||
+ speed == BFA_PORT_SPEED_16GBPS ||
+ speed == BFA_PORT_SPEED_AUTO))
+ return BFA_STATUS_UNSUPP_SPEED;
+ } else {
+ if (speed != BFA_PORT_SPEED_10GBPS)
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+
fcport->cfg.speed = speed;
return BFA_STATUS_OK;
@@ -3520,7 +3752,28 @@ bfa_fcport_cfg_topology(struct bfa_s *bfa, enum bfa_port_topology topology)
switch (topology) {
case BFA_PORT_TOPOLOGY_P2P:
+ break;
+
case BFA_PORT_TOPOLOGY_LOOP:
+ if ((bfa_fcport_is_qos_enabled(bfa) != BFA_FALSE) ||
+ (fcport->qos_attr.state != BFA_QOS_DISABLED))
+ return BFA_STATUS_ERROR_QOS_ENABLED;
+ if (fcport->cfg.ratelimit != BFA_FALSE)
+ return BFA_STATUS_ERROR_TRL_ENABLED;
+ if ((bfa_fcport_is_trunk_enabled(bfa) != BFA_FALSE) ||
+ (fcport->trunk.attr.state != BFA_TRUNK_DISABLED))
+ return BFA_STATUS_ERROR_TRUNK_ENABLED;
+ if ((bfa_fcport_get_speed(bfa) == BFA_PORT_SPEED_16GBPS) ||
+ (fcport->cfg.speed == BFA_PORT_SPEED_16GBPS))
+ return BFA_STATUS_UNSUPP_SPEED;
+ if (bfa_mfg_is_mezz(bfa->ioc.attr->card_type))
+ return BFA_STATUS_LOOP_UNSUPP_MEZZ;
+ if (bfa_fcport_is_dport(bfa) != BFA_FALSE)
+ return BFA_STATUS_DPORT_ERR;
+ if (bfa_fcport_is_ddport(bfa) != BFA_FALSE)
+ return BFA_STATUS_DPORT_ERR;
+ break;
+
case BFA_PORT_TOPOLOGY_AUTO:
break;
@@ -3543,6 +3796,17 @@ bfa_fcport_get_topology(struct bfa_s *bfa)
return fcport->topology;
}
+/*
+ * Get config topology.
+ */
+enum bfa_port_topology
+bfa_fcport_get_cfg_topology(struct bfa_s *bfa)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ return fcport->cfg.topology;
+}
+
bfa_status_t
bfa_fcport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa)
{
@@ -3570,15 +3834,6 @@ bfa_fcport_clr_hardalpa(struct bfa_s *bfa)
return BFA_STATUS_OK;
}
-bfa_boolean_t
-bfa_fcport_get_hardalpa(struct bfa_s *bfa, u8 *alpa)
-{
- struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
-
- *alpa = fcport->cfg.hardalpa;
- return fcport->cfg.cfg_hardalpa;
-}
-
u8
bfa_fcport_get_myalpa(struct bfa_s *bfa)
{
@@ -3618,9 +3873,11 @@ bfa_fcport_get_maxfrsize(struct bfa_s *bfa)
u8
bfa_fcport_get_rx_bbcredit(struct bfa_s *bfa)
{
- struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+ if (bfa_fcport_get_topology(bfa) != BFA_PORT_TOPOLOGY_LOOP)
+ return (BFA_FCPORT_MOD(bfa))->cfg.rx_bbcredit;
- return fcport->cfg.rx_bbcredit;
+ else
+ return 0;
}
void
@@ -3629,23 +3886,11 @@ bfa_fcport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit)
struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
fcport->cfg.tx_bbcredit = (u8)tx_bbcredit;
- bfa_fcport_send_txcredit(fcport);
}
/*
* Get port attributes.
*/
-
-wwn_t
-bfa_fcport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node)
-{
- struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
- if (node)
- return fcport->nwwn;
- else
- return fcport->pwwn;
-}
-
void
bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr)
{
@@ -3675,16 +3920,22 @@ bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr)
/* beacon attributes */
attr->beacon = fcport->beacon;
attr->link_e2e_beacon = fcport->link_e2e_beacon;
- attr->plog_enabled = (bfa_boolean_t)fcport->bfa->plog->plog_enabled;
- attr->io_profile = bfa_fcpim_get_io_profile(fcport->bfa);
attr->pport_cfg.path_tov = bfa_fcpim_path_tov_get(bfa);
attr->pport_cfg.q_depth = bfa_fcpim_qdepth_get(bfa);
- attr->port_state = bfa_sm_to_state(hal_port_sm_table, fcport->sm);
- if (bfa_ioc_is_disabled(&fcport->bfa->ioc))
- attr->port_state = BFA_PORT_ST_IOCDIS;
- else if (bfa_ioc_fw_mismatch(&fcport->bfa->ioc))
- attr->port_state = BFA_PORT_ST_FWMISMATCH;
+ attr->port_state = bfa_fcport_sm_to_state(hal_port_sm_table, fcport->sm);
+
+ attr->fec_state = fcport->fec_state;
+
+ /* PBC Disabled State */
+ if (bfa_fcport_is_pbcdisabled(bfa))
+ attr->port_state = BFA_PORT_ST_PREBOOT_DISABLED;
+ else {
+ if (bfa_ioc_is_disabled(&fcport->bfa->ioc))
+ attr->port_state = BFA_PORT_ST_IOCDIS;
+ else if (bfa_ioc_fw_mismatch(&fcport->bfa->ioc))
+ attr->port_state = BFA_PORT_ST_FWMISMATCH;
+ }
/* FCoE vlan */
attr->fcoe_vlan = fcport->fcoe_vlan;
@@ -3696,25 +3947,26 @@ bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr)
* Fetch port statistics (FCQoS or FCoE).
*/
bfa_status_t
-bfa_fcport_get_stats(struct bfa_s *bfa, union bfa_fcport_stats_u *stats,
- bfa_cb_port_t cbfn, void *cbarg)
+bfa_fcport_get_stats(struct bfa_s *bfa, struct bfa_cb_pending_q_s *cb)
{
struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
- if (fcport->stats_busy) {
- bfa_trc(bfa, fcport->stats_busy);
- return BFA_STATUS_DEVBUSY;
- }
+ if (!bfa_iocfc_is_operational(bfa) ||
+ !fcport->stats_dma_ready)
+ return BFA_STATUS_IOC_NON_OP;
- fcport->stats_busy = BFA_TRUE;
- fcport->stats_ret = stats;
- fcport->stats_cbfn = cbfn;
- fcport->stats_cbarg = cbarg;
+ if (!list_empty(&fcport->statsclr_pending_q))
+ return BFA_STATUS_DEVBUSY;
- bfa_fcport_send_stats_get(fcport);
+ if (list_empty(&fcport->stats_pending_q)) {
+ list_add_tail(&cb->hcb_qe.qe, &fcport->stats_pending_q);
+ bfa_fcport_send_stats_get(fcport);
+ bfa_timer_start(bfa, &fcport->timer,
+ bfa_fcport_stats_get_timeout,
+ fcport, BFA_FCPORT_STATS_TOV);
+ } else
+ list_add_tail(&cb->hcb_qe.qe, &fcport->stats_pending_q);
- bfa_timer_start(bfa, &fcport->timer, bfa_fcport_stats_get_timeout,
- fcport, BFA_FCPORT_STATS_TOV);
return BFA_STATUS_OK;
}
@@ -3722,27 +3974,29 @@ bfa_fcport_get_stats(struct bfa_s *bfa, union bfa_fcport_stats_u *stats,
* Reset port statistics (FCQoS or FCoE).
*/
bfa_status_t
-bfa_fcport_clear_stats(struct bfa_s *bfa, bfa_cb_port_t cbfn, void *cbarg)
+bfa_fcport_clear_stats(struct bfa_s *bfa, struct bfa_cb_pending_q_s *cb)
{
struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
- if (fcport->stats_busy) {
- bfa_trc(bfa, fcport->stats_busy);
- return BFA_STATUS_DEVBUSY;
- }
+ if (!bfa_iocfc_is_operational(bfa) ||
+ !fcport->stats_dma_ready)
+ return BFA_STATUS_IOC_NON_OP;
- fcport->stats_busy = BFA_TRUE;
- fcport->stats_cbfn = cbfn;
- fcport->stats_cbarg = cbarg;
+ if (!list_empty(&fcport->stats_pending_q))
+ return BFA_STATUS_DEVBUSY;
- bfa_fcport_send_stats_clear(fcport);
+ if (list_empty(&fcport->statsclr_pending_q)) {
+ list_add_tail(&cb->hcb_qe.qe, &fcport->statsclr_pending_q);
+ bfa_fcport_send_stats_clear(fcport);
+ bfa_timer_start(bfa, &fcport->timer,
+ bfa_fcport_stats_clr_timeout,
+ fcport, BFA_FCPORT_STATS_TOV);
+ } else
+ list_add_tail(&cb->hcb_qe.qe, &fcport->statsclr_pending_q);
- bfa_timer_start(bfa, &fcport->timer, bfa_fcport_stats_clr_timeout,
- fcport, BFA_FCPORT_STATS_TOV);
return BFA_STATUS_OK;
}
-
/*
* Fetch port attributes.
*/
@@ -3751,12 +4005,55 @@ bfa_fcport_is_disabled(struct bfa_s *bfa)
{
struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
- return bfa_sm_to_state(hal_port_sm_table, fcport->sm) ==
+ return bfa_fcport_sm_to_state(hal_port_sm_table, fcport->sm) ==
BFA_PORT_ST_DISABLED;
}
bfa_boolean_t
+bfa_fcport_is_dport(struct bfa_s *bfa)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ return (bfa_fcport_sm_to_state(hal_port_sm_table, fcport->sm) ==
+ BFA_PORT_ST_DPORT);
+}
+
+bfa_boolean_t
+bfa_fcport_is_ddport(struct bfa_s *bfa)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ return (bfa_fcport_sm_to_state(hal_port_sm_table, fcport->sm) ==
+ BFA_PORT_ST_DDPORT);
+}
+
+bfa_status_t
+bfa_fcport_set_qos_bw(struct bfa_s *bfa, struct bfa_qos_bw_s *qos_bw)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+ enum bfa_ioc_type_e ioc_type = bfa_get_type(bfa);
+
+ bfa_trc(bfa, ioc_type);
+
+ if ((qos_bw->high == 0) || (qos_bw->med == 0) || (qos_bw->low == 0))
+ return BFA_STATUS_QOS_BW_INVALID;
+
+ if ((qos_bw->high + qos_bw->med + qos_bw->low) != 100)
+ return BFA_STATUS_QOS_BW_INVALID;
+
+ if ((qos_bw->med > qos_bw->high) || (qos_bw->low > qos_bw->med) ||
+ (qos_bw->low > qos_bw->high))
+ return BFA_STATUS_QOS_BW_INVALID;
+
+ if ((ioc_type == BFA_IOC_TYPE_FC) &&
+ (fcport->cfg.topology != BFA_PORT_TOPOLOGY_LOOP))
+ fcport->cfg.qos_bw = *qos_bw;
+
+ return BFA_STATUS_OK;
+}
+
+bfa_boolean_t
bfa_fcport_is_ratelim(struct bfa_s *bfa)
{
struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
@@ -3778,6 +4075,22 @@ bfa_fcport_get_ratelim_speed(struct bfa_s *bfa)
}
+void
+bfa_fcport_beacon(void *dev, bfa_boolean_t beacon,
+ bfa_boolean_t link_e2e_beacon)
+{
+ struct bfa_s *bfa = dev;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ bfa_trc(bfa, beacon);
+ bfa_trc(bfa, link_e2e_beacon);
+ bfa_trc(bfa, fcport->beacon);
+ bfa_trc(bfa, fcport->link_e2e_beacon);
+
+ fcport->beacon = beacon;
+ fcport->link_e2e_beacon = link_e2e_beacon;
+}
+
bfa_boolean_t
bfa_fcport_is_linkup(struct bfa_s *bfa)
{
@@ -3797,6 +4110,123 @@ bfa_fcport_is_qos_enabled(struct bfa_s *bfa)
return fcport->cfg.qos_enabled;
}
+bfa_boolean_t
+bfa_fcport_is_trunk_enabled(struct bfa_s *bfa)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ return fcport->cfg.trunked;
+}
+
+bfa_status_t
+bfa_fcport_cfg_bbcr(struct bfa_s *bfa, bfa_boolean_t on_off, u8 bb_scn)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ bfa_trc(bfa, on_off);
+
+ if (bfa_ioc_get_type(&fcport->bfa->ioc) != BFA_IOC_TYPE_FC)
+ return BFA_STATUS_BBCR_FC_ONLY;
+
+ if (bfa_mfg_is_mezz(bfa->ioc.attr->card_type) &&
+ (bfa->ioc.attr->card_type != BFA_MFG_TYPE_CHINOOK))
+ return BFA_STATUS_CMD_NOTSUPP_MEZZ;
+
+ if (on_off) {
+ if (fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP)
+ return BFA_STATUS_TOPOLOGY_LOOP;
+
+ if (fcport->cfg.qos_enabled)
+ return BFA_STATUS_ERROR_QOS_ENABLED;
+
+ if (fcport->cfg.trunked)
+ return BFA_STATUS_TRUNK_ENABLED;
+
+ if ((fcport->cfg.speed != BFA_PORT_SPEED_AUTO) &&
+ (fcport->cfg.speed < bfa_ioc_speed_sup(&bfa->ioc)))
+ return BFA_STATUS_ERR_BBCR_SPEED_UNSUPPORT;
+
+ if (bfa_ioc_speed_sup(&bfa->ioc) < BFA_PORT_SPEED_8GBPS)
+ return BFA_STATUS_FEATURE_NOT_SUPPORTED;
+
+ if (fcport->cfg.bb_cr_enabled) {
+ if (bb_scn != fcport->cfg.bb_scn)
+ return BFA_STATUS_BBCR_CFG_NO_CHANGE;
+ else
+ return BFA_STATUS_NO_CHANGE;
+ }
+
+ if ((bb_scn == 0) || (bb_scn > BFA_BB_SCN_MAX))
+ bb_scn = BFA_BB_SCN_DEF;
+
+ fcport->cfg.bb_cr_enabled = on_off;
+ fcport->cfg.bb_scn = bb_scn;
+ } else {
+ if (!fcport->cfg.bb_cr_enabled)
+ return BFA_STATUS_NO_CHANGE;
+
+ fcport->cfg.bb_cr_enabled = on_off;
+ fcport->cfg.bb_scn = 0;
+ }
+
+ return BFA_STATUS_OK;
+}
+
+bfa_status_t
+bfa_fcport_get_bbcr_attr(struct bfa_s *bfa,
+ struct bfa_bbcr_attr_s *bbcr_attr)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa);
+
+ if (bfa_ioc_get_type(&fcport->bfa->ioc) != BFA_IOC_TYPE_FC)
+ return BFA_STATUS_BBCR_FC_ONLY;
+
+ if (fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP)
+ return BFA_STATUS_TOPOLOGY_LOOP;
+
+ *bbcr_attr = fcport->bbcr_attr;
+
+ return BFA_STATUS_OK;
+}
+
+void
+bfa_fcport_dportenable(struct bfa_s *bfa)
+{
+ /*
+ * Assume caller check for port is in disable state
+ */
+ bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_DPORTENABLE);
+ bfa_port_set_dportenabled(&bfa->modules.port, BFA_TRUE);
+}
+
+void
+bfa_fcport_dportdisable(struct bfa_s *bfa)
+{
+ /*
+ * Assume caller check for port is in disable state
+ */
+ bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_DPORTDISABLE);
+ bfa_port_set_dportenabled(&bfa->modules.port, BFA_FALSE);
+}
+
+static void
+bfa_fcport_ddportenable(struct bfa_s *bfa)
+{
+ /*
+ * Assume caller check for port is in disable state
+ */
+ bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_DDPORTENABLE);
+}
+
+static void
+bfa_fcport_ddportdisable(struct bfa_s *bfa)
+{
+ /*
+ * Assume caller check for port is in disable state
+ */
+ bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_DDPORTDISABLE);
+}
+
/*
* Rport State machine functions
*/
@@ -4091,6 +4521,10 @@ bfa_rport_sm_offline(struct bfa_rport_s *rp, enum bfa_rport_event event)
bfa_sm_set_state(rp, bfa_rport_sm_iocdisable);
break;
+ case BFA_RPORT_SM_OFFLINE:
+ bfa_rport_offline_cb(rp);
+ break;
+
default:
bfa_stats(rp, sm_off_unexp);
bfa_sm_fault(rp->bfa, event);
@@ -4207,6 +4641,7 @@ bfa_rport_sm_offline_pending(struct bfa_rport_s *rp,
case BFA_RPORT_SM_HWFAIL:
bfa_stats(rp, sm_offp_hwf);
bfa_sm_set_state(rp, bfa_rport_sm_iocdisable);
+ bfa_rport_offline_cb(rp);
break;
default:
@@ -4285,19 +4720,23 @@ bfa_rport_qresume(void *cbarg)
bfa_sm_send_event(rp, BFA_RPORT_SM_QRESUME);
}
-static void
-bfa_rport_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len)
+void
+bfa_rport_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo,
+ struct bfa_s *bfa)
{
+ struct bfa_mem_kva_s *rport_kva = BFA_MEM_RPORT_KVA(bfa);
+
if (cfg->fwcfg.num_rports < BFA_RPORT_MIN)
cfg->fwcfg.num_rports = BFA_RPORT_MIN;
- *km_len += cfg->fwcfg.num_rports * sizeof(struct bfa_rport_s);
+ /* kva memory */
+ bfa_mem_kva_setup(minfo, rport_kva,
+ cfg->fwcfg.num_rports * sizeof(struct bfa_rport_s));
}
-static void
+void
bfa_rport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
+ struct bfa_pcidev_s *pcidev)
{
struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(bfa);
struct bfa_rport_s *rp;
@@ -4305,8 +4744,9 @@ bfa_rport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
INIT_LIST_HEAD(&mod->rp_free_q);
INIT_LIST_HEAD(&mod->rp_active_q);
+ INIT_LIST_HEAD(&mod->rp_unused_q);
- rp = (struct bfa_rport_s *) bfa_meminfo_kva(meminfo);
+ rp = (struct bfa_rport_s *) bfa_mem_kva_curp(mod);
mod->rps_list = rp;
mod->num_rports = cfg->fwcfg.num_rports;
@@ -4331,31 +4771,19 @@ bfa_rport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
/*
* consume memory
*/
- bfa_meminfo_kva(meminfo) = (u8 *) rp;
+ bfa_mem_kva_curp(mod) = (u8 *) rp;
}
-static void
-bfa_rport_detach(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_rport_start(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_rport_stop(struct bfa_s *bfa)
-{
-}
-
-static void
+void
bfa_rport_iocdisable(struct bfa_s *bfa)
{
struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(bfa);
struct bfa_rport_s *rport;
struct list_head *qe, *qen;
+ /* Enqueue unused rport resources to free_q */
+ list_splice_tail_init(&mod->rp_unused_q, &mod->rp_free_q);
+
list_for_each_safe(qe, qen, &mod->rp_active_q) {
rport = (struct bfa_rport_s *) qe;
bfa_sm_send_event(rport, BFA_RPORT_SM_HWFAIL);
@@ -4399,11 +4827,11 @@ bfa_rport_send_fwcreate(struct bfa_rport_s *rp)
}
bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_CREATE_REQ,
- bfa_lpuid(rp->bfa));
+ bfa_fn_lpu(rp->bfa));
m->bfa_handle = rp->rport_tag;
m->max_frmsz = cpu_to_be16(rp->rport_info.max_frmsz);
m->pid = rp->rport_info.pid;
- m->lp_tag = rp->rport_info.lp_tag;
+ m->lp_fwtag = bfa_lps_get_fwtag(rp->bfa, (u8)rp->rport_info.lp_tag);
m->local_pid = rp->rport_info.local_pid;
m->fc_class = rp->rport_info.fc_class;
m->vf_en = rp->rport_info.vf_en;
@@ -4413,7 +4841,7 @@ bfa_rport_send_fwcreate(struct bfa_rport_s *rp)
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT);
+ bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT, m->mh);
return BFA_TRUE;
}
@@ -4432,13 +4860,13 @@ bfa_rport_send_fwdelete(struct bfa_rport_s *rp)
}
bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_DELETE_REQ,
- bfa_lpuid(rp->bfa));
+ bfa_fn_lpu(rp->bfa));
m->fw_handle = rp->fw_handle;
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT);
+ bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT, m->mh);
return BFA_TRUE;
}
@@ -4457,14 +4885,14 @@ bfa_rport_send_fwspeed(struct bfa_rport_s *rp)
}
bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_SET_SPEED_REQ,
- bfa_lpuid(rp->bfa));
+ bfa_fn_lpu(rp->bfa));
m->fw_handle = rp->fw_handle;
m->speed = (u8)rp->rport_info.speed;
/*
* queue I/O message to firmware
*/
- bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT);
+ bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT, m->mh);
return BFA_TRUE;
}
@@ -4492,6 +4920,7 @@ bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
rp = BFA_RPORT_FROM_TAG(bfa, msg.create_rsp->bfa_handle);
rp->fw_handle = msg.create_rsp->fw_handle;
rp->qos_attr = msg.create_rsp->qos_attr;
+ bfa_rport_set_lunmask(bfa, rp);
WARN_ON(msg.create_rsp->status != BFA_STATUS_OK);
bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP);
break;
@@ -4499,6 +4928,7 @@ bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
case BFI_RPORT_I2H_DELETE_RSP:
rp = BFA_RPORT_FROM_TAG(bfa, msg.delete_rsp->bfa_handle);
WARN_ON(msg.delete_rsp->status != BFA_STATUS_OK);
+ bfa_rport_unset_lunmask(bfa, rp);
bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP);
break;
@@ -4508,13 +4938,39 @@ bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
bfa_sm_send_event(rp, BFA_RPORT_SM_QOS_SCN);
break;
+ case BFI_RPORT_I2H_LIP_SCN_ONLINE:
+ bfa_fcport_update_loop_info(BFA_FCPORT_MOD(bfa),
+ &msg.lip_scn->loop_info);
+ bfa_cb_rport_scn_online(bfa);
+ break;
+
+ case BFI_RPORT_I2H_LIP_SCN_OFFLINE:
+ bfa_cb_rport_scn_offline(bfa);
+ break;
+
+ case BFI_RPORT_I2H_NO_DEV:
+ rp = BFA_RPORT_FROM_TAG(bfa, msg.lip_scn->bfa_handle);
+ bfa_cb_rport_scn_no_dev(rp->rport_drv);
+ break;
+
default:
bfa_trc(bfa, m->mhdr.msg_id);
WARN_ON(1);
}
}
+void
+bfa_rport_res_recfg(struct bfa_s *bfa, u16 num_rport_fw)
+{
+ struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(bfa);
+ struct list_head *qe;
+ int i;
+ for (i = 0; i < (mod->num_rports - num_rport_fw); i++) {
+ bfa_q_deq_tail(&mod->rp_free_q, &qe);
+ list_add_tail(qe, &mod->rp_unused_q);
+ }
+}
/*
* bfa_rport_api
@@ -4564,10 +5020,43 @@ bfa_rport_speed(struct bfa_rport_s *rport, enum bfa_port_speed speed)
WARN_ON(speed == 0);
WARN_ON(speed == BFA_PORT_SPEED_AUTO);
- rport->rport_info.speed = speed;
- bfa_sm_send_event(rport, BFA_RPORT_SM_SET_SPEED);
+ if (rport) {
+ rport->rport_info.speed = speed;
+ bfa_sm_send_event(rport, BFA_RPORT_SM_SET_SPEED);
+ }
+}
+
+/* Set Rport LUN Mask */
+void
+bfa_rport_set_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp)
+{
+ struct bfa_lps_mod_s *lps_mod = BFA_LPS_MOD(bfa);
+ wwn_t lp_wwn, rp_wwn;
+ u8 lp_tag = (u8)rp->rport_info.lp_tag;
+
+ rp_wwn = ((struct bfa_fcs_rport_s *)rp->rport_drv)->pwwn;
+ lp_wwn = (BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag))->pwwn;
+
+ BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag)->lun_mask =
+ rp->lun_mask = BFA_TRUE;
+ bfa_fcpim_lunmask_rp_update(bfa, lp_wwn, rp_wwn, rp->rport_tag, lp_tag);
}
+/* Unset Rport LUN mask */
+void
+bfa_rport_unset_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp)
+{
+ struct bfa_lps_mod_s *lps_mod = BFA_LPS_MOD(bfa);
+ wwn_t lp_wwn, rp_wwn;
+
+ rp_wwn = ((struct bfa_fcs_rport_s *)rp->rport_drv)->pwwn;
+ lp_wwn = (BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag))->pwwn;
+
+ BFA_LPS_FROM_TAG(lps_mod, rp->rport_info.lp_tag)->lun_mask =
+ rp->lun_mask = BFA_FALSE;
+ bfa_fcpim_lunmask_rp_update(bfa, lp_wwn, rp_wwn,
+ BFA_RPORT_TAG_INVALID, BFA_LP_TAG_INVALID);
+}
/*
* SGPG related functions
@@ -4576,27 +5065,52 @@ bfa_rport_speed(struct bfa_rport_s *rport, enum bfa_port_speed speed)
/*
* Compute and return memory needed by FCP(im) module.
*/
-static void
-bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
- u32 *dm_len)
+void
+bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo,
+ struct bfa_s *bfa)
{
+ struct bfa_sgpg_mod_s *sgpg_mod = BFA_SGPG_MOD(bfa);
+ struct bfa_mem_kva_s *sgpg_kva = BFA_MEM_SGPG_KVA(bfa);
+ struct bfa_mem_dma_s *seg_ptr;
+ u16 nsegs, idx, per_seg_sgpg, num_sgpg;
+ u32 sgpg_sz = sizeof(struct bfi_sgpg_s);
+
if (cfg->drvcfg.num_sgpgs < BFA_SGPG_MIN)
cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN;
+ else if (cfg->drvcfg.num_sgpgs > BFA_SGPG_MAX)
+ cfg->drvcfg.num_sgpgs = BFA_SGPG_MAX;
- *km_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfa_sgpg_s);
- *dm_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfi_sgpg_s);
-}
+ num_sgpg = cfg->drvcfg.num_sgpgs;
+ nsegs = BFI_MEM_DMA_NSEGS(num_sgpg, sgpg_sz);
+ per_seg_sgpg = BFI_MEM_NREQS_SEG(sgpg_sz);
-static void
+ bfa_mem_dma_seg_iter(sgpg_mod, seg_ptr, nsegs, idx) {
+ if (num_sgpg >= per_seg_sgpg) {
+ num_sgpg -= per_seg_sgpg;
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ per_seg_sgpg * sgpg_sz);
+ } else
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ num_sgpg * sgpg_sz);
+ }
+
+ /* kva memory */
+ bfa_mem_kva_setup(minfo, sgpg_kva,
+ cfg->drvcfg.num_sgpgs * sizeof(struct bfa_sgpg_s));
+}
+
+void
bfa_sgpg_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *minfo, struct bfa_pcidev_s *pcidev)
+ struct bfa_pcidev_s *pcidev)
{
struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa);
- int i;
struct bfa_sgpg_s *hsgpg;
struct bfi_sgpg_s *sgpg;
u64 align_len;
+ struct bfa_mem_dma_s *seg_ptr;
+ u32 sgpg_sz = sizeof(struct bfi_sgpg_s);
+ u16 i, idx, nsegs, per_seg_sgpg, num_sgpg;
union {
u64 pa;
@@ -4608,59 +5122,45 @@ bfa_sgpg_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
bfa_trc(bfa, cfg->drvcfg.num_sgpgs);
- mod->num_sgpgs = cfg->drvcfg.num_sgpgs;
- mod->sgpg_arr_pa = bfa_meminfo_dma_phys(minfo);
- align_len = (BFA_SGPG_ROUNDUP(mod->sgpg_arr_pa) - mod->sgpg_arr_pa);
- mod->sgpg_arr_pa += align_len;
- mod->hsgpg_arr = (struct bfa_sgpg_s *) (bfa_meminfo_kva(minfo) +
- align_len);
- mod->sgpg_arr = (struct bfi_sgpg_s *) (bfa_meminfo_dma_virt(minfo) +
- align_len);
+ mod->free_sgpgs = mod->num_sgpgs = cfg->drvcfg.num_sgpgs;
- hsgpg = mod->hsgpg_arr;
- sgpg = mod->sgpg_arr;
- sgpg_pa.pa = mod->sgpg_arr_pa;
- mod->free_sgpgs = mod->num_sgpgs;
+ num_sgpg = cfg->drvcfg.num_sgpgs;
+ nsegs = BFI_MEM_DMA_NSEGS(num_sgpg, sgpg_sz);
- WARN_ON(sgpg_pa.pa & (sizeof(struct bfi_sgpg_s) - 1));
+ /* dma/kva mem claim */
+ hsgpg = (struct bfa_sgpg_s *) bfa_mem_kva_curp(mod);
- for (i = 0; i < mod->num_sgpgs; i++) {
- memset(hsgpg, 0, sizeof(*hsgpg));
- memset(sgpg, 0, sizeof(*sgpg));
+ bfa_mem_dma_seg_iter(mod, seg_ptr, nsegs, idx) {
- hsgpg->sgpg = sgpg;
- sgpg_pa_tmp.pa = bfa_sgaddr_le(sgpg_pa.pa);
- hsgpg->sgpg_pa = sgpg_pa_tmp.addr;
- list_add_tail(&hsgpg->qe, &mod->sgpg_q);
+ if (!bfa_mem_dma_virt(seg_ptr))
+ break;
- hsgpg++;
- sgpg++;
- sgpg_pa.pa += sizeof(struct bfi_sgpg_s);
- }
+ align_len = BFA_SGPG_ROUNDUP(bfa_mem_dma_phys(seg_ptr)) -
+ bfa_mem_dma_phys(seg_ptr);
- bfa_meminfo_kva(minfo) = (u8 *) hsgpg;
- bfa_meminfo_dma_virt(minfo) = (u8 *) sgpg;
- bfa_meminfo_dma_phys(minfo) = sgpg_pa.pa;
-}
+ sgpg = (struct bfi_sgpg_s *)
+ (((u8 *) bfa_mem_dma_virt(seg_ptr)) + align_len);
+ sgpg_pa.pa = bfa_mem_dma_phys(seg_ptr) + align_len;
+ WARN_ON(sgpg_pa.pa & (sgpg_sz - 1));
-static void
-bfa_sgpg_detach(struct bfa_s *bfa)
-{
-}
+ per_seg_sgpg = (seg_ptr->mem_len - (u32)align_len) / sgpg_sz;
-static void
-bfa_sgpg_start(struct bfa_s *bfa)
-{
-}
+ for (i = 0; num_sgpg > 0 && i < per_seg_sgpg; i++, num_sgpg--) {
+ memset(hsgpg, 0, sizeof(*hsgpg));
+ memset(sgpg, 0, sizeof(*sgpg));
-static void
-bfa_sgpg_stop(struct bfa_s *bfa)
-{
-}
+ hsgpg->sgpg = sgpg;
+ sgpg_pa_tmp.pa = bfa_sgaddr_le(sgpg_pa.pa);
+ hsgpg->sgpg_pa = sgpg_pa_tmp.addr;
+ list_add_tail(&hsgpg->qe, &mod->sgpg_q);
-static void
-bfa_sgpg_iocdisable(struct bfa_s *bfa)
-{
+ sgpg++;
+ hsgpg++;
+ sgpg_pa.pa += sgpg_sz;
+ }
+ }
+
+ bfa_mem_kva_curp(mod) = (u8 *) hsgpg;
}
bfa_status_t
@@ -4782,31 +5282,13 @@ __bfa_cb_uf_recv(void *cbarg, bfa_boolean_t complete)
}
static void
-claim_uf_pbs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
-{
- u32 uf_pb_tot_sz;
-
- ufm->uf_pbs_kva = (struct bfa_uf_buf_s *) bfa_meminfo_dma_virt(mi);
- ufm->uf_pbs_pa = bfa_meminfo_dma_phys(mi);
- uf_pb_tot_sz = BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * ufm->num_ufs),
- BFA_DMA_ALIGN_SZ);
-
- bfa_meminfo_dma_virt(mi) += uf_pb_tot_sz;
- bfa_meminfo_dma_phys(mi) += uf_pb_tot_sz;
-
- memset((void *)ufm->uf_pbs_kva, 0, uf_pb_tot_sz);
-}
-
-static void
-claim_uf_post_msgs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
+claim_uf_post_msgs(struct bfa_uf_mod_s *ufm)
{
struct bfi_uf_buf_post_s *uf_bp_msg;
- struct bfi_sge_s *sge;
- union bfi_addr_u sga_zero = { {0} };
u16 i;
u16 buf_len;
- ufm->uf_buf_posts = (struct bfi_uf_buf_post_s *) bfa_meminfo_kva(mi);
+ ufm->uf_buf_posts = (struct bfi_uf_buf_post_s *) bfa_mem_kva_curp(ufm);
uf_bp_msg = ufm->uf_buf_posts;
for (i = 0, uf_bp_msg = ufm->uf_buf_posts; i < ufm->num_ufs;
@@ -4817,28 +5299,18 @@ claim_uf_post_msgs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
buf_len = sizeof(struct bfa_uf_buf_s);
uf_bp_msg->buf_len = cpu_to_be16(buf_len);
bfi_h2i_set(uf_bp_msg->mh, BFI_MC_UF, BFI_UF_H2I_BUF_POST,
- bfa_lpuid(ufm->bfa));
-
- sge = uf_bp_msg->sge;
- sge[0].sg_len = buf_len;
- sge[0].flags = BFI_SGE_DATA_LAST;
- bfa_dma_addr_set(sge[0].sga, ufm_pbs_pa(ufm, i));
- bfa_sge_to_be(sge);
-
- sge[1].sg_len = buf_len;
- sge[1].flags = BFI_SGE_PGDLEN;
- sge[1].sga = sga_zero;
- bfa_sge_to_be(&sge[1]);
+ bfa_fn_lpu(ufm->bfa));
+ bfa_alen_set(&uf_bp_msg->alen, buf_len, ufm_pbs_pa(ufm, i));
}
/*
* advance pointer beyond consumed memory
*/
- bfa_meminfo_kva(mi) = (u8 *) uf_bp_msg;
+ bfa_mem_kva_curp(ufm) = (u8 *) uf_bp_msg;
}
static void
-claim_ufs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
+claim_ufs(struct bfa_uf_mod_s *ufm)
{
u16 i;
struct bfa_uf_s *uf;
@@ -4846,7 +5318,7 @@ claim_ufs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
/*
* Claim block of memory for UF list
*/
- ufm->uf_list = (struct bfa_uf_s *) bfa_meminfo_kva(mi);
+ ufm->uf_list = (struct bfa_uf_s *) bfa_mem_kva_curp(ufm);
/*
* Initialize UFs and queue it in UF free queue
@@ -4855,8 +5327,8 @@ claim_ufs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
memset(uf, 0, sizeof(struct bfa_uf_s));
uf->bfa = ufm->bfa;
uf->uf_tag = i;
- uf->pb_len = sizeof(struct bfa_uf_buf_s);
- uf->buf_kva = (void *)&ufm->uf_pbs_kva[i];
+ uf->pb_len = BFA_PER_UF_DMA_SZ;
+ uf->buf_kva = bfa_mem_get_dmabuf_kva(ufm, i, BFA_PER_UF_DMA_SZ);
uf->buf_pa = ufm_pbs_pa(ufm, i);
list_add_tail(&uf->qe, &ufm->uf_free_q);
}
@@ -4864,53 +5336,57 @@ claim_ufs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
/*
* advance memory pointer
*/
- bfa_meminfo_kva(mi) = (u8 *) uf;
+ bfa_mem_kva_curp(ufm) = (u8 *) uf;
}
static void
-uf_mem_claim(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi)
+uf_mem_claim(struct bfa_uf_mod_s *ufm)
{
- claim_uf_pbs(ufm, mi);
- claim_ufs(ufm, mi);
- claim_uf_post_msgs(ufm, mi);
+ claim_ufs(ufm);
+ claim_uf_post_msgs(ufm);
}
-static void
-bfa_uf_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, u32 *dm_len)
+void
+bfa_uf_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo,
+ struct bfa_s *bfa)
{
- u32 num_ufs = cfg->fwcfg.num_uf_bufs;
-
- /*
- * dma-able memory for UF posted bufs
- */
- *dm_len += BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * num_ufs),
- BFA_DMA_ALIGN_SZ);
+ struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa);
+ struct bfa_mem_kva_s *uf_kva = BFA_MEM_UF_KVA(bfa);
+ u32 num_ufs = cfg->fwcfg.num_uf_bufs;
+ struct bfa_mem_dma_s *seg_ptr;
+ u16 nsegs, idx, per_seg_uf = 0;
+
+ nsegs = BFI_MEM_DMA_NSEGS(num_ufs, BFA_PER_UF_DMA_SZ);
+ per_seg_uf = BFI_MEM_NREQS_SEG(BFA_PER_UF_DMA_SZ);
+
+ bfa_mem_dma_seg_iter(ufm, seg_ptr, nsegs, idx) {
+ if (num_ufs >= per_seg_uf) {
+ num_ufs -= per_seg_uf;
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ per_seg_uf * BFA_PER_UF_DMA_SZ);
+ } else
+ bfa_mem_dma_setup(minfo, seg_ptr,
+ num_ufs * BFA_PER_UF_DMA_SZ);
+ }
- /*
- * kernel Virtual memory for UFs and UF buf post msg copies
- */
- *ndm_len += sizeof(struct bfa_uf_s) * num_ufs;
- *ndm_len += sizeof(struct bfi_uf_buf_post_s) * num_ufs;
+ /* kva memory */
+ bfa_mem_kva_setup(minfo, uf_kva, cfg->fwcfg.num_uf_bufs *
+ (sizeof(struct bfa_uf_s) + sizeof(struct bfi_uf_buf_post_s)));
}
-static void
+void
bfa_uf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
- struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev)
+ struct bfa_pcidev_s *pcidev)
{
struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa);
- memset(ufm, 0, sizeof(struct bfa_uf_mod_s));
ufm->bfa = bfa;
ufm->num_ufs = cfg->fwcfg.num_uf_bufs;
INIT_LIST_HEAD(&ufm->uf_free_q);
INIT_LIST_HEAD(&ufm->uf_posted_q);
+ INIT_LIST_HEAD(&ufm->uf_unused_q);
- uf_mem_claim(ufm, meminfo);
-}
-
-static void
-bfa_uf_detach(struct bfa_s *bfa)
-{
+ uf_mem_claim(ufm);
}
static struct bfa_uf_s *
@@ -4939,7 +5415,7 @@ bfa_uf_post(struct bfa_uf_mod_s *ufm, struct bfa_uf_s *uf)
memcpy(uf_post_msg, &ufm->uf_buf_posts[uf->uf_tag],
sizeof(struct bfi_uf_buf_post_s));
- bfa_reqq_produce(ufm->bfa, BFA_REQQ_FCXP);
+ bfa_reqq_produce(ufm->bfa, BFA_REQQ_FCXP, uf_post_msg->mh);
bfa_trc(ufm->bfa, uf->uf_tag);
@@ -4963,16 +5439,17 @@ uf_recv(struct bfa_s *bfa, struct bfi_uf_frm_rcvd_s *m)
{
struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa);
u16 uf_tag = m->buf_tag;
- struct bfa_uf_buf_s *uf_buf = &ufm->uf_pbs_kva[uf_tag];
struct bfa_uf_s *uf = &ufm->uf_list[uf_tag];
- u8 *buf = &uf_buf->d[0];
- struct fchs_s *fchs;
+ struct bfa_uf_buf_s *uf_buf;
+ uint8_t *buf;
+
+ uf_buf = (struct bfa_uf_buf_s *)
+ bfa_mem_get_dmabuf_kva(ufm, uf_tag, uf->pb_len);
+ buf = &uf_buf->d[0];
m->frm_len = be16_to_cpu(m->frm_len);
m->xfr_len = be16_to_cpu(m->xfr_len);
- fchs = (struct fchs_s *)uf_buf;
-
list_del(&uf->qe); /* dequeue from posted queue */
uf->data_ptr = buf;
@@ -4996,26 +5473,7 @@ uf_recv(struct bfa_s *bfa, struct bfi_uf_frm_rcvd_s *m)
bfa_cb_queue(bfa, &uf->hcb_qe, __bfa_cb_uf_recv, uf);
}
-static void
-bfa_uf_stop(struct bfa_s *bfa)
-{
-}
-
-static void
-bfa_uf_iocdisable(struct bfa_s *bfa)
-{
- struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa);
- struct bfa_uf_s *uf;
- struct list_head *qe, *qen;
-
- list_for_each_safe(qe, qen, &ufm->uf_posted_q) {
- uf = (struct bfa_uf_s *) qe;
- list_del(&uf->qe);
- bfa_uf_put(ufm, uf);
- }
-}
-
-static void
+void
bfa_uf_start(struct bfa_s *bfa)
{
bfa_uf_post_all(BFA_UF_MOD(bfa));
@@ -5072,4 +5530,1268 @@ bfa_uf_isr(struct bfa_s *bfa, struct bfi_msg_s *msg)
}
}
+void
+bfa_uf_res_recfg(struct bfa_s *bfa, u16 num_uf_fw)
+{
+ struct bfa_uf_mod_s *mod = BFA_UF_MOD(bfa);
+ struct list_head *qe;
+ int i;
+
+ for (i = 0; i < (mod->num_ufs - num_uf_fw); i++) {
+ bfa_q_deq_tail(&mod->uf_free_q, &qe);
+ list_add_tail(qe, &mod->uf_unused_q);
+ }
+}
+
+/*
+ * Dport forward declaration
+ */
+
+enum bfa_dport_test_state_e {
+ BFA_DPORT_ST_DISABLED = 0, /*!< dport is disabled */
+ BFA_DPORT_ST_INP = 1, /*!< test in progress */
+ BFA_DPORT_ST_COMP = 2, /*!< test complete successfully */
+ BFA_DPORT_ST_NO_SFP = 3, /*!< sfp is not present */
+ BFA_DPORT_ST_NOTSTART = 4, /*!< test not start dport is enabled */
+};
+
+static void bfa_dport_sm_disabled(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_enabling_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_enabling(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_enabled(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_disabling_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_disabling(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_starting_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_starting(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_dynamic_disabling(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_sm_dynamic_disabling_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event);
+static void bfa_dport_qresume(void *cbarg);
+static void bfa_dport_req_comp(struct bfa_dport_s *dport,
+ struct bfi_diag_dport_rsp_s *msg);
+static void bfa_dport_scn(struct bfa_dport_s *dport,
+ struct bfi_diag_dport_scn_s *msg);
+
+/*
+ * BFA fcdiag module
+ */
+#define BFA_DIAG_QTEST_TOV 1000 /* msec */
+
+/*
+ * Set port status to busy
+ */
+static void
+bfa_fcdiag_set_busy_status(struct bfa_fcdiag_s *fcdiag)
+{
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(fcdiag->bfa);
+
+ if (fcdiag->lb.lock)
+ fcport->diag_busy = BFA_TRUE;
+ else
+ fcport->diag_busy = BFA_FALSE;
+}
+
+void
+bfa_fcdiag_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
+ struct bfa_pcidev_s *pcidev)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ struct bfa_dport_s *dport = &fcdiag->dport;
+
+ fcdiag->bfa = bfa;
+ fcdiag->trcmod = bfa->trcmod;
+ /* The common DIAG attach bfa_diag_attach() will do all memory claim */
+ dport->bfa = bfa;
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_reqq_winit(&dport->reqq_wait, bfa_dport_qresume, dport);
+ dport->cbfn = NULL;
+ dport->cbarg = NULL;
+ dport->test_state = BFA_DPORT_ST_DISABLED;
+ memset(&dport->result, 0, sizeof(struct bfa_diag_dport_result_s));
+}
+
+void
+bfa_fcdiag_iocdisable(struct bfa_s *bfa)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ struct bfa_dport_s *dport = &fcdiag->dport;
+
+ bfa_trc(fcdiag, fcdiag->lb.lock);
+ if (fcdiag->lb.lock) {
+ fcdiag->lb.status = BFA_STATUS_IOC_FAILURE;
+ fcdiag->lb.cbfn(fcdiag->lb.cbarg, fcdiag->lb.status);
+ fcdiag->lb.lock = 0;
+ bfa_fcdiag_set_busy_status(fcdiag);
+ }
+
+ bfa_sm_send_event(dport, BFA_DPORT_SM_HWFAIL);
+}
+
+static void
+bfa_fcdiag_queuetest_timeout(void *cbarg)
+{
+ struct bfa_fcdiag_s *fcdiag = cbarg;
+ struct bfa_diag_qtest_result_s *res = fcdiag->qtest.result;
+
+ bfa_trc(fcdiag, fcdiag->qtest.all);
+ bfa_trc(fcdiag, fcdiag->qtest.count);
+
+ fcdiag->qtest.timer_active = 0;
+
+ res->status = BFA_STATUS_ETIMER;
+ res->count = QTEST_CNT_DEFAULT - fcdiag->qtest.count;
+ if (fcdiag->qtest.all)
+ res->queue = fcdiag->qtest.all;
+
+ bfa_trc(fcdiag, BFA_STATUS_ETIMER);
+ fcdiag->qtest.status = BFA_STATUS_ETIMER;
+ fcdiag->qtest.cbfn(fcdiag->qtest.cbarg, fcdiag->qtest.status);
+ fcdiag->qtest.lock = 0;
+}
+
+static bfa_status_t
+bfa_fcdiag_queuetest_send(struct bfa_fcdiag_s *fcdiag)
+{
+ u32 i;
+ struct bfi_diag_qtest_req_s *req;
+
+ req = bfa_reqq_next(fcdiag->bfa, fcdiag->qtest.queue);
+ if (!req)
+ return BFA_STATUS_DEVBUSY;
+
+ /* build host command */
+ bfi_h2i_set(req->mh, BFI_MC_DIAG, BFI_DIAG_H2I_QTEST,
+ bfa_fn_lpu(fcdiag->bfa));
+
+ for (i = 0; i < BFI_LMSG_PL_WSZ; i++)
+ req->data[i] = QTEST_PAT_DEFAULT;
+
+ bfa_trc(fcdiag, fcdiag->qtest.queue);
+ /* ring door bell */
+ bfa_reqq_produce(fcdiag->bfa, fcdiag->qtest.queue, req->mh);
+ return BFA_STATUS_OK;
+}
+
+static void
+bfa_fcdiag_queuetest_comp(struct bfa_fcdiag_s *fcdiag,
+ bfi_diag_qtest_rsp_t *rsp)
+{
+ struct bfa_diag_qtest_result_s *res = fcdiag->qtest.result;
+ bfa_status_t status = BFA_STATUS_OK;
+ int i;
+
+ /* Check timer, should still be active */
+ if (!fcdiag->qtest.timer_active) {
+ bfa_trc(fcdiag, fcdiag->qtest.timer_active);
+ return;
+ }
+
+ /* update count */
+ fcdiag->qtest.count--;
+
+ /* Check result */
+ for (i = 0; i < BFI_LMSG_PL_WSZ; i++) {
+ if (rsp->data[i] != ~(QTEST_PAT_DEFAULT)) {
+ res->status = BFA_STATUS_DATACORRUPTED;
+ break;
+ }
+ }
+
+ if (res->status == BFA_STATUS_OK) {
+ if (fcdiag->qtest.count > 0) {
+ status = bfa_fcdiag_queuetest_send(fcdiag);
+ if (status == BFA_STATUS_OK)
+ return;
+ else
+ res->status = status;
+ } else if (fcdiag->qtest.all > 0 &&
+ fcdiag->qtest.queue < (BFI_IOC_MAX_CQS - 1)) {
+ fcdiag->qtest.count = QTEST_CNT_DEFAULT;
+ fcdiag->qtest.queue++;
+ status = bfa_fcdiag_queuetest_send(fcdiag);
+ if (status == BFA_STATUS_OK)
+ return;
+ else
+ res->status = status;
+ }
+ }
+
+ /* Stop timer when we comp all queue */
+ if (fcdiag->qtest.timer_active) {
+ bfa_timer_stop(&fcdiag->qtest.timer);
+ fcdiag->qtest.timer_active = 0;
+ }
+ res->queue = fcdiag->qtest.queue;
+ res->count = QTEST_CNT_DEFAULT - fcdiag->qtest.count;
+ bfa_trc(fcdiag, res->count);
+ bfa_trc(fcdiag, res->status);
+ fcdiag->qtest.status = res->status;
+ fcdiag->qtest.cbfn(fcdiag->qtest.cbarg, fcdiag->qtest.status);
+ fcdiag->qtest.lock = 0;
+}
+
+static void
+bfa_fcdiag_loopback_comp(struct bfa_fcdiag_s *fcdiag,
+ struct bfi_diag_lb_rsp_s *rsp)
+{
+ struct bfa_diag_loopback_result_s *res = fcdiag->lb.result;
+
+ res->numtxmfrm = be32_to_cpu(rsp->res.numtxmfrm);
+ res->numosffrm = be32_to_cpu(rsp->res.numosffrm);
+ res->numrcvfrm = be32_to_cpu(rsp->res.numrcvfrm);
+ res->badfrminf = be32_to_cpu(rsp->res.badfrminf);
+ res->badfrmnum = be32_to_cpu(rsp->res.badfrmnum);
+ res->status = rsp->res.status;
+ fcdiag->lb.status = rsp->res.status;
+ bfa_trc(fcdiag, fcdiag->lb.status);
+ fcdiag->lb.cbfn(fcdiag->lb.cbarg, fcdiag->lb.status);
+ fcdiag->lb.lock = 0;
+ bfa_fcdiag_set_busy_status(fcdiag);
+}
+
+static bfa_status_t
+bfa_fcdiag_loopback_send(struct bfa_fcdiag_s *fcdiag,
+ struct bfa_diag_loopback_s *loopback)
+{
+ struct bfi_diag_lb_req_s *lb_req;
+
+ lb_req = bfa_reqq_next(fcdiag->bfa, BFA_REQQ_DIAG);
+ if (!lb_req)
+ return BFA_STATUS_DEVBUSY;
+
+ /* build host command */
+ bfi_h2i_set(lb_req->mh, BFI_MC_DIAG, BFI_DIAG_H2I_LOOPBACK,
+ bfa_fn_lpu(fcdiag->bfa));
+
+ lb_req->lb_mode = loopback->lb_mode;
+ lb_req->speed = loopback->speed;
+ lb_req->loopcnt = loopback->loopcnt;
+ lb_req->pattern = loopback->pattern;
+
+ /* ring door bell */
+ bfa_reqq_produce(fcdiag->bfa, BFA_REQQ_DIAG, lb_req->mh);
+
+ bfa_trc(fcdiag, loopback->lb_mode);
+ bfa_trc(fcdiag, loopback->speed);
+ bfa_trc(fcdiag, loopback->loopcnt);
+ bfa_trc(fcdiag, loopback->pattern);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * cpe/rme intr handler
+ */
+void
+bfa_fcdiag_intr(struct bfa_s *bfa, struct bfi_msg_s *msg)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+
+ switch (msg->mhdr.msg_id) {
+ case BFI_DIAG_I2H_LOOPBACK:
+ bfa_fcdiag_loopback_comp(fcdiag,
+ (struct bfi_diag_lb_rsp_s *) msg);
+ break;
+ case BFI_DIAG_I2H_QTEST:
+ bfa_fcdiag_queuetest_comp(fcdiag, (bfi_diag_qtest_rsp_t *)msg);
+ break;
+ case BFI_DIAG_I2H_DPORT:
+ bfa_dport_req_comp(&fcdiag->dport,
+ (struct bfi_diag_dport_rsp_s *)msg);
+ break;
+ case BFI_DIAG_I2H_DPORT_SCN:
+ bfa_dport_scn(&fcdiag->dport,
+ (struct bfi_diag_dport_scn_s *)msg);
+ break;
+ default:
+ bfa_trc(fcdiag, msg->mhdr.msg_id);
+ WARN_ON(1);
+ }
+}
+
+/*
+ * Loopback test
+ *
+ * @param[in] *bfa - bfa data struct
+ * @param[in] opmode - port operation mode
+ * @param[in] speed - port speed
+ * @param[in] lpcnt - loop count
+ * @param[in] pat - pattern to build packet
+ * @param[in] *result - pt to bfa_diag_loopback_result_t data struct
+ * @param[in] cbfn - callback function
+ * @param[in] cbarg - callback functioin arg
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_fcdiag_loopback(struct bfa_s *bfa, enum bfa_port_opmode opmode,
+ enum bfa_port_speed speed, u32 lpcnt, u32 pat,
+ struct bfa_diag_loopback_result_s *result, bfa_cb_diag_t cbfn,
+ void *cbarg)
+{
+ struct bfa_diag_loopback_s loopback;
+ struct bfa_port_attr_s attr;
+ bfa_status_t status;
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+
+ if (!bfa_iocfc_is_operational(bfa))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* if port is PBC disabled, return error */
+ if (bfa_fcport_is_pbcdisabled(bfa)) {
+ bfa_trc(fcdiag, BFA_STATUS_PBC);
+ return BFA_STATUS_PBC;
+ }
+
+ if (bfa_fcport_is_disabled(bfa) == BFA_FALSE) {
+ bfa_trc(fcdiag, opmode);
+ return BFA_STATUS_PORT_NOT_DISABLED;
+ }
+
+ /*
+ * Check if input speed is supported by the port mode
+ */
+ if (bfa_ioc_get_type(&bfa->ioc) == BFA_IOC_TYPE_FC) {
+ if (!(speed == BFA_PORT_SPEED_1GBPS ||
+ speed == BFA_PORT_SPEED_2GBPS ||
+ speed == BFA_PORT_SPEED_4GBPS ||
+ speed == BFA_PORT_SPEED_8GBPS ||
+ speed == BFA_PORT_SPEED_16GBPS ||
+ speed == BFA_PORT_SPEED_AUTO)) {
+ bfa_trc(fcdiag, speed);
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+ bfa_fcport_get_attr(bfa, &attr);
+ bfa_trc(fcdiag, attr.speed_supported);
+ if (speed > attr.speed_supported)
+ return BFA_STATUS_UNSUPP_SPEED;
+ } else {
+ if (speed != BFA_PORT_SPEED_10GBPS) {
+ bfa_trc(fcdiag, speed);
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+ }
+
+ /*
+ * For CT2, 1G is not supported
+ */
+ if ((speed == BFA_PORT_SPEED_1GBPS) &&
+ (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id))) {
+ bfa_trc(fcdiag, speed);
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+
+ /* For Mezz card, port speed entered needs to be checked */
+ if (bfa_mfg_is_mezz(bfa->ioc.attr->card_type)) {
+ if (bfa_ioc_get_type(&bfa->ioc) == BFA_IOC_TYPE_FC) {
+ if (!(speed == BFA_PORT_SPEED_1GBPS ||
+ speed == BFA_PORT_SPEED_2GBPS ||
+ speed == BFA_PORT_SPEED_4GBPS ||
+ speed == BFA_PORT_SPEED_8GBPS ||
+ speed == BFA_PORT_SPEED_16GBPS ||
+ speed == BFA_PORT_SPEED_AUTO))
+ return BFA_STATUS_UNSUPP_SPEED;
+ } else {
+ if (speed != BFA_PORT_SPEED_10GBPS)
+ return BFA_STATUS_UNSUPP_SPEED;
+ }
+ }
+ /* check to see if fcport is dport */
+ if (bfa_fcport_is_dport(bfa)) {
+ bfa_trc(fcdiag, fcdiag->lb.lock);
+ return BFA_STATUS_DPORT_ENABLED;
+ }
+ /* check to see if there is another destructive diag cmd running */
+ if (fcdiag->lb.lock) {
+ bfa_trc(fcdiag, fcdiag->lb.lock);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ fcdiag->lb.lock = 1;
+ loopback.lb_mode = opmode;
+ loopback.speed = speed;
+ loopback.loopcnt = lpcnt;
+ loopback.pattern = pat;
+ fcdiag->lb.result = result;
+ fcdiag->lb.cbfn = cbfn;
+ fcdiag->lb.cbarg = cbarg;
+ memset(result, 0, sizeof(struct bfa_diag_loopback_result_s));
+ bfa_fcdiag_set_busy_status(fcdiag);
+
+ /* Send msg to fw */
+ status = bfa_fcdiag_loopback_send(fcdiag, &loopback);
+ return status;
+}
+
+/*
+ * DIAG queue test command
+ *
+ * @param[in] *bfa - bfa data struct
+ * @param[in] force - 1: don't do ioc op checking
+ * @param[in] queue - queue no. to test
+ * @param[in] *result - pt to bfa_diag_qtest_result_t data struct
+ * @param[in] cbfn - callback function
+ * @param[in] *cbarg - callback functioin arg
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_fcdiag_queuetest(struct bfa_s *bfa, u32 force, u32 queue,
+ struct bfa_diag_qtest_result_s *result, bfa_cb_diag_t cbfn,
+ void *cbarg)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ bfa_status_t status;
+ bfa_trc(fcdiag, force);
+ bfa_trc(fcdiag, queue);
+
+ if (!force && !bfa_iocfc_is_operational(bfa))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* check to see if there is another destructive diag cmd running */
+ if (fcdiag->qtest.lock) {
+ bfa_trc(fcdiag, fcdiag->qtest.lock);
+ return BFA_STATUS_DEVBUSY;
+ }
+
+ /* Initialization */
+ fcdiag->qtest.lock = 1;
+ fcdiag->qtest.cbfn = cbfn;
+ fcdiag->qtest.cbarg = cbarg;
+ fcdiag->qtest.result = result;
+ fcdiag->qtest.count = QTEST_CNT_DEFAULT;
+
+ /* Init test results */
+ fcdiag->qtest.result->status = BFA_STATUS_OK;
+ fcdiag->qtest.result->count = 0;
+
+ /* send */
+ if (queue < BFI_IOC_MAX_CQS) {
+ fcdiag->qtest.result->queue = (u8)queue;
+ fcdiag->qtest.queue = (u8)queue;
+ fcdiag->qtest.all = 0;
+ } else {
+ fcdiag->qtest.result->queue = 0;
+ fcdiag->qtest.queue = 0;
+ fcdiag->qtest.all = 1;
+ }
+ status = bfa_fcdiag_queuetest_send(fcdiag);
+
+ /* Start a timer */
+ if (status == BFA_STATUS_OK) {
+ bfa_timer_start(bfa, &fcdiag->qtest.timer,
+ bfa_fcdiag_queuetest_timeout, fcdiag,
+ BFA_DIAG_QTEST_TOV);
+ fcdiag->qtest.timer_active = 1;
+ }
+ return status;
+}
+
+/*
+ * DIAG PLB is running
+ *
+ * @param[in] *bfa - bfa data struct
+ *
+ * @param[out]
+ */
+bfa_status_t
+bfa_fcdiag_lb_is_running(struct bfa_s *bfa)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ return fcdiag->lb.lock ? BFA_STATUS_DIAG_BUSY : BFA_STATUS_OK;
+}
+
+/*
+ * D-port
+ */
+#define bfa_dport_result_start(__dport, __mode) do { \
+ (__dport)->result.start_time = ktime_get_real_seconds(); \
+ (__dport)->result.status = DPORT_TEST_ST_INPRG; \
+ (__dport)->result.mode = (__mode); \
+ (__dport)->result.rp_pwwn = (__dport)->rp_pwwn; \
+ (__dport)->result.rp_nwwn = (__dport)->rp_nwwn; \
+ (__dport)->result.lpcnt = (__dport)->lpcnt; \
+} while (0)
+
+static bfa_boolean_t bfa_dport_send_req(struct bfa_dport_s *dport,
+ enum bfi_dport_req req);
+static void
+bfa_cb_fcdiag_dport(struct bfa_dport_s *dport, bfa_status_t bfa_status)
+{
+ if (dport->cbfn != NULL) {
+ dport->cbfn(dport->cbarg, bfa_status);
+ dport->cbfn = NULL;
+ dport->cbarg = NULL;
+ }
+}
+
+static void
+bfa_dport_sm_disabled(struct bfa_dport_s *dport, enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_ENABLE:
+ bfa_fcport_dportenable(dport->bfa);
+ if (bfa_dport_send_req(dport, BFI_DPORT_ENABLE))
+ bfa_sm_set_state(dport, bfa_dport_sm_enabling);
+ else
+ bfa_sm_set_state(dport, bfa_dport_sm_enabling_qwait);
+ break;
+
+ case BFA_DPORT_SM_DISABLE:
+ /* Already disabled */
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ /* ignore */
+ break;
+
+ case BFA_DPORT_SM_SCN:
+ if (dport->i2hmsg.scn.state == BFI_DPORT_SCN_DDPORT_ENABLE) {
+ bfa_fcport_ddportenable(dport->bfa);
+ dport->dynamic = BFA_TRUE;
+ dport->test_state = BFA_DPORT_ST_NOTSTART;
+ bfa_sm_set_state(dport, bfa_dport_sm_enabled);
+ } else {
+ bfa_trc(dport->bfa, dport->i2hmsg.scn.state);
+ WARN_ON(1);
+ }
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_enabling_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_QRESUME:
+ bfa_sm_set_state(dport, bfa_dport_sm_enabling);
+ bfa_dport_send_req(dport, BFI_DPORT_ENABLE);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_reqq_wcancel(&dport->reqq_wait);
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_FAILED);
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_enabling(struct bfa_dport_s *dport, enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+ switch (event) {
+ case BFA_DPORT_SM_FWRSP:
+ memset(&dport->result, 0,
+ sizeof(struct bfa_diag_dport_result_s));
+ if (dport->i2hmsg.rsp.status == BFA_STATUS_DPORT_INV_SFP) {
+ dport->test_state = BFA_DPORT_ST_NO_SFP;
+ } else {
+ dport->test_state = BFA_DPORT_ST_INP;
+ bfa_dport_result_start(dport, BFA_DPORT_OPMODE_AUTO);
+ }
+ bfa_sm_set_state(dport, bfa_dport_sm_enabled);
+ break;
+
+ case BFA_DPORT_SM_REQFAIL:
+ dport->test_state = BFA_DPORT_ST_DISABLED;
+ bfa_fcport_dportdisable(dport->bfa);
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_FAILED);
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_enabled(struct bfa_dport_s *dport, enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_START:
+ if (bfa_dport_send_req(dport, BFI_DPORT_START))
+ bfa_sm_set_state(dport, bfa_dport_sm_starting);
+ else
+ bfa_sm_set_state(dport, bfa_dport_sm_starting_qwait);
+ break;
+
+ case BFA_DPORT_SM_DISABLE:
+ bfa_fcport_dportdisable(dport->bfa);
+ if (bfa_dport_send_req(dport, BFI_DPORT_DISABLE))
+ bfa_sm_set_state(dport, bfa_dport_sm_disabling);
+ else
+ bfa_sm_set_state(dport, bfa_dport_sm_disabling_qwait);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ break;
+
+ case BFA_DPORT_SM_SCN:
+ switch (dport->i2hmsg.scn.state) {
+ case BFI_DPORT_SCN_TESTCOMP:
+ dport->test_state = BFA_DPORT_ST_COMP;
+ break;
+
+ case BFI_DPORT_SCN_TESTSTART:
+ dport->test_state = BFA_DPORT_ST_INP;
+ break;
+
+ case BFI_DPORT_SCN_TESTSKIP:
+ case BFI_DPORT_SCN_SUBTESTSTART:
+ /* no state change */
+ break;
+
+ case BFI_DPORT_SCN_SFP_REMOVED:
+ dport->test_state = BFA_DPORT_ST_NO_SFP;
+ break;
+
+ case BFI_DPORT_SCN_DDPORT_DISABLE:
+ bfa_fcport_ddportdisable(dport->bfa);
+
+ if (bfa_dport_send_req(dport, BFI_DPORT_DYN_DISABLE))
+ bfa_sm_set_state(dport,
+ bfa_dport_sm_dynamic_disabling);
+ else
+ bfa_sm_set_state(dport,
+ bfa_dport_sm_dynamic_disabling_qwait);
+ break;
+
+ case BFI_DPORT_SCN_FCPORT_DISABLE:
+ bfa_fcport_ddportdisable(dport->bfa);
+
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ dport->dynamic = BFA_FALSE;
+ break;
+
+ default:
+ bfa_trc(dport->bfa, dport->i2hmsg.scn.state);
+ bfa_sm_fault(dport->bfa, event);
+ }
+ break;
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_disabling_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_QRESUME:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabling);
+ bfa_dport_send_req(dport, BFI_DPORT_DISABLE);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_reqq_wcancel(&dport->reqq_wait);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_OK);
+ break;
+
+ case BFA_DPORT_SM_SCN:
+ /* ignore */
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_disabling(struct bfa_dport_s *dport, enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_FWRSP:
+ dport->test_state = BFA_DPORT_ST_DISABLED;
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_OK);
+ break;
+
+ case BFA_DPORT_SM_SCN:
+ /* no state change */
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_starting_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_QRESUME:
+ bfa_sm_set_state(dport, bfa_dport_sm_starting);
+ bfa_dport_send_req(dport, BFI_DPORT_START);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_reqq_wcancel(&dport->reqq_wait);
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_FAILED);
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_starting(struct bfa_dport_s *dport, enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_FWRSP:
+ memset(&dport->result, 0,
+ sizeof(struct bfa_diag_dport_result_s));
+ if (dport->i2hmsg.rsp.status == BFA_STATUS_DPORT_INV_SFP) {
+ dport->test_state = BFA_DPORT_ST_NO_SFP;
+ } else {
+ dport->test_state = BFA_DPORT_ST_INP;
+ bfa_dport_result_start(dport, BFA_DPORT_OPMODE_MANU);
+ }
+ fallthrough;
+
+ case BFA_DPORT_SM_REQFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_enabled);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_FAILED);
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_dynamic_disabling(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_SCN:
+ switch (dport->i2hmsg.scn.state) {
+ case BFI_DPORT_SCN_DDPORT_DISABLED:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ dport->dynamic = BFA_FALSE;
+ bfa_fcport_enable(dport->bfa);
+ break;
+
+ default:
+ bfa_trc(dport->bfa, dport->i2hmsg.scn.state);
+ bfa_sm_fault(dport->bfa, event);
+
+ }
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_OK);
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static void
+bfa_dport_sm_dynamic_disabling_qwait(struct bfa_dport_s *dport,
+ enum bfa_dport_sm_event event)
+{
+ bfa_trc(dport->bfa, event);
+
+ switch (event) {
+ case BFA_DPORT_SM_QRESUME:
+ bfa_sm_set_state(dport, bfa_dport_sm_dynamic_disabling);
+ bfa_dport_send_req(dport, BFI_DPORT_DYN_DISABLE);
+ break;
+
+ case BFA_DPORT_SM_HWFAIL:
+ bfa_sm_set_state(dport, bfa_dport_sm_disabled);
+ bfa_reqq_wcancel(&dport->reqq_wait);
+ bfa_cb_fcdiag_dport(dport, BFA_STATUS_OK);
+ break;
+
+ case BFA_DPORT_SM_SCN:
+ /* ignore */
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, event);
+ }
+}
+
+static bfa_boolean_t
+bfa_dport_send_req(struct bfa_dport_s *dport, enum bfi_dport_req req)
+{
+ struct bfi_diag_dport_req_s *m;
+
+ /*
+ * check for room in queue to send request now
+ */
+ m = bfa_reqq_next(dport->bfa, BFA_REQQ_DIAG);
+ if (!m) {
+ bfa_reqq_wait(dport->bfa, BFA_REQQ_PORT, &dport->reqq_wait);
+ return BFA_FALSE;
+ }
+
+ bfi_h2i_set(m->mh, BFI_MC_DIAG, BFI_DIAG_H2I_DPORT,
+ bfa_fn_lpu(dport->bfa));
+ m->req = req;
+ if ((req == BFI_DPORT_ENABLE) || (req == BFI_DPORT_START)) {
+ m->lpcnt = cpu_to_be32(dport->lpcnt);
+ m->payload = cpu_to_be32(dport->payload);
+ }
+
+ /*
+ * queue I/O message to firmware
+ */
+ bfa_reqq_produce(dport->bfa, BFA_REQQ_DIAG, m->mh);
+
+ return BFA_TRUE;
+}
+
+static void
+bfa_dport_qresume(void *cbarg)
+{
+ struct bfa_dport_s *dport = cbarg;
+
+ bfa_sm_send_event(dport, BFA_DPORT_SM_QRESUME);
+}
+
+static void
+bfa_dport_req_comp(struct bfa_dport_s *dport, struct bfi_diag_dport_rsp_s *msg)
+{
+ msg->status = cpu_to_be32(msg->status);
+ dport->i2hmsg.rsp.status = msg->status;
+ dport->rp_pwwn = msg->pwwn;
+ dport->rp_nwwn = msg->nwwn;
+
+ if ((msg->status == BFA_STATUS_OK) ||
+ (msg->status == BFA_STATUS_DPORT_NO_SFP)) {
+ bfa_trc(dport->bfa, msg->status);
+ bfa_trc(dport->bfa, dport->rp_pwwn);
+ bfa_trc(dport->bfa, dport->rp_nwwn);
+ bfa_sm_send_event(dport, BFA_DPORT_SM_FWRSP);
+
+ } else {
+ bfa_trc(dport->bfa, msg->status);
+ bfa_sm_send_event(dport, BFA_DPORT_SM_REQFAIL);
+ }
+ bfa_cb_fcdiag_dport(dport, msg->status);
+}
+
+static bfa_boolean_t
+bfa_dport_is_sending_req(struct bfa_dport_s *dport)
+{
+ if (bfa_sm_cmp_state(dport, bfa_dport_sm_enabling) ||
+ bfa_sm_cmp_state(dport, bfa_dport_sm_enabling_qwait) ||
+ bfa_sm_cmp_state(dport, bfa_dport_sm_disabling) ||
+ bfa_sm_cmp_state(dport, bfa_dport_sm_disabling_qwait) ||
+ bfa_sm_cmp_state(dport, bfa_dport_sm_starting) ||
+ bfa_sm_cmp_state(dport, bfa_dport_sm_starting_qwait)) {
+ return BFA_TRUE;
+ } else {
+ return BFA_FALSE;
+ }
+}
+
+static void
+bfa_dport_scn(struct bfa_dport_s *dport, struct bfi_diag_dport_scn_s *msg)
+{
+ int i;
+ uint8_t subtesttype;
+
+ bfa_trc(dport->bfa, msg->state);
+ dport->i2hmsg.scn.state = msg->state;
+
+ switch (dport->i2hmsg.scn.state) {
+ case BFI_DPORT_SCN_TESTCOMP:
+ dport->result.end_time = ktime_get_real_seconds();
+ bfa_trc(dport->bfa, dport->result.end_time);
+
+ dport->result.status = msg->info.testcomp.status;
+ bfa_trc(dport->bfa, dport->result.status);
+
+ dport->result.roundtrip_latency =
+ cpu_to_be32(msg->info.testcomp.latency);
+ dport->result.est_cable_distance =
+ cpu_to_be32(msg->info.testcomp.distance);
+ dport->result.buffer_required =
+ be16_to_cpu(msg->info.testcomp.numbuffer);
+
+ dport->result.frmsz = be16_to_cpu(msg->info.testcomp.frm_sz);
+ dport->result.speed = msg->info.testcomp.speed;
+
+ bfa_trc(dport->bfa, dport->result.roundtrip_latency);
+ bfa_trc(dport->bfa, dport->result.est_cable_distance);
+ bfa_trc(dport->bfa, dport->result.buffer_required);
+ bfa_trc(dport->bfa, dport->result.frmsz);
+ bfa_trc(dport->bfa, dport->result.speed);
+
+ for (i = DPORT_TEST_ELOOP; i < DPORT_TEST_MAX; i++) {
+ dport->result.subtest[i].status =
+ msg->info.testcomp.subtest_status[i];
+ bfa_trc(dport->bfa, dport->result.subtest[i].status);
+ }
+ break;
+
+ case BFI_DPORT_SCN_TESTSKIP:
+ case BFI_DPORT_SCN_DDPORT_ENABLE:
+ memset(&dport->result, 0,
+ sizeof(struct bfa_diag_dport_result_s));
+ break;
+
+ case BFI_DPORT_SCN_TESTSTART:
+ memset(&dport->result, 0,
+ sizeof(struct bfa_diag_dport_result_s));
+ dport->rp_pwwn = msg->info.teststart.pwwn;
+ dport->rp_nwwn = msg->info.teststart.nwwn;
+ dport->lpcnt = cpu_to_be32(msg->info.teststart.numfrm);
+ bfa_dport_result_start(dport, msg->info.teststart.mode);
+ break;
+
+ case BFI_DPORT_SCN_SUBTESTSTART:
+ subtesttype = msg->info.teststart.type;
+ dport->result.subtest[subtesttype].start_time =
+ ktime_get_real_seconds();
+ dport->result.subtest[subtesttype].status =
+ DPORT_TEST_ST_INPRG;
+
+ bfa_trc(dport->bfa, subtesttype);
+ bfa_trc(dport->bfa,
+ dport->result.subtest[subtesttype].start_time);
+ break;
+
+ case BFI_DPORT_SCN_SFP_REMOVED:
+ case BFI_DPORT_SCN_DDPORT_DISABLED:
+ case BFI_DPORT_SCN_DDPORT_DISABLE:
+ case BFI_DPORT_SCN_FCPORT_DISABLE:
+ dport->result.status = DPORT_TEST_ST_IDLE;
+ break;
+
+ default:
+ bfa_sm_fault(dport->bfa, msg->state);
+ }
+
+ bfa_sm_send_event(dport, BFA_DPORT_SM_SCN);
+}
+
+/*
+ * Dport enable
+ *
+ * @param[in] *bfa - bfa data struct
+ */
+bfa_status_t
+bfa_dport_enable(struct bfa_s *bfa, u32 lpcnt, u32 pat,
+ bfa_cb_diag_t cbfn, void *cbarg)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ struct bfa_dport_s *dport = &fcdiag->dport;
+
+ /*
+ * Dport is not support in MEZZ card
+ */
+ if (bfa_mfg_is_mezz(dport->bfa->ioc.attr->card_type)) {
+ bfa_trc(dport->bfa, BFA_STATUS_PBC);
+ return BFA_STATUS_CMD_NOTSUPP_MEZZ;
+ }
+
+ /*
+ * Dport is supported in CT2 or above
+ */
+ if (!(bfa_asic_id_ct2(dport->bfa->ioc.pcidev.device_id))) {
+ bfa_trc(dport->bfa, dport->bfa->ioc.pcidev.device_id);
+ return BFA_STATUS_FEATURE_NOT_SUPPORTED;
+ }
+
+ /*
+ * Check to see if IOC is down
+ */
+ if (!bfa_iocfc_is_operational(bfa))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /* if port is PBC disabled, return error */
+ if (bfa_fcport_is_pbcdisabled(bfa)) {
+ bfa_trc(dport->bfa, BFA_STATUS_PBC);
+ return BFA_STATUS_PBC;
+ }
+
+ /*
+ * Check if port mode is FC port
+ */
+ if (bfa_ioc_get_type(&bfa->ioc) != BFA_IOC_TYPE_FC) {
+ bfa_trc(dport->bfa, bfa_ioc_get_type(&bfa->ioc));
+ return BFA_STATUS_CMD_NOTSUPP_CNA;
+ }
+
+ /*
+ * Check if port is in LOOP mode
+ */
+ if ((bfa_fcport_get_cfg_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) ||
+ (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_TOPOLOGY_LOOP;
+ }
+
+ /*
+ * Check if port is TRUNK mode
+ */
+ if (bfa_fcport_is_trunk_enabled(bfa)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_ERROR_TRUNK_ENABLED;
+ }
+
+ /*
+ * Check if diag loopback is running
+ */
+ if (bfa_fcdiag_lb_is_running(bfa)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_DIAG_BUSY;
+ }
+
+ /*
+ * Check to see if port is disable or in dport state
+ */
+ if ((bfa_fcport_is_disabled(bfa) == BFA_FALSE) &&
+ (bfa_fcport_is_dport(bfa) == BFA_FALSE)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_PORT_NOT_DISABLED;
+ }
+
+ /*
+ * Check if dport is in dynamic mode
+ */
+ if (dport->dynamic)
+ return BFA_STATUS_DDPORT_ERR;
+
+ /*
+ * Check if dport is busy
+ */
+ if (bfa_dport_is_sending_req(dport))
+ return BFA_STATUS_DEVBUSY;
+
+ /*
+ * Check if dport is already enabled
+ */
+ if (bfa_sm_cmp_state(dport, bfa_dport_sm_enabled)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_DPORT_ENABLED;
+ }
+
+ bfa_trc(dport->bfa, lpcnt);
+ bfa_trc(dport->bfa, pat);
+ dport->lpcnt = (lpcnt) ? lpcnt : DPORT_ENABLE_LOOPCNT_DEFAULT;
+ dport->payload = (pat) ? pat : LB_PATTERN_DEFAULT;
+ dport->cbfn = cbfn;
+ dport->cbarg = cbarg;
+
+ bfa_sm_send_event(dport, BFA_DPORT_SM_ENABLE);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Dport disable
+ *
+ * @param[in] *bfa - bfa data struct
+ */
+bfa_status_t
+bfa_dport_disable(struct bfa_s *bfa, bfa_cb_diag_t cbfn, void *cbarg)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ struct bfa_dport_s *dport = &fcdiag->dport;
+
+ if (bfa_ioc_is_disabled(&bfa->ioc))
+ return BFA_STATUS_IOC_DISABLED;
+
+ /* if port is PBC disabled, return error */
+ if (bfa_fcport_is_pbcdisabled(bfa)) {
+ bfa_trc(dport->bfa, BFA_STATUS_PBC);
+ return BFA_STATUS_PBC;
+ }
+
+ /*
+ * Check if dport is in dynamic mode
+ */
+ if (dport->dynamic) {
+ return BFA_STATUS_DDPORT_ERR;
+ }
+
+ /*
+ * Check to see if port is disable or in dport state
+ */
+ if ((bfa_fcport_is_disabled(bfa) == BFA_FALSE) &&
+ (bfa_fcport_is_dport(bfa) == BFA_FALSE)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_PORT_NOT_DISABLED;
+ }
+
+ /*
+ * Check if dport is busy
+ */
+ if (bfa_dport_is_sending_req(dport))
+ return BFA_STATUS_DEVBUSY;
+
+ /*
+ * Check if dport is already disabled
+ */
+ if (bfa_sm_cmp_state(dport, bfa_dport_sm_disabled)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_DPORT_DISABLED;
+ }
+
+ dport->cbfn = cbfn;
+ dport->cbarg = cbarg;
+
+ bfa_sm_send_event(dport, BFA_DPORT_SM_DISABLE);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Dport start -- restart dport test
+ *
+ * @param[in] *bfa - bfa data struct
+ */
+bfa_status_t
+bfa_dport_start(struct bfa_s *bfa, u32 lpcnt, u32 pat,
+ bfa_cb_diag_t cbfn, void *cbarg)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ struct bfa_dport_s *dport = &fcdiag->dport;
+
+ /*
+ * Check to see if IOC is down
+ */
+ if (!bfa_iocfc_is_operational(bfa))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /*
+ * Check if dport is in dynamic mode
+ */
+ if (dport->dynamic)
+ return BFA_STATUS_DDPORT_ERR;
+
+ /*
+ * Check if dport is busy
+ */
+ if (bfa_dport_is_sending_req(dport))
+ return BFA_STATUS_DEVBUSY;
+
+ /*
+ * Check if dport is in enabled state.
+ * Test can only be restart when previous test has completed
+ */
+ if (!bfa_sm_cmp_state(dport, bfa_dport_sm_enabled)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_DPORT_DISABLED;
+
+ } else {
+ if (dport->test_state == BFA_DPORT_ST_NO_SFP)
+ return BFA_STATUS_DPORT_INV_SFP;
+
+ if (dport->test_state == BFA_DPORT_ST_INP)
+ return BFA_STATUS_DEVBUSY;
+
+ WARN_ON(dport->test_state != BFA_DPORT_ST_COMP);
+ }
+
+ bfa_trc(dport->bfa, lpcnt);
+ bfa_trc(dport->bfa, pat);
+
+ dport->lpcnt = (lpcnt) ? lpcnt : DPORT_ENABLE_LOOPCNT_DEFAULT;
+ dport->payload = (pat) ? pat : LB_PATTERN_DEFAULT;
+
+ dport->cbfn = cbfn;
+ dport->cbarg = cbarg;
+
+ bfa_sm_send_event(dport, BFA_DPORT_SM_START);
+ return BFA_STATUS_OK;
+}
+
+/*
+ * Dport show -- return dport test result
+ *
+ * @param[in] *bfa - bfa data struct
+ */
+bfa_status_t
+bfa_dport_show(struct bfa_s *bfa, struct bfa_diag_dport_result_s *result)
+{
+ struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa);
+ struct bfa_dport_s *dport = &fcdiag->dport;
+
+ /*
+ * Check to see if IOC is down
+ */
+ if (!bfa_iocfc_is_operational(bfa))
+ return BFA_STATUS_IOC_NON_OP;
+
+ /*
+ * Check if dport is busy
+ */
+ if (bfa_dport_is_sending_req(dport))
+ return BFA_STATUS_DEVBUSY;
+
+ /*
+ * Check if dport is in enabled state.
+ */
+ if (!bfa_sm_cmp_state(dport, bfa_dport_sm_enabled)) {
+ bfa_trc(dport->bfa, 0);
+ return BFA_STATUS_DPORT_DISABLED;
+
+ }
+
+ /*
+ * Check if there is SFP
+ */
+ if (dport->test_state == BFA_DPORT_ST_NO_SFP)
+ return BFA_STATUS_DPORT_INV_SFP;
+
+ memcpy(result, &dport->result, sizeof(struct bfa_diag_dport_result_s));
+
+ return BFA_STATUS_OK;
+}
diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h
index 5902a45c080f..99a960a8a2db 100644
--- a/drivers/scsi/bfa/bfa_svc.h
+++ b/drivers/scsi/bfa/bfa_svc.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFA_SVC_H__
@@ -26,6 +19,7 @@
* Scatter-gather DMA related defines
*/
#define BFA_SGPG_MIN (16)
+#define BFA_SGPG_MAX (8192)
/*
* Alignment macro for SG page allocation
@@ -54,17 +48,21 @@ struct bfa_sgpg_s {
*/
#define BFA_SGPG_NPAGE(_nsges) (((_nsges) / BFI_SGPG_DATA_SGES) + 1)
+/* Max SGPG dma segs required */
+#define BFA_SGPG_DMA_SEGS \
+ BFI_MEM_DMA_NSEGS(BFA_SGPG_MAX, (uint32_t)sizeof(struct bfi_sgpg_s))
+
struct bfa_sgpg_mod_s {
struct bfa_s *bfa;
int num_sgpgs; /* number of SG pages */
int free_sgpgs; /* number of free SG pages */
- struct bfa_sgpg_s *hsgpg_arr; /* BFA SG page array */
- struct bfi_sgpg_s *sgpg_arr; /* actual SG page array */
- u64 sgpg_arr_pa; /* SG page array DMA addr */
struct list_head sgpg_q; /* queue of free SG pages */
struct list_head sgpg_wait_q; /* wait queue for SG pages */
+ struct bfa_mem_dma_s dma_seg[BFA_SGPG_DMA_SEGS];
+ struct bfa_mem_kva_s kva_seg;
};
#define BFA_SGPG_MOD(__bfa) (&(__bfa)->modules.sgpg_mod)
+#define BFA_MEM_SGPG_KVA(__bfa) (&(BFA_SGPG_MOD(__bfa)->kva_seg))
bfa_status_t bfa_sgpg_malloc(struct bfa_s *bfa, struct list_head *sgpg_q,
int nsgpgs);
@@ -79,26 +77,35 @@ void bfa_sgpg_wcancel(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe);
* FCXP related defines
*/
#define BFA_FCXP_MIN (1)
+#define BFA_FCXP_MAX (256)
#define BFA_FCXP_MAX_IBUF_SZ (2 * 1024 + 256)
#define BFA_FCXP_MAX_LBUF_SZ (4 * 1024 + 256)
+/* Max FCXP dma segs required */
+#define BFA_FCXP_DMA_SEGS \
+ BFI_MEM_DMA_NSEGS(BFA_FCXP_MAX, \
+ (u32)BFA_FCXP_MAX_IBUF_SZ + BFA_FCXP_MAX_LBUF_SZ)
+
struct bfa_fcxp_mod_s {
struct bfa_s *bfa; /* backpointer to BFA */
struct bfa_fcxp_s *fcxp_list; /* array of FCXPs */
u16 num_fcxps; /* max num FCXP requests */
- struct list_head fcxp_free_q; /* free FCXPs */
- struct list_head fcxp_active_q; /* active FCXPs */
- void *req_pld_list_kva; /* list of FCXP req pld */
- u64 req_pld_list_pa; /* list of FCXP req pld */
- void *rsp_pld_list_kva; /* list of FCXP resp pld */
- u64 rsp_pld_list_pa; /* list of FCXP resp pld */
- struct list_head wait_q; /* wait queue for free fcxp */
+ struct list_head fcxp_req_free_q; /* free FCXPs used for sending req */
+ struct list_head fcxp_rsp_free_q; /* free FCXPs used for sending req */
+ struct list_head fcxp_active_q; /* active FCXPs */
+ struct list_head req_wait_q; /* wait queue for free req_fcxp */
+ struct list_head rsp_wait_q; /* wait queue for free rsp_fcxp */
+ struct list_head fcxp_req_unused_q; /* unused req_fcxps */
+ struct list_head fcxp_rsp_unused_q; /* unused rsp_fcxps */
u32 req_pld_sz;
u32 rsp_pld_sz;
+ struct bfa_mem_dma_s dma_seg[BFA_FCXP_DMA_SEGS];
+ struct bfa_mem_kva_s kva_seg;
};
#define BFA_FCXP_MOD(__bfa) (&(__bfa)->modules.fcxp_mod)
#define BFA_FCXP_FROM_TAG(__mod, __tag) (&(__mod)->fcxp_list[__tag])
+#define BFA_MEM_FCXP_KVA(__bfa) (&(BFA_FCXP_MOD(__bfa)->kva_seg))
typedef void (*fcxp_send_cb_t) (struct bfa_s *ioc, struct bfa_fcxp_s *fcxp,
void *cb_arg, bfa_status_t req_status,
@@ -186,6 +193,7 @@ struct bfa_fcxp_s {
struct bfa_cb_qe_s hcb_qe; /* comp: callback qelem */
struct bfa_reqq_wait_s reqq_wqe;
bfa_boolean_t reqq_waiting;
+ bfa_boolean_t req_rsp; /* Used to track req/rsp fcxp */
};
struct bfa_fcxp_wqe_s {
@@ -206,42 +214,31 @@ struct bfa_fcxp_wqe_s {
#define BFA_FCXP_RSP_FCHS(_fcxp) (&((_fcxp)->rsp_info.fchs))
#define BFA_FCXP_RSP_PLD(_fcxp) (bfa_fcxp_get_rspbuf(_fcxp))
-#define BFA_FCXP_REQ_PLD_PA(_fcxp) \
- ((_fcxp)->fcxp_mod->req_pld_list_pa + \
- ((_fcxp)->fcxp_mod->req_pld_sz * (_fcxp)->fcxp_tag))
+#define BFA_FCXP_REQ_PLD_PA(_fcxp) \
+ bfa_mem_get_dmabuf_pa((_fcxp)->fcxp_mod, (_fcxp)->fcxp_tag, \
+ (_fcxp)->fcxp_mod->req_pld_sz + (_fcxp)->fcxp_mod->rsp_pld_sz)
-#define BFA_FCXP_RSP_PLD_PA(_fcxp) \
- ((_fcxp)->fcxp_mod->rsp_pld_list_pa + \
- ((_fcxp)->fcxp_mod->rsp_pld_sz * (_fcxp)->fcxp_tag))
+/* fcxp_buf = req_buf + rsp_buf :- add req_buf_sz to get to rsp_buf */
+#define BFA_FCXP_RSP_PLD_PA(_fcxp) \
+ (bfa_mem_get_dmabuf_pa((_fcxp)->fcxp_mod, (_fcxp)->fcxp_tag, \
+ (_fcxp)->fcxp_mod->req_pld_sz + (_fcxp)->fcxp_mod->rsp_pld_sz) + \
+ (_fcxp)->fcxp_mod->req_pld_sz)
void bfa_fcxp_isr(struct bfa_s *bfa, struct bfi_msg_s *msg);
-
-/*
- * RPORT related defines
- */
-enum bfa_rport_event {
- BFA_RPORT_SM_CREATE = 1, /* rport create event */
- BFA_RPORT_SM_DELETE = 2, /* deleting an existing rport */
- BFA_RPORT_SM_ONLINE = 3, /* rport is online */
- BFA_RPORT_SM_OFFLINE = 4, /* rport is offline */
- BFA_RPORT_SM_FWRSP = 5, /* firmware response */
- BFA_RPORT_SM_HWFAIL = 6, /* IOC h/w failure */
- BFA_RPORT_SM_QOS_SCN = 7, /* QoS SCN from firmware */
- BFA_RPORT_SM_SET_SPEED = 8, /* Set Rport Speed */
- BFA_RPORT_SM_QRESUME = 9, /* space in requeue queue */
-};
-
#define BFA_RPORT_MIN 4
struct bfa_rport_mod_s {
struct bfa_rport_s *rps_list; /* list of rports */
struct list_head rp_free_q; /* free bfa_rports */
struct list_head rp_active_q; /* free bfa_rports */
+ struct list_head rp_unused_q; /* unused bfa rports */
u16 num_rports; /* number of rports */
+ struct bfa_mem_kva_s kva_seg;
};
#define BFA_RPORT_MOD(__bfa) (&(__bfa)->modules.rport_mod)
+#define BFA_MEM_RPORT_KVA(__bfa) (&(BFA_RPORT_MOD(__bfa)->kva_seg))
/*
* Convert rport tag to RPORT
@@ -254,6 +251,7 @@ struct bfa_rport_mod_s {
* protected functions
*/
void bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg);
+void bfa_rport_res_recfg(struct bfa_s *bfa, u16 num_rport_fw);
/*
* BFA rport information.
@@ -271,15 +269,34 @@ struct bfa_rport_info_s {
};
/*
+ * RPORT related defines
+ */
+enum bfa_rport_event {
+ BFA_RPORT_SM_CREATE = 1, /* rport create event */
+ BFA_RPORT_SM_DELETE = 2, /* deleting an existing rport */
+ BFA_RPORT_SM_ONLINE = 3, /* rport is online */
+ BFA_RPORT_SM_OFFLINE = 4, /* rport is offline */
+ BFA_RPORT_SM_FWRSP = 5, /* firmware response */
+ BFA_RPORT_SM_HWFAIL = 6, /* IOC h/w failure */
+ BFA_RPORT_SM_QOS_SCN = 7, /* QoS SCN from firmware */
+ BFA_RPORT_SM_SET_SPEED = 8, /* Set Rport Speed */
+ BFA_RPORT_SM_QRESUME = 9, /* space in requeue queue */
+};
+
+struct bfa_rport_s;
+typedef void (*bfa_rport_sm_t)(struct bfa_rport_s *, enum bfa_rport_event);
+
+/*
* BFA rport data structure
*/
struct bfa_rport_s {
struct list_head qe; /* queue element */
- bfa_sm_t sm; /* state machine */
+ bfa_rport_sm_t sm; /* state machine */
struct bfa_s *bfa; /* backpointer to BFA */
void *rport_drv; /* fcs/driver rport object */
u16 fw_handle; /* firmware rport handle */
u16 rport_tag; /* BFA rport tag */
+ u8 lun_mask; /* LUN mask flag */
struct bfa_rport_info_s rport_info; /* rport info from fcs/driver */
struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */
struct bfa_cb_qe_s hcb_qe; /* BFA callback qelem */
@@ -298,7 +315,7 @@ struct bfa_rport_s {
*/
#define BFA_UF_MIN (4)
-
+#define BFA_UF_MAX (256)
struct bfa_uf_s {
struct list_head qe; /* queue element */
@@ -326,36 +343,59 @@ struct bfa_uf_s {
*/
typedef void (*bfa_cb_uf_recv_t) (void *cbarg, struct bfa_uf_s *uf);
+#define BFA_UF_BUFSZ (2 * 1024 + 256)
+
+struct bfa_uf_buf_s {
+ u8 d[BFA_UF_BUFSZ];
+};
+
+#define BFA_PER_UF_DMA_SZ \
+ (u32)BFA_ROUNDUP(sizeof(struct bfa_uf_buf_s), BFA_DMA_ALIGN_SZ)
+
+/* Max UF dma segs required */
+#define BFA_UF_DMA_SEGS BFI_MEM_DMA_NSEGS(BFA_UF_MAX, BFA_PER_UF_DMA_SZ)
+
struct bfa_uf_mod_s {
struct bfa_s *bfa; /* back pointer to BFA */
struct bfa_uf_s *uf_list; /* array of UFs */
u16 num_ufs; /* num unsolicited rx frames */
struct list_head uf_free_q; /* free UFs */
struct list_head uf_posted_q; /* UFs posted to IOC */
- struct bfa_uf_buf_s *uf_pbs_kva; /* list UF bufs request pld */
- u64 uf_pbs_pa; /* phy addr for UF bufs */
+ struct list_head uf_unused_q; /* unused UF's */
struct bfi_uf_buf_post_s *uf_buf_posts;
/* pre-built UF post msgs */
bfa_cb_uf_recv_t ufrecv; /* uf recv handler function */
void *cbarg; /* uf receive handler arg */
+ struct bfa_mem_dma_s dma_seg[BFA_UF_DMA_SEGS];
+ struct bfa_mem_kva_s kva_seg;
};
#define BFA_UF_MOD(__bfa) (&(__bfa)->modules.uf_mod)
+#define BFA_MEM_UF_KVA(__bfa) (&(BFA_UF_MOD(__bfa)->kva_seg))
#define ufm_pbs_pa(_ufmod, _uftag) \
- ((_ufmod)->uf_pbs_pa + sizeof(struct bfa_uf_buf_s) * (_uftag))
+ bfa_mem_get_dmabuf_pa(_ufmod, _uftag, BFA_PER_UF_DMA_SZ)
void bfa_uf_isr(struct bfa_s *bfa, struct bfi_msg_s *msg);
-
-#define BFA_UF_BUFSZ (2 * 1024 + 256)
+void bfa_uf_res_recfg(struct bfa_s *bfa, u16 num_uf_fw);
/*
- * @todo private
+ * lps_pvt BFA LPS private functions
*/
-struct bfa_uf_buf_s {
- u8 d[BFA_UF_BUFSZ];
+
+enum bfa_lps_event {
+ BFA_LPS_SM_LOGIN = 1, /* login request from user */
+ BFA_LPS_SM_LOGOUT = 2, /* logout request from user */
+ BFA_LPS_SM_FWRSP = 3, /* f/w response to login/logout */
+ BFA_LPS_SM_RESUME = 4, /* space present in reqq queue */
+ BFA_LPS_SM_DELETE = 5, /* lps delete from user */
+ BFA_LPS_SM_OFFLINE = 6, /* Link is offline */
+ BFA_LPS_SM_RX_CVL = 7, /* Rx clear virtual link */
+ BFA_LPS_SM_SET_N2N_PID = 8, /* Set assigned PID for n2n */
};
+struct bfa_lps_s;
+typedef void (*bfa_lps_sm_t)(struct bfa_lps_s *, enum bfa_lps_event);
/*
* LPS - bfa lport login/logout service interface
@@ -363,8 +403,9 @@ struct bfa_uf_buf_s {
struct bfa_lps_s {
struct list_head qe; /* queue element */
struct bfa_s *bfa; /* parent bfa instance */
- bfa_sm_t sm; /* finite state machine */
- u8 lp_tag; /* lport tag */
+ bfa_lps_sm_t sm; /* finite state machine */
+ u8 bfa_tag; /* lport tag */
+ u8 fw_tag; /* lport fw tag */
u8 reqq; /* lport request queue */
u8 alpa; /* ALPA for loop topologies */
u32 lp_pid; /* lport port ID */
@@ -379,6 +420,7 @@ struct bfa_lps_s {
u16 pr_bbcred; /* BB_CREDIT from peer */
u8 lsrjt_rsn; /* LSRJT reason */
u8 lsrjt_expl; /* LSRJT explanation */
+ u8 lun_mask; /* LUN mask flag */
wwn_t pwwn; /* port wwn of lport */
wwn_t nwwn; /* node wwn of lport */
wwn_t pr_pwwn; /* port wwn of lport peer */
@@ -395,12 +437,15 @@ struct bfa_lps_s {
struct bfa_lps_mod_s {
struct list_head lps_free_q;
struct list_head lps_active_q;
+ struct list_head lps_login_q;
struct bfa_lps_s *lps_arr;
int num_lps;
+ struct bfa_mem_kva_s kva_seg;
};
#define BFA_LPS_MOD(__bfa) (&(__bfa)->modules.lps_mod)
#define BFA_LPS_FROM_TAG(__mod, __tag) (&(__mod)->lps_arr[__tag])
+#define BFA_MEM_LPS_KVA(__bfa) (&(BFA_LPS_MOD(__bfa)->kva_seg))
/*
* external functions
@@ -413,14 +458,26 @@ void bfa_lps_isr(struct bfa_s *bfa, struct bfi_msg_s *msg);
*/
#define BFA_FCPORT(_bfa) (&((_bfa)->modules.port))
-typedef void (*bfa_cb_port_t) (void *cbarg, enum bfa_status status);
+
+/*
+ * BFA port link notification state machine events
+ */
+
+enum bfa_fcport_ln_sm_event {
+ BFA_FCPORT_LN_SM_LINKUP = 1, /* linkup event */
+ BFA_FCPORT_LN_SM_LINKDOWN = 2, /* linkdown event */
+ BFA_FCPORT_LN_SM_NOTIFICATION = 3 /* done notification */
+};
+
+struct bfa_fcport_ln_s;
+typedef void (*bfa_fcport_ln_sm_t)(struct bfa_fcport_ln_s *, enum bfa_fcport_ln_sm_event);
/*
* Link notification data structure
*/
struct bfa_fcport_ln_s {
struct bfa_fcport_s *fcport;
- bfa_sm_t sm;
+ bfa_fcport_ln_sm_t sm;
struct bfa_cb_qe_s ln_qe; /* BFA callback queue elem for ln */
enum bfa_port_linkstate ln_event; /* ln event for callback */
};
@@ -430,19 +487,44 @@ struct bfa_fcport_trunk_s {
};
/*
+ * BFA port state machine events
+ */
+enum bfa_fcport_sm_event {
+ BFA_FCPORT_SM_START = 1, /* start port state machine */
+ BFA_FCPORT_SM_STOP = 2, /* stop port state machine */
+ BFA_FCPORT_SM_ENABLE = 3, /* enable port */
+ BFA_FCPORT_SM_DISABLE = 4, /* disable port state machine */
+ BFA_FCPORT_SM_FWRSP = 5, /* firmware enable/disable rsp */
+ BFA_FCPORT_SM_LINKUP = 6, /* firmware linkup event */
+ BFA_FCPORT_SM_LINKDOWN = 7, /* firmware linkup down */
+ BFA_FCPORT_SM_QRESUME = 8, /* CQ space available */
+ BFA_FCPORT_SM_HWFAIL = 9, /* IOC h/w failure */
+ BFA_FCPORT_SM_DPORTENABLE = 10, /* enable dport */
+ BFA_FCPORT_SM_DPORTDISABLE = 11,/* disable dport */
+ BFA_FCPORT_SM_FAA_MISCONFIG = 12, /* FAA misconfiguratin */
+ BFA_FCPORT_SM_DDPORTENABLE = 13, /* enable ddport */
+ BFA_FCPORT_SM_DDPORTDISABLE = 14, /* disable ddport */
+};
+
+struct bfa_fcport_s;
+typedef void (*bfa_fcport_sm_t)(struct bfa_fcport_s *, enum bfa_fcport_sm_event);
+
+/*
* BFA FC port data structure
*/
struct bfa_fcport_s {
struct bfa_s *bfa; /* parent BFA instance */
- bfa_sm_t sm; /* port state machine */
+ bfa_fcport_sm_t sm; /* port state machine */
wwn_t nwwn; /* node wwn of physical port */
wwn_t pwwn; /* port wwn of physical oprt */
enum bfa_port_speed speed_sup;
/* supported speeds */
enum bfa_port_speed speed; /* current speed */
enum bfa_port_topology topology; /* current topology */
- u8 myalpa; /* my ALPA in LOOP topology */
u8 rsvd[3];
+ u8 myalpa; /* my ALPA in LOOP topology */
+ u8 alpabm_valid; /* alpa bitmap valid or not */
+ struct fc_alpabm_s alpabm; /* alpa bitmap */
struct bfa_port_cfg_s cfg; /* current port configuration */
bfa_boolean_t use_flash_cfg; /* get port cfg from flash */
struct bfa_qos_attr_s qos_attr; /* QoS Attributes */
@@ -467,21 +549,24 @@ struct bfa_fcport_s {
u8 *stats_kva;
u64 stats_pa;
union bfa_fcport_stats_u *stats;
- union bfa_fcport_stats_u *stats_ret; /* driver stats location */
bfa_status_t stats_status; /* stats/statsclr status */
- bfa_boolean_t stats_busy; /* outstanding stats/statsclr */
+ struct list_head stats_pending_q;
+ struct list_head statsclr_pending_q;
bfa_boolean_t stats_qfull;
- u32 stats_reset_time; /* stats reset time stamp */
- bfa_cb_port_t stats_cbfn; /* driver callback function */
- void *stats_cbarg; /* *!< user callback arg */
+ time64_t stats_reset_time; /* stats reset time stamp */
bfa_boolean_t diag_busy; /* diag busy status */
bfa_boolean_t beacon; /* port beacon status */
bfa_boolean_t link_e2e_beacon; /* link beacon status */
struct bfa_fcport_trunk_s trunk;
u16 fcoe_vlan;
+ struct bfa_mem_dma_s fcport_dma;
+ bfa_boolean_t stats_dma_ready;
+ struct bfa_bbcr_attr_s bbcr_attr;
+ enum bfa_fec_state_s fec_state;
};
#define BFA_FCPORT_MOD(__bfa) (&(__bfa)->modules.fcport)
+#define BFA_MEM_FCPORT_DMA(__bfa) (&(BFA_FCPORT_MOD(__bfa)->fcport_dma))
/*
* protected functions
@@ -500,30 +585,42 @@ enum bfa_port_speed bfa_fcport_get_speed(struct bfa_s *bfa);
bfa_status_t bfa_fcport_cfg_topology(struct bfa_s *bfa,
enum bfa_port_topology topo);
enum bfa_port_topology bfa_fcport_get_topology(struct bfa_s *bfa);
+enum bfa_port_topology bfa_fcport_get_cfg_topology(struct bfa_s *bfa);
bfa_status_t bfa_fcport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa);
-bfa_boolean_t bfa_fcport_get_hardalpa(struct bfa_s *bfa, u8 *alpa);
u8 bfa_fcport_get_myalpa(struct bfa_s *bfa);
bfa_status_t bfa_fcport_clr_hardalpa(struct bfa_s *bfa);
bfa_status_t bfa_fcport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxsize);
u16 bfa_fcport_get_maxfrsize(struct bfa_s *bfa);
u8 bfa_fcport_get_rx_bbcredit(struct bfa_s *bfa);
void bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr);
-wwn_t bfa_fcport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node);
void bfa_fcport_event_register(struct bfa_s *bfa,
void (*event_cbfn) (void *cbarg,
enum bfa_port_linkstate event), void *event_cbarg);
bfa_boolean_t bfa_fcport_is_disabled(struct bfa_s *bfa);
+bfa_boolean_t bfa_fcport_is_dport(struct bfa_s *bfa);
+bfa_boolean_t bfa_fcport_is_ddport(struct bfa_s *bfa);
+bfa_status_t bfa_fcport_set_qos_bw(struct bfa_s *bfa,
+ struct bfa_qos_bw_s *qos_bw);
enum bfa_port_speed bfa_fcport_get_ratelim_speed(struct bfa_s *bfa);
void bfa_fcport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit);
bfa_boolean_t bfa_fcport_is_ratelim(struct bfa_s *bfa);
+void bfa_fcport_beacon(void *dev, bfa_boolean_t beacon,
+ bfa_boolean_t link_e2e_beacon);
bfa_boolean_t bfa_fcport_is_linkup(struct bfa_s *bfa);
bfa_status_t bfa_fcport_get_stats(struct bfa_s *bfa,
- union bfa_fcport_stats_u *stats,
- bfa_cb_port_t cbfn, void *cbarg);
-bfa_status_t bfa_fcport_clear_stats(struct bfa_s *bfa, bfa_cb_port_t cbfn,
- void *cbarg);
+ struct bfa_cb_pending_q_s *cb);
+bfa_status_t bfa_fcport_clear_stats(struct bfa_s *bfa,
+ struct bfa_cb_pending_q_s *cb);
bfa_boolean_t bfa_fcport_is_qos_enabled(struct bfa_s *bfa);
+bfa_boolean_t bfa_fcport_is_trunk_enabled(struct bfa_s *bfa);
+void bfa_fcport_dportenable(struct bfa_s *bfa);
+void bfa_fcport_dportdisable(struct bfa_s *bfa);
+bfa_status_t bfa_fcport_is_pbcdisabled(struct bfa_s *bfa);
+bfa_status_t bfa_fcport_cfg_bbcr(struct bfa_s *bfa,
+ bfa_boolean_t on_off, u8 bb_scn);
+bfa_status_t bfa_fcport_get_bbcr_attr(struct bfa_s *bfa,
+ struct bfa_bbcr_attr_s *bbcr_attr);
/*
* bfa rport API functions
@@ -537,27 +634,40 @@ void bfa_cb_rport_offline(void *rport);
void bfa_cb_rport_qos_scn_flowid(void *rport,
struct bfa_rport_qos_attr_s old_qos_attr,
struct bfa_rport_qos_attr_s new_qos_attr);
+void bfa_cb_rport_scn_online(struct bfa_s *bfa);
+void bfa_cb_rport_scn_offline(struct bfa_s *bfa);
+void bfa_cb_rport_scn_no_dev(void *rp);
void bfa_cb_rport_qos_scn_prio(void *rport,
struct bfa_rport_qos_attr_s old_qos_attr,
struct bfa_rport_qos_attr_s new_qos_attr);
/*
+ * Rport LUN masking related
+ */
+#define BFA_RPORT_TAG_INVALID 0xffff
+#define BFA_LP_TAG_INVALID 0xff
+void bfa_rport_set_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp);
+void bfa_rport_unset_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp);
+
+/*
* bfa fcxp API functions
*/
-struct bfa_fcxp_s *bfa_fcxp_alloc(void *bfad_fcxp, struct bfa_s *bfa,
+struct bfa_fcxp_s *bfa_fcxp_req_rsp_alloc(void *bfad_fcxp, struct bfa_s *bfa,
int nreq_sgles, int nrsp_sgles,
bfa_fcxp_get_sgaddr_t get_req_sga,
bfa_fcxp_get_sglen_t get_req_sglen,
bfa_fcxp_get_sgaddr_t get_rsp_sga,
- bfa_fcxp_get_sglen_t get_rsp_sglen);
-void bfa_fcxp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe,
+ bfa_fcxp_get_sglen_t get_rsp_sglen,
+ bfa_boolean_t req);
+void bfa_fcxp_req_rsp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe,
bfa_fcxp_alloc_cbfn_t alloc_cbfn,
void *cbarg, void *bfad_fcxp,
int nreq_sgles, int nrsp_sgles,
bfa_fcxp_get_sgaddr_t get_req_sga,
bfa_fcxp_get_sglen_t get_req_sglen,
bfa_fcxp_get_sgaddr_t get_rsp_sga,
- bfa_fcxp_get_sglen_t get_rsp_sglen);
+ bfa_fcxp_get_sglen_t get_rsp_sglen,
+ bfa_boolean_t req);
void bfa_fcxp_walloc_cancel(struct bfa_s *bfa,
struct bfa_fcxp_wqe_s *wqe);
void bfa_fcxp_discard(struct bfa_fcxp_s *fcxp);
@@ -574,9 +684,8 @@ void bfa_fcxp_send(struct bfa_fcxp_s *fcxp, struct bfa_rport_s *rport,
bfa_cb_fcxp_send_t cbfn,
void *cbarg,
u32 rsp_maxlen, u8 rsp_timeout);
-bfa_status_t bfa_fcxp_abort(struct bfa_fcxp_s *fcxp);
-u32 bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp);
u32 bfa_fcxp_get_maxrsp(struct bfa_s *bfa);
+void bfa_fcxp_res_recfg(struct bfa_s *bfa, u16 num_fcxp_fw);
static inline void *
bfa_uf_get_frmbuf(struct bfa_uf_s *uf)
@@ -611,11 +720,105 @@ void bfa_lps_fdisc(struct bfa_lps_s *lps, void *uarg, u16 pdusz,
wwn_t pwwn, wwn_t nwwn);
void bfa_lps_fdisclogo(struct bfa_lps_s *lps);
void bfa_lps_set_n2n_pid(struct bfa_lps_s *lps, u32 n2n_pid);
+u8 bfa_lps_get_fwtag(struct bfa_s *bfa, u8 lp_tag);
u32 bfa_lps_get_base_pid(struct bfa_s *bfa);
u8 bfa_lps_get_tag_from_pid(struct bfa_s *bfa, u32 pid);
void bfa_cb_lps_flogi_comp(void *bfad, void *uarg, bfa_status_t status);
+void bfa_cb_lps_flogo_comp(void *bfad, void *uarg);
void bfa_cb_lps_fdisc_comp(void *bfad, void *uarg, bfa_status_t status);
void bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg);
void bfa_cb_lps_cvl_event(void *bfad, void *uarg);
+/* FAA specific APIs */
+bfa_status_t bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
+ bfa_cb_iocfc_t cbfn, void *cbarg);
+
+/*
+ * FC DIAG data structure
+ */
+struct bfa_fcdiag_qtest_s {
+ struct bfa_diag_qtest_result_s *result;
+ bfa_cb_diag_t cbfn;
+ void *cbarg;
+ struct bfa_timer_s timer;
+ u32 status;
+ u32 count;
+ u8 lock;
+ u8 queue;
+ u8 all;
+ u8 timer_active;
+};
+
+struct bfa_fcdiag_lb_s {
+ bfa_cb_diag_t cbfn;
+ void *cbarg;
+ void *result;
+ bfa_boolean_t lock;
+ u32 status;
+};
+
+/*
+ * BFA DPORT state machine events
+ */
+enum bfa_dport_sm_event {
+ BFA_DPORT_SM_ENABLE = 1, /* dport enable event */
+ BFA_DPORT_SM_DISABLE = 2, /* dport disable event */
+ BFA_DPORT_SM_FWRSP = 3, /* fw enable/disable rsp */
+ BFA_DPORT_SM_QRESUME = 4, /* CQ space available */
+ BFA_DPORT_SM_HWFAIL = 5, /* IOC h/w failure */
+ BFA_DPORT_SM_START = 6, /* re-start dport test */
+ BFA_DPORT_SM_REQFAIL = 7, /* request failure */
+ BFA_DPORT_SM_SCN = 8, /* state change notify frm fw */
+};
+
+struct bfa_dport_s;
+typedef void (*bfa_dport_sm_t)(struct bfa_dport_s *, enum bfa_dport_sm_event);
+
+struct bfa_dport_s {
+ struct bfa_s *bfa; /* Back pointer to BFA */
+ bfa_dport_sm_t sm; /* finite state machine */
+ struct bfa_reqq_wait_s reqq_wait;
+ bfa_cb_diag_t cbfn;
+ void *cbarg;
+ union bfi_diag_dport_msg_u i2hmsg;
+ u8 test_state; /* enum dport_test_state */
+ u8 dynamic; /* boolean_t */
+ u8 rsvd[2];
+ u32 lpcnt;
+ u32 payload; /* user defined payload pattern */
+ wwn_t rp_pwwn;
+ wwn_t rp_nwwn;
+ struct bfa_diag_dport_result_s result;
+};
+
+struct bfa_fcdiag_s {
+ struct bfa_s *bfa; /* Back pointer to BFA */
+ struct bfa_trc_mod_s *trcmod;
+ struct bfa_fcdiag_lb_s lb;
+ struct bfa_fcdiag_qtest_s qtest;
+ struct bfa_dport_s dport;
+};
+
+#define BFA_FCDIAG_MOD(__bfa) (&(__bfa)->modules.fcdiag)
+
+void bfa_fcdiag_intr(struct bfa_s *bfa, struct bfi_msg_s *msg);
+
+bfa_status_t bfa_fcdiag_loopback(struct bfa_s *bfa,
+ enum bfa_port_opmode opmode,
+ enum bfa_port_speed speed, u32 lpcnt, u32 pat,
+ struct bfa_diag_loopback_result_s *result,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_fcdiag_queuetest(struct bfa_s *bfa, u32 ignore,
+ u32 queue, struct bfa_diag_qtest_result_s *result,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_fcdiag_lb_is_running(struct bfa_s *bfa);
+bfa_status_t bfa_dport_enable(struct bfa_s *bfa, u32 lpcnt, u32 pat,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_dport_disable(struct bfa_s *bfa, bfa_cb_diag_t cbfn,
+ void *cbarg);
+bfa_status_t bfa_dport_start(struct bfa_s *bfa, u32 lpcnt, u32 pat,
+ bfa_cb_diag_t cbfn, void *cbarg);
+bfa_status_t bfa_dport_show(struct bfa_s *bfa,
+ struct bfa_diag_dport_result_s *result);
+
#endif /* __BFA_SVC_H__ */
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index 59b5e9b61d71..bdfd06516671 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -26,7 +19,7 @@
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/firmware.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/fcntl.h>
#include "bfad_drv.h"
@@ -56,14 +49,16 @@ int fdmi_enable = BFA_TRUE;
int pcie_max_read_reqsz;
int bfa_debugfs_enable = 1;
int msix_disable_cb = 0, msix_disable_ct = 0;
+int max_xfer_size = BFAD_MAX_SECTORS >> 1;
+static int max_rport_logins = BFA_FCS_MAX_RPORT_LOGINS;
/* Firmware releated */
-u32 bfi_image_ct_fc_size, bfi_image_ct_cna_size, bfi_image_cb_fc_size;
-u32 *bfi_image_ct_fc, *bfi_image_ct_cna, *bfi_image_cb_fc;
+u32 bfi_image_cb_size, bfi_image_ct_size, bfi_image_ct2_size;
+u32 *bfi_image_cb, *bfi_image_ct, *bfi_image_ct2;
-#define BFAD_FW_FILE_CT_FC "ctfw_fc.bin"
-#define BFAD_FW_FILE_CT_CNA "ctfw_cna.bin"
-#define BFAD_FW_FILE_CB_FC "cbfw_fc.bin"
+#define BFAD_FW_FILE_CB "cbfw-3.2.5.1.bin"
+#define BFAD_FW_FILE_CT "ctfw-3.2.5.1.bin"
+#define BFAD_FW_FILE_CT2 "ct2fw-3.2.5.1.bin"
static u32 *bfad_load_fwimg(struct pci_dev *pdev);
static void bfad_free_fwimg(void);
@@ -71,18 +66,18 @@ static void bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
u32 *bfi_image_size, char *fw_name);
static const char *msix_name_ct[] = {
+ "ctrl",
"cpe0", "cpe1", "cpe2", "cpe3",
- "rme0", "rme1", "rme2", "rme3",
- "ctrl" };
+ "rme0", "rme1", "rme2", "rme3" };
static const char *msix_name_cb[] = {
"cpe0", "cpe1", "cpe2", "cpe3",
"rme0", "rme1", "rme2", "rme3",
"eemc", "elpu0", "elpu1", "epss", "mlpu" };
-MODULE_FIRMWARE(BFAD_FW_FILE_CT_FC);
-MODULE_FIRMWARE(BFAD_FW_FILE_CT_CNA);
-MODULE_FIRMWARE(BFAD_FW_FILE_CB_FC);
+MODULE_FIRMWARE(BFAD_FW_FILE_CB);
+MODULE_FIRMWARE(BFAD_FW_FILE_CT);
+MODULE_FIRMWARE(BFAD_FW_FILE_CT2);
module_param(os_name, charp, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(os_name, "OS name of the hba host machine");
@@ -128,13 +123,9 @@ MODULE_PARM_DESC(bfa_linkup_delay, "Link up delay, default=30 secs for "
"boot port. Otherwise 10 secs in RHEL4 & 0 for "
"[RHEL5, SLES10, ESX40] Range[>0]");
module_param(msix_disable_cb, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(msix_disable_cb, "Disable Message Signaled Interrupts "
- "for Brocade-415/425/815/825 cards, default=0, "
- " Range[false:0|true:1]");
+MODULE_PARM_DESC(msix_disable_cb, "Disable Message Signaled Interrupts for QLogic-415/425/815/825 cards, default=0 Range[false:0|true:1]");
module_param(msix_disable_ct, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(msix_disable_ct, "Disable Message Signaled Interrupts "
- "if possible for Brocade-1010/1020/804/1007/902/1741 "
- "cards, default=0, Range[false:0|true:1]");
+MODULE_PARM_DESC(msix_disable_ct, "Disable Message Signaled Interrupts if possible for QLogic-1010/1020/804/1007/902/1741 cards, default=0, Range[false:0|true:1]");
module_param(fdmi_enable, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(fdmi_enable, "Enables fdmi registration, default=1, "
"Range[false:0|true:1]");
@@ -144,6 +135,11 @@ MODULE_PARM_DESC(pcie_max_read_reqsz, "PCIe max read request size, default=0 "
module_param(bfa_debugfs_enable, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(bfa_debugfs_enable, "Enables debugfs feature, default=1,"
" Range[false:0|true:1]");
+module_param(max_xfer_size, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_xfer_size, "default=32MB,"
+ " Range[64k|128k|256k|512k|1024k|2048k]");
+module_param(max_rport_logins, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_rport_logins, "Max number of logins to initiator and target rports on a port (physical/logical), default=1024");
static void
bfad_sm_uninit(struct bfad_s *bfad, enum bfad_sm_event event);
@@ -197,6 +193,7 @@ static void
bfad_sm_created(struct bfad_s *bfad, enum bfad_sm_event event)
{
unsigned long flags;
+ bfa_status_t ret;
bfa_trc(bfad, event);
@@ -210,7 +207,7 @@ bfad_sm_created(struct bfad_s *bfad, enum bfad_sm_event event)
if (bfad_setup_intr(bfad)) {
printk(KERN_WARNING "bfad%d: bfad_setup_intr failed\n",
bfad->inst_no);
- bfa_sm_send_event(bfad, BFAD_E_INTR_INIT_FAILED);
+ bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
break;
}
@@ -235,8 +232,26 @@ bfad_sm_created(struct bfad_s *bfad, enum bfad_sm_event event)
printk(KERN_WARNING
"bfa %s: bfa init failed\n",
bfad->pci_name);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_fcs_init(&bfad->bfa_fcs);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ ret = bfad_cfg_pport(bfad, BFA_LPORT_ROLE_FCP_IM);
+ if (ret != BFA_STATUS_OK) {
+ init_completion(&bfad->comp);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfad->pport.flags |= BFAD_PORT_DELETE;
+ bfa_fcs_exit(&bfad->bfa_fcs);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ wait_for_completion(&bfad->comp);
+
+ bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
+ break;
+ }
bfad->bfad_flags |= BFAD_HAL_INIT_FAIL;
- bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
+ bfa_sm_send_event(bfad, BFAD_E_HAL_INIT_FAILED);
}
break;
@@ -266,12 +281,14 @@ bfad_sm_initializing(struct bfad_s *bfad, enum bfad_sm_event event)
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
retval = bfad_start_ops(bfad);
- if (retval != BFA_STATUS_OK)
+ if (retval != BFA_STATUS_OK) {
+ bfa_sm_set_state(bfad, bfad_sm_failed);
break;
+ }
bfa_sm_set_state(bfad, bfad_sm_operational);
break;
- case BFAD_E_INTR_INIT_FAILED:
+ case BFAD_E_INIT_FAILED:
bfa_sm_set_state(bfad, bfad_sm_uninit);
kthread_stop(bfad->bfad_tsk);
spin_lock_irqsave(&bfad->bfad_lock, flags);
@@ -279,7 +296,7 @@ bfad_sm_initializing(struct bfad_s *bfad, enum bfad_sm_event event)
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
break;
- case BFAD_E_INIT_FAILED:
+ case BFAD_E_HAL_INIT_FAILED:
bfa_sm_set_state(bfad, bfad_sm_failed);
break;
default:
@@ -303,19 +320,14 @@ bfad_sm_failed(struct bfad_s *bfad, enum bfad_sm_event event)
break;
case BFAD_E_STOP:
- if (bfad->bfad_flags & BFAD_CFG_PPORT_DONE)
- bfad_uncfg_pport(bfad);
- if (bfad->bfad_flags & BFAD_FC4_PROBE_DONE) {
- bfad_im_probe_undo(bfad);
- bfad->bfad_flags &= ~BFAD_FC4_PROBE_DONE;
- }
- bfad_stop(bfad);
+ bfa_sm_set_state(bfad, bfad_sm_fcs_exit);
+ bfa_sm_send_event(bfad, BFAD_E_FCS_EXIT_COMP);
break;
case BFAD_E_EXIT_COMP:
bfa_sm_set_state(bfad, bfad_sm_uninit);
bfad_remove_intr(bfad);
- del_timer_sync(&bfad->hal_tmo);
+ timer_delete_sync(&bfad->hal_tmo);
break;
default:
@@ -364,7 +376,7 @@ bfad_sm_stopping(struct bfad_s *bfad, enum bfad_sm_event event)
case BFAD_E_EXIT_COMP:
bfa_sm_set_state(bfad, bfad_sm_uninit);
bfad_remove_intr(bfad);
- del_timer_sync(&bfad->hal_tmo);
+ timer_delete_sync(&bfad->hal_tmo);
bfad_im_probe_undo(bfad);
bfad->bfad_flags &= ~BFAD_FC4_PROBE_DONE;
bfad_uncfg_pport(bfad);
@@ -452,23 +464,6 @@ bfa_fcb_lport_new(struct bfad_s *bfad, struct bfa_fcs_lport_s *port,
return port_drv;
}
-void
-bfa_fcb_lport_delete(struct bfad_s *bfad, enum bfa_lport_role roles,
- struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv)
-{
- struct bfad_port_s *port_drv;
-
- /* this will be only called from rmmod context */
- if (vp_drv && !vp_drv->comp_del) {
- port_drv = (vp_drv) ? (&(vp_drv)->drv_port) :
- ((vf_drv) ? (&(vf_drv)->base_port) :
- (&(bfad)->pport));
- bfa_trc(bfad, roles);
- if (roles & BFA_LPORT_ROLE_FCP_IM)
- bfad_im_port_delete(bfad, port_drv);
- }
-}
-
/*
* FCS RPORT alloc callback, after successful PLOGI by FCS
*/
@@ -501,7 +496,7 @@ bfa_fcb_pbc_vport_create(struct bfad_s *bfad, struct bfi_pbc_vport_s pbc_vport)
struct bfad_vport_s *vport;
int rc;
- vport = kzalloc(sizeof(struct bfad_vport_s), GFP_KERNEL);
+ vport = kzalloc(sizeof(struct bfad_vport_s), GFP_ATOMIC);
if (!vport) {
bfa_trc(bfad, 0);
return;
@@ -527,28 +522,26 @@ bfa_fcb_pbc_vport_create(struct bfad_s *bfad, struct bfi_pbc_vport_s pbc_vport)
void
bfad_hal_mem_release(struct bfad_s *bfad)
{
- int i;
struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
- struct bfa_mem_elem_s *meminfo_elem;
-
- for (i = 0; i < BFA_MEM_TYPE_MAX; i++) {
- meminfo_elem = &hal_meminfo->meminfo[i];
- if (meminfo_elem->kva != NULL) {
- switch (meminfo_elem->mem_type) {
- case BFA_MEM_TYPE_KVA:
- vfree(meminfo_elem->kva);
- break;
- case BFA_MEM_TYPE_DMA:
- dma_free_coherent(&bfad->pcidev->dev,
- meminfo_elem->mem_len,
- meminfo_elem->kva,
- (dma_addr_t) meminfo_elem->dma);
- break;
- default:
- WARN_ON(1);
- break;
- }
- }
+ struct bfa_mem_dma_s *dma_info, *dma_elem;
+ struct bfa_mem_kva_s *kva_info, *kva_elem;
+ struct list_head *dm_qe, *km_qe;
+
+ dma_info = &hal_meminfo->dma_info;
+ kva_info = &hal_meminfo->kva_info;
+
+ /* Iterate through the KVA meminfo queue */
+ list_for_each(km_qe, &kva_info->qe) {
+ kva_elem = (struct bfa_mem_kva_s *) km_qe;
+ vfree(kva_elem->kva);
+ }
+
+ /* Iterate through the DMA meminfo queue */
+ list_for_each(dm_qe, &dma_info->qe) {
+ dma_elem = (struct bfa_mem_dma_s *) dm_qe;
+ dma_free_coherent(&bfad->pcidev->dev,
+ dma_elem->mem_len, dma_elem->kva,
+ (dma_addr_t) dma_elem->dma);
}
memset(hal_meminfo, 0, sizeof(struct bfa_meminfo_s));
@@ -563,15 +556,15 @@ bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg)
bfa_cfg->fwcfg.num_ioim_reqs = num_ios;
if (num_tms > 0)
bfa_cfg->fwcfg.num_tskim_reqs = num_tms;
- if (num_fcxps > 0)
+ if (num_fcxps > 0 && num_fcxps <= BFA_FCXP_MAX)
bfa_cfg->fwcfg.num_fcxp_reqs = num_fcxps;
- if (num_ufbufs > 0)
+ if (num_ufbufs > 0 && num_ufbufs <= BFA_UF_MAX)
bfa_cfg->fwcfg.num_uf_bufs = num_ufbufs;
if (reqq_size > 0)
bfa_cfg->drvcfg.num_reqq_elems = reqq_size;
if (rspq_size > 0)
bfa_cfg->drvcfg.num_rspq_elems = rspq_size;
- if (num_sgpgs > 0)
+ if (num_sgpgs > 0 && num_sgpgs <= BFA_SGPG_MAX)
bfa_cfg->drvcfg.num_sgpgs = num_sgpgs;
/*
@@ -591,85 +584,45 @@ bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg)
bfa_status_t
bfad_hal_mem_alloc(struct bfad_s *bfad)
{
- int i;
struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
- struct bfa_mem_elem_s *meminfo_elem;
- dma_addr_t phys_addr;
- void *kva;
+ struct bfa_mem_dma_s *dma_info, *dma_elem;
+ struct bfa_mem_kva_s *kva_info, *kva_elem;
+ struct list_head *dm_qe, *km_qe;
bfa_status_t rc = BFA_STATUS_OK;
- int retry_count = 0;
- int reset_value = 1;
- int min_num_sgpgs = 512;
+ dma_addr_t phys_addr;
bfa_cfg_get_default(&bfad->ioc_cfg);
-
-retry:
bfad_update_hal_cfg(&bfad->ioc_cfg);
bfad->cfg_data.ioc_queue_depth = bfad->ioc_cfg.fwcfg.num_ioim_reqs;
- bfa_cfg_get_meminfo(&bfad->ioc_cfg, hal_meminfo);
-
- for (i = 0; i < BFA_MEM_TYPE_MAX; i++) {
- meminfo_elem = &hal_meminfo->meminfo[i];
- switch (meminfo_elem->mem_type) {
- case BFA_MEM_TYPE_KVA:
- kva = vmalloc(meminfo_elem->mem_len);
- if (kva == NULL) {
- bfad_hal_mem_release(bfad);
- rc = BFA_STATUS_ENOMEM;
- goto ext;
- }
- memset(kva, 0, meminfo_elem->mem_len);
- meminfo_elem->kva = kva;
- break;
- case BFA_MEM_TYPE_DMA:
- kva = dma_alloc_coherent(&bfad->pcidev->dev,
- meminfo_elem->mem_len, &phys_addr, GFP_KERNEL);
- if (kva == NULL) {
- bfad_hal_mem_release(bfad);
- /*
- * If we cannot allocate with default
- * num_sgpages try with half the value.
- */
- if (num_sgpgs > min_num_sgpgs) {
- printk(KERN_INFO
- "bfad[%d]: memory allocation failed"
- " with num_sgpgs: %d\n",
- bfad->inst_no, num_sgpgs);
- nextLowerInt(&num_sgpgs);
- printk(KERN_INFO
- "bfad[%d]: trying to allocate memory"
- " with num_sgpgs: %d\n",
- bfad->inst_no, num_sgpgs);
- retry_count++;
- goto retry;
- } else {
- if (num_sgpgs_parm > 0)
- num_sgpgs = num_sgpgs_parm;
- else {
- reset_value =
- (1 << retry_count);
- num_sgpgs *= reset_value;
- }
- rc = BFA_STATUS_ENOMEM;
- goto ext;
- }
- }
-
- if (num_sgpgs_parm > 0)
- num_sgpgs = num_sgpgs_parm;
- else {
- reset_value = (1 << retry_count);
- num_sgpgs *= reset_value;
- }
-
- memset(kva, 0, meminfo_elem->mem_len);
- meminfo_elem->kva = kva;
- meminfo_elem->dma = phys_addr;
- break;
- default:
- break;
+ bfa_cfg_get_meminfo(&bfad->ioc_cfg, hal_meminfo, &bfad->bfa);
+
+ dma_info = &hal_meminfo->dma_info;
+ kva_info = &hal_meminfo->kva_info;
+
+ /* Iterate through the KVA meminfo queue */
+ list_for_each(km_qe, &kva_info->qe) {
+ kva_elem = (struct bfa_mem_kva_s *) km_qe;
+ kva_elem->kva = vzalloc(kva_elem->mem_len);
+ if (kva_elem->kva == NULL) {
+ bfad_hal_mem_release(bfad);
+ rc = BFA_STATUS_ENOMEM;
+ goto ext;
+ }
+ }
+ /* Iterate through the DMA meminfo queue */
+ list_for_each(dm_qe, &dma_info->qe) {
+ dma_elem = (struct bfa_mem_dma_s *) dm_qe;
+ dma_elem->kva = dma_alloc_coherent(&bfad->pcidev->dev,
+ dma_elem->mem_len,
+ &phys_addr, GFP_KERNEL);
+ if (dma_elem->kva == NULL) {
+ bfad_hal_mem_release(bfad);
+ rc = BFA_STATUS_ENOMEM;
+ goto ext;
}
+ dma_elem->dma = phys_addr;
+ memset(dma_elem->kva, 0, dma_elem->mem_len);
}
ext:
return rc;
@@ -711,6 +664,7 @@ bfad_vport_create(struct bfad_s *bfad, u16 vf_id,
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_fcs_vport_start(&vport->fcs_vport);
+ list_add_tail(&vport->list_entry, &bfad->vport_list);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
return BFA_STATUS_OK;
@@ -729,9 +683,10 @@ ext:
}
void
-bfad_bfa_tmo(unsigned long data)
+bfad_bfa_tmo(struct timer_list *t)
{
- struct bfad_s *bfad = (struct bfad_s *) data;
+ struct bfad_s *bfad = timer_container_of(bfad, t,
+ hal_tmo);
unsigned long flags;
struct list_head doneq;
@@ -756,9 +711,7 @@ bfad_bfa_tmo(unsigned long data)
void
bfad_init_timer(struct bfad_s *bfad)
{
- init_timer(&bfad->hal_tmo);
- bfad->hal_tmo.function = bfad_bfa_tmo;
- bfad->hal_tmo.data = (unsigned long)bfad;
+ timer_setup(&bfad->hal_tmo, bfad_bfa_tmo, 0);
mod_timer(&bfad->hal_tmo,
jiffies + msecs_to_jiffies(BFA_TIMER_FREQ));
@@ -767,7 +720,7 @@ bfad_init_timer(struct bfad_s *bfad)
int
bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
{
- int rc = -ENODEV;
+ int rc = -ENODEV;
if (pci_enable_device(pdev)) {
printk(KERN_ERR "pci_enable_device fail %p\n", pdev);
@@ -779,17 +732,19 @@ bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
pci_set_master(pdev);
-
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
- printk(KERN_ERR "pci_set_dma_mask fail %p\n", pdev);
- goto out_release_region;
- }
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (rc) {
+ rc = -ENODEV;
+ printk(KERN_ERR "dma_set_mask_and_coherent fail %p\n", pdev);
+ goto out_release_region;
+ }
bfad->pci_bar0_kva = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+ bfad->pci_bar2_kva = pci_iomap(pdev, 2, pci_resource_len(pdev, 2));
if (bfad->pci_bar0_kva == NULL) {
printk(KERN_ERR "Fail to map bar0\n");
+ rc = -ENODEV;
goto out_release_region;
}
@@ -797,6 +752,7 @@ bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
bfad->hal_pcidev.pci_func = PCI_FUNC(pdev->devfn);
bfad->hal_pcidev.pci_bar_kva = bfad->pci_bar0_kva;
bfad->hal_pcidev.device_id = pdev->device;
+ bfad->hal_pcidev.ssid = pdev->subsystem_device;
bfad->pci_name = pci_name(pdev);
bfad->pci_attr.vendor_id = pdev->vendor;
@@ -808,52 +764,25 @@ bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
bfad->pcidev = pdev;
/* Adjust PCIe Maximum Read Request Size */
- if (pcie_max_read_reqsz > 0) {
- int pcie_cap_reg;
- u16 pcie_dev_ctl;
- u16 mask = 0xffff;
-
- switch (pcie_max_read_reqsz) {
- case 128:
- mask = 0x0;
- break;
- case 256:
- mask = 0x1000;
- break;
- case 512:
- mask = 0x2000;
- break;
- case 1024:
- mask = 0x3000;
- break;
- case 2048:
- mask = 0x4000;
- break;
- case 4096:
- mask = 0x5000;
- break;
- default:
- break;
- }
-
- pcie_cap_reg = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- if (mask != 0xffff && pcie_cap_reg) {
- pcie_cap_reg += 0x08;
- pci_read_config_word(pdev, pcie_cap_reg, &pcie_dev_ctl);
- if ((pcie_dev_ctl & 0x7000) != mask) {
- printk(KERN_WARNING "BFA[%s]: "
+ if (pci_is_pcie(pdev) && pcie_max_read_reqsz) {
+ if (pcie_max_read_reqsz >= 128 &&
+ pcie_max_read_reqsz <= 4096 &&
+ is_power_of_2(pcie_max_read_reqsz)) {
+ int max_rq = pcie_get_readrq(pdev);
+ printk(KERN_WARNING "BFA[%s]: "
"pcie_max_read_request_size is %d, "
- "reset to %d\n", bfad->pci_name,
- (1 << ((pcie_dev_ctl & 0x7000) >> 12)) << 7,
+ "reset to %d\n", bfad->pci_name, max_rq,
pcie_max_read_reqsz);
-
- pcie_dev_ctl &= ~0x7000;
- pci_write_config_word(pdev, pcie_cap_reg,
- pcie_dev_ctl | mask);
- }
+ pcie_set_readrq(pdev, pcie_max_read_reqsz);
+ } else {
+ printk(KERN_WARNING "BFA[%s]: invalid "
+ "pcie_max_read_request_size %d ignored\n",
+ bfad->pci_name, pcie_max_read_reqsz);
}
}
+ pci_save_state(pdev);
+
return 0;
out_release_region:
@@ -868,9 +797,9 @@ void
bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad)
{
pci_iounmap(pdev, bfad->pci_bar0_kva);
+ pci_iounmap(pdev, bfad->pci_bar2_kva);
pci_release_regions(pdev);
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
}
bfa_status_t
@@ -889,9 +818,8 @@ bfad_drv_init(struct bfad_s *bfad)
printk(KERN_WARNING "bfad%d bfad_hal_mem_alloc failure\n",
bfad->inst_no);
printk(KERN_WARNING
- "Not enough memory to attach all Brocade HBA ports, %s",
- "System may need more memory.\n");
- goto out_hal_mem_alloc_failure;
+ "Not enough memory to attach all QLogic BR-series HBA ports. System may need more memory.\n");
+ return BFA_STATUS_FAILED;
}
bfad->bfa.trcmod = bfad->trcmod;
@@ -913,29 +841,6 @@ bfad_drv_init(struct bfad_s *bfad)
bfad->bfad_flags |= BFAD_DRV_INIT_DONE;
return BFA_STATUS_OK;
-
-out_hal_mem_alloc_failure:
- return BFA_STATUS_FAILED;
-}
-
-void
-bfad_drv_uninit(struct bfad_s *bfad)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&bfad->bfad_lock, flags);
- init_completion(&bfad->comp);
- bfa_iocfc_stop(&bfad->bfa);
- spin_unlock_irqrestore(&bfad->bfad_lock, flags);
- wait_for_completion(&bfad->comp);
-
- del_timer_sync(&bfad->hal_tmo);
- bfa_isr_disable(&bfad->bfa);
- bfa_detach(&bfad->bfa);
- bfad_remove_intr(bfad);
- bfad_hal_mem_release(bfad);
-
- bfad->bfad_flags &= ~BFAD_DRV_INIT_DONE;
}
void
@@ -945,6 +850,7 @@ bfad_drv_start(struct bfad_s *bfad)
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_iocfc_start(&bfad->bfa);
+ bfa_fcs_pbc_vport_init(&bfad->bfa_fcs);
bfa_fcs_fabric_modstart(&bfad->bfa_fcs);
bfad->bfad_flags |= BFAD_HAL_START_DONE;
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
@@ -1032,47 +938,55 @@ bfad_start_ops(struct bfad_s *bfad) {
struct bfad_vport_s *vport, *vport_new;
struct bfa_fcs_driver_info_s driver_info;
+ /* Limit min/max. xfer size to [64k-32MB] */
+ if (max_xfer_size < BFAD_MIN_SECTORS >> 1)
+ max_xfer_size = BFAD_MIN_SECTORS >> 1;
+ if (max_xfer_size > BFAD_MAX_SECTORS >> 1)
+ max_xfer_size = BFAD_MAX_SECTORS >> 1;
+
/* Fill the driver_info info to fcs*/
memset(&driver_info, 0, sizeof(driver_info));
- strncpy(driver_info.version, BFAD_DRIVER_VERSION,
- sizeof(driver_info.version) - 1);
+ strscpy(driver_info.version, BFAD_DRIVER_VERSION,
+ sizeof(driver_info.version));
if (host_name)
- strncpy(driver_info.host_machine_name, host_name,
- sizeof(driver_info.host_machine_name) - 1);
+ strscpy(driver_info.host_machine_name, host_name,
+ sizeof(driver_info.host_machine_name));
if (os_name)
- strncpy(driver_info.host_os_name, os_name,
- sizeof(driver_info.host_os_name) - 1);
+ strscpy(driver_info.host_os_name, os_name,
+ sizeof(driver_info.host_os_name));
if (os_patch)
- strncpy(driver_info.host_os_patch, os_patch,
- sizeof(driver_info.host_os_patch) - 1);
+ strscpy(driver_info.host_os_patch, os_patch,
+ sizeof(driver_info.host_os_patch));
- strncpy(driver_info.os_device_name, bfad->pci_name,
- sizeof(driver_info.os_device_name - 1));
+ strscpy(driver_info.os_device_name, bfad->pci_name,
+ sizeof(driver_info.os_device_name));
- /* FCS INIT */
+ /* FCS driver info init */
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_fcs_driver_info_init(&bfad->bfa_fcs, &driver_info);
- bfa_fcs_init(&bfad->bfa_fcs);
+
+ if (bfad->bfad_flags & BFAD_CFG_PPORT_DONE)
+ bfa_fcs_update_cfg(&bfad->bfa_fcs);
+ else
+ bfa_fcs_init(&bfad->bfa_fcs);
+
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
- retval = bfad_cfg_pport(bfad, BFA_LPORT_ROLE_FCP_IM);
- if (retval != BFA_STATUS_OK) {
- if (bfa_sm_cmp_state(bfad, bfad_sm_initializing))
- bfa_sm_set_state(bfad, bfad_sm_failed);
- bfad_stop(bfad);
- return BFA_STATUS_FAILED;
+ if (!(bfad->bfad_flags & BFAD_CFG_PPORT_DONE)) {
+ retval = bfad_cfg_pport(bfad, BFA_LPORT_ROLE_FCP_IM);
+ if (retval != BFA_STATUS_OK)
+ return BFA_STATUS_FAILED;
}
+ /* Setup fc host fixed attribute if the lk supports */
+ bfad_fc_host_init(bfad->pport.im_port);
+
/* BFAD level FC4 IM specific resource allocation */
retval = bfad_im_probe(bfad);
if (retval != BFA_STATUS_OK) {
printk(KERN_WARNING "bfad_im_probe failed\n");
if (bfa_sm_cmp_state(bfad, bfad_sm_initializing))
bfa_sm_set_state(bfad, bfad_sm_failed);
- bfad_im_probe_undo(bfad);
- bfad->bfad_flags &= ~BFAD_FC4_PROBE_DONE;
- bfad_uncfg_pport(bfad);
- bfad_stop(bfad);
return BFA_STATUS_FAILED;
} else
bfad->bfad_flags |= BFAD_FC4_PROBE_DONE;
@@ -1124,22 +1038,18 @@ bfad_start_ops(struct bfad_s *bfad) {
int
bfad_worker(void *ptr)
{
- struct bfad_s *bfad;
- unsigned long flags;
-
- bfad = (struct bfad_s *)ptr;
-
- while (!kthread_should_stop()) {
+ struct bfad_s *bfad = ptr;
+ unsigned long flags;
- /* Send event BFAD_E_INIT_SUCCESS */
- bfa_sm_send_event(bfad, BFAD_E_INIT_SUCCESS);
+ if (kthread_should_stop())
+ return 0;
- spin_lock_irqsave(&bfad->bfad_lock, flags);
- bfad->bfad_tsk = NULL;
- spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ /* Send event BFAD_E_INIT_SUCCESS */
+ bfa_sm_send_event(bfad, BFAD_E_INIT_SUCCESS);
- break;
- }
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfad->bfad_tsk = NULL;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
return 0;
}
@@ -1233,8 +1143,8 @@ bfad_install_msix_handler(struct bfad_s *bfad)
for (i = 0; i < bfad->nvec; i++) {
sprintf(bfad->msix_tab[i].name, "bfa-%s-%s",
bfad->pci_name,
- ((bfa_asic_id_ct(bfad->hal_pcidev.device_id)) ?
- msix_name_ct[i] : msix_name_cb[i]));
+ ((bfa_asic_id_cb(bfad->hal_pcidev.device_id)) ?
+ msix_name_cb[i] : msix_name_ct[i]));
error = request_irq(bfad->msix_tab[i].msix.vector,
(irq_handler_t) bfad_msix, 0,
@@ -1248,6 +1158,9 @@ bfad_install_msix_handler(struct bfad_s *bfad)
free_irq(bfad->msix_tab[j].msix.vector,
&bfad->msix_tab[j]);
+ bfad->bfad_flags &= ~BFAD_MSIX_ON;
+ pci_disable_msix(bfad->pcidev);
+
return 1;
}
}
@@ -1261,10 +1174,11 @@ bfad_install_msix_handler(struct bfad_s *bfad)
int
bfad_setup_intr(struct bfad_s *bfad)
{
- int error = 0;
+ int error;
u32 mask = 0, i, num_bit = 0, max_bit = 0;
struct msix_entry msix_entries[MAX_MSIX_ENTRY];
struct pci_dev *pdev = bfad->pcidev;
+ u16 reg;
/* Call BFA to get the msix map for this PCI function. */
bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit);
@@ -1272,28 +1186,36 @@ bfad_setup_intr(struct bfad_s *bfad)
/* Set up the msix entry table */
bfad_init_msix_entry(bfad, msix_entries, mask, max_bit);
- if ((bfa_asic_id_ct(pdev->device) && !msix_disable_ct) ||
- (!bfa_asic_id_ct(pdev->device) && !msix_disable_cb)) {
+ if ((bfa_asic_id_ctc(pdev->device) && !msix_disable_ct) ||
+ (bfa_asic_id_cb(pdev->device) && !msix_disable_cb)) {
+
+ error = pci_enable_msix_exact(bfad->pcidev,
+ msix_entries, bfad->nvec);
+ /* In CT1 & CT2, try to allocate just one vector */
+ if (error == -ENOSPC && bfa_asic_id_ctc(pdev->device)) {
+ printk(KERN_WARNING "bfa %s: trying one msix "
+ "vector failed to allocate %d[%d]\n",
+ bfad->pci_name, bfad->nvec, error);
+ bfad->nvec = 1;
+ error = pci_enable_msix_exact(bfad->pcidev,
+ msix_entries, 1);
+ }
- error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec);
if (error) {
- /*
- * Only error number of vector is available.
- * We don't have a mechanism to map multiple
- * interrupts into one vector, so even if we
- * can try to request less vectors, we don't
- * know how to associate interrupt events to
- * vectors. Linux doesn't duplicate vectors
- * in the MSIX table for this case.
- */
-
printk(KERN_WARNING "bfad%d: "
- "pci_enable_msix failed (%d),"
- " use line based.\n", bfad->inst_no, error);
-
+ "pci_enable_msix_exact failed (%d), "
+ "use line based.\n",
+ bfad->inst_no, error);
goto line_based;
}
+ /* Disable INTX in MSI-X mode */
+ pci_read_config_word(pdev, PCI_COMMAND, &reg);
+
+ if (!(reg & PCI_COMMAND_INTX_DISABLE))
+ pci_write_config_word(pdev, PCI_COMMAND,
+ reg | PCI_COMMAND_INTX_DISABLE);
+
/* Save the vectors */
for (i = 0; i < bfad->nvec; i++) {
bfa_trc(bfad, msix_entries[i].vector);
@@ -1304,19 +1226,18 @@ bfad_setup_intr(struct bfad_s *bfad)
bfad->bfad_flags |= BFAD_MSIX_ON;
- return error;
+ return 0;
}
line_based:
- error = 0;
- if (request_irq
- (bfad->pcidev->irq, (irq_handler_t) bfad_intx, BFAD_IRQ_FLAGS,
- BFAD_DRIVER_NAME, bfad) != 0) {
- /* Enable interrupt handler failed */
- return 1;
- }
+ error = request_irq(bfad->pcidev->irq, (irq_handler_t)bfad_intx,
+ BFAD_IRQ_FLAGS, BFAD_DRIVER_NAME, bfad);
+ if (error)
+ return error;
- return error;
+ bfad->bfad_flags |= BFAD_INTX_ON;
+
+ return 0;
}
void
@@ -1331,7 +1252,7 @@ bfad_remove_intr(struct bfad_s *bfad)
pci_disable_msix(bfad->pcidev);
bfad->bfad_flags &= ~BFAD_MSIX_ON;
- } else {
+ } else if (bfad->bfad_flags & BFAD_INTX_ON) {
free_irq(bfad->pcidev->irq, bfad);
}
}
@@ -1343,7 +1264,7 @@ int
bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct bfad_s *bfad;
- int error = -ENODEV, retval;
+ int error = -ENODEV, retval, i;
/* For single port cards - only claim function 0 */
if ((pdev->device == BFA_PCI_DEVICE_ID_FC_8G1P) &&
@@ -1367,6 +1288,12 @@ bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
bfa_trc_init(bfad->trcmod);
bfa_trc(bfad, bfad_inst);
+ /* AEN INIT */
+ INIT_LIST_HEAD(&bfad->free_aen_q);
+ INIT_LIST_HEAD(&bfad->active_aen_q);
+ for (i = 0; i < BFA_AEN_MAX_ENTRY; i++)
+ list_add_tail(&bfad->aen_list[i].qe, &bfad->free_aen_q);
+
if (!(bfad_load_fwimg(pdev))) {
kfree(bfad->trcmod);
goto out_alloc_trace_failure;
@@ -1388,11 +1315,14 @@ bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
bfa_sm_set_state(bfad, bfad_sm_uninit);
spin_lock_init(&bfad->bfad_lock);
+ spin_lock_init(&bfad->bfad_aen_spinlock);
+
pci_set_drvdata(pdev, bfad);
bfad->ref_count = 0;
bfad->pport.bfad = bfad;
INIT_LIST_HEAD(&bfad->pbc_vport_list);
+ INIT_LIST_HEAD(&bfad->vport_list);
/* Setup the debugfs node for this bfad */
if (bfa_debugfs_enable)
@@ -1410,7 +1340,6 @@ bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
return 0;
out_bfad_sm_failure:
- bfa_detach(&bfad->bfa);
bfad_hal_mem_release(bfad);
out_drv_init_failure:
/* Remove the debugfs node for this bfad */
@@ -1472,6 +1401,193 @@ bfad_pci_remove(struct pci_dev *pdev)
kfree(bfad);
}
+/*
+ * PCI Error Recovery entry, error detected.
+ */
+static pci_ers_result_t
+bfad_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct bfad_s *bfad = pci_get_drvdata(pdev);
+ unsigned long flags;
+ pci_ers_result_t ret = PCI_ERS_RESULT_NONE;
+
+ dev_printk(KERN_ERR, &pdev->dev,
+ "error detected state: %d - flags: 0x%x\n",
+ state, bfad->bfad_flags);
+
+ switch (state) {
+ case pci_channel_io_normal: /* non-fatal error */
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfad->bfad_flags &= ~BFAD_EEH_BUSY;
+ /* Suspend/fail all bfa operations */
+ bfa_ioc_suspend(&bfad->bfa.ioc);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ timer_delete_sync(&bfad->hal_tmo);
+ ret = PCI_ERS_RESULT_CAN_RECOVER;
+ break;
+ case pci_channel_io_frozen: /* fatal error */
+ init_completion(&bfad->comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfad->bfad_flags |= BFAD_EEH_BUSY;
+ /* Suspend/fail all bfa operations */
+ bfa_ioc_suspend(&bfad->bfa.ioc);
+ bfa_fcs_stop(&bfad->bfa_fcs);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ wait_for_completion(&bfad->comp);
+
+ bfad_remove_intr(bfad);
+ timer_delete_sync(&bfad->hal_tmo);
+ pci_disable_device(pdev);
+ ret = PCI_ERS_RESULT_NEED_RESET;
+ break;
+ case pci_channel_io_perm_failure: /* PCI Card is DEAD */
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfad->bfad_flags |= BFAD_EEH_BUSY |
+ BFAD_EEH_PCI_CHANNEL_IO_PERM_FAILURE;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ /* If the error_detected handler is called with the reason
+ * pci_channel_io_perm_failure - it will subsequently call
+ * pci_remove() entry point to remove the pci device from the
+ * system - So defer the cleanup to pci_remove(); cleaning up
+ * here causes inconsistent state during pci_remove().
+ */
+ ret = PCI_ERS_RESULT_DISCONNECT;
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ return ret;
+}
+
+static int restart_bfa(struct bfad_s *bfad)
+{
+ unsigned long flags;
+ struct pci_dev *pdev = bfad->pcidev;
+
+ bfa_attach(&bfad->bfa, bfad, &bfad->ioc_cfg,
+ &bfad->meminfo, &bfad->hal_pcidev);
+
+ /* Enable Interrupt and wait bfa_init completion */
+ if (bfad_setup_intr(bfad)) {
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "%s: bfad_setup_intr failed\n", bfad->pci_name);
+ bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
+ return -1;
+ }
+
+ init_completion(&bfad->comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_iocfc_init(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ /* Set up interrupt handler for each vectors */
+ if ((bfad->bfad_flags & BFAD_MSIX_ON) &&
+ bfad_install_msix_handler(bfad))
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "%s: install_msix failed.\n", bfad->pci_name);
+
+ bfad_init_timer(bfad);
+ wait_for_completion(&bfad->comp);
+ bfad_drv_start(bfad);
+
+ return 0;
+}
+
+/*
+ * PCI Error Recovery entry, re-initialize the chip.
+ */
+static pci_ers_result_t
+bfad_pci_slot_reset(struct pci_dev *pdev)
+{
+ struct bfad_s *bfad = pci_get_drvdata(pdev);
+ u8 byte;
+ int rc;
+
+ dev_printk(KERN_ERR, &pdev->dev,
+ "bfad_pci_slot_reset flags: 0x%x\n", bfad->bfad_flags);
+
+ if (pci_enable_device(pdev)) {
+ dev_printk(KERN_ERR, &pdev->dev, "Cannot re-enable "
+ "PCI device after reset.\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_restore_state(pdev);
+
+ /*
+ * Read some byte (e.g. DMA max. payload size which can't
+ * be 0xff any time) to make sure - we did not hit another PCI error
+ * in the middle of recovery. If we did, then declare permanent failure.
+ */
+ pci_read_config_byte(pdev, 0x68, &byte);
+ if (byte == 0xff) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "slot_reset failed ... got another PCI error !\n");
+ goto out_disable_device;
+ }
+
+ pci_set_master(pdev);
+
+ rc = dma_set_mask_and_coherent(&bfad->pcidev->dev, DMA_BIT_MASK(64));
+ if (rc)
+ goto out_disable_device;
+
+ if (restart_bfa(bfad) == -1)
+ goto out_disable_device;
+
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "slot_reset completed flags: 0x%x!\n", bfad->bfad_flags);
+
+ return PCI_ERS_RESULT_RECOVERED;
+
+out_disable_device:
+ pci_disable_device(pdev);
+ return PCI_ERS_RESULT_DISCONNECT;
+}
+
+static pci_ers_result_t
+bfad_pci_mmio_enabled(struct pci_dev *pdev)
+{
+ unsigned long flags;
+ struct bfad_s *bfad = pci_get_drvdata(pdev);
+
+ dev_printk(KERN_INFO, &pdev->dev, "mmio_enabled\n");
+
+ /* Fetch FW diagnostic information */
+ bfa_ioc_debug_save_ftrc(&bfad->bfa.ioc);
+
+ /* Cancel all pending IOs */
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ init_completion(&bfad->comp);
+ bfa_fcs_stop(&bfad->bfa_fcs);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ wait_for_completion(&bfad->comp);
+
+ bfad_remove_intr(bfad);
+ timer_delete_sync(&bfad->hal_tmo);
+ pci_disable_device(pdev);
+
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static void
+bfad_pci_resume(struct pci_dev *pdev)
+{
+ unsigned long flags;
+ struct bfad_s *bfad = pci_get_drvdata(pdev);
+
+ dev_printk(KERN_WARNING, &pdev->dev, "resume\n");
+
+ /* wait until the link is online */
+ bfad_rport_online_wait(bfad);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfad->bfad_flags &= ~BFAD_EEH_BUSY;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
struct pci_device_id bfad_id_table[] = {
{
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
@@ -1501,17 +1617,44 @@ struct pci_device_id bfad_id_table[] = {
.class = (PCI_CLASS_SERIAL_FIBER << 8),
.class_mask = ~0,
},
+ {
+ .vendor = BFA_PCI_VENDOR_ID_BROCADE,
+ .device = BFA_PCI_DEVICE_ID_CT2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = (PCI_CLASS_SERIAL_FIBER << 8),
+ .class_mask = ~0,
+ },
+ {
+ .vendor = BFA_PCI_VENDOR_ID_BROCADE,
+ .device = BFA_PCI_DEVICE_ID_CT2_QUAD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = (PCI_CLASS_SERIAL_FIBER << 8),
+ .class_mask = ~0,
+ },
{0, 0},
};
MODULE_DEVICE_TABLE(pci, bfad_id_table);
+/*
+ * PCI error recovery handlers.
+ */
+static const struct pci_error_handlers bfad_err_handler = {
+ .error_detected = bfad_pci_error_detected,
+ .slot_reset = bfad_pci_slot_reset,
+ .mmio_enabled = bfad_pci_mmio_enabled,
+ .resume = bfad_pci_resume,
+};
+
static struct pci_driver bfad_pci_driver = {
.name = BFAD_DRIVER_NAME,
.id_table = bfad_id_table,
.probe = bfad_pci_probe,
- .remove = __devexit_p(bfad_pci_remove),
+ .remove = bfad_pci_remove,
+ .err_handler = &bfad_err_handler,
};
/*
@@ -1522,7 +1665,7 @@ bfad_init(void)
{
int error = 0;
- printk(KERN_INFO "Brocade BFA FC/FCOE SCSI driver - version: %s\n",
+ pr_info("QLogic BR-series BFA FC/FCOE SCSI driver - version: %s\n",
BFAD_DRIVER_VERSION);
if (num_sgpgs > 0)
@@ -1530,9 +1673,8 @@ bfad_init(void)
error = bfad_im_module_init();
if (error) {
- error = -ENOMEM;
printk(KERN_WARNING "bfad_im_module_init failure\n");
- goto ext;
+ return -ENOMEM;
}
if (strcmp(FCPI_NAME, " fcpim") == 0)
@@ -1540,6 +1682,7 @@ bfad_init(void)
bfa_auto_recover = ioc_auto_recover;
bfa_fcs_rport_set_del_timeout(rport_del_timeout);
+ bfa_fcs_rport_set_max_logins(max_rport_logins);
error = pci_register_driver(&bfad_pci_driver);
if (error) {
@@ -1594,38 +1737,40 @@ out:
static u32 *
bfad_load_fwimg(struct pci_dev *pdev)
{
- if (pdev->device == BFA_PCI_DEVICE_ID_CT_FC) {
- if (bfi_image_ct_fc_size == 0)
- bfad_read_firmware(pdev, &bfi_image_ct_fc,
- &bfi_image_ct_fc_size, BFAD_FW_FILE_CT_FC);
- return bfi_image_ct_fc;
- } else if (pdev->device == BFA_PCI_DEVICE_ID_CT) {
- if (bfi_image_ct_cna_size == 0)
- bfad_read_firmware(pdev, &bfi_image_ct_cna,
- &bfi_image_ct_cna_size, BFAD_FW_FILE_CT_CNA);
- return bfi_image_ct_cna;
- } else {
- if (bfi_image_cb_fc_size == 0)
- bfad_read_firmware(pdev, &bfi_image_cb_fc,
- &bfi_image_cb_fc_size, BFAD_FW_FILE_CB_FC);
- return bfi_image_cb_fc;
+ if (bfa_asic_id_ct2(pdev->device)) {
+ if (bfi_image_ct2_size == 0)
+ bfad_read_firmware(pdev, &bfi_image_ct2,
+ &bfi_image_ct2_size, BFAD_FW_FILE_CT2);
+ return bfi_image_ct2;
+ } else if (bfa_asic_id_ct(pdev->device)) {
+ if (bfi_image_ct_size == 0)
+ bfad_read_firmware(pdev, &bfi_image_ct,
+ &bfi_image_ct_size, BFAD_FW_FILE_CT);
+ return bfi_image_ct;
+ } else if (bfa_asic_id_cb(pdev->device)) {
+ if (bfi_image_cb_size == 0)
+ bfad_read_firmware(pdev, &bfi_image_cb,
+ &bfi_image_cb_size, BFAD_FW_FILE_CB);
+ return bfi_image_cb;
}
+
+ return NULL;
}
static void
bfad_free_fwimg(void)
{
- if (bfi_image_ct_fc_size && bfi_image_ct_fc)
- vfree(bfi_image_ct_fc);
- if (bfi_image_ct_cna_size && bfi_image_ct_cna)
- vfree(bfi_image_ct_cna);
- if (bfi_image_cb_fc_size && bfi_image_cb_fc)
- vfree(bfi_image_cb_fc);
+ if (bfi_image_ct2_size && bfi_image_ct2)
+ vfree(bfi_image_ct2);
+ if (bfi_image_ct_size && bfi_image_ct)
+ vfree(bfi_image_ct);
+ if (bfi_image_cb_size && bfi_image_cb)
+ vfree(bfi_image_cb);
}
module_init(bfad_init);
module_exit(bfad_exit);
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Brocade Fibre Channel HBA Driver" BFAD_PROTO_NAME);
-MODULE_AUTHOR("Brocade Communications Systems, Inc.");
+MODULE_DESCRIPTION("QLogic BR-series Fibre Channel HBA Driver" BFAD_PROTO_NAME);
+MODULE_AUTHOR("QLogic Corporation");
MODULE_VERSION(BFAD_DRIVER_VERSION);
diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c
index a94ea4235433..54bc1539e1e9 100644
--- a/drivers/scsi/bfa/bfad_attr.c
+++ b/drivers/scsi/bfa/bfad_attr.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -218,6 +211,9 @@ bfad_im_get_host_speed(struct Scsi_Host *shost)
case BFA_PORT_SPEED_10GBPS:
fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
break;
+ case BFA_PORT_SPEED_16GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_16GBIT;
+ break;
case BFA_PORT_SPEED_8GBPS:
fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
break;
@@ -279,8 +275,10 @@ bfad_im_get_stats(struct Scsi_Host *shost)
rc = bfa_port_get_stats(BFA_FCPORT(&bfad->bfa),
fcstats, bfad_hcb_comp, &fcomp);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
- if (rc != BFA_STATUS_OK)
+ if (rc != BFA_STATUS_OK) {
+ kfree(fcstats);
return NULL;
+ }
wait_for_completion(&fcomp.comp);
@@ -332,23 +330,10 @@ bfad_im_reset_stats(struct Scsi_Host *shost)
}
/*
- * FC transport template entry, get rport loss timeout.
- */
-static void
-bfad_im_get_rport_loss_tmo(struct fc_rport *rport)
-{
- struct bfad_itnim_data_s *itnim_data = rport->dd_data;
- struct bfad_itnim_s *itnim = itnim_data->itnim;
- struct bfad_s *bfad = itnim->im->bfad;
- unsigned long flags;
-
- spin_lock_irqsave(&bfad->bfad_lock, flags);
- rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa);
- spin_unlock_irqrestore(&bfad->bfad_lock, flags);
-}
-
-/*
* FC transport template entry, set rport loss timeout.
+ * Update dev_loss_tmo based on the value pushed down by the stack
+ * In case it is lesser than path_tov of driver, set it to path_tov + 1
+ * to ensure that the driver times out before the application
*/
static void
bfad_im_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
@@ -356,15 +341,11 @@ bfad_im_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
struct bfad_itnim_data_s *itnim_data = rport->dd_data;
struct bfad_itnim_s *itnim = itnim_data->itnim;
struct bfad_s *bfad = itnim->im->bfad;
- unsigned long flags;
-
- if (timeout > 0) {
- spin_lock_irqsave(&bfad->bfad_lock, flags);
- bfa_fcpim_path_tov_set(&bfad->bfa, timeout);
- rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa);
- spin_unlock_irqrestore(&bfad->bfad_lock, flags);
- }
+ uint16_t path_tov = bfa_fcpim_path_tov_get(&bfad->bfa);
+ rport->dev_loss_tmo = timeout;
+ if (timeout < path_tov)
+ rport->dev_loss_tmo = path_tov + 1;
}
static int
@@ -423,6 +404,23 @@ bfad_im_vport_create(struct fc_vport *fc_vport, bool disable)
vshost = vport->drv_port.im_port->shost;
fc_host_node_name(vshost) = wwn_to_u64((u8 *)&port_cfg.nwwn);
fc_host_port_name(vshost) = wwn_to_u64((u8 *)&port_cfg.pwwn);
+ fc_host_supported_classes(vshost) = FC_COS_CLASS3;
+
+ memset(fc_host_supported_fc4s(vshost), 0,
+ sizeof(fc_host_supported_fc4s(vshost)));
+
+ /* For FCP type 0x08 */
+ if (supported_fc4s & BFA_LPORT_ROLE_FCP_IM)
+ fc_host_supported_fc4s(vshost)[2] = 1;
+
+ /* For fibre channel services type 0x20 */
+ fc_host_supported_fc4s(vshost)[7] = 1;
+
+ fc_host_supported_speeds(vshost) =
+ bfad_im_supported_speeds(&bfad->bfa);
+ fc_host_maxframe_size(vshost) =
+ bfa_fcport_get_maxfrsize(&bfad->bfa);
+
fc_vport->dd_data = vport;
vport->drv_port.im_port->fc_vport = fc_vport;
} else if (rc == BFA_STATUS_INVALID_WWN)
@@ -440,13 +438,49 @@ bfad_im_vport_create(struct fc_vport *fc_vport, bool disable)
}
static int
+bfad_im_issue_fc_host_lip(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+ uint32_t status;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ status = bfa_port_disable(&bfad->bfa.modules.port,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (status != BFA_STATUS_OK)
+ return -EIO;
+
+ wait_for_completion(&fcomp.comp);
+ if (fcomp.status != BFA_STATUS_OK)
+ return -EIO;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ status = bfa_port_enable(&bfad->bfa.modules.port,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (status != BFA_STATUS_OK)
+ return -EIO;
+
+ wait_for_completion(&fcomp.comp);
+ if (fcomp.status != BFA_STATUS_OK)
+ return -EIO;
+
+ return 0;
+}
+
+static int
bfad_im_vport_delete(struct fc_vport *fc_vport)
{
struct bfad_vport_s *vport = (struct bfad_vport_s *)fc_vport->dd_data;
struct bfad_im_port_s *im_port =
(struct bfad_im_port_s *) vport->drv_port.im_port;
struct bfad_s *bfad = im_port->bfad;
- struct bfad_port_s *port;
struct bfa_fcs_vport_s *fcs_vport;
struct Scsi_Host *vshost;
wwn_t pwwn;
@@ -454,10 +488,12 @@ bfad_im_vport_delete(struct fc_vport *fc_vport)
unsigned long flags;
struct completion fcomp;
- if (im_port->flags & BFAD_PORT_DELETE)
- goto free_scsi_host;
-
- port = im_port->port;
+ if (im_port->flags & BFAD_PORT_DELETE) {
+ bfad_scsi_host_free(bfad, im_port);
+ list_del(&vport->list_entry);
+ kfree(vport);
+ return 0;
+ }
vshost = vport->drv_port.im_port->shost;
u64_to_wwn(fc_host_port_name(vshost), (u8 *)&pwwn);
@@ -486,9 +522,8 @@ bfad_im_vport_delete(struct fc_vport *fc_vport)
wait_for_completion(vport->comp_del);
-free_scsi_host:
bfad_scsi_host_free(bfad, im_port);
-
+ list_del(&vport->list_entry);
kfree(vport);
return 0;
@@ -527,6 +562,34 @@ bfad_im_vport_disable(struct fc_vport *fc_vport, bool disable)
return 0;
}
+static void
+bfad_im_vport_set_symbolic_name(struct fc_vport *fc_vport)
+{
+ struct bfad_vport_s *vport = (struct bfad_vport_s *)fc_vport->dd_data;
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *)vport->drv_port.im_port;
+ struct bfad_s *bfad = im_port->bfad;
+ struct Scsi_Host *vshost = vport->drv_port.im_port->shost;
+ char *sym_name = fc_vport->symbolic_name;
+ struct bfa_fcs_vport_s *fcs_vport;
+ wwn_t pwwn;
+ unsigned long flags;
+
+ u64_to_wwn(fc_host_port_name(vshost), (u8 *)&pwwn);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs, 0, pwwn);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (fcs_vport == NULL)
+ return;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (strlen(sym_name) > 0)
+ bfa_fcs_lport_set_symname(&fcs_vport->lport, sym_name);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
struct fc_function_template bfad_im_fc_function_template = {
/* Target dynamic attributes */
@@ -574,12 +637,14 @@ struct fc_function_template bfad_im_fc_function_template = {
.show_rport_maxframe_size = 1,
.show_rport_supported_classes = 1,
.show_rport_dev_loss_tmo = 1,
- .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo,
.set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo,
-
+ .issue_fc_host_lip = bfad_im_issue_fc_host_lip,
.vport_create = bfad_im_vport_create,
.vport_delete = bfad_im_vport_delete,
.vport_disable = bfad_im_vport_disable,
+ .set_vport_symbolic_name = bfad_im_vport_set_symbolic_name,
+ .bsg_request = bfad_im_bsg_request,
+ .bsg_timeout = bfad_im_bsg_timeout,
};
struct fc_function_template bfad_im_vport_fc_function_template = {
@@ -629,7 +694,6 @@ struct fc_function_template bfad_im_vport_fc_function_template = {
.show_rport_maxframe_size = 1,
.show_rport_supported_classes = 1,
.show_rport_dev_loss_tmo = 1,
- .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo,
.set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo,
};
@@ -647,7 +711,7 @@ bfad_im_serial_num_show(struct device *dev, struct device_attribute *attr,
char serial_num[BFA_ADAPTER_SERIAL_NUM_LEN];
bfa_get_adapter_serial_num(&bfad->bfa, serial_num);
- return snprintf(buf, PAGE_SIZE, "%s\n", serial_num);
+ return sysfs_emit(buf, "%s\n", serial_num);
}
static ssize_t
@@ -661,7 +725,7 @@ bfad_im_model_show(struct device *dev, struct device_attribute *attr,
char model[BFA_ADAPTER_MODEL_NAME_LEN];
bfa_get_adapter_model(&bfad->bfa, model);
- return snprintf(buf, PAGE_SIZE, "%s\n", model);
+ return sysfs_emit(buf, "%s\n", model);
}
static ssize_t
@@ -674,52 +738,74 @@ bfad_im_model_desc_show(struct device *dev, struct device_attribute *attr,
struct bfad_s *bfad = im_port->bfad;
char model[BFA_ADAPTER_MODEL_NAME_LEN];
char model_descr[BFA_ADAPTER_MODEL_DESCR_LEN];
+ int nports = 0;
bfa_get_adapter_model(&bfad->bfa, model);
- if (!strcmp(model, "Brocade-425"))
+ nports = bfa_get_nports(&bfad->bfa);
+ if (!strcmp(model, "QLogic-425"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 4Gbps PCIe dual port FC HBA");
- else if (!strcmp(model, "Brocade-825"))
+ "QLogic BR-series 4Gbps PCIe dual port FC HBA");
+ else if (!strcmp(model, "QLogic-825"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 8Gbps PCIe dual port FC HBA");
- else if (!strcmp(model, "Brocade-42B"))
+ "QLogic BR-series 8Gbps PCIe dual port FC HBA");
+ else if (!strcmp(model, "QLogic-42B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "HP 4Gbps PCIe dual port FC HBA");
- else if (!strcmp(model, "Brocade-82B"))
+ "QLogic BR-series 4Gbps PCIe dual port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-82B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "HP 8Gbps PCIe dual port FC HBA");
- else if (!strcmp(model, "Brocade-1010"))
+ "QLogic BR-series 8Gbps PCIe dual port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-1010"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps single port CNA");
- else if (!strcmp(model, "Brocade-1020"))
+ "QLogic BR-series 10Gbps single port CNA");
+ else if (!strcmp(model, "QLogic-1020"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps dual port CNA");
- else if (!strcmp(model, "Brocade-1007"))
+ "QLogic BR-series 10Gbps dual port CNA");
+ else if (!strcmp(model, "QLogic-1007"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps CNA");
- else if (!strcmp(model, "Brocade-415"))
+ "QLogic BR-series 10Gbps CNA for IBM Blade Center");
+ else if (!strcmp(model, "QLogic-415"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 4Gbps PCIe single port FC HBA");
- else if (!strcmp(model, "Brocade-815"))
+ "QLogic BR-series 4Gbps PCIe single port FC HBA");
+ else if (!strcmp(model, "QLogic-815"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 8Gbps PCIe single port FC HBA");
- else if (!strcmp(model, "Brocade-41B"))
+ "QLogic BR-series 8Gbps PCIe single port FC HBA");
+ else if (!strcmp(model, "QLogic-41B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "HP 4Gbps PCIe single port FC HBA");
- else if (!strcmp(model, "Brocade-81B"))
+ "QLogic BR-series 4Gbps PCIe single port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-81B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "HP 8Gbps PCIe single port FC HBA");
- else if (!strcmp(model, "Brocade-804"))
+ "QLogic BR-series 8Gbps PCIe single port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-804"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "HP Bladesystem C-class 8Gbps FC HBA");
- else if (!strcmp(model, "Brocade-902"))
+ "QLogic BR-series 8Gbps FC HBA for HP Bladesystem C-class");
+ else if (!strcmp(model, "QLogic-1741"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps CNA");
- else
+ "QLogic BR-series 10Gbps CNA for Dell M-Series Blade Servers");
+ else if (strstr(model, "QLogic-1860")) {
+ if (nports == 1 && bfa_ioc_is_cna(&bfad->bfa.ioc))
+ snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
+ "QLogic BR-series 10Gbps single port CNA");
+ else if (nports == 1 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
+ snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
+ "QLogic BR-series 16Gbps PCIe single port FC HBA");
+ else if (nports == 2 && bfa_ioc_is_cna(&bfad->bfa.ioc))
+ snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
+ "QLogic BR-series 10Gbps dual port CNA");
+ else if (nports == 2 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
+ snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
+ "QLogic BR-series 16Gbps PCIe dual port FC HBA");
+ } else if (!strcmp(model, "QLogic-1867")) {
+ if (nports == 1 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
+ snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
+ "QLogic BR-series 16Gbps PCIe single port FC HBA for IBM");
+ else if (nports == 2 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
+ snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
+ "QLogic BR-series 16Gbps PCIe dual port FC HBA for IBM");
+ } else
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
"Invalid Model");
- return snprintf(buf, PAGE_SIZE, "%s\n", model_descr);
+ return sysfs_emit(buf, "%s\n", model_descr);
}
static ssize_t
@@ -733,7 +819,7 @@ bfad_im_node_name_show(struct device *dev, struct device_attribute *attr,
u64 nwwn;
nwwn = bfa_fcs_lport_get_nwwn(port->fcs_port);
- return snprintf(buf, PAGE_SIZE, "0x%llx\n", cpu_to_be64(nwwn));
+ return sysfs_emit(buf, "0x%llx\n", cpu_to_be64(nwwn));
}
static ssize_t
@@ -748,9 +834,9 @@ bfad_im_symbolic_name_show(struct device *dev, struct device_attribute *attr,
char symname[BFA_SYMNAME_MAXLEN];
bfa_fcs_lport_get_attr(&bfad->bfa_fcs.fabric.bport, &port_attr);
- strncpy(symname, port_attr.port_cfg.sym_name.symname,
+ strscpy(symname, port_attr.port_cfg.sym_name.symname,
BFA_SYMNAME_MAXLEN);
- return snprintf(buf, PAGE_SIZE, "%s\n", symname);
+ return sysfs_emit(buf, "%s\n", symname);
}
static ssize_t
@@ -764,14 +850,7 @@ bfad_im_hw_version_show(struct device *dev, struct device_attribute *attr,
char hw_ver[BFA_VERSION_LEN];
bfa_get_pci_chip_rev(&bfad->bfa, hw_ver);
- return snprintf(buf, PAGE_SIZE, "%s\n", hw_ver);
-}
-
-static ssize_t
-bfad_im_drv_version_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_VERSION);
+ return sysfs_emit(buf, "%s\n", hw_ver);
}
static ssize_t
@@ -785,7 +864,7 @@ bfad_im_optionrom_version_show(struct device *dev,
char optrom_ver[BFA_VERSION_LEN];
bfa_get_adapter_optrom_ver(&bfad->bfa, optrom_ver);
- return snprintf(buf, PAGE_SIZE, "%s\n", optrom_ver);
+ return sysfs_emit(buf, "%s\n", optrom_ver);
}
static ssize_t
@@ -799,7 +878,7 @@ bfad_im_fw_version_show(struct device *dev, struct device_attribute *attr,
char fw_ver[BFA_VERSION_LEN];
bfa_get_adapter_fw_ver(&bfad->bfa, fw_ver);
- return snprintf(buf, PAGE_SIZE, "%s\n", fw_ver);
+ return sysfs_emit(buf, "%s\n", fw_ver);
}
static ssize_t
@@ -811,18 +890,11 @@ bfad_im_num_of_ports_show(struct device *dev, struct device_attribute *attr,
(struct bfad_im_port_s *) shost->hostdata[0];
struct bfad_s *bfad = im_port->bfad;
- return snprintf(buf, PAGE_SIZE, "%d\n",
+ return sysfs_emit(buf, "%d\n",
bfa_get_nports(&bfad->bfa));
}
static ssize_t
-bfad_im_drv_name_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_NAME);
-}
-
-static ssize_t
bfad_im_num_of_discovered_ports_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -832,19 +904,20 @@ bfad_im_num_of_discovered_ports_show(struct device *dev,
struct bfad_port_s *port = im_port->port;
struct bfad_s *bfad = im_port->bfad;
int nrports = 2048;
- wwn_t *rports = NULL;
+ struct bfa_rport_qualifier_s *rports = NULL;
unsigned long flags;
- rports = kzalloc(sizeof(wwn_t) * nrports , GFP_ATOMIC);
+ rports = kcalloc(nrports, sizeof(struct bfa_rport_qualifier_s),
+ GFP_ATOMIC);
if (rports == NULL)
- return snprintf(buf, PAGE_SIZE, "Failed\n");
+ return sysfs_emit(buf, "Failed\n");
spin_lock_irqsave(&bfad->bfad_lock, flags);
- bfa_fcs_lport_get_rports(port->fcs_port, rports, &nrports);
+ bfa_fcs_lport_get_rport_quals(port->fcs_port, rports, &nrports);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
kfree(rports);
- return snprintf(buf, PAGE_SIZE, "%d\n", nrports);
+ return sysfs_emit(buf, "%d\n", nrports);
}
static DEVICE_ATTR(serial_number, S_IRUGO,
@@ -857,48 +930,64 @@ static DEVICE_ATTR(symbolic_name, S_IRUGO,
bfad_im_symbolic_name_show, NULL);
static DEVICE_ATTR(hardware_version, S_IRUGO,
bfad_im_hw_version_show, NULL);
-static DEVICE_ATTR(driver_version, S_IRUGO,
- bfad_im_drv_version_show, NULL);
+static DEVICE_STRING_ATTR_RO(driver_version, S_IRUGO,
+ BFAD_DRIVER_VERSION);
static DEVICE_ATTR(option_rom_version, S_IRUGO,
bfad_im_optionrom_version_show, NULL);
static DEVICE_ATTR(firmware_version, S_IRUGO,
bfad_im_fw_version_show, NULL);
static DEVICE_ATTR(number_of_ports, S_IRUGO,
bfad_im_num_of_ports_show, NULL);
-static DEVICE_ATTR(driver_name, S_IRUGO, bfad_im_drv_name_show, NULL);
+static DEVICE_STRING_ATTR_RO(driver_name, S_IRUGO, BFAD_DRIVER_NAME);
static DEVICE_ATTR(number_of_discovered_ports, S_IRUGO,
bfad_im_num_of_discovered_ports_show, NULL);
-struct device_attribute *bfad_im_host_attrs[] = {
- &dev_attr_serial_number,
- &dev_attr_model,
- &dev_attr_model_description,
- &dev_attr_node_name,
- &dev_attr_symbolic_name,
- &dev_attr_hardware_version,
- &dev_attr_driver_version,
- &dev_attr_option_rom_version,
- &dev_attr_firmware_version,
- &dev_attr_number_of_ports,
- &dev_attr_driver_name,
- &dev_attr_number_of_discovered_ports,
+static struct attribute *bfad_im_host_attrs[] = {
+ &dev_attr_serial_number.attr,
+ &dev_attr_model.attr,
+ &dev_attr_model_description.attr,
+ &dev_attr_node_name.attr,
+ &dev_attr_symbolic_name.attr,
+ &dev_attr_hardware_version.attr,
+ &dev_attr_driver_version.attr.attr,
+ &dev_attr_option_rom_version.attr,
+ &dev_attr_firmware_version.attr,
+ &dev_attr_number_of_ports.attr,
+ &dev_attr_driver_name.attr.attr,
+ &dev_attr_number_of_discovered_ports.attr,
NULL,
};
-struct device_attribute *bfad_im_vport_attrs[] = {
- &dev_attr_serial_number,
- &dev_attr_model,
- &dev_attr_model_description,
- &dev_attr_node_name,
- &dev_attr_symbolic_name,
- &dev_attr_hardware_version,
- &dev_attr_driver_version,
- &dev_attr_option_rom_version,
- &dev_attr_firmware_version,
- &dev_attr_number_of_ports,
- &dev_attr_driver_name,
- &dev_attr_number_of_discovered_ports,
+static const struct attribute_group bfad_im_host_attr_group = {
+ .attrs = bfad_im_host_attrs
+};
+
+const struct attribute_group *bfad_im_host_groups[] = {
+ &bfad_im_host_attr_group,
+ NULL
+};
+
+static struct attribute *bfad_im_vport_attrs[] = {
+ &dev_attr_serial_number.attr,
+ &dev_attr_model.attr,
+ &dev_attr_model_description.attr,
+ &dev_attr_node_name.attr,
+ &dev_attr_symbolic_name.attr,
+ &dev_attr_hardware_version.attr,
+ &dev_attr_driver_version.attr.attr,
+ &dev_attr_option_rom_version.attr,
+ &dev_attr_firmware_version.attr,
+ &dev_attr_number_of_ports.attr,
+ &dev_attr_driver_name.attr.attr,
+ &dev_attr_number_of_discovered_ports.attr,
NULL,
};
+static const struct attribute_group bfad_im_vport_attr_group = {
+ .attrs = bfad_im_vport_attrs
+};
+const struct attribute_group *bfad_im_vport_groups[] = {
+ &bfad_im_vport_attr_group,
+ NULL
+};
diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c
new file mode 100644
index 000000000000..54bd11e6d593
--- /dev/null
+++ b/drivers/scsi/bfa/bfad_bsg.c
@@ -0,0 +1,3612 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
+ * All rights reserved
+ * www.qlogic.com
+ *
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
+ */
+
+#include <linux/uaccess.h>
+#include "bfad_drv.h"
+#include "bfad_im.h"
+#include "bfad_bsg.h"
+
+BFA_TRC_FILE(LDRV, BSG);
+
+static int
+bfad_iocmd_ioc_enable(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ /* If IOC is not in disabled state - return */
+ if (!bfa_ioc_is_disabled(&bfad->bfa.ioc)) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+ }
+
+ init_completion(&bfad->enable_comp);
+ bfa_iocfc_enable(&bfad->bfa);
+ iocmd->status = BFA_STATUS_OK;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ wait_for_completion(&bfad->enable_comp);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_disable(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (bfa_ioc_is_disabled(&bfad->bfa.ioc)) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+ }
+
+ if (bfad->disable_active) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return -EBUSY;
+ }
+
+ bfad->disable_active = BFA_TRUE;
+ init_completion(&bfad->disable_comp);
+ bfa_iocfc_disable(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ wait_for_completion(&bfad->disable_comp);
+ bfad->disable_active = BFA_FALSE;
+ iocmd->status = BFA_STATUS_OK;
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_get_info(struct bfad_s *bfad, void *cmd)
+{
+ int i;
+ struct bfa_bsg_ioc_info_s *iocmd = (struct bfa_bsg_ioc_info_s *)cmd;
+ struct bfad_im_port_s *im_port;
+ struct bfa_port_attr_s pattr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_fcport_get_attr(&bfad->bfa, &pattr);
+ iocmd->nwwn = pattr.nwwn;
+ iocmd->pwwn = pattr.pwwn;
+ iocmd->ioc_type = bfa_get_type(&bfad->bfa);
+ iocmd->mac = bfa_get_mac(&bfad->bfa);
+ iocmd->factory_mac = bfa_get_mfg_mac(&bfad->bfa);
+ bfa_get_adapter_serial_num(&bfad->bfa, iocmd->serialnum);
+ iocmd->factorynwwn = pattr.factorynwwn;
+ iocmd->factorypwwn = pattr.factorypwwn;
+ iocmd->bfad_num = bfad->inst_no;
+ im_port = bfad->pport.im_port;
+ iocmd->host = im_port->shost->host_no;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ strcpy(iocmd->name, bfad->adapter_name);
+ strcpy(iocmd->port_name, bfad->port_name);
+ strcpy(iocmd->hwpath, bfad->pci_name);
+
+ /* set adapter hw path */
+ strcpy(iocmd->adapter_hwpath, bfad->pci_name);
+ for (i = 0; iocmd->adapter_hwpath[i] != ':' && i < BFA_STRING_32; i++)
+ ;
+ for (; iocmd->adapter_hwpath[++i] != ':' && i < BFA_STRING_32; )
+ ;
+ iocmd->adapter_hwpath[i] = '\0';
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_ioc_attr_s *iocmd = (struct bfa_bsg_ioc_attr_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_ioc_get_attr(&bfad->bfa.ioc, &iocmd->ioc_attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ /* fill in driver attr info */
+ strcpy(iocmd->ioc_attr.driver_attr.driver, BFAD_DRIVER_NAME);
+ strscpy(iocmd->ioc_attr.driver_attr.driver_ver,
+ BFAD_DRIVER_VERSION, BFA_VERSION_LEN);
+ strcpy(iocmd->ioc_attr.driver_attr.fw_ver,
+ iocmd->ioc_attr.adapter_attr.fw_ver);
+ strcpy(iocmd->ioc_attr.driver_attr.bios_ver,
+ iocmd->ioc_attr.adapter_attr.optrom_ver);
+
+ /* copy chip rev info first otherwise it will be overwritten */
+ memcpy(bfad->pci_attr.chip_rev, iocmd->ioc_attr.pci_attr.chip_rev,
+ sizeof(bfad->pci_attr.chip_rev));
+ memcpy(&iocmd->ioc_attr.pci_attr, &bfad->pci_attr,
+ sizeof(struct bfa_ioc_pci_attr_s));
+
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_ioc_stats_s *iocmd = (struct bfa_bsg_ioc_stats_s *)cmd;
+
+ bfa_ioc_get_stats(&bfad->bfa, &iocmd->ioc_stats);
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_get_fwstats(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_ioc_fwstats_s *iocmd =
+ (struct bfa_bsg_ioc_fwstats_s *)cmd;
+ void *iocmd_bufptr;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_ioc_fwstats_s),
+ sizeof(struct bfa_fw_stats_s)) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ goto out;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_ioc_fwstats_s);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ioc_fw_stats_get(&bfad->bfa.ioc, iocmd_bufptr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ goto out;
+ }
+out:
+ bfa_trc(bfad, 0x6666);
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_reset_stats(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ if (v_cmd == IOCMD_IOC_RESET_STATS) {
+ bfa_ioc_clear_stats(&bfad->bfa);
+ iocmd->status = BFA_STATUS_OK;
+ } else if (v_cmd == IOCMD_IOC_RESET_FWSTATS) {
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ioc_fw_stats_clear(&bfad->bfa.ioc);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ }
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_set_name(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_ioc_name_s *iocmd = (struct bfa_bsg_ioc_name_s *) cmd;
+
+ if (v_cmd == IOCMD_IOC_SET_ADAPTER_NAME)
+ strcpy(bfad->adapter_name, iocmd->name);
+ else if (v_cmd == IOCMD_IOC_SET_PORT_NAME)
+ strcpy(bfad->port_name, iocmd->name);
+
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_iocfc_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_iocfc_attr_s *iocmd = (struct bfa_bsg_iocfc_attr_s *)cmd;
+
+ iocmd->status = BFA_STATUS_OK;
+ bfa_iocfc_get_attr(&bfad->bfa, &iocmd->iocfc_attr);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_fw_sig_inv(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ioc_fwsig_invalidate(&bfad->bfa.ioc);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_iocfc_set_intr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_iocfc_intr_s *iocmd = (struct bfa_bsg_iocfc_intr_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_iocfc_israttr_set(&bfad->bfa, &iocmd->attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_port_enable(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_port_enable(&bfad->bfa.modules.port,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ return 0;
+ }
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ return 0;
+}
+
+static int
+bfad_iocmd_port_disable(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_port_disable(&bfad->bfa.modules.port,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ return 0;
+ }
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ return 0;
+}
+
+static int
+bfad_iocmd_port_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_port_attr_s *iocmd = (struct bfa_bsg_port_attr_s *)cmd;
+ struct bfa_lport_attr_s port_attr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_fcport_get_attr(&bfad->bfa, &iocmd->attr);
+ bfa_fcs_lport_get_attr(&bfad->bfa_fcs.fabric.bport, &port_attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (iocmd->attr.topology != BFA_PORT_TOPOLOGY_NONE)
+ iocmd->attr.pid = port_attr.pid;
+ else
+ iocmd->attr.pid = 0;
+
+ iocmd->attr.port_type = port_attr.port_type;
+ iocmd->attr.loopback = port_attr.loopback;
+ iocmd->attr.authfail = port_attr.authfail;
+ strscpy(iocmd->attr.port_symname.symname,
+ port_attr.port_cfg.sym_name.symname,
+ sizeof(iocmd->attr.port_symname.symname));
+
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_port_get_stats(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_port_stats_s *iocmd = (struct bfa_bsg_port_stats_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ void *iocmd_bufptr;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_port_stats_s),
+ sizeof(union bfa_port_stats_u)) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_port_stats_s);
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_port_get_stats(&bfad->bfa.modules.port,
+ iocmd_bufptr, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ goto out;
+ }
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_port_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_port_clear_stats(&bfad->bfa.modules.port,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ return 0;
+ }
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ return 0;
+}
+
+static int
+bfad_iocmd_set_port_cfg(struct bfad_s *bfad, void *iocmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_port_cfg_s *cmd = (struct bfa_bsg_port_cfg_s *)iocmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (v_cmd == IOCMD_PORT_CFG_TOPO)
+ cmd->status = bfa_fcport_cfg_topology(&bfad->bfa, cmd->param);
+ else if (v_cmd == IOCMD_PORT_CFG_SPEED)
+ cmd->status = bfa_fcport_cfg_speed(&bfad->bfa, cmd->param);
+ else if (v_cmd == IOCMD_PORT_CFG_ALPA)
+ cmd->status = bfa_fcport_cfg_hardalpa(&bfad->bfa, cmd->param);
+ else if (v_cmd == IOCMD_PORT_CLR_ALPA)
+ cmd->status = bfa_fcport_clr_hardalpa(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_port_cfg_maxfrsize(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_port_cfg_maxfrsize_s *iocmd =
+ (struct bfa_bsg_port_cfg_maxfrsize_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcport_cfg_maxfrsize(&bfad->bfa, iocmd->maxfrsize);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_port_cfg_bbcr(struct bfad_s *bfad, unsigned int cmd, void *pcmd)
+{
+ struct bfa_bsg_bbcr_enable_s *iocmd =
+ (struct bfa_bsg_bbcr_enable_s *)pcmd;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (cmd == IOCMD_PORT_BBCR_ENABLE)
+ rc = bfa_fcport_cfg_bbcr(&bfad->bfa, BFA_TRUE, iocmd->bb_scn);
+ else if (cmd == IOCMD_PORT_BBCR_DISABLE)
+ rc = bfa_fcport_cfg_bbcr(&bfad->bfa, BFA_FALSE, 0);
+ else {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ iocmd->status = rc;
+ return 0;
+}
+
+static int
+bfad_iocmd_port_get_bbcr_attr(struct bfad_s *bfad, void *pcmd)
+{
+ struct bfa_bsg_bbcr_attr_s *iocmd = (struct bfa_bsg_bbcr_attr_s *) pcmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status =
+ bfa_fcport_get_bbcr_attr(&bfad->bfa, &iocmd->attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+
+static int
+bfad_iocmd_lport_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_bsg_lport_attr_s *iocmd = (struct bfa_bsg_lport_attr_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ bfa_fcs_lport_get_attr(fcs_port, &iocmd->port_attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_lport_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_bsg_lport_stats_s *iocmd =
+ (struct bfa_bsg_lport_stats_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ bfa_fcs_lport_get_stats(fcs_port, &iocmd->port_stats);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_lport_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_bsg_reset_stats_s *iocmd =
+ (struct bfa_bsg_reset_stats_s *)cmd;
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(&bfad->bfa);
+ struct list_head *qe, *qen;
+ struct bfa_itnim_s *itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->vpwwn);
+ if (fcs_port == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ bfa_fcs_lport_clear_stats(fcs_port);
+ /* clear IO stats from all active itnims */
+ list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+ itnim = (struct bfa_itnim_s *) qe;
+ if (itnim->rport->rport_info.lp_tag != fcs_port->lp_tag)
+ continue;
+ bfa_itnim_clear_stats(itnim);
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_lport_get_iostats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_bsg_lport_iostats_s *iocmd =
+ (struct bfa_bsg_lport_iostats_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ bfa_fcpim_port_iostats(&bfad->bfa, &iocmd->iostats,
+ fcs_port->lp_tag);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_lport_get_rports(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_lport_get_rports_s *iocmd =
+ (struct bfa_bsg_lport_get_rports_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ unsigned long flags;
+ void *iocmd_bufptr;
+
+ if (iocmd->nrports == 0)
+ return -EINVAL;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_lport_get_rports_s),
+ sizeof(struct bfa_rport_qualifier_s) * iocmd->nrports)
+ != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd +
+ sizeof(struct bfa_bsg_lport_get_rports_s);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, 0);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ bfa_fcs_lport_get_rport_quals(fcs_port,
+ (struct bfa_rport_qualifier_s *)iocmd_bufptr,
+ &iocmd->nrports);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_rport_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_rport_attr_s *iocmd = (struct bfa_bsg_rport_attr_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_rport_s *fcs_rport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ if (iocmd->pid)
+ fcs_rport = bfa_fcs_lport_get_rport_by_qualifier(fcs_port,
+ iocmd->rpwwn, iocmd->pid);
+ else
+ fcs_rport = bfa_fcs_rport_lookup(fcs_port, iocmd->rpwwn);
+ if (fcs_rport == NULL) {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ goto out;
+ }
+
+ bfa_fcs_rport_get_attr(fcs_rport, &iocmd->attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_rport_get_addr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_rport_scsi_addr_s *iocmd =
+ (struct bfa_bsg_rport_scsi_addr_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_itnim_s *fcs_itnim;
+ struct bfad_itnim_s *drv_itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ fcs_itnim = bfa_fcs_itnim_lookup(fcs_port, iocmd->rpwwn);
+ if (fcs_itnim == NULL) {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ goto out;
+ }
+
+ drv_itnim = fcs_itnim->itnim_drv;
+
+ if (drv_itnim && drv_itnim->im_port)
+ iocmd->host = drv_itnim->im_port->shost->host_no;
+ else {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ goto out;
+ }
+
+ iocmd->target = drv_itnim->scsi_tgt_id;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ iocmd->bus = 0;
+ iocmd->lun = 0;
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_rport_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_rport_stats_s *iocmd =
+ (struct bfa_bsg_rport_stats_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_rport_s *fcs_rport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ fcs_rport = bfa_fcs_rport_lookup(fcs_port, iocmd->rpwwn);
+ if (fcs_rport == NULL) {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ goto out;
+ }
+
+ memcpy((void *)&iocmd->stats, (void *)&fcs_rport->stats,
+ sizeof(struct bfa_rport_stats_s));
+ if (bfa_fcs_rport_get_halrport(fcs_rport)) {
+ memcpy((void *)&iocmd->stats.hal_stats,
+ (void *)&(bfa_fcs_rport_get_halrport(fcs_rport)->stats),
+ sizeof(struct bfa_rport_hal_stats_s));
+ }
+
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_rport_clr_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_rport_reset_stats_s *iocmd =
+ (struct bfa_bsg_rport_reset_stats_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_rport_s *fcs_rport;
+ struct bfa_rport_s *rport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ fcs_rport = bfa_fcs_rport_lookup(fcs_port, iocmd->rpwwn);
+ if (fcs_rport == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ goto out;
+ }
+
+ memset((char *)&fcs_rport->stats, 0, sizeof(struct bfa_rport_stats_s));
+ rport = bfa_fcs_rport_get_halrport(fcs_rport);
+ if (rport)
+ memset(&rport->stats, 0, sizeof(rport->stats));
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_rport_set_speed(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_rport_set_speed_s *iocmd =
+ (struct bfa_bsg_rport_set_speed_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_rport_s *fcs_rport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (fcs_port == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ goto out;
+ }
+
+ fcs_rport = bfa_fcs_rport_lookup(fcs_port, iocmd->rpwwn);
+ if (fcs_rport == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ goto out;
+ }
+
+ fcs_rport->rpf.assigned_speed = iocmd->speed;
+ /* Set this speed in f/w only if the RPSC speed is not available */
+ if (fcs_rport->rpf.rpsc_speed == BFA_PORT_SPEED_UNKNOWN)
+ if (fcs_rport->bfa_rport)
+ bfa_rport_speed(fcs_rport->bfa_rport, iocmd->speed);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_vport_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_fcs_vport_s *fcs_vport;
+ struct bfa_bsg_vport_attr_s *iocmd = (struct bfa_bsg_vport_attr_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->vpwwn);
+ if (fcs_vport == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_VWWN;
+ goto out;
+ }
+
+ bfa_fcs_vport_get_attr(fcs_vport, &iocmd->vport_attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_vport_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_fcs_vport_s *fcs_vport;
+ struct bfa_bsg_vport_stats_s *iocmd =
+ (struct bfa_bsg_vport_stats_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->vpwwn);
+ if (fcs_vport == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_VWWN;
+ goto out;
+ }
+
+ memcpy((void *)&iocmd->vport_stats, (void *)&fcs_vport->vport_stats,
+ sizeof(struct bfa_vport_stats_s));
+ memcpy((void *)&iocmd->vport_stats.port_stats,
+ (void *)&fcs_vport->lport.stats,
+ sizeof(struct bfa_lport_stats_s));
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_vport_clr_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_fcs_vport_s *fcs_vport;
+ struct bfa_bsg_reset_stats_s *iocmd =
+ (struct bfa_bsg_reset_stats_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->vpwwn);
+ if (fcs_vport == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_VWWN;
+ goto out;
+ }
+
+ memset(&fcs_vport->vport_stats, 0, sizeof(struct bfa_vport_stats_s));
+ memset(&fcs_vport->lport.stats, 0, sizeof(struct bfa_lport_stats_s));
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_fabric_get_lports(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_fabric_get_lports_s *iocmd =
+ (struct bfa_bsg_fabric_get_lports_s *)cmd;
+ bfa_fcs_vf_t *fcs_vf;
+ uint32_t nports = iocmd->nports;
+ unsigned long flags;
+ void *iocmd_bufptr;
+
+ if (nports == 0) {
+ iocmd->status = BFA_STATUS_EINVAL;
+ goto out;
+ }
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_fabric_get_lports_s),
+ sizeof(wwn_t) * iocmd->nports) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ goto out;
+ }
+
+ iocmd_bufptr = (char *)iocmd +
+ sizeof(struct bfa_bsg_fabric_get_lports_s);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_vf = bfa_fcs_vf_lookup(&bfad->bfa_fcs, iocmd->vf_id);
+ if (fcs_vf == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_VFID;
+ goto out;
+ }
+ bfa_fcs_vf_get_ports(fcs_vf, (wwn_t *)iocmd_bufptr, &nports);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ iocmd->nports = nports;
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_qos_set_bw(struct bfad_s *bfad, void *pcmd)
+{
+ struct bfa_bsg_qos_bw_s *iocmd = (struct bfa_bsg_qos_bw_s *)pcmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcport_set_qos_bw(&bfad->bfa, &iocmd->qos_bw);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ratelim(struct bfad_s *bfad, unsigned int cmd, void *pcmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) &&
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else {
+ if (cmd == IOCMD_RATELIM_ENABLE)
+ fcport->cfg.ratelimit = BFA_TRUE;
+ else if (cmd == IOCMD_RATELIM_DISABLE)
+ fcport->cfg.ratelimit = BFA_FALSE;
+
+ if (fcport->cfg.trl_def_speed == BFA_PORT_SPEED_UNKNOWN)
+ fcport->cfg.trl_def_speed = BFA_PORT_SPEED_1GBPS;
+
+ iocmd->status = BFA_STATUS_OK;
+ }
+
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ratelim_speed(struct bfad_s *bfad, unsigned int cmd, void *pcmd)
+{
+ struct bfa_bsg_trl_speed_s *iocmd = (struct bfa_bsg_trl_speed_s *)pcmd;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ /* Auto and speeds greater than the supported speed, are invalid */
+ if ((iocmd->speed == BFA_PORT_SPEED_AUTO) ||
+ (iocmd->speed > fcport->speed_sup)) {
+ iocmd->status = BFA_STATUS_UNSUPP_SPEED;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+ }
+
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) &&
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else {
+ fcport->cfg.trl_def_speed = iocmd->speed;
+ iocmd->status = BFA_STATUS_OK;
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_cfg_fcpim(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcpim_s *iocmd = (struct bfa_bsg_fcpim_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_fcpim_path_tov_set(&bfad->bfa, iocmd->param);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_get_modstats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcpim_modstats_s *iocmd =
+ (struct bfa_bsg_fcpim_modstats_s *)cmd;
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(&bfad->bfa);
+ struct list_head *qe, *qen;
+ struct bfa_itnim_s *itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ /* accumulate IO stats from itnim */
+ memset((void *)&iocmd->modstats, 0, sizeof(struct bfa_itnim_iostats_s));
+ list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+ itnim = (struct bfa_itnim_s *) qe;
+ bfa_fcpim_add_stats(&iocmd->modstats, &(itnim->stats));
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_clr_modstats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcpim_modstatsclr_s *iocmd =
+ (struct bfa_bsg_fcpim_modstatsclr_s *)cmd;
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(&bfad->bfa);
+ struct list_head *qe, *qen;
+ struct bfa_itnim_s *itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ list_for_each_safe(qe, qen, &fcpim->itnim_q) {
+ itnim = (struct bfa_itnim_s *) qe;
+ bfa_itnim_clear_stats(itnim);
+ }
+ memset(&fcpim->del_itn_stats, 0,
+ sizeof(struct bfa_fcpim_del_itn_stats_s));
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_get_del_itn_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcpim_del_itn_stats_s *iocmd =
+ (struct bfa_bsg_fcpim_del_itn_stats_s *)cmd;
+ struct bfa_fcpim_s *fcpim = BFA_FCPIM(&bfad->bfa);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ memcpy((void *)&iocmd->modstats, (void *)&fcpim->del_itn_stats,
+ sizeof(struct bfa_fcpim_del_itn_stats_s));
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_itnim_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_itnim_attr_s *iocmd = (struct bfa_bsg_itnim_attr_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->lpwwn);
+ if (!fcs_port)
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ else
+ iocmd->status = bfa_fcs_itnim_attr_get(fcs_port,
+ iocmd->rpwwn, &iocmd->attr);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_itnim_get_iostats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_itnim_iostats_s *iocmd =
+ (struct bfa_bsg_itnim_iostats_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_itnim_s *itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->lpwwn);
+ if (!fcs_port) {
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ bfa_trc(bfad, 0);
+ } else {
+ itnim = bfa_fcs_itnim_lookup(fcs_port, iocmd->rpwwn);
+ if (itnim == NULL)
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ else {
+ iocmd->status = BFA_STATUS_OK;
+ if (bfa_fcs_itnim_get_halitn(itnim))
+ memcpy((void *)&iocmd->iostats, (void *)
+ &(bfa_fcs_itnim_get_halitn(itnim)->stats),
+ sizeof(struct bfa_itnim_iostats_s));
+ }
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_itnim_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_rport_reset_stats_s *iocmd =
+ (struct bfa_bsg_rport_reset_stats_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_itnim_s *itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->pwwn);
+ if (!fcs_port)
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ else {
+ itnim = bfa_fcs_itnim_lookup(fcs_port, iocmd->rpwwn);
+ if (itnim == NULL)
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ else {
+ iocmd->status = BFA_STATUS_OK;
+ bfa_fcs_itnim_stats_clear(fcs_port, iocmd->rpwwn);
+ bfa_itnim_clear_stats(bfa_fcs_itnim_get_halitn(itnim));
+ }
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_itnim_get_itnstats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_itnim_itnstats_s *iocmd =
+ (struct bfa_bsg_itnim_itnstats_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_itnim_s *itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->lpwwn);
+ if (!fcs_port) {
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ bfa_trc(bfad, 0);
+ } else {
+ itnim = bfa_fcs_itnim_lookup(fcs_port, iocmd->rpwwn);
+ if (itnim == NULL)
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ else {
+ iocmd->status = BFA_STATUS_OK;
+ bfa_fcs_itnim_stats_get(fcs_port, iocmd->rpwwn,
+ &iocmd->itnstats);
+ }
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_fcport_enable(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcport_enable(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_fcport_disable(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcport_disable(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ioc_get_pcifn_cfg(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_pcifn_cfg_s *iocmd = (struct bfa_bsg_pcifn_cfg_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ablk_query(&bfad->bfa.modules.ablk,
+ &iocmd->pcifn_cfg,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_pcifn_create(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_pcifn_s *iocmd = (struct bfa_bsg_pcifn_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ablk_pf_create(&bfad->bfa.modules.ablk,
+ &iocmd->pcifn_id, iocmd->port,
+ iocmd->pcifn_class, iocmd->bw_min,
+ iocmd->bw_max, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_pcifn_delete(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_pcifn_s *iocmd = (struct bfa_bsg_pcifn_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ablk_pf_delete(&bfad->bfa.modules.ablk,
+ iocmd->pcifn_id,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_pcifn_bw(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_pcifn_s *iocmd = (struct bfa_bsg_pcifn_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ablk_pf_update(&bfad->bfa.modules.ablk,
+ iocmd->pcifn_id, iocmd->bw_min,
+ iocmd->bw_max, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ bfa_trc(bfad, iocmd->status);
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_adapter_cfg_mode(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_adapter_cfg_mode_s *iocmd =
+ (struct bfa_bsg_adapter_cfg_mode_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags = 0;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ablk_adapter_config(&bfad->bfa.modules.ablk,
+ iocmd->cfg.mode, iocmd->cfg.max_pf,
+ iocmd->cfg.max_vf, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_port_cfg_mode(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_port_cfg_mode_s *iocmd =
+ (struct bfa_bsg_port_cfg_mode_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags = 0;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_ablk_port_config(&bfad->bfa.modules.ablk,
+ iocmd->instance, iocmd->cfg.mode,
+ iocmd->cfg.max_pf, iocmd->cfg.max_vf,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_ablk_optrom(struct bfad_s *bfad, unsigned int cmd, void *pcmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (cmd == IOCMD_FLASH_ENABLE_OPTROM)
+ iocmd->status = bfa_ablk_optrom_en(&bfad->bfa.modules.ablk,
+ bfad_hcb_comp, &fcomp);
+ else
+ iocmd->status = bfa_ablk_optrom_dis(&bfad->bfa.modules.ablk,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_faa_query(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_faa_attr_s *iocmd = (struct bfa_bsg_faa_attr_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ iocmd->status = BFA_STATUS_OK;
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_faa_query(&bfad->bfa, &iocmd->faa_attr,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_cee_attr(struct bfad_s *bfad, void *cmd, unsigned int payload_len)
+{
+ struct bfa_bsg_cee_attr_s *iocmd =
+ (struct bfa_bsg_cee_attr_s *)cmd;
+ void *iocmd_bufptr;
+ struct bfad_hal_comp cee_comp;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_cee_attr_s),
+ sizeof(struct bfa_cee_attr_s)) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_cee_attr_s);
+
+ cee_comp.status = 0;
+ init_completion(&cee_comp.comp);
+ mutex_lock(&bfad_mutex);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_cee_get_attr(&bfad->bfa.modules.cee, iocmd_bufptr,
+ bfad_hcb_comp, &cee_comp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ mutex_unlock(&bfad_mutex);
+ bfa_trc(bfad, 0x5555);
+ goto out;
+ }
+ wait_for_completion(&cee_comp.comp);
+ mutex_unlock(&bfad_mutex);
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_cee_get_stats(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_cee_stats_s *iocmd =
+ (struct bfa_bsg_cee_stats_s *)cmd;
+ void *iocmd_bufptr;
+ struct bfad_hal_comp cee_comp;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_cee_stats_s),
+ sizeof(struct bfa_cee_stats_s)) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_cee_stats_s);
+
+ cee_comp.status = 0;
+ init_completion(&cee_comp.comp);
+ mutex_lock(&bfad_mutex);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_cee_get_stats(&bfad->bfa.modules.cee, iocmd_bufptr,
+ bfad_hcb_comp, &cee_comp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ mutex_unlock(&bfad_mutex);
+ bfa_trc(bfad, 0x5555);
+ goto out;
+ }
+ wait_for_completion(&cee_comp.comp);
+ mutex_unlock(&bfad_mutex);
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_cee_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_cee_reset_stats(&bfad->bfa.modules.cee, NULL, NULL);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ bfa_trc(bfad, 0x5555);
+ return 0;
+}
+
+static int
+bfad_iocmd_sfp_media(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_sfp_media_s *iocmd = (struct bfa_bsg_sfp_media_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_sfp_media(BFA_SFP_MOD(&bfad->bfa), &iocmd->media,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_SFP_NOT_READY)
+ goto out;
+
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_sfp_speed(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_sfp_speed_s *iocmd = (struct bfa_bsg_sfp_speed_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_sfp_speed(BFA_SFP_MOD(&bfad->bfa), iocmd->speed,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_SFP_NOT_READY)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_flash_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_flash_attr_s *iocmd =
+ (struct bfa_bsg_flash_attr_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_get_attr(BFA_FLASH(&bfad->bfa), &iocmd->attr,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_flash_erase_part(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_flash_s *iocmd = (struct bfa_bsg_flash_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_erase_part(BFA_FLASH(&bfad->bfa), iocmd->type,
+ iocmd->instance, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_flash_update_part(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_flash_s *iocmd = (struct bfa_bsg_flash_s *)cmd;
+ void *iocmd_bufptr;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_flash_s),
+ iocmd->bufsz) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_flash_s);
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_update_part(BFA_FLASH(&bfad->bfa),
+ iocmd->type, iocmd->instance, iocmd_bufptr,
+ iocmd->bufsz, 0, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_flash_read_part(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_flash_s *iocmd = (struct bfa_bsg_flash_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ void *iocmd_bufptr;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_flash_s),
+ iocmd->bufsz) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_flash_s);
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_read_part(BFA_FLASH(&bfad->bfa), iocmd->type,
+ iocmd->instance, iocmd_bufptr, iocmd->bufsz, 0,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_temp(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_get_temp_s *iocmd =
+ (struct bfa_bsg_diag_get_temp_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_diag_tsensor_query(BFA_DIAG_MOD(&bfad->bfa),
+ &iocmd->result, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_memtest(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_memtest_s *iocmd =
+ (struct bfa_bsg_diag_memtest_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_diag_memtest(BFA_DIAG_MOD(&bfad->bfa),
+ &iocmd->memtest, iocmd->pat,
+ &iocmd->result, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_loopback(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_loopback_s *iocmd =
+ (struct bfa_bsg_diag_loopback_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcdiag_loopback(&bfad->bfa, iocmd->opmode,
+ iocmd->speed, iocmd->lpcnt, iocmd->pat,
+ &iocmd->result, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_fwping(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_fwping_s *iocmd =
+ (struct bfa_bsg_diag_fwping_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_diag_fwping(BFA_DIAG_MOD(&bfad->bfa), iocmd->cnt,
+ iocmd->pattern, &iocmd->result,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ bfa_trc(bfad, 0x77771);
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_queuetest(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_qtest_s *iocmd = (struct bfa_bsg_diag_qtest_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcdiag_queuetest(&bfad->bfa, iocmd->force,
+ iocmd->queue, &iocmd->result,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_sfp(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_sfp_show_s *iocmd =
+ (struct bfa_bsg_sfp_show_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_sfp_show(BFA_SFP_MOD(&bfad->bfa), &iocmd->sfp,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ bfa_trc(bfad, iocmd->status);
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_led(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_led_s *iocmd = (struct bfa_bsg_diag_led_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_diag_ledtest(BFA_DIAG_MOD(&bfad->bfa),
+ &iocmd->ledtest);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_beacon_lport(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_beacon_s *iocmd =
+ (struct bfa_bsg_diag_beacon_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_diag_beacon_port(BFA_DIAG_MOD(&bfad->bfa),
+ iocmd->beacon, iocmd->link_e2e_beacon,
+ iocmd->second);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_lb_stat(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_diag_lb_stat_s *iocmd =
+ (struct bfa_bsg_diag_lb_stat_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcdiag_lb_is_running(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfa_trc(bfad, iocmd->status);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_dport_enable(struct bfad_s *bfad, void *pcmd)
+{
+ struct bfa_bsg_dport_enable_s *iocmd =
+ (struct bfa_bsg_dport_enable_s *)pcmd;
+ unsigned long flags;
+ struct bfad_hal_comp fcomp;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_dport_enable(&bfad->bfa, iocmd->lpcnt,
+ iocmd->pat, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ bfa_trc(bfad, iocmd->status);
+ else {
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ }
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_dport_disable(struct bfad_s *bfad, void *pcmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd;
+ unsigned long flags;
+ struct bfad_hal_comp fcomp;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_dport_disable(&bfad->bfa, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ bfa_trc(bfad, iocmd->status);
+ else {
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ }
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_dport_start(struct bfad_s *bfad, void *pcmd)
+{
+ struct bfa_bsg_dport_enable_s *iocmd =
+ (struct bfa_bsg_dport_enable_s *)pcmd;
+ unsigned long flags;
+ struct bfad_hal_comp fcomp;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_dport_start(&bfad->bfa, iocmd->lpcnt,
+ iocmd->pat, bfad_hcb_comp,
+ &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ } else {
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ }
+
+ return 0;
+}
+
+static int
+bfad_iocmd_diag_dport_show(struct bfad_s *bfad, void *pcmd)
+{
+ struct bfa_bsg_diag_dport_show_s *iocmd =
+ (struct bfa_bsg_diag_dport_show_s *)pcmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_dport_show(&bfad->bfa, &iocmd->result);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+
+static int
+bfad_iocmd_phy_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_phy_attr_s *iocmd =
+ (struct bfa_bsg_phy_attr_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_phy_get_attr(BFA_PHY(&bfad->bfa), iocmd->instance,
+ &iocmd->attr, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_phy_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_phy_stats_s *iocmd =
+ (struct bfa_bsg_phy_stats_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_phy_get_stats(BFA_PHY(&bfad->bfa), iocmd->instance,
+ &iocmd->stats, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_phy_read(struct bfad_s *bfad, void *cmd, unsigned int payload_len)
+{
+ struct bfa_bsg_phy_s *iocmd = (struct bfa_bsg_phy_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ void *iocmd_bufptr;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_phy_s),
+ iocmd->bufsz) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_phy_s);
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_phy_read(BFA_PHY(&bfad->bfa),
+ iocmd->instance, iocmd_bufptr, iocmd->bufsz,
+ 0, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_vhba_query(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_vhba_attr_s *iocmd =
+ (struct bfa_bsg_vhba_attr_s *)cmd;
+ struct bfa_vhba_attr_s *attr = &iocmd->attr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ attr->pwwn = bfad->bfa.ioc.attr->pwwn;
+ attr->nwwn = bfad->bfa.ioc.attr->nwwn;
+ attr->plog_enabled = (bfa_boolean_t)bfad->bfa.plog->plog_enabled;
+ attr->io_profile = bfa_fcpim_get_io_profile(&bfad->bfa);
+ attr->path_tov = bfa_fcpim_path_tov_get(&bfad->bfa);
+ iocmd->status = BFA_STATUS_OK;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_phy_update(struct bfad_s *bfad, void *cmd, unsigned int payload_len)
+{
+ struct bfa_bsg_phy_s *iocmd = (struct bfa_bsg_phy_s *)cmd;
+ void *iocmd_bufptr;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ if (bfad_chk_iocmd_sz(payload_len,
+ sizeof(struct bfa_bsg_phy_s),
+ iocmd->bufsz) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_phy_s);
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_phy_update(BFA_PHY(&bfad->bfa),
+ iocmd->instance, iocmd_bufptr, iocmd->bufsz,
+ 0, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_porglog_get(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_debug_s *iocmd = (struct bfa_bsg_debug_s *)cmd;
+ void *iocmd_bufptr;
+
+ if (iocmd->bufsz < sizeof(struct bfa_plog_s)) {
+ bfa_trc(bfad, sizeof(struct bfa_plog_s));
+ iocmd->status = BFA_STATUS_EINVAL;
+ goto out;
+ }
+
+ iocmd->status = BFA_STATUS_OK;
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_debug_s);
+ memcpy(iocmd_bufptr, (u8 *) &bfad->plog_buf, sizeof(struct bfa_plog_s));
+out:
+ return 0;
+}
+
+#define BFA_DEBUG_FW_CORE_CHUNK_SZ 0x4000U /* 16K chunks for FW dump */
+static int
+bfad_iocmd_debug_fw_core(struct bfad_s *bfad, void *cmd,
+ unsigned int payload_len)
+{
+ struct bfa_bsg_debug_s *iocmd = (struct bfa_bsg_debug_s *)cmd;
+ void *iocmd_bufptr;
+ unsigned long flags;
+ u32 offset;
+
+ if (bfad_chk_iocmd_sz(payload_len, sizeof(struct bfa_bsg_debug_s),
+ BFA_DEBUG_FW_CORE_CHUNK_SZ) != BFA_STATUS_OK) {
+ iocmd->status = BFA_STATUS_VERSION_FAIL;
+ return 0;
+ }
+
+ if (iocmd->bufsz < BFA_DEBUG_FW_CORE_CHUNK_SZ ||
+ !IS_ALIGNED(iocmd->bufsz, sizeof(u16)) ||
+ !IS_ALIGNED(iocmd->offset, sizeof(u32))) {
+ bfa_trc(bfad, BFA_DEBUG_FW_CORE_CHUNK_SZ);
+ iocmd->status = BFA_STATUS_EINVAL;
+ goto out;
+ }
+
+ iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_debug_s);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ offset = iocmd->offset;
+ iocmd->status = bfa_ioc_debug_fwcore(&bfad->bfa.ioc, iocmd_bufptr,
+ &offset, &iocmd->bufsz);
+ iocmd->offset = offset;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_debug_ctl(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ unsigned long flags;
+
+ if (v_cmd == IOCMD_DEBUG_FW_STATE_CLR) {
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfad->bfa.ioc.dbg_fwsave_once = BFA_TRUE;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ } else if (v_cmd == IOCMD_DEBUG_PORTLOG_CLR)
+ bfad->plog_buf.head = bfad->plog_buf.tail = 0;
+ else if (v_cmd == IOCMD_DEBUG_START_DTRC)
+ bfa_trc_init(bfad->trcmod);
+ else if (v_cmd == IOCMD_DEBUG_STOP_DTRC)
+ bfa_trc_stop(bfad->trcmod);
+
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_porglog_ctl(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_portlogctl_s *iocmd = (struct bfa_bsg_portlogctl_s *)cmd;
+
+ if (iocmd->ctl == BFA_TRUE)
+ bfad->plog_buf.plog_enabled = 1;
+ else
+ bfad->plog_buf.plog_enabled = 0;
+
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_cfg_profile(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_fcpim_profile_s *iocmd =
+ (struct bfa_bsg_fcpim_profile_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (v_cmd == IOCMD_FCPIM_PROFILE_ON)
+ iocmd->status = bfa_fcpim_profile_on(&bfad->bfa, ktime_get_real_seconds());
+ else if (v_cmd == IOCMD_FCPIM_PROFILE_OFF)
+ iocmd->status = bfa_fcpim_profile_off(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_itnim_get_ioprofile(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_itnim_ioprofile_s *iocmd =
+ (struct bfa_bsg_itnim_ioprofile_s *)cmd;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_itnim_s *itnim;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs,
+ iocmd->vf_id, iocmd->lpwwn);
+ if (!fcs_port)
+ iocmd->status = BFA_STATUS_UNKNOWN_LWWN;
+ else {
+ itnim = bfa_fcs_itnim_lookup(fcs_port, iocmd->rpwwn);
+ if (itnim == NULL)
+ iocmd->status = BFA_STATUS_UNKNOWN_RWWN;
+ else
+ iocmd->status = bfa_itnim_get_ioprofile(
+ bfa_fcs_itnim_get_halitn(itnim),
+ &iocmd->ioprofile);
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_fcport_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcport_stats_s *iocmd =
+ (struct bfa_bsg_fcport_stats_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+ struct bfa_cb_pending_q_s cb_qe;
+
+ init_completion(&fcomp.comp);
+ bfa_pending_q_init_status(&cb_qe, bfad_hcb_comp, &fcomp, &iocmd->stats);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcport_get_stats(&bfad->bfa, &cb_qe);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ goto out;
+ }
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_fcport_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+ struct bfa_cb_pending_q_s cb_qe;
+
+ init_completion(&fcomp.comp);
+ bfa_pending_q_init_status(&cb_qe, bfad_hcb_comp, &fcomp, NULL);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcport_clear_stats(&bfad->bfa, &cb_qe);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ goto out;
+ }
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_boot_cfg(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_boot_s *iocmd = (struct bfa_bsg_boot_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_update_part(BFA_FLASH(&bfad->bfa),
+ BFA_FLASH_PART_BOOT, bfad->bfa.ioc.port_id,
+ &iocmd->cfg, sizeof(struct bfa_boot_cfg_s), 0,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_boot_query(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_boot_s *iocmd = (struct bfa_bsg_boot_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_read_part(BFA_FLASH(&bfad->bfa),
+ BFA_FLASH_PART_BOOT, bfad->bfa.ioc.port_id,
+ &iocmd->cfg, sizeof(struct bfa_boot_cfg_s), 0,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_preboot_query(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_preboot_s *iocmd = (struct bfa_bsg_preboot_s *)cmd;
+ struct bfi_iocfc_cfgrsp_s *cfgrsp = bfad->bfa.iocfc.cfgrsp;
+ struct bfa_boot_pbc_s *pbcfg = &iocmd->cfg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ pbcfg->enable = cfgrsp->pbc_cfg.boot_enabled;
+ pbcfg->nbluns = cfgrsp->pbc_cfg.nbluns;
+ pbcfg->speed = cfgrsp->pbc_cfg.port_speed;
+ memcpy(pbcfg->pblun, cfgrsp->pbc_cfg.blun, sizeof(pbcfg->pblun));
+ iocmd->status = BFA_STATUS_OK;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_ethboot_cfg(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_ethboot_s *iocmd = (struct bfa_bsg_ethboot_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_update_part(BFA_FLASH(&bfad->bfa),
+ BFA_FLASH_PART_PXECFG,
+ bfad->bfa.ioc.port_id, &iocmd->cfg,
+ sizeof(struct bfa_ethboot_cfg_s), 0,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_ethboot_query(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_ethboot_s *iocmd = (struct bfa_bsg_ethboot_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_flash_read_part(BFA_FLASH(&bfad->bfa),
+ BFA_FLASH_PART_PXECFG,
+ bfad->bfa.ioc.port_id, &iocmd->cfg,
+ sizeof(struct bfa_ethboot_cfg_s), 0,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK)
+ goto out;
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_cfg_trunk(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+ struct bfa_fcport_trunk_s *trunk = &fcport->trunk;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ if (bfa_fcport_is_dport(&bfad->bfa)) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return BFA_STATUS_DPORT_ERR;
+ }
+
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) ||
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else {
+ if (v_cmd == IOCMD_TRUNK_ENABLE) {
+ trunk->attr.state = BFA_TRUNK_OFFLINE;
+ bfa_fcport_disable(&bfad->bfa);
+ fcport->cfg.trunked = BFA_TRUE;
+ } else if (v_cmd == IOCMD_TRUNK_DISABLE) {
+ trunk->attr.state = BFA_TRUNK_DISABLED;
+ bfa_fcport_disable(&bfad->bfa);
+ fcport->cfg.trunked = BFA_FALSE;
+ }
+
+ if (!bfa_fcport_is_disabled(&bfad->bfa))
+ bfa_fcport_enable(&bfad->bfa);
+
+ iocmd->status = BFA_STATUS_OK;
+ }
+
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_trunk_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_trunk_attr_s *iocmd = (struct bfa_bsg_trunk_attr_s *)cmd;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+ struct bfa_fcport_trunk_s *trunk = &fcport->trunk;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) ||
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else {
+ memcpy((void *)&iocmd->attr, (void *)&trunk->attr,
+ sizeof(struct bfa_trunk_attr_s));
+ iocmd->attr.port_id = bfa_lps_get_base_pid(&bfad->bfa);
+ iocmd->status = BFA_STATUS_OK;
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_qos(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (bfa_ioc_get_type(&bfad->bfa.ioc) == BFA_IOC_TYPE_FC) {
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) &&
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else {
+ if (v_cmd == IOCMD_QOS_ENABLE)
+ fcport->cfg.qos_enabled = BFA_TRUE;
+ else if (v_cmd == IOCMD_QOS_DISABLE) {
+ fcport->cfg.qos_enabled = BFA_FALSE;
+ fcport->cfg.qos_bw.high = BFA_QOS_BW_HIGH;
+ fcport->cfg.qos_bw.med = BFA_QOS_BW_MED;
+ fcport->cfg.qos_bw.low = BFA_QOS_BW_LOW;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_qos_get_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_qos_attr_s *iocmd = (struct bfa_bsg_qos_attr_s *)cmd;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) &&
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else {
+ iocmd->attr.state = fcport->qos_attr.state;
+ iocmd->attr.total_bb_cr =
+ be32_to_cpu(fcport->qos_attr.total_bb_cr);
+ iocmd->attr.qos_bw.high = fcport->cfg.qos_bw.high;
+ iocmd->attr.qos_bw.med = fcport->cfg.qos_bw.med;
+ iocmd->attr.qos_bw.low = fcport->cfg.qos_bw.low;
+ iocmd->attr.qos_bw_op = fcport->qos_attr.qos_bw_op;
+ iocmd->status = BFA_STATUS_OK;
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_qos_get_vc_attr(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_qos_vc_attr_s *iocmd =
+ (struct bfa_bsg_qos_vc_attr_s *)cmd;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+ struct bfa_qos_vc_attr_s *bfa_vc_attr = &fcport->qos_vc_attr;
+ unsigned long flags;
+ u32 i = 0;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->attr.total_vc_count = be16_to_cpu(bfa_vc_attr->total_vc_count);
+ iocmd->attr.shared_credit = be16_to_cpu(bfa_vc_attr->shared_credit);
+ iocmd->attr.elp_opmode_flags =
+ be32_to_cpu(bfa_vc_attr->elp_opmode_flags);
+
+ /* Individual VC info */
+ while (i < iocmd->attr.total_vc_count) {
+ iocmd->attr.vc_info[i].vc_credit =
+ bfa_vc_attr->vc_info[i].vc_credit;
+ iocmd->attr.vc_info[i].borrow_credit =
+ bfa_vc_attr->vc_info[i].borrow_credit;
+ iocmd->attr.vc_info[i].priority =
+ bfa_vc_attr->vc_info[i].priority;
+ i++;
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ iocmd->status = BFA_STATUS_OK;
+ return 0;
+}
+
+static int
+bfad_iocmd_qos_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcport_stats_s *iocmd =
+ (struct bfa_bsg_fcport_stats_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+ struct bfa_cb_pending_q_s cb_qe;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+
+ init_completion(&fcomp.comp);
+ bfa_pending_q_init_status(&cb_qe, bfad_hcb_comp, &fcomp, &iocmd->stats);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ WARN_ON(!bfa_ioc_get_fcmode(&bfad->bfa.ioc));
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) &&
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else
+ iocmd->status = bfa_fcport_get_stats(&bfad->bfa, &cb_qe);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ goto out;
+ }
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_qos_reset_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+ struct bfa_cb_pending_q_s cb_qe;
+ struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa);
+
+ init_completion(&fcomp.comp);
+ bfa_pending_q_init_status(&cb_qe, bfad_hcb_comp, &fcomp, NULL);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ WARN_ON(!bfa_ioc_get_fcmode(&bfad->bfa.ioc));
+ if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) &&
+ (fcport->topology == BFA_PORT_TOPOLOGY_LOOP))
+ iocmd->status = BFA_STATUS_TOPOLOGY_LOOP;
+ else
+ iocmd->status = bfa_fcport_clear_stats(&bfad->bfa, &cb_qe);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status != BFA_STATUS_OK) {
+ bfa_trc(bfad, iocmd->status);
+ goto out;
+ }
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_vf_get_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_vf_stats_s *iocmd =
+ (struct bfa_bsg_vf_stats_s *)cmd;
+ struct bfa_fcs_fabric_s *fcs_vf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_vf = bfa_fcs_vf_lookup(&bfad->bfa_fcs, iocmd->vf_id);
+ if (fcs_vf == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_VFID;
+ goto out;
+ }
+ memcpy((void *)&iocmd->stats, (void *)&fcs_vf->stats,
+ sizeof(struct bfa_vf_stats_s));
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+static int
+bfad_iocmd_vf_clr_stats(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_vf_reset_stats_s *iocmd =
+ (struct bfa_bsg_vf_reset_stats_s *)cmd;
+ struct bfa_fcs_fabric_s *fcs_vf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_vf = bfa_fcs_vf_lookup(&bfad->bfa_fcs, iocmd->vf_id);
+ if (fcs_vf == NULL) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_UNKNOWN_VFID;
+ goto out;
+ }
+ memset((void *)&fcs_vf->stats, 0, sizeof(struct bfa_vf_stats_s));
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ iocmd->status = BFA_STATUS_OK;
+out:
+ return 0;
+}
+
+/*
+ * Set the SCSI device sdev_bflags - sdev_bflags are used by the
+ * SCSI mid-layer to choose LUN Scanning mode REPORT_LUNS vs. Sequential Scan
+ *
+ * Internally iterates over all the ITNIM's part of the im_port & sets the
+ * sdev_bflags for the scsi_device associated with LUN #0.
+ */
+static void bfad_reset_sdev_bflags(struct bfad_im_port_s *im_port,
+ int lunmask_cfg)
+{
+ const blist_flags_t scan_flags = BLIST_NOREPORTLUN | BLIST_SPARSELUN;
+ struct bfad_itnim_s *itnim;
+ struct scsi_device *sdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(im_port->shost->host_lock, flags);
+ list_for_each_entry(itnim, &im_port->itnim_mapped_list, list_entry) {
+ sdev = __scsi_device_lookup(im_port->shost, itnim->channel,
+ itnim->scsi_tgt_id, 0);
+ if (sdev) {
+ if (lunmask_cfg == BFA_TRUE)
+ sdev->sdev_bflags |= scan_flags;
+ else
+ sdev->sdev_bflags &= ~scan_flags;
+ }
+ }
+ spin_unlock_irqrestore(im_port->shost->host_lock, flags);
+}
+
+/* Function to reset the LUN SCAN mode */
+static void
+bfad_iocmd_lunmask_reset_lunscan_mode(struct bfad_s *bfad, int lunmask_cfg)
+{
+ struct bfad_im_port_s *pport_im = bfad->pport.im_port;
+ struct bfad_vport_s *vport = NULL;
+
+ /* Set the scsi device LUN SCAN flags for base port */
+ bfad_reset_sdev_bflags(pport_im, lunmask_cfg);
+
+ /* Set the scsi device LUN SCAN flags for the vports */
+ list_for_each_entry(vport, &bfad->vport_list, list_entry)
+ bfad_reset_sdev_bflags(vport->drv_port.im_port, lunmask_cfg);
+}
+
+static int
+bfad_iocmd_lunmask(struct bfad_s *bfad, void *pcmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (v_cmd == IOCMD_FCPIM_LUNMASK_ENABLE) {
+ iocmd->status = bfa_fcpim_lunmask_update(&bfad->bfa, BFA_TRUE);
+ /* Set the LUN Scanning mode to be Sequential scan */
+ if (iocmd->status == BFA_STATUS_OK)
+ bfad_iocmd_lunmask_reset_lunscan_mode(bfad, BFA_TRUE);
+ } else if (v_cmd == IOCMD_FCPIM_LUNMASK_DISABLE) {
+ iocmd->status = bfa_fcpim_lunmask_update(&bfad->bfa, BFA_FALSE);
+ /* Set the LUN Scanning mode to default REPORT_LUNS scan */
+ if (iocmd->status == BFA_STATUS_OK)
+ bfad_iocmd_lunmask_reset_lunscan_mode(bfad, BFA_FALSE);
+ } else if (v_cmd == IOCMD_FCPIM_LUNMASK_CLEAR)
+ iocmd->status = bfa_fcpim_lunmask_clear(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_lunmask_query(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcpim_lunmask_query_s *iocmd =
+ (struct bfa_bsg_fcpim_lunmask_query_s *)cmd;
+ struct bfa_lunmask_cfg_s *lun_mask = &iocmd->lun_mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcpim_lunmask_query(&bfad->bfa, lun_mask);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_cfg_lunmask(struct bfad_s *bfad, void *cmd, unsigned int v_cmd)
+{
+ struct bfa_bsg_fcpim_lunmask_s *iocmd =
+ (struct bfa_bsg_fcpim_lunmask_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (v_cmd == IOCMD_FCPIM_LUNMASK_ADD)
+ iocmd->status = bfa_fcpim_lunmask_add(&bfad->bfa, iocmd->vf_id,
+ &iocmd->pwwn, iocmd->rpwwn, iocmd->lun);
+ else if (v_cmd == IOCMD_FCPIM_LUNMASK_DELETE)
+ iocmd->status = bfa_fcpim_lunmask_delete(&bfad->bfa,
+ iocmd->vf_id, &iocmd->pwwn,
+ iocmd->rpwwn, iocmd->lun);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_throttle_query(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcpim_throttle_s *iocmd =
+ (struct bfa_bsg_fcpim_throttle_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcpim_throttle_get(&bfad->bfa,
+ (void *)&iocmd->throttle);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_fcpim_throttle_set(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fcpim_throttle_s *iocmd =
+ (struct bfa_bsg_fcpim_throttle_s *)cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fcpim_throttle_set(&bfad->bfa,
+ iocmd->throttle.cfg_value);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_tfru_read(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_tfru_s *iocmd =
+ (struct bfa_bsg_tfru_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags = 0;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_tfru_read(BFA_FRU(&bfad->bfa),
+ &iocmd->data, iocmd->len, iocmd->offset,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status == BFA_STATUS_OK) {
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ }
+
+ return 0;
+}
+
+static int
+bfad_iocmd_tfru_write(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_tfru_s *iocmd =
+ (struct bfa_bsg_tfru_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags = 0;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_tfru_write(BFA_FRU(&bfad->bfa),
+ &iocmd->data, iocmd->len, iocmd->offset,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status == BFA_STATUS_OK) {
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ }
+
+ return 0;
+}
+
+static int
+bfad_iocmd_fruvpd_read(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fruvpd_s *iocmd =
+ (struct bfa_bsg_fruvpd_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags = 0;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fruvpd_read(BFA_FRU(&bfad->bfa),
+ &iocmd->data, iocmd->len, iocmd->offset,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status == BFA_STATUS_OK) {
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ }
+
+ return 0;
+}
+
+static int
+bfad_iocmd_fruvpd_update(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fruvpd_s *iocmd =
+ (struct bfa_bsg_fruvpd_s *)cmd;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags = 0;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fruvpd_update(BFA_FRU(&bfad->bfa),
+ &iocmd->data, iocmd->len, iocmd->offset,
+ bfad_hcb_comp, &fcomp, iocmd->trfr_cmpl);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (iocmd->status == BFA_STATUS_OK) {
+ wait_for_completion(&fcomp.comp);
+ iocmd->status = fcomp.status;
+ }
+
+ return 0;
+}
+
+static int
+bfad_iocmd_fruvpd_get_max_size(struct bfad_s *bfad, void *cmd)
+{
+ struct bfa_bsg_fruvpd_max_size_s *iocmd =
+ (struct bfa_bsg_fruvpd_max_size_s *)cmd;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ iocmd->status = bfa_fruvpd_get_max_size(BFA_FRU(&bfad->bfa),
+ &iocmd->max_size);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+}
+
+static int
+bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd,
+ unsigned int payload_len)
+{
+ int rc = -EINVAL;
+
+ switch (cmd) {
+ case IOCMD_IOC_ENABLE:
+ rc = bfad_iocmd_ioc_enable(bfad, iocmd);
+ break;
+ case IOCMD_IOC_DISABLE:
+ rc = bfad_iocmd_ioc_disable(bfad, iocmd);
+ break;
+ case IOCMD_IOC_GET_INFO:
+ rc = bfad_iocmd_ioc_get_info(bfad, iocmd);
+ break;
+ case IOCMD_IOC_GET_ATTR:
+ rc = bfad_iocmd_ioc_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_IOC_GET_STATS:
+ rc = bfad_iocmd_ioc_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_IOC_GET_FWSTATS:
+ rc = bfad_iocmd_ioc_get_fwstats(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_IOC_RESET_STATS:
+ case IOCMD_IOC_RESET_FWSTATS:
+ rc = bfad_iocmd_ioc_reset_stats(bfad, iocmd, cmd);
+ break;
+ case IOCMD_IOC_SET_ADAPTER_NAME:
+ case IOCMD_IOC_SET_PORT_NAME:
+ rc = bfad_iocmd_ioc_set_name(bfad, iocmd, cmd);
+ break;
+ case IOCMD_IOCFC_GET_ATTR:
+ rc = bfad_iocmd_iocfc_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_IOCFC_SET_INTR:
+ rc = bfad_iocmd_iocfc_set_intr(bfad, iocmd);
+ break;
+ case IOCMD_PORT_ENABLE:
+ rc = bfad_iocmd_port_enable(bfad, iocmd);
+ break;
+ case IOCMD_PORT_DISABLE:
+ rc = bfad_iocmd_port_disable(bfad, iocmd);
+ break;
+ case IOCMD_PORT_GET_ATTR:
+ rc = bfad_iocmd_port_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_PORT_GET_STATS:
+ rc = bfad_iocmd_port_get_stats(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_PORT_RESET_STATS:
+ rc = bfad_iocmd_port_reset_stats(bfad, iocmd);
+ break;
+ case IOCMD_PORT_CFG_TOPO:
+ case IOCMD_PORT_CFG_SPEED:
+ case IOCMD_PORT_CFG_ALPA:
+ case IOCMD_PORT_CLR_ALPA:
+ rc = bfad_iocmd_set_port_cfg(bfad, iocmd, cmd);
+ break;
+ case IOCMD_PORT_CFG_MAXFRSZ:
+ rc = bfad_iocmd_port_cfg_maxfrsize(bfad, iocmd);
+ break;
+ case IOCMD_PORT_BBCR_ENABLE:
+ case IOCMD_PORT_BBCR_DISABLE:
+ rc = bfad_iocmd_port_cfg_bbcr(bfad, cmd, iocmd);
+ break;
+ case IOCMD_PORT_BBCR_GET_ATTR:
+ rc = bfad_iocmd_port_get_bbcr_attr(bfad, iocmd);
+ break;
+ case IOCMD_LPORT_GET_ATTR:
+ rc = bfad_iocmd_lport_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_LPORT_GET_STATS:
+ rc = bfad_iocmd_lport_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_LPORT_RESET_STATS:
+ rc = bfad_iocmd_lport_reset_stats(bfad, iocmd);
+ break;
+ case IOCMD_LPORT_GET_IOSTATS:
+ rc = bfad_iocmd_lport_get_iostats(bfad, iocmd);
+ break;
+ case IOCMD_LPORT_GET_RPORTS:
+ rc = bfad_iocmd_lport_get_rports(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_RPORT_GET_ATTR:
+ rc = bfad_iocmd_rport_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_RPORT_GET_ADDR:
+ rc = bfad_iocmd_rport_get_addr(bfad, iocmd);
+ break;
+ case IOCMD_RPORT_GET_STATS:
+ rc = bfad_iocmd_rport_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_RPORT_RESET_STATS:
+ rc = bfad_iocmd_rport_clr_stats(bfad, iocmd);
+ break;
+ case IOCMD_RPORT_SET_SPEED:
+ rc = bfad_iocmd_rport_set_speed(bfad, iocmd);
+ break;
+ case IOCMD_VPORT_GET_ATTR:
+ rc = bfad_iocmd_vport_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_VPORT_GET_STATS:
+ rc = bfad_iocmd_vport_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_VPORT_RESET_STATS:
+ rc = bfad_iocmd_vport_clr_stats(bfad, iocmd);
+ break;
+ case IOCMD_FABRIC_GET_LPORTS:
+ rc = bfad_iocmd_fabric_get_lports(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_RATELIM_ENABLE:
+ case IOCMD_RATELIM_DISABLE:
+ rc = bfad_iocmd_ratelim(bfad, cmd, iocmd);
+ break;
+ case IOCMD_RATELIM_DEF_SPEED:
+ rc = bfad_iocmd_ratelim_speed(bfad, cmd, iocmd);
+ break;
+ case IOCMD_FCPIM_FAILOVER:
+ rc = bfad_iocmd_cfg_fcpim(bfad, iocmd);
+ break;
+ case IOCMD_FCPIM_MODSTATS:
+ rc = bfad_iocmd_fcpim_get_modstats(bfad, iocmd);
+ break;
+ case IOCMD_FCPIM_MODSTATSCLR:
+ rc = bfad_iocmd_fcpim_clr_modstats(bfad, iocmd);
+ break;
+ case IOCMD_FCPIM_DEL_ITN_STATS:
+ rc = bfad_iocmd_fcpim_get_del_itn_stats(bfad, iocmd);
+ break;
+ case IOCMD_ITNIM_GET_ATTR:
+ rc = bfad_iocmd_itnim_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_ITNIM_GET_IOSTATS:
+ rc = bfad_iocmd_itnim_get_iostats(bfad, iocmd);
+ break;
+ case IOCMD_ITNIM_RESET_STATS:
+ rc = bfad_iocmd_itnim_reset_stats(bfad, iocmd);
+ break;
+ case IOCMD_ITNIM_GET_ITNSTATS:
+ rc = bfad_iocmd_itnim_get_itnstats(bfad, iocmd);
+ break;
+ case IOCMD_FCPORT_ENABLE:
+ rc = bfad_iocmd_fcport_enable(bfad, iocmd);
+ break;
+ case IOCMD_FCPORT_DISABLE:
+ rc = bfad_iocmd_fcport_disable(bfad, iocmd);
+ break;
+ case IOCMD_IOC_PCIFN_CFG:
+ rc = bfad_iocmd_ioc_get_pcifn_cfg(bfad, iocmd);
+ break;
+ case IOCMD_IOC_FW_SIG_INV:
+ rc = bfad_iocmd_ioc_fw_sig_inv(bfad, iocmd);
+ break;
+ case IOCMD_PCIFN_CREATE:
+ rc = bfad_iocmd_pcifn_create(bfad, iocmd);
+ break;
+ case IOCMD_PCIFN_DELETE:
+ rc = bfad_iocmd_pcifn_delete(bfad, iocmd);
+ break;
+ case IOCMD_PCIFN_BW:
+ rc = bfad_iocmd_pcifn_bw(bfad, iocmd);
+ break;
+ case IOCMD_ADAPTER_CFG_MODE:
+ rc = bfad_iocmd_adapter_cfg_mode(bfad, iocmd);
+ break;
+ case IOCMD_PORT_CFG_MODE:
+ rc = bfad_iocmd_port_cfg_mode(bfad, iocmd);
+ break;
+ case IOCMD_FLASH_ENABLE_OPTROM:
+ case IOCMD_FLASH_DISABLE_OPTROM:
+ rc = bfad_iocmd_ablk_optrom(bfad, cmd, iocmd);
+ break;
+ case IOCMD_FAA_QUERY:
+ rc = bfad_iocmd_faa_query(bfad, iocmd);
+ break;
+ case IOCMD_CEE_GET_ATTR:
+ rc = bfad_iocmd_cee_attr(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_CEE_GET_STATS:
+ rc = bfad_iocmd_cee_get_stats(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_CEE_RESET_STATS:
+ rc = bfad_iocmd_cee_reset_stats(bfad, iocmd);
+ break;
+ case IOCMD_SFP_MEDIA:
+ rc = bfad_iocmd_sfp_media(bfad, iocmd);
+ break;
+ case IOCMD_SFP_SPEED:
+ rc = bfad_iocmd_sfp_speed(bfad, iocmd);
+ break;
+ case IOCMD_FLASH_GET_ATTR:
+ rc = bfad_iocmd_flash_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_FLASH_ERASE_PART:
+ rc = bfad_iocmd_flash_erase_part(bfad, iocmd);
+ break;
+ case IOCMD_FLASH_UPDATE_PART:
+ rc = bfad_iocmd_flash_update_part(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_FLASH_READ_PART:
+ rc = bfad_iocmd_flash_read_part(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_DIAG_TEMP:
+ rc = bfad_iocmd_diag_temp(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_MEMTEST:
+ rc = bfad_iocmd_diag_memtest(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_LOOPBACK:
+ rc = bfad_iocmd_diag_loopback(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_FWPING:
+ rc = bfad_iocmd_diag_fwping(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_QUEUETEST:
+ rc = bfad_iocmd_diag_queuetest(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_SFP:
+ rc = bfad_iocmd_diag_sfp(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_LED:
+ rc = bfad_iocmd_diag_led(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_BEACON_LPORT:
+ rc = bfad_iocmd_diag_beacon_lport(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_LB_STAT:
+ rc = bfad_iocmd_diag_lb_stat(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_DPORT_ENABLE:
+ rc = bfad_iocmd_diag_dport_enable(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_DPORT_DISABLE:
+ rc = bfad_iocmd_diag_dport_disable(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_DPORT_SHOW:
+ rc = bfad_iocmd_diag_dport_show(bfad, iocmd);
+ break;
+ case IOCMD_DIAG_DPORT_START:
+ rc = bfad_iocmd_diag_dport_start(bfad, iocmd);
+ break;
+ case IOCMD_PHY_GET_ATTR:
+ rc = bfad_iocmd_phy_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_PHY_GET_STATS:
+ rc = bfad_iocmd_phy_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_PHY_UPDATE_FW:
+ rc = bfad_iocmd_phy_update(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_PHY_READ_FW:
+ rc = bfad_iocmd_phy_read(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_VHBA_QUERY:
+ rc = bfad_iocmd_vhba_query(bfad, iocmd);
+ break;
+ case IOCMD_DEBUG_PORTLOG:
+ rc = bfad_iocmd_porglog_get(bfad, iocmd);
+ break;
+ case IOCMD_DEBUG_FW_CORE:
+ rc = bfad_iocmd_debug_fw_core(bfad, iocmd, payload_len);
+ break;
+ case IOCMD_DEBUG_FW_STATE_CLR:
+ case IOCMD_DEBUG_PORTLOG_CLR:
+ case IOCMD_DEBUG_START_DTRC:
+ case IOCMD_DEBUG_STOP_DTRC:
+ rc = bfad_iocmd_debug_ctl(bfad, iocmd, cmd);
+ break;
+ case IOCMD_DEBUG_PORTLOG_CTL:
+ rc = bfad_iocmd_porglog_ctl(bfad, iocmd);
+ break;
+ case IOCMD_FCPIM_PROFILE_ON:
+ case IOCMD_FCPIM_PROFILE_OFF:
+ rc = bfad_iocmd_fcpim_cfg_profile(bfad, iocmd, cmd);
+ break;
+ case IOCMD_ITNIM_GET_IOPROFILE:
+ rc = bfad_iocmd_itnim_get_ioprofile(bfad, iocmd);
+ break;
+ case IOCMD_FCPORT_GET_STATS:
+ rc = bfad_iocmd_fcport_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_FCPORT_RESET_STATS:
+ rc = bfad_iocmd_fcport_reset_stats(bfad, iocmd);
+ break;
+ case IOCMD_BOOT_CFG:
+ rc = bfad_iocmd_boot_cfg(bfad, iocmd);
+ break;
+ case IOCMD_BOOT_QUERY:
+ rc = bfad_iocmd_boot_query(bfad, iocmd);
+ break;
+ case IOCMD_PREBOOT_QUERY:
+ rc = bfad_iocmd_preboot_query(bfad, iocmd);
+ break;
+ case IOCMD_ETHBOOT_CFG:
+ rc = bfad_iocmd_ethboot_cfg(bfad, iocmd);
+ break;
+ case IOCMD_ETHBOOT_QUERY:
+ rc = bfad_iocmd_ethboot_query(bfad, iocmd);
+ break;
+ case IOCMD_TRUNK_ENABLE:
+ case IOCMD_TRUNK_DISABLE:
+ rc = bfad_iocmd_cfg_trunk(bfad, iocmd, cmd);
+ break;
+ case IOCMD_TRUNK_GET_ATTR:
+ rc = bfad_iocmd_trunk_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_QOS_ENABLE:
+ case IOCMD_QOS_DISABLE:
+ rc = bfad_iocmd_qos(bfad, iocmd, cmd);
+ break;
+ case IOCMD_QOS_GET_ATTR:
+ rc = bfad_iocmd_qos_get_attr(bfad, iocmd);
+ break;
+ case IOCMD_QOS_GET_VC_ATTR:
+ rc = bfad_iocmd_qos_get_vc_attr(bfad, iocmd);
+ break;
+ case IOCMD_QOS_GET_STATS:
+ rc = bfad_iocmd_qos_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_QOS_RESET_STATS:
+ rc = bfad_iocmd_qos_reset_stats(bfad, iocmd);
+ break;
+ case IOCMD_QOS_SET_BW:
+ rc = bfad_iocmd_qos_set_bw(bfad, iocmd);
+ break;
+ case IOCMD_VF_GET_STATS:
+ rc = bfad_iocmd_vf_get_stats(bfad, iocmd);
+ break;
+ case IOCMD_VF_RESET_STATS:
+ rc = bfad_iocmd_vf_clr_stats(bfad, iocmd);
+ break;
+ case IOCMD_FCPIM_LUNMASK_ENABLE:
+ case IOCMD_FCPIM_LUNMASK_DISABLE:
+ case IOCMD_FCPIM_LUNMASK_CLEAR:
+ rc = bfad_iocmd_lunmask(bfad, iocmd, cmd);
+ break;
+ case IOCMD_FCPIM_LUNMASK_QUERY:
+ rc = bfad_iocmd_fcpim_lunmask_query(bfad, iocmd);
+ break;
+ case IOCMD_FCPIM_LUNMASK_ADD:
+ case IOCMD_FCPIM_LUNMASK_DELETE:
+ rc = bfad_iocmd_fcpim_cfg_lunmask(bfad, iocmd, cmd);
+ break;
+ case IOCMD_FCPIM_THROTTLE_QUERY:
+ rc = bfad_iocmd_fcpim_throttle_query(bfad, iocmd);
+ break;
+ case IOCMD_FCPIM_THROTTLE_SET:
+ rc = bfad_iocmd_fcpim_throttle_set(bfad, iocmd);
+ break;
+ /* TFRU */
+ case IOCMD_TFRU_READ:
+ rc = bfad_iocmd_tfru_read(bfad, iocmd);
+ break;
+ case IOCMD_TFRU_WRITE:
+ rc = bfad_iocmd_tfru_write(bfad, iocmd);
+ break;
+ /* FRU */
+ case IOCMD_FRUVPD_READ:
+ rc = bfad_iocmd_fruvpd_read(bfad, iocmd);
+ break;
+ case IOCMD_FRUVPD_UPDATE:
+ rc = bfad_iocmd_fruvpd_update(bfad, iocmd);
+ break;
+ case IOCMD_FRUVPD_GET_MAX_SIZE:
+ rc = bfad_iocmd_fruvpd_get_max_size(bfad, iocmd);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int
+bfad_im_bsg_vendor_request(struct bsg_job *job)
+{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ uint32_t vendor_cmd = bsg_request->rqst_data.h_vendor.vendor_cmd[0];
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
+ struct bfad_im_port_s *im_port = bfad_get_im_port(shost);
+ struct bfad_s *bfad = im_port->bfad;
+ void *payload_kbuf;
+ int rc = -EINVAL;
+
+ /* Allocate a temp buffer to hold the passed in user space command */
+ payload_kbuf = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
+ if (!payload_kbuf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Copy the sg_list passed in to a linear buffer: holds the cmnd data */
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt, payload_kbuf,
+ job->request_payload.payload_len);
+
+ /* Invoke IOCMD handler - to handle all the vendor command requests */
+ rc = bfad_iocmd_handler(bfad, vendor_cmd, payload_kbuf,
+ job->request_payload.payload_len);
+ if (rc != BFA_STATUS_OK)
+ goto error;
+
+ /* Copy the response data to the job->reply_payload sg_list */
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ payload_kbuf,
+ job->reply_payload.payload_len);
+
+ /* free the command buffer */
+ kfree(payload_kbuf);
+
+ /* Fill the BSG job reply data */
+ job->reply_len = job->reply_payload.payload_len;
+ bsg_reply->reply_payload_rcv_len = job->reply_payload.payload_len;
+ bsg_reply->result = rc;
+
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ return rc;
+error:
+ /* free the command buffer */
+ kfree(payload_kbuf);
+out:
+ bsg_reply->result = rc;
+ job->reply_len = sizeof(uint32_t);
+ bsg_reply->reply_payload_rcv_len = 0;
+ return rc;
+}
+
+/* FC passthru call backs */
+static u64
+bfad_fcxp_get_req_sgaddr_cb(void *bfad_fcxp, int sgeid)
+{
+ struct bfad_fcxp *drv_fcxp = bfad_fcxp;
+ struct bfa_sge_s *sge;
+ u64 addr;
+
+ sge = drv_fcxp->req_sge + sgeid;
+ addr = (u64)(size_t) sge->sg_addr;
+ return addr;
+}
+
+static u32
+bfad_fcxp_get_req_sglen_cb(void *bfad_fcxp, int sgeid)
+{
+ struct bfad_fcxp *drv_fcxp = bfad_fcxp;
+ struct bfa_sge_s *sge;
+
+ sge = drv_fcxp->req_sge + sgeid;
+ return sge->sg_len;
+}
+
+static u64
+bfad_fcxp_get_rsp_sgaddr_cb(void *bfad_fcxp, int sgeid)
+{
+ struct bfad_fcxp *drv_fcxp = bfad_fcxp;
+ struct bfa_sge_s *sge;
+ u64 addr;
+
+ sge = drv_fcxp->rsp_sge + sgeid;
+ addr = (u64)(size_t) sge->sg_addr;
+ return addr;
+}
+
+static u32
+bfad_fcxp_get_rsp_sglen_cb(void *bfad_fcxp, int sgeid)
+{
+ struct bfad_fcxp *drv_fcxp = bfad_fcxp;
+ struct bfa_sge_s *sge;
+
+ sge = drv_fcxp->rsp_sge + sgeid;
+ return sge->sg_len;
+}
+
+static void
+bfad_send_fcpt_cb(void *bfad_fcxp, struct bfa_fcxp_s *fcxp, void *cbarg,
+ bfa_status_t req_status, u32 rsp_len, u32 resid_len,
+ struct fchs_s *rsp_fchs)
+{
+ struct bfad_fcxp *drv_fcxp = bfad_fcxp;
+
+ drv_fcxp->req_status = req_status;
+ drv_fcxp->rsp_len = rsp_len;
+
+ /* bfa_fcxp will be automatically freed by BFA */
+ drv_fcxp->bfa_fcxp = NULL;
+ complete(&drv_fcxp->comp);
+}
+
+static struct bfad_buf_info *
+bfad_fcxp_map_sg(struct bfad_s *bfad, void *payload_kbuf,
+ uint32_t payload_len, uint32_t *num_sgles)
+{
+ struct bfad_buf_info *buf_base, *buf_info;
+ struct bfa_sge_s *sg_table;
+ int sge_num = 1;
+
+ buf_base = kcalloc(sizeof(struct bfad_buf_info) +
+ sizeof(struct bfa_sge_s),
+ sge_num, GFP_KERNEL);
+ if (!buf_base)
+ return NULL;
+
+ sg_table = (struct bfa_sge_s *) (((uint8_t *)buf_base) +
+ (sizeof(struct bfad_buf_info) * sge_num));
+
+ /* Allocate dma coherent memory */
+ buf_info = buf_base;
+ buf_info->size = payload_len;
+ buf_info->virt = dma_alloc_coherent(&bfad->pcidev->dev,
+ buf_info->size, &buf_info->phys,
+ GFP_KERNEL);
+ if (!buf_info->virt)
+ goto out_free_mem;
+
+ /* copy the linear bsg buffer to buf_info */
+ memcpy(buf_info->virt, payload_kbuf, buf_info->size);
+
+ /*
+ * Setup SG table
+ */
+ sg_table->sg_len = buf_info->size;
+ sg_table->sg_addr = (void *)(size_t) buf_info->phys;
+
+ *num_sgles = sge_num;
+
+ return buf_base;
+
+out_free_mem:
+ kfree(buf_base);
+ return NULL;
+}
+
+static void
+bfad_fcxp_free_mem(struct bfad_s *bfad, struct bfad_buf_info *buf_base,
+ uint32_t num_sgles)
+{
+ int i;
+ struct bfad_buf_info *buf_info = buf_base;
+
+ if (buf_base) {
+ for (i = 0; i < num_sgles; buf_info++, i++) {
+ if (buf_info->virt != NULL)
+ dma_free_coherent(&bfad->pcidev->dev,
+ buf_info->size, buf_info->virt,
+ buf_info->phys);
+ }
+ kfree(buf_base);
+ }
+}
+
+static int
+bfad_fcxp_bsg_send(struct bsg_job *job, struct bfad_fcxp *drv_fcxp,
+ bfa_bsg_fcpt_t *bsg_fcpt)
+{
+ struct bfa_fcxp_s *hal_fcxp;
+ struct bfad_s *bfad = drv_fcxp->port->bfad;
+ unsigned long flags;
+ uint8_t lp_tag;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ /* Allocate bfa_fcxp structure */
+ hal_fcxp = bfa_fcxp_req_rsp_alloc(drv_fcxp, &bfad->bfa,
+ drv_fcxp->num_req_sgles,
+ drv_fcxp->num_rsp_sgles,
+ bfad_fcxp_get_req_sgaddr_cb,
+ bfad_fcxp_get_req_sglen_cb,
+ bfad_fcxp_get_rsp_sgaddr_cb,
+ bfad_fcxp_get_rsp_sglen_cb, BFA_TRUE);
+ if (!hal_fcxp) {
+ bfa_trc(bfad, 0);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return BFA_STATUS_ENOMEM;
+ }
+
+ drv_fcxp->bfa_fcxp = hal_fcxp;
+
+ lp_tag = bfa_lps_get_tag_from_pid(&bfad->bfa, bsg_fcpt->fchs.s_id);
+
+ bfa_fcxp_send(hal_fcxp, drv_fcxp->bfa_rport, bsg_fcpt->vf_id, lp_tag,
+ bsg_fcpt->cts, bsg_fcpt->cos,
+ job->request_payload.payload_len,
+ &bsg_fcpt->fchs, bfad_send_fcpt_cb, bfad,
+ job->reply_payload.payload_len, bsg_fcpt->tsecs);
+
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return BFA_STATUS_OK;
+}
+
+static int
+bfad_im_bsg_els_ct_request(struct bsg_job *job)
+{
+ struct bfa_bsg_data *bsg_data;
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
+ struct bfad_im_port_s *im_port = bfad_get_im_port(shost);
+ struct bfad_s *bfad = im_port->bfad;
+ bfa_bsg_fcpt_t *bsg_fcpt;
+ struct bfad_fcxp *drv_fcxp;
+ struct bfa_fcs_lport_s *fcs_port;
+ struct bfa_fcs_rport_s *fcs_rport;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ uint32_t command_type = bsg_request->msgcode;
+ unsigned long flags;
+ struct bfad_buf_info *rsp_buf_info;
+ void *req_kbuf = NULL, *rsp_kbuf = NULL;
+ int rc = -EINVAL;
+
+ job->reply_len = sizeof(uint32_t); /* Atleast uint32_t reply_len */
+ bsg_reply->reply_payload_rcv_len = 0;
+
+ /* Get the payload passed in from userspace */
+ bsg_data = (struct bfa_bsg_data *) (((char *)bsg_request) +
+ sizeof(struct fc_bsg_request));
+ if (bsg_data == NULL)
+ goto out;
+
+ /*
+ * Allocate buffer for bsg_fcpt and do a copy_from_user op for payload
+ * buffer of size bsg_data->payload_len
+ */
+ bsg_fcpt = kzalloc(bsg_data->payload_len, GFP_KERNEL);
+ if (!bsg_fcpt) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user((uint8_t *)bsg_fcpt,
+ (void *)(unsigned long)bsg_data->payload,
+ bsg_data->payload_len)) {
+ kfree(bsg_fcpt);
+ rc = -EIO;
+ goto out;
+ }
+
+ drv_fcxp = kzalloc(sizeof(struct bfad_fcxp), GFP_KERNEL);
+ if (drv_fcxp == NULL) {
+ kfree(bsg_fcpt);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ fcs_port = bfa_fcs_lookup_port(&bfad->bfa_fcs, bsg_fcpt->vf_id,
+ bsg_fcpt->lpwwn);
+ if (fcs_port == NULL) {
+ bsg_fcpt->status = BFA_STATUS_UNKNOWN_LWWN;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ goto out_free_mem;
+ }
+
+ /* Check if the port is online before sending FC Passthru cmd */
+ if (!bfa_fcs_lport_is_online(fcs_port)) {
+ bsg_fcpt->status = BFA_STATUS_PORT_OFFLINE;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ goto out_free_mem;
+ }
+
+ drv_fcxp->port = fcs_port->bfad_port;
+
+ if (!drv_fcxp->port->bfad)
+ drv_fcxp->port->bfad = bfad;
+
+ /* Fetch the bfa_rport - if nexus needed */
+ if (command_type == FC_BSG_HST_ELS_NOLOGIN ||
+ command_type == FC_BSG_HST_CT) {
+ /* BSG HST commands: no nexus needed */
+ drv_fcxp->bfa_rport = NULL;
+
+ } else if (command_type == FC_BSG_RPT_ELS ||
+ command_type == FC_BSG_RPT_CT) {
+ /* BSG RPT commands: nexus needed */
+ fcs_rport = bfa_fcs_lport_get_rport_by_pwwn(fcs_port,
+ bsg_fcpt->dpwwn);
+ if (fcs_rport == NULL) {
+ bsg_fcpt->status = BFA_STATUS_UNKNOWN_RWWN;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ goto out_free_mem;
+ }
+
+ drv_fcxp->bfa_rport = fcs_rport->bfa_rport;
+
+ } else { /* Unknown BSG msgcode; return -EINVAL */
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ goto out_free_mem;
+ }
+
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ /* allocate memory for req / rsp buffers */
+ req_kbuf = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
+ if (!req_kbuf) {
+ printk(KERN_INFO "bfa %s: fcpt request buffer alloc failed\n",
+ bfad->pci_name);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+
+ rsp_kbuf = kzalloc(job->reply_payload.payload_len, GFP_KERNEL);
+ if (!rsp_kbuf) {
+ printk(KERN_INFO "bfa %s: fcpt response buffer alloc failed\n",
+ bfad->pci_name);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+
+ /* map req sg - copy the sg_list passed in to the linear buffer */
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt, req_kbuf,
+ job->request_payload.payload_len);
+
+ drv_fcxp->reqbuf_info = bfad_fcxp_map_sg(bfad, req_kbuf,
+ job->request_payload.payload_len,
+ &drv_fcxp->num_req_sgles);
+ if (!drv_fcxp->reqbuf_info) {
+ printk(KERN_INFO "bfa %s: fcpt request fcxp_map_sg failed\n",
+ bfad->pci_name);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+
+ drv_fcxp->req_sge = (struct bfa_sge_s *)
+ (((uint8_t *)drv_fcxp->reqbuf_info) +
+ (sizeof(struct bfad_buf_info) *
+ drv_fcxp->num_req_sgles));
+
+ /* map rsp sg */
+ drv_fcxp->rspbuf_info = bfad_fcxp_map_sg(bfad, rsp_kbuf,
+ job->reply_payload.payload_len,
+ &drv_fcxp->num_rsp_sgles);
+ if (!drv_fcxp->rspbuf_info) {
+ printk(KERN_INFO "bfa %s: fcpt response fcxp_map_sg failed\n",
+ bfad->pci_name);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+
+ rsp_buf_info = (struct bfad_buf_info *)drv_fcxp->rspbuf_info;
+ drv_fcxp->rsp_sge = (struct bfa_sge_s *)
+ (((uint8_t *)drv_fcxp->rspbuf_info) +
+ (sizeof(struct bfad_buf_info) *
+ drv_fcxp->num_rsp_sgles));
+
+ /* fcxp send */
+ init_completion(&drv_fcxp->comp);
+ rc = bfad_fcxp_bsg_send(job, drv_fcxp, bsg_fcpt);
+ if (rc == BFA_STATUS_OK) {
+ wait_for_completion(&drv_fcxp->comp);
+ bsg_fcpt->status = drv_fcxp->req_status;
+ } else {
+ bsg_fcpt->status = rc;
+ goto out_free_mem;
+ }
+
+ /* fill the job->reply data */
+ if (drv_fcxp->req_status == BFA_STATUS_OK) {
+ job->reply_len = drv_fcxp->rsp_len;
+ bsg_reply->reply_payload_rcv_len = drv_fcxp->rsp_len;
+ bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
+ } else {
+ bsg_reply->reply_payload_rcv_len =
+ sizeof(struct fc_bsg_ctels_reply);
+ job->reply_len = sizeof(uint32_t);
+ bsg_reply->reply_data.ctels_reply.status =
+ FC_CTELS_STATUS_REJECT;
+ }
+
+ /* Copy the response data to the reply_payload sg list */
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ (uint8_t *)rsp_buf_info->virt,
+ job->reply_payload.payload_len);
+
+out_free_mem:
+ bfad_fcxp_free_mem(bfad, drv_fcxp->rspbuf_info,
+ drv_fcxp->num_rsp_sgles);
+ bfad_fcxp_free_mem(bfad, drv_fcxp->reqbuf_info,
+ drv_fcxp->num_req_sgles);
+ kfree(req_kbuf);
+ kfree(rsp_kbuf);
+
+ /* Need a copy to user op */
+ if (copy_to_user((void *)(unsigned long)bsg_data->payload,
+ (void *)bsg_fcpt, bsg_data->payload_len))
+ rc = -EIO;
+
+ kfree(bsg_fcpt);
+ kfree(drv_fcxp);
+out:
+ bsg_reply->result = rc;
+
+ if (rc == BFA_STATUS_OK)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ return rc;
+}
+
+int
+bfad_im_bsg_request(struct bsg_job *job)
+{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ uint32_t rc = BFA_STATUS_OK;
+
+ switch (bsg_request->msgcode) {
+ case FC_BSG_HST_VENDOR:
+ /* Process BSG HST Vendor requests */
+ rc = bfad_im_bsg_vendor_request(job);
+ break;
+ case FC_BSG_HST_ELS_NOLOGIN:
+ case FC_BSG_RPT_ELS:
+ case FC_BSG_HST_CT:
+ case FC_BSG_RPT_CT:
+ /* Process BSG ELS/CT commands */
+ rc = bfad_im_bsg_els_ct_request(job);
+ break;
+ default:
+ bsg_reply->result = rc = -EINVAL;
+ bsg_reply->reply_payload_rcv_len = 0;
+ break;
+ }
+
+ return rc;
+}
+
+int
+bfad_im_bsg_timeout(struct bsg_job *job)
+{
+ /* Don't complete the BSG job request - return -EAGAIN
+ * to reset bsg job timeout : for ELS/CT pass thru we
+ * already have timer to track the request.
+ */
+ return -EAGAIN;
+}
diff --git a/drivers/scsi/bfa/bfad_bsg.h b/drivers/scsi/bfa/bfad_bsg.h
new file mode 100644
index 000000000000..e525339df4ae
--- /dev/null
+++ b/drivers/scsi/bfa/bfad_bsg.h
@@ -0,0 +1,829 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
+ * All rights reserved
+ * www.qlogic.com
+ *
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
+ */
+#ifndef BFAD_BSG_H
+#define BFAD_BSG_H
+
+#include "bfa_defs.h"
+#include "bfa_defs_fcs.h"
+
+/* Definitions of vendor unique structures and command codes passed in
+ * using FC_BSG_HST_VENDOR message code.
+ */
+enum {
+ IOCMD_IOC_ENABLE = 0x1,
+ IOCMD_IOC_DISABLE,
+ IOCMD_IOC_GET_ATTR,
+ IOCMD_IOC_GET_INFO,
+ IOCMD_IOC_GET_STATS,
+ IOCMD_IOC_GET_FWSTATS,
+ IOCMD_IOC_RESET_STATS,
+ IOCMD_IOC_RESET_FWSTATS,
+ IOCMD_IOC_SET_ADAPTER_NAME,
+ IOCMD_IOC_SET_PORT_NAME,
+ IOCMD_IOC_FW_SIG_INV,
+ IOCMD_IOCFC_GET_ATTR,
+ IOCMD_IOCFC_SET_INTR,
+ IOCMD_PORT_ENABLE,
+ IOCMD_PORT_DISABLE,
+ IOCMD_PORT_GET_ATTR,
+ IOCMD_PORT_GET_STATS,
+ IOCMD_PORT_RESET_STATS,
+ IOCMD_PORT_CFG_TOPO,
+ IOCMD_PORT_CFG_SPEED,
+ IOCMD_PORT_CFG_ALPA,
+ IOCMD_PORT_CFG_MAXFRSZ,
+ IOCMD_PORT_CLR_ALPA,
+ IOCMD_PORT_BBCR_ENABLE,
+ IOCMD_PORT_BBCR_DISABLE,
+ IOCMD_PORT_BBCR_GET_ATTR,
+ IOCMD_LPORT_GET_ATTR,
+ IOCMD_LPORT_GET_RPORTS,
+ IOCMD_LPORT_GET_STATS,
+ IOCMD_LPORT_RESET_STATS,
+ IOCMD_LPORT_GET_IOSTATS,
+ IOCMD_RPORT_GET_ATTR,
+ IOCMD_RPORT_GET_ADDR,
+ IOCMD_RPORT_GET_STATS,
+ IOCMD_RPORT_RESET_STATS,
+ IOCMD_RPORT_SET_SPEED,
+ IOCMD_VPORT_GET_ATTR,
+ IOCMD_VPORT_GET_STATS,
+ IOCMD_VPORT_RESET_STATS,
+ IOCMD_FABRIC_GET_LPORTS,
+ IOCMD_RATELIM_ENABLE,
+ IOCMD_RATELIM_DISABLE,
+ IOCMD_RATELIM_DEF_SPEED,
+ IOCMD_FCPIM_FAILOVER,
+ IOCMD_FCPIM_MODSTATS,
+ IOCMD_FCPIM_MODSTATSCLR,
+ IOCMD_FCPIM_DEL_ITN_STATS,
+ IOCMD_ITNIM_GET_ATTR,
+ IOCMD_ITNIM_GET_IOSTATS,
+ IOCMD_ITNIM_RESET_STATS,
+ IOCMD_ITNIM_GET_ITNSTATS,
+ IOCMD_IOC_PCIFN_CFG,
+ IOCMD_FCPORT_ENABLE,
+ IOCMD_FCPORT_DISABLE,
+ IOCMD_PCIFN_CREATE,
+ IOCMD_PCIFN_DELETE,
+ IOCMD_PCIFN_BW,
+ IOCMD_ADAPTER_CFG_MODE,
+ IOCMD_PORT_CFG_MODE,
+ IOCMD_FLASH_ENABLE_OPTROM,
+ IOCMD_FLASH_DISABLE_OPTROM,
+ IOCMD_FAA_QUERY,
+ IOCMD_CEE_GET_ATTR,
+ IOCMD_CEE_GET_STATS,
+ IOCMD_CEE_RESET_STATS,
+ IOCMD_SFP_MEDIA,
+ IOCMD_SFP_SPEED,
+ IOCMD_FLASH_GET_ATTR,
+ IOCMD_FLASH_ERASE_PART,
+ IOCMD_FLASH_UPDATE_PART,
+ IOCMD_FLASH_READ_PART,
+ IOCMD_DIAG_TEMP,
+ IOCMD_DIAG_MEMTEST,
+ IOCMD_DIAG_LOOPBACK,
+ IOCMD_DIAG_FWPING,
+ IOCMD_DIAG_QUEUETEST,
+ IOCMD_DIAG_SFP,
+ IOCMD_DIAG_LED,
+ IOCMD_DIAG_BEACON_LPORT,
+ IOCMD_DIAG_LB_STAT,
+ IOCMD_PHY_GET_ATTR,
+ IOCMD_PHY_GET_STATS,
+ IOCMD_PHY_UPDATE_FW,
+ IOCMD_PHY_READ_FW,
+ IOCMD_VHBA_QUERY,
+ IOCMD_DEBUG_PORTLOG,
+ IOCMD_DEBUG_FW_CORE,
+ IOCMD_DEBUG_FW_STATE_CLR,
+ IOCMD_DEBUG_PORTLOG_CLR,
+ IOCMD_DEBUG_START_DTRC,
+ IOCMD_DEBUG_STOP_DTRC,
+ IOCMD_DEBUG_PORTLOG_CTL,
+ IOCMD_FCPIM_PROFILE_ON,
+ IOCMD_FCPIM_PROFILE_OFF,
+ IOCMD_ITNIM_GET_IOPROFILE,
+ IOCMD_FCPORT_GET_STATS,
+ IOCMD_FCPORT_RESET_STATS,
+ IOCMD_BOOT_CFG,
+ IOCMD_BOOT_QUERY,
+ IOCMD_PREBOOT_QUERY,
+ IOCMD_ETHBOOT_CFG,
+ IOCMD_ETHBOOT_QUERY,
+ IOCMD_TRUNK_ENABLE,
+ IOCMD_TRUNK_DISABLE,
+ IOCMD_TRUNK_GET_ATTR,
+ IOCMD_QOS_ENABLE,
+ IOCMD_QOS_DISABLE,
+ IOCMD_QOS_GET_ATTR,
+ IOCMD_QOS_GET_VC_ATTR,
+ IOCMD_QOS_GET_STATS,
+ IOCMD_QOS_RESET_STATS,
+ IOCMD_VF_GET_STATS,
+ IOCMD_VF_RESET_STATS,
+ IOCMD_FCPIM_LUNMASK_ENABLE,
+ IOCMD_FCPIM_LUNMASK_DISABLE,
+ IOCMD_FCPIM_LUNMASK_CLEAR,
+ IOCMD_FCPIM_LUNMASK_QUERY,
+ IOCMD_FCPIM_LUNMASK_ADD,
+ IOCMD_FCPIM_LUNMASK_DELETE,
+ IOCMD_DIAG_DPORT_ENABLE,
+ IOCMD_DIAG_DPORT_DISABLE,
+ IOCMD_QOS_SET_BW,
+ IOCMD_FCPIM_THROTTLE_QUERY,
+ IOCMD_FCPIM_THROTTLE_SET,
+ IOCMD_TFRU_READ,
+ IOCMD_TFRU_WRITE,
+ IOCMD_FRUVPD_READ,
+ IOCMD_FRUVPD_UPDATE,
+ IOCMD_FRUVPD_GET_MAX_SIZE,
+ IOCMD_DIAG_DPORT_SHOW,
+ IOCMD_DIAG_DPORT_START,
+};
+
+struct bfa_bsg_gen_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+};
+
+struct bfa_bsg_portlogctl_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ bfa_boolean_t ctl;
+ int inst_no;
+};
+
+struct bfa_bsg_fcpim_profile_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+};
+
+struct bfa_bsg_itnim_ioprofile_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t lpwwn;
+ wwn_t rpwwn;
+ struct bfa_itnim_ioprofile_s ioprofile;
+};
+
+struct bfa_bsg_fcport_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ union bfa_fcport_stats_u stats;
+};
+
+struct bfa_bsg_ioc_name_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ char name[BFA_ADAPTER_SYM_NAME_LEN];
+};
+
+struct bfa_bsg_ioc_info_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ char serialnum[64];
+ char hwpath[BFA_STRING_32];
+ char adapter_hwpath[BFA_STRING_32];
+ char guid[BFA_ADAPTER_SYM_NAME_LEN*2];
+ char name[BFA_ADAPTER_SYM_NAME_LEN];
+ char port_name[BFA_ADAPTER_SYM_NAME_LEN];
+ char eth_name[BFA_ADAPTER_SYM_NAME_LEN];
+ wwn_t pwwn;
+ wwn_t nwwn;
+ wwn_t factorypwwn;
+ wwn_t factorynwwn;
+ mac_t mac;
+ mac_t factory_mac; /* Factory mac address */
+ mac_t current_mac; /* Currently assigned mac address */
+ enum bfa_ioc_type_e ioc_type;
+ u16 pvid; /* Port vlan id */
+ u16 rsvd1;
+ u32 host;
+ u32 bandwidth; /* For PF support */
+ u32 rsvd2;
+};
+
+struct bfa_bsg_ioc_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_ioc_attr_s ioc_attr;
+};
+
+struct bfa_bsg_ioc_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_ioc_stats_s ioc_stats;
+};
+
+struct bfa_bsg_ioc_fwstats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 buf_size;
+ u32 rsvd1;
+ u64 buf_ptr;
+};
+
+struct bfa_bsg_iocfc_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_iocfc_attr_s iocfc_attr;
+};
+
+struct bfa_bsg_iocfc_intr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_iocfc_intr_attr_s attr;
+};
+
+struct bfa_bsg_port_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_port_attr_s attr;
+};
+
+struct bfa_bsg_port_cfg_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 param;
+ u32 rsvd1;
+};
+
+struct bfa_bsg_port_cfg_maxfrsize_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 maxfrsize;
+};
+
+struct bfa_bsg_port_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 buf_size;
+ u32 rsvd1;
+ u64 buf_ptr;
+};
+
+struct bfa_bsg_lport_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ struct bfa_lport_attr_s port_attr;
+};
+
+struct bfa_bsg_lport_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ struct bfa_lport_stats_s port_stats;
+};
+
+struct bfa_bsg_lport_iostats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ struct bfa_itnim_iostats_s iostats;
+};
+
+struct bfa_bsg_lport_get_rports_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ u64 rbuf_ptr;
+ u32 nrports;
+ u32 rsvd;
+};
+
+struct bfa_bsg_rport_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ wwn_t rpwwn;
+ u32 pid;
+ u32 rsvd;
+ struct bfa_rport_attr_s attr;
+};
+
+struct bfa_bsg_rport_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ wwn_t rpwwn;
+ struct bfa_rport_stats_s stats;
+};
+
+struct bfa_bsg_rport_scsi_addr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ wwn_t rpwwn;
+ u32 host;
+ u32 bus;
+ u32 target;
+ u32 lun;
+};
+
+struct bfa_bsg_rport_reset_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ wwn_t rpwwn;
+};
+
+struct bfa_bsg_rport_set_speed_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ enum bfa_port_speed speed;
+ u32 rsvd;
+ wwn_t pwwn;
+ wwn_t rpwwn;
+};
+
+struct bfa_bsg_vport_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t vpwwn;
+ struct bfa_vport_attr_s vport_attr;
+};
+
+struct bfa_bsg_vport_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t vpwwn;
+ struct bfa_vport_stats_s vport_stats;
+};
+
+struct bfa_bsg_reset_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t vpwwn;
+};
+
+struct bfa_bsg_fabric_get_lports_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ u64 buf_ptr;
+ u32 nports;
+ u32 rsvd;
+};
+
+struct bfa_bsg_trl_speed_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ enum bfa_port_speed speed;
+};
+
+struct bfa_bsg_fcpim_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 param;
+};
+
+struct bfa_bsg_fcpim_modstats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ struct bfa_itnim_iostats_s modstats;
+};
+
+struct bfa_bsg_fcpim_del_itn_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ struct bfa_fcpim_del_itn_stats_s modstats;
+};
+
+struct bfa_bsg_fcpim_modstatsclr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+};
+
+struct bfa_bsg_itnim_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t lpwwn;
+ wwn_t rpwwn;
+ struct bfa_itnim_attr_s attr;
+};
+
+struct bfa_bsg_itnim_iostats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t lpwwn;
+ wwn_t rpwwn;
+ struct bfa_itnim_iostats_s iostats;
+};
+
+struct bfa_bsg_itnim_itnstats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t lpwwn;
+ wwn_t rpwwn;
+ struct bfa_itnim_stats_s itnstats;
+};
+
+struct bfa_bsg_pcifn_cfg_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_ablk_cfg_s pcifn_cfg;
+};
+
+struct bfa_bsg_pcifn_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 pcifn_id;
+ u16 bw_min;
+ u16 bw_max;
+ u8 port;
+ enum bfi_pcifn_class pcifn_class;
+ u8 rsvd[1];
+};
+
+struct bfa_bsg_adapter_cfg_mode_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_adapter_cfg_mode_s cfg;
+};
+
+struct bfa_bsg_port_cfg_mode_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 instance;
+ struct bfa_port_cfg_mode_s cfg;
+};
+
+struct bfa_bsg_bbcr_enable_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u8 bb_scn;
+ u8 rsvd;
+};
+
+struct bfa_bsg_bbcr_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_bbcr_attr_s attr;
+};
+
+struct bfa_bsg_faa_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_faa_attr_s faa_attr;
+};
+
+struct bfa_bsg_cee_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 buf_size;
+ u32 rsvd1;
+ u64 buf_ptr;
+};
+
+struct bfa_bsg_cee_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 buf_size;
+ u32 rsvd1;
+ u64 buf_ptr;
+};
+
+struct bfa_bsg_sfp_media_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ enum bfa_defs_sfp_media_e media;
+};
+
+struct bfa_bsg_sfp_speed_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ enum bfa_port_speed speed;
+};
+
+struct bfa_bsg_flash_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_flash_attr_s attr;
+};
+
+struct bfa_bsg_flash_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u8 instance;
+ u8 rsvd;
+ enum bfa_flash_part_type type;
+ int bufsz;
+ u64 buf_ptr;
+};
+
+struct bfa_bsg_diag_get_temp_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_diag_results_tempsensor_s result;
+};
+
+struct bfa_bsg_diag_memtest_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd[3];
+ u32 pat;
+ struct bfa_diag_memtest_result result;
+ struct bfa_diag_memtest_s memtest;
+};
+
+struct bfa_bsg_diag_loopback_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ enum bfa_port_opmode opmode;
+ enum bfa_port_speed speed;
+ u32 lpcnt;
+ u32 pat;
+ struct bfa_diag_loopback_result_s result;
+};
+
+struct bfa_bsg_diag_dport_show_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_diag_dport_result_s result;
+};
+
+struct bfa_bsg_dport_enable_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u16 lpcnt;
+ u16 pat;
+};
+
+struct bfa_bsg_diag_fwping_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 cnt;
+ u32 pattern;
+ struct bfa_diag_results_fwping result;
+};
+
+struct bfa_bsg_diag_qtest_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 force;
+ u32 queue;
+ struct bfa_diag_qtest_result_s result;
+};
+
+struct bfa_bsg_sfp_show_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct sfp_mem_s sfp;
+};
+
+struct bfa_bsg_diag_led_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_diag_ledtest_s ledtest;
+};
+
+struct bfa_bsg_diag_beacon_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ bfa_boolean_t beacon;
+ bfa_boolean_t link_e2e_beacon;
+ u32 second;
+};
+
+struct bfa_bsg_diag_lb_stat_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+};
+
+struct bfa_bsg_phy_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 instance;
+ struct bfa_phy_attr_s attr;
+};
+
+struct bfa_bsg_phy_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 instance;
+ u64 bufsz;
+ u64 buf_ptr;
+};
+
+struct bfa_bsg_debug_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 bufsz;
+ int inst_no;
+ u64 buf_ptr;
+ u64 offset;
+};
+
+struct bfa_bsg_phy_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 instance;
+ struct bfa_phy_stats_s stats;
+};
+
+struct bfa_bsg_vhba_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 pcifn_id;
+ struct bfa_vhba_attr_s attr;
+};
+
+struct bfa_bsg_boot_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_boot_cfg_s cfg;
+};
+
+struct bfa_bsg_preboot_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_boot_pbc_s cfg;
+};
+
+struct bfa_bsg_ethboot_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_ethboot_cfg_s cfg;
+};
+
+struct bfa_bsg_trunk_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_trunk_attr_s attr;
+};
+
+struct bfa_bsg_qos_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_qos_attr_s attr;
+};
+
+struct bfa_bsg_qos_vc_attr_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_qos_vc_attr_s attr;
+};
+
+struct bfa_bsg_qos_bw_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ struct bfa_qos_bw_s qos_bw;
+};
+
+struct bfa_bsg_vf_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ struct bfa_vf_stats_s stats;
+};
+
+struct bfa_bsg_vf_reset_stats_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+};
+
+struct bfa_bsg_fcpim_lunmask_query_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ struct bfa_lunmask_cfg_s lun_mask;
+};
+
+struct bfa_bsg_fcpim_lunmask_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ wwn_t pwwn;
+ wwn_t rpwwn;
+ struct scsi_lun lun;
+};
+
+struct bfa_bsg_fcpim_throttle_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 vf_id;
+ struct bfa_defs_fcpim_throttle_s throttle;
+};
+
+#define BFA_TFRU_DATA_SIZE 64
+#define BFA_MAX_FRUVPD_TRANSFER_SIZE 0x1000
+
+struct bfa_bsg_tfru_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 offset;
+ u32 len;
+ u8 data[BFA_TFRU_DATA_SIZE];
+};
+
+struct bfa_bsg_fruvpd_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd1;
+ u32 offset;
+ u32 len;
+ u8 data[BFA_MAX_FRUVPD_TRANSFER_SIZE];
+ u8 trfr_cmpl;
+ u8 rsvd2[3];
+};
+
+struct bfa_bsg_fruvpd_max_size_s {
+ bfa_status_t status;
+ u16 bfad_num;
+ u16 rsvd;
+ u32 max_size;
+};
+
+struct bfa_bsg_fcpt_s {
+ bfa_status_t status;
+ u16 vf_id;
+ wwn_t lpwwn;
+ wwn_t dpwwn;
+ u32 tsecs;
+ int cts;
+ enum fc_cos cos;
+ struct fchs_s fchs;
+};
+#define bfa_bsg_fcpt_t struct bfa_bsg_fcpt_s
+
+#pragma pack(1)
+struct bfa_bsg_data {
+ int payload_len;
+ u64 payload;
+};
+#pragma pack()
+
+#define bfad_chk_iocmd_sz(__payload_len, __hdrsz, __bufsz) \
+ (((__payload_len) != ((__hdrsz) + (__bufsz))) ? \
+ BFA_STATUS_FAILED : BFA_STATUS_OK)
+
+#endif /* BFAD_BSG_H */
diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c
index 48be0c54f2de..f6dd077d47c9 100644
--- a/drivers/scsi/bfa/bfad_debugfs.c
+++ b/drivers/scsi/bfa/bfad_debugfs.c
@@ -1,21 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#include <linux/debugfs.h>
+#include <linux/export.h>
#include "bfad_drv.h"
#include "bfad_im.h"
@@ -79,7 +73,7 @@ bfad_debugfs_open_fwtrc(struct inode *inode, struct file *file)
fw_debug->buffer_len = sizeof(struct bfa_trc_mod_s);
- fw_debug->debug_buffer = vmalloc(fw_debug->buffer_len);
+ fw_debug->debug_buffer = vzalloc(fw_debug->buffer_len);
if (!fw_debug->debug_buffer) {
kfree(fw_debug);
printk(KERN_INFO "bfad[%d]: Failed to allocate fwtrc buffer\n",
@@ -87,8 +81,6 @@ bfad_debugfs_open_fwtrc(struct inode *inode, struct file *file)
return -ENOMEM;
}
- memset(fw_debug->debug_buffer, 0, fw_debug->buffer_len);
-
spin_lock_irqsave(&bfad->bfad_lock, flags);
rc = bfa_ioc_debug_fwtrc(&bfad->bfa.ioc,
fw_debug->debug_buffer,
@@ -123,7 +115,7 @@ bfad_debugfs_open_fwsave(struct inode *inode, struct file *file)
fw_debug->buffer_len = sizeof(struct bfa_trc_mod_s);
- fw_debug->debug_buffer = vmalloc(fw_debug->buffer_len);
+ fw_debug->debug_buffer = vzalloc(fw_debug->buffer_len);
if (!fw_debug->debug_buffer) {
kfree(fw_debug);
printk(KERN_INFO "bfad[%d]: Failed to allocate fwsave buffer\n",
@@ -131,8 +123,6 @@ bfad_debugfs_open_fwsave(struct inode *inode, struct file *file)
return -ENOMEM;
}
- memset(fw_debug->debug_buffer, 0, fw_debug->buffer_len);
-
spin_lock_irqsave(&bfad->bfad_lock, flags);
rc = bfa_ioc_debug_fwsave(&bfad->bfa.ioc,
fw_debug->debug_buffer,
@@ -172,31 +162,9 @@ bfad_debugfs_open_reg(struct inode *inode, struct file *file)
static loff_t
bfad_debugfs_lseek(struct file *file, loff_t offset, int orig)
{
- struct bfad_debug_info *debug;
- loff_t pos = file->f_pos;
-
- debug = file->private_data;
-
- switch (orig) {
- case 0:
- file->f_pos = offset;
- break;
- case 1:
- file->f_pos += offset;
- break;
- case 2:
- file->f_pos = debug->buffer_len - offset;
- break;
- default:
- return -EINVAL;
- }
-
- if (file->f_pos < 0 || file->f_pos > debug->buffer_len) {
- file->f_pos = pos;
- return -EINVAL;
- }
-
- return file->f_pos;
+ struct bfad_debug_info *debug = file->private_data;
+ return fixed_size_llseek(file, offset, orig,
+ debug->buffer_len);
}
static ssize_t
@@ -214,10 +182,10 @@ bfad_debugfs_read(struct file *file, char __user *buf,
#define BFA_REG_CT_ADDRSZ (0x40000)
#define BFA_REG_CB_ADDRSZ (0x20000)
-#define BFA_REG_ADDRSZ(__bfa) \
- ((bfa_ioc_devid(&(__bfa)->ioc) == BFA_PCI_DEVICE_ID_CT) ? \
- BFA_REG_CT_ADDRSZ : BFA_REG_CB_ADDRSZ)
-#define BFA_REG_ADDRMSK(__bfa) ((u32)(BFA_REG_ADDRSZ(__bfa) - 1))
+#define BFA_REG_ADDRSZ(__ioc) \
+ ((u32)(bfa_asic_id_ctc(bfa_ioc_devid(__ioc)) ? \
+ BFA_REG_CT_ADDRSZ : BFA_REG_CB_ADDRSZ))
+#define BFA_REG_ADDRMSK(__ioc) (BFA_REG_ADDRSZ(__ioc) - 1)
static bfa_status_t
bfad_reg_offset_check(struct bfa_s *bfa, u32 offset, u32 len)
@@ -236,7 +204,7 @@ bfad_reg_offset_check(struct bfa_s *bfa, u32 offset, u32 len)
return BFA_STATUS_EINVAL;
} else {
/* CB register space 64KB */
- if ((offset + (len<<2)) > BFA_REG_ADDRMSK(bfa))
+ if ((offset + (len<<2)) > BFA_REG_ADDRMSK(&bfa->ioc))
return BFA_STATUS_EINVAL;
}
return BFA_STATUS_OK;
@@ -275,27 +243,19 @@ bfad_debugfs_write_regrd(struct file *file, const char __user *buf,
struct bfad_s *bfad = port->bfad;
struct bfa_s *bfa = &bfad->bfa;
struct bfa_ioc_s *ioc = &bfa->ioc;
- int addr, len, rc, i;
+ int addr, rc, i;
+ u32 len;
u32 *regbuf;
void __iomem *rb, *reg_addr;
unsigned long flags;
void *kern_buf;
- kern_buf = kzalloc(nbytes, GFP_KERNEL);
-
- if (!kern_buf) {
- printk(KERN_INFO "bfad[%d]: Failed to allocate buffer\n",
- bfad->inst_no);
- return -ENOMEM;
- }
-
- if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) {
- kfree(kern_buf);
- return -ENOMEM;
- }
+ kern_buf = memdup_user_nul(buf, nbytes);
+ if (IS_ERR(kern_buf))
+ return PTR_ERR(kern_buf);
rc = sscanf(kern_buf, "%x:%x", &addr, &len);
- if (rc < 2) {
+ if (rc < 2 || len > (UINT_MAX >> 2)) {
printk(KERN_INFO
"bfad[%d]: %s failed to read user buf\n",
bfad->inst_no, __func__);
@@ -317,7 +277,7 @@ bfad_debugfs_write_regrd(struct file *file, const char __user *buf,
bfad->reglen = len << 2;
rb = bfa_ioc_bar0(ioc);
- addr &= BFA_REG_ADDRMSK(bfa);
+ addr &= BFA_REG_ADDRMSK(ioc);
/* offset and len sanity check */
rc = bfad_reg_offset_check(bfa, addr, len);
@@ -357,18 +317,9 @@ bfad_debugfs_write_regwr(struct file *file, const char __user *buf,
unsigned long flags;
void *kern_buf;
- kern_buf = kzalloc(nbytes, GFP_KERNEL);
-
- if (!kern_buf) {
- printk(KERN_INFO "bfad[%d]: Failed to allocate buffer\n",
- bfad->inst_no);
- return -ENOMEM;
- }
-
- if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) {
- kfree(kern_buf);
- return -ENOMEM;
- }
+ kern_buf = memdup_user_nul(buf, nbytes);
+ if (IS_ERR(kern_buf))
+ return PTR_ERR(kern_buf);
rc = sscanf(kern_buf, "%x:%x", &addr, &val);
if (rc < 2) {
@@ -380,7 +331,7 @@ bfad_debugfs_write_regwr(struct file *file, const char __user *buf,
}
kfree(kern_buf);
- addr &= BFA_REG_ADDRMSK(bfa); /* offset only 17 bit and word align */
+ addr &= BFA_REG_ADDRMSK(ioc); /* offset only 17 bit and word align */
/* offset and len sanity check */
rc = bfad_reg_offset_check(bfa, addr, 1);
@@ -420,8 +371,7 @@ bfad_debugfs_release_fwtrc(struct inode *inode, struct file *file)
if (!fw_debug)
return 0;
- if (fw_debug->debug_buffer)
- vfree(fw_debug->debug_buffer);
+ vfree(fw_debug->debug_buffer);
file->private_data = NULL;
kfree(fw_debug);
@@ -471,7 +421,7 @@ static const struct file_operations bfad_debugfs_op_regwr = {
struct bfad_debugfs_entry {
const char *name;
- mode_t mode;
+ umode_t mode;
const struct file_operations *fops;
};
@@ -501,11 +451,6 @@ bfad_debugfs_init(struct bfad_port_s *port)
if (!bfa_debugfs_root) {
bfa_debugfs_root = debugfs_create_dir("bfa", NULL);
atomic_set(&bfa_debugfs_port_count, 0);
- if (!bfa_debugfs_root) {
- printk(KERN_WARNING
- "BFA debugfs root dir creation failed\n");
- goto err;
- }
}
/* Setup the pci_dev debugfs directory for the port */
@@ -513,12 +458,6 @@ bfad_debugfs_init(struct bfad_port_s *port)
if (!port->port_debugfs_root) {
port->port_debugfs_root =
debugfs_create_dir(name, bfa_debugfs_root);
- if (!port->port_debugfs_root) {
- printk(KERN_WARNING
- "bfa %s: debugfs root creation failed\n",
- bfad->pci_name);
- goto err;
- }
atomic_inc(&bfa_debugfs_port_count);
@@ -530,16 +469,9 @@ bfad_debugfs_init(struct bfad_port_s *port)
port->port_debugfs_root,
port,
file->fops);
- if (!bfad->bfad_dentry_files[i]) {
- printk(KERN_WARNING
- "bfa %s: debugfs %s creation failed\n",
- bfad->pci_name, file->name);
- goto err;
- }
}
}
-err:
return;
}
@@ -556,8 +488,7 @@ bfad_debugfs_exit(struct bfad_port_s *port)
}
}
- /*
- * Remove the pci_dev debugfs directory for the port */
+ /* Remove the pci_dev debugfs directory for the port */
if (port->port_debugfs_root) {
debugfs_remove(port->port_debugfs_root);
port->port_debugfs_root = NULL;
diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h
index 7f9ea90254cd..ce2ec5108947 100644
--- a/drivers/scsi/bfa/bfad_drv.h
+++ b/drivers/scsi/bfa/bfad_drv.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
@@ -27,7 +20,6 @@
#define __BFAD_DRV_H__
#include <linux/types.h>
-#include <linux/version.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
@@ -43,6 +35,8 @@
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_transport.h>
+#include <scsi/scsi_bsg_fc.h>
+#include <scsi/scsi_devinfo.h>
#include "bfa_modules.h"
#include "bfa_fcs.h"
@@ -55,7 +49,7 @@
#ifdef BFA_DRIVER_VERSION
#define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION
#else
-#define BFAD_DRIVER_VERSION "2.3.2.3"
+#define BFAD_DRIVER_VERSION "3.2.25.1"
#endif
#define BFAD_PROTO_NAME FCPI_NAME
@@ -79,7 +73,9 @@
#define BFAD_HAL_INIT_FAIL 0x00000100
#define BFAD_FC4_PROBE_DONE 0x00000200
#define BFAD_PORT_DELETE 0x00000001
-
+#define BFAD_INTX_ON 0x00000400
+#define BFAD_EEH_BUSY 0x00000800
+#define BFAD_EEH_PCI_CHANNEL_IO_PERM_FAILURE 0x00001000
/*
* BFAD related definition
*/
@@ -92,6 +88,8 @@
*/
#define BFAD_LUN_QUEUE_DEPTH 32
#define BFAD_IO_MAX_SGE SG_ALL
+#define BFAD_MIN_SECTORS 128 /* 64k */
+#define BFAD_MAX_SECTORS 0xFFFF /* 32 MB */
#define bfad_isr_t irq_handler_t
@@ -110,6 +108,7 @@ struct bfad_msix_s {
enum {
BFA_TRC_LDRV_BFAD = 1,
BFA_TRC_LDRV_IM = 2,
+ BFA_TRC_LDRV_BSG = 3,
};
enum bfad_port_pvb_type {
@@ -176,11 +175,27 @@ union bfad_tmp_buf {
wwn_t wwn[BFA_FCS_MAX_LPORTS];
};
+/* BFAD state machine events */
+enum bfad_sm_event {
+ BFAD_E_CREATE = 1,
+ BFAD_E_KTHREAD_CREATE_FAILED = 2,
+ BFAD_E_INIT = 3,
+ BFAD_E_INIT_SUCCESS = 4,
+ BFAD_E_HAL_INIT_FAILED = 5,
+ BFAD_E_INIT_FAILED = 6,
+ BFAD_E_FCS_EXIT_COMP = 7,
+ BFAD_E_EXIT_COMP = 8,
+ BFAD_E_STOP = 9
+};
+
+struct bfad_s;
+typedef void (*bfad_sm_t)(struct bfad_s *, enum bfad_sm_event);
+
/*
* BFAD (PCI function) data structure
*/
struct bfad_s {
- bfa_sm_t sm; /* state machine */
+ bfad_sm_t sm; /* state machine */
struct list_head list_entry;
struct bfa_s bfa;
struct bfa_fcs_s bfa_fcs;
@@ -189,8 +204,10 @@ struct bfad_s {
struct bfa_pcidev_s hal_pcidev;
struct bfa_ioc_pci_attr_s pci_attr;
void __iomem *pci_bar0_kva;
+ void __iomem *pci_bar2_kva;
struct completion comp;
struct completion suspend;
+ struct completion enable_comp;
struct completion disable_comp;
bfa_boolean_t disable_active;
struct bfad_port_s pport; /* physical port of the BFAD */
@@ -218,19 +235,11 @@ struct bfad_s {
char *regdata;
u32 reglen;
struct dentry *bfad_dentry_files[5];
-};
-
-/* BFAD state machine events */
-enum bfad_sm_event {
- BFAD_E_CREATE = 1,
- BFAD_E_KTHREAD_CREATE_FAILED = 2,
- BFAD_E_INIT = 3,
- BFAD_E_INIT_SUCCESS = 4,
- BFAD_E_INIT_FAILED = 5,
- BFAD_E_INTR_INIT_FAILED = 6,
- BFAD_E_FCS_EXIT_COMP = 7,
- BFAD_E_EXIT_COMP = 8,
- BFAD_E_STOP = 9
+ struct list_head free_aen_q;
+ struct list_head active_aen_q;
+ struct bfa_aen_entry_s aen_list[BFA_AEN_MAX_ENTRY];
+ spinlock_t bfad_aen_spinlock;
+ struct list_head vport_list;
};
/*
@@ -273,21 +282,6 @@ struct bfad_hal_comp {
struct completion comp;
};
-/*
- * Macro to obtain the immediate lower power
- * of two for the integer.
- */
-#define nextLowerInt(x) \
-do { \
- int __i; \
- (*x)--; \
- for (__i = 1; __i < (sizeof(int)*8); __i <<= 1) \
- (*x) = (*x) | (*x) >> __i; \
- (*x)++; \
- (*x) = (*x) >> 1; \
-} while (0)
-
-
#define BFA_LOG(level, bfad, mask, fmt, arg...) \
do { \
if (((mask) == 4) || (level[1] <= '4')) \
@@ -314,11 +308,10 @@ int bfad_setup_intr(struct bfad_s *bfad);
void bfad_remove_intr(struct bfad_s *bfad);
void bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg);
bfa_status_t bfad_hal_mem_alloc(struct bfad_s *bfad);
-void bfad_bfa_tmo(unsigned long data);
+void bfad_bfa_tmo(struct timer_list *t);
void bfad_init_timer(struct bfad_s *bfad);
int bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad);
void bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad);
-void bfad_drv_uninit(struct bfad_s *bfad);
int bfad_worker(void *ptr);
void bfad_debugfs_init(struct bfad_port_s *port);
void bfad_debugfs_exit(struct bfad_port_s *port);
@@ -354,6 +347,7 @@ extern int msix_disable_ct;
extern int fdmi_enable;
extern int supported_fc4s;
extern int pcie_max_read_reqsz;
+extern int max_xfer_size;
extern int bfa_debugfs_enable;
extern struct mutex bfad_mutex;
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index c2b36179e8e8..f56e008ee52b 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -1,24 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
/*
* bfad_im.c Linux driver IM module.
*/
+#include <linux/export.h>
+
#include "bfad_drv.h"
#include "bfad_im.h"
#include "bfa_fcs.h"
@@ -30,7 +25,7 @@ struct scsi_transport_template *bfad_im_scsi_transport_template;
struct scsi_transport_template *bfad_im_scsi_vport_transport_template;
static void bfad_im_itnim_work_handler(struct work_struct *work);
static int bfad_im_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *cmnd);
-static int bfad_im_slave_alloc(struct scsi_device *sdev);
+static int bfad_im_sdev_init(struct scsi_device *sdev);
static void bfad_im_fc_rport_add(struct bfad_im_port_s *im_port,
struct bfad_itnim_s *itnim);
@@ -67,16 +62,18 @@ bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio,
host_status = DID_ERROR;
}
}
- cmnd->result = ScsiResult(host_status, scsi_status);
+ cmnd->result = host_status << 16 | scsi_status;
break;
- case BFI_IOIM_STS_ABORTED:
case BFI_IOIM_STS_TIMEDOUT:
+ cmnd->result = DID_TIME_OUT << 16;
+ break;
case BFI_IOIM_STS_PATHTOV:
+ cmnd->result = DID_TRANSPORT_DISRUPTED << 16;
+ break;
default:
- host_status = DID_ERROR;
- cmnd->result = ScsiResult(host_status, 0);
+ cmnd->result = DID_ERROR << 16;
}
/* Unmap DMA, if host is NULL, it means a scsi passthru cmd */
@@ -99,7 +96,7 @@ bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio,
}
}
- cmnd->scsi_done(cmnd);
+ scsi_done(cmnd);
}
void
@@ -109,7 +106,7 @@ bfa_cb_ioim_good_comp(void *drv, struct bfad_ioim_s *dio)
struct bfad_itnim_data_s *itnim_data;
struct bfad_itnim_s *itnim;
- cmnd->result = ScsiResult(DID_OK, SCSI_STATUS_GOOD);
+ cmnd->result = DID_OK << 16 | SAM_STAT_GOOD;
/* Unmap DMA, if host is NULL, it means a scsi passthru cmd */
if (cmnd->device->host != NULL)
@@ -127,7 +124,7 @@ bfa_cb_ioim_good_comp(void *drv, struct bfad_ioim_s *dio)
}
}
- cmnd->scsi_done(cmnd);
+ scsi_done(cmnd);
}
void
@@ -136,7 +133,7 @@ bfa_cb_ioim_abort(void *drv, struct bfad_ioim_s *dio)
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
struct bfad_s *bfad = drv;
- cmnd->result = ScsiResult(DID_ERROR, 0);
+ cmnd->result = DID_ERROR << 16;
/* Unmap DMA, if host is NULL, it means a scsi passthru cmd */
if (cmnd->device->host != NULL)
@@ -153,10 +150,10 @@ bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk,
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dtsk;
wait_queue_head_t *wq;
- cmnd->SCp.Status |= tsk_status << 1;
- set_bit(IO_DONE_BIT, (unsigned long *)&cmnd->SCp.Status);
- wq = (wait_queue_head_t *) cmnd->SCp.ptr;
- cmnd->SCp.ptr = NULL;
+ bfad_priv(cmnd)->status |= tsk_status << 1;
+ set_bit(IO_DONE_BIT, &bfad_priv(cmnd)->status);
+ wq = bfad_priv(cmnd)->wq;
+ bfad_priv(cmnd)->wq = NULL;
if (wq)
wake_up(wq);
@@ -175,21 +172,11 @@ bfad_im_info(struct Scsi_Host *shost)
struct bfad_im_port_s *im_port =
(struct bfad_im_port_s *) shost->hostdata[0];
struct bfad_s *bfad = im_port->bfad;
- struct bfa_s *bfa = &bfad->bfa;
- struct bfa_ioc_s *ioc = &bfa->ioc;
- char model[BFA_ADAPTER_MODEL_NAME_LEN];
-
- bfa_get_adapter_model(bfa, model);
memset(bfa_buf, 0, sizeof(bfa_buf));
- if (ioc->ctdev && !ioc->fcmode)
- snprintf(bfa_buf, sizeof(bfa_buf),
- "Brocade FCOE Adapter, " "model: %s hwpath: %s driver: %s",
- model, bfad->pci_name, BFAD_DRIVER_VERSION);
- else
- snprintf(bfa_buf, sizeof(bfa_buf),
- "Brocade FC Adapter, " "model: %s hwpath: %s driver: %s",
- model, bfad->pci_name, BFAD_DRIVER_VERSION);
+ snprintf(bfa_buf, sizeof(bfa_buf),
+ "QLogic BR-series FC/FCOE Adapter, hwpath: %s driver: %s",
+ bfad->pci_name, BFAD_DRIVER_VERSION);
return bfa_buf;
}
@@ -214,7 +201,7 @@ bfad_im_abort_handler(struct scsi_cmnd *cmnd)
spin_lock_irqsave(&bfad->bfad_lock, flags);
hal_io = (struct bfa_ioim_s *) cmnd->host_scribble;
if (!hal_io) {
- /* IO has been completed, retrun success */
+ /* IO has been completed, return success */
rc = SUCCESS;
goto out;
}
@@ -239,7 +226,7 @@ bfad_im_abort_handler(struct scsi_cmnd *cmnd)
timeout *= 2;
}
- cmnd->scsi_done(cmnd);
+ scsi_done(cmnd);
bfa_trc(bfad, hal_io->iotag);
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
"scsi%d: complete abort 0x%p iotag 0x%x\n",
@@ -272,8 +259,21 @@ bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd,
* happens.
*/
cmnd->host_scribble = NULL;
- cmnd->SCp.Status = 0;
+ bfad_priv(cmnd)->status = 0;
bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim);
+ /*
+ * bfa_itnim can be NULL if the port gets disconnected and the bfa
+ * and fcs layers have cleaned up their nexus with the targets and
+ * the same has not been cleaned up by the shim
+ */
+ if (bfa_itnim == NULL) {
+ bfa_tskim_free(tskim);
+ BFA_LOG(KERN_ERR, bfad, bfa_log_level,
+ "target reset, bfa_itnim is NULL\n");
+ rc = BFA_STATUS_FAILED;
+ goto out;
+ }
+
memset(&scsilun, 0, sizeof(scsilun));
bfa_tskim_start(tskim, bfa_itnim, scsilun,
FCP_TM_TARGET_RESET, BFAD_TARGET_RESET_TMO);
@@ -326,18 +326,30 @@ bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd)
* if happens.
*/
cmnd->host_scribble = NULL;
- cmnd->SCp.ptr = (char *)&wq;
- cmnd->SCp.Status = 0;
+ bfad_priv(cmnd)->wq = &wq;
+ bfad_priv(cmnd)->status = 0;
bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim);
+ /*
+ * bfa_itnim can be NULL if the port gets disconnected and the bfa
+ * and fcs layers have cleaned up their nexus with the targets and
+ * the same has not been cleaned up by the shim
+ */
+ if (bfa_itnim == NULL) {
+ bfa_tskim_free(tskim);
+ BFA_LOG(KERN_ERR, bfad, bfa_log_level,
+ "lun reset, bfa_itnim is NULL\n");
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ rc = FAILED;
+ goto out;
+ }
int_to_scsilun(cmnd->device->lun, &scsilun);
bfa_tskim_start(tskim, bfa_itnim, scsilun,
FCP_TM_LUN_RESET, BFAD_LUN_RESET_TMO);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
- wait_event(wq, test_bit(IO_DONE_BIT,
- (unsigned long *)&cmnd->SCp.Status));
+ wait_event(wq, test_bit(IO_DONE_BIT, &bfad_priv(cmnd)->status));
- task_status = cmnd->SCp.Status >> 1;
+ task_status = bfad_priv(cmnd)->status >> 1;
if (task_status != BFI_TSKIM_STS_OK) {
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
"LUN reset failure, status: %d\n", task_status);
@@ -349,60 +361,53 @@ out:
}
/*
- * Scsi_Host template entry, resets the bus and abort all commands.
+ * Scsi_Host template entry, resets the target and abort all commands.
*/
static int
-bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd)
+bfad_im_reset_target_handler(struct scsi_cmnd *cmnd)
{
struct Scsi_Host *shost = cmnd->device->host;
+ struct scsi_target *starget = scsi_target(cmnd->device);
struct bfad_im_port_s *im_port =
(struct bfad_im_port_s *) shost->hostdata[0];
struct bfad_s *bfad = im_port->bfad;
struct bfad_itnim_s *itnim;
unsigned long flags;
- u32 i, rc, err_cnt = 0;
+ u32 rc, rtn = FAILED;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
enum bfi_tskim_status task_status;
spin_lock_irqsave(&bfad->bfad_lock, flags);
- for (i = 0; i < MAX_FCP_TARGET; i++) {
- itnim = bfad_get_itnim(im_port, i);
- if (itnim) {
- cmnd->SCp.ptr = (char *)&wq;
- rc = bfad_im_target_reset_send(bfad, cmnd, itnim);
- if (rc != BFA_STATUS_OK) {
- err_cnt++;
- continue;
- }
-
+ itnim = bfad_get_itnim(im_port, starget->id);
+ if (itnim) {
+ bfad_priv(cmnd)->wq = &wq;
+ rc = bfad_im_target_reset_send(bfad, cmnd, itnim);
+ if (rc == BFA_STATUS_OK) {
/* wait target reset to complete */
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
wait_event(wq, test_bit(IO_DONE_BIT,
- (unsigned long *)&cmnd->SCp.Status));
+ &bfad_priv(cmnd)->status));
spin_lock_irqsave(&bfad->bfad_lock, flags);
- task_status = cmnd->SCp.Status >> 1;
- if (task_status != BFI_TSKIM_STS_OK) {
+ task_status = bfad_priv(cmnd)->status >> 1;
+ if (task_status != BFI_TSKIM_STS_OK)
BFA_LOG(KERN_ERR, bfad, bfa_log_level,
"target reset failure,"
" status: %d\n", task_status);
- err_cnt++;
- }
+ else
+ rtn = SUCCESS;
}
}
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
- if (err_cnt)
- return FAILED;
-
- return SUCCESS;
+ return rtn;
}
/*
- * Scsi_Host template entry slave_destroy.
+ * Scsi_Host template entry sdev_destroy.
*/
static void
-bfad_im_slave_destroy(struct scsi_device *sdev)
+bfad_im_sdev_destroy(struct scsi_device *sdev)
{
sdev->hostdata = NULL;
return;
@@ -416,13 +421,13 @@ bfad_im_slave_destroy(struct scsi_device *sdev)
* BFA FCS itnim alloc callback, after successful PRLI
* Context: Interrupt
*/
-void
+int
bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim,
struct bfad_itnim_s **itnim_drv)
{
*itnim_drv = kzalloc(sizeof(struct bfad_itnim_s), GFP_ATOMIC);
if (*itnim_drv == NULL)
- return;
+ return -ENOMEM;
(*itnim_drv)->im = bfad->im;
*itnim = &(*itnim_drv)->fcs_itnim;
@@ -433,6 +438,7 @@ bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim,
*/
INIT_WORK(&(*itnim_drv)->itnim_work, bfad_im_itnim_work_handler);
bfad->bfad_flags |= BFAD_RPORT_ONLINE;
+ return 0;
}
/*
@@ -528,23 +534,17 @@ int
bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port,
struct device *dev)
{
- int error = 1;
+ struct bfad_im_port_pointer *im_portp;
+ int error;
mutex_lock(&bfad_mutex);
- if (!idr_pre_get(&bfad_im_port_index, GFP_KERNEL)) {
+ error = idr_alloc(&bfad_im_port_index, im_port, 0, 0, GFP_KERNEL);
+ if (error < 0) {
mutex_unlock(&bfad_mutex);
- printk(KERN_WARNING "idr_pre_get failure\n");
+ printk(KERN_WARNING "idr_alloc failure\n");
goto out;
}
-
- error = idr_get_new(&bfad_im_port_index, im_port,
- &im_port->idr_id);
- if (error) {
- mutex_unlock(&bfad_mutex);
- printk(KERN_WARNING "idr_get_new failure\n");
- goto out;
- }
-
+ im_port->idr_id = error;
mutex_unlock(&bfad_mutex);
im_port->shost = bfad_scsi_host_alloc(im_port, bfad);
@@ -553,7 +553,8 @@ bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port,
goto out_free_idr;
}
- im_port->shost->hostdata[0] = (unsigned long)im_port;
+ im_portp = shost_priv(im_port->shost);
+ im_portp->p = im_port;
im_port->shost->unique_id = im_port->idr_id;
im_port->shost->this_id = -1;
im_port->shost->max_id = MAX_FCP_TARGET;
@@ -572,9 +573,6 @@ bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port,
goto out_fc_rel;
}
- /* setup host fixed attribute if the lk supports */
- bfad_fc_host_init(im_port);
-
return 0;
out_fc_rel:
@@ -669,28 +667,51 @@ bfad_im_port_clean(struct bfad_im_port_s *im_port)
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
}
+static void bfad_aen_im_notify_handler(struct work_struct *work)
+{
+ struct bfad_im_s *im =
+ container_of(work, struct bfad_im_s, aen_im_notify_work);
+ struct bfa_aen_entry_s *aen_entry;
+ struct bfad_s *bfad = im->bfad;
+ struct Scsi_Host *shost = bfad->pport.im_port->shost;
+ void *event_data;
+ unsigned long flags;
+
+ while (!list_empty(&bfad->active_aen_q)) {
+ spin_lock_irqsave(&bfad->bfad_aen_spinlock, flags);
+ bfa_q_deq(&bfad->active_aen_q, &aen_entry);
+ spin_unlock_irqrestore(&bfad->bfad_aen_spinlock, flags);
+ event_data = (char *)aen_entry + sizeof(struct list_head);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(struct bfa_aen_entry_s) -
+ sizeof(struct list_head),
+ (char *)event_data, BFAD_NL_VENDOR_ID);
+ spin_lock_irqsave(&bfad->bfad_aen_spinlock, flags);
+ list_add_tail(&aen_entry->qe, &bfad->free_aen_q);
+ spin_unlock_irqrestore(&bfad->bfad_aen_spinlock, flags);
+ }
+}
+
bfa_status_t
bfad_im_probe(struct bfad_s *bfad)
{
struct bfad_im_s *im;
- bfa_status_t rc = BFA_STATUS_OK;
im = kzalloc(sizeof(struct bfad_im_s), GFP_KERNEL);
- if (im == NULL) {
- rc = BFA_STATUS_ENOMEM;
- goto ext;
- }
+ if (im == NULL)
+ return BFA_STATUS_ENOMEM;
bfad->im = im;
im->bfad = bfad;
if (bfad_thread_workq(bfad) != BFA_STATUS_OK) {
kfree(im);
- rc = BFA_STATUS_FAILED;
+ bfad->im = NULL;
+ return BFA_STATUS_FAILED;
}
-ext:
- return rc;
+ INIT_WORK(&im->aen_im_notify_work, bfad_aen_im_notify_handler);
+ return BFA_STATUS_OK;
}
void
@@ -713,9 +734,12 @@ bfad_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad)
else
sht = &bfad_im_vport_template;
+ if (max_xfer_size != BFAD_MAX_SECTORS >> 1)
+ sht->max_sectors = max_xfer_size << 1;
+
sht->sg_tablesize = bfad->cfg_data.io_max_sge;
- return scsi_host_alloc(sht, sizeof(unsigned long));
+ return scsi_host_alloc(sht, sizeof(struct bfad_im_port_pointer));
}
void
@@ -732,7 +756,6 @@ void
bfad_destroy_workq(struct bfad_im_s *im)
{
if (im && im->drv_workq) {
- flush_workqueue(im->drv_workq);
destroy_workqueue(im->drv_workq);
im->drv_workq = NULL;
}
@@ -744,9 +767,8 @@ bfad_thread_workq(struct bfad_s *bfad)
struct bfad_im_s *im = bfad->im;
bfa_trc(bfad, 0);
- snprintf(im->drv_workq_name, KOBJ_NAME_LEN, "bfad_wq_%d",
- bfad->inst_no);
- im->drv_workq = create_singlethread_workqueue(im->drv_workq_name);
+ im->drv_workq = alloc_ordered_workqueue("bfad_wq_%d", WQ_MEM_RECLAIM,
+ bfad->inst_no);
if (!im->drv_workq)
return BFA_STATUS_FAILED;
@@ -762,13 +784,9 @@ bfad_thread_workq(struct bfad_s *bfad)
* Return non-zero if fails.
*/
static int
-bfad_im_slave_configure(struct scsi_device *sdev)
+bfad_im_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, bfa_lun_queue_depth);
- else
- scsi_deactivate_tcq(sdev, bfa_lun_queue_depth);
-
+ scsi_change_queue_depth(sdev, bfa_lun_queue_depth);
return 0;
}
@@ -777,20 +795,22 @@ struct scsi_host_template bfad_im_scsi_host_template = {
.name = BFAD_DRIVER_NAME,
.info = bfad_im_info,
.queuecommand = bfad_im_queuecommand,
+ .cmd_size = sizeof(struct bfad_cmd_priv),
+ .eh_timed_out = fc_eh_timed_out,
.eh_abort_handler = bfad_im_abort_handler,
.eh_device_reset_handler = bfad_im_reset_lun_handler,
- .eh_bus_reset_handler = bfad_im_reset_bus_handler,
+ .eh_target_reset_handler = bfad_im_reset_target_handler,
- .slave_alloc = bfad_im_slave_alloc,
- .slave_configure = bfad_im_slave_configure,
- .slave_destroy = bfad_im_slave_destroy,
+ .sdev_init = bfad_im_sdev_init,
+ .sdev_configure = bfad_im_sdev_configure,
+ .sdev_destroy = bfad_im_sdev_destroy,
.this_id = -1,
.sg_tablesize = BFAD_IO_MAX_SGE,
.cmd_per_lun = 3,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = bfad_im_host_attrs,
- .max_sectors = 0xFFFF,
+ .shost_groups = bfad_im_host_groups,
+ .max_sectors = BFAD_MAX_SECTORS,
+ .vendor_id = BFA_PCI_VENDOR_ID_BROCADE,
};
struct scsi_host_template bfad_im_vport_template = {
@@ -798,20 +818,21 @@ struct scsi_host_template bfad_im_vport_template = {
.name = BFAD_DRIVER_NAME,
.info = bfad_im_info,
.queuecommand = bfad_im_queuecommand,
+ .cmd_size = sizeof(struct bfad_cmd_priv),
+ .eh_timed_out = fc_eh_timed_out,
.eh_abort_handler = bfad_im_abort_handler,
.eh_device_reset_handler = bfad_im_reset_lun_handler,
- .eh_bus_reset_handler = bfad_im_reset_bus_handler,
+ .eh_target_reset_handler = bfad_im_reset_target_handler,
- .slave_alloc = bfad_im_slave_alloc,
- .slave_configure = bfad_im_slave_configure,
- .slave_destroy = bfad_im_slave_destroy,
+ .sdev_init = bfad_im_sdev_init,
+ .sdev_configure = bfad_im_sdev_configure,
+ .sdev_destroy = bfad_im_sdev_destroy,
.this_id = -1,
.sg_tablesize = BFAD_IO_MAX_SGE,
.cmd_per_lun = 3,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = bfad_im_vport_attrs,
- .max_sectors = 0xFFFF,
+ .shost_groups = bfad_im_vport_groups,
+ .max_sectors = BFAD_MAX_SECTORS,
};
bfa_status_t
@@ -840,6 +861,8 @@ bfad_im_module_exit(void)
if (bfad_im_scsi_vport_transport_template)
fc_release_transport(bfad_im_scsi_vport_transport_template);
+
+ idr_destroy(&bfad_im_port_index);
}
void
@@ -855,14 +878,8 @@ bfad_ramp_up_qdepth(struct bfad_itnim_s *itnim, struct scsi_device *sdev)
if (bfa_lun_queue_depth > tmp_sdev->queue_depth) {
if (tmp_sdev->id != sdev->id)
continue;
- if (tmp_sdev->ordered_tags)
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_ORDERED_TAG,
- tmp_sdev->queue_depth + 1);
- else
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_SIMPLE_TAG,
- tmp_sdev->queue_depth + 1);
+ scsi_change_queue_depth(tmp_sdev,
+ tmp_sdev->queue_depth + 1);
itnim->last_ramp_up_time = jiffies;
}
@@ -899,22 +916,78 @@ bfad_get_itnim(struct bfad_im_port_s *im_port, int id)
}
/*
- * Scsi_Host template entry slave_alloc
+ * Function is invoked from the SCSI Host Template sdev_init() entry point.
+ * Has the logic to query the LUN Mask database to check if this LUN needs to
+ * be made visible to the SCSI mid-layer or not.
+ *
+ * Returns BFA_STATUS_OK if this LUN needs to be added to the OS stack.
+ * Returns -ENXIO to notify SCSI mid-layer to not add this LUN to the OS stack.
*/
static int
-bfad_im_slave_alloc(struct scsi_device *sdev)
+bfad_im_check_if_make_lun_visible(struct scsi_device *sdev,
+ struct fc_rport *rport)
+{
+ struct bfad_itnim_data_s *itnim_data =
+ (struct bfad_itnim_data_s *) rport->dd_data;
+ struct bfa_s *bfa = itnim_data->itnim->bfa_itnim->bfa;
+ struct bfa_rport_s *bfa_rport = itnim_data->itnim->bfa_itnim->rport;
+ struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(bfa);
+ int i = 0, ret = -ENXIO;
+
+ for (i = 0; i < MAX_LUN_MASK_CFG; i++) {
+ if (lun_list[i].state == BFA_IOIM_LUN_MASK_ACTIVE &&
+ scsilun_to_int(&lun_list[i].lun) == sdev->lun &&
+ lun_list[i].rp_tag == bfa_rport->rport_tag &&
+ lun_list[i].lp_tag == (u8)bfa_rport->rport_info.lp_tag) {
+ ret = BFA_STATUS_OK;
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Scsi_Host template entry sdev_init
+ */
+static int
+bfad_im_sdev_init(struct scsi_device *sdev)
{
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct bfad_itnim_data_s *itnim_data;
+ struct bfa_s *bfa;
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
+ itnim_data = (struct bfad_itnim_data_s *) rport->dd_data;
+ bfa = itnim_data->itnim->bfa_itnim->bfa;
+
+ if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_ENABLED) {
+ /*
+ * We should not mask LUN 0 - since this will translate
+ * to no LUN / TARGET for SCSI ml resulting no scan.
+ */
+ if (sdev->lun == 0) {
+ sdev->sdev_bflags |= BLIST_NOREPORTLUN |
+ BLIST_SPARSELUN;
+ goto done;
+ }
+
+ /*
+ * Query LUN Mask configuration - to expose this LUN
+ * to the SCSI mid-layer or to mask it.
+ */
+ if (bfad_im_check_if_make_lun_visible(sdev, rport) !=
+ BFA_STATUS_OK)
+ return -ENXIO;
+ }
+done:
sdev->hostdata = rport->dd_data;
return 0;
}
-static u32
+u32
bfad_im_supported_speeds(struct bfa_s *bfa)
{
struct bfa_ioc_attr_s *ioc_attr;
@@ -925,7 +998,10 @@ bfad_im_supported_speeds(struct bfa_s *bfa)
return 0;
bfa_ioc_get_attr(&bfa->ioc, ioc_attr);
- if (ioc_attr->adapter_attr.max_speed == BFA_PORT_SPEED_8GBPS) {
+ if (ioc_attr->adapter_attr.max_speed == BFA_PORT_SPEED_16GBPS)
+ supported_speed |= FC_PORTSPEED_16GBIT | FC_PORTSPEED_8GBIT |
+ FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT;
+ else if (ioc_attr->adapter_attr.max_speed == BFA_PORT_SPEED_8GBPS) {
if (ioc_attr->adapter_attr.is_mezz) {
supported_speed |= FC_PORTSPEED_8GBIT |
FC_PORTSPEED_4GBIT |
@@ -970,7 +1046,7 @@ bfad_fc_host_init(struct bfad_im_port_s *im_port)
/* For fibre channel services type 0x20 */
fc_host_supported_fc4s(host)[7] = 1;
- strncpy(symname, bfad->bfa_fcs.fabric.bport.port_cfg.sym_name.symname,
+ strscpy(symname, bfad->bfa_fcs.fabric.bport.port_cfg.sym_name.symname,
BFA_SYMNAME_MAXLEN);
sprintf(fc_host_symbolic_name(host), "%s", symname);
@@ -1015,6 +1091,8 @@ bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim)
&& (fc_rport->scsi_target_id < MAX_FCP_TARGET))
itnim->scsi_tgt_id = fc_rport->scsi_target_id;
+ itnim->channel = fc_rport->channel;
+
return;
}
@@ -1121,9 +1199,9 @@ bfad_im_itnim_work_handler(struct work_struct *work)
/*
* Scsi_Host template entry, queue a SCSI command to the BFAD.
*/
-static int
-bfad_im_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+static int bfad_im_queuecommand_lck(struct scsi_cmnd *cmnd)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
struct bfad_im_port_s *im_port =
(struct bfad_im_port_s *) cmnd->device->host->hostdata[0];
struct bfad_s *bfad = im_port->bfad;
@@ -1142,25 +1220,32 @@ bfad_im_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd
return 0;
}
+ if (bfad->bfad_flags & BFAD_EEH_BUSY) {
+ if (bfad->bfad_flags & BFAD_EEH_PCI_CHANNEL_IO_PERM_FAILURE)
+ cmnd->result = DID_NO_CONNECT << 16;
+ else
+ cmnd->result = DID_REQUEUE << 16;
+ done(cmnd);
+ return 0;
+ }
+
sg_cnt = scsi_dma_map(cmnd);
if (sg_cnt < 0)
return SCSI_MLQUEUE_HOST_BUSY;
- cmnd->scsi_done = done;
-
spin_lock_irqsave(&bfad->bfad_lock, flags);
if (!(bfad->bfad_flags & BFAD_HAL_START_DONE)) {
printk(KERN_WARNING
"bfad%d, queuecommand %p %x failed, BFA stopped\n",
bfad->inst_no, cmnd, cmnd->cmnd[0]);
- cmnd->result = ScsiResult(DID_NO_CONNECT, 0);
+ cmnd->result = DID_NO_CONNECT << 16;
goto out_fail_cmd;
}
itnim = itnim_data->itnim;
if (!itnim) {
- cmnd->result = ScsiResult(DID_IMM_RETRY, 0);
+ cmnd->result = DID_IMM_RETRY << 16;
goto out_fail_cmd;
}
diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h
index c296c8968511..0884af04bd1f 100644
--- a/drivers/scsi/bfa/bfad_im.h
+++ b/drivers/scsi/bfa/bfad_im.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFAD_IM_H__
@@ -37,12 +30,12 @@ int bfad_im_scsi_host_alloc(struct bfad_s *bfad,
struct bfad_im_port_s *im_port, struct device *dev);
void bfad_im_scsi_host_free(struct bfad_s *bfad,
struct bfad_im_port_s *im_port);
+u32 bfad_im_supported_speeds(struct bfa_s *bfa);
#define MAX_FCP_TARGET 1024
#define MAX_FCP_LUN 16384
#define BFAD_TARGET_RESET_TMO 60
#define BFAD_LUN_RESET_TMO 60
-#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
#define BFA_QUEUE_FULL_RAMP_UP_TIME 120
/*
@@ -50,6 +43,22 @@ void bfad_im_scsi_host_free(struct bfad_s *bfad,
*/
#define IO_DONE_BIT 0
+/**
+ * struct bfad_cmd_priv - private data per SCSI command.
+ * @status: Lowest bit represents IO_DONE. The next seven bits hold a value of
+ * type enum bfi_tskim_status.
+ * @wq: Wait queue used to wait for completion of an operation.
+ */
+struct bfad_cmd_priv {
+ unsigned long status;
+ wait_queue_head_t *wq;
+};
+
+static inline struct bfad_cmd_priv *bfad_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
struct bfad_itnim_data_s {
struct bfad_itnim_s *itnim;
};
@@ -67,6 +76,16 @@ struct bfad_im_port_s {
struct fc_vport *fc_vport;
};
+struct bfad_im_port_pointer {
+ struct bfad_im_port_s *p;
+};
+
+static inline struct bfad_im_port_s *bfad_get_im_port(struct Scsi_Host *host)
+{
+ struct bfad_im_port_pointer *im_portp = shost_priv(host);
+ return im_portp->p;
+}
+
enum bfad_itnim_state {
ITNIM_STATE_NONE,
ITNIM_STATE_ONLINE,
@@ -91,6 +110,7 @@ struct bfad_itnim_s {
struct fc_rport *fc_rport;
struct bfa_itnim_s *bfa_itnim;
u16 scsi_tgt_id;
+ u16 channel;
u16 queue_work;
unsigned long last_ramp_up_time;
unsigned long last_queue_full_time;
@@ -114,9 +134,42 @@ struct bfad_fcp_binding {
struct bfad_im_s {
struct bfad_s *bfad;
struct workqueue_struct *drv_workq;
- char drv_workq_name[KOBJ_NAME_LEN];
+ struct work_struct aen_im_notify_work;
};
+#define bfad_get_aen_entry(_drv, _entry) do { \
+ unsigned long _flags; \
+ spin_lock_irqsave(&(_drv)->bfad_aen_spinlock, _flags); \
+ bfa_q_deq(&(_drv)->free_aen_q, &(_entry)); \
+ if (_entry) \
+ list_add_tail(&(_entry)->qe, &(_drv)->active_aen_q); \
+ spin_unlock_irqrestore(&(_drv)->bfad_aen_spinlock, _flags); \
+} while (0)
+
+/* post fc_host vendor event */
+static inline void bfad_im_post_vendor_event(struct bfa_aen_entry_s *entry,
+ struct bfad_s *drv, int cnt,
+ enum bfa_aen_category cat,
+ int evt)
+{
+ struct timespec64 ts;
+
+ ktime_get_real_ts64(&ts);
+ /*
+ * 'unsigned long aen_tv_sec' overflows in y2106 on 32-bit
+ * architectures, or in 2038 if user space interprets it
+ * as 'signed'.
+ */
+ entry->aen_tv_sec = ts.tv_sec;
+ entry->aen_tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ entry->bfad_num = drv->inst_no;
+ entry->seq_num = cnt;
+ entry->aen_category = cat;
+ entry->aen_type = evt;
+ if (drv->bfad_flags & BFAD_FC4_PROBE_DONE)
+ queue_work(drv->im->drv_workq, &drv->im->aen_im_notify_work);
+}
+
struct Scsi_Host *bfad_scsi_host_alloc(struct bfad_im_port_s *im_port,
struct bfad_s *);
bfa_status_t bfad_thread_workq(struct bfad_s *bfad);
@@ -136,9 +189,12 @@ extern struct fc_function_template bfad_im_vport_fc_function_template;
extern struct scsi_transport_template *bfad_im_scsi_transport_template;
extern struct scsi_transport_template *bfad_im_scsi_vport_transport_template;
-extern struct device_attribute *bfad_im_host_attrs[];
-extern struct device_attribute *bfad_im_vport_attrs[];
+extern const struct attribute_group *bfad_im_host_groups[];
+extern const struct attribute_group *bfad_im_vport_groups[];
irqreturn_t bfad_intx(int irq, void *dev_id);
+int bfad_im_bsg_request(struct bsg_job *job);
+int bfad_im_bsg_timeout(struct bsg_job *job);
+
#endif
diff --git a/drivers/scsi/bfa/bfi.h b/drivers/scsi/bfa/bfi.h
index 72b69a0c3b51..e1e0e967bcc3 100644
--- a/drivers/scsi/bfa/bfi.h
+++ b/drivers/scsi/bfa/bfi.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFI_H__
@@ -23,17 +16,30 @@
#pragma pack(1)
+/* Per dma segment max size */
+#define BFI_MEM_DMA_SEG_SZ (131072)
+
+/* Get number of dma segments required */
+#define BFI_MEM_DMA_NSEGS(_num_reqs, _req_sz) \
+ ((u16)(((((_num_reqs) * (_req_sz)) + BFI_MEM_DMA_SEG_SZ - 1) & \
+ ~(BFI_MEM_DMA_SEG_SZ - 1)) / BFI_MEM_DMA_SEG_SZ))
+
+/* Get num dma reqs - that fit in a segment */
+#define BFI_MEM_NREQS_SEG(_rqsz) (BFI_MEM_DMA_SEG_SZ / (_rqsz))
+
+/* Get segment num from tag */
+#define BFI_MEM_SEG_FROM_TAG(_tag, _rqsz) ((_tag) / BFI_MEM_NREQS_SEG(_rqsz))
+
+/* Get dma req offset in a segment */
+#define BFI_MEM_SEG_REQ_OFFSET(_tag, _sz) \
+ ((_tag) - (BFI_MEM_SEG_FROM_TAG(_tag, _sz) * BFI_MEM_NREQS_SEG(_sz)))
+
/*
* BFI FW image type
*/
#define BFI_FLASH_CHUNK_SZ 256 /* Flash chunk size */
#define BFI_FLASH_CHUNK_SZ_WORDS (BFI_FLASH_CHUNK_SZ/sizeof(u32))
-enum {
- BFI_IMAGE_CB_FC,
- BFI_IMAGE_CT_FC,
- BFI_IMAGE_CT_CNA,
- BFI_IMAGE_MAX,
-};
+#define BFI_FLASH_IMAGE_SZ 0x100000
/*
* Msg header common to all msgs
@@ -43,17 +49,20 @@ struct bfi_mhdr_s {
u8 msg_id; /* msg opcode with in the class */
union {
struct {
- u8 rsvd;
- u8 lpu_id; /* msg destination */
+ u8 qid;
+ u8 fn_lpu; /* msg destination */
} h2i;
u16 i2htok; /* token in msgs to host */
} mtag;
};
-#define bfi_h2i_set(_mh, _mc, _op, _lpuid) do { \
+#define bfi_fn_lpu(__fn, __lpu) ((__fn) << 1 | (__lpu))
+#define bfi_mhdr_2_fn(_mh) ((_mh)->mtag.h2i.fn_lpu >> 1)
+
+#define bfi_h2i_set(_mh, _mc, _op, _fn_lpu) do { \
(_mh).msg_class = (_mc); \
(_mh).msg_id = (_op); \
- (_mh).mtag.h2i.lpu_id = (_lpuid); \
+ (_mh).mtag.h2i.fn_lpu = (_fn_lpu); \
} while (0)
#define bfi_i2h_set(_mh, _mc, _op, _i2htok) do { \
@@ -101,7 +110,7 @@ union bfi_addr_u {
};
/*
- * Scatter Gather Element
+ * Scatter Gather Element used for fast-path IO requests
*/
struct bfi_sge_s {
#ifdef __BIG_ENDIAN
@@ -116,6 +125,14 @@ struct bfi_sge_s {
union bfi_addr_u sga;
};
+/**
+ * Generic DMA addr-len pair.
+ */
+struct bfi_alen_s {
+ union bfi_addr_u al_addr; /* DMA addr of buffer */
+ u32 al_len; /* length of buffer */
+};
+
/*
* Scatter Gather Page
*/
@@ -127,6 +144,12 @@ struct bfi_sgpg_s {
u32 rsvd[BFI_SGPG_RSVD_WD_LEN];
};
+/* FCP module definitions */
+#define BFI_IO_MAX (2000)
+#define BFI_IOIM_SNSLEN (256)
+#define BFI_IOIM_SNSBUF_SEGS \
+ BFI_MEM_DMA_NSEGS(BFI_IO_MAX, BFI_IOIM_SNSLEN)
+
/*
* Large Message structure - 128 Bytes size Msgs
*/
@@ -149,18 +172,29 @@ struct bfi_mbmsg_s {
};
/*
+ * Supported PCI function class codes (personality)
+ */
+enum bfi_pcifn_class {
+ BFI_PCIFN_CLASS_FC = 0x0c04,
+ BFI_PCIFN_CLASS_ETH = 0x0200,
+};
+
+/*
* Message Classes
*/
enum bfi_mclass {
BFI_MC_IOC = 1, /* IO Controller (IOC) */
+ BFI_MC_DIAG = 2, /* Diagnostic Msgs */
+ BFI_MC_FLASH = 3, /* Flash message class */
+ BFI_MC_CEE = 4, /* CEE */
BFI_MC_FCPORT = 5, /* FC port */
BFI_MC_IOCFC = 6, /* FC - IO Controller (IOC) */
- BFI_MC_LL = 7, /* Link Layer */
+ BFI_MC_ABLK = 7, /* ASIC block configuration */
BFI_MC_UF = 8, /* Unsolicited frame receive */
BFI_MC_FCXP = 9, /* FC Transport */
BFI_MC_LPS = 10, /* lport fc login services */
BFI_MC_RPORT = 11, /* Remote port */
- BFI_MC_ITNIM = 12, /* I-T nexus (Initiator mode) */
+ BFI_MC_ITN = 12, /* I-T nexus (Initiator mode) */
BFI_MC_IOIM_READ = 13, /* read IO (Initiator mode) */
BFI_MC_IOIM_WRITE = 14, /* write IO (Initiator mode) */
BFI_MC_IOIM_IO = 15, /* IO (Initiator mode) */
@@ -168,30 +202,38 @@ enum bfi_mclass {
BFI_MC_IOIM_IOCOM = 17, /* good IO completion */
BFI_MC_TSKIM = 18, /* Initiator Task management */
BFI_MC_PORT = 21, /* Physical port */
- BFI_MC_MAX = 32
+ BFI_MC_SFP = 22, /* SFP module */
+ BFI_MC_PHY = 25, /* External PHY message class */
+ BFI_MC_FRU = 34,
+ BFI_MC_MAX = 35
};
#define BFI_IOC_MAX_CQS 4
#define BFI_IOC_MAX_CQS_ASIC 8
#define BFI_IOC_MSGLEN_MAX 32 /* 32 bytes */
-#define BFI_BOOT_TYPE_OFF 8
-#define BFI_BOOT_LOADER_OFF 12
-
-#define BFI_BOOT_TYPE_NORMAL 0
-#define BFI_BOOT_TYPE_FLASH 1
-#define BFI_BOOT_TYPE_MEMTEST 2
-
-#define BFI_BOOT_LOADER_OS 0
-#define BFI_BOOT_LOADER_BIOS 1
-#define BFI_BOOT_LOADER_UEFI 2
-
/*
*----------------------------------------------------------------------
* IOC
*----------------------------------------------------------------------
*/
+/*
+ * Different asic generations
+ */
+enum bfi_asic_gen {
+ BFI_ASIC_GEN_CB = 1, /* crossbow 8G FC */
+ BFI_ASIC_GEN_CT = 2, /* catapult 8G FC or 10G CNA */
+ BFI_ASIC_GEN_CT2 = 3, /* catapult-2 16G FC or 10G CNA */
+};
+
+enum bfi_asic_mode {
+ BFI_ASIC_MODE_FC = 1, /* FC upto 8G speed */
+ BFI_ASIC_MODE_FC16 = 2, /* FC upto 16G speed */
+ BFI_ASIC_MODE_ETH = 3, /* Ethernet ports */
+ BFI_ASIC_MODE_COMBO = 4, /* FC 16G and Ethernet 10G port */
+};
+
enum bfi_ioc_h2i_msgs {
BFI_IOC_H2I_ENABLE_REQ = 1,
BFI_IOC_H2I_DISABLE_REQ = 2,
@@ -204,8 +246,8 @@ enum bfi_ioc_i2h_msgs {
BFI_IOC_I2H_ENABLE_REPLY = BFA_I2HM(1),
BFI_IOC_I2H_DISABLE_REPLY = BFA_I2HM(2),
BFI_IOC_I2H_GETATTR_REPLY = BFA_I2HM(3),
- BFI_IOC_I2H_READY_EVENT = BFA_I2HM(4),
- BFI_IOC_I2H_HBEAT = BFA_I2HM(5),
+ BFI_IOC_I2H_HBEAT = BFA_I2HM(4),
+ BFI_IOC_I2H_ACQ_ADDR_REPLY = BFA_I2HM(5),
};
/*
@@ -216,11 +258,13 @@ struct bfi_ioc_getattr_req_s {
union bfi_addr_u attr_addr;
};
+#define BFI_IOC_ATTR_UUID_SZ 16
struct bfi_ioc_attr_s {
wwn_t mfg_pwwn; /* Mfg port wwn */
wwn_t mfg_nwwn; /* Mfg node wwn */
mac_t mfg_mac; /* Mfg mac */
- u16 rsvd_a;
+ u8 port_mode; /* bfi_port_mode */
+ u8 rsvd_a;
wwn_t pwwn;
wwn_t nwwn;
mac_t mac; /* PBC or Mfg mac */
@@ -240,6 +284,10 @@ struct bfi_ioc_attr_s {
char optrom_version[BFA_VERSION_LEN];
struct bfa_mfg_vpd_s vpd;
u32 card_type; /* card type */
+ u8 mfg_day; /* manufacturing day */
+ u8 mfg_month; /* manufacturing month */
+ u16 mfg_year; /* manufacturing year */
+ u8 uuid[BFI_IOC_ATTR_UUID_SZ]; /*!< chinook uuid */
};
/*
@@ -270,23 +318,71 @@ struct bfi_ioc_getattr_reply_s {
#define BFI_IOC_TRC_ENTS 256
#define BFI_IOC_FW_SIGNATURE (0xbfadbfad)
+#define BFA_IOC_FW_INV_SIGN (0xdeaddead)
#define BFI_IOC_MD5SUM_SZ 4
+
+struct bfi_ioc_fwver_s {
+#ifdef __BIG_ENDIAN
+ uint8_t patch;
+ uint8_t maint;
+ uint8_t minor;
+ uint8_t major;
+ uint8_t rsvd[2];
+ uint8_t build;
+ uint8_t phase;
+#else
+ uint8_t major;
+ uint8_t minor;
+ uint8_t maint;
+ uint8_t patch;
+ uint8_t phase;
+ uint8_t build;
+ uint8_t rsvd[2];
+#endif
+};
+
struct bfi_ioc_image_hdr_s {
- u32 signature; /* constant signature */
- u32 rsvd_a;
- u32 exec; /* exec vector */
- u32 param; /* parameters */
- u32 rsvd_b[4];
+ u32 signature; /* constant signature */
+ u8 asic_gen; /* asic generation */
+ u8 asic_mode;
+ u8 port0_mode; /* device mode for port 0 */
+ u8 port1_mode; /* device mode for port 1 */
+ u32 exec; /* exec vector */
+ u32 bootenv; /* firmware boot env */
+ u32 rsvd_b[2];
+ struct bfi_ioc_fwver_s fwver;
u32 md5sum[BFI_IOC_MD5SUM_SZ];
};
-/*
- * BFI_IOC_I2H_READY_EVENT message
- */
-struct bfi_ioc_rdy_event_s {
- struct bfi_mhdr_s mh; /* common msg header */
- u8 init_status; /* init event status */
- u8 rsvd[3];
+enum bfi_ioc_img_ver_cmp_e {
+ BFI_IOC_IMG_VER_INCOMP,
+ BFI_IOC_IMG_VER_OLD,
+ BFI_IOC_IMG_VER_SAME,
+ BFI_IOC_IMG_VER_BETTER
+};
+
+#define BFI_FWBOOT_DEVMODE_OFF 4
+#define BFI_FWBOOT_TYPE_OFF 8
+#define BFI_FWBOOT_ENV_OFF 12
+#define BFI_FWBOOT_DEVMODE(__asic_gen, __asic_mode, __p0_mode, __p1_mode) \
+ (((u32)(__asic_gen)) << 24 | \
+ ((u32)(__asic_mode)) << 16 | \
+ ((u32)(__p0_mode)) << 8 | \
+ ((u32)(__p1_mode)))
+
+enum bfi_fwboot_type {
+ BFI_FWBOOT_TYPE_NORMAL = 0,
+ BFI_FWBOOT_TYPE_FLASH = 1,
+ BFI_FWBOOT_TYPE_MEMTEST = 2,
+};
+
+#define BFI_FWBOOT_TYPE_NORMAL 0
+#define BFI_FWBOOT_TYPE_MEMTEST 2
+#define BFI_FWBOOT_ENV_OS 0
+
+enum bfi_port_mode {
+ BFI_PORT_MODE_FC = 1,
+ BFI_PORT_MODE_ETH = 2,
};
struct bfi_ioc_hbeat_s {
@@ -310,6 +406,10 @@ enum bfi_ioc_state {
BFI_IOC_MEMTEST = 9, /* IOC is doing memtest */
};
+#define BFA_IOC_CB_JOIN_SH 16
+#define BFA_IOC_CB_FWSTATE_MASK 0x0000ffff
+#define BFA_IOC_CB_JOIN_MASK 0xffff0000
+
#define BFI_IOC_ENDIAN_SIG 0x12345678
enum {
@@ -345,8 +445,8 @@ enum {
*/
struct bfi_ioc_ctrl_req_s {
struct bfi_mhdr_s mh;
- u8 ioc_class;
- u8 rsvd[3];
+ u16 clscode;
+ u16 rsvd;
u32 tv_sec;
};
#define bfi_ioc_enable_req_t struct bfi_ioc_ctrl_req_s;
@@ -358,7 +458,9 @@ struct bfi_ioc_ctrl_req_s {
struct bfi_ioc_ctrl_reply_s {
struct bfi_mhdr_s mh; /* Common msg header */
u8 status; /* enable/disable status */
- u8 rsvd[3];
+ u8 port_mode; /* bfa_mode_s */
+ u8 cap_bm; /* capability bit mask */
+ u8 rsvd;
};
#define bfi_ioc_enable_reply_t struct bfi_ioc_ctrl_reply_s;
#define bfi_ioc_disable_reply_t struct bfi_ioc_ctrl_reply_s;
@@ -380,7 +482,7 @@ union bfi_ioc_h2i_msg_u {
*/
union bfi_ioc_i2h_msg_u {
struct bfi_mhdr_s mh;
- struct bfi_ioc_rdy_event_s rdy_event;
+ struct bfi_ioc_ctrl_reply_s fw_event;
u32 mboxmsg[BFI_IOC_MSGSZ];
};
@@ -393,6 +495,7 @@ union bfi_ioc_i2h_msg_u {
#define BFI_PBC_MAX_BLUNS 8
#define BFI_PBC_MAX_VPORTS 16
+#define BFI_PBC_PORT_DISABLED 2
/*
* PBC boot lun configuration
@@ -574,6 +677,641 @@ union bfi_port_i2h_msg_u {
struct bfi_port_generic_rsp_s clearstats_rsp;
};
+/*
+ *----------------------------------------------------------------------
+ * ABLK
+ *----------------------------------------------------------------------
+ */
+enum bfi_ablk_h2i_msgs_e {
+ BFI_ABLK_H2I_QUERY = 1,
+ BFI_ABLK_H2I_ADPT_CONFIG = 2,
+ BFI_ABLK_H2I_PORT_CONFIG = 3,
+ BFI_ABLK_H2I_PF_CREATE = 4,
+ BFI_ABLK_H2I_PF_DELETE = 5,
+ BFI_ABLK_H2I_PF_UPDATE = 6,
+ BFI_ABLK_H2I_OPTROM_ENABLE = 7,
+ BFI_ABLK_H2I_OPTROM_DISABLE = 8,
+};
+
+enum bfi_ablk_i2h_msgs_e {
+ BFI_ABLK_I2H_QUERY = BFA_I2HM(BFI_ABLK_H2I_QUERY),
+ BFI_ABLK_I2H_ADPT_CONFIG = BFA_I2HM(BFI_ABLK_H2I_ADPT_CONFIG),
+ BFI_ABLK_I2H_PORT_CONFIG = BFA_I2HM(BFI_ABLK_H2I_PORT_CONFIG),
+ BFI_ABLK_I2H_PF_CREATE = BFA_I2HM(BFI_ABLK_H2I_PF_CREATE),
+ BFI_ABLK_I2H_PF_DELETE = BFA_I2HM(BFI_ABLK_H2I_PF_DELETE),
+ BFI_ABLK_I2H_PF_UPDATE = BFA_I2HM(BFI_ABLK_H2I_PF_UPDATE),
+ BFI_ABLK_I2H_OPTROM_ENABLE = BFA_I2HM(BFI_ABLK_H2I_OPTROM_ENABLE),
+ BFI_ABLK_I2H_OPTROM_DISABLE = BFA_I2HM(BFI_ABLK_H2I_OPTROM_DISABLE),
+};
+
+/* BFI_ABLK_H2I_QUERY */
+struct bfi_ablk_h2i_query_s {
+ struct bfi_mhdr_s mh;
+ union bfi_addr_u addr;
+};
+
+/* BFI_ABL_H2I_ADPT_CONFIG, BFI_ABLK_H2I_PORT_CONFIG */
+struct bfi_ablk_h2i_cfg_req_s {
+ struct bfi_mhdr_s mh;
+ u8 mode;
+ u8 port;
+ u8 max_pf;
+ u8 max_vf;
+};
+
+/*
+ * BFI_ABLK_H2I_PF_CREATE, BFI_ABLK_H2I_PF_DELETE,
+ */
+struct bfi_ablk_h2i_pf_req_s {
+ struct bfi_mhdr_s mh;
+ u8 pcifn;
+ u8 port;
+ u16 pers;
+ u16 bw_min; /* percent BW @ max speed */
+ u16 bw_max; /* percent BW @ max speed */
+};
+
+/* BFI_ABLK_H2I_OPTROM_ENABLE, BFI_ABLK_H2I_OPTROM_DISABLE */
+struct bfi_ablk_h2i_optrom_s {
+ struct bfi_mhdr_s mh;
+};
+
+/*
+ * BFI_ABLK_I2H_QUERY
+ * BFI_ABLK_I2H_PORT_CONFIG
+ * BFI_ABLK_I2H_PF_CREATE
+ * BFI_ABLK_I2H_PF_DELETE
+ * BFI_ABLK_I2H_PF_UPDATE
+ * BFI_ABLK_I2H_OPTROM_ENABLE
+ * BFI_ABLK_I2H_OPTROM_DISABLE
+ */
+struct bfi_ablk_i2h_rsp_s {
+ struct bfi_mhdr_s mh;
+ u8 status;
+ u8 pcifn;
+ u8 port_mode;
+};
+
+
+/*
+ * CEE module specific messages
+ */
+
+/* Mailbox commands from host to firmware */
+enum bfi_cee_h2i_msgs_e {
+ BFI_CEE_H2I_GET_CFG_REQ = 1,
+ BFI_CEE_H2I_RESET_STATS = 2,
+ BFI_CEE_H2I_GET_STATS_REQ = 3,
+};
+
+enum bfi_cee_i2h_msgs_e {
+ BFI_CEE_I2H_GET_CFG_RSP = BFA_I2HM(1),
+ BFI_CEE_I2H_RESET_STATS_RSP = BFA_I2HM(2),
+ BFI_CEE_I2H_GET_STATS_RSP = BFA_I2HM(3),
+};
+
+/*
+ * H2I command structure for resetting the stats
+ */
+struct bfi_cee_reset_stats_s {
+ struct bfi_mhdr_s mh;
+};
+
+/*
+ * Get configuration command from host
+ */
+struct bfi_cee_get_req_s {
+ struct bfi_mhdr_s mh;
+ union bfi_addr_u dma_addr;
+};
+
+/*
+ * Reply message from firmware
+ */
+struct bfi_cee_get_rsp_s {
+ struct bfi_mhdr_s mh;
+ u8 cmd_status;
+ u8 rsvd[3];
+};
+
+/*
+ * Reply message from firmware
+ */
+struct bfi_cee_stats_rsp_s {
+ struct bfi_mhdr_s mh;
+ u8 cmd_status;
+ u8 rsvd[3];
+};
+
+/* Mailbox message structures from firmware to host */
+union bfi_cee_i2h_msg_u {
+ struct bfi_mhdr_s mh;
+ struct bfi_cee_get_rsp_s get_rsp;
+ struct bfi_cee_stats_rsp_s stats_rsp;
+};
+
+/*
+ * SFP related
+ */
+
+enum bfi_sfp_h2i_e {
+ BFI_SFP_H2I_SHOW = 1,
+ BFI_SFP_H2I_SCN = 2,
+};
+
+enum bfi_sfp_i2h_e {
+ BFI_SFP_I2H_SHOW = BFA_I2HM(BFI_SFP_H2I_SHOW),
+ BFI_SFP_I2H_SCN = BFA_I2HM(BFI_SFP_H2I_SCN),
+};
+
+/*
+ * SFP state change notification
+ */
+struct bfi_sfp_scn_s {
+ struct bfi_mhdr_s mhr; /* host msg header */
+ u8 event;
+ u8 sfpid;
+ u8 pomlvl; /* pom level: normal/warning/alarm */
+ u8 is_elb; /* e-loopback */
+};
+
+/*
+ * SFP state
+ */
+enum bfa_sfp_stat_e {
+ BFA_SFP_STATE_INIT = 0, /* SFP state is uninit */
+ BFA_SFP_STATE_REMOVED = 1, /* SFP is removed */
+ BFA_SFP_STATE_INSERTED = 2, /* SFP is inserted */
+ BFA_SFP_STATE_VALID = 3, /* SFP is valid */
+ BFA_SFP_STATE_UNSUPPORT = 4, /* SFP is unsupport */
+ BFA_SFP_STATE_FAILED = 5, /* SFP i2c read fail */
+};
+
+/*
+ * SFP memory access type
+ */
+enum bfi_sfp_mem_e {
+ BFI_SFP_MEM_ALL = 0x1, /* access all data field */
+ BFI_SFP_MEM_DIAGEXT = 0x2, /* access diag ext data field only */
+};
+
+struct bfi_sfp_req_s {
+ struct bfi_mhdr_s mh;
+ u8 memtype;
+ u8 rsvd[3];
+ struct bfi_alen_s alen;
+};
+
+struct bfi_sfp_rsp_s {
+ struct bfi_mhdr_s mh;
+ u8 status;
+ u8 state;
+ u8 rsvd[2];
+};
+
+/*
+ * FLASH module specific
+ */
+enum bfi_flash_h2i_msgs {
+ BFI_FLASH_H2I_QUERY_REQ = 1,
+ BFI_FLASH_H2I_ERASE_REQ = 2,
+ BFI_FLASH_H2I_WRITE_REQ = 3,
+ BFI_FLASH_H2I_READ_REQ = 4,
+ BFI_FLASH_H2I_BOOT_VER_REQ = 5,
+};
+
+enum bfi_flash_i2h_msgs {
+ BFI_FLASH_I2H_QUERY_RSP = BFA_I2HM(1),
+ BFI_FLASH_I2H_ERASE_RSP = BFA_I2HM(2),
+ BFI_FLASH_I2H_WRITE_RSP = BFA_I2HM(3),
+ BFI_FLASH_I2H_READ_RSP = BFA_I2HM(4),
+ BFI_FLASH_I2H_BOOT_VER_RSP = BFA_I2HM(5),
+ BFI_FLASH_I2H_EVENT = BFA_I2HM(127),
+};
+
+/*
+ * Flash query request
+ */
+struct bfi_flash_query_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ struct bfi_alen_s alen;
+};
+
+/*
+ * Flash erase request
+ */
+struct bfi_flash_erase_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 type; /* partition type */
+ u8 instance; /* partition instance */
+ u8 rsv[3];
+};
+
+/*
+ * Flash write request
+ */
+struct bfi_flash_write_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ struct bfi_alen_s alen;
+ u32 type; /* partition type */
+ u8 instance; /* partition instance */
+ u8 last;
+ u8 rsv[2];
+ u32 offset;
+ u32 length;
+};
+
+/*
+ * Flash read request
+ */
+struct bfi_flash_read_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 type; /* partition type */
+ u8 instance; /* partition instance */
+ u8 rsv[3];
+ u32 offset;
+ u32 length;
+ struct bfi_alen_s alen;
+};
+
+/*
+ * Flash query response
+ */
+struct bfi_flash_query_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 status;
+};
+
+/*
+ * Flash read response
+ */
+struct bfi_flash_read_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 type; /* partition type */
+ u8 instance; /* partition instance */
+ u8 rsv[3];
+ u32 status;
+ u32 length;
+};
+
+/*
+ * Flash write response
+ */
+struct bfi_flash_write_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 type; /* partition type */
+ u8 instance; /* partition instance */
+ u8 rsv[3];
+ u32 status;
+ u32 length;
+};
+
+/*
+ * Flash erase response
+ */
+struct bfi_flash_erase_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 type; /* partition type */
+ u8 instance; /* partition instance */
+ u8 rsv[3];
+ u32 status;
+};
+
+/*
+ * Flash event notification
+ */
+struct bfi_flash_event_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ bfa_status_t status;
+ u32 param;
+};
+
+/*
+ *----------------------------------------------------------------------
+ * DIAG
+ *----------------------------------------------------------------------
+ */
+enum bfi_diag_h2i {
+ BFI_DIAG_H2I_PORTBEACON = 1,
+ BFI_DIAG_H2I_LOOPBACK = 2,
+ BFI_DIAG_H2I_FWPING = 3,
+ BFI_DIAG_H2I_TEMPSENSOR = 4,
+ BFI_DIAG_H2I_LEDTEST = 5,
+ BFI_DIAG_H2I_QTEST = 6,
+ BFI_DIAG_H2I_DPORT = 7,
+};
+
+enum bfi_diag_i2h {
+ BFI_DIAG_I2H_PORTBEACON = BFA_I2HM(BFI_DIAG_H2I_PORTBEACON),
+ BFI_DIAG_I2H_LOOPBACK = BFA_I2HM(BFI_DIAG_H2I_LOOPBACK),
+ BFI_DIAG_I2H_FWPING = BFA_I2HM(BFI_DIAG_H2I_FWPING),
+ BFI_DIAG_I2H_TEMPSENSOR = BFA_I2HM(BFI_DIAG_H2I_TEMPSENSOR),
+ BFI_DIAG_I2H_LEDTEST = BFA_I2HM(BFI_DIAG_H2I_LEDTEST),
+ BFI_DIAG_I2H_QTEST = BFA_I2HM(BFI_DIAG_H2I_QTEST),
+ BFI_DIAG_I2H_DPORT = BFA_I2HM(BFI_DIAG_H2I_DPORT),
+ BFI_DIAG_I2H_DPORT_SCN = BFA_I2HM(8),
+};
+
+#define BFI_DIAG_MAX_SGES 2
+#define BFI_DIAG_DMA_BUF_SZ (2 * 1024)
+#define BFI_BOOT_MEMTEST_RES_ADDR 0x900
+#define BFI_BOOT_MEMTEST_RES_SIG 0xA0A1A2A3
+
+struct bfi_diag_lb_req_s {
+ struct bfi_mhdr_s mh;
+ u32 loopcnt;
+ u32 pattern;
+ u8 lb_mode; /*!< bfa_port_opmode_t */
+ u8 speed; /*!< bfa_port_speed_t */
+ u8 rsvd[2];
+};
+
+struct bfi_diag_lb_rsp_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ struct bfa_diag_loopback_result_s res; /* 16 bytes */
+};
+
+struct bfi_diag_fwping_req_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ struct bfi_alen_s alen; /* 12 bytes */
+ u32 data; /* user input data pattern */
+ u32 count; /* user input dma count */
+ u8 qtag; /* track CPE vc */
+ u8 rsv[3];
+};
+
+struct bfi_diag_fwping_rsp_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ u32 data; /* user input data pattern */
+ u8 qtag; /* track CPE vc */
+ u8 dma_status; /* dma status */
+ u8 rsv[2];
+};
+
+/*
+ * Temperature Sensor
+ */
+struct bfi_diag_ts_req_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ u16 temp; /* 10-bit A/D value */
+ u16 brd_temp; /* 9-bit board temp */
+ u8 status;
+ u8 ts_junc; /* show junction tempsensor */
+ u8 ts_brd; /* show board tempsensor */
+ u8 rsv;
+};
+#define bfi_diag_ts_rsp_t struct bfi_diag_ts_req_s
+
+struct bfi_diag_ledtest_req_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ u8 cmd;
+ u8 color;
+ u8 portid;
+ u8 led; /* bitmap of LEDs to be tested */
+ u16 freq; /* no. of blinks every 10 secs */
+ u8 rsv[2];
+};
+
+/* notify host led operation is done */
+struct bfi_diag_ledtest_rsp_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+};
+
+struct bfi_diag_portbeacon_req_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ u32 period; /* beaconing period */
+ u8 beacon; /* 1: beacon on */
+ u8 rsvd[3];
+};
+
+/* notify host the beacon is off */
+struct bfi_diag_portbeacon_rsp_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+};
+
+struct bfi_diag_qtest_req_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ u32 data[BFI_LMSG_PL_WSZ]; /* fill up tcm prefetch area */
+};
+#define bfi_diag_qtest_rsp_t struct bfi_diag_qtest_req_s
+
+/*
+ * D-port test
+ */
+enum bfi_dport_req {
+ BFI_DPORT_DISABLE = 0, /* disable dport request */
+ BFI_DPORT_ENABLE = 1, /* enable dport request */
+ BFI_DPORT_START = 2, /* start dport request */
+ BFI_DPORT_SHOW = 3, /* show dport request */
+ BFI_DPORT_DYN_DISABLE = 4, /* disable dynamic dport request */
+};
+
+enum bfi_dport_scn {
+ BFI_DPORT_SCN_TESTSTART = 1,
+ BFI_DPORT_SCN_TESTCOMP = 2,
+ BFI_DPORT_SCN_SFP_REMOVED = 3,
+ BFI_DPORT_SCN_DDPORT_ENABLE = 4,
+ BFI_DPORT_SCN_DDPORT_DISABLE = 5,
+ BFI_DPORT_SCN_FCPORT_DISABLE = 6,
+ BFI_DPORT_SCN_SUBTESTSTART = 7,
+ BFI_DPORT_SCN_TESTSKIP = 8,
+ BFI_DPORT_SCN_DDPORT_DISABLED = 9,
+};
+
+struct bfi_diag_dport_req_s {
+ struct bfi_mhdr_s mh; /* 4 bytes */
+ u8 req; /* request 1: enable 0: disable */
+ u8 rsvd[3];
+ u32 lpcnt;
+ u32 payload;
+};
+
+struct bfi_diag_dport_rsp_s {
+ struct bfi_mhdr_s mh; /* header 4 bytes */
+ bfa_status_t status; /* reply status */
+ wwn_t pwwn; /* switch port wwn. 8 bytes */
+ wwn_t nwwn; /* switch node wwn. 8 bytes */
+};
+
+struct bfi_diag_dport_scn_teststart_s {
+ wwn_t pwwn; /* switch port wwn. 8 bytes */
+ wwn_t nwwn; /* switch node wwn. 8 bytes */
+ u8 type; /* bfa_diag_dport_test_type_e */
+ u8 mode; /* bfa_diag_dport_test_opmode */
+ u8 rsvd[2];
+ u32 numfrm; /* from switch uint in 1M */
+};
+
+struct bfi_diag_dport_scn_testcomp_s {
+ u8 status; /* bfa_diag_dport_test_status_e */
+ u8 speed; /* bfa_port_speed_t */
+ u16 numbuffer; /* from switch */
+ u8 subtest_status[DPORT_TEST_MAX]; /* 4 bytes */
+ u32 latency; /* from switch */
+ u32 distance; /* from switch unit in meters */
+ /* Buffers required to saturate the link */
+ u16 frm_sz; /* from switch for buf_reqd */
+ u8 rsvd[2];
+};
+
+struct bfi_diag_dport_scn_s { /* max size == RDS_RMESZ */
+ struct bfi_mhdr_s mh; /* header 4 bytes */
+ u8 state; /* new state */
+ u8 rsvd[3];
+ union {
+ struct bfi_diag_dport_scn_teststart_s teststart;
+ struct bfi_diag_dport_scn_testcomp_s testcomp;
+ } info;
+};
+
+union bfi_diag_dport_msg_u {
+ struct bfi_diag_dport_req_s req;
+ struct bfi_diag_dport_rsp_s rsp;
+ struct bfi_diag_dport_scn_s scn;
+};
+
+/*
+ * PHY module specific
+ */
+enum bfi_phy_h2i_msgs_e {
+ BFI_PHY_H2I_QUERY_REQ = 1,
+ BFI_PHY_H2I_STATS_REQ = 2,
+ BFI_PHY_H2I_WRITE_REQ = 3,
+ BFI_PHY_H2I_READ_REQ = 4,
+};
+
+enum bfi_phy_i2h_msgs_e {
+ BFI_PHY_I2H_QUERY_RSP = BFA_I2HM(1),
+ BFI_PHY_I2H_STATS_RSP = BFA_I2HM(2),
+ BFI_PHY_I2H_WRITE_RSP = BFA_I2HM(3),
+ BFI_PHY_I2H_READ_RSP = BFA_I2HM(4),
+};
+
+/*
+ * External PHY query request
+ */
+struct bfi_phy_query_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u8 instance;
+ u8 rsv[3];
+ struct bfi_alen_s alen;
+};
+
+/*
+ * External PHY stats request
+ */
+struct bfi_phy_stats_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u8 instance;
+ u8 rsv[3];
+ struct bfi_alen_s alen;
+};
+
+/*
+ * External PHY write request
+ */
+struct bfi_phy_write_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u8 instance;
+ u8 last;
+ u8 rsv[2];
+ u32 offset;
+ u32 length;
+ struct bfi_alen_s alen;
+};
+
+/*
+ * External PHY read request
+ */
+struct bfi_phy_read_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u8 instance;
+ u8 rsv[3];
+ u32 offset;
+ u32 length;
+ struct bfi_alen_s alen;
+};
+
+/*
+ * External PHY query response
+ */
+struct bfi_phy_query_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 status;
+};
+
+/*
+ * External PHY stats response
+ */
+struct bfi_phy_stats_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 status;
+};
+
+/*
+ * External PHY read response
+ */
+struct bfi_phy_read_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 status;
+ u32 length;
+};
+
+/*
+ * External PHY write response
+ */
+struct bfi_phy_write_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 status;
+ u32 length;
+};
+
+enum bfi_fru_h2i_msgs {
+ BFI_FRUVPD_H2I_WRITE_REQ = 1,
+ BFI_FRUVPD_H2I_READ_REQ = 2,
+ BFI_TFRU_H2I_WRITE_REQ = 3,
+ BFI_TFRU_H2I_READ_REQ = 4,
+};
+
+enum bfi_fru_i2h_msgs {
+ BFI_FRUVPD_I2H_WRITE_RSP = BFA_I2HM(1),
+ BFI_FRUVPD_I2H_READ_RSP = BFA_I2HM(2),
+ BFI_TFRU_I2H_WRITE_RSP = BFA_I2HM(3),
+ BFI_TFRU_I2H_READ_RSP = BFA_I2HM(4),
+};
+
+/*
+ * FRU write request
+ */
+struct bfi_fru_write_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u8 last;
+ u8 rsv_1[3];
+ u8 trfr_cmpl;
+ u8 rsv_2[3];
+ u32 offset;
+ u32 length;
+ struct bfi_alen_s alen;
+};
+
+/*
+ * FRU read request
+ */
+struct bfi_fru_read_req_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 offset;
+ u32 length;
+ struct bfi_alen_s alen;
+};
+
+/*
+ * FRU response
+ */
+struct bfi_fru_rsp_s {
+ struct bfi_mhdr_s mh; /* Common msg header */
+ u32 status;
+ u32 length;
+};
#pragma pack()
#endif /* __BFI_H__ */
diff --git a/drivers/scsi/bfa/bfi_cbreg.h b/drivers/scsi/bfa/bfi_cbreg.h
deleted file mode 100644
index 39ad42b66b5b..000000000000
--- a/drivers/scsi/bfa/bfi_cbreg.h
+++ /dev/null
@@ -1,305 +0,0 @@
-
-/*
- * bfi_cbreg.h crossbow host block register definitions
- *
- * !!! Do not edit. Auto generated. !!!
- */
-
-#ifndef __BFI_CBREG_H__
-#define __BFI_CBREG_H__
-
-
-#define HOSTFN0_INT_STATUS 0x00014000
-#define __HOSTFN0_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN0_INT_STATUS_LVL_SH 20
-#define __HOSTFN0_INT_STATUS_LVL(_v) ((_v) << __HOSTFN0_INT_STATUS_LVL_SH)
-#define __HOSTFN0_INT_STATUS_P 0x000fffff
-#define HOSTFN0_INT_MSK 0x00014004
-#define HOST_PAGE_NUM_FN0 0x00014008
-#define __HOST_PAGE_NUM_FN 0x000001ff
-#define HOSTFN1_INT_STATUS 0x00014100
-#define __HOSTFN1_INT_STAT_LVL_MK 0x00f00000
-#define __HOSTFN1_INT_STAT_LVL_SH 20
-#define __HOSTFN1_INT_STAT_LVL(_v) ((_v) << __HOSTFN1_INT_STAT_LVL_SH)
-#define __HOSTFN1_INT_STAT_P 0x000fffff
-#define HOSTFN1_INT_MSK 0x00014104
-#define HOST_PAGE_NUM_FN1 0x00014108
-#define APP_PLL_400_CTL_REG 0x00014204
-#define __P_400_PLL_LOCK 0x80000000
-#define __APP_PLL_400_SRAM_USE_100MHZ 0x00100000
-#define __APP_PLL_400_RESET_TIMER_MK 0x000e0000
-#define __APP_PLL_400_RESET_TIMER_SH 17
-#define __APP_PLL_400_RESET_TIMER(_v) ((_v) << __APP_PLL_400_RESET_TIMER_SH)
-#define __APP_PLL_400_LOGIC_SOFT_RESET 0x00010000
-#define __APP_PLL_400_CNTLMT0_1_MK 0x0000c000
-#define __APP_PLL_400_CNTLMT0_1_SH 14
-#define __APP_PLL_400_CNTLMT0_1(_v) ((_v) << __APP_PLL_400_CNTLMT0_1_SH)
-#define __APP_PLL_400_JITLMT0_1_MK 0x00003000
-#define __APP_PLL_400_JITLMT0_1_SH 12
-#define __APP_PLL_400_JITLMT0_1(_v) ((_v) << __APP_PLL_400_JITLMT0_1_SH)
-#define __APP_PLL_400_HREF 0x00000800
-#define __APP_PLL_400_HDIV 0x00000400
-#define __APP_PLL_400_P0_1_MK 0x00000300
-#define __APP_PLL_400_P0_1_SH 8
-#define __APP_PLL_400_P0_1(_v) ((_v) << __APP_PLL_400_P0_1_SH)
-#define __APP_PLL_400_Z0_2_MK 0x000000e0
-#define __APP_PLL_400_Z0_2_SH 5
-#define __APP_PLL_400_Z0_2(_v) ((_v) << __APP_PLL_400_Z0_2_SH)
-#define __APP_PLL_400_RSEL200500 0x00000010
-#define __APP_PLL_400_ENARST 0x00000008
-#define __APP_PLL_400_BYPASS 0x00000004
-#define __APP_PLL_400_LRESETN 0x00000002
-#define __APP_PLL_400_ENABLE 0x00000001
-#define APP_PLL_212_CTL_REG 0x00014208
-#define __P_212_PLL_LOCK 0x80000000
-#define __APP_PLL_212_RESET_TIMER_MK 0x000e0000
-#define __APP_PLL_212_RESET_TIMER_SH 17
-#define __APP_PLL_212_RESET_TIMER(_v) ((_v) << __APP_PLL_212_RESET_TIMER_SH)
-#define __APP_PLL_212_LOGIC_SOFT_RESET 0x00010000
-#define __APP_PLL_212_CNTLMT0_1_MK 0x0000c000
-#define __APP_PLL_212_CNTLMT0_1_SH 14
-#define __APP_PLL_212_CNTLMT0_1(_v) ((_v) << __APP_PLL_212_CNTLMT0_1_SH)
-#define __APP_PLL_212_JITLMT0_1_MK 0x00003000
-#define __APP_PLL_212_JITLMT0_1_SH 12
-#define __APP_PLL_212_JITLMT0_1(_v) ((_v) << __APP_PLL_212_JITLMT0_1_SH)
-#define __APP_PLL_212_HREF 0x00000800
-#define __APP_PLL_212_HDIV 0x00000400
-#define __APP_PLL_212_P0_1_MK 0x00000300
-#define __APP_PLL_212_P0_1_SH 8
-#define __APP_PLL_212_P0_1(_v) ((_v) << __APP_PLL_212_P0_1_SH)
-#define __APP_PLL_212_Z0_2_MK 0x000000e0
-#define __APP_PLL_212_Z0_2_SH 5
-#define __APP_PLL_212_Z0_2(_v) ((_v) << __APP_PLL_212_Z0_2_SH)
-#define __APP_PLL_212_RSEL200500 0x00000010
-#define __APP_PLL_212_ENARST 0x00000008
-#define __APP_PLL_212_BYPASS 0x00000004
-#define __APP_PLL_212_LRESETN 0x00000002
-#define __APP_PLL_212_ENABLE 0x00000001
-#define HOST_SEM0_REG 0x00014230
-#define __HOST_SEMAPHORE 0x00000001
-#define HOST_SEM1_REG 0x00014234
-#define HOST_SEM2_REG 0x00014238
-#define HOST_SEM3_REG 0x0001423c
-#define HOST_SEM0_INFO_REG 0x00014240
-#define HOST_SEM1_INFO_REG 0x00014244
-#define HOST_SEM2_INFO_REG 0x00014248
-#define HOST_SEM3_INFO_REG 0x0001424c
-#define HOSTFN0_LPU0_CMD_STAT 0x00019000
-#define __HOSTFN0_LPU0_MBOX_INFO_MK 0xfffffffe
-#define __HOSTFN0_LPU0_MBOX_INFO_SH 1
-#define __HOSTFN0_LPU0_MBOX_INFO(_v) ((_v) << __HOSTFN0_LPU0_MBOX_INFO_SH)
-#define __HOSTFN0_LPU0_MBOX_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN0_CMD_STAT 0x00019008
-#define __LPU0_HOSTFN0_MBOX_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN0_MBOX_INFO_SH 1
-#define __LPU0_HOSTFN0_MBOX_INFO(_v) ((_v) << __LPU0_HOSTFN0_MBOX_INFO_SH)
-#define __LPU0_HOSTFN0_MBOX_CMD_STATUS 0x00000001
-#define HOSTFN1_LPU1_CMD_STAT 0x00019014
-#define __HOSTFN1_LPU1_MBOX_INFO_MK 0xfffffffe
-#define __HOSTFN1_LPU1_MBOX_INFO_SH 1
-#define __HOSTFN1_LPU1_MBOX_INFO(_v) ((_v) << __HOSTFN1_LPU1_MBOX_INFO_SH)
-#define __HOSTFN1_LPU1_MBOX_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN1_CMD_STAT 0x0001901c
-#define __LPU1_HOSTFN1_MBOX_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN1_MBOX_INFO_SH 1
-#define __LPU1_HOSTFN1_MBOX_INFO(_v) ((_v) << __LPU1_HOSTFN1_MBOX_INFO_SH)
-#define __LPU1_HOSTFN1_MBOX_CMD_STATUS 0x00000001
-#define CPE_Q0_DEPTH 0x00010014
-#define CPE_Q0_PI 0x0001001c
-#define CPE_Q0_CI 0x00010020
-#define CPE_Q1_DEPTH 0x00010034
-#define CPE_Q1_PI 0x0001003c
-#define CPE_Q1_CI 0x00010040
-#define CPE_Q2_DEPTH 0x00010054
-#define CPE_Q2_PI 0x0001005c
-#define CPE_Q2_CI 0x00010060
-#define CPE_Q3_DEPTH 0x00010074
-#define CPE_Q3_PI 0x0001007c
-#define CPE_Q3_CI 0x00010080
-#define CPE_Q4_DEPTH 0x00010094
-#define CPE_Q4_PI 0x0001009c
-#define CPE_Q4_CI 0x000100a0
-#define CPE_Q5_DEPTH 0x000100b4
-#define CPE_Q5_PI 0x000100bc
-#define CPE_Q5_CI 0x000100c0
-#define CPE_Q6_DEPTH 0x000100d4
-#define CPE_Q6_PI 0x000100dc
-#define CPE_Q6_CI 0x000100e0
-#define CPE_Q7_DEPTH 0x000100f4
-#define CPE_Q7_PI 0x000100fc
-#define CPE_Q7_CI 0x00010100
-#define RME_Q0_DEPTH 0x00011014
-#define RME_Q0_PI 0x0001101c
-#define RME_Q0_CI 0x00011020
-#define RME_Q1_DEPTH 0x00011034
-#define RME_Q1_PI 0x0001103c
-#define RME_Q1_CI 0x00011040
-#define RME_Q2_DEPTH 0x00011054
-#define RME_Q2_PI 0x0001105c
-#define RME_Q2_CI 0x00011060
-#define RME_Q3_DEPTH 0x00011074
-#define RME_Q3_PI 0x0001107c
-#define RME_Q3_CI 0x00011080
-#define RME_Q4_DEPTH 0x00011094
-#define RME_Q4_PI 0x0001109c
-#define RME_Q4_CI 0x000110a0
-#define RME_Q5_DEPTH 0x000110b4
-#define RME_Q5_PI 0x000110bc
-#define RME_Q5_CI 0x000110c0
-#define RME_Q6_DEPTH 0x000110d4
-#define RME_Q6_PI 0x000110dc
-#define RME_Q6_CI 0x000110e0
-#define RME_Q7_DEPTH 0x000110f4
-#define RME_Q7_PI 0x000110fc
-#define RME_Q7_CI 0x00011100
-#define PSS_CTL_REG 0x00018800
-#define __PSS_I2C_CLK_DIV_MK 0x00030000
-#define __PSS_I2C_CLK_DIV_SH 16
-#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH)
-#define __PSS_LMEM_INIT_DONE 0x00001000
-#define __PSS_LMEM_RESET 0x00000200
-#define __PSS_LMEM_INIT_EN 0x00000100
-#define __PSS_LPU1_RESET 0x00000002
-#define __PSS_LPU0_RESET 0x00000001
-#define PSS_ERR_STATUS_REG 0x00018810
-#define __PSS_LMEM1_CORR_ERR 0x00000800
-#define __PSS_LMEM0_CORR_ERR 0x00000400
-#define __PSS_LMEM1_UNCORR_ERR 0x00000200
-#define __PSS_LMEM0_UNCORR_ERR 0x00000100
-#define __PSS_BAL_PERR 0x00000080
-#define __PSS_DIP_IF_ERR 0x00000040
-#define __PSS_IOH_IF_ERR 0x00000020
-#define __PSS_TDS_IF_ERR 0x00000010
-#define __PSS_RDS_IF_ERR 0x00000008
-#define __PSS_SGM_IF_ERR 0x00000004
-#define __PSS_LPU1_RAM_ERR 0x00000002
-#define __PSS_LPU0_RAM_ERR 0x00000001
-#define ERR_SET_REG 0x00018818
-#define __PSS_ERR_STATUS_SET 0x00000fff
-
-
-/*
- * These definitions are either in error/missing in spec. Its auto-generated
- * from hard coded values in regparse.pl.
- */
-#define __EMPHPOST_AT_4G_MK_FIX 0x0000001c
-#define __EMPHPOST_AT_4G_SH_FIX 0x00000002
-#define __EMPHPRE_AT_4G_FIX 0x00000003
-#define __SFP_TXRATE_EN_FIX 0x00000100
-#define __SFP_RXRATE_EN_FIX 0x00000080
-
-
-/*
- * These register definitions are auto-generated from hard coded values
- * in regparse.pl.
- */
-#define HOSTFN0_LPU_MBOX0_0 0x00019200
-#define HOSTFN1_LPU_MBOX0_8 0x00019260
-#define LPU_HOSTFN0_MBOX0_0 0x00019280
-#define LPU_HOSTFN1_MBOX0_8 0x000192e0
-
-
-/*
- * These register mapping definitions are auto-generated from mapping tables
- * in regparse.pl.
- */
-#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG
-#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG
-#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG
-#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG
-#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG
-#define BFA_IOC_FAIL_SYNC HOST_SEM5_INFO_REG
-
-#define CPE_Q_DEPTH(__n) \
- (CPE_Q0_DEPTH + (__n) * (CPE_Q1_DEPTH - CPE_Q0_DEPTH))
-#define CPE_Q_PI(__n) \
- (CPE_Q0_PI + (__n) * (CPE_Q1_PI - CPE_Q0_PI))
-#define CPE_Q_CI(__n) \
- (CPE_Q0_CI + (__n) * (CPE_Q1_CI - CPE_Q0_CI))
-#define RME_Q_DEPTH(__n) \
- (RME_Q0_DEPTH + (__n) * (RME_Q1_DEPTH - RME_Q0_DEPTH))
-#define RME_Q_PI(__n) \
- (RME_Q0_PI + (__n) * (RME_Q1_PI - RME_Q0_PI))
-#define RME_Q_CI(__n) \
- (RME_Q0_CI + (__n) * (RME_Q1_CI - RME_Q0_CI))
-
-#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
-#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
-#define CPE_Q_MASK(__q) ((__q) & 0x3)
-#define RME_Q_MASK(__q) ((__q) & 0x3)
-
-
-/*
- * PCI MSI-X vector defines
- */
-enum {
- BFA_MSIX_CPE_Q0 = 0,
- BFA_MSIX_CPE_Q1 = 1,
- BFA_MSIX_CPE_Q2 = 2,
- BFA_MSIX_CPE_Q3 = 3,
- BFA_MSIX_CPE_Q4 = 4,
- BFA_MSIX_CPE_Q5 = 5,
- BFA_MSIX_CPE_Q6 = 6,
- BFA_MSIX_CPE_Q7 = 7,
- BFA_MSIX_RME_Q0 = 8,
- BFA_MSIX_RME_Q1 = 9,
- BFA_MSIX_RME_Q2 = 10,
- BFA_MSIX_RME_Q3 = 11,
- BFA_MSIX_RME_Q4 = 12,
- BFA_MSIX_RME_Q5 = 13,
- BFA_MSIX_RME_Q6 = 14,
- BFA_MSIX_RME_Q7 = 15,
- BFA_MSIX_ERR_EMC = 16,
- BFA_MSIX_ERR_LPU0 = 17,
- BFA_MSIX_ERR_LPU1 = 18,
- BFA_MSIX_ERR_PSS = 19,
- BFA_MSIX_MBOX_LPU0 = 20,
- BFA_MSIX_MBOX_LPU1 = 21,
- BFA_MSIX_CB_MAX = 22,
-};
-
-/*
- * And corresponding host interrupt status bit field defines
- */
-#define __HFN_INT_CPE_Q0 0x00000001U
-#define __HFN_INT_CPE_Q1 0x00000002U
-#define __HFN_INT_CPE_Q2 0x00000004U
-#define __HFN_INT_CPE_Q3 0x00000008U
-#define __HFN_INT_CPE_Q4 0x00000010U
-#define __HFN_INT_CPE_Q5 0x00000020U
-#define __HFN_INT_CPE_Q6 0x00000040U
-#define __HFN_INT_CPE_Q7 0x00000080U
-#define __HFN_INT_RME_Q0 0x00000100U
-#define __HFN_INT_RME_Q1 0x00000200U
-#define __HFN_INT_RME_Q2 0x00000400U
-#define __HFN_INT_RME_Q3 0x00000800U
-#define __HFN_INT_RME_Q4 0x00001000U
-#define __HFN_INT_RME_Q5 0x00002000U
-#define __HFN_INT_RME_Q6 0x00004000U
-#define __HFN_INT_RME_Q7 0x00008000U
-#define __HFN_INT_ERR_EMC 0x00010000U
-#define __HFN_INT_ERR_LPU0 0x00020000U
-#define __HFN_INT_ERR_LPU1 0x00040000U
-#define __HFN_INT_ERR_PSS 0x00080000U
-#define __HFN_INT_MBOX_LPU0 0x00100000U
-#define __HFN_INT_MBOX_LPU1 0x00200000U
-#define __HFN_INT_MBOX1_LPU0 0x00400000U
-#define __HFN_INT_MBOX1_LPU1 0x00800000U
-#define __HFN_INT_CPE_MASK 0x000000ffU
-#define __HFN_INT_RME_MASK 0x0000ff00U
-
-
-/*
- * crossbow memory map.
- */
-#define PSS_SMEM_PAGE_START 0x8000
-#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15))
-#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff)
-
-/*
- * End of crossbow memory map
- */
-
-
-#endif /* __BFI_CBREG_H__ */
-
diff --git a/drivers/scsi/bfa/bfi_ctreg.h b/drivers/scsi/bfa/bfi_ctreg.h
deleted file mode 100644
index fc4ce4a5a183..000000000000
--- a/drivers/scsi/bfa/bfi_ctreg.h
+++ /dev/null
@@ -1,636 +0,0 @@
-
-/*
- * bfi_ctreg.h catapult host block register definitions
- *
- * !!! Do not edit. Auto generated. !!!
- */
-
-#ifndef __BFI_CTREG_H__
-#define __BFI_CTREG_H__
-
-
-#define HOSTFN0_LPU_MBOX0_0 0x00019200
-#define HOSTFN1_LPU_MBOX0_8 0x00019260
-#define LPU_HOSTFN0_MBOX0_0 0x00019280
-#define LPU_HOSTFN1_MBOX0_8 0x000192e0
-#define HOSTFN2_LPU_MBOX0_0 0x00019400
-#define HOSTFN3_LPU_MBOX0_8 0x00019460
-#define LPU_HOSTFN2_MBOX0_0 0x00019480
-#define LPU_HOSTFN3_MBOX0_8 0x000194e0
-#define HOSTFN0_INT_STATUS 0x00014000
-#define __HOSTFN0_HALT_OCCURRED 0x01000000
-#define __HOSTFN0_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN0_INT_STATUS_LVL_SH 20
-#define __HOSTFN0_INT_STATUS_LVL(_v) ((_v) << __HOSTFN0_INT_STATUS_LVL_SH)
-#define __HOSTFN0_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN0_INT_STATUS_P_SH 16
-#define __HOSTFN0_INT_STATUS_P(_v) ((_v) << __HOSTFN0_INT_STATUS_P_SH)
-#define __HOSTFN0_INT_STATUS_F 0x0000ffff
-#define HOSTFN0_INT_MSK 0x00014004
-#define HOST_PAGE_NUM_FN0 0x00014008
-#define __HOST_PAGE_NUM_FN 0x000001ff
-#define HOST_MSIX_ERR_INDEX_FN0 0x0001400c
-#define __MSIX_ERR_INDEX_FN 0x000001ff
-#define HOSTFN1_INT_STATUS 0x00014100
-#define __HOSTFN1_HALT_OCCURRED 0x01000000
-#define __HOSTFN1_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN1_INT_STATUS_LVL_SH 20
-#define __HOSTFN1_INT_STATUS_LVL(_v) ((_v) << __HOSTFN1_INT_STATUS_LVL_SH)
-#define __HOSTFN1_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN1_INT_STATUS_P_SH 16
-#define __HOSTFN1_INT_STATUS_P(_v) ((_v) << __HOSTFN1_INT_STATUS_P_SH)
-#define __HOSTFN1_INT_STATUS_F 0x0000ffff
-#define HOSTFN1_INT_MSK 0x00014104
-#define HOST_PAGE_NUM_FN1 0x00014108
-#define HOST_MSIX_ERR_INDEX_FN1 0x0001410c
-#define APP_PLL_425_CTL_REG 0x00014204
-#define __P_425_PLL_LOCK 0x80000000
-#define __APP_PLL_425_SRAM_USE_100MHZ 0x00100000
-#define __APP_PLL_425_RESET_TIMER_MK 0x000e0000
-#define __APP_PLL_425_RESET_TIMER_SH 17
-#define __APP_PLL_425_RESET_TIMER(_v) ((_v) << __APP_PLL_425_RESET_TIMER_SH)
-#define __APP_PLL_425_LOGIC_SOFT_RESET 0x00010000
-#define __APP_PLL_425_CNTLMT0_1_MK 0x0000c000
-#define __APP_PLL_425_CNTLMT0_1_SH 14
-#define __APP_PLL_425_CNTLMT0_1(_v) ((_v) << __APP_PLL_425_CNTLMT0_1_SH)
-#define __APP_PLL_425_JITLMT0_1_MK 0x00003000
-#define __APP_PLL_425_JITLMT0_1_SH 12
-#define __APP_PLL_425_JITLMT0_1(_v) ((_v) << __APP_PLL_425_JITLMT0_1_SH)
-#define __APP_PLL_425_HREF 0x00000800
-#define __APP_PLL_425_HDIV 0x00000400
-#define __APP_PLL_425_P0_1_MK 0x00000300
-#define __APP_PLL_425_P0_1_SH 8
-#define __APP_PLL_425_P0_1(_v) ((_v) << __APP_PLL_425_P0_1_SH)
-#define __APP_PLL_425_Z0_2_MK 0x000000e0
-#define __APP_PLL_425_Z0_2_SH 5
-#define __APP_PLL_425_Z0_2(_v) ((_v) << __APP_PLL_425_Z0_2_SH)
-#define __APP_PLL_425_RSEL200500 0x00000010
-#define __APP_PLL_425_ENARST 0x00000008
-#define __APP_PLL_425_BYPASS 0x00000004
-#define __APP_PLL_425_LRESETN 0x00000002
-#define __APP_PLL_425_ENABLE 0x00000001
-#define APP_PLL_312_CTL_REG 0x00014208
-#define __P_312_PLL_LOCK 0x80000000
-#define __ENABLE_MAC_AHB_1 0x00800000
-#define __ENABLE_MAC_AHB_0 0x00400000
-#define __ENABLE_MAC_1 0x00200000
-#define __ENABLE_MAC_0 0x00100000
-#define __APP_PLL_312_RESET_TIMER_MK 0x000e0000
-#define __APP_PLL_312_RESET_TIMER_SH 17
-#define __APP_PLL_312_RESET_TIMER(_v) ((_v) << __APP_PLL_312_RESET_TIMER_SH)
-#define __APP_PLL_312_LOGIC_SOFT_RESET 0x00010000
-#define __APP_PLL_312_CNTLMT0_1_MK 0x0000c000
-#define __APP_PLL_312_CNTLMT0_1_SH 14
-#define __APP_PLL_312_CNTLMT0_1(_v) ((_v) << __APP_PLL_312_CNTLMT0_1_SH)
-#define __APP_PLL_312_JITLMT0_1_MK 0x00003000
-#define __APP_PLL_312_JITLMT0_1_SH 12
-#define __APP_PLL_312_JITLMT0_1(_v) ((_v) << __APP_PLL_312_JITLMT0_1_SH)
-#define __APP_PLL_312_HREF 0x00000800
-#define __APP_PLL_312_HDIV 0x00000400
-#define __APP_PLL_312_P0_1_MK 0x00000300
-#define __APP_PLL_312_P0_1_SH 8
-#define __APP_PLL_312_P0_1(_v) ((_v) << __APP_PLL_312_P0_1_SH)
-#define __APP_PLL_312_Z0_2_MK 0x000000e0
-#define __APP_PLL_312_Z0_2_SH 5
-#define __APP_PLL_312_Z0_2(_v) ((_v) << __APP_PLL_312_Z0_2_SH)
-#define __APP_PLL_312_RSEL200500 0x00000010
-#define __APP_PLL_312_ENARST 0x00000008
-#define __APP_PLL_312_BYPASS 0x00000004
-#define __APP_PLL_312_LRESETN 0x00000002
-#define __APP_PLL_312_ENABLE 0x00000001
-#define MBIST_CTL_REG 0x00014220
-#define __EDRAM_BISTR_START 0x00000004
-#define __MBIST_RESET 0x00000002
-#define __MBIST_START 0x00000001
-#define MBIST_STAT_REG 0x00014224
-#define __EDRAM_BISTR_STATUS 0x00000008
-#define __EDRAM_BISTR_DONE 0x00000004
-#define __MEM_BIT_STATUS 0x00000002
-#define __MBIST_DONE 0x00000001
-#define HOST_SEM0_REG 0x00014230
-#define __HOST_SEMAPHORE 0x00000001
-#define HOST_SEM1_REG 0x00014234
-#define HOST_SEM2_REG 0x00014238
-#define HOST_SEM3_REG 0x0001423c
-#define HOST_SEM0_INFO_REG 0x00014240
-#define HOST_SEM1_INFO_REG 0x00014244
-#define HOST_SEM2_INFO_REG 0x00014248
-#define HOST_SEM3_INFO_REG 0x0001424c
-#define ETH_MAC_SER_REG 0x00014288
-#define __APP_EMS_CKBUFAMPIN 0x00000020
-#define __APP_EMS_REFCLKSEL 0x00000010
-#define __APP_EMS_CMLCKSEL 0x00000008
-#define __APP_EMS_REFCKBUFEN2 0x00000004
-#define __APP_EMS_REFCKBUFEN1 0x00000002
-#define __APP_EMS_CHANNEL_SEL 0x00000001
-#define HOSTFN2_INT_STATUS 0x00014300
-#define __HOSTFN2_HALT_OCCURRED 0x01000000
-#define __HOSTFN2_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN2_INT_STATUS_LVL_SH 20
-#define __HOSTFN2_INT_STATUS_LVL(_v) ((_v) << __HOSTFN2_INT_STATUS_LVL_SH)
-#define __HOSTFN2_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN2_INT_STATUS_P_SH 16
-#define __HOSTFN2_INT_STATUS_P(_v) ((_v) << __HOSTFN2_INT_STATUS_P_SH)
-#define __HOSTFN2_INT_STATUS_F 0x0000ffff
-#define HOSTFN2_INT_MSK 0x00014304
-#define HOST_PAGE_NUM_FN2 0x00014308
-#define HOST_MSIX_ERR_INDEX_FN2 0x0001430c
-#define HOSTFN3_INT_STATUS 0x00014400
-#define __HALT_OCCURRED 0x01000000
-#define __HOSTFN3_INT_STATUS_LVL_MK 0x00f00000
-#define __HOSTFN3_INT_STATUS_LVL_SH 20
-#define __HOSTFN3_INT_STATUS_LVL(_v) ((_v) << __HOSTFN3_INT_STATUS_LVL_SH)
-#define __HOSTFN3_INT_STATUS_P_MK 0x000f0000
-#define __HOSTFN3_INT_STATUS_P_SH 16
-#define __HOSTFN3_INT_STATUS_P(_v) ((_v) << __HOSTFN3_INT_STATUS_P_SH)
-#define __HOSTFN3_INT_STATUS_F 0x0000ffff
-#define HOSTFN3_INT_MSK 0x00014404
-#define HOST_PAGE_NUM_FN3 0x00014408
-#define HOST_MSIX_ERR_INDEX_FN3 0x0001440c
-#define FNC_ID_REG 0x00014600
-#define __FUNCTION_NUMBER 0x00000007
-#define FNC_PERS_REG 0x00014604
-#define __F3_FUNCTION_ACTIVE 0x80000000
-#define __F3_FUNCTION_MODE 0x40000000
-#define __F3_PORT_MAP_MK 0x30000000
-#define __F3_PORT_MAP_SH 28
-#define __F3_PORT_MAP(_v) ((_v) << __F3_PORT_MAP_SH)
-#define __F3_VM_MODE 0x08000000
-#define __F3_INTX_STATUS_MK 0x07000000
-#define __F3_INTX_STATUS_SH 24
-#define __F3_INTX_STATUS(_v) ((_v) << __F3_INTX_STATUS_SH)
-#define __F2_FUNCTION_ACTIVE 0x00800000
-#define __F2_FUNCTION_MODE 0x00400000
-#define __F2_PORT_MAP_MK 0x00300000
-#define __F2_PORT_MAP_SH 20
-#define __F2_PORT_MAP(_v) ((_v) << __F2_PORT_MAP_SH)
-#define __F2_VM_MODE 0x00080000
-#define __F2_INTX_STATUS_MK 0x00070000
-#define __F2_INTX_STATUS_SH 16
-#define __F2_INTX_STATUS(_v) ((_v) << __F2_INTX_STATUS_SH)
-#define __F1_FUNCTION_ACTIVE 0x00008000
-#define __F1_FUNCTION_MODE 0x00004000
-#define __F1_PORT_MAP_MK 0x00003000
-#define __F1_PORT_MAP_SH 12
-#define __F1_PORT_MAP(_v) ((_v) << __F1_PORT_MAP_SH)
-#define __F1_VM_MODE 0x00000800
-#define __F1_INTX_STATUS_MK 0x00000700
-#define __F1_INTX_STATUS_SH 8
-#define __F1_INTX_STATUS(_v) ((_v) << __F1_INTX_STATUS_SH)
-#define __F0_FUNCTION_ACTIVE 0x00000080
-#define __F0_FUNCTION_MODE 0x00000040
-#define __F0_PORT_MAP_MK 0x00000030
-#define __F0_PORT_MAP_SH 4
-#define __F0_PORT_MAP(_v) ((_v) << __F0_PORT_MAP_SH)
-#define __F0_VM_MODE 0x00000008
-#define __F0_INTX_STATUS 0x00000007
-enum {
- __F0_INTX_STATUS_MSIX = 0x0,
- __F0_INTX_STATUS_INTA = 0x1,
- __F0_INTX_STATUS_INTB = 0x2,
- __F0_INTX_STATUS_INTC = 0x3,
- __F0_INTX_STATUS_INTD = 0x4,
-};
-#define OP_MODE 0x0001460c
-#define __APP_ETH_CLK_LOWSPEED 0x00000004
-#define __GLOBAL_CORECLK_HALFSPEED 0x00000002
-#define __GLOBAL_FCOE_MODE 0x00000001
-#define HOST_SEM4_REG 0x00014610
-#define HOST_SEM5_REG 0x00014614
-#define HOST_SEM6_REG 0x00014618
-#define HOST_SEM7_REG 0x0001461c
-#define HOST_SEM4_INFO_REG 0x00014620
-#define HOST_SEM5_INFO_REG 0x00014624
-#define HOST_SEM6_INFO_REG 0x00014628
-#define HOST_SEM7_INFO_REG 0x0001462c
-#define HOSTFN0_LPU0_MBOX0_CMD_STAT 0x00019000
-#define __HOSTFN0_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN0_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN0_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN0_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN0_LPU1_MBOX0_CMD_STAT 0x00019004
-#define __HOSTFN0_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN0_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN0_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN0_LPU1_MBOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN0_MBOX0_CMD_STAT 0x00019008
-#define __LPU0_HOSTFN0_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN0_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN0_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN0_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN0_MBOX0_CMD_STAT 0x0001900c
-#define __LPU1_HOSTFN0_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN0_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN0_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN1_LPU0_MBOX0_CMD_STAT 0x00019010
-#define __HOSTFN1_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN1_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN1_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN1_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN1_LPU1_MBOX0_CMD_STAT 0x00019014
-#define __HOSTFN1_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN1_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN1_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN1_LPU1_MBOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN1_MBOX0_CMD_STAT 0x00019018
-#define __LPU0_HOSTFN1_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN1_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN1_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN1_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN1_MBOX0_CMD_STAT 0x0001901c
-#define __LPU1_HOSTFN1_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN1_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN1_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN1_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN2_LPU0_MBOX0_CMD_STAT 0x00019150
-#define __HOSTFN2_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN2_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN2_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN2_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN2_LPU1_MBOX0_CMD_STAT 0x00019154
-#define __HOSTFN2_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN2_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN2_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN2_LPU1_MBOX0BOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN2_MBOX0_CMD_STAT 0x00019158
-#define __LPU0_HOSTFN2_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN2_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN2_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN2_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN2_MBOX0_CMD_STAT 0x0001915c
-#define __LPU1_HOSTFN2_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN2_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN2_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN2_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN3_LPU0_MBOX0_CMD_STAT 0x00019160
-#define __HOSTFN3_LPU0_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN3_LPU0_MBOX0_INFO_SH 1
-#define __HOSTFN3_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU0_MBOX0_INFO_SH)
-#define __HOSTFN3_LPU0_MBOX0_CMD_STATUS 0x00000001
-#define HOSTFN3_LPU1_MBOX0_CMD_STAT 0x00019164
-#define __HOSTFN3_LPU1_MBOX0_INFO_MK 0xfffffffe
-#define __HOSTFN3_LPU1_MBOX0_INFO_SH 1
-#define __HOSTFN3_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU1_MBOX0_INFO_SH)
-#define __HOSTFN3_LPU1_MBOX0_CMD_STATUS 0x00000001
-#define LPU0_HOSTFN3_MBOX0_CMD_STAT 0x00019168
-#define __LPU0_HOSTFN3_MBOX0_INFO_MK 0xfffffffe
-#define __LPU0_HOSTFN3_MBOX0_INFO_SH 1
-#define __LPU0_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN3_MBOX0_INFO_SH)
-#define __LPU0_HOSTFN3_MBOX0_CMD_STATUS 0x00000001
-#define LPU1_HOSTFN3_MBOX0_CMD_STAT 0x0001916c
-#define __LPU1_HOSTFN3_MBOX0_INFO_MK 0xfffffffe
-#define __LPU1_HOSTFN3_MBOX0_INFO_SH 1
-#define __LPU1_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN3_MBOX0_INFO_SH)
-#define __LPU1_HOSTFN3_MBOX0_CMD_STATUS 0x00000001
-#define FW_INIT_HALT_P0 0x000191ac
-#define __FW_INIT_HALT_P 0x00000001
-#define FW_INIT_HALT_P1 0x000191bc
-#define CPE_PI_PTR_Q0 0x00038000
-#define __CPE_PI_UNUSED_MK 0xffff0000
-#define __CPE_PI_UNUSED_SH 16
-#define __CPE_PI_UNUSED(_v) ((_v) << __CPE_PI_UNUSED_SH)
-#define __CPE_PI_PTR 0x0000ffff
-#define CPE_PI_PTR_Q1 0x00038040
-#define CPE_CI_PTR_Q0 0x00038004
-#define __CPE_CI_UNUSED_MK 0xffff0000
-#define __CPE_CI_UNUSED_SH 16
-#define __CPE_CI_UNUSED(_v) ((_v) << __CPE_CI_UNUSED_SH)
-#define __CPE_CI_PTR 0x0000ffff
-#define CPE_CI_PTR_Q1 0x00038044
-#define CPE_DEPTH_Q0 0x00038008
-#define __CPE_DEPTH_UNUSED_MK 0xf8000000
-#define __CPE_DEPTH_UNUSED_SH 27
-#define __CPE_DEPTH_UNUSED(_v) ((_v) << __CPE_DEPTH_UNUSED_SH)
-#define __CPE_MSIX_VEC_INDEX_MK 0x07ff0000
-#define __CPE_MSIX_VEC_INDEX_SH 16
-#define __CPE_MSIX_VEC_INDEX(_v) ((_v) << __CPE_MSIX_VEC_INDEX_SH)
-#define __CPE_DEPTH 0x0000ffff
-#define CPE_DEPTH_Q1 0x00038048
-#define CPE_QCTRL_Q0 0x0003800c
-#define __CPE_CTRL_UNUSED30_MK 0xfc000000
-#define __CPE_CTRL_UNUSED30_SH 26
-#define __CPE_CTRL_UNUSED30(_v) ((_v) << __CPE_CTRL_UNUSED30_SH)
-#define __CPE_FUNC_INT_CTRL_MK 0x03000000
-#define __CPE_FUNC_INT_CTRL_SH 24
-#define __CPE_FUNC_INT_CTRL(_v) ((_v) << __CPE_FUNC_INT_CTRL_SH)
-enum {
- __CPE_FUNC_INT_CTRL_DISABLE = 0x0,
- __CPE_FUNC_INT_CTRL_F2NF = 0x1,
- __CPE_FUNC_INT_CTRL_3QUART = 0x2,
- __CPE_FUNC_INT_CTRL_HALF = 0x3,
-};
-#define __CPE_CTRL_UNUSED20_MK 0x00f00000
-#define __CPE_CTRL_UNUSED20_SH 20
-#define __CPE_CTRL_UNUSED20(_v) ((_v) << __CPE_CTRL_UNUSED20_SH)
-#define __CPE_SCI_TH_MK 0x000f0000
-#define __CPE_SCI_TH_SH 16
-#define __CPE_SCI_TH(_v) ((_v) << __CPE_SCI_TH_SH)
-#define __CPE_CTRL_UNUSED10_MK 0x0000c000
-#define __CPE_CTRL_UNUSED10_SH 14
-#define __CPE_CTRL_UNUSED10(_v) ((_v) << __CPE_CTRL_UNUSED10_SH)
-#define __CPE_ACK_PENDING 0x00002000
-#define __CPE_CTRL_UNUSED40_MK 0x00001c00
-#define __CPE_CTRL_UNUSED40_SH 10
-#define __CPE_CTRL_UNUSED40(_v) ((_v) << __CPE_CTRL_UNUSED40_SH)
-#define __CPE_PCIEID_MK 0x00000300
-#define __CPE_PCIEID_SH 8
-#define __CPE_PCIEID(_v) ((_v) << __CPE_PCIEID_SH)
-#define __CPE_CTRL_UNUSED00_MK 0x000000fe
-#define __CPE_CTRL_UNUSED00_SH 1
-#define __CPE_CTRL_UNUSED00(_v) ((_v) << __CPE_CTRL_UNUSED00_SH)
-#define __CPE_ESIZE 0x00000001
-#define CPE_QCTRL_Q1 0x0003804c
-#define __CPE_CTRL_UNUSED31_MK 0xfc000000
-#define __CPE_CTRL_UNUSED31_SH 26
-#define __CPE_CTRL_UNUSED31(_v) ((_v) << __CPE_CTRL_UNUSED31_SH)
-#define __CPE_CTRL_UNUSED21_MK 0x00f00000
-#define __CPE_CTRL_UNUSED21_SH 20
-#define __CPE_CTRL_UNUSED21(_v) ((_v) << __CPE_CTRL_UNUSED21_SH)
-#define __CPE_CTRL_UNUSED11_MK 0x0000c000
-#define __CPE_CTRL_UNUSED11_SH 14
-#define __CPE_CTRL_UNUSED11(_v) ((_v) << __CPE_CTRL_UNUSED11_SH)
-#define __CPE_CTRL_UNUSED41_MK 0x00001c00
-#define __CPE_CTRL_UNUSED41_SH 10
-#define __CPE_CTRL_UNUSED41(_v) ((_v) << __CPE_CTRL_UNUSED41_SH)
-#define __CPE_CTRL_UNUSED01_MK 0x000000fe
-#define __CPE_CTRL_UNUSED01_SH 1
-#define __CPE_CTRL_UNUSED01(_v) ((_v) << __CPE_CTRL_UNUSED01_SH)
-#define RME_PI_PTR_Q0 0x00038020
-#define __LATENCY_TIME_STAMP_MK 0xffff0000
-#define __LATENCY_TIME_STAMP_SH 16
-#define __LATENCY_TIME_STAMP(_v) ((_v) << __LATENCY_TIME_STAMP_SH)
-#define __RME_PI_PTR 0x0000ffff
-#define RME_PI_PTR_Q1 0x00038060
-#define RME_CI_PTR_Q0 0x00038024
-#define __DELAY_TIME_STAMP_MK 0xffff0000
-#define __DELAY_TIME_STAMP_SH 16
-#define __DELAY_TIME_STAMP(_v) ((_v) << __DELAY_TIME_STAMP_SH)
-#define __RME_CI_PTR 0x0000ffff
-#define RME_CI_PTR_Q1 0x00038064
-#define RME_DEPTH_Q0 0x00038028
-#define __RME_DEPTH_UNUSED_MK 0xf8000000
-#define __RME_DEPTH_UNUSED_SH 27
-#define __RME_DEPTH_UNUSED(_v) ((_v) << __RME_DEPTH_UNUSED_SH)
-#define __RME_MSIX_VEC_INDEX_MK 0x07ff0000
-#define __RME_MSIX_VEC_INDEX_SH 16
-#define __RME_MSIX_VEC_INDEX(_v) ((_v) << __RME_MSIX_VEC_INDEX_SH)
-#define __RME_DEPTH 0x0000ffff
-#define RME_DEPTH_Q1 0x00038068
-#define RME_QCTRL_Q0 0x0003802c
-#define __RME_INT_LATENCY_TIMER_MK 0xff000000
-#define __RME_INT_LATENCY_TIMER_SH 24
-#define __RME_INT_LATENCY_TIMER(_v) ((_v) << __RME_INT_LATENCY_TIMER_SH)
-#define __RME_INT_DELAY_TIMER_MK 0x00ff0000
-#define __RME_INT_DELAY_TIMER_SH 16
-#define __RME_INT_DELAY_TIMER(_v) ((_v) << __RME_INT_DELAY_TIMER_SH)
-#define __RME_INT_DELAY_DISABLE 0x00008000
-#define __RME_DLY_DELAY_DISABLE 0x00004000
-#define __RME_ACK_PENDING 0x00002000
-#define __RME_FULL_INTERRUPT_DISABLE 0x00001000
-#define __RME_CTRL_UNUSED10_MK 0x00000c00
-#define __RME_CTRL_UNUSED10_SH 10
-#define __RME_CTRL_UNUSED10(_v) ((_v) << __RME_CTRL_UNUSED10_SH)
-#define __RME_PCIEID_MK 0x00000300
-#define __RME_PCIEID_SH 8
-#define __RME_PCIEID(_v) ((_v) << __RME_PCIEID_SH)
-#define __RME_CTRL_UNUSED00_MK 0x000000fe
-#define __RME_CTRL_UNUSED00_SH 1
-#define __RME_CTRL_UNUSED00(_v) ((_v) << __RME_CTRL_UNUSED00_SH)
-#define __RME_ESIZE 0x00000001
-#define RME_QCTRL_Q1 0x0003806c
-#define __RME_CTRL_UNUSED11_MK 0x00000c00
-#define __RME_CTRL_UNUSED11_SH 10
-#define __RME_CTRL_UNUSED11(_v) ((_v) << __RME_CTRL_UNUSED11_SH)
-#define __RME_CTRL_UNUSED01_MK 0x000000fe
-#define __RME_CTRL_UNUSED01_SH 1
-#define __RME_CTRL_UNUSED01(_v) ((_v) << __RME_CTRL_UNUSED01_SH)
-#define PSS_CTL_REG 0x00018800
-#define __PSS_I2C_CLK_DIV_MK 0x007f0000
-#define __PSS_I2C_CLK_DIV_SH 16
-#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH)
-#define __PSS_LMEM_INIT_DONE 0x00001000
-#define __PSS_LMEM_RESET 0x00000200
-#define __PSS_LMEM_INIT_EN 0x00000100
-#define __PSS_LPU1_RESET 0x00000002
-#define __PSS_LPU0_RESET 0x00000001
-#define PSS_ERR_STATUS_REG 0x00018810
-#define __PSS_LPU1_TCM_READ_ERR 0x00200000
-#define __PSS_LPU0_TCM_READ_ERR 0x00100000
-#define __PSS_LMEM5_CORR_ERR 0x00080000
-#define __PSS_LMEM4_CORR_ERR 0x00040000
-#define __PSS_LMEM3_CORR_ERR 0x00020000
-#define __PSS_LMEM2_CORR_ERR 0x00010000
-#define __PSS_LMEM1_CORR_ERR 0x00008000
-#define __PSS_LMEM0_CORR_ERR 0x00004000
-#define __PSS_LMEM5_UNCORR_ERR 0x00002000
-#define __PSS_LMEM4_UNCORR_ERR 0x00001000
-#define __PSS_LMEM3_UNCORR_ERR 0x00000800
-#define __PSS_LMEM2_UNCORR_ERR 0x00000400
-#define __PSS_LMEM1_UNCORR_ERR 0x00000200
-#define __PSS_LMEM0_UNCORR_ERR 0x00000100
-#define __PSS_BAL_PERR 0x00000080
-#define __PSS_DIP_IF_ERR 0x00000040
-#define __PSS_IOH_IF_ERR 0x00000020
-#define __PSS_TDS_IF_ERR 0x00000010
-#define __PSS_RDS_IF_ERR 0x00000008
-#define __PSS_SGM_IF_ERR 0x00000004
-#define __PSS_LPU1_RAM_ERR 0x00000002
-#define __PSS_LPU0_RAM_ERR 0x00000001
-#define ERR_SET_REG 0x00018818
-#define __PSS_ERR_STATUS_SET 0x003fffff
-#define PMM_1T_RESET_REG_P0 0x0002381c
-#define __PMM_1T_RESET_P 0x00000001
-#define PMM_1T_RESET_REG_P1 0x00023c1c
-#define HQM_QSET0_RXQ_DRBL_P0 0x00038000
-#define __RXQ0_ADD_VECTORS_P 0x80000000
-#define __RXQ0_STOP_P 0x40000000
-#define __RXQ0_PRD_PTR_P 0x0000ffff
-#define HQM_QSET1_RXQ_DRBL_P0 0x00038080
-#define __RXQ1_ADD_VECTORS_P 0x80000000
-#define __RXQ1_STOP_P 0x40000000
-#define __RXQ1_PRD_PTR_P 0x0000ffff
-#define HQM_QSET0_RXQ_DRBL_P1 0x0003c000
-#define HQM_QSET1_RXQ_DRBL_P1 0x0003c080
-#define HQM_QSET0_TXQ_DRBL_P0 0x00038020
-#define __TXQ0_ADD_VECTORS_P 0x80000000
-#define __TXQ0_STOP_P 0x40000000
-#define __TXQ0_PRD_PTR_P 0x0000ffff
-#define HQM_QSET1_TXQ_DRBL_P0 0x000380a0
-#define __TXQ1_ADD_VECTORS_P 0x80000000
-#define __TXQ1_STOP_P 0x40000000
-#define __TXQ1_PRD_PTR_P 0x0000ffff
-#define HQM_QSET0_TXQ_DRBL_P1 0x0003c020
-#define HQM_QSET1_TXQ_DRBL_P1 0x0003c0a0
-#define HQM_QSET0_IB_DRBL_1_P0 0x00038040
-#define __IB1_0_ACK_P 0x80000000
-#define __IB1_0_DISABLE_P 0x40000000
-#define __IB1_0_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB1_0_COALESCING_CFG_P_SH 16
-#define __IB1_0_COALESCING_CFG_P(_v) ((_v) << __IB1_0_COALESCING_CFG_P_SH)
-#define __IB1_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET1_IB_DRBL_1_P0 0x000380c0
-#define __IB1_1_ACK_P 0x80000000
-#define __IB1_1_DISABLE_P 0x40000000
-#define __IB1_1_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB1_1_COALESCING_CFG_P_SH 16
-#define __IB1_1_COALESCING_CFG_P(_v) ((_v) << __IB1_1_COALESCING_CFG_P_SH)
-#define __IB1_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET0_IB_DRBL_1_P1 0x0003c040
-#define HQM_QSET1_IB_DRBL_1_P1 0x0003c0c0
-#define HQM_QSET0_IB_DRBL_2_P0 0x00038060
-#define __IB2_0_ACK_P 0x80000000
-#define __IB2_0_DISABLE_P 0x40000000
-#define __IB2_0_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB2_0_COALESCING_CFG_P_SH 16
-#define __IB2_0_COALESCING_CFG_P(_v) ((_v) << __IB2_0_COALESCING_CFG_P_SH)
-#define __IB2_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET1_IB_DRBL_2_P0 0x000380e0
-#define __IB2_1_ACK_P 0x80000000
-#define __IB2_1_DISABLE_P 0x40000000
-#define __IB2_1_COALESCING_CFG_P_MK 0x00ff0000
-#define __IB2_1_COALESCING_CFG_P_SH 16
-#define __IB2_1_COALESCING_CFG_P(_v) ((_v) << __IB2_1_COALESCING_CFG_P_SH)
-#define __IB2_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff
-#define HQM_QSET0_IB_DRBL_2_P1 0x0003c060
-#define HQM_QSET1_IB_DRBL_2_P1 0x0003c0e0
-
-
-/*
- * These definitions are either in error/missing in spec. Its auto-generated
- * from hard coded values in regparse.pl.
- */
-#define __EMPHPOST_AT_4G_MK_FIX 0x0000001c
-#define __EMPHPOST_AT_4G_SH_FIX 0x00000002
-#define __EMPHPRE_AT_4G_FIX 0x00000003
-#define __SFP_TXRATE_EN_FIX 0x00000100
-#define __SFP_RXRATE_EN_FIX 0x00000080
-
-
-/*
- * These register definitions are auto-generated from hard coded values
- * in regparse.pl.
- */
-
-
-/*
- * These register mapping definitions are auto-generated from mapping tables
- * in regparse.pl.
- */
-#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG
-#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG
-#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG
-#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG
-#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG
-#define BFA_IOC_FAIL_SYNC HOST_SEM5_INFO_REG
-
-#define CPE_DEPTH_Q(__n) \
- (CPE_DEPTH_Q0 + (__n) * (CPE_DEPTH_Q1 - CPE_DEPTH_Q0))
-#define CPE_QCTRL_Q(__n) \
- (CPE_QCTRL_Q0 + (__n) * (CPE_QCTRL_Q1 - CPE_QCTRL_Q0))
-#define CPE_PI_PTR_Q(__n) \
- (CPE_PI_PTR_Q0 + (__n) * (CPE_PI_PTR_Q1 - CPE_PI_PTR_Q0))
-#define CPE_CI_PTR_Q(__n) \
- (CPE_CI_PTR_Q0 + (__n) * (CPE_CI_PTR_Q1 - CPE_CI_PTR_Q0))
-#define RME_DEPTH_Q(__n) \
- (RME_DEPTH_Q0 + (__n) * (RME_DEPTH_Q1 - RME_DEPTH_Q0))
-#define RME_QCTRL_Q(__n) \
- (RME_QCTRL_Q0 + (__n) * (RME_QCTRL_Q1 - RME_QCTRL_Q0))
-#define RME_PI_PTR_Q(__n) \
- (RME_PI_PTR_Q0 + (__n) * (RME_PI_PTR_Q1 - RME_PI_PTR_Q0))
-#define RME_CI_PTR_Q(__n) \
- (RME_CI_PTR_Q0 + (__n) * (RME_CI_PTR_Q1 - RME_CI_PTR_Q0))
-#define HQM_QSET_RXQ_DRBL_P0(__n) \
- (HQM_QSET0_RXQ_DRBL_P0 + (__n) * \
- (HQM_QSET1_RXQ_DRBL_P0 - HQM_QSET0_RXQ_DRBL_P0))
-#define HQM_QSET_TXQ_DRBL_P0(__n) \
- (HQM_QSET0_TXQ_DRBL_P0 + (__n) * \
- (HQM_QSET1_TXQ_DRBL_P0 - HQM_QSET0_TXQ_DRBL_P0))
-#define HQM_QSET_IB_DRBL_1_P0(__n) \
- (HQM_QSET0_IB_DRBL_1_P0 + (__n) * \
- (HQM_QSET1_IB_DRBL_1_P0 - HQM_QSET0_IB_DRBL_1_P0))
-#define HQM_QSET_IB_DRBL_2_P0(__n) \
- (HQM_QSET0_IB_DRBL_2_P0 + (__n) * \
- (HQM_QSET1_IB_DRBL_2_P0 - HQM_QSET0_IB_DRBL_2_P0))
-#define HQM_QSET_RXQ_DRBL_P1(__n) \
- (HQM_QSET0_RXQ_DRBL_P1 + (__n) * \
- (HQM_QSET1_RXQ_DRBL_P1 - HQM_QSET0_RXQ_DRBL_P1))
-#define HQM_QSET_TXQ_DRBL_P1(__n) \
- (HQM_QSET0_TXQ_DRBL_P1 + (__n) * \
- (HQM_QSET1_TXQ_DRBL_P1 - HQM_QSET0_TXQ_DRBL_P1))
-#define HQM_QSET_IB_DRBL_1_P1(__n) \
- (HQM_QSET0_IB_DRBL_1_P1 + (__n) * \
- (HQM_QSET1_IB_DRBL_1_P1 - HQM_QSET0_IB_DRBL_1_P1))
-#define HQM_QSET_IB_DRBL_2_P1(__n) \
- (HQM_QSET0_IB_DRBL_2_P1 + (__n) * \
- (HQM_QSET1_IB_DRBL_2_P1 - HQM_QSET0_IB_DRBL_2_P1))
-
-#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
-#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
-#define CPE_Q_MASK(__q) ((__q) & 0x3)
-#define RME_Q_MASK(__q) ((__q) & 0x3)
-
-
-/*
- * PCI MSI-X vector defines
- */
-enum {
- BFA_MSIX_CPE_Q0 = 0,
- BFA_MSIX_CPE_Q1 = 1,
- BFA_MSIX_CPE_Q2 = 2,
- BFA_MSIX_CPE_Q3 = 3,
- BFA_MSIX_RME_Q0 = 4,
- BFA_MSIX_RME_Q1 = 5,
- BFA_MSIX_RME_Q2 = 6,
- BFA_MSIX_RME_Q3 = 7,
- BFA_MSIX_LPU_ERR = 8,
- BFA_MSIX_CT_MAX = 9,
-};
-
-/*
- * And corresponding host interrupt status bit field defines
- */
-#define __HFN_INT_CPE_Q0 0x00000001U
-#define __HFN_INT_CPE_Q1 0x00000002U
-#define __HFN_INT_CPE_Q2 0x00000004U
-#define __HFN_INT_CPE_Q3 0x00000008U
-#define __HFN_INT_CPE_Q4 0x00000010U
-#define __HFN_INT_CPE_Q5 0x00000020U
-#define __HFN_INT_CPE_Q6 0x00000040U
-#define __HFN_INT_CPE_Q7 0x00000080U
-#define __HFN_INT_RME_Q0 0x00000100U
-#define __HFN_INT_RME_Q1 0x00000200U
-#define __HFN_INT_RME_Q2 0x00000400U
-#define __HFN_INT_RME_Q3 0x00000800U
-#define __HFN_INT_RME_Q4 0x00001000U
-#define __HFN_INT_RME_Q5 0x00002000U
-#define __HFN_INT_RME_Q6 0x00004000U
-#define __HFN_INT_RME_Q7 0x00008000U
-#define __HFN_INT_ERR_EMC 0x00010000U
-#define __HFN_INT_ERR_LPU0 0x00020000U
-#define __HFN_INT_ERR_LPU1 0x00040000U
-#define __HFN_INT_ERR_PSS 0x00080000U
-#define __HFN_INT_MBOX_LPU0 0x00100000U
-#define __HFN_INT_MBOX_LPU1 0x00200000U
-#define __HFN_INT_MBOX1_LPU0 0x00400000U
-#define __HFN_INT_MBOX1_LPU1 0x00800000U
-#define __HFN_INT_LL_HALT 0x01000000U
-#define __HFN_INT_CPE_MASK 0x000000ffU
-#define __HFN_INT_RME_MASK 0x0000ff00U
-
-
-/*
- * catapult memory map.
- */
-#define LL_PGN_HQM0 0x0096
-#define LL_PGN_HQM1 0x0097
-#define PSS_SMEM_PAGE_START 0x8000
-#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15))
-#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff)
-
-/*
- * End of catapult memory map
- */
-
-
-#endif /* __BFI_CTREG_H__ */
diff --git a/drivers/scsi/bfa/bfi_ms.h b/drivers/scsi/bfa/bfi_ms.h
index 19e888a57555..b9dc0b9bb814 100644
--- a/drivers/scsi/bfa/bfi_ms.h
+++ b/drivers/scsi/bfa/bfi_ms.h
@@ -1,18 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License (GPL) Version 2 as
- * published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*/
#ifndef __BFI_MS_H__
@@ -28,11 +21,15 @@ enum bfi_iocfc_h2i_msgs {
BFI_IOCFC_H2I_CFG_REQ = 1,
BFI_IOCFC_H2I_SET_INTR_REQ = 2,
BFI_IOCFC_H2I_UPDATEQ_REQ = 3,
+ BFI_IOCFC_H2I_FAA_QUERY_REQ = 4,
+ BFI_IOCFC_H2I_ADDR_REQ = 5,
};
enum bfi_iocfc_i2h_msgs {
BFI_IOCFC_I2H_CFG_REPLY = BFA_I2HM(1),
BFI_IOCFC_I2H_UPDATEQ_RSP = BFA_I2HM(3),
+ BFI_IOCFC_I2H_FAA_QUERY_RSP = BFA_I2HM(4),
+ BFI_IOCFC_I2H_ADDR_MSG = BFA_I2HM(5),
};
struct bfi_iocfc_cfg_s {
@@ -40,6 +37,12 @@ struct bfi_iocfc_cfg_s {
u8 sense_buf_len; /* SCSI sense length */
u16 rsvd_1;
u32 endian_sig; /* endian signature of host */
+ u8 rsvd_2;
+ u8 single_msix_vec;
+ u8 rsvd[2];
+ __be16 num_ioim_reqs;
+ __be16 num_fwtio_reqs;
+
/*
* Request and response circular queue base addresses, size and
@@ -54,7 +57,8 @@ struct bfi_iocfc_cfg_s {
union bfi_addr_u stats_addr; /* DMA-able address for stats */
union bfi_addr_u cfgrsp_addr; /* config response dma address */
- union bfi_addr_u ioim_snsbase; /* IO sense buffer base address */
+ union bfi_addr_u ioim_snsbase[BFI_IOIM_SNSBUF_SEGS];
+ /* IO sense buf base addr segments */
struct bfa_iocfc_intr_attr_s intr_attr; /* IOC interrupt attributes */
};
@@ -68,11 +72,25 @@ struct bfi_iocfc_bootwwns {
u8 rsvd[7];
};
+/**
+ * Queue configuration response from firmware
+ */
+struct bfi_iocfc_qreg_s {
+ u32 cpe_q_ci_off[BFI_IOC_MAX_CQS];
+ u32 cpe_q_pi_off[BFI_IOC_MAX_CQS];
+ u32 cpe_qctl_off[BFI_IOC_MAX_CQS];
+ u32 rme_q_ci_off[BFI_IOC_MAX_CQS];
+ u32 rme_q_pi_off[BFI_IOC_MAX_CQS];
+ u32 rme_qctl_off[BFI_IOC_MAX_CQS];
+ u8 hw_qid[BFI_IOC_MAX_CQS];
+};
+
struct bfi_iocfc_cfgrsp_s {
struct bfa_iocfc_fwcfg_s fwcfg;
struct bfa_iocfc_intr_attr_s intr_attr;
struct bfi_iocfc_bootwwns bootwwns;
struct bfi_pbc_s pbc_cfg;
+ struct bfi_iocfc_qreg_s qreg;
};
/*
@@ -150,6 +168,44 @@ union bfi_iocfc_i2h_msg_u {
u32 mboxmsg[BFI_IOC_MSGSZ];
};
+/*
+ * BFI_IOCFC_H2I_FAA_ENABLE_REQ BFI_IOCFC_H2I_FAA_DISABLE_REQ message
+ */
+struct bfi_faa_en_dis_s {
+ struct bfi_mhdr_s mh; /* common msg header */
+};
+
+struct bfi_faa_addr_msg_s {
+ struct bfi_mhdr_s mh; /* common msg header */
+ u8 rsvd[4];
+ wwn_t pwwn; /* Fabric acquired PWWN */
+ wwn_t nwwn; /* Fabric acquired PWWN */
+};
+
+/*
+ * BFI_IOCFC_H2I_FAA_QUERY_REQ message
+ */
+struct bfi_faa_query_s {
+ struct bfi_mhdr_s mh; /* common msg header */
+ u8 faa_status; /* FAA status */
+ u8 addr_source; /* PWWN source */
+ u8 rsvd[2];
+ wwn_t faa; /* Fabric acquired PWWN */
+};
+
+/*
+ * BFI_IOCFC_I2H_FAA_ENABLE_RSP, BFI_IOCFC_I2H_FAA_DISABLE_RSP message
+ */
+struct bfi_faa_en_dis_rsp_s {
+ struct bfi_mhdr_s mh; /* common msg header */
+ u8 status; /* updateq status */
+ u8 rsvd[3];
+};
+
+/*
+ * BFI_IOCFC_I2H_FAA_QUERY_RSP message
+ */
+#define bfi_faa_query_rsp_t struct bfi_faa_query_s
enum bfi_fcport_h2i {
BFI_FCPORT_H2I_ENABLE_REQ = (1),
@@ -213,7 +269,7 @@ struct bfi_fcport_enable_req_s {
struct bfi_fcport_set_svc_params_req_s {
struct bfi_mhdr_s mh; /* msg header */
__be16 tx_bbcredit; /* Tx credits */
- u16 rsvd;
+ u8 rsvd[2];
};
/*
@@ -293,12 +349,12 @@ struct bfi_fcxp_send_req_s {
u8 class; /* FC class used for req/rsp */
u8 rsp_timeout; /* timeout in secs, 0-no response */
u8 cts; /* continue sequence */
- u8 lp_tag; /* lport tag */
+ u8 lp_fwtag; /* lport tag */
struct fchs_s fchs; /* request FC header structure */
__be32 req_len; /* request payload length */
__be32 rsp_maxlen; /* max response length expected */
- struct bfi_sge_s req_sge[BFA_FCXP_MAX_SGES]; /* request buf */
- struct bfi_sge_s rsp_sge[BFA_FCXP_MAX_SGES]; /* response buf */
+ struct bfi_alen_s req_alen; /* request buffer */
+ struct bfi_alen_s rsp_alen; /* response buffer */
};
/*
@@ -328,7 +384,7 @@ struct bfi_uf_buf_post_s {
struct bfi_mhdr_s mh; /* Common msg header */
u16 buf_tag; /* buffer tag */
__be16 buf_len; /* total buffer length */
- struct bfi_sge_s sge[BFA_UF_MAX_SGES]; /* buffer DMA SGEs */
+ struct bfi_alen_s alen; /* buffer address/len pair */
};
struct bfi_uf_frm_rcvd_s {
@@ -346,26 +402,28 @@ enum bfi_lps_h2i_msgs {
};
enum bfi_lps_i2h_msgs {
- BFI_LPS_H2I_LOGIN_RSP = BFA_I2HM(1),
- BFI_LPS_H2I_LOGOUT_RSP = BFA_I2HM(2),
- BFI_LPS_H2I_CVL_EVENT = BFA_I2HM(3),
+ BFI_LPS_I2H_LOGIN_RSP = BFA_I2HM(1),
+ BFI_LPS_I2H_LOGOUT_RSP = BFA_I2HM(2),
+ BFI_LPS_I2H_CVL_EVENT = BFA_I2HM(3),
};
struct bfi_lps_login_req_s {
struct bfi_mhdr_s mh; /* common msg header */
- u8 lp_tag;
+ u8 bfa_tag;
u8 alpa;
__be16 pdu_size;
wwn_t pwwn;
wwn_t nwwn;
u8 fdisc;
u8 auth_en;
- u8 rsvd[2];
+ u8 lps_role;
+ u8 bb_scn;
+ u32 vvl_flag;
};
struct bfi_lps_login_rsp_s {
struct bfi_mhdr_s mh; /* common msg header */
- u8 lp_tag;
+ u8 fw_tag;
u8 status;
u8 lsrjt_rsn;
u8 lsrjt_expl;
@@ -380,31 +438,33 @@ struct bfi_lps_login_rsp_s {
mac_t fcf_mac;
u8 ext_status;
u8 brcd_switch; /* attached peer is brcd switch */
+ u8 bfa_tag;
+ u8 rsvd;
};
struct bfi_lps_logout_req_s {
struct bfi_mhdr_s mh; /* common msg header */
- u8 lp_tag;
+ u8 fw_tag;
u8 rsvd[3];
wwn_t port_name;
};
struct bfi_lps_logout_rsp_s {
struct bfi_mhdr_s mh; /* common msg header */
- u8 lp_tag;
+ u8 bfa_tag;
u8 status;
u8 rsvd[2];
};
struct bfi_lps_cvl_event_s {
struct bfi_mhdr_s mh; /* common msg header */
- u8 lp_tag;
+ u8 bfa_tag;
u8 rsvd[3];
};
struct bfi_lps_n2n_pid_req_s {
struct bfi_mhdr_s mh; /* common msg header */
- u8 lp_tag;
+ u8 fw_tag;
u32 lp_pid:24;
};
@@ -432,6 +492,9 @@ enum bfi_rport_i2h_msgs {
BFI_RPORT_I2H_CREATE_RSP = BFA_I2HM(1),
BFI_RPORT_I2H_DELETE_RSP = BFA_I2HM(2),
BFI_RPORT_I2H_QOS_SCN = BFA_I2HM(3),
+ BFI_RPORT_I2H_LIP_SCN_ONLINE = BFA_I2HM(4),
+ BFI_RPORT_I2H_LIP_SCN_OFFLINE = BFA_I2HM(5),
+ BFI_RPORT_I2H_NO_DEV = BFA_I2HM(6),
};
struct bfi_rport_create_req_s {
@@ -439,7 +502,7 @@ struct bfi_rport_create_req_s {
u16 bfa_handle; /* host rport handle */
__be16 max_frmsz; /* max rcv pdu size */
u32 pid:24, /* remote port ID */
- lp_tag:8; /* local port tag */
+ lp_fwtag:8; /* local port tag */
u32 local_pid:24, /* local port ID */
cisc:8;
u8 fc_class; /* supported FC classes */
@@ -484,6 +547,14 @@ struct bfi_rport_qos_scn_s {
struct bfa_rport_qos_attr_s new_qos_attr; /* New QoS Attributes */
};
+struct bfi_rport_lip_scn_s {
+ struct bfi_mhdr_s mh; /*!< common msg header */
+ u16 bfa_handle; /*!< host rport handle */
+ u8 status; /*!< scn online status */
+ u8 rsvd;
+ struct bfa_fcport_loop_info_s loop_info;
+};
+
union bfi_rport_h2i_msg_u {
struct bfi_msg_s *msg;
struct bfi_rport_create_req_s *create_req;
@@ -496,68 +567,70 @@ union bfi_rport_i2h_msg_u {
struct bfi_rport_create_rsp_s *create_rsp;
struct bfi_rport_delete_rsp_s *delete_rsp;
struct bfi_rport_qos_scn_s *qos_scn_evt;
+ struct bfi_rport_lip_scn_s *lip_scn;
};
/*
* Initiator mode I-T nexus interface defines.
*/
-enum bfi_itnim_h2i {
- BFI_ITNIM_H2I_CREATE_REQ = 1, /* i-t nexus creation */
- BFI_ITNIM_H2I_DELETE_REQ = 2, /* i-t nexus deletion */
+enum bfi_itn_h2i {
+ BFI_ITN_H2I_CREATE_REQ = 1, /* i-t nexus creation */
+ BFI_ITN_H2I_DELETE_REQ = 2, /* i-t nexus deletion */
};
-enum bfi_itnim_i2h {
- BFI_ITNIM_I2H_CREATE_RSP = BFA_I2HM(1),
- BFI_ITNIM_I2H_DELETE_RSP = BFA_I2HM(2),
- BFI_ITNIM_I2H_SLER_EVENT = BFA_I2HM(3),
+enum bfi_itn_i2h {
+ BFI_ITN_I2H_CREATE_RSP = BFA_I2HM(1),
+ BFI_ITN_I2H_DELETE_RSP = BFA_I2HM(2),
+ BFI_ITN_I2H_SLER_EVENT = BFA_I2HM(3),
};
-struct bfi_itnim_create_req_s {
+struct bfi_itn_create_req_s {
struct bfi_mhdr_s mh; /* common msg header */
u16 fw_handle; /* f/w handle for itnim */
u8 class; /* FC class for IO */
u8 seq_rec; /* sequence recovery support */
u8 msg_no; /* seq id of the msg */
+ u8 role;
};
-struct bfi_itnim_create_rsp_s {
+struct bfi_itn_create_rsp_s {
struct bfi_mhdr_s mh; /* common msg header */
u16 bfa_handle; /* bfa handle for itnim */
u8 status; /* fcp request status */
u8 seq_id; /* seq id of the msg */
};
-struct bfi_itnim_delete_req_s {
+struct bfi_itn_delete_req_s {
struct bfi_mhdr_s mh; /* common msg header */
u16 fw_handle; /* f/w itnim handle */
u8 seq_id; /* seq id of the msg */
u8 rsvd;
};
-struct bfi_itnim_delete_rsp_s {
+struct bfi_itn_delete_rsp_s {
struct bfi_mhdr_s mh; /* common msg header */
u16 bfa_handle; /* bfa handle for itnim */
u8 status; /* fcp request status */
u8 seq_id; /* seq id of the msg */
};
-struct bfi_itnim_sler_event_s {
+struct bfi_itn_sler_event_s {
struct bfi_mhdr_s mh; /* common msg header */
u16 bfa_handle; /* bfa handle for itnim */
u16 rsvd;
};
-union bfi_itnim_h2i_msg_u {
- struct bfi_itnim_create_req_s *create_req;
- struct bfi_itnim_delete_req_s *delete_req;
+union bfi_itn_h2i_msg_u {
+ struct bfi_itn_create_req_s *create_req;
+ struct bfi_itn_delete_req_s *delete_req;
struct bfi_msg_s *msg;
};
-union bfi_itnim_i2h_msg_u {
- struct bfi_itnim_create_rsp_s *create_rsp;
- struct bfi_itnim_delete_rsp_s *delete_rsp;
- struct bfi_itnim_sler_event_s *sler_event;
+union bfi_itn_i2h_msg_u {
+ struct bfi_itn_create_rsp_s *create_rsp;
+ struct bfi_itn_delete_rsp_s *delete_rsp;
+ struct bfi_itn_sler_event_s *sler_event;
struct bfi_msg_s *msg;
};
@@ -599,7 +672,7 @@ struct bfi_ioim_req_s {
/*
* SG elements array within the IO request must be double word
- * aligned. This aligment is required to optimize SGM setup for the IO.
+ * aligned. This alignment is required to optimize SGM setup for the IO.
*/
struct bfi_sge_s sges[BFI_SGE_INLINE_MAX];
u8 io_timeout;
@@ -693,7 +766,6 @@ enum bfi_ioim_status {
BFI_IOIM_STS_PATHTOV = 8,
};
-#define BFI_IOIM_SNSLEN (256)
/*
* I/O response message
*/
@@ -761,6 +833,7 @@ enum bfi_tskim_status {
*/
BFI_TSKIM_STS_TIMEOUT = 10, /* TM request timedout */
BFI_TSKIM_STS_ABORTED = 11, /* Aborted on host request */
+ BFI_TSKIM_STS_UTAG = 12, /* unknown tag for request */
};
struct bfi_tskim_rsp_s {
@@ -772,4 +845,27 @@ struct bfi_tskim_rsp_s {
#pragma pack()
+/*
+ * Crossbow PCI MSI-X vector defines
+ */
+enum {
+ BFI_MSIX_CPE_QMIN_CB = 0,
+ BFI_MSIX_CPE_QMAX_CB = 7,
+ BFI_MSIX_RME_QMIN_CB = 8,
+ BFI_MSIX_RME_QMAX_CB = 15,
+ BFI_MSIX_CB_MAX = 22,
+};
+
+/*
+ * Catapult FC PCI MSI-X vector defines
+ */
+enum {
+ BFI_MSIX_LPU_ERR_CT = 0,
+ BFI_MSIX_CPE_QMIN_CT = 1,
+ BFI_MSIX_CPE_QMAX_CT = 4,
+ BFI_MSIX_RME_QMIN_CT = 5,
+ BFI_MSIX_RME_QMAX_CT = 8,
+ BFI_MSIX_CT_MAX = 9,
+};
+
#endif /* __BFI_MS_H__ */
diff --git a/drivers/scsi/bfa/bfi_reg.h b/drivers/scsi/bfa/bfi_reg.h
new file mode 100644
index 000000000000..0803b710a26e
--- /dev/null
+++ b/drivers/scsi/bfa/bfi_reg.h
@@ -0,0 +1,452 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
+ * All rights reserved
+ * www.qlogic.com
+ *
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
+ */
+
+/*
+ * bfi_reg.h ASIC register defines for all QLogic BR-series adapter ASICs
+ */
+
+#ifndef __BFI_REG_H__
+#define __BFI_REG_H__
+
+#define HOSTFN0_INT_STATUS 0x00014000 /* cb/ct */
+#define HOSTFN1_INT_STATUS 0x00014100 /* cb/ct */
+#define HOSTFN2_INT_STATUS 0x00014300 /* ct */
+#define HOSTFN3_INT_STATUS 0x00014400 /* ct */
+#define HOSTFN0_INT_MSK 0x00014004 /* cb/ct */
+#define HOSTFN1_INT_MSK 0x00014104 /* cb/ct */
+#define HOSTFN2_INT_MSK 0x00014304 /* ct */
+#define HOSTFN3_INT_MSK 0x00014404 /* ct */
+
+#define HOST_PAGE_NUM_FN0 0x00014008 /* cb/ct */
+#define HOST_PAGE_NUM_FN1 0x00014108 /* cb/ct */
+#define HOST_PAGE_NUM_FN2 0x00014308 /* ct */
+#define HOST_PAGE_NUM_FN3 0x00014408 /* ct */
+
+#define APP_PLL_LCLK_CTL_REG 0x00014204 /* cb/ct */
+#define __P_LCLK_PLL_LOCK 0x80000000
+#define __APP_PLL_LCLK_SRAM_USE_100MHZ 0x00100000
+#define __APP_PLL_LCLK_RESET_TIMER_MK 0x000e0000
+#define __APP_PLL_LCLK_RESET_TIMER_SH 17
+#define __APP_PLL_LCLK_RESET_TIMER(_v) ((_v) << __APP_PLL_LCLK_RESET_TIMER_SH)
+#define __APP_PLL_LCLK_LOGIC_SOFT_RESET 0x00010000
+#define __APP_PLL_LCLK_CNTLMT0_1_MK 0x0000c000
+#define __APP_PLL_LCLK_CNTLMT0_1_SH 14
+#define __APP_PLL_LCLK_CNTLMT0_1(_v) ((_v) << __APP_PLL_LCLK_CNTLMT0_1_SH)
+#define __APP_PLL_LCLK_JITLMT0_1_MK 0x00003000
+#define __APP_PLL_LCLK_JITLMT0_1_SH 12
+#define __APP_PLL_LCLK_JITLMT0_1(_v) ((_v) << __APP_PLL_LCLK_JITLMT0_1_SH)
+#define __APP_PLL_LCLK_HREF 0x00000800
+#define __APP_PLL_LCLK_HDIV 0x00000400
+#define __APP_PLL_LCLK_P0_1_MK 0x00000300
+#define __APP_PLL_LCLK_P0_1_SH 8
+#define __APP_PLL_LCLK_P0_1(_v) ((_v) << __APP_PLL_LCLK_P0_1_SH)
+#define __APP_PLL_LCLK_Z0_2_MK 0x000000e0
+#define __APP_PLL_LCLK_Z0_2_SH 5
+#define __APP_PLL_LCLK_Z0_2(_v) ((_v) << __APP_PLL_LCLK_Z0_2_SH)
+#define __APP_PLL_LCLK_RSEL200500 0x00000010
+#define __APP_PLL_LCLK_ENARST 0x00000008
+#define __APP_PLL_LCLK_BYPASS 0x00000004
+#define __APP_PLL_LCLK_LRESETN 0x00000002
+#define __APP_PLL_LCLK_ENABLE 0x00000001
+#define APP_PLL_SCLK_CTL_REG 0x00014208 /* cb/ct */
+#define __P_SCLK_PLL_LOCK 0x80000000
+#define __APP_PLL_SCLK_RESET_TIMER_MK 0x000e0000
+#define __APP_PLL_SCLK_RESET_TIMER_SH 17
+#define __APP_PLL_SCLK_RESET_TIMER(_v) ((_v) << __APP_PLL_SCLK_RESET_TIMER_SH)
+#define __APP_PLL_SCLK_LOGIC_SOFT_RESET 0x00010000
+#define __APP_PLL_SCLK_CNTLMT0_1_MK 0x0000c000
+#define __APP_PLL_SCLK_CNTLMT0_1_SH 14
+#define __APP_PLL_SCLK_CNTLMT0_1(_v) ((_v) << __APP_PLL_SCLK_CNTLMT0_1_SH)
+#define __APP_PLL_SCLK_JITLMT0_1_MK 0x00003000
+#define __APP_PLL_SCLK_JITLMT0_1_SH 12
+#define __APP_PLL_SCLK_JITLMT0_1(_v) ((_v) << __APP_PLL_SCLK_JITLMT0_1_SH)
+#define __APP_PLL_SCLK_HREF 0x00000800
+#define __APP_PLL_SCLK_HDIV 0x00000400
+#define __APP_PLL_SCLK_P0_1_MK 0x00000300
+#define __APP_PLL_SCLK_P0_1_SH 8
+#define __APP_PLL_SCLK_P0_1(_v) ((_v) << __APP_PLL_SCLK_P0_1_SH)
+#define __APP_PLL_SCLK_Z0_2_MK 0x000000e0
+#define __APP_PLL_SCLK_Z0_2_SH 5
+#define __APP_PLL_SCLK_Z0_2(_v) ((_v) << __APP_PLL_SCLK_Z0_2_SH)
+#define __APP_PLL_SCLK_RSEL200500 0x00000010
+#define __APP_PLL_SCLK_ENARST 0x00000008
+#define __APP_PLL_SCLK_BYPASS 0x00000004
+#define __APP_PLL_SCLK_LRESETN 0x00000002
+#define __APP_PLL_SCLK_ENABLE 0x00000001
+#define __ENABLE_MAC_AHB_1 0x00800000 /* ct */
+#define __ENABLE_MAC_AHB_0 0x00400000 /* ct */
+#define __ENABLE_MAC_1 0x00200000 /* ct */
+#define __ENABLE_MAC_0 0x00100000 /* ct */
+
+#define HOST_SEM0_REG 0x00014230 /* cb/ct */
+#define HOST_SEM1_REG 0x00014234 /* cb/ct */
+#define HOST_SEM2_REG 0x00014238 /* cb/ct */
+#define HOST_SEM3_REG 0x0001423c /* cb/ct */
+#define HOST_SEM4_REG 0x00014610 /* cb/ct */
+#define HOST_SEM5_REG 0x00014614 /* cb/ct */
+#define HOST_SEM6_REG 0x00014618 /* cb/ct */
+#define HOST_SEM7_REG 0x0001461c /* cb/ct */
+#define HOST_SEM0_INFO_REG 0x00014240 /* cb/ct */
+#define HOST_SEM1_INFO_REG 0x00014244 /* cb/ct */
+#define HOST_SEM2_INFO_REG 0x00014248 /* cb/ct */
+#define HOST_SEM3_INFO_REG 0x0001424c /* cb/ct */
+#define HOST_SEM4_INFO_REG 0x00014620 /* cb/ct */
+#define HOST_SEM5_INFO_REG 0x00014624 /* cb/ct */
+#define HOST_SEM6_INFO_REG 0x00014628 /* cb/ct */
+#define HOST_SEM7_INFO_REG 0x0001462c /* cb/ct */
+
+#define HOSTFN0_LPU0_CMD_STAT 0x00019000 /* cb/ct */
+#define HOSTFN0_LPU1_CMD_STAT 0x00019004 /* cb/ct */
+#define HOSTFN1_LPU0_CMD_STAT 0x00019010 /* cb/ct */
+#define HOSTFN1_LPU1_CMD_STAT 0x00019014 /* cb/ct */
+#define HOSTFN2_LPU0_CMD_STAT 0x00019150 /* ct */
+#define HOSTFN2_LPU1_CMD_STAT 0x00019154 /* ct */
+#define HOSTFN3_LPU0_CMD_STAT 0x00019160 /* ct */
+#define HOSTFN3_LPU1_CMD_STAT 0x00019164 /* ct */
+#define LPU0_HOSTFN0_CMD_STAT 0x00019008 /* cb/ct */
+#define LPU1_HOSTFN0_CMD_STAT 0x0001900c /* cb/ct */
+#define LPU0_HOSTFN1_CMD_STAT 0x00019018 /* cb/ct */
+#define LPU1_HOSTFN1_CMD_STAT 0x0001901c /* cb/ct */
+#define LPU0_HOSTFN2_CMD_STAT 0x00019158 /* ct */
+#define LPU1_HOSTFN2_CMD_STAT 0x0001915c /* ct */
+#define LPU0_HOSTFN3_CMD_STAT 0x00019168 /* ct */
+#define LPU1_HOSTFN3_CMD_STAT 0x0001916c /* ct */
+
+#define PSS_CTL_REG 0x00018800 /* cb/ct */
+#define __PSS_I2C_CLK_DIV_MK 0x007f0000
+#define __PSS_I2C_CLK_DIV_SH 16
+#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH)
+#define __PSS_LMEM_INIT_DONE 0x00001000
+#define __PSS_LMEM_RESET 0x00000200
+#define __PSS_LMEM_INIT_EN 0x00000100
+#define __PSS_LPU1_RESET 0x00000002
+#define __PSS_LPU0_RESET 0x00000001
+#define PSS_ERR_STATUS_REG 0x00018810 /* cb/ct */
+#define ERR_SET_REG 0x00018818 /* cb/ct */
+#define PSS_GPIO_OUT_REG 0x000188c0 /* cb/ct */
+#define __PSS_GPIO_OUT_REG 0x00000fff
+#define PSS_GPIO_OE_REG 0x000188c8 /* cb/ct */
+#define __PSS_GPIO_OE_REG 0x000000ff
+
+#define HOSTFN0_LPU_MBOX0_0 0x00019200 /* cb/ct */
+#define HOSTFN1_LPU_MBOX0_8 0x00019260 /* cb/ct */
+#define LPU_HOSTFN0_MBOX0_0 0x00019280 /* cb/ct */
+#define LPU_HOSTFN1_MBOX0_8 0x000192e0 /* cb/ct */
+#define HOSTFN2_LPU_MBOX0_0 0x00019400 /* ct */
+#define HOSTFN3_LPU_MBOX0_8 0x00019460 /* ct */
+#define LPU_HOSTFN2_MBOX0_0 0x00019480 /* ct */
+#define LPU_HOSTFN3_MBOX0_8 0x000194e0 /* ct */
+
+#define HOST_MSIX_ERR_INDEX_FN0 0x0001400c /* ct */
+#define HOST_MSIX_ERR_INDEX_FN1 0x0001410c /* ct */
+#define HOST_MSIX_ERR_INDEX_FN2 0x0001430c /* ct */
+#define HOST_MSIX_ERR_INDEX_FN3 0x0001440c /* ct */
+
+#define MBIST_CTL_REG 0x00014220 /* ct */
+#define __EDRAM_BISTR_START 0x00000004
+#define MBIST_STAT_REG 0x00014224 /* ct */
+#define ETH_MAC_SER_REG 0x00014288 /* ct */
+#define __APP_EMS_CKBUFAMPIN 0x00000020
+#define __APP_EMS_REFCLKSEL 0x00000010
+#define __APP_EMS_CMLCKSEL 0x00000008
+#define __APP_EMS_REFCKBUFEN2 0x00000004
+#define __APP_EMS_REFCKBUFEN1 0x00000002
+#define __APP_EMS_CHANNEL_SEL 0x00000001
+#define FNC_PERS_REG 0x00014604 /* ct */
+#define __F3_FUNCTION_ACTIVE 0x80000000
+#define __F3_FUNCTION_MODE 0x40000000
+#define __F3_PORT_MAP_MK 0x30000000
+#define __F3_PORT_MAP_SH 28
+#define __F3_PORT_MAP(_v) ((_v) << __F3_PORT_MAP_SH)
+#define __F3_VM_MODE 0x08000000
+#define __F3_INTX_STATUS_MK 0x07000000
+#define __F3_INTX_STATUS_SH 24
+#define __F3_INTX_STATUS(_v) ((_v) << __F3_INTX_STATUS_SH)
+#define __F2_FUNCTION_ACTIVE 0x00800000
+#define __F2_FUNCTION_MODE 0x00400000
+#define __F2_PORT_MAP_MK 0x00300000
+#define __F2_PORT_MAP_SH 20
+#define __F2_PORT_MAP(_v) ((_v) << __F2_PORT_MAP_SH)
+#define __F2_VM_MODE 0x00080000
+#define __F2_INTX_STATUS_MK 0x00070000
+#define __F2_INTX_STATUS_SH 16
+#define __F2_INTX_STATUS(_v) ((_v) << __F2_INTX_STATUS_SH)
+#define __F1_FUNCTION_ACTIVE 0x00008000
+#define __F1_FUNCTION_MODE 0x00004000
+#define __F1_PORT_MAP_MK 0x00003000
+#define __F1_PORT_MAP_SH 12
+#define __F1_PORT_MAP(_v) ((_v) << __F1_PORT_MAP_SH)
+#define __F1_VM_MODE 0x00000800
+#define __F1_INTX_STATUS_MK 0x00000700
+#define __F1_INTX_STATUS_SH 8
+#define __F1_INTX_STATUS(_v) ((_v) << __F1_INTX_STATUS_SH)
+#define __F0_FUNCTION_ACTIVE 0x00000080
+#define __F0_FUNCTION_MODE 0x00000040
+#define __F0_PORT_MAP_MK 0x00000030
+#define __F0_PORT_MAP_SH 4
+#define __F0_PORT_MAP(_v) ((_v) << __F0_PORT_MAP_SH)
+#define __F0_VM_MODE 0x00000008
+#define __F0_INTX_STATUS 0x00000007
+enum {
+ __F0_INTX_STATUS_MSIX = 0x0,
+ __F0_INTX_STATUS_INTA = 0x1,
+ __F0_INTX_STATUS_INTB = 0x2,
+ __F0_INTX_STATUS_INTC = 0x3,
+ __F0_INTX_STATUS_INTD = 0x4,
+};
+
+#define OP_MODE 0x0001460c /* ct */
+#define __APP_ETH_CLK_LOWSPEED 0x00000004
+#define __GLOBAL_CORECLK_HALFSPEED 0x00000002
+#define __GLOBAL_FCOE_MODE 0x00000001
+#define FW_INIT_HALT_P0 0x000191ac /* ct */
+#define __FW_INIT_HALT_P 0x00000001
+#define FW_INIT_HALT_P1 0x000191bc /* ct */
+#define PMM_1T_RESET_REG_P0 0x0002381c /* ct */
+#define __PMM_1T_RESET_P 0x00000001
+#define PMM_1T_RESET_REG_P1 0x00023c1c /* ct */
+
+/**
+ * Catapult-2 specific defines
+ */
+#define CT2_PCI_CPQ_BASE 0x00030000
+#define CT2_PCI_APP_BASE 0x00030100
+#define CT2_PCI_ETH_BASE 0x00030400
+
+/*
+ * APP block registers
+ */
+#define CT2_HOSTFN_INT_STATUS (CT2_PCI_APP_BASE + 0x00)
+#define CT2_HOSTFN_INTR_MASK (CT2_PCI_APP_BASE + 0x04)
+#define CT2_HOSTFN_PERSONALITY0 (CT2_PCI_APP_BASE + 0x08)
+#define __PME_STATUS_ 0x00200000
+#define __PF_VF_BAR_SIZE_MODE__MK 0x00180000
+#define __PF_VF_BAR_SIZE_MODE__SH 19
+#define __PF_VF_BAR_SIZE_MODE_(_v) ((_v) << __PF_VF_BAR_SIZE_MODE__SH)
+#define __FC_LL_PORT_MAP__MK 0x00060000
+#define __FC_LL_PORT_MAP__SH 17
+#define __FC_LL_PORT_MAP_(_v) ((_v) << __FC_LL_PORT_MAP__SH)
+#define __PF_VF_ACTIVE_ 0x00010000
+#define __PF_VF_CFG_RDY_ 0x00008000
+#define __PF_VF_ENABLE_ 0x00004000
+#define __PF_DRIVER_ACTIVE_ 0x00002000
+#define __PF_PME_SEND_ENABLE_ 0x00001000
+#define __PF_EXROM_OFFSET__MK 0x00000ff0
+#define __PF_EXROM_OFFSET__SH 4
+#define __PF_EXROM_OFFSET_(_v) ((_v) << __PF_EXROM_OFFSET__SH)
+#define __FC_LL_MODE_ 0x00000008
+#define __PF_INTX_PIN_ 0x00000007
+#define CT2_HOSTFN_PERSONALITY1 (CT2_PCI_APP_BASE + 0x0C)
+#define __PF_NUM_QUEUES1__MK 0xff000000
+#define __PF_NUM_QUEUES1__SH 24
+#define __PF_NUM_QUEUES1_(_v) ((_v) << __PF_NUM_QUEUES1__SH)
+#define __PF_VF_QUE_OFFSET1__MK 0x00ff0000
+#define __PF_VF_QUE_OFFSET1__SH 16
+#define __PF_VF_QUE_OFFSET1_(_v) ((_v) << __PF_VF_QUE_OFFSET1__SH)
+#define __PF_VF_NUM_QUEUES__MK 0x0000ff00
+#define __PF_VF_NUM_QUEUES__SH 8
+#define __PF_VF_NUM_QUEUES_(_v) ((_v) << __PF_VF_NUM_QUEUES__SH)
+#define __PF_VF_QUE_OFFSET_ 0x000000ff
+#define CT2_HOSTFN_PAGE_NUM (CT2_PCI_APP_BASE + 0x18)
+#define CT2_HOSTFN_MSIX_VT_INDEX_MBOX_ERR (CT2_PCI_APP_BASE + 0x38)
+
+/*
+ * Catapult-2 CPQ block registers
+ */
+#define CT2_HOSTFN_LPU0_MBOX0 (CT2_PCI_CPQ_BASE + 0x00)
+#define CT2_HOSTFN_LPU1_MBOX0 (CT2_PCI_CPQ_BASE + 0x20)
+#define CT2_LPU0_HOSTFN_MBOX0 (CT2_PCI_CPQ_BASE + 0x40)
+#define CT2_LPU1_HOSTFN_MBOX0 (CT2_PCI_CPQ_BASE + 0x60)
+#define CT2_HOSTFN_LPU0_CMD_STAT (CT2_PCI_CPQ_BASE + 0x80)
+#define CT2_HOSTFN_LPU1_CMD_STAT (CT2_PCI_CPQ_BASE + 0x84)
+#define CT2_LPU0_HOSTFN_CMD_STAT (CT2_PCI_CPQ_BASE + 0x88)
+#define CT2_LPU1_HOSTFN_CMD_STAT (CT2_PCI_CPQ_BASE + 0x8c)
+#define CT2_HOSTFN_LPU0_READ_STAT (CT2_PCI_CPQ_BASE + 0x90)
+#define CT2_HOSTFN_LPU1_READ_STAT (CT2_PCI_CPQ_BASE + 0x94)
+#define CT2_LPU0_HOSTFN_MBOX0_MSK (CT2_PCI_CPQ_BASE + 0x98)
+#define CT2_LPU1_HOSTFN_MBOX0_MSK (CT2_PCI_CPQ_BASE + 0x9C)
+#define CT2_HOST_SEM0_REG 0x000148f0
+#define CT2_HOST_SEM1_REG 0x000148f4
+#define CT2_HOST_SEM2_REG 0x000148f8
+#define CT2_HOST_SEM3_REG 0x000148fc
+#define CT2_HOST_SEM4_REG 0x00014900
+#define CT2_HOST_SEM5_REG 0x00014904
+#define CT2_HOST_SEM6_REG 0x00014908
+#define CT2_HOST_SEM7_REG 0x0001490c
+#define CT2_HOST_SEM0_INFO_REG 0x000148b0
+#define CT2_HOST_SEM1_INFO_REG 0x000148b4
+#define CT2_HOST_SEM2_INFO_REG 0x000148b8
+#define CT2_HOST_SEM3_INFO_REG 0x000148bc
+#define CT2_HOST_SEM4_INFO_REG 0x000148c0
+#define CT2_HOST_SEM5_INFO_REG 0x000148c4
+#define CT2_HOST_SEM6_INFO_REG 0x000148c8
+#define CT2_HOST_SEM7_INFO_REG 0x000148cc
+
+#define CT2_APP_PLL_LCLK_CTL_REG 0x00014808
+#define __APP_LPUCLK_HALFSPEED 0x40000000
+#define __APP_PLL_LCLK_LOAD 0x20000000
+#define __APP_PLL_LCLK_FBCNT_MK 0x1fe00000
+#define __APP_PLL_LCLK_FBCNT_SH 21
+#define __APP_PLL_LCLK_FBCNT(_v) ((_v) << __APP_PLL_SCLK_FBCNT_SH)
+enum {
+ __APP_PLL_LCLK_FBCNT_425_MHZ = 6,
+ __APP_PLL_LCLK_FBCNT_468_MHZ = 4,
+};
+#define __APP_PLL_LCLK_EXTFB 0x00000800
+#define __APP_PLL_LCLK_ENOUTS 0x00000400
+#define __APP_PLL_LCLK_RATE 0x00000010
+#define CT2_APP_PLL_SCLK_CTL_REG 0x0001480c
+#define __P_SCLK_PLL_LOCK 0x80000000
+#define __APP_PLL_SCLK_REFCLK_SEL 0x40000000
+#define __APP_PLL_SCLK_CLK_DIV2 0x20000000
+#define __APP_PLL_SCLK_LOAD 0x10000000
+#define __APP_PLL_SCLK_FBCNT_MK 0x0ff00000
+#define __APP_PLL_SCLK_FBCNT_SH 20
+#define __APP_PLL_SCLK_FBCNT(_v) ((_v) << __APP_PLL_SCLK_FBCNT_SH)
+enum {
+ __APP_PLL_SCLK_FBCNT_NORM = 6,
+ __APP_PLL_SCLK_FBCNT_10G_FC = 10,
+};
+#define __APP_PLL_SCLK_EXTFB 0x00000800
+#define __APP_PLL_SCLK_ENOUTS 0x00000400
+#define __APP_PLL_SCLK_RATE 0x00000010
+#define CT2_PCIE_MISC_REG 0x00014804
+#define __ETH_CLK_ENABLE_PORT1 0x00000010
+#define CT2_CHIP_MISC_PRG 0x000148a4
+#define __ETH_CLK_ENABLE_PORT0 0x00004000
+#define __APP_LPU_SPEED 0x00000002
+#define CT2_MBIST_STAT_REG 0x00014818
+#define CT2_MBIST_CTL_REG 0x0001481c
+#define CT2_PMM_1T_CONTROL_REG_P0 0x0002381c
+#define __PMM_1T_PNDB_P 0x00000002
+#define CT2_PMM_1T_CONTROL_REG_P1 0x00023c1c
+#define CT2_WGN_STATUS 0x00014990
+#define __A2T_AHB_LOAD 0x00000800
+#define __WGN_READY 0x00000400
+#define __GLBL_PF_VF_CFG_RDY 0x00000200
+#define CT2_NFC_STS_REG 0x00027410
+#define CT2_NFC_CSR_CLR_REG 0x00027420
+#define CT2_NFC_CSR_SET_REG 0x00027424
+#define __HALT_NFC_CONTROLLER 0x00000002
+#define __NFC_CONTROLLER_HALTED 0x00001000
+#define CT2_RSC_GPR15_REG 0x0002765c
+#define CT2_CSI_FW_CTL_REG 0x00027080
+#define CT2_CSI_FW_CTL_SET_REG 0x00027088
+#define __RESET_AND_START_SCLK_LCLK_PLLS 0x00010000
+
+#define CT2_CSI_MAC0_CONTROL_REG 0x000270d0
+#define __CSI_MAC_RESET 0x00000010
+#define __CSI_MAC_AHB_RESET 0x00000008
+#define CT2_CSI_MAC1_CONTROL_REG 0x000270d4
+#define CT2_CSI_MAC_CONTROL_REG(__n) \
+ (CT2_CSI_MAC0_CONTROL_REG + \
+ (__n) * (CT2_CSI_MAC1_CONTROL_REG - CT2_CSI_MAC0_CONTROL_REG))
+
+#define CT2_NFC_FLASH_STS_REG 0x00014834
+#define __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS 0x00000020
+/*
+ * Name semaphore registers based on usage
+ */
+#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG
+#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG
+#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG
+#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG
+#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG
+#define BFA_IOC_FAIL_SYNC HOST_SEM5_INFO_REG
+
+/*
+ * CT2 semaphore register locations changed
+ */
+#define CT2_BFA_IOC0_HBEAT_REG CT2_HOST_SEM0_INFO_REG
+#define CT2_BFA_IOC0_STATE_REG CT2_HOST_SEM1_INFO_REG
+#define CT2_BFA_IOC1_HBEAT_REG CT2_HOST_SEM2_INFO_REG
+#define CT2_BFA_IOC1_STATE_REG CT2_HOST_SEM3_INFO_REG
+#define CT2_BFA_FW_USE_COUNT CT2_HOST_SEM4_INFO_REG
+#define CT2_BFA_IOC_FAIL_SYNC CT2_HOST_SEM5_INFO_REG
+
+#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
+#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q))
+
+/*
+ * And corresponding host interrupt status bit field defines
+ */
+#define __HFN_INT_CPE_Q0 0x00000001U
+#define __HFN_INT_CPE_Q1 0x00000002U
+#define __HFN_INT_CPE_Q2 0x00000004U
+#define __HFN_INT_CPE_Q3 0x00000008U
+#define __HFN_INT_CPE_Q4 0x00000010U
+#define __HFN_INT_CPE_Q5 0x00000020U
+#define __HFN_INT_CPE_Q6 0x00000040U
+#define __HFN_INT_CPE_Q7 0x00000080U
+#define __HFN_INT_RME_Q0 0x00000100U
+#define __HFN_INT_RME_Q1 0x00000200U
+#define __HFN_INT_RME_Q2 0x00000400U
+#define __HFN_INT_RME_Q3 0x00000800U
+#define __HFN_INT_RME_Q4 0x00001000U
+#define __HFN_INT_RME_Q5 0x00002000U
+#define __HFN_INT_RME_Q6 0x00004000U
+#define __HFN_INT_RME_Q7 0x00008000U
+#define __HFN_INT_ERR_EMC 0x00010000U
+#define __HFN_INT_ERR_LPU0 0x00020000U
+#define __HFN_INT_ERR_LPU1 0x00040000U
+#define __HFN_INT_ERR_PSS 0x00080000U
+#define __HFN_INT_MBOX_LPU0 0x00100000U
+#define __HFN_INT_MBOX_LPU1 0x00200000U
+#define __HFN_INT_MBOX1_LPU0 0x00400000U
+#define __HFN_INT_MBOX1_LPU1 0x00800000U
+#define __HFN_INT_LL_HALT 0x01000000U
+#define __HFN_INT_CPE_MASK 0x000000ffU
+#define __HFN_INT_RME_MASK 0x0000ff00U
+#define __HFN_INT_ERR_MASK \
+ (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 | __HFN_INT_ERR_LPU1 | \
+ __HFN_INT_ERR_PSS | __HFN_INT_LL_HALT)
+#define __HFN_INT_FN0_MASK \
+ (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | __HFN_INT_CPE_Q2 | \
+ __HFN_INT_CPE_Q3 | __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | \
+ __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | __HFN_INT_MBOX_LPU0)
+#define __HFN_INT_FN1_MASK \
+ (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | __HFN_INT_CPE_Q6 | \
+ __HFN_INT_CPE_Q7 | __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | \
+ __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | __HFN_INT_MBOX_LPU1)
+
+/*
+ * Host interrupt status defines for catapult-2
+ */
+#define __HFN_INT_MBOX_LPU0_CT2 0x00010000U
+#define __HFN_INT_MBOX_LPU1_CT2 0x00020000U
+#define __HFN_INT_ERR_PSS_CT2 0x00040000U
+#define __HFN_INT_ERR_LPU0_CT2 0x00080000U
+#define __HFN_INT_ERR_LPU1_CT2 0x00100000U
+#define __HFN_INT_CPQ_HALT_CT2 0x00200000U
+#define __HFN_INT_ERR_WGN_CT2 0x00400000U
+#define __HFN_INT_ERR_LEHRX_CT2 0x00800000U
+#define __HFN_INT_ERR_LEHTX_CT2 0x01000000U
+#define __HFN_INT_ERR_MASK_CT2 \
+ (__HFN_INT_ERR_PSS_CT2 | __HFN_INT_ERR_LPU0_CT2 | \
+ __HFN_INT_ERR_LPU1_CT2 | __HFN_INT_CPQ_HALT_CT2 | \
+ __HFN_INT_ERR_WGN_CT2 | __HFN_INT_ERR_LEHRX_CT2 | \
+ __HFN_INT_ERR_LEHTX_CT2)
+#define __HFN_INT_FN0_MASK_CT2 \
+ (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | __HFN_INT_CPE_Q2 | \
+ __HFN_INT_CPE_Q3 | __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | \
+ __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | __HFN_INT_MBOX_LPU0_CT2)
+#define __HFN_INT_FN1_MASK_CT2 \
+ (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | __HFN_INT_CPE_Q6 | \
+ __HFN_INT_CPE_Q7 | __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | \
+ __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | __HFN_INT_MBOX_LPU1_CT2)
+
+/*
+ * asic memory map.
+ */
+#define PSS_SMEM_PAGE_START 0x8000
+#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15))
+#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff)
+
+#endif /* __BFI_REG_H__ */
diff --git a/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h b/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h
index 97a61b4d81b7..698f5ebaa0c2 100644
--- a/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h
+++ b/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h
@@ -1,3 +1,17 @@
+/* 57xx_hsi_bnx2fc.h: QLogic Linux FCoE offload driver.
+ * Handles operations such as session offload/upload etc, and manages
+ * session resources such as connection id and qp resources.
+ *
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ */
+
#ifndef __57XX_FCOE_HSI_LINUX_LE__
#define __57XX_FCOE_HSI_LINUX_LE__
@@ -19,6 +33,23 @@ struct b577xx_doorbell_hdr {
/*
* doorbell message sent to the chip
*/
+struct b577xx_doorbell {
+#if defined(__BIG_ENDIAN)
+ u16 zero_fill2;
+ u8 zero_fill1;
+ struct b577xx_doorbell_hdr header;
+#elif defined(__LITTLE_ENDIAN)
+ struct b577xx_doorbell_hdr header;
+ u8 zero_fill1;
+ u16 zero_fill2;
+#endif
+};
+
+
+
+/*
+ * doorbell message sent to the chip
+ */
struct b577xx_doorbell_set_prod {
#if defined(__BIG_ENDIAN)
u16 prod;
@@ -39,106 +70,63 @@ struct regpair {
/*
- * Fixed size structure in order to plant it in Union structure
+ * ABTS info $$KEEP_ENDIANNESS$$
*/
-struct fcoe_abts_rsp_union {
- u32 r_ctl;
- u32 abts_rsp_payload[7];
+struct fcoe_abts_info {
+ __le16 aborted_task_id;
+ __le16 reserved0;
+ __le32 reserved1;
};
/*
- * 4 regs size
+ * Fixed size structure in order to plant it in Union structure
+ * $$KEEP_ENDIANNESS$$
*/
-struct fcoe_bd_ctx {
- u32 buf_addr_hi;
- u32 buf_addr_lo;
-#if defined(__BIG_ENDIAN)
- u16 rsrv0;
- u16 buf_len;
-#elif defined(__LITTLE_ENDIAN)
- u16 buf_len;
- u16 rsrv0;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 rsrv1;
- u16 flags;
-#elif defined(__LITTLE_ENDIAN)
- u16 flags;
- u16 rsrv1;
-#endif
+struct fcoe_abts_rsp_union {
+ u8 r_ctl;
+ u8 rsrv[3];
+ __le32 abts_rsp_payload[7];
};
-struct fcoe_cleanup_flow_info {
-#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u16 task_id;
-#elif defined(__LITTLE_ENDIAN)
- u16 task_id;
- u16 reserved1;
-#endif
- u32 reserved2[7];
+/*
+ * 4 regs size $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_bd_ctx {
+ __le32 buf_addr_hi;
+ __le32 buf_addr_lo;
+ __le16 buf_len;
+ __le16 rsrv0;
+ __le16 flags;
+ __le16 rsrv1;
};
-struct fcoe_fcp_cmd_payload {
- u32 opaque[8];
-};
-
-struct fcoe_fc_hdr {
-#if defined(__BIG_ENDIAN)
- u8 cs_ctl;
- u8 s_id[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 s_id[3];
- u8 cs_ctl;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 r_ctl;
- u8 d_id[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 d_id[3];
- u8 r_ctl;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 seq_id;
- u8 df_ctl;
- u16 seq_cnt;
-#elif defined(__LITTLE_ENDIAN)
- u16 seq_cnt;
- u8 df_ctl;
- u8 seq_id;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 type;
- u8 f_ctl[3];
-#elif defined(__LITTLE_ENDIAN)
- u8 f_ctl[3];
- u8 type;
-#endif
- u32 parameters;
-#if defined(__BIG_ENDIAN)
- u16 ox_id;
- u16 rx_id;
-#elif defined(__LITTLE_ENDIAN)
- u16 rx_id;
- u16 ox_id;
-#endif
+/*
+ * FCoE cached sges context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_cached_sge_ctx {
+ struct regpair cur_buf_addr;
+ __le16 cur_buf_rem;
+ __le16 second_buf_rem;
+ struct regpair second_buf_addr;
};
-struct fcoe_fc_frame {
- struct fcoe_fc_hdr fc_hdr;
- u32 reserved0[2];
-};
-union fcoe_cmd_flow_info {
- struct fcoe_fcp_cmd_payload fcp_cmd_payload;
- struct fcoe_fc_frame mp_fc_frame;
+/*
+ * Cleanup info $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_cleanup_info {
+ __le16 cleaned_task_id;
+ __le16 rolled_tx_seq_cnt;
+ __le32 rolled_tx_data_offset;
};
-
+/*
+ * Fcp RSP flags $$KEEP_ENDIANNESS$$
+ */
struct fcoe_fcp_rsp_flags {
u8 flags;
#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID (0x1<<0)
@@ -155,95 +143,168 @@ struct fcoe_fcp_rsp_flags {
#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS_SHIFT 5
};
-
+/*
+ * Fcp RSP payload $$KEEP_ENDIANNESS$$
+ */
struct fcoe_fcp_rsp_payload {
struct regpair reserved0;
- u32 fcp_resid;
-#if defined(__BIG_ENDIAN)
- u16 retry_delay_timer;
- struct fcoe_fcp_rsp_flags fcp_flags;
- u8 scsi_status_code;
-#elif defined(__LITTLE_ENDIAN)
+ __le32 fcp_resid;
u8 scsi_status_code;
struct fcoe_fcp_rsp_flags fcp_flags;
- u16 retry_delay_timer;
-#endif
- u32 fcp_rsp_len;
- u32 fcp_sns_len;
+ __le16 retry_delay_timer;
+ __le32 fcp_rsp_len;
+ __le32 fcp_sns_len;
};
-
/*
* Fixed size structure in order to plant it in Union structure
+ * $$KEEP_ENDIANNESS$$
*/
struct fcoe_fcp_rsp_union {
struct fcoe_fcp_rsp_payload payload;
struct regpair reserved0;
};
+/*
+ * FC header $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fc_hdr {
+ u8 s_id[3];
+ u8 cs_ctl;
+ u8 d_id[3];
+ u8 r_ctl;
+ __le16 seq_cnt;
+ u8 df_ctl;
+ u8 seq_id;
+ u8 f_ctl[3];
+ u8 type;
+ __le32 parameters;
+ __le16 rx_id;
+ __le16 ox_id;
+};
-struct fcoe_fcp_xfr_rdy_payload {
- u32 burst_len;
- u32 data_ro;
+/*
+ * FC header union $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_mp_rsp_union {
+ struct fcoe_fc_hdr fc_hdr;
+ __le32 mp_payload_len;
+ __le32 rsrv;
};
-struct fcoe_read_flow_info {
- struct fcoe_fc_hdr fc_data_in_hdr;
- u32 reserved[2];
+/*
+ * Completion information $$KEEP_ENDIANNESS$$
+ */
+union fcoe_comp_flow_info {
+ struct fcoe_fcp_rsp_union fcp_rsp;
+ struct fcoe_abts_rsp_union abts_rsp;
+ struct fcoe_mp_rsp_union mp_rsp;
+ __le32 opaque[8];
};
-struct fcoe_write_flow_info {
- struct fcoe_fc_hdr fc_data_out_hdr;
- struct fcoe_fcp_xfr_rdy_payload fcp_xfr_payload;
+
+/*
+ * External ABTS info $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_abts_info {
+ __le32 rsrv0[6];
+ struct fcoe_abts_info ctx;
};
-union fcoe_rsp_flow_info {
- struct fcoe_fcp_rsp_union fcp_rsp;
- struct fcoe_abts_rsp_union abts_rsp;
+
+/*
+ * External cleanup info $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_cleanup_info {
+ __le32 rsrv0[6];
+ struct fcoe_cleanup_info ctx;
};
+
/*
- * 32 bytes used for general purposes
+ * Fcoe FW Tx sequence context $$KEEP_ENDIANNESS$$
*/
-union fcoe_general_task_ctx {
- union fcoe_cmd_flow_info cmd_info;
- struct fcoe_read_flow_info read_info;
- struct fcoe_write_flow_info write_info;
- union fcoe_rsp_flow_info rsp_info;
- struct fcoe_cleanup_flow_info cleanup_info;
- u32 comp_info[8];
+struct fcoe_fw_tx_seq_ctx {
+ __le32 data_offset;
+ __le16 seq_cnt;
+ __le16 rsrv0;
+};
+
+/*
+ * Fcoe external FW Tx sequence context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_fw_tx_seq_ctx {
+ __le32 rsrv0[6];
+ struct fcoe_fw_tx_seq_ctx ctx;
+};
+
+
+/*
+ * FCoE multiple sges context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_mul_sges_ctx {
+ struct regpair cur_sge_addr;
+ __le16 cur_sge_off;
+ u8 cur_sge_idx;
+ u8 sgl_size;
+};
+
+/*
+ * FCoE external multiple sges context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_ext_mul_sges_ctx {
+ struct fcoe_mul_sges_ctx mul_sgl;
+ struct regpair rsrv0;
};
/*
- * FCoE KCQ CQE parameters
+ * FCP CMD payload $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fcp_cmd_payload {
+ __le32 opaque[8];
+};
+
+
+
+
+
+/*
+ * Fcp xfr rdy payload $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fcp_xfr_rdy_payload {
+ __le32 burst_len;
+ __le32 data_ro;
+};
+
+
+/*
+ * FC frame $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_fc_frame {
+ struct fcoe_fc_hdr fc_hdr;
+ __le32 reserved0[2];
+};
+
+
+
+
+/*
+ * FCoE KCQ CQE parameters $$KEEP_ENDIANNESS$$
*/
union fcoe_kcqe_params {
- u32 reserved0[4];
+ __le32 reserved0[4];
};
/*
- * FCoE KCQ CQE
+ * FCoE KCQ CQE $$KEEP_ENDIANNESS$$
*/
struct fcoe_kcqe {
- u32 fcoe_conn_id;
- u32 completion_status;
- u32 fcoe_conn_context_id;
+ __le32 fcoe_conn_id;
+ __le32 completion_status;
+ __le32 fcoe_conn_context_id;
union fcoe_kcqe_params params;
-#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KCQE_RESERVED0 (0x7<<0)
-#define FCOE_KCQE_RESERVED0_SHIFT 0
-#define FCOE_KCQE_RAMROD_COMPLETION (0x1<<3)
-#define FCOE_KCQE_RAMROD_COMPLETION_SHIFT 3
-#define FCOE_KCQE_LAYER_CODE (0x7<<4)
-#define FCOE_KCQE_LAYER_CODE_SHIFT 4
-#define FCOE_KCQE_LINKED_WITH_NEXT (0x1<<7)
-#define FCOE_KCQE_LINKED_WITH_NEXT_SHIFT 7
- u8 op_code;
- u16 qe_self_seq;
-#elif defined(__LITTLE_ENDIAN)
- u16 qe_self_seq;
+ __le16 qe_self_seq;
u8 op_code;
u8 flags;
#define FCOE_KCQE_RESERVED0 (0x7<<0)
@@ -254,23 +315,14 @@ struct fcoe_kcqe {
#define FCOE_KCQE_LAYER_CODE_SHIFT 4
#define FCOE_KCQE_LINKED_WITH_NEXT (0x1<<7)
#define FCOE_KCQE_LINKED_WITH_NEXT_SHIFT 7
-#endif
};
+
+
/*
- * FCoE KWQE header
+ * FCoE KWQE header $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_header {
-#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KWQE_HEADER_RESERVED0 (0xF<<0)
-#define FCOE_KWQE_HEADER_RESERVED0_SHIFT 0
-#define FCOE_KWQE_HEADER_LAYER_CODE (0x7<<4)
-#define FCOE_KWQE_HEADER_LAYER_CODE_SHIFT 4
-#define FCOE_KWQE_HEADER_RESERVED1 (0x1<<7)
-#define FCOE_KWQE_HEADER_RESERVED1_SHIFT 7
- u8 op_code;
-#elif defined(__LITTLE_ENDIAN)
u8 op_code;
u8 flags;
#define FCOE_KWQE_HEADER_RESERVED0 (0xF<<0)
@@ -279,50 +331,23 @@ struct fcoe_kwqe_header {
#define FCOE_KWQE_HEADER_LAYER_CODE_SHIFT 4
#define FCOE_KWQE_HEADER_RESERVED1 (0x1<<7)
#define FCOE_KWQE_HEADER_RESERVED1_SHIFT 7
-#endif
};
/*
- * FCoE firmware init request 1
+ * FCoE firmware init request 1 $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_init1 {
-#if defined(__BIG_ENDIAN)
+ __le16 num_tasks;
struct fcoe_kwqe_header hdr;
- u16 num_tasks;
-#elif defined(__LITTLE_ENDIAN)
- u16 num_tasks;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 task_list_pbl_addr_lo;
- u32 task_list_pbl_addr_hi;
- u32 dummy_buffer_addr_lo;
- u32 dummy_buffer_addr_hi;
-#if defined(__BIG_ENDIAN)
- u16 rq_num_wqes;
- u16 sq_num_wqes;
-#elif defined(__LITTLE_ENDIAN)
- u16 sq_num_wqes;
- u16 rq_num_wqes;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 cq_num_wqes;
- u16 rq_buffer_log_size;
-#elif defined(__LITTLE_ENDIAN)
- u16 rq_buffer_log_size;
- u16 cq_num_wqes;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE (0xF<<0)
-#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE_SHIFT 0
-#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC (0x7<<4)
-#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC_SHIFT 4
-#define FCOE_KWQE_INIT1_RESERVED1 (0x1<<7)
-#define FCOE_KWQE_INIT1_RESERVED1_SHIFT 7
- u8 num_sessions_log;
- u16 mtu;
-#elif defined(__LITTLE_ENDIAN)
- u16 mtu;
+ __le32 task_list_pbl_addr_lo;
+ __le32 task_list_pbl_addr_hi;
+ __le32 dummy_buffer_addr_lo;
+ __le32 dummy_buffer_addr_hi;
+ __le16 sq_num_wqes;
+ __le16 rq_num_wqes;
+ __le16 rq_buffer_log_size;
+ __le16 cq_num_wqes;
+ __le16 mtu;
u8 num_sessions_log;
u8 flags;
#define FCOE_KWQE_INIT1_LOG_PAGE_SIZE (0xF<<0)
@@ -331,113 +356,73 @@ struct fcoe_kwqe_init1 {
#define FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC_SHIFT 4
#define FCOE_KWQE_INIT1_RESERVED1 (0x1<<7)
#define FCOE_KWQE_INIT1_RESERVED1_SHIFT 7
-#endif
};
/*
- * FCoE firmware init request 2
+ * FCoE firmware init request 2 $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_init2 {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
+ u8 hsi_major_version;
+ u8 hsi_minor_version;
struct fcoe_kwqe_header hdr;
-#endif
- u32 hash_tbl_pbl_addr_lo;
- u32 hash_tbl_pbl_addr_hi;
- u32 t2_hash_tbl_addr_lo;
- u32 t2_hash_tbl_addr_hi;
- u32 t2_ptr_hash_tbl_addr_lo;
- u32 t2_ptr_hash_tbl_addr_hi;
- u32 free_list_count;
+ __le32 hash_tbl_pbl_addr_lo;
+ __le32 hash_tbl_pbl_addr_hi;
+ __le32 t2_hash_tbl_addr_lo;
+ __le32 t2_hash_tbl_addr_hi;
+ __le32 t2_ptr_hash_tbl_addr_lo;
+ __le32 t2_ptr_hash_tbl_addr_hi;
+ __le32 free_list_count;
};
/*
- * FCoE firmware init request 3
+ * FCoE firmware init request 3 $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_init3 {
-#if defined(__BIG_ENDIAN)
+ __le16 reserved0;
struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 error_bit_map_lo;
- u32 error_bit_map_hi;
-#if defined(__BIG_ENDIAN)
+ __le32 error_bit_map_lo;
+ __le32 error_bit_map_hi;
+ u8 perf_config;
u8 reserved21[3];
- u8 cached_session_enable;
-#elif defined(__LITTLE_ENDIAN)
- u8 cached_session_enable;
- u8 reserved21[3];
-#endif
- u32 reserved2[4];
+ __le32 reserved2[4];
};
/*
- * FCoE connection offload request 1
+ * FCoE connection offload request 1 $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_conn_offload1 {
-#if defined(__BIG_ENDIAN)
+ __le16 fcoe_conn_id;
struct fcoe_kwqe_header hdr;
- u16 fcoe_conn_id;
-#elif defined(__LITTLE_ENDIAN)
- u16 fcoe_conn_id;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 sq_addr_lo;
- u32 sq_addr_hi;
- u32 rq_pbl_addr_lo;
- u32 rq_pbl_addr_hi;
- u32 rq_first_pbe_addr_lo;
- u32 rq_first_pbe_addr_hi;
-#if defined(__BIG_ENDIAN)
- u16 reserved0;
- u16 rq_prod;
-#elif defined(__LITTLE_ENDIAN)
- u16 rq_prod;
- u16 reserved0;
-#endif
+ __le32 sq_addr_lo;
+ __le32 sq_addr_hi;
+ __le32 rq_pbl_addr_lo;
+ __le32 rq_pbl_addr_hi;
+ __le32 rq_first_pbe_addr_lo;
+ __le32 rq_first_pbe_addr_hi;
+ __le16 rq_prod;
+ __le16 reserved0;
};
/*
- * FCoE connection offload request 2
+ * FCoE connection offload request 2 $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_conn_offload2 {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 tx_max_fc_pay_len;
-#elif defined(__LITTLE_ENDIAN)
- u16 tx_max_fc_pay_len;
+ __le16 tx_max_fc_pay_len;
struct fcoe_kwqe_header hdr;
-#endif
- u32 cq_addr_lo;
- u32 cq_addr_hi;
- u32 xferq_addr_lo;
- u32 xferq_addr_hi;
- u32 conn_db_addr_lo;
- u32 conn_db_addr_hi;
- u32 reserved1;
+ __le32 cq_addr_lo;
+ __le32 cq_addr_hi;
+ __le32 xferq_addr_lo;
+ __le32 xferq_addr_hi;
+ __le32 conn_db_addr_lo;
+ __le32 conn_db_addr_hi;
+ __le32 reserved1;
};
/*
- * FCoE connection offload request 3
+ * FCoE connection offload request 3 $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_conn_offload3 {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 vlan_tag;
-#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID (0xFFF<<0)
-#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT 0
-#define FCOE_KWQE_CONN_OFFLOAD3_CFI (0x1<<12)
-#define FCOE_KWQE_CONN_OFFLOAD3_CFI_SHIFT 12
-#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY (0x7<<13)
-#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT 13
-#elif defined(__LITTLE_ENDIAN)
- u16 vlan_tag;
+ __le16 vlan_tag;
#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID (0xFFF<<0)
#define FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT 0
#define FCOE_KWQE_CONN_OFFLOAD3_CFI (0x1<<12)
@@ -445,34 +430,8 @@ struct fcoe_kwqe_conn_offload3 {
#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY (0x7<<13)
#define FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT 13
struct fcoe_kwqe_header hdr;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 tx_max_conc_seqs_c3;
- u8 s_id[3];
-#elif defined(__LITTLE_ENDIAN)
u8 s_id[3];
u8 tx_max_conc_seqs_c3;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 flags;
-#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS (0x1<<0)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS_SHIFT 0
-#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES (0x1<<1)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES_SHIFT 1
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT (0x1<<2)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT_SHIFT 2
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ (0x1<<3)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT 3
-#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID (0x1<<4)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT 4
-#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID (0x1<<5)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_C2_VALID_SHIFT 5
-#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0 (0x1<<6)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0_SHIFT 6
-#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG (0x1<<7)
-#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT 7
- u8 d_id[3];
-#elif defined(__LITTLE_ENDIAN)
u8 d_id[3];
u8 flags;
#define FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS (0x1<<0)
@@ -491,79 +450,44 @@ struct fcoe_kwqe_conn_offload3 {
#define FCOE_KWQE_CONN_OFFLOAD3_B_ACK_0_SHIFT 6
#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG (0x1<<7)
#define FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT 7
-#endif
- u32 reserved;
- u32 confq_first_pbe_addr_lo;
- u32 confq_first_pbe_addr_hi;
-#if defined(__BIG_ENDIAN)
- u16 rx_max_fc_pay_len;
- u16 tx_total_conc_seqs;
-#elif defined(__LITTLE_ENDIAN)
- u16 tx_total_conc_seqs;
- u16 rx_max_fc_pay_len;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 rx_open_seqs_exch_c3;
- u8 rx_max_conc_seqs_c3;
- u16 rx_total_conc_seqs;
-#elif defined(__LITTLE_ENDIAN)
- u16 rx_total_conc_seqs;
+ __le32 reserved;
+ __le32 confq_first_pbe_addr_lo;
+ __le32 confq_first_pbe_addr_hi;
+ __le16 tx_total_conc_seqs;
+ __le16 rx_max_fc_pay_len;
+ __le16 rx_total_conc_seqs;
u8 rx_max_conc_seqs_c3;
u8 rx_open_seqs_exch_c3;
-#endif
};
/*
- * FCoE connection offload request 4
+ * FCoE connection offload request 4 $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_conn_offload4 {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u8 reserved2;
- u8 e_d_tov_timer_val;
-#elif defined(__LITTLE_ENDIAN)
u8 e_d_tov_timer_val;
u8 reserved2;
struct fcoe_kwqe_header hdr;
-#endif
- u8 src_mac_addr_lo32[4];
-#if defined(__BIG_ENDIAN)
- u8 dst_mac_addr_hi16[2];
- u8 src_mac_addr_hi16[2];
-#elif defined(__LITTLE_ENDIAN)
- u8 src_mac_addr_hi16[2];
- u8 dst_mac_addr_hi16[2];
-#endif
- u8 dst_mac_addr_lo32[4];
- u32 lcq_addr_lo;
- u32 lcq_addr_hi;
- u32 confq_pbl_base_addr_lo;
- u32 confq_pbl_base_addr_hi;
+ u8 src_mac_addr_lo[2];
+ u8 src_mac_addr_mid[2];
+ u8 src_mac_addr_hi[2];
+ u8 dst_mac_addr_hi[2];
+ u8 dst_mac_addr_lo[2];
+ u8 dst_mac_addr_mid[2];
+ __le32 lcq_addr_lo;
+ __le32 lcq_addr_hi;
+ __le32 confq_pbl_base_addr_lo;
+ __le32 confq_pbl_base_addr_hi;
};
/*
- * FCoE connection enable request
+ * FCoE connection enable request $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_conn_enable_disable {
-#if defined(__BIG_ENDIAN)
- struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
+ __le16 reserved0;
struct fcoe_kwqe_header hdr;
-#endif
- u8 src_mac_addr_lo32[4];
-#if defined(__BIG_ENDIAN)
- u16 vlan_tag;
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID (0xFFF<<0)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT 0
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI (0x1<<12)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI_SHIFT 12
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY (0x7<<13)
-#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT 13
- u8 src_mac_addr_hi16[2];
-#elif defined(__LITTLE_ENDIAN)
- u8 src_mac_addr_hi16[2];
+ u8 src_mac_addr_lo[2];
+ u8 src_mac_addr_mid[2];
+ u8 src_mac_addr_hi[2];
u16 vlan_tag;
#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID (0xFFF<<0)
#define FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT 0
@@ -571,82 +495,52 @@ struct fcoe_kwqe_conn_enable_disable {
#define FCOE_KWQE_CONN_ENABLE_DISABLE_CFI_SHIFT 12
#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY (0x7<<13)
#define FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT 13
-#endif
- u8 dst_mac_addr_lo32[4];
-#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u8 dst_mac_addr_hi16[2];
-#elif defined(__LITTLE_ENDIAN)
- u8 dst_mac_addr_hi16[2];
- u16 reserved1;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 vlan_flag;
- u8 s_id[3];
-#elif defined(__LITTLE_ENDIAN)
+ u8 dst_mac_addr_lo[2];
+ u8 dst_mac_addr_mid[2];
+ u8 dst_mac_addr_hi[2];
+ __le16 reserved1;
u8 s_id[3];
u8 vlan_flag;
-#endif
-#if defined(__BIG_ENDIAN)
- u8 reserved3;
- u8 d_id[3];
-#elif defined(__LITTLE_ENDIAN)
u8 d_id[3];
u8 reserved3;
-#endif
- u32 context_id;
- u32 conn_id;
- u32 reserved4;
+ __le32 context_id;
+ __le32 conn_id;
+ __le32 reserved4;
};
/*
- * FCoE connection destroy request
+ * FCoE connection destroy request $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_conn_destroy {
-#if defined(__BIG_ENDIAN)
+ __le16 reserved0;
struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 context_id;
- u32 conn_id;
- u32 reserved1[5];
+ __le32 context_id;
+ __le32 conn_id;
+ __le32 reserved1[5];
};
/*
- * FCoe destroy request
+ * FCoe destroy request $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_destroy {
-#if defined(__BIG_ENDIAN)
+ __le16 reserved0;
struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 reserved1[7];
+ __le32 reserved1[7];
};
/*
- * FCoe statistics request
+ * FCoe statistics request $$KEEP_ENDIANNESS$$
*/
struct fcoe_kwqe_stat {
-#if defined(__BIG_ENDIAN)
+ __le16 reserved0;
struct fcoe_kwqe_header hdr;
- u16 reserved0;
-#elif defined(__LITTLE_ENDIAN)
- u16 reserved0;
- struct fcoe_kwqe_header hdr;
-#endif
- u32 stat_params_addr_lo;
- u32 stat_params_addr_hi;
- u32 reserved1[5];
+ __le32 stat_params_addr_lo;
+ __le32 stat_params_addr_hi;
+ __le32 reserved1[5];
};
/*
- * FCoE KWQ WQE
+ * FCoE KWQ WQE $$KEEP_ENDIANNESS$$
*/
union fcoe_kwqe {
struct fcoe_kwqe_init1 init1;
@@ -662,19 +556,42 @@ union fcoe_kwqe {
struct fcoe_kwqe_stat statistics;
};
-struct fcoe_mul_sges_ctx {
- struct regpair cur_sge_addr;
-#if defined(__BIG_ENDIAN)
- u8 sgl_size;
- u8 cur_sge_idx;
- u16 cur_sge_off;
-#elif defined(__LITTLE_ENDIAN)
- u16 cur_sge_off;
- u8 cur_sge_idx;
- u8 sgl_size;
-#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * TX SGL context $$KEEP_ENDIANNESS$$
+ */
+union fcoe_sgl_union_ctx {
+ struct fcoe_cached_sge_ctx cached_sge;
+ struct fcoe_ext_mul_sges_ctx sgl;
+ __le32 opaque[5];
};
+/*
+ * Data-In/ELS/BLS information $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_read_flow_info {
+ union fcoe_sgl_union_ctx sgl_ctx;
+ __le32 rsrv0[3];
+};
+
+
+/*
+ * Fcoe stat context $$KEEP_ENDIANNESS$$
+ */
struct fcoe_s_stat_ctx {
u8 flags;
#define FCOE_S_STAT_CTX_ACTIVE (0x1<<0)
@@ -693,51 +610,34 @@ struct fcoe_s_stat_ctx {
#define FCOE_S_STAT_CTX_RSRV1_SHIFT 6
};
-struct fcoe_seq_ctx {
-#if defined(__BIG_ENDIAN)
- u16 low_seq_cnt;
- struct fcoe_s_stat_ctx s_stat;
- u8 seq_id;
-#elif defined(__LITTLE_ENDIAN)
+/*
+ * Fcoe rx seq context $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_rx_seq_ctx {
u8 seq_id;
struct fcoe_s_stat_ctx s_stat;
- u16 low_seq_cnt;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 err_seq_cnt;
- u16 high_seq_cnt;
-#elif defined(__LITTLE_ENDIAN)
- u16 high_seq_cnt;
- u16 err_seq_cnt;
-#endif
- u32 low_exp_ro;
- u32 high_exp_ro;
+ __le16 seq_cnt;
+ __le32 low_exp_ro;
+ __le32 high_exp_ro;
};
-struct fcoe_single_sge_ctx {
- struct regpair cur_buf_addr;
-#if defined(__BIG_ENDIAN)
- u16 reserved0;
- u16 cur_buf_rem;
-#elif defined(__LITTLE_ENDIAN)
- u16 cur_buf_rem;
- u16 reserved0;
-#endif
-};
-
-union fcoe_sgl_ctx {
- struct fcoe_single_sge_ctx single_sge;
- struct fcoe_mul_sges_ctx mul_sges;
+/*
+ * Fcoe rx_wr union context $$KEEP_ENDIANNESS$$
+ */
+union fcoe_rx_wr_union_ctx {
+ struct fcoe_read_flow_info read_info;
+ union fcoe_comp_flow_info comp_info;
+ __le32 opaque[8];
};
/*
- * FCoE SQ element
+ * FCoE SQ element $$KEEP_ENDIANNESS$$
*/
struct fcoe_sqe {
- u16 wqe;
+ __le16 wqe;
#define FCOE_SQE_TASK_ID (0x7FFF<<0)
#define FCOE_SQE_TASK_ID_SHIFT 0
#define FCOE_SQE_TOGGLE_BIT (0x1<<15)
@@ -746,135 +646,141 @@ struct fcoe_sqe {
-struct fcoe_task_ctx_entry_tx_only {
- union fcoe_sgl_ctx sgl_ctx;
+/*
+ * 14 regs $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_tx_only {
+ union fcoe_sgl_union_ctx sgl_ctx;
+ __le32 rsrv0;
};
-struct fcoe_task_ctx_entry_txwr_rxrd {
-#if defined(__BIG_ENDIAN)
- u16 verify_tx_seq;
+/*
+ * 32 bytes (8 regs) used for TX only purposes $$KEEP_ENDIANNESS$$
+ */
+union fcoe_tx_wr_rx_rd_union_ctx {
+ struct fcoe_fc_frame tx_frame;
+ struct fcoe_fcp_cmd_payload fcp_cmd;
+ struct fcoe_ext_cleanup_info cleanup;
+ struct fcoe_ext_abts_info abts;
+ struct fcoe_ext_fw_tx_seq_ctx tx_seq;
+ __le32 opaque[8];
+};
+
+/*
+ * tce_tx_wr_rx_rd_const $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_tx_wr_rx_rd_const {
u8 init_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE (0x7<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE (0x1<<3)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT 3
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE (0x1<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE (0x1<<5)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE_SHIFT 5
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5 (0x3<<6)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5_SHIFT 6
- u8 tx_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4 (0xF<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4_SHIFT 4
-#elif defined(__LITTLE_ENDIAN)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE (0x7<<0)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT 0
+#define FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE (0x1<<3)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT 3
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE (0x1<<4)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT 4
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE (0x3<<5)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE_SHIFT 5
+#define FCOE_TCE_TX_WR_RX_RD_CONST_SUPPORT_REC_TOV (0x1<<7)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_SUPPORT_REC_TOV_SHIFT 7
u8 tx_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4 (0xF<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV4_SHIFT 4
- u8 init_flags;
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE (0x7<<0)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE (0x1<<3)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT 3
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE (0x1<<4)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE (0x1<<5)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE_SHIFT 5
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5 (0x3<<6)
-#define FCOE_TASK_CTX_ENTRY_TXWR_RXRD_RSRV5_SHIFT 6
- u16 verify_tx_seq;
-#endif
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_VALID (0x1<<0)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_VALID_SHIFT 0
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE (0xF<<1)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE_SHIFT 1
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV1 (0x1<<5)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV1_SHIFT 5
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_SEQ_INIT (0x1<<6)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_TX_SEQ_INIT_SHIFT 6
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV2 (0x1<<7)
+#define FCOE_TCE_TX_WR_RX_RD_CONST_RSRV2_SHIFT 7
+ __le16 rsrv3;
+ __le32 verify_tx_seq;
};
/*
- * Common section. Both TX and RX processing might write and read from it in
- * different flows
+ * tce_tx_wr_rx_rd $$KEEP_ENDIANNESS$$
*/
-struct fcoe_task_ctx_entry_tx_rx_cmn {
- u32 data_2_trns;
- union fcoe_general_task_ctx general;
-#if defined(__BIG_ENDIAN)
- u16 tx_low_seq_cnt;
- struct fcoe_s_stat_ctx tx_s_stat;
- u8 tx_seq_id;
-#elif defined(__LITTLE_ENDIAN)
- u8 tx_seq_id;
- struct fcoe_s_stat_ctx tx_s_stat;
- u16 tx_low_seq_cnt;
-#endif
- u32 common_flags;
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID (0xFFFFFF<<0)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID (0x1<<24)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID_SHIFT 24
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_SEQ_INIT (0x1<<25)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_SEQ_INIT_SHIFT 25
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_XFER (0x1<<26)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_XFER_SHIFT 26
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_CONF (0x1<<27)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_PEND_CONF_SHIFT 27
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME (0x1<<28)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME_SHIFT 28
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_RSRV (0x7<<29)
-#define FCOE_TASK_CTX_ENTRY_TX_RX_CMN_RSRV_SHIFT 29
-};
-
-struct fcoe_task_ctx_entry_rxwr_txrd {
-#if defined(__BIG_ENDIAN)
- u16 rx_id;
- u16 rx_flags;
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE (0x7<<4)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ (0x1<<7)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ_SHIFT 7
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME (0x1<<8)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME_SHIFT 8
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0 (0x7F<<9)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0_SHIFT 9
-#elif defined(__LITTLE_ENDIAN)
- u16 rx_flags;
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE (0xF<<0)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE_SHIFT 0
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE (0x7<<4)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE_SHIFT 4
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ (0x1<<7)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_CONF_REQ_SHIFT 7
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME (0x1<<8)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_MISS_FRAME_SHIFT 8
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0 (0x7F<<9)
-#define FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RESERVED0_SHIFT 9
- u16 rx_id;
-#endif
+struct fcoe_tce_tx_wr_rx_rd {
+ union fcoe_tx_wr_rx_rd_union_ctx union_ctx;
+ struct fcoe_tce_tx_wr_rx_rd_const const_ctx;
+};
+
+/*
+ * tce_rx_wr_tx_rd_const $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_wr_tx_rd_const {
+ __le32 data_2_trns;
+ __le32 init_flags;
+#define FCOE_TCE_RX_WR_TX_RD_CONST_CID (0xFFFFFF<<0)
+#define FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT 0
+#define FCOE_TCE_RX_WR_TX_RD_CONST_RSRV0 (0xFF<<24)
+#define FCOE_TCE_RX_WR_TX_RD_CONST_RSRV0_SHIFT 24
+};
+
+/*
+ * tce_rx_wr_tx_rd_var $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_wr_tx_rd_var {
+ __le16 rx_flags;
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV1 (0xF<<0)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV1_SHIFT 0
+#define FCOE_TCE_RX_WR_TX_RD_VAR_NUM_RQ_WQE (0x7<<4)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_NUM_RQ_WQE_SHIFT 4
+#define FCOE_TCE_RX_WR_TX_RD_VAR_CONF_REQ (0x1<<7)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_CONF_REQ_SHIFT 7
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_STATE (0xF<<8)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_STATE_SHIFT 8
+#define FCOE_TCE_RX_WR_TX_RD_VAR_EXP_FIRST_FRAME (0x1<<12)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_EXP_FIRST_FRAME_SHIFT 12
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_SEQ_INIT (0x1<<13)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_SEQ_INIT_SHIFT 13
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV2 (0x1<<14)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RSRV2_SHIFT 14
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_VALID (0x1<<15)
+#define FCOE_TCE_RX_WR_TX_RD_VAR_RX_VALID_SHIFT 15
+ __le16 rx_id;
+ struct fcoe_fcp_xfr_rdy_payload fcp_xfr_rdy;
};
-struct fcoe_task_ctx_entry_rx_only {
- struct fcoe_seq_ctx seq_ctx;
- struct fcoe_seq_ctx ooo_seq_ctx;
- u32 rsrv3;
- union fcoe_sgl_ctx sgl_ctx;
+/*
+ * tce_rx_wr_tx_rd $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_wr_tx_rd {
+ struct fcoe_tce_rx_wr_tx_rd_const const_ctx;
+ struct fcoe_tce_rx_wr_tx_rd_var var_ctx;
+};
+
+/*
+ * tce_rx_only $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_tce_rx_only {
+ struct fcoe_rx_seq_ctx rx_seq_ctx;
+ union fcoe_rx_wr_union_ctx union_ctx;
};
+/*
+ * task_ctx_entry $$KEEP_ENDIANNESS$$
+ */
struct fcoe_task_ctx_entry {
- struct fcoe_task_ctx_entry_tx_only tx_wr_only;
- struct fcoe_task_ctx_entry_txwr_rxrd tx_wr_rx_rd;
- struct fcoe_task_ctx_entry_tx_rx_cmn cmn;
- struct fcoe_task_ctx_entry_rxwr_txrd rx_wr_tx_rd;
- struct fcoe_task_ctx_entry_rx_only rx_wr_only;
- u32 reserved[4];
+ struct fcoe_tce_tx_only txwr_only;
+ struct fcoe_tce_tx_wr_rx_rd txwr_rxrd;
+ struct fcoe_tce_rx_wr_tx_rd rxwr_txrd;
+ struct fcoe_tce_rx_only rxwr_only;
};
+
+
+
+
+
+
+
+
/*
- * FCoE XFRQ element
+ * FCoE XFRQ element $$KEEP_ENDIANNESS$$
*/
struct fcoe_xfrqe {
- u16 wqe;
+ __le16 wqe;
#define FCOE_XFRQE_TASK_ID (0x7FFF<<0)
#define FCOE_XFRQE_TASK_ID_SHIFT 0
#define FCOE_XFRQE_TOGGLE_BIT (0x1<<15)
@@ -883,17 +789,26 @@ struct fcoe_xfrqe {
/*
- * FCoE CONFQ element
+ * fcoe rx doorbell message sent to the chip $$KEEP_ENDIANNESS$$
+ */
+struct b577xx_fcoe_rx_doorbell {
+ struct b577xx_doorbell_hdr hdr;
+ u8 params;
+#define B577XX_FCOE_RX_DOORBELL_NEGATIVE_ARM (0x1F<<0)
+#define B577XX_FCOE_RX_DOORBELL_NEGATIVE_ARM_SHIFT 0
+#define B577XX_FCOE_RX_DOORBELL_OPCODE (0x7<<5)
+#define B577XX_FCOE_RX_DOORBELL_OPCODE_SHIFT 5
+ __le16 doorbell_cq_cons;
+};
+
+
+/*
+ * FCoE CONFQ element $$KEEP_ENDIANNESS$$
*/
struct fcoe_confqe {
-#if defined(__BIG_ENDIAN)
- u16 rx_id;
- u16 ox_id;
-#elif defined(__LITTLE_ENDIAN)
- u16 ox_id;
- u16 rx_id;
-#endif
- u32 param;
+ __le16 ox_id;
+ __le16 rx_id;
+ __le32 param;
};
@@ -914,10 +829,10 @@ struct fcoe_conn_db {
/*
- * FCoE CQ element
+ * FCoE CQ element $$KEEP_ENDIANNESS$$
*/
struct fcoe_cqe {
- u16 wqe;
+ __le16 wqe;
#define FCOE_CQE_CQE_INFO (0x3FFF<<0)
#define FCOE_CQE_CQE_INFO_SHIFT 0
#define FCOE_CQE_CQE_TYPE (0x1<<14)
@@ -928,61 +843,46 @@ struct fcoe_cqe {
/*
- * FCoE error/warning resporting entry
+ * FCoE error/warning reporting entry $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_partial_err_report_entry {
+ __le32 err_warn_bitmap_lo;
+ __le32 err_warn_bitmap_hi;
+ __le32 tx_buf_off;
+ __le32 rx_buf_off;
+};
+
+/*
+ * FCoE error/warning reporting entry $$KEEP_ENDIANNESS$$
*/
struct fcoe_err_report_entry {
- u32 err_warn_bitmap_lo;
- u32 err_warn_bitmap_hi;
- u32 tx_buf_off;
- u32 rx_buf_off;
+ struct fcoe_partial_err_report_entry data;
struct fcoe_fc_hdr fc_hdr;
};
/*
- * FCoE hash table entry (32 bytes)
+ * FCoE hash table entry (32 bytes) $$KEEP_ENDIANNESS$$
*/
struct fcoe_hash_table_entry {
-#if defined(__BIG_ENDIAN)
- u8 d_id_0;
- u8 s_id_2;
- u8 s_id_1;
- u8 s_id_0;
-#elif defined(__LITTLE_ENDIAN)
u8 s_id_0;
u8 s_id_1;
u8 s_id_2;
u8 d_id_0;
-#endif
-#if defined(__BIG_ENDIAN)
- u16 dst_mac_addr_hi;
- u8 d_id_2;
- u8 d_id_1;
-#elif defined(__LITTLE_ENDIAN)
u8 d_id_1;
u8 d_id_2;
- u16 dst_mac_addr_hi;
-#endif
- u32 dst_mac_addr_lo;
-#if defined(__BIG_ENDIAN)
- u16 vlan_id;
- u16 src_mac_addr_hi;
-#elif defined(__LITTLE_ENDIAN)
- u16 src_mac_addr_hi;
- u16 vlan_id;
-#endif
- u32 src_mac_addr_lo;
-#if defined(__BIG_ENDIAN)
- u16 reserved1;
- u8 reserved0;
- u8 vlan_flag;
-#elif defined(__LITTLE_ENDIAN)
+ __le16 dst_mac_addr_hi;
+ __le16 dst_mac_addr_mid;
+ __le16 dst_mac_addr_lo;
+ __le16 src_mac_addr_hi;
+ __le16 vlan_id;
+ __le16 src_mac_addr_lo;
+ __le16 src_mac_addr_mid;
u8 vlan_flag;
u8 reserved0;
- u16 reserved1;
-#endif
- u32 reserved2;
- u32 field_id;
+ __le16 reserved1;
+ __le32 reserved2;
+ __le32 field_id;
#define FCOE_HASH_TABLE_ENTRY_CID (0xFFFFFF<<0)
#define FCOE_HASH_TABLE_ENTRY_CID_SHIFT 0
#define FCOE_HASH_TABLE_ENTRY_RESERVED3 (0x7F<<24)
@@ -991,11 +891,27 @@ struct fcoe_hash_table_entry {
#define FCOE_HASH_TABLE_ENTRY_VALID_SHIFT 31
};
+
/*
- * FCoE pending work request CQE
+ * FCoE LCQ element $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_lcqe {
+ __le32 wqe;
+#define FCOE_LCQE_TASK_ID (0xFFFF<<0)
+#define FCOE_LCQE_TASK_ID_SHIFT 0
+#define FCOE_LCQE_LCQE_TYPE (0xFF<<16)
+#define FCOE_LCQE_LCQE_TYPE_SHIFT 16
+#define FCOE_LCQE_RESERVED (0xFF<<24)
+#define FCOE_LCQE_RESERVED_SHIFT 24
+};
+
+
+
+/*
+ * FCoE pending work request CQE $$KEEP_ENDIANNESS$$
*/
struct fcoe_pend_wq_cqe {
- u16 wqe;
+ __le16 wqe;
#define FCOE_PEND_WQ_CQE_TASK_ID (0x3FFF<<0)
#define FCOE_PEND_WQ_CQE_TASK_ID_SHIFT 0
#define FCOE_PEND_WQ_CQE_CQE_TYPE (0x1<<14)
@@ -1006,53 +922,61 @@ struct fcoe_pend_wq_cqe {
/*
- * FCoE RX statistics parameters section#0
+ * FCoE RX statistics parameters section#0 $$KEEP_ENDIANNESS$$
*/
struct fcoe_rx_stat_params_section0 {
- u32 fcoe_ver_cnt;
- u32 fcoe_rx_pkt_cnt;
- u32 fcoe_rx_byte_cnt;
- u32 fcoe_rx_drop_pkt_cnt;
+ __le32 fcoe_rx_pkt_cnt;
+ __le32 fcoe_rx_byte_cnt;
};
/*
- * FCoE RX statistics parameters section#1
+ * FCoE RX statistics parameters section#1 $$KEEP_ENDIANNESS$$
*/
struct fcoe_rx_stat_params_section1 {
- u32 fc_crc_cnt;
- u32 eofa_del_cnt;
- u32 miss_frame_cnt;
- u32 seq_timeout_cnt;
- u32 drop_seq_cnt;
- u32 fcoe_rx_drop_pkt_cnt;
- u32 fcp_rx_pkt_cnt;
- u32 reserved0;
+ __le32 fcoe_ver_cnt;
+ __le32 fcoe_rx_drop_pkt_cnt;
+};
+
+
+/*
+ * FCoE RX statistics parameters section#2 $$KEEP_ENDIANNESS$$
+ */
+struct fcoe_rx_stat_params_section2 {
+ __le32 fc_crc_cnt;
+ __le32 eofa_del_cnt;
+ __le32 miss_frame_cnt;
+ __le32 seq_timeout_cnt;
+ __le32 drop_seq_cnt;
+ __le32 fcoe_rx_drop_pkt_cnt;
+ __le32 fcp_rx_pkt_cnt;
+ __le32 reserved0;
};
/*
- * FCoE TX statistics parameters
+ * FCoE TX statistics parameters $$KEEP_ENDIANNESS$$
*/
struct fcoe_tx_stat_params {
- u32 fcoe_tx_pkt_cnt;
- u32 fcoe_tx_byte_cnt;
- u32 fcp_tx_pkt_cnt;
- u32 reserved0;
+ __le32 fcoe_tx_pkt_cnt;
+ __le32 fcoe_tx_byte_cnt;
+ __le32 fcp_tx_pkt_cnt;
+ __le32 reserved0;
};
/*
- * FCoE statistics parameters
+ * FCoE statistics parameters $$KEEP_ENDIANNESS$$
*/
struct fcoe_statistics_params {
struct fcoe_tx_stat_params tx_stat;
struct fcoe_rx_stat_params_section0 rx_stat0;
struct fcoe_rx_stat_params_section1 rx_stat1;
+ struct fcoe_rx_stat_params_section2 rx_stat2;
};
/*
- * FCoE t2 hash table entry (64 bytes)
+ * FCoE t2 hash table entry (64 bytes) $$KEEP_ENDIANNESS$$
*/
struct fcoe_t2_hash_table_entry {
struct fcoe_hash_table_entry data;
@@ -1060,11 +984,13 @@ struct fcoe_t2_hash_table_entry {
struct regpair reserved0[3];
};
+
+
/*
- * FCoE unsolicited CQE
+ * FCoE unsolicited CQE $$KEEP_ENDIANNESS$$
*/
struct fcoe_unsolicited_cqe {
- u16 wqe;
+ __le16 wqe;
#define FCOE_UNSOLICITED_CQE_SUBTYPE (0x3<<0)
#define FCOE_UNSOLICITED_CQE_SUBTYPE_SHIFT 0
#define FCOE_UNSOLICITED_CQE_PKT_LEN (0xFFF<<2)
@@ -1075,6 +1001,4 @@ struct fcoe_unsolicited_cqe {
#define FCOE_UNSOLICITED_CQE_TOGGLE_BIT_SHIFT 15
};
-
-
#endif /* __57XX_FCOE_HSI_LINUX_LE__ */
diff --git a/drivers/scsi/bnx2fc/Kconfig b/drivers/scsi/bnx2fc/Kconfig
index 6a38080e35ed..3cf7e08df809 100644
--- a/drivers/scsi/bnx2fc/Kconfig
+++ b/drivers/scsi/bnx2fc/Kconfig
@@ -1,11 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SCSI_BNX2X_FCOE
- tristate "Broadcom NetXtreme II FCoE support"
+ tristate "QLogic FCoE offload support"
depends on PCI
+ depends on (IPV6 || IPV6=n)
+ depends on LIBFC
+ depends on LIBFCOE
select NETDEVICES
- select NETDEV_1000
- select LIBFC
- select LIBFCOE
+ select ETHERNET
+ select NET_VENDOR_BROADCOM
select CNIC
- ---help---
- This driver supports FCoE offload for the Broadcom NetXtreme II
- devices.
+ help
+ This driver supports FCoE offload for the QLogic devices.
diff --git a/drivers/scsi/bnx2fc/Makefile b/drivers/scsi/bnx2fc/Makefile
index a92695a25176..1d72e279a97d 100644
--- a/drivers/scsi/bnx2fc/Makefile
+++ b/drivers/scsi/bnx2fc/Makefile
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SCSI_BNX2X_FCOE) += bnx2fc.o
-bnx2fc-y := bnx2fc_els.o bnx2fc_fcoe.o bnx2fc_hwi.o bnx2fc_io.o bnx2fc_tgt.o
+bnx2fc-y := bnx2fc_els.o bnx2fc_fcoe.o bnx2fc_hwi.o bnx2fc_io.o bnx2fc_tgt.o \
+ bnx2fc_debug.o
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h
index 0a404bfb44fe..6d47a4d8eed6 100644
--- a/drivers/scsi/bnx2fc/bnx2fc.h
+++ b/drivers/scsi/bnx2fc/bnx2fc.h
@@ -1,8 +1,8 @@
-#ifndef _BNX2FC_H_
-#define _BNX2FC_H_
-/* bnx2fc.h: Broadcom NetXtreme II Linux FCoE offload driver.
+/* bnx2fc.h: QLogic Linux FCoE offload driver.
*
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,6 +11,10 @@
* Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
+#ifndef _BNX2FC_H_
+#define _BNX2FC_H_
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -36,7 +40,7 @@
#include <linux/bitops.h>
#include <linux/log2.h>
#include <linux/interrupt.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/io.h>
#include <scsi/scsi.h>
@@ -47,7 +51,6 @@
#include <scsi/scsi_tcq.h>
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
-#include <scsi/fc_encode.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fip.h>
@@ -57,20 +60,23 @@
#include <scsi/fc/fc_fcp.h>
#include "57xx_hsi_bnx2fc.h"
-#include "bnx2fc_debug.h"
-#include "../../net/cnic_if.h"
+#include "../../net/ethernet/broadcom/cnic_if.h"
+#include "../../net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h"
#include "bnx2fc_constants.h"
#define BNX2FC_NAME "bnx2fc"
-#define BNX2FC_VERSION "1.0.1"
+#define BNX2FC_VERSION "2.12.13"
#define PFX "bnx2fc: "
+#define BCM_CHIP_LEN 16
+
#define BNX2X_DOORBELL_PCI_BAR 2
#define BNX2FC_MAX_BD_LEN 0xffff
-#define BNX2FC_BD_SPLIT_SZ 0x8000
-#define BNX2FC_MAX_BDS_PER_CMD 256
+#define BNX2FC_BD_SPLIT_SZ 0xffff
+#define BNX2FC_MAX_BDS_PER_CMD 255
+#define BNX2FC_FW_MAX_BDS_PER_CMD 255
#define BNX2FC_SQ_WQES_MAX 256
@@ -81,12 +87,11 @@
#define BNX2FC_RQ_WQES_MAX 16
#define BNX2FC_CQ_WQES_MAX (BNX2FC_SQ_WQES_MAX + BNX2FC_RQ_WQES_MAX)
-#define BNX2FC_NUM_MAX_SESS 128
+#define BNX2FC_NUM_MAX_SESS 1024
#define BNX2FC_NUM_MAX_SESS_LOG (ilog2(BNX2FC_NUM_MAX_SESS))
-#define BNX2FC_MAX_OUTSTANDING_CMNDS 2048
-#define BNX2FC_CAN_QUEUE BNX2FC_MAX_OUTSTANDING_CMNDS
-#define BNX2FC_ELSTM_XIDS BNX2FC_CAN_QUEUE
+#define BNX2FC_MAX_NPIV 256
+
#define BNX2FC_MIN_PAYLOAD 256
#define BNX2FC_MAX_PAYLOAD 2048
#define BNX2FC_MFS \
@@ -102,27 +107,25 @@
#define BNX2FC_RQ_WQE_SIZE (BNX2FC_RQ_BUF_SZ)
#define BNX2FC_XFERQ_WQE_SIZE (sizeof(struct fcoe_xfrqe))
#define BNX2FC_CONFQ_WQE_SIZE (sizeof(struct fcoe_confqe))
-#define BNX2FC_5771X_DB_PAGE_SIZE 128
+#define BNX2X_DB_SHIFT 3
-#define BNX2FC_MAX_TASKS \
- (BNX2FC_MAX_OUTSTANDING_CMNDS + BNX2FC_ELSTM_XIDS)
#define BNX2FC_TASK_SIZE 128
#define BNX2FC_TASKS_PER_PAGE (PAGE_SIZE/BNX2FC_TASK_SIZE)
-#define BNX2FC_TASK_CTX_ARR_SZ (BNX2FC_MAX_TASKS/BNX2FC_TASKS_PER_PAGE)
#define BNX2FC_MAX_ROWS_IN_HASH_TBL 8
#define BNX2FC_HASH_TBL_CHUNK_SIZE (16 * 1024)
#define BNX2FC_MAX_SEQS 255
+#define BNX2FC_MAX_RETRY_CNT 3
+#define BNX2FC_MAX_RPORT_RETRY_CNT 255
#define BNX2FC_READ (1 << 1)
#define BNX2FC_WRITE (1 << 0)
#define BNX2FC_MIN_XID 0
-#define BNX2FC_MAX_XID \
- (BNX2FC_MAX_OUTSTANDING_CMNDS + BNX2FC_ELSTM_XIDS - 1)
-#define FCOE_MIN_XID (BNX2FC_MAX_XID + 1)
-#define FCOE_MAX_XID (FCOE_MIN_XID + 4095)
+#define FCOE_MAX_NUM_XIDS 0x2000
+#define FCOE_MAX_XID_OFFSET (FCOE_MAX_NUM_XIDS - 1)
+#define FCOE_XIDS_PER_CPU_OFFSET ((512 * nr_cpu_ids) - 1)
#define BNX2FC_MAX_LUN 0xFFFF
#define BNX2FC_MAX_FCP_TGT 256
#define BNX2FC_MAX_CMD_LEN 16
@@ -134,13 +137,30 @@
#define BNX2FC_FW_TIMEOUT (3 * HZ)
#define PORT_MAX 2
-#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
-
/* FC FCP Status */
#define FC_GOOD 0
#define BNX2FC_RNID_HBA 0x7
+#define SRR_RETRY_COUNT 5
+#define REC_RETRY_COUNT 1
+#define BNX2FC_NUM_ERR_BITS 63
+
+#define BNX2FC_RELOGIN_WAIT_TIME 200
+#define BNX2FC_RELOGIN_WAIT_CNT 10
+
+#define BNX2FC_STATS(hba, stat, cnt) \
+ do { \
+ u32 val; \
+ \
+ val = fw_stats->stat.cnt; \
+ if (hba->prev_stats.stat.cnt <= val) \
+ val -= hba->prev_stats.stat.cnt; \
+ else \
+ val += (0xfffffff - hba->prev_stats.stat.cnt); \
+ hba->bfw_stats.cnt += val; \
+ } while (0)
+
/* bnx2fc driver uses only one instance of fcoe_percpu_s */
extern struct fcoe_percpu_s bnx2fc_global;
@@ -152,36 +172,41 @@ struct bnx2fc_percpu_s {
spinlock_t fp_work_lock;
};
+struct bnx2fc_fw_stats {
+ u64 fc_crc_cnt;
+ u64 fcoe_tx_pkt_cnt;
+ u64 fcoe_rx_pkt_cnt;
+ u64 fcoe_tx_byte_cnt;
+ u64 fcoe_rx_byte_cnt;
+};
struct bnx2fc_hba {
- struct list_head link;
+ struct list_head list;
struct cnic_dev *cnic;
struct pci_dev *pcidev;
- struct net_device *netdev;
struct net_device *phys_dev;
unsigned long reg_with_cnic;
#define BNX2FC_CNIC_REGISTERED 1
- struct packet_type fcoe_packet_type;
- struct packet_type fip_packet_type;
struct bnx2fc_cmd_mgr *cmd_mgr;
- struct workqueue_struct *timer_work_queue;
- struct kref kref;
spinlock_t hba_lock;
struct mutex hba_mutex;
+ struct mutex hba_stats_mutex;
unsigned long adapter_state;
#define ADAPTER_STATE_UP 0
#define ADAPTER_STATE_GOING_DOWN 1
#define ADAPTER_STATE_LINK_DOWN 2
#define ADAPTER_STATE_READY 3
- u32 flags;
- unsigned long init_done;
- #define BNX2FC_FW_INIT_DONE 0
- #define BNX2FC_CTLR_INIT_DONE 1
- #define BNX2FC_CREATE_DONE 2
- struct fcoe_ctlr ctlr;
- u8 vlan_enabled;
- int vlan_id;
+ unsigned long flags;
+ #define BNX2FC_FLAG_FW_INIT_DONE 0
+ #define BNX2FC_FLAG_DESTROY_CMPL 1
u32 next_conn_id;
+
+ /* xid resources */
+ u16 max_xid;
+ u32 max_tasks;
+ u32 max_outstanding_cmds;
+ u32 elstm_xids;
+
struct fcoe_task_ctx_entry **task_ctx;
dma_addr_t *task_ctx_dma;
struct regpair *task_ctx_bd_tbl;
@@ -199,38 +224,56 @@ struct bnx2fc_hba {
char *dummy_buffer;
dma_addr_t dummy_buf_dma;
+ /* Active list of offloaded sessions */
+ struct bnx2fc_rport **tgt_ofld_list;
+
+ /* statistics */
+ struct bnx2fc_fw_stats bfw_stats;
+ struct fcoe_statistics_params prev_stats;
struct fcoe_statistics_params *stats_buffer;
dma_addr_t stats_buf_dma;
-
- /*
- * PCI related info.
- */
- u16 pci_did;
- u16 pci_vid;
- u16 pci_sdid;
- u16 pci_svid;
- u16 pci_func;
- u16 pci_devno;
-
- struct task_struct *l2_thread;
-
- /* linkdown handling */
- wait_queue_head_t shutdown_wait;
- int wait_for_link_down;
+ struct completion stat_req_done;
+ struct fcoe_capabilities fcoe_cap;
/*destroy handling */
struct timer_list destroy_timer;
wait_queue_head_t destroy_wait;
- /* Active list of offloaded sessions */
- struct bnx2fc_rport *tgt_ofld_list[BNX2FC_NUM_MAX_SESS];
+ /* linkdown handling */
+ wait_queue_head_t shutdown_wait;
+ int wait_for_link_down;
int num_ofld_sess;
+ struct list_head vports;
- /* statistics */
- struct completion stat_req_done;
+ char chip_num[BCM_CHIP_LEN];
+};
+
+struct bnx2fc_interface {
+ struct list_head list;
+ unsigned long if_flags;
+ #define BNX2FC_CTLR_INIT_DONE 0
+ struct bnx2fc_hba *hba;
+ struct net_device *netdev;
+ struct packet_type fcoe_packet_type;
+ struct packet_type fip_packet_type;
+ struct workqueue_struct *timer_work_queue;
+ struct kref kref;
+ u8 vlan_enabled;
+ int vlan_id;
+ bool enabled;
+ u8 tm_timeout;
};
-#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_hba, ctlr)
+#define bnx2fc_from_ctlr(x) \
+ ((struct bnx2fc_interface *)((x) + 1))
+
+#define bnx2fc_to_ctlr(x) \
+ ((struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1))
+
+struct bnx2fc_lport {
+ struct list_head list;
+ struct fc_lport *lport;
+};
struct bnx2fc_cmd_mgr {
struct bnx2fc_hba *hba;
@@ -247,9 +290,11 @@ struct bnx2fc_rport {
struct fc_rport_priv *rdata;
void __iomem *ctx_base;
#define DPM_TRIGER_TYPE 0x40
+ u32 io_timeout;
u32 fcoe_conn_id;
u32 context_id;
u32 sid;
+ int dev_type;
unsigned long flags;
#define BNX2FC_FLAG_SESSION_READY 0x1
@@ -257,14 +302,19 @@ struct bnx2fc_rport {
#define BNX2FC_FLAG_DISABLED 0x3
#define BNX2FC_FLAG_DESTROYED 0x4
#define BNX2FC_FLAG_OFLD_REQ_CMPL 0x5
-#define BNX2FC_FLAG_DESTROY_CMPL 0x6
-#define BNX2FC_FLAG_CTX_ALLOC_FAILURE 0x7
-#define BNX2FC_FLAG_UPLD_REQ_COMPL 0x8
-#define BNX2FC_FLAG_EXPL_LOGO 0x9
+#define BNX2FC_FLAG_CTX_ALLOC_FAILURE 0x6
+#define BNX2FC_FLAG_UPLD_REQ_COMPL 0x7
+#define BNX2FC_FLAG_DISABLE_FAILED 0x9
+#define BNX2FC_FLAG_ENABLED 0xa
+ u8 src_addr[ETH_ALEN];
u32 max_sqes;
u32 max_rqes;
u32 max_cqes;
+ atomic_t free_sqes;
+
+ struct b577xx_doorbell_set_prod sq_db;
+ struct b577xx_fcoe_rx_doorbell rx_db;
struct fcoe_sqe *sq;
dma_addr_t sq_dma;
@@ -274,7 +324,7 @@ struct bnx2fc_rport {
struct fcoe_cqe *cq;
dma_addr_t cq_dma;
- u32 cq_cons_idx;
+ u16 cq_cons_idx;
u8 cq_curr_toggle_bit;
u32 cq_mem_size;
@@ -308,21 +358,13 @@ struct bnx2fc_rport {
dma_addr_t lcq_dma;
u32 lcq_mem_size;
- void *ofld_req[4];
- dma_addr_t ofld_req_dma[4];
- void *enbl_req;
- dma_addr_t enbl_req_dma;
-
spinlock_t tgt_lock;
spinlock_t cq_lock;
atomic_t num_active_ios;
u32 flush_in_prog;
- unsigned long work_time_slice;
unsigned long timestamp;
- struct list_head free_task_list;
+ unsigned long retry_delay_timestamp;
struct bnx2fc_cmd *pending_queue[BNX2FC_SQ_WQES_MAX+1];
- atomic_t pi;
- atomic_t ci;
struct list_head active_cmd_queue;
struct list_head els_queue;
struct list_head io_retire_queue;
@@ -336,6 +378,7 @@ struct bnx2fc_rport {
};
struct bnx2fc_mp_req {
+ u64 tm_lun;
u8 tm_flags;
u32 req_len;
@@ -357,6 +400,8 @@ struct bnx2fc_els_cb_arg {
struct bnx2fc_cmd *aborted_io_req;
struct bnx2fc_cmd *io_req;
u16 l2_oxid;
+ u32 offset;
+ enum fc_rctl r_ctl;
};
/* bnx2fc command structure */
@@ -370,6 +415,7 @@ struct bnx2fc_cmd {
#define BNX2FC_ABTS 3
#define BNX2FC_ELS 4
#define BNX2FC_CLEANUP 5
+#define BNX2FC_SEQ_CLEANUP 6
u8 io_req_flags;
struct kref refcount;
struct fcoe_port *port;
@@ -380,9 +426,12 @@ struct bnx2fc_cmd {
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg);
struct bnx2fc_els_cb_arg *cb_arg;
struct delayed_work timeout_work; /* timer for ULP timeouts */
- struct completion tm_done;
- int wait_for_comp;
+ struct completion abts_done;
+ struct completion cleanup_done;
+ int wait_for_abts_comp;
+ int wait_for_cleanup_comp;
u16 xid;
+ struct fcoe_err_report_entry err_entry;
struct fcoe_task_ctx_entry *task;
struct io_bdt *bd_tbl;
struct fcp_rsp *rsp;
@@ -399,6 +448,13 @@ struct bnx2fc_cmd {
#define BNX2FC_FLAG_IO_COMPL 0x9
#define BNX2FC_FLAG_ELS_DONE 0xa
#define BNX2FC_FLAG_ELS_TIMEOUT 0xb
+#define BNX2FC_FLAG_CMD_LOST 0xc
+#define BNX2FC_FLAG_SRR_SENT 0xd
+#define BNX2FC_FLAG_ISSUE_CLEANUP_REQ 0xe
+ u8 rec_retry;
+ u8 srr_retry;
+ u32 srr_offset;
+ u8 srr_rctl;
u32 fcp_resid;
u32 fcp_rsp_len;
u32 fcp_sns_len;
@@ -418,16 +474,28 @@ struct io_bdt {
struct bnx2fc_work {
struct list_head list;
struct bnx2fc_rport *tgt;
+ struct fcoe_task_ctx_entry *task;
+ unsigned char rq_data[BNX2FC_RQ_BUF_SZ];
u16 wqe;
+ u8 num_rq;
};
struct bnx2fc_unsol_els {
struct fc_lport *lport;
struct fc_frame *fp;
+ struct bnx2fc_hba *hba;
struct work_struct unsol_els_work;
};
+struct bnx2fc_priv {
+ struct bnx2fc_cmd *io_req;
+};
+static inline struct bnx2fc_priv *bnx2fc_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt);
struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type);
void bnx2fc_cmd_release(struct kref *ref);
int bnx2fc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd);
@@ -435,6 +503,8 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba);
int bnx2fc_send_fw_fcoe_destroy_msg(struct bnx2fc_hba *hba);
int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt);
+int bnx2fc_send_session_enable_req(struct fcoe_port *port,
+ struct bnx2fc_rport *tgt);
int bnx2fc_send_session_disable_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt);
int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba,
@@ -446,8 +516,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba);
void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba);
int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba);
void bnx2fc_free_fw_resc(struct bnx2fc_hba *hba);
-struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba,
- u16 min_xid, u16 max_xid);
+struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba);
void bnx2fc_cmd_mgr_free(struct bnx2fc_cmd_mgr *cmgr);
void bnx2fc_get_link_state(struct bnx2fc_hba *hba);
char *bnx2fc_get_next_rqe(struct bnx2fc_rport *tgt, u8 num_items);
@@ -465,6 +534,10 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req);
void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u16 orig_xid);
+void bnx2fc_init_seq_cleanup_task(struct bnx2fc_cmd *seq_clnup_req,
+ struct fcoe_task_ctx_entry *task,
+ struct bnx2fc_cmd *orig_io_req,
+ u32 offset);
void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task);
void bnx2fc_init_task(struct bnx2fc_cmd *io_req,
@@ -472,7 +545,6 @@ void bnx2fc_init_task(struct bnx2fc_cmd *io_req,
void bnx2fc_add_2_sq(struct bnx2fc_rport *tgt, u16 xid);
void bnx2fc_ring_doorbell(struct bnx2fc_rport *tgt);
int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd);
-int bnx2fc_eh_host_reset(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_target_reset(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd);
void bnx2fc_rport_event_handler(struct fc_lport *lport,
@@ -480,7 +552,7 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
enum fc_rport_event event);
void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
- u8 num_rq);
+ u8 num_rq, unsigned char *rq_data);
void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
@@ -489,7 +561,7 @@ void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
u8 num_rq);
void bnx2fc_process_tm_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
- u8 num_rq);
+ u8 num_rq, unsigned char *rq_data);
void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
@@ -505,13 +577,27 @@ struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
struct fc_frame *,
void *),
void *arg, u32 timeout);
+void bnx2fc_arm_cq(struct bnx2fc_rport *tgt);
int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt);
-void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe);
+void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe,
+ unsigned char *rq_data, u8 num_rq,
+ struct fcoe_task_ctx_entry *task);
struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
u32 port_id);
void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
unsigned char *buf,
u32 frame_len, u16 l2_oxid);
int bnx2fc_send_stat_req(struct bnx2fc_hba *hba);
+int bnx2fc_post_io_req(struct bnx2fc_rport *tgt, struct bnx2fc_cmd *io_req);
+int bnx2fc_send_rec(struct bnx2fc_cmd *orig_io_req);
+int bnx2fc_send_srr(struct bnx2fc_cmd *orig_io_req, u32 offset, u8 r_ctl);
+void bnx2fc_process_seq_cleanup_compl(struct bnx2fc_cmd *seq_clnup_req,
+ struct fcoe_task_ctx_entry *task,
+ u8 rx_state);
+int bnx2fc_initiate_seq_cleanup(struct bnx2fc_cmd *orig_io_req, u32 offset,
+ enum fc_rctl r_ctl);
+
+
+#include "bnx2fc_debug.h"
#endif
diff --git a/drivers/scsi/bnx2fc/bnx2fc_constants.h b/drivers/scsi/bnx2fc/bnx2fc_constants.h
index fe7769173c43..9ed150307a39 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_constants.h
+++ b/drivers/scsi/bnx2fc/bnx2fc_constants.h
@@ -1,3 +1,17 @@
+/* bnx2fc_constants.h: QLogic Linux FCoE offload driver.
+ * Handles operations such as session offload/upload etc, and manages
+ * session resources such as connection id and qp resources.
+ *
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ */
+
#ifndef __BNX2FC_CONSTANTS_H_
#define __BNX2FC_CONSTANTS_H_
@@ -5,6 +19,12 @@
* This file defines HSI constants for the FCoE flows
*/
+/* Current FCoE HSI version number composed of two fields (16 bit) */
+/* Implies on a change broken previous HSI */
+#define FCOE_HSI_MAJOR_VERSION (2)
+/* Implies on a change which does not broken previous HSI */
+#define FCOE_HSI_MINOR_VERSION (1)
+
/* KWQ/KCQ FCoE layer code */
#define FCOE_KWQE_LAYER_CODE (7)
@@ -40,21 +60,63 @@
#define FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x3)
#define FCOE_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x4)
#define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5)
+#define FCOE_KCQE_COMPLETION_STATUS_WRONG_HSI_VERSION (0x6)
+#define FCOE_KCQE_COMPLETION_STATUS_PARITY_ERROR (0x81)
+
+/* CQE type */
+#define FCOE_PENDING_CQE_TYPE 0
+#define FCOE_UNSOLIC_CQE_TYPE 1
/* Unsolicited CQE type */
#define FCOE_UNSOLICITED_FRAME_CQE_TYPE 0
#define FCOE_ERROR_DETECTION_CQE_TYPE 1
#define FCOE_WARNING_DETECTION_CQE_TYPE 2
+/* E_D_TOV timer resolution in ms */
+#define FCOE_E_D_TOV_TIMER_RESOLUTION_MS (20)
+
+/* E_D_TOV timer resolution for SDM (4 micro) */
+#define FCOE_E_D_TOV_SDM_TIMER_RESOLUTION \
+ (FCOE_E_D_TOV_TIMER_RESOLUTION_MS * 1000 / 4)
+
+/* REC timer resolution in ms */
+#define FCOE_REC_TIMER_RESOLUTION_MS (20)
+
+/* REC timer resolution for SDM (4 micro) */
+#define FCOE_REC_SDM_TIMER_RESOLUTION (FCOE_REC_TIMER_RESOLUTION_MS * 1000 / 4)
+
+/* E_D_TOV timer default wraparound value (2 sec) in 20 ms resolution */
+#define FCOE_E_D_TOV_DEFAULT_WRAPAROUND_VAL \
+ (2000 / FCOE_E_D_TOV_TIMER_RESOLUTION_MS)
+
+/* REC_TOV timer default wraparound value (3 sec) in 20 ms resolution */
+#define FCOE_REC_TOV_DEFAULT_WRAPAROUND_VAL \
+ (3000 / FCOE_REC_TIMER_RESOLUTION_MS)
+
+#define FCOE_NUM_OF_TIMER_TASKS (8 * 1024)
+
+#define FCOE_NUM_OF_CACHED_TASKS_TIMER (8)
+
/* Task context constants */
+/******** Remove FCP_CMD write tce sleep ***********************/
+/* In case timer services are required then shall be updated by Xstorm after
+ * start processing the task. In case no timer facilities are required then the
+ * driver would initialize the state to this value
+ *
+#define FCOE_TASK_TX_STATE_NORMAL 0
+ * After driver has initialize the task in case timer services required *
+#define FCOE_TASK_TX_STATE_INIT 1
+******** Remove FCP_CMD write tce sleep ***********************/
/* After driver has initialize the task in case timer services required */
#define FCOE_TASK_TX_STATE_INIT 0
/* In case timer services are required then shall be updated by Xstorm after
* start processing the task. In case no timer facilities are required then the
- * driver would initialize the state to this value */
+ * driver would initialize the state to this value
+ */
#define FCOE_TASK_TX_STATE_NORMAL 1
/* Task is under abort procedure. Updated in order to stop processing of
- * pending WQEs on this task */
+ * pending WQEs on this task
+ */
#define FCOE_TASK_TX_STATE_ABORT 2
/* For E_D_T_TOV timer expiration in Xstorm (Class 2 only) */
#define FCOE_TASK_TX_STATE_ERROR 3
@@ -66,17 +128,8 @@
#define FCOE_TASK_TX_STATE_EXCHANGE_CLEANUP 6
/* For sequence cleanup request task */
#define FCOE_TASK_TX_STATE_SEQUENCE_CLEANUP 7
-/* Mark task as aborted and indicate that ABTS was not transmitted */
-#define FCOE_TASK_TX_STATE_BEFORE_ABTS_TX 8
-/* Mark task as aborted and indicate that ABTS was transmitted */
-#define FCOE_TASK_TX_STATE_AFTER_ABTS_TX 9
/* For completion the ABTS task. */
-#define FCOE_TASK_TX_STATE_ABTS_TX_COMPLETED 10
-/* Mark task as aborted and indicate that Exchange cleanup was not transmitted
- */
-#define FCOE_TASK_TX_STATE_BEFORE_EXCHANGE_CLEANUP_TX 11
-/* Mark task as aborted and indicate that Exchange cleanup was transmitted */
-#define FCOE_TASK_TX_STATE_AFTER_EXCHANGE_CLEANUP_TX 12
+#define FCOE_TASK_TX_STATE_ABTS_TX 8
#define FCOE_TASK_RX_STATE_NORMAL 0
#define FCOE_TASK_RX_STATE_COMPLETED 1
@@ -86,25 +139,25 @@
#define FCOE_TASK_RX_STATE_WARNING 3
/* For E_D_T_TOV timer expiration in Ustorm */
#define FCOE_TASK_RX_STATE_ERROR 4
-/* ABTS ACC arrived wait for local completion to finally complete the task. */
-#define FCOE_TASK_RX_STATE_ABTS_ACC_ARRIVED 5
-/* local completion arrived wait for ABTS ACC to finally complete the task. */
-#define FCOE_TASK_RX_STATE_ABTS_LOCAL_COMP_ARRIVED 6
+/* FW only: First visit at rx-path, part of the abts round trip */
+#define FCOE_TASK_RX_STATE_ABTS_IN_PROCESS 5
+/* FW only: Second visit at rx-path, after ABTS frame transmitted */
+#define FCOE_TASK_RX_STATE_ABTS_TRANSMITTED 6
/* Special completion indication in case of task was aborted. */
#define FCOE_TASK_RX_STATE_ABTS_COMPLETED 7
-/* Special completion indication in case of task was cleaned. */
-#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_COMPLETED 8
-/* Special completion indication (in task requested the exchange cleanup) in
- * case cleaned task is in non-valid. */
-#define FCOE_TASK_RX_STATE_ABORT_CLEANUP_COMPLETED 9
+/* FW only: First visit at rx-path, part of the cleanup round trip */
+#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_IN_PROCESS 8
+/* FW only: Special completion indication in case of task was cleaned. */
+#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_COMPLETED 9
+/* Not in used: Special completion indication (in task requested the exchange
+ * cleanup) in case cleaned task is in non-valid.
+ */
+#define FCOE_TASK_RX_STATE_ABORT_CLEANUP_COMPLETED 10
/* Special completion indication (in task requested the sequence cleanup) in
- * case cleaned task was already returned to normal. */
-#define FCOE_TASK_RX_STATE_IGNORED_SEQUENCE_CLEANUP 10
-/* Exchange cleanup arrived wait until xfer will be handled to finally
- * complete the task. */
-#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_ARRIVED 11
-/* Xfer handled, wait for exchange cleanup to finally complete the task. */
-#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_HANDLED_XFER 12
+ * case cleaned task was already returned to normal.
+ */
+#define FCOE_TASK_RX_STATE_IGNORED_SEQUENCE_CLEANUP 11
+
#define FCOE_TASK_TYPE_WRITE 0
#define FCOE_TASK_TYPE_READ 1
@@ -120,11 +173,40 @@
#define FCOE_TASK_CLASS_TYPE_3 0
#define FCOE_TASK_CLASS_TYPE_2 1
+/* FCoE/FC packet fields */
+#define FCOE_ETH_TYPE 0x8906
+
+/* FCoE maximum elements in hash table */
+#define FCOE_MAX_ELEMENTS_IN_HASH_TABLE_ROW 8
+
+/* FCoE half of the elements in hash table */
+#define FCOE_HALF_ELEMENTS_IN_HASH_TABLE_ROW \
+ (FCOE_MAX_ELEMENTS_IN_HASH_TABLE_ROW / 2)
+
+/* FcoE number of cached T2 entries */
+#define T_FCOE_NUMBER_OF_CACHED_T2_ENTRIES (4)
+
+/* FCoE maximum elements in hash table */
+#define FCOE_HASH_TBL_CHUNK_SIZE 16384
+
/* Everest FCoE connection type */
#define B577XX_FCOE_CONNECTION_TYPE 4
-/* Error codes for Error Reporting in fast path flows */
-/* XFER error codes */
+/* FCoE number of rows (in log). This number derives
+ * from the maximum connections supported which is 2048.
+ * TBA: Need a different constant for E2
+ */
+#define FCOE_MAX_NUM_SESSIONS_LOG 11
+
+#define FC_ABTS_REPLY_MAX_PAYLOAD_LEN 12
+
+/* Error codes for Error Reporting in slow path flows */
+#define FCOE_SLOW_PATH_ERROR_CODE_TOO_MANY_FUNCS 0
+#define FCOE_SLOW_PATH_ERROR_CODE_NO_LICENSE 1
+
+/* Error codes for Error Reporting in fast path flows
+ * XFER error codes
+ */
#define FCOE_ERROR_CODE_XFER_OOO_RO 0
#define FCOE_ERROR_CODE_XFER_RO_NOT_ALIGNED 1
#define FCOE_ERROR_CODE_XFER_NULL_BURST_LEN 2
@@ -155,17 +237,17 @@
#define FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET 23
#define FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET 24
#define FCOE_ERROR_CODE_DATA_EOFN_END_SEQ_SET 25
-#define FCOE_ERROR_CODE_DATA_EOFT_END_SEQ_RESET 26
-#define FCOE_ERROR_CODE_DATA_TASK_TYPE_NOT_READ 27
+#define FCOE_ERROR_CODE_DATA_EOFT_END_SEQ_RESET 26
+#define FCOE_ERROR_CODE_DATA_TASK_TYPE_NOT_READ 27
#define FCOE_ERROR_CODE_DATA_FCTL 28
/* Middle path error codes */
-#define FCOE_ERROR_CODE_MIDPATH_TYPE_NOT_ELS 29
+#define FCOE_ERROR_CODE_MIDPATH_INVALID_TYPE 29
#define FCOE_ERROR_CODE_MIDPATH_SOFI3_SEQ_ACTIVE_SET 30
#define FCOE_ERROR_CODE_MIDPATH_SOFN_SEQ_ACTIVE_RESET 31
#define FCOE_ERROR_CODE_MIDPATH_EOFN_END_SEQ_SET 32
#define FCOE_ERROR_CODE_MIDPATH_EOFT_END_SEQ_RESET 33
-#define FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_FCTL 34
+#define FCOE_ERROR_CODE_MIDPATH_REPLY_FCTL 34
#define FCOE_ERROR_CODE_MIDPATH_INVALID_REPLY 35
#define FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_RCTL 36
@@ -173,7 +255,7 @@
#define FCOE_ERROR_CODE_ABTS_REPLY_F_CTL 37
#define FCOE_ERROR_CODE_ABTS_REPLY_DDF_RCTL_FIELD 38
#define FCOE_ERROR_CODE_ABTS_REPLY_INVALID_BLS_RCTL 39
-#define FCOE_ERROR_CODE_ABTS_REPLY_INVALID_RCTL 40
+#define FCOE_ERROR_CODE_ABTS_REPLY_INVALID_RCTL 40
#define FCOE_ERROR_CODE_ABTS_REPLY_RCTL_GENERAL_MISMATCH 41
/* Common error codes */
@@ -185,7 +267,7 @@
#define FCOE_ERROR_CODE_COMMON_DATA_NO_MORE_SGES 47
#define FCOE_ERROR_CODE_COMMON_OPTIONAL_FC_HDR 48
#define FCOE_ERROR_CODE_COMMON_READ_TCE_OX_ID_TOO_BIG 49
-#define FCOE_ERROR_CODE_COMMON_DATA_WAS_NOT_TRANSMITTED 50
+#define FCOE_ERROR_CODE_COMMON_DATA_WAS_NOT_TRANSMITTED 50
/* Unsolicited Rx error codes */
#define FCOE_ERROR_CODE_UNSOLICITED_TYPE_NOT_ELS 51
diff --git a/drivers/scsi/bnx2fc/bnx2fc_debug.c b/drivers/scsi/bnx2fc/bnx2fc_debug.c
new file mode 100644
index 000000000000..47ba3ba1e03b
--- /dev/null
+++ b/drivers/scsi/bnx2fc/bnx2fc_debug.c
@@ -0,0 +1,84 @@
+/* bnx2fc_debug.c: QLogic Linux FCoE offload driver.
+ * Handles operations such as session offload/upload etc, and manages
+ * session resources such as connection id and qp resources.
+ *
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#include "bnx2fc.h"
+
+void BNX2FC_IO_DBG(const struct bnx2fc_cmd *io_req, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (likely(!(bnx2fc_debug_level & LOG_IO)))
+ return;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (io_req && io_req->port && io_req->port->lport &&
+ io_req->port->lport->host)
+ shost_printk(KERN_INFO, io_req->port->lport->host,
+ PFX "xid:0x%x %pV",
+ io_req->xid, &vaf);
+ else
+ pr_info("NULL %pV", &vaf);
+
+ va_end(args);
+}
+
+void BNX2FC_TGT_DBG(const struct bnx2fc_rport *tgt, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (likely(!(bnx2fc_debug_level & LOG_TGT)))
+ return;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (tgt && tgt->port && tgt->port->lport && tgt->port->lport->host &&
+ tgt->rport)
+ shost_printk(KERN_INFO, tgt->port->lport->host,
+ PFX "port:%x %pV",
+ tgt->rport->port_id, &vaf);
+ else
+ pr_info("NULL %pV", &vaf);
+
+ va_end(args);
+}
+
+void BNX2FC_HBA_DBG(const struct fc_lport *lport, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (likely(!(bnx2fc_debug_level & LOG_HBA)))
+ return;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (lport && lport->host)
+ shost_printk(KERN_INFO, lport->host, PFX "%pV", &vaf);
+ else
+ pr_info("NULL %pV", &vaf);
+
+ va_end(args);
+}
diff --git a/drivers/scsi/bnx2fc/bnx2fc_debug.h b/drivers/scsi/bnx2fc/bnx2fc_debug.h
index 7f6aff68cc53..76717acee3ab 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_debug.h
+++ b/drivers/scsi/bnx2fc/bnx2fc_debug.h
@@ -1,3 +1,17 @@
+/* bnx2fc_debug.h: QLogic Linux FCoE offload driver.
+ * Handles operations such as session offload/upload etc, and manages
+ * session resources such as connection id and qp resources.
+ *
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ */
+
#ifndef __BNX2FC_DEBUG__
#define __BNX2FC_DEBUG__
@@ -11,60 +25,23 @@
extern unsigned int bnx2fc_debug_level;
-#define BNX2FC_CHK_LOGGING(LEVEL, CMD) \
- do { \
- if (unlikely(bnx2fc_debug_level & LEVEL)) \
- do { \
- CMD; \
- } while (0); \
- } while (0)
-
-#define BNX2FC_ELS_DBG(fmt, arg...) \
- BNX2FC_CHK_LOGGING(LOG_ELS, \
- printk(KERN_ALERT PFX fmt, ##arg))
-
-#define BNX2FC_MISC_DBG(fmt, arg...) \
- BNX2FC_CHK_LOGGING(LOG_MISC, \
- printk(KERN_ALERT PFX fmt, ##arg))
-
-#define BNX2FC_IO_DBG(io_req, fmt, arg...) \
- do { \
- if (!io_req || !io_req->port || !io_req->port->lport || \
- !io_req->port->lport->host) \
- BNX2FC_CHK_LOGGING(LOG_IO, \
- printk(KERN_ALERT PFX "NULL " fmt, ##arg)); \
- else \
- BNX2FC_CHK_LOGGING(LOG_IO, \
- shost_printk(KERN_ALERT, \
- (io_req)->port->lport->host, \
- PFX "xid:0x%x " fmt, \
- (io_req)->xid, ##arg)); \
- } while (0)
-
-#define BNX2FC_TGT_DBG(tgt, fmt, arg...) \
- do { \
- if (!tgt || !tgt->port || !tgt->port->lport || \
- !tgt->port->lport->host || !tgt->rport) \
- BNX2FC_CHK_LOGGING(LOG_TGT, \
- printk(KERN_ALERT PFX "NULL " fmt, ##arg)); \
- else \
- BNX2FC_CHK_LOGGING(LOG_TGT, \
- shost_printk(KERN_ALERT, \
- (tgt)->port->lport->host, \
- PFX "port:%x " fmt, \
- (tgt)->rport->port_id, ##arg)); \
- } while (0)
-
-
-#define BNX2FC_HBA_DBG(lport, fmt, arg...) \
- do { \
- if (!lport || !lport->host) \
- BNX2FC_CHK_LOGGING(LOG_HBA, \
- printk(KERN_ALERT PFX "NULL " fmt, ##arg)); \
- else \
- BNX2FC_CHK_LOGGING(LOG_HBA, \
- shost_printk(KERN_ALERT, lport->host, \
- PFX fmt, ##arg)); \
- } while (0)
+#define BNX2FC_ELS_DBG(fmt, ...) \
+do { \
+ if (unlikely(bnx2fc_debug_level & LOG_ELS)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define BNX2FC_MISC_DBG(fmt, ...) \
+do { \
+ if (unlikely(bnx2fc_debug_level & LOG_MISC)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+__printf(2, 3)
+void BNX2FC_IO_DBG(const struct bnx2fc_cmd *io_req, const char *fmt, ...);
+__printf(2, 3)
+void BNX2FC_TGT_DBG(const struct bnx2fc_rport *tgt, const char *fmt, ...);
+__printf(2, 3)
+void BNX2FC_HBA_DBG(const struct fc_lport *lport, const char *fmt, ...);
#endif
diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c
index 52c358427ce2..754f2e82d955 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_els.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_els.c
@@ -1,9 +1,11 @@
/*
- * bnx2fc_els.c: Broadcom NetXtreme II Linux FCoE offload driver.
+ * bnx2fc_els.c: QLogic Linux FCoE offload driver.
* This file contains helper routines that handle ELS requests
* and responses.
*
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -60,13 +62,20 @@ int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req)
struct fc_els_rrq rrq;
struct bnx2fc_rport *tgt = aborted_io_req->tgt;
- struct fc_lport *lport = tgt->rdata->local_port;
+ struct fc_lport *lport = NULL;
struct bnx2fc_els_cb_arg *cb_arg = NULL;
- u32 sid = tgt->sid;
- u32 r_a_tov = lport->r_a_tov;
+ u32 sid = 0;
+ u32 r_a_tov = 0;
unsigned long start = jiffies;
int rc;
+ if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags))
+ return -EINVAL;
+
+ lport = tgt->rdata->local_port;
+ sid = tgt->sid;
+ r_a_tov = lport->r_a_tov;
+
BNX2FC_ELS_DBG("Sending RRQ orig_xid = 0x%x\n",
aborted_io_req->xid);
memset(&rrq, 0, sizeof(rrq));
@@ -83,7 +92,7 @@ int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req)
rrq.rrq_cmd = ELS_RRQ;
hton24(rrq.rrq_s_id, sid);
rrq.rrq_ox_id = htons(aborted_io_req->xid);
- rrq.rrq_rx_id = htons(aborted_io_req->task->rx_wr_tx_rd.rx_id);
+ rrq.rrq_rx_id = htons(aborted_io_req->task->rxwr_txrd.var_ctx.rx_id);
retry_rrq:
rc = bnx2fc_initiate_els(tgt, ELS_RRQ, &rrq, sizeof(rrq),
@@ -253,13 +262,418 @@ int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp)
return rc;
}
+static void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg)
+{
+ struct bnx2fc_mp_req *mp_req;
+ struct fc_frame_header *fc_hdr, *fh;
+ struct bnx2fc_cmd *srr_req;
+ struct bnx2fc_cmd *orig_io_req;
+ struct fc_frame *fp;
+ unsigned char *buf;
+ void *resp_buf;
+ u32 resp_len, hdr_len;
+ u8 opcode;
+ int rc = 0;
+
+ orig_io_req = cb_arg->aborted_io_req;
+ srr_req = cb_arg->io_req;
+ if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &srr_req->req_flags)) {
+ /* SRR timedout */
+ BNX2FC_IO_DBG(srr_req, "srr timed out, abort "
+ "orig_io - 0x%x\n",
+ orig_io_req->xid);
+ rc = bnx2fc_initiate_abts(srr_req);
+ if (rc != SUCCESS) {
+ BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts "
+ "failed. issue cleanup\n");
+ bnx2fc_initiate_cleanup(srr_req);
+ }
+ if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) ||
+ test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) {
+ BNX2FC_IO_DBG(srr_req, "srr_compl:xid 0x%x flags = %lx",
+ orig_io_req->xid, orig_io_req->req_flags);
+ goto srr_compl_done;
+ }
+ orig_io_req->srr_retry++;
+ if (orig_io_req->srr_retry <= SRR_RETRY_COUNT) {
+ struct bnx2fc_rport *tgt = orig_io_req->tgt;
+ spin_unlock_bh(&tgt->tgt_lock);
+ rc = bnx2fc_send_srr(orig_io_req,
+ orig_io_req->srr_offset,
+ orig_io_req->srr_rctl);
+ spin_lock_bh(&tgt->tgt_lock);
+ if (!rc)
+ goto srr_compl_done;
+ }
+
+ rc = bnx2fc_initiate_abts(orig_io_req);
+ if (rc != SUCCESS) {
+ BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts "
+ "failed xid = 0x%x. issue cleanup\n",
+ orig_io_req->xid);
+ bnx2fc_initiate_cleanup(orig_io_req);
+ }
+ goto srr_compl_done;
+ }
+ if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) ||
+ test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) {
+ BNX2FC_IO_DBG(srr_req, "srr_compl:xid - 0x%x flags = %lx",
+ orig_io_req->xid, orig_io_req->req_flags);
+ goto srr_compl_done;
+ }
+ mp_req = &(srr_req->mp_req);
+ fc_hdr = &(mp_req->resp_fc_hdr);
+ resp_len = mp_req->resp_len;
+ resp_buf = mp_req->resp_buf;
+
+ hdr_len = sizeof(*fc_hdr);
+ buf = kzalloc(PAGE_SIZE, GFP_ATOMIC);
+ if (!buf) {
+ printk(KERN_ERR PFX "srr buf: mem alloc failure\n");
+ goto srr_compl_done;
+ }
+ memcpy(buf, fc_hdr, hdr_len);
+ memcpy(buf + hdr_len, resp_buf, resp_len);
+
+ fp = fc_frame_alloc(NULL, resp_len);
+ if (!fp) {
+ printk(KERN_ERR PFX "fc_frame_alloc failure\n");
+ goto free_buf;
+ }
+
+ fh = (struct fc_frame_header *) fc_frame_header_get(fp);
+ /* Copy FC Frame header and payload into the frame */
+ memcpy(fh, buf, hdr_len + resp_len);
+
+ opcode = fc_frame_payload_op(fp);
+ switch (opcode) {
+ case ELS_LS_ACC:
+ BNX2FC_IO_DBG(srr_req, "SRR success\n");
+ break;
+ case ELS_LS_RJT:
+ BNX2FC_IO_DBG(srr_req, "SRR rejected\n");
+ rc = bnx2fc_initiate_abts(orig_io_req);
+ if (rc != SUCCESS) {
+ BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts "
+ "failed xid = 0x%x. issue cleanup\n",
+ orig_io_req->xid);
+ bnx2fc_initiate_cleanup(orig_io_req);
+ }
+ break;
+ default:
+ BNX2FC_IO_DBG(srr_req, "srr compl - invalid opcode = %d\n",
+ opcode);
+ break;
+ }
+ fc_frame_free(fp);
+free_buf:
+ kfree(buf);
+srr_compl_done:
+ kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+}
+
+static void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg)
+{
+ struct bnx2fc_cmd *orig_io_req, *new_io_req;
+ struct bnx2fc_cmd *rec_req;
+ struct bnx2fc_mp_req *mp_req;
+ struct fc_frame_header *fc_hdr, *fh;
+ struct fc_els_ls_rjt *rjt;
+ struct fc_els_rec_acc *acc;
+ struct bnx2fc_rport *tgt;
+ struct fcoe_err_report_entry *err_entry;
+ struct scsi_cmnd *sc_cmd;
+ enum fc_rctl r_ctl;
+ unsigned char *buf;
+ void *resp_buf;
+ struct fc_frame *fp;
+ u8 opcode;
+ u32 offset;
+ u32 e_stat;
+ u32 resp_len, hdr_len;
+ int rc = 0;
+ bool send_seq_clnp = false;
+ bool abort_io = false;
+
+ BNX2FC_MISC_DBG("Entered rec_compl callback\n");
+ rec_req = cb_arg->io_req;
+ orig_io_req = cb_arg->aborted_io_req;
+ BNX2FC_IO_DBG(rec_req, "rec_compl: orig xid = 0x%x", orig_io_req->xid);
+ tgt = orig_io_req->tgt;
+
+ /* Handle REC timeout case */
+ if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rec_req->req_flags)) {
+ BNX2FC_IO_DBG(rec_req, "timed out, abort "
+ "orig_io - 0x%x\n",
+ orig_io_req->xid);
+ /* els req is timed out. send abts for els */
+ rc = bnx2fc_initiate_abts(rec_req);
+ if (rc != SUCCESS) {
+ BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts "
+ "failed. issue cleanup\n");
+ bnx2fc_initiate_cleanup(rec_req);
+ }
+ orig_io_req->rec_retry++;
+ /* REC timedout. send ABTS to the orig IO req */
+ if (orig_io_req->rec_retry <= REC_RETRY_COUNT) {
+ spin_unlock_bh(&tgt->tgt_lock);
+ rc = bnx2fc_send_rec(orig_io_req);
+ spin_lock_bh(&tgt->tgt_lock);
+ if (!rc)
+ goto rec_compl_done;
+ }
+ rc = bnx2fc_initiate_abts(orig_io_req);
+ if (rc != SUCCESS) {
+ BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts "
+ "failed xid = 0x%x. issue cleanup\n",
+ orig_io_req->xid);
+ bnx2fc_initiate_cleanup(orig_io_req);
+ }
+ goto rec_compl_done;
+ }
+
+ if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) {
+ BNX2FC_IO_DBG(rec_req, "completed"
+ "orig_io - 0x%x\n",
+ orig_io_req->xid);
+ goto rec_compl_done;
+ }
+ if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) {
+ BNX2FC_IO_DBG(rec_req, "abts in prog "
+ "orig_io - 0x%x\n",
+ orig_io_req->xid);
+ goto rec_compl_done;
+ }
+
+ mp_req = &(rec_req->mp_req);
+ fc_hdr = &(mp_req->resp_fc_hdr);
+ resp_len = mp_req->resp_len;
+ acc = resp_buf = mp_req->resp_buf;
+
+ hdr_len = sizeof(*fc_hdr);
+
+ buf = kzalloc(PAGE_SIZE, GFP_ATOMIC);
+ if (!buf) {
+ printk(KERN_ERR PFX "rec buf: mem alloc failure\n");
+ goto rec_compl_done;
+ }
+ memcpy(buf, fc_hdr, hdr_len);
+ memcpy(buf + hdr_len, resp_buf, resp_len);
+
+ fp = fc_frame_alloc(NULL, resp_len);
+ if (!fp) {
+ printk(KERN_ERR PFX "fc_frame_alloc failure\n");
+ goto free_buf;
+ }
+
+ fh = (struct fc_frame_header *) fc_frame_header_get(fp);
+ /* Copy FC Frame header and payload into the frame */
+ memcpy(fh, buf, hdr_len + resp_len);
+
+ opcode = fc_frame_payload_op(fp);
+ if (opcode == ELS_LS_RJT) {
+ BNX2FC_IO_DBG(rec_req, "opcode is RJT\n");
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ if ((rjt->er_reason == ELS_RJT_LOGIC ||
+ rjt->er_reason == ELS_RJT_UNAB) &&
+ rjt->er_explan == ELS_EXPL_OXID_RXID) {
+ BNX2FC_IO_DBG(rec_req, "handle CMD LOST case\n");
+ new_io_req = bnx2fc_cmd_alloc(tgt);
+ if (!new_io_req)
+ goto abort_io;
+ new_io_req->sc_cmd = orig_io_req->sc_cmd;
+ /* cleanup orig_io_req that is with the FW */
+ set_bit(BNX2FC_FLAG_CMD_LOST,
+ &orig_io_req->req_flags);
+ bnx2fc_initiate_cleanup(orig_io_req);
+ /* Post a new IO req with the same sc_cmd */
+ BNX2FC_IO_DBG(rec_req, "Post IO request again\n");
+ rc = bnx2fc_post_io_req(tgt, new_io_req);
+ if (!rc)
+ goto free_frame;
+ BNX2FC_IO_DBG(rec_req, "REC: io post err\n");
+ }
+abort_io:
+ rc = bnx2fc_initiate_abts(orig_io_req);
+ if (rc != SUCCESS) {
+ BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts "
+ "failed. issue cleanup\n");
+ bnx2fc_initiate_cleanup(orig_io_req);
+ }
+ } else if (opcode == ELS_LS_ACC) {
+ /* REVISIT: Check if the exchange is already aborted */
+ offset = ntohl(acc->reca_fc4value);
+ e_stat = ntohl(acc->reca_e_stat);
+ if (e_stat & ESB_ST_SEQ_INIT) {
+ BNX2FC_IO_DBG(rec_req, "target has the seq init\n");
+ goto free_frame;
+ }
+ BNX2FC_IO_DBG(rec_req, "e_stat = 0x%x, offset = 0x%x\n",
+ e_stat, offset);
+ /* Seq initiative is with us */
+ err_entry = (struct fcoe_err_report_entry *)
+ &orig_io_req->err_entry;
+ sc_cmd = orig_io_req->sc_cmd;
+ if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ /* SCSI WRITE command */
+ if (offset == orig_io_req->data_xfer_len) {
+ BNX2FC_IO_DBG(rec_req, "WRITE - resp lost\n");
+ /* FCP_RSP lost */
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ offset = 0;
+ } else {
+ /* start transmitting from offset */
+ BNX2FC_IO_DBG(rec_req, "XFER_RDY/DATA lost\n");
+ send_seq_clnp = true;
+ r_ctl = FC_RCTL_DD_DATA_DESC;
+ if (bnx2fc_initiate_seq_cleanup(orig_io_req,
+ offset, r_ctl))
+ abort_io = true;
+ /* XFER_RDY */
+ }
+ } else {
+ /* SCSI READ command */
+ if (err_entry->data.rx_buf_off ==
+ orig_io_req->data_xfer_len) {
+ /* FCP_RSP lost */
+ BNX2FC_IO_DBG(rec_req, "READ - resp lost\n");
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ offset = 0;
+ } else {
+ /* request retransmission from this offset */
+ send_seq_clnp = true;
+ offset = err_entry->data.rx_buf_off;
+ BNX2FC_IO_DBG(rec_req, "RD DATA lost\n");
+ /* FCP_DATA lost */
+ r_ctl = FC_RCTL_DD_SOL_DATA;
+ if (bnx2fc_initiate_seq_cleanup(orig_io_req,
+ offset, r_ctl))
+ abort_io = true;
+ }
+ }
+ if (abort_io) {
+ rc = bnx2fc_initiate_abts(orig_io_req);
+ if (rc != SUCCESS) {
+ BNX2FC_IO_DBG(rec_req, "rec_compl:initiate_abts"
+ " failed. issue cleanup\n");
+ bnx2fc_initiate_cleanup(orig_io_req);
+ }
+ } else if (!send_seq_clnp) {
+ BNX2FC_IO_DBG(rec_req, "Send SRR - FCP_RSP\n");
+ spin_unlock_bh(&tgt->tgt_lock);
+ rc = bnx2fc_send_srr(orig_io_req, offset, r_ctl);
+ spin_lock_bh(&tgt->tgt_lock);
+
+ if (rc) {
+ BNX2FC_IO_DBG(rec_req, "Unable to send SRR"
+ " IO will abort\n");
+ }
+ }
+ }
+free_frame:
+ fc_frame_free(fp);
+free_buf:
+ kfree(buf);
+rec_compl_done:
+ kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+ kfree(cb_arg);
+}
+
+int bnx2fc_send_rec(struct bnx2fc_cmd *orig_io_req)
+{
+ struct fc_els_rec rec;
+ struct bnx2fc_rport *tgt = orig_io_req->tgt;
+ struct fc_lport *lport = tgt->rdata->local_port;
+ struct bnx2fc_els_cb_arg *cb_arg = NULL;
+ u32 sid = tgt->sid;
+ u32 r_a_tov = lport->r_a_tov;
+ int rc;
+
+ BNX2FC_IO_DBG(orig_io_req, "Sending REC\n");
+ memset(&rec, 0, sizeof(rec));
+
+ cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
+ if (!cb_arg) {
+ printk(KERN_ERR PFX "Unable to allocate cb_arg for REC\n");
+ rc = -ENOMEM;
+ goto rec_err;
+ }
+ kref_get(&orig_io_req->refcount);
+
+ cb_arg->aborted_io_req = orig_io_req;
+
+ rec.rec_cmd = ELS_REC;
+ hton24(rec.rec_s_id, sid);
+ rec.rec_ox_id = htons(orig_io_req->xid);
+ rec.rec_rx_id = htons(orig_io_req->task->rxwr_txrd.var_ctx.rx_id);
+
+ rc = bnx2fc_initiate_els(tgt, ELS_REC, &rec, sizeof(rec),
+ bnx2fc_rec_compl, cb_arg,
+ r_a_tov);
+ if (rc) {
+ BNX2FC_IO_DBG(orig_io_req, "REC failed - release\n");
+ spin_lock_bh(&tgt->tgt_lock);
+ kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+ spin_unlock_bh(&tgt->tgt_lock);
+ kfree(cb_arg);
+ }
+rec_err:
+ return rc;
+}
+
+int bnx2fc_send_srr(struct bnx2fc_cmd *orig_io_req, u32 offset, u8 r_ctl)
+{
+ struct fcp_srr srr;
+ struct bnx2fc_rport *tgt = orig_io_req->tgt;
+ struct fc_lport *lport = tgt->rdata->local_port;
+ struct bnx2fc_els_cb_arg *cb_arg = NULL;
+ u32 r_a_tov = lport->r_a_tov;
+ int rc;
+
+ BNX2FC_IO_DBG(orig_io_req, "Sending SRR\n");
+ memset(&srr, 0, sizeof(srr));
+
+ cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
+ if (!cb_arg) {
+ printk(KERN_ERR PFX "Unable to allocate cb_arg for SRR\n");
+ rc = -ENOMEM;
+ goto srr_err;
+ }
+ kref_get(&orig_io_req->refcount);
+
+ cb_arg->aborted_io_req = orig_io_req;
+
+ srr.srr_op = ELS_SRR;
+ srr.srr_ox_id = htons(orig_io_req->xid);
+ srr.srr_rx_id = htons(orig_io_req->task->rxwr_txrd.var_ctx.rx_id);
+ srr.srr_rel_off = htonl(offset);
+ srr.srr_r_ctl = r_ctl;
+ orig_io_req->srr_offset = offset;
+ orig_io_req->srr_rctl = r_ctl;
+
+ rc = bnx2fc_initiate_els(tgt, ELS_SRR, &srr, sizeof(srr),
+ bnx2fc_srr_compl, cb_arg,
+ r_a_tov);
+ if (rc) {
+ BNX2FC_IO_DBG(orig_io_req, "SRR failed - release\n");
+ spin_lock_bh(&tgt->tgt_lock);
+ kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+ spin_unlock_bh(&tgt->tgt_lock);
+ kfree(cb_arg);
+ } else
+ set_bit(BNX2FC_FLAG_SRR_SENT, &orig_io_req->req_flags);
+
+srr_err:
+ return rc;
+}
+
static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
void *data, u32 data_len,
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg),
struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec)
{
struct fcoe_port *port = tgt->port;
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
struct fc_rport *rport = tgt->rport;
struct fc_lport *lport = port->lport;
struct bnx2fc_cmd *els_req;
@@ -274,17 +688,16 @@ static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
rc = fc_remote_port_chkready(rport);
if (rc) {
- printk(KERN_ALERT PFX "els 0x%x: rport not ready\n", op);
+ printk(KERN_ERR PFX "els 0x%x: rport not ready\n", op);
rc = -EINVAL;
goto els_err;
}
if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
- printk(KERN_ALERT PFX "els 0x%x: link is not ready\n", op);
+ printk(KERN_ERR PFX "els 0x%x: link is not ready\n", op);
rc = -EINVAL;
goto els_err;
}
- if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) ||
- (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags))) {
+ if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags))) {
printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op);
rc = -EINVAL;
goto els_err;
@@ -301,11 +714,12 @@ static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
els_req->cb_func = cb_func;
cb_arg->io_req = els_req;
els_req->cb_arg = cb_arg;
+ els_req->data_xfer_len = data_len;
mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req);
rc = bnx2fc_init_mp_req(els_req);
if (rc == FAILED) {
- printk(KERN_ALERT PFX "ELS MP request init failed\n");
+ printk(KERN_ERR PFX "ELS MP request init failed\n");
spin_lock_bh(&tgt->tgt_lock);
kref_put(&els_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
@@ -324,7 +738,7 @@ static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) {
memcpy(mp_req->req_buf, data, data_len);
} else {
- printk(KERN_ALERT PFX "Invalid ELS op 0x%x\n", op);
+ printk(KERN_ERR PFX "Invalid ELS op 0x%x\n", op);
els_req->cb_func = NULL;
els_req->cb_arg = NULL;
spin_lock_bh(&tgt->tgt_lock);
@@ -342,9 +756,14 @@ static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
did = tgt->rport->port_id;
sid = tgt->sid;
- __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid,
- FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ |
- FC_FC_SEQ_INIT, 0);
+ if (op == ELS_SRR)
+ __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS4_REQ, did, sid,
+ FC_TYPE_FCP, FC_FC_FIRST_SEQ |
+ FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+ else
+ __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid,
+ FC_TYPE_ELS, FC_FC_FIRST_SEQ |
+ FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
/* Obtain exchange id */
xid = els_req->xid;
@@ -352,7 +771,8 @@ static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
index = xid % BNX2FC_TASKS_PER_PAGE;
/* Initialize task context for this IO request */
- task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+ task_page = (struct fcoe_task_ctx_entry *)
+ interface->hba->task_ctx[task_idx];
task = &(task_page[index]);
bnx2fc_init_mp_task(els_req, task);
@@ -417,12 +837,13 @@ void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req,
hdr = (u64 *)fc_hdr;
temp_hdr = (u64 *)
- &task->cmn.general.cmd_info.mp_fc_frame.fc_hdr;
+ &task->rxwr_only.union_ctx.comp_info.mp_rsp.fc_hdr;
hdr[0] = cpu_to_be64(temp_hdr[0]);
hdr[1] = cpu_to_be64(temp_hdr[1]);
hdr[2] = cpu_to_be64(temp_hdr[2]);
- mp_req->resp_len = task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_off;
+ mp_req->resp_len =
+ task->rxwr_only.union_ctx.comp_info.mp_rsp.mp_payload_len;
/* Parse ELS response */
if ((els_req->cb_func) && (els_req->cb_arg)) {
@@ -433,43 +854,57 @@ void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req,
kref_put(&els_req->refcount, bnx2fc_cmd_release);
}
+#define BNX2FC_FCOE_MAC_METHOD_GRANGED_MAC 1
+#define BNX2FC_FCOE_MAC_METHOD_FCF_MAP 2
+#define BNX2FC_FCOE_MAC_METHOD_FCOE_SET_MAC 3
static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg)
{
struct fcoe_ctlr *fip = arg;
struct fc_exch *exch = fc_seq_exch(seq);
struct fc_lport *lport = exch->lp;
- u8 *mac;
+
struct fc_frame_header *fh;
- u8 op;
+ u8 *granted_mac;
+ u8 fcoe_mac[6];
+ u8 fc_map[3];
+ int method;
if (IS_ERR(fp))
goto done;
- mac = fr_cb(fp)->granted_mac;
- if (is_zero_ether_addr(mac)) {
- fh = fc_frame_header_get(fp);
- if (fh->fh_type != FC_TYPE_ELS) {
- printk(KERN_ERR PFX "bnx2fc_flogi_resp:"
- "fh_type != FC_TYPE_ELS\n");
- fc_frame_free(fp);
- return;
- }
- op = fc_frame_payload_op(fp);
- if (lport->vport) {
- if (op == ELS_LS_RJT) {
- printk(KERN_ERR PFX "bnx2fc_flogi_resp is LS_RJT\n");
- fc_vport_terminate(lport->vport);
- fc_frame_free(fp);
- return;
- }
- }
- if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
- fc_frame_free(fp);
- return;
- }
+ fh = fc_frame_header_get(fp);
+ granted_mac = fr_cb(fp)->granted_mac;
+
+ /*
+ * We set the source MAC for FCoE traffic based on the Granted MAC
+ * address from the switch.
+ *
+ * If granted_mac is non-zero, we use that.
+ * If the granted_mac is zeroed out, create the FCoE MAC based on
+ * the sel_fcf->fc_map and the d_id fo the FLOGI frame.
+ * If sel_fcf->fc_map is 0, then we use the default FCF-MAC plus the
+ * d_id of the FLOGI frame.
+ */
+ if (!is_zero_ether_addr(granted_mac)) {
+ ether_addr_copy(fcoe_mac, granted_mac);
+ method = BNX2FC_FCOE_MAC_METHOD_GRANGED_MAC;
+ } else if (fip->sel_fcf && fip->sel_fcf->fc_map != 0) {
+ hton24(fc_map, fip->sel_fcf->fc_map);
+ fcoe_mac[0] = fc_map[0];
+ fcoe_mac[1] = fc_map[1];
+ fcoe_mac[2] = fc_map[2];
+ fcoe_mac[3] = fh->fh_d_id[0];
+ fcoe_mac[4] = fh->fh_d_id[1];
+ fcoe_mac[5] = fh->fh_d_id[2];
+ method = BNX2FC_FCOE_MAC_METHOD_FCF_MAP;
+ } else {
+ fc_fcoe_set_mac(fcoe_mac, fh->fh_d_id);
+ method = BNX2FC_FCOE_MAC_METHOD_FCOE_SET_MAC;
}
- fip->update_mac(lport, mac);
+
+ BNX2FC_HBA_DBG(lport, "fcoe_mac=%pM method=%d\n", fcoe_mac, method);
+ fip->update_mac(lport, fcoe_mac);
done:
fc_lport_flogi_resp(seq, fp, lport);
}
@@ -495,8 +930,8 @@ struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
void *arg, u32 timeout)
{
struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
- struct fcoe_ctlr *fip = &hba->ctlr;
+ struct bnx2fc_interface *interface = port->priv;
+ struct fcoe_ctlr *fip = bnx2fc_to_ctlr(interface);
struct fc_frame_header *fh = fc_frame_header_get(fp);
switch (op) {
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index ab255fbc7f36..0f68739d380a 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -1,9 +1,11 @@
-/* bnx2fc_fcoe.c: Broadcom NetXtreme II Linux FCoE offload driver.
+/* bnx2fc_fcoe.c: QLogic Linux FCoE offload driver.
* This file contains the code that interacts with libfc, libfcoe,
* cnic modules to create FCoE instances, send/receive non-offloaded
* FIP/FCoE packets, listen to link events etc.
*
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,23 +16,26 @@
#include "bnx2fc.h"
+#include <linux/ethtool.h>
+
static struct list_head adapter_list;
+static struct list_head if_list;
static u32 adapter_count;
static DEFINE_MUTEX(bnx2fc_dev_lock);
DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu);
#define DRV_MODULE_NAME "bnx2fc"
#define DRV_MODULE_VERSION BNX2FC_VERSION
-#define DRV_MODULE_RELDATE "Mar 17, 2011"
+#define DRV_MODULE_RELDATE "October 15, 2015"
-static char version[] __devinitdata =
- "Broadcom NetXtreme II FCoE Driver " DRV_MODULE_NAME \
+static char version[] =
+ "QLogic FCoE Driver " DRV_MODULE_NAME \
" v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Bhanu Prakash Gollapudi <bprakash@broadcom.com>");
-MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710 FCoE Driver");
+MODULE_DESCRIPTION("QLogic FCoE Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
@@ -47,53 +52,99 @@ struct workqueue_struct *bnx2fc_wq;
* Here the io threads are per cpu but the l2 thread is just one
*/
struct fcoe_percpu_s bnx2fc_global;
-DEFINE_SPINLOCK(bnx2fc_global_lock);
+static DEFINE_SPINLOCK(bnx2fc_global_lock);
static struct cnic_ulp_ops bnx2fc_cnic_cb;
static struct libfc_function_template bnx2fc_libfc_fcn_templ;
static struct scsi_host_template bnx2fc_shost_template;
static struct fc_function_template bnx2fc_transport_function;
+static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ;
static struct fc_function_template bnx2fc_vport_xport_function;
-static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode);
+static int bnx2fc_create(struct net_device *netdev, enum fip_mode fip_mode);
+static void __bnx2fc_destroy(struct bnx2fc_interface *interface);
static int bnx2fc_destroy(struct net_device *net_device);
static int bnx2fc_enable(struct net_device *netdev);
static int bnx2fc_disable(struct net_device *netdev);
+/* fcoe_syfs control interface handlers */
+static int bnx2fc_ctlr_alloc(struct net_device *netdev);
+static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev);
+
static void bnx2fc_recv_frame(struct sk_buff *skb);
-static void bnx2fc_start_disc(struct bnx2fc_hba *hba);
+static void bnx2fc_start_disc(struct bnx2fc_interface *interface);
static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev);
-static int bnx2fc_net_config(struct fc_lport *lp);
static int bnx2fc_lport_config(struct fc_lport *lport);
-static int bnx2fc_em_config(struct fc_lport *lport);
+static int bnx2fc_em_config(struct fc_lport *lport, struct bnx2fc_hba *hba);
static int bnx2fc_bind_adapter_devices(struct bnx2fc_hba *hba);
static void bnx2fc_unbind_adapter_devices(struct bnx2fc_hba *hba);
static int bnx2fc_bind_pcidev(struct bnx2fc_hba *hba);
static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba);
-static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
+static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
struct device *parent, int npiv);
-static void bnx2fc_destroy_work(struct work_struct *work);
+static void bnx2fc_port_destroy(struct fcoe_port *port);
static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device *phys_dev);
+static struct bnx2fc_interface *bnx2fc_interface_lookup(struct net_device
+ *phys_dev);
+static inline void bnx2fc_interface_put(struct bnx2fc_interface *interface);
static struct bnx2fc_hba *bnx2fc_find_hba_for_cnic(struct cnic_dev *cnic);
static int bnx2fc_fw_init(struct bnx2fc_hba *hba);
static void bnx2fc_fw_destroy(struct bnx2fc_hba *hba);
static void bnx2fc_port_shutdown(struct fc_lport *lport);
-static void bnx2fc_stop(struct bnx2fc_hba *hba);
+static void bnx2fc_stop(struct bnx2fc_interface *interface);
static int __init bnx2fc_mod_init(void);
static void __exit bnx2fc_mod_exit(void);
unsigned int bnx2fc_debug_level;
module_param_named(debug_logging, bnx2fc_debug_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging,
+ "Option to enable extended logging,\n"
+ "\t\tDefault is 0 - no logging.\n"
+ "\t\t0x01 - SCSI cmd error, cleanup.\n"
+ "\t\t0x02 - Session setup, cleanup, etc.\n"
+ "\t\t0x04 - lport events, link, mtu, etc.\n"
+ "\t\t0x08 - ELS logs.\n"
+ "\t\t0x10 - fcoe L2 fame related logs.\n"
+ "\t\t0xff - LOG all messages.");
-static int bnx2fc_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu);
-/* notification function for CPU hotplug events */
-static struct notifier_block bnx2fc_cpu_notifier = {
- .notifier_call = bnx2fc_cpu_callback,
-};
+static uint bnx2fc_devloss_tmo;
+module_param_named(devloss_tmo, bnx2fc_devloss_tmo, uint, S_IRUGO);
+MODULE_PARM_DESC(devloss_tmo, " Change devloss_tmo for the remote ports "
+ "attached via bnx2fc.");
+
+static uint bnx2fc_max_luns = BNX2FC_MAX_LUN;
+module_param_named(max_luns, bnx2fc_max_luns, uint, S_IRUGO);
+MODULE_PARM_DESC(max_luns, " Change the default max_lun per SCSI host. Default "
+ "0xffff.");
+
+static uint bnx2fc_queue_depth;
+module_param_named(queue_depth, bnx2fc_queue_depth, uint, S_IRUGO);
+MODULE_PARM_DESC(queue_depth, " Change the default queue depth of SCSI devices "
+ "attached via bnx2fc.");
+
+static uint bnx2fc_log_fka;
+module_param_named(log_fka, bnx2fc_log_fka, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(log_fka, " Print message to kernel log when fcoe is "
+ "initiating a FIP keep alive when debug logging is enabled.");
+
+static inline struct net_device *bnx2fc_netdev(const struct fc_lport *lport)
+{
+ return ((struct bnx2fc_interface *)
+ ((struct fcoe_port *)lport_priv(lport))->priv)->netdev;
+}
+
+static void bnx2fc_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
+{
+ struct fcoe_ctlr_device *ctlr_dev =
+ fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ struct bnx2fc_interface *fcoe = fcoe_ctlr_priv(ctlr);
+
+ fcf_dev->vlan_id = fcoe->vlan_id;
+}
static void bnx2fc_clean_rx_queue(struct fc_lport *lp)
{
@@ -101,15 +152,11 @@ static void bnx2fc_clean_rx_queue(struct fc_lport *lp)
struct fcoe_rcv_info *fr;
struct sk_buff_head *list;
struct sk_buff *skb, *next;
- struct sk_buff *head;
bg = &bnx2fc_global;
spin_lock_bh(&bg->fcoe_rx_list.lock);
list = &bg->fcoe_rx_list;
- head = list->next;
- for (skb = head; skb != (struct sk_buff *)list;
- skb = next) {
- next = skb->next;
+ skb_queue_walk_safe(list, skb, next) {
fr = fcoe_dev_from_skb(skb);
if (fr->fr_dev == lp) {
__skb_unlink(skb, list);
@@ -142,7 +189,8 @@ static void bnx2fc_abort_io(struct fc_lport *lport)
static void bnx2fc_cleanup(struct fc_lport *lport)
{
struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct bnx2fc_rport *tgt;
int i;
@@ -219,18 +267,21 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
struct fcoe_crc_eof *cp;
struct sk_buff *skb;
struct fc_frame_header *fh;
- struct bnx2fc_hba *hba;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
+ struct bnx2fc_hba *hba;
struct fcoe_port *port;
struct fcoe_hdr *hp;
struct bnx2fc_rport *tgt;
- struct fcoe_dev_stats *stats;
u8 sof, eof;
u32 crc;
unsigned int hlen, tlen, elen;
int wlen, rc = 0;
port = (struct fcoe_port *)lport_priv(lport);
- hba = port->priv;
+ interface = port->priv;
+ ctlr = bnx2fc_to_ctlr(interface);
+ hba = interface->hba;
fh = fc_frame_header_get(fp);
@@ -242,12 +293,12 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
}
if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
- if (!hba->ctlr.sel_fcf) {
+ if (!ctlr->sel_fcf) {
BNX2FC_HBA_DBG(lport, "FCF not selected yet!\n");
kfree_skb(skb);
return -EINVAL;
}
- if (fcoe_ctlr_els_send(&hba->ctlr, lport, skb))
+ if (fcoe_ctlr_els_send(ctlr, lport, skb))
return 0;
}
@@ -296,17 +347,16 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
return -ENOMEM;
}
frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
- cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
- + frag->page_offset;
+ cp = kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag);
} else {
- cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+ cp = skb_put(skb, tlen);
}
memset(cp, 0, sizeof(*cp));
cp->fcoe_eof = eof;
cp->fcoe_crc32 = cpu_to_le32(~crc);
if (skb_is_nonlinear(skb)) {
- kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+ kunmap_atomic(cp);
cp = NULL;
}
@@ -316,19 +366,19 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
skb_reset_network_header(skb);
skb->mac_len = elen;
skb->protocol = htons(ETH_P_FCOE);
- skb->dev = hba->netdev;
+ skb->dev = interface->netdev;
/* fill up mac and fcoe headers */
eh = eth_hdr(skb);
eh->h_proto = htons(ETH_P_FCOE);
- if (hba->ctlr.map_dest)
+ if (ctlr->map_dest)
fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
else
/* insert GW address */
- memcpy(eh->h_dest, hba->ctlr.dest_addr, ETH_ALEN);
+ memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
- if (unlikely(hba->ctlr.flogi_oxid != FC_XID_UNKNOWN))
- memcpy(eh->h_source, hba->ctlr.ctl_src_addr, ETH_ALEN);
+ if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
+ memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
else
memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
@@ -348,10 +398,8 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
}
/*update tx stats */
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->TxFrames++;
- stats->TxWords += wlen;
- put_cpu();
+ this_cpu_inc(lport->stats->TxFrames);
+ this_cpu_add(lport->stats->TxWords, wlen);
/* send down to lld */
fr_dev(fp) = lport;
@@ -377,22 +425,27 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype, struct net_device *olddev)
{
struct fc_lport *lport;
- struct bnx2fc_hba *hba;
- struct fc_frame_header *fh;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
struct fcoe_rcv_info *fr;
struct fcoe_percpu_s *bg;
- unsigned short oxid;
- hba = container_of(ptype, struct bnx2fc_hba, fcoe_packet_type);
- lport = hba->ctlr.lp;
+ interface = container_of(ptype, struct bnx2fc_interface,
+ fcoe_packet_type);
+ ctlr = bnx2fc_to_ctlr(interface);
+ lport = ctlr->lp;
if (unlikely(lport == NULL)) {
- printk(KERN_ALERT PFX "bnx2fc_rcv: lport is NULL\n");
+ printk(KERN_ERR PFX "bnx2fc_rcv: lport is NULL\n");
goto err;
}
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return -1;
+
if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
- printk(KERN_ALERT PFX "bnx2fc_rcv: Wrong FC type frame\n");
+ printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n");
goto err;
}
@@ -405,22 +458,18 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
goto err;
skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
- fh = (struct fc_frame_header *) skb_transport_header(skb);
-
- oxid = ntohs(fh->fh_ox_id);
fr = fcoe_dev_from_skb(skb);
fr->fr_dev = lport;
- fr->ptype = ptype;
bg = &bnx2fc_global;
- spin_lock_bh(&bg->fcoe_rx_list.lock);
+ spin_lock(&bg->fcoe_rx_list.lock);
__skb_queue_tail(&bg->fcoe_rx_list, skb);
if (bg->fcoe_rx_list.qlen == 1)
- wake_up_process(bg->thread);
+ wake_up_process(bg->kthread);
- spin_unlock_bh(&bg->fcoe_rx_list.lock);
+ spin_unlock(&bg->fcoe_rx_list.lock);
return 0;
err:
@@ -433,7 +482,7 @@ static int bnx2fc_l2_rcv_thread(void *arg)
struct fcoe_percpu_s *bg = arg;
struct sk_buff *skb;
- set_user_nice(current, -20);
+ set_user_nice(current, MIN_NICE);
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
@@ -453,23 +502,25 @@ static int bnx2fc_l2_rcv_thread(void *arg)
static void bnx2fc_recv_frame(struct sk_buff *skb)
{
- u32 fr_len;
+ u64 crc_err;
+ u32 fr_len, fr_crc;
struct fc_lport *lport;
struct fcoe_rcv_info *fr;
- struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
struct fcoe_crc_eof crc_eof;
struct fc_frame *fp;
struct fc_lport *vn_port;
- struct fcoe_port *port;
+ struct fcoe_port *port, *phys_port;
u8 *mac = NULL;
u8 *dest_mac = NULL;
struct fcoe_hdr *hp;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
fr = fcoe_dev_from_skb(skb);
lport = fr->fr_dev;
if (unlikely(lport == NULL)) {
- printk(KERN_ALERT PFX "Invalid lport struct\n");
+ printk(KERN_ERR PFX "Invalid lport struct\n");
kfree_skb(skb);
return;
}
@@ -485,36 +536,50 @@ static void bnx2fc_recv_frame(struct sk_buff *skb)
skb_pull(skb, sizeof(struct fcoe_hdr));
fr_len = skb->len - sizeof(struct fcoe_crc_eof);
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->RxFrames++;
- stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+ this_cpu_inc(lport->stats->RxFrames);
+ this_cpu_add(lport->stats->RxWords, fr_len / FCOE_WORD_TO_BYTE);
fp = (struct fc_frame *)skb;
fc_frame_init(fp);
fr_dev(fp) = lport;
fr_sof(fp) = hp->fcoe_sof;
if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
- put_cpu();
kfree_skb(skb);
return;
}
fr_eof(fp) = crc_eof.fcoe_eof;
fr_crc(fp) = crc_eof.fcoe_crc32;
if (pskb_trim(skb, fr_len)) {
- put_cpu();
kfree_skb(skb);
return;
}
+ phys_port = lport_priv(lport);
+ interface = phys_port->priv;
+ ctlr = bnx2fc_to_ctlr(interface);
+
fh = fc_frame_header_get(fp);
+ if (ntoh24(&dest_mac[3]) != ntoh24(fh->fh_d_id)) {
+ BNX2FC_HBA_DBG(lport, "FC frame d_id mismatch with MAC %pM.\n",
+ dest_mac);
+ kfree_skb(skb);
+ return;
+ }
+
vn_port = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
if (vn_port) {
port = lport_priv(vn_port);
- if (compare_ether_addr(port->data_src_addr, dest_mac)
- != 0) {
+ if (!ether_addr_equal(port->data_src_addr, dest_mac)) {
BNX2FC_HBA_DBG(lport, "fpma mismatch\n");
- put_cpu();
+ kfree_skb(skb);
+ return;
+ }
+ }
+ if (ctlr->state) {
+ if (!ether_addr_equal(mac, ctlr->dest_addr)) {
+ BNX2FC_HBA_DBG(lport, "Wrong source address: mac:%pM dest_addr:%pM.\n",
+ mac, ctlr->dest_addr);
kfree_skb(skb);
return;
}
@@ -522,7 +587,6 @@ static void bnx2fc_recv_frame(struct sk_buff *skb)
if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
fh->fh_type == FC_TYPE_FCP) {
/* Drop FCP data. We dont this in L2 path */
- put_cpu();
kfree_skb(skb);
return;
}
@@ -532,24 +596,41 @@ static void bnx2fc_recv_frame(struct sk_buff *skb)
case ELS_LOGO:
if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
/* drop non-FIP LOGO */
- put_cpu();
kfree_skb(skb);
return;
}
break;
}
}
- if (le32_to_cpu(fr_crc(fp)) !=
- ~crc32(~0, skb->data, fr_len)) {
- if (stats->InvalidCRCCount < 5)
+
+ if (fh->fh_r_ctl == FC_RCTL_BA_ABTS) {
+ /* Drop incoming ABTS */
+ kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * If the destination ID from the frame header does not match what we
+ * have on record for lport and the search for a NPIV port came up
+ * empty then this is not addressed to our port so simply drop it.
+ */
+ if (lport->port_id != ntoh24(fh->fh_d_id) && !vn_port) {
+ BNX2FC_HBA_DBG(lport, "Dropping frame due to destination mismatch: lport->port_id=%x fh->d_id=%x.\n",
+ lport->port_id, ntoh24(fh->fh_d_id));
+ kfree_skb(skb);
+ return;
+ }
+
+ fr_crc = le32_to_cpu(fr_crc(fp));
+
+ if (unlikely(fr_crc != ~crc32(~0, skb->data, fr_len))) {
+ crc_err = this_cpu_inc_return(lport->stats->InvalidCRCCount);
+ if (crc_err < 5)
printk(KERN_WARNING PFX "dropping frame with "
"CRC error\n");
- stats->InvalidCRCCount++;
- put_cpu();
kfree_skb(skb);
return;
}
- put_cpu();
fc_exch_recv(lport, fp);
}
@@ -558,13 +639,13 @@ static void bnx2fc_recv_frame(struct sk_buff *skb)
*
* @arg: ptr to bnx2fc_percpu_info structure
*/
-int bnx2fc_percpu_io_thread(void *arg)
+static int bnx2fc_percpu_io_thread(void *arg)
{
struct bnx2fc_percpu_s *p = arg;
struct bnx2fc_work *work, *tmp;
LIST_HEAD(work_list);
- set_user_nice(current, -20);
+ set_user_nice(current, MIN_NICE);
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
@@ -575,7 +656,10 @@ int bnx2fc_percpu_io_thread(void *arg)
list_for_each_entry_safe(work, tmp, &work_list, list) {
list_del_init(&work->list);
- bnx2fc_process_cq_compl(work->tgt, work->wqe);
+ bnx2fc_process_cq_compl(work->tgt, work->wqe,
+ work->rq_data,
+ work->num_rq,
+ work->task);
kfree(work);
}
@@ -594,7 +678,8 @@ static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost)
struct fc_host_statistics *bnx2fc_stats;
struct fc_lport *lport = shost_priv(shost);
struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct fcoe_statistics_params *fw_stats;
int rc = 0;
@@ -602,21 +687,28 @@ static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost)
if (!fw_stats)
return NULL;
+ mutex_lock(&hba->hba_stats_mutex);
+
bnx2fc_stats = fc_get_host_stats(shost);
init_completion(&hba->stat_req_done);
if (bnx2fc_send_stat_req(hba))
- return bnx2fc_stats;
+ goto unlock_stats_mutex;
rc = wait_for_completion_timeout(&hba->stat_req_done, (2 * HZ));
if (!rc) {
BNX2FC_HBA_DBG(lport, "FW stat req timed out\n");
- return bnx2fc_stats;
- }
- bnx2fc_stats->invalid_crc_count += fw_stats->rx_stat1.fc_crc_cnt;
- bnx2fc_stats->tx_frames += fw_stats->tx_stat.fcoe_tx_pkt_cnt;
- bnx2fc_stats->tx_words += (fw_stats->tx_stat.fcoe_tx_byte_cnt) / 4;
- bnx2fc_stats->rx_frames += fw_stats->rx_stat0.fcoe_rx_pkt_cnt;
- bnx2fc_stats->rx_words += (fw_stats->rx_stat0.fcoe_rx_byte_cnt) / 4;
+ goto unlock_stats_mutex;
+ }
+ BNX2FC_STATS(hba, rx_stat2, fc_crc_cnt);
+ bnx2fc_stats->invalid_crc_count += hba->bfw_stats.fc_crc_cnt;
+ BNX2FC_STATS(hba, tx_stat, fcoe_tx_pkt_cnt);
+ bnx2fc_stats->tx_frames += hba->bfw_stats.fcoe_tx_pkt_cnt;
+ BNX2FC_STATS(hba, tx_stat, fcoe_tx_byte_cnt);
+ bnx2fc_stats->tx_words += ((hba->bfw_stats.fcoe_tx_byte_cnt) / 4);
+ BNX2FC_STATS(hba, rx_stat0, fcoe_rx_pkt_cnt);
+ bnx2fc_stats->rx_frames += hba->bfw_stats.fcoe_rx_pkt_cnt;
+ BNX2FC_STATS(hba, rx_stat0, fcoe_rx_byte_cnt);
+ bnx2fc_stats->rx_words += ((hba->bfw_stats.fcoe_rx_byte_cnt) / 4);
bnx2fc_stats->dumped_frames = 0;
bnx2fc_stats->lip_count = 0;
@@ -625,18 +717,24 @@ static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost)
bnx2fc_stats->loss_of_signal_count = 0;
bnx2fc_stats->prim_seq_protocol_err_count = 0;
+ memcpy(&hba->prev_stats, hba->stats_buffer,
+ sizeof(struct fcoe_statistics_params));
+
+unlock_stats_mutex:
+ mutex_unlock(&hba->hba_stats_mutex);
return bnx2fc_stats;
}
static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev)
{
struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct Scsi_Host *shost = lport->host;
int rc = 0;
shost->max_cmd_len = BNX2FC_MAX_CMD_LEN;
- shost->max_lun = BNX2FC_MAX_LUN;
+ shost->max_lun = bnx2fc_max_luns;
shost->max_id = BNX2FC_MAX_FCP_TGT;
shost->max_channel = 0;
if (lport->vport)
@@ -652,43 +750,19 @@ static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev)
}
if (!lport->vport)
fc_host_max_npiv_vports(lport->host) = USHRT_MAX;
- sprintf(fc_host_symbolic_name(lport->host), "%s v%s over %s",
- BNX2FC_NAME, BNX2FC_VERSION,
- hba->netdev->name);
+ snprintf(fc_host_symbolic_name(lport->host), 256,
+ "%s (QLogic %s) v%s over %s",
+ BNX2FC_NAME, hba->chip_num, BNX2FC_VERSION,
+ interface->netdev->name);
return 0;
}
-static void bnx2fc_link_speed_update(struct fc_lport *lport)
-{
- struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
- struct net_device *netdev = hba->netdev;
- struct ethtool_cmd ecmd;
-
- if (!dev_ethtool_get_settings(netdev, &ecmd)) {
- lport->link_supported_speeds &=
- ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
- if (ecmd.supported & (SUPPORTED_1000baseT_Half |
- SUPPORTED_1000baseT_Full))
- lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
- if (ecmd.supported & SUPPORTED_10000baseT_Full)
- lport->link_supported_speeds |= FC_PORTSPEED_10GBIT;
-
- switch (ethtool_cmd_speed(&ecmd)) {
- case SPEED_1000:
- lport->link_speed = FC_PORTSPEED_1GBIT;
- break;
- case SPEED_10000:
- lport->link_speed = FC_PORTSPEED_10GBIT;
- break;
- }
- }
-}
static int bnx2fc_link_ok(struct fc_lport *lport)
{
struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct net_device *dev = hba->phys_dev;
int rc = 0;
@@ -710,20 +784,24 @@ static int bnx2fc_link_ok(struct fc_lport *lport)
*/
void bnx2fc_get_link_state(struct bnx2fc_hba *hba)
{
- if (test_bit(__LINK_STATE_NOCARRIER, &hba->netdev->state))
+ if (test_bit(__LINK_STATE_NOCARRIER, &hba->phys_dev->state))
set_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
else
clear_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
}
-static int bnx2fc_net_config(struct fc_lport *lport)
+static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev)
{
struct bnx2fc_hba *hba;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
struct fcoe_port *port;
u64 wwnn, wwpn;
port = lport_priv(lport);
- hba = port->priv;
+ interface = port->priv;
+ ctlr = bnx2fc_to_ctlr(interface);
+ hba = interface->hba;
/* require support for get_pauseparam ethtool op. */
if (!hba->phys_dev->ethtool_ops ||
@@ -735,16 +813,21 @@ static int bnx2fc_net_config(struct fc_lport *lport)
skb_queue_head_init(&port->fcoe_pending_queue);
port->fcoe_pending_queue_active = 0;
- setup_timer(&port->timer, fcoe_queue_timer, (unsigned long) lport);
+ timer_setup(&port->timer, fcoe_queue_timer, 0);
- bnx2fc_link_speed_update(lport);
+ fcoe_link_speed_update(lport);
if (!lport->vport) {
- wwnn = fcoe_wwn_from_mac(hba->ctlr.ctl_src_addr, 1, 0);
+ if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
+ wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
+ 1, 0);
BNX2FC_HBA_DBG(lport, "WWNN = 0x%llx\n", wwnn);
fc_set_wwnn(lport, wwnn);
- wwpn = fcoe_wwn_from_mac(hba->ctlr.ctl_src_addr, 2, 0);
+ if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
+ wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
+ 2, 0);
+
BNX2FC_HBA_DBG(lport, "WWPN = 0x%llx\n", wwpn);
fc_set_wwpn(lport, wwpn);
}
@@ -752,13 +835,13 @@ static int bnx2fc_net_config(struct fc_lport *lport)
return 0;
}
-static void bnx2fc_destroy_timer(unsigned long data)
+static void bnx2fc_destroy_timer(struct timer_list *t)
{
- struct bnx2fc_hba *hba = (struct bnx2fc_hba *)data;
+ struct bnx2fc_hba *hba = timer_container_of(hba, t, destroy_timer);
- BNX2FC_HBA_DBG(hba->ctlr.lp, "ERROR:bnx2fc_destroy_timer - "
- "Destroy compl not received!!\n");
- hba->flags |= BNX2FC_FLAG_DESTROY_CMPL;
+ printk(KERN_ERR PFX "ERROR:bnx2fc_destroy_timer - "
+ "Destroy compl not received!!\n");
+ set_bit(BNX2FC_FLAG_DESTROY_CMPL, &hba->flags);
wake_up_interruptible(&hba->destroy_wait);
}
@@ -767,101 +850,132 @@ static void bnx2fc_destroy_timer(unsigned long data)
*
* @context: adapter structure pointer
* @event: event type
+ * @vlan_id: vlan id - associated vlan id with this event
*
* Handles NETDEV_UP, NETDEV_DOWN, NETDEV_GOING_DOWN,NETDEV_CHANGE and
- * NETDEV_CHANGE_MTU events
+ * NETDEV_CHANGE_MTU events. Handle NETDEV_UNREGISTER only for vlans.
*/
-static void bnx2fc_indicate_netevent(void *context, unsigned long event)
+static void bnx2fc_indicate_netevent(void *context, unsigned long event,
+ u16 vlan_id)
{
struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context;
- struct fc_lport *lport = hba->ctlr.lp;
+ struct fcoe_ctlr_device *cdev;
+ struct fc_lport *lport;
struct fc_lport *vport;
+ struct bnx2fc_interface *interface, *tmp;
+ struct fcoe_ctlr *ctlr;
+ int wait_for_upload = 0;
u32 link_possible = 1;
- if (!test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
- BNX2FC_MISC_DBG("driver not ready. event=%s %ld\n",
- hba->netdev->name, event);
+ if (vlan_id != 0 && event != NETDEV_UNREGISTER)
return;
- }
-
- /*
- * ASSUMPTION:
- * indicate_netevent cannot be called from cnic unless bnx2fc
- * does register_device
- */
- BUG_ON(!lport);
-
- BNX2FC_HBA_DBG(lport, "enter netevent handler - event=%s %ld\n",
- hba->netdev->name, event);
switch (event) {
case NETDEV_UP:
- BNX2FC_HBA_DBG(lport, "Port up, adapter_state = %ld\n",
- hba->adapter_state);
if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
printk(KERN_ERR "indicate_netevent: "\
- "adapter is not UP!!\n");
+ "hba is not UP!!\n");
break;
case NETDEV_DOWN:
- BNX2FC_HBA_DBG(lport, "Port down\n");
clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
link_possible = 0;
break;
case NETDEV_GOING_DOWN:
- BNX2FC_HBA_DBG(lport, "Port going down\n");
set_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
link_possible = 0;
break;
case NETDEV_CHANGE:
- BNX2FC_HBA_DBG(lport, "NETDEV_CHANGE\n");
break;
+ case NETDEV_UNREGISTER:
+ if (!vlan_id)
+ return;
+ mutex_lock(&bnx2fc_dev_lock);
+ list_for_each_entry_safe(interface, tmp, &if_list, list) {
+ if (interface->hba == hba &&
+ interface->vlan_id == (vlan_id & VLAN_VID_MASK))
+ __bnx2fc_destroy(interface);
+ }
+ mutex_unlock(&bnx2fc_dev_lock);
+ return;
+
default:
- printk(KERN_ERR PFX "Unkonwn netevent %ld", event);
return;
}
- bnx2fc_link_speed_update(lport);
+ mutex_lock(&bnx2fc_dev_lock);
+ list_for_each_entry(interface, &if_list, list) {
+
+ if (interface->hba != hba)
+ continue;
+
+ ctlr = bnx2fc_to_ctlr(interface);
+ lport = ctlr->lp;
+ BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n",
+ interface->netdev->name, event);
+
+ fcoe_link_speed_update(lport);
+
+ cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
+
+ if (link_possible && !bnx2fc_link_ok(lport)) {
+ switch (cdev->enabled) {
+ case FCOE_CTLR_DISABLED:
+ pr_info("Link up while interface is disabled.\n");
+ break;
+ case FCOE_CTLR_ENABLED:
+ case FCOE_CTLR_UNUSED:
+ /* Reset max recv frame size to default */
+ fc_set_mfs(lport, BNX2FC_MFS);
+ /*
+ * ctlr link up will only be handled during
+ * enable to avoid sending discovery
+ * solicitation on a stale vlan
+ */
+ if (interface->enabled)
+ fcoe_ctlr_link_up(ctlr);
+ }
+ } else if (fcoe_ctlr_link_down(ctlr)) {
+ switch (cdev->enabled) {
+ case FCOE_CTLR_DISABLED:
+ pr_info("Link down while interface is disabled.\n");
+ break;
+ case FCOE_CTLR_ENABLED:
+ case FCOE_CTLR_UNUSED:
+ mutex_lock(&lport->lp_mutex);
+ list_for_each_entry(vport, &lport->vports, list)
+ fc_host_port_type(vport->host) =
+ FC_PORTTYPE_UNKNOWN;
+ mutex_unlock(&lport->lp_mutex);
+ fc_host_port_type(lport->host) =
+ FC_PORTTYPE_UNKNOWN;
+ this_cpu_inc(lport->stats->LinkFailureCount);
+ fcoe_clean_pending_queue(lport);
+ wait_for_upload = 1;
+ }
+ }
+ }
+ mutex_unlock(&bnx2fc_dev_lock);
- if (link_possible && !bnx2fc_link_ok(lport)) {
- printk(KERN_ERR "indicate_netevent: call ctlr_link_up\n");
- fcoe_ctlr_link_up(&hba->ctlr);
- } else {
- printk(KERN_ERR "indicate_netevent: call ctlr_link_down\n");
- if (fcoe_ctlr_link_down(&hba->ctlr)) {
- clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
- mutex_lock(&lport->lp_mutex);
- list_for_each_entry(vport, &lport->vports, list)
- fc_host_port_type(vport->host) =
- FC_PORTTYPE_UNKNOWN;
- mutex_unlock(&lport->lp_mutex);
- fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
- per_cpu_ptr(lport->dev_stats,
- get_cpu())->LinkFailureCount++;
- put_cpu();
- fcoe_clean_pending_queue(lport);
-
- init_waitqueue_head(&hba->shutdown_wait);
- BNX2FC_HBA_DBG(lport, "indicate_netevent "
- "num_ofld_sess = %d\n",
- hba->num_ofld_sess);
- hba->wait_for_link_down = 1;
- BNX2FC_HBA_DBG(lport, "waiting for uploads to "
- "compl proc = %s\n",
- current->comm);
- wait_event_interruptible(hba->shutdown_wait,
- (hba->num_ofld_sess == 0));
- BNX2FC_HBA_DBG(lport, "wakeup - num_ofld_sess = %d\n",
+ if (wait_for_upload) {
+ clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
+ init_waitqueue_head(&hba->shutdown_wait);
+ BNX2FC_MISC_DBG("indicate_netevent "
+ "num_ofld_sess = %d\n",
hba->num_ofld_sess);
- hba->wait_for_link_down = 0;
+ hba->wait_for_link_down = 1;
+ wait_event_interruptible(hba->shutdown_wait,
+ (hba->num_ofld_sess == 0));
+ BNX2FC_MISC_DBG("wakeup - num_ofld_sess = %d\n",
+ hba->num_ofld_sess);
+ hba->wait_for_link_down = 0;
- if (signal_pending(current))
- flush_signals(current);
- }
+ if (signal_pending(current))
+ flush_signals(current);
}
}
@@ -873,30 +987,26 @@ static int bnx2fc_libfc_config(struct fc_lport *lport)
sizeof(struct libfc_function_template));
fc_elsct_init(lport);
fc_exch_init(lport);
- fc_rport_init(lport);
fc_disc_init(lport);
+ fc_disc_config(lport, lport);
return 0;
}
-static int bnx2fc_em_config(struct fc_lport *lport)
+static int bnx2fc_em_config(struct fc_lport *lport, struct bnx2fc_hba *hba)
{
- struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
+ int fcoe_min_xid, fcoe_max_xid;
- if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, FCOE_MIN_XID,
- FCOE_MAX_XID, NULL)) {
+ fcoe_min_xid = hba->max_xid + 1;
+ if (nr_cpu_ids <= 2)
+ fcoe_max_xid = hba->max_xid + FCOE_XIDS_PER_CPU_OFFSET;
+ else
+ fcoe_max_xid = hba->max_xid + FCOE_MAX_XID_OFFSET;
+ if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, fcoe_min_xid,
+ fcoe_max_xid, NULL)) {
printk(KERN_ERR PFX "em_config:fc_exch_mgr_alloc failed\n");
return -ENOMEM;
}
- hba->cmd_mgr = bnx2fc_cmd_mgr_alloc(hba, BNX2FC_MIN_XID,
- BNX2FC_MAX_XID);
-
- if (!hba->cmd_mgr) {
- printk(KERN_ERR PFX "em_config:bnx2fc_cmd_mgr_alloc failed\n");
- fc_exch_mgr_free(lport);
- return -ENOMEM;
- }
return 0;
}
@@ -904,16 +1014,13 @@ static int bnx2fc_lport_config(struct fc_lport *lport)
{
lport->link_up = 0;
lport->qfull = 0;
- lport->max_retry_count = 3;
- lport->max_rport_retry_count = 3;
+ lport->max_retry_count = BNX2FC_MAX_RETRY_CNT;
+ lport->max_rport_retry_count = BNX2FC_MAX_RPORT_RETRY_CNT;
lport->e_d_tov = 2 * 1000;
lport->r_a_tov = 10 * 1000;
- /* REVISIT: enable when supporting tape devices
lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
- */
- lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS);
lport->does_npiv = 1;
memset(&lport->rnid_gen, 0, sizeof(struct fc_els_rnid_gen));
@@ -943,18 +1050,20 @@ static int bnx2fc_fip_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype,
struct net_device *orig_dev)
{
- struct bnx2fc_hba *hba;
- hba = container_of(ptype, struct bnx2fc_hba, fip_packet_type);
- fcoe_ctlr_recv(&hba->ctlr, skb);
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
+ interface = container_of(ptype, struct bnx2fc_interface,
+ fip_packet_type);
+ ctlr = bnx2fc_to_ctlr(interface);
+ fcoe_ctlr_recv(ctlr, skb);
return 0;
}
/**
* bnx2fc_update_src_mac - Update Ethernet MAC filters.
*
- * @fip: FCoE controller.
- * @old: Unicast MAC address to delete if the MAC is non-zero.
- * @new: Unicast MAC address to add.
+ * @lport: The local port
+ * @addr: Location of data to copy
*
* Remove any previously-set unicast MAC filter.
* Add secondary FCoE MAC address filter for our OUI.
@@ -987,6 +1096,20 @@ static u8 *bnx2fc_get_src_mac(struct fc_lport *lport)
*/
static void bnx2fc_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
+ struct fip_header *fiph;
+ struct ethhdr *eth_hdr;
+ u16 op;
+ u8 sub;
+
+ fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2);
+ eth_hdr = (struct ethhdr *)skb_mac_header(skb);
+ op = ntohs(fiph->fip_op);
+ sub = fiph->fip_subcode;
+
+ if (op == FIP_OP_CTRL && sub == FIP_SC_SOL && bnx2fc_log_fka)
+ BNX2FC_MISC_DBG("Sending FKA from %pM to %pM.\n",
+ eth_hdr->h_source, eth_hdr->h_dest);
+
skb->dev = bnx2fc_from_ctlr(fip)->netdev;
dev_queue_xmit(skb);
}
@@ -996,25 +1119,41 @@ static int bnx2fc_vport_create(struct fc_vport *vport, bool disabled)
struct Scsi_Host *shost = vport_to_shost(vport);
struct fc_lport *n_port = shost_priv(shost);
struct fcoe_port *port = lport_priv(n_port);
- struct bnx2fc_hba *hba = port->priv;
- struct net_device *netdev = hba->netdev;
+ struct bnx2fc_interface *interface = port->priv;
+ struct net_device *netdev = interface->netdev;
struct fc_lport *vn_port;
+ int rc;
+ char buf[32];
- if (!test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
+ rc = fcoe_validate_vport_create(vport);
+ if (rc) {
+ fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
+ printk(KERN_ERR PFX "Failed to create vport, "
+ "WWPN (0x%s) already exists\n",
+ buf);
+ return rc;
+ }
+
+ if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags)) {
printk(KERN_ERR PFX "vn ports cannot be created on"
- "this hba\n");
+ "this interface\n");
return -EIO;
}
+ rtnl_lock();
mutex_lock(&bnx2fc_dev_lock);
- vn_port = bnx2fc_if_create(hba, &vport->dev, 1);
+ vn_port = bnx2fc_if_create(interface, &vport->dev, 1);
mutex_unlock(&bnx2fc_dev_lock);
+ rtnl_unlock();
- if (IS_ERR(vn_port)) {
+ if (!vn_port) {
printk(KERN_ERR PFX "bnx2fc_vport_create (%s) failed\n",
netdev->name);
return -EIO;
}
+ if (bnx2fc_devloss_tmo)
+ fc_host_dev_loss_tmo(vn_port->host) = bnx2fc_devloss_tmo;
+
if (disabled) {
fc_vport_set_state(vport, FC_VPORT_DISABLED);
} else {
@@ -1026,17 +1165,47 @@ static int bnx2fc_vport_create(struct fc_vport *vport, bool disabled)
return 0;
}
+static void bnx2fc_free_vport(struct bnx2fc_hba *hba, struct fc_lport *lport)
+{
+ struct bnx2fc_lport *blport, *tmp;
+
+ spin_lock_bh(&hba->hba_lock);
+ list_for_each_entry_safe(blport, tmp, &hba->vports, list) {
+ if (blport->lport == lport) {
+ list_del(&blport->list);
+ kfree(blport);
+ }
+ }
+ spin_unlock_bh(&hba->hba_lock);
+}
+
static int bnx2fc_vport_destroy(struct fc_vport *vport)
{
struct Scsi_Host *shost = vport_to_shost(vport);
struct fc_lport *n_port = shost_priv(shost);
struct fc_lport *vn_port = vport->dd_data;
struct fcoe_port *port = lport_priv(vn_port);
+ struct bnx2fc_interface *interface = port->priv;
+ struct fc_lport *v_port;
+ bool found = false;
mutex_lock(&n_port->lp_mutex);
+ list_for_each_entry(v_port, &n_port->vports, list)
+ if (v_port->vport == vport) {
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ mutex_unlock(&n_port->lp_mutex);
+ return -ENOENT;
+ }
list_del(&vn_port->list);
mutex_unlock(&n_port->lp_mutex);
- queue_work(bnx2fc_wq, &port->destroy_work);
+ bnx2fc_free_vport(interface->hba, port->lport);
+ bnx2fc_port_shutdown(port->lport);
+ bnx2fc_port_destroy(port);
+ bnx2fc_interface_put(interface);
return 0;
}
@@ -1056,10 +1225,11 @@ static int bnx2fc_vport_disable(struct fc_vport *vport, bool disable)
}
-static int bnx2fc_netdev_setup(struct bnx2fc_hba *hba)
+static int bnx2fc_interface_setup(struct bnx2fc_interface *interface)
{
- struct net_device *netdev = hba->netdev;
- struct net_device *physdev = hba->phys_dev;
+ struct net_device *netdev = interface->netdev;
+ struct net_device *physdev = interface->hba->phys_dev;
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
struct netdev_hw_addr *ha;
int sel_san_mac = 0;
@@ -1074,7 +1244,8 @@ static int bnx2fc_netdev_setup(struct bnx2fc_hba *hba)
if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
(is_valid_ether_addr(ha->addr))) {
- memcpy(hba->ctlr.ctl_src_addr, ha->addr, ETH_ALEN);
+ memcpy(ctlr->ctl_src_addr, ha->addr,
+ ETH_ALEN);
sel_san_mac = 1;
BNX2FC_MISC_DBG("Found SAN MAC\n");
}
@@ -1084,15 +1255,15 @@ static int bnx2fc_netdev_setup(struct bnx2fc_hba *hba)
if (!sel_san_mac)
return -ENODEV;
- hba->fip_packet_type.func = bnx2fc_fip_recv;
- hba->fip_packet_type.type = htons(ETH_P_FIP);
- hba->fip_packet_type.dev = netdev;
- dev_add_pack(&hba->fip_packet_type);
+ interface->fip_packet_type.func = bnx2fc_fip_recv;
+ interface->fip_packet_type.type = htons(ETH_P_FIP);
+ interface->fip_packet_type.dev = netdev;
+ dev_add_pack(&interface->fip_packet_type);
- hba->fcoe_packet_type.func = bnx2fc_rcv;
- hba->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
- hba->fcoe_packet_type.dev = netdev;
- dev_add_pack(&hba->fcoe_packet_type);
+ interface->fcoe_packet_type.func = bnx2fc_rcv;
+ interface->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+ interface->fcoe_packet_type.dev = netdev;
+ dev_add_pack(&interface->fcoe_packet_type);
return 0;
}
@@ -1128,55 +1299,61 @@ static void bnx2fc_release_transport(void)
static void bnx2fc_interface_release(struct kref *kref)
{
- struct bnx2fc_hba *hba;
+ struct fcoe_ctlr_device *ctlr_dev;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
struct net_device *netdev;
- struct net_device *phys_dev;
- hba = container_of(kref, struct bnx2fc_hba, kref);
+ interface = container_of(kref, struct bnx2fc_interface, kref);
BNX2FC_MISC_DBG("Interface is being released\n");
- netdev = hba->netdev;
- phys_dev = hba->phys_dev;
+ ctlr = bnx2fc_to_ctlr(interface);
+ ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
+ netdev = interface->netdev;
/* tear-down FIP controller */
- if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done))
- fcoe_ctlr_destroy(&hba->ctlr);
+ if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags))
+ fcoe_ctlr_destroy(ctlr);
+
+ fcoe_ctlr_device_delete(ctlr_dev);
- /* Free the command manager */
- if (hba->cmd_mgr) {
- bnx2fc_cmd_mgr_free(hba->cmd_mgr);
- hba->cmd_mgr = NULL;
- }
dev_put(netdev);
module_put(THIS_MODULE);
}
-static inline void bnx2fc_interface_get(struct bnx2fc_hba *hba)
+static inline void bnx2fc_interface_get(struct bnx2fc_interface *interface)
{
- kref_get(&hba->kref);
+ kref_get(&interface->kref);
}
-static inline void bnx2fc_interface_put(struct bnx2fc_hba *hba)
+static inline void bnx2fc_interface_put(struct bnx2fc_interface *interface)
{
- kref_put(&hba->kref, bnx2fc_interface_release);
+ kref_put(&interface->kref, bnx2fc_interface_release);
}
-static void bnx2fc_interface_destroy(struct bnx2fc_hba *hba)
+static void bnx2fc_hba_destroy(struct bnx2fc_hba *hba)
{
+ /* Free the command manager */
+ if (hba->cmd_mgr) {
+ bnx2fc_cmd_mgr_free(hba->cmd_mgr);
+ hba->cmd_mgr = NULL;
+ }
+ kfree(hba->tgt_ofld_list);
bnx2fc_unbind_pcidev(hba);
kfree(hba);
}
/**
- * bnx2fc_interface_create - create a new fcoe instance
+ * bnx2fc_hba_create - create a new bnx2fc hba
*
* @cnic: pointer to cnic device
*
- * Creates a new FCoE instance on the given device which include allocating
- * hba structure, scsi_host and lport structures.
+ * Creates a new FCoE hba on the given device.
+ *
*/
-static struct bnx2fc_hba *bnx2fc_interface_create(struct cnic_dev *cnic)
+static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic)
{
struct bnx2fc_hba *hba;
+ struct fcoe_capabilities *fcoe_cap;
int rc;
hba = kzalloc(sizeof(*hba), GFP_KERNEL);
@@ -1186,67 +1363,116 @@ static struct bnx2fc_hba *bnx2fc_interface_create(struct cnic_dev *cnic)
}
spin_lock_init(&hba->hba_lock);
mutex_init(&hba->hba_mutex);
+ mutex_init(&hba->hba_stats_mutex);
hba->cnic = cnic;
+
+ hba->max_tasks = cnic->max_fcoe_exchanges;
+ hba->elstm_xids = (hba->max_tasks / 2);
+ hba->max_outstanding_cmds = hba->elstm_xids;
+ hba->max_xid = (hba->max_tasks - 1);
+
rc = bnx2fc_bind_pcidev(hba);
- if (rc)
+ if (rc) {
+ printk(KERN_ERR PFX "create_adapter: bind error\n");
goto bind_err;
+ }
hba->phys_dev = cnic->netdev;
- /* will get overwritten after we do vlan discovery */
- hba->netdev = hba->phys_dev;
+ hba->next_conn_id = 0;
+
+ hba->tgt_ofld_list =
+ kcalloc(BNX2FC_NUM_MAX_SESS, sizeof(struct bnx2fc_rport *),
+ GFP_KERNEL);
+ if (!hba->tgt_ofld_list) {
+ printk(KERN_ERR PFX "Unable to allocate tgt offload list\n");
+ goto tgtofld_err;
+ }
+
+ hba->num_ofld_sess = 0;
+
+ hba->cmd_mgr = bnx2fc_cmd_mgr_alloc(hba);
+ if (!hba->cmd_mgr) {
+ printk(KERN_ERR PFX "em_config:bnx2fc_cmd_mgr_alloc failed\n");
+ goto cmgr_err;
+ }
+ fcoe_cap = &hba->fcoe_cap;
+
+ fcoe_cap->capability1 = BNX2FC_TM_MAX_SQES <<
+ FCOE_IOS_PER_CONNECTION_SHIFT;
+ fcoe_cap->capability1 |= BNX2FC_NUM_MAX_SESS <<
+ FCOE_LOGINS_PER_PORT_SHIFT;
+ fcoe_cap->capability2 = hba->max_outstanding_cmds <<
+ FCOE_NUMBER_OF_EXCHANGES_SHIFT;
+ fcoe_cap->capability2 |= BNX2FC_MAX_NPIV <<
+ FCOE_NPIV_WWN_PER_PORT_SHIFT;
+ fcoe_cap->capability3 = BNX2FC_NUM_MAX_SESS <<
+ FCOE_TARGETS_SUPPORTED_SHIFT;
+ fcoe_cap->capability3 |= hba->max_outstanding_cmds <<
+ FCOE_OUTSTANDING_COMMANDS_SHIFT;
+ fcoe_cap->capability4 = FCOE_CAPABILITY4_STATEFUL;
init_waitqueue_head(&hba->shutdown_wait);
init_waitqueue_head(&hba->destroy_wait);
+ INIT_LIST_HEAD(&hba->vports);
return hba;
+
+cmgr_err:
+ kfree(hba->tgt_ofld_list);
+tgtofld_err:
+ bnx2fc_unbind_pcidev(hba);
bind_err:
- printk(KERN_ERR PFX "create_interface: bind error\n");
kfree(hba);
return NULL;
}
-static int bnx2fc_interface_setup(struct bnx2fc_hba *hba,
- enum fip_state fip_mode)
+static struct bnx2fc_interface *
+bnx2fc_interface_create(struct bnx2fc_hba *hba,
+ struct net_device *netdev,
+ enum fip_mode fip_mode)
{
+ struct fcoe_ctlr_device *ctlr_dev;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
+ int size;
int rc = 0;
- struct net_device *netdev = hba->netdev;
- struct fcoe_ctlr *fip = &hba->ctlr;
+ size = (sizeof(*interface) + sizeof(struct fcoe_ctlr));
+ ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &bnx2fc_fcoe_sysfs_templ,
+ size);
+ if (!ctlr_dev) {
+ printk(KERN_ERR PFX "Unable to allocate interface structure\n");
+ return NULL;
+ }
+ ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ ctlr->cdev = ctlr_dev;
+ interface = fcoe_ctlr_priv(ctlr);
dev_hold(netdev);
- kref_init(&hba->kref);
-
- hba->flags = 0;
+ kref_init(&interface->kref);
+ interface->hba = hba;
+ interface->netdev = netdev;
/* Initialize FIP */
- memset(fip, 0, sizeof(*fip));
- fcoe_ctlr_init(fip, fip_mode);
- hba->ctlr.send = bnx2fc_fip_send;
- hba->ctlr.update_mac = bnx2fc_update_src_mac;
- hba->ctlr.get_src_addr = bnx2fc_get_src_mac;
- set_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done);
-
- rc = bnx2fc_netdev_setup(hba);
- if (rc)
- goto setup_err;
-
- hba->next_conn_id = 0;
-
- memset(hba->tgt_ofld_list, 0, sizeof(hba->tgt_ofld_list));
- hba->num_ofld_sess = 0;
+ fcoe_ctlr_init(ctlr, fip_mode);
+ ctlr->send = bnx2fc_fip_send;
+ ctlr->update_mac = bnx2fc_update_src_mac;
+ ctlr->get_src_addr = bnx2fc_get_src_mac;
+ set_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags);
- return 0;
+ rc = bnx2fc_interface_setup(interface);
+ if (!rc)
+ return interface;
-setup_err:
- fcoe_ctlr_destroy(&hba->ctlr);
+ fcoe_ctlr_destroy(ctlr);
dev_put(netdev);
- bnx2fc_interface_put(hba);
- return rc;
+ fcoe_ctlr_device_delete(ctlr_dev);
+ return NULL;
}
/**
* bnx2fc_if_create - Create FCoE instance on a given interface
*
- * @hba: FCoE interface to create a local port on
+ * @interface: FCoE interface to create a local port on
* @parent: Device pointer to be the parent in sysfs for the SCSI host
* @npiv: Indicates if the port is vport or not
*
@@ -1254,16 +1480,26 @@ setup_err:
*
* Returns: Allocated fc_lport or an error pointer
*/
-static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
+static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
struct device *parent, int npiv)
{
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
struct fc_lport *lport, *n_port;
struct fcoe_port *port;
struct Scsi_Host *shost;
struct fc_vport *vport = dev_to_vport(parent);
+ struct bnx2fc_lport *blport;
+ struct bnx2fc_hba *hba = interface->hba;
int rc = 0;
+ blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL);
+ if (!blport) {
+ BNX2FC_HBA_DBG(ctlr->lp, "Unable to alloc blport\n");
+ return NULL;
+ }
+
/* Allocate Scsi_Host structure */
+ bnx2fc_shost_template.can_queue = hba->max_outstanding_cmds;
if (!npiv)
lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port));
else
@@ -1271,13 +1507,13 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
if (!lport) {
printk(KERN_ERR PFX "could not allocate scsi host structure\n");
- return NULL;
+ goto free_blport;
}
shost = lport->host;
port = lport_priv(lport);
port->lport = lport;
- port->priv = hba;
- INIT_WORK(&port->destroy_work, bnx2fc_destroy_work);
+ port->priv = interface;
+ port->get_netdev = bnx2fc_netdev;
/* Configure fcoe_port */
rc = bnx2fc_lport_config(lport);
@@ -1291,7 +1527,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
fc_set_wwpn(lport, vport->port_name);
}
/* Configure netdev and networking properties of the lport */
- rc = bnx2fc_net_config(lport);
+ rc = bnx2fc_net_config(lport, interface->netdev);
if (rc) {
printk(KERN_ERR PFX "Error on bnx2fc_net_config\n");
goto lp_config_err;
@@ -1299,22 +1535,25 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
rc = bnx2fc_shost_config(lport, parent);
if (rc) {
- printk(KERN_ERR PFX "Couldnt configure shost for %s\n",
- hba->netdev->name);
+ printk(KERN_ERR PFX "Couldn't configure shost for %s\n",
+ interface->netdev->name);
goto lp_config_err;
}
/* Initialize the libfc library */
rc = bnx2fc_libfc_config(lport);
if (rc) {
- printk(KERN_ERR PFX "Couldnt configure libfc\n");
+ printk(KERN_ERR PFX "Couldn't configure libfc\n");
goto shost_err;
}
fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
+ if (bnx2fc_devloss_tmo)
+ fc_host_dev_loss_tmo(shost) = bnx2fc_devloss_tmo;
+
/* Allocate exchange manager */
if (!npiv)
- rc = bnx2fc_em_config(lport);
+ rc = bnx2fc_em_config(lport, hba);
else {
shost = vport_to_shost(vport);
n_port = shost_priv(shost);
@@ -1326,36 +1565,53 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
goto shost_err;
}
- bnx2fc_interface_get(hba);
+ bnx2fc_interface_get(interface);
+
+ spin_lock_bh(&hba->hba_lock);
+ blport->lport = lport;
+ list_add_tail(&blport->list, &hba->vports);
+ spin_unlock_bh(&hba->hba_lock);
+
return lport;
shost_err:
scsi_remove_host(shost);
lp_config_err:
scsi_host_put(lport->host);
+free_blport:
+ kfree(blport);
return NULL;
}
-static void bnx2fc_netdev_cleanup(struct bnx2fc_hba *hba)
+static void bnx2fc_net_cleanup(struct bnx2fc_interface *interface)
{
/* Dont listen for Ethernet packets anymore */
- __dev_remove_pack(&hba->fcoe_packet_type);
- __dev_remove_pack(&hba->fip_packet_type);
+ __dev_remove_pack(&interface->fcoe_packet_type);
+ __dev_remove_pack(&interface->fip_packet_type);
synchronize_net();
}
-static void bnx2fc_if_destroy(struct fc_lport *lport)
+static void bnx2fc_interface_cleanup(struct bnx2fc_interface *interface)
{
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+ struct fc_lport *lport = ctlr->lp;
struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
- BNX2FC_HBA_DBG(hba->ctlr.lp, "ENTERED bnx2fc_if_destroy\n");
/* Stop the transmit retry timer */
- del_timer_sync(&port->timer);
+ timer_delete_sync(&port->timer);
/* Free existing transmit skbs */
fcoe_clean_pending_queue(lport);
+ bnx2fc_net_cleanup(interface);
+
+ bnx2fc_free_vport(hba, lport);
+}
+
+static void bnx2fc_if_destroy(struct fc_lport *lport)
+{
+
/* Free queued packets for the receive thread */
bnx2fc_clean_rx_queue(lport);
@@ -1374,15 +1630,25 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
/* Release Scsi_Host */
scsi_host_put(lport->host);
+}
+
+static void __bnx2fc_destroy(struct bnx2fc_interface *interface)
+{
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+ struct fc_lport *lport = ctlr->lp;
+ struct fcoe_port *port = lport_priv(lport);
- bnx2fc_interface_put(hba);
+ bnx2fc_interface_cleanup(interface);
+ bnx2fc_stop(interface);
+ list_del(&interface->list);
+ bnx2fc_port_destroy(port);
+ bnx2fc_interface_put(interface);
}
/**
* bnx2fc_destroy - Destroy a bnx2fc FCoE interface
*
- * @buffer: The name of the Ethernet interface to be destroyed
- * @kp: The associated kernel parameter
+ * @netdev: The net device that the FCoE interface is on
*
* Called from sysfs.
*
@@ -1390,68 +1656,40 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
*/
static int bnx2fc_destroy(struct net_device *netdev)
{
- struct bnx2fc_hba *hba = NULL;
- struct net_device *phys_dev;
+ struct bnx2fc_interface *interface = NULL;
+ struct workqueue_struct *timer_work_queue;
+ struct fcoe_ctlr *ctlr;
int rc = 0;
rtnl_lock();
-
mutex_lock(&bnx2fc_dev_lock);
- /* obtain physical netdev */
- if (netdev->priv_flags & IFF_802_1Q_VLAN)
- phys_dev = vlan_dev_real_dev(netdev);
- else {
- printk(KERN_ERR PFX "Not a vlan device\n");
- rc = -ENODEV;
- goto netdev_err;
- }
- hba = bnx2fc_hba_lookup(phys_dev);
- if (!hba || !hba->ctlr.lp) {
+ interface = bnx2fc_interface_lookup(netdev);
+ ctlr = bnx2fc_to_ctlr(interface);
+ if (!interface || !ctlr->lp) {
rc = -ENODEV;
- printk(KERN_ERR PFX "bnx2fc_destroy: hba or lport not found\n");
- goto netdev_err;
- }
-
- if (!test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
- printk(KERN_ERR PFX "bnx2fc_destroy: Create not called\n");
+ printk(KERN_ERR PFX "bnx2fc_destroy: interface or lport not found\n");
goto netdev_err;
}
- bnx2fc_netdev_cleanup(hba);
-
- bnx2fc_stop(hba);
-
- bnx2fc_if_destroy(hba->ctlr.lp);
-
- destroy_workqueue(hba->timer_work_queue);
+ timer_work_queue = interface->timer_work_queue;
+ __bnx2fc_destroy(interface);
+ destroy_workqueue(timer_work_queue);
- if (test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done))
- bnx2fc_fw_destroy(hba);
-
- clear_bit(BNX2FC_CREATE_DONE, &hba->init_done);
netdev_err:
mutex_unlock(&bnx2fc_dev_lock);
rtnl_unlock();
return rc;
}
-static void bnx2fc_destroy_work(struct work_struct *work)
+static void bnx2fc_port_destroy(struct fcoe_port *port)
{
- struct fcoe_port *port;
struct fc_lport *lport;
- port = container_of(work, struct fcoe_port, destroy_work);
lport = port->lport;
+ BNX2FC_HBA_DBG(lport, "Entered %s, destroying lport %p\n", __func__, lport);
- BNX2FC_HBA_DBG(lport, "Entered bnx2fc_destroy_work\n");
-
- bnx2fc_port_shutdown(lport);
- rtnl_lock();
- mutex_lock(&bnx2fc_dev_lock);
bnx2fc_if_destroy(lport);
- mutex_unlock(&bnx2fc_dev_lock);
- rtnl_unlock();
}
static void bnx2fc_unbind_adapter_devices(struct bnx2fc_hba *hba)
@@ -1483,32 +1721,95 @@ mem_err:
static int bnx2fc_bind_pcidev(struct bnx2fc_hba *hba)
{
struct cnic_dev *cnic;
+ struct pci_dev *pdev;
if (!hba->cnic) {
printk(KERN_ERR PFX "cnic is NULL\n");
return -ENODEV;
}
cnic = hba->cnic;
- hba->pcidev = cnic->pcidev;
- if (hba->pcidev)
- pci_dev_get(hba->pcidev);
+ pdev = hba->pcidev = cnic->pcidev;
+ if (!hba->pcidev)
+ return -ENODEV;
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_NX2_57710:
+ strscpy(hba->chip_num, "BCM57710", sizeof(hba->chip_num));
+ break;
+ case PCI_DEVICE_ID_NX2_57711:
+ strscpy(hba->chip_num, "BCM57711", sizeof(hba->chip_num));
+ break;
+ case PCI_DEVICE_ID_NX2_57712:
+ case PCI_DEVICE_ID_NX2_57712_MF:
+ case PCI_DEVICE_ID_NX2_57712_VF:
+ strscpy(hba->chip_num, "BCM57712", sizeof(hba->chip_num));
+ break;
+ case PCI_DEVICE_ID_NX2_57800:
+ case PCI_DEVICE_ID_NX2_57800_MF:
+ case PCI_DEVICE_ID_NX2_57800_VF:
+ strscpy(hba->chip_num, "BCM57800", sizeof(hba->chip_num));
+ break;
+ case PCI_DEVICE_ID_NX2_57810:
+ case PCI_DEVICE_ID_NX2_57810_MF:
+ case PCI_DEVICE_ID_NX2_57810_VF:
+ strscpy(hba->chip_num, "BCM57810", sizeof(hba->chip_num));
+ break;
+ case PCI_DEVICE_ID_NX2_57840:
+ case PCI_DEVICE_ID_NX2_57840_MF:
+ case PCI_DEVICE_ID_NX2_57840_VF:
+ case PCI_DEVICE_ID_NX2_57840_2_20:
+ case PCI_DEVICE_ID_NX2_57840_4_10:
+ strscpy(hba->chip_num, "BCM57840", sizeof(hba->chip_num));
+ break;
+ default:
+ pr_err(PFX "Unknown device id 0x%x\n", pdev->device);
+ break;
+ }
+ pci_dev_get(hba->pcidev);
return 0;
}
static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba)
{
- if (hba->pcidev)
+ if (hba->pcidev) {
+ hba->chip_num[0] = '\0';
pci_dev_put(hba->pcidev);
+ }
hba->pcidev = NULL;
}
+/**
+ * bnx2fc_ulp_get_stats - cnic callback to populate FCoE stats
+ *
+ * @handle: transport handle pointing to adapter structure
+ */
+static int bnx2fc_ulp_get_stats(void *handle)
+{
+ struct bnx2fc_hba *hba = handle;
+ struct cnic_dev *cnic;
+ struct fcoe_stats_info *stats_addr;
+
+ if (!hba)
+ return -EINVAL;
+
+ cnic = hba->cnic;
+ stats_addr = &cnic->stats_addr->fcoe_stat;
+ if (!stats_addr)
+ return -EINVAL;
+
+ strscpy(stats_addr->version, BNX2FC_VERSION,
+ sizeof(stats_addr->version));
+ stats_addr->txq_size = BNX2FC_SQ_WQES_MAX;
+ stats_addr->rxq_size = BNX2FC_CQ_WQES_MAX;
+
+ return 0;
+}
/**
* bnx2fc_ulp_start - cnic callback to initialize & start adapter instance
*
- * @handle: transport handle pointing to adapter struture
+ * @handle: transport handle pointing to adapter structure
*
* This function maps adapter structure to pcidev structure and initiates
* firmware handshake to enable/initialize on-chip FCoE components.
@@ -1521,28 +1822,29 @@ static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba)
static void bnx2fc_ulp_start(void *handle)
{
struct bnx2fc_hba *hba = handle;
- struct fc_lport *lport = hba->ctlr.lp;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
+ struct fc_lport *lport;
- BNX2FC_MISC_DBG("Entered %s\n", __func__);
mutex_lock(&bnx2fc_dev_lock);
- if (test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done))
- goto start_disc;
-
- if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done))
+ if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags))
bnx2fc_fw_init(hba);
-start_disc:
- mutex_unlock(&bnx2fc_dev_lock);
-
BNX2FC_MISC_DBG("bnx2fc started.\n");
- /* Kick off Fabric discovery*/
- if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
- printk(KERN_ERR PFX "ulp_init: start discovery\n");
- lport->tt.frame_send = bnx2fc_xmit;
- bnx2fc_start_disc(hba);
+ list_for_each_entry(interface, &if_list, list) {
+ if (interface->hba == hba) {
+ ctlr = bnx2fc_to_ctlr(interface);
+ lport = ctlr->lp;
+ /* Kick off Fabric discovery*/
+ printk(KERN_ERR PFX "ulp_init: start discovery\n");
+ lport->tt.frame_send = bnx2fc_xmit;
+ bnx2fc_start_disc(interface);
+ }
}
+
+ mutex_unlock(&bnx2fc_dev_lock);
}
static void bnx2fc_port_shutdown(struct fc_lport *lport)
@@ -1552,37 +1854,26 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport)
fc_lport_destroy(lport);
}
-static void bnx2fc_stop(struct bnx2fc_hba *hba)
+static void bnx2fc_stop(struct bnx2fc_interface *interface)
{
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
struct fc_lport *lport;
struct fc_lport *vport;
- BNX2FC_MISC_DBG("ENTERED %s - init_done = %ld\n", __func__,
- hba->init_done);
- if (test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done) &&
- test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
- lport = hba->ctlr.lp;
- bnx2fc_port_shutdown(lport);
- BNX2FC_HBA_DBG(lport, "bnx2fc_stop: waiting for %d "
- "offloaded sessions\n",
- hba->num_ofld_sess);
- wait_event_interruptible(hba->shutdown_wait,
- (hba->num_ofld_sess == 0));
- mutex_lock(&lport->lp_mutex);
- list_for_each_entry(vport, &lport->vports, list)
- fc_host_port_type(vport->host) = FC_PORTTYPE_UNKNOWN;
- mutex_unlock(&lport->lp_mutex);
- fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
- fcoe_ctlr_link_down(&hba->ctlr);
- fcoe_clean_pending_queue(lport);
-
- mutex_lock(&hba->hba_mutex);
- clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
- clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
+ if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags))
+ return;
- clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
- mutex_unlock(&hba->hba_mutex);
- }
+ lport = ctlr->lp;
+ bnx2fc_port_shutdown(lport);
+
+ mutex_lock(&lport->lp_mutex);
+ list_for_each_entry(vport, &lport->vports, list)
+ fc_host_port_type(vport->host) =
+ FC_PORTTYPE_UNKNOWN;
+ mutex_unlock(&lport->lp_mutex);
+ fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
+ fcoe_ctlr_link_down(ctlr);
+ fcoe_clean_pending_queue(lport);
}
static int bnx2fc_fw_init(struct bnx2fc_hba *hba)
@@ -1621,8 +1912,7 @@ static int bnx2fc_fw_init(struct bnx2fc_hba *hba)
}
- /* Mark HBA to indicate that the FW INIT is done */
- set_bit(BNX2FC_FW_INIT_DONE, &hba->init_done);
+ set_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags);
return 0;
err_unbind:
@@ -1633,22 +1923,22 @@ err_out:
static void bnx2fc_fw_destroy(struct bnx2fc_hba *hba)
{
- if (test_and_clear_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
+ if (test_and_clear_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags)) {
if (bnx2fc_send_fw_fcoe_destroy_msg(hba) == 0) {
- init_timer(&hba->destroy_timer);
+ timer_setup(&hba->destroy_timer, bnx2fc_destroy_timer,
+ 0);
hba->destroy_timer.expires = BNX2FC_FW_TIMEOUT +
jiffies;
- hba->destroy_timer.function = bnx2fc_destroy_timer;
- hba->destroy_timer.data = (unsigned long)hba;
add_timer(&hba->destroy_timer);
wait_event_interruptible(hba->destroy_wait,
- (hba->flags &
- BNX2FC_FLAG_DESTROY_CMPL));
+ test_bit(BNX2FC_FLAG_DESTROY_CMPL,
+ &hba->flags));
+ clear_bit(BNX2FC_FLAG_DESTROY_CMPL, &hba->flags);
/* This should never happen */
if (signal_pending(current))
flush_signals(current);
- del_timer_sync(&hba->destroy_timer);
+ timer_delete_sync(&hba->destroy_timer);
}
bnx2fc_unbind_adapter_devices(hba);
}
@@ -1664,45 +1954,68 @@ static void bnx2fc_fw_destroy(struct bnx2fc_hba *hba)
*/
static void bnx2fc_ulp_stop(void *handle)
{
- struct bnx2fc_hba *hba = (struct bnx2fc_hba *)handle;
+ struct bnx2fc_hba *hba = handle;
+ struct bnx2fc_interface *interface;
printk(KERN_ERR "ULP_STOP\n");
mutex_lock(&bnx2fc_dev_lock);
- bnx2fc_stop(hba);
+ if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags))
+ goto exit;
+ list_for_each_entry(interface, &if_list, list) {
+ if (interface->hba == hba)
+ bnx2fc_stop(interface);
+ }
+ BUG_ON(hba->num_ofld_sess != 0);
+
+ mutex_lock(&hba->hba_mutex);
+ clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+ clear_bit(ADAPTER_STATE_GOING_DOWN,
+ &hba->adapter_state);
+
+ clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
+ mutex_unlock(&hba->hba_mutex);
+
bnx2fc_fw_destroy(hba);
+exit:
mutex_unlock(&bnx2fc_dev_lock);
}
-static void bnx2fc_start_disc(struct bnx2fc_hba *hba)
+static void bnx2fc_start_disc(struct bnx2fc_interface *interface)
{
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
struct fc_lport *lport;
int wait_cnt = 0;
BNX2FC_MISC_DBG("Entered %s\n", __func__);
/* Kick off FIP/FLOGI */
- if (!test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
+ if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags)) {
printk(KERN_ERR PFX "Init not done yet\n");
return;
}
- lport = hba->ctlr.lp;
+ lport = ctlr->lp;
BNX2FC_HBA_DBG(lport, "calling fc_fabric_login\n");
- if (!bnx2fc_link_ok(lport)) {
+ if (!bnx2fc_link_ok(lport) && interface->enabled) {
BNX2FC_HBA_DBG(lport, "ctlr_link_up\n");
- fcoe_ctlr_link_up(&hba->ctlr);
+ fcoe_ctlr_link_up(ctlr);
fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
- set_bit(ADAPTER_STATE_READY, &hba->adapter_state);
+ set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
}
/* wait for the FCF to be selected before issuing FLOGI */
- while (!hba->ctlr.sel_fcf) {
+ while (!ctlr->sel_fcf) {
msleep(250);
/* give up after 3 secs */
if (++wait_cnt > 12)
break;
}
+
+ /* Reset max receive frame size to default */
+ if (fc_set_mfs(lport, BNX2FC_MFS))
+ return;
+
fc_lport_init(lport);
fc_fabric_login(lport);
}
@@ -1723,157 +2036,268 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev)
BNX2FC_MISC_DBG("Entered %s\n", __func__);
/* bnx2fc works only when bnx2x is loaded */
- if (!test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
+ if (!test_bit(CNIC_F_BNX2X_CLASS, &dev->flags) ||
+ (dev->max_fcoe_conn == 0)) {
printk(KERN_ERR PFX "bnx2fc FCoE not supported on %s,"
- " flags: %lx\n",
- dev->netdev->name, dev->flags);
+ " flags: %lx fcoe_conn: %d\n",
+ dev->netdev->name, dev->flags, dev->max_fcoe_conn);
return;
}
- /* Configure FCoE interface */
- hba = bnx2fc_interface_create(dev);
+ hba = bnx2fc_hba_create(dev);
if (!hba) {
printk(KERN_ERR PFX "hba initialization failed\n");
return;
}
+ pr_info(PFX "FCoE initialized for %s.\n", dev->netdev->name);
+
/* Add HBA to the adapter list */
mutex_lock(&bnx2fc_dev_lock);
- list_add_tail(&hba->link, &adapter_list);
+ list_add_tail(&hba->list, &adapter_list);
adapter_count++;
mutex_unlock(&bnx2fc_dev_lock);
+ dev->fcoe_cap = &hba->fcoe_cap;
clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic);
rc = dev->register_device(dev, CNIC_ULP_FCOE,
(void *) hba);
if (rc)
- printk(KERN_ALERT PFX "register_device failed, rc = %d\n", rc);
+ printk(KERN_ERR PFX "register_device failed, rc = %d\n", rc);
else
set_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic);
}
+/* Assumes rtnl_lock and the bnx2fc_dev_lock are already taken */
+static int __bnx2fc_disable(struct fcoe_ctlr *ctlr)
+{
+ struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr);
+
+ if (interface->enabled) {
+ if (!ctlr->lp) {
+ pr_err(PFX "__bnx2fc_disable: lport not found\n");
+ return -ENODEV;
+ } else {
+ interface->enabled = false;
+ fcoe_ctlr_link_down(ctlr);
+ fcoe_clean_pending_queue(ctlr->lp);
+ }
+ }
+ return 0;
+}
+/*
+ * Deperecated: Use bnx2fc_enabled()
+ */
static int bnx2fc_disable(struct net_device *netdev)
{
- struct bnx2fc_hba *hba;
- struct net_device *phys_dev;
- struct ethtool_drvinfo drvinfo;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
int rc = 0;
rtnl_lock();
-
mutex_lock(&bnx2fc_dev_lock);
- /* obtain physical netdev */
- if (netdev->priv_flags & IFF_802_1Q_VLAN)
- phys_dev = vlan_dev_real_dev(netdev);
- else {
- printk(KERN_ERR PFX "Not a vlan device\n");
- rc = -ENODEV;
- goto nodev;
- }
+ interface = bnx2fc_interface_lookup(netdev);
+ ctlr = bnx2fc_to_ctlr(interface);
- /* verify if the physical device is a netxtreme2 device */
- if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) {
- memset(&drvinfo, 0, sizeof(drvinfo));
- phys_dev->ethtool_ops->get_drvinfo(phys_dev, &drvinfo);
- if (strcmp(drvinfo.driver, "bnx2x")) {
- printk(KERN_ERR PFX "Not a netxtreme2 device\n");
- rc = -ENODEV;
- goto nodev;
- }
- } else {
- printk(KERN_ERR PFX "unable to obtain drv_info\n");
+ if (!interface) {
rc = -ENODEV;
- goto nodev;
- }
-
- printk(KERN_ERR PFX "phys_dev is netxtreme2 device\n");
-
- /* obtain hba and initialize rest of the structure */
- hba = bnx2fc_hba_lookup(phys_dev);
- if (!hba || !hba->ctlr.lp) {
- rc = -ENODEV;
- printk(KERN_ERR PFX "bnx2fc_disable: hba or lport not found\n");
+ pr_err(PFX "bnx2fc_disable: interface not found\n");
} else {
- fcoe_ctlr_link_down(&hba->ctlr);
- fcoe_clean_pending_queue(hba->ctlr.lp);
+ rc = __bnx2fc_disable(ctlr);
}
-
-nodev:
mutex_unlock(&bnx2fc_dev_lock);
rtnl_unlock();
return rc;
}
+static uint bnx2fc_npiv_create_vports(struct fc_lport *lport,
+ struct cnic_fc_npiv_tbl *npiv_tbl)
+{
+ struct fc_vport_identifiers vpid;
+ uint i, created = 0;
+ u64 wwnn = 0;
+ char wwpn_str[32];
+ char wwnn_str[32];
+
+ if (npiv_tbl->count > MAX_NPIV_ENTRIES) {
+ BNX2FC_HBA_DBG(lport, "Exceeded count max of npiv table\n");
+ goto done;
+ }
+
+ /* Sanity check the first entry to make sure it's not 0 */
+ if (wwn_to_u64(npiv_tbl->wwnn[0]) == 0 &&
+ wwn_to_u64(npiv_tbl->wwpn[0]) == 0) {
+ BNX2FC_HBA_DBG(lport, "First NPIV table entries invalid.\n");
+ goto done;
+ }
+
+ vpid.roles = FC_PORT_ROLE_FCP_INITIATOR;
+ vpid.vport_type = FC_PORTTYPE_NPIV;
+ vpid.disable = false;
+
+ for (i = 0; i < npiv_tbl->count; i++) {
+ wwnn = wwn_to_u64(npiv_tbl->wwnn[i]);
+ if (wwnn == 0) {
+ /*
+ * If we get a 0 element from for the WWNN then assume
+ * the WWNN should be the same as the physical port.
+ */
+ wwnn = lport->wwnn;
+ }
+ vpid.node_name = wwnn;
+ vpid.port_name = wwn_to_u64(npiv_tbl->wwpn[i]);
+ scnprintf(vpid.symbolic_name, sizeof(vpid.symbolic_name),
+ "NPIV[%u]:%016llx-%016llx",
+ created, vpid.port_name, vpid.node_name);
+ fcoe_wwn_to_str(vpid.node_name, wwnn_str, sizeof(wwnn_str));
+ fcoe_wwn_to_str(vpid.port_name, wwpn_str, sizeof(wwpn_str));
+ BNX2FC_HBA_DBG(lport, "Creating vport %s:%s.\n", wwnn_str,
+ wwpn_str);
+ if (fc_vport_create(lport->host, 0, &vpid))
+ created++;
+ else
+ BNX2FC_HBA_DBG(lport, "Failed to create vport\n");
+ }
+done:
+ return created;
+}
+
+static int __bnx2fc_enable(struct fcoe_ctlr *ctlr)
+{
+ struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr);
+ struct bnx2fc_hba *hba;
+ struct cnic_fc_npiv_tbl *npiv_tbl;
+ struct fc_lport *lport;
+
+ if (!interface->enabled) {
+ if (!ctlr->lp) {
+ pr_err(PFX "__bnx2fc_enable: lport not found\n");
+ return -ENODEV;
+ } else if (!bnx2fc_link_ok(ctlr->lp)) {
+ fcoe_ctlr_link_up(ctlr);
+ interface->enabled = true;
+ }
+ }
+
+ /* Create static NPIV ports if any are contained in NVRAM */
+ hba = interface->hba;
+ lport = ctlr->lp;
+
+ if (!hba)
+ goto done;
+
+ if (!hba->cnic)
+ goto done;
+ if (!lport)
+ goto done;
+
+ if (!lport->host)
+ goto done;
+
+ if (!hba->cnic->get_fc_npiv_tbl)
+ goto done;
+
+ npiv_tbl = kzalloc(sizeof(struct cnic_fc_npiv_tbl), GFP_KERNEL);
+ if (!npiv_tbl)
+ goto done;
+
+ if (hba->cnic->get_fc_npiv_tbl(hba->cnic, npiv_tbl))
+ goto done_free;
+
+ bnx2fc_npiv_create_vports(lport, npiv_tbl);
+done_free:
+ kfree(npiv_tbl);
+done:
+ return 0;
+}
+
+/*
+ * Deprecated: Use bnx2fc_enabled()
+ */
static int bnx2fc_enable(struct net_device *netdev)
{
- struct bnx2fc_hba *hba;
- struct net_device *phys_dev;
- struct ethtool_drvinfo drvinfo;
+ struct bnx2fc_interface *interface;
+ struct fcoe_ctlr *ctlr;
int rc = 0;
rtnl_lock();
-
- BNX2FC_MISC_DBG("Entered %s\n", __func__);
mutex_lock(&bnx2fc_dev_lock);
- /* obtain physical netdev */
- if (netdev->priv_flags & IFF_802_1Q_VLAN)
- phys_dev = vlan_dev_real_dev(netdev);
- else {
- printk(KERN_ERR PFX "Not a vlan device\n");
+ interface = bnx2fc_interface_lookup(netdev);
+ ctlr = bnx2fc_to_ctlr(interface);
+ if (!interface) {
rc = -ENODEV;
- goto nodev;
- }
- /* verify if the physical device is a netxtreme2 device */
- if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) {
- memset(&drvinfo, 0, sizeof(drvinfo));
- phys_dev->ethtool_ops->get_drvinfo(phys_dev, &drvinfo);
- if (strcmp(drvinfo.driver, "bnx2x")) {
- printk(KERN_ERR PFX "Not a netxtreme2 device\n");
- rc = -ENODEV;
- goto nodev;
- }
+ pr_err(PFX "bnx2fc_enable: interface not found\n");
} else {
- printk(KERN_ERR PFX "unable to obtain drv_info\n");
- rc = -ENODEV;
- goto nodev;
+ rc = __bnx2fc_enable(ctlr);
}
- /* obtain hba and initialize rest of the structure */
- hba = bnx2fc_hba_lookup(phys_dev);
- if (!hba || !hba->ctlr.lp) {
- rc = -ENODEV;
- printk(KERN_ERR PFX "bnx2fc_enable: hba or lport not found\n");
- } else if (!bnx2fc_link_ok(hba->ctlr.lp))
- fcoe_ctlr_link_up(&hba->ctlr);
-
-nodev:
mutex_unlock(&bnx2fc_dev_lock);
rtnl_unlock();
return rc;
}
/**
- * bnx2fc_create - Create bnx2fc FCoE interface
+ * bnx2fc_ctlr_enabled() - Enable or disable an FCoE Controller
+ * @cdev: The FCoE Controller that is being enabled or disabled
*
- * @buffer: The name of Ethernet interface to create on
- * @kp: The associated kernel param
+ * fcoe_sysfs will ensure that the state of 'enabled' has
+ * changed, so no checking is necessary here. This routine simply
+ * calls fcoe_enable or fcoe_disable, both of which are deprecated.
+ * When those routines are removed the functionality can be merged
+ * here.
+ */
+static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev)
+{
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev);
+
+ switch (cdev->enabled) {
+ case FCOE_CTLR_ENABLED:
+ return __bnx2fc_enable(ctlr);
+ case FCOE_CTLR_DISABLED:
+ return __bnx2fc_disable(ctlr);
+ case FCOE_CTLR_UNUSED:
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+enum bnx2fc_create_link_state {
+ BNX2FC_CREATE_LINK_DOWN,
+ BNX2FC_CREATE_LINK_UP,
+};
+
+/**
+ * _bnx2fc_create() - Create bnx2fc FCoE interface
+ * @netdev : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
+ * @link_state: The ctlr link state on creation
*
- * Called from sysfs.
+ * Called from either the libfcoe 'create' module parameter
+ * via fcoe_create or from fcoe_syfs's ctlr_create file.
+ *
+ * libfcoe's 'create' module parameter is deprecated so some
+ * consolidation of code can be done when that interface is
+ * removed.
*
* Returns: 0 for success
*/
-static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
+static int _bnx2fc_create(struct net_device *netdev,
+ enum fip_mode fip_mode,
+ enum bnx2fc_create_link_state link_state)
{
+ struct fcoe_ctlr_device *cdev;
+ struct fcoe_ctlr *ctlr;
+ struct bnx2fc_interface *interface;
struct bnx2fc_hba *hba;
- struct net_device *phys_dev;
+ struct net_device *phys_dev = netdev;
struct fc_lport *lport;
struct ethtool_drvinfo drvinfo;
int rc = 0;
- int vlan_id;
+ int vlan_id = 0;
BNX2FC_MISC_DBG("Entered bnx2fc_create\n");
if (fip_mode != FIP_MODE_FABRIC) {
@@ -1891,19 +2315,14 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
}
/* obtain physical netdev */
- if (netdev->priv_flags & IFF_802_1Q_VLAN) {
+ if (is_vlan_dev(netdev))
phys_dev = vlan_dev_real_dev(netdev);
- vlan_id = vlan_dev_vlan_id(netdev);
- } else {
- printk(KERN_ERR PFX "Not a vlan device\n");
- rc = -EINVAL;
- goto netdev_err;
- }
+
/* verify if the physical device is a netxtreme2 device */
if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) {
memset(&drvinfo, 0, sizeof(drvinfo));
phys_dev->ethtool_ops->get_drvinfo(phys_dev, &drvinfo);
- if (strcmp(drvinfo.driver, "bnx2x")) {
+ if (strncmp(drvinfo.driver, "bnx2x", strlen("bnx2x"))) {
printk(KERN_ERR PFX "Not a netxtreme2 device\n");
rc = -EINVAL;
goto netdev_err;
@@ -1914,7 +2333,7 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
goto netdev_err;
}
- /* obtain hba and initialize rest of the structure */
+ /* obtain interface and initialize rest of the structure */
hba = bnx2fc_hba_lookup(phys_dev);
if (!hba) {
rc = -ENODEV;
@@ -1922,67 +2341,86 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
goto netdev_err;
}
- if (!test_bit(BNX2FC_FW_INIT_DONE, &hba->init_done)) {
- rc = bnx2fc_fw_init(hba);
- if (rc)
- goto netdev_err;
- }
-
- if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
+ if (bnx2fc_interface_lookup(netdev)) {
rc = -EEXIST;
goto netdev_err;
}
- /* update netdev with vlan netdev */
- hba->netdev = netdev;
- hba->vlan_id = vlan_id;
- hba->vlan_enabled = 1;
+ interface = bnx2fc_interface_create(hba, netdev, fip_mode);
+ if (!interface) {
+ printk(KERN_ERR PFX "bnx2fc_interface_create failed\n");
+ rc = -ENOMEM;
+ goto netdev_err;
+ }
- rc = bnx2fc_interface_setup(hba, fip_mode);
- if (rc) {
- printk(KERN_ERR PFX "bnx2fc_interface_setup failed\n");
- goto ifput_err;
+ if (is_vlan_dev(netdev)) {
+ vlan_id = vlan_dev_vlan_id(netdev);
+ interface->vlan_enabled = 1;
}
- hba->timer_work_queue =
- create_singlethread_workqueue("bnx2fc_timer_wq");
- if (!hba->timer_work_queue) {
+ ctlr = bnx2fc_to_ctlr(interface);
+ cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
+ interface->vlan_id = vlan_id;
+ interface->tm_timeout = BNX2FC_TM_TIMEOUT;
+
+ interface->timer_work_queue = alloc_ordered_workqueue(
+ "%s", WQ_MEM_RECLAIM, "bnx2fc_timer_wq");
+ if (!interface->timer_work_queue) {
printk(KERN_ERR PFX "ulp_init could not create timer_wq\n");
rc = -EINVAL;
goto ifput_err;
}
- lport = bnx2fc_if_create(hba, &hba->pcidev->dev, 0);
+ lport = bnx2fc_if_create(interface, &cdev->dev, 0);
if (!lport) {
printk(KERN_ERR PFX "Failed to create interface (%s)\n",
netdev->name);
- bnx2fc_netdev_cleanup(hba);
rc = -EINVAL;
goto if_create_err;
}
+ /* Add interface to if_list */
+ list_add_tail(&interface->list, &if_list);
+
lport->boot_time = jiffies;
/* Make this master N_port */
- hba->ctlr.lp = lport;
+ ctlr->lp = lport;
+
+ if (link_state == BNX2FC_CREATE_LINK_UP)
+ cdev->enabled = FCOE_CTLR_ENABLED;
+ else
+ cdev->enabled = FCOE_CTLR_DISABLED;
+
+ if (link_state == BNX2FC_CREATE_LINK_UP &&
+ !bnx2fc_link_ok(lport)) {
+ fcoe_ctlr_link_up(ctlr);
+ fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
+ set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
+ }
+
+ BNX2FC_HBA_DBG(lport, "create: START DISC\n");
+ bnx2fc_start_disc(interface);
+
+ if (link_state == BNX2FC_CREATE_LINK_UP)
+ interface->enabled = true;
- set_bit(BNX2FC_CREATE_DONE, &hba->init_done);
- printk(KERN_ERR PFX "create: START DISC\n");
- bnx2fc_start_disc(hba);
/*
* Release from kref_init in bnx2fc_interface_setup, on success
* lport should be holding a reference taken in bnx2fc_if_create
*/
- bnx2fc_interface_put(hba);
+ bnx2fc_interface_put(interface);
/* put netdev that was held while calling dev_get_by_name */
mutex_unlock(&bnx2fc_dev_lock);
rtnl_unlock();
return 0;
if_create_err:
- destroy_workqueue(hba->timer_work_queue);
+ destroy_workqueue(interface->timer_work_queue);
ifput_err:
- bnx2fc_interface_put(hba);
+ bnx2fc_net_cleanup(interface);
+ bnx2fc_interface_put(interface);
+ goto mod_err;
netdev_err:
module_put(THIS_MODULE);
mod_err:
@@ -1992,50 +2430,90 @@ mod_err:
}
/**
- * bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc adapter instance
+ * bnx2fc_create() - Create a bnx2fc interface
+ * @netdev : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
+ *
+ * Called from fcoe transport
+ *
+ * Returns: 0 for success
+ */
+static int bnx2fc_create(struct net_device *netdev, enum fip_mode fip_mode)
+{
+ return _bnx2fc_create(netdev, fip_mode, BNX2FC_CREATE_LINK_UP);
+}
+
+/**
+ * bnx2fc_ctlr_alloc() - Allocate a bnx2fc interface from fcoe_sysfs
+ * @netdev: The net_device to be used by the allocated FCoE Controller
+ *
+ * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr
+ * in a link_down state. The allows the user an opportunity to configure
+ * the FCoE Controller from sysfs before enabling the FCoE Controller.
+ *
+ * Creating in with this routine starts the FCoE Controller in Fabric
+ * mode. The user can change to VN2VN or another mode before enabling.
+ */
+static int bnx2fc_ctlr_alloc(struct net_device *netdev)
+{
+ return _bnx2fc_create(netdev, FIP_MODE_FABRIC,
+ BNX2FC_CREATE_LINK_DOWN);
+}
+
+/**
+ * bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc hba instance
*
* @cnic: Pointer to cnic device instance
*
**/
static struct bnx2fc_hba *bnx2fc_find_hba_for_cnic(struct cnic_dev *cnic)
{
- struct list_head *list;
- struct list_head *temp;
struct bnx2fc_hba *hba;
/* Called with bnx2fc_dev_lock held */
- list_for_each_safe(list, temp, &adapter_list) {
- hba = (struct bnx2fc_hba *)list;
+ list_for_each_entry(hba, &adapter_list, list) {
if (hba->cnic == cnic)
return hba;
}
return NULL;
}
-static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device *phys_dev)
+static struct bnx2fc_interface *bnx2fc_interface_lookup(struct net_device
+ *netdev)
+{
+ struct bnx2fc_interface *interface;
+
+ /* Called with bnx2fc_dev_lock held */
+ list_for_each_entry(interface, &if_list, list) {
+ if (interface->netdev == netdev)
+ return interface;
+ }
+ return NULL;
+}
+
+static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device
+ *phys_dev)
{
- struct list_head *list;
- struct list_head *temp;
struct bnx2fc_hba *hba;
/* Called with bnx2fc_dev_lock held */
- list_for_each_safe(list, temp, &adapter_list) {
- hba = (struct bnx2fc_hba *)list;
+ list_for_each_entry(hba, &adapter_list, list) {
if (hba->phys_dev == phys_dev)
return hba;
}
- printk(KERN_ERR PFX "hba_lookup: hba NULL\n");
+ printk(KERN_ERR PFX "adapter_lookup: hba NULL\n");
return NULL;
}
/**
* bnx2fc_ulp_exit - shuts down adapter instance and frees all resources
*
- * @dev cnic device handle
+ * @dev: cnic device handle
*/
static void bnx2fc_ulp_exit(struct cnic_dev *dev)
{
struct bnx2fc_hba *hba;
+ struct bnx2fc_interface *interface, *tmp;
BNX2FC_MISC_DBG("Entered bnx2fc_ulp_exit\n");
@@ -2054,21 +2532,25 @@ static void bnx2fc_ulp_exit(struct cnic_dev *dev)
return;
}
- list_del_init(&hba->link);
+ list_del_init(&hba->list);
adapter_count--;
- if (test_bit(BNX2FC_CREATE_DONE, &hba->init_done)) {
+ list_for_each_entry_safe(interface, tmp, &if_list, list)
/* destroy not called yet, move to quiesced list */
- bnx2fc_netdev_cleanup(hba);
- bnx2fc_if_destroy(hba->ctlr.lp);
- }
+ if (interface->hba == hba)
+ __bnx2fc_destroy(interface);
mutex_unlock(&bnx2fc_dev_lock);
bnx2fc_ulp_stop(hba);
/* unregister cnic device */
if (test_and_clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic))
hba->cnic->unregister_device(hba->cnic, CNIC_ULP_FCOE);
- bnx2fc_interface_destroy(hba);
+ bnx2fc_hba_destroy(hba);
+}
+
+static void bnx2fc_rport_terminate_io(struct fc_rport *rport)
+{
+ /* This is a no-op */
}
/**
@@ -2088,15 +2570,17 @@ static int bnx2fc_fcoe_reset(struct Scsi_Host *shost)
static bool bnx2fc_match(struct net_device *netdev)
{
+ struct net_device *phys_dev = netdev;
+
mutex_lock(&bnx2fc_dev_lock);
- if (netdev->priv_flags & IFF_802_1Q_VLAN) {
- struct net_device *phys_dev = vlan_dev_real_dev(netdev);
+ if (is_vlan_dev(netdev))
+ phys_dev = vlan_dev_real_dev(netdev);
- if (bnx2fc_hba_lookup(phys_dev)) {
- mutex_unlock(&bnx2fc_dev_lock);
- return true;
- }
+ if (bnx2fc_hba_lookup(phys_dev)) {
+ mutex_unlock(&bnx2fc_dev_lock);
+ return true;
}
+
mutex_unlock(&bnx2fc_dev_lock);
return false;
}
@@ -2106,6 +2590,7 @@ static struct fcoe_transport bnx2fc_transport = {
.name = {"bnx2fc"},
.attached = false,
.list = LIST_HEAD_INIT(bnx2fc_transport.list),
+ .alloc = bnx2fc_ctlr_alloc,
.match = bnx2fc_match,
.create = bnx2fc_create,
.destroy = bnx2fc_destroy,
@@ -2114,35 +2599,32 @@ static struct fcoe_transport bnx2fc_transport = {
};
/**
- * bnx2fc_percpu_thread_create - Create a receive thread for an
- * online CPU
+ * bnx2fc_cpu_online - Create a receive thread for an online CPU
*
* @cpu: cpu index for the online cpu
*/
-static void bnx2fc_percpu_thread_create(unsigned int cpu)
+static int bnx2fc_cpu_online(unsigned int cpu)
{
struct bnx2fc_percpu_s *p;
struct task_struct *thread;
p = &per_cpu(bnx2fc_percpu, cpu);
- thread = kthread_create(bnx2fc_percpu_io_thread,
- (void *)p,
- "bnx2fc_thread/%d", cpu);
- /* bind thread to the cpu */
- if (likely(!IS_ERR(p->iothread))) {
- kthread_bind(thread, cpu);
- p->iothread = thread;
- wake_up_process(thread);
- }
+ thread = kthread_create_on_cpu(bnx2fc_percpu_io_thread,
+ (void *)p, cpu, "bnx2fc_thread/%d");
+ if (IS_ERR(thread))
+ return PTR_ERR(thread);
+
+ p->iothread = thread;
+ wake_up_process(thread);
+ return 0;
}
-static void bnx2fc_percpu_thread_destroy(unsigned int cpu)
+static int bnx2fc_cpu_offline(unsigned int cpu)
{
struct bnx2fc_percpu_s *p;
struct task_struct *thread;
struct bnx2fc_work *work, *tmp;
- LIST_HEAD(work_list);
BNX2FC_MISC_DBG("destroying io thread for CPU %d\n", cpu);
@@ -2152,11 +2634,11 @@ static void bnx2fc_percpu_thread_destroy(unsigned int cpu)
thread = p->iothread;
p->iothread = NULL;
-
/* Free all work in the list */
- list_for_each_entry_safe(work, tmp, &work_list, list) {
+ list_for_each_entry_safe(work, tmp, &p->work_list, list) {
list_del_init(&work->list);
- bnx2fc_process_cq_compl(work->tgt, work->wqe);
+ bnx2fc_process_cq_compl(work->tgt, work->wqe, work->rq_data,
+ work->num_rq, work->task);
kfree(work);
}
@@ -2164,41 +2646,21 @@ static void bnx2fc_percpu_thread_destroy(unsigned int cpu)
if (thread)
kthread_stop(thread);
+ return 0;
}
-/**
- * bnx2fc_cpu_callback - Handler for CPU hotplug events
- *
- * @nfb: The callback data block
- * @action: The event triggering the callback
- * @hcpu: The index of the CPU that the event is for
- *
- * This creates or destroys per-CPU data for fcoe
- *
- * Returns NOTIFY_OK always.
- */
-static int bnx2fc_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
+static int bnx2fc_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
- unsigned cpu = (unsigned long)hcpu;
+ if (!bnx2fc_queue_depth)
+ return 0;
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- printk(PFX "CPU %x online: Create Rx thread\n", cpu);
- bnx2fc_percpu_thread_create(cpu);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- printk(PFX "CPU %x offline: Remove Rx thread\n", cpu);
- bnx2fc_percpu_thread_destroy(cpu);
- break;
- default:
- break;
- }
- return NOTIFY_OK;
+ scsi_change_queue_depth(sdev, bnx2fc_queue_depth);
+ return 0;
}
+static enum cpuhp_state bnx2fc_online_state;
+
/**
* bnx2fc_mod_init - module init entry point
*
@@ -2224,6 +2686,7 @@ static int __init bnx2fc_mod_init(void)
}
INIT_LIST_HEAD(&adapter_list);
+ INIT_LIST_HEAD(&if_list);
mutex_init(&bnx2fc_dev_lock);
adapter_count = 0;
@@ -2232,7 +2695,7 @@ static int __init bnx2fc_mod_init(void)
if (rc)
goto detach_ft;
- bnx2fc_wq = alloc_workqueue("bnx2fc", 0, 0);
+ bnx2fc_wq = alloc_workqueue("bnx2fc", WQ_PERCPU, 0);
if (!bnx2fc_wq) {
rc = -ENOMEM;
goto release_bt;
@@ -2240,16 +2703,15 @@ static int __init bnx2fc_mod_init(void)
bg = &bnx2fc_global;
skb_queue_head_init(&bg->fcoe_rx_list);
- l2_thread = kthread_create(bnx2fc_l2_rcv_thread,
- (void *)bg,
- "bnx2fc_l2_thread");
+ l2_thread = kthread_run(bnx2fc_l2_rcv_thread,
+ (void *)bg,
+ "bnx2fc_l2_thread");
if (IS_ERR(l2_thread)) {
rc = PTR_ERR(l2_thread);
goto free_wq;
}
- wake_up_process(l2_thread);
spin_lock_bh(&bg->fcoe_rx_list.lock);
- bg->thread = l2_thread;
+ bg->kthread = l2_thread;
spin_unlock_bh(&bg->fcoe_rx_list.lock);
for_each_possible_cpu(cpu) {
@@ -2258,17 +2720,17 @@ static int __init bnx2fc_mod_init(void)
spin_lock_init(&p->fp_work_lock);
}
- for_each_online_cpu(cpu) {
- bnx2fc_percpu_thread_create(cpu);
- }
-
- /* Initialize per CPU interrupt thread */
- register_hotcpu_notifier(&bnx2fc_cpu_notifier);
+ rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "scsi/bnx2fc:online",
+ bnx2fc_cpu_online, bnx2fc_cpu_offline);
+ if (rc < 0)
+ goto stop_thread;
+ bnx2fc_online_state = rc;
cnic_register_driver(CNIC_ULP_FCOE, &bnx2fc_cnic_cb);
-
return 0;
+stop_thread:
+ kthread_stop(l2_thread);
free_wq:
destroy_workqueue(bnx2fc_wq);
release_bt:
@@ -2286,7 +2748,6 @@ static void __exit bnx2fc_mod_exit(void)
struct fcoe_percpu_s *bg;
struct task_struct *l2_thread;
struct sk_buff *skb;
- unsigned int cpu = 0;
/*
* NOTE: Since cnic calls register_driver routine rtnl_lock,
@@ -2295,30 +2756,30 @@ static void __exit bnx2fc_mod_exit(void)
* held.
*/
mutex_lock(&bnx2fc_dev_lock);
- list_splice(&adapter_list, &to_be_deleted);
- INIT_LIST_HEAD(&adapter_list);
+ list_splice_init(&adapter_list, &to_be_deleted);
adapter_count = 0;
mutex_unlock(&bnx2fc_dev_lock);
/* Unregister with cnic */
- list_for_each_entry_safe(hba, next, &to_be_deleted, link) {
- list_del_init(&hba->link);
- printk(KERN_ERR PFX "MOD_EXIT:destroy hba = 0x%p, kref = %d\n",
- hba, atomic_read(&hba->kref.refcount));
+ list_for_each_entry_safe(hba, next, &to_be_deleted, list) {
+ list_del_init(&hba->list);
+ printk(KERN_ERR PFX "MOD_EXIT:destroy hba = 0x%p\n",
+ hba);
bnx2fc_ulp_stop(hba);
/* unregister cnic device */
if (test_and_clear_bit(BNX2FC_CNIC_REGISTERED,
&hba->reg_with_cnic))
- hba->cnic->unregister_device(hba->cnic, CNIC_ULP_FCOE);
- bnx2fc_interface_destroy(hba);
+ hba->cnic->unregister_device(hba->cnic,
+ CNIC_ULP_FCOE);
+ bnx2fc_hba_destroy(hba);
}
cnic_unregister_driver(CNIC_ULP_FCOE);
/* Destroy global thread */
bg = &bnx2fc_global;
spin_lock_bh(&bg->fcoe_rx_list.lock);
- l2_thread = bg->thread;
- bg->thread = NULL;
+ l2_thread = bg->kthread;
+ bg->kthread = NULL;
while ((skb = __skb_dequeue(&bg->fcoe_rx_list)) != NULL)
kfree_skb(skb);
@@ -2327,12 +2788,7 @@ static void __exit bnx2fc_mod_exit(void)
if (l2_thread)
kthread_stop(l2_thread);
- unregister_hotcpu_notifier(&bnx2fc_cpu_notifier);
-
- /* Destroy per cpu threads */
- for_each_online_cpu(cpu) {
- bnx2fc_percpu_thread_destroy(cpu);
- }
+ cpuhp_remove_state(bnx2fc_online_state);
destroy_workqueue(bnx2fc_wq);
/*
@@ -2348,6 +2804,19 @@ static void __exit bnx2fc_mod_exit(void)
module_init(bnx2fc_mod_init);
module_exit(bnx2fc_mod_exit);
+static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = {
+ .set_fcoe_ctlr_enabled = bnx2fc_ctlr_enabled,
+ .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb,
+
+ .get_fcoe_fcf_selected = fcoe_fcf_get_selected,
+ .get_fcoe_fcf_vlan_id = bnx2fc_fcf_get_vlan_id,
+};
+
static struct fc_function_template bnx2fc_transport_function = {
.show_host_node_name = 1,
.show_host_port_name = 1,
@@ -2380,11 +2849,12 @@ static struct fc_function_template bnx2fc_transport_function = {
.issue_fc_host_lip = bnx2fc_fcoe_reset,
- .terminate_rport_io = fc_rport_terminate_io,
+ .terminate_rport_io = bnx2fc_rport_terminate_io,
.vport_create = bnx2fc_vport_create,
.vport_delete = bnx2fc_vport_destroy,
.vport_disable = bnx2fc_vport_disable,
+ .bsg_request = fc_lport_bsg_request,
};
static struct fc_function_template bnx2fc_vport_xport_function = {
@@ -2418,28 +2888,78 @@ static struct fc_function_template bnx2fc_vport_xport_function = {
.get_fc_host_stats = fc_get_host_stats,
.issue_fc_host_lip = bnx2fc_fcoe_reset,
.terminate_rport_io = fc_rport_terminate_io,
+ .bsg_request = fc_lport_bsg_request,
};
-/**
+/*
+ * Additional scsi_host attributes.
+ */
+static ssize_t
+bnx2fc_tm_timeout_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct fc_lport *lport = shost_priv(shost);
+ struct fcoe_port *port = lport_priv(lport);
+ struct bnx2fc_interface *interface = port->priv;
+
+ sprintf(buf, "%u\n", interface->tm_timeout);
+ return strlen(buf);
+}
+
+static ssize_t
+bnx2fc_tm_timeout_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct fc_lport *lport = shost_priv(shost);
+ struct fcoe_port *port = lport_priv(lport);
+ struct bnx2fc_interface *interface = port->priv;
+ int rval, val;
+
+ rval = kstrtouint(buf, 10, &val);
+ if (rval)
+ return rval;
+ if (val > 255)
+ return -ERANGE;
+
+ interface->tm_timeout = (u8)val;
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(tm_timeout, S_IRUGO|S_IWUSR, bnx2fc_tm_timeout_show,
+ bnx2fc_tm_timeout_store);
+
+static struct attribute *bnx2fc_host_attrs[] = {
+ &dev_attr_tm_timeout.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(bnx2fc_host);
+
+/*
* scsi_host_template structure used while registering with SCSI-ml
*/
static struct scsi_host_template bnx2fc_shost_template = {
.module = THIS_MODULE,
- .name = "Broadcom Offload FCoE Initiator",
+ .name = "QLogic Offload FCoE Initiator",
.queuecommand = bnx2fc_queuecommand,
+ .eh_timed_out = fc_eh_timed_out,
.eh_abort_handler = bnx2fc_eh_abort, /* abts */
.eh_device_reset_handler = bnx2fc_eh_device_reset, /* lun reset */
.eh_target_reset_handler = bnx2fc_eh_target_reset, /* tgt reset */
.eh_host_reset_handler = fc_eh_host_reset,
- .slave_alloc = fc_slave_alloc,
- .change_queue_depth = fc_change_queue_depth,
- .change_queue_type = fc_change_queue_type,
+ .sdev_init = fc_sdev_init,
+ .change_queue_depth = scsi_change_queue_depth,
.this_id = -1,
.cmd_per_lun = 3,
- .can_queue = BNX2FC_CAN_QUEUE,
- .use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = BNX2FC_MAX_BDS_PER_CMD,
- .max_sectors = 512,
+ .dma_boundary = 0x7fff,
+ .max_sectors = 0x3fbf,
+ .track_queue_depth = 1,
+ .sdev_configure = bnx2fc_sdev_configure,
+ .shost_groups = bnx2fc_host_groups,
+ .cmd_size = sizeof(struct bnx2fc_priv),
};
static struct libfc_function_template bnx2fc_libfc_fcn_templ = {
@@ -2447,10 +2967,11 @@ static struct libfc_function_template bnx2fc_libfc_fcn_templ = {
.elsct_send = bnx2fc_elsct_send,
.fcp_abort_io = bnx2fc_abort_io,
.fcp_cleanup = bnx2fc_cleanup,
+ .get_lesb = fcoe_get_lesb,
.rport_event_callback = bnx2fc_rport_event_handler,
};
-/**
+/*
* bnx2fc_cnic_cb - global template of bnx2fc - cnic driver interface
* structure carrying callback function pointers
*/
@@ -2462,4 +2983,5 @@ static struct cnic_ulp_ops bnx2fc_cnic_cb = {
.cnic_stop = bnx2fc_ulp_stop,
.indicate_kcqes = bnx2fc_indicate_kcqe,
.indicate_netevent = bnx2fc_indicate_netevent,
+ .cnic_get_stats = bnx2fc_ulp_get_stats,
};
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index f756d5f85c7a..090d436bcef8 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
@@ -1,8 +1,10 @@
-/* bnx2fc_hwi.c: Broadcom NetXtreme II Linux FCoE offload driver.
+/* bnx2fc_hwi.c: QLogic Linux FCoE offload driver.
* This file contains the code that low level functions that interact
* with 57712 FCoE firmware.
*
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,7 +25,7 @@ static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba,
struct fcoe_kcqe *ofld_kcqe);
static void bnx2fc_init_failure(struct bnx2fc_hba *hba, u32 err_code);
static void bnx2fc_process_conn_destroy_cmpl(struct bnx2fc_hba *hba,
- struct fcoe_kcqe *conn_destroy);
+ struct fcoe_kcqe *destroy_kcqe);
int bnx2fc_send_stat_req(struct bnx2fc_hba *hba)
{
@@ -67,7 +69,7 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba)
int rc = 0;
if (!hba->cnic) {
- printk(KERN_ALERT PFX "hba->cnic NULL during fcoe fw init\n");
+ printk(KERN_ERR PFX "hba->cnic NULL during fcoe fw init\n");
return -ENODEV;
}
@@ -77,7 +79,7 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba)
fcoe_init1.hdr.flags = (FCOE_KWQE_LAYER_CODE <<
FCOE_KWQE_HEADER_LAYER_CODE_SHIFT);
- fcoe_init1.num_tasks = BNX2FC_MAX_TASKS;
+ fcoe_init1.num_tasks = hba->max_tasks;
fcoe_init1.sq_num_wqes = BNX2FC_SQ_WQES_MAX;
fcoe_init1.rq_num_wqes = BNX2FC_RQ_WQES_MAX;
fcoe_init1.rq_buffer_log_size = BNX2FC_RQ_BUF_LOG_SZ;
@@ -100,6 +102,10 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba)
fcoe_init2.hdr.flags = (FCOE_KWQE_LAYER_CODE <<
FCOE_KWQE_HEADER_LAYER_CODE_SHIFT);
+ fcoe_init2.hsi_major_version = FCOE_HSI_MAJOR_VERSION;
+ fcoe_init2.hsi_minor_version = FCOE_HSI_MINOR_VERSION;
+
+
fcoe_init2.hash_tbl_pbl_addr_lo = (u32) hba->hash_tbl_pbl_dma;
fcoe_init2.hash_tbl_pbl_addr_hi = (u32)
((u64) hba->hash_tbl_pbl_dma >> 32);
@@ -122,6 +128,11 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba)
fcoe_init3.error_bit_map_lo = 0xffffffff;
fcoe_init3.error_bit_map_hi = 0xffffffff;
+ /*
+ * enable both cached connection and cached tasks
+ * 0 = none, 1 = cached connection, 2 = cached tasks, 3 = both
+ */
+ fcoe_init3.perf_config = 3;
kwqe_arr[0] = (struct kwqe *) &fcoe_init1;
kwqe_arr[1] = (struct kwqe *) &fcoe_init2;
@@ -161,7 +172,9 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt)
{
struct fc_lport *lport = port->lport;
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+ struct bnx2fc_hba *hba = interface->hba;
struct kwqe *kwqe_arr[4];
struct fcoe_kwqe_conn_offload1 ofld_req1;
struct fcoe_kwqe_conn_offload2 ofld_req2;
@@ -223,7 +236,7 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
ofld_req3.hdr.flags =
(FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT);
- ofld_req3.vlan_tag = hba->vlan_id <<
+ ofld_req3.vlan_tag = interface->vlan_id <<
FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT;
ofld_req3.vlan_tag |= 3 << FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT;
@@ -273,11 +286,23 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
ofld_req3.flags |= (((rdata->sp_features & FC_SP_FT_SEQC) ? 1 : 0) <<
FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT_SHIFT);
+ /*
+ * Info from PRLI response, this info is used for sequence level error
+ * recovery support
+ */
+ if (tgt->dev_type == TYPE_TAPE) {
+ ofld_req3.flags |= 1 <<
+ FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT;
+ ofld_req3.flags |= (((rdata->flags & FC_RP_FLAGS_REC_SUPPORTED)
+ ? 1 : 0) <<
+ FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT);
+ }
+
/* vlan flag */
- ofld_req3.flags |= (hba->vlan_enabled <<
+ ofld_req3.flags |= (interface->vlan_enabled <<
FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT);
- /* C2_VALID and ACK flags are not set as they are not suppported */
+ /* C2_VALID and ACK flags are not set as they are not supported */
/* Initialize offload request 4 structure */
@@ -289,19 +314,20 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
ofld_req4.e_d_tov_timer_val = lport->e_d_tov / 20;
- ofld_req4.src_mac_addr_lo32[0] = port->data_src_addr[5];
+ ofld_req4.src_mac_addr_lo[0] = port->data_src_addr[5];
/* local mac */
- ofld_req4.src_mac_addr_lo32[1] = port->data_src_addr[4];
- ofld_req4.src_mac_addr_lo32[2] = port->data_src_addr[3];
- ofld_req4.src_mac_addr_lo32[3] = port->data_src_addr[2];
- ofld_req4.src_mac_addr_hi16[0] = port->data_src_addr[1];
- ofld_req4.src_mac_addr_hi16[1] = port->data_src_addr[0];
- ofld_req4.dst_mac_addr_lo32[0] = hba->ctlr.dest_addr[5];/* fcf mac */
- ofld_req4.dst_mac_addr_lo32[1] = hba->ctlr.dest_addr[4];
- ofld_req4.dst_mac_addr_lo32[2] = hba->ctlr.dest_addr[3];
- ofld_req4.dst_mac_addr_lo32[3] = hba->ctlr.dest_addr[2];
- ofld_req4.dst_mac_addr_hi16[0] = hba->ctlr.dest_addr[1];
- ofld_req4.dst_mac_addr_hi16[1] = hba->ctlr.dest_addr[0];
+ ofld_req4.src_mac_addr_lo[1] = port->data_src_addr[4];
+ ofld_req4.src_mac_addr_mid[0] = port->data_src_addr[3];
+ ofld_req4.src_mac_addr_mid[1] = port->data_src_addr[2];
+ ofld_req4.src_mac_addr_hi[0] = port->data_src_addr[1];
+ ofld_req4.src_mac_addr_hi[1] = port->data_src_addr[0];
+ ofld_req4.dst_mac_addr_lo[0] = ctlr->dest_addr[5];
+ /* fcf mac */
+ ofld_req4.dst_mac_addr_lo[1] = ctlr->dest_addr[4];
+ ofld_req4.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+ ofld_req4.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+ ofld_req4.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+ ofld_req4.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
ofld_req4.lcq_addr_lo = (u32) tgt->lcq_dma;
ofld_req4.lcq_addr_hi = (u32)((u64) tgt->lcq_dma >> 32);
@@ -327,11 +353,13 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
* @port: port structure pointer
* @tgt: bnx2fc_rport structure pointer
*/
-static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
+int bnx2fc_send_session_enable_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt)
{
struct kwqe *kwqe_arr[2];
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+ struct bnx2fc_hba *hba = interface->hba;
struct fcoe_kwqe_conn_enable_disable enbl_req;
struct fc_lport *lport = port->lport;
struct fc_rport *rport = tgt->rport;
@@ -345,20 +373,21 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
enbl_req.hdr.flags =
(FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT);
- enbl_req.src_mac_addr_lo32[0] = port->data_src_addr[5];
+ enbl_req.src_mac_addr_lo[0] = port->data_src_addr[5];
/* local mac */
- enbl_req.src_mac_addr_lo32[1] = port->data_src_addr[4];
- enbl_req.src_mac_addr_lo32[2] = port->data_src_addr[3];
- enbl_req.src_mac_addr_lo32[3] = port->data_src_addr[2];
- enbl_req.src_mac_addr_hi16[0] = port->data_src_addr[1];
- enbl_req.src_mac_addr_hi16[1] = port->data_src_addr[0];
-
- enbl_req.dst_mac_addr_lo32[0] = hba->ctlr.dest_addr[5];/* fcf mac */
- enbl_req.dst_mac_addr_lo32[1] = hba->ctlr.dest_addr[4];
- enbl_req.dst_mac_addr_lo32[2] = hba->ctlr.dest_addr[3];
- enbl_req.dst_mac_addr_lo32[3] = hba->ctlr.dest_addr[2];
- enbl_req.dst_mac_addr_hi16[0] = hba->ctlr.dest_addr[1];
- enbl_req.dst_mac_addr_hi16[1] = hba->ctlr.dest_addr[0];
+ enbl_req.src_mac_addr_lo[1] = port->data_src_addr[4];
+ enbl_req.src_mac_addr_mid[0] = port->data_src_addr[3];
+ enbl_req.src_mac_addr_mid[1] = port->data_src_addr[2];
+ enbl_req.src_mac_addr_hi[0] = port->data_src_addr[1];
+ enbl_req.src_mac_addr_hi[1] = port->data_src_addr[0];
+ memcpy(tgt->src_addr, port->data_src_addr, ETH_ALEN);
+
+ enbl_req.dst_mac_addr_lo[0] = ctlr->dest_addr[5];
+ enbl_req.dst_mac_addr_lo[1] = ctlr->dest_addr[4];
+ enbl_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+ enbl_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+ enbl_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+ enbl_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
port_id = fc_host_port_id(lport->host);
if (port_id != tgt->sid) {
@@ -374,10 +403,10 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
enbl_req.d_id[0] = (port_id & 0x000000FF);
enbl_req.d_id[1] = (port_id & 0x0000FF00) >> 8;
enbl_req.d_id[2] = (port_id & 0x00FF0000) >> 16;
- enbl_req.vlan_tag = hba->vlan_id <<
+ enbl_req.vlan_tag = interface->vlan_id <<
FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT;
enbl_req.vlan_tag |= 3 << FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT;
- enbl_req.vlan_flag = hba->vlan_enabled;
+ enbl_req.vlan_flag = interface->vlan_enabled;
enbl_req.context_id = tgt->context_id;
enbl_req.conn_id = tgt->fcoe_conn_id;
@@ -397,7 +426,9 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
int bnx2fc_send_session_disable_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt)
{
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+ struct bnx2fc_hba *hba = interface->hba;
struct fcoe_kwqe_conn_enable_disable disable_req;
struct kwqe *kwqe_arr[2];
struct fc_rport *rport = tgt->rport;
@@ -411,18 +442,19 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port,
disable_req.hdr.flags =
(FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT);
- disable_req.src_mac_addr_lo32[0] = port->data_src_addr[5];
- disable_req.src_mac_addr_lo32[2] = port->data_src_addr[3];
- disable_req.src_mac_addr_lo32[3] = port->data_src_addr[2];
- disable_req.src_mac_addr_hi16[0] = port->data_src_addr[1];
- disable_req.src_mac_addr_hi16[1] = port->data_src_addr[0];
+ disable_req.src_mac_addr_lo[0] = tgt->src_addr[5];
+ disable_req.src_mac_addr_lo[1] = tgt->src_addr[4];
+ disable_req.src_mac_addr_mid[0] = tgt->src_addr[3];
+ disable_req.src_mac_addr_mid[1] = tgt->src_addr[2];
+ disable_req.src_mac_addr_hi[0] = tgt->src_addr[1];
+ disable_req.src_mac_addr_hi[1] = tgt->src_addr[0];
- disable_req.dst_mac_addr_lo32[0] = hba->ctlr.dest_addr[5];/* fcf mac */
- disable_req.dst_mac_addr_lo32[1] = hba->ctlr.dest_addr[4];
- disable_req.dst_mac_addr_lo32[2] = hba->ctlr.dest_addr[3];
- disable_req.dst_mac_addr_lo32[3] = hba->ctlr.dest_addr[2];
- disable_req.dst_mac_addr_hi16[0] = hba->ctlr.dest_addr[1];
- disable_req.dst_mac_addr_hi16[1] = hba->ctlr.dest_addr[0];
+ disable_req.dst_mac_addr_lo[0] = ctlr->dest_addr[5];
+ disable_req.dst_mac_addr_lo[1] = ctlr->dest_addr[4];
+ disable_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+ disable_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+ disable_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+ disable_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
port_id = tgt->sid;
disable_req.s_id[0] = (port_id & 0x000000FF);
@@ -436,11 +468,11 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port,
disable_req.d_id[2] = (port_id & 0x00FF0000) >> 16;
disable_req.context_id = tgt->context_id;
disable_req.conn_id = tgt->fcoe_conn_id;
- disable_req.vlan_tag = hba->vlan_id <<
+ disable_req.vlan_tag = interface->vlan_id <<
FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT;
disable_req.vlan_tag |=
3 << FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT;
- disable_req.vlan_flag = hba->vlan_enabled;
+ disable_req.vlan_flag = interface->vlan_enabled;
kwqe_arr[0] = (struct kwqe *) &disable_req;
@@ -453,7 +485,7 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port,
/**
* bnx2fc_send_session_destroy_req - initiates FCoE Session destroy
*
- * @port: port structure pointer
+ * @hba: adapter structure pointer
* @tgt: bnx2fc_rport structure pointer
*/
int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba,
@@ -480,16 +512,36 @@ int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba,
return rc;
}
+static bool is_valid_lport(struct bnx2fc_hba *hba, struct fc_lport *lport)
+{
+ struct bnx2fc_lport *blport;
+
+ spin_lock_bh(&hba->hba_lock);
+ list_for_each_entry(blport, &hba->vports, list) {
+ if (blport->lport == lport) {
+ spin_unlock_bh(&hba->hba_lock);
+ return true;
+ }
+ }
+ spin_unlock_bh(&hba->hba_lock);
+ return false;
+
+}
+
+
static void bnx2fc_unsol_els_work(struct work_struct *work)
{
struct bnx2fc_unsol_els *unsol_els;
struct fc_lport *lport;
+ struct bnx2fc_hba *hba;
struct fc_frame *fp;
unsol_els = container_of(work, struct bnx2fc_unsol_els, unsol_els_work);
lport = unsol_els->lport;
fp = unsol_els->fp;
- fc_exch_recv(lport, fp);
+ hba = unsol_els->hba;
+ if (is_valid_lport(hba, lport))
+ fc_exch_recv(lport, fp);
kfree(unsol_els);
}
@@ -499,6 +551,7 @@ void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
{
struct fcoe_port *port = tgt->port;
struct fc_lport *lport = port->lport;
+ struct bnx2fc_interface *interface = port->priv;
struct bnx2fc_unsol_els *unsol_els;
struct fc_frame_header *fh;
struct fc_frame *fp;
@@ -559,6 +612,7 @@ void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
fr_eof(fp) = FC_EOF_T;
fr_crc(fp) = cpu_to_le32(~crc);
unsol_els->lport = lport;
+ unsol_els->hba = interface->hba;
unsol_els->fp = fp;
INIT_WORK(&unsol_els->unsol_els_work, bnx2fc_unsol_els_work);
queue_work(bnx2fc_wq, &unsol_els->unsol_els_work);
@@ -579,10 +633,11 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
u16 xid;
u32 frame_len, len;
struct bnx2fc_cmd *io_req = NULL;
- struct fcoe_task_ctx_entry *task, *task_page;
- struct bnx2fc_hba *hba = tgt->port->priv;
- int task_idx, index;
+ struct bnx2fc_interface *interface = tgt->port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
int rc = 0;
+ u64 err_warn_bit_map;
+ u8 err_warn = 0xff;
BNX2FC_TGT_DBG(tgt, "Entered UNSOL COMPLETION wqe = 0x%x\n", wqe);
@@ -640,44 +695,42 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
xid = err_entry->fc_hdr.ox_id;
BNX2FC_TGT_DBG(tgt, "Unsol Error Frame OX_ID = 0x%x\n", xid);
BNX2FC_TGT_DBG(tgt, "err_warn_bitmap = %08x:%08x\n",
- err_entry->err_warn_bitmap_hi,
- err_entry->err_warn_bitmap_lo);
+ err_entry->data.err_warn_bitmap_hi,
+ err_entry->data.err_warn_bitmap_lo);
BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x\n",
- err_entry->tx_buf_off, err_entry->rx_buf_off);
-
- bnx2fc_return_rqe(tgt, 1);
+ err_entry->data.tx_buf_off, err_entry->data.rx_buf_off);
- if (xid > BNX2FC_MAX_XID) {
+ if (xid > hba->max_xid) {
BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n",
xid);
- spin_unlock_bh(&tgt->tgt_lock);
- break;
+ goto ret_err_rqe;
}
- task_idx = xid / BNX2FC_TASKS_PER_PAGE;
- index = xid % BNX2FC_TASKS_PER_PAGE;
- task_page = (struct fcoe_task_ctx_entry *)
- hba->task_ctx[task_idx];
- task = &(task_page[index]);
io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid];
- if (!io_req) {
- spin_unlock_bh(&tgt->tgt_lock);
- break;
- }
+ if (!io_req)
+ goto ret_err_rqe;
if (io_req->cmd_type != BNX2FC_SCSI_CMD) {
printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n");
- spin_unlock_bh(&tgt->tgt_lock);
- break;
+ goto ret_err_rqe;
}
if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP,
&io_req->req_flags)) {
BNX2FC_IO_DBG(io_req, "unsol_err: cleanup in "
"progress.. ignore unsol err\n");
- spin_unlock_bh(&tgt->tgt_lock);
- break;
+ goto ret_err_rqe;
+ }
+
+ err_warn_bit_map = (u64)
+ ((u64)err_entry->data.err_warn_bitmap_hi << 32) |
+ (u64)err_entry->data.err_warn_bitmap_lo;
+ for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) {
+ if (err_warn_bit_map & (u64)((u64)1 << i)) {
+ err_warn = i;
+ break;
+ }
}
/*
@@ -687,26 +740,58 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
* logging out the target, when the ABTS eventually
* times out.
*/
- if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS,
- &io_req->req_flags)) {
- /*
- * Cancel the timeout_work, as we received IO
- * completion with FW error.
- */
- if (cancel_delayed_work(&io_req->timeout_work))
- kref_put(&io_req->refcount,
- bnx2fc_cmd_release); /* timer hold */
-
- rc = bnx2fc_initiate_abts(io_req);
- if (rc != SUCCESS) {
- BNX2FC_IO_DBG(io_req, "err_warn: initiate_abts "
- "failed. issue cleanup\n");
- rc = bnx2fc_initiate_cleanup(io_req);
- BUG_ON(rc);
- }
- } else
+ if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
printk(KERN_ERR PFX "err_warn: io_req (0x%x) already "
"in ABTS processing\n", xid);
+ goto ret_err_rqe;
+ }
+ BNX2FC_TGT_DBG(tgt, "err = 0x%x\n", err_warn);
+ if (tgt->dev_type != TYPE_TAPE)
+ goto skip_rec;
+ switch (err_warn) {
+ case FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION:
+ case FCOE_ERROR_CODE_DATA_OOO_RO:
+ case FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT:
+ case FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET:
+ case FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ:
+ case FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET:
+ BNX2FC_TGT_DBG(tgt, "REC TOV popped for xid - 0x%x\n",
+ xid);
+ memcpy(&io_req->err_entry, err_entry,
+ sizeof(struct fcoe_err_report_entry));
+ if (!test_bit(BNX2FC_FLAG_SRR_SENT,
+ &io_req->req_flags)) {
+ spin_unlock_bh(&tgt->tgt_lock);
+ rc = bnx2fc_send_rec(io_req);
+ spin_lock_bh(&tgt->tgt_lock);
+
+ if (rc)
+ goto skip_rec;
+ } else
+ printk(KERN_ERR PFX "SRR in progress\n");
+ goto ret_err_rqe;
+ default:
+ break;
+ }
+
+skip_rec:
+ set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags);
+ /*
+ * Cancel the timeout_work, as we received IO
+ * completion with FW error.
+ */
+ if (cancel_delayed_work(&io_req->timeout_work))
+ kref_put(&io_req->refcount, bnx2fc_cmd_release);
+
+ rc = bnx2fc_initiate_abts(io_req);
+ if (rc != SUCCESS) {
+ printk(KERN_ERR PFX "err_warn: initiate_abts "
+ "failed xid = 0x%x. issue cleanup\n",
+ io_req->xid);
+ bnx2fc_initiate_cleanup(io_req);
+ }
+ret_err_rqe:
+ bnx2fc_return_rqe(tgt, 1);
spin_unlock_bh(&tgt->tgt_lock);
break;
@@ -722,11 +807,45 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
xid = cpu_to_be16(err_entry->fc_hdr.ox_id);
BNX2FC_TGT_DBG(tgt, "Unsol Warning Frame OX_ID = 0x%x\n", xid);
BNX2FC_TGT_DBG(tgt, "err_warn_bitmap = %08x:%08x",
- err_entry->err_warn_bitmap_hi,
- err_entry->err_warn_bitmap_lo);
+ err_entry->data.err_warn_bitmap_hi,
+ err_entry->data.err_warn_bitmap_lo);
BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x",
- err_entry->tx_buf_off, err_entry->rx_buf_off);
+ err_entry->data.tx_buf_off, err_entry->data.rx_buf_off);
+ if (xid > hba->max_xid) {
+ BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", xid);
+ goto ret_warn_rqe;
+ }
+
+ err_warn_bit_map = (u64)
+ ((u64)err_entry->data.err_warn_bitmap_hi << 32) |
+ (u64)err_entry->data.err_warn_bitmap_lo;
+ for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) {
+ if (err_warn_bit_map & ((u64)1 << i)) {
+ err_warn = i;
+ break;
+ }
+ }
+ BNX2FC_TGT_DBG(tgt, "warn = 0x%x\n", err_warn);
+
+ io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid];
+ if (!io_req)
+ goto ret_warn_rqe;
+
+ if (io_req->cmd_type != BNX2FC_SCSI_CMD) {
+ printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n");
+ goto ret_warn_rqe;
+ }
+
+ memcpy(&io_req->err_entry, err_entry,
+ sizeof(struct fcoe_err_report_entry));
+
+ if (err_warn == FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION)
+ /* REC_TOV is not a warning code */
+ BUG_ON(1);
+ else
+ BNX2FC_TGT_DBG(tgt, "Unsolicited warning\n");
+ret_warn_rqe:
bnx2fc_return_rqe(tgt, 1);
spin_unlock_bh(&tgt->tgt_lock);
break;
@@ -737,35 +856,22 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
}
}
-void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe)
+void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe,
+ unsigned char *rq_data, u8 num_rq,
+ struct fcoe_task_ctx_entry *task)
{
- struct fcoe_task_ctx_entry *task;
- struct fcoe_task_ctx_entry *task_page;
struct fcoe_port *port = tgt->port;
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct bnx2fc_cmd *io_req;
- int task_idx, index;
+
u16 xid;
u8 cmd_type;
u8 rx_state = 0;
- u8 num_rq;
spin_lock_bh(&tgt->tgt_lock);
- xid = wqe & FCOE_PEND_WQ_CQE_TASK_ID;
- if (xid >= BNX2FC_MAX_TASKS) {
- printk(KERN_ALERT PFX "ERROR:xid out of range\n");
- spin_unlock_bh(&tgt->tgt_lock);
- return;
- }
- task_idx = xid / BNX2FC_TASKS_PER_PAGE;
- index = xid % BNX2FC_TASKS_PER_PAGE;
- task_page = (struct fcoe_task_ctx_entry *)hba->task_ctx[task_idx];
- task = &(task_page[index]);
-
- num_rq = ((task->rx_wr_tx_rd.rx_flags &
- FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE) >>
- FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE_SHIFT);
+ xid = wqe & FCOE_PEND_WQ_CQE_TASK_ID;
io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid];
if (io_req == NULL) {
@@ -777,22 +883,20 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe)
/* Timestamp IO completion time */
cmd_type = io_req->cmd_type;
- /* optimized completion path */
- if (cmd_type == BNX2FC_SCSI_CMD) {
- rx_state = ((task->rx_wr_tx_rd.rx_flags &
- FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE) >>
- FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE_SHIFT);
+ rx_state = ((task->rxwr_txrd.var_ctx.rx_flags &
+ FCOE_TCE_RX_WR_TX_RD_VAR_RX_STATE) >>
+ FCOE_TCE_RX_WR_TX_RD_VAR_RX_STATE_SHIFT);
+ /* Process other IO completion types */
+ switch (cmd_type) {
+ case BNX2FC_SCSI_CMD:
if (rx_state == FCOE_TASK_RX_STATE_COMPLETED) {
- bnx2fc_process_scsi_cmd_compl(io_req, task, num_rq);
+ bnx2fc_process_scsi_cmd_compl(io_req, task, num_rq,
+ rq_data);
spin_unlock_bh(&tgt->tgt_lock);
return;
}
- }
- /* Process other IO completion types */
- switch (cmd_type) {
- case BNX2FC_SCSI_CMD:
if (rx_state == FCOE_TASK_RX_STATE_ABTS_COMPLETED)
bnx2fc_process_abts_compl(io_req, task, num_rq);
else if (rx_state ==
@@ -805,7 +909,7 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe)
case BNX2FC_TASK_MGMT_CMD:
BNX2FC_IO_DBG(io_req, "Processing TM complete\n");
- bnx2fc_process_tm_compl(io_req, task, num_rq);
+ bnx2fc_process_tm_compl(io_req, task, num_rq, rq_data);
break;
case BNX2FC_ABTS:
@@ -819,8 +923,16 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe)
break;
case BNX2FC_ELS:
- BNX2FC_IO_DBG(io_req, "cq_compl - call process_els_compl\n");
- bnx2fc_process_els_compl(io_req, task, num_rq);
+ if (rx_state == FCOE_TASK_RX_STATE_COMPLETED)
+ bnx2fc_process_els_compl(io_req, task, num_rq);
+ else if (rx_state == FCOE_TASK_RX_STATE_ABTS_COMPLETED)
+ bnx2fc_process_abts_compl(io_req, task, num_rq);
+ else if (rx_state ==
+ FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_COMPLETED)
+ bnx2fc_process_cleanup_compl(io_req, task, num_rq);
+ else
+ printk(KERN_ERR PFX "Invalid rx state = %d\n",
+ rx_state);
break;
case BNX2FC_CLEANUP:
@@ -828,6 +940,13 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe)
kref_put(&io_req->refcount, bnx2fc_cmd_release);
break;
+ case BNX2FC_SEQ_CLEANUP:
+ BNX2FC_IO_DBG(io_req, "cq_compl(0x%x) - seq cleanup resp\n",
+ io_req->xid);
+ bnx2fc_process_seq_cleanup_compl(io_req, task, rx_state);
+ kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ break;
+
default:
printk(KERN_ERR PFX "Invalid cmd_type %d\n", cmd_type);
break;
@@ -835,7 +954,22 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe)
spin_unlock_bh(&tgt->tgt_lock);
}
-struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe)
+void bnx2fc_arm_cq(struct bnx2fc_rport *tgt)
+{
+ struct b577xx_fcoe_rx_doorbell *rx_db = &tgt->rx_db;
+ u32 msg;
+
+ wmb();
+ rx_db->doorbell_cq_cons = tgt->cq_cons_idx | (tgt->cq_curr_toggle_bit <<
+ FCOE_CQE_TOGGLE_BIT_SHIFT);
+ msg = *((u32 *)rx_db);
+ writel(cpu_to_le32(msg), tgt->ctx_base);
+
+}
+
+static struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe,
+ unsigned char *rq_data, u8 num_rq,
+ struct fcoe_task_ctx_entry *task)
{
struct bnx2fc_work *work;
work = kzalloc(sizeof(struct bnx2fc_work), GFP_ATOMIC);
@@ -845,16 +979,96 @@ struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe)
INIT_LIST_HEAD(&work->list);
work->tgt = tgt;
work->wqe = wqe;
+ work->num_rq = num_rq;
+ work->task = task;
+ if (rq_data)
+ memcpy(work->rq_data, rq_data, BNX2FC_RQ_BUF_SZ);
+
return work;
}
+/* Pending work request completion */
+static bool bnx2fc_pending_work(struct bnx2fc_rport *tgt, unsigned int wqe)
+{
+ unsigned int cpu = wqe % num_possible_cpus();
+ struct bnx2fc_percpu_s *fps;
+ struct bnx2fc_work *work;
+ struct fcoe_task_ctx_entry *task;
+ struct fcoe_task_ctx_entry *task_page;
+ struct fcoe_port *port = tgt->port;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
+ unsigned char *rq_data = NULL;
+ unsigned char rq_data_buff[BNX2FC_RQ_BUF_SZ];
+ int task_idx, index;
+ u16 xid;
+ u8 num_rq;
+ int i;
+
+ xid = wqe & FCOE_PEND_WQ_CQE_TASK_ID;
+ if (xid >= hba->max_tasks) {
+ pr_err(PFX "ERROR:xid out of range\n");
+ return false;
+ }
+
+ task_idx = xid / BNX2FC_TASKS_PER_PAGE;
+ index = xid % BNX2FC_TASKS_PER_PAGE;
+ task_page = (struct fcoe_task_ctx_entry *)hba->task_ctx[task_idx];
+ task = &task_page[index];
+
+ num_rq = ((task->rxwr_txrd.var_ctx.rx_flags &
+ FCOE_TCE_RX_WR_TX_RD_VAR_NUM_RQ_WQE) >>
+ FCOE_TCE_RX_WR_TX_RD_VAR_NUM_RQ_WQE_SHIFT);
+
+ memset(rq_data_buff, 0, BNX2FC_RQ_BUF_SZ);
+
+ if (!num_rq)
+ goto num_rq_zero;
+
+ rq_data = bnx2fc_get_next_rqe(tgt, 1);
+
+ if (num_rq > 1) {
+ /* We do not need extra sense data */
+ for (i = 1; i < num_rq; i++)
+ bnx2fc_get_next_rqe(tgt, 1);
+ }
+
+ if (rq_data)
+ memcpy(rq_data_buff, rq_data, BNX2FC_RQ_BUF_SZ);
+
+ /* return RQ entries */
+ for (i = 0; i < num_rq; i++)
+ bnx2fc_return_rqe(tgt, 1);
+
+num_rq_zero:
+
+ fps = &per_cpu(bnx2fc_percpu, cpu);
+ spin_lock_bh(&fps->fp_work_lock);
+ if (fps->iothread) {
+ work = bnx2fc_alloc_work(tgt, wqe, rq_data_buff,
+ num_rq, task);
+ if (work) {
+ list_add_tail(&work->list, &fps->work_list);
+ wake_up_process(fps->iothread);
+ spin_unlock_bh(&fps->fp_work_lock);
+ return true;
+ }
+ }
+ spin_unlock_bh(&fps->fp_work_lock);
+ bnx2fc_process_cq_compl(tgt, wqe,
+ rq_data_buff, num_rq, task);
+
+ return true;
+}
+
int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt)
{
struct fcoe_cqe *cq;
u32 cq_cons;
struct fcoe_cqe *cqe;
+ u32 num_free_sqes = 0;
+ u32 num_cqes = 0;
u16 wqe;
- bool more_cqes_found = false;
/*
* cq_lock is a low contention lock used to protect
@@ -872,62 +1086,35 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt)
cq_cons = tgt->cq_cons_idx;
cqe = &cq[cq_cons];
- do {
- more_cqes_found ^= true;
+ while (((wqe = cqe->wqe) & FCOE_CQE_TOGGLE_BIT) ==
+ (tgt->cq_curr_toggle_bit <<
+ FCOE_CQE_TOGGLE_BIT_SHIFT)) {
- while (((wqe = cqe->wqe) & FCOE_CQE_TOGGLE_BIT) ==
- (tgt->cq_curr_toggle_bit <<
- FCOE_CQE_TOGGLE_BIT_SHIFT)) {
-
- /* new entry on the cq */
- if (wqe & FCOE_CQE_CQE_TYPE) {
- /* Unsolicited event notification */
- bnx2fc_process_unsol_compl(tgt, wqe);
- } else {
- struct bnx2fc_work *work = NULL;
- struct bnx2fc_percpu_s *fps = NULL;
- unsigned int cpu = wqe % num_possible_cpus();
-
- fps = &per_cpu(bnx2fc_percpu, cpu);
- spin_lock_bh(&fps->fp_work_lock);
- if (unlikely(!fps->iothread))
- goto unlock;
-
- work = bnx2fc_alloc_work(tgt, wqe);
- if (work)
- list_add_tail(&work->list,
- &fps->work_list);
-unlock:
- spin_unlock_bh(&fps->fp_work_lock);
-
- /* Pending work request completion */
- if (fps->iothread && work)
- wake_up_process(fps->iothread);
- else
- bnx2fc_process_cq_compl(tgt, wqe);
- }
- cqe++;
- tgt->cq_cons_idx++;
-
- if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) {
- tgt->cq_cons_idx = 0;
- cqe = cq;
- tgt->cq_curr_toggle_bit =
- 1 - tgt->cq_curr_toggle_bit;
- }
+ /* new entry on the cq */
+ if (wqe & FCOE_CQE_CQE_TYPE) {
+ /* Unsolicited event notification */
+ bnx2fc_process_unsol_compl(tgt, wqe);
+ } else {
+ if (bnx2fc_pending_work(tgt, wqe))
+ num_free_sqes++;
}
- /* Re-arm CQ */
- if (more_cqes_found) {
- tgt->conn_db->cq_arm.lo = -1;
- wmb();
+ cqe++;
+ tgt->cq_cons_idx++;
+ num_cqes++;
+
+ if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) {
+ tgt->cq_cons_idx = 0;
+ cqe = cq;
+ tgt->cq_curr_toggle_bit =
+ 1 - tgt->cq_curr_toggle_bit;
}
- } while (more_cqes_found);
-
- /*
- * Commit tgt->cq_cons_idx change to the memory
- * spin_lock implies full memory barrier, no need to smp_wmb
- */
-
+ }
+ if (num_cqes) {
+ /* Arm CQ only if doorbell is mapped */
+ if (tgt->ctx_base)
+ bnx2fc_arm_cq(tgt);
+ atomic_add(num_free_sqes, &tgt->free_sqes);
+ }
spin_unlock_bh(&tgt->cq_lock);
return 0;
}
@@ -947,7 +1134,7 @@ static void bnx2fc_fastpath_notification(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt = hba->tgt_ofld_list[conn_id];
if (!tgt) {
- printk(KERN_ALERT PFX "conn_id 0x%x not valid\n", conn_id);
+ printk(KERN_ERR PFX "conn_id 0x%x not valid\n", conn_id);
return;
}
@@ -967,10 +1154,9 @@ static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba,
struct fcoe_kcqe *ofld_kcqe)
{
struct bnx2fc_rport *tgt;
- struct fcoe_port *port;
+ struct bnx2fc_interface *interface;
u32 conn_id;
u32 context_id;
- int rc;
conn_id = ofld_kcqe->fcoe_conn_id;
context_id = ofld_kcqe->fcoe_conn_context_id;
@@ -981,9 +1167,9 @@ static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba,
}
BNX2FC_TGT_DBG(tgt, "Entered ofld compl - context_id = 0x%x\n",
ofld_kcqe->fcoe_conn_context_id);
- port = tgt->port;
- if (hba != tgt->port->priv) {
- printk(KERN_ALERT PFX "ERROR:ofld_cmpl: HBA mis-match\n");
+ interface = tgt->port->priv;
+ if (hba != interface->hba) {
+ printk(KERN_ERR PFX "ERROR:ofld_cmpl: HBA mismatch\n");
goto ofld_cmpl_err;
}
/*
@@ -998,17 +1184,10 @@ static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba,
"resources\n");
set_bit(BNX2FC_FLAG_CTX_ALLOC_FAILURE, &tgt->flags);
}
- goto ofld_cmpl_err;
} else {
-
- /* now enable the session */
- rc = bnx2fc_send_session_enable_req(port, tgt);
- if (rc) {
- printk(KERN_ALERT PFX "enable session failed\n");
- goto ofld_cmpl_err;
- }
+ /* FW offload request successfully completed */
+ set_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
}
- return;
ofld_cmpl_err:
set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
wake_up_interruptible(&tgt->ofld_wait);
@@ -1027,6 +1206,7 @@ static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba,
struct fcoe_kcqe *ofld_kcqe)
{
struct bnx2fc_rport *tgt;
+ struct bnx2fc_interface *interface;
u32 conn_id;
u32 context_id;
@@ -1034,7 +1214,7 @@ static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba,
conn_id = ofld_kcqe->fcoe_conn_id;
tgt = hba->tgt_ofld_list[conn_id];
if (!tgt) {
- printk(KERN_ALERT PFX "ERROR:enbl_cmpl: No pending ofld req\n");
+ printk(KERN_ERR PFX "ERROR:enbl_cmpl: No pending ofld req\n");
return;
}
@@ -1046,22 +1226,17 @@ static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba,
* and enable
*/
if (tgt->context_id != context_id) {
- printk(KERN_ALERT PFX "context id mis-match\n");
+ printk(KERN_ERR PFX "context id mismatch\n");
return;
}
- if (hba != tgt->port->priv) {
- printk(KERN_ALERT PFX "bnx2fc-enbl_cmpl: HBA mis-match\n");
+ interface = tgt->port->priv;
+ if (hba != interface->hba) {
+ printk(KERN_ERR PFX "bnx2fc-enbl_cmpl: HBA mismatch\n");
goto enbl_cmpl_err;
}
- if (ofld_kcqe->completion_status) {
- goto enbl_cmpl_err;
- } else {
+ if (!ofld_kcqe->completion_status)
/* enable successful - rport ready for issuing IOs */
- set_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
- set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
- wake_up_interruptible(&tgt->ofld_wait);
- }
- return;
+ set_bit(BNX2FC_FLAG_ENABLED, &tgt->flags);
enbl_cmpl_err:
set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
@@ -1078,20 +1253,23 @@ static void bnx2fc_process_conn_disable_cmpl(struct bnx2fc_hba *hba,
conn_id = disable_kcqe->fcoe_conn_id;
tgt = hba->tgt_ofld_list[conn_id];
if (!tgt) {
- printk(KERN_ALERT PFX "ERROR: disable_cmpl: No disable req\n");
+ printk(KERN_ERR PFX "ERROR: disable_cmpl: No disable req\n");
return;
}
BNX2FC_TGT_DBG(tgt, PFX "disable_cmpl: conn_id %d\n", conn_id);
if (disable_kcqe->completion_status) {
- printk(KERN_ALERT PFX "ERROR: Disable failed with cmpl status %d\n",
+ printk(KERN_ERR PFX "Disable failed with cmpl status %d\n",
disable_kcqe->completion_status);
- return;
+ set_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags);
+ set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
+ wake_up_interruptible(&tgt->upld_wait);
} else {
/* disable successful */
BNX2FC_TGT_DBG(tgt, "disable successful\n");
clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
+ clear_bit(BNX2FC_FLAG_ENABLED, &tgt->flags);
set_bit(BNX2FC_FLAG_DISABLED, &tgt->flags);
set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
wake_up_interruptible(&tgt->upld_wait);
@@ -1107,14 +1285,14 @@ static void bnx2fc_process_conn_destroy_cmpl(struct bnx2fc_hba *hba,
conn_id = destroy_kcqe->fcoe_conn_id;
tgt = hba->tgt_ofld_list[conn_id];
if (!tgt) {
- printk(KERN_ALERT PFX "destroy_cmpl: No destroy req\n");
+ printk(KERN_ERR PFX "destroy_cmpl: No destroy req\n");
return;
}
BNX2FC_TGT_DBG(tgt, "destroy_cmpl: conn_id %d\n", conn_id);
if (destroy_kcqe->completion_status) {
- printk(KERN_ALERT PFX "Destroy conn failed, cmpl status %d\n",
+ printk(KERN_ERR PFX "Destroy conn failed, cmpl status %d\n",
destroy_kcqe->completion_status);
return;
} else {
@@ -1141,17 +1319,22 @@ static void bnx2fc_init_failure(struct bnx2fc_hba *hba, u32 err_code)
case FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR:
printk(KERN_ERR PFX "init_failure due to NIC error\n");
break;
-
+ case FCOE_KCQE_COMPLETION_STATUS_ERROR:
+ printk(KERN_ERR PFX "init failure due to compl status err\n");
+ break;
+ case FCOE_KCQE_COMPLETION_STATUS_WRONG_HSI_VERSION:
+ printk(KERN_ERR PFX "init failure due to HSI mismatch\n");
+ break;
default:
printk(KERN_ERR PFX "Unknown Error code %d\n", err_code);
}
}
/**
- * bnx2fc_indicae_kcqe - process KCQE
+ * bnx2fc_indicate_kcqe() - process KCQE
*
- * @hba: adapter structure pointer
- * @kcqe: kcqe pointer
+ * @context: adapter structure pointer
+ * @kcq: kcqe pointer
* @num_cqe: Number of completion queue elements
*
* Generic KCQ event handler
@@ -1200,7 +1383,7 @@ void bnx2fc_indicate_kcqe(void *context, struct kcqe *kcq[],
} else {
printk(KERN_ERR PFX "DESTROY success\n");
}
- hba->flags |= BNX2FC_FLAG_DESTROY_CMPL;
+ set_bit(BNX2FC_FLAG_DESTROY_CMPL, &hba->flags);
wake_up_interruptible(&hba->destroy_wait);
break;
@@ -1220,9 +1403,8 @@ void bnx2fc_indicate_kcqe(void *context, struct kcqe *kcq[],
break;
case FCOE_KCQE_OPCODE_FCOE_ERROR:
- /* fall thru */
default:
- printk(KERN_ALERT PFX "unknown opcode 0x%x\n",
+ printk(KERN_ERR PFX "unknown opcode 0x%x\n",
kcqe->op_code);
}
}
@@ -1247,23 +1429,15 @@ void bnx2fc_add_2_sq(struct bnx2fc_rport *tgt, u16 xid)
void bnx2fc_ring_doorbell(struct bnx2fc_rport *tgt)
{
- struct b577xx_doorbell_set_prod ev_doorbell;
+ struct b577xx_doorbell_set_prod *sq_db = &tgt->sq_db;
u32 msg;
wmb();
-
- memset(&ev_doorbell, 0, sizeof(struct b577xx_doorbell_set_prod));
- ev_doorbell.header.header = B577XX_DOORBELL_HDR_DB_TYPE;
-
- ev_doorbell.prod = tgt->sq_prod_idx |
+ sq_db->prod = tgt->sq_prod_idx |
(tgt->sq_curr_toggle_bit << 15);
- ev_doorbell.header.header |= B577XX_FCOE_CONNECTION_TYPE <<
- B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT;
- msg = *((u32 *)&ev_doorbell);
+ msg = *((u32 *)sq_db);
writel(cpu_to_le32(msg), tgt->ctx_base);
- mmiowb();
-
}
int bnx2fc_map_doorbell(struct bnx2fc_rport *tgt)
@@ -1272,13 +1446,13 @@ int bnx2fc_map_doorbell(struct bnx2fc_rport *tgt)
struct fcoe_port *port = tgt->port;
u32 reg_off;
resource_size_t reg_base;
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
reg_base = pci_resource_start(hba->pcidev,
BNX2X_DOORBELL_PCI_BAR);
- reg_off = BNX2FC_5771X_DB_PAGE_SIZE *
- (context_id & 0x1FFFF) + DPM_TRIGER_TYPE;
- tgt->ctx_base = ioremap_nocache(reg_base + reg_off, 4);
+ reg_off = (1 << BNX2X_DB_SHIFT) * (context_id & 0x1FFFF);
+ tgt->ctx_base = ioremap(reg_base + reg_off, 4);
if (!tgt->ctx_base)
return -ENOMEM;
return 0;
@@ -1311,6 +1485,86 @@ void bnx2fc_return_rqe(struct bnx2fc_rport *tgt, u8 num_items)
tgt->conn_db->rq_prod = tgt->rq_prod_idx;
}
+void bnx2fc_init_seq_cleanup_task(struct bnx2fc_cmd *seq_clnp_req,
+ struct fcoe_task_ctx_entry *task,
+ struct bnx2fc_cmd *orig_io_req,
+ u32 offset)
+{
+ struct scsi_cmnd *sc_cmd = orig_io_req->sc_cmd;
+ struct bnx2fc_rport *tgt = seq_clnp_req->tgt;
+ struct fcoe_bd_ctx *bd = orig_io_req->bd_tbl->bd_tbl;
+ struct fcoe_ext_mul_sges_ctx *sgl;
+ u8 task_type = FCOE_TASK_TYPE_SEQUENCE_CLEANUP;
+ u8 orig_task_type;
+ u16 orig_xid = orig_io_req->xid;
+ u32 context_id = tgt->context_id;
+ u64 phys_addr = (u64)orig_io_req->bd_tbl->bd_tbl_dma;
+ u32 orig_offset = offset;
+ int bd_count;
+ int i;
+
+ memset(task, 0, sizeof(struct fcoe_task_ctx_entry));
+
+ if (sc_cmd->sc_data_direction == DMA_TO_DEVICE)
+ orig_task_type = FCOE_TASK_TYPE_WRITE;
+ else
+ orig_task_type = FCOE_TASK_TYPE_READ;
+
+ /* Tx flags */
+ task->txwr_rxrd.const_ctx.tx_flags =
+ FCOE_TASK_TX_STATE_SEQUENCE_CLEANUP <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE_SHIFT;
+ /* init flags */
+ task->txwr_rxrd.const_ctx.init_flags = task_type <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
+ task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT;
+ task->rxwr_txrd.const_ctx.init_flags = context_id <<
+ FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT;
+ task->rxwr_txrd.const_ctx.init_flags = context_id <<
+ FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT;
+
+ task->txwr_rxrd.union_ctx.cleanup.ctx.cleaned_task_id = orig_xid;
+
+ task->txwr_rxrd.union_ctx.cleanup.ctx.rolled_tx_seq_cnt = 0;
+ task->txwr_rxrd.union_ctx.cleanup.ctx.rolled_tx_data_offset = offset;
+
+ bd_count = orig_io_req->bd_tbl->bd_valid;
+
+ /* obtain the appropriate bd entry from relative offset */
+ for (i = 0; i < bd_count; i++) {
+ if (offset < bd[i].buf_len)
+ break;
+ offset -= bd[i].buf_len;
+ }
+ phys_addr += (i * sizeof(struct fcoe_bd_ctx));
+
+ if (orig_task_type == FCOE_TASK_TYPE_WRITE) {
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.lo =
+ (u32)phys_addr;
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.hi =
+ (u32)((u64)phys_addr >> 32);
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.sgl_size =
+ bd_count;
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_off =
+ offset; /* adjusted offset */
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_idx = i;
+ } else {
+
+ /* Multiple SGEs were used for this IO */
+ sgl = &task->rxwr_only.union_ctx.read_info.sgl_ctx.sgl;
+ sgl->mul_sgl.cur_sge_addr.lo = (u32)phys_addr;
+ sgl->mul_sgl.cur_sge_addr.hi = (u32)((u64)phys_addr >> 32);
+ sgl->mul_sgl.sgl_size = bd_count;
+ sgl->mul_sgl.cur_sge_off = offset; /*adjusted offset */
+ sgl->mul_sgl.cur_sge_idx = i;
+
+ memset(&task->rxwr_only.rx_seq_ctx, 0,
+ sizeof(struct fcoe_rx_seq_ctx));
+ task->rxwr_only.rx_seq_ctx.low_exp_ro = orig_offset;
+ task->rxwr_only.rx_seq_ctx.high_exp_ro = orig_offset;
+ }
+}
void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u16 orig_xid)
@@ -1322,18 +1576,31 @@ void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req,
memset(task, 0, sizeof(struct fcoe_task_ctx_entry));
/* Tx Write Rx Read */
- task->tx_wr_rx_rd.tx_flags = FCOE_TASK_TX_STATE_EXCHANGE_CLEANUP <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT;
- task->tx_wr_rx_rd.init_flags = task_type <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT;
- task->tx_wr_rx_rd.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT;
- /* Common */
- task->cmn.common_flags = context_id <<
- FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT;
- task->cmn.general.cleanup_info.task_id = orig_xid;
-
-
+ /* init flags */
+ task->txwr_rxrd.const_ctx.init_flags = task_type <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
+ task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT;
+ if (tgt->dev_type == TYPE_TAPE)
+ task->txwr_rxrd.const_ctx.init_flags |=
+ FCOE_TASK_DEV_TYPE_TAPE <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+ else
+ task->txwr_rxrd.const_ctx.init_flags |=
+ FCOE_TASK_DEV_TYPE_DISK <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+ task->txwr_rxrd.union_ctx.cleanup.ctx.cleaned_task_id = orig_xid;
+
+ /* Tx flags */
+ task->txwr_rxrd.const_ctx.tx_flags =
+ FCOE_TASK_TX_STATE_EXCHANGE_CLEANUP <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE_SHIFT;
+
+ /* Rx Read Tx Write */
+ task->rxwr_txrd.const_ctx.init_flags = context_id <<
+ FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT;
+ task->rxwr_txrd.var_ctx.rx_flags |= 1 <<
+ FCOE_TCE_RX_WR_TX_RD_VAR_EXP_FIRST_FRAME_SHIFT;
}
void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
@@ -1342,6 +1609,7 @@ void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
struct bnx2fc_mp_req *mp_req = &(io_req->mp_req);
struct bnx2fc_rport *tgt = io_req->tgt;
struct fc_frame_header *fc_hdr;
+ struct fcoe_ext_mul_sges_ctx *sgl;
u8 task_type = 0;
u64 *hdr;
u64 temp_hdr[3];
@@ -1367,47 +1635,54 @@ void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
/* Tx only */
if ((task_type == FCOE_TASK_TYPE_MIDPATH) ||
(task_type == FCOE_TASK_TYPE_UNSOLICITED)) {
- task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo =
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.lo =
(u32)mp_req->mp_req_bd_dma;
- task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi =
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.hi =
(u32)((u64)mp_req->mp_req_bd_dma >> 32);
- task->tx_wr_only.sgl_ctx.mul_sges.sgl_size = 1;
- BNX2FC_IO_DBG(io_req, "init_mp_task - bd_dma = 0x%llx\n",
- (unsigned long long)mp_req->mp_req_bd_dma);
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.sgl_size = 1;
}
/* Tx Write Rx Read */
- task->tx_wr_rx_rd.tx_flags = FCOE_TASK_TX_STATE_INIT <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT;
- task->tx_wr_rx_rd.init_flags = task_type <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT;
- task->tx_wr_rx_rd.init_flags |= FCOE_TASK_DEV_TYPE_DISK <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT;
- task->tx_wr_rx_rd.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT;
-
- /* Common */
- task->cmn.data_2_trns = io_req->data_xfer_len;
- context_id = tgt->context_id;
- task->cmn.common_flags = context_id <<
- FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT;
- task->cmn.common_flags |= 1 <<
- FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID_SHIFT;
- task->cmn.common_flags |= 1 <<
- FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME_SHIFT;
+ /* init flags */
+ task->txwr_rxrd.const_ctx.init_flags = task_type <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
+ if (tgt->dev_type == TYPE_TAPE)
+ task->txwr_rxrd.const_ctx.init_flags |=
+ FCOE_TASK_DEV_TYPE_TAPE <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+ else
+ task->txwr_rxrd.const_ctx.init_flags |=
+ FCOE_TASK_DEV_TYPE_DISK <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+ task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT;
+
+ /* tx flags */
+ task->txwr_rxrd.const_ctx.tx_flags = FCOE_TASK_TX_STATE_INIT <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE_SHIFT;
/* Rx Write Tx Read */
+ task->rxwr_txrd.const_ctx.data_2_trns = io_req->data_xfer_len;
+
+ /* rx flags */
+ task->rxwr_txrd.var_ctx.rx_flags |= 1 <<
+ FCOE_TCE_RX_WR_TX_RD_VAR_EXP_FIRST_FRAME_SHIFT;
+
+ context_id = tgt->context_id;
+ task->rxwr_txrd.const_ctx.init_flags = context_id <<
+ FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT;
+
fc_hdr = &(mp_req->req_fc_hdr);
if (task_type == FCOE_TASK_TYPE_MIDPATH) {
fc_hdr->fh_ox_id = cpu_to_be16(io_req->xid);
fc_hdr->fh_rx_id = htons(0xffff);
- task->rx_wr_tx_rd.rx_id = 0xffff;
+ task->rxwr_txrd.var_ctx.rx_id = 0xffff;
} else if (task_type == FCOE_TASK_TYPE_UNSOLICITED) {
fc_hdr->fh_rx_id = cpu_to_be16(io_req->xid);
}
/* Fill FC Header into middle path buffer */
- hdr = (u64 *) &task->cmn.general.cmd_info.mp_fc_frame.fc_hdr;
+ hdr = (u64 *) &task->txwr_rxrd.union_ctx.tx_frame.fc_hdr;
memcpy(temp_hdr, fc_hdr, sizeof(temp_hdr));
hdr[0] = cpu_to_be64(temp_hdr[0]);
hdr[1] = cpu_to_be64(temp_hdr[1]);
@@ -1415,12 +1690,12 @@ void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
/* Rx Only */
if (task_type == FCOE_TASK_TYPE_MIDPATH) {
+ sgl = &task->rxwr_only.union_ctx.read_info.sgl_ctx.sgl;
- task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo =
- (u32)mp_req->mp_resp_bd_dma;
- task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi =
+ sgl->mul_sgl.cur_sge_addr.lo = (u32)mp_req->mp_resp_bd_dma;
+ sgl->mul_sgl.cur_sge_addr.hi =
(u32)((u64)mp_req->mp_resp_bd_dma >> 32);
- task->rx_wr_only.sgl_ctx.mul_sges.sgl_size = 1;
+ sgl->mul_sgl.sgl_size = 1;
}
}
@@ -1431,7 +1706,11 @@ void bnx2fc_init_task(struct bnx2fc_cmd *io_req,
struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
struct io_bdt *bd_tbl = io_req->bd_tbl;
struct bnx2fc_rport *tgt = io_req->tgt;
- u64 *fcp_cmnd;
+ struct fcoe_cached_sge_ctx *cached_sge;
+ struct fcoe_ext_mul_sges_ctx *sgl;
+ int dev_type = tgt->dev_type;
+ struct fcp_cmnd *fcp_cmnd;
+ u64 *raw_fcp_cmnd;
u64 tmp_fcp_cmnd[4];
u32 context_id;
int cnt, i;
@@ -1448,86 +1727,131 @@ void bnx2fc_init_task(struct bnx2fc_cmd *io_req,
task_type = FCOE_TASK_TYPE_READ;
/* Tx only */
+ bd_count = bd_tbl->bd_valid;
+ cached_sge = &task->rxwr_only.union_ctx.read_info.sgl_ctx.cached_sge;
if (task_type == FCOE_TASK_TYPE_WRITE) {
- task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo =
- (u32)bd_tbl->bd_tbl_dma;
- task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi =
- (u32)((u64)bd_tbl->bd_tbl_dma >> 32);
- task->tx_wr_only.sgl_ctx.mul_sges.sgl_size =
- bd_tbl->bd_valid;
+ if ((dev_type == TYPE_DISK) && (bd_count == 1)) {
+ struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl;
+
+ task->txwr_only.sgl_ctx.cached_sge.cur_buf_addr.lo =
+ cached_sge->cur_buf_addr.lo =
+ fcoe_bd_tbl->buf_addr_lo;
+ task->txwr_only.sgl_ctx.cached_sge.cur_buf_addr.hi =
+ cached_sge->cur_buf_addr.hi =
+ fcoe_bd_tbl->buf_addr_hi;
+ task->txwr_only.sgl_ctx.cached_sge.cur_buf_rem =
+ cached_sge->cur_buf_rem =
+ fcoe_bd_tbl->buf_len;
+
+ task->txwr_rxrd.const_ctx.init_flags |= 1 <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE_SHIFT;
+ } else {
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.lo =
+ (u32)bd_tbl->bd_tbl_dma;
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.cur_sge_addr.hi =
+ (u32)((u64)bd_tbl->bd_tbl_dma >> 32);
+ task->txwr_only.sgl_ctx.sgl.mul_sgl.sgl_size =
+ bd_tbl->bd_valid;
+ }
}
/*Tx Write Rx Read */
/* Init state to NORMAL */
- task->tx_wr_rx_rd.tx_flags = FCOE_TASK_TX_STATE_NORMAL <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT;
- task->tx_wr_rx_rd.init_flags = task_type <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT;
- task->tx_wr_rx_rd.init_flags |= FCOE_TASK_DEV_TYPE_DISK <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT;
- task->tx_wr_rx_rd.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT;
-
- /* Common */
- task->cmn.data_2_trns = io_req->data_xfer_len;
- context_id = tgt->context_id;
- task->cmn.common_flags = context_id <<
- FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT;
- task->cmn.common_flags |= 1 <<
- FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID_SHIFT;
- task->cmn.common_flags |= 1 <<
- FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME_SHIFT;
-
- /* Set initiative ownership */
- task->cmn.common_flags |= FCOE_TASK_CTX_ENTRY_TX_RX_CMN_SEQ_INIT;
+ task->txwr_rxrd.const_ctx.init_flags |= task_type <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TASK_TYPE_SHIFT;
+ if (dev_type == TYPE_TAPE) {
+ task->txwr_rxrd.const_ctx.init_flags |=
+ FCOE_TASK_DEV_TYPE_TAPE <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+ io_req->rec_retry = 0;
+ io_req->rec_retry = 0;
+ } else
+ task->txwr_rxrd.const_ctx.init_flags |=
+ FCOE_TASK_DEV_TYPE_DISK <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_DEV_TYPE_SHIFT;
+ task->txwr_rxrd.const_ctx.init_flags |= FCOE_TASK_CLASS_TYPE_3 <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_CLASS_TYPE_SHIFT;
+ /* tx flags */
+ task->txwr_rxrd.const_ctx.tx_flags = FCOE_TASK_TX_STATE_NORMAL <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_TX_STATE_SHIFT;
/* Set initial seq counter */
- task->cmn.tx_low_seq_cnt = 1;
-
- /* Set state to "waiting for the first packet" */
- task->cmn.common_flags |= FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME;
+ task->txwr_rxrd.union_ctx.tx_seq.ctx.seq_cnt = 1;
/* Fill FCP_CMND IU */
- fcp_cmnd = (u64 *)
- task->cmn.general.cmd_info.fcp_cmd_payload.opaque;
- bnx2fc_build_fcp_cmnd(io_req, (struct fcp_cmnd *)&tmp_fcp_cmnd);
+ fcp_cmnd = (struct fcp_cmnd *)&tmp_fcp_cmnd;
+ bnx2fc_build_fcp_cmnd(io_req, fcp_cmnd);
+ int_to_scsilun(sc_cmd->device->lun, &fcp_cmnd->fc_lun);
+ memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len);
+ raw_fcp_cmnd = (u64 *)
+ task->txwr_rxrd.union_ctx.fcp_cmd.opaque;
/* swap fcp_cmnd */
cnt = sizeof(struct fcp_cmnd) / sizeof(u64);
for (i = 0; i < cnt; i++) {
- *fcp_cmnd = cpu_to_be64(tmp_fcp_cmnd[i]);
- fcp_cmnd++;
+ *raw_fcp_cmnd = cpu_to_be64(tmp_fcp_cmnd[i]);
+ raw_fcp_cmnd++;
}
/* Rx Write Tx Read */
- task->rx_wr_tx_rd.rx_id = 0xffff;
+ task->rxwr_txrd.const_ctx.data_2_trns = io_req->data_xfer_len;
+
+ context_id = tgt->context_id;
+ task->rxwr_txrd.const_ctx.init_flags = context_id <<
+ FCOE_TCE_RX_WR_TX_RD_CONST_CID_SHIFT;
+
+ /* rx flags */
+ /* Set state to "waiting for the first packet" */
+ task->rxwr_txrd.var_ctx.rx_flags |= 1 <<
+ FCOE_TCE_RX_WR_TX_RD_VAR_EXP_FIRST_FRAME_SHIFT;
+
+ task->rxwr_txrd.var_ctx.rx_id = 0xffff;
/* Rx Only */
- if (task_type == FCOE_TASK_TYPE_READ) {
+ if (task_type != FCOE_TASK_TYPE_READ)
+ return;
- bd_count = bd_tbl->bd_valid;
+ sgl = &task->rxwr_only.union_ctx.read_info.sgl_ctx.sgl;
+ bd_count = bd_tbl->bd_valid;
+
+ if (dev_type == TYPE_DISK) {
if (bd_count == 1) {
struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl;
- task->rx_wr_only.sgl_ctx.single_sge.cur_buf_addr.lo =
- fcoe_bd_tbl->buf_addr_lo;
- task->rx_wr_only.sgl_ctx.single_sge.cur_buf_addr.hi =
- fcoe_bd_tbl->buf_addr_hi;
- task->rx_wr_only.sgl_ctx.single_sge.cur_buf_rem =
- fcoe_bd_tbl->buf_len;
- task->tx_wr_rx_rd.init_flags |= 1 <<
- FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE_SHIFT;
+ cached_sge->cur_buf_addr.lo = fcoe_bd_tbl->buf_addr_lo;
+ cached_sge->cur_buf_addr.hi = fcoe_bd_tbl->buf_addr_hi;
+ cached_sge->cur_buf_rem = fcoe_bd_tbl->buf_len;
+ task->txwr_rxrd.const_ctx.init_flags |= 1 <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE_SHIFT;
+ } else if (bd_count == 2) {
+ struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl;
+
+ cached_sge->cur_buf_addr.lo = fcoe_bd_tbl->buf_addr_lo;
+ cached_sge->cur_buf_addr.hi = fcoe_bd_tbl->buf_addr_hi;
+ cached_sge->cur_buf_rem = fcoe_bd_tbl->buf_len;
+
+ fcoe_bd_tbl++;
+ cached_sge->second_buf_addr.lo =
+ fcoe_bd_tbl->buf_addr_lo;
+ cached_sge->second_buf_addr.hi =
+ fcoe_bd_tbl->buf_addr_hi;
+ cached_sge->second_buf_rem = fcoe_bd_tbl->buf_len;
+ task->txwr_rxrd.const_ctx.init_flags |= 1 <<
+ FCOE_TCE_TX_WR_RX_RD_CONST_CACHED_SGE_SHIFT;
} else {
- task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo =
- (u32)bd_tbl->bd_tbl_dma;
- task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi =
+ sgl->mul_sgl.cur_sge_addr.lo = (u32)bd_tbl->bd_tbl_dma;
+ sgl->mul_sgl.cur_sge_addr.hi =
(u32)((u64)bd_tbl->bd_tbl_dma >> 32);
- task->rx_wr_only.sgl_ctx.mul_sges.sgl_size =
- bd_tbl->bd_valid;
+ sgl->mul_sgl.sgl_size = bd_count;
}
+ } else {
+ sgl->mul_sgl.cur_sge_addr.lo = (u32)bd_tbl->bd_tbl_dma;
+ sgl->mul_sgl.cur_sge_addr.hi =
+ (u32)((u64)bd_tbl->bd_tbl_dma >> 32);
+ sgl->mul_sgl.sgl_size = bd_count;
}
}
@@ -1545,6 +1869,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba)
int rc = 0;
struct regpair *task_ctx_bdt;
dma_addr_t addr;
+ int task_ctx_arr_sz;
int i;
/*
@@ -1562,13 +1887,13 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba)
rc = -1;
goto out;
}
- memset(hba->task_ctx_bd_tbl, 0, PAGE_SIZE);
/*
* Allocate task_ctx which is an array of pointers pointing to
* a page containing 32 task contexts
*/
- hba->task_ctx = kzalloc((BNX2FC_TASK_CTX_ARR_SZ * sizeof(void *)),
+ task_ctx_arr_sz = (hba->max_tasks / BNX2FC_TASKS_PER_PAGE);
+ hba->task_ctx = kzalloc((task_ctx_arr_sz * sizeof(void *)),
GFP_KERNEL);
if (!hba->task_ctx) {
printk(KERN_ERR PFX "unable to allocate task context array\n");
@@ -1579,7 +1904,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba)
/*
* Allocate task_ctx_dma which is an array of dma addresses
*/
- hba->task_ctx_dma = kmalloc((BNX2FC_TASK_CTX_ARR_SZ *
+ hba->task_ctx_dma = kmalloc((task_ctx_arr_sz *
sizeof(dma_addr_t)), GFP_KERNEL);
if (!hba->task_ctx_dma) {
printk(KERN_ERR PFX "unable to alloc context mapping array\n");
@@ -1588,7 +1913,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba)
}
task_ctx_bdt = (struct regpair *)hba->task_ctx_bd_tbl;
- for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) {
+ for (i = 0; i < task_ctx_arr_sz; i++) {
hba->task_ctx[i] = dma_alloc_coherent(&hba->pcidev->dev,
PAGE_SIZE,
@@ -1599,7 +1924,6 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba)
rc = -1;
goto out3;
}
- memset(hba->task_ctx[i], 0, PAGE_SIZE);
addr = (u64)hba->task_ctx_dma[i];
task_ctx_bdt->hi = cpu_to_le32((u64)addr >> 32);
task_ctx_bdt->lo = cpu_to_le32((u32)addr);
@@ -1608,7 +1932,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba)
return 0;
out3:
- for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) {
+ for (i = 0; i < task_ctx_arr_sz; i++) {
if (hba->task_ctx[i]) {
dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
@@ -1632,6 +1956,7 @@ out:
void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba)
{
+ int task_ctx_arr_sz;
int i;
if (hba->task_ctx_bd_tbl) {
@@ -1641,8 +1966,9 @@ void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba)
hba->task_ctx_bd_tbl = NULL;
}
+ task_ctx_arr_sz = (hba->max_tasks / BNX2FC_TASKS_PER_PAGE);
if (hba->task_ctx) {
- for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) {
+ for (i = 0; i < task_ctx_arr_sz; i++) {
if (hba->task_ctx[i]) {
dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
hba->task_ctx[i],
@@ -1662,26 +1988,29 @@ static void bnx2fc_free_hash_table(struct bnx2fc_hba *hba)
{
int i;
int segment_count;
- int hash_table_size;
u32 *pbl;
- segment_count = hba->hash_tbl_segment_count;
- hash_table_size = BNX2FC_NUM_MAX_SESS * BNX2FC_MAX_ROWS_IN_HASH_TBL *
- sizeof(struct fcoe_hash_table_entry);
+ if (hba->hash_tbl_segments) {
- pbl = hba->hash_tbl_pbl;
- for (i = 0; i < segment_count; ++i) {
- dma_addr_t dma_address;
+ pbl = hba->hash_tbl_pbl;
+ if (pbl) {
+ segment_count = hba->hash_tbl_segment_count;
+ for (i = 0; i < segment_count; ++i) {
+ dma_addr_t dma_address;
- dma_address = le32_to_cpu(*pbl);
- ++pbl;
- dma_address += ((u64)le32_to_cpu(*pbl)) << 32;
- ++pbl;
- dma_free_coherent(&hba->pcidev->dev,
- BNX2FC_HASH_TBL_CHUNK_SIZE,
- hba->hash_tbl_segments[i],
- dma_address);
+ dma_address = le32_to_cpu(*pbl);
+ ++pbl;
+ dma_address += ((u64)le32_to_cpu(*pbl)) << 32;
+ ++pbl;
+ dma_free_coherent(&hba->pcidev->dev,
+ BNX2FC_HASH_TBL_CHUNK_SIZE,
+ hba->hash_tbl_segments[i],
+ dma_address);
+ }
+ }
+ kfree(hba->hash_tbl_segments);
+ hba->hash_tbl_segments = NULL;
}
if (hba->hash_tbl_pbl) {
@@ -1719,41 +2048,27 @@ static int bnx2fc_allocate_hash_table(struct bnx2fc_hba *hba)
dma_segment_array = kzalloc(dma_segment_array_size, GFP_KERNEL);
if (!dma_segment_array) {
printk(KERN_ERR PFX "hash table pointers (dma) alloc failed\n");
- return -ENOMEM;
+ goto cleanup_ht;
}
for (i = 0; i < segment_count; ++i) {
- hba->hash_tbl_segments[i] =
- dma_alloc_coherent(&hba->pcidev->dev,
- BNX2FC_HASH_TBL_CHUNK_SIZE,
- &dma_segment_array[i],
- GFP_KERNEL);
+ hba->hash_tbl_segments[i] = dma_alloc_coherent(&hba->pcidev->dev,
+ BNX2FC_HASH_TBL_CHUNK_SIZE,
+ &dma_segment_array[i],
+ GFP_KERNEL);
if (!hba->hash_tbl_segments[i]) {
printk(KERN_ERR PFX "hash segment alloc failed\n");
- while (--i >= 0) {
- dma_free_coherent(&hba->pcidev->dev,
- BNX2FC_HASH_TBL_CHUNK_SIZE,
- hba->hash_tbl_segments[i],
- dma_segment_array[i]);
- hba->hash_tbl_segments[i] = NULL;
- }
- kfree(dma_segment_array);
- return -ENOMEM;
+ goto cleanup_dma;
}
- memset(hba->hash_tbl_segments[i], 0,
- BNX2FC_HASH_TBL_CHUNK_SIZE);
}
- hba->hash_tbl_pbl = dma_alloc_coherent(&hba->pcidev->dev,
- PAGE_SIZE,
+ hba->hash_tbl_pbl = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
&hba->hash_tbl_pbl_dma,
GFP_KERNEL);
if (!hba->hash_tbl_pbl) {
printk(KERN_ERR PFX "hash table pbl alloc failed\n");
- kfree(dma_segment_array);
- return -ENOMEM;
+ goto cleanup_dma;
}
- memset(hba->hash_tbl_pbl, 0, PAGE_SIZE);
pbl = hba->hash_tbl_pbl;
for (i = 0; i < segment_count; ++i) {
@@ -1766,16 +2081,28 @@ static int bnx2fc_allocate_hash_table(struct bnx2fc_hba *hba)
pbl = hba->hash_tbl_pbl;
i = 0;
while (*pbl && *(pbl + 1)) {
- u32 lo;
- u32 hi;
- lo = *pbl;
++pbl;
- hi = *pbl;
++pbl;
++i;
}
kfree(dma_segment_array);
return 0;
+
+cleanup_dma:
+ for (i = 0; i < segment_count; ++i) {
+ if (hba->hash_tbl_segments[i])
+ dma_free_coherent(&hba->pcidev->dev,
+ BNX2FC_HASH_TBL_CHUNK_SIZE,
+ hba->hash_tbl_segments[i],
+ dma_segment_array[i]);
+ }
+
+ kfree(dma_segment_array);
+
+cleanup_ht:
+ kfree(hba->hash_tbl_segments);
+ hba->hash_tbl_segments = NULL;
+ return -ENOMEM;
}
/**
@@ -1802,7 +2129,6 @@ int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba)
bnx2fc_free_fw_resc(hba);
return -ENOMEM;
}
- memset(hba->t2_hash_tbl_ptr, 0x00, mem_size);
mem_size = BNX2FC_NUM_MAX_SESS *
sizeof(struct fcoe_t2_hash_table_entry);
@@ -1814,7 +2140,6 @@ int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba)
bnx2fc_free_fw_resc(hba);
return -ENOMEM;
}
- memset(hba->t2_hash_tbl, 0x00, mem_size);
for (i = 0; i < BNX2FC_NUM_MAX_SESS; i++) {
addr = (unsigned long) hba->t2_hash_tbl_dma +
((i+1) * sizeof(struct fcoe_t2_hash_table_entry));
@@ -1831,8 +2156,7 @@ int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba)
return -ENOMEM;
}
- hba->stats_buffer = dma_alloc_coherent(&hba->pcidev->dev,
- PAGE_SIZE,
+ hba->stats_buffer = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
&hba->stats_buf_dma,
GFP_KERNEL);
if (!hba->stats_buffer) {
@@ -1840,7 +2164,6 @@ int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba)
bnx2fc_free_fw_resc(hba);
return -ENOMEM;
}
- memset(hba->stats_buffer, 0x00, PAGE_SIZE);
return 0;
}
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index b5b5c346d779..33057908f147 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -1,7 +1,9 @@
-/* bnx2fc_io.c: Broadcom NetXtreme II Linux FCoE offload driver.
+/* bnx2fc_io.c: QLogic Linux FCoE offload driver.
* IO manager and SCSI IO processing.
*
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,22 +19,21 @@
static int bnx2fc_split_bd(struct bnx2fc_cmd *io_req, u64 addr, int sg_len,
int bd_index);
static int bnx2fc_map_sg(struct bnx2fc_cmd *io_req);
-static void bnx2fc_build_bd_list_from_sg(struct bnx2fc_cmd *io_req);
-static int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
- struct bnx2fc_cmd *io_req);
+static int bnx2fc_build_bd_list_from_sg(struct bnx2fc_cmd *io_req);
static void bnx2fc_unmap_sg_list(struct bnx2fc_cmd *io_req);
static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req);
static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req,
struct fcoe_fcp_rsp_payload *fcp_rsp,
- u8 num_rq);
+ u8 num_rq, unsigned char *rq_data);
void bnx2fc_cmd_timer_set(struct bnx2fc_cmd *io_req,
unsigned int timer_msec)
{
- struct bnx2fc_hba *hba = io_req->port->priv;
+ struct bnx2fc_interface *interface = io_req->port->priv;
- if (queue_delayed_work(hba->timer_work_queue, &io_req->timeout_work,
- msecs_to_jiffies(timer_msec)))
+ if (queue_delayed_work(interface->timer_work_queue,
+ &io_req->timeout_work,
+ msecs_to_jiffies(timer_msec)))
kref_get(&io_req->refcount);
}
@@ -40,11 +41,8 @@ static void bnx2fc_cmd_timeout(struct work_struct *work)
{
struct bnx2fc_cmd *io_req = container_of(work, struct bnx2fc_cmd,
timeout_work.work);
- struct fc_lport *lport;
- struct fc_rport_priv *rdata;
u8 cmd_type = io_req->cmd_type;
struct bnx2fc_rport *tgt = io_req->tgt;
- int logo_issued;
int rc;
BNX2FC_IO_DBG(io_req, "cmd_timeout, cmd_type = %d,"
@@ -72,33 +70,22 @@ static void bnx2fc_cmd_timeout(struct work_struct *work)
&io_req->req_flags)) {
/* Handle eh_abort timeout */
BNX2FC_IO_DBG(io_req, "eh_abort timed out\n");
- complete(&io_req->tm_done);
+ complete(&io_req->abts_done);
} else if (test_bit(BNX2FC_FLAG_ISSUE_ABTS,
&io_req->req_flags)) {
/* Handle internally generated ABTS timeout */
BNX2FC_IO_DBG(io_req, "ABTS timed out refcnt = %d\n",
- io_req->refcount.refcount.counter);
+ kref_read(&io_req->refcount));
if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags))) {
-
- lport = io_req->port->lport;
- rdata = io_req->tgt->rdata;
- logo_issued = test_and_set_bit(
- BNX2FC_FLAG_EXPL_LOGO,
- &tgt->flags);
+ /*
+ * Cleanup and return original command to
+ * mid-layer.
+ */
+ bnx2fc_initiate_cleanup(io_req);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
- /* Explicitly logo the target */
- if (!logo_issued) {
- BNX2FC_IO_DBG(io_req, "Explicit "
- "logo - tgt flags = 0x%lx\n",
- tgt->flags);
-
- mutex_lock(&lport->disc.disc_mutex);
- lport->tt.rport_logoff(rdata);
- mutex_unlock(&lport->disc.disc_mutex);
- }
return;
}
} else {
@@ -116,28 +103,10 @@ static void bnx2fc_cmd_timeout(struct work_struct *work)
rc = bnx2fc_initiate_abts(io_req);
if (rc == SUCCESS)
goto done;
- /*
- * Explicitly logo the target if
- * abts initiation fails
- */
- lport = io_req->port->lport;
- rdata = io_req->tgt->rdata;
- logo_issued = test_and_set_bit(
- BNX2FC_FLAG_EXPL_LOGO,
- &tgt->flags);
+
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
- if (!logo_issued) {
- BNX2FC_IO_DBG(io_req, "Explicit "
- "logo - tgt flags = 0x%lx\n",
- tgt->flags);
-
-
- mutex_lock(&lport->disc.disc_mutex);
- lport->tt.rport_logoff(rdata);
- mutex_unlock(&lport->disc.disc_mutex);
- }
return;
} else {
BNX2FC_IO_DBG(io_req, "IO already in "
@@ -152,22 +121,9 @@ static void bnx2fc_cmd_timeout(struct work_struct *work)
if (!test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags)) {
- lport = io_req->port->lport;
- rdata = io_req->tgt->rdata;
- logo_issued = test_and_set_bit(
- BNX2FC_FLAG_EXPL_LOGO,
- &tgt->flags);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
- /* Explicitly logo the target */
- if (!logo_issued) {
- BNX2FC_IO_DBG(io_req, "Explicitly logo"
- "(els)\n");
- mutex_lock(&lport->disc.disc_mutex);
- lport->tt.rport_logoff(rdata);
- mutex_unlock(&lport->disc.disc_mutex);
- }
return;
}
} else {
@@ -217,26 +173,42 @@ static void bnx2fc_scsi_done(struct bnx2fc_cmd *io_req, int err_code)
return;
BNX2FC_IO_DBG(io_req, "scsi_done. err_code = 0x%x\n", err_code);
+ if (test_bit(BNX2FC_FLAG_CMD_LOST, &io_req->req_flags)) {
+ /* Do not call scsi done for this IO */
+ return;
+ }
+
bnx2fc_unmap_sg_list(io_req);
io_req->sc_cmd = NULL;
+
+ /* Sanity checks before returning command to mid-layer */
if (!sc_cmd) {
printk(KERN_ERR PFX "scsi_done - sc_cmd NULL. "
"IO(0x%x) already cleaned up\n",
io_req->xid);
return;
}
+ if (!sc_cmd->device) {
+ pr_err(PFX "0x%x: sc_cmd->device is NULL.\n", io_req->xid);
+ return;
+ }
+ if (!sc_cmd->device->host) {
+ pr_err(PFX "0x%x: sc_cmd->device->host is NULL.\n",
+ io_req->xid);
+ return;
+ }
+
sc_cmd->result = err_code << 16;
BNX2FC_IO_DBG(io_req, "sc=%p, result=0x%x, retries=%d, allowed=%d\n",
sc_cmd, host_byte(sc_cmd->result), sc_cmd->retries,
sc_cmd->allowed);
scsi_set_resid(sc_cmd, scsi_bufflen(sc_cmd));
- sc_cmd->SCp.ptr = NULL;
- sc_cmd->scsi_done(sc_cmd);
+ bnx2fc_priv(sc_cmd)->io_req = NULL;
+ scsi_done(sc_cmd);
}
-struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba,
- u16 min_xid, u16 max_xid)
+struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba)
{
struct bnx2fc_cmd_mgr *cmgr;
struct io_bdt *bdt_info;
@@ -248,6 +220,8 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba,
int num_ios, num_pri_ios;
size_t bd_tbl_sz;
int arr_sz = num_possible_cpus() + 1;
+ u16 min_xid = BNX2FC_MIN_XID;
+ u16 max_xid = hba->max_xid;
if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN) {
printk(KERN_ERR PFX "cmd_mgr_alloc: Invalid min_xid 0x%x \
@@ -266,21 +240,23 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba,
return NULL;
}
- cmgr->free_list = kzalloc(sizeof(*cmgr->free_list) *
- arr_sz, GFP_KERNEL);
+ cmgr->hba = hba;
+ cmgr->free_list = kcalloc(arr_sz, sizeof(*cmgr->free_list),
+ GFP_KERNEL);
if (!cmgr->free_list) {
printk(KERN_ERR PFX "failed to alloc free_list\n");
goto mem_err;
}
- cmgr->free_list_lock = kzalloc(sizeof(*cmgr->free_list_lock) *
- arr_sz, GFP_KERNEL);
+ cmgr->free_list_lock = kcalloc(arr_sz, sizeof(*cmgr->free_list_lock),
+ GFP_KERNEL);
if (!cmgr->free_list_lock) {
printk(KERN_ERR PFX "failed to alloc free_list_lock\n");
+ kfree(cmgr->free_list);
+ cmgr->free_list = NULL;
goto mem_err;
}
- cmgr->hba = hba;
cmgr->cmds = (struct bnx2fc_cmd **)(cmgr + 1);
for (i = 0; i < arr_sz; i++) {
@@ -294,7 +270,7 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba,
* of slow path requests.
*/
xid = BNX2FC_MIN_XID;
- num_pri_ios = num_ios - BNX2FC_ELSTM_XIDS;
+ num_pri_ios = num_ios - hba->elstm_xids;
for (i = 0; i < num_ios; i++) {
io_req = kzalloc(sizeof(*io_req), GFP_KERNEL);
@@ -319,7 +295,7 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba,
/* Allocate pool of io_bdts - one for each bnx2fc_cmd */
mem_size = num_ios * sizeof(struct io_bdt *);
- cmgr->io_bdt_pool = kmalloc(mem_size, GFP_KERNEL);
+ cmgr->io_bdt_pool = kzalloc(mem_size, GFP_KERNEL);
if (!cmgr->io_bdt_pool) {
printk(KERN_ERR PFX "failed to alloc io_bdt_pool\n");
goto mem_err;
@@ -363,7 +339,7 @@ void bnx2fc_cmd_mgr_free(struct bnx2fc_cmd_mgr *cmgr)
struct bnx2fc_hba *hba = cmgr->hba;
size_t bd_tbl_sz;
u16 min_xid = BNX2FC_MIN_XID;
- u16 max_xid = BNX2FC_MAX_XID;
+ u16 max_xid = hba->max_xid;
int num_ios;
int i;
@@ -401,11 +377,10 @@ free_cmd_pool:
goto free_cmgr;
for (i = 0; i < num_possible_cpus() + 1; i++) {
- struct list_head *list;
- struct list_head *tmp;
+ struct bnx2fc_cmd *tmp, *io_req;
- list_for_each_safe(list, tmp, &cmgr->free_list[i]) {
- struct bnx2fc_cmd *io_req = (struct bnx2fc_cmd *)list;
+ list_for_each_entry_safe(io_req, tmp,
+ &cmgr->free_list[i], link) {
list_del(&io_req->link);
kfree(io_req);
}
@@ -419,12 +394,13 @@ free_cmgr:
struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type)
{
struct fcoe_port *port = tgt->port;
- struct bnx2fc_hba *hba = port->priv;
- struct bnx2fc_cmd_mgr *cmd_mgr = hba->cmd_mgr;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_cmd_mgr *cmd_mgr = interface->hba->cmd_mgr;
struct bnx2fc_cmd *io_req;
struct list_head *listp;
struct io_bdt *bd_tbl;
int index = RESERVE_FREE_LIST_INDEX;
+ u32 free_sqes;
u32 max_sqes;
u16 xid;
@@ -445,8 +421,10 @@ struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type)
* cmgr lock
*/
spin_lock_bh(&cmd_mgr->free_list_lock[index]);
+ free_sqes = atomic_read(&tgt->free_sqes);
if ((list_empty(&(cmd_mgr->free_list[index]))) ||
- (tgt->num_active_ios.counter >= max_sqes)) {
+ (tgt->num_active_ios.counter >= max_sqes) ||
+ (free_sqes + max_sqes <= BNX2FC_SQ_WQES_MAX)) {
BNX2FC_TGT_DBG(tgt, "No free els_tm cmds available "
"ios(%d):sqes(%d)\n",
tgt->num_active_ios.counter, tgt->max_sqes);
@@ -463,6 +441,7 @@ struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type)
xid = io_req->xid;
cmd_mgr->cmds[xid] = io_req;
atomic_inc(&tgt->num_active_ios);
+ atomic_dec(&tgt->free_sqes);
spin_unlock_bh(&cmd_mgr->free_list_lock[index]);
INIT_LIST_HEAD(&io_req->link);
@@ -481,17 +460,19 @@ struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type)
kref_init(&io_req->refcount);
return io_req;
}
-static struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt)
+
+struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt)
{
struct fcoe_port *port = tgt->port;
- struct bnx2fc_hba *hba = port->priv;
- struct bnx2fc_cmd_mgr *cmd_mgr = hba->cmd_mgr;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_cmd_mgr *cmd_mgr = interface->hba->cmd_mgr;
struct bnx2fc_cmd *io_req;
struct list_head *listp;
struct io_bdt *bd_tbl;
+ u32 free_sqes;
u32 max_sqes;
u16 xid;
- int index = get_cpu();
+ int index = raw_smp_processor_id();
max_sqes = BNX2FC_SCSI_MAX_SQES;
/*
@@ -499,10 +480,11 @@ static struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt)
* cmgr lock
*/
spin_lock_bh(&cmd_mgr->free_list_lock[index]);
+ free_sqes = atomic_read(&tgt->free_sqes);
if ((list_empty(&cmd_mgr->free_list[index])) ||
- (tgt->num_active_ios.counter >= max_sqes)) {
+ (tgt->num_active_ios.counter >= max_sqes) ||
+ (free_sqes + max_sqes <= BNX2FC_SQ_WQES_MAX)) {
spin_unlock_bh(&cmd_mgr->free_list_lock[index]);
- put_cpu();
return NULL;
}
@@ -513,8 +495,8 @@ static struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt)
xid = io_req->xid;
cmd_mgr->cmds[xid] = io_req;
atomic_inc(&tgt->num_active_ios);
+ atomic_dec(&tgt->free_sqes);
spin_unlock_bh(&cmd_mgr->free_list_lock[index]);
- put_cpu();
INIT_LIST_HEAD(&io_req->link);
@@ -562,7 +544,8 @@ void bnx2fc_cmd_release(struct kref *ref)
static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req)
{
struct bnx2fc_mp_req *mp_req = &(io_req->mp_req);
- struct bnx2fc_hba *hba = io_req->port->priv;
+ struct bnx2fc_interface *interface = io_req->port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
size_t sz = sizeof(struct fcoe_bd_ctx);
/* clear tm flags */
@@ -580,13 +563,13 @@ static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req)
mp_req->mp_resp_bd = NULL;
}
if (mp_req->req_buf) {
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
mp_req->req_buf,
mp_req->req_buf_dma);
mp_req->req_buf = NULL;
}
if (mp_req->resp_buf) {
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
mp_req->resp_buf,
mp_req->resp_buf_dma);
mp_req->resp_buf = NULL;
@@ -598,16 +581,21 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req)
struct bnx2fc_mp_req *mp_req;
struct fcoe_bd_ctx *mp_req_bd;
struct fcoe_bd_ctx *mp_resp_bd;
- struct bnx2fc_hba *hba = io_req->port->priv;
+ struct bnx2fc_interface *interface = io_req->port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
dma_addr_t addr;
size_t sz;
mp_req = (struct bnx2fc_mp_req *)&(io_req->mp_req);
memset(mp_req, 0, sizeof(struct bnx2fc_mp_req));
- mp_req->req_len = sizeof(struct fcp_cmnd);
- io_req->data_xfer_len = mp_req->req_len;
- mp_req->req_buf = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ if (io_req->cmd_type != BNX2FC_ELS) {
+ mp_req->req_len = sizeof(struct fcp_cmnd);
+ io_req->data_xfer_len = mp_req->req_len;
+ } else
+ mp_req->req_len = io_req->data_xfer_len;
+
+ mp_req->req_buf = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
&mp_req->req_buf_dma,
GFP_ATOMIC);
if (!mp_req->req_buf) {
@@ -616,7 +604,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req)
return FAILED;
}
- mp_req->resp_buf = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ mp_req->resp_buf = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
&mp_req->resp_buf_dma,
GFP_ATOMIC);
if (!mp_req->resp_buf) {
@@ -624,8 +612,8 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req)
bnx2fc_free_mp_resc(io_req);
return FAILED;
}
- memset(mp_req->req_buf, 0, PAGE_SIZE);
- memset(mp_req->resp_buf, 0, PAGE_SIZE);
+ memset(mp_req->req_buf, 0, CNIC_PAGE_SIZE);
+ memset(mp_req->resp_buf, 0, CNIC_PAGE_SIZE);
/* Allocate and map mp_req_bd and mp_resp_bd */
sz = sizeof(struct fcoe_bd_ctx);
@@ -640,7 +628,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req)
mp_req->mp_resp_bd = dma_alloc_coherent(&hba->pcidev->dev, sz,
&mp_req->mp_resp_bd_dma,
GFP_ATOMIC);
- if (!mp_req->mp_req_bd) {
+ if (!mp_req->mp_resp_bd) {
printk(KERN_ERR PFX "unable to alloc MP resp bd\n");
bnx2fc_free_mp_resc(io_req);
return FAILED;
@@ -650,7 +638,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req)
mp_req_bd = mp_req->mp_req_bd;
mp_req_bd->buf_addr_lo = (u32)addr & 0xffffffff;
mp_req_bd->buf_addr_hi = (u32)((u64)addr >> 32);
- mp_req_bd->buf_len = PAGE_SIZE;
+ mp_req_bd->buf_len = CNIC_PAGE_SIZE;
mp_req_bd->flags = 0;
/*
@@ -662,25 +650,23 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req)
addr = mp_req->resp_buf_dma;
mp_resp_bd->buf_addr_lo = (u32)addr & 0xffffffff;
mp_resp_bd->buf_addr_hi = (u32)((u64)addr >> 32);
- mp_resp_bd->buf_len = PAGE_SIZE;
+ mp_resp_bd->buf_len = CNIC_PAGE_SIZE;
mp_resp_bd->flags = 0;
return SUCCESS;
}
-static int bnx2fc_initiate_tmf(struct scsi_cmnd *sc_cmd, u8 tm_flags)
+static int bnx2fc_initiate_tmf(struct fc_lport *lport, struct fc_rport *rport,
+ u64 tm_lun, u8 tm_flags)
{
- struct fc_lport *lport;
- struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
- struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct fc_rport_libfc_priv *rp;
struct fcoe_port *port;
- struct bnx2fc_hba *hba;
+ struct bnx2fc_interface *interface;
struct bnx2fc_rport *tgt;
struct bnx2fc_cmd *io_req;
struct bnx2fc_mp_req *tm_req;
struct fcoe_task_ctx_entry *task;
struct fcoe_task_ctx_entry *task_page;
- struct Scsi_Host *host = sc_cmd->device->host;
struct fc_frame_header *fc_hdr;
struct fcp_cmnd *fcp_cmnd;
int task_idx, index;
@@ -689,17 +675,17 @@ static int bnx2fc_initiate_tmf(struct scsi_cmnd *sc_cmd, u8 tm_flags)
u32 sid, did;
unsigned long start = jiffies;
- lport = shost_priv(host);
port = lport_priv(lport);
- hba = port->priv;
+ interface = port->priv;
if (rport == NULL) {
- printk(KERN_ALERT PFX "device_reset: rport is NULL\n");
+ printk(KERN_ERR PFX "device_reset: rport is NULL\n");
rc = FAILED;
goto tmf_err;
}
+ rp = rport->dd_data;
- rc = fc_block_scsi_eh(sc_cmd);
+ rc = fc_block_rport(rport);
if (rc)
return rc;
@@ -728,7 +714,7 @@ retry_tmf:
goto retry_tmf;
}
/* Initialize rest of io_req fields */
- io_req->sc_cmd = sc_cmd;
+ io_req->sc_cmd = NULL;
io_req->port = port;
io_req->tgt = tgt;
@@ -737,18 +723,22 @@ retry_tmf:
rc = bnx2fc_init_mp_req(io_req);
if (rc == FAILED) {
printk(KERN_ERR PFX "Task mgmt MP request init failed\n");
+ spin_lock_bh(&tgt->tgt_lock);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ spin_unlock_bh(&tgt->tgt_lock);
goto tmf_err;
}
/* Set TM flags */
io_req->io_req_flags = 0;
tm_req->tm_flags = tm_flags;
+ tm_req->tm_lun = tm_lun;
/* Fill FCP_CMND */
bnx2fc_build_fcp_cmnd(io_req, (struct fcp_cmnd *)tm_req->req_buf);
fcp_cmnd = (struct fcp_cmnd *)tm_req->req_buf;
- memset(fcp_cmnd->fc_cdb, 0, sc_cmd->cmd_len);
+ int_to_scsilun(tm_lun, &fcp_cmnd->fc_lun);
+ memset(fcp_cmnd->fc_cdb, 0, BNX2FC_MAX_CMD_LEN);
fcp_cmnd->fc_dl = 0;
/* Fill FC header */
@@ -766,12 +756,11 @@ retry_tmf:
index = xid % BNX2FC_TASKS_PER_PAGE;
/* Initialize task context for this IO request */
- task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+ task_page = (struct fcoe_task_ctx_entry *)
+ interface->hba->task_ctx[task_idx];
task = &(task_page[index]);
bnx2fc_init_mp_task(io_req, task);
- sc_cmd->SCp.ptr = (char *)io_req;
-
/* Obtain free SQ entry */
spin_lock_bh(&tgt->tgt_lock);
bnx2fc_add_2_sq(tgt, xid);
@@ -780,28 +769,43 @@ retry_tmf:
io_req->on_tmf_queue = 1;
list_add_tail(&io_req->link, &tgt->active_tm_queue);
- init_completion(&io_req->tm_done);
- io_req->wait_for_comp = 1;
+ init_completion(&io_req->abts_done);
+ io_req->wait_for_abts_comp = 1;
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt);
spin_unlock_bh(&tgt->tgt_lock);
- rc = wait_for_completion_timeout(&io_req->tm_done,
- BNX2FC_TM_TIMEOUT * HZ);
+ rc = wait_for_completion_timeout(&io_req->abts_done,
+ interface->tm_timeout * HZ);
spin_lock_bh(&tgt->tgt_lock);
- io_req->wait_for_comp = 0;
- if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags)))
+ io_req->wait_for_abts_comp = 0;
+ if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags))) {
set_bit(BNX2FC_FLAG_TM_TIMEOUT, &io_req->req_flags);
+ if (io_req->on_tmf_queue) {
+ list_del_init(&io_req->link);
+ io_req->on_tmf_queue = 0;
+ }
+ io_req->wait_for_cleanup_comp = 1;
+ init_completion(&io_req->cleanup_done);
+ bnx2fc_initiate_cleanup(io_req);
+ spin_unlock_bh(&tgt->tgt_lock);
+ rc = wait_for_completion_timeout(&io_req->cleanup_done,
+ BNX2FC_FW_TIMEOUT);
+ spin_lock_bh(&tgt->tgt_lock);
+ io_req->wait_for_cleanup_comp = 0;
+ if (!rc)
+ kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ }
spin_unlock_bh(&tgt->tgt_lock);
if (!rc) {
- printk(KERN_ERR PFX "task mgmt command failed...\n");
+ BNX2FC_TGT_DBG(tgt, "task mgmt command failed...\n");
rc = FAILED;
} else {
- printk(KERN_ERR PFX "task mgmt command success...\n");
+ BNX2FC_TGT_DBG(tgt, "task mgmt command success...\n");
rc = SUCCESS;
}
tmf_err:
@@ -814,7 +818,7 @@ int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req)
struct bnx2fc_rport *tgt = io_req->tgt;
struct fc_rport *rport = tgt->rport;
struct fc_rport_priv *rdata = tgt->rdata;
- struct bnx2fc_hba *hba;
+ struct bnx2fc_interface *interface;
struct fcoe_port *port;
struct bnx2fc_cmd *abts_io_req;
struct fcoe_task_ctx_entry *task;
@@ -831,7 +835,7 @@ int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req)
BNX2FC_IO_DBG(io_req, "Entered bnx2fc_initiate_abts\n");
port = io_req->port;
- hba = port->priv;
+ interface = port->priv;
lport = port->lport;
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
@@ -841,7 +845,7 @@ int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req)
}
if (rport == NULL) {
- printk(KERN_ALERT PFX "initiate_abts: rport is NULL\n");
+ printk(KERN_ERR PFX "initiate_abts: rport is NULL\n");
rc = FAILED;
goto abts_err;
}
@@ -854,7 +858,7 @@ int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req)
abts_io_req = bnx2fc_elstm_alloc(tgt, BNX2FC_ABTS);
if (!abts_io_req) {
- printk(KERN_ERR PFX "abts: couldnt allocate cmd\n");
+ printk(KERN_ERR PFX "abts: couldn't allocate cmd\n");
rc = FAILED;
goto abts_err;
}
@@ -873,7 +877,7 @@ int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req)
/* Obtain oxid and rxid for the original exchange to be aborted */
fc_hdr->fh_ox_id = htons(io_req->xid);
- fc_hdr->fh_rx_id = htons(io_req->task->rx_wr_tx_rd.rx_id);
+ fc_hdr->fh_rx_id = htons(io_req->task->rxwr_txrd.var_ctx.rx_id);
sid = tgt->sid;
did = rport->port_id;
@@ -888,7 +892,8 @@ int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req)
index = xid % BNX2FC_TASKS_PER_PAGE;
/* Initialize task context for this IO request */
- task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+ task_page = (struct fcoe_task_ctx_entry *)
+ interface->hba->task_ctx[task_idx];
task = &(task_page[index]);
bnx2fc_init_mp_task(abts_io_req, task);
@@ -916,11 +921,78 @@ abts_err:
return rc;
}
+int bnx2fc_initiate_seq_cleanup(struct bnx2fc_cmd *orig_io_req, u32 offset,
+ enum fc_rctl r_ctl)
+{
+ struct bnx2fc_rport *tgt = orig_io_req->tgt;
+ struct bnx2fc_interface *interface;
+ struct fcoe_port *port;
+ struct bnx2fc_cmd *seq_clnp_req;
+ struct fcoe_task_ctx_entry *task;
+ struct fcoe_task_ctx_entry *task_page;
+ struct bnx2fc_els_cb_arg *cb_arg = NULL;
+ int task_idx, index;
+ u16 xid;
+ int rc = 0;
+
+ BNX2FC_IO_DBG(orig_io_req, "bnx2fc_initiate_seq_cleanup xid = 0x%x\n",
+ orig_io_req->xid);
+ kref_get(&orig_io_req->refcount);
+
+ port = orig_io_req->port;
+ interface = port->priv;
+
+ cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
+ if (!cb_arg) {
+ printk(KERN_ERR PFX "Unable to alloc cb_arg for seq clnup\n");
+ rc = -ENOMEM;
+ goto cleanup_err;
+ }
+
+ seq_clnp_req = bnx2fc_elstm_alloc(tgt, BNX2FC_SEQ_CLEANUP);
+ if (!seq_clnp_req) {
+ printk(KERN_ERR PFX "cleanup: couldn't allocate cmd\n");
+ rc = -ENOMEM;
+ kfree(cb_arg);
+ goto cleanup_err;
+ }
+ /* Initialize rest of io_req fields */
+ seq_clnp_req->sc_cmd = NULL;
+ seq_clnp_req->port = port;
+ seq_clnp_req->tgt = tgt;
+ seq_clnp_req->data_xfer_len = 0; /* No data transfer for cleanup */
+
+ xid = seq_clnp_req->xid;
+
+ task_idx = xid/BNX2FC_TASKS_PER_PAGE;
+ index = xid % BNX2FC_TASKS_PER_PAGE;
+
+ /* Initialize task context for this IO request */
+ task_page = (struct fcoe_task_ctx_entry *)
+ interface->hba->task_ctx[task_idx];
+ task = &(task_page[index]);
+ cb_arg->aborted_io_req = orig_io_req;
+ cb_arg->io_req = seq_clnp_req;
+ cb_arg->r_ctl = r_ctl;
+ cb_arg->offset = offset;
+ seq_clnp_req->cb_arg = cb_arg;
+
+ printk(KERN_ERR PFX "call init_seq_cleanup_task\n");
+ bnx2fc_init_seq_cleanup_task(seq_clnp_req, task, orig_io_req, offset);
+
+ /* Obtain free SQ entry */
+ bnx2fc_add_2_sq(tgt, xid);
+
+ /* Ring doorbell */
+ bnx2fc_ring_doorbell(tgt);
+cleanup_err:
+ return rc;
+}
+
int bnx2fc_initiate_cleanup(struct bnx2fc_cmd *io_req)
{
- struct fc_lport *lport;
struct bnx2fc_rport *tgt = io_req->tgt;
- struct bnx2fc_hba *hba;
+ struct bnx2fc_interface *interface;
struct fcoe_port *port;
struct bnx2fc_cmd *cleanup_io_req;
struct fcoe_task_ctx_entry *task;
@@ -933,12 +1005,11 @@ int bnx2fc_initiate_cleanup(struct bnx2fc_cmd *io_req)
BNX2FC_IO_DBG(io_req, "Entered bnx2fc_initiate_cleanup\n");
port = io_req->port;
- hba = port->priv;
- lport = port->lport;
+ interface = port->priv;
cleanup_io_req = bnx2fc_elstm_alloc(tgt, BNX2FC_CLEANUP);
if (!cleanup_io_req) {
- printk(KERN_ERR PFX "cleanup: couldnt allocate cmd\n");
+ printk(KERN_ERR PFX "cleanup: couldn't allocate cmd\n");
rc = -1;
goto cleanup_err;
}
@@ -955,7 +1026,8 @@ int bnx2fc_initiate_cleanup(struct bnx2fc_cmd *io_req)
index = xid % BNX2FC_TASKS_PER_PAGE;
/* Initialize task context for this IO request */
- task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
+ task_page = (struct fcoe_task_ctx_entry *)
+ interface->hba->task_ctx[task_idx];
task = &(task_page[index]);
orig_xid = io_req->xid;
@@ -966,6 +1038,9 @@ int bnx2fc_initiate_cleanup(struct bnx2fc_cmd *io_req)
/* Obtain free SQ entry */
bnx2fc_add_2_sq(tgt, xid);
+ /* Set flag that cleanup request is pending with the firmware */
+ set_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ, &io_req->req_flags);
+
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt);
@@ -983,7 +1058,10 @@ cleanup_err:
*/
int bnx2fc_eh_target_reset(struct scsi_cmnd *sc_cmd)
{
- return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_TGT_RESET);
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ struct fc_lport *lport = shost_priv(rport_to_shost(rport));
+
+ return bnx2fc_initiate_tmf(lport, rport, 0, FCP_TMF_TGT_RESET);
}
/**
@@ -996,7 +1074,45 @@ int bnx2fc_eh_target_reset(struct scsi_cmnd *sc_cmd)
*/
int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
{
- return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET);
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ struct fc_lport *lport = shost_priv(rport_to_shost(rport));
+
+ return bnx2fc_initiate_tmf(lport, rport, sc_cmd->device->lun,
+ FCP_TMF_LUN_RESET);
+}
+
+static int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req)
+ __must_hold(&tgt->tgt_lock)
+{
+ struct bnx2fc_rport *tgt = io_req->tgt;
+ unsigned int time_left;
+
+ init_completion(&io_req->cleanup_done);
+ io_req->wait_for_cleanup_comp = 1;
+ bnx2fc_initiate_cleanup(io_req);
+
+ spin_unlock_bh(&tgt->tgt_lock);
+
+ /*
+ * Can't wait forever on cleanup response lest we let the SCSI error
+ * handler wait forever
+ */
+ time_left = wait_for_completion_timeout(&io_req->cleanup_done,
+ BNX2FC_FW_TIMEOUT);
+ if (!time_left) {
+ BNX2FC_IO_DBG(io_req, "%s(): Wait for cleanup timed out.\n",
+ __func__);
+
+ /*
+ * Put the extra reference to the SCSI command since it would
+ * not have been returned in this case.
+ */
+ kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ }
+
+ spin_lock_bh(&tgt->tgt_lock);
+ io_req->wait_for_cleanup_comp = 0;
+ return SUCCESS;
}
/**
@@ -1014,8 +1130,8 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
struct bnx2fc_cmd *io_req;
struct fc_lport *lport;
struct bnx2fc_rport *tgt;
- int rc = FAILED;
-
+ int rc;
+ unsigned int time_left;
rc = fc_block_scsi_eh(sc_cmd);
if (rc)
@@ -1023,8 +1139,8 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
lport = shost_priv(sc_cmd->device->host);
if ((lport->state != LPORT_ST_READY) || !(lport->link_up)) {
- printk(KERN_ALERT PFX "eh_abort: link not ready\n");
- return rc;
+ printk(KERN_ERR PFX "eh_abort: link not ready\n");
+ return FAILED;
}
tgt = (struct bnx2fc_rport *)&rp[1];
@@ -1032,7 +1148,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
BNX2FC_TGT_DBG(tgt, "Entered bnx2fc_eh_abort\n");
spin_lock_bh(&tgt->tgt_lock);
- io_req = (struct bnx2fc_cmd *)sc_cmd->SCp.ptr;
+ io_req = bnx2fc_priv(sc_cmd)->io_req;
if (!io_req) {
/* Command might have just completed */
printk(KERN_ERR PFX "eh_abort: io_req is NULL\n");
@@ -1040,7 +1156,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
return SUCCESS;
}
BNX2FC_IO_DBG(io_req, "eh_abort - refcnt = %d\n",
- io_req->refcount.refcount.counter);
+ kref_read(&io_req->refcount));
/* Hold IO request across abort processing */
kref_get(&io_req->refcount);
@@ -1054,7 +1170,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
* io_req is no longer in the active_q.
*/
if (tgt->flush_in_prog) {
- printk(KERN_ALERT PFX "eh_abort: io_req (xid = 0x%x) "
+ printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
"flush in progress\n", io_req->xid);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
@@ -1062,19 +1178,14 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
}
if (io_req->on_active_queue == 0) {
- printk(KERN_ALERT PFX "eh_abort: io_req (xid = 0x%x) "
+ printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
"not on active_q\n", io_req->xid);
/*
- * This condition can happen only due to the FW bug,
- * where we do not receive cleanup response from
- * the FW. Handle this case gracefully by erroring
- * back the IO request to SCSI-ml
+ * The IO is still with the FW.
+ * Return failure and let SCSI-ml retry eh_abort.
*/
- bnx2fc_scsi_done(io_req, DID_ABORT);
-
- kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
- return SUCCESS;
+ return FAILED;
}
/*
@@ -1089,40 +1200,72 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
/* Move IO req to retire queue */
list_add_tail(&io_req->link, &tgt->io_retire_queue);
- init_completion(&io_req->tm_done);
- io_req->wait_for_comp = 1;
+ init_completion(&io_req->abts_done);
+ init_completion(&io_req->cleanup_done);
- if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
- /* Cancel the current timer running on this io_req */
+ if (test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
+ printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
+ "already in abts processing\n", io_req->xid);
if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
- set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
- rc = bnx2fc_initiate_abts(io_req);
- } else {
- printk(KERN_ALERT PFX "eh_abort: io_req (xid = 0x%x) "
- "already in abts processing\n", io_req->xid);
- kref_put(&io_req->refcount, bnx2fc_cmd_release);
- spin_unlock_bh(&tgt->tgt_lock);
- return SUCCESS;
+ /*
+ * We don't want to hold off the upper layer timer so simply
+ * cleanup the command and return that I/O was successfully
+ * aborted.
+ */
+ bnx2fc_abts_cleanup(io_req);
+ /* This only occurs when an task abort was requested while ABTS
+ is in progress. Setting the IO_CLEANUP flag will skip the
+ RRQ process in the case when the fw generated SCSI_CMD cmpl
+ was a result from the ABTS request rather than the CLEANUP
+ request */
+ set_bit(BNX2FC_FLAG_IO_CLEANUP, &io_req->req_flags);
+ rc = FAILED;
+ goto done;
}
+
+ /* Cancel the current timer running on this io_req */
+ if (cancel_delayed_work(&io_req->timeout_work))
+ kref_put(&io_req->refcount,
+ bnx2fc_cmd_release); /* drop timer hold */
+ set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
+ io_req->wait_for_abts_comp = 1;
+ rc = bnx2fc_initiate_abts(io_req);
if (rc == FAILED) {
- kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ io_req->wait_for_cleanup_comp = 1;
+ bnx2fc_initiate_cleanup(io_req);
spin_unlock_bh(&tgt->tgt_lock);
- return rc;
+ wait_for_completion(&io_req->cleanup_done);
+ spin_lock_bh(&tgt->tgt_lock);
+ io_req->wait_for_cleanup_comp = 0;
+ goto done;
}
spin_unlock_bh(&tgt->tgt_lock);
- wait_for_completion(&io_req->tm_done);
+ /* Wait 2 * RA_TOV + 1 to be sure timeout function hasn't fired */
+ time_left = wait_for_completion_timeout(&io_req->abts_done,
+ msecs_to_jiffies(2 * rp->r_a_tov + 1));
+ if (time_left)
+ BNX2FC_IO_DBG(io_req,
+ "Timed out in eh_abort waiting for abts_done");
spin_lock_bh(&tgt->tgt_lock);
- io_req->wait_for_comp = 0;
- if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
- &io_req->req_flags))) {
+ io_req->wait_for_abts_comp = 0;
+ if (test_bit(BNX2FC_FLAG_IO_COMPL, &io_req->req_flags)) {
+ BNX2FC_IO_DBG(io_req, "IO completed in a different context\n");
+ rc = SUCCESS;
+ } else if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
+ &io_req->req_flags))) {
/* Let the scsi-ml try to recover this command */
printk(KERN_ERR PFX "abort failed, xid = 0x%x\n",
io_req->xid);
- rc = FAILED;
+ /*
+ * Cleanup firmware residuals before returning control back
+ * to SCSI ML.
+ */
+ rc = bnx2fc_abts_cleanup(io_req);
+ goto done;
} else {
/*
* We come here even when there was a race condition
@@ -1134,22 +1277,78 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
bnx2fc_scsi_done(io_req, DID_ABORT);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
}
-
+done:
/* release the reference taken in eh_abort */
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
return rc;
}
+void bnx2fc_process_seq_cleanup_compl(struct bnx2fc_cmd *seq_clnp_req,
+ struct fcoe_task_ctx_entry *task,
+ u8 rx_state)
+{
+ struct bnx2fc_els_cb_arg *cb_arg = seq_clnp_req->cb_arg;
+ struct bnx2fc_cmd *orig_io_req = cb_arg->aborted_io_req;
+ u32 offset = cb_arg->offset;
+ enum fc_rctl r_ctl = cb_arg->r_ctl;
+ int rc = 0;
+ struct bnx2fc_rport *tgt = orig_io_req->tgt;
+
+ BNX2FC_IO_DBG(orig_io_req, "Entered process_cleanup_compl xid = 0x%x"
+ "cmd_type = %d\n",
+ seq_clnp_req->xid, seq_clnp_req->cmd_type);
+
+ if (rx_state == FCOE_TASK_RX_STATE_IGNORED_SEQUENCE_CLEANUP) {
+ printk(KERN_ERR PFX "seq cleanup ignored - xid = 0x%x\n",
+ seq_clnp_req->xid);
+ goto free_cb_arg;
+ }
+
+ spin_unlock_bh(&tgt->tgt_lock);
+ rc = bnx2fc_send_srr(orig_io_req, offset, r_ctl);
+ spin_lock_bh(&tgt->tgt_lock);
+
+ if (rc)
+ printk(KERN_ERR PFX "clnup_compl: Unable to send SRR"
+ " IO will abort\n");
+ seq_clnp_req->cb_arg = NULL;
+ kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
+free_cb_arg:
+ kfree(cb_arg);
+ return;
+}
+
void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq)
{
BNX2FC_IO_DBG(io_req, "Entered process_cleanup_compl "
"refcnt = %d, cmd_type = %d\n",
- io_req->refcount.refcount.counter, io_req->cmd_type);
+ kref_read(&io_req->refcount), io_req->cmd_type);
+ /*
+ * Test whether there is a cleanup request pending. If not just
+ * exit.
+ */
+ if (!test_and_clear_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ,
+ &io_req->req_flags))
+ return;
+ /*
+ * If we receive a cleanup completion for this request then the
+ * firmware will not give us an abort completion for this request
+ * so clear any ABTS pending flags.
+ */
+ if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags) &&
+ !test_bit(BNX2FC_FLAG_ABTS_DONE, &io_req->req_flags)) {
+ set_bit(BNX2FC_FLAG_ABTS_DONE, &io_req->req_flags);
+ if (io_req->wait_for_abts_comp)
+ complete(&io_req->abts_done);
+ }
+
bnx2fc_scsi_done(io_req, DID_ERROR);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ if (io_req->wait_for_cleanup_comp)
+ complete(&io_req->cleanup_done);
}
void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
@@ -1164,7 +1363,7 @@ void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
BNX2FC_IO_DBG(io_req, "Entered process_abts_compl xid = 0x%x"
"refcnt = %d, cmd_type = %d\n",
io_req->xid,
- io_req->refcount.refcount.counter, io_req->cmd_type);
+ kref_read(&io_req->refcount), io_req->cmd_type);
if (test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags)) {
@@ -1173,6 +1372,16 @@ void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
return;
}
+ /*
+ * If we receive an ABTS completion here then we will not receive
+ * a cleanup completion so clear any cleanup pending flags.
+ */
+ if (test_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ, &io_req->req_flags)) {
+ clear_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ, &io_req->req_flags);
+ if (io_req->wait_for_cleanup_comp)
+ complete(&io_req->cleanup_done);
+ }
+
/* Do not issue RRQ as this IO is already cleanedup */
if (test_and_set_bit(BNX2FC_FLAG_IO_CLEANUP,
&io_req->req_flags))
@@ -1189,7 +1398,7 @@ void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
- r_ctl = task->cmn.general.rsp_info.abts_rsp.r_ctl;
+ r_ctl = (u8)task->rxwr_only.union_ctx.comp_info.abts_rsp.r_ctl;
switch (r_ctl) {
case FC_RCTL_BA_ACC:
@@ -1217,10 +1426,10 @@ void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
bnx2fc_cmd_timer_set(io_req, r_a_tov);
io_compl:
- if (io_req->wait_for_comp) {
+ if (io_req->wait_for_abts_comp) {
if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
&io_req->req_flags))
- complete(&io_req->tm_done);
+ complete(&io_req->abts_done);
} else {
/*
* We end up here when ABTS is issued as
@@ -1244,14 +1453,11 @@ io_compl:
static void bnx2fc_lun_reset_cmpl(struct bnx2fc_cmd *io_req)
{
- struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
struct bnx2fc_rport *tgt = io_req->tgt;
- struct list_head *list;
- struct list_head *tmp;
- struct bnx2fc_cmd *cmd;
- int tm_lun = sc_cmd->device->lun;
+ struct bnx2fc_cmd *cmd, *tmp;
+ struct bnx2fc_mp_req *tm_req = &io_req->mp_req;
+ u64 lun;
int rc = 0;
- int lun;
/* called with tgt_lock held */
BNX2FC_IO_DBG(io_req, "Entered bnx2fc_lun_reset_cmpl\n");
@@ -1259,11 +1465,12 @@ static void bnx2fc_lun_reset_cmpl(struct bnx2fc_cmd *io_req)
* Walk thru the active_ios queue and ABORT the IO
* that matches with the LUN that was reset
*/
- list_for_each_safe(list, tmp, &tgt->active_cmd_queue) {
+ list_for_each_entry_safe(cmd, tmp, &tgt->active_cmd_queue, link) {
BNX2FC_TGT_DBG(tgt, "LUN RST cmpl: scan for pending IOs\n");
- cmd = (struct bnx2fc_cmd *)list;
+ if (!cmd->sc_cmd)
+ continue;
lun = cmd->sc_cmd->device->lun;
- if (lun == tm_lun) {
+ if (lun == tm_req->tm_lun) {
/* Initiate ABTS on this cmd */
if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS,
&cmd->req_flags)) {
@@ -1286,9 +1493,7 @@ static void bnx2fc_lun_reset_cmpl(struct bnx2fc_cmd *io_req)
static void bnx2fc_tgt_reset_cmpl(struct bnx2fc_cmd *io_req)
{
struct bnx2fc_rport *tgt = io_req->tgt;
- struct list_head *list;
- struct list_head *tmp;
- struct bnx2fc_cmd *cmd;
+ struct bnx2fc_cmd *cmd, *tmp;
int rc = 0;
/* called with tgt_lock held */
@@ -1297,9 +1502,8 @@ static void bnx2fc_tgt_reset_cmpl(struct bnx2fc_cmd *io_req)
* Walk thru the active_ios queue and ABORT the IO
* that matches with the LUN that was reset
*/
- list_for_each_safe(list, tmp, &tgt->active_cmd_queue) {
+ list_for_each_entry_safe(cmd, tmp, &tgt->active_cmd_queue, link) {
BNX2FC_TGT_DBG(tgt, "TGT RST cmpl: scan for pending IOs\n");
- cmd = (struct bnx2fc_cmd *)list;
/* Initiate ABTS */
if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS,
&cmd->req_flags)) {
@@ -1318,7 +1522,8 @@ static void bnx2fc_tgt_reset_cmpl(struct bnx2fc_cmd *io_req)
}
void bnx2fc_process_tm_compl(struct bnx2fc_cmd *io_req,
- struct fcoe_task_ctx_entry *task, u8 num_rq)
+ struct fcoe_task_ctx_entry *task, u8 num_rq,
+ unsigned char *rq_data)
{
struct bnx2fc_mp_req *tm_req;
struct fc_frame_header *fc_hdr;
@@ -1344,19 +1549,20 @@ void bnx2fc_process_tm_compl(struct bnx2fc_cmd *io_req,
fc_hdr = &(tm_req->resp_fc_hdr);
hdr = (u64 *)fc_hdr;
temp_hdr = (u64 *)
- &task->cmn.general.cmd_info.mp_fc_frame.fc_hdr;
+ &task->rxwr_only.union_ctx.comp_info.mp_rsp.fc_hdr;
hdr[0] = cpu_to_be64(temp_hdr[0]);
hdr[1] = cpu_to_be64(temp_hdr[1]);
hdr[2] = cpu_to_be64(temp_hdr[2]);
- tm_req->resp_len = task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_off;
+ tm_req->resp_len =
+ task->rxwr_only.union_ctx.comp_info.mp_rsp.mp_payload_len;
rsp_buf = tm_req->resp_buf;
if (fc_hdr->fh_r_ctl == FC_RCTL_DD_CMD_STATUS) {
bnx2fc_parse_fcp_rsp(io_req,
(struct fcoe_fcp_rsp_payload *)
- rsp_buf, num_rq);
+ rsp_buf, num_rq, rq_data);
if (io_req->fcp_rsp_code == 0) {
/* TM successful */
if (tm_req->tm_flags & FCP_TMF_LUN_RESET)
@@ -1368,31 +1574,36 @@ void bnx2fc_process_tm_compl(struct bnx2fc_cmd *io_req,
printk(KERN_ERR PFX "tmf's fc_hdr r_ctl = 0x%x\n",
fc_hdr->fh_r_ctl);
}
- if (!sc_cmd->SCp.ptr) {
- printk(KERN_ALERT PFX "tm_compl: SCp.ptr is NULL\n");
- return;
- }
- switch (io_req->fcp_status) {
- case FC_GOOD:
- if (io_req->cdb_status == 0) {
- /* Good IO completion */
- sc_cmd->result = DID_OK << 16;
- } else {
- /* Transport status is good, SCSI status not good */
- sc_cmd->result = (DID_OK << 16) | io_req->cdb_status;
+ if (sc_cmd) {
+ if (!bnx2fc_priv(sc_cmd)->io_req) {
+ printk(KERN_ERR PFX "tm_compl: io_req is NULL\n");
+ return;
}
- if (io_req->fcp_resid)
- scsi_set_resid(sc_cmd, io_req->fcp_resid);
- break;
+ switch (io_req->fcp_status) {
+ case FC_GOOD:
+ if (io_req->cdb_status == 0) {
+ /* Good IO completion */
+ sc_cmd->result = DID_OK << 16;
+ } else {
+ /* Transport status is good, SCSI status not good */
+ sc_cmd->result = (DID_OK << 16) | io_req->cdb_status;
+ }
+ if (io_req->fcp_resid)
+ scsi_set_resid(sc_cmd, io_req->fcp_resid);
+ break;
- default:
- BNX2FC_IO_DBG(io_req, "process_tm_compl: fcp_status = %d\n",
- io_req->fcp_status);
- break;
- }
+ default:
+ BNX2FC_IO_DBG(io_req, "process_tm_compl: fcp_status = %d\n",
+ io_req->fcp_status);
+ break;
+ }
- sc_cmd = io_req->sc_cmd;
- io_req->sc_cmd = NULL;
+ sc_cmd = io_req->sc_cmd;
+ io_req->sc_cmd = NULL;
+
+ bnx2fc_priv(sc_cmd)->io_req = NULL;
+ scsi_done(sc_cmd);
+ }
/* check if the io_req exists in tgt's tmf_q */
if (io_req->on_tmf_queue) {
@@ -1401,17 +1612,14 @@ void bnx2fc_process_tm_compl(struct bnx2fc_cmd *io_req,
io_req->on_tmf_queue = 0;
} else {
- printk(KERN_ALERT PFX "Command not on active_cmd_queue!\n");
+ printk(KERN_ERR PFX "Command not on active_cmd_queue!\n");
return;
}
- sc_cmd->SCp.ptr = NULL;
- sc_cmd->scsi_done(sc_cmd);
-
kref_put(&io_req->refcount, bnx2fc_cmd_release);
- if (io_req->wait_for_comp) {
+ if (io_req->wait_for_abts_comp) {
BNX2FC_IO_DBG(io_req, "tm_compl - wake up the waiter\n");
- complete(&io_req->tm_done);
+ complete(&io_req->abts_done);
}
}
@@ -1442,6 +1650,8 @@ static int bnx2fc_split_bd(struct bnx2fc_cmd *io_req, u64 addr, int sg_len,
static int bnx2fc_map_sg(struct bnx2fc_cmd *io_req)
{
+ struct bnx2fc_interface *interface = io_req->port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct scsi_cmnd *sc = io_req->sc_cmd;
struct fcoe_bd_ctx *bd = io_req->bd_tbl->bd_tbl;
struct scatterlist *sg;
@@ -1453,7 +1663,13 @@ static int bnx2fc_map_sg(struct bnx2fc_cmd *io_req)
u64 addr;
int i;
- sg_count = scsi_dma_map(sc);
+ WARN_ON(scsi_sg_count(sc) > BNX2FC_MAX_BDS_PER_CMD);
+ /*
+ * Use dma_map_sg directly to ensure we're using the correct
+ * dev struct off of pcidev.
+ */
+ sg_count = dma_map_sg(&hba->pcidev->dev, scsi_sglist(sc),
+ scsi_sg_count(sc), sc->sc_data_direction);
scsi_for_each_sg(sc, sg, sg_count, i) {
sg_len = sg_dma_len(sg);
addr = sg_dma_address(sg);
@@ -1478,28 +1694,49 @@ static int bnx2fc_map_sg(struct bnx2fc_cmd *io_req)
return bd_count;
}
-static void bnx2fc_build_bd_list_from_sg(struct bnx2fc_cmd *io_req)
+static int bnx2fc_build_bd_list_from_sg(struct bnx2fc_cmd *io_req)
{
struct scsi_cmnd *sc = io_req->sc_cmd;
struct fcoe_bd_ctx *bd = io_req->bd_tbl->bd_tbl;
int bd_count;
- if (scsi_sg_count(sc))
+ if (scsi_sg_count(sc)) {
bd_count = bnx2fc_map_sg(io_req);
- else {
+ if (bd_count == 0)
+ return -ENOMEM;
+ } else {
bd_count = 0;
bd[0].buf_addr_lo = bd[0].buf_addr_hi = 0;
bd[0].buf_len = bd[0].flags = 0;
}
io_req->bd_tbl->bd_valid = bd_count;
+
+ /*
+ * Return the command to ML if BD count exceeds the max number
+ * that can be handled by FW.
+ */
+ if (bd_count > BNX2FC_FW_MAX_BDS_PER_CMD) {
+ pr_err("bd_count = %d exceeded FW supported max BD(255), task_id = 0x%x\n",
+ bd_count, io_req->xid);
+ return -ENOMEM;
+ }
+
+ return 0;
}
static void bnx2fc_unmap_sg_list(struct bnx2fc_cmd *io_req)
{
struct scsi_cmnd *sc = io_req->sc_cmd;
+ struct bnx2fc_interface *interface = io_req->port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
- if (io_req->bd_tbl->bd_valid && sc) {
- scsi_dma_unmap(sc);
+ /*
+ * Use dma_unmap_sg directly to ensure we're using the correct
+ * dev struct off of pcidev.
+ */
+ if (io_req->bd_tbl->bd_valid && sc && scsi_sg_count(sc)) {
+ dma_unmap_sg(&hba->pcidev->dev, scsi_sglist(sc),
+ scsi_sg_count(sc), sc->sc_data_direction);
io_req->bd_tbl->bd_valid = 0;
}
}
@@ -1507,60 +1744,34 @@ static void bnx2fc_unmap_sg_list(struct bnx2fc_cmd *io_req)
void bnx2fc_build_fcp_cmnd(struct bnx2fc_cmd *io_req,
struct fcp_cmnd *fcp_cmnd)
{
- struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
- char tag[2];
-
memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
- int_to_scsilun(sc_cmd->device->lun,
- (struct scsi_lun *) fcp_cmnd->fc_lun);
-
-
fcp_cmnd->fc_dl = htonl(io_req->data_xfer_len);
- memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len);
-
fcp_cmnd->fc_cmdref = 0;
fcp_cmnd->fc_pri_ta = 0;
fcp_cmnd->fc_tm_flags = io_req->mp_req.tm_flags;
fcp_cmnd->fc_flags = io_req->io_req_flags;
-
- if (scsi_populate_tag_msg(sc_cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- fcp_cmnd->fc_pri_ta = FCP_PTA_HEADQ;
- break;
- case ORDERED_QUEUE_TAG:
- fcp_cmnd->fc_pri_ta = FCP_PTA_ORDERED;
- break;
- default:
- fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
- break;
- }
- } else {
- fcp_cmnd->fc_pri_ta = 0;
- }
+ fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
}
static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req,
struct fcoe_fcp_rsp_payload *fcp_rsp,
- u8 num_rq)
+ u8 num_rq, unsigned char *rq_data)
{
struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
- struct bnx2fc_rport *tgt = io_req->tgt;
u8 rsp_flags = fcp_rsp->fcp_flags.flags;
u32 rq_buff_len = 0;
- int i;
- unsigned char *rq_data;
- unsigned char *dummy;
int fcp_sns_len = 0;
int fcp_rsp_len = 0;
io_req->fcp_status = FC_GOOD;
- io_req->fcp_resid = fcp_rsp->fcp_resid;
+ io_req->fcp_resid = 0;
+ if (rsp_flags & (FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER |
+ FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER))
+ io_req->fcp_resid = fcp_rsp->fcp_resid;
io_req->scsi_comp_flags = rsp_flags;
- CMD_SCSI_STATUS(sc_cmd) = io_req->cdb_status =
- fcp_rsp->scsi_status_code;
+ io_req->cdb_status = fcp_rsp->scsi_status_code;
/* Fetch fcp_rsp_info and fcp_sns_info if available */
if (num_rq) {
@@ -1588,25 +1799,17 @@ static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req,
if (rq_buff_len > num_rq * BNX2FC_RQ_BUF_SZ) {
/* Invalid sense sense length. */
- printk(KERN_ALERT PFX "invalid sns length %d\n",
+ printk(KERN_ERR PFX "invalid sns length %d\n",
rq_buff_len);
/* reset rq_buff_len */
rq_buff_len = num_rq * BNX2FC_RQ_BUF_SZ;
}
- rq_data = bnx2fc_get_next_rqe(tgt, 1);
-
- if (num_rq > 1) {
- /* We do not need extra sense data */
- for (i = 1; i < num_rq; i++)
- dummy = bnx2fc_get_next_rqe(tgt, 1);
- }
-
/* fetch fcp_rsp_code */
if ((fcp_rsp_len == 4) || (fcp_rsp_len == 8)) {
/* Only for task management function */
io_req->fcp_rsp_code = rq_data[3];
- printk(KERN_ERR PFX "fcp_rsp_code = %d\n",
+ BNX2FC_IO_DBG(io_req, "fcp_rsp_code = %d\n",
io_req->fcp_rsp_code);
}
@@ -1618,13 +1821,10 @@ static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req,
fcp_sns_len = SCSI_SENSE_BUFFERSIZE;
}
- memset(sc_cmd->sense_buffer, 0, sizeof(sc_cmd->sense_buffer));
+ memset(sc_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
if (fcp_sns_len)
memcpy(sc_cmd->sense_buffer, rq_data, fcp_sns_len);
- /* return RQ entries */
- for (i = 0; i < num_rq; i++)
- bnx2fc_return_rqe(tgt, 1);
}
}
@@ -1650,7 +1850,7 @@ int bnx2fc_queuecommand(struct Scsi_Host *host,
rval = fc_remote_port_chkready(rport);
if (rval) {
sc_cmd->result = rval;
- sc_cmd->scsi_done(sc_cmd);
+ scsi_done(sc_cmd);
return 0;
}
@@ -1663,12 +1863,6 @@ int bnx2fc_queuecommand(struct Scsi_Host *host,
tgt = (struct bnx2fc_rport *)&rp[1];
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
- if (test_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags)) {
- sc_cmd->result = DID_NO_CONNECT << 16;
- sc_cmd->scsi_done(sc_cmd);
- return 0;
-
- }
/*
* Session is not offloaded yet. Let SCSI-ml retry
* the command.
@@ -1676,32 +1870,45 @@ int bnx2fc_queuecommand(struct Scsi_Host *host,
rc = SCSI_MLQUEUE_TARGET_BUSY;
goto exit_qcmd;
}
+ if (tgt->retry_delay_timestamp) {
+ if (time_after(jiffies, tgt->retry_delay_timestamp)) {
+ tgt->retry_delay_timestamp = 0;
+ } else {
+ /* If retry_delay timer is active, flow off the ML */
+ rc = SCSI_MLQUEUE_TARGET_BUSY;
+ goto exit_qcmd;
+ }
+ }
+
+ spin_lock_bh(&tgt->tgt_lock);
io_req = bnx2fc_cmd_alloc(tgt);
if (!io_req) {
rc = SCSI_MLQUEUE_HOST_BUSY;
- goto exit_qcmd;
+ goto exit_qcmd_tgtlock;
}
io_req->sc_cmd = sc_cmd;
if (bnx2fc_post_io_req(tgt, io_req)) {
printk(KERN_ERR PFX "Unable to post io_req\n");
rc = SCSI_MLQUEUE_HOST_BUSY;
- goto exit_qcmd;
+ goto exit_qcmd_tgtlock;
}
+
+exit_qcmd_tgtlock:
+ spin_unlock_bh(&tgt->tgt_lock);
exit_qcmd:
return rc;
}
void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
- u8 num_rq)
+ u8 num_rq, unsigned char *rq_data)
{
struct fcoe_fcp_rsp_payload *fcp_rsp;
struct bnx2fc_rport *tgt = io_req->tgt;
struct scsi_cmnd *sc_cmd;
- struct Scsi_Host *host;
-
+ u16 scope = 0, qualifier = 0;
/* scsi_cmd_cmpl is called with tgt lock held */
@@ -1709,6 +1916,13 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
/* we will not receive ABTS response for this IO */
BNX2FC_IO_DBG(io_req, "Timer context finished processing "
"this scsi cmd\n");
+ if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP,
+ &io_req->req_flags)) {
+ BNX2FC_IO_DBG(io_req,
+ "Actual completion after cleanup request cleaning up\n");
+ bnx2fc_process_cleanup_compl(io_req, task, num_rq);
+ }
+ return;
}
/* Cancel the timeout_work, as we received IO completion */
@@ -1724,17 +1938,15 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
/* Fetch fcp_rsp from task context and perform cmd completion */
fcp_rsp = (struct fcoe_fcp_rsp_payload *)
- &(task->cmn.general.rsp_info.fcp_rsp.payload);
+ &(task->rxwr_only.union_ctx.comp_info.fcp_rsp.payload);
/* parse fcp_rsp and obtain sense data from RQ if available */
- bnx2fc_parse_fcp_rsp(io_req, fcp_rsp, num_rq);
+ bnx2fc_parse_fcp_rsp(io_req, fcp_rsp, num_rq, rq_data);
- host = sc_cmd->device->host;
- if (!sc_cmd->SCp.ptr) {
- printk(KERN_ERR PFX "SCp.ptr is NULL\n");
+ if (!bnx2fc_priv(sc_cmd)->io_req) {
+ printk(KERN_ERR PFX "io_req is NULL\n");
return;
}
- io_req->sc_cmd = NULL;
if (io_req->on_active_queue) {
list_del_init(&io_req->link);
@@ -1747,13 +1959,14 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
* between command abort and (late) completion.
*/
BNX2FC_IO_DBG(io_req, "xid not on active_cmd_queue\n");
- if (io_req->wait_for_comp)
+ if (io_req->wait_for_abts_comp)
if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
&io_req->req_flags))
- complete(&io_req->tm_done);
+ complete(&io_req->abts_done);
}
bnx2fc_unmap_sg_list(io_req);
+ io_req->sc_cmd = NULL;
switch (io_req->fcp_status) {
case FC_GOOD:
@@ -1766,59 +1979,90 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
" fcp_resid = 0x%x\n",
io_req->cdb_status, io_req->fcp_resid);
sc_cmd->result = (DID_OK << 16) | io_req->cdb_status;
+
+ if (io_req->cdb_status == SAM_STAT_TASK_SET_FULL ||
+ io_req->cdb_status == SAM_STAT_BUSY) {
+ /* Newer array firmware with BUSY or
+ * TASK_SET_FULL may return a status that needs
+ * the scope bits masked.
+ * Or a huge delay timestamp up to 27 minutes
+ * can result.
+ */
+ if (fcp_rsp->retry_delay_timer) {
+ /* Upper 2 bits */
+ scope = fcp_rsp->retry_delay_timer
+ & 0xC000;
+ /* Lower 14 bits */
+ qualifier = fcp_rsp->retry_delay_timer
+ & 0x3FFF;
+ }
+ if (scope > 0 && qualifier > 0 &&
+ qualifier <= 0x3FEF) {
+ /* Set the jiffies +
+ * retry_delay_timer * 100ms
+ * for the rport/tgt
+ */
+ tgt->retry_delay_timestamp = jiffies +
+ (qualifier * HZ / 10);
+ }
+ }
}
if (io_req->fcp_resid)
scsi_set_resid(sc_cmd, io_req->fcp_resid);
break;
default:
- printk(KERN_ALERT PFX "scsi_cmd_compl: fcp_status = %d\n",
+ printk(KERN_ERR PFX "scsi_cmd_compl: fcp_status = %d\n",
io_req->fcp_status);
break;
}
- sc_cmd->SCp.ptr = NULL;
- sc_cmd->scsi_done(sc_cmd);
+ bnx2fc_priv(sc_cmd)->io_req = NULL;
+ scsi_done(sc_cmd);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
}
-static int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
+int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
struct bnx2fc_cmd *io_req)
{
struct fcoe_task_ctx_entry *task;
struct fcoe_task_ctx_entry *task_page;
struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
struct fcoe_port *port = tgt->port;
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct fc_lport *lport = port->lport;
- struct fcoe_dev_stats *stats;
int task_idx, index;
u16 xid;
+ /* bnx2fc_post_io_req() is called with the tgt_lock held */
+
/* Initialize rest of io_req fields */
io_req->cmd_type = BNX2FC_SCSI_CMD;
io_req->port = port;
io_req->tgt = tgt;
io_req->data_xfer_len = scsi_bufflen(sc_cmd);
- sc_cmd->SCp.ptr = (char *)io_req;
+ bnx2fc_priv(sc_cmd)->io_req = io_req;
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
io_req->io_req_flags = BNX2FC_READ;
- stats->InputRequests++;
- stats->InputBytes += io_req->data_xfer_len;
+ this_cpu_inc(lport->stats->InputRequests);
+ this_cpu_add(lport->stats->InputBytes, io_req->data_xfer_len);
} else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
io_req->io_req_flags = BNX2FC_WRITE;
- stats->OutputRequests++;
- stats->OutputBytes += io_req->data_xfer_len;
+ this_cpu_inc(lport->stats->OutputRequests);
+ this_cpu_add(lport->stats->OutputBytes, io_req->data_xfer_len);
} else {
io_req->io_req_flags = 0;
- stats->ControlRequests++;
+ this_cpu_inc(lport->stats->ControlRequests);
}
- put_cpu();
xid = io_req->xid;
/* Build buffer descriptor list for firmware from sg list */
- bnx2fc_build_bd_list_from_sg(io_req);
+ if (bnx2fc_build_bd_list_from_sg(io_req)) {
+ printk(KERN_ERR PFX "BD list creation failed\n");
+ kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ return -EAGAIN;
+ }
task_idx = xid / BNX2FC_TASKS_PER_PAGE;
index = xid % BNX2FC_TASKS_PER_PAGE;
@@ -1828,24 +2072,21 @@ static int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
task = &(task_page[index]);
bnx2fc_init_task(io_req, task);
- spin_lock_bh(&tgt->tgt_lock);
-
if (tgt->flush_in_prog) {
printk(KERN_ERR PFX "Flush in progress..Host Busy\n");
kref_put(&io_req->refcount, bnx2fc_cmd_release);
- spin_unlock_bh(&tgt->tgt_lock);
return -EAGAIN;
}
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
printk(KERN_ERR PFX "Session not ready...post_io\n");
kref_put(&io_req->refcount, bnx2fc_cmd_release);
- spin_unlock_bh(&tgt->tgt_lock);
return -EAGAIN;
}
/* Time IO req */
- bnx2fc_cmd_timer_set(io_req, BNX2FC_IO_TIMEOUT);
+ if (tgt->io_timeout)
+ bnx2fc_cmd_timer_set(io_req, BNX2FC_IO_TIMEOUT);
/* Obtain free SQ entry */
bnx2fc_add_2_sq(tgt, xid);
@@ -1857,6 +2098,5 @@ static int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt);
- spin_unlock_bh(&tgt->tgt_lock);
return 0;
}
diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
index a2e3830bd268..77dcdfc412b1 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
@@ -1,8 +1,10 @@
-/* bnx2fc_tgt.c: Broadcom NetXtreme II Linux FCoE offload driver.
+/* bnx2fc_tgt.c: QLogic Linux FCoE offload driver.
* Handles operations such as session offload/upload etc, and manages
* session resources such as connection id and qp resources.
*
- * Copyright (c) 2008 - 2010 Broadcom Corporation
+ * Copyright (c) 2008-2013 Broadcom Corporation
+ * Copyright (c) 2014-2016 QLogic Corporation
+ * Copyright (c) 2016-2017 Cavium Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -12,8 +14,8 @@
*/
#include "bnx2fc.h"
-static void bnx2fc_upld_timer(unsigned long data);
-static void bnx2fc_ofld_timer(unsigned long data);
+static void bnx2fc_upld_timer(struct timer_list *t);
+static void bnx2fc_ofld_timer(struct timer_list *t);
static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
struct fcoe_port *port,
struct fc_rport_priv *rdata);
@@ -25,22 +27,23 @@ static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
static void bnx2fc_free_conn_id(struct bnx2fc_hba *hba, u32 conn_id);
-static void bnx2fc_upld_timer(unsigned long data)
+static void bnx2fc_upld_timer(struct timer_list *t)
{
- struct bnx2fc_rport *tgt = (struct bnx2fc_rport *)data;
+ struct bnx2fc_rport *tgt = timer_container_of(tgt, t, upld_timer);
BNX2FC_TGT_DBG(tgt, "upld_timer - Upload compl not received!!\n");
/* fake upload completion */
clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
+ clear_bit(BNX2FC_FLAG_ENABLED, &tgt->flags);
set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
wake_up_interruptible(&tgt->upld_wait);
}
-static void bnx2fc_ofld_timer(unsigned long data)
+static void bnx2fc_ofld_timer(struct timer_list *t)
{
- struct bnx2fc_rport *tgt = (struct bnx2fc_rport *)data;
+ struct bnx2fc_rport *tgt = timer_container_of(tgt, t, ofld_timer);
BNX2FC_TGT_DBG(tgt, "entered bnx2fc_ofld_timer\n");
/* NOTE: This function should never be called, as
@@ -55,17 +58,32 @@ static void bnx2fc_ofld_timer(unsigned long data)
* resources are freed up in bnx2fc_offload_session
*/
clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
+ clear_bit(BNX2FC_FLAG_ENABLED, &tgt->flags);
set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
wake_up_interruptible(&tgt->ofld_wait);
}
+static void bnx2fc_ofld_wait(struct bnx2fc_rport *tgt)
+{
+ timer_setup(&tgt->ofld_timer, bnx2fc_ofld_timer, 0);
+ mod_timer(&tgt->ofld_timer, jiffies + BNX2FC_FW_TIMEOUT);
+
+ wait_event_interruptible(tgt->ofld_wait,
+ (test_bit(
+ BNX2FC_FLAG_OFLD_REQ_CMPL,
+ &tgt->flags)));
+ if (signal_pending(current))
+ flush_signals(current);
+ timer_delete_sync(&tgt->ofld_timer);
+}
+
static void bnx2fc_offload_session(struct fcoe_port *port,
struct bnx2fc_rport *tgt,
struct fc_rport_priv *rdata)
{
- struct fc_lport *lport = rdata->local_port;
struct fc_rport *rport = rdata->rport;
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
int rval;
int i = 0;
@@ -75,7 +93,7 @@ static void bnx2fc_offload_session(struct fcoe_port *port,
if (rval) {
printk(KERN_ERR PFX "Failed to allocate conn id for "
"port_id (%6x)\n", rport->port_id);
- goto ofld_err;
+ goto tgt_init_err;
}
/* Allocate session resources */
@@ -102,17 +120,7 @@ retry_ofld:
* wait for the session is offloaded and enabled. 3 Secs
* should be ample time for this process to complete.
*/
- setup_timer(&tgt->ofld_timer, bnx2fc_ofld_timer, (unsigned long)tgt);
- mod_timer(&tgt->ofld_timer, jiffies + BNX2FC_FW_TIMEOUT);
-
- wait_event_interruptible(tgt->ofld_wait,
- (test_bit(
- BNX2FC_FLAG_OFLD_REQ_CMPL,
- &tgt->flags)));
- if (signal_pending(current))
- flush_signals(current);
-
- del_timer_sync(&tgt->ofld_timer);
+ bnx2fc_ofld_wait(tgt);
if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) {
if (test_and_clear_bit(BNX2FC_FLAG_CTX_ALLOC_FAILURE,
@@ -120,36 +128,43 @@ retry_ofld:
BNX2FC_TGT_DBG(tgt, "ctx_alloc_failure, "
"retry ofld..%d\n", i++);
msleep_interruptible(1000);
- if (i > 3) {
- i = 0;
+ if (i > 3)
goto ofld_err;
- }
goto retry_ofld;
}
goto ofld_err;
}
if (bnx2fc_map_doorbell(tgt)) {
printk(KERN_ERR PFX "map doorbell failed - no mem\n");
- /* upload will take care of cleaning up sess resc */
- lport->tt.rport_logoff(rdata);
+ goto ofld_err;
+ }
+ clear_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
+ rval = bnx2fc_send_session_enable_req(port, tgt);
+ if (rval) {
+ pr_err(PFX "enable session failed\n");
+ goto ofld_err;
}
+ bnx2fc_ofld_wait(tgt);
+ if (!(test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags)))
+ goto ofld_err;
return;
ofld_err:
/* couldn't offload the session. log off from this rport */
BNX2FC_TGT_DBG(tgt, "bnx2fc_offload_session - offload error\n");
- lport->tt.rport_logoff(rdata);
+ clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
/* Free session resources */
bnx2fc_free_session_resc(hba, tgt);
+tgt_init_err:
if (tgt->fcoe_conn_id != -1)
bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
+ fc_rport_logoff(rdata);
}
void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
{
struct bnx2fc_cmd *io_req;
- struct list_head *list;
- struct list_head *tmp;
+ struct bnx2fc_cmd *tmp;
int rc;
int i = 0;
BNX2FC_TGT_DBG(tgt, "Entered flush_active_ios - %d\n",
@@ -158,9 +173,8 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
spin_lock_bh(&tgt->tgt_lock);
tgt->flush_in_prog = 1;
- list_for_each_safe(list, tmp, &tgt->active_cmd_queue) {
+ list_for_each_entry_safe(io_req, tmp, &tgt->active_cmd_queue, link) {
i++;
- io_req = (struct bnx2fc_cmd *)list;
list_del_init(&io_req->link);
io_req->on_active_queue = 0;
BNX2FC_IO_DBG(io_req, "cmd_queue cleanup\n");
@@ -171,7 +185,7 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
/* Handle eh_abort timeout */
BNX2FC_IO_DBG(io_req, "eh_abort for IO "
"cleaned up\n");
- complete(&io_req->tm_done);
+ complete(&io_req->abts_done);
}
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
@@ -179,13 +193,27 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
set_bit(BNX2FC_FLAG_IO_COMPL, &io_req->req_flags);
set_bit(BNX2FC_FLAG_IO_CLEANUP, &io_req->req_flags);
- rc = bnx2fc_initiate_cleanup(io_req);
- BUG_ON(rc);
+
+ /* Do not issue cleanup when disable request failed */
+ if (test_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags))
+ bnx2fc_process_cleanup_compl(io_req, io_req->task, 0);
+ else {
+ rc = bnx2fc_initiate_cleanup(io_req);
+ BUG_ON(rc);
+ }
}
- list_for_each_safe(list, tmp, &tgt->els_queue) {
+ list_for_each_entry_safe(io_req, tmp, &tgt->active_tm_queue, link) {
+ i++;
+ list_del_init(&io_req->link);
+ io_req->on_tmf_queue = 0;
+ BNX2FC_IO_DBG(io_req, "tm_queue cleanup\n");
+ if (io_req->wait_for_abts_comp)
+ complete(&io_req->abts_done);
+ }
+
+ list_for_each_entry_safe(io_req, tmp, &tgt->els_queue, link) {
i++;
- io_req = (struct bnx2fc_cmd *)list;
list_del_init(&io_req->link);
io_req->on_active_queue = 0;
@@ -200,19 +228,32 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
io_req->cb_arg = NULL;
}
- rc = bnx2fc_initiate_cleanup(io_req);
- BUG_ON(rc);
+ /* Do not issue cleanup when disable request failed */
+ if (test_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags))
+ bnx2fc_process_cleanup_compl(io_req, io_req->task, 0);
+ else {
+ rc = bnx2fc_initiate_cleanup(io_req);
+ BUG_ON(rc);
+ }
}
- list_for_each_safe(list, tmp, &tgt->io_retire_queue) {
+ list_for_each_entry_safe(io_req, tmp, &tgt->io_retire_queue, link) {
i++;
- io_req = (struct bnx2fc_cmd *)list;
list_del_init(&io_req->link);
BNX2FC_IO_DBG(io_req, "retire_queue flush\n");
- if (cancel_delayed_work(&io_req->timeout_work))
+ if (cancel_delayed_work(&io_req->timeout_work)) {
+ if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
+ &io_req->req_flags)) {
+ /* Handle eh_abort timeout */
+ BNX2FC_IO_DBG(io_req, "eh_abort for IO "
+ "in retire_q\n");
+ if (io_req->wait_for_abts_comp)
+ complete(&io_req->abts_done);
+ }
kref_put(&io_req->refcount, bnx2fc_cmd_release);
+ }
clear_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags);
}
@@ -232,10 +273,24 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
spin_unlock_bh(&tgt->tgt_lock);
}
+static void bnx2fc_upld_wait(struct bnx2fc_rport *tgt)
+{
+ timer_setup(&tgt->upld_timer, bnx2fc_upld_timer, 0);
+ mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT);
+ wait_event_interruptible(tgt->upld_wait,
+ (test_bit(
+ BNX2FC_FLAG_UPLD_REQ_COMPL,
+ &tgt->flags)));
+ if (signal_pending(current))
+ flush_signals(current);
+ timer_delete_sync(&tgt->upld_timer);
+}
+
static void bnx2fc_upload_session(struct fcoe_port *port,
struct bnx2fc_rport *tgt)
{
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
BNX2FC_TGT_DBG(tgt, "upload_session: active_ios = %d\n",
tgt->num_active_ios.counter);
@@ -251,19 +306,8 @@ static void bnx2fc_upload_session(struct fcoe_port *port,
* wait for upload to complete. 3 Secs
* should be sufficient time for this process to complete.
*/
- setup_timer(&tgt->upld_timer, bnx2fc_upld_timer, (unsigned long)tgt);
- mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT);
-
BNX2FC_TGT_DBG(tgt, "waiting for disable compl\n");
- wait_event_interruptible(tgt->upld_wait,
- (test_bit(
- BNX2FC_FLAG_UPLD_REQ_COMPL,
- &tgt->flags)));
-
- if (signal_pending(current))
- flush_signals(current);
-
- del_timer_sync(&tgt->upld_timer);
+ bnx2fc_upld_wait(tgt);
/*
* traverse thru the active_q and tmf_q and cleanup
@@ -280,28 +324,21 @@ static void bnx2fc_upload_session(struct fcoe_port *port,
bnx2fc_send_session_destroy_req(hba, tgt);
/* wait for destroy to complete */
- setup_timer(&tgt->upld_timer,
- bnx2fc_upld_timer, (unsigned long)tgt);
- mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT);
-
- wait_event_interruptible(tgt->upld_wait,
- (test_bit(
- BNX2FC_FLAG_UPLD_REQ_COMPL,
- &tgt->flags)));
+ bnx2fc_upld_wait(tgt);
if (!(test_bit(BNX2FC_FLAG_DESTROYED, &tgt->flags)))
printk(KERN_ERR PFX "ERROR!! destroy timed out\n");
BNX2FC_TGT_DBG(tgt, "destroy wait complete flags = 0x%lx\n",
tgt->flags);
- if (signal_pending(current))
- flush_signals(current);
-
- del_timer_sync(&tgt->upld_timer);
- } else
+ } else if (test_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags)) {
+ printk(KERN_ERR PFX "ERROR!! DISABLE req failed, destroy"
+ " not sent to FW\n");
+ } else {
printk(KERN_ERR PFX "ERROR!! DISABLE req timed out, destroy"
" not sent to FW\n");
+ }
/* Free session resources */
bnx2fc_free_session_resc(hba, tgt);
@@ -314,7 +351,10 @@ static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
{
struct fc_rport *rport = rdata->rport;
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
+ struct b577xx_doorbell_set_prod *sq_db = &tgt->sq_db;
+ struct b577xx_fcoe_rx_doorbell *rx_db = &tgt->rx_db;
tgt->rport = rport;
tgt->rdata = rdata;
@@ -335,6 +375,7 @@ static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
tgt->max_sqes = BNX2FC_SQ_WQES_MAX;
tgt->max_rqes = BNX2FC_RQ_WQES_MAX;
tgt->max_cqes = BNX2FC_CQ_WQES_MAX;
+ atomic_set(&tgt->free_sqes, BNX2FC_SQ_WQES_MAX);
/* Initialize the toggle bit */
tgt->sq_curr_toggle_bit = 1;
@@ -344,8 +385,29 @@ static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
tgt->rq_prod_idx = 0x8000;
tgt->rq_cons_idx = 0;
atomic_set(&tgt->num_active_ios, 0);
-
- tgt->work_time_slice = 2;
+ tgt->retry_delay_timestamp = 0;
+
+ if (rdata->flags & FC_RP_FLAGS_RETRY &&
+ rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET &&
+ !(rdata->ids.roles & FC_RPORT_ROLE_FCP_INITIATOR)) {
+ tgt->dev_type = TYPE_TAPE;
+ tgt->io_timeout = 0; /* use default ULP timeout */
+ } else {
+ tgt->dev_type = TYPE_DISK;
+ tgt->io_timeout = BNX2FC_IO_TIMEOUT;
+ }
+
+ /* initialize sq doorbell */
+ sq_db->header.header = B577XX_DOORBELL_HDR_DB_TYPE;
+ sq_db->header.header |= B577XX_FCOE_CONNECTION_TYPE <<
+ B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT;
+ /* initialize rx doorbell */
+ rx_db->hdr.header = ((0x1 << B577XX_DOORBELL_HDR_RX_SHIFT) |
+ (0x1 << B577XX_DOORBELL_HDR_DB_TYPE_SHIFT) |
+ (B577XX_FCOE_CONNECTION_TYPE <<
+ B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT));
+ rx_db->params = (0x2 << B577XX_FCOE_RX_DOORBELL_NEGATIVE_ARM_SHIFT) |
+ (0x3 << B577XX_FCOE_RX_DOORBELL_OPCODE_SHIFT);
spin_lock_init(&tgt->tgt_lock);
spin_lock_init(&tgt->cq_lock);
@@ -367,7 +429,7 @@ static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
return 0;
}
-/**
+/*
* This event_callback is called after successful completion of libfc
* initiated target login. bnx2fc can proceed with initiating the session
* establishment.
@@ -377,7 +439,8 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
enum fc_rport_event event)
{
struct fcoe_port *port = lport_priv(lport);
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct fc_rport *rport = rdata->rport;
struct fc_rport_libfc_priv *rp;
struct bnx2fc_rport *tgt;
@@ -388,7 +451,7 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
switch (event) {
case RPORT_EV_READY:
if (!rport) {
- printk(KERN_ALERT PFX "rport is NULL: ERROR!\n");
+ printk(KERN_ERR PFX "rport is NULL: ERROR!\n");
break;
}
@@ -400,7 +463,7 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
* We should not come here, as lport will
* take care of fabric login
*/
- printk(KERN_ALERT PFX "%x - rport_event_handler ERROR\n",
+ printk(KERN_ERR PFX "%x - rport_event_handler ERROR\n",
rdata->ids.port_id);
break;
}
@@ -417,14 +480,14 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
}
/*
- * Offlaod process is protected with hba mutex.
+ * Offload process is protected with hba mutex.
* Use the same mutex_lock for upload process too
*/
mutex_lock(&hba->hba_mutex);
tgt = (struct bnx2fc_rport *)&rp[1];
/* This can happen when ADISC finds the same target */
- if (test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags)) {
+ if (test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags)) {
BNX2FC_TGT_DBG(tgt, "already offloaded\n");
mutex_unlock(&hba->hba_mutex);
return;
@@ -439,11 +502,8 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
BNX2FC_TGT_DBG(tgt, "OFFLOAD num_ofld_sess = %d\n",
hba->num_ofld_sess);
- if (test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags)) {
- /*
- * Session is offloaded and enabled. Map
- * doorbell register for this target
- */
+ if (test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags)) {
+ /* Session is offloaded and enabled. */
BNX2FC_TGT_DBG(tgt, "sess offloaded\n");
/* This counter is protected with hba mutex */
hba->num_ofld_sess++;
@@ -468,7 +528,7 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
break;
if (!rport) {
- printk(KERN_ALERT PFX "%x - rport not created Yet!!\n",
+ printk(KERN_INFO PFX "%x - rport not created Yet!!\n",
port_id);
break;
}
@@ -480,7 +540,7 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
*/
tgt = (struct bnx2fc_rport *)&rp[1];
- if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) {
+ if (!(test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags))) {
mutex_unlock(&hba->hba_mutex);
break;
}
@@ -498,12 +558,6 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
(hba->num_ofld_sess == 0)) {
wake_up_interruptible(&hba->shutdown_wait);
}
- if (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags)) {
- printk(KERN_ERR PFX "Relogin to the tgt\n");
- mutex_lock(&lport->disc.disc_mutex);
- lport->tt.rport_login(rdata);
- mutex_unlock(&lport->disc.disc_mutex);
- }
mutex_unlock(&hba->hba_mutex);
break;
@@ -522,7 +576,8 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport,
struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
u32 port_id)
{
- struct bnx2fc_hba *hba = port->priv;
+ struct bnx2fc_interface *interface = port->priv;
+ struct bnx2fc_hba *hba = interface->hba;
struct bnx2fc_rport *tgt;
struct fc_rport_priv *rdata;
int i;
@@ -537,7 +592,7 @@ struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
"obtained\n");
return tgt;
} else {
- printk(KERN_ERR PFX "rport 0x%x "
+ BNX2FC_TGT_DBG(tgt, "rport 0x%x "
"is in DELETED state\n",
rdata->ids.port_id);
return NULL;
@@ -596,13 +651,11 @@ static void bnx2fc_free_conn_id(struct bnx2fc_hba *hba, u32 conn_id)
/* called with hba mutex held */
spin_lock_bh(&hba->hba_lock);
hba->tgt_ofld_list[conn_id] = NULL;
- hba->next_conn_id = conn_id;
spin_unlock_bh(&hba->hba_lock);
}
-/**
- *bnx2fc_alloc_session_resc - Allocate qp resources for the session
- *
+/*
+ * bnx2fc_alloc_session_resc - Allocate qp resources for the session
*/
static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
@@ -613,56 +666,56 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
/* Allocate and map SQ */
tgt->sq_mem_size = tgt->max_sqes * BNX2FC_SQ_WQE_SIZE;
- tgt->sq_mem_size = (tgt->sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ tgt->sq_mem_size = (tgt->sq_mem_size + (CNIC_PAGE_SIZE - 1)) &
+ CNIC_PAGE_MASK;
tgt->sq = dma_alloc_coherent(&hba->pcidev->dev, tgt->sq_mem_size,
&tgt->sq_dma, GFP_KERNEL);
if (!tgt->sq) {
- printk(KERN_ALERT PFX "unable to allocate SQ memory %d\n",
+ printk(KERN_ERR PFX "unable to allocate SQ memory %d\n",
tgt->sq_mem_size);
goto mem_alloc_failure;
}
- memset(tgt->sq, 0, tgt->sq_mem_size);
/* Allocate and map CQ */
tgt->cq_mem_size = tgt->max_cqes * BNX2FC_CQ_WQE_SIZE;
- tgt->cq_mem_size = (tgt->cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ tgt->cq_mem_size = (tgt->cq_mem_size + (CNIC_PAGE_SIZE - 1)) &
+ CNIC_PAGE_MASK;
tgt->cq = dma_alloc_coherent(&hba->pcidev->dev, tgt->cq_mem_size,
&tgt->cq_dma, GFP_KERNEL);
if (!tgt->cq) {
- printk(KERN_ALERT PFX "unable to allocate CQ memory %d\n",
+ printk(KERN_ERR PFX "unable to allocate CQ memory %d\n",
tgt->cq_mem_size);
goto mem_alloc_failure;
}
- memset(tgt->cq, 0, tgt->cq_mem_size);
/* Allocate and map RQ and RQ PBL */
tgt->rq_mem_size = tgt->max_rqes * BNX2FC_RQ_WQE_SIZE;
- tgt->rq_mem_size = (tgt->rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ tgt->rq_mem_size = (tgt->rq_mem_size + (CNIC_PAGE_SIZE - 1)) &
+ CNIC_PAGE_MASK;
tgt->rq = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_mem_size,
- &tgt->rq_dma, GFP_KERNEL);
+ &tgt->rq_dma, GFP_KERNEL);
if (!tgt->rq) {
- printk(KERN_ALERT PFX "unable to allocate RQ memory %d\n",
+ printk(KERN_ERR PFX "unable to allocate RQ memory %d\n",
tgt->rq_mem_size);
goto mem_alloc_failure;
}
- memset(tgt->rq, 0, tgt->rq_mem_size);
- tgt->rq_pbl_size = (tgt->rq_mem_size / PAGE_SIZE) * sizeof(void *);
- tgt->rq_pbl_size = (tgt->rq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ tgt->rq_pbl_size = (tgt->rq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *);
+ tgt->rq_pbl_size = (tgt->rq_pbl_size + (CNIC_PAGE_SIZE - 1)) &
+ CNIC_PAGE_MASK;
tgt->rq_pbl = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_pbl_size,
&tgt->rq_pbl_dma, GFP_KERNEL);
if (!tgt->rq_pbl) {
- printk(KERN_ALERT PFX "unable to allocate RQ PBL %d\n",
+ printk(KERN_ERR PFX "unable to allocate RQ PBL %d\n",
tgt->rq_pbl_size);
goto mem_alloc_failure;
}
- memset(tgt->rq_pbl, 0, tgt->rq_pbl_size);
- num_pages = tgt->rq_mem_size / PAGE_SIZE;
+ num_pages = tgt->rq_mem_size / CNIC_PAGE_SIZE;
page = tgt->rq_dma;
pbl = (u32 *)tgt->rq_pbl;
@@ -671,53 +724,52 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
pbl++;
*pbl = (u32)((u64)page >> 32);
pbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
}
/* Allocate and map XFERQ */
tgt->xferq_mem_size = tgt->max_sqes * BNX2FC_XFERQ_WQE_SIZE;
- tgt->xferq_mem_size = (tgt->xferq_mem_size + (PAGE_SIZE - 1)) &
- PAGE_MASK;
+ tgt->xferq_mem_size = (tgt->xferq_mem_size + (CNIC_PAGE_SIZE - 1)) &
+ CNIC_PAGE_MASK;
- tgt->xferq = dma_alloc_coherent(&hba->pcidev->dev, tgt->xferq_mem_size,
- &tgt->xferq_dma, GFP_KERNEL);
+ tgt->xferq = dma_alloc_coherent(&hba->pcidev->dev,
+ tgt->xferq_mem_size, &tgt->xferq_dma,
+ GFP_KERNEL);
if (!tgt->xferq) {
- printk(KERN_ALERT PFX "unable to allocate XFERQ %d\n",
+ printk(KERN_ERR PFX "unable to allocate XFERQ %d\n",
tgt->xferq_mem_size);
goto mem_alloc_failure;
}
- memset(tgt->xferq, 0, tgt->xferq_mem_size);
/* Allocate and map CONFQ & CONFQ PBL */
tgt->confq_mem_size = tgt->max_sqes * BNX2FC_CONFQ_WQE_SIZE;
- tgt->confq_mem_size = (tgt->confq_mem_size + (PAGE_SIZE - 1)) &
- PAGE_MASK;
+ tgt->confq_mem_size = (tgt->confq_mem_size + (CNIC_PAGE_SIZE - 1)) &
+ CNIC_PAGE_MASK;
- tgt->confq = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_mem_size,
- &tgt->confq_dma, GFP_KERNEL);
+ tgt->confq = dma_alloc_coherent(&hba->pcidev->dev,
+ tgt->confq_mem_size, &tgt->confq_dma,
+ GFP_KERNEL);
if (!tgt->confq) {
- printk(KERN_ALERT PFX "unable to allocate CONFQ %d\n",
+ printk(KERN_ERR PFX "unable to allocate CONFQ %d\n",
tgt->confq_mem_size);
goto mem_alloc_failure;
}
- memset(tgt->confq, 0, tgt->confq_mem_size);
tgt->confq_pbl_size =
- (tgt->confq_mem_size / PAGE_SIZE) * sizeof(void *);
+ (tgt->confq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *);
tgt->confq_pbl_size =
- (tgt->confq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ (tgt->confq_pbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK;
tgt->confq_pbl = dma_alloc_coherent(&hba->pcidev->dev,
tgt->confq_pbl_size,
&tgt->confq_pbl_dma, GFP_KERNEL);
if (!tgt->confq_pbl) {
- printk(KERN_ALERT PFX "unable to allocate CONFQ PBL %d\n",
+ printk(KERN_ERR PFX "unable to allocate CONFQ PBL %d\n",
tgt->confq_pbl_size);
goto mem_alloc_failure;
}
- memset(tgt->confq_pbl, 0, tgt->confq_pbl_size);
- num_pages = tgt->confq_mem_size / PAGE_SIZE;
+ num_pages = tgt->confq_mem_size / CNIC_PAGE_SIZE;
page = tgt->confq_dma;
pbl = (u32 *)tgt->confq_pbl;
@@ -726,7 +778,7 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
pbl++;
*pbl = (u32)((u64)page >> 32);
pbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
}
/* Allocate and map ConnDB */
@@ -736,42 +788,36 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
tgt->conn_db_mem_size,
&tgt->conn_db_dma, GFP_KERNEL);
if (!tgt->conn_db) {
- printk(KERN_ALERT PFX "unable to allocate conn_db %d\n",
+ printk(KERN_ERR PFX "unable to allocate conn_db %d\n",
tgt->conn_db_mem_size);
goto mem_alloc_failure;
}
- memset(tgt->conn_db, 0, tgt->conn_db_mem_size);
/* Allocate and map LCQ */
tgt->lcq_mem_size = (tgt->max_sqes + 8) * BNX2FC_SQ_WQE_SIZE;
- tgt->lcq_mem_size = (tgt->lcq_mem_size + (PAGE_SIZE - 1)) &
- PAGE_MASK;
+ tgt->lcq_mem_size = (tgt->lcq_mem_size + (CNIC_PAGE_SIZE - 1)) &
+ CNIC_PAGE_MASK;
tgt->lcq = dma_alloc_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
&tgt->lcq_dma, GFP_KERNEL);
if (!tgt->lcq) {
- printk(KERN_ALERT PFX "unable to allocate lcq %d\n",
+ printk(KERN_ERR PFX "unable to allocate lcq %d\n",
tgt->lcq_mem_size);
goto mem_alloc_failure;
}
- memset(tgt->lcq, 0, tgt->lcq_mem_size);
- /* Arm CQ */
- tgt->conn_db->cq_arm.lo = -1;
tgt->conn_db->rq_prod = 0x8000;
return 0;
mem_alloc_failure:
- bnx2fc_free_session_resc(hba, tgt);
- bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
return -ENOMEM;
}
/**
- * bnx2i_free_session_resc - free qp resources for the session
+ * bnx2fc_free_session_resc - free qp resources for the session
*
* @hba: adapter structure pointer
* @tgt: bnx2fc_rport structure pointer
@@ -781,12 +827,13 @@ mem_alloc_failure:
static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
{
+ void __iomem *ctx_base_ptr;
+
BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n");
- if (tgt->ctx_base) {
- iounmap(tgt->ctx_base);
- tgt->ctx_base = NULL;
- }
+ ctx_base_ptr = tgt->ctx_base;
+ tgt->ctx_base = NULL;
+
/* Free LCQ */
if (tgt->lcq) {
dma_free_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
@@ -828,17 +875,18 @@ static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
tgt->rq = NULL;
}
/* Free CQ */
- spin_lock_bh(&tgt->cq_lock);
if (tgt->cq) {
dma_free_coherent(&hba->pcidev->dev, tgt->cq_mem_size,
tgt->cq, tgt->cq_dma);
tgt->cq = NULL;
}
- spin_unlock_bh(&tgt->cq_lock);
/* Free SQ */
if (tgt->sq) {
dma_free_coherent(&hba->pcidev->dev, tgt->sq_mem_size,
tgt->sq, tgt->sq_dma);
tgt->sq = NULL;
}
+
+ if (ctx_base_ptr)
+ iounmap(ctx_base_ptr);
}
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_constants.h b/drivers/scsi/bnx2i/57xx_iscsi_constants.h
index 30e6bdbd65af..917534109a50 100644
--- a/drivers/scsi/bnx2i/57xx_iscsi_constants.h
+++ b/drivers/scsi/bnx2i/57xx_iscsi_constants.h
@@ -1,13 +1,15 @@
-/* 57xx_iscsi_constants.h: Broadcom NetXtreme II iSCSI HSI
+/* 57xx_iscsi_constants.h: QLogic NetXtreme II iSCSI HSI
*
- * Copyright (c) 2006 - 2010 Broadcom Corporation
+ * Copyright (c) 2006 - 2013 Broadcom Corporation
+ * Copyright (c) 2014, QLogic Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
- * Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Maintained by: QLogic-Storage-Upstream@qlogic.com
*/
#ifndef __57XX_ISCSI_CONSTANTS_H_
#define __57XX_ISCSI_CONSTANTS_H_
@@ -122,10 +124,11 @@
#define ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED (0x51)
#define ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY (0x80)
+#define ISCSI_KCQE_COMPLETION_STATUS_PARITY_ERR (0x81)
/* SQ/RQ/CQ DB structure sizes */
#define ISCSI_SQ_DB_SIZE (16)
-#define ISCSI_RQ_DB_SIZE (16)
+#define ISCSI_RQ_DB_SIZE (64)
#define ISCSI_CQ_DB_SIZE (80)
#define ISCSI_SQN_TO_NOTIFY_NOT_VALID 0xFFFF
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h
index dad6c8a34317..19b3a97dbacd 100644
--- a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h
+++ b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h
@@ -1,13 +1,15 @@
-/* 57xx_iscsi_hsi.h: Broadcom NetXtreme II iSCSI HSI.
+/* 57xx_iscsi_hsi.h: QLogic NetXtreme II iSCSI HSI.
*
- * Copyright (c) 2006 - 2010 Broadcom Corporation
+ * Copyright (c) 2006 - 2013 Broadcom Corporation
+ * Copyright (c) 2014, QLogic Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
- * Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Maintained by: QLogic-Storage-Upstream@qlogic.com
*/
#ifndef __57XX_ISCSI_HSI_LINUX_LE__
#define __57XX_ISCSI_HSI_LINUX_LE__
@@ -267,7 +269,13 @@ struct bnx2i_cmd_request {
* task statistics for write response
*/
struct bnx2i_write_resp_task_stat {
- u32 num_data_ins;
+#if defined(__BIG_ENDIAN)
+ u16 num_r2ts;
+ u16 num_data_outs;
+#elif defined(__LITTLE_ENDIAN)
+ u16 num_data_outs;
+ u16 num_r2ts;
+#endif
};
/*
@@ -275,11 +283,11 @@ struct bnx2i_write_resp_task_stat {
*/
struct bnx2i_read_resp_task_stat {
#if defined(__BIG_ENDIAN)
- u16 num_data_outs;
- u16 num_r2ts;
+ u16 reserved;
+ u16 num_data_ins;
#elif defined(__LITTLE_ENDIAN)
- u16 num_r2ts;
- u16 num_data_outs;
+ u16 num_data_ins;
+ u16 reserved;
#endif
};
@@ -575,8 +583,10 @@ struct iscsi_kwqe_init1 {
#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4
#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5)
#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5
-#define ISCSI_KWQE_INIT1_RESERVED1 (0x3<<6)
-#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 6
+#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE (0x1<<6)
+#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE_SHIFT 6
+#define ISCSI_KWQE_INIT1_RESERVED1 (0x1<<7)
+#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 7
u16 cq_num_wqes;
#elif defined(__LITTLE_ENDIAN)
u16 cq_num_wqes;
@@ -587,8 +597,10 @@ struct iscsi_kwqe_init1 {
#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4
#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5)
#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5
-#define ISCSI_KWQE_INIT1_RESERVED1 (0x3<<6)
-#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 6
+#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE (0x1<<6)
+#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE_SHIFT 6
+#define ISCSI_KWQE_INIT1_RESERVED1 (0x1<<7)
+#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 7
u8 cq_log_wqes_per_page;
#endif
#if defined(__BIG_ENDIAN)
@@ -707,8 +719,10 @@ struct iscsi_kwqe_conn_update {
#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2
#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3)
#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3
-#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0xF<<4)
-#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 4
+#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE (0x3<<4)
+#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE_SHIFT 4
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0x3<<6)
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 6
#elif defined(__LITTLE_ENDIAN)
u8 conn_flags;
#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST (0x1<<0)
@@ -719,8 +733,10 @@ struct iscsi_kwqe_conn_update {
#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2
#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3)
#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3
-#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0xF<<4)
-#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 4
+#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE (0x3<<4)
+#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE_SHIFT 4
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0x3<<6)
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 6
u8 reserved2;
u8 max_outstanding_r2ts;
u8 session_error_recovery_level;
diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig
index 45a6154ce972..75ace2302fed 100644
--- a/drivers/scsi/bnx2i/Kconfig
+++ b/drivers/scsi/bnx2i/Kconfig
@@ -1,11 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SCSI_BNX2_ISCSI
- tristate "Broadcom NetXtreme II iSCSI support"
+ tristate "QLogic NetXtreme II iSCSI support"
depends on NET
depends on PCI
+ depends on (IPV6 || IPV6=n)
select SCSI_ISCSI_ATTRS
select NETDEVICES
- select NETDEV_1000
+ select ETHERNET
+ select NET_VENDOR_BROADCOM
select CNIC
- ---help---
- This driver supports iSCSI offload for the Broadcom NetXtreme II
+ help
+ This driver supports iSCSI offload for the QLogic NetXtreme II
devices.
diff --git a/drivers/scsi/bnx2i/Makefile b/drivers/scsi/bnx2i/Makefile
index b5802bd2e76a..25378671bb1e 100644
--- a/drivers/scsi/bnx2i/Makefile
+++ b/drivers/scsi/bnx2i/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
bnx2i-y := bnx2i_init.o bnx2i_hwi.o bnx2i_iscsi.o bnx2i_sysfs.o
obj-$(CONFIG_SCSI_BNX2_ISCSI) += bnx2i.o
diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h
index 6bdd25a93db9..7030efee5c46 100644
--- a/drivers/scsi/bnx2i/bnx2i.h
+++ b/drivers/scsi/bnx2i/bnx2i.h
@@ -1,15 +1,17 @@
-/* bnx2i.h: Broadcom NetXtreme II iSCSI driver.
+/* bnx2i.h: QLogic NetXtreme II iSCSI driver.
*
- * Copyright (c) 2006 - 2010 Broadcom Corporation
+ * Copyright (c) 2006 - 2013 Broadcom Corporation
* Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
* Copyright (c) 2007, 2008 Mike Christie
+ * Copyright (c) 2014, QLogic Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
- * Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Maintained by: QLogic-Storage-Upstream@qlogic.com
*/
#ifndef _BNX2I_H_
@@ -22,11 +24,14 @@
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sched/signal.h>
#include <linux/in.h>
#include <linux/kfifo.h>
#include <linux/netdevice.h>
#include <linux/completion.h>
+#include <linux/kthread.h>
+#include <linux/cpu.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -37,10 +42,12 @@
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
-#include "../../net/cnic_if.h"
+#include "../../net/ethernet/broadcom/cnic_if.h"
#include "57xx_iscsi_hsi.h"
#include "57xx_iscsi_constants.h"
+#include "../../net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h"
+
#define BNX2_ISCSI_DRIVER_NAME "bnx2i"
#define BNX2I_MAX_ADAPTERS 8
@@ -59,7 +66,7 @@
#define MAX_PAGES_PER_CTRL_STRUCT_POOL 8
#define BNX2I_RESERVED_SLOW_PATH_CMD_SLOTS 4
-#define BNX2I_5771X_DBELL_PAGE_SIZE 128
+#define BNX2X_DB_SHIFT 3
/* 5706/08 hardware has limit on maximum buffer size per BD it can handle */
#define MAX_BD_LENGTH 65535
@@ -123,6 +130,43 @@
#define REG_WR(__hba, offset, val) \
writel(val, __hba->regview + offset)
+#ifdef CONFIG_32BIT
+#define GET_STATS_64(__hba, dst, field) \
+ do { \
+ spin_lock_bh(&__hba->stat_lock); \
+ dst->field##_lo = __hba->stats.field##_lo; \
+ dst->field##_hi = __hba->stats.field##_hi; \
+ spin_unlock_bh(&__hba->stat_lock); \
+ } while (0)
+
+#define ADD_STATS_64(__hba, field, len) \
+ do { \
+ if (spin_trylock(&__hba->stat_lock)) { \
+ if (__hba->stats.field##_lo + len < \
+ __hba->stats.field##_lo) \
+ __hba->stats.field##_hi++; \
+ __hba->stats.field##_lo += len; \
+ spin_unlock(&__hba->stat_lock); \
+ } \
+ } while (0)
+
+#else
+#define GET_STATS_64(__hba, dst, field) \
+ do { \
+ u64 val, *out; \
+ \
+ val = __hba->bnx2i_stats.field; \
+ out = (u64 *)&__hba->stats.field##_lo; \
+ *out = cpu_to_le64(val); \
+ out = (u64 *)&dst->field##_lo; \
+ *out = cpu_to_le64(val); \
+ } while (0)
+
+#define ADD_STATS_64(__hba, field, len) \
+ do { \
+ __hba->bnx2i_stats.field += len; \
+ } while (0)
+#endif
/**
* struct generic_pdu_resc - login pdu resource structure
@@ -202,10 +246,13 @@ struct io_bdt {
/**
* bnx2i_cmd - iscsi command structure
*
+ * @hdr: iSCSI header
+ * @conn: iscsi_conn pointer
* @scsi_cmd: SCSI-ML task pointer corresponding to this iscsi cmd
* @sg: SG list
* @io_tbl: buffer descriptor (BD) table
* @bd_tbl_dma: buffer descriptor (BD) table's dma address
+ * @req: bnx2i specific command request struct
*/
struct bnx2i_cmd {
struct iscsi_hdr hdr;
@@ -229,6 +276,7 @@ struct bnx2i_cmd {
* @gen_pdu: login/nopout/logout pdu resources
* @violation_notified: bit mask used to track iscsi error/warning messages
* already printed out
+ * @work_cnt: keeps track of the number of outstanding work
*
* iSCSI connection structure
*/
@@ -252,6 +300,8 @@ struct bnx2i_conn {
*/
struct generic_pdu_resc gen_pdu;
u64 violation_notified;
+
+ atomic_t work_cnt;
};
@@ -279,6 +329,15 @@ struct iscsi_cid_queue {
struct bnx2i_conn **conn_cid_tbl;
};
+
+struct bnx2i_stats_info {
+ u64 rx_pdus;
+ u64 rx_bytes;
+ u64 tx_pdus;
+ u64 tx_bytes;
+};
+
+
/**
* struct bnx2i_hba - bnx2i adapter structure
*
@@ -332,6 +391,8 @@ struct iscsi_cid_queue {
* @ctx_ccell_tasks: captures number of ccells and tasks supported by
* currently offloaded connection, used to decode
* context memory
+ * @stat_lock: spin lock used by the statistic collector (32 bit)
+ * @stats: local iSCSI statistic collection place holder
*
* Adapter Data Structure
*/
@@ -341,6 +402,7 @@ struct bnx2i_hba {
struct pci_dev *pcidev;
struct net_device *netdev;
void __iomem *regview;
+ resource_size_t reg_base;
u32 age;
unsigned long cnic_dev_type;
@@ -417,6 +479,12 @@ struct bnx2i_hba {
u32 num_sess_opened;
u32 num_conn_opened;
unsigned int ctx_ccell_tasks;
+
+#ifdef CONFIG_32BIT
+ spinlock_t stat_lock;
+#endif
+ struct bnx2i_stats_info bnx2i_stats;
+ struct iscsi_stats_info stats;
};
@@ -478,7 +546,7 @@ struct bnx2i_5771x_cq_db {
struct bnx2i_5771x_sq_rq_db {
u16 prod_idx;
- u8 reserved0[14]; /* Pad structure size to 16 bytes */
+ u8 reserved0[62]; /* Pad structure size to 64 bytes */
};
@@ -514,8 +582,8 @@ struct bnx2i_5771x_dbell {
* @sq_mem_size: SQ size
* @sq_prod_qe: SQ producer entry pointer
* @sq_cons_qe: SQ consumer entry pointer
- * @sq_first_qe: virtaul address of first entry in SQ
- * @sq_last_qe: virtaul address of last entry in SQ
+ * @sq_first_qe: virtual address of first entry in SQ
+ * @sq_last_qe: virtual address of last entry in SQ
* @sq_prod_idx: SQ producer index
* @sq_cons_idx: SQ consumer index
* @sqe_left: number sq entry left
@@ -527,8 +595,8 @@ struct bnx2i_5771x_dbell {
* @cq_mem_size: CQ size
* @cq_prod_qe: CQ producer entry pointer
* @cq_cons_qe: CQ consumer entry pointer
- * @cq_first_qe: virtaul address of first entry in CQ
- * @cq_last_qe: virtaul address of last entry in CQ
+ * @cq_first_qe: virtual address of first entry in CQ
+ * @cq_last_qe: virtual address of last entry in CQ
* @cq_prod_idx: CQ producer index
* @cq_cons_idx: CQ consumer index
* @cqe_left: number cq entry left
@@ -542,8 +610,8 @@ struct bnx2i_5771x_dbell {
* @rq_mem_size: RQ size
* @rq_prod_qe: RQ producer entry pointer
* @rq_cons_qe: RQ consumer entry pointer
- * @rq_first_qe: virtaul address of first entry in RQ
- * @rq_last_qe: virtaul address of last entry in RQ
+ * @rq_first_qe: virtual address of first entry in RQ
+ * @rq_last_qe: virtual address of last entry in RQ
* @rq_prod_idx: RQ producer index
* @rq_cons_idx: RQ consumer index
* @rqe_left: number rq entry left
@@ -661,7 +729,6 @@ enum {
* @hba: adapter to which this connection belongs
* @conn: iscsi connection this EP is linked to
* @cls_ep: associated iSCSI endpoint pointer
- * @sess: iscsi session this EP is linked to
* @cm_sk: cnic sock struct
* @hba_age: age to detect if 'iscsid' issues ep_disconnect()
* after HBA reset is completed by bnx2i/cnic/bnx2
@@ -687,7 +754,7 @@ struct bnx2i_endpoint {
u32 hba_age;
u32 state;
unsigned long timestamp;
- int num_active_cmds;
+ atomic_t num_active_cmds;
u32 ec_shift;
struct qp_info qp;
@@ -700,6 +767,19 @@ struct bnx2i_endpoint {
};
+struct bnx2i_work {
+ struct list_head list;
+ struct iscsi_session *session;
+ struct bnx2i_conn *bnx2i_conn;
+ struct cqe cqe;
+};
+
+struct bnx2i_percpu_s {
+ struct task_struct *iothread;
+ struct list_head work_list;
+ spinlock_t p_work_lock;
+};
+
/* Global variables */
extern unsigned int error_mask1, error_mask2;
@@ -715,29 +795,26 @@ extern struct cnic_ulp_ops bnx2i_cnic_cb;
extern unsigned int sq_size;
extern unsigned int rq_size;
-extern struct device_attribute *bnx2i_dev_attributes[];
+extern const struct attribute_group *bnx2i_dev_groups[];
/*
* Function Prototypes
*/
-extern void bnx2i_identify_device(struct bnx2i_hba *hba);
+extern void bnx2i_identify_device(struct bnx2i_hba *hba, struct cnic_dev *dev);
extern void bnx2i_ulp_init(struct cnic_dev *dev);
extern void bnx2i_ulp_exit(struct cnic_dev *dev);
extern void bnx2i_start(void *handle);
extern void bnx2i_stop(void *handle);
+extern int bnx2i_get_stats(void *handle);
+
extern struct bnx2i_hba *get_adapter_list_head(void);
struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
u16 iscsi_cid);
-int bnx2i_alloc_ep_pool(void);
-void bnx2i_release_ep_pool(void);
-struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba);
-struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba);
-
struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic);
struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic);
@@ -776,21 +853,19 @@ extern int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba,
struct bnx2i_endpoint *ep);
extern void bnx2i_free_qp_resc(struct bnx2i_hba *hba,
struct bnx2i_endpoint *ep);
-extern void bnx2i_ep_ofld_timer(unsigned long data);
+extern void bnx2i_ep_ofld_timer(struct timer_list *t);
extern struct bnx2i_endpoint *bnx2i_find_ep_in_ofld_list(
struct bnx2i_hba *hba, u32 iscsi_cid);
extern struct bnx2i_endpoint *bnx2i_find_ep_in_destroy_list(
struct bnx2i_hba *hba, u32 iscsi_cid);
extern int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep);
-extern void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action);
+extern int bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action);
extern int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep);
-/* Debug related function prototypes */
-extern void bnx2i_print_pend_cmd_queue(struct bnx2i_conn *conn);
-extern void bnx2i_print_active_cmd_queue(struct bnx2i_conn *conn);
-extern void bnx2i_print_xmit_pdu_queue(struct bnx2i_conn *conn);
-extern void bnx2i_print_recv_state(struct bnx2i_conn *conn);
-
+extern int bnx2i_percpu_io_thread(void *arg);
+extern int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe);
#endif
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
index 5c54a2d9b834..40db5190a222 100644
--- a/drivers/scsi/bnx2i/bnx2i_hwi.c
+++ b/drivers/scsi/bnx2i/bnx2i_hwi.c
@@ -1,15 +1,17 @@
-/* bnx2i_hwi.c: Broadcom NetXtreme II iSCSI driver.
+/* bnx2i_hwi.c: QLogic NetXtreme II iSCSI driver.
*
- * Copyright (c) 2006 - 2010 Broadcom Corporation
+ * Copyright (c) 2006 - 2013 Broadcom Corporation
* Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
* Copyright (c) 2007, 2008 Mike Christie
+ * Copyright (c) 2014, QLogic Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
- * Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Maintained by: QLogic-Storage-Upstream@qlogic.com
*/
#include <linux/gfp.h>
@@ -17,6 +19,8 @@
#include <scsi/libiscsi.h>
#include "bnx2i.h"
+DECLARE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu);
+
/**
* bnx2i_get_cid_num - get cid from ep
* @ep: endpoint pointer
@@ -59,7 +63,7 @@ static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba)
* yield integral num of page buffers
*/
/* adjust SQ */
- num_elements_per_pg = PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
+ num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
if (hba->max_sqes < num_elements_per_pg)
hba->max_sqes = num_elements_per_pg;
else if (hba->max_sqes % num_elements_per_pg)
@@ -67,7 +71,7 @@ static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba)
~(num_elements_per_pg - 1);
/* adjust CQ */
- num_elements_per_pg = PAGE_SIZE / BNX2I_CQE_SIZE;
+ num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_CQE_SIZE;
if (hba->max_cqes < num_elements_per_pg)
hba->max_cqes = num_elements_per_pg;
else if (hba->max_cqes % num_elements_per_pg)
@@ -75,7 +79,7 @@ static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba)
~(num_elements_per_pg - 1);
/* adjust RQ */
- num_elements_per_pg = PAGE_SIZE / BNX2I_RQ_WQE_SIZE;
+ num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_RQ_WQE_SIZE;
if (hba->max_rqes < num_elements_per_pg)
hba->max_rqes = num_elements_per_pg;
else if (hba->max_rqes % num_elements_per_pg)
@@ -124,23 +128,23 @@ static void bnx2i_iscsi_license_error(struct bnx2i_hba *hba, u32 error_code)
/**
* bnx2i_arm_cq_event_coalescing - arms CQ to enable EQ notification
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
* @action: action, ARM or DISARM. For now only ARM_CQE is used
*
* Arm'ing CQ will enable chip to generate global EQ events inorder to interrupt
* the driver. EQ event is generated CQ index is hit or at least 1 CQ is
* outstanding and on chip timer expires
*/
-void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action)
+int bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action)
{
struct bnx2i_5771x_cq_db *cq_db;
u16 cq_index;
- u16 next_index;
+ u16 next_index = 0;
u32 num_active_cmds;
/* Coalesce CQ entries only on 10G devices */
if (!test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
- return;
+ return 0;
/* Do not update CQ DB multiple times before firmware writes
* '0xFFFF' to CQDB->SQN field. Deviation may cause spurious
@@ -150,16 +154,17 @@ void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action)
if (action != CNIC_ARM_CQE_FP)
if (cq_db->sqn[0] && cq_db->sqn[0] != 0xFFFF)
- return;
+ return 0;
if (action == CNIC_ARM_CQE || action == CNIC_ARM_CQE_FP) {
- num_active_cmds = ep->num_active_cmds;
+ num_active_cmds = atomic_read(&ep->num_active_cmds);
if (num_active_cmds <= event_coal_min)
next_index = 1;
- else
- next_index = event_coal_min +
- ((num_active_cmds - event_coal_min) >>
- ep->ec_shift);
+ else {
+ next_index = num_active_cmds >> ep->ec_shift;
+ if (next_index > num_active_cmds - event_coal_min)
+ next_index = num_active_cmds - event_coal_min;
+ }
if (!next_index)
next_index = 1;
cq_index = ep->qp.cqe_exp_seq_sn + next_index - 1;
@@ -170,12 +175,13 @@ void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action)
cq_db->sqn[0] = cq_index;
}
+ return next_index;
}
/**
* bnx2i_get_rq_buf - copy RQ buffer contents to driver buffer
- * @conn: iscsi connection on which RQ event occurred
+ * @bnx2i_conn: iscsi connection on which RQ event occurred
* @ptr: driver buffer to which RQ buffer contents is to
* be copied
* @len: length of valid data inside RQ buf
@@ -217,7 +223,7 @@ static void bnx2i_ring_577xx_doorbell(struct bnx2i_conn *conn)
/**
* bnx2i_put_rq_buf - Replenish RQ buffer, if required ring on chip doorbell
- * @conn: iscsi connection on which event to post
+ * @bnx2i_conn: iscsi connection on which event to post
* @count: number of RQ buffer being posted to chip
*
* No need to ring hardware doorbell for 57710 family of devices
@@ -247,13 +253,12 @@ void bnx2i_put_rq_buf(struct bnx2i_conn *bnx2i_conn, int count)
writew(ep->qp.rq_prod_idx,
ep->qp.ctx_base + CNIC_RECV_DOORBELL);
}
- mmiowb();
}
/**
* bnx2i_ring_sq_dbell - Ring SQ doorbell to wake-up the processing engine
- * @conn: iscsi connection to which new SQ entries belong
+ * @bnx2i_conn: iscsi connection to which new SQ entries belong
* @count: number of SQ WQEs to post
*
* SQ DB is updated in host memory and TX Doorbell is rung for 57710 family
@@ -265,7 +270,7 @@ static void bnx2i_ring_sq_dbell(struct bnx2i_conn *bnx2i_conn, int count)
struct bnx2i_5771x_sq_rq_db *sq_db;
struct bnx2i_endpoint *ep = bnx2i_conn->ep;
- ep->num_active_cmds++;
+ atomic_inc(&ep->num_active_cmds);
wmb(); /* flush SQ WQE memory before the doorbell is rung */
if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
sq_db = (struct bnx2i_5771x_sq_rq_db *) ep->qp.sq_pgtbl_virt;
@@ -273,14 +278,12 @@ static void bnx2i_ring_sq_dbell(struct bnx2i_conn *bnx2i_conn, int count)
bnx2i_ring_577xx_doorbell(bnx2i_conn);
} else
writew(count, ep->qp.ctx_base + CNIC_SEND_DOORBELL);
-
- mmiowb(); /* flush posted PCI writes */
}
/**
* bnx2i_ring_dbell_update_sq_params - update SQ driver parameters
- * @conn: iscsi connection to which new SQ entries belong
+ * @bnx2i_conn: iscsi connection to which new SQ entries belong
* @count: number of SQ WQEs to post
*
* this routine will update SQ driver parameters and ring the doorbell
@@ -317,22 +320,20 @@ static void bnx2i_ring_dbell_update_sq_params(struct bnx2i_conn *bnx2i_conn,
/**
* bnx2i_send_iscsi_login - post iSCSI login request MP WQE to hardware
- * @conn: iscsi connection
- * @cmd: driver command structure which is requesting
- * a WQE to sent to chip for further processing
+ * @bnx2i_conn: iscsi connection
+ * @task: transport layer's command structure pointer which is requesting
+ * a WQE to sent to chip for further processing
*
* prepare and post an iSCSI Login request WQE to CNIC firmware
*/
int bnx2i_send_iscsi_login(struct bnx2i_conn *bnx2i_conn,
struct iscsi_task *task)
{
- struct bnx2i_cmd *bnx2i_cmd;
struct bnx2i_login_request *login_wqe;
- struct iscsi_login *login_hdr;
+ struct iscsi_login_req *login_hdr;
u32 dword;
- bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
- login_hdr = (struct iscsi_login *)task->hdr;
+ login_hdr = (struct iscsi_login_req *)task->hdr;
login_wqe = (struct bnx2i_login_request *)
bnx2i_conn->ep->qp.sq_prod_qe;
@@ -372,7 +373,7 @@ int bnx2i_send_iscsi_login(struct bnx2i_conn *bnx2i_conn,
/**
* bnx2i_send_iscsi_tmf - post iSCSI task management request MP WQE to hardware
- * @conn: iscsi connection
+ * @bnx2i_conn: iscsi connection
* @mtask: driver command structure which is requesting
* a WQE to sent to chip for further processing
*
@@ -385,12 +386,10 @@ int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn,
struct iscsi_tm *tmfabort_hdr;
struct scsi_cmnd *ref_sc;
struct iscsi_task *ctask;
- struct bnx2i_cmd *bnx2i_cmd;
struct bnx2i_tmf_request *tmfabort_wqe;
u32 dword;
u32 scsi_lun[2];
- bnx2i_cmd = (struct bnx2i_cmd *)mtask->dd_data;
tmfabort_hdr = (struct iscsi_tm *)mtask->hdr;
tmfabort_wqe = (struct bnx2i_tmf_request *)
bnx2i_conn->ep->qp.sq_prod_qe;
@@ -430,7 +429,7 @@ int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn,
default:
tmfabort_wqe->ref_itt = RESERVED_ITT;
}
- memcpy(scsi_lun, tmfabort_hdr->lun, sizeof(struct scsi_lun));
+ memcpy(scsi_lun, &tmfabort_hdr->lun, sizeof(struct scsi_lun));
tmfabort_wqe->lun[0] = be32_to_cpu(scsi_lun[0]);
tmfabort_wqe->lun[1] = be32_to_cpu(scsi_lun[1]);
@@ -448,7 +447,7 @@ int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn,
/**
* bnx2i_send_iscsi_text - post iSCSI text WQE to hardware
- * @conn: iscsi connection
+ * @bnx2i_conn: iscsi connection
* @mtask: driver command structure which is requesting
* a WQE to sent to chip for further processing
*
@@ -457,12 +456,10 @@ int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn,
int bnx2i_send_iscsi_text(struct bnx2i_conn *bnx2i_conn,
struct iscsi_task *mtask)
{
- struct bnx2i_cmd *bnx2i_cmd;
struct bnx2i_text_request *text_wqe;
struct iscsi_text *text_hdr;
u32 dword;
- bnx2i_cmd = (struct bnx2i_cmd *)mtask->dd_data;
text_hdr = (struct iscsi_text *)mtask->hdr;
text_wqe = (struct bnx2i_text_request *) bnx2i_conn->ep->qp.sq_prod_qe;
@@ -498,7 +495,7 @@ int bnx2i_send_iscsi_text(struct bnx2i_conn *bnx2i_conn,
/**
* bnx2i_send_iscsi_scsicmd - post iSCSI scsicmd request WQE to hardware
- * @conn: iscsi connection
+ * @bnx2i_conn: iscsi connection
* @cmd: driver command structure which is requesting
* a WQE to sent to chip for further processing
*
@@ -520,9 +517,9 @@ int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *bnx2i_conn,
/**
* bnx2i_send_iscsi_nopout - post iSCSI NOPOUT request WQE to hardware
- * @conn: iscsi connection
- * @cmd: driver command structure which is requesting
- * a WQE to sent to chip for further processing
+ * @bnx2i_conn: iscsi connection
+ * @task: transport layer's command structure pointer which is
+ * requesting a WQE to sent to chip for further processing
* @datap: payload buffer pointer
* @data_len: payload data length
* @unsol: indicated whether nopout pdu is unsolicited pdu or
@@ -535,11 +532,9 @@ int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn,
char *datap, int data_len, int unsol)
{
struct bnx2i_endpoint *ep = bnx2i_conn->ep;
- struct bnx2i_cmd *bnx2i_cmd;
struct bnx2i_nop_out_request *nopout_wqe;
struct iscsi_nopout *nopout_hdr;
- bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
nopout_hdr = (struct iscsi_nopout *)task->hdr;
nopout_wqe = (struct bnx2i_nop_out_request *)ep->qp.sq_prod_qe;
@@ -547,19 +542,16 @@ int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn,
nopout_wqe->op_code = nopout_hdr->opcode;
nopout_wqe->op_attr = ISCSI_FLAG_CMD_FINAL;
- memcpy(nopout_wqe->lun, nopout_hdr->lun, 8);
+ memcpy(nopout_wqe->lun, &nopout_hdr->lun, 8);
- if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
- u32 tmp = nopout_wqe->lun[0];
- /* 57710 requires LUN field to be swapped */
- nopout_wqe->lun[0] = nopout_wqe->lun[1];
- nopout_wqe->lun[1] = tmp;
- }
+ /* 57710 requires LUN field to be swapped */
+ if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+ swap(nopout_wqe->lun[0], nopout_wqe->lun[1]);
nopout_wqe->itt = ((u16)task->itt |
(ISCSI_TASK_TYPE_MPATH <<
ISCSI_TMF_REQUEST_TYPE_SHIFT));
- nopout_wqe->ttt = nopout_hdr->ttt;
+ nopout_wqe->ttt = be32_to_cpu(nopout_hdr->ttt);
nopout_wqe->flags = 0;
if (!unsol)
nopout_wqe->flags = ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION;
@@ -587,20 +579,18 @@ int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn,
/**
* bnx2i_send_iscsi_logout - post iSCSI logout request WQE to hardware
- * @conn: iscsi connection
- * @cmd: driver command structure which is requesting
- * a WQE to sent to chip for further processing
+ * @bnx2i_conn: iscsi connection
+ * @task: transport layer's command structure pointer which is
+ * requesting a WQE to sent to chip for further processing
*
* prepare and post logout request WQE to CNIC firmware
*/
int bnx2i_send_iscsi_logout(struct bnx2i_conn *bnx2i_conn,
struct iscsi_task *task)
{
- struct bnx2i_cmd *bnx2i_cmd;
struct bnx2i_logout_request *logout_wqe;
struct iscsi_logout *logout_hdr;
- bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
logout_hdr = (struct iscsi_logout *)task->hdr;
logout_wqe = (struct bnx2i_logout_request *)
@@ -688,13 +678,14 @@ void bnx2i_update_iscsi_conn(struct iscsi_conn *conn)
/**
* bnx2i_ep_ofld_timer - post iSCSI logout request WQE to hardware
- * @data: endpoint (transport handle) structure pointer
+ * @t: timer context used to fetch the endpoint (transport
+ * handle) structure pointer
*
* routine to handle connection offload/destroy request timeout
*/
-void bnx2i_ep_ofld_timer(unsigned long data)
+void bnx2i_ep_ofld_timer(struct timer_list *t)
{
- struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) data;
+ struct bnx2i_endpoint *ep = timer_container_of(ep, t, ofld_timer);
if (ep->state == EP_STATE_OFLD_START) {
printk(KERN_ALERT "ofld_timer: CONN_OFLD timeout\n");
@@ -752,7 +743,7 @@ void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
/**
* bnx2i_send_conn_destroy - initiates iscsi connection teardown process
* @hba: adapter structure pointer
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* this routine prepares and posts CONN_OFLD_REQ1/2 KWQE to initiate
* iscsi connection context clean-up process
@@ -787,7 +778,7 @@ int bnx2i_send_conn_destroy(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
/**
* bnx2i_570x_send_conn_ofld_req - initiates iscsi conn context setup process
* @hba: adapter structure pointer
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* 5706/5708/5709 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
*/
@@ -847,7 +838,7 @@ static int bnx2i_570x_send_conn_ofld_req(struct bnx2i_hba *hba,
/**
* bnx2i_5771x_send_conn_ofld_req - initiates iscsi connection context creation
* @hba: adapter structure pointer
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* 57710 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
*/
@@ -916,7 +907,7 @@ static int bnx2i_5771x_send_conn_ofld_req(struct bnx2i_hba *hba,
* bnx2i_send_conn_ofld_req - initiates iscsi connection context setup process
*
* @hba: adapter structure pointer
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* this routine prepares and posts CONN_OFLD_REQ1/2 KWQE
*/
@@ -935,7 +926,7 @@ int bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
/**
* setup_qp_page_tables - iscsi QP page table setup function
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* Sets up page tables for SQ/RQ/CQ, 1G/sec (5706/5708/5709) devices requires
* 64-bit address in big endian format. Whereas 10G/sec (57710) requires
@@ -955,7 +946,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
/* SQ page table */
memset(ep->qp.sq_pgtbl_virt, 0, ep->qp.sq_pgtbl_size);
- num_pages = ep->qp.sq_mem_size / PAGE_SIZE;
+ num_pages = ep->qp.sq_mem_size / CNIC_PAGE_SIZE;
page = ep->qp.sq_phys;
if (cnic_dev_10g)
@@ -969,7 +960,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
ptbl++;
*ptbl = (u32) ((u64) page >> 32);
ptbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
} else {
/* PTE is written in big endian format for
* 5706/5708/5709 devices */
@@ -977,13 +968,13 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
ptbl++;
*ptbl = (u32) page;
ptbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
}
}
/* RQ page table */
memset(ep->qp.rq_pgtbl_virt, 0, ep->qp.rq_pgtbl_size);
- num_pages = ep->qp.rq_mem_size / PAGE_SIZE;
+ num_pages = ep->qp.rq_mem_size / CNIC_PAGE_SIZE;
page = ep->qp.rq_phys;
if (cnic_dev_10g)
@@ -997,7 +988,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
ptbl++;
*ptbl = (u32) ((u64) page >> 32);
ptbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
} else {
/* PTE is written in big endian format for
* 5706/5708/5709 devices */
@@ -1005,13 +996,13 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
ptbl++;
*ptbl = (u32) page;
ptbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
}
}
/* CQ page table */
memset(ep->qp.cq_pgtbl_virt, 0, ep->qp.cq_pgtbl_size);
- num_pages = ep->qp.cq_mem_size / PAGE_SIZE;
+ num_pages = ep->qp.cq_mem_size / CNIC_PAGE_SIZE;
page = ep->qp.cq_phys;
if (cnic_dev_10g)
@@ -1025,7 +1016,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
ptbl++;
*ptbl = (u32) ((u64) page >> 32);
ptbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
} else {
/* PTE is written in big endian format for
* 5706/5708/5709 devices */
@@ -1033,7 +1024,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
ptbl++;
*ptbl = (u32) page;
ptbl++;
- page += PAGE_SIZE;
+ page += CNIC_PAGE_SIZE;
}
}
}
@@ -1042,7 +1033,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
/**
* bnx2i_alloc_qp_resc - allocates required resources for QP.
* @hba: adapter structure pointer
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* Allocate QP (transport layer for iSCSI connection) resources, DMA'able
* memory for SQ/RQ/CQ and page tables. EP structure elements such
@@ -1060,11 +1051,11 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
/* Allocate page table memory for SQ which is page aligned */
ep->qp.sq_mem_size = hba->max_sqes * BNX2I_SQ_WQE_SIZE;
ep->qp.sq_mem_size =
- (ep->qp.sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ (ep->qp.sq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK;
ep->qp.sq_pgtbl_size =
- (ep->qp.sq_mem_size / PAGE_SIZE) * sizeof(void *);
+ (ep->qp.sq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *);
ep->qp.sq_pgtbl_size =
- (ep->qp.sq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ (ep->qp.sq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK;
ep->qp.sq_pgtbl_virt =
dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size,
@@ -1085,7 +1076,6 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
goto mem_alloc_err;
}
- memset(ep->qp.sq_virt, 0x00, ep->qp.sq_mem_size);
ep->qp.sq_first_qe = ep->qp.sq_virt;
ep->qp.sq_prod_qe = ep->qp.sq_first_qe;
ep->qp.sq_cons_qe = ep->qp.sq_first_qe;
@@ -1097,11 +1087,11 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
/* Allocate page table memory for CQ which is page aligned */
ep->qp.cq_mem_size = hba->max_cqes * BNX2I_CQE_SIZE;
ep->qp.cq_mem_size =
- (ep->qp.cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ (ep->qp.cq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK;
ep->qp.cq_pgtbl_size =
- (ep->qp.cq_mem_size / PAGE_SIZE) * sizeof(void *);
+ (ep->qp.cq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *);
ep->qp.cq_pgtbl_size =
- (ep->qp.cq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ (ep->qp.cq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK;
ep->qp.cq_pgtbl_virt =
dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size,
@@ -1121,7 +1111,6 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
ep->qp.cq_mem_size);
goto mem_alloc_err;
}
- memset(ep->qp.cq_virt, 0x00, ep->qp.cq_mem_size);
ep->qp.cq_first_qe = ep->qp.cq_virt;
ep->qp.cq_prod_qe = ep->qp.cq_first_qe;
@@ -1140,11 +1129,11 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
/* Allocate page table memory for RQ which is page aligned */
ep->qp.rq_mem_size = hba->max_rqes * BNX2I_RQ_WQE_SIZE;
ep->qp.rq_mem_size =
- (ep->qp.rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ (ep->qp.rq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK;
ep->qp.rq_pgtbl_size =
- (ep->qp.rq_mem_size / PAGE_SIZE) * sizeof(void *);
+ (ep->qp.rq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *);
ep->qp.rq_pgtbl_size =
- (ep->qp.rq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ (ep->qp.rq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK;
ep->qp.rq_pgtbl_virt =
dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size,
@@ -1187,7 +1176,7 @@ mem_alloc_err:
/**
* bnx2i_free_qp_resc - free memory resources held by QP
* @hba: adapter structure pointer
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* Free QP resources - SQ/RQ/CQ memory and page tables.
*/
@@ -1260,10 +1249,13 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
int rc = 0;
u64 mask64;
+ memset(&iscsi_init, 0x00, sizeof(struct iscsi_kwqe_init1));
+ memset(&iscsi_init2, 0x00, sizeof(struct iscsi_kwqe_init2));
+
bnx2i_adjust_qp_size(hba);
iscsi_init.flags =
- ISCSI_PAGE_SIZE_4K << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT;
+ (CNIC_PAGE_BITS - 8) << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT;
if (en_tcp_dack)
iscsi_init.flags |= ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE;
iscsi_init.reserved0 = 0;
@@ -1281,15 +1273,15 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
((hba->num_ccell & 0xFFFF) | (hba->max_sqes << 16));
iscsi_init.num_ccells_per_conn = hba->num_ccell;
iscsi_init.num_tasks_per_conn = hba->max_sqes;
- iscsi_init.sq_wqes_per_page = PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
+ iscsi_init.sq_wqes_per_page = CNIC_PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
iscsi_init.sq_num_wqes = hba->max_sqes;
iscsi_init.cq_log_wqes_per_page =
- (u8) bnx2i_power_of2(PAGE_SIZE / BNX2I_CQE_SIZE);
+ (u8) bnx2i_power_of2(CNIC_PAGE_SIZE / BNX2I_CQE_SIZE);
iscsi_init.cq_num_wqes = hba->max_cqes;
iscsi_init.cq_num_pages = (hba->max_cqes * BNX2I_CQE_SIZE +
- (PAGE_SIZE - 1)) / PAGE_SIZE;
+ (CNIC_PAGE_SIZE - 1)) / CNIC_PAGE_SIZE;
iscsi_init.sq_num_pages = (hba->max_sqes * BNX2I_SQ_WQE_SIZE +
- (PAGE_SIZE - 1)) / PAGE_SIZE;
+ (CNIC_PAGE_SIZE - 1)) / CNIC_PAGE_SIZE;
iscsi_init.rq_buffer_size = BNX2I_RQ_WQE_SIZE;
iscsi_init.rq_num_wqes = hba->max_rqes;
@@ -1308,14 +1300,18 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN) |
/* EMC */
(1ULL << ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN));
- if (error_mask1)
+ if (error_mask1) {
iscsi_init2.error_bit_map[0] = error_mask1;
- else
+ mask64 ^= (u32)(mask64);
+ mask64 |= error_mask1;
+ } else
iscsi_init2.error_bit_map[0] = (u32) mask64;
- if (error_mask2)
+ if (error_mask2) {
iscsi_init2.error_bit_map[1] = error_mask2;
- else
+ mask64 &= 0xffffffff;
+ mask64 |= ((u64)error_mask2 << 32);
+ } else
iscsi_init2.error_bit_map[1] = (u32) (mask64 >> 32);
iscsi_error_mask = mask64;
@@ -1331,24 +1327,26 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
/**
* bnx2i_process_scsi_cmd_resp - this function handles scsi cmd completion.
- * @conn: iscsi connection
+ * @session: iscsi session
+ * @bnx2i_conn: bnx2i connection
* @cqe: pointer to newly DMA'ed CQE entry for processing
*
* process SCSI CMD Response CQE & complete the request to SCSI-ML
*/
-static int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session,
- struct bnx2i_conn *bnx2i_conn,
- struct cqe *cqe)
+int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct cqe *cqe)
{
struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+ struct bnx2i_hba *hba = bnx2i_conn->hba;
struct bnx2i_cmd_response *resp_cqe;
struct bnx2i_cmd *bnx2i_cmd;
struct iscsi_task *task;
- struct iscsi_cmd_rsp *hdr;
+ struct iscsi_scsi_rsp *hdr;
u32 datalen = 0;
resp_cqe = (struct bnx2i_cmd_response *)cqe;
- spin_lock(&session->lock);
+ spin_lock_bh(&session->back_lock);
task = iscsi_itt_to_task(conn,
resp_cqe->itt & ISCSI_CMD_RESPONSE_INDEX);
if (!task)
@@ -1358,20 +1356,30 @@ static int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session,
if (bnx2i_cmd->req.op_attr & ISCSI_CMD_REQUEST_READ) {
conn->datain_pdus_cnt +=
- resp_cqe->task_stat.read_stat.num_data_outs;
+ resp_cqe->task_stat.read_stat.num_data_ins;
conn->rxdata_octets +=
bnx2i_cmd->req.total_data_transfer_length;
+ ADD_STATS_64(hba, rx_pdus,
+ resp_cqe->task_stat.read_stat.num_data_ins);
+ ADD_STATS_64(hba, rx_bytes,
+ bnx2i_cmd->req.total_data_transfer_length);
} else {
conn->dataout_pdus_cnt +=
- resp_cqe->task_stat.read_stat.num_data_outs;
+ resp_cqe->task_stat.write_stat.num_data_outs;
conn->r2t_pdus_cnt +=
- resp_cqe->task_stat.read_stat.num_r2ts;
+ resp_cqe->task_stat.write_stat.num_r2ts;
conn->txdata_octets +=
bnx2i_cmd->req.total_data_transfer_length;
+ ADD_STATS_64(hba, tx_pdus,
+ resp_cqe->task_stat.write_stat.num_data_outs);
+ ADD_STATS_64(hba, tx_bytes,
+ bnx2i_cmd->req.total_data_transfer_length);
+ ADD_STATS_64(hba, rx_pdus,
+ resp_cqe->task_stat.write_stat.num_r2ts);
}
bnx2i_iscsi_unmap_sg_list(bnx2i_cmd);
- hdr = (struct iscsi_cmd_rsp *)task->hdr;
+ hdr = (struct iscsi_scsi_rsp *)task->hdr;
resp_cqe = (struct bnx2i_cmd_response *)cqe;
hdr->opcode = resp_cqe->op_code;
hdr->max_cmdsn = cpu_to_be32(resp_cqe->max_cmd_sn);
@@ -1409,7 +1417,7 @@ done:
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr,
conn->data, datalen);
fail:
- spin_unlock(&session->lock);
+ spin_unlock_bh(&session->back_lock);
return 0;
}
@@ -1434,7 +1442,7 @@ static int bnx2i_process_login_resp(struct iscsi_session *session,
int pad_len;
login = (struct bnx2i_login_response *) cqe;
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
task = iscsi_itt_to_task(conn,
login->itt & ISCSI_LOGIN_RESPONSE_INDEX);
if (!task)
@@ -1477,7 +1485,7 @@ static int bnx2i_process_login_resp(struct iscsi_session *session,
bnx2i_conn->gen_pdu.resp_buf,
bnx2i_conn->gen_pdu.resp_wr_ptr - bnx2i_conn->gen_pdu.resp_buf);
done:
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
return 0;
}
@@ -1502,7 +1510,7 @@ static int bnx2i_process_text_resp(struct iscsi_session *session,
int pad_len;
text = (struct bnx2i_text_response *) cqe;
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
task = iscsi_itt_to_task(conn, text->itt & ISCSI_LOGIN_RESPONSE_INDEX);
if (!task)
goto done;
@@ -1538,7 +1546,7 @@ static int bnx2i_process_text_resp(struct iscsi_session *session,
bnx2i_conn->gen_pdu.resp_wr_ptr -
bnx2i_conn->gen_pdu.resp_buf);
done:
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
return 0;
}
@@ -1561,7 +1569,7 @@ static int bnx2i_process_tmf_resp(struct iscsi_session *session,
struct iscsi_tm_rsp *resp_hdr;
tmf_cqe = (struct bnx2i_tmf_response *)cqe;
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
task = iscsi_itt_to_task(conn,
tmf_cqe->itt & ISCSI_TMF_RESPONSE_INDEX);
if (!task)
@@ -1577,7 +1585,7 @@ static int bnx2i_process_tmf_resp(struct iscsi_session *session,
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0);
done:
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
return 0;
}
@@ -1600,7 +1608,7 @@ static int bnx2i_process_logout_resp(struct iscsi_session *session,
struct iscsi_logout_rsp *resp_hdr;
logout = (struct bnx2i_logout_response *) cqe;
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
task = iscsi_itt_to_task(conn,
logout->itt & ISCSI_LOGOUT_RESPONSE_INDEX);
if (!task)
@@ -1624,7 +1632,7 @@ static int bnx2i_process_logout_resp(struct iscsi_session *session,
bnx2i_conn->ep->state = EP_STATE_LOGOUT_RESP_RCVD;
done:
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
return 0;
}
@@ -1645,17 +1653,17 @@ static void bnx2i_process_nopin_local_cmpl(struct iscsi_session *session,
struct iscsi_task *task;
nop_in = (struct bnx2i_nop_in_msg *)cqe;
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
task = iscsi_itt_to_task(conn,
nop_in->itt & ISCSI_NOP_IN_MSG_INDEX);
if (task)
__iscsi_put_task(task);
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
}
/**
* bnx2i_unsol_pdu_adjust_rq - makes adjustments to RQ after unsol pdu is recvd
- * @conn: iscsi connection
+ * @bnx2i_conn: iscsi connection
*
* Firmware advances RQ producer index for every unsolicited PDU even if
* payload data length is '0'. This function makes corresponding
@@ -1689,7 +1697,7 @@ static int bnx2i_process_nopin_mesg(struct iscsi_session *session,
nop_in = (struct bnx2i_nop_in_msg *)cqe;
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
hdr = (struct iscsi_nopin *)&bnx2i_conn->gen_pdu.resp_hdr;
memset(hdr, 0, sizeof(struct iscsi_hdr));
hdr->opcode = nop_in->op_code;
@@ -1711,11 +1719,11 @@ static int bnx2i_process_nopin_mesg(struct iscsi_session *session,
hdr->flags = ISCSI_FLAG_CMD_FINAL;
hdr->itt = task->hdr->itt;
hdr->ttt = cpu_to_be32(nop_in->ttt);
- memcpy(hdr->lun, nop_in->lun, 8);
+ memcpy(&hdr->lun, nop_in->lun, 8);
}
done:
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
return tgt_async_nop;
}
@@ -1748,13 +1756,13 @@ static void bnx2i_process_async_mesg(struct iscsi_session *session,
return;
}
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
resp_hdr = (struct iscsi_async *) &bnx2i_conn->gen_pdu.resp_hdr;
memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
resp_hdr->opcode = async_cqe->op_code;
resp_hdr->flags = 0x80;
- memcpy(resp_hdr->lun, async_cqe->lun, 8);
+ memcpy(&resp_hdr->lun, async_cqe->lun, 8);
resp_hdr->exp_cmdsn = cpu_to_be32(async_cqe->exp_cmd_sn);
resp_hdr->max_cmdsn = cpu_to_be32(async_cqe->max_cmd_sn);
@@ -1767,7 +1775,7 @@ static void bnx2i_process_async_mesg(struct iscsi_session *session,
__iscsi_complete_pdu(bnx2i_conn->cls_conn->dd_data,
(struct iscsi_hdr *)resp_hdr, NULL, 0);
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
}
@@ -1794,7 +1802,7 @@ static void bnx2i_process_reject_mesg(struct iscsi_session *session,
} else
bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
hdr = (struct iscsi_reject *) &bnx2i_conn->gen_pdu.resp_hdr;
memset(hdr, 0, sizeof(struct iscsi_hdr));
hdr->opcode = reject->op_code;
@@ -1805,7 +1813,7 @@ static void bnx2i_process_reject_mesg(struct iscsi_session *session,
hdr->ffffffff = cpu_to_be32(RESERVED_ITT);
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, conn->data,
reject->data_length);
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
}
/**
@@ -1825,44 +1833,157 @@ static void bnx2i_process_cmd_cleanup_resp(struct iscsi_session *session,
struct iscsi_task *task;
cmd_clean_rsp = (struct bnx2i_cleanup_response *)cqe;
- spin_lock(&session->lock);
+ spin_lock(&session->back_lock);
task = iscsi_itt_to_task(conn,
cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX);
if (!task)
printk(KERN_ALERT "bnx2i: cmd clean ITT %x not active\n",
cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX);
- spin_unlock(&session->lock);
+ spin_unlock(&session->back_lock);
complete(&bnx2i_conn->cmd_cleanup_cmpl);
}
+/**
+ * bnx2i_percpu_io_thread - thread per cpu for ios
+ *
+ * @arg: ptr to bnx2i_percpu_info structure
+ */
+int bnx2i_percpu_io_thread(void *arg)
+{
+ struct bnx2i_percpu_s *p = arg;
+ struct bnx2i_work *work, *tmp;
+ LIST_HEAD(work_list);
+
+ set_user_nice(current, MIN_NICE);
+
+ while (!kthread_should_stop()) {
+ spin_lock_bh(&p->p_work_lock);
+ while (!list_empty(&p->work_list)) {
+ list_splice_init(&p->work_list, &work_list);
+ spin_unlock_bh(&p->p_work_lock);
+
+ list_for_each_entry_safe(work, tmp, &work_list, list) {
+ list_del_init(&work->list);
+ /* work allocated in the bh, freed here */
+ bnx2i_process_scsi_cmd_resp(work->session,
+ work->bnx2i_conn,
+ &work->cqe);
+ atomic_dec(&work->bnx2i_conn->work_cnt);
+ kfree(work);
+ }
+ spin_lock_bh(&p->p_work_lock);
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_bh(&p->p_work_lock);
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+
+ return 0;
+}
+
+
+/**
+ * bnx2i_queue_scsi_cmd_resp - queue cmd completion to the percpu thread
+ * @session: iscsi session
+ * @bnx2i_conn: bnx2i connection
+ * @cqe: pointer to newly DMA'ed CQE entry for processing
+ *
+ * this function is called by generic KCQ handler to queue all pending cmd
+ * completion CQEs
+ *
+ * The implementation is to queue the cmd response based on the
+ * last recorded command for the given connection. The
+ * cpu_id gets recorded upon task_xmit. No out-of-order completion!
+ */
+static int bnx2i_queue_scsi_cmd_resp(struct iscsi_session *session,
+ struct bnx2i_conn *bnx2i_conn,
+ struct bnx2i_nop_in_msg *cqe)
+{
+ struct bnx2i_work *bnx2i_work = NULL;
+ struct bnx2i_percpu_s *p = NULL;
+ struct iscsi_task *task;
+ struct scsi_cmnd *sc;
+ int rc = 0;
+
+ spin_lock(&session->back_lock);
+ task = iscsi_itt_to_task(bnx2i_conn->cls_conn->dd_data,
+ cqe->itt & ISCSI_CMD_RESPONSE_INDEX);
+ if (!task || !task->sc) {
+ spin_unlock(&session->back_lock);
+ return -EINVAL;
+ }
+ sc = task->sc;
+
+ spin_unlock(&session->back_lock);
+
+ p = &per_cpu(bnx2i_percpu, blk_mq_rq_cpu(scsi_cmd_to_rq(sc)));
+ spin_lock(&p->p_work_lock);
+ if (unlikely(!p->iothread)) {
+ rc = -EINVAL;
+ goto err;
+ }
+ /* Alloc and copy to the cqe */
+ bnx2i_work = kzalloc(sizeof(struct bnx2i_work), GFP_ATOMIC);
+ if (bnx2i_work) {
+ INIT_LIST_HEAD(&bnx2i_work->list);
+ bnx2i_work->session = session;
+ bnx2i_work->bnx2i_conn = bnx2i_conn;
+ memcpy(&bnx2i_work->cqe, cqe, sizeof(struct cqe));
+ list_add_tail(&bnx2i_work->list, &p->work_list);
+ atomic_inc(&bnx2i_conn->work_cnt);
+ wake_up_process(p->iothread);
+ spin_unlock(&p->p_work_lock);
+ goto done;
+ } else
+ rc = -ENOMEM;
+err:
+ spin_unlock(&p->p_work_lock);
+ bnx2i_process_scsi_cmd_resp(session, bnx2i_conn, (struct cqe *)cqe);
+done:
+ return rc;
+}
+
/**
* bnx2i_process_new_cqes - process newly DMA'ed CQE's
- * @bnx2i_conn: iscsi connection
+ * @bnx2i_conn: bnx2i connection
*
* this function is called by generic KCQ handler to process all pending CQE's
*/
-static void bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn)
+static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn)
{
struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
struct iscsi_session *session = conn->session;
- struct qp_info *qp = &bnx2i_conn->ep->qp;
+ struct bnx2i_hba *hba = bnx2i_conn->hba;
+ struct qp_info *qp;
struct bnx2i_nop_in_msg *nopin;
int tgt_async_msg;
+ int cqe_cnt = 0;
+
+ if (bnx2i_conn->ep == NULL)
+ return 0;
+ qp = &bnx2i_conn->ep->qp;
+
+ if (!qp->cq_virt) {
+ printk(KERN_ALERT "bnx2i (%s): cq resr freed in bh execution!",
+ hba->netdev->name);
+ goto out;
+ }
while (1) {
nopin = (struct bnx2i_nop_in_msg *) qp->cq_cons_qe;
if (nopin->cq_req_sn != qp->cqe_exp_seq_sn)
break;
- if (unlikely(test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx))) {
+ if (unlikely(test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags))) {
if (nopin->op_code == ISCSI_OP_NOOP_IN &&
nopin->itt == (u16) RESERVED_ITT) {
printk(KERN_ALERT "bnx2i: Unsolicited "
- "NOP-In detected for suspended "
- "connection dev=%s!\n",
- bnx2i_conn->hba->netdev->name);
+ "NOP-In detected for suspended "
+ "connection dev=%s!\n",
+ hba->netdev->name);
bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
goto cqe_out;
}
@@ -1873,9 +1994,10 @@ static void bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn)
switch (nopin->op_code) {
case ISCSI_OP_SCSI_CMD_RSP:
case ISCSI_OP_SCSI_DATA_IN:
- bnx2i_process_scsi_cmd_resp(session, bnx2i_conn,
- qp->cq_cons_qe);
- break;
+ /* Run the kthread engine only for data cmds
+ All other cmds will be completed in this bh! */
+ bnx2i_queue_scsi_cmd_resp(session, bnx2i_conn, nopin);
+ goto done;
case ISCSI_OP_LOGIN_RSP:
bnx2i_process_login_resp(session, bnx2i_conn,
qp->cq_cons_qe);
@@ -1918,13 +2040,25 @@ static void bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn)
printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n",
nopin->op_code);
}
- if (!tgt_async_msg)
- bnx2i_conn->ep->num_active_cmds--;
+
+ ADD_STATS_64(hba, rx_pdus, 1);
+ ADD_STATS_64(hba, rx_bytes, nopin->data_length);
+done:
+ if (!tgt_async_msg) {
+ if (!atomic_read(&bnx2i_conn->ep->num_active_cmds))
+ printk(KERN_ALERT "bnx2i (%s): no active cmd! "
+ "op 0x%x\n",
+ hba->netdev->name,
+ nopin->op_code);
+ else
+ atomic_dec(&bnx2i_conn->ep->num_active_cmds);
+ }
cqe_out:
/* clear out in production version only, till beta keep opcode
* field intact, will be helpful in debugging (context dump)
* nopin->op_code = 0;
*/
+ cqe_cnt++;
qp->cqe_exp_seq_sn++;
if (qp->cqe_exp_seq_sn == (qp->cqe_size * 2 + 1))
qp->cqe_exp_seq_sn = ISCSI_INITIAL_SN;
@@ -1937,6 +2071,8 @@ cqe_out:
qp->cq_cons_idx++;
}
}
+out:
+ return cqe_cnt;
}
/**
@@ -1952,6 +2088,7 @@ static void bnx2i_fastpath_notification(struct bnx2i_hba *hba,
{
struct bnx2i_conn *bnx2i_conn;
u32 iscsi_cid;
+ int nxt_idx;
iscsi_cid = new_cqe_kcqe->iscsi_conn_id;
bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
@@ -1964,9 +2101,12 @@ static void bnx2i_fastpath_notification(struct bnx2i_hba *hba,
printk(KERN_ALERT "cid #%x - ep not bound\n", iscsi_cid);
return;
}
+
bnx2i_process_new_cqes(bnx2i_conn);
- bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE_FP);
- bnx2i_process_new_cqes(bnx2i_conn);
+ nxt_idx = bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep,
+ CNIC_ARM_CQE_FP);
+ if (nxt_idx && nxt_idx == bnx2i_process_new_cqes(bnx2i_conn))
+ bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE_FP);
}
@@ -2066,10 +2206,8 @@ static void bnx2i_process_iscsi_error(struct bnx2i_hba *hba,
{
struct bnx2i_conn *bnx2i_conn;
u32 iscsi_cid;
- char warn_notice[] = "iscsi_warning";
- char error_notice[] = "iscsi_error";
- char additional_notice[64];
- char *message;
+ const char *additional_notice = "";
+ const char *message;
int need_recovery;
u64 err_mask64;
@@ -2084,133 +2222,132 @@ static void bnx2i_process_iscsi_error(struct bnx2i_hba *hba,
if (err_mask64 & iscsi_error_mask) {
need_recovery = 0;
- message = warn_notice;
+ message = "iscsi_warning";
} else {
need_recovery = 1;
- message = error_notice;
+ message = "iscsi_error";
}
switch (iscsi_err->completion_status) {
case ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR:
- strcpy(additional_notice, "hdr digest err");
+ additional_notice = "hdr digest err";
break;
case ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR:
- strcpy(additional_notice, "data digest err");
+ additional_notice = "data digest err";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE:
- strcpy(additional_notice, "wrong opcode rcvd");
+ additional_notice = "wrong opcode rcvd";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_AHS_LEN:
- strcpy(additional_notice, "AHS len > 0 rcvd");
+ additional_notice = "AHS len > 0 rcvd";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ITT:
- strcpy(additional_notice, "invalid ITT rcvd");
+ additional_notice = "invalid ITT rcvd";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_STATSN:
- strcpy(additional_notice, "wrong StatSN rcvd");
+ additional_notice = "wrong StatSN rcvd";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN:
- strcpy(additional_notice, "wrong DataSN rcvd");
+ additional_notice = "wrong DataSN rcvd";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T:
- strcpy(additional_notice, "pend R2T violation");
+ additional_notice = "pend R2T violation";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_0:
- strcpy(additional_notice, "ERL0, UO");
+ additional_notice = "ERL0, UO";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_1:
- strcpy(additional_notice, "ERL0, U1");
+ additional_notice = "ERL0, U1";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_2:
- strcpy(additional_notice, "ERL0, U2");
+ additional_notice = "ERL0, U2";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_3:
- strcpy(additional_notice, "ERL0, U3");
+ additional_notice = "ERL0, U3";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_4:
- strcpy(additional_notice, "ERL0, U4");
+ additional_notice = "ERL0, U4";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_5:
- strcpy(additional_notice, "ERL0, U5");
+ additional_notice = "ERL0, U5";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_6:
- strcpy(additional_notice, "ERL0, U6");
+ additional_notice = "ERL0, U6";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_RCV_LEN:
- strcpy(additional_notice, "invalid resi len");
+ additional_notice = "invalid resi len";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_RCV_PDU_LEN:
- strcpy(additional_notice, "MRDSL violation");
+ additional_notice = "MRDSL violation";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_F_BIT_ZERO:
- strcpy(additional_notice, "F-bit not set");
+ additional_notice = "F-bit not set";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV:
- strcpy(additional_notice, "invalid TTT");
+ additional_notice = "invalid TTT";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATASN:
- strcpy(additional_notice, "invalid DataSN");
+ additional_notice = "invalid DataSN";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_BURST_LEN:
- strcpy(additional_notice, "burst len violation");
+ additional_notice = "burst len violation";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_BUFFER_OFF:
- strcpy(additional_notice, "buf offset violation");
+ additional_notice = "buf offset violation";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN:
- strcpy(additional_notice, "invalid LUN field");
+ additional_notice = "invalid LUN field";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_R2TSN:
- strcpy(additional_notice, "invalid R2TSN field");
+ additional_notice = "invalid R2TSN field";
break;
#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0 \
ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0
case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0:
- strcpy(additional_notice, "invalid cmd len1");
+ additional_notice = "invalid cmd len1";
break;
#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1 \
ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1
case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1:
- strcpy(additional_notice, "invalid cmd len2");
+ additional_notice = "invalid cmd len2";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_EXCEED:
- strcpy(additional_notice,
- "pend r2t exceeds MaxOutstandingR2T value");
+ additional_notice = "pend r2t exceeds MaxOutstandingR2T value";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_IS_RSRV:
- strcpy(additional_notice, "TTT is rsvd");
+ additional_notice = "TTT is rsvd";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_BURST_LEN:
- strcpy(additional_notice, "MBL violation");
+ additional_notice = "MBL violation";
break;
#define BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO \
ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_NOT_ZERO
case BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO:
- strcpy(additional_notice, "data seg len != 0");
+ additional_notice = "data seg len != 0";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REJECT_PDU_LEN:
- strcpy(additional_notice, "reject pdu len error");
+ additional_notice = "reject pdu len error";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ASYNC_PDU_LEN:
- strcpy(additional_notice, "async pdu len error");
+ additional_notice = "async pdu len error";
break;
case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_NOPIN_PDU_LEN:
- strcpy(additional_notice, "nopin pdu len error");
+ additional_notice = "nopin pdu len error";
break;
#define BNX2_ERR_PEND_R2T_IN_CLEANUP \
ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_IN_CLEANUP
case BNX2_ERR_PEND_R2T_IN_CLEANUP:
- strcpy(additional_notice, "pend r2t in cleanup");
+ additional_notice = "pend r2t in cleanup";
break;
case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_FRAGMENT:
- strcpy(additional_notice, "IP fragments rcvd");
+ additional_notice = "IP fragments rcvd";
break;
case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_OPTIONS:
- strcpy(additional_notice, "IP options error");
+ additional_notice = "IP options error";
break;
case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_URGENT_FLAG:
- strcpy(additional_notice, "urgent flag error");
+ additional_notice = "urgent flag error";
break;
default:
printk(KERN_ALERT "iscsi_err - unknown err %x\n",
@@ -2256,12 +2393,12 @@ static void bnx2i_process_conn_destroy_cmpl(struct bnx2i_hba *hba,
ep = bnx2i_find_ep_in_destroy_list(hba, conn_destroy->iscsi_conn_id);
if (!ep) {
printk(KERN_ALERT "bnx2i_conn_destroy_cmpl: no pending "
- "offload request, unexpected complection\n");
+ "offload request, unexpected completion\n");
return;
}
if (hba != ep->hba) {
- printk(KERN_ALERT "conn destroy- error hba mis-match\n");
+ printk(KERN_ALERT "conn destroy- error hba mismatch\n");
return;
}
@@ -2287,7 +2424,6 @@ static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba,
{
u32 cid_addr;
struct bnx2i_endpoint *ep;
- u32 cid_num;
ep = bnx2i_find_ep_in_ofld_list(hba, ofld_kcqe->iscsi_conn_id);
if (!ep) {
@@ -2296,7 +2432,7 @@ static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba,
}
if (hba != ep->hba) {
- printk(KERN_ALERT "ofld_cmpl: error hba mis-match\n");
+ printk(KERN_ALERT "ofld_cmpl: error hba mismatch\n");
return;
}
@@ -2312,7 +2448,7 @@ static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba,
printk(KERN_ALERT "bnx2i (%s): ofld1 cmpl - invalid "
"opcode\n", hba->netdev->name);
else if (ofld_kcqe->completion_status ==
- ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY)
+ ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY)
/* error status code valid only for 5771x chipset */
ep->state = EP_STATE_OFLD_FAILED_CID_BUSY;
else
@@ -2322,7 +2458,6 @@ static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba,
} else {
ep->state = EP_STATE_OFLD_COMPL;
cid_addr = ofld_kcqe->iscsi_conn_context_id;
- cid_num = bnx2i_get_cid_num(ep);
ep->ep_cid = cid_addr;
ep->qp.ctx_base = NULL;
}
@@ -2331,8 +2466,9 @@ static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba,
/**
* bnx2i_indicate_kcqe - process iscsi conn update completion KCQE
- * @hba: adapter structure pointer
- * @update_kcqe: kcqe pointer
+ * @context: adapter structure pointer
+ * @kcqe: kcqe pointer
+ * @num_cqe: number of kcqes to process
*
* Generic KCQ event handler/dispatcher
*/
@@ -2386,14 +2522,20 @@ static void bnx2i_indicate_kcqe(void *context, struct kcqe *kcqe[],
* bnx2i_indicate_netevent - Generic netdev event handler
* @context: adapter structure pointer
* @event: event type
+ * @vlan_id: vlans id - associated vlan id with this event
*
* Handles four netdev events, NETDEV_UP, NETDEV_DOWN,
* NETDEV_GOING_DOWN and NETDEV_CHANGE
*/
-static void bnx2i_indicate_netevent(void *context, unsigned long event)
+static void bnx2i_indicate_netevent(void *context, unsigned long event,
+ u16 vlan_id)
{
struct bnx2i_hba *hba = context;
+ /* Ignore all netevent coming from vlans */
+ if (vlan_id != 0)
+ return;
+
switch (event) {
case NETDEV_UP:
if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
@@ -2473,8 +2615,7 @@ static void bnx2i_cm_abort_cmpl(struct cnic_sock *cm_sk)
/**
* bnx2i_cm_remote_close - process received TCP FIN
- * @hba: adapter structure pointer
- * @update_kcqe: kcqe pointer
+ * @cm_sk: cnic sock structure pointer
*
* function callback exported via bnx2i - cnic driver interface to indicate
* async TCP events such as FIN
@@ -2490,8 +2631,7 @@ static void bnx2i_cm_remote_close(struct cnic_sock *cm_sk)
/**
* bnx2i_cm_remote_abort - process TCP RST and start conn cleanup
- * @hba: adapter structure pointer
- * @update_kcqe: kcqe pointer
+ * @cm_sk: cnic sock structure pointer
*
* function callback exported via bnx2i - cnic driver interface to
* indicate async TCP events (RST) sent by the peer.
@@ -2511,7 +2651,7 @@ static void bnx2i_cm_remote_abort(struct cnic_sock *cm_sk)
static int bnx2i_send_nl_mesg(void *context, u32 msg_type,
- char *buf, u16 buflen)
+ char *buf, u16 buflen)
{
struct bnx2i_hba *hba = context;
int rc;
@@ -2528,10 +2668,9 @@ static int bnx2i_send_nl_mesg(void *context, u32 msg_type,
}
-/**
+/*
* bnx2i_cnic_cb - global template of bnx2i - cnic driver interface structure
* carrying callback function pointers
- *
*/
struct cnic_ulp_ops bnx2i_cnic_cb = {
.cnic_init = bnx2i_ulp_init,
@@ -2546,6 +2685,7 @@ struct cnic_ulp_ops bnx2i_cnic_cb = {
.cm_remote_close = bnx2i_cm_remote_close,
.cm_remote_abort = bnx2i_cm_remote_abort,
.iscsi_nl_send_msg = bnx2i_send_nl_mesg,
+ .cnic_get_stats = bnx2i_get_stats,
.owner = THIS_MODULE
};
@@ -2572,13 +2712,13 @@ int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep)
if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
reg_base = pci_resource_start(ep->hba->pcidev,
BNX2X_DOORBELL_PCI_BAR);
- reg_off = BNX2I_5771X_DBELL_PAGE_SIZE * (cid_num & 0x1FFFF) +
- DPM_TRIGER_TYPE;
- ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off, 4);
+ reg_off = (1 << BNX2X_DB_SHIFT) * (cid_num & 0x1FFFF);
+ ep->qp.ctx_base = ioremap(reg_base + reg_off, 4);
+ if (!ep->qp.ctx_base)
+ return -ENOMEM;
goto arm_cq;
}
- reg_base = ep->hba->netdev->base_addr;
if ((test_bit(BNX2I_NX2_DEV_5709, &ep->hba->cnic_dev_type)) &&
(ep->hba->mail_queue_access == BNX2I_MQ_BIN_MODE)) {
config2 = REG_RD(ep->hba, BNX2_MQ_CONFIG2);
@@ -2594,7 +2734,7 @@ int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep)
/* 5709 device in normal node and 5706/5708 devices */
reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num);
- ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off,
+ ep->qp.ctx_base = ioremap(ep->hba->reg_base + reg_off,
MB_KERNEL_CTX_SIZE);
if (!ep->qp.ctx_base)
return -ENOMEM;
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
index 6adbdc34a9a5..cecc3a026762 100644
--- a/drivers/scsi/bnx2i/bnx2i_init.c
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -1,15 +1,17 @@
-/* bnx2i.c: Broadcom NetXtreme II iSCSI driver.
+/* bnx2i.c: QLogic NetXtreme II iSCSI driver.
*
- * Copyright (c) 2006 - 2010 Broadcom Corporation
+ * Copyright (c) 2006 - 2013 Broadcom Corporation
* Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
* Copyright (c) 2007, 2008 Mike Christie
+ * Copyright (c) 2014, QLogic Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
- * Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Maintained by: QLogic-Storage-Upstream@qlogic.com
*/
#include "bnx2i.h"
@@ -18,19 +20,19 @@ static struct list_head adapter_list = LIST_HEAD_INIT(adapter_list);
static u32 adapter_count;
#define DRV_MODULE_NAME "bnx2i"
-#define DRV_MODULE_VERSION "2.6.2.3"
-#define DRV_MODULE_RELDATE "Dec 31, 2010"
+#define DRV_MODULE_VERSION "2.7.10.1"
+#define DRV_MODULE_RELDATE "Jul 16, 2014"
-static char version[] __devinitdata =
- "Broadcom NetXtreme II iSCSI Driver " DRV_MODULE_NAME \
+static char version[] =
+ "QLogic NetXtreme II iSCSI Driver " DRV_MODULE_NAME \
" v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Anil Veerabhadrappa <anilgv@broadcom.com> and "
"Eddie Wai <eddie.wai@broadcom.com>");
-MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/57710/57711/57712"
- " iSCSI Driver");
+MODULE_DESCRIPTION("QLogic NetXtreme II BCM5706/5708/5709/57710/57711/57712"
+ "/57800/57810/57840 iSCSI Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
@@ -40,7 +42,7 @@ unsigned int event_coal_min = 24;
module_param(event_coal_min, int, 0664);
MODULE_PARM_DESC(event_coal_min, "Event Coalescing Minimum Commands");
-unsigned int event_coal_div = 1;
+unsigned int event_coal_div = 2;
module_param(event_coal_div, int, 0664);
MODULE_PARM_DESC(event_coal_div, "Event Coalescing Divide Factor");
@@ -49,11 +51,11 @@ module_param(en_tcp_dack, int, 0664);
MODULE_PARM_DESC(en_tcp_dack, "Enable TCP Delayed ACK");
unsigned int error_mask1 = 0x00;
-module_param(error_mask1, int, 0664);
+module_param(error_mask1, uint, 0664);
MODULE_PARM_DESC(error_mask1, "Config FW iSCSI Error Mask #1");
unsigned int error_mask2 = 0x00;
-module_param(error_mask2, int, 0664);
+module_param(error_mask2, uint, 0664);
MODULE_PARM_DESC(error_mask2, "Config FW iSCSI Error Mask #2");
unsigned int sq_size;
@@ -66,37 +68,38 @@ MODULE_PARM_DESC(rq_size, "Configure RQ size");
u64 iscsi_error_mask = 0x00;
+DEFINE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu);
/**
* bnx2i_identify_device - identifies NetXtreme II device type
* @hba: Adapter structure pointer
+ * @dev: Corresponding cnic device
*
* This function identifies the NX2 device type and sets appropriate
* queue mailbox register access method, 5709 requires driver to
* access MBOX regs using *bin* mode
*/
-void bnx2i_identify_device(struct bnx2i_hba *hba)
+void bnx2i_identify_device(struct bnx2i_hba *hba, struct cnic_dev *dev)
{
hba->cnic_dev_type = 0;
- if ((hba->pci_did == PCI_DEVICE_ID_NX2_5706) ||
- (hba->pci_did == PCI_DEVICE_ID_NX2_5706S))
- set_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type);
- else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5708) ||
- (hba->pci_did == PCI_DEVICE_ID_NX2_5708S))
- set_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type);
- else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5709) ||
- (hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) {
- set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type);
- hba->mail_queue_access = BNX2I_MQ_BIN_MODE;
- } else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57711 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57711E ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57712 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57712E)
+ if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) {
+ if (hba->pci_did == PCI_DEVICE_ID_NX2_5706 ||
+ hba->pci_did == PCI_DEVICE_ID_NX2_5706S) {
+ set_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type);
+ } else if (hba->pci_did == PCI_DEVICE_ID_NX2_5708 ||
+ hba->pci_did == PCI_DEVICE_ID_NX2_5708S) {
+ set_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type);
+ } else if (hba->pci_did == PCI_DEVICE_ID_NX2_5709 ||
+ hba->pci_did == PCI_DEVICE_ID_NX2_5709S) {
+ set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type);
+ hba->mail_queue_access = BNX2I_MQ_BIN_MODE;
+ }
+ } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
set_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type);
- else
+ } else {
printk(KERN_ALERT "bnx2i: unknown device, 0x%x\n",
hba->pci_did);
+ }
}
@@ -163,23 +166,14 @@ void bnx2i_start(void *handle)
struct bnx2i_hba *hba = handle;
int i = HZ;
- if (!hba->cnic->max_iscsi_conn) {
- printk(KERN_ALERT "bnx2i: dev %s does not support "
- "iSCSI\n", hba->netdev->name);
+ /* On some bnx2x devices, it is possible that iSCSI is no
+ * longer supported after firmware is downloaded. In that
+ * case, the iscsi_init_msg will return failure.
+ */
- if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
- mutex_lock(&bnx2i_dev_lock);
- list_del_init(&hba->link);
- adapter_count--;
- hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
- clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
- mutex_unlock(&bnx2i_dev_lock);
- bnx2i_free_hba(hba);
- }
- return;
- }
bnx2i_send_fw_iscsi_init_msg(hba);
- while (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) && i--)
+ while (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) &&
+ !test_bit(ADAPTER_STATE_INIT_FAILED, &hba->adapter_state) && i--)
msleep(BNX2I_INIT_POLL_TIME);
}
@@ -281,6 +275,13 @@ static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic)
int rc;
mutex_lock(&bnx2i_dev_lock);
+ if (!cnic->max_iscsi_conn) {
+ printk(KERN_ALERT "bnx2i: dev %s does not support "
+ "iSCSI\n", hba->netdev->name);
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
hba->cnic = cnic;
rc = cnic->register_device(cnic, CNIC_ULP_ISCSI, hba);
if (!rc) {
@@ -298,6 +299,7 @@ static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic)
else
printk(KERN_ERR "bnx2i dev reg, unknown error, %d\n", rc);
+out:
mutex_unlock(&bnx2i_dev_lock);
return rc;
@@ -362,6 +364,96 @@ void bnx2i_ulp_exit(struct cnic_dev *dev)
/**
+ * bnx2i_get_stats - Retrieve various statistic from iSCSI offload
+ * @handle: bnx2i_hba
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ * retrieve various iSCSI offload related statistics.
+ */
+int bnx2i_get_stats(void *handle)
+{
+ struct bnx2i_hba *hba = handle;
+ struct iscsi_stats_info *stats;
+
+ if (!hba)
+ return -EINVAL;
+
+ stats = (struct iscsi_stats_info *)hba->cnic->stats_addr;
+
+ if (!stats)
+ return -ENOMEM;
+
+ strscpy(stats->version, DRV_MODULE_VERSION, sizeof(stats->version));
+ memcpy(stats->mac_add1 + 2, hba->cnic->mac_addr, ETH_ALEN);
+
+ stats->max_frame_size = hba->netdev->mtu;
+ stats->txq_size = hba->max_sqes;
+ stats->rxq_size = hba->max_cqes;
+
+ stats->txq_avg_depth = 0;
+ stats->rxq_avg_depth = 0;
+
+ GET_STATS_64(hba, stats, rx_pdus);
+ GET_STATS_64(hba, stats, rx_bytes);
+
+ GET_STATS_64(hba, stats, tx_pdus);
+ GET_STATS_64(hba, stats, tx_bytes);
+
+ return 0;
+}
+
+
+/**
+ * bnx2i_cpu_online - Create a receive thread for an online CPU
+ *
+ * @cpu: cpu index for the online cpu
+ */
+static int bnx2i_cpu_online(unsigned int cpu)
+{
+ struct bnx2i_percpu_s *p;
+ struct task_struct *thread;
+
+ p = &per_cpu(bnx2i_percpu, cpu);
+
+ thread = kthread_create_on_cpu(bnx2i_percpu_io_thread, (void *)p,
+ cpu, "bnx2i_thread/%d");
+ if (IS_ERR(thread))
+ return PTR_ERR(thread);
+
+ p->iothread = thread;
+ wake_up_process(thread);
+ return 0;
+}
+
+static int bnx2i_cpu_offline(unsigned int cpu)
+{
+ struct bnx2i_percpu_s *p;
+ struct task_struct *thread;
+ struct bnx2i_work *work, *tmp;
+
+ /* Prevent any new work from being queued for this CPU */
+ p = &per_cpu(bnx2i_percpu, cpu);
+ spin_lock_bh(&p->p_work_lock);
+ thread = p->iothread;
+ p->iothread = NULL;
+
+ /* Free all work in the list */
+ list_for_each_entry_safe(work, tmp, &p->work_list, list) {
+ list_del_init(&work->list);
+ bnx2i_process_scsi_cmd_resp(work->session,
+ work->bnx2i_conn, &work->cqe);
+ kfree(work);
+ }
+
+ spin_unlock_bh(&p->p_work_lock);
+ if (thread)
+ kthread_stop(thread);
+ return 0;
+}
+
+static enum cpuhp_state bnx2i_online_state;
+
+/**
* bnx2i_mod_init - module init entry point
*
* initialize any driver wide global data structures such as endpoint pool,
@@ -371,14 +463,14 @@ void bnx2i_ulp_exit(struct cnic_dev *dev)
static int __init bnx2i_mod_init(void)
{
int err;
+ unsigned cpu = 0;
+ struct bnx2i_percpu_s *p;
printk(KERN_INFO "%s", version);
if (sq_size && !is_power_of_2(sq_size))
sq_size = roundup_pow_of_two(sq_size);
- mutex_init(&bnx2i_dev_lock);
-
bnx2i_scsi_xport_template =
iscsi_register_transport(&bnx2i_iscsi_transport);
if (!bnx2i_scsi_xport_template) {
@@ -393,8 +485,23 @@ static int __init bnx2i_mod_init(void)
goto unreg_xport;
}
+ /* Create percpu kernel threads to handle iSCSI I/O completions */
+ for_each_possible_cpu(cpu) {
+ p = &per_cpu(bnx2i_percpu, cpu);
+ INIT_LIST_HEAD(&p->work_list);
+ spin_lock_init(&p->p_work_lock);
+ p->iothread = NULL;
+ }
+
+ err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "scsi/bnx2i:online",
+ bnx2i_cpu_online, bnx2i_cpu_offline);
+ if (err < 0)
+ goto unreg_driver;
+ bnx2i_online_state = err;
return 0;
+unreg_driver:
+ cnic_unregister_driver(CNIC_ULP_ISCSI);
unreg_xport:
iscsi_unregister_transport(&bnx2i_iscsi_transport);
out:
@@ -430,6 +537,8 @@ static void __exit bnx2i_mod_exit(void)
}
mutex_unlock(&bnx2i_dev_lock);
+ cpuhp_remove_state(bnx2i_online_state);
+
iscsi_unregister_transport(&bnx2i_iscsi_transport);
cnic_unregister_driver(CNIC_ULP_ISCSI);
}
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index 041928b23cb0..6c80e5b514fd 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -1,16 +1,18 @@
/*
- * bnx2i_iscsi.c: Broadcom NetXtreme II iSCSI driver.
+ * bnx2i_iscsi.c: QLogic NetXtreme II iSCSI driver.
*
- * Copyright (c) 2006 - 2010 Broadcom Corporation
+ * Copyright (c) 2006 - 2013 Broadcom Corporation
* Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved.
* Copyright (c) 2007, 2008 Mike Christie
+ * Copyright (c) 2014, QLogic Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
- * Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Maintained by: QLogic-Storage-Upstream@qlogic.com
*/
#include <linux/slab.h>
@@ -20,13 +22,14 @@
struct scsi_transport_template *bnx2i_scsi_xport_template;
struct iscsi_transport bnx2i_iscsi_transport;
-static struct scsi_host_template bnx2i_host_template;
+static const struct scsi_host_template bnx2i_host_template;
/*
* Global endpoint resource info
*/
static DEFINE_SPINLOCK(bnx2i_resc_lock); /* protects global resources */
+DECLARE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu);
static int bnx2i_adapter_ready(struct bnx2i_hba *hba)
{
@@ -225,7 +228,7 @@ static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd)
/**
* bnx2i_bind_conn_to_iscsi_cid - bind conn structure to 'iscsi_cid'
* @hba: pointer to adapter instance
- * @conn: pointer to iscsi connection
+ * @bnx2i_conn: pointer to iscsi connection
* @iscsi_cid: iscsi context ID, range 0 - (MAX_CONN - 1)
*
* update iscsi cid table entry with connection pointer. This enables
@@ -460,7 +463,6 @@ static int bnx2i_alloc_bdt(struct bnx2i_hba *hba, struct iscsi_session *session,
* bnx2i_destroy_cmd_pool - destroys iscsi command pool and release BD table
* @hba: adapter instance pointer
* @session: iscsi session pointer
- * @cmd: iscsi command structure
*/
static void bnx2i_destroy_cmd_pool(struct bnx2i_hba *hba,
struct iscsi_session *session)
@@ -524,7 +526,7 @@ static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
struct iscsi_bd *mp_bdt;
u64 addr;
- hba->mp_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ hba->mp_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
&hba->mp_bd_dma, GFP_KERNEL);
if (!hba->mp_bd_tbl) {
printk(KERN_ERR "unable to allocate Middle Path BDT\n");
@@ -532,11 +534,12 @@ static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
goto out;
}
- hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev,
+ CNIC_PAGE_SIZE,
&hba->dummy_buf_dma, GFP_KERNEL);
if (!hba->dummy_buffer) {
printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n");
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
hba->mp_bd_tbl, hba->mp_bd_dma);
hba->mp_bd_tbl = NULL;
rc = -1;
@@ -547,7 +550,7 @@ static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
addr = (unsigned long) hba->dummy_buf_dma;
mp_bdt->buffer_addr_lo = addr & 0xffffffff;
mp_bdt->buffer_addr_hi = addr >> 32;
- mp_bdt->buffer_length = PAGE_SIZE;
+ mp_bdt->buffer_length = CNIC_PAGE_SIZE;
mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
ISCSI_BD_FIRST_IN_BD_CHAIN;
out:
@@ -564,22 +567,21 @@ out:
static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
{
if (hba->mp_bd_tbl) {
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
hba->mp_bd_tbl, hba->mp_bd_dma);
hba->mp_bd_tbl = NULL;
}
if (hba->dummy_buffer) {
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
hba->dummy_buffer, hba->dummy_buf_dma);
hba->dummy_buffer = NULL;
}
- return;
+ return;
}
/**
* bnx2i_drop_session - notifies iscsid of connection error.
- * @hba: adapter instance pointer
- * @session: iscsi session pointer
+ * @cls_session: iscsi cls session pointer
*
* This notifies iscsid that there is a error, so it can initiate
* recovery.
@@ -595,7 +597,7 @@ void bnx2i_drop_session(struct iscsi_cls_session *cls_session)
/**
* bnx2i_ep_destroy_list_add - add an entry to EP destroy list
* @hba: pointer to adapter instance
- * @ep: pointer to endpoint (transport indentifier) structure
+ * @ep: pointer to endpoint (transport identifier) structure
*
* EP destroy queue manager
*/
@@ -612,7 +614,7 @@ static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
* bnx2i_ep_destroy_list_del - add an entry to EP destroy list
*
* @hba: pointer to adapter instance
- * @ep: pointer to endpoint (transport indentifier) structure
+ * @ep: pointer to endpoint (transport identifier) structure
*
* EP destroy queue manager
*/
@@ -629,7 +631,7 @@ static int bnx2i_ep_destroy_list_del(struct bnx2i_hba *hba,
/**
* bnx2i_ep_ofld_list_add - add an entry to ep offload pending list
* @hba: pointer to adapter instance
- * @ep: pointer to endpoint (transport indentifier) structure
+ * @ep: pointer to endpoint (transport identifier) structure
*
* pending conn offload completion queue manager
*/
@@ -645,7 +647,7 @@ static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
/**
* bnx2i_ep_ofld_list_del - add an entry to ep offload pending list
* @hba: pointer to adapter instance
- * @ep: pointer to endpoint (transport indentifier) structure
+ * @ep: pointer to endpoint (transport identifier) structure
*
* pending conn offload completion queue manager
*/
@@ -671,7 +673,7 @@ bnx2i_find_ep_in_ofld_list(struct bnx2i_hba *hba, u32 iscsi_cid)
{
struct list_head *list;
struct list_head *tmp;
- struct bnx2i_endpoint *ep;
+ struct bnx2i_endpoint *ep = NULL;
read_lock_bh(&hba->ep_rdwr_lock);
list_for_each_safe(list, tmp, &hba->ep_ofld_list) {
@@ -699,7 +701,7 @@ bnx2i_find_ep_in_destroy_list(struct bnx2i_hba *hba, u32 iscsi_cid)
{
struct list_head *list;
struct list_head *tmp;
- struct bnx2i_endpoint *ep;
+ struct bnx2i_endpoint *ep = NULL;
read_lock_bh(&hba->ep_rdwr_lock);
list_for_each_safe(list, tmp, &hba->ep_destroy_list) {
@@ -720,7 +722,7 @@ bnx2i_find_ep_in_destroy_list(struct bnx2i_hba *hba, u32 iscsi_cid)
/**
* bnx2i_ep_active_list_add - add an entry to ep active list
* @hba: pointer to adapter instance
- * @ep: pointer to endpoint (transport indentifier) structure
+ * @ep: pointer to endpoint (transport identifier) structure
*
* current active conn queue manager
*/
@@ -736,7 +738,7 @@ static void bnx2i_ep_active_list_add(struct bnx2i_hba *hba,
/**
* bnx2i_ep_active_list_del - deletes an entry to ep active list
* @hba: pointer to adapter instance
- * @ep: pointer to endpoint (transport indentifier) structure
+ * @ep: pointer to endpoint (transport identifier) structure
*
* current active conn queue manager
*/
@@ -789,7 +791,7 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
return NULL;
shost->dma_boundary = cnic->pcidev->dma_mask;
shost->transportt = bnx2i_scsi_xport_template;
- shost->max_id = ISCSI_MAX_CONNS_PER_HBA;
+ shost->max_id = ISCSI_MAX_CONNS_PER_HBA - 1;
shost->max_channel = 0;
shost->max_lun = 512;
shost->max_cmd_len = 16;
@@ -807,16 +809,16 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
hba->pci_func = PCI_FUNC(hba->pcidev->devfn);
hba->pci_devno = PCI_SLOT(hba->pcidev->devfn);
- bnx2i_identify_device(hba);
+ bnx2i_identify_device(hba, cnic);
bnx2i_setup_host_queue_size(hba, shost);
+ hba->reg_base = pci_resource_start(hba->pcidev, 0);
if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) {
- hba->regview = ioremap_nocache(hba->netdev->base_addr,
- BNX2_MQ_CONFIG2);
+ hba->regview = pci_iomap(hba->pcidev, 0, BNX2_MQ_CONFIG2);
if (!hba->regview)
goto ioreg_map_err;
} else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) {
- hba->regview = ioremap_nocache(hba->netdev->base_addr, 4096);
+ hba->regview = pci_iomap(hba->pcidev, 0, 4096);
if (!hba->regview)
goto ioreg_map_err;
}
@@ -873,6 +875,11 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
hba->conn_ctx_destroy_tmo = 2 * HZ;
}
+#ifdef CONFIG_32BIT
+ spin_lock_init(&hba->stat_lock);
+#endif
+ memset(&hba->stats, 0, sizeof(struct iscsi_stats_info));
+
if (iscsi_host_add(shost, &hba->pcidev->dev))
goto free_dump_mem;
return hba;
@@ -883,7 +890,7 @@ cid_que_err:
bnx2i_free_mp_bdt(hba);
mp_bdt_mem_err:
if (hba->regview) {
- iounmap(hba->regview);
+ pci_iounmap(hba->pcidev, hba->regview);
hba->regview = NULL;
}
ioreg_map_err:
@@ -902,16 +909,16 @@ void bnx2i_free_hba(struct bnx2i_hba *hba)
{
struct Scsi_Host *shost = hba->shost;
- iscsi_host_remove(shost);
+ iscsi_host_remove(shost, false);
INIT_LIST_HEAD(&hba->ep_ofld_list);
INIT_LIST_HEAD(&hba->ep_active_list);
INIT_LIST_HEAD(&hba->ep_destroy_list);
- pci_dev_put(hba->pcidev);
if (hba->regview) {
- iounmap(hba->regview);
+ pci_iounmap(hba->pcidev, hba->regview);
hba->regview = NULL;
}
+ pci_dev_put(hba->pcidev);
bnx2i_free_mp_bdt(hba);
bnx2i_release_free_cid_que(hba);
iscsi_host_free(shost);
@@ -928,14 +935,14 @@ static void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba,
struct bnx2i_conn *bnx2i_conn)
{
if (bnx2i_conn->gen_pdu.resp_bd_tbl) {
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
bnx2i_conn->gen_pdu.resp_bd_tbl,
bnx2i_conn->gen_pdu.resp_bd_dma);
bnx2i_conn->gen_pdu.resp_bd_tbl = NULL;
}
if (bnx2i_conn->gen_pdu.req_bd_tbl) {
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
bnx2i_conn->gen_pdu.req_bd_tbl,
bnx2i_conn->gen_pdu.req_bd_dma);
bnx2i_conn->gen_pdu.req_bd_tbl = NULL;
@@ -992,13 +999,13 @@ static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf;
bnx2i_conn->gen_pdu.req_bd_tbl =
- dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
&bnx2i_conn->gen_pdu.req_bd_dma, GFP_KERNEL);
if (bnx2i_conn->gen_pdu.req_bd_tbl == NULL)
goto login_req_bd_tbl_failure;
bnx2i_conn->gen_pdu.resp_bd_tbl =
- dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
&bnx2i_conn->gen_pdu.resp_bd_dma,
GFP_KERNEL);
if (bnx2i_conn->gen_pdu.resp_bd_tbl == NULL)
@@ -1007,7 +1014,7 @@ static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
return 0;
login_resp_bd_tbl_failure:
- dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+ dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE,
bnx2i_conn->gen_pdu.req_bd_tbl,
bnx2i_conn->gen_pdu.req_bd_dma);
bnx2i_conn->gen_pdu.req_bd_tbl = NULL;
@@ -1163,10 +1170,10 @@ static void bnx2i_cleanup_task(struct iscsi_task *task)
if (task->state == ISCSI_TASK_ABRT_TMF) {
bnx2i_send_cmd_cleanup_req(hba, task->dd_data);
- spin_unlock_bh(&conn->session->lock);
+ spin_unlock_bh(&conn->session->back_lock);
wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl,
msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT));
- spin_lock_bh(&conn->session->lock);
+ spin_lock_bh(&conn->session->back_lock);
}
bnx2i_iscsi_unmap_sg_list(task->dd_data);
}
@@ -1180,12 +1187,18 @@ static int
bnx2i_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+ struct bnx2i_hba *hba = bnx2i_conn->hba;
struct bnx2i_cmd *cmd = task->dd_data;
memset(bnx2i_conn->gen_pdu.req_buf, 0, ISCSI_DEF_MAX_RECV_SEG_LEN);
bnx2i_setup_cmd_wqe_template(cmd);
bnx2i_conn->gen_pdu.req_buf_size = task->data_count;
+
+ /* Tx PDU/data length count */
+ ADD_STATS_64(hba, tx_pdus, 1);
+ ADD_STATS_64(hba, tx_bytes, task->data_count);
+
if (task->data_count) {
memcpy(bnx2i_conn->gen_pdu.req_buf, task->data,
task->data_count);
@@ -1212,9 +1225,10 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
struct bnx2i_conn *bnx2i_conn = conn->dd_data;
struct scsi_cmnd *sc = task->sc;
struct bnx2i_cmd *cmd = task->dd_data;
- struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
+ struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
- if (bnx2i_conn->ep->num_active_cmds + 1 > hba->max_sqes)
+ if (atomic_read(&bnx2i_conn->ep->num_active_cmds) + 1 >
+ hba->max_sqes)
return -ENOMEM;
/*
@@ -1259,7 +1273,8 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
/**
* bnx2i_session_create - create a new iscsi session
- * @cmds_max: max commands supported
+ * @ep: pointer to iscsi endpoint
+ * @cmds_max: user specified maximum commands
* @qdepth: scsi queue depth to support
* @initial_cmdsn: initial iscsi CMDSN to be used for this session
*
@@ -1354,6 +1369,9 @@ bnx2i_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid)
bnx2i_conn = conn->dd_data;
bnx2i_conn->cls_conn = cls_conn;
bnx2i_conn->hba = hba;
+
+ atomic_set(&bnx2i_conn->work_cnt, 0);
+
/* 'ep' ptr will be assigned in bind() call */
bnx2i_conn->ep = NULL;
init_completion(&bnx2i_conn->cmd_cleanup_cmpl);
@@ -1402,17 +1420,23 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
* Forcefully terminate all in progress connection recovery at the
* earliest, either in bind(), send_pdu(LOGIN), or conn_start()
*/
- if (bnx2i_adapter_ready(hba))
- return -EIO;
+ if (bnx2i_adapter_ready(hba)) {
+ ret_code = -EIO;
+ goto put_ep;
+ }
bnx2i_ep = ep->dd_data;
if ((bnx2i_ep->state == EP_STATE_TCP_FIN_RCVD) ||
- (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD))
+ (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD)) {
/* Peer disconnect via' FIN or RST */
- return -EINVAL;
+ ret_code = -EINVAL;
+ goto put_ep;
+ }
- if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
- return -EINVAL;
+ if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) {
+ ret_code = -EINVAL;
+ goto put_ep;
+ }
if (bnx2i_ep->hba != hba) {
/* Error - TCP connection does not belong to this device
@@ -1423,7 +1447,8 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data,
"belong to hba (%s)\n",
hba->netdev->name);
- return -EEXIST;
+ ret_code = -EEXIST;
+ goto put_ep;
}
bnx2i_ep->conn = bnx2i_conn;
bnx2i_conn->ep = bnx2i_ep;
@@ -1440,6 +1465,8 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
bnx2i_put_rq_buf(bnx2i_conn, 0);
bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE);
+put_ep:
+ iscsi_put_endpoint(ep);
return ret_code;
}
@@ -1457,11 +1484,34 @@ static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
struct bnx2i_conn *bnx2i_conn = conn->dd_data;
struct Scsi_Host *shost;
struct bnx2i_hba *hba;
+ struct bnx2i_work *work, *tmp;
+ unsigned cpu = 0;
+ struct bnx2i_percpu_s *p;
shost = iscsi_session_to_shost(iscsi_conn_to_session(cls_conn));
hba = iscsi_host_priv(shost);
bnx2i_conn_free_login_resources(hba, bnx2i_conn);
+
+ if (atomic_read(&bnx2i_conn->work_cnt)) {
+ for_each_online_cpu(cpu) {
+ p = &per_cpu(bnx2i_percpu, cpu);
+ spin_lock_bh(&p->p_work_lock);
+ list_for_each_entry_safe(work, tmp,
+ &p->work_list, list) {
+ if (work->session == conn->session &&
+ work->bnx2i_conn == bnx2i_conn) {
+ list_del_init(&work->list);
+ kfree(work);
+ if (!atomic_dec_and_test(
+ &bnx2i_conn->work_cnt))
+ break;
+ }
+ }
+ spin_unlock_bh(&p->p_work_lock);
+ }
+ }
+
iscsi_conn_teardown(cls_conn);
}
@@ -1567,9 +1617,8 @@ static int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
* this should normally not sleep for a long time so it should
* not disrupt the caller.
*/
+ timer_setup(&bnx2i_conn->ep->ofld_timer, bnx2i_ep_ofld_timer, 0);
bnx2i_conn->ep->ofld_timer.expires = 1 * HZ + jiffies;
- bnx2i_conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer;
- bnx2i_conn->ep->ofld_timer.data = (unsigned long) bnx2i_conn->ep;
add_timer(&bnx2i_conn->ep->ofld_timer);
/* update iSCSI context for this conn, wait for CNIC to complete */
wait_event_interruptible(bnx2i_conn->ep->ofld_wait,
@@ -1577,7 +1626,7 @@ static int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
if (signal_pending(current))
flush_signals(current);
- del_timer_sync(&bnx2i_conn->ep->ofld_timer);
+ timer_delete_sync(&bnx2i_conn->ep->ofld_timer);
iscsi_conn_start(cls_conn);
return 0;
@@ -1603,12 +1652,11 @@ static void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
stats->r2t_pdus = conn->r2t_pdus_cnt;
stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
- stats->custom_length = 3;
- strcpy(stats->custom[2].desc, "eh_abort_cnt");
- stats->custom[2].value = conn->eh_abort_cnt;
stats->digest_err = 0;
stats->timeout_err = 0;
- stats->custom_length = 0;
+ strcpy(stats->custom[0].desc, "eh_abort_cnt");
+ stats->custom[0].value = conn->eh_abort_cnt;
+ stats->custom_length = 1;
}
@@ -1656,7 +1704,7 @@ no_nx2_route:
/**
* bnx2i_tear_down_conn - tear down iscsi/tcp connection and free resources
* @hba: pointer to adapter instance
- * @ep: endpoint (transport indentifier) structure
+ * @ep: endpoint (transport identifier) structure
*
* destroys cm_sock structure and on chip iscsi context
*/
@@ -1673,7 +1721,7 @@ static int bnx2i_tear_down_conn(struct bnx2i_hba *hba,
struct iscsi_conn *conn = ep->conn->cls_conn->dd_data;
/* Must suspend all rx queue activity for this ep */
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+ set_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags);
}
/* CONN_DISCONNECT timeout may or may not be an issue depending
* on what transcribed in TCP layer, different targets behave
@@ -1686,10 +1734,8 @@ static int bnx2i_tear_down_conn(struct bnx2i_hba *hba,
}
ep->state = EP_STATE_CLEANUP_START;
- init_timer(&ep->ofld_timer);
+ timer_setup(&ep->ofld_timer, bnx2i_ep_ofld_timer, 0);
ep->ofld_timer.expires = hba->conn_ctx_destroy_tmo + jiffies;
- ep->ofld_timer.function = bnx2i_ep_ofld_timer;
- ep->ofld_timer.data = (unsigned long) ep;
add_timer(&ep->ofld_timer);
bnx2i_ep_destroy_list_add(hba, ep);
@@ -1703,7 +1749,7 @@ static int bnx2i_tear_down_conn(struct bnx2i_hba *hba,
if (signal_pending(current))
flush_signals(current);
- del_timer_sync(&ep->ofld_timer);
+ timer_delete_sync(&ep->ofld_timer);
bnx2i_ep_destroy_list_del(hba, ep);
@@ -1769,7 +1815,7 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
}
bnx2i_ep = ep->dd_data;
- bnx2i_ep->num_active_cmds = 0;
+ atomic_set(&bnx2i_ep->num_active_cmds, 0);
iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
if (iscsi_cid == -1) {
printk(KERN_ALERT "bnx2i (%s): alloc_ep - unable to allocate "
@@ -1792,10 +1838,8 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
bnx2i_ep->state = EP_STATE_OFLD_START;
bnx2i_ep_ofld_list_add(hba, bnx2i_ep);
- init_timer(&bnx2i_ep->ofld_timer);
+ timer_setup(&bnx2i_ep->ofld_timer, bnx2i_ep_ofld_timer, 0);
bnx2i_ep->ofld_timer.expires = 2 * HZ + jiffies;
- bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer;
- bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep;
add_timer(&bnx2i_ep->ofld_timer);
if (bnx2i_send_conn_ofld_req(hba, bnx2i_ep)) {
@@ -1817,7 +1861,7 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
if (signal_pending(current))
flush_signals(current);
- del_timer_sync(&bnx2i_ep->ofld_timer);
+ timer_delete_sync(&bnx2i_ep->ofld_timer);
bnx2i_ep_ofld_list_del(hba, bnx2i_ep);
@@ -1866,7 +1910,8 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
bnx2i_ep_active_list_add(hba, bnx2i_ep);
- if (bnx2i_map_ep_dbell_regs(bnx2i_ep))
+ rc = bnx2i_map_ep_dbell_regs(bnx2i_ep);
+ if (rc)
goto del_active_ep;
mutex_unlock(&hba->net_dev_lock);
@@ -1932,7 +1977,7 @@ static int bnx2i_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
/**
* bnx2i_ep_tcp_conn_active - check EP state transition
- * @ep: endpoint pointer
+ * @bnx2i_ep: endpoint pointer
*
* check if underlying TCP connection is active
*/
@@ -1975,9 +2020,9 @@ static int bnx2i_ep_tcp_conn_active(struct bnx2i_endpoint *bnx2i_ep)
}
-/*
+/**
* bnx2i_hw_ep_disconnect - executes TCP connection teardown process in the hw
- * @ep: TCP connection (bnx2i endpoint) handle
+ * @bnx2i_ep: TCP connection (bnx2i endpoint) handle
*
* executes TCP connection teardown process
*/
@@ -2010,17 +2055,15 @@ int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep)
session = conn->session;
}
- init_timer(&bnx2i_ep->ofld_timer);
+ timer_setup(&bnx2i_ep->ofld_timer, bnx2i_ep_ofld_timer, 0);
bnx2i_ep->ofld_timer.expires = hba->conn_teardown_tmo + jiffies;
- bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer;
- bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep;
add_timer(&bnx2i_ep->ofld_timer);
if (!test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic))
goto out;
if (session) {
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
if (bnx2i_ep->state != EP_STATE_TCP_FIN_RCVD) {
if (session->state == ISCSI_STATE_LOGGING_OUT) {
if (bnx2i_ep->state == EP_STATE_LOGOUT_SENT) {
@@ -2036,7 +2079,7 @@ int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep)
} else
close = 1;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
}
bnx2i_ep->state = EP_STATE_DISCONN_START;
@@ -2052,11 +2095,12 @@ int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep)
else
/* wait for option-2 conn teardown */
wait_event_interruptible(bnx2i_ep->ofld_wait,
- bnx2i_ep->state != EP_STATE_DISCONN_START);
+ ((bnx2i_ep->state != EP_STATE_DISCONN_START)
+ && (bnx2i_ep->state != EP_STATE_TCP_FIN_RCVD)));
if (signal_pending(current))
flush_signals(current);
- del_timer_sync(&bnx2i_ep->ofld_timer);
+ timer_delete_sync(&bnx2i_ep->ofld_timer);
destroy_conn:
bnx2i_ep_active_list_del(hba, bnx2i_ep);
@@ -2078,7 +2122,6 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
{
struct bnx2i_endpoint *bnx2i_ep;
struct bnx2i_conn *bnx2i_conn = NULL;
- struct iscsi_conn *conn = NULL;
struct bnx2i_hba *hba;
bnx2i_ep = ep->dd_data;
@@ -2091,11 +2134,8 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
!time_after(jiffies, bnx2i_ep->timestamp + (12 * HZ)))
msleep(250);
- if (bnx2i_ep->conn) {
+ if (bnx2i_ep->conn)
bnx2i_conn = bnx2i_ep->conn;
- conn = bnx2i_conn->cls_conn->dd_data;
- iscsi_suspend_queue(conn);
- }
hba = bnx2i_ep->hba;
mutex_lock(&hba->net_dev_lock);
@@ -2133,8 +2173,8 @@ out:
/**
* bnx2i_nl_set_path - ISCSI_UEVENT_PATH_UPDATE user message handler
- * @buf: pointer to buffer containing iscsi path message
- *
+ * @shost: scsi host pointer
+ * @params: pointer to buffer containing iscsi path message
*/
static int bnx2i_nl_set_path(struct Scsi_Host *shost, struct iscsi_path *params)
{
@@ -2149,27 +2189,86 @@ static int bnx2i_nl_set_path(struct Scsi_Host *shost, struct iscsi_path *params)
return 0;
}
+static umode_t bnx2i_attr_is_visible(int param_type, int param)
+{
+ switch (param_type) {
+ case ISCSI_HOST_PARAM:
+ switch (param) {
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case ISCSI_PARAM:
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ case ISCSI_PARAM_HDRDGST_EN:
+ case ISCSI_PARAM_DATADGST_EN:
+ case ISCSI_PARAM_CONN_ADDRESS:
+ case ISCSI_PARAM_CONN_PORT:
+ case ISCSI_PARAM_EXP_STATSN:
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ case ISCSI_PARAM_PING_TMO:
+ case ISCSI_PARAM_RECV_TMO:
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ case ISCSI_PARAM_MAX_R2T:
+ case ISCSI_PARAM_IMM_DATA_EN:
+ case ISCSI_PARAM_FIRST_BURST:
+ case ISCSI_PARAM_MAX_BURST:
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ case ISCSI_PARAM_ERL:
+ case ISCSI_PARAM_TARGET_NAME:
+ case ISCSI_PARAM_TPGT:
+ case ISCSI_PARAM_USERNAME:
+ case ISCSI_PARAM_PASSWORD:
+ case ISCSI_PARAM_USERNAME_IN:
+ case ISCSI_PARAM_PASSWORD_IN:
+ case ISCSI_PARAM_FAST_ABORT:
+ case ISCSI_PARAM_ABORT_TMO:
+ case ISCSI_PARAM_LU_RESET_TMO:
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ case ISCSI_PARAM_IFACE_NAME:
+ case ISCSI_PARAM_INITIATOR_NAME:
+ case ISCSI_PARAM_BOOT_ROOT:
+ case ISCSI_PARAM_BOOT_NIC:
+ case ISCSI_PARAM_BOOT_TARGET:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
/*
* 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template
* used while registering with the scsi host and iSCSI transport module.
*/
-static struct scsi_host_template bnx2i_host_template = {
+static const struct scsi_host_template bnx2i_host_template = {
.module = THIS_MODULE,
- .name = "Broadcom Offload iSCSI Initiator",
+ .name = "QLogic Offload iSCSI Initiator",
.proc_name = "bnx2i",
.queuecommand = iscsi_queuecommand,
+ .eh_timed_out = iscsi_eh_cmd_timed_out,
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler = iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_recover_target,
- .change_queue_depth = iscsi_change_queue_depth,
- .can_queue = 1024,
+ .change_queue_depth = scsi_change_queue_depth,
+ .target_alloc = iscsi_target_alloc,
+ .can_queue = 2048,
.max_sectors = 127,
- .cmd_per_lun = 24,
+ .cmd_per_lun = 128,
.this_id = -1,
- .use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = ISCSI_MAX_BDS_PER_CMD,
- .shost_attrs = bnx2i_dev_attributes,
+ .shost_groups = bnx2i_dev_groups,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct iscsi_cmd),
};
struct iscsi_transport bnx2i_iscsi_transport = {
@@ -2179,37 +2278,13 @@ struct iscsi_transport bnx2i_iscsi_transport = {
CAP_MULTI_R2T | CAP_DATADGST |
CAP_DATA_PATH_OFFLOAD |
CAP_TEXT_NEGO,
- .param_mask = ISCSI_MAX_RECV_DLENGTH |
- ISCSI_MAX_XMIT_DLENGTH |
- ISCSI_HDRDGST_EN |
- ISCSI_DATADGST_EN |
- ISCSI_INITIAL_R2T_EN |
- ISCSI_MAX_R2T |
- ISCSI_IMM_DATA_EN |
- ISCSI_FIRST_BURST |
- ISCSI_MAX_BURST |
- ISCSI_PDU_INORDER_EN |
- ISCSI_DATASEQ_INORDER_EN |
- ISCSI_ERL |
- ISCSI_CONN_PORT |
- ISCSI_CONN_ADDRESS |
- ISCSI_EXP_STATSN |
- ISCSI_PERSISTENT_PORT |
- ISCSI_PERSISTENT_ADDRESS |
- ISCSI_TARGET_NAME | ISCSI_TPGT |
- ISCSI_USERNAME | ISCSI_PASSWORD |
- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO |
- ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
- ISCSI_HOST_NETDEV_NAME,
.create_session = bnx2i_session_create,
.destroy_session = bnx2i_session_destroy,
.create_conn = bnx2i_conn_create,
.bind_conn = bnx2i_conn_bind,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = bnx2i_conn_destroy,
+ .attr_is_visible = bnx2i_attr_is_visible,
.set_param = iscsi_set_param,
.get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param,
diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c
index 9174196d9033..d6b0bbb5176b 100644
--- a/drivers/scsi/bnx2i/bnx2i_sysfs.c
+++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c
@@ -1,13 +1,15 @@
-/* bnx2i_sysfs.c: Broadcom NetXtreme II iSCSI driver.
+/* bnx2i_sysfs.c: QLogic NetXtreme II iSCSI driver.
*
- * Copyright (c) 2004 - 2010 Broadcom Corporation
+ * Copyright (c) 2004 - 2013 Broadcom Corporation
+ * Copyright (c) 2014, QLogic Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Anil Veerabhadrappa (anilgv@broadcom.com)
- * Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com)
+ * Maintained by: QLogic-Storage-Upstream@qlogic.com
*/
#include "bnx2i.h"
@@ -28,6 +30,7 @@ static inline struct bnx2i_hba *bnx2i_dev_to_hba(struct device *dev)
/**
* bnx2i_show_sq_info - return(s currently configured send queue (SQ) size
* @dev: device pointer
+ * @attr: device attribute (unused)
* @buf: buffer to return current SQ size parameter
*
* Returns current SQ size parameter, this paramater determines the number
@@ -45,6 +48,7 @@ static ssize_t bnx2i_show_sq_info(struct device *dev,
/**
* bnx2i_set_sq_info - update send queue (SQ) size parameter
* @dev: device pointer
+ * @attr: device attribute (unused)
* @buf: buffer to return current SQ size parameter
* @count: parameter buffer size
*
@@ -85,6 +89,7 @@ skip_config:
/**
* bnx2i_show_ccell_info - returns command cell (HQ) size
* @dev: device pointer
+ * @attr: device attribute (unused)
* @buf: buffer to return current SQ size parameter
*
* returns per-connection TCP history queue size parameter
@@ -99,8 +104,9 @@ static ssize_t bnx2i_show_ccell_info(struct device *dev,
/**
- * bnx2i_get_link_state - set command cell (HQ) size
+ * bnx2i_set_ccell_info - set command cell (HQ) size
* @dev: device pointer
+ * @attr: device attribute (unused)
* @buf: buffer to return current SQ size parameter
* @count: parameter buffer size
*
@@ -136,8 +142,17 @@ static DEVICE_ATTR(sq_size, S_IRUGO | S_IWUSR,
static DEVICE_ATTR(num_ccell, S_IRUGO | S_IWUSR,
bnx2i_show_ccell_info, bnx2i_set_ccell_info);
-struct device_attribute *bnx2i_dev_attributes[] = {
- &dev_attr_sq_size,
- &dev_attr_num_ccell,
+static struct attribute *bnx2i_dev_attributes[] = {
+ &dev_attr_sq_size.attr,
+ &dev_attr_num_ccell.attr,
+ NULL
+};
+
+static const struct attribute_group bnx2i_dev_attr_group = {
+ .attrs = bnx2i_dev_attributes
+};
+
+const struct attribute_group *bnx2i_dev_groups[] = {
+ &bnx2i_dev_attr_group,
NULL
};
diff --git a/drivers/scsi/bvme6000_scsi.c b/drivers/scsi/bvme6000_scsi.c
index d40ea2f5be10..baf5f4e47937 100644
--- a/drivers/scsi/bvme6000_scsi.c
+++ b/drivers/scsi/bvme6000_scsi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Detection routine for the NCR53c710 based BVME6000 SCSI Controllers for Linux.
*
@@ -34,7 +35,7 @@ static struct scsi_host_template bvme6000_scsi_driver_template = {
static struct platform_device *bvme6000_scsi_device;
-static __devinit int
+static int
bvme6000_probe(struct platform_device *dev)
{
struct Scsi_Host *host;
@@ -88,7 +89,7 @@ bvme6000_probe(struct platform_device *dev)
return -ENODEV;
}
-static __devexit int
+static void
bvme6000_device_remove(struct platform_device *dev)
{
struct Scsi_Host *host = platform_get_drvdata(dev);
@@ -98,17 +99,14 @@ bvme6000_device_remove(struct platform_device *dev)
NCR_700_release(host);
kfree(hostdata);
free_irq(host->irq, host);
-
- return 0;
}
static struct platform_driver bvme6000_scsi_driver = {
.driver = {
.name = "bvme6000-scsi",
- .owner = THIS_MODULE,
},
.probe = bvme6000_probe,
- .remove = __devexit_p(bvme6000_device_remove),
+ .remove = bvme6000_device_remove,
};
static int __init bvme6000_scsi_init(void)
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index a15474eef5f7..fa07a6f54003 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SCSI Media Changer device driver for Linux 2.6
*
@@ -43,7 +44,6 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SCSI_CHANGER_MAJOR);
MODULE_ALIAS_SCSI_DEVICE(TYPE_MEDIUM_CHANGER);
-static DEFINE_MUTEX(ch_mutex);
static int init = 1;
module_param(init, int, 0444);
MODULE_PARM_DESC(init, \
@@ -63,7 +63,7 @@ static int verbose = 1;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose,"be verbose (default: on)");
-static int debug = 0;
+static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,"enable/disable debug messages, also prints more "
"detailed sense codes on scsi errors (default: off)");
@@ -84,24 +84,30 @@ static const char * vendor_labels[CH_TYPES-4] = {
};
// module_param_string_array(vendor_labels, NULL, 0444);
+#define ch_printk(prefix, ch, fmt, a...) \
+ sdev_prefix_printk(prefix, (ch)->device, (ch)->name, fmt, ##a)
+
#define DPRINTK(fmt, arg...) \
do { \
if (debug) \
- printk(KERN_DEBUG "%s: " fmt, ch->name, ##arg); \
+ ch_printk(KERN_DEBUG, ch, fmt, ##arg); \
} while (0)
#define VPRINTK(level, fmt, arg...) \
do { \
if (verbose) \
- printk(level "%s: " fmt, ch->name, ##arg); \
+ ch_printk(level, ch, fmt, ##arg); \
} while (0)
/* ------------------------------------------------------------------- */
#define MAX_RETRIES 1
-static struct class * ch_sysfs_class;
+static const struct class ch_sysfs_class = {
+ .name = "scsi_changer",
+};
typedef struct {
+ struct kref ref;
struct list_head list;
int minor;
char name[8];
@@ -109,7 +115,6 @@ typedef struct {
struct scsi_device **dt; /* ptrs to data transfer elements */
u_int firsts[CH_TYPES];
u_int counts[CH_TYPES];
- u_int unit_attention;
u_int voltags;
struct mutex lock;
} scsi_changer;
@@ -179,40 +184,40 @@ static int ch_find_errno(struct scsi_sense_hdr *sshdr)
}
static int
-ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
- void *buffer, unsigned buflength,
- enum dma_data_direction direction)
+ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len,
+ void *buffer, unsigned int buflength, enum req_op op)
{
- int errno, retries = 0, timeout, result;
+ int errno = 0, timeout, result;
struct scsi_sense_hdr sshdr;
+ struct scsi_failure failure_defs[] = {
+ {
+ .sense = UNIT_ATTENTION,
+ .asc = SCMD_FAILURE_ASC_ANY,
+ .ascq = SCMD_FAILURE_ASCQ_ANY,
+ .allowed = 3,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ {}
+ };
+ struct scsi_failures failures = {
+ .failure_definitions = failure_defs,
+ };
+ const struct scsi_exec_args exec_args = {
+ .sshdr = &sshdr,
+ .failures = &failures,
+ };
timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS)
? timeout_init : timeout_move;
- retry:
- errno = 0;
- if (debug) {
- DPRINTK("command: ");
- __scsi_print_command(cmd);
- }
-
- result = scsi_execute_req(ch->device, cmd, direction, buffer,
- buflength, &sshdr, timeout * HZ,
- MAX_RETRIES, NULL);
-
- DPRINTK("result: 0x%x\n",result);
- if (driver_byte(result) & DRIVER_SENSE) {
+ result = scsi_execute_cmd(ch->device, cmd, op, buffer, buflength,
+ timeout * HZ, MAX_RETRIES, &exec_args);
+ if (result < 0)
+ return result;
+ if (scsi_sense_valid(&sshdr)) {
if (debug)
- scsi_print_sense_hdr(ch->name, &sshdr);
+ scsi_print_sense_hdr(ch->device, ch->name, &sshdr);
errno = ch_find_errno(&sshdr);
-
- switch(sshdr.sense_key) {
- case UNIT_ATTENTION:
- ch->unit_attention = 1;
- if (retries++ < 3)
- goto retry;
- break;
- }
}
return errno;
}
@@ -240,21 +245,22 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
u_char *buffer;
int result;
- buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(512, GFP_KERNEL);
if(!buffer)
return -ENOMEM;
retry:
memset(cmd,0,sizeof(cmd));
cmd[0] = READ_ELEMENT_STATUS;
- cmd[1] = (ch->device->lun << 5) |
+ cmd[1] = ((ch->device->lun & 0x7) << 5) |
(ch->voltags ? 0x10 : 0) |
ch_elem_to_typecode(ch,elem);
cmd[2] = (elem >> 8) & 0xff;
cmd[3] = elem & 0xff;
cmd[5] = 1;
cmd[9] = 255;
- if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
+ if (0 == (result = ch_do_scsi(ch, cmd, 12,
+ buffer, 256, REQ_OP_DRV_IN))) {
if (((buffer[16] << 8) | buffer[17]) != elem) {
DPRINTK("asked for element 0x%02x, got 0x%02x\n",
elem,(buffer[16] << 8) | buffer[17]);
@@ -283,8 +289,8 @@ ch_init_elem(scsi_changer *ch)
VPRINTK(KERN_INFO, "INITIALIZE ELEMENT STATUS, may take some time ...\n");
memset(cmd,0,sizeof(cmd));
cmd[0] = INITIALIZE_ELEMENT_STATUS;
- cmd[1] = ch->device->lun << 5;
- err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
+ cmd[1] = (ch->device->lun & 0x7) << 5;
+ err = ch_do_scsi(ch, cmd, 6, NULL, 0, REQ_OP_DRV_IN);
VPRINTK(KERN_INFO, "... finished\n");
return err;
}
@@ -297,19 +303,19 @@ ch_readconfig(scsi_changer *ch)
int result,id,lun,i;
u_int elem;
- buffer = kzalloc(512, GFP_KERNEL | GFP_DMA);
+ buffer = kzalloc(512, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
memset(cmd,0,sizeof(cmd));
cmd[0] = MODE_SENSE;
- cmd[1] = ch->device->lun << 5;
+ cmd[1] = (ch->device->lun & 0x7) << 5;
cmd[2] = 0x1d;
cmd[4] = 255;
- result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE);
+ result = ch_do_scsi(ch, cmd, 10, buffer, 255, REQ_OP_DRV_IN);
if (0 != result) {
cmd[1] |= (1<<3);
- result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE);
+ result = ch_do_scsi(ch, cmd, 10, buffer, 255, REQ_OP_DRV_IN);
}
if (0 == result) {
ch->firsts[CHET_MT] =
@@ -341,7 +347,7 @@ ch_readconfig(scsi_changer *ch)
ch->firsts[CHET_DT],
ch->counts[CHET_DT]);
} else {
- VPRINTK(KERN_INFO, "reading element address assigment page failed!\n");
+ VPRINTK(KERN_INFO, "reading element address assignment page failed!\n");
}
/* vendor specific element types */
@@ -428,13 +434,13 @@ ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
cmd[0] = POSITION_TO_ELEMENT;
- cmd[1] = ch->device->lun << 5;
+ cmd[1] = (ch->device->lun & 0x7) << 5;
cmd[2] = (trans >> 8) & 0xff;
cmd[3] = trans & 0xff;
cmd[4] = (elem >> 8) & 0xff;
cmd[5] = elem & 0xff;
cmd[8] = rotate ? 1 : 0;
- return ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
+ return ch_do_scsi(ch, cmd, 10, NULL, 0, REQ_OP_DRV_IN);
}
static int
@@ -447,7 +453,7 @@ ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
cmd[0] = MOVE_MEDIUM;
- cmd[1] = ch->device->lun << 5;
+ cmd[1] = (ch->device->lun & 0x7) << 5;
cmd[2] = (trans >> 8) & 0xff;
cmd[3] = trans & 0xff;
cmd[4] = (src >> 8) & 0xff;
@@ -455,7 +461,7 @@ ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate)
cmd[6] = (dest >> 8) & 0xff;
cmd[7] = dest & 0xff;
cmd[10] = rotate ? 1 : 0;
- return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE);
+ return ch_do_scsi(ch, cmd, 12, NULL, 0, REQ_OP_DRV_IN);
}
static int
@@ -470,7 +476,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src,
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
cmd[0] = EXCHANGE_MEDIUM;
- cmd[1] = ch->device->lun << 5;
+ cmd[1] = (ch->device->lun & 0x7) << 5;
cmd[2] = (trans >> 8) & 0xff;
cmd[3] = trans & 0xff;
cmd[4] = (src >> 8) & 0xff;
@@ -481,7 +487,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src,
cmd[9] = dest2 & 0xff;
cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0);
- return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE);
+ return ch_do_scsi(ch, cmd, 12, NULL, 0, REQ_OP_DRV_IN);
}
static void
@@ -518,7 +524,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem,
elem, tag);
memset(cmd,0,sizeof(cmd));
cmd[0] = SEND_VOLUME_TAG;
- cmd[1] = (ch->device->lun << 5) |
+ cmd[1] = ((ch->device->lun & 0x7) << 5) |
ch_elem_to_typecode(ch,elem);
cmd[2] = (elem >> 8) & 0xff;
cmd[3] = elem & 0xff;
@@ -531,7 +537,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem,
memcpy(buffer,tag,32);
ch_check_voltag(buffer);
- result = ch_do_scsi(ch, cmd, buffer, 256, DMA_TO_DEVICE);
+ result = ch_do_scsi(ch, cmd, 12, buffer, 256, REQ_OP_DRV_OUT);
kfree(buffer);
return result;
}
@@ -565,6 +571,15 @@ static int ch_gstatus(scsi_changer *ch, int type, unsigned char __user *dest)
/* ------------------------------------------------------------------------ */
+static void ch_destroy(struct kref *ref)
+{
+ scsi_changer *ch = container_of(ref, scsi_changer, ref);
+
+ ch->device = NULL;
+ kfree(ch->dt);
+ kfree(ch);
+}
+
static int
ch_release(struct inode *inode, struct file *file)
{
@@ -572,6 +587,7 @@ ch_release(struct inode *inode, struct file *file)
scsi_device_put(ch->device);
file->private_data = NULL;
+ kref_put(&ch->ref, ch_destroy);
return 0;
}
@@ -581,19 +597,22 @@ ch_open(struct inode *inode, struct file *file)
scsi_changer *ch;
int minor = iminor(inode);
- mutex_lock(&ch_mutex);
spin_lock(&ch_index_lock);
ch = idr_find(&ch_index_idr, minor);
- if (NULL == ch || scsi_device_get(ch->device)) {
+ if (ch == NULL || !kref_get_unless_zero(&ch->ref)) {
spin_unlock(&ch_index_lock);
- mutex_unlock(&ch_mutex);
return -ENXIO;
}
spin_unlock(&ch_index_lock);
-
+ if (scsi_device_get(ch->device)) {
+ kref_put(&ch->ref, ch_destroy);
+ return -ENXIO;
+ }
+ /* Synchronize with ch_probe() */
+ mutex_lock(&ch->lock);
file->private_data = ch;
- mutex_unlock(&ch_mutex);
+ mutex_unlock(&ch->lock);
return 0;
}
@@ -605,6 +624,12 @@ ch_checkrange(scsi_changer *ch, unsigned int type, unsigned int unit)
return 0;
}
+struct changer_element_status32 {
+ int ces_type;
+ compat_uptr_t ces_data;
+};
+#define CHIOGSTATUS32 _IOW('c', 8, struct changer_element_status32)
+
static long ch_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -612,6 +637,11 @@ static long ch_ioctl(struct file *file,
int retval;
void __user *argp = (void __user *)arg;
+ retval = scsi_ioctl_block_when_processing_errors(ch->device, cmd,
+ file->f_flags & O_NDELAY);
+ if (retval)
+ return retval;
+
switch (cmd) {
case CHIOGPARAMS:
{
@@ -634,19 +664,23 @@ static long ch_ioctl(struct file *file,
memset(&vparams,0,sizeof(vparams));
if (ch->counts[CHET_V1]) {
vparams.cvp_n1 = ch->counts[CHET_V1];
- strncpy(vparams.cvp_label1,vendor_labels[0],16);
+ strscpy(vparams.cvp_label1, vendor_labels[0],
+ sizeof(vparams.cvp_label1));
}
if (ch->counts[CHET_V2]) {
vparams.cvp_n2 = ch->counts[CHET_V2];
- strncpy(vparams.cvp_label2,vendor_labels[1],16);
+ strscpy(vparams.cvp_label2, vendor_labels[1],
+ sizeof(vparams.cvp_label2));
}
if (ch->counts[CHET_V3]) {
vparams.cvp_n3 = ch->counts[CHET_V3];
- strncpy(vparams.cvp_label3,vendor_labels[2],16);
+ strscpy(vparams.cvp_label3, vendor_labels[2],
+ sizeof(vparams.cvp_label3));
}
if (ch->counts[CHET_V4]) {
vparams.cvp_n4 = ch->counts[CHET_V4];
- strncpy(vparams.cvp_label4,vendor_labels[3],16);
+ strscpy(vparams.cvp_label4, vendor_labels[3],
+ sizeof(vparams.cvp_label4));
}
if (copy_to_user(argp, &vparams, sizeof(vparams)))
return -EFAULT;
@@ -730,7 +764,20 @@ static long ch_ioctl(struct file *file,
return ch_gstatus(ch, ces.ces_type, ces.ces_data);
}
+#ifdef CONFIG_COMPAT
+ case CHIOGSTATUS32:
+ {
+ struct changer_element_status32 ces32;
+
+ if (copy_from_user(&ces32, argp, sizeof(ces32)))
+ return -EFAULT;
+ if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES)
+ return -EINVAL;
+ return ch_gstatus(ch, ces32.ces_type,
+ compat_ptr(ces32.ces_data));
+ }
+#endif
case CHIOGELEM:
{
struct changer_get_element cge;
@@ -746,7 +793,7 @@ static long ch_ioctl(struct file *file,
return -EINVAL;
elem = ch->firsts[cge.cge_type] + cge.cge_unit;
- buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(512, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
mutex_lock(&ch->lock);
@@ -754,7 +801,7 @@ static long ch_ioctl(struct file *file,
voltag_retry:
memset(ch_cmd, 0, sizeof(ch_cmd));
ch_cmd[0] = READ_ELEMENT_STATUS;
- ch_cmd[1] = (ch->device->lun << 5) |
+ ch_cmd[1] = ((ch->device->lun & 0x7) << 5) |
(ch->voltags ? 0x10 : 0) |
ch_elem_to_typecode(ch,elem);
ch_cmd[2] = (elem >> 8) & 0xff;
@@ -762,7 +809,7 @@ static long ch_ioctl(struct file *file,
ch_cmd[5] = 1;
ch_cmd[9] = 255;
- result = ch_do_scsi(ch, ch_cmd, buffer, 256, DMA_FROM_DEVICE);
+ result = ch_do_scsi(ch, ch_cmd, 12, buffer, 256, REQ_OP_DRV_IN);
if (!result) {
cge.cge_status = buffer[18];
cge.cge_flags = 0;
@@ -839,63 +886,19 @@ static long ch_ioctl(struct file *file,
}
default:
- return scsi_ioctl(ch->device, cmd, argp);
+ return scsi_ioctl(ch->device, file->f_mode & FMODE_WRITE, cmd,
+ argp);
}
}
-#ifdef CONFIG_COMPAT
-
-struct changer_element_status32 {
- int ces_type;
- compat_uptr_t ces_data;
-};
-#define CHIOGSTATUS32 _IOW('c', 8,struct changer_element_status32)
-
-static long ch_ioctl_compat(struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- scsi_changer *ch = file->private_data;
-
- switch (cmd) {
- case CHIOGPARAMS:
- case CHIOGVPARAMS:
- case CHIOPOSITION:
- case CHIOMOVE:
- case CHIOEXCHANGE:
- case CHIOGELEM:
- case CHIOINITELEM:
- case CHIOSVOLTAG:
- /* compatible */
- return ch_ioctl(file, cmd, arg);
- case CHIOGSTATUS32:
- {
- struct changer_element_status32 ces32;
- unsigned char __user *data;
-
- if (copy_from_user(&ces32, (void __user *)arg, sizeof (ces32)))
- return -EFAULT;
- if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES)
- return -EINVAL;
-
- data = compat_ptr(ces32.ces_data);
- return ch_gstatus(ch, ces32.ces_type, data);
- }
- default:
- // return scsi_ioctl_compat(ch->device, cmd, (void*)arg);
- return -ENOIOCTLCMD;
-
- }
-}
-#endif
-
/* ------------------------------------------------------------------------ */
static int ch_probe(struct device *dev)
{
struct scsi_device *sd = to_scsi_device(dev);
struct device *class_dev;
- int minor, ret = -ENOMEM;
+ int ret;
scsi_changer *ch;
if (sd->type != TYPE_MEDIUM_CHANGER)
@@ -905,46 +908,60 @@ static int ch_probe(struct device *dev)
if (NULL == ch)
return -ENOMEM;
- if (!idr_pre_get(&ch_index_idr, GFP_KERNEL))
- goto free_ch;
-
+ idr_preload(GFP_KERNEL);
spin_lock(&ch_index_lock);
- ret = idr_get_new(&ch_index_idr, ch, &minor);
+ ret = idr_alloc(&ch_index_idr, ch, 0, CH_MAX_DEVS + 1, GFP_NOWAIT);
spin_unlock(&ch_index_lock);
+ idr_preload_end();
- if (ret)
+ if (ret < 0) {
+ if (ret == -ENOSPC)
+ ret = -ENODEV;
goto free_ch;
-
- if (minor > CH_MAX_DEVS) {
- ret = -ENODEV;
- goto remove_idr;
}
- ch->minor = minor;
+ ch->minor = ret;
sprintf(ch->name,"ch%d",ch->minor);
+ ret = scsi_device_get(sd);
+ if (ret) {
+ sdev_printk(KERN_WARNING, sd, "ch%d: failed to get device\n",
+ ch->minor);
+ goto remove_idr;
+ }
- class_dev = device_create(ch_sysfs_class, dev,
+ mutex_init(&ch->lock);
+ kref_init(&ch->ref);
+ ch->device = sd;
+ class_dev = device_create(&ch_sysfs_class, dev,
MKDEV(SCSI_CHANGER_MAJOR, ch->minor), ch,
"s%s", ch->name);
if (IS_ERR(class_dev)) {
- printk(KERN_WARNING "ch%d: device_create failed\n",
- ch->minor);
+ sdev_printk(KERN_WARNING, sd, "ch%d: device_create failed\n",
+ ch->minor);
ret = PTR_ERR(class_dev);
- goto remove_idr;
+ goto put_device;
}
- mutex_init(&ch->lock);
- ch->device = sd;
- ch_readconfig(ch);
+ mutex_lock(&ch->lock);
+ ret = ch_readconfig(ch);
+ if (ret) {
+ mutex_unlock(&ch->lock);
+ goto destroy_dev;
+ }
if (init)
ch_init_elem(ch);
+ mutex_unlock(&ch->lock);
dev_set_drvdata(dev, ch);
sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name);
return 0;
+destroy_dev:
+ device_destroy(&ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR, ch->minor));
+put_device:
+ scsi_device_put(sd);
remove_idr:
- idr_remove(&ch_index_idr, minor);
+ idr_remove(&ch_index_idr, ch->minor);
free_ch:
kfree(ch);
return ret;
@@ -956,18 +973,19 @@ static int ch_remove(struct device *dev)
spin_lock(&ch_index_lock);
idr_remove(&ch_index_idr, ch->minor);
+ dev_set_drvdata(dev, NULL);
spin_unlock(&ch_index_lock);
- device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR,ch->minor));
- kfree(ch->dt);
- kfree(ch);
+ device_destroy(&ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR, ch->minor));
+ scsi_device_put(ch->device);
+ kref_put(&ch->ref, ch_destroy);
return 0;
}
static struct scsi_driver ch_template = {
- .owner = THIS_MODULE,
.gendrv = {
.name = "ch",
+ .owner = THIS_MODULE,
.probe = ch_probe,
.remove = ch_remove,
},
@@ -978,9 +996,7 @@ static const struct file_operations changer_fops = {
.open = ch_open,
.release = ch_release,
.unlocked_ioctl = ch_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ch_ioctl_compat,
-#endif
+ .compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};
@@ -989,11 +1005,9 @@ static int __init init_ch_module(void)
int rc;
printk(KERN_INFO "SCSI Media Changer driver v" VERSION " \n");
- ch_sysfs_class = class_create(THIS_MODULE, "scsi_changer");
- if (IS_ERR(ch_sysfs_class)) {
- rc = PTR_ERR(ch_sysfs_class);
+ rc = class_register(&ch_sysfs_class);
+ if (rc)
return rc;
- }
rc = register_chrdev(SCSI_CHANGER_MAJOR,"ch",&changer_fops);
if (rc < 0) {
printk("Unable to get major %d for SCSI-Changer\n",
@@ -1008,7 +1022,7 @@ static int __init init_ch_module(void)
fail2:
unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");
fail1:
- class_destroy(ch_sysfs_class);
+ class_unregister(&ch_sysfs_class);
return rc;
}
@@ -1016,15 +1030,9 @@ static void __exit exit_ch_module(void)
{
scsi_unregister_driver(&ch_template.gendrv);
unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");
- class_destroy(ch_sysfs_class);
+ class_unregister(&ch_sysfs_class);
idr_destroy(&ch_index_idr);
}
module_init(init_ch_module);
module_exit(exit_ch_module);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index 450e011f981a..340785536998 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -1,10 +1,11 @@
-/*
+// SPDX-License-Identifier: GPL-2.0
+/*
* ASCII values for a number of symbolic constants, printing functions,
* etc.
* Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422)
* Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002)
* by D. Gilbert and aeb (20020609)
- * Update to SPC-4 T10/1713-D Rev 20, 22 May 2009, D. Gilbert 20090624
+ * Updated to SPC-4 T10/1713-D Rev 36g, D. Gilbert 20130701
*/
#include <linux/blkdev.h>
@@ -18,29 +19,31 @@
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dbg.h>
-
-
/* Commands with service actions that change the command name */
-#define MAINTENANCE_IN 0xa3
-#define MAINTENANCE_OUT 0xa4
-#define SERVICE_ACTION_IN_12 0xab
-#define SERVICE_ACTION_OUT_12 0xa9
-#define SERVICE_ACTION_IN_16 0x9e
-#define SERVICE_ACTION_OUT_16 0x9f
+#define THIRD_PARTY_COPY_OUT 0x83
+#define THIRD_PARTY_COPY_IN 0x84
+struct sa_name_list {
+ int opcode;
+ const struct value_name_pair *arr;
+ int arr_sz;
+};
+struct value_name_pair {
+ int value;
+ const char * name;
+};
-#ifdef CONFIG_SCSI_CONSTANTS
static const char * cdb_byte0_names[] = {
/* 00-03 */ "Test Unit Ready", "Rezero Unit/Rewind", NULL, "Request Sense",
/* 04-07 */ "Format Unit/Medium", "Read Block Limits", NULL,
"Reassign Blocks",
/* 08-0d */ "Read(6)", NULL, "Write(6)", "Seek(6)", NULL, NULL,
-/* 0e-12 */ NULL, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 0e-12 */ NULL, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
/* 13-16 */ "Verify(6)", "Recover Buffered Data", "Mode Select(6)",
"Reserve(6)",
/* 17-1a */ "Release(6)", "Copy", "Erase", "Mode Sense(6)",
-/* 1b-1d */ "Start/Stop Unit", "Receive Diagnostic", "Send Diagnostic",
+/* 1b-1d */ "Start/Stop Unit", "Receive Diagnostic", "Send Diagnostic",
/* 1e-1f */ "Prevent/Allow Medium Removal", NULL,
/* 20-22 */ NULL, NULL, NULL,
/* 23-28 */ "Read Format Capacities", "Set Window",
@@ -48,16 +51,16 @@ static const char * cdb_byte0_names[] = {
/* 29-2d */ "Read Generation", "Write(10)", "Seek(10)", "Erase(10)",
"Read updated block",
/* 2e-31 */ "Write Verify(10)", "Verify(10)", "Search High", "Search Equal",
-/* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position",
/* 35-37 */ "Synchronize Cache(10)", "Lock/Unlock Cache(10)",
- "Read Defect Data(10)",
-/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
- "Read Buffer",
+ "Read Defect Data(10)",
+/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
+ "Read Buffer",
/* 3d-3f */ "Update Block", "Read Long(10)", "Write Long(10)",
/* 40-41 */ "Change Definition", "Write Same(10)",
/* 42-48 */ "Unmap/Read sub-channel", "Read TOC/PMA/ATIP",
"Read density support", "Play audio(10)", "Get configuration",
- "Play audio msf", "Play audio track/index",
+ "Play audio msf", "Sanitize/Play audio track/index",
/* 49-4f */ "Play track relative(10)", "Get event status notification",
"Pause/resume", "Log Select", "Log Sense", "Stop play/scan",
NULL,
@@ -72,17 +75,17 @@ static const char * cdb_byte0_names[] = {
/* 70-77 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, "Extended CDB",
"Variable length",
-/* 80-84 */ "Xdwrite(16)", "Rebuild(16)", "Regenerate(16)", "Extended copy",
- "Receive copy results",
+/* 80-84 */ "Xdwrite(16)", "Rebuild(16)", "Regenerate(16)",
+ "Third party copy out", "Third party copy in",
/* 85-89 */ "ATA command pass through(16)", "Access control in",
- "Access control out", "Read(16)", "Memory Export Out(16)",
+ "Access control out", "Read(16)", "Compare and Write",
/* 8a-8f */ "Write(16)", "ORWrite", "Read attributes", "Write attributes",
"Write and verify(16)", "Verify(16)",
/* 90-94 */ "Pre-fetch(16)", "Synchronize cache(16)",
"Lock/unlock cache(16)", "Write same(16)", NULL,
/* 95-99 */ NULL, NULL, NULL, NULL, NULL,
-/* 9a-9f */ NULL, NULL, NULL, NULL, "Service action in(16)",
- "Service action out(16)",
+/* 9a-9f */ NULL, NULL, NULL, "Service action bidirectional",
+ "Service action in(16)", "Service action out(16)",
/* a0-a5 */ "Report luns", "ATA command pass through(12)/Blank",
"Security protocol in", "Maintenance in", "Maintenance out",
"Move medium/play audio(12)",
@@ -101,11 +104,6 @@ static const char * cdb_byte0_names[] = {
"Volume set (out), Send DVD structure",
};
-struct value_name_pair {
- int value;
- const char * name;
-};
-
static const struct value_name_pair maint_in_arr[] = {
{0x5, "Report identifying information"},
{0xa, "Report target port groups"},
@@ -122,6 +120,7 @@ static const struct value_name_pair maint_out_arr[] = {
{0x6, "Set identifying information"},
{0xa, "Set target port groups"},
{0xb, "Change aliases"},
+ {0xc, "Remove I_T nexus"},
{0xe, "Set priority"},
{0xf, "Set timestamp"},
{0x10, "Management protocol out"},
@@ -138,10 +137,16 @@ static const struct value_name_pair serv_out12_arr[] = {
};
#define SERV_OUT12_SZ ARRAY_SIZE(serv_out12_arr)
+static const struct value_name_pair serv_bidi_arr[] = {
+ {-1, "dummy entry"},
+};
+#define SERV_BIDI_SZ ARRAY_SIZE(serv_bidi_arr)
+
static const struct value_name_pair serv_in16_arr[] = {
{0x10, "Read capacity(16)"},
{0x11, "Read long(16)"},
{0x12, "Get LBA status"},
+ {0x13, "Report referrals"},
};
#define SERV_IN16_SZ ARRAY_SIZE(serv_in16_arr)
@@ -151,6 +156,51 @@ static const struct value_name_pair serv_out16_arr[] = {
};
#define SERV_OUT16_SZ ARRAY_SIZE(serv_out16_arr)
+static const struct value_name_pair pr_in_arr[] = {
+ {0x0, "Persistent reserve in, read keys"},
+ {0x1, "Persistent reserve in, read reservation"},
+ {0x2, "Persistent reserve in, report capabilities"},
+ {0x3, "Persistent reserve in, read full status"},
+};
+#define PR_IN_SZ ARRAY_SIZE(pr_in_arr)
+
+static const struct value_name_pair pr_out_arr[] = {
+ {0x0, "Persistent reserve out, register"},
+ {0x1, "Persistent reserve out, reserve"},
+ {0x2, "Persistent reserve out, release"},
+ {0x3, "Persistent reserve out, clear"},
+ {0x4, "Persistent reserve out, preempt"},
+ {0x5, "Persistent reserve out, preempt and abort"},
+ {0x6, "Persistent reserve out, register and ignore existing key"},
+ {0x7, "Persistent reserve out, register and move"},
+};
+#define PR_OUT_SZ ARRAY_SIZE(pr_out_arr)
+
+/* SPC-4 rev 34 renamed the Extended Copy opcode to Third Party Copy Out.
+ LID1 (List Identifier length: 1 byte) is the Extended Copy found in SPC-2
+ and SPC-3 */
+static const struct value_name_pair tpc_out_arr[] = {
+ {0x0, "Extended copy(LID1)"},
+ {0x1, "Extended copy(LID4)"},
+ {0x10, "Populate token"},
+ {0x11, "Write using token"},
+ {0x1c, "Copy operation abort"},
+};
+#define TPC_OUT_SZ ARRAY_SIZE(tpc_out_arr)
+
+static const struct value_name_pair tpc_in_arr[] = {
+ {0x0, "Receive copy status(LID1)"},
+ {0x1, "Receive copy data(LID1)"},
+ {0x3, "Receive copy operating parameters"},
+ {0x4, "Receive copy failure details(LID1)"},
+ {0x5, "Receive copy status(LID4)"},
+ {0x6, "Receive copy data(LID4)"},
+ {0x7, "Receive ROD token information"},
+ {0x8, "Report all ROD tokens"},
+};
+#define TPC_IN_SZ ARRAY_SIZE(tpc_in_arr)
+
+
static const struct value_name_pair variable_length_arr[] = {
{0x1, "Rebuild(32)"},
{0x2, "Regenerate(32)"},
@@ -191,981 +241,97 @@ static const struct value_name_pair variable_length_arr[] = {
};
#define VARIABLE_LENGTH_SZ ARRAY_SIZE(variable_length_arr)
-static const char * get_sa_name(const struct value_name_pair * arr,
- int arr_sz, int service_action)
+static struct sa_name_list sa_names_arr[] = {
+ {VARIABLE_LENGTH_CMD, variable_length_arr, VARIABLE_LENGTH_SZ},
+ {MAINTENANCE_IN, maint_in_arr, MAINT_IN_SZ},
+ {MAINTENANCE_OUT, maint_out_arr, MAINT_OUT_SZ},
+ {PERSISTENT_RESERVE_IN, pr_in_arr, PR_IN_SZ},
+ {PERSISTENT_RESERVE_OUT, pr_out_arr, PR_OUT_SZ},
+ {SERVICE_ACTION_IN_12, serv_in12_arr, SERV_IN12_SZ},
+ {SERVICE_ACTION_OUT_12, serv_out12_arr, SERV_OUT12_SZ},
+ {SERVICE_ACTION_BIDIRECTIONAL, serv_bidi_arr, SERV_BIDI_SZ},
+ {SERVICE_ACTION_IN_16, serv_in16_arr, SERV_IN16_SZ},
+ {SERVICE_ACTION_OUT_16, serv_out16_arr, SERV_OUT16_SZ},
+ {THIRD_PARTY_COPY_IN, tpc_in_arr, TPC_IN_SZ},
+ {THIRD_PARTY_COPY_OUT, tpc_out_arr, TPC_OUT_SZ},
+ {0, NULL, 0},
+};
+
+bool scsi_opcode_sa_name(int opcode, int service_action,
+ const char **cdb_name, const char **sa_name)
{
- int k;
+ struct sa_name_list *sa_name_ptr;
+ const struct value_name_pair *arr = NULL;
+ int arr_sz, k;
- for (k = 0; k < arr_sz; ++k, ++arr) {
- if (service_action == arr->value)
- break;
- }
- return (k < arr_sz) ? arr->name : NULL;
-}
+ *cdb_name = NULL;
+ if (opcode >= VENDOR_SPECIFIC_CDB)
+ return false;
-/* attempt to guess cdb length if cdb_len==0 . No trailing linefeed. */
-static void print_opcode_name(unsigned char * cdbp, int cdb_len)
-{
- int sa, len, cdb0;
- const char * name;
+ if (opcode < ARRAY_SIZE(cdb_byte0_names))
+ *cdb_name = cdb_byte0_names[opcode];
- cdb0 = cdbp[0];
- switch(cdb0) {
- case VARIABLE_LENGTH_CMD:
- len = scsi_varlen_cdb_length(cdbp);
- if (len < 10) {
- printk("short variable length command, "
- "len=%d ext_len=%d", len, cdb_len);
+ for (sa_name_ptr = sa_names_arr; sa_name_ptr->arr; ++sa_name_ptr) {
+ if (sa_name_ptr->opcode == opcode) {
+ arr = sa_name_ptr->arr;
+ arr_sz = sa_name_ptr->arr_sz;
break;
}
- sa = (cdbp[8] << 8) + cdbp[9];
- name = get_sa_name(variable_length_arr, VARIABLE_LENGTH_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
-
- if ((cdb_len > 0) && (len != cdb_len))
- printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len);
-
- break;
- case MAINTENANCE_IN:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- case MAINTENANCE_OUT:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(maint_out_arr, MAINT_OUT_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- case SERVICE_ACTION_IN_12:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_in12_arr, SERV_IN12_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- case SERVICE_ACTION_OUT_12:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_out12_arr, SERV_OUT12_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- case SERVICE_ACTION_IN_16:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_in16_arr, SERV_IN16_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- case SERVICE_ACTION_OUT_16:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_out16_arr, SERV_OUT16_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- default:
- if (cdb0 < 0xc0) {
- name = cdb_byte0_names[cdb0];
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x (reserved)", cdb0);
- } else
- printk("cdb[0]=0x%x (vendor)", cdb0);
- break;
}
-}
+ if (!arr)
+ return false;
-#else /* ifndef CONFIG_SCSI_CONSTANTS */
-
-static void print_opcode_name(unsigned char * cdbp, int cdb_len)
-{
- int sa, len, cdb0;
-
- cdb0 = cdbp[0];
- switch(cdb0) {
- case VARIABLE_LENGTH_CMD:
- len = scsi_varlen_cdb_length(cdbp);
- if (len < 10) {
- printk("short opcode=0x%x command, len=%d "
- "ext_len=%d", cdb0, len, cdb_len);
+ for (k = 0; k < arr_sz; ++k, ++arr) {
+ if (service_action == arr->value)
break;
- }
- sa = (cdbp[8] << 8) + cdbp[9];
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- if (len != cdb_len)
- printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len);
- break;
- case MAINTENANCE_IN:
- case MAINTENANCE_OUT:
- case SERVICE_ACTION_IN_12:
- case SERVICE_ACTION_OUT_12:
- case SERVICE_ACTION_IN_16:
- case SERVICE_ACTION_OUT_16:
- sa = cdbp[1] & 0x1f;
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- default:
- if (cdb0 < 0xc0)
- printk("cdb[0]=0x%x", cdb0);
- else
- printk("cdb[0]=0x%x (vendor)", cdb0);
- break;
}
-}
-#endif
-
-void __scsi_print_command(unsigned char *cdb)
-{
- int k, len;
+ if (k < arr_sz)
+ *sa_name = arr->name;
- print_opcode_name(cdb, 0);
- len = scsi_command_size(cdb);
- /* print out all bytes in cdb */
- for (k = 0; k < len; ++k)
- printk(" %02x", cdb[k]);
- printk("\n");
+ return true;
}
-EXPORT_SYMBOL(__scsi_print_command);
-
-void scsi_print_command(struct scsi_cmnd *cmd)
-{
- int k;
-
- if (cmd->cmnd == NULL)
- return;
-
- scmd_printk(KERN_INFO, cmd, "CDB: ");
- print_opcode_name(cmd->cmnd, cmd->cmd_len);
-
- /* print out all bytes in cdb */
- printk(":");
- for (k = 0; k < cmd->cmd_len; ++k)
- printk(" %02x", cmd->cmnd[k]);
- printk("\n");
-}
-EXPORT_SYMBOL(scsi_print_command);
-
-/**
- * scsi_print_status - print scsi status description
- * @scsi_status: scsi status value
- *
- * If the status is recognized, the description is printed.
- * Otherwise "Unknown status" is output. No trailing space.
- * If CONFIG_SCSI_CONSTANTS is not set, then print status in hex
- * (e.g. "0x2" for Check Condition).
- **/
-void
-scsi_print_status(unsigned char scsi_status) {
-#ifdef CONFIG_SCSI_CONSTANTS
- const char * ccp;
-
- switch (scsi_status) {
- case 0: ccp = "Good"; break;
- case 0x2: ccp = "Check Condition"; break;
- case 0x4: ccp = "Condition Met"; break;
- case 0x8: ccp = "Busy"; break;
- case 0x10: ccp = "Intermediate"; break;
- case 0x14: ccp = "Intermediate-Condition Met"; break;
- case 0x18: ccp = "Reservation Conflict"; break;
- case 0x22: ccp = "Command Terminated"; break; /* obsolete */
- case 0x28: ccp = "Task set Full"; break; /* was: Queue Full */
- case 0x30: ccp = "ACA Active"; break;
- case 0x40: ccp = "Task Aborted"; break;
- default: ccp = "Unknown status";
- }
- printk(KERN_INFO "%s", ccp);
-#else
- printk(KERN_INFO "0x%0x", scsi_status);
-#endif
-}
-EXPORT_SYMBOL(scsi_print_status);
-
-#ifdef CONFIG_SCSI_CONSTANTS
struct error_info {
unsigned short code12; /* 0x0302 looks better than 0x03,0x02 */
- const char * text;
+ unsigned short size;
};
/*
- * The canonical list of T10 Additional Sense Codes is available at:
- * http://www.t10.org/lists/asc-num.txt
+ * There are 700+ entries in this table. To save space, we don't store
+ * (code, pointer) pairs, which would make sizeof(struct
+ * error_info)==16 on 64 bits. Rather, the second element just stores
+ * the size (including \0) of the corresponding string, and we use the
+ * sum of these to get the appropriate offset into additional_text
+ * defined below. This approach saves 12 bytes per entry.
*/
static const struct error_info additional[] =
{
- {0x0000, "No additional sense information"},
- {0x0001, "Filemark detected"},
- {0x0002, "End-of-partition/medium detected"},
- {0x0003, "Setmark detected"},
- {0x0004, "Beginning-of-partition/medium detected"},
- {0x0005, "End-of-data detected"},
- {0x0006, "I/O process terminated"},
- {0x0007, "Programmable early warning detected"},
- {0x0011, "Audio play operation in progress"},
- {0x0012, "Audio play operation paused"},
- {0x0013, "Audio play operation successfully completed"},
- {0x0014, "Audio play operation stopped due to error"},
- {0x0015, "No current audio status to return"},
- {0x0016, "Operation in progress"},
- {0x0017, "Cleaning requested"},
- {0x0018, "Erase operation in progress"},
- {0x0019, "Locate operation in progress"},
- {0x001A, "Rewind operation in progress"},
- {0x001B, "Set capacity operation in progress"},
- {0x001C, "Verify operation in progress"},
- {0x001D, "ATA pass through information available"},
- {0x001E, "Conflicting SA creation request"},
-
- {0x0100, "No index/sector signal"},
-
- {0x0200, "No seek complete"},
-
- {0x0300, "Peripheral device write fault"},
- {0x0301, "No write current"},
- {0x0302, "Excessive write errors"},
-
- {0x0400, "Logical unit not ready, cause not reportable"},
- {0x0401, "Logical unit is in process of becoming ready"},
- {0x0402, "Logical unit not ready, initializing command required"},
- {0x0403, "Logical unit not ready, manual intervention required"},
- {0x0404, "Logical unit not ready, format in progress"},
- {0x0405, "Logical unit not ready, rebuild in progress"},
- {0x0406, "Logical unit not ready, recalculation in progress"},
- {0x0407, "Logical unit not ready, operation in progress"},
- {0x0408, "Logical unit not ready, long write in progress"},
- {0x0409, "Logical unit not ready, self-test in progress"},
- {0x040A, "Logical unit not accessible, asymmetric access state "
- "transition"},
- {0x040B, "Logical unit not accessible, target port in standby state"},
- {0x040C, "Logical unit not accessible, target port in unavailable "
- "state"},
- {0x040D, "Logical unit not ready, structure check required"},
- {0x0410, "Logical unit not ready, auxiliary memory not accessible"},
- {0x0411, "Logical unit not ready, notify (enable spinup) required"},
- {0x0412, "Logical unit not ready, offline"},
- {0x0413, "Logical unit not ready, SA creation in progress"},
- {0x0414, "Logical unit not ready, space allocation in progress"},
-
- {0x0500, "Logical unit does not respond to selection"},
-
- {0x0600, "No reference position found"},
-
- {0x0700, "Multiple peripheral devices selected"},
-
- {0x0800, "Logical unit communication failure"},
- {0x0801, "Logical unit communication time-out"},
- {0x0802, "Logical unit communication parity error"},
- {0x0803, "Logical unit communication CRC error (Ultra-DMA/32)"},
- {0x0804, "Unreachable copy target"},
-
- {0x0900, "Track following error"},
- {0x0901, "Tracking servo failure"},
- {0x0902, "Focus servo failure"},
- {0x0903, "Spindle servo failure"},
- {0x0904, "Head select fault"},
-
- {0x0A00, "Error log overflow"},
-
- {0x0B00, "Warning"},
- {0x0B01, "Warning - specified temperature exceeded"},
- {0x0B02, "Warning - enclosure degraded"},
- {0x0B03, "Warning - background self-test failed"},
- {0x0B04, "Warning - background pre-scan detected medium error"},
- {0x0B05, "Warning - background medium scan detected medium error"},
- {0x0B06, "Warning - non-volatile cache now volatile"},
- {0x0B07, "Warning - degraded power to non-volatile cache"},
- {0x0B08, "Warning - power loss expected"},
-
- {0x0C00, "Write error"},
- {0x0C01, "Write error - recovered with auto reallocation"},
- {0x0C02, "Write error - auto reallocation failed"},
- {0x0C03, "Write error - recommend reassignment"},
- {0x0C04, "Compression check miscompare error"},
- {0x0C05, "Data expansion occurred during compression"},
- {0x0C06, "Block not compressible"},
- {0x0C07, "Write error - recovery needed"},
- {0x0C08, "Write error - recovery failed"},
- {0x0C09, "Write error - loss of streaming"},
- {0x0C0A, "Write error - padding blocks added"},
- {0x0C0B, "Auxiliary memory write error"},
- {0x0C0C, "Write error - unexpected unsolicited data"},
- {0x0C0D, "Write error - not enough unsolicited data"},
- {0x0C0F, "Defects in error window"},
-
- {0x0D00, "Error detected by third party temporary initiator"},
- {0x0D01, "Third party device failure"},
- {0x0D02, "Copy target device not reachable"},
- {0x0D03, "Incorrect copy target device type"},
- {0x0D04, "Copy target device data underrun"},
- {0x0D05, "Copy target device data overrun"},
-
- {0x0E00, "Invalid information unit"},
- {0x0E01, "Information unit too short"},
- {0x0E02, "Information unit too long"},
- {0x0E03, "Invalid field in command information unit"},
-
- {0x1000, "Id CRC or ECC error"},
- {0x1001, "Logical block guard check failed"},
- {0x1002, "Logical block application tag check failed"},
- {0x1003, "Logical block reference tag check failed"},
-
- {0x1100, "Unrecovered read error"},
- {0x1101, "Read retries exhausted"},
- {0x1102, "Error too long to correct"},
- {0x1103, "Multiple read errors"},
- {0x1104, "Unrecovered read error - auto reallocate failed"},
- {0x1105, "L-EC uncorrectable error"},
- {0x1106, "CIRC unrecovered error"},
- {0x1107, "Data re-synchronization error"},
- {0x1108, "Incomplete block read"},
- {0x1109, "No gap found"},
- {0x110A, "Miscorrected error"},
- {0x110B, "Unrecovered read error - recommend reassignment"},
- {0x110C, "Unrecovered read error - recommend rewrite the data"},
- {0x110D, "De-compression CRC error"},
- {0x110E, "Cannot decompress using declared algorithm"},
- {0x110F, "Error reading UPC/EAN number"},
- {0x1110, "Error reading ISRC number"},
- {0x1111, "Read error - loss of streaming"},
- {0x1112, "Auxiliary memory read error"},
- {0x1113, "Read error - failed retransmission request"},
- {0x1114, "Read error - lba marked bad by application client"},
-
- {0x1200, "Address mark not found for id field"},
-
- {0x1300, "Address mark not found for data field"},
-
- {0x1400, "Recorded entity not found"},
- {0x1401, "Record not found"},
- {0x1402, "Filemark or setmark not found"},
- {0x1403, "End-of-data not found"},
- {0x1404, "Block sequence error"},
- {0x1405, "Record not found - recommend reassignment"},
- {0x1406, "Record not found - data auto-reallocated"},
- {0x1407, "Locate operation failure"},
-
- {0x1500, "Random positioning error"},
- {0x1501, "Mechanical positioning error"},
- {0x1502, "Positioning error detected by read of medium"},
-
- {0x1600, "Data synchronization mark error"},
- {0x1601, "Data sync error - data rewritten"},
- {0x1602, "Data sync error - recommend rewrite"},
- {0x1603, "Data sync error - data auto-reallocated"},
- {0x1604, "Data sync error - recommend reassignment"},
-
- {0x1700, "Recovered data with no error correction applied"},
- {0x1701, "Recovered data with retries"},
- {0x1702, "Recovered data with positive head offset"},
- {0x1703, "Recovered data with negative head offset"},
- {0x1704, "Recovered data with retries and/or circ applied"},
- {0x1705, "Recovered data using previous sector id"},
- {0x1706, "Recovered data without ECC - data auto-reallocated"},
- {0x1707, "Recovered data without ECC - recommend reassignment"},
- {0x1708, "Recovered data without ECC - recommend rewrite"},
- {0x1709, "Recovered data without ECC - data rewritten"},
-
- {0x1800, "Recovered data with error correction applied"},
- {0x1801, "Recovered data with error corr. & retries applied"},
- {0x1802, "Recovered data - data auto-reallocated"},
- {0x1803, "Recovered data with CIRC"},
- {0x1804, "Recovered data with L-EC"},
- {0x1805, "Recovered data - recommend reassignment"},
- {0x1806, "Recovered data - recommend rewrite"},
- {0x1807, "Recovered data with ECC - data rewritten"},
- {0x1808, "Recovered data with linking"},
-
- {0x1900, "Defect list error"},
- {0x1901, "Defect list not available"},
- {0x1902, "Defect list error in primary list"},
- {0x1903, "Defect list error in grown list"},
-
- {0x1A00, "Parameter list length error"},
-
- {0x1B00, "Synchronous data transfer error"},
-
- {0x1C00, "Defect list not found"},
- {0x1C01, "Primary defect list not found"},
- {0x1C02, "Grown defect list not found"},
-
- {0x1D00, "Miscompare during verify operation"},
- {0x1D01, "Miscompare verify of unmapped LBA"},
-
- {0x1E00, "Recovered id with ECC correction"},
-
- {0x1F00, "Partial defect list transfer"},
-
- {0x2000, "Invalid command operation code"},
- {0x2001, "Access denied - initiator pending-enrolled"},
- {0x2002, "Access denied - no access rights"},
- {0x2003, "Access denied - invalid mgmt id key"},
- {0x2004, "Illegal command while in write capable state"},
- {0x2005, "Obsolete"},
- {0x2006, "Illegal command while in explicit address mode"},
- {0x2007, "Illegal command while in implicit address mode"},
- {0x2008, "Access denied - enrollment conflict"},
- {0x2009, "Access denied - invalid LU identifier"},
- {0x200A, "Access denied - invalid proxy token"},
- {0x200B, "Access denied - ACL LUN conflict"},
-
- {0x2100, "Logical block address out of range"},
- {0x2101, "Invalid element address"},
- {0x2102, "Invalid address for write"},
- {0x2103, "Invalid write crossing layer jump"},
-
- {0x2200, "Illegal function (use 20 00, 24 00, or 26 00)"},
-
- {0x2400, "Invalid field in cdb"},
- {0x2401, "CDB decryption error"},
- {0x2402, "Obsolete"},
- {0x2403, "Obsolete"},
- {0x2404, "Security audit value frozen"},
- {0x2405, "Security working key frozen"},
- {0x2406, "Nonce not unique"},
- {0x2407, "Nonce timestamp out of range"},
- {0x2408, "Invalid XCDB"},
-
- {0x2500, "Logical unit not supported"},
-
- {0x2600, "Invalid field in parameter list"},
- {0x2601, "Parameter not supported"},
- {0x2602, "Parameter value invalid"},
- {0x2603, "Threshold parameters not supported"},
- {0x2604, "Invalid release of persistent reservation"},
- {0x2605, "Data decryption error"},
- {0x2606, "Too many target descriptors"},
- {0x2607, "Unsupported target descriptor type code"},
- {0x2608, "Too many segment descriptors"},
- {0x2609, "Unsupported segment descriptor type code"},
- {0x260A, "Unexpected inexact segment"},
- {0x260B, "Inline data length exceeded"},
- {0x260C, "Invalid operation for copy source or destination"},
- {0x260D, "Copy segment granularity violation"},
- {0x260E, "Invalid parameter while port is enabled"},
- {0x260F, "Invalid data-out buffer integrity check value"},
- {0x2610, "Data decryption key fail limit reached"},
- {0x2611, "Incomplete key-associated data set"},
- {0x2612, "Vendor specific key reference not found"},
-
- {0x2700, "Write protected"},
- {0x2701, "Hardware write protected"},
- {0x2702, "Logical unit software write protected"},
- {0x2703, "Associated write protect"},
- {0x2704, "Persistent write protect"},
- {0x2705, "Permanent write protect"},
- {0x2706, "Conditional write protect"},
- {0x2707, "Space allocation failed write protect"},
-
- {0x2800, "Not ready to ready change, medium may have changed"},
- {0x2801, "Import or export element accessed"},
- {0x2802, "Format-layer may have changed"},
- {0x2803, "Import/export element accessed, medium changed"},
-
- {0x2900, "Power on, reset, or bus device reset occurred"},
- {0x2901, "Power on occurred"},
- {0x2902, "Scsi bus reset occurred"},
- {0x2903, "Bus device reset function occurred"},
- {0x2904, "Device internal reset"},
- {0x2905, "Transceiver mode changed to single-ended"},
- {0x2906, "Transceiver mode changed to lvd"},
- {0x2907, "I_T nexus loss occurred"},
-
- {0x2A00, "Parameters changed"},
- {0x2A01, "Mode parameters changed"},
- {0x2A02, "Log parameters changed"},
- {0x2A03, "Reservations preempted"},
- {0x2A04, "Reservations released"},
- {0x2A05, "Registrations preempted"},
- {0x2A06, "Asymmetric access state changed"},
- {0x2A07, "Implicit asymmetric access state transition failed"},
- {0x2A08, "Priority changed"},
- {0x2A09, "Capacity data has changed"},
- {0x2A0A, "Error history I_T nexus cleared"},
- {0x2A0B, "Error history snapshot released"},
- {0x2A0C, "Error recovery attributes have changed"},
- {0x2A0D, "Data encryption capabilities changed"},
- {0x2A10, "Timestamp changed"},
- {0x2A11, "Data encryption parameters changed by another i_t nexus"},
- {0x2A12, "Data encryption parameters changed by vendor specific "
- "event"},
- {0x2A13, "Data encryption key instance counter has changed"},
- {0x2A14, "SA creation capabilities data has changed"},
-
- {0x2B00, "Copy cannot execute since host cannot disconnect"},
-
- {0x2C00, "Command sequence error"},
- {0x2C01, "Too many windows specified"},
- {0x2C02, "Invalid combination of windows specified"},
- {0x2C03, "Current program area is not empty"},
- {0x2C04, "Current program area is empty"},
- {0x2C05, "Illegal power condition request"},
- {0x2C06, "Persistent prevent conflict"},
- {0x2C07, "Previous busy status"},
- {0x2C08, "Previous task set full status"},
- {0x2C09, "Previous reservation conflict status"},
- {0x2C0A, "Partition or collection contains user objects"},
- {0x2C0B, "Not reserved"},
-
- {0x2D00, "Overwrite error on update in place"},
-
- {0x2E00, "Insufficient time for operation"},
-
- {0x2F00, "Commands cleared by another initiator"},
- {0x2F01, "Commands cleared by power loss notification"},
- {0x2F02, "Commands cleared by device server"},
-
- {0x3000, "Incompatible medium installed"},
- {0x3001, "Cannot read medium - unknown format"},
- {0x3002, "Cannot read medium - incompatible format"},
- {0x3003, "Cleaning cartridge installed"},
- {0x3004, "Cannot write medium - unknown format"},
- {0x3005, "Cannot write medium - incompatible format"},
- {0x3006, "Cannot format medium - incompatible medium"},
- {0x3007, "Cleaning failure"},
- {0x3008, "Cannot write - application code mismatch"},
- {0x3009, "Current session not fixated for append"},
- {0x300A, "Cleaning request rejected"},
- {0x300C, "WORM medium - overwrite attempted"},
- {0x300D, "WORM medium - integrity check"},
- {0x3010, "Medium not formatted"},
- {0x3011, "Incompatible volume type"},
- {0x3012, "Incompatible volume qualifier"},
-
- {0x3100, "Medium format corrupted"},
- {0x3101, "Format command failed"},
- {0x3102, "Zoned formatting failed due to spare linking"},
-
- {0x3200, "No defect spare location available"},
- {0x3201, "Defect list update failure"},
-
- {0x3300, "Tape length error"},
-
- {0x3400, "Enclosure failure"},
-
- {0x3500, "Enclosure services failure"},
- {0x3501, "Unsupported enclosure function"},
- {0x3502, "Enclosure services unavailable"},
- {0x3503, "Enclosure services transfer failure"},
- {0x3504, "Enclosure services transfer refused"},
- {0x3505, "Enclosure services checksum error"},
-
- {0x3600, "Ribbon, ink, or toner failure"},
-
- {0x3700, "Rounded parameter"},
-
- {0x3800, "Event status notification"},
- {0x3802, "Esn - power management class event"},
- {0x3804, "Esn - media class event"},
- {0x3806, "Esn - device busy class event"},
- {0x3807, "Thin Provisioning soft threshold reached"},
-
- {0x3900, "Saving parameters not supported"},
-
- {0x3A00, "Medium not present"},
- {0x3A01, "Medium not present - tray closed"},
- {0x3A02, "Medium not present - tray open"},
- {0x3A03, "Medium not present - loadable"},
- {0x3A04, "Medium not present - medium auxiliary memory accessible"},
-
- {0x3B00, "Sequential positioning error"},
- {0x3B01, "Tape position error at beginning-of-medium"},
- {0x3B02, "Tape position error at end-of-medium"},
- {0x3B03, "Tape or electronic vertical forms unit not ready"},
- {0x3B04, "Slew failure"},
- {0x3B05, "Paper jam"},
- {0x3B06, "Failed to sense top-of-form"},
- {0x3B07, "Failed to sense bottom-of-form"},
- {0x3B08, "Reposition error"},
- {0x3B09, "Read past end of medium"},
- {0x3B0A, "Read past beginning of medium"},
- {0x3B0B, "Position past end of medium"},
- {0x3B0C, "Position past beginning of medium"},
- {0x3B0D, "Medium destination element full"},
- {0x3B0E, "Medium source element empty"},
- {0x3B0F, "End of medium reached"},
- {0x3B11, "Medium magazine not accessible"},
- {0x3B12, "Medium magazine removed"},
- {0x3B13, "Medium magazine inserted"},
- {0x3B14, "Medium magazine locked"},
- {0x3B15, "Medium magazine unlocked"},
- {0x3B16, "Mechanical positioning or changer error"},
- {0x3B17, "Read past end of user object"},
- {0x3B18, "Element disabled"},
- {0x3B19, "Element enabled"},
- {0x3B1A, "Data transfer device removed"},
- {0x3B1B, "Data transfer device inserted"},
-
- {0x3D00, "Invalid bits in identify message"},
-
- {0x3E00, "Logical unit has not self-configured yet"},
- {0x3E01, "Logical unit failure"},
- {0x3E02, "Timeout on logical unit"},
- {0x3E03, "Logical unit failed self-test"},
- {0x3E04, "Logical unit unable to update self-test log"},
-
- {0x3F00, "Target operating conditions have changed"},
- {0x3F01, "Microcode has been changed"},
- {0x3F02, "Changed operating definition"},
- {0x3F03, "Inquiry data has changed"},
- {0x3F04, "Component device attached"},
- {0x3F05, "Device identifier changed"},
- {0x3F06, "Redundancy group created or modified"},
- {0x3F07, "Redundancy group deleted"},
- {0x3F08, "Spare created or modified"},
- {0x3F09, "Spare deleted"},
- {0x3F0A, "Volume set created or modified"},
- {0x3F0B, "Volume set deleted"},
- {0x3F0C, "Volume set deassigned"},
- {0x3F0D, "Volume set reassigned"},
- {0x3F0E, "Reported luns data has changed"},
- {0x3F0F, "Echo buffer overwritten"},
- {0x3F10, "Medium loadable"},
- {0x3F11, "Medium auxiliary memory accessible"},
- {0x3F12, "iSCSI IP address added"},
- {0x3F13, "iSCSI IP address removed"},
- {0x3F14, "iSCSI IP address changed"},
-/*
- * {0x40NN, "Ram failure"},
- * {0x40NN, "Diagnostic failure on component nn"},
- * {0x41NN, "Data path failure"},
- * {0x42NN, "Power-on or self-test failure"},
- */
- {0x4300, "Message error"},
-
- {0x4400, "Internal target failure"},
- {0x4471, "ATA device failed set features"},
-
- {0x4500, "Select or reselect failure"},
-
- {0x4600, "Unsuccessful soft reset"},
-
- {0x4700, "Scsi parity error"},
- {0x4701, "Data phase CRC error detected"},
- {0x4702, "Scsi parity error detected during st data phase"},
- {0x4703, "Information unit iuCRC error detected"},
- {0x4704, "Asynchronous information protection error detected"},
- {0x4705, "Protocol service CRC error"},
- {0x4706, "Phy test function in progress"},
- {0x477f, "Some commands cleared by iSCSI Protocol event"},
-
- {0x4800, "Initiator detected error message received"},
-
- {0x4900, "Invalid message error"},
-
- {0x4A00, "Command phase error"},
-
- {0x4B00, "Data phase error"},
- {0x4B01, "Invalid target port transfer tag received"},
- {0x4B02, "Too much write data"},
- {0x4B03, "Ack/nak timeout"},
- {0x4B04, "Nak received"},
- {0x4B05, "Data offset error"},
- {0x4B06, "Initiator response timeout"},
-
- {0x4C00, "Logical unit failed self-configuration"},
-/*
- * {0x4DNN, "Tagged overlapped commands (nn = queue tag)"},
- */
- {0x4E00, "Overlapped commands attempted"},
-
- {0x5000, "Write append error"},
- {0x5001, "Write append position error"},
- {0x5002, "Position error related to timing"},
-
- {0x5100, "Erase failure"},
- {0x5101, "Erase failure - incomplete erase operation detected"},
-
- {0x5200, "Cartridge fault"},
-
- {0x5300, "Media load or eject failed"},
- {0x5301, "Unload tape failure"},
- {0x5302, "Medium removal prevented"},
- {0x5303, "Medium removal prevented by data transfer element"},
- {0x5304, "Medium thread or unthread failure"},
-
- {0x5400, "Scsi to host system interface failure"},
-
- {0x5500, "System resource failure"},
- {0x5501, "System buffer full"},
- {0x5502, "Insufficient reservation resources"},
- {0x5503, "Insufficient resources"},
- {0x5504, "Insufficient registration resources"},
- {0x5505, "Insufficient access control resources"},
- {0x5506, "Auxiliary memory out of space"},
- {0x5507, "Quota error"},
- {0x5508, "Maximum number of supplemental decryption keys exceeded"},
- {0x5509, "Medium auxiliary memory not accessible"},
- {0x550A, "Data currently unavailable"},
-
- {0x5700, "Unable to recover table-of-contents"},
-
- {0x5800, "Generation does not exist"},
-
- {0x5900, "Updated block read"},
-
- {0x5A00, "Operator request or state change input"},
- {0x5A01, "Operator medium removal request"},
- {0x5A02, "Operator selected write protect"},
- {0x5A03, "Operator selected write permit"},
-
- {0x5B00, "Log exception"},
- {0x5B01, "Threshold condition met"},
- {0x5B02, "Log counter at maximum"},
- {0x5B03, "Log list codes exhausted"},
-
- {0x5C00, "Rpl status change"},
- {0x5C01, "Spindles synchronized"},
- {0x5C02, "Spindles not synchronized"},
-
- {0x5D00, "Failure prediction threshold exceeded"},
- {0x5D01, "Media failure prediction threshold exceeded"},
- {0x5D02, "Logical unit failure prediction threshold exceeded"},
- {0x5D03, "Spare area exhaustion prediction threshold exceeded"},
- {0x5D10, "Hardware impending failure general hard drive failure"},
- {0x5D11, "Hardware impending failure drive error rate too high"},
- {0x5D12, "Hardware impending failure data error rate too high"},
- {0x5D13, "Hardware impending failure seek error rate too high"},
- {0x5D14, "Hardware impending failure too many block reassigns"},
- {0x5D15, "Hardware impending failure access times too high"},
- {0x5D16, "Hardware impending failure start unit times too high"},
- {0x5D17, "Hardware impending failure channel parametrics"},
- {0x5D18, "Hardware impending failure controller detected"},
- {0x5D19, "Hardware impending failure throughput performance"},
- {0x5D1A, "Hardware impending failure seek time performance"},
- {0x5D1B, "Hardware impending failure spin-up retry count"},
- {0x5D1C, "Hardware impending failure drive calibration retry count"},
- {0x5D20, "Controller impending failure general hard drive failure"},
- {0x5D21, "Controller impending failure drive error rate too high"},
- {0x5D22, "Controller impending failure data error rate too high"},
- {0x5D23, "Controller impending failure seek error rate too high"},
- {0x5D24, "Controller impending failure too many block reassigns"},
- {0x5D25, "Controller impending failure access times too high"},
- {0x5D26, "Controller impending failure start unit times too high"},
- {0x5D27, "Controller impending failure channel parametrics"},
- {0x5D28, "Controller impending failure controller detected"},
- {0x5D29, "Controller impending failure throughput performance"},
- {0x5D2A, "Controller impending failure seek time performance"},
- {0x5D2B, "Controller impending failure spin-up retry count"},
- {0x5D2C, "Controller impending failure drive calibration retry count"},
- {0x5D30, "Data channel impending failure general hard drive failure"},
- {0x5D31, "Data channel impending failure drive error rate too high"},
- {0x5D32, "Data channel impending failure data error rate too high"},
- {0x5D33, "Data channel impending failure seek error rate too high"},
- {0x5D34, "Data channel impending failure too many block reassigns"},
- {0x5D35, "Data channel impending failure access times too high"},
- {0x5D36, "Data channel impending failure start unit times too high"},
- {0x5D37, "Data channel impending failure channel parametrics"},
- {0x5D38, "Data channel impending failure controller detected"},
- {0x5D39, "Data channel impending failure throughput performance"},
- {0x5D3A, "Data channel impending failure seek time performance"},
- {0x5D3B, "Data channel impending failure spin-up retry count"},
- {0x5D3C, "Data channel impending failure drive calibration retry "
- "count"},
- {0x5D40, "Servo impending failure general hard drive failure"},
- {0x5D41, "Servo impending failure drive error rate too high"},
- {0x5D42, "Servo impending failure data error rate too high"},
- {0x5D43, "Servo impending failure seek error rate too high"},
- {0x5D44, "Servo impending failure too many block reassigns"},
- {0x5D45, "Servo impending failure access times too high"},
- {0x5D46, "Servo impending failure start unit times too high"},
- {0x5D47, "Servo impending failure channel parametrics"},
- {0x5D48, "Servo impending failure controller detected"},
- {0x5D49, "Servo impending failure throughput performance"},
- {0x5D4A, "Servo impending failure seek time performance"},
- {0x5D4B, "Servo impending failure spin-up retry count"},
- {0x5D4C, "Servo impending failure drive calibration retry count"},
- {0x5D50, "Spindle impending failure general hard drive failure"},
- {0x5D51, "Spindle impending failure drive error rate too high"},
- {0x5D52, "Spindle impending failure data error rate too high"},
- {0x5D53, "Spindle impending failure seek error rate too high"},
- {0x5D54, "Spindle impending failure too many block reassigns"},
- {0x5D55, "Spindle impending failure access times too high"},
- {0x5D56, "Spindle impending failure start unit times too high"},
- {0x5D57, "Spindle impending failure channel parametrics"},
- {0x5D58, "Spindle impending failure controller detected"},
- {0x5D59, "Spindle impending failure throughput performance"},
- {0x5D5A, "Spindle impending failure seek time performance"},
- {0x5D5B, "Spindle impending failure spin-up retry count"},
- {0x5D5C, "Spindle impending failure drive calibration retry count"},
- {0x5D60, "Firmware impending failure general hard drive failure"},
- {0x5D61, "Firmware impending failure drive error rate too high"},
- {0x5D62, "Firmware impending failure data error rate too high"},
- {0x5D63, "Firmware impending failure seek error rate too high"},
- {0x5D64, "Firmware impending failure too many block reassigns"},
- {0x5D65, "Firmware impending failure access times too high"},
- {0x5D66, "Firmware impending failure start unit times too high"},
- {0x5D67, "Firmware impending failure channel parametrics"},
- {0x5D68, "Firmware impending failure controller detected"},
- {0x5D69, "Firmware impending failure throughput performance"},
- {0x5D6A, "Firmware impending failure seek time performance"},
- {0x5D6B, "Firmware impending failure spin-up retry count"},
- {0x5D6C, "Firmware impending failure drive calibration retry count"},
- {0x5DFF, "Failure prediction threshold exceeded (false)"},
-
- {0x5E00, "Low power condition on"},
- {0x5E01, "Idle condition activated by timer"},
- {0x5E02, "Standby condition activated by timer"},
- {0x5E03, "Idle condition activated by command"},
- {0x5E04, "Standby condition activated by command"},
- {0x5E05, "Idle_b condition activated by timer"},
- {0x5E06, "Idle_b condition activated by command"},
- {0x5E07, "Idle_c condition activated by timer"},
- {0x5E08, "Idle_c condition activated by command"},
- {0x5E09, "Standby_y condition activated by timer"},
- {0x5E0A, "Standby_y condition activated by command"},
- {0x5E41, "Power state change to active"},
- {0x5E42, "Power state change to idle"},
- {0x5E43, "Power state change to standby"},
- {0x5E45, "Power state change to sleep"},
- {0x5E47, "Power state change to device control"},
-
- {0x6000, "Lamp failure"},
-
- {0x6100, "Video acquisition error"},
- {0x6101, "Unable to acquire video"},
- {0x6102, "Out of focus"},
-
- {0x6200, "Scan head positioning error"},
-
- {0x6300, "End of user area encountered on this track"},
- {0x6301, "Packet does not fit in available space"},
-
- {0x6400, "Illegal mode for this track"},
- {0x6401, "Invalid packet size"},
-
- {0x6500, "Voltage fault"},
-
- {0x6600, "Automatic document feeder cover up"},
- {0x6601, "Automatic document feeder lift up"},
- {0x6602, "Document jam in automatic document feeder"},
- {0x6603, "Document miss feed automatic in document feeder"},
-
- {0x6700, "Configuration failure"},
- {0x6701, "Configuration of incapable logical units failed"},
- {0x6702, "Add logical unit failed"},
- {0x6703, "Modification of logical unit failed"},
- {0x6704, "Exchange of logical unit failed"},
- {0x6705, "Remove of logical unit failed"},
- {0x6706, "Attachment of logical unit failed"},
- {0x6707, "Creation of logical unit failed"},
- {0x6708, "Assign failure occurred"},
- {0x6709, "Multiply assigned logical unit"},
- {0x670A, "Set target port groups command failed"},
- {0x670B, "ATA device feature not enabled"},
-
- {0x6800, "Logical unit not configured"},
-
- {0x6900, "Data loss on logical unit"},
- {0x6901, "Multiple logical unit failures"},
- {0x6902, "Parity/data mismatch"},
-
- {0x6A00, "Informational, refer to log"},
-
- {0x6B00, "State change has occurred"},
- {0x6B01, "Redundancy level got better"},
- {0x6B02, "Redundancy level got worse"},
-
- {0x6C00, "Rebuild failure occurred"},
-
- {0x6D00, "Recalculate failure occurred"},
-
- {0x6E00, "Command to logical unit failed"},
-
- {0x6F00, "Copy protection key exchange failure - authentication "
- "failure"},
- {0x6F01, "Copy protection key exchange failure - key not present"},
- {0x6F02, "Copy protection key exchange failure - key not established"},
- {0x6F03, "Read of scrambled sector without authentication"},
- {0x6F04, "Media region code is mismatched to logical unit region"},
- {0x6F05, "Drive region must be permanent/region reset count error"},
- {0x6F06, "Insufficient block count for binding nonce recording"},
- {0x6F07, "Conflict in binding nonce recording"},
-/*
- * {0x70NN, "Decompression exception short algorithm id of nn"},
- */
- {0x7100, "Decompression exception long algorithm id"},
-
- {0x7200, "Session fixation error"},
- {0x7201, "Session fixation error writing lead-in"},
- {0x7202, "Session fixation error writing lead-out"},
- {0x7203, "Session fixation error - incomplete track in session"},
- {0x7204, "Empty or partially written reserved track"},
- {0x7205, "No more track reservations allowed"},
- {0x7206, "RMZ extension is not allowed"},
- {0x7207, "No more test zone extensions are allowed"},
-
- {0x7300, "Cd control error"},
- {0x7301, "Power calibration area almost full"},
- {0x7302, "Power calibration area is full"},
- {0x7303, "Power calibration area error"},
- {0x7304, "Program memory area update failure"},
- {0x7305, "Program memory area is full"},
- {0x7306, "RMA/PMA is almost full"},
- {0x7310, "Current power calibration area almost full"},
- {0x7311, "Current power calibration area is full"},
- {0x7317, "RDZ is full"},
-
- {0x7400, "Security error"},
- {0x7401, "Unable to decrypt data"},
- {0x7402, "Unencrypted data encountered while decrypting"},
- {0x7403, "Incorrect data encryption key"},
- {0x7404, "Cryptographic integrity validation failed"},
- {0x7405, "Error decrypting data"},
- {0x7406, "Unknown signature verification key"},
- {0x7407, "Encryption parameters not useable"},
- {0x7408, "Digital signature validation failure"},
- {0x7409, "Encryption mode mismatch on read"},
- {0x740A, "Encrypted block not raw read enabled"},
- {0x740B, "Incorrect Encryption parameters"},
- {0x740C, "Unable to decrypt parameter list"},
- {0x740D, "Encryption algorithm disabled"},
- {0x7410, "SA creation parameter value invalid"},
- {0x7411, "SA creation parameter value rejected"},
- {0x7412, "Invalid SA usage"},
- {0x7421, "Data Encryption configuration prevented"},
- {0x7430, "SA creation parameter not supported"},
- {0x7440, "Authentication failed"},
- {0x7461, "External data encryption key manager access error"},
- {0x7462, "External data encryption key manager error"},
- {0x7463, "External data encryption key not found"},
- {0x7464, "External data encryption request not authorized"},
- {0x746E, "External data encryption control timeout"},
- {0x746F, "External data encryption control error"},
- {0x7471, "Logical unit access not authorized"},
- {0x7479, "Security conflict in translated device"},
-
- {0, NULL}
+#define SENSE_CODE(c, s) {c, sizeof(s)},
+#include "sense_codes.h"
+#undef SENSE_CODE
};
+static const char *additional_text =
+#define SENSE_CODE(c, s) s "\0"
+#include "sense_codes.h"
+#undef SENSE_CODE
+ ;
+
struct error_info2 {
unsigned char code1, code2_min, code2_max;
+ const char * str;
const char * fmt;
};
static const struct error_info2 additional2[] =
{
- {0x40, 0x00, 0x7f, "Ram failure (%x)"},
- {0x40, 0x80, 0xff, "Diagnostic failure on component (%x)"},
- {0x41, 0x00, 0xff, "Data path failure (%x)"},
- {0x42, 0x00, 0xff, "Power-on or self-test failure (%x)"},
- {0x4D, 0x00, 0xff, "Tagged overlapped commands (task tag %x)"},
- {0x70, 0x00, 0xff, "Decompression exception short algorithm id of %x"},
- {0, 0, 0, NULL}
+ {0x40, 0x00, 0x7f, "Ram failure", ""},
+ {0x40, 0x80, 0xff, "Diagnostic failure on component", ""},
+ {0x41, 0x00, 0xff, "Data path failure", ""},
+ {0x42, 0x00, 0xff, "Power-on or self-test failure", ""},
+ {0x4D, 0x00, 0xff, "Tagged overlapped commands", "task tag "},
+ {0x70, 0x00, 0xff, "Decompression exception", "short algorithm id of "},
+ {0, 0, 0, NULL, NULL}
};
/* description of the sense key values */
@@ -1185,20 +351,21 @@ static const char * const snstext[] = {
"Vendor Specific(9)",
"Copy Aborted", /* A: COPY or COMPARE was aborted */
"Aborted Command", /* B: The target aborted the command */
- "Equal", /* C: A SEARCH DATA command found data equal */
+ "Equal", /* C: A SEARCH DATA command found data equal,
+ reserved in SPC-4 rev 36 */
"Volume Overflow", /* D: Medium full with still data to be written */
"Miscompare", /* E: Source data and data on the medium
do not agree */
+ "Completed", /* F: command completed sense data reported,
+ may occur for successful command */
};
-#endif
/* Get sense key string or NULL if not available */
const char *
-scsi_sense_key_string(unsigned char key) {
-#ifdef CONFIG_SCSI_CONSTANTS
- if (key <= 0xE)
+scsi_sense_key_string(unsigned char key)
+{
+ if (key < ARRAY_SIZE(snstext))
return snstext[key];
-#endif
return NULL;
}
EXPORT_SYMBOL(scsi_sense_key_string);
@@ -1208,255 +375,70 @@ EXPORT_SYMBOL(scsi_sense_key_string);
* This string may contain a "%x" and should be printed with ascq as arg.
*/
const char *
-scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
-#ifdef CONFIG_SCSI_CONSTANTS
+scsi_extd_sense_format(unsigned char asc, unsigned char ascq, const char **fmt)
+{
int i;
unsigned short code = ((asc << 8) | ascq);
+ unsigned offset = 0;
- for (i = 0; additional[i].text; i++)
+ *fmt = NULL;
+ for (i = 0; i < ARRAY_SIZE(additional); i++) {
if (additional[i].code12 == code)
- return additional[i].text;
+ return additional_text + offset;
+ offset += additional[i].size;
+ }
for (i = 0; additional2[i].fmt; i++) {
if (additional2[i].code1 == asc &&
ascq >= additional2[i].code2_min &&
- ascq <= additional2[i].code2_max)
- return additional2[i].fmt;
+ ascq <= additional2[i].code2_max) {
+ *fmt = additional2[i].fmt;
+ return additional2[i].str;
+ }
}
-#endif
return NULL;
}
EXPORT_SYMBOL(scsi_extd_sense_format);
-void
-scsi_show_extd_sense(unsigned char asc, unsigned char ascq)
-{
- const char *extd_sense_fmt = scsi_extd_sense_format(asc, ascq);
-
- if (extd_sense_fmt) {
- if (strstr(extd_sense_fmt, "%x")) {
- printk("Add. Sense: ");
- printk(extd_sense_fmt, ascq);
- } else
- printk("Add. Sense: %s", extd_sense_fmt);
- } else {
- if (asc >= 0x80)
- printk("<<vendor>> ASC=0x%x ASCQ=0x%x", asc,
- ascq);
- if (ascq >= 0x80)
- printk("ASC=0x%x <<vendor>> ASCQ=0x%x", asc,
- ascq);
- else
- printk("ASC=0x%x ASCQ=0x%x", asc, ascq);
- }
-
- printk("\n");
-}
-EXPORT_SYMBOL(scsi_show_extd_sense);
-
-void
-scsi_show_sense_hdr(struct scsi_sense_hdr *sshdr)
-{
- const char *sense_txt;
-
- sense_txt = scsi_sense_key_string(sshdr->sense_key);
- if (sense_txt)
- printk("Sense Key : %s ", sense_txt);
- else
- printk("Sense Key : 0x%x ", sshdr->sense_key);
-
- printk("%s", scsi_sense_is_deferred(sshdr) ? "[deferred] " :
- "[current] ");
-
- if (sshdr->response_code >= 0x72)
- printk("[descriptor]");
-
- printk("\n");
-}
-EXPORT_SYMBOL(scsi_show_sense_hdr);
-
-/*
- * Print normalized SCSI sense header with a prefix.
- */
-void
-scsi_print_sense_hdr(const char *name, struct scsi_sense_hdr *sshdr)
-{
- printk(KERN_INFO "%s: ", name);
- scsi_show_sense_hdr(sshdr);
- printk(KERN_INFO "%s: ", name);
- scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
-}
-EXPORT_SYMBOL(scsi_print_sense_hdr);
-
-/*
- * Print normalized SCSI sense header with device information and a prefix.
- */
-void
-scsi_cmd_print_sense_hdr(struct scsi_cmnd *scmd, const char *desc,
- struct scsi_sense_hdr *sshdr)
-{
- scmd_printk(KERN_INFO, scmd, "%s: ", desc);
- scsi_show_sense_hdr(sshdr);
- scmd_printk(KERN_INFO, scmd, "%s: ", desc);
- scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
-}
-EXPORT_SYMBOL(scsi_cmd_print_sense_hdr);
-
-static void
-scsi_decode_sense_buffer(const unsigned char *sense_buffer, int sense_len,
- struct scsi_sense_hdr *sshdr)
-{
- int k, num, res;
-
- res = scsi_normalize_sense(sense_buffer, sense_len, sshdr);
- if (0 == res) {
- /* this may be SCSI-1 sense data */
- num = (sense_len < 32) ? sense_len : 32;
- printk("Unrecognized sense data (in hex):");
- for (k = 0; k < num; ++k) {
- if (0 == (k % 16)) {
- printk("\n");
- printk(KERN_INFO " ");
- }
- printk("%02x ", sense_buffer[k]);
- }
- printk("\n");
- return;
- }
-}
-
-static void
-scsi_decode_sense_extras(const unsigned char *sense_buffer, int sense_len,
- struct scsi_sense_hdr *sshdr)
-{
- int k, num, res;
-
- if (sshdr->response_code < 0x72)
- {
- /* only decode extras for "fixed" format now */
- char buff[80];
- int blen, fixed_valid;
- unsigned int info;
-
- fixed_valid = sense_buffer[0] & 0x80;
- info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) |
- (sense_buffer[5] << 8) | sense_buffer[6]);
- res = 0;
- memset(buff, 0, sizeof(buff));
- blen = sizeof(buff) - 1;
- if (fixed_valid)
- res += snprintf(buff + res, blen - res,
- "Info fld=0x%x", info);
- if (sense_buffer[2] & 0x80) {
- /* current command has read a filemark */
- if (res > 0)
- res += snprintf(buff + res, blen - res, ", ");
- res += snprintf(buff + res, blen - res, "FMK");
- }
- if (sense_buffer[2] & 0x40) {
- /* end-of-medium condition exists */
- if (res > 0)
- res += snprintf(buff + res, blen - res, ", ");
- res += snprintf(buff + res, blen - res, "EOM");
- }
- if (sense_buffer[2] & 0x20) {
- /* incorrect block length requested */
- if (res > 0)
- res += snprintf(buff + res, blen - res, ", ");
- res += snprintf(buff + res, blen - res, "ILI");
- }
- if (res > 0)
- printk("%s\n", buff);
- } else if (sshdr->additional_length > 0) {
- /* descriptor format with sense descriptors */
- num = 8 + sshdr->additional_length;
- num = (sense_len < num) ? sense_len : num;
- printk("Descriptor sense data with sense descriptors "
- "(in hex):");
- for (k = 0; k < num; ++k) {
- if (0 == (k % 16)) {
- printk("\n");
- printk(KERN_INFO " ");
- }
- printk("%02x ", sense_buffer[k]);
- }
-
- printk("\n");
- }
-
-}
-
-/* Normalize and print sense buffer with name prefix */
-void __scsi_print_sense(const char *name, const unsigned char *sense_buffer,
- int sense_len)
-{
- struct scsi_sense_hdr sshdr;
-
- printk(KERN_INFO "%s: ", name);
- scsi_decode_sense_buffer(sense_buffer, sense_len, &sshdr);
- scsi_show_sense_hdr(&sshdr);
- scsi_decode_sense_extras(sense_buffer, sense_len, &sshdr);
- printk(KERN_INFO "%s: ", name);
- scsi_show_extd_sense(sshdr.asc, sshdr.ascq);
-}
-EXPORT_SYMBOL(__scsi_print_sense);
-
-/* Normalize and print sense buffer in SCSI command */
-void scsi_print_sense(char *name, struct scsi_cmnd *cmd)
-{
- struct scsi_sense_hdr sshdr;
-
- scmd_printk(KERN_INFO, cmd, " ");
- scsi_decode_sense_buffer(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE,
- &sshdr);
- scsi_show_sense_hdr(&sshdr);
- scsi_decode_sense_extras(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE,
- &sshdr);
- scmd_printk(KERN_INFO, cmd, " ");
- scsi_show_extd_sense(sshdr.asc, sshdr.ascq);
-}
-EXPORT_SYMBOL(scsi_print_sense);
-
-#ifdef CONFIG_SCSI_CONSTANTS
-
static const char * const hostbyte_table[]={
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET",
"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR",
"DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY", "DID_REQUEUE",
-"DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST" };
-#define NUM_HOSTBYTE_STRS ARRAY_SIZE(hostbyte_table)
+"DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE",
+"DID_NEXUS_FAILURE", "DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR" };
-static const char * const driverbyte_table[]={
-"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR",
-"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE"};
-#define NUM_DRIVERBYTE_STRS ARRAY_SIZE(driverbyte_table)
-
-void scsi_show_result(int result)
+const char *scsi_hostbyte_string(int result)
{
- int hb = host_byte(result);
- int db = driver_byte(result);
+ enum scsi_host_status hb = host_byte(result);
+ const char *hb_string = NULL;
- printk("Result: hostbyte=%s driverbyte=%s\n",
- (hb < NUM_HOSTBYTE_STRS ? hostbyte_table[hb] : "invalid"),
- (db < NUM_DRIVERBYTE_STRS ? driverbyte_table[db] : "invalid"));
+ if (hb < ARRAY_SIZE(hostbyte_table))
+ hb_string = hostbyte_table[hb];
+ return hb_string;
}
+EXPORT_SYMBOL(scsi_hostbyte_string);
+
+#define scsi_mlreturn_name(result) { result, #result }
+static const struct value_name_pair scsi_mlreturn_arr[] = {
+ scsi_mlreturn_name(NEEDS_RETRY),
+ scsi_mlreturn_name(SUCCESS),
+ scsi_mlreturn_name(FAILED),
+ scsi_mlreturn_name(QUEUED),
+ scsi_mlreturn_name(SOFT_ERROR),
+ scsi_mlreturn_name(ADD_TO_MLQUEUE),
+ scsi_mlreturn_name(TIMEOUT_ERROR),
+ scsi_mlreturn_name(SCSI_RETURN_NOT_HANDLED),
+ scsi_mlreturn_name(FAST_IO_FAIL)
+};
-#else
-
-void scsi_show_result(int result)
+const char *scsi_mlreturn_string(int result)
{
- printk("Result: hostbyte=0x%02x driverbyte=0x%02x\n",
- host_byte(result), driver_byte(result));
-}
-
-#endif
-EXPORT_SYMBOL(scsi_show_result);
-
+ const struct value_name_pair *arr = scsi_mlreturn_arr;
+ int k;
-void scsi_print_result(struct scsi_cmnd *cmd)
-{
- scmd_printk(KERN_INFO, cmd, " ");
- scsi_show_result(cmd->result);
+ for (k = 0; k < ARRAY_SIZE(scsi_mlreturn_arr); ++k, ++arr) {
+ if (result == arr->value)
+ return arr->name;
+ }
+ return NULL;
}
-EXPORT_SYMBOL(scsi_print_result);
-
-
+EXPORT_SYMBOL(scsi_mlreturn_string);
diff --git a/drivers/scsi/csiostor/Kconfig b/drivers/scsi/csiostor/Kconfig
new file mode 100644
index 000000000000..c6c03f9e3798
--- /dev/null
+++ b/drivers/scsi/csiostor/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SCSI_CHELSIO_FCOE
+ tristate "Chelsio Communications FCoE support"
+ depends on PCI && SCSI
+ depends on SCSI_FC_ATTRS
+ select FW_LOADER
+ help
+ This driver supports FCoE Offload functionality over
+ Chelsio T4-based 10Gb Converged Network Adapters.
+
+ For general information about Chelsio and our products, visit
+ our website at <http://www.chelsio.com>.
+
+ For customer support, please visit our customer support page at
+ <http://www.chelsio.com/support.html>.
+
+ Please send feedback to <linux-bugs@chelsio.com>.
+
+ To compile this driver as a module choose M here; the module
+ will be called csiostor.
diff --git a/drivers/scsi/csiostor/Makefile b/drivers/scsi/csiostor/Makefile
new file mode 100644
index 000000000000..d047e22eac0d
--- /dev/null
+++ b/drivers/scsi/csiostor/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+## Chelsio FCoE driver
+#
+##
+
+ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4
+
+obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor.o
+
+csiostor-objs := csio_attr.o csio_init.o csio_lnode.o csio_scsi.o \
+ csio_hw.o csio_hw_t5.o csio_isr.o \
+ csio_mb.o csio_rnode.o csio_wr.o
diff --git a/drivers/scsi/csiostor/csio_attr.c b/drivers/scsi/csiostor/csio_attr.c
new file mode 100644
index 000000000000..200e50089711
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_attr.c
@@ -0,0 +1,805 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/jiffies.h>
+#include <scsi/fc/fc_fs.h>
+
+#include "csio_init.h"
+
+static void
+csio_vport_set_state(struct csio_lnode *ln);
+
+/*
+ * csio_reg_rnode - Register a remote port with FC transport.
+ * @rn: Rnode representing remote port.
+ *
+ * Call fc_remote_port_add() to register this remote port with FC transport.
+ * If remote port is Initiator OR Target OR both, change the role appropriately.
+ *
+ */
+void
+csio_reg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct fc_rport_identifiers ids;
+ struct fc_rport *rport;
+ struct csio_service_parms *sp;
+
+ ids.node_name = wwn_to_u64(csio_rn_wwnn(rn));
+ ids.port_name = wwn_to_u64(csio_rn_wwpn(rn));
+ ids.port_id = rn->nport_id;
+ ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+ if (rn->role & CSIO_RNFR_INITIATOR || rn->role & CSIO_RNFR_TARGET) {
+ rport = rn->rport;
+ CSIO_ASSERT(rport != NULL);
+ goto update_role;
+ }
+
+ rn->rport = fc_remote_port_add(shost, 0, &ids);
+ if (!rn->rport) {
+ csio_ln_err(ln, "Failed to register rport = 0x%x.\n",
+ rn->nport_id);
+ return;
+ }
+
+ ln->num_reg_rnodes++;
+ rport = rn->rport;
+ spin_lock_irq(shost->host_lock);
+ *((struct csio_rnode **)rport->dd_data) = rn;
+ spin_unlock_irq(shost->host_lock);
+
+ sp = &rn->rn_sparm;
+ rport->maxframe_size = ntohs(sp->csp.sp_bb_data);
+ if (ntohs(sp->clsp[2].cp_class) & FC_CPC_VALID)
+ rport->supported_classes = FC_COS_CLASS3;
+ else
+ rport->supported_classes = FC_COS_UNSPECIFIED;
+update_role:
+ if (rn->role & CSIO_RNFR_INITIATOR)
+ ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (rn->role & CSIO_RNFR_TARGET)
+ ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+
+ if (ids.roles != FC_RPORT_ROLE_UNKNOWN)
+ fc_remote_port_rolechg(rport, ids.roles);
+
+ rn->scsi_id = rport->scsi_target_id;
+
+ csio_ln_dbg(ln, "Remote port x%x role 0x%x registered\n",
+ rn->nport_id, ids.roles);
+}
+
+/*
+ * csio_unreg_rnode - Unregister a remote port with FC transport.
+ * @rn: Rnode representing remote port.
+ *
+ * Call fc_remote_port_delete() to unregister this remote port with FC
+ * transport.
+ *
+ */
+void
+csio_unreg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct fc_rport *rport = rn->rport;
+
+ rn->role &= ~(CSIO_RNFR_INITIATOR | CSIO_RNFR_TARGET);
+ fc_remote_port_delete(rport);
+ ln->num_reg_rnodes--;
+
+ csio_ln_dbg(ln, "Remote port x%x un-registered\n", rn->nport_id);
+}
+
+/*
+ * csio_lnode_async_event - Async events from local port.
+ * @ln: lnode representing local port.
+ *
+ * Async events from local node that FC transport/SCSI ML
+ * should be made aware of (Eg: RSCN).
+ */
+void
+csio_lnode_async_event(struct csio_lnode *ln, enum csio_ln_fc_evt fc_evt)
+{
+ switch (fc_evt) {
+ case CSIO_LN_FC_RSCN:
+ /* Get payload of rscn from ln */
+ /* For each RSCN entry */
+ /*
+ * fc_host_post_event(shost,
+ * fc_get_event_number(),
+ * FCH_EVT_RSCN,
+ * rscn_entry);
+ */
+ break;
+ case CSIO_LN_FC_LINKUP:
+ /* send fc_host_post_event */
+ /* set vport state */
+ if (csio_is_npiv_ln(ln))
+ csio_vport_set_state(ln);
+
+ break;
+ case CSIO_LN_FC_LINKDOWN:
+ /* send fc_host_post_event */
+ /* set vport state */
+ if (csio_is_npiv_ln(ln))
+ csio_vport_set_state(ln);
+
+ break;
+ case CSIO_LN_FC_ATTRIB_UPDATE:
+ csio_fchost_attr_init(ln);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * csio_fchost_attr_init - Initialize FC transport attributes
+ * @ln: Lnode.
+ *
+ */
+void
+csio_fchost_attr_init(struct csio_lnode *ln)
+{
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+
+ fc_host_node_name(shost) = wwn_to_u64(csio_ln_wwnn(ln));
+ fc_host_port_name(shost) = wwn_to_u64(csio_ln_wwpn(ln));
+
+ fc_host_supported_classes(shost) = FC_COS_CLASS3;
+ fc_host_max_npiv_vports(shost) =
+ (csio_lnode_to_hw(ln))->fres_info.max_vnps;
+ fc_host_supported_speeds(shost) = FC_PORTSPEED_10GBIT |
+ FC_PORTSPEED_1GBIT;
+
+ fc_host_maxframe_size(shost) = ntohs(ln->ln_sparm.csp.sp_bb_data);
+ memset(fc_host_supported_fc4s(shost), 0,
+ sizeof(fc_host_supported_fc4s(shost)));
+ fc_host_supported_fc4s(shost)[7] = 1;
+
+ memset(fc_host_active_fc4s(shost), 0,
+ sizeof(fc_host_active_fc4s(shost)));
+ fc_host_active_fc4s(shost)[7] = 1;
+}
+
+/*
+ * csio_get_host_port_id - sysfs entries for nport_id is
+ * populated/cached from this function
+ */
+static void
+csio_get_host_port_id(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ fc_host_port_id(shost) = ln->nport_id;
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_port_type - Return FC local port type.
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_port_type(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ if (csio_is_npiv_ln(ln))
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+ else
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_port_state - Return FC local port state.
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ char state[16];
+
+ spin_lock_irq(&hw->lock);
+
+ csio_lnode_state_to_str(ln, state);
+ if (!strcmp(state, "READY"))
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ else if (!strcmp(state, "OFFLINE"))
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_host_speed - Return link speed to FC transport.
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_speed(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ switch (hw->pport[ln->portid].link_speed) {
+ case FW_PORT_CAP32_SPEED_1G:
+ fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_10G:
+ fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_25G:
+ fc_host_speed(shost) = FC_PORTSPEED_25GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_40G:
+ fc_host_speed(shost) = FC_PORTSPEED_40GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_50G:
+ fc_host_speed(shost) = FC_PORTSPEED_50GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_100G:
+ fc_host_speed(shost) = FC_PORTSPEED_100GBIT;
+ break;
+ default:
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+ break;
+ }
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_host_fabric_name - Return fabric name
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_fabric_name(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_rnode *rn = NULL;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ rn = csio_rnode_lookup_portid(ln, FC_FID_FLOGI);
+ if (rn)
+ fc_host_fabric_name(shost) = wwn_to_u64(csio_rn_wwnn(rn));
+ else
+ fc_host_fabric_name(shost) = 0;
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_host_speed - Return FC transport statistics.
+ * @ln: Lnode.
+ *
+ */
+static struct fc_host_statistics *
+csio_get_stats(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct fc_host_statistics *fhs = &ln->fch_stats;
+ struct fw_fcoe_port_stats fcoe_port_stats;
+ uint64_t seconds;
+
+ memset(&fcoe_port_stats, 0, sizeof(struct fw_fcoe_port_stats));
+ csio_get_phy_port_stats(hw, ln->portid, &fcoe_port_stats);
+
+ fhs->tx_frames += (be64_to_cpu(fcoe_port_stats.tx_bcast_frames) +
+ be64_to_cpu(fcoe_port_stats.tx_mcast_frames) +
+ be64_to_cpu(fcoe_port_stats.tx_ucast_frames) +
+ be64_to_cpu(fcoe_port_stats.tx_offload_frames));
+ fhs->tx_words += (be64_to_cpu(fcoe_port_stats.tx_bcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.tx_mcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.tx_ucast_bytes) +
+ be64_to_cpu(fcoe_port_stats.tx_offload_bytes)) /
+ CSIO_WORD_TO_BYTE;
+ fhs->rx_frames += (be64_to_cpu(fcoe_port_stats.rx_bcast_frames) +
+ be64_to_cpu(fcoe_port_stats.rx_mcast_frames) +
+ be64_to_cpu(fcoe_port_stats.rx_ucast_frames));
+ fhs->rx_words += (be64_to_cpu(fcoe_port_stats.rx_bcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.rx_mcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.rx_ucast_bytes)) /
+ CSIO_WORD_TO_BYTE;
+ fhs->error_frames += be64_to_cpu(fcoe_port_stats.rx_err_frames);
+ fhs->fcp_input_requests += ln->stats.n_input_requests;
+ fhs->fcp_output_requests += ln->stats.n_output_requests;
+ fhs->fcp_control_requests += ln->stats.n_control_requests;
+ fhs->fcp_input_megabytes += ln->stats.n_input_bytes >> 20;
+ fhs->fcp_output_megabytes += ln->stats.n_output_bytes >> 20;
+ fhs->link_failure_count = ln->stats.n_link_down;
+ /* Reset stats for the device */
+ seconds = jiffies_to_msecs(jiffies) - hw->stats.n_reset_start;
+ do_div(seconds, 1000);
+ fhs->seconds_since_last_reset = seconds;
+
+ return fhs;
+}
+
+/*
+ * csio_set_rport_loss_tmo - Set the rport dev loss timeout
+ * @rport: fc rport.
+ * @timeout: new value for dev loss tmo.
+ *
+ * If timeout is non zero set the dev_loss_tmo to timeout, else set
+ * dev_loss_tmo to one.
+ */
+static void
+csio_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
+{
+ if (timeout)
+ rport->dev_loss_tmo = timeout;
+ else
+ rport->dev_loss_tmo = 1;
+}
+
+static void
+csio_vport_set_state(struct csio_lnode *ln)
+{
+ struct fc_vport *fc_vport = ln->fc_vport;
+ struct csio_lnode *pln = ln->pln;
+ char state[16];
+
+ /* Set fc vport state based on phyiscal lnode */
+ csio_lnode_state_to_str(pln, state);
+ if (strcmp(state, "READY")) {
+ fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
+ return;
+ }
+
+ if (!(pln->flags & CSIO_LNF_NPIVSUPP)) {
+ fc_vport_set_state(fc_vport, FC_VPORT_NO_FABRIC_SUPP);
+ return;
+ }
+
+ /* Set fc vport state based on virtual lnode */
+ csio_lnode_state_to_str(ln, state);
+ if (strcmp(state, "READY")) {
+ fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
+ return;
+ }
+ fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE);
+}
+
+static int
+csio_fcoe_alloc_vnp(struct csio_hw *hw, struct csio_lnode *ln)
+{
+ struct csio_lnode *pln;
+ struct csio_mb *mbp;
+ struct fw_fcoe_vnp_cmd *rsp;
+ int ret = 0;
+ int retry = 0;
+
+ /* Issue VNP cmd to alloc vport */
+ /* Allocate Mbox request */
+ spin_lock_irq(&hw->lock);
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ pln = ln->pln;
+ ln->fcf_flowid = pln->fcf_flowid;
+ ln->portid = pln->portid;
+
+ csio_fcoe_vnp_alloc_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ pln->fcf_flowid, pln->vnp_flowid, 0,
+ csio_ln_wwnn(ln), csio_ln_wwpn(ln), NULL);
+
+ for (retry = 0; retry < 3; retry++) {
+ /* FW is expected to complete vnp cmd in immediate mode
+ * without much delay.
+ * Otherwise, there will be increase in IO latency since HW
+ * lock is held till completion of vnp mbox cmd.
+ */
+ ret = csio_mb_issue(hw, mbp);
+ if (ret != -EBUSY)
+ break;
+
+ /* Retry if mbox returns busy */
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ if (ret) {
+ csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n");
+ goto out_free;
+ }
+
+ /* Process Mbox response of VNP command */
+ rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+ if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) {
+ csio_ln_err(ln, "FCOE VNP ALLOC cmd returned 0x%x!\n",
+ FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)));
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ ln->vnp_flowid = FW_FCOE_VNP_CMD_VNPI_GET(
+ ntohl(rsp->gen_wwn_to_vnpi));
+ memcpy(csio_ln_wwnn(ln), rsp->vnport_wwnn, 8);
+ memcpy(csio_ln_wwpn(ln), rsp->vnport_wwpn, 8);
+
+ csio_ln_dbg(ln, "FCOE VNPI: 0x%x\n", ln->vnp_flowid);
+ csio_ln_dbg(ln, "\tWWNN: %x%x%x%x%x%x%x%x\n",
+ ln->ln_sparm.wwnn[0], ln->ln_sparm.wwnn[1],
+ ln->ln_sparm.wwnn[2], ln->ln_sparm.wwnn[3],
+ ln->ln_sparm.wwnn[4], ln->ln_sparm.wwnn[5],
+ ln->ln_sparm.wwnn[6], ln->ln_sparm.wwnn[7]);
+ csio_ln_dbg(ln, "\tWWPN: %x%x%x%x%x%x%x%x\n",
+ ln->ln_sparm.wwpn[0], ln->ln_sparm.wwpn[1],
+ ln->ln_sparm.wwpn[2], ln->ln_sparm.wwpn[3],
+ ln->ln_sparm.wwpn[4], ln->ln_sparm.wwpn[5],
+ ln->ln_sparm.wwpn[6], ln->ln_sparm.wwpn[7]);
+
+out_free:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ spin_unlock_irq(&hw->lock);
+ return ret;
+}
+
+static int
+csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln)
+{
+ struct csio_mb *mbp;
+ struct fw_fcoe_vnp_cmd *rsp;
+ int ret = 0;
+ int retry = 0;
+
+ /* Issue VNP cmd to free vport */
+ /* Allocate Mbox request */
+
+ spin_lock_irq(&hw->lock);
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ csio_fcoe_vnp_free_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ ln->fcf_flowid, ln->vnp_flowid,
+ NULL);
+
+ for (retry = 0; retry < 3; retry++) {
+ ret = csio_mb_issue(hw, mbp);
+ if (ret != -EBUSY)
+ break;
+
+ /* Retry if mbox returns busy */
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ if (ret) {
+ csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n");
+ goto out_free;
+ }
+
+ /* Process Mbox response of VNP command */
+ rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+ if (FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) {
+ csio_ln_err(ln, "FCOE VNP FREE cmd returned 0x%x!\n",
+ FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16)));
+ ret = -EINVAL;
+ }
+
+out_free:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ spin_unlock_irq(&hw->lock);
+ return ret;
+}
+
+static int
+csio_vport_create(struct fc_vport *fc_vport, bool disable)
+{
+ struct Scsi_Host *shost = fc_vport->shost;
+ struct csio_lnode *pln = shost_priv(shost);
+ struct csio_lnode *ln = NULL;
+ struct csio_hw *hw = csio_lnode_to_hw(pln);
+ uint8_t wwn[8];
+ int ret = -1;
+
+ ln = csio_shost_init(hw, &fc_vport->dev, false, pln);
+ if (!ln)
+ goto error;
+
+ if (fc_vport->node_name != 0) {
+ u64_to_wwn(fc_vport->node_name, wwn);
+
+ if (!CSIO_VALID_WWN(wwn)) {
+ csio_ln_err(ln,
+ "vport create failed. Invalid wwnn\n");
+ goto error;
+ }
+ memcpy(csio_ln_wwnn(ln), wwn, 8);
+ }
+
+ if (fc_vport->port_name != 0) {
+ u64_to_wwn(fc_vport->port_name, wwn);
+
+ if (!CSIO_VALID_WWN(wwn)) {
+ csio_ln_err(ln,
+ "vport create failed. Invalid wwpn\n");
+ goto error;
+ }
+
+ if (csio_lnode_lookup_by_wwpn(hw, wwn)) {
+ csio_ln_err(ln,
+ "vport create failed. wwpn already exists\n");
+ goto error;
+ }
+ memcpy(csio_ln_wwpn(ln), wwn, 8);
+ }
+
+ fc_vport_set_state(fc_vport, FC_VPORT_INITIALIZING);
+ ln->fc_vport = fc_vport;
+
+ if (csio_fcoe_alloc_vnp(hw, ln))
+ goto error;
+
+ *(struct csio_lnode **)fc_vport->dd_data = ln;
+ if (!fc_vport->node_name)
+ fc_vport->node_name = wwn_to_u64(csio_ln_wwnn(ln));
+ if (!fc_vport->port_name)
+ fc_vport->port_name = wwn_to_u64(csio_ln_wwpn(ln));
+ csio_fchost_attr_init(ln);
+ return 0;
+error:
+ if (ln)
+ csio_shost_exit(ln);
+
+ return ret;
+}
+
+static int
+csio_vport_delete(struct fc_vport *fc_vport)
+{
+ struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data;
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ int rmv;
+
+ spin_lock_irq(&hw->lock);
+ rmv = csio_is_hw_removing(hw);
+ spin_unlock_irq(&hw->lock);
+
+ if (rmv) {
+ csio_shost_exit(ln);
+ return 0;
+ }
+
+ /* Quiesce ios and send remove event to lnode */
+ scsi_block_requests(shost);
+ spin_lock_irq(&hw->lock);
+ csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln);
+ csio_lnode_close(ln);
+ spin_unlock_irq(&hw->lock);
+ scsi_unblock_requests(shost);
+
+ /* Free vnp */
+ if (fc_vport->vport_state != FC_VPORT_DISABLED)
+ csio_fcoe_free_vnp(hw, ln);
+
+ csio_shost_exit(ln);
+ return 0;
+}
+
+static int
+csio_vport_disable(struct fc_vport *fc_vport, bool disable)
+{
+ struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data;
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ /* disable vport */
+ if (disable) {
+ /* Quiesce ios and send stop event to lnode */
+ scsi_block_requests(shost);
+ spin_lock_irq(&hw->lock);
+ csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln);
+ csio_lnode_stop(ln);
+ spin_unlock_irq(&hw->lock);
+ scsi_unblock_requests(shost);
+
+ /* Free vnp */
+ csio_fcoe_free_vnp(hw, ln);
+ fc_vport_set_state(fc_vport, FC_VPORT_DISABLED);
+ csio_ln_err(ln, "vport disabled\n");
+ return 0;
+ } else {
+ /* enable vport */
+ fc_vport_set_state(fc_vport, FC_VPORT_INITIALIZING);
+ if (csio_fcoe_alloc_vnp(hw, ln)) {
+ csio_ln_err(ln, "vport enabled failed.\n");
+ return -1;
+ }
+ csio_ln_err(ln, "vport enabled\n");
+ return 0;
+ }
+}
+
+static void
+csio_dev_loss_tmo_callbk(struct fc_rport *rport)
+{
+ struct csio_rnode *rn;
+ struct csio_hw *hw;
+ struct csio_lnode *ln;
+
+ rn = *((struct csio_rnode **)rport->dd_data);
+ ln = csio_rnode_to_lnode(rn);
+ hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+
+ /* return if driver is being removed or same rnode comes back online */
+ if (csio_is_hw_removing(hw) || csio_is_rnode_ready(rn))
+ goto out;
+
+ csio_ln_dbg(ln, "devloss timeout on rnode:%p portid:x%x flowid:x%x\n",
+ rn, rn->nport_id, csio_rn_flowid(rn));
+
+ CSIO_INC_STATS(ln, n_dev_loss_tmo);
+
+ /*
+ * enqueue devloss event to event worker thread to serialize all
+ * rnode events.
+ */
+ if (csio_enqueue_evt(hw, CSIO_EVT_DEV_LOSS, &rn, sizeof(rn))) {
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto out;
+ }
+
+ if (!(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irq(&hw->lock);
+ schedule_work(&hw->evtq_work);
+ return;
+ }
+
+out:
+ spin_unlock_irq(&hw->lock);
+}
+
+/* FC transport functions template - Physical port */
+struct fc_function_template csio_fc_transport_funcs = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .get_host_port_id = csio_get_host_port_id,
+ .show_host_port_id = 1,
+
+ .get_host_port_type = csio_get_host_port_type,
+ .show_host_port_type = 1,
+
+ .get_host_port_state = csio_get_host_port_state,
+ .show_host_port_state = 1,
+
+ .show_host_active_fc4s = 1,
+ .get_host_speed = csio_get_host_speed,
+ .show_host_speed = 1,
+ .get_host_fabric_name = csio_get_host_fabric_name,
+ .show_host_fabric_name = 1,
+
+ .get_fc_host_stats = csio_get_stats,
+
+ .dd_fcrport_size = sizeof(struct csio_rnode *),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+
+ .show_starget_port_id = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+
+ .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk,
+ .dd_fcvport_size = sizeof(struct csio_lnode *),
+
+ .vport_create = csio_vport_create,
+ .vport_disable = csio_vport_disable,
+ .vport_delete = csio_vport_delete,
+};
+
+/* FC transport functions template - Virtual port */
+struct fc_function_template csio_fc_transport_vport_funcs = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .get_host_port_id = csio_get_host_port_id,
+ .show_host_port_id = 1,
+
+ .get_host_port_type = csio_get_host_port_type,
+ .show_host_port_type = 1,
+
+ .get_host_port_state = csio_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_active_fc4s = 1,
+
+ .get_host_speed = csio_get_host_speed,
+ .show_host_speed = 1,
+
+ .get_host_fabric_name = csio_get_host_fabric_name,
+ .show_host_fabric_name = 1,
+
+ .get_fc_host_stats = csio_get_stats,
+
+ .dd_fcrport_size = sizeof(struct csio_rnode *),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+
+ .show_starget_port_id = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+
+ .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk,
+
+};
diff --git a/drivers/scsi/csiostor/csio_defs.h b/drivers/scsi/csiostor/csio_defs.h
new file mode 100644
index 000000000000..e50e93e7fe5a
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_defs.h
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_DEFS_H__
+#define __CSIO_DEFS_H__
+
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/pci.h>
+#include <linux/jiffies.h>
+
+#define CSIO_INVALID_IDX 0xFFFFFFFF
+#define CSIO_INC_STATS(elem, val) ((elem)->stats.val++)
+#define CSIO_DEC_STATS(elem, val) ((elem)->stats.val--)
+#define CSIO_VALID_WWN(__n) ((*__n >> 4) == 0x5 ? true : false)
+#define CSIO_DID_MASK 0xFFFFFF
+#define CSIO_WORD_TO_BYTE 4
+
+#ifndef readq
+static inline u64 readq(void __iomem *addr)
+{
+ return readl(addr) + ((u64)readl(addr + 4) << 32);
+}
+
+static inline void writeq(u64 val, void __iomem *addr)
+{
+ writel(val, addr);
+ writel(val >> 32, addr + 4);
+}
+#endif
+
+static inline int
+csio_list_deleted(struct list_head *list)
+{
+ return ((list->next == list) && (list->prev == list));
+}
+
+#define csio_list_next(elem) (((struct list_head *)(elem))->next)
+#define csio_list_prev(elem) (((struct list_head *)(elem))->prev)
+
+/* State machine */
+struct csio_lnode;
+
+/* State machine evets */
+enum csio_ln_ev {
+ CSIO_LNE_NONE = (uint32_t)0,
+ CSIO_LNE_LINKUP,
+ CSIO_LNE_FAB_INIT_DONE,
+ CSIO_LNE_LINK_DOWN,
+ CSIO_LNE_DOWN_LINK,
+ CSIO_LNE_LOGO,
+ CSIO_LNE_CLOSE,
+ CSIO_LNE_MAX_EVENT,
+};
+
+typedef void (*csio_sm_state_t)(struct csio_lnode *ln, enum csio_ln_ev evt);
+
+struct csio_sm {
+ struct list_head sm_list;
+ csio_sm_state_t sm_state;
+};
+
+static inline void
+csio_set_state(void *smp, void *state)
+{
+ ((struct csio_sm *)smp)->sm_state = state;
+}
+
+static inline void
+csio_init_state(struct csio_sm *smp, void *state)
+{
+ csio_set_state(smp, state);
+}
+
+static inline void
+csio_post_event(void *smp, uint32_t evt)
+{
+ ((struct csio_sm *)smp)->sm_state(smp, evt);
+}
+
+static inline csio_sm_state_t
+csio_get_state(void *smp)
+{
+ return ((struct csio_sm *)smp)->sm_state;
+}
+
+static inline bool
+csio_match_state(void *smp, void *state)
+{
+ return (csio_get_state(smp) == (csio_sm_state_t)state);
+}
+
+#define CSIO_ASSERT(cond) BUG_ON(!(cond))
+
+#ifdef __CSIO_DEBUG__
+#define CSIO_DB_ASSERT(__c) CSIO_ASSERT((__c))
+#else
+#define CSIO_DB_ASSERT(__c)
+#endif
+
+#endif /* ifndef __CSIO_DEFS_H__ */
diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c
new file mode 100644
index 000000000000..7aa418ebfe01
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_hw.c
@@ -0,0 +1,4434 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/firmware.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/compiler.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+
+int csio_dbg_level = 0xFEFF;
+unsigned int csio_port_mask = 0xf;
+
+/* Default FW event queue entries. */
+static uint32_t csio_evtq_sz = CSIO_EVTQ_SIZE;
+
+/* Default MSI param level */
+int csio_msi = 2;
+
+/* FCoE function instances */
+static int dev_num;
+
+/* FCoE Adapter types & its description */
+static const struct csio_adap_desc csio_t5_fcoe_adapters[] = {
+ {"T580-Dbg 10G", "Chelsio T580-Dbg 10G [FCoE]"},
+ {"T520-CR 10G", "Chelsio T520-CR 10G [FCoE]"},
+ {"T522-CR 10G/1G", "Chelsio T522-CR 10G/1G [FCoE]"},
+ {"T540-CR 10G", "Chelsio T540-CR 10G [FCoE]"},
+ {"T520-BCH 10G", "Chelsio T520-BCH 10G [FCoE]"},
+ {"T540-BCH 10G", "Chelsio T540-BCH 10G [FCoE]"},
+ {"T540-CH 10G", "Chelsio T540-CH 10G [FCoE]"},
+ {"T520-SO 10G", "Chelsio T520-SO 10G [FCoE]"},
+ {"T520-CX4 10G", "Chelsio T520-CX4 10G [FCoE]"},
+ {"T520-BT 10G", "Chelsio T520-BT 10G [FCoE]"},
+ {"T504-BT 1G", "Chelsio T504-BT 1G [FCoE]"},
+ {"B520-SR 10G", "Chelsio B520-SR 10G [FCoE]"},
+ {"B504-BT 1G", "Chelsio B504-BT 1G [FCoE]"},
+ {"T580-CR 10G", "Chelsio T580-CR 10G [FCoE]"},
+ {"T540-LP-CR 10G", "Chelsio T540-LP-CR 10G [FCoE]"},
+ {"AMSTERDAM 10G", "Chelsio AMSTERDAM 10G [FCoE]"},
+ {"T580-LP-CR 40G", "Chelsio T580-LP-CR 40G [FCoE]"},
+ {"T520-LL-CR 10G", "Chelsio T520-LL-CR 10G [FCoE]"},
+ {"T560-CR 40G", "Chelsio T560-CR 40G [FCoE]"},
+ {"T580-CR 40G", "Chelsio T580-CR 40G [FCoE]"},
+ {"T580-SO 40G", "Chelsio T580-SO 40G [FCoE]"},
+ {"T502-BT 1G", "Chelsio T502-BT 1G [FCoE]"}
+};
+
+static void csio_mgmtm_cleanup(struct csio_mgmtm *);
+static void csio_hw_mbm_cleanup(struct csio_hw *);
+
+/* State machine forward declarations */
+static void csio_hws_uninit(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_configuring(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_initializing(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_ready(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_quiescing(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_quiesced(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_resetting(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_removing(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_pcierr(struct csio_hw *, enum csio_hw_ev);
+
+static void csio_hw_initialize(struct csio_hw *hw);
+static void csio_evtq_stop(struct csio_hw *hw);
+static void csio_evtq_start(struct csio_hw *hw);
+
+int csio_is_hw_ready(struct csio_hw *hw)
+{
+ return csio_match_state(hw, csio_hws_ready);
+}
+
+int csio_is_hw_removing(struct csio_hw *hw)
+{
+ return csio_match_state(hw, csio_hws_removing);
+}
+
+
+/*
+ * csio_hw_wait_op_done_val - wait until an operation is completed
+ * @hw: the HW module
+ * @reg: the register to check for completion
+ * @mask: a single-bit field within @reg that indicates completion
+ * @polarity: the value of the field when the operation is completed
+ * @attempts: number of check iterations
+ * @delay: delay in usecs between iterations
+ * @valp: where to store the value of the register at completion time
+ *
+ * Wait until an operation is completed by checking a bit in a register
+ * up to @attempts times. If @valp is not NULL the value of the register
+ * at the time it indicated completion is stored there. Returns 0 if the
+ * operation completes and -EAGAIN otherwise.
+ */
+int
+csio_hw_wait_op_done_val(struct csio_hw *hw, int reg, uint32_t mask,
+ int polarity, int attempts, int delay, uint32_t *valp)
+{
+ uint32_t val;
+ while (1) {
+ val = csio_rd_reg32(hw, reg);
+
+ if (!!(val & mask) == polarity) {
+ if (valp)
+ *valp = val;
+ return 0;
+ }
+
+ if (--attempts == 0)
+ return -EAGAIN;
+ if (delay)
+ udelay(delay);
+ }
+}
+
+/*
+ * csio_hw_tp_wr_bits_indirect - set/clear bits in an indirect TP register
+ * @hw: the adapter
+ * @addr: the indirect TP register address
+ * @mask: specifies the field within the register to modify
+ * @val: new value for the field
+ *
+ * Sets a field of an indirect TP register to the given value.
+ */
+void
+csio_hw_tp_wr_bits_indirect(struct csio_hw *hw, unsigned int addr,
+ unsigned int mask, unsigned int val)
+{
+ csio_wr_reg32(hw, addr, TP_PIO_ADDR_A);
+ val |= csio_rd_reg32(hw, TP_PIO_DATA_A) & ~mask;
+ csio_wr_reg32(hw, val, TP_PIO_DATA_A);
+}
+
+void
+csio_set_reg_field(struct csio_hw *hw, uint32_t reg, uint32_t mask,
+ uint32_t value)
+{
+ uint32_t val = csio_rd_reg32(hw, reg) & ~mask;
+
+ csio_wr_reg32(hw, val | value, reg);
+ /* Flush */
+ csio_rd_reg32(hw, reg);
+
+}
+
+static int
+csio_memory_write(struct csio_hw *hw, int mtype, u32 addr, u32 len, u32 *buf)
+{
+ return hw->chip_ops->chip_memory_rw(hw, MEMWIN_CSIOSTOR, mtype,
+ addr, len, buf, 0);
+}
+
+/*
+ * EEPROM reads take a few tens of us while writes can take a bit over 5 ms.
+ */
+#define EEPROM_MAX_RD_POLL 40
+#define EEPROM_MAX_WR_POLL 6
+#define EEPROM_STAT_ADDR 0x7bfc
+#define VPD_BASE 0x400
+#define VPD_BASE_OLD 0
+#define VPD_LEN 1024
+#define VPD_INFO_FLD_HDR_SIZE 3
+
+/*
+ * csio_hw_seeprom_read - read a serial EEPROM location
+ * @hw: hw to read
+ * @addr: EEPROM virtual address
+ * @data: where to store the read data
+ *
+ * Read a 32-bit word from a location in serial EEPROM using the card's PCI
+ * VPD capability. Note that this function must be called with a virtual
+ * address.
+ */
+static int
+csio_hw_seeprom_read(struct csio_hw *hw, uint32_t addr, uint32_t *data)
+{
+ uint16_t val = 0;
+ int attempts = EEPROM_MAX_RD_POLL;
+ uint32_t base = hw->params.pci.vpd_cap_addr;
+
+ if (addr >= EEPROMVSIZE || (addr & 3))
+ return -EINVAL;
+
+ pci_write_config_word(hw->pdev, base + PCI_VPD_ADDR, (uint16_t)addr);
+
+ do {
+ udelay(10);
+ pci_read_config_word(hw->pdev, base + PCI_VPD_ADDR, &val);
+ } while (!(val & PCI_VPD_ADDR_F) && --attempts);
+
+ if (!(val & PCI_VPD_ADDR_F)) {
+ csio_err(hw, "reading EEPROM address 0x%x failed\n", addr);
+ return -EINVAL;
+ }
+
+ pci_read_config_dword(hw->pdev, base + PCI_VPD_DATA, data);
+ *data = le32_to_cpu(*(__le32 *)data);
+
+ return 0;
+}
+
+/*
+ * Partial EEPROM Vital Product Data structure. Includes only the ID and
+ * VPD-R sections.
+ */
+struct t4_vpd_hdr {
+ u8 id_tag;
+ u8 id_len[2];
+ u8 id_data[ID_LEN];
+ u8 vpdr_tag;
+ u8 vpdr_len[2];
+};
+
+/*
+ * csio_hw_get_vpd_keyword_val - Locates an information field keyword in
+ * the VPD
+ * @v: Pointer to buffered vpd data structure
+ * @kw: The keyword to search for
+ *
+ * Returns the value of the information field keyword or
+ * -EINVAL otherwise.
+ */
+static int
+csio_hw_get_vpd_keyword_val(const struct t4_vpd_hdr *v, const char *kw)
+{
+ int32_t i;
+ int32_t offset , len;
+ const uint8_t *buf = &v->id_tag;
+ const uint8_t *vpdr_len = &v->vpdr_tag;
+ offset = sizeof(struct t4_vpd_hdr);
+ len = (uint16_t)vpdr_len[1] + ((uint16_t)vpdr_len[2] << 8);
+
+ if (len + sizeof(struct t4_vpd_hdr) > VPD_LEN)
+ return -EINVAL;
+
+ for (i = offset; (i + VPD_INFO_FLD_HDR_SIZE) <= (offset + len);) {
+ if (memcmp(buf + i , kw, 2) == 0) {
+ i += VPD_INFO_FLD_HDR_SIZE;
+ return i;
+ }
+
+ i += VPD_INFO_FLD_HDR_SIZE + buf[i+2];
+ }
+
+ return -EINVAL;
+}
+
+static int
+csio_pci_capability(struct pci_dev *pdev, int cap, int *pos)
+{
+ *pos = pci_find_capability(pdev, cap);
+ if (*pos)
+ return 0;
+
+ return -1;
+}
+
+/*
+ * csio_hw_get_vpd_params - read VPD parameters from VPD EEPROM
+ * @hw: HW module
+ * @p: where to store the parameters
+ *
+ * Reads card parameters stored in VPD EEPROM.
+ */
+static int
+csio_hw_get_vpd_params(struct csio_hw *hw, struct csio_vpd *p)
+{
+ int i, ret, ec, sn, addr;
+ uint8_t *vpd, csum;
+ const struct t4_vpd_hdr *v;
+ /* To get around compilation warning from strstrip */
+ char __always_unused *s;
+
+ if (csio_is_valid_vpd(hw))
+ return 0;
+
+ ret = csio_pci_capability(hw->pdev, PCI_CAP_ID_VPD,
+ &hw->params.pci.vpd_cap_addr);
+ if (ret)
+ return -EINVAL;
+
+ vpd = kzalloc(VPD_LEN, GFP_ATOMIC);
+ if (vpd == NULL)
+ return -ENOMEM;
+
+ /*
+ * Card information normally starts at VPD_BASE but early cards had
+ * it at 0.
+ */
+ ret = csio_hw_seeprom_read(hw, VPD_BASE, (uint32_t *)(vpd));
+ addr = *vpd == 0x82 ? VPD_BASE : VPD_BASE_OLD;
+
+ for (i = 0; i < VPD_LEN; i += 4) {
+ ret = csio_hw_seeprom_read(hw, addr + i, (uint32_t *)(vpd + i));
+ if (ret) {
+ kfree(vpd);
+ return ret;
+ }
+ }
+
+ /* Reset the VPD flag! */
+ hw->flags &= (~CSIO_HWF_VPD_VALID);
+
+ v = (const struct t4_vpd_hdr *)vpd;
+
+#define FIND_VPD_KW(var, name) do { \
+ var = csio_hw_get_vpd_keyword_val(v, name); \
+ if (var < 0) { \
+ csio_err(hw, "missing VPD keyword " name "\n"); \
+ kfree(vpd); \
+ return -EINVAL; \
+ } \
+} while (0)
+
+ FIND_VPD_KW(i, "RV");
+ for (csum = 0; i >= 0; i--)
+ csum += vpd[i];
+
+ if (csum) {
+ csio_err(hw, "corrupted VPD EEPROM, actual csum %u\n", csum);
+ kfree(vpd);
+ return -EINVAL;
+ }
+ FIND_VPD_KW(ec, "EC");
+ FIND_VPD_KW(sn, "SN");
+#undef FIND_VPD_KW
+
+ memcpy(p->id, v->id_data, ID_LEN);
+ s = strstrip(p->id);
+ memcpy(p->ec, vpd + ec, EC_LEN);
+ s = strstrip(p->ec);
+ i = vpd[sn - VPD_INFO_FLD_HDR_SIZE + 2];
+ memcpy(p->sn, vpd + sn, min(i, SERNUM_LEN));
+ s = strstrip(p->sn);
+
+ csio_valid_vpd_copied(hw);
+
+ kfree(vpd);
+ return 0;
+}
+
+/*
+ * csio_hw_sf1_read - read data from the serial flash
+ * @hw: the HW module
+ * @byte_cnt: number of bytes to read
+ * @cont: whether another operation will be chained
+ * @lock: whether to lock SF for PL access only
+ * @valp: where to store the read data
+ *
+ * Reads up to 4 bytes of data from the serial flash. The location of
+ * the read needs to be specified prior to calling this by issuing the
+ * appropriate commands to the serial flash.
+ */
+static int
+csio_hw_sf1_read(struct csio_hw *hw, uint32_t byte_cnt, int32_t cont,
+ int32_t lock, uint32_t *valp)
+{
+ int ret;
+
+ if (!byte_cnt || byte_cnt > 4)
+ return -EINVAL;
+ if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F)
+ return -EBUSY;
+
+ csio_wr_reg32(hw, SF_LOCK_V(lock) | SF_CONT_V(cont) |
+ BYTECNT_V(byte_cnt - 1), SF_OP_A);
+ ret = csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS,
+ 10, NULL);
+ if (!ret)
+ *valp = csio_rd_reg32(hw, SF_DATA_A);
+ return ret;
+}
+
+/*
+ * csio_hw_sf1_write - write data to the serial flash
+ * @hw: the HW module
+ * @byte_cnt: number of bytes to write
+ * @cont: whether another operation will be chained
+ * @lock: whether to lock SF for PL access only
+ * @val: value to write
+ *
+ * Writes up to 4 bytes of data to the serial flash. The location of
+ * the write needs to be specified prior to calling this by issuing the
+ * appropriate commands to the serial flash.
+ */
+static int
+csio_hw_sf1_write(struct csio_hw *hw, uint32_t byte_cnt, uint32_t cont,
+ int32_t lock, uint32_t val)
+{
+ if (!byte_cnt || byte_cnt > 4)
+ return -EINVAL;
+ if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F)
+ return -EBUSY;
+
+ csio_wr_reg32(hw, val, SF_DATA_A);
+ csio_wr_reg32(hw, SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1) |
+ OP_V(1) | SF_LOCK_V(lock), SF_OP_A);
+
+ return csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS,
+ 10, NULL);
+}
+
+/*
+ * csio_hw_flash_wait_op - wait for a flash operation to complete
+ * @hw: the HW module
+ * @attempts: max number of polls of the status register
+ * @delay: delay between polls in ms
+ *
+ * Wait for a flash operation to complete by polling the status register.
+ */
+static int
+csio_hw_flash_wait_op(struct csio_hw *hw, int32_t attempts, int32_t delay)
+{
+ int ret;
+ uint32_t status;
+
+ while (1) {
+ ret = csio_hw_sf1_write(hw, 1, 1, 1, SF_RD_STATUS);
+ if (ret != 0)
+ return ret;
+
+ ret = csio_hw_sf1_read(hw, 1, 0, 1, &status);
+ if (ret != 0)
+ return ret;
+
+ if (!(status & 1))
+ return 0;
+ if (--attempts == 0)
+ return -EAGAIN;
+ if (delay)
+ msleep(delay);
+ }
+}
+
+/*
+ * csio_hw_read_flash - read words from serial flash
+ * @hw: the HW module
+ * @addr: the start address for the read
+ * @nwords: how many 32-bit words to read
+ * @data: where to store the read data
+ * @byte_oriented: whether to store data as bytes or as words
+ *
+ * Read the specified number of 32-bit words from the serial flash.
+ * If @byte_oriented is set the read data is stored as a byte array
+ * (i.e., big-endian), otherwise as 32-bit words in the platform's
+ * natural endianess.
+ */
+static int
+csio_hw_read_flash(struct csio_hw *hw, uint32_t addr, uint32_t nwords,
+ uint32_t *data, int32_t byte_oriented)
+{
+ int ret;
+
+ if (addr + nwords * sizeof(uint32_t) > hw->params.sf_size || (addr & 3))
+ return -EINVAL;
+
+ addr = swab32(addr) | SF_RD_DATA_FAST;
+
+ ret = csio_hw_sf1_write(hw, 4, 1, 0, addr);
+ if (ret != 0)
+ return ret;
+
+ ret = csio_hw_sf1_read(hw, 1, 1, 0, data);
+ if (ret != 0)
+ return ret;
+
+ for ( ; nwords; nwords--, data++) {
+ ret = csio_hw_sf1_read(hw, 4, nwords > 1, nwords == 1, data);
+ if (nwords == 1)
+ csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */
+ if (ret)
+ return ret;
+ if (byte_oriented)
+ *data = (__force __u32) htonl(*data);
+ }
+ return 0;
+}
+
+/*
+ * csio_hw_write_flash - write up to a page of data to the serial flash
+ * @hw: the hw
+ * @addr: the start address to write
+ * @n: length of data to write in bytes
+ * @data: the data to write
+ *
+ * Writes up to a page of data (256 bytes) to the serial flash starting
+ * at the given address. All the data must be written to the same page.
+ */
+static int
+csio_hw_write_flash(struct csio_hw *hw, uint32_t addr,
+ uint32_t n, const uint8_t *data)
+{
+ int ret = -EINVAL;
+ uint32_t buf[64];
+ uint32_t i, c, left, val, offset = addr & 0xff;
+
+ if (addr >= hw->params.sf_size || offset + n > SF_PAGE_SIZE)
+ return -EINVAL;
+
+ val = swab32(addr) | SF_PROG_PAGE;
+
+ ret = csio_hw_sf1_write(hw, 1, 0, 1, SF_WR_ENABLE);
+ if (ret != 0)
+ goto unlock;
+
+ ret = csio_hw_sf1_write(hw, 4, 1, 1, val);
+ if (ret != 0)
+ goto unlock;
+
+ for (left = n; left; left -= c) {
+ c = min(left, 4U);
+ for (val = 0, i = 0; i < c; ++i)
+ val = (val << 8) + *data++;
+
+ ret = csio_hw_sf1_write(hw, c, c != left, 1, val);
+ if (ret)
+ goto unlock;
+ }
+ ret = csio_hw_flash_wait_op(hw, 8, 1);
+ if (ret)
+ goto unlock;
+
+ csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */
+
+ /* Read the page to verify the write succeeded */
+ ret = csio_hw_read_flash(hw, addr & ~0xff, ARRAY_SIZE(buf), buf, 1);
+ if (ret)
+ return ret;
+
+ if (memcmp(data - n, (uint8_t *)buf + offset, n)) {
+ csio_err(hw,
+ "failed to correctly write the flash page at %#x\n",
+ addr);
+ return -EINVAL;
+ }
+
+ return 0;
+
+unlock:
+ csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */
+ return ret;
+}
+
+/*
+ * csio_hw_flash_erase_sectors - erase a range of flash sectors
+ * @hw: the HW module
+ * @start: the first sector to erase
+ * @end: the last sector to erase
+ *
+ * Erases the sectors in the given inclusive range.
+ */
+static int
+csio_hw_flash_erase_sectors(struct csio_hw *hw, int32_t start, int32_t end)
+{
+ int ret = 0;
+
+ while (start <= end) {
+
+ ret = csio_hw_sf1_write(hw, 1, 0, 1, SF_WR_ENABLE);
+ if (ret != 0)
+ goto out;
+
+ ret = csio_hw_sf1_write(hw, 4, 0, 1,
+ SF_ERASE_SECTOR | (start << 8));
+ if (ret != 0)
+ goto out;
+
+ ret = csio_hw_flash_wait_op(hw, 14, 500);
+ if (ret != 0)
+ goto out;
+
+ start++;
+ }
+out:
+ if (ret)
+ csio_err(hw, "erase of flash sector %d failed, error %d\n",
+ start, ret);
+ csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */
+ return 0;
+}
+
+static void
+csio_hw_print_fw_version(struct csio_hw *hw, char *str)
+{
+ csio_info(hw, "%s: %u.%u.%u.%u\n", str,
+ FW_HDR_FW_VER_MAJOR_G(hw->fwrev),
+ FW_HDR_FW_VER_MINOR_G(hw->fwrev),
+ FW_HDR_FW_VER_MICRO_G(hw->fwrev),
+ FW_HDR_FW_VER_BUILD_G(hw->fwrev));
+}
+
+/*
+ * csio_hw_get_fw_version - read the firmware version
+ * @hw: HW module
+ * @vers: where to place the version
+ *
+ * Reads the FW version from flash.
+ */
+static int
+csio_hw_get_fw_version(struct csio_hw *hw, uint32_t *vers)
+{
+ return csio_hw_read_flash(hw, FLASH_FW_START +
+ offsetof(struct fw_hdr, fw_ver), 1,
+ vers, 0);
+}
+
+/*
+ * csio_hw_get_tp_version - read the TP microcode version
+ * @hw: HW module
+ * @vers: where to place the version
+ *
+ * Reads the TP microcode version from flash.
+ */
+static int
+csio_hw_get_tp_version(struct csio_hw *hw, u32 *vers)
+{
+ return csio_hw_read_flash(hw, FLASH_FW_START +
+ offsetof(struct fw_hdr, tp_microcode_ver), 1,
+ vers, 0);
+}
+
+/*
+ * csio_hw_fw_dload - download firmware.
+ * @hw: HW module
+ * @fw_data: firmware image to write.
+ * @size: image size
+ *
+ * Write the supplied firmware image to the card's serial flash.
+ */
+static int
+csio_hw_fw_dload(struct csio_hw *hw, uint8_t *fw_data, uint32_t size)
+{
+ uint32_t csum;
+ int32_t addr;
+ int ret;
+ uint32_t i;
+ uint8_t first_page[SF_PAGE_SIZE];
+ const __be32 *p = (const __be32 *)fw_data;
+ struct fw_hdr *hdr = (struct fw_hdr *)fw_data;
+ uint32_t sf_sec_size;
+
+ if ((!hw->params.sf_size) || (!hw->params.sf_nsec)) {
+ csio_err(hw, "Serial Flash data invalid\n");
+ return -EINVAL;
+ }
+
+ if (!size) {
+ csio_err(hw, "FW image has no data\n");
+ return -EINVAL;
+ }
+
+ if (size & 511) {
+ csio_err(hw, "FW image size not multiple of 512 bytes\n");
+ return -EINVAL;
+ }
+
+ if (ntohs(hdr->len512) * 512 != size) {
+ csio_err(hw, "FW image size differs from size in FW header\n");
+ return -EINVAL;
+ }
+
+ if (size > FLASH_FW_MAX_SIZE) {
+ csio_err(hw, "FW image too large, max is %u bytes\n",
+ FLASH_FW_MAX_SIZE);
+ return -EINVAL;
+ }
+
+ for (csum = 0, i = 0; i < size / sizeof(csum); i++)
+ csum += ntohl(p[i]);
+
+ if (csum != 0xffffffff) {
+ csio_err(hw, "corrupted firmware image, checksum %#x\n", csum);
+ return -EINVAL;
+ }
+
+ sf_sec_size = hw->params.sf_size / hw->params.sf_nsec;
+ i = DIV_ROUND_UP(size, sf_sec_size); /* # of sectors spanned */
+
+ csio_dbg(hw, "Erasing sectors... start:%d end:%d\n",
+ FLASH_FW_START_SEC, FLASH_FW_START_SEC + i - 1);
+
+ ret = csio_hw_flash_erase_sectors(hw, FLASH_FW_START_SEC,
+ FLASH_FW_START_SEC + i - 1);
+ if (ret) {
+ csio_err(hw, "Flash Erase failed\n");
+ goto out;
+ }
+
+ /*
+ * We write the correct version at the end so the driver can see a bad
+ * version if the FW write fails. Start by writing a copy of the
+ * first page with a bad version.
+ */
+ memcpy(first_page, fw_data, SF_PAGE_SIZE);
+ ((struct fw_hdr *)first_page)->fw_ver = htonl(0xffffffff);
+ ret = csio_hw_write_flash(hw, FLASH_FW_START, SF_PAGE_SIZE, first_page);
+ if (ret)
+ goto out;
+
+ csio_dbg(hw, "Writing Flash .. start:%d end:%d\n",
+ FW_IMG_START, FW_IMG_START + size);
+
+ addr = FLASH_FW_START;
+ for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) {
+ addr += SF_PAGE_SIZE;
+ fw_data += SF_PAGE_SIZE;
+ ret = csio_hw_write_flash(hw, addr, SF_PAGE_SIZE, fw_data);
+ if (ret)
+ goto out;
+ }
+
+ ret = csio_hw_write_flash(hw,
+ FLASH_FW_START +
+ offsetof(struct fw_hdr, fw_ver),
+ sizeof(hdr->fw_ver),
+ (const uint8_t *)&hdr->fw_ver);
+
+out:
+ if (ret)
+ csio_err(hw, "firmware download failed, error %d\n", ret);
+ return ret;
+}
+
+static int
+csio_hw_get_flash_params(struct csio_hw *hw)
+{
+ /* Table for non-Numonix supported flash parts. Numonix parts are left
+ * to the preexisting code. All flash parts have 64KB sectors.
+ */
+ static struct flash_desc {
+ u32 vendor_and_model_id;
+ u32 size_mb;
+ } supported_flash[] = {
+ { 0x150201, 4 << 20 }, /* Spansion 4MB S25FL032P */
+ };
+
+ u32 part, manufacturer;
+ u32 density, size = 0;
+ u32 flashid = 0;
+ int ret;
+
+ ret = csio_hw_sf1_write(hw, 1, 1, 0, SF_RD_ID);
+ if (!ret)
+ ret = csio_hw_sf1_read(hw, 3, 0, 1, &flashid);
+ csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */
+ if (ret)
+ return ret;
+
+ /* Check to see if it's one of our non-standard supported Flash parts.
+ */
+ for (part = 0; part < ARRAY_SIZE(supported_flash); part++)
+ if (supported_flash[part].vendor_and_model_id == flashid) {
+ hw->params.sf_size = supported_flash[part].size_mb;
+ hw->params.sf_nsec =
+ hw->params.sf_size / SF_SEC_SIZE;
+ goto found;
+ }
+
+ /* Decode Flash part size. The code below looks repetitive with
+ * common encodings, but that's not guaranteed in the JEDEC
+ * specification for the Read JEDEC ID command. The only thing that
+ * we're guaranteed by the JEDEC specification is where the
+ * Manufacturer ID is in the returned result. After that each
+ * Manufacturer ~could~ encode things completely differently.
+ * Note, all Flash parts must have 64KB sectors.
+ */
+ manufacturer = flashid & 0xff;
+ switch (manufacturer) {
+ case 0x20: { /* Micron/Numonix */
+ /* This Density -> Size decoding table is taken from Micron
+ * Data Sheets.
+ */
+ density = (flashid >> 16) & 0xff;
+ switch (density) {
+ case 0x14 ... 0x19: /* 1MB - 32MB */
+ size = 1 << density;
+ break;
+ case 0x20: /* 64MB */
+ size = 1 << 26;
+ break;
+ case 0x21: /* 128MB */
+ size = 1 << 27;
+ break;
+ case 0x22: /* 256MB */
+ size = 1 << 28;
+ }
+ break;
+ }
+ case 0x9d: { /* ISSI -- Integrated Silicon Solution, Inc. */
+ /* This Density -> Size decoding table is taken from ISSI
+ * Data Sheets.
+ */
+ density = (flashid >> 16) & 0xff;
+ switch (density) {
+ case 0x16: /* 32 MB */
+ size = 1 << 25;
+ break;
+ case 0x17: /* 64MB */
+ size = 1 << 26;
+ }
+ break;
+ }
+ case 0xc2: /* Macronix */
+ case 0xef: /* Winbond */ {
+ /* This Density -> Size decoding table is taken from
+ * Macronix and Winbond Data Sheets.
+ */
+ density = (flashid >> 16) & 0xff;
+ switch (density) {
+ case 0x17: /* 8MB */
+ case 0x18: /* 16MB */
+ size = 1 << density;
+ }
+ }
+ }
+
+ /* If we didn't recognize the FLASH part, that's no real issue: the
+ * Hardware/Software contract says that Hardware will _*ALWAYS*_
+ * use a FLASH part which is at least 4MB in size and has 64KB
+ * sectors. The unrecognized FLASH part is likely to be much larger
+ * than 4MB, but that's all we really need.
+ */
+ if (size == 0) {
+ csio_warn(hw, "Unknown Flash Part, ID = %#x, assuming 4MB\n",
+ flashid);
+ size = 1 << 22;
+ }
+
+ /* Store decoded Flash size */
+ hw->params.sf_size = size;
+ hw->params.sf_nsec = size / SF_SEC_SIZE;
+
+found:
+ if (hw->params.sf_size < FLASH_MIN_SIZE)
+ csio_warn(hw, "WARNING: Flash Part ID %#x, size %#x < %#x\n",
+ flashid, hw->params.sf_size, FLASH_MIN_SIZE);
+ return 0;
+}
+
+/*****************************************************************************/
+/* HW State machine assists */
+/*****************************************************************************/
+
+static int
+csio_hw_dev_ready(struct csio_hw *hw)
+{
+ uint32_t reg;
+ int cnt = 6;
+ int src_pf;
+
+ while (((reg = csio_rd_reg32(hw, PL_WHOAMI_A)) == 0xFFFFFFFF) &&
+ (--cnt != 0))
+ mdelay(100);
+
+ if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
+ src_pf = SOURCEPF_G(reg);
+ else
+ src_pf = T6_SOURCEPF_G(reg);
+
+ if ((cnt == 0) && (((int32_t)(src_pf) < 0) ||
+ (src_pf >= CSIO_MAX_PFN))) {
+ csio_err(hw, "PL_WHOAMI returned 0x%x, cnt:%d\n", reg, cnt);
+ return -EIO;
+ }
+
+ hw->pfn = src_pf;
+
+ return 0;
+}
+
+/*
+ * csio_do_hello - Perform the HELLO FW Mailbox command and process response.
+ * @hw: HW module
+ * @state: Device state
+ *
+ * FW_HELLO_CMD has to be polled for completion.
+ */
+static int
+csio_do_hello(struct csio_hw *hw, enum csio_dev_state *state)
+{
+ struct csio_mb *mbp;
+ int rv = 0;
+ enum fw_retval retval;
+ uint8_t mpfn;
+ char state_str[16];
+ int retries = FW_CMD_HELLO_RETRIES;
+
+ memset(state_str, 0, sizeof(state_str));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ rv = -ENOMEM;
+ CSIO_INC_STATS(hw, n_err_nomem);
+ goto out;
+ }
+
+retry:
+ csio_mb_hello(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn,
+ hw->pfn, CSIO_MASTER_MAY, NULL);
+
+ rv = csio_mb_issue(hw, mbp);
+ if (rv) {
+ csio_err(hw, "failed to issue HELLO cmd. ret:%d.\n", rv);
+ goto out_free_mb;
+ }
+
+ csio_mb_process_hello_rsp(hw, mbp, &retval, state, &mpfn);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "HELLO cmd failed with ret: %d\n", retval);
+ rv = -EINVAL;
+ goto out_free_mb;
+ }
+
+ /* Firmware has designated us to be master */
+ if (hw->pfn == mpfn) {
+ hw->flags |= CSIO_HWF_MASTER;
+ } else if (*state == CSIO_DEV_STATE_UNINIT) {
+ /*
+ * If we're not the Master PF then we need to wait around for
+ * the Master PF Driver to finish setting up the adapter.
+ *
+ * Note that we also do this wait if we're a non-Master-capable
+ * PF and there is no current Master PF; a Master PF may show up
+ * momentarily and we wouldn't want to fail pointlessly. (This
+ * can happen when an OS loads lots of different drivers rapidly
+ * at the same time). In this case, the Master PF returned by
+ * the firmware will be PCIE_FW_MASTER_MASK so the test below
+ * will work ...
+ */
+
+ int waiting = FW_CMD_HELLO_TIMEOUT;
+
+ /*
+ * Wait for the firmware to either indicate an error or
+ * initialized state. If we see either of these we bail out
+ * and report the issue to the caller. If we exhaust the
+ * "hello timeout" and we haven't exhausted our retries, try
+ * again. Otherwise bail with a timeout error.
+ */
+ for (;;) {
+ uint32_t pcie_fw;
+
+ spin_unlock_irq(&hw->lock);
+ msleep(50);
+ spin_lock_irq(&hw->lock);
+ waiting -= 50;
+
+ /*
+ * If neither Error nor Initialized are indicated
+ * by the firmware keep waiting till we exhaust our
+ * timeout ... and then retry if we haven't exhausted
+ * our retries ...
+ */
+ pcie_fw = csio_rd_reg32(hw, PCIE_FW_A);
+ if (!(pcie_fw & (PCIE_FW_ERR_F|PCIE_FW_INIT_F))) {
+ if (waiting <= 0) {
+ if (retries-- > 0)
+ goto retry;
+
+ rv = -ETIMEDOUT;
+ break;
+ }
+ continue;
+ }
+
+ /*
+ * We either have an Error or Initialized condition
+ * report errors preferentially.
+ */
+ if (state) {
+ if (pcie_fw & PCIE_FW_ERR_F) {
+ *state = CSIO_DEV_STATE_ERR;
+ rv = -ETIMEDOUT;
+ } else if (pcie_fw & PCIE_FW_INIT_F)
+ *state = CSIO_DEV_STATE_INIT;
+ }
+
+ /*
+ * If we arrived before a Master PF was selected and
+ * there's not a valid Master PF, grab its identity
+ * for our caller.
+ */
+ if (mpfn == PCIE_FW_MASTER_M &&
+ (pcie_fw & PCIE_FW_MASTER_VLD_F))
+ mpfn = PCIE_FW_MASTER_G(pcie_fw);
+ break;
+ }
+ hw->flags &= ~CSIO_HWF_MASTER;
+ }
+
+ switch (*state) {
+ case CSIO_DEV_STATE_UNINIT:
+ strcpy(state_str, "Initializing");
+ break;
+ case CSIO_DEV_STATE_INIT:
+ strcpy(state_str, "Initialized");
+ break;
+ case CSIO_DEV_STATE_ERR:
+ strcpy(state_str, "Error");
+ break;
+ default:
+ strcpy(state_str, "Unknown");
+ break;
+ }
+
+ if (hw->pfn == mpfn)
+ csio_info(hw, "PF: %d, Coming up as MASTER, HW state: %s\n",
+ hw->pfn, state_str);
+ else
+ csio_info(hw,
+ "PF: %d, Coming up as SLAVE, Master PF: %d, HW state: %s\n",
+ hw->pfn, mpfn, state_str);
+
+out_free_mb:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ return rv;
+}
+
+/*
+ * csio_do_bye - Perform the BYE FW Mailbox command and process response.
+ * @hw: HW module
+ *
+ */
+static int
+csio_do_bye(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ csio_mb_bye(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of BYE command failed\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_do_reset- Perform the device reset.
+ * @hw: HW module
+ * @fw_rst: FW reset
+ *
+ * If fw_rst is set, issues FW reset mbox cmd otherwise
+ * does PIO reset.
+ * Performs reset of the function.
+ */
+static int
+csio_do_reset(struct csio_hw *hw, bool fw_rst)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ if (!fw_rst) {
+ /* PIO reset */
+ csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A);
+ mdelay(2000);
+ return 0;
+ }
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ PIORSTMODE_F | PIORST_F, 0, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of RESET command failed.n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "RESET cmd failed with ret:0x%x.\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+static int
+csio_hw_validate_caps(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ struct fw_caps_config_cmd *rsp = (struct fw_caps_config_cmd *)mbp->mb;
+ uint16_t caps;
+
+ caps = ntohs(rsp->fcoecaps);
+
+ if (!(caps & FW_CAPS_CONFIG_FCOE_INITIATOR)) {
+ csio_err(hw, "No FCoE Initiator capability in the firmware.\n");
+ return -EINVAL;
+ }
+
+ if (!(caps & FW_CAPS_CONFIG_FCOE_CTRL_OFLD)) {
+ csio_err(hw, "No FCoE Control Offload capability\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_hw_fw_halt - issue a reset/halt to FW and put uP into RESET
+ * @hw: the HW module
+ * @mbox: mailbox to use for the FW RESET command (if desired)
+ * @force: force uP into RESET even if FW RESET command fails
+ *
+ * Issues a RESET command to firmware (if desired) with a HALT indication
+ * and then puts the microprocessor into RESET state. The RESET command
+ * will only be issued if a legitimate mailbox is provided (mbox <=
+ * PCIE_FW_MASTER_MASK).
+ *
+ * This is generally used in order for the host to safely manipulate the
+ * adapter without fear of conflicting with whatever the firmware might
+ * be doing. The only way out of this state is to RESTART the firmware
+ * ...
+ */
+static int
+csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force)
+{
+ enum fw_retval retval = 0;
+
+ /*
+ * If a legitimate mailbox is provided, issue a RESET command
+ * with a HALT indication.
+ */
+ if (mbox <= PCIE_FW_MASTER_M) {
+ struct csio_mb *mbp;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ PIORSTMODE_F | PIORST_F, FW_RESET_CMD_HALT_F,
+ NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of RESET command failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ mempool_free(mbp, hw->mb_mempool);
+ }
+
+ /*
+ * Normally we won't complete the operation if the firmware RESET
+ * command fails but if our caller insists we'll go ahead and put the
+ * uP into RESET. This can be useful if the firmware is hung or even
+ * missing ... We'll have to take the risk of putting the uP into
+ * RESET without the cooperation of firmware in that case.
+ *
+ * We also force the firmware's HALT flag to be on in case we bypassed
+ * the firmware RESET command above or we're dealing with old firmware
+ * which doesn't have the HALT capability. This will serve as a flag
+ * for the incoming firmware to know that it's coming out of a HALT
+ * rather than a RESET ... if it's new enough to understand that ...
+ */
+ if (retval == 0 || force) {
+ csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, UPCRST_F);
+ csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F,
+ PCIE_FW_HALT_F);
+ }
+
+ /*
+ * And we always return the result of the firmware RESET command
+ * even when we force the uP into RESET ...
+ */
+ return retval ? -EINVAL : 0;
+}
+
+/*
+ * csio_hw_fw_restart - restart the firmware by taking the uP out of RESET
+ * @hw: the HW module
+ * @reset: if we want to do a RESET to restart things
+ *
+ * Restart firmware previously halted by csio_hw_fw_halt(). On successful
+ * return the previous PF Master remains as the new PF Master and there
+ * is no need to issue a new HELLO command, etc.
+ *
+ * We do this in two ways:
+ *
+ * 1. If we're dealing with newer firmware we'll simply want to take
+ * the chip's microprocessor out of RESET. This will cause the
+ * firmware to start up from its start vector. And then we'll loop
+ * until the firmware indicates it's started again (PCIE_FW.HALT
+ * reset to 0) or we timeout.
+ *
+ * 2. If we're dealing with older firmware then we'll need to RESET
+ * the chip since older firmware won't recognize the PCIE_FW.HALT
+ * flag and automatically RESET itself on startup.
+ */
+static int
+csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset)
+{
+ if (reset) {
+ /*
+ * Since we're directing the RESET instead of the firmware
+ * doing it automatically, we need to clear the PCIE_FW.HALT
+ * bit.
+ */
+ csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F, 0);
+
+ /*
+ * If we've been given a valid mailbox, first try to get the
+ * firmware to do the RESET. If that works, great and we can
+ * return success. Otherwise, if we haven't been given a
+ * valid mailbox or the RESET command failed, fall back to
+ * hitting the chip with a hammer.
+ */
+ if (mbox <= PCIE_FW_MASTER_M) {
+ csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0);
+ msleep(100);
+ if (csio_do_reset(hw, true) == 0)
+ return 0;
+ }
+
+ csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A);
+ msleep(2000);
+ } else {
+ int ms;
+
+ csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0);
+ for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) {
+ if (!(csio_rd_reg32(hw, PCIE_FW_A) & PCIE_FW_HALT_F))
+ return 0;
+ msleep(100);
+ ms += 100;
+ }
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/*
+ * csio_hw_fw_upgrade - perform all of the steps necessary to upgrade FW
+ * @hw: the HW module
+ * @mbox: mailbox to use for the FW RESET command (if desired)
+ * @fw_data: the firmware image to write
+ * @size: image size
+ * @force: force upgrade even if firmware doesn't cooperate
+ *
+ * Perform all of the steps necessary for upgrading an adapter's
+ * firmware image. Normally this requires the cooperation of the
+ * existing firmware in order to halt all existing activities
+ * but if an invalid mailbox token is passed in we skip that step
+ * (though we'll still put the adapter microprocessor into RESET in
+ * that case).
+ *
+ * On successful return the new firmware will have been loaded and
+ * the adapter will have been fully RESET losing all previous setup
+ * state. On unsuccessful return the adapter may be completely hosed ...
+ * positive errno indicates that the adapter is ~probably~ intact, a
+ * negative errno indicates that things are looking bad ...
+ */
+static int
+csio_hw_fw_upgrade(struct csio_hw *hw, uint32_t mbox,
+ const u8 *fw_data, uint32_t size, int32_t force)
+{
+ const struct fw_hdr *fw_hdr = (const struct fw_hdr *)fw_data;
+ int reset, ret;
+
+ ret = csio_hw_fw_halt(hw, mbox, force);
+ if (ret != 0 && !force)
+ return ret;
+
+ ret = csio_hw_fw_dload(hw, (uint8_t *) fw_data, size);
+ if (ret != 0)
+ return ret;
+
+ /*
+ * Older versions of the firmware don't understand the new
+ * PCIE_FW.HALT flag and so won't know to perform a RESET when they
+ * restart. So for newly loaded older firmware we'll have to do the
+ * RESET for it so it starts up on a clean slate. We can tell if
+ * the newly loaded firmware will handle this right by checking
+ * its header flags to see if it advertises the capability.
+ */
+ reset = ((ntohl(fw_hdr->flags) & FW_HDR_FLAGS_RESET_HALT) == 0);
+ return csio_hw_fw_restart(hw, mbox, reset);
+}
+
+/*
+ * csio_get_device_params - Get device parameters.
+ * @hw: HW module
+ *
+ */
+static int
+csio_get_device_params(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ u32 param[6];
+ int i, j = 0;
+
+ /* Initialize portids to -1 */
+ for (i = 0; i < CSIO_MAX_PPORTS; i++)
+ hw->pport[i].portid = -1;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get port vec information. */
+ param[0] = FW_PARAM_DEV(PORTVEC);
+
+ /* Get Core clock. */
+ param[1] = FW_PARAM_DEV(CCLK);
+
+ /* Get EQ id start and end. */
+ param[2] = FW_PARAM_PFVF(EQ_START);
+ param[3] = FW_PARAM_PFVF(EQ_END);
+
+ /* Get IQ id start and end. */
+ param[4] = FW_PARAM_PFVF(IQFLINT_START);
+ param[5] = FW_PARAM_PFVF(IQFLINT_END);
+
+ csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn, 0,
+ ARRAY_SIZE(param), param, NULL, false, NULL);
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_PARAMS_CMD(read) failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_params_rsp(hw, mbp, &retval,
+ ARRAY_SIZE(param), param);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PARAMS_CMD(read) failed with ret:0x%x!\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ /* cache the information. */
+ hw->port_vec = param[0];
+ hw->vpd.cclk = param[1];
+ wrm->fw_eq_start = param[2];
+ wrm->fw_iq_start = param[4];
+
+ /* Using FW configured max iqs & eqs */
+ if ((hw->flags & CSIO_HWF_USING_SOFT_PARAMS) ||
+ !csio_is_hw_master(hw)) {
+ hw->cfg_niq = param[5] - param[4] + 1;
+ hw->cfg_neq = param[3] - param[2] + 1;
+ csio_dbg(hw, "Using fwconfig max niqs %d neqs %d\n",
+ hw->cfg_niq, hw->cfg_neq);
+ }
+
+ hw->port_vec &= csio_port_mask;
+
+ hw->num_pports = hweight32(hw->port_vec);
+
+ csio_dbg(hw, "Port vector: 0x%x, #ports: %d\n",
+ hw->port_vec, hw->num_pports);
+
+ for (i = 0; i < hw->num_pports; i++) {
+ while ((hw->port_vec & (1 << j)) == 0)
+ j++;
+ hw->pport[i].portid = j++;
+ csio_dbg(hw, "Found Port:%d\n", hw->pport[i].portid);
+ }
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+
+/*
+ * csio_config_device_caps - Get and set device capabilities.
+ * @hw: HW module
+ *
+ */
+static int
+csio_config_device_caps(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ int rv = -EINVAL;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get device capabilities */
+ csio_mb_caps_config(hw, mbp, CSIO_MB_DEFAULT_TMO, 0, 0, 0, 0, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD(r) failed!\n");
+ goto out;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_CAPS_CONFIG_CMD(r) returned %d!\n", retval);
+ goto out;
+ }
+
+ /* Validate device capabilities */
+ rv = csio_hw_validate_caps(hw, mbp);
+ if (rv != 0)
+ goto out;
+
+ /* Don't config device capabilities if already configured */
+ if (hw->fw_state == CSIO_DEV_STATE_INIT) {
+ rv = 0;
+ goto out;
+ }
+
+ /* Write back desired device capabilities */
+ csio_mb_caps_config(hw, mbp, CSIO_MB_DEFAULT_TMO, true, true,
+ false, true, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD(w) failed!\n");
+ goto out;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_CAPS_CONFIG_CMD(w) returned %d!\n", retval);
+ goto out;
+ }
+
+ rv = 0;
+out:
+ mempool_free(mbp, hw->mb_mempool);
+ return rv;
+}
+
+static inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec)
+{
+ enum cc_fec cc_fec = 0;
+
+ if (fw_fec & FW_PORT_CAP32_FEC_RS)
+ cc_fec |= FEC_RS;
+ if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS)
+ cc_fec |= FEC_BASER_RS;
+
+ return cc_fec;
+}
+
+static inline fw_port_cap32_t cc_to_fwcap_pause(enum cc_pause cc_pause)
+{
+ fw_port_cap32_t fw_pause = 0;
+
+ if (cc_pause & PAUSE_RX)
+ fw_pause |= FW_PORT_CAP32_FC_RX;
+ if (cc_pause & PAUSE_TX)
+ fw_pause |= FW_PORT_CAP32_FC_TX;
+
+ return fw_pause;
+}
+
+static inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec)
+{
+ fw_port_cap32_t fw_fec = 0;
+
+ if (cc_fec & FEC_RS)
+ fw_fec |= FW_PORT_CAP32_FEC_RS;
+ if (cc_fec & FEC_BASER_RS)
+ fw_fec |= FW_PORT_CAP32_FEC_BASER_RS;
+
+ return fw_fec;
+}
+
+/**
+ * fwcap_to_fwspeed - return highest speed in Port Capabilities
+ * @acaps: advertised Port Capabilities
+ *
+ * Get the highest speed for the port from the advertised Port
+ * Capabilities.
+ */
+fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps)
+{
+ #define TEST_SPEED_RETURN(__caps_speed) \
+ do { \
+ if (acaps & FW_PORT_CAP32_SPEED_##__caps_speed) \
+ return FW_PORT_CAP32_SPEED_##__caps_speed; \
+ } while (0)
+
+ TEST_SPEED_RETURN(400G);
+ TEST_SPEED_RETURN(200G);
+ TEST_SPEED_RETURN(100G);
+ TEST_SPEED_RETURN(50G);
+ TEST_SPEED_RETURN(40G);
+ TEST_SPEED_RETURN(25G);
+ TEST_SPEED_RETURN(10G);
+ TEST_SPEED_RETURN(1G);
+ TEST_SPEED_RETURN(100M);
+
+ #undef TEST_SPEED_RETURN
+
+ return 0;
+}
+
+/**
+ * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits
+ * @caps16: a 16-bit Port Capabilities value
+ *
+ * Returns the equivalent 32-bit Port Capabilities value.
+ */
+fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16)
+{
+ fw_port_cap32_t caps32 = 0;
+
+ #define CAP16_TO_CAP32(__cap) \
+ do { \
+ if (caps16 & FW_PORT_CAP_##__cap) \
+ caps32 |= FW_PORT_CAP32_##__cap; \
+ } while (0)
+
+ CAP16_TO_CAP32(SPEED_100M);
+ CAP16_TO_CAP32(SPEED_1G);
+ CAP16_TO_CAP32(SPEED_25G);
+ CAP16_TO_CAP32(SPEED_10G);
+ CAP16_TO_CAP32(SPEED_40G);
+ CAP16_TO_CAP32(SPEED_100G);
+ CAP16_TO_CAP32(FC_RX);
+ CAP16_TO_CAP32(FC_TX);
+ CAP16_TO_CAP32(ANEG);
+ CAP16_TO_CAP32(MDIAUTO);
+ CAP16_TO_CAP32(MDISTRAIGHT);
+ CAP16_TO_CAP32(FEC_RS);
+ CAP16_TO_CAP32(FEC_BASER_RS);
+ CAP16_TO_CAP32(802_3_PAUSE);
+ CAP16_TO_CAP32(802_3_ASM_DIR);
+
+ #undef CAP16_TO_CAP32
+
+ return caps32;
+}
+
+/**
+ * fwcaps32_to_caps16 - convert 32-bit Port Capabilities to 16-bits
+ * @caps32: a 32-bit Port Capabilities value
+ *
+ * Returns the equivalent 16-bit Port Capabilities value. Note that
+ * not all 32-bit Port Capabilities can be represented in the 16-bit
+ * Port Capabilities and some fields/values may not make it.
+ */
+fw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32)
+{
+ fw_port_cap16_t caps16 = 0;
+
+ #define CAP32_TO_CAP16(__cap) \
+ do { \
+ if (caps32 & FW_PORT_CAP32_##__cap) \
+ caps16 |= FW_PORT_CAP_##__cap; \
+ } while (0)
+
+ CAP32_TO_CAP16(SPEED_100M);
+ CAP32_TO_CAP16(SPEED_1G);
+ CAP32_TO_CAP16(SPEED_10G);
+ CAP32_TO_CAP16(SPEED_25G);
+ CAP32_TO_CAP16(SPEED_40G);
+ CAP32_TO_CAP16(SPEED_100G);
+ CAP32_TO_CAP16(FC_RX);
+ CAP32_TO_CAP16(FC_TX);
+ CAP32_TO_CAP16(802_3_PAUSE);
+ CAP32_TO_CAP16(802_3_ASM_DIR);
+ CAP32_TO_CAP16(ANEG);
+ CAP32_TO_CAP16(FORCE_PAUSE);
+ CAP32_TO_CAP16(MDIAUTO);
+ CAP32_TO_CAP16(MDISTRAIGHT);
+ CAP32_TO_CAP16(FEC_RS);
+ CAP32_TO_CAP16(FEC_BASER_RS);
+
+ #undef CAP32_TO_CAP16
+
+ return caps16;
+}
+
+/**
+ * lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities
+ * @lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value
+ *
+ * Translates old FW_PORT_ACTION_GET_PORT_INFO lstatus field into new
+ * 32-bit Port Capabilities value.
+ */
+fw_port_cap32_t lstatus_to_fwcap(u32 lstatus)
+{
+ fw_port_cap32_t linkattr = 0;
+
+ /* The format of the Link Status in the old
+ * 16-bit Port Information message isn't the same as the
+ * 16-bit Port Capabilities bitfield used everywhere else.
+ */
+ if (lstatus & FW_PORT_CMD_RXPAUSE_F)
+ linkattr |= FW_PORT_CAP32_FC_RX;
+ if (lstatus & FW_PORT_CMD_TXPAUSE_F)
+ linkattr |= FW_PORT_CAP32_FC_TX;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M))
+ linkattr |= FW_PORT_CAP32_SPEED_100M;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G))
+ linkattr |= FW_PORT_CAP32_SPEED_1G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G))
+ linkattr |= FW_PORT_CAP32_SPEED_10G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G))
+ linkattr |= FW_PORT_CAP32_SPEED_25G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G))
+ linkattr |= FW_PORT_CAP32_SPEED_40G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G))
+ linkattr |= FW_PORT_CAP32_SPEED_100G;
+
+ return linkattr;
+}
+
+/**
+ * csio_init_link_config - initialize a link's SW state
+ * @lc: pointer to structure holding the link state
+ * @pcaps: link Port Capabilities
+ * @acaps: link current Advertised Port Capabilities
+ *
+ * Initializes the SW state maintained for each link, including the link's
+ * capabilities and default speed/flow-control/autonegotiation settings.
+ */
+static void csio_init_link_config(struct link_config *lc, fw_port_cap32_t pcaps,
+ fw_port_cap32_t acaps)
+{
+ lc->pcaps = pcaps;
+ lc->def_acaps = acaps;
+ lc->lpacaps = 0;
+ lc->speed_caps = 0;
+ lc->speed = 0;
+ lc->requested_fc = PAUSE_RX | PAUSE_TX;
+ lc->fc = lc->requested_fc;
+
+ /*
+ * For Forward Error Control, we default to whatever the Firmware
+ * tells us the Link is currently advertising.
+ */
+ lc->requested_fec = FEC_AUTO;
+ lc->fec = fwcap_to_cc_fec(lc->def_acaps);
+
+ /* If the Port is capable of Auto-Negtotiation, initialize it as
+ * "enabled" and copy over all of the Physical Port Capabilities
+ * to the Advertised Port Capabilities. Otherwise mark it as
+ * Auto-Negotiate disabled and select the highest supported speed
+ * for the link. Note parallel structure in t4_link_l1cfg_core()
+ * and t4_handle_get_port_info().
+ */
+ if (lc->pcaps & FW_PORT_CAP32_ANEG) {
+ lc->acaps = lc->pcaps & ADVERT_MASK;
+ lc->autoneg = AUTONEG_ENABLE;
+ lc->requested_fc |= PAUSE_AUTONEG;
+ } else {
+ lc->acaps = 0;
+ lc->autoneg = AUTONEG_DISABLE;
+ }
+}
+
+static void csio_link_l1cfg(struct link_config *lc, uint16_t fw_caps,
+ uint32_t *rcaps)
+{
+ unsigned int fw_mdi = FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO);
+ fw_port_cap32_t fw_fc, cc_fec, fw_fec, lrcap;
+
+ lc->link_ok = 0;
+
+ /*
+ * Convert driver coding of Pause Frame Flow Control settings into the
+ * Firmware's API.
+ */
+ fw_fc = cc_to_fwcap_pause(lc->requested_fc);
+
+ /*
+ * Convert Common Code Forward Error Control settings into the
+ * Firmware's API. If the current Requested FEC has "Automatic"
+ * (IEEE 802.3) specified, then we use whatever the Firmware
+ * sent us as part of it's IEEE 802.3-based interpretation of
+ * the Transceiver Module EPROM FEC parameters. Otherwise we
+ * use whatever is in the current Requested FEC settings.
+ */
+ if (lc->requested_fec & FEC_AUTO)
+ cc_fec = fwcap_to_cc_fec(lc->def_acaps);
+ else
+ cc_fec = lc->requested_fec;
+ fw_fec = cc_to_fwcap_fec(cc_fec);
+
+ /* Figure out what our Requested Port Capabilities are going to be.
+ * Note parallel structure in t4_handle_get_port_info() and
+ * init_link_config().
+ */
+ if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) {
+ lrcap = (lc->pcaps & ADVERT_MASK) | fw_fc | fw_fec;
+ lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
+ lc->fec = cc_fec;
+ } else if (lc->autoneg == AUTONEG_DISABLE) {
+ lrcap = lc->speed_caps | fw_fc | fw_fec | fw_mdi;
+ lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
+ lc->fec = cc_fec;
+ } else {
+ lrcap = lc->acaps | fw_fc | fw_fec | fw_mdi;
+ }
+
+ *rcaps = lrcap;
+}
+
+/*
+ * csio_enable_ports - Bring up all available ports.
+ * @hw: HW module.
+ *
+ */
+static int
+csio_enable_ports(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ u16 fw_caps = FW_CAPS_UNKNOWN;
+ enum fw_retval retval;
+ uint8_t portid;
+ fw_port_cap32_t pcaps, acaps, rcaps;
+ int i;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < hw->num_pports; i++) {
+ portid = hw->pport[i].portid;
+
+ if (fw_caps == FW_CAPS_UNKNOWN) {
+ u32 param, val;
+
+ param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) |
+ FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32));
+ val = 1;
+
+ csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ hw->pfn, 0, 1, &param, &val, true,
+ NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_PARAMS_CMD(r) port:%d\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_params_rsp(hw, mbp, &retval,
+ 0, NULL);
+ fw_caps = retval ? FW_CAPS16 : FW_CAPS32;
+ }
+
+ /* Read PORT information */
+ csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid,
+ false, 0, fw_caps, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_PORT_CMD(r) port:%d\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_port_rsp(hw, mbp, &retval, fw_caps,
+ &pcaps, &acaps);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PORT_CMD(r) port:%d failed: 0x%x\n",
+ portid, retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_init_link_config(&hw->pport[i].link_cfg, pcaps, acaps);
+
+ csio_link_l1cfg(&hw->pport[i].link_cfg, fw_caps, &rcaps);
+
+ /* Write back PORT information */
+ csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid,
+ true, rcaps, fw_caps, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_PORT_CMD(w) port:%d\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PORT_CMD(w) port:%d failed :0x%x\n",
+ portid, retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ } /* For all ports */
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_get_fcoe_resinfo - Read fcoe fw resource info.
+ * @hw: HW module
+ * Issued with lock held.
+ */
+static int
+csio_get_fcoe_resinfo(struct csio_hw *hw)
+{
+ struct csio_fcoe_res_info *res_info = &hw->fres_info;
+ struct fw_fcoe_res_info_cmd *rsp;
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get FCoE FW resource information */
+ csio_fcoe_read_res_info_init_mb(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_FCOE_RES_INFO_CMD\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ rsp = (struct fw_fcoe_res_info_cmd *)(mbp->mb);
+ retval = FW_CMD_RETVAL_G(ntohl(rsp->retval_len16));
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_FCOE_RES_INFO_CMD failed with ret x%x\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ res_info->e_d_tov = ntohs(rsp->e_d_tov);
+ res_info->r_a_tov_seq = ntohs(rsp->r_a_tov_seq);
+ res_info->r_a_tov_els = ntohs(rsp->r_a_tov_els);
+ res_info->r_r_tov = ntohs(rsp->r_r_tov);
+ res_info->max_xchgs = ntohl(rsp->max_xchgs);
+ res_info->max_ssns = ntohl(rsp->max_ssns);
+ res_info->used_xchgs = ntohl(rsp->used_xchgs);
+ res_info->used_ssns = ntohl(rsp->used_ssns);
+ res_info->max_fcfs = ntohl(rsp->max_fcfs);
+ res_info->max_vnps = ntohl(rsp->max_vnps);
+ res_info->used_fcfs = ntohl(rsp->used_fcfs);
+ res_info->used_vnps = ntohl(rsp->used_vnps);
+
+ csio_dbg(hw, "max ssns:%d max xchgs:%d\n", res_info->max_ssns,
+ res_info->max_xchgs);
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+static int
+csio_hw_check_fwconfig(struct csio_hw *hw, u32 *param)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ u32 _param[1];
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /*
+ * Find out whether we're dealing with a version of
+ * the firmware which has configuration file support.
+ */
+ _param[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
+ FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_CF));
+
+ csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn, 0,
+ ARRAY_SIZE(_param), _param, NULL, false, NULL);
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_PARAMS_CMD(read) failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_params_rsp(hw, mbp, &retval,
+ ARRAY_SIZE(_param), _param);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PARAMS_CMD(read) failed with ret:0x%x!\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+ *param = _param[0];
+
+ return 0;
+}
+
+static int
+csio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path)
+{
+ int ret = 0;
+ const struct firmware *cf;
+ struct pci_dev *pci_dev = hw->pdev;
+ struct device *dev = &pci_dev->dev;
+ unsigned int mtype = 0, maddr = 0;
+ uint32_t *cfg_data;
+ int value_to_add = 0;
+ const char *fw_cfg_file;
+
+ if (csio_is_t5(pci_dev->device & CSIO_HW_CHIP_MASK))
+ fw_cfg_file = FW_CFG_NAME_T5;
+ else
+ fw_cfg_file = FW_CFG_NAME_T6;
+
+ if (request_firmware(&cf, fw_cfg_file, dev) < 0) {
+ csio_err(hw, "could not find config file %s, err: %d\n",
+ fw_cfg_file, ret);
+ return -ENOENT;
+ }
+
+ if (cf->size%4 != 0)
+ value_to_add = 4 - (cf->size % 4);
+
+ cfg_data = kzalloc(cf->size+value_to_add, GFP_KERNEL);
+ if (cfg_data == NULL) {
+ ret = -ENOMEM;
+ goto leave;
+ }
+
+ memcpy((void *)cfg_data, (const void *)cf->data, cf->size);
+ if (csio_hw_check_fwconfig(hw, fw_cfg_param) != 0) {
+ ret = -EINVAL;
+ goto leave;
+ }
+
+ mtype = FW_PARAMS_PARAM_Y_G(*fw_cfg_param);
+ maddr = FW_PARAMS_PARAM_Z_G(*fw_cfg_param) << 16;
+
+ ret = csio_memory_write(hw, mtype, maddr,
+ cf->size + value_to_add, cfg_data);
+
+ if ((ret == 0) && (value_to_add != 0)) {
+ union {
+ u32 word;
+ char buf[4];
+ } last;
+ size_t size = cf->size & ~0x3;
+ int i;
+
+ last.word = cfg_data[size >> 2];
+ for (i = value_to_add; i < 4; i++)
+ last.buf[i] = 0;
+ ret = csio_memory_write(hw, mtype, maddr + size, 4, &last.word);
+ }
+ if (ret == 0) {
+ csio_info(hw, "config file upgraded to %s\n", fw_cfg_file);
+ snprintf(path, 64, "%s%s", "/lib/firmware/", fw_cfg_file);
+ }
+
+leave:
+ kfree(cfg_data);
+ release_firmware(cf);
+ return ret;
+}
+
+/*
+ * HW initialization: contact FW, obtain config, perform basic init.
+ *
+ * If the firmware we're dealing with has Configuration File support, then
+ * we use that to perform all configuration -- either using the configuration
+ * file stored in flash on the adapter or using a filesystem-local file
+ * if available.
+ *
+ * If we don't have configuration file support in the firmware, then we'll
+ * have to set things up the old fashioned way with hard-coded register
+ * writes and firmware commands ...
+ */
+
+/*
+ * Attempt to initialize the HW via a Firmware Configuration File.
+ */
+static int
+csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param)
+{
+ struct csio_mb *mbp = NULL;
+ struct fw_caps_config_cmd *caps_cmd;
+ unsigned int mtype, maddr;
+ int rv = -EINVAL;
+ uint32_t finiver = 0, finicsum = 0, cfcsum = 0;
+ char path[64];
+ char *config_name = NULL;
+
+ /*
+ * Reset device if necessary
+ */
+ if (reset) {
+ rv = csio_do_reset(hw, true);
+ if (rv != 0)
+ goto bye;
+ }
+
+ /*
+ * If we have a configuration file in host ,
+ * then use that. Otherwise, use the configuration file stored
+ * in the HW flash ...
+ */
+ spin_unlock_irq(&hw->lock);
+ rv = csio_hw_flash_config(hw, fw_cfg_param, path);
+ spin_lock_irq(&hw->lock);
+ if (rv != 0) {
+ /*
+ * config file was not found. Use default
+ * config file from flash.
+ */
+ config_name = "On FLASH";
+ mtype = FW_MEMTYPE_CF_FLASH;
+ maddr = hw->chip_ops->chip_flash_cfg_addr(hw);
+ } else {
+ config_name = path;
+ mtype = FW_PARAMS_PARAM_Y_G(*fw_cfg_param);
+ maddr = FW_PARAMS_PARAM_Z_G(*fw_cfg_param) << 16;
+ }
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+ /*
+ * Tell the firmware to process the indicated Configuration File.
+ * If there are no errors and the caller has provided return value
+ * pointers for the [fini] section version, checksum and computed
+ * checksum, pass those back to the caller.
+ */
+ caps_cmd = (struct fw_caps_config_cmd *)(mbp->mb);
+ CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1);
+ caps_cmd->op_to_write =
+ htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_READ_F);
+ caps_cmd->cfvalid_to_len16 =
+ htonl(FW_CAPS_CONFIG_CMD_CFVALID_F |
+ FW_CAPS_CONFIG_CMD_MEMTYPE_CF_V(mtype) |
+ FW_CAPS_CONFIG_CMD_MEMADDR64K_CF_V(maddr >> 16) |
+ FW_LEN16(*caps_cmd));
+
+ if (csio_mb_issue(hw, mbp)) {
+ rv = -EINVAL;
+ goto bye;
+ }
+
+ rv = csio_mb_fw_retval(mbp);
+ /* If the CAPS_CONFIG failed with an ENOENT (for a Firmware
+ * Configuration File in FLASH), our last gasp effort is to use the
+ * Firmware Configuration File which is embedded in the
+ * firmware. A very few early versions of the firmware didn't
+ * have one embedded but we can ignore those.
+ */
+ if (rv == ENOENT) {
+ CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1);
+ caps_cmd->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_READ_F);
+ caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd));
+
+ if (csio_mb_issue(hw, mbp)) {
+ rv = -EINVAL;
+ goto bye;
+ }
+
+ rv = csio_mb_fw_retval(mbp);
+ config_name = "Firmware Default";
+ }
+ if (rv != FW_SUCCESS)
+ goto bye;
+
+ finiver = ntohl(caps_cmd->finiver);
+ finicsum = ntohl(caps_cmd->finicsum);
+ cfcsum = ntohl(caps_cmd->cfcsum);
+
+ /*
+ * And now tell the firmware to use the configuration we just loaded.
+ */
+ caps_cmd->op_to_write =
+ htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_WRITE_F);
+ caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd));
+
+ if (csio_mb_issue(hw, mbp)) {
+ rv = -EINVAL;
+ goto bye;
+ }
+
+ rv = csio_mb_fw_retval(mbp);
+ if (rv != FW_SUCCESS) {
+ csio_dbg(hw, "FW_CAPS_CONFIG_CMD returned %d!\n", rv);
+ goto bye;
+ }
+
+ if (finicsum != cfcsum) {
+ csio_warn(hw,
+ "Config File checksum mismatch: csum=%#x, computed=%#x\n",
+ finicsum, cfcsum);
+ }
+
+ /* Validate device capabilities */
+ rv = csio_hw_validate_caps(hw, mbp);
+ if (rv != 0)
+ goto bye;
+
+ mempool_free(mbp, hw->mb_mempool);
+ mbp = NULL;
+
+ /*
+ * Note that we're operating with parameters
+ * not supplied by the driver, rather than from hard-wired
+ * initialization constants buried in the driver.
+ */
+ hw->flags |= CSIO_HWF_USING_SOFT_PARAMS;
+
+ /* device parameters */
+ rv = csio_get_device_params(hw);
+ if (rv != 0)
+ goto bye;
+
+ /* Configure SGE */
+ csio_wr_sge_init(hw);
+
+ /*
+ * And finally tell the firmware to initialize itself using the
+ * parameters from the Configuration File.
+ */
+ /* Post event to notify completion of configuration */
+ csio_post_event(&hw->sm, CSIO_HWE_INIT);
+
+ csio_info(hw, "Successfully configure using Firmware "
+ "Configuration File %s, version %#x, computed checksum %#x\n",
+ config_name, finiver, cfcsum);
+ return 0;
+
+ /*
+ * Something bad happened. Return the error ...
+ */
+bye:
+ if (mbp)
+ mempool_free(mbp, hw->mb_mempool);
+ hw->flags &= ~CSIO_HWF_USING_SOFT_PARAMS;
+ csio_warn(hw, "Configuration file error %d\n", rv);
+ return rv;
+}
+
+/* Is the given firmware API compatible with the one the driver was compiled
+ * with?
+ */
+static int fw_compatible(const struct fw_hdr *hdr1, const struct fw_hdr *hdr2)
+{
+
+ /* short circuit if it's the exact same firmware version */
+ if (hdr1->chip == hdr2->chip && hdr1->fw_ver == hdr2->fw_ver)
+ return 1;
+
+#define SAME_INTF(x) (hdr1->intfver_##x == hdr2->intfver_##x)
+ if (hdr1->chip == hdr2->chip && SAME_INTF(nic) && SAME_INTF(vnic) &&
+ SAME_INTF(ri) && SAME_INTF(iscsi) && SAME_INTF(fcoe))
+ return 1;
+#undef SAME_INTF
+
+ return 0;
+}
+
+/* The firmware in the filesystem is usable, but should it be installed?
+ * This routine explains itself in detail if it indicates the filesystem
+ * firmware should be installed.
+ */
+static int csio_should_install_fs_fw(struct csio_hw *hw, int card_fw_usable,
+ int k, int c)
+{
+ const char *reason;
+
+ if (!card_fw_usable) {
+ reason = "incompatible or unusable";
+ goto install;
+ }
+
+ if (k > c) {
+ reason = "older than the version supported with this driver";
+ goto install;
+ }
+
+ return 0;
+
+install:
+ csio_err(hw, "firmware on card (%u.%u.%u.%u) is %s, "
+ "installing firmware %u.%u.%u.%u on card.\n",
+ FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c),
+ FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c), reason,
+ FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k),
+ FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k));
+
+ return 1;
+}
+
+static struct fw_info fw_info_array[] = {
+ {
+ .chip = CHELSIO_T5,
+ .fs_name = FW_CFG_NAME_T5,
+ .fw_mod_name = FW_FNAME_T5,
+ .fw_hdr = {
+ .chip = FW_HDR_CHIP_T5,
+ .fw_ver = __cpu_to_be32(FW_VERSION(T5)),
+ .intfver_nic = FW_INTFVER(T5, NIC),
+ .intfver_vnic = FW_INTFVER(T5, VNIC),
+ .intfver_ri = FW_INTFVER(T5, RI),
+ .intfver_iscsi = FW_INTFVER(T5, ISCSI),
+ .intfver_fcoe = FW_INTFVER(T5, FCOE),
+ },
+ }, {
+ .chip = CHELSIO_T6,
+ .fs_name = FW_CFG_NAME_T6,
+ .fw_mod_name = FW_FNAME_T6,
+ .fw_hdr = {
+ .chip = FW_HDR_CHIP_T6,
+ .fw_ver = __cpu_to_be32(FW_VERSION(T6)),
+ .intfver_nic = FW_INTFVER(T6, NIC),
+ .intfver_vnic = FW_INTFVER(T6, VNIC),
+ .intfver_ri = FW_INTFVER(T6, RI),
+ .intfver_iscsi = FW_INTFVER(T6, ISCSI),
+ .intfver_fcoe = FW_INTFVER(T6, FCOE),
+ },
+ }
+};
+
+static struct fw_info *find_fw_info(int chip)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_info_array); i++) {
+ if (fw_info_array[i].chip == chip)
+ return &fw_info_array[i];
+ }
+ return NULL;
+}
+
+static int csio_hw_prep_fw(struct csio_hw *hw, struct fw_info *fw_info,
+ const u8 *fw_data, unsigned int fw_size,
+ struct fw_hdr *card_fw, enum csio_dev_state state,
+ int *reset)
+{
+ int ret, card_fw_usable, fs_fw_usable;
+ const struct fw_hdr *fs_fw;
+ const struct fw_hdr *drv_fw;
+
+ drv_fw = &fw_info->fw_hdr;
+
+ /* Read the header of the firmware on the card */
+ ret = csio_hw_read_flash(hw, FLASH_FW_START,
+ sizeof(*card_fw) / sizeof(uint32_t),
+ (uint32_t *)card_fw, 1);
+ if (ret == 0) {
+ card_fw_usable = fw_compatible(drv_fw, (const void *)card_fw);
+ } else {
+ csio_err(hw,
+ "Unable to read card's firmware header: %d\n", ret);
+ card_fw_usable = 0;
+ }
+
+ if (fw_data != NULL) {
+ fs_fw = (const void *)fw_data;
+ fs_fw_usable = fw_compatible(drv_fw, fs_fw);
+ } else {
+ fs_fw = NULL;
+ fs_fw_usable = 0;
+ }
+
+ if (card_fw_usable && card_fw->fw_ver == drv_fw->fw_ver &&
+ (!fs_fw_usable || fs_fw->fw_ver == drv_fw->fw_ver)) {
+ /* Common case: the firmware on the card is an exact match and
+ * the filesystem one is an exact match too, or the filesystem
+ * one is absent/incompatible.
+ */
+ } else if (fs_fw_usable && state == CSIO_DEV_STATE_UNINIT &&
+ csio_should_install_fs_fw(hw, card_fw_usable,
+ be32_to_cpu(fs_fw->fw_ver),
+ be32_to_cpu(card_fw->fw_ver))) {
+ ret = csio_hw_fw_upgrade(hw, hw->pfn, fw_data,
+ fw_size, 0);
+ if (ret != 0) {
+ csio_err(hw,
+ "failed to install firmware: %d\n", ret);
+ goto bye;
+ }
+
+ /* Installed successfully, update the cached header too. */
+ memcpy(card_fw, fs_fw, sizeof(*card_fw));
+ card_fw_usable = 1;
+ *reset = 0; /* already reset as part of load_fw */
+ }
+
+ if (!card_fw_usable) {
+ uint32_t d, c, k;
+
+ d = be32_to_cpu(drv_fw->fw_ver);
+ c = be32_to_cpu(card_fw->fw_ver);
+ k = fs_fw ? be32_to_cpu(fs_fw->fw_ver) : 0;
+
+ csio_err(hw, "Cannot find a usable firmware: "
+ "chip state %d, "
+ "driver compiled with %d.%d.%d.%d, "
+ "card has %d.%d.%d.%d, filesystem has %d.%d.%d.%d\n",
+ state,
+ FW_HDR_FW_VER_MAJOR_G(d), FW_HDR_FW_VER_MINOR_G(d),
+ FW_HDR_FW_VER_MICRO_G(d), FW_HDR_FW_VER_BUILD_G(d),
+ FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c),
+ FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c),
+ FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k),
+ FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k));
+ ret = -EINVAL;
+ goto bye;
+ }
+
+ /* We're using whatever's on the card and it's known to be good. */
+ hw->fwrev = be32_to_cpu(card_fw->fw_ver);
+ hw->tp_vers = be32_to_cpu(card_fw->tp_microcode_ver);
+
+bye:
+ return ret;
+}
+
+/*
+ * Returns -EINVAL if attempts to flash the firmware failed,
+ * -ENOMEM if memory allocation failed else returns 0,
+ * if flashing was not attempted because the card had the
+ * latest firmware ECANCELED is returned
+ */
+static int
+csio_hw_flash_fw(struct csio_hw *hw, int *reset)
+{
+ int ret = -ECANCELED;
+ const struct firmware *fw;
+ struct fw_info *fw_info;
+ struct fw_hdr *card_fw;
+ struct pci_dev *pci_dev = hw->pdev;
+ struct device *dev = &pci_dev->dev ;
+ const u8 *fw_data = NULL;
+ unsigned int fw_size = 0;
+ const char *fw_bin_file;
+
+ /* This is the firmware whose headers the driver was compiled
+ * against
+ */
+ fw_info = find_fw_info(CHELSIO_CHIP_VERSION(hw->chip_id));
+ if (fw_info == NULL) {
+ csio_err(hw,
+ "unable to get firmware info for chip %d.\n",
+ CHELSIO_CHIP_VERSION(hw->chip_id));
+ return -EINVAL;
+ }
+
+ /* allocate memory to read the header of the firmware on the
+ * card
+ */
+ card_fw = kmalloc(sizeof(*card_fw), GFP_KERNEL);
+ if (!card_fw)
+ return -ENOMEM;
+
+ if (csio_is_t5(pci_dev->device & CSIO_HW_CHIP_MASK))
+ fw_bin_file = FW_FNAME_T5;
+ else
+ fw_bin_file = FW_FNAME_T6;
+
+ if (request_firmware(&fw, fw_bin_file, dev) < 0) {
+ csio_err(hw, "could not find firmware image %s, err: %d\n",
+ fw_bin_file, ret);
+ } else {
+ fw_data = fw->data;
+ fw_size = fw->size;
+ }
+
+ /* upgrade FW logic */
+ ret = csio_hw_prep_fw(hw, fw_info, fw_data, fw_size, card_fw,
+ hw->fw_state, reset);
+
+ /* Cleaning up */
+ if (fw != NULL)
+ release_firmware(fw);
+ kfree(card_fw);
+ return ret;
+}
+
+static int csio_hw_check_fwver(struct csio_hw *hw)
+{
+ if (csio_is_t6(hw->pdev->device & CSIO_HW_CHIP_MASK) &&
+ (hw->fwrev < CSIO_MIN_T6_FW)) {
+ csio_hw_print_fw_version(hw, "T6 unsupported fw");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_hw_configure - Configure HW
+ * @hw - HW module
+ *
+ */
+static void
+csio_hw_configure(struct csio_hw *hw)
+{
+ int reset = 1;
+ int rv;
+ u32 param[1];
+
+ rv = csio_hw_dev_ready(hw);
+ if (rv != 0) {
+ CSIO_INC_STATS(hw, n_err_fatal);
+ csio_post_event(&hw->sm, CSIO_HWE_FATAL);
+ goto out;
+ }
+
+ /* HW version */
+ hw->chip_ver = (char)csio_rd_reg32(hw, PL_REV_A);
+
+ /* Needed for FW download */
+ rv = csio_hw_get_flash_params(hw);
+ if (rv != 0) {
+ csio_err(hw, "Failed to get serial flash params rv:%d\n", rv);
+ csio_post_event(&hw->sm, CSIO_HWE_FATAL);
+ goto out;
+ }
+
+ /* Set PCIe completion timeout to 4 seconds */
+ if (pci_is_pcie(hw->pdev))
+ pcie_capability_clear_and_set_word(hw->pdev, PCI_EXP_DEVCTL2,
+ PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0xd);
+
+ hw->chip_ops->chip_set_mem_win(hw, MEMWIN_CSIOSTOR);
+
+ rv = csio_hw_get_fw_version(hw, &hw->fwrev);
+ if (rv != 0)
+ goto out;
+
+ csio_hw_print_fw_version(hw, "Firmware revision");
+
+ rv = csio_do_hello(hw, &hw->fw_state);
+ if (rv != 0) {
+ CSIO_INC_STATS(hw, n_err_fatal);
+ csio_post_event(&hw->sm, CSIO_HWE_FATAL);
+ goto out;
+ }
+
+ /* Read vpd */
+ rv = csio_hw_get_vpd_params(hw, &hw->vpd);
+ if (rv != 0)
+ goto out;
+
+ csio_hw_get_fw_version(hw, &hw->fwrev);
+ csio_hw_get_tp_version(hw, &hw->tp_vers);
+ if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
+
+ /* Do firmware update */
+ spin_unlock_irq(&hw->lock);
+ rv = csio_hw_flash_fw(hw, &reset);
+ spin_lock_irq(&hw->lock);
+
+ if (rv != 0)
+ goto out;
+
+ rv = csio_hw_check_fwver(hw);
+ if (rv < 0)
+ goto out;
+
+ /* If the firmware doesn't support Configuration Files,
+ * return an error.
+ */
+ rv = csio_hw_check_fwconfig(hw, param);
+ if (rv != 0) {
+ csio_info(hw, "Firmware doesn't support "
+ "Firmware Configuration files\n");
+ goto out;
+ }
+
+ /* The firmware provides us with a memory buffer where we can
+ * load a Configuration File from the host if we want to
+ * override the Configuration File in flash.
+ */
+ rv = csio_hw_use_fwconfig(hw, reset, param);
+ if (rv == -ENOENT) {
+ csio_info(hw, "Could not initialize "
+ "adapter, error%d\n", rv);
+ goto out;
+ }
+ if (rv != 0) {
+ csio_info(hw, "Could not initialize "
+ "adapter, error%d\n", rv);
+ goto out;
+ }
+
+ } else {
+ rv = csio_hw_check_fwver(hw);
+ if (rv < 0)
+ goto out;
+
+ if (hw->fw_state == CSIO_DEV_STATE_INIT) {
+
+ hw->flags |= CSIO_HWF_USING_SOFT_PARAMS;
+
+ /* device parameters */
+ rv = csio_get_device_params(hw);
+ if (rv != 0)
+ goto out;
+
+ /* Get device capabilities */
+ rv = csio_config_device_caps(hw);
+ if (rv != 0)
+ goto out;
+
+ /* Configure SGE */
+ csio_wr_sge_init(hw);
+
+ /* Post event to notify completion of configuration */
+ csio_post_event(&hw->sm, CSIO_HWE_INIT);
+ goto out;
+ }
+ } /* if not master */
+
+out:
+ return;
+}
+
+/*
+ * csio_hw_initialize - Initialize HW
+ * @hw - HW module
+ *
+ */
+static void
+csio_hw_initialize(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ int rv;
+ int i;
+
+ if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp)
+ goto out;
+
+ csio_mb_initialize(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_INITIALIZE_CMD failed!\n");
+ goto free_and_out;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_INITIALIZE_CMD returned 0x%x!\n",
+ retval);
+ goto free_and_out;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+ }
+
+ rv = csio_get_fcoe_resinfo(hw);
+ if (rv != 0) {
+ csio_err(hw, "Failed to read fcoe resource info: %d\n", rv);
+ goto out;
+ }
+
+ spin_unlock_irq(&hw->lock);
+ rv = csio_config_queues(hw);
+ spin_lock_irq(&hw->lock);
+
+ if (rv != 0) {
+ csio_err(hw, "Config of queues failed!: %d\n", rv);
+ goto out;
+ }
+
+ for (i = 0; i < hw->num_pports; i++)
+ hw->pport[i].mod_type = FW_PORT_MOD_TYPE_NA;
+
+ if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
+ rv = csio_enable_ports(hw);
+ if (rv != 0) {
+ csio_err(hw, "Failed to enable ports: %d\n", rv);
+ goto out;
+ }
+ }
+
+ csio_post_event(&hw->sm, CSIO_HWE_INIT_DONE);
+ return;
+
+free_and_out:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ return;
+}
+
+#define PF_INTR_MASK (PFSW_F | PFCIM_F)
+
+/*
+ * csio_hw_intr_enable - Enable HW interrupts
+ * @hw: Pointer to HW module.
+ *
+ * Enable interrupts in HW registers.
+ */
+static void
+csio_hw_intr_enable(struct csio_hw *hw)
+{
+ uint16_t vec = (uint16_t)csio_get_mb_intr_idx(csio_hw_to_mbm(hw));
+ u32 pf = 0;
+ uint32_t pl = csio_rd_reg32(hw, PL_INT_ENABLE_A);
+
+ if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
+ pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
+ else
+ pf = T6_SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
+
+ /*
+ * Set aivec for MSI/MSIX. PCIE_PF_CFG.INTXType is set up
+ * by FW, so do nothing for INTX.
+ */
+ if (hw->intr_mode == CSIO_IM_MSIX)
+ csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A),
+ AIVEC_V(AIVEC_M), vec);
+ else if (hw->intr_mode == CSIO_IM_MSI)
+ csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A),
+ AIVEC_V(AIVEC_M), 0);
+
+ csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE_A));
+
+ /* Turn on MB interrupts - this will internally flush PIO as well */
+ csio_mb_intr_enable(hw);
+
+ /* These are common registers - only a master can modify them */
+ if (csio_is_hw_master(hw)) {
+ /*
+ * Disable the Serial FLASH interrupt, if enabled!
+ */
+ pl &= (~SF_F);
+ csio_wr_reg32(hw, pl, PL_INT_ENABLE_A);
+
+ csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE_F |
+ EGRESS_SIZE_ERR_F | ERR_INVALID_CIDX_INC_F |
+ ERR_CPL_OPCODE_0_F | ERR_DROPPED_DB_F |
+ ERR_DATA_CPL_ON_HIGH_QID1_F |
+ ERR_DATA_CPL_ON_HIGH_QID0_F | ERR_BAD_DB_PIDX3_F |
+ ERR_BAD_DB_PIDX2_F | ERR_BAD_DB_PIDX1_F |
+ ERR_BAD_DB_PIDX0_F | ERR_ING_CTXT_PRIO_F |
+ ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F,
+ SGE_INT_ENABLE3_A);
+ csio_set_reg_field(hw, PL_INT_MAP0_A, 0, 1 << pf);
+ }
+
+ hw->flags |= CSIO_HWF_HW_INTR_ENABLED;
+
+}
+
+/*
+ * csio_hw_intr_disable - Disable HW interrupts
+ * @hw: Pointer to HW module.
+ *
+ * Turn off Mailbox and PCI_PF_CFG interrupts.
+ */
+void
+csio_hw_intr_disable(struct csio_hw *hw)
+{
+ u32 pf = 0;
+
+ if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
+ pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
+ else
+ pf = T6_SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A));
+
+ if (!(hw->flags & CSIO_HWF_HW_INTR_ENABLED))
+ return;
+
+ hw->flags &= ~CSIO_HWF_HW_INTR_ENABLED;
+
+ csio_wr_reg32(hw, 0, MYPF_REG(PL_PF_INT_ENABLE_A));
+ if (csio_is_hw_master(hw))
+ csio_set_reg_field(hw, PL_INT_MAP0_A, 1 << pf, 0);
+
+ /* Turn off MB interrupts */
+ csio_mb_intr_disable(hw);
+
+}
+
+void
+csio_hw_fatal_err(struct csio_hw *hw)
+{
+ csio_set_reg_field(hw, SGE_CONTROL_A, GLOBALENABLE_F, 0);
+ csio_hw_intr_disable(hw);
+
+ /* Do not reset HW, we may need FW state for debugging */
+ csio_fatal(hw, "HW Fatal error encountered!\n");
+}
+
+/*****************************************************************************/
+/* START: HW SM */
+/*****************************************************************************/
+/*
+ * csio_hws_uninit - Uninit state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_uninit(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_CFG:
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_configuring - Configuring state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_configuring(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_INIT:
+ csio_set_state(&hw->sm, csio_hws_initializing);
+ csio_hw_initialize(hw);
+ break;
+
+ case CSIO_HWE_INIT_DONE:
+ csio_set_state(&hw->sm, csio_hws_ready);
+ /* Fan out event to all lnode SMs */
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREADY);
+ break;
+
+ case CSIO_HWE_FATAL:
+ csio_set_state(&hw->sm, csio_hws_uninit);
+ break;
+
+ case CSIO_HWE_PCI_REMOVE:
+ csio_do_bye(hw);
+ break;
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_initializing - Initializing state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_initializing(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_INIT_DONE:
+ csio_set_state(&hw->sm, csio_hws_ready);
+
+ /* Fan out event to all lnode SMs */
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREADY);
+
+ /* Enable interrupts */
+ csio_hw_intr_enable(hw);
+ break;
+
+ case CSIO_HWE_FATAL:
+ csio_set_state(&hw->sm, csio_hws_uninit);
+ break;
+
+ case CSIO_HWE_PCI_REMOVE:
+ csio_do_bye(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_ready - Ready state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_ready(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ /* Remember the event */
+ hw->evtflag = evt;
+
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_HBA_RESET:
+ case CSIO_HWE_FW_DLOAD:
+ case CSIO_HWE_SUSPEND:
+ case CSIO_HWE_PCI_REMOVE:
+ case CSIO_HWE_PCIERR_DETECTED:
+ csio_set_state(&hw->sm, csio_hws_quiescing);
+ /* cleanup all outstanding cmds */
+ if (evt == CSIO_HWE_HBA_RESET ||
+ evt == CSIO_HWE_PCIERR_DETECTED)
+ csio_scsim_cleanup_io(csio_hw_to_scsim(hw), false);
+ else
+ csio_scsim_cleanup_io(csio_hw_to_scsim(hw), true);
+
+ csio_hw_intr_disable(hw);
+ csio_hw_mbm_cleanup(hw);
+ csio_evtq_stop(hw);
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWSTOP);
+ csio_evtq_flush(hw);
+ csio_mgmtm_cleanup(csio_hw_to_mgmtm(hw));
+ csio_post_event(&hw->sm, CSIO_HWE_QUIESCED);
+ break;
+
+ case CSIO_HWE_FATAL:
+ csio_set_state(&hw->sm, csio_hws_uninit);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_quiescing - Quiescing state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_quiescing(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_QUIESCED:
+ switch (hw->evtflag) {
+ case CSIO_HWE_FW_DLOAD:
+ csio_set_state(&hw->sm, csio_hws_resetting);
+ /* Download firmware */
+ fallthrough;
+
+ case CSIO_HWE_HBA_RESET:
+ csio_set_state(&hw->sm, csio_hws_resetting);
+ /* Start reset of the HBA */
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWRESET);
+ csio_wr_destroy_queues(hw, false);
+ csio_do_reset(hw, false);
+ csio_post_event(&hw->sm, CSIO_HWE_HBA_RESET_DONE);
+ break;
+
+ case CSIO_HWE_PCI_REMOVE:
+ csio_set_state(&hw->sm, csio_hws_removing);
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREMOVE);
+ csio_wr_destroy_queues(hw, true);
+ /* Now send the bye command */
+ csio_do_bye(hw);
+ break;
+
+ case CSIO_HWE_SUSPEND:
+ csio_set_state(&hw->sm, csio_hws_quiesced);
+ break;
+
+ case CSIO_HWE_PCIERR_DETECTED:
+ csio_set_state(&hw->sm, csio_hws_pcierr);
+ csio_wr_destroy_queues(hw, false);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+
+ }
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_quiesced - Quiesced state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_quiesced(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_RESUME:
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_resetting - HW Resetting state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_resetting(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_HBA_RESET_DONE:
+ csio_evtq_start(hw);
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_removing - PCI Hotplug removing state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_removing(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_HBA_RESET:
+ if (!csio_is_hw_master(hw))
+ break;
+ /*
+ * The BYE should have already been issued, so we can't
+ * use the mailbox interface. Hence we use the PL_RST
+ * register directly.
+ */
+ csio_err(hw, "Resetting HW and waiting 2 seconds...\n");
+ csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A);
+ mdelay(2000);
+ break;
+
+ /* Should never receive any new events */
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+
+ }
+}
+
+/*
+ * csio_hws_pcierr - PCI Error state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_pcierr(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_PCIERR_SLOT_RESET:
+ csio_evtq_start(hw);
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* END: HW SM */
+/*****************************************************************************/
+
+/*
+ * csio_handle_intr_status - table driven interrupt handler
+ * @hw: HW instance
+ * @reg: the interrupt status register to process
+ * @acts: table of interrupt actions
+ *
+ * A table driven interrupt handler that applies a set of masks to an
+ * interrupt status word and performs the corresponding actions if the
+ * interrupts described by the mask have occurred. The actions include
+ * optionally emitting a warning or alert message. The table is terminated
+ * by an entry specifying mask 0. Returns the number of fatal interrupt
+ * conditions.
+ */
+int
+csio_handle_intr_status(struct csio_hw *hw, unsigned int reg,
+ const struct intr_info *acts)
+{
+ int fatal = 0;
+ unsigned int mask = 0;
+ unsigned int status = csio_rd_reg32(hw, reg);
+
+ for ( ; acts->mask; ++acts) {
+ if (!(status & acts->mask))
+ continue;
+ if (acts->fatal) {
+ fatal++;
+ csio_fatal(hw, "Fatal %s (0x%x)\n",
+ acts->msg, status & acts->mask);
+ } else if (acts->msg)
+ csio_info(hw, "%s (0x%x)\n",
+ acts->msg, status & acts->mask);
+ mask |= acts->mask;
+ }
+ status &= mask;
+ if (status) /* clear processed interrupts */
+ csio_wr_reg32(hw, status, reg);
+ return fatal;
+}
+
+/*
+ * TP interrupt handler.
+ */
+static void csio_tp_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info tp_intr_info[] = {
+ { 0x3fffffff, "TP parity error", -1, 1 },
+ { FLMTXFLSTEMPTY_F, "TP out of Tx pages", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, TP_INT_CAUSE_A, tp_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * SGE interrupt handler.
+ */
+static void csio_sge_intr_handler(struct csio_hw *hw)
+{
+ uint64_t v;
+
+ static struct intr_info sge_intr_info[] = {
+ { ERR_CPL_EXCEED_IQE_SIZE_F,
+ "SGE received CPL exceeding IQE size", -1, 1 },
+ { ERR_INVALID_CIDX_INC_F,
+ "SGE GTS CIDX increment too large", -1, 0 },
+ { ERR_CPL_OPCODE_0_F, "SGE received 0-length CPL", -1, 0 },
+ { ERR_DROPPED_DB_F, "SGE doorbell dropped", -1, 0 },
+ { ERR_DATA_CPL_ON_HIGH_QID1_F | ERR_DATA_CPL_ON_HIGH_QID0_F,
+ "SGE IQID > 1023 received CPL for FL", -1, 0 },
+ { ERR_BAD_DB_PIDX3_F, "SGE DBP 3 pidx increment too large", -1,
+ 0 },
+ { ERR_BAD_DB_PIDX2_F, "SGE DBP 2 pidx increment too large", -1,
+ 0 },
+ { ERR_BAD_DB_PIDX1_F, "SGE DBP 1 pidx increment too large", -1,
+ 0 },
+ { ERR_BAD_DB_PIDX0_F, "SGE DBP 0 pidx increment too large", -1,
+ 0 },
+ { ERR_ING_CTXT_PRIO_F,
+ "SGE too many priority ingress contexts", -1, 0 },
+ { ERR_EGR_CTXT_PRIO_F,
+ "SGE too many priority egress contexts", -1, 0 },
+ { INGRESS_SIZE_ERR_F, "SGE illegal ingress QID", -1, 0 },
+ { EGRESS_SIZE_ERR_F, "SGE illegal egress QID", -1, 0 },
+ { 0, NULL, 0, 0 }
+ };
+
+ v = (uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE1_A) |
+ ((uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE2_A) << 32);
+ if (v) {
+ csio_fatal(hw, "SGE parity error (%#llx)\n",
+ (unsigned long long)v);
+ csio_wr_reg32(hw, (uint32_t)(v & 0xFFFFFFFF),
+ SGE_INT_CAUSE1_A);
+ csio_wr_reg32(hw, (uint32_t)(v >> 32), SGE_INT_CAUSE2_A);
+ }
+
+ v |= csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info);
+
+ if (csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info) ||
+ v != 0)
+ csio_hw_fatal_err(hw);
+}
+
+#define CIM_OBQ_INTR (OBQULP0PARERR_F | OBQULP1PARERR_F | OBQULP2PARERR_F |\
+ OBQULP3PARERR_F | OBQSGEPARERR_F | OBQNCSIPARERR_F)
+#define CIM_IBQ_INTR (IBQTP0PARERR_F | IBQTP1PARERR_F | IBQULPPARERR_F |\
+ IBQSGEHIPARERR_F | IBQSGELOPARERR_F | IBQNCSIPARERR_F)
+
+/*
+ * CIM interrupt handler.
+ */
+static void csio_cim_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info cim_intr_info[] = {
+ { PREFDROPINT_F, "CIM control register prefetch drop", -1, 1 },
+ { CIM_OBQ_INTR, "CIM OBQ parity error", -1, 1 },
+ { CIM_IBQ_INTR, "CIM IBQ parity error", -1, 1 },
+ { MBUPPARERR_F, "CIM mailbox uP parity error", -1, 1 },
+ { MBHOSTPARERR_F, "CIM mailbox host parity error", -1, 1 },
+ { TIEQINPARERRINT_F, "CIM TIEQ outgoing parity error", -1, 1 },
+ { TIEQOUTPARERRINT_F, "CIM TIEQ incoming parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info cim_upintr_info[] = {
+ { RSVDSPACEINT_F, "CIM reserved space access", -1, 1 },
+ { ILLTRANSINT_F, "CIM illegal transaction", -1, 1 },
+ { ILLWRINT_F, "CIM illegal write", -1, 1 },
+ { ILLRDINT_F, "CIM illegal read", -1, 1 },
+ { ILLRDBEINT_F, "CIM illegal read BE", -1, 1 },
+ { ILLWRBEINT_F, "CIM illegal write BE", -1, 1 },
+ { SGLRDBOOTINT_F, "CIM single read from boot space", -1, 1 },
+ { SGLWRBOOTINT_F, "CIM single write to boot space", -1, 1 },
+ { BLKWRBOOTINT_F, "CIM block write to boot space", -1, 1 },
+ { SGLRDFLASHINT_F, "CIM single read from flash space", -1, 1 },
+ { SGLWRFLASHINT_F, "CIM single write to flash space", -1, 1 },
+ { BLKWRFLASHINT_F, "CIM block write to flash space", -1, 1 },
+ { SGLRDEEPROMINT_F, "CIM single EEPROM read", -1, 1 },
+ { SGLWREEPROMINT_F, "CIM single EEPROM write", -1, 1 },
+ { BLKRDEEPROMINT_F, "CIM block EEPROM read", -1, 1 },
+ { BLKWREEPROMINT_F, "CIM block EEPROM write", -1, 1 },
+ { SGLRDCTLINT_F, "CIM single read from CTL space", -1, 1 },
+ { SGLWRCTLINT_F, "CIM single write to CTL space", -1, 1 },
+ { BLKRDCTLINT_F, "CIM block read from CTL space", -1, 1 },
+ { BLKWRCTLINT_F, "CIM block write to CTL space", -1, 1 },
+ { SGLRDPLINT_F, "CIM single read from PL space", -1, 1 },
+ { SGLWRPLINT_F, "CIM single write to PL space", -1, 1 },
+ { BLKRDPLINT_F, "CIM block read from PL space", -1, 1 },
+ { BLKWRPLINT_F, "CIM block write to PL space", -1, 1 },
+ { REQOVRLOOKUPINT_F, "CIM request FIFO overwrite", -1, 1 },
+ { RSPOVRLOOKUPINT_F, "CIM response FIFO overwrite", -1, 1 },
+ { TIMEOUTINT_F, "CIM PIF timeout", -1, 1 },
+ { TIMEOUTMAINT_F, "CIM PIF MA timeout", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ int fat;
+
+ fat = csio_handle_intr_status(hw, CIM_HOST_INT_CAUSE_A,
+ cim_intr_info) +
+ csio_handle_intr_status(hw, CIM_HOST_UPACC_INT_CAUSE_A,
+ cim_upintr_info);
+ if (fat)
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * ULP RX interrupt handler.
+ */
+static void csio_ulprx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info ulprx_intr_info[] = {
+ { 0x1800000, "ULPRX context error", -1, 1 },
+ { 0x7fffff, "ULPRX parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, ULP_RX_INT_CAUSE_A, ulprx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * ULP TX interrupt handler.
+ */
+static void csio_ulptx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info ulptx_intr_info[] = {
+ { PBL_BOUND_ERR_CH3_F, "ULPTX channel 3 PBL out of bounds", -1,
+ 0 },
+ { PBL_BOUND_ERR_CH2_F, "ULPTX channel 2 PBL out of bounds", -1,
+ 0 },
+ { PBL_BOUND_ERR_CH1_F, "ULPTX channel 1 PBL out of bounds", -1,
+ 0 },
+ { PBL_BOUND_ERR_CH0_F, "ULPTX channel 0 PBL out of bounds", -1,
+ 0 },
+ { 0xfffffff, "ULPTX parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, ULP_TX_INT_CAUSE_A, ulptx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * PM TX interrupt handler.
+ */
+static void csio_pmtx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info pmtx_intr_info[] = {
+ { PCMD_LEN_OVFL0_F, "PMTX channel 0 pcmd too large", -1, 1 },
+ { PCMD_LEN_OVFL1_F, "PMTX channel 1 pcmd too large", -1, 1 },
+ { PCMD_LEN_OVFL2_F, "PMTX channel 2 pcmd too large", -1, 1 },
+ { ZERO_C_CMD_ERROR_F, "PMTX 0-length pcmd", -1, 1 },
+ { 0xffffff0, "PMTX framing error", -1, 1 },
+ { OESPI_PAR_ERROR_F, "PMTX oespi parity error", -1, 1 },
+ { DB_OPTIONS_PAR_ERROR_F, "PMTX db_options parity error", -1,
+ 1 },
+ { ICSPI_PAR_ERROR_F, "PMTX icspi parity error", -1, 1 },
+ { PMTX_C_PCMD_PAR_ERROR_F, "PMTX c_pcmd parity error", -1, 1},
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, PM_TX_INT_CAUSE_A, pmtx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * PM RX interrupt handler.
+ */
+static void csio_pmrx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info pmrx_intr_info[] = {
+ { ZERO_E_CMD_ERROR_F, "PMRX 0-length pcmd", -1, 1 },
+ { 0x3ffff0, "PMRX framing error", -1, 1 },
+ { OCSPI_PAR_ERROR_F, "PMRX ocspi parity error", -1, 1 },
+ { DB_OPTIONS_PAR_ERROR_F, "PMRX db_options parity error", -1,
+ 1 },
+ { IESPI_PAR_ERROR_F, "PMRX iespi parity error", -1, 1 },
+ { PMRX_E_PCMD_PAR_ERROR_F, "PMRX e_pcmd parity error", -1, 1},
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, PM_RX_INT_CAUSE_A, pmrx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * CPL switch interrupt handler.
+ */
+static void csio_cplsw_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info cplsw_intr_info[] = {
+ { CIM_OP_MAP_PERR_F, "CPLSW CIM op_map parity error", -1, 1 },
+ { CIM_OVFL_ERROR_F, "CPLSW CIM overflow", -1, 1 },
+ { TP_FRAMING_ERROR_F, "CPLSW TP framing error", -1, 1 },
+ { SGE_FRAMING_ERROR_F, "CPLSW SGE framing error", -1, 1 },
+ { CIM_FRAMING_ERROR_F, "CPLSW CIM framing error", -1, 1 },
+ { ZERO_SWITCH_ERROR_F, "CPLSW no-switch error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, CPL_INTR_CAUSE_A, cplsw_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * LE interrupt handler.
+ */
+static void csio_le_intr_handler(struct csio_hw *hw)
+{
+ enum chip_type chip = CHELSIO_CHIP_VERSION(hw->chip_id);
+
+ static struct intr_info le_intr_info[] = {
+ { LIPMISS_F, "LE LIP miss", -1, 0 },
+ { LIP0_F, "LE 0 LIP error", -1, 0 },
+ { PARITYERR_F, "LE parity error", -1, 1 },
+ { UNKNOWNCMD_F, "LE unknown command", -1, 1 },
+ { REQQPARERR_F, "LE request queue parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ static struct intr_info t6_le_intr_info[] = {
+ { T6_LIPMISS_F, "LE LIP miss", -1, 0 },
+ { T6_LIP0_F, "LE 0 LIP error", -1, 0 },
+ { TCAMINTPERR_F, "LE parity error", -1, 1 },
+ { T6_UNKNOWNCMD_F, "LE unknown command", -1, 1 },
+ { SSRAMINTPERR_F, "LE request queue parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, LE_DB_INT_CAUSE_A,
+ (chip == CHELSIO_T5) ?
+ le_intr_info : t6_le_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * MPS interrupt handler.
+ */
+static void csio_mps_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info mps_rx_intr_info[] = {
+ { 0xffffff, "MPS Rx parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_tx_intr_info[] = {
+ { TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 },
+ { NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 },
+ { TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error",
+ -1, 1 },
+ { TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error",
+ -1, 1 },
+ { BUBBLE_F, "MPS Tx underflow", -1, 1 },
+ { SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 },
+ { FRMERR_F, "MPS Tx framing error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_trc_intr_info[] = {
+ { FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 },
+ { PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error",
+ -1, 1 },
+ { MISCPERR_F, "MPS TRC misc parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_stat_sram_intr_info[] = {
+ { 0x1fffff, "MPS statistics SRAM parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_stat_tx_intr_info[] = {
+ { 0xfffff, "MPS statistics Tx FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_stat_rx_intr_info[] = {
+ { 0xffffff, "MPS statistics Rx FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_cls_intr_info[] = {
+ { MATCHSRAM_F, "MPS match SRAM parity error", -1, 1 },
+ { MATCHTCAM_F, "MPS match TCAM parity error", -1, 1 },
+ { HASHSRAM_F, "MPS hash SRAM parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ int fat;
+
+ fat = csio_handle_intr_status(hw, MPS_RX_PERR_INT_CAUSE_A,
+ mps_rx_intr_info) +
+ csio_handle_intr_status(hw, MPS_TX_INT_CAUSE_A,
+ mps_tx_intr_info) +
+ csio_handle_intr_status(hw, MPS_TRC_INT_CAUSE_A,
+ mps_trc_intr_info) +
+ csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_SRAM_A,
+ mps_stat_sram_intr_info) +
+ csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A,
+ mps_stat_tx_intr_info) +
+ csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A,
+ mps_stat_rx_intr_info) +
+ csio_handle_intr_status(hw, MPS_CLS_INT_CAUSE_A,
+ mps_cls_intr_info);
+
+ csio_wr_reg32(hw, 0, MPS_INT_CAUSE_A);
+ csio_rd_reg32(hw, MPS_INT_CAUSE_A); /* flush */
+ if (fat)
+ csio_hw_fatal_err(hw);
+}
+
+#define MEM_INT_MASK (PERR_INT_CAUSE_F | ECC_CE_INT_CAUSE_F | \
+ ECC_UE_INT_CAUSE_F)
+
+/*
+ * EDC/MC interrupt handler.
+ */
+static void csio_mem_intr_handler(struct csio_hw *hw, int idx)
+{
+ static const char name[3][5] = { "EDC0", "EDC1", "MC" };
+
+ unsigned int addr, cnt_addr, v;
+
+ if (idx <= MEM_EDC1) {
+ addr = EDC_REG(EDC_INT_CAUSE_A, idx);
+ cnt_addr = EDC_REG(EDC_ECC_STATUS_A, idx);
+ } else {
+ addr = MC_INT_CAUSE_A;
+ cnt_addr = MC_ECC_STATUS_A;
+ }
+
+ v = csio_rd_reg32(hw, addr) & MEM_INT_MASK;
+ if (v & PERR_INT_CAUSE_F)
+ csio_fatal(hw, "%s FIFO parity error\n", name[idx]);
+ if (v & ECC_CE_INT_CAUSE_F) {
+ uint32_t cnt = ECC_CECNT_G(csio_rd_reg32(hw, cnt_addr));
+
+ csio_wr_reg32(hw, ECC_CECNT_V(ECC_CECNT_M), cnt_addr);
+ csio_warn(hw, "%u %s correctable ECC data error%s\n",
+ cnt, name[idx], cnt > 1 ? "s" : "");
+ }
+ if (v & ECC_UE_INT_CAUSE_F)
+ csio_fatal(hw, "%s uncorrectable ECC data error\n", name[idx]);
+
+ csio_wr_reg32(hw, v, addr);
+ if (v & (PERR_INT_CAUSE_F | ECC_UE_INT_CAUSE_F))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * MA interrupt handler.
+ */
+static void csio_ma_intr_handler(struct csio_hw *hw)
+{
+ uint32_t v, status = csio_rd_reg32(hw, MA_INT_CAUSE_A);
+
+ if (status & MEM_PERR_INT_CAUSE_F)
+ csio_fatal(hw, "MA parity error, parity status %#x\n",
+ csio_rd_reg32(hw, MA_PARITY_ERROR_STATUS_A));
+ if (status & MEM_WRAP_INT_CAUSE_F) {
+ v = csio_rd_reg32(hw, MA_INT_WRAP_STATUS_A);
+ csio_fatal(hw,
+ "MA address wrap-around error by client %u to address %#x\n",
+ MEM_WRAP_CLIENT_NUM_G(v), MEM_WRAP_ADDRESS_G(v) << 4);
+ }
+ csio_wr_reg32(hw, status, MA_INT_CAUSE_A);
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * SMB interrupt handler.
+ */
+static void csio_smb_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info smb_intr_info[] = {
+ { MSTTXFIFOPARINT_F, "SMB master Tx FIFO parity error", -1, 1 },
+ { MSTRXFIFOPARINT_F, "SMB master Rx FIFO parity error", -1, 1 },
+ { SLVFIFOPARINT_F, "SMB slave FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, SMB_INT_CAUSE_A, smb_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * NC-SI interrupt handler.
+ */
+static void csio_ncsi_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info ncsi_intr_info[] = {
+ { CIM_DM_PRTY_ERR_F, "NC-SI CIM parity error", -1, 1 },
+ { MPS_DM_PRTY_ERR_F, "NC-SI MPS parity error", -1, 1 },
+ { TXFIFO_PRTY_ERR_F, "NC-SI Tx FIFO parity error", -1, 1 },
+ { RXFIFO_PRTY_ERR_F, "NC-SI Rx FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, NCSI_INT_CAUSE_A, ncsi_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * XGMAC interrupt handler.
+ */
+static void csio_xgmac_intr_handler(struct csio_hw *hw, int port)
+{
+ uint32_t v = csio_rd_reg32(hw, T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A));
+
+ v &= TXFIFO_PRTY_ERR_F | RXFIFO_PRTY_ERR_F;
+ if (!v)
+ return;
+
+ if (v & TXFIFO_PRTY_ERR_F)
+ csio_fatal(hw, "XGMAC %d Tx FIFO parity error\n", port);
+ if (v & RXFIFO_PRTY_ERR_F)
+ csio_fatal(hw, "XGMAC %d Rx FIFO parity error\n", port);
+ csio_wr_reg32(hw, v, T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A));
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * PL interrupt handler.
+ */
+static void csio_pl_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info pl_intr_info[] = {
+ { FATALPERR_F, "T4 fatal parity error", -1, 1 },
+ { PERRVFID_F, "PL VFID_MAP parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, PL_PL_INT_CAUSE_A, pl_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * csio_hw_slow_intr_handler - control path interrupt handler
+ * @hw: HW module
+ *
+ * Interrupt handler for non-data global interrupt events, e.g., errors.
+ * The designation 'slow' is because it involves register reads, while
+ * data interrupts typically don't involve any MMIOs.
+ */
+int
+csio_hw_slow_intr_handler(struct csio_hw *hw)
+{
+ uint32_t cause = csio_rd_reg32(hw, PL_INT_CAUSE_A);
+
+ if (!(cause & CSIO_GLBL_INTR_MASK)) {
+ CSIO_INC_STATS(hw, n_plint_unexp);
+ return 0;
+ }
+
+ csio_dbg(hw, "Slow interrupt! cause: 0x%x\n", cause);
+
+ CSIO_INC_STATS(hw, n_plint_cnt);
+
+ if (cause & CIM_F)
+ csio_cim_intr_handler(hw);
+
+ if (cause & MPS_F)
+ csio_mps_intr_handler(hw);
+
+ if (cause & NCSI_F)
+ csio_ncsi_intr_handler(hw);
+
+ if (cause & PL_F)
+ csio_pl_intr_handler(hw);
+
+ if (cause & SMB_F)
+ csio_smb_intr_handler(hw);
+
+ if (cause & XGMAC0_F)
+ csio_xgmac_intr_handler(hw, 0);
+
+ if (cause & XGMAC1_F)
+ csio_xgmac_intr_handler(hw, 1);
+
+ if (cause & XGMAC_KR0_F)
+ csio_xgmac_intr_handler(hw, 2);
+
+ if (cause & XGMAC_KR1_F)
+ csio_xgmac_intr_handler(hw, 3);
+
+ if (cause & PCIE_F)
+ hw->chip_ops->chip_pcie_intr_handler(hw);
+
+ if (cause & MC_F)
+ csio_mem_intr_handler(hw, MEM_MC);
+
+ if (cause & EDC0_F)
+ csio_mem_intr_handler(hw, MEM_EDC0);
+
+ if (cause & EDC1_F)
+ csio_mem_intr_handler(hw, MEM_EDC1);
+
+ if (cause & LE_F)
+ csio_le_intr_handler(hw);
+
+ if (cause & TP_F)
+ csio_tp_intr_handler(hw);
+
+ if (cause & MA_F)
+ csio_ma_intr_handler(hw);
+
+ if (cause & PM_TX_F)
+ csio_pmtx_intr_handler(hw);
+
+ if (cause & PM_RX_F)
+ csio_pmrx_intr_handler(hw);
+
+ if (cause & ULP_RX_F)
+ csio_ulprx_intr_handler(hw);
+
+ if (cause & CPL_SWITCH_F)
+ csio_cplsw_intr_handler(hw);
+
+ if (cause & SGE_F)
+ csio_sge_intr_handler(hw);
+
+ if (cause & ULP_TX_F)
+ csio_ulptx_intr_handler(hw);
+
+ /* Clear the interrupts just processed for which we are the master. */
+ csio_wr_reg32(hw, cause & CSIO_GLBL_INTR_MASK, PL_INT_CAUSE_A);
+ csio_rd_reg32(hw, PL_INT_CAUSE_A); /* flush */
+
+ return 1;
+}
+
+/*****************************************************************************
+ * HW <--> mailbox interfacing routines.
+ ****************************************************************************/
+/*
+ * csio_mberr_worker - Worker thread (dpc) for mailbox/error completions
+ *
+ * @data: Private data pointer.
+ *
+ * Called from worker thread context.
+ */
+static void
+csio_mberr_worker(void *data)
+{
+ struct csio_hw *hw = (struct csio_hw *)data;
+ struct csio_mbm *mbm = &hw->mbm;
+ LIST_HEAD(cbfn_q);
+ struct csio_mb *mbp_next;
+ int rv;
+
+ timer_delete_sync(&mbm->timer);
+
+ spin_lock_irq(&hw->lock);
+ if (list_empty(&mbm->cbfn_q)) {
+ spin_unlock_irq(&hw->lock);
+ return;
+ }
+
+ list_splice_tail_init(&mbm->cbfn_q, &cbfn_q);
+ mbm->stats.n_cbfnq = 0;
+
+ /* Try to start waiting mailboxes */
+ if (!list_empty(&mbm->req_q)) {
+ mbp_next = list_first_entry(&mbm->req_q, struct csio_mb, list);
+ list_del_init(&mbp_next->list);
+
+ rv = csio_mb_issue(hw, mbp_next);
+ if (rv != 0)
+ list_add_tail(&mbp_next->list, &mbm->req_q);
+ else
+ CSIO_DEC_STATS(mbm, n_activeq);
+ }
+ spin_unlock_irq(&hw->lock);
+
+ /* Now callback completions */
+ csio_mb_completions(hw, &cbfn_q);
+}
+
+/*
+ * csio_hw_mb_timer - Top-level Mailbox timeout handler.
+ *
+ * @data: private data pointer
+ *
+ **/
+static void
+csio_hw_mb_timer(struct timer_list *t)
+{
+ struct csio_mbm *mbm = timer_container_of(mbm, t, timer);
+ struct csio_hw *hw = mbm->hw;
+ struct csio_mb *mbp = NULL;
+
+ spin_lock_irq(&hw->lock);
+ mbp = csio_mb_tmo_handler(hw);
+ spin_unlock_irq(&hw->lock);
+
+ /* Call back the function for the timed-out Mailbox */
+ if (mbp)
+ mbp->mb_cbfn(hw, mbp);
+
+}
+
+/*
+ * csio_hw_mbm_cleanup - Cleanup Mailbox module.
+ * @hw: HW module
+ *
+ * Called with lock held, should exit with lock held.
+ * Cancels outstanding mailboxes (waiting, in-flight) and gathers them
+ * into a local queue. Drops lock and calls the completions. Holds
+ * lock and returns.
+ */
+static void
+csio_hw_mbm_cleanup(struct csio_hw *hw)
+{
+ LIST_HEAD(cbfn_q);
+
+ csio_mb_cancel_all(hw, &cbfn_q);
+
+ spin_unlock_irq(&hw->lock);
+ csio_mb_completions(hw, &cbfn_q);
+ spin_lock_irq(&hw->lock);
+}
+
+/*****************************************************************************
+ * Event handling
+ ****************************************************************************/
+int
+csio_enqueue_evt(struct csio_hw *hw, enum csio_evt type, void *evt_msg,
+ uint16_t len)
+{
+ struct csio_evt_msg *evt_entry = NULL;
+
+ if (type >= CSIO_EVT_MAX)
+ return -EINVAL;
+
+ if (len > CSIO_EVT_MSG_SIZE)
+ return -EINVAL;
+
+ if (hw->flags & CSIO_HWF_FWEVT_STOP)
+ return -EINVAL;
+
+ if (list_empty(&hw->evt_free_q)) {
+ csio_err(hw, "Failed to alloc evt entry, msg type %d len %d\n",
+ type, len);
+ return -ENOMEM;
+ }
+
+ evt_entry = list_first_entry(&hw->evt_free_q,
+ struct csio_evt_msg, list);
+ list_del_init(&evt_entry->list);
+
+ /* copy event msg and queue the event */
+ evt_entry->type = type;
+ memcpy((void *)evt_entry->data, evt_msg, len);
+ list_add_tail(&evt_entry->list, &hw->evt_active_q);
+
+ CSIO_DEC_STATS(hw, n_evt_freeq);
+ CSIO_INC_STATS(hw, n_evt_activeq);
+
+ return 0;
+}
+
+static int
+csio_enqueue_evt_lock(struct csio_hw *hw, enum csio_evt type, void *evt_msg,
+ uint16_t len, bool msg_sg)
+{
+ struct csio_evt_msg *evt_entry = NULL;
+ struct csio_fl_dma_buf *fl_sg;
+ uint32_t off = 0;
+ unsigned long flags;
+ int n, ret = 0;
+
+ if (type >= CSIO_EVT_MAX)
+ return -EINVAL;
+
+ if (len > CSIO_EVT_MSG_SIZE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ if (hw->flags & CSIO_HWF_FWEVT_STOP) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (list_empty(&hw->evt_free_q)) {
+ csio_err(hw, "Failed to alloc evt entry, msg type %d len %d\n",
+ type, len);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ evt_entry = list_first_entry(&hw->evt_free_q,
+ struct csio_evt_msg, list);
+ list_del_init(&evt_entry->list);
+
+ /* copy event msg and queue the event */
+ evt_entry->type = type;
+
+ /* If Payload in SG list*/
+ if (msg_sg) {
+ fl_sg = (struct csio_fl_dma_buf *) evt_msg;
+ for (n = 0; (n < CSIO_MAX_FLBUF_PER_IQWR && off < len); n++) {
+ memcpy((void *)((uintptr_t)evt_entry->data + off),
+ fl_sg->flbufs[n].vaddr,
+ fl_sg->flbufs[n].len);
+ off += fl_sg->flbufs[n].len;
+ }
+ } else
+ memcpy((void *)evt_entry->data, evt_msg, len);
+
+ list_add_tail(&evt_entry->list, &hw->evt_active_q);
+ CSIO_DEC_STATS(hw, n_evt_freeq);
+ CSIO_INC_STATS(hw, n_evt_activeq);
+out:
+ spin_unlock_irqrestore(&hw->lock, flags);
+ return ret;
+}
+
+static void
+csio_free_evt(struct csio_hw *hw, struct csio_evt_msg *evt_entry)
+{
+ if (evt_entry) {
+ spin_lock_irq(&hw->lock);
+ list_del_init(&evt_entry->list);
+ list_add_tail(&evt_entry->list, &hw->evt_free_q);
+ CSIO_DEC_STATS(hw, n_evt_activeq);
+ CSIO_INC_STATS(hw, n_evt_freeq);
+ spin_unlock_irq(&hw->lock);
+ }
+}
+
+void
+csio_evtq_flush(struct csio_hw *hw)
+{
+ uint32_t count;
+ count = 30;
+ while (hw->flags & CSIO_HWF_FWEVT_PENDING && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ CSIO_DB_ASSERT(!(hw->flags & CSIO_HWF_FWEVT_PENDING));
+}
+
+static void
+csio_evtq_stop(struct csio_hw *hw)
+{
+ hw->flags |= CSIO_HWF_FWEVT_STOP;
+}
+
+static void
+csio_evtq_start(struct csio_hw *hw)
+{
+ hw->flags &= ~CSIO_HWF_FWEVT_STOP;
+}
+
+static void
+csio_evtq_cleanup(struct csio_hw *hw)
+{
+ struct list_head *evt_entry, *next_entry;
+
+ /* Release outstanding events from activeq to freeq*/
+ if (!list_empty(&hw->evt_active_q))
+ list_splice_tail_init(&hw->evt_active_q, &hw->evt_free_q);
+
+ hw->stats.n_evt_activeq = 0;
+ hw->flags &= ~CSIO_HWF_FWEVT_PENDING;
+
+ /* Freeup event entry */
+ list_for_each_safe(evt_entry, next_entry, &hw->evt_free_q) {
+ kfree(evt_entry);
+ CSIO_DEC_STATS(hw, n_evt_freeq);
+ }
+
+ hw->stats.n_evt_freeq = 0;
+}
+
+
+static void
+csio_process_fwevtq_entry(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv)
+{
+ __u8 op;
+ void *msg = NULL;
+ uint32_t msg_len = 0;
+ bool msg_sg = 0;
+
+ op = ((struct rss_header *) wr)->opcode;
+ if (op == CPL_FW6_PLD) {
+ CSIO_INC_STATS(hw, n_cpl_fw6_pld);
+ if (!flb || !flb->totlen) {
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ return;
+ }
+
+ msg = (void *) flb;
+ msg_len = flb->totlen;
+ msg_sg = 1;
+ } else if (op == CPL_FW6_MSG || op == CPL_FW4_MSG) {
+
+ CSIO_INC_STATS(hw, n_cpl_fw6_msg);
+ /* skip RSS header */
+ msg = (void *)((uintptr_t)wr + sizeof(__be64));
+ msg_len = (op == CPL_FW6_MSG) ? sizeof(struct cpl_fw6_msg) :
+ sizeof(struct cpl_fw4_msg);
+ } else {
+ csio_warn(hw, "unexpected CPL %#x on FW event queue\n", op);
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ return;
+ }
+
+ /*
+ * Enqueue event to EventQ. Events processing happens
+ * in Event worker thread context
+ */
+ if (csio_enqueue_evt_lock(hw, CSIO_EVT_FW, msg,
+ (uint16_t)msg_len, msg_sg))
+ CSIO_INC_STATS(hw, n_evt_drop);
+}
+
+void
+csio_evtq_worker(struct work_struct *work)
+{
+ struct csio_hw *hw = container_of(work, struct csio_hw, evtq_work);
+ struct list_head *evt_entry, *next_entry;
+ LIST_HEAD(evt_q);
+ struct csio_evt_msg *evt_msg;
+ struct cpl_fw6_msg *msg;
+ struct csio_rnode *rn;
+ int rv = 0;
+ uint8_t evtq_stop = 0;
+
+ csio_dbg(hw, "event worker thread active evts#%d\n",
+ hw->stats.n_evt_activeq);
+
+ spin_lock_irq(&hw->lock);
+ while (!list_empty(&hw->evt_active_q)) {
+ list_splice_tail_init(&hw->evt_active_q, &evt_q);
+ spin_unlock_irq(&hw->lock);
+
+ list_for_each_safe(evt_entry, next_entry, &evt_q) {
+ evt_msg = (struct csio_evt_msg *) evt_entry;
+
+ /* Drop events if queue is STOPPED */
+ spin_lock_irq(&hw->lock);
+ if (hw->flags & CSIO_HWF_FWEVT_STOP)
+ evtq_stop = 1;
+ spin_unlock_irq(&hw->lock);
+ if (evtq_stop) {
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto free_evt;
+ }
+
+ switch (evt_msg->type) {
+ case CSIO_EVT_FW:
+ msg = (struct cpl_fw6_msg *)(evt_msg->data);
+
+ if ((msg->opcode == CPL_FW6_MSG ||
+ msg->opcode == CPL_FW4_MSG) &&
+ !msg->type) {
+ rv = csio_mb_fwevt_handler(hw,
+ msg->data);
+ if (!rv)
+ break;
+ /* Handle any remaining fw events */
+ csio_fcoe_fwevt_handler(hw,
+ msg->opcode, msg->data);
+ } else if (msg->opcode == CPL_FW6_PLD) {
+
+ csio_fcoe_fwevt_handler(hw,
+ msg->opcode, msg->data);
+ } else {
+ csio_warn(hw,
+ "Unhandled FW msg op %x type %x\n",
+ msg->opcode, msg->type);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ }
+ break;
+
+ case CSIO_EVT_MBX:
+ csio_mberr_worker(hw);
+ break;
+
+ case CSIO_EVT_DEV_LOSS:
+ memcpy(&rn, evt_msg->data, sizeof(rn));
+ csio_rnode_devloss_handler(rn);
+ break;
+
+ default:
+ csio_warn(hw, "Unhandled event %x on evtq\n",
+ evt_msg->type);
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+free_evt:
+ csio_free_evt(hw, evt_msg);
+ }
+
+ spin_lock_irq(&hw->lock);
+ }
+ hw->flags &= ~CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irq(&hw->lock);
+}
+
+int
+csio_fwevtq_handler(struct csio_hw *hw)
+{
+ int rv;
+
+ if (csio_q_iqid(hw, hw->fwevt_iq_idx) == CSIO_MAX_QID) {
+ CSIO_INC_STATS(hw, n_int_stray);
+ return -EINVAL;
+ }
+
+ rv = csio_wr_process_iq_idx(hw, hw->fwevt_iq_idx,
+ csio_process_fwevtq_entry, NULL);
+ return rv;
+}
+
+/****************************************************************************
+ * Entry points
+ ****************************************************************************/
+
+/* Management module */
+/*
+ * csio_mgmt_req_lookup - Lookup the given IO req exist in Active Q.
+ * mgmt - mgmt module
+ * @io_req - io request
+ *
+ * Return - 0:if given IO Req exists in active Q.
+ * -EINVAL :if lookup fails.
+ */
+int
+csio_mgmt_req_lookup(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req)
+{
+ struct list_head *tmp;
+
+ /* Lookup ioreq in the ACTIVEQ */
+ list_for_each(tmp, &mgmtm->active_q) {
+ if (io_req == (struct csio_ioreq *)tmp)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#define ECM_MIN_TMO 1000 /* Minimum timeout value for req */
+
+/*
+ * csio_mgmts_tmo_handler - MGMT IO Timeout handler.
+ * @data - Event data.
+ *
+ * Return - none.
+ */
+static void
+csio_mgmt_tmo_handler(struct timer_list *t)
+{
+ struct csio_mgmtm *mgmtm = timer_container_of(mgmtm, t, mgmt_timer);
+ struct list_head *tmp;
+ struct csio_ioreq *io_req;
+
+ csio_dbg(mgmtm->hw, "Mgmt timer invoked!\n");
+
+ spin_lock_irq(&mgmtm->hw->lock);
+
+ list_for_each(tmp, &mgmtm->active_q) {
+ io_req = (struct csio_ioreq *) tmp;
+ io_req->tmo -= min_t(uint32_t, io_req->tmo, ECM_MIN_TMO);
+
+ if (!io_req->tmo) {
+ /* Dequeue the request from retry Q. */
+ tmp = csio_list_prev(tmp);
+ list_del_init(&io_req->sm.sm_list);
+ if (io_req->io_cbfn) {
+ /* io_req will be freed by completion handler */
+ io_req->wr_status = -ETIMEDOUT;
+ io_req->io_cbfn(mgmtm->hw, io_req);
+ } else {
+ CSIO_DB_ASSERT(0);
+ }
+ }
+ }
+
+ /* If retry queue is not empty, re-arm timer */
+ if (!list_empty(&mgmtm->active_q))
+ mod_timer(&mgmtm->mgmt_timer,
+ jiffies + msecs_to_jiffies(ECM_MIN_TMO));
+ spin_unlock_irq(&mgmtm->hw->lock);
+}
+
+static void
+csio_mgmtm_cleanup(struct csio_mgmtm *mgmtm)
+{
+ struct csio_hw *hw = mgmtm->hw;
+ struct csio_ioreq *io_req;
+ struct list_head *tmp;
+ uint32_t count;
+
+ count = 30;
+ /* Wait for all outstanding req to complete gracefully */
+ while ((!list_empty(&mgmtm->active_q)) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* release outstanding req from ACTIVEQ */
+ list_for_each(tmp, &mgmtm->active_q) {
+ io_req = (struct csio_ioreq *) tmp;
+ tmp = csio_list_prev(tmp);
+ list_del_init(&io_req->sm.sm_list);
+ mgmtm->stats.n_active--;
+ if (io_req->io_cbfn) {
+ /* io_req will be freed by completion handler */
+ io_req->wr_status = -ETIMEDOUT;
+ io_req->io_cbfn(mgmtm->hw, io_req);
+ }
+ }
+}
+
+/*
+ * csio_mgmt_init - Mgmt module init entry point
+ * @mgmtsm - mgmt module
+ * @hw - HW module
+ *
+ * Initialize mgmt timer, resource wait queue, active queue,
+ * completion q. Allocate Egress and Ingress
+ * WR queues and save off the queue index returned by the WR
+ * module for future use. Allocate and save off mgmt reqs in the
+ * mgmt_req_freelist for future use. Make sure their SM is initialized
+ * to uninit state.
+ * Returns: 0 - on success
+ * -ENOMEM - on error.
+ */
+static int
+csio_mgmtm_init(struct csio_mgmtm *mgmtm, struct csio_hw *hw)
+{
+ timer_setup(&mgmtm->mgmt_timer, csio_mgmt_tmo_handler, 0);
+
+ INIT_LIST_HEAD(&mgmtm->active_q);
+ INIT_LIST_HEAD(&mgmtm->cbfn_q);
+
+ mgmtm->hw = hw;
+ /*mgmtm->iq_idx = hw->fwevt_iq_idx;*/
+
+ return 0;
+}
+
+/*
+ * csio_mgmtm_exit - MGMT module exit entry point
+ * @mgmtsm - mgmt module
+ *
+ * This function called during MGMT module uninit.
+ * Stop timers, free ioreqs allocated.
+ * Returns: None
+ *
+ */
+static void
+csio_mgmtm_exit(struct csio_mgmtm *mgmtm)
+{
+ timer_delete_sync(&mgmtm->mgmt_timer);
+}
+
+
+/**
+ * csio_hw_start - Kicks off the HW State machine
+ * @hw: Pointer to HW module.
+ *
+ * It is assumed that the initialization is a synchronous operation.
+ * So when we return after posting the event, the HW SM should be in
+ * the ready state, if there were no errors during init.
+ */
+int
+csio_hw_start(struct csio_hw *hw)
+{
+ spin_lock_irq(&hw->lock);
+ csio_post_event(&hw->sm, CSIO_HWE_CFG);
+ spin_unlock_irq(&hw->lock);
+
+ if (csio_is_hw_ready(hw))
+ return 0;
+ else if (csio_match_state(hw, csio_hws_uninit))
+ return -EINVAL;
+ else
+ return -ENODEV;
+}
+
+int
+csio_hw_stop(struct csio_hw *hw)
+{
+ csio_post_event(&hw->sm, CSIO_HWE_PCI_REMOVE);
+
+ if (csio_is_hw_removing(hw))
+ return 0;
+ else
+ return -EINVAL;
+}
+
+/* Max reset retries */
+#define CSIO_MAX_RESET_RETRIES 3
+
+/**
+ * csio_hw_reset - Reset the hardware
+ * @hw: HW module.
+ *
+ * Caller should hold lock across this function.
+ */
+int
+csio_hw_reset(struct csio_hw *hw)
+{
+ if (!csio_is_hw_master(hw))
+ return -EPERM;
+
+ if (hw->rst_retries >= CSIO_MAX_RESET_RETRIES) {
+ csio_dbg(hw, "Max hw reset attempts reached..");
+ return -EINVAL;
+ }
+
+ hw->rst_retries++;
+ csio_post_event(&hw->sm, CSIO_HWE_HBA_RESET);
+
+ if (csio_is_hw_ready(hw)) {
+ hw->rst_retries = 0;
+ hw->stats.n_reset_start = jiffies_to_msecs(jiffies);
+ return 0;
+ } else
+ return -EINVAL;
+}
+
+/*
+ * csio_hw_get_device_id - Caches the Adapter's vendor & device id.
+ * @hw: HW module.
+ */
+static void
+csio_hw_get_device_id(struct csio_hw *hw)
+{
+ /* Is the adapter device id cached already ?*/
+ if (csio_is_dev_id_cached(hw))
+ return;
+
+ /* Get the PCI vendor & device id */
+ pci_read_config_word(hw->pdev, PCI_VENDOR_ID,
+ &hw->params.pci.vendor_id);
+ pci_read_config_word(hw->pdev, PCI_DEVICE_ID,
+ &hw->params.pci.device_id);
+
+ csio_dev_id_cached(hw);
+ hw->chip_id = (hw->params.pci.device_id & CSIO_HW_CHIP_MASK);
+
+} /* csio_hw_get_device_id */
+
+/*
+ * csio_hw_set_description - Set the model, description of the hw.
+ * @hw: HW module.
+ * @ven_id: PCI Vendor ID
+ * @dev_id: PCI Device ID
+ */
+static void
+csio_hw_set_description(struct csio_hw *hw, uint16_t ven_id, uint16_t dev_id)
+{
+ uint32_t adap_type, prot_type;
+
+ if (ven_id == CSIO_VENDOR_ID) {
+ prot_type = (dev_id & CSIO_ASIC_DEVID_PROTO_MASK);
+ adap_type = (dev_id & CSIO_ASIC_DEVID_TYPE_MASK);
+
+ if (prot_type == CSIO_T5_FCOE_ASIC) {
+ memcpy(hw->hw_ver,
+ csio_t5_fcoe_adapters[adap_type].model_no, 16);
+ memcpy(hw->model_desc,
+ csio_t5_fcoe_adapters[adap_type].description,
+ 32);
+ } else {
+ char tempName[32] = "Chelsio FCoE Controller";
+ memcpy(hw->model_desc, tempName, 32);
+ }
+ }
+} /* csio_hw_set_description */
+
+/**
+ * csio_hw_init - Initialize HW module.
+ * @hw: Pointer to HW module.
+ *
+ * Initialize the members of the HW module.
+ */
+int
+csio_hw_init(struct csio_hw *hw)
+{
+ int rv = -EINVAL;
+ uint32_t i;
+ uint16_t ven_id, dev_id;
+ struct csio_evt_msg *evt_entry;
+
+ INIT_LIST_HEAD(&hw->sm.sm_list);
+ csio_init_state(&hw->sm, csio_hws_uninit);
+ spin_lock_init(&hw->lock);
+ INIT_LIST_HEAD(&hw->sln_head);
+
+ /* Get the PCI vendor & device id */
+ csio_hw_get_device_id(hw);
+
+ strcpy(hw->name, CSIO_HW_NAME);
+
+ /* Initialize the HW chip ops T5 specific ops */
+ hw->chip_ops = &t5_ops;
+
+ /* Set the model & its description */
+
+ ven_id = hw->params.pci.vendor_id;
+ dev_id = hw->params.pci.device_id;
+
+ csio_hw_set_description(hw, ven_id, dev_id);
+
+ /* Initialize default log level */
+ hw->params.log_level = (uint32_t) csio_dbg_level;
+
+ csio_set_fwevt_intr_idx(hw, -1);
+ csio_set_nondata_intr_idx(hw, -1);
+
+ /* Init all the modules: Mailbox, WorkRequest and Transport */
+ if (csio_mbm_init(csio_hw_to_mbm(hw), hw, csio_hw_mb_timer))
+ goto err;
+
+ rv = csio_wrm_init(csio_hw_to_wrm(hw), hw);
+ if (rv)
+ goto err_mbm_exit;
+
+ rv = csio_scsim_init(csio_hw_to_scsim(hw), hw);
+ if (rv)
+ goto err_wrm_exit;
+
+ rv = csio_mgmtm_init(csio_hw_to_mgmtm(hw), hw);
+ if (rv)
+ goto err_scsim_exit;
+ /* Pre-allocate evtq and initialize them */
+ INIT_LIST_HEAD(&hw->evt_active_q);
+ INIT_LIST_HEAD(&hw->evt_free_q);
+ for (i = 0; i < csio_evtq_sz; i++) {
+
+ evt_entry = kzalloc(sizeof(struct csio_evt_msg), GFP_KERNEL);
+ if (!evt_entry) {
+ rv = -ENOMEM;
+ csio_err(hw, "Failed to initialize eventq");
+ goto err_evtq_cleanup;
+ }
+
+ list_add_tail(&evt_entry->list, &hw->evt_free_q);
+ CSIO_INC_STATS(hw, n_evt_freeq);
+ }
+
+ hw->dev_num = dev_num;
+ dev_num++;
+
+ return 0;
+
+err_evtq_cleanup:
+ csio_evtq_cleanup(hw);
+ csio_mgmtm_exit(csio_hw_to_mgmtm(hw));
+err_scsim_exit:
+ csio_scsim_exit(csio_hw_to_scsim(hw));
+err_wrm_exit:
+ csio_wrm_exit(csio_hw_to_wrm(hw), hw);
+err_mbm_exit:
+ csio_mbm_exit(csio_hw_to_mbm(hw));
+err:
+ return rv;
+}
+
+/**
+ * csio_hw_exit - Un-initialize HW module.
+ * @hw: Pointer to HW module.
+ *
+ */
+void
+csio_hw_exit(struct csio_hw *hw)
+{
+ csio_evtq_cleanup(hw);
+ csio_mgmtm_exit(csio_hw_to_mgmtm(hw));
+ csio_scsim_exit(csio_hw_to_scsim(hw));
+ csio_wrm_exit(csio_hw_to_wrm(hw), hw);
+ csio_mbm_exit(csio_hw_to_mbm(hw));
+}
diff --git a/drivers/scsi/csiostor/csio_hw.h b/drivers/scsi/csiostor/csio_hw.h
new file mode 100644
index 000000000000..e351af6e7c81
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_hw.h
@@ -0,0 +1,666 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_HW_H__
+#define __CSIO_HW_H__
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/compiler.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/mempool.h>
+#include <linux/io.h>
+#include <linux/spinlock_types.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "t4_hw.h"
+#include "csio_hw_chip.h"
+#include "csio_wr.h"
+#include "csio_mb.h"
+#include "csio_scsi.h"
+#include "csio_defs.h"
+#include "t4_regs.h"
+#include "t4_msg.h"
+
+/*
+ * An error value used by host. Should not clash with FW defined return values.
+ */
+#define FW_HOSTERROR 255
+
+#define CSIO_HW_NAME "Chelsio FCoE Adapter"
+#define CSIO_MAX_PFN 8
+#define CSIO_MAX_PPORTS 4
+
+#define CSIO_MAX_LUN 0xFFFF
+#define CSIO_MAX_QUEUE 2048
+#define CSIO_MAX_CMD_PER_LUN 32
+#define CSIO_MAX_DDP_BUF_SIZE (1024 * 1024)
+#define CSIO_MAX_SECTOR_SIZE 128
+#define CSIO_MIN_T6_FW 0x01102D00 /* FW 1.16.45.0 */
+
+/* Interrupts */
+#define CSIO_EXTRA_MSI_IQS 2 /* Extra iqs for INTX/MSI mode
+ * (Forward intr iq + fw iq) */
+#define CSIO_EXTRA_VECS 2 /* non-data + FW evt */
+#define CSIO_MAX_SCSI_CPU 128
+#define CSIO_MAX_SCSI_QSETS (CSIO_MAX_SCSI_CPU * CSIO_MAX_PPORTS)
+#define CSIO_MAX_MSIX_VECS (CSIO_MAX_SCSI_QSETS + CSIO_EXTRA_VECS)
+
+/* Queues */
+enum {
+ CSIO_INTR_WRSIZE = 128,
+ CSIO_INTR_IQSIZE = ((CSIO_MAX_MSIX_VECS + 1) * CSIO_INTR_WRSIZE),
+ CSIO_FWEVT_WRSIZE = 128,
+ CSIO_FWEVT_IQLEN = 128,
+ CSIO_FWEVT_FLBUFS = 64,
+ CSIO_FWEVT_IQSIZE = (CSIO_FWEVT_WRSIZE * CSIO_FWEVT_IQLEN),
+ CSIO_HW_NIQ = 1,
+ CSIO_HW_NFLQ = 1,
+ CSIO_HW_NEQ = 1,
+ CSIO_HW_NINTXQ = 1,
+};
+
+struct csio_msix_entries {
+ void *dev_id; /* Priv object associated w/ this msix*/
+ char desc[24]; /* Description of this vector */
+};
+
+struct csio_scsi_qset {
+ int iq_idx; /* Ingress index */
+ int eq_idx; /* Egress index */
+ uint32_t intr_idx; /* MSIX Vector index */
+};
+
+struct csio_scsi_cpu_info {
+ int16_t max_cpus;
+};
+
+extern int csio_dbg_level;
+extern unsigned int csio_port_mask;
+extern int csio_msi;
+
+#define CSIO_VENDOR_ID 0x1425
+#define CSIO_ASIC_DEVID_PROTO_MASK 0xFF00
+#define CSIO_ASIC_DEVID_TYPE_MASK 0x00FF
+
+#define CSIO_GLBL_INTR_MASK (CIM_F | MPS_F | PL_F | PCIE_F | MC_F | \
+ EDC0_F | EDC1_F | LE_F | TP_F | MA_F | \
+ PM_TX_F | PM_RX_F | ULP_RX_F | \
+ CPL_SWITCH_F | SGE_F | ULP_TX_F | SF_F)
+
+/*
+ * Hard parameters used to initialize the card in the absence of a
+ * configuration file.
+ */
+enum {
+ /* General */
+ CSIO_SGE_DBFIFO_INT_THRESH = 10,
+
+ CSIO_SGE_RX_DMA_OFFSET = 2,
+
+ CSIO_SGE_FLBUF_SIZE1 = 65536,
+ CSIO_SGE_FLBUF_SIZE2 = 1536,
+ CSIO_SGE_FLBUF_SIZE3 = 9024,
+ CSIO_SGE_FLBUF_SIZE4 = 9216,
+ CSIO_SGE_FLBUF_SIZE5 = 2048,
+ CSIO_SGE_FLBUF_SIZE6 = 128,
+ CSIO_SGE_FLBUF_SIZE7 = 8192,
+ CSIO_SGE_FLBUF_SIZE8 = 16384,
+
+ CSIO_SGE_TIMER_VAL_0 = 5,
+ CSIO_SGE_TIMER_VAL_1 = 10,
+ CSIO_SGE_TIMER_VAL_2 = 20,
+ CSIO_SGE_TIMER_VAL_3 = 50,
+ CSIO_SGE_TIMER_VAL_4 = 100,
+ CSIO_SGE_TIMER_VAL_5 = 200,
+
+ CSIO_SGE_INT_CNT_VAL_0 = 1,
+ CSIO_SGE_INT_CNT_VAL_1 = 4,
+ CSIO_SGE_INT_CNT_VAL_2 = 8,
+ CSIO_SGE_INT_CNT_VAL_3 = 16,
+};
+
+/* Slowpath events */
+enum csio_evt {
+ CSIO_EVT_FW = 0, /* FW event */
+ CSIO_EVT_MBX, /* MBX event */
+ CSIO_EVT_SCN, /* State change notification */
+ CSIO_EVT_DEV_LOSS, /* Device loss event */
+ CSIO_EVT_MAX, /* Max supported event */
+};
+
+#define CSIO_EVT_MSG_SIZE 512
+#define CSIO_EVTQ_SIZE 512
+
+/* Event msg */
+struct csio_evt_msg {
+ struct list_head list; /* evt queue*/
+ enum csio_evt type;
+ uint8_t data[CSIO_EVT_MSG_SIZE];
+};
+
+enum {
+ SERNUM_LEN = 16, /* Serial # length */
+ EC_LEN = 16, /* E/C length */
+ ID_LEN = 16, /* ID length */
+};
+
+enum {
+ SF_SIZE = SF_SEC_SIZE * 16, /* serial flash size */
+};
+
+/* serial flash and firmware constants */
+enum {
+ SF_ATTEMPTS = 10, /* max retries for SF operations */
+
+ /* flash command opcodes */
+ SF_PROG_PAGE = 2, /* program page */
+ SF_WR_DISABLE = 4, /* disable writes */
+ SF_RD_STATUS = 5, /* read status register */
+ SF_WR_ENABLE = 6, /* enable writes */
+ SF_RD_DATA_FAST = 0xb, /* read flash */
+ SF_RD_ID = 0x9f, /* read ID */
+ SF_ERASE_SECTOR = 0xd8, /* erase sector */
+};
+
+/* Management module */
+enum {
+ CSIO_MGMT_EQ_WRSIZE = 512,
+ CSIO_MGMT_IQ_WRSIZE = 128,
+ CSIO_MGMT_EQLEN = 64,
+ CSIO_MGMT_IQLEN = 64,
+};
+
+#define CSIO_MGMT_EQSIZE (CSIO_MGMT_EQLEN * CSIO_MGMT_EQ_WRSIZE)
+#define CSIO_MGMT_IQSIZE (CSIO_MGMT_IQLEN * CSIO_MGMT_IQ_WRSIZE)
+
+/* mgmt module stats */
+struct csio_mgmtm_stats {
+ uint32_t n_abort_req; /* Total abort request */
+ uint32_t n_abort_rsp; /* Total abort response */
+ uint32_t n_close_req; /* Total close request */
+ uint32_t n_close_rsp; /* Total close response */
+ uint32_t n_err; /* Total Errors */
+ uint32_t n_drop; /* Total request dropped */
+ uint32_t n_active; /* Count of active_q */
+ uint32_t n_cbfn; /* Count of cbfn_q */
+};
+
+/* MGMT module */
+struct csio_mgmtm {
+ struct csio_hw *hw; /* Pointer to HW moduel */
+ int eq_idx; /* Egress queue index */
+ int iq_idx; /* Ingress queue index */
+ int msi_vec; /* MSI vector */
+ struct list_head active_q; /* Outstanding ELS/CT */
+ struct list_head abort_q; /* Outstanding abort req */
+ struct list_head cbfn_q; /* Completion queue */
+ struct list_head mgmt_req_freelist; /* Free poll of reqs */
+ /* ELSCT request freelist*/
+ struct timer_list mgmt_timer; /* MGMT timer */
+ struct csio_mgmtm_stats stats; /* ELS/CT stats */
+};
+
+struct csio_adap_desc {
+ char model_no[16];
+ char description[32];
+};
+
+struct pci_params {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ int vpd_cap_addr;
+ uint16_t speed;
+ uint8_t width;
+};
+
+/* User configurable hw parameters */
+struct csio_hw_params {
+ uint32_t sf_size; /* serial flash
+ * size in bytes
+ */
+ uint32_t sf_nsec; /* # of flash sectors */
+ struct pci_params pci;
+ uint32_t log_level; /* Module-level for
+ * debug log.
+ */
+};
+
+struct csio_vpd {
+ uint32_t cclk;
+ uint8_t ec[EC_LEN + 1];
+ uint8_t sn[SERNUM_LEN + 1];
+ uint8_t id[ID_LEN + 1];
+};
+
+/* Firmware Port Capabilities types. */
+
+typedef u16 fw_port_cap16_t; /* 16-bit Port Capabilities integral value */
+typedef u32 fw_port_cap32_t; /* 32-bit Port Capabilities integral value */
+
+enum fw_caps {
+ FW_CAPS_UNKNOWN = 0, /* 0'ed out initial state */
+ FW_CAPS16 = 1, /* old Firmware: 16-bit Port Capabilities */
+ FW_CAPS32 = 2, /* new Firmware: 32-bit Port Capabilities */
+};
+
+enum cc_pause {
+ PAUSE_RX = 1 << 0,
+ PAUSE_TX = 1 << 1,
+ PAUSE_AUTONEG = 1 << 2
+};
+
+enum cc_fec {
+ FEC_AUTO = 1 << 0, /* IEEE 802.3 "automatic" */
+ FEC_RS = 1 << 1, /* Reed-Solomon */
+ FEC_BASER_RS = 1 << 2 /* BaseR/Reed-Solomon */
+};
+
+struct link_config {
+ fw_port_cap32_t pcaps; /* link capabilities */
+ fw_port_cap32_t def_acaps; /* default advertised capabilities */
+ fw_port_cap32_t acaps; /* advertised capabilities */
+ fw_port_cap32_t lpacaps; /* peer advertised capabilities */
+
+ fw_port_cap32_t speed_caps; /* speed(s) user has requested */
+ unsigned int speed; /* actual link speed (Mb/s) */
+
+ enum cc_pause requested_fc; /* flow control user has requested */
+ enum cc_pause fc; /* actual link flow control */
+
+ enum cc_fec requested_fec; /* Forward Error Correction: */
+ enum cc_fec fec; /* requested and actual in use */
+
+ unsigned char autoneg; /* autonegotiating? */
+
+ unsigned char link_ok; /* link up? */
+ unsigned char link_down_rc; /* link down reason */
+};
+
+#define FW_LEN16(fw_struct) FW_CMD_LEN16_V(sizeof(fw_struct) / 16)
+
+#define ADVERT_MASK (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) | \
+ FW_PORT_CAP32_ANEG)
+
+/* Enable or disable autonegotiation. */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+struct csio_pport {
+ uint16_t pcap;
+ uint16_t acap;
+ uint8_t portid;
+ uint8_t link_status;
+ uint16_t link_speed;
+ uint8_t mac[6];
+ uint8_t mod_type;
+ uint8_t rsvd1;
+ uint8_t rsvd2;
+ uint8_t rsvd3;
+ struct link_config link_cfg;
+};
+
+/* fcoe resource information */
+struct csio_fcoe_res_info {
+ uint16_t e_d_tov;
+ uint16_t r_a_tov_seq;
+ uint16_t r_a_tov_els;
+ uint16_t r_r_tov;
+ uint32_t max_xchgs;
+ uint32_t max_ssns;
+ uint32_t used_xchgs;
+ uint32_t used_ssns;
+ uint32_t max_fcfs;
+ uint32_t max_vnps;
+ uint32_t used_fcfs;
+ uint32_t used_vnps;
+};
+
+/* HW State machine Events */
+enum csio_hw_ev {
+ CSIO_HWE_CFG = (uint32_t)1, /* Starts off the State machine */
+ CSIO_HWE_INIT, /* Config done, start Init */
+ CSIO_HWE_INIT_DONE, /* Init Mailboxes sent, HW ready */
+ CSIO_HWE_FATAL, /* Fatal error during initialization */
+ CSIO_HWE_PCIERR_DETECTED,/* PCI error recovery detetced */
+ CSIO_HWE_PCIERR_SLOT_RESET, /* Slot reset after PCI recoviery */
+ CSIO_HWE_PCIERR_RESUME, /* Resume after PCI error recovery */
+ CSIO_HWE_QUIESCED, /* HBA quiesced */
+ CSIO_HWE_HBA_RESET, /* HBA reset requested */
+ CSIO_HWE_HBA_RESET_DONE, /* HBA reset completed */
+ CSIO_HWE_FW_DLOAD, /* FW download requested */
+ CSIO_HWE_PCI_REMOVE, /* PCI de-instantiation */
+ CSIO_HWE_SUSPEND, /* HW suspend for Online(hot) replacement */
+ CSIO_HWE_RESUME, /* HW resume for Online(hot) replacement */
+ CSIO_HWE_MAX, /* Max HW event */
+};
+
+/* hw stats */
+struct csio_hw_stats {
+ uint32_t n_evt_activeq; /* Number of event in active Q */
+ uint32_t n_evt_freeq; /* Number of event in free Q */
+ uint32_t n_evt_drop; /* Number of event droped */
+ uint32_t n_evt_unexp; /* Number of unexpected events */
+ uint32_t n_pcich_offline;/* Number of pci channel offline */
+ uint32_t n_lnlkup_miss; /* Number of lnode lookup miss */
+ uint32_t n_cpl_fw6_msg; /* Number of cpl fw6 message*/
+ uint32_t n_cpl_fw6_pld; /* Number of cpl fw6 payload*/
+ uint32_t n_cpl_unexp; /* Number of unexpected cpl */
+ uint32_t n_mbint_unexp; /* Number of unexpected mbox */
+ /* interrupt */
+ uint32_t n_plint_unexp; /* Number of unexpected PL */
+ /* interrupt */
+ uint32_t n_plint_cnt; /* Number of PL interrupt */
+ uint32_t n_int_stray; /* Number of stray interrupt */
+ uint32_t n_err; /* Number of hw errors */
+ uint32_t n_err_fatal; /* Number of fatal errors */
+ uint32_t n_err_nomem; /* Number of memory alloc failure */
+ uint32_t n_err_io; /* Number of IO failure */
+ enum csio_hw_ev n_evt_sm[CSIO_HWE_MAX]; /* Number of sm events */
+ uint64_t n_reset_start; /* Start time after the reset */
+ uint32_t rsvd1;
+};
+
+/* Defines for hw->flags */
+#define CSIO_HWF_MASTER 0x00000001 /* This is the Master
+ * function for the
+ * card.
+ */
+#define CSIO_HWF_HW_INTR_ENABLED 0x00000002 /* Are HW Interrupt
+ * enable bit set?
+ */
+#define CSIO_HWF_FWEVT_PENDING 0x00000004 /* FW events pending */
+#define CSIO_HWF_Q_MEM_ALLOCED 0x00000008 /* Queues have been
+ * allocated memory.
+ */
+#define CSIO_HWF_Q_FW_ALLOCED 0x00000010 /* Queues have been
+ * allocated in FW.
+ */
+#define CSIO_HWF_VPD_VALID 0x00000020 /* Valid VPD copied */
+#define CSIO_HWF_DEVID_CACHED 0X00000040 /* PCI vendor & device
+ * id cached */
+#define CSIO_HWF_FWEVT_STOP 0x00000080 /* Stop processing
+ * FW events
+ */
+#define CSIO_HWF_USING_SOFT_PARAMS 0x00000100 /* Using FW config
+ * params
+ */
+#define CSIO_HWF_HOST_INTR_ENABLED 0x00000200 /* Are host interrupts
+ * enabled?
+ */
+#define CSIO_HWF_ROOT_NO_RELAXED_ORDERING 0x00000400 /* Is PCIe relaxed
+ * ordering enabled
+ */
+
+#define csio_is_hw_intr_enabled(__hw) \
+ ((__hw)->flags & CSIO_HWF_HW_INTR_ENABLED)
+#define csio_is_host_intr_enabled(__hw) \
+ ((__hw)->flags & CSIO_HWF_HOST_INTR_ENABLED)
+#define csio_is_hw_master(__hw) ((__hw)->flags & CSIO_HWF_MASTER)
+#define csio_is_valid_vpd(__hw) ((__hw)->flags & CSIO_HWF_VPD_VALID)
+#define csio_is_dev_id_cached(__hw) ((__hw)->flags & CSIO_HWF_DEVID_CACHED)
+#define csio_valid_vpd_copied(__hw) ((__hw)->flags |= CSIO_HWF_VPD_VALID)
+#define csio_dev_id_cached(__hw) ((__hw)->flags |= CSIO_HWF_DEVID_CACHED)
+
+/* Defines for intr_mode */
+enum csio_intr_mode {
+ CSIO_IM_NONE = 0,
+ CSIO_IM_INTX = 1,
+ CSIO_IM_MSI = 2,
+ CSIO_IM_MSIX = 3,
+};
+
+/* Master HW structure: One per function */
+struct csio_hw {
+ struct csio_sm sm; /* State machine: should
+ * be the 1st member.
+ */
+ spinlock_t lock; /* Lock for hw */
+
+ struct csio_scsim scsim; /* SCSI module*/
+ struct csio_wrm wrm; /* Work request module*/
+ struct pci_dev *pdev; /* PCI device */
+
+ void __iomem *regstart; /* Virtual address of
+ * register map
+ */
+ /* SCSI queue sets */
+ uint32_t num_sqsets; /* Number of SCSI
+ * queue sets */
+ uint32_t num_scsi_msix_cpus; /* Number of CPUs that
+ * will be used
+ * for ingress
+ * processing.
+ */
+
+ struct csio_scsi_qset sqset[CSIO_MAX_PPORTS][CSIO_MAX_SCSI_CPU];
+ struct csio_scsi_cpu_info scsi_cpu_info[CSIO_MAX_PPORTS];
+
+ uint32_t evtflag; /* Event flag */
+ uint32_t flags; /* HW flags */
+
+ struct csio_mgmtm mgmtm; /* management module */
+ struct csio_mbm mbm; /* Mailbox module */
+
+ /* Lnodes */
+ uint32_t num_lns; /* Number of lnodes */
+ struct csio_lnode *rln; /* Root lnode */
+ struct list_head sln_head; /* Sibling node list
+ * list
+ */
+ int intr_iq_idx; /* Forward interrupt
+ * queue.
+ */
+ int fwevt_iq_idx; /* FW evt queue */
+ struct work_struct evtq_work; /* Worker thread for
+ * HW events.
+ */
+ struct list_head evt_free_q; /* freelist of evt
+ * elements
+ */
+ struct list_head evt_active_q; /* active evt queue*/
+
+ /* board related info */
+ char name[32];
+ char hw_ver[16];
+ char model_desc[32];
+ char drv_version[32];
+ char fwrev_str[32];
+ uint32_t optrom_ver;
+ uint32_t fwrev;
+ uint32_t tp_vers;
+ char chip_ver;
+ uint16_t chip_id; /* Tells T4/T5 chip */
+ enum csio_dev_state fw_state;
+ struct csio_vpd vpd;
+
+ uint8_t pfn; /* Physical Function
+ * number
+ */
+ uint32_t port_vec; /* Port vector */
+ uint8_t num_pports; /* Number of physical
+ * ports.
+ */
+ uint8_t rst_retries; /* Reset retries */
+ uint8_t cur_evt; /* current s/m evt */
+ uint8_t prev_evt; /* Previous s/m evt */
+ uint32_t dev_num; /* device number */
+ struct csio_pport pport[CSIO_MAX_PPORTS]; /* Ports (XGMACs) */
+ struct csio_hw_params params; /* Hw parameters */
+
+ struct dma_pool *scsi_dma_pool; /* DMA pool for SCSI */
+ mempool_t *mb_mempool; /* Mailbox memory pool*/
+ mempool_t *rnode_mempool; /* rnode memory pool */
+
+ /* Interrupt */
+ enum csio_intr_mode intr_mode; /* INTx, MSI, MSIX */
+ uint32_t fwevt_intr_idx; /* FW evt MSIX/interrupt
+ * index
+ */
+ uint32_t nondata_intr_idx; /* nondata MSIX/intr
+ * idx
+ */
+
+ uint8_t cfg_neq; /* FW configured no of
+ * egress queues
+ */
+ uint8_t cfg_niq; /* FW configured no of
+ * iq queues.
+ */
+
+ struct csio_fcoe_res_info fres_info; /* Fcoe resource info */
+ struct csio_hw_chip_ops *chip_ops; /* T4/T5 Chip specific
+ * Operations
+ */
+
+ /* MSIX vectors */
+ struct csio_msix_entries msix_entries[CSIO_MAX_MSIX_VECS];
+
+ struct dentry *debugfs_root; /* Debug FS */
+ struct csio_hw_stats stats; /* Hw statistics */
+};
+
+/* Register access macros */
+#define csio_reg(_b, _r) ((_b) + (_r))
+
+#define csio_rd_reg8(_h, _r) readb(csio_reg((_h)->regstart, (_r)))
+#define csio_rd_reg16(_h, _r) readw(csio_reg((_h)->regstart, (_r)))
+#define csio_rd_reg32(_h, _r) readl(csio_reg((_h)->regstart, (_r)))
+#define csio_rd_reg64(_h, _r) readq(csio_reg((_h)->regstart, (_r)))
+
+#define csio_wr_reg8(_h, _v, _r) writeb((_v), \
+ csio_reg((_h)->regstart, (_r)))
+#define csio_wr_reg16(_h, _v, _r) writew((_v), \
+ csio_reg((_h)->regstart, (_r)))
+#define csio_wr_reg32(_h, _v, _r) writel((_v), \
+ csio_reg((_h)->regstart, (_r)))
+#define csio_wr_reg64(_h, _v, _r) writeq((_v), \
+ csio_reg((_h)->regstart, (_r)))
+
+void csio_set_reg_field(struct csio_hw *, uint32_t, uint32_t, uint32_t);
+
+/* Core clocks <==> uSecs */
+static inline uint32_t
+csio_core_ticks_to_us(struct csio_hw *hw, uint32_t ticks)
+{
+ /* add Core Clock / 2 to round ticks to nearest uS */
+ return (ticks * 1000 + hw->vpd.cclk/2) / hw->vpd.cclk;
+}
+
+static inline uint32_t
+csio_us_to_core_ticks(struct csio_hw *hw, uint32_t us)
+{
+ return (us * hw->vpd.cclk) / 1000;
+}
+
+/* Easy access macros */
+#define csio_hw_to_wrm(hw) ((struct csio_wrm *)(&(hw)->wrm))
+#define csio_hw_to_mbm(hw) ((struct csio_mbm *)(&(hw)->mbm))
+#define csio_hw_to_scsim(hw) ((struct csio_scsim *)(&(hw)->scsim))
+#define csio_hw_to_mgmtm(hw) ((struct csio_mgmtm *)(&(hw)->mgmtm))
+
+#define CSIO_PCI_BUS(hw) ((hw)->pdev->bus->number)
+#define CSIO_PCI_DEV(hw) (PCI_SLOT((hw)->pdev->devfn))
+#define CSIO_PCI_FUNC(hw) (PCI_FUNC((hw)->pdev->devfn))
+
+#define csio_set_fwevt_intr_idx(_h, _i) ((_h)->fwevt_intr_idx = (_i))
+#define csio_get_fwevt_intr_idx(_h) ((_h)->fwevt_intr_idx)
+#define csio_set_nondata_intr_idx(_h, _i) ((_h)->nondata_intr_idx = (_i))
+#define csio_get_nondata_intr_idx(_h) ((_h)->nondata_intr_idx)
+
+/* Printing/logging */
+#define CSIO_DEVID(__dev) ((__dev)->dev_num)
+#define CSIO_DEVID_LO(__dev) (CSIO_DEVID((__dev)) & 0xFFFF)
+#define CSIO_DEVID_HI(__dev) ((CSIO_DEVID((__dev)) >> 16) & 0xFFFF)
+
+#define csio_info(__hw, __fmt, ...) \
+ dev_info(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#define csio_fatal(__hw, __fmt, ...) \
+ dev_crit(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#define csio_err(__hw, __fmt, ...) \
+ dev_err(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#define csio_warn(__hw, __fmt, ...) \
+ dev_warn(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#ifdef __CSIO_DEBUG__
+#define csio_dbg(__hw, __fmt, ...) \
+ csio_info((__hw), __fmt, ##__VA_ARGS__);
+#else
+#define csio_dbg(__hw, __fmt, ...)
+#endif
+
+int csio_hw_wait_op_done_val(struct csio_hw *, int, uint32_t, int,
+ int, int, uint32_t *);
+void csio_hw_tp_wr_bits_indirect(struct csio_hw *, unsigned int,
+ unsigned int, unsigned int);
+int csio_mgmt_req_lookup(struct csio_mgmtm *, struct csio_ioreq *);
+void csio_hw_intr_disable(struct csio_hw *);
+int csio_hw_slow_intr_handler(struct csio_hw *);
+int csio_handle_intr_status(struct csio_hw *, unsigned int,
+ const struct intr_info *);
+
+fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps);
+fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16);
+fw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32);
+fw_port_cap32_t lstatus_to_fwcap(u32 lstatus);
+
+int csio_hw_start(struct csio_hw *);
+int csio_hw_stop(struct csio_hw *);
+int csio_hw_reset(struct csio_hw *);
+int csio_is_hw_ready(struct csio_hw *);
+int csio_is_hw_removing(struct csio_hw *);
+
+int csio_fwevtq_handler(struct csio_hw *);
+void csio_evtq_worker(struct work_struct *);
+int csio_enqueue_evt(struct csio_hw *, enum csio_evt, void *, uint16_t);
+void csio_evtq_flush(struct csio_hw *hw);
+
+int csio_request_irqs(struct csio_hw *);
+void csio_intr_enable(struct csio_hw *);
+void csio_intr_disable(struct csio_hw *, bool);
+void csio_hw_fatal_err(struct csio_hw *);
+
+struct csio_lnode *csio_lnode_alloc(struct csio_hw *);
+int csio_config_queues(struct csio_hw *);
+
+int csio_hw_init(struct csio_hw *);
+void csio_hw_exit(struct csio_hw *);
+#endif /* ifndef __CSIO_HW_H__ */
diff --git a/drivers/scsi/csiostor/csio_hw_chip.h b/drivers/scsi/csiostor/csio_hw_chip.h
new file mode 100644
index 000000000000..aaabdbe11d88
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_hw_chip.h
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2013 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_HW_CHIP_H__
+#define __CSIO_HW_CHIP_H__
+
+#include "csio_defs.h"
+
+/* Define MACRO values */
+#define CSIO_HW_T5 0x5000
+#define CSIO_T5_FCOE_ASIC 0x5600
+#define CSIO_HW_T6 0x6000
+#define CSIO_T6_FCOE_ASIC 0x6600
+#define CSIO_HW_CHIP_MASK 0xF000
+
+#define T5_REGMAP_SIZE (332 * 1024)
+#define FW_FNAME_T5 "cxgb4/t5fw.bin"
+#define FW_CFG_NAME_T5 "cxgb4/t5-config.txt"
+#define FW_FNAME_T6 "cxgb4/t6fw.bin"
+#define FW_CFG_NAME_T6 "cxgb4/t6-config.txt"
+
+#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision))
+#define CHELSIO_CHIP_FPGA 0x100
+#define CHELSIO_CHIP_VERSION(code) (((code) >> 12) & 0xf)
+#define CHELSIO_CHIP_RELEASE(code) ((code) & 0xf)
+
+#define CHELSIO_T5 0x5
+#define CHELSIO_T6 0x6
+
+enum chip_type {
+ T5_A0 = CHELSIO_CHIP_CODE(CHELSIO_T5, 0),
+ T5_A1 = CHELSIO_CHIP_CODE(CHELSIO_T5, 1),
+ T5_FIRST_REV = T5_A0,
+ T5_LAST_REV = T5_A1,
+
+ T6_A0 = CHELSIO_CHIP_CODE(CHELSIO_T6, 0),
+ T6_FIRST_REV = T6_A0,
+ T6_LAST_REV = T6_A0,
+};
+
+static inline int csio_is_t5(uint16_t chip)
+{
+ return (chip == CSIO_HW_T5);
+}
+
+static inline int csio_is_t6(uint16_t chip)
+{
+ return (chip == CSIO_HW_T6);
+}
+
+/* Define MACRO DEFINITIONS */
+#define CSIO_DEVICE(devid, idx) \
+ { PCI_VENDOR_ID_CHELSIO, (devid), PCI_ANY_ID, PCI_ANY_ID, 0, 0, (idx) }
+
+#include "t4fw_api.h"
+#include "t4fw_version.h"
+
+#define FW_VERSION(chip) ( \
+ FW_HDR_FW_VER_MAJOR_G(chip##FW_VERSION_MAJOR) | \
+ FW_HDR_FW_VER_MINOR_G(chip##FW_VERSION_MINOR) | \
+ FW_HDR_FW_VER_MICRO_G(chip##FW_VERSION_MICRO) | \
+ FW_HDR_FW_VER_BUILD_G(chip##FW_VERSION_BUILD))
+#define FW_INTFVER(chip, intf) (FW_HDR_INTFVER_##intf)
+
+struct fw_info {
+ u8 chip;
+ char *fs_name;
+ char *fw_mod_name;
+ struct fw_hdr fw_hdr;
+};
+
+/* Declare ENUMS */
+enum { MEM_EDC0, MEM_EDC1, MEM_MC, MEM_MC0 = MEM_MC, MEM_MC1 };
+
+enum {
+ MEMWIN_APERTURE = 2048,
+ MEMWIN_BASE = 0x1b800,
+};
+
+/* Slow path handlers */
+struct intr_info {
+ unsigned int mask; /* bits to check in interrupt status */
+ const char *msg; /* message to print or NULL */
+ short stat_idx; /* stat counter to increment or -1 */
+ unsigned short fatal; /* whether the condition reported is fatal */
+};
+
+/* T4/T5 Chip specific ops */
+struct csio_hw;
+struct csio_hw_chip_ops {
+ int (*chip_set_mem_win)(struct csio_hw *, uint32_t);
+ void (*chip_pcie_intr_handler)(struct csio_hw *);
+ uint32_t (*chip_flash_cfg_addr)(struct csio_hw *);
+ int (*chip_mc_read)(struct csio_hw *, int, uint32_t,
+ __be32 *, uint64_t *);
+ int (*chip_edc_read)(struct csio_hw *, int, uint32_t,
+ __be32 *, uint64_t *);
+ int (*chip_memory_rw)(struct csio_hw *, u32, int, u32,
+ u32, uint32_t *, int);
+ void (*chip_dfs_create_ext_mem)(struct csio_hw *);
+};
+
+extern struct csio_hw_chip_ops t5_ops;
+
+#endif /* #ifndef __CSIO_HW_CHIP_H__ */
diff --git a/drivers/scsi/csiostor/csio_hw_t5.c b/drivers/scsi/csiostor/csio_hw_t5.c
new file mode 100644
index 000000000000..86fded97d799
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_hw_t5.c
@@ -0,0 +1,369 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2013 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "csio_hw.h"
+#include "csio_init.h"
+
+static int
+csio_t5_set_mem_win(struct csio_hw *hw, uint32_t win)
+{
+ u32 mem_win_base;
+ /*
+ * Truncation intentional: we only read the bottom 32-bits of the
+ * 64-bit BAR0/BAR1 ... We use the hardware backdoor mechanism to
+ * read BAR0 instead of using pci_resource_start() because we could be
+ * operating from within a Virtual Machine which is trapping our
+ * accesses to our Configuration Space and we need to set up the PCI-E
+ * Memory Window decoders with the actual addresses which will be
+ * coming across the PCI-E link.
+ */
+
+ /* For T5, only relative offset inside the PCIe BAR is passed */
+ mem_win_base = MEMWIN_BASE;
+
+ /*
+ * Set up memory window for accessing adapter memory ranges. (Read
+ * back MA register to ensure that changes propagate before we attempt
+ * to use the new values.)
+ */
+ csio_wr_reg32(hw, mem_win_base | BIR_V(0) |
+ WINDOW_V(ilog2(MEMWIN_APERTURE) - 10),
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win));
+ csio_rd_reg32(hw,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win));
+
+ return 0;
+}
+
+/*
+ * Interrupt handler for the PCIE module.
+ */
+static void
+csio_t5_pcie_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info pcie_intr_info[] = {
+ { MSTGRPPERR_F, "Master Response Read Queue parity error",
+ -1, 1 },
+ { MSTTIMEOUTPERR_F, "Master Timeout FIFO parity error", -1, 1 },
+ { MSIXSTIPERR_F, "MSI-X STI SRAM parity error", -1, 1 },
+ { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 },
+ { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 },
+ { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 },
+ { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 },
+ { PIOCPLGRPPERR_F, "PCI PIO completion Group FIFO parity error",
+ -1, 1 },
+ { PIOREQGRPPERR_F, "PCI PIO request Group FIFO parity error",
+ -1, 1 },
+ { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 },
+ { MSTTAGQPERR_F, "PCI master tag queue parity error", -1, 1 },
+ { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 },
+ { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 },
+ { DREQWRPERR_F, "PCI DMA channel write request parity error",
+ -1, 1 },
+ { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 },
+ { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 },
+ { HREQWRPERR_F, "PCI HMA channel count parity error", -1, 1 },
+ { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 },
+ { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 },
+ { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 },
+ { FIDPERR_F, "PCI FID parity error", -1, 1 },
+ { VFIDPERR_F, "PCI INTx clear parity error", -1, 1 },
+ { MAGRPPERR_F, "PCI MA group FIFO parity error", -1, 1 },
+ { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 },
+ { IPRXHDRGRPPERR_F, "PCI IP Rx header group parity error",
+ -1, 1 },
+ { IPRXDATAGRPPERR_F, "PCI IP Rx data group parity error",
+ -1, 1 },
+ { RPLPERR_F, "PCI IP replay buffer parity error", -1, 1 },
+ { IPSOTPERR_F, "PCI IP SOT buffer parity error", -1, 1 },
+ { TRGT1GRPPERR_F, "PCI TRGT1 group FIFOs parity error", -1, 1 },
+ { READRSPERR_F, "Outbound read error", -1, 0 },
+ { 0, NULL, 0, 0 }
+ };
+
+ int fat;
+ fat = csio_handle_intr_status(hw, PCIE_INT_CAUSE_A, pcie_intr_info);
+ if (fat)
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * csio_t5_flash_cfg_addr - return the address of the flash configuration file
+ * @hw: the HW module
+ *
+ * Return the address within the flash where the Firmware Configuration
+ * File is stored.
+ */
+static unsigned int
+csio_t5_flash_cfg_addr(struct csio_hw *hw)
+{
+ return FLASH_CFG_START;
+}
+
+/*
+ * csio_t5_mc_read - read from MC through backdoor accesses
+ * @hw: the hw module
+ * @idx: index to the register
+ * @addr: address of first byte requested
+ * @data: 64 bytes of data containing the requested address
+ * @ecc: where to store the corresponding 64-bit ECC word
+ *
+ * Read 64 bytes of data from MC starting at a 64-byte-aligned address
+ * that covers the requested address @addr. If @parity is not %NULL it
+ * is assigned the 64-bit ECC word for the read data.
+ */
+static int
+csio_t5_mc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
+ uint64_t *ecc)
+{
+ int i;
+ uint32_t mc_bist_cmd_reg, mc_bist_cmd_addr_reg, mc_bist_cmd_len_reg;
+ uint32_t mc_bist_data_pattern_reg;
+
+ mc_bist_cmd_reg = MC_REG(MC_P_BIST_CMD_A, idx);
+ mc_bist_cmd_addr_reg = MC_REG(MC_P_BIST_CMD_ADDR_A, idx);
+ mc_bist_cmd_len_reg = MC_REG(MC_P_BIST_CMD_LEN_A, idx);
+ mc_bist_data_pattern_reg = MC_REG(MC_P_BIST_DATA_PATTERN_A, idx);
+
+ if (csio_rd_reg32(hw, mc_bist_cmd_reg) & START_BIST_F)
+ return -EBUSY;
+ csio_wr_reg32(hw, addr & ~0x3fU, mc_bist_cmd_addr_reg);
+ csio_wr_reg32(hw, 64, mc_bist_cmd_len_reg);
+ csio_wr_reg32(hw, 0xc, mc_bist_data_pattern_reg);
+ csio_wr_reg32(hw, BIST_OPCODE_V(1) | START_BIST_F | BIST_CMD_GAP_V(1),
+ mc_bist_cmd_reg);
+ i = csio_hw_wait_op_done_val(hw, mc_bist_cmd_reg, START_BIST_F,
+ 0, 10, 1, NULL);
+ if (i)
+ return i;
+
+#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA_A, i)
+
+ for (i = 15; i >= 0; i--)
+ *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i)));
+ if (ecc)
+ *ecc = csio_rd_reg64(hw, MC_DATA(16));
+#undef MC_DATA
+ return 0;
+}
+
+/*
+ * csio_t5_edc_read - read from EDC through backdoor accesses
+ * @hw: the hw module
+ * @idx: which EDC to access
+ * @addr: address of first byte requested
+ * @data: 64 bytes of data containing the requested address
+ * @ecc: where to store the corresponding 64-bit ECC word
+ *
+ * Read 64 bytes of data from EDC starting at a 64-byte-aligned address
+ * that covers the requested address @addr. If @parity is not %NULL it
+ * is assigned the 64-bit ECC word for the read data.
+ */
+static int
+csio_t5_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
+ uint64_t *ecc)
+{
+ int i;
+ uint32_t edc_bist_cmd_reg, edc_bist_cmd_addr_reg, edc_bist_cmd_len_reg;
+ uint32_t edc_bist_cmd_data_pattern;
+
+/*
+ * These macro are missing in t4_regs.h file.
+ */
+#define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR)
+#define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx)
+
+ edc_bist_cmd_reg = EDC_REG_T5(EDC_H_BIST_CMD_A, idx);
+ edc_bist_cmd_addr_reg = EDC_REG_T5(EDC_H_BIST_CMD_ADDR_A, idx);
+ edc_bist_cmd_len_reg = EDC_REG_T5(EDC_H_BIST_CMD_LEN_A, idx);
+ edc_bist_cmd_data_pattern = EDC_REG_T5(EDC_H_BIST_DATA_PATTERN_A, idx);
+#undef EDC_REG_T5
+#undef EDC_STRIDE_T5
+
+ if (csio_rd_reg32(hw, edc_bist_cmd_reg) & START_BIST_F)
+ return -EBUSY;
+ csio_wr_reg32(hw, addr & ~0x3fU, edc_bist_cmd_addr_reg);
+ csio_wr_reg32(hw, 64, edc_bist_cmd_len_reg);
+ csio_wr_reg32(hw, 0xc, edc_bist_cmd_data_pattern);
+ csio_wr_reg32(hw, BIST_OPCODE_V(1) | START_BIST_F | BIST_CMD_GAP_V(1),
+ edc_bist_cmd_reg);
+ i = csio_hw_wait_op_done_val(hw, edc_bist_cmd_reg, START_BIST_F,
+ 0, 10, 1, NULL);
+ if (i)
+ return i;
+
+#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA_A, i) + idx)
+
+ for (i = 15; i >= 0; i--)
+ *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i)));
+ if (ecc)
+ *ecc = csio_rd_reg64(hw, EDC_DATA(16));
+#undef EDC_DATA
+ return 0;
+}
+
+/*
+ * csio_t5_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window
+ * @hw: the csio_hw
+ * @win: PCI-E memory Window to use
+ * @mtype: memory type: MEM_EDC0, MEM_EDC1, MEM_MC0 (or MEM_MC) or MEM_MC1
+ * @addr: address within indicated memory type
+ * @len: amount of memory to transfer
+ * @buf: host memory buffer
+ * @dir: direction of transfer 1 => read, 0 => write
+ *
+ * Reads/writes an [almost] arbitrary memory region in the firmware: the
+ * firmware memory address, length and host buffer must be aligned on
+ * 32-bit boundaries. The memory is transferred as a raw byte sequence
+ * from/to the firmware's memory. If this memory contains data
+ * structures which contain multi-byte integers, it's the callers
+ * responsibility to perform appropriate byte order conversions.
+ */
+static int
+csio_t5_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr,
+ u32 len, uint32_t *buf, int dir)
+{
+ u32 pos, start, offset, memoffset;
+ u32 edc_size, mc_size, win_pf, mem_reg, mem_aperture, mem_base;
+
+ /*
+ * Argument sanity checks ...
+ */
+ if ((addr & 0x3) || (len & 0x3))
+ return -EINVAL;
+
+ /* Offset into the region of memory which is being accessed
+ * MEM_EDC0 = 0
+ * MEM_EDC1 = 1
+ * MEM_MC = 2 -- T4
+ * MEM_MC0 = 2 -- For T5
+ * MEM_MC1 = 3 -- For T5
+ */
+ edc_size = EDRAM0_SIZE_G(csio_rd_reg32(hw, MA_EDRAM0_BAR_A));
+ if (mtype != MEM_MC1)
+ memoffset = (mtype * (edc_size * 1024 * 1024));
+ else {
+ mc_size = EXT_MEM_SIZE_G(csio_rd_reg32(hw,
+ MA_EXT_MEMORY_BAR_A));
+ memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
+ }
+
+ /* Determine the PCIE_MEM_ACCESS_OFFSET */
+ addr = addr + memoffset;
+
+ /*
+ * Each PCI-E Memory Window is programmed with a window size -- or
+ * "aperture" -- which controls the granularity of its mapping onto
+ * adapter memory. We need to grab that aperture in order to know
+ * how to use the specified window. The window is also programmed
+ * with the base address of the Memory Window in BAR0's address
+ * space. For T4 this is an absolute PCI-E Bus Address. For T5
+ * the address is relative to BAR0.
+ */
+ mem_reg = csio_rd_reg32(hw,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win));
+ mem_aperture = 1 << (WINDOW_V(mem_reg) + 10);
+ mem_base = PCIEOFST_G(mem_reg) << 10;
+
+ start = addr & ~(mem_aperture-1);
+ offset = addr - start;
+ win_pf = PFNUM_V(hw->pfn);
+
+ csio_dbg(hw, "csio_t5_memory_rw: mem_reg: 0x%x, mem_aperture: 0x%x\n",
+ mem_reg, mem_aperture);
+ csio_dbg(hw, "csio_t5_memory_rw: mem_base: 0x%x, mem_offset: 0x%x\n",
+ mem_base, memoffset);
+ csio_dbg(hw, "csio_t5_memory_rw: start:0x%x, offset:0x%x, win_pf:%d\n",
+ start, offset, win_pf);
+ csio_dbg(hw, "csio_t5_memory_rw: mtype: %d, addr: 0x%x, len: %d\n",
+ mtype, addr, len);
+
+ for (pos = start; len > 0; pos += mem_aperture, offset = 0) {
+ /*
+ * Move PCI-E Memory Window to our current transfer
+ * position. Read it back to ensure that changes propagate
+ * before we attempt to use the new value.
+ */
+ csio_wr_reg32(hw, pos | win_pf,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win));
+ csio_rd_reg32(hw,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win));
+
+ while (offset < mem_aperture && len > 0) {
+ if (dir)
+ *buf++ = csio_rd_reg32(hw, mem_base + offset);
+ else
+ csio_wr_reg32(hw, *buf++, mem_base + offset);
+
+ offset += sizeof(__be32);
+ len -= sizeof(__be32);
+ }
+ }
+ return 0;
+}
+
+/*
+ * csio_t5_dfs_create_ext_mem - setup debugfs for MC0 or MC1 to read the values
+ * @hw: the csio_hw
+ *
+ * This function creates files in the debugfs with external memory region
+ * MC0 & MC1.
+ */
+static void
+csio_t5_dfs_create_ext_mem(struct csio_hw *hw)
+{
+ u32 size;
+ int i = csio_rd_reg32(hw, MA_TARGET_MEM_ENABLE_A);
+
+ if (i & EXT_MEM_ENABLE_F) {
+ size = csio_rd_reg32(hw, MA_EXT_MEMORY_BAR_A);
+ csio_add_debugfs_mem(hw, "mc0", MEM_MC0,
+ EXT_MEM_SIZE_G(size));
+ }
+ if (i & EXT_MEM1_ENABLE_F) {
+ size = csio_rd_reg32(hw, MA_EXT_MEMORY1_BAR_A);
+ csio_add_debugfs_mem(hw, "mc1", MEM_MC1,
+ EXT_MEM_SIZE_G(size));
+ }
+}
+
+/* T5 adapter specific function */
+struct csio_hw_chip_ops t5_ops = {
+ .chip_set_mem_win = csio_t5_set_mem_win,
+ .chip_pcie_intr_handler = csio_t5_pcie_intr_handler,
+ .chip_flash_cfg_addr = csio_t5_flash_cfg_addr,
+ .chip_mc_read = csio_t5_mc_read,
+ .chip_edc_read = csio_t5_edc_read,
+ .chip_memory_rw = csio_t5_memory_rw,
+ .chip_dfs_create_ext_mem = csio_t5_dfs_create_ext_mem,
+};
diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c
new file mode 100644
index 000000000000..db0c2174430a
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_init.c
@@ -0,0 +1,1253 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/notifier.h>
+#include <linux/kdebug.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/export.h>
+
+#include "csio_init.h"
+#include "csio_defs.h"
+
+#define CSIO_MIN_MEMPOOL_SZ 64
+
+static struct dentry *csio_debugfs_root;
+
+static struct scsi_transport_template *csio_fcoe_transport;
+static struct scsi_transport_template *csio_fcoe_transport_vport;
+
+/*
+ * debugfs support
+ */
+static ssize_t
+csio_mem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ loff_t pos = *ppos;
+ loff_t avail = file_inode(file)->i_size;
+ unsigned int mem = (uintptr_t)file->private_data & 3;
+ struct csio_hw *hw = file->private_data - mem;
+
+ if (pos < 0)
+ return -EINVAL;
+ if (pos >= avail)
+ return 0;
+ if (count > avail - pos)
+ count = avail - pos;
+
+ while (count) {
+ size_t len;
+ int ret, ofst;
+ __be32 data[16];
+
+ if (mem == MEM_MC)
+ ret = hw->chip_ops->chip_mc_read(hw, 0, pos,
+ data, NULL);
+ else
+ ret = hw->chip_ops->chip_edc_read(hw, mem, pos,
+ data, NULL);
+ if (ret)
+ return ret;
+
+ ofst = pos % sizeof(data);
+ len = min(count, sizeof(data) - ofst);
+ if (copy_to_user(buf, (u8 *)data + ofst, len))
+ return -EFAULT;
+
+ buf += len;
+ pos += len;
+ count -= len;
+ }
+ count = pos - *ppos;
+ *ppos = pos;
+ return count;
+}
+
+static const struct file_operations csio_mem_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = csio_mem_read,
+ .llseek = default_llseek,
+};
+
+void csio_add_debugfs_mem(struct csio_hw *hw, const char *name,
+ unsigned int idx, unsigned int size_mb)
+{
+ debugfs_create_file_size(name, S_IRUSR, hw->debugfs_root,
+ (void *)hw + idx, &csio_mem_debugfs_fops,
+ size_mb << 20);
+}
+
+static int csio_setup_debugfs(struct csio_hw *hw)
+{
+ int i;
+
+ if (IS_ERR_OR_NULL(hw->debugfs_root))
+ return -1;
+
+ i = csio_rd_reg32(hw, MA_TARGET_MEM_ENABLE_A);
+ if (i & EDRAM0_ENABLE_F)
+ csio_add_debugfs_mem(hw, "edc0", MEM_EDC0, 5);
+ if (i & EDRAM1_ENABLE_F)
+ csio_add_debugfs_mem(hw, "edc1", MEM_EDC1, 5);
+
+ hw->chip_ops->chip_dfs_create_ext_mem(hw);
+ return 0;
+}
+
+/*
+ * csio_dfs_create - Creates and sets up per-hw debugfs.
+ *
+ */
+static int
+csio_dfs_create(struct csio_hw *hw)
+{
+ if (csio_debugfs_root) {
+ hw->debugfs_root = debugfs_create_dir(pci_name(hw->pdev),
+ csio_debugfs_root);
+ csio_setup_debugfs(hw);
+ }
+
+ return 0;
+}
+
+/*
+ * csio_dfs_destroy - Destroys per-hw debugfs.
+ */
+static void
+csio_dfs_destroy(struct csio_hw *hw)
+{
+ debugfs_remove_recursive(hw->debugfs_root);
+}
+
+/*
+ * csio_dfs_init - Debug filesystem initialization for the module.
+ *
+ */
+static void
+csio_dfs_init(void)
+{
+ csio_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+}
+
+/*
+ * csio_dfs_exit - debugfs cleanup for the module.
+ */
+static void
+csio_dfs_exit(void)
+{
+ debugfs_remove(csio_debugfs_root);
+}
+
+/*
+ * csio_pci_init - PCI initialization.
+ * @pdev: PCI device.
+ * @bars: Bitmask of bars to be requested.
+ *
+ * Initializes the PCI function by enabling MMIO, setting bus
+ * mastership and setting DMA mask.
+ */
+static int
+csio_pci_init(struct pci_dev *pdev, int *bars)
+{
+ int rv = -ENODEV;
+
+ *bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
+ if (pci_enable_device_mem(pdev))
+ goto err;
+
+ if (pci_request_selected_regions(pdev, *bars, KBUILD_MODNAME))
+ goto err_disable_device;
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ rv = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (rv)
+ rv = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (rv) {
+ rv = -ENODEV;
+ dev_err(&pdev->dev, "No suitable DMA available.\n");
+ goto err_release_regions;
+ }
+
+ return 0;
+
+err_release_regions:
+ pci_release_selected_regions(pdev, *bars);
+err_disable_device:
+ pci_disable_device(pdev);
+err:
+ return rv;
+
+}
+
+/*
+ * csio_pci_exit - PCI unitialization.
+ * @pdev: PCI device.
+ * @bars: Bars to be released.
+ *
+ */
+static void
+csio_pci_exit(struct pci_dev *pdev, int *bars)
+{
+ pci_release_selected_regions(pdev, *bars);
+ pci_disable_device(pdev);
+}
+
+/*
+ * csio_hw_init_workers - Initialize the HW module's worker threads.
+ * @hw: HW module.
+ *
+ */
+static void
+csio_hw_init_workers(struct csio_hw *hw)
+{
+ INIT_WORK(&hw->evtq_work, csio_evtq_worker);
+}
+
+static void
+csio_hw_exit_workers(struct csio_hw *hw)
+{
+ cancel_work_sync(&hw->evtq_work);
+}
+
+static int
+csio_create_queues(struct csio_hw *hw)
+{
+ int i, j;
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ int rv;
+ struct csio_scsi_cpu_info *info;
+
+ if (hw->flags & CSIO_HWF_Q_FW_ALLOCED)
+ return 0;
+
+ if (hw->intr_mode != CSIO_IM_MSIX) {
+ rv = csio_wr_iq_create(hw, NULL, hw->intr_iq_idx,
+ 0, hw->pport[0].portid, false, NULL);
+ if (rv != 0) {
+ csio_err(hw, " Forward Interrupt IQ failed!: %d\n", rv);
+ return rv;
+ }
+ }
+
+ /* FW event queue */
+ rv = csio_wr_iq_create(hw, NULL, hw->fwevt_iq_idx,
+ csio_get_fwevt_intr_idx(hw),
+ hw->pport[0].portid, true, NULL);
+ if (rv != 0) {
+ csio_err(hw, "FW event IQ config failed!: %d\n", rv);
+ return rv;
+ }
+
+ /* Create mgmt queue */
+ rv = csio_wr_eq_create(hw, NULL, mgmtm->eq_idx,
+ mgmtm->iq_idx, hw->pport[0].portid, NULL);
+
+ if (rv != 0) {
+ csio_err(hw, "Mgmt EQ create failed!: %d\n", rv);
+ goto err;
+ }
+
+ /* Create SCSI queues */
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+
+ for (j = 0; j < info->max_cpus; j++) {
+ struct csio_scsi_qset *sqset = &hw->sqset[i][j];
+
+ rv = csio_wr_iq_create(hw, NULL, sqset->iq_idx,
+ sqset->intr_idx, i, false, NULL);
+ if (rv != 0) {
+ csio_err(hw,
+ "SCSI module IQ config failed [%d][%d]:%d\n",
+ i, j, rv);
+ goto err;
+ }
+ rv = csio_wr_eq_create(hw, NULL, sqset->eq_idx,
+ sqset->iq_idx, i, NULL);
+ if (rv != 0) {
+ csio_err(hw,
+ "SCSI module EQ config failed [%d][%d]:%d\n",
+ i, j, rv);
+ goto err;
+ }
+ } /* for all CPUs */
+ } /* For all ports */
+
+ hw->flags |= CSIO_HWF_Q_FW_ALLOCED;
+ return 0;
+err:
+ csio_wr_destroy_queues(hw, true);
+ return -EINVAL;
+}
+
+/*
+ * csio_config_queues - Configure the DMA queues.
+ * @hw: HW module.
+ *
+ * Allocates memory for queues are registers them with FW.
+ */
+int
+csio_config_queues(struct csio_hw *hw)
+{
+ int i, j, idx, k = 0;
+ int rv;
+ struct csio_scsi_qset *sqset;
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ struct csio_scsi_qset *orig;
+ struct csio_scsi_cpu_info *info;
+
+ if (hw->flags & CSIO_HWF_Q_MEM_ALLOCED)
+ return csio_create_queues(hw);
+
+ /* Calculate number of SCSI queues for MSIX we would like */
+ hw->num_scsi_msix_cpus = num_online_cpus();
+ hw->num_sqsets = num_online_cpus() * hw->num_pports;
+
+ if (hw->num_sqsets > CSIO_MAX_SCSI_QSETS) {
+ hw->num_sqsets = CSIO_MAX_SCSI_QSETS;
+ hw->num_scsi_msix_cpus = CSIO_MAX_SCSI_CPU;
+ }
+
+ /* Initialize max_cpus, may get reduced during msix allocations */
+ for (i = 0; i < hw->num_pports; i++)
+ hw->scsi_cpu_info[i].max_cpus = hw->num_scsi_msix_cpus;
+
+ csio_dbg(hw, "nsqsets:%d scpus:%d\n",
+ hw->num_sqsets, hw->num_scsi_msix_cpus);
+
+ csio_intr_enable(hw);
+
+ if (hw->intr_mode != CSIO_IM_MSIX) {
+
+ /* Allocate Forward interrupt iq. */
+ hw->intr_iq_idx = csio_wr_alloc_q(hw, CSIO_INTR_IQSIZE,
+ CSIO_INTR_WRSIZE, CSIO_INGRESS,
+ (void *)hw, 0, 0, NULL);
+ if (hw->intr_iq_idx == -1) {
+ csio_err(hw,
+ "Forward interrupt queue creation failed\n");
+ goto intr_disable;
+ }
+ }
+
+ /* Allocate the FW evt queue */
+ hw->fwevt_iq_idx = csio_wr_alloc_q(hw, CSIO_FWEVT_IQSIZE,
+ CSIO_FWEVT_WRSIZE,
+ CSIO_INGRESS, (void *)hw,
+ CSIO_FWEVT_FLBUFS, 0,
+ csio_fwevt_intx_handler);
+ if (hw->fwevt_iq_idx == -1) {
+ csio_err(hw, "FW evt queue creation failed\n");
+ goto intr_disable;
+ }
+
+ /* Allocate the mgmt queue */
+ mgmtm->eq_idx = csio_wr_alloc_q(hw, CSIO_MGMT_EQSIZE,
+ CSIO_MGMT_EQ_WRSIZE,
+ CSIO_EGRESS, (void *)hw, 0, 0, NULL);
+ if (mgmtm->eq_idx == -1) {
+ csio_err(hw, "Failed to alloc egress queue for mgmt module\n");
+ goto intr_disable;
+ }
+
+ /* Use FW IQ for MGMT req completion */
+ mgmtm->iq_idx = hw->fwevt_iq_idx;
+
+ /* Allocate SCSI queues */
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+
+ for (j = 0; j < hw->num_scsi_msix_cpus; j++) {
+ sqset = &hw->sqset[i][j];
+
+ if (j >= info->max_cpus) {
+ k = j % info->max_cpus;
+ orig = &hw->sqset[i][k];
+ sqset->eq_idx = orig->eq_idx;
+ sqset->iq_idx = orig->iq_idx;
+ continue;
+ }
+
+ idx = csio_wr_alloc_q(hw, csio_scsi_eqsize, 0,
+ CSIO_EGRESS, (void *)hw, 0, 0,
+ NULL);
+ if (idx == -1) {
+ csio_err(hw, "EQ creation failed for idx:%d\n",
+ idx);
+ goto intr_disable;
+ }
+
+ sqset->eq_idx = idx;
+
+ idx = csio_wr_alloc_q(hw, CSIO_SCSI_IQSIZE,
+ CSIO_SCSI_IQ_WRSZ, CSIO_INGRESS,
+ (void *)hw, 0, 0,
+ csio_scsi_intx_handler);
+ if (idx == -1) {
+ csio_err(hw, "IQ creation failed for idx:%d\n",
+ idx);
+ goto intr_disable;
+ }
+ sqset->iq_idx = idx;
+ } /* for all CPUs */
+ } /* For all ports */
+
+ hw->flags |= CSIO_HWF_Q_MEM_ALLOCED;
+
+ rv = csio_create_queues(hw);
+ if (rv != 0)
+ goto intr_disable;
+
+ /*
+ * Now request IRQs for the vectors. In the event of a failure,
+ * cleanup is handled internally by this function.
+ */
+ rv = csio_request_irqs(hw);
+ if (rv != 0)
+ return -EINVAL;
+
+ return 0;
+
+intr_disable:
+ csio_intr_disable(hw, false);
+
+ return -EINVAL;
+}
+
+static int
+csio_resource_alloc(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ int rv = -ENOMEM;
+
+ wrm->num_q = ((CSIO_MAX_SCSI_QSETS * 2) + CSIO_HW_NIQ +
+ CSIO_HW_NEQ + CSIO_HW_NFLQ + CSIO_HW_NINTXQ);
+
+ hw->mb_mempool = mempool_create_kmalloc_pool(CSIO_MIN_MEMPOOL_SZ,
+ sizeof(struct csio_mb));
+ if (!hw->mb_mempool)
+ goto err;
+
+ hw->rnode_mempool = mempool_create_kmalloc_pool(CSIO_MIN_MEMPOOL_SZ,
+ sizeof(struct csio_rnode));
+ if (!hw->rnode_mempool)
+ goto err_free_mb_mempool;
+
+ hw->scsi_dma_pool = dma_pool_create("csio_scsi_dma_pool",
+ &hw->pdev->dev, CSIO_SCSI_RSP_LEN,
+ 8, 0);
+ if (!hw->scsi_dma_pool)
+ goto err_free_rn_pool;
+
+ return 0;
+
+err_free_rn_pool:
+ mempool_destroy(hw->rnode_mempool);
+ hw->rnode_mempool = NULL;
+err_free_mb_mempool:
+ mempool_destroy(hw->mb_mempool);
+ hw->mb_mempool = NULL;
+err:
+ return rv;
+}
+
+static void
+csio_resource_free(struct csio_hw *hw)
+{
+ dma_pool_destroy(hw->scsi_dma_pool);
+ hw->scsi_dma_pool = NULL;
+ mempool_destroy(hw->rnode_mempool);
+ hw->rnode_mempool = NULL;
+ mempool_destroy(hw->mb_mempool);
+ hw->mb_mempool = NULL;
+}
+
+/*
+ * csio_hw_alloc - Allocate and initialize the HW module.
+ * @pdev: PCI device.
+ *
+ * Allocates HW structure, DMA, memory resources, maps BARS to
+ * host memory and initializes HW module.
+ */
+static struct csio_hw *csio_hw_alloc(struct pci_dev *pdev)
+{
+ struct csio_hw *hw;
+
+ hw = kzalloc(sizeof(struct csio_hw), GFP_KERNEL);
+ if (!hw)
+ goto err;
+
+ hw->pdev = pdev;
+ strscpy(hw->drv_version, CSIO_DRV_VERSION,
+ sizeof(hw->drv_version));
+
+ /* memory pool/DMA pool allocation */
+ if (csio_resource_alloc(hw))
+ goto err_free_hw;
+
+ /* Get the start address of registers from BAR 0 */
+ hw->regstart = ioremap(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (!hw->regstart) {
+ csio_err(hw, "Could not map BAR 0, regstart = %p\n",
+ hw->regstart);
+ goto err_resource_free;
+ }
+
+ csio_hw_init_workers(hw);
+
+ if (csio_hw_init(hw))
+ goto err_unmap_bar;
+
+ csio_dfs_create(hw);
+
+ csio_dbg(hw, "hw:%p\n", hw);
+
+ return hw;
+
+err_unmap_bar:
+ csio_hw_exit_workers(hw);
+ iounmap(hw->regstart);
+err_resource_free:
+ csio_resource_free(hw);
+err_free_hw:
+ kfree(hw);
+err:
+ return NULL;
+}
+
+/*
+ * csio_hw_free - Uninitialize and free the HW module.
+ * @hw: The HW module
+ *
+ * Disable interrupts, uninit the HW module, free resources, free hw.
+ */
+static void
+csio_hw_free(struct csio_hw *hw)
+{
+ csio_intr_disable(hw, true);
+ csio_hw_exit_workers(hw);
+ csio_hw_exit(hw);
+ iounmap(hw->regstart);
+ csio_dfs_destroy(hw);
+ csio_resource_free(hw);
+ kfree(hw);
+}
+
+/**
+ * csio_shost_init - Create and initialize the lnode module.
+ * @hw: The HW module.
+ * @dev: The device associated with this invocation.
+ * @probe: Called from probe context or not?
+ * @pln: Parent lnode if any.
+ *
+ * Allocates lnode structure via scsi_host_alloc, initializes
+ * shost, initializes lnode module and registers with SCSI ML
+ * via scsi_host_add. This function is shared between physical and
+ * virtual node ports.
+ */
+struct csio_lnode *
+csio_shost_init(struct csio_hw *hw, struct device *dev,
+ bool probe, struct csio_lnode *pln)
+{
+ struct Scsi_Host *shost = NULL;
+ struct csio_lnode *ln;
+
+ csio_fcoe_shost_template.cmd_per_lun = csio_lun_qdepth;
+ csio_fcoe_shost_vport_template.cmd_per_lun = csio_lun_qdepth;
+
+ /*
+ * hw->pdev is the physical port's PCI dev structure,
+ * which will be different from the NPIV dev structure.
+ */
+ if (dev == &hw->pdev->dev)
+ shost = scsi_host_alloc(
+ &csio_fcoe_shost_template,
+ sizeof(struct csio_lnode));
+ else
+ shost = scsi_host_alloc(
+ &csio_fcoe_shost_vport_template,
+ sizeof(struct csio_lnode));
+
+ if (!shost)
+ goto err;
+
+ ln = shost_priv(shost);
+ memset(ln, 0, sizeof(struct csio_lnode));
+
+ /* Link common lnode to this lnode */
+ ln->dev_num = (shost->host_no << 16);
+
+ shost->can_queue = CSIO_MAX_QUEUE;
+ shost->this_id = -1;
+ shost->unique_id = shost->host_no;
+ shost->max_cmd_len = 16; /* Max CDB length supported */
+ shost->max_id = min_t(uint32_t, csio_fcoe_rnodes,
+ hw->fres_info.max_ssns);
+ shost->max_lun = CSIO_MAX_LUN;
+ if (dev == &hw->pdev->dev)
+ shost->transportt = csio_fcoe_transport;
+ else
+ shost->transportt = csio_fcoe_transport_vport;
+
+ /* root lnode */
+ if (!hw->rln)
+ hw->rln = ln;
+
+ /* Other initialization here: Common, Transport specific */
+ if (csio_lnode_init(ln, hw, pln))
+ goto err_shost_put;
+
+ if (scsi_add_host_with_dma(shost, dev, &hw->pdev->dev))
+ goto err_lnode_exit;
+
+ return ln;
+
+err_lnode_exit:
+ csio_lnode_exit(ln);
+err_shost_put:
+ scsi_host_put(shost);
+err:
+ return NULL;
+}
+
+/**
+ * csio_shost_exit - De-instantiate the shost.
+ * @ln: The lnode module corresponding to the shost.
+ *
+ */
+void
+csio_shost_exit(struct csio_lnode *ln)
+{
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ /* Inform transport */
+ fc_remove_host(shost);
+
+ /* Inform SCSI ML */
+ scsi_remove_host(shost);
+
+ /* Flush all the events, so that any rnode removal events
+ * already queued are all handled, before we remove the lnode.
+ */
+ spin_lock_irq(&hw->lock);
+ csio_evtq_flush(hw);
+ spin_unlock_irq(&hw->lock);
+
+ csio_lnode_exit(ln);
+ scsi_host_put(shost);
+}
+
+struct csio_lnode *
+csio_lnode_alloc(struct csio_hw *hw)
+{
+ return csio_shost_init(hw, &hw->pdev->dev, false, NULL);
+}
+
+void
+csio_lnodes_block_request(struct csio_hw *hw)
+{
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct csio_lnode *ln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Blocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_block_requests(shost);
+
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_unblock_request(struct csio_hw *hw)
+{
+ struct csio_lnode *ln;
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "unblocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_unblock_requests(shost);
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_block_by_port(struct csio_hw *hw, uint8_t portid)
+{
+ struct csio_lnode *ln;
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ if (sln->portid != portid)
+ continue;
+
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Blocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_block_requests(shost);
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_unblock_by_port(struct csio_hw *hw, uint8_t portid)
+{
+ struct csio_lnode *ln;
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ if (sln->portid != portid)
+ continue;
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "unblocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_unblock_requests(shost);
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_exit(struct csio_hw *hw, bool npiv)
+{
+ struct csio_lnode *sln;
+ struct csio_lnode *ln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "lnodes_exit: Failed to allocate lnodes_list.\n");
+ return;
+ }
+
+ /* Get all child lnodes(NPIV ports) */
+ spin_lock_irq(&hw->lock);
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ /* Delete NPIV lnodes */
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Deleting child lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ fc_vport_terminate(ln->fc_vport);
+ }
+
+ /* Delete only npiv lnodes */
+ if (npiv)
+ goto free_lnodes;
+
+ cur_cnt = 0;
+ /* Get all physical lnodes */
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ lnode_list[cur_cnt++] = sln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ /* Delete physical lnodes */
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Deleting parent lnode: %p\n", lnode_list[ii]);
+ csio_shost_exit(lnode_list[ii]);
+ }
+
+free_lnodes:
+ kfree(lnode_list);
+}
+
+/*
+ * csio_lnode_init_post: Set lnode attributes after starting HW.
+ * @ln: lnode.
+ *
+ */
+static void
+csio_lnode_init_post(struct csio_lnode *ln)
+{
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+
+ csio_fchost_attr_init(ln);
+
+ scsi_scan_host(shost);
+}
+
+/*
+ * csio_probe_one - Instantiate this function.
+ * @pdev: PCI device
+ * @id: Device ID
+ *
+ * This is the .probe() callback of the driver. This function:
+ * - Initializes the PCI function by enabling MMIO, setting bus
+ * mastership and setting DMA mask.
+ * - Allocates HW structure, DMA, memory resources, maps BARS to
+ * host memory and initializes HW module.
+ * - Allocates lnode structure via scsi_host_alloc, initializes
+ * shost, initialized lnode module and registers with SCSI ML
+ * via scsi_host_add.
+ * - Enables interrupts, and starts the chip by kicking off the
+ * HW state machine.
+ * - Once hardware is ready, initiated scan of the host via
+ * scsi_scan_host.
+ */
+static int csio_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int rv;
+ int bars;
+ int i;
+ struct csio_hw *hw;
+ struct csio_lnode *ln;
+
+ /* probe only T5 and T6 cards */
+ if (!csio_is_t5((pdev->device & CSIO_HW_CHIP_MASK)) &&
+ !csio_is_t6((pdev->device & CSIO_HW_CHIP_MASK)))
+ return -ENODEV;
+
+ rv = csio_pci_init(pdev, &bars);
+ if (rv)
+ goto err;
+
+ hw = csio_hw_alloc(pdev);
+ if (!hw) {
+ rv = -ENODEV;
+ goto err_pci_exit;
+ }
+
+ if (!pcie_relaxed_ordering_enabled(pdev))
+ hw->flags |= CSIO_HWF_ROOT_NO_RELAXED_ORDERING;
+
+ pci_set_drvdata(pdev, hw);
+
+ rv = csio_hw_start(hw);
+ if (rv) {
+ if (rv == -EINVAL) {
+ dev_err(&pdev->dev,
+ "Failed to start FW, continuing in debug mode.\n");
+ return 0;
+ }
+ goto err_lnode_exit;
+ }
+
+ sprintf(hw->fwrev_str, "%u.%u.%u.%u\n",
+ FW_HDR_FW_VER_MAJOR_G(hw->fwrev),
+ FW_HDR_FW_VER_MINOR_G(hw->fwrev),
+ FW_HDR_FW_VER_MICRO_G(hw->fwrev),
+ FW_HDR_FW_VER_BUILD_G(hw->fwrev));
+
+ for (i = 0; i < hw->num_pports; i++) {
+ ln = csio_shost_init(hw, &pdev->dev, true, NULL);
+ if (!ln) {
+ rv = -ENODEV;
+ break;
+ }
+ /* Initialize portid */
+ ln->portid = hw->pport[i].portid;
+
+ spin_lock_irq(&hw->lock);
+ if (csio_lnode_start(ln) != 0)
+ rv = -ENODEV;
+ spin_unlock_irq(&hw->lock);
+
+ if (rv)
+ break;
+
+ csio_lnode_init_post(ln);
+ }
+
+ if (rv)
+ goto err_lnode_exit;
+
+ return 0;
+
+err_lnode_exit:
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+ csio_hw_stop(hw);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+ csio_lnodes_exit(hw, 0);
+ csio_hw_free(hw);
+err_pci_exit:
+ csio_pci_exit(pdev, &bars);
+err:
+ dev_err(&pdev->dev, "probe of device failed: %d\n", rv);
+ return rv;
+}
+
+/*
+ * csio_remove_one - Remove one instance of the driver at this PCI function.
+ * @pdev: PCI device
+ *
+ * Used during hotplug operation.
+ */
+static void csio_remove_one(struct pci_dev *pdev)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+ int bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+
+ /* Stops lnode, Rnode s/m
+ * Quiesce IOs.
+ * All sessions with remote ports are unregistered.
+ */
+ csio_hw_stop(hw);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+
+ csio_lnodes_exit(hw, 0);
+ csio_hw_free(hw);
+ csio_pci_exit(pdev, &bars);
+}
+
+/*
+ * csio_pci_error_detected - PCI error was detected
+ * @pdev: PCI device
+ *
+ */
+static pci_ers_result_t
+csio_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+
+ /* Post PCI error detected evt to HW s/m
+ * HW s/m handles this evt by quiescing IOs, unregisters rports
+ * and finally takes the device to offline.
+ */
+ csio_post_event(&hw->sm, CSIO_HWE_PCIERR_DETECTED);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+ csio_lnodes_exit(hw, 0);
+ csio_intr_disable(hw, true);
+ pci_disable_device(pdev);
+ return state == pci_channel_io_perm_failure ?
+ PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET;
+}
+
+/*
+ * csio_pci_slot_reset - PCI slot has been reset.
+ * @pdev: PCI device
+ *
+ */
+static pci_ers_result_t
+csio_pci_slot_reset(struct pci_dev *pdev)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+ int ready;
+
+ if (pci_enable_device(pdev)) {
+ dev_err(&pdev->dev, "cannot re-enable device in slot reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_set_master(pdev);
+ pci_restore_state(pdev);
+
+ /* Bring HW s/m to ready state.
+ * but don't resume IOs.
+ */
+ spin_lock_irq(&hw->lock);
+ csio_post_event(&hw->sm, CSIO_HWE_PCIERR_SLOT_RESET);
+ ready = csio_is_hw_ready(hw);
+ spin_unlock_irq(&hw->lock);
+
+ if (ready) {
+ return PCI_ERS_RESULT_RECOVERED;
+ } else {
+ dev_err(&pdev->dev, "Can't initialize HW when in slot reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+}
+
+/*
+ * csio_pci_resume - Resume normal operations
+ * @pdev: PCI device
+ *
+ */
+static void
+csio_pci_resume(struct pci_dev *pdev)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+ struct csio_lnode *ln;
+ int rv = 0;
+ int i;
+
+ /* Bring the LINK UP and Resume IO */
+
+ for (i = 0; i < hw->num_pports; i++) {
+ ln = csio_shost_init(hw, &pdev->dev, true, NULL);
+ if (!ln) {
+ rv = -ENODEV;
+ break;
+ }
+ /* Initialize portid */
+ ln->portid = hw->pport[i].portid;
+
+ spin_lock_irq(&hw->lock);
+ if (csio_lnode_start(ln) != 0)
+ rv = -ENODEV;
+ spin_unlock_irq(&hw->lock);
+
+ if (rv)
+ break;
+
+ csio_lnode_init_post(ln);
+ }
+
+ if (rv)
+ goto err_resume_exit;
+
+ return;
+
+err_resume_exit:
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+ csio_hw_stop(hw);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+ csio_lnodes_exit(hw, 0);
+ csio_hw_free(hw);
+ dev_err(&pdev->dev, "resume of device failed: %d\n", rv);
+}
+
+static const struct pci_error_handlers csio_err_handler = {
+ .error_detected = csio_pci_error_detected,
+ .slot_reset = csio_pci_slot_reset,
+ .resume = csio_pci_resume,
+};
+
+/*
+ * Macros needed to support the PCI Device ID Table ...
+ */
+#define CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN \
+ static const struct pci_device_id csio_pci_tbl[] = {
+/* Define for FCoE uses PF6 */
+#define CH_PCI_DEVICE_ID_FUNCTION 0x6
+
+#define CH_PCI_ID_TABLE_ENTRY(devid) \
+ { PCI_VDEVICE(CHELSIO, (devid)), 0 }
+
+#define CH_PCI_DEVICE_ID_TABLE_DEFINE_END { 0, } }
+
+#include "t4_pci_id_tbl.h"
+
+static struct pci_driver csio_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = csio_pci_tbl,
+ .probe = csio_probe_one,
+ .remove = csio_remove_one,
+ .err_handler = &csio_err_handler,
+};
+
+/*
+ * csio_init - Chelsio storage driver initialization function.
+ *
+ */
+static int __init
+csio_init(void)
+{
+ int rv = -ENOMEM;
+
+ pr_info("%s %s\n", CSIO_DRV_DESC, CSIO_DRV_VERSION);
+
+ csio_dfs_init();
+
+ csio_fcoe_transport = fc_attach_transport(&csio_fc_transport_funcs);
+ if (!csio_fcoe_transport)
+ goto err;
+
+ csio_fcoe_transport_vport =
+ fc_attach_transport(&csio_fc_transport_vport_funcs);
+ if (!csio_fcoe_transport_vport)
+ goto err_vport;
+
+ rv = pci_register_driver(&csio_pci_driver);
+ if (rv)
+ goto err_pci;
+
+ return 0;
+
+err_pci:
+ fc_release_transport(csio_fcoe_transport_vport);
+err_vport:
+ fc_release_transport(csio_fcoe_transport);
+err:
+ csio_dfs_exit();
+ return rv;
+}
+
+/*
+ * csio_exit - Chelsio storage driver uninitialization .
+ *
+ * Function that gets called in the unload path.
+ */
+static void __exit
+csio_exit(void)
+{
+ pci_unregister_driver(&csio_pci_driver);
+ csio_dfs_exit();
+ fc_release_transport(csio_fcoe_transport_vport);
+ fc_release_transport(csio_fcoe_transport);
+}
+
+module_init(csio_init);
+module_exit(csio_exit);
+MODULE_AUTHOR(CSIO_DRV_AUTHOR);
+MODULE_DESCRIPTION(CSIO_DRV_DESC);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DEVICE_TABLE(pci, csio_pci_tbl);
+MODULE_VERSION(CSIO_DRV_VERSION);
+MODULE_FIRMWARE(FW_FNAME_T5);
+MODULE_FIRMWARE(FW_FNAME_T6);
+MODULE_SOFTDEP("pre: cxgb4");
diff --git a/drivers/scsi/csiostor/csio_init.h b/drivers/scsi/csiostor/csio_init.h
new file mode 100644
index 000000000000..20244254325a
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_init.h
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_INIT_H__
+#define __CSIO_INIT_H__
+
+#include <linux/pci.h>
+#include <linux/if_ether.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "csio_scsi.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+#include "csio_hw.h"
+
+#define CSIO_DRV_AUTHOR "Chelsio Communications"
+#define CSIO_DRV_DESC "Chelsio FCoE driver"
+#define CSIO_DRV_VERSION "1.0.0-ko"
+
+extern struct fc_function_template csio_fc_transport_funcs;
+extern struct fc_function_template csio_fc_transport_vport_funcs;
+
+void csio_fchost_attr_init(struct csio_lnode *);
+
+/* INTx handlers */
+void csio_scsi_intx_handler(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *, void *);
+
+void csio_fwevt_intx_handler(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *, void *);
+
+/* Common os lnode APIs */
+void csio_lnodes_block_request(struct csio_hw *);
+void csio_lnodes_unblock_request(struct csio_hw *);
+void csio_lnodes_block_by_port(struct csio_hw *, uint8_t);
+void csio_lnodes_unblock_by_port(struct csio_hw *, uint8_t);
+
+struct csio_lnode *csio_shost_init(struct csio_hw *, struct device *, bool,
+ struct csio_lnode *);
+void csio_shost_exit(struct csio_lnode *);
+void csio_lnodes_exit(struct csio_hw *, bool);
+
+/* DebugFS helper routines */
+void csio_add_debugfs_mem(struct csio_hw *, const char *,
+ unsigned int, unsigned int);
+
+static inline struct Scsi_Host *
+csio_ln_to_shost(struct csio_lnode *ln)
+{
+ return container_of((void *)ln, struct Scsi_Host, hostdata[0]);
+}
+
+/* SCSI -- locking version of get/put ioreqs */
+static inline struct csio_ioreq *
+csio_get_scsi_ioreq_lock(struct csio_hw *hw, struct csio_scsim *scsim)
+{
+ struct csio_ioreq *ioreq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scsim->freelist_lock, flags);
+ ioreq = csio_get_scsi_ioreq(scsim);
+ spin_unlock_irqrestore(&scsim->freelist_lock, flags);
+
+ return ioreq;
+}
+
+static inline void
+csio_put_scsi_ioreq_lock(struct csio_hw *hw, struct csio_scsim *scsim,
+ struct csio_ioreq *ioreq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scsim->freelist_lock, flags);
+ csio_put_scsi_ioreq(scsim, ioreq);
+ spin_unlock_irqrestore(&scsim->freelist_lock, flags);
+}
+
+/* Called in interrupt context */
+static inline void
+csio_put_scsi_ioreq_list_lock(struct csio_hw *hw, struct csio_scsim *scsim,
+ struct list_head *reqlist, int n)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scsim->freelist_lock, flags);
+ csio_put_scsi_ioreq_list(scsim, reqlist, n);
+ spin_unlock_irqrestore(&scsim->freelist_lock, flags);
+}
+
+/* Called in interrupt context */
+static inline void
+csio_put_scsi_ddp_list_lock(struct csio_hw *hw, struct csio_scsim *scsim,
+ struct list_head *reqlist, int n)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ csio_put_scsi_ddp_list(scsim, reqlist, n);
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+#endif /* ifndef __CSIO_INIT_H__ */
diff --git a/drivers/scsi/csiostor/csio_isr.c b/drivers/scsi/csiostor/csio_isr.c
new file mode 100644
index 000000000000..b2540402fafc
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_isr.c
@@ -0,0 +1,610 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+
+#include "csio_init.h"
+#include "csio_hw.h"
+
+static irqreturn_t
+csio_nondata_isr(int irq, void *dev_id)
+{
+ struct csio_hw *hw = (struct csio_hw *) dev_id;
+ int rv;
+ unsigned long flags;
+
+ if (unlikely(!hw))
+ return IRQ_NONE;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ spin_lock_irqsave(&hw->lock, flags);
+ csio_hw_slow_intr_handler(hw);
+ rv = csio_mb_isr_handler(hw);
+
+ if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irqrestore(&hw->lock, flags);
+ schedule_work(&hw->evtq_work);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_fwevt_handler - Common FW event handler routine.
+ * @hw: HW module.
+ *
+ * This is the ISR for FW events. It is shared b/w MSIX
+ * and INTx handlers.
+ */
+static void
+csio_fwevt_handler(struct csio_hw *hw)
+{
+ int rv;
+ unsigned long flags;
+
+ rv = csio_fwevtq_handler(hw);
+
+ spin_lock_irqsave(&hw->lock, flags);
+ if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irqrestore(&hw->lock, flags);
+ schedule_work(&hw->evtq_work);
+ return;
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+} /* csio_fwevt_handler */
+
+/*
+ * csio_fwevt_isr() - FW events MSIX ISR
+ * @irq:
+ * @dev_id:
+ *
+ * Process WRs on the FW event queue.
+ *
+ */
+static irqreturn_t
+csio_fwevt_isr(int irq, void *dev_id)
+{
+ struct csio_hw *hw = (struct csio_hw *) dev_id;
+
+ if (unlikely(!hw))
+ return IRQ_NONE;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ csio_fwevt_handler(hw);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_fwevt_isr() - INTx wrapper for handling FW events.
+ * @irq:
+ * @dev_id:
+ */
+void
+csio_fwevt_intx_handler(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv)
+{
+ csio_fwevt_handler(hw);
+} /* csio_fwevt_intx_handler */
+
+/*
+ * csio_process_scsi_cmpl - Process a SCSI WR completion.
+ * @hw: HW module.
+ * @wr: The completed WR from the ingress queue.
+ * @len: Length of the WR.
+ * @flb: Freelist buffer array.
+ *
+ */
+static void
+csio_process_scsi_cmpl(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *cbfn_q)
+{
+ struct csio_ioreq *ioreq;
+ uint8_t *scsiwr;
+ uint8_t subop;
+ void *cmnd;
+ unsigned long flags;
+
+ ioreq = csio_scsi_cmpl_handler(hw, wr, len, flb, NULL, &scsiwr);
+ if (likely(ioreq)) {
+ if (unlikely(*scsiwr == FW_SCSI_ABRT_CLS_WR)) {
+ subop = FW_SCSI_ABRT_CLS_WR_SUB_OPCODE_GET(
+ ((struct fw_scsi_abrt_cls_wr *)
+ scsiwr)->sub_opcode_to_chk_all_io);
+
+ csio_dbg(hw, "%s cmpl recvd ioreq:%p status:%d\n",
+ subop ? "Close" : "Abort",
+ ioreq, ioreq->wr_status);
+
+ spin_lock_irqsave(&hw->lock, flags);
+ if (subop)
+ csio_scsi_closed(ioreq,
+ (struct list_head *)cbfn_q);
+ else
+ csio_scsi_aborted(ioreq,
+ (struct list_head *)cbfn_q);
+ /*
+ * We call scsi_done for I/Os that driver thinks aborts
+ * have timed out. If there is a race caused by FW
+ * completing abort at the exact same time that the
+ * driver has deteced the abort timeout, the following
+ * check prevents calling of scsi_done twice for the
+ * same command: once from the eh_abort_handler, another
+ * from csio_scsi_isr_handler(). This also avoids the
+ * need to check if csio_scsi_cmnd(req) is NULL in the
+ * fast path.
+ */
+ cmnd = csio_scsi_cmnd(ioreq);
+ if (unlikely(cmnd == NULL))
+ list_del_init(&ioreq->sm.sm_list);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ if (unlikely(cmnd == NULL))
+ csio_put_scsi_ioreq_lock(hw,
+ csio_hw_to_scsim(hw), ioreq);
+ } else {
+ spin_lock_irqsave(&hw->lock, flags);
+ csio_scsi_completed(ioreq, (struct list_head *)cbfn_q);
+ spin_unlock_irqrestore(&hw->lock, flags);
+ }
+ }
+}
+
+/*
+ * csio_scsi_isr_handler() - Common SCSI ISR handler.
+ * @iq: Ingress queue pointer.
+ *
+ * Processes SCSI completions on the SCSI IQ indicated by scm->iq_idx
+ * by calling csio_wr_process_iq_idx. If there are completions on the
+ * isr_cbfn_q, yank them out into a local queue and call their io_cbfns.
+ * Once done, add these completions onto the freelist.
+ * This routine is shared b/w MSIX and INTx.
+ */
+static inline irqreturn_t
+csio_scsi_isr_handler(struct csio_q *iq)
+{
+ struct csio_hw *hw = (struct csio_hw *)iq->owner;
+ LIST_HEAD(cbfn_q);
+ struct list_head *tmp;
+ struct csio_scsim *scm;
+ struct csio_ioreq *ioreq;
+ int isr_completions = 0;
+
+ scm = csio_hw_to_scsim(hw);
+
+ if (unlikely(csio_wr_process_iq(hw, iq, csio_process_scsi_cmpl,
+ &cbfn_q) != 0))
+ return IRQ_NONE;
+
+ /* Call back the completion routines */
+ list_for_each(tmp, &cbfn_q) {
+ ioreq = (struct csio_ioreq *)tmp;
+ isr_completions++;
+ ioreq->io_cbfn(hw, ioreq);
+ /* Release ddp buffer if used for this req */
+ if (unlikely(ioreq->dcopy))
+ csio_put_scsi_ddp_list_lock(hw, scm, &ioreq->gen_list,
+ ioreq->nsge);
+ }
+
+ if (isr_completions) {
+ /* Return the ioreqs back to ioreq->freelist */
+ csio_put_scsi_ioreq_list_lock(hw, scm, &cbfn_q,
+ isr_completions);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_scsi_isr() - SCSI MSIX handler
+ * @irq:
+ * @dev_id:
+ *
+ * This is the top level SCSI MSIX handler. Calls csio_scsi_isr_handler()
+ * for handling SCSI completions.
+ */
+static irqreturn_t
+csio_scsi_isr(int irq, void *dev_id)
+{
+ struct csio_q *iq = (struct csio_q *) dev_id;
+ struct csio_hw *hw;
+
+ if (unlikely(!iq))
+ return IRQ_NONE;
+
+ hw = (struct csio_hw *)iq->owner;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ csio_scsi_isr_handler(iq);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_scsi_intx_handler() - SCSI INTx handler
+ * @irq:
+ * @dev_id:
+ *
+ * This is the top level SCSI INTx handler. Calls csio_scsi_isr_handler()
+ * for handling SCSI completions.
+ */
+void
+csio_scsi_intx_handler(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv)
+{
+ struct csio_q *iq = priv;
+
+ csio_scsi_isr_handler(iq);
+
+} /* csio_scsi_intx_handler */
+
+/*
+ * csio_fcoe_isr() - INTx/MSI interrupt service routine for FCoE.
+ * @irq:
+ * @dev_id:
+ *
+ *
+ */
+static irqreturn_t
+csio_fcoe_isr(int irq, void *dev_id)
+{
+ struct csio_hw *hw = (struct csio_hw *) dev_id;
+ struct csio_q *intx_q = NULL;
+ int rv;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
+
+ if (unlikely(!hw))
+ return IRQ_NONE;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ /* Disable the interrupt for this PCI function. */
+ if (hw->intr_mode == CSIO_IM_INTX)
+ csio_wr_reg32(hw, 0, MYPF_REG(PCIE_PF_CLI_A));
+
+ /*
+ * The read in the following function will flush the
+ * above write.
+ */
+ if (csio_hw_slow_intr_handler(hw))
+ ret = IRQ_HANDLED;
+
+ /* Get the INTx Forward interrupt IQ. */
+ intx_q = csio_get_q(hw, hw->intr_iq_idx);
+
+ CSIO_DB_ASSERT(intx_q);
+
+ /* IQ handler is not possible for intx_q, hence pass in NULL */
+ if (likely(csio_wr_process_iq(hw, intx_q, NULL, NULL) == 0))
+ ret = IRQ_HANDLED;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ rv = csio_mb_isr_handler(hw);
+ if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irqrestore(&hw->lock, flags);
+ schedule_work(&hw->evtq_work);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ return ret;
+}
+
+static void
+csio_add_msix_desc(struct csio_hw *hw)
+{
+ int i;
+ struct csio_msix_entries *entryp = &hw->msix_entries[0];
+ int k = CSIO_EXTRA_VECS;
+ int len = sizeof(entryp->desc) - 1;
+ int cnt = hw->num_sqsets + k;
+
+ /* Non-data vector */
+ memset(entryp->desc, 0, len + 1);
+ snprintf(entryp->desc, len, "csio-%02x:%02x:%x-nondata",
+ CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw));
+
+ entryp++;
+ memset(entryp->desc, 0, len + 1);
+ snprintf(entryp->desc, len, "csio-%02x:%02x:%x-fwevt",
+ CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw));
+ entryp++;
+
+ /* Name SCSI vecs */
+ for (i = k; i < cnt; i++, entryp++) {
+ memset(entryp->desc, 0, len + 1);
+ snprintf(entryp->desc, len, "csio-%02x:%02x:%x-scsi%d",
+ CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw),
+ CSIO_PCI_FUNC(hw), i - CSIO_EXTRA_VECS);
+ }
+}
+
+int
+csio_request_irqs(struct csio_hw *hw)
+{
+ int rv, i, j, k = 0;
+ struct csio_msix_entries *entryp = &hw->msix_entries[0];
+ struct csio_scsi_cpu_info *info;
+ struct pci_dev *pdev = hw->pdev;
+
+ if (hw->intr_mode != CSIO_IM_MSIX) {
+ rv = request_irq(pci_irq_vector(pdev, 0), csio_fcoe_isr,
+ hw->intr_mode == CSIO_IM_MSI ? 0 : IRQF_SHARED,
+ KBUILD_MODNAME, hw);
+ if (rv) {
+ csio_err(hw, "Failed to allocate interrupt line.\n");
+ goto out_free_irqs;
+ }
+
+ goto out;
+ }
+
+ /* Add the MSIX vector descriptions */
+ csio_add_msix_desc(hw);
+
+ rv = request_irq(pci_irq_vector(pdev, k), csio_nondata_isr, 0,
+ entryp[k].desc, hw);
+ if (rv) {
+ csio_err(hw, "IRQ request failed for vec %d err:%d\n",
+ pci_irq_vector(pdev, k), rv);
+ goto out_free_irqs;
+ }
+
+ entryp[k++].dev_id = hw;
+
+ rv = request_irq(pci_irq_vector(pdev, k), csio_fwevt_isr, 0,
+ entryp[k].desc, hw);
+ if (rv) {
+ csio_err(hw, "IRQ request failed for vec %d err:%d\n",
+ pci_irq_vector(pdev, k), rv);
+ goto out_free_irqs;
+ }
+
+ entryp[k++].dev_id = (void *)hw;
+
+ /* Allocate IRQs for SCSI */
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+ for (j = 0; j < info->max_cpus; j++, k++) {
+ struct csio_scsi_qset *sqset = &hw->sqset[i][j];
+ struct csio_q *q = hw->wrm.q_arr[sqset->iq_idx];
+
+ rv = request_irq(pci_irq_vector(pdev, k), csio_scsi_isr, 0,
+ entryp[k].desc, q);
+ if (rv) {
+ csio_err(hw,
+ "IRQ request failed for vec %d err:%d\n",
+ pci_irq_vector(pdev, k), rv);
+ goto out_free_irqs;
+ }
+
+ entryp[k].dev_id = q;
+
+ } /* for all scsi cpus */
+ } /* for all ports */
+
+out:
+ hw->flags |= CSIO_HWF_HOST_INTR_ENABLED;
+ return 0;
+
+out_free_irqs:
+ for (i = 0; i < k; i++)
+ free_irq(pci_irq_vector(pdev, i), hw->msix_entries[i].dev_id);
+ pci_free_irq_vectors(hw->pdev);
+ return -EINVAL;
+}
+
+/* Reduce per-port max possible CPUs */
+static void
+csio_reduce_sqsets(struct csio_hw *hw, int cnt)
+{
+ int i;
+ struct csio_scsi_cpu_info *info;
+
+ while (cnt < hw->num_sqsets) {
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+ if (info->max_cpus > 1) {
+ info->max_cpus--;
+ hw->num_sqsets--;
+ if (hw->num_sqsets <= cnt)
+ break;
+ }
+ }
+ }
+
+ csio_dbg(hw, "Reduced sqsets to %d\n", hw->num_sqsets);
+}
+
+static void csio_calc_sets(struct irq_affinity *affd, unsigned int nvecs)
+{
+ struct csio_hw *hw = affd->priv;
+ u8 i;
+
+ if (!nvecs)
+ return;
+
+ if (nvecs < hw->num_pports) {
+ affd->nr_sets = 1;
+ affd->set_size[0] = nvecs;
+ return;
+ }
+
+ affd->nr_sets = hw->num_pports;
+ for (i = 0; i < hw->num_pports; i++)
+ affd->set_size[i] = nvecs / hw->num_pports;
+}
+
+static int
+csio_enable_msix(struct csio_hw *hw)
+{
+ int i, j, k, n, min, cnt;
+ int extra = CSIO_EXTRA_VECS;
+ struct csio_scsi_cpu_info *info;
+ struct irq_affinity desc = {
+ .pre_vectors = CSIO_EXTRA_VECS,
+ .calc_sets = csio_calc_sets,
+ .priv = hw,
+ };
+
+ if (hw->num_pports > IRQ_AFFINITY_MAX_SETS)
+ return -ENOSPC;
+
+ min = hw->num_pports + extra;
+ cnt = hw->num_sqsets + extra;
+
+ /* Max vectors required based on #niqs configured in fw */
+ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS || !csio_is_hw_master(hw))
+ cnt = min_t(uint8_t, hw->cfg_niq, cnt);
+
+ csio_dbg(hw, "FW supp #niq:%d, trying %d msix's\n", hw->cfg_niq, cnt);
+
+ cnt = pci_alloc_irq_vectors_affinity(hw->pdev, min, cnt,
+ PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, &desc);
+ if (cnt < 0)
+ return cnt;
+
+ if (cnt < (hw->num_sqsets + extra)) {
+ csio_dbg(hw, "Reducing sqsets to %d\n", cnt - extra);
+ csio_reduce_sqsets(hw, cnt - extra);
+ }
+
+ /* Distribute vectors */
+ k = 0;
+ csio_set_nondata_intr_idx(hw, k);
+ csio_set_mb_intr_idx(csio_hw_to_mbm(hw), k++);
+ csio_set_fwevt_intr_idx(hw, k++);
+
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+
+ for (j = 0; j < hw->num_scsi_msix_cpus; j++) {
+ n = (j % info->max_cpus) + k;
+ hw->sqset[i][j].intr_idx = n;
+ }
+
+ k += info->max_cpus;
+ }
+
+ return 0;
+}
+
+void
+csio_intr_enable(struct csio_hw *hw)
+{
+ hw->intr_mode = CSIO_IM_NONE;
+ hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED;
+
+ /* Try MSIX, then MSI or fall back to INTx */
+ if ((csio_msi == 2) && !csio_enable_msix(hw))
+ hw->intr_mode = CSIO_IM_MSIX;
+ else {
+ /* Max iqs required based on #niqs configured in fw */
+ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS ||
+ !csio_is_hw_master(hw)) {
+ int extra = CSIO_EXTRA_MSI_IQS;
+
+ if (hw->cfg_niq < (hw->num_sqsets + extra)) {
+ csio_dbg(hw, "Reducing sqsets to %d\n",
+ hw->cfg_niq - extra);
+ csio_reduce_sqsets(hw, hw->cfg_niq - extra);
+ }
+ }
+
+ if ((csio_msi == 1) && !pci_enable_msi(hw->pdev))
+ hw->intr_mode = CSIO_IM_MSI;
+ else
+ hw->intr_mode = CSIO_IM_INTX;
+ }
+
+ csio_dbg(hw, "Using %s interrupt mode.\n",
+ (hw->intr_mode == CSIO_IM_MSIX) ? "MSIX" :
+ ((hw->intr_mode == CSIO_IM_MSI) ? "MSI" : "INTx"));
+}
+
+void
+csio_intr_disable(struct csio_hw *hw, bool free)
+{
+ csio_hw_intr_disable(hw);
+
+ if (free) {
+ int i;
+
+ switch (hw->intr_mode) {
+ case CSIO_IM_MSIX:
+ for (i = 0; i < hw->num_sqsets + CSIO_EXTRA_VECS; i++) {
+ free_irq(pci_irq_vector(hw->pdev, i),
+ hw->msix_entries[i].dev_id);
+ }
+ break;
+ case CSIO_IM_MSI:
+ case CSIO_IM_INTX:
+ free_irq(pci_irq_vector(hw->pdev, 0), hw);
+ break;
+ default:
+ break;
+ }
+ }
+
+ pci_free_irq_vectors(hw->pdev);
+ hw->intr_mode = CSIO_IM_NONE;
+ hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED;
+}
diff --git a/drivers/scsi/csiostor/csio_lnode.c b/drivers/scsi/csiostor/csio_lnode.c
new file mode 100644
index 000000000000..6cc1d53165a0
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_lnode.c
@@ -0,0 +1,2152 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/utsname.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+#include <linux/unaligned.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_gs.h>
+#include <scsi/fc/fc_ms.h>
+
+#include "csio_hw.h"
+#include "csio_mb.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+
+int csio_fcoe_rnodes = 1024;
+int csio_fdmi_enable = 1;
+
+#define PORT_ID_PTR(_x) ((uint8_t *)(&_x) + 1)
+
+/* Lnode SM declarations */
+static void csio_lns_uninit(struct csio_lnode *, enum csio_ln_ev);
+static void csio_lns_online(struct csio_lnode *, enum csio_ln_ev);
+static void csio_lns_ready(struct csio_lnode *, enum csio_ln_ev);
+static void csio_lns_offline(struct csio_lnode *, enum csio_ln_ev);
+
+static int csio_ln_mgmt_submit_req(struct csio_ioreq *,
+ void (*io_cbfn) (struct csio_hw *, struct csio_ioreq *),
+ enum fcoe_cmn_type, struct csio_dma_buf *, uint32_t);
+
+/* LN event mapping */
+static enum csio_ln_ev fwevt_to_lnevt[] = {
+ CSIO_LNE_NONE, /* None */
+ CSIO_LNE_NONE, /* PLOGI_ACC_RCVD */
+ CSIO_LNE_NONE, /* PLOGI_RJT_RCVD */
+ CSIO_LNE_NONE, /* PLOGI_RCVD */
+ CSIO_LNE_NONE, /* PLOGO_RCVD */
+ CSIO_LNE_NONE, /* PRLI_ACC_RCVD */
+ CSIO_LNE_NONE, /* PRLI_RJT_RCVD */
+ CSIO_LNE_NONE, /* PRLI_RCVD */
+ CSIO_LNE_NONE, /* PRLO_RCVD */
+ CSIO_LNE_NONE, /* NPORT_ID_CHGD */
+ CSIO_LNE_LOGO, /* FLOGO_RCVD */
+ CSIO_LNE_LOGO, /* CLR_VIRT_LNK_RCVD */
+ CSIO_LNE_FAB_INIT_DONE,/* FLOGI_ACC_RCVD */
+ CSIO_LNE_NONE, /* FLOGI_RJT_RCVD */
+ CSIO_LNE_FAB_INIT_DONE,/* FDISC_ACC_RCVD */
+ CSIO_LNE_NONE, /* FDISC_RJT_RCVD */
+ CSIO_LNE_NONE, /* FLOGI_TMO_MAX_RETRY */
+ CSIO_LNE_NONE, /* IMPL_LOGO_ADISC_ACC */
+ CSIO_LNE_NONE, /* IMPL_LOGO_ADISC_RJT */
+ CSIO_LNE_NONE, /* IMPL_LOGO_ADISC_CNFLT */
+ CSIO_LNE_NONE, /* PRLI_TMO */
+ CSIO_LNE_NONE, /* ADISC_TMO */
+ CSIO_LNE_NONE, /* RSCN_DEV_LOST */
+ CSIO_LNE_NONE, /* SCR_ACC_RCVD */
+ CSIO_LNE_NONE, /* ADISC_RJT_RCVD */
+ CSIO_LNE_NONE, /* LOGO_SNT */
+ CSIO_LNE_NONE, /* PROTO_ERR_IMPL_LOGO */
+};
+
+#define CSIO_FWE_TO_LNE(_evt) ((_evt > PROTO_ERR_IMPL_LOGO) ? \
+ CSIO_LNE_NONE : \
+ fwevt_to_lnevt[_evt])
+
+#define csio_ct_rsp(cp) (((struct fc_ct_hdr *)cp)->ct_cmd)
+#define csio_ct_reason(cp) (((struct fc_ct_hdr *)cp)->ct_reason)
+#define csio_ct_expl(cp) (((struct fc_ct_hdr *)cp)->ct_explan)
+#define csio_ct_get_pld(cp) ((void *)(((uint8_t *)cp) + FC_CT_HDR_LEN))
+
+/*
+ * csio_ln_match_by_portid - lookup lnode using given portid.
+ * @hw: HW module
+ * @portid: port-id.
+ *
+ * If found, returns lnode matching given portid otherwise returns NULL.
+ */
+static struct csio_lnode *
+csio_ln_lookup_by_portid(struct csio_hw *hw, uint8_t portid)
+{
+ struct csio_lnode *ln;
+ struct list_head *tmp;
+
+ /* Match siblings lnode with portid */
+ list_for_each(tmp, &hw->sln_head) {
+ ln = (struct csio_lnode *) tmp;
+ if (ln->portid == portid)
+ return ln;
+ }
+
+ return NULL;
+}
+
+/*
+ * csio_ln_lookup_by_vnpi - Lookup lnode using given vnp id.
+ * @hw - HW module
+ * @vnpi - vnp index.
+ * Returns - If found, returns lnode matching given vnp id
+ * otherwise returns NULL.
+ */
+static struct csio_lnode *
+csio_ln_lookup_by_vnpi(struct csio_hw *hw, uint32_t vnp_id)
+{
+ struct list_head *tmp1, *tmp2;
+ struct csio_lnode *sln = NULL, *cln = NULL;
+
+ if (list_empty(&hw->sln_head)) {
+ CSIO_INC_STATS(hw, n_lnlkup_miss);
+ return NULL;
+ }
+ /* Traverse sibling lnodes */
+ list_for_each(tmp1, &hw->sln_head) {
+ sln = (struct csio_lnode *) tmp1;
+
+ /* Match sibling lnode */
+ if (sln->vnp_flowid == vnp_id)
+ return sln;
+
+ if (list_empty(&sln->cln_head))
+ continue;
+
+ /* Traverse children lnodes */
+ list_for_each(tmp2, &sln->cln_head) {
+ cln = (struct csio_lnode *) tmp2;
+
+ if (cln->vnp_flowid == vnp_id)
+ return cln;
+ }
+ }
+ CSIO_INC_STATS(hw, n_lnlkup_miss);
+ return NULL;
+}
+
+/**
+ * csio_lnode_lookup_by_wwpn - Lookup lnode using given wwpn.
+ * @hw: HW module.
+ * @wwpn: WWPN.
+ *
+ * If found, returns lnode matching given wwpn, returns NULL otherwise.
+ */
+struct csio_lnode *
+csio_lnode_lookup_by_wwpn(struct csio_hw *hw, uint8_t *wwpn)
+{
+ struct list_head *tmp1, *tmp2;
+ struct csio_lnode *sln = NULL, *cln = NULL;
+
+ if (list_empty(&hw->sln_head)) {
+ CSIO_INC_STATS(hw, n_lnlkup_miss);
+ return NULL;
+ }
+ /* Traverse sibling lnodes */
+ list_for_each(tmp1, &hw->sln_head) {
+ sln = (struct csio_lnode *) tmp1;
+
+ /* Match sibling lnode */
+ if (!memcmp(csio_ln_wwpn(sln), wwpn, 8))
+ return sln;
+
+ if (list_empty(&sln->cln_head))
+ continue;
+
+ /* Traverse children lnodes */
+ list_for_each(tmp2, &sln->cln_head) {
+ cln = (struct csio_lnode *) tmp2;
+
+ if (!memcmp(csio_ln_wwpn(cln), wwpn, 8))
+ return cln;
+ }
+ }
+ return NULL;
+}
+
+/* FDMI */
+static void
+csio_fill_ct_iu(void *buf, uint8_t type, uint8_t sub_type, uint16_t op)
+{
+ struct fc_ct_hdr *cmd = (struct fc_ct_hdr *)buf;
+ cmd->ct_rev = FC_CT_REV;
+ cmd->ct_fs_type = type;
+ cmd->ct_fs_subtype = sub_type;
+ cmd->ct_cmd = htons(op);
+}
+
+static int
+csio_hostname(uint8_t *buf, size_t buf_len)
+{
+ if (snprintf(buf, buf_len, "%s", init_utsname()->nodename) > 0)
+ return 0;
+ return -1;
+}
+
+static int
+csio_osname(uint8_t *buf, size_t buf_len)
+{
+ if (snprintf(buf, buf_len, "%s %s %s",
+ init_utsname()->sysname,
+ init_utsname()->release,
+ init_utsname()->version) > 0)
+ return 0;
+
+ return -1;
+}
+
+static inline void
+csio_append_attrib(uint8_t **ptr, uint16_t type, void *val, size_t val_len)
+{
+ uint16_t len;
+ struct fc_fdmi_attr_entry *ae = (struct fc_fdmi_attr_entry *)*ptr;
+
+ if (WARN_ON(val_len > U16_MAX))
+ return;
+
+ len = val_len;
+
+ ae->type = htons(type);
+ len += 4; /* includes attribute type and length */
+ len = (len + 3) & ~3; /* should be multiple of 4 bytes */
+ ae->len = htons(len);
+ memcpy(ae->value, val, val_len);
+ if (len > val_len)
+ memset(ae->value + val_len, 0, len - val_len);
+ *ptr += len;
+}
+
+/*
+ * csio_ln_fdmi_done - FDMI registeration completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_done(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ void *cmd;
+ struct csio_lnode *ln = fdmi_req->lnode;
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi rpa cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi rpa cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+}
+
+/*
+ * csio_ln_fdmi_rhba_cbfn - RHBA completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_rhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ void *cmd;
+ uint8_t *pld;
+ uint32_t len = 0;
+ __be32 val;
+ __be16 mfs;
+ uint32_t numattrs = 0;
+ struct csio_lnode *ln = fdmi_req->lnode;
+ struct fs_fdmi_attrs *attrib_blk;
+ struct fc_fdmi_port_name *port_name;
+ uint8_t buf[64];
+ uint8_t *fc4_type;
+ unsigned long flags;
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi rhba cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi rhba cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+
+ if (!csio_is_rnode_ready(fdmi_req->rnode)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ return;
+ }
+
+ /* Prepare CT hdr for RPA cmd */
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_RPA);
+
+ /* Prepare RPA payload */
+ pld = (uint8_t *)csio_ct_get_pld(cmd);
+ port_name = (struct fc_fdmi_port_name *)pld;
+ memcpy(&port_name->portname, csio_ln_wwpn(ln), 8);
+ pld += sizeof(*port_name);
+
+ /* Start appending Port attributes */
+ attrib_blk = (struct fs_fdmi_attrs *)pld;
+ attrib_blk->numattrs = 0;
+ len += sizeof(attrib_blk->numattrs);
+ pld += sizeof(attrib_blk->numattrs);
+
+ fc4_type = &buf[0];
+ memset(fc4_type, 0, FC_FDMI_PORT_ATTR_FC4TYPES_LEN);
+ fc4_type[2] = 1;
+ fc4_type[7] = 1;
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_FC4TYPES,
+ fc4_type, FC_FDMI_PORT_ATTR_FC4TYPES_LEN);
+ numattrs++;
+ val = htonl(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_SUPPORTEDSPEED,
+ &val,
+ FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN);
+ numattrs++;
+
+ if (hw->pport[ln->portid].link_speed == FW_PORT_CAP_SPEED_1G)
+ val = htonl(FC_PORTSPEED_1GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP_SPEED_10G)
+ val = htonl(FC_PORTSPEED_10GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_25G)
+ val = htonl(FC_PORTSPEED_25GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_40G)
+ val = htonl(FC_PORTSPEED_40GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_50G)
+ val = htonl(FC_PORTSPEED_50GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_100G)
+ val = htonl(FC_PORTSPEED_100GBIT);
+ else
+ val = htonl(CSIO_HBA_PORTSPEED_UNKNOWN);
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_CURRENTPORTSPEED,
+ &val, FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN);
+ numattrs++;
+
+ mfs = ln->ln_sparm.csp.sp_bb_data;
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_MAXFRAMESIZE,
+ &mfs, sizeof(mfs));
+ numattrs++;
+
+ strcpy(buf, "csiostor");
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_OSDEVICENAME, buf,
+ strlen(buf));
+ numattrs++;
+
+ if (!csio_hostname(buf, sizeof(buf))) {
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_HOSTNAME,
+ buf, strlen(buf));
+ numattrs++;
+ }
+ attrib_blk->numattrs = htonl(numattrs);
+ len = (uint32_t)(pld - (uint8_t *)cmd);
+
+ /* Submit FDMI RPA request */
+ spin_lock_irqsave(&hw->lock, flags);
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_done,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi rpa req\n");
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+/*
+ * csio_ln_fdmi_dprt_cbfn - DPRT completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_dprt_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ void *cmd;
+ uint8_t *pld;
+ uint32_t len = 0;
+ uint32_t numattrs = 0;
+ __be32 maxpayload = htonl(65536);
+ struct fc_fdmi_hba_identifier *hbaid;
+ struct csio_lnode *ln = fdmi_req->lnode;
+ struct fc_fdmi_rpl *reg_pl;
+ struct fs_fdmi_attrs *attrib_blk;
+ uint8_t buf[64];
+ unsigned long flags;
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi dprt cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ if (!csio_is_rnode_ready(fdmi_req->rnode)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ return;
+ }
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi dprt cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+
+ /* Prepare CT hdr for RHBA cmd */
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_RHBA);
+ len = FC_CT_HDR_LEN;
+
+ /* Prepare RHBA payload */
+ pld = (uint8_t *)csio_ct_get_pld(cmd);
+ hbaid = (struct fc_fdmi_hba_identifier *)pld;
+ memcpy(&hbaid->id, csio_ln_wwpn(ln), 8); /* HBA identifer */
+ pld += sizeof(*hbaid);
+
+ /* Register one port per hba */
+ reg_pl = (struct fc_fdmi_rpl *)pld;
+ reg_pl->numport = htonl(1);
+ memcpy(&reg_pl->port[0].portname, csio_ln_wwpn(ln), 8);
+ pld += sizeof(*reg_pl);
+
+ /* Start appending HBA attributes hba */
+ attrib_blk = (struct fs_fdmi_attrs *)pld;
+ attrib_blk->numattrs = 0;
+ len += sizeof(attrib_blk->numattrs);
+ pld += sizeof(attrib_blk->numattrs);
+
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_NODENAME, csio_ln_wwnn(ln),
+ FC_FDMI_HBA_ATTR_NODENAME_LEN);
+ numattrs++;
+
+ memset(buf, 0, sizeof(buf));
+
+ strcpy(buf, "Chelsio Communications");
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MANUFACTURER, buf,
+ strlen(buf));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_SERIALNUMBER,
+ hw->vpd.sn, sizeof(hw->vpd.sn));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MODEL, hw->vpd.id,
+ sizeof(hw->vpd.id));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MODELDESCRIPTION,
+ hw->model_desc, strlen(hw->model_desc));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_HARDWAREVERSION,
+ hw->hw_ver, sizeof(hw->hw_ver));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_FIRMWAREVERSION,
+ hw->fwrev_str, strlen(hw->fwrev_str));
+ numattrs++;
+
+ if (!csio_osname(buf, sizeof(buf))) {
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_OSNAMEVERSION,
+ buf, strlen(buf));
+ numattrs++;
+ }
+
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MAXCTPAYLOAD,
+ &maxpayload, FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN);
+ len = (uint32_t)(pld - (uint8_t *)cmd);
+ numattrs++;
+ attrib_blk->numattrs = htonl(numattrs);
+
+ /* Submit FDMI RHBA request */
+ spin_lock_irqsave(&hw->lock, flags);
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_rhba_cbfn,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi rhba req\n");
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+/*
+ * csio_ln_fdmi_dhba_cbfn - DHBA completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_dhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ struct csio_lnode *ln = fdmi_req->lnode;
+ void *cmd;
+ struct fc_fdmi_port_name *port_name;
+ uint32_t len;
+ unsigned long flags;
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi dhba cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ if (!csio_is_rnode_ready(fdmi_req->rnode)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ return;
+ }
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi dhba cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+
+ /* Send FDMI cmd to de-register any Port attributes if registered
+ * before
+ */
+
+ /* Prepare FDMI DPRT cmd */
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_DPRT);
+ len = FC_CT_HDR_LEN;
+ port_name = (struct fc_fdmi_port_name *)csio_ct_get_pld(cmd);
+ memcpy(&port_name->portname, csio_ln_wwpn(ln), 8);
+ len += sizeof(*port_name);
+
+ /* Submit FDMI request */
+ spin_lock_irqsave(&hw->lock, flags);
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_dprt_cbfn,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi dprt req\n");
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+/**
+ * csio_ln_fdmi_start - Start an FDMI request.
+ * @ln: lnode
+ * @context: session context
+ *
+ * Issued with lock held.
+ */
+int
+csio_ln_fdmi_start(struct csio_lnode *ln, void *context)
+{
+ struct csio_ioreq *fdmi_req;
+ struct csio_rnode *fdmi_rn = (struct csio_rnode *)context;
+ void *cmd;
+ struct fc_fdmi_hba_identifier *hbaid;
+ uint32_t len;
+
+ if (!(ln->flags & CSIO_LNF_FDMI_ENABLE))
+ return -EPROTONOSUPPORT;
+
+ if (!csio_is_rnode_ready(fdmi_rn))
+ CSIO_INC_STATS(ln, n_fdmi_err);
+
+ /* Send FDMI cmd to de-register any HBA attributes if registered
+ * before
+ */
+
+ fdmi_req = ln->mgmt_req;
+ fdmi_req->lnode = ln;
+ fdmi_req->rnode = fdmi_rn;
+
+ /* Prepare FDMI DHBA cmd */
+ cmd = fdmi_req->dma_buf.vaddr;
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_DHBA);
+ len = FC_CT_HDR_LEN;
+
+ hbaid = (struct fc_fdmi_hba_identifier *)csio_ct_get_pld(cmd);
+ memcpy(&hbaid->id, csio_ln_wwpn(ln), 8);
+ len += sizeof(*hbaid);
+
+ /* Submit FDMI request */
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_dhba_cbfn,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi dhba req\n");
+ }
+
+ return 0;
+}
+
+/*
+ * csio_ln_vnp_read_cbfn - vnp read completion handler.
+ * @hw: HW lnode
+ * @cbfn: Completion handler.
+ *
+ * Reads vnp response and updates ln parameters.
+ */
+static void
+csio_ln_vnp_read_cbfn(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ struct csio_lnode *ln = ((struct csio_lnode *)mbp->priv);
+ struct fw_fcoe_vnp_cmd *rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+ struct fc_els_csp *csp;
+ struct fc_els_cssp *clsp;
+ enum fw_retval retval;
+ __be32 nport_id = 0;
+
+ retval = FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16));
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FCOE VNP read cmd returned error:0x%x\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+
+ memcpy(ln->mac, rsp->vnport_mac, sizeof(ln->mac));
+ memcpy(&nport_id, &rsp->vnport_mac[3], sizeof(uint8_t)*3);
+ ln->nport_id = ntohl(nport_id);
+ ln->nport_id = ln->nport_id >> 8;
+
+ /* Update WWNs */
+ /*
+ * This may look like a duplication of what csio_fcoe_enable_link()
+ * does, but is absolutely necessary if the vnpi changes between
+ * a FCOE LINK UP and FCOE LINK DOWN.
+ */
+ memcpy(csio_ln_wwnn(ln), rsp->vnport_wwnn, 8);
+ memcpy(csio_ln_wwpn(ln), rsp->vnport_wwpn, 8);
+
+ /* Copy common sparam */
+ csp = (struct fc_els_csp *)rsp->cmn_srv_parms;
+ ln->ln_sparm.csp.sp_hi_ver = csp->sp_hi_ver;
+ ln->ln_sparm.csp.sp_lo_ver = csp->sp_lo_ver;
+ ln->ln_sparm.csp.sp_bb_cred = csp->sp_bb_cred;
+ ln->ln_sparm.csp.sp_features = csp->sp_features;
+ ln->ln_sparm.csp.sp_bb_data = csp->sp_bb_data;
+ ln->ln_sparm.csp.sp_r_a_tov = csp->sp_r_a_tov;
+ ln->ln_sparm.csp.sp_e_d_tov = csp->sp_e_d_tov;
+
+ /* Copy word 0 & word 1 of class sparam */
+ clsp = (struct fc_els_cssp *)rsp->clsp_word_0_1;
+ ln->ln_sparm.clsp[2].cp_class = clsp->cp_class;
+ ln->ln_sparm.clsp[2].cp_init = clsp->cp_init;
+ ln->ln_sparm.clsp[2].cp_recip = clsp->cp_recip;
+ ln->ln_sparm.clsp[2].cp_rdfs = clsp->cp_rdfs;
+
+ spin_unlock_irq(&hw->lock);
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ /* Send an event to update local attribs */
+ csio_lnode_async_event(ln, CSIO_LN_FC_ATTRIB_UPDATE);
+}
+
+/*
+ * csio_ln_vnp_read - Read vnp params.
+ * @ln: lnode
+ * @cbfn: Completion handler.
+ *
+ * Issued with lock held.
+ */
+static int
+csio_ln_vnp_read(struct csio_lnode *ln,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_hw *hw = ln->hwp;
+ struct csio_mb *mbp;
+
+ /* Allocate Mbox request */
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Prepare VNP Command */
+ csio_fcoe_vnp_read_init_mb(ln, mbp,
+ CSIO_MB_DEFAULT_TMO,
+ ln->fcf_flowid,
+ ln->vnp_flowid,
+ cbfn);
+
+ /* Issue MBOX cmd */
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Failed to issue mbox FCoE VNP command\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_fcoe_enable_link - Enable fcoe link.
+ * @ln: lnode
+ * @enable: enable/disable
+ * Issued with lock held.
+ * Issues mbox cmd to bring up FCOE link on port associated with given ln.
+ */
+static int
+csio_fcoe_enable_link(struct csio_lnode *ln, bool enable)
+{
+ struct csio_hw *hw = ln->hwp;
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ uint8_t portid;
+ uint8_t sub_op;
+ struct fw_fcoe_link_cmd *lcmd;
+ int i;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ portid = ln->portid;
+ sub_op = enable ? FCOE_LINK_UP : FCOE_LINK_DOWN;
+
+ csio_dbg(hw, "bringing FCOE LINK %s on Port:%d\n",
+ sub_op ? "UP" : "DOWN", portid);
+
+ csio_write_fcoe_link_cond_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ portid, sub_op, 0, 0, 0, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FCOE LINK cmd on port[%d]\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw,
+ "FCOE LINK %s cmd on port[%d] failed with "
+ "ret:x%x\n", sub_op ? "UP" : "DOWN", portid, retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ if (!enable)
+ goto out;
+
+ lcmd = (struct fw_fcoe_link_cmd *)mbp->mb;
+
+ memcpy(csio_ln_wwnn(ln), lcmd->vnport_wwnn, 8);
+ memcpy(csio_ln_wwpn(ln), lcmd->vnport_wwpn, 8);
+
+ for (i = 0; i < CSIO_MAX_PPORTS; i++)
+ if (hw->pport[i].portid == portid)
+ memcpy(hw->pport[i].mac, lcmd->phy_mac, 6);
+
+out:
+ mempool_free(mbp, hw->mb_mempool);
+ return 0;
+}
+
+/*
+ * csio_ln_read_fcf_cbfn - Read fcf parameters
+ * @ln: lnode
+ *
+ * read fcf response and Update ln fcf information.
+ */
+static void
+csio_ln_read_fcf_cbfn(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ struct csio_lnode *ln = (struct csio_lnode *)mbp->priv;
+ struct csio_fcf_info *fcf_info;
+ struct fw_fcoe_fcf_cmd *rsp =
+ (struct fw_fcoe_fcf_cmd *)(mbp->mb);
+ enum fw_retval retval;
+
+ retval = FW_CMD_RETVAL_G(ntohl(rsp->retval_len16));
+ if (retval != FW_SUCCESS) {
+ csio_ln_err(ln, "FCOE FCF cmd failed with ret x%x\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ fcf_info = ln->fcfinfo;
+ fcf_info->priority = FW_FCOE_FCF_CMD_PRIORITY_GET(
+ ntohs(rsp->priority_pkd));
+ fcf_info->vf_id = ntohs(rsp->vf_id);
+ fcf_info->vlan_id = rsp->vlan_id;
+ fcf_info->max_fcoe_size = ntohs(rsp->max_fcoe_size);
+ fcf_info->fka_adv = be32_to_cpu(rsp->fka_adv);
+ fcf_info->fcfi = FW_FCOE_FCF_CMD_FCFI_GET(ntohl(rsp->op_to_fcfi));
+ fcf_info->fpma = FW_FCOE_FCF_CMD_FPMA_GET(rsp->fpma_to_portid);
+ fcf_info->spma = FW_FCOE_FCF_CMD_SPMA_GET(rsp->fpma_to_portid);
+ fcf_info->login = FW_FCOE_FCF_CMD_LOGIN_GET(rsp->fpma_to_portid);
+ fcf_info->portid = FW_FCOE_FCF_CMD_PORTID_GET(rsp->fpma_to_portid);
+ memcpy(fcf_info->fc_map, rsp->fc_map, sizeof(fcf_info->fc_map));
+ memcpy(fcf_info->mac, rsp->mac, sizeof(fcf_info->mac));
+ memcpy(fcf_info->name_id, rsp->name_id, sizeof(fcf_info->name_id));
+ memcpy(fcf_info->fabric, rsp->fabric, sizeof(fcf_info->fabric));
+ memcpy(fcf_info->spma_mac, rsp->spma_mac, sizeof(fcf_info->spma_mac));
+
+ spin_unlock_irq(&hw->lock);
+
+ mempool_free(mbp, hw->mb_mempool);
+}
+
+/*
+ * csio_ln_read_fcf_entry - Read fcf entry.
+ * @ln: lnode
+ * @cbfn: Completion handler.
+ *
+ * Issued with lock held.
+ */
+static int
+csio_ln_read_fcf_entry(struct csio_lnode *ln,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_hw *hw = ln->hwp;
+ struct csio_mb *mbp;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get FCoE FCF information */
+ csio_fcoe_read_fcf_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ ln->portid, ln->fcf_flowid, cbfn);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FCOE FCF cmd\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_handle_link_up - Logical Linkup event.
+ * @hw - HW module.
+ * @portid - Physical port number
+ * @fcfi - FCF index.
+ * @vnpi - VNP index.
+ * Returns - none.
+ *
+ * This event is received from FW, when virtual link is established between
+ * Physical port[ENode] and FCF. If its new vnpi, then local node object is
+ * created on this FCF and set to [ONLINE] state.
+ * Lnode waits for FW_RDEV_CMD event to be received indicating that
+ * Fabric login is completed and lnode moves to [READY] state.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_handle_link_up(struct csio_hw *hw, uint8_t portid, uint32_t fcfi,
+ uint32_t vnpi)
+{
+ struct csio_lnode *ln = NULL;
+
+ /* Lookup lnode based on vnpi */
+ ln = csio_ln_lookup_by_vnpi(hw, vnpi);
+ if (!ln) {
+ /* Pick lnode based on portid */
+ ln = csio_ln_lookup_by_portid(hw, portid);
+ if (!ln) {
+ csio_err(hw, "failed to lookup fcoe lnode on port:%d\n",
+ portid);
+ CSIO_DB_ASSERT(0);
+ return;
+ }
+
+ /* Check if lnode has valid vnp flowid */
+ if (ln->vnp_flowid != CSIO_INVALID_IDX) {
+ /* New VN-Port */
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_alloc(hw);
+ spin_lock_irq(&hw->lock);
+ if (!ln) {
+ csio_err(hw,
+ "failed to allocate fcoe lnode"
+ "for port:%d vnpi:x%x\n",
+ portid, vnpi);
+ CSIO_DB_ASSERT(0);
+ return;
+ }
+ ln->portid = portid;
+ }
+ ln->vnp_flowid = vnpi;
+ ln->dev_num &= ~0xFFFF;
+ ln->dev_num |= vnpi;
+ }
+
+ /*Initialize fcfi */
+ ln->fcf_flowid = fcfi;
+
+ csio_info(hw, "Port:%d - FCOE LINK UP\n", portid);
+
+ CSIO_INC_STATS(ln, n_link_up);
+
+ /* Send LINKUP event to SM */
+ csio_post_event(&ln->sm, CSIO_LNE_LINKUP);
+}
+
+/*
+ * csio_post_event_rns
+ * @ln - FCOE lnode
+ * @evt - Given rnode event
+ * Returns - none
+ *
+ * Posts given rnode event to all FCOE rnodes connected with given Lnode.
+ * This routine is invoked when lnode receives LINK_DOWN/DOWN_LINK/CLOSE
+ * event.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_post_event_rns(struct csio_lnode *ln, enum csio_rn_ev evt)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp, *next;
+ struct csio_rnode *rn;
+
+ list_for_each_safe(tmp, next, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ csio_post_event(&rn->sm, evt);
+ }
+}
+
+/*
+ * csio_cleanup_rns
+ * @ln - FCOE lnode
+ * Returns - none
+ *
+ * Frees all FCOE rnodes connected with given Lnode.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_cleanup_rns(struct csio_lnode *ln)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp, *next_rn;
+ struct csio_rnode *rn;
+
+ list_for_each_safe(tmp, next_rn, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ csio_put_rnode(ln, rn);
+ }
+
+}
+
+/*
+ * csio_post_event_lns
+ * @ln - FCOE lnode
+ * @evt - Given lnode event
+ * Returns - none
+ *
+ * Posts given lnode event to all FCOE lnodes connected with given Lnode.
+ * This routine is invoked when lnode receives LINK_DOWN/DOWN_LINK/CLOSE
+ * event.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_post_event_lns(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct list_head *tmp;
+ struct csio_lnode *cln, *sln;
+
+ /* If NPIV lnode, send evt only to that and return */
+ if (csio_is_npiv_ln(ln)) {
+ csio_post_event(&ln->sm, evt);
+ return;
+ }
+
+ sln = ln;
+ /* Traverse children lnodes list and send evt */
+ list_for_each(tmp, &sln->cln_head) {
+ cln = (struct csio_lnode *) tmp;
+ csio_post_event(&cln->sm, evt);
+ }
+
+ /* Send evt to parent lnode */
+ csio_post_event(&ln->sm, evt);
+}
+
+/*
+ * csio_ln_down - Lcoal nport is down
+ * @ln - FCOE Lnode
+ * Returns - none
+ *
+ * Sends LINK_DOWN events to Lnode and its associated NPIVs lnodes.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_ln_down(struct csio_lnode *ln)
+{
+ csio_post_event_lns(ln, CSIO_LNE_LINK_DOWN);
+}
+
+/*
+ * csio_handle_link_down - Logical Linkdown event.
+ * @hw - HW module.
+ * @portid - Physical port number
+ * @fcfi - FCF index.
+ * @vnpi - VNP index.
+ * Returns - none
+ *
+ * This event is received from FW, when virtual link goes down between
+ * Physical port[ENode] and FCF. Lnode and its associated NPIVs lnode hosted on
+ * this vnpi[VN-Port] will be de-instantiated.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_handle_link_down(struct csio_hw *hw, uint8_t portid, uint32_t fcfi,
+ uint32_t vnpi)
+{
+ struct csio_fcf_info *fp;
+ struct csio_lnode *ln;
+
+ /* Lookup lnode based on vnpi */
+ ln = csio_ln_lookup_by_vnpi(hw, vnpi);
+ if (ln) {
+ fp = ln->fcfinfo;
+ CSIO_INC_STATS(ln, n_link_down);
+
+ /*Warn if linkdown received if lnode is not in ready state */
+ if (!csio_is_lnode_ready(ln)) {
+ csio_ln_warn(ln,
+ "warn: FCOE link is already in offline "
+ "Ignoring Fcoe linkdown event on portid %d\n",
+ portid);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ return;
+ }
+
+ /* Verify portid */
+ if (fp->portid != portid) {
+ csio_ln_warn(ln,
+ "warn: FCOE linkdown recv with "
+ "invalid port %d\n", portid);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ return;
+ }
+
+ /* verify fcfi */
+ if (ln->fcf_flowid != fcfi) {
+ csio_ln_warn(ln,
+ "warn: FCOE linkdown recv with "
+ "invalid fcfi x%x\n", fcfi);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ return;
+ }
+
+ csio_info(hw, "Port:%d - FCOE LINK DOWN\n", portid);
+
+ /* Send LINK_DOWN event to lnode s/m */
+ csio_ln_down(ln);
+
+ return;
+ } else {
+ csio_warn(hw,
+ "warn: FCOE linkdown recv with invalid vnpi x%x\n",
+ vnpi);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ }
+}
+
+/*
+ * csio_is_lnode_ready - Checks FCOE lnode is in ready state.
+ * @ln: Lnode module
+ *
+ * Returns True if FCOE lnode is in ready state.
+ */
+int
+csio_is_lnode_ready(struct csio_lnode *ln)
+{
+ return (csio_get_state(ln) == csio_lns_ready);
+}
+
+/*****************************************************************************/
+/* START: Lnode SM */
+/*****************************************************************************/
+/*
+ * csio_lns_uninit - The request in uninit state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "uninit" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_uninit(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_lnode *rln = hw->rln;
+ int rv;
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_LINKUP:
+ csio_set_state(&ln->sm, csio_lns_online);
+ /* Read FCF only for physical lnode */
+ if (csio_is_phys_ln(ln)) {
+ rv = csio_ln_read_fcf_entry(ln,
+ csio_ln_read_fcf_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ break;
+ }
+
+ /* Add FCF record */
+ list_add_tail(&ln->fcfinfo->list, &rln->fcf_lsthead);
+ }
+
+ rv = csio_ln_vnp_read(ln, csio_ln_vnp_read_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ }
+ break;
+
+ case CSIO_LNE_DOWN_LINK:
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[uninit].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+ break;
+ } /* switch event */
+}
+
+/*
+ * csio_lns_online - The request in online state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "online" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_online(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_LINKUP:
+ csio_ln_warn(ln,
+ "warn: FCOE link is up already "
+ "Ignoring linkup on port:%d\n", ln->portid);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ break;
+
+ case CSIO_LNE_FAB_INIT_DONE:
+ csio_set_state(&ln->sm, csio_lns_ready);
+
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_async_event(ln, CSIO_LN_FC_LINKUP);
+ spin_lock_irq(&hw->lock);
+
+ break;
+
+ case CSIO_LNE_LINK_DOWN:
+ case CSIO_LNE_DOWN_LINK:
+ csio_set_state(&ln->sm, csio_lns_uninit);
+ if (csio_is_phys_ln(ln)) {
+ /* Remove FCF entry */
+ list_del_init(&ln->fcfinfo->list);
+ }
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[uninit].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+
+ break;
+ } /* switch event */
+}
+
+/*
+ * csio_lns_ready - The request in ready state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "ready" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_ready(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_FAB_INIT_DONE:
+ csio_ln_dbg(ln,
+ "ignoring event %d recv from did x%x"
+ "in ln state[ready].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ break;
+
+ case CSIO_LNE_LINK_DOWN:
+ csio_set_state(&ln->sm, csio_lns_offline);
+ csio_post_event_rns(ln, CSIO_RNFE_DOWN);
+
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_async_event(ln, CSIO_LN_FC_LINKDOWN);
+ spin_lock_irq(&hw->lock);
+
+ if (csio_is_phys_ln(ln)) {
+ /* Remove FCF entry */
+ list_del_init(&ln->fcfinfo->list);
+ }
+ break;
+
+ case CSIO_LNE_DOWN_LINK:
+ csio_set_state(&ln->sm, csio_lns_offline);
+ csio_post_event_rns(ln, CSIO_RNFE_DOWN);
+
+ /* Host need to issue aborts in case if FW has not returned
+ * WRs with status "ABORTED"
+ */
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_async_event(ln, CSIO_LN_FC_LINKDOWN);
+ spin_lock_irq(&hw->lock);
+
+ if (csio_is_phys_ln(ln)) {
+ /* Remove FCF entry */
+ list_del_init(&ln->fcfinfo->list);
+ }
+ break;
+
+ case CSIO_LNE_CLOSE:
+ csio_set_state(&ln->sm, csio_lns_uninit);
+ csio_post_event_rns(ln, CSIO_RNFE_CLOSE);
+ break;
+
+ case CSIO_LNE_LOGO:
+ csio_set_state(&ln->sm, csio_lns_offline);
+ csio_post_event_rns(ln, CSIO_RNFE_DOWN);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[uninit].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+ CSIO_DB_ASSERT(0);
+ break;
+ } /* switch event */
+}
+
+/*
+ * csio_lns_offline - The request in offline state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "offline" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_offline(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_lnode *rln = hw->rln;
+ int rv;
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_LINKUP:
+ csio_set_state(&ln->sm, csio_lns_online);
+ /* Read FCF only for physical lnode */
+ if (csio_is_phys_ln(ln)) {
+ rv = csio_ln_read_fcf_entry(ln,
+ csio_ln_read_fcf_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ break;
+ }
+
+ /* Add FCF record */
+ list_add_tail(&ln->fcfinfo->list, &rln->fcf_lsthead);
+ }
+
+ rv = csio_ln_vnp_read(ln, csio_ln_vnp_read_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ }
+ break;
+
+ case CSIO_LNE_LINK_DOWN:
+ case CSIO_LNE_DOWN_LINK:
+ case CSIO_LNE_LOGO:
+ csio_ln_dbg(ln,
+ "ignoring event %d recv from did x%x"
+ "in ln state[offline].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ break;
+
+ case CSIO_LNE_CLOSE:
+ csio_set_state(&ln->sm, csio_lns_uninit);
+ csio_post_event_rns(ln, CSIO_RNFE_CLOSE);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[offline]\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+ CSIO_DB_ASSERT(0);
+ break;
+ } /* switch event */
+}
+
+/*****************************************************************************/
+/* END: Lnode SM */
+/*****************************************************************************/
+
+static void
+csio_free_fcfinfo(struct kref *kref)
+{
+ struct csio_fcf_info *fcfinfo = container_of(kref,
+ struct csio_fcf_info, kref);
+ kfree(fcfinfo);
+}
+
+/* Helper routines for attributes */
+/*
+ * csio_lnode_state_to_str - Get current state of FCOE lnode.
+ * @ln - lnode
+ * @str - state of lnode.
+ *
+ */
+void
+csio_lnode_state_to_str(struct csio_lnode *ln, int8_t *str)
+{
+ if (csio_get_state(ln) == csio_lns_uninit) {
+ strcpy(str, "UNINIT");
+ return;
+ }
+ if (csio_get_state(ln) == csio_lns_ready) {
+ strcpy(str, "READY");
+ return;
+ }
+ if (csio_get_state(ln) == csio_lns_offline) {
+ strcpy(str, "OFFLINE");
+ return;
+ }
+ strcpy(str, "UNKNOWN");
+} /* csio_lnode_state_to_str */
+
+
+int
+csio_get_phy_port_stats(struct csio_hw *hw, uint8_t portid,
+ struct fw_fcoe_port_stats *port_stats)
+{
+ struct csio_mb *mbp;
+ struct fw_fcoe_port_cmd_params portparams;
+ enum fw_retval retval;
+ int idx;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ csio_err(hw, "FCoE FCF PARAMS command out of memory!\n");
+ return -EINVAL;
+ }
+ portparams.portid = portid;
+
+ for (idx = 1; idx <= 3; idx++) {
+ portparams.idx = (idx-1)*6 + 1;
+ portparams.nstats = 6;
+ if (idx == 3)
+ portparams.nstats = 4;
+ csio_fcoe_read_portparams_init_mb(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ &portparams, NULL);
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FCoE port params failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+ csio_mb_process_portparams_rsp(hw, mbp, &retval,
+ &portparams, port_stats);
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+ return 0;
+}
+
+/*
+ * csio_ln_mgmt_wr_handler -Mgmt Work Request handler.
+ * @wr - WR.
+ * @len - WR len.
+ * This handler is invoked when an outstanding mgmt WR is completed.
+ * Its invoked in the context of FW event worker thread for every
+ * mgmt event received.
+ * Return - none.
+ */
+
+static void
+csio_ln_mgmt_wr_handler(struct csio_hw *hw, void *wr, uint32_t len)
+{
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ struct csio_ioreq *io_req = NULL;
+ struct fw_fcoe_els_ct_wr *wr_cmd;
+
+
+ wr_cmd = (struct fw_fcoe_els_ct_wr *) wr;
+
+ if (len < sizeof(struct fw_fcoe_els_ct_wr)) {
+ csio_err(mgmtm->hw,
+ "Invalid ELS CT WR length recvd, len:%x\n", len);
+ mgmtm->stats.n_err++;
+ return;
+ }
+
+ io_req = (struct csio_ioreq *) ((uintptr_t) wr_cmd->cookie);
+ io_req->wr_status = csio_wr_status(wr_cmd);
+
+ /* lookup ioreq exists in our active Q */
+ spin_lock_irq(&hw->lock);
+ if (csio_mgmt_req_lookup(mgmtm, io_req) != 0) {
+ csio_err(mgmtm->hw,
+ "Error- Invalid IO handle recv in WR. handle: %p\n",
+ io_req);
+ mgmtm->stats.n_err++;
+ spin_unlock_irq(&hw->lock);
+ return;
+ }
+
+ mgmtm = csio_hw_to_mgmtm(hw);
+
+ /* Dequeue from active queue */
+ list_del_init(&io_req->sm.sm_list);
+ mgmtm->stats.n_active--;
+ spin_unlock_irq(&hw->lock);
+
+ /* io_req will be freed by completion handler */
+ if (io_req->io_cbfn)
+ io_req->io_cbfn(hw, io_req);
+}
+
+/**
+ * csio_fcoe_fwevt_handler - Event handler for Firmware FCoE events.
+ * @hw: HW module
+ * @cpl_op: CPL opcode
+ * @cmd: FW cmd/WR.
+ *
+ * Process received FCoE cmd/WR event from FW.
+ */
+void
+csio_fcoe_fwevt_handler(struct csio_hw *hw, __u8 cpl_op, __be64 *cmd)
+{
+ struct csio_lnode *ln;
+ struct csio_rnode *rn;
+ uint8_t portid, opcode = *(uint8_t *)cmd;
+ struct fw_fcoe_link_cmd *lcmd;
+ struct fw_wr_hdr *wr;
+ struct fw_rdev_wr *rdev_wr;
+ enum fw_fcoe_link_status lstatus;
+ uint32_t fcfi, rdev_flowid, vnpi;
+ enum csio_ln_ev evt;
+
+ if (cpl_op == CPL_FW6_MSG && opcode == FW_FCOE_LINK_CMD) {
+
+ lcmd = (struct fw_fcoe_link_cmd *)cmd;
+ lstatus = lcmd->lstatus;
+ portid = FW_FCOE_LINK_CMD_PORTID_GET(
+ ntohl(lcmd->op_to_portid));
+ fcfi = FW_FCOE_LINK_CMD_FCFI_GET(ntohl(lcmd->sub_opcode_fcfi));
+ vnpi = FW_FCOE_LINK_CMD_VNPI_GET(ntohl(lcmd->vnpi_pkd));
+
+ if (lstatus == FCOE_LINKUP) {
+
+ /* HW lock here */
+ spin_lock_irq(&hw->lock);
+ csio_handle_link_up(hw, portid, fcfi, vnpi);
+ spin_unlock_irq(&hw->lock);
+ /* HW un lock here */
+
+ } else if (lstatus == FCOE_LINKDOWN) {
+
+ /* HW lock here */
+ spin_lock_irq(&hw->lock);
+ csio_handle_link_down(hw, portid, fcfi, vnpi);
+ spin_unlock_irq(&hw->lock);
+ /* HW un lock here */
+ } else {
+ csio_warn(hw, "Unexpected FCOE LINK status:0x%x\n",
+ lcmd->lstatus);
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+ } else if (cpl_op == CPL_FW6_PLD) {
+ wr = (struct fw_wr_hdr *) (cmd + 4);
+ if (FW_WR_OP_G(be32_to_cpu(wr->hi))
+ == FW_RDEV_WR) {
+
+ rdev_wr = (struct fw_rdev_wr *) (cmd + 4);
+
+ rdev_flowid = FW_RDEV_WR_FLOWID_GET(
+ ntohl(rdev_wr->alloc_to_len16));
+ vnpi = FW_RDEV_WR_ASSOC_FLOWID_GET(
+ ntohl(rdev_wr->flags_to_assoc_flowid));
+
+ csio_dbg(hw,
+ "FW_RDEV_WR: flowid:x%x ev_cause:x%x "
+ "vnpi:0x%x\n", rdev_flowid,
+ rdev_wr->event_cause, vnpi);
+
+ if (rdev_wr->protocol != PROT_FCOE) {
+ csio_err(hw,
+ "FW_RDEV_WR: invalid proto:x%x "
+ "received with flowid:x%x\n",
+ rdev_wr->protocol,
+ rdev_flowid);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ return;
+ }
+
+ /* HW lock here */
+ spin_lock_irq(&hw->lock);
+ ln = csio_ln_lookup_by_vnpi(hw, vnpi);
+ if (!ln) {
+ csio_err(hw,
+ "FW_DEV_WR: invalid vnpi:x%x received "
+ "with flowid:x%x\n", vnpi, rdev_flowid);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto out_pld;
+ }
+
+ rn = csio_confirm_rnode(ln, rdev_flowid,
+ &rdev_wr->u.fcoe_rdev);
+ if (!rn) {
+ csio_ln_dbg(ln,
+ "Failed to confirm rnode "
+ "for flowid:x%x\n", rdev_flowid);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto out_pld;
+ }
+
+ /* save previous event for debugging */
+ ln->prev_evt = ln->cur_evt;
+ ln->cur_evt = rdev_wr->event_cause;
+ CSIO_INC_STATS(ln, n_evt_fw[rdev_wr->event_cause]);
+
+ /* Translate all the fabric events to lnode SM events */
+ evt = CSIO_FWE_TO_LNE(rdev_wr->event_cause);
+ if (evt) {
+ csio_ln_dbg(ln,
+ "Posting event to lnode event:%d "
+ "cause:%d flowid:x%x\n", evt,
+ rdev_wr->event_cause, rdev_flowid);
+ csio_post_event(&ln->sm, evt);
+ }
+
+ /* Handover event to rn SM here. */
+ csio_rnode_fwevt_handler(rn, rdev_wr->event_cause);
+out_pld:
+ spin_unlock_irq(&hw->lock);
+ return;
+ } else {
+ csio_warn(hw, "unexpected WR op(0x%x) recv\n",
+ FW_WR_OP_G(be32_to_cpu((wr->hi))));
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+ } else if (cpl_op == CPL_FW6_MSG) {
+ wr = (struct fw_wr_hdr *) (cmd);
+ if (FW_WR_OP_G(be32_to_cpu(wr->hi)) == FW_FCOE_ELS_CT_WR) {
+ csio_ln_mgmt_wr_handler(hw, wr,
+ sizeof(struct fw_fcoe_els_ct_wr));
+ } else {
+ csio_warn(hw, "unexpected WR op(0x%x) recv\n",
+ FW_WR_OP_G(be32_to_cpu((wr->hi))));
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+ } else {
+ csio_warn(hw, "unexpected CPL op(0x%x) recv\n", opcode);
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+}
+
+/**
+ * csio_lnode_start - Kickstart lnode discovery.
+ * @ln: lnode
+ *
+ * This routine kickstarts the discovery by issuing an FCOE_LINK (up) command.
+ */
+int
+csio_lnode_start(struct csio_lnode *ln)
+{
+ int rv = 0;
+ if (csio_is_phys_ln(ln) && !(ln->flags & CSIO_LNF_LINK_ENABLE)) {
+ rv = csio_fcoe_enable_link(ln, 1);
+ ln->flags |= CSIO_LNF_LINK_ENABLE;
+ }
+
+ return rv;
+}
+
+/**
+ * csio_lnode_stop - Stop the lnode.
+ * @ln: lnode
+ *
+ * This routine is invoked by HW module to stop lnode and its associated NPIV
+ * lnodes.
+ */
+void
+csio_lnode_stop(struct csio_lnode *ln)
+{
+ csio_post_event_lns(ln, CSIO_LNE_DOWN_LINK);
+ if (csio_is_phys_ln(ln) && (ln->flags & CSIO_LNF_LINK_ENABLE)) {
+ csio_fcoe_enable_link(ln, 0);
+ ln->flags &= ~CSIO_LNF_LINK_ENABLE;
+ }
+ csio_ln_dbg(ln, "stopping ln :%p\n", ln);
+}
+
+/**
+ * csio_lnode_close - Close an lnode.
+ * @ln: lnode
+ *
+ * This routine is invoked by HW module to close an lnode and its
+ * associated NPIV lnodes. Lnode and its associated NPIV lnodes are
+ * set to uninitialized state.
+ */
+void
+csio_lnode_close(struct csio_lnode *ln)
+{
+ csio_post_event_lns(ln, CSIO_LNE_CLOSE);
+ if (csio_is_phys_ln(ln))
+ ln->vnp_flowid = CSIO_INVALID_IDX;
+
+ csio_ln_dbg(ln, "closed ln :%p\n", ln);
+}
+
+/*
+ * csio_ln_prep_ecwr - Prepare ELS/CT WR.
+ * @io_req - IO request.
+ * @wr_len - WR len
+ * @immd_len - WR immediate data
+ * @sub_op - Sub opcode
+ * @sid - source portid.
+ * @did - destination portid
+ * @flow_id - flowid
+ * @fw_wr - ELS/CT WR to be prepared.
+ * Returns: 0 - on success
+ */
+static int
+csio_ln_prep_ecwr(struct csio_ioreq *io_req, uint32_t wr_len,
+ uint32_t immd_len, uint8_t sub_op, uint32_t sid,
+ uint32_t did, uint32_t flow_id, uint8_t *fw_wr)
+{
+ struct fw_fcoe_els_ct_wr *wr;
+ __be32 port_id;
+
+ wr = (struct fw_fcoe_els_ct_wr *)fw_wr;
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_FCOE_ELS_CT_WR) |
+ FW_FCOE_ELS_CT_WR_IMMDLEN(immd_len));
+
+ wr_len = DIV_ROUND_UP(wr_len, 16);
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(flow_id) |
+ FW_WR_LEN16_V(wr_len));
+ wr->els_ct_type = sub_op;
+ wr->ctl_pri = 0;
+ wr->cp_en_class = 0;
+ wr->cookie = io_req->fw_handle;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(
+ io_req->lnode->hwp, io_req->iq_idx));
+ wr->fl_to_sp = FW_FCOE_ELS_CT_WR_SP(1);
+ wr->tmo_val = (uint8_t) io_req->tmo;
+ port_id = htonl(sid);
+ memcpy(wr->l_id, PORT_ID_PTR(port_id), 3);
+ port_id = htonl(did);
+ memcpy(wr->r_id, PORT_ID_PTR(port_id), 3);
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(io_req->dma_buf.len);
+ wr->rsp_dmaaddr = cpu_to_be64(io_req->dma_buf.paddr);
+ return 0;
+}
+
+/*
+ * csio_ln_mgmt_submit_wr - Post elsct work request.
+ * @mgmtm - mgmtm
+ * @io_req - io request.
+ * @sub_op - ELS or CT request type
+ * @pld - Dma Payload buffer
+ * @pld_len - Payload len
+ * Prepares ELSCT Work request and sents it to FW.
+ * Returns: 0 - on success
+ */
+static int
+csio_ln_mgmt_submit_wr(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req,
+ uint8_t sub_op, struct csio_dma_buf *pld,
+ uint32_t pld_len)
+{
+ struct csio_wr_pair wrp;
+ struct csio_lnode *ln = io_req->lnode;
+ struct csio_rnode *rn = io_req->rnode;
+ struct csio_hw *hw = mgmtm->hw;
+ uint8_t fw_wr[64];
+ struct ulptx_sgl dsgl;
+ uint32_t wr_size = 0;
+ uint8_t im_len = 0;
+ uint32_t wr_off = 0;
+
+ int ret = 0;
+
+ /* Calculate WR Size for this ELS REQ */
+ wr_size = sizeof(struct fw_fcoe_els_ct_wr);
+
+ /* Send as immediate data if pld < 256 */
+ if (pld_len < 256) {
+ wr_size += ALIGN(pld_len, 8);
+ im_len = (uint8_t)pld_len;
+ } else
+ wr_size += sizeof(struct ulptx_sgl);
+
+ /* Roundup WR size in units of 16 bytes */
+ wr_size = ALIGN(wr_size, 16);
+
+ /* Get WR to send ELS REQ */
+ ret = csio_wr_get(hw, mgmtm->eq_idx, wr_size, &wrp);
+ if (ret != 0) {
+ csio_err(hw, "Failed to get WR for ec_req %p ret:%d\n",
+ io_req, ret);
+ return ret;
+ }
+
+ /* Prepare Generic WR used by all ELS/CT cmd */
+ csio_ln_prep_ecwr(io_req, wr_size, im_len, sub_op,
+ ln->nport_id, rn->nport_id,
+ csio_rn_flowid(rn),
+ &fw_wr[0]);
+
+ /* Copy ELS/CT WR CMD */
+ csio_wr_copy_to_wrp(&fw_wr[0], &wrp, wr_off,
+ sizeof(struct fw_fcoe_els_ct_wr));
+ wr_off += sizeof(struct fw_fcoe_els_ct_wr);
+
+ /* Copy payload to Immediate section of WR */
+ if (im_len)
+ csio_wr_copy_to_wrp(pld->vaddr, &wrp, wr_off, im_len);
+ else {
+ /* Program DSGL to dma payload */
+ dsgl.cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) |
+ ULPTX_MORE_F | ULPTX_NSGE_V(1));
+ dsgl.len0 = cpu_to_be32(pld_len);
+ dsgl.addr0 = cpu_to_be64(pld->paddr);
+ csio_wr_copy_to_wrp(&dsgl, &wrp, ALIGN(wr_off, 8),
+ sizeof(struct ulptx_sgl));
+ }
+
+ /* Issue work request to xmit ELS/CT req to FW */
+ csio_wr_issue(mgmtm->hw, mgmtm->eq_idx, false);
+ return ret;
+}
+
+/*
+ * csio_ln_mgmt_submit_req - Submit FCOE Mgmt request.
+ * @io_req - IO Request
+ * @io_cbfn - Completion handler.
+ * @req_type - ELS or CT request type
+ * @pld - Dma Payload buffer
+ * @pld_len - Payload len
+ *
+ *
+ * This API used submit managment ELS/CT request.
+ * This called with hw lock held
+ * Returns: 0 - on success
+ * -ENOMEM - on error.
+ */
+static int
+csio_ln_mgmt_submit_req(struct csio_ioreq *io_req,
+ void (*io_cbfn) (struct csio_hw *, struct csio_ioreq *),
+ enum fcoe_cmn_type req_type, struct csio_dma_buf *pld,
+ uint32_t pld_len)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(io_req->lnode);
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ int rv;
+
+ BUG_ON(pld_len > pld->len);
+
+ io_req->io_cbfn = io_cbfn; /* Upper layer callback handler */
+ io_req->fw_handle = (uintptr_t) (io_req);
+ io_req->eq_idx = mgmtm->eq_idx;
+ io_req->iq_idx = mgmtm->iq_idx;
+
+ rv = csio_ln_mgmt_submit_wr(mgmtm, io_req, req_type, pld, pld_len);
+ if (rv == 0) {
+ list_add_tail(&io_req->sm.sm_list, &mgmtm->active_q);
+ mgmtm->stats.n_active++;
+ }
+ return rv;
+}
+
+/*
+ * csio_ln_fdmi_init - FDMI Init entry point.
+ * @ln: lnode
+ */
+static int
+csio_ln_fdmi_init(struct csio_lnode *ln)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_dma_buf *dma_buf;
+
+ /* Allocate MGMT request required for FDMI */
+ ln->mgmt_req = kzalloc(sizeof(struct csio_ioreq), GFP_KERNEL);
+ if (!ln->mgmt_req) {
+ csio_ln_err(ln, "Failed to alloc ioreq for FDMI\n");
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Allocate Dma buffers for FDMI response Payload */
+ dma_buf = &ln->mgmt_req->dma_buf;
+ dma_buf->len = 2048;
+ dma_buf->vaddr = dma_alloc_coherent(&hw->pdev->dev, dma_buf->len,
+ &dma_buf->paddr, GFP_KERNEL);
+ if (!dma_buf->vaddr) {
+ csio_err(hw, "Failed to alloc DMA buffer for FDMI!\n");
+ kfree(ln->mgmt_req);
+ ln->mgmt_req = NULL;
+ return -ENOMEM;
+ }
+
+ ln->flags |= CSIO_LNF_FDMI_ENABLE;
+ return 0;
+}
+
+/*
+ * csio_ln_fdmi_exit - FDMI exit entry point.
+ * @ln: lnode
+ */
+static int
+csio_ln_fdmi_exit(struct csio_lnode *ln)
+{
+ struct csio_dma_buf *dma_buf;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ if (!ln->mgmt_req)
+ return 0;
+
+ dma_buf = &ln->mgmt_req->dma_buf;
+ if (dma_buf->vaddr)
+ dma_free_coherent(&hw->pdev->dev, dma_buf->len, dma_buf->vaddr,
+ dma_buf->paddr);
+
+ kfree(ln->mgmt_req);
+ return 0;
+}
+
+int
+csio_scan_done(struct csio_lnode *ln, unsigned long ticks,
+ unsigned long time, unsigned long max_scan_ticks,
+ unsigned long delta_scan_ticks)
+{
+ int rv = 0;
+
+ if (time >= max_scan_ticks)
+ return 1;
+
+ if (!ln->tgt_scan_tick)
+ ln->tgt_scan_tick = ticks;
+
+ if (((ticks - ln->tgt_scan_tick) >= delta_scan_ticks)) {
+ if (!ln->last_scan_ntgts)
+ ln->last_scan_ntgts = ln->n_scsi_tgts;
+ else {
+ if (ln->last_scan_ntgts == ln->n_scsi_tgts)
+ return 1;
+
+ ln->last_scan_ntgts = ln->n_scsi_tgts;
+ }
+ ln->tgt_scan_tick = ticks;
+ }
+ return rv;
+}
+
+/*
+ * csio_notify_lnodes:
+ * @hw: HW module
+ * @note: Notification
+ *
+ * Called from the HW SM to fan out notifications to the
+ * Lnode SM. Since the HW SM is entered with lock held,
+ * there is no need to hold locks here.
+ *
+ */
+void
+csio_notify_lnodes(struct csio_hw *hw, enum csio_ln_notify note)
+{
+ struct list_head *tmp;
+ struct csio_lnode *ln;
+
+ csio_dbg(hw, "Notifying all nodes of event %d\n", note);
+
+ /* Traverse children lnodes list and send evt */
+ list_for_each(tmp, &hw->sln_head) {
+ ln = (struct csio_lnode *) tmp;
+
+ switch (note) {
+ case CSIO_LN_NOTIFY_HWREADY:
+ csio_lnode_start(ln);
+ break;
+
+ case CSIO_LN_NOTIFY_HWRESET:
+ case CSIO_LN_NOTIFY_HWREMOVE:
+ csio_lnode_close(ln);
+ break;
+
+ case CSIO_LN_NOTIFY_HWSTOP:
+ csio_lnode_stop(ln);
+ break;
+
+ default:
+ break;
+
+ }
+ }
+}
+
+/*
+ * csio_disable_lnodes:
+ * @hw: HW module
+ * @portid:port id
+ * @disable: disable/enable flag.
+ * If disable=1, disables all lnode hosted on given physical port.
+ * otherwise enables all the lnodes on given phsysical port.
+ * This routine need to called with hw lock held.
+ */
+void
+csio_disable_lnodes(struct csio_hw *hw, uint8_t portid, bool disable)
+{
+ struct list_head *tmp;
+ struct csio_lnode *ln;
+
+ csio_dbg(hw, "Notifying event to all nodes of port:%d\n", portid);
+
+ /* Traverse sibling lnodes list and send evt */
+ list_for_each(tmp, &hw->sln_head) {
+ ln = (struct csio_lnode *) tmp;
+ if (ln->portid != portid)
+ continue;
+
+ if (disable)
+ csio_lnode_stop(ln);
+ else
+ csio_lnode_start(ln);
+ }
+}
+
+/*
+ * csio_ln_init - Initialize an lnode.
+ * @ln: lnode
+ *
+ */
+static int
+csio_ln_init(struct csio_lnode *ln)
+{
+ int rv = -EINVAL;
+ struct csio_lnode *pln;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ csio_init_state(&ln->sm, csio_lns_uninit);
+ ln->vnp_flowid = CSIO_INVALID_IDX;
+ ln->fcf_flowid = CSIO_INVALID_IDX;
+
+ if (csio_is_root_ln(ln)) {
+
+ /* This is the lnode used during initialization */
+
+ ln->fcfinfo = kzalloc(sizeof(struct csio_fcf_info), GFP_KERNEL);
+ if (!ln->fcfinfo) {
+ csio_ln_err(ln, "Failed to alloc FCF record\n");
+ CSIO_INC_STATS(hw, n_err_nomem);
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&ln->fcf_lsthead);
+ kref_init(&ln->fcfinfo->kref);
+
+ if (csio_fdmi_enable && csio_ln_fdmi_init(ln))
+ goto err;
+
+ } else { /* Either a non-root physical or a virtual lnode */
+
+ /*
+ * THe rest is common for non-root physical and NPIV lnodes.
+ * Just get references to all other modules
+ */
+
+ if (csio_is_npiv_ln(ln)) {
+ /* NPIV */
+ pln = csio_parent_lnode(ln);
+ kref_get(&pln->fcfinfo->kref);
+ ln->fcfinfo = pln->fcfinfo;
+ } else {
+ /* Another non-root physical lnode (FCF) */
+ ln->fcfinfo = kzalloc(sizeof(struct csio_fcf_info),
+ GFP_KERNEL);
+ if (!ln->fcfinfo) {
+ csio_ln_err(ln, "Failed to alloc FCF info\n");
+ CSIO_INC_STATS(hw, n_err_nomem);
+ goto err;
+ }
+
+ kref_init(&ln->fcfinfo->kref);
+
+ if (csio_fdmi_enable && csio_ln_fdmi_init(ln))
+ goto err;
+ }
+
+ } /* if (!csio_is_root_ln(ln)) */
+
+ return 0;
+err:
+ return rv;
+}
+
+static void
+csio_ln_exit(struct csio_lnode *ln)
+{
+ struct csio_lnode *pln;
+
+ csio_cleanup_rns(ln);
+ if (csio_is_npiv_ln(ln)) {
+ pln = csio_parent_lnode(ln);
+ kref_put(&pln->fcfinfo->kref, csio_free_fcfinfo);
+ } else {
+ kref_put(&ln->fcfinfo->kref, csio_free_fcfinfo);
+ if (csio_fdmi_enable)
+ csio_ln_fdmi_exit(ln);
+ }
+ ln->fcfinfo = NULL;
+}
+
+/*
+ * csio_lnode_init - Initialize the members of an lnode.
+ * @ln: lnode
+ */
+int
+csio_lnode_init(struct csio_lnode *ln, struct csio_hw *hw,
+ struct csio_lnode *pln)
+{
+ int rv = -EINVAL;
+
+ /* Link this lnode to hw */
+ csio_lnode_to_hw(ln) = hw;
+
+ /* Link child to parent if child lnode */
+ if (pln)
+ ln->pln = pln;
+ else
+ ln->pln = NULL;
+
+ /* Initialize scsi_tgt and timers to zero */
+ ln->n_scsi_tgts = 0;
+ ln->last_scan_ntgts = 0;
+ ln->tgt_scan_tick = 0;
+
+ /* Initialize rnode list */
+ INIT_LIST_HEAD(&ln->rnhead);
+ INIT_LIST_HEAD(&ln->cln_head);
+
+ /* Initialize log level for debug */
+ ln->params.log_level = hw->params.log_level;
+
+ if (csio_ln_init(ln))
+ goto err;
+
+ /* Add lnode to list of sibling or children lnodes */
+ spin_lock_irq(&hw->lock);
+ list_add_tail(&ln->sm.sm_list, pln ? &pln->cln_head : &hw->sln_head);
+ if (pln)
+ pln->num_vports++;
+ spin_unlock_irq(&hw->lock);
+
+ hw->num_lns++;
+
+ return 0;
+err:
+ csio_lnode_to_hw(ln) = NULL;
+ return rv;
+}
+
+/**
+ * csio_lnode_exit - De-instantiate an lnode.
+ * @ln: lnode
+ *
+ */
+void
+csio_lnode_exit(struct csio_lnode *ln)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ csio_ln_exit(ln);
+
+ /* Remove this lnode from hw->sln_head */
+ spin_lock_irq(&hw->lock);
+
+ list_del_init(&ln->sm.sm_list);
+
+ /* If it is children lnode, decrement the
+ * counter in its parent lnode
+ */
+ if (ln->pln)
+ ln->pln->num_vports--;
+
+ /* Update root lnode pointer */
+ if (list_empty(&hw->sln_head))
+ hw->rln = NULL;
+ else
+ hw->rln = (struct csio_lnode *)csio_list_next(&hw->sln_head);
+
+ spin_unlock_irq(&hw->lock);
+
+ csio_lnode_to_hw(ln) = NULL;
+ hw->num_lns--;
+}
diff --git a/drivers/scsi/csiostor/csio_lnode.h b/drivers/scsi/csiostor/csio_lnode.h
new file mode 100644
index 000000000000..607698a0f063
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_lnode.h
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_LNODE_H__
+#define __CSIO_LNODE_H__
+
+#include <linux/kref.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <scsi/fc/fc_els.h>
+
+
+#include "csio_defs.h"
+#include "csio_hw.h"
+
+#define CSIO_FCOE_MAX_NPIV 128
+#define CSIO_FCOE_MAX_RNODES 2048
+
+/* FDMI port attribute unknown speed */
+#define CSIO_HBA_PORTSPEED_UNKNOWN 0x8000
+
+extern int csio_fcoe_rnodes;
+extern int csio_fdmi_enable;
+
+struct csio_fcf_info {
+ struct list_head list;
+ uint8_t priority;
+ uint8_t mac[6];
+ uint8_t name_id[8];
+ uint8_t fabric[8];
+ uint16_t vf_id;
+ uint8_t vlan_id;
+ uint16_t max_fcoe_size;
+ uint8_t fc_map[3];
+ uint32_t fka_adv;
+ uint32_t fcfi;
+ uint8_t get_next:1;
+ uint8_t link_aff:1;
+ uint8_t fpma:1;
+ uint8_t spma:1;
+ uint8_t login:1;
+ uint8_t portid;
+ uint8_t spma_mac[6];
+ struct kref kref;
+};
+
+/* Defines for flags */
+#define CSIO_LNF_FIPSUPP 0x00000001 /* Fip Supported */
+#define CSIO_LNF_NPIVSUPP 0x00000002 /* NPIV supported */
+#define CSIO_LNF_LINK_ENABLE 0x00000004 /* Link enabled */
+#define CSIO_LNF_FDMI_ENABLE 0x00000008 /* FDMI support */
+
+/* Transport events */
+enum csio_ln_fc_evt {
+ CSIO_LN_FC_LINKUP = 1,
+ CSIO_LN_FC_LINKDOWN,
+ CSIO_LN_FC_RSCN,
+ CSIO_LN_FC_ATTRIB_UPDATE,
+};
+
+/* Lnode stats */
+struct csio_lnode_stats {
+ uint32_t n_link_up; /* Link down */
+ uint32_t n_link_down; /* Link up */
+ uint32_t n_err; /* error */
+ uint32_t n_err_nomem; /* memory not available */
+ uint32_t n_inval_parm; /* Invalid parameters */
+ uint32_t n_evt_unexp; /* unexpected event */
+ uint32_t n_evt_drop; /* dropped event */
+ uint32_t n_rnode_match; /* matched rnode */
+ uint32_t n_dev_loss_tmo; /* Device loss timeout */
+ uint32_t n_fdmi_err; /* fdmi err */
+ uint32_t n_evt_fw[PROTO_ERR_IMPL_LOGO + 1]; /* fw events */
+ enum csio_ln_ev n_evt_sm[CSIO_LNE_MAX_EVENT]; /* State m/c events */
+ uint32_t n_rnode_alloc; /* rnode allocated */
+ uint32_t n_rnode_free; /* rnode freed */
+ uint32_t n_rnode_nomem; /* rnode alloc failure */
+ uint32_t n_input_requests; /* Input Requests */
+ uint32_t n_output_requests; /* Output Requests */
+ uint32_t n_control_requests; /* Control Requests */
+ uint32_t n_input_bytes; /* Input Bytes */
+ uint32_t n_output_bytes; /* Output Bytes */
+ uint32_t rsvd1;
+};
+
+/* Common Lnode params */
+struct csio_lnode_params {
+ uint32_t ra_tov;
+ uint32_t fcfi;
+ uint32_t log_level; /* Module level for debugging */
+};
+
+struct csio_service_parms {
+ struct fc_els_csp csp; /* Common service parms */
+ uint8_t wwpn[8]; /* WWPN */
+ uint8_t wwnn[8]; /* WWNN */
+ struct fc_els_cssp clsp[4]; /* Class service params */
+ uint8_t vvl[16]; /* Vendor version level */
+};
+
+/* Lnode */
+struct csio_lnode {
+ struct csio_sm sm; /* State machine + sibling
+ * lnode list.
+ */
+ struct csio_hw *hwp; /* Pointer to the HW module */
+ uint8_t portid; /* Port ID */
+ uint8_t rsvd1;
+ uint16_t rsvd2;
+ uint32_t dev_num; /* Device number */
+ uint32_t flags; /* Flags */
+ struct list_head fcf_lsthead; /* FCF entries */
+ struct csio_fcf_info *fcfinfo; /* FCF in use */
+ struct csio_ioreq *mgmt_req; /* MGMT request */
+
+ /* FCoE identifiers */
+ uint8_t mac[6];
+ uint32_t nport_id;
+ struct csio_service_parms ln_sparm; /* Service parms */
+
+ /* Firmware identifiers */
+ uint32_t fcf_flowid; /*fcf flowid */
+ uint32_t vnp_flowid;
+ uint16_t ssn_cnt; /* Registered Session */
+ uint8_t cur_evt; /* Current event */
+ uint8_t prev_evt; /* Previous event */
+
+ /* Children */
+ struct list_head cln_head; /* Head of the children lnode
+ * list.
+ */
+ uint32_t num_vports; /* Total NPIV/children LNodes*/
+ struct csio_lnode *pln; /* Parent lnode of child
+ * lnodes.
+ */
+ struct list_head cmpl_q; /* Pending I/Os on this lnode */
+
+ /* Remote node information */
+ struct list_head rnhead; /* Head of rnode list */
+ uint32_t num_reg_rnodes; /* Number of rnodes registered
+ * with the host.
+ */
+ uint32_t n_scsi_tgts; /* Number of scsi targets
+ * found
+ */
+ uint32_t last_scan_ntgts;/* Number of scsi targets
+ * found per last scan.
+ */
+ uint32_t tgt_scan_tick; /* timer started after
+ * new tgt found
+ */
+ /* FC transport data */
+ struct fc_vport *fc_vport;
+ struct fc_host_statistics fch_stats;
+
+ struct csio_lnode_stats stats; /* Common lnode stats */
+ struct csio_lnode_params params; /* Common lnode params */
+};
+
+#define csio_lnode_to_hw(ln) ((ln)->hwp)
+#define csio_root_lnode(ln) (csio_lnode_to_hw((ln))->rln)
+#define csio_parent_lnode(ln) ((ln)->pln)
+#define csio_ln_flowid(ln) ((ln)->vnp_flowid)
+#define csio_ln_wwpn(ln) ((ln)->ln_sparm.wwpn)
+#define csio_ln_wwnn(ln) ((ln)->ln_sparm.wwnn)
+
+#define csio_is_root_ln(ln) (((ln) == csio_root_lnode((ln))) ? 1 : 0)
+#define csio_is_phys_ln(ln) (((ln)->pln == NULL) ? 1 : 0)
+#define csio_is_npiv_ln(ln) (((ln)->pln != NULL) ? 1 : 0)
+
+
+#define csio_ln_dbg(_ln, _fmt, ...) \
+ csio_dbg(_ln->hwp, "%x:%x "_fmt, CSIO_DEVID_HI(_ln), \
+ CSIO_DEVID_LO(_ln), ##__VA_ARGS__);
+
+#define csio_ln_err(_ln, _fmt, ...) \
+ csio_err(_ln->hwp, "%x:%x "_fmt, CSIO_DEVID_HI(_ln), \
+ CSIO_DEVID_LO(_ln), ##__VA_ARGS__);
+
+#define csio_ln_warn(_ln, _fmt, ...) \
+ csio_warn(_ln->hwp, "%x:%x "_fmt, CSIO_DEVID_HI(_ln), \
+ CSIO_DEVID_LO(_ln), ##__VA_ARGS__);
+
+/* HW->Lnode notifications */
+enum csio_ln_notify {
+ CSIO_LN_NOTIFY_HWREADY = 1,
+ CSIO_LN_NOTIFY_HWSTOP,
+ CSIO_LN_NOTIFY_HWREMOVE,
+ CSIO_LN_NOTIFY_HWRESET,
+};
+
+void csio_fcoe_fwevt_handler(struct csio_hw *, __u8 cpl_op, __be64 *);
+int csio_is_lnode_ready(struct csio_lnode *);
+void csio_lnode_state_to_str(struct csio_lnode *ln, int8_t *str);
+struct csio_lnode *csio_lnode_lookup_by_wwpn(struct csio_hw *, uint8_t *);
+int csio_get_phy_port_stats(struct csio_hw *, uint8_t ,
+ struct fw_fcoe_port_stats *);
+int csio_scan_done(struct csio_lnode *, unsigned long, unsigned long,
+ unsigned long, unsigned long);
+void csio_notify_lnodes(struct csio_hw *, enum csio_ln_notify);
+void csio_disable_lnodes(struct csio_hw *, uint8_t, bool);
+void csio_lnode_async_event(struct csio_lnode *, enum csio_ln_fc_evt);
+int csio_ln_fdmi_start(struct csio_lnode *, void *);
+int csio_lnode_start(struct csio_lnode *);
+void csio_lnode_stop(struct csio_lnode *);
+void csio_lnode_close(struct csio_lnode *);
+int csio_lnode_init(struct csio_lnode *, struct csio_hw *,
+ struct csio_lnode *);
+void csio_lnode_exit(struct csio_lnode *);
+
+#endif /* ifndef __CSIO_LNODE_H__ */
diff --git a/drivers/scsi/csiostor/csio_mb.c b/drivers/scsi/csiostor/csio_mb.c
new file mode 100644
index 000000000000..c7b4c464f6b8
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_mb.c
@@ -0,0 +1,1690 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+#include "csio_mb.h"
+#include "csio_wr.h"
+
+#define csio_mb_is_host_owner(__owner) ((__owner) == CSIO_MBOWNER_PL)
+
+/* MB Command/Response Helpers */
+/*
+ * csio_mb_fw_retval - FW return value from a mailbox response.
+ * @mbp: Mailbox structure
+ *
+ */
+enum fw_retval
+csio_mb_fw_retval(struct csio_mb *mbp)
+{
+ struct fw_cmd_hdr *hdr;
+
+ hdr = (struct fw_cmd_hdr *)(mbp->mb);
+
+ return FW_CMD_RETVAL_G(ntohl(hdr->lo));
+}
+
+/*
+ * csio_mb_hello - FW HELLO command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @m_mbox: Master mailbox number, if any.
+ * @a_mbox: Mailbox number for asycn notifications.
+ * @master: Device mastership.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_hello(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ uint32_t m_mbox, uint32_t a_mbox, enum csio_dev_master master,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_hello_cmd *cmdp = (struct fw_hello_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP_V(FW_HELLO_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ cmdp->err_to_clearinit = htonl(
+ FW_HELLO_CMD_MASTERDIS_V(master == CSIO_MASTER_CANT) |
+ FW_HELLO_CMD_MASTERFORCE_V(master == CSIO_MASTER_MUST) |
+ FW_HELLO_CMD_MBMASTER_V(master == CSIO_MASTER_MUST ?
+ m_mbox : FW_HELLO_CMD_MBMASTER_M) |
+ FW_HELLO_CMD_MBASYNCNOT_V(a_mbox) |
+ FW_HELLO_CMD_STAGE_V(fw_hello_cmd_stage_os) |
+ FW_HELLO_CMD_CLEARINIT_F);
+
+}
+
+/*
+ * csio_mb_process_hello_rsp - FW HELLO response processing helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @retval: Mailbox return value from Firmware
+ * @state: State that the function is in.
+ * @mpfn: Master pfn
+ *
+ */
+void
+csio_mb_process_hello_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval, enum csio_dev_state *state,
+ uint8_t *mpfn)
+{
+ struct fw_hello_cmd *rsp = (struct fw_hello_cmd *)(mbp->mb);
+ uint32_t value;
+
+ *retval = FW_CMD_RETVAL_G(ntohl(rsp->retval_len16));
+
+ if (*retval == FW_SUCCESS) {
+ hw->fwrev = ntohl(rsp->fwrev);
+
+ value = ntohl(rsp->err_to_clearinit);
+ *mpfn = FW_HELLO_CMD_MBMASTER_G(value);
+
+ if (value & FW_HELLO_CMD_INIT_F)
+ *state = CSIO_DEV_STATE_INIT;
+ else if (value & FW_HELLO_CMD_ERR_F)
+ *state = CSIO_DEV_STATE_ERR;
+ else
+ *state = CSIO_DEV_STATE_UNINIT;
+ }
+}
+
+/*
+ * csio_mb_bye - FW BYE command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_bye(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_bye_cmd *cmdp = (struct fw_bye_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP_V(FW_BYE_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+}
+
+/*
+ * csio_mb_reset - FW RESET command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @reset: Type of reset.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_reset(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ int reset, int halt,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_reset_cmd *cmdp = (struct fw_reset_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP_V(FW_RESET_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ cmdp->val = htonl(reset);
+ cmdp->halt_pkd = htonl(halt);
+
+}
+
+/*
+ * csio_mb_params - FW PARAMS command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: Command timeout.
+ * @pf: PF number.
+ * @vf: VF number.
+ * @nparams: Number of parameters
+ * @params: Parameter mnemonic array.
+ * @val: Parameter value array.
+ * @wr: Write/Read PARAMS.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_params(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ unsigned int pf, unsigned int vf, unsigned int nparams,
+ const u32 *params, u32 *val, bool wr,
+ void (*cbfn)(struct csio_hw *, struct csio_mb *))
+{
+ uint32_t i;
+ uint32_t temp_params = 0, temp_val = 0;
+ struct fw_params_cmd *cmdp = (struct fw_params_cmd *)(mbp->mb);
+ __be32 *p = &cmdp->param[0].mnem;
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP_V(FW_PARAMS_CMD) |
+ FW_CMD_REQUEST_F |
+ (wr ? FW_CMD_WRITE_F : FW_CMD_READ_F) |
+ FW_PARAMS_CMD_PFN_V(pf) |
+ FW_PARAMS_CMD_VFN_V(vf));
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+ /* Write Params */
+ if (wr) {
+ while (nparams--) {
+ temp_params = *params++;
+ temp_val = *val++;
+
+ *p++ = htonl(temp_params);
+ *p++ = htonl(temp_val);
+ }
+ } else {
+ for (i = 0; i < nparams; i++, p += 2) {
+ temp_params = *params++;
+ *p = htonl(temp_params);
+ }
+ }
+
+}
+
+/*
+ * csio_mb_process_read_params_rsp - FW PARAMS response processing helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @retval: Mailbox return value from Firmware
+ * @nparams: Number of parameters
+ * @val: Parameter value array.
+ *
+ */
+void
+csio_mb_process_read_params_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval, unsigned int nparams,
+ u32 *val)
+{
+ struct fw_params_cmd *rsp = (struct fw_params_cmd *)(mbp->mb);
+ uint32_t i;
+ __be32 *p = &rsp->param[0].val;
+
+ *retval = FW_CMD_RETVAL_G(ntohl(rsp->retval_len16));
+
+ if (*retval == FW_SUCCESS)
+ for (i = 0; i < nparams; i++, p += 2)
+ *val++ = ntohl(*p);
+}
+
+/*
+ * csio_mb_ldst - FW LDST command
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: timeout
+ * @reg: register
+ *
+ */
+void
+csio_mb_ldst(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo, int reg)
+{
+ struct fw_ldst_cmd *ldst_cmd = (struct fw_ldst_cmd *)(mbp->mb);
+ CSIO_INIT_MBP(mbp, ldst_cmd, tmo, hw, NULL, 1);
+
+ /*
+ * Construct and send the Firmware LDST Command to retrieve the
+ * specified PCI-E Configuration Space register.
+ */
+ ldst_cmd->op_to_addrspace =
+ htonl(FW_CMD_OP_V(FW_LDST_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_READ_F |
+ FW_LDST_CMD_ADDRSPACE_V(FW_LDST_ADDRSPC_FUNC_PCIE));
+ ldst_cmd->cycles_to_len16 = htonl(FW_LEN16(struct fw_ldst_cmd));
+ ldst_cmd->u.pcie.select_naccess = FW_LDST_CMD_NACCESS_V(1);
+ ldst_cmd->u.pcie.ctrl_to_fn =
+ (FW_LDST_CMD_LC_F | FW_LDST_CMD_FN_V(hw->pfn));
+ ldst_cmd->u.pcie.r = (uint8_t)reg;
+}
+
+/*
+ *
+ * csio_mb_caps_config - FW Read/Write Capabilities command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @wr: Write if 1, Read if 0
+ * @init: Turn on initiator mode.
+ * @tgt: Turn on target mode.
+ * @cofld: If 1, Control Offload for FCoE
+ * @cbfn: Callback, if any.
+ *
+ * This helper assumes that cmdp has MB payload from a previous CAPS
+ * read command.
+ */
+void
+csio_mb_caps_config(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ bool wr, bool init, bool tgt, bool cofld,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_caps_config_cmd *cmdp =
+ (struct fw_caps_config_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, wr ? 0 : 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
+ FW_CMD_REQUEST_F |
+ (wr ? FW_CMD_WRITE_F : FW_CMD_READ_F));
+ cmdp->cfvalid_to_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+ /* Read config */
+ if (!wr)
+ return;
+
+ /* Write config */
+ cmdp->fcoecaps = 0;
+
+ if (cofld)
+ cmdp->fcoecaps |= htons(FW_CAPS_CONFIG_FCOE_CTRL_OFLD);
+ if (init)
+ cmdp->fcoecaps |= htons(FW_CAPS_CONFIG_FCOE_INITIATOR);
+ if (tgt)
+ cmdp->fcoecaps |= htons(FW_CAPS_CONFIG_FCOE_TARGET);
+}
+
+/*
+ * csio_mb_port- FW PORT command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: COmmand timeout
+ * @portid: Port ID to get/set info
+ * @wr: Write/Read PORT information.
+ * @fc: Flow control
+ * @caps: Port capabilites to set.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_port(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ u8 portid, bool wr, uint32_t fc, uint16_t fw_caps,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_port_cmd *cmdp = (struct fw_port_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_portid = htonl(FW_CMD_OP_V(FW_PORT_CMD) |
+ FW_CMD_REQUEST_F |
+ (wr ? FW_CMD_EXEC_F : FW_CMD_READ_F) |
+ FW_PORT_CMD_PORTID_V(portid));
+ if (!wr) {
+ cmdp->action_to_len16 = htonl(
+ FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16
+ ? FW_PORT_ACTION_GET_PORT_INFO
+ : FW_PORT_ACTION_GET_PORT_INFO32) |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ return;
+ }
+
+ /* Set port */
+ cmdp->action_to_len16 = htonl(
+ FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16
+ ? FW_PORT_ACTION_L1_CFG
+ : FW_PORT_ACTION_L1_CFG32) |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+ if (fw_caps == FW_CAPS16)
+ cmdp->u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(fc));
+ else
+ cmdp->u.l1cfg32.rcap32 = cpu_to_be32(fc);
+}
+
+/*
+ * csio_mb_process_read_port_rsp - FW PORT command response processing helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @retval: Mailbox return value from Firmware
+ * @caps: port capabilities
+ *
+ */
+void
+csio_mb_process_read_port_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval, uint16_t fw_caps,
+ u32 *pcaps, u32 *acaps)
+{
+ struct fw_port_cmd *rsp = (struct fw_port_cmd *)(mbp->mb);
+
+ *retval = FW_CMD_RETVAL_G(ntohl(rsp->action_to_len16));
+
+ if (*retval == FW_SUCCESS) {
+ if (fw_caps == FW_CAPS16) {
+ *pcaps = fwcaps16_to_caps32(ntohs(rsp->u.info.pcap));
+ *acaps = fwcaps16_to_caps32(ntohs(rsp->u.info.acap));
+ } else {
+ *pcaps = be32_to_cpu(rsp->u.info32.pcaps32);
+ *acaps = be32_to_cpu(rsp->u.info32.acaps32);
+ }
+ }
+}
+
+/*
+ * csio_mb_initialize - FW INITIALIZE command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: COmmand timeout
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_initialize(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_initialize_cmd *cmdp = (struct fw_initialize_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP_V(FW_INITIALIZE_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+}
+
+/*
+ * csio_mb_iq_alloc - Initializes the mailbox to allocate an
+ * Ingress DMA queue in the firmware.
+ *
+ * @hw: The hw structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private object
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @iq_params: Ingress queue params needed for allocation.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+static void
+csio_mb_iq_alloc(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_iq_cmd *cmdp = (struct fw_iq_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP_V(FW_IQ_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_EXEC_F |
+ FW_IQ_CMD_PFN_V(iq_params->pfn) |
+ FW_IQ_CMD_VFN_V(iq_params->vfn));
+
+ cmdp->alloc_to_len16 = htonl(FW_IQ_CMD_ALLOC_F |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+ cmdp->type_to_iqandstindex = htonl(
+ FW_IQ_CMD_VIID_V(iq_params->viid) |
+ FW_IQ_CMD_TYPE_V(iq_params->type) |
+ FW_IQ_CMD_IQASYNCH_V(iq_params->iqasynch));
+
+ cmdp->fl0size = htons(iq_params->fl0size);
+ cmdp->fl0size = htons(iq_params->fl1size);
+
+} /* csio_mb_iq_alloc */
+
+/*
+ * csio_mb_iq_write - Initializes the mailbox for writing into an
+ * Ingress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private object
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cascaded_req: TRUE - if this request is cascased with iq-alloc request.
+ * @iq_params: Ingress queue params needed for writing.
+ * @cbfn: The call-back function
+ *
+ * NOTE: We OR relevant bits with cmdp->XXX, instead of just equating,
+ * because this IQ write request can be cascaded with a previous
+ * IQ alloc request, and we dont want to over-write the bits set by
+ * that request. This logic will work even in a non-cascaded case, since the
+ * cmdp structure is zeroed out by CSIO_INIT_MBP.
+ */
+static void
+csio_mb_iq_write(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, bool cascaded_req,
+ struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_iq_cmd *cmdp = (struct fw_iq_cmd *)(mbp->mb);
+
+ uint32_t iq_start_stop = (iq_params->iq_start) ?
+ FW_IQ_CMD_IQSTART_F :
+ FW_IQ_CMD_IQSTOP_F;
+ int relaxed = !(hw->flags & CSIO_HWF_ROOT_NO_RELAXED_ORDERING);
+
+ /*
+ * If this IQ write is cascaded with IQ alloc request, do not
+ * re-initialize with 0's.
+ *
+ */
+ if (!cascaded_req)
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn |= htonl(FW_CMD_OP_V(FW_IQ_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_WRITE_F |
+ FW_IQ_CMD_PFN_V(iq_params->pfn) |
+ FW_IQ_CMD_VFN_V(iq_params->vfn));
+ cmdp->alloc_to_len16 |= htonl(iq_start_stop |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ cmdp->iqid |= htons(iq_params->iqid);
+ cmdp->fl0id |= htons(iq_params->fl0id);
+ cmdp->fl1id |= htons(iq_params->fl1id);
+ cmdp->type_to_iqandstindex |= htonl(
+ FW_IQ_CMD_IQANDST_V(iq_params->iqandst) |
+ FW_IQ_CMD_IQANUS_V(iq_params->iqanus) |
+ FW_IQ_CMD_IQANUD_V(iq_params->iqanud) |
+ FW_IQ_CMD_IQANDSTINDEX_V(iq_params->iqandstindex));
+ cmdp->iqdroprss_to_iqesize |= htons(
+ FW_IQ_CMD_IQPCIECH_V(iq_params->iqpciech) |
+ FW_IQ_CMD_IQDCAEN_V(iq_params->iqdcaen) |
+ FW_IQ_CMD_IQDCACPU_V(iq_params->iqdcacpu) |
+ FW_IQ_CMD_IQINTCNTTHRESH_V(iq_params->iqintcntthresh) |
+ FW_IQ_CMD_IQCPRIO_V(iq_params->iqcprio) |
+ FW_IQ_CMD_IQESIZE_V(iq_params->iqesize));
+
+ cmdp->iqsize |= htons(iq_params->iqsize);
+ cmdp->iqaddr |= cpu_to_be64(iq_params->iqaddr);
+
+ if (iq_params->type == 0) {
+ cmdp->iqns_to_fl0congen |= htonl(
+ FW_IQ_CMD_IQFLINTIQHSEN_V(iq_params->iqflintiqhsen)|
+ FW_IQ_CMD_IQFLINTCONGEN_V(iq_params->iqflintcongen));
+ }
+
+ if (iq_params->fl0size && iq_params->fl0addr &&
+ (iq_params->fl0id != 0xFFFF)) {
+
+ cmdp->iqns_to_fl0congen |= htonl(
+ FW_IQ_CMD_FL0HOSTFCMODE_V(iq_params->fl0hostfcmode)|
+ FW_IQ_CMD_FL0CPRIO_V(iq_params->fl0cprio) |
+ FW_IQ_CMD_FL0FETCHRO_V(relaxed) |
+ FW_IQ_CMD_FL0DATARO_V(relaxed) |
+ FW_IQ_CMD_FL0PADEN_V(iq_params->fl0paden) |
+ FW_IQ_CMD_FL0PACKEN_V(iq_params->fl0packen));
+ cmdp->fl0dcaen_to_fl0cidxfthresh |= htons(
+ FW_IQ_CMD_FL0DCAEN_V(iq_params->fl0dcaen) |
+ FW_IQ_CMD_FL0DCACPU_V(iq_params->fl0dcacpu) |
+ FW_IQ_CMD_FL0FBMIN_V(iq_params->fl0fbmin) |
+ FW_IQ_CMD_FL0FBMAX_V(iq_params->fl0fbmax) |
+ FW_IQ_CMD_FL0CIDXFTHRESH_V(iq_params->fl0cidxfthresh));
+ cmdp->fl0size |= htons(iq_params->fl0size);
+ cmdp->fl0addr |= cpu_to_be64(iq_params->fl0addr);
+ }
+} /* csio_mb_iq_write */
+
+/*
+ * csio_mb_iq_alloc_write - Initializes the mailbox for allocating an
+ * Ingress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @iq_params: Ingress queue params needed for allocation & writing.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_iq_alloc_write(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ csio_mb_iq_alloc(hw, mbp, priv, mb_tmo, iq_params, cbfn);
+ csio_mb_iq_write(hw, mbp, priv, mb_tmo, true, iq_params, cbfn);
+} /* csio_mb_iq_alloc_write */
+
+/*
+ * csio_mb_iq_alloc_write_rsp - Process the allocation & writing
+ * of ingress DMA queue mailbox's response.
+ *
+ * @hw: The HW structure.
+ * @mbp: Mailbox structure to initialize.
+ * @retval: Firmware return value.
+ * @iq_params: Ingress queue parameters, after allocation and write.
+ *
+ */
+void
+csio_mb_iq_alloc_write_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *ret_val,
+ struct csio_iq_params *iq_params)
+{
+ struct fw_iq_cmd *rsp = (struct fw_iq_cmd *)(mbp->mb);
+
+ *ret_val = FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16));
+ if (*ret_val == FW_SUCCESS) {
+ iq_params->physiqid = ntohs(rsp->physiqid);
+ iq_params->iqid = ntohs(rsp->iqid);
+ iq_params->fl0id = ntohs(rsp->fl0id);
+ iq_params->fl1id = ntohs(rsp->fl1id);
+ } else {
+ iq_params->physiqid = iq_params->iqid =
+ iq_params->fl0id = iq_params->fl1id = 0;
+ }
+} /* csio_mb_iq_alloc_write_rsp */
+
+/*
+ * csio_mb_iq_free - Initializes the mailbox for freeing a
+ * specified Ingress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @iq_params: Parameters of ingress queue, that is to be freed.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_iq_free(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_iq_cmd *cmdp = (struct fw_iq_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP_V(FW_IQ_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_EXEC_F |
+ FW_IQ_CMD_PFN_V(iq_params->pfn) |
+ FW_IQ_CMD_VFN_V(iq_params->vfn));
+ cmdp->alloc_to_len16 = htonl(FW_IQ_CMD_FREE_F |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ cmdp->type_to_iqandstindex = htonl(FW_IQ_CMD_TYPE_V(iq_params->type));
+
+ cmdp->iqid = htons(iq_params->iqid);
+ cmdp->fl0id = htons(iq_params->fl0id);
+ cmdp->fl1id = htons(iq_params->fl1id);
+
+} /* csio_mb_iq_free */
+
+/*
+ * csio_mb_eq_ofld_alloc - Initializes the mailbox for allocating
+ * an offload-egress queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @eq_ofld_params: (Offload) Egress queue parameters.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+static void
+csio_mb_eq_ofld_alloc(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_eq_ofld_cmd *cmdp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+ cmdp->op_to_vfn = htonl(FW_CMD_OP_V(FW_EQ_OFLD_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_EXEC_F |
+ FW_EQ_OFLD_CMD_PFN_V(eq_ofld_params->pfn) |
+ FW_EQ_OFLD_CMD_VFN_V(eq_ofld_params->vfn));
+ cmdp->alloc_to_len16 = htonl(FW_EQ_OFLD_CMD_ALLOC_F |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+} /* csio_mb_eq_ofld_alloc */
+
+/*
+ * csio_mb_eq_ofld_write - Initializes the mailbox for writing
+ * an alloacted offload-egress queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cascaded_req: TRUE - if this request is cascased with Eq-alloc request.
+ * @eq_ofld_params: (Offload) Egress queue parameters.
+ * @cbfn: The call-back function
+ *
+ *
+ * NOTE: We OR relevant bits with cmdp->XXX, instead of just equating,
+ * because this EQ write request can be cascaded with a previous
+ * EQ alloc request, and we dont want to over-write the bits set by
+ * that request. This logic will work even in a non-cascaded case, since the
+ * cmdp structure is zeroed out by CSIO_INIT_MBP.
+ */
+static void
+csio_mb_eq_ofld_write(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, bool cascaded_req,
+ struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_eq_ofld_cmd *cmdp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ uint32_t eq_start_stop = (eq_ofld_params->eqstart) ?
+ FW_EQ_OFLD_CMD_EQSTART_F :
+ FW_EQ_OFLD_CMD_EQSTOP_F;
+
+ /*
+ * If this EQ write is cascaded with EQ alloc request, do not
+ * re-initialize with 0's.
+ *
+ */
+ if (!cascaded_req)
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn |= htonl(FW_CMD_OP_V(FW_EQ_OFLD_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_WRITE_F |
+ FW_EQ_OFLD_CMD_PFN_V(eq_ofld_params->pfn) |
+ FW_EQ_OFLD_CMD_VFN_V(eq_ofld_params->vfn));
+ cmdp->alloc_to_len16 |= htonl(eq_start_stop |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+ cmdp->eqid_pkd |= htonl(FW_EQ_OFLD_CMD_EQID_V(eq_ofld_params->eqid));
+
+ cmdp->fetchszm_to_iqid |= htonl(
+ FW_EQ_OFLD_CMD_HOSTFCMODE_V(eq_ofld_params->hostfcmode) |
+ FW_EQ_OFLD_CMD_CPRIO_V(eq_ofld_params->cprio) |
+ FW_EQ_OFLD_CMD_PCIECHN_V(eq_ofld_params->pciechn) |
+ FW_EQ_OFLD_CMD_IQID_V(eq_ofld_params->iqid));
+
+ cmdp->dcaen_to_eqsize |= htonl(
+ FW_EQ_OFLD_CMD_DCAEN_V(eq_ofld_params->dcaen) |
+ FW_EQ_OFLD_CMD_DCACPU_V(eq_ofld_params->dcacpu) |
+ FW_EQ_OFLD_CMD_FBMIN_V(eq_ofld_params->fbmin) |
+ FW_EQ_OFLD_CMD_FBMAX_V(eq_ofld_params->fbmax) |
+ FW_EQ_OFLD_CMD_CIDXFTHRESHO_V(eq_ofld_params->cidxfthresho) |
+ FW_EQ_OFLD_CMD_CIDXFTHRESH_V(eq_ofld_params->cidxfthresh) |
+ FW_EQ_OFLD_CMD_EQSIZE_V(eq_ofld_params->eqsize));
+
+ cmdp->eqaddr |= cpu_to_be64(eq_ofld_params->eqaddr);
+
+} /* csio_mb_eq_ofld_write */
+
+/*
+ * csio_mb_eq_ofld_alloc_write - Initializes the mailbox for allocation
+ * writing into an Engress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @eq_ofld_params: (Offload) Egress queue parameters.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_eq_ofld_alloc_write(struct csio_hw *hw, struct csio_mb *mbp,
+ void *priv, uint32_t mb_tmo,
+ struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ csio_mb_eq_ofld_alloc(hw, mbp, priv, mb_tmo, eq_ofld_params, cbfn);
+ csio_mb_eq_ofld_write(hw, mbp, priv, mb_tmo, true,
+ eq_ofld_params, cbfn);
+} /* csio_mb_eq_ofld_alloc_write */
+
+/*
+ * csio_mb_eq_ofld_alloc_write_rsp - Process the allocation
+ * & write egress DMA queue mailbox's response.
+ *
+ * @hw: The HW structure.
+ * @mbp: Mailbox structure to initialize.
+ * @retval: Firmware return value.
+ * @eq_ofld_params: (Offload) Egress queue parameters.
+ *
+ */
+void
+csio_mb_eq_ofld_alloc_write_rsp(struct csio_hw *hw,
+ struct csio_mb *mbp, enum fw_retval *ret_val,
+ struct csio_eq_params *eq_ofld_params)
+{
+ struct fw_eq_ofld_cmd *rsp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ *ret_val = FW_CMD_RETVAL_G(ntohl(rsp->alloc_to_len16));
+
+ if (*ret_val == FW_SUCCESS) {
+ eq_ofld_params->eqid = FW_EQ_OFLD_CMD_EQID_G(
+ ntohl(rsp->eqid_pkd));
+ eq_ofld_params->physeqid = FW_EQ_OFLD_CMD_PHYSEQID_G(
+ ntohl(rsp->physeqid_pkd));
+ } else
+ eq_ofld_params->eqid = 0;
+
+} /* csio_mb_eq_ofld_alloc_write_rsp */
+
+/*
+ * csio_mb_eq_ofld_free - Initializes the mailbox for freeing a
+ * specified Engress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data area.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @eq_ofld_params: (Offload) Egress queue parameters, that is to be freed.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_eq_ofld_free(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_eq_ofld_cmd *cmdp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP_V(FW_EQ_OFLD_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_EXEC_F |
+ FW_EQ_OFLD_CMD_PFN_V(eq_ofld_params->pfn) |
+ FW_EQ_OFLD_CMD_VFN_V(eq_ofld_params->vfn));
+ cmdp->alloc_to_len16 = htonl(FW_EQ_OFLD_CMD_FREE_F |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ cmdp->eqid_pkd = htonl(FW_EQ_OFLD_CMD_EQID_V(eq_ofld_params->eqid));
+
+} /* csio_mb_eq_ofld_free */
+
+/*
+ * csio_write_fcoe_link_cond_init_mb - Initialize Mailbox to write FCoE link
+ * condition.
+ *
+ * @ln: The Lnode structure
+ * @mbp: Mailbox structure to initialize
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cbfn: The call back function.
+ *
+ *
+ */
+void
+csio_write_fcoe_link_cond_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint8_t port_id, uint32_t sub_opcode,
+ uint8_t cos, bool link_status, uint32_t fcfi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_link_cmd *cmdp =
+ (struct fw_fcoe_link_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_portid = htonl((
+ FW_CMD_OP_V(FW_FCOE_LINK_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_WRITE_F |
+ FW_FCOE_LINK_CMD_PORTID(port_id)));
+ cmdp->sub_opcode_fcfi = htonl(
+ FW_FCOE_LINK_CMD_SUB_OPCODE(sub_opcode) |
+ FW_FCOE_LINK_CMD_FCFI(fcfi));
+ cmdp->lstatus = link_status;
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+} /* csio_write_fcoe_link_cond_init_mb */
+
+/*
+ * csio_fcoe_read_res_info_init_mb - Initializes the mailbox for reading FCoE
+ * resource information(FW_GET_RES_INFO_CMD).
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_fcoe_read_res_info_init_mb(struct csio_hw *hw, struct csio_mb *mbp,
+ uint32_t mb_tmo,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_res_info_cmd *cmdp =
+ (struct fw_fcoe_res_info_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, hw, cbfn, 1);
+
+ cmdp->op_to_read = htonl((FW_CMD_OP_V(FW_FCOE_RES_INFO_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_READ_F));
+
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+} /* csio_fcoe_read_res_info_init_mb */
+
+/*
+ * csio_fcoe_vnp_alloc_init_mb - Initializes the mailbox for allocating VNP
+ * in the firmware (FW_FCOE_VNP_CMD).
+ *
+ * @ln: The Lnode structure.
+ * @mbp: Mailbox structure to initialize.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcfi: FCF Index.
+ * @vnpi: vnpi
+ * @iqid: iqid
+ * @vnport_wwnn: vnport WWNN
+ * @vnport_wwpn: vnport WWPN
+ * @cbfn: The call-back function.
+ *
+ *
+ */
+void
+csio_fcoe_vnp_alloc_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t fcfi, uint32_t vnpi, uint16_t iqid,
+ uint8_t vnport_wwnn[8], uint8_t vnport_wwpn[8],
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_vnp_cmd *cmdp =
+ (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_fcfi = htonl((FW_CMD_OP_V(FW_FCOE_VNP_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_EXEC_F |
+ FW_FCOE_VNP_CMD_FCFI(fcfi)));
+
+ cmdp->alloc_to_len16 = htonl(FW_FCOE_VNP_CMD_ALLOC |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+ cmdp->gen_wwn_to_vnpi = htonl(FW_FCOE_VNP_CMD_VNPI(vnpi));
+
+ cmdp->iqid = htons(iqid);
+
+ if (!wwn_to_u64(vnport_wwnn) && !wwn_to_u64(vnport_wwpn))
+ cmdp->gen_wwn_to_vnpi |= htonl(FW_FCOE_VNP_CMD_GEN_WWN);
+
+ if (vnport_wwnn)
+ memcpy(cmdp->vnport_wwnn, vnport_wwnn, 8);
+ if (vnport_wwpn)
+ memcpy(cmdp->vnport_wwpn, vnport_wwpn, 8);
+
+} /* csio_fcoe_vnp_alloc_init_mb */
+
+/*
+ * csio_fcoe_vnp_read_init_mb - Prepares VNP read cmd.
+ * @ln: The Lnode structure.
+ * @mbp: Mailbox structure to initialize.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcfi: FCF Index.
+ * @vnpi: vnpi
+ * @cbfn: The call-back handler.
+ */
+void
+csio_fcoe_vnp_read_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t fcfi, uint32_t vnpi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_vnp_cmd *cmdp =
+ (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+ cmdp->op_to_fcfi = htonl(FW_CMD_OP_V(FW_FCOE_VNP_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_READ_F |
+ FW_FCOE_VNP_CMD_FCFI(fcfi));
+ cmdp->alloc_to_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ cmdp->gen_wwn_to_vnpi = htonl(FW_FCOE_VNP_CMD_VNPI(vnpi));
+}
+
+/*
+ * csio_fcoe_vnp_free_init_mb - Initializes the mailbox for freeing an
+ * alloacted VNP in the firmware (FW_FCOE_VNP_CMD).
+ *
+ * @ln: The Lnode structure.
+ * @mbp: Mailbox structure to initialize.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcfi: FCF flow id
+ * @vnpi: VNP flow id
+ * @cbfn: The call-back function.
+ * Return: None
+ */
+void
+csio_fcoe_vnp_free_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t fcfi, uint32_t vnpi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_vnp_cmd *cmdp =
+ (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_fcfi = htonl(FW_CMD_OP_V(FW_FCOE_VNP_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_EXEC_F |
+ FW_FCOE_VNP_CMD_FCFI(fcfi));
+ cmdp->alloc_to_len16 = htonl(FW_FCOE_VNP_CMD_FREE |
+ FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+ cmdp->gen_wwn_to_vnpi = htonl(FW_FCOE_VNP_CMD_VNPI(vnpi));
+}
+
+/*
+ * csio_fcoe_read_fcf_init_mb - Initializes the mailbox to read the
+ * FCF records.
+ *
+ * @ln: The Lnode structure
+ * @mbp: Mailbox structure to initialize
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcf_params: FC-Forwarder parameters.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_fcoe_read_fcf_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t portid, uint32_t fcfi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_fcf_cmd *cmdp =
+ (struct fw_fcoe_fcf_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_fcfi = htonl(FW_CMD_OP_V(FW_FCOE_FCF_CMD) |
+ FW_CMD_REQUEST_F |
+ FW_CMD_READ_F |
+ FW_FCOE_FCF_CMD_FCFI(fcfi));
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
+
+} /* csio_fcoe_read_fcf_init_mb */
+
+void
+csio_fcoe_read_portparams_init_mb(struct csio_hw *hw, struct csio_mb *mbp,
+ uint32_t mb_tmo,
+ struct fw_fcoe_port_cmd_params *portparams,
+ void (*cbfn)(struct csio_hw *,
+ struct csio_mb *))
+{
+ struct fw_fcoe_stats_cmd *cmdp = (struct fw_fcoe_stats_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, hw, cbfn, 1);
+ mbp->mb_size = 64;
+
+ cmdp->op_to_flowid = htonl(FW_CMD_OP_V(FW_FCOE_STATS_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_READ_F);
+ cmdp->free_to_len16 = htonl(FW_CMD_LEN16_V(CSIO_MAX_MB_SIZE/16));
+
+ cmdp->u.ctl.nstats_port = FW_FCOE_STATS_CMD_NSTATS(portparams->nstats) |
+ FW_FCOE_STATS_CMD_PORT(portparams->portid);
+
+ cmdp->u.ctl.port_valid_ix = FW_FCOE_STATS_CMD_IX(portparams->idx) |
+ FW_FCOE_STATS_CMD_PORT_VALID;
+
+} /* csio_fcoe_read_portparams_init_mb */
+
+void
+csio_mb_process_portparams_rsp(struct csio_hw *hw,
+ struct csio_mb *mbp,
+ enum fw_retval *retval,
+ struct fw_fcoe_port_cmd_params *portparams,
+ struct fw_fcoe_port_stats *portstats)
+{
+ struct fw_fcoe_stats_cmd *rsp = (struct fw_fcoe_stats_cmd *)(mbp->mb);
+ struct fw_fcoe_port_stats stats;
+ uint8_t *src;
+ uint8_t *dst;
+
+ *retval = FW_CMD_RETVAL_G(ntohl(rsp->free_to_len16));
+
+ memset(&stats, 0, sizeof(struct fw_fcoe_port_stats));
+
+ if (*retval == FW_SUCCESS) {
+ dst = (uint8_t *)(&stats) + ((portparams->idx - 1) * 8);
+ src = (uint8_t *)rsp + (CSIO_STATS_OFFSET * 8);
+ memcpy(dst, src, (portparams->nstats * 8));
+ if (portparams->idx == 1) {
+ /* Get the first 6 flits from the Mailbox */
+ portstats->tx_bcast_bytes = stats.tx_bcast_bytes;
+ portstats->tx_bcast_frames = stats.tx_bcast_frames;
+ portstats->tx_mcast_bytes = stats.tx_mcast_bytes;
+ portstats->tx_mcast_frames = stats.tx_mcast_frames;
+ portstats->tx_ucast_bytes = stats.tx_ucast_bytes;
+ portstats->tx_ucast_frames = stats.tx_ucast_frames;
+ }
+ if (portparams->idx == 7) {
+ /* Get the second 6 flits from the Mailbox */
+ portstats->tx_drop_frames = stats.tx_drop_frames;
+ portstats->tx_offload_bytes = stats.tx_offload_bytes;
+ portstats->tx_offload_frames = stats.tx_offload_frames;
+#if 0
+ portstats->rx_pf_bytes = stats.rx_pf_bytes;
+ portstats->rx_pf_frames = stats.rx_pf_frames;
+#endif
+ portstats->rx_bcast_bytes = stats.rx_bcast_bytes;
+ portstats->rx_bcast_frames = stats.rx_bcast_frames;
+ portstats->rx_mcast_bytes = stats.rx_mcast_bytes;
+ }
+ if (portparams->idx == 13) {
+ /* Get the last 4 flits from the Mailbox */
+ portstats->rx_mcast_frames = stats.rx_mcast_frames;
+ portstats->rx_ucast_bytes = stats.rx_ucast_bytes;
+ portstats->rx_ucast_frames = stats.rx_ucast_frames;
+ portstats->rx_err_frames = stats.rx_err_frames;
+ }
+ }
+}
+
+/* Entry points/APIs for MB module */
+/*
+ * csio_mb_intr_enable - Enable Interrupts from mailboxes.
+ * @hw: The HW structure
+ *
+ * Enables CIM interrupt bit in appropriate INT_ENABLE registers.
+ */
+void
+csio_mb_intr_enable(struct csio_hw *hw)
+{
+ csio_wr_reg32(hw, MBMSGRDYINTEN_F, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A));
+ csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A));
+}
+
+/*
+ * csio_mb_intr_disable - Disable Interrupts from mailboxes.
+ * @hw: The HW structure
+ *
+ * Disable bit in HostInterruptEnable CIM register.
+ */
+void
+csio_mb_intr_disable(struct csio_hw *hw)
+{
+ csio_wr_reg32(hw, MBMSGRDYINTEN_V(0),
+ MYPF_REG(CIM_PF_HOST_INT_ENABLE_A));
+ csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A));
+}
+
+static void
+csio_mb_dump_fw_dbg(struct csio_hw *hw, __be64 *cmd)
+{
+ struct fw_debug_cmd *dbg = (struct fw_debug_cmd *)cmd;
+
+ if ((FW_DEBUG_CMD_TYPE_G(ntohl(dbg->op_type))) == 1) {
+ csio_info(hw, "FW print message:\n");
+ csio_info(hw, "\tdebug->dprtstridx = %d\n",
+ ntohs(dbg->u.prt.dprtstridx));
+ csio_info(hw, "\tdebug->dprtstrparam0 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam0));
+ csio_info(hw, "\tdebug->dprtstrparam1 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam1));
+ csio_info(hw, "\tdebug->dprtstrparam2 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam2));
+ csio_info(hw, "\tdebug->dprtstrparam3 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam3));
+ } else {
+ /* This is a FW assertion */
+ csio_fatal(hw, "FW assertion at %.16s:%u, val0 %#x, val1 %#x\n",
+ dbg->u.assert.filename_0_7,
+ ntohl(dbg->u.assert.line),
+ ntohl(dbg->u.assert.x),
+ ntohl(dbg->u.assert.y));
+ }
+}
+
+static void
+csio_mb_debug_cmd_handler(struct csio_hw *hw)
+{
+ int i;
+ __be64 cmd[CSIO_MB_MAX_REGS];
+ uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A);
+ uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A);
+ int size = sizeof(struct fw_debug_cmd);
+
+ /* Copy mailbox data */
+ for (i = 0; i < size; i += 8)
+ cmd[i / 8] = cpu_to_be64(csio_rd_reg64(hw, data_reg + i));
+
+ csio_mb_dump_fw_dbg(hw, cmd);
+
+ /* Notify FW of mailbox by setting owner as UP */
+ csio_wr_reg32(hw, MBMSGVALID_F | MBINTREQ_F |
+ MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg);
+
+ csio_rd_reg32(hw, ctl_reg);
+ wmb();
+}
+
+/*
+ * csio_mb_issue - generic routine for issuing Mailbox commands.
+ * @hw: The HW structure
+ * @mbp: Mailbox command to issue
+ *
+ * Caller should hold hw lock across this call.
+ */
+int
+csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ uint32_t owner, ctl;
+ int i;
+ uint32_t ii;
+ __be64 *cmd = mbp->mb;
+ __be64 hdr;
+ struct csio_mbm *mbm = &hw->mbm;
+ uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A);
+ uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A);
+ int size = mbp->mb_size;
+ int rv = -EINVAL;
+ struct fw_cmd_hdr *fw_hdr;
+
+ /* Determine mode */
+ if (mbp->mb_cbfn == NULL) {
+ /* Need to issue/get results in the same context */
+ if (mbp->tmo < CSIO_MB_POLL_FREQ) {
+ csio_err(hw, "Invalid tmo: 0x%x\n", mbp->tmo);
+ goto error_out;
+ }
+ } else if (!csio_is_host_intr_enabled(hw) ||
+ !csio_is_hw_intr_enabled(hw)) {
+ csio_err(hw, "Cannot issue mailbox in interrupt mode 0x%x\n",
+ *((uint8_t *)mbp->mb));
+ goto error_out;
+ }
+
+ if (mbm->mcurrent != NULL) {
+ /* Queue mbox cmd, if another mbox cmd is active */
+ if (mbp->mb_cbfn == NULL) {
+ rv = -EBUSY;
+ csio_dbg(hw, "Couldn't own Mailbox %x op:0x%x\n",
+ hw->pfn, *((uint8_t *)mbp->mb));
+
+ goto error_out;
+ } else {
+ list_add_tail(&mbp->list, &mbm->req_q);
+ CSIO_INC_STATS(mbm, n_activeq);
+
+ return 0;
+ }
+ }
+
+ /* Now get ownership of mailbox */
+ owner = MBOWNER_G(csio_rd_reg32(hw, ctl_reg));
+
+ if (!csio_mb_is_host_owner(owner)) {
+
+ for (i = 0; (owner == CSIO_MBOWNER_NONE) && (i < 3); i++)
+ owner = MBOWNER_G(csio_rd_reg32(hw, ctl_reg));
+ /*
+ * Mailbox unavailable. In immediate mode, fail the command.
+ * In other modes, enqueue the request.
+ */
+ if (!csio_mb_is_host_owner(owner)) {
+ if (mbp->mb_cbfn == NULL) {
+ rv = owner ? -EBUSY : -ETIMEDOUT;
+
+ csio_dbg(hw,
+ "Couldn't own Mailbox %x op:0x%x "
+ "owner:%x\n",
+ hw->pfn, *((uint8_t *)mbp->mb), owner);
+ goto error_out;
+ } else {
+ if (mbm->mcurrent == NULL) {
+ csio_err(hw,
+ "Couldn't own Mailbox %x "
+ "op:0x%x owner:%x\n",
+ hw->pfn, *((uint8_t *)mbp->mb),
+ owner);
+ csio_err(hw,
+ "No outstanding driver"
+ " mailbox as well\n");
+ goto error_out;
+ }
+ }
+ }
+ }
+
+ /* Mailbox is available, copy mailbox data into it */
+ for (i = 0; i < size; i += 8) {
+ csio_wr_reg64(hw, be64_to_cpu(*cmd), data_reg + i);
+ cmd++;
+ }
+
+ CSIO_DUMP_MB(hw, hw->pfn, data_reg);
+
+ /* Start completion timers in non-immediate modes and notify FW */
+ if (mbp->mb_cbfn != NULL) {
+ mbm->mcurrent = mbp;
+ mod_timer(&mbm->timer, jiffies + msecs_to_jiffies(mbp->tmo));
+ csio_wr_reg32(hw, MBMSGVALID_F | MBINTREQ_F |
+ MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg);
+ } else
+ csio_wr_reg32(hw, MBMSGVALID_F | MBOWNER_V(CSIO_MBOWNER_FW),
+ ctl_reg);
+
+ /* Flush posted writes */
+ csio_rd_reg32(hw, ctl_reg);
+ wmb();
+
+ CSIO_INC_STATS(mbm, n_req);
+
+ if (mbp->mb_cbfn)
+ return 0;
+
+ /* Poll for completion in immediate mode */
+ cmd = mbp->mb;
+
+ for (ii = 0; ii < mbp->tmo; ii += CSIO_MB_POLL_FREQ) {
+ mdelay(CSIO_MB_POLL_FREQ);
+
+ /* Check for response */
+ ctl = csio_rd_reg32(hw, ctl_reg);
+ if (csio_mb_is_host_owner(MBOWNER_G(ctl))) {
+
+ if (!(ctl & MBMSGVALID_F)) {
+ csio_wr_reg32(hw, 0, ctl_reg);
+ continue;
+ }
+
+ CSIO_DUMP_MB(hw, hw->pfn, data_reg);
+
+ hdr = cpu_to_be64(csio_rd_reg64(hw, data_reg));
+ fw_hdr = (struct fw_cmd_hdr *)&hdr;
+
+ switch (FW_CMD_OP_G(ntohl(fw_hdr->hi))) {
+ case FW_DEBUG_CMD:
+ csio_mb_debug_cmd_handler(hw);
+ continue;
+ }
+
+ /* Copy response */
+ for (i = 0; i < size; i += 8)
+ *cmd++ = cpu_to_be64(csio_rd_reg64
+ (hw, data_reg + i));
+ csio_wr_reg32(hw, 0, ctl_reg);
+
+ if (csio_mb_fw_retval(mbp) != FW_SUCCESS)
+ CSIO_INC_STATS(mbm, n_err);
+
+ CSIO_INC_STATS(mbm, n_rsp);
+ return 0;
+ }
+ }
+
+ CSIO_INC_STATS(mbm, n_tmo);
+
+ csio_err(hw, "Mailbox %x op:0x%x timed out!\n",
+ hw->pfn, *((uint8_t *)cmd));
+
+ return -ETIMEDOUT;
+
+error_out:
+ CSIO_INC_STATS(mbm, n_err);
+ return rv;
+}
+
+/*
+ * csio_mb_completions - Completion handler for Mailbox commands
+ * @hw: The HW structure
+ * @cbfn_q: Completion queue.
+ *
+ */
+void
+csio_mb_completions(struct csio_hw *hw, struct list_head *cbfn_q)
+{
+ struct csio_mb *mbp;
+ struct csio_mbm *mbm = &hw->mbm;
+ enum fw_retval rv;
+
+ while (!list_empty(cbfn_q)) {
+ mbp = list_first_entry(cbfn_q, struct csio_mb, list);
+ list_del_init(&mbp->list);
+
+ rv = csio_mb_fw_retval(mbp);
+ if ((rv != FW_SUCCESS) && (rv != FW_HOSTERROR))
+ CSIO_INC_STATS(mbm, n_err);
+ else if (rv != FW_HOSTERROR)
+ CSIO_INC_STATS(mbm, n_rsp);
+
+ if (mbp->mb_cbfn)
+ mbp->mb_cbfn(hw, mbp);
+ }
+}
+
+static void
+csio_mb_portmod_changed(struct csio_hw *hw, uint8_t port_id)
+{
+ static char *mod_str[] = {
+ NULL, "LR", "SR", "ER", "TWINAX", "active TWINAX", "LRM"
+ };
+
+ struct csio_pport *port = &hw->pport[port_id];
+
+ if (port->mod_type == FW_PORT_MOD_TYPE_NONE)
+ csio_info(hw, "Port:%d - port module unplugged\n", port_id);
+ else if (port->mod_type < ARRAY_SIZE(mod_str))
+ csio_info(hw, "Port:%d - %s port module inserted\n", port_id,
+ mod_str[port->mod_type]);
+ else if (port->mod_type == FW_PORT_MOD_TYPE_NOTSUPPORTED)
+ csio_info(hw,
+ "Port:%d - unsupported optical port module "
+ "inserted\n", port_id);
+ else if (port->mod_type == FW_PORT_MOD_TYPE_UNKNOWN)
+ csio_info(hw,
+ "Port:%d - unknown port module inserted, forcing "
+ "TWINAX\n", port_id);
+ else if (port->mod_type == FW_PORT_MOD_TYPE_ERROR)
+ csio_info(hw, "Port:%d - transceiver module error\n", port_id);
+ else
+ csio_info(hw, "Port:%d - unknown module type %d inserted\n",
+ port_id, port->mod_type);
+}
+
+int
+csio_mb_fwevt_handler(struct csio_hw *hw, __be64 *cmd)
+{
+ uint8_t opcode = *(uint8_t *)cmd;
+ struct fw_port_cmd *pcmd;
+ uint8_t port_id;
+ uint32_t link_status;
+ uint16_t action;
+ uint8_t mod_type;
+ fw_port_cap32_t linkattr;
+
+ if (opcode == FW_PORT_CMD) {
+ pcmd = (struct fw_port_cmd *)cmd;
+ port_id = FW_PORT_CMD_PORTID_G(
+ ntohl(pcmd->op_to_portid));
+ action = FW_PORT_CMD_ACTION_G(
+ ntohl(pcmd->action_to_len16));
+ if (action != FW_PORT_ACTION_GET_PORT_INFO &&
+ action != FW_PORT_ACTION_GET_PORT_INFO32) {
+ csio_err(hw, "Unhandled FW_PORT_CMD action: %u\n",
+ action);
+ return -EINVAL;
+ }
+
+ if (action == FW_PORT_ACTION_GET_PORT_INFO) {
+ link_status = ntohl(pcmd->u.info.lstatus_to_modtype);
+ mod_type = FW_PORT_CMD_MODTYPE_G(link_status);
+ linkattr = lstatus_to_fwcap(link_status);
+
+ hw->pport[port_id].link_status =
+ FW_PORT_CMD_LSTATUS_G(link_status);
+ } else {
+ link_status =
+ ntohl(pcmd->u.info32.lstatus32_to_cbllen32);
+ mod_type = FW_PORT_CMD_MODTYPE32_G(link_status);
+ linkattr = ntohl(pcmd->u.info32.linkattr32);
+
+ hw->pport[port_id].link_status =
+ FW_PORT_CMD_LSTATUS32_G(link_status);
+ }
+
+ hw->pport[port_id].link_speed = fwcap_to_fwspeed(linkattr);
+
+ csio_info(hw, "Port:%x - LINK %s\n", port_id,
+ hw->pport[port_id].link_status ? "UP" : "DOWN");
+
+ if (mod_type != hw->pport[port_id].mod_type) {
+ hw->pport[port_id].mod_type = mod_type;
+ csio_mb_portmod_changed(hw, port_id);
+ }
+ } else if (opcode == FW_DEBUG_CMD) {
+ csio_mb_dump_fw_dbg(hw, cmd);
+ } else {
+ csio_dbg(hw, "Gen MB can't handle op:0x%x on evtq.\n", opcode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_mb_isr_handler - Handle mailboxes related interrupts.
+ * @hw: The HW structure
+ *
+ * Called from the ISR to handle Mailbox related interrupts.
+ * HW Lock should be held across this call.
+ */
+int
+csio_mb_isr_handler(struct csio_hw *hw)
+{
+ struct csio_mbm *mbm = &hw->mbm;
+ struct csio_mb *mbp = mbm->mcurrent;
+ __be64 *cmd;
+ uint32_t ctl, cim_cause, pl_cause;
+ int i;
+ uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A);
+ uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A);
+ int size;
+ __be64 hdr;
+ struct fw_cmd_hdr *fw_hdr;
+
+ pl_cause = csio_rd_reg32(hw, MYPF_REG(PL_PF_INT_CAUSE_A));
+ cim_cause = csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A));
+
+ if (!(pl_cause & PFCIM_F) || !(cim_cause & MBMSGRDYINT_F)) {
+ CSIO_INC_STATS(hw, n_mbint_unexp);
+ return -EINVAL;
+ }
+
+ /*
+ * The cause registers below HAVE to be cleared in the SAME
+ * order as below: The low level cause register followed by
+ * the upper level cause register. In other words, CIM-cause
+ * first followed by PL-Cause next.
+ */
+ csio_wr_reg32(hw, MBMSGRDYINT_F, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A));
+ csio_wr_reg32(hw, PFCIM_F, MYPF_REG(PL_PF_INT_CAUSE_A));
+
+ ctl = csio_rd_reg32(hw, ctl_reg);
+
+ if (csio_mb_is_host_owner(MBOWNER_G(ctl))) {
+
+ CSIO_DUMP_MB(hw, hw->pfn, data_reg);
+
+ if (!(ctl & MBMSGVALID_F)) {
+ csio_warn(hw,
+ "Stray mailbox interrupt recvd,"
+ " mailbox data not valid\n");
+ csio_wr_reg32(hw, 0, ctl_reg);
+ /* Flush */
+ csio_rd_reg32(hw, ctl_reg);
+ return -EINVAL;
+ }
+
+ hdr = cpu_to_be64(csio_rd_reg64(hw, data_reg));
+ fw_hdr = (struct fw_cmd_hdr *)&hdr;
+
+ switch (FW_CMD_OP_G(ntohl(fw_hdr->hi))) {
+ case FW_DEBUG_CMD:
+ csio_mb_debug_cmd_handler(hw);
+ return -EINVAL;
+#if 0
+ case FW_ERROR_CMD:
+ case FW_INITIALIZE_CMD: /* When we are not master */
+#endif
+ }
+
+ CSIO_ASSERT(mbp != NULL);
+
+ cmd = mbp->mb;
+ size = mbp->mb_size;
+ /* Get response */
+ for (i = 0; i < size; i += 8)
+ *cmd++ = cpu_to_be64(csio_rd_reg64
+ (hw, data_reg + i));
+
+ csio_wr_reg32(hw, 0, ctl_reg);
+ /* Flush */
+ csio_rd_reg32(hw, ctl_reg);
+
+ mbm->mcurrent = NULL;
+
+ /* Add completion to tail of cbfn queue */
+ list_add_tail(&mbp->list, &mbm->cbfn_q);
+ CSIO_INC_STATS(mbm, n_cbfnq);
+
+ /*
+ * Enqueue event to EventQ. Events processing happens
+ * in Event worker thread context
+ */
+ if (csio_enqueue_evt(hw, CSIO_EVT_MBX, mbp, sizeof(mbp)))
+ CSIO_INC_STATS(hw, n_evt_drop);
+
+ return 0;
+
+ } else {
+ /*
+ * We can get here if mailbox MSIX vector is shared,
+ * or in INTx case. Or a stray interrupt.
+ */
+ csio_dbg(hw, "Host not owner, no mailbox interrupt\n");
+ CSIO_INC_STATS(hw, n_int_stray);
+ return -EINVAL;
+ }
+}
+
+/*
+ * csio_mb_tmo_handler - Timeout handler
+ * @hw: The HW structure
+ *
+ */
+struct csio_mb *
+csio_mb_tmo_handler(struct csio_hw *hw)
+{
+ struct csio_mbm *mbm = &hw->mbm;
+ struct csio_mb *mbp = mbm->mcurrent;
+ struct fw_cmd_hdr *fw_hdr;
+
+ /*
+ * Could be a race b/w the completion handler and the timer
+ * and the completion handler won that race.
+ */
+ if (mbp == NULL) {
+ CSIO_DB_ASSERT(0);
+ return NULL;
+ }
+
+ fw_hdr = (struct fw_cmd_hdr *)(mbp->mb);
+
+ csio_dbg(hw, "Mailbox num:%x op:0x%x timed out\n", hw->pfn,
+ FW_CMD_OP_G(ntohl(fw_hdr->hi)));
+
+ mbm->mcurrent = NULL;
+ CSIO_INC_STATS(mbm, n_tmo);
+ fw_hdr->lo = htonl(FW_CMD_RETVAL_V(FW_ETIMEDOUT));
+
+ return mbp;
+}
+
+/*
+ * csio_mb_cancel_all - Cancel all waiting commands.
+ * @hw: The HW structure
+ * @cbfn_q: The callback queue.
+ *
+ * Caller should hold hw lock across this call.
+ */
+void
+csio_mb_cancel_all(struct csio_hw *hw, struct list_head *cbfn_q)
+{
+ struct csio_mb *mbp;
+ struct csio_mbm *mbm = &hw->mbm;
+ struct fw_cmd_hdr *hdr;
+ struct list_head *tmp;
+
+ if (mbm->mcurrent) {
+ mbp = mbm->mcurrent;
+
+ /* Stop mailbox completion timer */
+ timer_delete_sync(&mbm->timer);
+
+ /* Add completion to tail of cbfn queue */
+ list_add_tail(&mbp->list, cbfn_q);
+ mbm->mcurrent = NULL;
+ }
+
+ if (!list_empty(&mbm->req_q)) {
+ list_splice_tail_init(&mbm->req_q, cbfn_q);
+ mbm->stats.n_activeq = 0;
+ }
+
+ if (!list_empty(&mbm->cbfn_q)) {
+ list_splice_tail_init(&mbm->cbfn_q, cbfn_q);
+ mbm->stats.n_cbfnq = 0;
+ }
+
+ if (list_empty(cbfn_q))
+ return;
+
+ list_for_each(tmp, cbfn_q) {
+ mbp = (struct csio_mb *)tmp;
+ hdr = (struct fw_cmd_hdr *)(mbp->mb);
+
+ csio_dbg(hw, "Cancelling pending mailbox num %x op:%x\n",
+ hw->pfn, FW_CMD_OP_G(ntohl(hdr->hi)));
+
+ CSIO_INC_STATS(mbm, n_cancel);
+ hdr->lo = htonl(FW_CMD_RETVAL_V(FW_HOSTERROR));
+ }
+}
+
+/*
+ * csio_mbm_init - Initialize Mailbox module
+ * @mbm: Mailbox module
+ * @hw: The HW structure
+ * @timer: Timing function for interrupting mailboxes
+ *
+ * Initialize timer and the request/response queues.
+ */
+int
+csio_mbm_init(struct csio_mbm *mbm, struct csio_hw *hw,
+ void (*timer_fn)(struct timer_list *))
+{
+ mbm->hw = hw;
+ timer_setup(&mbm->timer, timer_fn, 0);
+
+ INIT_LIST_HEAD(&mbm->req_q);
+ INIT_LIST_HEAD(&mbm->cbfn_q);
+ csio_set_mb_intr_idx(mbm, -1);
+
+ return 0;
+}
+
+/*
+ * csio_mbm_exit - Uninitialize mailbox module
+ * @mbm: Mailbox module
+ *
+ * Stop timer.
+ */
+void
+csio_mbm_exit(struct csio_mbm *mbm)
+{
+ timer_delete_sync(&mbm->timer);
+
+ CSIO_DB_ASSERT(mbm->mcurrent == NULL);
+ CSIO_DB_ASSERT(list_empty(&mbm->req_q));
+ CSIO_DB_ASSERT(list_empty(&mbm->cbfn_q));
+}
diff --git a/drivers/scsi/csiostor/csio_mb.h b/drivers/scsi/csiostor/csio_mb.h
new file mode 100644
index 000000000000..b07e891c5936
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_mb.h
@@ -0,0 +1,263 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_MB_H__
+#define __CSIO_MB_H__
+
+#include <linux/timer.h>
+#include <linux/completion.h>
+
+#include "t4fw_api.h"
+#include "t4fw_api_stor.h"
+#include "csio_defs.h"
+
+#define CSIO_STATS_OFFSET (2)
+#define CSIO_NUM_STATS_PER_MB (6)
+
+struct fw_fcoe_port_cmd_params {
+ uint8_t portid;
+ uint8_t idx;
+ uint8_t nstats;
+};
+
+#define CSIO_DUMP_MB(__hw, __num, __mb) \
+ csio_dbg(__hw, "\t%llx %llx %llx %llx %llx %llx %llx %llx\n", \
+ (unsigned long long)csio_rd_reg64(__hw, __mb), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 8), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 16), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 24), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 32), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 40), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 48), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 56))
+
+#define CSIO_MB_MAX_REGS 8
+#define CSIO_MAX_MB_SIZE 64
+#define CSIO_MB_POLL_FREQ 5 /* 5 ms */
+#define CSIO_MB_DEFAULT_TMO FW_CMD_MAX_TIMEOUT
+
+/* Device master in HELLO command */
+enum csio_dev_master { CSIO_MASTER_CANT, CSIO_MASTER_MAY, CSIO_MASTER_MUST };
+
+enum csio_mb_owner { CSIO_MBOWNER_NONE, CSIO_MBOWNER_FW, CSIO_MBOWNER_PL };
+
+enum csio_dev_state {
+ CSIO_DEV_STATE_UNINIT,
+ CSIO_DEV_STATE_INIT,
+ CSIO_DEV_STATE_ERR
+};
+
+#define FW_PARAM_DEV(param) \
+ (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | \
+ FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_##param))
+
+#define FW_PARAM_PFVF(param) \
+ (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) | \
+ FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_##param)| \
+ FW_PARAMS_PARAM_Y_V(0) | \
+ FW_PARAMS_PARAM_Z_V(0))
+
+#define CSIO_INIT_MBP(__mbp, __cp, __tmo, __priv, __fn, __clear) \
+do { \
+ if (__clear) \
+ memset((__cp), 0, \
+ CSIO_MB_MAX_REGS * sizeof(__be64)); \
+ INIT_LIST_HEAD(&(__mbp)->list); \
+ (__mbp)->tmo = (__tmo); \
+ (__mbp)->priv = (void *)(__priv); \
+ (__mbp)->mb_cbfn = (__fn); \
+ (__mbp)->mb_size = sizeof(*(__cp)); \
+} while (0)
+
+struct csio_mbm_stats {
+ uint32_t n_req; /* number of mbox req */
+ uint32_t n_rsp; /* number of mbox rsp */
+ uint32_t n_activeq; /* number of mbox req active Q */
+ uint32_t n_cbfnq; /* number of mbox req cbfn Q */
+ uint32_t n_tmo; /* number of mbox timeout */
+ uint32_t n_cancel; /* number of mbox cancel */
+ uint32_t n_err; /* number of mbox error */
+};
+
+/* Driver version of Mailbox */
+struct csio_mb {
+ struct list_head list; /* for req/resp */
+ /* queue in driver */
+ __be64 mb[CSIO_MB_MAX_REGS]; /* MB in HW format */
+ int mb_size; /* Size of this
+ * mailbox.
+ */
+ uint32_t tmo; /* Timeout */
+ struct completion cmplobj; /* MB Completion
+ * object
+ */
+ void (*mb_cbfn) (struct csio_hw *, struct csio_mb *);
+ /* Callback fn */
+ void *priv; /* Owner private ptr */
+};
+
+struct csio_mbm {
+ uint32_t a_mbox; /* Async mbox num */
+ uint32_t intr_idx; /* Interrupt index */
+ struct timer_list timer; /* Mbox timer */
+ struct csio_hw *hw; /* Hardware pointer */
+ struct list_head req_q; /* Mbox request queue */
+ struct list_head cbfn_q; /* Mbox completion q */
+ struct csio_mb *mcurrent; /* Current mailbox */
+ uint32_t req_q_cnt; /* Outstanding mbox
+ * cmds
+ */
+ struct csio_mbm_stats stats; /* Statistics */
+};
+
+#define csio_set_mb_intr_idx(_m, _i) ((_m)->intr_idx = (_i))
+#define csio_get_mb_intr_idx(_m) ((_m)->intr_idx)
+
+struct csio_iq_params;
+struct csio_eq_params;
+
+enum fw_retval csio_mb_fw_retval(struct csio_mb *);
+
+/* MB helpers */
+void csio_mb_hello(struct csio_hw *, struct csio_mb *, uint32_t,
+ uint32_t, uint32_t, enum csio_dev_master,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_hello_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, enum csio_dev_state *,
+ uint8_t *);
+
+void csio_mb_bye(struct csio_hw *, struct csio_mb *, uint32_t,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_reset(struct csio_hw *, struct csio_mb *, uint32_t, int, int,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_params(struct csio_hw *, struct csio_mb *, uint32_t, unsigned int,
+ unsigned int, unsigned int, const u32 *, u32 *, bool,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_read_params_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, unsigned int , u32 *);
+
+void csio_mb_ldst(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ int reg);
+
+void csio_mb_caps_config(struct csio_hw *, struct csio_mb *, uint32_t,
+ bool, bool, bool, bool,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_port(struct csio_hw *, struct csio_mb *, uint32_t,
+ uint8_t, bool, uint32_t, uint16_t,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_read_port_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, uint16_t,
+ uint32_t *, uint32_t *);
+
+void csio_mb_initialize(struct csio_hw *, struct csio_mb *, uint32_t,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_iq_alloc_write(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t, struct csio_iq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_iq_alloc_write_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, struct csio_iq_params *);
+
+void csio_mb_iq_free(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t, struct csio_iq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_eq_ofld_alloc_write(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t, struct csio_eq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_eq_ofld_alloc_write_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, struct csio_eq_params *);
+
+void csio_mb_eq_ofld_free(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t , struct csio_eq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_read_res_info_init_mb(struct csio_hw *, struct csio_mb *,
+ uint32_t,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_write_fcoe_link_cond_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint8_t, uint32_t, uint8_t, bool, uint32_t,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_vnp_alloc_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint32_t , uint32_t , uint16_t,
+ uint8_t [8], uint8_t [8],
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_vnp_read_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint32_t , uint32_t ,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_vnp_free_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t , uint32_t, uint32_t ,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_read_fcf_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint32_t, uint32_t,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_read_portparams_init_mb(struct csio_hw *hw,
+ struct csio_mb *mbp, uint32_t mb_tmo,
+ struct fw_fcoe_port_cmd_params *portparams,
+ void (*cbfn)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_portparams_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval,
+ struct fw_fcoe_port_cmd_params *portparams,
+ struct fw_fcoe_port_stats *portstats);
+
+/* MB module functions */
+int csio_mbm_init(struct csio_mbm *, struct csio_hw *,
+ void (*)(struct timer_list *));
+void csio_mbm_exit(struct csio_mbm *);
+void csio_mb_intr_enable(struct csio_hw *);
+void csio_mb_intr_disable(struct csio_hw *);
+
+int csio_mb_issue(struct csio_hw *, struct csio_mb *);
+void csio_mb_completions(struct csio_hw *, struct list_head *);
+int csio_mb_fwevt_handler(struct csio_hw *, __be64 *);
+int csio_mb_isr_handler(struct csio_hw *);
+struct csio_mb *csio_mb_tmo_handler(struct csio_hw *);
+void csio_mb_cancel_all(struct csio_hw *, struct list_head *);
+
+#endif /* ifndef __CSIO_MB_H__ */
diff --git a/drivers/scsi/csiostor/csio_rnode.c b/drivers/scsi/csiostor/csio_rnode.c
new file mode 100644
index 000000000000..713e13adf4dc
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_rnode.c
@@ -0,0 +1,921 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/string.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fs.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+
+static int csio_rnode_init(struct csio_rnode *, struct csio_lnode *);
+static void csio_rnode_exit(struct csio_rnode *);
+
+/* Static machine forward declarations */
+static void csio_rns_uninit(struct csio_rnode *, enum csio_rn_ev);
+static void csio_rns_ready(struct csio_rnode *, enum csio_rn_ev);
+static void csio_rns_offline(struct csio_rnode *, enum csio_rn_ev);
+static void csio_rns_disappeared(struct csio_rnode *, enum csio_rn_ev);
+
+/* RNF event mapping */
+static enum csio_rn_ev fwevt_to_rnevt[] = {
+ CSIO_RNFE_NONE, /* None */
+ CSIO_RNFE_LOGGED_IN, /* PLOGI_ACC_RCVD */
+ CSIO_RNFE_NONE, /* PLOGI_RJT_RCVD */
+ CSIO_RNFE_PLOGI_RECV, /* PLOGI_RCVD */
+ CSIO_RNFE_LOGO_RECV, /* PLOGO_RCVD */
+ CSIO_RNFE_PRLI_DONE, /* PRLI_ACC_RCVD */
+ CSIO_RNFE_NONE, /* PRLI_RJT_RCVD */
+ CSIO_RNFE_PRLI_RECV, /* PRLI_RCVD */
+ CSIO_RNFE_PRLO_RECV, /* PRLO_RCVD */
+ CSIO_RNFE_NONE, /* NPORT_ID_CHGD */
+ CSIO_RNFE_LOGO_RECV, /* FLOGO_RCVD */
+ CSIO_RNFE_NONE, /* CLR_VIRT_LNK_RCVD */
+ CSIO_RNFE_LOGGED_IN, /* FLOGI_ACC_RCVD */
+ CSIO_RNFE_NONE, /* FLOGI_RJT_RCVD */
+ CSIO_RNFE_LOGGED_IN, /* FDISC_ACC_RCVD */
+ CSIO_RNFE_NONE, /* FDISC_RJT_RCVD */
+ CSIO_RNFE_NONE, /* FLOGI_TMO_MAX_RETRY */
+ CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_ACC */
+ CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_RJT */
+ CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_CNFLT */
+ CSIO_RNFE_NONE, /* PRLI_TMO */
+ CSIO_RNFE_NONE, /* ADISC_TMO */
+ CSIO_RNFE_NAME_MISSING, /* RSCN_DEV_LOST */
+ CSIO_RNFE_NONE, /* SCR_ACC_RCVD */
+ CSIO_RNFE_NONE, /* ADISC_RJT_RCVD */
+ CSIO_RNFE_NONE, /* LOGO_SNT */
+ CSIO_RNFE_LOGO_RECV, /* PROTO_ERR_IMPL_LOGO */
+};
+
+#define CSIO_FWE_TO_RNFE(_evt) ((_evt > PROTO_ERR_IMPL_LOGO) ? \
+ CSIO_RNFE_NONE : \
+ fwevt_to_rnevt[_evt])
+int
+csio_is_rnode_ready(struct csio_rnode *rn)
+{
+ return csio_match_state(rn, csio_rns_ready);
+}
+
+static int
+csio_is_rnode_uninit(struct csio_rnode *rn)
+{
+ return csio_match_state(rn, csio_rns_uninit);
+}
+
+static int
+csio_is_rnode_wka(uint8_t rport_type)
+{
+ if ((rport_type == FLOGI_VFPORT) ||
+ (rport_type == FDISC_VFPORT) ||
+ (rport_type == NS_VNPORT) ||
+ (rport_type == FDMI_VNPORT))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * csio_rn_lookup - Finds the rnode with the given flowid
+ * @ln - lnode
+ * @flowid - flowid.
+ *
+ * Does the rnode lookup on the given lnode and flowid.If no matching entry
+ * found, NULL is returned.
+ */
+static struct csio_rnode *
+csio_rn_lookup(struct csio_lnode *ln, uint32_t flowid)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp;
+ struct csio_rnode *rn;
+
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ if (rn->flowid == flowid)
+ return rn;
+ }
+
+ return NULL;
+}
+
+/*
+ * csio_rn_lookup_wwpn - Finds the rnode with the given wwpn
+ * @ln: lnode
+ * @wwpn: wwpn
+ *
+ * Does the rnode lookup on the given lnode and wwpn. If no matching entry
+ * found, NULL is returned.
+ */
+static struct csio_rnode *
+csio_rn_lookup_wwpn(struct csio_lnode *ln, uint8_t *wwpn)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp;
+ struct csio_rnode *rn;
+
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ if (!memcmp(csio_rn_wwpn(rn), wwpn, 8))
+ return rn;
+ }
+
+ return NULL;
+}
+
+/**
+ * csio_rnode_lookup_portid - Finds the rnode with the given portid
+ * @ln: lnode
+ * @portid: port id
+ *
+ * Lookup the rnode list for a given portid. If no matching entry
+ * found, NULL is returned.
+ */
+struct csio_rnode *
+csio_rnode_lookup_portid(struct csio_lnode *ln, uint32_t portid)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp;
+ struct csio_rnode *rn;
+
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ if (rn->nport_id == portid)
+ return rn;
+ }
+
+ return NULL;
+}
+
+static int
+csio_rn_dup_flowid(struct csio_lnode *ln, uint32_t rdev_flowid,
+ uint32_t *vnp_flowid)
+{
+ struct csio_rnode *rnhead;
+ struct list_head *tmp, *tmp1;
+ struct csio_rnode *rn;
+ struct csio_lnode *ln_tmp;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ list_for_each(tmp1, &hw->sln_head) {
+ ln_tmp = (struct csio_lnode *) tmp1;
+ if (ln_tmp == ln)
+ continue;
+
+ rnhead = (struct csio_rnode *)&ln_tmp->rnhead;
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+
+ rn = (struct csio_rnode *) tmp;
+ if (csio_is_rnode_ready(rn)) {
+ if (rn->flowid == rdev_flowid) {
+ *vnp_flowid = csio_ln_flowid(ln_tmp);
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct csio_rnode *
+csio_alloc_rnode(struct csio_lnode *ln)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ struct csio_rnode *rn = mempool_alloc(hw->rnode_mempool, GFP_ATOMIC);
+ if (!rn)
+ goto err;
+
+ memset(rn, 0, sizeof(struct csio_rnode));
+ if (csio_rnode_init(rn, ln))
+ goto err_free;
+
+ CSIO_INC_STATS(ln, n_rnode_alloc);
+
+ return rn;
+
+err_free:
+ mempool_free(rn, hw->rnode_mempool);
+err:
+ CSIO_INC_STATS(ln, n_rnode_nomem);
+ return NULL;
+}
+
+static void
+csio_free_rnode(struct csio_rnode *rn)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(csio_rnode_to_lnode(rn));
+
+ csio_rnode_exit(rn);
+ CSIO_INC_STATS(rn->lnp, n_rnode_free);
+ mempool_free(rn, hw->rnode_mempool);
+}
+
+/*
+ * csio_get_rnode - Gets rnode with the given flowid
+ * @ln - lnode
+ * @flowid - flow id.
+ *
+ * Does the rnode lookup on the given lnode and flowid. If no matching
+ * rnode found, then new rnode with given npid is allocated and returned.
+ */
+static struct csio_rnode *
+csio_get_rnode(struct csio_lnode *ln, uint32_t flowid)
+{
+ struct csio_rnode *rn;
+
+ rn = csio_rn_lookup(ln, flowid);
+ if (!rn) {
+ rn = csio_alloc_rnode(ln);
+ if (!rn)
+ return NULL;
+
+ rn->flowid = flowid;
+ }
+
+ return rn;
+}
+
+/*
+ * csio_put_rnode - Frees the given rnode
+ * @ln - lnode
+ * @flowid - flow id.
+ *
+ * Does the rnode lookup on the given lnode and flowid. If no matching
+ * rnode found, then new rnode with given npid is allocated and returned.
+ */
+void
+csio_put_rnode(struct csio_lnode *ln, struct csio_rnode *rn)
+{
+ CSIO_DB_ASSERT(csio_is_rnode_uninit(rn) != 0);
+ csio_free_rnode(rn);
+}
+
+/*
+ * csio_confirm_rnode - confirms rnode based on wwpn.
+ * @ln: lnode
+ * @rdev_flowid: remote device flowid
+ * @rdevp: remote device params
+ * This routines searches other rnode in list having same wwpn of new rnode.
+ * If there is a match, then matched rnode is returned and otherwise new rnode
+ * is returned.
+ * returns rnode.
+ */
+struct csio_rnode *
+csio_confirm_rnode(struct csio_lnode *ln, uint32_t rdev_flowid,
+ struct fcoe_rdev_entry *rdevp)
+{
+ uint8_t rport_type;
+ struct csio_rnode *rn, *match_rn;
+ uint32_t vnp_flowid = 0;
+ __be32 *port_id;
+
+ port_id = (__be32 *)&rdevp->r_id[0];
+ rport_type =
+ FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type);
+
+ /* Drop rdev event for cntrl port */
+ if (rport_type == FAB_CTLR_VNPORT) {
+ csio_ln_dbg(ln,
+ "Unhandled rport_type:%d recv in rdev evt "
+ "ssni:x%x\n", rport_type, rdev_flowid);
+ return NULL;
+ }
+
+ /* Lookup on flowid */
+ rn = csio_rn_lookup(ln, rdev_flowid);
+ if (!rn) {
+
+ /* Drop events with duplicate flowid */
+ if (csio_rn_dup_flowid(ln, rdev_flowid, &vnp_flowid)) {
+ csio_ln_warn(ln,
+ "ssni:%x already active on vnpi:%x",
+ rdev_flowid, vnp_flowid);
+ return NULL;
+ }
+
+ /* Lookup on wwpn for NPORTs */
+ rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn);
+ if (!rn)
+ goto alloc_rnode;
+
+ } else {
+ /* Lookup well-known ports with nport id */
+ if (csio_is_rnode_wka(rport_type)) {
+ match_rn = csio_rnode_lookup_portid(ln,
+ ((ntohl(*port_id) >> 8) & CSIO_DID_MASK));
+ if (match_rn == NULL) {
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ goto alloc_rnode;
+ }
+
+ /*
+ * Now compare the wwpn to confirm that
+ * same port relogged in. If so update the matched rn.
+ * Else, go ahead and alloc a new rnode.
+ */
+ if (!memcmp(csio_rn_wwpn(match_rn), rdevp->wwpn, 8)) {
+ if (rn == match_rn)
+ goto found_rnode;
+ csio_ln_dbg(ln,
+ "nport_id:x%x and wwpn:%llx"
+ " match for ssni:x%x\n",
+ rn->nport_id,
+ wwn_to_u64(rdevp->wwpn),
+ rdev_flowid);
+ if (csio_is_rnode_ready(rn)) {
+ csio_ln_warn(ln,
+ "rnode is already"
+ "active ssni:x%x\n",
+ rdev_flowid);
+ CSIO_ASSERT(0);
+ }
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ rn = match_rn;
+
+ /* Update rn */
+ goto found_rnode;
+ }
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ goto alloc_rnode;
+ }
+
+ /* wwpn match */
+ if (!memcmp(csio_rn_wwpn(rn), rdevp->wwpn, 8))
+ goto found_rnode;
+
+ /* Search for rnode that have same wwpn */
+ match_rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn);
+ if (match_rn != NULL) {
+ csio_ln_dbg(ln,
+ "ssni:x%x changed for rport name(wwpn):%llx "
+ "did:x%x\n", rdev_flowid,
+ wwn_to_u64(rdevp->wwpn),
+ match_rn->nport_id);
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ rn = match_rn;
+ } else {
+ csio_ln_dbg(ln,
+ "rnode wwpn mismatch found ssni:x%x "
+ "name(wwpn):%llx\n",
+ rdev_flowid,
+ wwn_to_u64(csio_rn_wwpn(rn)));
+ if (csio_is_rnode_ready(rn)) {
+ csio_ln_warn(ln,
+ "rnode is already active "
+ "wwpn:%llx ssni:x%x\n",
+ wwn_to_u64(csio_rn_wwpn(rn)),
+ rdev_flowid);
+ CSIO_ASSERT(0);
+ }
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ goto alloc_rnode;
+ }
+ }
+
+found_rnode:
+ csio_ln_dbg(ln, "found rnode:%p ssni:x%x name(wwpn):%llx\n",
+ rn, rdev_flowid, wwn_to_u64(rdevp->wwpn));
+
+ /* Update flowid */
+ csio_rn_flowid(rn) = rdev_flowid;
+
+ /* update rdev entry */
+ rn->rdev_entry = rdevp;
+ CSIO_INC_STATS(ln, n_rnode_match);
+ return rn;
+
+alloc_rnode:
+ rn = csio_get_rnode(ln, rdev_flowid);
+ if (!rn)
+ return NULL;
+
+ csio_ln_dbg(ln, "alloc rnode:%p ssni:x%x name(wwpn):%llx\n",
+ rn, rdev_flowid, wwn_to_u64(rdevp->wwpn));
+
+ /* update rdev entry */
+ rn->rdev_entry = rdevp;
+ return rn;
+}
+
+/*
+ * csio_rn_verify_rparams - verify rparams.
+ * @ln: lnode
+ * @rn: rnode
+ * @rdevp: remote device params
+ * returns success if rparams are verified.
+ */
+static int
+csio_rn_verify_rparams(struct csio_lnode *ln, struct csio_rnode *rn,
+ struct fcoe_rdev_entry *rdevp)
+{
+ uint8_t null[8];
+ uint8_t rport_type;
+ uint8_t fc_class;
+ __be32 *did;
+
+ did = (__be32 *) &rdevp->r_id[0];
+ rport_type =
+ FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type);
+ switch (rport_type) {
+ case FLOGI_VFPORT:
+ rn->role = CSIO_RNFR_FABRIC;
+ if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_FLOGI) {
+ csio_ln_err(ln, "ssni:x%x invalid fabric portid\n",
+ csio_rn_flowid(rn));
+ return -EINVAL;
+ }
+ /* NPIV support */
+ if (FW_RDEV_WR_NPIV_GET(rdevp->vft_to_qos))
+ ln->flags |= CSIO_LNF_NPIVSUPP;
+
+ break;
+
+ case NS_VNPORT:
+ rn->role = CSIO_RNFR_NS;
+ if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_DIR_SERV) {
+ csio_ln_err(ln, "ssni:x%x invalid fabric portid\n",
+ csio_rn_flowid(rn));
+ return -EINVAL;
+ }
+ break;
+
+ case REG_FC4_VNPORT:
+ case REG_VNPORT:
+ rn->role = CSIO_RNFR_NPORT;
+ if (rdevp->event_cause == PRLI_ACC_RCVD ||
+ rdevp->event_cause == PRLI_RCVD) {
+ if (FW_RDEV_WR_TASK_RETRY_ID_GET(
+ rdevp->enh_disc_to_tgt))
+ rn->fcp_flags |= FCP_SPPF_OVLY_ALLOW;
+
+ if (FW_RDEV_WR_RETRY_GET(rdevp->enh_disc_to_tgt))
+ rn->fcp_flags |= FCP_SPPF_RETRY;
+
+ if (FW_RDEV_WR_CONF_CMPL_GET(rdevp->enh_disc_to_tgt))
+ rn->fcp_flags |= FCP_SPPF_CONF_COMPL;
+
+ if (FW_RDEV_WR_TGT_GET(rdevp->enh_disc_to_tgt))
+ rn->role |= CSIO_RNFR_TARGET;
+
+ if (FW_RDEV_WR_INI_GET(rdevp->enh_disc_to_tgt))
+ rn->role |= CSIO_RNFR_INITIATOR;
+ }
+
+ break;
+
+ case FDMI_VNPORT:
+ case FAB_CTLR_VNPORT:
+ rn->role = 0;
+ break;
+
+ default:
+ csio_ln_err(ln, "ssni:x%x invalid rport type recv x%x\n",
+ csio_rn_flowid(rn), rport_type);
+ return -EINVAL;
+ }
+
+ /* validate wwpn/wwnn for Name server/remote port */
+ if (rport_type == REG_VNPORT || rport_type == NS_VNPORT) {
+ memset(null, 0, 8);
+ if (!memcmp(rdevp->wwnn, null, 8)) {
+ csio_ln_err(ln,
+ "ssni:x%x invalid wwnn received from"
+ " rport did:x%x\n",
+ csio_rn_flowid(rn),
+ (ntohl(*did) & CSIO_DID_MASK));
+ return -EINVAL;
+ }
+
+ if (!memcmp(rdevp->wwpn, null, 8)) {
+ csio_ln_err(ln,
+ "ssni:x%x invalid wwpn received from"
+ " rport did:x%x\n",
+ csio_rn_flowid(rn),
+ (ntohl(*did) & CSIO_DID_MASK));
+ return -EINVAL;
+ }
+
+ }
+
+ /* Copy wwnn, wwpn and nport id */
+ rn->nport_id = (ntohl(*did) >> 8) & CSIO_DID_MASK;
+ memcpy(csio_rn_wwnn(rn), rdevp->wwnn, 8);
+ memcpy(csio_rn_wwpn(rn), rdevp->wwpn, 8);
+ rn->rn_sparm.csp.sp_bb_data = rdevp->rcv_fr_sz;
+ fc_class = FW_RDEV_WR_CLASS_GET(rdevp->vft_to_qos);
+ rn->rn_sparm.clsp[fc_class - 1].cp_class = htons(FC_CPC_VALID);
+
+ return 0;
+}
+
+static void
+__csio_reg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_unlock_irq(&hw->lock);
+ csio_reg_rnode(rn);
+ spin_lock_irq(&hw->lock);
+
+ if (rn->role & CSIO_RNFR_TARGET)
+ ln->n_scsi_tgts++;
+
+ if (rn->nport_id == FC_FID_MGMT_SERV)
+ csio_ln_fdmi_start(ln, (void *) rn);
+}
+
+static void
+__csio_unreg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ LIST_HEAD(tmp_q);
+ int cmpl = 0;
+
+ if (!list_empty(&rn->host_cmpl_q)) {
+ csio_dbg(hw, "Returning completion queue I/Os\n");
+ list_splice_tail_init(&rn->host_cmpl_q, &tmp_q);
+ cmpl = 1;
+ }
+
+ if (rn->role & CSIO_RNFR_TARGET) {
+ ln->n_scsi_tgts--;
+ ln->last_scan_ntgts--;
+ }
+
+ spin_unlock_irq(&hw->lock);
+ csio_unreg_rnode(rn);
+ spin_lock_irq(&hw->lock);
+
+ /* Cleanup I/Os that were waiting for rnode to unregister */
+ if (cmpl)
+ csio_scsi_cleanup_io_q(csio_hw_to_scsim(hw), &tmp_q);
+
+}
+
+/*****************************************************************************/
+/* START: Rnode SM */
+/*****************************************************************************/
+
+/*
+ * csio_rns_uninit -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_uninit(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret) {
+ csio_set_state(&rn->sm, csio_rns_ready);
+ __csio_reg_rnode(rn);
+ } else {
+ CSIO_INC_STATS(rn, n_err_inval);
+ }
+ break;
+ case CSIO_RNFE_LOGO_RECV:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv "
+ "in rn state[uninit]\n", csio_rn_flowid(rn), evt);
+ CSIO_INC_STATS(rn, n_evt_drop);
+ break;
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv "
+ "in rn state[uninit]\n", csio_rn_flowid(rn), evt);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_rns_ready -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_ready(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv from did:x%x "
+ "in rn state[ready]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_drop);
+ break;
+
+ case CSIO_RNFE_PRLI_DONE:
+ case CSIO_RNFE_PRLI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret)
+ __csio_reg_rnode(rn);
+ else
+ CSIO_INC_STATS(rn, n_err_inval);
+
+ break;
+ case CSIO_RNFE_DOWN:
+ csio_set_state(&rn->sm, csio_rns_offline);
+ __csio_unreg_rnode(rn);
+
+ /* FW expected to internally aborted outstanding SCSI WRs
+ * and return all SCSI WRs to host with status "ABORTED".
+ */
+ break;
+
+ case CSIO_RNFE_LOGO_RECV:
+ csio_set_state(&rn->sm, csio_rns_offline);
+
+ __csio_unreg_rnode(rn);
+
+ /* FW expected to internally aborted outstanding SCSI WRs
+ * and return all SCSI WRs to host with status "ABORTED".
+ */
+ break;
+
+ case CSIO_RNFE_CLOSE:
+ /*
+ * Each rnode receives CLOSE event when driver is removed or
+ * device is reset
+ * Note: All outstanding IOs on remote port need to returned
+ * to uppper layer with appropriate error before sending
+ * CLOSE event
+ */
+ csio_set_state(&rn->sm, csio_rns_uninit);
+ __csio_unreg_rnode(rn);
+ break;
+
+ case CSIO_RNFE_NAME_MISSING:
+ csio_set_state(&rn->sm, csio_rns_disappeared);
+ __csio_unreg_rnode(rn);
+
+ /*
+ * FW expected to internally aborted outstanding SCSI WRs
+ * and return all SCSI WRs to host with status "ABORTED".
+ */
+
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv from did:x%x "
+ "in rn state[uninit]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_rns_offline -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_offline(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret) {
+ csio_set_state(&rn->sm, csio_rns_ready);
+ __csio_reg_rnode(rn);
+ } else {
+ CSIO_INC_STATS(rn, n_err_inval);
+ csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
+ }
+ break;
+
+ case CSIO_RNFE_DOWN:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv from did:x%x "
+ "in rn state[offline]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_drop);
+ break;
+
+ case CSIO_RNFE_CLOSE:
+ /* Each rnode receives CLOSE event when driver is removed or
+ * device is reset
+ * Note: All outstanding IOs on remote port need to returned
+ * to uppper layer with appropriate error before sending
+ * CLOSE event
+ */
+ csio_set_state(&rn->sm, csio_rns_uninit);
+ break;
+
+ case CSIO_RNFE_NAME_MISSING:
+ csio_set_state(&rn->sm, csio_rns_disappeared);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv from did:x%x "
+ "in rn state[offline]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_rns_disappeared -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_disappeared(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret) {
+ csio_set_state(&rn->sm, csio_rns_ready);
+ __csio_reg_rnode(rn);
+ } else {
+ CSIO_INC_STATS(rn, n_err_inval);
+ csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
+ }
+ break;
+
+ case CSIO_RNFE_CLOSE:
+ /* Each rnode receives CLOSE event when driver is removed or
+ * device is reset.
+ * Note: All outstanding IOs on remote port need to returned
+ * to uppper layer with appropriate error before sending
+ * CLOSE event
+ */
+ csio_set_state(&rn->sm, csio_rns_uninit);
+ break;
+
+ case CSIO_RNFE_DOWN:
+ case CSIO_RNFE_NAME_MISSING:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv from did x%x"
+ "in rn state[disappeared]\n", csio_rn_flowid(rn),
+ evt, rn->nport_id);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv from did x%x"
+ "in rn state[disappeared]\n", csio_rn_flowid(rn),
+ evt, rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* END: Rnode SM */
+/*****************************************************************************/
+
+/*
+ * csio_rnode_devloss_handler - Device loss event handler
+ * @rn: rnode
+ *
+ * Post event to close rnode SM and free rnode.
+ */
+void
+csio_rnode_devloss_handler(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+
+ /* ignore if same rnode came back as online */
+ if (csio_is_rnode_ready(rn))
+ return;
+
+ csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
+
+ /* Free rn if in uninit state */
+ if (csio_is_rnode_uninit(rn))
+ csio_put_rnode(ln, rn);
+}
+
+/**
+ * csio_rnode_fwevt_handler - Event handler for firmware rnode events.
+ * @rn: rnode
+ * @fwevt: firmware event to handle
+ */
+void
+csio_rnode_fwevt_handler(struct csio_rnode *rn, uint8_t fwevt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ enum csio_rn_ev evt;
+
+ evt = CSIO_FWE_TO_RNFE(fwevt);
+ if (!evt) {
+ csio_ln_err(ln, "ssni:x%x Unhandled FW Rdev event: %d\n",
+ csio_rn_flowid(rn), fwevt);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ return;
+ }
+ CSIO_INC_STATS(rn, n_evt_fw[fwevt]);
+
+ /* Track previous & current events for debugging */
+ rn->prev_evt = rn->cur_evt;
+ rn->cur_evt = fwevt;
+
+ /* Post event to rnode SM */
+ csio_post_event(&rn->sm, evt);
+
+ /* Free rn if in uninit state */
+ if (csio_is_rnode_uninit(rn))
+ csio_put_rnode(ln, rn);
+}
+
+/*
+ * csio_rnode_init - Initialize rnode.
+ * @rn: RNode
+ * @ln: Associated lnode
+ *
+ * Caller is responsible for holding the lock. The lock is required
+ * to be held for inserting the rnode in ln->rnhead list.
+ */
+static int
+csio_rnode_init(struct csio_rnode *rn, struct csio_lnode *ln)
+{
+ csio_rnode_to_lnode(rn) = ln;
+ csio_init_state(&rn->sm, csio_rns_uninit);
+ INIT_LIST_HEAD(&rn->host_cmpl_q);
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+
+ /* Add rnode to list of lnodes->rnhead */
+ list_add_tail(&rn->sm.sm_list, &ln->rnhead);
+
+ return 0;
+}
+
+static void
+csio_rnode_exit(struct csio_rnode *rn)
+{
+ list_del_init(&rn->sm.sm_list);
+ CSIO_DB_ASSERT(list_empty(&rn->host_cmpl_q));
+}
diff --git a/drivers/scsi/csiostor/csio_rnode.h b/drivers/scsi/csiostor/csio_rnode.h
new file mode 100644
index 000000000000..433434221222
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_rnode.h
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_RNODE_H__
+#define __CSIO_RNODE_H__
+
+#include "csio_defs.h"
+
+/* State machine evets */
+enum csio_rn_ev {
+ CSIO_RNFE_NONE = (uint32_t)0, /* None */
+ CSIO_RNFE_LOGGED_IN, /* [N/F]Port login
+ * complete.
+ */
+ CSIO_RNFE_PRLI_DONE, /* PRLI completed */
+ CSIO_RNFE_PLOGI_RECV, /* Received PLOGI */
+ CSIO_RNFE_PRLI_RECV, /* Received PLOGI */
+ CSIO_RNFE_LOGO_RECV, /* Received LOGO */
+ CSIO_RNFE_PRLO_RECV, /* Received PRLO */
+ CSIO_RNFE_DOWN, /* Rnode is down */
+ CSIO_RNFE_CLOSE, /* Close rnode */
+ CSIO_RNFE_NAME_MISSING, /* Rnode name missing
+ * in name server.
+ */
+ CSIO_RNFE_MAX_EVENT,
+};
+
+/* rnode stats */
+struct csio_rnode_stats {
+ uint32_t n_err; /* error */
+ uint32_t n_err_inval; /* invalid parameter */
+ uint32_t n_err_nomem; /* error nomem */
+ uint32_t n_evt_unexp; /* unexpected event */
+ uint32_t n_evt_drop; /* unexpected event */
+ uint32_t n_evt_fw[PROTO_ERR_IMPL_LOGO + 1]; /* fw events */
+ enum csio_rn_ev n_evt_sm[CSIO_RNFE_MAX_EVENT]; /* State m/c events */
+ uint32_t n_lun_rst; /* Number of resets of
+ * of LUNs under this
+ * target
+ */
+ uint32_t n_lun_rst_fail; /* Number of LUN reset
+ * failures.
+ */
+ uint32_t n_tgt_rst; /* Number of target resets */
+ uint32_t n_tgt_rst_fail; /* Number of target reset
+ * failures.
+ */
+};
+
+/* Defines for rnode role */
+#define CSIO_RNFR_INITIATOR 0x1
+#define CSIO_RNFR_TARGET 0x2
+#define CSIO_RNFR_FABRIC 0x4
+#define CSIO_RNFR_NS 0x8
+#define CSIO_RNFR_NPORT 0x10
+
+struct csio_rnode {
+ struct csio_sm sm; /* State machine -
+ * should be the
+ * 1st member
+ */
+ struct csio_lnode *lnp; /* Pointer to owning
+ * Lnode */
+ uint32_t flowid; /* Firmware ID */
+ struct list_head host_cmpl_q; /* SCSI IOs
+ * pending to completed
+ * to Mid-layer.
+ */
+ /* FC identifiers for remote node */
+ uint32_t nport_id;
+ uint16_t fcp_flags; /* FCP Flags */
+ uint8_t cur_evt; /* Current event */
+ uint8_t prev_evt; /* Previous event */
+ uint32_t role; /* Fabric/Target/
+ * Initiator/NS
+ */
+ struct fcoe_rdev_entry *rdev_entry; /* Rdev entry */
+ struct csio_service_parms rn_sparm;
+
+ /* FC transport attributes */
+ struct fc_rport *rport; /* FC transport rport */
+ uint32_t supp_classes; /* Supported FC classes */
+ uint32_t maxframe_size; /* Max Frame size */
+ uint32_t scsi_id; /* Transport given SCSI id */
+
+ struct csio_rnode_stats stats; /* Common rnode stats */
+};
+
+#define csio_rn_flowid(rn) ((rn)->flowid)
+#define csio_rn_wwpn(rn) ((rn)->rn_sparm.wwpn)
+#define csio_rn_wwnn(rn) ((rn)->rn_sparm.wwnn)
+#define csio_rnode_to_lnode(rn) ((rn)->lnp)
+
+int csio_is_rnode_ready(struct csio_rnode *rn);
+void csio_rnode_state_to_str(struct csio_rnode *rn, int8_t *str);
+
+struct csio_rnode *csio_rnode_lookup_portid(struct csio_lnode *, uint32_t);
+struct csio_rnode *csio_confirm_rnode(struct csio_lnode *,
+ uint32_t, struct fcoe_rdev_entry *);
+
+void csio_rnode_fwevt_handler(struct csio_rnode *rn, uint8_t fwevt);
+
+void csio_put_rnode(struct csio_lnode *ln, struct csio_rnode *rn);
+
+void csio_reg_rnode(struct csio_rnode *);
+void csio_unreg_rnode(struct csio_rnode *);
+
+void csio_rnode_devloss_handler(struct csio_rnode *);
+
+#endif /* ifndef __CSIO_RNODE_H__ */
diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c
new file mode 100644
index 000000000000..34bde6650fae
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_scsi.c
@@ -0,0 +1,2529 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/compiler.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+#include <asm/page.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+#include "csio_scsi.h"
+#include "csio_init.h"
+
+int csio_scsi_eqsize = 65536;
+int csio_scsi_iqlen = 128;
+int csio_scsi_ioreqs = 2048;
+uint32_t csio_max_scan_tmo;
+uint32_t csio_delta_scan_tmo = 5;
+int csio_lun_qdepth = 32;
+
+static int csio_ddp_descs = 128;
+
+static int csio_do_abrt_cls(struct csio_hw *,
+ struct csio_ioreq *, bool);
+
+static void csio_scsis_uninit(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_io_active(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_tm_active(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_aborting(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_closing(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_shost_cmpl_await(struct csio_ioreq *, enum csio_scsi_ev);
+
+/*
+ * csio_scsi_match_io - Match an ioreq with the given SCSI level data.
+ * @ioreq: The I/O request
+ * @sld: Level information
+ *
+ * Should be called with lock held.
+ *
+ */
+static bool
+csio_scsi_match_io(struct csio_ioreq *ioreq, struct csio_scsi_level_data *sld)
+{
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(ioreq);
+
+ switch (sld->level) {
+ case CSIO_LEV_LUN:
+ if (scmnd == NULL)
+ return false;
+
+ return ((ioreq->lnode == sld->lnode) &&
+ (ioreq->rnode == sld->rnode) &&
+ ((uint64_t)scmnd->device->lun == sld->oslun));
+
+ case CSIO_LEV_RNODE:
+ return ((ioreq->lnode == sld->lnode) &&
+ (ioreq->rnode == sld->rnode));
+ case CSIO_LEV_LNODE:
+ return (ioreq->lnode == sld->lnode);
+ case CSIO_LEV_ALL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * csio_scsi_gather_active_ios - Gather active I/Os based on level
+ * @scm: SCSI module
+ * @sld: Level information
+ * @dest: The queue where these I/Os have to be gathered.
+ *
+ * Should be called with lock held.
+ */
+static void
+csio_scsi_gather_active_ios(struct csio_scsim *scm,
+ struct csio_scsi_level_data *sld,
+ struct list_head *dest)
+{
+ struct list_head *tmp, *next;
+
+ if (list_empty(&scm->active_q))
+ return;
+
+ /* Just splice the entire active_q into dest */
+ if (sld->level == CSIO_LEV_ALL) {
+ list_splice_tail_init(&scm->active_q, dest);
+ return;
+ }
+
+ list_for_each_safe(tmp, next, &scm->active_q) {
+ if (csio_scsi_match_io((struct csio_ioreq *)tmp, sld)) {
+ list_del_init(tmp);
+ list_add_tail(tmp, dest);
+ }
+ }
+}
+
+static inline bool
+csio_scsi_itnexus_loss_error(uint16_t error)
+{
+ switch (error) {
+ case FW_ERR_LINK_DOWN:
+ case FW_RDEV_NOT_READY:
+ case FW_ERR_RDEV_LOST:
+ case FW_ERR_RDEV_LOGO:
+ case FW_ERR_RDEV_IMPL_LOGO:
+ return true;
+ }
+ return false;
+}
+
+/*
+ * csio_scsi_fcp_cmnd - Frame the SCSI FCP command paylod.
+ * @req: IO req structure.
+ * @addr: DMA location to place the payload.
+ *
+ * This routine is shared between FCP_WRITE, FCP_READ and FCP_CMD requests.
+ */
+static inline void
+csio_scsi_fcp_cmnd(struct csio_ioreq *req, void *addr)
+{
+ struct fcp_cmnd *fcp_cmnd = (struct fcp_cmnd *)addr;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ /* Check for Task Management */
+ if (likely(csio_priv(scmnd)->fc_tm_flags == 0)) {
+ int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun);
+ fcp_cmnd->fc_tm_flags = 0;
+ fcp_cmnd->fc_cmdref = 0;
+
+ memcpy(fcp_cmnd->fc_cdb, scmnd->cmnd, 16);
+ fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
+ fcp_cmnd->fc_dl = cpu_to_be32(scsi_bufflen(scmnd));
+
+ if (req->nsge)
+ if (req->datadir == DMA_TO_DEVICE)
+ fcp_cmnd->fc_flags = FCP_CFL_WRDATA;
+ else
+ fcp_cmnd->fc_flags = FCP_CFL_RDDATA;
+ else
+ fcp_cmnd->fc_flags = 0;
+ } else {
+ memset(fcp_cmnd, 0, sizeof(*fcp_cmnd));
+ int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun);
+ fcp_cmnd->fc_tm_flags = csio_priv(scmnd)->fc_tm_flags;
+ }
+}
+
+/*
+ * csio_scsi_init_cmd_wr - Initialize the SCSI CMD WR.
+ * @req: IO req structure.
+ * @addr: DMA location to place the payload.
+ * @size: Size of WR (including FW WR + immed data + rsp SG entry
+ *
+ * Wrapper for populating fw_scsi_cmd_wr.
+ */
+static inline void
+csio_scsi_init_cmd_wr(struct csio_ioreq *req, void *addr, uint32_t size)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_cmd_wr *wr = (struct fw_scsi_cmd_wr *)addr;
+ struct csio_dma_buf *dma_buf;
+ uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_CMD_WR) |
+ FW_SCSI_CMD_WR_IMMDLEN(imm));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
+ FW_WR_LEN16_V(
+ DIV_ROUND_UP(size, 16)));
+
+ wr->cookie = (uintptr_t) req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t) req->tmo;
+ wr->r3 = 0;
+ memset(&wr->r5, 0, 8);
+
+ /* Get RSP DMA buffer */
+ dma_buf = &req->dma_buf;
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
+ wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
+
+ wr->r6 = 0;
+
+ wr->u.fcoe.ctl_pri = 0;
+ wr->u.fcoe.cp_en_class = 0;
+ wr->u.fcoe.r4_lo[0] = 0;
+ wr->u.fcoe.r4_lo[1] = 0;
+
+ /* Frame a FCP command */
+ csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)addr +
+ sizeof(struct fw_scsi_cmd_wr)));
+}
+
+#define CSIO_SCSI_CMD_WR_SZ(_imm) \
+ (sizeof(struct fw_scsi_cmd_wr) + /* WR size */ \
+ ALIGN((_imm), 16)) /* Immed data */
+
+#define CSIO_SCSI_CMD_WR_SZ_16(_imm) \
+ (ALIGN(CSIO_SCSI_CMD_WR_SZ((_imm)), 16))
+
+/*
+ * csio_scsi_cmd - Create a SCSI CMD WR.
+ * @req: IO req structure.
+ *
+ * Gets a WR slot in the ingress queue and initializes it with SCSI CMD WR.
+ *
+ */
+static inline void
+csio_scsi_cmd(struct csio_ioreq *req)
+{
+ struct csio_wr_pair wrp;
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ uint32_t size = CSIO_SCSI_CMD_WR_SZ_16(scsim->proto_cmd_len);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (unlikely(req->drv_status != 0))
+ return;
+
+ if (wrp.size1 >= size) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_cmd_wr(req, wrp.addr1, size);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_cmd_wr(req, (void *)tmpwr, size);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+}
+
+/*
+ * csio_scsi_init_ulptx_dsgl - Fill in a ULP_TX_SC_DSGL
+ * @hw: HW module
+ * @req: IO request
+ * @sgl: ULP TX SGL pointer.
+ *
+ */
+static inline void
+csio_scsi_init_ultptx_dsgl(struct csio_hw *hw, struct csio_ioreq *req,
+ struct ulptx_sgl *sgl)
+{
+ struct ulptx_sge_pair *sge_pair = NULL;
+ struct scatterlist *sgel;
+ uint32_t i = 0;
+ uint32_t xfer_len;
+ struct list_head *tmp;
+ struct csio_dma_buf *dma_buf;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_MORE_F |
+ ULPTX_NSGE_V(req->nsge));
+ /* Now add the data SGLs */
+ if (likely(!req->dcopy)) {
+ scsi_for_each_sg(scmnd, sgel, req->nsge, i) {
+ if (i == 0) {
+ sgl->addr0 = cpu_to_be64(sg_dma_address(sgel));
+ sgl->len0 = cpu_to_be32(sg_dma_len(sgel));
+ sge_pair = (struct ulptx_sge_pair *)(sgl + 1);
+ continue;
+ }
+ if ((i - 1) & 0x1) {
+ sge_pair->addr[1] = cpu_to_be64(
+ sg_dma_address(sgel));
+ sge_pair->len[1] = cpu_to_be32(
+ sg_dma_len(sgel));
+ sge_pair++;
+ } else {
+ sge_pair->addr[0] = cpu_to_be64(
+ sg_dma_address(sgel));
+ sge_pair->len[0] = cpu_to_be32(
+ sg_dma_len(sgel));
+ }
+ }
+ } else {
+ /* Program sg elements with driver's DDP buffer */
+ xfer_len = scsi_bufflen(scmnd);
+ list_for_each(tmp, &req->gen_list) {
+ dma_buf = (struct csio_dma_buf *)tmp;
+ if (i == 0) {
+ sgl->addr0 = cpu_to_be64(dma_buf->paddr);
+ sgl->len0 = cpu_to_be32(
+ min(xfer_len, dma_buf->len));
+ sge_pair = (struct ulptx_sge_pair *)(sgl + 1);
+ } else if ((i - 1) & 0x1) {
+ sge_pair->addr[1] = cpu_to_be64(dma_buf->paddr);
+ sge_pair->len[1] = cpu_to_be32(
+ min(xfer_len, dma_buf->len));
+ sge_pair++;
+ } else {
+ sge_pair->addr[0] = cpu_to_be64(dma_buf->paddr);
+ sge_pair->len[0] = cpu_to_be32(
+ min(xfer_len, dma_buf->len));
+ }
+ xfer_len -= min(xfer_len, dma_buf->len);
+ i++;
+ }
+ }
+}
+
+/*
+ * csio_scsi_init_read_wr - Initialize the READ SCSI WR.
+ * @req: IO req structure.
+ * @wrp: DMA location to place the payload.
+ * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL
+ *
+ * Wrapper for populating fw_scsi_read_wr.
+ */
+static inline void
+csio_scsi_init_read_wr(struct csio_ioreq *req, void *wrp, uint32_t size)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_read_wr *wr = (struct fw_scsi_read_wr *)wrp;
+ struct ulptx_sgl *sgl;
+ struct csio_dma_buf *dma_buf;
+ uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_READ_WR) |
+ FW_SCSI_READ_WR_IMMDLEN(imm));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
+ FW_WR_LEN16_V(DIV_ROUND_UP(size, 16)));
+ wr->cookie = (uintptr_t)req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t)(req->tmo);
+ wr->use_xfer_cnt = 1;
+ wr->xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ wr->ini_xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ /* Get RSP DMA buffer */
+ dma_buf = &req->dma_buf;
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
+ wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
+
+ wr->r4 = 0;
+
+ wr->u.fcoe.ctl_pri = 0;
+ wr->u.fcoe.cp_en_class = 0;
+ wr->u.fcoe.r3_lo[0] = 0;
+ wr->u.fcoe.r3_lo[1] = 0;
+ csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_read_wr)));
+
+ /* Move WR pointer past command and immediate data */
+ sgl = (struct ulptx_sgl *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_read_wr) + ALIGN(imm, 16));
+
+ /* Fill in the DSGL */
+ csio_scsi_init_ultptx_dsgl(hw, req, sgl);
+}
+
+/*
+ * csio_scsi_init_write_wr - Initialize the WRITE SCSI WR.
+ * @req: IO req structure.
+ * @wrp: DMA location to place the payload.
+ * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL
+ *
+ * Wrapper for populating fw_scsi_write_wr.
+ */
+static inline void
+csio_scsi_init_write_wr(struct csio_ioreq *req, void *wrp, uint32_t size)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_write_wr *wr = (struct fw_scsi_write_wr *)wrp;
+ struct ulptx_sgl *sgl;
+ struct csio_dma_buf *dma_buf;
+ uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_WRITE_WR) |
+ FW_SCSI_WRITE_WR_IMMDLEN(imm));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
+ FW_WR_LEN16_V(DIV_ROUND_UP(size, 16)));
+ wr->cookie = (uintptr_t)req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t)(req->tmo);
+ wr->use_xfer_cnt = 1;
+ wr->xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ wr->ini_xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ /* Get RSP DMA buffer */
+ dma_buf = &req->dma_buf;
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
+ wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
+
+ wr->r4 = 0;
+
+ wr->u.fcoe.ctl_pri = 0;
+ wr->u.fcoe.cp_en_class = 0;
+ wr->u.fcoe.r3_lo[0] = 0;
+ wr->u.fcoe.r3_lo[1] = 0;
+ csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_write_wr)));
+
+ /* Move WR pointer past command and immediate data */
+ sgl = (struct ulptx_sgl *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_write_wr) + ALIGN(imm, 16));
+
+ /* Fill in the DSGL */
+ csio_scsi_init_ultptx_dsgl(hw, req, sgl);
+}
+
+/* Calculate WR size needed for fw_scsi_read_wr/fw_scsi_write_wr */
+#define CSIO_SCSI_DATA_WRSZ(req, oper, sz, imm) \
+do { \
+ (sz) = sizeof(struct fw_scsi_##oper##_wr) + /* WR size */ \
+ ALIGN((imm), 16) + /* Immed data */ \
+ sizeof(struct ulptx_sgl); /* ulptx_sgl */ \
+ \
+ if (unlikely((req)->nsge > 1)) \
+ (sz) += (sizeof(struct ulptx_sge_pair) * \
+ (ALIGN(((req)->nsge - 1), 2) / 2)); \
+ /* Data SGE */ \
+} while (0)
+
+/*
+ * csio_scsi_read - Create a SCSI READ WR.
+ * @req: IO req structure.
+ *
+ * Gets a WR slot in the ingress queue and initializes it with
+ * SCSI READ WR.
+ *
+ */
+static inline void
+csio_scsi_read(struct csio_ioreq *req)
+{
+ struct csio_wr_pair wrp;
+ uint32_t size;
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+
+ CSIO_SCSI_DATA_WRSZ(req, read, size, scsim->proto_cmd_len);
+ size = ALIGN(size, 16);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (likely(req->drv_status == 0)) {
+ if (likely(wrp.size1 >= size)) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_read_wr(req, wrp.addr1, size);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_read_wr(req, (void *)tmpwr, size);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+ }
+}
+
+/*
+ * csio_scsi_write - Create a SCSI WRITE WR.
+ * @req: IO req structure.
+ *
+ * Gets a WR slot in the ingress queue and initializes it with
+ * SCSI WRITE WR.
+ *
+ */
+static inline void
+csio_scsi_write(struct csio_ioreq *req)
+{
+ struct csio_wr_pair wrp;
+ uint32_t size;
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+
+ CSIO_SCSI_DATA_WRSZ(req, write, size, scsim->proto_cmd_len);
+ size = ALIGN(size, 16);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (likely(req->drv_status == 0)) {
+ if (likely(wrp.size1 >= size)) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_write_wr(req, wrp.addr1, size);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_write_wr(req, (void *)tmpwr, size);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+ }
+}
+
+/*
+ * csio_setup_ddp - Setup DDP buffers for Read request.
+ * @req: IO req structure.
+ *
+ * Checks SGLs/Data buffers are virtually contiguous required for DDP.
+ * If contiguous,driver posts SGLs in the WR otherwise post internal
+ * buffers for such request for DDP.
+ */
+static inline void
+csio_setup_ddp(struct csio_scsim *scsim, struct csio_ioreq *req)
+{
+#ifdef __CSIO_DEBUG__
+ struct csio_hw *hw = req->lnode->hwp;
+#endif
+ struct scatterlist *sgel = NULL;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+ uint64_t sg_addr = 0;
+ uint32_t ddp_pagesz = 4096;
+ uint32_t buf_off;
+ struct csio_dma_buf *dma_buf = NULL;
+ uint32_t alloc_len = 0;
+ uint32_t xfer_len = 0;
+ uint32_t sg_len = 0;
+ uint32_t i;
+
+ scsi_for_each_sg(scmnd, sgel, req->nsge, i) {
+ sg_addr = sg_dma_address(sgel);
+ sg_len = sg_dma_len(sgel);
+
+ buf_off = sg_addr & (ddp_pagesz - 1);
+
+ /* Except 1st buffer,all buffer addr have to be Page aligned */
+ if (i != 0 && buf_off) {
+ csio_dbg(hw, "SGL addr not DDP aligned (%llx:%d)\n",
+ sg_addr, sg_len);
+ goto unaligned;
+ }
+
+ /* Except last buffer,all buffer must end on page boundary */
+ if ((i != (req->nsge - 1)) &&
+ ((buf_off + sg_len) & (ddp_pagesz - 1))) {
+ csio_dbg(hw,
+ "SGL addr not ending on page boundary"
+ "(%llx:%d)\n", sg_addr, sg_len);
+ goto unaligned;
+ }
+ }
+
+ /* SGL's are virtually contiguous. HW will DDP to SGLs */
+ req->dcopy = 0;
+ csio_scsi_read(req);
+
+ return;
+
+unaligned:
+ CSIO_INC_STATS(scsim, n_unaligned);
+ /*
+ * For unaligned SGLs, driver will allocate internal DDP buffer.
+ * Once command is completed data from DDP buffer copied to SGLs
+ */
+ req->dcopy = 1;
+
+ /* Use gen_list to store the DDP buffers */
+ INIT_LIST_HEAD(&req->gen_list);
+ xfer_len = scsi_bufflen(scmnd);
+
+ i = 0;
+ /* Allocate ddp buffers for this request */
+ while (alloc_len < xfer_len) {
+ dma_buf = csio_get_scsi_ddp(scsim);
+ if (dma_buf == NULL || i > scsim->max_sge) {
+ req->drv_status = -EBUSY;
+ break;
+ }
+ alloc_len += dma_buf->len;
+ /* Added to IO req */
+ list_add_tail(&dma_buf->list, &req->gen_list);
+ i++;
+ }
+
+ if (!req->drv_status) {
+ /* set number of ddp bufs used */
+ req->nsge = i;
+ csio_scsi_read(req);
+ return;
+ }
+
+ /* release dma descs */
+ if (i > 0)
+ csio_put_scsi_ddp_list(scsim, &req->gen_list, i);
+}
+
+/*
+ * csio_scsi_init_abrt_cls_wr - Initialize an ABORT/CLOSE WR.
+ * @req: IO req structure.
+ * @addr: DMA location to place the payload.
+ * @size: Size of WR
+ * @abort: abort OR close
+ *
+ * Wrapper for populating fw_scsi_cmd_wr.
+ */
+static inline void
+csio_scsi_init_abrt_cls_wr(struct csio_ioreq *req, void *addr, uint32_t size,
+ bool abort)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_abrt_cls_wr *wr = (struct fw_scsi_abrt_cls_wr *)addr;
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_ABRT_CLS_WR));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
+ FW_WR_LEN16_V(
+ DIV_ROUND_UP(size, 16)));
+
+ wr->cookie = (uintptr_t) req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t) req->tmo;
+ /* 0 for CHK_ALL_IO tells FW to look up t_cookie */
+ wr->sub_opcode_to_chk_all_io =
+ (FW_SCSI_ABRT_CLS_WR_SUB_OPCODE(abort) |
+ FW_SCSI_ABRT_CLS_WR_CHK_ALL_IO(0));
+ wr->r3[0] = 0;
+ wr->r3[1] = 0;
+ wr->r3[2] = 0;
+ wr->r3[3] = 0;
+ /* Since we re-use the same ioreq for abort as well */
+ wr->t_cookie = (uintptr_t) req;
+}
+
+static inline void
+csio_scsi_abrt_cls(struct csio_ioreq *req, bool abort)
+{
+ struct csio_wr_pair wrp;
+ struct csio_hw *hw = req->lnode->hwp;
+ uint32_t size = ALIGN(sizeof(struct fw_scsi_abrt_cls_wr), 16);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (req->drv_status != 0)
+ return;
+
+ if (wrp.size1 >= size) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_abrt_cls_wr(req, wrp.addr1, size, abort);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_abrt_cls_wr(req, (void *)tmpwr, size, abort);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+}
+
+/*****************************************************************************/
+/* START: SCSI SM */
+/*****************************************************************************/
+static void
+csio_scsis_uninit(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_START_IO:
+
+ if (req->nsge) {
+ if (req->datadir == DMA_TO_DEVICE) {
+ req->dcopy = 0;
+ csio_scsi_write(req);
+ } else
+ csio_setup_ddp(scsim, req);
+ } else {
+ csio_scsi_cmd(req);
+ }
+
+ if (likely(req->drv_status == 0)) {
+ /* change state and enqueue on active_q */
+ csio_set_state(&req->sm, csio_scsis_io_active);
+ list_add_tail(&req->sm.sm_list, &scsim->active_q);
+ csio_wr_issue(hw, req->eq_idx, false);
+ CSIO_INC_STATS(scsim, n_active);
+
+ return;
+ }
+ break;
+
+ case CSIO_SCSIE_START_TM:
+ csio_scsi_cmd(req);
+ if (req->drv_status == 0) {
+ /*
+ * NOTE: We collect the affected I/Os prior to issuing
+ * LUN reset, and not after it. This is to prevent
+ * aborting I/Os that get issued after the LUN reset,
+ * but prior to LUN reset completion (in the event that
+ * the host stack has not blocked I/Os to a LUN that is
+ * being reset.
+ */
+ csio_set_state(&req->sm, csio_scsis_tm_active);
+ list_add_tail(&req->sm.sm_list, &scsim->active_q);
+ csio_wr_issue(hw, req->eq_idx, false);
+ CSIO_INC_STATS(scsim, n_tm_active);
+ }
+ return;
+
+ case CSIO_SCSIE_ABORT:
+ case CSIO_SCSIE_CLOSE:
+ /*
+ * NOTE:
+ * We could get here due to :
+ * - a window in the cleanup path of the SCSI module
+ * (csio_scsi_abort_io()). Please see NOTE in this function.
+ * - a window in the time we tried to issue an abort/close
+ * of a request to FW, and the FW completed the request
+ * itself.
+ * Print a message for now, and return INVAL either way.
+ */
+ req->drv_status = -EINVAL;
+ csio_warn(hw, "Trying to abort/close completed IO:%p!\n", req);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_io_active(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+ struct csio_rnode *rn;
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ CSIO_DEC_STATS(scm, n_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ /*
+ * In MSIX mode, with multiple queues, the SCSI compeltions
+ * could reach us sooner than the FW events sent to indicate
+ * I-T nexus loss (link down, remote device logo etc). We
+ * dont want to be returning such I/Os to the upper layer
+ * immediately, since we wouldnt have reported the I-T nexus
+ * loss itself. This forces us to serialize such completions
+ * with the reporting of the I-T nexus loss. Therefore, we
+ * internally queue up such up such completions in the rnode.
+ * The reporting of I-T nexus loss to the upper layer is then
+ * followed by the returning of I/Os in this internal queue.
+ * Having another state alongwith another queue helps us take
+ * actions for events such as ABORT received while we are
+ * in this rnode queue.
+ */
+ if (unlikely(req->wr_status != FW_SUCCESS)) {
+ rn = req->rnode;
+ /*
+ * FW says remote device is lost, but rnode
+ * doesn't reflect it.
+ */
+ if (csio_scsi_itnexus_loss_error(req->wr_status) &&
+ csio_is_rnode_ready(rn)) {
+ csio_set_state(&req->sm,
+ csio_scsis_shost_cmpl_await);
+ list_add_tail(&req->sm.sm_list,
+ &rn->host_cmpl_q);
+ }
+ }
+
+ break;
+
+ case CSIO_SCSIE_ABORT:
+ csio_scsi_abrt_cls(req, SCSI_ABORT);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_aborting);
+ }
+ break;
+
+ case CSIO_SCSIE_CLOSE:
+ csio_scsi_abrt_cls(req, SCSI_CLOSE);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_closing);
+ }
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_tm_active(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ CSIO_DEC_STATS(scm, n_tm_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+
+ break;
+
+ case CSIO_SCSIE_ABORT:
+ csio_scsi_abrt_cls(req, SCSI_ABORT);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_aborting);
+ }
+ break;
+
+
+ case CSIO_SCSIE_CLOSE:
+ csio_scsi_abrt_cls(req, SCSI_CLOSE);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_closing);
+ }
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_tm_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_aborting(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ csio_dbg(hw,
+ "ioreq %p recvd cmpltd (wr_status:%d) "
+ "in aborting st\n", req, req->wr_status);
+ /*
+ * Use -ECANCELED to explicitly tell the ABORTED event that
+ * the original I/O was returned to driver by FW.
+ * We dont really care if the I/O was returned with success by
+ * FW (because the ABORT and completion of the I/O crossed each
+ * other), or any other return value. Once we are in aborting
+ * state, the success or failure of the I/O is unimportant to
+ * us.
+ */
+ req->drv_status = -ECANCELED;
+ break;
+
+ case CSIO_SCSIE_ABORT:
+ CSIO_INC_STATS(scm, n_abrt_dups);
+ break;
+
+ case CSIO_SCSIE_ABORTED:
+
+ csio_dbg(hw, "abort of %p return status:0x%x drv_status:%x\n",
+ req, req->wr_status, req->drv_status);
+ /*
+ * Check if original I/O WR completed before the Abort
+ * completion.
+ */
+ if (req->drv_status != -ECANCELED) {
+ csio_warn(hw,
+ "Abort completed before original I/O,"
+ " req:%p\n", req);
+ CSIO_DB_ASSERT(0);
+ }
+
+ /*
+ * There are the following possible scenarios:
+ * 1. The abort completed successfully, FW returned FW_SUCCESS.
+ * 2. The completion of an I/O and the receipt of
+ * abort for that I/O by the FW crossed each other.
+ * The FW returned FW_EINVAL. The original I/O would have
+ * returned with FW_SUCCESS or any other SCSI error.
+ * 3. The FW couldn't sent the abort out on the wire, as there
+ * was an I-T nexus loss (link down, remote device logged
+ * out etc). FW sent back an appropriate IT nexus loss status
+ * for the abort.
+ * 4. FW sent an abort, but abort timed out (remote device
+ * didnt respond). FW replied back with
+ * FW_SCSI_ABORT_TIMEDOUT.
+ * 5. FW couldn't genuinely abort the request for some reason,
+ * and sent us an error.
+ *
+ * The first 3 scenarios are treated as succesful abort
+ * operations by the host, while the last 2 are failed attempts
+ * to abort. Manipulate the return value of the request
+ * appropriately, so that host can convey these results
+ * back to the upper layer.
+ */
+ if ((req->wr_status == FW_SUCCESS) ||
+ (req->wr_status == FW_EINVAL) ||
+ csio_scsi_itnexus_loss_error(req->wr_status))
+ req->wr_status = FW_SCSI_ABORT_REQUESTED;
+
+ CSIO_DEC_STATS(scm, n_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ case CSIO_SCSIE_CLOSE:
+ /*
+ * We can receive this event from the module
+ * cleanup paths, if the FW forgot to reply to the ABORT WR
+ * and left this ioreq in this state. For now, just ignore
+ * the event. The CLOSE event is sent to this state, as
+ * the LINK may have already gone down.
+ */
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_closing(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ csio_dbg(hw,
+ "ioreq %p recvd cmpltd (wr_status:%d) "
+ "in closing st\n", req, req->wr_status);
+ /*
+ * Use -ECANCELED to explicitly tell the CLOSED event that
+ * the original I/O was returned to driver by FW.
+ * We dont really care if the I/O was returned with success by
+ * FW (because the CLOSE and completion of the I/O crossed each
+ * other), or any other return value. Once we are in aborting
+ * state, the success or failure of the I/O is unimportant to
+ * us.
+ */
+ req->drv_status = -ECANCELED;
+ break;
+
+ case CSIO_SCSIE_CLOSED:
+ /*
+ * Check if original I/O WR completed before the Close
+ * completion.
+ */
+ if (req->drv_status != -ECANCELED) {
+ csio_fatal(hw,
+ "Close completed before original I/O,"
+ " req:%p\n", req);
+ CSIO_DB_ASSERT(0);
+ }
+
+ /*
+ * Either close succeeded, or we issued close to FW at the
+ * same time FW compelted it to us. Either way, the I/O
+ * is closed.
+ */
+ CSIO_DB_ASSERT((req->wr_status == FW_SUCCESS) ||
+ (req->wr_status == FW_EINVAL));
+ req->wr_status = FW_SCSI_CLOSE_REQUESTED;
+
+ CSIO_DEC_STATS(scm, n_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ case CSIO_SCSIE_CLOSE:
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_shost_cmpl_await(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ switch (evt) {
+ case CSIO_SCSIE_ABORT:
+ case CSIO_SCSIE_CLOSE:
+ /*
+ * Just succeed the abort request, and hope that
+ * the remote device unregister path will cleanup
+ * this I/O to the upper layer within a sane
+ * amount of time.
+ */
+ /*
+ * A close can come in during a LINK DOWN. The FW would have
+ * returned us the I/O back, but not the remote device lost
+ * FW event. In this interval, if the I/O times out at the upper
+ * layer, a close can come in. Take the same action as abort:
+ * return success, and hope that the remote device unregister
+ * path will cleanup this I/O. If the FW still doesnt send
+ * the msg, the close times out, and the upper layer resorts
+ * to the next level of error recovery.
+ */
+ req->drv_status = 0;
+ break;
+ case CSIO_SCSIE_DRVCLEANUP:
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+ default:
+ csio_dbg(req->lnode->hwp, "Unhandled event:%d sent to req:%p\n",
+ evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+/*
+ * csio_scsi_cmpl_handler - WR completion handler for SCSI.
+ * @hw: HW module.
+ * @wr: The completed WR from the ingress queue.
+ * @len: Length of the WR.
+ * @flb: Freelist buffer array.
+ * @priv: Private object
+ * @scsiwr: Pointer to SCSI WR.
+ *
+ * This is the WR completion handler called per completion from the
+ * ISR. It is called with lock held. It walks past the RSS and CPL message
+ * header where the actual WR is present.
+ * It then gets the status, WR handle (ioreq pointer) and the len of
+ * the WR, based on WR opcode. Only on a non-good status is the entire
+ * WR copied into the WR cache (ioreq->fw_wr).
+ * The ioreq corresponding to the WR is returned to the caller.
+ * NOTE: The SCSI queue doesnt allocate a freelist today, hence
+ * no freelist buffer is expected.
+ */
+struct csio_ioreq *
+csio_scsi_cmpl_handler(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv, uint8_t **scsiwr)
+{
+ struct csio_ioreq *ioreq = NULL;
+ struct cpl_fw6_msg *cpl;
+ uint8_t *tempwr;
+ uint8_t status;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ /* skip RSS header */
+ cpl = (struct cpl_fw6_msg *)((uintptr_t)wr + sizeof(__be64));
+
+ if (unlikely(cpl->opcode != CPL_FW6_MSG)) {
+ csio_warn(hw, "Error: Invalid CPL msg %x recvd on SCSI q\n",
+ cpl->opcode);
+ CSIO_INC_STATS(scm, n_inval_cplop);
+ return NULL;
+ }
+
+ tempwr = (uint8_t *)(cpl->data);
+ status = csio_wr_status(tempwr);
+ *scsiwr = tempwr;
+
+ if (likely((*tempwr == FW_SCSI_READ_WR) ||
+ (*tempwr == FW_SCSI_WRITE_WR) ||
+ (*tempwr == FW_SCSI_CMD_WR))) {
+ ioreq = (struct csio_ioreq *)((uintptr_t)
+ (((struct fw_scsi_read_wr *)tempwr)->cookie));
+ CSIO_DB_ASSERT(virt_addr_valid(ioreq));
+
+ ioreq->wr_status = status;
+
+ return ioreq;
+ }
+
+ if (*tempwr == FW_SCSI_ABRT_CLS_WR) {
+ ioreq = (struct csio_ioreq *)((uintptr_t)
+ (((struct fw_scsi_abrt_cls_wr *)tempwr)->cookie));
+ CSIO_DB_ASSERT(virt_addr_valid(ioreq));
+
+ ioreq->wr_status = status;
+ return ioreq;
+ }
+
+ csio_warn(hw, "WR with invalid opcode in SCSI IQ: %x\n", *tempwr);
+ CSIO_INC_STATS(scm, n_inval_scsiop);
+ return NULL;
+}
+
+/*
+ * csio_scsi_cleanup_io_q - Cleanup the given queue.
+ * @scm: SCSI module.
+ * @q: Queue to be cleaned up.
+ *
+ * Called with lock held. Has to exit with lock held.
+ */
+void
+csio_scsi_cleanup_io_q(struct csio_scsim *scm, struct list_head *q)
+{
+ struct csio_hw *hw = scm->hw;
+ struct csio_ioreq *ioreq;
+ struct list_head *tmp, *next;
+ struct scsi_cmnd *scmnd;
+
+ /* Call back the completion routines of the active_q */
+ list_for_each_safe(tmp, next, q) {
+ ioreq = (struct csio_ioreq *)tmp;
+ csio_scsi_drvcleanup(ioreq);
+ list_del_init(&ioreq->sm.sm_list);
+ scmnd = csio_scsi_cmnd(ioreq);
+ spin_unlock_irq(&hw->lock);
+
+ /*
+ * Upper layers may have cleared this command, hence this
+ * check to avoid accessing stale references.
+ */
+ if (scmnd != NULL)
+ ioreq->io_cbfn(hw, ioreq);
+
+ spin_lock_irq(&scm->freelist_lock);
+ csio_put_scsi_ioreq(scm, ioreq);
+ spin_unlock_irq(&scm->freelist_lock);
+
+ spin_lock_irq(&hw->lock);
+ }
+}
+
+#define CSIO_SCSI_ABORT_Q_POLL_MS 2000
+
+static void
+csio_abrt_cls(struct csio_ioreq *ioreq, struct scsi_cmnd *scmnd)
+{
+ struct csio_lnode *ln = ioreq->lnode;
+ struct csio_hw *hw = ln->hwp;
+ int ready = 0;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ int rv;
+
+ if (csio_scsi_cmnd(ioreq) != scmnd) {
+ CSIO_INC_STATS(scsim, n_abrt_race_comp);
+ return;
+ }
+
+ ready = csio_is_lnode_ready(ln);
+
+ rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE));
+ if (rv != 0) {
+ if (ready)
+ CSIO_INC_STATS(scsim, n_abrt_busy_error);
+ else
+ CSIO_INC_STATS(scsim, n_cls_busy_error);
+ }
+}
+
+/*
+ * csio_scsi_abort_io_q - Abort all I/Os on given queue
+ * @scm: SCSI module.
+ * @q: Queue to abort.
+ * @tmo: Timeout in ms
+ *
+ * Attempt to abort all I/Os on given queue, and wait for a max
+ * of tmo milliseconds for them to complete. Returns success
+ * if all I/Os are aborted. Else returns -ETIMEDOUT.
+ * Should be entered with lock held. Exits with lock held.
+ * NOTE:
+ * Lock has to be held across the loop that aborts I/Os, since dropping the lock
+ * in between can cause the list to be corrupted. As a result, the caller
+ * of this function has to ensure that the number of I/os to be aborted
+ * is finite enough to not cause lock-held-for-too-long issues.
+ */
+static int
+csio_scsi_abort_io_q(struct csio_scsim *scm, struct list_head *q, uint32_t tmo)
+{
+ struct csio_hw *hw = scm->hw;
+ struct list_head *tmp, *next;
+ int count = DIV_ROUND_UP(tmo, CSIO_SCSI_ABORT_Q_POLL_MS);
+ struct scsi_cmnd *scmnd;
+
+ if (list_empty(q))
+ return 0;
+
+ csio_dbg(hw, "Aborting SCSI I/Os\n");
+
+ /* Now abort/close I/Os in the queue passed */
+ list_for_each_safe(tmp, next, q) {
+ scmnd = csio_scsi_cmnd((struct csio_ioreq *)tmp);
+ csio_abrt_cls((struct csio_ioreq *)tmp, scmnd);
+ }
+
+ /* Wait till all active I/Os are completed/aborted/closed */
+ while (!list_empty(q) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* all aborts completed */
+ if (list_empty(q))
+ return 0;
+
+ return -ETIMEDOUT;
+}
+
+/*
+ * csio_scsim_cleanup_io - Cleanup all I/Os in SCSI module.
+ * @scm: SCSI module.
+ * @abort: abort required.
+ * Called with lock held, should exit with lock held.
+ * Can sleep when waiting for I/Os to complete.
+ */
+int
+csio_scsim_cleanup_io(struct csio_scsim *scm, bool abort)
+{
+ struct csio_hw *hw = scm->hw;
+ int rv = 0;
+ int count = DIV_ROUND_UP(60 * 1000, CSIO_SCSI_ABORT_Q_POLL_MS);
+
+ /* No I/Os pending */
+ if (list_empty(&scm->active_q))
+ return 0;
+
+ /* Wait until all active I/Os are completed */
+ while (!list_empty(&scm->active_q) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* all I/Os completed */
+ if (list_empty(&scm->active_q))
+ return 0;
+
+ /* Else abort */
+ if (abort) {
+ rv = csio_scsi_abort_io_q(scm, &scm->active_q, 30000);
+ if (rv == 0)
+ return rv;
+ csio_dbg(hw, "Some I/O aborts timed out, cleaning up..\n");
+ }
+
+ csio_scsi_cleanup_io_q(scm, &scm->active_q);
+
+ CSIO_DB_ASSERT(list_empty(&scm->active_q));
+
+ return rv;
+}
+
+/*
+ * csio_scsim_cleanup_io_lnode - Cleanup all I/Os of given lnode.
+ * @scm: SCSI module.
+ * @lnode: lnode
+ *
+ * Called with lock held, should exit with lock held.
+ * Can sleep (with dropped lock) when waiting for I/Os to complete.
+ */
+int
+csio_scsim_cleanup_io_lnode(struct csio_scsim *scm, struct csio_lnode *ln)
+{
+ struct csio_hw *hw = scm->hw;
+ struct csio_scsi_level_data sld;
+ int rv;
+ int count = DIV_ROUND_UP(60 * 1000, CSIO_SCSI_ABORT_Q_POLL_MS);
+
+ csio_dbg(hw, "Gathering all SCSI I/Os on lnode %p\n", ln);
+
+ sld.level = CSIO_LEV_LNODE;
+ sld.lnode = ln;
+ INIT_LIST_HEAD(&ln->cmpl_q);
+ csio_scsi_gather_active_ios(scm, &sld, &ln->cmpl_q);
+
+ /* No I/Os pending on this lnode */
+ if (list_empty(&ln->cmpl_q))
+ return 0;
+
+ /* Wait until all active I/Os on this lnode are completed */
+ while (!list_empty(&ln->cmpl_q) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* all I/Os completed */
+ if (list_empty(&ln->cmpl_q))
+ return 0;
+
+ csio_dbg(hw, "Some I/Os pending on ln:%p, aborting them..\n", ln);
+
+ /* I/Os are pending, abort them */
+ rv = csio_scsi_abort_io_q(scm, &ln->cmpl_q, 30000);
+ if (rv != 0) {
+ csio_dbg(hw, "Some I/O aborts timed out, cleaning up..\n");
+ csio_scsi_cleanup_io_q(scm, &ln->cmpl_q);
+ }
+
+ CSIO_DB_ASSERT(list_empty(&ln->cmpl_q));
+
+ return rv;
+}
+
+static ssize_t
+csio_show_hw_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ if (csio_is_hw_ready(hw))
+ return sysfs_emit(buf, "ready\n");
+
+ return sysfs_emit(buf, "not ready\n");
+}
+
+/* Device reset */
+static ssize_t
+csio_device_reset(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ if (*buf != '1')
+ return -EINVAL;
+
+ /* Delete NPIV lnodes */
+ csio_lnodes_exit(hw, 1);
+
+ /* Block upper IOs */
+ csio_lnodes_block_request(hw);
+
+ spin_lock_irq(&hw->lock);
+ csio_hw_reset(hw);
+ spin_unlock_irq(&hw->lock);
+
+ /* Unblock upper IOs */
+ csio_lnodes_unblock_request(hw);
+ return count;
+}
+
+/* disable port */
+static ssize_t
+csio_disable_port(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ bool disable;
+
+ if (*buf == '1' || *buf == '0')
+ disable = (*buf == '1') ? true : false;
+ else
+ return -EINVAL;
+
+ /* Block upper IOs */
+ csio_lnodes_block_by_port(hw, ln->portid);
+
+ spin_lock_irq(&hw->lock);
+ csio_disable_lnodes(hw, ln->portid, disable);
+ spin_unlock_irq(&hw->lock);
+
+ /* Unblock upper IOs */
+ csio_lnodes_unblock_by_port(hw, ln->portid);
+ return count;
+}
+
+/* Show debug level */
+static ssize_t
+csio_show_dbg_level(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+
+ return sysfs_emit(buf, "%x\n", ln->params.log_level);
+}
+
+/* Store debug level */
+static ssize_t
+csio_store_dbg_level(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ uint32_t dbg_level = 0;
+
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+
+ if (sscanf(buf, "%i", &dbg_level))
+ return -EINVAL;
+
+ ln->params.log_level = dbg_level;
+ hw->params.log_level = dbg_level;
+
+ return 0;
+}
+
+static DEVICE_ATTR(hw_state, S_IRUGO, csio_show_hw_state, NULL);
+static DEVICE_ATTR(device_reset, S_IWUSR, NULL, csio_device_reset);
+static DEVICE_ATTR(disable_port, S_IWUSR, NULL, csio_disable_port);
+static DEVICE_ATTR(dbg_level, S_IRUGO | S_IWUSR, csio_show_dbg_level,
+ csio_store_dbg_level);
+
+static struct attribute *csio_fcoe_lport_attrs[] = {
+ &dev_attr_hw_state.attr,
+ &dev_attr_device_reset.attr,
+ &dev_attr_disable_port.attr,
+ &dev_attr_dbg_level.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(csio_fcoe_lport);
+
+static ssize_t
+csio_show_num_reg_rnodes(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+
+ return sysfs_emit(buf, "%d\n", ln->num_reg_rnodes);
+}
+
+static DEVICE_ATTR(num_reg_rnodes, S_IRUGO, csio_show_num_reg_rnodes, NULL);
+
+static struct attribute *csio_fcoe_vport_attrs[] = {
+ &dev_attr_num_reg_rnodes.attr,
+ &dev_attr_dbg_level.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(csio_fcoe_vport);
+
+static inline uint32_t
+csio_scsi_copy_to_sgl(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *scmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ struct scatterlist *sg;
+ uint32_t bytes_left;
+ uint32_t bytes_copy;
+ uint32_t buf_off = 0;
+ uint32_t start_off = 0;
+ uint32_t sg_off = 0;
+ void *sg_addr;
+ void *buf_addr;
+ struct csio_dma_buf *dma_buf;
+
+ bytes_left = scsi_bufflen(scmnd);
+ sg = scsi_sglist(scmnd);
+ dma_buf = (struct csio_dma_buf *)csio_list_next(&req->gen_list);
+
+ /* Copy data from driver buffer to SGs of SCSI CMD */
+ while (bytes_left > 0 && sg && dma_buf) {
+ if (buf_off >= dma_buf->len) {
+ buf_off = 0;
+ dma_buf = (struct csio_dma_buf *)
+ csio_list_next(dma_buf);
+ continue;
+ }
+
+ if (start_off >= sg->length) {
+ start_off -= sg->length;
+ sg = sg_next(sg);
+ continue;
+ }
+
+ buf_addr = dma_buf->vaddr + buf_off;
+ sg_off = sg->offset + start_off;
+ bytes_copy = min((dma_buf->len - buf_off),
+ sg->length - start_off);
+ bytes_copy = min((uint32_t)(PAGE_SIZE - (sg_off & ~PAGE_MASK)),
+ bytes_copy);
+
+ sg_addr = kmap_atomic(sg_page(sg) + (sg_off >> PAGE_SHIFT));
+ if (!sg_addr) {
+ csio_err(hw, "failed to kmap sg:%p of ioreq:%p\n",
+ sg, req);
+ break;
+ }
+
+ csio_dbg(hw, "copy_to_sgl:sg_addr %p sg_off %d buf %p len %d\n",
+ sg_addr, sg_off, buf_addr, bytes_copy);
+ memcpy(sg_addr + (sg_off & ~PAGE_MASK), buf_addr, bytes_copy);
+ kunmap_atomic(sg_addr);
+
+ start_off += bytes_copy;
+ buf_off += bytes_copy;
+ bytes_left -= bytes_copy;
+ }
+
+ if (bytes_left > 0)
+ return DID_ERROR;
+ else
+ return DID_OK;
+}
+
+/*
+ * csio_scsi_err_handler - SCSI error handler.
+ * @hw: HW module.
+ * @req: IO request.
+ *
+ */
+static inline void
+csio_scsi_err_handler(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+ struct fcp_resp_with_ext *fcp_resp;
+ struct fcp_resp_rsp_info *rsp_info;
+ struct csio_dma_buf *dma_buf;
+ uint8_t flags, scsi_status = 0;
+ uint32_t host_status = DID_OK;
+ uint32_t rsp_len = 0, sns_len = 0;
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+
+
+ switch (req->wr_status) {
+ case FW_HOSTERROR:
+ if (unlikely(!csio_is_hw_ready(hw)))
+ return;
+
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_hosterror);
+
+ break;
+ case FW_SCSI_RSP_ERR:
+ dma_buf = &req->dma_buf;
+ fcp_resp = (struct fcp_resp_with_ext *)dma_buf->vaddr;
+ rsp_info = (struct fcp_resp_rsp_info *)(fcp_resp + 1);
+ flags = fcp_resp->resp.fr_flags;
+ scsi_status = fcp_resp->resp.fr_status;
+
+ if (flags & FCP_RSP_LEN_VAL) {
+ rsp_len = be32_to_cpu(fcp_resp->ext.fr_rsp_len);
+ if ((rsp_len != 0 && rsp_len != 4 && rsp_len != 8) ||
+ (rsp_info->rsp_code != FCP_TMF_CMPL)) {
+ host_status = DID_ERROR;
+ goto out;
+ }
+ }
+
+ if ((flags & FCP_SNS_LEN_VAL) && fcp_resp->ext.fr_sns_len) {
+ sns_len = be32_to_cpu(fcp_resp->ext.fr_sns_len);
+ if (sns_len > SCSI_SENSE_BUFFERSIZE)
+ sns_len = SCSI_SENSE_BUFFERSIZE;
+
+ memcpy(cmnd->sense_buffer,
+ &rsp_info->_fr_resvd[0] + rsp_len, sns_len);
+ CSIO_INC_STATS(scm, n_autosense);
+ }
+
+ scsi_set_resid(cmnd, 0);
+
+ /* Under run */
+ if (flags & FCP_RESID_UNDER) {
+ scsi_set_resid(cmnd,
+ be32_to_cpu(fcp_resp->ext.fr_resid));
+
+ if (!(flags & FCP_SNS_LEN_VAL) &&
+ (scsi_status == SAM_STAT_GOOD) &&
+ ((scsi_bufflen(cmnd) - scsi_get_resid(cmnd))
+ < cmnd->underflow))
+ host_status = DID_ERROR;
+ } else if (flags & FCP_RESID_OVER)
+ host_status = DID_ERROR;
+
+ CSIO_INC_STATS(scm, n_rsperror);
+ break;
+
+ case FW_SCSI_OVER_FLOW_ERR:
+ csio_warn(hw,
+ "Over-flow error,cmnd:0x%x expected len:0x%x"
+ " resid:0x%x\n", cmnd->cmnd[0],
+ scsi_bufflen(cmnd), scsi_get_resid(cmnd));
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_ovflerror);
+ break;
+
+ case FW_SCSI_UNDER_FLOW_ERR:
+ csio_warn(hw,
+ "Under-flow error,cmnd:0x%x expected"
+ " len:0x%x resid:0x%x lun:0x%llx ssn:0x%x\n",
+ cmnd->cmnd[0], scsi_bufflen(cmnd),
+ scsi_get_resid(cmnd), cmnd->device->lun,
+ rn->flowid);
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_unflerror);
+ break;
+
+ case FW_SCSI_ABORT_REQUESTED:
+ case FW_SCSI_ABORTED:
+ case FW_SCSI_CLOSE_REQUESTED:
+ csio_dbg(hw, "Req %p cmd:%p op:%x %s\n", req, cmnd,
+ cmnd->cmnd[0],
+ (req->wr_status == FW_SCSI_CLOSE_REQUESTED) ?
+ "closed" : "aborted");
+ /*
+ * csio_eh_abort_handler checks this value to
+ * succeed or fail the abort request.
+ */
+ host_status = DID_REQUEUE;
+ if (req->wr_status == FW_SCSI_CLOSE_REQUESTED)
+ CSIO_INC_STATS(scm, n_closed);
+ else
+ CSIO_INC_STATS(scm, n_aborted);
+ break;
+
+ case FW_SCSI_ABORT_TIMEDOUT:
+ /* FW timed out the abort itself */
+ csio_dbg(hw, "FW timed out abort req:%p cmnd:%p status:%x\n",
+ req, cmnd, req->wr_status);
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_abrt_timedout);
+ break;
+
+ case FW_RDEV_NOT_READY:
+ /*
+ * In firmware, a RDEV can get into this state
+ * temporarily, before moving into dissapeared/lost
+ * state. So, the driver should complete the request equivalent
+ * to device-disappeared!
+ */
+ CSIO_INC_STATS(scm, n_rdev_nr_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_RDEV_LOST:
+ CSIO_INC_STATS(scm, n_rdev_lost_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_RDEV_LOGO:
+ CSIO_INC_STATS(scm, n_rdev_logo_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_RDEV_IMPL_LOGO:
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_LINK_DOWN:
+ CSIO_INC_STATS(scm, n_link_down_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_FCOE_NO_XCHG:
+ CSIO_INC_STATS(scm, n_no_xchg_error);
+ host_status = DID_ERROR;
+ break;
+
+ default:
+ csio_err(hw, "Unknown SCSI FW WR status:%d req:%p cmnd:%p\n",
+ req->wr_status, req, cmnd);
+ CSIO_DB_ASSERT(0);
+
+ CSIO_INC_STATS(scm, n_unknown_error);
+ host_status = DID_ERROR;
+ break;
+ }
+
+out:
+ if (req->nsge > 0) {
+ scsi_dma_unmap(cmnd);
+ if (req->dcopy && (host_status == DID_OK))
+ host_status = csio_scsi_copy_to_sgl(hw, req);
+ }
+
+ cmnd->result = (((host_status) << 16) | scsi_status);
+ scsi_done(cmnd);
+
+ /* Wake up waiting threads */
+ csio_scsi_cmnd(req) = NULL;
+ complete(&req->cmplobj);
+}
+
+/*
+ * csio_scsi_cbfn - SCSI callback function.
+ * @hw: HW module.
+ * @req: IO request.
+ *
+ */
+static void
+csio_scsi_cbfn(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ uint8_t scsi_status = SAM_STAT_GOOD;
+ uint32_t host_status = DID_OK;
+
+ if (likely(req->wr_status == FW_SUCCESS)) {
+ if (req->nsge > 0) {
+ scsi_dma_unmap(cmnd);
+ if (req->dcopy)
+ host_status = csio_scsi_copy_to_sgl(hw, req);
+ }
+
+ cmnd->result = (((host_status) << 16) | scsi_status);
+ scsi_done(cmnd);
+ csio_scsi_cmnd(req) = NULL;
+ CSIO_INC_STATS(csio_hw_to_scsim(hw), n_tot_success);
+ } else {
+ /* Error handling */
+ csio_scsi_err_handler(hw, req);
+ }
+}
+
+/**
+ * csio_queuecommand - Entry point to kickstart an I/O request.
+ * @host: The scsi_host pointer.
+ * @cmnd: The I/O request from ML.
+ *
+ * This routine does the following:
+ * - Checks for HW and Rnode module readiness.
+ * - Gets a free ioreq structure (which is already initialized
+ * to uninit during its allocation).
+ * - Maps SG elements.
+ * - Initializes ioreq members.
+ * - Kicks off the SCSI state machine for this IO.
+ * - Returns busy status on error.
+ */
+static int
+csio_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmnd)
+{
+ struct csio_lnode *ln = shost_priv(host);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+ struct csio_ioreq *ioreq = NULL;
+ unsigned long flags;
+ int nsge = 0;
+ int rv = SCSI_MLQUEUE_HOST_BUSY, nr;
+ int retval;
+ struct csio_scsi_qset *sqset;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+
+ sqset = &hw->sqset[ln->portid][blk_mq_rq_cpu(scsi_cmd_to_rq(cmnd))];
+
+ nr = fc_remote_port_chkready(rport);
+ if (nr) {
+ cmnd->result = nr;
+ CSIO_INC_STATS(scsim, n_rn_nr_error);
+ goto err_done;
+ }
+
+ if (unlikely(!csio_is_hw_ready(hw))) {
+ cmnd->result = (DID_REQUEUE << 16);
+ CSIO_INC_STATS(scsim, n_hw_nr_error);
+ goto err_done;
+ }
+
+ /* Get req->nsge, if there are SG elements to be mapped */
+ nsge = scsi_dma_map(cmnd);
+ if (unlikely(nsge < 0)) {
+ CSIO_INC_STATS(scsim, n_dmamap_error);
+ goto err;
+ }
+
+ /* Do we support so many mappings? */
+ if (unlikely(nsge > scsim->max_sge)) {
+ csio_warn(hw,
+ "More SGEs than can be supported."
+ " SGEs: %d, Max SGEs: %d\n", nsge, scsim->max_sge);
+ CSIO_INC_STATS(scsim, n_unsupp_sge_error);
+ goto err_dma_unmap;
+ }
+
+ /* Get a free ioreq structure - SM is already set to uninit */
+ ioreq = csio_get_scsi_ioreq_lock(hw, scsim);
+ if (!ioreq) {
+ csio_err(hw, "Out of I/O request elements. Active #:%d\n",
+ scsim->stats.n_active);
+ CSIO_INC_STATS(scsim, n_no_req_error);
+ goto err_dma_unmap;
+ }
+
+ ioreq->nsge = nsge;
+ ioreq->lnode = ln;
+ ioreq->rnode = rn;
+ ioreq->iq_idx = sqset->iq_idx;
+ ioreq->eq_idx = sqset->eq_idx;
+ ioreq->wr_status = 0;
+ ioreq->drv_status = 0;
+ csio_scsi_cmnd(ioreq) = (void *)cmnd;
+ ioreq->tmo = 0;
+ ioreq->datadir = cmnd->sc_data_direction;
+
+ if (cmnd->sc_data_direction == DMA_TO_DEVICE) {
+ CSIO_INC_STATS(ln, n_output_requests);
+ ln->stats.n_output_bytes += scsi_bufflen(cmnd);
+ } else if (cmnd->sc_data_direction == DMA_FROM_DEVICE) {
+ CSIO_INC_STATS(ln, n_input_requests);
+ ln->stats.n_input_bytes += scsi_bufflen(cmnd);
+ } else
+ CSIO_INC_STATS(ln, n_control_requests);
+
+ /* Set cbfn */
+ ioreq->io_cbfn = csio_scsi_cbfn;
+
+ /* Needed during abort */
+ cmnd->host_scribble = (unsigned char *)ioreq;
+ csio_priv(cmnd)->fc_tm_flags = 0;
+
+ /* Kick off SCSI IO SM on the ioreq */
+ spin_lock_irqsave(&hw->lock, flags);
+ retval = csio_scsi_start_io(ioreq);
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ if (retval != 0) {
+ csio_err(hw, "ioreq: %p couldn't be started, status:%d\n",
+ ioreq, retval);
+ CSIO_INC_STATS(scsim, n_busy_error);
+ goto err_put_req;
+ }
+
+ return 0;
+
+err_put_req:
+ csio_put_scsi_ioreq_lock(hw, scsim, ioreq);
+err_dma_unmap:
+ if (nsge > 0)
+ scsi_dma_unmap(cmnd);
+err:
+ return rv;
+
+err_done:
+ scsi_done(cmnd);
+ return 0;
+}
+
+static int
+csio_do_abrt_cls(struct csio_hw *hw, struct csio_ioreq *ioreq, bool abort)
+{
+ int rv;
+ int cpu = smp_processor_id();
+ struct csio_lnode *ln = ioreq->lnode;
+ struct csio_scsi_qset *sqset = &hw->sqset[ln->portid][cpu];
+
+ ioreq->tmo = CSIO_SCSI_ABRT_TMO_MS;
+ /*
+ * Use current processor queue for posting the abort/close, but retain
+ * the ingress queue ID of the original I/O being aborted/closed - we
+ * need the abort/close completion to be received on the same queue
+ * as the original I/O.
+ */
+ ioreq->eq_idx = sqset->eq_idx;
+
+ if (abort == SCSI_ABORT)
+ rv = csio_scsi_abort(ioreq);
+ else
+ rv = csio_scsi_close(ioreq);
+
+ return rv;
+}
+
+static int
+csio_eh_abort_handler(struct scsi_cmnd *cmnd)
+{
+ struct csio_ioreq *ioreq;
+ struct csio_lnode *ln = shost_priv(cmnd->device->host);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ int ready = 0, ret;
+ unsigned long tmo = 0;
+ int rv;
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+
+ ret = fc_block_scsi_eh(cmnd);
+ if (ret)
+ return ret;
+
+ ioreq = (struct csio_ioreq *)cmnd->host_scribble;
+ if (!ioreq)
+ return SUCCESS;
+
+ if (!rn)
+ return FAILED;
+
+ csio_dbg(hw,
+ "Request to abort ioreq:%p cmd:%p cdb:%08llx"
+ " ssni:0x%x lun:%llu iq:0x%x\n",
+ ioreq, cmnd, *((uint64_t *)cmnd->cmnd), rn->flowid,
+ cmnd->device->lun, csio_q_physiqid(hw, ioreq->iq_idx));
+
+ if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) != cmnd) {
+ CSIO_INC_STATS(scsim, n_abrt_race_comp);
+ return SUCCESS;
+ }
+
+ ready = csio_is_lnode_ready(ln);
+ tmo = CSIO_SCSI_ABRT_TMO_MS;
+
+ reinit_completion(&ioreq->cmplobj);
+ spin_lock_irq(&hw->lock);
+ rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE));
+ spin_unlock_irq(&hw->lock);
+
+ if (rv != 0) {
+ if (rv == -EINVAL) {
+ /* Return success, if abort/close request issued on
+ * already completed IO
+ */
+ return SUCCESS;
+ }
+ if (ready)
+ CSIO_INC_STATS(scsim, n_abrt_busy_error);
+ else
+ CSIO_INC_STATS(scsim, n_cls_busy_error);
+
+ goto inval_scmnd;
+ }
+
+ wait_for_completion_timeout(&ioreq->cmplobj, msecs_to_jiffies(tmo));
+
+ /* FW didnt respond to abort within our timeout */
+ if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) {
+
+ csio_err(hw, "Abort timed out -- req: %p\n", ioreq);
+ CSIO_INC_STATS(scsim, n_abrt_timedout);
+
+inval_scmnd:
+ if (ioreq->nsge > 0)
+ scsi_dma_unmap(cmnd);
+
+ spin_lock_irq(&hw->lock);
+ csio_scsi_cmnd(ioreq) = NULL;
+ spin_unlock_irq(&hw->lock);
+
+ cmnd->result = (DID_ERROR << 16);
+ scsi_done(cmnd);
+
+ return FAILED;
+ }
+
+ /* FW successfully aborted the request */
+ if (host_byte(cmnd->result) == DID_REQUEUE) {
+ csio_info(hw,
+ "Aborted SCSI command to (%d:%llu) tag %u\n",
+ cmnd->device->id, cmnd->device->lun,
+ scsi_cmd_to_rq(cmnd)->tag);
+ return SUCCESS;
+ } else {
+ csio_info(hw,
+ "Failed to abort SCSI command, (%d:%llu) tag %u\n",
+ cmnd->device->id, cmnd->device->lun,
+ scsi_cmd_to_rq(cmnd)->tag);
+ return FAILED;
+ }
+}
+
+/*
+ * csio_tm_cbfn - TM callback function.
+ * @hw: HW module.
+ * @req: IO request.
+ *
+ * Cache the result in 'cmnd', since ioreq will be freed soon
+ * after we return from here, and the waiting thread shouldnt trust
+ * the ioreq contents.
+ */
+static void
+csio_tm_cbfn(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ struct csio_dma_buf *dma_buf;
+ uint8_t flags = 0;
+ struct fcp_resp_with_ext *fcp_resp;
+ struct fcp_resp_rsp_info *rsp_info;
+
+ csio_dbg(hw, "req: %p in csio_tm_cbfn status: %d\n",
+ req, req->wr_status);
+
+ /* Cache FW return status */
+ csio_priv(cmnd)->wr_status = req->wr_status;
+
+ /* Special handling based on FCP response */
+
+ /*
+ * FW returns us this error, if flags were set. FCP4 says
+ * FCP_RSP_LEN_VAL in flags shall be set for TM completions.
+ * So if a target were to set this bit, we expect that the
+ * rsp_code is set to FCP_TMF_CMPL for a successful TM
+ * completion. Any other rsp_code means TM operation failed.
+ * If a target were to just ignore setting flags, we treat
+ * the TM operation as success, and FW returns FW_SUCCESS.
+ */
+ if (req->wr_status == FW_SCSI_RSP_ERR) {
+ dma_buf = &req->dma_buf;
+ fcp_resp = (struct fcp_resp_with_ext *)dma_buf->vaddr;
+ rsp_info = (struct fcp_resp_rsp_info *)(fcp_resp + 1);
+
+ flags = fcp_resp->resp.fr_flags;
+
+ /* Modify return status if flags indicate success */
+ if (flags & FCP_RSP_LEN_VAL)
+ if (rsp_info->rsp_code == FCP_TMF_CMPL)
+ csio_priv(cmnd)->wr_status = FW_SUCCESS;
+
+ csio_dbg(hw, "TM FCP rsp code: %d\n", rsp_info->rsp_code);
+ }
+
+ /* Wake up the TM handler thread */
+ csio_scsi_cmnd(req) = NULL;
+}
+
+static int
+csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd)
+{
+ struct csio_lnode *ln = shost_priv(cmnd->device->host);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+ struct csio_ioreq *ioreq = NULL;
+ struct csio_scsi_qset *sqset;
+ unsigned long flags;
+ int retval;
+ int count, ret;
+ LIST_HEAD(local_q);
+ struct csio_scsi_level_data sld;
+
+ if (!rn)
+ goto fail;
+
+ csio_dbg(hw, "Request to reset LUN:%llu (ssni:0x%x tgtid:%d)\n",
+ cmnd->device->lun, rn->flowid, rn->scsi_id);
+
+ if (!csio_is_lnode_ready(ln)) {
+ csio_err(hw,
+ "LUN reset cannot be issued on non-ready"
+ " local node vnpi:0x%x (LUN:%llu)\n",
+ ln->vnp_flowid, cmnd->device->lun);
+ goto fail;
+ }
+
+ /* Lnode is ready, now wait on rport node readiness */
+ ret = fc_block_scsi_eh(cmnd);
+ if (ret)
+ return ret;
+
+ /*
+ * If we have blocked in the previous call, at this point, either the
+ * remote node has come back online, or device loss timer has fired
+ * and the remote node is destroyed. Allow the LUN reset only for
+ * the former case, since LUN reset is a TMF I/O on the wire, and we
+ * need a valid session to issue it.
+ */
+ if (fc_remote_port_chkready(rn->rport)) {
+ csio_err(hw,
+ "LUN reset cannot be issued on non-ready"
+ " remote node ssni:0x%x (LUN:%llu)\n",
+ rn->flowid, cmnd->device->lun);
+ goto fail;
+ }
+
+ /* Get a free ioreq structure - SM is already set to uninit */
+ ioreq = csio_get_scsi_ioreq_lock(hw, scsim);
+
+ if (!ioreq) {
+ csio_err(hw, "Out of IO request elements. Active # :%d\n",
+ scsim->stats.n_active);
+ goto fail;
+ }
+
+ sqset = &hw->sqset[ln->portid][smp_processor_id()];
+ ioreq->nsge = 0;
+ ioreq->lnode = ln;
+ ioreq->rnode = rn;
+ ioreq->iq_idx = sqset->iq_idx;
+ ioreq->eq_idx = sqset->eq_idx;
+
+ csio_scsi_cmnd(ioreq) = cmnd;
+ cmnd->host_scribble = (unsigned char *)ioreq;
+ csio_priv(cmnd)->wr_status = 0;
+
+ csio_priv(cmnd)->fc_tm_flags = FCP_TMF_LUN_RESET;
+ ioreq->tmo = CSIO_SCSI_LUNRST_TMO_MS / 1000;
+
+ /*
+ * FW times the LUN reset for ioreq->tmo, so we got to wait a little
+ * longer (10s for now) than that to allow FW to return the timed
+ * out command.
+ */
+ count = DIV_ROUND_UP((ioreq->tmo + 10) * 1000, CSIO_SCSI_TM_POLL_MS);
+
+ /* Set cbfn */
+ ioreq->io_cbfn = csio_tm_cbfn;
+
+ /* Save of the ioreq info for later use */
+ sld.level = CSIO_LEV_LUN;
+ sld.lnode = ioreq->lnode;
+ sld.rnode = ioreq->rnode;
+ sld.oslun = cmnd->device->lun;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ /* Kick off TM SM on the ioreq */
+ retval = csio_scsi_start_tm(ioreq);
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ if (retval != 0) {
+ csio_err(hw, "Failed to issue LUN reset, req:%p, status:%d\n",
+ ioreq, retval);
+ goto fail_ret_ioreq;
+ }
+
+ csio_dbg(hw, "Waiting max %d secs for LUN reset completion\n",
+ count * (CSIO_SCSI_TM_POLL_MS / 1000));
+ /* Wait for completion */
+ while ((((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd)
+ && count--)
+ msleep(CSIO_SCSI_TM_POLL_MS);
+
+ /* LUN reset timed-out */
+ if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) {
+ csio_err(hw, "LUN reset (%d:%llu) timed out\n",
+ cmnd->device->id, cmnd->device->lun);
+
+ spin_lock_irq(&hw->lock);
+ csio_scsi_drvcleanup(ioreq);
+ list_del_init(&ioreq->sm.sm_list);
+ spin_unlock_irq(&hw->lock);
+
+ goto fail_ret_ioreq;
+ }
+
+ /* LUN reset returned, check cached status */
+ if (csio_priv(cmnd)->wr_status != FW_SUCCESS) {
+ csio_err(hw, "LUN reset failed (%d:%llu), status: %d\n",
+ cmnd->device->id, cmnd->device->lun,
+ csio_priv(cmnd)->wr_status);
+ goto fail;
+ }
+
+ /* LUN reset succeeded, Start aborting affected I/Os */
+ /*
+ * Since the host guarantees during LUN reset that there
+ * will not be any more I/Os to that LUN, until the LUN reset
+ * completes, we gather pending I/Os after the LUN reset.
+ */
+ spin_lock_irq(&hw->lock);
+ csio_scsi_gather_active_ios(scsim, &sld, &local_q);
+
+ retval = csio_scsi_abort_io_q(scsim, &local_q, 30000);
+ spin_unlock_irq(&hw->lock);
+
+ /* Aborts may have timed out */
+ if (retval != 0) {
+ csio_err(hw,
+ "Attempt to abort I/Os during LUN reset of %llu"
+ " returned %d\n", cmnd->device->lun, retval);
+ /* Return I/Os back to active_q */
+ spin_lock_irq(&hw->lock);
+ list_splice_tail_init(&local_q, &scsim->active_q);
+ spin_unlock_irq(&hw->lock);
+ goto fail;
+ }
+
+ CSIO_INC_STATS(rn, n_lun_rst);
+
+ csio_info(hw, "LUN reset occurred (%d:%llu)\n",
+ cmnd->device->id, cmnd->device->lun);
+
+ return SUCCESS;
+
+fail_ret_ioreq:
+ csio_put_scsi_ioreq_lock(hw, scsim, ioreq);
+fail:
+ CSIO_INC_STATS(rn, n_lun_rst_fail);
+ return FAILED;
+}
+
+static int
+csio_sdev_init(struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+
+ if (!rport || fc_remote_port_chkready(rport))
+ return -ENXIO;
+
+ sdev->hostdata = *((struct csio_lnode **)(rport->dd_data));
+
+ return 0;
+}
+
+static int
+csio_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
+{
+ scsi_change_queue_depth(sdev, csio_lun_qdepth);
+ return 0;
+}
+
+static void
+csio_sdev_destroy(struct scsi_device *sdev)
+{
+ sdev->hostdata = NULL;
+}
+
+static int
+csio_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ int rv = 1;
+
+ spin_lock_irq(shost->host_lock);
+ if (!ln->hwp || csio_list_deleted(&ln->sm.sm_list))
+ goto out;
+
+ rv = csio_scan_done(ln, jiffies, time, csio_max_scan_tmo * HZ,
+ csio_delta_scan_tmo * HZ);
+out:
+ spin_unlock_irq(shost->host_lock);
+
+ return rv;
+}
+
+struct scsi_host_template csio_fcoe_shost_template = {
+ .module = THIS_MODULE,
+ .name = CSIO_DRV_DESC,
+ .proc_name = KBUILD_MODNAME,
+ .queuecommand = csio_queuecommand,
+ .cmd_size = sizeof(struct csio_cmd_priv),
+ .eh_timed_out = fc_eh_timed_out,
+ .eh_abort_handler = csio_eh_abort_handler,
+ .eh_device_reset_handler = csio_eh_lun_reset_handler,
+ .sdev_init = csio_sdev_init,
+ .sdev_configure = csio_sdev_configure,
+ .sdev_destroy = csio_sdev_destroy,
+ .scan_finished = csio_scan_finished,
+ .this_id = -1,
+ .sg_tablesize = CSIO_SCSI_MAX_SGE,
+ .cmd_per_lun = CSIO_MAX_CMD_PER_LUN,
+ .shost_groups = csio_fcoe_lport_groups,
+ .max_sectors = CSIO_MAX_SECTOR_SIZE,
+};
+
+struct scsi_host_template csio_fcoe_shost_vport_template = {
+ .module = THIS_MODULE,
+ .name = CSIO_DRV_DESC,
+ .proc_name = KBUILD_MODNAME,
+ .queuecommand = csio_queuecommand,
+ .eh_timed_out = fc_eh_timed_out,
+ .eh_abort_handler = csio_eh_abort_handler,
+ .eh_device_reset_handler = csio_eh_lun_reset_handler,
+ .sdev_init = csio_sdev_init,
+ .sdev_configure = csio_sdev_configure,
+ .sdev_destroy = csio_sdev_destroy,
+ .scan_finished = csio_scan_finished,
+ .this_id = -1,
+ .sg_tablesize = CSIO_SCSI_MAX_SGE,
+ .cmd_per_lun = CSIO_MAX_CMD_PER_LUN,
+ .shost_groups = csio_fcoe_vport_groups,
+ .max_sectors = CSIO_MAX_SECTOR_SIZE,
+};
+
+/*
+ * csio_scsi_alloc_ddp_bufs - Allocate buffers for DDP of unaligned SGLs.
+ * @scm: SCSI Module
+ * @hw: HW device.
+ * @buf_size: buffer size
+ * @num_buf : Number of buffers.
+ *
+ * This routine allocates DMA buffers required for SCSI Data xfer, if
+ * each SGL buffer for a SCSI Read request posted by SCSI midlayer are
+ * not virtually contiguous.
+ */
+static int
+csio_scsi_alloc_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw,
+ int buf_size, int num_buf)
+{
+ int n = 0;
+ struct list_head *tmp;
+ struct csio_dma_buf *ddp_desc = NULL;
+ uint32_t unit_size = 0;
+
+ if (!num_buf)
+ return 0;
+
+ if (!buf_size)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&scm->ddp_freelist);
+
+ /* Align buf size to page size */
+ buf_size = (buf_size + PAGE_SIZE - 1) & PAGE_MASK;
+ /* Initialize dma descriptors */
+ for (n = 0; n < num_buf; n++) {
+ /* Set unit size to request size */
+ unit_size = buf_size;
+ ddp_desc = kzalloc(sizeof(struct csio_dma_buf), GFP_KERNEL);
+ if (!ddp_desc) {
+ csio_err(hw,
+ "Failed to allocate ddp descriptors,"
+ " Num allocated = %d.\n",
+ scm->stats.n_free_ddp);
+ goto no_mem;
+ }
+
+ /* Allocate Dma buffers for DDP */
+ ddp_desc->vaddr = dma_alloc_coherent(&hw->pdev->dev, unit_size,
+ &ddp_desc->paddr, GFP_KERNEL);
+ if (!ddp_desc->vaddr) {
+ csio_err(hw,
+ "SCSI response DMA buffer (ddp) allocation"
+ " failed!\n");
+ kfree(ddp_desc);
+ goto no_mem;
+ }
+
+ ddp_desc->len = unit_size;
+
+ /* Added it to scsi ddp freelist */
+ list_add_tail(&ddp_desc->list, &scm->ddp_freelist);
+ CSIO_INC_STATS(scm, n_free_ddp);
+ }
+
+ return 0;
+no_mem:
+ /* release dma descs back to freelist and free dma memory */
+ list_for_each(tmp, &scm->ddp_freelist) {
+ ddp_desc = (struct csio_dma_buf *) tmp;
+ tmp = csio_list_prev(tmp);
+ dma_free_coherent(&hw->pdev->dev, ddp_desc->len,
+ ddp_desc->vaddr, ddp_desc->paddr);
+ list_del_init(&ddp_desc->list);
+ kfree(ddp_desc);
+ }
+ scm->stats.n_free_ddp = 0;
+
+ return -ENOMEM;
+}
+
+/*
+ * csio_scsi_free_ddp_bufs - free DDP buffers of unaligned SGLs.
+ * @scm: SCSI Module
+ * @hw: HW device.
+ *
+ * This routine frees ddp buffers.
+ */
+static void
+csio_scsi_free_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw)
+{
+ struct list_head *tmp;
+ struct csio_dma_buf *ddp_desc;
+
+ /* release dma descs back to freelist and free dma memory */
+ list_for_each(tmp, &scm->ddp_freelist) {
+ ddp_desc = (struct csio_dma_buf *) tmp;
+ tmp = csio_list_prev(tmp);
+ dma_free_coherent(&hw->pdev->dev, ddp_desc->len,
+ ddp_desc->vaddr, ddp_desc->paddr);
+ list_del_init(&ddp_desc->list);
+ kfree(ddp_desc);
+ }
+ scm->stats.n_free_ddp = 0;
+}
+
+/**
+ * csio_scsim_init - Initialize SCSI Module
+ * @scm: SCSI Module
+ * @hw: HW module
+ *
+ */
+int
+csio_scsim_init(struct csio_scsim *scm, struct csio_hw *hw)
+{
+ int i;
+ struct csio_ioreq *ioreq;
+ struct csio_dma_buf *dma_buf;
+
+ INIT_LIST_HEAD(&scm->active_q);
+ scm->hw = hw;
+
+ scm->proto_cmd_len = sizeof(struct fcp_cmnd);
+ scm->proto_rsp_len = CSIO_SCSI_RSP_LEN;
+ scm->max_sge = CSIO_SCSI_MAX_SGE;
+
+ spin_lock_init(&scm->freelist_lock);
+
+ /* Pre-allocate ioreqs and initialize them */
+ INIT_LIST_HEAD(&scm->ioreq_freelist);
+ for (i = 0; i < csio_scsi_ioreqs; i++) {
+
+ ioreq = kzalloc(sizeof(struct csio_ioreq), GFP_KERNEL);
+ if (!ioreq) {
+ csio_err(hw,
+ "I/O request element allocation failed, "
+ " Num allocated = %d.\n",
+ scm->stats.n_free_ioreq);
+
+ goto free_ioreq;
+ }
+
+ /* Allocate Dma buffers for Response Payload */
+ dma_buf = &ioreq->dma_buf;
+ dma_buf->vaddr = dma_pool_alloc(hw->scsi_dma_pool, GFP_KERNEL,
+ &dma_buf->paddr);
+ if (!dma_buf->vaddr) {
+ csio_err(hw,
+ "SCSI response DMA buffer allocation"
+ " failed!\n");
+ kfree(ioreq);
+ goto free_ioreq;
+ }
+
+ dma_buf->len = scm->proto_rsp_len;
+
+ /* Set state to uninit */
+ csio_init_state(&ioreq->sm, csio_scsis_uninit);
+ INIT_LIST_HEAD(&ioreq->gen_list);
+ init_completion(&ioreq->cmplobj);
+
+ list_add_tail(&ioreq->sm.sm_list, &scm->ioreq_freelist);
+ CSIO_INC_STATS(scm, n_free_ioreq);
+ }
+
+ if (csio_scsi_alloc_ddp_bufs(scm, hw, PAGE_SIZE, csio_ddp_descs))
+ goto free_ioreq;
+
+ return 0;
+
+free_ioreq:
+ /*
+ * Free up existing allocations, since an error
+ * from here means we are returning for good
+ */
+ while (!list_empty(&scm->ioreq_freelist)) {
+ struct csio_sm *tmp;
+
+ tmp = list_first_entry(&scm->ioreq_freelist,
+ struct csio_sm, sm_list);
+ list_del_init(&tmp->sm_list);
+ ioreq = (struct csio_ioreq *)tmp;
+
+ dma_buf = &ioreq->dma_buf;
+ dma_pool_free(hw->scsi_dma_pool, dma_buf->vaddr,
+ dma_buf->paddr);
+
+ kfree(ioreq);
+ }
+
+ scm->stats.n_free_ioreq = 0;
+
+ return -ENOMEM;
+}
+
+/**
+ * csio_scsim_exit: Uninitialize SCSI Module
+ * @scm: SCSI Module
+ *
+ */
+void
+csio_scsim_exit(struct csio_scsim *scm)
+{
+ struct csio_ioreq *ioreq;
+ struct csio_dma_buf *dma_buf;
+
+ while (!list_empty(&scm->ioreq_freelist)) {
+ struct csio_sm *tmp;
+
+ tmp = list_first_entry(&scm->ioreq_freelist,
+ struct csio_sm, sm_list);
+ list_del_init(&tmp->sm_list);
+ ioreq = (struct csio_ioreq *)tmp;
+
+ dma_buf = &ioreq->dma_buf;
+ dma_pool_free(scm->hw->scsi_dma_pool, dma_buf->vaddr,
+ dma_buf->paddr);
+
+ kfree(ioreq);
+ }
+
+ scm->stats.n_free_ioreq = 0;
+
+ csio_scsi_free_ddp_bufs(scm, scm->hw);
+}
diff --git a/drivers/scsi/csiostor/csio_scsi.h b/drivers/scsi/csiostor/csio_scsi.h
new file mode 100644
index 000000000000..39dda3c88f0d
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_scsi.h
@@ -0,0 +1,352 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_SCSI_H__
+#define __CSIO_SCSI_H__
+
+#include <linux/spinlock_types.h>
+#include <linux/completion.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/fc/fc_fcp.h>
+
+#include "csio_defs.h"
+#include "csio_wr.h"
+
+extern struct scsi_host_template csio_fcoe_shost_template;
+extern struct scsi_host_template csio_fcoe_shost_vport_template;
+
+extern int csio_scsi_eqsize;
+extern int csio_scsi_iqlen;
+extern int csio_scsi_ioreqs;
+extern uint32_t csio_max_scan_tmo;
+extern uint32_t csio_delta_scan_tmo;
+extern int csio_lun_qdepth;
+
+/*
+ **************************** NOTE *******************************
+ * How do we calculate MAX FCoE SCSI SGEs? Here is the math:
+ * Max Egress WR size = 512 bytes
+ * One SCSI egress WR has the following fixed no of bytes:
+ * 48 (sizeof(struct fw_scsi_write[read]_wr)) - FW WR
+ * + 32 (sizeof(struct fc_fcp_cmnd)) - Immediate FCP_CMD
+ * ------
+ * 80
+ * ------
+ * That leaves us with 512 - 96 = 432 bytes for data SGE. Using
+ * struct ulptx_sgl header for the SGE consumes:
+ * - 4 bytes for cmnd_sge.
+ * - 12 bytes for the first SGL.
+ * That leaves us with 416 bytes for the remaining SGE pairs. Which is
+ * is 416 / 24 (size(struct ulptx_sge_pair)) = 17 SGE pairs,
+ * or 34 SGEs. Adding the first SGE fetches us 35 SGEs.
+ */
+#define CSIO_SCSI_MAX_SGE 35
+#define CSIO_SCSI_ABRT_TMO_MS 60000
+#define CSIO_SCSI_LUNRST_TMO_MS 60000
+#define CSIO_SCSI_TM_POLL_MS 2000 /* should be less than
+ * all TM timeouts.
+ */
+#define CSIO_SCSI_IQ_WRSZ 128
+#define CSIO_SCSI_IQSIZE (csio_scsi_iqlen * CSIO_SCSI_IQ_WRSZ)
+
+#define CSIO_MAX_SNS_LEN 128
+#define CSIO_SCSI_RSP_LEN (FCP_RESP_WITH_EXT + 4 + CSIO_MAX_SNS_LEN)
+
+/* Reference to scsi_cmnd */
+#define csio_scsi_cmnd(req) ((req)->scratch1)
+
+struct csio_scsi_stats {
+ uint64_t n_tot_success; /* Total number of good I/Os */
+ uint32_t n_rn_nr_error; /* No. of remote-node-not-
+ * ready errors
+ */
+ uint32_t n_hw_nr_error; /* No. of hw-module-not-
+ * ready errors
+ */
+ uint32_t n_dmamap_error; /* No. of DMA map erros */
+ uint32_t n_unsupp_sge_error; /* No. of too-many-SGes
+ * errors.
+ */
+ uint32_t n_no_req_error; /* No. of Out-of-ioreqs error */
+ uint32_t n_busy_error; /* No. of -EBUSY errors */
+ uint32_t n_hosterror; /* No. of FW_HOSTERROR I/O */
+ uint32_t n_rsperror; /* No. of response errors */
+ uint32_t n_autosense; /* No. of auto sense replies */
+ uint32_t n_ovflerror; /* No. of overflow errors */
+ uint32_t n_unflerror; /* No. of underflow errors */
+ uint32_t n_rdev_nr_error;/* No. of rdev not
+ * ready errors
+ */
+ uint32_t n_rdev_lost_error;/* No. of rdev lost errors */
+ uint32_t n_rdev_logo_error;/* No. of rdev logo errors */
+ uint32_t n_link_down_error;/* No. of link down errors */
+ uint32_t n_no_xchg_error; /* No. no exchange error */
+ uint32_t n_unknown_error;/* No. of unhandled errors */
+ uint32_t n_aborted; /* No. of aborted I/Os */
+ uint32_t n_abrt_timedout; /* No. of abort timedouts */
+ uint32_t n_abrt_fail; /* No. of abort failures */
+ uint32_t n_abrt_dups; /* No. of duplicate aborts */
+ uint32_t n_abrt_race_comp; /* No. of aborts that raced
+ * with completions.
+ */
+ uint32_t n_abrt_busy_error;/* No. of abort failures
+ * due to -EBUSY.
+ */
+ uint32_t n_closed; /* No. of closed I/Os */
+ uint32_t n_cls_busy_error; /* No. of close failures
+ * due to -EBUSY.
+ */
+ uint32_t n_active; /* No. of IOs in active_q */
+ uint32_t n_tm_active; /* No. of TMs in active_q */
+ uint32_t n_wcbfn; /* No. of I/Os in worker
+ * cbfn q
+ */
+ uint32_t n_free_ioreq; /* No. of freelist entries */
+ uint32_t n_free_ddp; /* No. of DDP freelist */
+ uint32_t n_unaligned; /* No. of Unaligned SGls */
+ uint32_t n_inval_cplop; /* No. invalid CPL op's in IQ */
+ uint32_t n_inval_scsiop; /* No. invalid scsi op's in IQ*/
+};
+
+struct csio_scsim {
+ struct csio_hw *hw; /* Pointer to HW moduel */
+ uint8_t max_sge; /* Max SGE */
+ uint8_t proto_cmd_len; /* Proto specific SCSI
+ * cmd length
+ */
+ uint16_t proto_rsp_len; /* Proto specific SCSI
+ * response length
+ */
+ spinlock_t freelist_lock; /* Lock for ioreq freelist */
+ struct list_head active_q; /* Outstanding SCSI I/Os */
+ struct list_head ioreq_freelist; /* Free list of ioreq's */
+ struct list_head ddp_freelist; /* DDP descriptor freelist */
+ struct csio_scsi_stats stats; /* This module's statistics */
+};
+
+/* State machine defines */
+enum csio_scsi_ev {
+ CSIO_SCSIE_START_IO = 1, /* Start a regular SCSI IO */
+ CSIO_SCSIE_START_TM, /* Start a TM IO */
+ CSIO_SCSIE_COMPLETED, /* IO Completed */
+ CSIO_SCSIE_ABORT, /* Abort IO */
+ CSIO_SCSIE_ABORTED, /* IO Aborted */
+ CSIO_SCSIE_CLOSE, /* Close exchange */
+ CSIO_SCSIE_CLOSED, /* Exchange closed */
+ CSIO_SCSIE_DRVCLEANUP, /* Driver wants to manually
+ * cleanup this I/O.
+ */
+};
+
+enum csio_scsi_lev {
+ CSIO_LEV_ALL = 1,
+ CSIO_LEV_LNODE,
+ CSIO_LEV_RNODE,
+ CSIO_LEV_LUN,
+};
+
+struct csio_scsi_level_data {
+ enum csio_scsi_lev level;
+ struct csio_rnode *rnode;
+ struct csio_lnode *lnode;
+ uint64_t oslun;
+};
+
+struct csio_cmd_priv {
+ uint8_t fc_tm_flags; /* task management flags */
+ uint16_t wr_status;
+};
+
+static inline struct csio_cmd_priv *csio_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
+static inline struct csio_ioreq *
+csio_get_scsi_ioreq(struct csio_scsim *scm)
+{
+ struct csio_sm *req;
+
+ if (likely(!list_empty(&scm->ioreq_freelist))) {
+ req = list_first_entry(&scm->ioreq_freelist,
+ struct csio_sm, sm_list);
+ list_del_init(&req->sm_list);
+ CSIO_DEC_STATS(scm, n_free_ioreq);
+ return (struct csio_ioreq *)req;
+ } else
+ return NULL;
+}
+
+static inline void
+csio_put_scsi_ioreq(struct csio_scsim *scm, struct csio_ioreq *ioreq)
+{
+ list_add_tail(&ioreq->sm.sm_list, &scm->ioreq_freelist);
+ CSIO_INC_STATS(scm, n_free_ioreq);
+}
+
+static inline void
+csio_put_scsi_ioreq_list(struct csio_scsim *scm, struct list_head *reqlist,
+ int n)
+{
+ list_splice_init(reqlist, &scm->ioreq_freelist);
+ scm->stats.n_free_ioreq += n;
+}
+
+static inline struct csio_dma_buf *
+csio_get_scsi_ddp(struct csio_scsim *scm)
+{
+ struct csio_dma_buf *ddp;
+
+ if (likely(!list_empty(&scm->ddp_freelist))) {
+ ddp = list_first_entry(&scm->ddp_freelist,
+ struct csio_dma_buf, list);
+ list_del_init(&ddp->list);
+ CSIO_DEC_STATS(scm, n_free_ddp);
+ return ddp;
+ } else
+ return NULL;
+}
+
+static inline void
+csio_put_scsi_ddp(struct csio_scsim *scm, struct csio_dma_buf *ddp)
+{
+ list_add_tail(&ddp->list, &scm->ddp_freelist);
+ CSIO_INC_STATS(scm, n_free_ddp);
+}
+
+static inline void
+csio_put_scsi_ddp_list(struct csio_scsim *scm, struct list_head *reqlist,
+ int n)
+{
+ list_splice_tail_init(reqlist, &scm->ddp_freelist);
+ scm->stats.n_free_ddp += n;
+}
+
+static inline void
+csio_scsi_completed(struct csio_ioreq *ioreq, struct list_head *cbfn_q)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_COMPLETED);
+ if (csio_list_deleted(&ioreq->sm.sm_list))
+ list_add_tail(&ioreq->sm.sm_list, cbfn_q);
+}
+
+static inline void
+csio_scsi_aborted(struct csio_ioreq *ioreq, struct list_head *cbfn_q)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_ABORTED);
+ list_add_tail(&ioreq->sm.sm_list, cbfn_q);
+}
+
+static inline void
+csio_scsi_closed(struct csio_ioreq *ioreq, struct list_head *cbfn_q)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_CLOSED);
+ list_add_tail(&ioreq->sm.sm_list, cbfn_q);
+}
+
+static inline void
+csio_scsi_drvcleanup(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_DRVCLEANUP);
+}
+
+/*
+ * csio_scsi_start_io - Kick starts the IO SM.
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_start_io(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_START_IO);
+ return ioreq->drv_status;
+}
+
+/*
+ * csio_scsi_start_tm - Kicks off the Task management IO SM.
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_start_tm(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_START_TM);
+ return ioreq->drv_status;
+}
+
+/*
+ * csio_scsi_abort - Abort an IO request
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_abort(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_ABORT);
+ return ioreq->drv_status;
+}
+
+/*
+ * csio_scsi_close - Close an IO request
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_close(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_CLOSE);
+ return ioreq->drv_status;
+}
+
+void csio_scsi_cleanup_io_q(struct csio_scsim *, struct list_head *);
+int csio_scsim_cleanup_io(struct csio_scsim *, bool abort);
+int csio_scsim_cleanup_io_lnode(struct csio_scsim *,
+ struct csio_lnode *);
+struct csio_ioreq *csio_scsi_cmpl_handler(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *,
+ void *, uint8_t **);
+int csio_scsi_qconfig(struct csio_hw *);
+int csio_scsim_init(struct csio_scsim *, struct csio_hw *);
+void csio_scsim_exit(struct csio_scsim *);
+
+#endif /* __CSIO_SCSI_H__ */
diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c
new file mode 100644
index 000000000000..010a1df37f15
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_wr.c
@@ -0,0 +1,1719 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <asm/page.h>
+#include <linux/cache.h>
+
+#include "t4_values.h"
+#include "csio_hw.h"
+#include "csio_wr.h"
+#include "csio_mb.h"
+#include "csio_defs.h"
+
+int csio_intr_coalesce_cnt; /* value:SGE_INGRESS_RX_THRESHOLD[0] */
+static int csio_sge_thresh_reg; /* SGE_INGRESS_RX_THRESHOLD[0] */
+
+int csio_intr_coalesce_time = 10; /* value:SGE_TIMER_VALUE_1 */
+static int csio_sge_timer_reg = 1;
+
+#define CSIO_SET_FLBUF_SIZE(_hw, _reg, _val) \
+ csio_wr_reg32((_hw), (_val), SGE_FL_BUFFER_SIZE##_reg##_A)
+
+static void
+csio_get_flbuf_size(struct csio_hw *hw, struct csio_sge *sge, uint32_t reg)
+{
+ sge->sge_fl_buf_size[reg] = csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE0_A +
+ reg * sizeof(uint32_t));
+}
+
+/* Free list buffer size */
+static inline uint32_t
+csio_wr_fl_bufsz(struct csio_sge *sge, struct csio_dma_buf *buf)
+{
+ return sge->sge_fl_buf_size[buf->paddr & 0xF];
+}
+
+/* Size of the egress queue status page */
+static inline uint32_t
+csio_wr_qstat_pgsz(struct csio_hw *hw)
+{
+ return (hw->wrm.sge.sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64;
+}
+
+/* Ring freelist doorbell */
+static inline void
+csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq)
+{
+ /*
+ * Ring the doorbell only when we have atleast CSIO_QCREDIT_SZ
+ * number of bytes in the freelist queue. This translates to atleast
+ * 8 freelist buffer pointers (since each pointer is 8 bytes).
+ */
+ if (flq->inc_idx >= 8) {
+ csio_wr_reg32(hw, DBPRIO_F | QID_V(flq->un.fl.flid) |
+ PIDX_T5_V(flq->inc_idx / 8) | DBTYPE_F,
+ MYPF_REG(SGE_PF_KDOORBELL_A));
+ flq->inc_idx &= 7;
+ }
+}
+
+/* Write a 0 cidx increment value to enable SGE interrupts for this queue */
+static void
+csio_wr_sge_intr_enable(struct csio_hw *hw, uint16_t iqid)
+{
+ csio_wr_reg32(hw, CIDXINC_V(0) |
+ INGRESSQID_V(iqid) |
+ TIMERREG_V(X_TIMERREG_RESTART_COUNTER),
+ MYPF_REG(SGE_PF_GTS_A));
+}
+
+/*
+ * csio_wr_fill_fl - Populate the FL buffers of a FL queue.
+ * @hw: HW module.
+ * @flq: Freelist queue.
+ *
+ * Fill up freelist buffer entries with buffers of size specified
+ * in the size register.
+ *
+ */
+static int
+csio_wr_fill_fl(struct csio_hw *hw, struct csio_q *flq)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ __be64 *d = (__be64 *)(flq->vstart);
+ struct csio_dma_buf *buf = &flq->un.fl.bufs[0];
+ uint64_t paddr;
+ int sreg = flq->un.fl.sreg;
+ int n = flq->credits;
+
+ while (n--) {
+ buf->len = sge->sge_fl_buf_size[sreg];
+ buf->vaddr = dma_alloc_coherent(&hw->pdev->dev, buf->len,
+ &buf->paddr, GFP_KERNEL);
+ if (!buf->vaddr) {
+ csio_err(hw, "Could only fill %d buffers!\n", n + 1);
+ return -ENOMEM;
+ }
+
+ paddr = buf->paddr | (sreg & 0xF);
+
+ *d++ = cpu_to_be64(paddr);
+ buf++;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_wr_update_fl -
+ * @hw: HW module.
+ * @flq: Freelist queue.
+ *
+ *
+ */
+static inline void
+csio_wr_update_fl(struct csio_hw *hw, struct csio_q *flq, uint16_t n)
+{
+
+ flq->inc_idx += n;
+ flq->pidx += n;
+ if (unlikely(flq->pidx >= flq->credits))
+ flq->pidx -= (uint16_t)flq->credits;
+
+ CSIO_INC_STATS(flq, n_flq_refill);
+}
+
+/*
+ * csio_wr_alloc_q - Allocate a WR queue and initialize it.
+ * @hw: HW module
+ * @qsize: Size of the queue in bytes
+ * @wrsize: Since of WR in this queue, if fixed.
+ * @type: Type of queue (Ingress/Egress/Freelist)
+ * @owner: Module that owns this queue.
+ * @nflb: Number of freelist buffers for FL.
+ * @sreg: What is the FL buffer size register?
+ * @iq_int_handler: Ingress queue handler in INTx mode.
+ *
+ * This function allocates and sets up a queue for the caller
+ * of size qsize, aligned at the required boundary. This is subject to
+ * be free entries being available in the queue array. If one is found,
+ * it is initialized with the allocated queue, marked as being used (owner),
+ * and a handle returned to the caller in form of the queue's index
+ * into the q_arr array.
+ * If user has indicated a freelist (by specifying nflb > 0), create
+ * another queue (with its own index into q_arr) for the freelist. Allocate
+ * memory for DMA buffer metadata (vaddr, len etc). Save off the freelist
+ * idx in the ingress queue's flq.idx. This is how a Freelist is associated
+ * with its owning ingress queue.
+ */
+int
+csio_wr_alloc_q(struct csio_hw *hw, uint32_t qsize, uint32_t wrsize,
+ uint16_t type, void *owner, uint32_t nflb, int sreg,
+ iq_handler_t iq_intx_handler)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q, *flq;
+ int free_idx = wrm->free_qidx;
+ int ret_idx = free_idx;
+ uint32_t qsz;
+ int flq_idx;
+
+ if (free_idx >= wrm->num_q) {
+ csio_err(hw, "No more free queues.\n");
+ return -1;
+ }
+
+ switch (type) {
+ case CSIO_EGRESS:
+ qsz = ALIGN(qsize, CSIO_QCREDIT_SZ) + csio_wr_qstat_pgsz(hw);
+ break;
+ case CSIO_INGRESS:
+ switch (wrsize) {
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ break;
+ default:
+ csio_err(hw, "Invalid Ingress queue WR size:%d\n",
+ wrsize);
+ return -1;
+ }
+
+ /*
+ * Number of elements must be a multiple of 16
+ * So this includes status page size
+ */
+ qsz = ALIGN(qsize/wrsize, 16) * wrsize;
+
+ break;
+ case CSIO_FREELIST:
+ qsz = ALIGN(qsize/wrsize, 8) * wrsize + csio_wr_qstat_pgsz(hw);
+ break;
+ default:
+ csio_err(hw, "Invalid queue type: 0x%x\n", type);
+ return -1;
+ }
+
+ q = wrm->q_arr[free_idx];
+
+ q->vstart = dma_alloc_coherent(&hw->pdev->dev, qsz, &q->pstart,
+ GFP_KERNEL);
+ if (!q->vstart) {
+ csio_err(hw,
+ "Failed to allocate DMA memory for "
+ "queue at id: %d size: %d\n", free_idx, qsize);
+ return -1;
+ }
+
+ q->type = type;
+ q->owner = owner;
+ q->pidx = q->cidx = q->inc_idx = 0;
+ q->size = qsz;
+ q->wr_sz = wrsize; /* If using fixed size WRs */
+
+ wrm->free_qidx++;
+
+ if (type == CSIO_INGRESS) {
+ /* Since queue area is set to zero */
+ q->un.iq.genbit = 1;
+
+ /*
+ * Ingress queue status page size is always the size of
+ * the ingress queue entry.
+ */
+ q->credits = (qsz - q->wr_sz) / q->wr_sz;
+ q->vwrap = (void *)((uintptr_t)(q->vstart) + qsz
+ - q->wr_sz);
+
+ /* Allocate memory for FL if requested */
+ if (nflb > 0) {
+ flq_idx = csio_wr_alloc_q(hw, nflb * sizeof(__be64),
+ sizeof(__be64), CSIO_FREELIST,
+ owner, 0, sreg, NULL);
+ if (flq_idx == -1) {
+ csio_err(hw,
+ "Failed to allocate FL queue"
+ " for IQ idx:%d\n", free_idx);
+ return -1;
+ }
+
+ /* Associate the new FL with the Ingress quue */
+ q->un.iq.flq_idx = flq_idx;
+
+ flq = wrm->q_arr[q->un.iq.flq_idx];
+ flq->un.fl.bufs = kcalloc(flq->credits,
+ sizeof(struct csio_dma_buf),
+ GFP_KERNEL);
+ if (!flq->un.fl.bufs) {
+ csio_err(hw,
+ "Failed to allocate FL queue bufs"
+ " for IQ idx:%d\n", free_idx);
+ return -1;
+ }
+
+ flq->un.fl.packen = 0;
+ flq->un.fl.offset = 0;
+ flq->un.fl.sreg = sreg;
+
+ /* Fill up the free list buffers */
+ if (csio_wr_fill_fl(hw, flq))
+ return -1;
+
+ /*
+ * Make sure in a FLQ, atleast 1 credit (8 FL buffers)
+ * remains unpopulated,otherwise HW thinks
+ * FLQ is empty.
+ */
+ flq->pidx = flq->inc_idx = flq->credits - 8;
+ } else {
+ q->un.iq.flq_idx = -1;
+ }
+
+ /* Associate the IQ INTx handler. */
+ q->un.iq.iq_intx_handler = iq_intx_handler;
+
+ csio_q_iqid(hw, ret_idx) = CSIO_MAX_QID;
+
+ } else if (type == CSIO_EGRESS) {
+ q->credits = (qsz - csio_wr_qstat_pgsz(hw)) / CSIO_QCREDIT_SZ;
+ q->vwrap = (void *)((uintptr_t)(q->vstart) + qsz
+ - csio_wr_qstat_pgsz(hw));
+ csio_q_eqid(hw, ret_idx) = CSIO_MAX_QID;
+ } else { /* Freelist */
+ q->credits = (qsz - csio_wr_qstat_pgsz(hw)) / sizeof(__be64);
+ q->vwrap = (void *)((uintptr_t)(q->vstart) + qsz
+ - csio_wr_qstat_pgsz(hw));
+ csio_q_flid(hw, ret_idx) = CSIO_MAX_QID;
+ }
+
+ return ret_idx;
+}
+
+/*
+ * csio_wr_iq_create_rsp - Response handler for IQ creation.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @iq_idx: Ingress queue that got created.
+ *
+ * Handle FW_IQ_CMD mailbox completion. Save off the assigned IQ/FL ids.
+ */
+static int
+csio_wr_iq_create_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx)
+{
+ struct csio_iq_params iqp;
+ enum fw_retval retval;
+ uint32_t iq_id;
+ int flq_idx;
+
+ memset(&iqp, 0, sizeof(struct csio_iq_params));
+
+ csio_mb_iq_alloc_write_rsp(hw, mbp, &retval, &iqp);
+
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "IQ cmd returned 0x%x!\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_q_iqid(hw, iq_idx) = iqp.iqid;
+ csio_q_physiqid(hw, iq_idx) = iqp.physiqid;
+ csio_q_pidx(hw, iq_idx) = csio_q_cidx(hw, iq_idx) = 0;
+ csio_q_inc_idx(hw, iq_idx) = 0;
+
+ /* Actual iq-id. */
+ iq_id = iqp.iqid - hw->wrm.fw_iq_start;
+
+ /* Set the iq-id to iq map table. */
+ if (iq_id >= CSIO_MAX_IQ) {
+ csio_err(hw,
+ "Exceeding MAX_IQ(%d) supported!"
+ " iqid:%d rel_iqid:%d FW iq_start:%d\n",
+ CSIO_MAX_IQ, iq_id, iqp.iqid, hw->wrm.fw_iq_start);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+ csio_q_set_intr_map(hw, iq_idx, iq_id);
+
+ /*
+ * During FW_IQ_CMD, FW sets interrupt_sent bit to 1 in the SGE
+ * ingress context of this queue. This will block interrupts to
+ * this queue until the next GTS write. Therefore, we do a
+ * 0-cidx increment GTS write for this queue just to clear the
+ * interrupt_sent bit. This will re-enable interrupts to this
+ * queue.
+ */
+ csio_wr_sge_intr_enable(hw, iqp.physiqid);
+
+ flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
+ if (flq_idx != -1) {
+ struct csio_q *flq = hw->wrm.q_arr[flq_idx];
+
+ csio_q_flid(hw, flq_idx) = iqp.fl0id;
+ csio_q_cidx(hw, flq_idx) = 0;
+ csio_q_pidx(hw, flq_idx) = csio_q_credits(hw, flq_idx) - 8;
+ csio_q_inc_idx(hw, flq_idx) = csio_q_credits(hw, flq_idx) - 8;
+
+ /* Now update SGE about the buffers allocated during init */
+ csio_wr_ring_fldb(hw, flq);
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_wr_iq_create - Configure an Ingress queue with FW.
+ * @hw: The HW module.
+ * @priv: Private data object.
+ * @iq_idx: Ingress queue index in the WR module.
+ * @vec: MSIX vector.
+ * @portid: PCIE Channel to be associated with this queue.
+ * @async: Is this a FW asynchronous message handling queue?
+ * @cbfn: Completion callback.
+ *
+ * This API configures an ingress queue with FW by issuing a FW_IQ_CMD mailbox
+ * with alloc/write bits set.
+ */
+int
+csio_wr_iq_create(struct csio_hw *hw, void *priv, int iq_idx,
+ uint32_t vec, uint8_t portid, bool async,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_mb *mbp;
+ struct csio_iq_params iqp;
+ int flq_idx;
+
+ memset(&iqp, 0, sizeof(struct csio_iq_params));
+ csio_q_portid(hw, iq_idx) = portid;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ csio_err(hw, "IQ command out of memory!\n");
+ return -ENOMEM;
+ }
+
+ switch (hw->intr_mode) {
+ case CSIO_IM_INTX:
+ case CSIO_IM_MSI:
+ /* For interrupt forwarding queue only */
+ if (hw->intr_iq_idx == iq_idx)
+ iqp.iqandst = X_INTERRUPTDESTINATION_PCIE;
+ else
+ iqp.iqandst = X_INTERRUPTDESTINATION_IQ;
+ iqp.iqandstindex =
+ csio_q_physiqid(hw, hw->intr_iq_idx);
+ break;
+ case CSIO_IM_MSIX:
+ iqp.iqandst = X_INTERRUPTDESTINATION_PCIE;
+ iqp.iqandstindex = (uint16_t)vec;
+ break;
+ case CSIO_IM_NONE:
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ /* Pass in the ingress queue cmd parameters */
+ iqp.pfn = hw->pfn;
+ iqp.vfn = 0;
+ iqp.iq_start = 1;
+ iqp.viid = 0;
+ iqp.type = FW_IQ_TYPE_FL_INT_CAP;
+ iqp.iqasynch = async;
+ if (csio_intr_coalesce_cnt)
+ iqp.iqanus = X_UPDATESCHEDULING_COUNTER_OPTTIMER;
+ else
+ iqp.iqanus = X_UPDATESCHEDULING_TIMER;
+ iqp.iqanud = X_UPDATEDELIVERY_INTERRUPT;
+ iqp.iqpciech = portid;
+ iqp.iqintcntthresh = (uint8_t)csio_sge_thresh_reg;
+
+ switch (csio_q_wr_sz(hw, iq_idx)) {
+ case 16:
+ iqp.iqesize = 0; break;
+ case 32:
+ iqp.iqesize = 1; break;
+ case 64:
+ iqp.iqesize = 2; break;
+ case 128:
+ iqp.iqesize = 3; break;
+ }
+
+ iqp.iqsize = csio_q_size(hw, iq_idx) /
+ csio_q_wr_sz(hw, iq_idx);
+ iqp.iqaddr = csio_q_pstart(hw, iq_idx);
+
+ flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
+ if (flq_idx != -1) {
+ enum chip_type chip = CHELSIO_CHIP_VERSION(hw->chip_id);
+ struct csio_q *flq = hw->wrm.q_arr[flq_idx];
+
+ iqp.fl0paden = 1;
+ iqp.fl0packen = flq->un.fl.packen ? 1 : 0;
+ iqp.fl0fbmin = X_FETCHBURSTMIN_64B;
+ iqp.fl0fbmax = ((chip == CHELSIO_T5) ?
+ X_FETCHBURSTMAX_512B : X_FETCHBURSTMAX_256B);
+ iqp.fl0size = csio_q_size(hw, flq_idx) / CSIO_QCREDIT_SZ;
+ iqp.fl0addr = csio_q_pstart(hw, flq_idx);
+ }
+
+ csio_mb_iq_alloc_write(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &iqp, cbfn);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of IQ cmd failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_iq_create_rsp(hw, mbp, iq_idx);
+}
+
+/*
+ * csio_wr_eq_create_rsp - Response handler for EQ creation.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @eq_idx: Egress queue that got created.
+ *
+ * Handle FW_EQ_OFLD_CMD mailbox completion. Save off the assigned EQ ids.
+ */
+static int
+csio_wr_eq_cfg_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx)
+{
+ struct csio_eq_params eqp;
+ enum fw_retval retval;
+
+ memset(&eqp, 0, sizeof(struct csio_eq_params));
+
+ csio_mb_eq_ofld_alloc_write_rsp(hw, mbp, &retval, &eqp);
+
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "EQ OFLD cmd returned 0x%x!\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_q_eqid(hw, eq_idx) = (uint16_t)eqp.eqid;
+ csio_q_physeqid(hw, eq_idx) = (uint16_t)eqp.physeqid;
+ csio_q_pidx(hw, eq_idx) = csio_q_cidx(hw, eq_idx) = 0;
+ csio_q_inc_idx(hw, eq_idx) = 0;
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_wr_eq_create - Configure an Egress queue with FW.
+ * @hw: HW module.
+ * @priv: Private data.
+ * @eq_idx: Egress queue index in the WR module.
+ * @iq_idx: Associated ingress queue index.
+ * @cbfn: Completion callback.
+ *
+ * This API configures a offload egress queue with FW by issuing a
+ * FW_EQ_OFLD_CMD (with alloc + write ) mailbox.
+ */
+int
+csio_wr_eq_create(struct csio_hw *hw, void *priv, int eq_idx,
+ int iq_idx, uint8_t portid,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_mb *mbp;
+ struct csio_eq_params eqp;
+
+ memset(&eqp, 0, sizeof(struct csio_eq_params));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ csio_err(hw, "EQ command out of memory!\n");
+ return -ENOMEM;
+ }
+
+ eqp.pfn = hw->pfn;
+ eqp.vfn = 0;
+ eqp.eqstart = 1;
+ eqp.hostfcmode = X_HOSTFCMODE_STATUS_PAGE;
+ eqp.iqid = csio_q_iqid(hw, iq_idx);
+ eqp.fbmin = X_FETCHBURSTMIN_64B;
+ eqp.fbmax = X_FETCHBURSTMAX_512B;
+ eqp.cidxfthresh = 0;
+ eqp.pciechn = portid;
+ eqp.eqsize = csio_q_size(hw, eq_idx) / CSIO_QCREDIT_SZ;
+ eqp.eqaddr = csio_q_pstart(hw, eq_idx);
+
+ csio_mb_eq_ofld_alloc_write(hw, mbp, priv, CSIO_MB_DEFAULT_TMO,
+ &eqp, cbfn);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of EQ OFLD cmd failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_eq_cfg_rsp(hw, mbp, eq_idx);
+}
+
+/*
+ * csio_wr_iq_destroy_rsp - Response handler for IQ removal.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @iq_idx: Ingress queue that was freed.
+ *
+ * Handle FW_IQ_CMD (free) mailbox completion.
+ */
+static int
+csio_wr_iq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx)
+{
+ enum fw_retval retval = csio_mb_fw_retval(mbp);
+ int rv = 0;
+
+ if (retval != FW_SUCCESS)
+ rv = -EINVAL;
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return rv;
+}
+
+/*
+ * csio_wr_iq_destroy - Free an ingress queue.
+ * @hw: The HW module.
+ * @priv: Private data object.
+ * @iq_idx: Ingress queue index to destroy
+ * @cbfn: Completion callback.
+ *
+ * This API frees an ingress queue by issuing the FW_IQ_CMD
+ * with the free bit set.
+ */
+static int
+csio_wr_iq_destroy(struct csio_hw *hw, void *priv, int iq_idx,
+ void (*cbfn)(struct csio_hw *, struct csio_mb *))
+{
+ int rv = 0;
+ struct csio_mb *mbp;
+ struct csio_iq_params iqp;
+ int flq_idx;
+
+ memset(&iqp, 0, sizeof(struct csio_iq_params));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp)
+ return -ENOMEM;
+
+ iqp.pfn = hw->pfn;
+ iqp.vfn = 0;
+ iqp.iqid = csio_q_iqid(hw, iq_idx);
+ iqp.type = FW_IQ_TYPE_FL_INT_CAP;
+
+ flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
+ if (flq_idx != -1)
+ iqp.fl0id = csio_q_flid(hw, flq_idx);
+ else
+ iqp.fl0id = 0xFFFF;
+
+ iqp.fl1id = 0xFFFF;
+
+ csio_mb_iq_free(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &iqp, cbfn);
+
+ rv = csio_mb_issue(hw, mbp);
+ if (rv != 0) {
+ mempool_free(mbp, hw->mb_mempool);
+ return rv;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_iq_destroy_rsp(hw, mbp, iq_idx);
+}
+
+/*
+ * csio_wr_eq_destroy_rsp - Response handler for OFLD EQ creation.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @eq_idx: Egress queue that was freed.
+ *
+ * Handle FW_OFLD_EQ_CMD (free) mailbox completion.
+ */
+static int
+csio_wr_eq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx)
+{
+ enum fw_retval retval = csio_mb_fw_retval(mbp);
+ int rv = 0;
+
+ if (retval != FW_SUCCESS)
+ rv = -EINVAL;
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return rv;
+}
+
+/*
+ * csio_wr_eq_destroy - Free an Egress queue.
+ * @hw: The HW module.
+ * @priv: Private data object.
+ * @eq_idx: Egress queue index to destroy
+ * @cbfn: Completion callback.
+ *
+ * This API frees an Egress queue by issuing the FW_EQ_OFLD_CMD
+ * with the free bit set.
+ */
+static int
+csio_wr_eq_destroy(struct csio_hw *hw, void *priv, int eq_idx,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ int rv = 0;
+ struct csio_mb *mbp;
+ struct csio_eq_params eqp;
+
+ memset(&eqp, 0, sizeof(struct csio_eq_params));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp)
+ return -ENOMEM;
+
+ eqp.pfn = hw->pfn;
+ eqp.vfn = 0;
+ eqp.eqid = csio_q_eqid(hw, eq_idx);
+
+ csio_mb_eq_ofld_free(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &eqp, cbfn);
+
+ rv = csio_mb_issue(hw, mbp);
+ if (rv != 0) {
+ mempool_free(mbp, hw->mb_mempool);
+ return rv;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_eq_destroy_rsp(hw, mbp, eq_idx);
+}
+
+/*
+ * csio_wr_cleanup_eq_stpg - Cleanup Egress queue status page
+ * @hw: HW module
+ * @qidx: Egress queue index
+ *
+ * Cleanup the Egress queue status page.
+ */
+static void
+csio_wr_cleanup_eq_stpg(struct csio_hw *hw, int qidx)
+{
+ struct csio_q *q = csio_hw_to_wrm(hw)->q_arr[qidx];
+ struct csio_qstatus_page *stp = (struct csio_qstatus_page *)q->vwrap;
+
+ memset(stp, 0, sizeof(*stp));
+}
+
+/*
+ * csio_wr_cleanup_iq_ftr - Cleanup Footer entries in IQ
+ * @hw: HW module
+ * @qidx: Ingress queue index
+ *
+ * Cleanup the footer entries in the given ingress queue,
+ * set to 1 the internal copy of genbit.
+ */
+static void
+csio_wr_cleanup_iq_ftr(struct csio_hw *hw, int qidx)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q = wrm->q_arr[qidx];
+ void *wr;
+ struct csio_iqwr_footer *ftr;
+ uint32_t i = 0;
+
+ /* set to 1 since we are just about zero out genbit */
+ q->un.iq.genbit = 1;
+
+ for (i = 0; i < q->credits; i++) {
+ /* Get the WR */
+ wr = (void *)((uintptr_t)q->vstart +
+ (i * q->wr_sz));
+ /* Get the footer */
+ ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
+ (q->wr_sz - sizeof(*ftr)));
+ /* Zero out footer */
+ memset(ftr, 0, sizeof(*ftr));
+ }
+}
+
+int
+csio_wr_destroy_queues(struct csio_hw *hw, bool cmd)
+{
+ int i, flq_idx;
+ struct csio_q *q;
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ int rv;
+
+ for (i = 0; i < wrm->free_qidx; i++) {
+ q = wrm->q_arr[i];
+
+ switch (q->type) {
+ case CSIO_EGRESS:
+ if (csio_q_eqid(hw, i) != CSIO_MAX_QID) {
+ csio_wr_cleanup_eq_stpg(hw, i);
+ if (!cmd) {
+ csio_q_eqid(hw, i) = CSIO_MAX_QID;
+ continue;
+ }
+
+ rv = csio_wr_eq_destroy(hw, NULL, i, NULL);
+ if ((rv == -EBUSY) || (rv == -ETIMEDOUT))
+ cmd = false;
+
+ csio_q_eqid(hw, i) = CSIO_MAX_QID;
+ }
+ fallthrough;
+ case CSIO_INGRESS:
+ if (csio_q_iqid(hw, i) != CSIO_MAX_QID) {
+ csio_wr_cleanup_iq_ftr(hw, i);
+ if (!cmd) {
+ csio_q_iqid(hw, i) = CSIO_MAX_QID;
+ flq_idx = csio_q_iq_flq_idx(hw, i);
+ if (flq_idx != -1)
+ csio_q_flid(hw, flq_idx) =
+ CSIO_MAX_QID;
+ continue;
+ }
+
+ rv = csio_wr_iq_destroy(hw, NULL, i, NULL);
+ if ((rv == -EBUSY) || (rv == -ETIMEDOUT))
+ cmd = false;
+
+ csio_q_iqid(hw, i) = CSIO_MAX_QID;
+ flq_idx = csio_q_iq_flq_idx(hw, i);
+ if (flq_idx != -1)
+ csio_q_flid(hw, flq_idx) = CSIO_MAX_QID;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ hw->flags &= ~CSIO_HWF_Q_FW_ALLOCED;
+
+ return 0;
+}
+
+/*
+ * csio_wr_get - Get requested size of WR entry/entries from queue.
+ * @hw: HW module.
+ * @qidx: Index of queue.
+ * @size: Cumulative size of Work request(s).
+ * @wrp: Work request pair.
+ *
+ * If requested credits are available, return the start address of the
+ * work request in the work request pair. Set pidx accordingly and
+ * return.
+ *
+ * NOTE about WR pair:
+ * ==================
+ * A WR can start towards the end of a queue, and then continue at the
+ * beginning, since the queue is considered to be circular. This will
+ * require a pair of address/size to be passed back to the caller -
+ * hence Work request pair format.
+ */
+int
+csio_wr_get(struct csio_hw *hw, int qidx, uint32_t size,
+ struct csio_wr_pair *wrp)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q = wrm->q_arr[qidx];
+ void *cwr = (void *)((uintptr_t)(q->vstart) +
+ (q->pidx * CSIO_QCREDIT_SZ));
+ struct csio_qstatus_page *stp = (struct csio_qstatus_page *)q->vwrap;
+ uint16_t cidx = q->cidx = ntohs(stp->cidx);
+ uint16_t pidx = q->pidx;
+ uint32_t req_sz = ALIGN(size, CSIO_QCREDIT_SZ);
+ int req_credits = req_sz / CSIO_QCREDIT_SZ;
+ int credits;
+
+ CSIO_DB_ASSERT(q->owner != NULL);
+ CSIO_DB_ASSERT((qidx >= 0) && (qidx < wrm->free_qidx));
+ CSIO_DB_ASSERT(cidx <= q->credits);
+
+ /* Calculate credits */
+ if (pidx > cidx) {
+ credits = q->credits - (pidx - cidx) - 1;
+ } else if (cidx > pidx) {
+ credits = cidx - pidx - 1;
+ } else {
+ /* cidx == pidx, empty queue */
+ credits = q->credits;
+ CSIO_INC_STATS(q, n_qempty);
+ }
+
+ /*
+ * Check if we have enough credits.
+ * credits = 1 implies queue is full.
+ */
+ if (!credits || (req_credits > credits)) {
+ CSIO_INC_STATS(q, n_qfull);
+ return -EBUSY;
+ }
+
+ /*
+ * If we are here, we have enough credits to satisfy the
+ * request. Check if we are near the end of q, and if WR spills over.
+ * If it does, use the first addr/size to cover the queue until
+ * the end. Fit the remainder portion of the request at the top
+ * of queue and return it in the second addr/len. Set pidx
+ * accordingly.
+ */
+ if (unlikely(((uintptr_t)cwr + req_sz) > (uintptr_t)(q->vwrap))) {
+ wrp->addr1 = cwr;
+ wrp->size1 = (uint32_t)((uintptr_t)q->vwrap - (uintptr_t)cwr);
+ wrp->addr2 = q->vstart;
+ wrp->size2 = req_sz - wrp->size1;
+ q->pidx = (uint16_t)(ALIGN(wrp->size2, CSIO_QCREDIT_SZ) /
+ CSIO_QCREDIT_SZ);
+ CSIO_INC_STATS(q, n_qwrap);
+ CSIO_INC_STATS(q, n_eq_wr_split);
+ } else {
+ wrp->addr1 = cwr;
+ wrp->size1 = req_sz;
+ wrp->addr2 = NULL;
+ wrp->size2 = 0;
+ q->pidx += (uint16_t)req_credits;
+
+ /* We are the end of queue, roll back pidx to top of queue */
+ if (unlikely(q->pidx == q->credits)) {
+ q->pidx = 0;
+ CSIO_INC_STATS(q, n_qwrap);
+ }
+ }
+
+ q->inc_idx = (uint16_t)req_credits;
+
+ CSIO_INC_STATS(q, n_tot_reqs);
+
+ return 0;
+}
+
+/*
+ * csio_wr_copy_to_wrp - Copies given data into WR.
+ * @data_buf - Data buffer
+ * @wrp - Work request pair.
+ * @wr_off - Work request offset.
+ * @data_len - Data length.
+ *
+ * Copies the given data in Work Request. Work request pair(wrp) specifies
+ * address information of Work request.
+ * Returns: none
+ */
+void
+csio_wr_copy_to_wrp(void *data_buf, struct csio_wr_pair *wrp,
+ uint32_t wr_off, uint32_t data_len)
+{
+ uint32_t nbytes;
+
+ /* Number of space available in buffer addr1 of WRP */
+ nbytes = ((wrp->size1 - wr_off) >= data_len) ?
+ data_len : (wrp->size1 - wr_off);
+
+ memcpy((uint8_t *) wrp->addr1 + wr_off, data_buf, nbytes);
+ data_len -= nbytes;
+
+ /* Write the remaining data from the beginning of circular buffer */
+ if (data_len) {
+ CSIO_DB_ASSERT(data_len <= wrp->size2);
+ CSIO_DB_ASSERT(wrp->addr2 != NULL);
+ memcpy(wrp->addr2, (uint8_t *) data_buf + nbytes, data_len);
+ }
+}
+
+/*
+ * csio_wr_issue - Notify chip of Work request.
+ * @hw: HW module.
+ * @qidx: Index of queue.
+ * @prio: 0: Low priority, 1: High priority
+ *
+ * Rings the SGE Doorbell by writing the current producer index of the passed
+ * in queue into the register.
+ *
+ */
+int
+csio_wr_issue(struct csio_hw *hw, int qidx, bool prio)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q = wrm->q_arr[qidx];
+
+ CSIO_DB_ASSERT((qidx >= 0) && (qidx < wrm->free_qidx));
+
+ wmb();
+ /* Ring SGE Doorbell writing q->pidx into it */
+ csio_wr_reg32(hw, DBPRIO_V(prio) | QID_V(q->un.eq.physeqid) |
+ PIDX_T5_V(q->inc_idx) | DBTYPE_F,
+ MYPF_REG(SGE_PF_KDOORBELL_A));
+ q->inc_idx = 0;
+
+ return 0;
+}
+
+static inline uint32_t
+csio_wr_avail_qcredits(struct csio_q *q)
+{
+ if (q->pidx > q->cidx)
+ return q->pidx - q->cidx;
+ else if (q->cidx > q->pidx)
+ return q->credits - (q->cidx - q->pidx);
+ else
+ return 0; /* cidx == pidx, empty queue */
+}
+
+/*
+ * csio_wr_inval_flq_buf - Invalidate a free list buffer entry.
+ * @hw: HW module.
+ * @flq: The freelist queue.
+ *
+ * Invalidate the driver's version of a freelist buffer entry,
+ * without freeing the associated the DMA memory. The entry
+ * to be invalidated is picked up from the current Free list
+ * queue cidx.
+ *
+ */
+static inline void
+csio_wr_inval_flq_buf(struct csio_hw *hw, struct csio_q *flq)
+{
+ flq->cidx++;
+ if (flq->cidx == flq->credits) {
+ flq->cidx = 0;
+ CSIO_INC_STATS(flq, n_qwrap);
+ }
+}
+
+/*
+ * csio_wr_process_fl - Process a freelist completion.
+ * @hw: HW module.
+ * @q: The ingress queue attached to the Freelist.
+ * @wr: The freelist completion WR in the ingress queue.
+ * @len_to_qid: The lower 32-bits of the first flit of the RSP footer
+ * @iq_handler: Caller's handler for this completion.
+ * @priv: Private pointer of caller
+ *
+ */
+static inline void
+csio_wr_process_fl(struct csio_hw *hw, struct csio_q *q,
+ void *wr, uint32_t len_to_qid,
+ void (*iq_handler)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *priv)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ struct csio_fl_dma_buf flb;
+ struct csio_dma_buf *buf, *fbuf;
+ uint32_t bufsz, len, lastlen = 0;
+ struct csio_q *flq = hw->wrm.q_arr[q->un.iq.flq_idx];
+
+ CSIO_DB_ASSERT(flq != NULL);
+
+ len = len_to_qid;
+
+ if (len & IQWRF_NEWBUF) {
+ if (flq->un.fl.offset > 0) {
+ csio_wr_inval_flq_buf(hw, flq);
+ flq->un.fl.offset = 0;
+ }
+ len = IQWRF_LEN_GET(len);
+ }
+
+ CSIO_DB_ASSERT(len != 0);
+
+ flb.totlen = len;
+
+ /* Consume all freelist buffers used for len bytes */
+ for (fbuf = flb.flbufs; ; fbuf++) {
+ buf = &flq->un.fl.bufs[flq->cidx];
+ bufsz = csio_wr_fl_bufsz(sge, buf);
+
+ fbuf->paddr = buf->paddr;
+ fbuf->vaddr = buf->vaddr;
+
+ flb.offset = flq->un.fl.offset;
+ lastlen = min(bufsz, len);
+ fbuf->len = lastlen;
+
+ len -= lastlen;
+ if (!len)
+ break;
+ csio_wr_inval_flq_buf(hw, flq);
+ }
+
+ flb.defer_free = flq->un.fl.packen ? 0 : 1;
+
+ iq_handler(hw, wr, q->wr_sz - sizeof(struct csio_iqwr_footer),
+ &flb, priv);
+
+ if (flq->un.fl.packen)
+ flq->un.fl.offset += ALIGN(lastlen, sge->csio_fl_align);
+ else
+ csio_wr_inval_flq_buf(hw, flq);
+
+}
+
+/*
+ * csio_is_new_iqwr - Is this a new Ingress queue entry ?
+ * @q: Ingress quueue.
+ * @ftr: Ingress queue WR SGE footer.
+ *
+ * The entry is new if our generation bit matches the corresponding
+ * bit in the footer of the current WR.
+ */
+static inline bool
+csio_is_new_iqwr(struct csio_q *q, struct csio_iqwr_footer *ftr)
+{
+ return (q->un.iq.genbit == (ftr->u.type_gen >> IQWRF_GEN_SHIFT));
+}
+
+/*
+ * csio_wr_process_iq - Process elements in Ingress queue.
+ * @hw: HW pointer
+ * @qidx: Index of queue
+ * @iq_handler: Handler for this queue
+ * @priv: Caller's private pointer
+ *
+ * This routine walks through every entry of the ingress queue, calling
+ * the provided iq_handler with the entry, until the generation bit
+ * flips.
+ */
+int
+csio_wr_process_iq(struct csio_hw *hw, struct csio_q *q,
+ void (*iq_handler)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *priv)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ void *wr = (void *)((uintptr_t)q->vstart + (q->cidx * q->wr_sz));
+ struct csio_iqwr_footer *ftr;
+ uint32_t wr_type, fw_qid, qid;
+ struct csio_q *q_completed;
+ struct csio_q *flq = csio_iq_has_fl(q) ?
+ wrm->q_arr[q->un.iq.flq_idx] : NULL;
+ int rv = 0;
+
+ /* Get the footer */
+ ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
+ (q->wr_sz - sizeof(*ftr)));
+
+ /*
+ * When q wrapped around last time, driver should have inverted
+ * ic.genbit as well.
+ */
+ while (csio_is_new_iqwr(q, ftr)) {
+
+ CSIO_DB_ASSERT(((uintptr_t)wr + q->wr_sz) <=
+ (uintptr_t)q->vwrap);
+ rmb();
+ wr_type = IQWRF_TYPE_GET(ftr->u.type_gen);
+
+ switch (wr_type) {
+ case X_RSPD_TYPE_CPL:
+ /* Subtract footer from WR len */
+ iq_handler(hw, wr, q->wr_sz - sizeof(*ftr), NULL, priv);
+ break;
+ case X_RSPD_TYPE_FLBUF:
+ csio_wr_process_fl(hw, q, wr,
+ ntohl(ftr->pldbuflen_qid),
+ iq_handler, priv);
+ break;
+ case X_RSPD_TYPE_INTR:
+ fw_qid = ntohl(ftr->pldbuflen_qid);
+ qid = fw_qid - wrm->fw_iq_start;
+ q_completed = hw->wrm.intr_map[qid];
+
+ if (unlikely(qid ==
+ csio_q_physiqid(hw, hw->intr_iq_idx))) {
+ /*
+ * We are already in the Forward Interrupt
+ * Interrupt Queue Service! Do-not service
+ * again!
+ *
+ */
+ } else {
+ CSIO_DB_ASSERT(q_completed);
+ CSIO_DB_ASSERT(
+ q_completed->un.iq.iq_intx_handler);
+
+ /* Call the queue handler. */
+ q_completed->un.iq.iq_intx_handler(hw, NULL,
+ 0, NULL, (void *)q_completed);
+ }
+ break;
+ default:
+ csio_warn(hw, "Unknown resp type 0x%x received\n",
+ wr_type);
+ CSIO_INC_STATS(q, n_rsp_unknown);
+ break;
+ }
+
+ /*
+ * Ingress *always* has fixed size WR entries. Therefore,
+ * there should always be complete WRs towards the end of
+ * queue.
+ */
+ if (((uintptr_t)wr + q->wr_sz) == (uintptr_t)q->vwrap) {
+
+ /* Roll over to start of queue */
+ q->cidx = 0;
+ wr = q->vstart;
+
+ /* Toggle genbit */
+ q->un.iq.genbit ^= 0x1;
+
+ CSIO_INC_STATS(q, n_qwrap);
+ } else {
+ q->cidx++;
+ wr = (void *)((uintptr_t)(q->vstart) +
+ (q->cidx * q->wr_sz));
+ }
+
+ ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
+ (q->wr_sz - sizeof(*ftr)));
+ q->inc_idx++;
+
+ } /* while (q->un.iq.genbit == hdr->genbit) */
+
+ /*
+ * We need to re-arm SGE interrupts in case we got a stray interrupt,
+ * especially in msix mode. With INTx, this may be a common occurrence.
+ */
+ if (unlikely(!q->inc_idx)) {
+ CSIO_INC_STATS(q, n_stray_comp);
+ rv = -EINVAL;
+ goto restart;
+ }
+
+ /* Replenish free list buffers if pending falls below low water mark */
+ if (flq) {
+ uint32_t avail = csio_wr_avail_qcredits(flq);
+ if (avail <= 16) {
+ /* Make sure in FLQ, atleast 1 credit (8 FL buffers)
+ * remains unpopulated otherwise HW thinks
+ * FLQ is empty.
+ */
+ csio_wr_update_fl(hw, flq, (flq->credits - 8) - avail);
+ csio_wr_ring_fldb(hw, flq);
+ }
+ }
+
+restart:
+ /* Now inform SGE about our incremental index value */
+ csio_wr_reg32(hw, CIDXINC_V(q->inc_idx) |
+ INGRESSQID_V(q->un.iq.physiqid) |
+ TIMERREG_V(csio_sge_timer_reg),
+ MYPF_REG(SGE_PF_GTS_A));
+ q->stats.n_tot_rsps += q->inc_idx;
+
+ q->inc_idx = 0;
+
+ return rv;
+}
+
+int
+csio_wr_process_iq_idx(struct csio_hw *hw, int qidx,
+ void (*iq_handler)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *priv)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *iq = wrm->q_arr[qidx];
+
+ return csio_wr_process_iq(hw, iq, iq_handler, priv);
+}
+
+static int
+csio_closest_timer(struct csio_sge *s, int time)
+{
+ int i, delta, match = 0, min_delta = INT_MAX;
+
+ for (i = 0; i < ARRAY_SIZE(s->timer_val); i++) {
+ delta = time - s->timer_val[i];
+ if (delta < 0)
+ delta = -delta;
+ if (delta < min_delta) {
+ min_delta = delta;
+ match = i;
+ }
+ }
+ return match;
+}
+
+static int
+csio_closest_thresh(struct csio_sge *s, int cnt)
+{
+ int i, delta, match = 0, min_delta = INT_MAX;
+
+ for (i = 0; i < ARRAY_SIZE(s->counter_val); i++) {
+ delta = cnt - s->counter_val[i];
+ if (delta < 0)
+ delta = -delta;
+ if (delta < min_delta) {
+ min_delta = delta;
+ match = i;
+ }
+ }
+ return match;
+}
+
+static void
+csio_wr_fixup_host_params(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ uint32_t clsz = L1_CACHE_BYTES;
+ uint32_t s_hps = PAGE_SHIFT - 10;
+ uint32_t stat_len = clsz > 64 ? 128 : 64;
+ u32 fl_align = clsz < 32 ? 32 : clsz;
+ u32 pack_align;
+ u32 ingpad, ingpack;
+
+ csio_wr_reg32(hw, HOSTPAGESIZEPF0_V(s_hps) | HOSTPAGESIZEPF1_V(s_hps) |
+ HOSTPAGESIZEPF2_V(s_hps) | HOSTPAGESIZEPF3_V(s_hps) |
+ HOSTPAGESIZEPF4_V(s_hps) | HOSTPAGESIZEPF5_V(s_hps) |
+ HOSTPAGESIZEPF6_V(s_hps) | HOSTPAGESIZEPF7_V(s_hps),
+ SGE_HOST_PAGE_SIZE_A);
+
+ /* T5 introduced the separation of the Free List Padding and
+ * Packing Boundaries. Thus, we can select a smaller Padding
+ * Boundary to avoid uselessly chewing up PCIe Link and Memory
+ * Bandwidth, and use a Packing Boundary which is large enough
+ * to avoid false sharing between CPUs, etc.
+ *
+ * For the PCI Link, the smaller the Padding Boundary the
+ * better. For the Memory Controller, a smaller Padding
+ * Boundary is better until we cross under the Memory Line
+ * Size (the minimum unit of transfer to/from Memory). If we
+ * have a Padding Boundary which is smaller than the Memory
+ * Line Size, that'll involve a Read-Modify-Write cycle on the
+ * Memory Controller which is never good.
+ */
+
+ /* We want the Packing Boundary to be based on the Cache Line
+ * Size in order to help avoid False Sharing performance
+ * issues between CPUs, etc. We also want the Packing
+ * Boundary to incorporate the PCI-E Maximum Payload Size. We
+ * get best performance when the Packing Boundary is a
+ * multiple of the Maximum Payload Size.
+ */
+ pack_align = fl_align;
+ if (pci_is_pcie(hw->pdev)) {
+ u32 mps, mps_log;
+ u16 devctl;
+
+ /* The PCIe Device Control Maximum Payload Size field
+ * [bits 7:5] encodes sizes as powers of 2 starting at
+ * 128 bytes.
+ */
+ pcie_capability_read_word(hw->pdev, PCI_EXP_DEVCTL, &devctl);
+ mps_log = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5) + 7;
+ mps = 1 << mps_log;
+ if (mps > pack_align)
+ pack_align = mps;
+ }
+
+ /* T5/T6 have a special interpretation of the "0"
+ * value for the Packing Boundary. This corresponds to 16
+ * bytes instead of the expected 32 bytes.
+ */
+ if (pack_align <= 16) {
+ ingpack = INGPACKBOUNDARY_16B_X;
+ fl_align = 16;
+ } else if (pack_align == 32) {
+ ingpack = INGPACKBOUNDARY_64B_X;
+ fl_align = 64;
+ } else {
+ u32 pack_align_log = fls(pack_align) - 1;
+
+ ingpack = pack_align_log - INGPACKBOUNDARY_SHIFT_X;
+ fl_align = pack_align;
+ }
+
+ /* Use the smallest Ingress Padding which isn't smaller than
+ * the Memory Controller Read/Write Size. We'll take that as
+ * being 8 bytes since we don't know of any system with a
+ * wider Memory Controller Bus Width.
+ */
+ if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
+ ingpad = INGPADBOUNDARY_32B_X;
+ else
+ ingpad = T6_INGPADBOUNDARY_8B_X;
+
+ csio_set_reg_field(hw, SGE_CONTROL_A,
+ INGPADBOUNDARY_V(INGPADBOUNDARY_M) |
+ EGRSTATUSPAGESIZE_F,
+ INGPADBOUNDARY_V(ingpad) |
+ EGRSTATUSPAGESIZE_V(stat_len != 64));
+ csio_set_reg_field(hw, SGE_CONTROL2_A,
+ INGPACKBOUNDARY_V(INGPACKBOUNDARY_M),
+ INGPACKBOUNDARY_V(ingpack));
+
+ /* FL BUFFER SIZE#0 is Page size i,e already aligned to cache line */
+ csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0_A);
+
+ /*
+ * If using hard params, the following will get set correctly
+ * in csio_wr_set_sge().
+ */
+ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS) {
+ csio_wr_reg32(hw,
+ (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2_A) +
+ fl_align - 1) & ~(fl_align - 1),
+ SGE_FL_BUFFER_SIZE2_A);
+ csio_wr_reg32(hw,
+ (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3_A) +
+ fl_align - 1) & ~(fl_align - 1),
+ SGE_FL_BUFFER_SIZE3_A);
+ }
+
+ sge->csio_fl_align = fl_align;
+
+ csio_wr_reg32(hw, HPZ0_V(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ_A);
+
+ /* default value of rx_dma_offset of the NIC driver */
+ csio_set_reg_field(hw, SGE_CONTROL_A,
+ PKTSHIFT_V(PKTSHIFT_M),
+ PKTSHIFT_V(CSIO_SGE_RX_DMA_OFFSET));
+
+ csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG_A,
+ CSUM_HAS_PSEUDO_HDR_F, 0);
+}
+
+static void
+csio_init_intr_coalesce_parms(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+
+ csio_sge_thresh_reg = csio_closest_thresh(sge, csio_intr_coalesce_cnt);
+ if (csio_intr_coalesce_cnt) {
+ csio_sge_thresh_reg = 0;
+ csio_sge_timer_reg = X_TIMERREG_RESTART_COUNTER;
+ return;
+ }
+
+ csio_sge_timer_reg = csio_closest_timer(sge, csio_intr_coalesce_time);
+}
+
+/*
+ * csio_wr_get_sge - Get SGE register values.
+ * @hw: HW module.
+ *
+ * Used by non-master functions and by master-functions relying on config file.
+ */
+static void
+csio_wr_get_sge(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ uint32_t ingpad;
+ int i;
+ u32 timer_value_0_and_1, timer_value_2_and_3, timer_value_4_and_5;
+ u32 ingress_rx_threshold;
+
+ sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A);
+
+ ingpad = INGPADBOUNDARY_G(sge->sge_control);
+
+ switch (ingpad) {
+ case X_INGPCIEBOUNDARY_32B:
+ sge->csio_fl_align = 32; break;
+ case X_INGPCIEBOUNDARY_64B:
+ sge->csio_fl_align = 64; break;
+ case X_INGPCIEBOUNDARY_128B:
+ sge->csio_fl_align = 128; break;
+ case X_INGPCIEBOUNDARY_256B:
+ sge->csio_fl_align = 256; break;
+ case X_INGPCIEBOUNDARY_512B:
+ sge->csio_fl_align = 512; break;
+ case X_INGPCIEBOUNDARY_1024B:
+ sge->csio_fl_align = 1024; break;
+ case X_INGPCIEBOUNDARY_2048B:
+ sge->csio_fl_align = 2048; break;
+ case X_INGPCIEBOUNDARY_4096B:
+ sge->csio_fl_align = 4096; break;
+ }
+
+ for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++)
+ csio_get_flbuf_size(hw, sge, i);
+
+ timer_value_0_and_1 = csio_rd_reg32(hw, SGE_TIMER_VALUE_0_AND_1_A);
+ timer_value_2_and_3 = csio_rd_reg32(hw, SGE_TIMER_VALUE_2_AND_3_A);
+ timer_value_4_and_5 = csio_rd_reg32(hw, SGE_TIMER_VALUE_4_AND_5_A);
+
+ sge->timer_val[0] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE0_G(timer_value_0_and_1));
+ sge->timer_val[1] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE1_G(timer_value_0_and_1));
+ sge->timer_val[2] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE2_G(timer_value_2_and_3));
+ sge->timer_val[3] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE3_G(timer_value_2_and_3));
+ sge->timer_val[4] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE4_G(timer_value_4_and_5));
+ sge->timer_val[5] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE5_G(timer_value_4_and_5));
+
+ ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD_A);
+ sge->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold);
+ sge->counter_val[1] = THRESHOLD_1_G(ingress_rx_threshold);
+ sge->counter_val[2] = THRESHOLD_2_G(ingress_rx_threshold);
+ sge->counter_val[3] = THRESHOLD_3_G(ingress_rx_threshold);
+
+ csio_init_intr_coalesce_parms(hw);
+}
+
+/*
+ * csio_wr_set_sge - Initialize SGE registers
+ * @hw: HW module.
+ *
+ * Used by Master function to initialize SGE registers in the absence
+ * of a config file.
+ */
+static void
+csio_wr_set_sge(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ int i;
+
+ /*
+ * Set up our basic SGE mode to deliver CPL messages to our Ingress
+ * Queue and Packet Date to the Free List.
+ */
+ csio_set_reg_field(hw, SGE_CONTROL_A, RXPKTCPLMODE_F, RXPKTCPLMODE_F);
+
+ sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A);
+
+ /* sge->csio_fl_align is set up by csio_wr_fixup_host_params(). */
+
+ /*
+ * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows
+ * and generate an interrupt when this occurs so we can recover.
+ */
+ csio_set_reg_field(hw, SGE_DBFIFO_STATUS_A,
+ LP_INT_THRESH_T5_V(LP_INT_THRESH_T5_M),
+ LP_INT_THRESH_T5_V(CSIO_SGE_DBFIFO_INT_THRESH));
+ csio_set_reg_field(hw, SGE_DBFIFO_STATUS2_A,
+ HP_INT_THRESH_T5_V(LP_INT_THRESH_T5_M),
+ HP_INT_THRESH_T5_V(CSIO_SGE_DBFIFO_INT_THRESH));
+
+ csio_set_reg_field(hw, SGE_DOORBELL_CONTROL_A, ENABLE_DROP_F,
+ ENABLE_DROP_F);
+
+ /* SGE_FL_BUFFER_SIZE0 is set up by csio_wr_fixup_host_params(). */
+
+ CSIO_SET_FLBUF_SIZE(hw, 1, CSIO_SGE_FLBUF_SIZE1);
+ csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE2 + sge->csio_fl_align - 1)
+ & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE2_A);
+ csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE3 + sge->csio_fl_align - 1)
+ & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE3_A);
+ CSIO_SET_FLBUF_SIZE(hw, 4, CSIO_SGE_FLBUF_SIZE4);
+ CSIO_SET_FLBUF_SIZE(hw, 5, CSIO_SGE_FLBUF_SIZE5);
+ CSIO_SET_FLBUF_SIZE(hw, 6, CSIO_SGE_FLBUF_SIZE6);
+ CSIO_SET_FLBUF_SIZE(hw, 7, CSIO_SGE_FLBUF_SIZE7);
+ CSIO_SET_FLBUF_SIZE(hw, 8, CSIO_SGE_FLBUF_SIZE8);
+
+ for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++)
+ csio_get_flbuf_size(hw, sge, i);
+
+ /* Initialize interrupt coalescing attributes */
+ sge->timer_val[0] = CSIO_SGE_TIMER_VAL_0;
+ sge->timer_val[1] = CSIO_SGE_TIMER_VAL_1;
+ sge->timer_val[2] = CSIO_SGE_TIMER_VAL_2;
+ sge->timer_val[3] = CSIO_SGE_TIMER_VAL_3;
+ sge->timer_val[4] = CSIO_SGE_TIMER_VAL_4;
+ sge->timer_val[5] = CSIO_SGE_TIMER_VAL_5;
+
+ sge->counter_val[0] = CSIO_SGE_INT_CNT_VAL_0;
+ sge->counter_val[1] = CSIO_SGE_INT_CNT_VAL_1;
+ sge->counter_val[2] = CSIO_SGE_INT_CNT_VAL_2;
+ sge->counter_val[3] = CSIO_SGE_INT_CNT_VAL_3;
+
+ csio_wr_reg32(hw, THRESHOLD_0_V(sge->counter_val[0]) |
+ THRESHOLD_1_V(sge->counter_val[1]) |
+ THRESHOLD_2_V(sge->counter_val[2]) |
+ THRESHOLD_3_V(sge->counter_val[3]),
+ SGE_INGRESS_RX_THRESHOLD_A);
+
+ csio_wr_reg32(hw,
+ TIMERVALUE0_V(csio_us_to_core_ticks(hw, sge->timer_val[0])) |
+ TIMERVALUE1_V(csio_us_to_core_ticks(hw, sge->timer_val[1])),
+ SGE_TIMER_VALUE_0_AND_1_A);
+
+ csio_wr_reg32(hw,
+ TIMERVALUE2_V(csio_us_to_core_ticks(hw, sge->timer_val[2])) |
+ TIMERVALUE3_V(csio_us_to_core_ticks(hw, sge->timer_val[3])),
+ SGE_TIMER_VALUE_2_AND_3_A);
+
+ csio_wr_reg32(hw,
+ TIMERVALUE4_V(csio_us_to_core_ticks(hw, sge->timer_val[4])) |
+ TIMERVALUE5_V(csio_us_to_core_ticks(hw, sge->timer_val[5])),
+ SGE_TIMER_VALUE_4_AND_5_A);
+
+ csio_init_intr_coalesce_parms(hw);
+}
+
+void
+csio_wr_sge_init(struct csio_hw *hw)
+{
+ /*
+ * If we are master and chip is not initialized:
+ * - If we plan to use the config file, we need to fixup some
+ * host specific registers, and read the rest of the SGE
+ * configuration.
+ * - If we dont plan to use the config file, we need to initialize
+ * SGE entirely, including fixing the host specific registers.
+ * If we are master and chip is initialized, just read and work off of
+ * the already initialized SGE values.
+ * If we arent the master, we are only allowed to read and work off of
+ * the already initialized SGE values.
+ *
+ * Therefore, before calling this function, we assume that the master-
+ * ship of the card, state and whether to use config file or not, have
+ * already been decided.
+ */
+ if (csio_is_hw_master(hw)) {
+ if (hw->fw_state != CSIO_DEV_STATE_INIT)
+ csio_wr_fixup_host_params(hw);
+
+ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS)
+ csio_wr_get_sge(hw);
+ else
+ csio_wr_set_sge(hw);
+ } else
+ csio_wr_get_sge(hw);
+}
+
+/*
+ * csio_wrm_init - Initialize Work request module.
+ * @wrm: WR module
+ * @hw: HW pointer
+ *
+ * Allocates memory for an array of queue pointers starting at q_arr.
+ */
+int
+csio_wrm_init(struct csio_wrm *wrm, struct csio_hw *hw)
+{
+ int i;
+
+ if (!wrm->num_q) {
+ csio_err(hw, "Num queues is not set\n");
+ return -EINVAL;
+ }
+
+ wrm->q_arr = kcalloc(wrm->num_q, sizeof(struct csio_q *), GFP_KERNEL);
+ if (!wrm->q_arr)
+ goto err;
+
+ for (i = 0; i < wrm->num_q; i++) {
+ wrm->q_arr[i] = kzalloc(sizeof(struct csio_q), GFP_KERNEL);
+ if (!wrm->q_arr[i]) {
+ while (--i >= 0)
+ kfree(wrm->q_arr[i]);
+ goto err_free_arr;
+ }
+ }
+ wrm->free_qidx = 0;
+
+ return 0;
+
+err_free_arr:
+ kfree(wrm->q_arr);
+err:
+ return -ENOMEM;
+}
+
+/*
+ * csio_wrm_exit - Initialize Work request module.
+ * @wrm: WR module
+ * @hw: HW module
+ *
+ * Uninitialize WR module. Free q_arr and pointers in it.
+ * We have the additional job of freeing the DMA memory associated
+ * with the queues.
+ */
+void
+csio_wrm_exit(struct csio_wrm *wrm, struct csio_hw *hw)
+{
+ int i;
+ uint32_t j;
+ struct csio_q *q;
+ struct csio_dma_buf *buf;
+
+ for (i = 0; i < wrm->num_q; i++) {
+ q = wrm->q_arr[i];
+
+ if (wrm->free_qidx && (i < wrm->free_qidx)) {
+ if (q->type == CSIO_FREELIST) {
+ if (!q->un.fl.bufs)
+ continue;
+ for (j = 0; j < q->credits; j++) {
+ buf = &q->un.fl.bufs[j];
+ if (!buf->vaddr)
+ continue;
+ dma_free_coherent(&hw->pdev->dev,
+ buf->len, buf->vaddr,
+ buf->paddr);
+ }
+ kfree(q->un.fl.bufs);
+ }
+ dma_free_coherent(&hw->pdev->dev, q->size,
+ q->vstart, q->pstart);
+ }
+ kfree(q);
+ }
+
+ hw->flags &= ~CSIO_HWF_Q_MEM_ALLOCED;
+
+ kfree(wrm->q_arr);
+}
diff --git a/drivers/scsi/csiostor/csio_wr.h b/drivers/scsi/csiostor/csio_wr.h
new file mode 100644
index 000000000000..0c0dd9a658cc
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_wr.h
@@ -0,0 +1,512 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_WR_H__
+#define __CSIO_WR_H__
+
+#include <linux/cache.h>
+
+#include "csio_defs.h"
+#include "t4fw_api.h"
+#include "t4fw_api_stor.h"
+
+/*
+ * SGE register field values.
+ */
+#define X_INGPCIEBOUNDARY_32B 0
+#define X_INGPCIEBOUNDARY_64B 1
+#define X_INGPCIEBOUNDARY_128B 2
+#define X_INGPCIEBOUNDARY_256B 3
+#define X_INGPCIEBOUNDARY_512B 4
+#define X_INGPCIEBOUNDARY_1024B 5
+#define X_INGPCIEBOUNDARY_2048B 6
+#define X_INGPCIEBOUNDARY_4096B 7
+
+/* GTS register */
+#define X_TIMERREG_COUNTER0 0
+#define X_TIMERREG_COUNTER1 1
+#define X_TIMERREG_COUNTER2 2
+#define X_TIMERREG_COUNTER3 3
+#define X_TIMERREG_COUNTER4 4
+#define X_TIMERREG_COUNTER5 5
+#define X_TIMERREG_RESTART_COUNTER 6
+#define X_TIMERREG_UPDATE_CIDX 7
+
+/*
+ * Egress Context field values
+ */
+#define X_FETCHBURSTMIN_16B 0
+#define X_FETCHBURSTMIN_32B 1
+#define X_FETCHBURSTMIN_64B 2
+#define X_FETCHBURSTMIN_128B 3
+
+#define X_FETCHBURSTMAX_64B 0
+#define X_FETCHBURSTMAX_128B 1
+#define X_FETCHBURSTMAX_256B 2
+#define X_FETCHBURSTMAX_512B 3
+
+#define X_HOSTFCMODE_NONE 0
+#define X_HOSTFCMODE_INGRESS_QUEUE 1
+#define X_HOSTFCMODE_STATUS_PAGE 2
+#define X_HOSTFCMODE_BOTH 3
+
+/*
+ * Ingress Context field values
+ */
+#define X_UPDATESCHEDULING_TIMER 0
+#define X_UPDATESCHEDULING_COUNTER_OPTTIMER 1
+
+#define X_UPDATEDELIVERY_NONE 0
+#define X_UPDATEDELIVERY_INTERRUPT 1
+#define X_UPDATEDELIVERY_STATUS_PAGE 2
+#define X_UPDATEDELIVERY_BOTH 3
+
+#define X_INTERRUPTDESTINATION_PCIE 0
+#define X_INTERRUPTDESTINATION_IQ 1
+
+#define X_RSPD_TYPE_FLBUF 0
+#define X_RSPD_TYPE_CPL 1
+#define X_RSPD_TYPE_INTR 2
+
+/* WR status is at the same position as retval in a CMD header */
+#define csio_wr_status(_wr) \
+ (FW_CMD_RETVAL_G(ntohl(((struct fw_cmd_hdr *)(_wr))->lo)))
+
+struct csio_hw;
+
+extern int csio_intr_coalesce_cnt;
+extern int csio_intr_coalesce_time;
+
+/* Ingress queue params */
+struct csio_iq_params {
+
+ uint8_t iq_start:1;
+ uint8_t iq_stop:1;
+ uint8_t pfn:3;
+
+ uint8_t vfn;
+
+ uint16_t physiqid;
+ uint16_t iqid;
+
+ uint16_t fl0id;
+ uint16_t fl1id;
+
+ uint8_t viid;
+
+ uint8_t type;
+ uint8_t iqasynch;
+ uint8_t reserved4;
+
+ uint8_t iqandst;
+ uint8_t iqanus;
+ uint8_t iqanud;
+
+ uint16_t iqandstindex;
+
+ uint8_t iqdroprss;
+ uint8_t iqpciech;
+ uint8_t iqdcaen;
+
+ uint8_t iqdcacpu;
+ uint8_t iqintcntthresh;
+ uint8_t iqo;
+
+ uint8_t iqcprio;
+ uint8_t iqesize;
+
+ uint16_t iqsize;
+
+ uint64_t iqaddr;
+
+ uint8_t iqflintiqhsen;
+ uint8_t reserved5;
+ uint8_t iqflintcongen;
+ uint8_t iqflintcngchmap;
+
+ uint32_t reserved6;
+
+ uint8_t fl0hostfcmode;
+ uint8_t fl0cprio;
+ uint8_t fl0paden;
+ uint8_t fl0packen;
+ uint8_t fl0congen;
+ uint8_t fl0dcaen;
+
+ uint8_t fl0dcacpu;
+ uint8_t fl0fbmin;
+
+ uint8_t fl0fbmax;
+ uint8_t fl0cidxfthresho;
+ uint8_t fl0cidxfthresh;
+
+ uint16_t fl0size;
+
+ uint64_t fl0addr;
+
+ uint64_t reserved7;
+
+ uint8_t fl1hostfcmode;
+ uint8_t fl1cprio;
+ uint8_t fl1paden;
+ uint8_t fl1packen;
+ uint8_t fl1congen;
+ uint8_t fl1dcaen;
+
+ uint8_t fl1dcacpu;
+ uint8_t fl1fbmin;
+
+ uint8_t fl1fbmax;
+ uint8_t fl1cidxfthresho;
+ uint8_t fl1cidxfthresh;
+
+ uint16_t fl1size;
+
+ uint64_t fl1addr;
+};
+
+/* Egress queue params */
+struct csio_eq_params {
+
+ uint8_t pfn;
+ uint8_t vfn;
+
+ uint8_t eqstart:1;
+ uint8_t eqstop:1;
+
+ uint16_t physeqid;
+ uint32_t eqid;
+
+ uint8_t hostfcmode:2;
+ uint8_t cprio:1;
+ uint8_t pciechn:3;
+
+ uint16_t iqid;
+
+ uint8_t dcaen:1;
+ uint8_t dcacpu:5;
+
+ uint8_t fbmin:3;
+ uint8_t fbmax:3;
+
+ uint8_t cidxfthresho:1;
+ uint8_t cidxfthresh:3;
+
+ uint16_t eqsize;
+
+ uint64_t eqaddr;
+};
+
+struct csio_dma_buf {
+ struct list_head list;
+ void *vaddr; /* Virtual address */
+ dma_addr_t paddr; /* Physical address */
+ uint32_t len; /* Buffer size */
+};
+
+/* Generic I/O request structure */
+struct csio_ioreq {
+ struct csio_sm sm; /* SM, List
+ * should be the first member
+ */
+ int iq_idx; /* Ingress queue index */
+ int eq_idx; /* Egress queue index */
+ uint32_t nsge; /* Number of SG elements */
+ uint32_t tmo; /* Driver timeout */
+ uint32_t datadir; /* Data direction */
+ struct csio_dma_buf dma_buf; /* Req/resp DMA buffers */
+ uint16_t wr_status; /* WR completion status */
+ int16_t drv_status; /* Driver internal status */
+ struct csio_lnode *lnode; /* Owner lnode */
+ struct csio_rnode *rnode; /* Src/destination rnode */
+ void (*io_cbfn) (struct csio_hw *, struct csio_ioreq *);
+ /* completion callback */
+ void *scratch1; /* Scratch area 1.
+ */
+ void *scratch2; /* Scratch area 2. */
+ struct list_head gen_list; /* Any list associated with
+ * this ioreq.
+ */
+ uint64_t fw_handle; /* Unique handle passed
+ * to FW
+ */
+ uint8_t dcopy; /* Data copy required */
+ uint8_t reserved1;
+ uint16_t reserved2;
+ struct completion cmplobj; /* ioreq completion object */
+} ____cacheline_aligned_in_smp;
+
+/*
+ * Egress status page for egress cidx updates
+ */
+struct csio_qstatus_page {
+ __be32 qid;
+ __be16 cidx;
+ __be16 pidx;
+};
+
+
+enum {
+ CSIO_MAX_FLBUF_PER_IQWR = 4,
+ CSIO_QCREDIT_SZ = 64, /* pidx/cidx increments
+ * in bytes
+ */
+ CSIO_MAX_QID = 0xFFFF,
+ CSIO_MAX_IQ = 128,
+
+ CSIO_SGE_NTIMERS = 6,
+ CSIO_SGE_NCOUNTERS = 4,
+ CSIO_SGE_FL_SIZE_REGS = 16,
+};
+
+/* Defines for type */
+enum {
+ CSIO_EGRESS = 1,
+ CSIO_INGRESS = 2,
+ CSIO_FREELIST = 3,
+};
+
+/*
+ * Structure for footer (last 2 flits) of Ingress Queue Entry.
+ */
+struct csio_iqwr_footer {
+ __be32 hdrbuflen_pidx;
+ __be32 pldbuflen_qid;
+ union {
+ u8 type_gen;
+ __be64 last_flit;
+ } u;
+};
+
+#define IQWRF_NEWBUF (1 << 31)
+#define IQWRF_LEN_GET(x) (((x) >> 0) & 0x7fffffffU)
+#define IQWRF_GEN_SHIFT 7
+#define IQWRF_TYPE_GET(x) (((x) >> 4) & 0x3U)
+
+
+/*
+ * WR pair:
+ * ========
+ * A WR can start towards the end of a queue, and then continue at the
+ * beginning, since the queue is considered to be circular. This will
+ * require a pair of address/len to be passed back to the caller -
+ * hence the Work request pair structure.
+ */
+struct csio_wr_pair {
+ void *addr1;
+ uint32_t size1;
+ void *addr2;
+ uint32_t size2;
+};
+
+/*
+ * The following structure is used by ingress processing to return the
+ * free list buffers to consumers.
+ */
+struct csio_fl_dma_buf {
+ struct csio_dma_buf flbufs[CSIO_MAX_FLBUF_PER_IQWR];
+ /* Freelist DMA buffers */
+ int offset; /* Offset within the
+ * first FL buf.
+ */
+ uint32_t totlen; /* Total length */
+ uint8_t defer_free; /* Free of buffer can
+ * deferred
+ */
+};
+
+/* Data-types */
+typedef void (*iq_handler_t)(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *, void *);
+
+struct csio_iq {
+ uint16_t iqid; /* Queue ID */
+ uint16_t physiqid; /* Physical Queue ID */
+ uint16_t genbit; /* Generation bit,
+ * initially set to 1
+ */
+ int flq_idx; /* Freelist queue index */
+ iq_handler_t iq_intx_handler; /* IQ INTx handler routine */
+};
+
+struct csio_eq {
+ uint16_t eqid; /* Qid */
+ uint16_t physeqid; /* Physical Queue ID */
+ uint8_t wrap[512]; /* Temp area for q-wrap around*/
+};
+
+struct csio_fl {
+ uint16_t flid; /* Qid */
+ uint16_t packen; /* Packing enabled? */
+ int offset; /* Offset within FL buf */
+ int sreg; /* Size register */
+ struct csio_dma_buf *bufs; /* Free list buffer ptr array
+ * indexed using flq->cidx/pidx
+ */
+};
+
+struct csio_qstats {
+ uint32_t n_tot_reqs; /* Total no. of Requests */
+ uint32_t n_tot_rsps; /* Total no. of responses */
+ uint32_t n_qwrap; /* Queue wraps */
+ uint32_t n_eq_wr_split; /* Number of split EQ WRs */
+ uint32_t n_qentry; /* Queue entry */
+ uint32_t n_qempty; /* Queue empty */
+ uint32_t n_qfull; /* Queue fulls */
+ uint32_t n_rsp_unknown; /* Unknown response type */
+ uint32_t n_stray_comp; /* Stray completion intr */
+ uint32_t n_flq_refill; /* Number of FL refills */
+};
+
+/* Queue metadata */
+struct csio_q {
+ uint16_t type; /* Type: Ingress/Egress/FL */
+ uint16_t pidx; /* producer index */
+ uint16_t cidx; /* consumer index */
+ uint16_t inc_idx; /* Incremental index */
+ uint32_t wr_sz; /* Size of all WRs in this q
+ * if fixed
+ */
+ void *vstart; /* Base virtual address
+ * of queue
+ */
+ void *vwrap; /* Virtual end address to
+ * wrap around at
+ */
+ uint32_t credits; /* Size of queue in credits */
+ void *owner; /* Owner */
+ union { /* Queue contexts */
+ struct csio_iq iq;
+ struct csio_eq eq;
+ struct csio_fl fl;
+ } un;
+
+ dma_addr_t pstart; /* Base physical address of
+ * queue
+ */
+ uint32_t portid; /* PCIE Channel */
+ uint32_t size; /* Size of queue in bytes */
+ struct csio_qstats stats; /* Statistics */
+} ____cacheline_aligned_in_smp;
+
+struct csio_sge {
+ uint32_t csio_fl_align; /* Calculated and cached
+ * for fast path
+ */
+ uint32_t sge_control; /* padding, boundaries,
+ * lengths, etc.
+ */
+ uint32_t sge_host_page_size; /* Host page size */
+ uint32_t sge_fl_buf_size[CSIO_SGE_FL_SIZE_REGS];
+ /* free list buffer sizes */
+ uint16_t timer_val[CSIO_SGE_NTIMERS];
+ uint8_t counter_val[CSIO_SGE_NCOUNTERS];
+};
+
+/* Work request module */
+struct csio_wrm {
+ int num_q; /* Number of queues */
+ struct csio_q **q_arr; /* Array of queue pointers
+ * allocated dynamically
+ * based on configured values
+ */
+ uint32_t fw_iq_start; /* Start ID of IQ for this fn*/
+ uint32_t fw_eq_start; /* Start ID of EQ for this fn*/
+ struct csio_q *intr_map[CSIO_MAX_IQ];
+ /* IQ-id to IQ map table. */
+ int free_qidx; /* queue idx of free queue */
+ struct csio_sge sge; /* SGE params */
+};
+
+#define csio_get_q(__hw, __idx) ((__hw)->wrm.q_arr[__idx])
+#define csio_q_type(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->type)
+#define csio_q_pidx(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->pidx)
+#define csio_q_cidx(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->cidx)
+#define csio_q_inc_idx(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->inc_idx)
+#define csio_q_vstart(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->vstart)
+#define csio_q_pstart(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->pstart)
+#define csio_q_size(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->size)
+#define csio_q_credits(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->credits)
+#define csio_q_portid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->portid)
+#define csio_q_wr_sz(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->wr_sz)
+#define csio_q_iqid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.iq.iqid)
+#define csio_q_physiqid(__hw, __idx) \
+ ((__hw)->wrm.q_arr[(__idx)]->un.iq.physiqid)
+#define csio_q_iq_flq_idx(__hw, __idx) \
+ ((__hw)->wrm.q_arr[(__idx)]->un.iq.flq_idx)
+#define csio_q_eqid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.eq.eqid)
+#define csio_q_flid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.fl.flid)
+
+#define csio_q_physeqid(__hw, __idx) \
+ ((__hw)->wrm.q_arr[(__idx)]->un.eq.physeqid)
+#define csio_iq_has_fl(__iq) ((__iq)->un.iq.flq_idx != -1)
+
+#define csio_q_iq_to_flid(__hw, __iq_idx) \
+ csio_q_flid((__hw), (__hw)->wrm.q_arr[(__iq_qidx)]->un.iq.flq_idx)
+#define csio_q_set_intr_map(__hw, __iq_idx, __rel_iq_id) \
+ (__hw)->wrm.intr_map[__rel_iq_id] = csio_get_q(__hw, __iq_idx)
+#define csio_q_eq_wrap(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.eq.wrap)
+
+struct csio_mb;
+
+int csio_wr_alloc_q(struct csio_hw *, uint32_t, uint32_t,
+ uint16_t, void *, uint32_t, int, iq_handler_t);
+int csio_wr_iq_create(struct csio_hw *, void *, int,
+ uint32_t, uint8_t, bool,
+ void (*)(struct csio_hw *, struct csio_mb *));
+int csio_wr_eq_create(struct csio_hw *, void *, int, int, uint8_t,
+ void (*)(struct csio_hw *, struct csio_mb *));
+int csio_wr_destroy_queues(struct csio_hw *, bool cmd);
+
+
+int csio_wr_get(struct csio_hw *, int, uint32_t,
+ struct csio_wr_pair *);
+void csio_wr_copy_to_wrp(void *, struct csio_wr_pair *, uint32_t, uint32_t);
+int csio_wr_issue(struct csio_hw *, int, bool);
+int csio_wr_process_iq(struct csio_hw *, struct csio_q *,
+ void (*)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *);
+int csio_wr_process_iq_idx(struct csio_hw *, int,
+ void (*)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *);
+
+void csio_wr_sge_init(struct csio_hw *);
+int csio_wrm_init(struct csio_wrm *, struct csio_hw *);
+void csio_wrm_exit(struct csio_wrm *, struct csio_hw *);
+
+#endif /* ifndef __CSIO_WR_H__ */
diff --git a/drivers/scsi/csiostor/t4fw_api_stor.h b/drivers/scsi/csiostor/t4fw_api_stor.h
new file mode 100644
index 000000000000..097e52c0f8e1
--- /dev/null
+++ b/drivers/scsi/csiostor/t4fw_api_stor.h
@@ -0,0 +1,539 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2009-2010 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _T4FW_API_STOR_H_
+#define _T4FW_API_STOR_H_
+
+
+/******************************************************************************
+ * R E T U R N V A L U E S
+ ********************************/
+
+enum fw_fcoe_link_sub_op {
+ FCOE_LINK_DOWN = 0x0,
+ FCOE_LINK_UP = 0x1,
+ FCOE_LINK_COND = 0x2,
+};
+
+enum fw_fcoe_link_status {
+ FCOE_LINKDOWN = 0x0,
+ FCOE_LINKUP = 0x1,
+};
+
+enum fw_ofld_prot {
+ PROT_FCOE = 0x1,
+ PROT_ISCSI = 0x2,
+};
+
+enum rport_type_fcoe {
+ FLOGI_VFPORT = 0x1, /* 0xfffffe */
+ FDISC_VFPORT = 0x2, /* 0xfffffe */
+ NS_VNPORT = 0x3, /* 0xfffffc */
+ REG_FC4_VNPORT = 0x4, /* any FC4 type VN_PORT */
+ REG_VNPORT = 0x5, /* 0xfffxxx - non FC4 port in switch */
+ FDMI_VNPORT = 0x6, /* 0xfffffa */
+ FAB_CTLR_VNPORT = 0x7, /* 0xfffffd */
+};
+
+enum event_cause_fcoe {
+ PLOGI_ACC_RCVD = 0x01,
+ PLOGI_RJT_RCVD = 0x02,
+ PLOGI_RCVD = 0x03,
+ PLOGO_RCVD = 0x04,
+ PRLI_ACC_RCVD = 0x05,
+ PRLI_RJT_RCVD = 0x06,
+ PRLI_RCVD = 0x07,
+ PRLO_RCVD = 0x08,
+ NPORT_ID_CHGD = 0x09,
+ FLOGO_RCVD = 0x0a,
+ CLR_VIRT_LNK_RCVD = 0x0b,
+ FLOGI_ACC_RCVD = 0x0c,
+ FLOGI_RJT_RCVD = 0x0d,
+ FDISC_ACC_RCVD = 0x0e,
+ FDISC_RJT_RCVD = 0x0f,
+ FLOGI_TMO_MAX_RETRY = 0x10,
+ IMPL_LOGO_ADISC_ACC = 0x11,
+ IMPL_LOGO_ADISC_RJT = 0x12,
+ IMPL_LOGO_ADISC_CNFLT = 0x13,
+ PRLI_TMO = 0x14,
+ ADISC_TMO = 0x15,
+ RSCN_DEV_LOST = 0x16,
+ SCR_ACC_RCVD = 0x17,
+ ADISC_RJT_RCVD = 0x18,
+ LOGO_SNT = 0x19,
+ PROTO_ERR_IMPL_LOGO = 0x1a,
+};
+
+enum fcoe_cmn_type {
+ FCOE_ELS,
+ FCOE_CT,
+ FCOE_SCSI_CMD,
+ FCOE_UNSOL_ELS,
+};
+
+enum fw_wr_stor_opcodes {
+ FW_RDEV_WR = 0x38,
+ FW_FCOE_ELS_CT_WR = 0x30,
+ FW_SCSI_WRITE_WR = 0x31,
+ FW_SCSI_READ_WR = 0x32,
+ FW_SCSI_CMD_WR = 0x33,
+ FW_SCSI_ABRT_CLS_WR = 0x34,
+};
+
+struct fw_rdev_wr {
+ __be32 op_to_immdlen;
+ __be32 alloc_to_len16;
+ __be64 cookie;
+ u8 protocol;
+ u8 event_cause;
+ u8 cur_state;
+ u8 prev_state;
+ __be32 flags_to_assoc_flowid;
+ union rdev_entry {
+ struct fcoe_rdev_entry {
+ __be32 flowid;
+ u8 protocol;
+ u8 event_cause;
+ u8 flags;
+ u8 rjt_reason;
+ u8 cur_login_st;
+ u8 prev_login_st;
+ __be16 rcv_fr_sz;
+ u8 rd_xfer_rdy_to_rport_type;
+ u8 vft_to_qos;
+ u8 org_proc_assoc_to_acc_rsp_code;
+ u8 enh_disc_to_tgt;
+ u8 wwnn[8];
+ u8 wwpn[8];
+ __be16 iqid;
+ u8 fc_oui[3];
+ u8 r_id[3];
+ } fcoe_rdev;
+ struct iscsi_rdev_entry {
+ __be32 flowid;
+ u8 protocol;
+ u8 event_cause;
+ u8 flags;
+ u8 r3;
+ __be16 iscsi_opts;
+ __be16 tcp_opts;
+ __be16 ip_opts;
+ __be16 max_rcv_len;
+ __be16 max_snd_len;
+ __be16 first_brst_len;
+ __be16 max_brst_len;
+ __be16 r4;
+ __be16 def_time2wait;
+ __be16 def_time2ret;
+ __be16 nop_out_intrvl;
+ __be16 non_scsi_to;
+ __be16 isid;
+ __be16 tsid;
+ __be16 port;
+ __be16 tpgt;
+ u8 r5[6];
+ __be16 iqid;
+ } iscsi_rdev;
+ } u;
+};
+
+#define FW_RDEV_WR_FLOWID_GET(x) (((x) >> 8) & 0xfffff)
+#define FW_RDEV_WR_ASSOC_FLOWID_GET(x) (((x) >> 0) & 0xfffff)
+#define FW_RDEV_WR_RPORT_TYPE_GET(x) (((x) >> 0) & 0x1f)
+#define FW_RDEV_WR_NPIV_GET(x) (((x) >> 6) & 0x1)
+#define FW_RDEV_WR_CLASS_GET(x) (((x) >> 4) & 0x3)
+#define FW_RDEV_WR_TASK_RETRY_ID_GET(x) (((x) >> 5) & 0x1)
+#define FW_RDEV_WR_RETRY_GET(x) (((x) >> 4) & 0x1)
+#define FW_RDEV_WR_CONF_CMPL_GET(x) (((x) >> 3) & 0x1)
+#define FW_RDEV_WR_INI_GET(x) (((x) >> 1) & 0x1)
+#define FW_RDEV_WR_TGT_GET(x) (((x) >> 0) & 0x1)
+
+struct fw_fcoe_els_ct_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 els_ct_type;
+ u8 ctl_pri;
+ u8 cp_en_class;
+ __be16 xfer_cnt;
+ u8 fl_to_sp;
+ u8 l_id[3];
+ u8 r5;
+ u8 r_id[3];
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r6;
+};
+
+#define FW_FCOE_ELS_CT_WR_OPCODE(x) ((x) << 24)
+#define FW_FCOE_ELS_CT_WR_OPCODE_GET(x) (((x) >> 24) & 0xff)
+#define FW_FCOE_ELS_CT_WR_IMMDLEN(x) ((x) << 0)
+#define FW_FCOE_ELS_CT_WR_IMMDLEN_GET(x) (((x) >> 0) & 0xff)
+#define FW_FCOE_ELS_CT_WR_SP(x) ((x) << 0)
+
+struct fw_scsi_write_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 use_xfer_cnt;
+ union fw_scsi_write_priv {
+ struct fcoe_write_priv {
+ u8 ctl_pri;
+ u8 cp_en_class;
+ u8 r3_lo[2];
+ } fcoe;
+ struct iscsi_write_priv {
+ u8 r3[4];
+ } iscsi;
+ } u;
+ __be32 xfer_cnt;
+ __be32 ini_xfer_cnt;
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r4;
+};
+
+#define FW_SCSI_WRITE_WR_IMMDLEN(x) ((x) << 0)
+
+struct fw_scsi_read_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 use_xfer_cnt;
+ union fw_scsi_read_priv {
+ struct fcoe_read_priv {
+ u8 ctl_pri;
+ u8 cp_en_class;
+ u8 r3_lo[2];
+ } fcoe;
+ struct iscsi_read_priv {
+ u8 r3[4];
+ } iscsi;
+ } u;
+ __be32 xfer_cnt;
+ __be32 ini_xfer_cnt;
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r4;
+};
+
+#define FW_SCSI_READ_WR_IMMDLEN(x) ((x) << 0)
+
+struct fw_scsi_cmd_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 r3;
+ union fw_scsi_cmd_priv {
+ struct fcoe_cmd_priv {
+ u8 ctl_pri;
+ u8 cp_en_class;
+ u8 r4_lo[2];
+ } fcoe;
+ struct iscsi_cmd_priv {
+ u8 r4[4];
+ } iscsi;
+ } u;
+ u8 r5[8];
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r6;
+};
+
+#define FW_SCSI_CMD_WR_IMMDLEN(x) ((x) << 0)
+
+#define SCSI_ABORT 0
+#define SCSI_CLOSE 1
+
+struct fw_scsi_abrt_cls_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 sub_opcode_to_chk_all_io;
+ u8 r3[4];
+ u64 t_cookie;
+};
+
+#define FW_SCSI_ABRT_CLS_WR_SUB_OPCODE(x) ((x) << 2)
+#define FW_SCSI_ABRT_CLS_WR_SUB_OPCODE_GET(x) (((x) >> 2) & 0x3f)
+#define FW_SCSI_ABRT_CLS_WR_CHK_ALL_IO(x) ((x) << 0)
+
+enum fw_cmd_stor_opcodes {
+ FW_FCOE_RES_INFO_CMD = 0x31,
+ FW_FCOE_LINK_CMD = 0x32,
+ FW_FCOE_VNP_CMD = 0x33,
+ FW_FCOE_SPARAMS_CMD = 0x35,
+ FW_FCOE_STATS_CMD = 0x37,
+ FW_FCOE_FCF_CMD = 0x38,
+};
+
+struct fw_fcoe_res_info_cmd {
+ __be32 op_to_read;
+ __be32 retval_len16;
+ __be16 e_d_tov;
+ __be16 r_a_tov_seq;
+ __be16 r_a_tov_els;
+ __be16 r_r_tov;
+ __be32 max_xchgs;
+ __be32 max_ssns;
+ __be32 used_xchgs;
+ __be32 used_ssns;
+ __be32 max_fcfs;
+ __be32 max_vnps;
+ __be32 used_fcfs;
+ __be32 used_vnps;
+};
+
+struct fw_fcoe_link_cmd {
+ __be32 op_to_portid;
+ __be32 retval_len16;
+ __be32 sub_opcode_fcfi;
+ u8 r3;
+ u8 lstatus;
+ __be16 flags;
+ u8 r4;
+ u8 set_vlan;
+ __be16 vlan_id;
+ __be32 vnpi_pkd;
+ __be16 r6;
+ u8 phy_mac[6];
+ u8 vnport_wwnn[8];
+ u8 vnport_wwpn[8];
+};
+
+#define FW_FCOE_LINK_CMD_PORTID(x) ((x) << 0)
+#define FW_FCOE_LINK_CMD_PORTID_GET(x) (((x) >> 0) & 0xf)
+#define FW_FCOE_LINK_CMD_SUB_OPCODE(x) ((x) << 24U)
+#define FW_FCOE_LINK_CMD_FCFI(x) ((x) << 0)
+#define FW_FCOE_LINK_CMD_FCFI_GET(x) (((x) >> 0) & 0xffffff)
+#define FW_FCOE_LINK_CMD_VNPI_GET(x) (((x) >> 0) & 0xfffff)
+
+struct fw_fcoe_vnp_cmd {
+ __be32 op_to_fcfi;
+ __be32 alloc_to_len16;
+ __be32 gen_wwn_to_vnpi;
+ __be32 vf_id;
+ __be16 iqid;
+ u8 vnport_mac[6];
+ u8 vnport_wwnn[8];
+ u8 vnport_wwpn[8];
+ u8 cmn_srv_parms[16];
+ u8 clsp_word_0_1[8];
+};
+
+#define FW_FCOE_VNP_CMD_FCFI(x) ((x) << 0)
+#define FW_FCOE_VNP_CMD_ALLOC (1U << 31)
+#define FW_FCOE_VNP_CMD_FREE (1U << 30)
+#define FW_FCOE_VNP_CMD_MODIFY (1U << 29)
+#define FW_FCOE_VNP_CMD_GEN_WWN (1U << 22)
+#define FW_FCOE_VNP_CMD_VFID_EN (1U << 20)
+#define FW_FCOE_VNP_CMD_VNPI(x) ((x) << 0)
+#define FW_FCOE_VNP_CMD_VNPI_GET(x) (((x) >> 0) & 0xfffff)
+
+struct fw_fcoe_sparams_cmd {
+ __be32 op_to_portid;
+ __be32 retval_len16;
+ u8 r3[7];
+ u8 cos;
+ u8 lport_wwnn[8];
+ u8 lport_wwpn[8];
+ u8 cmn_srv_parms[16];
+ u8 cls_srv_parms[16];
+};
+
+#define FW_FCOE_SPARAMS_CMD_PORTID(x) ((x) << 0)
+
+struct fw_fcoe_stats_cmd {
+ __be32 op_to_flowid;
+ __be32 free_to_len16;
+ union fw_fcoe_stats {
+ struct fw_fcoe_stats_ctl {
+ u8 nstats_port;
+ u8 port_valid_ix;
+ __be16 r6;
+ __be32 r7;
+ __be64 stat0;
+ __be64 stat1;
+ __be64 stat2;
+ __be64 stat3;
+ __be64 stat4;
+ __be64 stat5;
+ } ctl;
+ struct fw_fcoe_port_stats {
+ __be64 tx_bcast_bytes;
+ __be64 tx_bcast_frames;
+ __be64 tx_mcast_bytes;
+ __be64 tx_mcast_frames;
+ __be64 tx_ucast_bytes;
+ __be64 tx_ucast_frames;
+ __be64 tx_drop_frames;
+ __be64 tx_offload_bytes;
+ __be64 tx_offload_frames;
+ __be64 rx_bcast_bytes;
+ __be64 rx_bcast_frames;
+ __be64 rx_mcast_bytes;
+ __be64 rx_mcast_frames;
+ __be64 rx_ucast_bytes;
+ __be64 rx_ucast_frames;
+ __be64 rx_err_frames;
+ } port_stats;
+ struct fw_fcoe_fcf_stats {
+ __be32 fip_tx_bytes;
+ __be32 fip_tx_fr;
+ __be64 fcf_ka;
+ __be64 mcast_adv_rcvd;
+ __be16 ucast_adv_rcvd;
+ __be16 sol_sent;
+ __be16 vlan_req;
+ __be16 vlan_rpl;
+ __be16 clr_vlink;
+ __be16 link_down;
+ __be16 link_up;
+ __be16 logo;
+ __be16 flogi_req;
+ __be16 flogi_rpl;
+ __be16 fdisc_req;
+ __be16 fdisc_rpl;
+ __be16 fka_prd_chg;
+ __be16 fc_map_chg;
+ __be16 vfid_chg;
+ u8 no_fka_req;
+ u8 no_vnp;
+ } fcf_stats;
+ struct fw_fcoe_pcb_stats {
+ __be64 tx_bytes;
+ __be64 tx_frames;
+ __be64 rx_bytes;
+ __be64 rx_frames;
+ __be32 vnp_ka;
+ __be32 unsol_els_rcvd;
+ __be64 unsol_cmd_rcvd;
+ __be16 implicit_logo;
+ __be16 flogi_inv_sparm;
+ __be16 fdisc_inv_sparm;
+ __be16 flogi_rjt;
+ __be16 fdisc_rjt;
+ __be16 no_ssn;
+ __be16 mac_flt_fail;
+ __be16 inv_fr_rcvd;
+ } pcb_stats;
+ struct fw_fcoe_scb_stats {
+ __be64 tx_bytes;
+ __be64 tx_frames;
+ __be64 rx_bytes;
+ __be64 rx_frames;
+ __be32 host_abrt_req;
+ __be32 adap_auto_abrt;
+ __be32 adap_abrt_rsp;
+ __be32 host_ios_req;
+ __be16 ssn_offl_ios;
+ __be16 ssn_not_rdy_ios;
+ u8 rx_data_ddp_err;
+ u8 ddp_flt_set_err;
+ __be16 rx_data_fr_err;
+ u8 bad_st_abrt_req;
+ u8 no_io_abrt_req;
+ u8 abort_tmo;
+ u8 abort_tmo_2;
+ __be32 abort_req;
+ u8 no_ppod_res_tmo;
+ u8 bp_tmo;
+ u8 adap_auto_cls;
+ u8 no_io_cls_req;
+ __be32 host_cls_req;
+ __be64 unsol_cmd_rcvd;
+ __be32 plogi_req_rcvd;
+ __be32 prli_req_rcvd;
+ __be16 logo_req_rcvd;
+ __be16 prlo_req_rcvd;
+ __be16 plogi_rjt_rcvd;
+ __be16 prli_rjt_rcvd;
+ __be32 adisc_req_rcvd;
+ __be32 rscn_rcvd;
+ __be32 rrq_req_rcvd;
+ __be32 unsol_els_rcvd;
+ u8 adisc_rjt_rcvd;
+ u8 scr_rjt;
+ u8 ct_rjt;
+ u8 inval_bls_rcvd;
+ __be32 ba_rjt_rcvd;
+ } scb_stats;
+ } u;
+};
+
+#define FW_FCOE_STATS_CMD_FLOWID(x) ((x) << 0)
+#define FW_FCOE_STATS_CMD_FREE (1U << 30)
+#define FW_FCOE_STATS_CMD_NSTATS(x) ((x) << 4)
+#define FW_FCOE_STATS_CMD_PORT(x) ((x) << 0)
+#define FW_FCOE_STATS_CMD_PORT_VALID (1U << 7)
+#define FW_FCOE_STATS_CMD_IX(x) ((x) << 0)
+
+struct fw_fcoe_fcf_cmd {
+ __be32 op_to_fcfi;
+ __be32 retval_len16;
+ __be16 priority_pkd;
+ u8 mac[6];
+ u8 name_id[8];
+ u8 fabric[8];
+ __be16 vf_id;
+ __be16 max_fcoe_size;
+ u8 vlan_id;
+ u8 fc_map[3];
+ __be32 fka_adv;
+ __be32 r6;
+ u8 r7_hi;
+ u8 fpma_to_portid;
+ u8 spma_mac[6];
+ __be64 r8;
+};
+
+#define FW_FCOE_FCF_CMD_FCFI(x) ((x) << 0)
+#define FW_FCOE_FCF_CMD_FCFI_GET(x) (((x) >> 0) & 0xfffff)
+#define FW_FCOE_FCF_CMD_PRIORITY_GET(x) (((x) >> 0) & 0xff)
+#define FW_FCOE_FCF_CMD_FPMA_GET(x) (((x) >> 6) & 0x1)
+#define FW_FCOE_FCF_CMD_SPMA_GET(x) (((x) >> 5) & 0x1)
+#define FW_FCOE_FCF_CMD_LOGIN_GET(x) (((x) >> 4) & 0x1)
+#define FW_FCOE_FCF_CMD_PORTID_GET(x) (((x) >> 0) & 0xf)
+
+#endif /* _T4FW_API_STOR_H_ */
diff --git a/drivers/scsi/cxgbi/Kconfig b/drivers/scsi/cxgbi/Kconfig
index 17eb5d522f42..75f9428a8a14 100644
--- a/drivers/scsi/cxgbi/Kconfig
+++ b/drivers/scsi/cxgbi/Kconfig
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
source "drivers/scsi/cxgbi/cxgb3i/Kconfig"
source "drivers/scsi/cxgbi/cxgb4i/Kconfig"
diff --git a/drivers/scsi/cxgbi/Makefile b/drivers/scsi/cxgbi/Makefile
index 86007e344955..abfd38a26fec 100644
--- a/drivers/scsi/cxgbi/Makefile
+++ b/drivers/scsi/cxgbi/Makefile
@@ -1,2 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+ccflags-y += -I $(srctree)/drivers/net/ethernet/chelsio/libcxgb
+
obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libcxgbi.o cxgb3i/
obj-$(CONFIG_SCSI_CXGB4_ISCSI) += libcxgbi.o cxgb4i/
diff --git a/drivers/scsi/cxgbi/cxgb3i/Kbuild b/drivers/scsi/cxgbi/cxgb3i/Kbuild
index 09dbf9efc8ea..8d8a43f5ea80 100644
--- a/drivers/scsi/cxgbi/cxgb3i/Kbuild
+++ b/drivers/scsi/cxgbi/cxgb3i/Kbuild
@@ -1,3 +1,5 @@
-EXTRA_CFLAGS += -I$(srctree)/drivers/net/cxgb3
+# SPDX-License-Identifier: GPL-2.0
+ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb3
+ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/libcxgb
obj-$(CONFIG_SCSI_CXGB3_ISCSI) += cxgb3i.o
diff --git a/drivers/scsi/cxgbi/cxgb3i/Kconfig b/drivers/scsi/cxgbi/cxgb3i/Kconfig
index 11dff23f7838..e20e6f3bfe64 100644
--- a/drivers/scsi/cxgbi/cxgb3i/Kconfig
+++ b/drivers/scsi/cxgbi/cxgb3i/Kconfig
@@ -1,9 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SCSI_CXGB3_ISCSI
tristate "Chelsio T3 iSCSI support"
- depends on PCI && INET
+ depends on PCI && INET && (IPV6 || IPV6=n)
select NETDEVICES
- select NETDEV_10000
+ select ETHERNET
+ select NET_VENDOR_CHELSIO
select CHELSIO_T3
+ select CHELSIO_LIB
select SCSI_ISCSI_ATTRS
- ---help---
+ help
This driver supports iSCSI offload for the Chelsio T3 devices.
diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
index fc2cdb62f53b..69de9657f7cb 100644
--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
+++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
@@ -1,7 +1,7 @@
/*
* cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management
*
- * Copyright (C) 2003-2008 Chelsio Communications. All rights reserved.
+ * Copyright (C) 2003-2015 Chelsio Communications. All rights reserved.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -14,7 +14,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <scsi/scsi_host.h>
@@ -33,8 +32,8 @@ static unsigned int dbg_level;
#define DRV_MODULE_NAME "cxgb3i"
#define DRV_MODULE_DESC "Chelsio T3 iSCSI Driver"
-#define DRV_MODULE_VERSION "2.0.0"
-#define DRV_MODULE_RELDATE "Jun. 2010"
+#define DRV_MODULE_VERSION "2.0.1-ko"
+#define DRV_MODULE_RELDATE "Apr. 2015"
static char version[] =
DRV_MODULE_DESC " " DRV_MODULE_NAME
@@ -58,7 +57,7 @@ MODULE_PARM_DESC(cxgb3i_snd_win, "TCP send window in bytes (default=128KB)");
static int cxgb3i_rx_credit_thres = 10 * 1024;
module_param(cxgb3i_rx_credit_thres, int, 0644);
-MODULE_PARM_DESC(rx_credit_thres,
+MODULE_PARM_DESC(cxgb3i_rx_credit_thres,
"RX credits return threshold in bytes (default=10KB)");
static unsigned int cxgb3i_max_connect = 8 * 1024;
@@ -81,22 +80,25 @@ static struct cxgb3_client t3_client = {
.event_handler = cxgb3i_dev_event_handler,
};
-static struct scsi_host_template cxgb3i_host_template = {
+static const struct scsi_host_template cxgb3i_host_template = {
.module = THIS_MODULE,
.name = DRV_MODULE_NAME,
.proc_name = DRV_MODULE_NAME,
.can_queue = CXGB3I_SCSI_HOST_QDEPTH,
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.sg_tablesize = SG_ALL,
.max_sectors = 0xFFFF,
.cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .eh_timed_out = iscsi_eh_cmd_timed_out,
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler = iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_recover_target,
.target_alloc = iscsi_target_alloc,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
.this_id = -1,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct iscsi_cmd),
};
static struct iscsi_transport cxgb3i_iscsi_transport = {
@@ -106,25 +108,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
| CAP_DATADGST | CAP_DIGEST_OFFLOAD |
CAP_PADDING_OFFLOAD | CAP_TEXT_NEGO,
- .param_mask = ISCSI_MAX_RECV_DLENGTH | ISCSI_MAX_XMIT_DLENGTH |
- ISCSI_HDRDGST_EN | ISCSI_DATADGST_EN |
- ISCSI_INITIAL_R2T_EN | ISCSI_MAX_R2T |
- ISCSI_IMM_DATA_EN | ISCSI_FIRST_BURST |
- ISCSI_MAX_BURST | ISCSI_PDU_INORDER_EN |
- ISCSI_DATASEQ_INORDER_EN | ISCSI_ERL |
- ISCSI_CONN_PORT | ISCSI_CONN_ADDRESS |
- ISCSI_EXP_STATSN | ISCSI_PERSISTENT_PORT |
- ISCSI_PERSISTENT_ADDRESS |
- ISCSI_TARGET_NAME | ISCSI_TPGT |
- ISCSI_USERNAME | ISCSI_PASSWORD |
- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO |
- ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
- ISCSI_HOST_INITIATOR_NAME |
- ISCSI_HOST_NETDEV_NAME,
+ .attr_is_visible = cxgbi_attr_is_visible,
.get_host_param = cxgbi_get_host_param,
.set_host_param = cxgbi_set_host_param,
/* session management */
@@ -134,6 +118,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
/* connection management */
.create_conn = cxgbi_create_conn,
.bind_conn = cxgbi_bind_conn,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = iscsi_tcp_conn_teardown,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
@@ -174,7 +159,7 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion);
static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
const struct l2t_entry *e)
{
- unsigned int wscale = cxgbi_sock_compute_wscale(cxgb3i_rcv_win);
+ unsigned int wscale = cxgbi_sock_compute_wscale(csk->rcv_win);
struct cpl_act_open_req *req = (struct cpl_act_open_req *)skb->head;
skb->priority = CPL_PRIORITY_SETUP;
@@ -190,7 +175,7 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
V_WND_SCALE(wscale) | V_MSS_IDX(csk->mss_idx) |
V_L2T_IDX(e->idx) | V_TX_CHANNEL(e->smt_idx));
req->opt0l = htonl(V_ULP_MODE(ULP2_MODE_ISCSI) |
- V_RCV_BUFSIZ(cxgb3i_rcv_win>>10));
+ V_RCV_BUFSIZ(csk->rcv_win >> 10));
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx,%u, %pI4:%u-%pI4:%u, %u,%u,%u.\n",
@@ -371,14 +356,14 @@ static inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb,
struct l2t_entry *l2t = csk->l2t;
skb_reset_transport_header(skb);
- req = (struct tx_data_wr *)__skb_push(skb, sizeof(*req));
+ req = __skb_push(skb, sizeof(*req));
req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA) |
(req_completion ? F_WR_COMPL : 0));
req->wr_lo = htonl(V_WR_TID(csk->tid));
/* len includes the length of any HW ULP additions */
req->len = htonl(len);
/* V_TX_ULP_SUBMODE sets both the mode and submode */
- req->flags = htonl(V_TX_ULP_SUBMODE(cxgbi_skcb_ulp_mode(skb)) |
+ req->flags = htonl(V_TX_ULP_SUBMODE(cxgbi_skcb_tx_ulp_mode(skb)) |
V_TX_SHOVE((skb_peek(&csk->write_queue) ? 0 : 1)));
req->sndseq = htonl(csk->snd_nxt);
req->param = htonl(V_TX_PORT(l2t->smt_idx));
@@ -387,15 +372,13 @@ static inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb,
req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT |
V_TX_CPU_IDX(csk->rss_qid));
/* sendbuffer is in units of 32KB. */
- req->param |= htonl(V_TX_SNDBUF(cxgb3i_snd_win >> 15));
+ req->param |= htonl(V_TX_SNDBUF(csk->snd_win >> 15));
cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
}
}
-/**
+/*
* push_tx_frames -- start transmit
- * @c3cn: the offloaded connection
- * @req_completion: request wr_ack or not
*
* Prepends TX_DATA_WR or CPL_CLOSE_CON_REQ headers to buffers waiting in a
* connection's send queue and sends them on to T3. Must be called with the
@@ -459,7 +442,7 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion)
req_completion = 1;
csk->wr_una_cred = 0;
}
- len += cxgbi_ulp_extra_len(cxgbi_skcb_ulp_mode(skb));
+ len += cxgbi_ulp_extra_len(cxgbi_skcb_tx_ulp_mode(skb));
make_tx_data_wr(csk, skb, len, req_completion);
csk->snd_nxt += len;
cxgbi_skcb_clear_flag(skb, SKCBF_TX_NEED_HDR);
@@ -512,7 +495,7 @@ static int do_act_establish(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
spin_lock_bh(&csk->lock);
if (csk->retry_timer.function) {
- del_timer(&csk->retry_timer);
+ timer_delete(&csk->retry_timer);
csk->retry_timer.function = NULL;
}
@@ -521,8 +504,8 @@ static int do_act_establish(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
csk, csk->state, csk->flags, csk->tid);
csk->copied_seq = csk->rcv_wup = csk->rcv_nxt = rcv_isn;
- if (cxgb3i_rcv_win > (M_RCV_BUFSIZ << 10))
- csk->rcv_wup -= cxgb3i_rcv_win - (M_RCV_BUFSIZ << 10);
+ if (csk->rcv_win > (M_RCV_BUFSIZ << 10))
+ csk->rcv_wup -= csk->rcv_win - (M_RCV_BUFSIZ << 10);
cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt));
@@ -562,10 +545,10 @@ static int act_open_rpl_status_to_errno(int status)
}
}
-static void act_open_retry_timer(unsigned long data)
+static void act_open_retry_timer(struct timer_list *t)
{
+ struct cxgbi_sock *csk = timer_container_of(csk, t, retry_timer);
struct sk_buff *skb;
- struct cxgbi_sock *csk = (struct cxgbi_sock *)data;
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx,%u.\n",
@@ -662,7 +645,7 @@ static int abort_status_to_errno(struct cxgbi_sock *csk, int abort_reason,
int *need_rst)
{
switch (abort_reason) {
- case CPL_ERR_BAD_SYN: /* fall through */
+ case CPL_ERR_BAD_SYN:
case CPL_ERR_CONN_RESET:
return csk->state > CTP_ESTABLISHED ? -EPIPE : -ECONNRESET;
case CPL_ERR_XMIT_TIMEDOUT:
@@ -903,22 +886,21 @@ free_cpl_skbs:
return -ENOMEM;
}
-/**
- * release_offload_resources - release offload resource
- * @c3cn: the offloaded iscsi tcp connection.
- * Release resources held by an offload connection (TID, L2T entry, etc.)
- */
static void l2t_put(struct cxgbi_sock *csk)
{
struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev;
if (csk->l2t) {
- l2t_release(L2DATA(t3dev), csk->l2t);
+ l2t_release(t3dev, csk->l2t);
csk->l2t = NULL;
cxgbi_sock_put(csk);
}
}
+/*
+ * release_offload_resources - release offload resource
+ * Release resources held by an offload connection (TID, L2T entry, etc.)
+ */
static void release_offload_resources(struct cxgbi_sock *csk)
{
struct t3cdev *t3dev = (struct t3cdev *)csk->cdev->lldev;
@@ -976,6 +958,7 @@ static int init_act_open(struct cxgbi_sock *csk)
struct net_device *ndev = cdev->ports[csk->port_id];
struct cxgbi_hba *chba = cdev->hbas[csk->port_id];
struct sk_buff *skb = NULL;
+ int ret;
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx.\n", csk, csk->state, csk->flags);
@@ -985,7 +968,8 @@ static int init_act_open(struct cxgbi_sock *csk)
csk->saddr.sin_addr.s_addr = chba->ipv4addr;
csk->rss_qid = 0;
- csk->l2t = t3_l2t_get(t3dev, dst->neighbour, ndev);
+ csk->l2t = t3_l2t_get(t3dev, dst, ndev,
+ &csk->daddr.sin_addr.s_addr);
if (!csk->l2t) {
pr_err("NO l2t available.\n");
return -EINVAL;
@@ -995,16 +979,21 @@ static int init_act_open(struct cxgbi_sock *csk)
csk->atid = cxgb3_alloc_atid(t3dev, &t3_client, csk);
if (csk->atid < 0) {
pr_err("NO atid available.\n");
- goto rel_resource;
+ ret = -EINVAL;
+ goto put_sock;
}
cxgbi_sock_set_flag(csk, CTPF_HAS_ATID);
cxgbi_sock_get(csk);
skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_KERNEL);
- if (!skb)
- goto rel_resource;
+ if (!skb) {
+ ret = -ENOMEM;
+ goto free_atid;
+ }
skb->sk = (struct sock *)csk;
set_arp_failure_handler(skb, act_open_arp_failure);
+ csk->snd_win = cxgb3i_snd_win;
+ csk->rcv_win = cxgb3i_rcv_win;
csk->wr_max_cred = csk->wr_cred = T3C_DATA(t3dev)->max_wrs - 1;
csk->wr_una_cred = 0;
@@ -1022,10 +1011,14 @@ static int init_act_open(struct cxgbi_sock *csk)
send_act_open_req(csk, skb, csk->l2t);
return 0;
-rel_resource:
- if (skb)
- __kfree_skb(skb);
- return -EINVAL;
+free_atid:
+ cxgb3_free_atid(t3dev, csk->atid);
+put_sock:
+ cxgbi_sock_put(csk);
+ l2t_release(t3dev, csk->l2t);
+ csk->l2t = NULL;
+
+ return ret;
}
cxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS] = {
@@ -1043,7 +1036,7 @@ cxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS] = {
* cxgb3i_ofld_init - allocate and initialize resources for each adapter found
* @cdev: cxgbi adapter
*/
-int cxgb3i_ofld_init(struct cxgbi_device *cdev)
+static int cxgb3i_ofld_init(struct cxgbi_device *cdev)
{
struct t3cdev *t3dev = (struct t3cdev *)cdev->lldev;
struct adap_ports port;
@@ -1091,69 +1084,74 @@ static inline void ulp_mem_io_set_hdr(struct sk_buff *skb, unsigned int addr)
req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_BYPASS));
req->cmd_lock_addr = htonl(V_ULP_MEMIO_ADDR(addr >> 5) |
V_ULPTX_CMD(ULP_MEM_WRITE));
- req->len = htonl(V_ULP_MEMIO_DATA_LEN(PPOD_SIZE >> 5) |
- V_ULPTX_NFLITS((PPOD_SIZE >> 3) + 1));
+ req->len = htonl(V_ULP_MEMIO_DATA_LEN(IPPOD_SIZE >> 5) |
+ V_ULPTX_NFLITS((IPPOD_SIZE >> 3) + 1));
}
-static int ddp_set_map(struct cxgbi_sock *csk, struct cxgbi_pagepod_hdr *hdr,
- unsigned int idx, unsigned int npods,
- struct cxgbi_gather_list *gl)
+static struct cxgbi_ppm *cdev2ppm(struct cxgbi_device *cdev)
{
- struct cxgbi_device *cdev = csk->cdev;
- struct cxgbi_ddp_info *ddp = cdev->ddp;
- unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
- int i;
+ return ((struct t3cdev *)cdev->lldev)->ulp_iscsi;
+}
- log_debug(1 << CXGBI_DBG_DDP,
- "csk 0x%p, idx %u, npods %u, gl 0x%p.\n",
- csk, idx, npods, gl);
+static int ddp_set_map(struct cxgbi_ppm *ppm, struct cxgbi_sock *csk,
+ struct cxgbi_task_tag_info *ttinfo)
+{
+ unsigned int idx = ttinfo->idx;
+ unsigned int npods = ttinfo->npods;
+ struct scatterlist *sg = ttinfo->sgl;
+ struct cxgbi_pagepod *ppod;
+ struct ulp_mem_io *req;
+ unsigned int sg_off;
+ unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit;
+ int i;
- for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+ for (i = 0; i < npods; i++, idx++, pm_addr += IPPOD_SIZE) {
struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) +
- PPOD_SIZE, 0, GFP_ATOMIC);
+ IPPOD_SIZE, 0, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
-
ulp_mem_io_set_hdr(skb, pm_addr);
- cxgbi_ddp_ppod_set((struct cxgbi_pagepod *)(skb->head +
- sizeof(struct ulp_mem_io)),
- hdr, gl, i * PPOD_PAGES_MAX);
+ req = (struct ulp_mem_io *)skb->head;
+ ppod = (struct cxgbi_pagepod *)(req + 1);
+ sg_off = i * PPOD_PAGES_MAX;
+ cxgbi_ddp_set_one_ppod(ppod, ttinfo, &sg,
+ &sg_off);
skb->priority = CPL_PRIORITY_CONTROL;
- cxgb3_ofld_send(cdev->lldev, skb);
+ cxgb3_ofld_send(ppm->lldev, skb);
}
return 0;
}
-static void ddp_clear_map(struct cxgbi_hba *chba, unsigned int tag,
- unsigned int idx, unsigned int npods)
+static void ddp_clear_map(struct cxgbi_device *cdev, struct cxgbi_ppm *ppm,
+ struct cxgbi_task_tag_info *ttinfo)
{
- struct cxgbi_device *cdev = chba->cdev;
- struct cxgbi_ddp_info *ddp = cdev->ddp;
- unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
+ unsigned int idx = ttinfo->idx;
+ unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit;
+ unsigned int npods = ttinfo->npods;
int i;
log_debug(1 << CXGBI_DBG_DDP,
- "cdev 0x%p, idx %u, npods %u, tag 0x%x.\n",
- cdev, idx, npods, tag);
+ "cdev 0x%p, clear idx %u, npods %u.\n",
+ cdev, idx, npods);
- for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+ for (i = 0; i < npods; i++, idx++, pm_addr += IPPOD_SIZE) {
struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) +
- PPOD_SIZE, 0, GFP_ATOMIC);
+ IPPOD_SIZE, 0, GFP_ATOMIC);
if (!skb) {
- pr_err("tag 0x%x, 0x%x, %d/%u, skb OOM.\n",
- tag, idx, i, npods);
+ pr_err("cdev 0x%p, clear ddp, %u,%d/%u, skb OOM.\n",
+ cdev, idx, i, npods);
continue;
}
ulp_mem_io_set_hdr(skb, pm_addr);
skb->priority = CPL_PRIORITY_CONTROL;
- cxgb3_ofld_send(cdev->lldev, skb);
+ cxgb3_ofld_send(ppm->lldev, skb);
}
}
static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk,
- unsigned int tid, int pg_idx, bool reply)
+ unsigned int tid, int pg_idx)
{
struct sk_buff *skb = alloc_wr(sizeof(struct cpl_set_tcb_field), 0,
GFP_KERNEL);
@@ -1169,7 +1167,7 @@ static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk,
req = (struct cpl_set_tcb_field *)skb->head;
req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
- req->reply = V_NO_REPLY(reply ? 0 : 1);
+ req->reply = V_NO_REPLY(1);
req->cpu_idx = 0;
req->word = htons(31);
req->mask = cpu_to_be64(0xF0000000);
@@ -1181,16 +1179,15 @@ static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk,
}
/**
- * cxgb3i_setup_conn_digest - setup conn. digest setting
+ * ddp_setup_conn_digest - setup conn. digest setting
* @csk: cxgb tcp socket
* @tid: connection id
* @hcrc: header digest enabled
* @dcrc: data digest enabled
- * @reply: request reply from h/w
* set up the iscsi digest settings for a connection identified by tid
*/
static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid,
- int hcrc, int dcrc, int reply)
+ int hcrc, int dcrc)
{
struct sk_buff *skb = alloc_wr(sizeof(struct cpl_set_tcb_field), 0,
GFP_KERNEL);
@@ -1206,7 +1203,7 @@ static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid,
req = (struct cpl_set_tcb_field *)skb->head;
req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
- req->reply = V_NO_REPLY(reply ? 0 : 1);
+ req->reply = V_NO_REPLY(1);
req->cpu_idx = 0;
req->word = htons(31);
req->mask = cpu_to_be64(0x0F000000);
@@ -1218,80 +1215,72 @@ static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid,
}
/**
- * t3_ddp_cleanup - release the cxgb3 adapter's ddp resource
- * @cdev: cxgb3i adapter
- * release all the resource held by the ddp pagepod manager for a given
- * adapter if needed
- */
-
-static void t3_ddp_cleanup(struct cxgbi_device *cdev)
-{
- struct t3cdev *tdev = (struct t3cdev *)cdev->lldev;
-
- if (cxgbi_ddp_cleanup(cdev)) {
- pr_info("t3dev 0x%p, ulp_iscsi no more user.\n", tdev);
- tdev->ulp_iscsi = NULL;
- }
-}
-
-/**
- * ddp_init - initialize the cxgb3 adapter's ddp resource
+ * cxgb3i_ddp_init - initialize the cxgb3 adapter's ddp resource
* @cdev: cxgb3i adapter
* initialize the ddp pagepod manager for a given adapter
*/
static int cxgb3i_ddp_init(struct cxgbi_device *cdev)
{
struct t3cdev *tdev = (struct t3cdev *)cdev->lldev;
- struct cxgbi_ddp_info *ddp = tdev->ulp_iscsi;
+ struct net_device *ndev = cdev->ports[0];
+ struct cxgbi_tag_format tformat;
+ unsigned int ppmax, tagmask = 0;
struct ulp_iscsi_info uinfo;
- unsigned int pgsz_factor[4];
- int err;
-
- if (ddp) {
- kref_get(&ddp->refcnt);
- pr_warn("t3dev 0x%p, ddp 0x%p already set up.\n",
- tdev, tdev->ulp_iscsi);
- cdev->ddp = ddp;
- return -EALREADY;
- }
+ int i, err;
err = tdev->ctl(tdev, ULP_ISCSI_GET_PARAMS, &uinfo);
if (err < 0) {
- pr_err("%s, failed to get iscsi param err=%d.\n",
- tdev->name, err);
+ pr_err("%s, failed to get iscsi param %d.\n",
+ ndev->name, err);
return err;
}
+ if (uinfo.llimit >= uinfo.ulimit) {
+ pr_warn("T3 %s, iscsi NOT enabled %u ~ %u!\n",
+ ndev->name, uinfo.llimit, uinfo.ulimit);
+ return -EACCES;
+ }
- err = cxgbi_ddp_init(cdev, uinfo.llimit, uinfo.ulimit,
- uinfo.max_txsz, uinfo.max_rxsz);
- if (err < 0)
- return err;
+ ppmax = (uinfo.ulimit - uinfo.llimit + 1) >> PPOD_SIZE_SHIFT;
+ tagmask = cxgbi_tagmask_set(ppmax);
- ddp = cdev->ddp;
+ pr_info("T3 %s: 0x%x~0x%x, 0x%x, tagmask 0x%x -> 0x%x.\n",
+ ndev->name, uinfo.llimit, uinfo.ulimit, ppmax, uinfo.tagmask,
+ tagmask);
- uinfo.tagmask = ddp->idx_mask << PPOD_IDX_SHIFT;
- cxgbi_ddp_page_size_factor(pgsz_factor);
- uinfo.ulimit = uinfo.llimit + (ddp->nppods << PPOD_SIZE_SHIFT);
+ memset(&tformat, 0, sizeof(struct cxgbi_tag_format));
+ for (i = 0; i < 4; i++)
+ tformat.pgsz_order[i] = uinfo.pgsz_factor[i];
+ cxgbi_tagmask_check(tagmask, &tformat);
- err = tdev->ctl(tdev, ULP_ISCSI_SET_PARAMS, &uinfo);
- if (err < 0) {
- pr_warn("%s unable to set iscsi param err=%d, ddp disabled.\n",
- tdev->name, err);
- cxgbi_ddp_cleanup(cdev);
+ err = cxgbi_ddp_ppm_setup(&tdev->ulp_iscsi, cdev, &tformat,
+ (uinfo.ulimit - uinfo.llimit + 1),
+ uinfo.llimit, uinfo.llimit, 0, 0, 0);
+ if (err)
return err;
+
+ if (!(cdev->flags & CXGBI_FLAG_DDP_OFF)) {
+ uinfo.tagmask = tagmask;
+ uinfo.ulimit = uinfo.llimit + (ppmax << PPOD_SIZE_SHIFT);
+
+ err = tdev->ctl(tdev, ULP_ISCSI_SET_PARAMS, &uinfo);
+ if (err < 0) {
+ pr_err("T3 %s fail to set iscsi param %d.\n",
+ ndev->name, err);
+ cdev->flags |= CXGBI_FLAG_DDP_OFF;
+ }
+ err = 0;
}
- tdev->ulp_iscsi = ddp;
cdev->csk_ddp_setup_digest = ddp_setup_conn_digest;
cdev->csk_ddp_setup_pgidx = ddp_setup_conn_pgidx;
- cdev->csk_ddp_set = ddp_set_map;
- cdev->csk_ddp_clear = ddp_clear_map;
-
- pr_info("tdev 0x%p, nppods %u, bits %u, mask 0x%x,0x%x pkt %u/%u, "
- "%u/%u.\n",
- tdev, ddp->nppods, ddp->idx_bits, ddp->idx_mask,
- ddp->rsvd_tag_mask, ddp->max_txsz, uinfo.max_txsz,
- ddp->max_rxsz, uinfo.max_rxsz);
+ cdev->csk_ddp_set_map = ddp_set_map;
+ cdev->csk_ddp_clear_map = ddp_clear_map;
+ cdev->cdev2ppm = cdev2ppm;
+ cdev->tx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ uinfo.max_txsz - ISCSI_PDU_NONPAYLOAD_LEN);
+ cdev->rx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ uinfo.max_rxsz - ISCSI_PDU_NONPAYLOAD_LEN);
+
return 0;
}
@@ -1335,17 +1324,14 @@ static void cxgb3i_dev_open(struct t3cdev *t3dev)
cdev->nports = adapter->params.nports;
cdev->mtus = adapter->params.mtus;
cdev->nmtus = NMTUS;
- cdev->snd_win = cxgb3i_snd_win;
- cdev->rcv_win = cxgb3i_rcv_win;
cdev->rx_credit_thres = cxgb3i_rx_credit_thres;
cdev->skb_tx_rsvd = CXGB3I_TX_HEADER_LEN;
cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr_norss);
- cdev->dev_ddp_cleanup = t3_ddp_cleanup;
cdev->itp = &cxgb3i_iscsi_transport;
err = cxgb3i_ddp_init(cdev);
if (err) {
- pr_info("0x%p ddp init failed\n", cdev);
+ pr_info("0x%p ddp init failed %d\n", cdev, err);
goto err_out;
}
diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h
index 20593fd69d8f..b0430c9359e7 100644
--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h
+++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h
@@ -1,7 +1,7 @@
/*
* cxgb3i.h: Chelsio S3xx iSCSI driver.
*
- * Copyright (c) 2008 Chelsio Communications, Inc.
+ * Copyright (c) 2008-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/scsi/cxgbi/cxgb4i/Kbuild b/drivers/scsi/cxgbi/cxgb4i/Kbuild
index b9f4af7454b7..fd3e0c964de6 100644
--- a/drivers/scsi/cxgbi/cxgb4i/Kbuild
+++ b/drivers/scsi/cxgbi/cxgb4i/Kbuild
@@ -1,3 +1,5 @@
-EXTRA_CFLAGS += -I$(srctree)/drivers/net/cxgb4
+# SPDX-License-Identifier: GPL-2.0
+ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4
+ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/libcxgb
obj-$(CONFIG_SCSI_CXGB4_ISCSI) += cxgb4i.o
diff --git a/drivers/scsi/cxgbi/cxgb4i/Kconfig b/drivers/scsi/cxgbi/cxgb4i/Kconfig
index d5302c27f377..63c8a0f3cd0c 100644
--- a/drivers/scsi/cxgbi/cxgb4i/Kconfig
+++ b/drivers/scsi/cxgbi/cxgb4i/Kconfig
@@ -1,9 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SCSI_CXGB4_ISCSI
tristate "Chelsio T4 iSCSI support"
- depends on PCI && INET
- select NETDEVICES
- select NETDEV_10000
+ depends on PCI && INET && (IPV6 || IPV6=n)
+ depends on PTP_1588_CLOCK_OPTIONAL
+ depends on THERMAL || !THERMAL
+ depends on ETHERNET
+ depends on TLS || TLS=n
+ select NET_VENDOR_CHELSIO
select CHELSIO_T4
+ select CHELSIO_LIB
select SCSI_ISCSI_ATTRS
- ---help---
+ help
This driver supports iSCSI offload for the Chelsio T4 devices.
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index f3a4cd7cf782..42676627c3af 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -1,7 +1,7 @@
/*
* cxgb4i.c: Chelsio T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,29 +13,37 @@
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
-#include <linux/version.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <scsi/scsi_host.h>
#include <net/tcp.h>
#include <net/dst.h>
#include <linux/netdevice.h>
+#include <net/addrconf.h>
+#include "t4_regs.h"
#include "t4_msg.h"
#include "cxgb4.h"
#include "cxgb4_uld.h"
#include "t4fw_api.h"
#include "l2t.h"
#include "cxgb4i.h"
+#include "clip_tbl.h"
static unsigned int dbg_level;
#include "../libcxgbi.h"
+#ifdef CONFIG_CHELSIO_T4_DCB
+#include <net/dcbevent.h>
+#include "cxgb4_dcb.h"
+#endif
+
#define DRV_MODULE_NAME "cxgb4i"
-#define DRV_MODULE_DESC "Chelsio T4 iSCSI Driver"
-#define DRV_MODULE_VERSION "0.9.1"
-#define DRV_MODULE_RELDATE "Aug. 2010"
+#define DRV_MODULE_DESC "Chelsio T4-T6 iSCSI Driver"
+#define DRV_MODULE_VERSION "0.9.5-ko"
+#define DRV_MODULE_RELDATE "Apr. 2015"
static char version[] =
DRV_MODULE_DESC " " DRV_MODULE_NAME
@@ -49,11 +57,13 @@ MODULE_LICENSE("GPL");
module_param(dbg_level, uint, 0644);
MODULE_PARM_DESC(dbg_level, "Debug flag (default=0)");
-static int cxgb4i_rcv_win = 256 * 1024;
+#define CXGB4I_DEFAULT_10G_RCV_WIN (256 * 1024)
+static int cxgb4i_rcv_win = -1;
module_param(cxgb4i_rcv_win, int, 0644);
-MODULE_PARM_DESC(cxgb4i_rcv_win, "TCP reveive window in bytes");
+MODULE_PARM_DESC(cxgb4i_rcv_win, "TCP receive window in bytes");
-static int cxgb4i_snd_win = 128 * 1024;
+#define CXGB4I_DEFAULT_10G_SND_WIN (128 * 1024)
+static int cxgb4i_snd_win = -1;
module_param(cxgb4i_snd_win, int, 0644);
MODULE_PARM_DESC(cxgb4i_snd_win, "TCP send window in bytes");
@@ -75,9 +85,14 @@ typedef void (*cxgb4i_cplhandler_func)(struct cxgbi_device *, struct sk_buff *);
static void *t4_uld_add(const struct cxgb4_lld_info *);
static int t4_uld_rx_handler(void *, const __be64 *, const struct pkt_gl *);
static int t4_uld_state_change(void *, enum cxgb4_state state);
+static inline int send_tx_flowc_wr(struct cxgbi_sock *);
static const struct cxgb4_uld_info cxgb4i_uld_info = {
.name = DRV_MODULE_NAME,
+ .nrxq = MAX_ULD_QSETS,
+ .ntxq = MAX_ULD_QSETS,
+ .rxq_size = 1024,
+ .lro = false,
.add = t4_uld_add,
.rx_handler = t4_uld_rx_handler,
.state_change = t4_uld_state_change,
@@ -89,16 +104,19 @@ static struct scsi_host_template cxgb4i_host_template = {
.proc_name = DRV_MODULE_NAME,
.can_queue = CXGB4I_SCSI_HOST_QDEPTH,
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.sg_tablesize = SG_ALL,
.max_sectors = 0xFFFF,
.cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .eh_timed_out = iscsi_eh_cmd_timed_out,
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler = iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_recover_target,
.target_alloc = iscsi_target_alloc,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
.this_id = -1,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct iscsi_cmd),
};
static struct iscsi_transport cxgb4i_iscsi_transport = {
@@ -107,25 +125,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST |
CAP_DATADGST | CAP_DIGEST_OFFLOAD |
CAP_PADDING_OFFLOAD | CAP_TEXT_NEGO,
- .param_mask = ISCSI_MAX_RECV_DLENGTH | ISCSI_MAX_XMIT_DLENGTH |
- ISCSI_HDRDGST_EN | ISCSI_DATADGST_EN |
- ISCSI_INITIAL_R2T_EN | ISCSI_MAX_R2T |
- ISCSI_IMM_DATA_EN | ISCSI_FIRST_BURST |
- ISCSI_MAX_BURST | ISCSI_PDU_INORDER_EN |
- ISCSI_DATASEQ_INORDER_EN | ISCSI_ERL |
- ISCSI_CONN_PORT | ISCSI_CONN_ADDRESS |
- ISCSI_EXP_STATSN | ISCSI_PERSISTENT_PORT |
- ISCSI_PERSISTENT_ADDRESS |
- ISCSI_TARGET_NAME | ISCSI_TPGT |
- ISCSI_USERNAME | ISCSI_PASSWORD |
- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO |
- ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
- ISCSI_HOST_INITIATOR_NAME |
- ISCSI_HOST_NETDEV_NAME,
+ .attr_is_visible = cxgbi_attr_is_visible,
.get_host_param = cxgbi_get_host_param,
.set_host_param = cxgbi_set_host_param,
/* session management */
@@ -135,6 +135,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
/* connection management */
.create_conn = cxgbi_create_conn,
.bind_conn = cxgbi_bind_conn,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = iscsi_tcp_conn_teardown,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
@@ -161,6 +162,15 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
.session_recovery_timedout = iscsi_session_recovery_timedout,
};
+#ifdef CONFIG_CHELSIO_T4_DCB
+static int
+cxgb4_dcb_change_notify(struct notifier_block *, unsigned long, void *);
+
+static struct notifier_block cxgb4_dcb_change = {
+ .notifier_call = cxgb4_dcb_change_notify,
+};
+#endif
+
static struct scsi_transport_template *cxgb4i_stt;
/*
@@ -169,15 +179,9 @@ static struct scsi_transport_template *cxgb4i_stt;
* The section below implments CPLs that related to iscsi tcp connection
* open/close/abort and data send/receive.
*/
-#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
-#define RCV_BUFSIZ_MASK 0x3FFU
-#define MAX_IMM_TX_PKT_LEN 128
-static inline void set_queue(struct sk_buff *skb, unsigned int queue,
- const struct cxgbi_sock *csk)
-{
- skb->queue_mapping = queue;
-}
+#define RCV_BUFSIZ_MASK 0x3FFU
+#define MAX_IMM_TX_PKT_LEN 256
static int push_tx_frames(struct cxgbi_sock *, int);
@@ -188,57 +192,251 @@ static int push_tx_frames(struct cxgbi_sock *, int);
* Returns true if a packet can be sent as an offload WR with immediate
* data. We currently use the same limit as for Ethernet packets.
*/
-static inline int is_ofld_imm(const struct sk_buff *skb)
+static inline bool is_ofld_imm(const struct sk_buff *skb)
{
- return skb->len <= (MAX_IMM_TX_PKT_LEN -
- sizeof(struct fw_ofld_tx_data_wr));
+ int len = skb->len;
+
+ if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_NEED_HDR)))
+ len += sizeof(struct fw_ofld_tx_data_wr);
+
+ if (likely(cxgbi_skcb_test_flag((struct sk_buff *)skb, SKCBF_TX_ISO)))
+ len += sizeof(struct cpl_tx_data_iso);
+
+ return (len <= MAX_IMM_OFLD_TX_DATA_WR_LEN);
}
static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
struct l2t_entry *e)
{
- struct cpl_act_open_req *req;
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(csk->cdev);
int wscale = cxgbi_sock_compute_wscale(csk->mss_idx);
unsigned long long opt0;
unsigned int opt2;
unsigned int qid_atid = ((unsigned int)csk->atid) |
(((unsigned int)csk->rss_qid) << 14);
- opt0 = KEEP_ALIVE(1) |
- WND_SCALE(wscale) |
- MSS_IDX(csk->mss_idx) |
- L2T_IDX(((struct l2t_entry *)csk->l2t)->idx) |
- TX_CHAN(csk->tx_chan) |
- SMAC_SEL(csk->smac_idx) |
- ULP_MODE(ULP_MODE_ISCSI) |
- RCV_BUFSIZ(cxgb4i_rcv_win >> 10);
- opt2 = RX_CHANNEL(0) |
- RSS_QUEUE_VALID |
- (1 << 20) | (1 << 22) |
- RSS_QUEUE(csk->rss_qid);
+ opt0 = KEEP_ALIVE_F |
+ WND_SCALE_V(wscale) |
+ MSS_IDX_V(csk->mss_idx) |
+ L2T_IDX_V(((struct l2t_entry *)csk->l2t)->idx) |
+ TX_CHAN_V(csk->tx_chan) |
+ SMAC_SEL_V(csk->smac_idx) |
+ ULP_MODE_V(ULP_MODE_ISCSI) |
+ RCV_BUFSIZ_V(csk->rcv_win >> 10);
+
+ opt2 = RX_CHANNEL_V(0) |
+ RSS_QUEUE_VALID_F |
+ RSS_QUEUE_V(csk->rss_qid);
+
+ if (is_t4(lldi->adapter_type)) {
+ struct cpl_act_open_req *req =
+ (struct cpl_act_open_req *)skb->head;
+
+ INIT_TP_WR(req, 0);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
+ qid_atid));
+ req->local_port = csk->saddr.sin_port;
+ req->peer_port = csk->daddr.sin_port;
+ req->local_ip = csk->saddr.sin_addr.s_addr;
+ req->peer_ip = csk->daddr.sin_addr.s_addr;
+ req->opt0 = cpu_to_be64(opt0);
+ req->params = cpu_to_be32(cxgb4_select_ntuple(
+ csk->cdev->ports[csk->port_id],
+ csk->l2t));
+ opt2 |= RX_FC_VALID_F;
+ req->opt2 = cpu_to_be32(opt2);
+
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
+ "csk t4 0x%p, %pI4:%u-%pI4:%u, atid %d, qid %u.\n",
+ csk, &req->local_ip, ntohs(req->local_port),
+ &req->peer_ip, ntohs(req->peer_port),
+ csk->atid, csk->rss_qid);
+ } else if (is_t5(lldi->adapter_type)) {
+ struct cpl_t5_act_open_req *req =
+ (struct cpl_t5_act_open_req *)skb->head;
+ u32 isn = (get_random_u32() & ~7UL) - 1;
+
+ INIT_TP_WR(req, 0);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
+ qid_atid));
+ req->local_port = csk->saddr.sin_port;
+ req->peer_port = csk->daddr.sin_port;
+ req->local_ip = csk->saddr.sin_addr.s_addr;
+ req->peer_ip = csk->daddr.sin_addr.s_addr;
+ req->opt0 = cpu_to_be64(opt0);
+ req->params = cpu_to_be64(FILTER_TUPLE_V(
+ cxgb4_select_ntuple(
+ csk->cdev->ports[csk->port_id],
+ csk->l2t)));
+ req->rsvd = cpu_to_be32(isn);
+ opt2 |= T5_ISS_VALID;
+ opt2 |= T5_OPT_2_VALID_F;
+
+ req->opt2 = cpu_to_be32(opt2);
+
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
+ "csk t5 0x%p, %pI4:%u-%pI4:%u, atid %d, qid %u.\n",
+ csk, &req->local_ip, ntohs(req->local_port),
+ &req->peer_ip, ntohs(req->peer_port),
+ csk->atid, csk->rss_qid);
+ } else {
+ struct cpl_t6_act_open_req *req =
+ (struct cpl_t6_act_open_req *)skb->head;
+ u32 isn = (get_random_u32() & ~7UL) - 1;
+
+ INIT_TP_WR(req, 0);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
+ qid_atid));
+ req->local_port = csk->saddr.sin_port;
+ req->peer_port = csk->daddr.sin_port;
+ req->local_ip = csk->saddr.sin_addr.s_addr;
+ req->peer_ip = csk->daddr.sin_addr.s_addr;
+ req->opt0 = cpu_to_be64(opt0);
+ req->params = cpu_to_be64(FILTER_TUPLE_V(
+ cxgb4_select_ntuple(
+ csk->cdev->ports[csk->port_id],
+ csk->l2t)));
+ req->rsvd = cpu_to_be32(isn);
+
+ opt2 |= T5_ISS_VALID;
+ opt2 |= RX_FC_DISABLE_F;
+ opt2 |= T5_OPT_2_VALID_F;
+
+ req->opt2 = cpu_to_be32(opt2);
+ req->rsvd2 = cpu_to_be32(0);
+ req->opt3 = cpu_to_be32(0);
+
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
+ "csk t6 0x%p, %pI4:%u-%pI4:%u, atid %d, qid %u.\n",
+ csk, &req->local_ip, ntohs(req->local_port),
+ &req->peer_ip, ntohs(req->peer_port),
+ csk->atid, csk->rss_qid);
+ }
set_wr_txq(skb, CPL_PRIORITY_SETUP, csk->port_id);
- req = (struct cpl_act_open_req *)skb->head;
- INIT_TP_WR(req, 0);
- OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
- qid_atid));
- req->local_port = csk->saddr.sin_port;
- req->peer_port = csk->daddr.sin_port;
- req->local_ip = csk->saddr.sin_addr.s_addr;
- req->peer_ip = csk->daddr.sin_addr.s_addr;
- req->opt0 = cpu_to_be64(opt0);
- req->params = 0;
- req->opt2 = cpu_to_be32(opt2);
+ pr_info_ipaddr("t%d csk 0x%p,%u,0x%lx,%u, rss_qid %u.\n",
+ (&csk->saddr), (&csk->daddr),
+ CHELSIO_CHIP_VERSION(lldi->adapter_type), csk,
+ csk->state, csk->flags, csk->atid, csk->rss_qid);
- log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
- "csk 0x%p, %pI4:%u-%pI4:%u, atid %d, qid %u.\n",
- csk, &req->local_ip, ntohs(req->local_port),
- &req->peer_ip, ntohs(req->peer_port),
- csk->atid, csk->rss_qid);
+ cxgb4_l2t_send(csk->cdev->ports[csk->port_id], skb, csk->l2t);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static void send_act_open_req6(struct cxgbi_sock *csk, struct sk_buff *skb,
+ struct l2t_entry *e)
+{
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(csk->cdev);
+ int wscale = cxgbi_sock_compute_wscale(csk->mss_idx);
+ unsigned long long opt0;
+ unsigned int opt2;
+ unsigned int qid_atid = ((unsigned int)csk->atid) |
+ (((unsigned int)csk->rss_qid) << 14);
+
+ opt0 = KEEP_ALIVE_F |
+ WND_SCALE_V(wscale) |
+ MSS_IDX_V(csk->mss_idx) |
+ L2T_IDX_V(((struct l2t_entry *)csk->l2t)->idx) |
+ TX_CHAN_V(csk->tx_chan) |
+ SMAC_SEL_V(csk->smac_idx) |
+ ULP_MODE_V(ULP_MODE_ISCSI) |
+ RCV_BUFSIZ_V(csk->rcv_win >> 10);
+
+ opt2 = RX_CHANNEL_V(0) |
+ RSS_QUEUE_VALID_F |
+ RSS_QUEUE_V(csk->rss_qid);
+
+ if (is_t4(lldi->adapter_type)) {
+ struct cpl_act_open_req6 *req =
+ (struct cpl_act_open_req6 *)skb->head;
+
+ INIT_TP_WR(req, 0);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ6,
+ qid_atid));
+ req->local_port = csk->saddr6.sin6_port;
+ req->peer_port = csk->daddr6.sin6_port;
+
+ req->local_ip_hi = *(__be64 *)(csk->saddr6.sin6_addr.s6_addr);
+ req->local_ip_lo = *(__be64 *)(csk->saddr6.sin6_addr.s6_addr +
+ 8);
+ req->peer_ip_hi = *(__be64 *)(csk->daddr6.sin6_addr.s6_addr);
+ req->peer_ip_lo = *(__be64 *)(csk->daddr6.sin6_addr.s6_addr +
+ 8);
+
+ req->opt0 = cpu_to_be64(opt0);
+
+ opt2 |= RX_FC_VALID_F;
+ req->opt2 = cpu_to_be32(opt2);
+
+ req->params = cpu_to_be32(cxgb4_select_ntuple(
+ csk->cdev->ports[csk->port_id],
+ csk->l2t));
+ } else if (is_t5(lldi->adapter_type)) {
+ struct cpl_t5_act_open_req6 *req =
+ (struct cpl_t5_act_open_req6 *)skb->head;
+
+ INIT_TP_WR(req, 0);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ6,
+ qid_atid));
+ req->local_port = csk->saddr6.sin6_port;
+ req->peer_port = csk->daddr6.sin6_port;
+ req->local_ip_hi = *(__be64 *)(csk->saddr6.sin6_addr.s6_addr);
+ req->local_ip_lo = *(__be64 *)(csk->saddr6.sin6_addr.s6_addr +
+ 8);
+ req->peer_ip_hi = *(__be64 *)(csk->daddr6.sin6_addr.s6_addr);
+ req->peer_ip_lo = *(__be64 *)(csk->daddr6.sin6_addr.s6_addr +
+ 8);
+ req->opt0 = cpu_to_be64(opt0);
+
+ opt2 |= T5_OPT_2_VALID_F;
+ req->opt2 = cpu_to_be32(opt2);
+
+ req->params = cpu_to_be64(FILTER_TUPLE_V(cxgb4_select_ntuple(
+ csk->cdev->ports[csk->port_id],
+ csk->l2t)));
+ } else {
+ struct cpl_t6_act_open_req6 *req =
+ (struct cpl_t6_act_open_req6 *)skb->head;
+
+ INIT_TP_WR(req, 0);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ6,
+ qid_atid));
+ req->local_port = csk->saddr6.sin6_port;
+ req->peer_port = csk->daddr6.sin6_port;
+ req->local_ip_hi = *(__be64 *)(csk->saddr6.sin6_addr.s6_addr);
+ req->local_ip_lo = *(__be64 *)(csk->saddr6.sin6_addr.s6_addr +
+ 8);
+ req->peer_ip_hi = *(__be64 *)(csk->daddr6.sin6_addr.s6_addr);
+ req->peer_ip_lo = *(__be64 *)(csk->daddr6.sin6_addr.s6_addr +
+ 8);
+ req->opt0 = cpu_to_be64(opt0);
+
+ opt2 |= RX_FC_DISABLE_F;
+ opt2 |= T5_OPT_2_VALID_F;
+
+ req->opt2 = cpu_to_be32(opt2);
+
+ req->params = cpu_to_be64(FILTER_TUPLE_V(cxgb4_select_ntuple(
+ csk->cdev->ports[csk->port_id],
+ csk->l2t)));
+
+ req->rsvd2 = cpu_to_be32(0);
+ req->opt3 = cpu_to_be32(0);
+ }
+
+ set_wr_txq(skb, CPL_PRIORITY_SETUP, csk->port_id);
+
+ pr_info("t%d csk 0x%p,%u,0x%lx,%u, [%pI6]:%u-[%pI6]:%u, rss_qid %u.\n",
+ CHELSIO_CHIP_VERSION(lldi->adapter_type), csk, csk->state,
+ csk->flags, csk->atid,
+ &csk->saddr6.sin6_addr, ntohs(csk->saddr.sin_port),
+ &csk->daddr6.sin6_addr, ntohs(csk->daddr.sin_port),
+ csk->rss_qid);
cxgb4_l2t_send(csk->cdev->ports[csk->port_id], skb, csk->l2t);
}
+#endif
static void send_close_req(struct cxgbi_sock *csk)
{
@@ -280,13 +478,19 @@ static void send_abort_req(struct cxgbi_sock *csk)
if (unlikely(csk->state == CTP_ABORTING) || !skb || !csk->cdev)
return;
+
+ if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) {
+ send_tx_flowc_wr(csk);
+ cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
+ }
+
cxgbi_sock_set_state(csk, CTP_ABORTING);
cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_PENDING);
cxgbi_sock_purge_write_queue(csk);
csk->cpl_abort_req = NULL;
req = (struct cpl_abort_req *)skb->head;
- set_queue(skb, CPL_PRIORITY_DATA, csk);
+ set_wr_txq(skb, CPL_PRIORITY_DATA, csk->port_id);
req->cmd = CPL_ABORT_SEND_RST;
t4_set_arp_err_handler(skb, csk, abort_arp_failure);
INIT_TP_WR(req, csk->tid);
@@ -312,7 +516,7 @@ static void send_abort_rpl(struct cxgbi_sock *csk, int rst_status)
csk, csk->state, csk->flags, csk->tid, rst_status);
csk->cpl_abort_rpl = NULL;
- set_queue(skb, CPL_PRIORITY_DATA, csk);
+ set_wr_txq(skb, CPL_PRIORITY_DATA, csk->port_id);
INIT_TP_WR(rpl, csk->tid);
OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_ABORT_RPL, csk->tid));
rpl->cmd = rst_status;
@@ -344,7 +548,8 @@ static u32 send_rx_credits(struct cxgbi_sock *csk, u32 credits)
INIT_TP_WR(req, csk->tid);
OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_RX_DATA_ACK,
csk->tid));
- req->credit_dack = cpu_to_be32(RX_CREDITS(credits) | RX_FORCE_ACK(1));
+ req->credit_dack = cpu_to_be32(RX_CREDITS_V(credits)
+ | RX_FORCE_ACK_F);
cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb);
return credits;
}
@@ -377,25 +582,51 @@ static inline unsigned int calc_tx_flits_ofld(const struct sk_buff *skb)
return DIV_ROUND_UP(skb->len, 8);
flits = skb_transport_offset(skb) / 8;
cnt = skb_shinfo(skb)->nr_frags;
- if (skb->tail != skb->transport_header)
+ if (skb_tail_pointer(skb) != skb_transport_header(skb))
cnt++;
return flits + sgl_len(cnt);
}
-static inline void send_tx_flowc_wr(struct cxgbi_sock *csk)
+#define FLOWC_WR_NPARAMS_MIN 9
+static inline int tx_flowc_wr_credits(int *nparamsp, int *flowclenp)
+{
+ int nparams, flowclen16, flowclen;
+
+ nparams = FLOWC_WR_NPARAMS_MIN;
+#ifdef CONFIG_CHELSIO_T4_DCB
+ nparams++;
+#endif
+ flowclen = offsetof(struct fw_flowc_wr, mnemval[nparams]);
+ flowclen16 = DIV_ROUND_UP(flowclen, 16);
+ flowclen = flowclen16 * 16;
+ /*
+ * Return the number of 16-byte credits used by the FlowC request.
+ * Pass back the nparams and actual FlowC length if requested.
+ */
+ if (nparamsp)
+ *nparamsp = nparams;
+ if (flowclenp)
+ *flowclenp = flowclen;
+
+ return flowclen16;
+}
+
+static inline int send_tx_flowc_wr(struct cxgbi_sock *csk)
{
struct sk_buff *skb;
struct fw_flowc_wr *flowc;
- int flowclen, i;
+ int nparams, flowclen16, flowclen;
- flowclen = 80;
+#ifdef CONFIG_CHELSIO_T4_DCB
+ u16 vlan = ((struct l2t_entry *)csk->l2t)->vlan;
+#endif
+ flowclen16 = tx_flowc_wr_credits(&nparams, &flowclen);
skb = alloc_wr(flowclen, 0, GFP_ATOMIC);
flowc = (struct fw_flowc_wr *)skb->head;
flowc->op_to_nparams =
- htonl(FW_WR_OP(FW_FLOWC_WR) | FW_FLOWC_WR_NPARAMS(8));
+ htonl(FW_WR_OP_V(FW_FLOWC_WR) | FW_FLOWC_WR_NPARAMS_V(nparams));
flowc->flowid_len16 =
- htonl(FW_WR_LEN16(DIV_ROUND_UP(72, 16)) |
- FW_WR_FLOWID(csk->tid));
+ htonl(FW_WR_LEN16_V(flowclen16) | FW_WR_FLOWID_V(csk->tid));
flowc->mnemval[0].mnemonic = FW_FLOWC_MNEM_PFNVFN;
flowc->mnemval[0].val = htonl(csk->cdev->pfvf);
flowc->mnemval[1].mnemonic = FW_FLOWC_MNEM_CH;
@@ -409,57 +640,121 @@ static inline void send_tx_flowc_wr(struct cxgbi_sock *csk)
flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT;
flowc->mnemval[5].val = htonl(csk->rcv_nxt);
flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF;
- flowc->mnemval[6].val = htonl(cxgb4i_snd_win);
+ flowc->mnemval[6].val = htonl(csk->snd_win);
flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS;
flowc->mnemval[7].val = htonl(csk->advmss);
flowc->mnemval[8].mnemonic = 0;
flowc->mnemval[8].val = 0;
- for (i = 0; i < 9; i++) {
- flowc->mnemval[i].r4[0] = 0;
- flowc->mnemval[i].r4[1] = 0;
- flowc->mnemval[i].r4[2] = 0;
+ flowc->mnemval[8].mnemonic = FW_FLOWC_MNEM_TXDATAPLEN_MAX;
+ if (csk->cdev->skb_iso_txhdr)
+ flowc->mnemval[8].val = cpu_to_be32(CXGBI_MAX_ISO_DATA_IN_SKB);
+ else
+ flowc->mnemval[8].val = cpu_to_be32(16128);
+#ifdef CONFIG_CHELSIO_T4_DCB
+ flowc->mnemval[9].mnemonic = FW_FLOWC_MNEM_DCBPRIO;
+ if (vlan == CPL_L2T_VLAN_NONE) {
+ pr_warn_ratelimited("csk %u without VLAN Tag on DCB Link\n",
+ csk->tid);
+ flowc->mnemval[9].val = cpu_to_be32(0);
+ } else {
+ flowc->mnemval[9].val = cpu_to_be32((vlan & VLAN_PRIO_MASK) >>
+ VLAN_PRIO_SHIFT);
}
- set_queue(skb, CPL_PRIORITY_DATA, csk);
+#endif
+
+ set_wr_txq(skb, CPL_PRIORITY_DATA, csk->port_id);
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p, tid 0x%x, %u,%u,%u,%u,%u,%u,%u.\n",
csk, csk->tid, 0, csk->tx_chan, csk->rss_qid,
- csk->snd_nxt, csk->rcv_nxt, cxgb4i_snd_win,
+ csk->snd_nxt, csk->rcv_nxt, csk->snd_win,
csk->advmss);
cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb);
+
+ return flowclen16;
}
-static inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb,
- int dlen, int len, u32 credits, int compl)
+static void
+cxgb4i_make_tx_iso_cpl(struct sk_buff *skb, struct cpl_tx_data_iso *cpl)
{
+ struct cxgbi_iso_info *info = (struct cxgbi_iso_info *)skb->head;
+ u32 imm_en = !!(info->flags & CXGBI_ISO_INFO_IMM_ENABLE);
+ u32 fslice = !!(info->flags & CXGBI_ISO_INFO_FSLICE);
+ u32 lslice = !!(info->flags & CXGBI_ISO_INFO_LSLICE);
+ u32 pdu_type = (info->op == ISCSI_OP_SCSI_CMD) ? 0 : 1;
+ u32 submode = cxgbi_skcb_tx_ulp_mode(skb) & 0x3;
+
+ cpl->op_to_scsi = cpu_to_be32(CPL_TX_DATA_ISO_OP_V(CPL_TX_DATA_ISO) |
+ CPL_TX_DATA_ISO_FIRST_V(fslice) |
+ CPL_TX_DATA_ISO_LAST_V(lslice) |
+ CPL_TX_DATA_ISO_CPLHDRLEN_V(0) |
+ CPL_TX_DATA_ISO_HDRCRC_V(submode & 1) |
+ CPL_TX_DATA_ISO_PLDCRC_V(((submode >> 1) & 1)) |
+ CPL_TX_DATA_ISO_IMMEDIATE_V(imm_en) |
+ CPL_TX_DATA_ISO_SCSI_V(pdu_type));
+
+ cpl->ahs_len = info->ahs;
+ cpl->mpdu = cpu_to_be16(DIV_ROUND_UP(info->mpdu, 4));
+ cpl->burst_size = cpu_to_be32(info->burst_size);
+ cpl->len = cpu_to_be32(info->len);
+ cpl->reserved2_seglen_offset =
+ cpu_to_be32(CPL_TX_DATA_ISO_SEGLEN_OFFSET_V(info->segment_offset));
+ cpl->datasn_offset = cpu_to_be32(info->datasn_offset);
+ cpl->buffer_offset = cpu_to_be32(info->buffer_offset);
+ cpl->reserved3 = cpu_to_be32(0);
+ log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
+ "iso: flags 0x%x, op %u, ahs %u, num_pdu %u, mpdu %u, "
+ "burst_size %u, iso_len %u\n",
+ info->flags, info->op, info->ahs, info->num_pdu,
+ info->mpdu, info->burst_size << 2, info->len);
+}
+
+static void
+cxgb4i_make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb, int dlen,
+ int len, u32 credits, int compl)
+{
+ struct cxgbi_device *cdev = csk->cdev;
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
struct fw_ofld_tx_data_wr *req;
- unsigned int submode = cxgbi_skcb_ulp_mode(skb) & 3;
- unsigned int wr_ulp_mode = 0;
+ struct cpl_tx_data_iso *cpl;
+ u32 submode = cxgbi_skcb_tx_ulp_mode(skb) & 0x3;
+ u32 wr_ulp_mode = 0;
+ u32 hdr_size = sizeof(*req);
+ u32 opcode = FW_OFLD_TX_DATA_WR;
+ u32 immlen = 0;
+ u32 force = is_t5(lldi->adapter_type) ? TX_FORCE_V(!submode) :
+ T6_TX_FORCE_F;
+
+ if (cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO)) {
+ hdr_size += sizeof(struct cpl_tx_data_iso);
+ opcode = FW_ISCSI_TX_DATA_WR;
+ immlen += sizeof(struct cpl_tx_data_iso);
+ submode |= 8;
+ }
- req = (struct fw_ofld_tx_data_wr *)__skb_push(skb, sizeof(*req));
+ if (is_ofld_imm(skb))
+ immlen += dlen;
+
+ req = (struct fw_ofld_tx_data_wr *)__skb_push(skb, hdr_size);
+ req->op_to_immdlen = cpu_to_be32(FW_WR_OP_V(opcode) |
+ FW_WR_COMPL_V(compl) |
+ FW_WR_IMMDLEN_V(immlen));
+ req->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(csk->tid) |
+ FW_WR_LEN16_V(credits));
+ req->plen = cpu_to_be32(len);
+ cpl = (struct cpl_tx_data_iso *)(req + 1);
+
+ if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO)))
+ cxgb4i_make_tx_iso_cpl(skb, cpl);
- if (is_ofld_imm(skb)) {
- req->op_to_immdlen = htonl(FW_WR_OP(FW_OFLD_TX_DATA_WR) |
- FW_WR_COMPL(1) |
- FW_WR_IMMDLEN(dlen));
- req->flowid_len16 = htonl(FW_WR_FLOWID(csk->tid) |
- FW_WR_LEN16(credits));
- } else {
- req->op_to_immdlen =
- cpu_to_be32(FW_WR_OP(FW_OFLD_TX_DATA_WR) |
- FW_WR_COMPL(1) |
- FW_WR_IMMDLEN(0));
- req->flowid_len16 =
- cpu_to_be32(FW_WR_FLOWID(csk->tid) |
- FW_WR_LEN16(credits));
- }
if (submode)
- wr_ulp_mode = FW_OFLD_TX_DATA_WR_ULPMODE(ULP2_MODE_ISCSI) |
- FW_OFLD_TX_DATA_WR_ULPSUBMODE(submode);
- req->tunnel_to_proxy = htonl(wr_ulp_mode) |
- FW_OFLD_TX_DATA_WR_SHOVE(skb_peek(&csk->write_queue) ? 0 : 1);
- req->plen = htonl(len);
+ wr_ulp_mode = FW_OFLD_TX_DATA_WR_ULPMODE_V(ULP2_MODE_ISCSI) |
+ FW_OFLD_TX_DATA_WR_ULPSUBMODE_V(submode);
+
+ req->tunnel_to_proxy = cpu_to_be32(wr_ulp_mode | force |
+ FW_OFLD_TX_DATA_WR_SHOVE_V(1U));
+
if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT))
cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
}
@@ -477,36 +772,61 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion)
if (unlikely(csk->state < CTP_ESTABLISHED ||
csk->state == CTP_CLOSE_WAIT_1 || csk->state >= CTP_ABORTING)) {
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK |
- 1 << CXGBI_DBG_PDU_TX,
- "csk 0x%p,%u,0x%lx,%u, in closing state.\n",
- csk, csk->state, csk->flags, csk->tid);
+ 1 << CXGBI_DBG_PDU_TX,
+ "csk 0x%p,%u,0x%lx,%u, in closing state.\n",
+ csk, csk->state, csk->flags, csk->tid);
return 0;
}
- while (csk->wr_cred && (skb = skb_peek(&csk->write_queue)) != NULL) {
- int dlen = skb->len;
- int len = skb->len;
- unsigned int credits_needed;
+ while (csk->wr_cred && ((skb = skb_peek(&csk->write_queue)) != NULL)) {
+ struct cxgbi_iso_info *iso_cpl;
+ u32 dlen = skb->len;
+ u32 len = skb->len;
+ u32 iso_cpl_len = 0;
+ u32 flowclen16 = 0;
+ u32 credits_needed;
+ u32 num_pdu = 1, hdr_len;
+
+ if (cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO))
+ iso_cpl_len = sizeof(struct cpl_tx_data_iso);
- skb_reset_transport_header(skb);
if (is_ofld_imm(skb))
- credits_needed = DIV_ROUND_UP(dlen +
- sizeof(struct fw_ofld_tx_data_wr), 16);
+ credits_needed = DIV_ROUND_UP(dlen + iso_cpl_len, 16);
else
- credits_needed = DIV_ROUND_UP(8*calc_tx_flits_ofld(skb)
- + sizeof(struct fw_ofld_tx_data_wr),
- 16);
+ credits_needed =
+ DIV_ROUND_UP((8 * calc_tx_flits_ofld(skb)) +
+ iso_cpl_len, 16);
+
+ if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_NEED_HDR)))
+ credits_needed +=
+ DIV_ROUND_UP(sizeof(struct fw_ofld_tx_data_wr), 16);
+
+ /*
+ * Assumes the initial credits is large enough to support
+ * fw_flowc_wr plus largest possible first payload
+ */
+ if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) {
+ flowclen16 = send_tx_flowc_wr(csk);
+ csk->wr_cred -= flowclen16;
+ csk->wr_una_cred += flowclen16;
+ cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
+ }
if (csk->wr_cred < credits_needed) {
log_debug(1 << CXGBI_DBG_PDU_TX,
- "csk 0x%p, skb %u/%u, wr %d < %u.\n",
- csk, skb->len, skb->data_len,
- credits_needed, csk->wr_cred);
+ "csk 0x%p, skb %u/%u, wr %d < %u.\n",
+ csk, skb->len, skb->data_len,
+ credits_needed, csk->wr_cred);
+
+ csk->no_tx_credits++;
break;
}
+
+ csk->no_tx_credits = 0;
+
__skb_unlink(skb, &csk->write_queue);
- set_queue(skb, CPL_PRIORITY_DATA, csk);
- skb->csum = credits_needed;
+ set_wr_txq(skb, CPL_PRIORITY_DATA, csk->port_id);
+ skb->csum = (__force __wsum)(credits_needed + flowclen16);
csk->wr_cred -= credits_needed;
csk->wr_una_cred += credits_needed;
cxgbi_sock_enqueue_wr(csk, skb);
@@ -516,26 +836,42 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion)
csk, skb->len, skb->data_len, credits_needed,
csk->wr_cred, csk->wr_una_cred);
+ if (!req_completion &&
+ ((csk->wr_una_cred >= (csk->wr_max_cred / 2)) ||
+ after(csk->write_seq, (csk->snd_una + csk->snd_win / 2))))
+ req_completion = 1;
+
if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_NEED_HDR))) {
- if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) {
- send_tx_flowc_wr(csk);
- skb->csum += 5;
- csk->wr_cred -= 5;
- csk->wr_una_cred += 5;
+ u32 ulp_mode = cxgbi_skcb_tx_ulp_mode(skb);
+
+ if (cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO)) {
+ iso_cpl = (struct cxgbi_iso_info *)skb->head;
+ num_pdu = iso_cpl->num_pdu;
+ hdr_len = cxgbi_skcb_tx_iscsi_hdrlen(skb);
+ len += (cxgbi_ulp_extra_len(ulp_mode) * num_pdu) +
+ (hdr_len * (num_pdu - 1));
+ } else {
+ len += cxgbi_ulp_extra_len(ulp_mode);
}
- len += cxgbi_ulp_extra_len(cxgbi_skcb_ulp_mode(skb));
- make_tx_data_wr(csk, skb, dlen, len, credits_needed,
- req_completion);
+
+ cxgb4i_make_tx_data_wr(csk, skb, dlen, len,
+ credits_needed, req_completion);
csk->snd_nxt += len;
cxgbi_skcb_clear_flag(skb, SKCBF_TX_NEED_HDR);
+ } else if (cxgbi_skcb_test_flag(skb, SKCBF_TX_FLAG_COMPL) &&
+ (csk->wr_una_cred >= (csk->wr_max_cred / 2))) {
+ struct cpl_close_con_req *req =
+ (struct cpl_close_con_req *)skb->data;
+
+ req->wr.wr_hi |= cpu_to_be32(FW_WR_COMPL_F);
}
+
total_size += skb->truesize;
t4_set_arp_err_handler(skb, csk, arp_failure_skb_discard);
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_TX,
- "csk 0x%p,%u,0x%lx,%u, skb 0x%p, %u.\n",
- csk, csk->state, csk->flags, csk->tid, skb, len);
-
+ "csk 0x%p,%u,0x%lx,%u, skb 0x%p, %u.\n",
+ csk, csk->state, csk->flags, csk->tid, skb, len);
cxgb4_l2t_send(csk->cdev->ports[csk->port_id], skb, csk->l2t);
}
return total_size;
@@ -558,7 +894,7 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb)
struct cpl_act_establish *req = (struct cpl_act_establish *)skb->data;
unsigned short tcp_opt = ntohs(req->tcp_opt);
unsigned int tid = GET_TID(req);
- unsigned int atid = GET_TID_TID(ntohl(req->tos_atid));
+ unsigned int atid = TID_TID_G(ntohl(req->tos_atid));
struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
struct tid_info *t = lldi->tids;
u32 rcv_isn = be32_to_cpu(req->rcv_isn);
@@ -575,13 +911,15 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb)
goto rel_skb;
}
- log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
- "csk 0x%p,%u,0x%lx, tid %u, atid %u, rseq %u.\n",
- csk, csk->state, csk->flags, tid, atid, rcv_isn);
+ pr_info_ipaddr("atid 0x%x, tid 0x%x, csk 0x%p,%u,0x%lx, isn %u.\n",
+ (&csk->saddr), (&csk->daddr),
+ atid, tid, csk, csk->state, csk->flags, rcv_isn);
+
+ module_put(cdev->owner);
cxgbi_sock_get(csk);
csk->tid = tid;
- cxgb4_insert_tid(lldi->tids, csk, tid);
+ cxgb4_insert_tid(lldi->tids, csk, tid, csk->csk_family);
cxgbi_sock_set_flag(csk, CTPF_HAS_TID);
free_atid(csk);
@@ -592,7 +930,7 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb)
csk, csk->state, csk->flags, csk->tid);
if (csk->retry_timer.function) {
- del_timer(&csk->retry_timer);
+ timer_delete(&csk->retry_timer);
csk->retry_timer.function = NULL;
}
@@ -601,18 +939,18 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb)
* Causes the first RX_DATA_ACK to supply any Rx credits we couldn't
* pass through opt0.
*/
- if (cxgb4i_rcv_win > (RCV_BUFSIZ_MASK << 10))
- csk->rcv_wup -= cxgb4i_rcv_win - (RCV_BUFSIZ_MASK << 10);
+ if (csk->rcv_win > (RCV_BUFSIZ_MASK << 10))
+ csk->rcv_wup -= csk->rcv_win - (RCV_BUFSIZ_MASK << 10);
- csk->advmss = lldi->mtus[GET_TCPOPT_MSS(tcp_opt)] - 40;
- if (GET_TCPOPT_TSTAMP(tcp_opt))
+ csk->advmss = lldi->mtus[TCPOPT_MSS_G(tcp_opt)] - 40;
+ if (TCPOPT_TSTAMP_G(tcp_opt))
csk->advmss -= 12;
if (csk->advmss < 128)
csk->advmss = 128;
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p, mss_idx %u, advmss %u.\n",
- csk, GET_TCPOPT_MSS(tcp_opt), csk->advmss);
+ csk, TCPOPT_MSS_G(tcp_opt), csk->advmss);
cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt));
@@ -647,10 +985,14 @@ static int act_open_rpl_status_to_errno(int status)
}
}
-static void csk_act_open_retry_timer(unsigned long data)
+static void csk_act_open_retry_timer(struct timer_list *t)
{
- struct sk_buff *skb;
- struct cxgbi_sock *csk = (struct cxgbi_sock *)data;
+ struct sk_buff *skb = NULL;
+ struct cxgbi_sock *csk = timer_container_of(csk, t, retry_timer);
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(csk->cdev);
+ void (*send_act_open_func)(struct cxgbi_sock *, struct sk_buff *,
+ struct l2t_entry *);
+ int t4 = is_t4(lldi->adapter_type), size, size6;
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx,%u.\n",
@@ -658,17 +1000,44 @@ static void csk_act_open_retry_timer(unsigned long data)
cxgbi_sock_get(csk);
spin_lock_bh(&csk->lock);
- skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_ATOMIC);
+
+ if (t4) {
+ size = sizeof(struct cpl_act_open_req);
+ size6 = sizeof(struct cpl_act_open_req6);
+ } else {
+ size = sizeof(struct cpl_t5_act_open_req);
+ size6 = sizeof(struct cpl_t5_act_open_req6);
+ }
+
+ if (csk->csk_family == AF_INET) {
+ send_act_open_func = send_act_open_req;
+ skb = alloc_wr(size, 0, GFP_ATOMIC);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ send_act_open_func = send_act_open_req6;
+ skb = alloc_wr(size6, 0, GFP_ATOMIC);
+#endif
+ }
+
if (!skb)
cxgbi_sock_fail_act_open(csk, -ENOMEM);
else {
skb->sk = (struct sock *)csk;
t4_set_arp_err_handler(skb, csk,
- cxgbi_sock_act_open_req_arp_failure);
- send_act_open_req(csk, skb, csk->l2t);
+ cxgbi_sock_act_open_req_arp_failure);
+ send_act_open_func(csk, skb, csk->l2t);
}
+
spin_unlock_bh(&csk->lock);
cxgbi_sock_put(csk);
+
+}
+
+static inline bool is_neg_adv(unsigned int status)
+{
+ return status == CPL_ERR_RTX_NEG_ADVICE ||
+ status == CPL_ERR_KEEPALV_NEG_ADVICE ||
+ status == CPL_ERR_PERSIST_NEG_ADVICE;
}
static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
@@ -677,8 +1046,8 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
struct cpl_act_open_rpl *rpl = (struct cpl_act_open_rpl *)skb->data;
unsigned int tid = GET_TID(rpl);
unsigned int atid =
- GET_TID_TID(GET_AOPEN_ATID(be32_to_cpu(rpl->atid_status)));
- unsigned int status = GET_AOPEN_STATUS(be32_to_cpu(rpl->atid_status));
+ TID_TID_G(AOPEN_ATID_G(be32_to_cpu(rpl->atid_status)));
+ unsigned int status = AOPEN_STATUS_G(be32_to_cpu(rpl->atid_status));
struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
struct tid_info *t = lldi->tids;
@@ -688,18 +1057,20 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
goto rel_skb;
}
- pr_info("%pI4:%u-%pI4:%u, atid %u,%u, status %u, csk 0x%p,%u,0x%lx.\n",
- &csk->saddr.sin_addr.s_addr, ntohs(csk->saddr.sin_port),
- &csk->daddr.sin_addr.s_addr, ntohs(csk->daddr.sin_port),
- atid, tid, status, csk, csk->state, csk->flags);
+ pr_info_ipaddr("tid %u/%u, status %u.\n"
+ "csk 0x%p,%u,0x%lx. ", (&csk->saddr), (&csk->daddr),
+ atid, tid, status, csk, csk->state, csk->flags);
- if (status == CPL_ERR_RTX_NEG_ADVICE)
+ if (is_neg_adv(status))
goto rel_skb;
+ module_put(cdev->owner);
+
if (status && status != CPL_ERR_TCAM_FULL &&
status != CPL_ERR_CONN_EXIST &&
status != CPL_ERR_ARP_MISS)
- cxgb4_remove_tid(lldi->tids, csk->port_id, GET_TID(rpl));
+ cxgb4_remove_tid(lldi->tids, csk->port_id, GET_TID(rpl),
+ csk->csk_family);
cxgbi_sock_get(csk);
spin_lock_bh(&csk->lock);
@@ -731,9 +1102,9 @@ static void do_peer_close(struct cxgbi_device *cdev, struct sk_buff *skb)
pr_err("can't find connection for tid %u.\n", tid);
goto rel_skb;
}
- log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
- "csk 0x%p,%u,0x%lx,%u.\n",
- csk, csk->state, csk->flags, csk->tid);
+ pr_info_ipaddr("csk 0x%p,%u,0x%lx,%u.\n",
+ (&csk->saddr), (&csk->daddr),
+ csk, csk->state, csk->flags, csk->tid);
cxgbi_sock_rcv_peer_close(csk);
rel_skb:
__kfree_skb(skb);
@@ -752,9 +1123,9 @@ static void do_close_con_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
pr_err("can't find connection for tid %u.\n", tid);
goto rel_skb;
}
- log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
- "csk 0x%p,%u,0x%lx,%u.\n",
- csk, csk->state, csk->flags, csk->tid);
+ pr_info_ipaddr("csk 0x%p,%u,0x%lx,%u.\n",
+ (&csk->saddr), (&csk->daddr),
+ csk, csk->state, csk->flags, csk->tid);
cxgbi_sock_rcv_close_conn_rpl(csk, ntohl(rpl->snd_nxt));
rel_skb:
__kfree_skb(skb);
@@ -764,7 +1135,7 @@ static int abort_status_to_errno(struct cxgbi_sock *csk, int abort_reason,
int *need_rst)
{
switch (abort_reason) {
- case CPL_ERR_BAD_SYN: /* fall through */
+ case CPL_ERR_BAD_SYN:
case CPL_ERR_CONN_RESET:
return csk->state > CTP_ESTABLISHED ?
-EPIPE : -ECONNRESET;
@@ -793,31 +1164,33 @@ static void do_abort_req_rss(struct cxgbi_device *cdev, struct sk_buff *skb)
goto rel_skb;
}
- log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
- "csk 0x%p,%u,0x%lx, tid %u, status 0x%x.\n",
- csk, csk->state, csk->flags, csk->tid, req->status);
+ pr_info_ipaddr("csk 0x%p,%u,0x%lx,%u, status %u.\n",
+ (&csk->saddr), (&csk->daddr),
+ csk, csk->state, csk->flags, csk->tid, req->status);
- if (req->status == CPL_ERR_RTX_NEG_ADVICE ||
- req->status == CPL_ERR_PERSIST_NEG_ADVICE)
+ if (is_neg_adv(req->status))
goto rel_skb;
cxgbi_sock_get(csk);
spin_lock_bh(&csk->lock);
- if (!cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD)) {
- cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD);
- cxgbi_sock_set_state(csk, CTP_ABORTING);
- goto done;
+ cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD);
+
+ if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) {
+ send_tx_flowc_wr(csk);
+ cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
}
- cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD);
+ cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD);
+ cxgbi_sock_set_state(csk, CTP_ABORTING);
+
send_abort_rpl(csk, rst_status);
if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
csk->err = abort_status_to_errno(csk, req->status, &rst_status);
cxgbi_sock_closed(csk);
}
-done:
+
spin_unlock_bh(&csk->lock);
cxgbi_sock_put(csk);
rel_skb:
@@ -836,10 +1209,9 @@ static void do_abort_rpl_rss(struct cxgbi_device *cdev, struct sk_buff *skb)
if (!csk)
goto rel_skb;
- log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
- "status 0x%x, csk 0x%p, s %u, 0x%lx.\n",
- rpl->status, csk, csk ? csk->state : 0,
- csk ? csk->flags : 0UL);
+ pr_info_ipaddr("csk 0x%p,%u,0x%lx,%u, status %u.\n",
+ (&csk->saddr), (&csk->daddr), csk,
+ csk->state, csk->flags, csk->tid, rpl->status);
if (rpl->status == CPL_ERR_ABORT_FAILED)
goto rel_skb;
@@ -849,6 +1221,27 @@ rel_skb:
__kfree_skb(skb);
}
+static void do_rx_data(struct cxgbi_device *cdev, struct sk_buff *skb)
+{
+ struct cxgbi_sock *csk;
+ struct cpl_rx_data *cpl = (struct cpl_rx_data *)skb->data;
+ unsigned int tid = GET_TID(cpl);
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
+ struct tid_info *t = lldi->tids;
+
+ csk = lookup_tid(t, tid);
+ if (!csk) {
+ pr_err("can't find connection for tid %u.\n", tid);
+ } else {
+ /* not expecting this, reset the connection. */
+ pr_err("csk 0x%p, tid %u, rcv cpl_rx_data.\n", csk, tid);
+ spin_lock_bh(&csk->lock);
+ send_abort_req(csk);
+ spin_unlock_bh(&csk->lock);
+ }
+ __kfree_skb(skb);
+}
+
static void do_rx_iscsi_hdr(struct cxgbi_device *cdev, struct sk_buff *skb)
{
struct cxgbi_sock *csk;
@@ -890,7 +1283,7 @@ static void do_rx_iscsi_hdr(struct cxgbi_device *cdev, struct sk_buff *skb)
if (!csk->skb_ulp_lhdr) {
unsigned char *bhs;
- unsigned int hlen, dlen;
+ unsigned int hlen, dlen, plen;
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
"csk 0x%p,%u,0x%lx, tid %u, skb 0x%p header.\n",
@@ -898,7 +1291,8 @@ static void do_rx_iscsi_hdr(struct cxgbi_device *cdev, struct sk_buff *skb)
csk->skb_ulp_lhdr = skb;
cxgbi_skcb_set_flag(skb, SKCBF_RX_HDR);
- if (cxgbi_skcb_tcp_seq(skb) != csk->rcv_nxt) {
+ if ((CHELSIO_CHIP_VERSION(lldi->adapter_type) <= CHELSIO_T5) &&
+ (cxgbi_skcb_tcp_seq(skb) != csk->rcv_nxt)) {
pr_info("tid %u, CPL_ISCSI_HDR, bad seq, 0x%x/0x%x.\n",
csk->tid, cxgbi_skcb_tcp_seq(skb),
csk->rcv_nxt);
@@ -909,11 +1303,15 @@ static void do_rx_iscsi_hdr(struct cxgbi_device *cdev, struct sk_buff *skb)
hlen = ntohs(cpl->len);
dlen = ntohl(*(unsigned int *)(bhs + 4)) & 0xFFFFFF;
- if ((hlen + dlen) != ISCSI_PDU_LEN(pdu_len_ddp) - 40) {
+ plen = ISCSI_PDU_LEN_G(pdu_len_ddp);
+ if (is_t4(lldi->adapter_type))
+ plen -= 40;
+
+ if ((hlen + dlen) != plen) {
pr_info("tid 0x%x, CPL_ISCSI_HDR, pdu len "
"mismatch %u != %u + %u, seq 0x%x.\n",
- csk->tid, ISCSI_PDU_LEN(pdu_len_ddp) - 40,
- hlen, dlen, cxgbi_skcb_tcp_seq(skb));
+ csk->tid, plen, hlen, dlen,
+ cxgbi_skcb_tcp_seq(skb));
goto abort_conn;
}
@@ -949,6 +1347,101 @@ rel_skb:
__kfree_skb(skb);
}
+static void do_rx_iscsi_data(struct cxgbi_device *cdev, struct sk_buff *skb)
+{
+ struct cxgbi_sock *csk;
+ struct cpl_iscsi_hdr *cpl = (struct cpl_iscsi_hdr *)skb->data;
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
+ struct tid_info *t = lldi->tids;
+ struct sk_buff *lskb;
+ u32 tid = GET_TID(cpl);
+ u16 pdu_len_ddp = be16_to_cpu(cpl->pdu_len_ddp);
+
+ csk = lookup_tid(t, tid);
+ if (unlikely(!csk)) {
+ pr_err("can't find conn. for tid %u.\n", tid);
+ goto rel_skb;
+ }
+
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
+ "csk 0x%p,%u,0x%lx, tid %u, skb 0x%p,%u, 0x%x.\n",
+ csk, csk->state, csk->flags, csk->tid, skb,
+ skb->len, pdu_len_ddp);
+
+ spin_lock_bh(&csk->lock);
+
+ if (unlikely(csk->state >= CTP_PASSIVE_CLOSE)) {
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
+ "csk 0x%p,%u,0x%lx,%u, bad state.\n",
+ csk, csk->state, csk->flags, csk->tid);
+
+ if (csk->state != CTP_ABORTING)
+ goto abort_conn;
+ else
+ goto discard;
+ }
+
+ cxgbi_skcb_tcp_seq(skb) = be32_to_cpu(cpl->seq);
+ cxgbi_skcb_flags(skb) = 0;
+
+ skb_reset_transport_header(skb);
+ __skb_pull(skb, sizeof(*cpl));
+ __pskb_trim(skb, ntohs(cpl->len));
+
+ if (!csk->skb_ulp_lhdr)
+ csk->skb_ulp_lhdr = skb;
+
+ lskb = csk->skb_ulp_lhdr;
+ cxgbi_skcb_set_flag(lskb, SKCBF_RX_DATA);
+
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
+ "csk 0x%p,%u,0x%lx, skb 0x%p data, 0x%p.\n",
+ csk, csk->state, csk->flags, skb, lskb);
+
+ __skb_queue_tail(&csk->receive_queue, skb);
+ spin_unlock_bh(&csk->lock);
+ return;
+
+abort_conn:
+ send_abort_req(csk);
+discard:
+ spin_unlock_bh(&csk->lock);
+rel_skb:
+ __kfree_skb(skb);
+}
+
+static void
+cxgb4i_process_ddpvld(struct cxgbi_sock *csk,
+ struct sk_buff *skb, u32 ddpvld)
+{
+ if (ddpvld & (1 << CPL_RX_DDP_STATUS_HCRC_SHIFT)) {
+ pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, hcrc bad 0x%lx.\n",
+ csk, skb, ddpvld, cxgbi_skcb_flags(skb));
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_HCRC_ERR);
+ }
+
+ if (ddpvld & (1 << CPL_RX_DDP_STATUS_DCRC_SHIFT)) {
+ pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, dcrc bad 0x%lx.\n",
+ csk, skb, ddpvld, cxgbi_skcb_flags(skb));
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_DCRC_ERR);
+ }
+
+ if (ddpvld & (1 << CPL_RX_DDP_STATUS_PAD_SHIFT)) {
+ log_debug(1 << CXGBI_DBG_PDU_RX,
+ "csk 0x%p, lhdr 0x%p, status 0x%x, pad bad.\n",
+ csk, skb, ddpvld);
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_PAD_ERR);
+ }
+
+ if ((ddpvld & (1 << CPL_RX_DDP_STATUS_DDP_SHIFT)) &&
+ !cxgbi_skcb_test_flag(skb, SKCBF_RX_DATA)) {
+ log_debug(1 << CXGBI_DBG_PDU_RX,
+ "csk 0x%p, lhdr 0x%p, 0x%x, data ddp'ed.\n",
+ csk, skb, ddpvld);
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_DATA_DDPD);
+ }
+}
+
static void do_rx_data_ddp(struct cxgbi_device *cdev,
struct sk_buff *skb)
{
@@ -958,7 +1451,7 @@ static void do_rx_data_ddp(struct cxgbi_device *cdev,
unsigned int tid = GET_TID(rpl);
struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
struct tid_info *t = lldi->tids;
- unsigned int status = ntohl(rpl->ddpvld);
+ u32 ddpvld = be32_to_cpu(rpl->ddpvld);
csk = lookup_tid(t, tid);
if (unlikely(!csk)) {
@@ -968,7 +1461,7 @@ static void do_rx_data_ddp(struct cxgbi_device *cdev,
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
"csk 0x%p,%u,0x%lx, skb 0x%p,0x%x, lhdr 0x%p.\n",
- csk, csk->state, csk->flags, skb, status, csk->skb_ulp_lhdr);
+ csk, csk->state, csk->flags, skb, ddpvld, csk->skb_ulp_lhdr);
spin_lock_bh(&csk->lock);
@@ -996,29 +1489,8 @@ static void do_rx_data_ddp(struct cxgbi_device *cdev,
pr_info("tid 0x%x, RX_DATA_DDP pdulen %u != %u.\n",
csk->tid, ntohs(rpl->len), cxgbi_skcb_rx_pdulen(lskb));
- if (status & (1 << CPL_RX_DDP_STATUS_HCRC_SHIFT)) {
- pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, hcrc bad 0x%lx.\n",
- csk, lskb, status, cxgbi_skcb_flags(lskb));
- cxgbi_skcb_set_flag(lskb, SKCBF_RX_HCRC_ERR);
- }
- if (status & (1 << CPL_RX_DDP_STATUS_DCRC_SHIFT)) {
- pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, dcrc bad 0x%lx.\n",
- csk, lskb, status, cxgbi_skcb_flags(lskb));
- cxgbi_skcb_set_flag(lskb, SKCBF_RX_DCRC_ERR);
- }
- if (status & (1 << CPL_RX_DDP_STATUS_PAD_SHIFT)) {
- log_debug(1 << CXGBI_DBG_PDU_RX,
- "csk 0x%p, lhdr 0x%p, status 0x%x, pad bad.\n",
- csk, lskb, status);
- cxgbi_skcb_set_flag(lskb, SKCBF_RX_PAD_ERR);
- }
- if ((status & (1 << CPL_RX_DDP_STATUS_DDP_SHIFT)) &&
- !cxgbi_skcb_test_flag(lskb, SKCBF_RX_DATA)) {
- log_debug(1 << CXGBI_DBG_PDU_RX,
- "csk 0x%p, lhdr 0x%p, 0x%x, data ddp'ed.\n",
- csk, lskb, status);
- cxgbi_skcb_set_flag(lskb, SKCBF_RX_DATA_DDPD);
- }
+ cxgb4i_process_ddpvld(csk, lskb, ddpvld);
+
log_debug(1 << CXGBI_DBG_PDU_RX,
"csk 0x%p, lskb 0x%p, f 0x%lx.\n",
csk, lskb, cxgbi_skcb_flags(lskb));
@@ -1036,6 +1508,98 @@ rel_skb:
__kfree_skb(skb);
}
+static void
+do_rx_iscsi_cmp(struct cxgbi_device *cdev, struct sk_buff *skb)
+{
+ struct cxgbi_sock *csk;
+ struct cpl_rx_iscsi_cmp *rpl = (struct cpl_rx_iscsi_cmp *)skb->data;
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
+ struct tid_info *t = lldi->tids;
+ struct sk_buff *data_skb = NULL;
+ u32 tid = GET_TID(rpl);
+ u32 ddpvld = be32_to_cpu(rpl->ddpvld);
+ u32 seq = be32_to_cpu(rpl->seq);
+ u16 pdu_len_ddp = be16_to_cpu(rpl->pdu_len_ddp);
+
+ csk = lookup_tid(t, tid);
+ if (unlikely(!csk)) {
+ pr_err("can't find connection for tid %u.\n", tid);
+ goto rel_skb;
+ }
+
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
+ "csk 0x%p,%u,0x%lx, skb 0x%p,0x%x, lhdr 0x%p, len %u, "
+ "pdu_len_ddp %u, status %u.\n",
+ csk, csk->state, csk->flags, skb, ddpvld, csk->skb_ulp_lhdr,
+ ntohs(rpl->len), pdu_len_ddp, rpl->status);
+
+ spin_lock_bh(&csk->lock);
+
+ if (unlikely(csk->state >= CTP_PASSIVE_CLOSE)) {
+ log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
+ "csk 0x%p,%u,0x%lx,%u, bad state.\n",
+ csk, csk->state, csk->flags, csk->tid);
+
+ if (csk->state != CTP_ABORTING)
+ goto abort_conn;
+ else
+ goto discard;
+ }
+
+ cxgbi_skcb_tcp_seq(skb) = seq;
+ cxgbi_skcb_flags(skb) = 0;
+ cxgbi_skcb_rx_pdulen(skb) = 0;
+
+ skb_reset_transport_header(skb);
+ __skb_pull(skb, sizeof(*rpl));
+ __pskb_trim(skb, be16_to_cpu(rpl->len));
+
+ csk->rcv_nxt = seq + pdu_len_ddp;
+
+ if (csk->skb_ulp_lhdr) {
+ data_skb = skb_peek(&csk->receive_queue);
+ if (!data_skb ||
+ !cxgbi_skcb_test_flag(data_skb, SKCBF_RX_DATA)) {
+ pr_err("Error! freelist data not found 0x%p, tid %u\n",
+ data_skb, tid);
+
+ goto abort_conn;
+ }
+ __skb_unlink(data_skb, &csk->receive_queue);
+
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_DATA);
+
+ __skb_queue_tail(&csk->receive_queue, skb);
+ __skb_queue_tail(&csk->receive_queue, data_skb);
+ } else {
+ __skb_queue_tail(&csk->receive_queue, skb);
+ }
+
+ csk->skb_ulp_lhdr = NULL;
+
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_HDR);
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_STATUS);
+ cxgbi_skcb_set_flag(skb, SKCBF_RX_ISCSI_COMPL);
+ cxgbi_skcb_rx_ddigest(skb) = be32_to_cpu(rpl->ulp_crc);
+
+ cxgb4i_process_ddpvld(csk, skb, ddpvld);
+
+ log_debug(1 << CXGBI_DBG_PDU_RX, "csk 0x%p, skb 0x%p, f 0x%lx.\n",
+ csk, skb, cxgbi_skcb_flags(skb));
+
+ cxgbi_conn_pdu_ready(csk);
+ spin_unlock_bh(&csk->lock);
+
+ return;
+
+abort_conn:
+ send_abort_req(csk);
+discard:
+ spin_unlock_bh(&csk->lock);
+rel_skb:
+ __kfree_skb(skb);
+}
+
static void do_fw4_ack(struct cxgbi_device *cdev, struct sk_buff *skb)
{
struct cxgbi_sock *csk;
@@ -1066,16 +1630,22 @@ static void do_set_tcb_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
struct cxgbi_sock *csk;
csk = lookup_tid(t, tid);
- if (!csk)
+ if (!csk) {
pr_err("can't find conn. for tid %u.\n", tid);
+ return;
+ }
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,%lx,%u, status 0x%x.\n",
csk, csk->state, csk->flags, csk->tid, rpl->status);
- if (rpl->status != CPL_ERR_NONE)
+ if (rpl->status != CPL_ERR_NONE) {
pr_err("csk 0x%p,%u, SET_TCB_RPL status %u.\n",
csk, tid, rpl->status);
+ csk->err = -EINVAL;
+ }
+
+ complete(&csk->cmpl);
__kfree_skb(skb);
}
@@ -1115,59 +1685,171 @@ static inline void l2t_put(struct cxgbi_sock *csk)
static void release_offload_resources(struct cxgbi_sock *csk)
{
struct cxgb4_lld_info *lldi;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct net_device *ndev = csk->cdev->ports[csk->port_id];
+#endif
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx,%u.\n",
csk, csk->state, csk->flags, csk->tid);
cxgbi_sock_free_cpl_skbs(csk);
+ cxgbi_sock_purge_write_queue(csk);
if (csk->wr_cred != csk->wr_max_cred) {
cxgbi_sock_purge_wr_queue(csk);
cxgbi_sock_reset_wr_list(csk);
}
l2t_put(csk);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (csk->csk_family == AF_INET6)
+ cxgb4_clip_release(ndev,
+ (const u32 *)&csk->saddr6.sin6_addr, 1);
+#endif
+
if (cxgbi_sock_flag(csk, CTPF_HAS_ATID))
free_atid(csk);
else if (cxgbi_sock_flag(csk, CTPF_HAS_TID)) {
lldi = cxgbi_cdev_priv(csk->cdev);
- cxgb4_remove_tid(lldi->tids, 0, csk->tid);
+ cxgb4_remove_tid(lldi->tids, 0, csk->tid,
+ csk->csk_family);
cxgbi_sock_clear_flag(csk, CTPF_HAS_TID);
cxgbi_sock_put(csk);
}
csk->dst = NULL;
- csk->cdev = NULL;
}
+#ifdef CONFIG_CHELSIO_T4_DCB
+static inline u8 get_iscsi_dcb_state(struct net_device *ndev)
+{
+ return ndev->dcbnl_ops->getstate(ndev);
+}
+
+static int select_priority(int pri_mask)
+{
+ if (!pri_mask)
+ return 0;
+ return (ffs(pri_mask) - 1);
+}
+
+static u8 get_iscsi_dcb_priority(struct net_device *ndev)
+{
+ int rv;
+ u8 caps;
+
+ struct dcb_app iscsi_dcb_app = {
+ .protocol = 3260
+ };
+
+ rv = (int)ndev->dcbnl_ops->getcap(ndev, DCB_CAP_ATTR_DCBX, &caps);
+ if (rv)
+ return 0;
+
+ if (caps & DCB_CAP_DCBX_VER_IEEE) {
+ iscsi_dcb_app.selector = IEEE_8021QAZ_APP_SEL_STREAM;
+ rv = dcb_ieee_getapp_mask(ndev, &iscsi_dcb_app);
+ if (!rv) {
+ iscsi_dcb_app.selector = IEEE_8021QAZ_APP_SEL_ANY;
+ rv = dcb_ieee_getapp_mask(ndev, &iscsi_dcb_app);
+ }
+ } else if (caps & DCB_CAP_DCBX_VER_CEE) {
+ iscsi_dcb_app.selector = DCB_APP_IDTYPE_PORTNUM;
+ rv = dcb_getapp(ndev, &iscsi_dcb_app);
+ }
+
+ log_debug(1 << CXGBI_DBG_ISCSI,
+ "iSCSI priority is set to %u\n", select_priority(rv));
+ return select_priority(rv);
+}
+#endif
+
static int init_act_open(struct cxgbi_sock *csk)
{
struct cxgbi_device *cdev = csk->cdev;
struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
struct net_device *ndev = cdev->ports[csk->port_id];
- struct port_info *pi = netdev_priv(ndev);
struct sk_buff *skb = NULL;
+ struct neighbour *n = NULL;
+ void *daddr;
unsigned int step;
-
+ unsigned int rxq_idx;
+ unsigned int size, size6;
+ unsigned int linkspeed;
+ unsigned int rcv_winf, snd_winf;
+#ifdef CONFIG_CHELSIO_T4_DCB
+ u8 priority = 0;
+#endif
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx,%u.\n",
csk, csk->state, csk->flags, csk->tid);
+ if (csk->csk_family == AF_INET)
+ daddr = &csk->daddr.sin_addr.s_addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ else if (csk->csk_family == AF_INET6)
+ daddr = &csk->daddr6.sin6_addr;
+#endif
+ else {
+ pr_err("address family 0x%x not supported\n", csk->csk_family);
+ goto rel_resource;
+ }
+
+ n = dst_neigh_lookup(csk->dst, daddr);
+
+ if (!n) {
+ pr_err("%s, can't get neighbour of csk->dst.\n", ndev->name);
+ goto rel_resource;
+ }
+
+ if (!(n->nud_state & NUD_VALID))
+ neigh_event_send(n, NULL);
+
csk->atid = cxgb4_alloc_atid(lldi->tids, csk);
if (csk->atid < 0) {
pr_err("%s, NO atid available.\n", ndev->name);
- return -EINVAL;
+ goto rel_resource_without_clip;
}
cxgbi_sock_set_flag(csk, CTPF_HAS_ATID);
cxgbi_sock_get(csk);
- csk->l2t = cxgb4_l2t_get(lldi->l2t, csk->dst->neighbour, ndev, 0);
+#ifdef CONFIG_CHELSIO_T4_DCB
+ if (get_iscsi_dcb_state(ndev))
+ priority = get_iscsi_dcb_priority(ndev);
+
+ csk->dcb_priority = priority;
+ csk->l2t = cxgb4_l2t_get(lldi->l2t, n, ndev, priority);
+#else
+ csk->l2t = cxgb4_l2t_get(lldi->l2t, n, ndev, 0);
+#endif
if (!csk->l2t) {
pr_err("%s, cannot alloc l2t.\n", ndev->name);
- goto rel_resource;
+ goto rel_resource_without_clip;
}
cxgbi_sock_get(csk);
- skb = alloc_wr(sizeof(struct cpl_act_open_req), 0, GFP_KERNEL);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (csk->csk_family == AF_INET6)
+ cxgb4_clip_get(ndev, (const u32 *)&csk->saddr6.sin6_addr, 1);
+#endif
+
+ if (is_t4(lldi->adapter_type)) {
+ size = sizeof(struct cpl_act_open_req);
+ size6 = sizeof(struct cpl_act_open_req6);
+ } else if (is_t5(lldi->adapter_type)) {
+ size = sizeof(struct cpl_t5_act_open_req);
+ size6 = sizeof(struct cpl_t5_act_open_req6);
+ } else {
+ size = sizeof(struct cpl_t6_act_open_req);
+ size6 = sizeof(struct cpl_t6_act_open_req6);
+ }
+
+ if (csk->csk_family == AF_INET)
+ skb = alloc_wr(size, 0, GFP_NOIO);
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ skb = alloc_wr(size6, 0, GFP_NOIO);
+#endif
+
if (!skb)
goto rel_resource;
skb->sk = (struct sock *)csk;
@@ -1177,33 +1859,72 @@ static int init_act_open(struct cxgbi_sock *csk)
csk->mtu = dst_mtu(csk->dst);
cxgb4_best_mtu(lldi->mtus, csk->mtu, &csk->mss_idx);
csk->tx_chan = cxgb4_port_chan(ndev);
- /* SMT two entries per row */
- csk->smac_idx = ((cxgb4_port_viid(ndev) & 0x7F)) << 1;
+ csk->smac_idx = ((struct port_info *)netdev_priv(ndev))->smt_idx;
step = lldi->ntxq / lldi->nchan;
csk->txq_idx = cxgb4_port_idx(ndev) * step;
step = lldi->nrxq / lldi->nchan;
- csk->rss_qid = lldi->rxq_ids[cxgb4_port_idx(ndev) * step];
- csk->wr_max_cred = csk->wr_cred = lldi->wr_cred;
+ rxq_idx = (cxgb4_port_idx(ndev) * step) + (cdev->rxq_idx_cntr % step);
+ cdev->rxq_idx_cntr++;
+ csk->rss_qid = lldi->rxq_ids[rxq_idx];
+ linkspeed = ((struct port_info *)netdev_priv(ndev))->link_cfg.speed;
+ csk->snd_win = cxgb4i_snd_win;
+ csk->rcv_win = cxgb4i_rcv_win;
+ if (cxgb4i_rcv_win <= 0) {
+ csk->rcv_win = CXGB4I_DEFAULT_10G_RCV_WIN;
+ rcv_winf = linkspeed / SPEED_10000;
+ if (rcv_winf)
+ csk->rcv_win *= rcv_winf;
+ }
+ if (cxgb4i_snd_win <= 0) {
+ csk->snd_win = CXGB4I_DEFAULT_10G_SND_WIN;
+ snd_winf = linkspeed / SPEED_10000;
+ if (snd_winf)
+ csk->snd_win *= snd_winf;
+ }
+ csk->wr_cred = lldi->wr_cred -
+ DIV_ROUND_UP(sizeof(struct cpl_abort_req), 16);
+ csk->wr_max_cred = csk->wr_cred;
csk->wr_una_cred = 0;
cxgbi_sock_reset_wr_list(csk);
csk->err = 0;
- log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
- "csk 0x%p,p%d,%s, %u,%u,%u, mss %u,%u, smac %u.\n",
- csk, pi->port_id, ndev->name, csk->tx_chan,
- csk->txq_idx, csk->rss_qid, csk->mtu, csk->mss_idx,
- csk->smac_idx);
+
+ pr_info_ipaddr("csk 0x%p,%u,0x%lx,%u,%u,%u, mtu %u,%u, smac %u.\n",
+ (&csk->saddr), (&csk->daddr), csk, csk->state,
+ csk->flags, csk->tx_chan, csk->txq_idx, csk->rss_qid,
+ csk->mtu, csk->mss_idx, csk->smac_idx);
+
+ /* must wait for either a act_open_rpl or act_open_establish */
+ if (!try_module_get(cdev->owner)) {
+ pr_err("%s, try_module_get failed.\n", ndev->name);
+ goto rel_resource;
+ }
cxgbi_sock_set_state(csk, CTP_ACTIVE_OPEN);
- send_act_open_req(csk, skb, csk->l2t);
+ if (csk->csk_family == AF_INET)
+ send_act_open_req(csk, skb, csk->l2t);
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ send_act_open_req6(csk, skb, csk->l2t);
+#endif
+ neigh_release(n);
+
return 0;
rel_resource:
+#if IS_ENABLED(CONFIG_IPV6)
+ if (csk->csk_family == AF_INET6)
+ cxgb4_clip_release(ndev,
+ (const u32 *)&csk->saddr6.sin6_addr, 1);
+#endif
+rel_resource_without_clip:
+ if (n)
+ neigh_release(n);
if (skb)
__kfree_skb(skb);
return -EINVAL;
}
-cxgb4i_cplhandler_func cxgb4i_cplhandlers[NUM_CPL_CMDS] = {
+static cxgb4i_cplhandler_func cxgb4i_cplhandlers[NUM_CPL_CMDS] = {
[CPL_ACT_ESTABLISH] = do_act_establish,
[CPL_ACT_OPEN_RPL] = do_act_open_rpl,
[CPL_PEER_CLOSE] = do_peer_close,
@@ -1212,11 +1933,15 @@ cxgb4i_cplhandler_func cxgb4i_cplhandlers[NUM_CPL_CMDS] = {
[CPL_CLOSE_CON_RPL] = do_close_con_rpl,
[CPL_FW4_ACK] = do_fw4_ack,
[CPL_ISCSI_HDR] = do_rx_iscsi_hdr,
+ [CPL_ISCSI_DATA] = do_rx_iscsi_data,
[CPL_SET_TCB_RPL] = do_set_tcb_rpl,
[CPL_RX_DATA_DDP] = do_rx_data_ddp,
+ [CPL_RX_ISCSI_DDP] = do_rx_data_ddp,
+ [CPL_RX_ISCSI_CMP] = do_rx_iscsi_cmp,
+ [CPL_RX_DATA] = do_rx_data,
};
-int cxgb4i_ofld_init(struct cxgbi_device *cdev)
+static int cxgb4i_ofld_init(struct cxgbi_device *cdev)
{
int rc;
@@ -1240,105 +1965,117 @@ int cxgb4i_ofld_init(struct cxgbi_device *cdev)
return 0;
}
-/*
- * functions to program the pagepod in h/w
- */
-#define ULPMEM_IDATA_MAX_NPPODS 4 /* 256/PPOD_SIZE */
-static inline void ulp_mem_io_set_hdr(struct ulp_mem_io *req,
- unsigned int wr_len, unsigned int dlen,
- unsigned int pm_addr)
+static inline void
+ulp_mem_io_set_hdr(struct cxgbi_device *cdev,
+ struct ulp_mem_io *req,
+ unsigned int wr_len, unsigned int dlen,
+ unsigned int pm_addr,
+ int tid)
{
+ struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
struct ulptx_idata *idata = (struct ulptx_idata *)(req + 1);
- INIT_ULPTX_WR(req, wr_len, 0, 0);
- req->cmd = htonl(ULPTX_CMD(ULP_TX_MEM_WRITE) | (1 << 23));
- req->dlen = htonl(ULP_MEMIO_DATA_LEN(dlen >> 5));
- req->lock_addr = htonl(ULP_MEMIO_ADDR(pm_addr >> 5));
+ INIT_ULPTX_WR(req, wr_len, 0, tid);
+ req->wr.wr_hi = htonl(FW_WR_OP_V(FW_ULPTX_WR) |
+ FW_WR_ATOMIC_V(0));
+ req->cmd = htonl(ULPTX_CMD_V(ULP_TX_MEM_WRITE) |
+ ULP_MEMIO_ORDER_V(is_t4(lldi->adapter_type)) |
+ T5_ULP_MEMIO_IMM_V(!is_t4(lldi->adapter_type)));
+ req->dlen = htonl(ULP_MEMIO_DATA_LEN_V(dlen >> 5));
+ req->lock_addr = htonl(ULP_MEMIO_ADDR_V(pm_addr >> 5));
req->len16 = htonl(DIV_ROUND_UP(wr_len - sizeof(req->wr), 16));
- idata->cmd_more = htonl(ULPTX_CMD(ULP_TX_SC_IMM));
+ idata->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM));
idata->len = htonl(dlen);
}
-static int ddp_ppod_write_idata(struct cxgbi_device *cdev, unsigned int port_id,
- struct cxgbi_pagepod_hdr *hdr, unsigned int idx,
- unsigned int npods,
- struct cxgbi_gather_list *gl,
- unsigned int gl_pidx)
+static struct sk_buff *
+ddp_ppod_init_idata(struct cxgbi_device *cdev,
+ struct cxgbi_ppm *ppm,
+ unsigned int idx, unsigned int npods,
+ unsigned int tid)
{
- struct cxgbi_ddp_info *ddp = cdev->ddp;
- struct sk_buff *skb;
- struct ulp_mem_io *req;
- struct ulptx_idata *idata;
- struct cxgbi_pagepod *ppod;
- unsigned int pm_addr = idx * PPOD_SIZE + ddp->llimit;
- unsigned int dlen = PPOD_SIZE * npods;
+ unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit;
+ unsigned int dlen = npods << PPOD_SIZE_SHIFT;
unsigned int wr_len = roundup(sizeof(struct ulp_mem_io) +
sizeof(struct ulptx_idata) + dlen, 16);
- unsigned int i;
+ struct sk_buff *skb = alloc_wr(wr_len, 0, GFP_ATOMIC);
- skb = alloc_wr(wr_len, 0, GFP_ATOMIC);
if (!skb) {
- pr_err("cdev 0x%p, idx %u, npods %u, OOM.\n",
- cdev, idx, npods);
- return -ENOMEM;
+ pr_err("%s: %s idx %u, npods %u, OOM.\n",
+ __func__, ppm->ndev->name, idx, npods);
+ return NULL;
}
- req = (struct ulp_mem_io *)skb->head;
- set_queue(skb, CPL_PRIORITY_CONTROL, NULL);
- ulp_mem_io_set_hdr(req, wr_len, dlen, pm_addr);
+ ulp_mem_io_set_hdr(cdev, (struct ulp_mem_io *)skb->head, wr_len, dlen,
+ pm_addr, tid);
+
+ return skb;
+}
+
+static int ddp_ppod_write_idata(struct cxgbi_ppm *ppm, struct cxgbi_sock *csk,
+ struct cxgbi_task_tag_info *ttinfo,
+ unsigned int idx, unsigned int npods,
+ struct scatterlist **sg_pp,
+ unsigned int *sg_off)
+{
+ struct cxgbi_device *cdev = csk->cdev;
+ struct sk_buff *skb = ddp_ppod_init_idata(cdev, ppm, idx, npods,
+ csk->tid);
+ struct ulp_mem_io *req;
+ struct ulptx_idata *idata;
+ struct cxgbi_pagepod *ppod;
+ int i;
+
+ if (!skb)
+ return -ENOMEM;
+
+ req = (struct ulp_mem_io *)skb->head;
idata = (struct ulptx_idata *)(req + 1);
ppod = (struct cxgbi_pagepod *)(idata + 1);
- for (i = 0; i < npods; i++, ppod++, gl_pidx += PPOD_PAGES_MAX) {
- if (!hdr && !gl)
- cxgbi_ddp_ppod_clear(ppod);
- else
- cxgbi_ddp_ppod_set(ppod, hdr, gl, gl_pidx);
- }
+ for (i = 0; i < npods; i++, ppod++)
+ cxgbi_ddp_set_one_ppod(ppod, ttinfo, sg_pp, sg_off);
+
+ cxgbi_skcb_set_flag(skb, SKCBF_TX_MEM_WRITE);
+ cxgbi_skcb_set_flag(skb, SKCBF_TX_FLAG_COMPL);
+ set_wr_txq(skb, CPL_PRIORITY_DATA, csk->port_id);
+
+ spin_lock_bh(&csk->lock);
+ cxgbi_sock_skb_entail(csk, skb);
+ spin_unlock_bh(&csk->lock);
- cxgb4_ofld_send(cdev->ports[port_id], skb);
return 0;
}
-static int ddp_set_map(struct cxgbi_sock *csk, struct cxgbi_pagepod_hdr *hdr,
- unsigned int idx, unsigned int npods,
- struct cxgbi_gather_list *gl)
+static int ddp_set_map(struct cxgbi_ppm *ppm, struct cxgbi_sock *csk,
+ struct cxgbi_task_tag_info *ttinfo)
{
+ unsigned int pidx = ttinfo->idx;
+ unsigned int npods = ttinfo->npods;
unsigned int i, cnt;
int err = 0;
+ struct scatterlist *sg = ttinfo->sgl;
+ unsigned int offset = 0;
- for (i = 0; i < npods; i += cnt, idx += cnt) {
- cnt = npods - i;
- if (cnt > ULPMEM_IDATA_MAX_NPPODS)
- cnt = ULPMEM_IDATA_MAX_NPPODS;
- err = ddp_ppod_write_idata(csk->cdev, csk->port_id, hdr,
- idx, cnt, gl, 4 * i);
- if (err < 0)
- break;
- }
- return err;
-}
+ ttinfo->cid = csk->port_id;
-static void ddp_clear_map(struct cxgbi_hba *chba, unsigned int tag,
- unsigned int idx, unsigned int npods)
-{
- unsigned int i, cnt;
- int err;
-
- for (i = 0; i < npods; i += cnt, idx += cnt) {
+ for (i = 0; i < npods; i += cnt, pidx += cnt) {
cnt = npods - i;
+
if (cnt > ULPMEM_IDATA_MAX_NPPODS)
cnt = ULPMEM_IDATA_MAX_NPPODS;
- err = ddp_ppod_write_idata(chba->cdev, chba->port_id, NULL,
- idx, cnt, NULL, 0);
+ err = ddp_ppod_write_idata(ppm, csk, ttinfo, pidx, cnt,
+ &sg, &offset);
if (err < 0)
break;
}
+
+ return err;
}
static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk, unsigned int tid,
- int pg_idx, bool reply)
+ int pg_idx)
{
struct sk_buff *skb;
struct cpl_set_tcb_field *req;
@@ -1354,7 +2091,7 @@ static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk, unsigned int tid,
req = (struct cpl_set_tcb_field *)skb->head;
INIT_TP_WR(req, csk->tid);
OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, csk->tid));
- req->reply_ctrl = htons(NO_REPLY(reply) | QUEUENO(csk->rss_qid));
+ req->reply_ctrl = htons(NO_REPLY_V(0) | QUEUENO_V(csk->rss_qid));
req->word_cookie = htons(0);
req->mask = cpu_to_be64(0x3 << 8);
req->val = cpu_to_be64(pg_idx << 8);
@@ -1363,12 +2100,15 @@ static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk, unsigned int tid,
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p, tid 0x%x, pg_idx %u.\n", csk, csk->tid, pg_idx);
+ reinit_completion(&csk->cmpl);
cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb);
- return 0;
+ wait_for_completion(&csk->cmpl);
+
+ return csk->err;
}
static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid,
- int hcrc, int dcrc, int reply)
+ int hcrc, int dcrc)
{
struct sk_buff *skb;
struct cpl_set_tcb_field *req;
@@ -1386,7 +2126,7 @@ static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid,
req = (struct cpl_set_tcb_field *)skb->head;
INIT_TP_WR(req, tid);
OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
- req->reply_ctrl = htons(NO_REPLY(reply) | QUEUENO(csk->rss_qid));
+ req->reply_ctrl = htons(NO_REPLY_V(0) | QUEUENO_V(csk->rss_qid));
req->word_cookie = htons(0);
req->mask = cpu_to_be64(0x3 << 4);
req->val = cpu_to_be64(((hcrc ? ULP_CRC_HEADER : 0) |
@@ -1396,59 +2136,87 @@ static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid,
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p, tid 0x%x, crc %d,%d.\n", csk, csk->tid, hcrc, dcrc);
+ reinit_completion(&csk->cmpl);
cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb);
- return 0;
+ wait_for_completion(&csk->cmpl);
+
+ return csk->err;
+}
+
+static struct cxgbi_ppm *cdev2ppm(struct cxgbi_device *cdev)
+{
+ return (struct cxgbi_ppm *)(*((struct cxgb4_lld_info *)
+ (cxgbi_cdev_priv(cdev)))->iscsi_ppm);
}
static int cxgb4i_ddp_init(struct cxgbi_device *cdev)
{
struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
- struct cxgbi_ddp_info *ddp = cdev->ddp;
- unsigned int tagmask, pgsz_factor[4];
- int err;
+ struct net_device *ndev = cdev->ports[0];
+ struct cxgbi_tag_format tformat;
+ int i, err;
- if (ddp) {
- kref_get(&ddp->refcnt);
- pr_warn("cdev 0x%p, ddp 0x%p already set up.\n",
- cdev, cdev->ddp);
- return -EALREADY;
+ if (!lldi->vr->iscsi.size) {
+ pr_warn("%s, iscsi NOT enabled, check config!\n", ndev->name);
+ return -EACCES;
}
- err = cxgbi_ddp_init(cdev, lldi->vr->iscsi.start,
- lldi->vr->iscsi.start + lldi->vr->iscsi.size - 1,
- lldi->iscsi_iolen, lldi->iscsi_iolen);
- if (err < 0)
- return err;
+ cdev->flags |= CXGBI_FLAG_USE_PPOD_OFLDQ;
+
+ memset(&tformat, 0, sizeof(struct cxgbi_tag_format));
+ for (i = 0; i < 4; i++)
+ tformat.pgsz_order[i] = (lldi->iscsi_pgsz_order >> (i << 3))
+ & 0xF;
+ cxgbi_tagmask_check(lldi->iscsi_tagmask, &tformat);
- ddp = cdev->ddp;
+ pr_info("iscsi_edram.start 0x%x iscsi_edram.size 0x%x",
+ lldi->vr->ppod_edram.start, lldi->vr->ppod_edram.size);
- tagmask = ddp->idx_mask << PPOD_IDX_SHIFT;
- cxgbi_ddp_page_size_factor(pgsz_factor);
- cxgb4_iscsi_init(lldi->ports[0], tagmask, pgsz_factor);
+ err = cxgbi_ddp_ppm_setup(lldi->iscsi_ppm, cdev, &tformat,
+ lldi->vr->iscsi.size, lldi->iscsi_llimit,
+ lldi->vr->iscsi.start, 2,
+ lldi->vr->ppod_edram.start,
+ lldi->vr->ppod_edram.size);
+
+ if (err < 0)
+ return err;
cdev->csk_ddp_setup_digest = ddp_setup_conn_digest;
cdev->csk_ddp_setup_pgidx = ddp_setup_conn_pgidx;
- cdev->csk_ddp_set = ddp_set_map;
- cdev->csk_ddp_clear = ddp_clear_map;
-
- pr_info("cxgb4i 0x%p tag: sw %u, rsvd %u,%u, mask 0x%x.\n",
- cdev, cdev->tag_format.sw_bits, cdev->tag_format.rsvd_bits,
- cdev->tag_format.rsvd_shift, cdev->tag_format.rsvd_mask);
- pr_info("cxgb4i 0x%p, nppods %u, bits %u, mask 0x%x,0x%x pkt %u/%u, "
- " %u/%u.\n",
- cdev, ddp->nppods, ddp->idx_bits, ddp->idx_mask,
- ddp->rsvd_tag_mask, ddp->max_txsz, lldi->iscsi_iolen,
- ddp->max_rxsz, lldi->iscsi_iolen);
- pr_info("cxgb4i 0x%p max payload size: %u/%u, %u/%u.\n",
- cdev, cdev->tx_max_size, ddp->max_txsz, cdev->rx_max_size,
- ddp->max_rxsz);
+ cdev->csk_ddp_set_map = ddp_set_map;
+ cdev->tx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ lldi->iscsi_iolen - ISCSI_PDU_NONPAYLOAD_LEN);
+ cdev->rx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ lldi->iscsi_iolen - ISCSI_PDU_NONPAYLOAD_LEN);
+ cdev->cdev2ppm = cdev2ppm;
+
return 0;
}
+static bool is_memfree(struct adapter *adap)
+{
+ u32 io;
+
+ io = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A);
+ if (is_t5(adap->params.chip)) {
+ if ((io & EXT_MEM0_ENABLE_F) || (io & EXT_MEM1_ENABLE_F))
+ return false;
+ } else if (io & EXT_MEM_ENABLE_F) {
+ return false;
+ }
+
+ return true;
+}
+
static void *t4_uld_add(const struct cxgb4_lld_info *lldi)
{
struct cxgbi_device *cdev;
struct port_info *pi;
+ struct net_device *ndev;
+ struct adapter *adap;
+ struct tid_info *t;
+ u32 max_cmds = CXGB4I_SCSI_HOST_QDEPTH;
+ u32 max_conn = CXGBI_MAX_CONN;
int i, rc;
cdev = cxgbi_device_register(sizeof(*lldi), lldi->nports);
@@ -1472,30 +2240,56 @@ static void *t4_uld_add(const struct cxgb4_lld_info *lldi)
cdev->nports = lldi->nports;
cdev->mtus = lldi->mtus;
cdev->nmtus = NMTUS;
- cdev->snd_win = cxgb4i_snd_win;
- cdev->rcv_win = cxgb4i_rcv_win;
- cdev->rx_credit_thres = cxgb4i_rx_credit_thres;
+ cdev->rx_credit_thres = (CHELSIO_CHIP_VERSION(lldi->adapter_type) <=
+ CHELSIO_T5) ? cxgb4i_rx_credit_thres : 0;
cdev->skb_tx_rsvd = CXGB4I_TX_HEADER_LEN;
cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr);
cdev->itp = &cxgb4i_iscsi_transport;
+ cdev->owner = THIS_MODULE;
- cdev->pfvf = FW_VIID_PFN_GET(cxgb4_port_viid(lldi->ports[0])) << 8;
+ cdev->pfvf = FW_PFVF_CMD_PFN_V(lldi->pf);
pr_info("cdev 0x%p,%s, pfvf %u.\n",
cdev, lldi->ports[0]->name, cdev->pfvf);
rc = cxgb4i_ddp_init(cdev);
if (rc) {
- pr_info("t4 0x%p ddp init failed.\n", cdev);
+ pr_info("t4 0x%p ddp init failed %d.\n", cdev, rc);
goto err_out;
}
+
+ ndev = cdev->ports[0];
+ adap = netdev2adap(ndev);
+ if (adap) {
+ t = &adap->tids;
+ if (t->ntids <= CXGBI_MAX_CONN)
+ max_conn = t->ntids;
+
+ if (is_memfree(adap)) {
+ cdev->flags |= CXGBI_FLAG_DEV_ISO_OFF;
+ max_cmds = CXGB4I_SCSI_HOST_QDEPTH >> 2;
+
+ pr_info("%s: 0x%p, tid %u, SO adapter.\n",
+ ndev->name, cdev, t->ntids);
+ }
+ } else {
+ pr_info("%s, 0x%p, NO adapter struct.\n", ndev->name, cdev);
+ }
+
+ /* ISO is enabled in T5/T6 firmware version >= 1.13.43.0 */
+ if (!is_t4(lldi->adapter_type) &&
+ (lldi->fw_vers >= 0x10d2b00) &&
+ !(cdev->flags & CXGBI_FLAG_DEV_ISO_OFF))
+ cdev->skb_iso_txhdr = sizeof(struct cpl_tx_data_iso);
+
rc = cxgb4i_ofld_init(cdev);
if (rc) {
pr_info("t4 0x%p ofld init failed.\n", cdev);
goto err_out;
}
- rc = cxgbi_hbas_add(cdev, CXGB4I_MAX_LUN, CXGBI_MAX_CONN,
- &cxgb4i_host_template, cxgb4i_stt);
+ cxgb4i_host_template.can_queue = max_cmds;
+ rc = cxgbi_hbas_add(cdev, CXGB4I_MAX_LUN, max_conn,
+ &cxgb4i_host_template, cxgb4i_stt);
if (rc)
goto err_out;
@@ -1544,12 +2338,12 @@ static int t4_uld_rx_handler(void *handle, const __be64 *rsp,
log_debug(1 << CXGBI_DBG_TOE,
"cdev %p, opcode 0x%x(0x%x,0x%x), skb %p.\n",
cdev, opc, rpl->ot.opcode_tid, ntohl(rpl->ot.opcode_tid), skb);
- if (cxgb4i_cplhandlers[opc])
- cxgb4i_cplhandlers[opc](cdev, skb);
- else {
+ if (opc >= ARRAY_SIZE(cxgb4i_cplhandlers) || !cxgb4i_cplhandlers[opc]) {
pr_err("No handler for opcode 0x%x.\n", opc);
__kfree_skb(skb);
- }
+ } else
+ cxgb4i_cplhandlers[opc](cdev, skb);
+
return 0;
nomem:
log_debug(1 << CXGBI_DBG_TOE, "OOM bailing out.\n");
@@ -1563,7 +2357,6 @@ static int t4_uld_state_change(void *handle, enum cxgb4_state state)
switch (state) {
case CXGB4_STATE_UP:
pr_info("cdev 0x%p, UP.\n", cdev);
- /* re-initialize */
break;
case CXGB4_STATE_START_RECOVERY:
pr_info("cdev 0x%p, RECOVERY.\n", cdev);
@@ -1574,6 +2367,7 @@ static int t4_uld_state_change(void *handle, enum cxgb4_state state)
break;
case CXGB4_STATE_DETACH:
pr_info("cdev 0x%p, DETACH.\n", cdev);
+ cxgbi_device_unregister(cdev);
break;
default:
pr_info("cdev 0x%p, unknown state %d.\n", cdev, state);
@@ -1582,6 +2376,71 @@ static int t4_uld_state_change(void *handle, enum cxgb4_state state)
return 0;
}
+#ifdef CONFIG_CHELSIO_T4_DCB
+static int
+cxgb4_dcb_change_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ int i, port = 0xFF;
+ struct net_device *ndev;
+ struct cxgbi_device *cdev = NULL;
+ struct dcb_app_type *iscsi_app = data;
+ struct cxgbi_ports_map *pmap;
+ u8 priority;
+
+ if (iscsi_app->dcbx & DCB_CAP_DCBX_VER_IEEE) {
+ if ((iscsi_app->app.selector != IEEE_8021QAZ_APP_SEL_STREAM) &&
+ (iscsi_app->app.selector != IEEE_8021QAZ_APP_SEL_ANY))
+ return NOTIFY_DONE;
+
+ priority = iscsi_app->app.priority;
+ } else if (iscsi_app->dcbx & DCB_CAP_DCBX_VER_CEE) {
+ if (iscsi_app->app.selector != DCB_APP_IDTYPE_PORTNUM)
+ return NOTIFY_DONE;
+
+ if (!iscsi_app->app.priority)
+ return NOTIFY_DONE;
+
+ priority = ffs(iscsi_app->app.priority) - 1;
+ } else {
+ return NOTIFY_DONE;
+ }
+
+ if (iscsi_app->app.protocol != 3260)
+ return NOTIFY_DONE;
+
+ log_debug(1 << CXGBI_DBG_ISCSI, "iSCSI priority for ifid %d is %u\n",
+ iscsi_app->ifindex, priority);
+
+ ndev = dev_get_by_index(&init_net, iscsi_app->ifindex);
+ if (!ndev)
+ return NOTIFY_DONE;
+
+ cdev = cxgbi_device_find_by_netdev_rcu(ndev, &port);
+
+ dev_put(ndev);
+ if (!cdev)
+ return NOTIFY_DONE;
+
+ pmap = &cdev->pmap;
+
+ for (i = 0; i < pmap->used; i++) {
+ if (pmap->port_csk[i]) {
+ struct cxgbi_sock *csk = pmap->port_csk[i];
+
+ if (csk->dcb_priority != priority) {
+ iscsi_conn_failure(csk->user_data,
+ ISCSI_ERR_CONN_FAILED);
+ pr_info("Restarting iSCSI connection %p with "
+ "priority %u->%u.\n", csk,
+ csk->dcb_priority, priority);
+ }
+ }
+ }
+ return NOTIFY_OK;
+}
+#endif
+
static int __init cxgb4i_init_module(void)
{
int rc;
@@ -1592,11 +2451,19 @@ static int __init cxgb4i_init_module(void)
if (rc < 0)
return rc;
cxgb4_register_uld(CXGB4_ULD_ISCSI, &cxgb4i_uld_info);
+
+#ifdef CONFIG_CHELSIO_T4_DCB
+ pr_info("%s dcb enabled.\n", DRV_MODULE_NAME);
+ register_dcbevent_notifier(&cxgb4_dcb_change);
+#endif
return 0;
}
static void __exit cxgb4i_exit_module(void)
{
+#ifdef CONFIG_CHELSIO_T4_DCB
+ unregister_dcbevent_notifier(&cxgb4_dcb_change);
+#endif
cxgb4_unregister_uld(CXGB4_ULD_ISCSI);
cxgbi_device_unregister_all(CXGBI_FLAG_DEV_T4);
cxgbi_iscsi_cleanup(&cxgb4i_iscsi_transport, &cxgb4i_stt);
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h
index 1096026ba241..2fd9c76fc21c 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h
@@ -1,7 +1,7 @@
/*
* cxgb4i.h: Chelsio T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,21 +23,6 @@
#define CXGB4I_TX_HEADER_LEN \
(sizeof(struct fw_ofld_tx_data_wr) + sizeof(struct sge_opaque_hdr))
-struct ulptx_idata {
- __be32 cmd_more;
- __be32 len;
-};
+#define T5_ISS_VALID (1 << 18)
-struct cpl_rx_data_ddp {
- union opcode_tid ot;
- __be16 urg;
- __be16 len;
- __be32 seq;
- union {
- __be32 nxt_seq;
- __be32 ddp_report;
- };
- __be32 ulp_crc;
- __be32 ddpvld;
-};
#endif /* __CXGB4I_H__ */
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index a2a9c7c6c643..bf75940f2be1 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -1,7 +1,7 @@
/*
* libcxgbi.c: Chelsio common library for T3/T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,12 @@
#include <linux/inet.h>
#include <net/dst.h>
#include <net/route.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
#include <linux/inetdevice.h> /* ip_dev_find */
+#include <linux/module.h>
#include <net/tcp.h>
static unsigned int dbg_level;
@@ -33,8 +38,12 @@ static unsigned int dbg_level;
#define DRV_MODULE_NAME "libcxgbi"
#define DRV_MODULE_DESC "Chelsio iSCSI driver library"
-#define DRV_MODULE_VERSION "0.9.0"
-#define DRV_MODULE_RELDATE "Jun. 2010"
+#define DRV_MODULE_VERSION "0.9.1-ko"
+#define DRV_MODULE_RELDATE "Apr. 2015"
+
+static char version[] =
+ DRV_MODULE_DESC " " DRV_MODULE_NAME
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Chelsio Communications, Inc.");
MODULE_DESCRIPTION(DRV_MODULE_DESC);
@@ -52,14 +61,25 @@ MODULE_PARM_DESC(dbg_level, "libiscsi debug level (default=0)");
static LIST_HEAD(cdev_list);
static DEFINE_MUTEX(cdev_mutex);
+static LIST_HEAD(cdev_rcu_list);
+static DEFINE_SPINLOCK(cdev_rcu_lock);
+
+static inline void cxgbi_decode_sw_tag(u32 sw_tag, int *idx, int *age)
+{
+ if (age)
+ *age = sw_tag & 0x7FFF;
+ if (idx)
+ *idx = (sw_tag >> 16) & 0x7FFF;
+}
+
int cxgbi_device_portmap_create(struct cxgbi_device *cdev, unsigned int base,
unsigned int max_conn)
{
struct cxgbi_ports_map *pmap = &cdev->pmap;
- pmap->port_csk = cxgbi_alloc_big_mem(max_conn *
- sizeof(struct cxgbi_sock *),
- GFP_KERNEL);
+ pmap->port_csk = kvzalloc(array_size(max_conn,
+ sizeof(struct cxgbi_sock *)),
+ GFP_KERNEL | __GFP_NOWARN);
if (!pmap->port_csk) {
pr_warn("cdev 0x%p, portmap OOM %u.\n", cdev, max_conn);
return -ENOMEM;
@@ -101,14 +121,10 @@ static inline void cxgbi_device_destroy(struct cxgbi_device *cdev)
"cdev 0x%p, p# %u.\n", cdev, cdev->nports);
cxgbi_hbas_remove(cdev);
cxgbi_device_portmap_cleanup(cdev);
- if (cdev->dev_ddp_cleanup)
- cdev->dev_ddp_cleanup(cdev);
- else
- cxgbi_ddp_cleanup(cdev);
- if (cdev->ddp)
- cxgbi_ddp_cleanup(cdev);
+ if (cdev->cdev2ppm)
+ cxgbi_ppm_release(cdev->cdev2ppm(cdev));
if (cdev->pmap.max_connect)
- cxgbi_free_big_mem(cdev->pmap.port_csk);
+ kvfree(cdev->pmap.port_csk);
kfree(cdev);
}
@@ -137,6 +153,10 @@ struct cxgbi_device *cxgbi_device_register(unsigned int extra,
list_add_tail(&cdev->list_head, &cdev_list);
mutex_unlock(&cdev_mutex);
+ spin_lock(&cdev_rcu_lock);
+ list_add_tail_rcu(&cdev->rcu_node, &cdev_rcu_list);
+ spin_unlock(&cdev_rcu_lock);
+
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p# %u.\n", cdev, nports);
return cdev;
@@ -148,9 +168,16 @@ void cxgbi_device_unregister(struct cxgbi_device *cdev)
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p# %u,%s.\n",
cdev, cdev->nports, cdev->nports ? cdev->ports[0]->name : "");
+
mutex_lock(&cdev_mutex);
list_del(&cdev->list_head);
mutex_unlock(&cdev_mutex);
+
+ spin_lock(&cdev_rcu_lock);
+ list_del_rcu(&cdev->rcu_node);
+ spin_unlock(&cdev_rcu_lock);
+ synchronize_rcu();
+
cxgbi_device_destroy(cdev);
}
EXPORT_SYMBOL_GPL(cxgbi_device_unregister);
@@ -162,12 +189,9 @@ void cxgbi_device_unregister_all(unsigned int flag)
mutex_lock(&cdev_mutex);
list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
if ((cdev->flags & flag) == flag) {
- log_debug(1 << CXGBI_DBG_DEV,
- "cdev 0x%p, p# %u,%s.\n",
- cdev, cdev->nports, cdev->nports ?
- cdev->ports[0]->name : "");
- list_del(&cdev->list_head);
- cxgbi_device_destroy(cdev);
+ mutex_unlock(&cdev_mutex);
+ cxgbi_device_unregister(cdev);
+ mutex_lock(&cdev_mutex);
}
}
mutex_unlock(&cdev_mutex);
@@ -186,20 +210,21 @@ struct cxgbi_device *cxgbi_device_find_by_lldev(void *lldev)
}
}
mutex_unlock(&cdev_mutex);
+
log_debug(1 << CXGBI_DBG_DEV,
"lldev 0x%p, NO match found.\n", lldev);
return NULL;
}
EXPORT_SYMBOL_GPL(cxgbi_device_find_by_lldev);
-static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
- int *port)
+struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
+ int *port)
{
struct net_device *vdev = NULL;
struct cxgbi_device *cdev, *tmp;
int i;
- if (ndev->priv_flags & IFF_802_1Q_VLAN) {
+ if (is_vlan_dev(ndev)) {
vdev = ndev;
ndev = vlan_dev_real_dev(ndev);
log_debug(1 << CXGBI_DBG_DEV,
@@ -223,6 +248,73 @@ static struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *ndev,
"ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
return NULL;
}
+EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev);
+
+struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *ndev,
+ int *port)
+{
+ struct net_device *vdev = NULL;
+ struct cxgbi_device *cdev;
+ int i;
+
+ if (is_vlan_dev(ndev)) {
+ vdev = ndev;
+ ndev = vlan_dev_real_dev(ndev);
+ pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
+ }
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(cdev, &cdev_rcu_list, rcu_node) {
+ for (i = 0; i < cdev->nports; i++) {
+ if (ndev == cdev->ports[i]) {
+ cdev->hbas[i]->vdev = vdev;
+ rcu_read_unlock();
+ if (port)
+ *port = i;
+ return cdev;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ log_debug(1 << CXGBI_DBG_DEV,
+ "ndev 0x%p, %s, NO match found.\n", ndev, ndev->name);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(cxgbi_device_find_by_netdev_rcu);
+
+static struct cxgbi_device *cxgbi_device_find_by_mac(struct net_device *ndev,
+ int *port)
+{
+ struct net_device *vdev = NULL;
+ struct cxgbi_device *cdev, *tmp;
+ int i;
+
+ if (is_vlan_dev(ndev)) {
+ vdev = ndev;
+ ndev = vlan_dev_real_dev(ndev);
+ pr_info("vlan dev %s -> %s.\n", vdev->name, ndev->name);
+ }
+
+ mutex_lock(&cdev_mutex);
+ list_for_each_entry_safe(cdev, tmp, &cdev_list, list_head) {
+ for (i = 0; i < cdev->nports; i++) {
+ if (!memcmp(ndev->dev_addr, cdev->ports[i]->dev_addr,
+ MAX_ADDR_LEN)) {
+ cdev->hbas[i]->vdev = vdev;
+ mutex_unlock(&cdev_mutex);
+ if (port)
+ *port = i;
+ return cdev;
+ }
+ }
+ }
+ mutex_unlock(&cdev_mutex);
+ log_debug(1 << CXGBI_DBG_DEV,
+ "ndev 0x%p, %s, NO match mac found.\n",
+ ndev, ndev->name);
+ return NULL;
+}
void cxgbi_hbas_remove(struct cxgbi_device *cdev)
{
@@ -236,7 +328,7 @@ void cxgbi_hbas_remove(struct cxgbi_device *cdev)
chba = cdev->hbas[i];
if (chba) {
cdev->hbas[i] = NULL;
- iscsi_host_remove(chba->shost);
+ iscsi_host_remove(chba->shost, false);
pci_dev_put(cdev->pdev);
iscsi_host_free(chba->shost);
}
@@ -244,8 +336,8 @@ void cxgbi_hbas_remove(struct cxgbi_device *cdev)
}
EXPORT_SYMBOL_GPL(cxgbi_hbas_remove);
-int cxgbi_hbas_add(struct cxgbi_device *cdev, unsigned int max_lun,
- unsigned int max_id, struct scsi_host_template *sht,
+int cxgbi_hbas_add(struct cxgbi_device *cdev, u64 max_lun,
+ unsigned int max_conns, const struct scsi_host_template *sht,
struct scsi_transport_template *stt)
{
struct cxgbi_hba *chba;
@@ -265,15 +357,17 @@ int cxgbi_hbas_add(struct cxgbi_device *cdev, unsigned int max_lun,
shost->transportt = stt;
shost->max_lun = max_lun;
- shost->max_id = max_id;
+ shost->max_id = max_conns - 1;
shost->max_channel = 0;
- shost->max_cmd_len = 16;
+ shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
chba = iscsi_host_priv(shost);
chba->cdev = cdev;
chba->ndev = cdev->ports[i];
chba->shost = shost;
+ shost->can_queue = sht->can_queue - ISCSI_MGMT_CMDS_MAX;
+
log_debug(1 << CXGBI_DBG_DEV,
"cdev 0x%p, p#%d %s: chba 0x%p.\n",
cdev, i, cdev->ports[i]->name, chba);
@@ -313,12 +407,42 @@ EXPORT_SYMBOL_GPL(cxgbi_hbas_add);
* If the source port is outside our allocation range, the caller is
* responsible for keeping track of their port usage.
*/
+
+static struct cxgbi_sock *find_sock_on_port(struct cxgbi_device *cdev,
+ unsigned char port_id)
+{
+ struct cxgbi_ports_map *pmap = &cdev->pmap;
+ unsigned int i;
+ unsigned int used;
+
+ if (!pmap->max_connect || !pmap->used)
+ return NULL;
+
+ spin_lock_bh(&pmap->lock);
+ used = pmap->used;
+ for (i = 0; used && i < pmap->max_connect; i++) {
+ struct cxgbi_sock *csk = pmap->port_csk[i];
+
+ if (csk) {
+ if (csk->port_id == port_id) {
+ spin_unlock_bh(&pmap->lock);
+ return csk;
+ }
+ used--;
+ }
+ }
+ spin_unlock_bh(&pmap->lock);
+
+ return NULL;
+}
+
static int sock_get_port(struct cxgbi_sock *csk)
{
struct cxgbi_device *cdev = csk->cdev;
struct cxgbi_ports_map *pmap = &cdev->pmap;
unsigned int start;
int idx;
+ __be16 *port;
if (!pmap->max_connect) {
pr_err("cdev 0x%p, p#%u %s, NO port map.\n",
@@ -326,9 +450,14 @@ static int sock_get_port(struct cxgbi_sock *csk)
return -EADDRNOTAVAIL;
}
- if (csk->saddr.sin_port) {
+ if (csk->csk_family == AF_INET)
+ port = &csk->saddr.sin_port;
+ else /* ipv6 */
+ port = &csk->saddr6.sin6_port;
+
+ if (*port) {
pr_err("source port NON-ZERO %u.\n",
- ntohs(csk->saddr.sin_port));
+ ntohs(*port));
return -EADDRINUSE;
}
@@ -346,8 +475,7 @@ static int sock_get_port(struct cxgbi_sock *csk)
idx = 0;
if (!pmap->port_csk[idx]) {
pmap->used++;
- csk->saddr.sin_port =
- htons(pmap->sport_base + idx);
+ *port = htons(pmap->sport_base + idx);
pmap->next = idx;
pmap->port_csk[idx] = csk;
spin_unlock_bh(&pmap->lock);
@@ -373,16 +501,22 @@ static void sock_put_port(struct cxgbi_sock *csk)
{
struct cxgbi_device *cdev = csk->cdev;
struct cxgbi_ports_map *pmap = &cdev->pmap;
+ __be16 *port;
+
+ if (csk->csk_family == AF_INET)
+ port = &csk->saddr.sin_port;
+ else /* ipv6 */
+ port = &csk->saddr6.sin6_port;
- if (csk->saddr.sin_port) {
- int idx = ntohs(csk->saddr.sin_port) - pmap->sport_base;
+ if (*port) {
+ int idx = ntohs(*port) - pmap->sport_base;
- csk->saddr.sin_port = 0;
+ *port = 0;
if (idx < 0 || idx >= pmap->max_connect) {
pr_err("cdev 0x%p, p#%u %s, port %u OOR.\n",
cdev, csk->port_id,
cdev->ports[csk->port_id]->name,
- ntohs(csk->saddr.sin_port));
+ ntohs(*port));
return;
}
@@ -439,7 +573,8 @@ static struct cxgbi_sock *cxgbi_sock_create(struct cxgbi_device *cdev)
kref_init(&csk->refcnt);
skb_queue_head_init(&csk->receive_queue);
skb_queue_head_init(&csk->write_queue);
- setup_timer(&csk->retry_timer, NULL, (unsigned long)csk);
+ timer_setup(&csk->retry_timer, NULL, 0);
+ init_completion(&csk->cmpl);
rwlock_init(&csk->callback_lock);
csk->cdev = cdev;
csk->flags = 0;
@@ -452,69 +587,84 @@ static struct cxgbi_sock *cxgbi_sock_create(struct cxgbi_device *cdev)
static struct rtable *find_route_ipv4(struct flowi4 *fl4,
__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport, u8 tos)
+ __be16 sport, __be16 dport, u8 tos,
+ int ifindex)
{
struct rtable *rt;
rt = ip_route_output_ports(&init_net, fl4, NULL, daddr, saddr,
- dport, sport, IPPROTO_TCP, tos, 0);
+ dport, sport, IPPROTO_TCP, tos, ifindex);
if (IS_ERR(rt))
return NULL;
return rt;
}
-static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
+static struct cxgbi_sock *
+cxgbi_check_route(struct sockaddr *dst_addr, int ifindex)
{
struct sockaddr_in *daddr = (struct sockaddr_in *)dst_addr;
struct dst_entry *dst;
struct net_device *ndev;
struct cxgbi_device *cdev;
struct rtable *rt = NULL;
+ struct neighbour *n;
struct flowi4 fl4;
struct cxgbi_sock *csk = NULL;
unsigned int mtu = 0;
int port = 0xFFFF;
int err = 0;
- if (daddr->sin_family != AF_INET) {
- pr_info("address family 0x%x NOT supported.\n",
- daddr->sin_family);
- err = -EAFNOSUPPORT;
- goto err_out;
- }
-
- rt = find_route_ipv4(&fl4, 0, daddr->sin_addr.s_addr, 0, daddr->sin_port, 0);
+ rt = find_route_ipv4(&fl4, 0, daddr->sin_addr.s_addr, 0,
+ daddr->sin_port, 0, ifindex);
if (!rt) {
pr_info("no route to ipv4 0x%x, port %u.\n",
- daddr->sin_addr.s_addr, daddr->sin_port);
+ be32_to_cpu(daddr->sin_addr.s_addr),
+ be16_to_cpu(daddr->sin_port));
err = -ENETUNREACH;
goto err_out;
}
dst = &rt->dst;
- ndev = dst->neighbour->dev;
+ n = dst_neigh_lookup(dst, &daddr->sin_addr.s_addr);
+ if (!n) {
+ err = -ENODEV;
+ goto rel_rt;
+ }
+ ndev = n->dev;
if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
pr_info("multi-cast route %pI4, port %u, dev %s.\n",
&daddr->sin_addr.s_addr, ntohs(daddr->sin_port),
ndev->name);
err = -ENETUNREACH;
- goto rel_rt;
+ goto rel_neigh;
}
if (ndev->flags & IFF_LOOPBACK) {
ndev = ip_dev_find(&init_net, daddr->sin_addr.s_addr);
+ if (!ndev) {
+ err = -ENETUNREACH;
+ goto rel_neigh;
+ }
mtu = ndev->mtu;
pr_info("rt dev %s, loopback -> %s, mtu %u.\n",
- dst->neighbour->dev->name, ndev->name, mtu);
+ n->dev->name, ndev->name, mtu);
+ }
+
+ if (!(ndev->flags & IFF_UP) || !netif_carrier_ok(ndev)) {
+ pr_info("%s interface not up.\n", ndev->name);
+ err = -ENETDOWN;
+ goto rel_neigh;
}
cdev = cxgbi_device_find_by_netdev(ndev, &port);
+ if (!cdev)
+ cdev = cxgbi_device_find_by_mac(ndev, &port);
if (!cdev) {
pr_info("dst %pI4, %s, NOT cxgbi device.\n",
&daddr->sin_addr.s_addr, ndev->name);
err = -ENETUNREACH;
- goto rel_rt;
+ goto rel_neigh;
}
log_debug(1 << CXGBI_DBG_SOCK,
"route to %pI4 :%u, ndev p#%d,%s, cdev 0x%p.\n",
@@ -524,26 +674,158 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
csk = cxgbi_sock_create(cdev);
if (!csk) {
err = -ENOMEM;
- goto rel_rt;
+ goto rel_neigh;
}
csk->cdev = cdev;
csk->port_id = port;
csk->mtu = mtu;
csk->dst = dst;
+
+ csk->csk_family = AF_INET;
csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr;
csk->daddr.sin_port = daddr->sin_port;
csk->daddr.sin_family = daddr->sin_family;
+ csk->saddr.sin_family = daddr->sin_family;
csk->saddr.sin_addr.s_addr = fl4.saddr;
+ neigh_release(n);
return csk;
+rel_neigh:
+ neigh_release(n);
+
rel_rt:
ip_rt_put(rt);
+err_out:
+ return ERR_PTR(err);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct rt6_info *find_route_ipv6(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ int ifindex)
+{
+ struct flowi6 fl;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.flowi6_oif = ifindex;
+ if (saddr)
+ memcpy(&fl.saddr, saddr, sizeof(struct in6_addr));
+ if (daddr)
+ memcpy(&fl.daddr, daddr, sizeof(struct in6_addr));
+ return (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
+}
+
+static struct cxgbi_sock *
+cxgbi_check_route6(struct sockaddr *dst_addr, int ifindex)
+{
+ struct sockaddr_in6 *daddr6 = (struct sockaddr_in6 *)dst_addr;
+ struct dst_entry *dst;
+ struct net_device *ndev;
+ struct cxgbi_device *cdev;
+ struct rt6_info *rt = NULL;
+ struct neighbour *n;
+ struct in6_addr pref_saddr;
+ struct cxgbi_sock *csk = NULL;
+ unsigned int mtu = 0;
+ int port = 0xFFFF;
+ int err = 0;
+
+ rt = find_route_ipv6(NULL, &daddr6->sin6_addr, ifindex);
+
+ if (!rt) {
+ pr_info("no route to ipv6 %pI6 port %u\n",
+ daddr6->sin6_addr.s6_addr,
+ be16_to_cpu(daddr6->sin6_port));
+ err = -ENETUNREACH;
+ goto err_out;
+ }
+
+ dst = &rt->dst;
+
+ n = dst_neigh_lookup(dst, &daddr6->sin6_addr);
+
+ if (!n) {
+ pr_info("%pI6, port %u, dst no neighbour.\n",
+ daddr6->sin6_addr.s6_addr,
+ be16_to_cpu(daddr6->sin6_port));
+ err = -ENETUNREACH;
+ goto rel_rt;
+ }
+ ndev = n->dev;
+
+ if (!(ndev->flags & IFF_UP) || !netif_carrier_ok(ndev)) {
+ pr_info("%s interface not up.\n", ndev->name);
+ err = -ENETDOWN;
+ goto rel_rt;
+ }
+
+ if (ipv6_addr_is_multicast(&daddr6->sin6_addr)) {
+ pr_info("multi-cast route %pI6 port %u, dev %s.\n",
+ daddr6->sin6_addr.s6_addr,
+ ntohs(daddr6->sin6_port), ndev->name);
+ err = -ENETUNREACH;
+ goto rel_rt;
+ }
+
+ cdev = cxgbi_device_find_by_netdev(ndev, &port);
+ if (!cdev)
+ cdev = cxgbi_device_find_by_mac(ndev, &port);
+ if (!cdev) {
+ pr_info("dst %pI6 %s, NOT cxgbi device.\n",
+ daddr6->sin6_addr.s6_addr, ndev->name);
+ err = -ENETUNREACH;
+ goto rel_rt;
+ }
+ log_debug(1 << CXGBI_DBG_SOCK,
+ "route to %pI6 :%u, ndev p#%d,%s, cdev 0x%p.\n",
+ daddr6->sin6_addr.s6_addr, ntohs(daddr6->sin6_port), port,
+ ndev->name, cdev);
+
+ csk = cxgbi_sock_create(cdev);
+ if (!csk) {
+ err = -ENOMEM;
+ goto rel_rt;
+ }
+ csk->cdev = cdev;
+ csk->port_id = port;
+ csk->mtu = mtu;
+ csk->dst = dst;
+
+ rt6_get_prefsrc(rt, &pref_saddr);
+ if (ipv6_addr_any(&pref_saddr)) {
+ struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
+
+ err = ipv6_dev_get_saddr(&init_net, idev ? idev->dev : NULL,
+ &daddr6->sin6_addr, 0, &pref_saddr);
+ if (err) {
+ pr_info("failed to get source address to reach %pI6\n",
+ &daddr6->sin6_addr);
+ goto rel_rt;
+ }
+ }
+
+ csk->csk_family = AF_INET6;
+ csk->daddr6.sin6_addr = daddr6->sin6_addr;
+ csk->daddr6.sin6_port = daddr6->sin6_port;
+ csk->daddr6.sin6_family = daddr6->sin6_family;
+ csk->saddr6.sin6_family = daddr6->sin6_family;
+ csk->saddr6.sin6_addr = pref_saddr;
+
+ neigh_release(n);
+ return csk;
+
+rel_rt:
+ if (n)
+ neigh_release(n);
+
+ ip6_rt_put(rt);
if (csk)
cxgbi_sock_closed(csk);
err_out:
return ERR_PTR(err);
}
+#endif /* IS_ENABLED(CONFIG_IPV6) */
void cxgbi_sock_established(struct cxgbi_sock *csk, unsigned int snd_isn,
unsigned int opt)
@@ -565,7 +847,7 @@ static void cxgbi_inform_iscsi_conn_closing(struct cxgbi_sock *csk)
read_lock_bh(&csk->callback_lock);
if (csk->user_data)
iscsi_conn_failure(csk->user_data,
- ISCSI_ERR_CONN_FAILED);
+ ISCSI_ERR_TCP_CONN_CLOSE);
read_unlock_bh(&csk->callback_lock);
}
}
@@ -596,7 +878,8 @@ static void need_active_close(struct cxgbi_sock *csk)
log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n",
csk, (csk)->state, (csk)->flags, (csk)->tid);
spin_lock_bh(&csk->lock);
- dst_confirm(csk->dst);
+ if (csk->dst)
+ dst_confirm(csk->dst);
data_lost = skb_queue_len(&csk->receive_queue);
__skb_queue_purge(&csk->receive_queue);
@@ -611,7 +894,8 @@ static void need_active_close(struct cxgbi_sock *csk)
}
if (close_req) {
- if (data_lost)
+ if (!cxgbi_sock_flag(csk, CTPF_LOGOUT_RSP_RCVD) ||
+ data_lost)
csk->cdev->csk_send_abort_req(csk);
else
csk->cdev->csk_send_close_req(csk);
@@ -637,6 +921,7 @@ EXPORT_SYMBOL_GPL(cxgbi_sock_fail_act_open);
void cxgbi_sock_act_open_req_arp_failure(void *handle, struct sk_buff *skb)
{
struct cxgbi_sock *csk = (struct cxgbi_sock *)skb->sk;
+ struct module *owner = csk->cdev->owner;
log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n",
csk, (csk)->state, (csk)->flags, (csk)->tid);
@@ -647,6 +932,8 @@ void cxgbi_sock_act_open_req_arp_failure(void *handle, struct sk_buff *skb)
spin_unlock_bh(&csk->lock);
cxgbi_sock_put(csk);
__kfree_skb(skb);
+
+ module_put(owner);
}
EXPORT_SYMBOL_GPL(cxgbi_sock_act_open_req_arp_failure);
@@ -654,18 +941,16 @@ void cxgbi_sock_rcv_abort_rpl(struct cxgbi_sock *csk)
{
cxgbi_sock_get(csk);
spin_lock_bh(&csk->lock);
+
+ cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_RCVD);
if (cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
- if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_RCVD))
- cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_RCVD);
- else {
- cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_RCVD);
- cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_PENDING);
- if (cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD))
- pr_err("csk 0x%p,%u,0x%lx,%u,ABT_RPL_RSS.\n",
- csk, csk->state, csk->flags, csk->tid);
- cxgbi_sock_closed(csk);
- }
+ cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_PENDING);
+ if (cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD))
+ pr_err("csk 0x%p,%u,0x%lx,%u,ABT_RPL_RSS.\n",
+ csk, csk->state, csk->flags, csk->tid);
+ cxgbi_sock_closed(csk);
}
+
spin_unlock_bh(&csk->lock);
cxgbi_sock_put(csk);
}
@@ -853,600 +1138,245 @@ void cxgbi_sock_check_wr_invariants(const struct cxgbi_sock *csk)
}
EXPORT_SYMBOL_GPL(cxgbi_sock_check_wr_invariants);
-static int cxgbi_sock_send_pdus(struct cxgbi_sock *csk, struct sk_buff *skb)
+static inline void
+scmd_get_params(struct scsi_cmnd *sc, struct scatterlist **sgl,
+ unsigned int *sgcnt, unsigned int *dlen,
+ unsigned int prot)
{
- struct cxgbi_device *cdev = csk->cdev;
- struct sk_buff *next;
- int err, copied = 0;
-
- spin_lock_bh(&csk->lock);
+ struct scsi_data_buffer *sdb = prot ? scsi_prot(sc) : &sc->sdb;
- if (csk->state != CTP_ESTABLISHED) {
- log_debug(1 << CXGBI_DBG_PDU_TX,
- "csk 0x%p,%u,0x%lx,%u, EAGAIN.\n",
- csk, csk->state, csk->flags, csk->tid);
- err = -EAGAIN;
- goto out_err;
- }
-
- if (csk->err) {
- log_debug(1 << CXGBI_DBG_PDU_TX,
- "csk 0x%p,%u,0x%lx,%u, EPIPE %d.\n",
- csk, csk->state, csk->flags, csk->tid, csk->err);
- err = -EPIPE;
- goto out_err;
- }
-
- if (csk->write_seq - csk->snd_una >= cdev->snd_win) {
- log_debug(1 << CXGBI_DBG_PDU_TX,
- "csk 0x%p,%u,0x%lx,%u, FULL %u-%u >= %u.\n",
- csk, csk->state, csk->flags, csk->tid, csk->write_seq,
- csk->snd_una, cdev->snd_win);
- err = -ENOBUFS;
- goto out_err;
- }
-
- while (skb) {
- int frags = skb_shinfo(skb)->nr_frags +
- (skb->len != skb->data_len);
-
- if (unlikely(skb_headroom(skb) < cdev->skb_tx_rsvd)) {
- pr_err("csk 0x%p, skb head %u < %u.\n",
- csk, skb_headroom(skb), cdev->skb_tx_rsvd);
- err = -EINVAL;
- goto out_err;
- }
-
- if (frags >= SKB_WR_LIST_SIZE) {
- pr_err("csk 0x%p, frags %d, %u,%u >%u.\n",
- csk, skb_shinfo(skb)->nr_frags, skb->len,
- skb->data_len, (uint)(SKB_WR_LIST_SIZE));
- err = -EINVAL;
- goto out_err;
- }
-
- next = skb->next;
- skb->next = NULL;
- cxgbi_skcb_set_flag(skb, SKCBF_TX_NEED_HDR);
- cxgbi_sock_skb_entail(csk, skb);
- copied += skb->len;
- csk->write_seq += skb->len +
- cxgbi_ulp_extra_len(cxgbi_skcb_ulp_mode(skb));
- skb = next;
- }
-done:
- if (likely(skb_queue_len(&csk->write_queue)))
- cdev->csk_push_tx_frames(csk, 1);
- spin_unlock_bh(&csk->lock);
- return copied;
-
-out_err:
- if (copied == 0 && err == -EPIPE)
- copied = csk->err ? csk->err : -EPIPE;
- else
- copied = err;
- goto done;
+ *sgl = sdb->table.sgl;
+ *sgcnt = sdb->table.nents;
+ *dlen = sdb->length;
+ /* Caution: for protection sdb, sdb->length is invalid */
}
-/*
- * Direct Data Placement -
- * Directly place the iSCSI Data-In or Data-Out PDU's payload into pre-posted
- * final destination host-memory buffers based on the Initiator Task Tag (ITT)
- * in Data-In or Target Task Tag (TTT) in Data-Out PDUs.
- * The host memory address is programmed into h/w in the format of pagepod
- * entries.
- * The location of the pagepod entry is encoded into ddp tag which is used as
- * the base for ITT/TTT.
- */
-
-static unsigned char ddp_page_order[DDP_PGIDX_MAX] = {0, 1, 2, 4};
-static unsigned char ddp_page_shift[DDP_PGIDX_MAX] = {12, 13, 14, 16};
-static unsigned char page_idx = DDP_PGIDX_MAX;
-
-static unsigned char sw_tag_idx_bits;
-static unsigned char sw_tag_age_bits;
-
-/*
- * Direct-Data Placement page size adjustment
- */
-static int ddp_adjust_page_table(void)
+void cxgbi_ddp_set_one_ppod(struct cxgbi_pagepod *ppod,
+ struct cxgbi_task_tag_info *ttinfo,
+ struct scatterlist **sg_pp, unsigned int *sg_off)
{
+ struct scatterlist *sg = sg_pp ? *sg_pp : NULL;
+ unsigned int offset = sg_off ? *sg_off : 0;
+ dma_addr_t addr = 0UL;
+ unsigned int len = 0;
int i;
- unsigned int base_order, order;
-
- if (PAGE_SIZE < (1UL << ddp_page_shift[0])) {
- pr_info("PAGE_SIZE 0x%lx too small, min 0x%lx\n",
- PAGE_SIZE, 1UL << ddp_page_shift[0]);
- return -EINVAL;
- }
- base_order = get_order(1UL << ddp_page_shift[0]);
- order = get_order(1UL << PAGE_SHIFT);
+ memcpy(ppod, &ttinfo->hdr, sizeof(struct cxgbi_pagepod_hdr));
- for (i = 0; i < DDP_PGIDX_MAX; i++) {
- /* first is the kernel page size, then just doubling */
- ddp_page_order[i] = order - base_order + i;
- ddp_page_shift[i] = PAGE_SHIFT + i;
+ if (sg) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
}
- return 0;
-}
-static int ddp_find_page_index(unsigned long pgsz)
-{
- int i;
-
- for (i = 0; i < DDP_PGIDX_MAX; i++) {
- if (pgsz == (1UL << ddp_page_shift[i]))
- return i;
- }
- pr_info("ddp page size %lu not supported.\n", pgsz);
- return DDP_PGIDX_MAX;
-}
-
-static void ddp_setup_host_page_size(void)
-{
- if (page_idx == DDP_PGIDX_MAX) {
- page_idx = ddp_find_page_index(PAGE_SIZE);
-
- if (page_idx == DDP_PGIDX_MAX) {
- pr_info("system PAGE %lu, update hw.\n", PAGE_SIZE);
- if (ddp_adjust_page_table() < 0) {
- pr_info("PAGE %lu, disable ddp.\n", PAGE_SIZE);
- return;
+ for (i = 0; i < PPOD_PAGES_MAX; i++) {
+ if (sg) {
+ ppod->addr[i] = cpu_to_be64(addr + offset);
+ offset += PAGE_SIZE;
+ if (offset == (len + sg->offset)) {
+ offset = 0;
+ sg = sg_next(sg);
+ if (sg) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ }
}
- page_idx = ddp_find_page_index(PAGE_SIZE);
+ } else {
+ ppod->addr[i] = 0ULL;
}
- pr_info("system PAGE %lu, ddp idx %u.\n", PAGE_SIZE, page_idx);
}
-}
-
-void cxgbi_ddp_page_size_factor(int *pgsz_factor)
-{
- int i;
-
- for (i = 0; i < DDP_PGIDX_MAX; i++)
- pgsz_factor[i] = ddp_page_order[i];
-}
-EXPORT_SYMBOL_GPL(cxgbi_ddp_page_size_factor);
-
-/*
- * DDP setup & teardown
- */
-
-void cxgbi_ddp_ppod_set(struct cxgbi_pagepod *ppod,
- struct cxgbi_pagepod_hdr *hdr,
- struct cxgbi_gather_list *gl, unsigned int gidx)
-{
- int i;
-
- memcpy(ppod, hdr, sizeof(*hdr));
- for (i = 0; i < (PPOD_PAGES_MAX + 1); i++, gidx++) {
- ppod->addr[i] = gidx < gl->nelem ?
- cpu_to_be64(gl->phys_addr[gidx]) : 0ULL;
- }
-}
-EXPORT_SYMBOL_GPL(cxgbi_ddp_ppod_set);
-
-void cxgbi_ddp_ppod_clear(struct cxgbi_pagepod *ppod)
-{
- memset(ppod, 0, sizeof(*ppod));
-}
-EXPORT_SYMBOL_GPL(cxgbi_ddp_ppod_clear);
-
-static inline int ddp_find_unused_entries(struct cxgbi_ddp_info *ddp,
- unsigned int start, unsigned int max,
- unsigned int count,
- struct cxgbi_gather_list *gl)
-{
- unsigned int i, j, k;
- /* not enough entries */
- if ((max - start) < count) {
- log_debug(1 << CXGBI_DBG_DDP,
- "NOT enough entries %u+%u < %u.\n", start, count, max);
- return -EBUSY;
+ /*
+ * the fifth address needs to be repeated in the next ppod, so do
+ * not move sg
+ */
+ if (sg_pp) {
+ *sg_pp = sg;
+ *sg_off = offset;
}
- max -= count;
- spin_lock(&ddp->map_lock);
- for (i = start; i < max;) {
- for (j = 0, k = i; j < count; j++, k++) {
- if (ddp->gl_map[k])
- break;
- }
- if (j == count) {
- for (j = 0, k = i; j < count; j++, k++)
- ddp->gl_map[k] = gl;
- spin_unlock(&ddp->map_lock);
- return i;
+ if (offset == len) {
+ offset = 0;
+ sg = sg_next(sg);
+ if (sg) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
}
- i += j + 1;
}
- spin_unlock(&ddp->map_lock);
- log_debug(1 << CXGBI_DBG_DDP,
- "NO suitable entries %u available.\n", count);
- return -EBUSY;
+ ppod->addr[i] = sg ? cpu_to_be64(addr + offset) : 0ULL;
}
+EXPORT_SYMBOL_GPL(cxgbi_ddp_set_one_ppod);
-static inline void ddp_unmark_entries(struct cxgbi_ddp_info *ddp,
- int start, int count)
-{
- spin_lock(&ddp->map_lock);
- memset(&ddp->gl_map[start], 0,
- count * sizeof(struct cxgbi_gather_list *));
- spin_unlock(&ddp->map_lock);
-}
+/*
+ * APIs interacting with open-iscsi libraries
+ */
-static inline void ddp_gl_unmap(struct pci_dev *pdev,
- struct cxgbi_gather_list *gl)
+int cxgbi_ddp_ppm_setup(void **ppm_pp, struct cxgbi_device *cdev,
+ struct cxgbi_tag_format *tformat,
+ unsigned int iscsi_size, unsigned int llimit,
+ unsigned int start, unsigned int rsvd_factor,
+ unsigned int edram_start, unsigned int edram_size)
{
- int i;
+ int err = cxgbi_ppm_init(ppm_pp, cdev->ports[0], cdev->pdev,
+ cdev->lldev, tformat, iscsi_size, llimit, start,
+ rsvd_factor, edram_start, edram_size);
- for (i = 0; i < gl->nelem; i++)
- dma_unmap_page(&pdev->dev, gl->phys_addr[i], PAGE_SIZE,
- PCI_DMA_FROMDEVICE);
-}
+ if (err >= 0) {
+ struct cxgbi_ppm *ppm = (struct cxgbi_ppm *)(*ppm_pp);
-static inline int ddp_gl_map(struct pci_dev *pdev,
- struct cxgbi_gather_list *gl)
-{
- int i;
-
- for (i = 0; i < gl->nelem; i++) {
- gl->phys_addr[i] = dma_map_page(&pdev->dev, gl->pages[i], 0,
- PAGE_SIZE,
- PCI_DMA_FROMDEVICE);
- if (unlikely(dma_mapping_error(&pdev->dev, gl->phys_addr[i]))) {
- log_debug(1 << CXGBI_DBG_DDP,
- "page %d 0x%p, 0x%p dma mapping err.\n",
- i, gl->pages[i], pdev);
- goto unmap;
- }
+ if (ppm->ppmax < 1024 ||
+ ppm->tformat.pgsz_idx_dflt >= DDP_PGIDX_MAX)
+ cdev->flags |= CXGBI_FLAG_DDP_OFF;
+ err = 0;
+ } else {
+ cdev->flags |= CXGBI_FLAG_DDP_OFF;
}
- return i;
-unmap:
- if (i) {
- unsigned int nelem = gl->nelem;
- gl->nelem = i;
- ddp_gl_unmap(pdev, gl);
- gl->nelem = nelem;
- }
- return -EINVAL;
-}
-
-static void ddp_release_gl(struct cxgbi_gather_list *gl,
- struct pci_dev *pdev)
-{
- ddp_gl_unmap(pdev, gl);
- kfree(gl);
+ return err;
}
+EXPORT_SYMBOL_GPL(cxgbi_ddp_ppm_setup);
-static struct cxgbi_gather_list *ddp_make_gl(unsigned int xferlen,
- struct scatterlist *sgl,
- unsigned int sgcnt,
- struct pci_dev *pdev,
- gfp_t gfp)
+static int cxgbi_ddp_sgl_check(struct scatterlist *sgl, int nents)
{
- struct cxgbi_gather_list *gl;
+ int i;
+ int last_sgidx = nents - 1;
struct scatterlist *sg = sgl;
- struct page *sgpage = sg_page(sg);
- unsigned int sglen = sg->length;
- unsigned int sgoffset = sg->offset;
- unsigned int npages = (xferlen + sgoffset + PAGE_SIZE - 1) >>
- PAGE_SHIFT;
- int i = 1, j = 0;
-
- if (xferlen < DDP_THRESHOLD) {
- log_debug(1 << CXGBI_DBG_DDP,
- "xfer %u < threshold %u, no ddp.\n",
- xferlen, DDP_THRESHOLD);
- return NULL;
- }
-
- gl = kzalloc(sizeof(struct cxgbi_gather_list) +
- npages * (sizeof(dma_addr_t) +
- sizeof(struct page *)), gfp);
- if (!gl) {
- log_debug(1 << CXGBI_DBG_DDP,
- "xfer %u, %u pages, OOM.\n", xferlen, npages);
- return NULL;
- }
-
- log_debug(1 << CXGBI_DBG_DDP,
- "xfer %u, sgl %u, gl max %u.\n", xferlen, sgcnt, npages);
-
- gl->pages = (struct page **)&gl->phys_addr[npages];
- gl->nelem = npages;
- gl->length = xferlen;
- gl->offset = sgoffset;
- gl->pages[0] = sgpage;
-
- for (i = 1, sg = sg_next(sgl), j = 0; i < sgcnt;
- i++, sg = sg_next(sg)) {
- struct page *page = sg_page(sg);
-
- if (sgpage == page && sg->offset == sgoffset + sglen)
- sglen += sg->length;
- else {
- /* make sure the sgl is fit for ddp:
- * each has the same page size, and
- * all of the middle pages are used completely
- */
- if ((j && sgoffset) || ((i != sgcnt - 1) &&
- ((sglen + sgoffset) & ~PAGE_MASK))) {
- log_debug(1 << CXGBI_DBG_DDP,
- "page %d/%u, %u + %u.\n",
- i, sgcnt, sgoffset, sglen);
- goto error_out;
- }
-
- j++;
- if (j == gl->nelem || sg->offset) {
- log_debug(1 << CXGBI_DBG_DDP,
- "page %d/%u, offset %u.\n",
- j, gl->nelem, sg->offset);
- goto error_out;
- }
- gl->pages[j] = page;
- sglen = sg->length;
- sgoffset = sg->offset;
- sgpage = page;
- }
- }
- gl->nelem = ++j;
- if (ddp_gl_map(pdev, gl) < 0)
- goto error_out;
+ for (i = 0; i < nents; i++, sg = sg_next(sg)) {
+ unsigned int len = sg->length + sg->offset;
- return gl;
-
-error_out:
- kfree(gl);
- return NULL;
-}
-
-static void ddp_tag_release(struct cxgbi_hba *chba, u32 tag)
-{
- struct cxgbi_device *cdev = chba->cdev;
- struct cxgbi_ddp_info *ddp = cdev->ddp;
- u32 idx;
-
- idx = (tag >> PPOD_IDX_SHIFT) & ddp->idx_mask;
- if (idx < ddp->nppods) {
- struct cxgbi_gather_list *gl = ddp->gl_map[idx];
- unsigned int npods;
-
- if (!gl || !gl->nelem) {
- pr_warn("tag 0x%x, idx %u, gl 0x%p, %u.\n",
- tag, idx, gl, gl ? gl->nelem : 0);
- return;
- }
- npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
- log_debug(1 << CXGBI_DBG_DDP,
- "tag 0x%x, release idx %u, npods %u.\n",
- tag, idx, npods);
- cdev->csk_ddp_clear(chba, tag, idx, npods);
- ddp_unmark_entries(ddp, idx, npods);
- ddp_release_gl(gl, ddp->pdev);
- } else
- pr_warn("tag 0x%x, idx %u > max %u.\n", tag, idx, ddp->nppods);
-}
-
-static int ddp_tag_reserve(struct cxgbi_sock *csk, unsigned int tid,
- u32 sw_tag, u32 *tagp, struct cxgbi_gather_list *gl,
- gfp_t gfp)
-{
- struct cxgbi_device *cdev = csk->cdev;
- struct cxgbi_ddp_info *ddp = cdev->ddp;
- struct cxgbi_tag_format *tformat = &cdev->tag_format;
- struct cxgbi_pagepod_hdr hdr;
- unsigned int npods;
- int idx = -1;
- int err = -ENOMEM;
- u32 tag;
-
- npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
- if (ddp->idx_last == ddp->nppods)
- idx = ddp_find_unused_entries(ddp, 0, ddp->nppods,
- npods, gl);
- else {
- idx = ddp_find_unused_entries(ddp, ddp->idx_last + 1,
- ddp->nppods, npods,
- gl);
- if (idx < 0 && ddp->idx_last >= npods) {
- idx = ddp_find_unused_entries(ddp, 0,
- min(ddp->idx_last + npods, ddp->nppods),
- npods, gl);
+ if ((sg->offset & 0x3) || (i && sg->offset) ||
+ ((i != last_sgidx) && len != PAGE_SIZE)) {
+ log_debug(1 << CXGBI_DBG_DDP,
+ "sg %u/%u, %u,%u, not aligned.\n",
+ i, nents, sg->offset, sg->length);
+ goto err_out;
}
}
- if (idx < 0) {
- log_debug(1 << CXGBI_DBG_DDP,
- "xferlen %u, gl %u, npods %u NO DDP.\n",
- gl->length, gl->nelem, npods);
- return idx;
- }
-
- tag = cxgbi_ddp_tag_base(tformat, sw_tag);
- tag |= idx << PPOD_IDX_SHIFT;
-
- hdr.rsvd = 0;
- hdr.vld_tid = htonl(PPOD_VALID_FLAG | PPOD_TID(tid));
- hdr.pgsz_tag_clr = htonl(tag & ddp->rsvd_tag_mask);
- hdr.max_offset = htonl(gl->length);
- hdr.page_offset = htonl(gl->offset);
-
- err = cdev->csk_ddp_set(csk, &hdr, idx, npods, gl);
- if (err < 0)
- goto unmark_entries;
-
- ddp->idx_last = idx;
- log_debug(1 << CXGBI_DBG_DDP,
- "xfer %u, gl %u,%u, tid 0x%x, tag 0x%x->0x%x(%u,%u).\n",
- gl->length, gl->nelem, gl->offset, tid, sw_tag, tag, idx,
- npods);
- *tagp = tag;
return 0;
-
-unmark_entries:
- ddp_unmark_entries(ddp, idx, npods);
- return err;
+err_out:
+ return -EINVAL;
}
-int cxgbi_ddp_reserve(struct cxgbi_sock *csk, unsigned int *tagp,
- unsigned int sw_tag, unsigned int xferlen,
- struct scatterlist *sgl, unsigned int sgcnt, gfp_t gfp)
+static int cxgbi_ddp_reserve(struct cxgbi_conn *cconn,
+ struct cxgbi_task_data *tdata, u32 sw_tag,
+ unsigned int xferlen)
{
+ struct cxgbi_sock *csk = cconn->cep->csk;
struct cxgbi_device *cdev = csk->cdev;
- struct cxgbi_tag_format *tformat = &cdev->tag_format;
- struct cxgbi_gather_list *gl;
+ struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
+ struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
+ struct scatterlist *sgl = ttinfo->sgl;
+ unsigned int sgcnt = ttinfo->nents;
+ unsigned int sg_offset = sgl->offset;
int err;
- if (page_idx >= DDP_PGIDX_MAX || !cdev->ddp ||
- xferlen < DDP_THRESHOLD) {
+ if (cdev->flags & CXGBI_FLAG_DDP_OFF) {
log_debug(1 << CXGBI_DBG_DDP,
- "pgidx %u, xfer %u, NO ddp.\n", page_idx, xferlen);
+ "cdev 0x%p DDP off.\n", cdev);
return -EINVAL;
}
- if (!cxgbi_sw_tag_usable(tformat, sw_tag)) {
+ if (!ppm || xferlen < DDP_THRESHOLD || !sgcnt ||
+ ppm->tformat.pgsz_idx_dflt >= DDP_PGIDX_MAX) {
log_debug(1 << CXGBI_DBG_DDP,
- "sw_tag 0x%x NOT usable.\n", sw_tag);
+ "ppm 0x%p, pgidx %u, xfer %u, sgcnt %u, NO ddp.\n",
+ ppm, ppm ? ppm->tformat.pgsz_idx_dflt : DDP_PGIDX_MAX,
+ xferlen, ttinfo->nents);
return -EINVAL;
}
- gl = ddp_make_gl(xferlen, sgl, sgcnt, cdev->pdev, gfp);
- if (!gl)
- return -ENOMEM;
+ /* make sure the buffer is suitable for ddp */
+ if (cxgbi_ddp_sgl_check(sgl, sgcnt) < 0)
+ return -EINVAL;
- err = ddp_tag_reserve(csk, csk->tid, sw_tag, tagp, gl, gfp);
- if (err < 0)
- ddp_release_gl(gl, cdev->pdev);
+ ttinfo->nr_pages = (xferlen + sgl->offset + (1 << PAGE_SHIFT) - 1) >>
+ PAGE_SHIFT;
- return err;
-}
+ /*
+ * the ddp tag will be used for the itt in the outgoing pdu,
+ * the itt generated by libiscsi is saved in the ppm and can be
+ * retrieved via the ddp tag
+ */
+ err = cxgbi_ppm_ppods_reserve(ppm, ttinfo->nr_pages, 0, &ttinfo->idx,
+ &ttinfo->tag, (unsigned long)sw_tag);
+ if (err < 0) {
+ cconn->ddp_full++;
+ return err;
+ }
+ ttinfo->npods = err;
-static void ddp_destroy(struct kref *kref)
-{
- struct cxgbi_ddp_info *ddp = container_of(kref,
- struct cxgbi_ddp_info,
- refcnt);
- struct cxgbi_device *cdev = ddp->cdev;
- int i = 0;
+ /* setup dma from scsi command sgl */
+ sgl->offset = 0;
+ err = dma_map_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE);
+ sgl->offset = sg_offset;
+ if (err == 0) {
+ pr_info("%s: 0x%x, xfer %u, sgl %u dma mapping err.\n",
+ __func__, sw_tag, xferlen, sgcnt);
+ goto rel_ppods;
+ }
+ if (err != ttinfo->nr_pages) {
+ log_debug(1 << CXGBI_DBG_DDP,
+ "%s: sw tag 0x%x, xfer %u, sgl %u, dma count %d.\n",
+ __func__, sw_tag, xferlen, sgcnt, err);
+ }
- pr_info("kref 0, destroy ddp 0x%p, cdev 0x%p.\n", ddp, cdev);
+ ttinfo->flags |= CXGBI_PPOD_INFO_FLAG_MAPPED;
+ ttinfo->cid = csk->port_id;
- while (i < ddp->nppods) {
- struct cxgbi_gather_list *gl = ddp->gl_map[i];
+ cxgbi_ppm_make_ppod_hdr(ppm, ttinfo->tag, csk->tid, sgl->offset,
+ xferlen, &ttinfo->hdr);
- if (gl) {
- int npods = (gl->nelem + PPOD_PAGES_MAX - 1)
- >> PPOD_PAGES_SHIFT;
- pr_info("cdev 0x%p, ddp %d + %d.\n", cdev, i, npods);
- kfree(gl);
- i += npods;
- } else
- i++;
+ if (cdev->flags & CXGBI_FLAG_USE_PPOD_OFLDQ) {
+ /* write ppod from xmit_pdu (of iscsi_scsi_command pdu) */
+ ttinfo->flags |= CXGBI_PPOD_INFO_FLAG_VALID;
+ } else {
+ /* write ppod from control queue now */
+ err = cdev->csk_ddp_set_map(ppm, csk, ttinfo);
+ if (err < 0)
+ goto rel_ppods;
}
- cxgbi_free_big_mem(ddp);
-}
-
-int cxgbi_ddp_cleanup(struct cxgbi_device *cdev)
-{
- struct cxgbi_ddp_info *ddp = cdev->ddp;
- log_debug(1 << CXGBI_DBG_DDP,
- "cdev 0x%p, release ddp 0x%p.\n", cdev, ddp);
- cdev->ddp = NULL;
- if (ddp)
- return kref_put(&ddp->refcnt, ddp_destroy);
return 0;
-}
-EXPORT_SYMBOL_GPL(cxgbi_ddp_cleanup);
-
-int cxgbi_ddp_init(struct cxgbi_device *cdev,
- unsigned int llimit, unsigned int ulimit,
- unsigned int max_txsz, unsigned int max_rxsz)
-{
- struct cxgbi_ddp_info *ddp;
- unsigned int ppmax, bits;
- ppmax = (ulimit - llimit + 1) >> PPOD_SIZE_SHIFT;
- bits = __ilog2_u32(ppmax) + 1;
- if (bits > PPOD_IDX_MAX_SIZE)
- bits = PPOD_IDX_MAX_SIZE;
- ppmax = (1 << (bits - 1)) - 1;
+rel_ppods:
+ cxgbi_ppm_ppod_release(ppm, ttinfo->idx);
- ddp = cxgbi_alloc_big_mem(sizeof(struct cxgbi_ddp_info) +
- ppmax * (sizeof(struct cxgbi_gather_list *) +
- sizeof(struct sk_buff *)),
- GFP_KERNEL);
- if (!ddp) {
- pr_warn("cdev 0x%p, ddp ppmax %u OOM.\n", cdev, ppmax);
- return -ENOMEM;
+ if (ttinfo->flags & CXGBI_PPOD_INFO_FLAG_MAPPED) {
+ ttinfo->flags &= ~CXGBI_PPOD_INFO_FLAG_MAPPED;
+ dma_unmap_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE);
}
- ddp->gl_map = (struct cxgbi_gather_list **)(ddp + 1);
- cdev->ddp = ddp;
-
- spin_lock_init(&ddp->map_lock);
- kref_init(&ddp->refcnt);
-
- ddp->cdev = cdev;
- ddp->pdev = cdev->pdev;
- ddp->llimit = llimit;
- ddp->ulimit = ulimit;
- ddp->max_txsz = min_t(unsigned int, max_txsz, ULP2_MAX_PKT_SIZE);
- ddp->max_rxsz = min_t(unsigned int, max_rxsz, ULP2_MAX_PKT_SIZE);
- ddp->nppods = ppmax;
- ddp->idx_last = ppmax;
- ddp->idx_bits = bits;
- ddp->idx_mask = (1 << bits) - 1;
- ddp->rsvd_tag_mask = (1 << (bits + PPOD_IDX_SHIFT)) - 1;
-
- cdev->tag_format.sw_bits = sw_tag_idx_bits + sw_tag_age_bits;
- cdev->tag_format.rsvd_bits = ddp->idx_bits;
- cdev->tag_format.rsvd_shift = PPOD_IDX_SHIFT;
- cdev->tag_format.rsvd_mask = (1 << cdev->tag_format.rsvd_bits) - 1;
-
- pr_info("%s tag format, sw %u, rsvd %u,%u, mask 0x%x.\n",
- cdev->ports[0]->name, cdev->tag_format.sw_bits,
- cdev->tag_format.rsvd_bits, cdev->tag_format.rsvd_shift,
- cdev->tag_format.rsvd_mask);
-
- cdev->tx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
- ddp->max_txsz - ISCSI_PDU_NONPAYLOAD_LEN);
- cdev->rx_max_size = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
- ddp->max_rxsz - ISCSI_PDU_NONPAYLOAD_LEN);
-
- log_debug(1 << CXGBI_DBG_DDP,
- "%s max payload size: %u/%u, %u/%u.\n",
- cdev->ports[0]->name, cdev->tx_max_size, ddp->max_txsz,
- cdev->rx_max_size, ddp->max_rxsz);
- return 0;
+ return -EINVAL;
}
-EXPORT_SYMBOL_GPL(cxgbi_ddp_init);
-
-/*
- * APIs interacting with open-iscsi libraries
- */
-
-static unsigned char padding[4];
static void task_release_itt(struct iscsi_task *task, itt_t hdr_itt)
{
struct scsi_cmnd *sc = task->sc;
struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
- struct cxgbi_hba *chba = cconn->chba;
- struct cxgbi_tag_format *tformat = &chba->cdev->tag_format;
+ struct cxgbi_device *cdev = cconn->chba->cdev;
+ struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
u32 tag = ntohl((__force u32)hdr_itt);
log_debug(1 << CXGBI_DBG_DDP,
- "cdev 0x%p, release tag 0x%x.\n", chba->cdev, tag);
- if (sc &&
- (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) &&
- cxgbi_is_ddp_tag(tformat, tag))
- ddp_tag_release(chba, tag);
+ "cdev 0x%p, task 0x%p, release tag 0x%x.\n",
+ cdev, task, tag);
+ if (sc && sc->sc_data_direction == DMA_FROM_DEVICE &&
+ cxgbi_ppm_is_ddp_tag(ppm, tag)) {
+ struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
+ struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
+
+ if (!(cdev->flags & CXGBI_FLAG_USE_PPOD_OFLDQ))
+ cdev->csk_ddp_clear_map(cdev, ppm, ttinfo);
+ cxgbi_ppm_ppod_release(ppm, ttinfo->idx);
+ dma_unmap_sg(&ppm->pdev->dev, ttinfo->sgl, ttinfo->nents,
+ DMA_FROM_DEVICE);
+ }
+}
+
+static inline u32 cxgbi_build_sw_tag(u32 idx, u32 age)
+{
+ /* assume idx and age both are < 0x7FFF (32767) */
+ return (idx << 16) | age;
}
static int task_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
@@ -1456,34 +1386,39 @@ static int task_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
struct iscsi_session *sess = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
- struct cxgbi_hba *chba = cconn->chba;
- struct cxgbi_tag_format *tformat = &chba->cdev->tag_format;
- u32 sw_tag = (sess->age << cconn->task_idx_bits) | task->itt;
+ struct cxgbi_device *cdev = cconn->chba->cdev;
+ struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
+ u32 sw_tag = cxgbi_build_sw_tag(task->itt, sess->age);
u32 tag = 0;
int err = -EINVAL;
- if (sc &&
- (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE)) {
- err = cxgbi_ddp_reserve(cconn->cep->csk, &tag, sw_tag,
- scsi_in(sc)->length,
- scsi_in(sc)->table.sgl,
- scsi_in(sc)->table.nents,
- GFP_ATOMIC);
- if (err < 0)
- log_debug(1 << CXGBI_DBG_DDP,
- "csk 0x%p, R task 0x%p, %u,%u, no ddp.\n",
- cconn->cep->csk, task, scsi_in(sc)->length,
- scsi_in(sc)->table.nents);
+ if (sc && sc->sc_data_direction == DMA_FROM_DEVICE) {
+ struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
+ struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
+
+ scmd_get_params(sc, &ttinfo->sgl, &ttinfo->nents,
+ &tdata->dlen, 0);
+ err = cxgbi_ddp_reserve(cconn, tdata, sw_tag, tdata->dlen);
+ if (!err)
+ tag = ttinfo->tag;
+ else
+ log_debug(1 << CXGBI_DBG_DDP,
+ "csk 0x%p, R task 0x%p, %u,%u, no ddp.\n",
+ cconn->cep->csk, task, tdata->dlen,
+ ttinfo->nents);
}
- if (err < 0)
- tag = cxgbi_set_non_ddp_tag(tformat, sw_tag);
+ if (err < 0) {
+ err = cxgbi_ppm_make_non_ddp_tag(ppm, sw_tag, &tag);
+ if (err < 0)
+ return err;
+ }
/* the itt need to sent in big-endian order */
*hdr_itt = (__force itt_t)htonl(tag);
log_debug(1 << CXGBI_DBG_DDP,
- "cdev 0x%p, task 0x%p, 0x%x(0x%x,0x%x)->0x%x/0x%x.\n",
- chba->cdev, task, sw_tag, task->itt, sess->age, tag, *hdr_itt);
+ "cdev 0x%p, task 0x%p, 0x%x(0x%x,0x%x)->0x%x/0x%x.\n",
+ cdev, task, sw_tag, task->itt, sess->age, tag, *hdr_itt);
return 0;
}
@@ -1492,19 +1427,24 @@ void cxgbi_parse_pdu_itt(struct iscsi_conn *conn, itt_t itt, int *idx, int *age)
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
struct cxgbi_device *cdev = cconn->chba->cdev;
- u32 tag = ntohl((__force u32) itt);
+ struct cxgbi_ppm *ppm = cdev->cdev2ppm(cdev);
+ u32 tag = ntohl((__force u32)itt);
u32 sw_bits;
- sw_bits = cxgbi_tag_nonrsvd_bits(&cdev->tag_format, tag);
- if (idx)
- *idx = sw_bits & ((1 << cconn->task_idx_bits) - 1);
- if (age)
- *age = (sw_bits >> cconn->task_idx_bits) & ISCSI_AGE_MASK;
+ if (ppm) {
+ if (cxgbi_ppm_is_ddp_tag(ppm, tag))
+ sw_bits = cxgbi_ppm_get_tag_caller_data(ppm, tag);
+ else
+ sw_bits = cxgbi_ppm_decode_non_ddp_tag(ppm, tag);
+ } else {
+ sw_bits = tag;
+ }
+ cxgbi_decode_sw_tag(sw_bits, idx, age);
log_debug(1 << CXGBI_DBG_DDP,
- "cdev 0x%p, tag 0x%x/0x%x, -> 0x%x(0x%x,0x%x).\n",
- cdev, tag, itt, sw_bits, idx ? *idx : 0xFFFFF,
- age ? *age : 0xFF);
+ "cdev 0x%p, tag 0x%x/0x%x, -> 0x%x(0x%x,0x%x).\n",
+ cdev, tag, itt, sw_bits, idx ? *idx : 0xFFFFF,
+ age ? *age : 0xFF);
}
EXPORT_SYMBOL_GPL(cxgbi_parse_pdu_itt);
@@ -1515,7 +1455,7 @@ void cxgbi_conn_tx_open(struct cxgbi_sock *csk)
if (conn) {
log_debug(1 << CXGBI_DBG_SOCK,
"csk 0x%p, cid %d.\n", csk, conn->id);
- iscsi_conn_queue_work(conn);
+ iscsi_conn_queue_xmit(conn);
}
}
EXPORT_SYMBOL_GPL(cxgbi_conn_tx_open);
@@ -1564,9 +1504,12 @@ static inline int read_pdu_skb(struct iscsi_conn *conn,
}
}
-static int skb_read_pdu_bhs(struct iscsi_conn *conn, struct sk_buff *skb)
+static int
+skb_read_pdu_bhs(struct cxgbi_sock *csk, struct iscsi_conn *conn,
+ struct sk_buff *skb)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ int err;
log_debug(1 << CXGBI_DBG_PDU_RX,
"conn 0x%p, skb 0x%p, len %u, flag 0x%lx.\n",
@@ -1585,7 +1528,35 @@ static int skb_read_pdu_bhs(struct iscsi_conn *conn, struct sk_buff *skb)
return -EIO;
}
- return read_pdu_skb(conn, skb, 0, 0);
+ if (cxgbi_skcb_test_flag(skb, SKCBF_RX_ISCSI_COMPL) &&
+ cxgbi_skcb_test_flag(skb, SKCBF_RX_DATA_DDPD)) {
+ /* If completion flag is set and data is directly
+ * placed in to the host memory then update
+ * task->exp_datasn to the datasn in completion
+ * iSCSI hdr as T6 adapter generates completion only
+ * for the last pdu of a sequence.
+ */
+ itt_t itt = ((struct iscsi_data *)skb->data)->itt;
+ struct iscsi_task *task = iscsi_itt_to_ctask(conn, itt);
+ u32 data_sn = be32_to_cpu(((struct iscsi_data *)
+ skb->data)->datasn);
+ if (task && task->sc) {
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ tcp_task->exp_datasn = data_sn;
+ }
+ }
+
+ err = read_pdu_skb(conn, skb, 0, 0);
+ if (likely(err >= 0)) {
+ struct iscsi_hdr *hdr = (struct iscsi_hdr *)skb->data;
+ u8 opcode = hdr->opcode & ISCSI_OPCODE_MASK;
+
+ if (unlikely(opcode == ISCSI_OP_LOGOUT_RSP))
+ cxgbi_sock_set_flag(csk, CTPF_LOGOUT_RSP_RCVD);
+ }
+
+ return err;
}
static int skb_read_pdu_data(struct iscsi_conn *conn, struct sk_buff *lskb,
@@ -1633,10 +1604,13 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
u32 credits;
log_debug(1 << CXGBI_DBG_PDU_RX,
- "csk 0x%p,%u,0x%lu,%u, seq %u, wup %u, thre %u, %u.\n",
+ "csk 0x%p,%u,0x%lx,%u, seq %u, wup %u, thre %u, %u.\n",
csk, csk->state, csk->flags, csk->tid, csk->copied_seq,
csk->rcv_wup, cdev->rx_credit_thres,
- cdev->rcv_win);
+ csk->rcv_win);
+
+ if (!cdev->rx_credit_thres)
+ return;
if (csk->state != CTP_ESTABLISHED)
return;
@@ -1644,10 +1618,7 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
credits = csk->copied_seq - csk->rcv_wup;
if (unlikely(!credits))
return;
- if (unlikely(cdev->rx_credit_thres == 0))
- return;
-
- must_send = credits + 16384 >= cdev->rcv_win;
+ must_send = credits + 16384 >= csk->rcv_win;
if (must_send || credits >= cdev->rx_credit_thres)
csk->rcv_wup += cdev->csk_send_rx_credits(csk, credits);
}
@@ -1663,11 +1634,11 @@ void cxgbi_conn_pdu_ready(struct cxgbi_sock *csk)
log_debug(1 << CXGBI_DBG_PDU_RX,
"csk 0x%p, conn 0x%p.\n", csk, conn);
- if (unlikely(!conn || conn->suspend_rx)) {
+ if (unlikely(!conn || test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags))) {
log_debug(1 << CXGBI_DBG_PDU_RX,
- "csk 0x%p, conn 0x%p, id %d, suspend_rx %lu!\n",
+ "csk 0x%p, conn 0x%p, id %d, conn flags 0x%lx!\n",
csk, conn, conn ? conn->id : 0xFF,
- conn ? conn->suspend_rx : 0xFF);
+ conn ? conn->flags : 0xFF);
return;
}
@@ -1690,7 +1661,7 @@ void cxgbi_conn_pdu_ready(struct cxgbi_sock *csk)
cxgbi_skcb_rx_pdulen(skb));
if (cxgbi_skcb_test_flag(skb, SKCBF_RX_COALESCED)) {
- err = skb_read_pdu_bhs(conn, skb);
+ err = skb_read_pdu_bhs(csk, conn, skb);
if (err < 0) {
pr_err("coalesced bhs, csk 0x%p, skb 0x%p,%u, "
"f 0x%lx, plen %u.\n",
@@ -1708,7 +1679,7 @@ void cxgbi_conn_pdu_ready(struct cxgbi_sock *csk)
cxgbi_skcb_flags(skb),
cxgbi_skcb_rx_pdulen(skb));
} else {
- err = skb_read_pdu_bhs(conn, skb);
+ err = skb_read_pdu_bhs(csk, conn, skb);
if (err < 0) {
pr_err("bhs, csk 0x%p, skb 0x%p,%u, "
"f 0x%lx, plen %u.\n",
@@ -1786,9 +1757,10 @@ static int sgl_seek_offset(struct scatterlist *sgl, unsigned int sgcnt,
return -EFAULT;
}
-static int sgl_read_to_frags(struct scatterlist *sg, unsigned int sgoffset,
- unsigned int dlen, skb_frag_t *frags,
- int frag_max)
+static int
+sgl_read_to_frags(struct scatterlist *sg, unsigned int sgoffset,
+ unsigned int dlen, struct page_frag *frags,
+ int frag_max, u32 *dlimit)
{
unsigned int datalen = dlen;
unsigned int sglen = sg->length - sgoffset;
@@ -1814,17 +1786,18 @@ static int sgl_read_to_frags(struct scatterlist *sg, unsigned int sgoffset,
copy = min(datalen, sglen);
if (i && page == frags[i - 1].page &&
sgoffset + sg->offset ==
- frags[i - 1].page_offset + frags[i - 1].size) {
+ frags[i - 1].offset + frags[i - 1].size) {
frags[i - 1].size += copy;
} else {
if (i >= frag_max) {
pr_warn("too many pages %u, dlen %u.\n",
frag_max, dlen);
+ *dlimit = dlen - datalen;
return -EINVAL;
}
frags[i].page = page;
- frags[i].page_offset = sg->offset + sgoffset;
+ frags[i].offset = sg->offset + sgoffset;
frags[i].size = copy;
i++;
}
@@ -1836,53 +1809,322 @@ static int sgl_read_to_frags(struct scatterlist *sg, unsigned int sgoffset,
return i;
}
-int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
+static void cxgbi_task_data_sgl_check(struct iscsi_task *task)
{
- struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
+ struct scsi_cmnd *sc = task->sc;
+ struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
+ struct scatterlist *sg, *sgl = NULL;
+ u32 sgcnt = 0;
+ int i;
+
+ tdata->flags = CXGBI_TASK_SGL_CHECKED;
+ if (!sc)
+ return;
+
+ scmd_get_params(sc, &sgl, &sgcnt, &tdata->dlen, 0);
+ if (!sgl || !sgcnt) {
+ tdata->flags |= CXGBI_TASK_SGL_COPY;
+ return;
+ }
+
+ for_each_sg(sgl, sg, sgcnt, i) {
+ if (page_count(sg_page(sg)) < 1) {
+ tdata->flags |= CXGBI_TASK_SGL_COPY;
+ return;
+ }
+ }
+}
+
+static int
+cxgbi_task_data_sgl_read(struct iscsi_task *task, u32 offset, u32 count,
+ u32 *dlimit)
+{
+ struct scsi_cmnd *sc = task->sc;
+ struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
+ struct scatterlist *sgl = NULL;
+ struct scatterlist *sg;
+ u32 dlen = 0;
+ u32 sgcnt;
+ int err;
+
+ if (!sc)
+ return 0;
+
+ scmd_get_params(sc, &sgl, &sgcnt, &dlen, 0);
+ if (!sgl || !sgcnt)
+ return 0;
+
+ err = sgl_seek_offset(sgl, sgcnt, offset, &tdata->sgoffset, &sg);
+ if (err < 0) {
+ pr_warn("tpdu max, sgl %u, bad offset %u/%u.\n",
+ sgcnt, offset, tdata->dlen);
+ return err;
+ }
+ err = sgl_read_to_frags(sg, tdata->sgoffset, count,
+ tdata->frags, MAX_SKB_FRAGS, dlimit);
+ if (err < 0) {
+ log_debug(1 << CXGBI_DBG_ISCSI,
+ "sgl max limit, sgl %u, offset %u, %u/%u, dlimit %u.\n",
+ sgcnt, offset, count, tdata->dlen, *dlimit);
+ return err;
+ }
+ tdata->offset = offset;
+ tdata->count = count;
+ tdata->nr_frags = err;
+ tdata->total_count = count;
+ tdata->total_offset = offset;
+
+ log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
+ "%s: offset %u, count %u,\n"
+ "err %u, total_count %u, total_offset %u\n",
+ __func__, offset, count, err, tdata->total_count, tdata->total_offset);
+
+ return 0;
+}
+
+int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 op)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *session = task->conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
struct cxgbi_device *cdev = cconn->chba->cdev;
- struct iscsi_conn *conn = task->conn;
+ struct cxgbi_sock *csk = cconn->cep ? cconn->cep->csk : NULL;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
struct scsi_cmnd *sc = task->sc;
- int headroom = SKB_TX_ISCSI_PDU_HEADER_MAX;
+ u32 headroom = SKB_TX_ISCSI_PDU_HEADER_MAX;
+ u32 max_txdata_len = conn->max_xmit_dlength;
+ u32 iso_tx_rsvd = 0, local_iso_info = 0;
+ u32 last_tdata_offset, last_tdata_count;
+ int err = 0;
+
+ if (!tcp_task) {
+ pr_err("task 0x%p, tcp_task 0x%p, tdata 0x%p.\n",
+ task, tcp_task, tdata);
+ return -ENOMEM;
+ }
+ if (!csk) {
+ pr_err("task 0x%p, csk gone.\n", task);
+ return -EPIPE;
+ }
+
+ op &= ISCSI_OPCODE_MASK;
tcp_task->dd_data = tdata;
task->hdr = NULL;
- if (SKB_MAX_HEAD(cdev->skb_tx_rsvd) > (512 * MAX_SKB_FRAGS) &&
- (opcode == ISCSI_OP_SCSI_DATA_OUT ||
- (opcode == ISCSI_OP_SCSI_CMD &&
- (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_TO_DEVICE))))
- /* data could goes into skb head */
- headroom += min_t(unsigned int,
- SKB_MAX_HEAD(cdev->skb_tx_rsvd),
- conn->max_xmit_dlength);
+ last_tdata_count = tdata->count;
+ last_tdata_offset = tdata->offset;
- tdata->skb = alloc_skb(cdev->skb_tx_rsvd + headroom, GFP_ATOMIC);
+ if ((op == ISCSI_OP_SCSI_DATA_OUT) ||
+ ((op == ISCSI_OP_SCSI_CMD) &&
+ (sc->sc_data_direction == DMA_TO_DEVICE))) {
+ u32 remaining_data_tosend, dlimit = 0;
+ u32 max_pdu_size, max_num_pdu, num_pdu;
+ u32 count;
+
+ /* Preserve conn->max_xmit_dlength because it can get updated to
+ * ISO data size.
+ */
+ if (task->state == ISCSI_TASK_PENDING)
+ tdata->max_xmit_dlength = conn->max_xmit_dlength;
+
+ if (!tdata->offset)
+ cxgbi_task_data_sgl_check(task);
+
+ remaining_data_tosend =
+ tdata->dlen - tdata->offset - tdata->count;
+
+recalculate_sgl:
+ max_txdata_len = tdata->max_xmit_dlength;
+ log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
+ "tdata->dlen %u, remaining to send %u "
+ "conn->max_xmit_dlength %u, "
+ "tdata->max_xmit_dlength %u\n",
+ tdata->dlen, remaining_data_tosend,
+ conn->max_xmit_dlength, tdata->max_xmit_dlength);
+
+ if (cdev->skb_iso_txhdr && !csk->disable_iso &&
+ (remaining_data_tosend > tdata->max_xmit_dlength) &&
+ !(remaining_data_tosend % 4)) {
+ u32 max_iso_data;
+
+ if ((op == ISCSI_OP_SCSI_CMD) &&
+ session->initial_r2t_en)
+ goto no_iso;
+
+ max_pdu_size = tdata->max_xmit_dlength +
+ ISCSI_PDU_NONPAYLOAD_LEN;
+ max_iso_data = rounddown(CXGBI_MAX_ISO_DATA_IN_SKB,
+ csk->advmss);
+ max_num_pdu = max_iso_data / max_pdu_size;
+
+ num_pdu = (remaining_data_tosend +
+ tdata->max_xmit_dlength - 1) /
+ tdata->max_xmit_dlength;
+
+ if (num_pdu > max_num_pdu)
+ num_pdu = max_num_pdu;
+
+ conn->max_xmit_dlength = tdata->max_xmit_dlength * num_pdu;
+ max_txdata_len = conn->max_xmit_dlength;
+ iso_tx_rsvd = cdev->skb_iso_txhdr;
+ local_iso_info = sizeof(struct cxgbi_iso_info);
+
+ log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
+ "max_pdu_size %u, max_num_pdu %u, "
+ "max_txdata %u, num_pdu %u\n",
+ max_pdu_size, max_num_pdu,
+ max_txdata_len, num_pdu);
+ }
+no_iso:
+ count = min_t(u32, max_txdata_len, remaining_data_tosend);
+ err = cxgbi_task_data_sgl_read(task,
+ tdata->offset + tdata->count,
+ count, &dlimit);
+ if (unlikely(err < 0)) {
+ log_debug(1 << CXGBI_DBG_ISCSI,
+ "task 0x%p, tcp_task 0x%p, tdata 0x%p, "
+ "sgl err %d, count %u, dlimit %u\n",
+ task, tcp_task, tdata, err, count, dlimit);
+ if (dlimit) {
+ remaining_data_tosend =
+ rounddown(dlimit,
+ tdata->max_xmit_dlength);
+ if (!remaining_data_tosend)
+ remaining_data_tosend = dlimit;
+
+ dlimit = 0;
+
+ conn->max_xmit_dlength = remaining_data_tosend;
+ goto recalculate_sgl;
+ }
+
+ pr_err("task 0x%p, tcp_task 0x%p, tdata 0x%p, "
+ "sgl err %d\n",
+ task, tcp_task, tdata, err);
+ goto ret_err;
+ }
+
+ if ((tdata->flags & CXGBI_TASK_SGL_COPY) ||
+ (tdata->nr_frags > MAX_SKB_FRAGS))
+ headroom += conn->max_xmit_dlength;
+ }
+
+ tdata->skb = alloc_skb(local_iso_info + cdev->skb_tx_rsvd +
+ iso_tx_rsvd + headroom, GFP_ATOMIC);
if (!tdata->skb) {
- pr_warn("alloc skb %u+%u, opcode 0x%x failed.\n",
- cdev->skb_tx_rsvd, headroom, opcode);
- return -ENOMEM;
+ tdata->count = last_tdata_count;
+ tdata->offset = last_tdata_offset;
+ err = -ENOMEM;
+ goto ret_err;
}
- skb_reserve(tdata->skb, cdev->skb_tx_rsvd);
- task->hdr = (struct iscsi_hdr *)tdata->skb->data;
- task->hdr_max = SKB_TX_ISCSI_PDU_HEADER_MAX; /* BHS + AHS */
+ skb_reserve(tdata->skb, local_iso_info + cdev->skb_tx_rsvd +
+ iso_tx_rsvd);
+
+ if (task->sc) {
+ task->hdr = (struct iscsi_hdr *)tdata->skb->data;
+ } else {
+ task->hdr = kzalloc(SKB_TX_ISCSI_PDU_HEADER_MAX, GFP_ATOMIC);
+ if (!task->hdr) {
+ __kfree_skb(tdata->skb);
+ tdata->skb = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ task->hdr_max = SKB_TX_ISCSI_PDU_HEADER_MAX;
+
+ if (iso_tx_rsvd)
+ cxgbi_skcb_set_flag(tdata->skb, SKCBF_TX_ISO);
/* data_out uses scsi_cmd's itt */
- if (opcode != ISCSI_OP_SCSI_DATA_OUT)
+ if (op != ISCSI_OP_SCSI_DATA_OUT)
task_reserve_itt(task, &task->hdr->itt);
log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
- "task 0x%p, op 0x%x, skb 0x%p,%u+%u/%u, itt 0x%x.\n",
- task, opcode, tdata->skb, cdev->skb_tx_rsvd, headroom,
- conn->max_xmit_dlength, ntohl(task->hdr->itt));
+ "task 0x%p, op 0x%x, skb 0x%p,%u+%u/%u, itt 0x%x.\n",
+ task, op, tdata->skb, cdev->skb_tx_rsvd, headroom,
+ conn->max_xmit_dlength, be32_to_cpu(task->hdr->itt));
return 0;
+
+ret_err:
+ conn->max_xmit_dlength = tdata->max_xmit_dlength;
+ return err;
}
EXPORT_SYMBOL_GPL(cxgbi_conn_alloc_pdu);
+static int
+cxgbi_prep_iso_info(struct iscsi_task *task, struct sk_buff *skb,
+ u32 count)
+{
+ struct cxgbi_iso_info *iso_info = (struct cxgbi_iso_info *)skb->head;
+ struct iscsi_r2t_info *r2t;
+ struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ u32 burst_size = 0, r2t_dlength = 0, dlength;
+ u32 max_pdu_len = tdata->max_xmit_dlength;
+ u32 segment_offset = 0;
+ u32 num_pdu;
+
+ if (unlikely(!cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO)))
+ return 0;
+
+ memset(iso_info, 0, sizeof(struct cxgbi_iso_info));
+
+ if (task->hdr->opcode == ISCSI_OP_SCSI_CMD && session->imm_data_en) {
+ iso_info->flags |= CXGBI_ISO_INFO_IMM_ENABLE;
+ burst_size = count;
+ }
+
+ dlength = ntoh24(task->hdr->dlength);
+ dlength = min(dlength, max_pdu_len);
+ hton24(task->hdr->dlength, dlength);
+
+ num_pdu = (count + max_pdu_len - 1) / max_pdu_len;
+
+ if (iscsi_task_has_unsol_data(task))
+ r2t = &task->unsol_r2t;
+ else
+ r2t = tcp_task->r2t;
+
+ if (r2t) {
+ log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
+ "count %u, tdata->count %u, num_pdu %u,"
+ "task->hdr_len %u, r2t->data_length %u, r2t->sent %u\n",
+ count, tdata->count, num_pdu, task->hdr_len,
+ r2t->data_length, r2t->sent);
+
+ r2t_dlength = r2t->data_length - r2t->sent;
+ segment_offset = r2t->sent;
+ r2t->datasn += num_pdu - 1;
+ }
+
+ if (!r2t || !r2t->sent)
+ iso_info->flags |= CXGBI_ISO_INFO_FSLICE;
+
+ if (task->hdr->flags & ISCSI_FLAG_CMD_FINAL)
+ iso_info->flags |= CXGBI_ISO_INFO_LSLICE;
+
+ task->hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
+
+ iso_info->op = task->hdr->opcode;
+ iso_info->ahs = task->hdr->hlength;
+ iso_info->num_pdu = num_pdu;
+ iso_info->mpdu = max_pdu_len;
+ iso_info->burst_size = (burst_size + r2t_dlength) >> 2;
+ iso_info->len = count + task->hdr_len;
+ iso_info->segment_offset = segment_offset;
+
+ cxgbi_skcb_tx_iscsi_hdrlen(skb) = task->hdr_len;
+ return 0;
+}
+
static inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
{
if (hcrc || dcrc) {
@@ -1892,137 +2134,289 @@ static inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
submode |= 1;
if (dcrc)
submode |= 2;
- cxgbi_skcb_ulp_mode(skb) = (ULP2_MODE_ISCSI << 4) | submode;
+ cxgbi_skcb_tx_ulp_mode(skb) = (ULP2_MODE_ISCSI << 4) | submode;
} else
- cxgbi_skcb_ulp_mode(skb) = 0;
+ cxgbi_skcb_tx_ulp_mode(skb) = 0;
}
+static struct page *rsvd_page;
+
int cxgbi_conn_init_pdu(struct iscsi_task *task, unsigned int offset,
unsigned int count)
{
struct iscsi_conn *conn = task->conn;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
- struct sk_buff *skb = tdata->skb;
- unsigned int datalen = count;
- int i, padlen = iscsi_padding(count);
+ struct sk_buff *skb;
+ struct scsi_cmnd *sc = task->sc;
+ u32 expected_count, expected_offset;
+ u32 datalen = count, dlimit = 0;
+ u32 i, padlen = iscsi_padding(count);
struct page *pg;
+ int err;
+
+ if (!tcp_task || (tcp_task->dd_data != tdata)) {
+ pr_err("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p.\n",
+ task, task->sc, tcp_task,
+ tcp_task ? tcp_task->dd_data : NULL, tdata);
+ return -EINVAL;
+ }
+ skb = tdata->skb;
log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
- "task 0x%p,0x%p, skb 0x%p, 0x%x,0x%x,0x%x, %u+%u.\n",
- task, task->sc, skb, (*skb->data) & ISCSI_OPCODE_MASK,
- ntohl(task->cmdsn), ntohl(task->hdr->itt), offset, count);
+ "task 0x%p,0x%p, skb 0x%p, 0x%x,0x%x,0x%x, %u+%u.\n",
+ task, task->sc, skb, (*skb->data) & ISCSI_OPCODE_MASK,
+ be32_to_cpu(task->cmdsn), be32_to_cpu(task->hdr->itt), offset, count);
skb_put(skb, task->hdr_len);
tx_skb_setmode(skb, conn->hdrdgst_en, datalen ? conn->datadgst_en : 0);
- if (!count)
+ if (!count) {
+ tdata->count = count;
+ tdata->offset = offset;
+ tdata->nr_frags = 0;
+ tdata->total_offset = 0;
+ tdata->total_count = 0;
+ if (tdata->max_xmit_dlength)
+ conn->max_xmit_dlength = tdata->max_xmit_dlength;
+ cxgbi_skcb_clear_flag(skb, SKCBF_TX_ISO);
return 0;
+ }
- if (task->sc) {
- struct scsi_data_buffer *sdb = scsi_out(task->sc);
- struct scatterlist *sg = NULL;
- int err;
+ log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
+ "data->total_count %u, tdata->total_offset %u\n",
+ tdata->total_count, tdata->total_offset);
- tdata->offset = offset;
- tdata->count = count;
- err = sgl_seek_offset(
- sdb->table.sgl, sdb->table.nents,
- tdata->offset, &tdata->sgoffset, &sg);
- if (err < 0) {
- pr_warn("tpdu, sgl %u, bad offset %u/%u.\n",
- sdb->table.nents, tdata->offset, sdb->length);
- return err;
- }
- err = sgl_read_to_frags(sg, tdata->sgoffset, tdata->count,
- tdata->frags, MAX_PDU_FRAGS);
+ expected_count = tdata->total_count;
+ expected_offset = tdata->total_offset;
+
+ if ((count != expected_count) ||
+ (offset != expected_offset)) {
+ err = cxgbi_task_data_sgl_read(task, offset, count, &dlimit);
if (err < 0) {
- pr_warn("tpdu, sgl %u, bad offset %u + %u.\n",
- sdb->table.nents, tdata->offset, tdata->count);
+ pr_err("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p "
+ "dlimit %u, sgl err %d.\n", task, task->sc,
+ tcp_task, tcp_task ? tcp_task->dd_data : NULL,
+ tdata, dlimit, err);
return err;
}
- tdata->nr_frags = err;
+ }
+
+ /* Restore original value of conn->max_xmit_dlength because
+ * it can get updated to ISO data size.
+ */
+ conn->max_xmit_dlength = tdata->max_xmit_dlength;
- if (tdata->nr_frags > MAX_SKB_FRAGS ||
- (padlen && tdata->nr_frags == MAX_SKB_FRAGS)) {
+ if (sc) {
+ struct page_frag *frag = tdata->frags;
+
+ if ((tdata->flags & CXGBI_TASK_SGL_COPY) ||
+ (tdata->nr_frags > MAX_SKB_FRAGS) ||
+ (padlen && (tdata->nr_frags ==
+ MAX_SKB_FRAGS))) {
char *dst = skb->data + task->hdr_len;
- skb_frag_t *frag = tdata->frags;
/* data fits in the skb's headroom */
for (i = 0; i < tdata->nr_frags; i++, frag++) {
- char *src = kmap_atomic(frag->page,
- KM_SOFTIRQ0);
+ char *src = kmap_atomic(frag->page);
- memcpy(dst, src+frag->page_offset, frag->size);
+ memcpy(dst, src + frag->offset, frag->size);
dst += frag->size;
- kunmap_atomic(src, KM_SOFTIRQ0);
+ kunmap_atomic(src);
}
+
if (padlen) {
memset(dst, 0, padlen);
padlen = 0;
}
skb_put(skb, count + padlen);
} else {
- /* data fit into frag_list */
- for (i = 0; i < tdata->nr_frags; i++)
- get_page(tdata->frags[i].page);
+ for (i = 0; i < tdata->nr_frags; i++, frag++) {
+ get_page(frag->page);
+ skb_fill_page_desc(skb, i, frag->page,
+ frag->offset, frag->size);
+ }
- memcpy(skb_shinfo(skb)->frags, tdata->frags,
- sizeof(skb_frag_t) * tdata->nr_frags);
- skb_shinfo(skb)->nr_frags = tdata->nr_frags;
skb->len += count;
skb->data_len += count;
skb->truesize += count;
}
-
} else {
- pg = virt_to_page(task->data);
-
+ pg = virt_to_head_page(task->data);
get_page(pg);
- skb_fill_page_desc(skb, 0, pg, offset_in_page(task->data),
- count);
+ skb_fill_page_desc(skb, 0, pg,
+ task->data - (char *)page_address(pg),
+ count);
skb->len += count;
skb->data_len += count;
skb->truesize += count;
}
if (padlen) {
- i = skb_shinfo(skb)->nr_frags;
+ get_page(rsvd_page);
skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
- virt_to_page(padding), offset_in_page(padding),
- padlen);
+ rsvd_page, 0, padlen);
skb->data_len += padlen;
skb->truesize += padlen;
skb->len += padlen;
}
+ if (likely(count > tdata->max_xmit_dlength))
+ cxgbi_prep_iso_info(task, skb, count);
+ else
+ cxgbi_skcb_clear_flag(skb, SKCBF_TX_ISO);
+
return 0;
}
EXPORT_SYMBOL_GPL(cxgbi_conn_init_pdu);
+static int cxgbi_sock_tx_queue_up(struct cxgbi_sock *csk, struct sk_buff *skb)
+{
+ struct cxgbi_device *cdev = csk->cdev;
+ struct cxgbi_iso_info *iso_cpl;
+ u32 frags = skb_shinfo(skb)->nr_frags;
+ u32 extra_len, num_pdu, hdr_len;
+ u32 iso_tx_rsvd = 0;
+
+ if (csk->state != CTP_ESTABLISHED) {
+ log_debug(1 << CXGBI_DBG_PDU_TX,
+ "csk 0x%p,%u,0x%lx,%u, EAGAIN.\n",
+ csk, csk->state, csk->flags, csk->tid);
+ return -EPIPE;
+ }
+
+ if (csk->err) {
+ log_debug(1 << CXGBI_DBG_PDU_TX,
+ "csk 0x%p,%u,0x%lx,%u, EPIPE %d.\n",
+ csk, csk->state, csk->flags, csk->tid, csk->err);
+ return -EPIPE;
+ }
+
+ if ((cdev->flags & CXGBI_FLAG_DEV_T3) &&
+ before((csk->snd_win + csk->snd_una), csk->write_seq)) {
+ log_debug(1 << CXGBI_DBG_PDU_TX,
+ "csk 0x%p,%u,0x%lx,%u, FULL %u-%u >= %u.\n",
+ csk, csk->state, csk->flags, csk->tid, csk->write_seq,
+ csk->snd_una, csk->snd_win);
+ return -ENOBUFS;
+ }
+
+ if (cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO))
+ iso_tx_rsvd = cdev->skb_iso_txhdr;
+
+ if (unlikely(skb_headroom(skb) < (cdev->skb_tx_rsvd + iso_tx_rsvd))) {
+ pr_err("csk 0x%p, skb head %u < %u.\n",
+ csk, skb_headroom(skb), cdev->skb_tx_rsvd);
+ return -EINVAL;
+ }
+
+ if (skb->len != skb->data_len)
+ frags++;
+
+ if (frags >= SKB_WR_LIST_SIZE) {
+ pr_err("csk 0x%p, frags %u, %u,%u >%u.\n",
+ csk, skb_shinfo(skb)->nr_frags, skb->len,
+ skb->data_len, (unsigned int)SKB_WR_LIST_SIZE);
+ return -EINVAL;
+ }
+
+ cxgbi_skcb_set_flag(skb, SKCBF_TX_NEED_HDR);
+ skb_reset_transport_header(skb);
+ cxgbi_sock_skb_entail(csk, skb);
+
+ extra_len = cxgbi_ulp_extra_len(cxgbi_skcb_tx_ulp_mode(skb));
+
+ if (likely(cxgbi_skcb_test_flag(skb, SKCBF_TX_ISO))) {
+ iso_cpl = (struct cxgbi_iso_info *)skb->head;
+ num_pdu = iso_cpl->num_pdu;
+ hdr_len = cxgbi_skcb_tx_iscsi_hdrlen(skb);
+ extra_len = (cxgbi_ulp_extra_len(cxgbi_skcb_tx_ulp_mode(skb)) *
+ num_pdu) + (hdr_len * (num_pdu - 1));
+ }
+
+ csk->write_seq += (skb->len + extra_len);
+
+ return 0;
+}
+
+static int cxgbi_sock_send_skb(struct cxgbi_sock *csk, struct sk_buff *skb)
+{
+ struct cxgbi_device *cdev = csk->cdev;
+ int len = skb->len;
+ int err;
+
+ spin_lock_bh(&csk->lock);
+ err = cxgbi_sock_tx_queue_up(csk, skb);
+ if (err < 0) {
+ spin_unlock_bh(&csk->lock);
+ return err;
+ }
+
+ if (likely(skb_queue_len(&csk->write_queue)))
+ cdev->csk_push_tx_frames(csk, 0);
+ spin_unlock_bh(&csk->lock);
+ return len;
+}
+
int cxgbi_conn_xmit_pdu(struct iscsi_task *task)
{
struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
- struct sk_buff *skb = tdata->skb;
- unsigned int datalen;
+ struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo;
+ struct sk_buff *skb;
+ struct cxgbi_sock *csk = NULL;
+ u32 pdulen = 0;
+ u32 datalen;
int err;
+ if (!tcp_task || (tcp_task->dd_data != tdata)) {
+ pr_err("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p.\n",
+ task, task->sc, tcp_task,
+ tcp_task ? tcp_task->dd_data : NULL, tdata);
+ return -EINVAL;
+ }
+
+ skb = tdata->skb;
if (!skb) {
log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
- "task 0x%p, skb NULL.\n", task);
+ "task 0x%p, skb NULL.\n", task);
return 0;
}
- datalen = skb->data_len;
+ if (cconn && cconn->cep)
+ csk = cconn->cep->csk;
+
+ if (!csk) {
+ log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
+ "task 0x%p, csk gone.\n", task);
+ return -EPIPE;
+ }
+
tdata->skb = NULL;
- err = cxgbi_sock_send_pdus(cconn->cep->csk, skb);
+ datalen = skb->data_len;
+
+ /* write ppod first if using ofldq to write ppod */
+ if (ttinfo->flags & CXGBI_PPOD_INFO_FLAG_VALID) {
+ struct cxgbi_ppm *ppm = csk->cdev->cdev2ppm(csk->cdev);
+
+ ttinfo->flags &= ~CXGBI_PPOD_INFO_FLAG_VALID;
+ if (csk->cdev->csk_ddp_set_map(ppm, csk, ttinfo) < 0)
+ pr_err("task 0x%p, ppod writing using ofldq failed.\n",
+ task);
+ /* continue. Let fl get the data */
+ }
+
+ if (!task->sc)
+ memcpy(skb->data, task->hdr, SKB_TX_ISCSI_PDU_HEADER_MAX);
+
+ err = cxgbi_sock_send_skb(csk, skb);
if (err > 0) {
- int pdulen = err;
+ pdulen += err;
- log_debug(1 << CXGBI_DBG_PDU_TX,
- "task 0x%p,0x%p, skb 0x%p, len %u/%u, rv %d.\n",
- task, task->sc, skb, skb->len, skb->data_len, err);
+ log_debug(1 << CXGBI_DBG_PDU_TX, "task 0x%p,0x%p, rv %d.\n",
+ task, task->sc, err);
if (task->conn->hdrdgst_en)
pdulen += ISCSI_DIGEST_SIZE;
@@ -2031,22 +2425,42 @@ int cxgbi_conn_xmit_pdu(struct iscsi_task *task)
pdulen += ISCSI_DIGEST_SIZE;
task->conn->txdata_octets += pdulen;
+
+ if (unlikely(cxgbi_is_iso_config(csk) && cxgbi_is_iso_disabled(csk))) {
+ if (time_after(jiffies, csk->prev_iso_ts + HZ)) {
+ csk->disable_iso = false;
+ csk->prev_iso_ts = 0;
+ log_debug(1 << CXGBI_DBG_PDU_TX,
+ "enable iso: csk 0x%p\n", csk);
+ }
+ }
+
return 0;
}
if (err == -EAGAIN || err == -ENOBUFS) {
log_debug(1 << CXGBI_DBG_PDU_TX,
- "task 0x%p, skb 0x%p, len %u/%u, %d EAGAIN.\n",
- task, skb, skb->len, skb->data_len, err);
+ "task 0x%p, skb 0x%p, len %u/%u, %d EAGAIN.\n",
+ task, skb, skb->len, skb->data_len, err);
/* reset skb to send when we are called again */
tdata->skb = skb;
+
+ if (cxgbi_is_iso_config(csk) && !cxgbi_is_iso_disabled(csk) &&
+ (csk->no_tx_credits++ >= 2)) {
+ csk->disable_iso = true;
+ csk->prev_iso_ts = jiffies;
+ log_debug(1 << CXGBI_DBG_PDU_TX,
+ "disable iso:csk 0x%p, ts:%lu\n",
+ csk, csk->prev_iso_ts);
+ }
+
return err;
}
- kfree_skb(skb);
log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
- "itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
- task->itt, skb, skb->len, skb->data_len, err);
+ "itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
+ task->itt, skb, skb->len, skb->data_len, err);
+ __kfree_skb(skb);
iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
return err;
@@ -2055,18 +2469,35 @@ EXPORT_SYMBOL_GPL(cxgbi_conn_xmit_pdu);
void cxgbi_cleanup_task(struct iscsi_task *task)
{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task);
+ if (!tcp_task || (tcp_task->dd_data != tdata)) {
+ pr_info("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p.\n",
+ task, task->sc, tcp_task,
+ tcp_task ? tcp_task->dd_data : NULL, tdata);
+ return;
+ }
+
log_debug(1 << CXGBI_DBG_ISCSI,
"task 0x%p, skb 0x%p, itt 0x%x.\n",
task, tdata->skb, task->hdr_itt);
+ tcp_task->dd_data = NULL;
+
+ if (!task->sc)
+ kfree(task->hdr);
+ task->hdr = NULL;
+
/* never reached the xmit task callout */
- if (tdata->skb)
+ if (tdata->skb) {
__kfree_skb(tdata->skb);
- memset(tdata, 0, sizeof(*tdata));
+ tdata->skb = NULL;
+ }
task_release_itt(task, task->hdr_itt);
+ memset(tdata, 0, sizeof(*tdata));
+
iscsi_tcp_cleanup_task(task);
}
EXPORT_SYMBOL_GPL(cxgbi_cleanup_task);
@@ -2138,11 +2569,10 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
struct cxgbi_sock *csk = cconn->cep->csk;
- int value, err = 0;
+ int err;
log_debug(1 << CXGBI_DBG_ISCSI,
"cls_conn 0x%p, param %d, buf(%d) %s.\n",
@@ -2154,25 +2584,17 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
if (!err && conn->hdrdgst_en)
err = csk->cdev->csk_ddp_setup_digest(csk, csk->tid,
conn->hdrdgst_en,
- conn->datadgst_en, 0);
+ conn->datadgst_en);
break;
case ISCSI_PARAM_DATADGST_EN:
err = iscsi_set_param(cls_conn, param, buf, buflen);
if (!err && conn->datadgst_en)
err = csk->cdev->csk_ddp_setup_digest(csk, csk->tid,
conn->hdrdgst_en,
- conn->datadgst_en, 0);
+ conn->datadgst_en);
break;
case ISCSI_PARAM_MAX_R2T:
- sscanf(buf, "%d", &value);
- if (value <= 0 || !is_power_of_2(value))
- return -EINVAL;
- if (session->max_r2t == value)
- break;
- iscsi_tcp_r2tpool_free(session);
- err = iscsi_set_param(cls_conn, param, buf, buflen);
- if (!err && iscsi_tcp_r2tpool_alloc(session))
- return -ENOMEM;
+ return iscsi_tcp_set_max_r2t(conn, buf);
case ISCSI_PARAM_MAX_RECV_DLENGTH:
err = iscsi_set_param(cls_conn, param, buf, buflen);
if (!err)
@@ -2195,7 +2617,6 @@ int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param,
{
struct cxgbi_endpoint *cep = ep->dd_data;
struct cxgbi_sock *csk;
- int len;
log_debug(1 << CXGBI_DBG_ISCSI,
"cls_conn 0x%p, param %d.\n", ep, param);
@@ -2213,9 +2634,9 @@ int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param,
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
&csk->daddr, param, buf);
default:
- return -ENOSYS;
+ break;
}
- return len;
+ return -ENOSYS;
}
EXPORT_SYMBOL_GPL(cxgbi_get_ep_param);
@@ -2251,6 +2672,7 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
+ struct cxgbi_ppm *ppm;
struct iscsi_endpoint *ep;
struct cxgbi_endpoint *cep;
struct cxgbi_sock *csk;
@@ -2263,13 +2685,18 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
/* setup ddp pagesize */
cep = ep->dd_data;
csk = cep->csk;
- err = csk->cdev->csk_ddp_setup_pgidx(csk, csk->tid, page_idx, 0);
+
+ ppm = csk->cdev->cdev2ppm(csk->cdev);
+ err = csk->cdev->csk_ddp_setup_pgidx(csk, csk->tid,
+ ppm->tformat.pgsz_idx_dflt);
if (err < 0)
- return err;
+ goto put_ep;
err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
- if (err)
- return -EINVAL;
+ if (err) {
+ err = -EINVAL;
+ goto put_ep;
+ }
/* calculate the tag idx bits needed for this conn based on cmds_max */
cconn->task_idx_bits = (__ilog2_u32(conn->session->cmds_max - 1)) + 1;
@@ -2290,7 +2717,9 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
/* init recv engine */
iscsi_tcp_hdr_recv_prep(tcp_conn);
- return 0;
+put_ep:
+ iscsi_put_endpoint(ep);
+ return err;
}
EXPORT_SYMBOL_GPL(cxgbi_bind_conn);
@@ -2405,12 +2834,14 @@ int cxgbi_get_host_param(struct Scsi_Host *shost, enum iscsi_host_param param,
break;
case ISCSI_HOST_PARAM_IPADDRESS:
{
- __be32 addr;
-
- addr = cxgbi_get_iscsi_ipv4(chba);
- len = sprintf(buf, "%pI4", &addr);
+ struct cxgbi_sock *csk = find_sock_on_port(chba->cdev,
+ chba->port_id);
+ if (csk) {
+ len = sprintf(buf, "%pIS",
+ (struct sockaddr *)&csk->saddr);
+ }
log_debug(1 << CXGBI_DBG_ISCSI,
- "hba %s, ipv4 %pI4.\n", chba->ndev->name, &addr);
+ "hba %s, addr %s.\n", chba->ndev->name, buf);
break;
}
default:
@@ -2429,6 +2860,7 @@ struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost,
struct cxgbi_endpoint *cep;
struct cxgbi_hba *hba = NULL;
struct cxgbi_sock *csk;
+ int ifindex = 0;
int err = -EINVAL;
log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_SOCK,
@@ -2443,7 +2875,20 @@ struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost,
}
}
- csk = cxgbi_check_route(dst_addr);
+check_route:
+ if (dst_addr->sa_family == AF_INET) {
+ csk = cxgbi_check_route(dst_addr, ifindex);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (dst_addr->sa_family == AF_INET6) {
+ csk = cxgbi_check_route6(dst_addr, ifindex);
+#endif
+ } else {
+ pr_info("address family 0x%x NOT supported.\n",
+ dst_addr->sa_family);
+ err = -EAFNOSUPPORT;
+ return (struct iscsi_endpoint *)ERR_PTR(err);
+ }
+
if (IS_ERR(csk))
return (struct iscsi_endpoint *)csk;
cxgbi_sock_get(csk);
@@ -2451,6 +2896,13 @@ struct iscsi_endpoint *cxgbi_ep_connect(struct Scsi_Host *shost,
if (!hba)
hba = csk->cdev->hbas[csk->port_id];
else if (hba != csk->cdev->hbas[csk->port_id]) {
+ if (ifindex != hba->ndev->ifindex) {
+ cxgbi_sock_put(csk);
+ cxgbi_sock_closed(csk);
+ ifindex = hba->ndev->ifindex;
+ goto check_route;
+ }
+
pr_info("Could not connect through requested host %u"
"hba 0x%p != 0x%p (%u).\n",
shost->host_no, hba,
@@ -2520,7 +2972,6 @@ void cxgbi_ep_disconnect(struct iscsi_endpoint *ep)
ep, cep, cconn, csk, csk->state, csk->flags);
if (cconn && cconn->iconn) {
- iscsi_suspend_tx(cconn->iconn);
write_lock_bh(&csk->callback_lock);
cep->csk->user_data = NULL;
cconn->cep = NULL;
@@ -2566,22 +3017,79 @@ void cxgbi_iscsi_cleanup(struct iscsi_transport *itp,
}
EXPORT_SYMBOL_GPL(cxgbi_iscsi_cleanup);
+umode_t cxgbi_attr_is_visible(int param_type, int param)
+{
+ switch (param_type) {
+ case ISCSI_HOST_PARAM:
+ switch (param) {
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ case ISCSI_HOST_PARAM_INITIATOR_NAME:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case ISCSI_PARAM:
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ case ISCSI_PARAM_HDRDGST_EN:
+ case ISCSI_PARAM_DATADGST_EN:
+ case ISCSI_PARAM_CONN_ADDRESS:
+ case ISCSI_PARAM_CONN_PORT:
+ case ISCSI_PARAM_EXP_STATSN:
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ case ISCSI_PARAM_PING_TMO:
+ case ISCSI_PARAM_RECV_TMO:
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ case ISCSI_PARAM_MAX_R2T:
+ case ISCSI_PARAM_IMM_DATA_EN:
+ case ISCSI_PARAM_FIRST_BURST:
+ case ISCSI_PARAM_MAX_BURST:
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ case ISCSI_PARAM_ERL:
+ case ISCSI_PARAM_TARGET_NAME:
+ case ISCSI_PARAM_TPGT:
+ case ISCSI_PARAM_USERNAME:
+ case ISCSI_PARAM_PASSWORD:
+ case ISCSI_PARAM_USERNAME_IN:
+ case ISCSI_PARAM_PASSWORD_IN:
+ case ISCSI_PARAM_FAST_ABORT:
+ case ISCSI_PARAM_ABORT_TMO:
+ case ISCSI_PARAM_LU_RESET_TMO:
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ case ISCSI_PARAM_IFACE_NAME:
+ case ISCSI_PARAM_INITIATOR_NAME:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxgbi_attr_is_visible);
+
static int __init libcxgbi_init_module(void)
{
- sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1;
- sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1;
+ pr_info("%s", version);
- pr_info("tag itt 0x%x, %u bits, age 0x%x, %u bits.\n",
- ISCSI_ITT_MASK, sw_tag_idx_bits,
- ISCSI_AGE_MASK, sw_tag_age_bits);
+ BUILD_BUG_ON(sizeof_field(struct sk_buff, cb) <
+ sizeof(struct cxgbi_skb_cb));
+ rsvd_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!rsvd_page)
+ return -ENOMEM;
- ddp_setup_host_page_size();
return 0;
}
static void __exit libcxgbi_exit_module(void)
{
cxgbi_device_unregister_all(0xFF);
+ put_page(rsvd_page);
return;
}
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h
index 9267844519c9..0909b03e2497 100644
--- a/drivers/scsi/cxgbi/libcxgbi.h
+++ b/drivers/scsi/cxgbi/libcxgbi.h
@@ -1,7 +1,7 @@
/*
* libcxgbi.h: Chelsio common library for T3/T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,8 @@
#include <scsi/scsi_device.h>
#include <scsi/libiscsi_tcp.h>
+#include <libcxgb_ppm.h>
+
enum cxgbi_dbg_flag {
CXGBI_DBG_ISCSI,
CXGBI_DBG_DDP,
@@ -44,6 +46,15 @@ enum cxgbi_dbg_flag {
pr_info(fmt, ##__VA_ARGS__); \
} while (0)
+#define pr_info_ipaddr(fmt_trail, \
+ addr1, addr2, args_trail...) \
+do { \
+ if (!((1 << CXGBI_DBG_SOCK) & dbg_level)) \
+ break; \
+ pr_info("%pISpc - %pISpc, " fmt_trail, \
+ addr1, addr2, args_trail); \
+} while (0)
+
/* max. connections per adapter */
#define CXGBI_MAX_CONN 16384
@@ -64,6 +75,14 @@ enum cxgbi_dbg_flag {
#define ULP2_MAX_PDU_PAYLOAD \
(ULP2_MAX_PKT_SIZE - ISCSI_PDU_NONPAYLOAD_LEN)
+#define CXGBI_ULP2_MAX_ISO_PAYLOAD 65535
+
+#define CXGBI_MAX_ISO_DATA_IN_SKB \
+ min_t(u32, MAX_SKB_FRAGS << PAGE_SHIFT, CXGBI_ULP2_MAX_ISO_PAYLOAD)
+
+#define cxgbi_is_iso_config(csk) ((csk)->cdev->skb_iso_txhdr)
+#define cxgbi_is_iso_disabled(csk) ((csk)->disable_iso)
+
/*
* For iscsi connections HW may inserts digest bytes into the pdu. Those digest
* bytes are not sent by the host but are part of the TCP payload and therefore
@@ -75,92 +94,11 @@ static inline unsigned int cxgbi_ulp_extra_len(int submode)
return ulp2_extra_len[submode & 3];
}
-/*
- * struct pagepod_hdr, pagepod - pagepod format
- */
-
#define CPL_RX_DDP_STATUS_DDP_SHIFT 16 /* ddp'able */
#define CPL_RX_DDP_STATUS_PAD_SHIFT 19 /* pad error */
#define CPL_RX_DDP_STATUS_HCRC_SHIFT 20 /* hcrc error */
#define CPL_RX_DDP_STATUS_DCRC_SHIFT 21 /* dcrc error */
-struct cxgbi_pagepod_hdr {
- u32 vld_tid;
- u32 pgsz_tag_clr;
- u32 max_offset;
- u32 page_offset;
- u64 rsvd;
-};
-
-#define PPOD_PAGES_MAX 4
-struct cxgbi_pagepod {
- struct cxgbi_pagepod_hdr hdr;
- u64 addr[PPOD_PAGES_MAX + 1];
-};
-
-struct cxgbi_tag_format {
- unsigned char sw_bits;
- unsigned char rsvd_bits;
- unsigned char rsvd_shift;
- unsigned char filler[1];
- u32 rsvd_mask;
-};
-
-struct cxgbi_gather_list {
- unsigned int tag;
- unsigned int length;
- unsigned int offset;
- unsigned int nelem;
- struct page **pages;
- dma_addr_t phys_addr[0];
-};
-
-struct cxgbi_ddp_info {
- struct kref refcnt;
- struct cxgbi_device *cdev;
- struct pci_dev *pdev;
- unsigned int max_txsz;
- unsigned int max_rxsz;
- unsigned int llimit;
- unsigned int ulimit;
- unsigned int nppods;
- unsigned int idx_last;
- unsigned char idx_bits;
- unsigned char filler[3];
- unsigned int idx_mask;
- unsigned int rsvd_tag_mask;
- spinlock_t map_lock;
- struct cxgbi_gather_list **gl_map;
-};
-
-#define DDP_PGIDX_MAX 4
-#define DDP_THRESHOLD 2048
-
-#define PPOD_PAGES_SHIFT 2 /* 4 pages per pod */
-
-#define PPOD_SIZE sizeof(struct cxgbi_pagepod) /* 64 */
-#define PPOD_SIZE_SHIFT 6
-
-#define ULPMEM_DSGL_MAX_NPPODS 16 /* 1024/PPOD_SIZE */
-#define ULPMEM_IDATA_MAX_NPPODS 4 /* 256/PPOD_SIZE */
-#define PCIE_MEMWIN_MAX_NPPODS 16 /* 1024/PPOD_SIZE */
-
-#define PPOD_COLOR_SHIFT 0
-#define PPOD_COLOR(x) ((x) << PPOD_COLOR_SHIFT)
-
-#define PPOD_IDX_SHIFT 6
-#define PPOD_IDX_MAX_SIZE 24
-
-#define PPOD_TID_SHIFT 0
-#define PPOD_TID(x) ((x) << PPOD_TID_SHIFT)
-
-#define PPOD_TAG_SHIFT 6
-#define PPOD_TAG(x) ((x) << PPOD_TAG_SHIFT)
-
-#define PPOD_VALID_SHIFT 24
-#define PPOD_VALID(x) ((x) << PPOD_VALID_SHIFT)
-#define PPOD_VALID_FLAG PPOD_VALID(1U)
-
/*
* sge_opaque_hdr -
* Opaque version of structure the SGE stores at skb->head of TX_DATA packets
@@ -189,6 +127,9 @@ struct cxgbi_sock {
int wr_max_cred;
int wr_cred;
int wr_una_cred;
+#ifdef CONFIG_CHELSIO_T4_DCB
+ u8 dcb_priority;
+#endif
unsigned char hcrc_len;
unsigned char dcrc_len;
@@ -202,12 +143,20 @@ struct cxgbi_sock {
spinlock_t lock;
struct kref refcnt;
unsigned int state;
- struct sockaddr_in saddr;
- struct sockaddr_in daddr;
+ unsigned int csk_family;
+ union {
+ struct sockaddr_in saddr;
+ struct sockaddr_in6 saddr6;
+ };
+ union {
+ struct sockaddr_in daddr;
+ struct sockaddr_in6 daddr6;
+ };
struct dst_entry *dst;
struct sk_buff_head receive_queue;
struct sk_buff_head write_queue;
struct timer_list retry_timer;
+ struct completion cmpl;
int err;
rwlock_t callback_lock;
void *user_data;
@@ -218,6 +167,12 @@ struct cxgbi_sock {
u32 snd_nxt;
u32 snd_una;
u32 write_seq;
+ u32 snd_win;
+ u32 rcv_win;
+
+ bool disable_iso;
+ u32 no_tx_credits;
+ unsigned long prev_iso_ts;
};
/*
@@ -247,6 +202,7 @@ enum cxgbi_sock_flags {
CTPF_HAS_ATID, /* reserved atid */
CTPF_HAS_TID, /* reserved hw tid */
CTPF_OFFLOAD_DOWN, /* offload function off */
+ CTPF_LOGOUT_RSP_RCVD, /* received logout response */
};
struct cxgbi_skb_rx_cb {
@@ -255,39 +211,46 @@ struct cxgbi_skb_rx_cb {
};
struct cxgbi_skb_tx_cb {
- void *l2t;
+ void *handle;
+ void *arp_err_handler;
struct sk_buff *wr_next;
+ u16 iscsi_hdr_len;
+ u8 ulp_mode;
};
enum cxgbi_skcb_flags {
SKCBF_TX_NEED_HDR, /* packet needs a header */
+ SKCBF_TX_MEM_WRITE, /* memory write */
+ SKCBF_TX_FLAG_COMPL, /* wr completion flag */
SKCBF_RX_COALESCED, /* received whole pdu */
SKCBF_RX_HDR, /* received pdu header */
SKCBF_RX_DATA, /* received pdu payload */
SKCBF_RX_STATUS, /* received ddp status */
+ SKCBF_RX_ISCSI_COMPL, /* received iscsi completion */
SKCBF_RX_DATA_DDPD, /* pdu payload ddp'd */
SKCBF_RX_HCRC_ERR, /* header digest error */
SKCBF_RX_DCRC_ERR, /* data digest error */
SKCBF_RX_PAD_ERR, /* padding byte error */
+ SKCBF_TX_ISO, /* iso cpl in tx skb */
};
struct cxgbi_skb_cb {
- unsigned char ulp_mode;
- unsigned long flags;
- unsigned int seq;
union {
struct cxgbi_skb_rx_cb rx;
struct cxgbi_skb_tx_cb tx;
};
+ unsigned long flags;
+ unsigned int seq;
};
#define CXGBI_SKB_CB(skb) ((struct cxgbi_skb_cb *)&((skb)->cb[0]))
#define cxgbi_skcb_flags(skb) (CXGBI_SKB_CB(skb)->flags)
-#define cxgbi_skcb_ulp_mode(skb) (CXGBI_SKB_CB(skb)->ulp_mode)
#define cxgbi_skcb_tcp_seq(skb) (CXGBI_SKB_CB(skb)->seq)
#define cxgbi_skcb_rx_ddigest(skb) (CXGBI_SKB_CB(skb)->rx.ddigest)
#define cxgbi_skcb_rx_pdulen(skb) (CXGBI_SKB_CB(skb)->rx.pdulen)
#define cxgbi_skcb_tx_wr_next(skb) (CXGBI_SKB_CB(skb)->tx.wr_next)
+#define cxgbi_skcb_tx_iscsi_hdrlen(skb) (CXGBI_SKB_CB(skb)->tx.iscsi_hdr_len)
+#define cxgbi_skcb_tx_ulp_mode(skb) (CXGBI_SKB_CB(skb)->tx.ulp_mode)
static inline void cxgbi_skcb_set_flag(struct sk_buff *skb,
enum cxgbi_skcb_flags flag)
@@ -301,8 +264,8 @@ static inline void cxgbi_skcb_clear_flag(struct sk_buff *skb,
__clear_bit(flag, &(cxgbi_skcb_flags(skb)));
}
-static inline int cxgbi_skcb_test_flag(struct sk_buff *skb,
- enum cxgbi_skcb_flags flag)
+static inline int cxgbi_skcb_test_flag(const struct sk_buff *skb,
+ enum cxgbi_skcb_flags flag)
{
return test_bit(flag, &(cxgbi_skcb_flags(skb)));
}
@@ -358,7 +321,7 @@ static inline void __cxgbi_sock_put(const char *fn, struct cxgbi_sock *csk)
{
log_debug(1 << CXGBI_DBG_SOCK,
"%s, put csk 0x%p, ref %u-1.\n",
- fn, csk, atomic_read(&csk->refcnt.refcount));
+ fn, csk, kref_read(&csk->refcnt));
kref_put(&csk->refcnt, cxgbi_sock_free);
}
#define cxgbi_sock_put(csk) __cxgbi_sock_put(__func__, csk)
@@ -367,7 +330,7 @@ static inline void __cxgbi_sock_get(const char *fn, struct cxgbi_sock *csk)
{
log_debug(1 << CXGBI_DBG_SOCK,
"%s, get csk 0x%p, ref %u+1.\n",
- fn, csk, atomic_read(&csk->refcnt.refcount));
+ fn, csk, kref_read(&csk->refcnt));
kref_get(&csk->refcnt);
}
#define cxgbi_sock_get(csk) __cxgbi_sock_get(__func__, csk)
@@ -431,11 +394,9 @@ static inline void cxgbi_sock_enqueue_wr(struct cxgbi_sock *csk,
cxgbi_skcb_tx_wr_next(skb) = NULL;
/*
* We want to take an extra reference since both us and the driver
- * need to free the packet before it's really freed. We know there's
- * just one user currently so we use atomic_set rather than skb_get
- * to avoid the atomic op.
+ * need to free the packet before it's really freed.
*/
- atomic_set(&skb->users, 2);
+ skb_get(skb);
if (!csk->wr_pending_head)
csk->wr_pending_head = skb;
@@ -509,8 +470,13 @@ struct cxgbi_ports_map {
#define CXGBI_FLAG_DEV_T4 0x2
#define CXGBI_FLAG_ADAPTER_RESET 0x4
#define CXGBI_FLAG_IPV4_SET 0x10
+#define CXGBI_FLAG_USE_PPOD_OFLDQ 0x40
+#define CXGBI_FLAG_DDP_OFF 0x100
+#define CXGBI_FLAG_DEV_ISO_OFF 0x400
+
struct cxgbi_device {
struct list_head list_head;
+ struct list_head rcu_node;
unsigned int flags;
struct net_device **ports;
void *lldev;
@@ -519,34 +485,31 @@ struct cxgbi_device {
unsigned char nmtus;
unsigned char nports;
struct pci_dev *pdev;
- struct dentry *debugfs_root;
struct iscsi_transport *itp;
+ struct module *owner;
unsigned int pfvf;
- unsigned int snd_win;
- unsigned int rcv_win;
unsigned int rx_credit_thres;
unsigned int skb_tx_rsvd;
+ u32 skb_iso_txhdr;
unsigned int skb_rx_extra; /* for msg coalesced mode */
unsigned int tx_max_size;
unsigned int rx_max_size;
+ unsigned int rxq_idx_cntr;
struct cxgbi_ports_map pmap;
- struct cxgbi_tag_format tag_format;
- struct cxgbi_ddp_info *ddp;
-
- void (*dev_ddp_cleanup)(struct cxgbi_device *);
- int (*csk_ddp_set)(struct cxgbi_sock *, struct cxgbi_pagepod_hdr *,
- unsigned int, unsigned int,
- struct cxgbi_gather_list *);
- void (*csk_ddp_clear)(struct cxgbi_hba *,
- unsigned int, unsigned int, unsigned int);
+
+ struct cxgbi_ppm* (*cdev2ppm)(struct cxgbi_device *);
+ int (*csk_ddp_set_map)(struct cxgbi_ppm *, struct cxgbi_sock *,
+ struct cxgbi_task_tag_info *);
+ void (*csk_ddp_clear_map)(struct cxgbi_device *cdev,
+ struct cxgbi_ppm *,
+ struct cxgbi_task_tag_info *);
int (*csk_ddp_setup_digest)(struct cxgbi_sock *,
- unsigned int, int, int, int);
+ unsigned int, int, int);
int (*csk_ddp_setup_pgidx)(struct cxgbi_sock *,
- unsigned int, int, bool);
+ unsigned int, int);
void (*csk_release_offload_resources)(struct cxgbi_sock *);
- int (*csk_rx_pdu_ready)(struct cxgbi_sock *, struct sk_buff *);
u32 (*csk_send_rx_credits)(struct cxgbi_sock *, u32);
int (*csk_push_tx_frames)(struct cxgbi_sock *, int);
void (*csk_send_abort_req)(struct cxgbi_sock *);
@@ -563,6 +526,8 @@ struct cxgbi_conn {
struct iscsi_conn *iconn;
struct cxgbi_hba *chba;
u32 task_idx_bits;
+ unsigned int ddp_full;
+ unsigned int ddp_tag_full;
};
struct cxgbi_endpoint {
@@ -571,108 +536,40 @@ struct cxgbi_endpoint {
struct cxgbi_sock *csk;
};
-#define MAX_PDU_FRAGS ((ULP2_MAX_PDU_PAYLOAD + 512 - 1) / 512)
struct cxgbi_task_data {
+#define CXGBI_TASK_SGL_CHECKED 0x1
+#define CXGBI_TASK_SGL_COPY 0x2
+ u8 flags;
unsigned short nr_frags;
- skb_frag_t frags[MAX_PDU_FRAGS];
+ struct page_frag frags[MAX_SKB_FRAGS];
struct sk_buff *skb;
+ unsigned int dlen;
unsigned int offset;
unsigned int count;
unsigned int sgoffset;
+ u32 total_count;
+ u32 total_offset;
+ u32 max_xmit_dlength;
+ struct cxgbi_task_tag_info ttinfo;
};
#define iscsi_task_cxgbi_data(task) \
((task)->dd_data + sizeof(struct iscsi_tcp_task))
-static inline int cxgbi_is_ddp_tag(struct cxgbi_tag_format *tformat, u32 tag)
-{
- return !(tag & (1 << (tformat->rsvd_bits + tformat->rsvd_shift - 1)));
-}
-
-static inline int cxgbi_sw_tag_usable(struct cxgbi_tag_format *tformat,
- u32 sw_tag)
-{
- sw_tag >>= (32 - tformat->rsvd_bits);
- return !sw_tag;
-}
-
-static inline u32 cxgbi_set_non_ddp_tag(struct cxgbi_tag_format *tformat,
- u32 sw_tag)
-{
- unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
- u32 mask = (1 << shift) - 1;
-
- if (sw_tag && (sw_tag & ~mask)) {
- u32 v1 = sw_tag & ((1 << shift) - 1);
- u32 v2 = (sw_tag >> (shift - 1)) << shift;
-
- return v2 | v1 | 1 << shift;
- }
-
- return sw_tag | 1 << shift;
-}
-
-static inline u32 cxgbi_ddp_tag_base(struct cxgbi_tag_format *tformat,
- u32 sw_tag)
-{
- u32 mask = (1 << tformat->rsvd_shift) - 1;
-
- if (sw_tag && (sw_tag & ~mask)) {
- u32 v1 = sw_tag & mask;
- u32 v2 = sw_tag >> tformat->rsvd_shift;
-
- v2 <<= tformat->rsvd_bits + tformat->rsvd_shift;
-
- return v2 | v1;
- }
-
- return sw_tag;
-}
-
-static inline u32 cxgbi_tag_rsvd_bits(struct cxgbi_tag_format *tformat,
- u32 tag)
-{
- if (cxgbi_is_ddp_tag(tformat, tag))
- return (tag >> tformat->rsvd_shift) & tformat->rsvd_mask;
-
- return 0;
-}
-
-static inline u32 cxgbi_tag_nonrsvd_bits(struct cxgbi_tag_format *tformat,
- u32 tag)
-{
- unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
- u32 v1, v2;
-
- if (cxgbi_is_ddp_tag(tformat, tag)) {
- v1 = tag & ((1 << tformat->rsvd_shift) - 1);
- v2 = (tag >> (shift + 1)) << tformat->rsvd_shift;
- } else {
- u32 mask = (1 << shift) - 1;
- tag &= ~(1 << shift);
- v1 = tag & mask;
- v2 = (tag >> 1) & ~mask;
- }
- return v1 | v2;
-}
-
-static inline void *cxgbi_alloc_big_mem(unsigned int size,
- gfp_t gfp)
-{
- void *p = kmalloc(size, gfp);
- if (!p)
- p = vmalloc(size);
- if (p)
- memset(p, 0, size);
- return p;
-}
-
-static inline void cxgbi_free_big_mem(void *addr)
-{
- if (is_vmalloc_addr(addr))
- vfree(addr);
- else
- kfree(addr);
-}
+struct cxgbi_iso_info {
+#define CXGBI_ISO_INFO_FSLICE 0x1
+#define CXGBI_ISO_INFO_LSLICE 0x2
+#define CXGBI_ISO_INFO_IMM_ENABLE 0x4
+ u8 flags;
+ u8 op;
+ u8 ahs;
+ u8 num_pdu;
+ u32 mpdu;
+ u32 burst_size;
+ u32 len;
+ u32 segment_offset;
+ u32 datasn_offset;
+ u32 buffer_offset;
+};
static inline void cxgbi_set_iscsi_ipv4(struct cxgbi_hba *chba, __be32 ipaddr)
{
@@ -683,17 +580,15 @@ static inline void cxgbi_set_iscsi_ipv4(struct cxgbi_hba *chba, __be32 ipaddr)
chba->ndev->name);
}
-static inline __be32 cxgbi_get_iscsi_ipv4(struct cxgbi_hba *chba)
-{
- return chba->ipv4addr;
-}
-
struct cxgbi_device *cxgbi_device_register(unsigned int, unsigned int);
void cxgbi_device_unregister(struct cxgbi_device *);
void cxgbi_device_unregister_all(unsigned int flag);
struct cxgbi_device *cxgbi_device_find_by_lldev(void *);
-int cxgbi_hbas_add(struct cxgbi_device *, unsigned int, unsigned int,
- struct scsi_host_template *,
+struct cxgbi_device *cxgbi_device_find_by_netdev(struct net_device *, int *);
+struct cxgbi_device *cxgbi_device_find_by_netdev_rcu(struct net_device *,
+ int *);
+int cxgbi_hbas_add(struct cxgbi_device *, u64, unsigned int,
+ const struct scsi_host_template *,
struct scsi_transport_template *);
void cxgbi_hbas_remove(struct cxgbi_device *);
@@ -709,6 +604,7 @@ int cxgbi_conn_xmit_pdu(struct iscsi_task *);
void cxgbi_cleanup_task(struct iscsi_task *task);
+umode_t cxgbi_attr_is_visible(int param_type, int param);
void cxgbi_get_conn_stats(struct iscsi_cls_conn *, struct iscsi_stats *);
int cxgbi_set_conn_param(struct iscsi_cls_conn *,
enum iscsi_param, char *, int);
@@ -736,7 +632,12 @@ int cxgbi_ddp_init(struct cxgbi_device *, unsigned int, unsigned int,
unsigned int, unsigned int);
int cxgbi_ddp_cleanup(struct cxgbi_device *);
void cxgbi_ddp_page_size_factor(int *);
-void cxgbi_ddp_ppod_clear(struct cxgbi_pagepod *);
-void cxgbi_ddp_ppod_set(struct cxgbi_pagepod *, struct cxgbi_pagepod_hdr *,
- struct cxgbi_gather_list *, unsigned int);
+void cxgbi_ddp_set_one_ppod(struct cxgbi_pagepod *,
+ struct cxgbi_task_tag_info *,
+ struct scatterlist **sg_pp, unsigned int *sg_off);
+int cxgbi_ddp_ppm_setup(void **ppm_pp, struct cxgbi_device *cdev,
+ struct cxgbi_tag_format *tformat,
+ unsigned int iscsi_size, unsigned int llimit,
+ unsigned int start, unsigned int rsvd_factor,
+ unsigned int edram_start, unsigned int edram_size);
#endif /*__LIBCXGBI_H__*/
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index f5b718d3c31b..386c8359e1cc 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -61,10 +61,10 @@
#include <asm/io.h>
#include <scsi/scsi.h>
-#include <scsi/scsicam.h> /* needed for scsicam_bios_param */
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_spi.h>
#include "dc395x.h"
@@ -83,65 +83,6 @@
/*#define DC395x_NO_SYNC*/
/*#define DC395x_NO_WIDE*/
-/*---------------------------------------------------------------------------
- Debugging
- ---------------------------------------------------------------------------*/
-/*
- * Types of debugging that can be enabled and disabled
- */
-#define DBG_KG 0x0001
-#define DBG_0 0x0002
-#define DBG_1 0x0004
-#define DBG_SG 0x0020
-#define DBG_FIFO 0x0040
-#define DBG_PIO 0x0080
-
-
-/*
- * Set set of things to output debugging for.
- * Undefine to remove all debugging
- */
-/*#define DEBUG_MASK (DBG_0|DBG_1|DBG_SG|DBG_FIFO|DBG_PIO)*/
-/*#define DEBUG_MASK DBG_0*/
-
-
-/*
- * Output a kernel mesage at the specified level and append the
- * driver name and a ": " to the start of the message
- */
-#define dprintkl(level, format, arg...) \
- printk(level DC395X_NAME ": " format , ## arg)
-
-
-#ifdef DEBUG_MASK
-/*
- * print a debug message - this is formated with KERN_DEBUG, then the
- * driver name followed by a ": " and then the message is output.
- * This also checks that the specified debug level is enabled before
- * outputing the message
- */
-#define dprintkdbg(type, format, arg...) \
- do { \
- if ((type) & (DEBUG_MASK)) \
- dprintkl(KERN_DEBUG , format , ## arg); \
- } while (0)
-
-/*
- * Check if the specified type of debugging is enabled
- */
-#define debug_enabled(type) ((DEBUG_MASK) & (type))
-
-#else
-/*
- * No debugging. Do nothing
- */
-#define dprintkdbg(type, format, arg...) \
- do {} while (0)
-#define debug_enabled(type) (0)
-
-#endif
-
-
#ifndef PCI_VENDOR_ID_TEKRAM
#define PCI_VENDOR_ID_TEKRAM 0x1DE1 /* Vendor ID */
#endif
@@ -160,22 +101,6 @@
#define DC395x_write16(acb,address,value) outw((value), acb->io_port_base + (address))
#define DC395x_write32(acb,address,value) outl((value), acb->io_port_base + (address))
-/* cmd->result */
-#define RES_TARGET 0x000000FF /* Target State */
-#define RES_TARGET_LNX STATUS_MASK /* Only official ... */
-#define RES_ENDMSG 0x0000FF00 /* End Message */
-#define RES_DID 0x00FF0000 /* DID_ codes */
-#define RES_DRV 0xFF000000 /* DRIVER_ codes */
-
-#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt))
-#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)<<1)
-
-#define SET_RES_TARGET(who,tgt) { who &= ~RES_TARGET; who |= (int)(tgt); }
-#define SET_RES_TARGET_LNX(who,tgt) { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; }
-#define SET_RES_MSG(who,msg) { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; }
-#define SET_RES_DID(who,did) { who &= ~RES_DID; who |= (int)(did) << 16; }
-#define SET_RES_DRV(who,drv) { who &= ~RES_DRV; who |= (int)(drv) << 24; }
-
#define TAG_NONE 255
/*
@@ -308,6 +233,8 @@ struct AdapterCtlBlk {
struct timer_list waiting_timer;
struct timer_list selto_timer;
+ unsigned long last_reset;
+
u16 srb_count;
u8 sel_timeout;
@@ -393,7 +320,7 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
struct ScsiReqBlk *srb);
static void set_xfer_rate(struct AdapterCtlBlk *acb,
struct DeviceCtlBlk *dcb);
-static void waiting_timeout(unsigned long ptr);
+static void waiting_timeout(struct timer_list *t);
/*---------------------------------------------------------------------------
@@ -446,7 +373,6 @@ static void *dc395x_scsi_phase1[] = {
/* real period:48ns,76ns,100ns,124ns,148ns,176ns,200ns,248ns */
static u8 clock_period[] = { 12, 18, 25, 31, 37, 43, 50, 62 };
-static u16 clock_speed[] = { 200, 133, 100, 80, 67, 58, 50, 40 };
/*---------------------------------------------------------------------------
@@ -489,7 +415,7 @@ struct ParameterData {
int def; /* default value */
int safe; /* safe value */
};
-static struct ParameterData __devinitdata cfg_data[] = {
+static struct ParameterData cfg_data[] = {
{ /* adapter id */
CFG_PARAM_UNSET,
0,
@@ -517,9 +443,7 @@ static struct ParameterData __devinitdata cfg_data[] = {
CFG_PARAM_UNSET,
0,
0x2f,
-#ifdef CONFIG_SCSI_MULTI_LUN
- NAC_SCANLUN |
-#endif
+ NAC_SCANLUN |
NAC_GT2DRIVES | NAC_GREATER_1G | NAC_POWERON_SCSI_RESET
/*| NAC_ACTIVE_NEG*/,
NAC_GT2DRIVES | NAC_GREATER_1G | NAC_POWERON_SCSI_RESET | 0x08
@@ -546,7 +470,7 @@ static struct ParameterData __devinitdata cfg_data[] = {
* command line overrides will be used. If set to 1 then safe and
* slow settings will be used.
*/
-static int use_safe_settings = 0;
+static bool use_safe_settings = 0;
module_param_named(safe, use_safe_settings, bool, 0);
MODULE_PARM_DESC(safe, "Use safe and slow settings only. Default: false");
@@ -574,13 +498,12 @@ MODULE_PARM_DESC(reset_delay, "Reset delay in seconds. Default 1 (0-180)");
* set_safe_settings - if the use_safe_settings option is set then
* set all values to the safe and slow values.
**/
-static void __devinit set_safe_settings(void)
+static void set_safe_settings(void)
{
if (use_safe_settings)
{
int i;
- dprintkl(KERN_INFO, "Using safe settings.\n");
for (i = 0; i < CFG_NUM; i++)
{
cfg_data[i].value = cfg_data[i].safe;
@@ -593,19 +516,10 @@ static void __devinit set_safe_settings(void)
* fix_settings - reset any boot parameters which are out of range
* back to the default values.
**/
-static void __devinit fix_settings(void)
+static void fix_settings(void)
{
int i;
- dprintkdbg(DBG_1,
- "setup: AdapterId=%08x MaxSpeed=%08x DevMode=%08x "
- "AdapterMode=%08x Tags=%08x ResetDelay=%08x\n",
- cfg_data[CFG_ADAPTER_ID].value,
- cfg_data[CFG_MAX_SPEED].value,
- cfg_data[CFG_DEV_MODE].value,
- cfg_data[CFG_ADAPTER_MODE].value,
- cfg_data[CFG_TAGS].value,
- cfg_data[CFG_RESET_DELAY].value);
for (i = 0; i < CFG_NUM; i++)
{
if (cfg_data[i].value < cfg_data[i].min
@@ -620,7 +534,7 @@ static void __devinit fix_settings(void)
* Mapping from the eeprom delay index value (index into this array)
* to the number of actual seconds that the delay should be for.
*/
-static char __devinitdata eeprom_index_to_delay_map[] =
+static char eeprom_index_to_delay_map[] =
{ 1, 3, 5, 10, 16, 30, 60, 120 };
@@ -630,7 +544,7 @@ static char __devinitdata eeprom_index_to_delay_map[] =
*
* @eeprom: The eeprom structure in which we find the delay index to map.
**/
-static void __devinit eeprom_index_to_delay(struct NvRamType *eeprom)
+static void eeprom_index_to_delay(struct NvRamType *eeprom)
{
eeprom->delay_time = eeprom_index_to_delay_map[eeprom->delay_time];
}
@@ -643,7 +557,7 @@ static void __devinit eeprom_index_to_delay(struct NvRamType *eeprom)
*
* @delay: The delay, in seconds, to find the eeprom index for.
**/
-static int __devinit delay_to_eeprom_index(int delay)
+static int delay_to_eeprom_index(int delay)
{
u8 idx = 0;
while (idx < 7 && eeprom_index_to_delay_map[idx] < delay)
@@ -659,7 +573,7 @@ static int __devinit delay_to_eeprom_index(int delay)
*
* @eeprom: The eeprom data to override with command line options.
**/
-static void __devinit eeprom_override(struct NvRamType *eeprom)
+static void eeprom_override(struct NvRamType *eeprom)
{
u8 id;
@@ -753,116 +667,14 @@ static inline struct ScsiReqBlk *find_cmd(struct scsi_cmnd *cmd,
return NULL;
}
-
-static struct ScsiReqBlk *srb_get_free(struct AdapterCtlBlk *acb)
-{
- struct list_head *head = &acb->srb_free_list;
- struct ScsiReqBlk *srb = NULL;
-
- if (!list_empty(head)) {
- srb = list_entry(head->next, struct ScsiReqBlk, list);
- list_del(head->next);
- dprintkdbg(DBG_0, "srb_get_free: srb=%p\n", srb);
- }
- return srb;
-}
-
-
-static void srb_free_insert(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
-{
- dprintkdbg(DBG_0, "srb_free_insert: srb=%p\n", srb);
- list_add_tail(&srb->list, &acb->srb_free_list);
-}
-
-
-static void srb_waiting_insert(struct DeviceCtlBlk *dcb,
- struct ScsiReqBlk *srb)
-{
- dprintkdbg(DBG_0, "srb_waiting_insert: (0x%p) <%02i-%i> srb=%p\n",
- srb->cmd, dcb->target_id, dcb->target_lun, srb);
- list_add(&srb->list, &dcb->srb_waiting_list);
-}
-
-
-static void srb_waiting_append(struct DeviceCtlBlk *dcb,
- struct ScsiReqBlk *srb)
-{
- dprintkdbg(DBG_0, "srb_waiting_append: (0x%p) <%02i-%i> srb=%p\n",
- srb->cmd, dcb->target_id, dcb->target_lun, srb);
- list_add_tail(&srb->list, &dcb->srb_waiting_list);
-}
-
-
-static void srb_going_append(struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
-{
- dprintkdbg(DBG_0, "srb_going_append: (0x%p) <%02i-%i> srb=%p\n",
- srb->cmd, dcb->target_id, dcb->target_lun, srb);
- list_add_tail(&srb->list, &dcb->srb_going_list);
-}
-
-
-static void srb_going_remove(struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
-{
- struct ScsiReqBlk *i;
- struct ScsiReqBlk *tmp;
- dprintkdbg(DBG_0, "srb_going_remove: (0x%p) <%02i-%i> srb=%p\n",
- srb->cmd, dcb->target_id, dcb->target_lun, srb);
-
- list_for_each_entry_safe(i, tmp, &dcb->srb_going_list, list)
- if (i == srb) {
- list_del(&srb->list);
- break;
- }
-}
-
-
-static void srb_waiting_remove(struct DeviceCtlBlk *dcb,
- struct ScsiReqBlk *srb)
-{
- struct ScsiReqBlk *i;
- struct ScsiReqBlk *tmp;
- dprintkdbg(DBG_0, "srb_waiting_remove: (0x%p) <%02i-%i> srb=%p\n",
- srb->cmd, dcb->target_id, dcb->target_lun, srb);
-
- list_for_each_entry_safe(i, tmp, &dcb->srb_waiting_list, list)
- if (i == srb) {
- list_del(&srb->list);
- break;
- }
-}
-
-
-static void srb_going_to_waiting_move(struct DeviceCtlBlk *dcb,
- struct ScsiReqBlk *srb)
-{
- dprintkdbg(DBG_0,
- "srb_going_to_waiting_move: (0x%p) <%02i-%i> srb=%p\n",
- srb->cmd, dcb->target_id, dcb->target_lun, srb);
- list_move(&srb->list, &dcb->srb_waiting_list);
-}
-
-
-static void srb_waiting_to_going_move(struct DeviceCtlBlk *dcb,
- struct ScsiReqBlk *srb)
-{
- dprintkdbg(DBG_0,
- "srb_waiting_to_going_move: (0x%p) <%02i-%i> srb=%p\n",
- srb->cmd, dcb->target_id, dcb->target_lun, srb);
- list_move(&srb->list, &dcb->srb_going_list);
-}
-
-
/* Sets the timer to wake us up */
static void waiting_set_timer(struct AdapterCtlBlk *acb, unsigned long to)
{
if (timer_pending(&acb->waiting_timer))
return;
- init_timer(&acb->waiting_timer);
- acb->waiting_timer.function = waiting_timeout;
- acb->waiting_timer.data = (unsigned long) acb;
- if (time_before(jiffies + to, acb->scsi_host->last_reset - HZ / 2))
+ if (time_before(jiffies + to, acb->last_reset - HZ / 2))
acb->waiting_timer.expires =
- acb->scsi_host->last_reset - HZ / 2 + 1;
+ acb->last_reset - HZ / 2 + 1;
else
acb->waiting_timer.expires = jiffies + to + 1;
add_timer(&acb->waiting_timer);
@@ -883,7 +695,7 @@ static void waiting_process_next(struct AdapterCtlBlk *acb)
return;
if (timer_pending(&acb->waiting_timer))
- del_timer(&acb->waiting_timer);
+ timer_delete(&acb->waiting_timer);
if (list_empty(dcb_list_head))
return;
@@ -926,7 +738,7 @@ static void waiting_process_next(struct AdapterCtlBlk *acb)
/* Try to send to the bus */
if (!start_scsi(acb, pos, srb))
- srb_waiting_to_going_move(pos, srb);
+ list_move(&srb->list, &pos->srb_going_list);
else
waiting_set_timer(acb, HZ/50);
break;
@@ -936,12 +748,10 @@ static void waiting_process_next(struct AdapterCtlBlk *acb)
/* Wake up waiting queue */
-static void waiting_timeout(unsigned long ptr)
+static void waiting_timeout(struct timer_list *t)
{
unsigned long flags;
- struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)ptr;
- dprintkdbg(DBG_1,
- "waiting_timeout: Queue woken up by timer. acb=%p\n", acb);
+ struct AdapterCtlBlk *acb = timer_container_of(acb, t, waiting_timer);
DC395x_LOCK_IO(acb->scsi_host, flags);
waiting_process_next(acb);
DC395x_UNLOCK_IO(acb->scsi_host, flags);
@@ -963,15 +773,15 @@ static void send_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
if (dcb->max_command <= list_size(&dcb->srb_going_list) ||
acb->active_dcb ||
(acb->acb_flag & (RESET_DETECT + RESET_DONE + RESET_DEV))) {
- srb_waiting_append(dcb, srb);
+ list_add_tail(&srb->list, &dcb->srb_waiting_list);
waiting_process_next(acb);
return;
}
- if (!start_scsi(acb, dcb, srb))
- srb_going_append(dcb, srb);
- else {
- srb_waiting_insert(dcb, srb);
+ if (!start_scsi(acb, dcb, srb)) {
+ list_add_tail(&srb->list, &dcb->srb_going_list);
+ } else {
+ list_add(&srb->list, &dcb->srb_waiting_list);
waiting_set_timer(acb, HZ / 50);
}
}
@@ -982,8 +792,6 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
{
int nseg;
enum dma_data_direction dir = cmd->sc_data_direction;
- dprintkdbg(DBG_0, "build_srb: (0x%p) <%02i-%i>\n",
- cmd, dcb->target_id, dcb->target_lun);
srb->dcb = dcb;
srb->cmd = cmd;
@@ -1005,12 +813,7 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
nseg = scsi_dma_map(cmd);
BUG_ON(nseg < 0);
- if (dir == PCI_DMA_NONE || !nseg) {
- dprintkdbg(DBG_0,
- "build_srb: [0] len=%d buf=%p use_sg=%d !MAP=%08x\n",
- cmd->bufflen, scsi_sglist(cmd), scsi_sg_count(cmd),
- srb->segment_x[0].address);
- } else {
+ if (!(dir == DMA_NONE || !nseg)) {
int i;
u32 reqlen = scsi_bufflen(cmd);
struct scatterlist *sg;
@@ -1018,11 +821,6 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
srb->sg_count = nseg;
- dprintkdbg(DBG_0,
- "build_srb: [n] len=%d buf=%p use_sg=%d segs=%d\n",
- reqlen, scsi_sglist(cmd), scsi_sg_count(cmd),
- srb->sg_count);
-
scsi_for_each_sg(cmd, sg, srb->sg_count, i) {
u32 busaddr = (u32)sg_dma_address(sg);
u32 seglen = (u32)sg->length;
@@ -1048,13 +846,9 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
sgp->length++;
}
- srb->sg_bus_addr = pci_map_single(dcb->acb->dev,
- srb->segment_x,
- SEGMENTX_LEN,
- PCI_DMA_TODEVICE);
+ srb->sg_bus_addr = dma_map_single(&dcb->acb->dev->dev,
+ srb->segment_x, SEGMENTX_LEN, DMA_TO_DEVICE);
- dprintkdbg(DBG_SG, "build_srb: [n] map sg %p->%08x(%05x)\n",
- srb->segment_x, srb->sg_bus_addr, SEGMENTX_LEN);
}
srb->request_length = srb->total_xfer_length;
@@ -1062,11 +856,10 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
/**
- * dc395x_queue_command - queue scsi command passed from the mid
+ * dc395x_queue_command_lck - queue scsi command passed from the mid
* layer, invoke 'done' on completion
*
* @cmd: pointer to scsi command object
- * @done: function pointer to be invoked on completion
*
* Returns 1 if the adapter (host) is busy, else returns 0. One
* reason for an adapter to be busy is that the number
@@ -1079,68 +872,55 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
* Locks: struct Scsi_Host::host_lock held on entry (with "irqsave")
* and is expected to be held on return.
*
- **/
-static int dc395x_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+ */
+static int dc395x_queue_command_lck(struct scsi_cmnd *cmd)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
struct DeviceCtlBlk *dcb;
struct ScsiReqBlk *srb;
struct AdapterCtlBlk *acb =
(struct AdapterCtlBlk *)cmd->device->host->hostdata;
- dprintkdbg(DBG_0, "queue_command: (0x%p) <%02i-%i> cmnd=0x%02x\n",
- cmd, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
/* Assume BAD_TARGET; will be cleared later */
- cmd->result = DID_BAD_TARGET << 16;
+ set_host_byte(cmd, DID_BAD_TARGET);
/* ignore invalid targets */
if (cmd->device->id >= acb->scsi_host->max_id ||
cmd->device->lun >= acb->scsi_host->max_lun ||
- cmd->device->lun >31) {
+ cmd->device->lun > 31)
goto complete;
- }
/* does the specified lun on the specified device exist */
- if (!(acb->dcb_map[cmd->device->id] & (1 << cmd->device->lun))) {
- dprintkl(KERN_INFO, "queue_command: Ignore target <%02i-%i>\n",
- cmd->device->id, cmd->device->lun);
+ if (!(acb->dcb_map[cmd->device->id] & (1 << cmd->device->lun)))
goto complete;
- }
/* do we have a DCB for the device */
dcb = find_dcb(acb, cmd->device->id, cmd->device->lun);
- if (!dcb) {
- /* should never happen */
- dprintkl(KERN_ERR, "queue_command: No such device <%02i-%i>",
- cmd->device->id, cmd->device->lun);
+ if (!dcb)
goto complete;
- }
- /* set callback and clear result in the command */
- cmd->scsi_done = done;
- cmd->result = 0;
+ set_host_byte(cmd, DID_OK);
+ set_status_byte(cmd, SAM_STAT_GOOD);
- srb = srb_get_free(acb);
- if (!srb)
- {
- /*
- * Return 1 since we are unable to queue this command at this
- * point in time.
- */
- dprintkdbg(DBG_0, "queue_command: No free srb's\n");
+ srb = list_first_entry_or_null(&acb->srb_free_list,
+ struct ScsiReqBlk, list);
+
+ if (!srb) {
+ /* should never happen */
return 1;
}
+ list_del(&srb->list);
build_srb(cmd, dcb, srb);
if (!list_empty(&dcb->srb_waiting_list)) {
/* append to waiting queue */
- srb_waiting_append(dcb, srb);
+ list_add_tail(&srb->list, &dcb->srb_waiting_list);
waiting_process_next(acb);
} else {
/* process immediately */
send_srb(acb, srb);
}
- dprintkdbg(DBG_1, "queue_command: (0x%p) done\n", cmd);
return 0;
complete:
@@ -1156,114 +936,8 @@ complete:
static DEF_SCSI_QCMD(dc395x_queue_command)
-/*
- * Return the disk geometry for the given SCSI device.
- */
-static int dc395x_bios_param(struct scsi_device *sdev,
- struct block_device *bdev, sector_t capacity, int *info)
-{
-#ifdef CONFIG_SCSI_DC395x_TRMS1040_TRADMAP
- int heads, sectors, cylinders;
- struct AdapterCtlBlk *acb;
- int size = capacity;
-
- dprintkdbg(DBG_0, "dc395x_bios_param..............\n");
- acb = (struct AdapterCtlBlk *)sdev->host->hostdata;
- heads = 64;
- sectors = 32;
- cylinders = size / (heads * sectors);
-
- if ((acb->gmode2 & NAC_GREATER_1G) && (cylinders > 1024)) {
- heads = 255;
- sectors = 63;
- cylinders = size / (heads * sectors);
- }
- geom[0] = heads;
- geom[1] = sectors;
- geom[2] = cylinders;
- return 0;
-#else
- return scsicam_bios_param(bdev, capacity, info);
-#endif
-}
-
-
-static void dump_register_info(struct AdapterCtlBlk *acb,
- struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
-{
- u16 pstat;
- struct pci_dev *dev = acb->dev;
- pci_read_config_word(dev, PCI_STATUS, &pstat);
- if (!dcb)
- dcb = acb->active_dcb;
- if (!srb && dcb)
- srb = dcb->active_srb;
- if (srb) {
- if (!srb->cmd)
- dprintkl(KERN_INFO, "dump: srb=%p cmd=%p OOOPS!\n",
- srb, srb->cmd);
- else
- dprintkl(KERN_INFO, "dump: srb=%p cmd=%p "
- "cmnd=0x%02x <%02i-%i>\n",
- srb, srb->cmd,
- srb->cmd->cmnd[0], srb->cmd->device->id,
- srb->cmd->device->lun);
- printk(" sglist=%p cnt=%i idx=%i len=%zu\n",
- srb->segment_x, srb->sg_count, srb->sg_index,
- srb->total_xfer_length);
- printk(" state=0x%04x status=0x%02x phase=0x%02x (%sconn.)\n",
- srb->state, srb->status, srb->scsi_phase,
- (acb->active_dcb) ? "" : "not");
- }
- dprintkl(KERN_INFO, "dump: SCSI{status=0x%04x fifocnt=0x%02x "
- "signals=0x%02x irqstat=0x%02x sync=0x%02x target=0x%02x "
- "rselid=0x%02x ctr=0x%08x irqen=0x%02x config=0x%04x "
- "config2=0x%02x cmd=0x%02x selto=0x%02x}\n",
- DC395x_read16(acb, TRM_S1040_SCSI_STATUS),
- DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
- DC395x_read8(acb, TRM_S1040_SCSI_SIGNAL),
- DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS),
- DC395x_read8(acb, TRM_S1040_SCSI_SYNC),
- DC395x_read8(acb, TRM_S1040_SCSI_TARGETID),
- DC395x_read8(acb, TRM_S1040_SCSI_IDMSG),
- DC395x_read32(acb, TRM_S1040_SCSI_COUNTER),
- DC395x_read8(acb, TRM_S1040_SCSI_INTEN),
- DC395x_read16(acb, TRM_S1040_SCSI_CONFIG0),
- DC395x_read8(acb, TRM_S1040_SCSI_CONFIG2),
- DC395x_read8(acb, TRM_S1040_SCSI_COMMAND),
- DC395x_read8(acb, TRM_S1040_SCSI_TIMEOUT));
- dprintkl(KERN_INFO, "dump: DMA{cmd=0x%04x fifocnt=0x%02x fstat=0x%02x "
- "irqstat=0x%02x irqen=0x%02x cfg=0x%04x tctr=0x%08x "
- "ctctr=0x%08x addr=0x%08x:0x%08x}\n",
- DC395x_read16(acb, TRM_S1040_DMA_COMMAND),
- DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
- DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
- DC395x_read8(acb, TRM_S1040_DMA_STATUS),
- DC395x_read8(acb, TRM_S1040_DMA_INTEN),
- DC395x_read16(acb, TRM_S1040_DMA_CONFIG),
- DC395x_read32(acb, TRM_S1040_DMA_XCNT),
- DC395x_read32(acb, TRM_S1040_DMA_CXCNT),
- DC395x_read32(acb, TRM_S1040_DMA_XHIGHADDR),
- DC395x_read32(acb, TRM_S1040_DMA_XLOWADDR));
- dprintkl(KERN_INFO, "dump: gen{gctrl=0x%02x gstat=0x%02x gtmr=0x%02x} "
- "pci{status=0x%04x}\n",
- DC395x_read8(acb, TRM_S1040_GEN_CONTROL),
- DC395x_read8(acb, TRM_S1040_GEN_STATUS),
- DC395x_read8(acb, TRM_S1040_GEN_TIMER),
- pstat);
-}
-
-
static inline void clear_fifo(struct AdapterCtlBlk *acb, char *txt)
{
-#if debug_enabled(DBG_FIFO)
- u8 lines = DC395x_read8(acb, TRM_S1040_SCSI_SIGNAL);
- u8 fifocnt = DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT);
- if (!(fifocnt & 0x40))
- dprintkdbg(DBG_FIFO,
- "clear_fifo: (%i bytes) on phase %02x in %s\n",
- fifocnt & 0x3f, lines, txt);
-#endif
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);
}
@@ -1272,7 +946,6 @@ static void reset_dev_param(struct AdapterCtlBlk *acb)
{
struct DeviceCtlBlk *dcb;
struct NvRamType *eeprom = &acb->eeprom;
- dprintkdbg(DBG_0, "reset_dev_param: acb=%p\n", acb);
list_for_each_entry(dcb, &acb->dcb_list, list) {
u8 period_index;
@@ -1300,12 +973,9 @@ static int __dc395x_eh_bus_reset(struct scsi_cmnd *cmd)
{
struct AdapterCtlBlk *acb =
(struct AdapterCtlBlk *)cmd->device->host->hostdata;
- dprintkl(KERN_INFO,
- "eh_bus_reset: (0%p) target=<%02i-%i> cmd=%p\n",
- cmd, cmd->device->id, cmd->device->lun, cmd);
if (timer_pending(&acb->waiting_timer))
- del_timer(&acb->waiting_timer);
+ timer_delete(&acb->waiting_timer);
/*
* disable interrupt
@@ -1319,7 +989,7 @@ static int __dc395x_eh_bus_reset(struct scsi_cmnd *cmd)
udelay(500);
/* We may be in serious trouble. Wait some seconds */
- acb->scsi_host->last_reset =
+ acb->last_reset =
jiffies + 3 * HZ / 2 +
HZ * acb->eeprom.delay_time;
@@ -1368,32 +1038,24 @@ static int dc395x_eh_abort(struct scsi_cmnd *cmd)
(struct AdapterCtlBlk *)cmd->device->host->hostdata;
struct DeviceCtlBlk *dcb;
struct ScsiReqBlk *srb;
- dprintkl(KERN_INFO, "eh_abort: (0x%p) target=<%02i-%i> cmd=%p\n",
- cmd, cmd->device->id, cmd->device->lun, cmd);
dcb = find_dcb(acb, cmd->device->id, cmd->device->lun);
- if (!dcb) {
- dprintkl(KERN_DEBUG, "eh_abort: No such device\n");
+ if (!dcb)
return FAILED;
- }
srb = find_cmd(cmd, &dcb->srb_waiting_list);
if (srb) {
- srb_waiting_remove(dcb, srb);
+ list_del(&srb->list);
pci_unmap_srb_sense(acb, srb);
pci_unmap_srb(acb, srb);
free_tag(dcb, srb);
- srb_free_insert(acb, srb);
- dprintkl(KERN_DEBUG, "eh_abort: Command was waiting\n");
- cmd->result = DID_ABORT << 16;
+ list_add_tail(&srb->list, &acb->srb_free_list);
+ set_host_byte(cmd, DID_ABORT);
return SUCCESS;
}
srb = find_cmd(cmd, &dcb->srb_going_list);
if (srb) {
- dprintkl(KERN_DEBUG, "eh_abort: Command in progress\n");
/* XXX: Should abort the command here */
- } else {
- dprintkl(KERN_DEBUG, "eh_abort: Command not found\n");
}
return FAILED;
}
@@ -1405,10 +1067,6 @@ static void build_sdtr(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
{
u8 *ptr = srb->msgout_buf + srb->msg_count;
if (srb->msg_count > 1) {
- dprintkl(KERN_INFO,
- "build_sdtr: msgout_buf BUSY (%i: %02x %02x)\n",
- srb->msg_count, srb->msgout_buf[0],
- srb->msgout_buf[1]);
return;
}
if (!(dcb->dev_mode & NTC_DO_SYNC_NEGO)) {
@@ -1417,12 +1075,8 @@ static void build_sdtr(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
} else if (dcb->sync_offset == 0)
dcb->sync_offset = SYNC_NEGO_OFFSET;
- *ptr++ = MSG_EXTENDED; /* (01h) */
- *ptr++ = 3; /* length */
- *ptr++ = EXTENDED_SDTR; /* (01h) */
- *ptr++ = dcb->min_nego_period; /* Transfer period (in 4ns) */
- *ptr++ = dcb->sync_offset; /* Transfer period (max. REQ/ACK dist) */
- srb->msg_count += 5;
+ srb->msg_count += spi_populate_sync_msg(ptr, dcb->min_nego_period,
+ dcb->sync_offset);
srb->state |= SRB_DO_SYNC_NEGO;
}
@@ -1434,18 +1088,10 @@ static void build_wdtr(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
u8 wide = ((dcb->dev_mode & NTC_DO_WIDE_NEGO) &
(acb->config & HCC_WIDE_CARD)) ? 1 : 0;
u8 *ptr = srb->msgout_buf + srb->msg_count;
- if (srb->msg_count > 1) {
- dprintkl(KERN_INFO,
- "build_wdtr: msgout_buf BUSY (%i: %02x %02x)\n",
- srb->msg_count, srb->msgout_buf[0],
- srb->msgout_buf[1]);
+ if (srb->msg_count > 1)
return;
- }
- *ptr++ = MSG_EXTENDED; /* (01h) */
- *ptr++ = 2; /* length */
- *ptr++ = EXTENDED_WDTR; /* (03h) */
- *ptr++ = wide;
- srb->msg_count += 4;
+
+ srb->msg_count += spi_populate_width_msg(ptr, wide);
srb->state |= SRB_DO_WIDE_NEGO;
}
@@ -1462,9 +1108,9 @@ static void selto_timer(struct AdapterCtlBlk *acb)
acb->selto_timer.function = selection_timeout_missed;
acb->selto_timer.data = (unsigned long) acb;
if (time_before
- (jiffies + HZ, acb->scsi_host->last_reset + HZ / 2))
+ (jiffies + HZ, acb->last_reset + HZ / 2))
acb->selto_timer.expires =
- acb->scsi_host->last_reset + HZ / 2 + 1;
+ acb->last_reset + HZ / 2 + 1;
else
acb->selto_timer.expires = jiffies + HZ + 1;
add_timer(&acb->selto_timer);
@@ -1476,11 +1122,9 @@ void selection_timeout_missed(unsigned long ptr)
unsigned long flags;
struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)ptr;
struct ScsiReqBlk *srb;
- dprintkl(KERN_DEBUG, "Chip forgot to produce SelTO IRQ!\n");
- if (!acb->active_dcb || !acb->active_dcb->active_srb) {
- dprintkl(KERN_DEBUG, "... but no cmd pending? Oops!\n");
+ if (!acb->active_dcb || !acb->active_dcb->active_srb)
return;
- }
+
DC395x_LOCK_IO(acb->scsi_host, flags);
srb = acb->active_dcb->active_srb;
disconnect(acb);
@@ -1492,11 +1136,9 @@ void selection_timeout_missed(unsigned long ptr)
static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
struct ScsiReqBlk* srb)
{
- u16 s_stat2, return_code;
+ u16 __maybe_unused s_stat2, return_code;
u8 s_stat, scsicommand, i, identify_message;
u8 *ptr;
- dprintkdbg(DBG_0, "start_scsi: (0x%p) <%02i-%i> srb=%p\n",
- dcb->target_id, dcb->target_lun, srb);
srb->tag_number = TAG_NONE; /* acb->tag_max_num: had error read in eeprom */
@@ -1505,8 +1147,6 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
s_stat2 = DC395x_read16(acb, TRM_S1040_SCSI_STATUS);
#if 1
if (s_stat & 0x20 /* s_stat2 & 0x02000 */ ) {
- dprintkdbg(DBG_KG, "start_scsi: (0x%p) BUSY %02x %04x\n",
- s_stat, s_stat2);
/*
* Try anyway?
*
@@ -1521,24 +1161,16 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
return 1;
}
#endif
- if (acb->active_dcb) {
- dprintkl(KERN_DEBUG, "start_scsi: (0x%p) Attempt to start a"
- "command while another command (0x%p) is active.",
- srb->cmd,
- acb->active_dcb->active_srb ?
- acb->active_dcb->active_srb->cmd : 0);
+ if (acb->active_dcb)
return 1;
- }
- if (DC395x_read16(acb, TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) {
- dprintkdbg(DBG_KG, "start_scsi: (0x%p) Failed (busy)\n", srb->cmd);
+
+ if (DC395x_read16(acb, TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT)
return 1;
- }
+
/* Allow starting of SCSI commands half a second before we allow the mid-level
* to queue them again after a reset */
- if (time_before(jiffies, acb->scsi_host->last_reset - HZ / 2)) {
- dprintkdbg(DBG_KG, "start_scsi: Refuse cmds (reset wait)\n");
+ if (time_before(jiffies, acb->last_reset - HZ / 2))
return 1;
- }
/* Flush FIFO */
clear_fifo(acb, "start_scsi");
@@ -1602,17 +1234,13 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
tag_number++;
}
if (tag_number >= dcb->max_command) {
- dprintkl(KERN_WARNING, "start_scsi: (0x%p) "
- "Out of tags target=<%02i-%i>)\n",
- srb->cmd, srb->cmd->device->id,
- srb->cmd->device->lun);
srb->state = SRB_READY;
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL,
DO_HWRESELECT);
return 1;
}
/* Send Tag id */
- DC395x_write8(acb, TRM_S1040_SCSI_FIFO, MSG_SIMPLE_QTAG);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, SIMPLE_QUEUE_TAG);
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, tag_number);
dcb->tag_mask |= tag_mask;
srb->tag_number = tag_number;
@@ -1622,9 +1250,6 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
#endif
/*polling:*/
/* Send CDB ..command block ......... */
- dprintkdbg(DBG_KG, "start_scsi: (0x%p) <%02i-%i> cmnd=0x%02x tag=%i\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun,
- srb->cmd->cmnd[0], srb->tag_number);
if (srb->flag & AUTO_REQSENSE) {
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, REQUEST_SENSE);
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, (dcb->target_lun << 5));
@@ -1646,8 +1271,6 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
* we caught an interrupt (must be reset or reselection ... )
* : Let's process it first!
*/
- dprintkdbg(DBG_0, "start_scsi: (0x%p) <%02i-%i> Failed - busy\n",
- srb->cmd, dcb->target_id, dcb->target_lun);
srb->state = SRB_READY;
free_tag(dcb, srb);
srb->msg_count = 0;
@@ -1711,17 +1334,9 @@ static void dc395x_handle_interrupt(struct AdapterCtlBlk *acb,
/* This acknowledges the IRQ */
scsi_intstatus = DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS);
- if ((scsi_status & 0x2007) == 0x2002)
- dprintkl(KERN_DEBUG,
- "COP after COP completed? %04x\n", scsi_status);
- if (debug_enabled(DBG_KG)) {
- if (scsi_intstatus & INT_SELTIMEOUT)
- dprintkdbg(DBG_KG, "handle_interrupt: Selection timeout\n");
- }
- /*dprintkl(KERN_DEBUG, "handle_interrupt: intstatus = 0x%02x ", scsi_intstatus); */
if (timer_pending(&acb->selto_timer))
- del_timer(&acb->selto_timer);
+ timer_delete(&acb->selto_timer);
if (scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) {
disconnect(acb); /* bus free interrupt */
@@ -1731,27 +1346,21 @@ static void dc395x_handle_interrupt(struct AdapterCtlBlk *acb,
reselect(acb);
goto out_unlock;
}
- if (scsi_intstatus & INT_SELECT) {
- dprintkl(KERN_INFO, "Host does not support target mode!\n");
+ if (scsi_intstatus & INT_SELECT)
goto out_unlock;
- }
+
if (scsi_intstatus & INT_SCSIRESET) {
scsi_reset_detect(acb);
goto out_unlock;
}
if (scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) {
dcb = acb->active_dcb;
- if (!dcb) {
- dprintkl(KERN_DEBUG,
- "Oops: BusService (%04x %02x) w/o ActiveDCB!\n",
- scsi_status, scsi_intstatus);
+ if (!dcb)
goto out_unlock;
- }
+
srb = dcb->active_srb;
- if (dcb->flag & ABORT_DEV_) {
- dprintkdbg(DBG_0, "MsgOut Abort Device.....\n");
+ if (dcb->flag & ABORT_DEV_)
enable_msgout_abort(acb, srb);
- }
/* software sequential machine */
phase = (u16)srb->scsi_phase;
@@ -1819,9 +1428,7 @@ static irqreturn_t dc395x_interrupt(int irq, void *dev_id)
}
else if (dma_status & 0x20) {
/* Error from the DMA engine */
- dprintkl(KERN_INFO, "Interrupt from DMA engine: 0x%02x!\n", dma_status);
#if 0
- dprintkl(KERN_INFO, "This means DMA error! Try to handle ...\n");
if (acb->active_dcb) {
acb->active_dcb-> flag |= ABORT_DEV_;
if (acb->active_dcb->active_srb)
@@ -1829,7 +1436,6 @@ static irqreturn_t dc395x_interrupt(int irq, void *dev_id)
}
DC395x_write8(acb, TRM_S1040_DMA_CONTROL, ABORTXFER | CLRXFIFO);
#else
- dprintkl(KERN_INFO, "Ignoring DMA error (probably a bad thing) ...\n");
acb = NULL;
#endif
handled = IRQ_HANDLED;
@@ -1842,7 +1448,6 @@ static irqreturn_t dc395x_interrupt(int irq, void *dev_id)
static void msgout_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "msgout_phase0: (0x%p)\n", srb->cmd);
if (srb->state & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT))
*pscsi_status = PH_BUS_FREE; /*.. initial phase */
@@ -1856,20 +1461,15 @@ static void msgout_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
{
u16 i;
u8 *ptr;
- dprintkdbg(DBG_0, "msgout_phase1: (0x%p)\n", srb->cmd);
clear_fifo(acb, "msgout_phase1");
- if (!(srb->state & SRB_MSGOUT)) {
+ if (!(srb->state & SRB_MSGOUT))
srb->state |= SRB_MSGOUT;
- dprintkl(KERN_DEBUG,
- "msgout_phase1: (0x%p) Phase unexpected\n",
- srb->cmd); /* So what ? */
- }
+
if (!srb->msg_count) {
- dprintkdbg(DBG_0, "msgout_phase1: (0x%p) NOP msg\n",
- srb->cmd);
- DC395x_write8(acb, TRM_S1040_SCSI_FIFO, MSG_NOP);
- DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, NOP);
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
+ /* it's important for atn stop */
DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
return;
}
@@ -1877,7 +1477,7 @@ static void msgout_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
for (i = 0; i < srb->msg_count; i++)
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, *ptr++);
srb->msg_count = 0;
- if (srb->msgout_buf[0] == MSG_ABORT)
+ if (srb->msgout_buf[0] == ABORT_TASK_SET)
srb->state = SRB_ABORT_SENT;
DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
@@ -1887,7 +1487,6 @@ static void msgout_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void command_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "command_phase0: (0x%p)\n", srb->cmd);
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
}
@@ -1898,7 +1497,6 @@ static void command_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
struct DeviceCtlBlk *dcb;
u8 *ptr;
u16 i;
- dprintkdbg(DBG_0, "command_phase1: (0x%p)\n", srb->cmd);
clear_fifo(acb, "command_phase1");
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_CLRATN);
@@ -1927,26 +1525,6 @@ static void command_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
/*
- * Verify that the remaining space in the hw sg lists is the same as
- * the count of remaining bytes in srb->total_xfer_length
- */
-static void sg_verify_length(struct ScsiReqBlk *srb)
-{
- if (debug_enabled(DBG_SG)) {
- unsigned len = 0;
- unsigned idx = srb->sg_index;
- struct SGentry *psge = srb->segment_x + idx;
- for (; idx < srb->sg_count; psge++, idx++)
- len += psge->length;
- if (len != srb->total_xfer_length)
- dprintkdbg(DBG_SG,
- "Inconsistent SRB S/G lengths (Tot=%i, Count=%i) !!\n",
- srb->total_xfer_length, len);
- }
-}
-
-
-/*
* Compute the next Scatter Gather list index and adjust its length
* and address if necessary
*/
@@ -1956,15 +1534,11 @@ static void sg_update_list(struct ScsiReqBlk *srb, u32 left)
u32 xferred = srb->total_xfer_length - left; /* bytes transferred */
struct SGentry *psge = srb->segment_x + srb->sg_index;
- dprintkdbg(DBG_0,
- "sg_update_list: Transferred %i of %i bytes, %i remain\n",
- xferred, srb->total_xfer_length, left);
if (xferred == 0) {
/* nothing to update since we did not transfer any data */
return;
}
- sg_verify_length(srb);
srb->total_xfer_length = left; /* update remaining count */
for (idx = srb->sg_index; idx < srb->sg_count; idx++) {
if (xferred >= psge->length) {
@@ -1972,19 +1546,19 @@ static void sg_update_list(struct ScsiReqBlk *srb, u32 left)
xferred -= psge->length;
} else {
/* Partial SG entry done */
+ dma_sync_single_for_cpu(&srb->dcb->acb->dev->dev,
+ srb->sg_bus_addr, SEGMENTX_LEN,
+ DMA_TO_DEVICE);
psge->length -= xferred;
psge->address += xferred;
srb->sg_index = idx;
- pci_dma_sync_single_for_device(srb->dcb->
- acb->dev,
- srb->sg_bus_addr,
- SEGMENTX_LEN,
- PCI_DMA_TODEVICE);
+ dma_sync_single_for_device(&srb->dcb->acb->dev->dev,
+ srb->sg_bus_addr, SEGMENTX_LEN,
+ DMA_TO_DEVICE);
break;
}
psge++;
}
- sg_verify_length(srb);
}
@@ -2040,8 +1614,6 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
struct DeviceCtlBlk *dcb = srb->dcb;
u16 scsi_status = *pscsi_status;
u32 d_left_counter = 0;
- dprintkdbg(DBG_0, "data_out_phase0: (0x%p) <%02i-%i>\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
/*
* KG: We need to drain the buffers before we draw any conclusions!
@@ -2055,14 +1627,6 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
* KG: Stop DMA engine pushing more data into the SCSI FIFO
* If we need more data, the DMA SG list will be freshly set up, anyway
*/
- dprintkdbg(DBG_PIO, "data_out_phase0: "
- "DMA{fifocnt=0x%02x fifostat=0x%02x} "
- "SCSI{fifocnt=0x%02x cnt=0x%06x status=0x%04x} total=0x%06x\n",
- DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
- DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
- DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
- DC395x_read32(acb, TRM_S1040_SCSI_COUNTER), scsi_status,
- srb->total_xfer_length);
DC395x_write8(acb, TRM_S1040_DMA_CONTROL, STOPDMAXFER | CLRXFIFO);
if (!(srb->state & SRB_XFERPAD)) {
@@ -2086,16 +1650,6 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
if (dcb->sync_period & WIDE_SYNC)
d_left_counter <<= 1;
- dprintkdbg(DBG_KG, "data_out_phase0: FIFO contains %i %s\n"
- "SCSI{fifocnt=0x%02x cnt=0x%08x} "
- "DMA{fifocnt=0x%04x cnt=0x%02x ctr=0x%08x}\n",
- DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
- (dcb->sync_period & WIDE_SYNC) ? "words" : "bytes",
- DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
- DC395x_read32(acb, TRM_S1040_SCSI_COUNTER),
- DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
- DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
- DC395x_read32(acb, TRM_S1040_DMA_CXCNT));
}
/*
* calculate all the residue data that not yet tranfered
@@ -2116,9 +1670,6 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
if (d_left_counter == 1 && dcb->sync_period & WIDE_SYNC
&& scsi_bufflen(srb->cmd) % 2) {
d_left_counter = 0;
- dprintkl(KERN_INFO,
- "data_out_phase0: Discard 1 byte (0x%02x)\n",
- scsi_status);
}
/*
* KG: Oops again. Same thinko as above: The SCSI might have been
@@ -2149,8 +1700,6 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
|| ((oldxferred & ~PAGE_MASK) ==
(PAGE_SIZE - diff))
) {
- dprintkl(KERN_INFO, "data_out_phase0: "
- "Work around chip bug (%i)?\n", diff);
d_left_counter =
srb->total_xfer_length - diff;
sg_update_list(srb, d_left_counter);
@@ -2161,17 +1710,14 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
}
}
}
- if ((*pscsi_status & PHASEMASK) != PH_DATA_OUT) {
+ if ((*pscsi_status & PHASEMASK) != PH_DATA_OUT)
cleanup_after_transfer(acb, srb);
- }
}
static void data_out_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "data_out_phase1: (0x%p) <%02i-%i>\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
clear_fifo(acb, "data_out_phase1");
/* do prepare before transfer when data out phase */
data_io_transfer(acb, srb, XFERDATAOUT);
@@ -2182,8 +1728,6 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
{
u16 scsi_status = *pscsi_status;
- dprintkdbg(DBG_0, "data_in_phase0: (0x%p) <%02i-%i>\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
/*
* KG: DataIn is much more tricky than DataOut. When the device is finished
@@ -2203,8 +1747,6 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
unsigned int sc, fc;
if (scsi_status & PARITYERROR) {
- dprintkl(KERN_INFO, "data_in_phase0: (0x%p) "
- "Parity Error\n", srb->cmd);
srb->status |= PARITY_ERROR;
}
/*
@@ -2216,26 +1758,14 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
if (!(DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT) & 0x80)) {
#if 0
int ctr = 6000000;
- dprintkl(KERN_DEBUG,
- "DIP0: Wait for DMA FIFO to flush ...\n");
/*DC395x_write8 (TRM_S1040_DMA_CONTROL, STOPDMAXFER); */
/*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 7); */
/*DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_DMA_IN); */
while (!
(DC395x_read16(acb, TRM_S1040_DMA_FIFOSTAT) &
0x80) && --ctr);
- if (ctr < 6000000 - 1)
- dprintkl(KERN_DEBUG
- "DIP0: Had to wait for DMA ...\n");
- if (!ctr)
- dprintkl(KERN_ERR,
- "Deadlock in DIP0 waiting for DMA FIFO empty!!\n");
/*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 0); */
#endif
- dprintkdbg(DBG_KG, "data_in_phase0: "
- "DMA{fifocnt=0x%02x fifostat=0x%02x}\n",
- DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
- DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT));
}
/* Now: Check remainig data: The SCSI counters should tell us ... */
sc = DC395x_read32(acb, TRM_S1040_SCSI_COUNTER);
@@ -2243,17 +1773,6 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
d_left_counter = sc + ((fc & 0x1f)
<< ((srb->dcb->sync_period & WIDE_SYNC) ? 1 :
0));
- dprintkdbg(DBG_KG, "data_in_phase0: "
- "SCSI{fifocnt=0x%02x%s ctr=0x%08x} "
- "DMA{fifocnt=0x%02x fifostat=0x%02x ctr=0x%08x} "
- "Remain{totxfer=%i scsi_fifo+ctr=%i}\n",
- fc,
- (srb->dcb->sync_period & WIDE_SYNC) ? "words" : "bytes",
- sc,
- fc,
- DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
- DC395x_read32(acb, TRM_S1040_DMA_CXCNT),
- srb->total_xfer_length, d_left_counter);
#if DC395x_LASTPIO
/* KG: Less than or equal to 4 bytes can not be transferred via DMA, it seems. */
if (d_left_counter
@@ -2262,12 +1781,6 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
/*u32 addr = (srb->segment_x[srb->sg_index].address); */
/*sg_update_list (srb, d_left_counter); */
- dprintkdbg(DBG_PIO, "data_in_phase0: PIO (%i %s) "
- "for remaining %i bytes:",
- fc & 0x1f,
- (srb->dcb->sync_period & WIDE_SYNC) ?
- "words" : "bytes",
- srb->total_xfer_length);
if (srb->dcb->sync_period & WIDE_SYNC)
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
CFG2_WIDEFIFO);
@@ -2291,9 +1804,6 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
byte = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
*virt++ = byte;
- if (debug_enabled(DBG_PIO))
- printk(" %02x", byte);
-
d_left_counter--;
sg_subtract_one(srb);
@@ -2316,8 +1826,6 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
*virt++ = byte;
srb->total_xfer_length--;
- if (debug_enabled(DBG_PIO))
- printk(" %02x", byte);
}
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
@@ -2326,10 +1834,7 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
scsi_kunmap_atomic_sg(base);
local_irq_restore(flags);
}
- /*printk(" %08x", *(u32*)(bus_to_virt (addr))); */
/*srb->total_xfer_length = 0; */
- if (debug_enabled(DBG_PIO))
- printk("\n");
}
#endif /* DC395x_LASTPIO */
@@ -2365,9 +1870,6 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
TempDMAstatus =
DC395x_read8(acb, TRM_S1040_DMA_STATUS);
} while (!(TempDMAstatus & DMAXFERCOMP) && --ctr);
- if (!ctr)
- dprintkl(KERN_ERR,
- "Deadlock in DataInPhase0 waiting for DMA!!\n");
srb->total_xfer_length = 0;
#endif
srb->total_xfer_length = d_left_counter;
@@ -2384,17 +1886,14 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
}
}
/* KG: The target may decide to disconnect: Empty FIFO before! */
- if ((*pscsi_status & PHASEMASK) != PH_DATA_IN) {
+ if ((*pscsi_status & PHASEMASK) != PH_DATA_IN)
cleanup_after_transfer(acb, srb);
- }
}
static void data_in_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "data_in_phase1: (0x%p) <%02i-%i>\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
data_io_transfer(acb, srb, XFERDATAIN);
}
@@ -2404,13 +1903,7 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
{
struct DeviceCtlBlk *dcb = srb->dcb;
u8 bval;
- dprintkdbg(DBG_0,
- "data_io_transfer: (0x%p) <%02i-%i> %c len=%i, sg=(%i/%i)\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun,
- ((io_dir & DMACMD_DIR) ? 'r' : 'w'),
- srb->total_xfer_length, srb->sg_index, srb->sg_count);
- if (srb == acb->tmp_srb)
- dprintkl(KERN_ERR, "data_io_transfer: Using tmp_srb!\n");
+
if (srb->sg_index >= srb->sg_count) {
/* can't happen? out of bounds error */
return;
@@ -2423,9 +1916,6 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
* Maybe, even ABORTXFER would be appropriate
*/
if (dma_status & XFERPENDING) {
- dprintkl(KERN_DEBUG, "data_io_transfer: Xfer pending! "
- "Expect trouble!\n");
- dump_register_info(acb, dcb, srb);
DC395x_write8(acb, TRM_S1040_DMA_CONTROL, CLRXFIFO);
}
/* clear_fifo(acb, "IO"); */
@@ -2504,9 +1994,6 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
left_io -= len;
while (len--) {
- if (debug_enabled(DBG_PIO))
- printk(" %02x", *virt);
-
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, *virt++);
sg_subtract_one(srb);
@@ -2518,21 +2005,16 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
if (srb->dcb->sync_period & WIDE_SYNC) {
if (ln % 2) {
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
- if (debug_enabled(DBG_PIO))
- printk(" |00");
}
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
}
/*DC395x_write32(acb, TRM_S1040_SCSI_COUNTER, ln); */
- if (debug_enabled(DBG_PIO))
- printk("\n");
DC395x_write8(acb, TRM_S1040_SCSI_COMMAND,
SCMD_FIFO_OUT);
}
}
#endif /* DC395x_LASTPIO */
else { /* xfer pad */
- u8 data = 0, data2 = 0;
if (srb->sg_count) {
srb->adapter_status = H_OVER_UNDER_RUN;
srb->status |= OVER_RUN;
@@ -2547,8 +2029,8 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
CFG2_WIDEFIFO);
if (io_dir & DMACMD_DIR) {
- data = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
- data2 = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
} else {
/* Danger, Robinson: If you find KGs
* scattered over the wide disk, the driver
@@ -2562,7 +2044,7 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
/* Danger, Robinson: If you find a collection of Ks on your disk
* something broke :-( */
if (io_dir & DMACMD_DIR)
- data = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
else
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 'K');
}
@@ -2578,8 +2060,6 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
static void status_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "status_phase0: (0x%p) <%02i-%i>\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
srb->target_status = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
srb->end_message = DC395x_read8(acb, TRM_S1040_SCSI_FIFO); /* get message */
srb->state = SRB_COMPLETED;
@@ -2592,8 +2072,6 @@ static void status_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void status_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "status_phase1: (0x%p) <%02i-%i>\n",
- srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
srb->state = SRB_STATUS;
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_COMP);
@@ -2623,9 +2101,6 @@ static inline void msgin_reject(struct AdapterCtlBlk *acb,
DC395x_ENABLE_MSGOUT;
srb->state &= ~SRB_MSGIN;
srb->state |= SRB_MSGOUT;
- dprintkl(KERN_INFO, "msgin_reject: 0x%02x <%02i-%i>\n",
- srb->msgin_buf[0],
- srb->dcb->target_id, srb->dcb->target_lun);
}
@@ -2634,13 +2109,6 @@ static struct ScsiReqBlk *msgin_qtag(struct AdapterCtlBlk *acb,
{
struct ScsiReqBlk *srb = NULL;
struct ScsiReqBlk *i;
- dprintkdbg(DBG_0, "msgin_qtag: (0x%p) tag=%i srb=%p\n",
- srb->cmd, tag, srb);
-
- if (!(dcb->tag_mask & (1 << tag)))
- dprintkl(KERN_DEBUG,
- "msgin_qtag: tag_mask=0x%08x does not reserve tag %i!\n",
- dcb->tag_mask, tag);
if (list_empty(&dcb->srb_going_list))
goto mingx0;
@@ -2653,8 +2121,6 @@ static struct ScsiReqBlk *msgin_qtag(struct AdapterCtlBlk *acb,
if (!srb)
goto mingx0;
- dprintkdbg(DBG_0, "msgin_qtag: (0x%p) <%02i-%i>\n",
- srb->cmd, srb->dcb->target_id, srb->dcb->target_lun);
if (dcb->flag & ABORT_DEV_) {
/*srb->state = SRB_ABORT_SENT; */
enable_msgout_abort(acb, srb);
@@ -2674,10 +2140,9 @@ static struct ScsiReqBlk *msgin_qtag(struct AdapterCtlBlk *acb,
srb = acb->tmp_srb;
srb->state = SRB_UNEXPECT_RESEL;
dcb->active_srb = srb;
- srb->msgout_buf[0] = MSG_ABORT_TAG;
+ srb->msgout_buf[0] = ABORT_TASK;
srb->msg_count = 1;
DC395x_ENABLE_MSGOUT;
- dprintkl(KERN_DEBUG, "msgin_qtag: Unknown tag %i - abort\n", tag);
return srb;
}
@@ -2696,8 +2161,6 @@ static inline void reprogram_regs(struct AdapterCtlBlk *acb,
static void msgin_set_async(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
{
struct DeviceCtlBlk *dcb = srb->dcb;
- dprintkl(KERN_DEBUG, "msgin_set_async: No sync transfers <%02i-%i>\n",
- dcb->target_id, dcb->target_lun);
dcb->sync_mode &= ~(SYNC_NEGO_ENABLE);
dcb->sync_mode |= SYNC_NEGO_DONE;
@@ -2710,7 +2173,6 @@ static void msgin_set_async(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
&& !(dcb->sync_mode & WIDE_NEGO_DONE)) {
build_wdtr(acb, dcb, srb);
DC395x_ENABLE_MSGOUT;
- dprintkdbg(DBG_0, "msgin_set_async(rej): Try WDTR anyway\n");
}
}
@@ -2721,12 +2183,6 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
struct DeviceCtlBlk *dcb = srb->dcb;
u8 bval;
int fact;
- dprintkdbg(DBG_1, "msgin_set_sync: <%02i> Sync: %ins "
- "(%02i.%01i MHz) Offset %i\n",
- dcb->target_id, srb->msgin_buf[3] << 2,
- (250 / srb->msgin_buf[3]),
- ((250 % srb->msgin_buf[3]) * 10) / srb->msgin_buf[3],
- srb->msgin_buf[4]);
if (srb->msgin_buf[4] > 15)
srb->msgin_buf[4] = 15;
@@ -2743,10 +2199,7 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
|| dcb->min_nego_period >
clock_period[bval]))
bval++;
- if (srb->msgin_buf[3] < clock_period[bval])
- dprintkl(KERN_INFO,
- "msgin_set_sync: Increase sync nego period to %ins\n",
- clock_period[bval] << 2);
+
srb->msgin_buf[3] = clock_period[bval];
dcb->sync_period &= 0xf0;
dcb->sync_period |= ALT_SYNC | bval;
@@ -2757,18 +2210,8 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
else
fact = 250;
- dprintkl(KERN_INFO,
- "Target %02i: %s Sync: %ins Offset %i (%02i.%01i MB/s)\n",
- dcb->target_id, (fact == 500) ? "Wide16" : "",
- dcb->min_nego_period << 2, dcb->sync_offset,
- (fact / dcb->min_nego_period),
- ((fact % dcb->min_nego_period) * 10 +
- dcb->min_nego_period / 2) / dcb->min_nego_period);
-
if (!(srb->state & SRB_DO_SYNC_NEGO)) {
/* Reply with corrected SDTR Message */
- dprintkl(KERN_DEBUG, "msgin_set_sync: answer w/%ins %i\n",
- srb->msgin_buf[3] << 2, srb->msgin_buf[4]);
memcpy(srb->msgout_buf, srb->msgin_buf, 5);
srb->msg_count = 5;
@@ -2779,7 +2222,6 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
&& !(dcb->sync_mode & WIDE_NEGO_DONE)) {
build_wdtr(acb, dcb, srb);
DC395x_ENABLE_MSGOUT;
- dprintkdbg(DBG_0, "msgin_set_sync: Also try WDTR\n");
}
}
srb->state &= ~SRB_DO_SYNC_NEGO;
@@ -2793,7 +2235,6 @@ static inline void msgin_set_nowide(struct AdapterCtlBlk *acb,
struct ScsiReqBlk *srb)
{
struct DeviceCtlBlk *dcb = srb->dcb;
- dprintkdbg(DBG_1, "msgin_set_nowide: <%02i>\n", dcb->target_id);
dcb->sync_period &= ~WIDE_SYNC;
dcb->sync_mode &= ~(WIDE_NEGO_ENABLE);
@@ -2804,7 +2245,6 @@ static inline void msgin_set_nowide(struct AdapterCtlBlk *acb,
&& !(dcb->sync_mode & SYNC_NEGO_DONE)) {
build_sdtr(acb, dcb, srb);
DC395x_ENABLE_MSGOUT;
- dprintkdbg(DBG_0, "msgin_set_nowide: Rejected. Try SDTR anyway\n");
}
}
@@ -2813,15 +2253,11 @@ static void msgin_set_wide(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
struct DeviceCtlBlk *dcb = srb->dcb;
u8 wide = (dcb->dev_mode & NTC_DO_WIDE_NEGO
&& acb->config & HCC_WIDE_CARD) ? 1 : 0;
- dprintkdbg(DBG_1, "msgin_set_wide: <%02i>\n", dcb->target_id);
if (srb->msgin_buf[3] > wide)
srb->msgin_buf[3] = wide;
/* Completed */
if (!(srb->state & SRB_DO_WIDE_NEGO)) {
- dprintkl(KERN_DEBUG,
- "msgin_set_wide: Wide nego initiated <%02i>\n",
- dcb->target_id);
memcpy(srb->msgout_buf, srb->msgin_buf, 4);
srb->msg_count = 4;
srb->state |= SRB_DO_WIDE_NEGO;
@@ -2835,15 +2271,11 @@ static void msgin_set_wide(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
dcb->sync_period &= ~WIDE_SYNC;
srb->state &= ~SRB_DO_WIDE_NEGO;
/*dcb->sync_mode &= ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); */
- dprintkdbg(DBG_1,
- "msgin_set_wide: Wide (%i bit) negotiated <%02i>\n",
- (8 << srb->msgin_buf[3]), dcb->target_id);
reprogram_regs(acb, dcb);
if ((dcb->sync_mode & SYNC_NEGO_ENABLE)
&& !(dcb->sync_mode & SYNC_NEGO_DONE)) {
build_sdtr(acb, dcb, srb);
DC395x_ENABLE_MSGOUT;
- dprintkdbg(DBG_0, "msgin_set_wide: Also try SDTR.\n");
}
}
@@ -2864,7 +2296,6 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
struct DeviceCtlBlk *dcb = acb->active_dcb;
- dprintkdbg(DBG_0, "msgin_phase0: (0x%p)\n", srb->cmd);
srb->msgin_buf[acb->msg_len++] = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
if (msgin_completed(srb->msgin_buf, acb->msg_len)) {
@@ -2916,9 +2347,8 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
msgin_reject(acb, srb);
break;
- case MSG_IGNOREWIDE:
+ case IGNORE_WIDE_RESIDUE:
/* Discard wide residual */
- dprintkdbg(DBG_0, "msgin_phase0: Ignore Wide Residual!\n");
break;
case COMMAND_COMPLETE:
@@ -2930,20 +2360,12 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
* SAVE POINTER may be ignored as we have the struct
* ScsiReqBlk* associated with the scsi command.
*/
- dprintkdbg(DBG_0, "msgin_phase0: (0x%p) "
- "SAVE POINTER rem=%i Ignore\n",
- srb->cmd, srb->total_xfer_length);
break;
case RESTORE_POINTERS:
- dprintkdbg(DBG_0, "msgin_phase0: RESTORE POINTER. Ignore\n");
break;
case ABORT:
- dprintkdbg(DBG_0, "msgin_phase0: (0x%p) "
- "<%02i-%i> ABORT msg\n",
- srb->cmd, dcb->target_id,
- dcb->target_lun);
dcb->flag |= ABORT_DEV_;
enable_msgout_abort(acb, srb);
break;
@@ -2951,7 +2373,6 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
default:
/* reject unknown messages */
if (srb->msgin_buf[0] & IDENTIFY_BASE) {
- dprintkdbg(DBG_0, "msgin_phase0: Identify msg\n");
srb->msg_count = 1;
srb->msgout_buf[0] = dcb->identify_msg;
DC395x_ENABLE_MSGOUT;
@@ -2974,7 +2395,6 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void msgin_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "msgin_phase1: (0x%p)\n", srb->cmd);
clear_fifo(acb, "msgin_phase1");
DC395x_write32(acb, TRM_S1040_SCSI_COUNTER, 1);
if (!(srb->state & SRB_MSGIN)) {
@@ -3028,10 +2448,9 @@ static void disconnect(struct AdapterCtlBlk *acb)
struct ScsiReqBlk *srb;
if (!dcb) {
- dprintkl(KERN_ERR, "disconnect: No such device\n");
udelay(500);
/* Suspend queue for a while */
- acb->scsi_host->last_reset =
+ acb->last_reset =
jiffies + HZ / 2 +
HZ * acb->eeprom.delay_time;
clear_fifo(acb, "disconnectEx");
@@ -3040,27 +2459,22 @@ static void disconnect(struct AdapterCtlBlk *acb)
}
srb = dcb->active_srb;
acb->active_dcb = NULL;
- dprintkdbg(DBG_0, "disconnect: (0x%p)\n", srb->cmd);
srb->scsi_phase = PH_BUS_FREE; /* initial phase */
clear_fifo(acb, "disconnect");
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_HWRESELECT);
if (srb->state & SRB_UNEXPECT_RESEL) {
- dprintkl(KERN_ERR,
- "disconnect: Unexpected reselection <%02i-%i>\n",
- dcb->target_id, dcb->target_lun);
srb->state = 0;
waiting_process_next(acb);
} else if (srb->state & SRB_ABORT_SENT) {
dcb->flag &= ~ABORT_DEV_;
- acb->scsi_host->last_reset = jiffies + HZ / 2 + 1;
- dprintkl(KERN_ERR, "disconnect: SRB_ABORT_SENT\n");
+ acb->last_reset = jiffies + HZ / 2 + 1;
doing_srb_done(acb, DID_ABORT, srb->cmd, 1);
waiting_process_next(acb);
} else {
if ((srb->state & (SRB_START_ + SRB_MSGOUT))
|| !(srb->
- state & (SRB_DISCONNECT + SRB_COMPLETED))) {
+ state & (SRB_DISCONNECT | SRB_COMPLETED))) {
/*
* Selection time out
* SRB_START_ || SRB_MSGOUT || (!SRB_DISCONNECT && !SRB_COMPLETED)
@@ -3069,16 +2483,10 @@ static void disconnect(struct AdapterCtlBlk *acb)
if (srb->state != SRB_START_
&& srb->state != SRB_MSGOUT) {
srb->state = SRB_READY;
- dprintkl(KERN_DEBUG,
- "disconnect: (0x%p) Unexpected\n",
- srb->cmd);
srb->target_status = SCSI_STAT_SEL_TIMEOUT;
goto disc1;
} else {
/* Normal selection timeout */
- dprintkdbg(DBG_KG, "disconnect: (0x%p) "
- "<%02i-%i> SelTO\n", srb->cmd,
- dcb->target_id, dcb->target_lun);
if (srb->retry_count++ > DC395x_MAX_RETRIES
|| acb->scan_devices) {
srb->target_status =
@@ -3086,10 +2494,7 @@ static void disconnect(struct AdapterCtlBlk *acb)
goto disc1;
}
free_tag(dcb, srb);
- srb_going_to_waiting_move(dcb, srb);
- dprintkdbg(DBG_KG,
- "disconnect: (0x%p) Retry\n",
- srb->cmd);
+ list_move(&srb->list, &dcb->srb_waiting_list);
waiting_set_timer(acb, HZ / 20);
}
} else if (srb->state & SRB_DISCONNECT) {
@@ -3098,9 +2503,6 @@ static void disconnect(struct AdapterCtlBlk *acb)
* SRB_DISCONNECT (This is what we expect!)
*/
if (bval & 0x40) {
- dprintkdbg(DBG_0, "disconnect: SCSI bus stat "
- " 0x%02x: ACK set! Other controllers?\n",
- bval);
/* It could come from another initiator, therefore don't do much ! */
} else
waiting_process_next(acb);
@@ -3124,8 +2526,6 @@ static void reselect(struct AdapterCtlBlk *acb)
struct ScsiReqBlk *srb = NULL;
u16 rsel_tar_lun_id;
u8 id, lun;
- u8 arblostflag = 0;
- dprintkdbg(DBG_0, "reselect: acb=%p\n", acb);
clear_fifo(acb, "reselect");
/*DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); */
@@ -3134,50 +2534,32 @@ static void reselect(struct AdapterCtlBlk *acb)
if (dcb) { /* Arbitration lost but Reselection win */
srb = dcb->active_srb;
if (!srb) {
- dprintkl(KERN_DEBUG, "reselect: Arb lost Resel won, "
- "but active_srb == NULL\n");
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
return;
}
/* Why the if ? */
if (!acb->scan_devices) {
- dprintkdbg(DBG_KG, "reselect: (0x%p) <%02i-%i> "
- "Arb lost but Resel win rsel=%i stat=0x%04x\n",
- srb->cmd, dcb->target_id,
- dcb->target_lun, rsel_tar_lun_id,
- DC395x_read16(acb, TRM_S1040_SCSI_STATUS));
- arblostflag = 1;
/*srb->state |= SRB_DISCONNECT; */
srb->state = SRB_READY;
free_tag(dcb, srb);
- srb_going_to_waiting_move(dcb, srb);
+ list_move(&srb->list, &dcb->srb_waiting_list);
waiting_set_timer(acb, HZ / 20);
/* return; */
}
}
/* Read Reselected Target Id and LUN */
- if (!(rsel_tar_lun_id & (IDENTIFY_BASE << 8)))
- dprintkl(KERN_DEBUG, "reselect: Expects identify msg. "
- "Got %i!\n", rsel_tar_lun_id);
id = rsel_tar_lun_id & 0xff;
lun = (rsel_tar_lun_id >> 8) & 7;
dcb = find_dcb(acb, id, lun);
if (!dcb) {
- dprintkl(KERN_ERR, "reselect: From non existent device "
- "<%02i-%i>\n", id, lun);
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
return;
}
acb->active_dcb = dcb;
- if (!(dcb->dev_mode & NTC_DO_DISCONNECT))
- dprintkl(KERN_DEBUG, "reselect: in spite of forbidden "
- "disconnection? <%02i-%i>\n",
- dcb->target_id, dcb->target_lun);
-
- if (dcb->sync_mode & EN_TAG_QUEUEING /*&& !arblostflag */) {
+ if (dcb->sync_mode & EN_TAG_QUEUEING) {
srb = acb->tmp_srb;
dcb->active_srb = srb;
} else {
@@ -3187,9 +2569,6 @@ static void reselect(struct AdapterCtlBlk *acb)
/*
* abort command
*/
- dprintkl(KERN_DEBUG,
- "reselect: w/o disconnected cmds <%02i-%i>\n",
- dcb->target_id, dcb->target_lun);
srb = acb->tmp_srb;
srb->state = SRB_UNEXPECT_RESEL;
dcb->active_srb = srb;
@@ -3206,7 +2585,6 @@ static void reselect(struct AdapterCtlBlk *acb)
srb->scsi_phase = PH_BUS_FREE; /* initial phase */
/* Program HA ID, target ID, period and offset */
- dprintkdbg(DBG_0, "reselect: select <%i>\n", dcb->target_id);
DC395x_write8(acb, TRM_S1040_SCSI_HOSTID, acb->scsi_host->this_id); /* host ID */
DC395x_write8(acb, TRM_S1040_SCSI_TARGETID, dcb->target_id); /* target ID */
DC395x_write8(acb, TRM_S1040_SCSI_OFFSET, dcb->sync_offset); /* offset */
@@ -3270,15 +2648,10 @@ static void pci_unmap_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
struct scsi_cmnd *cmd = srb->cmd;
enum dma_data_direction dir = cmd->sc_data_direction;
- if (scsi_sg_count(cmd) && dir != PCI_DMA_NONE) {
+ if (scsi_sg_count(cmd) && dir != DMA_NONE) {
/* unmap DC395x SG list */
- dprintkdbg(DBG_SG, "pci_unmap_srb: list=%08x(%05x)\n",
- srb->sg_bus_addr, SEGMENTX_LEN);
- pci_unmap_single(acb->dev, srb->sg_bus_addr,
- SEGMENTX_LEN,
- PCI_DMA_TODEVICE);
- dprintkdbg(DBG_SG, "pci_unmap_srb: segs=%i buffer=%p\n",
- scsi_sg_count(cmd), scsi_bufflen(cmd));
+ dma_unmap_single(&acb->dev->dev, srb->sg_bus_addr, SEGMENTX_LEN,
+ DMA_TO_DEVICE);
/* unmap the sg segments */
scsi_dma_unmap(cmd);
}
@@ -3292,10 +2665,8 @@ static void pci_unmap_srb_sense(struct AdapterCtlBlk *acb,
if (!(srb->flag & AUTO_REQSENSE))
return;
/* Unmap sense buffer */
- dprintkdbg(DBG_SG, "pci_unmap_srb_sense: buffer=%08x\n",
- srb->segment_x[0].address);
- pci_unmap_single(acb->dev, srb->segment_x[0].address,
- srb->segment_x[0].length, PCI_DMA_FROMDEVICE);
+ dma_unmap_single(&acb->dev->dev, srb->segment_x[0].address,
+ srb->segment_x[0].length, DMA_FROM_DEVICE);
/* Restore SG stuff */
srb->total_xfer_length = srb->xferred;
srb->segment_x[0].address =
@@ -3317,83 +2688,24 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
enum dma_data_direction dir = cmd->sc_data_direction;
int ckc_only = 1;
- dprintkdbg(DBG_1, "srb_done: (0x%p) <%02i-%i>\n", srb->cmd,
- srb->cmd->device->id, srb->cmd->device->lun);
- dprintkdbg(DBG_SG, "srb_done: srb=%p sg=%i(%i/%i) buf=%p\n",
- srb, scsi_sg_count(cmd), srb->sg_index, srb->sg_count,
- scsi_sgtalbe(cmd));
status = srb->target_status;
+ set_host_byte(cmd, DID_OK);
+ set_status_byte(cmd, SAM_STAT_GOOD);
if (srb->flag & AUTO_REQSENSE) {
- dprintkdbg(DBG_0, "srb_done: AUTO_REQSENSE1\n");
pci_unmap_srb_sense(acb, srb);
/*
** target status..........................
*/
srb->flag &= ~AUTO_REQSENSE;
srb->adapter_status = 0;
- srb->target_status = CHECK_CONDITION << 1;
- if (debug_enabled(DBG_1)) {
- switch (cmd->sense_buffer[2] & 0x0f) {
- case NOT_READY:
- dprintkl(KERN_DEBUG,
- "ReqSense: NOT_READY cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
- cmd->cmnd[0], dcb->target_id,
- dcb->target_lun, status, acb->scan_devices);
- break;
- case UNIT_ATTENTION:
- dprintkl(KERN_DEBUG,
- "ReqSense: UNIT_ATTENTION cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
- cmd->cmnd[0], dcb->target_id,
- dcb->target_lun, status, acb->scan_devices);
- break;
- case ILLEGAL_REQUEST:
- dprintkl(KERN_DEBUG,
- "ReqSense: ILLEGAL_REQUEST cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
- cmd->cmnd[0], dcb->target_id,
- dcb->target_lun, status, acb->scan_devices);
- break;
- case MEDIUM_ERROR:
- dprintkl(KERN_DEBUG,
- "ReqSense: MEDIUM_ERROR cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
- cmd->cmnd[0], dcb->target_id,
- dcb->target_lun, status, acb->scan_devices);
- break;
- case HARDWARE_ERROR:
- dprintkl(KERN_DEBUG,
- "ReqSense: HARDWARE_ERROR cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
- cmd->cmnd[0], dcb->target_id,
- dcb->target_lun, status, acb->scan_devices);
- break;
- }
- if (cmd->sense_buffer[7] >= 6)
- printk("sense=0x%02x ASC=0x%02x ASCQ=0x%02x "
- "(0x%08x 0x%08x)\n",
- cmd->sense_buffer[2], cmd->sense_buffer[12],
- cmd->sense_buffer[13],
- *((unsigned int *)(cmd->sense_buffer + 3)),
- *((unsigned int *)(cmd->sense_buffer + 8)));
- else
- printk("sense=0x%02x No ASC/ASCQ (0x%08x)\n",
- cmd->sense_buffer[2],
- *((unsigned int *)(cmd->sense_buffer + 3)));
- }
+ srb->target_status = SAM_STAT_CHECK_CONDITION;
- if (status == (CHECK_CONDITION << 1)) {
- cmd->result = DID_BAD_TARGET << 16;
+ if (status == SAM_STAT_CHECK_CONDITION) {
+ set_host_byte(cmd, DID_BAD_TARGET);
goto ckc_e;
}
- dprintkdbg(DBG_0, "srb_done: AUTO_REQSENSE2\n");
-
- if (srb->total_xfer_length
- && srb->total_xfer_length >= cmd->underflow)
- cmd->result =
- MK_RES_LNX(DRIVER_SENSE, DID_OK,
- srb->end_message, CHECK_CONDITION);
- /*SET_RES_DID(cmd->result,DID_OK) */
- else
- cmd->result =
- MK_RES_LNX(DRIVER_SENSE, DID_OK,
- srb->end_message, CHECK_CONDITION);
+
+ set_status_byte(cmd, SAM_STAT_CHECK_CONDITION);
goto ckc_e;
}
@@ -3403,18 +2715,16 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
/*
* target status..........................
*/
- if (status_byte(status) == CHECK_CONDITION) {
+ if (status == SAM_STAT_CHECK_CONDITION) {
request_sense(acb, dcb, srb);
return;
- } else if (status_byte(status) == QUEUE_FULL) {
+ } else if (status == SAM_STAT_TASK_SET_FULL) {
tempcnt = (u8)list_size(&dcb->srb_going_list);
- dprintkl(KERN_INFO, "QUEUE_FULL for dev <%02i-%i> with %i cmnds\n",
- dcb->target_id, dcb->target_lun, tempcnt);
if (tempcnt > 1)
tempcnt--;
dcb->max_command = tempcnt;
free_tag(dcb, srb);
- srb_going_to_waiting_move(dcb, srb);
+ list_move(&srb->list, &dcb->srb_waiting_list);
waiting_set_timer(acb, HZ / 20);
srb->adapter_status = 0;
srb->target_status = 0;
@@ -3422,13 +2732,11 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
} else if (status == SCSI_STAT_SEL_TIMEOUT) {
srb->adapter_status = H_SEL_TIMEOUT;
srb->target_status = 0;
- cmd->result = DID_NO_CONNECT << 16;
+ set_host_byte(cmd, DID_NO_CONNECT);
} else {
srb->adapter_status = 0;
- SET_RES_DID(cmd->result, DID_ERROR);
- SET_RES_MSG(cmd->result, srb->end_message);
- SET_RES_TARGET(cmd->result, status);
-
+ set_host_byte(cmd, DID_ERROR);
+ set_status_byte(cmd, status);
}
} else {
/*
@@ -3437,27 +2745,22 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
status = srb->adapter_status;
if (status & H_OVER_UNDER_RUN) {
srb->target_status = 0;
- SET_RES_DID(cmd->result, DID_OK);
- SET_RES_MSG(cmd->result, srb->end_message);
+ scsi_msg_to_host_byte(cmd, srb->end_message);
} else if (srb->status & PARITY_ERROR) {
- SET_RES_DID(cmd->result, DID_PARITY);
- SET_RES_MSG(cmd->result, srb->end_message);
+ set_host_byte(cmd, DID_PARITY);
} else { /* No error */
srb->adapter_status = 0;
srb->target_status = 0;
- SET_RES_DID(cmd->result, DID_OK);
}
}
- if (dir != PCI_DMA_NONE && scsi_sg_count(cmd))
- pci_dma_sync_sg_for_cpu(acb->dev, scsi_sglist(cmd),
- scsi_sg_count(cmd), dir);
-
ckc_only = 0;
/* Check Error Conditions */
ckc_e:
+ pci_unmap_srb(acb, srb);
+
if (cmd->cmnd[0] == INQUIRY) {
unsigned char *base = NULL;
struct ScsiInqData *ptr;
@@ -3469,16 +2772,15 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
base = scsi_kmap_atomic_sg(sg, scsi_sg_count(cmd), &offset, &len);
ptr = (struct ScsiInqData *)(base + offset);
- if (!ckc_only && (cmd->result & RES_DID) == 0
+ if (!ckc_only && get_host_byte(cmd) == DID_OK
&& cmd->cmnd[2] == 0 && scsi_bufflen(cmd) >= 8
- && dir != PCI_DMA_NONE && ptr && (ptr->Vers & 0x07) >= 2)
+ && dir != DMA_NONE && ptr && (ptr->Vers & 0x07) >= 2)
dcb->inquiry7 = ptr->Flags;
/*if( srb->cmd->cmnd[0] == INQUIRY && */
/* (host_byte(cmd->result) == DID_OK || status_byte(cmd->result) & CHECK_CONDITION) ) */
- if ((cmd->result == (DID_OK << 16)
- || status_byte(cmd->result) &
- CHECK_CONDITION)) {
+ if ((get_host_byte(cmd) == DID_OK) ||
+ (get_status_byte(cmd) == SAM_STAT_CHECK_CONDITION)) {
if (!dcb->init_tcq_flag) {
add_dev(acb, dcb, ptr);
dcb->init_tcq_flag = 1;
@@ -3491,29 +2793,13 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
/* Here is the info for Doug Gilbert's sg3 ... */
scsi_set_resid(cmd, srb->total_xfer_length);
- /* This may be interpreted by sb. or not ... */
- cmd->SCp.this_residual = srb->total_xfer_length;
- cmd->SCp.buffers_residual = 0;
- if (debug_enabled(DBG_KG)) {
- if (srb->total_xfer_length)
- dprintkdbg(DBG_KG, "srb_done: (0x%p) <%02i-%i> "
- "cmnd=0x%02x Missed %i bytes\n",
- cmd, cmd->device->id, cmd->device->lun,
- cmd->cmnd[0], srb->total_xfer_length);
- }
- srb_going_remove(dcb, srb);
- /* Add to free list */
- if (srb == acb->tmp_srb)
- dprintkl(KERN_ERR, "srb_done: ERROR! Completed cmd with tmp_srb\n");
- else {
- dprintkdbg(DBG_0, "srb_done: (0x%p) done result=0x%08x\n",
- cmd, cmd->result);
- srb_free_insert(acb, srb);
+ if (srb != acb->tmp_srb) {
+ /* Add to free list */
+ list_move_tail(&srb->list, &acb->srb_free_list);
}
- pci_unmap_srb(acb, srb);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
waiting_process_next(acb);
}
@@ -3523,7 +2809,6 @@ static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_flag,
struct scsi_cmnd *cmd, u8 force)
{
struct DeviceCtlBlk *dcb;
- dprintkl(KERN_INFO, "doing_srb_done: pids ");
list_for_each_entry(dcb, &acb->dcb_list, list) {
struct ScsiReqBlk *srb;
@@ -3531,68 +2816,47 @@ static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_flag,
struct scsi_cmnd *p;
list_for_each_entry_safe(srb, tmp, &dcb->srb_going_list, list) {
- enum dma_data_direction dir;
- int result;
-
p = srb->cmd;
- dir = p->sc_data_direction;
- result = MK_RES(0, did_flag, 0, 0);
printk("G:%p(%02i-%i) ", p,
- p->device->id, p->device->lun);
- srb_going_remove(dcb, srb);
+ p->device->id, (u8)p->device->lun);
+ list_del(&srb->list);
free_tag(dcb, srb);
- srb_free_insert(acb, srb);
- p->result = result;
+ list_add_tail(&srb->list, &acb->srb_free_list);
+ set_host_byte(p, did_flag);
+ set_status_byte(p, SAM_STAT_GOOD);
pci_unmap_srb_sense(acb, srb);
pci_unmap_srb(acb, srb);
if (force) {
/* For new EH, we normally don't need to give commands back,
* as they all complete or all time out */
- p->scsi_done(p);
+ scsi_done(p);
}
}
- if (!list_empty(&dcb->srb_going_list))
- dprintkl(KERN_DEBUG,
- "How could the ML send cmnds to the Going queue? <%02i-%i>\n",
- dcb->target_id, dcb->target_lun);
- if (dcb->tag_mask)
- dprintkl(KERN_DEBUG,
- "tag_mask for <%02i-%i> should be empty, is %08x!\n",
- dcb->target_id, dcb->target_lun,
- dcb->tag_mask);
/* Waiting queue */
list_for_each_entry_safe(srb, tmp, &dcb->srb_waiting_list, list) {
- int result;
p = srb->cmd;
- result = MK_RES(0, did_flag, 0, 0);
printk("W:%p<%02i-%i>", p, p->device->id,
- p->device->lun);
- srb_waiting_remove(dcb, srb);
- srb_free_insert(acb, srb);
- p->result = result;
+ (u8)p->device->lun);
+ list_move_tail(&srb->list, &acb->srb_free_list);
+ set_host_byte(p, did_flag);
+ set_status_byte(p, SAM_STAT_GOOD);
pci_unmap_srb_sense(acb, srb);
pci_unmap_srb(acb, srb);
if (force) {
/* For new EH, we normally don't need to give commands back,
* as they all complete or all time out */
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
}
- if (!list_empty(&dcb->srb_waiting_list))
- dprintkl(KERN_DEBUG, "ML queued %i cmnds again to <%02i-%i>\n",
- list_size(&dcb->srb_waiting_list), dcb->target_id,
- dcb->target_lun);
dcb->flag &= ~ABORT_DEV_;
}
- printk("\n");
}
static void reset_scsi_bus(struct AdapterCtlBlk *acb)
{
- dprintkdbg(DBG_0, "reset_scsi_bus: acb=%p\n", acb);
acb->acb_flag |= RESET_DEV; /* RESET_DETECT, RESET_DONE, RESET_DEV */
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_RSTSCSI);
@@ -3639,17 +2903,16 @@ static void set_basic_config(struct AdapterCtlBlk *acb)
static void scsi_reset_detect(struct AdapterCtlBlk *acb)
{
- dprintkl(KERN_INFO, "scsi_reset_detect: acb=%p\n", acb);
/* delay half a second */
if (timer_pending(&acb->waiting_timer))
- del_timer(&acb->waiting_timer);
+ timer_delete(&acb->waiting_timer);
DC395x_write8(acb, TRM_S1040_SCSI_CONTROL, DO_RSTMODULE);
DC395x_write8(acb, TRM_S1040_DMA_CONTROL, DMARESETMODULE);
/*DC395x_write8(acb, TRM_S1040_DMA_CONTROL,STOPDMAXFER); */
udelay(500);
/* Maybe we locked up the bus? Then lets wait even longer ... */
- acb->scsi_host->last_reset =
+ acb->last_reset =
jiffies + 5 * HZ / 2 +
HZ * acb->eeprom.delay_time;
@@ -3676,8 +2939,6 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
struct ScsiReqBlk *srb)
{
struct scsi_cmnd *cmd = srb->cmd;
- dprintkdbg(DBG_1, "request_sense: (0x%p) <%02i-%i>\n",
- cmd, cmd->device->id, cmd->device->lun);
srb->flag |= AUTO_REQSENSE;
srb->adapter_status = 0;
@@ -3696,20 +2957,14 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
srb->total_xfer_length = SCSI_SENSE_BUFFERSIZE;
srb->segment_x[0].length = SCSI_SENSE_BUFFERSIZE;
/* Map sense buffer */
- srb->segment_x[0].address =
- pci_map_single(acb->dev, cmd->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
- dprintkdbg(DBG_SG, "request_sense: map buffer %p->%08x(%05x)\n",
- cmd->sense_buffer, srb->segment_x[0].address,
- SCSI_SENSE_BUFFERSIZE);
+ srb->segment_x[0].address = dma_map_single(&acb->dev->dev,
+ cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE,
+ DMA_FROM_DEVICE);
srb->sg_count = 1;
srb->sg_index = 0;
if (start_scsi(acb, dcb, srb)) { /* Should only happen, if sb. else grabs the bus */
- dprintkl(KERN_DEBUG,
- "request_sense: (0x%p) failed <%02i-%i>\n",
- srb->cmd, dcb->target_id, dcb->target_lun);
- srb_going_to_waiting_move(dcb, srb);
+ list_move(&srb->list, &dcb->srb_waiting_list);
waiting_set_timer(acb, HZ / 100);
}
}
@@ -3736,7 +2991,6 @@ static struct DeviceCtlBlk *device_alloc(struct AdapterCtlBlk *acb,
struct DeviceCtlBlk *dcb;
dcb = kmalloc(sizeof(struct DeviceCtlBlk), GFP_ATOMIC);
- dprintkdbg(DBG_0, "device_alloc: <%02i-%i>\n", target, lun);
if (!dcb)
return NULL;
dcb->acb = NULL;
@@ -3747,13 +3001,13 @@ static struct DeviceCtlBlk *device_alloc(struct AdapterCtlBlk *acb,
dcb->max_command = 1;
dcb->target_id = target;
dcb->target_lun = lun;
+ dcb->dev_mode = eeprom->target[target].cfg0;
#ifndef DC395x_NO_DISCONNECT
dcb->identify_msg =
IDENTIFY(dcb->dev_mode & NTC_DO_DISCONNECT, lun);
#else
dcb->identify_msg = IDENTIFY(0, lun);
#endif
- dcb->dev_mode = eeprom->target[target].cfg0;
dcb->inquiry7 = 0;
dcb->sync_mode = 0;
dcb->min_nego_period = clock_period[period_index];
@@ -3773,14 +3027,19 @@ static struct DeviceCtlBlk *device_alloc(struct AdapterCtlBlk *acb,
#endif
if (dcb->target_lun != 0) {
/* Copy settings */
- struct DeviceCtlBlk *p;
- list_for_each_entry(p, &acb->dcb_list, list)
- if (p->target_id == dcb->target_id)
+ struct DeviceCtlBlk *p = NULL, *iter;
+
+ list_for_each_entry(iter, &acb->dcb_list, list)
+ if (iter->target_id == dcb->target_id) {
+ p = iter;
break;
- dprintkdbg(DBG_1,
- "device_alloc: <%02i-%i> copy from <%02i-%i>\n",
- dcb->target_id, dcb->target_lun,
- p->target_id, p->target_lun);
+ }
+
+ if (!p) {
+ kfree(dcb);
+ return NULL;
+ }
+
dcb->sync_mode = p->sync_mode;
dcb->sync_period = p->sync_period;
dcb->min_nego_period = p->min_nego_period;
@@ -3830,8 +3089,6 @@ static void adapter_remove_device(struct AdapterCtlBlk *acb,
{
struct DeviceCtlBlk *i;
struct DeviceCtlBlk *tmp;
- dprintkdbg(DBG_0, "adapter_remove_device: <%02i-%i>\n",
- dcb->target_id, dcb->target_lun);
/* fix up any pointers to this device that we have in the adapter */
if (acb->active_dcb == dcb)
@@ -3864,10 +3121,6 @@ static void adapter_remove_and_free_device(struct AdapterCtlBlk *acb,
struct DeviceCtlBlk *dcb)
{
if (list_size(&dcb->srb_going_list) > 1) {
- dprintkdbg(DBG_1, "adapter_remove_and_free_device: <%02i-%i> "
- "Won't remove because of %i active requests.\n",
- dcb->target_id, dcb->target_lun,
- list_size(&dcb->srb_going_list));
return;
}
adapter_remove_device(acb, dcb);
@@ -3885,8 +3138,6 @@ static void adapter_remove_and_free_all_devices(struct AdapterCtlBlk* acb)
{
struct DeviceCtlBlk *dcb;
struct DeviceCtlBlk *tmp;
- dprintkdbg(DBG_1, "adapter_remove_and_free_all_devices: num=%i\n",
- list_size(&acb->dcb_list));
list_for_each_entry_safe(dcb, tmp, &acb->dcb_list, list)
adapter_remove_and_free_device(acb, dcb);
@@ -3894,13 +3145,13 @@ static void adapter_remove_and_free_all_devices(struct AdapterCtlBlk* acb)
/**
- * dc395x_slave_alloc - Called by the scsi mid layer to tell us about a new
+ * dc395x_sdev_init - Called by the scsi mid layer to tell us about a new
* scsi device that we need to deal with. We allocate a new device and then
* insert that device into the adapters device list.
*
* @scsi_device: The new scsi device that we need to handle.
**/
-static int dc395x_slave_alloc(struct scsi_device *scsi_device)
+static int dc395x_sdev_init(struct scsi_device *scsi_device)
{
struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)scsi_device->host->hostdata;
struct DeviceCtlBlk *dcb;
@@ -3915,12 +3166,12 @@ static int dc395x_slave_alloc(struct scsi_device *scsi_device)
/**
- * dc395x_slave_destroy - Called by the scsi mid layer to tell us about a
+ * dc395x_sdev_destroy - Called by the scsi mid layer to tell us about a
* device that is going away.
*
* @scsi_device: The new scsi device that we need to handle.
**/
-static void dc395x_slave_destroy(struct scsi_device *scsi_device)
+static void dc395x_sdev_destroy(struct scsi_device *scsi_device)
{
struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)scsi_device->host->hostdata;
struct DeviceCtlBlk *dcb = find_dcb(acb, scsi_device->id, scsi_device->lun);
@@ -3938,7 +3189,7 @@ static void dc395x_slave_destroy(struct scsi_device *scsi_device)
*
* @io_port: base I/O address
**/
-static void __devinit trms1040_wait_30us(unsigned long io_port)
+static void trms1040_wait_30us(unsigned long io_port)
{
/* ScsiPortStallExecution(30); wait 30 us */
outb(5, io_port + TRM_S1040_GEN_TIMER);
@@ -3955,7 +3206,7 @@ static void __devinit trms1040_wait_30us(unsigned long io_port)
* @cmd: SB + op code (command) to send
* @addr: address to send
**/
-static void __devinit trms1040_write_cmd(unsigned long io_port, u8 cmd, u8 addr)
+static void trms1040_write_cmd(unsigned long io_port, u8 cmd, u8 addr)
{
int i;
u8 send_data;
@@ -4000,7 +3251,7 @@ static void __devinit trms1040_write_cmd(unsigned long io_port, u8 cmd, u8 addr)
* @addr: offset into EEPROM
* @byte: bytes to write
**/
-static void __devinit trms1040_set_data(unsigned long io_port, u8 addr, u8 byte)
+static void trms1040_set_data(unsigned long io_port, u8 addr, u8 byte)
{
int i;
u8 send_data;
@@ -4054,7 +3305,7 @@ static void __devinit trms1040_set_data(unsigned long io_port, u8 addr, u8 byte)
* @eeprom: the data to write
* @io_port: the base io port
**/
-static void __devinit trms1040_write_all(struct NvRamType *eeprom, unsigned long io_port)
+static void trms1040_write_all(struct NvRamType *eeprom, unsigned long io_port)
{
u8 *b_eeprom = (u8 *)eeprom;
u8 addr;
@@ -4094,7 +3345,7 @@ static void __devinit trms1040_write_all(struct NvRamType *eeprom, unsigned long
*
* Returns the byte read.
**/
-static u8 __devinit trms1040_get_data(unsigned long io_port, u8 addr)
+static u8 trms1040_get_data(unsigned long io_port, u8 addr)
{
int i;
u8 read_byte;
@@ -4132,7 +3383,7 @@ static u8 __devinit trms1040_get_data(unsigned long io_port, u8 addr)
* @eeprom: where to store the data
* @io_port: the base io port
**/
-static void __devinit trms1040_read_all(struct NvRamType *eeprom, unsigned long io_port)
+static void trms1040_read_all(struct NvRamType *eeprom, unsigned long io_port)
{
u8 *b_eeprom = (u8 *)eeprom;
u8 addr;
@@ -4162,7 +3413,7 @@ static void __devinit trms1040_read_all(struct NvRamType *eeprom, unsigned long
* @eeprom: caller allocated strcuture to read the eeprom data into
* @io_port: io port to read from
**/
-static void __devinit check_eeprom(struct NvRamType *eeprom, unsigned long io_port)
+static void check_eeprom(struct NvRamType *eeprom, unsigned long io_port)
{
u16 *w_eeprom = (u16 *)eeprom;
u16 w_addr;
@@ -4181,8 +3432,6 @@ static void __devinit check_eeprom(struct NvRamType *eeprom, unsigned long io_po
* Checksum is wrong.
* Load a set of defaults into the eeprom buffer
*/
- dprintkl(KERN_WARNING,
- "EEProm checksum error: using default values and options.\n");
eeprom->sub_vendor_id[0] = (u8)PCI_VENDOR_ID_TEKRAM;
eeprom->sub_vendor_id[1] = (u8)(PCI_VENDOR_ID_TEKRAM >> 8);
eeprom->sub_sys_id[0] = (u8)PCI_DEVICE_ID_TEKRAM_TRMS1040;
@@ -4232,17 +3481,8 @@ static void __devinit check_eeprom(struct NvRamType *eeprom, unsigned long io_po
*
* @eeprom: The eeprom data strucutre to show details for.
**/
-static void __devinit print_eeprom_settings(struct NvRamType *eeprom)
+static void print_eeprom_settings(struct NvRamType *eeprom)
{
- dprintkl(KERN_INFO, "Used settings: AdapterID=%02i, Speed=%i(%02i.%01iMHz), dev_mode=0x%02x\n",
- eeprom->scsi_id,
- eeprom->target[0].period,
- clock_speed[eeprom->target[0].period] / 10,
- clock_speed[eeprom->target[0].period] % 10,
- eeprom->target[0].cfg0);
- dprintkl(KERN_INFO, " AdaptMode=0x%02x, Tags=%i(%02i), DelayReset=%is\n",
- eeprom->channel_cfg, eeprom->max_tag,
- 1 << eeprom->max_tag, eeprom->delay_time);
}
@@ -4260,7 +3500,7 @@ static void adapter_sg_tables_free(struct AdapterCtlBlk *acb)
/*
* Allocate SG tables; as we have to pci_map them, an SG list (struct SGentry*)
* should never cross a page boundary */
-static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb)
+static int adapter_sg_tables_alloc(struct AdapterCtlBlk *acb)
{
const unsigned mem_needed = (DC395x_MAX_SRB_CNT+1)
*SEGMENTX_LEN;
@@ -4268,20 +3508,17 @@ static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb)
const unsigned srbs_per_page = PAGE_SIZE/SEGMENTX_LEN;
int srb_idx = 0;
unsigned i = 0;
- struct SGentry *uninitialized_var(ptr);
+ struct SGentry *ptr;
for (i = 0; i < DC395x_MAX_SRB_CNT; i++)
acb->srb_array[i].segment_x = NULL;
- dprintkdbg(DBG_1, "Allocate %i pages for SG tables\n", pages);
while (pages--) {
ptr = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ptr) {
adapter_sg_tables_free(acb);
return 1;
}
- dprintkdbg(DBG_1, "Allocate %li bytes at %p for SG segments %i\n",
- PAGE_SIZE, ptr, srb_idx);
i = 0;
while (i < srbs_per_page && srb_idx < DC395x_MAX_SRB_CNT)
acb->srb_array[srb_idx++].segment_x =
@@ -4290,8 +3527,6 @@ static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb)
if (i < srbs_per_page)
acb->srb.segment_x =
ptr + (i * DC395x_MAX_SG_LISTENTRY);
- else
- dprintkl(KERN_DEBUG, "No space for tmsrb SG table reserved?!\n");
return 0;
}
@@ -4306,13 +3541,11 @@ static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb)
*
* @acb: The adapter to print the information for.
**/
-static void __devinit adapter_print_config(struct AdapterCtlBlk *acb)
+static void adapter_print_config(struct AdapterCtlBlk *acb)
{
u8 bval;
bval = DC395x_read8(acb, TRM_S1040_GEN_STATUS);
- dprintkl(KERN_INFO, "%sConnectors: ",
- ((bval & WIDESCSI) ? "(Wide) " : ""));
if (!(bval & CON5068))
printk("ext%s ", !(bval & EXT68HIGH) ? "68" : "50");
if (!(bval & CON68))
@@ -4350,7 +3583,7 @@ static void __devinit adapter_print_config(struct AdapterCtlBlk *acb)
*
* @acb: The adapter to initialize.
**/
-static void __devinit adapter_init_params(struct AdapterCtlBlk *acb)
+static void adapter_init_params(struct AdapterCtlBlk *acb)
{
struct NvRamType *eeprom = &acb->eeprom;
int i;
@@ -4366,8 +3599,8 @@ static void __devinit adapter_init_params(struct AdapterCtlBlk *acb)
INIT_LIST_HEAD(&acb->srb_free_list);
/* temp SRB for Q tag used or abort command used */
acb->tmp_srb = &acb->srb;
- init_timer(&acb->waiting_timer);
- init_timer(&acb->selto_timer);
+ timer_setup(&acb->waiting_timer, waiting_timeout, 0);
+ timer_setup(&acb->selto_timer, NULL, 0);
acb->srb_count = DC395x_MAX_SRB_CNT;
@@ -4396,12 +3629,12 @@ static void __devinit adapter_init_params(struct AdapterCtlBlk *acb)
/* link static array of srbs into the srb free list */
for (i = 0; i < acb->srb_count - 1; i++)
- srb_free_insert(acb, &acb->srb_array[i]);
+ list_add_tail(&acb->srb_array[i].list, &acb->srb_free_list);
}
/**
- * adapter_init_host - Initialize the scsi host instance based on
+ * adapter_init_scsi_host - Initialize the scsi host instance based on
* values that we have already stored in the adapter instance. There's
* some mention that a lot of these are deprecated, so we won't use
* them (we'll use the ones in the adapter instance) but we'll fill
@@ -4412,7 +3645,7 @@ static void __devinit adapter_init_params(struct AdapterCtlBlk *acb)
*
* @host: The scsi host instance to fill in the values for.
**/
-static void __devinit adapter_init_scsi_host(struct Scsi_Host *host)
+static void adapter_init_scsi_host(struct Scsi_Host *host)
{
struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)host->hostdata;
struct NvRamType *eeprom = &acb->eeprom;
@@ -4426,21 +3659,16 @@ static void __devinit adapter_init_scsi_host(struct Scsi_Host *host)
host->dma_channel = -1;
host->unique_id = acb->io_port_base;
host->irq = acb->irq_level;
- host->last_reset = jiffies;
+ acb->last_reset = jiffies;
host->max_id = 16;
if (host->max_id - 1 == eeprom->scsi_id)
host->max_id--;
-#ifdef CONFIG_SCSI_MULTI_LUN
if (eeprom->channel_cfg & NAC_SCANLUN)
host->max_lun = 8;
else
host->max_lun = 1;
-#else
- host->max_lun = 1;
-#endif
-
}
@@ -4453,7 +3681,7 @@ static void __devinit adapter_init_scsi_host(struct Scsi_Host *host)
*
* @acb: The adapter which we are to init.
**/
-static void __devinit adapter_init_chip(struct AdapterCtlBlk *acb)
+static void adapter_init_chip(struct AdapterCtlBlk *acb)
{
struct NvRamType *eeprom = &acb->eeprom;
@@ -4477,14 +3705,13 @@ static void __devinit adapter_init_chip(struct AdapterCtlBlk *acb)
acb->config |= HCC_SCSI_RESET;
if (acb->config & HCC_SCSI_RESET) {
- dprintkl(KERN_INFO, "Performing initial SCSI bus reset\n");
DC395x_write8(acb, TRM_S1040_SCSI_CONTROL, DO_RSTSCSI);
/*while (!( DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET )); */
/*spin_unlock_irq (&io_request_lock); */
udelay(500);
- acb->scsi_host->last_reset =
+ acb->last_reset =
jiffies + HZ / 2 +
HZ * acb->eeprom.delay_time;
@@ -4494,23 +3721,23 @@ static void __devinit adapter_init_chip(struct AdapterCtlBlk *acb)
/**
- * init_adapter - Grab the resource for the card, setup the adapter
+ * adapter_init - Grab the resource for the card, setup the adapter
* information, set the card into a known state, create the various
* tables etc etc. This basically gets all adapter information all up
* to date, initialised and gets the chip in sync with it.
*
- * @host: This hosts adapter structure
+ * @acb: The adapter which we are to init.
* @io_port: The base I/O port
+ * @io_port_len: The I/O port size
* @irq: IRQ
*
* Returns 0 if the initialization succeeds, any other value on
* failure.
**/
-static int __devinit adapter_init(struct AdapterCtlBlk *acb,
- unsigned long io_port, u32 io_port_len, unsigned int irq)
+static int adapter_init(struct AdapterCtlBlk *acb, unsigned long io_port,
+ u32 io_port_len, unsigned int irq)
{
if (!request_region(io_port, io_port_len, DC395X_NAME)) {
- dprintkl(KERN_ERR, "Failed to reserve IO region 0x%lx\n", io_port);
goto failed;
}
/* store port base to indicate we have registered it */
@@ -4519,7 +3746,6 @@ static int __devinit adapter_init(struct AdapterCtlBlk *acb,
if (request_irq(irq, dc395x_interrupt, IRQF_SHARED, DC395X_NAME, acb)) {
/* release the region we just claimed */
- dprintkl(KERN_INFO, "Failed to register IRQ\n");
goto failed;
}
/* store irq to indicate we have registered it */
@@ -4536,18 +3762,12 @@ static int __devinit adapter_init(struct AdapterCtlBlk *acb,
adapter_print_config(acb);
if (adapter_sg_tables_alloc(acb)) {
- dprintkl(KERN_DEBUG, "Memory allocation for SG tables failed\n");
goto failed;
}
adapter_init_scsi_host(acb->scsi_host);
adapter_init_chip(acb);
set_basic_config(acb);
- dprintkdbg(DBG_0,
- "adapter_init: acb=%p, pdcb_map=%p psrb_array=%p "
- "size{acb=0x%04x dcb=0x%04x srb=0x%04x}\n",
- acb, acb->dcb_map, acb->srb_array, sizeof(struct AdapterCtlBlk),
- sizeof(struct DeviceCtlBlk), sizeof(struct ScsiReqBlk));
return 0;
failed:
@@ -4598,9 +3818,9 @@ static void adapter_uninit(struct AdapterCtlBlk *acb)
/* remove timers */
if (timer_pending(&acb->waiting_timer))
- del_timer(&acb->waiting_timer);
+ timer_delete(&acb->waiting_timer);
if (timer_pending(&acb->selto_timer))
- del_timer(&acb->selto_timer);
+ timer_delete(&acb->selto_timer);
adapter_uninit_chip(acb);
adapter_remove_and_free_all_devices(acb);
@@ -4615,68 +3835,52 @@ static void adapter_uninit(struct AdapterCtlBlk *acb)
}
-#undef SPRINTF
-#define SPRINTF(args...) pos += sprintf(pos, args)
-
#undef YESNO
#define YESNO(YN) \
- if (YN) SPRINTF(" Yes ");\
- else SPRINTF(" No ")
+ if (YN) seq_printf(m, " Yes ");\
+ else seq_printf(m, " No ")
-static int dc395x_proc_info(struct Scsi_Host *host, char *buffer,
- char **start, off_t offset, int length, int inout)
+static int dc395x_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)host->hostdata;
int spd, spd1;
- char *pos = buffer;
struct DeviceCtlBlk *dcb;
unsigned long flags;
int dev;
- if (inout) /* Has data been written to the file ? */
- return -EPERM;
-
- SPRINTF(DC395X_BANNER " PCI SCSI Host Adapter\n");
- SPRINTF(" Driver Version " DC395X_VERSION "\n");
+ seq_puts(m, DC395X_BANNER " PCI SCSI Host Adapter\n"
+ " Driver Version " DC395X_VERSION "\n");
DC395x_LOCK_IO(acb->scsi_host, flags);
- SPRINTF("SCSI Host Nr %i, ", host->host_no);
- SPRINTF("DC395U/UW/F DC315/U %s\n",
+ seq_printf(m, "SCSI Host Nr %i, ", host->host_no);
+ seq_printf(m, "DC395U/UW/F DC315/U %s\n",
(acb->config & HCC_WIDE_CARD) ? "Wide" : "");
- SPRINTF("io_port_base 0x%04lx, ", acb->io_port_base);
- SPRINTF("irq_level 0x%04x, ", acb->irq_level);
- SPRINTF(" SelTimeout %ims\n", (1638 * acb->sel_timeout) / 1000);
+ seq_printf(m, "io_port_base 0x%04lx, ", acb->io_port_base);
+ seq_printf(m, "irq_level 0x%04x, ", acb->irq_level);
+ seq_printf(m, " SelTimeout %ims\n", (1638 * acb->sel_timeout) / 1000);
- SPRINTF("MaxID %i, MaxLUN %i, ", host->max_id, host->max_lun);
- SPRINTF("AdapterID %i\n", host->this_id);
+ seq_printf(m, "MaxID %i, MaxLUN %llu, ", host->max_id, host->max_lun);
+ seq_printf(m, "AdapterID %i\n", host->this_id);
- SPRINTF("tag_max_num %i", acb->tag_max_num);
- /*SPRINTF(", DMA_Status %i\n", DC395x_read8(acb, TRM_S1040_DMA_STATUS)); */
- SPRINTF(", FilterCfg 0x%02x",
+ seq_printf(m, "tag_max_num %i", acb->tag_max_num);
+ /*seq_printf(m, ", DMA_Status %i\n", DC395x_read8(acb, TRM_S1040_DMA_STATUS)); */
+ seq_printf(m, ", FilterCfg 0x%02x",
DC395x_read8(acb, TRM_S1040_SCSI_CONFIG1));
- SPRINTF(", DelayReset %is\n", acb->eeprom.delay_time);
- /*SPRINTF("\n"); */
-
- SPRINTF("Nr of DCBs: %i\n", list_size(&acb->dcb_list));
- SPRINTF
- ("Map of attached LUNs: %02x %02x %02x %02x %02x %02x %02x %02x\n",
- acb->dcb_map[0], acb->dcb_map[1], acb->dcb_map[2],
- acb->dcb_map[3], acb->dcb_map[4], acb->dcb_map[5],
- acb->dcb_map[6], acb->dcb_map[7]);
- SPRINTF
- (" %02x %02x %02x %02x %02x %02x %02x %02x\n",
- acb->dcb_map[8], acb->dcb_map[9], acb->dcb_map[10],
- acb->dcb_map[11], acb->dcb_map[12], acb->dcb_map[13],
- acb->dcb_map[14], acb->dcb_map[15]);
-
- SPRINTF
- ("Un ID LUN Prty Sync Wide DsCn SndS TagQ nego_period SyncFreq SyncOffs MaxCmd\n");
+ seq_printf(m, ", DelayReset %is\n", acb->eeprom.delay_time);
+ /*seq_printf(m, "\n"); */
+
+ seq_printf(m, "Nr of DCBs: %i\n", list_size(&acb->dcb_list));
+ seq_printf(m, "Map of attached LUNs: %8ph\n", &acb->dcb_map[0]);
+ seq_printf(m, " %8ph\n", &acb->dcb_map[8]);
+
+ seq_puts(m,
+ "Un ID LUN Prty Sync Wide DsCn SndS TagQ nego_period SyncFreq SyncOffs MaxCmd\n");
dev = 0;
list_for_each_entry(dcb, &acb->dcb_list, list) {
int nego_period;
- SPRINTF("%02i %02i %02i ", dev, dcb->target_id,
+ seq_printf(m, "%02i %02i %02i ", dev, dcb->target_id,
dcb->target_lun);
YESNO(dcb->dev_mode & NTC_DO_PARITY_CHK);
YESNO(dcb->sync_offset);
@@ -4686,102 +3890,71 @@ static int dc395x_proc_info(struct Scsi_Host *host, char *buffer,
YESNO(dcb->sync_mode & EN_TAG_QUEUEING);
nego_period = clock_period[dcb->sync_period & 0x07] << 2;
if (dcb->sync_offset)
- SPRINTF(" %03i ns ", nego_period);
+ seq_printf(m, " %03i ns ", nego_period);
else
- SPRINTF(" (%03i ns)", (dcb->min_nego_period << 2));
+ seq_printf(m, " (%03i ns)", (dcb->min_nego_period << 2));
if (dcb->sync_offset & 0x0f) {
spd = 1000 / (nego_period);
spd1 = 1000 % (nego_period);
spd1 = (spd1 * 10 + nego_period / 2) / (nego_period);
- SPRINTF(" %2i.%1i M %02i ", spd, spd1,
+ seq_printf(m, " %2i.%1i M %02i ", spd, spd1,
(dcb->sync_offset & 0x0f));
} else
- SPRINTF(" ");
+ seq_puts(m, " ");
/* Add more info ... */
- SPRINTF(" %02i\n", dcb->max_command);
+ seq_printf(m, " %02i\n", dcb->max_command);
dev++;
}
if (timer_pending(&acb->waiting_timer))
- SPRINTF("Waiting queue timer running\n");
+ seq_puts(m, "Waiting queue timer running\n");
else
- SPRINTF("\n");
+ seq_putc(m, '\n');
list_for_each_entry(dcb, &acb->dcb_list, list) {
struct ScsiReqBlk *srb;
if (!list_empty(&dcb->srb_waiting_list))
- SPRINTF("DCB (%02i-%i): Waiting: %i:",
+ seq_printf(m, "DCB (%02i-%i): Waiting: %i:",
dcb->target_id, dcb->target_lun,
list_size(&dcb->srb_waiting_list));
list_for_each_entry(srb, &dcb->srb_waiting_list, list)
- SPRINTF(" %p", srb->cmd);
+ seq_printf(m, " %p", srb->cmd);
if (!list_empty(&dcb->srb_going_list))
- SPRINTF("\nDCB (%02i-%i): Going : %i:",
+ seq_printf(m, "\nDCB (%02i-%i): Going : %i:",
dcb->target_id, dcb->target_lun,
list_size(&dcb->srb_going_list));
list_for_each_entry(srb, &dcb->srb_going_list, list)
- SPRINTF(" %p", srb->cmd);
+ seq_printf(m, " %p", srb->cmd);
if (!list_empty(&dcb->srb_waiting_list) || !list_empty(&dcb->srb_going_list))
- SPRINTF("\n");
+ seq_putc(m, '\n');
}
- if (debug_enabled(DBG_1)) {
- SPRINTF("DCB list for ACB %p:\n", acb);
- list_for_each_entry(dcb, &acb->dcb_list, list) {
- SPRINTF("%p -> ", dcb);
- }
- SPRINTF("END\n");
- }
-
- *start = buffer + offset;
DC395x_UNLOCK_IO(acb->scsi_host, flags);
-
- if (pos - buffer < offset)
- return 0;
- else if (pos - buffer - offset < length)
- return pos - buffer - offset;
- else
- return length;
+ return 0;
}
-static struct scsi_host_template dc395x_driver_template = {
+static const struct scsi_host_template dc395x_driver_template = {
.module = THIS_MODULE,
.proc_name = DC395X_NAME,
- .proc_info = dc395x_proc_info,
+ .show_info = dc395x_show_info,
.name = DC395X_BANNER " " DC395X_VERSION,
.queuecommand = dc395x_queue_command,
- .bios_param = dc395x_bios_param,
- .slave_alloc = dc395x_slave_alloc,
- .slave_destroy = dc395x_slave_destroy,
+ .sdev_init = dc395x_sdev_init,
+ .sdev_destroy = dc395x_sdev_destroy,
.can_queue = DC395x_MAX_CAN_QUEUE,
.this_id = 7,
.sg_tablesize = DC395x_MAX_SG_TABLESIZE,
.cmd_per_lun = DC395x_MAX_CMD_PER_LUN,
.eh_abort_handler = dc395x_eh_abort,
.eh_bus_reset_handler = dc395x_eh_bus_reset,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
};
/**
- * banner_display - Display banner on first instance of driver
- * initialized.
- **/
-static void banner_display(void)
-{
- static int banner_done = 0;
- if (!banner_done)
- {
- dprintkl(KERN_INFO, "%s %s\n", DC395X_BANNER, DC395X_VERSION);
- banner_done = 1;
- }
-}
-
-
-/**
* dc395x_init_one - Initialise a single instance of the adapter.
*
* The PCI layer will call this once for each instance of the adapter
@@ -4794,8 +3967,7 @@ static void banner_display(void)
*
* Returns 0 on success, or an error code (-ve) on failure.
**/
-static int __devinit dc395x_init_one(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int dc395x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
struct Scsi_Host *scsi_host = NULL;
struct AdapterCtlBlk *acb = NULL;
@@ -4803,43 +3975,35 @@ static int __devinit dc395x_init_one(struct pci_dev *dev,
unsigned int io_port_len;
unsigned int irq;
- dprintkdbg(DBG_0, "Init one instance (%s)\n", pci_name(dev));
- banner_display();
-
if (pci_enable_device(dev))
- {
- dprintkl(KERN_INFO, "PCI Enable device failed.\n");
return -ENODEV;
- }
+
io_port_base = pci_resource_start(dev, 0) & PCI_BASE_ADDRESS_IO_MASK;
io_port_len = pci_resource_len(dev, 0);
irq = dev->irq;
- dprintkdbg(DBG_0, "IO_PORT=0x%04lx, IRQ=0x%x\n", io_port_base, dev->irq);
/* allocate scsi host information (includes out adapter) */
scsi_host = scsi_host_alloc(&dc395x_driver_template,
sizeof(struct AdapterCtlBlk));
- if (!scsi_host) {
- dprintkl(KERN_INFO, "scsi_host_alloc failed\n");
+ if (!scsi_host)
goto fail;
- }
+
acb = (struct AdapterCtlBlk*)scsi_host->hostdata;
acb->scsi_host = scsi_host;
acb->dev = dev;
/* initialise the adapter and everything we need */
if (adapter_init(acb, io_port_base, io_port_len, irq)) {
- dprintkl(KERN_INFO, "adapter init failed\n");
+ acb = NULL;
goto fail;
}
pci_set_master(dev);
/* get the scsi mid level to scan for new devices on the bus */
- if (scsi_add_host(scsi_host, &dev->dev)) {
- dprintkl(KERN_ERR, "scsi_add_host failed\n");
+ if (scsi_add_host(scsi_host, &dev->dev))
goto fail;
- }
+
pci_set_drvdata(dev, scsi_host);
scsi_scan_host(scsi_host);
@@ -4861,22 +4025,19 @@ fail:
*
* @dev: The PCI device to initialize.
**/
-static void __devexit dc395x_remove_one(struct pci_dev *dev)
+static void dc395x_remove_one(struct pci_dev *dev)
{
struct Scsi_Host *scsi_host = pci_get_drvdata(dev);
struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)(scsi_host->hostdata);
- dprintkdbg(DBG_0, "dc395x_remove_one: acb=%p\n", acb);
-
scsi_remove_host(scsi_host);
adapter_uninit(acb);
pci_disable_device(dev);
scsi_host_put(scsi_host);
- pci_set_drvdata(dev, NULL);
}
-static struct pci_device_id dc395x_pci_table[] = {
+static const struct pci_device_id dc395x_pci_table[] = {
{
.vendor = PCI_VENDOR_ID_TEKRAM,
.device = PCI_DEVICE_ID_TEKRAM_TRMS1040,
@@ -4892,32 +4053,9 @@ static struct pci_driver dc395x_driver = {
.name = DC395X_NAME,
.id_table = dc395x_pci_table,
.probe = dc395x_init_one,
- .remove = __devexit_p(dc395x_remove_one),
+ .remove = dc395x_remove_one,
};
-
-
-/**
- * dc395x_module_init - Module initialization function
- *
- * Used by both module and built-in driver to initialise this driver.
- **/
-static int __init dc395x_module_init(void)
-{
- return pci_register_driver(&dc395x_driver);
-}
-
-
-/**
- * dc395x_module_exit - Module cleanup function.
- **/
-static void __exit dc395x_module_exit(void)
-{
- pci_unregister_driver(&dc395x_driver);
-}
-
-
-module_init(dc395x_module_init);
-module_exit(dc395x_module_exit);
+module_pci_driver(dc395x_driver);
MODULE_AUTHOR("C.L. Huang / Erich Chen / Kurt Garloff");
MODULE_DESCRIPTION("SCSI host adapter driver for Tekram TRM-S1040 based adapters: Tekram DC395 and DC315 series");
diff --git a/drivers/scsi/dc395x.h b/drivers/scsi/dc395x.h
index fbf35e37701e..24a36c046d07 100644
--- a/drivers/scsi/dc395x.h
+++ b/drivers/scsi/dc395x.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/************************************************************************/
/* */
/* dc395x.h */
@@ -155,15 +156,6 @@
#define H_ABORT 0x0FF
/* SCSI BUS Status byte codes */
-#define SCSI_STAT_GOOD 0x0 /* Good status */
-#define SCSI_STAT_CHECKCOND 0x02 /* SCSI Check Condition */
-#define SCSI_STAT_CONDMET 0x04 /* Condition Met */
-#define SCSI_STAT_BUSY 0x08 /* Target busy status */
-#define SCSI_STAT_INTER 0x10 /* Intermediate status */
-#define SCSI_STAT_INTERCONDMET 0x14 /* Intermediate condition met */
-#define SCSI_STAT_RESCONFLICT 0x18 /* Reservation conflict */
-#define SCSI_STAT_CMDTERM 0x22 /* Command Terminated */
-#define SCSI_STAT_QUEUEFULL 0x28 /* Queue Full */
#define SCSI_STAT_UNEXP_BUS_F 0xFD /* Unexpect Bus Free */
#define SCSI_STAT_BUS_RST_DETECT 0xFE /* Scsi Bus Reset detected */
#define SCSI_STAT_SEL_TIMEOUT 0xFF /* Selection Time out */
@@ -180,35 +172,6 @@
#define SYNC_NEGO_OFFSET 15
-/* SCSI MSG BYTE */
-#define MSG_COMPLETE 0x00
-#define MSG_EXTENDED 0x01
-#define MSG_SAVE_PTR 0x02
-#define MSG_RESTORE_PTR 0x03
-#define MSG_DISCONNECT 0x04
-#define MSG_INITIATOR_ERROR 0x05
-#define MSG_ABORT 0x06
-#define MSG_REJECT_ 0x07
-#define MSG_NOP 0x08
-#define MSG_PARITY_ERROR 0x09
-#define MSG_LINK_CMD_COMPL 0x0A
-#define MSG_LINK_CMD_COMPL_FLG 0x0B
-#define MSG_BUS_RESET 0x0C
-#define MSG_ABORT_TAG 0x0D
-#define MSG_SIMPLE_QTAG 0x20
-#define MSG_HEAD_QTAG 0x21
-#define MSG_ORDER_QTAG 0x22
-#define MSG_IGNOREWIDE 0x23
-#define MSG_IDENTIFY 0x80
-#define MSG_HOST_ID 0xC0
-
-/* SCSI STATUS BYTE */
-#define STATUS_GOOD 0x00
-#define CHECK_CONDITION_ 0x02
-#define STATUS_BUSY 0x08
-#define STATUS_INTERMEDIATE 0x10
-#define RESERVE_CONFLICT 0x18
-
/* cmd->result */
#define STATUS_MASK_ 0xFF
#define MSG_MASK 0xFF00
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig
index 67070257919f..368eb94c2456 100644
--- a/drivers/scsi/device_handler/Kconfig
+++ b/drivers/scsi/device_handler/Kconfig
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# SCSI Device Handler configuration
#
menuconfig SCSI_DH
- tristate "SCSI Device Handlers"
+ bool "SCSI Device Handlers"
depends on SCSI
default n
help
@@ -13,13 +14,13 @@ menuconfig SCSI_DH
config SCSI_DH_RDAC
tristate "LSI RDAC Device Handler"
- depends on SCSI_DH
+ depends on SCSI_DH && SCSI
help
If you have a LSI RDAC select y. Otherwise, say N.
config SCSI_DH_HP_SW
tristate "HP/COMPAQ MSA Device Handler"
- depends on SCSI_DH
+ depends on SCSI_DH && SCSI
help
If you have a HP/COMPAQ MSA device that requires START_STOP to
be sent to start it and cannot upgrade the firmware then select y.
@@ -27,13 +28,13 @@ config SCSI_DH_HP_SW
config SCSI_DH_EMC
tristate "EMC CLARiiON Device Handler"
- depends on SCSI_DH
+ depends on SCSI_DH && SCSI
help
If you have a EMC CLARiiON select y. Otherwise, say N.
config SCSI_DH_ALUA
- tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)"
- depends on SCSI_DH && EXPERIMENTAL
+ tristate "SPC-3 ALUA Device Handler"
+ depends on SCSI_DH && SCSI
help
SCSI Device handler for generic SPC-3 Asymmetric Logical Unit
Access (ALUA).
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile
index e1d2ea083e15..0a603aefd2bb 100644
--- a/drivers/scsi/device_handler/Makefile
+++ b/drivers/scsi/device_handler/Makefile
@@ -1,7 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# SCSI Device Handler
#
-obj-$(CONFIG_SCSI_DH) += scsi_dh.o
obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o
obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o
obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
deleted file mode 100644
index 0119b8147797..000000000000
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * SCSI device handler infrastruture.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright IBM Corporation, 2007
- * Authors:
- * Chandra Seetharaman <sekharan@us.ibm.com>
- * Mike Anderson <andmike@linux.vnet.ibm.com>
- */
-
-#include <linux/slab.h>
-#include <scsi/scsi_dh.h>
-#include "../scsi_priv.h"
-
-static DEFINE_SPINLOCK(list_lock);
-static LIST_HEAD(scsi_dh_list);
-static int scsi_dh_list_idx = 1;
-
-static struct scsi_device_handler *get_device_handler(const char *name)
-{
- struct scsi_device_handler *tmp, *found = NULL;
-
- spin_lock(&list_lock);
- list_for_each_entry(tmp, &scsi_dh_list, list) {
- if (!strncmp(tmp->name, name, strlen(tmp->name))) {
- found = tmp;
- break;
- }
- }
- spin_unlock(&list_lock);
- return found;
-}
-
-static struct scsi_device_handler *get_device_handler_by_idx(int idx)
-{
- struct scsi_device_handler *tmp, *found = NULL;
-
- spin_lock(&list_lock);
- list_for_each_entry(tmp, &scsi_dh_list, list) {
- if (tmp->idx == idx) {
- found = tmp;
- break;
- }
- }
- spin_unlock(&list_lock);
- return found;
-}
-
-/*
- * device_handler_match - Attach a device handler to a device
- * @scsi_dh - The device handler to match against or NULL
- * @sdev - SCSI device to be tested against @scsi_dh
- *
- * Tests @sdev against the device handler @scsi_dh or against
- * all registered device_handler if @scsi_dh == NULL.
- * Returns the found device handler or NULL if not found.
- */
-static struct scsi_device_handler *
-device_handler_match(struct scsi_device_handler *scsi_dh,
- struct scsi_device *sdev)
-{
- struct scsi_device_handler *found_dh = NULL;
- int idx;
-
- idx = scsi_get_device_flags_keyed(sdev, sdev->vendor, sdev->model,
- SCSI_DEVINFO_DH);
- found_dh = get_device_handler_by_idx(idx);
-
- if (scsi_dh && found_dh != scsi_dh)
- found_dh = NULL;
-
- return found_dh;
-}
-
-/*
- * scsi_dh_handler_attach - Attach a device handler to a device
- * @sdev - SCSI device the device handler should attach to
- * @scsi_dh - The device handler to attach
- */
-static int scsi_dh_handler_attach(struct scsi_device *sdev,
- struct scsi_device_handler *scsi_dh)
-{
- int err = 0;
-
- if (sdev->scsi_dh_data) {
- if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
- err = -EBUSY;
- else
- kref_get(&sdev->scsi_dh_data->kref);
- } else if (scsi_dh->attach) {
- err = scsi_dh->attach(sdev);
- if (!err) {
- kref_init(&sdev->scsi_dh_data->kref);
- sdev->scsi_dh_data->sdev = sdev;
- }
- }
- return err;
-}
-
-static void __detach_handler (struct kref *kref)
-{
- struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref);
- scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev);
-}
-
-/*
- * scsi_dh_handler_detach - Detach a device handler from a device
- * @sdev - SCSI device the device handler should be detached from
- * @scsi_dh - Device handler to be detached
- *
- * Detach from a device handler. If a device handler is specified,
- * only detach if the currently attached handler matches @scsi_dh.
- */
-static void scsi_dh_handler_detach(struct scsi_device *sdev,
- struct scsi_device_handler *scsi_dh)
-{
- if (!sdev->scsi_dh_data)
- return;
-
- if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh)
- return;
-
- if (!scsi_dh)
- scsi_dh = sdev->scsi_dh_data->scsi_dh;
-
- if (scsi_dh && scsi_dh->detach)
- kref_put(&sdev->scsi_dh_data->kref, __detach_handler);
-}
-
-/*
- * Functions for sysfs attribute 'dh_state'
- */
-static ssize_t
-store_dh_state(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
- struct scsi_device_handler *scsi_dh;
- int err = -EINVAL;
-
- if (!sdev->scsi_dh_data) {
- /*
- * Attach to a device handler
- */
- if (!(scsi_dh = get_device_handler(buf)))
- return err;
- err = scsi_dh_handler_attach(sdev, scsi_dh);
- } else {
- scsi_dh = sdev->scsi_dh_data->scsi_dh;
- if (!strncmp(buf, "detach", 6)) {
- /*
- * Detach from a device handler
- */
- scsi_dh_handler_detach(sdev, scsi_dh);
- err = 0;
- } else if (!strncmp(buf, "activate", 8)) {
- /*
- * Activate a device handler
- */
- if (scsi_dh->activate)
- err = scsi_dh->activate(sdev, NULL, NULL);
- else
- err = 0;
- }
- }
-
- return err<0?err:count;
-}
-
-static ssize_t
-show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
-
- if (!sdev->scsi_dh_data)
- return snprintf(buf, 20, "detached\n");
-
- return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name);
-}
-
-static struct device_attribute scsi_dh_state_attr =
- __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
- store_dh_state);
-
-/*
- * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh
- */
-static int scsi_dh_sysfs_attr_add(struct device *dev, void *data)
-{
- struct scsi_device *sdev;
- int err;
-
- if (!scsi_is_sdev_device(dev))
- return 0;
-
- sdev = to_scsi_device(dev);
-
- err = device_create_file(&sdev->sdev_gendev,
- &scsi_dh_state_attr);
-
- return 0;
-}
-
-/*
- * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh
- */
-static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data)
-{
- struct scsi_device *sdev;
-
- if (!scsi_is_sdev_device(dev))
- return 0;
-
- sdev = to_scsi_device(dev);
-
- device_remove_file(&sdev->sdev_gendev,
- &scsi_dh_state_attr);
-
- return 0;
-}
-
-/*
- * scsi_dh_notifier - notifier chain callback
- */
-static int scsi_dh_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct scsi_device *sdev;
- int err = 0;
- struct scsi_device_handler *devinfo = NULL;
-
- if (!scsi_is_sdev_device(dev))
- return 0;
-
- sdev = to_scsi_device(dev);
-
- if (action == BUS_NOTIFY_ADD_DEVICE) {
- err = device_create_file(dev, &scsi_dh_state_attr);
- /* don't care about err */
- devinfo = device_handler_match(NULL, sdev);
- if (devinfo)
- err = scsi_dh_handler_attach(sdev, devinfo);
- } else if (action == BUS_NOTIFY_DEL_DEVICE) {
- device_remove_file(dev, &scsi_dh_state_attr);
- scsi_dh_handler_detach(sdev, NULL);
- }
- return err;
-}
-
-/*
- * scsi_dh_notifier_add - Callback for scsi_register_device_handler
- */
-static int scsi_dh_notifier_add(struct device *dev, void *data)
-{
- struct scsi_device_handler *scsi_dh = data;
- struct scsi_device *sdev;
-
- if (!scsi_is_sdev_device(dev))
- return 0;
-
- if (!get_device(dev))
- return 0;
-
- sdev = to_scsi_device(dev);
-
- if (device_handler_match(scsi_dh, sdev))
- scsi_dh_handler_attach(sdev, scsi_dh);
-
- put_device(dev);
-
- return 0;
-}
-
-/*
- * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler
- */
-static int scsi_dh_notifier_remove(struct device *dev, void *data)
-{
- struct scsi_device_handler *scsi_dh = data;
- struct scsi_device *sdev;
-
- if (!scsi_is_sdev_device(dev))
- return 0;
-
- if (!get_device(dev))
- return 0;
-
- sdev = to_scsi_device(dev);
-
- scsi_dh_handler_detach(sdev, scsi_dh);
-
- put_device(dev);
-
- return 0;
-}
-
-/*
- * scsi_register_device_handler - register a device handler personality
- * module.
- * @scsi_dh - device handler to be registered.
- *
- * Returns 0 on success, -EBUSY if handler already registered.
- */
-int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
-{
- int i;
-
- if (get_device_handler(scsi_dh->name))
- return -EBUSY;
-
- spin_lock(&list_lock);
- scsi_dh->idx = scsi_dh_list_idx++;
- list_add(&scsi_dh->list, &scsi_dh_list);
- spin_unlock(&list_lock);
-
- for (i = 0; scsi_dh->devlist[i].vendor; i++) {
- scsi_dev_info_list_add_keyed(0,
- scsi_dh->devlist[i].vendor,
- scsi_dh->devlist[i].model,
- NULL,
- scsi_dh->idx,
- SCSI_DEVINFO_DH);
- }
-
- bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
- printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
-
- return SCSI_DH_OK;
-}
-EXPORT_SYMBOL_GPL(scsi_register_device_handler);
-
-/*
- * scsi_unregister_device_handler - register a device handler personality
- * module.
- * @scsi_dh - device handler to be unregistered.
- *
- * Returns 0 on success, -ENODEV if handler not registered.
- */
-int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
-{
- int i;
-
- if (!get_device_handler(scsi_dh->name))
- return -ENODEV;
-
- bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
- scsi_dh_notifier_remove);
-
- for (i = 0; scsi_dh->devlist[i].vendor; i++) {
- scsi_dev_info_list_del_keyed(scsi_dh->devlist[i].vendor,
- scsi_dh->devlist[i].model,
- SCSI_DEVINFO_DH);
- }
-
- spin_lock(&list_lock);
- list_del(&scsi_dh->list);
- spin_unlock(&list_lock);
- printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
-
- return SCSI_DH_OK;
-}
-EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
-
-/*
- * scsi_dh_activate - activate the path associated with the scsi_device
- * corresponding to the given request queue.
- * Returns immediately without waiting for activation to be completed.
- * @q - Request queue that is associated with the scsi_device to be
- * activated.
- * @fn - Function to be called upon completion of the activation.
- * Function fn is called with data (below) and the error code.
- * Function fn may be called from the same calling context. So,
- * do not hold the lock in the caller which may be needed in fn.
- * @data - data passed to the function fn upon completion.
- *
- */
-int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
-{
- int err = 0;
- unsigned long flags;
- struct scsi_device *sdev;
- struct scsi_device_handler *scsi_dh = NULL;
- struct device *dev = NULL;
-
- spin_lock_irqsave(q->queue_lock, flags);
- sdev = q->queuedata;
- if (sdev && sdev->scsi_dh_data)
- scsi_dh = sdev->scsi_dh_data->scsi_dh;
- dev = get_device(&sdev->sdev_gendev);
- if (!scsi_dh || !dev ||
- sdev->sdev_state == SDEV_CANCEL ||
- sdev->sdev_state == SDEV_DEL)
- err = SCSI_DH_NOSYS;
- if (sdev->sdev_state == SDEV_OFFLINE)
- err = SCSI_DH_DEV_OFFLINED;
- spin_unlock_irqrestore(q->queue_lock, flags);
-
- if (err) {
- if (fn)
- fn(data, err);
- goto out;
- }
-
- if (scsi_dh->activate)
- err = scsi_dh->activate(sdev, fn, data);
-out:
- put_device(dev);
- return err;
-}
-EXPORT_SYMBOL_GPL(scsi_dh_activate);
-
-/*
- * scsi_dh_set_params - set the parameters for the device as per the
- * string specified in params.
- * @q - Request queue that is associated with the scsi_device for
- * which the parameters to be set.
- * @params - parameters in the following format
- * "no_of_params\0param1\0param2\0param3\0...\0"
- * for example, string for 2 parameters with value 10 and 21
- * is specified as "2\010\021\0".
- */
-int scsi_dh_set_params(struct request_queue *q, const char *params)
-{
- int err = -SCSI_DH_NOSYS;
- unsigned long flags;
- struct scsi_device *sdev;
- struct scsi_device_handler *scsi_dh = NULL;
-
- spin_lock_irqsave(q->queue_lock, flags);
- sdev = q->queuedata;
- if (sdev && sdev->scsi_dh_data)
- scsi_dh = sdev->scsi_dh_data->scsi_dh;
- if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev))
- err = 0;
- spin_unlock_irqrestore(q->queue_lock, flags);
-
- if (err)
- return err;
- err = scsi_dh->set_params(sdev, params);
- put_device(&sdev->sdev_gendev);
- return err;
-}
-EXPORT_SYMBOL_GPL(scsi_dh_set_params);
-
-/*
- * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
- * the given name. FALSE(0) otherwise.
- * @name - name of the device handler.
- */
-int scsi_dh_handler_exist(const char *name)
-{
- return (get_device_handler(name) != NULL);
-}
-EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
-
-/*
- * scsi_dh_handler_attach - Attach device handler
- * @sdev - sdev the handler should be attached to
- * @name - name of the handler to attach
- */
-int scsi_dh_attach(struct request_queue *q, const char *name)
-{
- unsigned long flags;
- struct scsi_device *sdev;
- struct scsi_device_handler *scsi_dh;
- int err = 0;
-
- scsi_dh = get_device_handler(name);
- if (!scsi_dh)
- return -EINVAL;
-
- spin_lock_irqsave(q->queue_lock, flags);
- sdev = q->queuedata;
- if (!sdev || !get_device(&sdev->sdev_gendev))
- err = -ENODEV;
- spin_unlock_irqrestore(q->queue_lock, flags);
-
- if (!err) {
- err = scsi_dh_handler_attach(sdev, scsi_dh);
- put_device(&sdev->sdev_gendev);
- }
- return err;
-}
-EXPORT_SYMBOL_GPL(scsi_dh_attach);
-
-/*
- * scsi_dh_handler_detach - Detach device handler
- * @sdev - sdev the handler should be detached from
- *
- * This function will detach the device handler only
- * if the sdev is not part of the internal list, ie
- * if it has been attached manually.
- */
-void scsi_dh_detach(struct request_queue *q)
-{
- unsigned long flags;
- struct scsi_device *sdev;
- struct scsi_device_handler *scsi_dh = NULL;
-
- spin_lock_irqsave(q->queue_lock, flags);
- sdev = q->queuedata;
- if (!sdev || !get_device(&sdev->sdev_gendev))
- sdev = NULL;
- spin_unlock_irqrestore(q->queue_lock, flags);
-
- if (!sdev)
- return;
-
- if (sdev->scsi_dh_data) {
- scsi_dh = sdev->scsi_dh_data->scsi_dh;
- scsi_dh_handler_detach(sdev, scsi_dh);
- }
- put_device(&sdev->sdev_gendev);
-}
-EXPORT_SYMBOL_GPL(scsi_dh_detach);
-
-static struct notifier_block scsi_dh_nb = {
- .notifier_call = scsi_dh_notifier
-};
-
-static int __init scsi_dh_init(void)
-{
- int r;
-
- r = scsi_dev_info_add_list(SCSI_DEVINFO_DH, "SCSI Device Handler");
- if (r)
- return r;
-
- r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
-
- if (!r)
- bus_for_each_dev(&scsi_bus_type, NULL, NULL,
- scsi_dh_sysfs_attr_add);
-
- return r;
-}
-
-static void __exit scsi_dh_exit(void)
-{
- bus_for_each_dev(&scsi_bus_type, NULL, NULL,
- scsi_dh_sysfs_attr_remove);
- bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
- scsi_dev_info_remove_list(SCSI_DEVINFO_DH);
-}
-
-module_init(scsi_dh_init);
-module_exit(scsi_dh_exit);
-
-MODULE_DESCRIPTION("SCSI device handler");
-MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 6fec9fe5dc39..6fd89ae33059 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -1,40 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic SCSI-3 ALUA SCSI Device Handler
*
* Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH.
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
+#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
#define ALUA_DH_NAME "alua"
-#define ALUA_DH_VER "1.3"
-
-#define TPGS_STATE_OPTIMIZED 0x0
-#define TPGS_STATE_NONOPTIMIZED 0x1
-#define TPGS_STATE_STANDBY 0x2
-#define TPGS_STATE_UNAVAILABLE 0x3
-#define TPGS_STATE_LBA_DEPENDENT 0x4
-#define TPGS_STATE_OFFLINE 0xe
-#define TPGS_STATE_TRANSITIONING 0xf
+#define ALUA_DH_VER "2.0"
#define TPGS_SUPPORT_NONE 0x00
#define TPGS_SUPPORT_OPTIMIZED 0x01
@@ -44,27 +26,73 @@
#define TPGS_SUPPORT_LBA_DEPENDENT 0x10
#define TPGS_SUPPORT_OFFLINE 0x40
#define TPGS_SUPPORT_TRANSITION 0x80
+#define TPGS_SUPPORT_ALL 0xdf
+
+#define RTPG_FMT_MASK 0x70
+#define RTPG_FMT_EXT_HDR 0x10
#define TPGS_MODE_UNINITIALIZED -1
#define TPGS_MODE_NONE 0x0
#define TPGS_MODE_IMPLICIT 0x1
#define TPGS_MODE_EXPLICIT 0x2
-#define ALUA_INQUIRY_SIZE 36
-#define ALUA_FAILOVER_TIMEOUT (60 * HZ)
+#define ALUA_RTPG_SIZE 128
+#define ALUA_FAILOVER_TIMEOUT 60
#define ALUA_FAILOVER_RETRIES 5
-
-struct alua_dh_data {
+#define ALUA_RTPG_DELAY_MSECS 5
+#define ALUA_RTPG_RETRY_DELAY 2
+
+/* device handler flags */
+#define ALUA_OPTIMIZE_STPG 0x01
+#define ALUA_RTPG_EXT_HDR_UNSUPP 0x02
+/* State machine flags */
+#define ALUA_PG_RUN_RTPG 0x10
+#define ALUA_PG_RUN_STPG 0x20
+#define ALUA_PG_RUNNING 0x40
+
+static uint optimize_stpg;
+module_param(optimize_stpg, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than sending a STPG, when implicit TPGS is supported (0=No,1=Yes). Default is 0.");
+
+static LIST_HEAD(port_group_list);
+static DEFINE_SPINLOCK(port_group_lock);
+static struct workqueue_struct *kaluad_wq;
+
+struct alua_port_group {
+ struct kref kref;
+ struct rcu_head rcu;
+ struct list_head node;
+ struct list_head dh_list;
+ unsigned char device_id_str[256];
+ int device_id_len;
int group_id;
- int rel_port;
int tpgs;
int state;
- unsigned char inq[ALUA_INQUIRY_SIZE];
- unsigned char *buff;
- int bufflen;
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
- int senselen;
+ int pref;
+ int valid_states;
+ unsigned flags; /* used for optimizing STPG */
+ unsigned char transition_tmo;
+ unsigned long expiry;
+ unsigned long interval;
+ struct delayed_work rtpg_work;
+ spinlock_t lock;
+ struct list_head rtpg_list;
+ struct scsi_device *rtpg_sdev;
+};
+
+struct alua_dh_data {
+ struct list_head node;
+ struct alua_port_group __rcu *pg;
+ int group_id;
+ spinlock_t pg_lock;
struct scsi_device *sdev;
+ int init_error;
+ struct mutex init_mutex;
+ bool disabled;
+};
+
+struct alua_queue_data {
+ struct list_head entry;
activate_complete callback_fn;
void *callback_data;
};
@@ -72,290 +100,194 @@ struct alua_dh_data {
#define ALUA_POLICY_SWITCH_CURRENT 0
#define ALUA_POLICY_SWITCH_ALL 1
-static char print_alua_state(int);
-static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *);
-
-static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev)
-{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct alua_dh_data *) scsi_dh_data->buf);
-}
-
-static int realloc_buffer(struct alua_dh_data *h, unsigned len)
-{
- if (h->buff && h->buff != h->inq)
- kfree(h->buff);
-
- h->buff = kmalloc(len, GFP_NOIO);
- if (!h->buff) {
- h->buff = h->inq;
- h->bufflen = ALUA_INQUIRY_SIZE;
- return 1;
- }
- h->bufflen = len;
- return 0;
-}
+static void alua_rtpg_work(struct work_struct *work);
+static bool alua_rtpg_queue(struct alua_port_group *pg,
+ struct scsi_device *sdev,
+ struct alua_queue_data *qdata, bool force);
+static void alua_check(struct scsi_device *sdev, bool force);
-static struct request *get_alua_req(struct scsi_device *sdev,
- void *buffer, unsigned buflen, int rw)
+static void release_port_group(struct kref *kref)
{
- struct request *rq;
- struct request_queue *q = sdev->request_queue;
-
- rq = blk_get_request(q, rw, GFP_NOIO);
-
- if (!rq) {
- sdev_printk(KERN_INFO, sdev,
- "%s: blk_get_request failed\n", __func__);
- return NULL;
- }
-
- if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) {
- blk_put_request(rq);
- sdev_printk(KERN_INFO, sdev,
- "%s: blk_rq_map_kern failed\n", __func__);
- return NULL;
- }
-
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
- rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER;
- rq->retries = ALUA_FAILOVER_RETRIES;
- rq->timeout = ALUA_FAILOVER_TIMEOUT;
-
- return rq;
+ struct alua_port_group *pg;
+
+ pg = container_of(kref, struct alua_port_group, kref);
+ if (pg->rtpg_sdev)
+ flush_delayed_work(&pg->rtpg_work);
+ spin_lock(&port_group_lock);
+ list_del(&pg->node);
+ spin_unlock(&port_group_lock);
+ kfree_rcu(pg, rcu);
}
/*
- * submit_std_inquiry - Issue a standard INQUIRY command
- * @sdev: sdev the command should be send to
+ * submit_rtpg - Issue a REPORT TARGET GROUP STATES command
+ * @sdev: sdev the command should be sent to
*/
-static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
+ int bufflen, struct scsi_sense_hdr *sshdr, int flags)
{
- struct request *rq;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
-
- rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ);
- if (!rq)
- goto done;
+ u8 cdb[MAX_COMMAND_SIZE];
+ blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
+ REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+ const struct scsi_exec_args exec_args = {
+ .sshdr = sshdr,
+ };
/* Prepare the command. */
- rq->cmd[0] = INQUIRY;
- rq->cmd[1] = 0;
- rq->cmd[2] = 0;
- rq->cmd[4] = ALUA_INQUIRY_SIZE;
- rq->cmd_len = COMMAND_SIZE(INQUIRY);
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = h->senselen = 0;
-
- err = blk_execute_rq(rq->q, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: std inquiry failed with %x\n",
- ALUA_DH_NAME, rq->errors);
- h->senselen = rq->sense_len;
- err = SCSI_DH_IO;
- }
- blk_put_request(rq);
-done:
- return err;
+ memset(cdb, 0x0, MAX_COMMAND_SIZE);
+ cdb[0] = MAINTENANCE_IN;
+ if (!(flags & ALUA_RTPG_EXT_HDR_UNSUPP))
+ cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
+ else
+ cdb[1] = MI_REPORT_TARGET_PGS;
+ put_unaligned_be32(bufflen, &cdb[6]);
+
+ return scsi_execute_cmd(sdev, cdb, opf, buff, bufflen,
+ ALUA_FAILOVER_TIMEOUT * HZ,
+ ALUA_FAILOVER_RETRIES, &exec_args);
}
/*
- * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command
- * @sdev: sdev the command should be sent to
+ * submit_stpg - Issue a SET TARGET PORT GROUP command
+ *
+ * Currently we're only setting the current target port group state
+ * to 'active/optimized' and let the array firmware figure out
+ * the states of the remaining groups.
*/
-static int submit_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+static int submit_stpg(struct scsi_device *sdev, int group_id,
+ struct scsi_sense_hdr *sshdr)
{
- struct request *rq;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
+ u8 cdb[MAX_COMMAND_SIZE];
+ unsigned char stpg_data[8];
+ int stpg_len = 8;
+ blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |
+ REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+ const struct scsi_exec_args exec_args = {
+ .sshdr = sshdr,
+ };
- rq = get_alua_req(sdev, h->buff, h->bufflen, READ);
- if (!rq)
- goto done;
+ /* Prepare the data buffer */
+ memset(stpg_data, 0, stpg_len);
+ stpg_data[4] = SCSI_ACCESS_STATE_OPTIMAL;
+ put_unaligned_be16(group_id, &stpg_data[6]);
/* Prepare the command. */
- rq->cmd[0] = INQUIRY;
- rq->cmd[1] = 1;
- rq->cmd[2] = 0x83;
- rq->cmd[4] = h->bufflen;
- rq->cmd_len = COMMAND_SIZE(INQUIRY);
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = h->senselen = 0;
-
- err = blk_execute_rq(rq->q, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: evpd inquiry failed with %x\n",
- ALUA_DH_NAME, rq->errors);
- h->senselen = rq->sense_len;
- err = SCSI_DH_IO;
- }
- blk_put_request(rq);
-done:
- return err;
+ memset(cdb, 0x0, MAX_COMMAND_SIZE);
+ cdb[0] = MAINTENANCE_OUT;
+ cdb[1] = MO_SET_TARGET_PGS;
+ put_unaligned_be32(stpg_len, &cdb[6]);
+
+ return scsi_execute_cmd(sdev, cdb, opf, stpg_data,
+ stpg_len, ALUA_FAILOVER_TIMEOUT * HZ,
+ ALUA_FAILOVER_RETRIES, &exec_args);
}
-/*
- * submit_rtpg - Issue a REPORT TARGET GROUP STATES command
- * @sdev: sdev the command should be sent to
- */
-static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
+static struct alua_port_group *alua_find_get_pg(char *id_str, size_t id_size,
+ int group_id)
{
- struct request *rq;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
+ struct alua_port_group *pg;
- rq = get_alua_req(sdev, h->buff, h->bufflen, READ);
- if (!rq)
- goto done;
+ if (!id_str || !id_size || !strlen(id_str))
+ return NULL;
- /* Prepare the command. */
- rq->cmd[0] = MAINTENANCE_IN;
- rq->cmd[1] = MI_REPORT_TARGET_PGS;
- rq->cmd[6] = (h->bufflen >> 24) & 0xff;
- rq->cmd[7] = (h->bufflen >> 16) & 0xff;
- rq->cmd[8] = (h->bufflen >> 8) & 0xff;
- rq->cmd[9] = h->bufflen & 0xff;
- rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN);
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = h->senselen = 0;
-
- err = blk_execute_rq(rq->q, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: rtpg failed with %x\n",
- ALUA_DH_NAME, rq->errors);
- h->senselen = rq->sense_len;
- err = SCSI_DH_IO;
+ list_for_each_entry(pg, &port_group_list, node) {
+ if (pg->group_id != group_id)
+ continue;
+ if (!pg->device_id_len || pg->device_id_len != id_size)
+ continue;
+ if (strncmp(pg->device_id_str, id_str, id_size))
+ continue;
+ if (!kref_get_unless_zero(&pg->kref))
+ continue;
+ return pg;
}
- blk_put_request(rq);
-done:
- return err;
+
+ return NULL;
}
/*
- * alua_stpg - Evaluate SET TARGET GROUP STATES
- * @sdev: the device to be evaluated
- * @state: the new target group state
+ * alua_alloc_pg - Allocate a new port_group structure
+ * @sdev: scsi device
+ * @group_id: port group id
+ * @tpgs: target port group settings
*
- * Send a SET TARGET GROUP STATES command to the device.
- * We only have to test here if we should resubmit the command;
- * any other error is assumed as a failure.
+ * Allocate a new port_group structure for a given
+ * device.
*/
-static void stpg_endio(struct request *req, int error)
+static struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev,
+ int group_id, int tpgs)
{
- struct alua_dh_data *h = req->end_io_data;
- struct scsi_sense_hdr sense_hdr;
- unsigned err = SCSI_DH_OK;
+ struct alua_port_group *pg, *tmp_pg;
- if (error || host_byte(req->errors) != DID_OK ||
- msg_byte(req->errors) != COMMAND_COMPLETE) {
- err = SCSI_DH_IO;
- goto done;
- }
+ pg = kzalloc(sizeof(struct alua_port_group), GFP_KERNEL);
+ if (!pg)
+ return ERR_PTR(-ENOMEM);
- if (h->senselen > 0) {
- err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
- &sense_hdr);
- if (!err) {
- err = SCSI_DH_IO;
- goto done;
- }
- err = alua_check_sense(h->sdev, &sense_hdr);
- if (err == ADD_TO_MLQUEUE) {
- err = SCSI_DH_RETRY;
- goto done;
- }
- sdev_printk(KERN_INFO, h->sdev,
- "%s: stpg sense code: %02x/%02x/%02x\n",
- ALUA_DH_NAME, sense_hdr.sense_key,
- sense_hdr.asc, sense_hdr.ascq);
- err = SCSI_DH_IO;
- }
- if (err == SCSI_DH_OK) {
- h->state = TPGS_STATE_OPTIMIZED;
- sdev_printk(KERN_INFO, h->sdev,
- "%s: port group %02x switched to state %c\n",
- ALUA_DH_NAME, h->group_id,
- print_alua_state(h->state));
- }
-done:
- req->end_io_data = NULL;
- __blk_put_request(req->q, req);
- if (h->callback_fn) {
- h->callback_fn(h->callback_data, err);
- h->callback_fn = h->callback_data = NULL;
+ pg->device_id_len = scsi_vpd_lun_id(sdev, pg->device_id_str,
+ sizeof(pg->device_id_str));
+ if (pg->device_id_len <= 0) {
+ /*
+ * TPGS supported but no device identification found.
+ * Generate private device identification.
+ */
+ sdev_printk(KERN_INFO, sdev,
+ "%s: No device descriptors found\n",
+ ALUA_DH_NAME);
+ pg->device_id_str[0] = '\0';
+ pg->device_id_len = 0;
+ }
+ pg->group_id = group_id;
+ pg->tpgs = tpgs;
+ pg->state = SCSI_ACCESS_STATE_OPTIMAL;
+ pg->valid_states = TPGS_SUPPORT_ALL;
+ if (optimize_stpg)
+ pg->flags |= ALUA_OPTIMIZE_STPG;
+ kref_init(&pg->kref);
+ INIT_DELAYED_WORK(&pg->rtpg_work, alua_rtpg_work);
+ INIT_LIST_HEAD(&pg->rtpg_list);
+ INIT_LIST_HEAD(&pg->node);
+ INIT_LIST_HEAD(&pg->dh_list);
+ spin_lock_init(&pg->lock);
+
+ spin_lock(&port_group_lock);
+ tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len,
+ group_id);
+ if (tmp_pg) {
+ spin_unlock(&port_group_lock);
+ kfree(pg);
+ return tmp_pg;
}
- return;
-}
-
-/*
- * submit_stpg - Issue a SET TARGET GROUP STATES command
- *
- * Currently we're only setting the current target port group state
- * to 'active/optimized' and let the array firmware figure out
- * the states of the remaining groups.
- */
-static unsigned submit_stpg(struct alua_dh_data *h)
-{
- struct request *rq;
- int stpg_len = 8;
- struct scsi_device *sdev = h->sdev;
-
- /* Prepare the data buffer */
- memset(h->buff, 0, stpg_len);
- h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f;
- h->buff[6] = (h->group_id >> 8) & 0xff;
- h->buff[7] = h->group_id & 0xff;
- rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
- if (!rq)
- return SCSI_DH_RES_TEMP_UNAVAIL;
+ list_add(&pg->node, &port_group_list);
+ spin_unlock(&port_group_lock);
- /* Prepare the command. */
- rq->cmd[0] = MAINTENANCE_OUT;
- rq->cmd[1] = MO_SET_TARGET_PGS;
- rq->cmd[6] = (stpg_len >> 24) & 0xff;
- rq->cmd[7] = (stpg_len >> 16) & 0xff;
- rq->cmd[8] = (stpg_len >> 8) & 0xff;
- rq->cmd[9] = stpg_len & 0xff;
- rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT);
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = h->senselen = 0;
- rq->end_io_data = h;
-
- blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio);
- return SCSI_DH_OK;
+ return pg;
}
/*
- * alua_std_inquiry - Evaluate standard INQUIRY command
+ * alua_check_tpgs - Evaluate TPGS setting
* @sdev: device to be checked
*
- * Just extract the TPGS setting to find out if ALUA
+ * Examine the TPGS setting of the sdev to find out if ALUA
* is supported.
*/
-static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+static int alua_check_tpgs(struct scsi_device *sdev)
{
- int err;
-
- err = submit_std_inquiry(sdev, h);
+ int tpgs = TPGS_MODE_NONE;
- if (err != SCSI_DH_OK)
- return err;
+ /*
+ * ALUA support for non-disk devices is fraught with
+ * difficulties, so disable it for now.
+ */
+ if (sdev->type != TYPE_DISK) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: disable for non-disk devices\n",
+ ALUA_DH_NAME);
+ return tpgs;
+ }
- /* Check TPGS setting */
- h->tpgs = (h->inq[5] >> 4) & 0x3;
- switch (h->tpgs) {
+ tpgs = scsi_device_tpgs(sdev);
+ switch (tpgs) {
case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:
sdev_printk(KERN_INFO, sdev,
"%s: supports implicit and explicit TPGS\n",
@@ -369,71 +301,38 @@ static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n",
ALUA_DH_NAME);
break;
- default:
- h->tpgs = TPGS_MODE_NONE;
+ case TPGS_MODE_NONE:
sdev_printk(KERN_INFO, sdev, "%s: not supported\n",
ALUA_DH_NAME);
- err = SCSI_DH_DEV_UNSUPP;
+ break;
+ default:
+ sdev_printk(KERN_INFO, sdev,
+ "%s: unsupported TPGS setting %d\n",
+ ALUA_DH_NAME, tpgs);
+ tpgs = TPGS_MODE_NONE;
break;
}
- return err;
+ return tpgs;
}
/*
- * alua_vpd_inquiry - Evaluate INQUIRY vpd page 0x83
+ * alua_check_vpd - Evaluate INQUIRY vpd page 0x83
* @sdev: device to be checked
*
* Extract the relative target port and the target port group
* descriptor from the list of identificators.
*/
-static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h,
+ int tpgs)
{
- int len;
- unsigned err;
- unsigned char *d;
-
- retry:
- err = submit_vpd_inquiry(sdev, h);
-
- if (err != SCSI_DH_OK)
- return err;
-
- /* Check if vpd page exceeds initial buffer */
- len = (h->buff[2] << 8) + h->buff[3] + 4;
- if (len > h->bufflen) {
- /* Resubmit with the correct length */
- if (realloc_buffer(h, len)) {
- sdev_printk(KERN_WARNING, sdev,
- "%s: kmalloc buffer failed\n",
- ALUA_DH_NAME);
- /* Temporary failure, bypass */
- return SCSI_DH_DEV_TEMP_BUSY;
- }
- goto retry;
- }
-
- /*
- * Now look for the correct descriptor.
- */
- d = h->buff + 4;
- while (d < h->buff + len) {
- switch (d[1] & 0xf) {
- case 0x4:
- /* Relative target port */
- h->rel_port = (d[6] << 8) + d[7];
- break;
- case 0x5:
- /* Target port group */
- h->group_id = (d[6] << 8) + d[7];
- break;
- default:
- break;
- }
- d += d[3] + 4;
- }
+ int rel_port = -1, group_id;
+ struct alua_port_group *pg, *old_pg = NULL;
+ bool pg_updated = false;
+ unsigned long flags;
- if (h->group_id == -1) {
+ group_id = scsi_vpd_tpg_id(sdev, &rel_port);
+ if (group_id < 0) {
/*
* Internal error; TPGS supported but required
* VPD identification descriptors not present.
@@ -442,93 +341,158 @@ static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
sdev_printk(KERN_INFO, sdev,
"%s: No target port descriptors found\n",
ALUA_DH_NAME);
- h->state = TPGS_STATE_OPTIMIZED;
- h->tpgs = TPGS_MODE_NONE;
- err = SCSI_DH_DEV_UNSUPP;
- } else {
+ return SCSI_DH_DEV_UNSUPP;
+ }
+
+ pg = alua_alloc_pg(sdev, group_id, tpgs);
+ if (IS_ERR(pg)) {
+ if (PTR_ERR(pg) == -ENOMEM)
+ return SCSI_DH_NOMEM;
+ return SCSI_DH_DEV_UNSUPP;
+ }
+ if (pg->device_id_len)
sdev_printk(KERN_INFO, sdev,
- "%s: port group %02x rel port %02x\n",
- ALUA_DH_NAME, h->group_id, h->rel_port);
+ "%s: device %s port group %x rel port %x\n",
+ ALUA_DH_NAME, pg->device_id_str,
+ group_id, rel_port);
+ else
+ sdev_printk(KERN_INFO, sdev,
+ "%s: port group %x rel port %x\n",
+ ALUA_DH_NAME, group_id, rel_port);
+
+ kref_get(&pg->kref);
+
+ /* Check for existing port group references */
+ spin_lock(&h->pg_lock);
+ old_pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock));
+ if (old_pg != pg) {
+ /* port group has changed. Update to new port group */
+ if (h->pg) {
+ spin_lock_irqsave(&old_pg->lock, flags);
+ list_del_rcu(&h->node);
+ spin_unlock_irqrestore(&old_pg->lock, flags);
+ }
+ rcu_assign_pointer(h->pg, pg);
+ pg_updated = true;
}
- return err;
+ spin_lock_irqsave(&pg->lock, flags);
+ if (pg_updated)
+ list_add_rcu(&h->node, &pg->dh_list);
+ spin_unlock_irqrestore(&pg->lock, flags);
+
+ spin_unlock(&h->pg_lock);
+
+ alua_rtpg_queue(pg, sdev, NULL, true);
+ kref_put(&pg->kref, release_port_group);
+
+ if (old_pg)
+ kref_put(&old_pg->kref, release_port_group);
+
+ return SCSI_DH_OK;
}
-static char print_alua_state(int state)
+static char print_alua_state(unsigned char state)
{
switch (state) {
- case TPGS_STATE_OPTIMIZED:
+ case SCSI_ACCESS_STATE_OPTIMAL:
return 'A';
- case TPGS_STATE_NONOPTIMIZED:
+ case SCSI_ACCESS_STATE_ACTIVE:
return 'N';
- case TPGS_STATE_STANDBY:
+ case SCSI_ACCESS_STATE_STANDBY:
return 'S';
- case TPGS_STATE_UNAVAILABLE:
+ case SCSI_ACCESS_STATE_UNAVAILABLE:
return 'U';
- case TPGS_STATE_LBA_DEPENDENT:
+ case SCSI_ACCESS_STATE_LBA:
return 'L';
- case TPGS_STATE_OFFLINE:
+ case SCSI_ACCESS_STATE_OFFLINE:
return 'O';
- case TPGS_STATE_TRANSITIONING:
+ case SCSI_ACCESS_STATE_TRANSITIONING:
return 'T';
default:
return 'X';
}
}
-static int alua_check_sense(struct scsi_device *sdev,
- struct scsi_sense_hdr *sense_hdr)
+static void alua_handle_state_transition(struct scsi_device *sdev)
+{
+ struct alua_dh_data *h = sdev->handler_data;
+ struct alua_port_group *pg;
+
+ rcu_read_lock();
+ pg = rcu_dereference(h->pg);
+ if (pg)
+ pg->state = SCSI_ACCESS_STATE_TRANSITIONING;
+ rcu_read_unlock();
+ alua_check(sdev, false);
+}
+
+static enum scsi_disposition alua_check_sense(struct scsi_device *sdev,
+ struct scsi_sense_hdr *sense_hdr)
{
switch (sense_hdr->sense_key) {
case NOT_READY:
- if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a)
+ if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) {
/*
* LUN Not Accessible - ALUA state transition
*/
- return ADD_TO_MLQUEUE;
- if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0b)
+ alua_handle_state_transition(sdev);
+ return NEEDS_RETRY;
+ }
+ break;
+ case UNIT_ATTENTION:
+ if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) {
/*
- * LUN Not Accessible -- Target port in standby state
+ * LUN Not Accessible - ALUA state transition
*/
- return SUCCESS;
- if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0c)
+ alua_handle_state_transition(sdev);
+ return NEEDS_RETRY;
+ }
+ if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) {
/*
- * LUN Not Accessible -- Target port in unavailable state
+ * Power On, Reset, or Bus Device Reset.
+ * Might have obscured a state transition,
+ * so schedule a recheck.
*/
- return SUCCESS;
- if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x12)
+ alua_check(sdev, true);
+ return ADD_TO_MLQUEUE;
+ }
+ if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x04)
/*
- * LUN Not Ready -- Offline
+ * Device internal reset
*/
- return SUCCESS;
- break;
- case UNIT_ATTENTION:
- if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+ return ADD_TO_MLQUEUE;
+ if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x01)
/*
- * Power On, Reset, or Bus Device Reset, just retry.
+ * Mode Parameters Changed
*/
return ADD_TO_MLQUEUE;
if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) {
/*
* ALUA state changed
*/
+ alua_check(sdev, true);
return ADD_TO_MLQUEUE;
}
if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) {
/*
* Implicit ALUA state transition failed
*/
+ alua_check(sdev, true);
return ADD_TO_MLQUEUE;
}
- if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e) {
+ if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x03)
+ /*
+ * Inquiry data has changed
+ */
+ return ADD_TO_MLQUEUE;
+ if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e)
/*
* REPORTED_LUNS_DATA_HAS_CHANGED is reported
* when switching controllers on targets like
* Intel Multi-Flex. We can just retry.
*/
return ADD_TO_MLQUEUE;
- }
-
break;
}
@@ -536,6 +500,31 @@ static int alua_check_sense(struct scsi_device *sdev,
}
/*
+ * alua_tur - Send a TEST UNIT READY
+ * @sdev: device to which the TEST UNIT READY command should be send
+ *
+ * Send a TEST UNIT READY to @sdev to figure out the device state
+ * Returns SCSI_DH_RETRY if the sense code is NOT READY/ALUA TRANSITIONING,
+ * SCSI_DH_OK if no error occurred, and SCSI_DH_IO otherwise.
+ */
+static int alua_tur(struct scsi_device *sdev)
+{
+ struct scsi_sense_hdr sense_hdr;
+ int retval;
+
+ retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ,
+ ALUA_FAILOVER_RETRIES, &sense_hdr);
+ if ((sense_hdr.sense_key == NOT_READY ||
+ sense_hdr.sense_key == UNIT_ATTENTION) &&
+ sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a)
+ return SCSI_DH_RETRY;
+ else if (retval)
+ return SCSI_DH_IO;
+ else
+ return SCSI_DH_OK;
+}
+
+/*
* alua_rtpg - Evaluate REPORT TARGET GROUP STATES
* @sdev: the device to be evaluated.
*
@@ -543,95 +532,531 @@ static int alua_check_sense(struct scsi_device *sdev,
* Returns SCSI_DH_DEV_OFFLINED if the path is
* found to be unusable.
*/
-static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
+static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
{
struct scsi_sense_hdr sense_hdr;
- int len, k, off, valid_states = 0;
- char *ucp;
+ struct alua_port_group *tmp_pg;
+ int len, k, off, bufflen = ALUA_RTPG_SIZE;
+ int group_id_old, state_old, pref_old, valid_states_old;
+ unsigned char *desc, *buff;
unsigned err;
- unsigned long expiry, interval = 10;
+ int retval;
+ unsigned int tpg_desc_tbl_off;
+ unsigned char orig_transition_tmo;
+ unsigned long flags;
+ bool transitioning_sense = false;
+
+ group_id_old = pg->group_id;
+ state_old = pg->state;
+ pref_old = pg->pref;
+ valid_states_old = pg->valid_states;
+
+ if (!pg->expiry) {
+ unsigned long transition_tmo = ALUA_FAILOVER_TIMEOUT * HZ;
+
+ if (pg->transition_tmo)
+ transition_tmo = pg->transition_tmo * HZ;
+
+ pg->expiry = round_jiffies_up(jiffies + transition_tmo);
+ }
+
+ buff = kzalloc(bufflen, GFP_KERNEL);
+ if (!buff)
+ return SCSI_DH_DEV_TEMP_BUSY;
- expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT);
retry:
- err = submit_rtpg(sdev, h);
+ err = 0;
+ retval = submit_rtpg(sdev, buff, bufflen, &sense_hdr, pg->flags);
- if (err == SCSI_DH_IO && h->senselen > 0) {
- err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
- &sense_hdr);
- if (!err)
+ if (retval) {
+ /*
+ * Some (broken) implementations have a habit of returning
+ * an error during things like firmware update etc.
+ * But if the target only supports active/optimized there's
+ * not much we can do; it's not that we can switch paths
+ * or anything.
+ * So ignore any errors to avoid spurious failures during
+ * path failover.
+ */
+ if ((pg->valid_states & ~TPGS_SUPPORT_OPTIMIZED) == 0) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: ignoring rtpg result %d\n",
+ ALUA_DH_NAME, retval);
+ kfree(buff);
+ return SCSI_DH_OK;
+ }
+ if (retval < 0 || !scsi_sense_valid(&sense_hdr)) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: rtpg failed, result %d\n",
+ ALUA_DH_NAME, retval);
+ kfree(buff);
+ if (retval < 0)
+ return SCSI_DH_DEV_TEMP_BUSY;
+ if (host_byte(retval) == DID_NO_CONNECT)
+ return SCSI_DH_RES_TEMP_UNAVAIL;
return SCSI_DH_IO;
+ }
- err = alua_check_sense(sdev, &sense_hdr);
- if (err == ADD_TO_MLQUEUE && time_before(jiffies, expiry))
+ /*
+ * submit_rtpg() has failed on existing arrays
+ * when requesting extended header info, and
+ * the array doesn't support extended headers,
+ * even though it shouldn't according to T10.
+ * The retry without rtpg_ext_hdr_req set
+ * handles this.
+ * Note: some arrays return a sense key of ILLEGAL_REQUEST
+ * with ASC 00h if they don't support the extended header.
+ */
+ if (!(pg->flags & ALUA_RTPG_EXT_HDR_UNSUPP) &&
+ sense_hdr.sense_key == ILLEGAL_REQUEST) {
+ pg->flags |= ALUA_RTPG_EXT_HDR_UNSUPP;
goto retry;
- sdev_printk(KERN_INFO, sdev,
- "%s: rtpg sense code %02x/%02x/%02x\n",
- ALUA_DH_NAME, sense_hdr.sense_key,
- sense_hdr.asc, sense_hdr.ascq);
- err = SCSI_DH_IO;
+ }
+ /*
+ * If the array returns with 'ALUA state transition'
+ * sense code here it cannot return RTPG data during
+ * transition. So set the state to 'transitioning' directly.
+ */
+ if (sense_hdr.sense_key == NOT_READY &&
+ sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) {
+ transitioning_sense = true;
+ goto skip_rtpg;
+ }
+ /*
+ * Retry on any other UNIT ATTENTION occurred.
+ */
+ if (sense_hdr.sense_key == UNIT_ATTENTION)
+ err = SCSI_DH_RETRY;
+ if (err == SCSI_DH_RETRY &&
+ pg->expiry != 0 && time_before(jiffies, pg->expiry)) {
+ sdev_printk(KERN_ERR, sdev, "%s: rtpg retry\n",
+ ALUA_DH_NAME);
+ scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr);
+ kfree(buff);
+ return err;
+ }
+ sdev_printk(KERN_ERR, sdev, "%s: rtpg failed\n",
+ ALUA_DH_NAME);
+ scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr);
+ kfree(buff);
+ pg->expiry = 0;
+ return SCSI_DH_IO;
}
- if (err != SCSI_DH_OK)
- return err;
- len = (h->buff[0] << 24) + (h->buff[1] << 16) +
- (h->buff[2] << 8) + h->buff[3] + 4;
+ len = get_unaligned_be32(&buff[0]) + 4;
- if (len > h->bufflen) {
+ if (len > bufflen) {
/* Resubmit with the correct length */
- if (realloc_buffer(h, len)) {
+ kfree(buff);
+ bufflen = len;
+ buff = kmalloc(bufflen, GFP_KERNEL);
+ if (!buff) {
sdev_printk(KERN_WARNING, sdev,
"%s: kmalloc buffer failed\n",__func__);
/* Temporary failure, bypass */
+ pg->expiry = 0;
return SCSI_DH_DEV_TEMP_BUSY;
}
goto retry;
}
- for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) {
- if (h->group_id == (ucp[2] << 8) + ucp[3]) {
- h->state = ucp[0] & 0x0f;
- valid_states = ucp[1];
+ orig_transition_tmo = pg->transition_tmo;
+ if ((buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR && buff[5] != 0)
+ pg->transition_tmo = buff[5];
+ else
+ pg->transition_tmo = ALUA_FAILOVER_TIMEOUT;
+
+ if (orig_transition_tmo != pg->transition_tmo) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: transition timeout set to %d seconds\n",
+ ALUA_DH_NAME, pg->transition_tmo);
+ pg->expiry = jiffies + pg->transition_tmo * HZ;
+ }
+
+ if ((buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR)
+ tpg_desc_tbl_off = 8;
+ else
+ tpg_desc_tbl_off = 4;
+
+ for (k = tpg_desc_tbl_off, desc = buff + tpg_desc_tbl_off;
+ k < len;
+ k += off, desc += off) {
+ u16 group_id = get_unaligned_be16(&desc[2]);
+
+ spin_lock_irqsave(&port_group_lock, flags);
+ tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len,
+ group_id);
+ spin_unlock_irqrestore(&port_group_lock, flags);
+ if (tmp_pg) {
+ if (spin_trylock_irqsave(&tmp_pg->lock, flags)) {
+ if ((tmp_pg == pg) ||
+ !(tmp_pg->flags & ALUA_PG_RUNNING)) {
+ struct alua_dh_data *h;
+
+ tmp_pg->state = desc[0] & 0x0f;
+ tmp_pg->pref = desc[0] >> 7;
+ rcu_read_lock();
+ list_for_each_entry_rcu(h,
+ &tmp_pg->dh_list, node) {
+ if (!h->sdev)
+ continue;
+ h->sdev->access_state = desc[0];
+ }
+ rcu_read_unlock();
+ }
+ if (tmp_pg == pg)
+ tmp_pg->valid_states = desc[1];
+ spin_unlock_irqrestore(&tmp_pg->lock, flags);
+ }
+ kref_put(&tmp_pg->kref, release_port_group);
}
- off = 8 + (ucp[7] * 4);
- }
-
- sdev_printk(KERN_INFO, sdev,
- "%s: port group %02x state %c supports %c%c%c%c%c%c%c\n",
- ALUA_DH_NAME, h->group_id, print_alua_state(h->state),
- valid_states&TPGS_SUPPORT_TRANSITION?'T':'t',
- valid_states&TPGS_SUPPORT_OFFLINE?'O':'o',
- valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l',
- valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u',
- valid_states&TPGS_SUPPORT_STANDBY?'S':'s',
- valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n',
- valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a');
-
- switch (h->state) {
- case TPGS_STATE_TRANSITIONING:
- if (time_before(jiffies, expiry)) {
+ off = 8 + (desc[7] * 4);
+ }
+
+ skip_rtpg:
+ spin_lock_irqsave(&pg->lock, flags);
+ if (transitioning_sense)
+ pg->state = SCSI_ACCESS_STATE_TRANSITIONING;
+
+ if (group_id_old != pg->group_id || state_old != pg->state ||
+ pref_old != pg->pref || valid_states_old != pg->valid_states)
+ sdev_printk(KERN_INFO, sdev,
+ "%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n",
+ ALUA_DH_NAME, pg->group_id, print_alua_state(pg->state),
+ pg->pref ? "preferred" : "non-preferred",
+ pg->valid_states&TPGS_SUPPORT_TRANSITION?'T':'t',
+ pg->valid_states&TPGS_SUPPORT_OFFLINE?'O':'o',
+ pg->valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l',
+ pg->valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u',
+ pg->valid_states&TPGS_SUPPORT_STANDBY?'S':'s',
+ pg->valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n',
+ pg->valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a');
+
+ switch (pg->state) {
+ case SCSI_ACCESS_STATE_TRANSITIONING:
+ if (time_before(jiffies, pg->expiry)) {
/* State transition, retry */
- interval *= 10;
- msleep(interval);
- goto retry;
+ pg->interval = ALUA_RTPG_RETRY_DELAY;
+ err = SCSI_DH_RETRY;
+ } else {
+ struct alua_dh_data *h;
+
+ /* Transitioning time exceeded, set port to standby */
+ err = SCSI_DH_IO;
+ pg->state = SCSI_ACCESS_STATE_STANDBY;
+ pg->expiry = 0;
+ rcu_read_lock();
+ list_for_each_entry_rcu(h, &pg->dh_list, node) {
+ if (!h->sdev)
+ continue;
+ h->sdev->access_state =
+ (pg->state & SCSI_ACCESS_STATE_MASK);
+ if (pg->pref)
+ h->sdev->access_state |=
+ SCSI_ACCESS_STATE_PREFERRED;
+ }
+ rcu_read_unlock();
}
- /* Transitioning time exceeded, set port to standby */
- err = SCSI_DH_RETRY;
- h->state = TPGS_STATE_STANDBY;
break;
- case TPGS_STATE_OFFLINE:
- case TPGS_STATE_UNAVAILABLE:
- /* Path unusable for unavailable/offline */
+ case SCSI_ACCESS_STATE_OFFLINE:
+ /* Path unusable */
err = SCSI_DH_DEV_OFFLINED;
+ pg->expiry = 0;
break;
default:
/* Useable path if active */
err = SCSI_DH_OK;
+ pg->expiry = 0;
break;
}
+ spin_unlock_irqrestore(&pg->lock, flags);
+ kfree(buff);
return err;
}
/*
+ * alua_stpg - Issue a SET TARGET PORT GROUP command
+ *
+ * Issue a SET TARGET PORT GROUP command and evaluate the
+ * response. Returns SCSI_DH_RETRY per default to trigger
+ * a re-evaluation of the target group state or SCSI_DH_OK
+ * if no further action needs to be taken.
+ */
+static unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg)
+{
+ int retval;
+ struct scsi_sense_hdr sense_hdr;
+
+ if (!(pg->tpgs & TPGS_MODE_EXPLICIT)) {
+ /* Only implicit ALUA supported, retry */
+ return SCSI_DH_RETRY;
+ }
+ switch (pg->state) {
+ case SCSI_ACCESS_STATE_OPTIMAL:
+ return SCSI_DH_OK;
+ case SCSI_ACCESS_STATE_ACTIVE:
+ if ((pg->flags & ALUA_OPTIMIZE_STPG) &&
+ !pg->pref &&
+ (pg->tpgs & TPGS_MODE_IMPLICIT))
+ return SCSI_DH_OK;
+ break;
+ case SCSI_ACCESS_STATE_STANDBY:
+ case SCSI_ACCESS_STATE_UNAVAILABLE:
+ break;
+ case SCSI_ACCESS_STATE_OFFLINE:
+ return SCSI_DH_IO;
+ case SCSI_ACCESS_STATE_TRANSITIONING:
+ break;
+ default:
+ sdev_printk(KERN_INFO, sdev,
+ "%s: stpg failed, unhandled TPGS state %d",
+ ALUA_DH_NAME, pg->state);
+ return SCSI_DH_NOSYS;
+ }
+ retval = submit_stpg(sdev, pg->group_id, &sense_hdr);
+
+ if (retval) {
+ if (retval < 0 || !scsi_sense_valid(&sense_hdr)) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: stpg failed, result %d",
+ ALUA_DH_NAME, retval);
+ if (retval < 0)
+ return SCSI_DH_DEV_TEMP_BUSY;
+ } else {
+ sdev_printk(KERN_INFO, sdev, "%s: stpg failed\n",
+ ALUA_DH_NAME);
+ scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr);
+ }
+ }
+ /* Retry RTPG */
+ return SCSI_DH_RETRY;
+}
+
+/*
+ * The caller must call scsi_device_put() on the returned pointer if it is not
+ * NULL.
+ */
+static struct scsi_device * __must_check
+alua_rtpg_select_sdev(struct alua_port_group *pg)
+{
+ struct alua_dh_data *h;
+ struct scsi_device *sdev = NULL, *prev_sdev;
+
+ lockdep_assert_held(&pg->lock);
+ if (WARN_ON(!pg->rtpg_sdev))
+ return NULL;
+
+ /*
+ * RCU protection isn't necessary for dh_list here
+ * as we hold pg->lock, but for access to h->pg.
+ */
+ rcu_read_lock();
+ list_for_each_entry_rcu(h, &pg->dh_list, node) {
+ if (!h->sdev)
+ continue;
+ if (h->sdev == pg->rtpg_sdev) {
+ h->disabled = true;
+ continue;
+ }
+ if (rcu_dereference(h->pg) == pg &&
+ !h->disabled &&
+ !scsi_device_get(h->sdev)) {
+ sdev = h->sdev;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (!sdev) {
+ pr_warn("%s: no device found for rtpg\n",
+ (pg->device_id_len ?
+ (char *)pg->device_id_str : "(nameless PG)"));
+ return NULL;
+ }
+
+ sdev_printk(KERN_INFO, sdev, "rtpg retry on different device\n");
+
+ prev_sdev = pg->rtpg_sdev;
+ pg->rtpg_sdev = sdev;
+
+ return prev_sdev;
+}
+
+static void alua_rtpg_work(struct work_struct *work)
+{
+ struct alua_port_group *pg =
+ container_of(work, struct alua_port_group, rtpg_work.work);
+ struct scsi_device *sdev, *prev_sdev = NULL;
+ LIST_HEAD(qdata_list);
+ int err = SCSI_DH_OK;
+ struct alua_queue_data *qdata, *tmp;
+ struct alua_dh_data *h;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pg->lock, flags);
+ sdev = pg->rtpg_sdev;
+ if (!sdev) {
+ WARN_ON(pg->flags & ALUA_PG_RUN_RTPG);
+ WARN_ON(pg->flags & ALUA_PG_RUN_STPG);
+ spin_unlock_irqrestore(&pg->lock, flags);
+ kref_put(&pg->kref, release_port_group);
+ return;
+ }
+ pg->flags |= ALUA_PG_RUNNING;
+ if (pg->flags & ALUA_PG_RUN_RTPG) {
+ int state = pg->state;
+
+ pg->flags &= ~ALUA_PG_RUN_RTPG;
+ spin_unlock_irqrestore(&pg->lock, flags);
+ if (state == SCSI_ACCESS_STATE_TRANSITIONING) {
+ if (alua_tur(sdev) == SCSI_DH_RETRY) {
+ spin_lock_irqsave(&pg->lock, flags);
+ pg->flags &= ~ALUA_PG_RUNNING;
+ pg->flags |= ALUA_PG_RUN_RTPG;
+ if (!pg->interval)
+ pg->interval = ALUA_RTPG_RETRY_DELAY;
+ spin_unlock_irqrestore(&pg->lock, flags);
+ queue_delayed_work(kaluad_wq, &pg->rtpg_work,
+ pg->interval * HZ);
+ return;
+ }
+ /* Send RTPG on failure or if TUR indicates SUCCESS */
+ }
+ err = alua_rtpg(sdev, pg);
+ spin_lock_irqsave(&pg->lock, flags);
+
+ /* If RTPG failed on the current device, try using another */
+ if (err == SCSI_DH_RES_TEMP_UNAVAIL &&
+ (prev_sdev = alua_rtpg_select_sdev(pg)))
+ err = SCSI_DH_IMM_RETRY;
+
+ if (err == SCSI_DH_RETRY || err == SCSI_DH_IMM_RETRY ||
+ pg->flags & ALUA_PG_RUN_RTPG) {
+ pg->flags &= ~ALUA_PG_RUNNING;
+ if (err == SCSI_DH_IMM_RETRY)
+ pg->interval = 0;
+ else if (!pg->interval && !(pg->flags & ALUA_PG_RUN_RTPG))
+ pg->interval = ALUA_RTPG_RETRY_DELAY;
+ pg->flags |= ALUA_PG_RUN_RTPG;
+ spin_unlock_irqrestore(&pg->lock, flags);
+ goto queue_rtpg;
+ }
+ if (err != SCSI_DH_OK)
+ pg->flags &= ~ALUA_PG_RUN_STPG;
+ }
+ if (pg->flags & ALUA_PG_RUN_STPG) {
+ pg->flags &= ~ALUA_PG_RUN_STPG;
+ spin_unlock_irqrestore(&pg->lock, flags);
+ err = alua_stpg(sdev, pg);
+ spin_lock_irqsave(&pg->lock, flags);
+ if (err == SCSI_DH_RETRY || pg->flags & ALUA_PG_RUN_RTPG) {
+ pg->flags |= ALUA_PG_RUN_RTPG;
+ pg->interval = 0;
+ pg->flags &= ~ALUA_PG_RUNNING;
+ spin_unlock_irqrestore(&pg->lock, flags);
+ goto queue_rtpg;
+ }
+ }
+
+ list_splice_init(&pg->rtpg_list, &qdata_list);
+ /*
+ * We went through an RTPG, for good or bad.
+ * Re-enable all devices for the next attempt.
+ */
+ list_for_each_entry(h, &pg->dh_list, node)
+ h->disabled = false;
+ pg->rtpg_sdev = NULL;
+ spin_unlock_irqrestore(&pg->lock, flags);
+
+ if (prev_sdev)
+ scsi_device_put(prev_sdev);
+
+ list_for_each_entry_safe(qdata, tmp, &qdata_list, entry) {
+ list_del(&qdata->entry);
+ if (qdata->callback_fn)
+ qdata->callback_fn(qdata->callback_data, err);
+ kfree(qdata);
+ }
+ spin_lock_irqsave(&pg->lock, flags);
+ pg->flags &= ~ALUA_PG_RUNNING;
+ spin_unlock_irqrestore(&pg->lock, flags);
+ scsi_device_put(sdev);
+ kref_put(&pg->kref, release_port_group);
+ return;
+
+queue_rtpg:
+ if (prev_sdev)
+ scsi_device_put(prev_sdev);
+ queue_delayed_work(kaluad_wq, &pg->rtpg_work, pg->interval * HZ);
+}
+
+/**
+ * alua_rtpg_queue() - cause RTPG to be submitted asynchronously
+ * @pg: ALUA port group associated with @sdev.
+ * @sdev: SCSI device for which to submit an RTPG.
+ * @qdata: Information about the callback to invoke after the RTPG.
+ * @force: Whether or not to submit an RTPG if a work item that will submit an
+ * RTPG already has been scheduled.
+ *
+ * Returns true if and only if alua_rtpg_work() will be called asynchronously.
+ * That function is responsible for calling @qdata->fn().
+ *
+ * Context: may be called from atomic context (alua_check()) only if the caller
+ * holds an sdev reference.
+ */
+static bool alua_rtpg_queue(struct alua_port_group *pg,
+ struct scsi_device *sdev,
+ struct alua_queue_data *qdata, bool force)
+{
+ int start_queue = 0;
+ unsigned long flags;
+
+ if (WARN_ON_ONCE(!pg) || scsi_device_get(sdev))
+ return false;
+
+ spin_lock_irqsave(&pg->lock, flags);
+ if (qdata) {
+ list_add_tail(&qdata->entry, &pg->rtpg_list);
+ pg->flags |= ALUA_PG_RUN_STPG;
+ force = true;
+ }
+ if (pg->rtpg_sdev == NULL) {
+ struct alua_dh_data *h = sdev->handler_data;
+
+ rcu_read_lock();
+ if (h && rcu_dereference(h->pg) == pg) {
+ pg->interval = 0;
+ pg->flags |= ALUA_PG_RUN_RTPG;
+ kref_get(&pg->kref);
+ pg->rtpg_sdev = sdev;
+ start_queue = 1;
+ }
+ rcu_read_unlock();
+ } else if (!(pg->flags & ALUA_PG_RUN_RTPG) && force) {
+ pg->flags |= ALUA_PG_RUN_RTPG;
+ /* Do not queue if the worker is already running */
+ if (!(pg->flags & ALUA_PG_RUNNING)) {
+ kref_get(&pg->kref);
+ start_queue = 1;
+ }
+ }
+
+ spin_unlock_irqrestore(&pg->lock, flags);
+
+ if (start_queue) {
+ if (queue_delayed_work(kaluad_wq, &pg->rtpg_work,
+ msecs_to_jiffies(ALUA_RTPG_DELAY_MSECS)))
+ sdev = NULL;
+ else
+ kref_put(&pg->kref, release_port_group);
+ }
+ if (sdev)
+ scsi_device_put(sdev);
+
+ return true;
+}
+
+/*
* alua_initialize - Initialize ALUA state
* @sdev: the device to be initialized
*
@@ -640,22 +1065,58 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
*/
static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h)
{
- int err;
-
- err = alua_std_inquiry(sdev, h);
- if (err != SCSI_DH_OK)
- goto out;
+ int err = SCSI_DH_DEV_UNSUPP, tpgs;
+
+ mutex_lock(&h->init_mutex);
+ h->disabled = false;
+ tpgs = alua_check_tpgs(sdev);
+ if (tpgs != TPGS_MODE_NONE)
+ err = alua_check_vpd(sdev, h, tpgs);
+ h->init_error = err;
+ mutex_unlock(&h->init_mutex);
+ return err;
+}
+/*
+ * alua_set_params - set/unset the optimize flag
+ * @sdev: device on the path to be activated
+ * params - parameters in the following format
+ * "no_of_params\0param1\0param2\0param3\0...\0"
+ * For example, to set the flag pass the following parameters
+ * from multipath.conf
+ * hardware_handler "2 alua 1"
+ */
+static int alua_set_params(struct scsi_device *sdev, const char *params)
+{
+ struct alua_dh_data *h = sdev->handler_data;
+ struct alua_port_group *pg = NULL;
+ unsigned int optimize = 0, argc;
+ const char *p = params;
+ int result = SCSI_DH_OK;
+ unsigned long flags;
- err = alua_vpd_inquiry(sdev, h);
- if (err != SCSI_DH_OK)
- goto out;
+ if ((sscanf(params, "%u", &argc) != 1) || (argc != 1))
+ return -EINVAL;
- err = alua_rtpg(sdev, h);
- if (err != SCSI_DH_OK)
- goto out;
+ while (*p++)
+ ;
+ if ((sscanf(p, "%u", &optimize) != 1) || (optimize > 1))
+ return -EINVAL;
-out:
- return err;
+ rcu_read_lock();
+ pg = rcu_dereference(h->pg);
+ if (!pg) {
+ rcu_read_unlock();
+ return -ENXIO;
+ }
+ spin_lock_irqsave(&pg->lock, flags);
+ if (optimize)
+ pg->flags |= ALUA_OPTIMIZE_STPG;
+ else
+ pg->flags &= ~ALUA_OPTIMIZE_STPG;
+ spin_unlock_irqrestore(&pg->lock, flags);
+ rcu_read_unlock();
+
+ return result;
}
/*
@@ -671,26 +1132,39 @@ out:
static int alua_activate(struct scsi_device *sdev,
activate_complete fn, void *data)
{
- struct alua_dh_data *h = get_alua_data(sdev);
+ struct alua_dh_data *h = sdev->handler_data;
int err = SCSI_DH_OK;
+ struct alua_queue_data *qdata;
+ struct alua_port_group *pg;
- if (h->group_id != -1) {
- err = alua_rtpg(sdev, h);
- if (err != SCSI_DH_OK)
- goto out;
+ qdata = kzalloc(sizeof(*qdata), GFP_KERNEL);
+ if (!qdata) {
+ err = SCSI_DH_RES_TEMP_UNAVAIL;
+ goto out;
}
-
- if (h->tpgs & TPGS_MODE_EXPLICIT &&
- h->state != TPGS_STATE_OPTIMIZED &&
- h->state != TPGS_STATE_LBA_DEPENDENT) {
- h->callback_fn = fn;
- h->callback_data = data;
- err = submit_stpg(h);
- if (err == SCSI_DH_OK)
- return 0;
- h->callback_fn = h->callback_data = NULL;
+ qdata->callback_fn = fn;
+ qdata->callback_data = data;
+
+ mutex_lock(&h->init_mutex);
+ rcu_read_lock();
+ pg = rcu_dereference(h->pg);
+ if (!pg || !kref_get_unless_zero(&pg->kref)) {
+ rcu_read_unlock();
+ kfree(qdata);
+ err = h->init_error;
+ mutex_unlock(&h->init_mutex);
+ goto out;
}
+ rcu_read_unlock();
+ mutex_unlock(&h->init_mutex);
+ if (alua_rtpg_queue(pg, sdev, qdata, true)) {
+ fn = NULL;
+ } else {
+ kfree(qdata);
+ err = SCSI_DH_DEV_OFFLINED;
+ }
+ kref_put(&pg->kref, release_port_group);
out:
if (fn)
fn(data, err);
@@ -698,59 +1172,63 @@ out:
}
/*
+ * alua_check - check path status
+ * @sdev: device on the path to be checked
+ *
+ * Check the device status
+ */
+static void alua_check(struct scsi_device *sdev, bool force)
+{
+ struct alua_dh_data *h = sdev->handler_data;
+ struct alua_port_group *pg;
+
+ rcu_read_lock();
+ pg = rcu_dereference(h->pg);
+ if (!pg || !kref_get_unless_zero(&pg->kref)) {
+ rcu_read_unlock();
+ return;
+ }
+ rcu_read_unlock();
+ alua_rtpg_queue(pg, sdev, NULL, force);
+ kref_put(&pg->kref, release_port_group);
+}
+
+/*
* alua_prep_fn - request callback
*
* Fail I/O to all paths not in state
* active/optimized or active/non-optimized.
*/
-static int alua_prep_fn(struct scsi_device *sdev, struct request *req)
+static blk_status_t alua_prep_fn(struct scsi_device *sdev, struct request *req)
{
- struct alua_dh_data *h = get_alua_data(sdev);
- int ret = BLKPREP_OK;
+ struct alua_dh_data *h = sdev->handler_data;
+ struct alua_port_group *pg;
+ unsigned char state = SCSI_ACCESS_STATE_OPTIMAL;
- if (h->state == TPGS_STATE_TRANSITIONING)
- ret = BLKPREP_DEFER;
- else if (h->state != TPGS_STATE_OPTIMIZED &&
- h->state != TPGS_STATE_NONOPTIMIZED &&
- h->state != TPGS_STATE_LBA_DEPENDENT) {
- ret = BLKPREP_KILL;
- req->cmd_flags |= REQ_QUIET;
- }
- return ret;
+ rcu_read_lock();
+ pg = rcu_dereference(h->pg);
+ if (pg)
+ state = pg->state;
+ rcu_read_unlock();
+ switch (state) {
+ case SCSI_ACCESS_STATE_OPTIMAL:
+ case SCSI_ACCESS_STATE_ACTIVE:
+ case SCSI_ACCESS_STATE_LBA:
+ case SCSI_ACCESS_STATE_TRANSITIONING:
+ return BLK_STS_OK;
+ default:
+ req->rq_flags |= RQF_QUIET;
+ return BLK_STS_IOERR;
+ }
}
-static const struct scsi_dh_devlist alua_dev_list[] = {
- {"HP", "MSA VOLUME" },
- {"HP", "HSV101" },
- {"HP", "HSV111" },
- {"HP", "HSV200" },
- {"HP", "HSV210" },
- {"HP", "HSV300" },
- {"IBM", "2107900" },
- {"IBM", "2145" },
- {"Pillar", "Axiom" },
- {"Intel", "Multi-Flex"},
- {"NETAPP", "LUN"},
- {"NETAPP", "LUN C-Mode"},
- {"AIX", "NVDISK"},
- {"Promise", "VTrak"},
- {NULL, NULL}
-};
-
-static int alua_bus_attach(struct scsi_device *sdev);
-static void alua_bus_detach(struct scsi_device *sdev);
+static void alua_rescan(struct scsi_device *sdev)
+{
+ struct alua_dh_data *h = sdev->handler_data;
-static struct scsi_device_handler alua_dh = {
- .name = ALUA_DH_NAME,
- .module = THIS_MODULE,
- .devlist = alua_dev_list,
- .attach = alua_bus_attach,
- .detach = alua_bus_detach,
- .prep_fn = alua_prep_fn,
- .check_sense = alua_check_sense,
- .activate = alua_activate,
-};
+ alua_initialize(sdev, h);
+}
/*
* alua_bus_attach - Attach device handler
@@ -758,46 +1236,28 @@ static struct scsi_device_handler alua_dh = {
*/
static int alua_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct alua_dh_data *h;
- unsigned long flags;
- int err = SCSI_DH_OK;
-
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
- ALUA_DH_NAME);
- return -ENOMEM;
- }
+ int err;
- scsi_dh_data->scsi_dh = &alua_dh;
- h = (struct alua_dh_data *) scsi_dh_data->buf;
- h->tpgs = TPGS_MODE_UNINITIALIZED;
- h->state = TPGS_STATE_OPTIMIZED;
- h->group_id = -1;
- h->rel_port = -1;
- h->buff = h->inq;
- h->bufflen = ALUA_INQUIRY_SIZE;
+ h = kzalloc(sizeof(*h) , GFP_KERNEL);
+ if (!h)
+ return SCSI_DH_NOMEM;
+ spin_lock_init(&h->pg_lock);
+ rcu_assign_pointer(h->pg, NULL);
+ h->init_error = SCSI_DH_OK;
h->sdev = sdev;
+ INIT_LIST_HEAD(&h->node);
+ mutex_init(&h->init_mutex);
err = alua_initialize(sdev, h);
- if ((err != SCSI_DH_OK) && (err != SCSI_DH_DEV_OFFLINED))
+ if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED)
goto failed;
- if (!try_module_get(THIS_MODULE))
- goto failed;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
- return 0;
-
+ sdev->handler_data = h;
+ return SCSI_DH_OK;
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n", ALUA_DH_NAME);
- return -EINVAL;
+ kfree(h);
+ return err;
}
/*
@@ -806,37 +1266,57 @@ failed:
*/
static void alua_bus_detach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
- struct alua_dh_data *h;
- unsigned long flags;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- scsi_dh_data = sdev->scsi_dh_data;
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
- h = (struct alua_dh_data *) scsi_dh_data->buf;
- if (h->buff && h->inq != h->buff)
- kfree(h->buff);
- kfree(scsi_dh_data);
- module_put(THIS_MODULE);
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", ALUA_DH_NAME);
+ struct alua_dh_data *h = sdev->handler_data;
+ struct alua_port_group *pg;
+
+ spin_lock(&h->pg_lock);
+ pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock));
+ rcu_assign_pointer(h->pg, NULL);
+ spin_unlock(&h->pg_lock);
+ if (pg) {
+ spin_lock_irq(&pg->lock);
+ list_del_rcu(&h->node);
+ spin_unlock_irq(&pg->lock);
+ kref_put(&pg->kref, release_port_group);
+ }
+ sdev->handler_data = NULL;
+ synchronize_rcu();
+ kfree(h);
}
+static struct scsi_device_handler alua_dh = {
+ .name = ALUA_DH_NAME,
+ .module = THIS_MODULE,
+ .attach = alua_bus_attach,
+ .detach = alua_bus_detach,
+ .prep_fn = alua_prep_fn,
+ .check_sense = alua_check_sense,
+ .activate = alua_activate,
+ .rescan = alua_rescan,
+ .set_params = alua_set_params,
+};
+
static int __init alua_init(void)
{
int r;
+ kaluad_wq = alloc_workqueue("kaluad", WQ_MEM_RECLAIM | WQ_PERCPU, 0);
+ if (!kaluad_wq)
+ return -ENOMEM;
+
r = scsi_register_device_handler(&alua_dh);
- if (r != 0)
+ if (r != 0) {
printk(KERN_ERR "%s: Failed to register scsi device handler",
ALUA_DH_NAME);
+ destroy_workqueue(kaluad_wq);
+ }
return r;
}
static void __exit alua_exit(void)
{
scsi_unregister_device_handler(&alua_dh);
+ destroy_workqueue(kaluad_wq);
}
module_init(alua_init);
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 48441f6908a4..3cf88db2d5b2 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Target driver for EMC CLARiiON AX/CX-series hardware.
* Based on code from Lars Marowsky-Bree <lmb@suse.de>
@@ -5,22 +6,9 @@
*
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
* Copyright (C) 2006 Mike Christie
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/slab.h>
+#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
@@ -87,12 +75,6 @@ struct clariion_dh_data {
*/
unsigned char buffer[CLARIION_BUFFER_SIZE];
/*
- * SCSI sense buffer for commands -- assumes serial issuance
- * and completion sequence of all commands for same multipath.
- */
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
- unsigned int senselen;
- /*
* LUN state
*/
int lun_state;
@@ -112,55 +94,41 @@ struct clariion_dh_data {
int current_sp;
};
-static inline struct clariion_dh_data
- *get_clariion_data(struct scsi_device *sdev)
-{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct clariion_dh_data *) scsi_dh_data->buf);
-}
-
/*
* Parse MODE_SELECT cmd reply.
*/
-static int trespass_endio(struct scsi_device *sdev, char *sense)
+static int trespass_endio(struct scsi_device *sdev,
+ struct scsi_sense_hdr *sshdr)
{
int err = SCSI_DH_IO;
- struct scsi_sense_hdr sshdr;
- if (!scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
- sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, "
- "0x%2x, 0x%2x while sending CLARiiON trespass "
- "command.\n", CLARIION_NAME, sshdr.sense_key,
- sshdr.asc, sshdr.ascq);
-
- if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
- (sshdr.ascq == 0x00)) {
- /*
- * Array based copy in progress -- do not send
- * mode_select or copy will be aborted mid-stream.
- */
- sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in "
- "progress while sending CLARiiON trespass "
- "command.\n", CLARIION_NAME);
- err = SCSI_DH_DEV_TEMP_BUSY;
- } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
- (sshdr.ascq == 0x03)) {
- /*
- * LUN Not Ready - Manual Intervention Required
- * indicates in-progress ucode upgrade (NDU).
- */
- sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress "
- "ucode upgrade NDU operation while sending "
- "CLARiiON trespass command.\n", CLARIION_NAME);
- err = SCSI_DH_DEV_TEMP_BUSY;
- } else
- err = SCSI_DH_DEV_FAILED;
- } else {
- sdev_printk(KERN_INFO, sdev,
- "%s: failed to send MODE SELECT, no sense available\n",
- CLARIION_NAME);
- }
+ sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, "
+ "0x%2x, 0x%2x while sending CLARiiON trespass "
+ "command.\n", CLARIION_NAME, sshdr->sense_key,
+ sshdr->asc, sshdr->ascq);
+
+ if (sshdr->sense_key == 0x05 && sshdr->asc == 0x04 &&
+ sshdr->ascq == 0x00) {
+ /*
+ * Array based copy in progress -- do not send
+ * mode_select or copy will be aborted mid-stream.
+ */
+ sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in "
+ "progress while sending CLARiiON trespass "
+ "command.\n", CLARIION_NAME);
+ err = SCSI_DH_DEV_TEMP_BUSY;
+ } else if (sshdr->sense_key == 0x02 && sshdr->asc == 0x04 &&
+ sshdr->ascq == 0x03) {
+ /*
+ * LUN Not Ready - Manual Intervention Required
+ * indicates in-progress ucode upgrade (NDU).
+ */
+ sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress "
+ "ucode upgrade NDU operation while sending "
+ "CLARiiON trespass command.\n", CLARIION_NAME);
+ err = SCSI_DH_DEV_TEMP_BUSY;
+ } else
+ err = SCSI_DH_DEV_FAILED;
return err;
}
@@ -206,7 +174,12 @@ static int parse_sp_info_reply(struct scsi_device *sdev,
csdev->lun_state = csdev->buffer[4];
csdev->current_sp = csdev->buffer[8];
csdev->port = csdev->buffer[7];
-
+ if (csdev->lun_state == CLARIION_LUN_OWNED)
+ sdev->access_state = SCSI_ACCESS_STATE_OPTIMAL;
+ else
+ sdev->access_state = SCSI_ACCESS_STATE_STANDBY;
+ if (csdev->default_sp == csdev->current_sp)
+ sdev->access_state |= SCSI_ACCESS_STATE_PREFERRED;
out:
return err;
}
@@ -259,103 +232,18 @@ out:
return sp_model;
}
-/*
- * Get block request for REQ_BLOCK_PC command issued to path. Currently
- * limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
- *
- * Uses data and sense buffers in hardware handler context structure and
- * assumes serial servicing of commands, both issuance and completion.
- */
-static struct request *get_req(struct scsi_device *sdev, int cmd,
- unsigned char *buffer)
-{
- struct request *rq;
- int len = 0;
-
- rq = blk_get_request(sdev->request_queue,
- (cmd != INQUIRY) ? WRITE : READ, GFP_NOIO);
- if (!rq) {
- sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
- return NULL;
- }
-
- rq->cmd_len = COMMAND_SIZE(cmd);
- rq->cmd[0] = cmd;
-
- switch (cmd) {
- case MODE_SELECT:
- len = sizeof(short_trespass);
- rq->cmd[1] = 0x10;
- rq->cmd[4] = len;
- break;
- case MODE_SELECT_10:
- len = sizeof(long_trespass);
- rq->cmd[1] = 0x10;
- rq->cmd[8] = len;
- break;
- case INQUIRY:
- len = CLARIION_BUFFER_SIZE;
- rq->cmd[4] = len;
- memset(buffer, 0, len);
- break;
- default:
- BUG_ON(1);
- break;
- }
-
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
- rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER;
- rq->timeout = CLARIION_TIMEOUT;
- rq->retries = CLARIION_RETRIES;
-
- if (blk_rq_map_kern(rq->q, rq, buffer, len, GFP_NOIO)) {
- blk_put_request(rq);
- return NULL;
- }
-
- return rq;
-}
-
-static int send_inquiry_cmd(struct scsi_device *sdev, int page,
- struct clariion_dh_data *csdev)
-{
- struct request *rq = get_req(sdev, INQUIRY, csdev->buffer);
- int err;
-
- if (!rq)
- return SCSI_DH_RES_TEMP_UNAVAIL;
-
- rq->sense = csdev->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = csdev->senselen = 0;
-
- rq->cmd[0] = INQUIRY;
- if (page != 0) {
- rq->cmd[1] = 1;
- rq->cmd[2] = page;
- }
- err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: failed to send %s INQUIRY: %x\n",
- CLARIION_NAME, page?"EVPD":"standard",
- rq->errors);
- csdev->senselen = rq->sense_len;
- err = SCSI_DH_IO;
- }
-
- blk_put_request(rq);
-
- return err;
-}
-
static int send_trespass_cmd(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{
- struct request *rq;
unsigned char *page22;
- int err, len, cmd;
+ unsigned char cdb[MAX_COMMAND_SIZE];
+ int err, res = SCSI_DH_OK, len;
+ struct scsi_sense_hdr sshdr;
+ blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |
+ REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+ const struct scsi_exec_args exec_args = {
+ .sshdr = &sshdr,
+ };
if (csdev->flags & CLARIION_SHORT_TRESPASS) {
page22 = short_trespass;
@@ -363,44 +251,40 @@ static int send_trespass_cmd(struct scsi_device *sdev,
/* Set Honor Reservations bit */
page22[6] |= 0x80;
len = sizeof(short_trespass);
- cmd = MODE_SELECT;
+ cdb[0] = MODE_SELECT;
+ cdb[1] = 0x10;
+ cdb[4] = len;
} else {
page22 = long_trespass;
if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
/* Set Honor Reservations bit */
page22[10] |= 0x80;
len = sizeof(long_trespass);
- cmd = MODE_SELECT_10;
+ cdb[0] = MODE_SELECT_10;
+ cdb[8] = len;
}
BUG_ON((len > CLARIION_BUFFER_SIZE));
memcpy(csdev->buffer, page22, len);
- rq = get_req(sdev, cmd, csdev->buffer);
- if (!rq)
- return SCSI_DH_RES_TEMP_UNAVAIL;
-
- rq->sense = csdev->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = csdev->senselen = 0;
-
- err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
- if (err == -EIO) {
- if (rq->sense_len) {
- err = trespass_endio(sdev, csdev->sense);
- } else {
+ err = scsi_execute_cmd(sdev, cdb, opf, csdev->buffer, len,
+ CLARIION_TIMEOUT * HZ, CLARIION_RETRIES,
+ &exec_args);
+ if (err) {
+ if (scsi_sense_valid(&sshdr))
+ res = trespass_endio(sdev, &sshdr);
+ else {
sdev_printk(KERN_INFO, sdev,
"%s: failed to send MODE SELECT: %x\n",
- CLARIION_NAME, rq->errors);
+ CLARIION_NAME, err);
+ res = SCSI_DH_IO;
}
}
- blk_put_request(rq);
-
- return err;
+ return res;
}
-static int clariion_check_sense(struct scsi_device *sdev,
- struct scsi_sense_hdr *sense_hdr)
+static enum scsi_disposition clariion_check_sense(struct scsi_device *sdev,
+ struct scsi_sense_hdr *sense_hdr)
{
switch (sense_hdr->sense_key) {
case NOT_READY:
@@ -447,40 +331,26 @@ static int clariion_check_sense(struct scsi_device *sdev,
return SCSI_RETURN_NOT_HANDLED;
}
-static int clariion_prep_fn(struct scsi_device *sdev, struct request *req)
+static blk_status_t clariion_prep_fn(struct scsi_device *sdev,
+ struct request *req)
{
- struct clariion_dh_data *h = get_clariion_data(sdev);
- int ret = BLKPREP_OK;
+ struct clariion_dh_data *h = sdev->handler_data;
if (h->lun_state != CLARIION_LUN_OWNED) {
- ret = BLKPREP_KILL;
- req->cmd_flags |= REQ_QUIET;
+ req->rq_flags |= RQF_QUIET;
+ return BLK_STS_IOERR;
}
- return ret;
+ return BLK_STS_OK;
}
static int clariion_std_inquiry(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{
- int err;
+ int err = SCSI_DH_OK;
char *sp_model;
- err = send_inquiry_cmd(sdev, 0, csdev);
- if (err != SCSI_DH_OK && csdev->senselen) {
- struct scsi_sense_hdr sshdr;
-
- if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
- &sshdr)) {
- sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
- "%02x/%02x/%02x\n", CLARIION_NAME,
- sshdr.sense_key, sshdr.asc, sshdr.ascq);
- }
- err = SCSI_DH_IO;
- goto out;
- }
-
- sp_model = parse_sp_model(sdev, csdev->buffer);
+ sp_model = parse_sp_model(sdev, sdev->inquiry);
if (!sp_model) {
err = SCSI_DH_DEV_UNSUPP;
goto out;
@@ -502,37 +372,19 @@ out:
static int clariion_send_inquiry(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{
- int err, retry = CLARIION_RETRIES;
-
-retry:
- err = send_inquiry_cmd(sdev, 0xC0, csdev);
- if (err != SCSI_DH_OK && csdev->senselen) {
- struct scsi_sense_hdr sshdr;
-
- err = scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
- &sshdr);
- if (!err)
- return SCSI_DH_IO;
-
- err = clariion_check_sense(sdev, &sshdr);
- if (retry > 0 && err == ADD_TO_MLQUEUE) {
- retry--;
- goto retry;
- }
- sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
- "%02x/%02x/%02x\n", CLARIION_NAME,
- sshdr.sense_key, sshdr.asc, sshdr.ascq);
- err = SCSI_DH_IO;
- } else {
+ int err = SCSI_DH_IO;
+
+ if (!scsi_get_vpd_page(sdev, 0xC0, csdev->buffer,
+ CLARIION_BUFFER_SIZE))
err = parse_sp_info_reply(sdev, csdev);
- }
+
return err;
}
static int clariion_activate(struct scsi_device *sdev,
activate_complete fn, void *data)
{
- struct clariion_dh_data *csdev = get_clariion_data(sdev);
+ struct clariion_dh_data *csdev = sdev->handler_data;
int result;
result = clariion_send_inquiry(sdev, csdev);
@@ -573,7 +425,7 @@ done:
*/
static int clariion_set_params(struct scsi_device *sdev, const char *params)
{
- struct clariion_dh_data *csdev = get_clariion_data(sdev);
+ struct clariion_dh_data *csdev = sdev->handler_data;
unsigned int hr = 0, st = 0, argc;
const char *p = params;
int result = SCSI_DH_OK;
@@ -621,45 +473,14 @@ done:
return result;
}
-static const struct scsi_dh_devlist clariion_dev_list[] = {
- {"DGC", "RAID"},
- {"DGC", "DISK"},
- {"DGC", "VRAID"},
- {NULL, NULL},
-};
-
-static int clariion_bus_attach(struct scsi_device *sdev);
-static void clariion_bus_detach(struct scsi_device *sdev);
-
-static struct scsi_device_handler clariion_dh = {
- .name = CLARIION_NAME,
- .module = THIS_MODULE,
- .devlist = clariion_dev_list,
- .attach = clariion_bus_attach,
- .detach = clariion_bus_detach,
- .check_sense = clariion_check_sense,
- .activate = clariion_activate,
- .prep_fn = clariion_prep_fn,
- .set_params = clariion_set_params,
-};
-
static int clariion_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct clariion_dh_data *h;
- unsigned long flags;
int err;
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
- CLARIION_NAME);
- return -ENOMEM;
- }
-
- scsi_dh_data->scsi_dh = &clariion_dh;
- h = (struct clariion_dh_data *) scsi_dh_data->buf;
+ h = kzalloc(sizeof(*h) , GFP_KERNEL);
+ if (!h)
+ return SCSI_DH_NOMEM;
h->lun_state = CLARIION_LUN_UNINITIALIZED;
h->default_sp = CLARIION_UNBOUND_LU;
h->current_sp = CLARIION_UNBOUND_LU;
@@ -672,45 +493,37 @@ static int clariion_bus_attach(struct scsi_device *sdev)
if (err != SCSI_DH_OK)
goto failed;
- if (!try_module_get(THIS_MODULE))
- goto failed;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
sdev_printk(KERN_INFO, sdev,
"%s: connected to SP %c Port %d (%s, default SP %c)\n",
CLARIION_NAME, h->current_sp + 'A',
h->port, lun_state[h->lun_state],
h->default_sp + 'A');
- return 0;
+ sdev->handler_data = h;
+ return SCSI_DH_OK;
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
- CLARIION_NAME);
- return -EINVAL;
+ kfree(h);
+ return err;
}
static void clariion_bus_detach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
- unsigned long flags;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- scsi_dh_data = sdev->scsi_dh_data;
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n",
- CLARIION_NAME);
-
- kfree(scsi_dh_data);
- module_put(THIS_MODULE);
+ kfree(sdev->handler_data);
+ sdev->handler_data = NULL;
}
+static struct scsi_device_handler clariion_dh = {
+ .name = CLARIION_NAME,
+ .module = THIS_MODULE,
+ .attach = clariion_bus_attach,
+ .detach = clariion_bus_detach,
+ .check_sense = clariion_check_sense,
+ .activate = clariion_activate,
+ .prep_fn = clariion_prep_fn,
+ .set_params = clariion_set_params,
+};
+
static int __init clariion_init(void)
{
int r;
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
index b479f1eef968..b6eaf49dfb00 100644
--- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c
+++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
* upgraded.
@@ -5,23 +6,10 @@
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
* Copyright (C) 2006 Mike Christie
* Copyright (C) 2008 Hannes Reinecke <hare@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/slab.h>
+#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
@@ -37,24 +25,14 @@
#define HP_SW_PATH_PASSIVE 1
struct hp_sw_dh_data {
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
int path_state;
int retries;
int retry_cnt;
struct scsi_device *sdev;
- activate_complete callback_fn;
- void *callback_data;
};
static int hp_sw_start_stop(struct hp_sw_dh_data *);
-static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
-{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
-}
-
/*
* tur_done - Handle TEST UNIT READY return status
* @sdev: sdev the command has been sent to
@@ -62,43 +40,31 @@ static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
*
* Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path
*/
-static int tur_done(struct scsi_device *sdev, unsigned char *sense)
+static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h,
+ struct scsi_sense_hdr *sshdr)
{
- struct scsi_sense_hdr sshdr;
- int ret;
+ int ret = SCSI_DH_IO;
- ret = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
- if (!ret) {
- sdev_printk(KERN_WARNING, sdev,
- "%s: sending tur failed, no sense available\n",
- HP_SW_NAME);
- ret = SCSI_DH_IO;
- goto done;
- }
- switch (sshdr.sense_key) {
- case UNIT_ATTENTION:
- ret = SCSI_DH_IMM_RETRY;
- break;
+ switch (sshdr->sense_key) {
case NOT_READY:
- if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) {
+ if (sshdr->asc == 0x04 && sshdr->ascq == 2) {
/*
* LUN not ready - Initialization command required
*
* This is the passive path
*/
- ret = SCSI_DH_DEV_OFFLINED;
+ h->path_state = HP_SW_PATH_PASSIVE;
+ ret = SCSI_DH_OK;
break;
}
- /* Fallthrough */
+ fallthrough;
default:
sdev_printk(KERN_WARNING, sdev,
"%s: sending tur failed, sense %x/%x/%x\n",
- HP_SW_NAME, sshdr.sense_key, sshdr.asc,
- sshdr.ascq);
+ HP_SW_NAME, sshdr->sense_key, sshdr->asc,
+ sshdr->ascq);
break;
}
-
-done:
return ret;
}
@@ -111,171 +77,122 @@ done:
*/
static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
{
- struct request *req;
- int ret;
-
-retry:
- req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
- if (!req)
- return SCSI_DH_RES_TEMP_UNAVAIL;
-
- req->cmd_type = REQ_TYPE_BLOCK_PC;
- req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER;
- req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY);
- req->cmd[0] = TEST_UNIT_READY;
- req->timeout = HP_SW_TIMEOUT;
- req->sense = h->sense;
- memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
- req->sense_len = 0;
-
- ret = blk_execute_rq(req->q, NULL, req, 1);
- if (ret == -EIO) {
- if (req->sense_len > 0) {
- ret = tur_done(sdev, h->sense);
- } else {
- sdev_printk(KERN_WARNING, sdev,
- "%s: sending tur failed with %x\n",
- HP_SW_NAME, req->errors);
- ret = SCSI_DH_IO;
- }
- } else {
+ unsigned char cmd[6] = { TEST_UNIT_READY };
+ struct scsi_sense_hdr sshdr;
+ int ret, res;
+ blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
+ REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+ struct scsi_failure failure_defs[] = {
+ {
+ .sense = UNIT_ATTENTION,
+ .asc = SCMD_FAILURE_ASC_ANY,
+ .ascq = SCMD_FAILURE_ASCQ_ANY,
+ .allowed = SCMD_FAILURE_NO_LIMIT,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ {}
+ };
+ struct scsi_failures failures = {
+ .failure_definitions = failure_defs,
+ };
+ const struct scsi_exec_args exec_args = {
+ .sshdr = &sshdr,
+ .failures = &failures,
+ };
+
+ res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
+ HP_SW_RETRIES, &exec_args);
+ if (res > 0 && scsi_sense_valid(&sshdr)) {
+ ret = tur_done(sdev, h, &sshdr);
+ } else if (res == 0) {
h->path_state = HP_SW_PATH_ACTIVE;
ret = SCSI_DH_OK;
+ } else {
+ sdev_printk(KERN_WARNING, sdev,
+ "%s: sending tur failed with %x\n",
+ HP_SW_NAME, res);
+ ret = SCSI_DH_IO;
}
- if (ret == SCSI_DH_IMM_RETRY) {
- blk_put_request(req);
- goto retry;
- }
- if (ret == SCSI_DH_DEV_OFFLINED) {
- h->path_state = HP_SW_PATH_PASSIVE;
- ret = SCSI_DH_OK;
- }
-
- blk_put_request(req);
return ret;
}
/*
- * start_done - Handle START STOP UNIT return status
- * @sdev: sdev the command has been sent to
- * @errors: blk error code
+ * hp_sw_start_stop - Send START STOP UNIT command
+ * @sdev: sdev command should be sent to
+ *
+ * Sending START STOP UNIT activates the SP.
*/
-static int start_done(struct scsi_device *sdev, unsigned char *sense)
+static int hp_sw_start_stop(struct hp_sw_dh_data *h)
{
+ unsigned char cmd[6] = { START_STOP, 0, 0, 0, 1, 0 };
struct scsi_sense_hdr sshdr;
- int rc;
-
- rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
- if (!rc) {
+ struct scsi_device *sdev = h->sdev;
+ int res, rc;
+ blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
+ REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+ struct scsi_failure failure_defs[] = {
+ {
+ /*
+ * LUN not ready - manual intervention required
+ *
+ * Switch-over in progress, retry.
+ */
+ .sense = NOT_READY,
+ .asc = 0x04,
+ .ascq = 0x03,
+ .allowed = HP_SW_RETRIES,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ {}
+ };
+ struct scsi_failures failures = {
+ .failure_definitions = failure_defs,
+ };
+ const struct scsi_exec_args exec_args = {
+ .sshdr = &sshdr,
+ .failures = &failures,
+ };
+
+ res = scsi_execute_cmd(sdev, cmd, opf, NULL, 0, HP_SW_TIMEOUT,
+ HP_SW_RETRIES, &exec_args);
+ if (!res) {
+ return SCSI_DH_OK;
+ } else if (res < 0 || !scsi_sense_valid(&sshdr)) {
sdev_printk(KERN_WARNING, sdev,
"%s: sending start_stop_unit failed, "
- "no sense available\n",
- HP_SW_NAME);
+ "no sense available\n", HP_SW_NAME);
return SCSI_DH_IO;
}
+
switch (sshdr.sense_key) {
case NOT_READY:
- if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
- /*
- * LUN not ready - manual intervention required
- *
- * Switch-over in progress, retry.
- */
+ if (sshdr.asc == 0x04 && sshdr.ascq == 3) {
rc = SCSI_DH_RETRY;
break;
}
- /* fall through */
+ fallthrough;
default:
sdev_printk(KERN_WARNING, sdev,
- "%s: sending start_stop_unit failed, sense %x/%x/%x\n",
- HP_SW_NAME, sshdr.sense_key, sshdr.asc,
- sshdr.ascq);
+ "%s: sending start_stop_unit failed, "
+ "sense %x/%x/%x\n", HP_SW_NAME,
+ sshdr.sense_key, sshdr.asc, sshdr.ascq);
rc = SCSI_DH_IO;
}
return rc;
}
-static void start_stop_endio(struct request *req, int error)
+static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
{
- struct hp_sw_dh_data *h = req->end_io_data;
- unsigned err = SCSI_DH_OK;
-
- if (error || host_byte(req->errors) != DID_OK ||
- msg_byte(req->errors) != COMMAND_COMPLETE) {
- sdev_printk(KERN_WARNING, h->sdev,
- "%s: sending start_stop_unit failed with %x\n",
- HP_SW_NAME, req->errors);
- err = SCSI_DH_IO;
- goto done;
- }
-
- if (req->sense_len > 0) {
- err = start_done(h->sdev, h->sense);
- if (err == SCSI_DH_RETRY) {
- err = SCSI_DH_IO;
- if (--h->retry_cnt) {
- blk_put_request(req);
- err = hp_sw_start_stop(h);
- if (err == SCSI_DH_OK)
- return;
- }
- }
- }
-done:
- req->end_io_data = NULL;
- __blk_put_request(req->q, req);
- if (h->callback_fn) {
- h->callback_fn(h->callback_data, err);
- h->callback_fn = h->callback_data = NULL;
- }
- return;
-
-}
-
-/*
- * hp_sw_start_stop - Send START STOP UNIT command
- * @sdev: sdev command should be sent to
- *
- * Sending START STOP UNIT activates the SP.
- */
-static int hp_sw_start_stop(struct hp_sw_dh_data *h)
-{
- struct request *req;
-
- req = blk_get_request(h->sdev->request_queue, WRITE, GFP_ATOMIC);
- if (!req)
- return SCSI_DH_RES_TEMP_UNAVAIL;
-
- req->cmd_type = REQ_TYPE_BLOCK_PC;
- req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER;
- req->cmd_len = COMMAND_SIZE(START_STOP);
- req->cmd[0] = START_STOP;
- req->cmd[4] = 1; /* Start spin cycle */
- req->timeout = HP_SW_TIMEOUT;
- req->sense = h->sense;
- memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
- req->sense_len = 0;
- req->end_io_data = h;
-
- blk_execute_rq_nowait(req->q, NULL, req, 1, start_stop_endio);
- return SCSI_DH_OK;
-}
-
-static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
-{
- struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
- int ret = BLKPREP_OK;
+ struct hp_sw_dh_data *h = sdev->handler_data;
if (h->path_state != HP_SW_PATH_ACTIVE) {
- ret = BLKPREP_KILL;
- req->cmd_flags |= REQ_QUIET;
+ req->rq_flags |= RQF_QUIET;
+ return BLK_STS_IOERR;
}
- return ret;
+ return BLK_STS_OK;
}
/*
@@ -292,107 +209,64 @@ static int hp_sw_activate(struct scsi_device *sdev,
activate_complete fn, void *data)
{
int ret = SCSI_DH_OK;
- struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+ struct hp_sw_dh_data *h = sdev->handler_data;
ret = hp_sw_tur(sdev, h);
- if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
- h->retry_cnt = h->retries;
- h->callback_fn = fn;
- h->callback_data = data;
+ if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE)
ret = hp_sw_start_stop(h);
- if (ret == SCSI_DH_OK)
- return 0;
- h->callback_fn = h->callback_data = NULL;
- }
if (fn)
fn(data, ret);
return 0;
}
-static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
- {"COMPAQ", "MSA1000 VOLUME"},
- {"COMPAQ", "HSV110"},
- {"HP", "HSV100"},
- {"DEC", "HSG80"},
- {NULL, NULL},
-};
-
-static int hp_sw_bus_attach(struct scsi_device *sdev);
-static void hp_sw_bus_detach(struct scsi_device *sdev);
-
-static struct scsi_device_handler hp_sw_dh = {
- .name = HP_SW_NAME,
- .module = THIS_MODULE,
- .devlist = hp_sw_dh_data_list,
- .attach = hp_sw_bus_attach,
- .detach = hp_sw_bus_detach,
- .activate = hp_sw_activate,
- .prep_fn = hp_sw_prep_fn,
-};
-
static int hp_sw_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct hp_sw_dh_data *h;
- unsigned long flags;
int ret;
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",
- HP_SW_NAME);
- return 0;
- }
-
- scsi_dh_data->scsi_dh = &hp_sw_dh;
- h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return SCSI_DH_NOMEM;
h->path_state = HP_SW_PATH_UNINITIALIZED;
h->retries = HP_SW_RETRIES;
h->sdev = sdev;
ret = hp_sw_tur(sdev, h);
- if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
+ if (ret != SCSI_DH_OK)
goto failed;
-
- if (!try_module_get(THIS_MODULE))
+ if (h->path_state == HP_SW_PATH_UNINITIALIZED) {
+ ret = SCSI_DH_NOSYS;
goto failed;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+ }
sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
"active":"passive");
- return 0;
-
+ sdev->handler_data = h;
+ return SCSI_DH_OK;
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
- HP_SW_NAME);
- return -EINVAL;
+ kfree(h);
+ return ret;
}
static void hp_sw_bus_detach( struct scsi_device *sdev )
{
- struct scsi_dh_data *scsi_dh_data;
- unsigned long flags;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- scsi_dh_data = sdev->scsi_dh_data;
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
- module_put(THIS_MODULE);
-
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME);
-
- kfree(scsi_dh_data);
+ kfree(sdev->handler_data);
+ sdev->handler_data = NULL;
}
+static struct scsi_device_handler hp_sw_dh = {
+ .name = HP_SW_NAME,
+ .module = THIS_MODULE,
+ .attach = hp_sw_bus_attach,
+ .detach = hp_sw_bus_detach,
+ .activate = hp_sw_activate,
+ .prep_fn = hp_sw_prep_fn,
+};
+
static int __init hp_sw_init(void)
{
return scsi_register_device_handler(&hp_sw_dh);
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index e7fc70d6b478..6e1b252cea0e 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -1,5 +1,5 @@
/*
- * Engenio/LSI RDAC SCSI Device Handler
+ * LSI/Engenio/NetApp E-Series RDAC SCSI Device Handler
*
* Copyright (C) 2005 Mike Christie. All rights reserved.
* Copyright (C) Chandra Seetharaman, IBM Corp. 2007
@@ -24,6 +24,7 @@
#include <scsi/scsi_dh.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
+#include <linux/module.h>
#define RDAC_NAME "rdac"
#define RDAC_RETRY_COUNT 5
@@ -35,7 +36,7 @@
* mode page were taken from the LSI RDAC 2.4 GPL'd
* driver, and then converted to Linux conventions.
*/
-#define RDAC_QUIESCENCE_TIME 20;
+#define RDAC_QUIESCENCE_TIME 20
/*
* Page Codes
*/
@@ -128,25 +129,7 @@ struct c4_inquiry {
u8 reserved[2];
};
-struct rdac_controller {
- u8 subsys_id[SUBSYS_ID_LEN];
- u8 slot_id[SLOT_ID_LEN];
- int use_ms10;
- struct kref kref;
- struct list_head node; /* list of all controllers */
- union {
- struct rdac_pg_legacy legacy;
- struct rdac_pg_expanded expanded;
- } mode_select;
- u8 index;
- u8 array_name[ARRAY_LABEL_LEN];
- spinlock_t ms_lock;
- int ms_queued;
- struct work_struct ms_work;
- struct scsi_device *ms_sdev;
- struct list_head ms_head;
-};
-
+#define UNIQUE_ID_LEN 16
struct c8_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC8 */
@@ -159,12 +142,32 @@ struct c8_inquiry {
u8 vol_user_label_len;
u8 vol_user_label[60];
u8 array_uniq_id_len;
- u8 array_unique_id[16];
+ u8 array_unique_id[UNIQUE_ID_LEN];
u8 array_user_label_len;
u8 array_user_label[60];
u8 lun[8];
};
+struct rdac_controller {
+ u8 array_id[UNIQUE_ID_LEN];
+ int use_ms10;
+ struct kref kref;
+ struct list_head node; /* list of all controllers */
+ union {
+ struct rdac_pg_legacy legacy;
+ struct rdac_pg_expanded expanded;
+ } mode_select;
+ u8 index;
+ u8 array_name[ARRAY_LABEL_LEN];
+ struct Scsi_Host *host;
+ spinlock_t ms_lock;
+ int ms_queued;
+ struct work_struct ms_work;
+ struct scsi_device *ms_sdev;
+ struct list_head ms_head;
+ struct list_head dh_list;
+};
+
struct c2_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC2 */
@@ -179,7 +182,9 @@ struct c2_inquiry {
};
struct rdac_dh_data {
+ struct list_head node;
struct rdac_controller *ctlr;
+ struct scsi_device *sdev;
#define UNINITIALIZED_LUN (1 << 8)
unsigned lun;
@@ -200,7 +205,6 @@ struct rdac_dh_data {
#define RDAC_NON_PREFERRED 1
char preferred;
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
union {
struct c2_inquiry c2;
struct c4_inquiry c4;
@@ -257,57 +261,20 @@ do { \
sdev_printk(KERN_INFO, sdev, RDAC_NAME ": " f "\n", ## arg); \
} while (0);
-static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
-{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct rdac_dh_data *) scsi_dh_data->buf);
-}
-
-static struct request *get_rdac_req(struct scsi_device *sdev,
- void *buffer, unsigned buflen, int rw)
+static unsigned int rdac_failover_get(struct rdac_controller *ctlr,
+ struct list_head *list,
+ unsigned char *cdb)
{
- struct request *rq;
- struct request_queue *q = sdev->request_queue;
-
- rq = blk_get_request(q, rw, GFP_NOIO);
-
- if (!rq) {
- sdev_printk(KERN_INFO, sdev,
- "get_rdac_req: blk_get_request failed.\n");
- return NULL;
- }
-
- if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) {
- blk_put_request(rq);
- sdev_printk(KERN_INFO, sdev,
- "get_rdac_req: blk_rq_map_kern failed.\n");
- return NULL;
- }
-
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
- rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER;
- rq->retries = RDAC_RETRIES;
- rq->timeout = RDAC_TIMEOUT;
-
- return rq;
-}
-
-static struct request *rdac_failover_get(struct scsi_device *sdev,
- struct rdac_dh_data *h, struct list_head *list)
-{
- struct request *rq;
struct rdac_mode_common *common;
unsigned data_size;
struct rdac_queue_data *qdata;
u8 *lun_table;
- if (h->ctlr->use_ms10) {
+ if (ctlr->use_ms10) {
struct rdac_pg_expanded *rdac_pg;
data_size = sizeof(struct rdac_pg_expanded);
- rdac_pg = &h->ctlr->mode_select.expanded;
+ rdac_pg = &ctlr->mode_select.expanded;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
@@ -319,7 +286,7 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
struct rdac_pg_legacy *rdac_pg;
data_size = sizeof(struct rdac_pg_legacy);
- rdac_pg = &h->ctlr->mode_select.legacy;
+ rdac_pg = &ctlr->mode_select.legacy;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
@@ -334,27 +301,17 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
lun_table[qdata->h->lun] = 0x81;
}
- /* get request for block layer packet command */
- rq = get_rdac_req(sdev, &h->ctlr->mode_select, data_size, WRITE);
- if (!rq)
- return NULL;
-
/* Prepare the command. */
- if (h->ctlr->use_ms10) {
- rq->cmd[0] = MODE_SELECT_10;
- rq->cmd[7] = data_size >> 8;
- rq->cmd[8] = data_size & 0xff;
+ if (ctlr->use_ms10) {
+ cdb[0] = MODE_SELECT_10;
+ cdb[7] = data_size >> 8;
+ cdb[8] = data_size & 0xff;
} else {
- rq->cmd[0] = MODE_SELECT;
- rq->cmd[4] = data_size;
+ cdb[0] = MODE_SELECT;
+ cdb[4] = data_size;
}
- rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = 0;
-
- return rq;
+ return data_size;
}
static void release_controller(struct kref *kref)
@@ -362,43 +319,33 @@ static void release_controller(struct kref *kref)
struct rdac_controller *ctlr;
ctlr = container_of(kref, struct rdac_controller, kref);
- flush_workqueue(kmpath_rdacd);
- spin_lock(&list_lock);
list_del(&ctlr->node);
- spin_unlock(&list_lock);
kfree(ctlr);
}
-static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
- char *array_name)
+static struct rdac_controller *get_controller(int index, char *array_name,
+ u8 *array_id, struct scsi_device *sdev)
{
struct rdac_controller *ctlr, *tmp;
- spin_lock(&list_lock);
-
list_for_each_entry(tmp, &ctlr_list, node) {
- if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
- (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
+ if ((memcmp(tmp->array_id, array_id, UNIQUE_ID_LEN) == 0) &&
+ (tmp->index == index) &&
+ (tmp->host == sdev->host)) {
kref_get(&tmp->kref);
- spin_unlock(&list_lock);
return tmp;
}
}
ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
if (!ctlr)
- goto done;
+ return NULL;
/* initialize fields of controller */
- memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
- memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
+ memcpy(ctlr->array_id, array_id, UNIQUE_ID_LEN);
+ ctlr->index = index;
+ ctlr->host = sdev->host;
memcpy(ctlr->array_name, array_name, ARRAY_LABEL_LEN);
- /* update the controller index */
- if (slot_id[1] == 0x31)
- ctlr->index = 0;
- else
- ctlr->index = 1;
-
kref_init(&ctlr->kref);
ctlr->use_ms10 = -1;
ctlr->ms_queued = 0;
@@ -407,51 +354,19 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
INIT_WORK(&ctlr->ms_work, send_mode_select);
INIT_LIST_HEAD(&ctlr->ms_head);
list_add(&ctlr->node, &ctlr_list);
-done:
- spin_unlock(&list_lock);
- return ctlr;
-}
-
-static int submit_inquiry(struct scsi_device *sdev, int page_code,
- unsigned int len, struct rdac_dh_data *h)
-{
- struct request *rq;
- struct request_queue *q = sdev->request_queue;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
-
- rq = get_rdac_req(sdev, &h->inq, len, READ);
- if (!rq)
- goto done;
+ INIT_LIST_HEAD(&ctlr->dh_list);
- /* Prepare the command. */
- rq->cmd[0] = INQUIRY;
- rq->cmd[1] = 1;
- rq->cmd[2] = page_code;
- rq->cmd[4] = len;
- rq->cmd_len = COMMAND_SIZE(INQUIRY);
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = 0;
-
- err = blk_execute_rq(q, NULL, rq, 1);
- if (err == -EIO)
- err = SCSI_DH_IO;
-
- blk_put_request(rq);
-done:
- return err;
+ return ctlr;
}
static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h,
- char *array_name)
+ char *array_name, u8 *array_id)
{
- int err, i;
- struct c8_inquiry *inqp;
+ int err = SCSI_DH_IO, i;
+ struct c8_inquiry *inqp = &h->inq.c8;
- err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry), h);
- if (err == SCSI_DH_OK) {
- inqp = &h->inq.c8;
+ if (!scsi_get_vpd_page(sdev, 0xC8, (unsigned char *)inqp,
+ sizeof(struct c8_inquiry))) {
if (inqp->page_code != 0xc8)
return SCSI_DH_NOSYS;
if (inqp->page_id[0] != 'e' || inqp->page_id[1] != 'd' ||
@@ -463,19 +378,22 @@ static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h,
*(array_name+i) = inqp->array_user_label[(2*i)+1];
*(array_name+ARRAY_LABEL_LEN-1) = '\0';
+ memset(array_id, 0, UNIQUE_ID_LEN);
+ memcpy(array_id, inqp->array_unique_id, inqp->array_uniq_id_len);
+ err = SCSI_DH_OK;
}
return err;
}
static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)
{
- int err;
- struct c9_inquiry *inqp;
+ int err = SCSI_DH_IO, access_state;
+ struct rdac_dh_data *tmp;
+ struct c9_inquiry *inqp = &h->inq.c9;
h->state = RDAC_STATE_ACTIVE;
- err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h);
- if (err == SCSI_DH_OK) {
- inqp = &h->inq.c9;
+ if (!scsi_get_vpd_page(sdev, 0xC9, (unsigned char *)inqp,
+ sizeof(struct c9_inquiry))) {
/* detect the operating mode */
if ((inqp->avte_cvp >> 5) & 0x1)
h->mode = RDAC_MODE_IOSHIP; /* LUN in IOSHIP mode */
@@ -485,49 +403,72 @@ static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)
h->mode = RDAC_MODE; /* LUN in RDAC mode */
/* Update ownership */
- if (inqp->avte_cvp & 0x1)
+ if (inqp->avte_cvp & 0x1) {
h->lun_state = RDAC_LUN_OWNED;
- else {
+ access_state = SCSI_ACCESS_STATE_OPTIMAL;
+ } else {
h->lun_state = RDAC_LUN_UNOWNED;
- if (h->mode == RDAC_MODE)
+ if (h->mode == RDAC_MODE) {
h->state = RDAC_STATE_PASSIVE;
+ access_state = SCSI_ACCESS_STATE_STANDBY;
+ } else
+ access_state = SCSI_ACCESS_STATE_ACTIVE;
}
/* Update path prio*/
- if (inqp->path_prio & 0x1)
+ if (inqp->path_prio & 0x1) {
h->preferred = RDAC_PREFERRED;
- else
+ access_state |= SCSI_ACCESS_STATE_PREFERRED;
+ } else
h->preferred = RDAC_NON_PREFERRED;
+ rcu_read_lock();
+ list_for_each_entry_rcu(tmp, &h->ctlr->dh_list, node) {
+ /* h->sdev should always be valid */
+ BUG_ON(!tmp->sdev);
+ tmp->sdev->access_state = access_state;
+ }
+ rcu_read_unlock();
+ err = SCSI_DH_OK;
}
return err;
}
static int initialize_controller(struct scsi_device *sdev,
- struct rdac_dh_data *h, char *array_name)
+ struct rdac_dh_data *h, char *array_name, u8 *array_id)
{
- int err;
- struct c4_inquiry *inqp;
+ int err = SCSI_DH_IO, index;
+ struct c4_inquiry *inqp = &h->inq.c4;
+
+ if (!scsi_get_vpd_page(sdev, 0xC4, (unsigned char *)inqp,
+ sizeof(struct c4_inquiry))) {
+ /* get the controller index */
+ if (inqp->slot_id[1] == 0x31)
+ index = 0;
+ else
+ index = 1;
- err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h);
- if (err == SCSI_DH_OK) {
- inqp = &h->inq.c4;
- h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id,
- array_name);
+ spin_lock(&list_lock);
+ h->ctlr = get_controller(index, array_name, array_id, sdev);
if (!h->ctlr)
err = SCSI_DH_RES_TEMP_UNAVAIL;
+ else {
+ h->sdev = sdev;
+ list_add_rcu(&h->node, &h->ctlr->dh_list);
+ }
+ spin_unlock(&list_lock);
+ err = SCSI_DH_OK;
}
return err;
}
static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
{
- int err;
- struct c2_inquiry *inqp;
+ int err = SCSI_DH_IO;
+ struct c2_inquiry *inqp = &h->inq.c2;
- err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry), h);
- if (err == SCSI_DH_OK) {
- inqp = &h->inq.c2;
+ if (!scsi_get_vpd_page(sdev, 0xC2, (unsigned char *)inqp,
+ sizeof(struct c2_inquiry))) {
/*
* If more than MODE6_MAX_LUN luns are supported, use
* mode select 10
@@ -536,65 +477,85 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
h->ctlr->use_ms10 = 1;
else
h->ctlr->use_ms10 = 0;
+ err = SCSI_DH_OK;
}
return err;
}
static int mode_select_handle_sense(struct scsi_device *sdev,
- unsigned char *sensebuf)
+ struct scsi_sense_hdr *sense_hdr)
{
- struct scsi_sense_hdr sense_hdr;
- int err = SCSI_DH_IO, ret;
- struct rdac_dh_data *h = get_rdac_data(sdev);
+ struct rdac_dh_data *h = sdev->handler_data;
- ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
- if (!ret)
- goto done;
-
- switch (sense_hdr.sense_key) {
- case NO_SENSE:
- case ABORTED_COMMAND:
- case UNIT_ATTENTION:
- err = SCSI_DH_RETRY;
- break;
- case NOT_READY:
- if (sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x01)
- /* LUN Not Ready and is in the Process of Becoming
- * Ready
- */
- err = SCSI_DH_RETRY;
- break;
- case ILLEGAL_REQUEST:
- if (sense_hdr.asc == 0x91 && sense_hdr.ascq == 0x36)
- /*
- * Command Lock contention
- */
- err = SCSI_DH_RETRY;
- break;
- default:
- break;
- }
+ if (!scsi_sense_valid(sense_hdr))
+ return SCSI_DH_IO;
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"MODE_SELECT returned with sense %02x/%02x/%02x",
(char *) h->ctlr->array_name, h->ctlr->index,
- sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq);
+ sense_hdr->sense_key, sense_hdr->asc, sense_hdr->ascq);
-done:
- return err;
+ return SCSI_DH_IO;
}
static void send_mode_select(struct work_struct *work)
{
struct rdac_controller *ctlr =
container_of(work, struct rdac_controller, ms_work);
- struct request *rq;
struct scsi_device *sdev = ctlr->ms_sdev;
- struct rdac_dh_data *h = get_rdac_data(sdev);
- struct request_queue *q = sdev->request_queue;
- int err, retry_cnt = RDAC_RETRY_COUNT;
+ struct rdac_dh_data *h = sdev->handler_data;
+ int rc, err;
struct rdac_queue_data *tmp, *qdata;
LIST_HEAD(list);
+ unsigned char cdb[MAX_COMMAND_SIZE];
+ struct scsi_sense_hdr sshdr;
+ unsigned int data_size;
+ blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |
+ REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+ struct scsi_failure failure_defs[] = {
+ {
+ .sense = NO_SENSE,
+ .asc = SCMD_FAILURE_ASC_ANY,
+ .ascq = SCMD_FAILURE_ASCQ_ANY,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ {
+ .sense = ABORTED_COMMAND,
+ .asc = SCMD_FAILURE_ASC_ANY,
+ .ascq = SCMD_FAILURE_ASCQ_ANY,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ {
+ .sense = UNIT_ATTENTION,
+ .asc = SCMD_FAILURE_ASC_ANY,
+ .ascq = SCMD_FAILURE_ASCQ_ANY,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ /* LUN Not Ready and is in the Process of Becoming Ready */
+ {
+ .sense = NOT_READY,
+ .asc = 0x04,
+ .ascq = 0x01,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ /* Command Lock contention */
+ {
+ .sense = ILLEGAL_REQUEST,
+ .asc = 0x91,
+ .ascq = 0x36,
+ .allowed = SCMD_FAILURE_NO_LIMIT,
+ .result = SAM_STAT_CHECK_CONDITION,
+ },
+ {}
+ };
+ struct scsi_failures failures = {
+ .total_allowed = RDAC_RETRY_COUNT,
+ .failure_definitions = failure_defs,
+ };
+ const struct scsi_exec_args exec_args = {
+ .sshdr = &sshdr,
+ .failures = &failures,
+ };
spin_lock(&ctlr->ms_lock);
list_splice_init(&ctlr->ms_head, &list);
@@ -602,32 +563,27 @@ static void send_mode_select(struct work_struct *work)
ctlr->ms_sdev = NULL;
spin_unlock(&ctlr->ms_lock);
-retry:
- err = SCSI_DH_RES_TEMP_UNAVAIL;
- rq = rdac_failover_get(sdev, h, &list);
- if (!rq)
- goto done;
+ memset(cdb, 0, sizeof(cdb));
- RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
- "%s MODE_SELECT command",
- (char *) h->ctlr->array_name, h->ctlr->index,
- (retry_cnt == RDAC_RETRY_COUNT) ? "queueing" : "retrying");
-
- err = blk_execute_rq(q, NULL, rq, 1);
- blk_put_request(rq);
- if (err != SCSI_DH_OK) {
- err = mode_select_handle_sense(sdev, h->sense);
- if (err == SCSI_DH_RETRY && retry_cnt--)
- goto retry;
- }
- if (err == SCSI_DH_OK) {
+ data_size = rdac_failover_get(ctlr, &list, cdb);
+
+ RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, queueing MODE_SELECT command",
+ (char *)h->ctlr->array_name, h->ctlr->index);
+
+ rc = scsi_execute_cmd(sdev, cdb, opf, &h->ctlr->mode_select, data_size,
+ RDAC_TIMEOUT * HZ, RDAC_RETRIES, &exec_args);
+ if (!rc) {
h->state = RDAC_STATE_ACTIVE;
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"MODE_SELECT completed",
(char *) h->ctlr->array_name, h->ctlr->index);
+ err = SCSI_DH_OK;
+ } else if (rc < 0) {
+ err = SCSI_DH_IO;
+ } else {
+ err = mode_select_handle_sense(sdev, &sshdr);
}
-done:
list_for_each_entry_safe(qdata, tmp, &list, entry) {
list_del(&qdata->entry);
if (err == SCSI_DH_OK)
@@ -649,7 +605,7 @@ static int queue_mode_select(struct scsi_device *sdev,
if (!qdata)
return SCSI_DH_RETRY;
- qdata->h = get_rdac_data(sdev);
+ qdata->h = sdev->handler_data;
qdata->callback_fn = fn;
qdata->callback_data = data;
@@ -668,7 +624,7 @@ static int queue_mode_select(struct scsi_device *sdev,
static int rdac_activate(struct scsi_device *sdev,
activate_complete fn, void *data)
{
- struct rdac_dh_data *h = get_rdac_data(sdev);
+ struct rdac_dh_data *h = sdev->handler_data;
int err = SCSI_DH_OK;
int act = 0;
@@ -701,23 +657,22 @@ done:
return 0;
}
-static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
+static blk_status_t rdac_prep_fn(struct scsi_device *sdev, struct request *req)
{
- struct rdac_dh_data *h = get_rdac_data(sdev);
- int ret = BLKPREP_OK;
+ struct rdac_dh_data *h = sdev->handler_data;
if (h->state != RDAC_STATE_ACTIVE) {
- ret = BLKPREP_KILL;
- req->cmd_flags |= REQ_QUIET;
+ req->rq_flags |= RQF_QUIET;
+ return BLK_STS_IOERR;
}
- return ret;
+ return BLK_STS_OK;
}
-static int rdac_check_sense(struct scsi_device *sdev,
- struct scsi_sense_hdr *sense_hdr)
+static enum scsi_disposition rdac_check_sense(struct scsi_device *sdev,
+ struct scsi_sense_hdr *sense_hdr)
{
- struct rdac_dh_data *h = get_rdac_data(sdev);
+ struct rdac_dh_data *h = sdev->handler_data;
RDAC_LOG(RDAC_LOG_SENSE, sdev, "array %s, ctlr %d, "
"I/O returned with sense %02x/%02x/%02x",
@@ -779,81 +734,24 @@ static int rdac_check_sense(struct scsi_device *sdev,
return SCSI_RETURN_NOT_HANDLED;
}
-static const struct scsi_dh_devlist rdac_dev_list[] = {
- {"IBM", "1722"},
- {"IBM", "1724"},
- {"IBM", "1726"},
- {"IBM", "1742"},
- {"IBM", "1745"},
- {"IBM", "1746"},
- {"IBM", "1814"},
- {"IBM", "1815"},
- {"IBM", "1818"},
- {"IBM", "3526"},
- {"SGI", "TP9400"},
- {"SGI", "TP9500"},
- {"SGI", "IS"},
- {"STK", "OPENstorage D280"},
- {"SUN", "CSM200_R"},
- {"SUN", "LCSM100_I"},
- {"SUN", "LCSM100_S"},
- {"SUN", "LCSM100_E"},
- {"SUN", "LCSM100_F"},
- {"DELL", "MD3000"},
- {"DELL", "MD3000i"},
- {"DELL", "MD32xx"},
- {"DELL", "MD32xxi"},
- {"DELL", "MD36xxi"},
- {"DELL", "MD36xxf"},
- {"LSI", "INF-01-00"},
- {"ENGENIO", "INF-01-00"},
- {"STK", "FLEXLINE 380"},
- {"SUN", "CSM100_R_FC"},
- {"SUN", "STK6580_6780"},
- {"SUN", "SUN_6180"},
- {NULL, NULL},
-};
-
-static int rdac_bus_attach(struct scsi_device *sdev);
-static void rdac_bus_detach(struct scsi_device *sdev);
-
-static struct scsi_device_handler rdac_dh = {
- .name = RDAC_NAME,
- .module = THIS_MODULE,
- .devlist = rdac_dev_list,
- .prep_fn = rdac_prep_fn,
- .check_sense = rdac_check_sense,
- .attach = rdac_bus_attach,
- .detach = rdac_bus_detach,
- .activate = rdac_activate,
-};
-
static int rdac_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct rdac_dh_data *h;
- unsigned long flags;
int err;
char array_name[ARRAY_LABEL_LEN];
+ char array_id[UNIQUE_ID_LEN];
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
- RDAC_NAME);
- return 0;
- }
-
- scsi_dh_data->scsi_dh = &rdac_dh;
- h = (struct rdac_dh_data *) scsi_dh_data->buf;
+ h = kzalloc(sizeof(*h) , GFP_KERNEL);
+ if (!h)
+ return SCSI_DH_NOMEM;
h->lun = UNINITIALIZED_LUN;
h->state = RDAC_STATE_ACTIVE;
- err = get_lun_info(sdev, h, array_name);
+ err = get_lun_info(sdev, h, array_name, array_id);
if (err != SCSI_DH_OK)
goto failed;
- err = initialize_controller(sdev, h, array_name);
+ err = initialize_controller(sdev, h, array_name, array_id);
if (err != SCSI_DH_OK)
goto failed;
@@ -865,50 +763,51 @@ static int rdac_bus_attach(struct scsi_device *sdev)
if (err != SCSI_DH_OK)
goto clean_ctlr;
- if (!try_module_get(THIS_MODULE))
- goto clean_ctlr;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
sdev_printk(KERN_NOTICE, sdev,
"%s: LUN %d (%s) (%s)\n",
RDAC_NAME, h->lun, mode[(int)h->mode],
lun_state[(int)h->lun_state]);
- return 0;
+ sdev->handler_data = h;
+ return SCSI_DH_OK;
clean_ctlr:
+ spin_lock(&list_lock);
kref_put(&h->ctlr->kref, release_controller);
+ spin_unlock(&list_lock);
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
- RDAC_NAME);
- return -EINVAL;
+ kfree(h);
+ return err;
}
static void rdac_bus_detach( struct scsi_device *sdev )
{
- struct scsi_dh_data *scsi_dh_data;
- struct rdac_dh_data *h;
- unsigned long flags;
+ struct rdac_dh_data *h = sdev->handler_data;
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- scsi_dh_data = sdev->scsi_dh_data;
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+ if (h->ctlr && h->ctlr->ms_queued)
+ flush_workqueue(kmpath_rdacd);
- h = (struct rdac_dh_data *) scsi_dh_data->buf;
- if (h->ctlr)
+ spin_lock(&list_lock);
+ if (h->ctlr) {
+ list_del_rcu(&h->node);
kref_put(&h->ctlr->kref, release_controller);
- kfree(scsi_dh_data);
- module_put(THIS_MODULE);
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME);
+ }
+ spin_unlock(&list_lock);
+ sdev->handler_data = NULL;
+ synchronize_rcu();
+ kfree(h);
}
-
+static struct scsi_device_handler rdac_dh = {
+ .name = RDAC_NAME,
+ .module = THIS_MODULE,
+ .prep_fn = rdac_prep_fn,
+ .check_sense = rdac_check_sense,
+ .attach = rdac_bus_attach,
+ .detach = rdac_bus_detach,
+ .activate = rdac_activate,
+};
static int __init rdac_init(void)
{
@@ -923,10 +822,13 @@ static int __init rdac_init(void)
/*
* Create workqueue to handle mode selects for rdac
*/
- kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd");
+ kmpath_rdacd =
+ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, "kmpath_rdacd");
if (!kmpath_rdacd) {
scsi_unregister_device_handler(&rdac_dh);
printk(KERN_ERR "kmpath_rdacd creation failed.\n");
+
+ r = -EINVAL;
}
done:
return r;
@@ -941,7 +843,7 @@ static void __exit rdac_exit(void)
module_init(rdac_init);
module_exit(rdac_exit);
-MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver");
+MODULE_DESCRIPTION("Multipath LSI/Engenio/NetApp E-Series RDAC driver");
MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
MODULE_VERSION("01.00.0000.0000");
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/dmx3191d.c b/drivers/scsi/dmx3191d.c
index 207352cc70cc..d6d091b2f3c7 100644
--- a/drivers/scsi/dmx3191d.c
+++ b/drivers/scsi/dmx3191d.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
dmx3191d.c - driver for the Domex DMX3191D SCSI card.
Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
@@ -5,19 +6,6 @@
Based on the generic NCR5380 driver by Drew Eckhardt et al.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
@@ -33,20 +21,16 @@
/*
* Definitions for the generic 5380 driver.
*/
-#define AUTOSENSE
-#define NCR5380_read(reg) inb(port + reg)
-#define NCR5380_write(reg, value) outb(value, port + reg)
+#define NCR5380_read(reg) inb(hostdata->base + (reg))
+#define NCR5380_write(reg, value) outb(value, hostdata->base + (reg))
-#define NCR5380_implementation_fields unsigned int port
-#define NCR5380_local_declare() NCR5380_implementation_fields
-#define NCR5380_setup(instance) port = instance->io_port
+#define NCR5380_dma_xfer_len NCR5380_dma_xfer_none
+#define NCR5380_dma_recv_setup NCR5380_dma_setup_none
+#define NCR5380_dma_send_setup NCR5380_dma_setup_none
+#define NCR5380_dma_residual NCR5380_dma_residual_none
-/*
- * Includes needed for NCR5380.[ch] (XXX: Move them to NCR5380.h)
- */
-#include <linux/delay.h>
-#include "scsi.h"
+#define NCR5380_implementation_fields /* none */
#include "NCR5380.h"
#include "NCR5380.c"
@@ -55,23 +39,27 @@
#define DMX3191D_REGION_LEN 8
-static struct scsi_host_template dmx3191d_driver_template = {
+static const struct scsi_host_template dmx3191d_driver_template = {
+ .module = THIS_MODULE,
.proc_name = DMX3191D_DRIVER_NAME,
.name = "Domex DMX3191D",
+ .info = NCR5380_info,
.queuecommand = NCR5380_queue_command,
.eh_abort_handler = NCR5380_abort,
- .eh_bus_reset_handler = NCR5380_bus_reset,
+ .eh_host_reset_handler = NCR5380_host_reset,
.can_queue = 32,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct NCR5380_cmd),
};
-static int __devinit dmx3191d_probe_one(struct pci_dev *pdev,
- const struct pci_device_id *id)
+static int dmx3191d_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
{
struct Scsi_Host *shost;
+ struct NCR5380_hostdata *hostdata;
unsigned long io;
int error = -ENODEV;
@@ -89,32 +77,34 @@ static int __devinit dmx3191d_probe_one(struct pci_dev *pdev,
sizeof(struct NCR5380_hostdata));
if (!shost)
goto out_release_region;
- shost->io_port = io;
- shost->irq = pdev->irq;
-
- NCR5380_init(shost, FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E);
-
- if (request_irq(pdev->irq, NCR5380_intr, IRQF_SHARED,
- DMX3191D_DRIVER_NAME, shost)) {
- /*
- * Steam powered scsi controllers run without an IRQ anyway
- */
- printk(KERN_WARNING "dmx3191: IRQ %d not available - "
- "switching to polled mode.\n", pdev->irq);
- shost->irq = SCSI_IRQ_NONE;
- }
+
+ hostdata = shost_priv(shost);
+ hostdata->base = io;
+
+ /* This card does not seem to raise an interrupt on pdev->irq.
+ * Steam-powered SCSI controllers run without an IRQ anyway.
+ */
+ shost->irq = NO_IRQ;
+
+ error = NCR5380_init(shost, 0);
+ if (error)
+ goto out_host_put;
+
+ NCR5380_maybe_reset_bus(shost);
pci_set_drvdata(pdev, shost);
error = scsi_add_host(shost, &pdev->dev);
if (error)
- goto out_free_irq;
+ goto out_exit;
scsi_scan_host(shost);
return 0;
- out_free_irq:
- free_irq(shost->irq, shost);
+out_exit:
+ NCR5380_exit(shost);
+out_host_put:
+ scsi_host_put(shost);
out_release_region:
release_region(io, DMX3191D_REGION_LEN);
out_disable_device:
@@ -123,23 +113,21 @@ static int __devinit dmx3191d_probe_one(struct pci_dev *pdev,
return error;
}
-static void __devexit dmx3191d_remove_one(struct pci_dev *pdev)
+static void dmx3191d_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct NCR5380_hostdata *hostdata = shost_priv(shost);
+ unsigned long io = hostdata->base;
scsi_remove_host(shost);
NCR5380_exit(shost);
-
- if (shost->irq != SCSI_IRQ_NONE)
- free_irq(shost->irq, shost);
- release_region(shost->io_port, DMX3191D_REGION_LEN);
- pci_disable_device(pdev);
-
scsi_host_put(shost);
+ release_region(io, DMX3191D_REGION_LEN);
+ pci_disable_device(pdev);
}
-static struct pci_device_id dmx3191d_pci_tbl[] = {
+static const struct pci_device_id dmx3191d_pci_tbl[] = {
{PCI_VENDOR_ID_DOMEX, PCI_DEVICE_ID_DOMEX_DMX3191D,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
{ }
@@ -150,21 +138,10 @@ static struct pci_driver dmx3191d_pci_driver = {
.name = DMX3191D_DRIVER_NAME,
.id_table = dmx3191d_pci_tbl,
.probe = dmx3191d_probe_one,
- .remove = __devexit_p(dmx3191d_remove_one),
+ .remove = dmx3191d_remove_one,
};
-static int __init dmx3191d_init(void)
-{
- return pci_register_driver(&dmx3191d_pci_driver);
-}
-
-static void __exit dmx3191d_exit(void)
-{
- pci_unregister_driver(&dmx3191d_pci_driver);
-}
-
-module_init(dmx3191d_init);
-module_exit(dmx3191d_exit);
+module_pci_driver(dmx3191d_pci_driver);
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_DESCRIPTION("Domex DMX3191D SCSI driver");
diff --git a/drivers/scsi/dpt/dpti_i2o.h b/drivers/scsi/dpt/dpti_i2o.h
deleted file mode 100644
index 179ad77f6cc9..000000000000
--- a/drivers/scsi/dpt/dpti_i2o.h
+++ /dev/null
@@ -1,446 +0,0 @@
-#ifndef _SCSI_I2O_H
-#define _SCSI_I2O_H
-
-/* I2O kernel space accessible structures/APIs
- *
- * (c) Copyright 1999, 2000 Red Hat Software
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- *************************************************************************
- *
- * This header file defined the I2O APIs/structures for use by
- * the I2O kernel modules.
- *
- */
-
-#ifdef __KERNEL__ /* This file to be included by kernel only */
-
-#include <linux/i2o-dev.h>
-
-#include <linux/notifier.h>
-#include <asm/atomic.h>
-
-
-/*
- * Tunable parameters first
- */
-
-/* How many different OSM's are we allowing */
-#define MAX_I2O_MODULES 64
-
-#define I2O_EVT_CAPABILITY_OTHER 0x01
-#define I2O_EVT_CAPABILITY_CHANGED 0x02
-
-#define I2O_EVT_SENSOR_STATE_CHANGED 0x01
-
-//#ifdef __KERNEL__ /* ioctl stuff only thing exported to users */
-
-#define I2O_MAX_MANAGERS 4
-
-/*
- * I2O Interface Objects
- */
-
-#include <linux/wait.h>
-typedef wait_queue_head_t adpt_wait_queue_head_t;
-#define ADPT_DECLARE_WAIT_QUEUE_HEAD(wait) DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait)
-typedef wait_queue_t adpt_wait_queue_t;
-
-/*
- * message structures
- */
-
-struct i2o_message
-{
- u8 version_offset;
- u8 flags;
- u16 size;
- u32 target_tid:12;
- u32 init_tid:12;
- u32 function:8;
- u32 initiator_context;
- /* List follows */
-};
-
-struct adpt_device;
-struct _adpt_hba;
-struct i2o_device
-{
- struct i2o_device *next; /* Chain */
- struct i2o_device *prev;
-
- char dev_name[8]; /* linux /dev name if available */
- i2o_lct_entry lct_data;/* Device LCT information */
- u32 flags;
- struct proc_dir_entry* proc_entry; /* /proc dir */
- struct adpt_device *owner;
- struct _adpt_hba *controller; /* Controlling IOP */
-};
-
-/*
- * Each I2O controller has one of these objects
- */
-
-struct i2o_controller
-{
- char name[16];
- int unit;
- int type;
- int enabled;
-
- struct notifier_block *event_notifer; /* Events */
- atomic_t users;
- struct i2o_device *devices; /* I2O device chain */
- struct i2o_controller *next; /* Controller chain */
-
-};
-
-/*
- * I2O System table entry
- */
-struct i2o_sys_tbl_entry
-{
- u16 org_id;
- u16 reserved1;
- u32 iop_id:12;
- u32 reserved2:20;
- u16 seg_num:12;
- u16 i2o_version:4;
- u8 iop_state;
- u8 msg_type;
- u16 frame_size;
- u16 reserved3;
- u32 last_changed;
- u32 iop_capabilities;
- u32 inbound_low;
- u32 inbound_high;
-};
-
-struct i2o_sys_tbl
-{
- u8 num_entries;
- u8 version;
- u16 reserved1;
- u32 change_ind;
- u32 reserved2;
- u32 reserved3;
- struct i2o_sys_tbl_entry iops[0];
-};
-
-/*
- * I2O classes / subclasses
- */
-
-/* Class ID and Code Assignments
- * (LCT.ClassID.Version field)
- */
-#define I2O_CLASS_VERSION_10 0x00
-#define I2O_CLASS_VERSION_11 0x01
-
-/* Class code names
- * (from v1.5 Table 6-1 Class Code Assignments.)
- */
-
-#define I2O_CLASS_EXECUTIVE 0x000
-#define I2O_CLASS_DDM 0x001
-#define I2O_CLASS_RANDOM_BLOCK_STORAGE 0x010
-#define I2O_CLASS_SEQUENTIAL_STORAGE 0x011
-#define I2O_CLASS_LAN 0x020
-#define I2O_CLASS_WAN 0x030
-#define I2O_CLASS_FIBRE_CHANNEL_PORT 0x040
-#define I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL 0x041
-#define I2O_CLASS_SCSI_PERIPHERAL 0x051
-#define I2O_CLASS_ATE_PORT 0x060
-#define I2O_CLASS_ATE_PERIPHERAL 0x061
-#define I2O_CLASS_FLOPPY_CONTROLLER 0x070
-#define I2O_CLASS_FLOPPY_DEVICE 0x071
-#define I2O_CLASS_BUS_ADAPTER_PORT 0x080
-#define I2O_CLASS_PEER_TRANSPORT_AGENT 0x090
-#define I2O_CLASS_PEER_TRANSPORT 0x091
-
-/* Rest of 0x092 - 0x09f reserved for peer-to-peer classes
- */
-
-#define I2O_CLASS_MATCH_ANYCLASS 0xffffffff
-
-/* Subclasses
- */
-
-#define I2O_SUBCLASS_i960 0x001
-#define I2O_SUBCLASS_HDM 0x020
-#define I2O_SUBCLASS_ISM 0x021
-
-/* Operation functions */
-
-#define I2O_PARAMS_FIELD_GET 0x0001
-#define I2O_PARAMS_LIST_GET 0x0002
-#define I2O_PARAMS_MORE_GET 0x0003
-#define I2O_PARAMS_SIZE_GET 0x0004
-#define I2O_PARAMS_TABLE_GET 0x0005
-#define I2O_PARAMS_FIELD_SET 0x0006
-#define I2O_PARAMS_LIST_SET 0x0007
-#define I2O_PARAMS_ROW_ADD 0x0008
-#define I2O_PARAMS_ROW_DELETE 0x0009
-#define I2O_PARAMS_TABLE_CLEAR 0x000A
-
-/*
- * I2O serial number conventions / formats
- * (circa v1.5)
- */
-
-#define I2O_SNFORMAT_UNKNOWN 0
-#define I2O_SNFORMAT_BINARY 1
-#define I2O_SNFORMAT_ASCII 2
-#define I2O_SNFORMAT_UNICODE 3
-#define I2O_SNFORMAT_LAN48_MAC 4
-#define I2O_SNFORMAT_WAN 5
-
-/* Plus new in v2.0 (Yellowstone pdf doc)
- */
-
-#define I2O_SNFORMAT_LAN64_MAC 6
-#define I2O_SNFORMAT_DDM 7
-#define I2O_SNFORMAT_IEEE_REG64 8
-#define I2O_SNFORMAT_IEEE_REG128 9
-#define I2O_SNFORMAT_UNKNOWN2 0xff
-
-/* Transaction Reply Lists (TRL) Control Word structure */
-
-#define TRL_SINGLE_FIXED_LENGTH 0x00
-#define TRL_SINGLE_VARIABLE_LENGTH 0x40
-#define TRL_MULTIPLE_FIXED_LENGTH 0x80
-
-/*
- * Messaging API values
- */
-
-#define I2O_CMD_ADAPTER_ASSIGN 0xB3
-#define I2O_CMD_ADAPTER_READ 0xB2
-#define I2O_CMD_ADAPTER_RELEASE 0xB5
-#define I2O_CMD_BIOS_INFO_SET 0xA5
-#define I2O_CMD_BOOT_DEVICE_SET 0xA7
-#define I2O_CMD_CONFIG_VALIDATE 0xBB
-#define I2O_CMD_CONN_SETUP 0xCA
-#define I2O_CMD_DDM_DESTROY 0xB1
-#define I2O_CMD_DDM_ENABLE 0xD5
-#define I2O_CMD_DDM_QUIESCE 0xC7
-#define I2O_CMD_DDM_RESET 0xD9
-#define I2O_CMD_DDM_SUSPEND 0xAF
-#define I2O_CMD_DEVICE_ASSIGN 0xB7
-#define I2O_CMD_DEVICE_RELEASE 0xB9
-#define I2O_CMD_HRT_GET 0xA8
-#define I2O_CMD_ADAPTER_CLEAR 0xBE
-#define I2O_CMD_ADAPTER_CONNECT 0xC9
-#define I2O_CMD_ADAPTER_RESET 0xBD
-#define I2O_CMD_LCT_NOTIFY 0xA2
-#define I2O_CMD_OUTBOUND_INIT 0xA1
-#define I2O_CMD_PATH_ENABLE 0xD3
-#define I2O_CMD_PATH_QUIESCE 0xC5
-#define I2O_CMD_PATH_RESET 0xD7
-#define I2O_CMD_STATIC_MF_CREATE 0xDD
-#define I2O_CMD_STATIC_MF_RELEASE 0xDF
-#define I2O_CMD_STATUS_GET 0xA0
-#define I2O_CMD_SW_DOWNLOAD 0xA9
-#define I2O_CMD_SW_UPLOAD 0xAB
-#define I2O_CMD_SW_REMOVE 0xAD
-#define I2O_CMD_SYS_ENABLE 0xD1
-#define I2O_CMD_SYS_MODIFY 0xC1
-#define I2O_CMD_SYS_QUIESCE 0xC3
-#define I2O_CMD_SYS_TAB_SET 0xA3
-
-#define I2O_CMD_UTIL_NOP 0x00
-#define I2O_CMD_UTIL_ABORT 0x01
-#define I2O_CMD_UTIL_CLAIM 0x09
-#define I2O_CMD_UTIL_RELEASE 0x0B
-#define I2O_CMD_UTIL_PARAMS_GET 0x06
-#define I2O_CMD_UTIL_PARAMS_SET 0x05
-#define I2O_CMD_UTIL_EVT_REGISTER 0x13
-#define I2O_CMD_UTIL_EVT_ACK 0x14
-#define I2O_CMD_UTIL_CONFIG_DIALOG 0x10
-#define I2O_CMD_UTIL_DEVICE_RESERVE 0x0D
-#define I2O_CMD_UTIL_DEVICE_RELEASE 0x0F
-#define I2O_CMD_UTIL_LOCK 0x17
-#define I2O_CMD_UTIL_LOCK_RELEASE 0x19
-#define I2O_CMD_UTIL_REPLY_FAULT_NOTIFY 0x15
-
-#define I2O_CMD_SCSI_EXEC 0x81
-#define I2O_CMD_SCSI_ABORT 0x83
-#define I2O_CMD_SCSI_BUSRESET 0x27
-
-#define I2O_CMD_BLOCK_READ 0x30
-#define I2O_CMD_BLOCK_WRITE 0x31
-#define I2O_CMD_BLOCK_CFLUSH 0x37
-#define I2O_CMD_BLOCK_MLOCK 0x49
-#define I2O_CMD_BLOCK_MUNLOCK 0x4B
-#define I2O_CMD_BLOCK_MMOUNT 0x41
-#define I2O_CMD_BLOCK_MEJECT 0x43
-
-#define I2O_PRIVATE_MSG 0xFF
-
-/*
- * Init Outbound Q status
- */
-
-#define I2O_CMD_OUTBOUND_INIT_IN_PROGRESS 0x01
-#define I2O_CMD_OUTBOUND_INIT_REJECTED 0x02
-#define I2O_CMD_OUTBOUND_INIT_FAILED 0x03
-#define I2O_CMD_OUTBOUND_INIT_COMPLETE 0x04
-
-/*
- * I2O Get Status State values
- */
-
-#define ADAPTER_STATE_INITIALIZING 0x01
-#define ADAPTER_STATE_RESET 0x02
-#define ADAPTER_STATE_HOLD 0x04
-#define ADAPTER_STATE_READY 0x05
-#define ADAPTER_STATE_OPERATIONAL 0x08
-#define ADAPTER_STATE_FAILED 0x10
-#define ADAPTER_STATE_FAULTED 0x11
-
-/* I2O API function return values */
-
-#define I2O_RTN_NO_ERROR 0
-#define I2O_RTN_NOT_INIT 1
-#define I2O_RTN_FREE_Q_EMPTY 2
-#define I2O_RTN_TCB_ERROR 3
-#define I2O_RTN_TRANSACTION_ERROR 4
-#define I2O_RTN_ADAPTER_ALREADY_INIT 5
-#define I2O_RTN_MALLOC_ERROR 6
-#define I2O_RTN_ADPTR_NOT_REGISTERED 7
-#define I2O_RTN_MSG_REPLY_TIMEOUT 8
-#define I2O_RTN_NO_STATUS 9
-#define I2O_RTN_NO_FIRM_VER 10
-#define I2O_RTN_NO_LINK_SPEED 11
-
-/* Reply message status defines for all messages */
-
-#define I2O_REPLY_STATUS_SUCCESS 0x00
-#define I2O_REPLY_STATUS_ABORT_DIRTY 0x01
-#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02
-#define I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03
-#define I2O_REPLY_STATUS_ERROR_DIRTY 0x04
-#define I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05
-#define I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06
-#define I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x08
-#define I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x09
-#define I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x0A
-#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0B
-#define I2O_REPLY_STATUS_PROGRESS_REPORT 0x80
-
-/* Status codes and Error Information for Parameter functions */
-
-#define I2O_PARAMS_STATUS_SUCCESS 0x00
-#define I2O_PARAMS_STATUS_BAD_KEY_ABORT 0x01
-#define I2O_PARAMS_STATUS_BAD_KEY_CONTINUE 0x02
-#define I2O_PARAMS_STATUS_BUFFER_FULL 0x03
-#define I2O_PARAMS_STATUS_BUFFER_TOO_SMALL 0x04
-#define I2O_PARAMS_STATUS_FIELD_UNREADABLE 0x05
-#define I2O_PARAMS_STATUS_FIELD_UNWRITEABLE 0x06
-#define I2O_PARAMS_STATUS_INSUFFICIENT_FIELDS 0x07
-#define I2O_PARAMS_STATUS_INVALID_GROUP_ID 0x08
-#define I2O_PARAMS_STATUS_INVALID_OPERATION 0x09
-#define I2O_PARAMS_STATUS_NO_KEY_FIELD 0x0A
-#define I2O_PARAMS_STATUS_NO_SUCH_FIELD 0x0B
-#define I2O_PARAMS_STATUS_NON_DYNAMIC_GROUP 0x0C
-#define I2O_PARAMS_STATUS_OPERATION_ERROR 0x0D
-#define I2O_PARAMS_STATUS_SCALAR_ERROR 0x0E
-#define I2O_PARAMS_STATUS_TABLE_ERROR 0x0F
-#define I2O_PARAMS_STATUS_WRONG_GROUP_TYPE 0x10
-
-/* DetailedStatusCode defines for Executive, DDM, Util and Transaction error
- * messages: Table 3-2 Detailed Status Codes.*/
-
-#define I2O_DSC_SUCCESS 0x0000
-#define I2O_DSC_BAD_KEY 0x0002
-#define I2O_DSC_TCL_ERROR 0x0003
-#define I2O_DSC_REPLY_BUFFER_FULL 0x0004
-#define I2O_DSC_NO_SUCH_PAGE 0x0005
-#define I2O_DSC_INSUFFICIENT_RESOURCE_SOFT 0x0006
-#define I2O_DSC_INSUFFICIENT_RESOURCE_HARD 0x0007
-#define I2O_DSC_CHAIN_BUFFER_TOO_LARGE 0x0009
-#define I2O_DSC_UNSUPPORTED_FUNCTION 0x000A
-#define I2O_DSC_DEVICE_LOCKED 0x000B
-#define I2O_DSC_DEVICE_RESET 0x000C
-#define I2O_DSC_INAPPROPRIATE_FUNCTION 0x000D
-#define I2O_DSC_INVALID_INITIATOR_ADDRESS 0x000E
-#define I2O_DSC_INVALID_MESSAGE_FLAGS 0x000F
-#define I2O_DSC_INVALID_OFFSET 0x0010
-#define I2O_DSC_INVALID_PARAMETER 0x0011
-#define I2O_DSC_INVALID_REQUEST 0x0012
-#define I2O_DSC_INVALID_TARGET_ADDRESS 0x0013
-#define I2O_DSC_MESSAGE_TOO_LARGE 0x0014
-#define I2O_DSC_MESSAGE_TOO_SMALL 0x0015
-#define I2O_DSC_MISSING_PARAMETER 0x0016
-#define I2O_DSC_TIMEOUT 0x0017
-#define I2O_DSC_UNKNOWN_ERROR 0x0018
-#define I2O_DSC_UNKNOWN_FUNCTION 0x0019
-#define I2O_DSC_UNSUPPORTED_VERSION 0x001A
-#define I2O_DSC_DEVICE_BUSY 0x001B
-#define I2O_DSC_DEVICE_NOT_AVAILABLE 0x001C
-
-/* Device Claim Types */
-#define I2O_CLAIM_PRIMARY 0x01000000
-#define I2O_CLAIM_MANAGEMENT 0x02000000
-#define I2O_CLAIM_AUTHORIZED 0x03000000
-#define I2O_CLAIM_SECONDARY 0x04000000
-
-/* Message header defines for VersionOffset */
-#define I2OVER15 0x0001
-#define I2OVER20 0x0002
-/* Default is 1.5, FIXME: Need support for both 1.5 and 2.0 */
-#define I2OVERSION I2OVER15
-#define SGL_OFFSET_0 I2OVERSION
-#define SGL_OFFSET_4 (0x0040 | I2OVERSION)
-#define SGL_OFFSET_5 (0x0050 | I2OVERSION)
-#define SGL_OFFSET_6 (0x0060 | I2OVERSION)
-#define SGL_OFFSET_7 (0x0070 | I2OVERSION)
-#define SGL_OFFSET_8 (0x0080 | I2OVERSION)
-#define SGL_OFFSET_9 (0x0090 | I2OVERSION)
-#define SGL_OFFSET_10 (0x00A0 | I2OVERSION)
-#define SGL_OFFSET_12 (0x00C0 | I2OVERSION)
-
-#define TRL_OFFSET_5 (0x0050 | I2OVERSION)
-#define TRL_OFFSET_6 (0x0060 | I2OVERSION)
-
- /* msg header defines for MsgFlags */
-#define MSG_STATIC 0x0100
-#define MSG_64BIT_CNTXT 0x0200
-#define MSG_MULTI_TRANS 0x1000
-#define MSG_FAIL 0x2000
-#define MSG_LAST 0x4000
-#define MSG_REPLY 0x8000
-
- /* minimum size msg */
-#define THREE_WORD_MSG_SIZE 0x00030000
-#define FOUR_WORD_MSG_SIZE 0x00040000
-#define FIVE_WORD_MSG_SIZE 0x00050000
-#define SIX_WORD_MSG_SIZE 0x00060000
-#define SEVEN_WORD_MSG_SIZE 0x00070000
-#define EIGHT_WORD_MSG_SIZE 0x00080000
-#define NINE_WORD_MSG_SIZE 0x00090000
-#define TEN_WORD_MSG_SIZE 0x000A0000
-#define I2O_MESSAGE_SIZE(x) ((x)<<16)
-
-
-/* Special TID Assignments */
-
-#define ADAPTER_TID 0
-#define HOST_TID 1
-
-#define MSG_FRAME_SIZE 128
-#define NMBR_MSG_FRAMES 128
-
-#define MSG_POOL_SIZE 16384
-
-#define I2O_POST_WAIT_OK 0
-#define I2O_POST_WAIT_TIMEOUT -ETIMEDOUT
-
-
-#endif /* __KERNEL__ */
-
-#endif /* _SCSI_I2O_H */
diff --git a/drivers/scsi/dpt/dpti_ioctl.h b/drivers/scsi/dpt/dpti_ioctl.h
deleted file mode 100644
index f60236721e0d..000000000000
--- a/drivers/scsi/dpt/dpti_ioctl.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/***************************************************************************
- dpti_ioctl.h - description
- -------------------
- begin : Thu Sep 7 2000
- copyright : (C) 2001 by Adaptec
-
- See Documentation/scsi/dpti.txt for history, notes, license info
- and credits
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-
-/***************************************************************************
- * This file is generated from osd_unix.h *
- * *************************************************************************/
-
-#ifndef _dpti_ioctl_h
-#define _dpti_ioctl_h
-
-// IOCTL interface commands
-
-#ifndef _IOWR
-# define _IOWR(x,y,z) (((x)<<8)|y)
-#endif
-#ifndef _IOW
-# define _IOW(x,y,z) (((x)<<8)|y)
-#endif
-#ifndef _IOR
-# define _IOR(x,y,z) (((x)<<8)|y)
-#endif
-#ifndef _IO
-# define _IO(x,y) (((x)<<8)|y)
-#endif
-/* EATA PassThrough Command */
-#define EATAUSRCMD _IOWR('D',65,EATA_CP)
-/* Set Debug Level If Enabled */
-#define DPT_DEBUG _IOW('D',66,int)
-/* Get Signature Structure */
-#define DPT_SIGNATURE _IOR('D',67,dpt_sig_S)
-#if defined __bsdi__
-#define DPT_SIGNATURE_PACKED _IOR('D',67,dpt_sig_S_Packed)
-#endif
-/* Get Number Of DPT Adapters */
-#define DPT_NUMCTRLS _IOR('D',68,int)
-/* Get Adapter Info Structure */
-#define DPT_CTRLINFO _IOR('D',69,CtrlInfo)
-/* Get Statistics If Enabled */
-#define DPT_STATINFO _IO('D',70)
-/* Clear Stats If Enabled */
-#define DPT_CLRSTAT _IO('D',71)
-/* Get System Info Structure */
-#define DPT_SYSINFO _IOR('D',72,sysInfo_S)
-/* Set Timeout Value */
-#define DPT_TIMEOUT _IO('D',73)
-/* Get config Data */
-#define DPT_CONFIG _IO('D',74)
-/* Get Blink LED Code */
-#define DPT_BLINKLED _IOR('D',75,int)
-/* Get Statistical information (if available) */
-#define DPT_STATS_INFO _IOR('D',80,STATS_DATA)
-/* Clear the statistical information */
-#define DPT_STATS_CLEAR _IO('D',81)
-/* Get Performance metrics */
-#define DPT_PERF_INFO _IOR('D',82,dpt_perf_t)
-/* Send an I2O command */
-#define I2OUSRCMD _IO('D',76)
-/* Inform driver to re-acquire LCT information */
-#define I2ORESCANCMD _IO('D',77)
-/* Inform driver to reset adapter */
-#define I2ORESETCMD _IO('D',78)
-/* See if the target is mounted */
-#define DPT_TARGET_BUSY _IOR('D',79, TARGET_BUSY_T)
-
-
- /* Structure Returned From Get Controller Info */
-
-typedef struct {
- uCHAR state; /* Operational state */
- uCHAR id; /* Host adapter SCSI id */
- int vect; /* Interrupt vector number */
- int base; /* Base I/O address */
- int njobs; /* # of jobs sent to HA */
- int qdepth; /* Controller queue depth. */
- int wakebase; /* mpx wakeup base index. */
- uINT SGsize; /* Scatter/Gather list size. */
- unsigned heads; /* heads for drives on cntlr. */
- unsigned sectors; /* sectors for drives on cntlr. */
- uCHAR do_drive32; /* Flag for Above 16 MB Ability */
- uCHAR BusQuiet; /* SCSI Bus Quiet Flag */
- char idPAL[4]; /* 4 Bytes Of The ID Pal */
- uCHAR primary; /* 1 For Primary, 0 For Secondary */
- uCHAR eataVersion; /* EATA Version */
- uINT cpLength; /* EATA Command Packet Length */
- uINT spLength; /* EATA Status Packet Length */
- uCHAR drqNum; /* DRQ Index (0,5,6,7) */
- uCHAR flag1; /* EATA Flags 1 (Byte 9) */
- uCHAR flag2; /* EATA Flags 2 (Byte 30) */
-} CtrlInfo;
-
-typedef struct {
- uSHORT length; // Remaining length of this
- uSHORT drvrHBAnum; // Relative HBA # used by the driver
- uINT baseAddr; // Base I/O address
- uSHORT blinkState; // Blink LED state (0=Not in blink LED)
- uCHAR pciBusNum; // PCI Bus # (Optional)
- uCHAR pciDeviceNum; // PCI Device # (Optional)
- uSHORT hbaFlags; // Miscellaneous HBA flags
- uSHORT Interrupt; // Interrupt set for this device.
-# if (defined(_DPT_ARC))
- uINT baseLength;
- ADAPTER_OBJECT *AdapterObject;
- LARGE_INTEGER DmaLogicalAddress;
- PVOID DmaVirtualAddress;
- LARGE_INTEGER ReplyLogicalAddress;
- PVOID ReplyVirtualAddress;
-# else
- uINT reserved1; // Reserved for future expansion
- uINT reserved2; // Reserved for future expansion
- uINT reserved3; // Reserved for future expansion
-# endif
-} drvrHBAinfo_S;
-
-typedef struct TARGET_BUSY
-{
- uLONG channel;
- uLONG id;
- uLONG lun;
- uLONG isBusy;
-} TARGET_BUSY_T;
-
-#endif
-
diff --git a/drivers/scsi/dpt/dptsig.h b/drivers/scsi/dpt/dptsig.h
deleted file mode 100644
index a6644b332b53..000000000000
--- a/drivers/scsi/dpt/dptsig.h
+++ /dev/null
@@ -1,336 +0,0 @@
-/* BSDI dptsig.h,v 1.7 1998/06/03 19:15:00 karels Exp */
-
-/*
- * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
- * All rights reserved.
- *
- * Redistribution and use in source form, with or without modification, are
- * permitted provided that redistributions of source code must retain the
- * above copyright notice, this list of conditions and the following disclaimer.
- *
- * This software is provided `as is' by Distributed Processing Technology and
- * any express or implied warranties, including, but not limited to, the
- * implied warranties of merchantability and fitness for a particular purpose,
- * are disclaimed. In no event shall Distributed Processing Technology be
- * liable for any direct, indirect, incidental, special, exemplary or
- * consequential damages (including, but not limited to, procurement of
- * substitute goods or services; loss of use, data, or profits; or business
- * interruptions) however caused and on any theory of liability, whether in
- * contract, strict liability, or tort (including negligence or otherwise)
- * arising in any way out of the use of this driver software, even if advised
- * of the possibility of such damage.
- *
- */
-
-#ifndef __DPTSIG_H_
-#define __DPTSIG_H_
-#ifdef _SINIX_ADDON
-#include "dpt.h"
-#endif
-/* DPT SIGNATURE SPEC AND HEADER FILE */
-/* Signature Version 1 (sorry no 'A') */
-
-/* to make sure we are talking the same size under all OS's */
-typedef unsigned char sigBYTE;
-typedef unsigned short sigWORD;
-typedef unsigned int sigINT;
-
-/*
- * use sigWORDLittleEndian for:
- * dsCapabilities
- * dsDeviceSupp
- * dsAdapterSupp
- * dsApplication
- * use sigLONGLittleEndian for:
- * dsOS
- * so that the sig can be standardised to Little Endian
- */
-#if (defined(_DPT_BIG_ENDIAN))
-# define sigWORDLittleEndian(x) ((((x)&0xFF)<<8)|(((x)>>8)&0xFF))
-# define sigLONGLittleEndian(x) \
- ((((x)&0xFF)<<24) | \
- (((x)&0xFF00)<<8) | \
- (((x)&0xFF0000L)>>8) | \
- (((x)&0xFF000000L)>>24))
-#else
-# define sigWORDLittleEndian(x) (x)
-# define sigLONGLittleEndian(x) (x)
-#endif
-
-/* must make sure the structure is not word or double-word aligned */
-/* --------------------------------------------------------------- */
-/* Borland will ignore the following pragma: */
-/* Word alignment is OFF by default. If in the, IDE make */
-/* sure that Options | Compiler | Code Generation | Word Alignment */
-/* is not checked. If using BCC, do not use the -a option. */
-
-#ifndef NO_PACK
-#if defined (_DPT_AIX)
-#pragma options align=packed
-#else
-#pragma pack(1)
-#endif /* aix */
-#endif
-/* For the Macintosh */
-#ifdef STRUCTALIGNMENTSUPPORTED
-#pragma options align=mac68k
-#endif
-
-
-/* Current Signature Version - sigBYTE dsSigVersion; */
-/* ------------------------------------------------------------------ */
-#define SIG_VERSION 1
-
-/* Processor Family - sigBYTE dsProcessorFamily; DISTINCT VALUES */
-/* ------------------------------------------------------------------ */
-/* What type of processor the file is meant to run on. */
-/* This will let us know whether to read sigWORDs as high/low or low/high. */
-#define PROC_INTEL 0x00 /* Intel 80x86/ia64 */
-#define PROC_MOTOROLA 0x01 /* Motorola 68K */
-#define PROC_MIPS4000 0x02 /* MIPS RISC 4000 */
-#define PROC_ALPHA 0x03 /* DEC Alpha */
-#define PROC_POWERPC 0x04 /* IBM Power PC */
-#define PROC_i960 0x05 /* Intel i960 */
-#define PROC_ULTRASPARC 0x06 /* SPARC processor */
-
-/* Specific Minimim Processor - sigBYTE dsProcessor; FLAG BITS */
-/* ------------------------------------------------------------------ */
-/* Different bit definitions dependent on processor_family */
-
-/* PROC_INTEL: */
-#define PROC_8086 0x01 /* Intel 8086 */
-#define PROC_286 0x02 /* Intel 80286 */
-#define PROC_386 0x04 /* Intel 80386 */
-#define PROC_486 0x08 /* Intel 80486 */
-#define PROC_PENTIUM 0x10 /* Intel 586 aka P5 aka Pentium */
-#define PROC_SEXIUM 0x20 /* Intel 686 aka P6 aka Pentium Pro or MMX */
-#define PROC_IA64 0x40 /* Intel IA64 processor */
-
-/* PROC_i960: */
-#define PROC_960RX 0x01 /* Intel 80960RC/RD */
-#define PROC_960HX 0x02 /* Intel 80960HA/HD/HT */
-
-/* PROC_MOTOROLA: */
-#define PROC_68000 0x01 /* Motorola 68000 */
-#define PROC_68010 0x02 /* Motorola 68010 */
-#define PROC_68020 0x04 /* Motorola 68020 */
-#define PROC_68030 0x08 /* Motorola 68030 */
-#define PROC_68040 0x10 /* Motorola 68040 */
-
-/* PROC_POWERPC */
-#define PROC_PPC601 0x01 /* PowerPC 601 */
-#define PROC_PPC603 0x02 /* PowerPC 603 */
-#define PROC_PPC604 0x04 /* PowerPC 604 */
-
-/* PROC_MIPS4000: */
-#define PROC_R4000 0x01 /* MIPS R4000 */
-
-/* Filetype - sigBYTE dsFiletype; DISTINCT VALUES */
-/* ------------------------------------------------------------------ */
-#define FT_EXECUTABLE 0 /* Executable Program */
-#define FT_SCRIPT 1 /* Script/Batch File??? */
-#define FT_HBADRVR 2 /* HBA Driver */
-#define FT_OTHERDRVR 3 /* Other Driver */
-#define FT_IFS 4 /* Installable Filesystem Driver */
-#define FT_ENGINE 5 /* DPT Engine */
-#define FT_COMPDRVR 6 /* Compressed Driver Disk */
-#define FT_LANGUAGE 7 /* Foreign Language file */
-#define FT_FIRMWARE 8 /* Downloadable or actual Firmware */
-#define FT_COMMMODL 9 /* Communications Module */
-#define FT_INT13 10 /* INT 13 style HBA Driver */
-#define FT_HELPFILE 11 /* Help file */
-#define FT_LOGGER 12 /* Event Logger */
-#define FT_INSTALL 13 /* An Install Program */
-#define FT_LIBRARY 14 /* Storage Manager Real-Mode Calls */
-#define FT_RESOURCE 15 /* Storage Manager Resource File */
-#define FT_MODEM_DB 16 /* Storage Manager Modem Database */
-
-/* Filetype flags - sigBYTE dsFiletypeFlags; FLAG BITS */
-/* ------------------------------------------------------------------ */
-#define FTF_DLL 0x01 /* Dynamic Link Library */
-#define FTF_NLM 0x02 /* Netware Loadable Module */
-#define FTF_OVERLAYS 0x04 /* Uses overlays */
-#define FTF_DEBUG 0x08 /* Debug version */
-#define FTF_TSR 0x10 /* TSR */
-#define FTF_SYS 0x20 /* DOS Loadable driver */
-#define FTF_PROTECTED 0x40 /* Runs in protected mode */
-#define FTF_APP_SPEC 0x80 /* Application Specific */
-#define FTF_ROM (FTF_SYS|FTF_TSR) /* Special Case */
-
-/* OEM - sigBYTE dsOEM; DISTINCT VALUES */
-/* ------------------------------------------------------------------ */
-#define OEM_DPT 0 /* DPT */
-#define OEM_ATT 1 /* ATT */
-#define OEM_NEC 2 /* NEC */
-#define OEM_ALPHA 3 /* Alphatronix */
-#define OEM_AST 4 /* AST */
-#define OEM_OLIVETTI 5 /* Olivetti */
-#define OEM_SNI 6 /* Siemens/Nixdorf */
-#define OEM_SUN 7 /* SUN Microsystems */
-
-/* Operating System - sigLONG dsOS; FLAG BITS */
-/* ------------------------------------------------------------------ */
-#define OS_DOS 0x00000001 /* PC/MS-DOS */
-#define OS_WINDOWS 0x00000002 /* Microsoft Windows 3.x */
-#define OS_WINDOWS_NT 0x00000004 /* Microsoft Windows NT */
-#define OS_OS2M 0x00000008 /* OS/2 1.2.x,MS 1.3.0,IBM 1.3.x - Monolithic */
-#define OS_OS2L 0x00000010 /* Microsoft OS/2 1.301 - LADDR */
-#define OS_OS22x 0x00000020 /* IBM OS/2 2.x */
-#define OS_NW286 0x00000040 /* Novell NetWare 286 */
-#define OS_NW386 0x00000080 /* Novell NetWare 386 */
-#define OS_GEN_UNIX 0x00000100 /* Generic Unix */
-#define OS_SCO_UNIX 0x00000200 /* SCO Unix */
-#define OS_ATT_UNIX 0x00000400 /* ATT Unix */
-#define OS_UNIXWARE 0x00000800 /* USL Unix */
-#define OS_INT_UNIX 0x00001000 /* Interactive Unix */
-#define OS_SOLARIS 0x00002000 /* SunSoft Solaris */
-#define OS_QNX 0x00004000 /* QNX for Tom Moch */
-#define OS_NEXTSTEP 0x00008000 /* NeXTSTEP/OPENSTEP/MACH */
-#define OS_BANYAN 0x00010000 /* Banyan Vines */
-#define OS_OLIVETTI_UNIX 0x00020000/* Olivetti Unix */
-#define OS_MAC_OS 0x00040000 /* Mac OS */
-#define OS_WINDOWS_95 0x00080000 /* Microsoft Windows '95 */
-#define OS_NW4x 0x00100000 /* Novell Netware 4.x */
-#define OS_BSDI_UNIX 0x00200000 /* BSDi Unix BSD/OS 2.0 and up */
-#define OS_AIX_UNIX 0x00400000 /* AIX Unix */
-#define OS_FREE_BSD 0x00800000 /* FreeBSD Unix */
-#define OS_LINUX 0x01000000 /* Linux */
-#define OS_DGUX_UNIX 0x02000000 /* Data General Unix */
-#define OS_SINIX_N 0x04000000 /* SNI SINIX-N */
-#define OS_PLAN9 0x08000000 /* ATT Plan 9 */
-#define OS_TSX 0x10000000 /* SNH TSX-32 */
-
-#define OS_OTHER 0x80000000 /* Other */
-
-/* Capabilities - sigWORD dsCapabilities; FLAG BITS */
-/* ------------------------------------------------------------------ */
-#define CAP_RAID0 0x0001 /* RAID-0 */
-#define CAP_RAID1 0x0002 /* RAID-1 */
-#define CAP_RAID3 0x0004 /* RAID-3 */
-#define CAP_RAID5 0x0008 /* RAID-5 */
-#define CAP_SPAN 0x0010 /* Spanning */
-#define CAP_PASS 0x0020 /* Provides passthrough */
-#define CAP_OVERLAP 0x0040 /* Passthrough supports overlapped commands */
-#define CAP_ASPI 0x0080 /* Supports ASPI Command Requests */
-#define CAP_ABOVE16MB 0x0100 /* ISA Driver supports greater than 16MB */
-#define CAP_EXTEND 0x8000 /* Extended info appears after description */
-#ifdef SNI_MIPS
-#define CAP_CACHEMODE 0x1000 /* dpt_force_cache is set in driver */
-#endif
-
-/* Devices Supported - sigWORD dsDeviceSupp; FLAG BITS */
-/* ------------------------------------------------------------------ */
-#define DEV_DASD 0x0001 /* DASD (hard drives) */
-#define DEV_TAPE 0x0002 /* Tape drives */
-#define DEV_PRINTER 0x0004 /* Printers */
-#define DEV_PROC 0x0008 /* Processors */
-#define DEV_WORM 0x0010 /* WORM drives */
-#define DEV_CDROM 0x0020 /* CD-ROM drives */
-#define DEV_SCANNER 0x0040 /* Scanners */
-#define DEV_OPTICAL 0x0080 /* Optical Drives */
-#define DEV_JUKEBOX 0x0100 /* Jukebox */
-#define DEV_COMM 0x0200 /* Communications Devices */
-#define DEV_OTHER 0x0400 /* Other Devices */
-#define DEV_ALL 0xFFFF /* All SCSI Devices */
-
-/* Adapters Families Supported - sigWORD dsAdapterSupp; FLAG BITS */
-/* ------------------------------------------------------------------ */
-#define ADF_2001 0x0001 /* PM2001 */
-#define ADF_2012A 0x0002 /* PM2012A */
-#define ADF_PLUS_ISA 0x0004 /* PM2011,PM2021 */
-#define ADF_PLUS_EISA 0x0008 /* PM2012B,PM2022 */
-#define ADF_SC3_ISA 0x0010 /* PM2021 */
-#define ADF_SC3_EISA 0x0020 /* PM2022,PM2122, etc */
-#define ADF_SC3_PCI 0x0040 /* SmartCache III PCI */
-#define ADF_SC4_ISA 0x0080 /* SmartCache IV ISA */
-#define ADF_SC4_EISA 0x0100 /* SmartCache IV EISA */
-#define ADF_SC4_PCI 0x0200 /* SmartCache IV PCI */
-#define ADF_SC5_PCI 0x0400 /* Fifth Generation I2O products */
-/*
- * Combinations of products
- */
-#define ADF_ALL_2000 (ADF_2001|ADF_2012A)
-#define ADF_ALL_PLUS (ADF_PLUS_ISA|ADF_PLUS_EISA)
-#define ADF_ALL_SC3 (ADF_SC3_ISA|ADF_SC3_EISA|ADF_SC3_PCI)
-#define ADF_ALL_SC4 (ADF_SC4_ISA|ADF_SC4_EISA|ADF_SC4_PCI)
-#define ADF_ALL_SC5 (ADF_SC5_PCI)
-/* All EATA Cacheing Products */
-#define ADF_ALL_CACHE (ADF_ALL_PLUS|ADF_ALL_SC3|ADF_ALL_SC4)
-/* All EATA Bus Mastering Products */
-#define ADF_ALL_MASTER (ADF_2012A|ADF_ALL_CACHE)
-/* All EATA Adapter Products */
-#define ADF_ALL_EATA (ADF_2001|ADF_ALL_MASTER)
-#define ADF_ALL ADF_ALL_EATA
-
-/* Application - sigWORD dsApplication; FLAG BITS */
-/* ------------------------------------------------------------------ */
-#define APP_DPTMGR 0x0001 /* DPT Storage Manager */
-#define APP_ENGINE 0x0002 /* DPT Engine */
-#define APP_SYTOS 0x0004 /* Sytron Sytos Plus */
-#define APP_CHEYENNE 0x0008 /* Cheyenne ARCServe + ARCSolo */
-#define APP_MSCDEX 0x0010 /* Microsoft CD-ROM extensions */
-#define APP_NOVABACK 0x0020 /* NovaStor Novaback */
-#define APP_AIM 0x0040 /* Archive Information Manager */
-
-/* Requirements - sigBYTE dsRequirements; FLAG BITS */
-/* ------------------------------------------------------------------ */
-#define REQ_SMARTROM 0x01 /* Requires SmartROM to be present */
-#define REQ_DPTDDL 0x02 /* Requires DPTDDL.SYS to be loaded */
-#define REQ_HBA_DRIVER 0x04 /* Requires an HBA driver to be loaded */
-#define REQ_ASPI_TRAN 0x08 /* Requires an ASPI Transport Modules */
-#define REQ_ENGINE 0x10 /* Requires a DPT Engine to be loaded */
-#define REQ_COMM_ENG 0x20 /* Requires a DPT Communications Engine */
-
-/*
- * You may adjust dsDescription_size with an override to a value less than
- * 50 so that the structure allocates less real space.
- */
-#if (!defined(dsDescription_size))
-# define dsDescription_size 50
-#endif
-
-typedef struct dpt_sig {
- char dsSignature[6]; /* ALWAYS "dPtSiG" */
- sigBYTE dsSigVersion; /* signature version (currently 1) */
- sigBYTE dsProcessorFamily; /* what type of processor */
- sigBYTE dsProcessor; /* precise processor */
- sigBYTE dsFiletype; /* type of file */
- sigBYTE dsFiletypeFlags; /* flags to specify load type, etc. */
- sigBYTE dsOEM; /* OEM file was created for */
- sigINT dsOS; /* which Operating systems */
- sigWORD dsCapabilities; /* RAID levels, etc. */
- sigWORD dsDeviceSupp; /* Types of SCSI devices supported */
- sigWORD dsAdapterSupp; /* DPT adapter families supported */
- sigWORD dsApplication; /* applications file is for */
- sigBYTE dsRequirements; /* Other driver dependencies */
- sigBYTE dsVersion; /* 1 */
- sigBYTE dsRevision; /* 'J' */
- sigBYTE dsSubRevision; /* '9' ' ' if N/A */
- sigBYTE dsMonth; /* creation month */
- sigBYTE dsDay; /* creation day */
- sigBYTE dsYear; /* creation year since 1980 (1993=13) */
- /* description (NULL terminated) */
- char dsDescription[dsDescription_size];
-} dpt_sig_S;
-/* 32 bytes minimum - with no description. Put NULL at description[0] */
-/* 81 bytes maximum - with 49 character description plus NULL. */
-
-/* This line added at Roycroft's request */
-/* Microsoft's NT compiler gets confused if you do a pack and don't */
-/* restore it. */
-
-#ifndef NO_UNPACK
-#if defined (_DPT_AIX)
-#pragma options align=reset
-#elif defined (UNPACK_FOUR)
-#pragma pack(4)
-#else
-#pragma pack()
-#endif /* aix */
-#endif
-/* For the Macintosh */
-#ifdef STRUCTALIGNMENTSUPPORTED
-#pragma options align=reset
-#endif
-
-#endif
diff --git a/drivers/scsi/dpt/osd_defs.h b/drivers/scsi/dpt/osd_defs.h
deleted file mode 100644
index de3ae5722982..000000000000
--- a/drivers/scsi/dpt/osd_defs.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* BSDI osd_defs.h,v 1.4 1998/06/03 19:14:58 karels Exp */
-/*
- * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
- * All rights reserved.
- *
- * Redistribution and use in source form, with or without modification, are
- * permitted provided that redistributions of source code must retain the
- * above copyright notice, this list of conditions and the following disclaimer.
- *
- * This software is provided `as is' by Distributed Processing Technology and
- * any express or implied warranties, including, but not limited to, the
- * implied warranties of merchantability and fitness for a particular purpose,
- * are disclaimed. In no event shall Distributed Processing Technology be
- * liable for any direct, indirect, incidental, special, exemplary or
- * consequential damages (including, but not limited to, procurement of
- * substitute goods or services; loss of use, data, or profits; or business
- * interruptions) however caused and on any theory of liability, whether in
- * contract, strict liability, or tort (including negligence or otherwise)
- * arising in any way out of the use of this driver software, even if advised
- * of the possibility of such damage.
- *
- */
-
-#ifndef _OSD_DEFS_H
-#define _OSD_DEFS_H
-
-/*File - OSD_DEFS.H
- ****************************************************************************
- *
- *Description:
- *
- * This file contains the OS dependent defines. This file is included
- *in osd_util.h and provides the OS specific defines for that file.
- *
- *Copyright Distributed Processing Technology, Corp.
- * 140 Candace Dr.
- * Maitland, Fl. 32751 USA
- * Phone: (407) 830-5522 Fax: (407) 260-5366
- * All Rights Reserved
- *
- *Author: Doug Anderson
- *Date: 1/31/94
- *
- *Editors:
- *
- *Remarks:
- *
- *
- *****************************************************************************/
-
-
-/*Definitions - Defines & Constants ----------------------------------------- */
-
- /* Define the operating system */
-#if (defined(__linux__))
-# define _DPT_LINUX
-#elif (defined(__bsdi__))
-# define _DPT_BSDI
-#elif (defined(__FreeBSD__))
-# define _DPT_FREE_BSD
-#else
-# define _DPT_SCO
-#endif
-
-#if defined (ZIL_CURSES)
-#define _DPT_CURSES
-#else
-#define _DPT_MOTIF
-#endif
-
- /* Redefine 'far' to nothing - no far pointer type required in UNIX */
-#define far
-
- /* Define the mutually exclusive semaphore type */
-#define SEMAPHORE_T unsigned int *
- /* Define a handle to a DLL */
-#define DLL_HANDLE_T unsigned int *
-
-#endif
diff --git a/drivers/scsi/dpt/osd_util.h b/drivers/scsi/dpt/osd_util.h
deleted file mode 100644
index b2613c2eaac7..000000000000
--- a/drivers/scsi/dpt/osd_util.h
+++ /dev/null
@@ -1,358 +0,0 @@
-/* BSDI osd_util.h,v 1.8 1998/06/03 19:14:58 karels Exp */
-
-/*
- * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
- * All rights reserved.
- *
- * Redistribution and use in source form, with or without modification, are
- * permitted provided that redistributions of source code must retain the
- * above copyright notice, this list of conditions and the following disclaimer.
- *
- * This software is provided `as is' by Distributed Processing Technology and
- * any express or implied warranties, including, but not limited to, the
- * implied warranties of merchantability and fitness for a particular purpose,
- * are disclaimed. In no event shall Distributed Processing Technology be
- * liable for any direct, indirect, incidental, special, exemplary or
- * consequential damages (including, but not limited to, procurement of
- * substitute goods or services; loss of use, data, or profits; or business
- * interruptions) however caused and on any theory of liability, whether in
- * contract, strict liability, or tort (including negligence or otherwise)
- * arising in any way out of the use of this driver software, even if advised
- * of the possibility of such damage.
- *
- */
-
-#ifndef __OSD_UTIL_H
-#define __OSD_UTIL_H
-
-/*File - OSD_UTIL.H
- ****************************************************************************
- *
- *Description:
- *
- * This file contains defines and function prototypes that are
- *operating system dependent. The resources defined in this file
- *are not specific to any particular application.
- *
- *Copyright Distributed Processing Technology, Corp.
- * 140 Candace Dr.
- * Maitland, Fl. 32751 USA
- * Phone: (407) 830-5522 Fax: (407) 260-5366
- * All Rights Reserved
- *
- *Author: Doug Anderson
- *Date: 1/7/94
- *
- *Editors:
- *
- *Remarks:
- *
- *
- *****************************************************************************/
-
-
-/*Definitions - Defines & Constants ----------------------------------------- */
-
-/*----------------------------- */
-/* Operating system selections: */
-/*----------------------------- */
-
-/*#define _DPT_MSDOS */
-/*#define _DPT_WIN_3X */
-/*#define _DPT_WIN_4X */
-/*#define _DPT_WIN_NT */
-/*#define _DPT_NETWARE */
-/*#define _DPT_OS2 */
-/*#define _DPT_SCO */
-/*#define _DPT_UNIXWARE */
-/*#define _DPT_SOLARIS */
-/*#define _DPT_NEXTSTEP */
-/*#define _DPT_BANYAN */
-
-/*-------------------------------- */
-/* Include the OS specific defines */
-/*-------------------------------- */
-
-/*#define OS_SELECTION From Above List */
-/*#define SEMAPHORE_T ??? */
-/*#define DLL_HANDLE_T ??? */
-
-#if (defined(KERNEL) && (defined(__FreeBSD__) || defined(__bsdi__)))
-# include "i386/isa/dpt_osd_defs.h"
-#else
-# include "osd_defs.h"
-#endif
-
-#ifndef DPT_UNALIGNED
- #define DPT_UNALIGNED
-#endif
-
-#ifndef DPT_EXPORT
- #define DPT_EXPORT
-#endif
-
-#ifndef DPT_IMPORT
- #define DPT_IMPORT
-#endif
-
-#ifndef DPT_RUNTIME_IMPORT
- #define DPT_RUNTIME_IMPORT DPT_IMPORT
-#endif
-
-/*--------------------- */
-/* OS dependent defines */
-/*--------------------- */
-
-#if defined (_DPT_MSDOS) || defined (_DPT_WIN_3X)
- #define _DPT_16_BIT
-#else
- #define _DPT_32_BIT
-#endif
-
-#if defined (_DPT_SCO) || defined (_DPT_UNIXWARE) || defined (_DPT_SOLARIS) || defined (_DPT_AIX) || defined (SNI_MIPS) || defined (_DPT_BSDI) || defined (_DPT_FREE_BSD) || defined(_DPT_LINUX)
- #define _DPT_UNIX
-#endif
-
-#if defined (_DPT_WIN_3x) || defined (_DPT_WIN_4X) || defined (_DPT_WIN_NT) \
- || defined (_DPT_OS2)
- #define _DPT_DLL_SUPPORT
-#endif
-
-#if !defined (_DPT_MSDOS) && !defined (_DPT_WIN_3X) && !defined (_DPT_NETWARE)
- #define _DPT_PREEMPTIVE
-#endif
-
-#if !defined (_DPT_MSDOS) && !defined (_DPT_WIN_3X)
- #define _DPT_MULTI_THREADED
-#endif
-
-#if !defined (_DPT_MSDOS)
- #define _DPT_MULTI_TASKING
-#endif
-
- /* These exist for platforms that */
- /* chunk when accessing mis-aligned */
- /* data */
-#if defined (SNI_MIPS) || defined (_DPT_SOLARIS)
- #if defined (_DPT_BIG_ENDIAN)
- #if !defined (_DPT_STRICT_ALIGN)
- #define _DPT_STRICT_ALIGN
- #endif
- #endif
-#endif
-
- /* Determine if in C or C++ mode */
-#ifdef __cplusplus
- #define _DPT_CPP
-#else
- #define _DPT_C
-#endif
-
-/*-------------------------------------------------------------------*/
-/* Under Solaris the compiler refuses to accept code like: */
-/* { {"DPT"}, 0, NULL .... }, */
-/* and complains about the {"DPT"} part by saying "cannot use { } */
-/* to initialize char*". */
-/* */
-/* By defining these ugly macros we can get around this and also */
-/* not have to copy and #ifdef large sections of code. I know that */
-/* these macros are *really* ugly, but they should help reduce */
-/* maintenance in the long run. */
-/* */
-/*-------------------------------------------------------------------*/
-#if !defined (DPTSQO)
- #if defined (_DPT_SOLARIS)
- #define DPTSQO
- #define DPTSQC
- #else
- #define DPTSQO {
- #define DPTSQC }
- #endif /* solaris */
-#endif /* DPTSQO */
-
-
-/*---------------------- */
-/* OS dependent typedefs */
-/*---------------------- */
-
-#if defined (_DPT_MSDOS) || defined (_DPT_SCO)
- #define BYTE unsigned char
- #define WORD unsigned short
-#endif
-
-#ifndef _DPT_TYPEDEFS
- #define _DPT_TYPEDEFS
- typedef unsigned char uCHAR;
- typedef unsigned short uSHORT;
- typedef unsigned int uINT;
- typedef unsigned long uLONG;
-
- typedef union {
- uCHAR u8[4];
- uSHORT u16[2];
- uLONG u32;
- } access_U;
-#endif
-
-#if !defined (NULL)
- #define NULL 0
-#endif
-
-
-/*Prototypes - function ----------------------------------------------------- */
-
-#ifdef __cplusplus
- extern "C" { /* Declare all these functions as "C" functions */
-#endif
-
-/*------------------------ */
-/* Byte reversal functions */
-/*------------------------ */
-
- /* Reverses the byte ordering of a 2 byte variable */
-#if (!defined(osdSwap2))
- uSHORT osdSwap2(DPT_UNALIGNED uSHORT *);
-#endif // !osdSwap2
-
- /* Reverses the byte ordering of a 4 byte variable and shifts left 8 bits */
-#if (!defined(osdSwap3))
- uLONG osdSwap3(DPT_UNALIGNED uLONG *);
-#endif // !osdSwap3
-
-
-#ifdef _DPT_NETWARE
- #include "novpass.h" /* For DPT_Bswapl() prototype */
- /* Inline the byte swap */
- #ifdef __cplusplus
- inline uLONG osdSwap4(uLONG *inLong) {
- return *inLong = DPT_Bswapl(*inLong);
- }
- #else
- #define osdSwap4(inLong) DPT_Bswapl(inLong)
- #endif // cplusplus
-#else
- /* Reverses the byte ordering of a 4 byte variable */
-# if (!defined(osdSwap4))
- uLONG osdSwap4(DPT_UNALIGNED uLONG *);
-# endif // !osdSwap4
-
- /* The following functions ALWAYS swap regardless of the *
- * presence of DPT_BIG_ENDIAN */
-
- uSHORT trueSwap2(DPT_UNALIGNED uSHORT *);
- uLONG trueSwap4(DPT_UNALIGNED uLONG *);
-
-#endif // netware
-
-
-/*-------------------------------------*
- * Network order swap functions *
- * *
- * These functions/macros will be used *
- * by the structure insert()/extract() *
- * functions. *
- *
- * We will enclose all structure *
- * portability modifications inside *
- * #ifdefs. When we are ready, we *
- * will #define DPT_PORTABLE to begin *
- * using the modifications. *
- *-------------------------------------*/
-uLONG netSwap4(uLONG val);
-
-#if defined (_DPT_BIG_ENDIAN)
-
-// for big-endian we need to swap
-
-#ifndef NET_SWAP_2
-#define NET_SWAP_2(x) (((x) >> 8) | ((x) << 8))
-#endif // NET_SWAP_2
-
-#ifndef NET_SWAP_4
-#define NET_SWAP_4(x) netSwap4((x))
-#endif // NET_SWAP_4
-
-#else
-
-// for little-endian we don't need to do anything
-
-#ifndef NET_SWAP_2
-#define NET_SWAP_2(x) (x)
-#endif // NET_SWAP_2
-
-#ifndef NET_SWAP_4
-#define NET_SWAP_4(x) (x)
-#endif // NET_SWAP_4
-
-#endif // big endian
-
-
-
-/*----------------------------------- */
-/* Run-time loadable module functions */
-/*----------------------------------- */
-
- /* Loads the specified run-time loadable DLL */
-DLL_HANDLE_T osdLoadModule(uCHAR *);
- /* Unloads the specified run-time loadable DLL */
-uSHORT osdUnloadModule(DLL_HANDLE_T);
- /* Returns a pointer to a function inside a run-time loadable DLL */
-void * osdGetFnAddr(DLL_HANDLE_T,uCHAR *);
-
-/*--------------------------------------- */
-/* Mutually exclusive semaphore functions */
-/*--------------------------------------- */
-
- /* Create a named semaphore */
-SEMAPHORE_T osdCreateNamedSemaphore(char *);
- /* Create a mutually exlusive semaphore */
-SEMAPHORE_T osdCreateSemaphore(void);
- /* create an event semaphore */
-SEMAPHORE_T osdCreateEventSemaphore(void);
- /* create a named event semaphore */
-SEMAPHORE_T osdCreateNamedEventSemaphore(char *);
-
- /* Destroy the specified mutually exclusive semaphore object */
-uSHORT osdDestroySemaphore(SEMAPHORE_T);
- /* Request access to the specified mutually exclusive semaphore */
-uLONG osdRequestSemaphore(SEMAPHORE_T,uLONG);
- /* Release access to the specified mutually exclusive semaphore */
-uSHORT osdReleaseSemaphore(SEMAPHORE_T);
- /* wait for a event to happen */
-uLONG osdWaitForEventSemaphore(SEMAPHORE_T, uLONG);
- /* signal an event */
-uLONG osdSignalEventSemaphore(SEMAPHORE_T);
- /* reset the event */
-uLONG osdResetEventSemaphore(SEMAPHORE_T);
-
-/*----------------- */
-/* Thread functions */
-/*----------------- */
-
- /* Releases control to the task switcher in non-preemptive */
- /* multitasking operating systems. */
-void osdSwitchThreads(void);
-
- /* Starts a thread function */
-uLONG osdStartThread(void *,void *);
-
-/* what is my thread id */
-uLONG osdGetThreadID(void);
-
-/* wakes up the specifed thread */
-void osdWakeThread(uLONG);
-
-/* osd sleep for x milliseconds */
-void osdSleep(uLONG);
-
-#define DPT_THREAD_PRIORITY_LOWEST 0x00
-#define DPT_THREAD_PRIORITY_NORMAL 0x01
-#define DPT_THREAD_PRIORITY_HIGHEST 0x02
-
-uCHAR osdSetThreadPriority(uLONG tid, uCHAR priority);
-
-#ifdef __cplusplus
- } /* end the xtern "C" declaration */
-#endif
-
-#endif /* osd_util_h */
diff --git a/drivers/scsi/dpt/sys_info.h b/drivers/scsi/dpt/sys_info.h
deleted file mode 100644
index a4aa1c31ff72..000000000000
--- a/drivers/scsi/dpt/sys_info.h
+++ /dev/null
@@ -1,417 +0,0 @@
-/* BSDI sys_info.h,v 1.6 1998/06/03 19:14:59 karels Exp */
-
-/*
- * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
- * All rights reserved.
- *
- * Redistribution and use in source form, with or without modification, are
- * permitted provided that redistributions of source code must retain the
- * above copyright notice, this list of conditions and the following disclaimer.
- *
- * This software is provided `as is' by Distributed Processing Technology and
- * any express or implied warranties, including, but not limited to, the
- * implied warranties of merchantability and fitness for a particular purpose,
- * are disclaimed. In no event shall Distributed Processing Technology be
- * liable for any direct, indirect, incidental, special, exemplary or
- * consequential damages (including, but not limited to, procurement of
- * substitute goods or services; loss of use, data, or profits; or business
- * interruptions) however caused and on any theory of liability, whether in
- * contract, strict liability, or tort (including negligence or otherwise)
- * arising in any way out of the use of this driver software, even if advised
- * of the possibility of such damage.
- *
- */
-
-#ifndef __SYS_INFO_H
-#define __SYS_INFO_H
-
-/*File - SYS_INFO.H
- ****************************************************************************
- *
- *Description:
- *
- * This file contains structure definitions for the OS dependent
- *layer system information buffers.
- *
- *Copyright Distributed Processing Technology, Corp.
- * 140 Candace Dr.
- * Maitland, Fl. 32751 USA
- * Phone: (407) 830-5522 Fax: (407) 260-5366
- * All Rights Reserved
- *
- *Author: Don Kemper
- *Date: 5/10/94
- *
- *Editors:
- *
- *Remarks:
- *
- *
- *****************************************************************************/
-
-
-/*Include Files ------------------------------------------------------------- */
-
-#include "osd_util.h"
-
-#ifndef NO_PACK
-#if defined (_DPT_AIX)
-#pragma options align=packed
-#else
-#pragma pack(1)
-#endif /* aix */
-#endif // no unpack
-
-
-/*struct - driveParam_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the drive parameters seen during
- *booting.
- *
- *---------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
- struct driveParam_S {
-#else
- typedef struct {
-#endif
-
- uSHORT cylinders; /* Up to 1024 */
- uCHAR heads; /* Up to 255 */
- uCHAR sectors; /* Up to 63 */
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } driveParam_S;
-#endif
-/*driveParam_S - end */
-
-
-/*struct - sysInfo_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the command system information that
- *should be returned by every OS dependent layer.
- *
- *---------------------------------------------------------------------------*/
-
-/*flags - bit definitions */
-#define SI_CMOS_Valid 0x0001
-#define SI_NumDrivesValid 0x0002
-#define SI_ProcessorValid 0x0004
-#define SI_MemorySizeValid 0x0008
-#define SI_DriveParamsValid 0x0010
-#define SI_SmartROMverValid 0x0020
-#define SI_OSversionValid 0x0040
-#define SI_OSspecificValid 0x0080 /* 1 if OS structure returned */
-#define SI_BusTypeValid 0x0100
-
-#define SI_ALL_VALID 0x0FFF /* All Std SysInfo is valid */
-#define SI_NO_SmartROM 0x8000
-
-/*busType - definitions */
-#define SI_ISA_BUS 0x00
-#define SI_MCA_BUS 0x01
-#define SI_EISA_BUS 0x02
-#define SI_PCI_BUS 0x04
-
-#ifdef __cplusplus
- struct sysInfo_S {
-#else
- typedef struct {
-#endif
-
- uCHAR drive0CMOS; /* CMOS Drive 0 Type */
- uCHAR drive1CMOS; /* CMOS Drive 1 Type */
- uCHAR numDrives; /* 0040:0075 contents */
- uCHAR processorFamily; /* Same as DPTSIG's definition */
- uCHAR processorType; /* Same as DPTSIG's definition */
- uCHAR smartROMMajorVersion;
- uCHAR smartROMMinorVersion; /* SmartROM version */
- uCHAR smartROMRevision;
- uSHORT flags; /* See bit definitions above */
- uSHORT conventionalMemSize; /* in KB */
- uINT extendedMemSize; /* in KB */
- uINT osType; /* Same as DPTSIG's definition */
- uCHAR osMajorVersion;
- uCHAR osMinorVersion; /* The OS version */
- uCHAR osRevision;
-#ifdef _SINIX_ADDON
- uCHAR busType; /* See defininitions above */
- uSHORT osSubRevision;
- uCHAR pad[2]; /* For alignment */
-#else
- uCHAR osSubRevision;
- uCHAR busType; /* See defininitions above */
- uCHAR pad[3]; /* For alignment */
-#endif
- driveParam_S drives[16]; /* SmartROM Logical Drives */
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } sysInfo_S;
-#endif
-/*sysInfo_S - end */
-
-
-/*struct - DOS_Info_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the system information specific to a
- *DOS workstation.
- *
- *---------------------------------------------------------------------------*/
-
-/*flags - bit definitions */
-#define DI_DOS_HIGH 0x01 /* DOS is loaded high */
-#define DI_DPMI_VALID 0x02 /* DPMI version is valid */
-
-#ifdef __cplusplus
- struct DOS_Info_S {
-#else
- typedef struct {
-#endif
-
- uCHAR flags; /* See bit definitions above */
- uSHORT driverLocation; /* SmartROM BIOS address */
- uSHORT DOS_version;
- uSHORT DPMI_version;
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } DOS_Info_S;
-#endif
-/*DOS_Info_S - end */
-
-
-/*struct - Netware_Info_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the system information specific to a
- *Netware machine.
- *
- *---------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
- struct Netware_Info_S {
-#else
- typedef struct {
-#endif
-
- uCHAR driverName[13]; /* ie PM12NW31.DSK */
- uCHAR serverName[48];
- uCHAR netwareVersion; /* The Netware OS version */
- uCHAR netwareSubVersion;
- uCHAR netwareRevision;
- uSHORT maxConnections; /* Probably 250 or 1000 */
- uSHORT connectionsInUse;
- uSHORT maxVolumes;
- uCHAR unused;
- uCHAR SFTlevel;
- uCHAR TTSlevel;
-
- uCHAR clibMajorVersion; /* The CLIB.NLM version */
- uCHAR clibMinorVersion;
- uCHAR clibRevision;
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } Netware_Info_S;
-#endif
-/*Netware_Info_S - end */
-
-
-/*struct - OS2_Info_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the system information specific to an
- *OS/2 machine.
- *
- *---------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
- struct OS2_Info_S {
-#else
- typedef struct {
-#endif
-
- uCHAR something;
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } OS2_Info_S;
-#endif
-/*OS2_Info_S - end */
-
-
-/*struct - WinNT_Info_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the system information specific to a
- *Windows NT machine.
- *
- *---------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
- struct WinNT_Info_S {
-#else
- typedef struct {
-#endif
-
- uCHAR something;
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } WinNT_Info_S;
-#endif
-/*WinNT_Info_S - end */
-
-
-/*struct - SCO_Info_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the system information specific to an
- *SCO UNIX machine.
- *
- *---------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
- struct SCO_Info_S {
-#else
- typedef struct {
-#endif
-
- uCHAR something;
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } SCO_Info_S;
-#endif
-/*SCO_Info_S - end */
-
-
-/*struct - USL_Info_S - start
- *===========================================================================
- *
- *Description:
- *
- * This structure defines the system information specific to a
- *USL UNIX machine.
- *
- *---------------------------------------------------------------------------*/
-
-#ifdef __cplusplus
- struct USL_Info_S {
-#else
- typedef struct {
-#endif
-
- uCHAR something;
-
-#ifdef __cplusplus
-
-//---------- Portability Additions ----------- in sp_sinfo.cpp
-#ifdef DPT_PORTABLE
- uSHORT netInsert(dptBuffer_S *buffer);
- uSHORT netExtract(dptBuffer_S *buffer);
-#endif // DPT PORTABLE
-//--------------------------------------------
-
- };
-#else
- } USL_Info_S;
-#endif
-/*USL_Info_S - end */
-
-
- /* Restore default structure packing */
-#ifndef NO_UNPACK
-#if defined (_DPT_AIX)
-#pragma options align=reset
-#elif defined (UNPACK_FOUR)
-#pragma pack(4)
-#else
-#pragma pack()
-#endif /* aix */
-#endif // no unpack
-
-#endif // __SYS_INFO_H
-
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
deleted file mode 100644
index b4f6c9a84e71..000000000000
--- a/drivers/scsi/dpt_i2o.c
+++ /dev/null
@@ -1,3700 +0,0 @@
-/***************************************************************************
- dpti.c - description
- -------------------
- begin : Thu Sep 7 2000
- copyright : (C) 2000 by Adaptec
-
- July 30, 2001 First version being submitted
- for inclusion in the kernel. V2.4
-
- See Documentation/scsi/dpti.txt for history, notes, license info
- and credits
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-/***************************************************************************
- * Sat Dec 20 2003 Go Taniguchi <go@turbolinux.co.jp>
- - Support 2.6 kernel and DMA-mapping
- - ioctl fix for raid tools
- - use schedule_timeout in long long loop
- **************************************************************************/
-
-/*#define DEBUG 1 */
-/*#define UARTDELAY 1 */
-
-#include <linux/module.h>
-
-MODULE_AUTHOR("Deanna Bonds, with _lots_ of help from Mark Salyzyn");
-MODULE_DESCRIPTION("Adaptec I2O RAID Driver");
-
-////////////////////////////////////////////////////////////////
-
-#include <linux/ioctl.h> /* For SCSI-Passthrough */
-#include <asm/uaccess.h>
-
-#include <linux/stat.h>
-#include <linux/slab.h> /* for kmalloc() */
-#include <linux/pci.h> /* for PCI support */
-#include <linux/proc_fs.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h> /* for udelay */
-#include <linux/interrupt.h>
-#include <linux/kernel.h> /* for printk */
-#include <linux/sched.h>
-#include <linux/reboot.h>
-#include <linux/spinlock.h>
-#include <linux/dma-mapping.h>
-
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/mutex.h>
-
-#include <asm/processor.h> /* for boot_cpu_data */
-#include <asm/pgtable.h>
-#include <asm/io.h> /* for virt_to_bus, etc. */
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_tcq.h>
-
-#include "dpt/dptsig.h"
-#include "dpti.h"
-
-/*============================================================================
- * Create a binary signature - this is read by dptsig
- * Needed for our management apps
- *============================================================================
- */
-static DEFINE_MUTEX(adpt_mutex);
-static dpt_sig_S DPTI_sig = {
- {'d', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION,
-#ifdef __i386__
- PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM,
-#elif defined(__ia64__)
- PROC_INTEL, PROC_IA64,
-#elif defined(__sparc__)
- PROC_ULTRASPARC, PROC_ULTRASPARC,
-#elif defined(__alpha__)
- PROC_ALPHA, PROC_ALPHA,
-#else
- (-1),(-1),
-#endif
- FT_HBADRVR, 0, OEM_DPT, OS_LINUX, CAP_OVERLAP, DEV_ALL,
- ADF_ALL_SC5, 0, 0, DPT_VERSION, DPT_REVISION, DPT_SUBREVISION,
- DPT_MONTH, DPT_DAY, DPT_YEAR, "Adaptec Linux I2O RAID Driver"
-};
-
-
-
-
-/*============================================================================
- * Globals
- *============================================================================
- */
-
-static DEFINE_MUTEX(adpt_configuration_lock);
-
-static struct i2o_sys_tbl *sys_tbl;
-static dma_addr_t sys_tbl_pa;
-static int sys_tbl_ind;
-static int sys_tbl_len;
-
-static adpt_hba* hba_chain = NULL;
-static int hba_count = 0;
-
-static struct class *adpt_sysfs_class;
-
-static long adpt_unlocked_ioctl(struct file *, unsigned int, unsigned long);
-#ifdef CONFIG_COMPAT
-static long compat_adpt_ioctl(struct file *, unsigned int, unsigned long);
-#endif
-
-static const struct file_operations adpt_fops = {
- .unlocked_ioctl = adpt_unlocked_ioctl,
- .open = adpt_open,
- .release = adpt_close,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = compat_adpt_ioctl,
-#endif
- .llseek = noop_llseek,
-};
-
-/* Structures and definitions for synchronous message posting.
- * See adpt_i2o_post_wait() for description
- * */
-struct adpt_i2o_post_wait_data
-{
- int status;
- u32 id;
- adpt_wait_queue_head_t *wq;
- struct adpt_i2o_post_wait_data *next;
-};
-
-static struct adpt_i2o_post_wait_data *adpt_post_wait_queue = NULL;
-static u32 adpt_post_wait_id = 0;
-static DEFINE_SPINLOCK(adpt_post_wait_lock);
-
-
-/*============================================================================
- * Functions
- *============================================================================
- */
-
-static inline int dpt_dma64(adpt_hba *pHba)
-{
- return (sizeof(dma_addr_t) > 4 && (pHba)->dma64);
-}
-
-static inline u32 dma_high(dma_addr_t addr)
-{
- return upper_32_bits(addr);
-}
-
-static inline u32 dma_low(dma_addr_t addr)
-{
- return (u32)addr;
-}
-
-static u8 adpt_read_blink_led(adpt_hba* host)
-{
- if (host->FwDebugBLEDflag_P) {
- if( readb(host->FwDebugBLEDflag_P) == 0xbc ){
- return readb(host->FwDebugBLEDvalue_P);
- }
- }
- return 0;
-}
-
-/*============================================================================
- * Scsi host template interface functions
- *============================================================================
- */
-
-static struct pci_device_id dptids[] = {
- { PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
- { PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci,dptids);
-
-static int adpt_detect(struct scsi_host_template* sht)
-{
- struct pci_dev *pDev = NULL;
- adpt_hba *pHba;
- adpt_hba *next;
-
- PINFO("Detecting Adaptec I2O RAID controllers...\n");
-
- /* search for all Adatpec I2O RAID cards */
- while ((pDev = pci_get_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) {
- if(pDev->device == PCI_DPT_DEVICE_ID ||
- pDev->device == PCI_DPT_RAPTOR_DEVICE_ID){
- if(adpt_install_hba(sht, pDev) ){
- PERROR("Could not Init an I2O RAID device\n");
- PERROR("Will not try to detect others.\n");
- return hba_count-1;
- }
- pci_dev_get(pDev);
- }
- }
-
- /* In INIT state, Activate IOPs */
- for (pHba = hba_chain; pHba; pHba = next) {
- next = pHba->next;
- // Activate does get status , init outbound, and get hrt
- if (adpt_i2o_activate_hba(pHba) < 0) {
- adpt_i2o_delete_hba(pHba);
- }
- }
-
-
- /* Active IOPs in HOLD state */
-
-rebuild_sys_tab:
- if (hba_chain == NULL)
- return 0;
-
- /*
- * If build_sys_table fails, we kill everything and bail
- * as we can't init the IOPs w/o a system table
- */
- if (adpt_i2o_build_sys_table() < 0) {
- adpt_i2o_sys_shutdown();
- return 0;
- }
-
- PDEBUG("HBA's in HOLD state\n");
-
- /* If IOP don't get online, we need to rebuild the System table */
- for (pHba = hba_chain; pHba; pHba = pHba->next) {
- if (adpt_i2o_online_hba(pHba) < 0) {
- adpt_i2o_delete_hba(pHba);
- goto rebuild_sys_tab;
- }
- }
-
- /* Active IOPs now in OPERATIONAL state */
- PDEBUG("HBA's in OPERATIONAL state\n");
-
- printk("dpti: If you have a lot of devices this could take a few minutes.\n");
- for (pHba = hba_chain; pHba; pHba = next) {
- next = pHba->next;
- printk(KERN_INFO"%s: Reading the hardware resource table.\n", pHba->name);
- if (adpt_i2o_lct_get(pHba) < 0){
- adpt_i2o_delete_hba(pHba);
- continue;
- }
-
- if (adpt_i2o_parse_lct(pHba) < 0){
- adpt_i2o_delete_hba(pHba);
- continue;
- }
- adpt_inquiry(pHba);
- }
-
- adpt_sysfs_class = class_create(THIS_MODULE, "dpt_i2o");
- if (IS_ERR(adpt_sysfs_class)) {
- printk(KERN_WARNING"dpti: unable to create dpt_i2o class\n");
- adpt_sysfs_class = NULL;
- }
-
- for (pHba = hba_chain; pHba; pHba = next) {
- next = pHba->next;
- if (adpt_scsi_host_alloc(pHba, sht) < 0){
- adpt_i2o_delete_hba(pHba);
- continue;
- }
- pHba->initialized = TRUE;
- pHba->state &= ~DPTI_STATE_RESET;
- if (adpt_sysfs_class) {
- struct device *dev = device_create(adpt_sysfs_class,
- NULL, MKDEV(DPTI_I2O_MAJOR, pHba->unit), NULL,
- "dpti%d", pHba->unit);
- if (IS_ERR(dev)) {
- printk(KERN_WARNING"dpti%d: unable to "
- "create device in dpt_i2o class\n",
- pHba->unit);
- }
- }
- }
-
- // Register our control device node
- // nodes will need to be created in /dev to access this
- // the nodes can not be created from within the driver
- if (hba_count && register_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER, &adpt_fops)) {
- adpt_i2o_sys_shutdown();
- return 0;
- }
- return hba_count;
-}
-
-
-/*
- * scsi_unregister will be called AFTER we return.
- */
-static int adpt_release(struct Scsi_Host *host)
-{
- adpt_hba* pHba = (adpt_hba*) host->hostdata[0];
-// adpt_i2o_quiesce_hba(pHba);
- adpt_i2o_delete_hba(pHba);
- scsi_unregister(host);
- return 0;
-}
-
-
-static void adpt_inquiry(adpt_hba* pHba)
-{
- u32 msg[17];
- u32 *mptr;
- u32 *lenptr;
- int direction;
- int scsidir;
- u32 len;
- u32 reqlen;
- u8* buf;
- dma_addr_t addr;
- u8 scb[16];
- s32 rcode;
-
- memset(msg, 0, sizeof(msg));
- buf = dma_alloc_coherent(&pHba->pDev->dev, 80, &addr, GFP_KERNEL);
- if(!buf){
- printk(KERN_ERR"%s: Could not allocate buffer\n",pHba->name);
- return;
- }
- memset((void*)buf, 0, 36);
-
- len = 36;
- direction = 0x00000000;
- scsidir =0x40000000; // DATA IN (iop<--dev)
-
- if (dpt_dma64(pHba))
- reqlen = 17; // SINGLE SGE, 64 bit
- else
- reqlen = 14; // SINGLE SGE, 32 bit
- /* Stick the headers on */
- msg[0] = reqlen<<16 | SGL_OFFSET_12;
- msg[1] = (0xff<<24|HOST_TID<<12|ADAPTER_TID);
- msg[2] = 0;
- msg[3] = 0;
- // Adaptec/DPT Private stuff
- msg[4] = I2O_CMD_SCSI_EXEC|DPT_ORGANIZATION_ID<<16;
- msg[5] = ADAPTER_TID | 1<<16 /* Interpret*/;
- /* Direction, disconnect ok | sense data | simple queue , CDBLen */
- // I2O_SCB_FLAG_ENABLE_DISCONNECT |
- // I2O_SCB_FLAG_SIMPLE_QUEUE_TAG |
- // I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
- msg[6] = scsidir|0x20a00000| 6 /* cmd len*/;
-
- mptr=msg+7;
-
- memset(scb, 0, sizeof(scb));
- // Write SCSI command into the message - always 16 byte block
- scb[0] = INQUIRY;
- scb[1] = 0;
- scb[2] = 0;
- scb[3] = 0;
- scb[4] = 36;
- scb[5] = 0;
- // Don't care about the rest of scb
-
- memcpy(mptr, scb, sizeof(scb));
- mptr+=4;
- lenptr=mptr++; /* Remember me - fill in when we know */
-
- /* Now fill in the SGList and command */
- *lenptr = len;
- if (dpt_dma64(pHba)) {
- *mptr++ = (0x7C<<24)+(2<<16)+0x02; /* Enable 64 bit */
- *mptr++ = 1 << PAGE_SHIFT;
- *mptr++ = 0xD0000000|direction|len;
- *mptr++ = dma_low(addr);
- *mptr++ = dma_high(addr);
- } else {
- *mptr++ = 0xD0000000|direction|len;
- *mptr++ = addr;
- }
-
- // Send it on it's way
- rcode = adpt_i2o_post_wait(pHba, msg, reqlen<<2, 120);
- if (rcode != 0) {
- sprintf(pHba->detail, "Adaptec I2O RAID");
- printk(KERN_INFO "%s: Inquiry Error (%d)\n",pHba->name,rcode);
- if (rcode != -ETIME && rcode != -EINTR)
- dma_free_coherent(&pHba->pDev->dev, 80, buf, addr);
- } else {
- memset(pHba->detail, 0, sizeof(pHba->detail));
- memcpy(&(pHba->detail), "Vendor: Adaptec ", 16);
- memcpy(&(pHba->detail[16]), " Model: ", 8);
- memcpy(&(pHba->detail[24]), (u8*) &buf[16], 16);
- memcpy(&(pHba->detail[40]), " FW: ", 4);
- memcpy(&(pHba->detail[44]), (u8*) &buf[32], 4);
- pHba->detail[48] = '\0'; /* precautionary */
- dma_free_coherent(&pHba->pDev->dev, 80, buf, addr);
- }
- adpt_i2o_status_get(pHba);
- return ;
-}
-
-
-static int adpt_slave_configure(struct scsi_device * device)
-{
- struct Scsi_Host *host = device->host;
- adpt_hba* pHba;
-
- pHba = (adpt_hba *) host->hostdata[0];
-
- if (host->can_queue && device->tagged_supported) {
- scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
- host->can_queue - 1);
- } else {
- scsi_adjust_queue_depth(device, 0, 1);
- }
- return 0;
-}
-
-static int adpt_queue_lck(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd *))
-{
- adpt_hba* pHba = NULL;
- struct adpt_device* pDev = NULL; /* dpt per device information */
-
- cmd->scsi_done = done;
- /*
- * SCSI REQUEST_SENSE commands will be executed automatically by the
- * Host Adapter for any errors, so they should not be executed
- * explicitly unless the Sense Data is zero indicating that no error
- * occurred.
- */
-
- if ((cmd->cmnd[0] == REQUEST_SENSE) && (cmd->sense_buffer[0] != 0)) {
- cmd->result = (DID_OK << 16);
- cmd->scsi_done(cmd);
- return 0;
- }
-
- pHba = (adpt_hba*)cmd->device->host->hostdata[0];
- if (!pHba) {
- return FAILED;
- }
-
- rmb();
- /*
- * TODO: I need to block here if I am processing ioctl cmds
- * but if the outstanding cmds all finish before the ioctl,
- * the scsi-core will not know to start sending cmds to me again.
- * I need to a way to restart the scsi-cores queues or should I block
- * calling scsi_done on the outstanding cmds instead
- * for now we don't set the IOCTL state
- */
- if(((pHba->state) & DPTI_STATE_IOCTL) || ((pHba->state) & DPTI_STATE_RESET)) {
- pHba->host->last_reset = jiffies;
- pHba->host->resetting = 1;
- return 1;
- }
-
- // TODO if the cmd->device if offline then I may need to issue a bus rescan
- // followed by a get_lct to see if the device is there anymore
- if((pDev = (struct adpt_device*) (cmd->device->hostdata)) == NULL) {
- /*
- * First command request for this device. Set up a pointer
- * to the device structure. This should be a TEST_UNIT_READY
- * command from scan_scsis_single.
- */
- if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun)) == NULL) {
- // TODO: if any luns are at this bus, scsi id then fake a TEST_UNIT_READY and INQUIRY response
- // with type 7F (for all luns less than the max for this bus,id) so the lun scan will continue.
- cmd->result = (DID_NO_CONNECT << 16);
- cmd->scsi_done(cmd);
- return 0;
- }
- cmd->device->hostdata = pDev;
- }
- pDev->pScsi_dev = cmd->device;
-
- /*
- * If we are being called from when the device is being reset,
- * delay processing of the command until later.
- */
- if (pDev->state & DPTI_DEV_RESET ) {
- return FAILED;
- }
- return adpt_scsi_to_i2o(pHba, cmd, pDev);
-}
-
-static DEF_SCSI_QCMD(adpt_queue)
-
-static int adpt_bios_param(struct scsi_device *sdev, struct block_device *dev,
- sector_t capacity, int geom[])
-{
- int heads=-1;
- int sectors=-1;
- int cylinders=-1;
-
- // *** First lets set the default geometry ****
-
- // If the capacity is less than ox2000
- if (capacity < 0x2000 ) { // floppy
- heads = 18;
- sectors = 2;
- }
- // else if between 0x2000 and 0x20000
- else if (capacity < 0x20000) {
- heads = 64;
- sectors = 32;
- }
- // else if between 0x20000 and 0x40000
- else if (capacity < 0x40000) {
- heads = 65;
- sectors = 63;
- }
- // else if between 0x4000 and 0x80000
- else if (capacity < 0x80000) {
- heads = 128;
- sectors = 63;
- }
- // else if greater than 0x80000
- else {
- heads = 255;
- sectors = 63;
- }
- cylinders = sector_div(capacity, heads * sectors);
-
- // Special case if CDROM
- if(sdev->type == 5) { // CDROM
- heads = 252;
- sectors = 63;
- cylinders = 1111;
- }
-
- geom[0] = heads;
- geom[1] = sectors;
- geom[2] = cylinders;
-
- PDEBUG("adpt_bios_param: exit\n");
- return 0;
-}
-
-
-static const char *adpt_info(struct Scsi_Host *host)
-{
- adpt_hba* pHba;
-
- pHba = (adpt_hba *) host->hostdata[0];
- return (char *) (pHba->detail);
-}
-
-static int adpt_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
- int length, int inout)
-{
- struct adpt_device* d;
- int id;
- int chan;
- int len = 0;
- int begin = 0;
- int pos = 0;
- adpt_hba* pHba;
- int unit;
-
- *start = buffer;
- if (inout == TRUE) {
- /*
- * The user has done a write and wants us to take the
- * data in the buffer and do something with it.
- * proc_scsiwrite calls us with inout = 1
- *
- * Read data from buffer (writing to us) - NOT SUPPORTED
- */
- return -EINVAL;
- }
-
- /*
- * inout = 0 means the user has done a read and wants information
- * returned, so we write information about the cards into the buffer
- * proc_scsiread() calls us with inout = 0
- */
-
- // Find HBA (host bus adapter) we are looking for
- mutex_lock(&adpt_configuration_lock);
- for (pHba = hba_chain; pHba; pHba = pHba->next) {
- if (pHba->host == host) {
- break; /* found adapter */
- }
- }
- mutex_unlock(&adpt_configuration_lock);
- if (pHba == NULL) {
- return 0;
- }
- host = pHba->host;
-
- len = sprintf(buffer , "Adaptec I2O RAID Driver Version: %s\n\n", DPT_I2O_VERSION);
- len += sprintf(buffer+len, "%s\n", pHba->detail);
- len += sprintf(buffer+len, "SCSI Host=scsi%d Control Node=/dev/%s irq=%d\n",
- pHba->host->host_no, pHba->name, host->irq);
- len += sprintf(buffer+len, "\tpost fifo size = %d\n\treply fifo size = %d\n\tsg table size = %d\n\n",
- host->can_queue, (int) pHba->reply_fifo_size , host->sg_tablesize);
-
- pos = begin + len;
-
- /* CHECKPOINT */
- if(pos > offset + length) {
- goto stop_output;
- }
- if(pos <= offset) {
- /*
- * If we haven't even written to where we last left
- * off (the last time we were called), reset the
- * beginning pointer.
- */
- len = 0;
- begin = pos;
- }
- len += sprintf(buffer+len, "Devices:\n");
- for(chan = 0; chan < MAX_CHANNEL; chan++) {
- for(id = 0; id < MAX_ID; id++) {
- d = pHba->channel[chan].device[id];
- while(d){
- len += sprintf(buffer+len,"\t%-24.24s", d->pScsi_dev->vendor);
- len += sprintf(buffer+len," Rev: %-8.8s\n", d->pScsi_dev->rev);
- pos = begin + len;
-
-
- /* CHECKPOINT */
- if(pos > offset + length) {
- goto stop_output;
- }
- if(pos <= offset) {
- len = 0;
- begin = pos;
- }
-
- unit = d->pI2o_dev->lct_data.tid;
- len += sprintf(buffer+len, "\tTID=%d, (Channel=%d, Target=%d, Lun=%d) (%s)\n\n",
- unit, (int)d->scsi_channel, (int)d->scsi_id, (int)d->scsi_lun,
- scsi_device_online(d->pScsi_dev)? "online":"offline");
- pos = begin + len;
-
- /* CHECKPOINT */
- if(pos > offset + length) {
- goto stop_output;
- }
- if(pos <= offset) {
- len = 0;
- begin = pos;
- }
-
- d = d->next_lun;
- }
- }
- }
-
- /*
- * begin is where we last checked our position with regards to offset
- * begin is always less than offset. len is relative to begin. It
- * is the number of bytes written past begin
- *
- */
-stop_output:
- /* stop the output and calculate the correct length */
- *(buffer + len) = '\0';
-
- *start = buffer + (offset - begin); /* Start of wanted data */
- len -= (offset - begin);
- if(len > length) {
- len = length;
- } else if(len < 0){
- len = 0;
- **start = '\0';
- }
- return len;
-}
-
-/*
- * Turn a struct scsi_cmnd * into a unique 32 bit 'context'.
- */
-static u32 adpt_cmd_to_context(struct scsi_cmnd *cmd)
-{
- return (u32)cmd->serial_number;
-}
-
-/*
- * Go from a u32 'context' to a struct scsi_cmnd * .
- * This could probably be made more efficient.
- */
-static struct scsi_cmnd *
- adpt_cmd_from_context(adpt_hba * pHba, u32 context)
-{
- struct scsi_cmnd * cmd;
- struct scsi_device * d;
-
- if (context == 0)
- return NULL;
-
- spin_unlock(pHba->host->host_lock);
- shost_for_each_device(d, pHba->host) {
- unsigned long flags;
- spin_lock_irqsave(&d->list_lock, flags);
- list_for_each_entry(cmd, &d->cmd_list, list) {
- if (((u32)cmd->serial_number == context)) {
- spin_unlock_irqrestore(&d->list_lock, flags);
- scsi_device_put(d);
- spin_lock(pHba->host->host_lock);
- return cmd;
- }
- }
- spin_unlock_irqrestore(&d->list_lock, flags);
- }
- spin_lock(pHba->host->host_lock);
-
- return NULL;
-}
-
-/*
- * Turn a pointer to ioctl reply data into an u32 'context'
- */
-static u32 adpt_ioctl_to_context(adpt_hba * pHba, void *reply)
-{
-#if BITS_PER_LONG == 32
- return (u32)(unsigned long)reply;
-#else
- ulong flags = 0;
- u32 nr, i;
-
- spin_lock_irqsave(pHba->host->host_lock, flags);
- nr = ARRAY_SIZE(pHba->ioctl_reply_context);
- for (i = 0; i < nr; i++) {
- if (pHba->ioctl_reply_context[i] == NULL) {
- pHba->ioctl_reply_context[i] = reply;
- break;
- }
- }
- spin_unlock_irqrestore(pHba->host->host_lock, flags);
- if (i >= nr) {
- kfree (reply);
- printk(KERN_WARNING"%s: Too many outstanding "
- "ioctl commands\n", pHba->name);
- return (u32)-1;
- }
-
- return i;
-#endif
-}
-
-/*
- * Go from an u32 'context' to a pointer to ioctl reply data.
- */
-static void *adpt_ioctl_from_context(adpt_hba *pHba, u32 context)
-{
-#if BITS_PER_LONG == 32
- return (void *)(unsigned long)context;
-#else
- void *p = pHba->ioctl_reply_context[context];
- pHba->ioctl_reply_context[context] = NULL;
-
- return p;
-#endif
-}
-
-/*===========================================================================
- * Error Handling routines
- *===========================================================================
- */
-
-static int adpt_abort(struct scsi_cmnd * cmd)
-{
- adpt_hba* pHba = NULL; /* host bus adapter structure */
- struct adpt_device* dptdevice; /* dpt per device information */
- u32 msg[5];
- int rcode;
-
- if(cmd->serial_number == 0){
- return FAILED;
- }
- pHba = (adpt_hba*) cmd->device->host->hostdata[0];
- printk(KERN_INFO"%s: Trying to Abort\n",pHba->name);
- if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) {
- printk(KERN_ERR "%s: Unable to abort: No device in cmnd\n",pHba->name);
- return FAILED;
- }
-
- memset(msg, 0, sizeof(msg));
- msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
- msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|dptdevice->tid;
- msg[2] = 0;
- msg[3]= 0;
- msg[4] = adpt_cmd_to_context(cmd);
- if (pHba->host)
- spin_lock_irq(pHba->host->host_lock);
- rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER);
- if (pHba->host)
- spin_unlock_irq(pHba->host->host_lock);
- if (rcode != 0) {
- if(rcode == -EOPNOTSUPP ){
- printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name);
- return FAILED;
- }
- printk(KERN_INFO"%s: Abort failed.\n",pHba->name);
- return FAILED;
- }
- printk(KERN_INFO"%s: Abort complete.\n",pHba->name);
- return SUCCESS;
-}
-
-
-#define I2O_DEVICE_RESET 0x27
-// This is the same for BLK and SCSI devices
-// NOTE this is wrong in the i2o.h definitions
-// This is not currently supported by our adapter but we issue it anyway
-static int adpt_device_reset(struct scsi_cmnd* cmd)
-{
- adpt_hba* pHba;
- u32 msg[4];
- u32 rcode;
- int old_state;
- struct adpt_device* d = cmd->device->hostdata;
-
- pHba = (void*) cmd->device->host->hostdata[0];
- printk(KERN_INFO"%s: Trying to reset device\n",pHba->name);
- if (!d) {
- printk(KERN_INFO"%s: Reset Device: Device Not found\n",pHba->name);
- return FAILED;
- }
- memset(msg, 0, sizeof(msg));
- msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
- msg[1] = (I2O_DEVICE_RESET<<24|HOST_TID<<12|d->tid);
- msg[2] = 0;
- msg[3] = 0;
-
- if (pHba->host)
- spin_lock_irq(pHba->host->host_lock);
- old_state = d->state;
- d->state |= DPTI_DEV_RESET;
- rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER);
- d->state = old_state;
- if (pHba->host)
- spin_unlock_irq(pHba->host->host_lock);
- if (rcode != 0) {
- if(rcode == -EOPNOTSUPP ){
- printk(KERN_INFO"%s: Device reset not supported\n",pHba->name);
- return FAILED;
- }
- printk(KERN_INFO"%s: Device reset failed\n",pHba->name);
- return FAILED;
- } else {
- printk(KERN_INFO"%s: Device reset successful\n",pHba->name);
- return SUCCESS;
- }
-}
-
-
-#define I2O_HBA_BUS_RESET 0x87
-// This version of bus reset is called by the eh_error handler
-static int adpt_bus_reset(struct scsi_cmnd* cmd)
-{
- adpt_hba* pHba;
- u32 msg[4];
- u32 rcode;
-
- pHba = (adpt_hba*)cmd->device->host->hostdata[0];
- memset(msg, 0, sizeof(msg));
- printk(KERN_WARNING"%s: Bus reset: SCSI Bus %d: tid: %d\n",pHba->name, cmd->device->channel,pHba->channel[cmd->device->channel].tid );
- msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
- msg[1] = (I2O_HBA_BUS_RESET<<24|HOST_TID<<12|pHba->channel[cmd->device->channel].tid);
- msg[2] = 0;
- msg[3] = 0;
- if (pHba->host)
- spin_lock_irq(pHba->host->host_lock);
- rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER);
- if (pHba->host)
- spin_unlock_irq(pHba->host->host_lock);
- if (rcode != 0) {
- printk(KERN_WARNING"%s: Bus reset failed.\n",pHba->name);
- return FAILED;
- } else {
- printk(KERN_WARNING"%s: Bus reset success.\n",pHba->name);
- return SUCCESS;
- }
-}
-
-// This version of reset is called by the eh_error_handler
-static int __adpt_reset(struct scsi_cmnd* cmd)
-{
- adpt_hba* pHba;
- int rcode;
- pHba = (adpt_hba*)cmd->device->host->hostdata[0];
- printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n",pHba->name,cmd->device->channel,pHba->channel[cmd->device->channel].tid );
- rcode = adpt_hba_reset(pHba);
- if(rcode == 0){
- printk(KERN_WARNING"%s: HBA reset complete\n",pHba->name);
- return SUCCESS;
- } else {
- printk(KERN_WARNING"%s: HBA reset failed (%x)\n",pHba->name, rcode);
- return FAILED;
- }
-}
-
-static int adpt_reset(struct scsi_cmnd* cmd)
-{
- int rc;
-
- spin_lock_irq(cmd->device->host->host_lock);
- rc = __adpt_reset(cmd);
- spin_unlock_irq(cmd->device->host->host_lock);
-
- return rc;
-}
-
-// This version of reset is called by the ioctls and indirectly from eh_error_handler via adpt_reset
-static int adpt_hba_reset(adpt_hba* pHba)
-{
- int rcode;
-
- pHba->state |= DPTI_STATE_RESET;
-
- // Activate does get status , init outbound, and get hrt
- if ((rcode=adpt_i2o_activate_hba(pHba)) < 0) {
- printk(KERN_ERR "%s: Could not activate\n", pHba->name);
- adpt_i2o_delete_hba(pHba);
- return rcode;
- }
-
- if ((rcode=adpt_i2o_build_sys_table()) < 0) {
- adpt_i2o_delete_hba(pHba);
- return rcode;
- }
- PDEBUG("%s: in HOLD state\n",pHba->name);
-
- if ((rcode=adpt_i2o_online_hba(pHba)) < 0) {
- adpt_i2o_delete_hba(pHba);
- return rcode;
- }
- PDEBUG("%s: in OPERATIONAL state\n",pHba->name);
-
- if ((rcode=adpt_i2o_lct_get(pHba)) < 0){
- adpt_i2o_delete_hba(pHba);
- return rcode;
- }
-
- if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0){
- adpt_i2o_delete_hba(pHba);
- return rcode;
- }
- pHba->state &= ~DPTI_STATE_RESET;
-
- adpt_fail_posted_scbs(pHba);
- return 0; /* return success */
-}
-
-/*===========================================================================
- *
- *===========================================================================
- */
-
-
-static void adpt_i2o_sys_shutdown(void)
-{
- adpt_hba *pHba, *pNext;
- struct adpt_i2o_post_wait_data *p1, *old;
-
- printk(KERN_INFO"Shutting down Adaptec I2O controllers.\n");
- printk(KERN_INFO" This could take a few minutes if there are many devices attached\n");
- /* Delete all IOPs from the controller chain */
- /* They should have already been released by the
- * scsi-core
- */
- for (pHba = hba_chain; pHba; pHba = pNext) {
- pNext = pHba->next;
- adpt_i2o_delete_hba(pHba);
- }
-
- /* Remove any timedout entries from the wait queue. */
-// spin_lock_irqsave(&adpt_post_wait_lock, flags);
- /* Nothing should be outstanding at this point so just
- * free them
- */
- for(p1 = adpt_post_wait_queue; p1;) {
- old = p1;
- p1 = p1->next;
- kfree(old);
- }
-// spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
- adpt_post_wait_queue = NULL;
-
- printk(KERN_INFO "Adaptec I2O controllers down.\n");
-}
-
-static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev)
-{
-
- adpt_hba* pHba = NULL;
- adpt_hba* p = NULL;
- ulong base_addr0_phys = 0;
- ulong base_addr1_phys = 0;
- u32 hba_map0_area_size = 0;
- u32 hba_map1_area_size = 0;
- void __iomem *base_addr_virt = NULL;
- void __iomem *msg_addr_virt = NULL;
- int dma64 = 0;
-
- int raptorFlag = FALSE;
-
- if(pci_enable_device(pDev)) {
- return -EINVAL;
- }
-
- if (pci_request_regions(pDev, "dpt_i2o")) {
- PERROR("dpti: adpt_config_hba: pci request region failed\n");
- return -EINVAL;
- }
-
- pci_set_master(pDev);
-
- /*
- * See if we should enable dma64 mode.
- */
- if (sizeof(dma_addr_t) > 4 &&
- pci_set_dma_mask(pDev, DMA_BIT_MASK(64)) == 0) {
- if (dma_get_required_mask(&pDev->dev) > DMA_BIT_MASK(32))
- dma64 = 1;
- }
- if (!dma64 && pci_set_dma_mask(pDev, DMA_BIT_MASK(32)) != 0)
- return -EINVAL;
-
- /* adapter only supports message blocks below 4GB */
- pci_set_consistent_dma_mask(pDev, DMA_BIT_MASK(32));
-
- base_addr0_phys = pci_resource_start(pDev,0);
- hba_map0_area_size = pci_resource_len(pDev,0);
-
- // Check if standard PCI card or single BAR Raptor
- if(pDev->device == PCI_DPT_DEVICE_ID){
- if(pDev->subsystem_device >=0xc032 && pDev->subsystem_device <= 0xc03b){
- // Raptor card with this device id needs 4M
- hba_map0_area_size = 0x400000;
- } else { // Not Raptor - it is a PCI card
- if(hba_map0_area_size > 0x100000 ){
- hba_map0_area_size = 0x100000;
- }
- }
- } else {// Raptor split BAR config
- // Use BAR1 in this configuration
- base_addr1_phys = pci_resource_start(pDev,1);
- hba_map1_area_size = pci_resource_len(pDev,1);
- raptorFlag = TRUE;
- }
-
-#if BITS_PER_LONG == 64
- /*
- * The original Adaptec 64 bit driver has this comment here:
- * "x86_64 machines need more optimal mappings"
- *
- * I assume some HBAs report ridiculously large mappings
- * and we need to limit them on platforms with IOMMUs.
- */
- if (raptorFlag == TRUE) {
- if (hba_map0_area_size > 128)
- hba_map0_area_size = 128;
- if (hba_map1_area_size > 524288)
- hba_map1_area_size = 524288;
- } else {
- if (hba_map0_area_size > 524288)
- hba_map0_area_size = 524288;
- }
-#endif
-
- base_addr_virt = ioremap(base_addr0_phys,hba_map0_area_size);
- if (!base_addr_virt) {
- pci_release_regions(pDev);
- PERROR("dpti: adpt_config_hba: io remap failed\n");
- return -EINVAL;
- }
-
- if(raptorFlag == TRUE) {
- msg_addr_virt = ioremap(base_addr1_phys, hba_map1_area_size );
- if (!msg_addr_virt) {
- PERROR("dpti: adpt_config_hba: io remap failed on BAR1\n");
- iounmap(base_addr_virt);
- pci_release_regions(pDev);
- return -EINVAL;
- }
- } else {
- msg_addr_virt = base_addr_virt;
- }
-
- // Allocate and zero the data structure
- pHba = kzalloc(sizeof(adpt_hba), GFP_KERNEL);
- if (!pHba) {
- if (msg_addr_virt != base_addr_virt)
- iounmap(msg_addr_virt);
- iounmap(base_addr_virt);
- pci_release_regions(pDev);
- return -ENOMEM;
- }
-
- mutex_lock(&adpt_configuration_lock);
-
- if(hba_chain != NULL){
- for(p = hba_chain; p->next; p = p->next);
- p->next = pHba;
- } else {
- hba_chain = pHba;
- }
- pHba->next = NULL;
- pHba->unit = hba_count;
- sprintf(pHba->name, "dpti%d", hba_count);
- hba_count++;
-
- mutex_unlock(&adpt_configuration_lock);
-
- pHba->pDev = pDev;
- pHba->base_addr_phys = base_addr0_phys;
-
- // Set up the Virtual Base Address of the I2O Device
- pHba->base_addr_virt = base_addr_virt;
- pHba->msg_addr_virt = msg_addr_virt;
- pHba->irq_mask = base_addr_virt+0x30;
- pHba->post_port = base_addr_virt+0x40;
- pHba->reply_port = base_addr_virt+0x44;
-
- pHba->hrt = NULL;
- pHba->lct = NULL;
- pHba->lct_size = 0;
- pHba->status_block = NULL;
- pHba->post_count = 0;
- pHba->state = DPTI_STATE_RESET;
- pHba->pDev = pDev;
- pHba->devices = NULL;
- pHba->dma64 = dma64;
-
- // Initializing the spinlocks
- spin_lock_init(&pHba->state_lock);
- spin_lock_init(&adpt_post_wait_lock);
-
- if(raptorFlag == 0){
- printk(KERN_INFO "Adaptec I2O RAID controller"
- " %d at %p size=%x irq=%d%s\n",
- hba_count-1, base_addr_virt,
- hba_map0_area_size, pDev->irq,
- dma64 ? " (64-bit DMA)" : "");
- } else {
- printk(KERN_INFO"Adaptec I2O RAID controller %d irq=%d%s\n",
- hba_count-1, pDev->irq,
- dma64 ? " (64-bit DMA)" : "");
- printk(KERN_INFO" BAR0 %p - size= %x\n",base_addr_virt,hba_map0_area_size);
- printk(KERN_INFO" BAR1 %p - size= %x\n",msg_addr_virt,hba_map1_area_size);
- }
-
- if (request_irq (pDev->irq, adpt_isr, IRQF_SHARED, pHba->name, pHba)) {
- printk(KERN_ERR"%s: Couldn't register IRQ %d\n", pHba->name, pDev->irq);
- adpt_i2o_delete_hba(pHba);
- return -EINVAL;
- }
-
- return 0;
-}
-
-
-static void adpt_i2o_delete_hba(adpt_hba* pHba)
-{
- adpt_hba* p1;
- adpt_hba* p2;
- struct i2o_device* d;
- struct i2o_device* next;
- int i;
- int j;
- struct adpt_device* pDev;
- struct adpt_device* pNext;
-
-
- mutex_lock(&adpt_configuration_lock);
- // scsi_unregister calls our adpt_release which
- // does a quiese
- if(pHba->host){
- free_irq(pHba->host->irq, pHba);
- }
- p2 = NULL;
- for( p1 = hba_chain; p1; p2 = p1,p1=p1->next){
- if(p1 == pHba) {
- if(p2) {
- p2->next = p1->next;
- } else {
- hba_chain = p1->next;
- }
- break;
- }
- }
-
- hba_count--;
- mutex_unlock(&adpt_configuration_lock);
-
- iounmap(pHba->base_addr_virt);
- pci_release_regions(pHba->pDev);
- if(pHba->msg_addr_virt != pHba->base_addr_virt){
- iounmap(pHba->msg_addr_virt);
- }
- if(pHba->FwDebugBuffer_P)
- iounmap(pHba->FwDebugBuffer_P);
- if(pHba->hrt) {
- dma_free_coherent(&pHba->pDev->dev,
- pHba->hrt->num_entries * pHba->hrt->entry_len << 2,
- pHba->hrt, pHba->hrt_pa);
- }
- if(pHba->lct) {
- dma_free_coherent(&pHba->pDev->dev, pHba->lct_size,
- pHba->lct, pHba->lct_pa);
- }
- if(pHba->status_block) {
- dma_free_coherent(&pHba->pDev->dev, sizeof(i2o_status_block),
- pHba->status_block, pHba->status_block_pa);
- }
- if(pHba->reply_pool) {
- dma_free_coherent(&pHba->pDev->dev,
- pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4,
- pHba->reply_pool, pHba->reply_pool_pa);
- }
-
- for(d = pHba->devices; d ; d = next){
- next = d->next;
- kfree(d);
- }
- for(i = 0 ; i < pHba->top_scsi_channel ; i++){
- for(j = 0; j < MAX_ID; j++){
- if(pHba->channel[i].device[j] != NULL){
- for(pDev = pHba->channel[i].device[j]; pDev; pDev = pNext){
- pNext = pDev->next_lun;
- kfree(pDev);
- }
- }
- }
- }
- pci_dev_put(pHba->pDev);
- if (adpt_sysfs_class)
- device_destroy(adpt_sysfs_class,
- MKDEV(DPTI_I2O_MAJOR, pHba->unit));
- kfree(pHba);
-
- if(hba_count <= 0){
- unregister_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER);
- if (adpt_sysfs_class) {
- class_destroy(adpt_sysfs_class);
- adpt_sysfs_class = NULL;
- }
- }
-}
-
-static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun)
-{
- struct adpt_device* d;
-
- if(chan < 0 || chan >= MAX_CHANNEL)
- return NULL;
-
- if( pHba->channel[chan].device == NULL){
- printk(KERN_DEBUG"Adaptec I2O RAID: Trying to find device before they are allocated\n");
- return NULL;
- }
-
- d = pHba->channel[chan].device[id];
- if(!d || d->tid == 0) {
- return NULL;
- }
-
- /* If it is the only lun at that address then this should match*/
- if(d->scsi_lun == lun){
- return d;
- }
-
- /* else we need to look through all the luns */
- for(d=d->next_lun ; d ; d = d->next_lun){
- if(d->scsi_lun == lun){
- return d;
- }
- }
- return NULL;
-}
-
-
-static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout)
-{
- // I used my own version of the WAIT_QUEUE_HEAD
- // to handle some version differences
- // When embedded in the kernel this could go back to the vanilla one
- ADPT_DECLARE_WAIT_QUEUE_HEAD(adpt_wq_i2o_post);
- int status = 0;
- ulong flags = 0;
- struct adpt_i2o_post_wait_data *p1, *p2;
- struct adpt_i2o_post_wait_data *wait_data =
- kmalloc(sizeof(struct adpt_i2o_post_wait_data), GFP_ATOMIC);
- DECLARE_WAITQUEUE(wait, current);
-
- if (!wait_data)
- return -ENOMEM;
-
- /*
- * The spin locking is needed to keep anyone from playing
- * with the queue pointers and id while we do the same
- */
- spin_lock_irqsave(&adpt_post_wait_lock, flags);
- // TODO we need a MORE unique way of getting ids
- // to support async LCT get
- wait_data->next = adpt_post_wait_queue;
- adpt_post_wait_queue = wait_data;
- adpt_post_wait_id++;
- adpt_post_wait_id &= 0x7fff;
- wait_data->id = adpt_post_wait_id;
- spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
-
- wait_data->wq = &adpt_wq_i2o_post;
- wait_data->status = -ETIMEDOUT;
-
- add_wait_queue(&adpt_wq_i2o_post, &wait);
-
- msg[2] |= 0x80000000 | ((u32)wait_data->id);
- timeout *= HZ;
- if((status = adpt_i2o_post_this(pHba, msg, len)) == 0){
- set_current_state(TASK_INTERRUPTIBLE);
- if(pHba->host)
- spin_unlock_irq(pHba->host->host_lock);
- if (!timeout)
- schedule();
- else{
- timeout = schedule_timeout(timeout);
- if (timeout == 0) {
- // I/O issued, but cannot get result in
- // specified time. Freeing resorces is
- // dangerous.
- status = -ETIME;
- }
- }
- if(pHba->host)
- spin_lock_irq(pHba->host->host_lock);
- }
- remove_wait_queue(&adpt_wq_i2o_post, &wait);
-
- if(status == -ETIMEDOUT){
- printk(KERN_INFO"dpti%d: POST WAIT TIMEOUT\n",pHba->unit);
- // We will have to free the wait_data memory during shutdown
- return status;
- }
-
- /* Remove the entry from the queue. */
- p2 = NULL;
- spin_lock_irqsave(&adpt_post_wait_lock, flags);
- for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p1->next) {
- if(p1 == wait_data) {
- if(p1->status == I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION ) {
- status = -EOPNOTSUPP;
- }
- if(p2) {
- p2->next = p1->next;
- } else {
- adpt_post_wait_queue = p1->next;
- }
- break;
- }
- }
- spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
-
- kfree(wait_data);
-
- return status;
-}
-
-
-static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len)
-{
-
- u32 m = EMPTY_QUEUE;
- u32 __iomem *msg;
- ulong timeout = jiffies + 30*HZ;
- do {
- rmb();
- m = readl(pHba->post_port);
- if (m != EMPTY_QUEUE) {
- break;
- }
- if(time_after(jiffies,timeout)){
- printk(KERN_WARNING"dpti%d: Timeout waiting for message frame!\n", pHba->unit);
- return -ETIMEDOUT;
- }
- schedule_timeout_uninterruptible(1);
- } while(m == EMPTY_QUEUE);
-
- msg = pHba->msg_addr_virt + m;
- memcpy_toio(msg, data, len);
- wmb();
-
- //post message
- writel(m, pHba->post_port);
- wmb();
-
- return 0;
-}
-
-
-static void adpt_i2o_post_wait_complete(u32 context, int status)
-{
- struct adpt_i2o_post_wait_data *p1 = NULL;
- /*
- * We need to search through the adpt_post_wait
- * queue to see if the given message is still
- * outstanding. If not, it means that the IOP
- * took longer to respond to the message than we
- * had allowed and timer has already expired.
- * Not much we can do about that except log
- * it for debug purposes, increase timeout, and recompile
- *
- * Lock needed to keep anyone from moving queue pointers
- * around while we're looking through them.
- */
-
- context &= 0x7fff;
-
- spin_lock(&adpt_post_wait_lock);
- for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
- if(p1->id == context) {
- p1->status = status;
- spin_unlock(&adpt_post_wait_lock);
- wake_up_interruptible(p1->wq);
- return;
- }
- }
- spin_unlock(&adpt_post_wait_lock);
- // If this happens we lose commands that probably really completed
- printk(KERN_DEBUG"dpti: Could Not find task %d in wait queue\n",context);
- printk(KERN_DEBUG" Tasks in wait queue:\n");
- for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
- printk(KERN_DEBUG" %d\n",p1->id);
- }
- return;
-}
-
-static s32 adpt_i2o_reset_hba(adpt_hba* pHba)
-{
- u32 msg[8];
- u8* status;
- dma_addr_t addr;
- u32 m = EMPTY_QUEUE ;
- ulong timeout = jiffies + (TMOUT_IOPRESET*HZ);
-
- if(pHba->initialized == FALSE) { // First time reset should be quick
- timeout = jiffies + (25*HZ);
- } else {
- adpt_i2o_quiesce_hba(pHba);
- }
-
- do {
- rmb();
- m = readl(pHba->post_port);
- if (m != EMPTY_QUEUE) {
- break;
- }
- if(time_after(jiffies,timeout)){
- printk(KERN_WARNING"Timeout waiting for message!\n");
- return -ETIMEDOUT;
- }
- schedule_timeout_uninterruptible(1);
- } while (m == EMPTY_QUEUE);
-
- status = dma_alloc_coherent(&pHba->pDev->dev, 4, &addr, GFP_KERNEL);
- if(status == NULL) {
- adpt_send_nop(pHba, m);
- printk(KERN_ERR"IOP reset failed - no free memory.\n");
- return -ENOMEM;
- }
- memset(status,0,4);
-
- msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0;
- msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID;
- msg[2]=0;
- msg[3]=0;
- msg[4]=0;
- msg[5]=0;
- msg[6]=dma_low(addr);
- msg[7]=dma_high(addr);
-
- memcpy_toio(pHba->msg_addr_virt+m, msg, sizeof(msg));
- wmb();
- writel(m, pHba->post_port);
- wmb();
-
- while(*status == 0){
- if(time_after(jiffies,timeout)){
- printk(KERN_WARNING"%s: IOP Reset Timeout\n",pHba->name);
- /* We lose 4 bytes of "status" here, but we cannot
- free these because controller may awake and corrupt
- those bytes at any time */
- /* dma_free_coherent(&pHba->pDev->dev, 4, buf, addr); */
- return -ETIMEDOUT;
- }
- rmb();
- schedule_timeout_uninterruptible(1);
- }
-
- if(*status == 0x01 /*I2O_EXEC_IOP_RESET_IN_PROGRESS*/) {
- PDEBUG("%s: Reset in progress...\n", pHba->name);
- // Here we wait for message frame to become available
- // indicated that reset has finished
- do {
- rmb();
- m = readl(pHba->post_port);
- if (m != EMPTY_QUEUE) {
- break;
- }
- if(time_after(jiffies,timeout)){
- printk(KERN_ERR "%s:Timeout waiting for IOP Reset.\n",pHba->name);
- /* We lose 4 bytes of "status" here, but we
- cannot free these because controller may
- awake and corrupt those bytes at any time */
- /* dma_free_coherent(&pHba->pDev->dev, 4, buf, addr); */
- return -ETIMEDOUT;
- }
- schedule_timeout_uninterruptible(1);
- } while (m == EMPTY_QUEUE);
- // Flush the offset
- adpt_send_nop(pHba, m);
- }
- adpt_i2o_status_get(pHba);
- if(*status == 0x02 ||
- pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
- printk(KERN_WARNING"%s: Reset reject, trying to clear\n",
- pHba->name);
- } else {
- PDEBUG("%s: Reset completed.\n", pHba->name);
- }
-
- dma_free_coherent(&pHba->pDev->dev, 4, status, addr);
-#ifdef UARTDELAY
- // This delay is to allow someone attached to the card through the debug UART to
- // set up the dump levels that they want before the rest of the initialization sequence
- adpt_delay(20000);
-#endif
- return 0;
-}
-
-
-static int adpt_i2o_parse_lct(adpt_hba* pHba)
-{
- int i;
- int max;
- int tid;
- struct i2o_device *d;
- i2o_lct *lct = pHba->lct;
- u8 bus_no = 0;
- s16 scsi_id;
- s16 scsi_lun;
- u32 buf[10]; // larger than 7, or 8 ...
- struct adpt_device* pDev;
-
- if (lct == NULL) {
- printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
- return -1;
- }
-
- max = lct->table_size;
- max -= 3;
- max /= 9;
-
- for(i=0;i<max;i++) {
- if( lct->lct_entry[i].user_tid != 0xfff){
- /*
- * If we have hidden devices, we need to inform the upper layers about
- * the possible maximum id reference to handle device access when
- * an array is disassembled. This code has no other purpose but to
- * allow us future access to devices that are currently hidden
- * behind arrays, hotspares or have not been configured (JBOD mode).
- */
- if( lct->lct_entry[i].class_id != I2O_CLASS_RANDOM_BLOCK_STORAGE &&
- lct->lct_entry[i].class_id != I2O_CLASS_SCSI_PERIPHERAL &&
- lct->lct_entry[i].class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
- continue;
- }
- tid = lct->lct_entry[i].tid;
- // I2O_DPT_DEVICE_INFO_GROUP_NO;
- if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
- continue;
- }
- bus_no = buf[0]>>16;
- scsi_id = buf[1];
- scsi_lun = (buf[2]>>8 )&0xff;
- if(bus_no >= MAX_CHANNEL) { // Something wrong skip it
- printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no);
- continue;
- }
- if (scsi_id >= MAX_ID){
- printk(KERN_WARNING"%s: SCSI ID %d out of range \n", pHba->name, bus_no);
- continue;
- }
- if(bus_no > pHba->top_scsi_channel){
- pHba->top_scsi_channel = bus_no;
- }
- if(scsi_id > pHba->top_scsi_id){
- pHba->top_scsi_id = scsi_id;
- }
- if(scsi_lun > pHba->top_scsi_lun){
- pHba->top_scsi_lun = scsi_lun;
- }
- continue;
- }
- d = kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
- if(d==NULL)
- {
- printk(KERN_CRIT"%s: Out of memory for I2O device data.\n",pHba->name);
- return -ENOMEM;
- }
-
- d->controller = pHba;
- d->next = NULL;
-
- memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
-
- d->flags = 0;
- tid = d->lct_data.tid;
- adpt_i2o_report_hba_unit(pHba, d);
- adpt_i2o_install_device(pHba, d);
- }
- bus_no = 0;
- for(d = pHba->devices; d ; d = d->next) {
- if(d->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT ||
- d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PORT){
- tid = d->lct_data.tid;
- // TODO get the bus_no from hrt-but for now they are in order
- //bus_no =
- if(bus_no > pHba->top_scsi_channel){
- pHba->top_scsi_channel = bus_no;
- }
- pHba->channel[bus_no].type = d->lct_data.class_id;
- pHba->channel[bus_no].tid = tid;
- if(adpt_i2o_query_scalar(pHba, tid, 0x0200, -1, buf, 28)>=0)
- {
- pHba->channel[bus_no].scsi_id = buf[1];
- PDEBUG("Bus %d - SCSI ID %d.\n", bus_no, buf[1]);
- }
- // TODO remove - this is just until we get from hrt
- bus_no++;
- if(bus_no >= MAX_CHANNEL) { // Something wrong skip it
- printk(KERN_WARNING"%s: Channel number %d out of range - LCT\n", pHba->name, bus_no);
- break;
- }
- }
- }
-
- // Setup adpt_device table
- for(d = pHba->devices; d ; d = d->next) {
- if(d->lct_data.class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
- d->lct_data.class_id == I2O_CLASS_SCSI_PERIPHERAL ||
- d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
-
- tid = d->lct_data.tid;
- scsi_id = -1;
- // I2O_DPT_DEVICE_INFO_GROUP_NO;
- if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)>=0) {
- bus_no = buf[0]>>16;
- scsi_id = buf[1];
- scsi_lun = (buf[2]>>8 )&0xff;
- if(bus_no >= MAX_CHANNEL) { // Something wrong skip it
- continue;
- }
- if (scsi_id >= MAX_ID) {
- continue;
- }
- if( pHba->channel[bus_no].device[scsi_id] == NULL){
- pDev = kzalloc(sizeof(struct adpt_device),GFP_KERNEL);
- if(pDev == NULL) {
- return -ENOMEM;
- }
- pHba->channel[bus_no].device[scsi_id] = pDev;
- } else {
- for( pDev = pHba->channel[bus_no].device[scsi_id];
- pDev->next_lun; pDev = pDev->next_lun){
- }
- pDev->next_lun = kzalloc(sizeof(struct adpt_device),GFP_KERNEL);
- if(pDev->next_lun == NULL) {
- return -ENOMEM;
- }
- pDev = pDev->next_lun;
- }
- pDev->tid = tid;
- pDev->scsi_channel = bus_no;
- pDev->scsi_id = scsi_id;
- pDev->scsi_lun = scsi_lun;
- pDev->pI2o_dev = d;
- d->owner = pDev;
- pDev->type = (buf[0])&0xff;
- pDev->flags = (buf[0]>>8)&0xff;
- if(scsi_id > pHba->top_scsi_id){
- pHba->top_scsi_id = scsi_id;
- }
- if(scsi_lun > pHba->top_scsi_lun){
- pHba->top_scsi_lun = scsi_lun;
- }
- }
- if(scsi_id == -1){
- printk(KERN_WARNING"Could not find SCSI ID for %s\n",
- d->lct_data.identity_tag);
- }
- }
- }
- return 0;
-}
-
-
-/*
- * Each I2O controller has a chain of devices on it - these match
- * the useful parts of the LCT of the board.
- */
-
-static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d)
-{
- mutex_lock(&adpt_configuration_lock);
- d->controller=pHba;
- d->owner=NULL;
- d->next=pHba->devices;
- d->prev=NULL;
- if (pHba->devices != NULL){
- pHba->devices->prev=d;
- }
- pHba->devices=d;
- *d->dev_name = 0;
-
- mutex_unlock(&adpt_configuration_lock);
- return 0;
-}
-
-static int adpt_open(struct inode *inode, struct file *file)
-{
- int minor;
- adpt_hba* pHba;
-
- mutex_lock(&adpt_mutex);
- //TODO check for root access
- //
- minor = iminor(inode);
- if (minor >= hba_count) {
- mutex_unlock(&adpt_mutex);
- return -ENXIO;
- }
- mutex_lock(&adpt_configuration_lock);
- for (pHba = hba_chain; pHba; pHba = pHba->next) {
- if (pHba->unit == minor) {
- break; /* found adapter */
- }
- }
- if (pHba == NULL) {
- mutex_unlock(&adpt_configuration_lock);
- mutex_unlock(&adpt_mutex);
- return -ENXIO;
- }
-
-// if(pHba->in_use){
- // mutex_unlock(&adpt_configuration_lock);
-// return -EBUSY;
-// }
-
- pHba->in_use = 1;
- mutex_unlock(&adpt_configuration_lock);
- mutex_unlock(&adpt_mutex);
-
- return 0;
-}
-
-static int adpt_close(struct inode *inode, struct file *file)
-{
- int minor;
- adpt_hba* pHba;
-
- minor = iminor(inode);
- if (minor >= hba_count) {
- return -ENXIO;
- }
- mutex_lock(&adpt_configuration_lock);
- for (pHba = hba_chain; pHba; pHba = pHba->next) {
- if (pHba->unit == minor) {
- break; /* found adapter */
- }
- }
- mutex_unlock(&adpt_configuration_lock);
- if (pHba == NULL) {
- return -ENXIO;
- }
-
- pHba->in_use = 0;
-
- return 0;
-}
-
-
-static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg)
-{
- u32 msg[MAX_MESSAGE_SIZE];
- u32* reply = NULL;
- u32 size = 0;
- u32 reply_size = 0;
- u32 __user *user_msg = arg;
- u32 __user * user_reply = NULL;
- void *sg_list[pHba->sg_tablesize];
- u32 sg_offset = 0;
- u32 sg_count = 0;
- int sg_index = 0;
- u32 i = 0;
- u32 rcode = 0;
- void *p = NULL;
- dma_addr_t addr;
- ulong flags = 0;
-
- memset(&msg, 0, MAX_MESSAGE_SIZE*4);
- // get user msg size in u32s
- if(get_user(size, &user_msg[0])){
- return -EFAULT;
- }
- size = size>>16;
-
- user_reply = &user_msg[size];
- if(size > MAX_MESSAGE_SIZE){
- return -EFAULT;
- }
- size *= 4; // Convert to bytes
-
- /* Copy in the user's I2O command */
- if(copy_from_user(msg, user_msg, size)) {
- return -EFAULT;
- }
- get_user(reply_size, &user_reply[0]);
- reply_size = reply_size>>16;
- if(reply_size > REPLY_FRAME_SIZE){
- reply_size = REPLY_FRAME_SIZE;
- }
- reply_size *= 4;
- reply = kzalloc(REPLY_FRAME_SIZE*4, GFP_KERNEL);
- if(reply == NULL) {
- printk(KERN_WARNING"%s: Could not allocate reply buffer\n",pHba->name);
- return -ENOMEM;
- }
- sg_offset = (msg[0]>>4)&0xf;
- msg[2] = 0x40000000; // IOCTL context
- msg[3] = adpt_ioctl_to_context(pHba, reply);
- if (msg[3] == (u32)-1)
- return -EBUSY;
-
- memset(sg_list,0, sizeof(sg_list[0])*pHba->sg_tablesize);
- if(sg_offset) {
- // TODO add 64 bit API
- struct sg_simple_element *sg = (struct sg_simple_element*) (msg+sg_offset);
- sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
- if (sg_count > pHba->sg_tablesize){
- printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count);
- kfree (reply);
- return -EINVAL;
- }
-
- for(i = 0; i < sg_count; i++) {
- int sg_size;
-
- if (!(sg[i].flag_count & 0x10000000 /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT*/)) {
- printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",pHba->name,i, sg[i].flag_count);
- rcode = -EINVAL;
- goto cleanup;
- }
- sg_size = sg[i].flag_count & 0xffffff;
- /* Allocate memory for the transfer */
- p = dma_alloc_coherent(&pHba->pDev->dev, sg_size, &addr, GFP_KERNEL);
- if(!p) {
- printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
- pHba->name,sg_size,i,sg_count);
- rcode = -ENOMEM;
- goto cleanup;
- }
- sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
- /* Copy in the user's SG buffer if necessary */
- if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) {
- // sg_simple_element API is 32 bit
- if (copy_from_user(p,(void __user *)(ulong)sg[i].addr_bus, sg_size)) {
- printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i);
- rcode = -EFAULT;
- goto cleanup;
- }
- }
- /* sg_simple_element API is 32 bit, but addr < 4GB */
- sg[i].addr_bus = addr;
- }
- }
-
- do {
- if(pHba->host)
- spin_lock_irqsave(pHba->host->host_lock, flags);
- // This state stops any new commands from enterring the
- // controller while processing the ioctl
-// pHba->state |= DPTI_STATE_IOCTL;
-// We can't set this now - The scsi subsystem sets host_blocked and
-// the queue empties and stops. We need a way to restart the queue
- rcode = adpt_i2o_post_wait(pHba, msg, size, FOREVER);
- if (rcode != 0)
- printk("adpt_i2o_passthru: post wait failed %d %p\n",
- rcode, reply);
-// pHba->state &= ~DPTI_STATE_IOCTL;
- if(pHba->host)
- spin_unlock_irqrestore(pHba->host->host_lock, flags);
- } while(rcode == -ETIMEDOUT);
-
- if(rcode){
- goto cleanup;
- }
-
- if(sg_offset) {
- /* Copy back the Scatter Gather buffers back to user space */
- u32 j;
- // TODO add 64 bit API
- struct sg_simple_element* sg;
- int sg_size;
-
- // re-acquire the original message to handle correctly the sg copy operation
- memset(&msg, 0, MAX_MESSAGE_SIZE*4);
- // get user msg size in u32s
- if(get_user(size, &user_msg[0])){
- rcode = -EFAULT;
- goto cleanup;
- }
- size = size>>16;
- size *= 4;
- if (size > MAX_MESSAGE_SIZE) {
- rcode = -EINVAL;
- goto cleanup;
- }
- /* Copy in the user's I2O command */
- if (copy_from_user (msg, user_msg, size)) {
- rcode = -EFAULT;
- goto cleanup;
- }
- sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
-
- // TODO add 64 bit API
- sg = (struct sg_simple_element*)(msg + sg_offset);
- for (j = 0; j < sg_count; j++) {
- /* Copy out the SG list to user's buffer if necessary */
- if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) {
- sg_size = sg[j].flag_count & 0xffffff;
- // sg_simple_element API is 32 bit
- if (copy_to_user((void __user *)(ulong)sg[j].addr_bus,sg_list[j], sg_size)) {
- printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus);
- rcode = -EFAULT;
- goto cleanup;
- }
- }
- }
- }
-
- /* Copy back the reply to user space */
- if (reply_size) {
- // we wrote our own values for context - now restore the user supplied ones
- if(copy_from_user(reply+2, user_msg+2, sizeof(u32)*2)) {
- printk(KERN_WARNING"%s: Could not copy message context FROM user\n",pHba->name);
- rcode = -EFAULT;
- }
- if(copy_to_user(user_reply, reply, reply_size)) {
- printk(KERN_WARNING"%s: Could not copy reply TO user\n",pHba->name);
- rcode = -EFAULT;
- }
- }
-
-
-cleanup:
- if (rcode != -ETIME && rcode != -EINTR) {
- struct sg_simple_element *sg =
- (struct sg_simple_element*) (msg +sg_offset);
- kfree (reply);
- while(sg_index) {
- if(sg_list[--sg_index]) {
- dma_free_coherent(&pHba->pDev->dev,
- sg[sg_index].flag_count & 0xffffff,
- sg_list[sg_index],
- sg[sg_index].addr_bus);
- }
- }
- }
- return rcode;
-}
-
-#if defined __ia64__
-static void adpt_ia64_info(sysInfo_S* si)
-{
- // This is all the info we need for now
- // We will add more info as our new
- // managmenent utility requires it
- si->processorType = PROC_IA64;
-}
-#endif
-
-#if defined __sparc__
-static void adpt_sparc_info(sysInfo_S* si)
-{
- // This is all the info we need for now
- // We will add more info as our new
- // managmenent utility requires it
- si->processorType = PROC_ULTRASPARC;
-}
-#endif
-#if defined __alpha__
-static void adpt_alpha_info(sysInfo_S* si)
-{
- // This is all the info we need for now
- // We will add more info as our new
- // managmenent utility requires it
- si->processorType = PROC_ALPHA;
-}
-#endif
-
-#if defined __i386__
-static void adpt_i386_info(sysInfo_S* si)
-{
- // This is all the info we need for now
- // We will add more info as our new
- // managmenent utility requires it
- switch (boot_cpu_data.x86) {
- case CPU_386:
- si->processorType = PROC_386;
- break;
- case CPU_486:
- si->processorType = PROC_486;
- break;
- case CPU_586:
- si->processorType = PROC_PENTIUM;
- break;
- default: // Just in case
- si->processorType = PROC_PENTIUM;
- break;
- }
-}
-#endif
-
-/*
- * This routine returns information about the system. This does not effect
- * any logic and if the info is wrong - it doesn't matter.
- */
-
-/* Get all the info we can not get from kernel services */
-static int adpt_system_info(void __user *buffer)
-{
- sysInfo_S si;
-
- memset(&si, 0, sizeof(si));
-
- si.osType = OS_LINUX;
- si.osMajorVersion = 0;
- si.osMinorVersion = 0;
- si.osRevision = 0;
- si.busType = SI_PCI_BUS;
- si.processorFamily = DPTI_sig.dsProcessorFamily;
-
-#if defined __i386__
- adpt_i386_info(&si);
-#elif defined (__ia64__)
- adpt_ia64_info(&si);
-#elif defined(__sparc__)
- adpt_sparc_info(&si);
-#elif defined (__alpha__)
- adpt_alpha_info(&si);
-#else
- si.processorType = 0xff ;
-#endif
- if (copy_to_user(buffer, &si, sizeof(si))){
- printk(KERN_WARNING"dpti: Could not copy buffer TO user\n");
- return -EFAULT;
- }
-
- return 0;
-}
-
-static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
-{
- int minor;
- int error = 0;
- adpt_hba* pHba;
- ulong flags = 0;
- void __user *argp = (void __user *)arg;
-
- minor = iminor(inode);
- if (minor >= DPTI_MAX_HBA){
- return -ENXIO;
- }
- mutex_lock(&adpt_configuration_lock);
- for (pHba = hba_chain; pHba; pHba = pHba->next) {
- if (pHba->unit == minor) {
- break; /* found adapter */
- }
- }
- mutex_unlock(&adpt_configuration_lock);
- if(pHba == NULL){
- return -ENXIO;
- }
-
- while((volatile u32) pHba->state & DPTI_STATE_RESET )
- schedule_timeout_uninterruptible(2);
-
- switch (cmd) {
- // TODO: handle 3 cases
- case DPT_SIGNATURE:
- if (copy_to_user(argp, &DPTI_sig, sizeof(DPTI_sig))) {
- return -EFAULT;
- }
- break;
- case I2OUSRCMD:
- return adpt_i2o_passthru(pHba, argp);
-
- case DPT_CTRLINFO:{
- drvrHBAinfo_S HbaInfo;
-
-#define FLG_OSD_PCI_VALID 0x0001
-#define FLG_OSD_DMA 0x0002
-#define FLG_OSD_I2O 0x0004
- memset(&HbaInfo, 0, sizeof(HbaInfo));
- HbaInfo.drvrHBAnum = pHba->unit;
- HbaInfo.baseAddr = (ulong) pHba->base_addr_phys;
- HbaInfo.blinkState = adpt_read_blink_led(pHba);
- HbaInfo.pciBusNum = pHba->pDev->bus->number;
- HbaInfo.pciDeviceNum=PCI_SLOT(pHba->pDev->devfn);
- HbaInfo.Interrupt = pHba->pDev->irq;
- HbaInfo.hbaFlags = FLG_OSD_PCI_VALID | FLG_OSD_DMA | FLG_OSD_I2O;
- if(copy_to_user(argp, &HbaInfo, sizeof(HbaInfo))){
- printk(KERN_WARNING"%s: Could not copy HbaInfo TO user\n",pHba->name);
- return -EFAULT;
- }
- break;
- }
- case DPT_SYSINFO:
- return adpt_system_info(argp);
- case DPT_BLINKLED:{
- u32 value;
- value = (u32)adpt_read_blink_led(pHba);
- if (copy_to_user(argp, &value, sizeof(value))) {
- return -EFAULT;
- }
- break;
- }
- case I2ORESETCMD:
- if(pHba->host)
- spin_lock_irqsave(pHba->host->host_lock, flags);
- adpt_hba_reset(pHba);
- if(pHba->host)
- spin_unlock_irqrestore(pHba->host->host_lock, flags);
- break;
- case I2ORESCANCMD:
- adpt_rescan(pHba);
- break;
- default:
- return -EINVAL;
- }
-
- return error;
-}
-
-static long adpt_unlocked_ioctl(struct file *file, uint cmd, ulong arg)
-{
- struct inode *inode;
- long ret;
-
- inode = file->f_dentry->d_inode;
-
- mutex_lock(&adpt_mutex);
- ret = adpt_ioctl(inode, file, cmd, arg);
- mutex_unlock(&adpt_mutex);
-
- return ret;
-}
-
-#ifdef CONFIG_COMPAT
-static long compat_adpt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct inode *inode;
- long ret;
-
- inode = file->f_dentry->d_inode;
-
- mutex_lock(&adpt_mutex);
-
- switch(cmd) {
- case DPT_SIGNATURE:
- case I2OUSRCMD:
- case DPT_CTRLINFO:
- case DPT_SYSINFO:
- case DPT_BLINKLED:
- case I2ORESETCMD:
- case I2ORESCANCMD:
- case (DPT_TARGET_BUSY & 0xFFFF):
- case DPT_TARGET_BUSY:
- ret = adpt_ioctl(inode, file, cmd, arg);
- break;
- default:
- ret = -ENOIOCTLCMD;
- }
-
- mutex_unlock(&adpt_mutex);
-
- return ret;
-}
-#endif
-
-static irqreturn_t adpt_isr(int irq, void *dev_id)
-{
- struct scsi_cmnd* cmd;
- adpt_hba* pHba = dev_id;
- u32 m;
- void __iomem *reply;
- u32 status=0;
- u32 context;
- ulong flags = 0;
- int handled = 0;
-
- if (pHba == NULL){
- printk(KERN_WARNING"adpt_isr: NULL dev_id\n");
- return IRQ_NONE;
- }
- if(pHba->host)
- spin_lock_irqsave(pHba->host->host_lock, flags);
-
- while( readl(pHba->irq_mask) & I2O_INTERRUPT_PENDING_B) {
- m = readl(pHba->reply_port);
- if(m == EMPTY_QUEUE){
- // Try twice then give up
- rmb();
- m = readl(pHba->reply_port);
- if(m == EMPTY_QUEUE){
- // This really should not happen
- printk(KERN_ERR"dpti: Could not get reply frame\n");
- goto out;
- }
- }
- if (pHba->reply_pool_pa <= m &&
- m < pHba->reply_pool_pa +
- (pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4)) {
- reply = (u8 *)pHba->reply_pool +
- (m - pHba->reply_pool_pa);
- } else {
- /* Ick, we should *never* be here */
- printk(KERN_ERR "dpti: reply frame not from pool\n");
- reply = (u8 *)bus_to_virt(m);
- }
-
- if (readl(reply) & MSG_FAIL) {
- u32 old_m = readl(reply+28);
- void __iomem *msg;
- u32 old_context;
- PDEBUG("%s: Failed message\n",pHba->name);
- if(old_m >= 0x100000){
- printk(KERN_ERR"%s: Bad preserved MFA (%x)- dropping frame\n",pHba->name,old_m);
- writel(m,pHba->reply_port);
- continue;
- }
- // Transaction context is 0 in failed reply frame
- msg = pHba->msg_addr_virt + old_m;
- old_context = readl(msg+12);
- writel(old_context, reply+12);
- adpt_send_nop(pHba, old_m);
- }
- context = readl(reply+8);
- if(context & 0x40000000){ // IOCTL
- void *p = adpt_ioctl_from_context(pHba, readl(reply+12));
- if( p != NULL) {
- memcpy_fromio(p, reply, REPLY_FRAME_SIZE * 4);
- }
- // All IOCTLs will also be post wait
- }
- if(context & 0x80000000){ // Post wait message
- status = readl(reply+16);
- if(status >> 24){
- status &= 0xffff; /* Get detail status */
- } else {
- status = I2O_POST_WAIT_OK;
- }
- if(!(context & 0x40000000)) {
- cmd = adpt_cmd_from_context(pHba,
- readl(reply+12));
- if(cmd != NULL) {
- printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context);
- }
- }
- adpt_i2o_post_wait_complete(context, status);
- } else { // SCSI message
- cmd = adpt_cmd_from_context (pHba, readl(reply+12));
- if(cmd != NULL){
- scsi_dma_unmap(cmd);
- if(cmd->serial_number != 0) { // If not timedout
- adpt_i2o_to_scsi(reply, cmd);
- }
- }
- }
- writel(m, pHba->reply_port);
- wmb();
- rmb();
- }
- handled = 1;
-out: if(pHba->host)
- spin_unlock_irqrestore(pHba->host->host_lock, flags);
- return IRQ_RETVAL(handled);
-}
-
-static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* d)
-{
- int i;
- u32 msg[MAX_MESSAGE_SIZE];
- u32* mptr;
- u32* lptr;
- u32 *lenptr;
- int direction;
- int scsidir;
- int nseg;
- u32 len;
- u32 reqlen;
- s32 rcode;
- dma_addr_t addr;
-
- memset(msg, 0 , sizeof(msg));
- len = scsi_bufflen(cmd);
- direction = 0x00000000;
-
- scsidir = 0x00000000; // DATA NO XFER
- if(len) {
- /*
- * Set SCBFlags to indicate if data is being transferred
- * in or out, or no data transfer
- * Note: Do not have to verify index is less than 0 since
- * cmd->cmnd[0] is an unsigned char
- */
- switch(cmd->sc_data_direction){
- case DMA_FROM_DEVICE:
- scsidir =0x40000000; // DATA IN (iop<--dev)
- break;
- case DMA_TO_DEVICE:
- direction=0x04000000; // SGL OUT
- scsidir =0x80000000; // DATA OUT (iop-->dev)
- break;
- case DMA_NONE:
- break;
- case DMA_BIDIRECTIONAL:
- scsidir =0x40000000; // DATA IN (iop<--dev)
- // Assume In - and continue;
- break;
- default:
- printk(KERN_WARNING"%s: scsi opcode 0x%x not supported.\n",
- pHba->name, cmd->cmnd[0]);
- cmd->result = (DID_OK <<16) | (INITIATOR_ERROR << 8);
- cmd->scsi_done(cmd);
- return 0;
- }
- }
- // msg[0] is set later
- // I2O_CMD_SCSI_EXEC
- msg[1] = ((0xff<<24)|(HOST_TID<<12)|d->tid);
- msg[2] = 0;
- msg[3] = adpt_cmd_to_context(cmd); /* Want SCSI control block back */
- // Our cards use the transaction context as the tag for queueing
- // Adaptec/DPT Private stuff
- msg[4] = I2O_CMD_SCSI_EXEC|(DPT_ORGANIZATION_ID<<16);
- msg[5] = d->tid;
- /* Direction, disconnect ok | sense data | simple queue , CDBLen */
- // I2O_SCB_FLAG_ENABLE_DISCONNECT |
- // I2O_SCB_FLAG_SIMPLE_QUEUE_TAG |
- // I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
- msg[6] = scsidir|0x20a00000|cmd->cmd_len;
-
- mptr=msg+7;
-
- // Write SCSI command into the message - always 16 byte block
- memset(mptr, 0, 16);
- memcpy(mptr, cmd->cmnd, cmd->cmd_len);
- mptr+=4;
- lenptr=mptr++; /* Remember me - fill in when we know */
- if (dpt_dma64(pHba)) {
- reqlen = 16; // SINGLE SGE
- *mptr++ = (0x7C<<24)+(2<<16)+0x02; /* Enable 64 bit */
- *mptr++ = 1 << PAGE_SHIFT;
- } else {
- reqlen = 14; // SINGLE SGE
- }
- /* Now fill in the SGList and command */
-
- nseg = scsi_dma_map(cmd);
- BUG_ON(nseg < 0);
- if (nseg) {
- struct scatterlist *sg;
-
- len = 0;
- scsi_for_each_sg(cmd, sg, nseg, i) {
- lptr = mptr;
- *mptr++ = direction|0x10000000|sg_dma_len(sg);
- len+=sg_dma_len(sg);
- addr = sg_dma_address(sg);
- *mptr++ = dma_low(addr);
- if (dpt_dma64(pHba))
- *mptr++ = dma_high(addr);
- /* Make this an end of list */
- if (i == nseg - 1)
- *lptr = direction|0xD0000000|sg_dma_len(sg);
- }
- reqlen = mptr - msg;
- *lenptr = len;
-
- if(cmd->underflow && len != cmd->underflow){
- printk(KERN_WARNING"Cmd len %08X Cmd underflow %08X\n",
- len, cmd->underflow);
- }
- } else {
- *lenptr = len = 0;
- reqlen = 12;
- }
-
- /* Stick the headers on */
- msg[0] = reqlen<<16 | ((reqlen > 12) ? SGL_OFFSET_12 : SGL_OFFSET_0);
-
- // Send it on it's way
- rcode = adpt_i2o_post_this(pHba, msg, reqlen<<2);
- if (rcode == 0) {
- return 0;
- }
- return rcode;
-}
-
-
-static s32 adpt_scsi_host_alloc(adpt_hba* pHba, struct scsi_host_template *sht)
-{
- struct Scsi_Host *host;
-
- host = scsi_host_alloc(sht, sizeof(adpt_hba*));
- if (host == NULL) {
- printk("%s: scsi_host_alloc returned NULL\n", pHba->name);
- return -1;
- }
- host->hostdata[0] = (unsigned long)pHba;
- pHba->host = host;
-
- host->irq = pHba->pDev->irq;
- /* no IO ports, so don't have to set host->io_port and
- * host->n_io_port
- */
- host->io_port = 0;
- host->n_io_port = 0;
- /* see comments in scsi_host.h */
- host->max_id = 16;
- host->max_lun = 256;
- host->max_channel = pHba->top_scsi_channel + 1;
- host->cmd_per_lun = 1;
- host->unique_id = (u32)sys_tbl_pa + pHba->unit;
- host->sg_tablesize = pHba->sg_tablesize;
- host->can_queue = pHba->post_fifo_size;
-
- return 0;
-}
-
-
-static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd)
-{
- adpt_hba* pHba;
- u32 hba_status;
- u32 dev_status;
- u32 reply_flags = readl(reply) & 0xff00; // Leave it shifted up 8 bits
- // I know this would look cleaner if I just read bytes
- // but the model I have been using for all the rest of the
- // io is in 4 byte words - so I keep that model
- u16 detailed_status = readl(reply+16) &0xffff;
- dev_status = (detailed_status & 0xff);
- hba_status = detailed_status >> 8;
-
- // calculate resid for sg
- scsi_set_resid(cmd, scsi_bufflen(cmd) - readl(reply+20));
-
- pHba = (adpt_hba*) cmd->device->host->hostdata[0];
-
- cmd->sense_buffer[0] = '\0'; // initialize sense valid flag to false
-
- if(!(reply_flags & MSG_FAIL)) {
- switch(detailed_status & I2O_SCSI_DSC_MASK) {
- case I2O_SCSI_DSC_SUCCESS:
- cmd->result = (DID_OK << 16);
- // handle underflow
- if (readl(reply+20) < cmd->underflow) {
- cmd->result = (DID_ERROR <<16);
- printk(KERN_WARNING"%s: SCSI CMD underflow\n",pHba->name);
- }
- break;
- case I2O_SCSI_DSC_REQUEST_ABORTED:
- cmd->result = (DID_ABORT << 16);
- break;
- case I2O_SCSI_DSC_PATH_INVALID:
- case I2O_SCSI_DSC_DEVICE_NOT_PRESENT:
- case I2O_SCSI_DSC_SELECTION_TIMEOUT:
- case I2O_SCSI_DSC_COMMAND_TIMEOUT:
- case I2O_SCSI_DSC_NO_ADAPTER:
- case I2O_SCSI_DSC_RESOURCE_UNAVAILABLE:
- printk(KERN_WARNING"%s: SCSI Timeout-Device (%d,%d,%d) hba status=0x%x, dev status=0x%x, cmd=0x%x\n",
- pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]);
- cmd->result = (DID_TIME_OUT << 16);
- break;
- case I2O_SCSI_DSC_ADAPTER_BUSY:
- case I2O_SCSI_DSC_BUS_BUSY:
- cmd->result = (DID_BUS_BUSY << 16);
- break;
- case I2O_SCSI_DSC_SCSI_BUS_RESET:
- case I2O_SCSI_DSC_BDR_MESSAGE_SENT:
- cmd->result = (DID_RESET << 16);
- break;
- case I2O_SCSI_DSC_PARITY_ERROR_FAILURE:
- printk(KERN_WARNING"%s: SCSI CMD parity error\n",pHba->name);
- cmd->result = (DID_PARITY << 16);
- break;
- case I2O_SCSI_DSC_UNABLE_TO_ABORT:
- case I2O_SCSI_DSC_COMPLETE_WITH_ERROR:
- case I2O_SCSI_DSC_UNABLE_TO_TERMINATE:
- case I2O_SCSI_DSC_MR_MESSAGE_RECEIVED:
- case I2O_SCSI_DSC_AUTOSENSE_FAILED:
- case I2O_SCSI_DSC_DATA_OVERRUN:
- case I2O_SCSI_DSC_UNEXPECTED_BUS_FREE:
- case I2O_SCSI_DSC_SEQUENCE_FAILURE:
- case I2O_SCSI_DSC_REQUEST_LENGTH_ERROR:
- case I2O_SCSI_DSC_PROVIDE_FAILURE:
- case I2O_SCSI_DSC_REQUEST_TERMINATED:
- case I2O_SCSI_DSC_IDE_MESSAGE_SENT:
- case I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT:
- case I2O_SCSI_DSC_MESSAGE_RECEIVED:
- case I2O_SCSI_DSC_INVALID_CDB:
- case I2O_SCSI_DSC_LUN_INVALID:
- case I2O_SCSI_DSC_SCSI_TID_INVALID:
- case I2O_SCSI_DSC_FUNCTION_UNAVAILABLE:
- case I2O_SCSI_DSC_NO_NEXUS:
- case I2O_SCSI_DSC_CDB_RECEIVED:
- case I2O_SCSI_DSC_LUN_ALREADY_ENABLED:
- case I2O_SCSI_DSC_QUEUE_FROZEN:
- case I2O_SCSI_DSC_REQUEST_INVALID:
- default:
- printk(KERN_WARNING"%s: SCSI error %0x-Device(%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
- pHba->name, detailed_status & I2O_SCSI_DSC_MASK, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
- hba_status, dev_status, cmd->cmnd[0]);
- cmd->result = (DID_ERROR << 16);
- break;
- }
-
- // copy over the request sense data if it was a check
- // condition status
- if (dev_status == SAM_STAT_CHECK_CONDITION) {
- u32 len = min(SCSI_SENSE_BUFFERSIZE, 40);
- // Copy over the sense data
- memcpy_fromio(cmd->sense_buffer, (reply+28) , len);
- if(cmd->sense_buffer[0] == 0x70 /* class 7 */ &&
- cmd->sense_buffer[2] == DATA_PROTECT ){
- /* This is to handle an array failed */
- cmd->result = (DID_TIME_OUT << 16);
- printk(KERN_WARNING"%s: SCSI Data Protect-Device (%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
- pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
- hba_status, dev_status, cmd->cmnd[0]);
-
- }
- }
- } else {
- /* In this condtion we could not talk to the tid
- * the card rejected it. We should signal a retry
- * for a limitted number of retries.
- */
- cmd->result = (DID_TIME_OUT << 16);
- printk(KERN_WARNING"%s: I2O MSG_FAIL - Device (%d,%d,%d) tid=%d, cmd=0x%x\n",
- pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
- ((struct adpt_device*)(cmd->device->hostdata))->tid, cmd->cmnd[0]);
- }
-
- cmd->result |= (dev_status);
-
- if(cmd->scsi_done != NULL){
- cmd->scsi_done(cmd);
- }
- return cmd->result;
-}
-
-
-static s32 adpt_rescan(adpt_hba* pHba)
-{
- s32 rcode;
- ulong flags = 0;
-
- if(pHba->host)
- spin_lock_irqsave(pHba->host->host_lock, flags);
- if ((rcode=adpt_i2o_lct_get(pHba)) < 0)
- goto out;
- if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0)
- goto out;
- rcode = 0;
-out: if(pHba->host)
- spin_unlock_irqrestore(pHba->host->host_lock, flags);
- return rcode;
-}
-
-
-static s32 adpt_i2o_reparse_lct(adpt_hba* pHba)
-{
- int i;
- int max;
- int tid;
- struct i2o_device *d;
- i2o_lct *lct = pHba->lct;
- u8 bus_no = 0;
- s16 scsi_id;
- s16 scsi_lun;
- u32 buf[10]; // at least 8 u32's
- struct adpt_device* pDev = NULL;
- struct i2o_device* pI2o_dev = NULL;
-
- if (lct == NULL) {
- printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
- return -1;
- }
-
- max = lct->table_size;
- max -= 3;
- max /= 9;
-
- // Mark each drive as unscanned
- for (d = pHba->devices; d; d = d->next) {
- pDev =(struct adpt_device*) d->owner;
- if(!pDev){
- continue;
- }
- pDev->state |= DPTI_DEV_UNSCANNED;
- }
-
- printk(KERN_INFO "%s: LCT has %d entries.\n", pHba->name,max);
-
- for(i=0;i<max;i++) {
- if( lct->lct_entry[i].user_tid != 0xfff){
- continue;
- }
-
- if( lct->lct_entry[i].class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
- lct->lct_entry[i].class_id == I2O_CLASS_SCSI_PERIPHERAL ||
- lct->lct_entry[i].class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
- tid = lct->lct_entry[i].tid;
- if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
- printk(KERN_ERR"%s: Could not query device\n",pHba->name);
- continue;
- }
- bus_no = buf[0]>>16;
- if (bus_no >= MAX_CHANNEL) { /* Something wrong skip it */
- printk(KERN_WARNING
- "%s: Channel number %d out of range\n",
- pHba->name, bus_no);
- continue;
- }
-
- scsi_id = buf[1];
- scsi_lun = (buf[2]>>8 )&0xff;
- pDev = pHba->channel[bus_no].device[scsi_id];
- /* da lun */
- while(pDev) {
- if(pDev->scsi_lun == scsi_lun) {
- break;
- }
- pDev = pDev->next_lun;
- }
- if(!pDev ) { // Something new add it
- d = kmalloc(sizeof(struct i2o_device),
- GFP_ATOMIC);
- if(d==NULL)
- {
- printk(KERN_CRIT "Out of memory for I2O device data.\n");
- return -ENOMEM;
- }
-
- d->controller = pHba;
- d->next = NULL;
-
- memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
-
- d->flags = 0;
- adpt_i2o_report_hba_unit(pHba, d);
- adpt_i2o_install_device(pHba, d);
-
- pDev = pHba->channel[bus_no].device[scsi_id];
- if( pDev == NULL){
- pDev =
- kzalloc(sizeof(struct adpt_device),
- GFP_ATOMIC);
- if(pDev == NULL) {
- return -ENOMEM;
- }
- pHba->channel[bus_no].device[scsi_id] = pDev;
- } else {
- while (pDev->next_lun) {
- pDev = pDev->next_lun;
- }
- pDev = pDev->next_lun =
- kzalloc(sizeof(struct adpt_device),
- GFP_ATOMIC);
- if(pDev == NULL) {
- return -ENOMEM;
- }
- }
- pDev->tid = d->lct_data.tid;
- pDev->scsi_channel = bus_no;
- pDev->scsi_id = scsi_id;
- pDev->scsi_lun = scsi_lun;
- pDev->pI2o_dev = d;
- d->owner = pDev;
- pDev->type = (buf[0])&0xff;
- pDev->flags = (buf[0]>>8)&0xff;
- // Too late, SCSI system has made up it's mind, but what the hey ...
- if(scsi_id > pHba->top_scsi_id){
- pHba->top_scsi_id = scsi_id;
- }
- if(scsi_lun > pHba->top_scsi_lun){
- pHba->top_scsi_lun = scsi_lun;
- }
- continue;
- } // end of new i2o device
-
- // We found an old device - check it
- while(pDev) {
- if(pDev->scsi_lun == scsi_lun) {
- if(!scsi_device_online(pDev->pScsi_dev)) {
- printk(KERN_WARNING"%s: Setting device (%d,%d,%d) back online\n",
- pHba->name,bus_no,scsi_id,scsi_lun);
- if (pDev->pScsi_dev) {
- scsi_device_set_state(pDev->pScsi_dev, SDEV_RUNNING);
- }
- }
- d = pDev->pI2o_dev;
- if(d->lct_data.tid != tid) { // something changed
- pDev->tid = tid;
- memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
- if (pDev->pScsi_dev) {
- pDev->pScsi_dev->changed = TRUE;
- pDev->pScsi_dev->removable = TRUE;
- }
- }
- // Found it - mark it scanned
- pDev->state = DPTI_DEV_ONLINE;
- break;
- }
- pDev = pDev->next_lun;
- }
- }
- }
- for (pI2o_dev = pHba->devices; pI2o_dev; pI2o_dev = pI2o_dev->next) {
- pDev =(struct adpt_device*) pI2o_dev->owner;
- if(!pDev){
- continue;
- }
- // Drive offline drives that previously existed but could not be found
- // in the LCT table
- if (pDev->state & DPTI_DEV_UNSCANNED){
- pDev->state = DPTI_DEV_OFFLINE;
- printk(KERN_WARNING"%s: Device (%d,%d,%d) offline\n",pHba->name,pDev->scsi_channel,pDev->scsi_id,pDev->scsi_lun);
- if (pDev->pScsi_dev) {
- scsi_device_set_state(pDev->pScsi_dev, SDEV_OFFLINE);
- }
- }
- }
- return 0;
-}
-
-static void adpt_fail_posted_scbs(adpt_hba* pHba)
-{
- struct scsi_cmnd* cmd = NULL;
- struct scsi_device* d = NULL;
-
- shost_for_each_device(d, pHba->host) {
- unsigned long flags;
- spin_lock_irqsave(&d->list_lock, flags);
- list_for_each_entry(cmd, &d->cmd_list, list) {
- if(cmd->serial_number == 0){
- continue;
- }
- cmd->result = (DID_OK << 16) | (QUEUE_FULL <<1);
- cmd->scsi_done(cmd);
- }
- spin_unlock_irqrestore(&d->list_lock, flags);
- }
-}
-
-
-/*============================================================================
- * Routines from i2o subsystem
- *============================================================================
- */
-
-
-
-/*
- * Bring an I2O controller into HOLD state. See the spec.
- */
-static int adpt_i2o_activate_hba(adpt_hba* pHba)
-{
- int rcode;
-
- if(pHba->initialized ) {
- if (adpt_i2o_status_get(pHba) < 0) {
- if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
- printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
- return rcode;
- }
- if (adpt_i2o_status_get(pHba) < 0) {
- printk(KERN_INFO "HBA not responding.\n");
- return -1;
- }
- }
-
- if(pHba->status_block->iop_state == ADAPTER_STATE_FAULTED) {
- printk(KERN_CRIT "%s: hardware fault\n", pHba->name);
- return -1;
- }
-
- if (pHba->status_block->iop_state == ADAPTER_STATE_READY ||
- pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL ||
- pHba->status_block->iop_state == ADAPTER_STATE_HOLD ||
- pHba->status_block->iop_state == ADAPTER_STATE_FAILED) {
- adpt_i2o_reset_hba(pHba);
- if (adpt_i2o_status_get(pHba) < 0 || pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
- printk(KERN_ERR "%s: Failed to initialize.\n", pHba->name);
- return -1;
- }
- }
- } else {
- if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
- printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
- return rcode;
- }
-
- }
-
- if (adpt_i2o_init_outbound_q(pHba) < 0) {
- return -1;
- }
-
- /* In HOLD state */
-
- if (adpt_i2o_hrt_get(pHba) < 0) {
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Bring a controller online into OPERATIONAL state.
- */
-
-static int adpt_i2o_online_hba(adpt_hba* pHba)
-{
- if (adpt_i2o_systab_send(pHba) < 0) {
- adpt_i2o_delete_hba(pHba);
- return -1;
- }
- /* In READY state */
-
- if (adpt_i2o_enable_hba(pHba) < 0) {
- adpt_i2o_delete_hba(pHba);
- return -1;
- }
-
- /* In OPERATIONAL state */
- return 0;
-}
-
-static s32 adpt_send_nop(adpt_hba*pHba,u32 m)
-{
- u32 __iomem *msg;
- ulong timeout = jiffies + 5*HZ;
-
- while(m == EMPTY_QUEUE){
- rmb();
- m = readl(pHba->post_port);
- if(m != EMPTY_QUEUE){
- break;
- }
- if(time_after(jiffies,timeout)){
- printk(KERN_ERR "%s: Timeout waiting for message frame!\n",pHba->name);
- return 2;
- }
- schedule_timeout_uninterruptible(1);
- }
- msg = (u32 __iomem *)(pHba->msg_addr_virt + m);
- writel( THREE_WORD_MSG_SIZE | SGL_OFFSET_0,&msg[0]);
- writel( I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0,&msg[1]);
- writel( 0,&msg[2]);
- wmb();
-
- writel(m, pHba->post_port);
- wmb();
- return 0;
-}
-
-static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba)
-{
- u8 *status;
- dma_addr_t addr;
- u32 __iomem *msg = NULL;
- int i;
- ulong timeout = jiffies + TMOUT_INITOUTBOUND*HZ;
- u32 m;
-
- do {
- rmb();
- m = readl(pHba->post_port);
- if (m != EMPTY_QUEUE) {
- break;
- }
-
- if(time_after(jiffies,timeout)){
- printk(KERN_WARNING"%s: Timeout waiting for message frame\n",pHba->name);
- return -ETIMEDOUT;
- }
- schedule_timeout_uninterruptible(1);
- } while(m == EMPTY_QUEUE);
-
- msg=(u32 __iomem *)(pHba->msg_addr_virt+m);
-
- status = dma_alloc_coherent(&pHba->pDev->dev, 4, &addr, GFP_KERNEL);
- if (!status) {
- adpt_send_nop(pHba, m);
- printk(KERN_WARNING"%s: IOP reset failed - no free memory.\n",
- pHba->name);
- return -ENOMEM;
- }
- memset(status, 0, 4);
-
- writel(EIGHT_WORD_MSG_SIZE| SGL_OFFSET_6, &msg[0]);
- writel(I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID, &msg[1]);
- writel(0, &msg[2]);
- writel(0x0106, &msg[3]); /* Transaction context */
- writel(4096, &msg[4]); /* Host page frame size */
- writel((REPLY_FRAME_SIZE)<<16|0x80, &msg[5]); /* Outbound msg frame size and Initcode */
- writel(0xD0000004, &msg[6]); /* Simple SG LE, EOB */
- writel((u32)addr, &msg[7]);
-
- writel(m, pHba->post_port);
- wmb();
-
- // Wait for the reply status to come back
- do {
- if (*status) {
- if (*status != 0x01 /*I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS*/) {
- break;
- }
- }
- rmb();
- if(time_after(jiffies,timeout)){
- printk(KERN_WARNING"%s: Timeout Initializing\n",pHba->name);
- /* We lose 4 bytes of "status" here, but we
- cannot free these because controller may
- awake and corrupt those bytes at any time */
- /* dma_free_coherent(&pHba->pDev->dev, 4, status, addr); */
- return -ETIMEDOUT;
- }
- schedule_timeout_uninterruptible(1);
- } while (1);
-
- // If the command was successful, fill the fifo with our reply
- // message packets
- if(*status != 0x04 /*I2O_EXEC_OUTBOUND_INIT_COMPLETE*/) {
- dma_free_coherent(&pHba->pDev->dev, 4, status, addr);
- return -2;
- }
- dma_free_coherent(&pHba->pDev->dev, 4, status, addr);
-
- if(pHba->reply_pool != NULL) {
- dma_free_coherent(&pHba->pDev->dev,
- pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4,
- pHba->reply_pool, pHba->reply_pool_pa);
- }
-
- pHba->reply_pool = dma_alloc_coherent(&pHba->pDev->dev,
- pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4,
- &pHba->reply_pool_pa, GFP_KERNEL);
- if (!pHba->reply_pool) {
- printk(KERN_ERR "%s: Could not allocate reply pool\n", pHba->name);
- return -ENOMEM;
- }
- memset(pHba->reply_pool, 0 , pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4);
-
- for(i = 0; i < pHba->reply_fifo_size; i++) {
- writel(pHba->reply_pool_pa + (i * REPLY_FRAME_SIZE * 4),
- pHba->reply_port);
- wmb();
- }
- adpt_i2o_status_get(pHba);
- return 0;
-}
-
-
-/*
- * I2O System Table. Contains information about
- * all the IOPs in the system. Used to inform IOPs
- * about each other's existence.
- *
- * sys_tbl_ver is the CurrentChangeIndicator that is
- * used by IOPs to track changes.
- */
-
-
-
-static s32 adpt_i2o_status_get(adpt_hba* pHba)
-{
- ulong timeout;
- u32 m;
- u32 __iomem *msg;
- u8 *status_block=NULL;
-
- if(pHba->status_block == NULL) {
- pHba->status_block = dma_alloc_coherent(&pHba->pDev->dev,
- sizeof(i2o_status_block),
- &pHba->status_block_pa, GFP_KERNEL);
- if(pHba->status_block == NULL) {
- printk(KERN_ERR
- "dpti%d: Get Status Block failed; Out of memory. \n",
- pHba->unit);
- return -ENOMEM;
- }
- }
- memset(pHba->status_block, 0, sizeof(i2o_status_block));
- status_block = (u8*)(pHba->status_block);
- timeout = jiffies+TMOUT_GETSTATUS*HZ;
- do {
- rmb();
- m = readl(pHba->post_port);
- if (m != EMPTY_QUEUE) {
- break;
- }
- if(time_after(jiffies,timeout)){
- printk(KERN_ERR "%s: Timeout waiting for message !\n",
- pHba->name);
- return -ETIMEDOUT;
- }
- schedule_timeout_uninterruptible(1);
- } while(m==EMPTY_QUEUE);
-
-
- msg=(u32 __iomem *)(pHba->msg_addr_virt+m);
-
- writel(NINE_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]);
- writel(I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID, &msg[1]);
- writel(1, &msg[2]);
- writel(0, &msg[3]);
- writel(0, &msg[4]);
- writel(0, &msg[5]);
- writel( dma_low(pHba->status_block_pa), &msg[6]);
- writel( dma_high(pHba->status_block_pa), &msg[7]);
- writel(sizeof(i2o_status_block), &msg[8]); // 88 bytes
-
- //post message
- writel(m, pHba->post_port);
- wmb();
-
- while(status_block[87]!=0xff){
- if(time_after(jiffies,timeout)){
- printk(KERN_ERR"dpti%d: Get status timeout.\n",
- pHba->unit);
- return -ETIMEDOUT;
- }
- rmb();
- schedule_timeout_uninterruptible(1);
- }
-
- // Set up our number of outbound and inbound messages
- pHba->post_fifo_size = pHba->status_block->max_inbound_frames;
- if (pHba->post_fifo_size > MAX_TO_IOP_MESSAGES) {
- pHba->post_fifo_size = MAX_TO_IOP_MESSAGES;
- }
-
- pHba->reply_fifo_size = pHba->status_block->max_outbound_frames;
- if (pHba->reply_fifo_size > MAX_FROM_IOP_MESSAGES) {
- pHba->reply_fifo_size = MAX_FROM_IOP_MESSAGES;
- }
-
- // Calculate the Scatter Gather list size
- if (dpt_dma64(pHba)) {
- pHba->sg_tablesize
- = ((pHba->status_block->inbound_frame_size * 4
- - 14 * sizeof(u32))
- / (sizeof(struct sg_simple_element) + sizeof(u32)));
- } else {
- pHba->sg_tablesize
- = ((pHba->status_block->inbound_frame_size * 4
- - 12 * sizeof(u32))
- / sizeof(struct sg_simple_element));
- }
- if (pHba->sg_tablesize > SG_LIST_ELEMENTS) {
- pHba->sg_tablesize = SG_LIST_ELEMENTS;
- }
-
-
-#ifdef DEBUG
- printk("dpti%d: State = ",pHba->unit);
- switch(pHba->status_block->iop_state) {
- case 0x01:
- printk("INIT\n");
- break;
- case 0x02:
- printk("RESET\n");
- break;
- case 0x04:
- printk("HOLD\n");
- break;
- case 0x05:
- printk("READY\n");
- break;
- case 0x08:
- printk("OPERATIONAL\n");
- break;
- case 0x10:
- printk("FAILED\n");
- break;
- case 0x11:
- printk("FAULTED\n");
- break;
- default:
- printk("%x (unknown!!)\n",pHba->status_block->iop_state);
- }
-#endif
- return 0;
-}
-
-/*
- * Get the IOP's Logical Configuration Table
- */
-static int adpt_i2o_lct_get(adpt_hba* pHba)
-{
- u32 msg[8];
- int ret;
- u32 buf[16];
-
- if ((pHba->lct_size == 0) || (pHba->lct == NULL)){
- pHba->lct_size = pHba->status_block->expected_lct_size;
- }
- do {
- if (pHba->lct == NULL) {
- pHba->lct = dma_alloc_coherent(&pHba->pDev->dev,
- pHba->lct_size, &pHba->lct_pa,
- GFP_ATOMIC);
- if(pHba->lct == NULL) {
- printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n",
- pHba->name);
- return -ENOMEM;
- }
- }
- memset(pHba->lct, 0, pHba->lct_size);
-
- msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6;
- msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
- msg[2] = 0;
- msg[3] = 0;
- msg[4] = 0xFFFFFFFF; /* All devices */
- msg[5] = 0x00000000; /* Report now */
- msg[6] = 0xD0000000|pHba->lct_size;
- msg[7] = (u32)pHba->lct_pa;
-
- if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 360))) {
- printk(KERN_ERR "%s: LCT Get failed (status=%#10x.\n",
- pHba->name, ret);
- printk(KERN_ERR"Adaptec: Error Reading Hardware.\n");
- return ret;
- }
-
- if ((pHba->lct->table_size << 2) > pHba->lct_size) {
- pHba->lct_size = pHba->lct->table_size << 2;
- dma_free_coherent(&pHba->pDev->dev, pHba->lct_size,
- pHba->lct, pHba->lct_pa);
- pHba->lct = NULL;
- }
- } while (pHba->lct == NULL);
-
- PDEBUG("%s: Hardware resource table read.\n", pHba->name);
-
-
- // I2O_DPT_EXEC_IOP_BUFFERS_GROUP_NO;
- if(adpt_i2o_query_scalar(pHba, 0 , 0x8000, -1, buf, sizeof(buf))>=0) {
- pHba->FwDebugBufferSize = buf[1];
- pHba->FwDebugBuffer_P = ioremap(pHba->base_addr_phys + buf[0],
- pHba->FwDebugBufferSize);
- if (pHba->FwDebugBuffer_P) {
- pHba->FwDebugFlags_P = pHba->FwDebugBuffer_P +
- FW_DEBUG_FLAGS_OFFSET;
- pHba->FwDebugBLEDvalue_P = pHba->FwDebugBuffer_P +
- FW_DEBUG_BLED_OFFSET;
- pHba->FwDebugBLEDflag_P = pHba->FwDebugBLEDvalue_P + 1;
- pHba->FwDebugStrLength_P = pHba->FwDebugBuffer_P +
- FW_DEBUG_STR_LENGTH_OFFSET;
- pHba->FwDebugBuffer_P += buf[2];
- pHba->FwDebugFlags = 0;
- }
- }
-
- return 0;
-}
-
-static int adpt_i2o_build_sys_table(void)
-{
- adpt_hba* pHba = hba_chain;
- int count = 0;
-
- if (sys_tbl)
- dma_free_coherent(&pHba->pDev->dev, sys_tbl_len,
- sys_tbl, sys_tbl_pa);
-
- sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs
- (hba_count) * sizeof(struct i2o_sys_tbl_entry);
-
- sys_tbl = dma_alloc_coherent(&pHba->pDev->dev,
- sys_tbl_len, &sys_tbl_pa, GFP_KERNEL);
- if (!sys_tbl) {
- printk(KERN_WARNING "SysTab Set failed. Out of memory.\n");
- return -ENOMEM;
- }
- memset(sys_tbl, 0, sys_tbl_len);
-
- sys_tbl->num_entries = hba_count;
- sys_tbl->version = I2OVERSION;
- sys_tbl->change_ind = sys_tbl_ind++;
-
- for(pHba = hba_chain; pHba; pHba = pHba->next) {
- u64 addr;
- // Get updated Status Block so we have the latest information
- if (adpt_i2o_status_get(pHba)) {
- sys_tbl->num_entries--;
- continue; // try next one
- }
-
- sys_tbl->iops[count].org_id = pHba->status_block->org_id;
- sys_tbl->iops[count].iop_id = pHba->unit + 2;
- sys_tbl->iops[count].seg_num = 0;
- sys_tbl->iops[count].i2o_version = pHba->status_block->i2o_version;
- sys_tbl->iops[count].iop_state = pHba->status_block->iop_state;
- sys_tbl->iops[count].msg_type = pHba->status_block->msg_type;
- sys_tbl->iops[count].frame_size = pHba->status_block->inbound_frame_size;
- sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ??
- sys_tbl->iops[count].iop_capabilities = pHba->status_block->iop_capabilities;
- addr = pHba->base_addr_phys + 0x40;
- sys_tbl->iops[count].inbound_low = dma_low(addr);
- sys_tbl->iops[count].inbound_high = dma_high(addr);
-
- count++;
- }
-
-#ifdef DEBUG
-{
- u32 *table = (u32*)sys_tbl;
- printk(KERN_DEBUG"sys_tbl_len=%d in 32bit words\n",(sys_tbl_len >>2));
- for(count = 0; count < (sys_tbl_len >>2); count++) {
- printk(KERN_INFO "sys_tbl[%d] = %0#10x\n",
- count, table[count]);
- }
-}
-#endif
-
- return 0;
-}
-
-
-/*
- * Dump the information block associated with a given unit (TID)
- */
-
-static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d)
-{
- char buf[64];
- int unit = d->lct_data.tid;
-
- printk(KERN_INFO "TID %3.3d ", unit);
-
- if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 3, buf, 16)>=0)
- {
- buf[16]=0;
- printk(" Vendor: %-12.12s", buf);
- }
- if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 4, buf, 16)>=0)
- {
- buf[16]=0;
- printk(" Device: %-12.12s", buf);
- }
- if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 6, buf, 8)>=0)
- {
- buf[8]=0;
- printk(" Rev: %-12.12s\n", buf);
- }
-#ifdef DEBUG
- printk(KERN_INFO "\tClass: %.21s\n", adpt_i2o_get_class_name(d->lct_data.class_id));
- printk(KERN_INFO "\tSubclass: 0x%04X\n", d->lct_data.sub_class);
- printk(KERN_INFO "\tFlags: ");
-
- if(d->lct_data.device_flags&(1<<0))
- printk("C"); // ConfigDialog requested
- if(d->lct_data.device_flags&(1<<1))
- printk("U"); // Multi-user capable
- if(!(d->lct_data.device_flags&(1<<4)))
- printk("P"); // Peer service enabled!
- if(!(d->lct_data.device_flags&(1<<5)))
- printk("M"); // Mgmt service enabled!
- printk("\n");
-#endif
-}
-
-#ifdef DEBUG
-/*
- * Do i2o class name lookup
- */
-static const char *adpt_i2o_get_class_name(int class)
-{
- int idx = 16;
- static char *i2o_class_name[] = {
- "Executive",
- "Device Driver Module",
- "Block Device",
- "Tape Device",
- "LAN Interface",
- "WAN Interface",
- "Fibre Channel Port",
- "Fibre Channel Device",
- "SCSI Device",
- "ATE Port",
- "ATE Device",
- "Floppy Controller",
- "Floppy Device",
- "Secondary Bus Port",
- "Peer Transport Agent",
- "Peer Transport",
- "Unknown"
- };
-
- switch(class&0xFFF) {
- case I2O_CLASS_EXECUTIVE:
- idx = 0; break;
- case I2O_CLASS_DDM:
- idx = 1; break;
- case I2O_CLASS_RANDOM_BLOCK_STORAGE:
- idx = 2; break;
- case I2O_CLASS_SEQUENTIAL_STORAGE:
- idx = 3; break;
- case I2O_CLASS_LAN:
- idx = 4; break;
- case I2O_CLASS_WAN:
- idx = 5; break;
- case I2O_CLASS_FIBRE_CHANNEL_PORT:
- idx = 6; break;
- case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
- idx = 7; break;
- case I2O_CLASS_SCSI_PERIPHERAL:
- idx = 8; break;
- case I2O_CLASS_ATE_PORT:
- idx = 9; break;
- case I2O_CLASS_ATE_PERIPHERAL:
- idx = 10; break;
- case I2O_CLASS_FLOPPY_CONTROLLER:
- idx = 11; break;
- case I2O_CLASS_FLOPPY_DEVICE:
- idx = 12; break;
- case I2O_CLASS_BUS_ADAPTER_PORT:
- idx = 13; break;
- case I2O_CLASS_PEER_TRANSPORT_AGENT:
- idx = 14; break;
- case I2O_CLASS_PEER_TRANSPORT:
- idx = 15; break;
- }
- return i2o_class_name[idx];
-}
-#endif
-
-
-static s32 adpt_i2o_hrt_get(adpt_hba* pHba)
-{
- u32 msg[6];
- int ret, size = sizeof(i2o_hrt);
-
- do {
- if (pHba->hrt == NULL) {
- pHba->hrt = dma_alloc_coherent(&pHba->pDev->dev,
- size, &pHba->hrt_pa, GFP_KERNEL);
- if (pHba->hrt == NULL) {
- printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", pHba->name);
- return -ENOMEM;
- }
- }
-
- msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
- msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
- msg[2]= 0;
- msg[3]= 0;
- msg[4]= (0xD0000000 | size); /* Simple transaction */
- msg[5]= (u32)pHba->hrt_pa; /* Dump it here */
-
- if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg),20))) {
- printk(KERN_ERR "%s: Unable to get HRT (status=%#10x)\n", pHba->name, ret);
- return ret;
- }
-
- if (pHba->hrt->num_entries * pHba->hrt->entry_len << 2 > size) {
- int newsize = pHba->hrt->num_entries * pHba->hrt->entry_len << 2;
- dma_free_coherent(&pHba->pDev->dev, size,
- pHba->hrt, pHba->hrt_pa);
- size = newsize;
- pHba->hrt = NULL;
- }
- } while(pHba->hrt == NULL);
- return 0;
-}
-
-/*
- * Query one scalar group value or a whole scalar group.
- */
-static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid,
- int group, int field, void *buf, int buflen)
-{
- u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field };
- u8 *opblk_va;
- dma_addr_t opblk_pa;
- u8 *resblk_va;
- dma_addr_t resblk_pa;
-
- int size;
-
- /* 8 bytes for header */
- resblk_va = dma_alloc_coherent(&pHba->pDev->dev,
- sizeof(u8) * (8 + buflen), &resblk_pa, GFP_KERNEL);
- if (resblk_va == NULL) {
- printk(KERN_CRIT "%s: query scalar failed; Out of memory.\n", pHba->name);
- return -ENOMEM;
- }
-
- opblk_va = dma_alloc_coherent(&pHba->pDev->dev,
- sizeof(opblk), &opblk_pa, GFP_KERNEL);
- if (opblk_va == NULL) {
- dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
- resblk_va, resblk_pa);
- printk(KERN_CRIT "%s: query operatio failed; Out of memory.\n",
- pHba->name);
- return -ENOMEM;
- }
- if (field == -1) /* whole group */
- opblk[4] = -1;
-
- memcpy(opblk_va, opblk, sizeof(opblk));
- size = adpt_i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, pHba, tid,
- opblk_va, opblk_pa, sizeof(opblk),
- resblk_va, resblk_pa, sizeof(u8)*(8+buflen));
- dma_free_coherent(&pHba->pDev->dev, sizeof(opblk), opblk_va, opblk_pa);
- if (size == -ETIME) {
- dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
- resblk_va, resblk_pa);
- printk(KERN_WARNING "%s: issue params failed; Timed out.\n", pHba->name);
- return -ETIME;
- } else if (size == -EINTR) {
- dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
- resblk_va, resblk_pa);
- printk(KERN_WARNING "%s: issue params failed; Interrupted.\n", pHba->name);
- return -EINTR;
- }
-
- memcpy(buf, resblk_va+8, buflen); /* cut off header */
-
- dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
- resblk_va, resblk_pa);
- if (size < 0)
- return size;
-
- return buflen;
-}
-
-
-/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET
- *
- * This function can be used for all UtilParamsGet/Set operations.
- * The OperationBlock is given in opblk-buffer,
- * and results are returned in resblk-buffer.
- * Note that the minimum sized resblk is 8 bytes and contains
- * ResultCount, ErrorInfoSize, BlockStatus and BlockSize.
- */
-static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid,
- void *opblk_va, dma_addr_t opblk_pa, int oplen,
- void *resblk_va, dma_addr_t resblk_pa, int reslen)
-{
- u32 msg[9];
- u32 *res = (u32 *)resblk_va;
- int wait_status;
-
- msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5;
- msg[1] = cmd << 24 | HOST_TID << 12 | tid;
- msg[2] = 0;
- msg[3] = 0;
- msg[4] = 0;
- msg[5] = 0x54000000 | oplen; /* OperationBlock */
- msg[6] = (u32)opblk_pa;
- msg[7] = 0xD0000000 | reslen; /* ResultBlock */
- msg[8] = (u32)resblk_pa;
-
- if ((wait_status = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 20))) {
- printk("adpt_i2o_issue_params: post_wait failed (%p)\n", resblk_va);
- return wait_status; /* -DetailedStatus */
- }
-
- if (res[1]&0x00FF0000) { /* BlockStatus != SUCCESS */
- printk(KERN_WARNING "%s: %s - Error:\n ErrorInfoSize = 0x%02x, "
- "BlockStatus = 0x%02x, BlockSize = 0x%04x\n",
- pHba->name,
- (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET"
- : "PARAMS_GET",
- res[1]>>24, (res[1]>>16)&0xFF, res[1]&0xFFFF);
- return -((res[1] >> 16) & 0xFF); /* -BlockStatus */
- }
-
- return 4 + ((res[1] & 0x0000FFFF) << 2); /* bytes used in resblk */
-}
-
-
-static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba)
-{
- u32 msg[4];
- int ret;
-
- adpt_i2o_status_get(pHba);
-
- /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */
-
- if((pHba->status_block->iop_state != ADAPTER_STATE_READY) &&
- (pHba->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)){
- return 0;
- }
-
- msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
- msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
- msg[2] = 0;
- msg[3] = 0;
-
- if((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
- printk(KERN_INFO"dpti%d: Unable to quiesce (status=%#x).\n",
- pHba->unit, -ret);
- } else {
- printk(KERN_INFO"dpti%d: Quiesced.\n",pHba->unit);
- }
-
- adpt_i2o_status_get(pHba);
- return ret;
-}
-
-
-/*
- * Enable IOP. Allows the IOP to resume external operations.
- */
-static int adpt_i2o_enable_hba(adpt_hba* pHba)
-{
- u32 msg[4];
- int ret;
-
- adpt_i2o_status_get(pHba);
- if(!pHba->status_block){
- return -ENOMEM;
- }
- /* Enable only allowed on READY state */
- if(pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL)
- return 0;
-
- if(pHba->status_block->iop_state != ADAPTER_STATE_READY)
- return -EINVAL;
-
- msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
- msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID;
- msg[2]= 0;
- msg[3]= 0;
-
- if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
- printk(KERN_WARNING"%s: Could not enable (status=%#10x).\n",
- pHba->name, ret);
- } else {
- PDEBUG("%s: Enabled.\n", pHba->name);
- }
-
- adpt_i2o_status_get(pHba);
- return ret;
-}
-
-
-static int adpt_i2o_systab_send(adpt_hba* pHba)
-{
- u32 msg[12];
- int ret;
-
- msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6;
- msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID;
- msg[2] = 0;
- msg[3] = 0;
- msg[4] = (0<<16) | ((pHba->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */
- msg[5] = 0; /* Segment 0 */
-
- /*
- * Provide three SGL-elements:
- * System table (SysTab), Private memory space declaration and
- * Private i/o space declaration
- */
- msg[6] = 0x54000000 | sys_tbl_len;
- msg[7] = (u32)sys_tbl_pa;
- msg[8] = 0x54000000 | 0;
- msg[9] = 0;
- msg[10] = 0xD4000000 | 0;
- msg[11] = 0;
-
- if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 120))) {
- printk(KERN_INFO "%s: Unable to set SysTab (status=%#10x).\n",
- pHba->name, ret);
- }
-#ifdef DEBUG
- else {
- PINFO("%s: SysTab set.\n", pHba->name);
- }
-#endif
-
- return ret;
- }
-
-
-/*============================================================================
- *
- *============================================================================
- */
-
-
-#ifdef UARTDELAY
-
-static static void adpt_delay(int millisec)
-{
- int i;
- for (i = 0; i < millisec; i++) {
- udelay(1000); /* delay for one millisecond */
- }
-}
-
-#endif
-
-static struct scsi_host_template driver_template = {
- .module = THIS_MODULE,
- .name = "dpt_i2o",
- .proc_name = "dpt_i2o",
- .proc_info = adpt_proc_info,
- .info = adpt_info,
- .queuecommand = adpt_queue,
- .eh_abort_handler = adpt_abort,
- .eh_device_reset_handler = adpt_device_reset,
- .eh_bus_reset_handler = adpt_bus_reset,
- .eh_host_reset_handler = adpt_reset,
- .bios_param = adpt_bios_param,
- .slave_configure = adpt_slave_configure,
- .can_queue = MAX_TO_IOP_MESSAGES,
- .this_id = 7,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-static int __init adpt_init(void)
-{
- int error;
- adpt_hba *pHba, *next;
-
- printk("Loading Adaptec I2O RAID: Version " DPT_I2O_VERSION "\n");
-
- error = adpt_detect(&driver_template);
- if (error < 0)
- return error;
- if (hba_chain == NULL)
- return -ENODEV;
-
- for (pHba = hba_chain; pHba; pHba = pHba->next) {
- error = scsi_add_host(pHba->host, &pHba->pDev->dev);
- if (error)
- goto fail;
- scsi_scan_host(pHba->host);
- }
- return 0;
-fail:
- for (pHba = hba_chain; pHba; pHba = next) {
- next = pHba->next;
- scsi_remove_host(pHba->host);
- }
- return error;
-}
-
-static void __exit adpt_exit(void)
-{
- adpt_hba *pHba, *next;
-
- for (pHba = hba_chain; pHba; pHba = pHba->next)
- scsi_remove_host(pHba->host);
- for (pHba = hba_chain; pHba; pHba = next) {
- next = pHba->next;
- adpt_release(pHba->host);
- }
-}
-
-module_init(adpt_init);
-module_exit(adpt_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/dpti.h b/drivers/scsi/dpti.h
deleted file mode 100644
index beded716f93f..000000000000
--- a/drivers/scsi/dpti.h
+++ /dev/null
@@ -1,337 +0,0 @@
-/***************************************************************************
- dpti.h - description
- -------------------
- begin : Thu Sep 7 2000
- copyright : (C) 2001 by Adaptec
-
- See Documentation/scsi/dpti.txt for history, notes, license info
- and credits
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-
-#ifndef _DPT_H
-#define _DPT_H
-
-#define MAX_TO_IOP_MESSAGES (255)
-#define MAX_FROM_IOP_MESSAGES (255)
-
-
-/*
- * SCSI interface function Prototypes
- */
-
-static int adpt_detect(struct scsi_host_template * sht);
-static int adpt_queue(struct Scsi_Host *h, struct scsi_cmnd * cmd);
-static int adpt_abort(struct scsi_cmnd * cmd);
-static int adpt_reset(struct scsi_cmnd* cmd);
-static int adpt_release(struct Scsi_Host *host);
-static int adpt_slave_configure(struct scsi_device *);
-
-static const char *adpt_info(struct Scsi_Host *pSHost);
-static int adpt_bios_param(struct scsi_device * sdev, struct block_device *dev,
- sector_t, int geom[]);
-
-static int adpt_bus_reset(struct scsi_cmnd* cmd);
-static int adpt_device_reset(struct scsi_cmnd* cmd);
-
-
-/*
- * struct scsi_host_template (see scsi/scsi_host.h)
- */
-
-#define DPT_DRIVER_NAME "Adaptec I2O RAID"
-
-#ifndef HOSTS_C
-
-#include "dpt/sys_info.h"
-#include <linux/wait.h>
-#include "dpt/dpti_i2o.h"
-#include "dpt/dpti_ioctl.h"
-
-#define DPT_I2O_VERSION "2.4 Build 5go"
-#define DPT_VERSION 2
-#define DPT_REVISION '4'
-#define DPT_SUBREVISION '5'
-#define DPT_BETA ""
-#define DPT_MONTH 8
-#define DPT_DAY 7
-#define DPT_YEAR (2001-1980)
-
-#define DPT_DRIVER "dpt_i2o"
-#define DPTI_I2O_MAJOR (151)
-#define DPT_ORGANIZATION_ID (0x1B) /* For Private Messages */
-#define DPTI_MAX_HBA (16)
-#define MAX_CHANNEL (5) // Maximum Channel # Supported
-#define MAX_ID (128) // Maximum Target ID Supported
-
-/* Sizes in 4 byte words */
-#define REPLY_FRAME_SIZE (17)
-#define MAX_MESSAGE_SIZE (128)
-#define SG_LIST_ELEMENTS (56)
-
-#define EMPTY_QUEUE 0xffffffff
-#define I2O_INTERRUPT_PENDING_B (0x08)
-
-#define PCI_DPT_VENDOR_ID (0x1044) // DPT PCI Vendor ID
-#define PCI_DPT_DEVICE_ID (0xA501) // DPT PCI I2O Device ID
-#define PCI_DPT_RAPTOR_DEVICE_ID (0xA511)
-
-/* Debugging macro from Linux Device Drivers - Rubini */
-#undef PDEBUG
-#ifdef DEBUG
-//TODO add debug level switch
-# define PDEBUG(fmt, args...) printk(KERN_DEBUG "dpti: " fmt, ##args)
-# define PDEBUGV(fmt, args...) printk(KERN_DEBUG "dpti: " fmt, ##args)
-#else
-# define PDEBUG(fmt, args...) /* not debugging: nothing */
-# define PDEBUGV(fmt, args...) /* not debugging: nothing */
-#endif
-
-#define PERROR(fmt, args...) printk(KERN_ERR fmt, ##args)
-#define PWARN(fmt, args...) printk(KERN_WARNING fmt, ##args)
-#define PINFO(fmt, args...) printk(KERN_INFO fmt, ##args)
-#define PCRIT(fmt, args...) printk(KERN_CRIT fmt, ##args)
-
-#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
-
-// Command timeouts
-#define FOREVER (0)
-#define TMOUT_INQUIRY (20)
-#define TMOUT_FLUSH (360/45)
-#define TMOUT_ABORT (30)
-#define TMOUT_SCSI (300)
-#define TMOUT_IOPRESET (360)
-#define TMOUT_GETSTATUS (15)
-#define TMOUT_INITOUTBOUND (15)
-#define TMOUT_LCT (360)
-
-
-#define I2O_SCSI_DEVICE_DSC_MASK 0x00FF
-
-#define I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION 0x000A
-
-#define I2O_SCSI_DSC_MASK 0xFF00
-#define I2O_SCSI_DSC_SUCCESS 0x0000
-#define I2O_SCSI_DSC_REQUEST_ABORTED 0x0200
-#define I2O_SCSI_DSC_UNABLE_TO_ABORT 0x0300
-#define I2O_SCSI_DSC_COMPLETE_WITH_ERROR 0x0400
-#define I2O_SCSI_DSC_ADAPTER_BUSY 0x0500
-#define I2O_SCSI_DSC_REQUEST_INVALID 0x0600
-#define I2O_SCSI_DSC_PATH_INVALID 0x0700
-#define I2O_SCSI_DSC_DEVICE_NOT_PRESENT 0x0800
-#define I2O_SCSI_DSC_UNABLE_TO_TERMINATE 0x0900
-#define I2O_SCSI_DSC_SELECTION_TIMEOUT 0x0A00
-#define I2O_SCSI_DSC_COMMAND_TIMEOUT 0x0B00
-#define I2O_SCSI_DSC_MR_MESSAGE_RECEIVED 0x0D00
-#define I2O_SCSI_DSC_SCSI_BUS_RESET 0x0E00
-#define I2O_SCSI_DSC_PARITY_ERROR_FAILURE 0x0F00
-#define I2O_SCSI_DSC_AUTOSENSE_FAILED 0x1000
-#define I2O_SCSI_DSC_NO_ADAPTER 0x1100
-#define I2O_SCSI_DSC_DATA_OVERRUN 0x1200
-#define I2O_SCSI_DSC_UNEXPECTED_BUS_FREE 0x1300
-#define I2O_SCSI_DSC_SEQUENCE_FAILURE 0x1400
-#define I2O_SCSI_DSC_REQUEST_LENGTH_ERROR 0x1500
-#define I2O_SCSI_DSC_PROVIDE_FAILURE 0x1600
-#define I2O_SCSI_DSC_BDR_MESSAGE_SENT 0x1700
-#define I2O_SCSI_DSC_REQUEST_TERMINATED 0x1800
-#define I2O_SCSI_DSC_IDE_MESSAGE_SENT 0x3300
-#define I2O_SCSI_DSC_RESOURCE_UNAVAILABLE 0x3400
-#define I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT 0x3500
-#define I2O_SCSI_DSC_MESSAGE_RECEIVED 0x3600
-#define I2O_SCSI_DSC_INVALID_CDB 0x3700
-#define I2O_SCSI_DSC_LUN_INVALID 0x3800
-#define I2O_SCSI_DSC_SCSI_TID_INVALID 0x3900
-#define I2O_SCSI_DSC_FUNCTION_UNAVAILABLE 0x3A00
-#define I2O_SCSI_DSC_NO_NEXUS 0x3B00
-#define I2O_SCSI_DSC_SCSI_IID_INVALID 0x3C00
-#define I2O_SCSI_DSC_CDB_RECEIVED 0x3D00
-#define I2O_SCSI_DSC_LUN_ALREADY_ENABLED 0x3E00
-#define I2O_SCSI_DSC_BUS_BUSY 0x3F00
-#define I2O_SCSI_DSC_QUEUE_FROZEN 0x4000
-
-
-#ifndef TRUE
-#define TRUE 1
-#define FALSE 0
-#endif
-
-#define HBA_FLAGS_INSTALLED_B 0x00000001 // Adapter Was Installed
-#define HBA_FLAGS_BLINKLED_B 0x00000002 // Adapter In Blink LED State
-#define HBA_FLAGS_IN_RESET 0x00000040 /* in reset */
-#define HBA_HOSTRESET_FAILED 0x00000080 /* adpt_resethost failed */
-
-
-// Device state flags
-#define DPTI_DEV_ONLINE 0x00
-#define DPTI_DEV_UNSCANNED 0x01
-#define DPTI_DEV_RESET 0x02
-#define DPTI_DEV_OFFLINE 0x04
-
-
-struct adpt_device {
- struct adpt_device* next_lun;
- u32 flags;
- u32 type;
- u32 capacity;
- u32 block_size;
- u8 scsi_channel;
- u8 scsi_id;
- u8 scsi_lun;
- u8 state;
- u16 tid;
- struct i2o_device* pI2o_dev;
- struct scsi_device *pScsi_dev;
-};
-
-struct adpt_channel {
- struct adpt_device* device[MAX_ID]; /* used as an array of 128 scsi ids */
- u8 scsi_id;
- u8 type;
- u16 tid;
- u32 state;
- struct i2o_device* pI2o_dev;
-};
-
-// HBA state flags
-#define DPTI_STATE_RESET (0x01)
-#define DPTI_STATE_IOCTL (0x02)
-
-typedef struct _adpt_hba {
- struct _adpt_hba *next;
- struct pci_dev *pDev;
- struct Scsi_Host *host;
- u32 state;
- spinlock_t state_lock;
- int unit;
- int host_no; /* SCSI host number */
- u8 initialized;
- u8 in_use; /* is the management node open*/
-
- char name[32];
- char detail[55];
-
- void __iomem *base_addr_virt;
- void __iomem *msg_addr_virt;
- ulong base_addr_phys;
- void __iomem *post_port;
- void __iomem *reply_port;
- void __iomem *irq_mask;
- u16 post_count;
- u32 post_fifo_size;
- u32 reply_fifo_size;
- u32* reply_pool;
- dma_addr_t reply_pool_pa;
- u32 sg_tablesize; // Scatter/Gather List Size.
- u8 top_scsi_channel;
- u8 top_scsi_id;
- u8 top_scsi_lun;
- u8 dma64;
-
- i2o_status_block* status_block;
- dma_addr_t status_block_pa;
- i2o_hrt* hrt;
- dma_addr_t hrt_pa;
- i2o_lct* lct;
- dma_addr_t lct_pa;
- uint lct_size;
- struct i2o_device* devices;
- struct adpt_channel channel[MAX_CHANNEL];
- struct proc_dir_entry* proc_entry; /* /proc dir */
-
- void __iomem *FwDebugBuffer_P; // Virtual Address Of FW Debug Buffer
- u32 FwDebugBufferSize; // FW Debug Buffer Size In Bytes
- void __iomem *FwDebugStrLength_P;// Virtual Addr Of FW Debug String Len
- void __iomem *FwDebugFlags_P; // Virtual Address Of FW Debug Flags
- void __iomem *FwDebugBLEDflag_P;// Virtual Addr Of FW Debug BLED
- void __iomem *FwDebugBLEDvalue_P;// Virtual Addr Of FW Debug BLED
- u32 FwDebugFlags;
- u32 *ioctl_reply_context[4];
-} adpt_hba;
-
-struct sg_simple_element {
- u32 flag_count;
- u32 addr_bus;
-};
-
-/*
- * Function Prototypes
- */
-
-static void adpt_i2o_sys_shutdown(void);
-static int adpt_init(void);
-static int adpt_i2o_build_sys_table(void);
-static irqreturn_t adpt_isr(int irq, void *dev_id);
-
-static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d);
-static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid,
- int group, int field, void *buf, int buflen);
-#ifdef DEBUG
-static const char *adpt_i2o_get_class_name(int class);
-#endif
-static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid,
- void *opblk, dma_addr_t opblk_pa, int oplen,
- void *resblk, dma_addr_t resblk_pa, int reslen);
-static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout);
-static int adpt_i2o_lct_get(adpt_hba* pHba);
-static int adpt_i2o_parse_lct(adpt_hba* pHba);
-static int adpt_i2o_activate_hba(adpt_hba* pHba);
-static int adpt_i2o_enable_hba(adpt_hba* pHba);
-static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d);
-static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len);
-static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba);
-static s32 adpt_i2o_status_get(adpt_hba* pHba);
-static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba);
-static s32 adpt_i2o_hrt_get(adpt_hba* pHba);
-static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* dptdevice);
-static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd);
-static s32 adpt_scsi_host_alloc(adpt_hba* pHba,struct scsi_host_template * sht);
-static s32 adpt_hba_reset(adpt_hba* pHba);
-static s32 adpt_i2o_reset_hba(adpt_hba* pHba);
-static s32 adpt_rescan(adpt_hba* pHba);
-static s32 adpt_i2o_reparse_lct(adpt_hba* pHba);
-static s32 adpt_send_nop(adpt_hba*pHba,u32 m);
-static void adpt_i2o_delete_hba(adpt_hba* pHba);
-static void adpt_inquiry(adpt_hba* pHba);
-static void adpt_fail_posted_scbs(adpt_hba* pHba);
-static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun);
-static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev) ;
-static int adpt_i2o_online_hba(adpt_hba* pHba);
-static void adpt_i2o_post_wait_complete(u32, int);
-static int adpt_i2o_systab_send(adpt_hba* pHba);
-
-static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg);
-static int adpt_open(struct inode *inode, struct file *file);
-static int adpt_close(struct inode *inode, struct file *file);
-
-
-#ifdef UARTDELAY
-static void adpt_delay(int millisec);
-#endif
-
-#define PRINT_BUFFER_SIZE 512
-
-#define HBA_FLAGS_DBG_FLAGS_MASK 0xffff0000 // Mask for debug flags
-#define HBA_FLAGS_DBG_KERNEL_PRINT_B 0x00010000 // Kernel Debugger Print
-#define HBA_FLAGS_DBG_FW_PRINT_B 0x00020000 // Firmware Debugger Print
-#define HBA_FLAGS_DBG_FUNCTION_ENTRY_B 0x00040000 // Function Entry Point
-#define HBA_FLAGS_DBG_FUNCTION_EXIT_B 0x00080000 // Function Exit
-#define HBA_FLAGS_DBG_ERROR_B 0x00100000 // Error Conditions
-#define HBA_FLAGS_DBG_INIT_B 0x00200000 // Init Prints
-#define HBA_FLAGS_DBG_OS_COMMANDS_B 0x00400000 // OS Command Info
-#define HBA_FLAGS_DBG_SCAN_B 0x00800000 // Device Scan
-
-#define FW_DEBUG_STR_LENGTH_OFFSET 0
-#define FW_DEBUG_FLAGS_OFFSET 4
-#define FW_DEBUG_BLED_OFFSET 8
-
-#define FW_DEBUG_FLAGS_NO_HEADERS_B 0x01
-#endif /* !HOSTS_C */
-#endif /* _DPT_H */
diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c
deleted file mode 100644
index c2677ba29c74..000000000000
--- a/drivers/scsi/dtc.c
+++ /dev/null
@@ -1,486 +0,0 @@
-
-#define AUTOSENSE
-#define PSEUDO_DMA
-#define DONT_USE_INTR
-#define UNSAFE /* Leave interrupts enabled during pseudo-dma I/O */
-#define xNDEBUG (NDEBUG_INTR+NDEBUG_RESELECTION+\
- NDEBUG_SELECTION+NDEBUG_ARBITRATION)
-#define DMA_WORKS_RIGHT
-
-
-/*
- * DTC 3180/3280 driver, by
- * Ray Van Tassle rayvt@comm.mot.com
- *
- * taken from ...
- * Trantor T128/T128F/T228 driver by...
- *
- * Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 440-4894
- *
- * DISTRIBUTION RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
-*/
-
-/*
- * Options :
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
- * increase compared to polled I/O.
- *
- * PARITY - enable parity checking. Not supported.
- *
- * UNSAFE - leave interrupts enabled during pseudo-DMA transfers.
- * You probably want this.
- *
- * The card is detected and initialized in one of several ways :
- * 1. Autoprobe (default) - since the board is memory mapped,
- * a BIOS signature is scanned for to locate the registers.
- * An interrupt is triggered to autoprobe for the interrupt
- * line.
- *
- * 2. With command line overrides - dtc=address,irq may be
- * used on the LILO command line to override the defaults.
- *
-*/
-
-/*----------------------------------------------------------------*/
-/* the following will set the monitor border color (useful to find
- where something crashed or gets stuck at */
-/* 1 = blue
- 2 = green
- 3 = cyan
- 4 = red
- 5 = magenta
- 6 = yellow
- 7 = white
-*/
-#if 0
-#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
-#else
-#define rtrc(i) {}
-#endif
-
-
-#include <asm/system.h>
-#include <linux/module.h>
-#include <linux/signal.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "dtc.h"
-#define AUTOPROBE_IRQ
-#include "NCR5380.h"
-
-
-#define DTC_PUBLIC_RELEASE 2
-
-/*
- * The DTC3180 & 3280 boards are memory mapped.
- *
- */
-
-/*
- */
-/* Offset from DTC_5380_OFFSET */
-#define DTC_CONTROL_REG 0x100 /* rw */
-#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */
-#define CSR_DIR_READ 0x40 /* rw direction, 1 = read 0 = write */
-
-#define CSR_RESET 0x80 /* wo Resets 53c400 */
-#define CSR_5380_REG 0x80 /* ro 5380 registers can be accessed */
-#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
-#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */
-#define CSR_5380_INTR 0x10 /* rw Enable 5380 interrupts */
-#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */
-#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Host buffer not ready */
-#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer ready */
-#define CSR_GATED_5380_IRQ 0x01 /* ro Last block xferred */
-#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR)
-
-
-#define DTC_BLK_CNT 0x101 /* rw
- * # of 128-byte blocks to transfer */
-
-
-#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */
-
-#define DTC_SWITCH_REG 0x3982 /* ro - DIP switches */
-#define DTC_RESUME_XFER 0x3982 /* wo - resume data xfer
- * after disconnect/reconnect*/
-
-#define DTC_5380_OFFSET 0x3880 /* 8 registers here, see NCR5380.h */
-
-/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */
-#define DTC_DATA_BUF 0x3900 /* rw 128 bytes long */
-
-static struct override {
- unsigned int address;
- int irq;
-} overrides
-#ifdef OVERRIDE
-[] __initdata = OVERRIDE;
-#else
-[4] __initdata = {
- { 0, IRQ_AUTO }, { 0, IRQ_AUTO }, { 0, IRQ_AUTO }, { 0, IRQ_AUTO }
-};
-#endif
-
-#define NO_OVERRIDES ARRAY_SIZE(overrides)
-
-static struct base {
- unsigned long address;
- int noauto;
-} bases[] __initdata = {
- { 0xcc000, 0 },
- { 0xc8000, 0 },
- { 0xdc000, 0 },
- { 0xd8000, 0 }
-};
-
-#define NO_BASES ARRAY_SIZE(bases)
-
-static const struct signature {
- const char *string;
- int offset;
-} signatures[] = {
- {"DATA TECHNOLOGY CORPORATION BIOS", 0x25},
-};
-
-#define NO_SIGNATURES ARRAY_SIZE(signatures)
-
-#ifndef MODULE
-/*
- * Function : dtc_setup(char *str, int *ints)
- *
- * Purpose : LILO command line initialization of the overrides array,
- *
- * Inputs : str - unused, ints - array of integer parameters with ints[0]
- * equal to the number of ints.
- *
- */
-
-static void __init dtc_setup(char *str, int *ints)
-{
- static int commandline_current = 0;
- int i;
- if (ints[0] != 2)
- printk("dtc_setup: usage dtc=address,irq\n");
- else if (commandline_current < NO_OVERRIDES) {
- overrides[commandline_current].address = ints[1];
- overrides[commandline_current].irq = ints[2];
- for (i = 0; i < NO_BASES; ++i)
- if (bases[i].address == ints[1]) {
- bases[i].noauto = 1;
- break;
- }
- ++commandline_current;
- }
-}
-#endif
-
-/*
- * Function : int dtc_detect(struct scsi_host_template * tpnt)
- *
- * Purpose : detects and initializes DTC 3180/3280 controllers
- * that were autoprobed, overridden on the LILO command line,
- * or specified at compile time.
- *
- * Inputs : tpnt - template for this SCSI adapter.
- *
- * Returns : 1 if a host adapter was found, 0 if not.
- *
-*/
-
-static int __init dtc_detect(struct scsi_host_template * tpnt)
-{
- static int current_override = 0, current_base = 0;
- struct Scsi_Host *instance;
- unsigned int addr;
- void __iomem *base;
- int sig, count;
-
- tpnt->proc_name = "dtc3x80";
- tpnt->proc_info = &dtc_proc_info;
-
- for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
- addr = 0;
- base = NULL;
-
- if (overrides[current_override].address) {
- addr = overrides[current_override].address;
- base = ioremap(addr, 0x2000);
- if (!base)
- addr = 0;
- } else
- for (; !addr && (current_base < NO_BASES); ++current_base) {
-#if (DTCDEBUG & DTCDEBUG_INIT)
- printk(KERN_DEBUG "scsi-dtc : probing address %08x\n", bases[current_base].address);
-#endif
- if (bases[current_base].noauto)
- continue;
- base = ioremap(bases[current_base].address, 0x2000);
- if (!base)
- continue;
- for (sig = 0; sig < NO_SIGNATURES; ++sig) {
- if (check_signature(base + signatures[sig].offset, signatures[sig].string, strlen(signatures[sig].string))) {
- addr = bases[current_base].address;
-#if (DTCDEBUG & DTCDEBUG_INIT)
- printk(KERN_DEBUG "scsi-dtc : detected board.\n");
-#endif
- goto found;
- }
- }
- iounmap(base);
- }
-
-#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
- printk(KERN_DEBUG "scsi-dtc : base = %08x\n", addr);
-#endif
-
- if (!addr)
- break;
-
-found:
- instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
- if (instance == NULL)
- break;
-
- instance->base = addr;
- ((struct NCR5380_hostdata *)(instance)->hostdata)->base = base;
-
- NCR5380_init(instance, 0);
-
- NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); /* Enable int's */
- if (overrides[current_override].irq != IRQ_AUTO)
- instance->irq = overrides[current_override].irq;
- else
- instance->irq = NCR5380_probe_irq(instance, DTC_IRQS);
-
-#ifndef DONT_USE_INTR
- /* With interrupts enabled, it will sometimes hang when doing heavy
- * reads. So better not enable them until I finger it out. */
- if (instance->irq != SCSI_IRQ_NONE)
- if (request_irq(instance->irq, dtc_intr, IRQF_DISABLED,
- "dtc", instance)) {
- printk(KERN_ERR "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
- }
-
- if (instance->irq == SCSI_IRQ_NONE) {
- printk(KERN_WARNING "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
- printk(KERN_WARNING "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
- }
-#else
- if (instance->irq != SCSI_IRQ_NONE)
- printk(KERN_WARNING "scsi%d : interrupts not used. Might as well not jumper it.\n", instance->host_no);
- instance->irq = SCSI_IRQ_NONE;
-#endif
-#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
- printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
-#endif
-
- printk(KERN_INFO "scsi%d : at 0x%05X", instance->host_no, (int) instance->base);
- if (instance->irq == SCSI_IRQ_NONE)
- printk(" interrupts disabled");
- else
- printk(" irq %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, DTC_PUBLIC_RELEASE);
- NCR5380_print_options(instance);
- printk("\n");
-
- ++current_override;
- ++count;
- }
- return count;
-}
-
-/*
- * Function : int dtc_biosparam(Disk * disk, struct block_device *dev, int *ip)
- *
- * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
- * the specified device / size.
- *
- * Inputs : size = size of device in sectors (512 bytes), dev = block device
- * major / minor, ip[] = {heads, sectors, cylinders}
- *
- * Returns : always 0 (success), initializes ip
- *
-*/
-
-/*
- * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
- * using hard disks on a trantor should verify that this mapping corresponds
- * to that used by the BIOS / ASPI driver by running the linux fdisk program
- * and matching the H_C_S coordinates to what DOS uses.
-*/
-
-static int dtc_biosparam(struct scsi_device *sdev, struct block_device *dev,
- sector_t capacity, int *ip)
-{
- int size = capacity;
-
- ip[0] = 64;
- ip[1] = 32;
- ip[2] = size >> 11;
- return 0;
-}
-
-
-/****************************************************************
- * Function : int NCR5380_pread (struct Scsi_Host *instance,
- * unsigned char *dst, int len)
- *
- * Purpose : Fast 5380 pseudo-dma read function, reads len bytes to
- * dst
- *
- * Inputs : dst = destination, len = length in bytes
- *
- * Returns : 0 on success, non zero on a failure such as a watchdog
- * timeout.
-*/
-
-static int dtc_maxi = 0;
-static int dtc_wmaxi = 0;
-
-static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, int len)
-{
- unsigned char *d = dst;
- int i; /* For counting time spent in the poll-loop */
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- i = 0;
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
- if (instance->irq == SCSI_IRQ_NONE)
- NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ);
- else
- NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE);
- NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */
- rtrc(1);
- while (len > 0) {
- rtrc(2);
- while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
- ++i;
- rtrc(3);
- memcpy_fromio(d, base + DTC_DATA_BUF, 128);
- d += 128;
- len -= 128;
- rtrc(7);
- /*** with int's on, it sometimes hangs after here.
- * Looks like something makes HBNR go away. */
- }
- rtrc(4);
- while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
- ++i;
- NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
- rtrc(0);
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- if (i > dtc_maxi)
- dtc_maxi = i;
- return (0);
-}
-
-/****************************************************************
- * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
- * unsigned char *src, int len)
- *
- * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
- * src
- *
- * Inputs : src = source, len = length in bytes
- *
- * Returns : 0 on success, non zero on a failure such as a watchdog
- * timeout.
-*/
-
-static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, int len)
-{
- int i;
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
- /* set direction (write) */
- if (instance->irq == SCSI_IRQ_NONE)
- NCR5380_write(DTC_CONTROL_REG, 0);
- else
- NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR);
- NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */
- for (i = 0; len > 0; ++i) {
- rtrc(5);
- /* Poll until the host buffer can accept data. */
- while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
- ++i;
- rtrc(3);
- memcpy_toio(base + DTC_DATA_BUF, src, 128);
- src += 128;
- len -= 128;
- }
- rtrc(4);
- while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
- ++i;
- rtrc(6);
- /* Wait until the last byte has been sent to the disk */
- while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
- ++i;
- rtrc(7);
- /* Check for parity error here. fixme. */
- NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
- rtrc(0);
- if (i > dtc_wmaxi)
- dtc_wmaxi = i;
- return (0);
-}
-
-MODULE_LICENSE("GPL");
-
-#include "NCR5380.c"
-
-static int dtc_release(struct Scsi_Host *shost)
-{
- NCR5380_local_declare();
- NCR5380_setup(shost);
- if (shost->irq)
- free_irq(shost->irq, shost);
- NCR5380_exit(shost);
- if (shost->io_port && shost->n_io_port)
- release_region(shost->io_port, shost->n_io_port);
- scsi_unregister(shost);
- iounmap(base);
- return 0;
-}
-
-static struct scsi_host_template driver_template = {
- .name = "DTC 3180/3280 ",
- .detect = dtc_detect,
- .release = dtc_release,
- .queuecommand = dtc_queue_command,
- .eh_abort_handler = dtc_abort,
- .eh_bus_reset_handler = dtc_bus_reset,
- .bios_param = dtc_biosparam,
- .can_queue = CAN_QUEUE,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = CMD_PER_LUN,
- .use_clustering = DISABLE_CLUSTERING,
-};
-#include "scsi_module.c"
diff --git a/drivers/scsi/dtc.h b/drivers/scsi/dtc.h
deleted file mode 100644
index cdc621204b66..000000000000
--- a/drivers/scsi/dtc.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * DTC controller, taken from T128 driver by...
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 440-4894
- *
- * DISTRIBUTION RELEASE 2.
- *
- * For more information, please consult
- *
- *
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-#ifndef DTC3280_H
-#define DTC3280_H
-
-#define DTCDEBUG 0
-#define DTCDEBUG_INIT 0x1
-#define DTCDEBUG_TRANSFER 0x2
-
-static int dtc_abort(Scsi_Cmnd *);
-static int dtc_biosparam(struct scsi_device *, struct block_device *,
- sector_t, int*);
-static int dtc_detect(struct scsi_host_template *);
-static int dtc_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int dtc_bus_reset(Scsi_Cmnd *);
-
-#ifndef CMD_PER_LUN
-#define CMD_PER_LUN 2
-#endif
-
-#ifndef CAN_QUEUE
-#define CAN_QUEUE 32
-#endif
-
-#define NCR5380_implementation_fields \
- void __iomem *base
-
-#define NCR5380_local_declare() \
- void __iomem *base
-
-#define NCR5380_setup(instance) \
- base = ((struct NCR5380_hostdata *)(instance)->hostdata)->base
-
-#define DTC_address(reg) (base + DTC_5380_OFFSET + reg)
-
-#define dbNCR5380_read(reg) \
- (rval=readb(DTC_address(reg)), \
- (((unsigned char) printk("DTC : read register %d at addr %p is: %02x\n"\
- , (reg), DTC_address(reg), rval)), rval ) )
-
-#define dbNCR5380_write(reg, value) do { \
- printk("DTC : write %02x to register %d at address %p\n", \
- (value), (reg), DTC_address(reg)); \
- writeb(value, DTC_address(reg));} while(0)
-
-
-#if !(DTCDEBUG & DTCDEBUG_TRANSFER)
-#define NCR5380_read(reg) (readb(DTC_address(reg)))
-#define NCR5380_write(reg, value) (writeb(value, DTC_address(reg)))
-#else
-#define NCR5380_read(reg) (readb(DTC_address(reg)))
-#define xNCR5380_read(reg) \
- (((unsigned char) printk("DTC : read register %d at address %p\n"\
- , (reg), DTC_address(reg))), readb(DTC_address(reg)))
-
-#define NCR5380_write(reg, value) do { \
- printk("DTC : write %02x to register %d at address %p\n", \
- (value), (reg), DTC_address(reg)); \
- writeb(value, DTC_address(reg));} while(0)
-#endif
-
-#define NCR5380_intr dtc_intr
-#define NCR5380_queue_command dtc_queue_command
-#define NCR5380_abort dtc_abort
-#define NCR5380_bus_reset dtc_bus_reset
-#define NCR5380_proc_info dtc_proc_info
-
-/* 15 12 11 10
- 1001 1100 0000 0000 */
-
-#define DTC_IRQS 0x9c00
-
-
-#endif /* DTC3280_H */
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
deleted file mode 100644
index 94de88955a99..000000000000
--- a/drivers/scsi/eata.c
+++ /dev/null
@@ -1,2590 +0,0 @@
-/*
- * eata.c - Low-level driver for EATA/DMA SCSI host adapters.
- *
- * 03 Jun 2003 Rev. 8.10 for linux-2.5.70
- * + Update for new IRQ API.
- * + Use "goto" when appropriate.
- * + Drop eata.h.
- * + Update for new module_param API.
- * + Module parameters can now be specified only in the
- * same format as the kernel boot options.
- *
- * boot option old module param
- * ----------- ------------------
- * addr,... io_port=addr,...
- * lc:[y|n] linked_comm=[1|0]
- * mq:xx max_queue_depth=xx
- * tm:[0|1|2] tag_mode=[0|1|2]
- * et:[y|n] ext_tran=[1|0]
- * rs:[y|n] rev_scan=[1|0]
- * ip:[y|n] isa_probe=[1|0]
- * ep:[y|n] eisa_probe=[1|0]
- * pp:[y|n] pci_probe=[1|0]
- *
- * A valid example using the new parameter format is:
- * modprobe eata "eata=0x7410,0x230,lc:y,tm:0,mq:4,ep:n"
- *
- * which is equivalent to the old format:
- * modprobe eata io_port=0x7410,0x230 linked_comm=1 tag_mode=0 \
- * max_queue_depth=4 eisa_probe=0
- *
- * 12 Feb 2003 Rev. 8.04 for linux 2.5.60
- * + Release irq before calling scsi_register.
- *
- * 12 Nov 2002 Rev. 8.02 for linux 2.5.47
- * + Release driver_lock before calling scsi_register.
- *
- * 11 Nov 2002 Rev. 8.01 for linux 2.5.47
- * + Fixed bios_param and scsicam_bios_param calling parameters.
- *
- * 28 Oct 2002 Rev. 8.00 for linux 2.5.44-ac4
- * + Use new tcq and adjust_queue_depth api.
- * + New command line option (tm:[0-2]) to choose the type of tags:
- * 0 -> disable tagging ; 1 -> simple tags ; 2 -> ordered tags.
- * Default is tm:0 (tagged commands disabled).
- * For compatibility the "tc:" option is an alias of the "tm:"
- * option; tc:n is equivalent to tm:0 and tc:y is equivalent to
- * tm:1.
- * + The tagged_comm module parameter has been removed, use tag_mode
- * instead, equivalent to the "tm:" boot option.
- *
- * 10 Oct 2002 Rev. 7.70 for linux 2.5.42
- * + Foreport from revision 6.70.
- *
- * 25 Jun 2002 Rev. 6.70 for linux 2.4.19
- * + This release is the first one tested on a Big Endian platform:
- * fixed endian-ness problem due to bitfields;
- * fixed endian-ness problem in read_pio.
- * + Added new options for selectively probing ISA, EISA and PCI bus:
- *
- * Boot option Parameter name Default according to
- *
- * ip:[y|n] isa_probe=[1|0] CONFIG_ISA defined
- * ep:[y|n] eisa_probe=[1|0] CONFIG_EISA defined
- * pp:[y|n] pci_probe=[1|0] CONFIG_PCI defined
- *
- * The default action is to perform probing if the corresponding
- * bus is configured and to skip probing otherwise.
- *
- * + If pci_probe is in effect and a list of I/O ports is specified
- * as parameter or boot option, pci_enable_device() is performed
- * on all pci devices matching PCI_CLASS_STORAGE_SCSI.
- *
- * 21 Feb 2002 Rev. 6.52 for linux 2.4.18
- * + Backport from rev. 7.22 (use io_request_lock).
- *
- * 20 Feb 2002 Rev. 7.22 for linux 2.5.5
- * + Remove any reference to virt_to_bus().
- * + Fix pio hang while detecting multiple HBAs.
- * + Fixed a board detection bug: in a system with
- * multiple ISA/EISA boards, all but the first one
- * were erroneously detected as PCI.
- *
- * 01 Jan 2002 Rev. 7.20 for linux 2.5.1
- * + Use the dynamic DMA mapping API.
- *
- * 19 Dec 2001 Rev. 7.02 for linux 2.5.1
- * + Use SCpnt->sc_data_direction if set.
- * + Use sglist.page instead of sglist.address.
- *
- * 11 Dec 2001 Rev. 7.00 for linux 2.5.1
- * + Use host->host_lock instead of io_request_lock.
- *
- * 1 May 2001 Rev. 6.05 for linux 2.4.4
- * + Clean up all pci related routines.
- * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d)
- *
- * 30 Jan 2001 Rev. 6.04 for linux 2.4.1
- * + Call pci_resource_start after pci_enable_device.
- *
- * 25 Jan 2001 Rev. 6.03 for linux 2.4.0
- * + "check_region" call replaced by "request_region".
- *
- * 22 Nov 2000 Rev. 6.02 for linux 2.4.0-test11
- * + Return code checked when calling pci_enable_device.
- * + Removed old scsi error handling support.
- * + The obsolete boot option flag eh:n is silently ignored.
- * + Removed error messages while a disk drive is powered up at
- * boot time.
- * + Improved boot messages: all tagged capable device are
- * indicated as "tagged" or "soft-tagged" :
- * - "soft-tagged" means that the driver is trying to do its
- * own tagging (i.e. the tc:y option is in effect);
- * - "tagged" means that the device supports tagged commands,
- * but the driver lets the HBA be responsible for tagging
- * support.
- *
- * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
- * + Updated to the new __setup interface for boot command line options.
- * + When loaded as a module, accepts the new parameter boot_options
- * which value is a string with the same format of the kernel boot
- * command line options. A valid example is:
- * modprobe eata 'boot_options="0x7410,0x230,lc:y,tc:n,mq:4"'
- *
- * 9 Sep 1999 Rev. 5.10 for linux 2.2.12 and 2.3.17
- * + 64bit cleanup for Linux/Alpha platform support
- * (contribution from H.J. Lu).
- *
- * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11
- * + Removed pre-2.2 source code compatibility.
- * + Added call to pci_set_master.
- *
- * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
- * + Added command line option (rs:[y|n]) to reverse the scan order
- * of PCI boards. The default is rs:y, which reverses the BIOS order
- * while registering PCI boards. The default value rs:y generates
- * the same order of all previous revisions of this driver.
- * Pls. note that "BIOS order" might have been reversed itself
- * after the 2.1.9x PCI modifications in the linux kernel.
- * The rs value is ignored when the explicit list of addresses
- * is used by the "eata=port0,port1,..." command line option.
- * + Added command line option (et:[y|n]) to force use of extended
- * translation (255 heads, 63 sectors) as disk geometry.
- * The default is et:n, which uses the disk geometry returned
- * by scsicam_bios_param. The default value et:n is compatible with
- * all previous revisions of this driver.
- *
- * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
- * Increased busy timeout from 10 msec. to 200 msec. while
- * processing interrupts.
- *
- * 16 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
- * Improved abort handling during the eh recovery process.
- *
- * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101
- * The driver is now fully SMP safe, including the
- * abort and reset routines.
- * Added command line options (eh:[y|n]) to choose between
- * new_eh_code and the old scsi code.
- * If linux version >= 2.1.101 the default is eh:y, while the eh
- * option is ignored for previous releases and the old scsi code
- * is used.
- *
- * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97
- * Reworked interrupt handler.
- *
- * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95
- * Major reliability improvement: when a batch with overlapping
- * requests is detected, requests are queued one at a time
- * eliminating any possible board or drive reordering.
- *
- * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95
- * Improved SMP support (if linux version >= 2.1.95).
- *
- * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94
- * Added support for new PCI code and IO-APIC remapping of irqs.
- * Performance improvement: when sequential i/o is detected,
- * always use direct sort instead of reverse sort.
- *
- * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
- * io_port is now unsigned long.
- *
- * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
- * Use new scsi error handling code (if linux version >= 2.1.88).
- * Use new interrupt code.
- *
- * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
- * Use of udelay inside the wait loops to avoid timeout
- * problems with fast cpus.
- * Removed check about useless calls to the interrupt service
- * routine (reported on SMP systems only).
- * At initialization time "sorted/unsorted" is displayed instead
- * of "linked/unlinked" to reinforce the fact that "linking" is
- * nothing but "elevator sorting" in the actual implementation.
- *
- * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
- * Use of serial_number_at_timeout in abort and reset processing.
- * Use of the __initfunc and __initdata macro in setup code.
- * Minor cleanups in the list_statistics code.
- * Increased controller busy timeout in order to better support
- * slow SCSI devices.
- *
- * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
- * When loading as a module, parameter passing is now supported
- * both in 2.0 and in 2.1 style.
- * Fixed data transfer direction for some SCSI opcodes.
- * Immediate acknowledge to request sense commands.
- * Linked commands to each disk device are now reordered by elevator
- * sorting. Rare cases in which reordering of write requests could
- * cause wrong results are managed.
- * Fixed spurious timeouts caused by long simple queue tag sequences.
- * New command line option (tm:[0-3]) to choose the type of tags:
- * 0 -> mixed (default); 1 -> simple; 2 -> head; 3 -> ordered.
- *
- * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
- * Added command line options to enable/disable linked commands
- * (lc:[y|n]), tagged commands (tc:[y|n]) and to set the max queue
- * depth (mq:xx). Default is "eata=lc:n,tc:n,mq:16".
- * Improved command linking.
- * Documented how to setup RAID-0 with DPT SmartRAID boards.
- *
- * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
- * Added linked command support.
- * Improved detection of PCI boards using ISA base addresses.
- *
- * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
- * Added support for tagged commands and queue depth adjustment.
- *
- * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
- * When CONFIG_PCI is defined, BIOS32 is used to include in the
- * list of i/o ports to be probed all the PCI SCSI controllers.
- * The list of i/o ports to be probed can be overwritten by the
- * "eata=port0,port1,...." boot command line option.
- * Scatter/gather lists are now allocated by a number of kmalloc
- * calls, in order to avoid the previous size limit of 64Kb.
- *
- * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25
- * Added support for EATA 2.0C, PCI, multichannel and wide SCSI.
- *
- * 27 Sep 1996 rev. 2.12 for linux 2.1.0
- * Portability cleanups (virtual/bus addressing, little/big endian
- * support).
- *
- * 09 Jul 1996 rev. 2.11 for linux 2.0.4
- * Number of internal retries is now limited.
- *
- * 16 Apr 1996 rev. 2.10 for linux 1.3.90
- * New argument "reset_flags" to the reset routine.
- *
- * 6 Jul 1995 rev. 2.01 for linux 1.3.7
- * Update required by the new /proc/scsi support.
- *
- * 11 Mar 1995 rev. 2.00 for linux 1.2.0
- * Fixed a bug which prevented media change detection for removable
- * disk drives.
- *
- * 23 Feb 1995 rev. 1.18 for linux 1.1.94
- * Added a check for scsi_register returning NULL.
- *
- * 11 Feb 1995 rev. 1.17 for linux 1.1.91
- * Now DEBUG_RESET is disabled by default.
- * Register a board even if it does not assert DMA protocol support
- * (DPT SK2011B does not report correctly the dmasup bit).
- *
- * 9 Feb 1995 rev. 1.16 for linux 1.1.90
- * Use host->wish_block instead of host->block.
- * New list of Data Out SCSI commands.
- *
- * 8 Feb 1995 rev. 1.15 for linux 1.1.89
- * Cleared target_time_out counter while performing a reset.
- * All external symbols renamed to avoid possible name conflicts.
- *
- * 28 Jan 1995 rev. 1.14 for linux 1.1.86
- * Added module support.
- * Log and do a retry when a disk drive returns a target status
- * different from zero on a recovered error.
- *
- * 24 Jan 1995 rev. 1.13 for linux 1.1.85
- * Use optimized board configuration, with a measured performance
- * increase in the range 10%-20% on i/o throughput.
- *
- * 16 Jan 1995 rev. 1.12 for linux 1.1.81
- * Fix mscp structure comments (no functional change).
- * Display a message if check_region detects a port address
- * already in use.
- *
- * 17 Dec 1994 rev. 1.11 for linux 1.1.74
- * Use the scsicam_bios_param routine. This allows an easy
- * migration path from disk partition tables created using
- * different SCSI drivers and non optimal disk geometry.
- *
- * 15 Dec 1994 rev. 1.10 for linux 1.1.74
- * Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
- * The host->block flag is set for all the detected ISA boards.
- * The detect routine no longer enforces LEVEL triggering
- * for EISA boards, it just prints a warning message.
- *
- * 30 Nov 1994 rev. 1.09 for linux 1.1.68
- * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
- * Added optional support for using a single board at a time.
- *
- * 18 Nov 1994 rev. 1.08 for linux 1.1.64
- * Forces sg_tablesize = 64 and can_queue = 64 if these
- * values are not correctly detected (DPT PM2012).
- *
- * 14 Nov 1994 rev. 1.07 for linux 1.1.63 Final BETA release.
- * 04 Aug 1994 rev. 1.00 for linux 1.1.39 First BETA release.
- *
- *
- * This driver is based on the CAM (Common Access Method Committee)
- * EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
- *
- * Copyright (C) 1994-2003 Dario Ballabio (ballabio_dario@emc.com)
- *
- * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that redistributions of source
- * code retain the above copyright notice and this comment without
- * modification.
- *
- */
-
-/*
- *
- * Here is a brief description of the DPT SCSI host adapters.
- * All these boards provide an EATA/DMA compatible programming interface
- * and are fully supported by this driver in any configuration, including
- * multiple SCSI channels:
- *
- * PM2011B/9X - Entry Level ISA
- * PM2021A/9X - High Performance ISA
- * PM2012A Old EISA
- * PM2012B Old EISA
- * PM2022A/9X - Entry Level EISA
- * PM2122A/9X - High Performance EISA
- * PM2322A/9X - Extra High Performance EISA
- * PM3021 - SmartRAID Adapter for ISA
- * PM3222 - SmartRAID Adapter for EISA (PM3222W is 16-bit wide SCSI)
- * PM3224 - SmartRAID Adapter for PCI (PM3224W is 16-bit wide SCSI)
- * PM33340UW - SmartRAID Adapter for PCI ultra wide multichannel
- *
- * The above list is just an indication: as a matter of fact all DPT
- * boards using the EATA/DMA protocol are supported by this driver,
- * since they use exactely the same programming interface.
- *
- * The DPT PM2001 provides only the EATA/PIO interface and hence is not
- * supported by this driver.
- *
- * This code has been tested with up to 3 Distributed Processing Technology
- * PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) EISA controllers,
- * in any combination of private and shared IRQ.
- * PCI support has been tested using up to 2 DPT PM3224W (DPT SCSI BIOS
- * v003.D0, firmware v07G.0).
- *
- * DPT SmartRAID boards support "Hardware Array" - a group of disk drives
- * which are all members of the same RAID-0, RAID-1 or RAID-5 array implemented
- * in host adapter hardware. Hardware Arrays are fully compatible with this
- * driver, since they look to it as a single disk drive.
- *
- * WARNING: to create a RAID-0 "Hardware Array" you must select "Other Unix"
- * as the current OS in the DPTMGR "Initial System Installation" menu.
- * Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0),
- * which is not supported by the actual SCSI subsystem.
- * To get the "Array Group" functionality, the Linux MD driver must be used
- * instead of the DPT "Array Group" feature.
- *
- * Multiple ISA, EISA and PCI boards can be configured in the same system.
- * It is suggested to put all the EISA boards on the same IRQ level, all
- * the PCI boards on another IRQ level, while ISA boards cannot share
- * interrupts.
- *
- * If you configure multiple boards on the same IRQ, the interrupt must
- * be _level_ triggered (not _edge_ triggered).
- *
- * This driver detects EATA boards by probes at fixed port addresses,
- * so no BIOS32 or PCI BIOS support is required.
- * The suggested way to detect a generic EATA PCI board is to force on it
- * any unused EISA address, even if there are other controllers on the EISA
- * bus, or even if you system has no EISA bus at all.
- * Do not force any ISA address on EATA PCI boards.
- *
- * If PCI bios support is configured into the kernel, BIOS32 is used to
- * include in the list of i/o ports to be probed all the PCI SCSI controllers.
- *
- * Due to a DPT BIOS "feature", it might not be possible to force an EISA
- * address on more than a single DPT PCI board, so in this case you have to
- * let the PCI BIOS assign the addresses.
- *
- * The sequence of detection probes is:
- *
- * - ISA 0x1F0;
- * - PCI SCSI controllers (only if BIOS32 is available);
- * - EISA/PCI 0x1C88 through 0xFC88 (corresponding to EISA slots 1 to 15);
- * - ISA 0x170, 0x230, 0x330.
- *
- * The above list of detection probes can be totally replaced by the
- * boot command line option: "eata=port0,port1,port2,...", where the
- * port0, port1... arguments are ISA/EISA/PCI addresses to be probed.
- * For example using "eata=0x7410,0x7450,0x230", the driver probes
- * only the two PCI addresses 0x7410 and 0x7450 and the ISA address 0x230,
- * in this order; "eata=0" totally disables this driver.
- *
- * After the optional list of detection probes, other possible command line
- * options are:
- *
- * et:y force use of extended translation (255 heads, 63 sectors);
- * et:n use disk geometry detected by scsicam_bios_param;
- * rs:y reverse scan order while detecting PCI boards;
- * rs:n use BIOS order while detecting PCI boards;
- * lc:y enables linked commands;
- * lc:n disables linked commands;
- * tm:0 disables tagged commands (same as tc:n);
- * tm:1 use simple queue tags (same as tc:y);
- * tm:2 use ordered queue tags (same as tc:2);
- * mq:xx set the max queue depth to the value xx (2 <= xx <= 32).
- *
- * The default value is: "eata=lc:n,mq:16,tm:0,et:n,rs:n".
- * An example using the list of detection probes could be:
- * "eata=0x7410,0x230,lc:y,tm:2,mq:4,et:n".
- *
- * When loading as a module, parameters can be specified as well.
- * The above example would be (use 1 in place of y and 0 in place of n):
- *
- * modprobe eata io_port=0x7410,0x230 linked_comm=1 \
- * max_queue_depth=4 ext_tran=0 tag_mode=2 \
- * rev_scan=1
- *
- * ----------------------------------------------------------------------------
- * In this implementation, linked commands are designed to work with any DISK
- * or CD-ROM, since this linking has only the intent of clustering (time-wise)
- * and reordering by elevator sorting commands directed to each device,
- * without any relation with the actual SCSI protocol between the controller
- * and the device.
- * If Q is the queue depth reported at boot time for each device (also named
- * cmds/lun) and Q > 2, whenever there is already an active command to the
- * device all other commands to the same device (up to Q-1) are kept waiting
- * in the elevator sorting queue. When the active command completes, the
- * commands in this queue are sorted by sector address. The sort is chosen
- * between increasing or decreasing by minimizing the seek distance between
- * the sector of the commands just completed and the sector of the first
- * command in the list to be sorted.
- * Trivial math assures that the unsorted average seek distance when doing
- * random seeks over S sectors is S/3.
- * When (Q-1) requests are uniformly distributed over S sectors, the average
- * distance between two adjacent requests is S/((Q-1) + 1), so the sorted
- * average seek distance for (Q-1) random requests over S sectors is S/Q.
- * The elevator sorting hence divides the seek distance by a factor Q/3.
- * The above pure geometric remarks are valid in all cases and the
- * driver effectively reduces the seek distance by the predicted factor
- * when there are Q concurrent read i/o operations on the device, but this
- * does not necessarily results in a noticeable performance improvement:
- * your mileage may vary....
- *
- * Note: command reordering inside a batch of queued commands could cause
- * wrong results only if there is at least one write request and the
- * intersection (sector-wise) of all requests is not empty.
- * When the driver detects a batch including overlapping requests
- * (a really rare event) strict serial (pid) order is enforced.
- * ----------------------------------------------------------------------------
- * The extended translation option (et:y) is useful when using large physical
- * disks/arrays. It could also be useful when switching between Adaptec boards
- * and DPT boards without reformatting the disk.
- * When a boot disk is partitioned with extended translation, in order to
- * be able to boot it with a DPT board is could be necessary to add to
- * lilo.conf additional commands as in the following example:
- *
- * fix-table
- * disk=/dev/sda bios=0x80 sectors=63 heads=128 cylindres=546
- *
- * where the above geometry should be replaced with the one reported at
- * power up by the DPT controller.
- * ----------------------------------------------------------------------------
- *
- * The boards are named EATA0, EATA1,... according to the detection order.
- *
- * In order to support multiple ISA boards in a reliable way,
- * the driver sets host->wish_block = 1 for all ISA boards.
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/blkdev.h>
-#include <linux/interrupt.h>
-#include <linux/stat.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ctype.h>
-#include <linux/spinlock.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <asm/byteorder.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsicam.h>
-
-static int eata2x_detect(struct scsi_host_template *);
-static int eata2x_release(struct Scsi_Host *);
-static int eata2x_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
-static int eata2x_eh_abort(struct scsi_cmnd *);
-static int eata2x_eh_host_reset(struct scsi_cmnd *);
-static int eata2x_bios_param(struct scsi_device *, struct block_device *,
- sector_t, int *);
-static int eata2x_slave_configure(struct scsi_device *);
-
-static struct scsi_host_template driver_template = {
- .name = "EATA/DMA 2.0x rev. 8.10.00 ",
- .detect = eata2x_detect,
- .release = eata2x_release,
- .queuecommand = eata2x_queuecommand,
- .eh_abort_handler = eata2x_eh_abort,
- .eh_host_reset_handler = eata2x_eh_host_reset,
- .bios_param = eata2x_bios_param,
- .slave_configure = eata2x_slave_configure,
- .this_id = 7,
- .unchecked_isa_dma = 1,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-#if !defined(__BIG_ENDIAN_BITFIELD) && !defined(__LITTLE_ENDIAN_BITFIELD)
-#error "Adjust your <asm/byteorder.h> defines"
-#endif
-
-/* Subversion values */
-#define ISA 0
-#define ESA 1
-
-#undef FORCE_CONFIG
-
-#undef DEBUG_LINKED_COMMANDS
-#undef DEBUG_DETECT
-#undef DEBUG_PCI_DETECT
-#undef DEBUG_INTERRUPT
-#undef DEBUG_RESET
-#undef DEBUG_GENERATE_ERRORS
-#undef DEBUG_GENERATE_ABORTS
-#undef DEBUG_GEOMETRY
-
-#define MAX_ISA 4
-#define MAX_VESA 0
-#define MAX_EISA 15
-#define MAX_PCI 16
-#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI)
-#define MAX_CHANNEL 4
-#define MAX_LUN 32
-#define MAX_TARGET 32
-#define MAX_MAILBOXES 64
-#define MAX_SGLIST 64
-#define MAX_LARGE_SGLIST 122
-#define MAX_INTERNAL_RETRIES 64
-#define MAX_CMD_PER_LUN 2
-#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
-
-#define SKIP ULONG_MAX
-#define FREE 0
-#define IN_USE 1
-#define LOCKED 2
-#define IN_RESET 3
-#define IGNORE 4
-#define READY 5
-#define ABORTING 6
-#define NO_DMA 0xff
-#define MAXLOOP 10000
-#define TAG_DISABLED 0
-#define TAG_SIMPLE 1
-#define TAG_ORDERED 2
-
-#define REG_CMD 7
-#define REG_STATUS 7
-#define REG_AUX_STATUS 8
-#define REG_DATA 0
-#define REG_DATA2 1
-#define REG_SEE 6
-#define REG_LOW 2
-#define REG_LM 3
-#define REG_MID 4
-#define REG_MSB 5
-#define REGION_SIZE 9UL
-#define MAX_ISA_ADDR 0x03ff
-#define MIN_EISA_ADDR 0x1c88
-#define MAX_EISA_ADDR 0xfc88
-#define BSY_ASSERTED 0x80
-#define DRQ_ASSERTED 0x08
-#define ABSY_ASSERTED 0x01
-#define IRQ_ASSERTED 0x02
-#define READ_CONFIG_PIO 0xf0
-#define SET_CONFIG_PIO 0xf1
-#define SEND_CP_PIO 0xf2
-#define RECEIVE_SP_PIO 0xf3
-#define TRUNCATE_XFR_PIO 0xf4
-#define RESET_PIO 0xf9
-#define READ_CONFIG_DMA 0xfd
-#define SET_CONFIG_DMA 0xfe
-#define SEND_CP_DMA 0xff
-#define ASOK 0x00
-#define ASST 0x01
-
-#define YESNO(a) ((a) ? 'y' : 'n')
-#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
-
-/* "EATA", in Big Endian format */
-#define EATA_SIG_BE 0x45415441
-
-/* Number of valid bytes in the board config structure for EATA 2.0x */
-#define EATA_2_0A_SIZE 28
-#define EATA_2_0B_SIZE 30
-#define EATA_2_0C_SIZE 34
-
-/* Board info structure */
-struct eata_info {
- u_int32_t data_len; /* Number of valid bytes after this field */
- u_int32_t sign; /* ASCII "EATA" signature */
-
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar version : 4,
- : 4;
- unchar haaval : 1,
- ata : 1,
- drqvld : 1,
- dmasup : 1,
- morsup : 1,
- trnxfr : 1,
- tarsup : 1,
- ocsena : 1;
-#else
- unchar : 4, /* unused low nibble */
- version : 4; /* EATA version, should be 0x1 */
- unchar ocsena : 1, /* Overlap Command Support Enabled */
- tarsup : 1, /* Target Mode Supported */
- trnxfr : 1, /* Truncate Transfer Cmd NOT Necessary */
- morsup : 1, /* More Supported */
- dmasup : 1, /* DMA Supported */
- drqvld : 1, /* DRQ Index (DRQX) is valid */
- ata : 1, /* This is an ATA device */
- haaval : 1; /* Host Adapter Address Valid */
-#endif
-
- ushort cp_pad_len; /* Number of pad bytes after cp_len */
- unchar host_addr[4]; /* Host Adapter SCSI ID for channels 3, 2, 1, 0 */
- u_int32_t cp_len; /* Number of valid bytes in cp */
- u_int32_t sp_len; /* Number of valid bytes in sp */
- ushort queue_size; /* Max number of cp that can be queued */
- ushort unused;
- ushort scatt_size; /* Max number of entries in scatter/gather table */
-
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar drqx : 2,
- second : 1,
- irq_tr : 1,
- irq : 4;
- unchar sync;
- unchar : 4,
- res1 : 1,
- large_sg : 1,
- forcaddr : 1,
- isaena : 1;
- unchar max_chan : 3,
- max_id : 5;
- unchar max_lun;
- unchar eisa : 1,
- pci : 1,
- idquest : 1,
- m1 : 1,
- : 4;
-#else
- unchar irq : 4, /* Interrupt Request assigned to this controller */
- irq_tr : 1, /* 0 for edge triggered, 1 for level triggered */
- second : 1, /* 1 if this is a secondary (not primary) controller */
- drqx : 2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
- unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
-
- /* Structure extension defined in EATA 2.0B */
- unchar isaena : 1, /* ISA i/o addressing is disabled/enabled */
- forcaddr : 1, /* Port address has been forced */
- large_sg : 1, /* 1 if large SG lists are supported */
- res1 : 1,
- : 4;
- unchar max_id : 5, /* Max SCSI target ID number */
- max_chan : 3; /* Max SCSI channel number on this board */
-
- /* Structure extension defined in EATA 2.0C */
- unchar max_lun; /* Max SCSI LUN number */
- unchar
- : 4,
- m1 : 1, /* This is a PCI with an M1 chip installed */
- idquest : 1, /* RAIDNUM returned is questionable */
- pci : 1, /* This board is PCI */
- eisa : 1; /* This board is EISA */
-#endif
-
- unchar raidnum; /* Uniquely identifies this HBA in a system */
- unchar notused;
-
- ushort ipad[247];
-};
-
-/* Board config structure */
-struct eata_config {
- ushort len; /* Number of bytes following this field */
-
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar : 4,
- tarena : 1,
- mdpena : 1,
- ocena : 1,
- edis : 1;
-#else
- unchar edis : 1, /* Disable EATA interface after config command */
- ocena : 1, /* Overlapped Commands Enabled */
- mdpena : 1, /* Transfer all Modified Data Pointer Messages */
- tarena : 1, /* Target Mode Enabled for this controller */
- : 4;
-#endif
- unchar cpad[511];
-};
-
-/* Returned status packet structure */
-struct mssp {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar eoc : 1,
- adapter_status : 7;
-#else
- unchar adapter_status : 7, /* State related to current command */
- eoc : 1; /* End Of Command (1 = command completed) */
-#endif
- unchar target_status; /* SCSI status received after data transfer */
- unchar unused[2];
- u_int32_t inv_res_len; /* Number of bytes not transferred */
- u_int32_t cpp_index; /* Index of address set in cp */
- char mess[12];
-};
-
-struct sg_list {
- unsigned int address; /* Segment Address */
- unsigned int num_bytes; /* Segment Length */
-};
-
-/* MailBox SCSI Command Packet */
-struct mscp {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar din : 1,
- dout : 1,
- interp : 1,
- : 1,
- sg : 1,
- reqsen :1,
- init : 1,
- sreset : 1;
- unchar sense_len;
- unchar unused[3];
- unchar : 7,
- fwnest : 1;
- unchar : 5,
- hbaci : 1,
- iat : 1,
- phsunit : 1;
- unchar channel : 3,
- target : 5;
- unchar one : 1,
- dispri : 1,
- luntar : 1,
- lun : 5;
-#else
- unchar sreset :1, /* SCSI Bus Reset Signal should be asserted */
- init :1, /* Re-initialize controller and self test */
- reqsen :1, /* Transfer Request Sense Data to addr using DMA */
- sg :1, /* Use Scatter/Gather */
- :1,
- interp :1, /* The controller interprets cp, not the target */
- dout :1, /* Direction of Transfer is Out (Host to Target) */
- din :1; /* Direction of Transfer is In (Target to Host) */
- unchar sense_len; /* Request Sense Length */
- unchar unused[3];
- unchar fwnest : 1, /* Send command to a component of an Array Group */
- : 7;
- unchar phsunit : 1, /* Send to Target Physical Unit (bypass RAID) */
- iat : 1, /* Inhibit Address Translation */
- hbaci : 1, /* Inhibit HBA Caching for this command */
- : 5;
- unchar target : 5, /* SCSI target ID */
- channel : 3; /* SCSI channel number */
- unchar lun : 5, /* SCSI logical unit number */
- luntar : 1, /* This cp is for Target (not LUN) */
- dispri : 1, /* Disconnect Privilege granted */
- one : 1; /* 1 */
-#endif
-
- unchar mess[3]; /* Massage to/from Target */
- unchar cdb[12]; /* Command Descriptor Block */
- u_int32_t data_len; /* If sg=0 Data Length, if sg=1 sglist length */
- u_int32_t cpp_index; /* Index of address to be returned in sp */
- u_int32_t data_address; /* If sg=0 Data Address, if sg=1 sglist address */
- u_int32_t sp_dma_addr; /* Address where sp is DMA'ed when cp completes */
- u_int32_t sense_addr; /* Address where Sense Data is DMA'ed on error */
-
- /* Additional fields begin here. */
- struct scsi_cmnd *SCpnt;
-
- /* All the cp structure is zero filled by queuecommand except the
- following CP_TAIL_SIZE bytes, initialized by detect */
- dma_addr_t cp_dma_addr; /* dma handle for this cp structure */
- struct sg_list *sglist; /* pointer to the allocated SG list */
-};
-
-#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t))
-
-struct hostdata {
- struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
- unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
- unsigned int last_cp_used; /* Index of last mailbox used */
- unsigned int iocount; /* Total i/o done for this board */
- int board_number; /* Number of this board */
- char board_name[16]; /* Name of this board */
- int in_reset; /* True if board is doing a reset */
- int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */
- int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If 1 redo i/o on target */
- unsigned int retries; /* Number of internal retries */
- unsigned long last_retried_pid; /* Pid of last retried command */
- unsigned char subversion; /* Bus type, either ISA or EISA/PCI */
- unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */
- unsigned char is_pci; /* 1 is bus type is PCI */
- struct pci_dev *pdev; /* pdev for PCI bus, NULL otherwise */
- struct mssp *sp_cpu_addr; /* cpu addr for DMA buffer sp */
- dma_addr_t sp_dma_addr; /* dma handle for DMA buffer sp */
- struct mssp sp; /* Local copy of sp buffer */
-};
-
-static struct Scsi_Host *sh[MAX_BOARDS];
-static const char *driver_name = "EATA";
-static char sha[MAX_BOARDS];
-static DEFINE_SPINLOCK(driver_lock);
-
-/* Initialize num_boards so that ihdlr can work while detect is in progress */
-static unsigned int num_boards = MAX_BOARDS;
-
-static unsigned long io_port[] = {
-
- /* Space for MAX_INT_PARAM ports usable while loading as a module */
- SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
- SKIP, SKIP,
-
- /* First ISA */
- 0x1f0,
-
- /* Space for MAX_PCI ports possibly reported by PCI_BIOS */
- SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
- SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
-
- /* MAX_EISA ports */
- 0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
- 0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
-
- /* Other (MAX_ISA - 1) ports */
- 0x170, 0x230, 0x330,
-
- /* End of list */
- 0x0
-};
-
-/* Device is Big Endian */
-#define H2DEV(x) cpu_to_be32(x)
-#define DEV2H(x) be32_to_cpu(x)
-#define H2DEV16(x) cpu_to_be16(x)
-#define DEV2H16(x) be16_to_cpu(x)
-
-/* But transfer orientation from the 16 bit data register is Little Endian */
-#define REG2H(x) le16_to_cpu(x)
-
-static irqreturn_t do_interrupt_handler(int, void *);
-static void flush_dev(struct scsi_device *, unsigned long, struct hostdata *,
- unsigned int);
-static int do_trace = 0;
-static int setup_done = 0;
-static int link_statistics;
-static int ext_tran = 0;
-static int rev_scan = 1;
-
-#if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE)
-static int tag_mode = TAG_SIMPLE;
-#else
-static int tag_mode = TAG_DISABLED;
-#endif
-
-#if defined(CONFIG_SCSI_EATA_LINKED_COMMANDS)
-static int linked_comm = 1;
-#else
-static int linked_comm = 0;
-#endif
-
-#if defined(CONFIG_SCSI_EATA_MAX_TAGS)
-static int max_queue_depth = CONFIG_SCSI_EATA_MAX_TAGS;
-#else
-static int max_queue_depth = MAX_CMD_PER_LUN;
-#endif
-
-#if defined(CONFIG_ISA)
-static int isa_probe = 1;
-#else
-static int isa_probe = 0;
-#endif
-
-#if defined(CONFIG_EISA)
-static int eisa_probe = 1;
-#else
-static int eisa_probe = 0;
-#endif
-
-#if defined(CONFIG_PCI)
-static int pci_probe = 1;
-#else
-static int pci_probe = 0;
-#endif
-
-#define MAX_INT_PARAM 10
-#define MAX_BOOT_OPTIONS_SIZE 256
-static char boot_options[MAX_BOOT_OPTIONS_SIZE];
-
-#if defined(MODULE)
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-module_param_string(eata, boot_options, MAX_BOOT_OPTIONS_SIZE, 0);
-MODULE_PARM_DESC(eata, " equivalent to the \"eata=...\" kernel boot option."
- " Example: modprobe eata \"eata=0x7410,0x230,lc:y,tm:0,mq:4,ep:n\"");
-MODULE_AUTHOR("Dario Ballabio");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("EATA/DMA SCSI Driver");
-
-#endif
-
-static int eata2x_slave_configure(struct scsi_device *dev)
-{
- int tqd, utqd;
- char *tag_suffix, *link_suffix;
-
- utqd = MAX_CMD_PER_LUN;
- tqd = max_queue_depth;
-
- if (TLDEV(dev->type) && dev->tagged_supported) {
- if (tag_mode == TAG_SIMPLE) {
- scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, tqd);
- tag_suffix = ", simple tags";
- } else if (tag_mode == TAG_ORDERED) {
- scsi_adjust_queue_depth(dev, MSG_ORDERED_TAG, tqd);
- tag_suffix = ", ordered tags";
- } else {
- scsi_adjust_queue_depth(dev, 0, tqd);
- tag_suffix = ", no tags";
- }
- } else if (TLDEV(dev->type) && linked_comm) {
- scsi_adjust_queue_depth(dev, 0, tqd);
- tag_suffix = ", untagged";
- } else {
- scsi_adjust_queue_depth(dev, 0, utqd);
- tag_suffix = "";
- }
-
- if (TLDEV(dev->type) && linked_comm && dev->queue_depth > 2)
- link_suffix = ", sorted";
- else if (TLDEV(dev->type))
- link_suffix = ", unsorted";
- else
- link_suffix = "";
-
- sdev_printk(KERN_INFO, dev,
- "cmds/lun %d%s%s.\n",
- dev->queue_depth, link_suffix, tag_suffix);
-
- return 0;
-}
-
-static int wait_on_busy(unsigned long iobase, unsigned int loop)
-{
- while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED) {
- udelay(1L);
- if (--loop == 0)
- return 1;
- }
- return 0;
-}
-
-static int do_dma(unsigned long iobase, unsigned long addr, unchar cmd)
-{
- unsigned char *byaddr;
- unsigned long devaddr;
-
- if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP)))
- return 1;
-
- if (addr) {
- devaddr = H2DEV(addr);
- byaddr = (unsigned char *)&devaddr;
- outb(byaddr[3], iobase + REG_LOW);
- outb(byaddr[2], iobase + REG_LM);
- outb(byaddr[1], iobase + REG_MID);
- outb(byaddr[0], iobase + REG_MSB);
- }
-
- outb(cmd, iobase + REG_CMD);
- return 0;
-}
-
-static int read_pio(unsigned long iobase, ushort * start, ushort * end)
-{
- unsigned int loop = MAXLOOP;
- ushort *p;
-
- for (p = start; p <= end; p++) {
- while (!(inb(iobase + REG_STATUS) & DRQ_ASSERTED)) {
- udelay(1L);
- if (--loop == 0)
- return 1;
- }
- loop = MAXLOOP;
- *p = REG2H(inw(iobase));
- }
-
- return 0;
-}
-
-static struct pci_dev *get_pci_dev(unsigned long port_base)
-{
-#if defined(CONFIG_PCI)
- unsigned int addr;
- struct pci_dev *dev = NULL;
-
- while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
- addr = pci_resource_start(dev, 0);
-
-#if defined(DEBUG_PCI_DETECT)
- printk("%s: get_pci_dev, bus %d, devfn 0x%x, addr 0x%x.\n",
- driver_name, dev->bus->number, dev->devfn, addr);
-#endif
-
- /* we are in so much trouble for a pci hotplug system with this driver
- * anyway, so doing this at least lets people unload the driver and not
- * cause memory problems, but in general this is a bad thing to do (this
- * driver needs to be converted to the proper PCI api someday... */
- pci_dev_put(dev);
- if (addr + PCI_BASE_ADDRESS_0 == port_base)
- return dev;
- }
-#endif /* end CONFIG_PCI */
- return NULL;
-}
-
-static void enable_pci_ports(void)
-{
-#if defined(CONFIG_PCI)
- struct pci_dev *dev = NULL;
-
- while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
-#if defined(DEBUG_PCI_DETECT)
- printk("%s: enable_pci_ports, bus %d, devfn 0x%x.\n",
- driver_name, dev->bus->number, dev->devfn);
-#endif
-
- if (pci_enable_device(dev))
- printk
- ("%s: warning, pci_enable_device failed, bus %d devfn 0x%x.\n",
- driver_name, dev->bus->number, dev->devfn);
- }
-
-#endif /* end CONFIG_PCI */
-}
-
-static int port_detect(unsigned long port_base, unsigned int j,
- struct scsi_host_template *tpnt)
-{
- unsigned char irq, dma_channel, subversion, i, is_pci = 0;
- unsigned char protocol_rev;
- struct eata_info info;
- char *bus_type, dma_name[16];
- struct pci_dev *pdev;
- /* Allowed DMA channels for ISA (0 indicates reserved) */
- unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
- struct Scsi_Host *shost;
- struct hostdata *ha;
- char name[16];
-
- sprintf(name, "%s%d", driver_name, j);
-
- if (!request_region(port_base, REGION_SIZE, driver_name)) {
-#if defined(DEBUG_DETECT)
- printk("%s: address 0x%03lx in use, skipping probe.\n", name,
- port_base);
-#endif
- goto fail;
- }
-
- spin_lock_irq(&driver_lock);
-
- if (do_dma(port_base, 0, READ_CONFIG_PIO)) {
-#if defined(DEBUG_DETECT)
- printk("%s: detect, do_dma failed at 0x%03lx.\n", name,
- port_base);
-#endif
- goto freelock;
- }
-
- /* Read the info structure */
- if (read_pio(port_base, (ushort *) & info, (ushort *) & info.ipad[0])) {
-#if defined(DEBUG_DETECT)
- printk("%s: detect, read_pio failed at 0x%03lx.\n", name,
- port_base);
-#endif
- goto freelock;
- }
-
- info.data_len = DEV2H(info.data_len);
- info.sign = DEV2H(info.sign);
- info.cp_pad_len = DEV2H16(info.cp_pad_len);
- info.cp_len = DEV2H(info.cp_len);
- info.sp_len = DEV2H(info.sp_len);
- info.scatt_size = DEV2H16(info.scatt_size);
- info.queue_size = DEV2H16(info.queue_size);
-
- /* Check the controller "EATA" signature */
- if (info.sign != EATA_SIG_BE) {
-#if defined(DEBUG_DETECT)
- printk("%s: signature 0x%04x discarded.\n", name, info.sign);
-#endif
- goto freelock;
- }
-
- if (info.data_len < EATA_2_0A_SIZE) {
- printk
- ("%s: config structure size (%d bytes) too short, detaching.\n",
- name, info.data_len);
- goto freelock;
- } else if (info.data_len == EATA_2_0A_SIZE)
- protocol_rev = 'A';
- else if (info.data_len == EATA_2_0B_SIZE)
- protocol_rev = 'B';
- else
- protocol_rev = 'C';
-
- if (protocol_rev != 'A' && info.forcaddr) {
- printk("%s: warning, port address has been forced.\n", name);
- bus_type = "PCI";
- is_pci = 1;
- subversion = ESA;
- } else if (port_base > MAX_EISA_ADDR
- || (protocol_rev == 'C' && info.pci)) {
- bus_type = "PCI";
- is_pci = 1;
- subversion = ESA;
- } else if (port_base >= MIN_EISA_ADDR
- || (protocol_rev == 'C' && info.eisa)) {
- bus_type = "EISA";
- subversion = ESA;
- } else if (protocol_rev == 'C' && !info.eisa && !info.pci) {
- bus_type = "ISA";
- subversion = ISA;
- } else if (port_base > MAX_ISA_ADDR) {
- bus_type = "PCI";
- is_pci = 1;
- subversion = ESA;
- } else {
- bus_type = "ISA";
- subversion = ISA;
- }
-
- if (!info.haaval || info.ata) {
- printk
- ("%s: address 0x%03lx, unusable %s board (%d%d), detaching.\n",
- name, port_base, bus_type, info.haaval, info.ata);
- goto freelock;
- }
-
- if (info.drqvld) {
- if (subversion == ESA)
- printk("%s: warning, weird %s board using DMA.\n", name,
- bus_type);
-
- subversion = ISA;
- dma_channel = dma_channel_table[3 - info.drqx];
- } else {
- if (subversion == ISA)
- printk("%s: warning, weird %s board not using DMA.\n",
- name, bus_type);
-
- subversion = ESA;
- dma_channel = NO_DMA;
- }
-
- if (!info.dmasup)
- printk("%s: warning, DMA protocol support not asserted.\n",
- name);
-
- irq = info.irq;
-
- if (subversion == ESA && !info.irq_tr)
- printk
- ("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
- name, irq);
-
- if (is_pci) {
- pdev = get_pci_dev(port_base);
- if (!pdev)
- printk
- ("%s: warning, failed to get pci_dev structure.\n",
- name);
- } else
- pdev = NULL;
-
- if (pdev && (irq != pdev->irq)) {
- printk("%s: IRQ %u mapped to IO-APIC IRQ %u.\n", name, irq,
- pdev->irq);
- irq = pdev->irq;
- }
-
- /* Board detected, allocate its IRQ */
- if (request_irq(irq, do_interrupt_handler,
- IRQF_DISABLED | ((subversion == ESA) ? IRQF_SHARED : 0),
- driver_name, (void *)&sha[j])) {
- printk("%s: unable to allocate IRQ %u, detaching.\n", name,
- irq);
- goto freelock;
- }
-
- if (subversion == ISA && request_dma(dma_channel, driver_name)) {
- printk("%s: unable to allocate DMA channel %u, detaching.\n",
- name, dma_channel);
- goto freeirq;
- }
-#if defined(FORCE_CONFIG)
- {
- struct eata_config *cf;
- dma_addr_t cf_dma_addr;
-
- cf = pci_alloc_consistent(pdev, sizeof(struct eata_config),
- &cf_dma_addr);
-
- if (!cf) {
- printk
- ("%s: config, pci_alloc_consistent failed, detaching.\n",
- name);
- goto freedma;
- }
-
- /* Set board configuration */
- memset((char *)cf, 0, sizeof(struct eata_config));
- cf->len = (ushort) H2DEV16((ushort) 510);
- cf->ocena = 1;
-
- if (do_dma(port_base, cf_dma_addr, SET_CONFIG_DMA)) {
- printk
- ("%s: busy timeout sending configuration, detaching.\n",
- name);
- pci_free_consistent(pdev, sizeof(struct eata_config),
- cf, cf_dma_addr);
- goto freedma;
- }
-
- }
-#endif
-
- spin_unlock_irq(&driver_lock);
- sh[j] = shost = scsi_register(tpnt, sizeof(struct hostdata));
- spin_lock_irq(&driver_lock);
-
- if (shost == NULL) {
- printk("%s: unable to register host, detaching.\n", name);
- goto freedma;
- }
-
- shost->io_port = port_base;
- shost->unique_id = port_base;
- shost->n_io_port = REGION_SIZE;
- shost->dma_channel = dma_channel;
- shost->irq = irq;
- shost->sg_tablesize = (ushort) info.scatt_size;
- shost->this_id = (ushort) info.host_addr[3];
- shost->can_queue = (ushort) info.queue_size;
- shost->cmd_per_lun = MAX_CMD_PER_LUN;
-
- ha = (struct hostdata *)shost->hostdata;
-
- memset(ha, 0, sizeof(struct hostdata));
- ha->subversion = subversion;
- ha->protocol_rev = protocol_rev;
- ha->is_pci = is_pci;
- ha->pdev = pdev;
- ha->board_number = j;
-
- if (ha->subversion == ESA)
- shost->unchecked_isa_dma = 0;
- else {
- unsigned long flags;
- shost->unchecked_isa_dma = 1;
-
- flags = claim_dma_lock();
- disable_dma(dma_channel);
- clear_dma_ff(dma_channel);
- set_dma_mode(dma_channel, DMA_MODE_CASCADE);
- enable_dma(dma_channel);
- release_dma_lock(flags);
-
- }
-
- strcpy(ha->board_name, name);
-
- /* DPT PM2012 does not allow to detect sg_tablesize correctly */
- if (shost->sg_tablesize > MAX_SGLIST || shost->sg_tablesize < 2) {
- printk("%s: detect, wrong n. of SG lists %d, fixed.\n",
- ha->board_name, shost->sg_tablesize);
- shost->sg_tablesize = MAX_SGLIST;
- }
-
- /* DPT PM2012 does not allow to detect can_queue correctly */
- if (shost->can_queue > MAX_MAILBOXES || shost->can_queue < 2) {
- printk("%s: detect, wrong n. of mbox %d, fixed.\n",
- ha->board_name, shost->can_queue);
- shost->can_queue = MAX_MAILBOXES;
- }
-
- if (protocol_rev != 'A') {
- if (info.max_chan > 0 && info.max_chan < MAX_CHANNEL)
- shost->max_channel = info.max_chan;
-
- if (info.max_id > 7 && info.max_id < MAX_TARGET)
- shost->max_id = info.max_id + 1;
-
- if (info.large_sg && shost->sg_tablesize == MAX_SGLIST)
- shost->sg_tablesize = MAX_LARGE_SGLIST;
- }
-
- if (protocol_rev == 'C') {
- if (info.max_lun > 7 && info.max_lun < MAX_LUN)
- shost->max_lun = info.max_lun + 1;
- }
-
- if (dma_channel == NO_DMA)
- sprintf(dma_name, "%s", "BMST");
- else
- sprintf(dma_name, "DMA %u", dma_channel);
-
- spin_unlock_irq(&driver_lock);
-
- for (i = 0; i < shost->can_queue; i++)
- ha->cp[i].cp_dma_addr = pci_map_single(ha->pdev,
- &ha->cp[i],
- sizeof(struct mscp),
- PCI_DMA_BIDIRECTIONAL);
-
- for (i = 0; i < shost->can_queue; i++) {
- size_t sz = shost->sg_tablesize *sizeof(struct sg_list);
- gfp_t gfp_mask = (shost->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC;
- ha->cp[i].sglist = kmalloc(sz, gfp_mask);
- if (!ha->cp[i].sglist) {
- printk
- ("%s: kmalloc SGlist failed, mbox %d, detaching.\n",
- ha->board_name, i);
- goto release;
- }
- }
-
- if (!(ha->sp_cpu_addr = pci_alloc_consistent(ha->pdev,
- sizeof(struct mssp),
- &ha->sp_dma_addr))) {
- printk("%s: pci_alloc_consistent failed, detaching.\n", ha->board_name);
- goto release;
- }
-
- if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN)
- max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
-
- if (max_queue_depth < MAX_CMD_PER_LUN)
- max_queue_depth = MAX_CMD_PER_LUN;
-
- if (tag_mode != TAG_DISABLED && tag_mode != TAG_SIMPLE)
- tag_mode = TAG_ORDERED;
-
- if (j == 0) {
- printk
- ("EATA/DMA 2.0x: Copyright (C) 1994-2003 Dario Ballabio.\n");
- printk
- ("%s config options -> tm:%d, lc:%c, mq:%d, rs:%c, et:%c, "
- "ip:%c, ep:%c, pp:%c.\n", driver_name, tag_mode,
- YESNO(linked_comm), max_queue_depth, YESNO(rev_scan),
- YESNO(ext_tran), YESNO(isa_probe), YESNO(eisa_probe),
- YESNO(pci_probe));
- }
-
- printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d.\n",
- ha->board_name, ha->protocol_rev, bus_type,
- (unsigned long)shost->io_port, shost->irq, dma_name,
- shost->sg_tablesize, shost->can_queue);
-
- if (shost->max_id > 8 || shost->max_lun > 8)
- printk
- ("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
- ha->board_name, shost->max_id, shost->max_lun);
-
- for (i = 0; i <= shost->max_channel; i++)
- printk("%s: SCSI channel %u enabled, host target ID %d.\n",
- ha->board_name, i, info.host_addr[3 - i]);
-
-#if defined(DEBUG_DETECT)
- printk("%s: Vers. 0x%x, ocs %u, tar %u, trnxfr %u, more %u, SYNC 0x%x, "
- "sec. %u, infol %d, cpl %d spl %d.\n", name, info.version,
- info.ocsena, info.tarsup, info.trnxfr, info.morsup, info.sync,
- info.second, info.data_len, info.cp_len, info.sp_len);
-
- if (protocol_rev == 'B' || protocol_rev == 'C')
- printk("%s: isaena %u, forcaddr %u, max_id %u, max_chan %u, "
- "large_sg %u, res1 %u.\n", name, info.isaena,
- info.forcaddr, info.max_id, info.max_chan, info.large_sg,
- info.res1);
-
- if (protocol_rev == 'C')
- printk("%s: max_lun %u, m1 %u, idquest %u, pci %u, eisa %u, "
- "raidnum %u.\n", name, info.max_lun, info.m1,
- info.idquest, info.pci, info.eisa, info.raidnum);
-#endif
-
- if (ha->pdev) {
- pci_set_master(ha->pdev);
- if (pci_set_dma_mask(ha->pdev, DMA_BIT_MASK(32)))
- printk("%s: warning, pci_set_dma_mask failed.\n",
- ha->board_name);
- }
-
- return 1;
-
- freedma:
- if (subversion == ISA)
- free_dma(dma_channel);
- freeirq:
- free_irq(irq, &sha[j]);
- freelock:
- spin_unlock_irq(&driver_lock);
- release_region(port_base, REGION_SIZE);
- fail:
- return 0;
-
- release:
- eata2x_release(shost);
- return 0;
-}
-
-static void internal_setup(char *str, int *ints)
-{
- int i, argc = ints[0];
- char *cur = str, *pc;
-
- if (argc > 0) {
- if (argc > MAX_INT_PARAM)
- argc = MAX_INT_PARAM;
-
- for (i = 0; i < argc; i++)
- io_port[i] = ints[i + 1];
-
- io_port[i] = 0;
- setup_done = 1;
- }
-
- while (cur && (pc = strchr(cur, ':'))) {
- int val = 0, c = *++pc;
-
- if (c == 'n' || c == 'N')
- val = 0;
- else if (c == 'y' || c == 'Y')
- val = 1;
- else
- val = (int)simple_strtoul(pc, NULL, 0);
-
- if (!strncmp(cur, "lc:", 3))
- linked_comm = val;
- else if (!strncmp(cur, "tm:", 3))
- tag_mode = val;
- else if (!strncmp(cur, "tc:", 3))
- tag_mode = val;
- else if (!strncmp(cur, "mq:", 3))
- max_queue_depth = val;
- else if (!strncmp(cur, "ls:", 3))
- link_statistics = val;
- else if (!strncmp(cur, "et:", 3))
- ext_tran = val;
- else if (!strncmp(cur, "rs:", 3))
- rev_scan = val;
- else if (!strncmp(cur, "ip:", 3))
- isa_probe = val;
- else if (!strncmp(cur, "ep:", 3))
- eisa_probe = val;
- else if (!strncmp(cur, "pp:", 3))
- pci_probe = val;
-
- if ((cur = strchr(cur, ',')))
- ++cur;
- }
-
- return;
-}
-
-static int option_setup(char *str)
-{
- int ints[MAX_INT_PARAM];
- char *cur = str;
- int i = 1;
-
- while (cur && isdigit(*cur) && i < MAX_INT_PARAM) {
- ints[i++] = simple_strtoul(cur, NULL, 0);
-
- if ((cur = strchr(cur, ',')) != NULL)
- cur++;
- }
-
- ints[0] = i - 1;
- internal_setup(cur, ints);
- return 1;
-}
-
-static void add_pci_ports(void)
-{
-#if defined(CONFIG_PCI)
- unsigned int addr, k;
- struct pci_dev *dev = NULL;
-
- for (k = 0; k < MAX_PCI; k++) {
-
- if (!(dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev)))
- break;
-
- if (pci_enable_device(dev)) {
-#if defined(DEBUG_PCI_DETECT)
- printk
- ("%s: detect, bus %d, devfn 0x%x, pci_enable_device failed.\n",
- driver_name, dev->bus->number, dev->devfn);
-#endif
-
- continue;
- }
-
- addr = pci_resource_start(dev, 0);
-
-#if defined(DEBUG_PCI_DETECT)
- printk("%s: detect, seq. %d, bus %d, devfn 0x%x, addr 0x%x.\n",
- driver_name, k, dev->bus->number, dev->devfn, addr);
-#endif
-
- /* Order addresses according to rev_scan value */
- io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
- addr + PCI_BASE_ADDRESS_0;
- }
-
- pci_dev_put(dev);
-#endif /* end CONFIG_PCI */
-}
-
-static int eata2x_detect(struct scsi_host_template *tpnt)
-{
- unsigned int j = 0, k;
-
- tpnt->proc_name = "eata2x";
-
- if (strlen(boot_options))
- option_setup(boot_options);
-
-#if defined(MODULE)
- /* io_port could have been modified when loading as a module */
- if (io_port[0] != SKIP) {
- setup_done = 1;
- io_port[MAX_INT_PARAM] = 0;
- }
-#endif
-
- for (k = MAX_INT_PARAM; io_port[k]; k++)
- if (io_port[k] == SKIP)
- continue;
- else if (io_port[k] <= MAX_ISA_ADDR) {
- if (!isa_probe)
- io_port[k] = SKIP;
- } else if (io_port[k] >= MIN_EISA_ADDR
- && io_port[k] <= MAX_EISA_ADDR) {
- if (!eisa_probe)
- io_port[k] = SKIP;
- }
-
- if (pci_probe) {
- if (!setup_done)
- add_pci_ports();
- else
- enable_pci_ports();
- }
-
- for (k = 0; io_port[k]; k++) {
-
- if (io_port[k] == SKIP)
- continue;
-
- if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt))
- j++;
- }
-
- num_boards = j;
- return j;
-}
-
-static void map_dma(unsigned int i, struct hostdata *ha)
-{
- unsigned int k, pci_dir;
- int count;
- struct scatterlist *sg;
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
- pci_dir = SCpnt->sc_data_direction;
-
- if (SCpnt->sense_buffer)
- cpp->sense_addr =
- H2DEV(pci_map_single(ha->pdev, SCpnt->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE));
-
- cpp->sense_len = SCSI_SENSE_BUFFERSIZE;
-
- if (!scsi_sg_count(SCpnt)) {
- cpp->data_len = 0;
- return;
- }
-
- count = pci_map_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
- pci_dir);
- BUG_ON(!count);
-
- scsi_for_each_sg(SCpnt, sg, count, k) {
- cpp->sglist[k].address = H2DEV(sg_dma_address(sg));
- cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(sg));
- }
-
- cpp->sg = 1;
- cpp->data_address = H2DEV(pci_map_single(ha->pdev, cpp->sglist,
- scsi_sg_count(SCpnt) *
- sizeof(struct sg_list),
- pci_dir));
- cpp->data_len = H2DEV((scsi_sg_count(SCpnt) * sizeof(struct sg_list)));
-}
-
-static void unmap_dma(unsigned int i, struct hostdata *ha)
-{
- unsigned int pci_dir;
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
- pci_dir = SCpnt->sc_data_direction;
-
- if (DEV2H(cpp->sense_addr))
- pci_unmap_single(ha->pdev, DEV2H(cpp->sense_addr),
- DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE);
-
- if (scsi_sg_count(SCpnt))
- pci_unmap_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
- pci_dir);
-
- if (!DEV2H(cpp->data_len))
- pci_dir = PCI_DMA_BIDIRECTIONAL;
-
- if (DEV2H(cpp->data_address))
- pci_unmap_single(ha->pdev, DEV2H(cpp->data_address),
- DEV2H(cpp->data_len), pci_dir);
-}
-
-static void sync_dma(unsigned int i, struct hostdata *ha)
-{
- unsigned int pci_dir;
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
- pci_dir = SCpnt->sc_data_direction;
-
- if (DEV2H(cpp->sense_addr))
- pci_dma_sync_single_for_cpu(ha->pdev, DEV2H(cpp->sense_addr),
- DEV2H(cpp->sense_len),
- PCI_DMA_FROMDEVICE);
-
- if (scsi_sg_count(SCpnt))
- pci_dma_sync_sg_for_cpu(ha->pdev, scsi_sglist(SCpnt),
- scsi_sg_count(SCpnt), pci_dir);
-
- if (!DEV2H(cpp->data_len))
- pci_dir = PCI_DMA_BIDIRECTIONAL;
-
- if (DEV2H(cpp->data_address))
- pci_dma_sync_single_for_cpu(ha->pdev,
- DEV2H(cpp->data_address),
- DEV2H(cpp->data_len), pci_dir);
-}
-
-static void scsi_to_dev_dir(unsigned int i, struct hostdata *ha)
-{
- unsigned int k;
-
- static const unsigned char data_out_cmds[] = {
- 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
- 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40,
- 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b, 0x5d
- };
-
- static const unsigned char data_none_cmds[] = {
- 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
- 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
- 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00
- };
-
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
-
- if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
- cpp->din = 1;
- cpp->dout = 0;
- return;
- } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
- cpp->din = 0;
- cpp->dout = 1;
- return;
- } else if (SCpnt->sc_data_direction == DMA_NONE) {
- cpp->din = 0;
- cpp->dout = 0;
- return;
- }
-
- if (SCpnt->sc_data_direction != DMA_BIDIRECTIONAL)
- panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n",
- ha->board_name);
-
- for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
- if (SCpnt->cmnd[0] == data_out_cmds[k]) {
- cpp->dout = 1;
- break;
- }
-
- if ((cpp->din = !cpp->dout))
- for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
- if (SCpnt->cmnd[0] == data_none_cmds[k]) {
- cpp->din = 0;
- break;
- }
-
-}
-
-static int eata2x_queuecommand_lck(struct scsi_cmnd *SCpnt,
- void (*done) (struct scsi_cmnd *))
-{
- struct Scsi_Host *shost = SCpnt->device->host;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- unsigned int i, k;
- struct mscp *cpp;
-
- if (SCpnt->host_scribble)
- panic("%s: qcomm, SCpnt %p already active.\n",
- ha->board_name, SCpnt);
-
- /* i is the mailbox number, look for the first free mailbox
- starting from last_cp_used */
- i = ha->last_cp_used + 1;
-
- for (k = 0; k < shost->can_queue; k++, i++) {
- if (i >= shost->can_queue)
- i = 0;
- if (ha->cp_stat[i] == FREE) {
- ha->last_cp_used = i;
- break;
- }
- }
-
- if (k == shost->can_queue) {
- printk("%s: qcomm, no free mailbox.\n", ha->board_name);
- return 1;
- }
-
- /* Set pointer to control packet structure */
- cpp = &ha->cp[i];
-
- memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE);
-
- /* Set pointer to status packet structure, Big Endian format */
- cpp->sp_dma_addr = H2DEV(ha->sp_dma_addr);
-
- SCpnt->scsi_done = done;
- cpp->cpp_index = i;
- SCpnt->host_scribble = (unsigned char *)&cpp->cpp_index;
-
- if (do_trace)
- scmd_printk(KERN_INFO, SCpnt,
- "qcomm, mbox %d.\n", i);
-
- cpp->reqsen = 1;
- cpp->dispri = 1;
-#if 0
- if (SCpnt->device->type == TYPE_TAPE)
- cpp->hbaci = 1;
-#endif
- cpp->one = 1;
- cpp->channel = SCpnt->device->channel;
- cpp->target = SCpnt->device->id;
- cpp->lun = SCpnt->device->lun;
- cpp->SCpnt = SCpnt;
- memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
-
- /* Use data transfer direction SCpnt->sc_data_direction */
- scsi_to_dev_dir(i, ha);
-
- /* Map DMA buffers and SG list */
- map_dma(i, ha);
-
- if (linked_comm && SCpnt->device->queue_depth > 2
- && TLDEV(SCpnt->device->type)) {
- ha->cp_stat[i] = READY;
- flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), ha, 0);
- return 0;
- }
-
- /* Send control packet to the board */
- if (do_dma(shost->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
- unmap_dma(i, ha);
- SCpnt->host_scribble = NULL;
- scmd_printk(KERN_INFO, SCpnt, "qcomm, adapter busy.\n");
- return 1;
- }
-
- ha->cp_stat[i] = IN_USE;
- return 0;
-}
-
-static DEF_SCSI_QCMD(eata2x_queuecommand)
-
-static int eata2x_eh_abort(struct scsi_cmnd *SCarg)
-{
- struct Scsi_Host *shost = SCarg->device->host;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- unsigned int i;
-
- if (SCarg->host_scribble == NULL) {
- scmd_printk(KERN_INFO, SCarg, "abort, cmd inactive.\n");
- return SUCCESS;
- }
-
- i = *(unsigned int *)SCarg->host_scribble;
- scmd_printk(KERN_WARNING, SCarg, "abort, mbox %d.\n", i);
-
- if (i >= shost->can_queue)
- panic("%s: abort, invalid SCarg->host_scribble.\n", ha->board_name);
-
- if (wait_on_busy(shost->io_port, MAXLOOP)) {
- printk("%s: abort, timeout error.\n", ha->board_name);
- return FAILED;
- }
-
- if (ha->cp_stat[i] == FREE) {
- printk("%s: abort, mbox %d is free.\n", ha->board_name, i);
- return SUCCESS;
- }
-
- if (ha->cp_stat[i] == IN_USE) {
- printk("%s: abort, mbox %d is in use.\n", ha->board_name, i);
-
- if (SCarg != ha->cp[i].SCpnt)
- panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
- ha->board_name, i, SCarg, ha->cp[i].SCpnt);
-
- if (inb(shost->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)
- printk("%s: abort, mbox %d, interrupt pending.\n",
- ha->board_name, i);
-
- return FAILED;
- }
-
- if (ha->cp_stat[i] == IN_RESET) {
- printk("%s: abort, mbox %d is in reset.\n", ha->board_name, i);
- return FAILED;
- }
-
- if (ha->cp_stat[i] == LOCKED) {
- printk("%s: abort, mbox %d is locked.\n", ha->board_name, i);
- return SUCCESS;
- }
-
- if (ha->cp_stat[i] == READY || ha->cp_stat[i] == ABORTING) {
- unmap_dma(i, ha);
- SCarg->result = DID_ABORT << 16;
- SCarg->host_scribble = NULL;
- ha->cp_stat[i] = FREE;
- printk("%s, abort, mbox %d ready, DID_ABORT, done.\n",
- ha->board_name, i);
- SCarg->scsi_done(SCarg);
- return SUCCESS;
- }
-
- panic("%s: abort, mbox %d, invalid cp_stat.\n", ha->board_name, i);
-}
-
-static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
-{
- unsigned int i, time, k, c, limit = 0;
- int arg_done = 0;
- struct scsi_cmnd *SCpnt;
- struct Scsi_Host *shost = SCarg->device->host;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
-
- scmd_printk(KERN_INFO, SCarg, "reset, enter.\n");
-
- spin_lock_irq(shost->host_lock);
-
- if (SCarg->host_scribble == NULL)
- printk("%s: reset, inactive.\n", ha->board_name);
-
- if (ha->in_reset) {
- printk("%s: reset, exit, already in reset.\n", ha->board_name);
- spin_unlock_irq(shost->host_lock);
- return FAILED;
- }
-
- if (wait_on_busy(shost->io_port, MAXLOOP)) {
- printk("%s: reset, exit, timeout error.\n", ha->board_name);
- spin_unlock_irq(shost->host_lock);
- return FAILED;
- }
-
- ha->retries = 0;
-
- for (c = 0; c <= shost->max_channel; c++)
- for (k = 0; k < shost->max_id; k++) {
- ha->target_redo[k][c] = 1;
- ha->target_to[k][c] = 0;
- }
-
- for (i = 0; i < shost->can_queue; i++) {
-
- if (ha->cp_stat[i] == FREE)
- continue;
-
- if (ha->cp_stat[i] == LOCKED) {
- ha->cp_stat[i] = FREE;
- printk("%s: reset, locked mbox %d forced free.\n",
- ha->board_name, i);
- continue;
- }
-
- if (!(SCpnt = ha->cp[i].SCpnt))
- panic("%s: reset, mbox %d, SCpnt == NULL.\n", ha->board_name, i);
-
- if (ha->cp_stat[i] == READY || ha->cp_stat[i] == ABORTING) {
- ha->cp_stat[i] = ABORTING;
- printk("%s: reset, mbox %d aborting.\n",
- ha->board_name, i);
- }
-
- else {
- ha->cp_stat[i] = IN_RESET;
- printk("%s: reset, mbox %d in reset.\n",
- ha->board_name, i);
- }
-
- if (SCpnt->host_scribble == NULL)
- panic("%s: reset, mbox %d, garbled SCpnt.\n", ha->board_name, i);
-
- if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: reset, mbox %d, index mismatch.\n", ha->board_name, i);
-
- if (SCpnt->scsi_done == NULL)
- panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n",
- ha->board_name, i);
-
- if (SCpnt == SCarg)
- arg_done = 1;
- }
-
- if (do_dma(shost->io_port, 0, RESET_PIO)) {
- printk("%s: reset, cannot reset, timeout error.\n", ha->board_name);
- spin_unlock_irq(shost->host_lock);
- return FAILED;
- }
-
- printk("%s: reset, board reset done, enabling interrupts.\n", ha->board_name);
-
-#if defined(DEBUG_RESET)
- do_trace = 1;
-#endif
-
- ha->in_reset = 1;
-
- spin_unlock_irq(shost->host_lock);
-
- /* FIXME: use a sleep instead */
- time = jiffies;
- while ((jiffies - time) < (10 * HZ) && limit++ < 200000)
- udelay(100L);
-
- spin_lock_irq(shost->host_lock);
-
- printk("%s: reset, interrupts disabled, loops %d.\n", ha->board_name, limit);
-
- for (i = 0; i < shost->can_queue; i++) {
-
- if (ha->cp_stat[i] == IN_RESET) {
- SCpnt = ha->cp[i].SCpnt;
- unmap_dma(i, ha);
- SCpnt->result = DID_RESET << 16;
- SCpnt->host_scribble = NULL;
-
- /* This mailbox is still waiting for its interrupt */
- ha->cp_stat[i] = LOCKED;
-
- printk
- ("%s, reset, mbox %d locked, DID_RESET, done.\n",
- ha->board_name, i);
- }
-
- else if (ha->cp_stat[i] == ABORTING) {
- SCpnt = ha->cp[i].SCpnt;
- unmap_dma(i, ha);
- SCpnt->result = DID_RESET << 16;
- SCpnt->host_scribble = NULL;
-
- /* This mailbox was never queued to the adapter */
- ha->cp_stat[i] = FREE;
-
- printk
- ("%s, reset, mbox %d aborting, DID_RESET, done.\n",
- ha->board_name, i);
- }
-
- else
- /* Any other mailbox has already been set free by interrupt */
- continue;
-
- SCpnt->scsi_done(SCpnt);
- }
-
- ha->in_reset = 0;
- do_trace = 0;
-
- if (arg_done)
- printk("%s: reset, exit, done.\n", ha->board_name);
- else
- printk("%s: reset, exit.\n", ha->board_name);
-
- spin_unlock_irq(shost->host_lock);
- return SUCCESS;
-}
-
-int eata2x_bios_param(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int *dkinfo)
-{
- unsigned int size = capacity;
-
- if (ext_tran || (scsicam_bios_param(bdev, capacity, dkinfo) < 0)) {
- dkinfo[0] = 255;
- dkinfo[1] = 63;
- dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
- }
-#if defined (DEBUG_GEOMETRY)
- printk("%s: bios_param, head=%d, sec=%d, cyl=%d.\n", driver_name,
- dkinfo[0], dkinfo[1], dkinfo[2]);
-#endif
-
- return 0;
-}
-
-static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
- unsigned int rev)
-{
- unsigned int i, j, k, y;
- unsigned long x;
-
- for (i = 0; i < n - 1; i++) {
- k = i;
-
- for (j = k + 1; j < n; j++)
- if (rev) {
- if (sk[j] > sk[k])
- k = j;
- } else {
- if (sk[j] < sk[k])
- k = j;
- }
-
- if (k != i) {
- x = sk[k];
- sk[k] = sk[i];
- sk[i] = x;
- y = da[k];
- da[k] = da[i];
- da[i] = y;
- }
- }
-
- return;
-}
-
-static int reorder(struct hostdata *ha, unsigned long cursec,
- unsigned int ihdlr, unsigned int il[], unsigned int n_ready)
-{
- struct scsi_cmnd *SCpnt;
- struct mscp *cpp;
- unsigned int k, n;
- unsigned int rev = 0, s = 1, r = 1;
- unsigned int input_only = 1, overlap = 0;
- unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
- unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
- unsigned long ioseek = 0;
-
- static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
- static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
- static unsigned int readysorted = 0, revcount = 0;
- static unsigned long seeksorted = 0, seeknosort = 0;
-
- if (link_statistics && !(++flushcount % link_statistics))
- printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"
- " av %ldK as %ldK.\n", flushcount, batchcount,
- inputcount, ovlcount, readycount, readysorted, sortcount,
- revcount, seeknosort / (readycount + 1),
- seeksorted / (readycount + 1));
-
- if (n_ready <= 1)
- return 0;
-
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
-
- if (!cpp->din)
- input_only = 0;
-
- if (blk_rq_pos(SCpnt->request) < minsec)
- minsec = blk_rq_pos(SCpnt->request);
- if (blk_rq_pos(SCpnt->request) > maxsec)
- maxsec = blk_rq_pos(SCpnt->request);
-
- sl[n] = blk_rq_pos(SCpnt->request);
- ioseek += blk_rq_sectors(SCpnt->request);
-
- if (!n)
- continue;
-
- if (sl[n] < sl[n - 1])
- s = 0;
- if (sl[n] > sl[n - 1])
- r = 0;
-
- if (link_statistics) {
- if (sl[n] > sl[n - 1])
- seek += sl[n] - sl[n - 1];
- else
- seek += sl[n - 1] - sl[n];
- }
-
- }
-
- if (link_statistics) {
- if (cursec > sl[0])
- seek += cursec - sl[0];
- else
- seek += sl[0] - cursec;
- }
-
- if (cursec > ((maxsec + minsec) / 2))
- rev = 1;
-
- if (ioseek > ((maxsec - minsec) / 2))
- rev = 0;
-
- if (!((rev && r) || (!rev && s)))
- sort(sl, il, n_ready, rev);
-
- if (!input_only)
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
- ll[n] = blk_rq_sectors(SCpnt->request);
- pl[n] = SCpnt->serial_number;
-
- if (!n)
- continue;
-
- if ((sl[n] == sl[n - 1])
- || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
- || (rev && ((sl[n] + ll[n]) > sl[n - 1])))
- overlap = 1;
- }
-
- if (overlap)
- sort(pl, il, n_ready, 0);
-
- if (link_statistics) {
- if (cursec > sl[0])
- iseek = cursec - sl[0];
- else
- iseek = sl[0] - cursec;
- batchcount++;
- readycount += n_ready;
- seeknosort += seek / 1024;
- if (input_only)
- inputcount++;
- if (overlap) {
- ovlcount++;
- seeksorted += iseek / 1024;
- } else
- seeksorted += (iseek + maxsec - minsec) / 1024;
- if (rev && !r) {
- revcount++;
- readysorted += n_ready;
- }
- if (!rev && !s) {
- sortcount++;
- readysorted += n_ready;
- }
- }
-#if defined(DEBUG_LINKED_COMMANDS)
- if (link_statistics && (overlap || !(flushcount % link_statistics)))
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
- scmd_printk(KERN_INFO, SCpnt,
- "%s mb %d fc %d nr %d sec %ld ns %u"
- " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
- (ihdlr ? "ihdlr" : "qcomm"),
- k, flushcount,
- n_ready, blk_rq_pos(SCpnt->request),
- blk_rq_sectors(SCpnt->request), cursec, YESNO(s),
- YESNO(r), YESNO(rev), YESNO(input_only),
- YESNO(overlap), cpp->din);
- }
-#endif
- return overlap;
-}
-
-static void flush_dev(struct scsi_device *dev, unsigned long cursec,
- struct hostdata *ha, unsigned int ihdlr)
-{
- struct scsi_cmnd *SCpnt;
- struct mscp *cpp;
- unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
-
- for (k = 0; k < dev->host->can_queue; k++) {
-
- if (ha->cp_stat[k] != READY && ha->cp_stat[k] != IN_USE)
- continue;
-
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
-
- if (SCpnt->device != dev)
- continue;
-
- if (ha->cp_stat[k] == IN_USE)
- return;
-
- il[n_ready++] = k;
- }
-
- if (reorder(ha, cursec, ihdlr, il, n_ready))
- n_ready = 1;
-
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
-
- if (do_dma(dev->host->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
- scmd_printk(KERN_INFO, SCpnt,
- "%s, mbox %d, adapter"
- " busy, will abort.\n",
- (ihdlr ? "ihdlr" : "qcomm"),
- k);
- ha->cp_stat[k] = ABORTING;
- continue;
- }
-
- ha->cp_stat[k] = IN_USE;
- }
-}
-
-static irqreturn_t ihdlr(struct Scsi_Host *shost)
-{
- struct scsi_cmnd *SCpnt;
- unsigned int i, k, c, status, tstatus, reg;
- struct mssp *spp;
- struct mscp *cpp;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- int irq = shost->irq;
-
- /* Check if this board need to be serviced */
- if (!(inb(shost->io_port + REG_AUX_STATUS) & IRQ_ASSERTED))
- goto none;
-
- ha->iocount++;
-
- if (do_trace)
- printk("%s: ihdlr, enter, irq %d, count %d.\n", ha->board_name, irq,
- ha->iocount);
-
- /* Check if this board is still busy */
- if (wait_on_busy(shost->io_port, 20 * MAXLOOP)) {
- reg = inb(shost->io_port + REG_STATUS);
- printk
- ("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n",
- ha->board_name, irq, reg, ha->iocount);
- goto none;
- }
-
- spp = &ha->sp;
-
- /* Make a local copy just before clearing the interrupt indication */
- memcpy(spp, ha->sp_cpu_addr, sizeof(struct mssp));
-
- /* Clear the completion flag and cp pointer on the dynamic copy of sp */
- memset(ha->sp_cpu_addr, 0, sizeof(struct mssp));
-
- /* Read the status register to clear the interrupt indication */
- reg = inb(shost->io_port + REG_STATUS);
-
-#if defined (DEBUG_INTERRUPT)
- {
- unsigned char *bytesp;
- int cnt;
- bytesp = (unsigned char *)spp;
- if (ha->iocount < 200) {
- printk("sp[] =");
- for (cnt = 0; cnt < 15; cnt++)
- printk(" 0x%x", bytesp[cnt]);
- printk("\n");
- }
- }
-#endif
-
- /* Reject any sp with supspect data */
- if (spp->eoc == 0 && ha->iocount > 1)
- printk
- ("%s: ihdlr, spp->eoc == 0, irq %d, reg 0x%x, count %d.\n",
- ha->board_name, irq, reg, ha->iocount);
- if (spp->cpp_index < 0 || spp->cpp_index >= shost->can_queue)
- printk
- ("%s: ihdlr, bad spp->cpp_index %d, irq %d, reg 0x%x, count %d.\n",
- ha->board_name, spp->cpp_index, irq, reg, ha->iocount);
- if (spp->eoc == 0 || spp->cpp_index < 0
- || spp->cpp_index >= shost->can_queue)
- goto handled;
-
- /* Find the mailbox to be serviced on this board */
- i = spp->cpp_index;
-
- cpp = &(ha->cp[i]);
-
-#if defined(DEBUG_GENERATE_ABORTS)
- if ((ha->iocount > 500) && ((ha->iocount % 500) < 3))
- goto handled;
-#endif
-
- if (ha->cp_stat[i] == IGNORE) {
- ha->cp_stat[i] = FREE;
- goto handled;
- } else if (ha->cp_stat[i] == LOCKED) {
- ha->cp_stat[i] = FREE;
- printk("%s: ihdlr, mbox %d unlocked, count %d.\n", ha->board_name, i,
- ha->iocount);
- goto handled;
- } else if (ha->cp_stat[i] == FREE) {
- printk("%s: ihdlr, mbox %d is free, count %d.\n", ha->board_name, i,
- ha->iocount);
- goto handled;
- } else if (ha->cp_stat[i] == IN_RESET)
- printk("%s: ihdlr, mbox %d is in reset.\n", ha->board_name, i);
- else if (ha->cp_stat[i] != IN_USE)
- panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n",
- ha->board_name, i, ha->cp_stat[i]);
-
- ha->cp_stat[i] = FREE;
- SCpnt = cpp->SCpnt;
-
- if (SCpnt == NULL)
- panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", ha->board_name, i);
-
- if (SCpnt->host_scribble == NULL)
- panic("%s: ihdlr, mbox %d, SCpnt %p garbled.\n", ha->board_name,
- i, SCpnt);
-
- if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: ihdlr, mbox %d, index mismatch %d.\n",
- ha->board_name, i,
- *(unsigned int *)SCpnt->host_scribble);
-
- sync_dma(i, ha);
-
- if (linked_comm && SCpnt->device->queue_depth > 2
- && TLDEV(SCpnt->device->type))
- flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), ha, 1);
-
- tstatus = status_byte(spp->target_status);
-
-#if defined(DEBUG_GENERATE_ERRORS)
- if ((ha->iocount > 500) && ((ha->iocount % 200) < 2))
- spp->adapter_status = 0x01;
-#endif
-
- switch (spp->adapter_status) {
- case ASOK: /* status OK */
-
- /* Forces a reset if a disk drive keeps returning BUSY */
- if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
- status = DID_ERROR << 16;
-
- /* If there was a bus reset, redo operation on each target */
- else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
- && ha->target_redo[SCpnt->device->id][SCpnt->
- device->
- channel])
- status = DID_BUS_BUSY << 16;
-
- /* Works around a flaw in scsi.c */
- else if (tstatus == CHECK_CONDITION
- && SCpnt->device->type == TYPE_DISK
- && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
- status = DID_BUS_BUSY << 16;
-
- else
- status = DID_OK << 16;
-
- if (tstatus == GOOD)
- ha->target_redo[SCpnt->device->id][SCpnt->device->
- channel] = 0;
-
- if (spp->target_status && SCpnt->device->type == TYPE_DISK &&
- (!(tstatus == CHECK_CONDITION && ha->iocount <= 1000 &&
- (SCpnt->sense_buffer[2] & 0xf) == NOT_READY)))
- printk("%s: ihdlr, target %d.%d:%d, "
- "target_status 0x%x, sense key 0x%x.\n",
- ha->board_name,
- SCpnt->device->channel, SCpnt->device->id,
- SCpnt->device->lun,
- spp->target_status, SCpnt->sense_buffer[2]);
-
- ha->target_to[SCpnt->device->id][SCpnt->device->channel] = 0;
-
- if (ha->last_retried_pid == SCpnt->serial_number)
- ha->retries = 0;
-
- break;
- case ASST: /* Selection Time Out */
- case 0x02: /* Command Time Out */
-
- if (ha->target_to[SCpnt->device->id][SCpnt->device->channel] > 1)
- status = DID_ERROR << 16;
- else {
- status = DID_TIME_OUT << 16;
- ha->target_to[SCpnt->device->id][SCpnt->device->
- channel]++;
- }
-
- break;
-
- /* Perform a limited number of internal retries */
- case 0x03: /* SCSI Bus Reset Received */
- case 0x04: /* Initial Controller Power-up */
-
- for (c = 0; c <= shost->max_channel; c++)
- for (k = 0; k < shost->max_id; k++)
- ha->target_redo[k][c] = 1;
-
- if (SCpnt->device->type != TYPE_TAPE
- && ha->retries < MAX_INTERNAL_RETRIES) {
-
-#if defined(DID_SOFT_ERROR)
- status = DID_SOFT_ERROR << 16;
-#else
- status = DID_BUS_BUSY << 16;
-#endif
-
- ha->retries++;
- ha->last_retried_pid = SCpnt->serial_number;
- } else
- status = DID_ERROR << 16;
-
- break;
- case 0x05: /* Unexpected Bus Phase */
- case 0x06: /* Unexpected Bus Free */
- case 0x07: /* Bus Parity Error */
- case 0x08: /* SCSI Hung */
- case 0x09: /* Unexpected Message Reject */
- case 0x0a: /* SCSI Bus Reset Stuck */
- case 0x0b: /* Auto Request-Sense Failed */
- case 0x0c: /* Controller Ram Parity Error */
- default:
- status = DID_ERROR << 16;
- break;
- }
-
- SCpnt->result = status | spp->target_status;
-
-#if defined(DEBUG_INTERRUPT)
- if (SCpnt->result || do_trace)
-#else
- if ((spp->adapter_status != ASOK && ha->iocount > 1000) ||
- (spp->adapter_status != ASOK &&
- spp->adapter_status != ASST && ha->iocount <= 1000) ||
- do_trace || msg_byte(spp->target_status))
-#endif
- scmd_printk(KERN_INFO, SCpnt, "ihdlr, mbox %2d, err 0x%x:%x,"
- " reg 0x%x, count %d.\n",
- i, spp->adapter_status, spp->target_status,
- reg, ha->iocount);
-
- unmap_dma(i, ha);
-
- /* Set the command state to inactive */
- SCpnt->host_scribble = NULL;
-
- SCpnt->scsi_done(SCpnt);
-
- if (do_trace)
- printk("%s: ihdlr, exit, irq %d, count %d.\n", ha->board_name,
- irq, ha->iocount);
-
- handled:
- return IRQ_HANDLED;
- none:
- return IRQ_NONE;
-}
-
-static irqreturn_t do_interrupt_handler(int dummy, void *shap)
-{
- struct Scsi_Host *shost;
- unsigned int j;
- unsigned long spin_flags;
- irqreturn_t ret;
-
- /* Check if the interrupt must be processed by this handler */
- if ((j = (unsigned int)((char *)shap - sha)) >= num_boards)
- return IRQ_NONE;
- shost = sh[j];
-
- spin_lock_irqsave(shost->host_lock, spin_flags);
- ret = ihdlr(shost);
- spin_unlock_irqrestore(shost->host_lock, spin_flags);
- return ret;
-}
-
-static int eata2x_release(struct Scsi_Host *shost)
-{
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- unsigned int i;
-
- for (i = 0; i < shost->can_queue; i++)
- kfree((&ha->cp[i])->sglist);
-
- for (i = 0; i < shost->can_queue; i++)
- pci_unmap_single(ha->pdev, ha->cp[i].cp_dma_addr,
- sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL);
-
- if (ha->sp_cpu_addr)
- pci_free_consistent(ha->pdev, sizeof(struct mssp),
- ha->sp_cpu_addr, ha->sp_dma_addr);
-
- free_irq(shost->irq, &sha[ha->board_number]);
-
- if (shost->dma_channel != NO_DMA)
- free_dma(shost->dma_channel);
-
- release_region(shost->io_port, shost->n_io_port);
- scsi_unregister(shost);
- return 0;
-}
-
-#include "scsi_module.c"
-
-#ifndef MODULE
-__setup("eata=", option_setup);
-#endif /* end MODULE */
diff --git a/drivers/scsi/eata_generic.h b/drivers/scsi/eata_generic.h
deleted file mode 100644
index 5016af5cf860..000000000000
--- a/drivers/scsi/eata_generic.h
+++ /dev/null
@@ -1,400 +0,0 @@
-/********************************************************
-* Header file for eata_dma.c and eata_pio.c *
-* Linux EATA SCSI drivers *
-* (c) 1993-96 Michael Neuffer *
-* mike@i-Connect.Net *
-* neuffer@mail.uni-mainz.de *
-*********************************************************
-* last change: 96/08/14 *
-********************************************************/
-
-
-#ifndef _EATA_GENERIC_H
-#define _EATA_GENERIC_H
-
-
-
-/*********************************************
- * Misc. definitions *
- *********************************************/
-
-#define R_LIMIT 0x20000
-
-#define MAXISA 4
-#define MAXEISA 16
-#define MAXPCI 16
-#define MAXIRQ 16
-#define MAXTARGET 16
-#define MAXCHANNEL 3
-
-#define IS_ISA 'I'
-#define IS_EISA 'E'
-#define IS_PCI 'P'
-
-#define BROKEN_INQUIRY 1
-
-#define BUSMASTER 0xff
-#define PIO 0xfe
-
-#define EATA_SIGNATURE 0x45415441 /* BIG ENDIAN coded "EATA" sig. */
-
-#define DPT_ID1 0x12
-#define DPT_ID2 0x14
-
-#define ATT_ID1 0x06
-#define ATT_ID2 0x94
-#define ATT_ID3 0x0
-
-#define NEC_ID1 0x38
-#define NEC_ID2 0xa3
-#define NEC_ID3 0x82
-
-
-#define EATA_CP_SIZE 44
-
-#define MAX_PCI_DEVICES 32 /* Maximum # Of Devices Per Bus */
-#define MAX_METHOD_2 16 /* Max Devices For Method 2 */
-#define MAX_PCI_BUS 16 /* Maximum # Of Busses Allowed */
-
-#define SG_SIZE 64
-#define SG_SIZE_BIG 252 /* max. 8096 elements, 64k */
-
-#define UPPER_DEVICE_QUEUE_LIMIT 64 /* The limit we have to set for the
- * device queue to keep the broken
- * midlevel SCSI code from producing
- * bogus timeouts
- */
-
-#define TYPE_DISK_QUEUE 16
-#define TYPE_TAPE_QUEUE 4
-#define TYPE_ROM_QUEUE 4
-#define TYPE_OTHER_QUEUE 2
-
-#define FREE 0
-#define OK 0
-#define NO_TIMEOUT 0
-#define USED 1
-#define TIMEOUT 2
-#define RESET 4
-#define LOCKED 8
-#define ABORTED 16
-
-#define READ 0
-#define WRITE 1
-#define OTHER 2
-
-#define HD(cmd) ((hostdata *)&(cmd->device->host->hostdata))
-#define CD(cmd) ((struct eata_ccb *)(cmd->host_scribble))
-#define SD(host) ((hostdata *)&(host->hostdata))
-
-/***********************************************
- * EATA Command & Register definitions *
- ***********************************************/
-#define PCI_REG_DPTconfig 0x40
-#define PCI_REG_PumpModeAddress 0x44
-#define PCI_REG_PumpModeData 0x48
-#define PCI_REG_ConfigParam1 0x50
-#define PCI_REG_ConfigParam2 0x54
-
-
-#define EATA_CMD_PIO_SETUPTEST 0xc6
-#define EATA_CMD_PIO_READ_CONFIG 0xf0
-#define EATA_CMD_PIO_SET_CONFIG 0xf1
-#define EATA_CMD_PIO_SEND_CP 0xf2
-#define EATA_CMD_PIO_RECEIVE_SP 0xf3
-#define EATA_CMD_PIO_TRUNC 0xf4
-
-#define EATA_CMD_RESET 0xf9
-#define EATA_CMD_IMMEDIATE 0xfa
-
-#define EATA_CMD_DMA_READ_CONFIG 0xfd
-#define EATA_CMD_DMA_SET_CONFIG 0xfe
-#define EATA_CMD_DMA_SEND_CP 0xff
-
-#define ECS_EMULATE_SENSE 0xd4
-
-#define EATA_GENERIC_ABORT 0x00
-#define EATA_SPECIFIC_RESET 0x01
-#define EATA_BUS_RESET 0x02
-#define EATA_SPECIFIC_ABORT 0x03
-#define EATA_QUIET_INTR 0x04
-#define EATA_COLD_BOOT_HBA 0x06 /* Only as a last resort */
-#define EATA_FORCE_IO 0x07
-
-#define HA_CTRLREG 0x206 /* control register for HBA */
-#define HA_CTRL_DISINT 0x02 /* CTRLREG: disable interrupts */
-#define HA_CTRL_RESCPU 0x04 /* CTRLREG: reset processor */
-#define HA_CTRL_8HEADS 0x08 /* CTRLREG: set for drives with*
- * >=8 heads (WD1003 rudimentary :-) */
-
-#define HA_WCOMMAND 0x07 /* command register offset */
-#define HA_WIFC 0x06 /* immediate command offset */
-#define HA_WCODE 0x05
-#define HA_WCODE2 0x04
-#define HA_WDMAADDR 0x02 /* DMA address LSB offset */
-#define HA_RAUXSTAT 0x08 /* aux status register offset*/
-#define HA_RSTATUS 0x07 /* status register offset */
-#define HA_RDATA 0x00 /* data register (16bit) */
-#define HA_WDATA 0x00 /* data register (16bit) */
-
-#define HA_ABUSY 0x01 /* aux busy bit */
-#define HA_AIRQ 0x02 /* aux IRQ pending bit */
-#define HA_SERROR 0x01 /* pr. command ended in error*/
-#define HA_SMORE 0x02 /* more data soon to come */
-#define HA_SCORR 0x04 /* data corrected */
-#define HA_SDRQ 0x08 /* data request active */
-#define HA_SSC 0x10 /* seek complete */
-#define HA_SFAULT 0x20 /* write fault */
-#define HA_SREADY 0x40 /* drive ready */
-#define HA_SBUSY 0x80 /* drive busy */
-#define HA_SDRDY HA_SSC+HA_SREADY+HA_SDRQ
-
-/**********************************************
- * Message definitions *
- **********************************************/
-
-#define HA_NO_ERROR 0x00 /* No Error */
-#define HA_ERR_SEL_TO 0x01 /* Selection Timeout */
-#define HA_ERR_CMD_TO 0x02 /* Command Timeout */
-#define HA_BUS_RESET 0x03 /* SCSI Bus Reset Received */
-#define HA_INIT_POWERUP 0x04 /* Initial Controller Power-up */
-#define HA_UNX_BUSPHASE 0x05 /* Unexpected Bus Phase */
-#define HA_UNX_BUS_FREE 0x06 /* Unexpected Bus Free */
-#define HA_BUS_PARITY 0x07 /* Bus Parity Error */
-#define HA_SCSI_HUNG 0x08 /* SCSI Hung */
-#define HA_UNX_MSGRJCT 0x09 /* Unexpected Message Rejected */
-#define HA_RESET_STUCK 0x0a /* SCSI Bus Reset Stuck */
-#define HA_RSENSE_FAIL 0x0b /* Auto Request-Sense Failed */
-#define HA_PARITY_ERR 0x0c /* Controller Ram Parity Error */
-#define HA_CP_ABORT_NA 0x0d /* Abort Message sent to non-active cmd */
-#define HA_CP_ABORTED 0x0e /* Abort Message sent to active cmd */
-#define HA_CP_RESET_NA 0x0f /* Reset Message sent to non-active cmd */
-#define HA_CP_RESET 0x10 /* Reset Message sent to active cmd */
-#define HA_ECC_ERR 0x11 /* Controller Ram ECC Error */
-#define HA_PCI_PARITY 0x12 /* PCI Parity Error */
-#define HA_PCI_MABORT 0x13 /* PCI Master Abort */
-#define HA_PCI_TABORT 0x14 /* PCI Target Abort */
-#define HA_PCI_STABORT 0x15 /* PCI Signaled Target Abort */
-
-/**********************************************
- * Other definitions *
- **********************************************/
-
-struct reg_bit { /* reading this one will clear the interrupt */
- __u8 error:1; /* previous command ended in an error */
- __u8 more:1; /* more DATA coming soon, poll BSY & DRQ (PIO) */
- __u8 corr:1; /* data read was successfully corrected with ECC*/
- __u8 drq:1; /* data request active */
- __u8 sc:1; /* seek complete */
- __u8 fault:1; /* write fault */
- __u8 ready:1; /* drive ready */
- __u8 busy:1; /* controller busy */
-};
-
-struct reg_abit { /* reading this won't clear the interrupt */
- __u8 abusy:1; /* auxiliary busy */
- __u8 irq:1; /* set when drive interrupt is asserted */
- __u8 dummy:6;
-};
-
-struct eata_register { /* EATA register set */
- __u8 data_reg[2]; /* R, couldn't figure this one out */
- __u8 cp_addr[4]; /* W, CP address register */
- union {
- __u8 command; /* W, command code: [read|set] conf, send CP*/
- struct reg_bit status; /* R, see register_bit1 */
- __u8 statusbyte;
- } ovr;
- struct reg_abit aux_stat; /* R, see register_bit2 */
-};
-
-struct get_conf { /* Read Configuration Array */
- __u32 len; /* Should return 0x22, 0x24, etc */
- __u32 signature; /* Signature MUST be "EATA" */
- __u8 version2:4,
- version:4; /* EATA Version level */
- __u8 OCS_enabled:1, /* Overlap Command Support enabled */
- TAR_support:1, /* SCSI Target Mode supported */
- TRNXFR:1, /* Truncate Transfer Cmd not necessary *
- * Only used in PIO Mode */
- MORE_support:1, /* MORE supported (only PIO Mode) */
- DMA_support:1, /* DMA supported Driver uses only *
- * this mode */
- DMA_valid:1, /* DRQ value in Byte 30 is valid */
- ATA:1, /* ATA device connected (not supported) */
- HAA_valid:1; /* Hostadapter Address is valid */
-
- __u16 cppadlen; /* Number of pad bytes send after CD data *
- * set to zero for DMA commands */
- __u8 scsi_id[4]; /* SCSI ID of controller 2-0 Byte 0 res. *
- * if not, zero is returned */
- __u32 cplen; /* CP length: number of valid cp bytes */
- __u32 splen; /* Number of bytes returned after *
- * Receive SP command */
- __u16 queuesiz; /* max number of queueable CPs */
- __u16 dummy;
- __u16 SGsiz; /* max number of SG table entries */
- __u8 IRQ:4, /* IRQ used this HA */
- IRQ_TR:1, /* IRQ Trigger: 0=edge, 1=level */
- SECOND:1, /* This is a secondary controller */
- DMA_channel:2; /* DRQ index, DRQ is 2comp of DRQX */
- __u8 sync; /* device at ID 7 tru 0 is running in *
- * synchronous mode, this will disappear */
- __u8 DSBLE:1, /* ISA i/o addressing is disabled */
- FORCADR:1, /* i/o address has been forced */
- SG_64K:1,
- SG_UAE:1,
- :4;
- __u8 MAX_ID:5, /* Max number of SCSI target IDs */
- MAX_CHAN:3; /* Number of SCSI busses on HBA */
- __u8 MAX_LUN; /* Max number of LUNs */
- __u8 :3,
- AUTOTRM:1,
- M1_inst:1,
- ID_qest:1, /* Raidnum ID is questionable */
- is_PCI:1, /* HBA is PCI */
- is_EISA:1; /* HBA is EISA */
- __u8 RAIDNUM; /* unique HBA identifier */
- __u8 unused[474];
-};
-
-struct eata_sg_list
-{
- __u32 data;
- __u32 len;
-};
-
-struct eata_ccb { /* Send Command Packet structure */
-
- __u8 SCSI_Reset:1, /* Cause a SCSI Bus reset on the cmd */
- HBA_Init:1, /* Cause Controller to reinitialize */
- Auto_Req_Sen:1, /* Do Auto Request Sense on errors */
- scatter:1, /* Data Ptr points to a SG Packet */
- Resrvd:1, /* RFU */
- Interpret:1, /* Interpret the SCSI cdb of own use */
- DataOut:1, /* Data Out phase with command */
- DataIn:1; /* Data In phase with command */
- __u8 reqlen; /* Request Sense Length *
- * Valid if Auto_Req_Sen=1 */
- __u8 unused[3];
- __u8 FWNEST:1, /* send cmd to phys RAID component */
- unused2:7;
- __u8 Phsunit:1, /* physical unit on mirrored pair */
- I_AT:1, /* inhibit address translation */
- I_HBA_C:1, /* HBA inhibit caching */
- unused3:5;
-
- __u8 cp_id:5, /* SCSI Device ID of target */
- cp_channel:3; /* SCSI Channel # of HBA */
- __u8 cp_lun:3,
- :2,
- cp_luntar:1, /* CP is for target ROUTINE */
- cp_dispri:1, /* Grant disconnect privilege */
- cp_identify:1; /* Always TRUE */
- __u8 cp_msg1; /* Message bytes 0-3 */
- __u8 cp_msg2;
- __u8 cp_msg3;
- __u8 cp_cdb[12]; /* Command Descriptor Block */
- __u32 cp_datalen; /* Data Transfer Length *
- * If scatter=1 len of sg package */
- void *cp_viraddr; /* address of this ccb */
- __u32 cp_dataDMA; /* Data Address, if scatter=1 *
- * address of scatter packet */
- __u32 cp_statDMA; /* address for Status Packet */
- __u32 cp_reqDMA; /* Request Sense Address, used if *
- * CP command ends with error */
- /* Additional CP info begins here */
- __u32 timestamp; /* Needed to measure command latency */
- __u32 timeout;
- __u8 sizeindex;
- __u8 rw_latency;
- __u8 retries;
- __u8 status; /* status of this queueslot */
- struct scsi_cmnd *cmd; /* address of cmd */
- struct eata_sg_list *sg_list;
-};
-
-
-struct eata_sp {
- __u8 hba_stat:7, /* HBA status */
- EOC:1; /* True if command finished */
- __u8 scsi_stat; /* Target SCSI status */
- __u8 reserved[2];
- __u32 residue_len; /* Number of bytes not transferred */
- struct eata_ccb *ccb; /* Address set in COMMAND PACKET */
- __u8 msg[12];
-};
-
-typedef struct hstd {
- __u8 vendor[9];
- __u8 name[18];
- __u8 revision[6];
- __u8 EATA_revision;
- __u32 firmware_revision;
- __u8 HBA_number;
- __u8 bustype; /* bustype of HBA */
- __u8 channel; /* # of avail. scsi channels */
- __u8 state; /* state of HBA */
- __u8 primary; /* true if primary */
- __u8 more_support:1, /* HBA supports MORE flag */
- immediate_support:1, /* HBA supports IMMEDIATE CMDs*/
- broken_INQUIRY:1; /* This is an EISA HBA with *
- * broken INQUIRY */
- __u8 do_latency; /* Latency measurement flag */
- __u32 reads[13];
- __u32 writes[13];
- __u32 reads_lat[12][4];
- __u32 writes_lat[12][4];
- __u32 all_lat[4];
- __u8 resetlevel[MAXCHANNEL];
- __u32 last_ccb; /* Last used ccb */
- __u32 cplen; /* size of CP in words */
- __u16 cppadlen; /* pad length of cp in words */
- __u16 queuesize;
- __u16 sgsize; /* # of entries in the SG list*/
- __u16 devflags; /* bits set for detected devices */
- __u8 hostid; /* SCSI ID of HBA */
- __u8 moresupport; /* HBA supports MORE flag */
- struct Scsi_Host *next;
- struct Scsi_Host *prev;
- struct pci_dev *pdev; /* PCI device or NULL for non PCI */
- struct eata_sp sp; /* status packet */
- struct eata_ccb ccb[0]; /* ccb array begins here */
-}hostdata;
-
-/* structure for max. 2 emulated drives */
-struct drive_geom_emul {
- __u8 trans; /* translation flag 1=transl */
- __u8 channel; /* SCSI channel number */
- __u8 HBA; /* HBA number (prim/sec) */
- __u8 id; /* drive id */
- __u8 lun; /* drive lun */
- __u32 heads; /* number of heads */
- __u32 sectors; /* number of sectors */
- __u32 cylinder; /* number of cylinders */
-};
-
-struct geom_emul {
- __u8 bios_drives; /* number of emulated drives */
- struct drive_geom_emul drv[2]; /* drive structures */
-};
-
-#endif /* _EATA_GENERIC_H */
-
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c
deleted file mode 100644
index d5f8362335d3..000000000000
--- a/drivers/scsi/eata_pio.c
+++ /dev/null
@@ -1,1001 +0,0 @@
-/************************************************************
- * *
- * Linux EATA SCSI PIO driver *
- * *
- * based on the CAM document CAM/89-004 rev. 2.0c, *
- * DPT's driver kit, some internal documents and source, *
- * and several other Linux scsi drivers and kernel docs. *
- * *
- * The driver currently: *
- * -supports all EATA-PIO boards *
- * -only supports DASD devices *
- * *
- * (c)1993-96 Michael Neuffer, Alfred Arnold *
- * neuffer@goofy.zdv.uni-mainz.de *
- * a.arnold@kfa-juelich.de *
- * *
- * Updated 2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> for *
- * Linux 2.5.x and the newer locking and error handling *
- * *
- * This program is free software; you can redistribute it *
- * and/or modify it under the terms of the GNU General *
- * Public License as published by the Free Software *
- * Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be *
- * useful, but WITHOUT ANY WARRANTY; without even the *
- * implied warranty of MERCHANTABILITY or FITNESS FOR A *
- * PARTICULAR PURPOSE. See the GNU General Public License *
- * for more details. *
- * *
- * You should have received a copy of the GNU General *
- * Public License along with this kernel; if not, write to *
- * the Free Software Foundation, Inc., 675 Mass Ave, *
- * Cambridge, MA 02139, USA. *
- * *
- * For the avoidance of doubt the "preferred form" of this *
- * code is one which is in an open non patent encumbered *
- * format. Where cryptographic key signing forms part of *
- * the process of creating an executable the information *
- * including keys needed to generate an equivalently *
- * functional executable are deemed to be part of the *
- * source code are deemed to be part of the source code. *
- * *
- ************************************************************
- * last change: 2002/11/02 OS: Linux 2.5.45 *
- ************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-
-#include <asm/io.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-
-#include "eata_generic.h"
-#include "eata_pio.h"
-
-
-static unsigned int ISAbases[MAXISA] = {
- 0x1F0, 0x170, 0x330, 0x230
-};
-
-static unsigned int ISAirqs[MAXISA] = {
- 14, 12, 15, 11
-};
-
-static unsigned char EISAbases[] = {
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1
-};
-
-static unsigned int registered_HBAs;
-static struct Scsi_Host *last_HBA;
-static struct Scsi_Host *first_HBA;
-static unsigned char reg_IRQ[16];
-static unsigned char reg_IRQL[16];
-static unsigned long int_counter;
-static unsigned long queue_counter;
-
-static struct scsi_host_template driver_template;
-
-/*
- * eata_proc_info
- * inout : decides on the direction of the dataflow and the meaning of the
- * variables
- * buffer: If inout==FALSE data is being written to it else read from it
- * *start: If inout==FALSE start of the valid data in the buffer
- * offset: If inout==FALSE offset from the beginning of the imaginary file
- * from which we start writing into the buffer
- * length: If inout==FALSE max number of bytes to be written into the buffer
- * else number of bytes in the buffer
- */
-static int eata_pio_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset,
- int length, int rw)
-{
- int len = 0;
- off_t begin = 0, pos = 0;
-
- if (rw)
- return -ENOSYS;
-
- len += sprintf(buffer+len, "EATA (Extended Attachment) PIO driver version: "
- "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
- len += sprintf(buffer + len, "queued commands: %10ld\n"
- "processed interrupts:%10ld\n", queue_counter, int_counter);
- len += sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
- shost->host_no, SD(shost)->name);
- len += sprintf(buffer + len, "Firmware revision: v%s\n",
- SD(shost)->revision);
- len += sprintf(buffer + len, "IO: PIO\n");
- len += sprintf(buffer + len, "Base IO : %#.4x\n", (u32) shost->base);
- len += sprintf(buffer + len, "Host Bus: %s\n",
- (SD(shost)->bustype == 'P')?"PCI ":
- (SD(shost)->bustype == 'E')?"EISA":"ISA ");
-
- pos = begin + len;
-
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length)
- goto stop_output;
-
-stop_output:
- DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
- *start = buffer + (offset - begin); /* Start of wanted data */
- len -= (offset - begin); /* Start slop */
- if (len > length)
- len = length; /* Ending slop */
- DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
-
- return len;
-}
-
-static int eata_pio_release(struct Scsi_Host *sh)
-{
- hostdata *hd = SD(sh);
- if (sh->irq && reg_IRQ[sh->irq] == 1)
- free_irq(sh->irq, NULL);
- else
- reg_IRQ[sh->irq]--;
- if (SD(sh)->channel == 0) {
- if (sh->io_port && sh->n_io_port)
- release_region(sh->io_port, sh->n_io_port);
- }
- /* At this point the PCI reference can go */
- if (hd->pdev)
- pci_dev_put(hd->pdev);
- return 1;
-}
-
-static void IncStat(struct scsi_pointer *SCp, unsigned int Increment)
-{
- SCp->ptr += Increment;
- if ((SCp->this_residual -= Increment) == 0) {
- if ((--SCp->buffers_residual) == 0)
- SCp->Status = 0;
- else {
- SCp->buffer++;
- SCp->ptr = sg_virt(SCp->buffer);
- SCp->this_residual = SCp->buffer->length;
- }
- }
-}
-
-static irqreturn_t eata_pio_int_handler(int irq, void *dev_id);
-
-static irqreturn_t do_eata_pio_int_handler(int irq, void *dev_id)
-{
- unsigned long flags;
- struct Scsi_Host *dev = dev_id;
- irqreturn_t ret;
-
- spin_lock_irqsave(dev->host_lock, flags);
- ret = eata_pio_int_handler(irq, dev_id);
- spin_unlock_irqrestore(dev->host_lock, flags);
- return ret;
-}
-
-static irqreturn_t eata_pio_int_handler(int irq, void *dev_id)
-{
- unsigned int eata_stat = 0xfffff;
- struct scsi_cmnd *cmd;
- hostdata *hd;
- struct eata_ccb *cp;
- unsigned long base;
- unsigned int x, z;
- struct Scsi_Host *sh;
- unsigned short zwickel = 0;
- unsigned char stat, odd;
- irqreturn_t ret = IRQ_NONE;
-
- for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev)
- {
- if (sh->irq != irq)
- continue;
- if (inb(sh->base + HA_RSTATUS) & HA_SBUSY)
- continue;
-
- int_counter++;
- ret = IRQ_HANDLED;
-
- hd = SD(sh);
-
- cp = &hd->ccb[0];
- cmd = cp->cmd;
- base = cmd->device->host->base;
-
- do {
- stat = inb(base + HA_RSTATUS);
- if (stat & HA_SDRQ) {
- if (cp->DataIn) {
- z = 256;
- odd = 0;
- while ((cmd->SCp.Status) && ((z > 0) || (odd))) {
- if (odd) {
- *(cmd->SCp.ptr) = zwickel >> 8;
- IncStat(&cmd->SCp, 1);
- odd = 0;
- }
- x = min_t(unsigned int, z, cmd->SCp.this_residual / 2);
- insw(base + HA_RDATA, cmd->SCp.ptr, x);
- z -= x;
- IncStat(&cmd->SCp, 2 * x);
- if ((z > 0) && (cmd->SCp.this_residual == 1)) {
- zwickel = inw(base + HA_RDATA);
- *(cmd->SCp.ptr) = zwickel & 0xff;
- IncStat(&cmd->SCp, 1);
- z--;
- odd = 1;
- }
- }
- while (z > 0) {
- zwickel = inw(base + HA_RDATA);
- z--;
- }
- } else { /* cp->DataOut */
-
- odd = 0;
- z = 256;
- while ((cmd->SCp.Status) && ((z > 0) || (odd))) {
- if (odd) {
- zwickel += *(cmd->SCp.ptr) << 8;
- IncStat(&cmd->SCp, 1);
- outw(zwickel, base + HA_RDATA);
- z--;
- odd = 0;
- }
- x = min_t(unsigned int, z, cmd->SCp.this_residual / 2);
- outsw(base + HA_RDATA, cmd->SCp.ptr, x);
- z -= x;
- IncStat(&cmd->SCp, 2 * x);
- if ((z > 0) && (cmd->SCp.this_residual == 1)) {
- zwickel = *(cmd->SCp.ptr);
- zwickel &= 0xff;
- IncStat(&cmd->SCp, 1);
- odd = 1;
- }
- }
- while (z > 0 || odd) {
- outw(zwickel, base + HA_RDATA);
- z--;
- odd = 0;
- }
- }
- }
- }
- while ((stat & HA_SDRQ) || ((stat & HA_SMORE) && hd->moresupport));
-
- /* terminate handler if HBA goes busy again, i.e. transfers
- * more data */
-
- if (stat & HA_SBUSY)
- break;
-
- /* OK, this is quite stupid, but I haven't found any correct
- * way to get HBA&SCSI status so far */
-
- if (!(inb(base + HA_RSTATUS) & HA_SERROR)) {
- cmd->result = (DID_OK << 16);
- hd->devflags |= (1 << cp->cp_id);
- } else if (hd->devflags & (1 << cp->cp_id))
- cmd->result = (DID_OK << 16) + 0x02;
- else
- cmd->result = (DID_NO_CONNECT << 16);
-
- if (cp->status == LOCKED) {
- cp->status = FREE;
- eata_stat = inb(base + HA_RSTATUS);
- printk(KERN_CRIT "eata_pio: int_handler, freeing locked " "queueslot\n");
- return ret;
- }
-#if DBG_INTR2
- if (stat != 0x50)
- printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, cmd->result);
-#endif
-
- cp->status = FREE; /* now we can release the slot */
-
- cmd->scsi_done(cmd);
- }
-
- return ret;
-}
-
-static inline unsigned int eata_pio_send_command(unsigned long base, unsigned char command)
-{
- unsigned int loop = 50;
-
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- if (--loop == 0)
- return 1;
-
- /* Enable interrupts for HBA. It is not the best way to do it at this
- * place, but I hope that it doesn't interfere with the IDE driver
- * initialization this way */
-
- outb(HA_CTRL_8HEADS, base + HA_CTRLREG);
-
- outb(command, base + HA_WCOMMAND);
- return 0;
-}
-
-static int eata_pio_queue_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
-{
- unsigned int x, y;
- unsigned long base;
-
- hostdata *hd;
- struct Scsi_Host *sh;
- struct eata_ccb *cp;
-
- queue_counter++;
-
- hd = HD(cmd);
- sh = cmd->device->host;
- base = sh->base;
-
- /* use only slot 0, as 2001 can handle only one cmd at a time */
-
- y = x = 0;
-
- if (hd->ccb[y].status != FREE) {
-
- DBG(DBG_QUEUE, printk(KERN_EMERG "can_queue %d, x %d, y %d\n", sh->can_queue, x, y));
-#if DEBUG_EATA
- panic(KERN_EMERG "eata_pio: run out of queue slots cmdno:%ld " "intrno: %ld\n", queue_counter, int_counter);
-#else
- panic(KERN_EMERG "eata_pio: run out of queue slots....\n");
-#endif
- }
-
- cp = &hd->ccb[y];
-
- memset(cp, 0, sizeof(struct eata_ccb));
-
- cp->status = USED; /* claim free slot */
-
- DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd,
- "eata_pio_queue 0x%p, y %d\n", cmd, y));
-
- cmd->scsi_done = (void *) done;
-
- if (cmd->sc_data_direction == DMA_TO_DEVICE)
- cp->DataOut = 1; /* Output mode */
- else
- cp->DataIn = 0; /* Input mode */
-
- cp->Interpret = (cmd->device->id == hd->hostid);
- cp->cp_datalen = cpu_to_be32(scsi_bufflen(cmd));
- cp->Auto_Req_Sen = 0;
- cp->cp_reqDMA = 0;
- cp->reqlen = 0;
-
- cp->cp_id = cmd->device->id;
- cp->cp_lun = cmd->device->lun;
- cp->cp_dispri = 0;
- cp->cp_identify = 1;
- memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd));
-
- cp->cp_statDMA = 0;
-
- cp->cp_viraddr = cp;
- cp->cmd = cmd;
- cmd->host_scribble = (char *) &hd->ccb[y];
-
- if (!scsi_bufflen(cmd)) {
- cmd->SCp.buffers_residual = 1;
- cmd->SCp.ptr = NULL;
- cmd->SCp.this_residual = 0;
- cmd->SCp.buffer = NULL;
- } else {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.buffers_residual = scsi_sg_count(cmd);
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- }
- cmd->SCp.Status = (cmd->SCp.this_residual != 0); /* TRUE as long as bytes
- * are to transfer */
-
- if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) {
- cmd->result = DID_BUS_BUSY << 16;
- scmd_printk(KERN_NOTICE, cmd,
- "eata_pio_queue pid 0x%p, HBA busy, "
- "returning DID_BUS_BUSY, done.\n", cmd);
- done(cmd);
- cp->status = FREE;
- return 0;
- }
- /* FIXME: timeout */
- while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- cpu_relax();
- outsw(base + HA_RDATA, cp, hd->cplen);
- outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
- for (x = 0; x < hd->cppadlen; x++)
- outw(0, base + HA_RDATA);
-
- DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd,
- "Queued base %#.4lx cmd: 0x%p "
- "slot %d irq %d\n", sh->base, cmd, y, sh->irq));
-
- return 0;
-}
-
-static DEF_SCSI_QCMD(eata_pio_queue)
-
-static int eata_pio_abort(struct scsi_cmnd *cmd)
-{
- unsigned int loop = 100;
-
- DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd,
- "eata_pio_abort called pid: 0x%p\n", cmd));
-
- while (inb(cmd->device->host->base + HA_RAUXSTAT) & HA_ABUSY)
- if (--loop == 0) {
- printk(KERN_WARNING "eata_pio: abort, timeout error.\n");
- return FAILED;
- }
- if (CD(cmd)->status == FREE) {
- DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n"));
- return FAILED;
- }
- if (CD(cmd)->status == USED) {
- DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n"));
- /* We want to sleep a bit more here */
- return FAILED; /* SNOOZE */
- }
- if (CD(cmd)->status == RESET) {
- printk(KERN_WARNING "eata_pio: abort, command reset error.\n");
- return FAILED;
- }
- if (CD(cmd)->status == LOCKED) {
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot " "locked.\n"));
- return FAILED;
- }
- panic("eata_pio: abort: invalid slot status\n");
-}
-
-static int eata_pio_host_reset(struct scsi_cmnd *cmd)
-{
- unsigned int x, limit = 0;
- unsigned char success = 0;
- struct scsi_cmnd *sp;
- struct Scsi_Host *host = cmd->device->host;
-
- DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd,
- "eata_pio_reset called\n"));
-
- spin_lock_irq(host->host_lock);
-
- if (HD(cmd)->state == RESET) {
- printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n");
- spin_unlock_irq(host->host_lock);
- return FAILED;
- }
-
- /* force all slots to be free */
-
- for (x = 0; x < cmd->device->host->can_queue; x++) {
-
- if (HD(cmd)->ccb[x].status == FREE)
- continue;
-
- sp = HD(cmd)->ccb[x].cmd;
- HD(cmd)->ccb[x].status = RESET;
- printk(KERN_WARNING "eata_pio_reset: slot %d in reset.\n", x);
-
- if (sp == NULL)
- panic("eata_pio_reset: slot %d, sp==NULL.\n", x);
- }
-
- /* hard reset the HBA */
- outb(EATA_CMD_RESET, cmd->device->host->base + HA_WCOMMAND);
-
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n"));
- HD(cmd)->state = RESET;
-
- spin_unlock_irq(host->host_lock);
- msleep(3000);
- spin_lock_irq(host->host_lock);
-
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, " "loops %d.\n", limit));
-
- for (x = 0; x < cmd->device->host->can_queue; x++) {
-
- /* Skip slots already set free by interrupt */
- if (HD(cmd)->ccb[x].status != RESET)
- continue;
-
- sp = HD(cmd)->ccb[x].cmd;
- sp->result = DID_RESET << 16;
-
- /* This mailbox is terminated */
- printk(KERN_WARNING "eata_pio_reset: reset ccb %d.\n", x);
- HD(cmd)->ccb[x].status = FREE;
-
- sp->scsi_done(sp);
- }
-
- HD(cmd)->state = 0;
-
- spin_unlock_irq(host->host_lock);
-
- if (success) { /* hmmm... */
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, success.\n"));
- return SUCCESS;
- } else {
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, wakeup.\n"));
- return FAILED;
- }
-}
-
-static char *get_pio_board_data(unsigned long base, unsigned int irq, unsigned int id, unsigned long cplen, unsigned short cppadlen)
-{
- struct eata_ccb cp;
- static char buff[256];
- int z;
-
- memset(&cp, 0, sizeof(struct eata_ccb));
- memset(buff, 0, sizeof(buff));
-
- cp.DataIn = 1;
- cp.Interpret = 1; /* Interpret command */
-
- cp.cp_datalen = cpu_to_be32(254);
- cp.cp_dataDMA = cpu_to_be32(0);
-
- cp.cp_id = id;
- cp.cp_lun = 0;
-
- cp.cp_cdb[0] = INQUIRY;
- cp.cp_cdb[1] = 0;
- cp.cp_cdb[2] = 0;
- cp.cp_cdb[3] = 0;
- cp.cp_cdb[4] = 254;
- cp.cp_cdb[5] = 0;
-
- if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP))
- return NULL;
-
- while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- cpu_relax();
-
- outsw(base + HA_RDATA, &cp, cplen);
- outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
- for (z = 0; z < cppadlen; z++)
- outw(0, base + HA_RDATA);
-
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- cpu_relax();
-
- if (inb(base + HA_RSTATUS) & HA_SERROR)
- return NULL;
- else if (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- return NULL;
- else {
- insw(base + HA_RDATA, &buff, 127);
- while (inb(base + HA_RSTATUS) & HA_SDRQ)
- inw(base + HA_RDATA);
- return buff;
- }
-}
-
-static int get_pio_conf_PIO(unsigned long base, struct get_conf *buf)
-{
- unsigned long loop = HZ / 2;
- int z;
- unsigned short *p;
-
- if (!request_region(base, 9, "eata_pio"))
- return 0;
-
- memset(buf, 0, sizeof(struct get_conf));
-
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- if (--loop == 0)
- goto fail;
-
- DBG(DBG_PIO && DBG_PROBE, printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#lx\n", base));
- eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG);
-
- loop = 50;
- for (p = (unsigned short *) buf; (long) p <= ((long) buf + (sizeof(struct get_conf) / 2)); p++) {
- while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- if (--loop == 0)
- goto fail;
-
- loop = 50;
- *p = inw(base + HA_RDATA);
- }
- if (inb(base + HA_RSTATUS) & HA_SERROR) {
- DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during "
- "transfer for HBA at %lx\n", base));
- goto fail;
- }
-
- if (cpu_to_be32(EATA_SIGNATURE) != buf->signature)
- goto fail;
-
- DBG(DBG_PIO && DBG_PROBE, printk(KERN_NOTICE "EATA Controller found "
- "at %#4lx EATA Level: %x\n",
- base, (unsigned int) (buf->version)));
-
- while (inb(base + HA_RSTATUS) & HA_SDRQ)
- inw(base + HA_RDATA);
-
- if (!ALLOW_DMA_BOARDS) {
- for (z = 0; z < MAXISA; z++)
- if (base == ISAbases[z]) {
- buf->IRQ = ISAirqs[z];
- break;
- }
- }
-
- return 1;
-
- fail:
- release_region(base, 9);
- return 0;
-}
-
-static void print_pio_config(struct get_conf *gc)
-{
- printk("Please check values: (read config data)\n");
- printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n", be32_to_cpu(gc->len), gc->version, gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support);
- printk("HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n", gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2], gc->scsi_id[1], be16_to_cpu(gc->queuesiz), be16_to_cpu(gc->SGsiz), gc->SECOND);
- printk("IRQ:%d IRQT:%d FORCADR:%d MCH:%d RIDQ:%d\n", gc->IRQ, gc->IRQ_TR, gc->FORCADR, gc->MAX_CHAN, gc->ID_qest);
-}
-
-static unsigned int print_selftest(unsigned int base)
-{
- unsigned char buffer[512];
-#ifdef VERBOSE_SETUP
- int z;
-#endif
-
- printk("eata_pio: executing controller self test & setup...\n");
- while (inb(base + HA_RSTATUS) & HA_SBUSY);
- outb(EATA_CMD_PIO_SETUPTEST, base + HA_WCOMMAND);
- do {
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- /* nothing */ ;
- if (inb(base + HA_RSTATUS) & HA_SDRQ) {
- insw(base + HA_RDATA, &buffer, 256);
-#ifdef VERBOSE_SETUP
- /* no beeps please... */
- for (z = 0; z < 511 && buffer[z]; z++)
- if (buffer[z] != 7)
- printk("%c", buffer[z]);
-#endif
- }
- } while (inb(base + HA_RSTATUS) & (HA_SBUSY | HA_SDRQ));
-
- return (!(inb(base + HA_RSTATUS) & HA_SERROR));
-}
-
-static int register_pio_HBA(long base, struct get_conf *gc, struct pci_dev *pdev)
-{
- unsigned long size = 0;
- char *buff;
- unsigned long cplen;
- unsigned short cppadlen;
- struct Scsi_Host *sh;
- hostdata *hd;
-
- DBG(DBG_REGISTER, print_pio_config(gc));
-
- if (gc->DMA_support) {
- printk("HBA at %#.4lx supports DMA. Please use EATA-DMA driver.\n", base);
- if (!ALLOW_DMA_BOARDS)
- return 0;
- }
-
- if ((buff = get_pio_board_data(base, gc->IRQ, gc->scsi_id[3], cplen = (cpu_to_be32(gc->cplen) + 1) / 2, cppadlen = (cpu_to_be16(gc->cppadlen) + 1) / 2)) == NULL) {
- printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", base);
- return 0;
- }
-
- if (!print_selftest(base) && !ALLOW_DMA_BOARDS) {
- printk("HBA at %#lx failed while performing self test & setup.\n", base);
- return 0;
- }
-
- size = sizeof(hostdata) + (sizeof(struct eata_ccb) * be16_to_cpu(gc->queuesiz));
-
- sh = scsi_register(&driver_template, size);
- if (sh == NULL)
- return 0;
-
- if (!reg_IRQ[gc->IRQ]) { /* Interrupt already registered ? */
- if (!request_irq(gc->IRQ, do_eata_pio_int_handler, IRQF_DISABLED, "EATA-PIO", sh)) {
- reg_IRQ[gc->IRQ]++;
- if (!gc->IRQ_TR)
- reg_IRQL[gc->IRQ] = 1; /* IRQ is edge triggered */
- } else {
- printk("Couldn't allocate IRQ %d, Sorry.\n", gc->IRQ);
- return 0;
- }
- } else { /* More than one HBA on this IRQ */
- if (reg_IRQL[gc->IRQ]) {
- printk("Can't support more than one HBA on this IRQ,\n" " if the IRQ is edge triggered. Sorry.\n");
- return 0;
- } else
- reg_IRQ[gc->IRQ]++;
- }
-
- hd = SD(sh);
-
- memset(hd->ccb, 0, (sizeof(struct eata_ccb) * be16_to_cpu(gc->queuesiz)));
- memset(hd->reads, 0, sizeof(hd->reads));
-
- strlcpy(SD(sh)->vendor, &buff[8], sizeof(SD(sh)->vendor));
- strlcpy(SD(sh)->name, &buff[16], sizeof(SD(sh)->name));
- SD(sh)->revision[0] = buff[32];
- SD(sh)->revision[1] = buff[33];
- SD(sh)->revision[2] = buff[34];
- SD(sh)->revision[3] = '.';
- SD(sh)->revision[4] = buff[35];
- SD(sh)->revision[5] = 0;
-
- switch (be32_to_cpu(gc->len)) {
- case 0x1c:
- SD(sh)->EATA_revision = 'a';
- break;
- case 0x1e:
- SD(sh)->EATA_revision = 'b';
- break;
- case 0x22:
- SD(sh)->EATA_revision = 'c';
- break;
- case 0x24:
- SD(sh)->EATA_revision = 'z';
- default:
- SD(sh)->EATA_revision = '?';
- }
-
- if (be32_to_cpu(gc->len) >= 0x22) {
- if (gc->is_PCI)
- hd->bustype = IS_PCI;
- else if (gc->is_EISA)
- hd->bustype = IS_EISA;
- else
- hd->bustype = IS_ISA;
- } else {
- if (buff[21] == '4')
- hd->bustype = IS_PCI;
- else if (buff[21] == '2')
- hd->bustype = IS_EISA;
- else
- hd->bustype = IS_ISA;
- }
-
- SD(sh)->cplen = cplen;
- SD(sh)->cppadlen = cppadlen;
- SD(sh)->hostid = gc->scsi_id[3];
- SD(sh)->devflags = 1 << gc->scsi_id[3];
- SD(sh)->moresupport = gc->MORE_support;
- sh->unique_id = base;
- sh->base = base;
- sh->io_port = base;
- sh->n_io_port = 9;
- sh->irq = gc->IRQ;
- sh->dma_channel = PIO;
- sh->this_id = gc->scsi_id[3];
- sh->can_queue = 1;
- sh->cmd_per_lun = 1;
- sh->sg_tablesize = SG_ALL;
-
- hd->channel = 0;
-
- hd->pdev = pci_dev_get(pdev); /* Keep a PCI reference */
-
- sh->max_id = 8;
- sh->max_lun = 8;
-
- if (gc->SECOND)
- hd->primary = 0;
- else
- hd->primary = 1;
-
- hd->next = NULL; /* build a linked list of all HBAs */
- hd->prev = last_HBA;
- if (hd->prev != NULL)
- SD(hd->prev)->next = sh;
- last_HBA = sh;
- if (first_HBA == NULL)
- first_HBA = sh;
- registered_HBAs++;
- return (1);
-}
-
-static void find_pio_ISA(struct get_conf *buf)
-{
- int i;
-
- for (i = 0; i < MAXISA; i++) {
- if (!ISAbases[i])
- continue;
- if (!get_pio_conf_PIO(ISAbases[i], buf))
- continue;
- if (!register_pio_HBA(ISAbases[i], buf, NULL))
- release_region(ISAbases[i], 9);
- else
- ISAbases[i] = 0;
- }
- return;
-}
-
-static void find_pio_EISA(struct get_conf *buf)
-{
- u32 base;
- int i;
-
-#ifdef CHECKPAL
- u8 pal1, pal2, pal3;
-#endif
-
- for (i = 0; i < MAXEISA; i++) {
- if (EISAbases[i]) { /* Still a possibility ? */
-
- base = 0x1c88 + (i * 0x1000);
-#ifdef CHECKPAL
- pal1 = inb((u16) base - 8);
- pal2 = inb((u16) base - 7);
- pal3 = inb((u16) base - 6);
-
- if (((pal1 == 0x12) && (pal2 == 0x14)) || ((pal1 == 0x38) && (pal2 == 0xa3) && (pal3 == 0x82)) || ((pal1 == 0x06) && (pal2 == 0x94) && (pal3 == 0x24))) {
- DBG(DBG_PROBE, printk(KERN_NOTICE "EISA EATA id tags found: " "%x %x %x \n", (int) pal1, (int) pal2, (int) pal3));
-#endif
- if (get_pio_conf_PIO(base, buf)) {
- DBG(DBG_PROBE && DBG_EISA, print_pio_config(buf));
- if (buf->IRQ) {
- if (!register_pio_HBA(base, buf, NULL))
- release_region(base, 9);
- } else {
- printk(KERN_NOTICE "eata_dma: No valid IRQ. HBA " "removed from list\n");
- release_region(base, 9);
- }
- }
- /* Nothing found here so we take it from the list */
- EISAbases[i] = 0;
-#ifdef CHECKPAL
- }
-#endif
- }
- }
- return;
-}
-
-static void find_pio_PCI(struct get_conf *buf)
-{
-#ifndef CONFIG_PCI
- printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
-#else
- struct pci_dev *dev = NULL;
- unsigned long base, x;
-
- while ((dev = pci_get_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev)) != NULL) {
- DBG(DBG_PROBE && DBG_PCI, printk("eata_pio: find_PCI, HBA at %s\n", pci_name(dev)));
- if (pci_enable_device(dev))
- continue;
- pci_set_master(dev);
- base = pci_resource_flags(dev, 0);
- if (base & IORESOURCE_MEM) {
- printk("eata_pio: invalid base address of device %s\n", pci_name(dev));
- continue;
- }
- base = pci_resource_start(dev, 0);
- /* EISA tag there ? */
- if ((inb(base) == 0x12) && (inb(base + 1) == 0x14))
- continue; /* Jep, it's forced, so move on */
- base += 0x10; /* Now, THIS is the real address */
- if (base != 0x1f8) {
- /* We didn't find it in the primary search */
- if (get_pio_conf_PIO(base, buf)) {
- if (buf->FORCADR) { /* If the address is forced */
- release_region(base, 9);
- continue; /* we'll find it later */
- }
-
- /* OK. We made it till here, so we can go now
- * and register it. We only have to check and
- * eventually remove it from the EISA and ISA list
- */
-
- if (!register_pio_HBA(base, buf, dev)) {
- release_region(base, 9);
- continue;
- }
-
- if (base < 0x1000) {
- for (x = 0; x < MAXISA; ++x) {
- if (ISAbases[x] == base) {
- ISAbases[x] = 0;
- break;
- }
- }
- } else if ((base & 0x0fff) == 0x0c88) {
- x = (base >> 12) & 0x0f;
- EISAbases[x] = 0;
- }
- }
-#ifdef CHECK_BLINK
- else if (check_blink_state(base)) {
- printk("eata_pio: HBA is in BLINK state.\n" "Consult your HBAs manual to correct this.\n");
- }
-#endif
- }
- }
-#endif /* #ifndef CONFIG_PCI */
-}
-
-static int eata_pio_detect(struct scsi_host_template *tpnt)
-{
- struct Scsi_Host *HBA_ptr;
- struct get_conf gc;
- int i;
-
- find_pio_PCI(&gc);
- find_pio_EISA(&gc);
- find_pio_ISA(&gc);
-
- for (i = 0; i <= MAXIRQ; i++)
- if (reg_IRQ[i])
- request_irq(i, do_eata_pio_int_handler, IRQF_DISABLED, "EATA-PIO", NULL);
-
- HBA_ptr = first_HBA;
-
- if (registered_HBAs != 0) {
- printk("EATA (Extended Attachment) PIO driver version: %d.%d%s\n"
- "(c) 1993-95 Michael Neuffer, neuffer@goofy.zdv.uni-mainz.de\n" " Alfred Arnold, a.arnold@kfa-juelich.de\n" "This release only supports DASD devices (harddisks)\n", VER_MAJOR, VER_MINOR, VER_SUB);
-
- printk("Registered HBAs:\n");
- printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: Ch: ID: Pr:" " QS: SG: CPL:\n");
- for (i = 1; i <= registered_HBAs; i++) {
- printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4lx %2d %d %d %c"
- " %2d %2d %2d\n",
- HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
- SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P') ?
- "PCI " : (SD(HBA_ptr)->bustype == 'E') ? "EISA" : "ISA ",
- HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel, HBA_ptr->this_id,
- SD(HBA_ptr)->primary ? 'Y' : 'N', HBA_ptr->can_queue,
- HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
- HBA_ptr = SD(HBA_ptr)->next;
- }
- }
- return (registered_HBAs);
-}
-
-static struct scsi_host_template driver_template = {
- .proc_name = "eata_pio",
- .name = "EATA (Extended Attachment) PIO driver",
- .proc_info = eata_pio_proc_info,
- .detect = eata_pio_detect,
- .release = eata_pio_release,
- .queuecommand = eata_pio_queue,
- .eh_abort_handler = eata_pio_abort,
- .eh_host_reset_handler = eata_pio_host_reset,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-MODULE_AUTHOR("Michael Neuffer, Alfred Arnold");
-MODULE_DESCRIPTION("EATA SCSI PIO driver");
-MODULE_LICENSE("GPL");
-
-#include "scsi_module.c"
diff --git a/drivers/scsi/eata_pio.h b/drivers/scsi/eata_pio.h
deleted file mode 100644
index 7deeb935748b..000000000000
--- a/drivers/scsi/eata_pio.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/********************************************************
-* Header file for eata_pio.c Linux EATA-PIO SCSI driver *
-* (c) 1993-96 Michael Neuffer *
-*********************************************************
-* last change: 2002/11/02 *
-********************************************************/
-
-
-#ifndef _EATA_PIO_H
-#define _EATA_PIO_H
-
-#define VER_MAJOR 0
-#define VER_MINOR 0
-#define VER_SUB "1b"
-
-/************************************************************************
- * Here you can switch parts of the code on and of *
- ************************************************************************/
-
-#define VERBOSE_SETUP /* show startup screen of 2001 */
-#define ALLOW_DMA_BOARDS 1
-
-/************************************************************************
- * Debug options. *
- * Enable DEBUG and whichever options you require. *
- ************************************************************************/
-#define DEBUG_EATA 1 /* Enable debug code. */
-#define DPT_DEBUG 0 /* Bobs special */
-#define DBG_DELAY 0 /* Build in delays so debug messages can be
- * be read before they vanish of the top of
- * the screen!
- */
-#define DBG_PROBE 0 /* Debug probe routines. */
-#define DBG_ISA 0 /* Trace ISA routines */
-#define DBG_EISA 0 /* Trace EISA routines */
-#define DBG_PCI 0 /* Trace PCI routines */
-#define DBG_PIO 0 /* Trace get_config_PIO */
-#define DBG_COM 0 /* Trace command call */
-#define DBG_QUEUE 0 /* Trace command queueing. */
-#define DBG_INTR 0 /* Trace interrupt service routine. */
-#define DBG_INTR2 0 /* Trace interrupt service routine. */
-#define DBG_PROC 0 /* Debug proc-fs related statistics */
-#define DBG_PROC_WRITE 0
-#define DBG_REGISTER 0 /* */
-#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort) */
-
-#if DEBUG_EATA
-#define DBG(x, y) if ((x)) {y;}
-#else
-#define DBG(x, y)
-#endif
-
-#endif /* _EATA_PIO_H */
diff --git a/drivers/scsi/elx/Kconfig b/drivers/scsi/elx/Kconfig
new file mode 100644
index 000000000000..831daea7a951
--- /dev/null
+++ b/drivers/scsi/elx/Kconfig
@@ -0,0 +1,9 @@
+config SCSI_EFCT
+ tristate "Emulex Fibre Channel Target"
+ depends on PCI && SCSI
+ depends on TARGET_CORE
+ depends on SCSI_FC_ATTRS
+ select CRC_T10DIF
+ help
+ The efct driver provides enhanced SCSI Target Mode
+ support for specific SLI-4 adapters.
diff --git a/drivers/scsi/elx/Makefile b/drivers/scsi/elx/Makefile
new file mode 100644
index 000000000000..a8537d7a2a6e
--- /dev/null
+++ b/drivers/scsi/elx/Makefile
@@ -0,0 +1,18 @@
+#// SPDX-License-Identifier: GPL-2.0
+#/*
+# * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+# * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+# */
+
+
+obj-$(CONFIG_SCSI_EFCT) := efct.o
+
+efct-objs := efct/efct_driver.o efct/efct_io.o efct/efct_scsi.o \
+ efct/efct_xport.o efct/efct_hw.o efct/efct_hw_queues.o \
+ efct/efct_lio.o efct/efct_unsol.o
+
+efct-objs += libefc/efc_cmds.o libefc/efc_domain.o libefc/efc_fabric.o \
+ libefc/efc_node.o libefc/efc_nport.o libefc/efc_device.o \
+ libefc/efclib.o libefc/efc_sm.o libefc/efc_els.o
+
+efct-objs += libefc_sli/sli4.o
diff --git a/drivers/scsi/elx/efct/efct_driver.c b/drivers/scsi/elx/efct/efct_driver.c
new file mode 100644
index 000000000000..1bd42f7db177
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_driver.c
@@ -0,0 +1,783 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efct_driver.h"
+
+#include "efct_hw.h"
+#include "efct_unsol.h"
+#include "efct_scsi.h"
+
+LIST_HEAD(efct_devices);
+
+static int logmask;
+module_param(logmask, int, 0444);
+MODULE_PARM_DESC(logmask, "logging bitmask (default 0)");
+
+static struct libefc_function_template efct_libefc_templ = {
+ .issue_mbox_rqst = efct_issue_mbox_rqst,
+ .send_els = efct_els_hw_srrs_send,
+ .send_bls = efct_efc_bls_send,
+
+ .new_nport = efct_scsi_tgt_new_nport,
+ .del_nport = efct_scsi_tgt_del_nport,
+ .scsi_new_node = efct_scsi_new_initiator,
+ .scsi_del_node = efct_scsi_del_initiator,
+ .hw_seq_free = efct_efc_hw_sequence_free,
+};
+
+static int
+efct_device_init(void)
+{
+ int rc;
+
+ /* driver-wide init for target-server */
+ rc = efct_scsi_tgt_driver_init();
+ if (rc) {
+ pr_err("efct_scsi_tgt_init failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = efct_scsi_reg_fc_transport();
+ if (rc) {
+ efct_scsi_tgt_driver_exit();
+ pr_err("failed to register to FC host\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static void
+efct_device_shutdown(void)
+{
+ efct_scsi_release_fc_transport();
+
+ efct_scsi_tgt_driver_exit();
+}
+
+static void *
+efct_device_alloc(u32 nid)
+{
+ struct efct *efct = NULL;
+
+ efct = kzalloc_node(sizeof(*efct), GFP_KERNEL, nid);
+ if (!efct)
+ return efct;
+
+ INIT_LIST_HEAD(&efct->list_entry);
+ list_add_tail(&efct->list_entry, &efct_devices);
+
+ return efct;
+}
+
+static void
+efct_teardown_msix(struct efct *efct)
+{
+ u32 i;
+
+ for (i = 0; i < efct->n_msix_vec; i++) {
+ free_irq(pci_irq_vector(efct->pci, i),
+ &efct->intr_context[i]);
+ }
+
+ pci_free_irq_vectors(efct->pci);
+}
+
+static int
+efct_efclib_config(struct efct *efct, struct libefc_function_template *tt)
+{
+ struct efc *efc;
+ struct sli4 *sli;
+ int rc = 0;
+
+ efc = kzalloc(sizeof(*efc), GFP_KERNEL);
+ if (!efc)
+ return -ENOMEM;
+
+ efct->efcport = efc;
+
+ memcpy(&efc->tt, tt, sizeof(*tt));
+ efc->base = efct;
+ efc->pci = efct->pci;
+
+ efc->def_wwnn = efct_get_wwnn(&efct->hw);
+ efc->def_wwpn = efct_get_wwpn(&efct->hw);
+ efc->enable_tgt = 1;
+ efc->log_level = EFC_LOG_LIB;
+
+ sli = &efct->hw.sli;
+ efc->max_xfer_size = sli->sge_supported_length *
+ sli_get_max_sgl(&efct->hw.sli);
+ efc->sli = sli;
+ efc->fcfi = efct->hw.fcf_indicator;
+
+ rc = efcport_init(efc);
+ if (rc)
+ efc_log_err(efc, "efcport_init failed\n");
+
+ return rc;
+}
+
+static int efct_request_firmware_update(struct efct *efct);
+
+static const char*
+efct_pci_model(u16 device)
+{
+ switch (device) {
+ case EFCT_DEVICE_LANCER_G6: return "LPE31004";
+ case EFCT_DEVICE_LANCER_G7: return "LPE36000";
+ default: return "unknown";
+ }
+}
+
+static int
+efct_device_attach(struct efct *efct)
+{
+ u32 rc = 0, i = 0;
+
+ if (efct->attached) {
+ efc_log_err(efct, "Device is already attached\n");
+ return -EIO;
+ }
+
+ snprintf(efct->name, sizeof(efct->name), "[%s%d] ", "fc",
+ efct->instance_index);
+
+ efct->logmask = logmask;
+ efct->filter_def = EFCT_DEFAULT_FILTER;
+ efct->max_isr_time_msec = EFCT_OS_MAX_ISR_TIME_MSEC;
+
+ efct->model = efct_pci_model(efct->pci->device);
+
+ efct->efct_req_fw_upgrade = true;
+
+ /* Allocate transport object and bring online */
+ efct->xport = efct_xport_alloc(efct);
+ if (!efct->xport) {
+ efc_log_err(efct, "failed to allocate transport object\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = efct_xport_attach(efct->xport);
+ if (rc) {
+ efc_log_err(efct, "failed to attach transport object\n");
+ goto xport_out;
+ }
+
+ rc = efct_xport_initialize(efct->xport);
+ if (rc) {
+ efc_log_err(efct, "failed to initialize transport object\n");
+ goto xport_out;
+ }
+
+ rc = efct_efclib_config(efct, &efct_libefc_templ);
+ if (rc) {
+ efc_log_err(efct, "failed to init efclib\n");
+ goto efclib_out;
+ }
+
+ for (i = 0; i < efct->n_msix_vec; i++) {
+ efc_log_debug(efct, "irq %d enabled\n", i);
+ enable_irq(pci_irq_vector(efct->pci, i));
+ }
+
+ efct->attached = true;
+
+ if (efct->efct_req_fw_upgrade)
+ efct_request_firmware_update(efct);
+
+ return rc;
+
+efclib_out:
+ efct_xport_detach(efct->xport);
+xport_out:
+ efct_xport_free(efct->xport);
+ efct->xport = NULL;
+out:
+ return rc;
+}
+
+static int
+efct_device_detach(struct efct *efct)
+{
+ int i;
+
+ if (!efct || !efct->attached) {
+ pr_err("Device is not attached\n");
+ return -EIO;
+ }
+
+ if (efct_xport_control(efct->xport, EFCT_XPORT_SHUTDOWN))
+ efc_log_err(efct, "Transport Shutdown timed out\n");
+
+ for (i = 0; i < efct->n_msix_vec; i++)
+ disable_irq(pci_irq_vector(efct->pci, i));
+
+ efct_xport_detach(efct->xport);
+
+ efct_xport_free(efct->xport);
+ efct->xport = NULL;
+
+ efcport_destroy(efct->efcport);
+ kfree(efct->efcport);
+
+ efct->attached = false;
+
+ return 0;
+}
+
+static void
+efct_fw_write_cb(int status, u32 actual_write_length,
+ u32 change_status, void *arg)
+{
+ struct efct_fw_write_result *result = arg;
+
+ result->status = status;
+ result->actual_xfer = actual_write_length;
+ result->change_status = change_status;
+
+ complete(&result->done);
+}
+
+static int
+efct_firmware_write(struct efct *efct, const u8 *buf, size_t buf_len,
+ u8 *change_status)
+{
+ int rc = 0;
+ u32 bytes_left;
+ u32 xfer_size;
+ u32 offset;
+ struct efc_dma dma;
+ int last = 0;
+ struct efct_fw_write_result result;
+
+ init_completion(&result.done);
+
+ bytes_left = buf_len;
+ offset = 0;
+
+ dma.size = FW_WRITE_BUFSIZE;
+ dma.virt = dma_alloc_coherent(&efct->pci->dev,
+ dma.size, &dma.phys, GFP_KERNEL);
+ if (!dma.virt)
+ return -ENOMEM;
+
+ while (bytes_left > 0) {
+ if (bytes_left > FW_WRITE_BUFSIZE)
+ xfer_size = FW_WRITE_BUFSIZE;
+ else
+ xfer_size = bytes_left;
+
+ memcpy(dma.virt, buf + offset, xfer_size);
+
+ if (bytes_left == xfer_size)
+ last = 1;
+
+ efct_hw_firmware_write(&efct->hw, &dma, xfer_size, offset,
+ last, efct_fw_write_cb, &result);
+
+ if (wait_for_completion_interruptible(&result.done) != 0) {
+ rc = -ENXIO;
+ break;
+ }
+
+ if (result.actual_xfer == 0 || result.status != 0) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (last)
+ *change_status = result.change_status;
+
+ bytes_left -= result.actual_xfer;
+ offset += result.actual_xfer;
+ }
+
+ dma_free_coherent(&efct->pci->dev, dma.size, dma.virt, dma.phys);
+ return rc;
+}
+
+static int
+efct_fw_reset(struct efct *efct)
+{
+ /*
+ * Firmware reset to activate the new firmware.
+ * Function 0 will update and load the new firmware
+ * during attach.
+ */
+ if (timer_pending(&efct->xport->stats_timer))
+ timer_delete(&efct->xport->stats_timer);
+
+ if (efct_hw_reset(&efct->hw, EFCT_HW_RESET_FIRMWARE)) {
+ efc_log_info(efct, "failed to reset firmware\n");
+ return -EIO;
+ }
+
+ efc_log_info(efct, "successfully reset firmware.Now resetting port\n");
+
+ efct_device_detach(efct);
+ return efct_device_attach(efct);
+}
+
+static int
+efct_request_firmware_update(struct efct *efct)
+{
+ int rc = 0;
+ u8 file_name[256], fw_change_status = 0;
+ const struct firmware *fw;
+ struct efct_hw_grp_hdr *fw_image;
+
+ snprintf(file_name, 256, "%s.grp", efct->model);
+
+ rc = request_firmware(&fw, file_name, &efct->pci->dev);
+ if (rc) {
+ efc_log_debug(efct, "Firmware file(%s) not found.\n", file_name);
+ return rc;
+ }
+
+ fw_image = (struct efct_hw_grp_hdr *)fw->data;
+
+ if (!strncmp(efct->hw.sli.fw_name[0], fw_image->revision,
+ strnlen(fw_image->revision, 16))) {
+ efc_log_debug(efct,
+ "Skip update. Firmware is already up to date.\n");
+ goto exit;
+ }
+
+ efc_log_info(efct, "Firmware update is initiated. %s -> %s\n",
+ efct->hw.sli.fw_name[0], fw_image->revision);
+
+ rc = efct_firmware_write(efct, fw->data, fw->size, &fw_change_status);
+ if (rc) {
+ efc_log_err(efct, "Firmware update failed. rc = %d\n", rc);
+ goto exit;
+ }
+
+ efc_log_info(efct, "Firmware updated successfully\n");
+ switch (fw_change_status) {
+ case 0x00:
+ efc_log_info(efct, "New firmware is active.\n");
+ break;
+ case 0x01:
+ efc_log_info(efct,
+ "System reboot needed to activate the new firmware\n");
+ break;
+ case 0x02:
+ case 0x03:
+ efc_log_info(efct,
+ "firmware reset to activate the new firmware\n");
+ efct_fw_reset(efct);
+ break;
+ default:
+ efc_log_info(efct, "Unexpected value change_status:%d\n",
+ fw_change_status);
+ break;
+ }
+
+exit:
+ release_firmware(fw);
+
+ return rc;
+}
+
+static void
+efct_device_free(struct efct *efct)
+{
+ if (efct) {
+ list_del(&efct->list_entry);
+ kfree(efct);
+ }
+}
+
+static int
+efct_device_interrupts_required(struct efct *efct)
+{
+ int rc;
+
+ rc = efct_hw_setup(&efct->hw, efct, efct->pci);
+ if (rc < 0)
+ return rc;
+
+ return efct->hw.config.n_eq;
+}
+
+static irqreturn_t
+efct_intr_thread(int irq, void *handle)
+{
+ struct efct_intr_context *intr_ctx = handle;
+ struct efct *efct = intr_ctx->efct;
+
+ efct_hw_process(&efct->hw, intr_ctx->index, efct->max_isr_time_msec);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+efct_intr_msix(int irq, void *handle)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+static int
+efct_setup_msix(struct efct *efct, u32 num_intrs)
+{
+ int rc = 0, i;
+
+ if (!pci_find_capability(efct->pci, PCI_CAP_ID_MSIX)) {
+ dev_err(&efct->pci->dev,
+ "%s : MSI-X not available\n", __func__);
+ return -EIO;
+ }
+
+ efct->n_msix_vec = num_intrs;
+
+ rc = pci_alloc_irq_vectors(efct->pci, num_intrs, num_intrs,
+ PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
+
+ if (rc < 0) {
+ dev_err(&efct->pci->dev, "Failed to alloc irq : %d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < num_intrs; i++) {
+ struct efct_intr_context *intr_ctx = NULL;
+
+ intr_ctx = &efct->intr_context[i];
+ intr_ctx->efct = efct;
+ intr_ctx->index = i;
+
+ rc = request_threaded_irq(pci_irq_vector(efct->pci, i),
+ efct_intr_msix, efct_intr_thread, 0,
+ EFCT_DRIVER_NAME, intr_ctx);
+ if (rc) {
+ dev_err(&efct->pci->dev,
+ "Failed to register %d vector: %d\n", i, rc);
+ goto out;
+ }
+ }
+
+ return rc;
+
+out:
+ while (--i >= 0)
+ free_irq(pci_irq_vector(efct->pci, i),
+ &efct->intr_context[i]);
+
+ pci_free_irq_vectors(efct->pci);
+ return rc;
+}
+
+static const struct pci_device_id efct_pci_table[] = {
+ {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G6), 0},
+ {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G7), 0},
+ {} /* terminate list */
+};
+
+static int
+efct_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct efct *efct = NULL;
+ int rc;
+ u32 i, r;
+ int num_interrupts = 0;
+ int nid;
+
+ dev_info(&pdev->dev, "%s\n", EFCT_DRIVER_NAME);
+
+ rc = pci_enable_device_mem(pdev);
+ if (rc)
+ return rc;
+
+ pci_set_master(pdev);
+
+ rc = pci_set_mwi(pdev);
+ if (rc) {
+ dev_info(&pdev->dev, "pci_set_mwi returned %d\n", rc);
+ goto mwi_out;
+ }
+
+ rc = pci_request_regions(pdev, EFCT_DRIVER_NAME);
+ if (rc) {
+ dev_err(&pdev->dev, "pci_request_regions failed %d\n", rc);
+ goto req_regions_out;
+ }
+
+ /* Fetch the Numa node id for this device */
+ nid = dev_to_node(&pdev->dev);
+ if (nid < 0) {
+ dev_err(&pdev->dev, "Warning Numa node ID is %d\n", nid);
+ nid = 0;
+ }
+
+ /* Allocate efct */
+ efct = efct_device_alloc(nid);
+ if (!efct) {
+ dev_err(&pdev->dev, "Failed to allocate efct\n");
+ rc = -ENOMEM;
+ goto alloc_out;
+ }
+
+ efct->pci = pdev;
+ efct->numa_node = nid;
+
+ /* Map all memory BARs */
+ for (i = 0, r = 0; i < EFCT_PCI_MAX_REGS; i++) {
+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+ efct->reg[r] = ioremap(pci_resource_start(pdev, i),
+ pci_resource_len(pdev, i));
+ r++;
+ }
+
+ /*
+ * If the 64-bit attribute is set, both this BAR and the
+ * next form the complete address. Skip processing the
+ * next BAR.
+ */
+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM_64)
+ i++;
+ }
+
+ pci_set_drvdata(pdev, efct);
+
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (rc) {
+ dev_err(&pdev->dev, "setting DMA_BIT_MASK failed\n");
+ goto dma_mask_out;
+ }
+
+ num_interrupts = efct_device_interrupts_required(efct);
+ if (num_interrupts < 0) {
+ efc_log_err(efct, "efct_device_interrupts_required failed\n");
+ rc = -1;
+ goto dma_mask_out;
+ }
+
+ /*
+ * Initialize MSIX interrupts, note,
+ * efct_setup_msix() enables the interrupt
+ */
+ rc = efct_setup_msix(efct, num_interrupts);
+ if (rc) {
+ dev_err(&pdev->dev, "Can't setup msix\n");
+ goto dma_mask_out;
+ }
+ /* Disable interrupt for now */
+ for (i = 0; i < efct->n_msix_vec; i++) {
+ efc_log_debug(efct, "irq %d disabled\n", i);
+ disable_irq(pci_irq_vector(efct->pci, i));
+ }
+
+ rc = efct_device_attach(efct);
+ if (rc)
+ goto attach_out;
+
+ return 0;
+
+attach_out:
+ efct_teardown_msix(efct);
+dma_mask_out:
+ pci_set_drvdata(pdev, NULL);
+
+ for (i = 0; i < EFCT_PCI_MAX_REGS; i++) {
+ if (efct->reg[i])
+ iounmap(efct->reg[i]);
+ }
+ efct_device_free(efct);
+alloc_out:
+ pci_release_regions(pdev);
+req_regions_out:
+ pci_clear_mwi(pdev);
+mwi_out:
+ pci_disable_device(pdev);
+ return rc;
+}
+
+static void
+efct_pci_remove(struct pci_dev *pdev)
+{
+ struct efct *efct = pci_get_drvdata(pdev);
+ u32 i;
+
+ if (!efct)
+ return;
+
+ efct_device_detach(efct);
+
+ efct_teardown_msix(efct);
+
+ for (i = 0; i < EFCT_PCI_MAX_REGS; i++) {
+ if (efct->reg[i])
+ iounmap(efct->reg[i]);
+ }
+
+ pci_set_drvdata(pdev, NULL);
+
+ efct_device_free(efct);
+
+ pci_release_regions(pdev);
+
+ pci_disable_device(pdev);
+}
+
+static void
+efct_device_prep_for_reset(struct efct *efct, struct pci_dev *pdev)
+{
+ if (efct) {
+ efc_log_debug(efct,
+ "PCI channel disable preparing for reset\n");
+ efct_device_detach(efct);
+ /* Disable interrupt and pci device */
+ efct_teardown_msix(efct);
+ }
+ pci_disable_device(pdev);
+}
+
+static void
+efct_device_prep_for_recover(struct efct *efct)
+{
+ if (efct) {
+ efc_log_debug(efct, "PCI channel preparing for recovery\n");
+ efct_hw_io_abort_all(&efct->hw);
+ }
+}
+
+/**
+ * efct_pci_io_error_detected - method for handling PCI I/O error
+ * @pdev: pointer to PCI device.
+ * @state: the current PCI connection state.
+ *
+ * This routine is registered to the PCI subsystem for error handling. This
+ * function is called by the PCI subsystem after a PCI bus error affecting
+ * this device has been detected. When this routine is invoked, it dispatches
+ * device error detected handling routine, which will perform the proper
+ * error detected operation.
+ *
+ * Return codes
+ * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ * PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ */
+static pci_ers_result_t
+efct_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct efct *efct = pci_get_drvdata(pdev);
+ pci_ers_result_t rc;
+
+ switch (state) {
+ case pci_channel_io_normal:
+ efct_device_prep_for_recover(efct);
+ rc = PCI_ERS_RESULT_CAN_RECOVER;
+ break;
+ case pci_channel_io_frozen:
+ efct_device_prep_for_reset(efct, pdev);
+ rc = PCI_ERS_RESULT_NEED_RESET;
+ break;
+ case pci_channel_io_perm_failure:
+ efct_device_detach(efct);
+ rc = PCI_ERS_RESULT_DISCONNECT;
+ break;
+ default:
+ efc_log_debug(efct, "Unknown PCI error state:0x%x\n", state);
+ efct_device_prep_for_reset(efct, pdev);
+ rc = PCI_ERS_RESULT_NEED_RESET;
+ break;
+ }
+
+ return rc;
+}
+
+static pci_ers_result_t
+efct_pci_io_slot_reset(struct pci_dev *pdev)
+{
+ int rc;
+ struct efct *efct = pci_get_drvdata(pdev);
+
+ rc = pci_enable_device_mem(pdev);
+ if (rc) {
+ efc_log_err(efct, "failed to enable PCI device after reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ /*
+ * As the new kernel behavior of pci_restore_state() API call clears
+ * device saved_state flag, need to save the restored state again.
+ */
+
+ pci_save_state(pdev);
+
+ pci_set_master(pdev);
+
+ rc = efct_setup_msix(efct, efct->n_msix_vec);
+ if (rc)
+ efc_log_err(efct, "rc %d returned, IRQ allocation failed\n",
+ rc);
+
+ /* Perform device reset */
+ efct_device_detach(efct);
+ /* Bring device to online*/
+ efct_device_attach(efct);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void
+efct_pci_io_resume(struct pci_dev *pdev)
+{
+ struct efct *efct = pci_get_drvdata(pdev);
+
+ /* Perform device reset */
+ efct_device_detach(efct);
+ /* Bring device to online*/
+ efct_device_attach(efct);
+}
+
+MODULE_DEVICE_TABLE(pci, efct_pci_table);
+
+static const struct pci_error_handlers efct_pci_err_handler = {
+ .error_detected = efct_pci_io_error_detected,
+ .slot_reset = efct_pci_io_slot_reset,
+ .resume = efct_pci_io_resume,
+};
+
+static struct pci_driver efct_pci_driver = {
+ .name = EFCT_DRIVER_NAME,
+ .id_table = efct_pci_table,
+ .probe = efct_pci_probe,
+ .remove = efct_pci_remove,
+ .err_handler = &efct_pci_err_handler,
+};
+
+static
+int __init efct_init(void)
+{
+ int rc;
+
+ rc = efct_device_init();
+ if (rc) {
+ pr_err("efct_device_init failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = pci_register_driver(&efct_pci_driver);
+ if (rc) {
+ pr_err("pci_register_driver failed rc=%d\n", rc);
+ efct_device_shutdown();
+ }
+
+ return rc;
+}
+
+static void __exit efct_exit(void)
+{
+ pci_unregister_driver(&efct_pci_driver);
+ efct_device_shutdown();
+}
+
+module_init(efct_init);
+module_exit(efct_exit);
+MODULE_VERSION(EFCT_DRIVER_VERSION);
+MODULE_DESCRIPTION("Emulex Fibre Channel Target driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Broadcom");
diff --git a/drivers/scsi/elx/efct/efct_driver.h b/drivers/scsi/elx/efct/efct_driver.h
new file mode 100644
index 000000000000..0e3c931db7c2
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_driver.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#if !defined(__EFCT_DRIVER_H__)
+#define __EFCT_DRIVER_H__
+
+/***************************************************************************
+ * OS specific includes
+ */
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include "../include/efc_common.h"
+#include "../libefc/efclib.h"
+#include "efct_hw.h"
+#include "efct_io.h"
+#include "efct_xport.h"
+
+#define EFCT_DRIVER_NAME "efct"
+#define EFCT_DRIVER_VERSION "1.0.0.0"
+
+/* EFCT_DEFAULT_FILTER-
+ * MRQ filter to segregate the IO flow.
+ */
+#define EFCT_DEFAULT_FILTER "0x01ff22ff,0,0,0"
+
+/* EFCT_OS_MAX_ISR_TIME_MSEC -
+ * maximum time driver code should spend in an interrupt
+ * or kernel thread context without yielding
+ */
+#define EFCT_OS_MAX_ISR_TIME_MSEC 1000
+
+#define EFCT_FC_MAX_SGL 64
+#define EFCT_FC_DIF_SEED 0
+
+/* Watermark */
+#define EFCT_WATERMARK_HIGH_PCT 90
+#define EFCT_WATERMARK_LOW_PCT 80
+#define EFCT_IO_WATERMARK_PER_INITIATOR 8
+
+#define EFCT_PCI_MAX_REGS 6
+#define MAX_PCI_INTERRUPTS 16
+
+struct efct_intr_context {
+ struct efct *efct;
+ u32 index;
+};
+
+struct efct {
+ struct pci_dev *pci;
+ void __iomem *reg[EFCT_PCI_MAX_REGS];
+
+ u32 n_msix_vec;
+ bool attached;
+ bool soft_wwn_enable;
+ u8 efct_req_fw_upgrade;
+ struct efct_intr_context intr_context[MAX_PCI_INTERRUPTS];
+ u32 numa_node;
+
+ char name[EFC_NAME_LENGTH];
+ u32 instance_index;
+ struct list_head list_entry;
+ struct efct_scsi_tgt tgt_efct;
+ struct efct_xport *xport;
+ struct efc *efcport;
+ struct Scsi_Host *shost;
+ int logmask;
+ u32 max_isr_time_msec;
+
+ const char *desc;
+
+ const char *model;
+
+ struct efct_hw hw;
+
+ u32 rq_selection_policy;
+ char *filter_def;
+ int topology;
+
+ /* Look up for target node */
+ struct xarray lookup;
+
+ /*
+ * Target IO timer value:
+ * Zero: target command timeout disabled.
+ * Non-zero: Timeout value, in seconds, for target commands
+ */
+ u32 target_io_timer_sec;
+
+ int speed;
+ struct dentry *sess_debugfs_dir;
+};
+
+#define FW_WRITE_BUFSIZE (64 * 1024)
+
+struct efct_fw_write_result {
+ struct completion done;
+ int status;
+ u32 actual_xfer;
+ u32 change_status;
+};
+
+extern struct list_head efct_devices;
+
+#endif /* __EFCT_DRIVER_H__ */
diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c
new file mode 100644
index 000000000000..5b079b8b7a08
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_hw.c
@@ -0,0 +1,3581 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efct_driver.h"
+#include "efct_hw.h"
+#include "efct_unsol.h"
+
+struct efct_hw_link_stat_cb_arg {
+ void (*cb)(int status, u32 num_counters,
+ struct efct_hw_link_stat_counts *counters, void *arg);
+ void *arg;
+};
+
+struct efct_hw_host_stat_cb_arg {
+ void (*cb)(int status, u32 num_counters,
+ struct efct_hw_host_stat_counts *counters, void *arg);
+ void *arg;
+};
+
+struct efct_hw_fw_wr_cb_arg {
+ void (*cb)(int status, u32 bytes_written, u32 change_status, void *arg);
+ void *arg;
+};
+
+struct efct_mbox_rqst_ctx {
+ int (*callback)(struct efc *efc, int status, u8 *mqe, void *arg);
+ void *arg;
+};
+
+static int
+efct_hw_link_event_init(struct efct_hw *hw)
+{
+ hw->link.status = SLI4_LINK_STATUS_MAX;
+ hw->link.topology = SLI4_LINK_TOPO_NONE;
+ hw->link.medium = SLI4_LINK_MEDIUM_MAX;
+ hw->link.speed = 0;
+ hw->link.loop_map = NULL;
+ hw->link.fc_id = U32_MAX;
+
+ return 0;
+}
+
+static int
+efct_hw_read_max_dump_size(struct efct_hw *hw)
+{
+ u8 buf[SLI4_BMBX_SIZE];
+ struct efct *efct = hw->os;
+ int rc = 0;
+ struct sli4_rsp_cmn_set_dump_location *rsp;
+
+ /* attempt to detemine the dump size for function 0 only. */
+ if (PCI_FUNC(efct->pci->devfn) != 0)
+ return rc;
+
+ if (sli_cmd_common_set_dump_location(&hw->sli, buf, 1, 0, NULL, 0))
+ return -EIO;
+
+ rsp = (struct sli4_rsp_cmn_set_dump_location *)
+ (buf + offsetof(struct sli4_cmd_sli_config, payload.embed));
+
+ rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
+ if (rc != 0) {
+ efc_log_debug(hw->os, "set dump location cmd failed\n");
+ return rc;
+ }
+
+ hw->dump_size =
+ le32_to_cpu(rsp->buffer_length_dword) & SLI4_CMN_SET_DUMP_BUFFER_LEN;
+
+ efc_log_debug(hw->os, "Dump size %x\n", hw->dump_size);
+
+ return rc;
+}
+
+static int
+__efct_read_topology_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg)
+{
+ struct sli4_cmd_read_topology *read_topo =
+ (struct sli4_cmd_read_topology *)mqe;
+ u8 speed;
+ struct efc_domain_record drec = {0};
+ struct efct *efct = hw->os;
+
+ if (status || le16_to_cpu(read_topo->hdr.status)) {
+ efc_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", status,
+ le16_to_cpu(read_topo->hdr.status));
+ return -EIO;
+ }
+
+ switch (le32_to_cpu(read_topo->dw2_attentype) &
+ SLI4_READTOPO_ATTEN_TYPE) {
+ case SLI4_READ_TOPOLOGY_LINK_UP:
+ hw->link.status = SLI4_LINK_STATUS_UP;
+ break;
+ case SLI4_READ_TOPOLOGY_LINK_DOWN:
+ hw->link.status = SLI4_LINK_STATUS_DOWN;
+ break;
+ case SLI4_READ_TOPOLOGY_LINK_NO_ALPA:
+ hw->link.status = SLI4_LINK_STATUS_NO_ALPA;
+ break;
+ default:
+ hw->link.status = SLI4_LINK_STATUS_MAX;
+ break;
+ }
+
+ switch (read_topo->topology) {
+ case SLI4_READ_TOPO_NON_FC_AL:
+ hw->link.topology = SLI4_LINK_TOPO_NON_FC_AL;
+ break;
+ case SLI4_READ_TOPO_FC_AL:
+ hw->link.topology = SLI4_LINK_TOPO_FC_AL;
+ if (hw->link.status == SLI4_LINK_STATUS_UP)
+ hw->link.loop_map = hw->loop_map.virt;
+ hw->link.fc_id = read_topo->acquired_al_pa;
+ break;
+ default:
+ hw->link.topology = SLI4_LINK_TOPO_MAX;
+ break;
+ }
+
+ hw->link.medium = SLI4_LINK_MEDIUM_FC;
+
+ speed = (le32_to_cpu(read_topo->currlink_state) &
+ SLI4_READTOPO_LINKSTATE_SPEED) >> 8;
+ switch (speed) {
+ case SLI4_READ_TOPOLOGY_SPEED_1G:
+ hw->link.speed = 1 * 1000;
+ break;
+ case SLI4_READ_TOPOLOGY_SPEED_2G:
+ hw->link.speed = 2 * 1000;
+ break;
+ case SLI4_READ_TOPOLOGY_SPEED_4G:
+ hw->link.speed = 4 * 1000;
+ break;
+ case SLI4_READ_TOPOLOGY_SPEED_8G:
+ hw->link.speed = 8 * 1000;
+ break;
+ case SLI4_READ_TOPOLOGY_SPEED_16G:
+ hw->link.speed = 16 * 1000;
+ break;
+ case SLI4_READ_TOPOLOGY_SPEED_32G:
+ hw->link.speed = 32 * 1000;
+ break;
+ case SLI4_READ_TOPOLOGY_SPEED_64G:
+ hw->link.speed = 64 * 1000;
+ break;
+ case SLI4_READ_TOPOLOGY_SPEED_128G:
+ hw->link.speed = 128 * 1000;
+ break;
+ }
+
+ drec.speed = hw->link.speed;
+ drec.fc_id = hw->link.fc_id;
+ drec.is_nport = true;
+ efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND, &drec);
+
+ return 0;
+}
+
+static int
+efct_hw_cb_link(void *ctx, void *e)
+{
+ struct efct_hw *hw = ctx;
+ struct sli4_link_event *event = e;
+ struct efc_domain *d = NULL;
+ int rc = 0;
+ struct efct *efct = hw->os;
+
+ efct_hw_link_event_init(hw);
+
+ switch (event->status) {
+ case SLI4_LINK_STATUS_UP:
+
+ hw->link = *event;
+ efct->efcport->link_status = EFC_LINK_STATUS_UP;
+
+ if (event->topology == SLI4_LINK_TOPO_NON_FC_AL) {
+ struct efc_domain_record drec = {0};
+
+ efc_log_info(hw->os, "Link Up, NPORT, speed is %d\n",
+ event->speed);
+ drec.speed = event->speed;
+ drec.fc_id = event->fc_id;
+ drec.is_nport = true;
+ efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND,
+ &drec);
+ } else if (event->topology == SLI4_LINK_TOPO_FC_AL) {
+ u8 buf[SLI4_BMBX_SIZE];
+
+ efc_log_info(hw->os, "Link Up, LOOP, speed is %d\n",
+ event->speed);
+
+ if (!sli_cmd_read_topology(&hw->sli, buf,
+ &hw->loop_map)) {
+ rc = efct_hw_command(hw, buf, EFCT_CMD_NOWAIT,
+ __efct_read_topology_cb, NULL);
+ }
+
+ if (rc)
+ efc_log_debug(hw->os, "READ_TOPOLOGY failed\n");
+ } else {
+ efc_log_info(hw->os, "%s(%#x), speed is %d\n",
+ "Link Up, unsupported topology ",
+ event->topology, event->speed);
+ }
+ break;
+ case SLI4_LINK_STATUS_DOWN:
+ efc_log_info(hw->os, "Link down\n");
+
+ hw->link.status = event->status;
+ efct->efcport->link_status = EFC_LINK_STATUS_DOWN;
+
+ d = efct->efcport->domain;
+ if (d)
+ efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_LOST, d);
+ break;
+ default:
+ efc_log_debug(hw->os, "unhandled link status %#x\n",
+ event->status);
+ break;
+ }
+
+ return 0;
+}
+
+int
+efct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev)
+{
+ u32 i, max_sgl, cpus;
+
+ if (hw->hw_setup_called)
+ return 0;
+
+ /*
+ * efct_hw_init() relies on NULL pointers indicating that a structure
+ * needs allocation. If a structure is non-NULL, efct_hw_init() won't
+ * free/realloc that memory
+ */
+ memset(hw, 0, sizeof(struct efct_hw));
+
+ hw->hw_setup_called = true;
+
+ hw->os = os;
+
+ mutex_init(&hw->bmbx_lock);
+ spin_lock_init(&hw->cmd_lock);
+ INIT_LIST_HEAD(&hw->cmd_head);
+ INIT_LIST_HEAD(&hw->cmd_pending);
+ hw->cmd_head_count = 0;
+
+ /* Create mailbox command ctx pool */
+ hw->cmd_ctx_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ,
+ sizeof(struct efct_command_ctx));
+ if (!hw->cmd_ctx_pool) {
+ efc_log_err(hw->os, "failed to allocate mailbox buffer pool\n");
+ return -EIO;
+ }
+
+ /* Create mailbox request ctx pool for library callback */
+ hw->mbox_rqst_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ,
+ sizeof(struct efct_mbox_rqst_ctx));
+ if (!hw->mbox_rqst_pool) {
+ efc_log_err(hw->os, "failed to allocate mbox request pool\n");
+ return -EIO;
+ }
+
+ spin_lock_init(&hw->io_lock);
+ INIT_LIST_HEAD(&hw->io_inuse);
+ INIT_LIST_HEAD(&hw->io_free);
+ INIT_LIST_HEAD(&hw->io_wait_free);
+
+ atomic_set(&hw->io_alloc_failed_count, 0);
+
+ hw->config.speed = SLI4_LINK_SPEED_AUTO_16_8_4;
+ if (sli_setup(&hw->sli, hw->os, pdev, ((struct efct *)os)->reg)) {
+ efc_log_err(hw->os, "SLI setup failed\n");
+ return -EIO;
+ }
+
+ efct_hw_link_event_init(hw);
+
+ sli_callback(&hw->sli, SLI4_CB_LINK, efct_hw_cb_link, hw);
+
+ /*
+ * Set all the queue sizes to the maximum allowed.
+ */
+ for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++)
+ hw->num_qentries[i] = hw->sli.qinfo.max_qentries[i];
+ /*
+ * Adjust the size of the WQs so that the CQ is twice as big as
+ * the WQ to allow for 2 completions per IO. This allows us to
+ * handle multi-phase as well as aborts.
+ */
+ hw->num_qentries[SLI4_QTYPE_WQ] = hw->num_qentries[SLI4_QTYPE_CQ] / 2;
+
+ /*
+ * The RQ assignment for RQ pair mode.
+ */
+
+ hw->config.rq_default_buffer_size = EFCT_HW_RQ_SIZE_PAYLOAD;
+ hw->config.n_io = hw->sli.ext[SLI4_RSRC_XRI].size;
+
+ cpus = num_possible_cpus();
+ hw->config.n_eq = cpus > EFCT_HW_MAX_NUM_EQ ? EFCT_HW_MAX_NUM_EQ : cpus;
+
+ max_sgl = sli_get_max_sgl(&hw->sli) - SLI4_SGE_MAX_RESERVED;
+ max_sgl = (max_sgl > EFCT_FC_MAX_SGL) ? EFCT_FC_MAX_SGL : max_sgl;
+ hw->config.n_sgl = max_sgl;
+
+ (void)efct_hw_read_max_dump_size(hw);
+
+ return 0;
+}
+
+static void
+efct_logfcfi(struct efct_hw *hw, u32 j, u32 i, u32 id)
+{
+ efc_log_info(hw->os,
+ "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n",
+ j, hw->config.filter_def[j], i, id);
+}
+
+static inline void
+efct_hw_init_free_io(struct efct_hw_io *io)
+{
+ /*
+ * Set io->done to NULL, to avoid any callbacks, should
+ * a completion be received for one of these IOs
+ */
+ io->done = NULL;
+ io->abort_done = NULL;
+ io->status_saved = false;
+ io->abort_in_progress = false;
+ io->type = 0xFFFF;
+ io->wq = NULL;
+}
+
+static bool efct_hw_iotype_is_originator(u16 io_type)
+{
+ switch (io_type) {
+ case EFCT_HW_FC_CT:
+ case EFCT_HW_ELS_REQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void
+efct_hw_io_restore_sgl(struct efct_hw *hw, struct efct_hw_io *io)
+{
+ /* Restore the default */
+ io->sgl = &io->def_sgl;
+ io->sgl_count = io->def_sgl_count;
+}
+
+static void
+efct_hw_wq_process_io(void *arg, u8 *cqe, int status)
+{
+ struct efct_hw_io *io = arg;
+ struct efct_hw *hw = io->hw;
+ struct sli4_fc_wcqe *wcqe = (void *)cqe;
+ u32 len = 0;
+ u32 ext = 0;
+
+ /* clear xbusy flag if WCQE[XB] is clear */
+ if (io->xbusy && (wcqe->flags & SLI4_WCQE_XB) == 0)
+ io->xbusy = false;
+
+ /* get extended CQE status */
+ switch (io->type) {
+ case EFCT_HW_BLS_ACC:
+ case EFCT_HW_BLS_RJT:
+ break;
+ case EFCT_HW_ELS_REQ:
+ sli_fc_els_did(&hw->sli, cqe, &ext);
+ len = sli_fc_response_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_ELS_RSP:
+ case EFCT_HW_FC_CT_RSP:
+ break;
+ case EFCT_HW_FC_CT:
+ len = sli_fc_response_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_IO_TARGET_WRITE:
+ len = sli_fc_io_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_IO_TARGET_READ:
+ len = sli_fc_io_length(&hw->sli, cqe);
+ break;
+ case EFCT_HW_IO_TARGET_RSP:
+ break;
+ case EFCT_HW_IO_DNRX_REQUEUE:
+ /* release the count for re-posting the buffer */
+ /* efct_hw_io_free(hw, io); */
+ break;
+ default:
+ efc_log_err(hw->os, "unhandled io type %#x for XRI 0x%x\n",
+ io->type, io->indicator);
+ break;
+ }
+ if (status) {
+ ext = sli_fc_ext_status(&hw->sli, cqe);
+ /*
+ * If we're not an originator IO, and XB is set, then issue
+ * abort for the IO from within the HW
+ */
+ if (efct_hw_iotype_is_originator(io->type) &&
+ wcqe->flags & SLI4_WCQE_XB) {
+ int rc;
+
+ efc_log_debug(hw->os, "aborting xri=%#x tag=%#x\n",
+ io->indicator, io->reqtag);
+
+ /*
+ * Because targets may send a response when the IO
+ * completes using the same XRI, we must wait for the
+ * XRI_ABORTED CQE to issue the IO callback
+ */
+ rc = efct_hw_io_abort(hw, io, false, NULL, NULL);
+ if (rc == 0) {
+ /*
+ * latch status to return after abort is
+ * complete
+ */
+ io->status_saved = true;
+ io->saved_status = status;
+ io->saved_ext = ext;
+ io->saved_len = len;
+ goto exit_efct_hw_wq_process_io;
+ } else if (rc == -EINPROGRESS) {
+ /*
+ * Already being aborted by someone else (ABTS
+ * perhaps). Just return original
+ * error.
+ */
+ efc_log_debug(hw->os, "%s%#x tag=%#x\n",
+ "abort in progress xri=",
+ io->indicator, io->reqtag);
+
+ } else {
+ /* Failed to abort for some other reason, log
+ * error
+ */
+ efc_log_debug(hw->os, "%s%#x tag=%#x rc=%d\n",
+ "Failed to abort xri=",
+ io->indicator, io->reqtag, rc);
+ }
+ }
+ }
+
+ if (io->done) {
+ efct_hw_done_t done = io->done;
+
+ io->done = NULL;
+
+ if (io->status_saved) {
+ /* use latched status if exists */
+ status = io->saved_status;
+ len = io->saved_len;
+ ext = io->saved_ext;
+ io->status_saved = false;
+ }
+
+ /* Restore default SGL */
+ efct_hw_io_restore_sgl(hw, io);
+ done(io, len, status, ext, io->arg);
+ }
+
+exit_efct_hw_wq_process_io:
+ return;
+}
+
+static int
+efct_hw_setup_io(struct efct_hw *hw)
+{
+ u32 i = 0;
+ struct efct_hw_io *io = NULL;
+ uintptr_t xfer_virt = 0;
+ uintptr_t xfer_phys = 0;
+ u32 index;
+ bool new_alloc = true;
+ struct efc_dma *dma;
+ struct efct *efct = hw->os;
+
+ if (!hw->io) {
+ hw->io = kmalloc_array(hw->config.n_io, sizeof(io), GFP_KERNEL);
+ if (!hw->io)
+ return -ENOMEM;
+
+ memset(hw->io, 0, hw->config.n_io * sizeof(io));
+
+ for (i = 0; i < hw->config.n_io; i++) {
+ hw->io[i] = kzalloc(sizeof(*io), GFP_KERNEL);
+ if (!hw->io[i])
+ goto error;
+ }
+
+ /* Create WQE buffs for IO */
+ hw->wqe_buffs = kzalloc((hw->config.n_io * hw->sli.wqe_size),
+ GFP_KERNEL);
+ if (!hw->wqe_buffs) {
+ kfree(hw->io);
+ return -ENOMEM;
+ }
+
+ } else {
+ /* re-use existing IOs, including SGLs */
+ new_alloc = false;
+ }
+
+ if (new_alloc) {
+ dma = &hw->xfer_rdy;
+ dma->size = sizeof(struct fcp_txrdy) * hw->config.n_io;
+ dma->virt = dma_alloc_coherent(&efct->pci->dev,
+ dma->size, &dma->phys, GFP_KERNEL);
+ if (!dma->virt)
+ return -ENOMEM;
+ }
+ xfer_virt = (uintptr_t)hw->xfer_rdy.virt;
+ xfer_phys = hw->xfer_rdy.phys;
+
+ /* Initialize the pool of HW IO objects */
+ for (i = 0; i < hw->config.n_io; i++) {
+ struct hw_wq_callback *wqcb;
+
+ io = hw->io[i];
+
+ /* initialize IO fields */
+ io->hw = hw;
+
+ /* Assign a WQE buff */
+ io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.wqe_size];
+
+ /* Allocate the request tag for this IO */
+ wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_io, io);
+ if (!wqcb) {
+ efc_log_err(hw->os, "can't allocate request tag\n");
+ return -ENOSPC;
+ }
+ io->reqtag = wqcb->instance_index;
+
+ /* Now for the fields that are initialized on each free */
+ efct_hw_init_free_io(io);
+
+ /* The XB flag isn't cleared on IO free, so init to zero */
+ io->xbusy = 0;
+
+ if (sli_resource_alloc(&hw->sli, SLI4_RSRC_XRI,
+ &io->indicator, &index)) {
+ efc_log_err(hw->os,
+ "sli_resource_alloc failed @ %d\n", i);
+ return -ENOMEM;
+ }
+
+ if (new_alloc) {
+ dma = &io->def_sgl;
+ dma->size = hw->config.n_sgl *
+ sizeof(struct sli4_sge);
+ dma->virt = dma_alloc_coherent(&efct->pci->dev,
+ dma->size, &dma->phys,
+ GFP_KERNEL);
+ if (!dma->virt) {
+ efc_log_err(hw->os, "dma_alloc fail %d\n", i);
+ memset(&io->def_sgl, 0,
+ sizeof(struct efc_dma));
+ return -ENOMEM;
+ }
+ }
+ io->def_sgl_count = hw->config.n_sgl;
+ io->sgl = &io->def_sgl;
+ io->sgl_count = io->def_sgl_count;
+
+ if (hw->xfer_rdy.size) {
+ io->xfer_rdy.virt = (void *)xfer_virt;
+ io->xfer_rdy.phys = xfer_phys;
+ io->xfer_rdy.size = sizeof(struct fcp_txrdy);
+
+ xfer_virt += sizeof(struct fcp_txrdy);
+ xfer_phys += sizeof(struct fcp_txrdy);
+ }
+ }
+
+ return 0;
+error:
+ for (i = 0; i < hw->config.n_io && hw->io[i]; i++) {
+ kfree(hw->io[i]);
+ hw->io[i] = NULL;
+ }
+
+ kfree(hw->io);
+ hw->io = NULL;
+
+ return -ENOMEM;
+}
+
+static int
+efct_hw_init_prereg_io(struct efct_hw *hw)
+{
+ u32 i, idx = 0;
+ struct efct_hw_io *io = NULL;
+ u8 cmd[SLI4_BMBX_SIZE];
+ int rc = 0;
+ u32 n_rem;
+ u32 n = 0;
+ u32 sgls_per_request = 256;
+ struct efc_dma **sgls = NULL;
+ struct efc_dma req;
+ struct efct *efct = hw->os;
+
+ sgls = kmalloc_array(sgls_per_request, sizeof(*sgls), GFP_KERNEL);
+ if (!sgls)
+ return -ENOMEM;
+
+ memset(&req, 0, sizeof(struct efc_dma));
+ req.size = 32 + sgls_per_request * 16;
+ req.virt = dma_alloc_coherent(&efct->pci->dev, req.size, &req.phys,
+ GFP_KERNEL);
+ if (!req.virt) {
+ kfree(sgls);
+ return -ENOMEM;
+ }
+
+ for (n_rem = hw->config.n_io; n_rem; n_rem -= n) {
+ /* Copy address of SGL's into local sgls[] array, break
+ * out if the xri is not contiguous.
+ */
+ u32 min = (sgls_per_request < n_rem) ? sgls_per_request : n_rem;
+
+ for (n = 0; n < min; n++) {
+ /* Check that we have contiguous xri values */
+ if (n > 0) {
+ if (hw->io[idx + n]->indicator !=
+ hw->io[idx + n - 1]->indicator + 1)
+ break;
+ }
+
+ sgls[n] = hw->io[idx + n]->sgl;
+ }
+
+ if (sli_cmd_post_sgl_pages(&hw->sli, cmd,
+ hw->io[idx]->indicator, n, sgls, NULL, &req)) {
+ rc = -EIO;
+ break;
+ }
+
+ rc = efct_hw_command(hw, cmd, EFCT_CMD_POLL, NULL, NULL);
+ if (rc) {
+ efc_log_err(hw->os, "SGL post failed, rc=%d\n", rc);
+ break;
+ }
+
+ /* Add to tail if successful */
+ for (i = 0; i < n; i++, idx++) {
+ io = hw->io[idx];
+ io->state = EFCT_HW_IO_STATE_FREE;
+ INIT_LIST_HEAD(&io->list_entry);
+ list_add_tail(&io->list_entry, &hw->io_free);
+ }
+ }
+
+ dma_free_coherent(&efct->pci->dev, req.size, req.virt, req.phys);
+ memset(&req, 0, sizeof(struct efc_dma));
+ kfree(sgls);
+
+ return rc;
+}
+
+static int
+efct_hw_init_io(struct efct_hw *hw)
+{
+ u32 i, idx = 0;
+ bool prereg = false;
+ struct efct_hw_io *io = NULL;
+ int rc = 0;
+
+ prereg = hw->sli.params.sgl_pre_registered;
+
+ if (prereg)
+ return efct_hw_init_prereg_io(hw);
+
+ for (i = 0; i < hw->config.n_io; i++, idx++) {
+ io = hw->io[idx];
+ io->state = EFCT_HW_IO_STATE_FREE;
+ INIT_LIST_HEAD(&io->list_entry);
+ list_add_tail(&io->list_entry, &hw->io_free);
+ }
+
+ return rc;
+}
+
+static int
+efct_hw_config_set_fdt_xfer_hint(struct efct_hw *hw, u32 fdt_xfer_hint)
+{
+ int rc = 0;
+ u8 buf[SLI4_BMBX_SIZE];
+ struct sli4_rqst_cmn_set_features_set_fdt_xfer_hint param;
+
+ memset(&param, 0, sizeof(param));
+ param.fdt_xfer_hint = cpu_to_le32(fdt_xfer_hint);
+ /* build the set_features command */
+ sli_cmd_common_set_features(&hw->sli, buf,
+ SLI4_SET_FEATURES_SET_FTD_XFER_HINT, sizeof(param), &param);
+
+ rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
+ if (rc)
+ efc_log_warn(hw->os, "set FDT hint %d failed: %d\n",
+ fdt_xfer_hint, rc);
+ else
+ efc_log_info(hw->os, "Set FTD transfer hint to %d\n",
+ le32_to_cpu(param.fdt_xfer_hint));
+
+ return rc;
+}
+
+static int
+efct_hw_config_rq(struct efct_hw *hw)
+{
+ u32 min_rq_count, i, rc;
+ struct sli4_cmd_rq_cfg rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
+ u8 buf[SLI4_BMBX_SIZE];
+
+ efc_log_info(hw->os, "using REG_FCFI standard\n");
+
+ /*
+ * Set the filter match/mask values from hw's
+ * filter_def values
+ */
+ for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
+ rq_cfg[i].rq_id = cpu_to_le16(0xffff);
+ rq_cfg[i].r_ctl_mask = (u8)hw->config.filter_def[i];
+ rq_cfg[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 8);
+ rq_cfg[i].type_mask = (u8)(hw->config.filter_def[i] >> 16);
+ rq_cfg[i].type_match = (u8)(hw->config.filter_def[i] >> 24);
+ }
+
+ /*
+ * Update the rq_id's of the FCF configuration
+ * (don't update more than the number of rq_cfg
+ * elements)
+ */
+ min_rq_count = (hw->hw_rq_count < SLI4_CMD_REG_FCFI_NUM_RQ_CFG) ?
+ hw->hw_rq_count : SLI4_CMD_REG_FCFI_NUM_RQ_CFG;
+ for (i = 0; i < min_rq_count; i++) {
+ struct hw_rq *rq = hw->hw_rq[i];
+ u32 j;
+
+ for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) {
+ u32 mask = (rq->filter_mask != 0) ?
+ rq->filter_mask : 1;
+
+ if (!(mask & (1U << j)))
+ continue;
+
+ rq_cfg[i].rq_id = cpu_to_le16(rq->hdr->id);
+ efct_logfcfi(hw, j, i, rq->hdr->id);
+ }
+ }
+
+ rc = -EIO;
+ if (!sli_cmd_reg_fcfi(&hw->sli, buf, 0, rq_cfg))
+ rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
+
+ if (rc != 0) {
+ efc_log_err(hw->os, "FCFI registration failed\n");
+ return rc;
+ }
+ hw->fcf_indicator =
+ le16_to_cpu(((struct sli4_cmd_reg_fcfi *)buf)->fcfi);
+
+ return rc;
+}
+
+static int
+efct_hw_config_mrq(struct efct_hw *hw, u8 mode, u16 fcf_index)
+{
+ u8 buf[SLI4_BMBX_SIZE], mrq_bitmask = 0;
+ struct hw_rq *rq;
+ struct sli4_cmd_reg_fcfi_mrq *rsp = NULL;
+ struct sli4_cmd_rq_cfg rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
+ u32 rc, i;
+
+ if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE)
+ goto issue_cmd;
+
+ /* Set the filter match/mask values from hw's filter_def values */
+ for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
+ rq_filter[i].rq_id = cpu_to_le16(0xffff);
+ rq_filter[i].type_mask = (u8)hw->config.filter_def[i];
+ rq_filter[i].type_match = (u8)(hw->config.filter_def[i] >> 8);
+ rq_filter[i].r_ctl_mask = (u8)(hw->config.filter_def[i] >> 16);
+ rq_filter[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 24);
+ }
+
+ rq = hw->hw_rq[0];
+ rq_filter[0].rq_id = cpu_to_le16(rq->hdr->id);
+ rq_filter[1].rq_id = cpu_to_le16(rq->hdr->id);
+
+ mrq_bitmask = 0x2;
+issue_cmd:
+ efc_log_debug(hw->os, "Issue reg_fcfi_mrq count:%d policy:%d mode:%d\n",
+ hw->hw_rq_count, hw->config.rq_selection_policy, mode);
+ /* Invoke REG_FCFI_MRQ */
+ rc = sli_cmd_reg_fcfi_mrq(&hw->sli, buf, mode, fcf_index,
+ hw->config.rq_selection_policy, mrq_bitmask,
+ hw->hw_mrq_count, rq_filter);
+ if (rc) {
+ efc_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed\n");
+ return -EIO;
+ }
+
+ rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
+
+ rsp = (struct sli4_cmd_reg_fcfi_mrq *)buf;
+
+ if ((rc) || (le16_to_cpu(rsp->hdr.status))) {
+ efc_log_err(hw->os, "FCFI MRQ reg failed. cmd=%x status=%x\n",
+ rsp->hdr.command, le16_to_cpu(rsp->hdr.status));
+ return -EIO;
+ }
+
+ if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE)
+ hw->fcf_indicator = le16_to_cpu(rsp->fcfi);
+
+ return 0;
+}
+
+static void
+efct_hw_queue_hash_add(struct efct_queue_hash *hash,
+ u16 id, u16 index)
+{
+ u32 hash_index = id & (EFCT_HW_Q_HASH_SIZE - 1);
+
+ /*
+ * Since the hash is always bigger than the number of queues, then we
+ * never have to worry about an infinite loop.
+ */
+ while (hash[hash_index].in_use)
+ hash_index = (hash_index + 1) & (EFCT_HW_Q_HASH_SIZE - 1);
+
+ /* not used, claim the entry */
+ hash[hash_index].id = id;
+ hash[hash_index].in_use = true;
+ hash[hash_index].index = index;
+}
+
+static int
+efct_hw_config_sli_port_health_check(struct efct_hw *hw, u8 query, u8 enable)
+{
+ int rc = 0;
+ u8 buf[SLI4_BMBX_SIZE];
+ struct sli4_rqst_cmn_set_features_health_check param;
+ u32 health_check_flag = 0;
+
+ memset(&param, 0, sizeof(param));
+
+ if (enable)
+ health_check_flag |= SLI4_RQ_HEALTH_CHECK_ENABLE;
+
+ if (query)
+ health_check_flag |= SLI4_RQ_HEALTH_CHECK_QUERY;
+
+ param.health_check_dword = cpu_to_le32(health_check_flag);
+
+ /* build the set_features command */
+ sli_cmd_common_set_features(&hw->sli, buf,
+ SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK, sizeof(param), &param);
+
+ rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL);
+ if (rc)
+ efc_log_err(hw->os, "efct_hw_command returns %d\n", rc);
+ else
+ efc_log_debug(hw->os, "SLI Port Health Check is enabled\n");
+
+ return rc;
+}
+
+int
+efct_hw_init(struct efct_hw *hw)
+{
+ int rc;
+ u32 i = 0;
+ int rem_count;
+ unsigned long flags = 0;
+ struct efct_hw_io *temp;
+ struct efc_dma *dma;
+
+ /*
+ * Make sure the command lists are empty. If this is start-of-day,
+ * they'll be empty since they were just initialized in efct_hw_setup.
+ * If we've just gone through a reset, the command and command pending
+ * lists should have been cleaned up as part of the reset
+ * (efct_hw_reset()).
+ */
+ spin_lock_irqsave(&hw->cmd_lock, flags);
+ if (!list_empty(&hw->cmd_head)) {
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+ efc_log_err(hw->os, "command found on cmd list\n");
+ return -EIO;
+ }
+ if (!list_empty(&hw->cmd_pending)) {
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+ efc_log_err(hw->os, "command found on pending list\n");
+ return -EIO;
+ }
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+
+ /* Free RQ buffers if prevously allocated */
+ efct_hw_rx_free(hw);
+
+ /*
+ * The IO queues must be initialized here for the reset case. The
+ * efct_hw_init_io() function will re-add the IOs to the free list.
+ * The cmd_head list should be OK since we free all entries in
+ * efct_hw_command_cancel() that is called in the efct_hw_reset().
+ */
+
+ /* If we are in this function due to a reset, there may be stale items
+ * on lists that need to be removed. Clean them up.
+ */
+ rem_count = 0;
+ while ((!list_empty(&hw->io_wait_free))) {
+ rem_count++;
+ temp = list_first_entry(&hw->io_wait_free, struct efct_hw_io,
+ list_entry);
+ list_del_init(&temp->list_entry);
+ }
+ if (rem_count > 0)
+ efc_log_debug(hw->os, "rmvd %d items from io_wait_free list\n",
+ rem_count);
+
+ rem_count = 0;
+ while ((!list_empty(&hw->io_inuse))) {
+ rem_count++;
+ temp = list_first_entry(&hw->io_inuse, struct efct_hw_io,
+ list_entry);
+ list_del_init(&temp->list_entry);
+ }
+ if (rem_count > 0)
+ efc_log_debug(hw->os, "rmvd %d items from io_inuse list\n",
+ rem_count);
+
+ rem_count = 0;
+ while ((!list_empty(&hw->io_free))) {
+ rem_count++;
+ temp = list_first_entry(&hw->io_free, struct efct_hw_io,
+ list_entry);
+ list_del_init(&temp->list_entry);
+ }
+ if (rem_count > 0)
+ efc_log_debug(hw->os, "rmvd %d items from io_free list\n",
+ rem_count);
+
+ /* If MRQ not required, Make sure we dont request feature. */
+ if (hw->config.n_rq == 1)
+ hw->sli.features &= (~SLI4_REQFEAT_MRQP);
+
+ if (sli_init(&hw->sli)) {
+ efc_log_err(hw->os, "SLI failed to initialize\n");
+ return -EIO;
+ }
+
+ if (hw->sliport_healthcheck) {
+ rc = efct_hw_config_sli_port_health_check(hw, 0, 1);
+ if (rc != 0) {
+ efc_log_err(hw->os, "Enable port Health check fail\n");
+ return rc;
+ }
+ }
+
+ /*
+ * Set FDT transfer hint, only works on Lancer
+ */
+ if (hw->sli.if_type == SLI4_INTF_IF_TYPE_2) {
+ /*
+ * Non-fatal error. In particular, we can disregard failure to
+ * set EFCT_HW_FDT_XFER_HINT on devices with legacy firmware
+ * that do not support EFCT_HW_FDT_XFER_HINT feature.
+ */
+ efct_hw_config_set_fdt_xfer_hint(hw, EFCT_HW_FDT_XFER_HINT);
+ }
+
+ /* zero the hashes */
+ memset(hw->cq_hash, 0, sizeof(hw->cq_hash));
+ efc_log_debug(hw->os, "Max CQs %d, hash size = %d\n",
+ EFCT_HW_MAX_NUM_CQ, EFCT_HW_Q_HASH_SIZE);
+
+ memset(hw->rq_hash, 0, sizeof(hw->rq_hash));
+ efc_log_debug(hw->os, "Max RQs %d, hash size = %d\n",
+ EFCT_HW_MAX_NUM_RQ, EFCT_HW_Q_HASH_SIZE);
+
+ memset(hw->wq_hash, 0, sizeof(hw->wq_hash));
+ efc_log_debug(hw->os, "Max WQs %d, hash size = %d\n",
+ EFCT_HW_MAX_NUM_WQ, EFCT_HW_Q_HASH_SIZE);
+
+ rc = efct_hw_init_queues(hw);
+ if (rc)
+ return rc;
+
+ rc = efct_hw_map_wq_cpu(hw);
+ if (rc)
+ return rc;
+
+ /* Allocate and p_st RQ buffers */
+ rc = efct_hw_rx_allocate(hw);
+ if (rc) {
+ efc_log_err(hw->os, "rx_allocate failed\n");
+ return rc;
+ }
+
+ rc = efct_hw_rx_post(hw);
+ if (rc) {
+ efc_log_err(hw->os, "WARNING - error posting RQ buffers\n");
+ return rc;
+ }
+
+ if (hw->config.n_eq == 1) {
+ rc = efct_hw_config_rq(hw);
+ if (rc) {
+ efc_log_err(hw->os, "config rq failed %d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0);
+ if (rc != 0) {
+ efc_log_err(hw->os, "REG_FCFI_MRQ FCFI reg failed\n");
+ return rc;
+ }
+
+ rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0);
+ if (rc != 0) {
+ efc_log_err(hw->os, "REG_FCFI_MRQ MRQ reg failed\n");
+ return rc;
+ }
+ }
+
+ /*
+ * Allocate the WQ request tag pool, if not previously allocated
+ * (the request tag value is 16 bits, thus the pool allocation size
+ * of 64k)
+ */
+ hw->wq_reqtag_pool = efct_hw_reqtag_pool_alloc(hw);
+ if (!hw->wq_reqtag_pool) {
+ efc_log_err(hw->os, "efct_hw_reqtag_pool_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ rc = efct_hw_setup_io(hw);
+ if (rc) {
+ efc_log_err(hw->os, "IO allocation failure\n");
+ return rc;
+ }
+
+ rc = efct_hw_init_io(hw);
+ if (rc) {
+ efc_log_err(hw->os, "IO initialization failure\n");
+ return rc;
+ }
+
+ dma = &hw->loop_map;
+ dma->size = SLI4_MIN_LOOP_MAP_BYTES;
+ dma->virt = dma_alloc_coherent(&hw->os->pci->dev, dma->size, &dma->phys,
+ GFP_KERNEL);
+ if (!dma->virt)
+ return -EIO;
+
+ /*
+ * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ
+ * entries
+ */
+ for (i = 0; i < hw->eq_count; i++)
+ sli_queue_arm(&hw->sli, &hw->eq[i], true);
+
+ /*
+ * Initialize RQ hash
+ */
+ for (i = 0; i < hw->rq_count; i++)
+ efct_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i);
+
+ /*
+ * Initialize WQ hash
+ */
+ for (i = 0; i < hw->wq_count; i++)
+ efct_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i);
+
+ /*
+ * Arming the CQ allows (e.g.) MQ completions to write CQ entries
+ */
+ for (i = 0; i < hw->cq_count; i++) {
+ efct_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i);
+ sli_queue_arm(&hw->sli, &hw->cq[i], true);
+ }
+
+ /* Set RQ process limit*/
+ for (i = 0; i < hw->hw_rq_count; i++) {
+ struct hw_rq *rq = hw->hw_rq[i];
+
+ hw->cq[rq->cq->instance].proc_limit = hw->config.n_io / 2;
+ }
+
+ /* record the fact that the queues are functional */
+ hw->state = EFCT_HW_STATE_ACTIVE;
+ /*
+ * Allocate a HW IOs for send frame.
+ */
+ hw->hw_wq[0]->send_frame_io = efct_hw_io_alloc(hw);
+ if (!hw->hw_wq[0]->send_frame_io)
+ efc_log_err(hw->os, "alloc for send_frame_io failed\n");
+
+ /* Initialize send frame sequence id */
+ atomic_set(&hw->send_frame_seq_id, 0);
+
+ return 0;
+}
+
+int
+efct_hw_parse_filter(struct efct_hw *hw, void *value)
+{
+ int rc = 0;
+ char *p = NULL, *pp = NULL;
+ char *token;
+ u32 idx = 0;
+
+ for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++)
+ hw->config.filter_def[idx] = 0;
+
+ p = kstrdup(value, GFP_KERNEL);
+ if (!p || !*p) {
+ efc_log_err(hw->os, "p is NULL\n");
+ return -ENOMEM;
+ }
+ pp = p;
+
+ idx = 0;
+ while ((token = strsep(&p, ",")) && *token) {
+ if (kstrtou32(token, 0, &hw->config.filter_def[idx++]))
+ efc_log_err(hw->os, "kstrtoint failed\n");
+
+ if (!p || !*p)
+ break;
+
+ if (idx == ARRAY_SIZE(hw->config.filter_def))
+ break;
+ }
+ kfree(pp);
+
+ return rc;
+}
+
+u64
+efct_get_wwnn(struct efct_hw *hw)
+{
+ struct sli4 *sli = &hw->sli;
+ u8 p[8];
+
+ memcpy(p, sli->wwnn, sizeof(p));
+ return get_unaligned_be64(p);
+}
+
+u64
+efct_get_wwpn(struct efct_hw *hw)
+{
+ struct sli4 *sli = &hw->sli;
+ u8 p[8];
+
+ memcpy(p, sli->wwpn, sizeof(p));
+ return get_unaligned_be64(p);
+}
+
+static struct efc_hw_rq_buffer *
+efct_hw_rx_buffer_alloc(struct efct_hw *hw, u32 rqindex, u32 count,
+ u32 size)
+{
+ struct efct *efct = hw->os;
+ struct efc_hw_rq_buffer *rq_buf = NULL;
+ struct efc_hw_rq_buffer *prq;
+ u32 i;
+
+ if (!count)
+ return NULL;
+
+ rq_buf = kmalloc_array(count, sizeof(*rq_buf), GFP_KERNEL);
+ if (!rq_buf)
+ return NULL;
+ memset(rq_buf, 0, sizeof(*rq_buf) * count);
+
+ for (i = 0, prq = rq_buf; i < count; i ++, prq++) {
+ prq->rqindex = rqindex;
+ prq->dma.size = size;
+ prq->dma.virt = dma_alloc_coherent(&efct->pci->dev,
+ prq->dma.size,
+ &prq->dma.phys,
+ GFP_KERNEL);
+ if (!prq->dma.virt) {
+ efc_log_err(hw->os, "DMA allocation failed\n");
+ kfree(rq_buf);
+ return NULL;
+ }
+ }
+ return rq_buf;
+}
+
+static void
+efct_hw_rx_buffer_free(struct efct_hw *hw,
+ struct efc_hw_rq_buffer *rq_buf,
+ u32 count)
+{
+ struct efct *efct = hw->os;
+ u32 i;
+ struct efc_hw_rq_buffer *prq;
+
+ if (rq_buf) {
+ for (i = 0, prq = rq_buf; i < count; i++, prq++) {
+ dma_free_coherent(&efct->pci->dev,
+ prq->dma.size, prq->dma.virt,
+ prq->dma.phys);
+ memset(&prq->dma, 0, sizeof(struct efc_dma));
+ }
+
+ kfree(rq_buf);
+ }
+}
+
+int
+efct_hw_rx_allocate(struct efct_hw *hw)
+{
+ struct efct *efct = hw->os;
+ u32 i;
+ int rc = 0;
+ u32 rqindex = 0;
+ u32 hdr_size = EFCT_HW_RQ_SIZE_HDR;
+ u32 payload_size = hw->config.rq_default_buffer_size;
+
+ rqindex = 0;
+
+ for (i = 0; i < hw->hw_rq_count; i++) {
+ struct hw_rq *rq = hw->hw_rq[i];
+
+ /* Allocate header buffers */
+ rq->hdr_buf = efct_hw_rx_buffer_alloc(hw, rqindex,
+ rq->entry_count,
+ hdr_size);
+ if (!rq->hdr_buf) {
+ efc_log_err(efct, "rx_buffer_alloc hdr_buf failed\n");
+ rc = -EIO;
+ break;
+ }
+
+ efc_log_debug(hw->os,
+ "rq[%2d] rq_id %02d header %4d by %4d bytes\n",
+ i, rq->hdr->id, rq->entry_count, hdr_size);
+
+ rqindex++;
+
+ /* Allocate payload buffers */
+ rq->payload_buf = efct_hw_rx_buffer_alloc(hw, rqindex,
+ rq->entry_count,
+ payload_size);
+ if (!rq->payload_buf) {
+ efc_log_err(efct, "rx_buffer_alloc fb_buf failed\n");
+ rc = -EIO;
+ break;
+ }
+ efc_log_debug(hw->os,
+ "rq[%2d] rq_id %02d default %4d by %4d bytes\n",
+ i, rq->data->id, rq->entry_count, payload_size);
+ rqindex++;
+ }
+
+ return rc ? -EIO : 0;
+}
+
+int
+efct_hw_rx_post(struct efct_hw *hw)
+{
+ u32 i;
+ u32 idx;
+ u32 rq_idx;
+ int rc = 0;
+
+ if (!hw->seq_pool) {
+ u32 count = 0;
+
+ for (i = 0; i < hw->hw_rq_count; i++)
+ count += hw->hw_rq[i]->entry_count;
+
+ hw->seq_pool = kmalloc_array(count,
+ sizeof(struct efc_hw_sequence), GFP_KERNEL);
+ if (!hw->seq_pool)
+ return -ENOMEM;
+ }
+
+ /*
+ * In RQ pair mode, we MUST post the header and payload buffer at the
+ * same time.
+ */
+ for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) {
+ struct hw_rq *rq = hw->hw_rq[rq_idx];
+
+ for (i = 0; i < rq->entry_count - 1; i++) {
+ struct efc_hw_sequence *seq;
+
+ seq = hw->seq_pool + idx;
+ idx++;
+ seq->header = &rq->hdr_buf[i];
+ seq->payload = &rq->payload_buf[i];
+ rc = efct_hw_sequence_free(hw, seq);
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+
+ if (rc && hw->seq_pool)
+ kfree(hw->seq_pool);
+
+ return rc;
+}
+
+void
+efct_hw_rx_free(struct efct_hw *hw)
+{
+ u32 i;
+
+ /* Free hw_rq buffers */
+ for (i = 0; i < hw->hw_rq_count; i++) {
+ struct hw_rq *rq = hw->hw_rq[i];
+
+ if (rq) {
+ efct_hw_rx_buffer_free(hw, rq->hdr_buf,
+ rq->entry_count);
+ rq->hdr_buf = NULL;
+ efct_hw_rx_buffer_free(hw, rq->payload_buf,
+ rq->entry_count);
+ rq->payload_buf = NULL;
+ }
+ }
+}
+
+static int
+efct_hw_cmd_submit_pending(struct efct_hw *hw)
+{
+ int rc = 0;
+
+ /* Assumes lock held */
+
+ /* Only submit MQE if there's room */
+ while (hw->cmd_head_count < (EFCT_HW_MQ_DEPTH - 1) &&
+ !list_empty(&hw->cmd_pending)) {
+ struct efct_command_ctx *ctx;
+
+ ctx = list_first_entry(&hw->cmd_pending,
+ struct efct_command_ctx, list_entry);
+ if (!ctx)
+ break;
+
+ list_del_init(&ctx->list_entry);
+
+ list_add_tail(&ctx->list_entry, &hw->cmd_head);
+ hw->cmd_head_count++;
+ if (sli_mq_write(&hw->sli, hw->mq, ctx->buf) < 0) {
+ efc_log_debug(hw->os,
+ "sli_queue_write failed: %d\n", rc);
+ rc = -EIO;
+ break;
+ }
+ }
+ return rc;
+}
+
+int
+efct_hw_command(struct efct_hw *hw, u8 *cmd, u32 opts, void *cb, void *arg)
+{
+ int rc = -EIO;
+ unsigned long flags = 0;
+ void *bmbx = NULL;
+
+ /*
+ * If the chip is in an error state (UE'd) then reject this mailbox
+ * command.
+ */
+ if (sli_fw_error_status(&hw->sli) > 0) {
+ efc_log_crit(hw->os, "Chip in an error state - reset needed\n");
+ efc_log_crit(hw->os, "status=%#x error1=%#x error2=%#x\n",
+ sli_reg_read_status(&hw->sli),
+ sli_reg_read_err1(&hw->sli),
+ sli_reg_read_err2(&hw->sli));
+
+ return -EIO;
+ }
+
+ /*
+ * Send a mailbox command to the hardware, and either wait for
+ * a completion (EFCT_CMD_POLL) or get an optional asynchronous
+ * completion (EFCT_CMD_NOWAIT).
+ */
+
+ if (opts == EFCT_CMD_POLL) {
+ mutex_lock(&hw->bmbx_lock);
+ bmbx = hw->sli.bmbx.virt;
+
+ memcpy(bmbx, cmd, SLI4_BMBX_SIZE);
+
+ if (sli_bmbx_command(&hw->sli) == 0) {
+ rc = 0;
+ memcpy(cmd, bmbx, SLI4_BMBX_SIZE);
+ }
+ mutex_unlock(&hw->bmbx_lock);
+ } else if (opts == EFCT_CMD_NOWAIT) {
+ struct efct_command_ctx *ctx = NULL;
+
+ if (hw->state != EFCT_HW_STATE_ACTIVE) {
+ efc_log_err(hw->os, "Can't send command, HW state=%d\n",
+ hw->state);
+ return -EIO;
+ }
+
+ ctx = mempool_alloc(hw->cmd_ctx_pool, GFP_ATOMIC);
+ if (!ctx)
+ return -ENOSPC;
+
+ memset(ctx, 0, sizeof(struct efct_command_ctx));
+
+ if (cb) {
+ ctx->cb = cb;
+ ctx->arg = arg;
+ }
+
+ memcpy(ctx->buf, cmd, SLI4_BMBX_SIZE);
+ ctx->ctx = hw;
+
+ spin_lock_irqsave(&hw->cmd_lock, flags);
+
+ /* Add to pending list */
+ INIT_LIST_HEAD(&ctx->list_entry);
+ list_add_tail(&ctx->list_entry, &hw->cmd_pending);
+
+ /* Submit as much of the pending list as we can */
+ rc = efct_hw_cmd_submit_pending(hw);
+
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+ }
+
+ return rc;
+}
+
+static int
+efct_hw_command_process(struct efct_hw *hw, int status, u8 *mqe,
+ size_t size)
+{
+ struct efct_command_ctx *ctx = NULL;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&hw->cmd_lock, flags);
+ if (!list_empty(&hw->cmd_head)) {
+ ctx = list_first_entry(&hw->cmd_head,
+ struct efct_command_ctx, list_entry);
+ list_del_init(&ctx->list_entry);
+ }
+ if (!ctx) {
+ efc_log_err(hw->os, "no command context\n");
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+ return -EIO;
+ }
+
+ hw->cmd_head_count--;
+
+ /* Post any pending requests */
+ efct_hw_cmd_submit_pending(hw);
+
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+
+ if (ctx->cb) {
+ memcpy(ctx->buf, mqe, size);
+ ctx->cb(hw, status, ctx->buf, ctx->arg);
+ }
+
+ mempool_free(ctx, hw->cmd_ctx_pool);
+
+ return 0;
+}
+
+static int
+efct_hw_mq_process(struct efct_hw *hw,
+ int status, struct sli4_queue *mq)
+{
+ u8 mqe[SLI4_BMBX_SIZE];
+ int rc;
+
+ rc = sli_mq_read(&hw->sli, mq, mqe);
+ if (!rc)
+ rc = efct_hw_command_process(hw, status, mqe, mq->size);
+
+ return rc;
+}
+
+static int
+efct_hw_command_cancel(struct efct_hw *hw)
+{
+ unsigned long flags = 0;
+ int rc = 0;
+
+ spin_lock_irqsave(&hw->cmd_lock, flags);
+
+ /*
+ * Manually clean up remaining commands. Note: since this calls
+ * efct_hw_command_process(), we'll also process the cmd_pending
+ * list, so no need to manually clean that out.
+ */
+ while (!list_empty(&hw->cmd_head)) {
+ u8 mqe[SLI4_BMBX_SIZE] = { 0 };
+ struct efct_command_ctx *ctx;
+
+ ctx = list_first_entry(&hw->cmd_head,
+ struct efct_command_ctx, list_entry);
+
+ efc_log_debug(hw->os, "hung command %08x\n",
+ !ctx ? U32_MAX : *((u32 *)ctx->buf));
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+ rc = efct_hw_command_process(hw, -1, mqe, SLI4_BMBX_SIZE);
+ spin_lock_irqsave(&hw->cmd_lock, flags);
+ }
+
+ spin_unlock_irqrestore(&hw->cmd_lock, flags);
+
+ return rc;
+}
+
+static void
+efct_mbox_rsp_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg)
+{
+ struct efct_mbox_rqst_ctx *ctx = arg;
+
+ if (ctx) {
+ if (ctx->callback)
+ (*ctx->callback)(hw->os->efcport, status, mqe,
+ ctx->arg);
+
+ mempool_free(ctx, hw->mbox_rqst_pool);
+ }
+}
+
+int
+efct_issue_mbox_rqst(void *base, void *cmd, void *cb, void *arg)
+{
+ struct efct_mbox_rqst_ctx *ctx;
+ struct efct *efct = base;
+ struct efct_hw *hw = &efct->hw;
+ int rc;
+
+ /*
+ * Allocate a callback context (which includes the mbox cmd buffer),
+ * we need this to be persistent as the mbox cmd submission may be
+ * queued and executed later execution.
+ */
+ ctx = mempool_alloc(hw->mbox_rqst_pool, GFP_ATOMIC);
+ if (!ctx)
+ return -EIO;
+
+ ctx->callback = cb;
+ ctx->arg = arg;
+
+ rc = efct_hw_command(hw, cmd, EFCT_CMD_NOWAIT, efct_mbox_rsp_cb, ctx);
+ if (rc) {
+ efc_log_err(efct, "issue mbox rqst failure rc:%d\n", rc);
+ mempool_free(ctx, hw->mbox_rqst_pool);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static inline struct efct_hw_io *
+_efct_hw_io_alloc(struct efct_hw *hw)
+{
+ struct efct_hw_io *io = NULL;
+
+ if (!list_empty(&hw->io_free)) {
+ io = list_first_entry(&hw->io_free, struct efct_hw_io,
+ list_entry);
+ list_del(&io->list_entry);
+ }
+ if (io) {
+ INIT_LIST_HEAD(&io->list_entry);
+ list_add_tail(&io->list_entry, &hw->io_inuse);
+ io->state = EFCT_HW_IO_STATE_INUSE;
+ io->abort_reqtag = U32_MAX;
+ io->wq = hw->wq_cpu_array[raw_smp_processor_id()];
+ if (!io->wq) {
+ efc_log_err(hw->os, "WQ not assigned for cpu:%d\n",
+ raw_smp_processor_id());
+ io->wq = hw->hw_wq[0];
+ }
+ kref_init(&io->ref);
+ io->release = efct_hw_io_free_internal;
+ } else {
+ atomic_add(1, &hw->io_alloc_failed_count);
+ }
+
+ return io;
+}
+
+struct efct_hw_io *
+efct_hw_io_alloc(struct efct_hw *hw)
+{
+ struct efct_hw_io *io = NULL;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&hw->io_lock, flags);
+ io = _efct_hw_io_alloc(hw);
+ spin_unlock_irqrestore(&hw->io_lock, flags);
+
+ return io;
+}
+
+static void
+efct_hw_io_free_move_correct_list(struct efct_hw *hw,
+ struct efct_hw_io *io)
+{
+ /*
+ * When an IO is freed, depending on the exchange busy flag,
+ * move it to the correct list.
+ */
+ if (io->xbusy) {
+ /*
+ * add to wait_free list and wait for XRI_ABORTED CQEs to clean
+ * up
+ */
+ INIT_LIST_HEAD(&io->list_entry);
+ list_add_tail(&io->list_entry, &hw->io_wait_free);
+ io->state = EFCT_HW_IO_STATE_WAIT_FREE;
+ } else {
+ /* IO not busy, add to free list */
+ INIT_LIST_HEAD(&io->list_entry);
+ list_add_tail(&io->list_entry, &hw->io_free);
+ io->state = EFCT_HW_IO_STATE_FREE;
+ }
+}
+
+static inline void
+efct_hw_io_free_common(struct efct_hw *hw, struct efct_hw_io *io)
+{
+ /* initialize IO fields */
+ efct_hw_init_free_io(io);
+
+ /* Restore default SGL */
+ efct_hw_io_restore_sgl(hw, io);
+}
+
+void
+efct_hw_io_free_internal(struct kref *arg)
+{
+ unsigned long flags = 0;
+ struct efct_hw_io *io = container_of(arg, struct efct_hw_io, ref);
+ struct efct_hw *hw = io->hw;
+
+ /* perform common cleanup */
+ efct_hw_io_free_common(hw, io);
+
+ spin_lock_irqsave(&hw->io_lock, flags);
+ /* remove from in-use list */
+ if (!list_empty(&io->list_entry) && !list_empty(&hw->io_inuse)) {
+ list_del_init(&io->list_entry);
+ efct_hw_io_free_move_correct_list(hw, io);
+ }
+ spin_unlock_irqrestore(&hw->io_lock, flags);
+}
+
+int
+efct_hw_io_free(struct efct_hw *hw, struct efct_hw_io *io)
+{
+ return kref_put(&io->ref, io->release);
+}
+
+struct efct_hw_io *
+efct_hw_io_lookup(struct efct_hw *hw, u32 xri)
+{
+ u32 ioindex;
+
+ ioindex = xri - hw->sli.ext[SLI4_RSRC_XRI].base[0];
+ return hw->io[ioindex];
+}
+
+int
+efct_hw_io_init_sges(struct efct_hw *hw, struct efct_hw_io *io,
+ enum efct_hw_io_type type)
+{
+ struct sli4_sge *data = NULL;
+ u32 i = 0;
+ u32 skips = 0;
+ u32 sge_flags = 0;
+
+ if (!io) {
+ efc_log_err(hw->os, "bad parameter hw=%p io=%p\n", hw, io);
+ return -EIO;
+ }
+
+ /* Clear / reset the scatter-gather list */
+ io->sgl = &io->def_sgl;
+ io->sgl_count = io->def_sgl_count;
+ io->first_data_sge = 0;
+
+ memset(io->sgl->virt, 0, 2 * sizeof(struct sli4_sge));
+ io->n_sge = 0;
+ io->sge_offset = 0;
+
+ io->type = type;
+
+ data = io->sgl->virt;
+
+ /*
+ * Some IO types have underlying hardware requirements on the order
+ * of SGEs. Process all special entries here.
+ */
+ switch (type) {
+ case EFCT_HW_IO_TARGET_WRITE:
+
+ /* populate host resident XFER_RDY buffer */
+ sge_flags = le32_to_cpu(data->dw2_flags);
+ sge_flags &= (~SLI4_SGE_TYPE_MASK);
+ sge_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT);
+ data->buffer_address_high =
+ cpu_to_le32(upper_32_bits(io->xfer_rdy.phys));
+ data->buffer_address_low =
+ cpu_to_le32(lower_32_bits(io->xfer_rdy.phys));
+ data->buffer_length = cpu_to_le32(io->xfer_rdy.size);
+ data->dw2_flags = cpu_to_le32(sge_flags);
+ data++;
+
+ skips = EFCT_TARGET_WRITE_SKIPS;
+
+ io->n_sge = 1;
+ break;
+ case EFCT_HW_IO_TARGET_READ:
+ /*
+ * For FCP_TSEND64, the first 2 entries are SKIP SGE's
+ */
+ skips = EFCT_TARGET_READ_SKIPS;
+ break;
+ case EFCT_HW_IO_TARGET_RSP:
+ /*
+ * No skips, etc. for FCP_TRSP64
+ */
+ break;
+ default:
+ efc_log_err(hw->os, "unsupported IO type %#x\n", type);
+ return -EIO;
+ }
+
+ /*
+ * Write skip entries
+ */
+ for (i = 0; i < skips; i++) {
+ sge_flags = le32_to_cpu(data->dw2_flags);
+ sge_flags &= (~SLI4_SGE_TYPE_MASK);
+ sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT);
+ data->dw2_flags = cpu_to_le32(sge_flags);
+ data++;
+ }
+
+ io->n_sge += skips;
+
+ /*
+ * Set last
+ */
+ sge_flags = le32_to_cpu(data->dw2_flags);
+ sge_flags |= SLI4_SGE_LAST;
+ data->dw2_flags = cpu_to_le32(sge_flags);
+
+ return 0;
+}
+
+int
+efct_hw_io_add_sge(struct efct_hw *hw, struct efct_hw_io *io,
+ uintptr_t addr, u32 length)
+{
+ struct sli4_sge *data = NULL;
+ u32 sge_flags = 0;
+
+ if (!io || !addr || !length) {
+ efc_log_err(hw->os,
+ "bad parameter hw=%p io=%p addr=%lx length=%u\n",
+ hw, io, addr, length);
+ return -EIO;
+ }
+
+ if (length > hw->sli.sge_supported_length) {
+ efc_log_err(hw->os,
+ "length of SGE %d bigger than allowed %d\n",
+ length, hw->sli.sge_supported_length);
+ return -EIO;
+ }
+
+ data = io->sgl->virt;
+ data += io->n_sge;
+
+ sge_flags = le32_to_cpu(data->dw2_flags);
+ sge_flags &= ~SLI4_SGE_TYPE_MASK;
+ sge_flags |= SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT;
+ sge_flags &= ~SLI4_SGE_DATA_OFFSET_MASK;
+ sge_flags |= SLI4_SGE_DATA_OFFSET_MASK & io->sge_offset;
+
+ data->buffer_address_high = cpu_to_le32(upper_32_bits(addr));
+ data->buffer_address_low = cpu_to_le32(lower_32_bits(addr));
+ data->buffer_length = cpu_to_le32(length);
+
+ /*
+ * Always assume this is the last entry and mark as such.
+ * If this is not the first entry unset the "last SGE"
+ * indication for the previous entry
+ */
+ sge_flags |= SLI4_SGE_LAST;
+ data->dw2_flags = cpu_to_le32(sge_flags);
+
+ if (io->n_sge) {
+ sge_flags = le32_to_cpu(data[-1].dw2_flags);
+ sge_flags &= ~SLI4_SGE_LAST;
+ data[-1].dw2_flags = cpu_to_le32(sge_flags);
+ }
+
+ /* Set first_data_bde if not previously set */
+ if (io->first_data_sge == 0)
+ io->first_data_sge = io->n_sge;
+
+ io->sge_offset += length;
+ io->n_sge++;
+
+ return 0;
+}
+
+void
+efct_hw_io_abort_all(struct efct_hw *hw)
+{
+ struct efct_hw_io *io_to_abort = NULL;
+ struct efct_hw_io *next_io = NULL;
+
+ list_for_each_entry_safe(io_to_abort, next_io,
+ &hw->io_inuse, list_entry) {
+ efct_hw_io_abort(hw, io_to_abort, true, NULL, NULL);
+ }
+}
+
+static void
+efct_hw_wq_process_abort(void *arg, u8 *cqe, int status)
+{
+ struct efct_hw_io *io = arg;
+ struct efct_hw *hw = io->hw;
+ u32 ext = 0;
+ u32 len = 0;
+ struct hw_wq_callback *wqcb;
+
+ /*
+ * For IOs that were aborted internally, we may need to issue the
+ * callback here depending on whether a XRI_ABORTED CQE is expected ot
+ * not. If the status is Local Reject/No XRI, then
+ * issue the callback now.
+ */
+ ext = sli_fc_ext_status(&hw->sli, cqe);
+ if (status == SLI4_FC_WCQE_STATUS_LOCAL_REJECT &&
+ ext == SLI4_FC_LOCAL_REJECT_NO_XRI && io->done) {
+ efct_hw_done_t done = io->done;
+
+ io->done = NULL;
+
+ /*
+ * Use latched status as this is always saved for an internal
+ * abort Note: We won't have both a done and abort_done
+ * function, so don't worry about
+ * clobbering the len, status and ext fields.
+ */
+ status = io->saved_status;
+ len = io->saved_len;
+ ext = io->saved_ext;
+ io->status_saved = false;
+ done(io, len, status, ext, io->arg);
+ }
+
+ if (io->abort_done) {
+ efct_hw_done_t done = io->abort_done;
+
+ io->abort_done = NULL;
+ done(io, len, status, ext, io->abort_arg);
+ }
+
+ /* clear abort bit to indicate abort is complete */
+ io->abort_in_progress = false;
+
+ /* Free the WQ callback */
+ if (io->abort_reqtag == U32_MAX) {
+ efc_log_err(hw->os, "HW IO already freed\n");
+ return;
+ }
+
+ wqcb = efct_hw_reqtag_get_instance(hw, io->abort_reqtag);
+ efct_hw_reqtag_free(hw, wqcb);
+
+ /*
+ * Call efct_hw_io_free() because this releases the WQ reservation as
+ * well as doing the refcount put. Don't duplicate the code here.
+ */
+ (void)efct_hw_io_free(hw, io);
+}
+
+static void
+efct_hw_fill_abort_wqe(struct efct_hw *hw, struct efct_hw_wqe *wqe)
+{
+ struct sli4_abort_wqe *abort = (void *)wqe->wqebuf;
+
+ memset(abort, 0, hw->sli.wqe_size);
+
+ abort->criteria = SLI4_ABORT_CRITERIA_XRI_TAG;
+ abort->ia_ir_byte |= wqe->send_abts ? 0 : 1;
+
+ /* Suppress ABTS retries */
+ abort->ia_ir_byte |= SLI4_ABRT_WQE_IR;
+
+ abort->t_tag = cpu_to_le32(wqe->id);
+ abort->command = SLI4_WQE_ABORT;
+ abort->request_tag = cpu_to_le16(wqe->abort_reqtag);
+
+ abort->dw10w0_flags = cpu_to_le16(SLI4_ABRT_WQE_QOSD);
+
+ abort->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT);
+}
+
+int
+efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort,
+ bool send_abts, void *cb, void *arg)
+{
+ struct hw_wq_callback *wqcb;
+ unsigned long flags = 0;
+
+ if (!io_to_abort) {
+ efc_log_err(hw->os, "bad parameter hw=%p io=%p\n",
+ hw, io_to_abort);
+ return -EIO;
+ }
+
+ if (hw->state != EFCT_HW_STATE_ACTIVE) {
+ efc_log_err(hw->os, "cannot send IO abort, HW state=%d\n",
+ hw->state);
+ return -EIO;
+ }
+
+ /* take a reference on IO being aborted */
+ if (kref_get_unless_zero(&io_to_abort->ref) == 0) {
+ /* command no longer active */
+ efc_log_debug(hw->os,
+ "io not active xri=0x%x tag=0x%x\n",
+ io_to_abort->indicator, io_to_abort->reqtag);
+ return -ENOENT;
+ }
+
+ /* Must have a valid WQ reference */
+ if (!io_to_abort->wq) {
+ efc_log_debug(hw->os, "io_to_abort xri=0x%x not active on WQ\n",
+ io_to_abort->indicator);
+ /* efct_ref_get(): same function */
+ kref_put(&io_to_abort->ref, io_to_abort->release);
+ return -ENOENT;
+ }
+
+ /*
+ * Validation checks complete; now check to see if already being
+ * aborted, if not set the flag.
+ */
+ if (cmpxchg(&io_to_abort->abort_in_progress, false, true)) {
+ /* efct_ref_get(): same function */
+ kref_put(&io_to_abort->ref, io_to_abort->release);
+ efc_log_debug(hw->os,
+ "io already being aborted xri=0x%x tag=0x%x\n",
+ io_to_abort->indicator, io_to_abort->reqtag);
+ return -EINPROGRESS;
+ }
+
+ /*
+ * If we got here, the possibilities are:
+ * - host owned xri
+ * - io_to_abort->wq_index != U32_MAX
+ * - submit ABORT_WQE to same WQ
+ * - port owned xri:
+ * - rxri: io_to_abort->wq_index == U32_MAX
+ * - submit ABORT_WQE to any WQ
+ * - non-rxri
+ * - io_to_abort->index != U32_MAX
+ * - submit ABORT_WQE to same WQ
+ * - io_to_abort->index == U32_MAX
+ * - submit ABORT_WQE to any WQ
+ */
+ io_to_abort->abort_done = cb;
+ io_to_abort->abort_arg = arg;
+
+ /* Allocate a request tag for the abort portion of this IO */
+ wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort);
+ if (!wqcb) {
+ efc_log_err(hw->os, "can't allocate request tag\n");
+ return -ENOSPC;
+ }
+
+ io_to_abort->abort_reqtag = wqcb->instance_index;
+ io_to_abort->wqe.send_abts = send_abts;
+ io_to_abort->wqe.id = io_to_abort->indicator;
+ io_to_abort->wqe.abort_reqtag = io_to_abort->abort_reqtag;
+
+ /*
+ * If the wqe is on the pending list, then set this wqe to be
+ * aborted when the IO's wqe is removed from the list.
+ */
+ if (io_to_abort->wq) {
+ spin_lock_irqsave(&io_to_abort->wq->queue->lock, flags);
+ if (io_to_abort->wqe.list_entry.next) {
+ io_to_abort->wqe.abort_wqe_submit_needed = true;
+ spin_unlock_irqrestore(&io_to_abort->wq->queue->lock,
+ flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&io_to_abort->wq->queue->lock, flags);
+ }
+
+ efct_hw_fill_abort_wqe(hw, &io_to_abort->wqe);
+
+ /* ABORT_WQE does not actually utilize an XRI on the Port,
+ * therefore, keep xbusy as-is to track the exchange's state,
+ * not the ABORT_WQE's state
+ */
+ if (efct_hw_wq_write(io_to_abort->wq, &io_to_abort->wqe)) {
+ io_to_abort->abort_in_progress = false;
+ /* efct_ref_get(): same function */
+ kref_put(&io_to_abort->ref, io_to_abort->release);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void
+efct_hw_reqtag_pool_free(struct efct_hw *hw)
+{
+ u32 i;
+ struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool;
+ struct hw_wq_callback *wqcb = NULL;
+
+ if (reqtag_pool) {
+ for (i = 0; i < U16_MAX; i++) {
+ wqcb = reqtag_pool->tags[i];
+ if (!wqcb)
+ continue;
+
+ kfree(wqcb);
+ }
+ kfree(reqtag_pool);
+ hw->wq_reqtag_pool = NULL;
+ }
+}
+
+struct reqtag_pool *
+efct_hw_reqtag_pool_alloc(struct efct_hw *hw)
+{
+ u32 i = 0;
+ struct reqtag_pool *reqtag_pool;
+ struct hw_wq_callback *wqcb;
+
+ reqtag_pool = kzalloc(sizeof(*reqtag_pool), GFP_KERNEL);
+ if (!reqtag_pool)
+ return NULL;
+
+ INIT_LIST_HEAD(&reqtag_pool->freelist);
+ /* initialize reqtag pool lock */
+ spin_lock_init(&reqtag_pool->lock);
+ for (i = 0; i < U16_MAX; i++) {
+ wqcb = kmalloc(sizeof(*wqcb), GFP_KERNEL);
+ if (!wqcb)
+ break;
+
+ reqtag_pool->tags[i] = wqcb;
+ wqcb->instance_index = i;
+ wqcb->callback = NULL;
+ wqcb->arg = NULL;
+ INIT_LIST_HEAD(&wqcb->list_entry);
+ list_add_tail(&wqcb->list_entry, &reqtag_pool->freelist);
+ }
+
+ return reqtag_pool;
+}
+
+struct hw_wq_callback *
+efct_hw_reqtag_alloc(struct efct_hw *hw,
+ void (*callback)(void *arg, u8 *cqe, int status),
+ void *arg)
+{
+ struct hw_wq_callback *wqcb = NULL;
+ struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool;
+ unsigned long flags = 0;
+
+ if (!callback)
+ return wqcb;
+
+ spin_lock_irqsave(&reqtag_pool->lock, flags);
+
+ if (!list_empty(&reqtag_pool->freelist)) {
+ wqcb = list_first_entry(&reqtag_pool->freelist,
+ struct hw_wq_callback, list_entry);
+ }
+
+ if (wqcb) {
+ list_del_init(&wqcb->list_entry);
+ spin_unlock_irqrestore(&reqtag_pool->lock, flags);
+ wqcb->callback = callback;
+ wqcb->arg = arg;
+ } else {
+ spin_unlock_irqrestore(&reqtag_pool->lock, flags);
+ }
+
+ return wqcb;
+}
+
+void
+efct_hw_reqtag_free(struct efct_hw *hw, struct hw_wq_callback *wqcb)
+{
+ unsigned long flags = 0;
+ struct reqtag_pool *reqtag_pool = hw->wq_reqtag_pool;
+
+ if (!wqcb->callback)
+ efc_log_err(hw->os, "WQCB is already freed\n");
+
+ spin_lock_irqsave(&reqtag_pool->lock, flags);
+ wqcb->callback = NULL;
+ wqcb->arg = NULL;
+ INIT_LIST_HEAD(&wqcb->list_entry);
+ list_add(&wqcb->list_entry, &hw->wq_reqtag_pool->freelist);
+ spin_unlock_irqrestore(&reqtag_pool->lock, flags);
+}
+
+struct hw_wq_callback *
+efct_hw_reqtag_get_instance(struct efct_hw *hw, u32 instance_index)
+{
+ struct hw_wq_callback *wqcb;
+
+ wqcb = hw->wq_reqtag_pool->tags[instance_index];
+ if (!wqcb)
+ efc_log_err(hw->os, "wqcb for instance %d is null\n",
+ instance_index);
+
+ return wqcb;
+}
+
+int
+efct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id)
+{
+ int index = -1;
+ int i = id & (EFCT_HW_Q_HASH_SIZE - 1);
+
+ /*
+ * Since the hash is always bigger than the maximum number of Qs, then
+ * we never have to worry about an infinite loop. We will always find
+ * an unused entry.
+ */
+ do {
+ if (hash[i].in_use && hash[i].id == id)
+ index = hash[i].index;
+ else
+ i = (i + 1) & (EFCT_HW_Q_HASH_SIZE - 1);
+ } while (index == -1 && hash[i].in_use);
+
+ return index;
+}
+
+int
+efct_hw_process(struct efct_hw *hw, u32 vector,
+ u32 max_isr_time_msec)
+{
+ struct hw_eq *eq;
+
+ /*
+ * The caller should disable interrupts if they wish to prevent us
+ * from processing during a shutdown. The following states are defined:
+ * EFCT_HW_STATE_UNINITIALIZED - No queues allocated
+ * EFCT_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset,
+ * queues are cleared.
+ * EFCT_HW_STATE_ACTIVE - Chip and queues are operational
+ * EFCT_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions
+ * EFCT_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox
+ * completions.
+ */
+ if (hw->state == EFCT_HW_STATE_UNINITIALIZED)
+ return 0;
+
+ /* Get pointer to struct hw_eq */
+ eq = hw->hw_eq[vector];
+ if (!eq)
+ return 0;
+
+ eq->use_count++;
+
+ return efct_hw_eq_process(hw, eq, max_isr_time_msec);
+}
+
+int
+efct_hw_eq_process(struct efct_hw *hw, struct hw_eq *eq,
+ u32 max_isr_time_msec)
+{
+ u8 eqe[sizeof(struct sli4_eqe)] = { 0 };
+ u32 tcheck_count;
+ u64 tstart;
+ u64 telapsed;
+ bool done = false;
+
+ tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS;
+ tstart = jiffies_to_msecs(jiffies);
+
+ while (!done && !sli_eq_read(&hw->sli, eq->queue, eqe)) {
+ u16 cq_id = 0;
+ int rc;
+
+ rc = sli_eq_parse(&hw->sli, eqe, &cq_id);
+ if (unlikely(rc)) {
+ if (rc == SLI4_EQE_STATUS_EQ_FULL) {
+ u32 i;
+
+ /*
+ * Received a sentinel EQE indicating the
+ * EQ is full. Process all CQs
+ */
+ for (i = 0; i < hw->cq_count; i++)
+ efct_hw_cq_process(hw, hw->hw_cq[i]);
+ continue;
+ } else {
+ return rc;
+ }
+ } else {
+ int index;
+
+ index = efct_hw_queue_hash_find(hw->cq_hash, cq_id);
+
+ if (likely(index >= 0))
+ efct_hw_cq_process(hw, hw->hw_cq[index]);
+ else
+ efc_log_err(hw->os, "bad CQ_ID %#06x\n", cq_id);
+ }
+
+ if (eq->queue->n_posted > eq->queue->posted_limit)
+ sli_queue_arm(&hw->sli, eq->queue, false);
+
+ if (tcheck_count && (--tcheck_count == 0)) {
+ tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS;
+ telapsed = jiffies_to_msecs(jiffies) - tstart;
+ if (telapsed >= max_isr_time_msec)
+ done = true;
+ }
+ }
+ sli_queue_eq_arm(&hw->sli, eq->queue, true);
+
+ return 0;
+}
+
+static int
+_efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe)
+{
+ int queue_rc;
+
+ /* Every so often, set the wqec bit to generate comsummed completions */
+ if (wq->wqec_count)
+ wq->wqec_count--;
+
+ if (wq->wqec_count == 0) {
+ struct sli4_generic_wqe *genwqe = (void *)wqe->wqebuf;
+
+ genwqe->cmdtype_wqec_byte |= SLI4_GEN_WQE_WQEC;
+ wq->wqec_count = wq->wqec_set_count;
+ }
+
+ /* Decrement WQ free count */
+ wq->free_count--;
+
+ queue_rc = sli_wq_write(&wq->hw->sli, wq->queue, wqe->wqebuf);
+
+ return (queue_rc < 0) ? -EIO : 0;
+}
+
+static void
+hw_wq_submit_pending(struct hw_wq *wq, u32 update_free_count)
+{
+ struct efct_hw_wqe *wqe;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&wq->queue->lock, flags);
+
+ /* Update free count with value passed in */
+ wq->free_count += update_free_count;
+
+ while ((wq->free_count > 0) && (!list_empty(&wq->pending_list))) {
+ wqe = list_first_entry(&wq->pending_list,
+ struct efct_hw_wqe, list_entry);
+ list_del_init(&wqe->list_entry);
+ _efct_hw_wq_write(wq, wqe);
+
+ if (wqe->abort_wqe_submit_needed) {
+ wqe->abort_wqe_submit_needed = false;
+ efct_hw_fill_abort_wqe(wq->hw, wqe);
+ INIT_LIST_HEAD(&wqe->list_entry);
+ list_add_tail(&wqe->list_entry, &wq->pending_list);
+ wq->wq_pending_count++;
+ }
+ }
+
+ spin_unlock_irqrestore(&wq->queue->lock, flags);
+}
+
+void
+efct_hw_cq_process(struct efct_hw *hw, struct hw_cq *cq)
+{
+ u8 cqe[sizeof(struct sli4_mcqe)];
+ u16 rid = U16_MAX;
+ /* completion type */
+ enum sli4_qentry ctype;
+ u32 n_processed = 0;
+ u32 tstart, telapsed;
+
+ tstart = jiffies_to_msecs(jiffies);
+
+ while (!sli_cq_read(&hw->sli, cq->queue, cqe)) {
+ int status;
+
+ status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid);
+ /*
+ * The sign of status is significant. If status is:
+ * == 0 : call completed correctly and
+ * the CQE indicated success
+ * > 0 : call completed correctly and
+ * the CQE indicated an error
+ * < 0 : call failed and no information is available about the
+ * CQE
+ */
+ if (status < 0) {
+ if (status == SLI4_MCQE_STATUS_NOT_COMPLETED)
+ /*
+ * Notification that an entry was consumed,
+ * but not completed
+ */
+ continue;
+
+ break;
+ }
+
+ switch (ctype) {
+ case SLI4_QENTRY_ASYNC:
+ sli_cqe_async(&hw->sli, cqe);
+ break;
+ case SLI4_QENTRY_MQ:
+ /*
+ * Process MQ entry. Note there is no way to determine
+ * the MQ_ID from the completion entry.
+ */
+ efct_hw_mq_process(hw, status, hw->mq);
+ break;
+ case SLI4_QENTRY_WQ:
+ efct_hw_wq_process(hw, cq, cqe, status, rid);
+ break;
+ case SLI4_QENTRY_WQ_RELEASE: {
+ u32 wq_id = rid;
+ int index;
+ struct hw_wq *wq = NULL;
+
+ index = efct_hw_queue_hash_find(hw->wq_hash, wq_id);
+
+ if (likely(index >= 0)) {
+ wq = hw->hw_wq[index];
+ } else {
+ efc_log_err(hw->os, "bad WQ_ID %#06x\n", wq_id);
+ break;
+ }
+ /* Submit any HW IOs that are on the WQ pending list */
+ hw_wq_submit_pending(wq, wq->wqec_set_count);
+
+ break;
+ }
+
+ case SLI4_QENTRY_RQ:
+ efct_hw_rqpair_process_rq(hw, cq, cqe);
+ break;
+ case SLI4_QENTRY_XABT: {
+ efct_hw_xabt_process(hw, cq, cqe, rid);
+ break;
+ }
+ default:
+ efc_log_debug(hw->os, "unhandled ctype=%#x rid=%#x\n",
+ ctype, rid);
+ break;
+ }
+
+ n_processed++;
+ if (n_processed == cq->queue->proc_limit)
+ break;
+
+ if (cq->queue->n_posted >= cq->queue->posted_limit)
+ sli_queue_arm(&hw->sli, cq->queue, false);
+ }
+
+ sli_queue_arm(&hw->sli, cq->queue, true);
+
+ if (n_processed > cq->queue->max_num_processed)
+ cq->queue->max_num_processed = n_processed;
+ telapsed = jiffies_to_msecs(jiffies) - tstart;
+ if (telapsed > cq->queue->max_process_time)
+ cq->queue->max_process_time = telapsed;
+}
+
+void
+efct_hw_wq_process(struct efct_hw *hw, struct hw_cq *cq,
+ u8 *cqe, int status, u16 rid)
+{
+ struct hw_wq_callback *wqcb;
+
+ if (rid == EFCT_HW_REQUE_XRI_REGTAG) {
+ if (status)
+ efc_log_err(hw->os, "reque xri failed, status = %d\n",
+ status);
+ return;
+ }
+
+ wqcb = efct_hw_reqtag_get_instance(hw, rid);
+ if (!wqcb) {
+ efc_log_err(hw->os, "invalid request tag: x%x\n", rid);
+ return;
+ }
+
+ if (!wqcb->callback) {
+ efc_log_err(hw->os, "wqcb callback is NULL\n");
+ return;
+ }
+
+ (*wqcb->callback)(wqcb->arg, cqe, status);
+}
+
+void
+efct_hw_xabt_process(struct efct_hw *hw, struct hw_cq *cq,
+ u8 *cqe, u16 rid)
+{
+ /* search IOs wait free list */
+ struct efct_hw_io *io = NULL;
+ unsigned long flags = 0;
+
+ io = efct_hw_io_lookup(hw, rid);
+ if (!io) {
+ /* IO lookup failure should never happen */
+ efc_log_err(hw->os, "xabt io lookup failed rid=%#x\n", rid);
+ return;
+ }
+
+ if (!io->xbusy)
+ efc_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid);
+ else
+ /* mark IO as no longer busy */
+ io->xbusy = false;
+
+ /*
+ * For IOs that were aborted internally, we need to issue any pending
+ * callback here.
+ */
+ if (io->done) {
+ efct_hw_done_t done = io->done;
+ void *arg = io->arg;
+
+ /*
+ * Use latched status as this is always saved for an internal
+ * abort
+ */
+ int status = io->saved_status;
+ u32 len = io->saved_len;
+ u32 ext = io->saved_ext;
+
+ io->done = NULL;
+ io->status_saved = false;
+
+ done(io, len, status, ext, arg);
+ }
+
+ spin_lock_irqsave(&hw->io_lock, flags);
+ if (io->state == EFCT_HW_IO_STATE_INUSE ||
+ io->state == EFCT_HW_IO_STATE_WAIT_FREE) {
+ /* if on wait_free list, caller has already freed IO;
+ * remove from wait_free list and add to free list.
+ * if on in-use list, already marked as no longer busy;
+ * just leave there and wait for caller to free.
+ */
+ if (io->state == EFCT_HW_IO_STATE_WAIT_FREE) {
+ io->state = EFCT_HW_IO_STATE_FREE;
+ list_del_init(&io->list_entry);
+ efct_hw_io_free_move_correct_list(hw, io);
+ }
+ }
+ spin_unlock_irqrestore(&hw->io_lock, flags);
+}
+
+static int
+efct_hw_flush(struct efct_hw *hw)
+{
+ u32 i = 0;
+
+ /* Process any remaining completions */
+ for (i = 0; i < hw->eq_count; i++)
+ efct_hw_process(hw, i, ~0);
+
+ return 0;
+}
+
+int
+efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe)
+{
+ int rc = 0;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&wq->queue->lock, flags);
+ if (list_empty(&wq->pending_list)) {
+ if (wq->free_count > 0) {
+ rc = _efct_hw_wq_write(wq, wqe);
+ } else {
+ INIT_LIST_HEAD(&wqe->list_entry);
+ list_add_tail(&wqe->list_entry, &wq->pending_list);
+ wq->wq_pending_count++;
+ }
+
+ spin_unlock_irqrestore(&wq->queue->lock, flags);
+ return rc;
+ }
+
+ INIT_LIST_HEAD(&wqe->list_entry);
+ list_add_tail(&wqe->list_entry, &wq->pending_list);
+ wq->wq_pending_count++;
+ while (wq->free_count > 0) {
+ wqe = list_first_entry(&wq->pending_list, struct efct_hw_wqe,
+ list_entry);
+ if (!wqe)
+ break;
+
+ list_del_init(&wqe->list_entry);
+ rc = _efct_hw_wq_write(wq, wqe);
+ if (rc)
+ break;
+
+ if (wqe->abort_wqe_submit_needed) {
+ wqe->abort_wqe_submit_needed = false;
+ efct_hw_fill_abort_wqe(wq->hw, wqe);
+
+ INIT_LIST_HEAD(&wqe->list_entry);
+ list_add_tail(&wqe->list_entry, &wq->pending_list);
+ wq->wq_pending_count++;
+ }
+ }
+
+ spin_unlock_irqrestore(&wq->queue->lock, flags);
+
+ return rc;
+}
+
+int
+efct_efc_bls_send(struct efc *efc, u32 type, struct sli_bls_params *bls)
+{
+ struct efct *efct = efc->base;
+
+ return efct_hw_bls_send(efct, type, bls, NULL, NULL);
+}
+
+int
+efct_hw_bls_send(struct efct *efct, u32 type, struct sli_bls_params *bls_params,
+ void *cb, void *arg)
+{
+ struct efct_hw *hw = &efct->hw;
+ struct efct_hw_io *hio;
+ struct sli_bls_payload bls;
+ int rc;
+
+ if (hw->state != EFCT_HW_STATE_ACTIVE) {
+ efc_log_err(hw->os,
+ "cannot send BLS, HW state=%d\n", hw->state);
+ return -EIO;
+ }
+
+ hio = efct_hw_io_alloc(hw);
+ if (!hio) {
+ efc_log_err(hw->os, "HIO allocation failed\n");
+ return -EIO;
+ }
+
+ hio->done = cb;
+ hio->arg = arg;
+
+ bls_params->xri = hio->indicator;
+ bls_params->tag = hio->reqtag;
+
+ if (type == FC_RCTL_BA_ACC) {
+ hio->type = EFCT_HW_BLS_ACC;
+ bls.type = SLI4_SLI_BLS_ACC;
+ memcpy(&bls.u.acc, bls_params->payload, sizeof(bls.u.acc));
+ } else {
+ hio->type = EFCT_HW_BLS_RJT;
+ bls.type = SLI4_SLI_BLS_RJT;
+ memcpy(&bls.u.rjt, bls_params->payload, sizeof(bls.u.rjt));
+ }
+
+ bls.ox_id = cpu_to_le16(bls_params->ox_id);
+ bls.rx_id = cpu_to_le16(bls_params->rx_id);
+
+ if (sli_xmit_bls_rsp64_wqe(&hw->sli, hio->wqe.wqebuf,
+ &bls, bls_params)) {
+ efc_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n");
+ return -EIO;
+ }
+
+ hio->xbusy = true;
+
+ /*
+ * Add IO to active io wqe list before submitting, in case the
+ * wcqe processing preempts this thread.
+ */
+ hio->wq->use_count++;
+ rc = efct_hw_wq_write(hio->wq, &hio->wqe);
+ if (rc >= 0) {
+ /* non-negative return is success */
+ rc = 0;
+ } else {
+ /* failed to write wqe, remove from active wqe list */
+ efc_log_err(hw->os,
+ "sli_queue_write failed: %d\n", rc);
+ hio->xbusy = false;
+ }
+
+ return rc;
+}
+
+static int
+efct_els_ssrs_send_cb(struct efct_hw_io *hio, u32 length, int status,
+ u32 ext_status, void *arg)
+{
+ struct efc_disc_io *io = arg;
+
+ efc_disc_io_complete(io, length, status, ext_status);
+ return 0;
+}
+
+static inline void
+efct_fill_els_params(struct efc_disc_io *io, struct sli_els_params *params)
+{
+ u8 *cmd = io->req.virt;
+
+ params->cmd = *cmd;
+ params->s_id = io->s_id;
+ params->d_id = io->d_id;
+ params->ox_id = io->iparam.els.ox_id;
+ params->rpi = io->rpi;
+ params->vpi = io->vpi;
+ params->rpi_registered = io->rpi_registered;
+ params->xmit_len = io->xmit_len;
+ params->rsp_len = io->rsp_len;
+ params->timeout = io->iparam.els.timeout;
+}
+
+static inline void
+efct_fill_ct_params(struct efc_disc_io *io, struct sli_ct_params *params)
+{
+ params->r_ctl = io->iparam.ct.r_ctl;
+ params->type = io->iparam.ct.type;
+ params->df_ctl = io->iparam.ct.df_ctl;
+ params->d_id = io->d_id;
+ params->ox_id = io->iparam.ct.ox_id;
+ params->rpi = io->rpi;
+ params->vpi = io->vpi;
+ params->rpi_registered = io->rpi_registered;
+ params->xmit_len = io->xmit_len;
+ params->rsp_len = io->rsp_len;
+ params->timeout = io->iparam.ct.timeout;
+}
+
+/**
+ * efct_els_hw_srrs_send() - Send a single request and response cmd.
+ * @efc: efc library structure
+ * @io: Discovery IO used to hold els and ct cmd context.
+ *
+ * This routine supports communication sequences consisting of a single
+ * request and single response between two endpoints. Examples include:
+ * - Sending an ELS request.
+ * - Sending an ELS response - To send an ELS response, the caller must provide
+ * the OX_ID from the received request.
+ * - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request,
+ * the caller must provide the R_CTL, TYPE, and DF_CTL
+ * values to place in the FC frame header.
+ *
+ * Return: Status of the request.
+ */
+int
+efct_els_hw_srrs_send(struct efc *efc, struct efc_disc_io *io)
+{
+ struct efct *efct = efc->base;
+ struct efct_hw_io *hio;
+ struct efct_hw *hw = &efct->hw;
+ struct efc_dma *send = &io->req;
+ struct efc_dma *receive = &io->rsp;
+ struct sli4_sge *sge = NULL;
+ int rc = 0;
+ u32 len = io->xmit_len;
+ u32 sge0_flags;
+ u32 sge1_flags;
+
+ hio = efct_hw_io_alloc(hw);
+ if (!hio) {
+ pr_err("HIO alloc failed\n");
+ return -EIO;
+ }
+
+ if (hw->state != EFCT_HW_STATE_ACTIVE) {
+ efc_log_debug(hw->os,
+ "cannot send SRRS, HW state=%d\n", hw->state);
+ return -EIO;
+ }
+
+ hio->done = efct_els_ssrs_send_cb;
+ hio->arg = io;
+
+ sge = hio->sgl->virt;
+
+ /* clear both SGE */
+ memset(hio->sgl->virt, 0, 2 * sizeof(struct sli4_sge));
+
+ sge0_flags = le32_to_cpu(sge[0].dw2_flags);
+ sge1_flags = le32_to_cpu(sge[1].dw2_flags);
+ if (send->size) {
+ sge[0].buffer_address_high =
+ cpu_to_le32(upper_32_bits(send->phys));
+ sge[0].buffer_address_low =
+ cpu_to_le32(lower_32_bits(send->phys));
+
+ sge0_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT);
+
+ sge[0].buffer_length = cpu_to_le32(len);
+ }
+
+ if (io->io_type == EFC_DISC_IO_ELS_REQ ||
+ io->io_type == EFC_DISC_IO_CT_REQ) {
+ sge[1].buffer_address_high =
+ cpu_to_le32(upper_32_bits(receive->phys));
+ sge[1].buffer_address_low =
+ cpu_to_le32(lower_32_bits(receive->phys));
+
+ sge1_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT);
+ sge1_flags |= SLI4_SGE_LAST;
+
+ sge[1].buffer_length = cpu_to_le32(receive->size);
+ } else {
+ sge0_flags |= SLI4_SGE_LAST;
+ }
+
+ sge[0].dw2_flags = cpu_to_le32(sge0_flags);
+ sge[1].dw2_flags = cpu_to_le32(sge1_flags);
+
+ switch (io->io_type) {
+ case EFC_DISC_IO_ELS_REQ: {
+ struct sli_els_params els_params;
+
+ hio->type = EFCT_HW_ELS_REQ;
+ efct_fill_els_params(io, &els_params);
+ els_params.xri = hio->indicator;
+ els_params.tag = hio->reqtag;
+
+ if (sli_els_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl,
+ &els_params)) {
+ efc_log_err(hw->os, "REQ WQE error\n");
+ rc = -EIO;
+ }
+ break;
+ }
+ case EFC_DISC_IO_ELS_RESP: {
+ struct sli_els_params els_params;
+
+ hio->type = EFCT_HW_ELS_RSP;
+ efct_fill_els_params(io, &els_params);
+ els_params.xri = hio->indicator;
+ els_params.tag = hio->reqtag;
+ if (sli_xmit_els_rsp64_wqe(&hw->sli, hio->wqe.wqebuf, send,
+ &els_params)){
+ efc_log_err(hw->os, "RSP WQE error\n");
+ rc = -EIO;
+ }
+ break;
+ }
+ case EFC_DISC_IO_CT_REQ: {
+ struct sli_ct_params ct_params;
+
+ hio->type = EFCT_HW_FC_CT;
+ efct_fill_ct_params(io, &ct_params);
+ ct_params.xri = hio->indicator;
+ ct_params.tag = hio->reqtag;
+ if (sli_gen_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl,
+ &ct_params)){
+ efc_log_err(hw->os, "GEN WQE error\n");
+ rc = -EIO;
+ }
+ break;
+ }
+ case EFC_DISC_IO_CT_RESP: {
+ struct sli_ct_params ct_params;
+
+ hio->type = EFCT_HW_FC_CT_RSP;
+ efct_fill_ct_params(io, &ct_params);
+ ct_params.xri = hio->indicator;
+ ct_params.tag = hio->reqtag;
+ if (sli_xmit_sequence64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl,
+ &ct_params)){
+ efc_log_err(hw->os, "XMIT SEQ WQE error\n");
+ rc = -EIO;
+ }
+ break;
+ }
+ default:
+ efc_log_err(hw->os, "bad SRRS type %#x\n", io->io_type);
+ rc = -EIO;
+ }
+
+ if (rc == 0) {
+ hio->xbusy = true;
+
+ /*
+ * Add IO to active io wqe list before submitting, in case the
+ * wcqe processing preempts this thread.
+ */
+ hio->wq->use_count++;
+ rc = efct_hw_wq_write(hio->wq, &hio->wqe);
+ if (rc >= 0) {
+ /* non-negative return is success */
+ rc = 0;
+ } else {
+ /* failed to write wqe, remove from active wqe list */
+ efc_log_err(hw->os,
+ "sli_queue_write failed: %d\n", rc);
+ hio->xbusy = false;
+ }
+ }
+
+ return rc;
+}
+
+int
+efct_hw_io_send(struct efct_hw *hw, enum efct_hw_io_type type,
+ struct efct_hw_io *io, union efct_hw_io_param_u *iparam,
+ void *cb, void *arg)
+{
+ int rc = 0;
+ bool send_wqe = true;
+
+ if (!io) {
+ pr_err("bad parm hw=%p io=%p\n", hw, io);
+ return -EIO;
+ }
+
+ if (hw->state != EFCT_HW_STATE_ACTIVE) {
+ efc_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state);
+ return -EIO;
+ }
+
+ /*
+ * Save state needed during later stages
+ */
+ io->type = type;
+ io->done = cb;
+ io->arg = arg;
+
+ /*
+ * Format the work queue entry used to send the IO
+ */
+ switch (type) {
+ case EFCT_HW_IO_TARGET_WRITE: {
+ u16 *flags = &iparam->fcp_tgt.flags;
+ struct fcp_txrdy *xfer = io->xfer_rdy.virt;
+
+ /*
+ * Fill in the XFER_RDY for IF_TYPE 0 devices
+ */
+ xfer->ft_data_ro = cpu_to_be32(iparam->fcp_tgt.offset);
+ xfer->ft_burst_len = cpu_to_be32(iparam->fcp_tgt.xmit_len);
+
+ if (io->xbusy)
+ *flags |= SLI4_IO_CONTINUATION;
+ else
+ *flags &= ~SLI4_IO_CONTINUATION;
+ iparam->fcp_tgt.xri = io->indicator;
+ iparam->fcp_tgt.tag = io->reqtag;
+
+ if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf,
+ &io->def_sgl, io->first_data_sge,
+ SLI4_CQ_DEFAULT,
+ 0, 0, &iparam->fcp_tgt)) {
+ efc_log_err(hw->os, "TRECEIVE WQE error\n");
+ rc = -EIO;
+ }
+ break;
+ }
+ case EFCT_HW_IO_TARGET_READ: {
+ u16 *flags = &iparam->fcp_tgt.flags;
+
+ if (io->xbusy)
+ *flags |= SLI4_IO_CONTINUATION;
+ else
+ *flags &= ~SLI4_IO_CONTINUATION;
+
+ iparam->fcp_tgt.xri = io->indicator;
+ iparam->fcp_tgt.tag = io->reqtag;
+
+ if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf,
+ &io->def_sgl, io->first_data_sge,
+ SLI4_CQ_DEFAULT,
+ 0, 0, &iparam->fcp_tgt)) {
+ efc_log_err(hw->os, "TSEND WQE error\n");
+ rc = -EIO;
+ }
+ break;
+ }
+ case EFCT_HW_IO_TARGET_RSP: {
+ u16 *flags = &iparam->fcp_tgt.flags;
+
+ if (io->xbusy)
+ *flags |= SLI4_IO_CONTINUATION;
+ else
+ *flags &= ~SLI4_IO_CONTINUATION;
+
+ iparam->fcp_tgt.xri = io->indicator;
+ iparam->fcp_tgt.tag = io->reqtag;
+
+ if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf,
+ &io->def_sgl, SLI4_CQ_DEFAULT,
+ 0, &iparam->fcp_tgt)) {
+ efc_log_err(hw->os, "TRSP WQE error\n");
+ rc = -EIO;
+ }
+
+ break;
+ }
+ default:
+ efc_log_err(hw->os, "unsupported IO type %#x\n", type);
+ rc = -EIO;
+ }
+
+ if (send_wqe && rc == 0) {
+ io->xbusy = true;
+
+ /*
+ * Add IO to active io wqe list before submitting, in case the
+ * wcqe processing preempts this thread.
+ */
+ hw->tcmd_wq_submit[io->wq->instance]++;
+ io->wq->use_count++;
+ rc = efct_hw_wq_write(io->wq, &io->wqe);
+ if (rc >= 0) {
+ /* non-negative return is success */
+ rc = 0;
+ } else {
+ /* failed to write wqe, remove from active wqe list */
+ efc_log_err(hw->os,
+ "sli_queue_write failed: %d\n", rc);
+ io->xbusy = false;
+ }
+ }
+
+ return rc;
+}
+
+int
+efct_hw_send_frame(struct efct_hw *hw, struct fc_frame_header *hdr,
+ u8 sof, u8 eof, struct efc_dma *payload,
+ struct efct_hw_send_frame_context *ctx,
+ void (*callback)(void *arg, u8 *cqe, int status),
+ void *arg)
+{
+ int rc;
+ struct efct_hw_wqe *wqe;
+ u32 xri;
+ struct hw_wq *wq;
+
+ wqe = &ctx->wqe;
+
+ /* populate the callback object */
+ ctx->hw = hw;
+
+ /* Fetch and populate request tag */
+ ctx->wqcb = efct_hw_reqtag_alloc(hw, callback, arg);
+ if (!ctx->wqcb) {
+ efc_log_err(hw->os, "can't allocate request tag\n");
+ return -ENOSPC;
+ }
+
+ wq = hw->hw_wq[0];
+
+ /* Set XRI and RX_ID in the header based on which WQ, and which
+ * send_frame_io we are using
+ */
+ xri = wq->send_frame_io->indicator;
+
+ /* Build the send frame WQE */
+ rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf,
+ sof, eof, (u32 *)hdr, payload, payload->len,
+ EFCT_HW_SEND_FRAME_TIMEOUT, xri,
+ ctx->wqcb->instance_index);
+ if (rc) {
+ efc_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", rc);
+ return -EIO;
+ }
+
+ /* Write to WQ */
+ rc = efct_hw_wq_write(wq, wqe);
+ if (rc) {
+ efc_log_err(hw->os, "efct_hw_wq_write failed: %d\n", rc);
+ return -EIO;
+ }
+
+ wq->use_count++;
+
+ return 0;
+}
+
+static int
+efct_hw_cb_link_stat(struct efct_hw *hw, int status,
+ u8 *mqe, void *arg)
+{
+ struct sli4_cmd_read_link_stats *mbox_rsp;
+ struct efct_hw_link_stat_cb_arg *cb_arg = arg;
+ struct efct_hw_link_stat_counts counts[EFCT_HW_LINK_STAT_MAX];
+ u32 num_counters, i;
+ u32 mbox_rsp_flags = 0;
+
+ mbox_rsp = (struct sli4_cmd_read_link_stats *)mqe;
+ mbox_rsp_flags = le32_to_cpu(mbox_rsp->dw1_flags);
+ num_counters = (mbox_rsp_flags & SLI4_READ_LNKSTAT_GEC) ? 20 : 13;
+ memset(counts, 0, sizeof(struct efct_hw_link_stat_counts) *
+ EFCT_HW_LINK_STAT_MAX);
+
+ /* Fill overflow counts, mask starts from SLI4_READ_LNKSTAT_W02OF*/
+ for (i = 0; i < EFCT_HW_LINK_STAT_MAX; i++)
+ counts[i].overflow = (mbox_rsp_flags & (1 << (i + 2)));
+
+ counts[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter =
+ le32_to_cpu(mbox_rsp->linkfail_errcnt);
+ counts[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter =
+ le32_to_cpu(mbox_rsp->losssync_errcnt);
+ counts[EFCT_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT].counter =
+ le32_to_cpu(mbox_rsp->losssignal_errcnt);
+ counts[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter =
+ le32_to_cpu(mbox_rsp->primseq_errcnt);
+ counts[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter =
+ le32_to_cpu(mbox_rsp->inval_txword_errcnt);
+ counts[EFCT_HW_LINK_STAT_CRC_COUNT].counter =
+ le32_to_cpu(mbox_rsp->crc_errcnt);
+ counts[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT].counter =
+ le32_to_cpu(mbox_rsp->primseq_eventtimeout_cnt);
+ counts[EFCT_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT].counter =
+ le32_to_cpu(mbox_rsp->elastic_bufoverrun_errcnt);
+ counts[EFCT_HW_LINK_STAT_ARB_TIMEOUT_COUNT].counter =
+ le32_to_cpu(mbox_rsp->arbit_fc_al_timeout_cnt);
+ counts[EFCT_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT].counter =
+ le32_to_cpu(mbox_rsp->adv_rx_buftor_to_buf_credit);
+ counts[EFCT_HW_LINK_STAT_CURR_RCV_B2B_CREDIT].counter =
+ le32_to_cpu(mbox_rsp->curr_rx_buf_to_buf_credit);
+ counts[EFCT_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT].counter =
+ le32_to_cpu(mbox_rsp->adv_tx_buf_to_buf_credit);
+ counts[EFCT_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT].counter =
+ le32_to_cpu(mbox_rsp->curr_tx_buf_to_buf_credit);
+ counts[EFCT_HW_LINK_STAT_RCV_EOFA_COUNT].counter =
+ le32_to_cpu(mbox_rsp->rx_eofa_cnt);
+ counts[EFCT_HW_LINK_STAT_RCV_EOFDTI_COUNT].counter =
+ le32_to_cpu(mbox_rsp->rx_eofdti_cnt);
+ counts[EFCT_HW_LINK_STAT_RCV_EOFNI_COUNT].counter =
+ le32_to_cpu(mbox_rsp->rx_eofni_cnt);
+ counts[EFCT_HW_LINK_STAT_RCV_SOFF_COUNT].counter =
+ le32_to_cpu(mbox_rsp->rx_soff_cnt);
+ counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT].counter =
+ le32_to_cpu(mbox_rsp->rx_dropped_no_aer_cnt);
+ counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT].counter =
+ le32_to_cpu(mbox_rsp->rx_dropped_no_avail_rpi_rescnt);
+ counts[EFCT_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT].counter =
+ le32_to_cpu(mbox_rsp->rx_dropped_no_avail_xri_rescnt);
+
+ if (cb_arg) {
+ if (cb_arg->cb) {
+ if (status == 0 && le16_to_cpu(mbox_rsp->hdr.status))
+ status = le16_to_cpu(mbox_rsp->hdr.status);
+ cb_arg->cb(status, num_counters, counts, cb_arg->arg);
+ }
+
+ kfree(cb_arg);
+ }
+
+ return 0;
+}
+
+int
+efct_hw_get_link_stats(struct efct_hw *hw, u8 req_ext_counters,
+ u8 clear_overflow_flags, u8 clear_all_counters,
+ void (*cb)(int status, u32 num_counters,
+ struct efct_hw_link_stat_counts *counters,
+ void *arg),
+ void *arg)
+{
+ int rc = -EIO;
+ struct efct_hw_link_stat_cb_arg *cb_arg;
+ u8 mbxdata[SLI4_BMBX_SIZE];
+
+ cb_arg = kzalloc(sizeof(*cb_arg), GFP_ATOMIC);
+ if (!cb_arg)
+ return -ENOMEM;
+
+ cb_arg->cb = cb;
+ cb_arg->arg = arg;
+
+ /* Send the HW command */
+ if (!sli_cmd_read_link_stats(&hw->sli, mbxdata, req_ext_counters,
+ clear_overflow_flags, clear_all_counters))
+ rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT,
+ efct_hw_cb_link_stat, cb_arg);
+
+ if (rc)
+ kfree(cb_arg);
+
+ return rc;
+}
+
+static int
+efct_hw_cb_host_stat(struct efct_hw *hw, int status, u8 *mqe, void *arg)
+{
+ struct sli4_cmd_read_status *mbox_rsp =
+ (struct sli4_cmd_read_status *)mqe;
+ struct efct_hw_host_stat_cb_arg *cb_arg = arg;
+ struct efct_hw_host_stat_counts counts[EFCT_HW_HOST_STAT_MAX];
+ u32 num_counters = EFCT_HW_HOST_STAT_MAX;
+
+ memset(counts, 0, sizeof(struct efct_hw_host_stat_counts) *
+ EFCT_HW_HOST_STAT_MAX);
+
+ counts[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter =
+ le32_to_cpu(mbox_rsp->trans_kbyte_cnt);
+ counts[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter =
+ le32_to_cpu(mbox_rsp->recv_kbyte_cnt);
+ counts[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter =
+ le32_to_cpu(mbox_rsp->trans_frame_cnt);
+ counts[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter =
+ le32_to_cpu(mbox_rsp->recv_frame_cnt);
+ counts[EFCT_HW_HOST_STAT_TX_SEQ_COUNT].counter =
+ le32_to_cpu(mbox_rsp->trans_seq_cnt);
+ counts[EFCT_HW_HOST_STAT_RX_SEQ_COUNT].counter =
+ le32_to_cpu(mbox_rsp->recv_seq_cnt);
+ counts[EFCT_HW_HOST_STAT_TOTAL_EXCH_ORIG].counter =
+ le32_to_cpu(mbox_rsp->tot_exchanges_orig);
+ counts[EFCT_HW_HOST_STAT_TOTAL_EXCH_RESP].counter =
+ le32_to_cpu(mbox_rsp->tot_exchanges_resp);
+ counts[EFCT_HW_HOSY_STAT_RX_P_BSY_COUNT].counter =
+ le32_to_cpu(mbox_rsp->recv_p_bsy_cnt);
+ counts[EFCT_HW_HOST_STAT_RX_F_BSY_COUNT].counter =
+ le32_to_cpu(mbox_rsp->recv_f_bsy_cnt);
+ counts[EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT].counter =
+ le32_to_cpu(mbox_rsp->no_rq_buf_dropped_frames_cnt);
+ counts[EFCT_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT].counter =
+ le32_to_cpu(mbox_rsp->empty_rq_timeout_cnt);
+ counts[EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT].counter =
+ le32_to_cpu(mbox_rsp->no_xri_dropped_frames_cnt);
+ counts[EFCT_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT].counter =
+ le32_to_cpu(mbox_rsp->empty_xri_pool_cnt);
+
+ if (cb_arg) {
+ if (cb_arg->cb) {
+ if (status == 0 && le16_to_cpu(mbox_rsp->hdr.status))
+ status = le16_to_cpu(mbox_rsp->hdr.status);
+ cb_arg->cb(status, num_counters, counts, cb_arg->arg);
+ }
+
+ kfree(cb_arg);
+ }
+
+ return 0;
+}
+
+int
+efct_hw_get_host_stats(struct efct_hw *hw, u8 cc,
+ void (*cb)(int status, u32 num_counters,
+ struct efct_hw_host_stat_counts *counters,
+ void *arg),
+ void *arg)
+{
+ int rc = -EIO;
+ struct efct_hw_host_stat_cb_arg *cb_arg;
+ u8 mbxdata[SLI4_BMBX_SIZE];
+
+ cb_arg = kmalloc(sizeof(*cb_arg), GFP_ATOMIC);
+ if (!cb_arg)
+ return -ENOMEM;
+
+ cb_arg->cb = cb;
+ cb_arg->arg = arg;
+
+ /* Send the HW command to get the host stats */
+ if (!sli_cmd_read_status(&hw->sli, mbxdata, cc))
+ rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT,
+ efct_hw_cb_host_stat, cb_arg);
+
+ if (rc) {
+ efc_log_debug(hw->os, "READ_HOST_STATS failed\n");
+ kfree(cb_arg);
+ }
+
+ return rc;
+}
+
+struct efct_hw_async_call_ctx {
+ efct_hw_async_cb_t callback;
+ void *arg;
+ u8 cmd[SLI4_BMBX_SIZE];
+};
+
+static void
+efct_hw_async_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg)
+{
+ struct efct_hw_async_call_ctx *ctx = arg;
+
+ if (ctx) {
+ if (ctx->callback)
+ (*ctx->callback)(hw, status, mqe, ctx->arg);
+
+ kfree(ctx);
+ }
+}
+
+int
+efct_hw_async_call(struct efct_hw *hw, efct_hw_async_cb_t callback, void *arg)
+{
+ struct efct_hw_async_call_ctx *ctx;
+ int rc;
+
+ /*
+ * Allocate a callback context (which includes the mbox cmd buffer),
+ * we need this to be persistent as the mbox cmd submission may be
+ * queued and executed later execution.
+ */
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->callback = callback;
+ ctx->arg = arg;
+
+ /* Build and send a NOP mailbox command */
+ if (sli_cmd_common_nop(&hw->sli, ctx->cmd, 0)) {
+ efc_log_err(hw->os, "COMMON_NOP format failure\n");
+ kfree(ctx);
+ return -EIO;
+ }
+
+ rc = efct_hw_command(hw, ctx->cmd, EFCT_CMD_NOWAIT, efct_hw_async_cb,
+ ctx);
+ if (rc) {
+ efc_log_err(hw->os, "COMMON_NOP command failure, rc=%d\n", rc);
+ kfree(ctx);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int
+efct_hw_cb_fw_write(struct efct_hw *hw, int status, u8 *mqe, void *arg)
+{
+ struct sli4_cmd_sli_config *mbox_rsp =
+ (struct sli4_cmd_sli_config *)mqe;
+ struct sli4_rsp_cmn_write_object *wr_obj_rsp;
+ struct efct_hw_fw_wr_cb_arg *cb_arg = arg;
+ u32 bytes_written;
+ u16 mbox_status;
+ u32 change_status;
+
+ wr_obj_rsp = (struct sli4_rsp_cmn_write_object *)
+ &mbox_rsp->payload.embed;
+ bytes_written = le32_to_cpu(wr_obj_rsp->actual_write_length);
+ mbox_status = le16_to_cpu(mbox_rsp->hdr.status);
+ change_status = (le32_to_cpu(wr_obj_rsp->change_status_dword) &
+ RSP_CHANGE_STATUS);
+
+ if (cb_arg) {
+ if (cb_arg->cb) {
+ if (!status && mbox_status)
+ status = mbox_status;
+ cb_arg->cb(status, bytes_written, change_status,
+ cb_arg->arg);
+ }
+
+ kfree(cb_arg);
+ }
+
+ return 0;
+}
+
+int
+efct_hw_firmware_write(struct efct_hw *hw, struct efc_dma *dma, u32 size,
+ u32 offset, int last,
+ void (*cb)(int status, u32 bytes_written,
+ u32 change_status, void *arg),
+ void *arg)
+{
+ int rc = -EIO;
+ u8 mbxdata[SLI4_BMBX_SIZE];
+ struct efct_hw_fw_wr_cb_arg *cb_arg;
+ int noc = 0;
+
+ cb_arg = kzalloc(sizeof(*cb_arg), GFP_KERNEL);
+ if (!cb_arg)
+ return -ENOMEM;
+
+ cb_arg->cb = cb;
+ cb_arg->arg = arg;
+
+ /* Write a portion of a firmware image to the device */
+ if (!sli_cmd_common_write_object(&hw->sli, mbxdata,
+ noc, last, size, offset, "/prg/",
+ dma))
+ rc = efct_hw_command(hw, mbxdata, EFCT_CMD_NOWAIT,
+ efct_hw_cb_fw_write, cb_arg);
+
+ if (rc != 0) {
+ efc_log_debug(hw->os, "COMMON_WRITE_OBJECT failed\n");
+ kfree(cb_arg);
+ }
+
+ return rc;
+}
+
+static int
+efct_hw_cb_port_control(struct efct_hw *hw, int status, u8 *mqe,
+ void *arg)
+{
+ return 0;
+}
+
+int
+efct_hw_port_control(struct efct_hw *hw, enum efct_hw_port ctrl,
+ uintptr_t value,
+ void (*cb)(int status, uintptr_t value, void *arg),
+ void *arg)
+{
+ int rc = -EIO;
+ u8 link[SLI4_BMBX_SIZE];
+ u32 speed = 0;
+ u8 reset_alpa = 0;
+
+ switch (ctrl) {
+ case EFCT_HW_PORT_INIT:
+ if (!sli_cmd_config_link(&hw->sli, link))
+ rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
+ efct_hw_cb_port_control, NULL);
+
+ if (rc != 0) {
+ efc_log_err(hw->os, "CONFIG_LINK failed\n");
+ break;
+ }
+ speed = hw->config.speed;
+ reset_alpa = (u8)(value & 0xff);
+
+ rc = -EIO;
+ if (!sli_cmd_init_link(&hw->sli, link, speed, reset_alpa))
+ rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
+ efct_hw_cb_port_control, NULL);
+ /* Free buffer on error, since no callback is coming */
+ if (rc)
+ efc_log_err(hw->os, "INIT_LINK failed\n");
+ break;
+
+ case EFCT_HW_PORT_SHUTDOWN:
+ if (!sli_cmd_down_link(&hw->sli, link))
+ rc = efct_hw_command(hw, link, EFCT_CMD_NOWAIT,
+ efct_hw_cb_port_control, NULL);
+ /* Free buffer on error, since no callback is coming */
+ if (rc)
+ efc_log_err(hw->os, "DOWN_LINK failed\n");
+ break;
+
+ default:
+ efc_log_debug(hw->os, "unhandled control %#x\n", ctrl);
+ break;
+ }
+
+ return rc;
+}
+
+void
+efct_hw_teardown(struct efct_hw *hw)
+{
+ u32 i = 0;
+ u32 destroy_queues;
+ u32 free_memory;
+ struct efc_dma *dma;
+ struct efct *efct = hw->os;
+
+ destroy_queues = (hw->state == EFCT_HW_STATE_ACTIVE);
+ free_memory = (hw->state != EFCT_HW_STATE_UNINITIALIZED);
+
+ /* Cancel Sliport Healthcheck */
+ if (hw->sliport_healthcheck) {
+ hw->sliport_healthcheck = 0;
+ efct_hw_config_sli_port_health_check(hw, 0, 0);
+ }
+
+ if (hw->state != EFCT_HW_STATE_QUEUES_ALLOCATED) {
+ hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS;
+
+ efct_hw_flush(hw);
+
+ if (list_empty(&hw->cmd_head))
+ efc_log_debug(hw->os,
+ "All commands completed on MQ queue\n");
+ else
+ efc_log_debug(hw->os,
+ "Some cmds still pending on MQ queue\n");
+
+ /* Cancel any remaining commands */
+ efct_hw_command_cancel(hw);
+ } else {
+ hw->state = EFCT_HW_STATE_TEARDOWN_IN_PROGRESS;
+ }
+
+ dma_free_coherent(&efct->pci->dev,
+ hw->rnode_mem.size, hw->rnode_mem.virt,
+ hw->rnode_mem.phys);
+ memset(&hw->rnode_mem, 0, sizeof(struct efc_dma));
+
+ if (hw->io) {
+ for (i = 0; i < hw->config.n_io; i++) {
+ if (hw->io[i] && hw->io[i]->sgl &&
+ hw->io[i]->sgl->virt) {
+ dma_free_coherent(&efct->pci->dev,
+ hw->io[i]->sgl->size,
+ hw->io[i]->sgl->virt,
+ hw->io[i]->sgl->phys);
+ }
+ kfree(hw->io[i]);
+ hw->io[i] = NULL;
+ }
+ kfree(hw->io);
+ hw->io = NULL;
+ kfree(hw->wqe_buffs);
+ hw->wqe_buffs = NULL;
+ }
+
+ dma = &hw->xfer_rdy;
+ dma_free_coherent(&efct->pci->dev,
+ dma->size, dma->virt, dma->phys);
+ memset(dma, 0, sizeof(struct efc_dma));
+
+ dma = &hw->loop_map;
+ dma_free_coherent(&efct->pci->dev,
+ dma->size, dma->virt, dma->phys);
+ memset(dma, 0, sizeof(struct efc_dma));
+
+ for (i = 0; i < hw->wq_count; i++)
+ sli_queue_free(&hw->sli, &hw->wq[i], destroy_queues,
+ free_memory);
+
+ for (i = 0; i < hw->rq_count; i++)
+ sli_queue_free(&hw->sli, &hw->rq[i], destroy_queues,
+ free_memory);
+
+ for (i = 0; i < hw->mq_count; i++)
+ sli_queue_free(&hw->sli, &hw->mq[i], destroy_queues,
+ free_memory);
+
+ for (i = 0; i < hw->cq_count; i++)
+ sli_queue_free(&hw->sli, &hw->cq[i], destroy_queues,
+ free_memory);
+
+ for (i = 0; i < hw->eq_count; i++)
+ sli_queue_free(&hw->sli, &hw->eq[i], destroy_queues,
+ free_memory);
+
+ /* Free rq buffers */
+ efct_hw_rx_free(hw);
+
+ efct_hw_queue_teardown(hw);
+
+ kfree(hw->wq_cpu_array);
+
+ sli_teardown(&hw->sli);
+
+ /* record the fact that the queues are non-functional */
+ hw->state = EFCT_HW_STATE_UNINITIALIZED;
+
+ /* free sequence free pool */
+ kfree(hw->seq_pool);
+ hw->seq_pool = NULL;
+
+ /* free hw_wq_callback pool */
+ efct_hw_reqtag_pool_free(hw);
+
+ mempool_destroy(hw->cmd_ctx_pool);
+ mempool_destroy(hw->mbox_rqst_pool);
+
+ /* Mark HW setup as not having been called */
+ hw->hw_setup_called = false;
+}
+
+static int
+efct_hw_sli_reset(struct efct_hw *hw, enum efct_hw_reset reset,
+ enum efct_hw_state prev_state)
+{
+ int rc = 0;
+
+ switch (reset) {
+ case EFCT_HW_RESET_FUNCTION:
+ efc_log_debug(hw->os, "issuing function level reset\n");
+ if (sli_reset(&hw->sli)) {
+ efc_log_err(hw->os, "sli_reset failed\n");
+ rc = -EIO;
+ }
+ break;
+ case EFCT_HW_RESET_FIRMWARE:
+ efc_log_debug(hw->os, "issuing firmware reset\n");
+ if (sli_fw_reset(&hw->sli)) {
+ efc_log_err(hw->os, "sli_soft_reset failed\n");
+ rc = -EIO;
+ }
+ /*
+ * Because the FW reset leaves the FW in a non-running state,
+ * follow that with a regular reset.
+ */
+ efc_log_debug(hw->os, "issuing function level reset\n");
+ if (sli_reset(&hw->sli)) {
+ efc_log_err(hw->os, "sli_reset failed\n");
+ rc = -EIO;
+ }
+ break;
+ default:
+ efc_log_err(hw->os, "unknown type - no reset performed\n");
+ hw->state = prev_state;
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int
+efct_hw_reset(struct efct_hw *hw, enum efct_hw_reset reset)
+{
+ int rc = 0;
+ enum efct_hw_state prev_state = hw->state;
+
+ if (hw->state != EFCT_HW_STATE_ACTIVE)
+ efc_log_debug(hw->os,
+ "HW state %d is not active\n", hw->state);
+
+ hw->state = EFCT_HW_STATE_RESET_IN_PROGRESS;
+
+ /*
+ * If the prev_state is already reset/teardown in progress,
+ * don't continue further
+ */
+ if (prev_state == EFCT_HW_STATE_RESET_IN_PROGRESS ||
+ prev_state == EFCT_HW_STATE_TEARDOWN_IN_PROGRESS)
+ return efct_hw_sli_reset(hw, reset, prev_state);
+
+ if (prev_state != EFCT_HW_STATE_UNINITIALIZED) {
+ efct_hw_flush(hw);
+
+ if (list_empty(&hw->cmd_head))
+ efc_log_debug(hw->os,
+ "All commands completed on MQ queue\n");
+ else
+ efc_log_err(hw->os,
+ "Some commands still pending on MQ queue\n");
+ }
+
+ /* Reset the chip */
+ rc = efct_hw_sli_reset(hw, reset, prev_state);
+ if (rc == -EINVAL)
+ return -EIO;
+
+ return rc;
+}
diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h
new file mode 100644
index 000000000000..f3f4aa78dce9
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_hw.h
@@ -0,0 +1,764 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#ifndef _EFCT_HW_H
+#define _EFCT_HW_H
+
+#include "../libefc_sli/sli4.h"
+
+/*
+ * EFCT PCI IDs
+ */
+#define EFCT_VENDOR_ID 0x10df
+/* LightPulse 16Gb x 4 FC (lancer-g6) */
+#define EFCT_DEVICE_LANCER_G6 0xe307
+/* LightPulse 32Gb x 4 FC (lancer-g7) */
+#define EFCT_DEVICE_LANCER_G7 0xf407
+
+/*Default RQ entries len used by driver*/
+#define EFCT_HW_RQ_ENTRIES_MIN 512
+#define EFCT_HW_RQ_ENTRIES_DEF 1024
+#define EFCT_HW_RQ_ENTRIES_MAX 4096
+
+/*Defines the size of the RQ buffers used for each RQ*/
+#define EFCT_HW_RQ_SIZE_HDR 128
+#define EFCT_HW_RQ_SIZE_PAYLOAD 1024
+
+/*Define the maximum number of multi-receive queues*/
+#define EFCT_HW_MAX_MRQS 8
+
+/*
+ * Define count of when to set the WQEC bit in a submitted
+ * WQE, causing a consummed/released completion to be posted.
+ */
+#define EFCT_HW_WQEC_SET_COUNT 32
+
+/*Send frame timeout in seconds*/
+#define EFCT_HW_SEND_FRAME_TIMEOUT 10
+
+/*
+ * FDT Transfer Hint value, reads greater than this value
+ * will be segmented to implement fairness. A value of zero disables
+ * the feature.
+ */
+#define EFCT_HW_FDT_XFER_HINT 8192
+
+#define EFCT_HW_TIMECHECK_ITERATIONS 100
+#define EFCT_HW_MAX_NUM_MQ 1
+#define EFCT_HW_MAX_NUM_RQ 32
+#define EFCT_HW_MAX_NUM_EQ 16
+#define EFCT_HW_MAX_NUM_WQ 32
+#define EFCT_HW_DEF_NUM_EQ 1
+
+#define OCE_HW_MAX_NUM_MRQ_PAIRS 16
+
+#define EFCT_HW_MQ_DEPTH 128
+#define EFCT_HW_EQ_DEPTH 1024
+
+/*
+ * A CQ will be assinged to each WQ
+ * (CQ must have 2X entries of the WQ for abort
+ * processing), plus a separate one for each RQ PAIR and one for MQ
+ */
+#define EFCT_HW_MAX_NUM_CQ \
+ ((EFCT_HW_MAX_NUM_WQ * 2) + 1 + (OCE_HW_MAX_NUM_MRQ_PAIRS * 2))
+
+#define EFCT_HW_Q_HASH_SIZE 128
+#define EFCT_HW_RQ_HEADER_SIZE 128
+#define EFCT_HW_RQ_HEADER_INDEX 0
+
+#define EFCT_HW_REQUE_XRI_REGTAG 65534
+
+/* Options for efct_hw_command() */
+enum efct_cmd_opts {
+ /* command executes synchronously and busy-waits for completion */
+ EFCT_CMD_POLL,
+ /* command executes asynchronously. Uses callback */
+ EFCT_CMD_NOWAIT,
+};
+
+enum efct_hw_reset {
+ EFCT_HW_RESET_FUNCTION,
+ EFCT_HW_RESET_FIRMWARE,
+ EFCT_HW_RESET_MAX
+};
+
+enum efct_hw_topo {
+ EFCT_HW_TOPOLOGY_AUTO,
+ EFCT_HW_TOPOLOGY_NPORT,
+ EFCT_HW_TOPOLOGY_LOOP,
+ EFCT_HW_TOPOLOGY_NONE,
+ EFCT_HW_TOPOLOGY_MAX
+};
+
+/* pack fw revision values into a single uint64_t */
+#define HW_FWREV(a, b, c, d) (((uint64_t)(a) << 48) | ((uint64_t)(b) << 32) \
+ | ((uint64_t)(c) << 16) | ((uint64_t)(d)))
+
+#define EFCT_FW_VER_STR(a, b, c, d) (#a "." #b "." #c "." #d)
+
+enum efct_hw_io_type {
+ EFCT_HW_ELS_REQ,
+ EFCT_HW_ELS_RSP,
+ EFCT_HW_FC_CT,
+ EFCT_HW_FC_CT_RSP,
+ EFCT_HW_BLS_ACC,
+ EFCT_HW_BLS_RJT,
+ EFCT_HW_IO_TARGET_READ,
+ EFCT_HW_IO_TARGET_WRITE,
+ EFCT_HW_IO_TARGET_RSP,
+ EFCT_HW_IO_DNRX_REQUEUE,
+ EFCT_HW_IO_MAX,
+};
+
+enum efct_hw_io_state {
+ EFCT_HW_IO_STATE_FREE,
+ EFCT_HW_IO_STATE_INUSE,
+ EFCT_HW_IO_STATE_WAIT_FREE,
+ EFCT_HW_IO_STATE_WAIT_SEC_HIO,
+};
+
+#define EFCT_TARGET_WRITE_SKIPS 1
+#define EFCT_TARGET_READ_SKIPS 2
+
+struct efct_hw;
+struct efct_io;
+
+#define EFCT_CMD_CTX_POOL_SZ 32
+/**
+ * HW command context.
+ * Stores the state for the asynchronous commands sent to the hardware.
+ */
+struct efct_command_ctx {
+ struct list_head list_entry;
+ int (*cb)(struct efct_hw *hw, int status, u8 *mqe, void *arg);
+ void *arg; /* Argument for callback */
+ /* buffer holding command / results */
+ u8 buf[SLI4_BMBX_SIZE];
+ void *ctx; /* upper layer context */
+};
+
+struct efct_hw_sgl {
+ uintptr_t addr;
+ size_t len;
+};
+
+union efct_hw_io_param_u {
+ struct sli_bls_params bls;
+ struct sli_els_params els;
+ struct sli_ct_params fc_ct;
+ struct sli_fcp_tgt_params fcp_tgt;
+};
+
+/* WQ steering mode */
+enum efct_hw_wq_steering {
+ EFCT_HW_WQ_STEERING_CLASS,
+ EFCT_HW_WQ_STEERING_REQUEST,
+ EFCT_HW_WQ_STEERING_CPU,
+};
+
+/* HW wqe object */
+struct efct_hw_wqe {
+ struct list_head list_entry;
+ bool abort_wqe_submit_needed;
+ bool send_abts;
+ u32 id;
+ u32 abort_reqtag;
+ u8 *wqebuf;
+};
+
+struct efct_hw_io;
+/* Typedef for HW "done" callback */
+typedef int (*efct_hw_done_t)(struct efct_hw_io *, u32 len, int status,
+ u32 ext, void *ul_arg);
+
+/**
+ * HW IO object.
+ *
+ * Stores the per-IO information necessary
+ * for both SLI and efct.
+ * @ref: reference counter for hw io object
+ * @state: state of IO: free, busy, wait_free
+ * @list_entry used for busy, wait_free, free lists
+ * @wqe Work queue object, with link for pending
+ * @hw pointer back to hardware context
+ * @xfer_rdy transfer ready data
+ * @type IO type
+ * @xbusy Exchange is active in FW
+ * @abort_in_progress if TRUE, abort is in progress
+ * @status_saved if TRUE, latched status should be returned
+ * @wq_class WQ class if steering mode is Class
+ * @reqtag request tag for this HW IO
+ * @wq WQ assigned to the exchange
+ * @done Function called on IO completion
+ * @arg argument passed to IO done callback
+ * @abort_done Function called on abort completion
+ * @abort_arg argument passed to abort done callback
+ * @wq_steering WQ steering mode request
+ * @saved_status Saved status
+ * @saved_len Status length
+ * @saved_ext Saved extended status
+ * @eq EQ on which this HIO came up
+ * @sge_offset SGE data offset
+ * @def_sgl_count Count of SGEs in default SGL
+ * @abort_reqtag request tag for an abort of this HW IO
+ * @indicator Exchange indicator
+ * @def_sgl default SGL
+ * @sgl pointer to current active SGL
+ * @sgl_count count of SGEs in io->sgl
+ * @first_data_sge index of first data SGE
+ * @n_sge number of active SGEs
+ */
+struct efct_hw_io {
+ struct kref ref;
+ enum efct_hw_io_state state;
+ void (*release)(struct kref *arg);
+ struct list_head list_entry;
+ struct efct_hw_wqe wqe;
+
+ struct efct_hw *hw;
+ struct efc_dma xfer_rdy;
+ u16 type;
+ bool xbusy;
+ int abort_in_progress;
+ bool status_saved;
+ u8 wq_class;
+ u16 reqtag;
+
+ struct hw_wq *wq;
+ efct_hw_done_t done;
+ void *arg;
+ efct_hw_done_t abort_done;
+ void *abort_arg;
+
+ enum efct_hw_wq_steering wq_steering;
+
+ u32 saved_status;
+ u32 saved_len;
+ u32 saved_ext;
+
+ struct hw_eq *eq;
+ u32 sge_offset;
+ u32 def_sgl_count;
+ u32 abort_reqtag;
+ u32 indicator;
+ struct efc_dma def_sgl;
+ struct efc_dma *sgl;
+ u32 sgl_count;
+ u32 first_data_sge;
+ u32 n_sge;
+};
+
+enum efct_hw_port {
+ EFCT_HW_PORT_INIT,
+ EFCT_HW_PORT_SHUTDOWN,
+};
+
+/* Node group rpi reference */
+struct efct_hw_rpi_ref {
+ atomic_t rpi_count;
+ atomic_t rpi_attached;
+};
+
+enum efct_hw_link_stat {
+ EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT,
+ EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT,
+ EFCT_HW_LINK_STAT_LOSS_OF_SIGNAL_COUNT,
+ EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT,
+ EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT,
+ EFCT_HW_LINK_STAT_CRC_COUNT,
+ EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_TIMEOUT_COUNT,
+ EFCT_HW_LINK_STAT_ELASTIC_BUFFER_OVERRUN_COUNT,
+ EFCT_HW_LINK_STAT_ARB_TIMEOUT_COUNT,
+ EFCT_HW_LINK_STAT_ADVERTISED_RCV_B2B_CREDIT,
+ EFCT_HW_LINK_STAT_CURR_RCV_B2B_CREDIT,
+ EFCT_HW_LINK_STAT_ADVERTISED_XMIT_B2B_CREDIT,
+ EFCT_HW_LINK_STAT_CURR_XMIT_B2B_CREDIT,
+ EFCT_HW_LINK_STAT_RCV_EOFA_COUNT,
+ EFCT_HW_LINK_STAT_RCV_EOFDTI_COUNT,
+ EFCT_HW_LINK_STAT_RCV_EOFNI_COUNT,
+ EFCT_HW_LINK_STAT_RCV_SOFF_COUNT,
+ EFCT_HW_LINK_STAT_RCV_DROPPED_NO_AER_COUNT,
+ EFCT_HW_LINK_STAT_RCV_DROPPED_NO_RPI_COUNT,
+ EFCT_HW_LINK_STAT_RCV_DROPPED_NO_XRI_COUNT,
+ EFCT_HW_LINK_STAT_MAX,
+};
+
+enum efct_hw_host_stat {
+ EFCT_HW_HOST_STAT_TX_KBYTE_COUNT,
+ EFCT_HW_HOST_STAT_RX_KBYTE_COUNT,
+ EFCT_HW_HOST_STAT_TX_FRAME_COUNT,
+ EFCT_HW_HOST_STAT_RX_FRAME_COUNT,
+ EFCT_HW_HOST_STAT_TX_SEQ_COUNT,
+ EFCT_HW_HOST_STAT_RX_SEQ_COUNT,
+ EFCT_HW_HOST_STAT_TOTAL_EXCH_ORIG,
+ EFCT_HW_HOST_STAT_TOTAL_EXCH_RESP,
+ EFCT_HW_HOSY_STAT_RX_P_BSY_COUNT,
+ EFCT_HW_HOST_STAT_RX_F_BSY_COUNT,
+ EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_RQ_BUF_COUNT,
+ EFCT_HW_HOST_STAT_EMPTY_RQ_TIMEOUT_COUNT,
+ EFCT_HW_HOST_STAT_DROP_FRM_DUE_TO_NO_XRI_COUNT,
+ EFCT_HW_HOST_STAT_EMPTY_XRI_POOL_COUNT,
+ EFCT_HW_HOST_STAT_MAX,
+};
+
+enum efct_hw_state {
+ EFCT_HW_STATE_UNINITIALIZED,
+ EFCT_HW_STATE_QUEUES_ALLOCATED,
+ EFCT_HW_STATE_ACTIVE,
+ EFCT_HW_STATE_RESET_IN_PROGRESS,
+ EFCT_HW_STATE_TEARDOWN_IN_PROGRESS,
+};
+
+struct efct_hw_link_stat_counts {
+ u8 overflow;
+ u32 counter;
+};
+
+struct efct_hw_host_stat_counts {
+ u32 counter;
+};
+
+/* Structure used for the hash lookup of queue IDs */
+struct efct_queue_hash {
+ bool in_use;
+ u16 id;
+ u16 index;
+};
+
+/* WQ callback object */
+struct hw_wq_callback {
+ u16 instance_index; /* use for request tag */
+ void (*callback)(void *arg, u8 *cqe, int status);
+ void *arg;
+ struct list_head list_entry;
+};
+
+struct reqtag_pool {
+ spinlock_t lock; /* pool lock */
+ struct hw_wq_callback *tags[U16_MAX];
+ struct list_head freelist;
+};
+
+struct efct_hw_config {
+ u32 n_eq;
+ u32 n_cq;
+ u32 n_mq;
+ u32 n_rq;
+ u32 n_wq;
+ u32 n_io;
+ u32 n_sgl;
+ u32 speed;
+ u32 topology;
+ /* size of the buffers for first burst */
+ u32 rq_default_buffer_size;
+ u8 esoc;
+ /* MRQ RQ selection policy */
+ u8 rq_selection_policy;
+ /* RQ quanta if rq_selection_policy == 2 */
+ u8 rr_quanta;
+ u32 filter_def[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
+};
+
+struct efct_hw {
+ struct efct *os;
+ struct sli4 sli;
+ u16 ulp_start;
+ u16 ulp_max;
+ u32 dump_size;
+ enum efct_hw_state state;
+ bool hw_setup_called;
+ u8 sliport_healthcheck;
+ u16 fcf_indicator;
+
+ /* HW configuration */
+ struct efct_hw_config config;
+
+ /* calculated queue sizes for each type */
+ u32 num_qentries[SLI4_QTYPE_MAX];
+
+ /* Storage for SLI queue objects */
+ struct sli4_queue wq[EFCT_HW_MAX_NUM_WQ];
+ struct sli4_queue rq[EFCT_HW_MAX_NUM_RQ];
+ u16 hw_rq_lookup[EFCT_HW_MAX_NUM_RQ];
+ struct sli4_queue mq[EFCT_HW_MAX_NUM_MQ];
+ struct sli4_queue cq[EFCT_HW_MAX_NUM_CQ];
+ struct sli4_queue eq[EFCT_HW_MAX_NUM_EQ];
+
+ /* HW queue */
+ u32 eq_count;
+ u32 cq_count;
+ u32 mq_count;
+ u32 wq_count;
+ u32 rq_count;
+ u32 cmd_head_count;
+ struct list_head eq_list;
+
+ struct efct_queue_hash cq_hash[EFCT_HW_Q_HASH_SIZE];
+ struct efct_queue_hash rq_hash[EFCT_HW_Q_HASH_SIZE];
+ struct efct_queue_hash wq_hash[EFCT_HW_Q_HASH_SIZE];
+
+ /* Storage for HW queue objects */
+ struct hw_wq *hw_wq[EFCT_HW_MAX_NUM_WQ];
+ struct hw_rq *hw_rq[EFCT_HW_MAX_NUM_RQ];
+ struct hw_mq *hw_mq[EFCT_HW_MAX_NUM_MQ];
+ struct hw_cq *hw_cq[EFCT_HW_MAX_NUM_CQ];
+ struct hw_eq *hw_eq[EFCT_HW_MAX_NUM_EQ];
+ /* count of hw_rq[] entries */
+ u32 hw_rq_count;
+ /* count of multirq RQs */
+ u32 hw_mrq_count;
+
+ struct hw_wq **wq_cpu_array;
+
+ /* Sequence objects used in incoming frame processing */
+ struct efc_hw_sequence *seq_pool;
+
+ /* Maintain an ordered, linked list of outstanding HW commands. */
+ struct mutex bmbx_lock;
+ spinlock_t cmd_lock;
+ struct list_head cmd_head;
+ struct list_head cmd_pending;
+ mempool_t *cmd_ctx_pool;
+ mempool_t *mbox_rqst_pool;
+
+ struct sli4_link_event link;
+
+ /* pointer array of IO objects */
+ struct efct_hw_io **io;
+ /* array of WQE buffs mapped to IO objects */
+ u8 *wqe_buffs;
+
+ /* IO lock to synchronize list access */
+ spinlock_t io_lock;
+ /* List of IO objects in use */
+ struct list_head io_inuse;
+ /* List of IO objects waiting to be freed */
+ struct list_head io_wait_free;
+ /* List of IO objects available for allocation */
+ struct list_head io_free;
+
+ struct efc_dma loop_map;
+
+ struct efc_dma xfer_rdy;
+
+ struct efc_dma rnode_mem;
+
+ atomic_t io_alloc_failed_count;
+
+ /* stat: wq sumbit count */
+ u32 tcmd_wq_submit[EFCT_HW_MAX_NUM_WQ];
+ /* stat: wq complete count */
+ u32 tcmd_wq_complete[EFCT_HW_MAX_NUM_WQ];
+
+ atomic_t send_frame_seq_id;
+ struct reqtag_pool *wq_reqtag_pool;
+};
+
+enum efct_hw_io_count_type {
+ EFCT_HW_IO_INUSE_COUNT,
+ EFCT_HW_IO_FREE_COUNT,
+ EFCT_HW_IO_WAIT_FREE_COUNT,
+ EFCT_HW_IO_N_TOTAL_IO_COUNT,
+};
+
+/* HW queue data structures */
+struct hw_eq {
+ struct list_head list_entry;
+ enum sli4_qtype type;
+ u32 instance;
+ u32 entry_count;
+ u32 entry_size;
+ struct efct_hw *hw;
+ struct sli4_queue *queue;
+ struct list_head cq_list;
+ u32 use_count;
+};
+
+struct hw_cq {
+ struct list_head list_entry;
+ enum sli4_qtype type;
+ u32 instance;
+ u32 entry_count;
+ u32 entry_size;
+ struct hw_eq *eq;
+ struct sli4_queue *queue;
+ struct list_head q_list;
+ u32 use_count;
+};
+
+struct hw_q {
+ struct list_head list_entry;
+ enum sli4_qtype type;
+};
+
+struct hw_mq {
+ struct list_head list_entry;
+ enum sli4_qtype type;
+ u32 instance;
+
+ u32 entry_count;
+ u32 entry_size;
+ struct hw_cq *cq;
+ struct sli4_queue *queue;
+
+ u32 use_count;
+};
+
+struct hw_wq {
+ struct list_head list_entry;
+ enum sli4_qtype type;
+ u32 instance;
+ struct efct_hw *hw;
+
+ u32 entry_count;
+ u32 entry_size;
+ struct hw_cq *cq;
+ struct sli4_queue *queue;
+ u32 class;
+
+ /* WQ consumed */
+ u32 wqec_set_count;
+ u32 wqec_count;
+ u32 free_count;
+ u32 total_submit_count;
+ struct list_head pending_list;
+
+ /* HW IO allocated for use with Send Frame */
+ struct efct_hw_io *send_frame_io;
+
+ /* Stats */
+ u32 use_count;
+ u32 wq_pending_count;
+};
+
+struct hw_rq {
+ struct list_head list_entry;
+ enum sli4_qtype type;
+ u32 instance;
+
+ u32 entry_count;
+ u32 use_count;
+ u32 hdr_entry_size;
+ u32 first_burst_entry_size;
+ u32 data_entry_size;
+ bool is_mrq;
+ u32 base_mrq_id;
+
+ struct hw_cq *cq;
+
+ u8 filter_mask;
+ struct sli4_queue *hdr;
+ struct sli4_queue *first_burst;
+ struct sli4_queue *data;
+
+ struct efc_hw_rq_buffer *hdr_buf;
+ struct efc_hw_rq_buffer *fb_buf;
+ struct efc_hw_rq_buffer *payload_buf;
+ /* RQ tracker for this RQ */
+ struct efc_hw_sequence **rq_tracker;
+};
+
+struct efct_hw_send_frame_context {
+ struct efct_hw *hw;
+ struct hw_wq_callback *wqcb;
+ struct efct_hw_wqe wqe;
+ void (*callback)(int status, void *arg);
+ void *arg;
+
+ /* General purpose elements */
+ struct efc_hw_sequence *seq;
+ struct efc_dma payload;
+};
+
+struct efct_hw_grp_hdr {
+ u32 size;
+ __be32 magic_number;
+ u32 word2;
+ u8 rev_name[128];
+ u8 date[12];
+ u8 revision[32];
+};
+
+static inline int
+efct_hw_get_link_speed(struct efct_hw *hw) {
+ return hw->link.speed;
+}
+
+int
+efct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev);
+int efct_hw_init(struct efct_hw *hw);
+int
+efct_hw_parse_filter(struct efct_hw *hw, void *value);
+int
+efct_hw_init_queues(struct efct_hw *hw);
+int
+efct_hw_map_wq_cpu(struct efct_hw *hw);
+uint64_t
+efct_get_wwnn(struct efct_hw *hw);
+uint64_t
+efct_get_wwpn(struct efct_hw *hw);
+
+int efct_hw_rx_allocate(struct efct_hw *hw);
+int efct_hw_rx_post(struct efct_hw *hw);
+void efct_hw_rx_free(struct efct_hw *hw);
+int
+efct_hw_command(struct efct_hw *hw, u8 *cmd, u32 opts, void *cb,
+ void *arg);
+int
+efct_issue_mbox_rqst(void *base, void *cmd, void *cb, void *arg);
+
+struct efct_hw_io *efct_hw_io_alloc(struct efct_hw *hw);
+int efct_hw_io_free(struct efct_hw *hw, struct efct_hw_io *io);
+u8 efct_hw_io_inuse(struct efct_hw *hw, struct efct_hw_io *io);
+int
+efct_hw_io_send(struct efct_hw *hw, enum efct_hw_io_type type,
+ struct efct_hw_io *io, union efct_hw_io_param_u *iparam,
+ void *cb, void *arg);
+int
+efct_hw_io_register_sgl(struct efct_hw *hw, struct efct_hw_io *io,
+ struct efc_dma *sgl,
+ u32 sgl_count);
+int
+efct_hw_io_init_sges(struct efct_hw *hw,
+ struct efct_hw_io *io, enum efct_hw_io_type type);
+
+int
+efct_hw_io_add_sge(struct efct_hw *hw, struct efct_hw_io *io,
+ uintptr_t addr, u32 length);
+int
+efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort,
+ bool send_abts, void *cb, void *arg);
+u32
+efct_hw_io_get_count(struct efct_hw *hw,
+ enum efct_hw_io_count_type io_count_type);
+struct efct_hw_io
+*efct_hw_io_lookup(struct efct_hw *hw, u32 indicator);
+void efct_hw_io_abort_all(struct efct_hw *hw);
+void efct_hw_io_free_internal(struct kref *arg);
+
+/* HW WQ request tag API */
+struct reqtag_pool *efct_hw_reqtag_pool_alloc(struct efct_hw *hw);
+void efct_hw_reqtag_pool_free(struct efct_hw *hw);
+struct hw_wq_callback
+*efct_hw_reqtag_alloc(struct efct_hw *hw,
+ void (*callback)(void *arg, u8 *cqe,
+ int status), void *arg);
+void
+efct_hw_reqtag_free(struct efct_hw *hw, struct hw_wq_callback *wqcb);
+struct hw_wq_callback
+*efct_hw_reqtag_get_instance(struct efct_hw *hw, u32 instance_index);
+
+/* RQ completion handlers for RQ pair mode */
+int
+efct_hw_rqpair_process_rq(struct efct_hw *hw,
+ struct hw_cq *cq, u8 *cqe);
+int
+efct_hw_rqpair_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq);
+static inline void
+efct_hw_sequence_copy(struct efc_hw_sequence *dst,
+ struct efc_hw_sequence *src)
+{
+ /* Copy src to dst, then zero out the linked list link */
+ *dst = *src;
+}
+
+int
+efct_efc_hw_sequence_free(struct efc *efc, struct efc_hw_sequence *seq);
+
+static inline int
+efct_hw_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq)
+{
+ /* Only RQ pair mode is supported */
+ return efct_hw_rqpair_sequence_free(hw, seq);
+}
+
+int
+efct_hw_eq_process(struct efct_hw *hw, struct hw_eq *eq,
+ u32 max_isr_time_msec);
+void efct_hw_cq_process(struct efct_hw *hw, struct hw_cq *cq);
+void
+efct_hw_wq_process(struct efct_hw *hw, struct hw_cq *cq,
+ u8 *cqe, int status, u16 rid);
+void
+efct_hw_xabt_process(struct efct_hw *hw, struct hw_cq *cq,
+ u8 *cqe, u16 rid);
+int
+efct_hw_process(struct efct_hw *hw, u32 vector, u32 max_isr_time_msec);
+int
+efct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id);
+int efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe);
+int
+efct_hw_send_frame(struct efct_hw *hw, struct fc_frame_header *hdr,
+ u8 sof, u8 eof, struct efc_dma *payload,
+ struct efct_hw_send_frame_context *ctx,
+ void (*callback)(void *arg, u8 *cqe, int status),
+ void *arg);
+int
+efct_els_hw_srrs_send(struct efc *efc, struct efc_disc_io *io);
+int
+efct_efc_bls_send(struct efc *efc, u32 type, struct sli_bls_params *bls);
+int
+efct_hw_bls_send(struct efct *efct, u32 type, struct sli_bls_params *bls_params,
+ void *cb, void *arg);
+
+/* Function for retrieving link statistics */
+int
+efct_hw_get_link_stats(struct efct_hw *hw,
+ u8 req_ext_counters,
+ u8 clear_overflow_flags,
+ u8 clear_all_counters,
+ void (*efct_hw_link_stat_cb_t)(int status,
+ u32 num_counters,
+ struct efct_hw_link_stat_counts *counters, void *arg),
+ void *arg);
+/* Function for retrieving host statistics */
+int
+efct_hw_get_host_stats(struct efct_hw *hw,
+ u8 cc,
+ void (*efct_hw_host_stat_cb_t)(int status,
+ u32 num_counters,
+ struct efct_hw_host_stat_counts *counters, void *arg),
+ void *arg);
+int
+efct_hw_firmware_write(struct efct_hw *hw, struct efc_dma *dma,
+ u32 size, u32 offset, int last,
+ void (*cb)(int status, u32 bytes_written,
+ u32 change_status, void *arg),
+ void *arg);
+typedef void (*efct_hw_async_cb_t)(struct efct_hw *hw, int status,
+ u8 *mqe, void *arg);
+int
+efct_hw_async_call(struct efct_hw *hw, efct_hw_async_cb_t callback, void *arg);
+
+struct hw_eq *efct_hw_new_eq(struct efct_hw *hw, u32 entry_count);
+struct hw_cq *efct_hw_new_cq(struct hw_eq *eq, u32 entry_count);
+u32
+efct_hw_new_cq_set(struct hw_eq *eqs[], struct hw_cq *cqs[],
+ u32 num_cqs, u32 entry_count);
+struct hw_mq *efct_hw_new_mq(struct hw_cq *cq, u32 entry_count);
+struct hw_wq
+*efct_hw_new_wq(struct hw_cq *cq, u32 entry_count);
+u32
+efct_hw_new_rq_set(struct hw_cq *cqs[], struct hw_rq *rqs[],
+ u32 num_rq_pairs, u32 entry_count);
+void efct_hw_del_eq(struct hw_eq *eq);
+void efct_hw_del_cq(struct hw_cq *cq);
+void efct_hw_del_mq(struct hw_mq *mq);
+void efct_hw_del_wq(struct hw_wq *wq);
+void efct_hw_del_rq(struct hw_rq *rq);
+void efct_hw_queue_teardown(struct efct_hw *hw);
+void efct_hw_teardown(struct efct_hw *hw);
+int
+efct_hw_reset(struct efct_hw *hw, enum efct_hw_reset reset);
+
+int
+efct_hw_port_control(struct efct_hw *hw, enum efct_hw_port ctrl,
+ uintptr_t value,
+ void (*cb)(int status, uintptr_t value, void *arg),
+ void *arg);
+
+#endif /* __EFCT_H__ */
diff --git a/drivers/scsi/elx/efct/efct_hw_queues.c b/drivers/scsi/elx/efct/efct_hw_queues.c
new file mode 100644
index 000000000000..3a1d1a5864a3
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_hw_queues.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efct_driver.h"
+#include "efct_hw.h"
+#include "efct_unsol.h"
+
+int
+efct_hw_init_queues(struct efct_hw *hw)
+{
+ struct hw_eq *eq = NULL;
+ struct hw_cq *cq = NULL;
+ struct hw_wq *wq = NULL;
+ struct hw_mq *mq = NULL;
+
+ struct hw_eq *eqs[EFCT_HW_MAX_NUM_EQ];
+ struct hw_cq *cqs[EFCT_HW_MAX_NUM_EQ];
+ struct hw_rq *rqs[EFCT_HW_MAX_NUM_EQ];
+ u32 i = 0, j;
+
+ hw->eq_count = 0;
+ hw->cq_count = 0;
+ hw->mq_count = 0;
+ hw->wq_count = 0;
+ hw->rq_count = 0;
+ hw->hw_rq_count = 0;
+ INIT_LIST_HEAD(&hw->eq_list);
+
+ for (i = 0; i < hw->config.n_eq; i++) {
+ /* Create EQ */
+ eq = efct_hw_new_eq(hw, EFCT_HW_EQ_DEPTH);
+ if (!eq) {
+ efct_hw_queue_teardown(hw);
+ return -ENOMEM;
+ }
+
+ eqs[i] = eq;
+
+ /* Create one MQ */
+ if (!i) {
+ cq = efct_hw_new_cq(eq,
+ hw->num_qentries[SLI4_QTYPE_CQ]);
+ if (!cq) {
+ efct_hw_queue_teardown(hw);
+ return -ENOMEM;
+ }
+
+ mq = efct_hw_new_mq(cq, EFCT_HW_MQ_DEPTH);
+ if (!mq) {
+ efct_hw_queue_teardown(hw);
+ return -ENOMEM;
+ }
+ }
+
+ /* Create WQ */
+ cq = efct_hw_new_cq(eq, hw->num_qentries[SLI4_QTYPE_CQ]);
+ if (!cq) {
+ efct_hw_queue_teardown(hw);
+ return -ENOMEM;
+ }
+
+ wq = efct_hw_new_wq(cq, hw->num_qentries[SLI4_QTYPE_WQ]);
+ if (!wq) {
+ efct_hw_queue_teardown(hw);
+ return -ENOMEM;
+ }
+ }
+
+ /* Create CQ set */
+ if (efct_hw_new_cq_set(eqs, cqs, i, hw->num_qentries[SLI4_QTYPE_CQ])) {
+ efct_hw_queue_teardown(hw);
+ return -EIO;
+ }
+
+ /* Create RQ set */
+ if (efct_hw_new_rq_set(cqs, rqs, i, EFCT_HW_RQ_ENTRIES_DEF)) {
+ efct_hw_queue_teardown(hw);
+ return -EIO;
+ }
+
+ for (j = 0; j < i ; j++) {
+ rqs[j]->filter_mask = 0;
+ rqs[j]->is_mrq = true;
+ rqs[j]->base_mrq_id = rqs[0]->hdr->id;
+ }
+
+ hw->hw_mrq_count = i;
+
+ return 0;
+}
+
+int
+efct_hw_map_wq_cpu(struct efct_hw *hw)
+{
+ struct efct *efct = hw->os;
+ u32 cpu = 0, i;
+
+ /* Init cpu_map array */
+ hw->wq_cpu_array = kcalloc(num_possible_cpus(), sizeof(void *),
+ GFP_KERNEL);
+ if (!hw->wq_cpu_array)
+ return -ENOMEM;
+
+ for (i = 0; i < hw->config.n_eq; i++) {
+ const struct cpumask *maskp;
+
+ /* Get a CPU mask for all CPUs affinitized to this vector */
+ maskp = pci_irq_get_affinity(efct->pci, i);
+ if (!maskp) {
+ efc_log_debug(efct, "maskp null for vector:%d\n", i);
+ continue;
+ }
+
+ /* Loop through all CPUs associated with vector idx */
+ for_each_cpu_and(cpu, maskp, cpu_present_mask) {
+ efc_log_debug(efct, "CPU:%d irq vector:%d\n", cpu, i);
+ hw->wq_cpu_array[cpu] = hw->hw_wq[i];
+ }
+ }
+
+ return 0;
+}
+
+struct hw_eq *
+efct_hw_new_eq(struct efct_hw *hw, u32 entry_count)
+{
+ struct hw_eq *eq = kzalloc(sizeof(*eq), GFP_KERNEL);
+
+ if (!eq)
+ return NULL;
+
+ eq->type = SLI4_QTYPE_EQ;
+ eq->hw = hw;
+ eq->entry_count = entry_count;
+ eq->instance = hw->eq_count++;
+ eq->queue = &hw->eq[eq->instance];
+ INIT_LIST_HEAD(&eq->cq_list);
+
+ if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_EQ, eq->queue, entry_count,
+ NULL)) {
+ efc_log_err(hw->os, "EQ[%d] alloc failure\n", eq->instance);
+ kfree(eq);
+ return NULL;
+ }
+
+ sli_eq_modify_delay(&hw->sli, eq->queue, 1, 0, 8);
+ hw->hw_eq[eq->instance] = eq;
+ INIT_LIST_HEAD(&eq->list_entry);
+ list_add_tail(&eq->list_entry, &hw->eq_list);
+ efc_log_debug(hw->os, "create eq[%2d] id %3d len %4d\n", eq->instance,
+ eq->queue->id, eq->entry_count);
+ return eq;
+}
+
+struct hw_cq *
+efct_hw_new_cq(struct hw_eq *eq, u32 entry_count)
+{
+ struct efct_hw *hw = eq->hw;
+ struct hw_cq *cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+
+ if (!cq)
+ return NULL;
+
+ cq->eq = eq;
+ cq->type = SLI4_QTYPE_CQ;
+ cq->instance = eq->hw->cq_count++;
+ cq->entry_count = entry_count;
+ cq->queue = &hw->cq[cq->instance];
+
+ INIT_LIST_HEAD(&cq->q_list);
+
+ if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_CQ, cq->queue,
+ cq->entry_count, eq->queue)) {
+ efc_log_err(hw->os, "CQ[%d] allocation failure len=%d\n",
+ eq->instance, eq->entry_count);
+ kfree(cq);
+ return NULL;
+ }
+
+ hw->hw_cq[cq->instance] = cq;
+ INIT_LIST_HEAD(&cq->list_entry);
+ list_add_tail(&cq->list_entry, &eq->cq_list);
+ efc_log_debug(hw->os, "create cq[%2d] id %3d len %4d\n", cq->instance,
+ cq->queue->id, cq->entry_count);
+ return cq;
+}
+
+u32
+efct_hw_new_cq_set(struct hw_eq *eqs[], struct hw_cq *cqs[],
+ u32 num_cqs, u32 entry_count)
+{
+ u32 i;
+ struct efct_hw *hw = eqs[0]->hw;
+ struct sli4 *sli4 = &hw->sli;
+ struct hw_cq *cq = NULL;
+ struct sli4_queue *qs[SLI4_MAX_CQ_SET_COUNT];
+ struct sli4_queue *assefct[SLI4_MAX_CQ_SET_COUNT];
+
+ /* Initialise CQS pointers to NULL */
+ for (i = 0; i < num_cqs; i++)
+ cqs[i] = NULL;
+
+ for (i = 0; i < num_cqs; i++) {
+ cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+ if (!cq)
+ goto error;
+
+ cqs[i] = cq;
+ cq->eq = eqs[i];
+ cq->type = SLI4_QTYPE_CQ;
+ cq->instance = hw->cq_count++;
+ cq->entry_count = entry_count;
+ cq->queue = &hw->cq[cq->instance];
+ qs[i] = cq->queue;
+ assefct[i] = eqs[i]->queue;
+ INIT_LIST_HEAD(&cq->q_list);
+ }
+
+ if (sli_cq_alloc_set(sli4, qs, num_cqs, entry_count, assefct)) {
+ efc_log_err(hw->os, "Failed to create CQ Set.\n");
+ goto error;
+ }
+
+ for (i = 0; i < num_cqs; i++) {
+ hw->hw_cq[cqs[i]->instance] = cqs[i];
+ INIT_LIST_HEAD(&cqs[i]->list_entry);
+ list_add_tail(&cqs[i]->list_entry, &cqs[i]->eq->cq_list);
+ }
+
+ return 0;
+
+error:
+ for (i = 0; i < num_cqs; i++) {
+ kfree(cqs[i]);
+ cqs[i] = NULL;
+ }
+ return -EIO;
+}
+
+struct hw_mq *
+efct_hw_new_mq(struct hw_cq *cq, u32 entry_count)
+{
+ struct efct_hw *hw = cq->eq->hw;
+ struct hw_mq *mq = kzalloc(sizeof(*mq), GFP_KERNEL);
+
+ if (!mq)
+ return NULL;
+
+ mq->cq = cq;
+ mq->type = SLI4_QTYPE_MQ;
+ mq->instance = cq->eq->hw->mq_count++;
+ mq->entry_count = entry_count;
+ mq->entry_size = EFCT_HW_MQ_DEPTH;
+ mq->queue = &hw->mq[mq->instance];
+
+ if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_MQ, mq->queue, mq->entry_size,
+ cq->queue)) {
+ efc_log_err(hw->os, "MQ allocation failure\n");
+ kfree(mq);
+ return NULL;
+ }
+
+ hw->hw_mq[mq->instance] = mq;
+ INIT_LIST_HEAD(&mq->list_entry);
+ list_add_tail(&mq->list_entry, &cq->q_list);
+ efc_log_debug(hw->os, "create mq[%2d] id %3d len %4d\n", mq->instance,
+ mq->queue->id, mq->entry_count);
+ return mq;
+}
+
+struct hw_wq *
+efct_hw_new_wq(struct hw_cq *cq, u32 entry_count)
+{
+ struct efct_hw *hw = cq->eq->hw;
+ struct hw_wq *wq = kzalloc(sizeof(*wq), GFP_KERNEL);
+
+ if (!wq)
+ return NULL;
+
+ wq->hw = cq->eq->hw;
+ wq->cq = cq;
+ wq->type = SLI4_QTYPE_WQ;
+ wq->instance = cq->eq->hw->wq_count++;
+ wq->entry_count = entry_count;
+ wq->queue = &hw->wq[wq->instance];
+ wq->wqec_set_count = EFCT_HW_WQEC_SET_COUNT;
+ wq->wqec_count = wq->wqec_set_count;
+ wq->free_count = wq->entry_count - 1;
+ INIT_LIST_HEAD(&wq->pending_list);
+
+ if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_WQ, wq->queue,
+ wq->entry_count, cq->queue)) {
+ efc_log_err(hw->os, "WQ allocation failure\n");
+ kfree(wq);
+ return NULL;
+ }
+
+ hw->hw_wq[wq->instance] = wq;
+ INIT_LIST_HEAD(&wq->list_entry);
+ list_add_tail(&wq->list_entry, &cq->q_list);
+ efc_log_debug(hw->os, "create wq[%2d] id %3d len %4d cls %d\n",
+ wq->instance, wq->queue->id, wq->entry_count, wq->class);
+ return wq;
+}
+
+u32
+efct_hw_new_rq_set(struct hw_cq *cqs[], struct hw_rq *rqs[],
+ u32 num_rq_pairs, u32 entry_count)
+{
+ struct efct_hw *hw = cqs[0]->eq->hw;
+ struct hw_rq *rq = NULL;
+ struct sli4_queue *qs[SLI4_MAX_RQ_SET_COUNT * 2] = { NULL };
+ u32 i, q_count, size;
+
+ /* Initialise RQS pointers */
+ for (i = 0; i < num_rq_pairs; i++)
+ rqs[i] = NULL;
+
+ /*
+ * Allocate an RQ object SET, where each element in set
+ * encapsulates 2 SLI queues (for rq pair)
+ */
+ for (i = 0, q_count = 0; i < num_rq_pairs; i++, q_count += 2) {
+ rq = kzalloc(sizeof(*rq), GFP_KERNEL);
+ if (!rq)
+ goto error;
+
+ rqs[i] = rq;
+ rq->instance = hw->hw_rq_count++;
+ rq->cq = cqs[i];
+ rq->type = SLI4_QTYPE_RQ;
+ rq->entry_count = entry_count;
+
+ /* Header RQ */
+ rq->hdr = &hw->rq[hw->rq_count];
+ rq->hdr_entry_size = EFCT_HW_RQ_HEADER_SIZE;
+ hw->hw_rq_lookup[hw->rq_count] = rq->instance;
+ hw->rq_count++;
+ qs[q_count] = rq->hdr;
+
+ /* Data RQ */
+ rq->data = &hw->rq[hw->rq_count];
+ rq->data_entry_size = hw->config.rq_default_buffer_size;
+ hw->hw_rq_lookup[hw->rq_count] = rq->instance;
+ hw->rq_count++;
+ qs[q_count + 1] = rq->data;
+
+ rq->rq_tracker = NULL;
+ }
+
+ if (sli_fc_rq_set_alloc(&hw->sli, num_rq_pairs, qs,
+ cqs[0]->queue->id,
+ rqs[0]->entry_count,
+ rqs[0]->hdr_entry_size,
+ rqs[0]->data_entry_size)) {
+ efc_log_err(hw->os, "RQ Set alloc failure for base CQ=%d\n",
+ cqs[0]->queue->id);
+ goto error;
+ }
+
+ for (i = 0; i < num_rq_pairs; i++) {
+ hw->hw_rq[rqs[i]->instance] = rqs[i];
+ INIT_LIST_HEAD(&rqs[i]->list_entry);
+ list_add_tail(&rqs[i]->list_entry, &cqs[i]->q_list);
+ size = sizeof(struct efc_hw_sequence *) * rqs[i]->entry_count;
+ rqs[i]->rq_tracker = kzalloc(size, GFP_KERNEL);
+ if (!rqs[i]->rq_tracker)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ for (i = 0; i < num_rq_pairs; i++) {
+ if (rqs[i]) {
+ kfree(rqs[i]->rq_tracker);
+ kfree(rqs[i]);
+ }
+ }
+
+ return -EIO;
+}
+
+void
+efct_hw_del_eq(struct hw_eq *eq)
+{
+ struct hw_cq *cq;
+ struct hw_cq *cq_next;
+
+ if (!eq)
+ return;
+
+ list_for_each_entry_safe(cq, cq_next, &eq->cq_list, list_entry)
+ efct_hw_del_cq(cq);
+ list_del(&eq->list_entry);
+ eq->hw->hw_eq[eq->instance] = NULL;
+ kfree(eq);
+}
+
+void
+efct_hw_del_cq(struct hw_cq *cq)
+{
+ struct hw_q *q;
+ struct hw_q *q_next;
+
+ if (!cq)
+ return;
+
+ list_for_each_entry_safe(q, q_next, &cq->q_list, list_entry) {
+ switch (q->type) {
+ case SLI4_QTYPE_MQ:
+ efct_hw_del_mq((struct hw_mq *)q);
+ break;
+ case SLI4_QTYPE_WQ:
+ efct_hw_del_wq((struct hw_wq *)q);
+ break;
+ case SLI4_QTYPE_RQ:
+ efct_hw_del_rq((struct hw_rq *)q);
+ break;
+ default:
+ break;
+ }
+ }
+ list_del(&cq->list_entry);
+ cq->eq->hw->hw_cq[cq->instance] = NULL;
+ kfree(cq);
+}
+
+void
+efct_hw_del_mq(struct hw_mq *mq)
+{
+ if (!mq)
+ return;
+
+ list_del(&mq->list_entry);
+ mq->cq->eq->hw->hw_mq[mq->instance] = NULL;
+ kfree(mq);
+}
+
+void
+efct_hw_del_wq(struct hw_wq *wq)
+{
+ if (!wq)
+ return;
+
+ list_del(&wq->list_entry);
+ wq->cq->eq->hw->hw_wq[wq->instance] = NULL;
+ kfree(wq);
+}
+
+void
+efct_hw_del_rq(struct hw_rq *rq)
+{
+ struct efct_hw *hw = NULL;
+
+ if (!rq)
+ return;
+ /* Free RQ tracker */
+ kfree(rq->rq_tracker);
+ rq->rq_tracker = NULL;
+ list_del(&rq->list_entry);
+ hw = rq->cq->eq->hw;
+ hw->hw_rq[rq->instance] = NULL;
+ kfree(rq);
+}
+
+void
+efct_hw_queue_teardown(struct efct_hw *hw)
+{
+ struct hw_eq *eq;
+ struct hw_eq *eq_next;
+
+ if (!hw->eq_list.next)
+ return;
+
+ list_for_each_entry_safe(eq, eq_next, &hw->eq_list, list_entry)
+ efct_hw_del_eq(eq);
+}
+
+static inline int
+efct_hw_rqpair_find(struct efct_hw *hw, u16 rq_id)
+{
+ return efct_hw_queue_hash_find(hw->rq_hash, rq_id);
+}
+
+static struct efc_hw_sequence *
+efct_hw_rqpair_get(struct efct_hw *hw, u16 rqindex, u16 bufindex)
+{
+ struct sli4_queue *rq_hdr = &hw->rq[rqindex];
+ struct efc_hw_sequence *seq = NULL;
+ struct hw_rq *rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]];
+ unsigned long flags = 0;
+
+ if (bufindex >= rq_hdr->length) {
+ efc_log_err(hw->os,
+ "RQidx %d bufidx %d exceed ring len %d for id %d\n",
+ rqindex, bufindex, rq_hdr->length, rq_hdr->id);
+ return NULL;
+ }
+
+ /* rq_hdr lock also covers rqindex+1 queue */
+ spin_lock_irqsave(&rq_hdr->lock, flags);
+
+ seq = rq->rq_tracker[bufindex];
+ rq->rq_tracker[bufindex] = NULL;
+
+ if (!seq) {
+ efc_log_err(hw->os,
+ "RQbuf NULL, rqidx %d, bufidx %d, cur q idx = %d\n",
+ rqindex, bufindex, rq_hdr->index);
+ }
+
+ spin_unlock_irqrestore(&rq_hdr->lock, flags);
+ return seq;
+}
+
+int
+efct_hw_rqpair_process_rq(struct efct_hw *hw, struct hw_cq *cq,
+ u8 *cqe)
+{
+ u16 rq_id;
+ u32 index;
+ int rqindex;
+ int rq_status;
+ u32 h_len;
+ u32 p_len;
+ struct efc_hw_sequence *seq;
+ struct hw_rq *rq;
+
+ rq_status = sli_fc_rqe_rqid_and_index(&hw->sli, cqe,
+ &rq_id, &index);
+ if (rq_status != 0) {
+ switch (rq_status) {
+ case SLI4_FC_ASYNC_RQ_BUF_LEN_EXCEEDED:
+ case SLI4_FC_ASYNC_RQ_DMA_FAILURE:
+ /* just get RQ buffer then return to chip */
+ rqindex = efct_hw_rqpair_find(hw, rq_id);
+ if (rqindex < 0) {
+ efc_log_debug(hw->os,
+ "status=%#x: lookup fail id=%#x\n",
+ rq_status, rq_id);
+ break;
+ }
+
+ /* get RQ buffer */
+ seq = efct_hw_rqpair_get(hw, rqindex, index);
+
+ /* return to chip */
+ if (efct_hw_rqpair_sequence_free(hw, seq)) {
+ efc_log_debug(hw->os,
+ "status=%#x,fail rtrn buf to RQ\n",
+ rq_status);
+ break;
+ }
+ break;
+ case SLI4_FC_ASYNC_RQ_INSUFF_BUF_NEEDED:
+ case SLI4_FC_ASYNC_RQ_INSUFF_BUF_FRM_DISC:
+ /*
+ * since RQ buffers were not consumed, cannot return
+ * them to chip
+ */
+ efc_log_debug(hw->os, "Warning: RCQE status=%#x,\n",
+ rq_status);
+ fallthrough;
+ default:
+ break;
+ }
+ return -EIO;
+ }
+
+ rqindex = efct_hw_rqpair_find(hw, rq_id);
+ if (rqindex < 0) {
+ efc_log_debug(hw->os, "Error: rq_id lookup failed for id=%#x\n",
+ rq_id);
+ return -EIO;
+ }
+
+ rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]];
+ rq->use_count++;
+
+ seq = efct_hw_rqpair_get(hw, rqindex, index);
+ if (WARN_ON(!seq))
+ return -EIO;
+
+ seq->hw = hw;
+
+ sli_fc_rqe_length(&hw->sli, cqe, &h_len, &p_len);
+ seq->header->dma.len = h_len;
+ seq->payload->dma.len = p_len;
+ seq->fcfi = sli_fc_rqe_fcfi(&hw->sli, cqe);
+ seq->hw_priv = cq->eq;
+
+ efct_unsolicited_cb(hw->os, seq);
+
+ return 0;
+}
+
+static int
+efct_hw_rqpair_put(struct efct_hw *hw, struct efc_hw_sequence *seq)
+{
+ struct sli4_queue *rq_hdr = &hw->rq[seq->header->rqindex];
+ struct sli4_queue *rq_payload = &hw->rq[seq->payload->rqindex];
+ u32 hw_rq_index = hw->hw_rq_lookup[seq->header->rqindex];
+ struct hw_rq *rq = hw->hw_rq[hw_rq_index];
+ u32 phys_hdr[2];
+ u32 phys_payload[2];
+ int qindex_hdr;
+ int qindex_payload;
+ unsigned long flags = 0;
+
+ /* Update the RQ verification lookup tables */
+ phys_hdr[0] = upper_32_bits(seq->header->dma.phys);
+ phys_hdr[1] = lower_32_bits(seq->header->dma.phys);
+ phys_payload[0] = upper_32_bits(seq->payload->dma.phys);
+ phys_payload[1] = lower_32_bits(seq->payload->dma.phys);
+
+ /* rq_hdr lock also covers payload / header->rqindex+1 queue */
+ spin_lock_irqsave(&rq_hdr->lock, flags);
+
+ /*
+ * Note: The header must be posted last for buffer pair mode because
+ * posting on the header queue posts the payload queue as well.
+ * We do not ring the payload queue independently in RQ pair mode.
+ */
+ qindex_payload = sli_rq_write(&hw->sli, rq_payload,
+ (void *)phys_payload);
+ qindex_hdr = sli_rq_write(&hw->sli, rq_hdr, (void *)phys_hdr);
+ if (qindex_hdr < 0 ||
+ qindex_payload < 0) {
+ efc_log_err(hw->os, "RQ_ID=%#x write failed\n", rq_hdr->id);
+ spin_unlock_irqrestore(&rq_hdr->lock, flags);
+ return -EIO;
+ }
+
+ /* ensure the indexes are the same */
+ WARN_ON(qindex_hdr != qindex_payload);
+
+ /* Update the lookup table */
+ if (!rq->rq_tracker[qindex_hdr]) {
+ rq->rq_tracker[qindex_hdr] = seq;
+ } else {
+ efc_log_debug(hw->os,
+ "expected rq_tracker[%d][%d] buffer to be NULL\n",
+ hw_rq_index, qindex_hdr);
+ }
+
+ spin_unlock_irqrestore(&rq_hdr->lock, flags);
+ return 0;
+}
+
+int
+efct_hw_rqpair_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq)
+{
+ int rc = 0;
+
+ /*
+ * Post the data buffer first. Because in RQ pair mode, ringing the
+ * doorbell of the header ring will post the data buffer as well.
+ */
+ if (efct_hw_rqpair_put(hw, seq)) {
+ efc_log_err(hw->os, "error writing buffers\n");
+ return -EIO;
+ }
+
+ return rc;
+}
+
+int
+efct_efc_hw_sequence_free(struct efc *efc, struct efc_hw_sequence *seq)
+{
+ struct efct *efct = efc->base;
+
+ return efct_hw_rqpair_sequence_free(&efct->hw, seq);
+}
diff --git a/drivers/scsi/elx/efct/efct_io.c b/drivers/scsi/elx/efct/efct_io.c
new file mode 100644
index 000000000000..c612f0a48839
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_io.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efct_driver.h"
+#include "efct_hw.h"
+#include "efct_io.h"
+
+struct efct_io_pool {
+ struct efct *efct;
+ spinlock_t lock; /* IO pool lock */
+ u32 io_num_ios; /* Total IOs allocated */
+ struct efct_io *ios[EFCT_NUM_SCSI_IOS];
+ struct list_head freelist;
+
+};
+
+struct efct_io_pool *
+efct_io_pool_create(struct efct *efct, u32 num_sgl)
+{
+ u32 i = 0;
+ struct efct_io_pool *io_pool;
+ struct efct_io *io;
+
+ /* Allocate the IO pool */
+ io_pool = kzalloc(sizeof(*io_pool), GFP_KERNEL);
+ if (!io_pool)
+ return NULL;
+
+ io_pool->efct = efct;
+ INIT_LIST_HEAD(&io_pool->freelist);
+ /* initialize IO pool lock */
+ spin_lock_init(&io_pool->lock);
+
+ for (i = 0; i < EFCT_NUM_SCSI_IOS; i++) {
+ io = kzalloc(sizeof(*io), GFP_KERNEL);
+ if (!io)
+ break;
+
+ io_pool->io_num_ios++;
+ io_pool->ios[i] = io;
+ io->tag = i;
+ io->instance_index = i;
+
+ /* Allocate a response buffer */
+ io->rspbuf.size = SCSI_RSP_BUF_LENGTH;
+ io->rspbuf.virt = dma_alloc_coherent(&efct->pci->dev,
+ io->rspbuf.size,
+ &io->rspbuf.phys, GFP_KERNEL);
+ if (!io->rspbuf.virt) {
+ efc_log_err(efct, "dma_alloc rspbuf failed\n");
+ efct_io_pool_free(io_pool);
+ return NULL;
+ }
+
+ /* Allocate SGL */
+ io->sgl = kzalloc(sizeof(*io->sgl) * num_sgl, GFP_KERNEL);
+ if (!io->sgl) {
+ efct_io_pool_free(io_pool);
+ return NULL;
+ }
+
+ io->sgl_allocated = num_sgl;
+ io->sgl_count = 0;
+
+ INIT_LIST_HEAD(&io->list_entry);
+ list_add_tail(&io->list_entry, &io_pool->freelist);
+ }
+
+ return io_pool;
+}
+
+int
+efct_io_pool_free(struct efct_io_pool *io_pool)
+{
+ struct efct *efct;
+ u32 i;
+ struct efct_io *io;
+
+ if (io_pool) {
+ efct = io_pool->efct;
+
+ for (i = 0; i < io_pool->io_num_ios; i++) {
+ io = io_pool->ios[i];
+ if (!io)
+ continue;
+
+ kfree(io->sgl);
+ dma_free_coherent(&efct->pci->dev,
+ io->rspbuf.size, io->rspbuf.virt,
+ io->rspbuf.phys);
+ memset(&io->rspbuf, 0, sizeof(struct efc_dma));
+ }
+
+ kfree(io_pool);
+ efct->xport->io_pool = NULL;
+ }
+
+ return 0;
+}
+
+struct efct_io *
+efct_io_pool_io_alloc(struct efct_io_pool *io_pool)
+{
+ struct efct_io *io = NULL;
+ struct efct *efct;
+ unsigned long flags = 0;
+
+ efct = io_pool->efct;
+
+ spin_lock_irqsave(&io_pool->lock, flags);
+
+ if (!list_empty(&io_pool->freelist)) {
+ io = list_first_entry(&io_pool->freelist, struct efct_io,
+ list_entry);
+ list_del_init(&io->list_entry);
+ }
+
+ spin_unlock_irqrestore(&io_pool->lock, flags);
+
+ if (!io)
+ return NULL;
+
+ io->io_type = EFCT_IO_TYPE_MAX;
+ io->hio_type = EFCT_HW_IO_MAX;
+ io->hio = NULL;
+ io->transferred = 0;
+ io->efct = efct;
+ io->timeout = 0;
+ io->sgl_count = 0;
+ io->tgt_task_tag = 0;
+ io->init_task_tag = 0;
+ io->hw_tag = 0;
+ io->display_name = "pending";
+ io->seq_init = 0;
+ io->io_free = 0;
+ io->release = NULL;
+ atomic_add_return(1, &efct->xport->io_active_count);
+ atomic_add_return(1, &efct->xport->io_total_alloc);
+ return io;
+}
+
+/* Free an object used to track an IO */
+void
+efct_io_pool_io_free(struct efct_io_pool *io_pool, struct efct_io *io)
+{
+ struct efct *efct;
+ struct efct_hw_io *hio = NULL;
+ unsigned long flags = 0;
+
+ efct = io_pool->efct;
+
+ spin_lock_irqsave(&io_pool->lock, flags);
+ hio = io->hio;
+ io->hio = NULL;
+ io->io_free = 1;
+ INIT_LIST_HEAD(&io->list_entry);
+ list_add(&io->list_entry, &io_pool->freelist);
+ spin_unlock_irqrestore(&io_pool->lock, flags);
+
+ if (hio)
+ efct_hw_io_free(&efct->hw, hio);
+
+ atomic_sub_return(1, &efct->xport->io_active_count);
+ atomic_add_return(1, &efct->xport->io_total_free);
+}
+
+/* Find an I/O given it's node and ox_id */
+struct efct_io *
+efct_io_find_tgt_io(struct efct *efct, struct efct_node *node,
+ u16 ox_id, u16 rx_id)
+{
+ struct efct_io *io = NULL;
+ unsigned long flags = 0;
+ u8 found = false;
+
+ spin_lock_irqsave(&node->active_ios_lock, flags);
+ list_for_each_entry(io, &node->active_ios, list_entry) {
+ if ((io->cmd_tgt && io->init_task_tag == ox_id) &&
+ (rx_id == 0xffff || io->tgt_task_tag == rx_id)) {
+ if (kref_get_unless_zero(&io->ref))
+ found = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&node->active_ios_lock, flags);
+ return found ? io : NULL;
+}
diff --git a/drivers/scsi/elx/efct/efct_io.h b/drivers/scsi/elx/efct/efct_io.h
new file mode 100644
index 000000000000..bb0f51811a7c
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_io.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#if !defined(__EFCT_IO_H__)
+#define __EFCT_IO_H__
+
+#include "efct_lio.h"
+
+#define EFCT_LOG_ENABLE_IO_ERRORS(efct) \
+ (((efct) != NULL) ? (((efct)->logmask & (1U << 6)) != 0) : 0)
+
+#define io_error_log(io, fmt, ...) \
+ do { \
+ if (EFCT_LOG_ENABLE_IO_ERRORS(io->efct)) \
+ efc_log_warn(io->efct, fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define SCSI_CMD_BUF_LENGTH 48
+#define SCSI_RSP_BUF_LENGTH (FCP_RESP_WITH_EXT + SCSI_SENSE_BUFFERSIZE)
+#define EFCT_NUM_SCSI_IOS 8192
+
+enum efct_io_type {
+ EFCT_IO_TYPE_IO = 0,
+ EFCT_IO_TYPE_ELS,
+ EFCT_IO_TYPE_CT,
+ EFCT_IO_TYPE_CT_RESP,
+ EFCT_IO_TYPE_BLS_RESP,
+ EFCT_IO_TYPE_ABORT,
+
+ EFCT_IO_TYPE_MAX,
+};
+
+enum efct_els_state {
+ EFCT_ELS_REQUEST = 0,
+ EFCT_ELS_REQUEST_DELAYED,
+ EFCT_ELS_REQUEST_DELAY_ABORT,
+ EFCT_ELS_REQ_ABORT,
+ EFCT_ELS_REQ_ABORTED,
+ EFCT_ELS_ABORT_IO_COMPL,
+};
+
+/**
+ * Scsi target IO object
+ * @efct: pointer back to efct
+ * @instance_index: unique instance index value
+ * @io: IO display name
+ * @node: pointer to node
+ * @list_entry: io list entry
+ * @io_pending_link: io pending list entry
+ * @ref: reference counter
+ * @release: release callback function
+ * @init_task_tag: initiator task tag (OX_ID) for back-end and SCSI logging
+ * @tgt_task_tag: target task tag (RX_ID) for back-end and SCSI logging
+ * @hw_tag: HW layer unique IO id
+ * @tag: unique IO identifier
+ * @sgl: SGL
+ * @sgl_allocated: Number of allocated SGEs
+ * @sgl_count: Number of SGEs in this SGL
+ * @tgt_io: backend target private IO data
+ * @exp_xfer_len: expected data transfer length, based on FC header
+ * @hw_priv: Declarations private to HW/SLI
+ * @io_type: indicates what this struct efct_io structure is used for
+ * @hio: hw io object
+ * @transferred: Number of bytes transferred
+ * @auto_resp: set if auto_trsp was set
+ * @low_latency: set if low latency request
+ * @wq_steering: selected WQ steering request
+ * @wq_class: selected WQ class if steering is class
+ * @xfer_req: transfer size for current request
+ * @scsi_tgt_cb: target callback function
+ * @scsi_tgt_cb_arg: target callback function argument
+ * @abort_cb: abort callback function
+ * @abort_cb_arg: abort callback function argument
+ * @bls_cb: BLS callback function
+ * @bls_cb_arg: BLS callback function argument
+ * @tmf_cmd: TMF command being processed
+ * @abort_rx_id: rx_id from the ABTS that initiated the command abort
+ * @cmd_tgt: True if this is a Target command
+ * @send_abts: when aborting, indicates ABTS is to be sent
+ * @cmd_ini: True if this is an Initiator command
+ * @seq_init: True if local node has sequence initiative
+ * @iparam: iparams for hw io send call
+ * @hio_type: HW IO type
+ * @wire_len: wire length
+ * @hw_cb: saved HW callback
+ * @io_to_abort: for abort handling, pointer to IO to abort
+ * @rspbuf: SCSI Response buffer
+ * @timeout: Timeout value in seconds for this IO
+ * @cs_ctl: CS_CTL priority for this IO
+ * @io_free: Is io object in freelist
+ * @app_id: application id
+ */
+struct efct_io {
+ struct efct *efct;
+ u32 instance_index;
+ const char *display_name;
+ struct efct_node *node;
+
+ struct list_head list_entry;
+ struct list_head io_pending_link;
+ struct kref ref;
+ void (*release)(struct kref *arg);
+ u32 init_task_tag;
+ u32 tgt_task_tag;
+ u32 hw_tag;
+ u32 tag;
+ struct efct_scsi_sgl *sgl;
+ u32 sgl_allocated;
+ u32 sgl_count;
+ struct efct_scsi_tgt_io tgt_io;
+ u32 exp_xfer_len;
+
+ void *hw_priv;
+
+ enum efct_io_type io_type;
+ struct efct_hw_io *hio;
+ size_t transferred;
+
+ bool auto_resp;
+ bool low_latency;
+ u8 wq_steering;
+ u8 wq_class;
+ u64 xfer_req;
+ efct_scsi_io_cb_t scsi_tgt_cb;
+ void *scsi_tgt_cb_arg;
+ efct_scsi_io_cb_t abort_cb;
+ void *abort_cb_arg;
+ efct_scsi_io_cb_t bls_cb;
+ void *bls_cb_arg;
+ enum efct_scsi_tmf_cmd tmf_cmd;
+ u16 abort_rx_id;
+
+ bool cmd_tgt;
+ bool send_abts;
+ bool cmd_ini;
+ bool seq_init;
+ union efct_hw_io_param_u iparam;
+ enum efct_hw_io_type hio_type;
+ u64 wire_len;
+ void *hw_cb;
+
+ struct efct_io *io_to_abort;
+
+ struct efc_dma rspbuf;
+ u32 timeout;
+ u8 cs_ctl;
+ u8 io_free;
+ u32 app_id;
+};
+
+struct efct_io_cb_arg {
+ int status;
+ int ext_status;
+ void *app;
+};
+
+struct efct_io_pool *
+efct_io_pool_create(struct efct *efct, u32 num_sgl);
+int
+efct_io_pool_free(struct efct_io_pool *io_pool);
+u32
+efct_io_pool_allocated(struct efct_io_pool *io_pool);
+
+struct efct_io *
+efct_io_pool_io_alloc(struct efct_io_pool *io_pool);
+void
+efct_io_pool_io_free(struct efct_io_pool *io_pool, struct efct_io *io);
+struct efct_io *
+efct_io_find_tgt_io(struct efct *efct, struct efct_node *node,
+ u16 ox_id, u16 rx_id);
+#endif /* __EFCT_IO_H__ */
diff --git a/drivers/scsi/elx/efct/efct_lio.c b/drivers/scsi/elx/efct/efct_lio.c
new file mode 100644
index 000000000000..bd3d489e56ae
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_lio.c
@@ -0,0 +1,1681 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include "efct_driver.h"
+#include "efct_lio.h"
+
+/*
+ * lio_wq is used to call the LIO backed during creation or deletion of
+ * sessions. This brings serialization to the session management as we create
+ * single threaded work queue.
+ */
+static struct workqueue_struct *lio_wq;
+
+static int
+efct_format_wwn(char *str, size_t len, const char *pre, u64 wwn)
+{
+ u8 a[8];
+
+ put_unaligned_be64(wwn, a);
+ return snprintf(str, len, "%s%8phC", pre, a);
+}
+
+static int
+efct_lio_parse_wwn(const char *name, u64 *wwp, u8 npiv)
+{
+ int num;
+ u8 b[8];
+
+ if (npiv) {
+ num = sscanf(name,
+ "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
+ &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6],
+ &b[7]);
+ } else {
+ num = sscanf(name,
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6],
+ &b[7]);
+ }
+
+ if (num != 8)
+ return -EINVAL;
+
+ *wwp = get_unaligned_be64(b);
+ return 0;
+}
+
+static int
+efct_lio_parse_npiv_wwn(const char *name, size_t size, u64 *wwpn, u64 *wwnn)
+{
+ unsigned int cnt = size;
+ int rc;
+
+ *wwpn = *wwnn = 0;
+ if (name[cnt - 1] == '\n' || name[cnt - 1] == 0)
+ cnt--;
+
+ /* validate we have enough characters for WWPN */
+ if ((cnt != (16 + 1 + 16)) || (name[16] != ':'))
+ return -EINVAL;
+
+ rc = efct_lio_parse_wwn(&name[0], wwpn, 1);
+ if (rc)
+ return rc;
+
+ rc = efct_lio_parse_wwn(&name[17], wwnn, 1);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static ssize_t
+efct_lio_tpg_enable_show(struct config_item *item, char *page)
+{
+ struct se_portal_group *se_tpg = to_tpg(item);
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return snprintf(page, PAGE_SIZE, "%d\n", tpg->enabled);
+}
+
+static ssize_t
+efct_lio_tpg_enable_store(struct config_item *item, const char *page,
+ size_t count)
+{
+ struct se_portal_group *se_tpg = to_tpg(item);
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+ struct efct *efct;
+ struct efc *efc;
+ unsigned long op;
+
+ if (!tpg->nport || !tpg->nport->efct) {
+ pr_err("%s: Unable to find EFCT device\n", __func__);
+ return -EINVAL;
+ }
+
+ efct = tpg->nport->efct;
+ efc = efct->efcport;
+
+ if (kstrtoul(page, 0, &op) < 0)
+ return -EINVAL;
+
+ if (op == 1) {
+ int ret;
+
+ tpg->enabled = true;
+ efc_log_debug(efct, "enable portal group %d\n", tpg->tpgt);
+
+ ret = efct_xport_control(efct->xport, EFCT_XPORT_PORT_ONLINE);
+ if (ret) {
+ efct->tgt_efct.lio_nport = NULL;
+ efc_log_debug(efct, "cannot bring port online\n");
+ return ret;
+ }
+ } else if (op == 0) {
+ efc_log_debug(efct, "disable portal group %d\n", tpg->tpgt);
+
+ if (efc->domain && efc->domain->nport)
+ efct_scsi_tgt_del_nport(efc, efc->domain->nport);
+
+ tpg->enabled = false;
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t
+efct_lio_npiv_tpg_enable_show(struct config_item *item, char *page)
+{
+ struct se_portal_group *se_tpg = to_tpg(item);
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return snprintf(page, PAGE_SIZE, "%d\n", tpg->enabled);
+}
+
+static ssize_t
+efct_lio_npiv_tpg_enable_store(struct config_item *item, const char *page,
+ size_t count)
+{
+ struct se_portal_group *se_tpg = to_tpg(item);
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+ struct efct_lio_vport *lio_vport = tpg->vport;
+ struct efct *efct;
+ struct efc *efc;
+ unsigned long op;
+
+ if (kstrtoul(page, 0, &op) < 0)
+ return -EINVAL;
+
+ if (!lio_vport) {
+ pr_err("Unable to find vport\n");
+ return -EINVAL;
+ }
+
+ efct = lio_vport->efct;
+ efc = efct->efcport;
+
+ if (op == 1) {
+ tpg->enabled = true;
+ efc_log_debug(efct, "enable portal group %d\n", tpg->tpgt);
+
+ if (efc->domain) {
+ int ret;
+
+ ret = efc_nport_vport_new(efc->domain,
+ lio_vport->npiv_wwpn,
+ lio_vport->npiv_wwnn,
+ U32_MAX, false, true,
+ NULL, NULL);
+ if (ret != 0) {
+ efc_log_err(efct, "Failed to create Vport\n");
+ return ret;
+ }
+ return count;
+ }
+
+ if (!(efc_vport_create_spec(efc, lio_vport->npiv_wwnn,
+ lio_vport->npiv_wwpn, U32_MAX,
+ false, true, NULL, NULL)))
+ return -ENOMEM;
+
+ } else if (op == 0) {
+ efc_log_debug(efct, "disable portal group %d\n", tpg->tpgt);
+
+ tpg->enabled = false;
+ /* only physical nport should exist, free lio_nport
+ * allocated in efct_lio_make_nport
+ */
+ if (efc->domain) {
+ efc_nport_vport_del(efct->efcport, efc->domain,
+ lio_vport->npiv_wwpn,
+ lio_vport->npiv_wwnn);
+ return count;
+ }
+ } else {
+ return -EINVAL;
+ }
+ return count;
+}
+
+static char *efct_lio_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->nport->wwpn_str;
+}
+
+static char *efct_lio_get_npiv_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->vport->wwpn_str;
+}
+
+static u16 efct_lio_get_tag(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpgt;
+}
+
+static u16 efct_lio_get_npiv_tag(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpgt;
+}
+
+static int efct_lio_check_demo_mode(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int efct_lio_check_demo_mode_cache(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int efct_lio_check_demo_write_protect(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpg_attrib.demo_mode_write_protect;
+}
+
+static int
+efct_lio_npiv_check_demo_write_protect(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpg_attrib.demo_mode_write_protect;
+}
+
+static int efct_lio_check_prod_write_protect(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpg_attrib.prod_mode_write_protect;
+}
+
+static int
+efct_lio_npiv_check_prod_write_protect(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpg_attrib.prod_mode_write_protect;
+}
+
+static int efct_lio_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct efct_scsi_tgt_io *ocp =
+ container_of(se_cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *io = container_of(ocp, struct efct_io, tgt_io);
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_CHK_STOP_FREE);
+ return target_put_sess_cmd(se_cmd);
+}
+
+static int
+efct_lio_abort_tgt_cb(struct efct_io *io,
+ enum efct_scsi_io_status scsi_status,
+ u32 flags, void *arg)
+{
+ efct_lio_io_printf(io, "Abort done, status:%d\n", scsi_status);
+ return 0;
+}
+
+static void
+efct_lio_aborted_task(struct se_cmd *se_cmd)
+{
+ struct efct_scsi_tgt_io *ocp =
+ container_of(se_cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *io = container_of(ocp, struct efct_io, tgt_io);
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_ABORTED_TASK);
+
+ if (ocp->rsp_sent)
+ return;
+
+ /* command has been aborted, cleanup here */
+ ocp->aborting = true;
+ ocp->err = EFCT_SCSI_STATUS_ABORTED;
+ /* terminate the exchange */
+ efct_scsi_tgt_abort_io(io, efct_lio_abort_tgt_cb, NULL);
+}
+
+static void efct_lio_release_cmd(struct se_cmd *se_cmd)
+{
+ struct efct_scsi_tgt_io *ocp =
+ container_of(se_cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *io = container_of(ocp, struct efct_io, tgt_io);
+ struct efct *efct = io->efct;
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_RELEASE_CMD);
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_CMPL_CMD);
+ efct_scsi_io_complete(io);
+ atomic_sub_return(1, &efct->tgt_efct.ios_in_use);
+}
+
+static void efct_lio_close_session(struct se_session *se_sess)
+{
+ struct efc_node *node = se_sess->fabric_sess_ptr;
+
+ pr_debug("se_sess=%p node=%p", se_sess, node);
+
+ if (!node) {
+ pr_debug("node is NULL");
+ return;
+ }
+
+ efc_node_post_shutdown(node, NULL);
+}
+
+static int efct_lio_get_cmd_state(struct se_cmd *cmd)
+{
+ struct efct_scsi_tgt_io *ocp =
+ container_of(cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *io = container_of(ocp, struct efct_io, tgt_io);
+
+ return io->tgt_io.state;
+}
+
+static int
+efct_lio_sg_map(struct efct_io *io)
+{
+ struct efct_scsi_tgt_io *ocp = &io->tgt_io;
+ struct se_cmd *cmd = &ocp->cmd;
+
+ ocp->seg_map_cnt = dma_map_sg(&io->efct->pci->dev, cmd->t_data_sg,
+ cmd->t_data_nents, cmd->data_direction);
+ if (ocp->seg_map_cnt == 0)
+ return -EFAULT;
+ return 0;
+}
+
+static void
+efct_lio_sg_unmap(struct efct_io *io)
+{
+ struct efct_scsi_tgt_io *ocp = &io->tgt_io;
+ struct se_cmd *cmd = &ocp->cmd;
+
+ if (WARN_ON(!ocp->seg_map_cnt || !cmd->t_data_sg))
+ return;
+
+ dma_unmap_sg(&io->efct->pci->dev, cmd->t_data_sg,
+ cmd->t_data_nents, cmd->data_direction);
+ ocp->seg_map_cnt = 0;
+}
+
+static int
+efct_lio_status_done(struct efct_io *io,
+ enum efct_scsi_io_status scsi_status,
+ u32 flags, void *arg)
+{
+ struct efct_scsi_tgt_io *ocp = &io->tgt_io;
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_RSP_DONE);
+ if (scsi_status != EFCT_SCSI_STATUS_GOOD) {
+ efct_lio_io_printf(io, "callback completed with error=%d\n",
+ scsi_status);
+ ocp->err = scsi_status;
+ }
+ if (ocp->seg_map_cnt)
+ efct_lio_sg_unmap(io);
+
+ efct_lio_io_printf(io, "status=%d, err=%d flags=0x%x, dir=%d\n",
+ scsi_status, ocp->err, flags, ocp->ddir);
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE);
+ transport_generic_free_cmd(&io->tgt_io.cmd, 0);
+ return 0;
+}
+
+static int
+efct_lio_datamove_done(struct efct_io *io, enum efct_scsi_io_status scsi_status,
+ u32 flags, void *arg);
+
+static int
+efct_lio_write_pending(struct se_cmd *cmd)
+{
+ struct efct_scsi_tgt_io *ocp =
+ container_of(cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *io = container_of(ocp, struct efct_io, tgt_io);
+ struct efct_scsi_sgl *sgl = io->sgl;
+ struct scatterlist *sg;
+ u32 flags = 0, cnt, curcnt;
+ u64 length = 0;
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_WRITE_PENDING);
+ efct_lio_io_printf(io, "trans_state=0x%x se_cmd_flags=0x%x\n",
+ cmd->transport_state, cmd->se_cmd_flags);
+
+ if (ocp->seg_cnt == 0) {
+ ocp->seg_cnt = cmd->t_data_nents;
+ ocp->cur_seg = 0;
+ if (efct_lio_sg_map(io)) {
+ efct_lio_io_printf(io, "efct_lio_sg_map failed\n");
+ return -EFAULT;
+ }
+ }
+ curcnt = (ocp->seg_map_cnt - ocp->cur_seg);
+ curcnt = (curcnt < io->sgl_allocated) ? curcnt : io->sgl_allocated;
+ /* find current sg */
+ for (cnt = 0, sg = cmd->t_data_sg; cnt < ocp->cur_seg; cnt++,
+ sg = sg_next(sg))
+ ;/* do nothing */
+
+ for (cnt = 0; cnt < curcnt; cnt++, sg = sg_next(sg)) {
+ sgl[cnt].addr = sg_dma_address(sg);
+ sgl[cnt].dif_addr = 0;
+ sgl[cnt].len = sg_dma_len(sg);
+ length += sgl[cnt].len;
+ ocp->cur_seg++;
+ }
+
+ if (ocp->cur_seg == ocp->seg_cnt)
+ flags = EFCT_SCSI_LAST_DATAPHASE;
+
+ return efct_scsi_recv_wr_data(io, flags, sgl, curcnt, length,
+ efct_lio_datamove_done, NULL);
+}
+
+static int
+efct_lio_queue_data_in(struct se_cmd *cmd)
+{
+ struct efct_scsi_tgt_io *ocp =
+ container_of(cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *io = container_of(ocp, struct efct_io, tgt_io);
+ struct efct_scsi_sgl *sgl = io->sgl;
+ struct scatterlist *sg = NULL;
+ uint flags = 0, cnt = 0, curcnt = 0;
+ u64 length = 0;
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_QUEUE_DATA_IN);
+
+ if (ocp->seg_cnt == 0) {
+ if (cmd->data_length) {
+ ocp->seg_cnt = cmd->t_data_nents;
+ ocp->cur_seg = 0;
+ if (efct_lio_sg_map(io)) {
+ efct_lio_io_printf(io,
+ "efct_lio_sg_map failed\n");
+ return -EAGAIN;
+ }
+ } else {
+ /* If command length is 0, send the response status */
+ struct efct_scsi_cmd_resp rsp;
+
+ memset(&rsp, 0, sizeof(rsp));
+ efct_lio_io_printf(io,
+ "cmd : %p length 0, send status\n",
+ cmd);
+ return efct_scsi_send_resp(io, 0, &rsp,
+ efct_lio_status_done, NULL);
+ }
+ }
+ curcnt = min(ocp->seg_map_cnt - ocp->cur_seg, io->sgl_allocated);
+
+ while (cnt < curcnt) {
+ sg = &cmd->t_data_sg[ocp->cur_seg];
+ sgl[cnt].addr = sg_dma_address(sg);
+ sgl[cnt].dif_addr = 0;
+ if (ocp->transferred_len + sg_dma_len(sg) >= cmd->data_length)
+ sgl[cnt].len = cmd->data_length - ocp->transferred_len;
+ else
+ sgl[cnt].len = sg_dma_len(sg);
+
+ ocp->transferred_len += sgl[cnt].len;
+ length += sgl[cnt].len;
+ ocp->cur_seg++;
+ cnt++;
+ if (ocp->transferred_len == cmd->data_length)
+ break;
+ }
+
+ if (ocp->transferred_len == cmd->data_length) {
+ flags = EFCT_SCSI_LAST_DATAPHASE;
+ ocp->seg_cnt = ocp->cur_seg;
+ }
+
+ /* If there is residual, disable Auto Good Response */
+ if (cmd->residual_count)
+ flags |= EFCT_SCSI_NO_AUTO_RESPONSE;
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_SEND_RD_DATA);
+
+ return efct_scsi_send_rd_data(io, flags, sgl, curcnt, length,
+ efct_lio_datamove_done, NULL);
+}
+
+static void
+efct_lio_send_resp(struct efct_io *io, enum efct_scsi_io_status scsi_status,
+ u32 flags)
+{
+ struct efct_scsi_cmd_resp rsp;
+ struct efct_scsi_tgt_io *ocp = &io->tgt_io;
+ struct se_cmd *cmd = &io->tgt_io.cmd;
+ int rc;
+
+ if (flags & EFCT_SCSI_IO_CMPL_RSP_SENT) {
+ ocp->rsp_sent = true;
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE);
+ transport_generic_free_cmd(&io->tgt_io.cmd, 0);
+ return;
+ }
+
+ /* send check condition if an error occurred */
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.scsi_status = cmd->scsi_status;
+ rsp.sense_data = (uint8_t *)io->tgt_io.sense_buffer;
+ rsp.sense_data_length = cmd->scsi_sense_length;
+
+ /* Check for residual underrun or overrun */
+ if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+ rsp.residual = -cmd->residual_count;
+ else if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)
+ rsp.residual = cmd->residual_count;
+
+ rc = efct_scsi_send_resp(io, 0, &rsp, efct_lio_status_done, NULL);
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_SEND_RSP);
+ if (rc != 0) {
+ efct_lio_io_printf(io, "Read done, send rsp failed %d\n", rc);
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE);
+ transport_generic_free_cmd(&io->tgt_io.cmd, 0);
+ } else {
+ ocp->rsp_sent = true;
+ }
+}
+
+static int
+efct_lio_datamove_done(struct efct_io *io, enum efct_scsi_io_status scsi_status,
+ u32 flags, void *arg)
+{
+ struct efct_scsi_tgt_io *ocp = &io->tgt_io;
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_DATA_DONE);
+ if (scsi_status != EFCT_SCSI_STATUS_GOOD) {
+ efct_lio_io_printf(io, "callback completed with error=%d\n",
+ scsi_status);
+ ocp->err = scsi_status;
+ }
+ efct_lio_io_printf(io, "seg_map_cnt=%d\n", ocp->seg_map_cnt);
+ if (ocp->seg_map_cnt) {
+ if (ocp->err == EFCT_SCSI_STATUS_GOOD &&
+ ocp->cur_seg < ocp->seg_cnt) {
+ int rc;
+
+ efct_lio_io_printf(io, "continuing cmd at segm=%d\n",
+ ocp->cur_seg);
+ if (ocp->ddir == DMA_TO_DEVICE)
+ rc = efct_lio_write_pending(&ocp->cmd);
+ else
+ rc = efct_lio_queue_data_in(&ocp->cmd);
+ if (!rc)
+ return 0;
+
+ ocp->err = EFCT_SCSI_STATUS_ERROR;
+ efct_lio_io_printf(io, "could not continue command\n");
+ }
+ efct_lio_sg_unmap(io);
+ }
+
+ if (io->tgt_io.aborting) {
+ efct_lio_io_printf(io, "IO done aborted\n");
+ return 0;
+ }
+
+ if (ocp->ddir == DMA_TO_DEVICE) {
+ efct_lio_io_printf(io, "Write done, trans_state=0x%x\n",
+ io->tgt_io.cmd.transport_state);
+ if (scsi_status != EFCT_SCSI_STATUS_GOOD) {
+ transport_generic_request_failure(&io->tgt_io.cmd,
+ TCM_CHECK_CONDITION_ABORT_CMD);
+ efct_set_lio_io_state(io,
+ EFCT_LIO_STATE_TGT_GENERIC_REQ_FAILURE);
+ } else {
+ efct_set_lio_io_state(io,
+ EFCT_LIO_STATE_TGT_EXECUTE_CMD);
+ target_execute_cmd(&io->tgt_io.cmd);
+ }
+ } else {
+ efct_lio_send_resp(io, scsi_status, flags);
+ }
+ return 0;
+}
+
+static int
+efct_lio_tmf_done(struct efct_io *io, enum efct_scsi_io_status scsi_status,
+ u32 flags, void *arg)
+{
+ efct_lio_tmfio_printf(io, "cmd=%p status=%d, flags=0x%x\n",
+ &io->tgt_io.cmd, scsi_status, flags);
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE);
+ transport_generic_free_cmd(&io->tgt_io.cmd, 0);
+ return 0;
+}
+
+static int
+efct_lio_null_tmf_done(struct efct_io *tmfio,
+ enum efct_scsi_io_status scsi_status,
+ u32 flags, void *arg)
+{
+ efct_lio_tmfio_printf(tmfio, "cmd=%p status=%d, flags=0x%x\n",
+ &tmfio->tgt_io.cmd, scsi_status, flags);
+
+ /* free struct efct_io only, no active se_cmd */
+ efct_scsi_io_complete(tmfio);
+ return 0;
+}
+
+static int
+efct_lio_queue_status(struct se_cmd *cmd)
+{
+ struct efct_scsi_cmd_resp rsp;
+ struct efct_scsi_tgt_io *ocp =
+ container_of(cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *io = container_of(ocp, struct efct_io, tgt_io);
+ int rc = 0;
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_QUEUE_STATUS);
+ efct_lio_io_printf(io,
+ "status=0x%x trans_state=0x%x se_cmd_flags=0x%x sns_len=%d\n",
+ cmd->scsi_status, cmd->transport_state, cmd->se_cmd_flags,
+ cmd->scsi_sense_length);
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.scsi_status = cmd->scsi_status;
+ rsp.sense_data = (u8 *)io->tgt_io.sense_buffer;
+ rsp.sense_data_length = cmd->scsi_sense_length;
+
+ /* Check for residual underrun or overrun, mark negitive value for
+ * underrun to recognize in HW
+ */
+ if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+ rsp.residual = -cmd->residual_count;
+ else if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)
+ rsp.residual = cmd->residual_count;
+
+ rc = efct_scsi_send_resp(io, 0, &rsp, efct_lio_status_done, NULL);
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_SEND_RSP);
+ if (rc == 0)
+ ocp->rsp_sent = true;
+ return rc;
+}
+
+static void efct_lio_queue_tm_rsp(struct se_cmd *cmd)
+{
+ struct efct_scsi_tgt_io *ocp =
+ container_of(cmd, struct efct_scsi_tgt_io, cmd);
+ struct efct_io *tmfio = container_of(ocp, struct efct_io, tgt_io);
+ struct se_tmr_req *se_tmr = cmd->se_tmr_req;
+ u8 rspcode;
+
+ efct_lio_tmfio_printf(tmfio, "cmd=%p function=0x%x tmr->response=%d\n",
+ cmd, se_tmr->function, se_tmr->response);
+ switch (se_tmr->response) {
+ case TMR_FUNCTION_COMPLETE:
+ rspcode = EFCT_SCSI_TMF_FUNCTION_COMPLETE;
+ break;
+ case TMR_TASK_DOES_NOT_EXIST:
+ rspcode = EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND;
+ break;
+ case TMR_LUN_DOES_NOT_EXIST:
+ rspcode = EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER;
+ break;
+ case TMR_FUNCTION_REJECTED:
+ default:
+ rspcode = EFCT_SCSI_TMF_FUNCTION_REJECTED;
+ break;
+ }
+ efct_scsi_send_tmf_resp(tmfio, rspcode, NULL, efct_lio_tmf_done, NULL);
+}
+
+static struct efct *efct_find_wwpn(u64 wwpn)
+{
+ struct efct *efct;
+
+ /* Search for the HBA that has this WWPN */
+ list_for_each_entry(efct, &efct_devices, list_entry) {
+
+ if (wwpn == efct_get_wwpn(&efct->hw))
+ return efct;
+ }
+
+ return NULL;
+}
+
+static struct se_wwn *
+efct_lio_make_nport(struct target_fabric_configfs *tf,
+ struct config_group *group, const char *name)
+{
+ struct efct_lio_nport *lio_nport;
+ struct efct *efct;
+ int ret;
+ u64 wwpn;
+
+ ret = efct_lio_parse_wwn(name, &wwpn, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ efct = efct_find_wwpn(wwpn);
+ if (!efct) {
+ pr_err("cannot find EFCT for base wwpn %s\n", name);
+ return ERR_PTR(-ENXIO);
+ }
+
+ lio_nport = kzalloc(sizeof(*lio_nport), GFP_KERNEL);
+ if (!lio_nport)
+ return ERR_PTR(-ENOMEM);
+
+ lio_nport->efct = efct;
+ lio_nport->wwpn = wwpn;
+ efct_format_wwn(lio_nport->wwpn_str, sizeof(lio_nport->wwpn_str),
+ "naa.", wwpn);
+ efct->tgt_efct.lio_nport = lio_nport;
+
+ return &lio_nport->nport_wwn;
+}
+
+static struct se_wwn *
+efct_lio_npiv_make_nport(struct target_fabric_configfs *tf,
+ struct config_group *group, const char *name)
+{
+ struct efct_lio_vport *lio_vport;
+ struct efct *efct;
+ int ret;
+ u64 p_wwpn, npiv_wwpn, npiv_wwnn;
+ char *p, *pbuf, tmp[128];
+ struct efct_lio_vport_list_t *vport_list;
+ struct fc_vport *new_fc_vport;
+ struct fc_vport_identifiers vport_id;
+ unsigned long flags = 0;
+
+ snprintf(tmp, sizeof(tmp), "%s", name);
+ pbuf = &tmp[0];
+
+ p = strsep(&pbuf, "@");
+
+ if (!p || !pbuf) {
+ pr_err("Unable to find separator operator(@)\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = efct_lio_parse_wwn(p, &p_wwpn, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = efct_lio_parse_npiv_wwn(pbuf, strlen(pbuf), &npiv_wwpn,
+ &npiv_wwnn);
+ if (ret)
+ return ERR_PTR(ret);
+
+ efct = efct_find_wwpn(p_wwpn);
+ if (!efct) {
+ pr_err("cannot find EFCT for base wwpn %s\n", name);
+ return ERR_PTR(-ENXIO);
+ }
+
+ lio_vport = kzalloc(sizeof(*lio_vport), GFP_KERNEL);
+ if (!lio_vport)
+ return ERR_PTR(-ENOMEM);
+
+ lio_vport->efct = efct;
+ lio_vport->wwpn = p_wwpn;
+ lio_vport->npiv_wwpn = npiv_wwpn;
+ lio_vport->npiv_wwnn = npiv_wwnn;
+
+ efct_format_wwn(lio_vport->wwpn_str, sizeof(lio_vport->wwpn_str),
+ "naa.", npiv_wwpn);
+
+ vport_list = kzalloc(sizeof(*vport_list), GFP_KERNEL);
+ if (!vport_list) {
+ kfree(lio_vport);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ vport_list->lio_vport = lio_vport;
+
+ memset(&vport_id, 0, sizeof(vport_id));
+ vport_id.port_name = npiv_wwpn;
+ vport_id.node_name = npiv_wwnn;
+ vport_id.roles = FC_PORT_ROLE_FCP_INITIATOR;
+ vport_id.vport_type = FC_PORTTYPE_NPIV;
+ vport_id.disable = false;
+
+ new_fc_vport = fc_vport_create(efct->shost, 0, &vport_id);
+ if (!new_fc_vport) {
+ efc_log_err(efct, "fc_vport_create failed\n");
+ kfree(lio_vport);
+ kfree(vport_list);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ lio_vport->fc_vport = new_fc_vport;
+ spin_lock_irqsave(&efct->tgt_efct.efct_lio_lock, flags);
+ INIT_LIST_HEAD(&vport_list->list_entry);
+ list_add_tail(&vport_list->list_entry, &efct->tgt_efct.vport_list);
+ spin_unlock_irqrestore(&efct->tgt_efct.efct_lio_lock, flags);
+
+ return &lio_vport->vport_wwn;
+}
+
+static void
+efct_lio_drop_nport(struct se_wwn *wwn)
+{
+ struct efct_lio_nport *lio_nport =
+ container_of(wwn, struct efct_lio_nport, nport_wwn);
+ struct efct *efct = lio_nport->efct;
+
+ /* only physical nport should exist, free lio_nport allocated
+ * in efct_lio_make_nport.
+ */
+ kfree(efct->tgt_efct.lio_nport);
+ efct->tgt_efct.lio_nport = NULL;
+}
+
+static void
+efct_lio_npiv_drop_nport(struct se_wwn *wwn)
+{
+ struct efct_lio_vport *lio_vport =
+ container_of(wwn, struct efct_lio_vport, vport_wwn);
+ struct efct_lio_vport_list_t *vport, *next_vport;
+ struct efct *efct = lio_vport->efct;
+ unsigned long flags = 0;
+
+ if (lio_vport->fc_vport)
+ fc_vport_terminate(lio_vport->fc_vport);
+
+ spin_lock_irqsave(&efct->tgt_efct.efct_lio_lock, flags);
+
+ list_for_each_entry_safe(vport, next_vport, &efct->tgt_efct.vport_list,
+ list_entry) {
+ if (vport->lio_vport == lio_vport) {
+ list_del(&vport->list_entry);
+ kfree(vport->lio_vport);
+ kfree(vport);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&efct->tgt_efct.efct_lio_lock, flags);
+}
+
+static struct se_portal_group *
+efct_lio_make_tpg(struct se_wwn *wwn, const char *name)
+{
+ struct efct_lio_nport *lio_nport =
+ container_of(wwn, struct efct_lio_nport, nport_wwn);
+ struct efct_lio_tpg *tpg;
+ struct efct *efct;
+ unsigned long n;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &n) || n > USHRT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg)
+ return ERR_PTR(-ENOMEM);
+
+ tpg->nport = lio_nport;
+ tpg->tpgt = n;
+ tpg->enabled = false;
+
+ tpg->tpg_attrib.generate_node_acls = 1;
+ tpg->tpg_attrib.demo_mode_write_protect = 1;
+ tpg->tpg_attrib.cache_dynamic_acls = 1;
+ tpg->tpg_attrib.demo_mode_login_only = 1;
+ tpg->tpg_attrib.session_deletion_wait = 1;
+
+ ret = core_tpg_register(wwn, &tpg->tpg, SCSI_PROTOCOL_FCP);
+ if (ret < 0) {
+ kfree(tpg);
+ return NULL;
+ }
+ efct = lio_nport->efct;
+ efct->tgt_efct.tpg = tpg;
+ efc_log_debug(efct, "create portal group %d\n", tpg->tpgt);
+
+ xa_init(&efct->lookup);
+ return &tpg->tpg;
+}
+
+static void
+efct_lio_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ struct efct *efct = tpg->nport->efct;
+
+ efc_log_debug(efct, "drop portal group %d\n", tpg->tpgt);
+ tpg->nport->efct->tgt_efct.tpg = NULL;
+ core_tpg_deregister(se_tpg);
+ xa_destroy(&efct->lookup);
+ kfree(tpg);
+}
+
+static struct se_portal_group *
+efct_lio_npiv_make_tpg(struct se_wwn *wwn, const char *name)
+{
+ struct efct_lio_vport *lio_vport =
+ container_of(wwn, struct efct_lio_vport, vport_wwn);
+ struct efct_lio_tpg *tpg;
+ struct efct *efct;
+ unsigned long n;
+ int ret;
+
+ efct = lio_vport->efct;
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &n) || n > USHRT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ if (n != 1) {
+ efc_log_err(efct, "Invalid tpgt index: %ld provided\n", n);
+ return ERR_PTR(-EINVAL);
+ }
+
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg)
+ return ERR_PTR(-ENOMEM);
+
+ tpg->vport = lio_vport;
+ tpg->tpgt = n;
+ tpg->enabled = false;
+
+ tpg->tpg_attrib.generate_node_acls = 1;
+ tpg->tpg_attrib.demo_mode_write_protect = 1;
+ tpg->tpg_attrib.cache_dynamic_acls = 1;
+ tpg->tpg_attrib.demo_mode_login_only = 1;
+ tpg->tpg_attrib.session_deletion_wait = 1;
+
+ ret = core_tpg_register(wwn, &tpg->tpg, SCSI_PROTOCOL_FCP);
+
+ if (ret < 0) {
+ kfree(tpg);
+ return NULL;
+ }
+ lio_vport->tpg = tpg;
+ efc_log_debug(efct, "create vport portal group %d\n", tpg->tpgt);
+
+ return &tpg->tpg;
+}
+
+static void
+efct_lio_npiv_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct efct_lio_tpg *tpg =
+ container_of(se_tpg, struct efct_lio_tpg, tpg);
+
+ efc_log_debug(tpg->vport->efct, "drop npiv portal group %d\n",
+ tpg->tpgt);
+ core_tpg_deregister(se_tpg);
+ kfree(tpg);
+}
+
+static int
+efct_lio_init_nodeacl(struct se_node_acl *se_nacl, const char *name)
+{
+ struct efct_lio_nacl *nacl;
+ u64 wwnn;
+
+ if (efct_lio_parse_wwn(name, &wwnn, 0) < 0)
+ return -EINVAL;
+
+ nacl = container_of(se_nacl, struct efct_lio_nacl, se_node_acl);
+ nacl->nport_wwnn = wwnn;
+
+ efct_format_wwn(nacl->nport_name, sizeof(nacl->nport_name), "", wwnn);
+ return 0;
+}
+
+static int efct_lio_check_demo_mode_login_only(struct se_portal_group *stpg)
+{
+ struct efct_lio_tpg *tpg = container_of(stpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpg_attrib.demo_mode_login_only;
+}
+
+static int
+efct_lio_npiv_check_demo_mode_login_only(struct se_portal_group *stpg)
+{
+ struct efct_lio_tpg *tpg = container_of(stpg, struct efct_lio_tpg, tpg);
+
+ return tpg->tpg_attrib.demo_mode_login_only;
+}
+
+static struct efct_lio_tpg *
+efct_get_vport_tpg(struct efc_node *node)
+{
+ struct efct *efct;
+ u64 wwpn = node->nport->wwpn;
+ struct efct_lio_vport_list_t *vport, *next;
+ struct efct_lio_vport *lio_vport = NULL;
+ struct efct_lio_tpg *tpg = NULL;
+ unsigned long flags = 0;
+
+ efct = node->efc->base;
+ spin_lock_irqsave(&efct->tgt_efct.efct_lio_lock, flags);
+ list_for_each_entry_safe(vport, next, &efct->tgt_efct.vport_list,
+ list_entry) {
+ lio_vport = vport->lio_vport;
+ if (wwpn && lio_vport && lio_vport->npiv_wwpn == wwpn) {
+ efc_log_debug(efct, "found tpg on vport\n");
+ tpg = lio_vport->tpg;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&efct->tgt_efct.efct_lio_lock, flags);
+ return tpg;
+}
+
+static void
+_efct_tgt_node_free(struct kref *arg)
+{
+ struct efct_node *tgt_node = container_of(arg, struct efct_node, ref);
+ struct efc_node *node = tgt_node->node;
+
+ efc_scsi_del_initiator_complete(node->efc, node);
+ kfree(tgt_node);
+}
+
+static int efct_session_cb(struct se_portal_group *se_tpg,
+ struct se_session *se_sess, void *private)
+{
+ struct efc_node *node = private;
+ struct efct_node *tgt_node;
+ struct efct *efct = node->efc->base;
+
+ tgt_node = kzalloc(sizeof(*tgt_node), GFP_KERNEL);
+ if (!tgt_node)
+ return -ENOMEM;
+
+ kref_init(&tgt_node->ref);
+ tgt_node->release = _efct_tgt_node_free;
+
+ tgt_node->session = se_sess;
+ node->tgt_node = tgt_node;
+ tgt_node->efct = efct;
+
+ tgt_node->node = node;
+
+ tgt_node->node_fc_id = node->rnode.fc_id;
+ tgt_node->port_fc_id = node->nport->fc_id;
+ tgt_node->vpi = node->nport->indicator;
+ tgt_node->rpi = node->rnode.indicator;
+
+ spin_lock_init(&tgt_node->active_ios_lock);
+ INIT_LIST_HEAD(&tgt_node->active_ios);
+
+ return 0;
+}
+
+int efct_scsi_tgt_new_device(struct efct *efct)
+{
+ u32 total_ios;
+
+ /* Get the max settings */
+ efct->tgt_efct.max_sge = sli_get_max_sge(&efct->hw.sli);
+ efct->tgt_efct.max_sgl = sli_get_max_sgl(&efct->hw.sli);
+
+ /* initialize IO watermark fields */
+ atomic_set(&efct->tgt_efct.ios_in_use, 0);
+ total_ios = efct->hw.config.n_io;
+ efc_log_debug(efct, "total_ios=%d\n", total_ios);
+ efct->tgt_efct.watermark_min =
+ (total_ios * EFCT_WATERMARK_LOW_PCT) / 100;
+ efct->tgt_efct.watermark_max =
+ (total_ios * EFCT_WATERMARK_HIGH_PCT) / 100;
+ atomic_set(&efct->tgt_efct.io_high_watermark,
+ efct->tgt_efct.watermark_max);
+ atomic_set(&efct->tgt_efct.watermark_hit, 0);
+ atomic_set(&efct->tgt_efct.initiator_count, 0);
+
+ lio_wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM,
+ "efct_lio_worker");
+ if (!lio_wq) {
+ efc_log_err(efct, "workqueue create failed\n");
+ return -EIO;
+ }
+
+ spin_lock_init(&efct->tgt_efct.efct_lio_lock);
+ INIT_LIST_HEAD(&efct->tgt_efct.vport_list);
+
+ return 0;
+}
+
+int efct_scsi_tgt_del_device(struct efct *efct)
+{
+ flush_workqueue(lio_wq);
+
+ return 0;
+}
+
+int
+efct_scsi_tgt_new_nport(struct efc *efc, struct efc_nport *nport)
+{
+ struct efct *efct = nport->efc->base;
+
+ efc_log_debug(efct, "New SPORT: %s bound to %s\n", nport->display_name,
+ efct->tgt_efct.lio_nport->wwpn_str);
+
+ return 0;
+}
+
+void
+efct_scsi_tgt_del_nport(struct efc *efc, struct efc_nport *nport)
+{
+ efc_log_debug(efc, "Del SPORT: %s\n", nport->display_name);
+}
+
+static void efct_lio_setup_session(struct work_struct *work)
+{
+ struct efct_lio_wq_data *wq_data =
+ container_of(work, struct efct_lio_wq_data, work);
+ struct efct *efct = wq_data->efct;
+ struct efc_node *node = wq_data->ptr;
+ char wwpn[WWN_NAME_LEN];
+ struct efct_lio_tpg *tpg;
+ struct efct_node *tgt_node;
+ struct se_portal_group *se_tpg;
+ struct se_session *se_sess;
+ int watermark;
+ int ini_count;
+ u64 id;
+
+ /* Check to see if it's belongs to vport,
+ * if not get physical port
+ */
+ tpg = efct_get_vport_tpg(node);
+ if (tpg) {
+ se_tpg = &tpg->tpg;
+ } else if (efct->tgt_efct.tpg) {
+ tpg = efct->tgt_efct.tpg;
+ se_tpg = &tpg->tpg;
+ } else {
+ efc_log_err(efct, "failed to init session\n");
+ return;
+ }
+
+ /*
+ * Format the FCP Initiator port_name into colon
+ * separated values to match the format by our explicit
+ * ConfigFS NodeACLs.
+ */
+ efct_format_wwn(wwpn, sizeof(wwpn), "", efc_node_get_wwpn(node));
+
+ se_sess = target_setup_session(se_tpg, 0, 0, TARGET_PROT_NORMAL, wwpn,
+ node, efct_session_cb);
+ if (IS_ERR(se_sess)) {
+ efc_log_err(efct, "failed to setup session\n");
+ kfree(wq_data);
+ efc_scsi_sess_reg_complete(node, -EIO);
+ return;
+ }
+
+ tgt_node = node->tgt_node;
+ id = (u64) tgt_node->port_fc_id << 32 | tgt_node->node_fc_id;
+
+ efc_log_debug(efct, "new initiator sess=%p node=%p id: %llx\n",
+ se_sess, node, id);
+
+ if (xa_err(xa_store(&efct->lookup, id, tgt_node, GFP_KERNEL)))
+ efc_log_err(efct, "Node lookup store failed\n");
+
+ efc_scsi_sess_reg_complete(node, 0);
+
+ /* update IO watermark: increment initiator count */
+ ini_count = atomic_add_return(1, &efct->tgt_efct.initiator_count);
+ watermark = efct->tgt_efct.watermark_max -
+ ini_count * EFCT_IO_WATERMARK_PER_INITIATOR;
+ watermark = (efct->tgt_efct.watermark_min > watermark) ?
+ efct->tgt_efct.watermark_min : watermark;
+ atomic_set(&efct->tgt_efct.io_high_watermark, watermark);
+
+ kfree(wq_data);
+}
+
+int efct_scsi_new_initiator(struct efc *efc, struct efc_node *node)
+{
+ struct efct *efct = node->efc->base;
+ struct efct_lio_wq_data *wq_data;
+
+ /*
+ * Since LIO only supports initiator validation at thread level,
+ * we are open minded and accept all callers.
+ */
+ wq_data = kzalloc(sizeof(*wq_data), GFP_ATOMIC);
+ if (!wq_data)
+ return -ENOMEM;
+
+ wq_data->ptr = node;
+ wq_data->efct = efct;
+ INIT_WORK(&wq_data->work, efct_lio_setup_session);
+ queue_work(lio_wq, &wq_data->work);
+ return EFC_SCSI_CALL_ASYNC;
+}
+
+static void efct_lio_remove_session(struct work_struct *work)
+{
+ struct efct_lio_wq_data *wq_data =
+ container_of(work, struct efct_lio_wq_data, work);
+ struct efct *efct = wq_data->efct;
+ struct efc_node *node = wq_data->ptr;
+ struct efct_node *tgt_node;
+ struct se_session *se_sess;
+
+ tgt_node = node->tgt_node;
+ if (!tgt_node) {
+ /* base driver has sent back-to-back requests
+ * to unreg session with no intervening
+ * register
+ */
+ efc_log_err(efct, "unreg session for NULL session\n");
+ efc_scsi_del_initiator_complete(node->efc, node);
+ return;
+ }
+
+ se_sess = tgt_node->session;
+ efc_log_debug(efct, "unreg session se_sess=%p node=%p\n",
+ se_sess, node);
+
+ /* first flag all session commands to complete */
+ target_stop_session(se_sess);
+
+ /* now wait for session commands to complete */
+ target_wait_for_sess_cmds(se_sess);
+ target_remove_session(se_sess);
+ tgt_node->session = NULL;
+ node->tgt_node = NULL;
+ kref_put(&tgt_node->ref, tgt_node->release);
+
+ kfree(wq_data);
+}
+
+int efct_scsi_del_initiator(struct efc *efc, struct efc_node *node, int reason)
+{
+ struct efct *efct = node->efc->base;
+ struct efct_node *tgt_node = node->tgt_node;
+ struct efct_lio_wq_data *wq_data;
+ int watermark;
+ int ini_count;
+ u64 id;
+
+ if (reason == EFCT_SCSI_INITIATOR_MISSING)
+ return EFC_SCSI_CALL_COMPLETE;
+
+ if (!tgt_node) {
+ efc_log_err(efct, "tgt_node is NULL\n");
+ return -EIO;
+ }
+
+ wq_data = kzalloc(sizeof(*wq_data), GFP_ATOMIC);
+ if (!wq_data)
+ return -ENOMEM;
+
+ id = (u64) tgt_node->port_fc_id << 32 | tgt_node->node_fc_id;
+ xa_erase(&efct->lookup, id);
+
+ wq_data->ptr = node;
+ wq_data->efct = efct;
+ INIT_WORK(&wq_data->work, efct_lio_remove_session);
+ queue_work(lio_wq, &wq_data->work);
+
+ /*
+ * update IO watermark: decrement initiator count
+ */
+ ini_count = atomic_sub_return(1, &efct->tgt_efct.initiator_count);
+
+ watermark = efct->tgt_efct.watermark_max -
+ ini_count * EFCT_IO_WATERMARK_PER_INITIATOR;
+ watermark = (efct->tgt_efct.watermark_min > watermark) ?
+ efct->tgt_efct.watermark_min : watermark;
+ atomic_set(&efct->tgt_efct.io_high_watermark, watermark);
+
+ return EFC_SCSI_CALL_ASYNC;
+}
+
+void efct_scsi_recv_cmd(struct efct_io *io, uint64_t lun, u8 *cdb,
+ u32 cdb_len, u32 flags)
+{
+ struct efct_scsi_tgt_io *ocp = &io->tgt_io;
+ struct se_cmd *se_cmd = &io->tgt_io.cmd;
+ struct efct *efct = io->efct;
+ char *ddir;
+ struct efct_node *tgt_node;
+ struct se_session *se_sess;
+ int rc = 0;
+
+ memset(ocp, 0, sizeof(struct efct_scsi_tgt_io));
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_RECV_CMD);
+ atomic_add_return(1, &efct->tgt_efct.ios_in_use);
+
+ /* set target timeout */
+ io->timeout = efct->target_io_timer_sec;
+
+ if (flags & EFCT_SCSI_CMD_SIMPLE)
+ ocp->task_attr = TCM_SIMPLE_TAG;
+ else if (flags & EFCT_SCSI_CMD_HEAD_OF_QUEUE)
+ ocp->task_attr = TCM_HEAD_TAG;
+ else if (flags & EFCT_SCSI_CMD_ORDERED)
+ ocp->task_attr = TCM_ORDERED_TAG;
+ else if (flags & EFCT_SCSI_CMD_ACA)
+ ocp->task_attr = TCM_ACA_TAG;
+
+ switch (flags & (EFCT_SCSI_CMD_DIR_IN | EFCT_SCSI_CMD_DIR_OUT)) {
+ case EFCT_SCSI_CMD_DIR_IN:
+ ddir = "FROM_INITIATOR";
+ ocp->ddir = DMA_TO_DEVICE;
+ break;
+ case EFCT_SCSI_CMD_DIR_OUT:
+ ddir = "TO_INITIATOR";
+ ocp->ddir = DMA_FROM_DEVICE;
+ break;
+ case EFCT_SCSI_CMD_DIR_IN | EFCT_SCSI_CMD_DIR_OUT:
+ ddir = "BIDIR";
+ ocp->ddir = DMA_BIDIRECTIONAL;
+ break;
+ default:
+ ddir = "NONE";
+ ocp->ddir = DMA_NONE;
+ break;
+ }
+
+ ocp->lun = lun;
+ efct_lio_io_printf(io, "new cmd=0x%x ddir=%s dl=%u\n",
+ cdb[0], ddir, io->exp_xfer_len);
+
+ tgt_node = io->node;
+ se_sess = tgt_node->session;
+ if (!se_sess) {
+ efc_log_err(efct, "No session found to submit IO se_cmd: %p\n",
+ &ocp->cmd);
+ efct_scsi_io_free(io);
+ return;
+ }
+
+ efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_SUBMIT_CMD);
+ rc = target_init_cmd(se_cmd, se_sess, &io->tgt_io.sense_buffer[0],
+ ocp->lun, io->exp_xfer_len, ocp->task_attr,
+ ocp->ddir, TARGET_SCF_ACK_KREF);
+ if (rc) {
+ efc_log_err(efct, "failed to init cmd se_cmd: %p\n", se_cmd);
+ efct_scsi_io_free(io);
+ return;
+ }
+
+ if (target_submit_prep(se_cmd, cdb, NULL, 0, NULL, 0,
+ NULL, 0, GFP_ATOMIC))
+ return;
+
+ target_submit(se_cmd);
+}
+
+int
+efct_scsi_recv_tmf(struct efct_io *tmfio, u32 lun, enum efct_scsi_tmf_cmd cmd,
+ struct efct_io *io_to_abort, u32 flags)
+{
+ unsigned char tmr_func;
+ struct efct *efct = tmfio->efct;
+ struct efct_scsi_tgt_io *ocp = &tmfio->tgt_io;
+ struct efct_node *tgt_node;
+ struct se_session *se_sess;
+ int rc;
+
+ memset(ocp, 0, sizeof(struct efct_scsi_tgt_io));
+ efct_set_lio_io_state(tmfio, EFCT_LIO_STATE_SCSI_RECV_TMF);
+ atomic_add_return(1, &efct->tgt_efct.ios_in_use);
+ efct_lio_tmfio_printf(tmfio, "%s: new tmf %x lun=%u\n",
+ tmfio->display_name, cmd, lun);
+
+ switch (cmd) {
+ case EFCT_SCSI_TMF_ABORT_TASK:
+ tmr_func = TMR_ABORT_TASK;
+ break;
+ case EFCT_SCSI_TMF_ABORT_TASK_SET:
+ tmr_func = TMR_ABORT_TASK_SET;
+ break;
+ case EFCT_SCSI_TMF_CLEAR_TASK_SET:
+ tmr_func = TMR_CLEAR_TASK_SET;
+ break;
+ case EFCT_SCSI_TMF_LOGICAL_UNIT_RESET:
+ tmr_func = TMR_LUN_RESET;
+ break;
+ case EFCT_SCSI_TMF_CLEAR_ACA:
+ tmr_func = TMR_CLEAR_ACA;
+ break;
+ case EFCT_SCSI_TMF_TARGET_RESET:
+ tmr_func = TMR_TARGET_WARM_RESET;
+ break;
+ case EFCT_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT:
+ case EFCT_SCSI_TMF_QUERY_TASK_SET:
+ default:
+ goto tmf_fail;
+ }
+
+ tmfio->tgt_io.tmf = tmr_func;
+ tmfio->tgt_io.lun = lun;
+ tmfio->tgt_io.io_to_abort = io_to_abort;
+
+ tgt_node = tmfio->node;
+
+ se_sess = tgt_node->session;
+ if (!se_sess)
+ return 0;
+
+ rc = target_submit_tmr(&ocp->cmd, se_sess, NULL, lun, ocp, tmr_func,
+ GFP_ATOMIC, tmfio->init_task_tag, TARGET_SCF_ACK_KREF);
+
+ efct_set_lio_io_state(tmfio, EFCT_LIO_STATE_TGT_SUBMIT_TMR);
+ if (rc)
+ goto tmf_fail;
+
+ return 0;
+
+tmf_fail:
+ efct_scsi_send_tmf_resp(tmfio, EFCT_SCSI_TMF_FUNCTION_REJECTED,
+ NULL, efct_lio_null_tmf_done, NULL);
+ return 0;
+}
+
+/* Start items for efct_lio_tpg_attrib_cit */
+
+#define DEF_EFCT_TPG_ATTRIB(name) \
+ \
+static ssize_t efct_lio_tpg_attrib_##name##_show( \
+ struct config_item *item, char *page) \
+{ \
+ struct se_portal_group *se_tpg = to_tpg(item); \
+ struct efct_lio_tpg *tpg = container_of(se_tpg, \
+ struct efct_lio_tpg, tpg); \
+ \
+ return sprintf(page, "%u\n", tpg->tpg_attrib.name); \
+} \
+ \
+static ssize_t efct_lio_tpg_attrib_##name##_store( \
+ struct config_item *item, const char *page, size_t count) \
+{ \
+ struct se_portal_group *se_tpg = to_tpg(item); \
+ struct efct_lio_tpg *tpg = container_of(se_tpg, \
+ struct efct_lio_tpg, tpg); \
+ struct efct_lio_tpg_attrib *a = &tpg->tpg_attrib; \
+ unsigned long val; \
+ int ret; \
+ \
+ ret = kstrtoul(page, 0, &val); \
+ if (ret < 0) { \
+ pr_err("kstrtoul() failed with ret: %d\n", ret); \
+ return ret; \
+ } \
+ \
+ if (val != 0 && val != 1) { \
+ pr_err("Illegal boolean value %lu\n", val); \
+ return -EINVAL; \
+ } \
+ \
+ a->name = val; \
+ \
+ return count; \
+} \
+CONFIGFS_ATTR(efct_lio_tpg_attrib_, name)
+
+DEF_EFCT_TPG_ATTRIB(generate_node_acls);
+DEF_EFCT_TPG_ATTRIB(cache_dynamic_acls);
+DEF_EFCT_TPG_ATTRIB(demo_mode_write_protect);
+DEF_EFCT_TPG_ATTRIB(prod_mode_write_protect);
+DEF_EFCT_TPG_ATTRIB(demo_mode_login_only);
+DEF_EFCT_TPG_ATTRIB(session_deletion_wait);
+
+static struct configfs_attribute *efct_lio_tpg_attrib_attrs[] = {
+ &efct_lio_tpg_attrib_attr_generate_node_acls,
+ &efct_lio_tpg_attrib_attr_cache_dynamic_acls,
+ &efct_lio_tpg_attrib_attr_demo_mode_write_protect,
+ &efct_lio_tpg_attrib_attr_prod_mode_write_protect,
+ &efct_lio_tpg_attrib_attr_demo_mode_login_only,
+ &efct_lio_tpg_attrib_attr_session_deletion_wait,
+ NULL,
+};
+
+#define DEF_EFCT_NPIV_TPG_ATTRIB(name) \
+ \
+static ssize_t efct_lio_npiv_tpg_attrib_##name##_show( \
+ struct config_item *item, char *page) \
+{ \
+ struct se_portal_group *se_tpg = to_tpg(item); \
+ struct efct_lio_tpg *tpg = container_of(se_tpg, \
+ struct efct_lio_tpg, tpg); \
+ \
+ return sprintf(page, "%u\n", tpg->tpg_attrib.name); \
+} \
+ \
+static ssize_t efct_lio_npiv_tpg_attrib_##name##_store( \
+ struct config_item *item, const char *page, size_t count) \
+{ \
+ struct se_portal_group *se_tpg = to_tpg(item); \
+ struct efct_lio_tpg *tpg = container_of(se_tpg, \
+ struct efct_lio_tpg, tpg); \
+ struct efct_lio_tpg_attrib *a = &tpg->tpg_attrib; \
+ unsigned long val; \
+ int ret; \
+ \
+ ret = kstrtoul(page, 0, &val); \
+ if (ret < 0) { \
+ pr_err("kstrtoul() failed with ret: %d\n", ret); \
+ return ret; \
+ } \
+ \
+ if (val != 0 && val != 1) { \
+ pr_err("Illegal boolean value %lu\n", val); \
+ return -EINVAL; \
+ } \
+ \
+ a->name = val; \
+ \
+ return count; \
+} \
+CONFIGFS_ATTR(efct_lio_npiv_tpg_attrib_, name)
+
+DEF_EFCT_NPIV_TPG_ATTRIB(generate_node_acls);
+DEF_EFCT_NPIV_TPG_ATTRIB(cache_dynamic_acls);
+DEF_EFCT_NPIV_TPG_ATTRIB(demo_mode_write_protect);
+DEF_EFCT_NPIV_TPG_ATTRIB(prod_mode_write_protect);
+DEF_EFCT_NPIV_TPG_ATTRIB(demo_mode_login_only);
+DEF_EFCT_NPIV_TPG_ATTRIB(session_deletion_wait);
+
+static struct configfs_attribute *efct_lio_npiv_tpg_attrib_attrs[] = {
+ &efct_lio_npiv_tpg_attrib_attr_generate_node_acls,
+ &efct_lio_npiv_tpg_attrib_attr_cache_dynamic_acls,
+ &efct_lio_npiv_tpg_attrib_attr_demo_mode_write_protect,
+ &efct_lio_npiv_tpg_attrib_attr_prod_mode_write_protect,
+ &efct_lio_npiv_tpg_attrib_attr_demo_mode_login_only,
+ &efct_lio_npiv_tpg_attrib_attr_session_deletion_wait,
+ NULL,
+};
+
+CONFIGFS_ATTR(efct_lio_tpg_, enable);
+static struct configfs_attribute *efct_lio_tpg_attrs[] = {
+ &efct_lio_tpg_attr_enable, NULL };
+CONFIGFS_ATTR(efct_lio_npiv_tpg_, enable);
+static struct configfs_attribute *efct_lio_npiv_tpg_attrs[] = {
+ &efct_lio_npiv_tpg_attr_enable, NULL };
+
+static const struct target_core_fabric_ops efct_lio_ops = {
+ .module = THIS_MODULE,
+ .fabric_name = "efct",
+ .node_acl_size = sizeof(struct efct_lio_nacl),
+ .max_data_sg_nents = 65535,
+ .tpg_get_wwn = efct_lio_get_fabric_wwn,
+ .tpg_get_tag = efct_lio_get_tag,
+ .fabric_init_nodeacl = efct_lio_init_nodeacl,
+ .tpg_check_demo_mode = efct_lio_check_demo_mode,
+ .tpg_check_demo_mode_cache = efct_lio_check_demo_mode_cache,
+ .tpg_check_demo_mode_write_protect = efct_lio_check_demo_write_protect,
+ .tpg_check_prod_mode_write_protect = efct_lio_check_prod_write_protect,
+ .check_stop_free = efct_lio_check_stop_free,
+ .aborted_task = efct_lio_aborted_task,
+ .release_cmd = efct_lio_release_cmd,
+ .close_session = efct_lio_close_session,
+ .write_pending = efct_lio_write_pending,
+ .get_cmd_state = efct_lio_get_cmd_state,
+ .queue_data_in = efct_lio_queue_data_in,
+ .queue_status = efct_lio_queue_status,
+ .queue_tm_rsp = efct_lio_queue_tm_rsp,
+ .fabric_make_wwn = efct_lio_make_nport,
+ .fabric_drop_wwn = efct_lio_drop_nport,
+ .fabric_make_tpg = efct_lio_make_tpg,
+ .fabric_drop_tpg = efct_lio_drop_tpg,
+ .tpg_check_demo_mode_login_only = efct_lio_check_demo_mode_login_only,
+ .tpg_check_prot_fabric_only = NULL,
+ .sess_get_initiator_sid = NULL,
+ .tfc_tpg_base_attrs = efct_lio_tpg_attrs,
+ .tfc_tpg_attrib_attrs = efct_lio_tpg_attrib_attrs,
+ .default_submit_type = TARGET_DIRECT_SUBMIT,
+ .direct_submit_supp = 1,
+};
+
+static const struct target_core_fabric_ops efct_lio_npiv_ops = {
+ .module = THIS_MODULE,
+ .fabric_name = "efct_npiv",
+ .node_acl_size = sizeof(struct efct_lio_nacl),
+ .max_data_sg_nents = 65535,
+ .tpg_get_wwn = efct_lio_get_npiv_fabric_wwn,
+ .tpg_get_tag = efct_lio_get_npiv_tag,
+ .fabric_init_nodeacl = efct_lio_init_nodeacl,
+ .tpg_check_demo_mode = efct_lio_check_demo_mode,
+ .tpg_check_demo_mode_cache = efct_lio_check_demo_mode_cache,
+ .tpg_check_demo_mode_write_protect =
+ efct_lio_npiv_check_demo_write_protect,
+ .tpg_check_prod_mode_write_protect =
+ efct_lio_npiv_check_prod_write_protect,
+ .check_stop_free = efct_lio_check_stop_free,
+ .aborted_task = efct_lio_aborted_task,
+ .release_cmd = efct_lio_release_cmd,
+ .close_session = efct_lio_close_session,
+ .write_pending = efct_lio_write_pending,
+ .get_cmd_state = efct_lio_get_cmd_state,
+ .queue_data_in = efct_lio_queue_data_in,
+ .queue_status = efct_lio_queue_status,
+ .queue_tm_rsp = efct_lio_queue_tm_rsp,
+ .fabric_make_wwn = efct_lio_npiv_make_nport,
+ .fabric_drop_wwn = efct_lio_npiv_drop_nport,
+ .fabric_make_tpg = efct_lio_npiv_make_tpg,
+ .fabric_drop_tpg = efct_lio_npiv_drop_tpg,
+ .tpg_check_demo_mode_login_only =
+ efct_lio_npiv_check_demo_mode_login_only,
+ .tpg_check_prot_fabric_only = NULL,
+ .sess_get_initiator_sid = NULL,
+ .tfc_tpg_base_attrs = efct_lio_npiv_tpg_attrs,
+ .tfc_tpg_attrib_attrs = efct_lio_npiv_tpg_attrib_attrs,
+
+ .default_submit_type = TARGET_DIRECT_SUBMIT,
+ .direct_submit_supp = 1,
+};
+
+int efct_scsi_tgt_driver_init(void)
+{
+ int rc;
+
+ /* Register the top level struct config_item_type with TCM core */
+ rc = target_register_template(&efct_lio_ops);
+ if (rc < 0) {
+ pr_err("target_fabric_configfs_register failed with %d\n", rc);
+ return rc;
+ }
+ rc = target_register_template(&efct_lio_npiv_ops);
+ if (rc < 0) {
+ pr_err("target_fabric_configfs_register failed with %d\n", rc);
+ target_unregister_template(&efct_lio_ops);
+ return rc;
+ }
+ return 0;
+}
+
+int efct_scsi_tgt_driver_exit(void)
+{
+ target_unregister_template(&efct_lio_ops);
+ target_unregister_template(&efct_lio_npiv_ops);
+ return 0;
+}
diff --git a/drivers/scsi/elx/efct/efct_lio.h b/drivers/scsi/elx/efct/efct_lio.h
new file mode 100644
index 000000000000..569a0d4b1894
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_lio.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#ifndef __EFCT_LIO_H__
+#define __EFCT_LIO_H__
+
+#include "efct_scsi.h"
+#include <target/target_core_base.h>
+
+#define efct_lio_io_printf(io, fmt, ...) \
+ efc_log_debug(io->efct, \
+ "[%s] [%04x][i:%04x t:%04x h:%04x]" fmt,\
+ io->node->display_name, io->instance_index, \
+ io->init_task_tag, io->tgt_task_tag, io->hw_tag,\
+ ##__VA_ARGS__)
+
+#define efct_lio_tmfio_printf(io, fmt, ...) \
+ efc_log_debug(io->efct, \
+ "[%s] [%04x][i:%04x t:%04x h:%04x][f:%02x]" fmt,\
+ io->node->display_name, io->instance_index, \
+ io->init_task_tag, io->tgt_task_tag, io->hw_tag,\
+ io->tgt_io.tmf, ##__VA_ARGS__)
+
+#define efct_set_lio_io_state(io, value) (io->tgt_io.state |= value)
+
+struct efct_lio_wq_data {
+ struct efct *efct;
+ void *ptr;
+ struct work_struct work;
+};
+
+/* Target private efct structure */
+struct efct_scsi_tgt {
+ u32 max_sge;
+ u32 max_sgl;
+
+ /*
+ * Variables used to send task set full. We are using a high watermark
+ * method to send task set full. We will reserve a fixed number of IOs
+ * per initiator plus a fudge factor. Once we reach this number,
+ * then the target will start sending task set full/busy responses.
+ */
+ atomic_t initiator_count;
+ atomic_t ios_in_use;
+ atomic_t io_high_watermark;
+
+ atomic_t watermark_hit;
+ int watermark_min;
+ int watermark_max;
+
+ struct efct_lio_nport *lio_nport;
+ struct efct_lio_tpg *tpg;
+
+ struct list_head vport_list;
+ /* Protects vport list*/
+ spinlock_t efct_lio_lock;
+
+ u64 wwnn;
+};
+
+struct efct_scsi_tgt_nport {
+ struct efct_lio_nport *lio_nport;
+};
+
+struct efct_node {
+ struct list_head list_entry;
+ struct kref ref;
+ void (*release)(struct kref *arg);
+ struct efct *efct;
+ struct efc_node *node;
+ struct se_session *session;
+ spinlock_t active_ios_lock;
+ struct list_head active_ios;
+ char display_name[EFC_NAME_LENGTH];
+ u32 port_fc_id;
+ u32 node_fc_id;
+ u32 vpi;
+ u32 rpi;
+ u32 abort_cnt;
+};
+
+#define EFCT_LIO_STATE_SCSI_RECV_CMD (1 << 0)
+#define EFCT_LIO_STATE_TGT_SUBMIT_CMD (1 << 1)
+#define EFCT_LIO_STATE_TFO_QUEUE_DATA_IN (1 << 2)
+#define EFCT_LIO_STATE_TFO_WRITE_PENDING (1 << 3)
+#define EFCT_LIO_STATE_TGT_EXECUTE_CMD (1 << 4)
+#define EFCT_LIO_STATE_SCSI_SEND_RD_DATA (1 << 5)
+#define EFCT_LIO_STATE_TFO_CHK_STOP_FREE (1 << 6)
+#define EFCT_LIO_STATE_SCSI_DATA_DONE (1 << 7)
+#define EFCT_LIO_STATE_TFO_QUEUE_STATUS (1 << 8)
+#define EFCT_LIO_STATE_SCSI_SEND_RSP (1 << 9)
+#define EFCT_LIO_STATE_SCSI_RSP_DONE (1 << 10)
+#define EFCT_LIO_STATE_TGT_GENERIC_FREE (1 << 11)
+#define EFCT_LIO_STATE_SCSI_RECV_TMF (1 << 12)
+#define EFCT_LIO_STATE_TGT_SUBMIT_TMR (1 << 13)
+#define EFCT_LIO_STATE_TFO_WRITE_PEND_STATUS (1 << 14)
+#define EFCT_LIO_STATE_TGT_GENERIC_REQ_FAILURE (1 << 15)
+
+#define EFCT_LIO_STATE_TFO_ABORTED_TASK (1 << 29)
+#define EFCT_LIO_STATE_TFO_RELEASE_CMD (1 << 30)
+#define EFCT_LIO_STATE_SCSI_CMPL_CMD (1u << 31)
+
+struct efct_scsi_tgt_io {
+ struct se_cmd cmd;
+ unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER];
+ enum dma_data_direction ddir;
+ int task_attr;
+ u64 lun;
+
+ u32 state;
+ u8 tmf;
+ struct efct_io *io_to_abort;
+ u32 seg_map_cnt;
+ u32 seg_cnt;
+ u32 cur_seg;
+ enum efct_scsi_io_status err;
+ bool aborting;
+ bool rsp_sent;
+ u32 transferred_len;
+};
+
+/* Handler return codes */
+enum {
+ SCSI_HANDLER_DATAPHASE_STARTED = 1,
+ SCSI_HANDLER_RESP_STARTED,
+ SCSI_HANDLER_VALIDATED_DATAPHASE_STARTED,
+ SCSI_CMD_NOT_SUPPORTED,
+};
+
+#define WWN_NAME_LEN 32
+struct efct_lio_vport {
+ u64 wwpn;
+ u64 npiv_wwpn;
+ u64 npiv_wwnn;
+ unsigned char wwpn_str[WWN_NAME_LEN];
+ struct se_wwn vport_wwn;
+ struct efct_lio_tpg *tpg;
+ struct efct *efct;
+ struct Scsi_Host *shost;
+ struct fc_vport *fc_vport;
+ atomic_t enable;
+};
+
+struct efct_lio_nport {
+ u64 wwpn;
+ unsigned char wwpn_str[WWN_NAME_LEN];
+ struct se_wwn nport_wwn;
+ struct efct_lio_tpg *tpg;
+ struct efct *efct;
+ atomic_t enable;
+};
+
+struct efct_lio_tpg_attrib {
+ u32 generate_node_acls;
+ u32 cache_dynamic_acls;
+ u32 demo_mode_write_protect;
+ u32 prod_mode_write_protect;
+ u32 demo_mode_login_only;
+ bool session_deletion_wait;
+};
+
+struct efct_lio_tpg {
+ struct se_portal_group tpg;
+ struct efct_lio_nport *nport;
+ struct efct_lio_vport *vport;
+ struct efct_lio_tpg_attrib tpg_attrib;
+ unsigned short tpgt;
+ bool enabled;
+};
+
+struct efct_lio_nacl {
+ u64 nport_wwnn;
+ char nport_name[WWN_NAME_LEN];
+ struct se_session *session;
+ struct se_node_acl se_node_acl;
+};
+
+struct efct_lio_vport_list_t {
+ struct list_head list_entry;
+ struct efct_lio_vport *lio_vport;
+};
+
+int efct_scsi_tgt_driver_init(void);
+int efct_scsi_tgt_driver_exit(void);
+
+#endif /*__EFCT_LIO_H__ */
diff --git a/drivers/scsi/elx/efct/efct_scsi.c b/drivers/scsi/elx/efct/efct_scsi.c
new file mode 100644
index 000000000000..afb154992053
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_scsi.c
@@ -0,0 +1,1157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efct_driver.h"
+#include "efct_hw.h"
+
+#define enable_tsend_auto_resp(efct) 1
+#define enable_treceive_auto_resp(efct) 0
+
+#define SCSI_IOFMT "[%04x][i:%04x t:%04x h:%04x]"
+
+#define scsi_io_printf(io, fmt, ...) \
+ efc_log_debug(io->efct, "[%s]" SCSI_IOFMT fmt, \
+ io->node->display_name, io->instance_index,\
+ io->init_task_tag, io->tgt_task_tag, io->hw_tag, ##__VA_ARGS__)
+
+#define EFCT_LOG_ENABLE_SCSI_TRACE(efct) \
+ (((efct) != NULL) ? (((efct)->logmask & (1U << 2)) != 0) : 0)
+
+#define scsi_io_trace(io, fmt, ...) \
+ do { \
+ if (EFCT_LOG_ENABLE_SCSI_TRACE(io->efct)) \
+ scsi_io_printf(io, fmt, ##__VA_ARGS__); \
+ } while (0)
+
+struct efct_io *
+efct_scsi_io_alloc(struct efct_node *node)
+{
+ struct efct *efct;
+ struct efct_xport *xport;
+ struct efct_io *io;
+ unsigned long flags;
+
+ efct = node->efct;
+
+ xport = efct->xport;
+
+ io = efct_io_pool_io_alloc(efct->xport->io_pool);
+ if (!io) {
+ efc_log_err(efct, "IO alloc Failed\n");
+ atomic_add_return(1, &xport->io_alloc_failed_count);
+ return NULL;
+ }
+
+ /* initialize refcount */
+ kref_init(&io->ref);
+ io->release = _efct_scsi_io_free;
+
+ /* set generic fields */
+ io->efct = efct;
+ io->node = node;
+ kref_get(&node->ref);
+
+ /* set type and name */
+ io->io_type = EFCT_IO_TYPE_IO;
+ io->display_name = "scsi_io";
+
+ io->cmd_ini = false;
+ io->cmd_tgt = true;
+
+ /* Add to node's active_ios list */
+ INIT_LIST_HEAD(&io->list_entry);
+ spin_lock_irqsave(&node->active_ios_lock, flags);
+ list_add(&io->list_entry, &node->active_ios);
+
+ spin_unlock_irqrestore(&node->active_ios_lock, flags);
+
+ return io;
+}
+
+void
+_efct_scsi_io_free(struct kref *arg)
+{
+ struct efct_io *io = container_of(arg, struct efct_io, ref);
+ struct efct *efct = io->efct;
+ struct efct_node *node = io->node;
+ unsigned long flags = 0;
+
+ scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
+
+ if (io->io_free) {
+ efc_log_err(efct, "IO already freed.\n");
+ return;
+ }
+
+ spin_lock_irqsave(&node->active_ios_lock, flags);
+ list_del_init(&io->list_entry);
+ spin_unlock_irqrestore(&node->active_ios_lock, flags);
+
+ kref_put(&node->ref, node->release);
+ io->node = NULL;
+ efct_io_pool_io_free(efct->xport->io_pool, io);
+}
+
+void
+efct_scsi_io_free(struct efct_io *io)
+{
+ scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
+ WARN_ON(!refcount_read(&io->ref.refcount));
+ kref_put(&io->ref, io->release);
+}
+
+static void
+efct_target_io_cb(struct efct_hw_io *hio, u32 length, int status,
+ u32 ext_status, void *app)
+{
+ u32 flags = 0;
+ struct efct_io *io = app;
+ struct efct *efct;
+ enum efct_scsi_io_status scsi_stat = EFCT_SCSI_STATUS_GOOD;
+ efct_scsi_io_cb_t cb;
+
+ if (!io || !io->efct) {
+ pr_err("%s: IO can not be NULL\n", __func__);
+ return;
+ }
+
+ scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
+
+ efct = io->efct;
+
+ io->transferred += length;
+
+ if (!io->scsi_tgt_cb) {
+ efct_scsi_check_pending(efct);
+ return;
+ }
+
+ /* Call target server completion */
+ cb = io->scsi_tgt_cb;
+
+ /* Clear the callback before invoking the callback */
+ io->scsi_tgt_cb = NULL;
+
+ /* if status was good, and auto-good-response was set,
+ * then callback target-server with IO_CMPL_RSP_SENT,
+ * otherwise send IO_CMPL
+ */
+ if (status == 0 && io->auto_resp)
+ flags |= EFCT_SCSI_IO_CMPL_RSP_SENT;
+ else
+ flags |= EFCT_SCSI_IO_CMPL;
+
+ switch (status) {
+ case SLI4_FC_WCQE_STATUS_SUCCESS:
+ scsi_stat = EFCT_SCSI_STATUS_GOOD;
+ break;
+ case SLI4_FC_WCQE_STATUS_DI_ERROR:
+ if (ext_status & SLI4_FC_DI_ERROR_GE)
+ scsi_stat = EFCT_SCSI_STATUS_DIF_GUARD_ERR;
+ else if (ext_status & SLI4_FC_DI_ERROR_AE)
+ scsi_stat = EFCT_SCSI_STATUS_DIF_APP_TAG_ERROR;
+ else if (ext_status & SLI4_FC_DI_ERROR_RE)
+ scsi_stat = EFCT_SCSI_STATUS_DIF_REF_TAG_ERROR;
+ else
+ scsi_stat = EFCT_SCSI_STATUS_DIF_UNKNOWN_ERROR;
+ break;
+ case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
+ switch (ext_status) {
+ case SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET:
+ case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
+ scsi_stat = EFCT_SCSI_STATUS_ABORTED;
+ break;
+ case SLI4_FC_LOCAL_REJECT_INVALID_RPI:
+ scsi_stat = EFCT_SCSI_STATUS_NEXUS_LOST;
+ break;
+ case SLI4_FC_LOCAL_REJECT_NO_XRI:
+ scsi_stat = EFCT_SCSI_STATUS_NO_IO;
+ break;
+ default:
+ /*we have seen 0x0d(TX_DMA_FAILED err)*/
+ scsi_stat = EFCT_SCSI_STATUS_ERROR;
+ break;
+ }
+ break;
+
+ case SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT:
+ /* target IO timed out */
+ scsi_stat = EFCT_SCSI_STATUS_TIMEDOUT_AND_ABORTED;
+ break;
+
+ case SLI4_FC_WCQE_STATUS_SHUTDOWN:
+ /* Target IO cancelled by HW */
+ scsi_stat = EFCT_SCSI_STATUS_SHUTDOWN;
+ break;
+
+ default:
+ scsi_stat = EFCT_SCSI_STATUS_ERROR;
+ break;
+ }
+
+ cb(io, scsi_stat, flags, io->scsi_tgt_cb_arg);
+
+ efct_scsi_check_pending(efct);
+}
+
+static int
+efct_scsi_build_sgls(struct efct_hw *hw, struct efct_hw_io *hio,
+ struct efct_scsi_sgl *sgl, u32 sgl_count,
+ enum efct_hw_io_type type)
+{
+ int rc;
+ u32 i;
+ struct efct *efct = hw->os;
+
+ /* Initialize HW SGL */
+ rc = efct_hw_io_init_sges(hw, hio, type);
+ if (rc) {
+ efc_log_err(efct, "efct_hw_io_init_sges failed: %d\n", rc);
+ return -EIO;
+ }
+
+ for (i = 0; i < sgl_count; i++) {
+ /* Add data SGE */
+ rc = efct_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len);
+ if (rc) {
+ efc_log_err(efct, "add sge failed cnt=%d rc=%d\n",
+ sgl_count, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static void efc_log_sgl(struct efct_io *io)
+{
+ struct efct_hw_io *hio = io->hio;
+ struct sli4_sge *data = NULL;
+ u32 *dword = NULL;
+ u32 i;
+ u32 n_sge;
+
+ scsi_io_trace(io, "def_sgl at 0x%x 0x%08x\n",
+ upper_32_bits(hio->def_sgl.phys),
+ lower_32_bits(hio->def_sgl.phys));
+ n_sge = (hio->sgl == &hio->def_sgl) ? hio->n_sge : hio->def_sgl_count;
+ for (i = 0, data = hio->def_sgl.virt; i < n_sge; i++, data++) {
+ dword = (u32 *)data;
+
+ scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i, dword[0], dword[1], dword[2], dword[3]);
+
+ if (dword[2] & (1U << 31))
+ break;
+ }
+}
+
+static void
+efct_scsi_check_pending_async_cb(struct efct_hw *hw, int status,
+ u8 *mqe, void *arg)
+{
+ struct efct_io *io = arg;
+
+ if (io) {
+ efct_hw_done_t cb = io->hw_cb;
+
+ if (!io->hw_cb)
+ return;
+
+ io->hw_cb = NULL;
+ (cb)(io->hio, 0, SLI4_FC_WCQE_STATUS_DISPATCH_ERROR, 0, io);
+ }
+}
+
+static int
+efct_scsi_io_dispatch_hw_io(struct efct_io *io, struct efct_hw_io *hio)
+{
+ int rc = 0;
+ struct efct *efct = io->efct;
+
+ /* Got a HW IO;
+ * update ini/tgt_task_tag with HW IO info and dispatch
+ */
+ io->hio = hio;
+ if (io->cmd_tgt)
+ io->tgt_task_tag = hio->indicator;
+ else if (io->cmd_ini)
+ io->init_task_tag = hio->indicator;
+ io->hw_tag = hio->reqtag;
+
+ hio->eq = io->hw_priv;
+
+ /* Copy WQ steering */
+ switch (io->wq_steering) {
+ case EFCT_SCSI_WQ_STEERING_CLASS >> EFCT_SCSI_WQ_STEERING_SHIFT:
+ hio->wq_steering = EFCT_HW_WQ_STEERING_CLASS;
+ break;
+ case EFCT_SCSI_WQ_STEERING_REQUEST >> EFCT_SCSI_WQ_STEERING_SHIFT:
+ hio->wq_steering = EFCT_HW_WQ_STEERING_REQUEST;
+ break;
+ case EFCT_SCSI_WQ_STEERING_CPU >> EFCT_SCSI_WQ_STEERING_SHIFT:
+ hio->wq_steering = EFCT_HW_WQ_STEERING_CPU;
+ break;
+ }
+
+ switch (io->io_type) {
+ case EFCT_IO_TYPE_IO:
+ rc = efct_scsi_build_sgls(&efct->hw, io->hio,
+ io->sgl, io->sgl_count, io->hio_type);
+ if (rc)
+ break;
+
+ if (EFCT_LOG_ENABLE_SCSI_TRACE(efct))
+ efc_log_sgl(io);
+
+ if (io->app_id)
+ io->iparam.fcp_tgt.app_id = io->app_id;
+
+ io->iparam.fcp_tgt.vpi = io->node->vpi;
+ io->iparam.fcp_tgt.rpi = io->node->rpi;
+ io->iparam.fcp_tgt.s_id = io->node->port_fc_id;
+ io->iparam.fcp_tgt.d_id = io->node->node_fc_id;
+ io->iparam.fcp_tgt.xmit_len = io->wire_len;
+
+ rc = efct_hw_io_send(&io->efct->hw, io->hio_type, io->hio,
+ &io->iparam, io->hw_cb, io);
+ break;
+ default:
+ scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
+ rc = -EIO;
+ break;
+ }
+ return rc;
+}
+
+static int
+efct_scsi_io_dispatch_no_hw_io(struct efct_io *io)
+{
+ int rc;
+
+ switch (io->io_type) {
+ case EFCT_IO_TYPE_ABORT: {
+ struct efct_hw_io *hio_to_abort = NULL;
+
+ hio_to_abort = io->io_to_abort->hio;
+
+ if (!hio_to_abort) {
+ /*
+ * If "IO to abort" does not have an
+ * associated HW IO, immediately make callback with
+ * success. The command must have been sent to
+ * the backend, but the data phase has not yet
+ * started, so we don't have a HW IO.
+ *
+ * Note: since the backend shims should be
+ * taking a reference on io_to_abort, it should not
+ * be possible to have been completed and freed by
+ * the backend before the abort got here.
+ */
+ scsi_io_printf(io, "IO: not active\n");
+ ((efct_hw_done_t)io->hw_cb)(io->hio, 0,
+ SLI4_FC_WCQE_STATUS_SUCCESS, 0, io);
+ rc = 0;
+ break;
+ }
+
+ /* HW IO is valid, abort it */
+ scsi_io_printf(io, "aborting\n");
+ rc = efct_hw_io_abort(&io->efct->hw, hio_to_abort,
+ io->send_abts, io->hw_cb, io);
+ if (rc) {
+ int status = SLI4_FC_WCQE_STATUS_SUCCESS;
+ efct_hw_done_t cb = io->hw_cb;
+
+ if (rc != -ENOENT && rc != -EINPROGRESS) {
+ status = -1;
+ scsi_io_printf(io, "Failed to abort IO rc=%d\n",
+ rc);
+ }
+ cb(io->hio, 0, status, 0, io);
+ rc = 0;
+ }
+
+ break;
+ }
+ default:
+ scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
+ rc = -EIO;
+ break;
+ }
+ return rc;
+}
+
+static struct efct_io *
+efct_scsi_dispatch_pending(struct efct *efct)
+{
+ struct efct_xport *xport = efct->xport;
+ struct efct_io *io = NULL;
+ struct efct_hw_io *hio;
+ unsigned long flags = 0;
+ int status;
+
+ spin_lock_irqsave(&xport->io_pending_lock, flags);
+
+ if (!list_empty(&xport->io_pending_list)) {
+ io = list_first_entry(&xport->io_pending_list, struct efct_io,
+ io_pending_link);
+ list_del_init(&io->io_pending_link);
+ }
+
+ if (!io) {
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+ return NULL;
+ }
+
+ if (io->io_type == EFCT_IO_TYPE_ABORT) {
+ hio = NULL;
+ } else {
+ hio = efct_hw_io_alloc(&efct->hw);
+ if (!hio) {
+ /*
+ * No HW IO available.Put IO back on
+ * the front of pending list
+ */
+ list_add(&xport->io_pending_list, &io->io_pending_link);
+ io = NULL;
+ } else {
+ hio->eq = io->hw_priv;
+ }
+ }
+
+ /* Must drop the lock before dispatching the IO */
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+
+ if (!io)
+ return NULL;
+
+ /*
+ * We pulled an IO off the pending list,
+ * and either got an HW IO or don't need one
+ */
+ atomic_sub_return(1, &xport->io_pending_count);
+ if (!hio)
+ status = efct_scsi_io_dispatch_no_hw_io(io);
+ else
+ status = efct_scsi_io_dispatch_hw_io(io, hio);
+ if (status) {
+ /*
+ * Invoke the HW callback, but do so in the
+ * separate execution context,provided by the
+ * NOP mailbox completion processing context
+ * by using efct_hw_async_call()
+ */
+ if (efct_hw_async_call(&efct->hw,
+ efct_scsi_check_pending_async_cb, io)) {
+ efc_log_debug(efct, "call hw async failed\n");
+ }
+ }
+
+ return io;
+}
+
+void
+efct_scsi_check_pending(struct efct *efct)
+{
+ struct efct_xport *xport = efct->xport;
+ struct efct_io *io = NULL;
+ int count = 0;
+ unsigned long flags = 0;
+ int dispatch = 0;
+
+ /* Guard against recursion */
+ if (atomic_add_return(1, &xport->io_pending_recursing)) {
+ /* This function is already running. Decrement and return. */
+ atomic_sub_return(1, &xport->io_pending_recursing);
+ return;
+ }
+
+ while (efct_scsi_dispatch_pending(efct))
+ count++;
+
+ if (count) {
+ atomic_sub_return(1, &xport->io_pending_recursing);
+ return;
+ }
+
+ /*
+ * If nothing was removed from the list,
+ * we might be in a case where we need to abort an
+ * active IO and the abort is on the pending list.
+ * Look for an abort we can dispatch.
+ */
+
+ spin_lock_irqsave(&xport->io_pending_lock, flags);
+
+ list_for_each_entry(io, &xport->io_pending_list, io_pending_link) {
+ if (io->io_type == EFCT_IO_TYPE_ABORT && io->io_to_abort->hio) {
+ /* This IO has a HW IO, so it is
+ * active. Dispatch the abort.
+ */
+ dispatch = 1;
+ list_del_init(&io->io_pending_link);
+ atomic_sub_return(1, &xport->io_pending_count);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+
+ if (dispatch) {
+ if (efct_scsi_io_dispatch_no_hw_io(io)) {
+ if (efct_hw_async_call(&efct->hw,
+ efct_scsi_check_pending_async_cb, io)) {
+ efc_log_debug(efct, "hw async failed\n");
+ }
+ }
+ }
+
+ atomic_sub_return(1, &xport->io_pending_recursing);
+}
+
+int
+efct_scsi_io_dispatch(struct efct_io *io, void *cb)
+{
+ struct efct_hw_io *hio;
+ struct efct *efct = io->efct;
+ struct efct_xport *xport = efct->xport;
+ unsigned long flags = 0;
+
+ io->hw_cb = cb;
+
+ /*
+ * if this IO already has a HW IO, then this is either
+ * not the first phase of the IO. Send it to the HW.
+ */
+ if (io->hio)
+ return efct_scsi_io_dispatch_hw_io(io, io->hio);
+
+ /*
+ * We don't already have a HW IO associated with the IO. First check
+ * the pending list. If not empty, add IO to the tail and process the
+ * pending list.
+ */
+ spin_lock_irqsave(&xport->io_pending_lock, flags);
+ if (!list_empty(&xport->io_pending_list)) {
+ /*
+ * If this is a low latency request,
+ * the put at the front of the IO pending
+ * queue, otherwise put it at the end of the queue.
+ */
+ if (io->low_latency) {
+ INIT_LIST_HEAD(&io->io_pending_link);
+ list_add(&xport->io_pending_list, &io->io_pending_link);
+ } else {
+ INIT_LIST_HEAD(&io->io_pending_link);
+ list_add_tail(&io->io_pending_link,
+ &xport->io_pending_list);
+ }
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+ atomic_add_return(1, &xport->io_pending_count);
+ atomic_add_return(1, &xport->io_total_pending);
+
+ /* process pending list */
+ efct_scsi_check_pending(efct);
+ return 0;
+ }
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+
+ /*
+ * We don't have a HW IO associated with the IO and there's nothing
+ * on the pending list. Attempt to allocate a HW IO and dispatch it.
+ */
+ hio = efct_hw_io_alloc(&io->efct->hw);
+ if (!hio) {
+ /* Couldn't get a HW IO. Save this IO on the pending list */
+ spin_lock_irqsave(&xport->io_pending_lock, flags);
+ INIT_LIST_HEAD(&io->io_pending_link);
+ list_add_tail(&io->io_pending_link, &xport->io_pending_list);
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+
+ atomic_add_return(1, &xport->io_total_pending);
+ atomic_add_return(1, &xport->io_pending_count);
+ return 0;
+ }
+
+ /* We successfully allocated a HW IO; dispatch to HW */
+ return efct_scsi_io_dispatch_hw_io(io, hio);
+}
+
+int
+efct_scsi_io_dispatch_abort(struct efct_io *io, void *cb)
+{
+ struct efct *efct = io->efct;
+ struct efct_xport *xport = efct->xport;
+ unsigned long flags = 0;
+
+ io->hw_cb = cb;
+
+ /*
+ * For aborts, we don't need a HW IO, but we still want
+ * to pass through the pending list to preserve ordering.
+ * Thus, if the pending list is not empty, add this abort
+ * to the pending list and process the pending list.
+ */
+ spin_lock_irqsave(&xport->io_pending_lock, flags);
+ if (!list_empty(&xport->io_pending_list)) {
+ INIT_LIST_HEAD(&io->io_pending_link);
+ list_add_tail(&io->io_pending_link, &xport->io_pending_list);
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+ atomic_add_return(1, &xport->io_pending_count);
+ atomic_add_return(1, &xport->io_total_pending);
+
+ /* process pending list */
+ efct_scsi_check_pending(efct);
+ return 0;
+ }
+ spin_unlock_irqrestore(&xport->io_pending_lock, flags);
+
+ /* nothing on pending list, dispatch abort */
+ return efct_scsi_io_dispatch_no_hw_io(io);
+}
+
+static inline int
+efct_scsi_xfer_data(struct efct_io *io, u32 flags,
+ struct efct_scsi_sgl *sgl, u32 sgl_count, u64 xwire_len,
+ enum efct_hw_io_type type, int enable_ar,
+ efct_scsi_io_cb_t cb, void *arg)
+{
+ struct efct *efct;
+ size_t residual = 0;
+
+ io->sgl_count = sgl_count;
+
+ efct = io->efct;
+
+ scsi_io_trace(io, "%s wire_len %llu\n",
+ (type == EFCT_HW_IO_TARGET_READ) ? "send" : "recv",
+ xwire_len);
+
+ io->hio_type = type;
+
+ io->scsi_tgt_cb = cb;
+ io->scsi_tgt_cb_arg = arg;
+
+ residual = io->exp_xfer_len - io->transferred;
+ io->wire_len = (xwire_len < residual) ? xwire_len : residual;
+ residual = (xwire_len - io->wire_len);
+
+ memset(&io->iparam, 0, sizeof(io->iparam));
+ io->iparam.fcp_tgt.ox_id = io->init_task_tag;
+ io->iparam.fcp_tgt.offset = io->transferred;
+ io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
+ io->iparam.fcp_tgt.timeout = io->timeout;
+
+ /* if this is the last data phase and there is no residual, enable
+ * auto-good-response
+ */
+ if (enable_ar && (flags & EFCT_SCSI_LAST_DATAPHASE) && residual == 0 &&
+ ((io->transferred + io->wire_len) == io->exp_xfer_len) &&
+ (!(flags & EFCT_SCSI_NO_AUTO_RESPONSE))) {
+ io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
+ io->auto_resp = true;
+ } else {
+ io->auto_resp = false;
+ }
+
+ /* save this transfer length */
+ io->xfer_req = io->wire_len;
+
+ /* Adjust the transferred count to account for overrun
+ * when the residual is calculated in efct_scsi_send_resp
+ */
+ io->transferred += residual;
+
+ /* Adjust the SGL size if there is overrun */
+
+ if (residual) {
+ struct efct_scsi_sgl *sgl_ptr = &io->sgl[sgl_count - 1];
+
+ while (residual) {
+ size_t len = sgl_ptr->len;
+
+ if (len > residual) {
+ sgl_ptr->len = len - residual;
+ residual = 0;
+ } else {
+ sgl_ptr->len = 0;
+ residual -= len;
+ io->sgl_count--;
+ }
+ sgl_ptr--;
+ }
+ }
+
+ /* Set latency and WQ steering */
+ io->low_latency = (flags & EFCT_SCSI_LOW_LATENCY) != 0;
+ io->wq_steering = (flags & EFCT_SCSI_WQ_STEERING_MASK) >>
+ EFCT_SCSI_WQ_STEERING_SHIFT;
+ io->wq_class = (flags & EFCT_SCSI_WQ_CLASS_MASK) >>
+ EFCT_SCSI_WQ_CLASS_SHIFT;
+
+ if (efct->xport) {
+ struct efct_xport *xport = efct->xport;
+
+ if (type == EFCT_HW_IO_TARGET_READ) {
+ xport->fcp_stats.input_requests++;
+ xport->fcp_stats.input_bytes += xwire_len;
+ } else if (type == EFCT_HW_IO_TARGET_WRITE) {
+ xport->fcp_stats.output_requests++;
+ xport->fcp_stats.output_bytes += xwire_len;
+ }
+ }
+ return efct_scsi_io_dispatch(io, efct_target_io_cb);
+}
+
+int
+efct_scsi_send_rd_data(struct efct_io *io, u32 flags,
+ struct efct_scsi_sgl *sgl, u32 sgl_count, u64 len,
+ efct_scsi_io_cb_t cb, void *arg)
+{
+ return efct_scsi_xfer_data(io, flags, sgl, sgl_count,
+ len, EFCT_HW_IO_TARGET_READ,
+ enable_tsend_auto_resp(io->efct), cb, arg);
+}
+
+int
+efct_scsi_recv_wr_data(struct efct_io *io, u32 flags,
+ struct efct_scsi_sgl *sgl, u32 sgl_count, u64 len,
+ efct_scsi_io_cb_t cb, void *arg)
+{
+ return efct_scsi_xfer_data(io, flags, sgl, sgl_count, len,
+ EFCT_HW_IO_TARGET_WRITE,
+ enable_treceive_auto_resp(io->efct), cb, arg);
+}
+
+int
+efct_scsi_send_resp(struct efct_io *io, u32 flags,
+ struct efct_scsi_cmd_resp *rsp,
+ efct_scsi_io_cb_t cb, void *arg)
+{
+ struct efct *efct;
+ int residual;
+ /* Always try auto resp */
+ bool auto_resp = true;
+ u8 scsi_status = 0;
+ u16 scsi_status_qualifier = 0;
+ u8 *sense_data = NULL;
+ u32 sense_data_length = 0;
+
+ efct = io->efct;
+
+ if (rsp) {
+ scsi_status = rsp->scsi_status;
+ scsi_status_qualifier = rsp->scsi_status_qualifier;
+ sense_data = rsp->sense_data;
+ sense_data_length = rsp->sense_data_length;
+ residual = rsp->residual;
+ } else {
+ residual = io->exp_xfer_len - io->transferred;
+ }
+
+ io->wire_len = 0;
+ io->hio_type = EFCT_HW_IO_TARGET_RSP;
+
+ io->scsi_tgt_cb = cb;
+ io->scsi_tgt_cb_arg = arg;
+
+ memset(&io->iparam, 0, sizeof(io->iparam));
+ io->iparam.fcp_tgt.ox_id = io->init_task_tag;
+ io->iparam.fcp_tgt.offset = 0;
+ io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
+ io->iparam.fcp_tgt.timeout = io->timeout;
+
+ /* Set low latency queueing request */
+ io->low_latency = (flags & EFCT_SCSI_LOW_LATENCY) != 0;
+ io->wq_steering = (flags & EFCT_SCSI_WQ_STEERING_MASK) >>
+ EFCT_SCSI_WQ_STEERING_SHIFT;
+ io->wq_class = (flags & EFCT_SCSI_WQ_CLASS_MASK) >>
+ EFCT_SCSI_WQ_CLASS_SHIFT;
+
+ if (scsi_status != 0 || residual || sense_data_length) {
+ struct fcp_resp_with_ext *fcprsp = io->rspbuf.virt;
+ u8 *sns_data;
+
+ if (!fcprsp) {
+ efc_log_err(efct, "NULL response buffer\n");
+ return -EIO;
+ }
+
+ sns_data = (u8 *)io->rspbuf.virt + sizeof(*fcprsp);
+
+ auto_resp = false;
+
+ memset(fcprsp, 0, sizeof(*fcprsp));
+
+ io->wire_len += sizeof(*fcprsp);
+
+ fcprsp->resp.fr_status = scsi_status;
+ fcprsp->resp.fr_retry_delay =
+ cpu_to_be16(scsi_status_qualifier);
+
+ /* set residual status if necessary */
+ if (residual != 0) {
+ /* FCP: if data transferred is less than the
+ * amount expected, then this is an underflow.
+ * If data transferred would have been greater
+ * than the amount expected this is an overflow
+ */
+ if (residual > 0) {
+ fcprsp->resp.fr_flags |= FCP_RESID_UNDER;
+ fcprsp->ext.fr_resid = cpu_to_be32(residual);
+ } else {
+ fcprsp->resp.fr_flags |= FCP_RESID_OVER;
+ fcprsp->ext.fr_resid = cpu_to_be32(-residual);
+ }
+ }
+
+ if (EFCT_SCSI_SNS_BUF_VALID(sense_data) && sense_data_length) {
+ if (sense_data_length > SCSI_SENSE_BUFFERSIZE) {
+ efc_log_err(efct, "Sense exceeds max size.\n");
+ return -EIO;
+ }
+
+ fcprsp->resp.fr_flags |= FCP_SNS_LEN_VAL;
+ memcpy(sns_data, sense_data, sense_data_length);
+ fcprsp->ext.fr_sns_len = cpu_to_be32(sense_data_length);
+ io->wire_len += sense_data_length;
+ }
+
+ io->sgl[0].addr = io->rspbuf.phys;
+ io->sgl[0].dif_addr = 0;
+ io->sgl[0].len = io->wire_len;
+ io->sgl_count = 1;
+ }
+
+ if (auto_resp)
+ io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
+
+ return efct_scsi_io_dispatch(io, efct_target_io_cb);
+}
+
+static int
+efct_target_bls_resp_cb(struct efct_hw_io *hio, u32 length, int status,
+ u32 ext_status, void *app)
+{
+ struct efct_io *io = app;
+ struct efct *efct;
+ enum efct_scsi_io_status bls_status;
+
+ efct = io->efct;
+
+ /* BLS isn't really a "SCSI" concept, but use SCSI status */
+ if (status) {
+ io_error_log(io, "s=%#x x=%#x\n", status, ext_status);
+ bls_status = EFCT_SCSI_STATUS_ERROR;
+ } else {
+ bls_status = EFCT_SCSI_STATUS_GOOD;
+ }
+
+ if (io->bls_cb) {
+ efct_scsi_io_cb_t bls_cb = io->bls_cb;
+ void *bls_cb_arg = io->bls_cb_arg;
+
+ io->bls_cb = NULL;
+ io->bls_cb_arg = NULL;
+
+ /* invoke callback */
+ bls_cb(io, bls_status, 0, bls_cb_arg);
+ }
+
+ efct_scsi_check_pending(efct);
+ return 0;
+}
+
+static int
+efct_target_send_bls_resp(struct efct_io *io,
+ efct_scsi_io_cb_t cb, void *arg)
+{
+ struct efct_node *node = io->node;
+ struct sli_bls_params *bls = &io->iparam.bls;
+ struct efct *efct = node->efct;
+ struct fc_ba_acc *acc;
+ int rc;
+
+ /* fill out IO structure with everything needed to send BA_ACC */
+ memset(&io->iparam, 0, sizeof(io->iparam));
+ bls->ox_id = io->init_task_tag;
+ bls->rx_id = io->abort_rx_id;
+ bls->vpi = io->node->vpi;
+ bls->rpi = io->node->rpi;
+ bls->s_id = U32_MAX;
+ bls->d_id = io->node->node_fc_id;
+ bls->rpi_registered = true;
+
+ acc = (void *)bls->payload;
+ acc->ba_ox_id = cpu_to_be16(bls->ox_id);
+ acc->ba_rx_id = cpu_to_be16(bls->rx_id);
+ acc->ba_high_seq_cnt = cpu_to_be16(U16_MAX);
+
+ /* generic io fields have already been populated */
+
+ /* set type and BLS-specific fields */
+ io->io_type = EFCT_IO_TYPE_BLS_RESP;
+ io->display_name = "bls_rsp";
+ io->hio_type = EFCT_HW_BLS_ACC;
+ io->bls_cb = cb;
+ io->bls_cb_arg = arg;
+
+ /* dispatch IO */
+ rc = efct_hw_bls_send(efct, FC_RCTL_BA_ACC, bls,
+ efct_target_bls_resp_cb, io);
+ return rc;
+}
+
+static int efct_bls_send_rjt_cb(struct efct_hw_io *hio, u32 length, int status,
+ u32 ext_status, void *app)
+{
+ struct efct_io *io = app;
+
+ efct_scsi_io_free(io);
+ return 0;
+}
+
+struct efct_io *
+efct_bls_send_rjt(struct efct_io *io, struct fc_frame_header *hdr)
+{
+ struct efct_node *node = io->node;
+ struct sli_bls_params *bls = &io->iparam.bls;
+ struct efct *efct = node->efct;
+ struct fc_ba_rjt *acc;
+ int rc;
+
+ /* fill out BLS Response-specific fields */
+ io->io_type = EFCT_IO_TYPE_BLS_RESP;
+ io->display_name = "ba_rjt";
+ io->hio_type = EFCT_HW_BLS_RJT;
+ io->init_task_tag = be16_to_cpu(hdr->fh_ox_id);
+
+ /* fill out iparam fields */
+ memset(&io->iparam, 0, sizeof(io->iparam));
+ bls->ox_id = be16_to_cpu(hdr->fh_ox_id);
+ bls->rx_id = be16_to_cpu(hdr->fh_rx_id);
+ bls->vpi = io->node->vpi;
+ bls->rpi = io->node->rpi;
+ bls->s_id = U32_MAX;
+ bls->d_id = io->node->node_fc_id;
+ bls->rpi_registered = true;
+
+ acc = (void *)bls->payload;
+ acc->br_reason = ELS_RJT_UNAB;
+ acc->br_explan = ELS_EXPL_NONE;
+
+ rc = efct_hw_bls_send(efct, FC_RCTL_BA_RJT, bls, efct_bls_send_rjt_cb,
+ io);
+ if (rc) {
+ efc_log_err(efct, "efct_scsi_io_dispatch() failed: %d\n", rc);
+ efct_scsi_io_free(io);
+ io = NULL;
+ }
+ return io;
+}
+
+int
+efct_scsi_send_tmf_resp(struct efct_io *io,
+ enum efct_scsi_tmf_resp rspcode,
+ u8 addl_rsp_info[3],
+ efct_scsi_io_cb_t cb, void *arg)
+{
+ int rc;
+ struct {
+ struct fcp_resp_with_ext rsp_ext;
+ struct fcp_resp_rsp_info info;
+ } *fcprsp;
+ u8 fcp_rspcode;
+
+ io->wire_len = 0;
+
+ switch (rspcode) {
+ case EFCT_SCSI_TMF_FUNCTION_COMPLETE:
+ fcp_rspcode = FCP_TMF_CMPL;
+ break;
+ case EFCT_SCSI_TMF_FUNCTION_SUCCEEDED:
+ case EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND:
+ fcp_rspcode = FCP_TMF_CMPL;
+ break;
+ case EFCT_SCSI_TMF_FUNCTION_REJECTED:
+ fcp_rspcode = FCP_TMF_REJECTED;
+ break;
+ case EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER:
+ fcp_rspcode = FCP_TMF_INVALID_LUN;
+ break;
+ case EFCT_SCSI_TMF_SERVICE_DELIVERY:
+ fcp_rspcode = FCP_TMF_FAILED;
+ break;
+ default:
+ fcp_rspcode = FCP_TMF_REJECTED;
+ break;
+ }
+
+ io->hio_type = EFCT_HW_IO_TARGET_RSP;
+
+ io->scsi_tgt_cb = cb;
+ io->scsi_tgt_cb_arg = arg;
+
+ if (io->tmf_cmd == EFCT_SCSI_TMF_ABORT_TASK) {
+ rc = efct_target_send_bls_resp(io, cb, arg);
+ return rc;
+ }
+
+ /* populate the FCP TMF response */
+ fcprsp = io->rspbuf.virt;
+ memset(fcprsp, 0, sizeof(*fcprsp));
+
+ fcprsp->rsp_ext.resp.fr_flags |= FCP_SNS_LEN_VAL;
+
+ if (addl_rsp_info) {
+ memcpy(fcprsp->info._fr_resvd, addl_rsp_info,
+ sizeof(fcprsp->info._fr_resvd));
+ }
+ fcprsp->info.rsp_code = fcp_rspcode;
+
+ io->wire_len = sizeof(*fcprsp);
+
+ fcprsp->rsp_ext.ext.fr_rsp_len =
+ cpu_to_be32(sizeof(struct fcp_resp_rsp_info));
+
+ io->sgl[0].addr = io->rspbuf.phys;
+ io->sgl[0].dif_addr = 0;
+ io->sgl[0].len = io->wire_len;
+ io->sgl_count = 1;
+
+ memset(&io->iparam, 0, sizeof(io->iparam));
+ io->iparam.fcp_tgt.ox_id = io->init_task_tag;
+ io->iparam.fcp_tgt.offset = 0;
+ io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
+ io->iparam.fcp_tgt.timeout = io->timeout;
+
+ rc = efct_scsi_io_dispatch(io, efct_target_io_cb);
+
+ return rc;
+}
+
+static int
+efct_target_abort_cb(struct efct_hw_io *hio, u32 length, int status,
+ u32 ext_status, void *app)
+{
+ struct efct_io *io = app;
+ struct efct *efct;
+ enum efct_scsi_io_status scsi_status;
+ efct_scsi_io_cb_t abort_cb;
+ void *abort_cb_arg;
+
+ efct = io->efct;
+
+ if (!io->abort_cb)
+ goto done;
+
+ abort_cb = io->abort_cb;
+ abort_cb_arg = io->abort_cb_arg;
+
+ io->abort_cb = NULL;
+ io->abort_cb_arg = NULL;
+
+ switch (status) {
+ case SLI4_FC_WCQE_STATUS_SUCCESS:
+ scsi_status = EFCT_SCSI_STATUS_GOOD;
+ break;
+ case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
+ switch (ext_status) {
+ case SLI4_FC_LOCAL_REJECT_NO_XRI:
+ scsi_status = EFCT_SCSI_STATUS_NO_IO;
+ break;
+ case SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS:
+ scsi_status = EFCT_SCSI_STATUS_ABORT_IN_PROGRESS;
+ break;
+ default:
+ /*we have seen 0x15 (abort in progress)*/
+ scsi_status = EFCT_SCSI_STATUS_ERROR;
+ break;
+ }
+ break;
+ case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
+ scsi_status = EFCT_SCSI_STATUS_CHECK_RESPONSE;
+ break;
+ default:
+ scsi_status = EFCT_SCSI_STATUS_ERROR;
+ break;
+ }
+ /* invoke callback */
+ abort_cb(io->io_to_abort, scsi_status, 0, abort_cb_arg);
+
+done:
+ /* done with IO to abort,efct_ref_get(): efct_scsi_tgt_abort_io() */
+ kref_put(&io->io_to_abort->ref, io->io_to_abort->release);
+
+ efct_io_pool_io_free(efct->xport->io_pool, io);
+
+ efct_scsi_check_pending(efct);
+ return 0;
+}
+
+int
+efct_scsi_tgt_abort_io(struct efct_io *io, efct_scsi_io_cb_t cb, void *arg)
+{
+ struct efct *efct;
+ struct efct_xport *xport;
+ int rc;
+ struct efct_io *abort_io = NULL;
+
+ efct = io->efct;
+ xport = efct->xport;
+
+ /* take a reference on IO being aborted */
+ if (kref_get_unless_zero(&io->ref) == 0) {
+ /* command no longer active */
+ scsi_io_printf(io, "command no longer active\n");
+ return -EIO;
+ }
+
+ /*
+ * allocate a new IO to send the abort request. Use efct_io_alloc()
+ * directly, as we need an IO object that will not fail allocation
+ * due to allocations being disabled (in efct_scsi_io_alloc())
+ */
+ abort_io = efct_io_pool_io_alloc(efct->xport->io_pool);
+ if (!abort_io) {
+ atomic_add_return(1, &xport->io_alloc_failed_count);
+ kref_put(&io->ref, io->release);
+ return -EIO;
+ }
+
+ /* Save the target server callback and argument */
+ /* set generic fields */
+ abort_io->cmd_tgt = true;
+ abort_io->node = io->node;
+
+ /* set type and abort-specific fields */
+ abort_io->io_type = EFCT_IO_TYPE_ABORT;
+ abort_io->display_name = "tgt_abort";
+ abort_io->io_to_abort = io;
+ abort_io->send_abts = false;
+ abort_io->abort_cb = cb;
+ abort_io->abort_cb_arg = arg;
+
+ /* now dispatch IO */
+ rc = efct_scsi_io_dispatch_abort(abort_io, efct_target_abort_cb);
+ if (rc)
+ kref_put(&io->ref, io->release);
+ return rc;
+}
+
+void
+efct_scsi_io_complete(struct efct_io *io)
+{
+ if (io->io_free) {
+ efc_log_debug(io->efct, "completion for non-busy io tag 0x%x\n",
+ io->tag);
+ return;
+ }
+
+ scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
+ kref_put(&io->ref, io->release);
+}
diff --git a/drivers/scsi/elx/efct/efct_scsi.h b/drivers/scsi/elx/efct/efct_scsi.h
new file mode 100644
index 000000000000..b04faffa3984
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_scsi.h
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#if !defined(__EFCT_SCSI_H__)
+#define __EFCT_SCSI_H__
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+
+/* efct_scsi_rcv_cmd() efct_scsi_rcv_tmf() flags */
+#define EFCT_SCSI_CMD_DIR_IN (1 << 0)
+#define EFCT_SCSI_CMD_DIR_OUT (1 << 1)
+#define EFCT_SCSI_CMD_SIMPLE (1 << 2)
+#define EFCT_SCSI_CMD_HEAD_OF_QUEUE (1 << 3)
+#define EFCT_SCSI_CMD_ORDERED (1 << 4)
+#define EFCT_SCSI_CMD_UNTAGGED (1 << 5)
+#define EFCT_SCSI_CMD_ACA (1 << 6)
+#define EFCT_SCSI_FIRST_BURST_ERR (1 << 7)
+#define EFCT_SCSI_FIRST_BURST_ABORTED (1 << 8)
+
+/* efct_scsi_send_rd_data/recv_wr_data/send_resp flags */
+#define EFCT_SCSI_LAST_DATAPHASE (1 << 0)
+#define EFCT_SCSI_NO_AUTO_RESPONSE (1 << 1)
+#define EFCT_SCSI_LOW_LATENCY (1 << 2)
+
+#define EFCT_SCSI_SNS_BUF_VALID(sense) ((sense) && \
+ (0x70 == (((const u8 *)(sense))[0] & 0x70)))
+
+#define EFCT_SCSI_WQ_STEERING_SHIFT 16
+#define EFCT_SCSI_WQ_STEERING_MASK (0xf << EFCT_SCSI_WQ_STEERING_SHIFT)
+#define EFCT_SCSI_WQ_STEERING_CLASS (0 << EFCT_SCSI_WQ_STEERING_SHIFT)
+#define EFCT_SCSI_WQ_STEERING_REQUEST (1 << EFCT_SCSI_WQ_STEERING_SHIFT)
+#define EFCT_SCSI_WQ_STEERING_CPU (2 << EFCT_SCSI_WQ_STEERING_SHIFT)
+
+#define EFCT_SCSI_WQ_CLASS_SHIFT (20)
+#define EFCT_SCSI_WQ_CLASS_MASK (0xf << EFCT_SCSI_WQ_CLASS_SHIFT)
+#define EFCT_SCSI_WQ_CLASS(x) ((x & EFCT_SCSI_WQ_CLASS_MASK) << \
+ EFCT_SCSI_WQ_CLASS_SHIFT)
+
+#define EFCT_SCSI_WQ_CLASS_LOW_LATENCY 1
+
+struct efct_scsi_cmd_resp {
+ u8 scsi_status;
+ u16 scsi_status_qualifier;
+ u8 *response_data;
+ u32 response_data_length;
+ u8 *sense_data;
+ u32 sense_data_length;
+ int residual;
+ u32 response_wire_length;
+};
+
+struct efct_vport {
+ struct efct *efct;
+ bool is_vport;
+ struct fc_host_statistics fc_host_stats;
+ struct Scsi_Host *shost;
+ struct fc_vport *fc_vport;
+ u64 npiv_wwpn;
+ u64 npiv_wwnn;
+};
+
+/* Status values returned by IO callbacks */
+enum efct_scsi_io_status {
+ EFCT_SCSI_STATUS_GOOD = 0,
+ EFCT_SCSI_STATUS_ABORTED,
+ EFCT_SCSI_STATUS_ERROR,
+ EFCT_SCSI_STATUS_DIF_GUARD_ERR,
+ EFCT_SCSI_STATUS_DIF_REF_TAG_ERROR,
+ EFCT_SCSI_STATUS_DIF_APP_TAG_ERROR,
+ EFCT_SCSI_STATUS_DIF_UNKNOWN_ERROR,
+ EFCT_SCSI_STATUS_PROTOCOL_CRC_ERROR,
+ EFCT_SCSI_STATUS_NO_IO,
+ EFCT_SCSI_STATUS_ABORT_IN_PROGRESS,
+ EFCT_SCSI_STATUS_CHECK_RESPONSE,
+ EFCT_SCSI_STATUS_COMMAND_TIMEOUT,
+ EFCT_SCSI_STATUS_TIMEDOUT_AND_ABORTED,
+ EFCT_SCSI_STATUS_SHUTDOWN,
+ EFCT_SCSI_STATUS_NEXUS_LOST,
+};
+
+struct efct_node;
+struct efct_io;
+struct efc_node;
+struct efc_nport;
+
+/* Callback used by send_rd_data(), recv_wr_data(), send_resp() */
+typedef int (*efct_scsi_io_cb_t)(struct efct_io *io,
+ enum efct_scsi_io_status status,
+ u32 flags, void *arg);
+
+/* Callback used by send_rd_io(), send_wr_io() */
+typedef int (*efct_scsi_rsp_io_cb_t)(struct efct_io *io,
+ enum efct_scsi_io_status status,
+ struct efct_scsi_cmd_resp *rsp,
+ u32 flags, void *arg);
+
+/* efct_scsi_cb_t flags */
+#define EFCT_SCSI_IO_CMPL (1 << 0)
+/* IO completed, response sent */
+#define EFCT_SCSI_IO_CMPL_RSP_SENT (1 << 1)
+#define EFCT_SCSI_IO_ABORTED (1 << 2)
+
+/* efct_scsi_recv_tmf() request values */
+enum efct_scsi_tmf_cmd {
+ EFCT_SCSI_TMF_ABORT_TASK = 1,
+ EFCT_SCSI_TMF_QUERY_TASK_SET,
+ EFCT_SCSI_TMF_ABORT_TASK_SET,
+ EFCT_SCSI_TMF_CLEAR_TASK_SET,
+ EFCT_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT,
+ EFCT_SCSI_TMF_LOGICAL_UNIT_RESET,
+ EFCT_SCSI_TMF_CLEAR_ACA,
+ EFCT_SCSI_TMF_TARGET_RESET,
+};
+
+/* efct_scsi_send_tmf_resp() response values */
+enum efct_scsi_tmf_resp {
+ EFCT_SCSI_TMF_FUNCTION_COMPLETE = 1,
+ EFCT_SCSI_TMF_FUNCTION_SUCCEEDED,
+ EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND,
+ EFCT_SCSI_TMF_FUNCTION_REJECTED,
+ EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER,
+ EFCT_SCSI_TMF_SERVICE_DELIVERY,
+};
+
+struct efct_scsi_sgl {
+ uintptr_t addr;
+ uintptr_t dif_addr;
+ size_t len;
+};
+
+enum efct_scsi_io_role {
+ EFCT_SCSI_IO_ROLE_ORIGINATOR,
+ EFCT_SCSI_IO_ROLE_RESPONDER,
+};
+
+struct efct_io *
+efct_scsi_io_alloc(struct efct_node *node);
+void efct_scsi_io_free(struct efct_io *io);
+struct efct_io *efct_io_get_instance(struct efct *efct, u32 index);
+
+int efct_scsi_tgt_driver_init(void);
+int efct_scsi_tgt_driver_exit(void);
+int efct_scsi_tgt_new_device(struct efct *efct);
+int efct_scsi_tgt_del_device(struct efct *efct);
+int
+efct_scsi_tgt_new_nport(struct efc *efc, struct efc_nport *nport);
+void
+efct_scsi_tgt_del_nport(struct efc *efc, struct efc_nport *nport);
+
+int
+efct_scsi_new_initiator(struct efc *efc, struct efc_node *node);
+
+enum efct_scsi_del_initiator_reason {
+ EFCT_SCSI_INITIATOR_DELETED,
+ EFCT_SCSI_INITIATOR_MISSING,
+};
+
+int
+efct_scsi_del_initiator(struct efc *efc, struct efc_node *node, int reason);
+void
+efct_scsi_recv_cmd(struct efct_io *io, uint64_t lun, u8 *cdb, u32 cdb_len,
+ u32 flags);
+int
+efct_scsi_recv_tmf(struct efct_io *tmfio, u32 lun, enum efct_scsi_tmf_cmd cmd,
+ struct efct_io *abortio, u32 flags);
+int
+efct_scsi_send_rd_data(struct efct_io *io, u32 flags, struct efct_scsi_sgl *sgl,
+ u32 sgl_count, u64 wire_len, efct_scsi_io_cb_t cb, void *arg);
+int
+efct_scsi_recv_wr_data(struct efct_io *io, u32 flags, struct efct_scsi_sgl *sgl,
+ u32 sgl_count, u64 wire_len, efct_scsi_io_cb_t cb, void *arg);
+int
+efct_scsi_send_resp(struct efct_io *io, u32 flags,
+ struct efct_scsi_cmd_resp *rsp, efct_scsi_io_cb_t cb, void *arg);
+int
+efct_scsi_send_tmf_resp(struct efct_io *io, enum efct_scsi_tmf_resp rspcode,
+ u8 addl_rsp_info[3], efct_scsi_io_cb_t cb, void *arg);
+int
+efct_scsi_tgt_abort_io(struct efct_io *io, efct_scsi_io_cb_t cb, void *arg);
+
+void efct_scsi_io_complete(struct efct_io *io);
+
+int efct_scsi_reg_fc_transport(void);
+void efct_scsi_release_fc_transport(void);
+int efct_scsi_new_device(struct efct *efct);
+void efct_scsi_del_device(struct efct *efct);
+void _efct_scsi_io_free(struct kref *arg);
+
+int
+efct_scsi_del_vport(struct efct *efct, struct Scsi_Host *shost);
+struct efct_vport *
+efct_scsi_new_vport(struct efct *efct, struct device *dev);
+
+int efct_scsi_io_dispatch(struct efct_io *io, void *cb);
+int efct_scsi_io_dispatch_abort(struct efct_io *io, void *cb);
+void efct_scsi_check_pending(struct efct *efct);
+struct efct_io *
+efct_bls_send_rjt(struct efct_io *io, struct fc_frame_header *hdr);
+
+#endif /* __EFCT_SCSI_H__ */
diff --git a/drivers/scsi/elx/efct/efct_unsol.c b/drivers/scsi/elx/efct/efct_unsol.c
new file mode 100644
index 000000000000..e6addab66a60
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_unsol.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efct_driver.h"
+#include "efct_unsol.h"
+
+#define frame_printf(efct, hdr, fmt, ...) \
+ do { \
+ char s_id_text[16]; \
+ efc_node_fcid_display(ntoh24((hdr)->fh_s_id), \
+ s_id_text, sizeof(s_id_text)); \
+ efc_log_debug(efct, "[%06x.%s] %02x/%04x/%04x: " fmt, \
+ ntoh24((hdr)->fh_d_id), s_id_text, \
+ (hdr)->fh_r_ctl, be16_to_cpu((hdr)->fh_ox_id), \
+ be16_to_cpu((hdr)->fh_rx_id), ##__VA_ARGS__); \
+ } while (0)
+
+static struct efct_node *
+efct_node_find(struct efct *efct, u32 port_id, u32 node_id)
+{
+ struct efct_node *node;
+ u64 id = (u64)port_id << 32 | node_id;
+
+ /*
+ * During node shutdown, Lookup will be removed first,
+ * before announcing to backend. So, no new IOs will be allowed
+ */
+ /* Find a target node, given s_id and d_id */
+ node = xa_load(&efct->lookup, id);
+ if (node)
+ kref_get(&node->ref);
+
+ return node;
+}
+
+static int
+efct_dispatch_frame(struct efct *efct, struct efc_hw_sequence *seq)
+{
+ struct efct_node *node;
+ struct fc_frame_header *hdr;
+ u32 s_id, d_id;
+
+ hdr = seq->header->dma.virt;
+
+ /* extract the s_id and d_id */
+ s_id = ntoh24(hdr->fh_s_id);
+ d_id = ntoh24(hdr->fh_d_id);
+
+ if (!(hdr->fh_type == FC_TYPE_FCP || hdr->fh_type == FC_TYPE_BLS))
+ return -EIO;
+
+ if (hdr->fh_type == FC_TYPE_FCP) {
+ node = efct_node_find(efct, d_id, s_id);
+ if (!node) {
+ efc_log_err(efct,
+ "Node not found, drop cmd d_id:%x s_id:%x\n",
+ d_id, s_id);
+ efct_hw_sequence_free(&efct->hw, seq);
+ return 0;
+ }
+
+ efct_dispatch_fcp_cmd(node, seq);
+ } else {
+ node = efct_node_find(efct, d_id, s_id);
+ if (!node) {
+ efc_log_err(efct, "ABTS: Node not found, d_id:%x s_id:%x\n",
+ d_id, s_id);
+ return -EIO;
+ }
+
+ efc_log_err(efct, "Received ABTS for Node:%p\n", node);
+ efct_node_recv_abts_frame(node, seq);
+ }
+
+ kref_put(&node->ref, node->release);
+ efct_hw_sequence_free(&efct->hw, seq);
+ return 0;
+}
+
+int
+efct_unsolicited_cb(void *arg, struct efc_hw_sequence *seq)
+{
+ struct efct *efct = arg;
+
+ /* Process FCP command */
+ if (!efct_dispatch_frame(efct, seq))
+ return 0;
+
+ /* Forward frame to discovery lib */
+ efc_dispatch_frame(efct->efcport, seq);
+ return 0;
+}
+
+static int
+efct_fc_tmf_rejected_cb(struct efct_io *io,
+ enum efct_scsi_io_status scsi_status,
+ u32 flags, void *arg)
+{
+ efct_scsi_io_free(io);
+ return 0;
+}
+
+static void
+efct_dispatch_unsol_tmf(struct efct_io *io, u8 tm_flags, u32 lun)
+{
+ u32 i;
+ struct {
+ u32 mask;
+ enum efct_scsi_tmf_cmd cmd;
+ } tmflist[] = {
+ {FCP_TMF_ABT_TASK_SET, EFCT_SCSI_TMF_ABORT_TASK_SET},
+ {FCP_TMF_CLR_TASK_SET, EFCT_SCSI_TMF_CLEAR_TASK_SET},
+ {FCP_TMF_LUN_RESET, EFCT_SCSI_TMF_LOGICAL_UNIT_RESET},
+ {FCP_TMF_TGT_RESET, EFCT_SCSI_TMF_TARGET_RESET},
+ {FCP_TMF_CLR_ACA, EFCT_SCSI_TMF_CLEAR_ACA} };
+
+ io->exp_xfer_len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(tmflist); i++) {
+ if (tmflist[i].mask & tm_flags) {
+ io->tmf_cmd = tmflist[i].cmd;
+ efct_scsi_recv_tmf(io, lun, tmflist[i].cmd, NULL, 0);
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(tmflist)) {
+ /* Not handled */
+ efc_log_err(io->node->efct, "TMF x%x rejected\n", tm_flags);
+ efct_scsi_send_tmf_resp(io, EFCT_SCSI_TMF_FUNCTION_REJECTED,
+ NULL, efct_fc_tmf_rejected_cb, NULL);
+ }
+}
+
+static int
+efct_validate_fcp_cmd(struct efct *efct, struct efc_hw_sequence *seq)
+{
+ /*
+ * If we received less than FCP_CMND_IU bytes, assume that the frame is
+ * corrupted in some way and drop it.
+ * This was seen when jamming the FCTL
+ * fill bytes field.
+ */
+ if (seq->payload->dma.len < sizeof(struct fcp_cmnd)) {
+ struct fc_frame_header *fchdr = seq->header->dma.virt;
+
+ efc_log_debug(efct,
+ "drop ox_id %04x payload (%zd) less than (%zd)\n",
+ be16_to_cpu(fchdr->fh_ox_id),
+ seq->payload->dma.len, sizeof(struct fcp_cmnd));
+ return -EIO;
+ }
+ return 0;
+}
+
+static void
+efct_populate_io_fcp_cmd(struct efct_io *io, struct fcp_cmnd *cmnd,
+ struct fc_frame_header *fchdr, bool sit)
+{
+ io->init_task_tag = be16_to_cpu(fchdr->fh_ox_id);
+ /* note, tgt_task_tag, hw_tag set when HW io is allocated */
+ io->exp_xfer_len = be32_to_cpu(cmnd->fc_dl);
+ io->transferred = 0;
+
+ /* The upper 7 bits of CS_CTL is the frame priority thru the SAN.
+ * Our assertion here is, the priority given to a frame containing
+ * the FCP cmd should be the priority given to ALL frames contained
+ * in that IO. Thus we need to save the incoming CS_CTL here.
+ */
+ if (ntoh24(fchdr->fh_f_ctl) & FC_FC_RES_B17)
+ io->cs_ctl = fchdr->fh_cs_ctl;
+ else
+ io->cs_ctl = 0;
+
+ io->seq_init = sit;
+}
+
+static u32
+efct_get_flags_fcp_cmd(struct fcp_cmnd *cmnd)
+{
+ u32 flags = 0;
+
+ switch (cmnd->fc_pri_ta & FCP_PTA_MASK) {
+ case FCP_PTA_SIMPLE:
+ flags |= EFCT_SCSI_CMD_SIMPLE;
+ break;
+ case FCP_PTA_HEADQ:
+ flags |= EFCT_SCSI_CMD_HEAD_OF_QUEUE;
+ break;
+ case FCP_PTA_ORDERED:
+ flags |= EFCT_SCSI_CMD_ORDERED;
+ break;
+ case FCP_PTA_ACA:
+ flags |= EFCT_SCSI_CMD_ACA;
+ break;
+ }
+ if (cmnd->fc_flags & FCP_CFL_WRDATA)
+ flags |= EFCT_SCSI_CMD_DIR_IN;
+ if (cmnd->fc_flags & FCP_CFL_RDDATA)
+ flags |= EFCT_SCSI_CMD_DIR_OUT;
+
+ return flags;
+}
+
+static void
+efct_sframe_common_send_cb(void *arg, u8 *cqe, int status)
+{
+ struct efct_hw_send_frame_context *ctx = arg;
+ struct efct_hw *hw = ctx->hw;
+
+ /* Free WQ completion callback */
+ efct_hw_reqtag_free(hw, ctx->wqcb);
+
+ /* Free sequence */
+ efct_hw_sequence_free(hw, ctx->seq);
+}
+
+static int
+efct_sframe_common_send(struct efct_node *node,
+ struct efc_hw_sequence *seq,
+ enum fc_rctl r_ctl, u32 f_ctl,
+ u8 type, void *payload, u32 payload_len)
+{
+ struct efct *efct = node->efct;
+ struct efct_hw *hw = &efct->hw;
+ int rc = 0;
+ struct fc_frame_header *req_hdr = seq->header->dma.virt;
+ struct fc_frame_header hdr;
+ struct efct_hw_send_frame_context *ctx;
+
+ u32 heap_size = seq->payload->dma.size;
+ uintptr_t heap_phys_base = seq->payload->dma.phys;
+ u8 *heap_virt_base = seq->payload->dma.virt;
+ u32 heap_offset = 0;
+
+ /* Build the FC header reusing the RQ header DMA buffer */
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.fh_r_ctl = r_ctl;
+ /* send it back to whomever sent it to us */
+ memcpy(hdr.fh_d_id, req_hdr->fh_s_id, sizeof(hdr.fh_d_id));
+ memcpy(hdr.fh_s_id, req_hdr->fh_d_id, sizeof(hdr.fh_s_id));
+ hdr.fh_type = type;
+ hton24(hdr.fh_f_ctl, f_ctl);
+ hdr.fh_ox_id = req_hdr->fh_ox_id;
+ hdr.fh_rx_id = req_hdr->fh_rx_id;
+ hdr.fh_cs_ctl = 0;
+ hdr.fh_df_ctl = 0;
+ hdr.fh_seq_cnt = 0;
+ hdr.fh_parm_offset = 0;
+
+ /*
+ * send_frame_seq_id is an atomic, we just let it increment,
+ * while storing only the low 8 bits to hdr->seq_id
+ */
+ hdr.fh_seq_id = (u8)atomic_add_return(1, &hw->send_frame_seq_id);
+ hdr.fh_seq_id--;
+
+ /* Allocate and fill in the send frame request context */
+ ctx = (void *)(heap_virt_base + heap_offset);
+ heap_offset += sizeof(*ctx);
+ if (heap_offset > heap_size) {
+ efc_log_err(efct, "Fill send frame failed offset %d size %d\n",
+ heap_offset, heap_size);
+ return -EIO;
+ }
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ /* Save sequence */
+ ctx->seq = seq;
+
+ /* Allocate a response payload DMA buffer from the heap */
+ ctx->payload.phys = heap_phys_base + heap_offset;
+ ctx->payload.virt = heap_virt_base + heap_offset;
+ ctx->payload.size = payload_len;
+ ctx->payload.len = payload_len;
+ heap_offset += payload_len;
+ if (heap_offset > heap_size) {
+ efc_log_err(efct, "Fill send frame failed offset %d size %d\n",
+ heap_offset, heap_size);
+ return -EIO;
+ }
+
+ /* Copy the payload in */
+ memcpy(ctx->payload.virt, payload, payload_len);
+
+ /* Send */
+ rc = efct_hw_send_frame(&efct->hw, (void *)&hdr, FC_SOF_N3,
+ FC_EOF_T, &ctx->payload, ctx,
+ efct_sframe_common_send_cb, ctx);
+ if (rc)
+ efc_log_debug(efct, "efct_hw_send_frame failed: %d\n", rc);
+
+ return rc;
+}
+
+static int
+efct_sframe_send_fcp_rsp(struct efct_node *node, struct efc_hw_sequence *seq,
+ void *rsp, u32 rsp_len)
+{
+ return efct_sframe_common_send(node, seq, FC_RCTL_DD_CMD_STATUS,
+ FC_FC_EX_CTX |
+ FC_FC_LAST_SEQ |
+ FC_FC_END_SEQ |
+ FC_FC_SEQ_INIT,
+ FC_TYPE_FCP,
+ rsp, rsp_len);
+}
+
+static int
+efct_sframe_send_task_set_full_or_busy(struct efct_node *node,
+ struct efc_hw_sequence *seq)
+{
+ struct fcp_resp_with_ext fcprsp;
+ struct fcp_cmnd *fcpcmd = seq->payload->dma.virt;
+ int rc = 0;
+ unsigned long flags = 0;
+ struct efct *efct = node->efct;
+
+ /* construct task set full or busy response */
+ memset(&fcprsp, 0, sizeof(fcprsp));
+ spin_lock_irqsave(&node->active_ios_lock, flags);
+ fcprsp.resp.fr_status = list_empty(&node->active_ios) ?
+ SAM_STAT_BUSY : SAM_STAT_TASK_SET_FULL;
+ spin_unlock_irqrestore(&node->active_ios_lock, flags);
+ *((u32 *)&fcprsp.ext.fr_resid) = be32_to_cpu(fcpcmd->fc_dl);
+
+ /* send it using send_frame */
+ rc = efct_sframe_send_fcp_rsp(node, seq, &fcprsp, sizeof(fcprsp));
+ if (rc)
+ efc_log_debug(efct, "efct_sframe_send_fcp_rsp failed %d\n", rc);
+
+ return rc;
+}
+
+int
+efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq)
+{
+ struct efct *efct = node->efct;
+ struct fc_frame_header *fchdr = seq->header->dma.virt;
+ struct fcp_cmnd *cmnd = NULL;
+ struct efct_io *io = NULL;
+ u32 lun;
+
+ if (!seq->payload) {
+ efc_log_err(efct, "Sequence payload is NULL.\n");
+ return -EIO;
+ }
+
+ cmnd = seq->payload->dma.virt;
+
+ /* perform FCP_CMND validation check(s) */
+ if (efct_validate_fcp_cmd(efct, seq))
+ return -EIO;
+
+ lun = scsilun_to_int(&cmnd->fc_lun);
+ if (lun == U32_MAX)
+ return -EIO;
+
+ io = efct_scsi_io_alloc(node);
+ if (!io) {
+ int rc;
+
+ /* Use SEND_FRAME to send task set full or busy */
+ rc = efct_sframe_send_task_set_full_or_busy(node, seq);
+ if (rc)
+ efc_log_err(efct, "Failed to send busy task: %d\n", rc);
+
+ return rc;
+ }
+
+ io->hw_priv = seq->hw_priv;
+
+ io->app_id = 0;
+
+ /* RQ pair, if we got here, SIT=1 */
+ efct_populate_io_fcp_cmd(io, cmnd, fchdr, true);
+
+ if (cmnd->fc_tm_flags) {
+ efct_dispatch_unsol_tmf(io, cmnd->fc_tm_flags, lun);
+ } else {
+ u32 flags = efct_get_flags_fcp_cmd(cmnd);
+
+ if (cmnd->fc_flags & FCP_CFL_LEN_MASK) {
+ efc_log_err(efct, "Additional CDB not supported\n");
+ return -EIO;
+ }
+ /*
+ * Can return failure for things like task set full and UAs,
+ * no need to treat as a dropped frame if rc != 0
+ */
+ efct_scsi_recv_cmd(io, lun, cmnd->fc_cdb,
+ sizeof(cmnd->fc_cdb), flags);
+ }
+
+ return 0;
+}
+
+static int
+efct_process_abts(struct efct_io *io, struct fc_frame_header *hdr)
+{
+ struct efct_node *node = io->node;
+ struct efct *efct = io->efct;
+ u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
+ u16 rx_id = be16_to_cpu(hdr->fh_rx_id);
+ struct efct_io *abortio;
+
+ /* Find IO and attempt to take a reference on it */
+ abortio = efct_io_find_tgt_io(efct, node, ox_id, rx_id);
+
+ if (abortio) {
+ /* Got a reference on the IO. Hold it until backend
+ * is notified below
+ */
+ efc_log_info(node->efct, "Abort ox_id [%04x] rx_id [%04x]\n",
+ ox_id, rx_id);
+
+ /*
+ * Save the ox_id for the ABTS as the init_task_tag in our
+ * manufactured
+ * TMF IO object
+ */
+ io->display_name = "abts";
+ io->init_task_tag = ox_id;
+ /* don't set tgt_task_tag, don't want to confuse with XRI */
+
+ /*
+ * Save the rx_id from the ABTS as it is
+ * needed for the BLS response,
+ * regardless of the IO context's rx_id
+ */
+ io->abort_rx_id = rx_id;
+
+ /* Call target server command abort */
+ io->tmf_cmd = EFCT_SCSI_TMF_ABORT_TASK;
+ efct_scsi_recv_tmf(io, abortio->tgt_io.lun,
+ EFCT_SCSI_TMF_ABORT_TASK, abortio, 0);
+
+ /*
+ * Backend will have taken an additional
+ * reference on the IO if needed;
+ * done with current reference.
+ */
+ kref_put(&abortio->ref, abortio->release);
+ } else {
+ /*
+ * Either IO was not found or it has been
+ * freed between finding it
+ * and attempting to get the reference,
+ */
+ efc_log_info(node->efct, "Abort: ox_id [%04x], IO not found\n",
+ ox_id);
+
+ /* Send a BA_RJT */
+ efct_bls_send_rjt(io, hdr);
+ }
+ return 0;
+}
+
+int
+efct_node_recv_abts_frame(struct efct_node *node, struct efc_hw_sequence *seq)
+{
+ struct efct *efct = node->efct;
+ struct fc_frame_header *hdr = seq->header->dma.virt;
+ struct efct_io *io = NULL;
+
+ node->abort_cnt++;
+ io = efct_scsi_io_alloc(node);
+ if (io) {
+ io->hw_priv = seq->hw_priv;
+ /* If we got this far, SIT=1 */
+ io->seq_init = 1;
+
+ /* fill out generic fields */
+ io->efct = efct;
+ io->node = node;
+ io->cmd_tgt = true;
+
+ efct_process_abts(io, seq->header->dma.virt);
+ } else {
+ efc_log_err(efct,
+ "SCSI IO allocation failed for ABTS received ");
+ efc_log_err(efct, "s_id %06x d_id %06x ox_id %04x rx_id %04x\n",
+ ntoh24(hdr->fh_s_id), ntoh24(hdr->fh_d_id),
+ be16_to_cpu(hdr->fh_ox_id),
+ be16_to_cpu(hdr->fh_rx_id));
+ }
+
+ return 0;
+}
diff --git a/drivers/scsi/elx/efct/efct_unsol.h b/drivers/scsi/elx/efct/efct_unsol.h
new file mode 100644
index 000000000000..16d1e3ba1833
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_unsol.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#if !defined(__OSC_UNSOL_H__)
+#define __OSC_UNSOL_H__
+
+int
+efct_unsolicited_cb(void *arg, struct efc_hw_sequence *seq);
+int
+efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq);
+int
+efct_node_recv_abts_frame(struct efct_node *node, struct efc_hw_sequence *seq);
+
+#endif /* __OSC_UNSOL_H__ */
diff --git a/drivers/scsi/elx/efct/efct_xport.c b/drivers/scsi/elx/efct/efct_xport.c
new file mode 100644
index 000000000000..dfe05fab7b42
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_xport.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efct_driver.h"
+#include "efct_unsol.h"
+
+static struct dentry *efct_debugfs_root;
+static atomic_t efct_debugfs_count;
+
+static const struct scsi_host_template efct_template = {
+ .module = THIS_MODULE,
+ .name = EFCT_DRIVER_NAME,
+ .supported_mode = MODE_TARGET,
+};
+
+/* globals */
+static struct fc_function_template efct_xport_functions;
+static struct fc_function_template efct_vport_functions;
+
+static struct scsi_transport_template *efct_xport_fc_tt;
+static struct scsi_transport_template *efct_vport_fc_tt;
+
+struct efct_xport *
+efct_xport_alloc(struct efct *efct)
+{
+ struct efct_xport *xport;
+
+ xport = kzalloc(sizeof(*xport), GFP_KERNEL);
+ if (!xport)
+ return xport;
+
+ xport->efct = efct;
+ return xport;
+}
+
+static int
+efct_xport_init_debugfs(struct efct *efct)
+{
+ /* Setup efct debugfs root directory */
+ if (!efct_debugfs_root) {
+ efct_debugfs_root = debugfs_create_dir("efct", NULL);
+ atomic_set(&efct_debugfs_count, 0);
+ }
+
+ /* Create a directory for sessions in root */
+ if (!efct->sess_debugfs_dir) {
+ efct->sess_debugfs_dir = debugfs_create_dir("sessions",
+ efct_debugfs_root);
+ if (IS_ERR(efct->sess_debugfs_dir)) {
+ efc_log_err(efct,
+ "failed to create debugfs entry for sessions\n");
+ goto debugfs_fail;
+ }
+ atomic_inc(&efct_debugfs_count);
+ }
+
+ return 0;
+
+debugfs_fail:
+ return -EIO;
+}
+
+static void efct_xport_delete_debugfs(struct efct *efct)
+{
+ /* Remove session debugfs directory */
+ debugfs_remove(efct->sess_debugfs_dir);
+ efct->sess_debugfs_dir = NULL;
+ atomic_dec(&efct_debugfs_count);
+
+ if (atomic_read(&efct_debugfs_count) == 0) {
+ /* remove root debugfs directory */
+ debugfs_remove(efct_debugfs_root);
+ efct_debugfs_root = NULL;
+ }
+}
+
+int
+efct_xport_attach(struct efct_xport *xport)
+{
+ struct efct *efct = xport->efct;
+ int rc;
+
+ rc = efct_hw_setup(&efct->hw, efct, efct->pci);
+ if (rc) {
+ efc_log_err(efct, "%s: Can't setup hardware\n", efct->desc);
+ return rc;
+ }
+
+ efct_hw_parse_filter(&efct->hw, (void *)efct->filter_def);
+
+ xport->io_pool = efct_io_pool_create(efct, efct->hw.config.n_sgl);
+ if (!xport->io_pool) {
+ efc_log_err(efct, "Can't allocate IO pool\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+efct_xport_link_stats_cb(int status, u32 num_counters,
+ struct efct_hw_link_stat_counts *counters, void *arg)
+{
+ union efct_xport_stats_u *result = arg;
+
+ result->stats.link_stats.link_failure_error_count =
+ counters[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter;
+ result->stats.link_stats.loss_of_sync_error_count =
+ counters[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter;
+ result->stats.link_stats.primitive_sequence_error_count =
+ counters[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter;
+ result->stats.link_stats.invalid_transmission_word_error_count =
+ counters[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter;
+ result->stats.link_stats.crc_error_count =
+ counters[EFCT_HW_LINK_STAT_CRC_COUNT].counter;
+
+ complete(&result->stats.done);
+}
+
+static void
+efct_xport_host_stats_cb(int status, u32 num_counters,
+ struct efct_hw_host_stat_counts *counters, void *arg)
+{
+ union efct_xport_stats_u *result = arg;
+
+ result->stats.host_stats.transmit_kbyte_count =
+ counters[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter;
+ result->stats.host_stats.receive_kbyte_count =
+ counters[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter;
+ result->stats.host_stats.transmit_frame_count =
+ counters[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter;
+ result->stats.host_stats.receive_frame_count =
+ counters[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter;
+
+ complete(&result->stats.done);
+}
+
+static void
+efct_xport_async_link_stats_cb(int status, u32 num_counters,
+ struct efct_hw_link_stat_counts *counters,
+ void *arg)
+{
+ union efct_xport_stats_u *result = arg;
+
+ result->stats.link_stats.link_failure_error_count =
+ counters[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter;
+ result->stats.link_stats.loss_of_sync_error_count =
+ counters[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter;
+ result->stats.link_stats.primitive_sequence_error_count =
+ counters[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter;
+ result->stats.link_stats.invalid_transmission_word_error_count =
+ counters[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter;
+ result->stats.link_stats.crc_error_count =
+ counters[EFCT_HW_LINK_STAT_CRC_COUNT].counter;
+}
+
+static void
+efct_xport_async_host_stats_cb(int status, u32 num_counters,
+ struct efct_hw_host_stat_counts *counters,
+ void *arg)
+{
+ union efct_xport_stats_u *result = arg;
+
+ result->stats.host_stats.transmit_kbyte_count =
+ counters[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter;
+ result->stats.host_stats.receive_kbyte_count =
+ counters[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter;
+ result->stats.host_stats.transmit_frame_count =
+ counters[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter;
+ result->stats.host_stats.receive_frame_count =
+ counters[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter;
+}
+
+static void
+efct_xport_config_stats_timer(struct efct *efct);
+
+static void
+efct_xport_stats_timer_cb(struct timer_list *t)
+{
+ struct efct_xport *xport = timer_container_of(xport, t, stats_timer);
+ struct efct *efct = xport->efct;
+
+ efct_xport_config_stats_timer(efct);
+}
+
+static void
+efct_xport_config_stats_timer(struct efct *efct)
+{
+ u32 timeout = 3 * 1000;
+ struct efct_xport *xport = NULL;
+
+ if (!efct) {
+ pr_err("%s: failed to locate EFCT device\n", __func__);
+ return;
+ }
+
+ xport = efct->xport;
+ efct_hw_get_link_stats(&efct->hw, 0, 0, 0,
+ efct_xport_async_link_stats_cb,
+ &xport->fc_xport_stats);
+ efct_hw_get_host_stats(&efct->hw, 0, efct_xport_async_host_stats_cb,
+ &xport->fc_xport_stats);
+
+ timer_setup(&xport->stats_timer,
+ &efct_xport_stats_timer_cb, 0);
+ mod_timer(&xport->stats_timer,
+ jiffies + msecs_to_jiffies(timeout));
+}
+
+int
+efct_xport_initialize(struct efct_xport *xport)
+{
+ struct efct *efct = xport->efct;
+ int rc = 0;
+
+ /* Initialize io lists */
+ spin_lock_init(&xport->io_pending_lock);
+ INIT_LIST_HEAD(&xport->io_pending_list);
+ atomic_set(&xport->io_active_count, 0);
+ atomic_set(&xport->io_pending_count, 0);
+ atomic_set(&xport->io_total_free, 0);
+ atomic_set(&xport->io_total_pending, 0);
+ atomic_set(&xport->io_alloc_failed_count, 0);
+ atomic_set(&xport->io_pending_recursing, 0);
+
+ rc = efct_hw_init(&efct->hw);
+ if (rc) {
+ efc_log_err(efct, "efct_hw_init failure\n");
+ goto out;
+ }
+
+ rc = efct_scsi_tgt_new_device(efct);
+ if (rc) {
+ efc_log_err(efct, "failed to initialize target\n");
+ goto hw_init_out;
+ }
+
+ rc = efct_scsi_new_device(efct);
+ if (rc) {
+ efc_log_err(efct, "failed to initialize initiator\n");
+ goto tgt_dev_out;
+ }
+
+ /* Get FC link and host statistics perodically*/
+ efct_xport_config_stats_timer(efct);
+
+ efct_xport_init_debugfs(efct);
+
+ return rc;
+
+tgt_dev_out:
+ efct_scsi_tgt_del_device(efct);
+
+hw_init_out:
+ efct_hw_teardown(&efct->hw);
+out:
+ return rc;
+}
+
+int
+efct_xport_status(struct efct_xport *xport, enum efct_xport_status cmd,
+ union efct_xport_stats_u *result)
+{
+ int rc = 0;
+ struct efct *efct = NULL;
+ union efct_xport_stats_u value;
+
+ efct = xport->efct;
+
+ switch (cmd) {
+ case EFCT_XPORT_CONFIG_PORT_STATUS:
+ if (xport->configured_link_state == 0) {
+ /*
+ * Initial state is offline. configured_link_state is
+ * set to online explicitly when port is brought online
+ */
+ xport->configured_link_state = EFCT_XPORT_PORT_OFFLINE;
+ }
+ result->value = xport->configured_link_state;
+ break;
+
+ case EFCT_XPORT_PORT_STATUS:
+ /* Determine port status based on link speed. */
+ value.value = efct_hw_get_link_speed(&efct->hw);
+ if (value.value == 0)
+ result->value = EFCT_XPORT_PORT_OFFLINE;
+ else
+ result->value = EFCT_XPORT_PORT_ONLINE;
+ break;
+
+ case EFCT_XPORT_LINK_SPEED:
+ result->value = efct_hw_get_link_speed(&efct->hw);
+ break;
+
+ case EFCT_XPORT_LINK_STATISTICS:
+ memcpy((void *)result, &efct->xport->fc_xport_stats,
+ sizeof(union efct_xport_stats_u));
+ break;
+ case EFCT_XPORT_LINK_STAT_RESET: {
+ /* Create a completion to synchronize the stat reset process */
+ init_completion(&result->stats.done);
+
+ /* First reset the link stats */
+ rc = efct_hw_get_link_stats(&efct->hw, 0, 1, 1,
+ efct_xport_link_stats_cb, result);
+ if (rc)
+ break;
+
+ /* Wait for completion to be signaled when the cmd completes */
+ if (wait_for_completion_interruptible(&result->stats.done)) {
+ /* Undefined failure */
+ efc_log_debug(efct, "sem wait failed\n");
+ rc = -EIO;
+ break;
+ }
+
+ /* Next reset the host stats */
+ rc = efct_hw_get_host_stats(&efct->hw, 1,
+ efct_xport_host_stats_cb, result);
+
+ if (rc)
+ break;
+
+ /* Wait for completion to be signaled when the cmd completes */
+ if (wait_for_completion_interruptible(&result->stats.done)) {
+ /* Undefined failure */
+ efc_log_debug(efct, "sem wait failed\n");
+ rc = -EIO;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EIO;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+efct_get_link_supported_speeds(struct efct *efct)
+{
+ u32 supported_speeds = 0;
+ u32 link_module_type, i;
+ struct {
+ u32 lmt_speed;
+ u32 speed;
+ } supported_speed_list[] = {
+ {SLI4_LINK_MODULE_TYPE_1GB, FC_PORTSPEED_1GBIT},
+ {SLI4_LINK_MODULE_TYPE_2GB, FC_PORTSPEED_2GBIT},
+ {SLI4_LINK_MODULE_TYPE_4GB, FC_PORTSPEED_4GBIT},
+ {SLI4_LINK_MODULE_TYPE_8GB, FC_PORTSPEED_8GBIT},
+ {SLI4_LINK_MODULE_TYPE_16GB, FC_PORTSPEED_16GBIT},
+ {SLI4_LINK_MODULE_TYPE_32GB, FC_PORTSPEED_32GBIT},
+ {SLI4_LINK_MODULE_TYPE_64GB, FC_PORTSPEED_64GBIT},
+ {SLI4_LINK_MODULE_TYPE_128GB, FC_PORTSPEED_128GBIT},
+ };
+
+ link_module_type = sli_get_lmt(&efct->hw.sli);
+
+ /* populate link supported speeds */
+ for (i = 0; i < ARRAY_SIZE(supported_speed_list); i++) {
+ if (link_module_type & supported_speed_list[i].lmt_speed)
+ supported_speeds |= supported_speed_list[i].speed;
+ }
+
+ return supported_speeds;
+}
+
+int
+efct_scsi_new_device(struct efct *efct)
+{
+ struct Scsi_Host *shost = NULL;
+ int error = 0;
+ struct efct_vport *vport = NULL;
+
+ shost = scsi_host_alloc(&efct_template, sizeof(*vport));
+ if (!shost) {
+ efc_log_err(efct, "failed to allocate Scsi_Host struct\n");
+ return -ENOMEM;
+ }
+
+ /* save shost to initiator-client context */
+ efct->shost = shost;
+
+ /* save efct information to shost LLD-specific space */
+ vport = (struct efct_vport *)shost->hostdata;
+ vport->efct = efct;
+
+ /*
+ * Set initial can_queue value to the max SCSI IOs. This is the maximum
+ * global queue depth (as opposed to the per-LUN queue depth --
+ * .cmd_per_lun This may need to be adjusted for I+T mode.
+ */
+ shost->can_queue = efct->hw.config.n_io;
+ shost->max_cmd_len = 16; /* 16-byte CDBs */
+ shost->max_id = 0xffff;
+ shost->max_lun = 0xffffffff;
+
+ /*
+ * can only accept (from mid-layer) as many SGEs as we've
+ * pre-registered
+ */
+ shost->sg_tablesize = sli_get_max_sgl(&efct->hw.sli);
+
+ /* attach FC Transport template to shost */
+ shost->transportt = efct_xport_fc_tt;
+ efc_log_debug(efct, "transport template=%p\n", efct_xport_fc_tt);
+
+ /* get pci_dev structure and add host to SCSI ML */
+ error = scsi_add_host_with_dma(shost, &efct->pci->dev,
+ &efct->pci->dev);
+ if (error) {
+ efc_log_debug(efct, "failed scsi_add_host_with_dma\n");
+ return -EIO;
+ }
+
+ /* Set symbolic name for host port */
+ snprintf(fc_host_symbolic_name(shost),
+ sizeof(fc_host_symbolic_name(shost)),
+ "Emulex %s FV%s DV%s", efct->model,
+ efct->hw.sli.fw_name[0], EFCT_DRIVER_VERSION);
+
+ /* Set host port supported classes */
+ fc_host_supported_classes(shost) = FC_COS_CLASS3;
+
+ fc_host_supported_speeds(shost) = efct_get_link_supported_speeds(efct);
+
+ fc_host_node_name(shost) = efct_get_wwnn(&efct->hw);
+ fc_host_port_name(shost) = efct_get_wwpn(&efct->hw);
+ fc_host_max_npiv_vports(shost) = 128;
+
+ return 0;
+}
+
+struct scsi_transport_template *
+efct_attach_fc_transport(void)
+{
+ struct scsi_transport_template *efct_fc_template = NULL;
+
+ efct_fc_template = fc_attach_transport(&efct_xport_functions);
+
+ if (!efct_fc_template)
+ pr_err("failed to attach EFCT with fc transport\n");
+
+ return efct_fc_template;
+}
+
+struct scsi_transport_template *
+efct_attach_vport_fc_transport(void)
+{
+ struct scsi_transport_template *efct_fc_template = NULL;
+
+ efct_fc_template = fc_attach_transport(&efct_vport_functions);
+
+ if (!efct_fc_template)
+ pr_err("failed to attach EFCT with fc transport\n");
+
+ return efct_fc_template;
+}
+
+int
+efct_scsi_reg_fc_transport(void)
+{
+ /* attach to appropriate scsi_tranport_* module */
+ efct_xport_fc_tt = efct_attach_fc_transport();
+ if (!efct_xport_fc_tt) {
+ pr_err("%s: failed to attach to scsi_transport_*", __func__);
+ return -EIO;
+ }
+
+ efct_vport_fc_tt = efct_attach_vport_fc_transport();
+ if (!efct_vport_fc_tt) {
+ pr_err("%s: failed to attach to scsi_transport_*", __func__);
+ efct_release_fc_transport(efct_xport_fc_tt);
+ efct_xport_fc_tt = NULL;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void
+efct_scsi_release_fc_transport(void)
+{
+ /* detach from scsi_transport_* */
+ efct_release_fc_transport(efct_xport_fc_tt);
+ efct_xport_fc_tt = NULL;
+ if (efct_vport_fc_tt)
+ efct_release_fc_transport(efct_vport_fc_tt);
+
+ efct_vport_fc_tt = NULL;
+}
+
+void
+efct_xport_detach(struct efct_xport *xport)
+{
+ struct efct *efct = xport->efct;
+
+ /* free resources associated with target-server and initiator-client */
+ efct_scsi_tgt_del_device(efct);
+
+ efct_scsi_del_device(efct);
+
+ /*Shutdown FC Statistics timer*/
+ if (timer_pending(&xport->stats_timer))
+ timer_delete(&xport->stats_timer);
+
+ efct_hw_teardown(&efct->hw);
+
+ efct_xport_delete_debugfs(efct);
+}
+
+static void
+efct_xport_domain_free_cb(struct efc *efc, void *arg)
+{
+ struct completion *done = arg;
+
+ complete(done);
+}
+
+int
+efct_xport_control(struct efct_xport *xport, enum efct_xport_ctrl cmd, ...)
+{
+ u32 rc = 0;
+ struct efct *efct = NULL;
+ va_list argp;
+
+ efct = xport->efct;
+
+ switch (cmd) {
+ case EFCT_XPORT_PORT_ONLINE: {
+ /* Bring the port on-line */
+ rc = efct_hw_port_control(&efct->hw, EFCT_HW_PORT_INIT, 0,
+ NULL, NULL);
+ if (rc)
+ efc_log_err(efct,
+ "%s: Can't init port\n", efct->desc);
+ else
+ xport->configured_link_state = cmd;
+ break;
+ }
+ case EFCT_XPORT_PORT_OFFLINE: {
+ if (efct_hw_port_control(&efct->hw, EFCT_HW_PORT_SHUTDOWN, 0,
+ NULL, NULL))
+ efc_log_err(efct, "port shutdown failed\n");
+ else
+ xport->configured_link_state = cmd;
+ break;
+ }
+
+ case EFCT_XPORT_SHUTDOWN: {
+ struct completion done;
+ unsigned long timeout;
+
+ /* if a PHYSDEV reset was performed (e.g. hw dump), will affect
+ * all PCI functions; orderly shutdown won't work,
+ * just force free
+ */
+ if (sli_reset_required(&efct->hw.sli)) {
+ struct efc_domain *domain = efct->efcport->domain;
+
+ if (domain)
+ efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_LOST,
+ domain);
+ } else {
+ efct_hw_port_control(&efct->hw, EFCT_HW_PORT_SHUTDOWN,
+ 0, NULL, NULL);
+ }
+
+ init_completion(&done);
+
+ efc_register_domain_free_cb(efct->efcport,
+ efct_xport_domain_free_cb, &done);
+
+ efc_log_debug(efct, "Waiting %d seconds for domain shutdown\n",
+ (EFC_SHUTDOWN_TIMEOUT_USEC / 1000000));
+
+ timeout = usecs_to_jiffies(EFC_SHUTDOWN_TIMEOUT_USEC);
+ if (!wait_for_completion_timeout(&done, timeout)) {
+ efc_log_err(efct, "Domain shutdown timed out!!\n");
+ WARN_ON(1);
+ }
+
+ efc_register_domain_free_cb(efct->efcport, NULL, NULL);
+
+ /* Free up any saved virtual ports */
+ efc_vport_del_all(efct->efcport);
+ break;
+ }
+
+ /*
+ * Set wwnn for the port. This will be used instead of the default
+ * provided by FW.
+ */
+ case EFCT_XPORT_WWNN_SET: {
+ u64 wwnn;
+
+ /* Retrieve arguments */
+ va_start(argp, cmd);
+ wwnn = va_arg(argp, uint64_t);
+ va_end(argp);
+
+ efc_log_debug(efct, " WWNN %016llx\n", wwnn);
+ xport->req_wwnn = wwnn;
+
+ break;
+ }
+ /*
+ * Set wwpn for the port. This will be used instead of the default
+ * provided by FW.
+ */
+ case EFCT_XPORT_WWPN_SET: {
+ u64 wwpn;
+
+ /* Retrieve arguments */
+ va_start(argp, cmd);
+ wwpn = va_arg(argp, uint64_t);
+ va_end(argp);
+
+ efc_log_debug(efct, " WWPN %016llx\n", wwpn);
+ xport->req_wwpn = wwpn;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ return rc;
+}
+
+void
+efct_xport_free(struct efct_xport *xport)
+{
+ if (xport) {
+ efct_io_pool_free(xport->io_pool);
+
+ kfree(xport);
+ }
+}
+
+void
+efct_release_fc_transport(struct scsi_transport_template *transport_template)
+{
+ if (transport_template)
+ pr_err("releasing transport layer\n");
+
+ /* Releasing FC transport */
+ fc_release_transport(transport_template);
+}
+
+static void
+efct_xport_remove_host(struct Scsi_Host *shost)
+{
+ fc_remove_host(shost);
+}
+
+void
+efct_scsi_del_device(struct efct *efct)
+{
+ if (!efct->shost)
+ return;
+
+ efc_log_debug(efct, "Unregistering with Transport Layer\n");
+ efct_xport_remove_host(efct->shost);
+ efc_log_debug(efct, "Unregistering with SCSI Midlayer\n");
+ scsi_remove_host(efct->shost);
+ scsi_host_put(efct->shost);
+ efct->shost = NULL;
+}
+
+static void
+efct_get_host_port_id(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+ struct efct *efct = vport->efct;
+ struct efc *efc = efct->efcport;
+ struct efc_nport *nport;
+
+ if (efc->domain && efc->domain->nport) {
+ nport = efc->domain->nport;
+ fc_host_port_id(shost) = nport->fc_id;
+ }
+}
+
+static void
+efct_get_host_port_type(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+ struct efct *efct = vport->efct;
+ struct efc *efc = efct->efcport;
+ int type = FC_PORTTYPE_UNKNOWN;
+
+ if (efc->domain && efc->domain->nport) {
+ if (efc->domain->is_loop) {
+ type = FC_PORTTYPE_LPORT;
+ } else {
+ struct efc_nport *nport = efc->domain->nport;
+
+ if (nport->is_vport)
+ type = FC_PORTTYPE_NPIV;
+ else if (nport->topology == EFC_NPORT_TOPO_P2P)
+ type = FC_PORTTYPE_PTP;
+ else if (nport->topology == EFC_NPORT_TOPO_UNKNOWN)
+ type = FC_PORTTYPE_UNKNOWN;
+ else
+ type = FC_PORTTYPE_NPORT;
+ }
+ }
+ fc_host_port_type(shost) = type;
+}
+
+static void
+efct_get_host_vport_type(struct Scsi_Host *shost)
+{
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+}
+
+static void
+efct_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+ struct efct *efct = vport->efct;
+ union efct_xport_stats_u status;
+ int rc;
+
+ rc = efct_xport_status(efct->xport, EFCT_XPORT_PORT_STATUS, &status);
+ if ((!rc) && (status.value == EFCT_XPORT_PORT_ONLINE))
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+}
+
+static void
+efct_get_host_speed(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+ struct efct *efct = vport->efct;
+ struct efc *efc = efct->efcport;
+ union efct_xport_stats_u speed;
+ u32 fc_speed = FC_PORTSPEED_UNKNOWN;
+ int rc;
+
+ if (!efc->domain || !efc->domain->nport) {
+ fc_host_speed(shost) = fc_speed;
+ return;
+ }
+
+ rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_SPEED, &speed);
+ if (!rc) {
+ switch (speed.value) {
+ case 1000:
+ fc_speed = FC_PORTSPEED_1GBIT;
+ break;
+ case 2000:
+ fc_speed = FC_PORTSPEED_2GBIT;
+ break;
+ case 4000:
+ fc_speed = FC_PORTSPEED_4GBIT;
+ break;
+ case 8000:
+ fc_speed = FC_PORTSPEED_8GBIT;
+ break;
+ case 10000:
+ fc_speed = FC_PORTSPEED_10GBIT;
+ break;
+ case 16000:
+ fc_speed = FC_PORTSPEED_16GBIT;
+ break;
+ case 32000:
+ fc_speed = FC_PORTSPEED_32GBIT;
+ break;
+ case 64000:
+ fc_speed = FC_PORTSPEED_64GBIT;
+ break;
+ case 128000:
+ fc_speed = FC_PORTSPEED_128GBIT;
+ break;
+ }
+ }
+
+ fc_host_speed(shost) = fc_speed;
+}
+
+static void
+efct_get_host_fabric_name(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+ struct efct *efct = vport->efct;
+ struct efc *efc = efct->efcport;
+
+ if (efc->domain) {
+ struct fc_els_flogi *sp =
+ (struct fc_els_flogi *)
+ efc->domain->flogi_service_params;
+
+ fc_host_fabric_name(shost) = be64_to_cpu(sp->fl_wwnn);
+ }
+}
+
+static struct fc_host_statistics *
+efct_get_stats(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+ struct efct *efct = vport->efct;
+ union efct_xport_stats_u stats;
+ struct efct_xport *xport = efct->xport;
+ int rc = 0;
+
+ rc = efct_xport_status(xport, EFCT_XPORT_LINK_STATISTICS, &stats);
+ if (rc) {
+ pr_err("efct_xport_status returned non 0 - %d\n", rc);
+ return NULL;
+ }
+
+ vport->fc_host_stats.loss_of_sync_count =
+ stats.stats.link_stats.loss_of_sync_error_count;
+ vport->fc_host_stats.link_failure_count =
+ stats.stats.link_stats.link_failure_error_count;
+ vport->fc_host_stats.prim_seq_protocol_err_count =
+ stats.stats.link_stats.primitive_sequence_error_count;
+ vport->fc_host_stats.invalid_tx_word_count =
+ stats.stats.link_stats.invalid_transmission_word_error_count;
+ vport->fc_host_stats.invalid_crc_count =
+ stats.stats.link_stats.crc_error_count;
+ /* mbox returns kbyte count so we need to convert to words */
+ vport->fc_host_stats.tx_words =
+ stats.stats.host_stats.transmit_kbyte_count * 256;
+ /* mbox returns kbyte count so we need to convert to words */
+ vport->fc_host_stats.rx_words =
+ stats.stats.host_stats.receive_kbyte_count * 256;
+ vport->fc_host_stats.tx_frames =
+ stats.stats.host_stats.transmit_frame_count;
+ vport->fc_host_stats.rx_frames =
+ stats.stats.host_stats.receive_frame_count;
+
+ vport->fc_host_stats.fcp_input_requests =
+ xport->fcp_stats.input_requests;
+ vport->fc_host_stats.fcp_output_requests =
+ xport->fcp_stats.output_requests;
+ vport->fc_host_stats.fcp_output_megabytes =
+ xport->fcp_stats.output_bytes >> 20;
+ vport->fc_host_stats.fcp_input_megabytes =
+ xport->fcp_stats.input_bytes >> 20;
+ vport->fc_host_stats.fcp_control_requests =
+ xport->fcp_stats.control_requests;
+
+ return &vport->fc_host_stats;
+}
+
+static void
+efct_reset_stats(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+ struct efct *efct = vport->efct;
+ /* argument has no purpose for this action */
+ union efct_xport_stats_u dummy;
+ int rc;
+
+ rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_STAT_RESET, &dummy);
+ if (rc)
+ pr_err("efct_xport_status returned non 0 - %d\n", rc);
+}
+
+static int
+efct_issue_lip(struct Scsi_Host *shost)
+{
+ struct efct_vport *vport =
+ shost ? (struct efct_vport *)shost->hostdata : NULL;
+ struct efct *efct = vport ? vport->efct : NULL;
+
+ if (!shost || !vport || !efct) {
+ pr_err("%s: shost=%p vport=%p efct=%p\n", __func__,
+ shost, vport, efct);
+ return -EPERM;
+ }
+
+ /*
+ * Bring the link down gracefully then re-init the link.
+ * The firmware will re-initialize the Fibre Channel interface as
+ * required. It does not issue a LIP.
+ */
+
+ if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_OFFLINE))
+ efc_log_debug(efct, "EFCT_XPORT_PORT_OFFLINE failed\n");
+
+ if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_ONLINE))
+ efc_log_debug(efct, "EFCT_XPORT_PORT_ONLINE failed\n");
+
+ return 0;
+}
+
+struct efct_vport *
+efct_scsi_new_vport(struct efct *efct, struct device *dev)
+{
+ struct Scsi_Host *shost = NULL;
+ int error = 0;
+ struct efct_vport *vport = NULL;
+
+ shost = scsi_host_alloc(&efct_template, sizeof(*vport));
+ if (!shost) {
+ efc_log_err(efct, "failed to allocate Scsi_Host struct\n");
+ return NULL;
+ }
+
+ /* save efct information to shost LLD-specific space */
+ vport = (struct efct_vport *)shost->hostdata;
+ vport->efct = efct;
+ vport->is_vport = true;
+
+ shost->can_queue = efct->hw.config.n_io;
+ shost->max_cmd_len = 16; /* 16-byte CDBs */
+ shost->max_id = 0xffff;
+ shost->max_lun = 0xffffffff;
+
+ /* can only accept (from mid-layer) as many SGEs as we've pre-regited*/
+ shost->sg_tablesize = sli_get_max_sgl(&efct->hw.sli);
+
+ /* attach FC Transport template to shost */
+ shost->transportt = efct_vport_fc_tt;
+ efc_log_debug(efct, "vport transport template=%p\n",
+ efct_vport_fc_tt);
+
+ /* get pci_dev structure and add host to SCSI ML */
+ error = scsi_add_host_with_dma(shost, dev, &efct->pci->dev);
+ if (error) {
+ efc_log_debug(efct, "failed scsi_add_host_with_dma\n");
+ return NULL;
+ }
+
+ /* Set symbolic name for host port */
+ snprintf(fc_host_symbolic_name(shost),
+ sizeof(fc_host_symbolic_name(shost)),
+ "Emulex %s FV%s DV%s", efct->model, efct->hw.sli.fw_name[0],
+ EFCT_DRIVER_VERSION);
+
+ /* Set host port supported classes */
+ fc_host_supported_classes(shost) = FC_COS_CLASS3;
+
+ fc_host_supported_speeds(shost) = efct_get_link_supported_speeds(efct);
+ vport->shost = shost;
+
+ return vport;
+}
+
+int efct_scsi_del_vport(struct efct *efct, struct Scsi_Host *shost)
+{
+ if (shost) {
+ efc_log_debug(efct,
+ "Unregistering vport with Transport Layer\n");
+ efct_xport_remove_host(shost);
+ efc_log_debug(efct, "Unregistering vport with SCSI Midlayer\n");
+ scsi_remove_host(shost);
+ scsi_host_put(shost);
+ return 0;
+ }
+ return -EIO;
+}
+
+static int
+efct_vport_create(struct fc_vport *fc_vport, bool disable)
+{
+ struct Scsi_Host *shost = fc_vport ? fc_vport->shost : NULL;
+ struct efct_vport *pport = shost ?
+ (struct efct_vport *)shost->hostdata :
+ NULL;
+ struct efct *efct = pport ? pport->efct : NULL;
+ struct efct_vport *vport = NULL;
+
+ if (!fc_vport || !shost || !efct)
+ goto fail;
+
+ vport = efct_scsi_new_vport(efct, &fc_vport->dev);
+ if (!vport) {
+ efc_log_err(efct, "failed to create vport\n");
+ goto fail;
+ }
+
+ vport->fc_vport = fc_vport;
+ vport->npiv_wwpn = fc_vport->port_name;
+ vport->npiv_wwnn = fc_vport->node_name;
+ fc_host_node_name(vport->shost) = vport->npiv_wwnn;
+ fc_host_port_name(vport->shost) = vport->npiv_wwpn;
+ *(struct efct_vport **)fc_vport->dd_data = vport;
+
+ return 0;
+
+fail:
+ return -EIO;
+}
+
+static int
+efct_vport_delete(struct fc_vport *fc_vport)
+{
+ struct efct_vport *vport = *(struct efct_vport **)fc_vport->dd_data;
+ struct Scsi_Host *shost = vport ? vport->shost : NULL;
+ struct efct *efct = vport ? vport->efct : NULL;
+ int rc;
+
+ rc = efct_scsi_del_vport(efct, shost);
+
+ if (rc)
+ pr_err("%s: vport delete failed\n", __func__);
+
+ return rc;
+}
+
+static int
+efct_vport_disable(struct fc_vport *fc_vport, bool disable)
+{
+ return 0;
+}
+
+static struct fc_function_template efct_xport_functions = {
+ .get_host_port_id = efct_get_host_port_id,
+ .get_host_port_type = efct_get_host_port_type,
+ .get_host_port_state = efct_get_host_port_state,
+ .get_host_speed = efct_get_host_speed,
+ .get_host_fabric_name = efct_get_host_fabric_name,
+
+ .get_fc_host_stats = efct_get_stats,
+ .reset_fc_host_stats = efct_reset_stats,
+
+ .issue_fc_host_lip = efct_issue_lip,
+
+ .vport_disable = efct_vport_disable,
+
+ /* allocation lengths for host-specific data */
+ .dd_fcrport_size = sizeof(struct efct_rport_data),
+ .dd_fcvport_size = 128, /* should be sizeof(...) */
+
+ /* remote port fixed attributes */
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .show_rport_dev_loss_tmo = 1,
+
+ /* target dynamic attributes */
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+
+ /* host fixed attributes */
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_supported_speeds = 1,
+ .show_host_maxframe_size = 1,
+
+ /* host dynamic attributes */
+ .show_host_port_id = 1,
+ .show_host_port_type = 1,
+ .show_host_port_state = 1,
+ /* active_fc4s is shown but doesn't change (thus no get function) */
+ .show_host_active_fc4s = 1,
+ .show_host_speed = 1,
+ .show_host_fabric_name = 1,
+ .show_host_symbolic_name = 1,
+ .vport_create = efct_vport_create,
+ .vport_delete = efct_vport_delete,
+};
+
+static struct fc_function_template efct_vport_functions = {
+ .get_host_port_id = efct_get_host_port_id,
+ .get_host_port_type = efct_get_host_vport_type,
+ .get_host_port_state = efct_get_host_port_state,
+ .get_host_speed = efct_get_host_speed,
+ .get_host_fabric_name = efct_get_host_fabric_name,
+
+ .get_fc_host_stats = efct_get_stats,
+ .reset_fc_host_stats = efct_reset_stats,
+
+ .issue_fc_host_lip = efct_issue_lip,
+
+ /* allocation lengths for host-specific data */
+ .dd_fcrport_size = sizeof(struct efct_rport_data),
+ .dd_fcvport_size = 128, /* should be sizeof(...) */
+
+ /* remote port fixed attributes */
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .show_rport_dev_loss_tmo = 1,
+
+ /* target dynamic attributes */
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+
+ /* host fixed attributes */
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_supported_speeds = 1,
+ .show_host_maxframe_size = 1,
+
+ /* host dynamic attributes */
+ .show_host_port_id = 1,
+ .show_host_port_type = 1,
+ .show_host_port_state = 1,
+ /* active_fc4s is shown but doesn't change (thus no get function) */
+ .show_host_active_fc4s = 1,
+ .show_host_speed = 1,
+ .show_host_fabric_name = 1,
+ .show_host_symbolic_name = 1,
+};
diff --git a/drivers/scsi/elx/efct/efct_xport.h b/drivers/scsi/elx/efct/efct_xport.h
new file mode 100644
index 000000000000..89f3c20ecb59
--- /dev/null
+++ b/drivers/scsi/elx/efct/efct_xport.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#if !defined(__EFCT_XPORT_H__)
+#define __EFCT_XPORT_H__
+
+enum efct_xport_ctrl {
+ EFCT_XPORT_PORT_ONLINE = 1,
+ EFCT_XPORT_PORT_OFFLINE,
+ EFCT_XPORT_SHUTDOWN,
+ EFCT_XPORT_POST_NODE_EVENT,
+ EFCT_XPORT_WWNN_SET,
+ EFCT_XPORT_WWPN_SET,
+};
+
+enum efct_xport_status {
+ EFCT_XPORT_PORT_STATUS,
+ EFCT_XPORT_CONFIG_PORT_STATUS,
+ EFCT_XPORT_LINK_SPEED,
+ EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+ EFCT_XPORT_LINK_STATISTICS,
+ EFCT_XPORT_LINK_STAT_RESET,
+ EFCT_XPORT_IS_QUIESCED
+};
+
+struct efct_xport_link_stats {
+ bool rec;
+ bool gec;
+ bool w02of;
+ bool w03of;
+ bool w04of;
+ bool w05of;
+ bool w06of;
+ bool w07of;
+ bool w08of;
+ bool w09of;
+ bool w10of;
+ bool w11of;
+ bool w12of;
+ bool w13of;
+ bool w14of;
+ bool w15of;
+ bool w16of;
+ bool w17of;
+ bool w18of;
+ bool w19of;
+ bool w20of;
+ bool w21of;
+ bool clrc;
+ bool clof1;
+ u32 link_failure_error_count;
+ u32 loss_of_sync_error_count;
+ u32 loss_of_signal_error_count;
+ u32 primitive_sequence_error_count;
+ u32 invalid_transmission_word_error_count;
+ u32 crc_error_count;
+ u32 primitive_sequence_event_timeout_count;
+ u32 elastic_buffer_overrun_error_count;
+ u32 arbitration_fc_al_timeout_count;
+ u32 advertised_receive_bufftor_to_buffer_credit;
+ u32 current_receive_buffer_to_buffer_credit;
+ u32 advertised_transmit_buffer_to_buffer_credit;
+ u32 current_transmit_buffer_to_buffer_credit;
+ u32 received_eofa_count;
+ u32 received_eofdti_count;
+ u32 received_eofni_count;
+ u32 received_soff_count;
+ u32 received_dropped_no_aer_count;
+ u32 received_dropped_no_available_rpi_resources_count;
+ u32 received_dropped_no_available_xri_resources_count;
+};
+
+struct efct_xport_host_stats {
+ bool cc;
+ u32 transmit_kbyte_count;
+ u32 receive_kbyte_count;
+ u32 transmit_frame_count;
+ u32 receive_frame_count;
+ u32 transmit_sequence_count;
+ u32 receive_sequence_count;
+ u32 total_exchanges_originator;
+ u32 total_exchanges_responder;
+ u32 receive_p_bsy_count;
+ u32 receive_f_bsy_count;
+ u32 dropped_frames_due_to_no_rq_buffer_count;
+ u32 empty_rq_timeout_count;
+ u32 dropped_frames_due_to_no_xri_count;
+ u32 empty_xri_pool_count;
+};
+
+struct efct_xport_host_statistics {
+ struct completion done;
+ struct efct_xport_link_stats link_stats;
+ struct efct_xport_host_stats host_stats;
+};
+
+union efct_xport_stats_u {
+ u32 value;
+ struct efct_xport_host_statistics stats;
+};
+
+struct efct_xport_fcp_stats {
+ u64 input_bytes;
+ u64 output_bytes;
+ u64 input_requests;
+ u64 output_requests;
+ u64 control_requests;
+};
+
+struct efct_xport {
+ struct efct *efct;
+ /* wwpn requested by user for primary nport */
+ u64 req_wwpn;
+ /* wwnn requested by user for primary nport */
+ u64 req_wwnn;
+
+ /* Nodes */
+ /* number of allocated nodes */
+ u32 nodes_count;
+ /* used to track how often IO pool is empty */
+ atomic_t io_alloc_failed_count;
+ /* array of pointers to nodes */
+ struct efc_node **nodes;
+
+ /* Io pool and counts */
+ /* pointer to IO pool */
+ struct efct_io_pool *io_pool;
+ /* lock for io_pending_list */
+ spinlock_t io_pending_lock;
+ /* list of IOs waiting for HW resources
+ * lock: xport->io_pending_lock
+ * link: efct_io_s->io_pending_link
+ */
+ struct list_head io_pending_list;
+ /* count of totals IOS allocated */
+ atomic_t io_total_alloc;
+ /* count of totals IOS free'd */
+ atomic_t io_total_free;
+ /* count of totals IOS that were pended */
+ atomic_t io_total_pending;
+ /* count of active IOS */
+ atomic_t io_active_count;
+ /* count of pending IOS */
+ atomic_t io_pending_count;
+ /* non-zero if efct_scsi_check_pending is executing */
+ atomic_t io_pending_recursing;
+
+ /* Port */
+ /* requested link state */
+ u32 configured_link_state;
+
+ /* Timer for Statistics */
+ struct timer_list stats_timer;
+ union efct_xport_stats_u fc_xport_stats;
+ struct efct_xport_fcp_stats fcp_stats;
+};
+
+struct efct_rport_data {
+ struct efc_node *node;
+};
+
+struct efct_xport *
+efct_xport_alloc(struct efct *efct);
+int
+efct_xport_attach(struct efct_xport *xport);
+int
+efct_xport_initialize(struct efct_xport *xport);
+void
+efct_xport_detach(struct efct_xport *xport);
+int
+efct_xport_control(struct efct_xport *xport, enum efct_xport_ctrl cmd, ...);
+int
+efct_xport_status(struct efct_xport *xport, enum efct_xport_status cmd,
+ union efct_xport_stats_u *result);
+void
+efct_xport_free(struct efct_xport *xport);
+
+struct scsi_transport_template *efct_attach_fc_transport(void);
+struct scsi_transport_template *efct_attach_vport_fc_transport(void);
+void
+efct_release_fc_transport(struct scsi_transport_template *transport_template);
+
+#endif /* __EFCT_XPORT_H__ */
diff --git a/drivers/scsi/elx/include/efc_common.h b/drivers/scsi/elx/include/efc_common.h
new file mode 100644
index 000000000000..8d57f69ace0a
--- /dev/null
+++ b/drivers/scsi/elx/include/efc_common.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#ifndef __EFC_COMMON_H__
+#define __EFC_COMMON_H__
+
+#include <linux/pci.h>
+
+struct efc_dma {
+ void *virt;
+ void *alloc;
+ dma_addr_t phys;
+
+ size_t size;
+ size_t len;
+ struct pci_dev *pdev;
+};
+
+#define efc_log_crit(efc, fmt, args...) \
+ dev_crit(&((efc)->pci)->dev, fmt, ##args)
+
+#define efc_log_err(efc, fmt, args...) \
+ dev_err(&((efc)->pci)->dev, fmt, ##args)
+
+#define efc_log_warn(efc, fmt, args...) \
+ dev_warn(&((efc)->pci)->dev, fmt, ##args)
+
+#define efc_log_info(efc, fmt, args...) \
+ dev_info(&((efc)->pci)->dev, fmt, ##args)
+
+#define efc_log_debug(efc, fmt, args...) \
+ dev_dbg(&((efc)->pci)->dev, fmt, ##args)
+
+#endif /* __EFC_COMMON_H__ */
diff --git a/drivers/scsi/elx/libefc/efc.h b/drivers/scsi/elx/libefc/efc.h
new file mode 100644
index 000000000000..468ff3cc9c00
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#ifndef __EFC_H__
+#define __EFC_H__
+
+#include "../include/efc_common.h"
+#include "efclib.h"
+#include "efc_sm.h"
+#include "efc_cmds.h"
+#include "efc_domain.h"
+#include "efc_nport.h"
+#include "efc_node.h"
+#include "efc_fabric.h"
+#include "efc_device.h"
+#include "efc_els.h"
+
+#define EFC_MAX_REMOTE_NODES 2048
+#define NODE_SPARAMS_SIZE 256
+
+enum efc_scsi_del_initiator_reason {
+ EFC_SCSI_INITIATOR_DELETED,
+ EFC_SCSI_INITIATOR_MISSING,
+};
+
+enum efc_scsi_del_target_reason {
+ EFC_SCSI_TARGET_DELETED,
+ EFC_SCSI_TARGET_MISSING,
+};
+
+#define EFC_FC_ELS_DEFAULT_RETRIES 3
+
+#define domain_sm_trace(domain) \
+ efc_log_debug(domain->efc, "[domain:%s] %-20s %-20s\n", \
+ domain->display_name, __func__, efc_sm_event_name(evt)) \
+
+#define domain_trace(domain, fmt, ...) \
+ efc_log_debug(domain->efc, \
+ "[%s]" fmt, domain->display_name, ##__VA_ARGS__) \
+
+#define node_sm_trace() \
+ efc_log_debug(node->efc, "[%s] %-20s %-20s\n", \
+ node->display_name, __func__, efc_sm_event_name(evt)) \
+
+#define nport_sm_trace(nport) \
+ efc_log_debug(nport->efc, \
+ "[%s] %-20s %-20s\n", nport->display_name, __func__, efc_sm_event_name(evt)) \
+
+#endif /* __EFC_H__ */
diff --git a/drivers/scsi/elx/libefc/efc_cmds.c b/drivers/scsi/elx/libefc/efc_cmds.c
new file mode 100644
index 000000000000..da4ac8a4ce12
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_cmds.c
@@ -0,0 +1,782 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efclib.h"
+#include "../libefc_sli/sli4.h"
+#include "efc_cmds.h"
+#include "efc_sm.h"
+
+static void
+efc_nport_free_resources(struct efc_nport *nport, int evt, void *data)
+{
+ struct efc *efc = nport->efc;
+
+ /* Clear the nport attached flag */
+ nport->attached = false;
+
+ /* Free the service parameters buffer */
+ if (nport->dma.virt) {
+ dma_free_coherent(&efc->pci->dev, nport->dma.size,
+ nport->dma.virt, nport->dma.phys);
+ memset(&nport->dma, 0, sizeof(struct efc_dma));
+ }
+
+ /* Free the SLI resources */
+ sli_resource_free(efc->sli, SLI4_RSRC_VPI, nport->indicator);
+
+ efc_nport_cb(efc, evt, nport);
+}
+
+static int
+efc_nport_get_mbox_status(struct efc_nport *nport, u8 *mqe, int status)
+{
+ struct efc *efc = nport->efc;
+ struct sli4_mbox_command_header *hdr =
+ (struct sli4_mbox_command_header *)mqe;
+
+ if (status || le16_to_cpu(hdr->status)) {
+ efc_log_debug(efc, "bad status vpi=%#x st=%x hdr=%x\n",
+ nport->indicator, status, le16_to_cpu(hdr->status));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+efc_nport_free_unreg_vpi_cb(struct efc *efc, int status, u8 *mqe, void *arg)
+{
+ struct efc_nport *nport = arg;
+ int evt = EFC_EVT_NPORT_FREE_OK;
+ int rc;
+
+ rc = efc_nport_get_mbox_status(nport, mqe, status);
+ if (rc)
+ evt = EFC_EVT_NPORT_FREE_FAIL;
+
+ efc_nport_free_resources(nport, evt, mqe);
+ return rc;
+}
+
+static void
+efc_nport_free_unreg_vpi(struct efc_nport *nport)
+{
+ struct efc *efc = nport->efc;
+ int rc;
+ u8 data[SLI4_BMBX_SIZE];
+
+ rc = sli_cmd_unreg_vpi(efc->sli, data, nport->indicator,
+ SLI4_UNREG_TYPE_PORT);
+ if (rc) {
+ efc_log_err(efc, "UNREG_VPI format failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_FREE_FAIL, data);
+ return;
+ }
+
+ rc = efc->tt.issue_mbox_rqst(efc->base, data,
+ efc_nport_free_unreg_vpi_cb, nport);
+ if (rc) {
+ efc_log_err(efc, "UNREG_VPI command failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_FREE_FAIL, data);
+ }
+}
+
+static void
+efc_nport_send_evt(struct efc_nport *nport, int evt, void *data)
+{
+ struct efc *efc = nport->efc;
+
+ /* Now inform the registered callbacks */
+ efc_nport_cb(efc, evt, nport);
+
+ /* Set the nport attached flag */
+ if (evt == EFC_EVT_NPORT_ATTACH_OK)
+ nport->attached = true;
+
+ /* If there is a pending free request, then handle it now */
+ if (nport->free_req_pending)
+ efc_nport_free_unreg_vpi(nport);
+}
+
+static int
+efc_nport_alloc_init_vpi_cb(struct efc *efc, int status, u8 *mqe, void *arg)
+{
+ struct efc_nport *nport = arg;
+
+ if (efc_nport_get_mbox_status(nport, mqe, status)) {
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, mqe);
+ return -EIO;
+ }
+
+ efc_nport_send_evt(nport, EFC_EVT_NPORT_ALLOC_OK, mqe);
+ return 0;
+}
+
+static void
+efc_nport_alloc_init_vpi(struct efc_nport *nport)
+{
+ struct efc *efc = nport->efc;
+ u8 data[SLI4_BMBX_SIZE];
+ int rc;
+
+ /* If there is a pending free request, then handle it now */
+ if (nport->free_req_pending) {
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_FREE_OK, data);
+ return;
+ }
+
+ rc = sli_cmd_init_vpi(efc->sli, data,
+ nport->indicator, nport->domain->indicator);
+ if (rc) {
+ efc_log_err(efc, "INIT_VPI format failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data);
+ return;
+ }
+
+ rc = efc->tt.issue_mbox_rqst(efc->base, data,
+ efc_nport_alloc_init_vpi_cb, nport);
+ if (rc) {
+ efc_log_err(efc, "INIT_VPI command failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data);
+ }
+}
+
+static int
+efc_nport_alloc_read_sparm64_cb(struct efc *efc, int status, u8 *mqe, void *arg)
+{
+ struct efc_nport *nport = arg;
+ u8 *payload = NULL;
+
+ if (efc_nport_get_mbox_status(nport, mqe, status)) {
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, mqe);
+ return -EIO;
+ }
+
+ payload = nport->dma.virt;
+
+ memcpy(&nport->sli_wwpn, payload + SLI4_READ_SPARM64_WWPN_OFFSET,
+ sizeof(nport->sli_wwpn));
+ memcpy(&nport->sli_wwnn, payload + SLI4_READ_SPARM64_WWNN_OFFSET,
+ sizeof(nport->sli_wwnn));
+
+ dma_free_coherent(&efc->pci->dev, nport->dma.size, nport->dma.virt,
+ nport->dma.phys);
+ memset(&nport->dma, 0, sizeof(struct efc_dma));
+ efc_nport_alloc_init_vpi(nport);
+ return 0;
+}
+
+static void
+efc_nport_alloc_read_sparm64(struct efc *efc, struct efc_nport *nport)
+{
+ u8 data[SLI4_BMBX_SIZE];
+ int rc;
+
+ /* Allocate memory for the service parameters */
+ nport->dma.size = EFC_SPARAM_DMA_SZ;
+ nport->dma.virt = dma_alloc_coherent(&efc->pci->dev,
+ nport->dma.size, &nport->dma.phys,
+ GFP_KERNEL);
+ if (!nport->dma.virt) {
+ efc_log_err(efc, "Failed to allocate DMA memory\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data);
+ return;
+ }
+
+ rc = sli_cmd_read_sparm64(efc->sli, data,
+ &nport->dma, nport->indicator);
+ if (rc) {
+ efc_log_err(efc, "READ_SPARM64 format failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data);
+ return;
+ }
+
+ rc = efc->tt.issue_mbox_rqst(efc->base, data,
+ efc_nport_alloc_read_sparm64_cb, nport);
+ if (rc) {
+ efc_log_err(efc, "READ_SPARM64 command failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ALLOC_FAIL, data);
+ }
+}
+
+int
+efc_cmd_nport_alloc(struct efc *efc, struct efc_nport *nport,
+ struct efc_domain *domain, u8 *wwpn)
+{
+ u32 index;
+
+ nport->indicator = U32_MAX;
+ nport->free_req_pending = false;
+
+ if (wwpn)
+ memcpy(&nport->sli_wwpn, wwpn, sizeof(nport->sli_wwpn));
+
+ /*
+ * allocate a VPI object for the port and stores it in the
+ * indicator field of the port object.
+ */
+ if (sli_resource_alloc(efc->sli, SLI4_RSRC_VPI,
+ &nport->indicator, &index)) {
+ efc_log_err(efc, "VPI allocation failure\n");
+ return -EIO;
+ }
+
+ if (domain) {
+ /*
+ * If the WWPN is NULL, fetch the default
+ * WWPN and WWNN before initializing the VPI
+ */
+ if (!wwpn)
+ efc_nport_alloc_read_sparm64(efc, nport);
+ else
+ efc_nport_alloc_init_vpi(nport);
+ } else if (!wwpn) {
+ /* domain NULL and wwpn non-NULL */
+ efc_log_err(efc, "need WWN for physical port\n");
+ sli_resource_free(efc->sli, SLI4_RSRC_VPI, nport->indicator);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+efc_nport_attach_reg_vpi_cb(struct efc *efc, int status, u8 *mqe,
+ void *arg)
+{
+ struct efc_nport *nport = arg;
+
+ nport->attaching = false;
+ if (efc_nport_get_mbox_status(nport, mqe, status)) {
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ATTACH_FAIL, mqe);
+ return -EIO;
+ }
+
+ efc_nport_send_evt(nport, EFC_EVT_NPORT_ATTACH_OK, mqe);
+ return 0;
+}
+
+int
+efc_cmd_nport_attach(struct efc *efc, struct efc_nport *nport, u32 fc_id)
+{
+ u8 buf[SLI4_BMBX_SIZE];
+ int rc = 0;
+
+ if (!nport) {
+ efc_log_err(efc, "bad param(s) nport=%p\n", nport);
+ return -EIO;
+ }
+
+ nport->fc_id = fc_id;
+
+ /* register previously-allocated VPI with the device */
+ rc = sli_cmd_reg_vpi(efc->sli, buf, nport->fc_id,
+ nport->sli_wwpn, nport->indicator,
+ nport->domain->indicator, false);
+ if (rc) {
+ efc_log_err(efc, "REG_VPI format failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ATTACH_FAIL, buf);
+ return rc;
+ }
+
+ rc = efc->tt.issue_mbox_rqst(efc->base, buf,
+ efc_nport_attach_reg_vpi_cb, nport);
+ if (rc) {
+ efc_log_err(efc, "REG_VPI command failure\n");
+ efc_nport_free_resources(nport, EFC_EVT_NPORT_ATTACH_FAIL, buf);
+ } else {
+ nport->attaching = true;
+ }
+
+ return rc;
+}
+
+int
+efc_cmd_nport_free(struct efc *efc, struct efc_nport *nport)
+{
+ if (!nport) {
+ efc_log_err(efc, "bad parameter(s) nport=%p\n", nport);
+ return -EIO;
+ }
+
+ /* Issue the UNREG_VPI command to free the assigned VPI context */
+ if (nport->attached)
+ efc_nport_free_unreg_vpi(nport);
+ else if (nport->attaching)
+ nport->free_req_pending = true;
+ else
+ efc_sm_post_event(&nport->sm, EFC_EVT_NPORT_FREE_OK, NULL);
+
+ return 0;
+}
+
+static int
+efc_domain_get_mbox_status(struct efc_domain *domain, u8 *mqe, int status)
+{
+ struct efc *efc = domain->efc;
+ struct sli4_mbox_command_header *hdr =
+ (struct sli4_mbox_command_header *)mqe;
+
+ if (status || le16_to_cpu(hdr->status)) {
+ efc_log_debug(efc, "bad status vfi=%#x st=%x hdr=%x\n",
+ domain->indicator, status,
+ le16_to_cpu(hdr->status));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void
+efc_domain_free_resources(struct efc_domain *domain, int evt, void *data)
+{
+ struct efc *efc = domain->efc;
+
+ /* Free the service parameters buffer */
+ if (domain->dma.virt) {
+ dma_free_coherent(&efc->pci->dev,
+ domain->dma.size, domain->dma.virt,
+ domain->dma.phys);
+ memset(&domain->dma, 0, sizeof(struct efc_dma));
+ }
+
+ /* Free the SLI resources */
+ sli_resource_free(efc->sli, SLI4_RSRC_VFI, domain->indicator);
+
+ efc_domain_cb(efc, evt, domain);
+}
+
+static void
+efc_domain_send_nport_evt(struct efc_domain *domain,
+ int port_evt, int domain_evt, void *data)
+{
+ struct efc *efc = domain->efc;
+
+ /* Send alloc/attach ok to the physical nport */
+ efc_nport_send_evt(domain->nport, port_evt, NULL);
+
+ /* Now inform the registered callbacks */
+ efc_domain_cb(efc, domain_evt, domain);
+}
+
+static int
+efc_domain_alloc_read_sparm64_cb(struct efc *efc, int status, u8 *mqe,
+ void *arg)
+{
+ struct efc_domain *domain = arg;
+
+ if (efc_domain_get_mbox_status(domain, mqe, status)) {
+ efc_domain_free_resources(domain,
+ EFC_HW_DOMAIN_ALLOC_FAIL, mqe);
+ return -EIO;
+ }
+
+ efc_domain_send_nport_evt(domain, EFC_EVT_NPORT_ALLOC_OK,
+ EFC_HW_DOMAIN_ALLOC_OK, mqe);
+ return 0;
+}
+
+static void
+efc_domain_alloc_read_sparm64(struct efc_domain *domain)
+{
+ struct efc *efc = domain->efc;
+ u8 data[SLI4_BMBX_SIZE];
+ int rc;
+
+ rc = sli_cmd_read_sparm64(efc->sli, data, &domain->dma, 0);
+ if (rc) {
+ efc_log_err(efc, "READ_SPARM64 format failure\n");
+ efc_domain_free_resources(domain,
+ EFC_HW_DOMAIN_ALLOC_FAIL, data);
+ return;
+ }
+
+ rc = efc->tt.issue_mbox_rqst(efc->base, data,
+ efc_domain_alloc_read_sparm64_cb, domain);
+ if (rc) {
+ efc_log_err(efc, "READ_SPARM64 command failure\n");
+ efc_domain_free_resources(domain,
+ EFC_HW_DOMAIN_ALLOC_FAIL, data);
+ }
+}
+
+static int
+efc_domain_alloc_init_vfi_cb(struct efc *efc, int status, u8 *mqe,
+ void *arg)
+{
+ struct efc_domain *domain = arg;
+
+ if (efc_domain_get_mbox_status(domain, mqe, status)) {
+ efc_domain_free_resources(domain,
+ EFC_HW_DOMAIN_ALLOC_FAIL, mqe);
+ return -EIO;
+ }
+
+ efc_domain_alloc_read_sparm64(domain);
+ return 0;
+}
+
+static void
+efc_domain_alloc_init_vfi(struct efc_domain *domain)
+{
+ struct efc *efc = domain->efc;
+ struct efc_nport *nport = domain->nport;
+ u8 data[SLI4_BMBX_SIZE];
+ int rc;
+
+ /*
+ * For FC, the HW alread registered an FCFI.
+ * Copy FCF information into the domain and jump to INIT_VFI.
+ */
+ domain->fcf_indicator = efc->fcfi;
+ rc = sli_cmd_init_vfi(efc->sli, data, domain->indicator,
+ domain->fcf_indicator, nport->indicator);
+ if (rc) {
+ efc_log_err(efc, "INIT_VFI format failure\n");
+ efc_domain_free_resources(domain,
+ EFC_HW_DOMAIN_ALLOC_FAIL, data);
+ return;
+ }
+
+ efc_log_err(efc, "%s issue mbox\n", __func__);
+ rc = efc->tt.issue_mbox_rqst(efc->base, data,
+ efc_domain_alloc_init_vfi_cb, domain);
+ if (rc) {
+ efc_log_err(efc, "INIT_VFI command failure\n");
+ efc_domain_free_resources(domain,
+ EFC_HW_DOMAIN_ALLOC_FAIL, data);
+ }
+}
+
+int
+efc_cmd_domain_alloc(struct efc *efc, struct efc_domain *domain, u32 fcf)
+{
+ u32 index;
+
+ if (!domain || !domain->nport) {
+ efc_log_err(efc, "bad parameter(s) domain=%p nport=%p\n",
+ domain, domain ? domain->nport : NULL);
+ return -EIO;
+ }
+
+ /* allocate memory for the service parameters */
+ domain->dma.size = EFC_SPARAM_DMA_SZ;
+ domain->dma.virt = dma_alloc_coherent(&efc->pci->dev,
+ domain->dma.size,
+ &domain->dma.phys, GFP_KERNEL);
+ if (!domain->dma.virt) {
+ efc_log_err(efc, "Failed to allocate DMA memory\n");
+ return -EIO;
+ }
+
+ domain->fcf = fcf;
+ domain->fcf_indicator = U32_MAX;
+ domain->indicator = U32_MAX;
+
+ if (sli_resource_alloc(efc->sli, SLI4_RSRC_VFI, &domain->indicator,
+ &index)) {
+ efc_log_err(efc, "VFI allocation failure\n");
+
+ dma_free_coherent(&efc->pci->dev,
+ domain->dma.size, domain->dma.virt,
+ domain->dma.phys);
+ memset(&domain->dma, 0, sizeof(struct efc_dma));
+
+ return -EIO;
+ }
+
+ efc_domain_alloc_init_vfi(domain);
+ return 0;
+}
+
+static int
+efc_domain_attach_reg_vfi_cb(struct efc *efc, int status, u8 *mqe,
+ void *arg)
+{
+ struct efc_domain *domain = arg;
+
+ if (efc_domain_get_mbox_status(domain, mqe, status)) {
+ efc_domain_free_resources(domain,
+ EFC_HW_DOMAIN_ATTACH_FAIL, mqe);
+ return -EIO;
+ }
+
+ efc_domain_send_nport_evt(domain, EFC_EVT_NPORT_ATTACH_OK,
+ EFC_HW_DOMAIN_ATTACH_OK, mqe);
+ return 0;
+}
+
+int
+efc_cmd_domain_attach(struct efc *efc, struct efc_domain *domain, u32 fc_id)
+{
+ u8 buf[SLI4_BMBX_SIZE];
+ int rc = 0;
+
+ if (!domain) {
+ efc_log_err(efc, "bad param(s) domain=%p\n", domain);
+ return -EIO;
+ }
+
+ domain->nport->fc_id = fc_id;
+
+ rc = sli_cmd_reg_vfi(efc->sli, buf, SLI4_BMBX_SIZE, domain->indicator,
+ domain->fcf_indicator, domain->dma,
+ domain->nport->indicator, domain->nport->sli_wwpn,
+ domain->nport->fc_id);
+ if (rc) {
+ efc_log_err(efc, "REG_VFI format failure\n");
+ goto cleanup;
+ }
+
+ rc = efc->tt.issue_mbox_rqst(efc->base, buf,
+ efc_domain_attach_reg_vfi_cb, domain);
+ if (rc) {
+ efc_log_err(efc, "REG_VFI command failure\n");
+ goto cleanup;
+ }
+
+ return rc;
+
+cleanup:
+ efc_domain_free_resources(domain, EFC_HW_DOMAIN_ATTACH_FAIL, buf);
+
+ return rc;
+}
+
+static int
+efc_domain_free_unreg_vfi_cb(struct efc *efc, int status, u8 *mqe, void *arg)
+{
+ struct efc_domain *domain = arg;
+ int evt = EFC_HW_DOMAIN_FREE_OK;
+ int rc;
+
+ rc = efc_domain_get_mbox_status(domain, mqe, status);
+ if (rc) {
+ evt = EFC_HW_DOMAIN_FREE_FAIL;
+ rc = -EIO;
+ }
+
+ efc_domain_free_resources(domain, evt, mqe);
+ return rc;
+}
+
+static void
+efc_domain_free_unreg_vfi(struct efc_domain *domain)
+{
+ struct efc *efc = domain->efc;
+ int rc;
+ u8 data[SLI4_BMBX_SIZE];
+
+ rc = sli_cmd_unreg_vfi(efc->sli, data, domain->indicator,
+ SLI4_UNREG_TYPE_DOMAIN);
+ if (rc) {
+ efc_log_err(efc, "UNREG_VFI format failure\n");
+ goto cleanup;
+ }
+
+ rc = efc->tt.issue_mbox_rqst(efc->base, data,
+ efc_domain_free_unreg_vfi_cb, domain);
+ if (rc) {
+ efc_log_err(efc, "UNREG_VFI command failure\n");
+ goto cleanup;
+ }
+
+ return;
+
+cleanup:
+ efc_domain_free_resources(domain, EFC_HW_DOMAIN_FREE_FAIL, data);
+}
+
+int
+efc_cmd_domain_free(struct efc *efc, struct efc_domain *domain)
+{
+ if (!domain) {
+ efc_log_err(efc, "bad parameter(s) domain=%p\n", domain);
+ return -EIO;
+ }
+
+ efc_domain_free_unreg_vfi(domain);
+ return 0;
+}
+
+int
+efc_cmd_node_alloc(struct efc *efc, struct efc_remote_node *rnode, u32 fc_addr,
+ struct efc_nport *nport)
+{
+ /* Check for invalid indicator */
+ if (rnode->indicator != U32_MAX) {
+ efc_log_err(efc,
+ "RPI allocation failure addr=%#x rpi=%#x\n",
+ fc_addr, rnode->indicator);
+ return -EIO;
+ }
+
+ /* NULL SLI port indicates an unallocated remote node */
+ rnode->nport = NULL;
+
+ if (sli_resource_alloc(efc->sli, SLI4_RSRC_RPI,
+ &rnode->indicator, &rnode->index)) {
+ efc_log_err(efc, "RPI allocation failure addr=%#x\n",
+ fc_addr);
+ return -EIO;
+ }
+
+ rnode->fc_id = fc_addr;
+ rnode->nport = nport;
+
+ return 0;
+}
+
+static int
+efc_cmd_node_attach_cb(struct efc *efc, int status, u8 *mqe, void *arg)
+{
+ struct efc_remote_node *rnode = arg;
+ struct sli4_mbox_command_header *hdr =
+ (struct sli4_mbox_command_header *)mqe;
+ int evt = 0;
+
+ if (status || le16_to_cpu(hdr->status)) {
+ efc_log_debug(efc, "bad status cqe=%#x mqe=%#x\n", status,
+ le16_to_cpu(hdr->status));
+ rnode->attached = false;
+ evt = EFC_EVT_NODE_ATTACH_FAIL;
+ } else {
+ rnode->attached = true;
+ evt = EFC_EVT_NODE_ATTACH_OK;
+ }
+
+ efc_remote_node_cb(efc, evt, rnode);
+
+ return 0;
+}
+
+int
+efc_cmd_node_attach(struct efc *efc, struct efc_remote_node *rnode,
+ struct efc_dma *sparms)
+{
+ int rc = -EIO;
+ u8 buf[SLI4_BMBX_SIZE];
+
+ if (!rnode || !sparms) {
+ efc_log_err(efc, "bad parameter(s) rnode=%p sparms=%p\n",
+ rnode, sparms);
+ return -EIO;
+ }
+
+ /*
+ * If the attach count is non-zero, this RPI has already been reg'd.
+ * Otherwise, register the RPI
+ */
+ if (rnode->index == U32_MAX) {
+ efc_log_err(efc, "bad parameter rnode->index invalid\n");
+ return -EIO;
+ }
+
+ /* Update a remote node object with the remote port's service params */
+ if (!sli_cmd_reg_rpi(efc->sli, buf, rnode->indicator,
+ rnode->nport->indicator, rnode->fc_id, sparms, 0, 0))
+ rc = efc->tt.issue_mbox_rqst(efc->base, buf,
+ efc_cmd_node_attach_cb, rnode);
+
+ return rc;
+}
+
+int
+efc_node_free_resources(struct efc *efc, struct efc_remote_node *rnode)
+{
+ int rc = 0;
+
+ if (!rnode) {
+ efc_log_err(efc, "bad parameter rnode=%p\n", rnode);
+ return -EIO;
+ }
+
+ if (rnode->nport) {
+ if (rnode->attached) {
+ efc_log_err(efc, "rnode is still attached\n");
+ return -EIO;
+ }
+ if (rnode->indicator != U32_MAX) {
+ if (sli_resource_free(efc->sli, SLI4_RSRC_RPI,
+ rnode->indicator)) {
+ efc_log_err(efc,
+ "RPI free fail RPI %d addr=%#x\n",
+ rnode->indicator, rnode->fc_id);
+ rc = -EIO;
+ } else {
+ rnode->indicator = U32_MAX;
+ rnode->index = U32_MAX;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int
+efc_cmd_node_free_cb(struct efc *efc, int status, u8 *mqe, void *arg)
+{
+ struct efc_remote_node *rnode = arg;
+ struct sli4_mbox_command_header *hdr =
+ (struct sli4_mbox_command_header *)mqe;
+ int evt = EFC_EVT_NODE_FREE_FAIL;
+ int rc = 0;
+
+ if (status || le16_to_cpu(hdr->status)) {
+ efc_log_debug(efc, "bad status cqe=%#x mqe=%#x\n", status,
+ le16_to_cpu(hdr->status));
+
+ /*
+ * In certain cases, a non-zero MQE status is OK (all must be
+ * true):
+ * - node is attached
+ * - status is 0x1400
+ */
+ if (!rnode->attached ||
+ (le16_to_cpu(hdr->status) != SLI4_MBX_STATUS_RPI_NOT_REG))
+ rc = -EIO;
+ }
+
+ if (!rc) {
+ rnode->attached = false;
+ evt = EFC_EVT_NODE_FREE_OK;
+ }
+
+ efc_remote_node_cb(efc, evt, rnode);
+
+ return rc;
+}
+
+int
+efc_cmd_node_detach(struct efc *efc, struct efc_remote_node *rnode)
+{
+ u8 buf[SLI4_BMBX_SIZE];
+ int rc = -EIO;
+
+ if (!rnode) {
+ efc_log_err(efc, "bad parameter rnode=%p\n", rnode);
+ return -EIO;
+ }
+
+ if (rnode->nport) {
+ if (!rnode->attached)
+ return -EIO;
+
+ rc = -EIO;
+
+ if (!sli_cmd_unreg_rpi(efc->sli, buf, rnode->indicator,
+ SLI4_RSRC_RPI, U32_MAX))
+ rc = efc->tt.issue_mbox_rqst(efc->base, buf,
+ efc_cmd_node_free_cb, rnode);
+
+ if (rc != 0) {
+ efc_log_err(efc, "UNREG_RPI failed\n");
+ rc = -EIO;
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/scsi/elx/libefc/efc_cmds.h b/drivers/scsi/elx/libefc/efc_cmds.h
new file mode 100644
index 000000000000..4d353ab04dc3
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_cmds.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#ifndef __EFC_CMDS_H__
+#define __EFC_CMDS_H__
+
+#define EFC_SPARAM_DMA_SZ 112
+int
+efc_cmd_nport_alloc(struct efc *efc, struct efc_nport *nport,
+ struct efc_domain *domain, u8 *wwpn);
+int
+efc_cmd_nport_attach(struct efc *efc, struct efc_nport *nport, u32 fc_id);
+int
+efc_cmd_nport_free(struct efc *efc, struct efc_nport *nport);
+int
+efc_cmd_domain_alloc(struct efc *efc, struct efc_domain *domain, u32 fcf);
+int
+efc_cmd_domain_attach(struct efc *efc, struct efc_domain *domain, u32 fc_id);
+int
+efc_cmd_domain_free(struct efc *efc, struct efc_domain *domain);
+int
+efc_cmd_node_detach(struct efc *efc, struct efc_remote_node *rnode);
+int
+efc_node_free_resources(struct efc *efc, struct efc_remote_node *rnode);
+int
+efc_cmd_node_attach(struct efc *efc, struct efc_remote_node *rnode,
+ struct efc_dma *sparms);
+int
+efc_cmd_node_alloc(struct efc *efc, struct efc_remote_node *rnode, u32 fc_addr,
+ struct efc_nport *nport);
+
+#endif /* __EFC_CMDS_H */
diff --git a/drivers/scsi/elx/libefc/efc_device.c b/drivers/scsi/elx/libefc/efc_device.c
new file mode 100644
index 000000000000..52be01333c6e
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_device.c
@@ -0,0 +1,1602 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * device_sm Node State Machine: Remote Device States
+ */
+
+#include "efc.h"
+#include "efc_device.h"
+#include "efc_fabric.h"
+
+void
+efc_d_send_prli_rsp(struct efc_node *node, u16 ox_id)
+{
+ int rc = EFC_SCSI_CALL_COMPLETE;
+ struct efc *efc = node->efc;
+
+ node->ls_acc_oxid = ox_id;
+ node->send_ls_acc = EFC_NODE_SEND_LS_ACC_PRLI;
+
+ /*
+ * Wait for backend session registration
+ * to complete before sending PRLI resp
+ */
+
+ if (node->init) {
+ efc_log_info(efc, "[%s] found(initiator) WWPN:%s WWNN:%s\n",
+ node->display_name, node->wwpn, node->wwnn);
+ if (node->nport->enable_tgt)
+ rc = efc->tt.scsi_new_node(efc, node);
+ }
+
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_SESS_REG_FAIL, NULL);
+
+ if (rc == EFC_SCSI_CALL_COMPLETE)
+ efc_node_post_event(node, EFC_EVT_NODE_SESS_REG_OK, NULL);
+}
+
+static void
+__efc_d_common(const char *funcname, struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = NULL;
+ struct efc *efc = NULL;
+
+ node = ctx->app;
+ efc = node->efc;
+
+ switch (evt) {
+ /* Handle shutdown events */
+ case EFC_EVT_SHUTDOWN:
+ efc_log_debug(efc, "[%s] %-20s %-20s\n", node->display_name,
+ funcname, efc_sm_event_name(evt));
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_node_transition(node, __efc_d_initiate_shutdown, NULL);
+ break;
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ efc_log_debug(efc, "[%s] %-20s %-20s\n",
+ node->display_name, funcname,
+ efc_sm_event_name(evt));
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_EXPLICIT_LOGO;
+ efc_node_transition(node, __efc_d_initiate_shutdown, NULL);
+ break;
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ efc_log_debug(efc, "[%s] %-20s %-20s\n", node->display_name,
+ funcname, efc_sm_event_name(evt));
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_IMPLICIT_LOGO;
+ efc_node_transition(node, __efc_d_initiate_shutdown, NULL);
+ break;
+
+ default:
+ /* call default event handler common to all nodes */
+ __efc_node_common(funcname, ctx, evt, arg);
+ }
+}
+
+static void
+__efc_d_wait_del_node(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ /*
+ * State is entered when a node sends a delete initiator/target call
+ * to the target-server/initiator-client and needs to wait for that
+ * work to complete.
+ */
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ fallthrough;
+
+ case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ /* These are expected events. */
+ break;
+
+ case EFC_EVT_NODE_DEL_INI_COMPLETE:
+ case EFC_EVT_NODE_DEL_TGT_COMPLETE:
+ /*
+ * node has either been detached or is in the process
+ * of being detached,
+ * call common node's initiate cleanup function
+ */
+ efc_node_initiate_cleanup(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_FAIL:
+ /* Can happen as ELS IO IO's complete */
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ break;
+
+ /* ignore shutdown events as we're already in shutdown path */
+ case EFC_EVT_SHUTDOWN:
+ /* have default shutdown event take precedence */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ fallthrough;
+
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ break;
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ /* don't care about domain_attach_ok */
+ break;
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+static void
+__efc_d_wait_del_ini_tgt(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ fallthrough;
+
+ case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ /* These are expected events. */
+ break;
+
+ case EFC_EVT_NODE_DEL_INI_COMPLETE:
+ case EFC_EVT_NODE_DEL_TGT_COMPLETE:
+ efc_node_transition(node, __efc_d_wait_del_node, NULL);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_FAIL:
+ /* Can happen as ELS IO IO's complete */
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ break;
+
+ /* ignore shutdown events as we're already in shutdown path */
+ case EFC_EVT_SHUTDOWN:
+ /* have default shutdown event take precedence */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ fallthrough;
+
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ break;
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ /* don't care about domain_attach_ok */
+ break;
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_initiate_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER: {
+ int rc = EFC_SCSI_CALL_COMPLETE;
+
+ /* assume no wait needed */
+ node->els_io_enabled = false;
+
+ /* make necessary delete upcall(s) */
+ if (node->init && !node->targ) {
+ efc_log_info(node->efc,
+ "[%s] delete (initiator) WWPN %s WWNN %s\n",
+ node->display_name,
+ node->wwpn, node->wwnn);
+ efc_node_transition(node,
+ __efc_d_wait_del_node,
+ NULL);
+ if (node->nport->enable_tgt)
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_INITIATOR_DELETED);
+
+ if (rc == EFC_SCSI_CALL_COMPLETE || rc < 0)
+ efc_node_post_event(node,
+ EFC_EVT_NODE_DEL_INI_COMPLETE, NULL);
+
+ } else if (node->targ && !node->init) {
+ efc_log_info(node->efc,
+ "[%s] delete (target) WWPN %s WWNN %s\n",
+ node->display_name,
+ node->wwpn, node->wwnn);
+ efc_node_transition(node,
+ __efc_d_wait_del_node,
+ NULL);
+ if (node->nport->enable_ini)
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_TARGET_DELETED);
+
+ if (rc == EFC_SCSI_CALL_COMPLETE)
+ efc_node_post_event(node,
+ EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL);
+
+ } else if (node->init && node->targ) {
+ efc_log_info(node->efc,
+ "[%s] delete (I+T) WWPN %s WWNN %s\n",
+ node->display_name, node->wwpn, node->wwnn);
+ efc_node_transition(node, __efc_d_wait_del_ini_tgt,
+ NULL);
+ if (node->nport->enable_tgt)
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_INITIATOR_DELETED);
+
+ if (rc == EFC_SCSI_CALL_COMPLETE)
+ efc_node_post_event(node,
+ EFC_EVT_NODE_DEL_INI_COMPLETE, NULL);
+ /* assume no wait needed */
+ rc = EFC_SCSI_CALL_COMPLETE;
+ if (node->nport->enable_ini)
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_TARGET_DELETED);
+
+ if (rc == EFC_SCSI_CALL_COMPLETE)
+ efc_node_post_event(node,
+ EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL);
+ }
+
+ /* we've initiated the upcalls as needed, now kick off the node
+ * detach to precipitate the aborting of outstanding exchanges
+ * associated with said node
+ *
+ * Beware: if we've made upcall(s), we've already transitioned
+ * to a new state by the time we execute this.
+ * consider doing this before the upcalls?
+ */
+ if (node->attached) {
+ /* issue hw node free; don't care if succeeds right
+ * away or sometime later, will check node->attached
+ * later in shutdown process
+ */
+ rc = efc_cmd_node_detach(efc, &node->rnode);
+ if (rc < 0)
+ node_printf(node,
+ "Failed freeing HW node, rc=%d\n",
+ rc);
+ }
+
+ /* if neither initiator nor target, proceed to cleanup */
+ if (!node->init && !node->targ) {
+ /*
+ * node has either been detached or is in
+ * the process of being detached,
+ * call common node's initiate cleanup function
+ */
+ efc_node_initiate_cleanup(node);
+ }
+ break;
+ }
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ /* Ignore, this can happen if an ELS is
+ * aborted while in a delay/retry state
+ */
+ break;
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_loop(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_DOMAIN_ATTACH_OK: {
+ /* send PLOGI automatically if initiator */
+ efc_node_init_device(node, true);
+ break;
+ }
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+efc_send_ls_acc_after_attach(struct efc_node *node,
+ struct fc_frame_header *hdr,
+ enum efc_node_send_ls_acc ls)
+{
+ u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
+
+ /* Save the OX_ID for sending LS_ACC sometime later */
+ WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE);
+
+ node->ls_acc_oxid = ox_id;
+ node->send_ls_acc = ls;
+ node->ls_acc_did = ntoh24(hdr->fh_d_id);
+}
+
+void
+efc_process_prli_payload(struct efc_node *node, void *prli)
+{
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp sp;
+ } *pp;
+
+ pp = prli;
+ node->init = (pp->sp.spp_flags & FCP_SPPF_INIT_FCN) != 0;
+ node->targ = (pp->sp.spp_flags & FCP_SPPF_TARG_FCN) != 0;
+}
+
+void
+__efc_d_wait_plogi_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_node_transition(node, __efc_d_initiate_shutdown, NULL);
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_OK: /* PLOGI ACC completions */
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ efc_node_transition(node, __efc_d_port_logged_in, NULL);
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_logo_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_OK:
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ case EFC_EVT_SRRS_ELS_REQ_FAIL:
+ /* LOGO response received, sent shutdown */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_LOGO,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ node_printf(node,
+ "LOGO sent (evt=%s), shutdown node\n",
+ efc_sm_event_name(evt));
+ /* sm: / post explicit logout */
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO,
+ NULL);
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+efc_node_init_device(struct efc_node *node, bool send_plogi)
+{
+ node->send_plogi = send_plogi;
+ if ((node->efc->nodedb_mask & EFC_NODEDB_PAUSE_NEW_NODES) &&
+ (node->rnode.fc_id != FC_FID_DOM_MGR)) {
+ node->nodedb_state = __efc_d_init;
+ efc_node_transition(node, __efc_node_paused, NULL);
+ } else {
+ efc_node_transition(node, __efc_d_init, NULL);
+ }
+}
+
+static void
+efc_d_check_plogi_topology(struct efc_node *node, u32 d_id)
+{
+ switch (node->nport->topology) {
+ case EFC_NPORT_TOPO_P2P:
+ /* we're not attached and nport is p2p,
+ * need to attach
+ */
+ efc_domain_attach(node->nport->domain, d_id);
+ efc_node_transition(node, __efc_d_wait_domain_attach, NULL);
+ break;
+ case EFC_NPORT_TOPO_FABRIC:
+ /* we're not attached and nport is fabric, domain
+ * attach should have already been requested as part
+ * of the fabric state machine, wait for it
+ */
+ efc_node_transition(node, __efc_d_wait_domain_attach, NULL);
+ break;
+ case EFC_NPORT_TOPO_UNKNOWN:
+ /* Two possibilities:
+ * 1. received a PLOGI before our FLOGI has completed
+ * (possible since completion comes in on another
+ * CQ), thus we don't know what we're connected to
+ * yet; transition to a state to wait for the
+ * fabric node to tell us;
+ * 2. PLOGI received before link went down and we
+ * haven't performed domain attach yet.
+ * Note: we cannot distinguish between 1. and 2.
+ * so have to assume PLOGI
+ * was received after link back up.
+ */
+ node_printf(node, "received PLOGI, unknown topology did=0x%x\n",
+ d_id);
+ efc_node_transition(node, __efc_d_wait_topology_notify, NULL);
+ break;
+ default:
+ node_printf(node, "received PLOGI, unexpected topology %d\n",
+ node->nport->topology);
+ }
+}
+
+void
+__efc_d_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ /*
+ * This state is entered when a node is instantiated,
+ * either having been discovered from a name services query,
+ * or having received a PLOGI/FLOGI.
+ */
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ if (!node->send_plogi)
+ break;
+ /* only send if we have initiator capability,
+ * and domain is attached
+ */
+ if (node->nport->enable_ini &&
+ node->nport->domain->attached) {
+ efc_send_plogi(node);
+
+ efc_node_transition(node, __efc_d_wait_plogi_rsp, NULL);
+ } else {
+ node_printf(node, "not sending plogi nport.ini=%d,",
+ node->nport->enable_ini);
+ node_printf(node, "domain attached=%d\n",
+ node->nport->domain->attached);
+ }
+ break;
+ case EFC_EVT_PLOGI_RCVD: {
+ /* T, or I+T */
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+ int rc;
+
+ efc_node_save_sparms(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PLOGI);
+
+ /* domain not attached; several possibilities: */
+ if (!node->nport->domain->attached) {
+ efc_d_check_plogi_topology(node, ntoh24(hdr->fh_d_id));
+ break;
+ }
+
+ /* domain already attached */
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_d_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL);
+
+ break;
+ }
+
+ case EFC_EVT_FDISC_RCVD: {
+ __efc_d_common(__func__, ctx, evt, arg);
+ break;
+ }
+
+ case EFC_EVT_FLOGI_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+ u32 d_id = ntoh24(hdr->fh_d_id);
+
+ /* sm: / save sparams, send FLOGI acc */
+ memcpy(node->nport->domain->flogi_service_params,
+ cbdata->payload->dma.virt,
+ sizeof(struct fc_els_flogi));
+
+ /* send FC LS_ACC response, override s_id */
+ efc_fabric_set_topology(node, EFC_NPORT_TOPO_P2P);
+
+ efc_send_flogi_p2p_acc(node, be16_to_cpu(hdr->fh_ox_id), d_id);
+
+ if (efc_p2p_setup(node->nport)) {
+ node_printf(node, "p2p failed, shutting down node\n");
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+ break;
+ }
+
+ efc_node_transition(node, __efc_p2p_wait_flogi_acc_cmpl, NULL);
+ break;
+ }
+
+ case EFC_EVT_LOGO_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ if (!node->nport->domain->attached) {
+ /* most likely a frame left over from before a link
+ * down; drop and
+ * shut node down w/ "explicit logout" so pending
+ * frames are processed
+ */
+ node_printf(node, "%s domain not attached, dropping\n",
+ efc_sm_event_name(evt));
+ efc_node_post_event(node,
+ EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
+ break;
+ }
+
+ efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL);
+ break;
+ }
+
+ case EFC_EVT_PRLI_RCVD:
+ case EFC_EVT_PRLO_RCVD:
+ case EFC_EVT_PDISC_RCVD:
+ case EFC_EVT_ADISC_RCVD:
+ case EFC_EVT_RSCN_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ if (!node->nport->domain->attached) {
+ /* most likely a frame left over from before a link
+ * down; drop and shut node down w/ "explicit logout"
+ * so pending frames are processed
+ */
+ node_printf(node, "%s domain not attached, dropping\n",
+ efc_sm_event_name(evt));
+
+ efc_node_post_event(node,
+ EFC_EVT_SHUTDOWN_EXPLICIT_LOGO,
+ NULL);
+ break;
+ }
+ node_printf(node, "%s received, sending reject\n",
+ efc_sm_event_name(evt));
+
+ efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
+ ELS_RJT_UNAB, ELS_EXPL_PLOGI_REQD, 0);
+
+ break;
+ }
+
+ case EFC_EVT_FCP_CMD_RCVD: {
+ /* note: problem, we're now expecting an ELS REQ completion
+ * from both the LOGO and PLOGI
+ */
+ if (!node->nport->domain->attached) {
+ /* most likely a frame left over from before a
+ * link down; drop and
+ * shut node down w/ "explicit logout" so pending
+ * frames are processed
+ */
+ node_printf(node, "%s domain not attached, dropping\n",
+ efc_sm_event_name(evt));
+ efc_node_post_event(node,
+ EFC_EVT_SHUTDOWN_EXPLICIT_LOGO,
+ NULL);
+ break;
+ }
+
+ /* Send LOGO */
+ node_printf(node, "FCP_CMND received, send LOGO\n");
+ if (efc_send_logo(node)) {
+ /*
+ * failed to send LOGO, go ahead and cleanup node
+ * anyways
+ */
+ node_printf(node, "Failed to send LOGO\n");
+ efc_node_post_event(node,
+ EFC_EVT_SHUTDOWN_EXPLICIT_LOGO,
+ NULL);
+ } else {
+ /* sent LOGO, wait for response */
+ efc_node_transition(node,
+ __efc_d_wait_logo_rsp, NULL);
+ }
+ break;
+ }
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ /* don't care about domain_attach_ok */
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_plogi_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ int rc;
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_PLOGI_RCVD: {
+ /* T, or I+T */
+ /* received PLOGI with svc parms, go ahead and attach node
+ * when PLOGI that was sent ultimately completes, it'll be a
+ * no-op
+ *
+ * If there is an outstanding PLOGI sent, can we set a flag
+ * to indicate that we don't want to retry it if it times out?
+ */
+ efc_node_save_sparms(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PLOGI);
+ /* sm: domain->attached / efc_node_attach */
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_d_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node,
+ EFC_EVT_NODE_ATTACH_FAIL, NULL);
+
+ break;
+ }
+
+ case EFC_EVT_PRLI_RCVD:
+ /* I, or I+T */
+ /* sent PLOGI and before completion was seen, received the
+ * PRLI from the remote node (WCQEs and RCQEs come in on
+ * different queues and order of processing cannot be assumed)
+ * Save OXID so PRLI can be sent after the attach and continue
+ * to wait for PLOGI response
+ */
+ efc_process_prli_payload(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PRLI);
+ efc_node_transition(node, __efc_d_wait_plogi_rsp_recvd_prli,
+ NULL);
+ break;
+
+ case EFC_EVT_LOGO_RCVD: /* why don't we do a shutdown here?? */
+ case EFC_EVT_PRLO_RCVD:
+ case EFC_EVT_PDISC_RCVD:
+ case EFC_EVT_FDISC_RCVD:
+ case EFC_EVT_ADISC_RCVD:
+ case EFC_EVT_RSCN_RCVD:
+ case EFC_EVT_SCR_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ node_printf(node, "%s received, sending reject\n",
+ efc_sm_event_name(evt));
+
+ efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
+ ELS_RJT_UNAB, ELS_EXPL_PLOGI_REQD, 0);
+
+ break;
+ }
+
+ case EFC_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */
+ /* Completion from PLOGI sent */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / save sparams, efc_node_attach */
+ efc_node_save_sparms(node, cbdata->els_rsp.virt);
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_d_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node,
+ EFC_EVT_NODE_ATTACH_FAIL, NULL);
+
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */
+ /* PLOGI failed, shutdown the node */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ /* Our PLOGI was rejected, this is ok in some cases */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ break;
+
+ case EFC_EVT_FCP_CMD_RCVD: {
+ /* not logged in yet and outstanding PLOGI so don't send LOGO,
+ * just drop
+ */
+ node_printf(node, "FCP_CMND received, drop\n");
+ break;
+ }
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ int rc;
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ /*
+ * Since we've received a PRLI, we have a port login and will
+ * just need to wait for the PLOGI response to do the node
+ * attach and then we can send the LS_ACC for the PRLI. If,
+ * during this time, we receive FCP_CMNDs (which is possible
+ * since we've already sent a PRLI and our peer may have
+ * accepted). At this time, we are not waiting on any other
+ * unsolicited frames to continue with the login process. Thus,
+ * it will not hurt to hold frames here.
+ */
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */
+ /* Completion from PLOGI sent */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / save sparams, efc_node_attach */
+ efc_node_save_sparms(node, cbdata->els_rsp.virt);
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_d_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
+ NULL);
+
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ /* PLOGI failed, shutdown the node */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_domain_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ int rc;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ WARN_ON(!node->nport->domain->attached);
+ /* sm: / efc_node_attach */
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_d_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
+ NULL);
+
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_topology_notify(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ int rc;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_NPORT_TOPOLOGY_NOTIFY: {
+ enum efc_nport_topology *topology = arg;
+
+ WARN_ON(node->nport->domain->attached);
+
+ WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI);
+
+ node_printf(node, "topology notification, topology=%d\n",
+ *topology);
+
+ /* At the time the PLOGI was received, the topology was unknown,
+ * so we didn't know which node would perform the domain attach:
+ * 1. The node from which the PLOGI was sent (p2p) or
+ * 2. The node to which the FLOGI was sent (fabric).
+ */
+ if (*topology == EFC_NPORT_TOPO_P2P) {
+ /* if this is p2p, need to attach to the domain using
+ * the d_id from the PLOGI received
+ */
+ efc_domain_attach(node->nport->domain,
+ node->ls_acc_did);
+ }
+ /* else, if this is fabric, the domain attach
+ * should be performed by the fabric node (node sending FLOGI);
+ * just wait for attach to complete
+ */
+
+ efc_node_transition(node, __efc_d_wait_domain_attach, NULL);
+ break;
+ }
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ WARN_ON(!node->nport->domain->attached);
+ node_printf(node, "domain attach ok\n");
+ /* sm: / efc_node_attach */
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_d_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node,
+ EFC_EVT_NODE_ATTACH_FAIL, NULL);
+
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_node_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_NODE_ATTACH_OK:
+ node->attached = true;
+ switch (node->send_ls_acc) {
+ case EFC_NODE_SEND_LS_ACC_PLOGI: {
+ /* sm: send_plogi_acc is set / send PLOGI acc */
+ /* Normal case for T, or I+T */
+ efc_send_plogi_acc(node, node->ls_acc_oxid);
+ efc_node_transition(node, __efc_d_wait_plogi_acc_cmpl,
+ NULL);
+ node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
+ node->ls_acc_io = NULL;
+ break;
+ }
+ case EFC_NODE_SEND_LS_ACC_PRLI: {
+ efc_d_send_prli_rsp(node, node->ls_acc_oxid);
+ node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
+ node->ls_acc_io = NULL;
+ break;
+ }
+ case EFC_NODE_SEND_LS_ACC_NONE:
+ default:
+ /* Normal case for I */
+ /* sm: send_plogi_acc is not set / send PLOGI acc */
+ efc_node_transition(node,
+ __efc_d_port_logged_in, NULL);
+ break;
+ }
+ break;
+
+ case EFC_EVT_NODE_ATTACH_FAIL:
+ /* node attach failed, shutdown the node */
+ node->attached = false;
+ node_printf(node, "node attach failed\n");
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_node_transition(node, __efc_d_initiate_shutdown, NULL);
+ break;
+
+ /* Handle shutdown events */
+ case EFC_EVT_SHUTDOWN:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_node_transition(node, __efc_d_wait_attach_evt_shutdown,
+ NULL);
+ break;
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_EXPLICIT_LOGO;
+ efc_node_transition(node, __efc_d_wait_attach_evt_shutdown,
+ NULL);
+ break;
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_IMPLICIT_LOGO;
+ efc_node_transition(node,
+ __efc_d_wait_attach_evt_shutdown, NULL);
+ break;
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ /* wait for any of these attach events and then shutdown */
+ case EFC_EVT_NODE_ATTACH_OK:
+ node->attached = true;
+ node_printf(node, "Attach evt=%s, proceed to shutdown\n",
+ efc_sm_event_name(evt));
+ efc_node_transition(node, __efc_d_initiate_shutdown, NULL);
+ break;
+
+ case EFC_EVT_NODE_ATTACH_FAIL:
+ /* node attach failed, shutdown the node */
+ node->attached = false;
+ node_printf(node, "Attach evt=%s, proceed to shutdown\n",
+ efc_sm_event_name(evt));
+ efc_node_transition(node, __efc_d_initiate_shutdown, NULL);
+ break;
+
+ /* ignore shutdown events as we're already in shutdown path */
+ case EFC_EVT_SHUTDOWN:
+ /* have default shutdown event take precedence */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ fallthrough;
+
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_port_logged_in(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ /* Normal case for I or I+T */
+ if (node->nport->enable_ini &&
+ !(node->rnode.fc_id != FC_FID_DOM_MGR)) {
+ /* sm: if enable_ini / send PRLI */
+ efc_send_prli(node);
+ /* can now expect ELS_REQ_OK/FAIL/RJT */
+ }
+ break;
+
+ case EFC_EVT_FCP_CMD_RCVD: {
+ break;
+ }
+
+ case EFC_EVT_PRLI_RCVD: {
+ /* Normal case for T or I+T */
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp sp;
+ } *pp;
+
+ pp = cbdata->payload->dma.virt;
+ if (pp->sp.spp_type != FC_TYPE_FCP) {
+ /*Only FCP is supported*/
+ efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
+ ELS_RJT_UNAB, ELS_EXPL_UNSUPR, 0);
+ break;
+ }
+
+ efc_process_prli_payload(node, cbdata->payload->dma.virt);
+ efc_d_send_prli_rsp(node, be16_to_cpu(hdr->fh_ox_id));
+ break;
+ }
+
+ case EFC_EVT_NODE_SESS_REG_OK:
+ if (node->send_ls_acc == EFC_NODE_SEND_LS_ACC_PRLI)
+ efc_send_prli_acc(node, node->ls_acc_oxid);
+
+ node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
+ efc_node_transition(node, __efc_d_device_ready, NULL);
+ break;
+
+ case EFC_EVT_NODE_SESS_REG_FAIL:
+ efc_send_ls_rjt(node, node->ls_acc_oxid, ELS_RJT_UNAB,
+ ELS_EXPL_UNSUPR, 0);
+ node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_OK: { /* PRLI response */
+ /* Normal case for I or I+T */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / process PRLI payload */
+ efc_process_prli_payload(node, cbdata->els_rsp.virt);
+ efc_node_transition(node, __efc_d_device_ready, NULL);
+ break;
+ }
+
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: { /* PRLI response failed */
+ /* I, I+T, assume some link failure, shutdown node */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+ break;
+ }
+
+ case EFC_EVT_SRRS_ELS_REQ_RJT: {
+ /* PRLI rejected by remote
+ * Normal for I, I+T (connected to an I)
+ * Node doesn't want to be a target, stay here and wait for a
+ * PRLI from the remote node
+ * if it really wants to connect to us as target
+ */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ break;
+ }
+
+ case EFC_EVT_SRRS_ELS_CMPL_OK: {
+ /* Normal T, I+T, target-server rejected the process login */
+ /* This would be received only in the case where we sent
+ * LS_RJT for the PRLI, so
+ * do nothing. (note: as T only we could shutdown the node)
+ */
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ break;
+ }
+
+ case EFC_EVT_PLOGI_RCVD: {
+ /*sm: / save sparams, set send_plogi_acc,
+ *post implicit logout
+ * Save plogi parameters
+ */
+ efc_node_save_sparms(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PLOGI);
+
+ /* Restart node attach with new service parameters,
+ * and send ACC
+ */
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN_IMPLICIT_LOGO,
+ NULL);
+ break;
+ }
+
+ case EFC_EVT_LOGO_RCVD: {
+ /* I, T, I+T */
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ node_printf(node, "%s received attached=%d\n",
+ efc_sm_event_name(evt),
+ node->attached);
+ /* sm: / send LOGO acc */
+ efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL);
+ break;
+ }
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_logo_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_OK:
+ case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+ /* sm: / post explicit logout */
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ efc_node_post_event(node,
+ EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
+ break;
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_device_ready(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ if (evt != EFC_EVT_FCP_CMD_RCVD)
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ node->fcp_enabled = true;
+ if (node->targ) {
+ efc_log_info(efc,
+ "[%s] found (target) WWPN %s WWNN %s\n",
+ node->display_name,
+ node->wwpn, node->wwnn);
+ if (node->nport->enable_ini)
+ efc->tt.scsi_new_node(efc, node);
+ }
+ break;
+
+ case EFC_EVT_EXIT:
+ node->fcp_enabled = false;
+ break;
+
+ case EFC_EVT_PLOGI_RCVD: {
+ /* sm: / save sparams, set send_plogi_acc, post implicit
+ * logout
+ * Save plogi parameters
+ */
+ efc_node_save_sparms(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PLOGI);
+
+ /*
+ * Restart node attach with new service parameters,
+ * and send ACC
+ */
+ efc_node_post_event(node,
+ EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL);
+ break;
+ }
+
+ case EFC_EVT_PRLI_RCVD: {
+ /* T, I+T: remote initiator is slow to get started */
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp sp;
+ } *pp;
+
+ pp = cbdata->payload->dma.virt;
+ if (pp->sp.spp_type != FC_TYPE_FCP) {
+ /*Only FCP is supported*/
+ efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
+ ELS_RJT_UNAB, ELS_EXPL_UNSUPR, 0);
+ break;
+ }
+
+ efc_process_prli_payload(node, cbdata->payload->dma.virt);
+ efc_send_prli_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ break;
+ }
+
+ case EFC_EVT_PRLO_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+ /* sm: / send PRLO acc */
+ efc_send_prlo_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ /* need implicit logout? */
+ break;
+ }
+
+ case EFC_EVT_LOGO_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ node_printf(node, "%s received attached=%d\n",
+ efc_sm_event_name(evt), node->attached);
+ /* sm: / send LOGO acc */
+ efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL);
+ break;
+ }
+
+ case EFC_EVT_ADISC_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+ /* sm: / send ADISC acc */
+ efc_send_adisc_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ break;
+ }
+
+ case EFC_EVT_ABTS_RCVD:
+ /* sm: / process ABTS */
+ efc_log_err(efc, "Unexpected event:%s\n",
+ efc_sm_event_name(evt));
+ break;
+
+ case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+ break;
+
+ case EFC_EVT_NODE_REFOUND:
+ break;
+
+ case EFC_EVT_NODE_MISSING:
+ if (node->nport->enable_rscn)
+ efc_node_transition(node, __efc_d_device_gone, NULL);
+
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_OK:
+ /* T, or I+T, PRLI accept completed ok */
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+ /* T, or I+T, PRLI accept failed to complete */
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ node_printf(node, "Failed to send PRLI LS_ACC\n");
+ break;
+
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_device_gone(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER: {
+ int rc = EFC_SCSI_CALL_COMPLETE;
+ int rc_2 = EFC_SCSI_CALL_COMPLETE;
+ static const char * const labels[] = {
+ "none", "initiator", "target", "initiator+target"
+ };
+
+ efc_log_info(efc, "[%s] missing (%s) WWPN %s WWNN %s\n",
+ node->display_name,
+ labels[(node->targ << 1) | (node->init)],
+ node->wwpn, node->wwnn);
+
+ switch (efc_node_get_enable(node)) {
+ case EFC_NODE_ENABLE_T_TO_T:
+ case EFC_NODE_ENABLE_I_TO_T:
+ case EFC_NODE_ENABLE_IT_TO_T:
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_TARGET_MISSING);
+ break;
+
+ case EFC_NODE_ENABLE_T_TO_I:
+ case EFC_NODE_ENABLE_I_TO_I:
+ case EFC_NODE_ENABLE_IT_TO_I:
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_INITIATOR_MISSING);
+ break;
+
+ case EFC_NODE_ENABLE_T_TO_IT:
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_INITIATOR_MISSING);
+ break;
+
+ case EFC_NODE_ENABLE_I_TO_IT:
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_TARGET_MISSING);
+ break;
+
+ case EFC_NODE_ENABLE_IT_TO_IT:
+ rc = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_INITIATOR_MISSING);
+ rc_2 = efc->tt.scsi_del_node(efc, node,
+ EFC_SCSI_TARGET_MISSING);
+ break;
+
+ default:
+ rc = EFC_SCSI_CALL_COMPLETE;
+ break;
+ }
+
+ if (rc == EFC_SCSI_CALL_COMPLETE &&
+ rc_2 == EFC_SCSI_CALL_COMPLETE)
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+
+ break;
+ }
+ case EFC_EVT_NODE_REFOUND:
+ /* two approaches, reauthenticate with PLOGI/PRLI, or ADISC */
+
+ /* reauthenticate with PLOGI/PRLI */
+ /* efc_node_transition(node, __efc_d_discovered, NULL); */
+
+ /* reauthenticate with ADISC */
+ /* sm: / send ADISC */
+ efc_send_adisc(node);
+ efc_node_transition(node, __efc_d_wait_adisc_rsp, NULL);
+ break;
+
+ case EFC_EVT_PLOGI_RCVD: {
+ /* sm: / save sparams, set send_plogi_acc, post implicit
+ * logout
+ * Save plogi parameters
+ */
+ efc_node_save_sparms(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PLOGI);
+
+ /*
+ * Restart node attach with new service parameters, and send
+ * ACC
+ */
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN_IMPLICIT_LOGO,
+ NULL);
+ break;
+ }
+
+ case EFC_EVT_FCP_CMD_RCVD: {
+ /* most likely a stale frame (received prior to link down),
+ * if attempt to send LOGO, will probably timeout and eat
+ * up 20s; thus, drop FCP_CMND
+ */
+ node_printf(node, "FCP_CMND received, drop\n");
+ break;
+ }
+ case EFC_EVT_LOGO_RCVD: {
+ /* I, T, I+T */
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ node_printf(node, "%s received attached=%d\n",
+ efc_sm_event_name(evt), node->attached);
+ /* sm: / send LOGO acc */
+ efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL);
+ break;
+ }
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_d_wait_adisc_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK:
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_ADISC,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_node_transition(node, __efc_d_device_ready, NULL);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ /* received an LS_RJT, in this case, send shutdown
+ * (explicit logo) event which will unregister the node,
+ * and start over with PLOGI
+ */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_ADISC,
+ __efc_d_common, __func__))
+ return;
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / post explicit logout */
+ efc_node_post_event(node,
+ EFC_EVT_SHUTDOWN_EXPLICIT_LOGO,
+ NULL);
+ break;
+
+ case EFC_EVT_LOGO_RCVD: {
+ /* In this case, we have the equivalent of an LS_RJT for
+ * the ADISC, so we need to abort the ADISC, and re-login
+ * with PLOGI
+ */
+ /* sm: / request abort, send LOGO acc */
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ node_printf(node, "%s received attached=%d\n",
+ efc_sm_event_name(evt), node->attached);
+
+ efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL);
+ break;
+ }
+ default:
+ __efc_d_common(__func__, ctx, evt, arg);
+ }
+}
diff --git a/drivers/scsi/elx/libefc/efc_device.h b/drivers/scsi/elx/libefc/efc_device.h
new file mode 100644
index 000000000000..3cf1d8c6698f
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_device.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * Node state machine functions for remote device node sm
+ */
+
+#ifndef __EFCT_DEVICE_H__
+#define __EFCT_DEVICE_H__
+void
+efc_node_init_device(struct efc_node *node, bool send_plogi);
+void
+efc_process_prli_payload(struct efc_node *node,
+ void *prli);
+void
+efc_d_send_prli_rsp(struct efc_node *node, uint16_t ox_id);
+void
+efc_send_ls_acc_after_attach(struct efc_node *node,
+ struct fc_frame_header *hdr,
+ enum efc_node_send_ls_acc ls);
+void
+__efc_d_wait_loop(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_plogi_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_plogi_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_domain_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_topology_notify(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_node_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_initiate_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_port_logged_in(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_logo_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_device_ready(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_device_gone(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_adisc_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_d_wait_logo_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+#endif /* __EFCT_DEVICE_H__ */
diff --git a/drivers/scsi/elx/libefc/efc_domain.c b/drivers/scsi/elx/libefc/efc_domain.c
new file mode 100644
index 000000000000..ca9d7ff2c0d2
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_domain.c
@@ -0,0 +1,1088 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * domain_sm Domain State Machine: States
+ */
+
+#include "efc.h"
+
+int
+efc_domain_cb(void *arg, int event, void *data)
+{
+ struct efc *efc = arg;
+ struct efc_domain *domain = NULL;
+ int rc = 0;
+ unsigned long flags = 0;
+
+ if (event != EFC_HW_DOMAIN_FOUND)
+ domain = data;
+
+ /* Accept domain callback events from the user driver */
+ spin_lock_irqsave(&efc->lock, flags);
+ switch (event) {
+ case EFC_HW_DOMAIN_FOUND: {
+ u64 fcf_wwn = 0;
+ struct efc_domain_record *drec = data;
+
+ /* extract the fcf_wwn */
+ fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));
+
+ efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);
+
+ /* lookup domain, or allocate a new one */
+ domain = efc->domain;
+ if (!domain) {
+ domain = efc_domain_alloc(efc, fcf_wwn);
+ if (!domain) {
+ efc_log_err(efc, "efc_domain_alloc() failed\n");
+ rc = -1;
+ break;
+ }
+ efc_sm_transition(&domain->drvsm, __efc_domain_init,
+ NULL);
+ }
+ efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);
+ break;
+ }
+
+ case EFC_HW_DOMAIN_LOST:
+ domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");
+ efc->hold_frames = true;
+ efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);
+ break;
+
+ case EFC_HW_DOMAIN_ALLOC_OK:
+ domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");
+ efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);
+ break;
+
+ case EFC_HW_DOMAIN_ALLOC_FAIL:
+ domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");
+ efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,
+ NULL);
+ break;
+
+ case EFC_HW_DOMAIN_ATTACH_OK:
+ domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");
+ efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);
+ break;
+
+ case EFC_HW_DOMAIN_ATTACH_FAIL:
+ domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");
+ efc_domain_post_event(domain,
+ EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);
+ break;
+
+ case EFC_HW_DOMAIN_FREE_OK:
+ domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");
+ efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);
+ break;
+
+ case EFC_HW_DOMAIN_FREE_FAIL:
+ domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");
+ efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);
+ break;
+
+ default:
+ efc_log_warn(efc, "unsupported event %#x\n", event);
+ }
+ spin_unlock_irqrestore(&efc->lock, flags);
+
+ if (efc->domain && domain->req_accept_frames) {
+ domain->req_accept_frames = false;
+ efc->hold_frames = false;
+ }
+
+ return rc;
+}
+
+static void
+_efc_domain_free(struct kref *arg)
+{
+ struct efc_domain *domain = container_of(arg, struct efc_domain, ref);
+ struct efc *efc = domain->efc;
+
+ if (efc->domain_free_cb)
+ (*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);
+
+ kfree(domain);
+}
+
+void
+efc_domain_free(struct efc_domain *domain)
+{
+ struct efc *efc;
+
+ efc = domain->efc;
+
+ /* Hold frames to clear the domain pointer from the xport lookup */
+ efc->hold_frames = false;
+
+ efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);
+
+ xa_destroy(&domain->lookup);
+ efc->domain = NULL;
+ kref_put(&domain->ref, domain->release);
+}
+
+struct efc_domain *
+efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)
+{
+ struct efc_domain *domain;
+
+ domain = kzalloc(sizeof(*domain), GFP_ATOMIC);
+ if (!domain)
+ return NULL;
+
+ domain->efc = efc;
+ domain->drvsm.app = domain;
+
+ /* initialize refcount */
+ kref_init(&domain->ref);
+ domain->release = _efc_domain_free;
+
+ xa_init(&domain->lookup);
+
+ INIT_LIST_HEAD(&domain->nport_list);
+ efc->domain = domain;
+ domain->fcf_wwn = fcf_wwn;
+ efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);
+
+ return domain;
+}
+
+void
+efc_register_domain_free_cb(struct efc *efc,
+ void (*callback)(struct efc *efc, void *arg),
+ void *arg)
+{
+ /* Register a callback to be called when the domain is freed */
+ efc->domain_free_cb = callback;
+ efc->domain_free_cb_arg = arg;
+ if (!efc->domain && callback)
+ (*callback)(efc, arg);
+}
+
+static void
+__efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_domain *domain = ctx->app;
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ case EFC_EVT_REENTER:
+ case EFC_EVT_EXIT:
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ /*
+ * this can arise if an FLOGI fails on the NPORT,
+ * and the NPORT is shutdown
+ */
+ break;
+ default:
+ efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
+ funcname, efc_sm_event_name(evt));
+ }
+}
+
+static void
+__efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_domain *domain = ctx->app;
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ case EFC_EVT_REENTER:
+ case EFC_EVT_EXIT:
+ break;
+ case EFC_EVT_DOMAIN_FOUND:
+ /* save drec, mark domain_found_pending */
+ memcpy(&domain->pending_drec, arg,
+ sizeof(domain->pending_drec));
+ domain->domain_found_pending = true;
+ break;
+ case EFC_EVT_DOMAIN_LOST:
+ /* unmark domain_found_pending */
+ domain->domain_found_pending = false;
+ break;
+
+ default:
+ efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
+ funcname, efc_sm_event_name(evt));
+ }
+}
+
+#define std_domain_state_decl(...)\
+ struct efc_domain *domain = NULL;\
+ struct efc *efc = NULL;\
+ \
+ WARN_ON(!ctx || !ctx->app);\
+ domain = ctx->app;\
+ WARN_ON(!domain->efc);\
+ efc = domain->efc
+
+void
+__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ domain->attached = false;
+ break;
+
+ case EFC_EVT_DOMAIN_FOUND: {
+ u32 i;
+ struct efc_domain_record *drec = arg;
+ struct efc_nport *nport;
+
+ u64 my_wwnn = efc->req_wwnn;
+ u64 my_wwpn = efc->req_wwpn;
+ __be64 bewwpn;
+
+ if (my_wwpn == 0 || my_wwnn == 0) {
+ efc_log_debug(efc, "using default hardware WWN config\n");
+ my_wwpn = efc->def_wwpn;
+ my_wwnn = efc->def_wwnn;
+ }
+
+ efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n",
+ my_wwpn, my_wwnn);
+
+ /* Allocate a nport and transition to __efc_nport_allocated */
+ nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX,
+ efc->enable_ini, efc->enable_tgt);
+
+ if (!nport) {
+ efc_log_err(efc, "efc_nport_alloc() failed\n");
+ break;
+ }
+ efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL);
+
+ bewwpn = cpu_to_be64(nport->wwpn);
+
+ /* allocate struct efc_nport object for local port
+ * Note: drec->fc_id is ALPA from read_topology only if loop
+ */
+ if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) {
+ efc_log_err(efc, "Can't allocate port\n");
+ efc_nport_free(nport);
+ break;
+ }
+
+ domain->is_loop = drec->is_loop;
+
+ /*
+ * If the loop position map includes ALPA == 0,
+ * then we are in a public loop (NL_PORT)
+ * Note that the first element of the loopmap[]
+ * contains the count of elements, and if
+ * ALPA == 0 is present, it will occupy the first
+ * location after the count.
+ */
+ domain->is_nlport = drec->map.loop[1] == 0x00;
+
+ if (!domain->is_loop) {
+ /* Initiate HW domain alloc */
+ if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
+ efc_log_err(efc,
+ "Failed to initiate HW domain allocation\n");
+ break;
+ }
+ efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
+ break;
+ }
+
+ efc_log_debug(efc, "%s fc_id=%#x speed=%d\n",
+ drec->is_loop ?
+ (domain->is_nlport ?
+ "public-loop" : "loop") : "other",
+ drec->fc_id, drec->speed);
+
+ nport->fc_id = drec->fc_id;
+ nport->topology = EFC_NPORT_TOPO_FC_AL;
+ snprintf(nport->display_name, sizeof(nport->display_name),
+ "s%06x", drec->fc_id);
+
+ if (efc->enable_ini) {
+ u32 count = drec->map.loop[0];
+
+ efc_log_debug(efc, "%d position map entries\n",
+ count);
+ for (i = 1; i <= count; i++) {
+ if (drec->map.loop[i] != drec->fc_id) {
+ struct efc_node *node;
+
+ efc_log_debug(efc, "%#x -> %#x\n",
+ drec->fc_id,
+ drec->map.loop[i]);
+ node = efc_node_alloc(nport,
+ drec->map.loop[i],
+ false, true);
+ if (!node) {
+ efc_log_err(efc,
+ "efc_node_alloc() failed\n");
+ break;
+ }
+ efc_node_transition(node,
+ __efc_d_wait_loop,
+ NULL);
+ }
+ }
+ }
+
+ /* Initiate HW domain alloc */
+ if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
+ efc_log_err(efc,
+ "Failed to initiate HW domain allocation\n");
+ break;
+ }
+ efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
+ break;
+ }
+ default:
+ __efc_domain_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_wait_alloc(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ switch (evt) {
+ case EFC_EVT_DOMAIN_ALLOC_OK: {
+ struct fc_els_flogi *sp;
+ struct efc_nport *nport;
+
+ nport = domain->nport;
+ if (WARN_ON(!nport))
+ return;
+
+ sp = (struct fc_els_flogi *)nport->service_params;
+
+ /* Save the domain service parameters */
+ memcpy(domain->service_params + 4, domain->dma.virt,
+ sizeof(struct fc_els_flogi) - 4);
+ memcpy(nport->service_params + 4, domain->dma.virt,
+ sizeof(struct fc_els_flogi) - 4);
+
+ /*
+ * Update the nport's service parameters,
+ * user might have specified non-default names
+ */
+ sp->fl_wwpn = cpu_to_be64(nport->wwpn);
+ sp->fl_wwnn = cpu_to_be64(nport->wwnn);
+
+ /*
+ * Take the loop topology path,
+ * unless we are an NL_PORT (public loop)
+ */
+ if (domain->is_loop && !domain->is_nlport) {
+ /*
+ * For loop, we already have our FC ID
+ * and don't need fabric login.
+ * Transition to the allocated state and
+ * post an event to attach to
+ * the domain. Note that this breaks the
+ * normal action/transition
+ * pattern here to avoid a race with the
+ * domain attach callback.
+ */
+ /* sm: is_loop / domain_attach */
+ efc_sm_transition(ctx, __efc_domain_allocated, NULL);
+ __efc_domain_attach_internal(domain, nport->fc_id);
+ break;
+ }
+ {
+ struct efc_node *node;
+
+ /* alloc fabric node, send FLOGI */
+ node = efc_node_find(nport, FC_FID_FLOGI);
+ if (node) {
+ efc_log_err(efc,
+ "Fabric Controller node already exists\n");
+ break;
+ }
+ node = efc_node_alloc(nport, FC_FID_FLOGI,
+ false, false);
+ if (!node) {
+ efc_log_err(efc,
+ "Error: efc_node_alloc() failed\n");
+ } else {
+ efc_node_transition(node,
+ __efc_fabric_init, NULL);
+ }
+ /* Accept frames */
+ domain->req_accept_frames = true;
+ }
+ /* sm: / start fabric logins */
+ efc_sm_transition(ctx, __efc_domain_allocated, NULL);
+ break;
+ }
+
+ case EFC_EVT_DOMAIN_ALLOC_FAIL:
+ efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;",
+ efc_sm_event_name(evt));
+ efc_log_err(efc, "shutting down domain\n");
+ domain->req_domain_free = true;
+ break;
+
+ case EFC_EVT_DOMAIN_FOUND:
+ /* Should not happen */
+ break;
+
+ case EFC_EVT_DOMAIN_LOST:
+ efc_log_debug(efc,
+ "%s received while waiting for hw_domain_alloc()\n",
+ efc_sm_event_name(evt));
+ efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
+ break;
+
+ default:
+ __efc_domain_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_allocated(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ switch (evt) {
+ case EFC_EVT_DOMAIN_REQ_ATTACH: {
+ int rc = 0;
+ u32 fc_id;
+
+ if (WARN_ON(!arg))
+ return;
+
+ fc_id = *((u32 *)arg);
+ efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n",
+ fc_id);
+ /* Update nport lookup */
+ rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport,
+ GFP_ATOMIC));
+ if (rc) {
+ efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
+ return;
+ }
+
+ /* Update display name for the nport */
+ efc_node_fcid_display(fc_id, domain->nport->display_name,
+ sizeof(domain->nport->display_name));
+
+ /* Issue domain attach call */
+ rc = efc_cmd_domain_attach(efc, domain, fc_id);
+ if (rc) {
+ efc_log_err(efc, "efc_hw_domain_attach failed: %d\n",
+ rc);
+ return;
+ }
+ /* sm: / domain_attach */
+ efc_sm_transition(ctx, __efc_domain_wait_attach, NULL);
+ break;
+ }
+
+ case EFC_EVT_DOMAIN_FOUND:
+ /* Should not happen */
+ efc_log_err(efc, "%s: evt: %d should not happen\n",
+ __func__, evt);
+ break;
+
+ case EFC_EVT_DOMAIN_LOST: {
+ efc_log_debug(efc,
+ "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n",
+ efc_sm_event_name(evt));
+ if (!list_empty(&domain->nport_list)) {
+ /*
+ * if there are nports, transition to
+ * wait state and send shutdown to each
+ * nport
+ */
+ struct efc_nport *nport = NULL, *nport_next = NULL;
+
+ efc_sm_transition(ctx, __efc_domain_wait_nports_free,
+ NULL);
+ list_for_each_entry_safe(nport, nport_next,
+ &domain->nport_list,
+ list_entry) {
+ efc_sm_post_event(&nport->sm,
+ EFC_EVT_SHUTDOWN, NULL);
+ }
+ } else {
+ /* no nports exist, free domain */
+ efc_sm_transition(ctx, __efc_domain_wait_shutdown,
+ NULL);
+ if (efc_cmd_domain_free(efc, domain))
+ efc_log_err(efc, "hw_domain_free failed\n");
+ }
+
+ break;
+ }
+
+ default:
+ __efc_domain_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_wait_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ switch (evt) {
+ case EFC_EVT_DOMAIN_ATTACH_OK: {
+ struct efc_node *node = NULL;
+ struct efc_nport *nport, *next_nport;
+ unsigned long index;
+
+ /*
+ * Set domain notify pending state to avoid
+ * duplicate domain event post
+ */
+ domain->domain_notify_pend = true;
+
+ /* Mark as attached */
+ domain->attached = true;
+
+ /* Transition to ready */
+ /* sm: / forward event to all nports and nodes */
+ efc_sm_transition(ctx, __efc_domain_ready, NULL);
+
+ /* We have an FCFI, so we can accept frames */
+ domain->req_accept_frames = true;
+
+ /*
+ * Notify all nodes that the domain attach request
+ * has completed
+ * Note: nport will have already received notification
+ * of nport attached as a result of the HW's port attach.
+ */
+ list_for_each_entry_safe(nport, next_nport,
+ &domain->nport_list, list_entry) {
+ xa_for_each(&nport->lookup, index, node) {
+ efc_node_post_event(node,
+ EFC_EVT_DOMAIN_ATTACH_OK,
+ NULL);
+ }
+ }
+ domain->domain_notify_pend = false;
+ break;
+ }
+
+ case EFC_EVT_DOMAIN_ATTACH_FAIL:
+ efc_log_debug(efc,
+ "%s received while waiting for hw attach\n",
+ efc_sm_event_name(evt));
+ break;
+
+ case EFC_EVT_DOMAIN_FOUND:
+ /* Should not happen */
+ efc_log_err(efc, "%s: evt: %d should not happen\n",
+ __func__, evt);
+ break;
+
+ case EFC_EVT_DOMAIN_LOST:
+ /*
+ * Domain lost while waiting for an attach to complete,
+ * go to a state that waits for the domain attach to
+ * complete, then handle domain lost
+ */
+ efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
+ break;
+
+ case EFC_EVT_DOMAIN_REQ_ATTACH:
+ /*
+ * In P2P we can get an attach request from
+ * the other FLOGI path, so drop this one
+ */
+ break;
+
+ default:
+ __efc_domain_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ switch (evt) {
+ case EFC_EVT_ENTER: {
+ /* start any pending vports */
+ if (efc_vport_start(domain)) {
+ efc_log_debug(domain->efc,
+ "efc_vport_start didn't start vports\n");
+ }
+ break;
+ }
+ case EFC_EVT_DOMAIN_LOST: {
+ if (!list_empty(&domain->nport_list)) {
+ /*
+ * if there are nports, transition to wait state
+ * and send shutdown to each nport
+ */
+ struct efc_nport *nport = NULL, *nport_next = NULL;
+
+ efc_sm_transition(ctx, __efc_domain_wait_nports_free,
+ NULL);
+ list_for_each_entry_safe(nport, nport_next,
+ &domain->nport_list,
+ list_entry) {
+ efc_sm_post_event(&nport->sm,
+ EFC_EVT_SHUTDOWN, NULL);
+ }
+ } else {
+ /* no nports exist, free domain */
+ efc_sm_transition(ctx, __efc_domain_wait_shutdown,
+ NULL);
+ if (efc_cmd_domain_free(efc, domain))
+ efc_log_err(efc, "hw_domain_free failed\n");
+ }
+ break;
+ }
+
+ case EFC_EVT_DOMAIN_FOUND:
+ /* Should not happen */
+ efc_log_err(efc, "%s: evt: %d should not happen\n",
+ __func__, evt);
+ break;
+
+ case EFC_EVT_DOMAIN_REQ_ATTACH: {
+ /* can happen during p2p */
+ u32 fc_id;
+
+ fc_id = *((u32 *)arg);
+
+ /* Assume that the domain is attached */
+ WARN_ON(!domain->attached);
+
+ /*
+ * Verify that the requested FC_ID
+ * is the same as the one we're working with
+ */
+ WARN_ON(domain->nport->fc_id != fc_id);
+ break;
+ }
+
+ default:
+ __efc_domain_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ /* Wait for nodes to free prior to the domain shutdown */
+ switch (evt) {
+ case EFC_EVT_ALL_CHILD_NODES_FREE: {
+ int rc;
+
+ /* sm: / efc_hw_domain_free */
+ efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL);
+
+ /* Request efc_hw_domain_free and wait for completion */
+ rc = efc_cmd_domain_free(efc, domain);
+ if (rc) {
+ efc_log_err(efc, "efc_hw_domain_free() failed: %d\n",
+ rc);
+ }
+ break;
+ }
+ default:
+ __efc_domain_common_shutdown(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ switch (evt) {
+ case EFC_EVT_DOMAIN_FREE_OK:
+ /* sm: / domain_free */
+ if (domain->domain_found_pending) {
+ /*
+ * save fcf_wwn and drec from this domain,
+ * free current domain and allocate
+ * a new one with the same fcf_wwn
+ * could use a SLI-4 "re-register VPI"
+ * operation here?
+ */
+ u64 fcf_wwn = domain->fcf_wwn;
+ struct efc_domain_record drec = domain->pending_drec;
+
+ efc_log_debug(efc, "Reallocating domain\n");
+ domain->req_domain_free = true;
+ domain = efc_domain_alloc(efc, fcf_wwn);
+
+ if (!domain) {
+ efc_log_err(efc,
+ "efc_domain_alloc() failed\n");
+ return;
+ }
+ /*
+ * got a new domain; at this point,
+ * there are at least two domains
+ * once the req_domain_free flag is processed,
+ * the associated domain will be removed.
+ */
+ efc_sm_transition(&domain->drvsm, __efc_domain_init,
+ NULL);
+ efc_sm_post_event(&domain->drvsm,
+ EFC_EVT_DOMAIN_FOUND, &drec);
+ } else {
+ domain->req_domain_free = true;
+ }
+ break;
+ default:
+ __efc_domain_common_shutdown(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ std_domain_state_decl();
+
+ domain_sm_trace(domain);
+
+ /*
+ * Wait for the domain alloc/attach completion
+ * after receiving a domain lost.
+ */
+ switch (evt) {
+ case EFC_EVT_DOMAIN_ALLOC_OK:
+ case EFC_EVT_DOMAIN_ATTACH_OK: {
+ if (!list_empty(&domain->nport_list)) {
+ /*
+ * if there are nports, transition to
+ * wait state and send shutdown to each nport
+ */
+ struct efc_nport *nport = NULL, *nport_next = NULL;
+
+ efc_sm_transition(ctx, __efc_domain_wait_nports_free,
+ NULL);
+ list_for_each_entry_safe(nport, nport_next,
+ &domain->nport_list,
+ list_entry) {
+ efc_sm_post_event(&nport->sm,
+ EFC_EVT_SHUTDOWN, NULL);
+ }
+ } else {
+ /* no nports exist, free domain */
+ efc_sm_transition(ctx, __efc_domain_wait_shutdown,
+ NULL);
+ if (efc_cmd_domain_free(efc, domain))
+ efc_log_err(efc, "hw_domain_free() failed\n");
+ }
+ break;
+ }
+ case EFC_EVT_DOMAIN_ALLOC_FAIL:
+ case EFC_EVT_DOMAIN_ATTACH_FAIL:
+ efc_log_err(efc, "[domain] %-20s: failed\n",
+ efc_sm_event_name(evt));
+ break;
+
+ default:
+ __efc_domain_common_shutdown(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id)
+{
+ memcpy(domain->dma.virt,
+ ((uint8_t *)domain->flogi_service_params) + 4,
+ sizeof(struct fc_els_flogi) - 4);
+ (void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH,
+ &s_id);
+}
+
+void
+efc_domain_attach(struct efc_domain *domain, u32 s_id)
+{
+ __efc_domain_attach_internal(domain, s_id);
+}
+
+int
+efc_domain_post_event(struct efc_domain *domain,
+ enum efc_sm_event event, void *arg)
+{
+ int rc;
+ bool req_domain_free;
+
+ rc = efc_sm_post_event(&domain->drvsm, event, arg);
+
+ req_domain_free = domain->req_domain_free;
+ domain->req_domain_free = false;
+
+ if (req_domain_free)
+ efc_domain_free(domain);
+
+ return rc;
+}
+
+static void
+efct_domain_process_pending(struct efc_domain *domain)
+{
+ struct efc *efc = domain->efc;
+ struct efc_hw_sequence *seq = NULL;
+ u32 processed = 0;
+ unsigned long flags = 0;
+
+ for (;;) {
+ /* need to check for hold frames condition after each frame
+ * processed because any given frame could cause a transition
+ * to a state that holds frames
+ */
+ if (efc->hold_frames)
+ break;
+
+ /* Get next frame/sequence */
+ spin_lock_irqsave(&efc->pend_frames_lock, flags);
+
+ if (!list_empty(&efc->pend_frames)) {
+ seq = list_first_entry(&efc->pend_frames,
+ struct efc_hw_sequence, list_entry);
+ list_del(&seq->list_entry);
+ }
+
+ if (!seq) {
+ processed = efc->pend_frames_processed;
+ efc->pend_frames_processed = 0;
+ spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
+ break;
+ }
+ efc->pend_frames_processed++;
+
+ spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
+
+ /* now dispatch frame(s) to dispatch function */
+ if (efc_domain_dispatch_frame(domain, seq))
+ efc->tt.hw_seq_free(efc, seq);
+
+ seq = NULL;
+ }
+
+ if (processed != 0)
+ efc_log_debug(efc, "%u domain frames held and processed\n",
+ processed);
+}
+
+void
+efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq)
+{
+ struct efc_domain *domain = efc->domain;
+
+ /*
+ * If we are holding frames or the domain is not yet registered or
+ * there's already frames on the pending list,
+ * then add the new frame to pending list
+ */
+ if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) {
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->pend_frames_lock, flags);
+ INIT_LIST_HEAD(&seq->list_entry);
+ list_add_tail(&seq->list_entry, &efc->pend_frames);
+ spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
+
+ if (domain) {
+ /* immediately process pending frames */
+ efct_domain_process_pending(domain);
+ }
+ } else {
+ /*
+ * We are not holding frames and pending list is empty,
+ * just process frame. A non-zero return means the frame
+ * was not handled - so cleanup
+ */
+ if (efc_domain_dispatch_frame(domain, seq))
+ efc->tt.hw_seq_free(efc, seq);
+ }
+}
+
+int
+efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
+{
+ struct efc_domain *domain = (struct efc_domain *)arg;
+ struct efc *efc = domain->efc;
+ struct fc_frame_header *hdr;
+ struct efc_node *node = NULL;
+ struct efc_nport *nport = NULL;
+ unsigned long flags = 0;
+ u32 s_id, d_id, rc = EFC_HW_SEQ_FREE;
+
+ if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) {
+ efc_log_err(efc, "Sequence header or payload is null\n");
+ return rc;
+ }
+
+ hdr = seq->header->dma.virt;
+
+ /* extract the s_id and d_id */
+ s_id = ntoh24(hdr->fh_s_id);
+ d_id = ntoh24(hdr->fh_d_id);
+
+ spin_lock_irqsave(&efc->lock, flags);
+
+ nport = efc_nport_find(domain, d_id);
+ if (!nport) {
+ if (hdr->fh_type == FC_TYPE_FCP) {
+ /* Drop frame */
+ efc_log_warn(efc, "FCP frame with invalid d_id x%x\n",
+ d_id);
+ goto out;
+ }
+
+ /* p2p will use this case */
+ nport = domain->nport;
+ if (!nport || !kref_get_unless_zero(&nport->ref)) {
+ efc_log_err(efc, "Physical nport is NULL\n");
+ goto out;
+ }
+ }
+
+ /* Lookup the node given the remote s_id */
+ node = efc_node_find(nport, s_id);
+
+ /* If not found, then create a new node */
+ if (!node) {
+ /*
+ * If this is solicited data or control based on R_CTL and
+ * there is no node context, then we can drop the frame
+ */
+ if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) ||
+ (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) {
+ efc_log_debug(efc, "sol data/ctrl frame without node\n");
+ goto out_release;
+ }
+
+ node = efc_node_alloc(nport, s_id, false, false);
+ if (!node) {
+ efc_log_err(efc, "efc_node_alloc() failed\n");
+ goto out_release;
+ }
+ /* don't send PLOGI on efc_d_init entry */
+ efc_node_init_device(node, false);
+ }
+
+ if (node->hold_frames || !list_empty(&node->pend_frames)) {
+ /* add frame to node's pending list */
+ spin_lock(&node->pend_frames_lock);
+ INIT_LIST_HEAD(&seq->list_entry);
+ list_add_tail(&seq->list_entry, &node->pend_frames);
+ spin_unlock(&node->pend_frames_lock);
+ rc = EFC_HW_SEQ_HOLD;
+ goto out_release;
+ }
+
+ /* now dispatch frame to the node frame handler */
+ efc_node_dispatch_frame(node, seq);
+
+out_release:
+ kref_put(&nport->ref, nport->release);
+out:
+ spin_unlock_irqrestore(&efc->lock, flags);
+ return rc;
+}
+
+void
+efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
+{
+ struct fc_frame_header *hdr = seq->header->dma.virt;
+ u32 port_id;
+ struct efc_node *node = (struct efc_node *)arg;
+ struct efc *efc = node->efc;
+
+ port_id = ntoh24(hdr->fh_s_id);
+
+ if (WARN_ON(port_id != node->rnode.fc_id))
+ return;
+
+ if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) ||
+ !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) {
+ node_printf(node,
+ "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n",
+ cpu_to_be32(((u32 *)hdr)[0]),
+ cpu_to_be32(((u32 *)hdr)[1]),
+ cpu_to_be32(((u32 *)hdr)[2]),
+ cpu_to_be32(((u32 *)hdr)[3]),
+ cpu_to_be32(((u32 *)hdr)[4]),
+ cpu_to_be32(((u32 *)hdr)[5]));
+ return;
+ }
+
+ switch (hdr->fh_r_ctl) {
+ case FC_RCTL_ELS_REQ:
+ case FC_RCTL_ELS_REP:
+ efc_node_recv_els_frame(node, seq);
+ break;
+
+ case FC_RCTL_BA_ABTS:
+ case FC_RCTL_BA_ACC:
+ case FC_RCTL_BA_RJT:
+ case FC_RCTL_BA_NOP:
+ efc_log_err(efc, "Received ABTS:\n");
+ break;
+
+ case FC_RCTL_DD_UNSOL_CMD:
+ case FC_RCTL_DD_UNSOL_CTL:
+ switch (hdr->fh_type) {
+ case FC_TYPE_FCP:
+ if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) {
+ if (!node->fcp_enabled) {
+ efc_node_recv_fcp_cmd(node, seq);
+ break;
+ }
+ efc_log_err(efc, "Recvd FCP CMD. Drop IO\n");
+ } else if ((hdr->fh_r_ctl & 0xf) ==
+ FC_RCTL_DD_SOL_DATA) {
+ node_printf(node,
+ "solicited data recvd. Drop IO\n");
+ }
+ break;
+
+ case FC_TYPE_CT:
+ efc_node_recv_ct_frame(node, seq);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl);
+ }
+}
diff --git a/drivers/scsi/elx/libefc/efc_domain.h b/drivers/scsi/elx/libefc/efc_domain.h
new file mode 100644
index 000000000000..5468ea7ab19b
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_domain.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * Declare driver's domain handler exported interface
+ */
+
+#ifndef __EFCT_DOMAIN_H__
+#define __EFCT_DOMAIN_H__
+
+struct efc_domain *
+efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn);
+void
+efc_domain_free(struct efc_domain *domain);
+
+void
+__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg);
+void
+__efc_domain_wait_alloc(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg);
+void
+__efc_domain_allocated(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg);
+void
+__efc_domain_wait_attach(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg);
+void
+__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg);
+void
+__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg);
+void
+__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg);
+void
+__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg);
+void
+efc_domain_attach(struct efc_domain *domain, u32 s_id);
+int
+efc_domain_post_event(struct efc_domain *domain, enum efc_sm_event event,
+ void *arg);
+void
+__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id);
+
+int
+efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq);
+void
+efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq);
+
+#endif /* __EFCT_DOMAIN_H__ */
diff --git a/drivers/scsi/elx/libefc/efc_els.c b/drivers/scsi/elx/libefc/efc_els.c
new file mode 100644
index 000000000000..1786cee08729
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_els.c
@@ -0,0 +1,1094 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * Functions to build and send ELS/CT/BLS commands and responses.
+ */
+
+#include "efc.h"
+#include "efc_els.h"
+#include "../libefc_sli/sli4.h"
+
+#define EFC_LOG_ENABLE_ELS_TRACE(efc) \
+ (((efc) != NULL) ? (((efc)->logmask & (1U << 1)) != 0) : 0)
+
+#define node_els_trace() \
+ do { \
+ if (EFC_LOG_ENABLE_ELS_TRACE(efc)) \
+ efc_log_info(efc, "[%s] %-20s\n", \
+ node->display_name, __func__); \
+ } while (0)
+
+#define els_io_printf(els, fmt, ...) \
+ efc_log_err((struct efc *)els->node->efc,\
+ "[%s] %-8s " fmt, \
+ els->node->display_name,\
+ els->display_name, ##__VA_ARGS__)
+
+#define EFC_ELS_RSP_LEN 1024
+#define EFC_ELS_GID_PT_RSP_LEN 8096
+
+struct efc_els_io_req *
+efc_els_io_alloc(struct efc_node *node, u32 reqlen)
+{
+ return efc_els_io_alloc_size(node, reqlen, EFC_ELS_RSP_LEN);
+}
+
+struct efc_els_io_req *
+efc_els_io_alloc_size(struct efc_node *node, u32 reqlen, u32 rsplen)
+{
+ struct efc *efc;
+ struct efc_els_io_req *els;
+ unsigned long flags = 0;
+
+ efc = node->efc;
+
+ if (!node->els_io_enabled) {
+ efc_log_err(efc, "els io alloc disabled\n");
+ return NULL;
+ }
+
+ els = mempool_alloc(efc->els_io_pool, GFP_ATOMIC);
+ if (!els) {
+ atomic_add_return(1, &efc->els_io_alloc_failed_count);
+ return NULL;
+ }
+
+ /* initialize refcount */
+ kref_init(&els->ref);
+ els->release = _efc_els_io_free;
+
+ /* populate generic io fields */
+ els->node = node;
+
+ /* now allocate DMA for request and response */
+ els->io.req.size = reqlen;
+ els->io.req.virt = dma_alloc_coherent(&efc->pci->dev, els->io.req.size,
+ &els->io.req.phys, GFP_KERNEL);
+ if (!els->io.req.virt) {
+ mempool_free(els, efc->els_io_pool);
+ return NULL;
+ }
+
+ els->io.rsp.size = rsplen;
+ els->io.rsp.virt = dma_alloc_coherent(&efc->pci->dev, els->io.rsp.size,
+ &els->io.rsp.phys, GFP_KERNEL);
+ if (!els->io.rsp.virt) {
+ dma_free_coherent(&efc->pci->dev, els->io.req.size,
+ els->io.req.virt, els->io.req.phys);
+ mempool_free(els, efc->els_io_pool);
+ els = NULL;
+ }
+
+ if (els) {
+ /* initialize fields */
+ els->els_retries_remaining = EFC_FC_ELS_DEFAULT_RETRIES;
+
+ /* add els structure to ELS IO list */
+ INIT_LIST_HEAD(&els->list_entry);
+ spin_lock_irqsave(&node->els_ios_lock, flags);
+ list_add_tail(&els->list_entry, &node->els_ios_list);
+ spin_unlock_irqrestore(&node->els_ios_lock, flags);
+ }
+
+ return els;
+}
+
+void
+efc_els_io_free(struct efc_els_io_req *els)
+{
+ kref_put(&els->ref, els->release);
+}
+
+void
+_efc_els_io_free(struct kref *arg)
+{
+ struct efc_els_io_req *els =
+ container_of(arg, struct efc_els_io_req, ref);
+ struct efc *efc;
+ struct efc_node *node;
+ int send_empty_event = false;
+ unsigned long flags = 0;
+
+ node = els->node;
+ efc = node->efc;
+
+ spin_lock_irqsave(&node->els_ios_lock, flags);
+
+ list_del(&els->list_entry);
+ /* Send list empty event if the IO allocator
+ * is disabled, and the list is empty
+ * If node->els_io_enabled was not checked,
+ * the event would be posted continually
+ */
+ send_empty_event = (!node->els_io_enabled &&
+ list_empty(&node->els_ios_list));
+
+ spin_unlock_irqrestore(&node->els_ios_lock, flags);
+
+ /* free ELS request and response buffers */
+ dma_free_coherent(&efc->pci->dev, els->io.rsp.size,
+ els->io.rsp.virt, els->io.rsp.phys);
+ dma_free_coherent(&efc->pci->dev, els->io.req.size,
+ els->io.req.virt, els->io.req.phys);
+
+ mempool_free(els, efc->els_io_pool);
+
+ if (send_empty_event)
+ efc_scsi_io_list_empty(node->efc, node);
+}
+
+static void
+efc_els_retry(struct efc_els_io_req *els);
+
+static void
+efc_els_delay_timer_cb(struct timer_list *t)
+{
+ struct efc_els_io_req *els = timer_container_of(els, t, delay_timer);
+
+ /* Retry delay timer expired, retry the ELS request */
+ efc_els_retry(els);
+}
+
+static int
+efc_els_req_cb(void *arg, u32 length, int status, u32 ext_status)
+{
+ struct efc_els_io_req *els;
+ struct efc_node *node;
+ struct efc *efc;
+ struct efc_node_cb cbdata;
+ u32 reason_code;
+
+ els = arg;
+ node = els->node;
+ efc = node->efc;
+
+ if (status)
+ els_io_printf(els, "status x%x ext x%x\n", status, ext_status);
+
+ /* set the response len element of els->rsp */
+ els->io.rsp.len = length;
+
+ cbdata.status = status;
+ cbdata.ext_status = ext_status;
+ cbdata.header = NULL;
+ cbdata.els_rsp = els->io.rsp;
+
+ /* set the response len element of els->rsp */
+ cbdata.rsp_len = length;
+
+ /* FW returns the number of bytes received on the link in
+ * the WCQE, not the amount placed in the buffer; use this info to
+ * check if there was an overrun.
+ */
+ if (length > els->io.rsp.size) {
+ efc_log_warn(efc,
+ "ELS response returned len=%d > buflen=%zu\n",
+ length, els->io.rsp.size);
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata);
+ return 0;
+ }
+
+ /* Post event to ELS IO object */
+ switch (status) {
+ case SLI4_FC_WCQE_STATUS_SUCCESS:
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_OK, &cbdata);
+ break;
+
+ case SLI4_FC_WCQE_STATUS_LS_RJT:
+ reason_code = (ext_status >> 16) & 0xff;
+
+ /* delay and retry if reason code is Logical Busy */
+ switch (reason_code) {
+ case ELS_RJT_BUSY:
+ els->node->els_req_cnt--;
+ els_io_printf(els,
+ "LS_RJT Logical Busy, delay and retry\n");
+ timer_setup(&els->delay_timer,
+ efc_els_delay_timer_cb, 0);
+ mod_timer(&els->delay_timer,
+ jiffies + msecs_to_jiffies(5000));
+ break;
+ default:
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_RJT,
+ &cbdata);
+ break;
+ }
+ break;
+
+ case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
+ switch (ext_status) {
+ case SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT:
+ efc_els_retry(els);
+ break;
+ default:
+ efc_log_err(efc, "LOCAL_REJECT with ext status:%x\n",
+ ext_status);
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL,
+ &cbdata);
+ break;
+ }
+ break;
+ default: /* Other error */
+ efc_log_warn(efc, "els req failed status x%x, ext_status x%x\n",
+ status, ext_status);
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata);
+ break;
+ }
+
+ return 0;
+}
+
+void efc_disc_io_complete(struct efc_disc_io *io, u32 len, u32 status,
+ u32 ext_status)
+{
+ struct efc_els_io_req *els =
+ container_of(io, struct efc_els_io_req, io);
+
+ WARN_ON_ONCE(!els->cb);
+
+ ((efc_hw_srrs_cb_t)els->cb) (els, len, status, ext_status);
+}
+
+static int efc_els_send_req(struct efc_node *node, struct efc_els_io_req *els,
+ enum efc_disc_io_type io_type)
+{
+ int rc = 0;
+ struct efc *efc = node->efc;
+ struct efc_node_cb cbdata;
+
+ /* update ELS request counter */
+ els->node->els_req_cnt++;
+
+ /* Prepare the IO request details */
+ els->io.io_type = io_type;
+ els->io.xmit_len = els->io.req.size;
+ els->io.rsp_len = els->io.rsp.size;
+ els->io.rpi = node->rnode.indicator;
+ els->io.vpi = node->nport->indicator;
+ els->io.s_id = node->nport->fc_id;
+ els->io.d_id = node->rnode.fc_id;
+
+ if (node->rnode.attached)
+ els->io.rpi_registered = true;
+
+ els->cb = efc_els_req_cb;
+
+ rc = efc->tt.send_els(efc, &els->io);
+ if (!rc)
+ return rc;
+
+ cbdata.status = EFC_STATUS_INVALID;
+ cbdata.ext_status = EFC_STATUS_INVALID;
+ cbdata.els_rsp = els->io.rsp;
+ efc_log_err(efc, "efc_els_send failed: %d\n", rc);
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata);
+
+ return rc;
+}
+
+static void
+efc_els_retry(struct efc_els_io_req *els)
+{
+ struct efc *efc;
+ struct efc_node_cb cbdata;
+ u32 rc;
+
+ efc = els->node->efc;
+ cbdata.status = EFC_STATUS_INVALID;
+ cbdata.ext_status = EFC_STATUS_INVALID;
+ cbdata.els_rsp = els->io.rsp;
+
+ if (els->els_retries_remaining) {
+ els->els_retries_remaining--;
+ rc = efc->tt.send_els(efc, &els->io);
+ } else {
+ rc = -EIO;
+ }
+
+ if (rc) {
+ efc_log_err(efc, "ELS retries exhausted\n");
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_REQ_FAIL, &cbdata);
+ }
+}
+
+static int
+efc_els_acc_cb(void *arg, u32 length, int status, u32 ext_status)
+{
+ struct efc_els_io_req *els;
+ struct efc_node *node;
+ struct efc *efc;
+ struct efc_node_cb cbdata;
+
+ els = arg;
+ node = els->node;
+ efc = node->efc;
+
+ cbdata.status = status;
+ cbdata.ext_status = ext_status;
+ cbdata.header = NULL;
+ cbdata.els_rsp = els->io.rsp;
+
+ /* Post node event */
+ switch (status) {
+ case SLI4_FC_WCQE_STATUS_SUCCESS:
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_CMPL_OK, &cbdata);
+ break;
+
+ default: /* Other error */
+ efc_log_warn(efc, "[%s] %-8s failed status x%x, ext x%x\n",
+ node->display_name, els->display_name,
+ status, ext_status);
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_CMPL_FAIL, &cbdata);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+efc_els_send_rsp(struct efc_els_io_req *els, u32 rsplen)
+{
+ int rc = 0;
+ struct efc_node_cb cbdata;
+ struct efc_node *node = els->node;
+ struct efc *efc = node->efc;
+
+ /* increment ELS completion counter */
+ node->els_cmpl_cnt++;
+
+ els->io.io_type = EFC_DISC_IO_ELS_RESP;
+ els->cb = efc_els_acc_cb;
+
+ /* Prepare the IO request details */
+ els->io.xmit_len = rsplen;
+ els->io.rsp_len = els->io.rsp.size;
+ els->io.rpi = node->rnode.indicator;
+ els->io.vpi = node->nport->indicator;
+ if (node->nport->fc_id != U32_MAX)
+ els->io.s_id = node->nport->fc_id;
+ else
+ els->io.s_id = els->io.iparam.els.s_id;
+ els->io.d_id = node->rnode.fc_id;
+
+ if (node->attached)
+ els->io.rpi_registered = true;
+
+ rc = efc->tt.send_els(efc, &els->io);
+ if (!rc)
+ return rc;
+
+ cbdata.status = EFC_STATUS_INVALID;
+ cbdata.ext_status = EFC_STATUS_INVALID;
+ cbdata.els_rsp = els->io.rsp;
+ efc_els_io_cleanup(els, EFC_EVT_SRRS_ELS_CMPL_FAIL, &cbdata);
+
+ return rc;
+}
+
+int
+efc_send_plogi(struct efc_node *node)
+{
+ struct efc_els_io_req *els;
+ struct efc *efc = node->efc;
+ struct fc_els_flogi *plogi;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*plogi));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+ els->display_name = "plogi";
+
+ /* Build PLOGI request */
+ plogi = els->io.req.virt;
+
+ memcpy(plogi, node->nport->service_params, sizeof(*plogi));
+
+ plogi->fl_cmd = ELS_PLOGI;
+ memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd));
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ);
+}
+
+int
+efc_send_flogi(struct efc_node *node)
+{
+ struct efc_els_io_req *els;
+ struct efc *efc;
+ struct fc_els_flogi *flogi;
+
+ efc = node->efc;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*flogi));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "flogi";
+
+ /* Build FLOGI request */
+ flogi = els->io.req.virt;
+
+ memcpy(flogi, node->nport->service_params, sizeof(*flogi));
+ flogi->fl_cmd = ELS_FLOGI;
+ memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ);
+}
+
+int
+efc_send_fdisc(struct efc_node *node)
+{
+ struct efc_els_io_req *els;
+ struct efc *efc;
+ struct fc_els_flogi *fdisc;
+
+ efc = node->efc;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*fdisc));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "fdisc";
+
+ /* Build FDISC request */
+ fdisc = els->io.req.virt;
+
+ memcpy(fdisc, node->nport->service_params, sizeof(*fdisc));
+ fdisc->fl_cmd = ELS_FDISC;
+ memset(fdisc->_fl_resvd, 0, sizeof(fdisc->_fl_resvd));
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ);
+}
+
+int
+efc_send_prli(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*pp));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "prli";
+
+ /* Build PRLI request */
+ pp = els->io.req.virt;
+
+ memset(pp, 0, sizeof(*pp));
+
+ pp->prli.prli_cmd = ELS_PRLI;
+ pp->prli.prli_spp_len = 16;
+ pp->prli.prli_len = cpu_to_be16(sizeof(*pp));
+ pp->spp.spp_type = FC_TYPE_FCP;
+ pp->spp.spp_type_ext = 0;
+ pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
+ pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS |
+ (node->nport->enable_ini ?
+ FCP_SPPF_INIT_FCN : 0) |
+ (node->nport->enable_tgt ?
+ FCP_SPPF_TARG_FCN : 0));
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ);
+}
+
+int
+efc_send_logo(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els;
+ struct fc_els_logo *logo;
+ struct fc_els_flogi *sparams;
+
+ node_els_trace();
+
+ sparams = (struct fc_els_flogi *)node->nport->service_params;
+
+ els = efc_els_io_alloc(node, sizeof(*logo));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "logo";
+
+ /* Build LOGO request */
+
+ logo = els->io.req.virt;
+
+ memset(logo, 0, sizeof(*logo));
+ logo->fl_cmd = ELS_LOGO;
+ hton24(logo->fl_n_port_id, node->rnode.nport->fc_id);
+ logo->fl_n_port_wwn = sparams->fl_wwpn;
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ);
+}
+
+int
+efc_send_adisc(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els;
+ struct fc_els_adisc *adisc;
+ struct fc_els_flogi *sparams;
+ struct efc_nport *nport = node->nport;
+
+ node_els_trace();
+
+ sparams = (struct fc_els_flogi *)node->nport->service_params;
+
+ els = efc_els_io_alloc(node, sizeof(*adisc));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "adisc";
+
+ /* Build ADISC request */
+
+ adisc = els->io.req.virt;
+
+ memset(adisc, 0, sizeof(*adisc));
+ adisc->adisc_cmd = ELS_ADISC;
+ hton24(adisc->adisc_hard_addr, nport->fc_id);
+ adisc->adisc_wwpn = sparams->fl_wwpn;
+ adisc->adisc_wwnn = sparams->fl_wwnn;
+ hton24(adisc->adisc_port_id, node->rnode.nport->fc_id);
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ);
+}
+
+int
+efc_send_scr(struct efc_node *node)
+{
+ struct efc_els_io_req *els;
+ struct efc *efc = node->efc;
+ struct fc_els_scr *req;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*req));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "scr";
+
+ req = els->io.req.virt;
+
+ memset(req, 0, sizeof(*req));
+ req->scr_cmd = ELS_SCR;
+ req->scr_reg_func = ELS_SCRF_FULL;
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_ELS_REQ);
+}
+
+int
+efc_send_ls_rjt(struct efc_node *node, u32 ox_id, u32 reason_code,
+ u32 reason_code_expl, u32 vendor_unique)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els = NULL;
+ struct fc_els_ls_rjt *rjt;
+
+ els = efc_els_io_alloc(node, sizeof(*rjt));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ node_els_trace();
+
+ els->display_name = "ls_rjt";
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+
+ rjt = els->io.req.virt;
+ memset(rjt, 0, sizeof(*rjt));
+
+ rjt->er_cmd = ELS_LS_RJT;
+ rjt->er_reason = reason_code;
+ rjt->er_explan = reason_code_expl;
+
+ return efc_els_send_rsp(els, sizeof(*rjt));
+}
+
+int
+efc_send_plogi_acc(struct efc_node *node, u32 ox_id)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els = NULL;
+ struct fc_els_flogi *plogi;
+ struct fc_els_flogi *req = (struct fc_els_flogi *)node->service_params;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*plogi));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "plogi_acc";
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+
+ plogi = els->io.req.virt;
+
+ /* copy our port's service parameters to payload */
+ memcpy(plogi, node->nport->service_params, sizeof(*plogi));
+ plogi->fl_cmd = ELS_LS_ACC;
+ memset(plogi->_fl_resvd, 0, sizeof(plogi->_fl_resvd));
+
+ /* Set Application header support bit if requested */
+ if (req->fl_csp.sp_features & cpu_to_be16(FC_SP_FT_BCAST))
+ plogi->fl_csp.sp_features |= cpu_to_be16(FC_SP_FT_BCAST);
+
+ return efc_els_send_rsp(els, sizeof(*plogi));
+}
+
+int
+efc_send_flogi_p2p_acc(struct efc_node *node, u32 ox_id, u32 s_id)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els = NULL;
+ struct fc_els_flogi *flogi;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*flogi));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "flogi_p2p_acc";
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+ els->io.iparam.els.s_id = s_id;
+
+ flogi = els->io.req.virt;
+
+ /* copy our port's service parameters to payload */
+ memcpy(flogi, node->nport->service_params, sizeof(*flogi));
+ flogi->fl_cmd = ELS_LS_ACC;
+ memset(flogi->_fl_resvd, 0, sizeof(flogi->_fl_resvd));
+
+ memset(flogi->fl_cssp, 0, sizeof(flogi->fl_cssp));
+
+ return efc_els_send_rsp(els, sizeof(*flogi));
+}
+
+int
+efc_send_prli_acc(struct efc_node *node, u32 ox_id)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els = NULL;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*pp));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "prli_acc";
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+
+ pp = els->io.req.virt;
+ memset(pp, 0, sizeof(*pp));
+
+ pp->prli.prli_cmd = ELS_LS_ACC;
+ pp->prli.prli_spp_len = 0x10;
+ pp->prli.prli_len = cpu_to_be16(sizeof(*pp));
+ pp->spp.spp_type = FC_TYPE_FCP;
+ pp->spp.spp_type_ext = 0;
+ pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR | FC_SPP_RESP_ACK;
+
+ pp->spp.spp_params = cpu_to_be32(FCP_SPPF_RD_XRDY_DIS |
+ (node->nport->enable_ini ?
+ FCP_SPPF_INIT_FCN : 0) |
+ (node->nport->enable_tgt ?
+ FCP_SPPF_TARG_FCN : 0));
+
+ return efc_els_send_rsp(els, sizeof(*pp));
+}
+
+int
+efc_send_prlo_acc(struct efc_node *node, u32 ox_id)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els = NULL;
+ struct {
+ struct fc_els_prlo prlo;
+ struct fc_els_spp spp;
+ } *pp;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*pp));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "prlo_acc";
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+
+ pp = els->io.req.virt;
+ memset(pp, 0, sizeof(*pp));
+ pp->prlo.prlo_cmd = ELS_LS_ACC;
+ pp->prlo.prlo_obs = 0x10;
+ pp->prlo.prlo_len = cpu_to_be16(sizeof(*pp));
+
+ pp->spp.spp_type = FC_TYPE_FCP;
+ pp->spp.spp_type_ext = 0;
+ pp->spp.spp_flags = FC_SPP_RESP_ACK;
+
+ return efc_els_send_rsp(els, sizeof(*pp));
+}
+
+int
+efc_send_ls_acc(struct efc_node *node, u32 ox_id)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els = NULL;
+ struct fc_els_ls_acc *acc;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*acc));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "ls_acc";
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+
+ acc = els->io.req.virt;
+ memset(acc, 0, sizeof(*acc));
+
+ acc->la_cmd = ELS_LS_ACC;
+
+ return efc_els_send_rsp(els, sizeof(*acc));
+}
+
+int
+efc_send_logo_acc(struct efc_node *node, u32 ox_id)
+{
+ struct efc_els_io_req *els = NULL;
+ struct efc *efc = node->efc;
+ struct fc_els_ls_acc *logo;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*logo));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "logo_acc";
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+
+ logo = els->io.req.virt;
+ memset(logo, 0, sizeof(*logo));
+
+ logo->la_cmd = ELS_LS_ACC;
+
+ return efc_els_send_rsp(els, sizeof(*logo));
+}
+
+int
+efc_send_adisc_acc(struct efc_node *node, u32 ox_id)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els = NULL;
+ struct fc_els_adisc *adisc;
+ struct fc_els_flogi *sparams;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*adisc));
+ if (!els) {
+ efc_log_err(efc, "els IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->display_name = "adisc_acc";
+
+ /* Go ahead and send the ELS_ACC */
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+ els->io.iparam.els.ox_id = ox_id;
+
+ sparams = (struct fc_els_flogi *)node->nport->service_params;
+ adisc = els->io.req.virt;
+ memset(adisc, 0, sizeof(*adisc));
+ adisc->adisc_cmd = ELS_LS_ACC;
+ adisc->adisc_wwpn = sparams->fl_wwpn;
+ adisc->adisc_wwnn = sparams->fl_wwnn;
+ hton24(adisc->adisc_port_id, node->rnode.nport->fc_id);
+
+ return efc_els_send_rsp(els, sizeof(*adisc));
+}
+
+static inline void
+fcct_build_req_header(struct fc_ct_hdr *hdr, u16 cmd, u16 max_size)
+{
+ hdr->ct_rev = FC_CT_REV;
+ hdr->ct_fs_type = FC_FST_DIR;
+ hdr->ct_fs_subtype = FC_NS_SUBTYPE;
+ hdr->ct_options = 0;
+ hdr->ct_cmd = cpu_to_be16(cmd);
+ /* words */
+ hdr->ct_mr_size = cpu_to_be16(max_size / (sizeof(u32)));
+ hdr->ct_reason = 0;
+ hdr->ct_explan = 0;
+ hdr->ct_vendor = 0;
+}
+
+int
+efc_ns_send_rftid(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els;
+ struct {
+ struct fc_ct_hdr hdr;
+ struct fc_ns_rft_id rftid;
+ } *ct;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*ct));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->io.iparam.ct.r_ctl = FC_RCTL_ELS_REQ;
+ els->io.iparam.ct.type = FC_TYPE_CT;
+ els->io.iparam.ct.df_ctl = 0;
+ els->io.iparam.ct.timeout = EFC_FC_ELS_SEND_DEFAULT_TIMEOUT;
+
+ els->display_name = "rftid";
+
+ ct = els->io.req.virt;
+ memset(ct, 0, sizeof(*ct));
+ fcct_build_req_header(&ct->hdr, FC_NS_RFT_ID,
+ sizeof(struct fc_ns_rft_id));
+
+ hton24(ct->rftid.fr_fid.fp_fid, node->rnode.nport->fc_id);
+ ct->rftid.fr_fts.ff_type_map[FC_TYPE_FCP / FC_NS_BPW] =
+ cpu_to_be32(1 << (FC_TYPE_FCP % FC_NS_BPW));
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_CT_REQ);
+}
+
+int
+efc_ns_send_rffid(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+ struct efc_els_io_req *els;
+ struct {
+ struct fc_ct_hdr hdr;
+ struct fc_ns_rff_id rffid;
+ } *ct;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc(node, sizeof(*ct));
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->io.iparam.ct.r_ctl = FC_RCTL_ELS_REQ;
+ els->io.iparam.ct.type = FC_TYPE_CT;
+ els->io.iparam.ct.df_ctl = 0;
+ els->io.iparam.ct.timeout = EFC_FC_ELS_SEND_DEFAULT_TIMEOUT;
+
+ els->display_name = "rffid";
+ ct = els->io.req.virt;
+
+ memset(ct, 0, sizeof(*ct));
+ fcct_build_req_header(&ct->hdr, FC_NS_RFF_ID,
+ sizeof(struct fc_ns_rff_id));
+
+ hton24(ct->rffid.fr_fid.fp_fid, node->rnode.nport->fc_id);
+ if (node->nport->enable_ini)
+ ct->rffid.fr_feat |= FCP_FEAT_INIT;
+ if (node->nport->enable_tgt)
+ ct->rffid.fr_feat |= FCP_FEAT_TARG;
+ ct->rffid.fr_type = FC_TYPE_FCP;
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_CT_REQ);
+}
+
+int
+efc_ns_send_gidpt(struct efc_node *node)
+{
+ struct efc_els_io_req *els = NULL;
+ struct efc *efc = node->efc;
+ struct {
+ struct fc_ct_hdr hdr;
+ struct fc_ns_gid_pt gidpt;
+ } *ct;
+
+ node_els_trace();
+
+ els = efc_els_io_alloc_size(node, sizeof(*ct), EFC_ELS_GID_PT_RSP_LEN);
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ els->io.iparam.ct.r_ctl = FC_RCTL_ELS_REQ;
+ els->io.iparam.ct.type = FC_TYPE_CT;
+ els->io.iparam.ct.df_ctl = 0;
+ els->io.iparam.ct.timeout = EFC_FC_ELS_SEND_DEFAULT_TIMEOUT;
+
+ els->display_name = "gidpt";
+
+ ct = els->io.req.virt;
+
+ memset(ct, 0, sizeof(*ct));
+ fcct_build_req_header(&ct->hdr, FC_NS_GID_PT,
+ sizeof(struct fc_ns_gid_pt));
+
+ ct->gidpt.fn_pt_type = FC_TYPE_FCP;
+
+ return efc_els_send_req(node, els, EFC_DISC_IO_CT_REQ);
+}
+
+void
+efc_els_io_cleanup(struct efc_els_io_req *els, int evt, void *arg)
+{
+ /* don't want further events that could come; e.g. abort requests
+ * from the node state machine; thus, disable state machine
+ */
+ els->els_req_free = true;
+ efc_node_post_els_resp(els->node, evt, arg);
+
+ efc_els_io_free(els);
+}
+
+static int
+efc_ct_acc_cb(void *arg, u32 length, int status, u32 ext_status)
+{
+ struct efc_els_io_req *els = arg;
+
+ efc_els_io_free(els);
+
+ return 0;
+}
+
+int
+efc_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id,
+ struct fc_ct_hdr *ct_hdr, u32 cmd_rsp_code,
+ u32 reason_code, u32 reason_code_explanation)
+{
+ struct efc_els_io_req *els = NULL;
+ struct fc_ct_hdr *rsp = NULL;
+
+ els = efc_els_io_alloc(node, 256);
+ if (!els) {
+ efc_log_err(efc, "IO alloc failed\n");
+ return -EIO;
+ }
+
+ rsp = els->io.rsp.virt;
+
+ *rsp = *ct_hdr;
+
+ fcct_build_req_header(rsp, cmd_rsp_code, 0);
+ rsp->ct_reason = reason_code;
+ rsp->ct_explan = reason_code_explanation;
+
+ els->display_name = "ct_rsp";
+ els->cb = efc_ct_acc_cb;
+
+ /* Prepare the IO request details */
+ els->io.io_type = EFC_DISC_IO_CT_RESP;
+ els->io.xmit_len = sizeof(*rsp);
+
+ els->io.rpi = node->rnode.indicator;
+ els->io.d_id = node->rnode.fc_id;
+
+ memset(&els->io.iparam, 0, sizeof(els->io.iparam));
+
+ els->io.iparam.ct.ox_id = ox_id;
+ els->io.iparam.ct.r_ctl = 3;
+ els->io.iparam.ct.type = FC_TYPE_CT;
+ els->io.iparam.ct.df_ctl = 0;
+ els->io.iparam.ct.timeout = 5;
+
+ if (efc->tt.send_els(efc, &els->io)) {
+ efc_els_io_free(els);
+ return -EIO;
+ }
+ return 0;
+}
+
+int
+efc_send_bls_acc(struct efc_node *node, struct fc_frame_header *hdr)
+{
+ struct sli_bls_params bls;
+ struct fc_ba_acc *acc;
+ struct efc *efc = node->efc;
+
+ memset(&bls, 0, sizeof(bls));
+ bls.ox_id = be16_to_cpu(hdr->fh_ox_id);
+ bls.rx_id = be16_to_cpu(hdr->fh_rx_id);
+ bls.s_id = ntoh24(hdr->fh_d_id);
+ bls.d_id = node->rnode.fc_id;
+ bls.rpi = node->rnode.indicator;
+ bls.vpi = node->nport->indicator;
+
+ acc = (void *)bls.payload;
+ acc->ba_ox_id = cpu_to_be16(bls.ox_id);
+ acc->ba_rx_id = cpu_to_be16(bls.rx_id);
+ acc->ba_high_seq_cnt = cpu_to_be16(U16_MAX);
+
+ return efc->tt.send_bls(efc, FC_RCTL_BA_ACC, &bls);
+}
diff --git a/drivers/scsi/elx/libefc/efc_els.h b/drivers/scsi/elx/libefc/efc_els.h
new file mode 100644
index 000000000000..3c4f820f602e
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_els.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#ifndef __EFC_ELS_H__
+#define __EFC_ELS_H__
+
+#define EFC_STATUS_INVALID INT_MAX
+#define EFC_ELS_IO_POOL_SZ 1024
+
+struct efc_els_io_req {
+ struct list_head list_entry;
+ struct kref ref;
+ void (*release)(struct kref *arg);
+ struct efc_node *node;
+ void *cb;
+ u32 els_retries_remaining;
+ bool els_req_free;
+ struct timer_list delay_timer;
+
+ const char *display_name;
+
+ struct efc_disc_io io;
+};
+
+typedef int(*efc_hw_srrs_cb_t)(void *arg, u32 length, int status,
+ u32 ext_status);
+
+void _efc_els_io_free(struct kref *arg);
+struct efc_els_io_req *
+efc_els_io_alloc(struct efc_node *node, u32 reqlen);
+struct efc_els_io_req *
+efc_els_io_alloc_size(struct efc_node *node, u32 reqlen, u32 rsplen);
+void efc_els_io_free(struct efc_els_io_req *els);
+
+/* ELS command send */
+typedef void (*els_cb_t)(struct efc_node *node,
+ struct efc_node_cb *cbdata, void *arg);
+int
+efc_send_plogi(struct efc_node *node);
+int
+efc_send_flogi(struct efc_node *node);
+int
+efc_send_fdisc(struct efc_node *node);
+int
+efc_send_prli(struct efc_node *node);
+int
+efc_send_prlo(struct efc_node *node);
+int
+efc_send_logo(struct efc_node *node);
+int
+efc_send_adisc(struct efc_node *node);
+int
+efc_send_pdisc(struct efc_node *node);
+int
+efc_send_scr(struct efc_node *node);
+int
+efc_ns_send_rftid(struct efc_node *node);
+int
+efc_ns_send_rffid(struct efc_node *node);
+int
+efc_ns_send_gidpt(struct efc_node *node);
+void
+efc_els_io_cleanup(struct efc_els_io_req *els, int evt, void *arg);
+
+/* ELS acc send */
+int
+efc_send_ls_acc(struct efc_node *node, u32 ox_id);
+int
+efc_send_ls_rjt(struct efc_node *node, u32 ox_id, u32 reason_cod,
+ u32 reason_code_expl, u32 vendor_unique);
+int
+efc_send_flogi_p2p_acc(struct efc_node *node, u32 ox_id, u32 s_id);
+int
+efc_send_flogi_acc(struct efc_node *node, u32 ox_id, u32 is_fport);
+int
+efc_send_plogi_acc(struct efc_node *node, u32 ox_id);
+int
+efc_send_prli_acc(struct efc_node *node, u32 ox_id);
+int
+efc_send_logo_acc(struct efc_node *node, u32 ox_id);
+int
+efc_send_prlo_acc(struct efc_node *node, u32 ox_id);
+int
+efc_send_adisc_acc(struct efc_node *node, u32 ox_id);
+
+int
+efc_bls_send_acc_hdr(struct efc *efc, struct efc_node *node,
+ struct fc_frame_header *hdr);
+int
+efc_bls_send_rjt_hdr(struct efc_els_io_req *io, struct fc_frame_header *hdr);
+
+int
+efc_els_io_list_empty(struct efc_node *node, struct list_head *list);
+
+/* CT */
+int
+efc_send_ct_rsp(struct efc *efc, struct efc_node *node, u16 ox_id,
+ struct fc_ct_hdr *ct_hdr, u32 cmd_rsp_code, u32 reason_code,
+ u32 reason_code_explanation);
+
+int
+efc_send_bls_acc(struct efc_node *node, struct fc_frame_header *hdr);
+
+#endif /* __EFC_ELS_H__ */
diff --git a/drivers/scsi/elx/libefc/efc_fabric.c b/drivers/scsi/elx/libefc/efc_fabric.c
new file mode 100644
index 000000000000..4ed9f46ded65
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_fabric.c
@@ -0,0 +1,1563 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * This file implements remote node state machines for:
+ * - Fabric logins.
+ * - Fabric controller events.
+ * - Name/directory services interaction.
+ * - Point-to-point logins.
+ */
+
+/*
+ * fabric_sm Node State Machine: Fabric States
+ * ns_sm Node State Machine: Name/Directory Services States
+ * p2p_sm Node State Machine: Point-to-Point Node States
+ */
+
+#include "efc.h"
+
+static void
+efc_fabric_initiate_shutdown(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+
+ node->els_io_enabled = false;
+
+ if (node->attached) {
+ int rc;
+
+ /* issue hw node free; don't care if succeeds right away
+ * or sometime later, will check node->attached later in
+ * shutdown process
+ */
+ rc = efc_cmd_node_detach(efc, &node->rnode);
+ if (rc < 0) {
+ node_printf(node, "Failed freeing HW node, rc=%d\n",
+ rc);
+ }
+ }
+ /*
+ * node has either been detached or is in the process of being detached,
+ * call common node's initiate cleanup function
+ */
+ efc_node_initiate_cleanup(node);
+}
+
+static void
+__efc_fabric_common(const char *funcname, struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = NULL;
+
+ node = ctx->app;
+
+ switch (evt) {
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ break;
+ case EFC_EVT_SHUTDOWN:
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ break;
+
+ default:
+ /* call default event handler common to all nodes */
+ __efc_node_common(funcname, ctx, evt, arg);
+ }
+}
+
+void
+__efc_fabric_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg)
+{
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_REENTER:
+ efc_log_debug(efc, ">>> reenter !!\n");
+ fallthrough;
+
+ case EFC_EVT_ENTER:
+ /* send FLOGI */
+ efc_send_flogi(node);
+ efc_node_transition(node, __efc_fabric_flogi_wait_rsp, NULL);
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+efc_fabric_set_topology(struct efc_node *node,
+ enum efc_nport_topology topology)
+{
+ node->nport->topology = topology;
+}
+
+void
+efc_fabric_notify_topology(struct efc_node *node)
+{
+ struct efc_node *tmp_node;
+ unsigned long index;
+
+ /*
+ * now loop through the nodes in the nport
+ * and send topology notification
+ */
+ xa_for_each(&node->nport->lookup, index, tmp_node) {
+ if (tmp_node != node) {
+ efc_node_post_event(tmp_node,
+ EFC_EVT_NPORT_TOPOLOGY_NOTIFY,
+ &node->nport->topology);
+ }
+ }
+}
+
+static bool efc_rnode_is_nport(struct fc_els_flogi *rsp)
+{
+ return !(ntohs(rsp->fl_csp.sp_features) & FC_SP_FT_FPORT);
+}
+
+void
+__efc_fabric_flogi_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK: {
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_FLOGI,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+
+ memcpy(node->nport->domain->flogi_service_params,
+ cbdata->els_rsp.virt,
+ sizeof(struct fc_els_flogi));
+
+ /* Check to see if the fabric is an F_PORT or and N_PORT */
+ if (!efc_rnode_is_nport(cbdata->els_rsp.virt)) {
+ /* sm: if not nport / efc_domain_attach */
+ /* ext_status has the fc_id, attach domain */
+ efc_fabric_set_topology(node, EFC_NPORT_TOPO_FABRIC);
+ efc_fabric_notify_topology(node);
+ WARN_ON(node->nport->domain->attached);
+ efc_domain_attach(node->nport->domain,
+ cbdata->ext_status);
+ efc_node_transition(node,
+ __efc_fabric_wait_domain_attach,
+ NULL);
+ break;
+ }
+
+ /* sm: if nport and p2p_winner / efc_domain_attach */
+ efc_fabric_set_topology(node, EFC_NPORT_TOPO_P2P);
+ if (efc_p2p_setup(node->nport)) {
+ node_printf(node,
+ "p2p setup failed, shutting down node\n");
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ break;
+ }
+
+ if (node->nport->p2p_winner) {
+ efc_node_transition(node,
+ __efc_p2p_wait_domain_attach,
+ NULL);
+ if (node->nport->domain->attached &&
+ !node->nport->domain->domain_notify_pend) {
+ /*
+ * already attached,
+ * just send ATTACH_OK
+ */
+ node_printf(node,
+ "p2p winner, domain already attached\n");
+ efc_node_post_event(node,
+ EFC_EVT_DOMAIN_ATTACH_OK,
+ NULL);
+ }
+ } else {
+ /*
+ * peer is p2p winner;
+ * PLOGI will be received on the
+ * remote SID=1 node;
+ * this node has served its purpose
+ */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ }
+
+ break;
+ }
+
+ case EFC_EVT_ELS_REQ_ABORTED:
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: {
+ struct efc_nport *nport = node->nport;
+ /*
+ * with these errors, we have no recovery,
+ * so shutdown the nport, leave the link
+ * up and the domain ready
+ */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_FLOGI,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ node_printf(node,
+ "FLOGI failed evt=%s, shutting down nport [%s]\n",
+ efc_sm_event_name(evt), nport->display_name);
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL);
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_vport_fabric_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ /* sm: / send FDISC */
+ efc_send_fdisc(node);
+ efc_node_transition(node, __efc_fabric_fdisc_wait_rsp, NULL);
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_fabric_fdisc_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK: {
+ /* fc_id is in ext_status */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_FDISC,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / efc_nport_attach */
+ efc_nport_attach(node->nport, cbdata->ext_status);
+ efc_node_transition(node, __efc_fabric_wait_domain_attach,
+ NULL);
+ break;
+ }
+
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: {
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_FDISC,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_log_err(node->efc, "FDISC failed, shutting down nport\n");
+ /* sm: / shutdown nport */
+ efc_sm_post_event(&node->nport->sm, EFC_EVT_SHUTDOWN, NULL);
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+static int
+efc_start_ns_node(struct efc_nport *nport)
+{
+ struct efc_node *ns;
+
+ /* Instantiate a name services node */
+ ns = efc_node_find(nport, FC_FID_DIR_SERV);
+ if (!ns) {
+ ns = efc_node_alloc(nport, FC_FID_DIR_SERV, false, false);
+ if (!ns)
+ return -EIO;
+ }
+ /*
+ * for found ns, should we be transitioning from here?
+ * breaks transition only
+ * 1. from within state machine or
+ * 2. if after alloc
+ */
+ if (ns->efc->nodedb_mask & EFC_NODEDB_PAUSE_NAMESERVER)
+ efc_node_pause(ns, __efc_ns_init);
+ else
+ efc_node_transition(ns, __efc_ns_init, NULL);
+ return 0;
+}
+
+static int
+efc_start_fabctl_node(struct efc_nport *nport)
+{
+ struct efc_node *fabctl;
+
+ fabctl = efc_node_find(nport, FC_FID_FCTRL);
+ if (!fabctl) {
+ fabctl = efc_node_alloc(nport, FC_FID_FCTRL,
+ false, false);
+ if (!fabctl)
+ return -EIO;
+ }
+ /*
+ * for found ns, should we be transitioning from here?
+ * breaks transition only
+ * 1. from within state machine or
+ * 2. if after alloc
+ */
+ efc_node_transition(fabctl, __efc_fabctl_init, NULL);
+ return 0;
+}
+
+void
+__efc_fabric_wait_domain_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ case EFC_EVT_NPORT_ATTACH_OK: {
+ int rc;
+
+ rc = efc_start_ns_node(node->nport);
+ if (rc)
+ return;
+
+ /* sm: if enable_ini / start fabctl node */
+ /* Instantiate the fabric controller (sends SCR) */
+ if (node->nport->enable_rscn) {
+ rc = efc_start_fabctl_node(node->nport);
+ if (rc)
+ return;
+ }
+ efc_node_transition(node, __efc_fabric_idle, NULL);
+ break;
+ }
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_fabric_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ break;
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_ns_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ /* sm: / send PLOGI */
+ efc_send_plogi(node);
+ efc_node_transition(node, __efc_ns_plogi_wait_rsp, NULL);
+ break;
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_ns_plogi_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK: {
+ int rc;
+
+ /* Save service parameters */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / save sparams, efc_node_attach */
+ efc_node_save_sparms(node, cbdata->els_rsp.virt);
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_ns_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
+ NULL);
+ break;
+ }
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_ns_wait_node_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_NODE_ATTACH_OK:
+ node->attached = true;
+ /* sm: / send RFTID */
+ efc_ns_send_rftid(node);
+ efc_node_transition(node, __efc_ns_rftid_wait_rsp, NULL);
+ break;
+
+ case EFC_EVT_NODE_ATTACH_FAIL:
+ /* node attach failed, shutdown the node */
+ node->attached = false;
+ node_printf(node, "Node attach failed\n");
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ break;
+
+ case EFC_EVT_SHUTDOWN:
+ node_printf(node, "Shutdown event received\n");
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_node_transition(node,
+ __efc_fabric_wait_attach_evt_shutdown,
+ NULL);
+ break;
+
+ /*
+ * if receive RSCN just ignore,
+ * we haven't sent GID_PT yet (ACC sent by fabctl node)
+ */
+ case EFC_EVT_RSCN_RCVD:
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_fabric_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ /* wait for any of these attach events and then shutdown */
+ case EFC_EVT_NODE_ATTACH_OK:
+ node->attached = true;
+ node_printf(node, "Attach evt=%s, proceed to shutdown\n",
+ efc_sm_event_name(evt));
+ efc_fabric_initiate_shutdown(node);
+ break;
+
+ case EFC_EVT_NODE_ATTACH_FAIL:
+ node->attached = false;
+ node_printf(node, "Attach evt=%s, proceed to shutdown\n",
+ efc_sm_event_name(evt));
+ efc_fabric_initiate_shutdown(node);
+ break;
+
+ /* ignore shutdown event as we're already in shutdown path */
+ case EFC_EVT_SHUTDOWN:
+ node_printf(node, "Shutdown event received\n");
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_ns_rftid_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK:
+ if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFT_ID,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / send RFFID */
+ efc_ns_send_rffid(node);
+ efc_node_transition(node, __efc_ns_rffid_wait_rsp, NULL);
+ break;
+
+ /*
+ * if receive RSCN just ignore,
+ * we haven't sent GID_PT yet (ACC sent by fabctl node)
+ */
+ case EFC_EVT_RSCN_RCVD:
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_ns_rffid_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ /*
+ * Waits for an RFFID response event;
+ * if rscn enabled, a GIDPT name services request is issued.
+ */
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK: {
+ if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_RFF_ID,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ if (node->nport->enable_rscn) {
+ /* sm: if enable_rscn / send GIDPT */
+ efc_ns_send_gidpt(node);
+
+ efc_node_transition(node, __efc_ns_gidpt_wait_rsp,
+ NULL);
+ } else {
+ /* if 'T' only, we're done, go to idle */
+ efc_node_transition(node, __efc_ns_idle, NULL);
+ }
+ break;
+ }
+ /*
+ * if receive RSCN just ignore,
+ * we haven't sent GID_PT yet (ACC sent by fabctl node)
+ */
+ case EFC_EVT_RSCN_RCVD:
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+static int
+efc_process_gidpt_payload(struct efc_node *node,
+ void *data, u32 gidpt_len)
+{
+ u32 i, j;
+ struct efc_node *newnode;
+ struct efc_nport *nport = node->nport;
+ struct efc *efc = node->efc;
+ u32 port_id = 0, port_count, plist_count;
+ struct efc_node *n;
+ struct efc_node **active_nodes;
+ int residual;
+ struct {
+ struct fc_ct_hdr hdr;
+ struct fc_gid_pn_resp pn_rsp;
+ } *rsp;
+ struct fc_gid_pn_resp *gidpt;
+ unsigned long index;
+
+ rsp = data;
+ gidpt = &rsp->pn_rsp;
+ residual = be16_to_cpu(rsp->hdr.ct_mr_size);
+
+ if (residual != 0)
+ efc_log_debug(node->efc, "residual is %u words\n", residual);
+
+ if (be16_to_cpu(rsp->hdr.ct_cmd) == FC_FS_RJT) {
+ node_printf(node,
+ "GIDPT request failed: rsn x%x rsn_expl x%x\n",
+ rsp->hdr.ct_reason, rsp->hdr.ct_explan);
+ return -EIO;
+ }
+
+ plist_count = (gidpt_len - sizeof(struct fc_ct_hdr)) / sizeof(*gidpt);
+
+ /* Count the number of nodes */
+ port_count = 0;
+ xa_for_each(&nport->lookup, index, n) {
+ port_count++;
+ }
+
+ /* Allocate a buffer for all nodes */
+ active_nodes = kcalloc(port_count, sizeof(*active_nodes), GFP_ATOMIC);
+ if (!active_nodes) {
+ node_printf(node, "efc_malloc failed\n");
+ return -EIO;
+ }
+
+ /* Fill buffer with fc_id of active nodes */
+ i = 0;
+ xa_for_each(&nport->lookup, index, n) {
+ port_id = n->rnode.fc_id;
+ switch (port_id) {
+ case FC_FID_FLOGI:
+ case FC_FID_FCTRL:
+ case FC_FID_DIR_SERV:
+ break;
+ default:
+ if (port_id != FC_FID_DOM_MGR)
+ active_nodes[i++] = n;
+ break;
+ }
+ }
+
+ /* update the active nodes buffer */
+ for (i = 0; i < plist_count; i++) {
+ hton24(gidpt[i].fp_fid, port_id);
+
+ for (j = 0; j < port_count; j++) {
+ if (active_nodes[j] &&
+ port_id == active_nodes[j]->rnode.fc_id) {
+ active_nodes[j] = NULL;
+ }
+ }
+
+ if (gidpt[i].fp_resvd & FC_NS_FID_LAST)
+ break;
+ }
+
+ /* Those remaining in the active_nodes[] are now gone ! */
+ for (i = 0; i < port_count; i++) {
+ /*
+ * if we're an initiator and the remote node
+ * is a target, then post the node missing event.
+ * if we're target and we have enabled
+ * target RSCN, then post the node missing event.
+ */
+ if (!active_nodes[i])
+ continue;
+
+ if ((node->nport->enable_ini && active_nodes[i]->targ) ||
+ (node->nport->enable_tgt && enable_target_rscn(efc))) {
+ efc_node_post_event(active_nodes[i],
+ EFC_EVT_NODE_MISSING, NULL);
+ } else {
+ node_printf(node,
+ "GID_PT: skipping non-tgt port_id x%06x\n",
+ active_nodes[i]->rnode.fc_id);
+ }
+ }
+ kfree(active_nodes);
+
+ for (i = 0; i < plist_count; i++) {
+ hton24(gidpt[i].fp_fid, port_id);
+
+ /* Don't create node for ourselves */
+ if (port_id == node->rnode.nport->fc_id) {
+ if (gidpt[i].fp_resvd & FC_NS_FID_LAST)
+ break;
+ continue;
+ }
+
+ newnode = efc_node_find(nport, port_id);
+ if (!newnode) {
+ if (!node->nport->enable_ini)
+ continue;
+
+ newnode = efc_node_alloc(nport, port_id, false, false);
+ if (!newnode) {
+ efc_log_err(efc, "efc_node_alloc() failed\n");
+ return -EIO;
+ }
+ /*
+ * send PLOGI automatically
+ * if initiator
+ */
+ efc_node_init_device(newnode, true);
+ }
+
+ if (node->nport->enable_ini && newnode->targ) {
+ efc_node_post_event(newnode, EFC_EVT_NODE_REFOUND,
+ NULL);
+ }
+
+ if (gidpt[i].fp_resvd & FC_NS_FID_LAST)
+ break;
+ }
+ return 0;
+}
+
+void
+__efc_ns_gidpt_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+ /*
+ * Wait for a GIDPT response from the name server. Process the FC_IDs
+ * that are reported by creating new remote ports, as needed.
+ */
+
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK: {
+ if (efc_node_check_ns_req(ctx, evt, arg, FC_NS_GID_PT,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / process GIDPT payload */
+ efc_process_gidpt_payload(node, cbdata->els_rsp.virt,
+ cbdata->els_rsp.len);
+ efc_node_transition(node, __efc_ns_idle, NULL);
+ break;
+ }
+
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: {
+ /* not much we can do; will retry with the next RSCN */
+ node_printf(node, "GID_PT failed to complete\n");
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_node_transition(node, __efc_ns_idle, NULL);
+ break;
+ }
+
+ /* if receive RSCN here, queue up another discovery processing */
+ case EFC_EVT_RSCN_RCVD: {
+ node_printf(node, "RSCN received during GID_PT processing\n");
+ node->rscn_pending = true;
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_ns_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ /*
+ * Wait for RSCN received events (posted from the fabric controller)
+ * and restart the GIDPT name services query and processing.
+ */
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ if (!node->rscn_pending)
+ break;
+
+ node_printf(node, "RSCN pending, restart discovery\n");
+ node->rscn_pending = false;
+ fallthrough;
+
+ case EFC_EVT_RSCN_RCVD: {
+ /* sm: / send GIDPT */
+ /*
+ * If target RSCN processing is enabled,
+ * and this is target only (not initiator),
+ * and tgt_rscn_delay is non-zero,
+ * then we delay issuing the GID_PT
+ */
+ if (efc->tgt_rscn_delay_msec != 0 &&
+ !node->nport->enable_ini && node->nport->enable_tgt &&
+ enable_target_rscn(efc)) {
+ efc_node_transition(node, __efc_ns_gidpt_delay, NULL);
+ } else {
+ efc_ns_send_gidpt(node);
+ efc_node_transition(node, __efc_ns_gidpt_wait_rsp,
+ NULL);
+ }
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+static void
+gidpt_delay_timer_cb(struct timer_list *t)
+{
+ struct efc_node *node = timer_container_of(node, t, gidpt_delay_timer);
+
+ timer_delete(&node->gidpt_delay_timer);
+
+ efc_node_post_event(node, EFC_EVT_GIDPT_DELAY_EXPIRED, NULL);
+}
+
+void
+__efc_ns_gidpt_delay(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER: {
+ u64 delay_msec, tmp;
+
+ /*
+ * Compute the delay time.
+ * Set to tgt_rscn_delay, if the time since last GIDPT
+ * is less than tgt_rscn_period, then use tgt_rscn_period.
+ */
+ delay_msec = efc->tgt_rscn_delay_msec;
+ tmp = jiffies_to_msecs(jiffies) - node->time_last_gidpt_msec;
+ if (tmp < efc->tgt_rscn_period_msec)
+ delay_msec = efc->tgt_rscn_period_msec;
+
+ timer_setup(&node->gidpt_delay_timer, &gidpt_delay_timer_cb,
+ 0);
+ mod_timer(&node->gidpt_delay_timer,
+ jiffies + msecs_to_jiffies(delay_msec));
+
+ break;
+ }
+
+ case EFC_EVT_GIDPT_DELAY_EXPIRED:
+ node->time_last_gidpt_msec = jiffies_to_msecs(jiffies);
+
+ efc_ns_send_gidpt(node);
+ efc_node_transition(node, __efc_ns_gidpt_wait_rsp, NULL);
+ break;
+
+ case EFC_EVT_RSCN_RCVD: {
+ efc_log_debug(efc,
+ "RSCN received while in GIDPT delay - no action\n");
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_fabctl_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ /* no need to login to fabric controller, just send SCR */
+ efc_send_scr(node);
+ efc_node_transition(node, __efc_fabctl_wait_scr_rsp, NULL);
+ break;
+
+ case EFC_EVT_NODE_ATTACH_OK:
+ node->attached = true;
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_fabctl_wait_scr_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ /*
+ * Fabric controller node state machine:
+ * Wait for an SCR response from the fabric controller.
+ */
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK:
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_SCR,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ efc_node_transition(node, __efc_fabctl_ready, NULL);
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+static void
+efc_process_rscn(struct efc_node *node, struct efc_node_cb *cbdata)
+{
+ struct efc *efc = node->efc;
+ struct efc_nport *nport = node->nport;
+ struct efc_node *ns;
+
+ /* Forward this event to the name-services node */
+ ns = efc_node_find(nport, FC_FID_DIR_SERV);
+ if (ns)
+ efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, cbdata);
+ else
+ efc_log_warn(efc, "can't find name server node\n");
+}
+
+void
+__efc_fabctl_ready(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ /*
+ * Fabric controller node state machine: Ready.
+ * In this state, the fabric controller sends a RSCN, which is received
+ * by this node and is forwarded to the name services node object; and
+ * the RSCN LS_ACC is sent.
+ */
+ switch (evt) {
+ case EFC_EVT_RSCN_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ /*
+ * sm: / process RSCN (forward to name services node),
+ * send LS_ACC
+ */
+ efc_process_rscn(node, cbdata);
+ efc_send_ls_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ efc_node_transition(node, __efc_fabctl_wait_ls_acc_cmpl,
+ NULL);
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_fabctl_wait_ls_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_OK:
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ efc_node_transition(node, __efc_fabctl_ready, NULL);
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+static uint64_t
+efc_get_wwpn(struct fc_els_flogi *sp)
+{
+ return be64_to_cpu(sp->fl_wwnn);
+}
+
+static int
+efc_rnode_is_winner(struct efc_nport *nport)
+{
+ struct fc_els_flogi *remote_sp;
+ u64 remote_wwpn;
+ u64 local_wwpn = nport->wwpn;
+ u64 wwn_bump = 0;
+
+ remote_sp = (struct fc_els_flogi *)nport->domain->flogi_service_params;
+ remote_wwpn = efc_get_wwpn(remote_sp);
+
+ local_wwpn ^= wwn_bump;
+
+ efc_log_debug(nport->efc, "r: %llx\n",
+ be64_to_cpu(remote_sp->fl_wwpn));
+ efc_log_debug(nport->efc, "l: %llx\n", local_wwpn);
+
+ if (remote_wwpn == local_wwpn) {
+ efc_log_warn(nport->efc,
+ "WWPN of remote node [%08x %08x] matches local WWPN\n",
+ (u32)(local_wwpn >> 32ll),
+ (u32)local_wwpn);
+ return -1;
+ }
+
+ return (remote_wwpn > local_wwpn);
+}
+
+void
+__efc_p2p_wait_domain_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_DOMAIN_ATTACH_OK: {
+ struct efc_nport *nport = node->nport;
+ struct efc_node *rnode;
+
+ /*
+ * this transient node (SID=0 (recv'd FLOGI)
+ * or DID=fabric (sent FLOGI))
+ * is the p2p winner, will use a separate node
+ * to send PLOGI to peer
+ */
+ WARN_ON(!node->nport->p2p_winner);
+
+ rnode = efc_node_find(nport, node->nport->p2p_remote_port_id);
+ if (rnode) {
+ /*
+ * the "other" transient p2p node has
+ * already kicked off the
+ * new node from which PLOGI is sent
+ */
+ node_printf(node,
+ "Node with fc_id x%x already exists\n",
+ rnode->rnode.fc_id);
+ } else {
+ /*
+ * create new node (SID=1, DID=2)
+ * from which to send PLOGI
+ */
+ rnode = efc_node_alloc(nport,
+ nport->p2p_remote_port_id,
+ false, false);
+ if (!rnode) {
+ efc_log_err(efc, "node alloc failed\n");
+ return;
+ }
+
+ efc_fabric_notify_topology(node);
+ /* sm: / allocate p2p remote node */
+ efc_node_transition(rnode, __efc_p2p_rnode_init,
+ NULL);
+ }
+
+ /*
+ * the transient node (SID=0 or DID=fabric)
+ * has served its purpose
+ */
+ if (node->rnode.fc_id == 0) {
+ /*
+ * if this is the SID=0 node,
+ * move to the init state in case peer
+ * has restarted FLOGI discovery and FLOGI is pending
+ */
+ /* don't send PLOGI on efc_d_init entry */
+ efc_node_init_device(node, false);
+ } else {
+ /*
+ * if this is the DID=fabric node
+ * (we initiated FLOGI), shut it down
+ */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ }
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_p2p_rnode_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ /* sm: / send PLOGI */
+ efc_send_plogi(node);
+ efc_node_transition(node, __efc_p2p_wait_plogi_rsp, NULL);
+ break;
+
+ case EFC_EVT_ABTS_RCVD:
+ /* sm: send BA_ACC */
+ efc_send_bls_acc(node, cbdata->header->dma.virt);
+
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_p2p_wait_flogi_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_OK:
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+
+ /* sm: if p2p_winner / domain_attach */
+ if (node->nport->p2p_winner) {
+ efc_node_transition(node,
+ __efc_p2p_wait_domain_attach,
+ NULL);
+ if (!node->nport->domain->attached) {
+ node_printf(node, "Domain not attached\n");
+ efc_domain_attach(node->nport->domain,
+ node->nport->p2p_port_id);
+ } else {
+ node_printf(node, "Domain already attached\n");
+ efc_node_post_event(node,
+ EFC_EVT_DOMAIN_ATTACH_OK,
+ NULL);
+ }
+ } else {
+ /* this node has served its purpose;
+ * we'll expect a PLOGI on a separate
+ * node (remote SID=0x1); return this node
+ * to init state in case peer
+ * restarts discovery -- it may already
+ * have (pending frames may exist).
+ */
+ /* don't send PLOGI on efc_d_init entry */
+ efc_node_init_device(node, false);
+ }
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+ /*
+ * LS_ACC failed, possibly due to link down;
+ * shutdown node and wait
+ * for FLOGI discovery to restart
+ */
+ node_printf(node, "FLOGI LS_ACC failed, shutting down\n");
+ WARN_ON(!node->els_cmpl_cnt);
+ node->els_cmpl_cnt--;
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ break;
+
+ case EFC_EVT_ABTS_RCVD: {
+ /* sm: / send BA_ACC */
+ efc_send_bls_acc(node, cbdata->header->dma.virt);
+ break;
+ }
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_p2p_wait_plogi_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_SRRS_ELS_REQ_OK: {
+ int rc;
+
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / save sparams, efc_node_attach */
+ efc_node_save_sparms(node, cbdata->els_rsp.virt);
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_p2p_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
+ NULL);
+ break;
+ }
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: {
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ node_printf(node, "PLOGI failed, shutting down\n");
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ break;
+ }
+
+ case EFC_EVT_PLOGI_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+ /* if we're in external loopback mode, just send LS_ACC */
+ if (node->efc->external_loopback) {
+ efc_send_plogi_acc(node, be16_to_cpu(hdr->fh_ox_id));
+ } else {
+ /*
+ * if this isn't external loopback,
+ * pass to default handler
+ */
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+ break;
+ }
+ case EFC_EVT_PRLI_RCVD:
+ /* I, or I+T */
+ /* sent PLOGI and before completion was seen, received the
+ * PRLI from the remote node (WCQEs and RCQEs come in on
+ * different queues and order of processing cannot be assumed)
+ * Save OXID so PRLI can be sent after the attach and continue
+ * to wait for PLOGI response
+ */
+ efc_process_prli_payload(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PRLI);
+ efc_node_transition(node, __efc_p2p_wait_plogi_rsp_recvd_prli,
+ NULL);
+ break;
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_p2p_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ /*
+ * Since we've received a PRLI, we have a port login and will
+ * just need to wait for the PLOGI response to do the node
+ * attach and then we can send the LS_ACC for the PRLI. If,
+ * during this time, we receive FCP_CMNDs (which is possible
+ * since we've already sent a PRLI and our peer may have
+ * accepted).
+ * At this time, we are not waiting on any other unsolicited
+ * frames to continue with the login process. Thus, it will not
+ * hurt to hold frames here.
+ */
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_OK: { /* PLOGI response received */
+ int rc;
+
+ /* Completion from PLOGI sent */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ /* sm: / save sparams, efc_node_attach */
+ efc_node_save_sparms(node, cbdata->els_rsp.virt);
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_p2p_wait_node_attach, NULL);
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL,
+ NULL);
+ break;
+ }
+ case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ /* PLOGI failed, shutdown the node */
+ if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI,
+ __efc_fabric_common, __func__)) {
+ return;
+ }
+ WARN_ON(!node->els_req_cnt);
+ node->els_req_cnt--;
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_p2p_wait_node_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node_cb *cbdata = arg;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_NODE_ATTACH_OK:
+ node->attached = true;
+ switch (node->send_ls_acc) {
+ case EFC_NODE_SEND_LS_ACC_PRLI: {
+ efc_d_send_prli_rsp(node->ls_acc_io,
+ node->ls_acc_oxid);
+ node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
+ node->ls_acc_io = NULL;
+ break;
+ }
+ case EFC_NODE_SEND_LS_ACC_PLOGI: /* Can't happen in P2P */
+ case EFC_NODE_SEND_LS_ACC_NONE:
+ default:
+ /* Normal case for I */
+ /* sm: send_plogi_acc is not set / send PLOGI acc */
+ efc_node_transition(node, __efc_d_port_logged_in,
+ NULL);
+ break;
+ }
+ break;
+
+ case EFC_EVT_NODE_ATTACH_FAIL:
+ /* node attach failed, shutdown the node */
+ node->attached = false;
+ node_printf(node, "Node attach failed\n");
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_fabric_initiate_shutdown(node);
+ break;
+
+ case EFC_EVT_SHUTDOWN:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ efc_node_transition(node,
+ __efc_fabric_wait_attach_evt_shutdown,
+ NULL);
+ break;
+ case EFC_EVT_PRLI_RCVD:
+ node_printf(node, "%s: PRLI received before node is attached\n",
+ efc_sm_event_name(evt));
+ efc_process_prli_payload(node, cbdata->payload->dma.virt);
+ efc_send_ls_acc_after_attach(node,
+ cbdata->header->dma.virt,
+ EFC_NODE_SEND_LS_ACC_PRLI);
+ break;
+
+ default:
+ __efc_fabric_common(__func__, ctx, evt, arg);
+ }
+}
+
+int
+efc_p2p_setup(struct efc_nport *nport)
+{
+ struct efc *efc = nport->efc;
+ int rnode_winner;
+
+ rnode_winner = efc_rnode_is_winner(nport);
+
+ /* set nport flags to indicate p2p "winner" */
+ if (rnode_winner == 1) {
+ nport->p2p_remote_port_id = 0;
+ nport->p2p_port_id = 0;
+ nport->p2p_winner = false;
+ } else if (rnode_winner == 0) {
+ nport->p2p_remote_port_id = 2;
+ nport->p2p_port_id = 1;
+ nport->p2p_winner = true;
+ } else {
+ /* no winner; only okay if external loopback enabled */
+ if (nport->efc->external_loopback) {
+ /*
+ * External loopback mode enabled;
+ * local nport and remote node
+ * will be registered with an NPortID = 1;
+ */
+ efc_log_debug(efc,
+ "External loopback mode enabled\n");
+ nport->p2p_remote_port_id = 1;
+ nport->p2p_port_id = 1;
+ nport->p2p_winner = true;
+ } else {
+ efc_log_warn(efc,
+ "failed to determine p2p winner\n");
+ return rnode_winner;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/scsi/elx/libefc/efc_fabric.h b/drivers/scsi/elx/libefc/efc_fabric.h
new file mode 100644
index 000000000000..b0947ae6fdca
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_fabric.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * Declarations for the interface exported by efc_fabric
+ */
+
+#ifndef __EFCT_FABRIC_H__
+#define __EFCT_FABRIC_H__
+#include "scsi/fc/fc_els.h"
+#include "scsi/fc/fc_fs.h"
+#include "scsi/fc/fc_ns.h"
+
+void
+__efc_fabric_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabric_flogi_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabric_domain_attach_wait(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabric_wait_domain_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+void
+__efc_vport_fabric_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabric_fdisc_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabric_wait_nport_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+void
+__efc_ns_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg);
+void
+__efc_ns_plogi_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_ns_rftid_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_ns_rffid_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_ns_wait_node_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabric_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_ns_logo_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event, void *arg);
+void
+__efc_ns_gidpt_wait_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_ns_idle(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg);
+void
+__efc_ns_gidpt_delay(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabctl_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabctl_wait_node_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabctl_wait_scr_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabctl_ready(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabctl_wait_ls_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_fabric_idle(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+void
+__efc_p2p_rnode_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_p2p_domain_attach_wait(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_p2p_wait_flogi_acc_cmpl(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_p2p_wait_plogi_rsp(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_p2p_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_p2p_wait_domain_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_p2p_wait_node_attach(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+int
+efc_p2p_setup(struct efc_nport *nport);
+void
+efc_fabric_set_topology(struct efc_node *node,
+ enum efc_nport_topology topology);
+void efc_fabric_notify_topology(struct efc_node *node);
+
+#endif /* __EFCT_FABRIC_H__ */
diff --git a/drivers/scsi/elx/libefc/efc_node.c b/drivers/scsi/elx/libefc/efc_node.c
new file mode 100644
index 000000000000..f17e052fe537
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_node.c
@@ -0,0 +1,1102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#include "efc.h"
+
+int
+efc_remote_node_cb(void *arg, int event, void *data)
+{
+ struct efc *efc = arg;
+ struct efc_remote_node *rnode = data;
+ struct efc_node *node = rnode->node;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->lock, flags);
+ efc_node_post_event(node, event, NULL);
+ spin_unlock_irqrestore(&efc->lock, flags);
+
+ return 0;
+}
+
+struct efc_node *
+efc_node_find(struct efc_nport *nport, u32 port_id)
+{
+ /* Find an FC node structure given the FC port ID */
+ return xa_load(&nport->lookup, port_id);
+}
+
+static void
+_efc_node_free(struct kref *arg)
+{
+ struct efc_node *node = container_of(arg, struct efc_node, ref);
+ struct efc *efc = node->efc;
+ struct efc_dma *dma;
+
+ dma = &node->sparm_dma_buf;
+ dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys);
+ memset(dma, 0, sizeof(struct efc_dma));
+ mempool_free(node, efc->node_pool);
+}
+
+struct efc_node *efc_node_alloc(struct efc_nport *nport,
+ u32 port_id, bool init, bool targ)
+{
+ int rc;
+ struct efc_node *node = NULL;
+ struct efc *efc = nport->efc;
+ struct efc_dma *dma;
+
+ if (nport->shutting_down) {
+ efc_log_debug(efc, "node allocation when shutting down %06x",
+ port_id);
+ return NULL;
+ }
+
+ node = mempool_alloc(efc->node_pool, GFP_ATOMIC);
+ if (!node) {
+ efc_log_err(efc, "node allocation failed %06x", port_id);
+ return NULL;
+ }
+ memset(node, 0, sizeof(*node));
+
+ dma = &node->sparm_dma_buf;
+ dma->size = NODE_SPARAMS_SIZE;
+ dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys);
+ if (!dma->virt) {
+ efc_log_err(efc, "node dma alloc failed\n");
+ goto dma_fail;
+ }
+ node->rnode.indicator = U32_MAX;
+ node->nport = nport;
+
+ node->efc = efc;
+ node->init = init;
+ node->targ = targ;
+
+ spin_lock_init(&node->pend_frames_lock);
+ INIT_LIST_HEAD(&node->pend_frames);
+ spin_lock_init(&node->els_ios_lock);
+ INIT_LIST_HEAD(&node->els_ios_list);
+ node->els_io_enabled = true;
+
+ rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport);
+ if (rc) {
+ efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc);
+ goto hw_alloc_fail;
+ }
+
+ node->rnode.node = node;
+ node->sm.app = node;
+ node->evtdepth = 0;
+
+ efc_node_update_display_name(node);
+
+ rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC));
+ if (rc) {
+ efc_log_err(efc, "Node lookup store failed: %d\n", rc);
+ goto xa_fail;
+ }
+
+ /* initialize refcount */
+ kref_init(&node->ref);
+ node->release = _efc_node_free;
+ kref_get(&nport->ref);
+
+ return node;
+
+xa_fail:
+ efc_node_free_resources(efc, &node->rnode);
+hw_alloc_fail:
+ dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys);
+dma_fail:
+ mempool_free(node, efc->node_pool);
+ return NULL;
+}
+
+void
+efc_node_free(struct efc_node *node)
+{
+ struct efc_nport *nport;
+ struct efc *efc;
+ int rc = 0;
+ struct efc_node *ns = NULL;
+
+ nport = node->nport;
+ efc = node->efc;
+
+ node_printf(node, "Free'd\n");
+
+ if (node->refound) {
+ /*
+ * Save the name server node. We will send fake RSCN event at
+ * the end to handle ignored RSCN event during node deletion
+ */
+ ns = efc_node_find(node->nport, FC_FID_DIR_SERV);
+ }
+
+ if (!node->nport) {
+ efc_log_err(efc, "Node already Freed\n");
+ return;
+ }
+
+ /* Free HW resources */
+ rc = efc_node_free_resources(efc, &node->rnode);
+ if (rc < 0)
+ efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc);
+
+ /* if the gidpt_delay_timer is still running, then delete it */
+ if (timer_pending(&node->gidpt_delay_timer))
+ timer_delete(&node->gidpt_delay_timer);
+
+ xa_erase(&nport->lookup, node->rnode.fc_id);
+
+ /*
+ * If the node_list is empty,
+ * then post a ALL_CHILD_NODES_FREE event to the nport,
+ * after the lock is released.
+ * The nport may be free'd as a result of the event.
+ */
+ if (xa_empty(&nport->lookup))
+ efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE,
+ NULL);
+
+ node->nport = NULL;
+ node->sm.current_state = NULL;
+
+ kref_put(&nport->ref, nport->release);
+ kref_put(&node->ref, node->release);
+
+ if (ns) {
+ /* sending fake RSCN event to name server node */
+ efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL);
+ }
+}
+
+static void
+efc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length)
+{
+ if (!dma || !buffer || !buffer_length)
+ return;
+
+ if (buffer_length > dma->size)
+ buffer_length = dma->size;
+
+ memcpy(dma->virt, buffer, buffer_length);
+ dma->len = buffer_length;
+}
+
+int
+efc_node_attach(struct efc_node *node)
+{
+ int rc = 0;
+ struct efc_nport *nport = node->nport;
+ struct efc_domain *domain = nport->domain;
+ struct efc *efc = node->efc;
+
+ if (!domain->attached) {
+ efc_log_err(efc, "Warning: unattached domain\n");
+ return -EIO;
+ }
+ /* Update node->wwpn/wwnn */
+
+ efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn),
+ efc_node_get_wwpn(node));
+ efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn),
+ efc_node_get_wwnn(node));
+
+ efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4,
+ sizeof(node->service_params) - 4);
+
+ /* take lock to protect node->rnode.attached */
+ rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf);
+ if (rc < 0)
+ efc_log_debug(efc, "efc_hw_node_attach failed: %d\n", rc);
+
+ return rc;
+}
+
+void
+efc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length)
+{
+ switch (fc_id) {
+ case FC_FID_FLOGI:
+ snprintf(buffer, buffer_length, "fabric");
+ break;
+ case FC_FID_FCTRL:
+ snprintf(buffer, buffer_length, "fabctl");
+ break;
+ case FC_FID_DIR_SERV:
+ snprintf(buffer, buffer_length, "nserve");
+ break;
+ default:
+ if (fc_id == FC_FID_DOM_MGR) {
+ snprintf(buffer, buffer_length, "dctl%02x",
+ (fc_id & 0x0000ff));
+ } else {
+ snprintf(buffer, buffer_length, "%06x", fc_id);
+ }
+ break;
+ }
+}
+
+void
+efc_node_update_display_name(struct efc_node *node)
+{
+ u32 port_id = node->rnode.fc_id;
+ struct efc_nport *nport = node->nport;
+ char portid_display[16];
+
+ efc_node_fcid_display(port_id, portid_display, sizeof(portid_display));
+
+ snprintf(node->display_name, sizeof(node->display_name), "%s.%s",
+ nport->display_name, portid_display);
+}
+
+void
+efc_node_send_ls_io_cleanup(struct efc_node *node)
+{
+ if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) {
+ efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n",
+ node->display_name, node->ls_acc_oxid);
+
+ node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;
+ node->ls_acc_io = NULL;
+ }
+}
+
+static void efc_node_handle_implicit_logo(struct efc_node *node)
+{
+ int rc;
+
+ /*
+ * currently, only case for implicit logo is PLOGI
+ * recvd. Thus, node's ELS IO pending list won't be
+ * empty (PLOGI will be on it)
+ */
+ WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI);
+ node_printf(node, "Reason: implicit logout, re-authenticate\n");
+
+ /* Re-attach node with the same HW node resources */
+ node->req_free = false;
+ rc = efc_node_attach(node);
+ efc_node_transition(node, __efc_d_wait_node_attach, NULL);
+ node->els_io_enabled = true;
+
+ if (rc < 0)
+ efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL);
+}
+
+static void efc_node_handle_explicit_logo(struct efc_node *node)
+{
+ s8 pend_frames_empty;
+ unsigned long flags = 0;
+
+ /* cleanup any pending LS_ACC ELSs */
+ efc_node_send_ls_io_cleanup(node);
+
+ spin_lock_irqsave(&node->pend_frames_lock, flags);
+ pend_frames_empty = list_empty(&node->pend_frames);
+ spin_unlock_irqrestore(&node->pend_frames_lock, flags);
+
+ /*
+ * there are two scenarios where we want to keep
+ * this node alive:
+ * 1. there are pending frames that need to be
+ * processed or
+ * 2. we're an initiator and the remote node is
+ * a target and we need to re-authenticate
+ */
+ node_printf(node, "Shutdown: explicit logo pend=%d ", !pend_frames_empty);
+ node_printf(node, "nport.ini=%d node.tgt=%d\n",
+ node->nport->enable_ini, node->targ);
+ if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) {
+ u8 send_plogi = false;
+
+ if (node->nport->enable_ini && node->targ) {
+ /*
+ * we're an initiator and
+ * node shutting down is a target;
+ * we'll need to re-authenticate in
+ * initial state
+ */
+ send_plogi = true;
+ }
+
+ /*
+ * transition to __efc_d_init
+ * (will retain HW node resources)
+ */
+ node->els_io_enabled = true;
+ node->req_free = false;
+
+ /*
+ * either pending frames exist or we are re-authenticating
+ * with PLOGI (or both); in either case, return to initial
+ * state
+ */
+ efc_node_init_device(node, send_plogi);
+ }
+ /* else: let node shutdown occur */
+}
+
+static void
+efc_node_purge_pending(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+ struct efc_hw_sequence *frame, *next;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&node->pend_frames_lock, flags);
+
+ list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) {
+ list_del(&frame->list_entry);
+ efc->tt.hw_seq_free(efc, frame);
+ }
+
+ spin_unlock_irqrestore(&node->pend_frames_lock, flags);
+}
+
+void
+__efc_node_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER: {
+ efc_node_hold_frames(node);
+ WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list));
+ /* by default, we will be freeing node after we unwind */
+ node->req_free = true;
+
+ switch (node->shutdown_reason) {
+ case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO:
+ /* Node shutdown b/c of PLOGI received when node
+ * already logged in. We have PLOGI service
+ * parameters, so submit node attach; we won't be
+ * freeing this node
+ */
+
+ efc_node_handle_implicit_logo(node);
+ break;
+
+ case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO:
+ efc_node_handle_explicit_logo(node);
+ break;
+
+ case EFC_NODE_SHUTDOWN_DEFAULT:
+ default: {
+ /*
+ * shutdown due to link down,
+ * node going away (xport event) or
+ * nport shutdown, purge pending and
+ * proceed to cleanup node
+ */
+
+ /* cleanup any pending LS_ACC ELSs */
+ efc_node_send_ls_io_cleanup(node);
+
+ node_printf(node,
+ "Shutdown reason: default, purge pending\n");
+ efc_node_purge_pending(node);
+ break;
+ }
+ }
+
+ break;
+ }
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ default:
+ __efc_node_common(__func__, ctx, evt, arg);
+ }
+}
+
+static bool
+efc_node_check_els_quiesced(struct efc_node *node)
+{
+ /* check to see if ELS requests, completions are quiesced */
+ if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 &&
+ efc_els_io_list_empty(node, &node->els_ios_list)) {
+ if (!node->attached) {
+ /* hw node detach already completed, proceed */
+ node_printf(node, "HW node not attached\n");
+ efc_node_transition(node,
+ __efc_node_wait_ios_shutdown,
+ NULL);
+ } else {
+ /*
+ * hw node detach hasn't completed,
+ * transition and wait
+ */
+ node_printf(node, "HW node still attached\n");
+ efc_node_transition(node, __efc_node_wait_node_free,
+ NULL);
+ }
+ return true;
+ }
+ return false;
+}
+
+void
+efc_node_initiate_cleanup(struct efc_node *node)
+{
+ /*
+ * if ELS's have already been quiesced, will move to next state
+ * if ELS's have not been quiesced, abort them
+ */
+ if (!efc_node_check_els_quiesced(node)) {
+ efc_node_hold_frames(node);
+ efc_node_transition(node, __efc_node_wait_els_shutdown, NULL);
+ }
+}
+
+void
+__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ bool check_quiesce = false;
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+ /* Node state machine: Wait for all ELSs to complete */
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ if (efc_els_io_list_empty(node, &node->els_ios_list)) {
+ node_printf(node, "All ELS IOs complete\n");
+ check_quiesce = true;
+ }
+ break;
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_OK:
+ case EFC_EVT_SRRS_ELS_REQ_FAIL:
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ case EFC_EVT_ELS_REQ_ABORTED:
+ if (WARN_ON(!node->els_req_cnt))
+ break;
+ node->els_req_cnt--;
+ check_quiesce = true;
+ break;
+
+ case EFC_EVT_SRRS_ELS_CMPL_OK:
+ case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+ if (WARN_ON(!node->els_cmpl_cnt))
+ break;
+ node->els_cmpl_cnt--;
+ check_quiesce = true;
+ break;
+
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ /* all ELS IO's complete */
+ node_printf(node, "All ELS IOs complete\n");
+ WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list));
+ check_quiesce = true;
+ break;
+
+ case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+ check_quiesce = true;
+ break;
+
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ /* don't care about domain_attach_ok */
+ break;
+
+ /* ignore shutdown events as we're already in shutdown path */
+ case EFC_EVT_SHUTDOWN:
+ /* have default shutdown event take precedence */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ fallthrough;
+
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ break;
+
+ default:
+ __efc_node_common(__func__, ctx, evt, arg);
+ }
+
+ if (check_quiesce)
+ efc_node_check_els_quiesced(node);
+}
+
+void
+__efc_node_wait_node_free(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_NODE_FREE_OK:
+ /* node is officially no longer attached */
+ node->attached = false;
+ efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL);
+ break;
+
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+ /* As IOs and ELS IO's complete we expect to get these events */
+ break;
+
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ /* don't care about domain_attach_ok */
+ break;
+
+ /* ignore shutdown events as we're already in shutdown path */
+ case EFC_EVT_SHUTDOWN:
+ /* have default shutdown event take precedence */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ fallthrough;
+
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ node_printf(node, "%s received\n", efc_sm_event_name(evt));
+ break;
+ default:
+ __efc_node_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+ struct efc *efc = node->efc;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ efc_node_hold_frames(node);
+
+ /* first check to see if no ELS IOs are outstanding */
+ if (efc_els_io_list_empty(node, &node->els_ios_list))
+ /* If there are any active IOS, Free them. */
+ efc_node_transition(node, __efc_node_shutdown, NULL);
+ break;
+
+ case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ if (efc_els_io_list_empty(node, &node->els_ios_list))
+ efc_node_transition(node, __efc_node_shutdown, NULL);
+ break;
+
+ case EFC_EVT_EXIT:
+ efc_node_accept_frames(node);
+ break;
+
+ case EFC_EVT_SRRS_ELS_REQ_FAIL:
+ /* Can happen as ELS IO IO's complete */
+ if (WARN_ON(!node->els_req_cnt))
+ break;
+ node->els_req_cnt--;
+ break;
+
+ /* ignore shutdown events as we're already in shutdown path */
+ case EFC_EVT_SHUTDOWN:
+ /* have default shutdown event take precedence */
+ node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
+ fallthrough;
+
+ case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
+ case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
+ efc_log_debug(efc, "[%s] %-20s\n", node->display_name,
+ efc_sm_event_name(evt));
+ break;
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ /* don't care about domain_attach_ok */
+ break;
+ default:
+ __efc_node_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = NULL;
+ struct efc *efc = NULL;
+ struct efc_node_cb *cbdata = arg;
+
+ node = ctx->app;
+ efc = node->efc;
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ case EFC_EVT_REENTER:
+ case EFC_EVT_EXIT:
+ case EFC_EVT_NPORT_TOPOLOGY_NOTIFY:
+ case EFC_EVT_NODE_MISSING:
+ case EFC_EVT_FCP_CMD_RCVD:
+ break;
+
+ case EFC_EVT_NODE_REFOUND:
+ node->refound = true;
+ break;
+
+ /*
+ * node->attached must be set appropriately
+ * for all node attach/detach events
+ */
+ case EFC_EVT_NODE_ATTACH_OK:
+ node->attached = true;
+ break;
+
+ case EFC_EVT_NODE_FREE_OK:
+ case EFC_EVT_NODE_ATTACH_FAIL:
+ node->attached = false;
+ break;
+
+ /*
+ * handle any ELS completions that
+ * other states either didn't care about
+ * or forgot about
+ */
+ case EFC_EVT_SRRS_ELS_CMPL_OK:
+ case EFC_EVT_SRRS_ELS_CMPL_FAIL:
+ if (WARN_ON(!node->els_cmpl_cnt))
+ break;
+ node->els_cmpl_cnt--;
+ break;
+
+ /*
+ * handle any ELS request completions that
+ * other states either didn't care about
+ * or forgot about
+ */
+ case EFC_EVT_SRRS_ELS_REQ_OK:
+ case EFC_EVT_SRRS_ELS_REQ_FAIL:
+ case EFC_EVT_SRRS_ELS_REQ_RJT:
+ case EFC_EVT_ELS_REQ_ABORTED:
+ if (WARN_ON(!node->els_req_cnt))
+ break;
+ node->els_req_cnt--;
+ break;
+
+ case EFC_EVT_ELS_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ /*
+ * Unsupported ELS was received,
+ * send LS_RJT, command not supported
+ */
+ efc_log_debug(efc,
+ "[%s] (%s) ELS x%02x, LS_RJT not supported\n",
+ node->display_name, funcname,
+ ((u8 *)cbdata->payload->dma.virt)[0]);
+
+ efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
+ ELS_RJT_UNSUP, ELS_EXPL_NONE, 0);
+ break;
+ }
+
+ case EFC_EVT_PLOGI_RCVD:
+ case EFC_EVT_FLOGI_RCVD:
+ case EFC_EVT_LOGO_RCVD:
+ case EFC_EVT_PRLI_RCVD:
+ case EFC_EVT_PRLO_RCVD:
+ case EFC_EVT_PDISC_RCVD:
+ case EFC_EVT_FDISC_RCVD:
+ case EFC_EVT_ADISC_RCVD:
+ case EFC_EVT_RSCN_RCVD:
+ case EFC_EVT_SCR_RCVD: {
+ struct fc_frame_header *hdr = cbdata->header->dma.virt;
+
+ /* sm: / send ELS_RJT */
+ efc_log_debug(efc, "[%s] (%s) %s sending ELS_RJT\n",
+ node->display_name, funcname,
+ efc_sm_event_name(evt));
+ /* if we didn't catch this in a state, send generic LS_RJT */
+ efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
+ ELS_RJT_UNAB, ELS_EXPL_NONE, 0);
+ break;
+ }
+ case EFC_EVT_ABTS_RCVD: {
+ efc_log_debug(efc, "[%s] (%s) %s sending BA_ACC\n",
+ node->display_name, funcname,
+ efc_sm_event_name(evt));
+
+ /* sm: / send BA_ACC */
+ efc_send_bls_acc(node, cbdata->header->dma.virt);
+ break;
+ }
+
+ default:
+ efc_log_debug(node->efc, "[%s] %-20s %-20s not handled\n",
+ node->display_name, funcname,
+ efc_sm_event_name(evt));
+ }
+}
+
+void
+efc_node_save_sparms(struct efc_node *node, void *payload)
+{
+ memcpy(node->service_params, payload, sizeof(node->service_params));
+}
+
+void
+efc_node_post_event(struct efc_node *node,
+ enum efc_sm_event evt, void *arg)
+{
+ bool free_node = false;
+
+ node->evtdepth++;
+
+ efc_sm_post_event(&node->sm, evt, arg);
+
+ /* If our event call depth is one and
+ * we're not holding frames
+ * then we can dispatch any pending frames.
+ * We don't want to allow the efc_process_node_pending()
+ * call to recurse.
+ */
+ if (!node->hold_frames && node->evtdepth == 1)
+ efc_process_node_pending(node);
+
+ node->evtdepth--;
+
+ /*
+ * Free the node object if so requested,
+ * and we're at an event call depth of zero
+ */
+ if (node->evtdepth == 0 && node->req_free)
+ free_node = true;
+
+ if (free_node)
+ efc_node_free(node);
+}
+
+void
+efc_node_transition(struct efc_node *node,
+ void (*state)(struct efc_sm_ctx *,
+ enum efc_sm_event, void *), void *data)
+{
+ struct efc_sm_ctx *ctx = &node->sm;
+
+ if (ctx->current_state == state) {
+ efc_node_post_event(node, EFC_EVT_REENTER, data);
+ } else {
+ efc_node_post_event(node, EFC_EVT_EXIT, data);
+ ctx->current_state = state;
+ efc_node_post_event(node, EFC_EVT_ENTER, data);
+ }
+}
+
+void
+efc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name)
+{
+ memset(buf, 0, buf_len);
+
+ snprintf(buf, buf_len, "eui.%016llX", (unsigned long long)eui_name);
+}
+
+u64
+efc_node_get_wwpn(struct efc_node *node)
+{
+ struct fc_els_flogi *sp =
+ (struct fc_els_flogi *)node->service_params;
+
+ return be64_to_cpu(sp->fl_wwpn);
+}
+
+u64
+efc_node_get_wwnn(struct efc_node *node)
+{
+ struct fc_els_flogi *sp =
+ (struct fc_els_flogi *)node->service_params;
+
+ return be64_to_cpu(sp->fl_wwnn);
+}
+
+int
+efc_node_check_els_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg,
+ u8 cmd, void (*efc_node_common_func)(const char *,
+ struct efc_sm_ctx *, enum efc_sm_event, void *),
+ const char *funcname)
+{
+ return 0;
+}
+
+int
+efc_node_check_ns_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg,
+ u16 cmd, void (*efc_node_common_func)(const char *,
+ struct efc_sm_ctx *, enum efc_sm_event, void *),
+ const char *funcname)
+{
+ return 0;
+}
+
+int
+efc_els_io_list_empty(struct efc_node *node, struct list_head *list)
+{
+ int empty;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&node->els_ios_lock, flags);
+ empty = list_empty(list);
+ spin_unlock_irqrestore(&node->els_ios_lock, flags);
+ return empty;
+}
+
+void
+efc_node_pause(struct efc_node *node,
+ void (*state)(struct efc_sm_ctx *,
+ enum efc_sm_event, void *))
+
+{
+ node->nodedb_state = state;
+ efc_node_transition(node, __efc_node_paused, NULL);
+}
+
+void
+__efc_node_paused(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_node *node = ctx->app;
+
+ efc_node_evt_set(ctx, evt, __func__);
+
+ node_sm_trace();
+
+ /*
+ * This state is entered when a state is "paused". When resumed, the
+ * node is transitioned to a previously saved state (node->ndoedb_state)
+ */
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ node_printf(node, "Paused\n");
+ break;
+
+ case EFC_EVT_RESUME: {
+ void (*pf)(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+ pf = node->nodedb_state;
+
+ node->nodedb_state = NULL;
+ efc_node_transition(node, pf, NULL);
+ break;
+ }
+
+ case EFC_EVT_DOMAIN_ATTACH_OK:
+ break;
+
+ case EFC_EVT_SHUTDOWN:
+ node->req_free = true;
+ break;
+
+ default:
+ __efc_node_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+efc_node_recv_els_frame(struct efc_node *node,
+ struct efc_hw_sequence *seq)
+{
+ u32 prli_size = sizeof(struct fc_els_prli) + sizeof(struct fc_els_spp);
+ struct {
+ u32 cmd;
+ enum efc_sm_event evt;
+ u32 payload_size;
+ } els_cmd_list[] = {
+ {ELS_PLOGI, EFC_EVT_PLOGI_RCVD, sizeof(struct fc_els_flogi)},
+ {ELS_FLOGI, EFC_EVT_FLOGI_RCVD, sizeof(struct fc_els_flogi)},
+ {ELS_LOGO, EFC_EVT_LOGO_RCVD, sizeof(struct fc_els_ls_acc)},
+ {ELS_PRLI, EFC_EVT_PRLI_RCVD, prli_size},
+ {ELS_PRLO, EFC_EVT_PRLO_RCVD, prli_size},
+ {ELS_PDISC, EFC_EVT_PDISC_RCVD, MAX_ACC_REJECT_PAYLOAD},
+ {ELS_FDISC, EFC_EVT_FDISC_RCVD, MAX_ACC_REJECT_PAYLOAD},
+ {ELS_ADISC, EFC_EVT_ADISC_RCVD, sizeof(struct fc_els_adisc)},
+ {ELS_RSCN, EFC_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD},
+ {ELS_SCR, EFC_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD},
+ };
+ struct efc_node_cb cbdata;
+ u8 *buf = seq->payload->dma.virt;
+ enum efc_sm_event evt = EFC_EVT_ELS_RCVD;
+ u32 i;
+
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.header = seq->header;
+ cbdata.payload = seq->payload;
+
+ /* find a matching event for the ELS command */
+ for (i = 0; i < ARRAY_SIZE(els_cmd_list); i++) {
+ if (els_cmd_list[i].cmd == buf[0]) {
+ evt = els_cmd_list[i].evt;
+ break;
+ }
+ }
+
+ efc_node_post_event(node, evt, &cbdata);
+}
+
+void
+efc_node_recv_ct_frame(struct efc_node *node,
+ struct efc_hw_sequence *seq)
+{
+ struct fc_ct_hdr *iu = seq->payload->dma.virt;
+ struct fc_frame_header *hdr = seq->header->dma.virt;
+ struct efc *efc = node->efc;
+ u16 gscmd = be16_to_cpu(iu->ct_cmd);
+
+ efc_log_err(efc, "[%s] Received cmd :%x sending CT_REJECT\n",
+ node->display_name, gscmd);
+ efc_send_ct_rsp(efc, node, be16_to_cpu(hdr->fh_ox_id), iu,
+ FC_FS_RJT, FC_FS_RJT_UNSUP, 0);
+}
+
+void
+efc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq)
+{
+ struct efc_node_cb cbdata;
+
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.header = seq->header;
+ cbdata.payload = seq->payload;
+
+ efc_node_post_event(node, EFC_EVT_FCP_CMD_RCVD, &cbdata);
+}
+
+void
+efc_process_node_pending(struct efc_node *node)
+{
+ struct efc *efc = node->efc;
+ struct efc_hw_sequence *seq = NULL;
+ u32 pend_frames_processed = 0;
+ unsigned long flags = 0;
+
+ for (;;) {
+ /* need to check for hold frames condition after each frame
+ * processed because any given frame could cause a transition
+ * to a state that holds frames
+ */
+ if (node->hold_frames)
+ break;
+
+ seq = NULL;
+ /* Get next frame/sequence */
+ spin_lock_irqsave(&node->pend_frames_lock, flags);
+
+ if (!list_empty(&node->pend_frames)) {
+ seq = list_first_entry(&node->pend_frames,
+ struct efc_hw_sequence, list_entry);
+ list_del(&seq->list_entry);
+ }
+ spin_unlock_irqrestore(&node->pend_frames_lock, flags);
+
+ if (!seq) {
+ pend_frames_processed = node->pend_frames_processed;
+ node->pend_frames_processed = 0;
+ break;
+ }
+ node->pend_frames_processed++;
+
+ /* now dispatch frame(s) to dispatch function */
+ efc_node_dispatch_frame(node, seq);
+ efc->tt.hw_seq_free(efc, seq);
+ }
+
+ if (pend_frames_processed != 0)
+ efc_log_debug(efc, "%u node frames held and processed\n",
+ pend_frames_processed);
+}
+
+void
+efc_scsi_sess_reg_complete(struct efc_node *node, u32 status)
+{
+ unsigned long flags = 0;
+ enum efc_sm_event evt = EFC_EVT_NODE_SESS_REG_OK;
+ struct efc *efc = node->efc;
+
+ if (status)
+ evt = EFC_EVT_NODE_SESS_REG_FAIL;
+
+ spin_lock_irqsave(&efc->lock, flags);
+ /* Notify the node to resume */
+ efc_node_post_event(node, evt, NULL);
+ spin_unlock_irqrestore(&efc->lock, flags);
+}
+
+void
+efc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node)
+{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->lock, flags);
+ /* Notify the node to resume */
+ efc_node_post_event(node, EFC_EVT_NODE_DEL_INI_COMPLETE, NULL);
+ spin_unlock_irqrestore(&efc->lock, flags);
+}
+
+void
+efc_scsi_del_target_complete(struct efc *efc, struct efc_node *node)
+{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->lock, flags);
+ /* Notify the node to resume */
+ efc_node_post_event(node, EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL);
+ spin_unlock_irqrestore(&efc->lock, flags);
+}
+
+void
+efc_scsi_io_list_empty(struct efc *efc, struct efc_node *node)
+{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->lock, flags);
+ efc_node_post_event(node, EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL);
+ spin_unlock_irqrestore(&efc->lock, flags);
+}
+
+void efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg)
+{
+ struct efc *efc = node->efc;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->lock, flags);
+ efc_node_post_event(node, evt, arg);
+ spin_unlock_irqrestore(&efc->lock, flags);
+}
+
+void efc_node_post_shutdown(struct efc_node *node, void *arg)
+{
+ unsigned long flags = 0;
+ struct efc *efc = node->efc;
+
+ spin_lock_irqsave(&efc->lock, flags);
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, arg);
+ spin_unlock_irqrestore(&efc->lock, flags);
+}
diff --git a/drivers/scsi/elx/libefc/efc_node.h b/drivers/scsi/elx/libefc/efc_node.h
new file mode 100644
index 000000000000..e57579988ba4
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_node.h
@@ -0,0 +1,191 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#if !defined(__EFC_NODE_H__)
+#define __EFC_NODE_H__
+#include "scsi/fc/fc_ns.h"
+
+#define EFC_NODEDB_PAUSE_FABRIC_LOGIN (1 << 0)
+#define EFC_NODEDB_PAUSE_NAMESERVER (1 << 1)
+#define EFC_NODEDB_PAUSE_NEW_NODES (1 << 2)
+
+#define MAX_ACC_REJECT_PAYLOAD sizeof(struct fc_els_ls_rjt)
+
+#define scsi_io_printf(io, fmt, ...) \
+ efc_log_debug(io->efc, "[%s] [%04x][i:%04x t:%04x h:%04x]" fmt, \
+ io->node->display_name, io->instance_index, io->init_task_tag, \
+ io->tgt_task_tag, io->hw_tag, ##__VA_ARGS__)
+
+static inline void
+efc_node_evt_set(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
+ const char *handler)
+{
+ struct efc_node *node = ctx->app;
+
+ if (evt == EFC_EVT_ENTER) {
+ strscpy_pad(node->current_state_name, handler,
+ sizeof(node->current_state_name));
+ } else if (evt == EFC_EVT_EXIT) {
+ memcpy(node->prev_state_name, node->current_state_name,
+ sizeof(node->prev_state_name));
+ strscpy_pad(node->current_state_name, "invalid",
+ sizeof(node->current_state_name));
+ }
+ node->prev_evt = node->current_evt;
+ node->current_evt = evt;
+}
+
+/**
+ * hold frames in pending frame list
+ *
+ * Unsolicited receive frames are held on the node pending frame list,
+ * rather than being processed.
+ */
+
+static inline void
+efc_node_hold_frames(struct efc_node *node)
+{
+ node->hold_frames = true;
+}
+
+/**
+ * accept frames
+ *
+ * Unsolicited receive frames processed rather than being held on the node
+ * pending frame list.
+ */
+
+static inline void
+efc_node_accept_frames(struct efc_node *node)
+{
+ node->hold_frames = false;
+}
+
+/*
+ * Node initiator/target enable defines
+ * All combinations of the SLI port (nport) initiator/target enable,
+ * and remote node initiator/target enable are enumerated.
+ * ex: EFC_NODE_ENABLE_T_TO_IT decodes to target mode is enabled on SLI port
+ * and I+T is enabled on remote node.
+ */
+enum efc_node_enable {
+ EFC_NODE_ENABLE_x_TO_x,
+ EFC_NODE_ENABLE_x_TO_T,
+ EFC_NODE_ENABLE_x_TO_I,
+ EFC_NODE_ENABLE_x_TO_IT,
+ EFC_NODE_ENABLE_T_TO_x,
+ EFC_NODE_ENABLE_T_TO_T,
+ EFC_NODE_ENABLE_T_TO_I,
+ EFC_NODE_ENABLE_T_TO_IT,
+ EFC_NODE_ENABLE_I_TO_x,
+ EFC_NODE_ENABLE_I_TO_T,
+ EFC_NODE_ENABLE_I_TO_I,
+ EFC_NODE_ENABLE_I_TO_IT,
+ EFC_NODE_ENABLE_IT_TO_x,
+ EFC_NODE_ENABLE_IT_TO_T,
+ EFC_NODE_ENABLE_IT_TO_I,
+ EFC_NODE_ENABLE_IT_TO_IT,
+};
+
+static inline enum efc_node_enable
+efc_node_get_enable(struct efc_node *node)
+{
+ u32 retval = 0;
+
+ if (node->nport->enable_ini)
+ retval |= (1U << 3);
+ if (node->nport->enable_tgt)
+ retval |= (1U << 2);
+ if (node->init)
+ retval |= (1U << 1);
+ if (node->targ)
+ retval |= (1U << 0);
+ return (enum efc_node_enable)retval;
+}
+
+int
+efc_node_check_els_req(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg,
+ u8 cmd, void (*efc_node_common_func)(const char *,
+ struct efc_sm_ctx *, enum efc_sm_event, void *),
+ const char *funcname);
+int
+efc_node_check_ns_req(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg,
+ u16 cmd, void (*efc_node_common_func)(const char *,
+ struct efc_sm_ctx *, enum efc_sm_event, void *),
+ const char *funcname);
+int
+efc_node_attach(struct efc_node *node);
+struct efc_node *
+efc_node_alloc(struct efc_nport *nport, u32 port_id,
+ bool init, bool targ);
+void
+efc_node_free(struct efc_node *efc);
+void
+efc_node_update_display_name(struct efc_node *node);
+void efc_node_post_event(struct efc_node *node, enum efc_sm_event evt,
+ void *arg);
+
+void
+__efc_node_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_node_wait_node_free(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+efc_node_save_sparms(struct efc_node *node, void *payload);
+void
+efc_node_transition(struct efc_node *node,
+ void (*state)(struct efc_sm_ctx *, enum efc_sm_event,
+ void *), void *data);
+void
+__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+void
+efc_node_initiate_cleanup(struct efc_node *node);
+
+void
+efc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name);
+
+void
+efc_node_pause(struct efc_node *node,
+ void (*state)(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg));
+void
+__efc_node_paused(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+int
+efc_node_active_ios_empty(struct efc_node *node);
+void
+efc_node_send_ls_io_cleanup(struct efc_node *node);
+
+int
+efc_els_io_list_empty(struct efc_node *node, struct list_head *list);
+
+void
+efc_process_node_pending(struct efc_node *domain);
+
+u64 efc_node_get_wwnn(struct efc_node *node);
+struct efc_node *
+efc_node_find(struct efc_nport *nport, u32 id);
+void
+efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg);
+void
+efc_node_recv_els_frame(struct efc_node *node, struct efc_hw_sequence *s);
+void
+efc_node_recv_ct_frame(struct efc_node *node, struct efc_hw_sequence *seq);
+void
+efc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq);
+
+#endif /* __EFC_NODE_H__ */
diff --git a/drivers/scsi/elx/libefc/efc_nport.c b/drivers/scsi/elx/libefc/efc_nport.c
new file mode 100644
index 000000000000..1a7437f4328e
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_nport.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * NPORT
+ *
+ * Port object for physical port and NPIV ports.
+ */
+
+/*
+ * NPORT REFERENCE COUNTING
+ *
+ * A nport reference should be taken when:
+ * - an nport is allocated
+ * - a vport populates associated nport
+ * - a remote node is allocated
+ * - a unsolicited frame is processed
+ * The reference should be dropped when:
+ * - the unsolicited frame processesing is done
+ * - the remote node is removed
+ * - the vport is removed
+ * - the nport is removed
+ */
+
+#include "efc.h"
+
+void
+efc_nport_cb(void *arg, int event, void *data)
+{
+ struct efc *efc = arg;
+ struct efc_nport *nport = data;
+ unsigned long flags = 0;
+
+ efc_log_debug(efc, "nport event: %s\n", efc_sm_event_name(event));
+
+ spin_lock_irqsave(&efc->lock, flags);
+ efc_sm_post_event(&nport->sm, event, NULL);
+ spin_unlock_irqrestore(&efc->lock, flags);
+}
+
+static struct efc_nport *
+efc_nport_find_wwn(struct efc_domain *domain, uint64_t wwnn, uint64_t wwpn)
+{
+ struct efc_nport *nport = NULL;
+
+ /* Find a nport, given the WWNN and WWPN */
+ list_for_each_entry(nport, &domain->nport_list, list_entry) {
+ if (nport->wwnn == wwnn && nport->wwpn == wwpn)
+ return nport;
+ }
+ return NULL;
+}
+
+static void
+_efc_nport_free(struct kref *arg)
+{
+ struct efc_nport *nport = container_of(arg, struct efc_nport, ref);
+
+ kfree(nport);
+}
+
+struct efc_nport *
+efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
+ u32 fc_id, bool enable_ini, bool enable_tgt)
+{
+ struct efc_nport *nport;
+
+ if (domain->efc->enable_ini)
+ enable_ini = 0;
+
+ /* Return a failure if this nport has already been allocated */
+ if ((wwpn != 0) || (wwnn != 0)) {
+ nport = efc_nport_find_wwn(domain, wwnn, wwpn);
+ if (nport) {
+ efc_log_err(domain->efc,
+ "NPORT %016llX %016llX already allocated\n",
+ wwnn, wwpn);
+ return NULL;
+ }
+ }
+
+ nport = kzalloc(sizeof(*nport), GFP_ATOMIC);
+ if (!nport)
+ return nport;
+
+ /* initialize refcount */
+ kref_init(&nport->ref);
+ nport->release = _efc_nport_free;
+
+ nport->efc = domain->efc;
+ snprintf(nport->display_name, sizeof(nport->display_name), "------");
+ nport->domain = domain;
+ xa_init(&nport->lookup);
+ nport->instance_index = domain->nport_count++;
+ nport->sm.app = nport;
+ nport->enable_ini = enable_ini;
+ nport->enable_tgt = enable_tgt;
+ nport->enable_rscn = (nport->enable_ini ||
+ (nport->enable_tgt && enable_target_rscn(nport->efc)));
+
+ /* Copy service parameters from domain */
+ memcpy(nport->service_params, domain->service_params,
+ sizeof(struct fc_els_flogi));
+
+ /* Update requested fc_id */
+ nport->fc_id = fc_id;
+
+ /* Update the nport's service parameters for the new wwn's */
+ nport->wwpn = wwpn;
+ nport->wwnn = wwnn;
+ snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), "%016llX",
+ (unsigned long long)wwnn);
+
+ /*
+ * if this is the "first" nport of the domain,
+ * then make it the "phys" nport
+ */
+ if (list_empty(&domain->nport_list))
+ domain->nport = nport;
+
+ INIT_LIST_HEAD(&nport->list_entry);
+ list_add_tail(&nport->list_entry, &domain->nport_list);
+
+ kref_get(&domain->ref);
+
+ efc_log_debug(domain->efc, "New Nport [%s]\n", nport->display_name);
+
+ return nport;
+}
+
+void
+efc_nport_free(struct efc_nport *nport)
+{
+ struct efc_domain *domain;
+
+ if (!nport)
+ return;
+
+ domain = nport->domain;
+ efc_log_debug(domain->efc, "[%s] free nport\n", nport->display_name);
+ list_del(&nport->list_entry);
+ /*
+ * if this is the physical nport,
+ * then clear it out of the domain
+ */
+ if (nport == domain->nport)
+ domain->nport = NULL;
+
+ xa_destroy(&nport->lookup);
+ xa_erase(&domain->lookup, nport->fc_id);
+
+ if (list_empty(&domain->nport_list))
+ efc_domain_post_event(domain, EFC_EVT_ALL_CHILD_NODES_FREE,
+ NULL);
+
+ kref_put(&domain->ref, domain->release);
+ kref_put(&nport->ref, nport->release);
+}
+
+struct efc_nport *
+efc_nport_find(struct efc_domain *domain, u32 d_id)
+{
+ struct efc_nport *nport;
+
+ /* Find a nport object, given an FC_ID */
+ nport = xa_load(&domain->lookup, d_id);
+ if (!nport || !kref_get_unless_zero(&nport->ref))
+ return NULL;
+
+ return nport;
+}
+
+int
+efc_nport_attach(struct efc_nport *nport, u32 fc_id)
+{
+ int rc;
+ struct efc_node *node;
+ struct efc *efc = nport->efc;
+ unsigned long index;
+
+ /* Set our lookup */
+ rc = xa_err(xa_store(&nport->domain->lookup, fc_id, nport, GFP_ATOMIC));
+ if (rc) {
+ efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
+ return rc;
+ }
+
+ /* Update our display_name */
+ efc_node_fcid_display(fc_id, nport->display_name,
+ sizeof(nport->display_name));
+
+ xa_for_each(&nport->lookup, index, node) {
+ efc_node_update_display_name(node);
+ }
+
+ efc_log_debug(nport->efc, "[%s] attach nport: fc_id x%06x\n",
+ nport->display_name, fc_id);
+
+ /* Register a nport, given an FC_ID */
+ rc = efc_cmd_nport_attach(efc, nport, fc_id);
+ if (rc < 0) {
+ efc_log_err(nport->efc,
+ "efc_hw_port_attach failed: %d\n", rc);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void
+efc_nport_shutdown(struct efc_nport *nport)
+{
+ struct efc *efc = nport->efc;
+ struct efc_node *node;
+ unsigned long index;
+
+ xa_for_each(&nport->lookup, index, node) {
+ if (!(node->rnode.fc_id == FC_FID_FLOGI && nport->is_vport)) {
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+ continue;
+ }
+
+ /*
+ * If this is a vport, logout of the fabric
+ * controller so that it deletes the vport
+ * on the switch.
+ */
+ /* if link is down, don't send logo */
+ if (efc->link_status == EFC_LINK_STATUS_DOWN) {
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
+ continue;
+ }
+
+ efc_log_debug(efc, "[%s] nport shutdown vport, send logo\n",
+ node->display_name);
+
+ if (!efc_send_logo(node)) {
+ /* sent LOGO, wait for response */
+ efc_node_transition(node, __efc_d_wait_logo_rsp, NULL);
+ continue;
+ }
+
+ /*
+ * failed to send LOGO,
+ * go ahead and cleanup node anyways
+ */
+ node_printf(node, "Failed to send LOGO\n");
+ efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
+ }
+}
+
+static void
+efc_vport_link_down(struct efc_nport *nport)
+{
+ struct efc *efc = nport->efc;
+ struct efc_vport *vport;
+
+ /* Clear the nport reference in the vport specification */
+ list_for_each_entry(vport, &efc->vport_list, list_entry) {
+ if (vport->nport == nport) {
+ kref_put(&nport->ref, nport->release);
+ vport->nport = NULL;
+ break;
+ }
+ }
+}
+
+static void
+__efc_nport_common(const char *funcname, struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+ struct efc_domain *domain = nport->domain;
+ struct efc *efc = nport->efc;
+
+ switch (evt) {
+ case EFC_EVT_ENTER:
+ case EFC_EVT_REENTER:
+ case EFC_EVT_EXIT:
+ case EFC_EVT_ALL_CHILD_NODES_FREE:
+ break;
+ case EFC_EVT_NPORT_ATTACH_OK:
+ efc_sm_transition(ctx, __efc_nport_attached, NULL);
+ break;
+ case EFC_EVT_SHUTDOWN:
+ /* Flag this nport as shutting down */
+ nport->shutting_down = true;
+
+ if (nport->is_vport)
+ efc_vport_link_down(nport);
+
+ if (xa_empty(&nport->lookup)) {
+ /* Remove the nport from the domain's lookup table */
+ xa_erase(&domain->lookup, nport->fc_id);
+ efc_sm_transition(ctx, __efc_nport_wait_port_free,
+ NULL);
+ if (efc_cmd_nport_free(efc, nport)) {
+ efc_log_debug(nport->efc,
+ "efc_hw_port_free failed\n");
+ /* Not much we can do, free the nport anyways */
+ efc_nport_free(nport);
+ }
+ } else {
+ /* sm: node list is not empty / shutdown nodes */
+ efc_sm_transition(ctx,
+ __efc_nport_wait_shutdown, NULL);
+ efc_nport_shutdown(nport);
+ }
+ break;
+ default:
+ efc_log_debug(nport->efc, "[%s] %-20s %-20s not handled\n",
+ nport->display_name, funcname,
+ efc_sm_event_name(evt));
+ }
+}
+
+void
+__efc_nport_allocated(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+ struct efc_domain *domain = nport->domain;
+
+ nport_sm_trace(nport);
+
+ switch (evt) {
+ /* the physical nport is attached */
+ case EFC_EVT_NPORT_ATTACH_OK:
+ WARN_ON(nport != domain->nport);
+ efc_sm_transition(ctx, __efc_nport_attached, NULL);
+ break;
+
+ case EFC_EVT_NPORT_ALLOC_OK:
+ /* ignore */
+ break;
+ default:
+ __efc_nport_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_nport_vport_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+ struct efc *efc = nport->efc;
+
+ nport_sm_trace(nport);
+
+ switch (evt) {
+ case EFC_EVT_ENTER: {
+ __be64 be_wwpn = cpu_to_be64(nport->wwpn);
+
+ if (nport->wwpn == 0)
+ efc_log_debug(efc, "vport: letting f/w select WWN\n");
+
+ if (nport->fc_id != U32_MAX) {
+ efc_log_debug(efc, "vport: hard coding port id: %x\n",
+ nport->fc_id);
+ }
+
+ efc_sm_transition(ctx, __efc_nport_vport_wait_alloc, NULL);
+ /* If wwpn is zero, then we'll let the f/w assign wwpn*/
+ if (efc_cmd_nport_alloc(efc, nport, nport->domain,
+ nport->wwpn == 0 ? NULL :
+ (uint8_t *)&be_wwpn)) {
+ efc_log_err(efc, "Can't allocate port\n");
+ break;
+ }
+
+ break;
+ }
+ default:
+ __efc_nport_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+ struct efc *efc = nport->efc;
+
+ nport_sm_trace(nport);
+
+ switch (evt) {
+ case EFC_EVT_NPORT_ALLOC_OK: {
+ struct fc_els_flogi *sp;
+
+ sp = (struct fc_els_flogi *)nport->service_params;
+
+ if (nport->wwnn == 0) {
+ nport->wwnn = be64_to_cpu(nport->sli_wwnn);
+ nport->wwpn = be64_to_cpu(nport->sli_wwpn);
+ snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
+ "%016llX", nport->wwpn);
+ }
+
+ /* Update the nport's service parameters */
+ sp->fl_wwpn = cpu_to_be64(nport->wwpn);
+ sp->fl_wwnn = cpu_to_be64(nport->wwnn);
+
+ /*
+ * if nport->fc_id is uninitialized,
+ * then request that the fabric node use FDISC
+ * to find an fc_id.
+ * Otherwise we're restoring vports, or we're in
+ * fabric emulation mode, so attach the fc_id
+ */
+ if (nport->fc_id == U32_MAX) {
+ struct efc_node *fabric;
+
+ fabric = efc_node_alloc(nport, FC_FID_FLOGI, false,
+ false);
+ if (!fabric) {
+ efc_log_err(efc, "efc_node_alloc() failed\n");
+ return;
+ }
+ efc_node_transition(fabric, __efc_vport_fabric_init,
+ NULL);
+ } else {
+ snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
+ "%016llX", nport->wwpn);
+ efc_nport_attach(nport, nport->fc_id);
+ }
+ efc_sm_transition(ctx, __efc_nport_vport_allocated, NULL);
+ break;
+ }
+ default:
+ __efc_nport_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_nport_vport_allocated(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+ struct efc *efc = nport->efc;
+
+ nport_sm_trace(nport);
+
+ /*
+ * This state is entered after the nport is allocated;
+ * it then waits for a fabric node
+ * FDISC to complete, which requests a nport attach.
+ * The nport attach complete is handled in this state.
+ */
+ switch (evt) {
+ case EFC_EVT_NPORT_ATTACH_OK: {
+ struct efc_node *node;
+
+ /* Find our fabric node, and forward this event */
+ node = efc_node_find(nport, FC_FID_FLOGI);
+ if (!node) {
+ efc_log_debug(efc, "can't find node %06x\n", FC_FID_FLOGI);
+ break;
+ }
+ /* sm: / forward nport attach to fabric node */
+ efc_node_post_event(node, evt, NULL);
+ efc_sm_transition(ctx, __efc_nport_attached, NULL);
+ break;
+ }
+ default:
+ __efc_nport_common(__func__, ctx, evt, arg);
+ }
+}
+
+static void
+efc_vport_update_spec(struct efc_nport *nport)
+{
+ struct efc *efc = nport->efc;
+ struct efc_vport *vport;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->vport_lock, flags);
+ list_for_each_entry(vport, &efc->vport_list, list_entry) {
+ if (vport->nport == nport) {
+ vport->wwnn = nport->wwnn;
+ vport->wwpn = nport->wwpn;
+ vport->tgt_data = nport->tgt_data;
+ vport->ini_data = nport->ini_data;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&efc->vport_lock, flags);
+}
+
+void
+__efc_nport_attached(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+ struct efc *efc = nport->efc;
+
+ nport_sm_trace(nport);
+
+ switch (evt) {
+ case EFC_EVT_ENTER: {
+ struct efc_node *node;
+ unsigned long index;
+
+ efc_log_debug(efc,
+ "[%s] NPORT attached WWPN %016llX WWNN %016llX\n",
+ nport->display_name,
+ nport->wwpn, nport->wwnn);
+
+ xa_for_each(&nport->lookup, index, node)
+ efc_node_update_display_name(node);
+
+ efc->tt.new_nport(efc, nport);
+
+ /*
+ * Update the vport (if its not the physical nport)
+ * parameters
+ */
+ if (nport->is_vport)
+ efc_vport_update_spec(nport);
+ break;
+ }
+
+ case EFC_EVT_EXIT:
+ efc_log_debug(efc,
+ "[%s] NPORT deattached WWPN %016llX WWNN %016llX\n",
+ nport->display_name,
+ nport->wwpn, nport->wwnn);
+
+ efc->tt.del_nport(efc, nport);
+ break;
+ default:
+ __efc_nport_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+ struct efc_domain *domain = nport->domain;
+ struct efc *efc = nport->efc;
+
+ nport_sm_trace(nport);
+
+ switch (evt) {
+ case EFC_EVT_NPORT_ALLOC_OK:
+ case EFC_EVT_NPORT_ALLOC_FAIL:
+ case EFC_EVT_NPORT_ATTACH_OK:
+ case EFC_EVT_NPORT_ATTACH_FAIL:
+ /* ignore these events - just wait for the all free event */
+ break;
+
+ case EFC_EVT_ALL_CHILD_NODES_FREE: {
+ /*
+ * Remove the nport from the domain's
+ * sparse vector lookup table
+ */
+ xa_erase(&domain->lookup, nport->fc_id);
+ efc_sm_transition(ctx, __efc_nport_wait_port_free, NULL);
+ if (efc_cmd_nport_free(efc, nport)) {
+ efc_log_err(nport->efc, "efc_hw_port_free failed\n");
+ /* Not much we can do, free the nport anyways */
+ efc_nport_free(nport);
+ }
+ break;
+ }
+ default:
+ __efc_nport_common(__func__, ctx, evt, arg);
+ }
+}
+
+void
+__efc_nport_wait_port_free(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg)
+{
+ struct efc_nport *nport = ctx->app;
+
+ nport_sm_trace(nport);
+
+ switch (evt) {
+ case EFC_EVT_NPORT_ATTACH_OK:
+ /* Ignore as we are waiting for the free CB */
+ break;
+ case EFC_EVT_NPORT_FREE_OK: {
+ /* All done, free myself */
+ efc_nport_free(nport);
+ break;
+ }
+ default:
+ __efc_nport_common(__func__, ctx, evt, arg);
+ }
+}
+
+static int
+efc_vport_nport_alloc(struct efc_domain *domain, struct efc_vport *vport)
+{
+ struct efc_nport *nport;
+
+ lockdep_assert_held(&domain->efc->lock);
+
+ nport = efc_nport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id,
+ vport->enable_ini, vport->enable_tgt);
+ vport->nport = nport;
+ if (!nport)
+ return -EIO;
+
+ kref_get(&nport->ref);
+ nport->is_vport = true;
+ nport->tgt_data = vport->tgt_data;
+ nport->ini_data = vport->ini_data;
+
+ efc_sm_transition(&nport->sm, __efc_nport_vport_init, NULL);
+
+ return 0;
+}
+
+int
+efc_vport_start(struct efc_domain *domain)
+{
+ struct efc *efc = domain->efc;
+ struct efc_vport *vport;
+ struct efc_vport *next;
+ int rc = 0;
+ unsigned long flags = 0;
+
+ /* Use the vport spec to find the associated vports and start them */
+ spin_lock_irqsave(&efc->vport_lock, flags);
+ list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
+ if (!vport->nport) {
+ if (efc_vport_nport_alloc(domain, vport))
+ rc = -EIO;
+ }
+ }
+ spin_unlock_irqrestore(&efc->vport_lock, flags);
+
+ return rc;
+}
+
+int
+efc_nport_vport_new(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
+ u32 fc_id, bool ini, bool tgt, void *tgt_data,
+ void *ini_data)
+{
+ struct efc *efc = domain->efc;
+ struct efc_vport *vport;
+ int rc = 0;
+ unsigned long flags = 0;
+
+ if (ini && domain->efc->enable_ini == 0) {
+ efc_log_debug(efc, "driver initiator mode not enabled\n");
+ return -EIO;
+ }
+
+ if (tgt && domain->efc->enable_tgt == 0) {
+ efc_log_debug(efc, "driver target mode not enabled\n");
+ return -EIO;
+ }
+
+ /*
+ * Create a vport spec if we need to recreate
+ * this vport after a link up event
+ */
+ vport = efc_vport_create_spec(domain->efc, wwnn, wwpn, fc_id, ini, tgt,
+ tgt_data, ini_data);
+ if (!vport) {
+ efc_log_err(efc, "failed to create vport object entry\n");
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&efc->lock, flags);
+ rc = efc_vport_nport_alloc(domain, vport);
+ spin_unlock_irqrestore(&efc->lock, flags);
+
+ return rc;
+}
+
+int
+efc_nport_vport_del(struct efc *efc, struct efc_domain *domain,
+ u64 wwpn, uint64_t wwnn)
+{
+ struct efc_nport *nport;
+ struct efc_vport *vport;
+ struct efc_vport *next;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->vport_lock, flags);
+ /* walk the efc_vport_list and remove from there */
+ list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
+ if (vport->wwpn == wwpn && vport->wwnn == wwnn) {
+ list_del(&vport->list_entry);
+ kfree(vport);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&efc->vport_lock, flags);
+
+ if (!domain) {
+ /* No domain means no nport to look for */
+ return 0;
+ }
+
+ spin_lock_irqsave(&efc->lock, flags);
+ list_for_each_entry(nport, &domain->nport_list, list_entry) {
+ if (nport->wwpn == wwpn && nport->wwnn == wwnn) {
+ /* Shutdown this NPORT */
+ efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL);
+ kref_put(&nport->ref, nport->release);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&efc->lock, flags);
+ return 0;
+}
+
+void
+efc_vport_del_all(struct efc *efc)
+{
+ struct efc_vport *vport;
+ struct efc_vport *next;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->vport_lock, flags);
+ list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
+ list_del(&vport->list_entry);
+ kfree(vport);
+ }
+ spin_unlock_irqrestore(&efc->vport_lock, flags);
+}
+
+struct efc_vport *
+efc_vport_create_spec(struct efc *efc, uint64_t wwnn, uint64_t wwpn,
+ u32 fc_id, bool enable_ini,
+ bool enable_tgt, void *tgt_data, void *ini_data)
+{
+ struct efc_vport *vport;
+ unsigned long flags = 0;
+
+ /*
+ * walk the efc_vport_list and return failure
+ * if a valid(vport with non zero WWPN and WWNN) vport entry
+ * is already created
+ */
+ spin_lock_irqsave(&efc->vport_lock, flags);
+ list_for_each_entry(vport, &efc->vport_list, list_entry) {
+ if ((wwpn && vport->wwpn == wwpn) &&
+ (wwnn && vport->wwnn == wwnn)) {
+ efc_log_err(efc,
+ "VPORT %016llX %016llX already allocated\n",
+ wwnn, wwpn);
+ spin_unlock_irqrestore(&efc->vport_lock, flags);
+ return NULL;
+ }
+ }
+
+ vport = kzalloc(sizeof(*vport), GFP_ATOMIC);
+ if (!vport) {
+ spin_unlock_irqrestore(&efc->vport_lock, flags);
+ return NULL;
+ }
+
+ vport->wwnn = wwnn;
+ vport->wwpn = wwpn;
+ vport->fc_id = fc_id;
+ vport->enable_tgt = enable_tgt;
+ vport->enable_ini = enable_ini;
+ vport->tgt_data = tgt_data;
+ vport->ini_data = ini_data;
+
+ INIT_LIST_HEAD(&vport->list_entry);
+ list_add_tail(&vport->list_entry, &efc->vport_list);
+ spin_unlock_irqrestore(&efc->vport_lock, flags);
+ return vport;
+}
diff --git a/drivers/scsi/elx/libefc/efc_nport.h b/drivers/scsi/elx/libefc/efc_nport.h
new file mode 100644
index 000000000000..b575ea205bbf
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_nport.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/**
+ * EFC FC port (NPORT) exported declarations
+ *
+ */
+
+#ifndef __EFC_NPORT_H__
+#define __EFC_NPORT_H__
+
+struct efc_nport *
+efc_nport_find(struct efc_domain *domain, u32 d_id);
+struct efc_nport *
+efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
+ u32 fc_id, bool enable_ini, bool enable_tgt);
+void
+efc_nport_free(struct efc_nport *nport);
+int
+efc_nport_attach(struct efc_nport *nport, u32 fc_id);
+
+void
+__efc_nport_allocated(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_nport_wait_port_free(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_nport_vport_init(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_nport_vport_allocated(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+void
+__efc_nport_attached(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+int
+efc_vport_start(struct efc_domain *domain);
+
+#endif /* __EFC_NPORT_H__ */
diff --git a/drivers/scsi/elx/libefc/efc_sm.c b/drivers/scsi/elx/libefc/efc_sm.c
new file mode 100644
index 000000000000..afd963782c1c
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_sm.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * Generic state machine framework.
+ */
+#include "efc.h"
+#include "efc_sm.h"
+
+/**
+ * efc_sm_post_event() - Post an event to a context.
+ *
+ * @ctx: State machine context
+ * @evt: Event to post
+ * @data: Event-specific data (if any)
+ */
+int
+efc_sm_post_event(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *data)
+{
+ if (!ctx->current_state)
+ return -EIO;
+
+ ctx->current_state(ctx, evt, data);
+ return 0;
+}
+
+void
+efc_sm_transition(struct efc_sm_ctx *ctx,
+ void (*state)(struct efc_sm_ctx *,
+ enum efc_sm_event, void *), void *data)
+
+{
+ if (ctx->current_state == state) {
+ efc_sm_post_event(ctx, EFC_EVT_REENTER, data);
+ } else {
+ efc_sm_post_event(ctx, EFC_EVT_EXIT, data);
+ ctx->current_state = state;
+ efc_sm_post_event(ctx, EFC_EVT_ENTER, data);
+ }
+}
+
+static char *event_name[] = EFC_SM_EVENT_NAME;
+
+const char *efc_sm_event_name(enum efc_sm_event evt)
+{
+ if (evt > EFC_EVT_LAST)
+ return "unknown";
+
+ return event_name[evt];
+}
diff --git a/drivers/scsi/elx/libefc/efc_sm.h b/drivers/scsi/elx/libefc/efc_sm.h
new file mode 100644
index 000000000000..e26867b4db24
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efc_sm.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ */
+
+/**
+ * Generic state machine framework declarations.
+ */
+
+#ifndef _EFC_SM_H
+#define _EFC_SM_H
+
+struct efc_sm_ctx;
+
+/* State Machine events */
+enum efc_sm_event {
+ /* Common Events */
+ EFC_EVT_ENTER,
+ EFC_EVT_REENTER,
+ EFC_EVT_EXIT,
+ EFC_EVT_SHUTDOWN,
+ EFC_EVT_ALL_CHILD_NODES_FREE,
+ EFC_EVT_RESUME,
+ EFC_EVT_TIMER_EXPIRED,
+
+ /* Domain Events */
+ EFC_EVT_RESPONSE,
+ EFC_EVT_ERROR,
+
+ EFC_EVT_DOMAIN_FOUND,
+ EFC_EVT_DOMAIN_ALLOC_OK,
+ EFC_EVT_DOMAIN_ALLOC_FAIL,
+ EFC_EVT_DOMAIN_REQ_ATTACH,
+ EFC_EVT_DOMAIN_ATTACH_OK,
+ EFC_EVT_DOMAIN_ATTACH_FAIL,
+ EFC_EVT_DOMAIN_LOST,
+ EFC_EVT_DOMAIN_FREE_OK,
+ EFC_EVT_DOMAIN_FREE_FAIL,
+ EFC_EVT_HW_DOMAIN_REQ_ATTACH,
+ EFC_EVT_HW_DOMAIN_REQ_FREE,
+
+ /* Sport Events */
+ EFC_EVT_NPORT_ALLOC_OK,
+ EFC_EVT_NPORT_ALLOC_FAIL,
+ EFC_EVT_NPORT_ATTACH_OK,
+ EFC_EVT_NPORT_ATTACH_FAIL,
+ EFC_EVT_NPORT_FREE_OK,
+ EFC_EVT_NPORT_FREE_FAIL,
+ EFC_EVT_NPORT_TOPOLOGY_NOTIFY,
+ EFC_EVT_HW_PORT_ALLOC_OK,
+ EFC_EVT_HW_PORT_ALLOC_FAIL,
+ EFC_EVT_HW_PORT_ATTACH_OK,
+ EFC_EVT_HW_PORT_REQ_ATTACH,
+ EFC_EVT_HW_PORT_REQ_FREE,
+ EFC_EVT_HW_PORT_FREE_OK,
+
+ /* Login Events */
+ EFC_EVT_SRRS_ELS_REQ_OK,
+ EFC_EVT_SRRS_ELS_CMPL_OK,
+ EFC_EVT_SRRS_ELS_REQ_FAIL,
+ EFC_EVT_SRRS_ELS_CMPL_FAIL,
+ EFC_EVT_SRRS_ELS_REQ_RJT,
+ EFC_EVT_NODE_ATTACH_OK,
+ EFC_EVT_NODE_ATTACH_FAIL,
+ EFC_EVT_NODE_FREE_OK,
+ EFC_EVT_NODE_FREE_FAIL,
+ EFC_EVT_ELS_FRAME,
+ EFC_EVT_ELS_REQ_TIMEOUT,
+ EFC_EVT_ELS_REQ_ABORTED,
+ /* request an ELS IO be aborted */
+ EFC_EVT_ABORT_ELS,
+ /* ELS abort process complete */
+ EFC_EVT_ELS_ABORT_CMPL,
+
+ EFC_EVT_ABTS_RCVD,
+
+ /* node is not in the GID_PT payload */
+ EFC_EVT_NODE_MISSING,
+ /* node is allocated and in the GID_PT payload */
+ EFC_EVT_NODE_REFOUND,
+ /* node shutting down due to PLOGI recvd (implicit logo) */
+ EFC_EVT_SHUTDOWN_IMPLICIT_LOGO,
+ /* node shutting down due to LOGO recvd/sent (explicit logo) */
+ EFC_EVT_SHUTDOWN_EXPLICIT_LOGO,
+
+ EFC_EVT_PLOGI_RCVD,
+ EFC_EVT_FLOGI_RCVD,
+ EFC_EVT_LOGO_RCVD,
+ EFC_EVT_PRLI_RCVD,
+ EFC_EVT_PRLO_RCVD,
+ EFC_EVT_PDISC_RCVD,
+ EFC_EVT_FDISC_RCVD,
+ EFC_EVT_ADISC_RCVD,
+ EFC_EVT_RSCN_RCVD,
+ EFC_EVT_SCR_RCVD,
+ EFC_EVT_ELS_RCVD,
+
+ EFC_EVT_FCP_CMD_RCVD,
+
+ EFC_EVT_GIDPT_DELAY_EXPIRED,
+
+ /* SCSI Target Server events */
+ EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY,
+ EFC_EVT_NODE_DEL_INI_COMPLETE,
+ EFC_EVT_NODE_DEL_TGT_COMPLETE,
+ EFC_EVT_NODE_SESS_REG_OK,
+ EFC_EVT_NODE_SESS_REG_FAIL,
+
+ /* Must be last */
+ EFC_EVT_LAST
+};
+
+/* State Machine event name lookup array */
+#define EFC_SM_EVENT_NAME { \
+ [EFC_EVT_ENTER] = "EFC_EVT_ENTER", \
+ [EFC_EVT_REENTER] = "EFC_EVT_REENTER", \
+ [EFC_EVT_EXIT] = "EFC_EVT_EXIT", \
+ [EFC_EVT_SHUTDOWN] = "EFC_EVT_SHUTDOWN", \
+ [EFC_EVT_ALL_CHILD_NODES_FREE] = "EFC_EVT_ALL_CHILD_NODES_FREE",\
+ [EFC_EVT_RESUME] = "EFC_EVT_RESUME", \
+ [EFC_EVT_TIMER_EXPIRED] = "EFC_EVT_TIMER_EXPIRED", \
+ [EFC_EVT_RESPONSE] = "EFC_EVT_RESPONSE", \
+ [EFC_EVT_ERROR] = "EFC_EVT_ERROR", \
+ [EFC_EVT_DOMAIN_FOUND] = "EFC_EVT_DOMAIN_FOUND", \
+ [EFC_EVT_DOMAIN_ALLOC_OK] = "EFC_EVT_DOMAIN_ALLOC_OK", \
+ [EFC_EVT_DOMAIN_ALLOC_FAIL] = "EFC_EVT_DOMAIN_ALLOC_FAIL", \
+ [EFC_EVT_DOMAIN_REQ_ATTACH] = "EFC_EVT_DOMAIN_REQ_ATTACH", \
+ [EFC_EVT_DOMAIN_ATTACH_OK] = "EFC_EVT_DOMAIN_ATTACH_OK", \
+ [EFC_EVT_DOMAIN_ATTACH_FAIL] = "EFC_EVT_DOMAIN_ATTACH_FAIL", \
+ [EFC_EVT_DOMAIN_LOST] = "EFC_EVT_DOMAIN_LOST", \
+ [EFC_EVT_DOMAIN_FREE_OK] = "EFC_EVT_DOMAIN_FREE_OK", \
+ [EFC_EVT_DOMAIN_FREE_FAIL] = "EFC_EVT_DOMAIN_FREE_FAIL", \
+ [EFC_EVT_HW_DOMAIN_REQ_ATTACH] = "EFC_EVT_HW_DOMAIN_REQ_ATTACH",\
+ [EFC_EVT_HW_DOMAIN_REQ_FREE] = "EFC_EVT_HW_DOMAIN_REQ_FREE", \
+ [EFC_EVT_NPORT_ALLOC_OK] = "EFC_EVT_NPORT_ALLOC_OK", \
+ [EFC_EVT_NPORT_ALLOC_FAIL] = "EFC_EVT_NPORT_ALLOC_FAIL", \
+ [EFC_EVT_NPORT_ATTACH_OK] = "EFC_EVT_NPORT_ATTACH_OK", \
+ [EFC_EVT_NPORT_ATTACH_FAIL] = "EFC_EVT_NPORT_ATTACH_FAIL", \
+ [EFC_EVT_NPORT_FREE_OK] = "EFC_EVT_NPORT_FREE_OK", \
+ [EFC_EVT_NPORT_FREE_FAIL] = "EFC_EVT_NPORT_FREE_FAIL", \
+ [EFC_EVT_NPORT_TOPOLOGY_NOTIFY] = "EFC_EVT_NPORT_TOPOLOGY_NOTIFY",\
+ [EFC_EVT_HW_PORT_ALLOC_OK] = "EFC_EVT_HW_PORT_ALLOC_OK", \
+ [EFC_EVT_HW_PORT_ALLOC_FAIL] = "EFC_EVT_HW_PORT_ALLOC_FAIL", \
+ [EFC_EVT_HW_PORT_ATTACH_OK] = "EFC_EVT_HW_PORT_ATTACH_OK", \
+ [EFC_EVT_HW_PORT_REQ_ATTACH] = "EFC_EVT_HW_PORT_REQ_ATTACH", \
+ [EFC_EVT_HW_PORT_REQ_FREE] = "EFC_EVT_HW_PORT_REQ_FREE", \
+ [EFC_EVT_HW_PORT_FREE_OK] = "EFC_EVT_HW_PORT_FREE_OK", \
+ [EFC_EVT_SRRS_ELS_REQ_OK] = "EFC_EVT_SRRS_ELS_REQ_OK", \
+ [EFC_EVT_SRRS_ELS_CMPL_OK] = "EFC_EVT_SRRS_ELS_CMPL_OK", \
+ [EFC_EVT_SRRS_ELS_REQ_FAIL] = "EFC_EVT_SRRS_ELS_REQ_FAIL", \
+ [EFC_EVT_SRRS_ELS_CMPL_FAIL] = "EFC_EVT_SRRS_ELS_CMPL_FAIL", \
+ [EFC_EVT_SRRS_ELS_REQ_RJT] = "EFC_EVT_SRRS_ELS_REQ_RJT", \
+ [EFC_EVT_NODE_ATTACH_OK] = "EFC_EVT_NODE_ATTACH_OK", \
+ [EFC_EVT_NODE_ATTACH_FAIL] = "EFC_EVT_NODE_ATTACH_FAIL", \
+ [EFC_EVT_NODE_FREE_OK] = "EFC_EVT_NODE_FREE_OK", \
+ [EFC_EVT_NODE_FREE_FAIL] = "EFC_EVT_NODE_FREE_FAIL", \
+ [EFC_EVT_ELS_FRAME] = "EFC_EVT_ELS_FRAME", \
+ [EFC_EVT_ELS_REQ_TIMEOUT] = "EFC_EVT_ELS_REQ_TIMEOUT", \
+ [EFC_EVT_ELS_REQ_ABORTED] = "EFC_EVT_ELS_REQ_ABORTED", \
+ [EFC_EVT_ABORT_ELS] = "EFC_EVT_ABORT_ELS", \
+ [EFC_EVT_ELS_ABORT_CMPL] = "EFC_EVT_ELS_ABORT_CMPL", \
+ [EFC_EVT_ABTS_RCVD] = "EFC_EVT_ABTS_RCVD", \
+ [EFC_EVT_NODE_MISSING] = "EFC_EVT_NODE_MISSING", \
+ [EFC_EVT_NODE_REFOUND] = "EFC_EVT_NODE_REFOUND", \
+ [EFC_EVT_SHUTDOWN_IMPLICIT_LOGO] = "EFC_EVT_SHUTDOWN_IMPLICIT_LOGO",\
+ [EFC_EVT_SHUTDOWN_EXPLICIT_LOGO] = "EFC_EVT_SHUTDOWN_EXPLICIT_LOGO",\
+ [EFC_EVT_PLOGI_RCVD] = "EFC_EVT_PLOGI_RCVD", \
+ [EFC_EVT_FLOGI_RCVD] = "EFC_EVT_FLOGI_RCVD", \
+ [EFC_EVT_LOGO_RCVD] = "EFC_EVT_LOGO_RCVD", \
+ [EFC_EVT_PRLI_RCVD] = "EFC_EVT_PRLI_RCVD", \
+ [EFC_EVT_PRLO_RCVD] = "EFC_EVT_PRLO_RCVD", \
+ [EFC_EVT_PDISC_RCVD] = "EFC_EVT_PDISC_RCVD", \
+ [EFC_EVT_FDISC_RCVD] = "EFC_EVT_FDISC_RCVD", \
+ [EFC_EVT_ADISC_RCVD] = "EFC_EVT_ADISC_RCVD", \
+ [EFC_EVT_RSCN_RCVD] = "EFC_EVT_RSCN_RCVD", \
+ [EFC_EVT_SCR_RCVD] = "EFC_EVT_SCR_RCVD", \
+ [EFC_EVT_ELS_RCVD] = "EFC_EVT_ELS_RCVD", \
+ [EFC_EVT_FCP_CMD_RCVD] = "EFC_EVT_FCP_CMD_RCVD", \
+ [EFC_EVT_NODE_DEL_INI_COMPLETE] = "EFC_EVT_NODE_DEL_INI_COMPLETE",\
+ [EFC_EVT_NODE_DEL_TGT_COMPLETE] = "EFC_EVT_NODE_DEL_TGT_COMPLETE",\
+ [EFC_EVT_LAST] = "EFC_EVT_LAST", \
+}
+
+int
+efc_sm_post_event(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *data);
+void
+efc_sm_transition(struct efc_sm_ctx *ctx,
+ void (*state)(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg),
+ void *data);
+void efc_sm_disable(struct efc_sm_ctx *ctx);
+const char *efc_sm_event_name(enum efc_sm_event evt);
+
+#endif /* ! _EFC_SM_H */
diff --git a/drivers/scsi/elx/libefc/efclib.c b/drivers/scsi/elx/libefc/efclib.c
new file mode 100644
index 000000000000..dd3e3d0a4761
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efclib.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * LIBEFC LOCKING
+ *
+ * The critical sections protected by the efc's spinlock are quite broad and
+ * may be improved upon in the future. The libefc code and its locking doesn't
+ * influence the I/O path, so excessive locking doesn't impact I/O performance.
+ *
+ * The strategy is to lock whenever processing a request from user driver. This
+ * means that the entry points into the libefc library are protected by efc
+ * lock. So all the state machine transitions are protected.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "efc.h"
+
+int efcport_init(struct efc *efc)
+{
+ u32 rc = 0;
+
+ spin_lock_init(&efc->lock);
+ INIT_LIST_HEAD(&efc->vport_list);
+ efc->hold_frames = false;
+ spin_lock_init(&efc->pend_frames_lock);
+ INIT_LIST_HEAD(&efc->pend_frames);
+
+ /* Create Node pool */
+ efc->node_pool = mempool_create_kmalloc_pool(EFC_MAX_REMOTE_NODES,
+ sizeof(struct efc_node));
+ if (!efc->node_pool) {
+ efc_log_err(efc, "Can't allocate node pool\n");
+ return -ENOMEM;
+ }
+
+ efc->node_dma_pool = dma_pool_create("node_dma_pool", &efc->pci->dev,
+ NODE_SPARAMS_SIZE, 0, 0);
+ if (!efc->node_dma_pool) {
+ efc_log_err(efc, "Can't allocate node dma pool\n");
+ mempool_destroy(efc->node_pool);
+ return -ENOMEM;
+ }
+
+ efc->els_io_pool = mempool_create_kmalloc_pool(EFC_ELS_IO_POOL_SZ,
+ sizeof(struct efc_els_io_req));
+ if (!efc->els_io_pool) {
+ efc_log_err(efc, "Can't allocate els io pool\n");
+ return -ENOMEM;
+ }
+
+ return rc;
+}
+
+static void
+efc_purge_pending(struct efc *efc)
+{
+ struct efc_hw_sequence *frame, *next;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&efc->pend_frames_lock, flags);
+
+ list_for_each_entry_safe(frame, next, &efc->pend_frames, list_entry) {
+ list_del(&frame->list_entry);
+ efc->tt.hw_seq_free(efc, frame);
+ }
+
+ spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
+}
+
+void efcport_destroy(struct efc *efc)
+{
+ efc_purge_pending(efc);
+ mempool_destroy(efc->els_io_pool);
+ mempool_destroy(efc->node_pool);
+ dma_pool_destroy(efc->node_dma_pool);
+}
diff --git a/drivers/scsi/elx/libefc/efclib.h b/drivers/scsi/elx/libefc/efclib.h
new file mode 100644
index 000000000000..57e338612812
--- /dev/null
+++ b/drivers/scsi/elx/libefc/efclib.h
@@ -0,0 +1,623 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+#ifndef __EFCLIB_H__
+#define __EFCLIB_H__
+
+#include "scsi/fc/fc_els.h"
+#include "scsi/fc/fc_fs.h"
+#include "scsi/fc/fc_ns.h"
+#include "scsi/fc/fc_gs.h"
+#include "scsi/fc_frame.h"
+#include "../include/efc_common.h"
+#include "../libefc_sli/sli4.h"
+
+#define EFC_SERVICE_PARMS_LENGTH 120
+#define EFC_NAME_LENGTH 32
+#define EFC_SM_NAME_LENGTH 64
+#define EFC_DISPLAY_BUS_INFO_LENGTH 16
+
+#define EFC_WWN_LENGTH 32
+
+#define EFC_FC_ELS_DEFAULT_RETRIES 3
+
+/* Timeouts */
+#define EFC_FC_ELS_SEND_DEFAULT_TIMEOUT 0
+#define EFC_FC_FLOGI_TIMEOUT_SEC 5
+#define EFC_SHUTDOWN_TIMEOUT_USEC 30000000
+
+/* Return values for calls from base driver to libefc */
+#define EFC_SCSI_CALL_COMPLETE 0
+#define EFC_SCSI_CALL_ASYNC 1
+
+/* Local port topology */
+enum efc_nport_topology {
+ EFC_NPORT_TOPO_UNKNOWN = 0,
+ EFC_NPORT_TOPO_FABRIC,
+ EFC_NPORT_TOPO_P2P,
+ EFC_NPORT_TOPO_FC_AL,
+};
+
+#define enable_target_rscn(efc) 1
+
+enum efc_node_shutd_rsn {
+ EFC_NODE_SHUTDOWN_DEFAULT = 0,
+ EFC_NODE_SHUTDOWN_EXPLICIT_LOGO,
+ EFC_NODE_SHUTDOWN_IMPLICIT_LOGO,
+};
+
+enum efc_node_send_ls_acc {
+ EFC_NODE_SEND_LS_ACC_NONE = 0,
+ EFC_NODE_SEND_LS_ACC_PLOGI,
+ EFC_NODE_SEND_LS_ACC_PRLI,
+};
+
+#define EFC_LINK_STATUS_UP 0
+#define EFC_LINK_STATUS_DOWN 1
+
+enum efc_sm_event;
+
+/* State machine context header */
+struct efc_sm_ctx {
+ void (*current_state)(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+
+ const char *description;
+ void *app;
+};
+
+/* Description of discovered Fabric Domain */
+struct efc_domain_record {
+ u32 index;
+ u32 priority;
+ u8 address[6];
+ u8 wwn[8];
+ union {
+ u8 vlan[512];
+ u8 loop[128];
+ } map;
+ u32 speed;
+ u32 fc_id;
+ bool is_loop;
+ bool is_nport;
+};
+
+/* Domain events */
+enum efc_hw_domain_event {
+ EFC_HW_DOMAIN_ALLOC_OK,
+ EFC_HW_DOMAIN_ALLOC_FAIL,
+ EFC_HW_DOMAIN_ATTACH_OK,
+ EFC_HW_DOMAIN_ATTACH_FAIL,
+ EFC_HW_DOMAIN_FREE_OK,
+ EFC_HW_DOMAIN_FREE_FAIL,
+ EFC_HW_DOMAIN_LOST,
+ EFC_HW_DOMAIN_FOUND,
+ EFC_HW_DOMAIN_CHANGED,
+};
+
+/**
+ * Fibre Channel port object
+ *
+ * @list_entry: nport list entry
+ * @ref: reference count, each node takes a reference
+ * @release: function to free nport object
+ * @efc: pointer back to efc
+ * @instance_index: unique instance index value
+ * @display_name: port display name
+ * @is_vport: Is NPIV port
+ * @free_req_pending: pending request to free resources
+ * @attached: mark attached if reg VPI succeeds
+ * @p2p_winner: TRUE if we're the point-to-point winner
+ * @domain: pointer back to domain
+ * @wwpn: port wwpn
+ * @wwnn: port wwnn
+ * @tgt_data: target backend private port data
+ * @ini_data: initiator backend private port data
+ * @indicator: VPI
+ * @fc_id: port FC address
+ * @dma: memory for Service Parameters
+ * @wwnn_str: wwpn string
+ * @sli_wwpn: SLI provided wwpn
+ * @sli_wwnn: SLI provided wwnn
+ * @sm: nport state machine context
+ * @lookup: fc_id to node lookup object
+ * @enable_ini: SCSI initiator enabled for this port
+ * @enable_tgt: SCSI target enabled for this port
+ * @enable_rscn: port will be expecting RSCN
+ * @shutting_down: nport in process of shutting down
+ * @p2p_port_id: our port id for point-to-point
+ * @topology: topology: fabric/p2p/unknown
+ * @service_params: login parameters
+ * @p2p_remote_port_id: remote node's port id for point-to-point
+ */
+
+struct efc_nport {
+ struct list_head list_entry;
+ struct kref ref;
+ void (*release)(struct kref *arg);
+ struct efc *efc;
+ u32 instance_index;
+ char display_name[EFC_NAME_LENGTH];
+ bool is_vport;
+ bool free_req_pending;
+ bool attached;
+ bool attaching;
+ bool p2p_winner;
+ struct efc_domain *domain;
+ u64 wwpn;
+ u64 wwnn;
+ void *tgt_data;
+ void *ini_data;
+
+ u32 indicator;
+ u32 fc_id;
+ struct efc_dma dma;
+
+ u8 wwnn_str[EFC_WWN_LENGTH];
+ __be64 sli_wwpn;
+ __be64 sli_wwnn;
+
+ struct efc_sm_ctx sm;
+ struct xarray lookup;
+ bool enable_ini;
+ bool enable_tgt;
+ bool enable_rscn;
+ bool shutting_down;
+ u32 p2p_port_id;
+ enum efc_nport_topology topology;
+ u8 service_params[EFC_SERVICE_PARMS_LENGTH];
+ u32 p2p_remote_port_id;
+};
+
+/**
+ * Fibre Channel domain object
+ *
+ * This object is a container for the various SLI components needed
+ * to connect to the domain of a FC or FCoE switch
+ * @efc: pointer back to efc
+ * @instance_index: unique instance index value
+ * @display_name: Node display name
+ * @nport_list: linked list of nports associated with this domain
+ * @ref: Reference count, each nport takes a reference
+ * @release: Function to free domain object
+ * @ini_domain: initiator backend private domain data
+ * @tgt_domain: target backend private domain data
+ * @sm: state machine context
+ * @fcf: FC Forwarder table index
+ * @fcf_indicator: FCFI
+ * @indicator: VFI
+ * @nport_count: Number of nports allocated
+ * @dma: memory for Service Parameters
+ * @fcf_wwn: WWN for FCF/switch
+ * @drvsm: driver domain sm context
+ * @attached: set true after attach completes
+ * @is_fc: is FC
+ * @is_loop: is loop topology
+ * @is_nlport: is public loop
+ * @domain_found_pending:A domain found is pending, drec is updated
+ * @req_domain_free: True if domain object should be free'd
+ * @req_accept_frames: set in domain state machine to enable frames
+ * @domain_notify_pend: Set in domain SM to avoid duplicate node event post
+ * @pending_drec: Pending drec if a domain found is pending
+ * @service_params: any nports service parameters
+ * @flogi_service_params:Fabric/P2p service parameters from FLOGI
+ * @lookup: d_id to node lookup object
+ * @nport: Pointer to first (physical) SLI port
+ */
+struct efc_domain {
+ struct efc *efc;
+ char display_name[EFC_NAME_LENGTH];
+ struct list_head nport_list;
+ struct kref ref;
+ void (*release)(struct kref *arg);
+ void *ini_domain;
+ void *tgt_domain;
+
+ /* Declarations private to HW/SLI */
+ u32 fcf;
+ u32 fcf_indicator;
+ u32 indicator;
+ u32 nport_count;
+ struct efc_dma dma;
+
+ /* Declarations private to FC trannport */
+ u64 fcf_wwn;
+ struct efc_sm_ctx drvsm;
+ bool attached;
+ bool is_fc;
+ bool is_loop;
+ bool is_nlport;
+ bool domain_found_pending;
+ bool req_domain_free;
+ bool req_accept_frames;
+ bool domain_notify_pend;
+
+ struct efc_domain_record pending_drec;
+ u8 service_params[EFC_SERVICE_PARMS_LENGTH];
+ u8 flogi_service_params[EFC_SERVICE_PARMS_LENGTH];
+
+ struct xarray lookup;
+
+ struct efc_nport *nport;
+};
+
+/**
+ * Remote Node object
+ *
+ * This object represents a connection between the SLI port and another
+ * Nx_Port on the fabric. Note this can be either a well known port such
+ * as a F_Port (i.e. ff:ff:fe) or another N_Port.
+ * @indicator: RPI
+ * @fc_id: FC address
+ * @attached: true if attached
+ * @nport: associated SLI port
+ * @node: associated node
+ */
+struct efc_remote_node {
+ u32 indicator;
+ u32 index;
+ u32 fc_id;
+
+ bool attached;
+
+ struct efc_nport *nport;
+ void *node;
+};
+
+/**
+ * FC Node object
+ * @efc: pointer back to efc structure
+ * @display_name: Node display name
+ * @nort: Assosiated nport pointer.
+ * @hold_frames: hold incoming frames if true
+ * @els_io_enabled: Enable allocating els ios for this node
+ * @els_ios_lock: lock to protect the els ios list
+ * @els_ios_list: ELS I/O's for this node
+ * @ini_node: backend initiator private node data
+ * @tgt_node: backend target private node data
+ * @rnode: Remote node
+ * @sm: state machine context
+ * @evtdepth: current event posting nesting depth
+ * @req_free: this node is to be free'd
+ * @attached: node is attached (REGLOGIN complete)
+ * @fcp_enabled: node is enabled to handle FCP
+ * @rscn_pending: for name server node RSCN is pending
+ * @send_plogi: send PLOGI accept, upon completion of node attach
+ * @send_plogi_acc: TRUE if io_alloc() is enabled.
+ * @send_ls_acc: type of LS acc to send
+ * @ls_acc_io: SCSI IO for LS acc
+ * @ls_acc_oxid: OX_ID for pending accept
+ * @ls_acc_did: D_ID for pending accept
+ * @shutdown_reason: reason for node shutdown
+ * @sparm_dma_buf: service parameters buffer
+ * @service_params: plogi/acc frame from remote device
+ * @pend_frames_lock: lock for inbound pending frames list
+ * @pend_frames: inbound pending frames list
+ * @pend_frames_processed:count of frames processed in hold frames interval
+ * @ox_id_in_use: used to verify one at a time us of ox_id
+ * @els_retries_remaining:for ELS, number of retries remaining
+ * @els_req_cnt: number of outstanding ELS requests
+ * @els_cmpl_cnt: number of outstanding ELS completions
+ * @abort_cnt: Abort counter for debugging purpos
+ * @current_state_name: current node state
+ * @prev_state_name: previous node state
+ * @current_evt: current event
+ * @prev_evt: previous event
+ * @targ: node is target capable
+ * @init: node is init capable
+ * @refound: Handle node refound case when node is being deleted
+ * @els_io_pend_list: list of pending (not yet processed) ELS IOs
+ * @els_io_active_list: list of active (processed) ELS IOs
+ * @nodedb_state: Node debugging, saved state
+ * @gidpt_delay_timer: GIDPT delay timer
+ * @time_last_gidpt_msec:Start time of last target RSCN GIDPT
+ * @wwnn: remote port WWNN
+ * @wwpn: remote port WWPN
+ */
+struct efc_node {
+ struct efc *efc;
+ char display_name[EFC_NAME_LENGTH];
+ struct efc_nport *nport;
+ struct kref ref;
+ void (*release)(struct kref *arg);
+ bool hold_frames;
+ bool els_io_enabled;
+ bool send_plogi_acc;
+ bool send_plogi;
+ bool rscn_pending;
+ bool fcp_enabled;
+ bool attached;
+ bool req_free;
+
+ spinlock_t els_ios_lock;
+ struct list_head els_ios_list;
+ void *ini_node;
+ void *tgt_node;
+
+ struct efc_remote_node rnode;
+ /* Declarations private to FC trannport */
+ struct efc_sm_ctx sm;
+ u32 evtdepth;
+
+ enum efc_node_send_ls_acc send_ls_acc;
+ void *ls_acc_io;
+ u32 ls_acc_oxid;
+ u32 ls_acc_did;
+ enum efc_node_shutd_rsn shutdown_reason;
+ bool targ;
+ bool init;
+ bool refound;
+ struct efc_dma sparm_dma_buf;
+ u8 service_params[EFC_SERVICE_PARMS_LENGTH];
+ spinlock_t pend_frames_lock;
+ struct list_head pend_frames;
+ u32 pend_frames_processed;
+ u32 ox_id_in_use;
+ u32 els_retries_remaining;
+ u32 els_req_cnt;
+ u32 els_cmpl_cnt;
+ u32 abort_cnt;
+
+ char current_state_name[EFC_SM_NAME_LENGTH];
+ char prev_state_name[EFC_SM_NAME_LENGTH];
+ int current_evt;
+ int prev_evt;
+
+ void (*nodedb_state)(struct efc_sm_ctx *ctx,
+ enum efc_sm_event evt, void *arg);
+ struct timer_list gidpt_delay_timer;
+ u64 time_last_gidpt_msec;
+
+ char wwnn[EFC_WWN_LENGTH];
+ char wwpn[EFC_WWN_LENGTH];
+};
+
+/**
+ * NPIV port
+ *
+ * Collection of the information required to restore a virtual port across
+ * link events
+ * @wwnn: node name
+ * @wwpn: port name
+ * @fc_id: port id
+ * @tgt_data: target backend pointer
+ * @ini_data: initiator backend pointe
+ * @nport: Used to match record after attaching for update
+ *
+ */
+
+struct efc_vport {
+ struct list_head list_entry;
+ u64 wwnn;
+ u64 wwpn;
+ u32 fc_id;
+ bool enable_tgt;
+ bool enable_ini;
+ void *tgt_data;
+ void *ini_data;
+ struct efc_nport *nport;
+};
+
+#define node_printf(node, fmt, args...) \
+ efc_log_info(node->efc, "[%s] " fmt, node->display_name, ##args)
+
+/* Node SM IO Context Callback structure */
+struct efc_node_cb {
+ int status;
+ int ext_status;
+ struct efc_hw_rq_buffer *header;
+ struct efc_hw_rq_buffer *payload;
+ struct efc_dma els_rsp;
+
+ /* Actual length of data received */
+ int rsp_len;
+};
+
+struct efc_hw_rq_buffer {
+ u16 rqindex;
+ struct efc_dma dma;
+};
+
+/**
+ * FC sequence object
+ *
+ * Defines a general FC sequence object
+ * @hw: HW that owns this sequence
+ * @fcfi: FCFI associated with sequence
+ * @header: Received frame header
+ * @payload: Received frame header
+ * @hw_priv: HW private context
+ */
+struct efc_hw_sequence {
+ struct list_head list_entry;
+ void *hw;
+ u8 fcfi;
+ struct efc_hw_rq_buffer *header;
+ struct efc_hw_rq_buffer *payload;
+ void *hw_priv;
+};
+
+enum efc_disc_io_type {
+ EFC_DISC_IO_ELS_REQ,
+ EFC_DISC_IO_ELS_RESP,
+ EFC_DISC_IO_CT_REQ,
+ EFC_DISC_IO_CT_RESP
+};
+
+struct efc_io_els_params {
+ u32 s_id;
+ u16 ox_id;
+ u8 timeout;
+};
+
+struct efc_io_ct_params {
+ u8 r_ctl;
+ u8 type;
+ u8 df_ctl;
+ u8 timeout;
+ u16 ox_id;
+};
+
+union efc_disc_io_param {
+ struct efc_io_els_params els;
+ struct efc_io_ct_params ct;
+};
+
+struct efc_disc_io {
+ struct efc_dma req; /* send buffer */
+ struct efc_dma rsp; /* receive buffer */
+ enum efc_disc_io_type io_type; /* EFC_DISC_IO_TYPE enum*/
+ u16 xmit_len; /* Length of els request*/
+ u16 rsp_len; /* Max length of rsps to be rcvd */
+ u32 rpi; /* Registered RPI */
+ u32 vpi; /* VPI for this nport */
+ u32 s_id;
+ u32 d_id;
+ bool rpi_registered; /* if false, use tmp RPI */
+ union efc_disc_io_param iparam;
+};
+
+/* Return value indiacating the sequence can not be freed */
+#define EFC_HW_SEQ_HOLD 0
+/* Return value indiacating the sequence can be freed */
+#define EFC_HW_SEQ_FREE 1
+
+struct libefc_function_template {
+ /*Sport*/
+ int (*new_nport)(struct efc *efc, struct efc_nport *sp);
+ void (*del_nport)(struct efc *efc, struct efc_nport *sp);
+
+ /*Scsi Node*/
+ int (*scsi_new_node)(struct efc *efc, struct efc_node *n);
+ int (*scsi_del_node)(struct efc *efc, struct efc_node *n, int reason);
+
+ int (*issue_mbox_rqst)(void *efct, void *buf, void *cb, void *arg);
+ /*Send ELS IO*/
+ int (*send_els)(struct efc *efc, struct efc_disc_io *io);
+ /*Send BLS IO*/
+ int (*send_bls)(struct efc *efc, u32 type, struct sli_bls_params *bls);
+ /*Free HW frame*/
+ int (*hw_seq_free)(struct efc *efc, struct efc_hw_sequence *seq);
+};
+
+#define EFC_LOG_LIB 0x01
+#define EFC_LOG_NODE 0x02
+#define EFC_LOG_PORT 0x04
+#define EFC_LOG_DOMAIN 0x08
+#define EFC_LOG_ELS 0x10
+#define EFC_LOG_DOMAIN_SM 0x20
+#define EFC_LOG_SM 0x40
+
+/* efc library port structure */
+struct efc {
+ void *base;
+ struct pci_dev *pci;
+ struct sli4 *sli;
+ u32 fcfi;
+ u64 req_wwpn;
+ u64 req_wwnn;
+
+ u64 def_wwpn;
+ u64 def_wwnn;
+ u64 max_xfer_size;
+ mempool_t *node_pool;
+ struct dma_pool *node_dma_pool;
+ u32 nodes_count;
+
+ u32 link_status;
+
+ struct list_head vport_list;
+ /* lock to protect the vport list */
+ spinlock_t vport_lock;
+
+ struct libefc_function_template tt;
+ /* lock to protect the discovery library.
+ * Refer to efclib.c for more details.
+ */
+ spinlock_t lock;
+
+ bool enable_ini;
+ bool enable_tgt;
+
+ u32 log_level;
+
+ struct efc_domain *domain;
+ void (*domain_free_cb)(struct efc *efc, void *arg);
+ void *domain_free_cb_arg;
+
+ u64 tgt_rscn_delay_msec;
+ u64 tgt_rscn_period_msec;
+
+ bool external_loopback;
+ u32 nodedb_mask;
+ u32 logmask;
+ mempool_t *els_io_pool;
+ atomic_t els_io_alloc_failed_count;
+
+ /* hold pending frames */
+ bool hold_frames;
+ /* lock to protect pending frames list access */
+ spinlock_t pend_frames_lock;
+ struct list_head pend_frames;
+ /* count of pending frames that were processed */
+ u32 pend_frames_processed;
+
+};
+
+/*
+ * EFC library registration
+ * **********************************/
+int efcport_init(struct efc *efc);
+void efcport_destroy(struct efc *efc);
+/*
+ * EFC Domain
+ * **********************************/
+int efc_domain_cb(void *arg, int event, void *data);
+void
+efc_register_domain_free_cb(struct efc *efc,
+ void (*callback)(struct efc *efc, void *arg),
+ void *arg);
+
+/*
+ * EFC nport
+ * **********************************/
+void efc_nport_cb(void *arg, int event, void *data);
+struct efc_vport *
+efc_vport_create_spec(struct efc *efc, u64 wwnn, u64 wwpn, u32 fc_id,
+ bool enable_ini, bool enable_tgt,
+ void *tgt_data, void *ini_data);
+int efc_nport_vport_new(struct efc_domain *domain, u64 wwpn,
+ u64 wwnn, u32 fc_id, bool ini, bool tgt,
+ void *tgt_data, void *ini_data);
+int efc_nport_vport_del(struct efc *efc, struct efc_domain *domain,
+ u64 wwpn, u64 wwnn);
+
+void efc_vport_del_all(struct efc *efc);
+
+/*
+ * EFC Node
+ * **********************************/
+int efc_remote_node_cb(void *arg, int event, void *data);
+void efc_node_fcid_display(u32 fc_id, char *buffer, u32 buf_len);
+void efc_node_post_shutdown(struct efc_node *node, void *arg);
+u64 efc_node_get_wwpn(struct efc_node *node);
+
+/*
+ * EFC FCP/ELS/CT interface
+ * **********************************/
+void efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq);
+void efc_disc_io_complete(struct efc_disc_io *io, u32 len, u32 status,
+ u32 ext_status);
+
+/*
+ * EFC SCSI INTERACTION LAYER
+ * **********************************/
+void efc_scsi_sess_reg_complete(struct efc_node *node, u32 status);
+void efc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node);
+void efc_scsi_del_target_complete(struct efc *efc, struct efc_node *node);
+void efc_scsi_io_list_empty(struct efc *efc, struct efc_node *node);
+
+#endif /* __EFCLIB_H__ */
diff --git a/drivers/scsi/elx/libefc_sli/sli4.c b/drivers/scsi/elx/libefc_sli/sli4.c
new file mode 100644
index 000000000000..d9a231fc0e0d
--- /dev/null
+++ b/drivers/scsi/elx/libefc_sli/sli4.c
@@ -0,0 +1,5155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * All common (i.e. transport-independent) SLI-4 functions are implemented
+ * in this file.
+ */
+#include "sli4.h"
+
+static struct sli4_asic_entry_t sli4_asic_table[] = {
+ { SLI4_ASIC_REV_B0, SLI4_ASIC_GEN_5},
+ { SLI4_ASIC_REV_D0, SLI4_ASIC_GEN_5},
+ { SLI4_ASIC_REV_A3, SLI4_ASIC_GEN_6},
+ { SLI4_ASIC_REV_A0, SLI4_ASIC_GEN_6},
+ { SLI4_ASIC_REV_A1, SLI4_ASIC_GEN_6},
+ { SLI4_ASIC_REV_A3, SLI4_ASIC_GEN_6},
+ { SLI4_ASIC_REV_A1, SLI4_ASIC_GEN_7},
+ { SLI4_ASIC_REV_A0, SLI4_ASIC_GEN_7},
+};
+
+/* Convert queue type enum (SLI_QTYPE_*) into a string */
+static char *SLI4_QNAME[] = {
+ "Event Queue",
+ "Completion Queue",
+ "Mailbox Queue",
+ "Work Queue",
+ "Receive Queue",
+ "Undefined"
+};
+
+/**
+ * sli_config_cmd_init() - Write a SLI_CONFIG command to the provided buffer.
+ *
+ * @sli4: SLI context pointer.
+ * @buf: Destination buffer for the command.
+ * @length: Length in bytes of attached command.
+ * @dma: DMA buffer for non-embedded commands.
+ * Return: Command payload buffer.
+ */
+static void *
+sli_config_cmd_init(struct sli4 *sli4, void *buf, u32 length,
+ struct efc_dma *dma)
+{
+ struct sli4_cmd_sli_config *config;
+ u32 flags;
+
+ if (length > sizeof(config->payload.embed) && !dma) {
+ efc_log_err(sli4, "Too big for an embedded cmd with len(%d)\n",
+ length);
+ return NULL;
+ }
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ config = buf;
+
+ config->hdr.command = SLI4_MBX_CMD_SLI_CONFIG;
+ if (!dma) {
+ flags = SLI4_SLICONF_EMB;
+ config->dw1_flags = cpu_to_le32(flags);
+ config->payload_len = cpu_to_le32(length);
+ return config->payload.embed;
+ }
+
+ flags = SLI4_SLICONF_PMDCMD_VAL_1;
+ flags &= ~SLI4_SLICONF_EMB;
+ config->dw1_flags = cpu_to_le32(flags);
+
+ config->payload.mem.addr.low = cpu_to_le32(lower_32_bits(dma->phys));
+ config->payload.mem.addr.high = cpu_to_le32(upper_32_bits(dma->phys));
+ config->payload.mem.length =
+ cpu_to_le32(dma->size & SLI4_SLICONF_PMD_LEN);
+ config->payload_len = cpu_to_le32(dma->size);
+ /* save pointer to DMA for BMBX dumping purposes */
+ sli4->bmbx_non_emb_pmd = dma;
+ return dma->virt;
+}
+
+/**
+ * sli_cmd_common_create_cq() - Write a COMMON_CREATE_CQ V2 command.
+ *
+ * @sli4: SLI context pointer.
+ * @buf: Destination buffer for the command.
+ * @qmem: DMA memory for queue.
+ * @eq_id: EQ id assosiated with this cq.
+ * Return: status -EIO/0.
+ */
+static int
+sli_cmd_common_create_cq(struct sli4 *sli4, void *buf, struct efc_dma *qmem,
+ u16 eq_id)
+{
+ struct sli4_rqst_cmn_create_cq_v2 *cqv2 = NULL;
+ u32 p;
+ uintptr_t addr;
+ u32 num_pages = 0;
+ size_t cmd_size = 0;
+ u32 page_size = 0;
+ u32 n_cqe = 0;
+ u32 dw5_flags = 0;
+ u16 dw6w1_arm = 0;
+ __le32 len;
+
+ /* First calculate number of pages and the mailbox cmd length */
+ n_cqe = qmem->size / SLI4_CQE_BYTES;
+ switch (n_cqe) {
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ page_size = SZ_4K;
+ break;
+ case 4096:
+ page_size = SZ_8K;
+ break;
+ default:
+ return -EIO;
+ }
+ num_pages = sli_page_count(qmem->size, page_size);
+
+ cmd_size = SLI4_RQST_CMDSZ(cmn_create_cq_v2)
+ + SZ_DMAADDR * num_pages;
+
+ cqv2 = sli_config_cmd_init(sli4, buf, cmd_size, NULL);
+ if (!cqv2)
+ return -EIO;
+
+ len = SLI4_RQST_PYLD_LEN_VAR(cmn_create_cq_v2, SZ_DMAADDR * num_pages);
+ sli_cmd_fill_hdr(&cqv2->hdr, SLI4_CMN_CREATE_CQ, SLI4_SUBSYSTEM_COMMON,
+ CMD_V2, len);
+ cqv2->page_size = page_size / SLI_PAGE_SIZE;
+
+ /* valid values for number of pages: 1, 2, 4, 8 (sec 4.4.3) */
+ cqv2->num_pages = cpu_to_le16(num_pages);
+ if (!num_pages || num_pages > SLI4_CREATE_CQV2_MAX_PAGES)
+ return -EIO;
+
+ switch (num_pages) {
+ case 1:
+ dw5_flags |= SLI4_CQ_CNT_VAL(256);
+ break;
+ case 2:
+ dw5_flags |= SLI4_CQ_CNT_VAL(512);
+ break;
+ case 4:
+ dw5_flags |= SLI4_CQ_CNT_VAL(1024);
+ break;
+ case 8:
+ dw5_flags |= SLI4_CQ_CNT_VAL(LARGE);
+ cqv2->cqe_count = cpu_to_le16(n_cqe);
+ break;
+ default:
+ efc_log_err(sli4, "num_pages %d not valid\n", num_pages);
+ return -EIO;
+ }
+
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ dw5_flags |= SLI4_CREATE_CQV2_AUTOVALID;
+
+ dw5_flags |= SLI4_CREATE_CQV2_EVT;
+ dw5_flags |= SLI4_CREATE_CQV2_VALID;
+
+ cqv2->dw5_flags = cpu_to_le32(dw5_flags);
+ cqv2->dw6w1_arm = cpu_to_le16(dw6w1_arm);
+ cqv2->eq_id = cpu_to_le16(eq_id);
+
+ for (p = 0, addr = qmem->phys; p < num_pages; p++, addr += page_size) {
+ cqv2->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr));
+ cqv2->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr));
+ }
+
+ return 0;
+}
+
+static int
+sli_cmd_common_create_eq(struct sli4 *sli4, void *buf, struct efc_dma *qmem)
+{
+ struct sli4_rqst_cmn_create_eq *eq;
+ u32 p;
+ uintptr_t addr;
+ u16 num_pages;
+ u32 dw5_flags = 0;
+ u32 dw6_flags = 0, ver;
+
+ eq = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(cmn_create_eq),
+ NULL);
+ if (!eq)
+ return -EIO;
+
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ ver = CMD_V2;
+ else
+ ver = CMD_V0;
+
+ sli_cmd_fill_hdr(&eq->hdr, SLI4_CMN_CREATE_EQ, SLI4_SUBSYSTEM_COMMON,
+ ver, SLI4_RQST_PYLD_LEN(cmn_create_eq));
+
+ /* valid values for number of pages: 1, 2, 4 (sec 4.4.3) */
+ num_pages = qmem->size / SLI_PAGE_SIZE;
+ eq->num_pages = cpu_to_le16(num_pages);
+
+ switch (num_pages) {
+ case 1:
+ dw5_flags |= SLI4_EQE_SIZE_4;
+ dw6_flags |= SLI4_EQ_CNT_VAL(1024);
+ break;
+ case 2:
+ dw5_flags |= SLI4_EQE_SIZE_4;
+ dw6_flags |= SLI4_EQ_CNT_VAL(2048);
+ break;
+ case 4:
+ dw5_flags |= SLI4_EQE_SIZE_4;
+ dw6_flags |= SLI4_EQ_CNT_VAL(4096);
+ break;
+ default:
+ efc_log_err(sli4, "num_pages %d not valid\n", num_pages);
+ return -EIO;
+ }
+
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ dw5_flags |= SLI4_CREATE_EQ_AUTOVALID;
+
+ dw5_flags |= SLI4_CREATE_EQ_VALID;
+ dw6_flags &= (~SLI4_CREATE_EQ_ARM);
+ eq->dw5_flags = cpu_to_le32(dw5_flags);
+ eq->dw6_flags = cpu_to_le32(dw6_flags);
+ eq->dw7_delaymulti = cpu_to_le32(SLI4_CREATE_EQ_DELAYMULTI);
+
+ for (p = 0, addr = qmem->phys; p < num_pages;
+ p++, addr += SLI_PAGE_SIZE) {
+ eq->page_address[p].low = cpu_to_le32(lower_32_bits(addr));
+ eq->page_address[p].high = cpu_to_le32(upper_32_bits(addr));
+ }
+
+ return 0;
+}
+
+static int
+sli_cmd_common_create_mq_ext(struct sli4 *sli4, void *buf, struct efc_dma *qmem,
+ u16 cq_id)
+{
+ struct sli4_rqst_cmn_create_mq_ext *mq;
+ u32 p;
+ uintptr_t addr;
+ u32 num_pages;
+ u16 dw6w1_flags = 0;
+
+ mq = sli_config_cmd_init(sli4, buf,
+ SLI4_CFG_PYLD_LENGTH(cmn_create_mq_ext), NULL);
+ if (!mq)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&mq->hdr, SLI4_CMN_CREATE_MQ_EXT,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN(cmn_create_mq_ext));
+
+ /* valid values for number of pages: 1, 2, 4, 8 (sec 4.4.12) */
+ num_pages = qmem->size / SLI_PAGE_SIZE;
+ mq->num_pages = cpu_to_le16(num_pages);
+ switch (num_pages) {
+ case 1:
+ dw6w1_flags |= SLI4_MQE_SIZE_16;
+ break;
+ case 2:
+ dw6w1_flags |= SLI4_MQE_SIZE_32;
+ break;
+ case 4:
+ dw6w1_flags |= SLI4_MQE_SIZE_64;
+ break;
+ case 8:
+ dw6w1_flags |= SLI4_MQE_SIZE_128;
+ break;
+ default:
+ efc_log_info(sli4, "num_pages %d not valid\n", num_pages);
+ return -EIO;
+ }
+
+ mq->async_event_bitmap = cpu_to_le32(SLI4_ASYNC_EVT_FC_ALL);
+
+ if (sli4->params.mq_create_version) {
+ mq->cq_id_v1 = cpu_to_le16(cq_id);
+ mq->hdr.dw3_version = cpu_to_le32(CMD_V1);
+ } else {
+ dw6w1_flags |= (cq_id << SLI4_CREATE_MQEXT_CQID_SHIFT);
+ }
+ mq->dw7_val = cpu_to_le32(SLI4_CREATE_MQEXT_VAL);
+
+ mq->dw6w1_flags = cpu_to_le16(dw6w1_flags);
+ for (p = 0, addr = qmem->phys; p < num_pages;
+ p++, addr += SLI_PAGE_SIZE) {
+ mq->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr));
+ mq->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr));
+ }
+
+ return 0;
+}
+
+int
+sli_cmd_wq_create(struct sli4 *sli4, void *buf, struct efc_dma *qmem, u16 cq_id)
+{
+ struct sli4_rqst_wq_create *wq;
+ u32 p;
+ uintptr_t addr;
+ u32 page_size = 0;
+ u32 n_wqe = 0;
+ u16 num_pages;
+
+ wq = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(wq_create),
+ NULL);
+ if (!wq)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&wq->hdr, SLI4_OPC_WQ_CREATE, SLI4_SUBSYSTEM_FC,
+ CMD_V1, SLI4_RQST_PYLD_LEN(wq_create));
+ n_wqe = qmem->size / sli4->wqe_size;
+
+ switch (qmem->size) {
+ case 4096:
+ case 8192:
+ case 16384:
+ case 32768:
+ page_size = SZ_4K;
+ break;
+ case 65536:
+ page_size = SZ_8K;
+ break;
+ case 131072:
+ page_size = SZ_16K;
+ break;
+ case 262144:
+ page_size = SZ_32K;
+ break;
+ case 524288:
+ page_size = SZ_64K;
+ break;
+ default:
+ return -EIO;
+ }
+
+ /* valid values for number of pages(num_pages): 1-8 */
+ num_pages = sli_page_count(qmem->size, page_size);
+ wq->num_pages = cpu_to_le16(num_pages);
+ if (!num_pages || num_pages > SLI4_WQ_CREATE_MAX_PAGES)
+ return -EIO;
+
+ wq->cq_id = cpu_to_le16(cq_id);
+
+ wq->page_size = page_size / SLI_PAGE_SIZE;
+
+ if (sli4->wqe_size == SLI4_WQE_EXT_BYTES)
+ wq->wqe_size_byte |= SLI4_WQE_EXT_SIZE;
+ else
+ wq->wqe_size_byte |= SLI4_WQE_SIZE;
+
+ wq->wqe_count = cpu_to_le16(n_wqe);
+
+ for (p = 0, addr = qmem->phys; p < num_pages; p++, addr += page_size) {
+ wq->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr));
+ wq->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr));
+ }
+
+ return 0;
+}
+
+static int
+sli_cmd_rq_create_v1(struct sli4 *sli4, void *buf, struct efc_dma *qmem,
+ u16 cq_id, u16 buffer_size)
+{
+ struct sli4_rqst_rq_create_v1 *rq;
+ u32 p;
+ uintptr_t addr;
+ u32 num_pages;
+
+ rq = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(rq_create_v1),
+ NULL);
+ if (!rq)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&rq->hdr, SLI4_OPC_RQ_CREATE, SLI4_SUBSYSTEM_FC,
+ CMD_V1, SLI4_RQST_PYLD_LEN(rq_create_v1));
+ /* Disable "no buffer warnings" to avoid Lancer bug */
+ rq->dim_dfd_dnb |= SLI4_RQ_CREATE_V1_DNB;
+
+ /* valid values for number of pages: 1-8 (sec 4.5.6) */
+ num_pages = sli_page_count(qmem->size, SLI_PAGE_SIZE);
+ rq->num_pages = cpu_to_le16(num_pages);
+ if (!num_pages ||
+ num_pages > SLI4_RQ_CREATE_V1_MAX_PAGES) {
+ efc_log_info(sli4, "num_pages %d not valid, max %d\n",
+ num_pages, SLI4_RQ_CREATE_V1_MAX_PAGES);
+ return -EIO;
+ }
+
+ /*
+ * RQE count is the total number of entries (note not lg2(# entries))
+ */
+ rq->rqe_count = cpu_to_le16(qmem->size / SLI4_RQE_SIZE);
+
+ rq->rqe_size_byte |= SLI4_RQE_SIZE_8;
+
+ rq->page_size = SLI4_RQ_PAGE_SIZE_4096;
+
+ if (buffer_size < sli4->rq_min_buf_size ||
+ buffer_size > sli4->rq_max_buf_size) {
+ efc_log_err(sli4, "buffer_size %d out of range (%d-%d)\n",
+ buffer_size, sli4->rq_min_buf_size,
+ sli4->rq_max_buf_size);
+ return -EIO;
+ }
+ rq->buffer_size = cpu_to_le32(buffer_size);
+
+ rq->cq_id = cpu_to_le16(cq_id);
+
+ for (p = 0, addr = qmem->phys;
+ p < num_pages;
+ p++, addr += SLI_PAGE_SIZE) {
+ rq->page_phys_addr[p].low = cpu_to_le32(lower_32_bits(addr));
+ rq->page_phys_addr[p].high = cpu_to_le32(upper_32_bits(addr));
+ }
+
+ return 0;
+}
+
+static int
+sli_cmd_rq_create_v2(struct sli4 *sli4, u32 num_rqs,
+ struct sli4_queue *qs[], u32 base_cq_id,
+ u32 header_buffer_size,
+ u32 payload_buffer_size, struct efc_dma *dma)
+{
+ struct sli4_rqst_rq_create_v2 *req = NULL;
+ u32 i, p, offset = 0;
+ u32 payload_size, page_count;
+ uintptr_t addr;
+ u32 num_pages;
+ __le32 len;
+
+ page_count = sli_page_count(qs[0]->dma.size, SLI_PAGE_SIZE) * num_rqs;
+
+ /* Payload length must accommodate both request and response */
+ payload_size = max(SLI4_RQST_CMDSZ(rq_create_v2) +
+ SZ_DMAADDR * page_count,
+ sizeof(struct sli4_rsp_cmn_create_queue_set));
+
+ dma->size = payload_size;
+ dma->virt = dma_alloc_coherent(&sli4->pci->dev, dma->size,
+ &dma->phys, GFP_KERNEL);
+ if (!dma->virt)
+ return -EIO;
+
+ memset(dma->virt, 0, payload_size);
+
+ req = sli_config_cmd_init(sli4, sli4->bmbx.virt, payload_size, dma);
+ if (!req)
+ return -EIO;
+
+ len = SLI4_RQST_PYLD_LEN_VAR(rq_create_v2, SZ_DMAADDR * page_count);
+ sli_cmd_fill_hdr(&req->hdr, SLI4_OPC_RQ_CREATE, SLI4_SUBSYSTEM_FC,
+ CMD_V2, len);
+ /* Fill Payload fields */
+ req->dim_dfd_dnb |= SLI4_RQCREATEV2_DNB;
+ num_pages = sli_page_count(qs[0]->dma.size, SLI_PAGE_SIZE);
+ req->num_pages = cpu_to_le16(num_pages);
+ req->rqe_count = cpu_to_le16(qs[0]->dma.size / SLI4_RQE_SIZE);
+ req->rqe_size_byte |= SLI4_RQE_SIZE_8;
+ req->page_size = SLI4_RQ_PAGE_SIZE_4096;
+ req->rq_count = num_rqs;
+ req->base_cq_id = cpu_to_le16(base_cq_id);
+ req->hdr_buffer_size = cpu_to_le16(header_buffer_size);
+ req->payload_buffer_size = cpu_to_le16(payload_buffer_size);
+
+ for (i = 0; i < num_rqs; i++) {
+ for (p = 0, addr = qs[i]->dma.phys; p < num_pages;
+ p++, addr += SLI_PAGE_SIZE) {
+ req->page_phys_addr[offset].low =
+ cpu_to_le32(lower_32_bits(addr));
+ req->page_phys_addr[offset].high =
+ cpu_to_le32(upper_32_bits(addr));
+ offset++;
+ }
+ }
+
+ return 0;
+}
+
+static void
+__sli_queue_destroy(struct sli4 *sli4, struct sli4_queue *q)
+{
+ if (!q->dma.size)
+ return;
+
+ dma_free_coherent(&sli4->pci->dev, q->dma.size,
+ q->dma.virt, q->dma.phys);
+ memset(&q->dma, 0, sizeof(struct efc_dma));
+}
+
+int
+__sli_queue_init(struct sli4 *sli4, struct sli4_queue *q, u32 qtype,
+ size_t size, u32 n_entries, u32 align)
+{
+ if (q->dma.virt) {
+ efc_log_err(sli4, "%s failed\n", __func__);
+ return -EIO;
+ }
+
+ memset(q, 0, sizeof(struct sli4_queue));
+
+ q->dma.size = size * n_entries;
+ q->dma.virt = dma_alloc_coherent(&sli4->pci->dev, q->dma.size,
+ &q->dma.phys, GFP_KERNEL);
+ if (!q->dma.virt) {
+ memset(&q->dma, 0, sizeof(struct efc_dma));
+ efc_log_err(sli4, "%s allocation failed\n", SLI4_QNAME[qtype]);
+ return -EIO;
+ }
+
+ memset(q->dma.virt, 0, size * n_entries);
+
+ spin_lock_init(&q->lock);
+
+ q->type = qtype;
+ q->size = size;
+ q->length = n_entries;
+
+ if (q->type == SLI4_QTYPE_EQ || q->type == SLI4_QTYPE_CQ) {
+ /* For prism, phase will be flipped after
+ * a sweep through eq and cq
+ */
+ q->phase = 1;
+ }
+
+ /* Limit to hwf the queue size per interrupt */
+ q->proc_limit = n_entries / 2;
+
+ if (q->type == SLI4_QTYPE_EQ)
+ q->posted_limit = q->length / 2;
+ else
+ q->posted_limit = 64;
+
+ return 0;
+}
+
+int
+sli_fc_rq_alloc(struct sli4 *sli4, struct sli4_queue *q,
+ u32 n_entries, u32 buffer_size,
+ struct sli4_queue *cq, bool is_hdr)
+{
+ if (__sli_queue_init(sli4, q, SLI4_QTYPE_RQ, SLI4_RQE_SIZE,
+ n_entries, SLI_PAGE_SIZE))
+ return -EIO;
+
+ if (sli_cmd_rq_create_v1(sli4, sli4->bmbx.virt, &q->dma, cq->id,
+ buffer_size))
+ goto error;
+
+ if (__sli_create_queue(sli4, q))
+ goto error;
+
+ if (is_hdr && q->id & 1) {
+ efc_log_info(sli4, "bad header RQ_ID %d\n", q->id);
+ goto error;
+ } else if (!is_hdr && (q->id & 1) == 0) {
+ efc_log_info(sli4, "bad data RQ_ID %d\n", q->id);
+ goto error;
+ }
+
+ if (is_hdr)
+ q->u.flag |= SLI4_QUEUE_FLAG_HDR;
+ else
+ q->u.flag &= ~SLI4_QUEUE_FLAG_HDR;
+
+ return 0;
+
+error:
+ __sli_queue_destroy(sli4, q);
+ return -EIO;
+}
+
+int
+sli_fc_rq_set_alloc(struct sli4 *sli4, u32 num_rq_pairs,
+ struct sli4_queue *qs[], u32 base_cq_id,
+ u32 n_entries, u32 header_buffer_size,
+ u32 payload_buffer_size)
+{
+ u32 i;
+ struct efc_dma dma = {0};
+ struct sli4_rsp_cmn_create_queue_set *rsp = NULL;
+ void __iomem *db_regaddr = NULL;
+ u32 num_rqs = num_rq_pairs * 2;
+
+ for (i = 0; i < num_rqs; i++) {
+ if (__sli_queue_init(sli4, qs[i], SLI4_QTYPE_RQ,
+ SLI4_RQE_SIZE, n_entries,
+ SLI_PAGE_SIZE)) {
+ goto error;
+ }
+ }
+
+ if (sli_cmd_rq_create_v2(sli4, num_rqs, qs, base_cq_id,
+ header_buffer_size, payload_buffer_size,
+ &dma)) {
+ goto error;
+ }
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_err(sli4, "bootstrap mailbox write failed RQSet\n");
+ goto error;
+ }
+
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ db_regaddr = sli4->reg[1] + SLI4_IF6_RQ_DB_REG;
+ else
+ db_regaddr = sli4->reg[0] + SLI4_RQ_DB_REG;
+
+ rsp = dma.virt;
+ if (rsp->hdr.status) {
+ efc_log_err(sli4, "bad create RQSet status=%#x addl=%#x\n",
+ rsp->hdr.status, rsp->hdr.additional_status);
+ goto error;
+ }
+
+ for (i = 0; i < num_rqs; i++) {
+ qs[i]->id = i + le16_to_cpu(rsp->q_id);
+ if ((qs[i]->id & 1) == 0)
+ qs[i]->u.flag |= SLI4_QUEUE_FLAG_HDR;
+ else
+ qs[i]->u.flag &= ~SLI4_QUEUE_FLAG_HDR;
+
+ qs[i]->db_regaddr = db_regaddr;
+ }
+
+ dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt, dma.phys);
+
+ return 0;
+
+error:
+ for (i = 0; i < num_rqs; i++)
+ __sli_queue_destroy(sli4, qs[i]);
+
+ if (dma.virt)
+ dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt,
+ dma.phys);
+
+ return -EIO;
+}
+
+static int
+sli_res_sli_config(struct sli4 *sli4, void *buf)
+{
+ struct sli4_cmd_sli_config *sli_config = buf;
+
+ /* sanity check */
+ if (!buf || sli_config->hdr.command !=
+ SLI4_MBX_CMD_SLI_CONFIG) {
+ efc_log_err(sli4, "bad parameter buf=%p cmd=%#x\n", buf,
+ buf ? sli_config->hdr.command : -1);
+ return -EIO;
+ }
+
+ if (le16_to_cpu(sli_config->hdr.status))
+ return le16_to_cpu(sli_config->hdr.status);
+
+ if (le32_to_cpu(sli_config->dw1_flags) & SLI4_SLICONF_EMB)
+ return sli_config->payload.embed[4];
+
+ efc_log_info(sli4, "external buffers not supported\n");
+ return -EIO;
+}
+
+int
+__sli_create_queue(struct sli4 *sli4, struct sli4_queue *q)
+{
+ struct sli4_rsp_cmn_create_queue *res_q = NULL;
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox write fail %s\n",
+ SLI4_QNAME[q->type]);
+ return -EIO;
+ }
+ if (sli_res_sli_config(sli4, sli4->bmbx.virt)) {
+ efc_log_err(sli4, "bad status create %s\n",
+ SLI4_QNAME[q->type]);
+ return -EIO;
+ }
+ res_q = (void *)((u8 *)sli4->bmbx.virt +
+ offsetof(struct sli4_cmd_sli_config, payload));
+
+ if (res_q->hdr.status) {
+ efc_log_err(sli4, "bad create %s status=%#x addl=%#x\n",
+ SLI4_QNAME[q->type], res_q->hdr.status,
+ res_q->hdr.additional_status);
+ return -EIO;
+ }
+ q->id = le16_to_cpu(res_q->q_id);
+ switch (q->type) {
+ case SLI4_QTYPE_EQ:
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ q->db_regaddr = sli4->reg[1] + SLI4_IF6_EQ_DB_REG;
+ else
+ q->db_regaddr = sli4->reg[0] + SLI4_EQCQ_DB_REG;
+ break;
+ case SLI4_QTYPE_CQ:
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ q->db_regaddr = sli4->reg[1] + SLI4_IF6_CQ_DB_REG;
+ else
+ q->db_regaddr = sli4->reg[0] + SLI4_EQCQ_DB_REG;
+ break;
+ case SLI4_QTYPE_MQ:
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ q->db_regaddr = sli4->reg[1] + SLI4_IF6_MQ_DB_REG;
+ else
+ q->db_regaddr = sli4->reg[0] + SLI4_MQ_DB_REG;
+ break;
+ case SLI4_QTYPE_RQ:
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ q->db_regaddr = sli4->reg[1] + SLI4_IF6_RQ_DB_REG;
+ else
+ q->db_regaddr = sli4->reg[0] + SLI4_RQ_DB_REG;
+ break;
+ case SLI4_QTYPE_WQ:
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ q->db_regaddr = sli4->reg[1] + SLI4_IF6_WQ_DB_REG;
+ else
+ q->db_regaddr = sli4->reg[0] + SLI4_IO_WQ_DB_REG;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int
+sli_get_queue_entry_size(struct sli4 *sli4, u32 qtype)
+{
+ u32 size = 0;
+
+ switch (qtype) {
+ case SLI4_QTYPE_EQ:
+ size = sizeof(u32);
+ break;
+ case SLI4_QTYPE_CQ:
+ size = 16;
+ break;
+ case SLI4_QTYPE_MQ:
+ size = 256;
+ break;
+ case SLI4_QTYPE_WQ:
+ size = sli4->wqe_size;
+ break;
+ case SLI4_QTYPE_RQ:
+ size = SLI4_RQE_SIZE;
+ break;
+ default:
+ efc_log_info(sli4, "unknown queue type %d\n", qtype);
+ return -1;
+ }
+ return size;
+}
+
+int
+sli_queue_alloc(struct sli4 *sli4, u32 qtype,
+ struct sli4_queue *q, u32 n_entries,
+ struct sli4_queue *assoc)
+{
+ int size;
+ u32 align = 0;
+
+ /* get queue size */
+ size = sli_get_queue_entry_size(sli4, qtype);
+ if (size < 0)
+ return -EIO;
+ align = SLI_PAGE_SIZE;
+
+ if (__sli_queue_init(sli4, q, qtype, size, n_entries, align))
+ return -EIO;
+
+ switch (qtype) {
+ case SLI4_QTYPE_EQ:
+ if (!sli_cmd_common_create_eq(sli4, sli4->bmbx.virt, &q->dma) &&
+ !__sli_create_queue(sli4, q))
+ return 0;
+
+ break;
+ case SLI4_QTYPE_CQ:
+ if (!sli_cmd_common_create_cq(sli4, sli4->bmbx.virt, &q->dma,
+ assoc ? assoc->id : 0) &&
+ !__sli_create_queue(sli4, q))
+ return 0;
+
+ break;
+ case SLI4_QTYPE_MQ:
+ assoc->u.flag |= SLI4_QUEUE_FLAG_MQ;
+ if (!sli_cmd_common_create_mq_ext(sli4, sli4->bmbx.virt,
+ &q->dma, assoc->id) &&
+ !__sli_create_queue(sli4, q))
+ return 0;
+
+ break;
+ case SLI4_QTYPE_WQ:
+ if (!sli_cmd_wq_create(sli4, sli4->bmbx.virt, &q->dma,
+ assoc ? assoc->id : 0) &&
+ !__sli_create_queue(sli4, q))
+ return 0;
+
+ break;
+ default:
+ efc_log_info(sli4, "unknown queue type %d\n", qtype);
+ }
+
+ __sli_queue_destroy(sli4, q);
+ return -EIO;
+}
+
+static int sli_cmd_cq_set_create(struct sli4 *sli4,
+ struct sli4_queue *qs[], u32 num_cqs,
+ struct sli4_queue *eqs[],
+ struct efc_dma *dma)
+{
+ struct sli4_rqst_cmn_create_cq_set_v0 *req = NULL;
+ uintptr_t addr;
+ u32 i, offset = 0, page_bytes = 0, payload_size;
+ u32 p = 0, page_size = 0, n_cqe = 0, num_pages_cq;
+ u32 dw5_flags = 0;
+ u16 dw6w1_flags = 0;
+ __le32 req_len;
+
+ n_cqe = qs[0]->dma.size / SLI4_CQE_BYTES;
+ switch (n_cqe) {
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ page_size = 1;
+ break;
+ case 4096:
+ page_size = 2;
+ break;
+ default:
+ return -EIO;
+ }
+
+ page_bytes = page_size * SLI_PAGE_SIZE;
+ num_pages_cq = sli_page_count(qs[0]->dma.size, page_bytes);
+ payload_size = max(SLI4_RQST_CMDSZ(cmn_create_cq_set_v0) +
+ (SZ_DMAADDR * num_pages_cq * num_cqs),
+ sizeof(struct sli4_rsp_cmn_create_queue_set));
+
+ dma->size = payload_size;
+ dma->virt = dma_alloc_coherent(&sli4->pci->dev, dma->size,
+ &dma->phys, GFP_KERNEL);
+ if (!dma->virt)
+ return -EIO;
+
+ memset(dma->virt, 0, payload_size);
+
+ req = sli_config_cmd_init(sli4, sli4->bmbx.virt, payload_size, dma);
+ if (!req)
+ return -EIO;
+
+ req_len = SLI4_RQST_PYLD_LEN_VAR(cmn_create_cq_set_v0,
+ SZ_DMAADDR * num_pages_cq * num_cqs);
+ sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_CREATE_CQ_SET, SLI4_SUBSYSTEM_FC,
+ CMD_V0, req_len);
+ req->page_size = page_size;
+
+ req->num_pages = cpu_to_le16(num_pages_cq);
+ switch (num_pages_cq) {
+ case 1:
+ dw5_flags |= SLI4_CQ_CNT_VAL(256);
+ break;
+ case 2:
+ dw5_flags |= SLI4_CQ_CNT_VAL(512);
+ break;
+ case 4:
+ dw5_flags |= SLI4_CQ_CNT_VAL(1024);
+ break;
+ case 8:
+ dw5_flags |= SLI4_CQ_CNT_VAL(LARGE);
+ dw6w1_flags |= (n_cqe & SLI4_CREATE_CQSETV0_CQE_COUNT);
+ break;
+ default:
+ efc_log_info(sli4, "num_pages %d not valid\n", num_pages_cq);
+ return -EIO;
+ }
+
+ dw5_flags |= SLI4_CREATE_CQSETV0_EVT;
+ dw5_flags |= SLI4_CREATE_CQSETV0_VALID;
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ dw5_flags |= SLI4_CREATE_CQSETV0_AUTOVALID;
+
+ dw6w1_flags &= ~SLI4_CREATE_CQSETV0_ARM;
+
+ req->dw5_flags = cpu_to_le32(dw5_flags);
+ req->dw6w1_flags = cpu_to_le16(dw6w1_flags);
+
+ req->num_cq_req = cpu_to_le16(num_cqs);
+
+ /* Fill page addresses of all the CQs. */
+ for (i = 0; i < num_cqs; i++) {
+ req->eq_id[i] = cpu_to_le16(eqs[i]->id);
+ for (p = 0, addr = qs[i]->dma.phys; p < num_pages_cq;
+ p++, addr += page_bytes) {
+ req->page_phys_addr[offset].low =
+ cpu_to_le32(lower_32_bits(addr));
+ req->page_phys_addr[offset].high =
+ cpu_to_le32(upper_32_bits(addr));
+ offset++;
+ }
+ }
+
+ return 0;
+}
+
+int
+sli_cq_alloc_set(struct sli4 *sli4, struct sli4_queue *qs[],
+ u32 num_cqs, u32 n_entries, struct sli4_queue *eqs[])
+{
+ u32 i;
+ struct efc_dma dma = {0};
+ struct sli4_rsp_cmn_create_queue_set *res;
+ void __iomem *db_regaddr;
+
+ /* Align the queue DMA memory */
+ for (i = 0; i < num_cqs; i++) {
+ if (__sli_queue_init(sli4, qs[i], SLI4_QTYPE_CQ, SLI4_CQE_BYTES,
+ n_entries, SLI_PAGE_SIZE))
+ goto error;
+ }
+
+ if (sli_cmd_cq_set_create(sli4, qs, num_cqs, eqs, &dma))
+ goto error;
+
+ if (sli_bmbx_command(sli4))
+ goto error;
+
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ db_regaddr = sli4->reg[1] + SLI4_IF6_CQ_DB_REG;
+ else
+ db_regaddr = sli4->reg[0] + SLI4_EQCQ_DB_REG;
+
+ res = dma.virt;
+ if (res->hdr.status) {
+ efc_log_err(sli4, "bad create CQSet status=%#x addl=%#x\n",
+ res->hdr.status, res->hdr.additional_status);
+ goto error;
+ }
+
+ /* Check if we got all requested CQs. */
+ if (le16_to_cpu(res->num_q_allocated) != num_cqs) {
+ efc_log_crit(sli4, "Requested count CQs doesn't match.\n");
+ goto error;
+ }
+ /* Fill the resp cq ids. */
+ for (i = 0; i < num_cqs; i++) {
+ qs[i]->id = le16_to_cpu(res->q_id) + i;
+ qs[i]->db_regaddr = db_regaddr;
+ }
+
+ dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt, dma.phys);
+
+ return 0;
+
+error:
+ for (i = 0; i < num_cqs; i++)
+ __sli_queue_destroy(sli4, qs[i]);
+
+ if (dma.virt)
+ dma_free_coherent(&sli4->pci->dev, dma.size, dma.virt,
+ dma.phys);
+
+ return -EIO;
+}
+
+static int
+sli_cmd_common_destroy_q(struct sli4 *sli4, u8 opc, u8 subsystem, u16 q_id)
+{
+ struct sli4_rqst_cmn_destroy_q *req;
+
+ /* Payload length must accommodate both request and response */
+ req = sli_config_cmd_init(sli4, sli4->bmbx.virt,
+ SLI4_CFG_PYLD_LENGTH(cmn_destroy_q), NULL);
+ if (!req)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&req->hdr, opc, subsystem,
+ CMD_V0, SLI4_RQST_PYLD_LEN(cmn_destroy_q));
+ req->q_id = cpu_to_le16(q_id);
+
+ return 0;
+}
+
+int
+sli_queue_free(struct sli4 *sli4, struct sli4_queue *q,
+ u32 destroy_queues, u32 free_memory)
+{
+ int rc = 0;
+ u8 opcode, subsystem;
+ struct sli4_rsp_hdr *res;
+
+ if (!q) {
+ efc_log_err(sli4, "bad parameter sli4=%p q=%p\n", sli4, q);
+ return -EIO;
+ }
+
+ if (!destroy_queues)
+ goto free_mem;
+
+ switch (q->type) {
+ case SLI4_QTYPE_EQ:
+ opcode = SLI4_CMN_DESTROY_EQ;
+ subsystem = SLI4_SUBSYSTEM_COMMON;
+ break;
+ case SLI4_QTYPE_CQ:
+ opcode = SLI4_CMN_DESTROY_CQ;
+ subsystem = SLI4_SUBSYSTEM_COMMON;
+ break;
+ case SLI4_QTYPE_MQ:
+ opcode = SLI4_CMN_DESTROY_MQ;
+ subsystem = SLI4_SUBSYSTEM_COMMON;
+ break;
+ case SLI4_QTYPE_WQ:
+ opcode = SLI4_OPC_WQ_DESTROY;
+ subsystem = SLI4_SUBSYSTEM_FC;
+ break;
+ case SLI4_QTYPE_RQ:
+ opcode = SLI4_OPC_RQ_DESTROY;
+ subsystem = SLI4_SUBSYSTEM_FC;
+ break;
+ default:
+ efc_log_info(sli4, "bad queue type %d\n", q->type);
+ rc = -EIO;
+ goto free_mem;
+ }
+
+ rc = sli_cmd_common_destroy_q(sli4, opcode, subsystem, q->id);
+ if (rc)
+ goto free_mem;
+
+ rc = sli_bmbx_command(sli4);
+ if (rc)
+ goto free_mem;
+
+ rc = sli_res_sli_config(sli4, sli4->bmbx.virt);
+ if (rc)
+ goto free_mem;
+
+ res = (void *)((u8 *)sli4->bmbx.virt +
+ offsetof(struct sli4_cmd_sli_config, payload));
+ if (res->status) {
+ efc_log_err(sli4, "destroy %s st=%#x addl=%#x\n",
+ SLI4_QNAME[q->type], res->status,
+ res->additional_status);
+ rc = -EIO;
+ goto free_mem;
+ }
+
+free_mem:
+ if (free_memory)
+ __sli_queue_destroy(sli4, q);
+
+ return rc;
+}
+
+int
+sli_queue_eq_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm)
+{
+ u32 val;
+ unsigned long flags = 0;
+ u32 a = arm ? SLI4_EQCQ_ARM : SLI4_EQCQ_UNARM;
+
+ spin_lock_irqsave(&q->lock, flags);
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ val = sli_format_if6_eq_db_data(q->n_posted, q->id, a);
+ else
+ val = sli_format_eq_db_data(q->n_posted, q->id, a);
+
+ writel(val, q->db_regaddr);
+ q->n_posted = 0;
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return 0;
+}
+
+int
+sli_queue_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm)
+{
+ u32 val = 0;
+ unsigned long flags = 0;
+ u32 a = arm ? SLI4_EQCQ_ARM : SLI4_EQCQ_UNARM;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ switch (q->type) {
+ case SLI4_QTYPE_EQ:
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ val = sli_format_if6_eq_db_data(q->n_posted, q->id, a);
+ else
+ val = sli_format_eq_db_data(q->n_posted, q->id, a);
+
+ writel(val, q->db_regaddr);
+ q->n_posted = 0;
+ break;
+ case SLI4_QTYPE_CQ:
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6)
+ val = sli_format_if6_cq_db_data(q->n_posted, q->id, a);
+ else
+ val = sli_format_cq_db_data(q->n_posted, q->id, a);
+
+ writel(val, q->db_regaddr);
+ q->n_posted = 0;
+ break;
+ default:
+ efc_log_info(sli4, "should only be used for EQ/CQ, not %s\n",
+ SLI4_QNAME[q->type]);
+ }
+
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return 0;
+}
+
+int
+sli_wq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry)
+{
+ u8 *qe = q->dma.virt;
+ u32 qindex;
+ u32 val = 0;
+
+ qindex = q->index;
+ qe += q->index * q->size;
+
+ if (sli4->params.perf_wq_id_association)
+ sli_set_wq_id_association(entry, q->id);
+
+ memcpy(qe, entry, q->size);
+ val = sli_format_wq_db_data(q->id);
+
+ writel(val, q->db_regaddr);
+ q->index = (q->index + 1) & (q->length - 1);
+
+ return qindex;
+}
+
+int
+sli_mq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry)
+{
+ u8 *qe = q->dma.virt;
+ u32 qindex;
+ u32 val = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q->lock, flags);
+ qindex = q->index;
+ qe += q->index * q->size;
+
+ memcpy(qe, entry, q->size);
+ val = sli_format_mq_db_data(q->id);
+ writel(val, q->db_regaddr);
+ q->index = (q->index + 1) & (q->length - 1);
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return qindex;
+}
+
+int
+sli_rq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry)
+{
+ u8 *qe = q->dma.virt;
+ u32 qindex;
+ u32 val = 0;
+
+ qindex = q->index;
+ qe += q->index * q->size;
+
+ memcpy(qe, entry, q->size);
+
+ /*
+ * In RQ-pair, an RQ either contains the FC header
+ * (i.e. is_hdr == TRUE) or the payload.
+ *
+ * Don't ring doorbell for payload RQ
+ */
+ if (!(q->u.flag & SLI4_QUEUE_FLAG_HDR))
+ goto skip;
+
+ val = sli_format_rq_db_data(q->id);
+ writel(val, q->db_regaddr);
+skip:
+ q->index = (q->index + 1) & (q->length - 1);
+
+ return qindex;
+}
+
+int
+sli_eq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry)
+{
+ u8 *qe = q->dma.virt;
+ unsigned long flags = 0;
+ u16 wflags = 0;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ qe += q->index * q->size;
+
+ /* Check if eqe is valid */
+ wflags = le16_to_cpu(((struct sli4_eqe *)qe)->dw0w0_flags);
+
+ if ((wflags & SLI4_EQE_VALID) != q->phase) {
+ spin_unlock_irqrestore(&q->lock, flags);
+ return -EIO;
+ }
+
+ if (sli4->if_type != SLI4_INTF_IF_TYPE_6) {
+ wflags &= ~SLI4_EQE_VALID;
+ ((struct sli4_eqe *)qe)->dw0w0_flags = cpu_to_le16(wflags);
+ }
+
+ memcpy(entry, qe, q->size);
+ q->index = (q->index + 1) & (q->length - 1);
+ q->n_posted++;
+ /*
+ * For prism, the phase value will be used
+ * to check the validity of eq/cq entries.
+ * The value toggles after a complete sweep
+ * through the queue.
+ */
+
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6 && q->index == 0)
+ q->phase ^= (u16)0x1;
+
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return 0;
+}
+
+int
+sli_cq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry)
+{
+ u8 *qe = q->dma.virt;
+ unsigned long flags = 0;
+ u32 dwflags = 0;
+ bool valid_bit_set;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ qe += q->index * q->size;
+
+ /* Check if cqe is valid */
+ dwflags = le32_to_cpu(((struct sli4_mcqe *)qe)->dw3_flags);
+ valid_bit_set = (dwflags & SLI4_MCQE_VALID) != 0;
+
+ if (valid_bit_set != q->phase) {
+ spin_unlock_irqrestore(&q->lock, flags);
+ return -EIO;
+ }
+
+ if (sli4->if_type != SLI4_INTF_IF_TYPE_6) {
+ dwflags &= ~SLI4_MCQE_VALID;
+ ((struct sli4_mcqe *)qe)->dw3_flags = cpu_to_le32(dwflags);
+ }
+
+ memcpy(entry, qe, q->size);
+ q->index = (q->index + 1) & (q->length - 1);
+ q->n_posted++;
+ /*
+ * For prism, the phase value will be used
+ * to check the validity of eq/cq entries.
+ * The value toggles after a complete sweep
+ * through the queue.
+ */
+
+ if (sli4->if_type == SLI4_INTF_IF_TYPE_6 && q->index == 0)
+ q->phase ^= (u16)0x1;
+
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return 0;
+}
+
+int
+sli_mq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry)
+{
+ u8 *qe = q->dma.virt;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ qe += q->u.r_idx * q->size;
+
+ /* Check if mqe is valid */
+ if (q->index == q->u.r_idx) {
+ spin_unlock_irqrestore(&q->lock, flags);
+ return -EIO;
+ }
+
+ memcpy(entry, qe, q->size);
+ q->u.r_idx = (q->u.r_idx + 1) & (q->length - 1);
+
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return 0;
+}
+
+int
+sli_eq_parse(struct sli4 *sli4, u8 *buf, u16 *cq_id)
+{
+ struct sli4_eqe *eqe = (void *)buf;
+ int rc = 0;
+ u16 flags = 0;
+ u16 majorcode;
+ u16 minorcode;
+
+ if (!buf || !cq_id) {
+ efc_log_err(sli4, "bad parameters sli4=%p buf=%p cq_id=%p\n",
+ sli4, buf, cq_id);
+ return -EIO;
+ }
+
+ flags = le16_to_cpu(eqe->dw0w0_flags);
+ majorcode = (flags & SLI4_EQE_MJCODE) >> 1;
+ minorcode = (flags & SLI4_EQE_MNCODE) >> 4;
+ switch (majorcode) {
+ case SLI4_MAJOR_CODE_STANDARD:
+ *cq_id = le16_to_cpu(eqe->resource_id);
+ break;
+ case SLI4_MAJOR_CODE_SENTINEL:
+ efc_log_info(sli4, "sentinel EQE\n");
+ rc = SLI4_EQE_STATUS_EQ_FULL;
+ break;
+ default:
+ efc_log_info(sli4, "Unsupported EQE: major %x minor %x\n",
+ majorcode, minorcode);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+int
+sli_cq_parse(struct sli4 *sli4, struct sli4_queue *cq, u8 *cqe,
+ enum sli4_qentry *etype, u16 *q_id)
+{
+ int rc = 0;
+
+ if (!cq || !cqe || !etype) {
+ efc_log_err(sli4, "bad params sli4=%p cq=%p cqe=%p etype=%p q_id=%p\n",
+ sli4, cq, cqe, etype, q_id);
+ return -EINVAL;
+ }
+
+ /* Parse a CQ entry to retrieve the event type and the queue id */
+ if (cq->u.flag & SLI4_QUEUE_FLAG_MQ) {
+ struct sli4_mcqe *mcqe = (void *)cqe;
+
+ if (le32_to_cpu(mcqe->dw3_flags) & SLI4_MCQE_AE) {
+ *etype = SLI4_QENTRY_ASYNC;
+ } else {
+ *etype = SLI4_QENTRY_MQ;
+ rc = sli_cqe_mq(sli4, mcqe);
+ }
+ *q_id = -1;
+ } else {
+ rc = sli_fc_cqe_parse(sli4, cq, cqe, etype, q_id);
+ }
+
+ return rc;
+}
+
+int
+sli_abort_wqe(struct sli4 *sli, void *buf, enum sli4_abort_type type,
+ bool send_abts, u32 ids, u32 mask, u16 tag, u16 cq_id)
+{
+ struct sli4_abort_wqe *abort = buf;
+
+ memset(buf, 0, sli->wqe_size);
+
+ switch (type) {
+ case SLI4_ABORT_XRI:
+ abort->criteria = SLI4_ABORT_CRITERIA_XRI_TAG;
+ if (mask) {
+ efc_log_warn(sli, "%#x aborting XRI %#x warning non-zero mask",
+ mask, ids);
+ mask = 0;
+ }
+ break;
+ case SLI4_ABORT_ABORT_ID:
+ abort->criteria = SLI4_ABORT_CRITERIA_ABORT_TAG;
+ break;
+ case SLI4_ABORT_REQUEST_ID:
+ abort->criteria = SLI4_ABORT_CRITERIA_REQUEST_TAG;
+ break;
+ default:
+ efc_log_info(sli, "unsupported type %#x\n", type);
+ return -EIO;
+ }
+
+ abort->ia_ir_byte |= send_abts ? 0 : 1;
+
+ /* Suppress ABTS retries */
+ abort->ia_ir_byte |= SLI4_ABRT_WQE_IR;
+
+ abort->t_mask = cpu_to_le32(mask);
+ abort->t_tag = cpu_to_le32(ids);
+ abort->command = SLI4_WQE_ABORT;
+ abort->request_tag = cpu_to_le16(tag);
+
+ abort->dw10w0_flags = cpu_to_le16(SLI4_ABRT_WQE_QOSD);
+
+ abort->cq_id = cpu_to_le16(cq_id);
+ abort->cmdtype_wqec_byte |= SLI4_CMD_ABORT_WQE;
+
+ return 0;
+}
+
+int
+sli_els_request64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl,
+ struct sli_els_params *params)
+{
+ struct sli4_els_request64_wqe *els = buf;
+ struct sli4_sge *sge = sgl->virt;
+ bool is_fabric = false;
+ struct sli4_bde *bptr;
+
+ memset(buf, 0, sli->wqe_size);
+
+ bptr = &els->els_request_payload;
+ if (sli->params.sgl_pre_registered) {
+ els->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_REQ_WQE_XBL;
+
+ els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_DBDE;
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (params->xmit_len & SLI4_BDE_LEN_MASK));
+
+ bptr->u.data.low = sge[0].buffer_address_low;
+ bptr->u.data.high = sge[0].buffer_address_high;
+ } else {
+ els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_XBL;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) |
+ ((2 * sizeof(struct sli4_sge)) &
+ SLI4_BDE_LEN_MASK));
+ bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys));
+ bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys));
+ }
+
+ els->els_request_payload_length = cpu_to_le32(params->xmit_len);
+ els->max_response_payload_length = cpu_to_le32(params->rsp_len);
+
+ els->xri_tag = cpu_to_le16(params->xri);
+ els->timer = params->timeout;
+ els->class_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+
+ els->command = SLI4_WQE_ELS_REQUEST64;
+
+ els->request_tag = cpu_to_le16(params->tag);
+
+ els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_IOD;
+
+ els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_QOSD;
+
+ /* figure out the ELS_ID value from the request buffer */
+
+ switch (params->cmd) {
+ case ELS_LOGO:
+ els->cmdtype_elsid_byte |=
+ SLI4_ELS_REQUEST64_LOGO << SLI4_REQ_WQE_ELSID_SHFT;
+ if (params->rpi_registered) {
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_REQ_WQE_CT_SHFT;
+ els->context_tag = cpu_to_le16(params->rpi);
+ } else {
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT;
+ els->context_tag = cpu_to_le16(params->vpi);
+ }
+ if (params->d_id == FC_FID_FLOGI)
+ is_fabric = true;
+ break;
+ case ELS_FDISC:
+ if (params->d_id == FC_FID_FLOGI)
+ is_fabric = true;
+ if (params->s_id == 0) {
+ els->cmdtype_elsid_byte |=
+ SLI4_ELS_REQUEST64_FDISC << SLI4_REQ_WQE_ELSID_SHFT;
+ is_fabric = true;
+ } else {
+ els->cmdtype_elsid_byte |=
+ SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT;
+ }
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT;
+ els->context_tag = cpu_to_le16(params->vpi);
+ els->sid_sp_dword |= cpu_to_le32(1 << SLI4_REQ_WQE_SP_SHFT);
+ break;
+ case ELS_FLOGI:
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT;
+ els->context_tag = cpu_to_le16(params->vpi);
+ /*
+ * Set SP here ... we haven't done a REG_VPI yet
+ * need to maybe not set this when we have
+ * completed VFI/VPI registrations ...
+ *
+ * Use the FC_ID of the SPORT if it has been allocated,
+ * otherwise use an S_ID of zero.
+ */
+ els->sid_sp_dword |= cpu_to_le32(1 << SLI4_REQ_WQE_SP_SHFT);
+ if (params->s_id != U32_MAX)
+ els->sid_sp_dword |= cpu_to_le32(params->s_id);
+ break;
+ case ELS_PLOGI:
+ els->cmdtype_elsid_byte |=
+ SLI4_ELS_REQUEST64_PLOGI << SLI4_REQ_WQE_ELSID_SHFT;
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT;
+ els->context_tag = cpu_to_le16(params->vpi);
+ break;
+ case ELS_SCR:
+ els->cmdtype_elsid_byte |=
+ SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT;
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT;
+ els->context_tag = cpu_to_le16(params->vpi);
+ break;
+ default:
+ els->cmdtype_elsid_byte |=
+ SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT;
+ if (params->rpi_registered) {
+ els->ct_byte |= (SLI4_GENERIC_CONTEXT_RPI <<
+ SLI4_REQ_WQE_CT_SHFT);
+ els->context_tag = cpu_to_le16(params->vpi);
+ } else {
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT;
+ els->context_tag = cpu_to_le16(params->vpi);
+ }
+ break;
+ }
+
+ if (is_fabric)
+ els->cmdtype_elsid_byte |= SLI4_ELS_REQUEST64_CMD_FABRIC;
+ else
+ els->cmdtype_elsid_byte |= SLI4_ELS_REQUEST64_CMD_NON_FABRIC;
+
+ els->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT);
+
+ if (((els->ct_byte & SLI4_REQ_WQE_CT) >> SLI4_REQ_WQE_CT_SHFT) !=
+ SLI4_GENERIC_CONTEXT_RPI)
+ els->remote_id_dword = cpu_to_le32(params->d_id);
+
+ if (((els->ct_byte & SLI4_REQ_WQE_CT) >> SLI4_REQ_WQE_CT_SHFT) ==
+ SLI4_GENERIC_CONTEXT_VPI)
+ els->temporary_rpi = cpu_to_le16(params->rpi);
+
+ return 0;
+}
+
+int
+sli_fcp_icmnd64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, u16 xri,
+ u16 tag, u16 cq_id, u32 rpi, u32 rnode_fcid, u8 timeout)
+{
+ struct sli4_fcp_icmnd64_wqe *icmnd = buf;
+ struct sli4_sge *sge = NULL;
+ struct sli4_bde *bptr;
+ u32 len;
+
+ memset(buf, 0, sli->wqe_size);
+
+ if (!sgl || !sgl->virt) {
+ efc_log_err(sli, "bad parameter sgl=%p virt=%p\n",
+ sgl, sgl ? sgl->virt : NULL);
+ return -EIO;
+ }
+ sge = sgl->virt;
+ bptr = &icmnd->bde;
+ if (sli->params.sgl_pre_registered) {
+ icmnd->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_ICMD_WQE_XBL;
+
+ icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_DBDE;
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[0].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+
+ bptr->u.data.low = sge[0].buffer_address_low;
+ bptr->u.data.high = sge[0].buffer_address_high;
+ } else {
+ icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_XBL;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) |
+ (sgl->size & SLI4_BDE_LEN_MASK));
+
+ bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys));
+ bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys));
+ }
+
+ len = le32_to_cpu(sge[0].buffer_length) +
+ le32_to_cpu(sge[1].buffer_length);
+ icmnd->payload_offset_length = cpu_to_le16(len);
+ icmnd->xri_tag = cpu_to_le16(xri);
+ icmnd->context_tag = cpu_to_le16(rpi);
+ icmnd->timer = timeout;
+
+ /* WQE word 4 contains read transfer length */
+ icmnd->class_pu_byte |= 2 << SLI4_ICMD_WQE_PU_SHFT;
+ icmnd->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+ icmnd->command = SLI4_WQE_FCP_ICMND64;
+ icmnd->dif_ct_bs_byte |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_ICMD_WQE_CT_SHFT;
+
+ icmnd->abort_tag = cpu_to_le32(xri);
+
+ icmnd->request_tag = cpu_to_le16(tag);
+ icmnd->len_loc1_byte |= SLI4_ICMD_WQE_LEN_LOC_BIT1;
+ icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_LEN_LOC_BIT2;
+ icmnd->cmd_type_byte |= SLI4_CMD_FCP_ICMND64_WQE;
+ icmnd->cq_id = cpu_to_le16(cq_id);
+
+ return 0;
+}
+
+int
+sli_fcp_iread64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u32 xfer_len, u16 xri, u16 tag,
+ u16 cq_id, u32 rpi, u32 rnode_fcid,
+ u8 dif, u8 bs, u8 timeout)
+{
+ struct sli4_fcp_iread64_wqe *iread = buf;
+ struct sli4_sge *sge = NULL;
+ struct sli4_bde *bptr;
+ u32 sge_flags, len;
+
+ memset(buf, 0, sli->wqe_size);
+
+ if (!sgl || !sgl->virt) {
+ efc_log_err(sli, "bad parameter sgl=%p virt=%p\n",
+ sgl, sgl ? sgl->virt : NULL);
+ return -EIO;
+ }
+
+ sge = sgl->virt;
+ bptr = &iread->bde;
+ if (sli->params.sgl_pre_registered) {
+ iread->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_IR_WQE_XBL;
+
+ iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_DBDE;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[0].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+
+ bptr->u.blp.low = sge[0].buffer_address_low;
+ bptr->u.blp.high = sge[0].buffer_address_high;
+ } else {
+ iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_XBL;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) |
+ (sgl->size & SLI4_BDE_LEN_MASK));
+
+ bptr->u.blp.low =
+ cpu_to_le32(lower_32_bits(sgl->phys));
+ bptr->u.blp.high =
+ cpu_to_le32(upper_32_bits(sgl->phys));
+
+ /*
+ * fill out fcp_cmnd buffer len and change resp buffer to be of
+ * type "skip" (note: response will still be written to sge[1]
+ * if necessary)
+ */
+ len = le32_to_cpu(sge[0].buffer_length);
+ iread->fcp_cmd_buffer_length = cpu_to_le16(len);
+
+ sge_flags = le32_to_cpu(sge[1].dw2_flags);
+ sge_flags &= (~SLI4_SGE_TYPE_MASK);
+ sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT);
+ sge[1].dw2_flags = cpu_to_le32(sge_flags);
+ }
+
+ len = le32_to_cpu(sge[0].buffer_length) +
+ le32_to_cpu(sge[1].buffer_length);
+ iread->payload_offset_length = cpu_to_le16(len);
+ iread->total_transfer_length = cpu_to_le32(xfer_len);
+
+ iread->xri_tag = cpu_to_le16(xri);
+ iread->context_tag = cpu_to_le16(rpi);
+
+ iread->timer = timeout;
+
+ /* WQE word 4 contains read transfer length */
+ iread->class_pu_byte |= 2 << SLI4_IR_WQE_PU_SHFT;
+ iread->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+ iread->command = SLI4_WQE_FCP_IREAD64;
+ iread->dif_ct_bs_byte |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_IR_WQE_CT_SHFT;
+ iread->dif_ct_bs_byte |= dif;
+ iread->dif_ct_bs_byte |= bs << SLI4_IR_WQE_BS_SHFT;
+
+ iread->abort_tag = cpu_to_le32(xri);
+
+ iread->request_tag = cpu_to_le16(tag);
+ iread->len_loc1_byte |= SLI4_IR_WQE_LEN_LOC_BIT1;
+ iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_LEN_LOC_BIT2;
+ iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_IOD;
+ iread->cmd_type_byte |= SLI4_CMD_FCP_IREAD64_WQE;
+ iread->cq_id = cpu_to_le16(cq_id);
+
+ if (sli->params.perf_hint) {
+ bptr = &iread->first_data_bde;
+ bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[first_data_sge].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+ bptr->u.data.low =
+ sge[first_data_sge].buffer_address_low;
+ bptr->u.data.high =
+ sge[first_data_sge].buffer_address_high;
+ }
+
+ return 0;
+}
+
+int
+sli_fcp_iwrite64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u32 xfer_len,
+ u32 first_burst, u16 xri, u16 tag,
+ u16 cq_id, u32 rpi,
+ u32 rnode_fcid,
+ u8 dif, u8 bs, u8 timeout)
+{
+ struct sli4_fcp_iwrite64_wqe *iwrite = buf;
+ struct sli4_sge *sge = NULL;
+ struct sli4_bde *bptr;
+ u32 sge_flags, min, len;
+
+ memset(buf, 0, sli->wqe_size);
+
+ if (!sgl || !sgl->virt) {
+ efc_log_err(sli, "bad parameter sgl=%p virt=%p\n",
+ sgl, sgl ? sgl->virt : NULL);
+ return -EIO;
+ }
+ sge = sgl->virt;
+ bptr = &iwrite->bde;
+ if (sli->params.sgl_pre_registered) {
+ iwrite->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_IWR_WQE_XBL;
+
+ iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_DBDE;
+ bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[0].buffer_length) & SLI4_BDE_LEN_MASK));
+ bptr->u.data.low = sge[0].buffer_address_low;
+ bptr->u.data.high = sge[0].buffer_address_high;
+ } else {
+ iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_XBL;
+
+ bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (sgl->size & SLI4_BDE_LEN_MASK));
+
+ bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys));
+ bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys));
+
+ /*
+ * fill out fcp_cmnd buffer len and change resp buffer to be of
+ * type "skip" (note: response will still be written to sge[1]
+ * if necessary)
+ */
+ len = le32_to_cpu(sge[0].buffer_length);
+ iwrite->fcp_cmd_buffer_length = cpu_to_le16(len);
+ sge_flags = le32_to_cpu(sge[1].dw2_flags);
+ sge_flags &= ~SLI4_SGE_TYPE_MASK;
+ sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT);
+ sge[1].dw2_flags = cpu_to_le32(sge_flags);
+ }
+
+ len = le32_to_cpu(sge[0].buffer_length) +
+ le32_to_cpu(sge[1].buffer_length);
+ iwrite->payload_offset_length = cpu_to_le16(len);
+ iwrite->total_transfer_length = cpu_to_le16(xfer_len);
+ min = (xfer_len < first_burst) ? xfer_len : first_burst;
+ iwrite->initial_transfer_length = cpu_to_le16(min);
+
+ iwrite->xri_tag = cpu_to_le16(xri);
+ iwrite->context_tag = cpu_to_le16(rpi);
+
+ iwrite->timer = timeout;
+ /* WQE word 4 contains read transfer length */
+ iwrite->class_pu_byte |= 2 << SLI4_IWR_WQE_PU_SHFT;
+ iwrite->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+ iwrite->command = SLI4_WQE_FCP_IWRITE64;
+ iwrite->dif_ct_bs_byte |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_IWR_WQE_CT_SHFT;
+ iwrite->dif_ct_bs_byte |= dif;
+ iwrite->dif_ct_bs_byte |= bs << SLI4_IWR_WQE_BS_SHFT;
+
+ iwrite->abort_tag = cpu_to_le32(xri);
+
+ iwrite->request_tag = cpu_to_le16(tag);
+ iwrite->len_loc1_byte |= SLI4_IWR_WQE_LEN_LOC_BIT1;
+ iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_LEN_LOC_BIT2;
+ iwrite->cmd_type_byte |= SLI4_CMD_FCP_IWRITE64_WQE;
+ iwrite->cq_id = cpu_to_le16(cq_id);
+
+ if (sli->params.perf_hint) {
+ bptr = &iwrite->first_data_bde;
+
+ bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[first_data_sge].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+
+ bptr->u.data.low = sge[first_data_sge].buffer_address_low;
+ bptr->u.data.high = sge[first_data_sge].buffer_address_high;
+ }
+
+ return 0;
+}
+
+int
+sli_fcp_treceive64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u16 cq_id, u8 dif, u8 bs,
+ struct sli_fcp_tgt_params *params)
+{
+ struct sli4_fcp_treceive64_wqe *trecv = buf;
+ struct sli4_fcp_128byte_wqe *trecv_128 = buf;
+ struct sli4_sge *sge = NULL;
+ struct sli4_bde *bptr;
+
+ memset(buf, 0, sli->wqe_size);
+
+ if (!sgl || !sgl->virt) {
+ efc_log_err(sli, "bad parameter sgl=%p virt=%p\n",
+ sgl, sgl ? sgl->virt : NULL);
+ return -EIO;
+ }
+ sge = sgl->virt;
+ bptr = &trecv->bde;
+ if (sli->params.sgl_pre_registered) {
+ trecv->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_TRCV_WQE_XBL;
+
+ trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_DBDE;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[0].buffer_length)
+ & SLI4_BDE_LEN_MASK));
+
+ bptr->u.data.low = sge[0].buffer_address_low;
+ bptr->u.data.high = sge[0].buffer_address_high;
+
+ trecv->payload_offset_length = sge[0].buffer_length;
+ } else {
+ trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_XBL;
+
+ /* if data is a single physical address, use a BDE */
+ if (!dif &&
+ params->xmit_len <= le32_to_cpu(sge[2].buffer_length)) {
+ trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_DBDE;
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[2].buffer_length)
+ & SLI4_BDE_LEN_MASK));
+
+ bptr->u.data.low = sge[2].buffer_address_low;
+ bptr->u.data.high = sge[2].buffer_address_high;
+ } else {
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) |
+ (sgl->size & SLI4_BDE_LEN_MASK));
+ bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys));
+ bptr->u.blp.high =
+ cpu_to_le32(upper_32_bits(sgl->phys));
+ }
+ }
+
+ trecv->relative_offset = cpu_to_le32(params->offset);
+
+ if (params->flags & SLI4_IO_CONTINUATION)
+ trecv->eat_xc_ccpe |= SLI4_TRCV_WQE_XC;
+
+ trecv->xri_tag = cpu_to_le16(params->xri);
+
+ trecv->context_tag = cpu_to_le16(params->rpi);
+
+ /* WQE uses relative offset */
+ trecv->class_ar_pu_byte |= 1 << SLI4_TRCV_WQE_PU_SHFT;
+
+ if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE)
+ trecv->class_ar_pu_byte |= SLI4_TRCV_WQE_AR;
+
+ trecv->command = SLI4_WQE_FCP_TRECEIVE64;
+ trecv->class_ar_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+ trecv->dif_ct_bs_byte |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_TRCV_WQE_CT_SHFT;
+ trecv->dif_ct_bs_byte |= bs << SLI4_TRCV_WQE_BS_SHFT;
+
+ trecv->remote_xid = cpu_to_le16(params->ox_id);
+
+ trecv->request_tag = cpu_to_le16(params->tag);
+
+ trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_IOD;
+
+ trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_LEN_LOC_BIT2;
+
+ trecv->cmd_type_byte |= SLI4_CMD_FCP_TRECEIVE64_WQE;
+
+ trecv->cq_id = cpu_to_le16(cq_id);
+
+ trecv->fcp_data_receive_length = cpu_to_le32(params->xmit_len);
+
+ if (sli->params.perf_hint) {
+ bptr = &trecv->first_data_bde;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[first_data_sge].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+ bptr->u.data.low = sge[first_data_sge].buffer_address_low;
+ bptr->u.data.high = sge[first_data_sge].buffer_address_high;
+ }
+
+ /* The upper 7 bits of csctl is the priority */
+ if (params->cs_ctl & SLI4_MASK_CCP) {
+ trecv->eat_xc_ccpe |= SLI4_TRCV_WQE_CCPE;
+ trecv->ccp = (params->cs_ctl & SLI4_MASK_CCP);
+ }
+
+ if (params->app_id && sli->wqe_size == SLI4_WQE_EXT_BYTES &&
+ !(trecv->eat_xc_ccpe & SLI4_TRSP_WQE_EAT)) {
+ trecv->lloc1_appid |= SLI4_TRCV_WQE_APPID;
+ trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_WQES;
+ trecv_128->dw[31] = params->app_id;
+ }
+ return 0;
+}
+
+int
+sli_fcp_cont_treceive64_wqe(struct sli4 *sli, void *buf,
+ struct efc_dma *sgl, u32 first_data_sge,
+ u16 sec_xri, u16 cq_id, u8 dif, u8 bs,
+ struct sli_fcp_tgt_params *params)
+{
+ int rc;
+
+ rc = sli_fcp_treceive64_wqe(sli, buf, sgl, first_data_sge,
+ cq_id, dif, bs, params);
+ if (!rc) {
+ struct sli4_fcp_treceive64_wqe *trecv = buf;
+
+ trecv->command = SLI4_WQE_FCP_CONT_TRECEIVE64;
+ trecv->dword5.sec_xri_tag = cpu_to_le16(sec_xri);
+ }
+ return rc;
+}
+
+int
+sli_fcp_trsp64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ u16 cq_id, u8 port_owned, struct sli_fcp_tgt_params *params)
+{
+ struct sli4_fcp_trsp64_wqe *trsp = buf;
+ struct sli4_fcp_128byte_wqe *trsp_128 = buf;
+
+ memset(buf, 0, sli4->wqe_size);
+
+ if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE) {
+ trsp->class_ag_byte |= SLI4_TRSP_WQE_AG;
+ } else {
+ struct sli4_sge *sge = sgl->virt;
+ struct sli4_bde *bptr;
+
+ if (sli4->params.sgl_pre_registered || port_owned)
+ trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_DBDE;
+ else
+ trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_XBL;
+ bptr = &trsp->bde;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[0].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+ bptr->u.data.low = sge[0].buffer_address_low;
+ bptr->u.data.high = sge[0].buffer_address_high;
+
+ trsp->fcp_response_length = cpu_to_le32(params->xmit_len);
+ }
+
+ if (params->flags & SLI4_IO_CONTINUATION)
+ trsp->eat_xc_ccpe |= SLI4_TRSP_WQE_XC;
+
+ trsp->xri_tag = cpu_to_le16(params->xri);
+ trsp->rpi = cpu_to_le16(params->rpi);
+
+ trsp->command = SLI4_WQE_FCP_TRSP64;
+ trsp->class_ag_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+
+ trsp->remote_xid = cpu_to_le16(params->ox_id);
+ trsp->request_tag = cpu_to_le16(params->tag);
+ if (params->flags & SLI4_IO_DNRX)
+ trsp->ct_dnrx_byte |= SLI4_TRSP_WQE_DNRX;
+ else
+ trsp->ct_dnrx_byte &= ~SLI4_TRSP_WQE_DNRX;
+
+ trsp->lloc1_appid |= 0x1;
+ trsp->cq_id = cpu_to_le16(cq_id);
+ trsp->cmd_type_byte = SLI4_CMD_FCP_TRSP64_WQE;
+
+ /* The upper 7 bits of csctl is the priority */
+ if (params->cs_ctl & SLI4_MASK_CCP) {
+ trsp->eat_xc_ccpe |= SLI4_TRSP_WQE_CCPE;
+ trsp->ccp = (params->cs_ctl & SLI4_MASK_CCP);
+ }
+
+ if (params->app_id && sli4->wqe_size == SLI4_WQE_EXT_BYTES &&
+ !(trsp->eat_xc_ccpe & SLI4_TRSP_WQE_EAT)) {
+ trsp->lloc1_appid |= SLI4_TRSP_WQE_APPID;
+ trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_WQES;
+ trsp_128->dw[31] = params->app_id;
+ }
+ return 0;
+}
+
+int
+sli_fcp_tsend64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u16 cq_id, u8 dif, u8 bs,
+ struct sli_fcp_tgt_params *params)
+{
+ struct sli4_fcp_tsend64_wqe *tsend = buf;
+ struct sli4_fcp_128byte_wqe *tsend_128 = buf;
+ struct sli4_sge *sge = NULL;
+ struct sli4_bde *bptr;
+
+ memset(buf, 0, sli4->wqe_size);
+
+ if (!sgl || !sgl->virt) {
+ efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n",
+ sgl, sgl ? sgl->virt : NULL);
+ return -EIO;
+ }
+ sge = sgl->virt;
+
+ bptr = &tsend->bde;
+ if (sli4->params.sgl_pre_registered) {
+ tsend->ll_qd_xbl_hlm_iod_dbde &= ~SLI4_TSEND_WQE_XBL;
+
+ tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_DBDE;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[2].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+
+ /* TSEND64_WQE specifies first two SGE are skipped (3rd is
+ * valid)
+ */
+ bptr->u.data.low = sge[2].buffer_address_low;
+ bptr->u.data.high = sge[2].buffer_address_high;
+ } else {
+ tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_XBL;
+
+ /* if data is a single physical address, use a BDE */
+ if (!dif &&
+ params->xmit_len <= le32_to_cpu(sge[2].buffer_length)) {
+ tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_DBDE;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[2].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+ /*
+ * TSEND64_WQE specifies first two SGE are skipped
+ * (i.e. 3rd is valid)
+ */
+ bptr->u.data.low =
+ sge[2].buffer_address_low;
+ bptr->u.data.high =
+ sge[2].buffer_address_high;
+ } else {
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) |
+ (sgl->size &
+ SLI4_BDE_LEN_MASK));
+ bptr->u.blp.low =
+ cpu_to_le32(lower_32_bits(sgl->phys));
+ bptr->u.blp.high =
+ cpu_to_le32(upper_32_bits(sgl->phys));
+ }
+ }
+
+ tsend->relative_offset = cpu_to_le32(params->offset);
+
+ if (params->flags & SLI4_IO_CONTINUATION)
+ tsend->dw10byte2 |= SLI4_TSEND_XC;
+
+ tsend->xri_tag = cpu_to_le16(params->xri);
+
+ tsend->rpi = cpu_to_le16(params->rpi);
+ /* WQE uses relative offset */
+ tsend->class_pu_ar_byte |= 1 << SLI4_TSEND_WQE_PU_SHFT;
+
+ if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE)
+ tsend->class_pu_ar_byte |= SLI4_TSEND_WQE_AR;
+
+ tsend->command = SLI4_WQE_FCP_TSEND64;
+ tsend->class_pu_ar_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+ tsend->ct_byte |= SLI4_GENERIC_CONTEXT_RPI << SLI4_TSEND_CT_SHFT;
+ tsend->ct_byte |= dif;
+ tsend->ct_byte |= bs << SLI4_TSEND_BS_SHFT;
+
+ tsend->remote_xid = cpu_to_le16(params->ox_id);
+
+ tsend->request_tag = cpu_to_le16(params->tag);
+
+ tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_LEN_LOC_BIT2;
+
+ tsend->cq_id = cpu_to_le16(cq_id);
+
+ tsend->cmd_type_byte |= SLI4_CMD_FCP_TSEND64_WQE;
+
+ tsend->fcp_data_transmit_length = cpu_to_le32(params->xmit_len);
+
+ if (sli4->params.perf_hint) {
+ bptr = &tsend->first_data_bde;
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (le32_to_cpu(sge[first_data_sge].buffer_length) &
+ SLI4_BDE_LEN_MASK));
+ bptr->u.data.low =
+ sge[first_data_sge].buffer_address_low;
+ bptr->u.data.high =
+ sge[first_data_sge].buffer_address_high;
+ }
+
+ /* The upper 7 bits of csctl is the priority */
+ if (params->cs_ctl & SLI4_MASK_CCP) {
+ tsend->dw10byte2 |= SLI4_TSEND_CCPE;
+ tsend->ccp = (params->cs_ctl & SLI4_MASK_CCP);
+ }
+
+ if (params->app_id && sli4->wqe_size == SLI4_WQE_EXT_BYTES &&
+ !(tsend->dw10byte2 & SLI4_TSEND_EAT)) {
+ tsend->dw10byte0 |= SLI4_TSEND_APPID_VALID;
+ tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQES;
+ tsend_128->dw[31] = params->app_id;
+ }
+ return 0;
+}
+
+int
+sli_gen_request64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ struct sli_ct_params *params)
+{
+ struct sli4_gen_request64_wqe *gen = buf;
+ struct sli4_sge *sge = NULL;
+ struct sli4_bde *bptr;
+
+ memset(buf, 0, sli4->wqe_size);
+
+ if (!sgl || !sgl->virt) {
+ efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n",
+ sgl, sgl ? sgl->virt : NULL);
+ return -EIO;
+ }
+ sge = sgl->virt;
+ bptr = &gen->bde;
+
+ if (sli4->params.sgl_pre_registered) {
+ gen->dw10flags1 &= ~SLI4_GEN_REQ64_WQE_XBL;
+
+ gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_DBDE;
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (params->xmit_len & SLI4_BDE_LEN_MASK));
+
+ bptr->u.data.low = sge[0].buffer_address_low;
+ bptr->u.data.high = sge[0].buffer_address_high;
+ } else {
+ gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_XBL;
+
+ bptr->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) |
+ ((2 * sizeof(struct sli4_sge)) &
+ SLI4_BDE_LEN_MASK));
+
+ bptr->u.blp.low =
+ cpu_to_le32(lower_32_bits(sgl->phys));
+ bptr->u.blp.high =
+ cpu_to_le32(upper_32_bits(sgl->phys));
+ }
+
+ gen->request_payload_length = cpu_to_le32(params->xmit_len);
+ gen->max_response_payload_length = cpu_to_le32(params->rsp_len);
+
+ gen->df_ctl = params->df_ctl;
+ gen->type = params->type;
+ gen->r_ctl = params->r_ctl;
+
+ gen->xri_tag = cpu_to_le16(params->xri);
+
+ gen->ct_byte = SLI4_GENERIC_CONTEXT_RPI << SLI4_GEN_REQ64_CT_SHFT;
+ gen->context_tag = cpu_to_le16(params->rpi);
+
+ gen->class_byte = SLI4_GENERIC_CLASS_CLASS_3;
+
+ gen->command = SLI4_WQE_GEN_REQUEST64;
+
+ gen->timer = params->timeout;
+
+ gen->request_tag = cpu_to_le16(params->tag);
+
+ gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_IOD;
+
+ gen->dw10flags0 |= SLI4_GEN_REQ64_WQE_QOSD;
+
+ gen->cmd_type_byte = SLI4_CMD_GEN_REQUEST64_WQE;
+
+ gen->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT);
+
+ return 0;
+}
+
+int
+sli_send_frame_wqe(struct sli4 *sli, void *buf, u8 sof, u8 eof, u32 *hdr,
+ struct efc_dma *payload, u32 req_len, u8 timeout, u16 xri,
+ u16 req_tag)
+{
+ struct sli4_send_frame_wqe *sf = buf;
+
+ memset(buf, 0, sli->wqe_size);
+
+ sf->dw10flags1 |= SLI4_SF_WQE_DBDE;
+ sf->bde.bde_type_buflen = cpu_to_le32(req_len &
+ SLI4_BDE_LEN_MASK);
+ sf->bde.u.data.low = cpu_to_le32(lower_32_bits(payload->phys));
+ sf->bde.u.data.high = cpu_to_le32(upper_32_bits(payload->phys));
+
+ /* Copy FC header */
+ sf->fc_header_0_1[0] = cpu_to_le32(hdr[0]);
+ sf->fc_header_0_1[1] = cpu_to_le32(hdr[1]);
+ sf->fc_header_2_5[0] = cpu_to_le32(hdr[2]);
+ sf->fc_header_2_5[1] = cpu_to_le32(hdr[3]);
+ sf->fc_header_2_5[2] = cpu_to_le32(hdr[4]);
+ sf->fc_header_2_5[3] = cpu_to_le32(hdr[5]);
+
+ sf->frame_length = cpu_to_le32(req_len);
+
+ sf->xri_tag = cpu_to_le16(xri);
+ sf->dw7flags0 &= ~SLI4_SF_PU;
+ sf->context_tag = 0;
+
+ sf->ct_byte &= ~SLI4_SF_CT;
+ sf->command = SLI4_WQE_SEND_FRAME;
+ sf->dw7flags0 |= SLI4_GENERIC_CLASS_CLASS_3;
+ sf->timer = timeout;
+
+ sf->request_tag = cpu_to_le16(req_tag);
+ sf->eof = eof;
+ sf->sof = sof;
+
+ sf->dw10flags1 &= ~SLI4_SF_QOSD;
+ sf->dw10flags0 |= SLI4_SF_LEN_LOC_BIT1;
+ sf->dw10flags2 &= ~SLI4_SF_XC;
+
+ sf->dw10flags1 |= SLI4_SF_XBL;
+
+ sf->cmd_type_byte |= SLI4_CMD_SEND_FRAME_WQE;
+ sf->cq_id = cpu_to_le16(0xffff);
+
+ return 0;
+}
+
+int
+sli_xmit_bls_rsp64_wqe(struct sli4 *sli, void *buf,
+ struct sli_bls_payload *payload,
+ struct sli_bls_params *params)
+{
+ struct sli4_xmit_bls_rsp_wqe *bls = buf;
+ u32 dw_ridflags = 0;
+
+ /*
+ * Callers can either specify RPI or S_ID, but not both
+ */
+ if (params->rpi_registered && params->s_id != U32_MAX) {
+ efc_log_info(sli, "S_ID specified for attached remote node %d\n",
+ params->rpi);
+ return -EIO;
+ }
+
+ memset(buf, 0, sli->wqe_size);
+
+ if (payload->type == SLI4_SLI_BLS_ACC) {
+ bls->payload_word0 =
+ cpu_to_le32((payload->u.acc.seq_id_last << 16) |
+ (payload->u.acc.seq_id_validity << 24));
+ bls->high_seq_cnt = payload->u.acc.high_seq_cnt;
+ bls->low_seq_cnt = payload->u.acc.low_seq_cnt;
+ } else if (payload->type == SLI4_SLI_BLS_RJT) {
+ bls->payload_word0 =
+ cpu_to_le32(*((u32 *)&payload->u.rjt));
+ dw_ridflags |= SLI4_BLS_RSP_WQE_AR;
+ } else {
+ efc_log_info(sli, "bad BLS type %#x\n", payload->type);
+ return -EIO;
+ }
+
+ bls->ox_id = payload->ox_id;
+ bls->rx_id = payload->rx_id;
+
+ if (params->rpi_registered) {
+ bls->dw8flags0 |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_BLS_RSP_WQE_CT_SHFT;
+ bls->context_tag = cpu_to_le16(params->rpi);
+ } else {
+ bls->dw8flags0 |=
+ SLI4_GENERIC_CONTEXT_VPI << SLI4_BLS_RSP_WQE_CT_SHFT;
+ bls->context_tag = cpu_to_le16(params->vpi);
+
+ bls->local_n_port_id_dword |=
+ cpu_to_le32(params->s_id & 0x00ffffff);
+
+ dw_ridflags = (dw_ridflags & ~SLI4_BLS_RSP_RID) |
+ (params->d_id & SLI4_BLS_RSP_RID);
+
+ bls->temporary_rpi = cpu_to_le16(params->rpi);
+ }
+
+ bls->xri_tag = cpu_to_le16(params->xri);
+
+ bls->dw8flags1 |= SLI4_GENERIC_CLASS_CLASS_3;
+
+ bls->command = SLI4_WQE_XMIT_BLS_RSP;
+
+ bls->request_tag = cpu_to_le16(params->tag);
+
+ bls->dw11flags1 |= SLI4_BLS_RSP_WQE_QOSD;
+
+ bls->remote_id_dword = cpu_to_le32(dw_ridflags);
+ bls->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT);
+
+ bls->dw12flags0 |= SLI4_CMD_XMIT_BLS_RSP64_WQE;
+
+ return 0;
+}
+
+int
+sli_xmit_els_rsp64_wqe(struct sli4 *sli, void *buf, struct efc_dma *rsp,
+ struct sli_els_params *params)
+{
+ struct sli4_xmit_els_rsp64_wqe *els = buf;
+
+ memset(buf, 0, sli->wqe_size);
+
+ if (sli->params.sgl_pre_registered)
+ els->flags2 |= SLI4_ELS_DBDE;
+ else
+ els->flags2 |= SLI4_ELS_XBL;
+
+ els->els_response_payload.bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (params->rsp_len & SLI4_BDE_LEN_MASK));
+ els->els_response_payload.u.data.low =
+ cpu_to_le32(lower_32_bits(rsp->phys));
+ els->els_response_payload.u.data.high =
+ cpu_to_le32(upper_32_bits(rsp->phys));
+
+ els->els_response_payload_length = cpu_to_le32(params->rsp_len);
+
+ els->xri_tag = cpu_to_le16(params->xri);
+
+ els->class_byte |= SLI4_GENERIC_CLASS_CLASS_3;
+
+ els->command = SLI4_WQE_ELS_RSP64;
+
+ els->request_tag = cpu_to_le16(params->tag);
+
+ els->ox_id = cpu_to_le16(params->ox_id);
+
+ els->flags2 |= SLI4_ELS_QOSD;
+
+ els->cmd_type_wqec = SLI4_ELS_REQUEST64_CMD_GEN;
+
+ els->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT);
+
+ if (params->rpi_registered) {
+ els->ct_byte |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_ELS_CT_OFFSET;
+ els->context_tag = cpu_to_le16(params->rpi);
+ return 0;
+ }
+
+ els->ct_byte |= SLI4_GENERIC_CONTEXT_VPI << SLI4_ELS_CT_OFFSET;
+ els->context_tag = cpu_to_le16(params->vpi);
+ els->rid_dw = cpu_to_le32(params->d_id & SLI4_ELS_RID);
+ els->temporary_rpi = cpu_to_le16(params->rpi);
+ if (params->s_id != U32_MAX) {
+ els->sid_dw |=
+ cpu_to_le32(SLI4_ELS_SP | (params->s_id & SLI4_ELS_SID));
+ }
+
+ return 0;
+}
+
+int
+sli_xmit_sequence64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *payload,
+ struct sli_ct_params *params)
+{
+ struct sli4_xmit_sequence64_wqe *xmit = buf;
+
+ memset(buf, 0, sli4->wqe_size);
+
+ if (!payload || !payload->virt) {
+ efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n",
+ payload, payload ? payload->virt : NULL);
+ return -EIO;
+ }
+
+ if (sli4->params.sgl_pre_registered)
+ xmit->dw10w0 |= cpu_to_le16(SLI4_SEQ_WQE_DBDE);
+ else
+ xmit->dw10w0 |= cpu_to_le16(SLI4_SEQ_WQE_XBL);
+
+ xmit->bde.bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (params->rsp_len & SLI4_BDE_LEN_MASK));
+ xmit->bde.u.data.low =
+ cpu_to_le32(lower_32_bits(payload->phys));
+ xmit->bde.u.data.high =
+ cpu_to_le32(upper_32_bits(payload->phys));
+ xmit->sequence_payload_len = cpu_to_le32(params->rsp_len);
+
+ xmit->remote_n_port_id_dword |= cpu_to_le32(params->d_id & 0x00ffffff);
+
+ xmit->relative_offset = 0;
+
+ /* sequence initiative - this matches what is seen from
+ * FC switches in response to FCGS commands
+ */
+ xmit->dw5flags0 &= (~SLI4_SEQ_WQE_SI);
+ xmit->dw5flags0 &= (~SLI4_SEQ_WQE_FT);/* force transmit */
+ xmit->dw5flags0 &= (~SLI4_SEQ_WQE_XO);/* exchange responder */
+ xmit->dw5flags0 |= SLI4_SEQ_WQE_LS;/* last in seqence */
+ xmit->df_ctl = params->df_ctl;
+ xmit->type = params->type;
+ xmit->r_ctl = params->r_ctl;
+
+ xmit->xri_tag = cpu_to_le16(params->xri);
+ xmit->context_tag = cpu_to_le16(params->rpi);
+
+ xmit->dw7flags0 &= ~SLI4_SEQ_WQE_DIF;
+ xmit->dw7flags0 |=
+ SLI4_GENERIC_CONTEXT_RPI << SLI4_SEQ_WQE_CT_SHIFT;
+ xmit->dw7flags0 &= ~SLI4_SEQ_WQE_BS;
+
+ xmit->command = SLI4_WQE_XMIT_SEQUENCE64;
+ xmit->dw7flags1 |= SLI4_GENERIC_CLASS_CLASS_3;
+ xmit->dw7flags1 &= ~SLI4_SEQ_WQE_PU;
+ xmit->timer = params->timeout;
+
+ xmit->abort_tag = 0;
+ xmit->request_tag = cpu_to_le16(params->tag);
+ xmit->remote_xid = cpu_to_le16(params->ox_id);
+
+ xmit->dw10w0 |=
+ cpu_to_le16(SLI4_ELS_REQUEST64_DIR_READ << SLI4_SEQ_WQE_IOD_SHIFT);
+
+ xmit->cmd_type_wqec_byte |= SLI4_CMD_XMIT_SEQUENCE64_WQE;
+
+ xmit->dw10w0 |= cpu_to_le16(2 << SLI4_SEQ_WQE_LEN_LOC_SHIFT);
+
+ xmit->cq_id = cpu_to_le16(0xFFFF);
+
+ return 0;
+}
+
+int
+sli_requeue_xri_wqe(struct sli4 *sli4, void *buf, u16 xri, u16 tag, u16 cq_id)
+{
+ struct sli4_requeue_xri_wqe *requeue = buf;
+
+ memset(buf, 0, sli4->wqe_size);
+
+ requeue->command = SLI4_WQE_REQUEUE_XRI;
+ requeue->xri_tag = cpu_to_le16(xri);
+ requeue->request_tag = cpu_to_le16(tag);
+ requeue->flags2 |= cpu_to_le16(SLI4_REQU_XRI_WQE_XC);
+ requeue->flags1 |= cpu_to_le16(SLI4_REQU_XRI_WQE_QOSD);
+ requeue->cq_id = cpu_to_le16(cq_id);
+ requeue->cmd_type_wqec_byte = SLI4_CMD_REQUEUE_XRI_WQE;
+ return 0;
+}
+
+int
+sli_fc_process_link_attention(struct sli4 *sli4, void *acqe)
+{
+ struct sli4_link_attention *link_attn = acqe;
+ struct sli4_link_event event = { 0 };
+
+ efc_log_info(sli4, "link=%d attn_type=%#x top=%#x speed=%#x pfault=%#x\n",
+ link_attn->link_number, link_attn->attn_type,
+ link_attn->topology, link_attn->port_speed,
+ link_attn->port_fault);
+ efc_log_info(sli4, "shared_lnk_status=%#x logl_lnk_speed=%#x evttag=%#x\n",
+ link_attn->shared_link_status,
+ le16_to_cpu(link_attn->logical_link_speed),
+ le32_to_cpu(link_attn->event_tag));
+
+ if (!sli4->link)
+ return -EIO;
+
+ event.medium = SLI4_LINK_MEDIUM_FC;
+
+ switch (link_attn->attn_type) {
+ case SLI4_LNK_ATTN_TYPE_LINK_UP:
+ event.status = SLI4_LINK_STATUS_UP;
+ break;
+ case SLI4_LNK_ATTN_TYPE_LINK_DOWN:
+ event.status = SLI4_LINK_STATUS_DOWN;
+ break;
+ case SLI4_LNK_ATTN_TYPE_NO_HARD_ALPA:
+ efc_log_info(sli4, "attn_type: no hard alpa\n");
+ event.status = SLI4_LINK_STATUS_NO_ALPA;
+ break;
+ default:
+ efc_log_info(sli4, "attn_type: unknown\n");
+ break;
+ }
+
+ switch (link_attn->event_type) {
+ case SLI4_EVENT_LINK_ATTENTION:
+ break;
+ case SLI4_EVENT_SHARED_LINK_ATTENTION:
+ efc_log_info(sli4, "event_type: FC shared link event\n");
+ break;
+ default:
+ efc_log_info(sli4, "event_type: unknown\n");
+ break;
+ }
+
+ switch (link_attn->topology) {
+ case SLI4_LNK_ATTN_P2P:
+ event.topology = SLI4_LINK_TOPO_NON_FC_AL;
+ break;
+ case SLI4_LNK_ATTN_FC_AL:
+ event.topology = SLI4_LINK_TOPO_FC_AL;
+ break;
+ case SLI4_LNK_ATTN_INTERNAL_LOOPBACK:
+ efc_log_info(sli4, "topology Internal loopback\n");
+ event.topology = SLI4_LINK_TOPO_LOOPBACK_INTERNAL;
+ break;
+ case SLI4_LNK_ATTN_SERDES_LOOPBACK:
+ efc_log_info(sli4, "topology serdes loopback\n");
+ event.topology = SLI4_LINK_TOPO_LOOPBACK_EXTERNAL;
+ break;
+ default:
+ efc_log_info(sli4, "topology: unknown\n");
+ break;
+ }
+
+ event.speed = link_attn->port_speed * 1000;
+
+ sli4->link(sli4->link_arg, (void *)&event);
+
+ return 0;
+}
+
+int
+sli_fc_cqe_parse(struct sli4 *sli4, struct sli4_queue *cq,
+ u8 *cqe, enum sli4_qentry *etype, u16 *r_id)
+{
+ u8 code = cqe[SLI4_CQE_CODE_OFFSET];
+ int rc;
+
+ switch (code) {
+ case SLI4_CQE_CODE_WORK_REQUEST_COMPLETION:
+ {
+ struct sli4_fc_wcqe *wcqe = (void *)cqe;
+
+ *etype = SLI4_QENTRY_WQ;
+ *r_id = le16_to_cpu(wcqe->request_tag);
+ rc = wcqe->status;
+
+ /* Flag errors except for FCP_RSP_FAILURE */
+ if (rc && rc != SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE) {
+ efc_log_info(sli4, "WCQE: status=%#x hw_status=%#x tag=%#x\n",
+ wcqe->status, wcqe->hw_status,
+ le16_to_cpu(wcqe->request_tag));
+ efc_log_info(sli4, "w1=%#x w2=%#x xb=%d\n",
+ le32_to_cpu(wcqe->wqe_specific_1),
+ le32_to_cpu(wcqe->wqe_specific_2),
+ (wcqe->flags & SLI4_WCQE_XB));
+ efc_log_info(sli4, " %08X %08X %08X %08X\n",
+ ((u32 *)cqe)[0], ((u32 *)cqe)[1],
+ ((u32 *)cqe)[2], ((u32 *)cqe)[3]);
+ }
+
+ break;
+ }
+ case SLI4_CQE_CODE_RQ_ASYNC:
+ {
+ struct sli4_fc_async_rcqe *rcqe = (void *)cqe;
+
+ *etype = SLI4_QENTRY_RQ;
+ *r_id = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID;
+ rc = rcqe->status;
+ break;
+ }
+ case SLI4_CQE_CODE_RQ_ASYNC_V1:
+ {
+ struct sli4_fc_async_rcqe_v1 *rcqe = (void *)cqe;
+
+ *etype = SLI4_QENTRY_RQ;
+ *r_id = le16_to_cpu(rcqe->rq_id);
+ rc = rcqe->status;
+ break;
+ }
+ case SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD:
+ {
+ struct sli4_fc_optimized_write_cmd_cqe *optcqe = (void *)cqe;
+
+ *etype = SLI4_QENTRY_OPT_WRITE_CMD;
+ *r_id = le16_to_cpu(optcqe->rq_id);
+ rc = optcqe->status;
+ break;
+ }
+ case SLI4_CQE_CODE_OPTIMIZED_WRITE_DATA:
+ {
+ struct sli4_fc_optimized_write_data_cqe *dcqe = (void *)cqe;
+
+ *etype = SLI4_QENTRY_OPT_WRITE_DATA;
+ *r_id = le16_to_cpu(dcqe->xri);
+ rc = dcqe->status;
+
+ /* Flag errors */
+ if (rc != SLI4_FC_WCQE_STATUS_SUCCESS) {
+ efc_log_info(sli4, "Optimized DATA CQE: status=%#x\n",
+ dcqe->status);
+ efc_log_info(sli4, "hstat=%#x xri=%#x dpl=%#x w3=%#x xb=%d\n",
+ dcqe->hw_status, le16_to_cpu(dcqe->xri),
+ le32_to_cpu(dcqe->total_data_placed),
+ ((u32 *)cqe)[3],
+ (dcqe->flags & SLI4_OCQE_XB));
+ }
+ break;
+ }
+ case SLI4_CQE_CODE_RQ_COALESCING:
+ {
+ struct sli4_fc_coalescing_rcqe *rcqe = (void *)cqe;
+
+ *etype = SLI4_QENTRY_RQ;
+ *r_id = le16_to_cpu(rcqe->rq_id);
+ rc = rcqe->status;
+ break;
+ }
+ case SLI4_CQE_CODE_XRI_ABORTED:
+ {
+ struct sli4_fc_xri_aborted_cqe *xa = (void *)cqe;
+
+ *etype = SLI4_QENTRY_XABT;
+ *r_id = le16_to_cpu(xa->xri);
+ rc = 0;
+ break;
+ }
+ case SLI4_CQE_CODE_RELEASE_WQE:
+ {
+ struct sli4_fc_wqec *wqec = (void *)cqe;
+
+ *etype = SLI4_QENTRY_WQ_RELEASE;
+ *r_id = le16_to_cpu(wqec->wq_id);
+ rc = 0;
+ break;
+ }
+ default:
+ efc_log_info(sli4, "CQE completion code %d not handled\n",
+ code);
+ *etype = SLI4_QENTRY_MAX;
+ *r_id = U16_MAX;
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+u32
+sli_fc_response_length(struct sli4 *sli4, u8 *cqe)
+{
+ struct sli4_fc_wcqe *wcqe = (void *)cqe;
+
+ return le32_to_cpu(wcqe->wqe_specific_1);
+}
+
+u32
+sli_fc_io_length(struct sli4 *sli4, u8 *cqe)
+{
+ struct sli4_fc_wcqe *wcqe = (void *)cqe;
+
+ return le32_to_cpu(wcqe->wqe_specific_1);
+}
+
+int
+sli_fc_els_did(struct sli4 *sli4, u8 *cqe, u32 *d_id)
+{
+ struct sli4_fc_wcqe *wcqe = (void *)cqe;
+
+ *d_id = 0;
+
+ if (wcqe->status)
+ return -EIO;
+ *d_id = le32_to_cpu(wcqe->wqe_specific_2) & 0x00ffffff;
+ return 0;
+}
+
+u32
+sli_fc_ext_status(struct sli4 *sli4, u8 *cqe)
+{
+ struct sli4_fc_wcqe *wcqe = (void *)cqe;
+ u32 mask;
+
+ switch (wcqe->status) {
+ case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
+ mask = U32_MAX;
+ break;
+ case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
+ case SLI4_FC_WCQE_STATUS_CMD_REJECT:
+ mask = 0xff;
+ break;
+ case SLI4_FC_WCQE_STATUS_NPORT_RJT:
+ case SLI4_FC_WCQE_STATUS_FABRIC_RJT:
+ case SLI4_FC_WCQE_STATUS_NPORT_BSY:
+ case SLI4_FC_WCQE_STATUS_FABRIC_BSY:
+ case SLI4_FC_WCQE_STATUS_LS_RJT:
+ mask = U32_MAX;
+ break;
+ case SLI4_FC_WCQE_STATUS_DI_ERROR:
+ mask = U32_MAX;
+ break;
+ default:
+ mask = 0;
+ }
+
+ return le32_to_cpu(wcqe->wqe_specific_2) & mask;
+}
+
+int
+sli_fc_rqe_rqid_and_index(struct sli4 *sli4, u8 *cqe, u16 *rq_id, u32 *index)
+{
+ int rc = -EIO;
+ u8 code = 0;
+ u16 rq_element_index;
+
+ *rq_id = 0;
+ *index = U32_MAX;
+
+ code = cqe[SLI4_CQE_CODE_OFFSET];
+
+ /* Retrieve the RQ index from the completion */
+ if (code == SLI4_CQE_CODE_RQ_ASYNC) {
+ struct sli4_fc_async_rcqe *rcqe = (void *)cqe;
+
+ *rq_id = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID;
+ rq_element_index =
+ le16_to_cpu(rcqe->rq_elmt_indx_word) & SLI4_RACQE_RQ_EL_INDX;
+ *index = rq_element_index;
+ if (rcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) {
+ rc = 0;
+ } else {
+ rc = rcqe->status;
+ efc_log_info(sli4, "status=%02x (%s) rq_id=%d\n",
+ rcqe->status,
+ sli_fc_get_status_string(rcqe->status),
+ le16_to_cpu(rcqe->fcfi_rq_id_word) &
+ SLI4_RACQE_RQ_ID);
+
+ efc_log_info(sli4, "pdpl=%x sof=%02x eof=%02x hdpl=%x\n",
+ le16_to_cpu(rcqe->data_placement_length),
+ rcqe->sof_byte, rcqe->eof_byte,
+ rcqe->hdpl_byte & SLI4_RACQE_HDPL);
+ }
+ } else if (code == SLI4_CQE_CODE_RQ_ASYNC_V1) {
+ struct sli4_fc_async_rcqe_v1 *rcqe_v1 = (void *)cqe;
+
+ *rq_id = le16_to_cpu(rcqe_v1->rq_id);
+ rq_element_index =
+ (le16_to_cpu(rcqe_v1->rq_elmt_indx_word) &
+ SLI4_RACQE_RQ_EL_INDX);
+ *index = rq_element_index;
+ if (rcqe_v1->status == SLI4_FC_ASYNC_RQ_SUCCESS) {
+ rc = 0;
+ } else {
+ rc = rcqe_v1->status;
+ efc_log_info(sli4, "status=%02x (%s) rq_id=%d, index=%x\n",
+ rcqe_v1->status,
+ sli_fc_get_status_string(rcqe_v1->status),
+ le16_to_cpu(rcqe_v1->rq_id), rq_element_index);
+
+ efc_log_info(sli4, "pdpl=%x sof=%02x eof=%02x hdpl=%x\n",
+ le16_to_cpu(rcqe_v1->data_placement_length),
+ rcqe_v1->sof_byte, rcqe_v1->eof_byte,
+ rcqe_v1->hdpl_byte & SLI4_RACQE_HDPL);
+ }
+ } else if (code == SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD) {
+ struct sli4_fc_optimized_write_cmd_cqe *optcqe = (void *)cqe;
+
+ *rq_id = le16_to_cpu(optcqe->rq_id);
+ *index = le16_to_cpu(optcqe->w1) & SLI4_OCQE_RQ_EL_INDX;
+ if (optcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) {
+ rc = 0;
+ } else {
+ rc = optcqe->status;
+ efc_log_info(sli4, "stat=%02x (%s) rqid=%d, idx=%x pdpl=%x\n",
+ optcqe->status,
+ sli_fc_get_status_string(optcqe->status),
+ le16_to_cpu(optcqe->rq_id), *index,
+ le16_to_cpu(optcqe->data_placement_length));
+
+ efc_log_info(sli4, "hdpl=%x oox=%d agxr=%d xri=0x%x rpi=%x\n",
+ (optcqe->hdpl_vld & SLI4_OCQE_HDPL),
+ (optcqe->flags1 & SLI4_OCQE_OOX),
+ (optcqe->flags1 & SLI4_OCQE_AGXR),
+ optcqe->xri, le16_to_cpu(optcqe->rpi));
+ }
+ } else if (code == SLI4_CQE_CODE_RQ_COALESCING) {
+ struct sli4_fc_coalescing_rcqe *rcqe = (void *)cqe;
+
+ rq_element_index = (le16_to_cpu(rcqe->rq_elmt_indx_word) &
+ SLI4_RCQE_RQ_EL_INDX);
+
+ *rq_id = le16_to_cpu(rcqe->rq_id);
+ if (rcqe->status == SLI4_FC_COALESCE_RQ_SUCCESS) {
+ *index = rq_element_index;
+ rc = 0;
+ } else {
+ *index = U32_MAX;
+ rc = rcqe->status;
+
+ efc_log_info(sli4, "stat=%02x (%s) rq_id=%d, idx=%x\n",
+ rcqe->status,
+ sli_fc_get_status_string(rcqe->status),
+ le16_to_cpu(rcqe->rq_id), rq_element_index);
+ efc_log_info(sli4, "rq_id=%#x sdpl=%x\n",
+ le16_to_cpu(rcqe->rq_id),
+ le16_to_cpu(rcqe->seq_placement_length));
+ }
+ } else {
+ struct sli4_fc_async_rcqe *rcqe = (void *)cqe;
+
+ *index = U32_MAX;
+ rc = rcqe->status;
+
+ efc_log_info(sli4, "status=%02x rq_id=%d, index=%x pdpl=%x\n",
+ rcqe->status,
+ le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID,
+ (le16_to_cpu(rcqe->rq_elmt_indx_word) & SLI4_RACQE_RQ_EL_INDX),
+ le16_to_cpu(rcqe->data_placement_length));
+ efc_log_info(sli4, "sof=%02x eof=%02x hdpl=%x\n",
+ rcqe->sof_byte, rcqe->eof_byte,
+ rcqe->hdpl_byte & SLI4_RACQE_HDPL);
+ }
+
+ return rc;
+}
+
+static int
+sli_bmbx_wait(struct sli4 *sli4, u32 msec)
+{
+ u32 val;
+ unsigned long end;
+
+ /* Wait for the bootstrap mailbox to report "ready" */
+ end = jiffies + msecs_to_jiffies(msec);
+ do {
+ val = readl(sli4->reg[0] + SLI4_BMBX_REG);
+ if (val & SLI4_BMBX_RDY)
+ return 0;
+
+ usleep_range(1000, 2000);
+ } while (time_before(jiffies, end));
+
+ return -EIO;
+}
+
+static int
+sli_bmbx_write(struct sli4 *sli4)
+{
+ u32 val;
+
+ /* write buffer location to bootstrap mailbox register */
+ val = sli_bmbx_write_hi(sli4->bmbx.phys);
+ writel(val, (sli4->reg[0] + SLI4_BMBX_REG));
+
+ if (sli_bmbx_wait(sli4, SLI4_BMBX_DELAY_US)) {
+ efc_log_crit(sli4, "BMBX WRITE_HI failed\n");
+ return -EIO;
+ }
+ val = sli_bmbx_write_lo(sli4->bmbx.phys);
+ writel(val, (sli4->reg[0] + SLI4_BMBX_REG));
+
+ /* wait for SLI Port to set ready bit */
+ return sli_bmbx_wait(sli4, SLI4_BMBX_TIMEOUT_MSEC);
+}
+
+int
+sli_bmbx_command(struct sli4 *sli4)
+{
+ void *cqe = (u8 *)sli4->bmbx.virt + SLI4_BMBX_SIZE;
+
+ if (sli_fw_error_status(sli4) > 0) {
+ efc_log_crit(sli4, "Chip is in an error state -Mailbox command rejected");
+ efc_log_crit(sli4, " status=%#x error1=%#x error2=%#x\n",
+ sli_reg_read_status(sli4),
+ sli_reg_read_err1(sli4),
+ sli_reg_read_err2(sli4));
+ return -EIO;
+ }
+
+ /* Submit a command to the bootstrap mailbox and check the status */
+ if (sli_bmbx_write(sli4)) {
+ efc_log_crit(sli4, "bmbx write fail phys=%pad reg=%#x\n",
+ &sli4->bmbx.phys, readl(sli4->reg[0] + SLI4_BMBX_REG));
+ return -EIO;
+ }
+
+ /* check completion queue entry status */
+ if (le32_to_cpu(((struct sli4_mcqe *)cqe)->dw3_flags) &
+ SLI4_MCQE_VALID) {
+ return sli_cqe_mq(sli4, cqe);
+ }
+ efc_log_crit(sli4, "invalid or wrong type\n");
+ return -EIO;
+}
+
+int
+sli_cmd_config_link(struct sli4 *sli4, void *buf)
+{
+ struct sli4_cmd_config_link *config_link = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ config_link->hdr.command = SLI4_MBX_CMD_CONFIG_LINK;
+
+ /* Port interprets zero in a field as "use default value" */
+
+ return 0;
+}
+
+int
+sli_cmd_down_link(struct sli4 *sli4, void *buf)
+{
+ struct sli4_mbox_command_header *hdr = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ hdr->command = SLI4_MBX_CMD_DOWN_LINK;
+
+ /* Port interprets zero in a field as "use default value" */
+
+ return 0;
+}
+
+int
+sli_cmd_dump_type4(struct sli4 *sli4, void *buf, u16 wki)
+{
+ struct sli4_cmd_dump4 *cmd = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ cmd->hdr.command = SLI4_MBX_CMD_DUMP;
+ cmd->type_dword = cpu_to_le32(0x4);
+ cmd->wki_selection = cpu_to_le16(wki);
+ return 0;
+}
+
+int
+sli_cmd_common_read_transceiver_data(struct sli4 *sli4, void *buf, u32 page_num,
+ struct efc_dma *dma)
+{
+ struct sli4_rqst_cmn_read_transceiver_data *req = NULL;
+ u32 psize;
+
+ if (!dma)
+ psize = SLI4_CFG_PYLD_LENGTH(cmn_read_transceiver_data);
+ else
+ psize = dma->size;
+
+ req = sli_config_cmd_init(sli4, buf, psize, dma);
+ if (!req)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_READ_TRANS_DATA,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN(cmn_read_transceiver_data));
+
+ req->page_number = cpu_to_le32(page_num);
+ req->port = cpu_to_le32(sli4->port_number);
+
+ return 0;
+}
+
+int
+sli_cmd_read_link_stats(struct sli4 *sli4, void *buf, u8 req_ext_counters,
+ u8 clear_overflow_flags,
+ u8 clear_all_counters)
+{
+ struct sli4_cmd_read_link_stats *cmd = buf;
+ u32 flags;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ cmd->hdr.command = SLI4_MBX_CMD_READ_LNK_STAT;
+
+ flags = 0;
+ if (req_ext_counters)
+ flags |= SLI4_READ_LNKSTAT_REC;
+ if (clear_all_counters)
+ flags |= SLI4_READ_LNKSTAT_CLRC;
+ if (clear_overflow_flags)
+ flags |= SLI4_READ_LNKSTAT_CLOF;
+
+ cmd->dw1_flags = cpu_to_le32(flags);
+ return 0;
+}
+
+int
+sli_cmd_read_status(struct sli4 *sli4, void *buf, u8 clear_counters)
+{
+ struct sli4_cmd_read_status *cmd = buf;
+ u32 flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ cmd->hdr.command = SLI4_MBX_CMD_READ_STATUS;
+ if (clear_counters)
+ flags |= SLI4_READSTATUS_CLEAR_COUNTERS;
+ else
+ flags &= ~SLI4_READSTATUS_CLEAR_COUNTERS;
+
+ cmd->dw1_flags = cpu_to_le32(flags);
+ return 0;
+}
+
+int
+sli_cmd_init_link(struct sli4 *sli4, void *buf, u32 speed, u8 reset_alpa)
+{
+ struct sli4_cmd_init_link *init_link = buf;
+ u32 flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ init_link->hdr.command = SLI4_MBX_CMD_INIT_LINK;
+
+ init_link->sel_reset_al_pa_dword =
+ cpu_to_le32(reset_alpa);
+ flags &= ~SLI4_INIT_LINK_F_LOOPBACK;
+
+ init_link->link_speed_sel_code = cpu_to_le32(speed);
+ switch (speed) {
+ case SLI4_LINK_SPEED_1G:
+ case SLI4_LINK_SPEED_2G:
+ case SLI4_LINK_SPEED_4G:
+ case SLI4_LINK_SPEED_8G:
+ case SLI4_LINK_SPEED_16G:
+ case SLI4_LINK_SPEED_32G:
+ case SLI4_LINK_SPEED_64G:
+ flags |= SLI4_INIT_LINK_F_FIXED_SPEED;
+ break;
+ case SLI4_LINK_SPEED_10G:
+ efc_log_info(sli4, "unsupported FC speed %d\n", speed);
+ init_link->flags0 = cpu_to_le32(flags);
+ return -EIO;
+ }
+
+ switch (sli4->topology) {
+ case SLI4_READ_CFG_TOPO_FC:
+ /* Attempt P2P but failover to FC-AL */
+ flags |= SLI4_INIT_LINK_F_FAIL_OVER;
+ flags |= SLI4_INIT_LINK_F_P2P_FAIL_OVER;
+ break;
+ case SLI4_READ_CFG_TOPO_FC_AL:
+ flags |= SLI4_INIT_LINK_F_FCAL_ONLY;
+ if (speed == SLI4_LINK_SPEED_16G ||
+ speed == SLI4_LINK_SPEED_32G) {
+ efc_log_info(sli4, "unsupported FC-AL speed %d\n",
+ speed);
+ init_link->flags0 = cpu_to_le32(flags);
+ return -EIO;
+ }
+ break;
+ case SLI4_READ_CFG_TOPO_NON_FC_AL:
+ flags |= SLI4_INIT_LINK_F_P2P_ONLY;
+ break;
+ default:
+
+ efc_log_info(sli4, "unsupported topology %#x\n", sli4->topology);
+
+ init_link->flags0 = cpu_to_le32(flags);
+ return -EIO;
+ }
+
+ flags &= ~SLI4_INIT_LINK_F_UNFAIR;
+ flags &= ~SLI4_INIT_LINK_F_NO_LIRP;
+ flags &= ~SLI4_INIT_LINK_F_LOOP_VALID_CHK;
+ flags &= ~SLI4_INIT_LINK_F_NO_LISA;
+ flags &= ~SLI4_INIT_LINK_F_PICK_HI_ALPA;
+ init_link->flags0 = cpu_to_le32(flags);
+
+ return 0;
+}
+
+int
+sli_cmd_init_vfi(struct sli4 *sli4, void *buf, u16 vfi, u16 fcfi, u16 vpi)
+{
+ struct sli4_cmd_init_vfi *init_vfi = buf;
+ u16 flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ init_vfi->hdr.command = SLI4_MBX_CMD_INIT_VFI;
+ init_vfi->vfi = cpu_to_le16(vfi);
+ init_vfi->fcfi = cpu_to_le16(fcfi);
+
+ /*
+ * If the VPI is valid, initialize it at the same time as
+ * the VFI
+ */
+ if (vpi != U16_MAX) {
+ flags |= SLI4_INIT_VFI_FLAG_VP;
+ init_vfi->flags0_word = cpu_to_le16(flags);
+ init_vfi->vpi = cpu_to_le16(vpi);
+ }
+
+ return 0;
+}
+
+int
+sli_cmd_init_vpi(struct sli4 *sli4, void *buf, u16 vpi, u16 vfi)
+{
+ struct sli4_cmd_init_vpi *init_vpi = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ init_vpi->hdr.command = SLI4_MBX_CMD_INIT_VPI;
+ init_vpi->vpi = cpu_to_le16(vpi);
+ init_vpi->vfi = cpu_to_le16(vfi);
+
+ return 0;
+}
+
+int
+sli_cmd_post_xri(struct sli4 *sli4, void *buf, u16 xri_base, u16 xri_count)
+{
+ struct sli4_cmd_post_xri *post_xri = buf;
+ u16 xri_count_flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ post_xri->hdr.command = SLI4_MBX_CMD_POST_XRI;
+ post_xri->xri_base = cpu_to_le16(xri_base);
+ xri_count_flags = xri_count & SLI4_POST_XRI_COUNT;
+ xri_count_flags |= SLI4_POST_XRI_FLAG_ENX;
+ xri_count_flags |= SLI4_POST_XRI_FLAG_VAL;
+ post_xri->xri_count_flags = cpu_to_le16(xri_count_flags);
+
+ return 0;
+}
+
+int
+sli_cmd_release_xri(struct sli4 *sli4, void *buf, u8 num_xri)
+{
+ struct sli4_cmd_release_xri *release_xri = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ release_xri->hdr.command = SLI4_MBX_CMD_RELEASE_XRI;
+ release_xri->xri_count_word = cpu_to_le16(num_xri &
+ SLI4_RELEASE_XRI_COUNT);
+
+ return 0;
+}
+
+static int
+sli_cmd_read_config(struct sli4 *sli4, void *buf)
+{
+ struct sli4_cmd_read_config *read_config = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ read_config->hdr.command = SLI4_MBX_CMD_READ_CONFIG;
+
+ return 0;
+}
+
+int
+sli_cmd_read_nvparms(struct sli4 *sli4, void *buf)
+{
+ struct sli4_cmd_read_nvparms *read_nvparms = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ read_nvparms->hdr.command = SLI4_MBX_CMD_READ_NVPARMS;
+
+ return 0;
+}
+
+int
+sli_cmd_write_nvparms(struct sli4 *sli4, void *buf, u8 *wwpn, u8 *wwnn,
+ u8 hard_alpa, u32 preferred_d_id)
+{
+ struct sli4_cmd_write_nvparms *write_nvparms = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ write_nvparms->hdr.command = SLI4_MBX_CMD_WRITE_NVPARMS;
+ memcpy(write_nvparms->wwpn, wwpn, 8);
+ memcpy(write_nvparms->wwnn, wwnn, 8);
+
+ write_nvparms->hard_alpa_d_id =
+ cpu_to_le32((preferred_d_id << 8) | hard_alpa);
+ return 0;
+}
+
+static int
+sli_cmd_read_rev(struct sli4 *sli4, void *buf, struct efc_dma *vpd)
+{
+ struct sli4_cmd_read_rev *read_rev = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ read_rev->hdr.command = SLI4_MBX_CMD_READ_REV;
+
+ if (vpd && vpd->size) {
+ read_rev->flags0_word |= cpu_to_le16(SLI4_READ_REV_FLAG_VPD);
+
+ read_rev->available_length_dword =
+ cpu_to_le32(vpd->size &
+ SLI4_READ_REV_AVAILABLE_LENGTH);
+
+ read_rev->hostbuf.low =
+ cpu_to_le32(lower_32_bits(vpd->phys));
+ read_rev->hostbuf.high =
+ cpu_to_le32(upper_32_bits(vpd->phys));
+ }
+
+ return 0;
+}
+
+int
+sli_cmd_read_sparm64(struct sli4 *sli4, void *buf, struct efc_dma *dma, u16 vpi)
+{
+ struct sli4_cmd_read_sparm64 *read_sparm64 = buf;
+
+ if (vpi == U16_MAX) {
+ efc_log_err(sli4, "special VPI not supported!!!\n");
+ return -EIO;
+ }
+
+ if (!dma || !dma->phys) {
+ efc_log_err(sli4, "bad DMA buffer\n");
+ return -EIO;
+ }
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ read_sparm64->hdr.command = SLI4_MBX_CMD_READ_SPARM64;
+
+ read_sparm64->bde_64.bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (dma->size & SLI4_BDE_LEN_MASK));
+ read_sparm64->bde_64.u.data.low =
+ cpu_to_le32(lower_32_bits(dma->phys));
+ read_sparm64->bde_64.u.data.high =
+ cpu_to_le32(upper_32_bits(dma->phys));
+
+ read_sparm64->vpi = cpu_to_le16(vpi);
+
+ return 0;
+}
+
+int
+sli_cmd_read_topology(struct sli4 *sli4, void *buf, struct efc_dma *dma)
+{
+ struct sli4_cmd_read_topology *read_topo = buf;
+
+ if (!dma || !dma->size)
+ return -EIO;
+
+ if (dma->size < SLI4_MIN_LOOP_MAP_BYTES) {
+ efc_log_err(sli4, "loop map buffer too small %zx\n", dma->size);
+ return -EIO;
+ }
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ read_topo->hdr.command = SLI4_MBX_CMD_READ_TOPOLOGY;
+
+ memset(dma->virt, 0, dma->size);
+
+ read_topo->bde_loop_map.bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (dma->size & SLI4_BDE_LEN_MASK));
+ read_topo->bde_loop_map.u.data.low =
+ cpu_to_le32(lower_32_bits(dma->phys));
+ read_topo->bde_loop_map.u.data.high =
+ cpu_to_le32(upper_32_bits(dma->phys));
+
+ return 0;
+}
+
+int
+sli_cmd_reg_fcfi(struct sli4 *sli4, void *buf, u16 index,
+ struct sli4_cmd_rq_cfg *rq_cfg)
+{
+ struct sli4_cmd_reg_fcfi *reg_fcfi = buf;
+ u32 i;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ reg_fcfi->hdr.command = SLI4_MBX_CMD_REG_FCFI;
+
+ reg_fcfi->fcf_index = cpu_to_le16(index);
+
+ for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
+ switch (i) {
+ case 0:
+ reg_fcfi->rqid0 = rq_cfg[0].rq_id;
+ break;
+ case 1:
+ reg_fcfi->rqid1 = rq_cfg[1].rq_id;
+ break;
+ case 2:
+ reg_fcfi->rqid2 = rq_cfg[2].rq_id;
+ break;
+ case 3:
+ reg_fcfi->rqid3 = rq_cfg[3].rq_id;
+ break;
+ }
+ reg_fcfi->rq_cfg[i].r_ctl_mask = rq_cfg[i].r_ctl_mask;
+ reg_fcfi->rq_cfg[i].r_ctl_match = rq_cfg[i].r_ctl_match;
+ reg_fcfi->rq_cfg[i].type_mask = rq_cfg[i].type_mask;
+ reg_fcfi->rq_cfg[i].type_match = rq_cfg[i].type_match;
+ }
+
+ return 0;
+}
+
+int
+sli_cmd_reg_fcfi_mrq(struct sli4 *sli4, void *buf, u8 mode, u16 fcf_index,
+ u8 rq_selection_policy, u8 mrq_bit_mask, u16 num_mrqs,
+ struct sli4_cmd_rq_cfg *rq_cfg)
+{
+ struct sli4_cmd_reg_fcfi_mrq *reg_fcfi_mrq = buf;
+ u32 i;
+ u32 mrq_flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ reg_fcfi_mrq->hdr.command = SLI4_MBX_CMD_REG_FCFI_MRQ;
+ if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) {
+ reg_fcfi_mrq->fcf_index = cpu_to_le16(fcf_index);
+ goto done;
+ }
+
+ reg_fcfi_mrq->dw8_vlan = cpu_to_le32(SLI4_REGFCFI_MRQ_MODE);
+
+ for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) {
+ reg_fcfi_mrq->rq_cfg[i].r_ctl_mask = rq_cfg[i].r_ctl_mask;
+ reg_fcfi_mrq->rq_cfg[i].r_ctl_match = rq_cfg[i].r_ctl_match;
+ reg_fcfi_mrq->rq_cfg[i].type_mask = rq_cfg[i].type_mask;
+ reg_fcfi_mrq->rq_cfg[i].type_match = rq_cfg[i].type_match;
+
+ switch (i) {
+ case 3:
+ reg_fcfi_mrq->rqid3 = rq_cfg[i].rq_id;
+ break;
+ case 2:
+ reg_fcfi_mrq->rqid2 = rq_cfg[i].rq_id;
+ break;
+ case 1:
+ reg_fcfi_mrq->rqid1 = rq_cfg[i].rq_id;
+ break;
+ case 0:
+ reg_fcfi_mrq->rqid0 = rq_cfg[i].rq_id;
+ break;
+ }
+ }
+
+ mrq_flags = num_mrqs & SLI4_REGFCFI_MRQ_MASK_NUM_PAIRS;
+ mrq_flags |= (mrq_bit_mask << 8);
+ mrq_flags |= (rq_selection_policy << 12);
+ reg_fcfi_mrq->dw9_mrqflags = cpu_to_le32(mrq_flags);
+done:
+ return 0;
+}
+
+int
+sli_cmd_reg_rpi(struct sli4 *sli4, void *buf, u32 rpi, u32 vpi, u32 fc_id,
+ struct efc_dma *dma, u8 update, u8 enable_t10_pi)
+{
+ struct sli4_cmd_reg_rpi *reg_rpi = buf;
+ u32 rportid_flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ reg_rpi->hdr.command = SLI4_MBX_CMD_REG_RPI;
+
+ reg_rpi->rpi = cpu_to_le16(rpi);
+
+ rportid_flags = fc_id & SLI4_REGRPI_REMOTE_N_PORTID;
+
+ if (update)
+ rportid_flags |= SLI4_REGRPI_UPD;
+ else
+ rportid_flags &= ~SLI4_REGRPI_UPD;
+
+ if (enable_t10_pi)
+ rportid_flags |= SLI4_REGRPI_ETOW;
+ else
+ rportid_flags &= ~SLI4_REGRPI_ETOW;
+
+ reg_rpi->dw2_rportid_flags = cpu_to_le32(rportid_flags);
+
+ reg_rpi->bde_64.bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (SLI4_REG_RPI_BUF_LEN & SLI4_BDE_LEN_MASK));
+ reg_rpi->bde_64.u.data.low =
+ cpu_to_le32(lower_32_bits(dma->phys));
+ reg_rpi->bde_64.u.data.high =
+ cpu_to_le32(upper_32_bits(dma->phys));
+
+ reg_rpi->vpi = cpu_to_le16(vpi);
+
+ return 0;
+}
+
+int
+sli_cmd_reg_vfi(struct sli4 *sli4, void *buf, size_t size,
+ u16 vfi, u16 fcfi, struct efc_dma dma,
+ u16 vpi, __be64 sli_wwpn, u32 fc_id)
+{
+ struct sli4_cmd_reg_vfi *reg_vfi = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ reg_vfi->hdr.command = SLI4_MBX_CMD_REG_VFI;
+
+ reg_vfi->vfi = cpu_to_le16(vfi);
+
+ reg_vfi->fcfi = cpu_to_le16(fcfi);
+
+ reg_vfi->sparm.bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (SLI4_REG_RPI_BUF_LEN & SLI4_BDE_LEN_MASK));
+ reg_vfi->sparm.u.data.low =
+ cpu_to_le32(lower_32_bits(dma.phys));
+ reg_vfi->sparm.u.data.high =
+ cpu_to_le32(upper_32_bits(dma.phys));
+
+ reg_vfi->e_d_tov = cpu_to_le32(sli4->e_d_tov);
+ reg_vfi->r_a_tov = cpu_to_le32(sli4->r_a_tov);
+
+ reg_vfi->dw0w1_flags |= cpu_to_le16(SLI4_REGVFI_VP);
+ reg_vfi->vpi = cpu_to_le16(vpi);
+ memcpy(reg_vfi->wwpn, &sli_wwpn, sizeof(reg_vfi->wwpn));
+ reg_vfi->dw10_lportid_flags = cpu_to_le32(fc_id);
+
+ return 0;
+}
+
+int
+sli_cmd_reg_vpi(struct sli4 *sli4, void *buf, u32 fc_id, __be64 sli_wwpn,
+ u16 vpi, u16 vfi, bool update)
+{
+ struct sli4_cmd_reg_vpi *reg_vpi = buf;
+ u32 flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ reg_vpi->hdr.command = SLI4_MBX_CMD_REG_VPI;
+
+ flags = (fc_id & SLI4_REGVPI_LOCAL_N_PORTID);
+ if (update)
+ flags |= SLI4_REGVPI_UPD;
+ else
+ flags &= ~SLI4_REGVPI_UPD;
+
+ reg_vpi->dw2_lportid_flags = cpu_to_le32(flags);
+ memcpy(reg_vpi->wwpn, &sli_wwpn, sizeof(reg_vpi->wwpn));
+ reg_vpi->vpi = cpu_to_le16(vpi);
+ reg_vpi->vfi = cpu_to_le16(vfi);
+
+ return 0;
+}
+
+static int
+sli_cmd_request_features(struct sli4 *sli4, void *buf, u32 features_mask,
+ bool query)
+{
+ struct sli4_cmd_request_features *req_features = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ req_features->hdr.command = SLI4_MBX_CMD_RQST_FEATURES;
+
+ if (query)
+ req_features->dw1_qry = cpu_to_le32(SLI4_REQFEAT_QRY);
+
+ req_features->cmd = cpu_to_le32(features_mask);
+
+ return 0;
+}
+
+int
+sli_cmd_unreg_fcfi(struct sli4 *sli4, void *buf, u16 indicator)
+{
+ struct sli4_cmd_unreg_fcfi *unreg_fcfi = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ unreg_fcfi->hdr.command = SLI4_MBX_CMD_UNREG_FCFI;
+ unreg_fcfi->fcfi = cpu_to_le16(indicator);
+
+ return 0;
+}
+
+int
+sli_cmd_unreg_rpi(struct sli4 *sli4, void *buf, u16 indicator,
+ enum sli4_resource which, u32 fc_id)
+{
+ struct sli4_cmd_unreg_rpi *unreg_rpi = buf;
+ u32 flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ unreg_rpi->hdr.command = SLI4_MBX_CMD_UNREG_RPI;
+ switch (which) {
+ case SLI4_RSRC_RPI:
+ flags |= SLI4_UNREG_RPI_II_RPI;
+ if (fc_id == U32_MAX)
+ break;
+
+ flags |= SLI4_UNREG_RPI_DP;
+ unreg_rpi->dw2_dest_n_portid =
+ cpu_to_le32(fc_id & SLI4_UNREG_RPI_DEST_N_PORTID_MASK);
+ break;
+ case SLI4_RSRC_VPI:
+ flags |= SLI4_UNREG_RPI_II_VPI;
+ break;
+ case SLI4_RSRC_VFI:
+ flags |= SLI4_UNREG_RPI_II_VFI;
+ break;
+ case SLI4_RSRC_FCFI:
+ flags |= SLI4_UNREG_RPI_II_FCFI;
+ break;
+ default:
+ efc_log_info(sli4, "unknown type %#x\n", which);
+ return -EIO;
+ }
+
+ unreg_rpi->dw1w1_flags = cpu_to_le16(flags);
+ unreg_rpi->index = cpu_to_le16(indicator);
+
+ return 0;
+}
+
+int
+sli_cmd_unreg_vfi(struct sli4 *sli4, void *buf, u16 index, u32 which)
+{
+ struct sli4_cmd_unreg_vfi *unreg_vfi = buf;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ unreg_vfi->hdr.command = SLI4_MBX_CMD_UNREG_VFI;
+ switch (which) {
+ case SLI4_UNREG_TYPE_DOMAIN:
+ unreg_vfi->index = cpu_to_le16(index);
+ break;
+ case SLI4_UNREG_TYPE_FCF:
+ unreg_vfi->index = cpu_to_le16(index);
+ break;
+ case SLI4_UNREG_TYPE_ALL:
+ unreg_vfi->index = cpu_to_le16(U32_MAX);
+ break;
+ default:
+ return -EIO;
+ }
+
+ if (which != SLI4_UNREG_TYPE_DOMAIN)
+ unreg_vfi->dw2_flags = cpu_to_le16(SLI4_UNREG_VFI_II_FCFI);
+
+ return 0;
+}
+
+int
+sli_cmd_unreg_vpi(struct sli4 *sli4, void *buf, u16 indicator, u32 which)
+{
+ struct sli4_cmd_unreg_vpi *unreg_vpi = buf;
+ u32 flags = 0;
+
+ memset(buf, 0, SLI4_BMBX_SIZE);
+
+ unreg_vpi->hdr.command = SLI4_MBX_CMD_UNREG_VPI;
+ unreg_vpi->index = cpu_to_le16(indicator);
+ switch (which) {
+ case SLI4_UNREG_TYPE_PORT:
+ flags |= SLI4_UNREG_VPI_II_VPI;
+ break;
+ case SLI4_UNREG_TYPE_DOMAIN:
+ flags |= SLI4_UNREG_VPI_II_VFI;
+ break;
+ case SLI4_UNREG_TYPE_FCF:
+ flags |= SLI4_UNREG_VPI_II_FCFI;
+ break;
+ case SLI4_UNREG_TYPE_ALL:
+ /* override indicator */
+ unreg_vpi->index = cpu_to_le16(U32_MAX);
+ flags |= SLI4_UNREG_VPI_II_FCFI;
+ break;
+ default:
+ return -EIO;
+ }
+
+ unreg_vpi->dw2w0_flags = cpu_to_le16(flags);
+ return 0;
+}
+
+static int
+sli_cmd_common_modify_eq_delay(struct sli4 *sli4, void *buf,
+ struct sli4_queue *q, int num_q, u32 shift,
+ u32 delay_mult)
+{
+ struct sli4_rqst_cmn_modify_eq_delay *req = NULL;
+ int i;
+
+ req = sli_config_cmd_init(sli4, buf,
+ SLI4_CFG_PYLD_LENGTH(cmn_modify_eq_delay), NULL);
+ if (!req)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_MODIFY_EQ_DELAY,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN(cmn_modify_eq_delay));
+ req->num_eq = cpu_to_le32(num_q);
+
+ for (i = 0; i < num_q; i++) {
+ req->eq_delay_record[i].eq_id = cpu_to_le32(q[i].id);
+ req->eq_delay_record[i].phase = cpu_to_le32(shift);
+ req->eq_delay_record[i].delay_multiplier =
+ cpu_to_le32(delay_mult);
+ }
+
+ return 0;
+}
+
+void
+sli4_cmd_lowlevel_set_watchdog(struct sli4 *sli4, void *buf,
+ size_t size, u16 timeout)
+{
+ struct sli4_rqst_lowlevel_set_watchdog *req = NULL;
+
+ req = sli_config_cmd_init(sli4, buf,
+ SLI4_CFG_PYLD_LENGTH(lowlevel_set_watchdog), NULL);
+ if (!req)
+ return;
+
+ sli_cmd_fill_hdr(&req->hdr, SLI4_OPC_LOWLEVEL_SET_WATCHDOG,
+ SLI4_SUBSYSTEM_LOWLEVEL, CMD_V0,
+ SLI4_RQST_PYLD_LEN(lowlevel_set_watchdog));
+ req->watchdog_timeout = cpu_to_le16(timeout);
+}
+
+static int
+sli_cmd_common_get_cntl_attributes(struct sli4 *sli4, void *buf,
+ struct efc_dma *dma)
+{
+ struct sli4_rqst_hdr *hdr = NULL;
+
+ hdr = sli_config_cmd_init(sli4, buf, SLI4_RQST_CMDSZ(hdr), dma);
+ if (!hdr)
+ return -EIO;
+
+ hdr->opcode = SLI4_CMN_GET_CNTL_ATTRIBUTES;
+ hdr->subsystem = SLI4_SUBSYSTEM_COMMON;
+ hdr->request_length = cpu_to_le32(dma->size);
+
+ return 0;
+}
+
+static int
+sli_cmd_common_get_cntl_addl_attributes(struct sli4 *sli4, void *buf,
+ struct efc_dma *dma)
+{
+ struct sli4_rqst_hdr *hdr = NULL;
+
+ hdr = sli_config_cmd_init(sli4, buf, SLI4_RQST_CMDSZ(hdr), dma);
+ if (!hdr)
+ return -EIO;
+
+ hdr->opcode = SLI4_CMN_GET_CNTL_ADDL_ATTRS;
+ hdr->subsystem = SLI4_SUBSYSTEM_COMMON;
+ hdr->request_length = cpu_to_le32(dma->size);
+
+ return 0;
+}
+
+int
+sli_cmd_common_nop(struct sli4 *sli4, void *buf, uint64_t context)
+{
+ struct sli4_rqst_cmn_nop *nop = NULL;
+
+ nop = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(cmn_nop),
+ NULL);
+ if (!nop)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&nop->hdr, SLI4_CMN_NOP, SLI4_SUBSYSTEM_COMMON,
+ CMD_V0, SLI4_RQST_PYLD_LEN(cmn_nop));
+
+ memcpy(&nop->context, &context, sizeof(context));
+
+ return 0;
+}
+
+int
+sli_cmd_common_get_resource_extent_info(struct sli4 *sli4, void *buf, u16 rtype)
+{
+ struct sli4_rqst_cmn_get_resource_extent_info *ext = NULL;
+
+ ext = sli_config_cmd_init(sli4, buf,
+ SLI4_RQST_CMDSZ(cmn_get_resource_extent_info), NULL);
+ if (!ext)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&ext->hdr, SLI4_CMN_GET_RSC_EXTENT_INFO,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN(cmn_get_resource_extent_info));
+
+ ext->resource_type = cpu_to_le16(rtype);
+
+ return 0;
+}
+
+int
+sli_cmd_common_get_sli4_parameters(struct sli4 *sli4, void *buf)
+{
+ struct sli4_rqst_hdr *hdr = NULL;
+
+ hdr = sli_config_cmd_init(sli4, buf,
+ SLI4_CFG_PYLD_LENGTH(cmn_get_sli4_params), NULL);
+ if (!hdr)
+ return -EIO;
+
+ hdr->opcode = SLI4_CMN_GET_SLI4_PARAMS;
+ hdr->subsystem = SLI4_SUBSYSTEM_COMMON;
+ hdr->request_length = SLI4_RQST_PYLD_LEN(cmn_get_sli4_params);
+
+ return 0;
+}
+
+static int
+sli_cmd_common_get_port_name(struct sli4 *sli4, void *buf)
+{
+ struct sli4_rqst_cmn_get_port_name *pname;
+
+ pname = sli_config_cmd_init(sli4, buf,
+ SLI4_CFG_PYLD_LENGTH(cmn_get_port_name), NULL);
+ if (!pname)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&pname->hdr, SLI4_CMN_GET_PORT_NAME,
+ SLI4_SUBSYSTEM_COMMON, CMD_V1,
+ SLI4_RQST_PYLD_LEN(cmn_get_port_name));
+
+ /* Set the port type value (ethernet=0, FC=1) for V1 commands */
+ pname->port_type = SLI4_PORT_TYPE_FC;
+
+ return 0;
+}
+
+int
+sli_cmd_common_write_object(struct sli4 *sli4, void *buf, u16 noc,
+ u16 eof, u32 desired_write_length,
+ u32 offset, char *obj_name,
+ struct efc_dma *dma)
+{
+ struct sli4_rqst_cmn_write_object *wr_obj = NULL;
+ struct sli4_bde *bde;
+ u32 dwflags = 0;
+
+ wr_obj = sli_config_cmd_init(sli4, buf,
+ SLI4_RQST_CMDSZ(cmn_write_object) + sizeof(*bde), NULL);
+ if (!wr_obj)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&wr_obj->hdr, SLI4_CMN_WRITE_OBJECT,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN_VAR(cmn_write_object, sizeof(*bde)));
+
+ if (noc)
+ dwflags |= SLI4_RQ_DES_WRITE_LEN_NOC;
+ if (eof)
+ dwflags |= SLI4_RQ_DES_WRITE_LEN_EOF;
+ dwflags |= (desired_write_length & SLI4_RQ_DES_WRITE_LEN);
+
+ wr_obj->desired_write_len_dword = cpu_to_le32(dwflags);
+
+ wr_obj->write_offset = cpu_to_le32(offset);
+ strscpy(wr_obj->object_name, obj_name);
+ wr_obj->host_buffer_descriptor_count = cpu_to_le32(1);
+
+ bde = (struct sli4_bde *)wr_obj->host_buffer_descriptor;
+
+ /* Setup to transfer xfer_size bytes to device */
+ bde->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (desired_write_length & SLI4_BDE_LEN_MASK));
+ bde->u.data.low = cpu_to_le32(lower_32_bits(dma->phys));
+ bde->u.data.high = cpu_to_le32(upper_32_bits(dma->phys));
+
+ return 0;
+}
+
+int
+sli_cmd_common_delete_object(struct sli4 *sli4, void *buf, char *obj_name)
+{
+ struct sli4_rqst_cmn_delete_object *req = NULL;
+
+ req = sli_config_cmd_init(sli4, buf,
+ SLI4_RQST_CMDSZ(cmn_delete_object), NULL);
+ if (!req)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_DELETE_OBJECT,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN(cmn_delete_object));
+
+ strscpy(req->object_name, obj_name);
+ return 0;
+}
+
+int
+sli_cmd_common_read_object(struct sli4 *sli4, void *buf, u32 desired_read_len,
+ u32 offset, char *obj_name, struct efc_dma *dma)
+{
+ struct sli4_rqst_cmn_read_object *rd_obj = NULL;
+ struct sli4_bde *bde;
+
+ rd_obj = sli_config_cmd_init(sli4, buf,
+ SLI4_RQST_CMDSZ(cmn_read_object) + sizeof(*bde), NULL);
+ if (!rd_obj)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&rd_obj->hdr, SLI4_CMN_READ_OBJECT,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN_VAR(cmn_read_object, sizeof(*bde)));
+ rd_obj->desired_read_length_dword =
+ cpu_to_le32(desired_read_len & SLI4_REQ_DESIRE_READLEN);
+
+ rd_obj->read_offset = cpu_to_le32(offset);
+ strscpy(rd_obj->object_name, obj_name);
+ rd_obj->host_buffer_descriptor_count = cpu_to_le32(1);
+
+ bde = (struct sli4_bde *)rd_obj->host_buffer_descriptor;
+
+ /* Setup to transfer xfer_size bytes to device */
+ bde->bde_type_buflen =
+ cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) |
+ (desired_read_len & SLI4_BDE_LEN_MASK));
+ if (dma) {
+ bde->u.data.low = cpu_to_le32(lower_32_bits(dma->phys));
+ bde->u.data.high = cpu_to_le32(upper_32_bits(dma->phys));
+ } else {
+ bde->u.data.low = 0;
+ bde->u.data.high = 0;
+ }
+
+ return 0;
+}
+
+int
+sli_cmd_dmtf_exec_clp_cmd(struct sli4 *sli4, void *buf, struct efc_dma *cmd,
+ struct efc_dma *resp)
+{
+ struct sli4_rqst_dmtf_exec_clp_cmd *clp_cmd = NULL;
+
+ clp_cmd = sli_config_cmd_init(sli4, buf,
+ SLI4_RQST_CMDSZ(dmtf_exec_clp_cmd), NULL);
+ if (!clp_cmd)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&clp_cmd->hdr, DMTF_EXEC_CLP_CMD, SLI4_SUBSYSTEM_DMTF,
+ CMD_V0, SLI4_RQST_PYLD_LEN(dmtf_exec_clp_cmd));
+
+ clp_cmd->cmd_buf_length = cpu_to_le32(cmd->size);
+ clp_cmd->cmd_buf_addr_low = cpu_to_le32(lower_32_bits(cmd->phys));
+ clp_cmd->cmd_buf_addr_high = cpu_to_le32(upper_32_bits(cmd->phys));
+ clp_cmd->resp_buf_length = cpu_to_le32(resp->size);
+ clp_cmd->resp_buf_addr_low = cpu_to_le32(lower_32_bits(resp->phys));
+ clp_cmd->resp_buf_addr_high = cpu_to_le32(upper_32_bits(resp->phys));
+ return 0;
+}
+
+int
+sli_cmd_common_set_dump_location(struct sli4 *sli4, void *buf, bool query,
+ bool is_buffer_list,
+ struct efc_dma *buffer, u8 fdb)
+{
+ struct sli4_rqst_cmn_set_dump_location *set_dump_loc = NULL;
+ u32 buffer_length_flag = 0;
+
+ set_dump_loc = sli_config_cmd_init(sli4, buf,
+ SLI4_RQST_CMDSZ(cmn_set_dump_location), NULL);
+ if (!set_dump_loc)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&set_dump_loc->hdr, SLI4_CMN_SET_DUMP_LOCATION,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN(cmn_set_dump_location));
+
+ if (is_buffer_list)
+ buffer_length_flag |= SLI4_CMN_SET_DUMP_BLP;
+
+ if (query)
+ buffer_length_flag |= SLI4_CMN_SET_DUMP_QRY;
+
+ if (fdb)
+ buffer_length_flag |= SLI4_CMN_SET_DUMP_FDB;
+
+ if (buffer) {
+ set_dump_loc->buf_addr_low =
+ cpu_to_le32(lower_32_bits(buffer->phys));
+ set_dump_loc->buf_addr_high =
+ cpu_to_le32(upper_32_bits(buffer->phys));
+
+ buffer_length_flag |=
+ buffer->len & SLI4_CMN_SET_DUMP_BUFFER_LEN;
+ } else {
+ set_dump_loc->buf_addr_low = 0;
+ set_dump_loc->buf_addr_high = 0;
+ set_dump_loc->buffer_length_dword = 0;
+ }
+ set_dump_loc->buffer_length_dword = cpu_to_le32(buffer_length_flag);
+ return 0;
+}
+
+int
+sli_cmd_common_set_features(struct sli4 *sli4, void *buf, u32 feature,
+ u32 param_len, void *parameter)
+{
+ struct sli4_rqst_cmn_set_features *cmd = NULL;
+
+ cmd = sli_config_cmd_init(sli4, buf,
+ SLI4_RQST_CMDSZ(cmn_set_features), NULL);
+ if (!cmd)
+ return -EIO;
+
+ sli_cmd_fill_hdr(&cmd->hdr, SLI4_CMN_SET_FEATURES,
+ SLI4_SUBSYSTEM_COMMON, CMD_V0,
+ SLI4_RQST_PYLD_LEN(cmn_set_features));
+
+ cmd->feature = cpu_to_le32(feature);
+ cmd->param_len = cpu_to_le32(param_len);
+ memcpy(cmd->params, parameter, param_len);
+
+ return 0;
+}
+
+int
+sli_cqe_mq(struct sli4 *sli4, void *buf)
+{
+ struct sli4_mcqe *mcqe = buf;
+ u32 dwflags = le32_to_cpu(mcqe->dw3_flags);
+ /*
+ * Firmware can split mbx completions into two MCQEs: first with only
+ * the "consumed" bit set and a second with the "complete" bit set.
+ * Thus, ignore MCQE unless "complete" is set.
+ */
+ if (!(dwflags & SLI4_MCQE_COMPLETED))
+ return SLI4_MCQE_STATUS_NOT_COMPLETED;
+
+ if (le16_to_cpu(mcqe->completion_status)) {
+ efc_log_info(sli4, "status(st=%#x ext=%#x con=%d cmp=%d ae=%d val=%d)\n",
+ le16_to_cpu(mcqe->completion_status),
+ le16_to_cpu(mcqe->extended_status),
+ (dwflags & SLI4_MCQE_CONSUMED),
+ (dwflags & SLI4_MCQE_COMPLETED),
+ (dwflags & SLI4_MCQE_AE),
+ (dwflags & SLI4_MCQE_VALID));
+ }
+
+ return le16_to_cpu(mcqe->completion_status);
+}
+
+int
+sli_cqe_async(struct sli4 *sli4, void *buf)
+{
+ struct sli4_acqe *acqe = buf;
+ int rc = -EIO;
+
+ if (!buf) {
+ efc_log_err(sli4, "bad parameter sli4=%p buf=%p\n", sli4, buf);
+ return -EIO;
+ }
+
+ switch (acqe->event_code) {
+ case SLI4_ACQE_EVENT_CODE_LINK_STATE:
+ efc_log_info(sli4, "Unsupported by FC link, evt code:%#x\n",
+ acqe->event_code);
+ break;
+ case SLI4_ACQE_EVENT_CODE_GRP_5:
+ efc_log_info(sli4, "ACQE GRP5\n");
+ break;
+ case SLI4_ACQE_EVENT_CODE_SLI_PORT_EVENT:
+ efc_log_info(sli4, "ACQE SLI Port, type=0x%x, data1,2=0x%08x,0x%08x\n",
+ acqe->event_type,
+ le32_to_cpu(acqe->event_data[0]),
+ le32_to_cpu(acqe->event_data[1]));
+ break;
+ case SLI4_ACQE_EVENT_CODE_FC_LINK_EVENT:
+ rc = sli_fc_process_link_attention(sli4, buf);
+ break;
+ default:
+ efc_log_info(sli4, "ACQE unknown=%#x\n", acqe->event_code);
+ }
+
+ return rc;
+}
+
+bool
+sli_fw_ready(struct sli4 *sli4)
+{
+ u32 val;
+
+ /* Determine if the chip FW is in a ready state */
+ val = sli_reg_read_status(sli4);
+ return (val & SLI4_PORT_STATUS_RDY) ? 1 : 0;
+}
+
+static bool
+sli_wait_for_fw_ready(struct sli4 *sli4, u32 timeout_ms)
+{
+ unsigned long end;
+
+ end = jiffies + msecs_to_jiffies(timeout_ms);
+
+ do {
+ if (sli_fw_ready(sli4))
+ return true;
+
+ usleep_range(1000, 2000);
+ } while (time_before(jiffies, end));
+
+ return false;
+}
+
+static bool
+sli_sliport_reset(struct sli4 *sli4)
+{
+ bool rc;
+ u32 val;
+
+ val = SLI4_PORT_CTRL_IP;
+ /* Initialize port, endian */
+ writel(val, (sli4->reg[0] + SLI4_PORT_CTRL_REG));
+
+ rc = sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC);
+ if (!rc)
+ efc_log_crit(sli4, "port failed to become ready after initialization\n");
+
+ return rc;
+}
+
+static bool
+sli_fw_init(struct sli4 *sli4)
+{
+ /*
+ * Is firmware ready for operation?
+ */
+ if (!sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC)) {
+ efc_log_crit(sli4, "FW status is NOT ready\n");
+ return false;
+ }
+
+ /*
+ * Reset port to a known state
+ */
+ return sli_sliport_reset(sli4);
+}
+
+static int
+sli_request_features(struct sli4 *sli4, u32 *features, bool query)
+{
+ struct sli4_cmd_request_features *req_features = sli4->bmbx.virt;
+
+ if (sli_cmd_request_features(sli4, sli4->bmbx.virt, *features, query)) {
+ efc_log_err(sli4, "bad REQUEST_FEATURES write\n");
+ return -EIO;
+ }
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox write fail\n");
+ return -EIO;
+ }
+
+ if (le16_to_cpu(req_features->hdr.status)) {
+ efc_log_err(sli4, "REQUEST_FEATURES bad status %#x\n",
+ le16_to_cpu(req_features->hdr.status));
+ return -EIO;
+ }
+
+ *features = le32_to_cpu(req_features->resp);
+ return 0;
+}
+
+void
+sli_calc_max_qentries(struct sli4 *sli4)
+{
+ enum sli4_qtype q;
+ u32 qentries;
+
+ for (q = SLI4_QTYPE_EQ; q < SLI4_QTYPE_MAX; q++) {
+ sli4->qinfo.max_qentries[q] =
+ sli_convert_mask_to_count(sli4->qinfo.count_method[q],
+ sli4->qinfo.count_mask[q]);
+ }
+
+ /* single, contiguous DMA allocations will be called for each queue
+ * of size (max_qentries * queue entry size); since these can be large,
+ * check against the OS max DMA allocation size
+ */
+ for (q = SLI4_QTYPE_EQ; q < SLI4_QTYPE_MAX; q++) {
+ qentries = sli4->qinfo.max_qentries[q];
+
+ efc_log_info(sli4, "[%s]: max_qentries from %d to %d\n",
+ SLI4_QNAME[q],
+ sli4->qinfo.max_qentries[q], qentries);
+ sli4->qinfo.max_qentries[q] = qentries;
+ }
+}
+
+static int
+sli_get_read_config(struct sli4 *sli4)
+{
+ struct sli4_rsp_read_config *conf = sli4->bmbx.virt;
+ u32 i, total;
+ u32 *base;
+
+ if (sli_cmd_read_config(sli4, sli4->bmbx.virt)) {
+ efc_log_err(sli4, "bad READ_CONFIG write\n");
+ return -EIO;
+ }
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox fail (READ_CONFIG)\n");
+ return -EIO;
+ }
+
+ if (le16_to_cpu(conf->hdr.status)) {
+ efc_log_err(sli4, "READ_CONFIG bad status %#x\n",
+ le16_to_cpu(conf->hdr.status));
+ return -EIO;
+ }
+
+ sli4->params.has_extents =
+ le32_to_cpu(conf->ext_dword) & SLI4_READ_CFG_RESP_RESOURCE_EXT;
+ if (sli4->params.has_extents) {
+ efc_log_err(sli4, "extents not supported\n");
+ return -EIO;
+ }
+
+ base = sli4->ext[0].base;
+ if (!base) {
+ int size = SLI4_RSRC_MAX * sizeof(u32);
+
+ base = kzalloc(size, GFP_KERNEL);
+ if (!base)
+ return -EIO;
+ }
+
+ for (i = 0; i < SLI4_RSRC_MAX; i++) {
+ sli4->ext[i].number = 1;
+ sli4->ext[i].n_alloc = 0;
+ sli4->ext[i].base = &base[i];
+ }
+
+ sli4->ext[SLI4_RSRC_VFI].base[0] = le16_to_cpu(conf->vfi_base);
+ sli4->ext[SLI4_RSRC_VFI].size = le16_to_cpu(conf->vfi_count);
+
+ sli4->ext[SLI4_RSRC_VPI].base[0] = le16_to_cpu(conf->vpi_base);
+ sli4->ext[SLI4_RSRC_VPI].size = le16_to_cpu(conf->vpi_count);
+
+ sli4->ext[SLI4_RSRC_RPI].base[0] = le16_to_cpu(conf->rpi_base);
+ sli4->ext[SLI4_RSRC_RPI].size = le16_to_cpu(conf->rpi_count);
+
+ sli4->ext[SLI4_RSRC_XRI].base[0] = le16_to_cpu(conf->xri_base);
+ sli4->ext[SLI4_RSRC_XRI].size = le16_to_cpu(conf->xri_count);
+
+ sli4->ext[SLI4_RSRC_FCFI].base[0] = 0;
+ sli4->ext[SLI4_RSRC_FCFI].size = le16_to_cpu(conf->fcfi_count);
+
+ for (i = 0; i < SLI4_RSRC_MAX; i++) {
+ total = sli4->ext[i].number * sli4->ext[i].size;
+ sli4->ext[i].use_map = bitmap_zalloc(total, GFP_KERNEL);
+ if (!sli4->ext[i].use_map) {
+ efc_log_err(sli4, "bitmap memory allocation failed %d\n",
+ i);
+ return -EIO;
+ }
+ sli4->ext[i].map_size = total;
+ }
+
+ sli4->topology = (le32_to_cpu(conf->topology_dword) &
+ SLI4_READ_CFG_RESP_TOPOLOGY) >> 24;
+ switch (sli4->topology) {
+ case SLI4_READ_CFG_TOPO_FC:
+ efc_log_info(sli4, "FC (unknown)\n");
+ break;
+ case SLI4_READ_CFG_TOPO_NON_FC_AL:
+ efc_log_info(sli4, "FC (direct attach)\n");
+ break;
+ case SLI4_READ_CFG_TOPO_FC_AL:
+ efc_log_info(sli4, "FC (arbitrated loop)\n");
+ break;
+ default:
+ efc_log_info(sli4, "bad topology %#x\n", sli4->topology);
+ }
+
+ sli4->e_d_tov = le16_to_cpu(conf->e_d_tov);
+ sli4->r_a_tov = le16_to_cpu(conf->r_a_tov);
+
+ sli4->link_module_type = le16_to_cpu(conf->lmt);
+
+ sli4->qinfo.max_qcount[SLI4_QTYPE_EQ] = le16_to_cpu(conf->eq_count);
+ sli4->qinfo.max_qcount[SLI4_QTYPE_CQ] = le16_to_cpu(conf->cq_count);
+ sli4->qinfo.max_qcount[SLI4_QTYPE_WQ] = le16_to_cpu(conf->wq_count);
+ sli4->qinfo.max_qcount[SLI4_QTYPE_RQ] = le16_to_cpu(conf->rq_count);
+
+ /*
+ * READ_CONFIG doesn't give the max number of MQ. Applications
+ * will typically want 1, but we may need another at some future
+ * date. Dummy up a "max" MQ count here.
+ */
+ sli4->qinfo.max_qcount[SLI4_QTYPE_MQ] = SLI4_USER_MQ_COUNT;
+ return 0;
+}
+
+static int
+sli_get_sli4_parameters(struct sli4 *sli4)
+{
+ struct sli4_rsp_cmn_get_sli4_params *parms;
+ u32 dw_loopback;
+ u32 dw_eq_pg_cnt;
+ u32 dw_cq_pg_cnt;
+ u32 dw_mq_pg_cnt;
+ u32 dw_wq_pg_cnt;
+ u32 dw_rq_pg_cnt;
+ u32 dw_sgl_pg_cnt;
+
+ if (sli_cmd_common_get_sli4_parameters(sli4, sli4->bmbx.virt))
+ return -EIO;
+
+ parms = (struct sli4_rsp_cmn_get_sli4_params *)
+ (((u8 *)sli4->bmbx.virt) +
+ offsetof(struct sli4_cmd_sli_config, payload.embed));
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox write fail\n");
+ return -EIO;
+ }
+
+ if (parms->hdr.status) {
+ efc_log_err(sli4, "COMMON_GET_SLI4_PARAMETERS bad status %#x",
+ parms->hdr.status);
+ efc_log_err(sli4, "additional status %#x\n",
+ parms->hdr.additional_status);
+ return -EIO;
+ }
+
+ dw_loopback = le32_to_cpu(parms->dw16_loopback_scope);
+ dw_eq_pg_cnt = le32_to_cpu(parms->dw6_eq_page_cnt);
+ dw_cq_pg_cnt = le32_to_cpu(parms->dw8_cq_page_cnt);
+ dw_mq_pg_cnt = le32_to_cpu(parms->dw10_mq_page_cnt);
+ dw_wq_pg_cnt = le32_to_cpu(parms->dw12_wq_page_cnt);
+ dw_rq_pg_cnt = le32_to_cpu(parms->dw14_rq_page_cnt);
+
+ sli4->params.auto_reg = (dw_loopback & SLI4_PARAM_AREG);
+ sli4->params.auto_xfer_rdy = (dw_loopback & SLI4_PARAM_AGXF);
+ sli4->params.hdr_template_req = (dw_loopback & SLI4_PARAM_HDRR);
+ sli4->params.t10_dif_inline_capable = (dw_loopback & SLI4_PARAM_TIMM);
+ sli4->params.t10_dif_separate_capable = (dw_loopback & SLI4_PARAM_TSMM);
+
+ sli4->params.mq_create_version = GET_Q_CREATE_VERSION(dw_mq_pg_cnt);
+ sli4->params.cq_create_version = GET_Q_CREATE_VERSION(dw_cq_pg_cnt);
+
+ sli4->rq_min_buf_size = le16_to_cpu(parms->min_rq_buffer_size);
+ sli4->rq_max_buf_size = le32_to_cpu(parms->max_rq_buffer_size);
+
+ sli4->qinfo.qpage_count[SLI4_QTYPE_EQ] =
+ (dw_eq_pg_cnt & SLI4_PARAM_EQ_PAGE_CNT_MASK);
+ sli4->qinfo.qpage_count[SLI4_QTYPE_CQ] =
+ (dw_cq_pg_cnt & SLI4_PARAM_CQ_PAGE_CNT_MASK);
+ sli4->qinfo.qpage_count[SLI4_QTYPE_MQ] =
+ (dw_mq_pg_cnt & SLI4_PARAM_MQ_PAGE_CNT_MASK);
+ sli4->qinfo.qpage_count[SLI4_QTYPE_WQ] =
+ (dw_wq_pg_cnt & SLI4_PARAM_WQ_PAGE_CNT_MASK);
+ sli4->qinfo.qpage_count[SLI4_QTYPE_RQ] =
+ (dw_rq_pg_cnt & SLI4_PARAM_RQ_PAGE_CNT_MASK);
+
+ /* save count methods and masks for each queue type */
+
+ sli4->qinfo.count_mask[SLI4_QTYPE_EQ] =
+ le16_to_cpu(parms->eqe_count_mask);
+ sli4->qinfo.count_method[SLI4_QTYPE_EQ] =
+ GET_Q_CNT_METHOD(dw_eq_pg_cnt);
+
+ sli4->qinfo.count_mask[SLI4_QTYPE_CQ] =
+ le16_to_cpu(parms->cqe_count_mask);
+ sli4->qinfo.count_method[SLI4_QTYPE_CQ] =
+ GET_Q_CNT_METHOD(dw_cq_pg_cnt);
+
+ sli4->qinfo.count_mask[SLI4_QTYPE_MQ] =
+ le16_to_cpu(parms->mqe_count_mask);
+ sli4->qinfo.count_method[SLI4_QTYPE_MQ] =
+ GET_Q_CNT_METHOD(dw_mq_pg_cnt);
+
+ sli4->qinfo.count_mask[SLI4_QTYPE_WQ] =
+ le16_to_cpu(parms->wqe_count_mask);
+ sli4->qinfo.count_method[SLI4_QTYPE_WQ] =
+ GET_Q_CNT_METHOD(dw_wq_pg_cnt);
+
+ sli4->qinfo.count_mask[SLI4_QTYPE_RQ] =
+ le16_to_cpu(parms->rqe_count_mask);
+ sli4->qinfo.count_method[SLI4_QTYPE_RQ] =
+ GET_Q_CNT_METHOD(dw_rq_pg_cnt);
+
+ /* now calculate max queue entries */
+ sli_calc_max_qentries(sli4);
+
+ dw_sgl_pg_cnt = le32_to_cpu(parms->dw18_sgl_page_cnt);
+
+ /* max # of pages */
+ sli4->max_sgl_pages = (dw_sgl_pg_cnt & SLI4_PARAM_SGL_PAGE_CNT_MASK);
+
+ /* bit map of available sizes */
+ sli4->sgl_page_sizes = (dw_sgl_pg_cnt &
+ SLI4_PARAM_SGL_PAGE_SZS_MASK) >> 8;
+ /* ignore HLM here. Use value from REQUEST_FEATURES */
+ sli4->sge_supported_length = le32_to_cpu(parms->sge_supported_length);
+ sli4->params.sgl_pre_reg_required = (dw_loopback & SLI4_PARAM_SGLR);
+ /* default to using pre-registered SGL's */
+ sli4->params.sgl_pre_registered = true;
+
+ sli4->params.perf_hint = dw_loopback & SLI4_PARAM_PHON;
+ sli4->params.perf_wq_id_association = (dw_loopback & SLI4_PARAM_PHWQ);
+
+ sli4->rq_batch = (le16_to_cpu(parms->dw15w1_rq_db_window) &
+ SLI4_PARAM_RQ_DB_WINDOW_MASK) >> 12;
+
+ /* Use the highest available WQE size. */
+ if (((dw_wq_pg_cnt & SLI4_PARAM_WQE_SZS_MASK) >> 8) &
+ SLI4_128BYTE_WQE_SUPPORT)
+ sli4->wqe_size = SLI4_WQE_EXT_BYTES;
+ else
+ sli4->wqe_size = SLI4_WQE_BYTES;
+
+ return 0;
+}
+
+static int
+sli_get_ctrl_attributes(struct sli4 *sli4)
+{
+ struct sli4_rsp_cmn_get_cntl_attributes *attr;
+ struct sli4_rsp_cmn_get_cntl_addl_attributes *add_attr;
+ struct efc_dma data;
+ u32 psize;
+
+ /*
+ * Issue COMMON_GET_CNTL_ATTRIBUTES to get port_number. Temporarily
+ * uses VPD DMA buffer as the response won't fit in the embedded
+ * buffer.
+ */
+ memset(sli4->vpd_data.virt, 0, sli4->vpd_data.size);
+ if (sli_cmd_common_get_cntl_attributes(sli4, sli4->bmbx.virt,
+ &sli4->vpd_data)) {
+ efc_log_err(sli4, "bad COMMON_GET_CNTL_ATTRIBUTES write\n");
+ return -EIO;
+ }
+
+ attr = sli4->vpd_data.virt;
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox write fail\n");
+ return -EIO;
+ }
+
+ if (attr->hdr.status) {
+ efc_log_err(sli4, "COMMON_GET_CNTL_ATTRIBUTES bad status %#x",
+ attr->hdr.status);
+ efc_log_err(sli4, "additional status %#x\n",
+ attr->hdr.additional_status);
+ return -EIO;
+ }
+
+ sli4->port_number = attr->port_num_type_flags & SLI4_CNTL_ATTR_PORTNUM;
+
+ memcpy(sli4->bios_version_string, attr->bios_version_str,
+ sizeof(sli4->bios_version_string));
+
+ /* get additional attributes */
+ psize = sizeof(struct sli4_rsp_cmn_get_cntl_addl_attributes);
+ data.size = psize;
+ data.virt = dma_alloc_coherent(&sli4->pci->dev, data.size,
+ &data.phys, GFP_KERNEL);
+ if (!data.virt) {
+ memset(&data, 0, sizeof(struct efc_dma));
+ efc_log_err(sli4, "Failed to allocate memory for GET_CNTL_ADDL_ATTR\n");
+ return -EIO;
+ }
+
+ if (sli_cmd_common_get_cntl_addl_attributes(sli4, sli4->bmbx.virt,
+ &data)) {
+ efc_log_err(sli4, "bad GET_CNTL_ADDL_ATTR write\n");
+ dma_free_coherent(&sli4->pci->dev, data.size,
+ data.virt, data.phys);
+ return -EIO;
+ }
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "mailbox fail (GET_CNTL_ADDL_ATTR)\n");
+ dma_free_coherent(&sli4->pci->dev, data.size,
+ data.virt, data.phys);
+ return -EIO;
+ }
+
+ add_attr = data.virt;
+ if (add_attr->hdr.status) {
+ efc_log_err(sli4, "GET_CNTL_ADDL_ATTR bad status %#x\n",
+ add_attr->hdr.status);
+ dma_free_coherent(&sli4->pci->dev, data.size,
+ data.virt, data.phys);
+ return -EIO;
+ }
+
+ memcpy(sli4->ipl_name, add_attr->ipl_file_name, sizeof(sli4->ipl_name));
+
+ efc_log_info(sli4, "IPL:%s\n", (char *)sli4->ipl_name);
+
+ dma_free_coherent(&sli4->pci->dev, data.size, data.virt,
+ data.phys);
+ memset(&data, 0, sizeof(struct efc_dma));
+ return 0;
+}
+
+static int
+sli_get_fw_rev(struct sli4 *sli4)
+{
+ struct sli4_cmd_read_rev *read_rev = sli4->bmbx.virt;
+
+ if (sli_cmd_read_rev(sli4, sli4->bmbx.virt, &sli4->vpd_data))
+ return -EIO;
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox write fail (READ_REV)\n");
+ return -EIO;
+ }
+
+ if (le16_to_cpu(read_rev->hdr.status)) {
+ efc_log_err(sli4, "READ_REV bad status %#x\n",
+ le16_to_cpu(read_rev->hdr.status));
+ return -EIO;
+ }
+
+ sli4->fw_rev[0] = le32_to_cpu(read_rev->first_fw_id);
+ memcpy(sli4->fw_name[0], read_rev->first_fw_name,
+ sizeof(sli4->fw_name[0]));
+
+ sli4->fw_rev[1] = le32_to_cpu(read_rev->second_fw_id);
+ memcpy(sli4->fw_name[1], read_rev->second_fw_name,
+ sizeof(sli4->fw_name[1]));
+
+ sli4->hw_rev[0] = le32_to_cpu(read_rev->first_hw_rev);
+ sli4->hw_rev[1] = le32_to_cpu(read_rev->second_hw_rev);
+ sli4->hw_rev[2] = le32_to_cpu(read_rev->third_hw_rev);
+
+ efc_log_info(sli4, "FW1:%s (%08x) / FW2:%s (%08x)\n",
+ read_rev->first_fw_name, le32_to_cpu(read_rev->first_fw_id),
+ read_rev->second_fw_name, le32_to_cpu(read_rev->second_fw_id));
+
+ efc_log_info(sli4, "HW1: %08x / HW2: %08x\n",
+ le32_to_cpu(read_rev->first_hw_rev),
+ le32_to_cpu(read_rev->second_hw_rev));
+
+ /* Check that all VPD data was returned */
+ if (le32_to_cpu(read_rev->returned_vpd_length) !=
+ le32_to_cpu(read_rev->actual_vpd_length)) {
+ efc_log_info(sli4, "VPD length: avail=%d return=%d actual=%d\n",
+ le32_to_cpu(read_rev->available_length_dword) &
+ SLI4_READ_REV_AVAILABLE_LENGTH,
+ le32_to_cpu(read_rev->returned_vpd_length),
+ le32_to_cpu(read_rev->actual_vpd_length));
+ }
+ sli4->vpd_length = le32_to_cpu(read_rev->returned_vpd_length);
+ return 0;
+}
+
+static int
+sli_get_config(struct sli4 *sli4)
+{
+ struct sli4_rsp_cmn_get_port_name *port_name;
+ struct sli4_cmd_read_nvparms *read_nvparms;
+
+ /*
+ * Read the device configuration
+ */
+ if (sli_get_read_config(sli4))
+ return -EIO;
+
+ if (sli_get_sli4_parameters(sli4))
+ return -EIO;
+
+ if (sli_get_ctrl_attributes(sli4))
+ return -EIO;
+
+ if (sli_cmd_common_get_port_name(sli4, sli4->bmbx.virt))
+ return -EIO;
+
+ port_name = (struct sli4_rsp_cmn_get_port_name *)
+ (((u8 *)sli4->bmbx.virt) +
+ offsetof(struct sli4_cmd_sli_config, payload.embed));
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox fail (GET_PORT_NAME)\n");
+ return -EIO;
+ }
+
+ sli4->port_name[0] = port_name->port_name[sli4->port_number];
+ sli4->port_name[1] = '\0';
+
+ if (sli_get_fw_rev(sli4))
+ return -EIO;
+
+ if (sli_cmd_read_nvparms(sli4, sli4->bmbx.virt)) {
+ efc_log_err(sli4, "bad READ_NVPARMS write\n");
+ return -EIO;
+ }
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox fail (READ_NVPARMS)\n");
+ return -EIO;
+ }
+
+ read_nvparms = sli4->bmbx.virt;
+ if (le16_to_cpu(read_nvparms->hdr.status)) {
+ efc_log_err(sli4, "READ_NVPARMS bad status %#x\n",
+ le16_to_cpu(read_nvparms->hdr.status));
+ return -EIO;
+ }
+
+ memcpy(sli4->wwpn, read_nvparms->wwpn, sizeof(sli4->wwpn));
+ memcpy(sli4->wwnn, read_nvparms->wwnn, sizeof(sli4->wwnn));
+
+ efc_log_info(sli4, "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ sli4->wwpn[0], sli4->wwpn[1], sli4->wwpn[2], sli4->wwpn[3],
+ sli4->wwpn[4], sli4->wwpn[5], sli4->wwpn[6], sli4->wwpn[7]);
+ efc_log_info(sli4, "WWNN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ sli4->wwnn[0], sli4->wwnn[1], sli4->wwnn[2], sli4->wwnn[3],
+ sli4->wwnn[4], sli4->wwnn[5], sli4->wwnn[6], sli4->wwnn[7]);
+
+ return 0;
+}
+
+int
+sli_setup(struct sli4 *sli4, void *os, struct pci_dev *pdev,
+ void __iomem *reg[])
+{
+ u32 intf = U32_MAX;
+ u32 pci_class_rev = 0;
+ u32 rev_id = 0;
+ u32 family = 0;
+ u32 asic_id = 0;
+ u32 i;
+ struct sli4_asic_entry_t *asic;
+
+ memset(sli4, 0, sizeof(struct sli4));
+
+ sli4->os = os;
+ sli4->pci = pdev;
+
+ for (i = 0; i < 6; i++)
+ sli4->reg[i] = reg[i];
+ /*
+ * Read the SLI_INTF register to discover the register layout
+ * and other capability information
+ */
+ if (pci_read_config_dword(pdev, SLI4_INTF_REG, &intf))
+ return -EIO;
+
+ if ((intf & SLI4_INTF_VALID_MASK) != (u32)SLI4_INTF_VALID_VALUE) {
+ efc_log_err(sli4, "SLI_INTF is not valid\n");
+ return -EIO;
+ }
+
+ /* driver only support SLI-4 */
+ if ((intf & SLI4_INTF_REV_MASK) != SLI4_INTF_REV_S4) {
+ efc_log_err(sli4, "Unsupported SLI revision (intf=%#x)\n", intf);
+ return -EIO;
+ }
+
+ sli4->sli_family = intf & SLI4_INTF_FAMILY_MASK;
+
+ sli4->if_type = intf & SLI4_INTF_IF_TYPE_MASK;
+ efc_log_info(sli4, "status=%#x error1=%#x error2=%#x\n",
+ sli_reg_read_status(sli4),
+ sli_reg_read_err1(sli4),
+ sli_reg_read_err2(sli4));
+
+ /*
+ * set the ASIC type and revision
+ */
+ if (pci_read_config_dword(pdev, PCI_CLASS_REVISION, &pci_class_rev))
+ return -EIO;
+
+ rev_id = pci_class_rev & 0xff;
+ family = sli4->sli_family;
+ if (family == SLI4_FAMILY_CHECK_ASIC_TYPE) {
+ if (!pci_read_config_dword(pdev, SLI4_ASIC_ID_REG, &asic_id))
+ family = asic_id & SLI4_ASIC_GEN_MASK;
+ }
+
+ for (i = 0, asic = sli4_asic_table; i < ARRAY_SIZE(sli4_asic_table);
+ i++, asic++) {
+ if (rev_id == asic->rev_id && family == asic->family) {
+ sli4->asic_type = family;
+ sli4->asic_rev = rev_id;
+ break;
+ }
+ }
+ /* Fail if no matching asic type/rev was found */
+ if (!sli4->asic_type) {
+ efc_log_err(sli4, "no matching asic family/rev found: %02x/%02x\n",
+ family, rev_id);
+ return -EIO;
+ }
+
+ /*
+ * The bootstrap mailbox is equivalent to a MQ with a single 256 byte
+ * entry, a CQ with a single 16 byte entry, and no event queue.
+ * Alignment must be 16 bytes as the low order address bits in the
+ * address register are also control / status.
+ */
+ sli4->bmbx.size = SLI4_BMBX_SIZE + sizeof(struct sli4_mcqe);
+ sli4->bmbx.virt = dma_alloc_coherent(&pdev->dev, sli4->bmbx.size,
+ &sli4->bmbx.phys, GFP_KERNEL);
+ if (!sli4->bmbx.virt) {
+ memset(&sli4->bmbx, 0, sizeof(struct efc_dma));
+ efc_log_err(sli4, "bootstrap mailbox allocation failed\n");
+ return -EIO;
+ }
+
+ if (sli4->bmbx.phys & SLI4_BMBX_MASK_LO) {
+ efc_log_err(sli4, "bad alignment for bootstrap mailbox\n");
+ return -EIO;
+ }
+
+ efc_log_info(sli4, "bmbx v=%p p=0x%x %08x s=%zd\n", sli4->bmbx.virt,
+ upper_32_bits(sli4->bmbx.phys),
+ lower_32_bits(sli4->bmbx.phys), sli4->bmbx.size);
+
+ /* 4096 is arbitrary. What should this value actually be? */
+ sli4->vpd_data.size = 4096;
+ sli4->vpd_data.virt = dma_alloc_coherent(&pdev->dev,
+ sli4->vpd_data.size,
+ &sli4->vpd_data.phys,
+ GFP_KERNEL);
+ if (!sli4->vpd_data.virt) {
+ memset(&sli4->vpd_data, 0, sizeof(struct efc_dma));
+ /* Note that failure isn't fatal in this specific case */
+ efc_log_info(sli4, "VPD buffer allocation failed\n");
+ }
+
+ if (!sli_fw_init(sli4)) {
+ efc_log_err(sli4, "FW initialization failed\n");
+ return -EIO;
+ }
+
+ /*
+ * Set one of fcpi(initiator), fcpt(target), fcpc(combined) to true
+ * in addition to any other desired features
+ */
+ sli4->features = (SLI4_REQFEAT_IAAB | SLI4_REQFEAT_NPIV |
+ SLI4_REQFEAT_DIF | SLI4_REQFEAT_VF |
+ SLI4_REQFEAT_FCPC | SLI4_REQFEAT_IAAR |
+ SLI4_REQFEAT_HLM | SLI4_REQFEAT_PERFH |
+ SLI4_REQFEAT_RXSEQ | SLI4_REQFEAT_RXRI |
+ SLI4_REQFEAT_MRQP);
+
+ /* use performance hints if available */
+ if (sli4->params.perf_hint)
+ sli4->features |= SLI4_REQFEAT_PERFH;
+
+ if (sli_request_features(sli4, &sli4->features, true))
+ return -EIO;
+
+ if (sli_get_config(sli4))
+ return -EIO;
+
+ return 0;
+}
+
+int
+sli_init(struct sli4 *sli4)
+{
+ if (sli4->params.has_extents) {
+ efc_log_info(sli4, "extend allocation not supported\n");
+ return -EIO;
+ }
+
+ sli4->features &= (~SLI4_REQFEAT_HLM);
+ sli4->features &= (~SLI4_REQFEAT_RXSEQ);
+ sli4->features &= (~SLI4_REQFEAT_RXRI);
+
+ if (sli_request_features(sli4, &sli4->features, false))
+ return -EIO;
+
+ return 0;
+}
+
+int
+sli_reset(struct sli4 *sli4)
+{
+ u32 i;
+
+ if (!sli_fw_init(sli4)) {
+ efc_log_crit(sli4, "FW initialization failed\n");
+ return -EIO;
+ }
+
+ kfree(sli4->ext[0].base);
+ sli4->ext[0].base = NULL;
+
+ for (i = 0; i < SLI4_RSRC_MAX; i++) {
+ bitmap_free(sli4->ext[i].use_map);
+ sli4->ext[i].use_map = NULL;
+ sli4->ext[i].base = NULL;
+ }
+
+ return sli_get_config(sli4);
+}
+
+int
+sli_fw_reset(struct sli4 *sli4)
+{
+ /*
+ * Firmware must be ready before issuing the reset.
+ */
+ if (!sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC)) {
+ efc_log_crit(sli4, "FW status is NOT ready\n");
+ return -EIO;
+ }
+
+ /* Lancer uses PHYDEV_CONTROL */
+ writel(SLI4_PHYDEV_CTRL_FRST, (sli4->reg[0] + SLI4_PHYDEV_CTRL_REG));
+
+ /* wait for the FW to become ready after the reset */
+ if (!sli_wait_for_fw_ready(sli4, SLI4_FW_READY_TIMEOUT_MSEC)) {
+ efc_log_crit(sli4, "Failed to be ready after firmware reset\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+sli_teardown(struct sli4 *sli4)
+{
+ u32 i;
+
+ kfree(sli4->ext[0].base);
+ sli4->ext[0].base = NULL;
+
+ for (i = 0; i < SLI4_RSRC_MAX; i++) {
+ sli4->ext[i].base = NULL;
+
+ bitmap_free(sli4->ext[i].use_map);
+ sli4->ext[i].use_map = NULL;
+ }
+
+ if (!sli_sliport_reset(sli4))
+ efc_log_err(sli4, "FW deinitialization failed\n");
+
+ dma_free_coherent(&sli4->pci->dev, sli4->vpd_data.size,
+ sli4->vpd_data.virt, sli4->vpd_data.phys);
+ memset(&sli4->vpd_data, 0, sizeof(struct efc_dma));
+
+ dma_free_coherent(&sli4->pci->dev, sli4->bmbx.size,
+ sli4->bmbx.virt, sli4->bmbx.phys);
+ memset(&sli4->bmbx, 0, sizeof(struct efc_dma));
+}
+
+int
+sli_callback(struct sli4 *sli4, enum sli4_callback which,
+ void *func, void *arg)
+{
+ if (!func) {
+ efc_log_err(sli4, "bad parameter sli4=%p which=%#x func=%p\n",
+ sli4, which, func);
+ return -EIO;
+ }
+
+ switch (which) {
+ case SLI4_CB_LINK:
+ sli4->link = func;
+ sli4->link_arg = arg;
+ break;
+ default:
+ efc_log_info(sli4, "unknown callback %#x\n", which);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int
+sli_eq_modify_delay(struct sli4 *sli4, struct sli4_queue *eq,
+ u32 num_eq, u32 shift, u32 delay_mult)
+{
+ sli_cmd_common_modify_eq_delay(sli4, sli4->bmbx.virt, eq, num_eq,
+ shift, delay_mult);
+
+ if (sli_bmbx_command(sli4)) {
+ efc_log_crit(sli4, "bootstrap mailbox write fail (MODIFY EQ DELAY)\n");
+ return -EIO;
+ }
+ if (sli_res_sli_config(sli4, sli4->bmbx.virt)) {
+ efc_log_err(sli4, "bad status MODIFY EQ DELAY\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int
+sli_resource_alloc(struct sli4 *sli4, enum sli4_resource rtype,
+ u32 *rid, u32 *index)
+{
+ int rc = 0;
+ u32 size;
+ u32 ext_idx;
+ u32 item_idx;
+ u32 position;
+
+ *rid = U32_MAX;
+ *index = U32_MAX;
+
+ switch (rtype) {
+ case SLI4_RSRC_VFI:
+ case SLI4_RSRC_VPI:
+ case SLI4_RSRC_RPI:
+ case SLI4_RSRC_XRI:
+ position =
+ find_first_zero_bit(sli4->ext[rtype].use_map,
+ sli4->ext[rtype].map_size);
+ if (position >= sli4->ext[rtype].map_size) {
+ efc_log_err(sli4, "out of resource %d (alloc=%d)\n",
+ rtype, sli4->ext[rtype].n_alloc);
+ rc = -EIO;
+ break;
+ }
+ set_bit(position, sli4->ext[rtype].use_map);
+ *index = position;
+
+ size = sli4->ext[rtype].size;
+
+ ext_idx = *index / size;
+ item_idx = *index % size;
+
+ *rid = sli4->ext[rtype].base[ext_idx] + item_idx;
+
+ sli4->ext[rtype].n_alloc++;
+ break;
+ default:
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+int
+sli_resource_free(struct sli4 *sli4, enum sli4_resource rtype, u32 rid)
+{
+ int rc = -EIO;
+ u32 x;
+ u32 size, *base;
+
+ switch (rtype) {
+ case SLI4_RSRC_VFI:
+ case SLI4_RSRC_VPI:
+ case SLI4_RSRC_RPI:
+ case SLI4_RSRC_XRI:
+ /*
+ * Figure out which extent contains the resource ID. I.e. find
+ * the extent such that
+ * extent->base <= resource ID < extent->base + extent->size
+ */
+ base = sli4->ext[rtype].base;
+ size = sli4->ext[rtype].size;
+
+ /*
+ * In the case of FW reset, this may be cleared
+ * but the force_free path will still attempt to
+ * free the resource. Prevent a NULL pointer access.
+ */
+ if (!base)
+ break;
+
+ for (x = 0; x < sli4->ext[rtype].number; x++) {
+ if ((rid < base[x] || (rid >= (base[x] + size))))
+ continue;
+
+ rid -= base[x];
+ clear_bit((x * size) + rid, sli4->ext[rtype].use_map);
+ rc = 0;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+int
+sli_resource_reset(struct sli4 *sli4, enum sli4_resource rtype)
+{
+ int rc = -EIO;
+ u32 i;
+
+ switch (rtype) {
+ case SLI4_RSRC_VFI:
+ case SLI4_RSRC_VPI:
+ case SLI4_RSRC_RPI:
+ case SLI4_RSRC_XRI:
+ for (i = 0; i < sli4->ext[rtype].map_size; i++)
+ clear_bit(i, sli4->ext[rtype].use_map);
+ rc = 0;
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+int sli_raise_ue(struct sli4 *sli4, u8 dump)
+{
+ u32 val = 0;
+
+ if (dump == SLI4_FUNC_DESC_DUMP) {
+ val = SLI4_PORT_CTRL_FDD | SLI4_PORT_CTRL_IP;
+ writel(val, (sli4->reg[0] + SLI4_PORT_CTRL_REG));
+ } else {
+ val = SLI4_PHYDEV_CTRL_FRST;
+
+ if (dump == SLI4_CHIP_LEVEL_DUMP)
+ val |= SLI4_PHYDEV_CTRL_DD;
+ writel(val, (sli4->reg[0] + SLI4_PHYDEV_CTRL_REG));
+ }
+
+ return 0;
+}
+
+int sli_dump_is_ready(struct sli4 *sli4)
+{
+ int rc = SLI4_DUMP_READY_STATUS_NOT_READY;
+ u32 port_val;
+ u32 bmbx_val;
+
+ /*
+ * Ensure that the port is ready AND the mailbox is
+ * ready before signaling that the dump is ready to go.
+ */
+ port_val = sli_reg_read_status(sli4);
+ bmbx_val = readl(sli4->reg[0] + SLI4_BMBX_REG);
+
+ if ((bmbx_val & SLI4_BMBX_RDY) &&
+ (port_val & SLI4_PORT_STATUS_RDY)) {
+ if (port_val & SLI4_PORT_STATUS_DIP)
+ rc = SLI4_DUMP_READY_STATUS_DD_PRESENT;
+ else if (port_val & SLI4_PORT_STATUS_FDP)
+ rc = SLI4_DUMP_READY_STATUS_FDB_PRESENT;
+ }
+
+ return rc;
+}
+
+bool sli_reset_required(struct sli4 *sli4)
+{
+ u32 val;
+
+ val = sli_reg_read_status(sli4);
+ return (val & SLI4_PORT_STATUS_RN);
+}
+
+int
+sli_cmd_post_sgl_pages(struct sli4 *sli4, void *buf, u16 xri,
+ u32 xri_count, struct efc_dma *page0[],
+ struct efc_dma *page1[], struct efc_dma *dma)
+{
+ struct sli4_rqst_post_sgl_pages *post = NULL;
+ u32 i;
+ __le32 req_len;
+
+ post = sli_config_cmd_init(sli4, buf,
+ SLI4_CFG_PYLD_LENGTH(post_sgl_pages), dma);
+ if (!post)
+ return -EIO;
+
+ /* payload size calculation */
+ /* 4 = xri_start + xri_count */
+ /* xri_count = # of XRI's registered */
+ /* sizeof(uint64_t) = physical address size */
+ /* 2 = # of physical addresses per page set */
+ req_len = cpu_to_le32(4 + (xri_count * (sizeof(uint64_t) * 2)));
+ sli_cmd_fill_hdr(&post->hdr, SLI4_OPC_POST_SGL_PAGES, SLI4_SUBSYSTEM_FC,
+ CMD_V0, req_len);
+ post->xri_start = cpu_to_le16(xri);
+ post->xri_count = cpu_to_le16(xri_count);
+
+ for (i = 0; i < xri_count; i++) {
+ post->page_set[i].page0_low =
+ cpu_to_le32(lower_32_bits(page0[i]->phys));
+ post->page_set[i].page0_high =
+ cpu_to_le32(upper_32_bits(page0[i]->phys));
+ }
+
+ if (page1) {
+ for (i = 0; i < xri_count; i++) {
+ post->page_set[i].page1_low =
+ cpu_to_le32(lower_32_bits(page1[i]->phys));
+ post->page_set[i].page1_high =
+ cpu_to_le32(upper_32_bits(page1[i]->phys));
+ }
+ }
+
+ return 0;
+}
+
+int
+sli_cmd_post_hdr_templates(struct sli4 *sli4, void *buf, struct efc_dma *dma,
+ u16 rpi, struct efc_dma *payload_dma)
+{
+ struct sli4_rqst_post_hdr_templates *req = NULL;
+ uintptr_t phys = 0;
+ u32 i = 0;
+ u32 page_count, payload_size;
+
+ page_count = sli_page_count(dma->size, SLI_PAGE_SIZE);
+
+ payload_size = ((sizeof(struct sli4_rqst_post_hdr_templates) +
+ (page_count * SZ_DMAADDR)) - sizeof(struct sli4_rqst_hdr));
+
+ if (page_count > 16) {
+ /*
+ * We can't fit more than 16 descriptors into an embedded mbox
+ * command, it has to be non-embedded
+ */
+ payload_dma->size = payload_size;
+ payload_dma->virt = dma_alloc_coherent(&sli4->pci->dev,
+ payload_dma->size,
+ &payload_dma->phys, GFP_KERNEL);
+ if (!payload_dma->virt) {
+ memset(payload_dma, 0, sizeof(struct efc_dma));
+ efc_log_err(sli4, "mbox payload memory allocation fail\n");
+ return -EIO;
+ }
+ req = sli_config_cmd_init(sli4, buf, payload_size, payload_dma);
+ } else {
+ req = sli_config_cmd_init(sli4, buf, payload_size, NULL);
+ }
+
+ if (!req)
+ return -EIO;
+
+ if (rpi == U16_MAX)
+ rpi = sli4->ext[SLI4_RSRC_RPI].base[0];
+
+ sli_cmd_fill_hdr(&req->hdr, SLI4_OPC_POST_HDR_TEMPLATES,
+ SLI4_SUBSYSTEM_FC, CMD_V0,
+ SLI4_RQST_PYLD_LEN(post_hdr_templates));
+
+ req->rpi_offset = cpu_to_le16(rpi);
+ req->page_count = cpu_to_le16(page_count);
+ phys = dma->phys;
+ for (i = 0; i < page_count; i++) {
+ req->page_descriptor[i].low = cpu_to_le32(lower_32_bits(phys));
+ req->page_descriptor[i].high = cpu_to_le32(upper_32_bits(phys));
+
+ phys += SLI_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+u32
+sli_fc_get_rpi_requirements(struct sli4 *sli4, u32 n_rpi)
+{
+ u32 bytes = 0;
+
+ /* Check if header templates needed */
+ if (sli4->params.hdr_template_req)
+ /* round up to a page */
+ bytes = round_up(n_rpi * SLI4_HDR_TEMPLATE_SIZE, SLI_PAGE_SIZE);
+
+ return bytes;
+}
+
+const char *
+sli_fc_get_status_string(u32 status)
+{
+ static struct {
+ u32 code;
+ const char *label;
+ } lookup[] = {
+ {SLI4_FC_WCQE_STATUS_SUCCESS, "SUCCESS"},
+ {SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE, "FCP_RSP_FAILURE"},
+ {SLI4_FC_WCQE_STATUS_REMOTE_STOP, "REMOTE_STOP"},
+ {SLI4_FC_WCQE_STATUS_LOCAL_REJECT, "LOCAL_REJECT"},
+ {SLI4_FC_WCQE_STATUS_NPORT_RJT, "NPORT_RJT"},
+ {SLI4_FC_WCQE_STATUS_FABRIC_RJT, "FABRIC_RJT"},
+ {SLI4_FC_WCQE_STATUS_NPORT_BSY, "NPORT_BSY"},
+ {SLI4_FC_WCQE_STATUS_FABRIC_BSY, "FABRIC_BSY"},
+ {SLI4_FC_WCQE_STATUS_LS_RJT, "LS_RJT"},
+ {SLI4_FC_WCQE_STATUS_CMD_REJECT, "CMD_REJECT"},
+ {SLI4_FC_WCQE_STATUS_FCP_TGT_LENCHECK, "FCP_TGT_LENCHECK"},
+ {SLI4_FC_WCQE_STATUS_RQ_BUF_LEN_EXCEEDED, "BUF_LEN_EXCEEDED"},
+ {SLI4_FC_WCQE_STATUS_RQ_INSUFF_BUF_NEEDED,
+ "RQ_INSUFF_BUF_NEEDED"},
+ {SLI4_FC_WCQE_STATUS_RQ_INSUFF_FRM_DISC, "RQ_INSUFF_FRM_DESC"},
+ {SLI4_FC_WCQE_STATUS_RQ_DMA_FAILURE, "RQ_DMA_FAILURE"},
+ {SLI4_FC_WCQE_STATUS_FCP_RSP_TRUNCATE, "FCP_RSP_TRUNCATE"},
+ {SLI4_FC_WCQE_STATUS_DI_ERROR, "DI_ERROR"},
+ {SLI4_FC_WCQE_STATUS_BA_RJT, "BA_RJT"},
+ {SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_NEEDED,
+ "RQ_INSUFF_XRI_NEEDED"},
+ {SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_DISC, "INSUFF_XRI_DISC"},
+ {SLI4_FC_WCQE_STATUS_RX_ERROR_DETECT, "RX_ERROR_DETECT"},
+ {SLI4_FC_WCQE_STATUS_RX_ABORT_REQUEST, "RX_ABORT_REQUEST"},
+ };
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(lookup); i++) {
+ if (status == lookup[i].code)
+ return lookup[i].label;
+ }
+ return "unknown";
+}
diff --git a/drivers/scsi/elx/libefc_sli/sli4.h b/drivers/scsi/elx/libefc_sli/sli4.h
new file mode 100644
index 000000000000..38af166cc786
--- /dev/null
+++ b/drivers/scsi/elx/libefc_sli/sli4.h
@@ -0,0 +1,4132 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ */
+
+/*
+ * All common SLI-4 structures and function prototypes.
+ */
+
+#ifndef _SLI4_H
+#define _SLI4_H
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "scsi/fc/fc_els.h"
+#include "scsi/fc/fc_fs.h"
+#include "../include/efc_common.h"
+
+/*************************************************************************
+ * Common SLI-4 register offsets and field definitions
+ */
+
+/* SLI_INTF - SLI Interface Definition Register */
+#define SLI4_INTF_REG 0x0058
+enum sli4_intf {
+ SLI4_INTF_REV_SHIFT = 4,
+ SLI4_INTF_REV_MASK = 0xf0,
+
+ SLI4_INTF_REV_S3 = 0x30,
+ SLI4_INTF_REV_S4 = 0x40,
+
+ SLI4_INTF_FAMILY_SHIFT = 8,
+ SLI4_INTF_FAMILY_MASK = 0x0f00,
+
+ SLI4_FAMILY_CHECK_ASIC_TYPE = 0x0f00,
+
+ SLI4_INTF_IF_TYPE_SHIFT = 12,
+ SLI4_INTF_IF_TYPE_MASK = 0xf000,
+
+ SLI4_INTF_IF_TYPE_2 = 0x2000,
+ SLI4_INTF_IF_TYPE_6 = 0x6000,
+
+ SLI4_INTF_VALID_SHIFT = 29,
+ SLI4_INTF_VALID_MASK = 0xe0000000,
+
+ SLI4_INTF_VALID_VALUE = 0xc0000000,
+};
+
+/* ASIC_ID - SLI ASIC Type and Revision Register */
+#define SLI4_ASIC_ID_REG 0x009c
+enum sli4_asic {
+ SLI4_ASIC_GEN_SHIFT = 8,
+ SLI4_ASIC_GEN_MASK = 0xff00,
+ SLI4_ASIC_GEN_5 = 0x0b00,
+ SLI4_ASIC_GEN_6 = 0x0c00,
+ SLI4_ASIC_GEN_7 = 0x0d00,
+};
+
+enum sli4_acic_revisions {
+ SLI4_ASIC_REV_A0 = 0x00,
+ SLI4_ASIC_REV_A1 = 0x01,
+ SLI4_ASIC_REV_A2 = 0x02,
+ SLI4_ASIC_REV_A3 = 0x03,
+ SLI4_ASIC_REV_B0 = 0x10,
+ SLI4_ASIC_REV_B1 = 0x11,
+ SLI4_ASIC_REV_B2 = 0x12,
+ SLI4_ASIC_REV_C0 = 0x20,
+ SLI4_ASIC_REV_C1 = 0x21,
+ SLI4_ASIC_REV_C2 = 0x22,
+ SLI4_ASIC_REV_D0 = 0x30,
+};
+
+struct sli4_asic_entry_t {
+ u32 rev_id;
+ u32 family;
+};
+
+/* BMBX - Bootstrap Mailbox Register */
+#define SLI4_BMBX_REG 0x0160
+enum sli4_bmbx {
+ SLI4_BMBX_MASK_HI = 0x3,
+ SLI4_BMBX_MASK_LO = 0xf,
+ SLI4_BMBX_RDY = 1 << 0,
+ SLI4_BMBX_HI = 1 << 1,
+ SLI4_BMBX_SIZE = 256,
+};
+
+static inline u32
+sli_bmbx_write_hi(u64 addr) {
+ u32 val;
+
+ val = upper_32_bits(addr) & ~SLI4_BMBX_MASK_HI;
+ val |= SLI4_BMBX_HI;
+
+ return val;
+}
+
+static inline u32
+sli_bmbx_write_lo(u64 addr) {
+ u32 val;
+
+ val = (upper_32_bits(addr) & SLI4_BMBX_MASK_HI) << 30;
+ val |= ((addr) & ~SLI4_BMBX_MASK_LO) >> 2;
+
+ return val;
+}
+
+/* SLIPORT_CONTROL - SLI Port Control Register */
+#define SLI4_PORT_CTRL_REG 0x0408
+enum sli4_port_ctrl {
+ SLI4_PORT_CTRL_IP = 1u << 27,
+ SLI4_PORT_CTRL_IDIS = 1u << 22,
+ SLI4_PORT_CTRL_FDD = 1u << 31,
+};
+
+/* SLI4_SLIPORT_ERROR - SLI Port Error Register */
+#define SLI4_PORT_ERROR1 0x040c
+#define SLI4_PORT_ERROR2 0x0410
+
+/* EQCQ_DOORBELL - EQ and CQ Doorbell Register */
+#define SLI4_EQCQ_DB_REG 0x120
+enum sli4_eqcq_e {
+ SLI4_EQ_ID_LO_MASK = 0x01ff,
+
+ SLI4_CQ_ID_LO_MASK = 0x03ff,
+
+ SLI4_EQCQ_CI_EQ = 0x0200,
+
+ SLI4_EQCQ_QT_EQ = 0x00000400,
+ SLI4_EQCQ_QT_CQ = 0x00000000,
+
+ SLI4_EQCQ_ID_HI_SHIFT = 11,
+ SLI4_EQCQ_ID_HI_MASK = 0xf800,
+
+ SLI4_EQCQ_NUM_SHIFT = 16,
+ SLI4_EQCQ_NUM_MASK = 0x1fff0000,
+
+ SLI4_EQCQ_ARM = 0x20000000,
+ SLI4_EQCQ_UNARM = 0x00000000,
+};
+
+static inline u32
+sli_format_eq_db_data(u16 num_popped, u16 id, u32 arm) {
+ u32 reg;
+
+ reg = (id & SLI4_EQ_ID_LO_MASK) | SLI4_EQCQ_QT_EQ;
+ reg |= (((id) >> 9) << SLI4_EQCQ_ID_HI_SHIFT) & SLI4_EQCQ_ID_HI_MASK;
+ reg |= ((num_popped) << SLI4_EQCQ_NUM_SHIFT) & SLI4_EQCQ_NUM_MASK;
+ reg |= arm | SLI4_EQCQ_CI_EQ;
+
+ return reg;
+}
+
+static inline u32
+sli_format_cq_db_data(u16 num_popped, u16 id, u32 arm) {
+ u32 reg;
+
+ reg = ((id) & SLI4_CQ_ID_LO_MASK) | SLI4_EQCQ_QT_CQ;
+ reg |= (((id) >> 10) << SLI4_EQCQ_ID_HI_SHIFT) & SLI4_EQCQ_ID_HI_MASK;
+ reg |= ((num_popped) << SLI4_EQCQ_NUM_SHIFT) & SLI4_EQCQ_NUM_MASK;
+ reg |= arm;
+
+ return reg;
+}
+
+/* EQ_DOORBELL - EQ Doorbell Register for IF_TYPE = 6*/
+#define SLI4_IF6_EQ_DB_REG 0x120
+enum sli4_eq_e {
+ SLI4_IF6_EQ_ID_MASK = 0x0fff,
+
+ SLI4_IF6_EQ_NUM_SHIFT = 16,
+ SLI4_IF6_EQ_NUM_MASK = 0x1fff0000,
+};
+
+static inline u32
+sli_format_if6_eq_db_data(u16 num_popped, u16 id, u32 arm) {
+ u32 reg;
+
+ reg = id & SLI4_IF6_EQ_ID_MASK;
+ reg |= (num_popped << SLI4_IF6_EQ_NUM_SHIFT) & SLI4_IF6_EQ_NUM_MASK;
+ reg |= arm;
+
+ return reg;
+}
+
+/* CQ_DOORBELL - CQ Doorbell Register for IF_TYPE = 6 */
+#define SLI4_IF6_CQ_DB_REG 0xc0
+enum sli4_cq_e {
+ SLI4_IF6_CQ_ID_MASK = 0xffff,
+
+ SLI4_IF6_CQ_NUM_SHIFT = 16,
+ SLI4_IF6_CQ_NUM_MASK = 0x1fff0000,
+};
+
+static inline u32
+sli_format_if6_cq_db_data(u16 num_popped, u16 id, u32 arm) {
+ u32 reg;
+
+ reg = id & SLI4_IF6_CQ_ID_MASK;
+ reg |= ((num_popped) << SLI4_IF6_CQ_NUM_SHIFT) & SLI4_IF6_CQ_NUM_MASK;
+ reg |= arm;
+
+ return reg;
+}
+
+/* MQ_DOORBELL - MQ Doorbell Register */
+#define SLI4_MQ_DB_REG 0x0140
+#define SLI4_IF6_MQ_DB_REG 0x0160
+enum sli4_mq_e {
+ SLI4_MQ_ID_MASK = 0xffff,
+
+ SLI4_MQ_NUM_SHIFT = 16,
+ SLI4_MQ_NUM_MASK = 0x3fff0000,
+};
+
+static inline u32
+sli_format_mq_db_data(u16 id) {
+ u32 reg;
+
+ reg = id & SLI4_MQ_ID_MASK;
+ reg |= (1 << SLI4_MQ_NUM_SHIFT) & SLI4_MQ_NUM_MASK;
+
+ return reg;
+}
+
+/* RQ_DOORBELL - RQ Doorbell Register */
+#define SLI4_RQ_DB_REG 0x0a0
+#define SLI4_IF6_RQ_DB_REG 0x0080
+enum sli4_rq_e {
+ SLI4_RQ_DB_ID_MASK = 0xffff,
+
+ SLI4_RQ_DB_NUM_SHIFT = 16,
+ SLI4_RQ_DB_NUM_MASK = 0x3fff0000,
+};
+
+static inline u32
+sli_format_rq_db_data(u16 id) {
+ u32 reg;
+
+ reg = id & SLI4_RQ_DB_ID_MASK;
+ reg |= (1 << SLI4_RQ_DB_NUM_SHIFT) & SLI4_RQ_DB_NUM_MASK;
+
+ return reg;
+}
+
+/* WQ_DOORBELL - WQ Doorbell Register */
+#define SLI4_IO_WQ_DB_REG 0x040
+#define SLI4_IF6_WQ_DB_REG 0x040
+enum sli4_wq_e {
+ SLI4_WQ_ID_MASK = 0xffff,
+
+ SLI4_WQ_IDX_SHIFT = 16,
+ SLI4_WQ_IDX_MASK = 0xff0000,
+
+ SLI4_WQ_NUM_SHIFT = 24,
+ SLI4_WQ_NUM_MASK = 0x0ff00000,
+};
+
+static inline u32
+sli_format_wq_db_data(u16 id) {
+ u32 reg;
+
+ reg = id & SLI4_WQ_ID_MASK;
+ reg |= (1 << SLI4_WQ_NUM_SHIFT) & SLI4_WQ_NUM_MASK;
+
+ return reg;
+}
+
+/* SLIPORT_STATUS - SLI Port Status Register */
+#define SLI4_PORT_STATUS_REGOFF 0x0404
+enum sli4_port_status {
+ SLI4_PORT_STATUS_FDP = 1u << 21,
+ SLI4_PORT_STATUS_RDY = 1u << 23,
+ SLI4_PORT_STATUS_RN = 1u << 24,
+ SLI4_PORT_STATUS_DIP = 1u << 25,
+ SLI4_PORT_STATUS_OTI = 1u << 29,
+ SLI4_PORT_STATUS_ERR = 1u << 31,
+};
+
+#define SLI4_PHYDEV_CTRL_REG 0x0414
+#define SLI4_PHYDEV_CTRL_FRST (1 << 1)
+#define SLI4_PHYDEV_CTRL_DD (1 << 2)
+
+/* Register name enums */
+enum sli4_regname_en {
+ SLI4_REG_BMBX,
+ SLI4_REG_EQ_DOORBELL,
+ SLI4_REG_CQ_DOORBELL,
+ SLI4_REG_RQ_DOORBELL,
+ SLI4_REG_IO_WQ_DOORBELL,
+ SLI4_REG_MQ_DOORBELL,
+ SLI4_REG_PHYSDEV_CONTROL,
+ SLI4_REG_PORT_CONTROL,
+ SLI4_REG_PORT_ERROR1,
+ SLI4_REG_PORT_ERROR2,
+ SLI4_REG_PORT_SEMAPHORE,
+ SLI4_REG_PORT_STATUS,
+ SLI4_REG_UNKWOWN /* must be last */
+};
+
+struct sli4_reg {
+ u32 rset;
+ u32 off;
+};
+
+struct sli4_dmaaddr {
+ __le32 low;
+ __le32 high;
+};
+
+/*
+ * a 3-word Buffer Descriptor Entry with
+ * address 1st 2 words, length last word
+ */
+struct sli4_bufptr {
+ struct sli4_dmaaddr addr;
+ __le32 length;
+};
+
+/* Buffer Descriptor Entry (BDE) */
+enum sli4_bde_e {
+ SLI4_BDE_LEN_MASK = 0x00ffffff,
+ SLI4_BDE_TYPE_MASK = 0xff000000,
+};
+
+struct sli4_bde {
+ __le32 bde_type_buflen;
+ union {
+ struct sli4_dmaaddr data;
+ struct {
+ __le32 offset;
+ __le32 rsvd2;
+ } imm;
+ struct sli4_dmaaddr blp;
+ } u;
+};
+
+/* Buffer Descriptors */
+enum sli4_bde_type {
+ SLI4_BDE_TYPE_SHIFT = 24,
+ SLI4_BDE_TYPE_64 = 0x00, /* Generic 64-bit data */
+ SLI4_BDE_TYPE_IMM = 0x01, /* Immediate data */
+ SLI4_BDE_TYPE_BLP = 0x40, /* Buffer List Pointer */
+};
+
+#define SLI4_BDE_TYPE_VAL(type) \
+ (SLI4_BDE_TYPE_##type << SLI4_BDE_TYPE_SHIFT)
+
+/* Scatter-Gather Entry (SGE) */
+#define SLI4_SGE_MAX_RESERVED 3
+
+enum sli4_sge_type {
+ /* DW2 */
+ SLI4_SGE_DATA_OFFSET_MASK = 0x07ffffff,
+ /*DW2W1*/
+ SLI4_SGE_TYPE_SHIFT = 27,
+ SLI4_SGE_TYPE_MASK = 0x78000000,
+ /*SGE Types*/
+ SLI4_SGE_TYPE_DATA = 0x00,
+ SLI4_SGE_TYPE_DIF = 0x04, /* Data Integrity Field */
+ SLI4_SGE_TYPE_LSP = 0x05, /* List Segment Pointer */
+ SLI4_SGE_TYPE_PEDIF = 0x06, /* Post Encryption Engine DIF */
+ SLI4_SGE_TYPE_PESEED = 0x07, /* Post Encryption DIF Seed */
+ SLI4_SGE_TYPE_DISEED = 0x08, /* DIF Seed */
+ SLI4_SGE_TYPE_ENC = 0x09, /* Encryption */
+ SLI4_SGE_TYPE_ATM = 0x0a, /* DIF Application Tag Mask */
+ SLI4_SGE_TYPE_SKIP = 0x0c, /* SKIP */
+
+ SLI4_SGE_LAST = 1u << 31,
+};
+
+struct sli4_sge {
+ __le32 buffer_address_high;
+ __le32 buffer_address_low;
+ __le32 dw2_flags;
+ __le32 buffer_length;
+};
+
+/* T10 DIF Scatter-Gather Entry (SGE) */
+struct sli4_dif_sge {
+ __le32 buffer_address_high;
+ __le32 buffer_address_low;
+ __le32 dw2_flags;
+ __le32 rsvd12;
+};
+
+/* Data Integrity Seed (DISEED) SGE */
+enum sli4_diseed_sge_flags {
+ /* DW2W1 */
+ SLI4_DISEED_SGE_HS = 1 << 2,
+ SLI4_DISEED_SGE_WS = 1 << 3,
+ SLI4_DISEED_SGE_IC = 1 << 4,
+ SLI4_DISEED_SGE_ICS = 1 << 5,
+ SLI4_DISEED_SGE_ATRT = 1 << 6,
+ SLI4_DISEED_SGE_AT = 1 << 7,
+ SLI4_DISEED_SGE_FAT = 1 << 8,
+ SLI4_DISEED_SGE_NA = 1 << 9,
+ SLI4_DISEED_SGE_HI = 1 << 10,
+
+ /* DW3W1 */
+ SLI4_DISEED_SGE_BS_MASK = 0x0007,
+ SLI4_DISEED_SGE_AI = 1 << 3,
+ SLI4_DISEED_SGE_ME = 1 << 4,
+ SLI4_DISEED_SGE_RE = 1 << 5,
+ SLI4_DISEED_SGE_CE = 1 << 6,
+ SLI4_DISEED_SGE_NR = 1 << 7,
+
+ SLI4_DISEED_SGE_OP_RX_SHIFT = 8,
+ SLI4_DISEED_SGE_OP_RX_MASK = 0x0f00,
+ SLI4_DISEED_SGE_OP_TX_SHIFT = 12,
+ SLI4_DISEED_SGE_OP_TX_MASK = 0xf000,
+};
+
+/* Opcode values */
+enum sli4_diseed_sge_opcodes {
+ SLI4_DISEED_SGE_OP_IN_NODIF_OUT_CRC,
+ SLI4_DISEED_SGE_OP_IN_CRC_OUT_NODIF,
+ SLI4_DISEED_SGE_OP_IN_NODIF_OUT_CSUM,
+ SLI4_DISEED_SGE_OP_IN_CSUM_OUT_NODIF,
+ SLI4_DISEED_SGE_OP_IN_CRC_OUT_CRC,
+ SLI4_DISEED_SGE_OP_IN_CSUM_OUT_CSUM,
+ SLI4_DISEED_SGE_OP_IN_CRC_OUT_CSUM,
+ SLI4_DISEED_SGE_OP_IN_CSUM_OUT_CRC,
+ SLI4_DISEED_SGE_OP_IN_RAW_OUT_RAW,
+};
+
+#define SLI4_DISEED_SGE_OP_RX_VALUE(stype) \
+ (SLI4_DISEED_SGE_OP_##stype << SLI4_DISEED_SGE_OP_RX_SHIFT)
+#define SLI4_DISEED_SGE_OP_TX_VALUE(stype) \
+ (SLI4_DISEED_SGE_OP_##stype << SLI4_DISEED_SGE_OP_TX_SHIFT)
+
+struct sli4_diseed_sge {
+ __le32 ref_tag_cmp;
+ __le32 ref_tag_repl;
+ __le16 app_tag_repl;
+ __le16 dw2w1_flags;
+ __le16 app_tag_cmp;
+ __le16 dw3w1_flags;
+};
+
+/* List Segment Pointer Scatter-Gather Entry (SGE) */
+#define SLI4_LSP_SGE_SEGLEN 0x00ffffff
+
+struct sli4_lsp_sge {
+ __le32 buffer_address_high;
+ __le32 buffer_address_low;
+ __le32 dw2_flags;
+ __le32 dw3_seglen;
+};
+
+enum sli4_eqe_e {
+ SLI4_EQE_VALID = 1,
+ SLI4_EQE_MJCODE = 0xe,
+ SLI4_EQE_MNCODE = 0xfff0,
+};
+
+struct sli4_eqe {
+ __le16 dw0w0_flags;
+ __le16 resource_id;
+};
+
+#define SLI4_MAJOR_CODE_STANDARD 0
+#define SLI4_MAJOR_CODE_SENTINEL 1
+
+/* Sentinel EQE indicating the EQ is full */
+#define SLI4_EQE_STATUS_EQ_FULL 2
+
+enum sli4_mcqe_e {
+ SLI4_MCQE_CONSUMED = 1u << 27,
+ SLI4_MCQE_COMPLETED = 1u << 28,
+ SLI4_MCQE_AE = 1u << 30,
+ SLI4_MCQE_VALID = 1u << 31,
+};
+
+/* Entry was consumed but not completed */
+#define SLI4_MCQE_STATUS_NOT_COMPLETED -2
+
+struct sli4_mcqe {
+ __le16 completion_status;
+ __le16 extended_status;
+ __le32 mqe_tag_low;
+ __le32 mqe_tag_high;
+ __le32 dw3_flags;
+};
+
+enum sli4_acqe_e {
+ SLI4_ACQE_AE = 1 << 6, /* async event - this is an ACQE */
+ SLI4_ACQE_VAL = 1 << 7, /* valid - contents of CQE are valid */
+};
+
+struct sli4_acqe {
+ __le32 event_data[3];
+ u8 rsvd12;
+ u8 event_code;
+ u8 event_type;
+ u8 ae_val;
+};
+
+enum sli4_acqe_event_code {
+ SLI4_ACQE_EVENT_CODE_LINK_STATE = 0x01,
+ SLI4_ACQE_EVENT_CODE_FIP = 0x02,
+ SLI4_ACQE_EVENT_CODE_DCBX = 0x03,
+ SLI4_ACQE_EVENT_CODE_ISCSI = 0x04,
+ SLI4_ACQE_EVENT_CODE_GRP_5 = 0x05,
+ SLI4_ACQE_EVENT_CODE_FC_LINK_EVENT = 0x10,
+ SLI4_ACQE_EVENT_CODE_SLI_PORT_EVENT = 0x11,
+ SLI4_ACQE_EVENT_CODE_VF_EVENT = 0x12,
+ SLI4_ACQE_EVENT_CODE_MR_EVENT = 0x13,
+};
+
+enum sli4_qtype {
+ SLI4_QTYPE_EQ,
+ SLI4_QTYPE_CQ,
+ SLI4_QTYPE_MQ,
+ SLI4_QTYPE_WQ,
+ SLI4_QTYPE_RQ,
+ SLI4_QTYPE_MAX, /* must be last */
+};
+
+#define SLI4_USER_MQ_COUNT 1
+#define SLI4_MAX_CQ_SET_COUNT 16
+#define SLI4_MAX_RQ_SET_COUNT 16
+
+enum sli4_qentry {
+ SLI4_QENTRY_ASYNC,
+ SLI4_QENTRY_MQ,
+ SLI4_QENTRY_RQ,
+ SLI4_QENTRY_WQ,
+ SLI4_QENTRY_WQ_RELEASE,
+ SLI4_QENTRY_OPT_WRITE_CMD,
+ SLI4_QENTRY_OPT_WRITE_DATA,
+ SLI4_QENTRY_XABT,
+ SLI4_QENTRY_MAX /* must be last */
+};
+
+enum sli4_queue_flags {
+ SLI4_QUEUE_FLAG_MQ = 1 << 0, /* CQ has MQ/Async completion */
+ SLI4_QUEUE_FLAG_HDR = 1 << 1, /* RQ for packet headers */
+ SLI4_QUEUE_FLAG_RQBATCH = 1 << 2, /* RQ index increment by 8 */
+};
+
+/* Generic Command Request header */
+enum sli4_cmd_version {
+ CMD_V0,
+ CMD_V1,
+ CMD_V2,
+};
+
+struct sli4_rqst_hdr {
+ u8 opcode;
+ u8 subsystem;
+ __le16 rsvd2;
+ __le32 timeout;
+ __le32 request_length;
+ __le32 dw3_version;
+};
+
+/* Generic Command Response header */
+struct sli4_rsp_hdr {
+ u8 opcode;
+ u8 subsystem;
+ __le16 rsvd2;
+ u8 status;
+ u8 additional_status;
+ __le16 rsvd6;
+ __le32 response_length;
+ __le32 actual_response_length;
+};
+
+#define SLI4_QUEUE_RQ_BATCH 8
+
+#define SZ_DMAADDR sizeof(struct sli4_dmaaddr)
+#define SLI4_RQST_CMDSZ(stype) sizeof(struct sli4_rqst_##stype)
+
+#define SLI4_RQST_PYLD_LEN(stype) \
+ cpu_to_le32(sizeof(struct sli4_rqst_##stype) - \
+ sizeof(struct sli4_rqst_hdr))
+
+#define SLI4_RQST_PYLD_LEN_VAR(stype, varpyld) \
+ cpu_to_le32((sizeof(struct sli4_rqst_##stype) + \
+ varpyld) - sizeof(struct sli4_rqst_hdr))
+
+#define SLI4_CFG_PYLD_LENGTH(stype) \
+ max(sizeof(struct sli4_rqst_##stype), \
+ sizeof(struct sli4_rsp_##stype))
+
+enum sli4_create_cqv2_e {
+ /* DW5_flags values*/
+ SLI4_CREATE_CQV2_CLSWM_MASK = 0x00003000,
+ SLI4_CREATE_CQV2_NODELAY = 0x00004000,
+ SLI4_CREATE_CQV2_AUTOVALID = 0x00008000,
+ SLI4_CREATE_CQV2_CQECNT_MASK = 0x18000000,
+ SLI4_CREATE_CQV2_VALID = 0x20000000,
+ SLI4_CREATE_CQV2_EVT = 0x80000000,
+ /* DW6W1_flags values*/
+ SLI4_CREATE_CQV2_ARM = 0x8000,
+};
+
+struct sli4_rqst_cmn_create_cq_v2 {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ u8 page_size;
+ u8 rsvd19;
+ __le32 dw5_flags;
+ __le16 eq_id;
+ __le16 dw6w1_arm;
+ __le16 cqe_count;
+ __le16 rsvd30;
+ __le32 rsvd32;
+ struct sli4_dmaaddr page_phys_addr[];
+};
+
+enum sli4_create_cqset_e {
+ /* DW5_flags values*/
+ SLI4_CREATE_CQSETV0_CLSWM_MASK = 0x00003000,
+ SLI4_CREATE_CQSETV0_NODELAY = 0x00004000,
+ SLI4_CREATE_CQSETV0_AUTOVALID = 0x00008000,
+ SLI4_CREATE_CQSETV0_CQECNT_MASK = 0x18000000,
+ SLI4_CREATE_CQSETV0_VALID = 0x20000000,
+ SLI4_CREATE_CQSETV0_EVT = 0x80000000,
+ /* DW5W1_flags values */
+ SLI4_CREATE_CQSETV0_CQE_COUNT = 0x7fff,
+ SLI4_CREATE_CQSETV0_ARM = 0x8000,
+};
+
+struct sli4_rqst_cmn_create_cq_set_v0 {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ u8 page_size;
+ u8 rsvd19;
+ __le32 dw5_flags;
+ __le16 num_cq_req;
+ __le16 dw6w1_flags;
+ __le16 eq_id[16];
+ struct sli4_dmaaddr page_phys_addr[];
+};
+
+/* CQE count */
+enum sli4_cq_cnt {
+ SLI4_CQ_CNT_256,
+ SLI4_CQ_CNT_512,
+ SLI4_CQ_CNT_1024,
+ SLI4_CQ_CNT_LARGE,
+};
+
+#define SLI4_CQ_CNT_SHIFT 27
+#define SLI4_CQ_CNT_VAL(type) (SLI4_CQ_CNT_##type << SLI4_CQ_CNT_SHIFT)
+
+#define SLI4_CQE_BYTES (4 * sizeof(u32))
+
+#define SLI4_CREATE_CQV2_MAX_PAGES 8
+
+/* Generic Common Create EQ/CQ/MQ/WQ/RQ Queue completion */
+struct sli4_rsp_cmn_create_queue {
+ struct sli4_rsp_hdr hdr;
+ __le16 q_id;
+ u8 rsvd18;
+ u8 ulp;
+ __le32 db_offset;
+ __le16 db_rs;
+ __le16 db_fmt;
+};
+
+struct sli4_rsp_cmn_create_queue_set {
+ struct sli4_rsp_hdr hdr;
+ __le16 q_id;
+ __le16 num_q_allocated;
+};
+
+/* Common Destroy Queue */
+struct sli4_rqst_cmn_destroy_q {
+ struct sli4_rqst_hdr hdr;
+ __le16 q_id;
+ __le16 rsvd;
+};
+
+struct sli4_rsp_cmn_destroy_q {
+ struct sli4_rsp_hdr hdr;
+};
+
+/* Modify the delay multiplier for EQs */
+struct sli4_eqdelay_rec {
+ __le32 eq_id;
+ __le32 phase;
+ __le32 delay_multiplier;
+};
+
+struct sli4_rqst_cmn_modify_eq_delay {
+ struct sli4_rqst_hdr hdr;
+ __le32 num_eq;
+ struct sli4_eqdelay_rec eq_delay_record[8];
+};
+
+struct sli4_rsp_cmn_modify_eq_delay {
+ struct sli4_rsp_hdr hdr;
+};
+
+enum sli4_create_cq_e {
+ /* DW5 */
+ SLI4_CREATE_EQ_AUTOVALID = 1u << 28,
+ SLI4_CREATE_EQ_VALID = 1u << 29,
+ SLI4_CREATE_EQ_EQESZ = 1u << 31,
+ /* DW6 */
+ SLI4_CREATE_EQ_COUNT = 7 << 26,
+ SLI4_CREATE_EQ_ARM = 1u << 31,
+ /* DW7 */
+ SLI4_CREATE_EQ_DELAYMULTI_SHIFT = 13,
+ SLI4_CREATE_EQ_DELAYMULTI_MASK = 0x007fe000,
+ SLI4_CREATE_EQ_DELAYMULTI = 0x00040000,
+};
+
+struct sli4_rqst_cmn_create_eq {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ __le16 rsvd18;
+ __le32 dw5_flags;
+ __le32 dw6_flags;
+ __le32 dw7_delaymulti;
+ __le32 rsvd32;
+ struct sli4_dmaaddr page_address[8];
+};
+
+struct sli4_rsp_cmn_create_eq {
+ struct sli4_rsp_cmn_create_queue q_rsp;
+};
+
+/* EQ count */
+enum sli4_eq_cnt {
+ SLI4_EQ_CNT_256,
+ SLI4_EQ_CNT_512,
+ SLI4_EQ_CNT_1024,
+ SLI4_EQ_CNT_2048,
+ SLI4_EQ_CNT_4096 = 3,
+};
+
+#define SLI4_EQ_CNT_SHIFT 26
+#define SLI4_EQ_CNT_VAL(type) (SLI4_EQ_CNT_##type << SLI4_EQ_CNT_SHIFT)
+
+#define SLI4_EQE_SIZE_4 0
+#define SLI4_EQE_SIZE_16 1
+
+/* Create a Mailbox Queue; accommodate v0 and v1 forms. */
+enum sli4_create_mq_flags {
+ /* DW6W1 */
+ SLI4_CREATE_MQEXT_RINGSIZE = 0xf,
+ SLI4_CREATE_MQEXT_CQID_SHIFT = 6,
+ SLI4_CREATE_MQEXT_CQIDV0_MASK = 0xffc0,
+ /* DW7 */
+ SLI4_CREATE_MQEXT_VAL = 1u << 31,
+ /* DW8 */
+ SLI4_CREATE_MQEXT_ACQV = 1u << 0,
+ SLI4_CREATE_MQEXT_ASYNC_CQIDV0 = 0x7fe,
+};
+
+struct sli4_rqst_cmn_create_mq_ext {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ __le16 cq_id_v1;
+ __le32 async_event_bitmap;
+ __le16 async_cq_id_v1;
+ __le16 dw6w1_flags;
+ __le32 dw7_val;
+ __le32 dw8_flags;
+ __le32 rsvd36;
+ struct sli4_dmaaddr page_phys_addr[];
+};
+
+struct sli4_rsp_cmn_create_mq_ext {
+ struct sli4_rsp_cmn_create_queue q_rsp;
+};
+
+enum sli4_mqe_size {
+ SLI4_MQE_SIZE_16 = 0x05,
+ SLI4_MQE_SIZE_32,
+ SLI4_MQE_SIZE_64,
+ SLI4_MQE_SIZE_128,
+};
+
+enum sli4_async_evt {
+ SLI4_ASYNC_EVT_LINK_STATE = 1 << 1,
+ SLI4_ASYNC_EVT_FIP = 1 << 2,
+ SLI4_ASYNC_EVT_GRP5 = 1 << 5,
+ SLI4_ASYNC_EVT_FC = 1 << 16,
+ SLI4_ASYNC_EVT_SLI_PORT = 1 << 17,
+};
+
+#define SLI4_ASYNC_EVT_FC_ALL \
+ (SLI4_ASYNC_EVT_LINK_STATE | \
+ SLI4_ASYNC_EVT_FIP | \
+ SLI4_ASYNC_EVT_GRP5 | \
+ SLI4_ASYNC_EVT_FC | \
+ SLI4_ASYNC_EVT_SLI_PORT)
+
+/* Create a Completion Queue. */
+struct sli4_rqst_cmn_create_cq_v0 {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ __le16 rsvd18;
+ __le32 dw5_flags;
+ __le32 dw6_flags;
+ __le32 rsvd28;
+ __le32 rsvd32;
+ struct sli4_dmaaddr page_phys_addr[];
+};
+
+enum sli4_create_rq_e {
+ SLI4_RQ_CREATE_DUA = 0x1,
+ SLI4_RQ_CREATE_BQU = 0x2,
+
+ SLI4_RQE_SIZE = 8,
+ SLI4_RQE_SIZE_8 = 0x2,
+ SLI4_RQE_SIZE_16 = 0x3,
+ SLI4_RQE_SIZE_32 = 0x4,
+ SLI4_RQE_SIZE_64 = 0x5,
+ SLI4_RQE_SIZE_128 = 0x6,
+
+ SLI4_RQ_PAGE_SIZE_4096 = 0x1,
+ SLI4_RQ_PAGE_SIZE_8192 = 0x2,
+ SLI4_RQ_PAGE_SIZE_16384 = 0x4,
+ SLI4_RQ_PAGE_SIZE_32768 = 0x8,
+ SLI4_RQ_PAGE_SIZE_64536 = 0x10,
+
+ SLI4_RQ_CREATE_V0_MAX_PAGES = 8,
+ SLI4_RQ_CREATE_V0_MIN_BUF_SIZE = 128,
+ SLI4_RQ_CREATE_V0_MAX_BUF_SIZE = 2048,
+};
+
+struct sli4_rqst_rq_create {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ u8 dua_bqu_byte;
+ u8 ulp;
+ __le16 rsvd16;
+ u8 rqe_count_byte;
+ u8 rsvd19;
+ __le32 rsvd20;
+ __le16 buffer_size;
+ __le16 cq_id;
+ __le32 rsvd28;
+ struct sli4_dmaaddr page_phys_addr[SLI4_RQ_CREATE_V0_MAX_PAGES];
+};
+
+struct sli4_rsp_rq_create {
+ struct sli4_rsp_cmn_create_queue rsp;
+};
+
+enum sli4_create_rqv1_e {
+ SLI4_RQ_CREATE_V1_DNB = 0x80,
+ SLI4_RQ_CREATE_V1_MAX_PAGES = 8,
+ SLI4_RQ_CREATE_V1_MIN_BUF_SIZE = 64,
+ SLI4_RQ_CREATE_V1_MAX_BUF_SIZE = 2048,
+};
+
+struct sli4_rqst_rq_create_v1 {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ u8 rsvd14;
+ u8 dim_dfd_dnb;
+ u8 page_size;
+ u8 rqe_size_byte;
+ __le16 rqe_count;
+ __le32 rsvd20;
+ __le16 rsvd24;
+ __le16 cq_id;
+ __le32 buffer_size;
+ struct sli4_dmaaddr page_phys_addr[SLI4_RQ_CREATE_V1_MAX_PAGES];
+};
+
+struct sli4_rsp_rq_create_v1 {
+ struct sli4_rsp_cmn_create_queue rsp;
+};
+
+#define SLI4_RQCREATEV2_DNB 0x80
+
+struct sli4_rqst_rq_create_v2 {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ u8 rq_count;
+ u8 dim_dfd_dnb;
+ u8 page_size;
+ u8 rqe_size_byte;
+ __le16 rqe_count;
+ __le16 hdr_buffer_size;
+ __le16 payload_buffer_size;
+ __le16 base_cq_id;
+ __le16 rsvd26;
+ __le32 rsvd42;
+ struct sli4_dmaaddr page_phys_addr[];
+};
+
+struct sli4_rsp_rq_create_v2 {
+ struct sli4_rsp_cmn_create_queue rsp;
+};
+
+#define SLI4_CQE_CODE_OFFSET 14
+
+enum sli4_cqe_code {
+ SLI4_CQE_CODE_WORK_REQUEST_COMPLETION = 0x01,
+ SLI4_CQE_CODE_RELEASE_WQE,
+ SLI4_CQE_CODE_RSVD,
+ SLI4_CQE_CODE_RQ_ASYNC,
+ SLI4_CQE_CODE_XRI_ABORTED,
+ SLI4_CQE_CODE_RQ_COALESCING,
+ SLI4_CQE_CODE_RQ_CONSUMPTION,
+ SLI4_CQE_CODE_MEASUREMENT_REPORTING,
+ SLI4_CQE_CODE_RQ_ASYNC_V1,
+ SLI4_CQE_CODE_RQ_COALESCING_V1,
+ SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD,
+ SLI4_CQE_CODE_OPTIMIZED_WRITE_DATA,
+};
+
+#define SLI4_WQ_CREATE_MAX_PAGES 8
+
+struct sli4_rqst_wq_create {
+ struct sli4_rqst_hdr hdr;
+ __le16 num_pages;
+ __le16 cq_id;
+ u8 page_size;
+ u8 wqe_size_byte;
+ __le16 wqe_count;
+ __le32 rsvd;
+ struct sli4_dmaaddr page_phys_addr[SLI4_WQ_CREATE_MAX_PAGES];
+};
+
+struct sli4_rsp_wq_create {
+ struct sli4_rsp_cmn_create_queue rsp;
+};
+
+enum sli4_link_attention_flags {
+ SLI4_LNK_ATTN_TYPE_LINK_UP = 0x01,
+ SLI4_LNK_ATTN_TYPE_LINK_DOWN = 0x02,
+ SLI4_LNK_ATTN_TYPE_NO_HARD_ALPA = 0x03,
+
+ SLI4_LNK_ATTN_P2P = 0x01,
+ SLI4_LNK_ATTN_FC_AL = 0x02,
+ SLI4_LNK_ATTN_INTERNAL_LOOPBACK = 0x03,
+ SLI4_LNK_ATTN_SERDES_LOOPBACK = 0x04,
+};
+
+struct sli4_link_attention {
+ u8 link_number;
+ u8 attn_type;
+ u8 topology;
+ u8 port_speed;
+ u8 port_fault;
+ u8 shared_link_status;
+ __le16 logical_link_speed;
+ __le32 event_tag;
+ u8 rsvd12;
+ u8 event_code;
+ u8 event_type;
+ u8 flags;
+};
+
+enum sli4_link_event_type {
+ SLI4_EVENT_LINK_ATTENTION = 0x01,
+ SLI4_EVENT_SHARED_LINK_ATTENTION = 0x02,
+};
+
+enum sli4_wcqe_flags {
+ SLI4_WCQE_XB = 0x10,
+ SLI4_WCQE_QX = 0x80,
+};
+
+struct sli4_fc_wcqe {
+ u8 hw_status;
+ u8 status;
+ __le16 request_tag;
+ __le32 wqe_specific_1;
+ __le32 wqe_specific_2;
+ u8 rsvd12;
+ u8 qx_byte;
+ u8 code;
+ u8 flags;
+};
+
+/* FC WQ consumed CQ queue entry */
+struct sli4_fc_wqec {
+ __le32 rsvd0;
+ __le32 rsvd1;
+ __le16 wqe_index;
+ __le16 wq_id;
+ __le16 rsvd12;
+ u8 code;
+ u8 vld_byte;
+};
+
+/* FC Completion Status Codes. */
+enum sli4_wcqe_status {
+ SLI4_FC_WCQE_STATUS_SUCCESS,
+ SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE,
+ SLI4_FC_WCQE_STATUS_REMOTE_STOP,
+ SLI4_FC_WCQE_STATUS_LOCAL_REJECT,
+ SLI4_FC_WCQE_STATUS_NPORT_RJT,
+ SLI4_FC_WCQE_STATUS_FABRIC_RJT,
+ SLI4_FC_WCQE_STATUS_NPORT_BSY,
+ SLI4_FC_WCQE_STATUS_FABRIC_BSY,
+ SLI4_FC_WCQE_STATUS_RSVD,
+ SLI4_FC_WCQE_STATUS_LS_RJT,
+ SLI4_FC_WCQE_STATUS_RX_BUF_OVERRUN,
+ SLI4_FC_WCQE_STATUS_CMD_REJECT,
+ SLI4_FC_WCQE_STATUS_FCP_TGT_LENCHECK,
+ SLI4_FC_WCQE_STATUS_RSVD1,
+ SLI4_FC_WCQE_STATUS_ELS_CMPLT_NO_AUTOREG,
+ SLI4_FC_WCQE_STATUS_RSVD2,
+ SLI4_FC_WCQE_STATUS_RQ_SUCCESS,
+ SLI4_FC_WCQE_STATUS_RQ_BUF_LEN_EXCEEDED,
+ SLI4_FC_WCQE_STATUS_RQ_INSUFF_BUF_NEEDED,
+ SLI4_FC_WCQE_STATUS_RQ_INSUFF_FRM_DISC,
+ SLI4_FC_WCQE_STATUS_RQ_DMA_FAILURE,
+ SLI4_FC_WCQE_STATUS_FCP_RSP_TRUNCATE,
+ SLI4_FC_WCQE_STATUS_DI_ERROR,
+ SLI4_FC_WCQE_STATUS_BA_RJT,
+ SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_NEEDED,
+ SLI4_FC_WCQE_STATUS_RQ_INSUFF_XRI_DISC,
+ SLI4_FC_WCQE_STATUS_RX_ERROR_DETECT,
+ SLI4_FC_WCQE_STATUS_RX_ABORT_REQUEST,
+
+ /* driver generated status codes */
+ SLI4_FC_WCQE_STATUS_DISPATCH_ERROR = 0xfd,
+ SLI4_FC_WCQE_STATUS_SHUTDOWN = 0xfe,
+ SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT = 0xff,
+};
+
+/* DI_ERROR Extended Status */
+enum sli4_fc_di_error_status {
+ SLI4_FC_DI_ERROR_GE = 1 << 0,
+ SLI4_FC_DI_ERROR_AE = 1 << 1,
+ SLI4_FC_DI_ERROR_RE = 1 << 2,
+ SLI4_FC_DI_ERROR_TDPV = 1 << 3,
+ SLI4_FC_DI_ERROR_UDB = 1 << 4,
+ SLI4_FC_DI_ERROR_EDIR = 1 << 5,
+};
+
+/* WQE DIF field contents */
+enum sli4_dif_fields {
+ SLI4_DIF_DISABLED,
+ SLI4_DIF_PASS_THROUGH,
+ SLI4_DIF_STRIP,
+ SLI4_DIF_INSERT,
+};
+
+/* Work Queue Entry (WQE) types */
+enum sli4_wqe_types {
+ SLI4_WQE_ABORT = 0x0f,
+ SLI4_WQE_ELS_REQUEST64 = 0x8a,
+ SLI4_WQE_FCP_IBIDIR64 = 0xac,
+ SLI4_WQE_FCP_IREAD64 = 0x9a,
+ SLI4_WQE_FCP_IWRITE64 = 0x98,
+ SLI4_WQE_FCP_ICMND64 = 0x9c,
+ SLI4_WQE_FCP_TRECEIVE64 = 0xa1,
+ SLI4_WQE_FCP_CONT_TRECEIVE64 = 0xe5,
+ SLI4_WQE_FCP_TRSP64 = 0xa3,
+ SLI4_WQE_FCP_TSEND64 = 0x9f,
+ SLI4_WQE_GEN_REQUEST64 = 0xc2,
+ SLI4_WQE_SEND_FRAME = 0xe1,
+ SLI4_WQE_XMIT_BCAST64 = 0x84,
+ SLI4_WQE_XMIT_BLS_RSP = 0x97,
+ SLI4_WQE_ELS_RSP64 = 0x95,
+ SLI4_WQE_XMIT_SEQUENCE64 = 0x82,
+ SLI4_WQE_REQUEUE_XRI = 0x93,
+};
+
+/* WQE command types */
+enum sli4_wqe_cmds {
+ SLI4_CMD_FCP_IREAD64_WQE = 0x00,
+ SLI4_CMD_FCP_ICMND64_WQE = 0x00,
+ SLI4_CMD_FCP_IWRITE64_WQE = 0x01,
+ SLI4_CMD_FCP_TRECEIVE64_WQE = 0x02,
+ SLI4_CMD_FCP_TRSP64_WQE = 0x03,
+ SLI4_CMD_FCP_TSEND64_WQE = 0x07,
+ SLI4_CMD_GEN_REQUEST64_WQE = 0x08,
+ SLI4_CMD_XMIT_BCAST64_WQE = 0x08,
+ SLI4_CMD_XMIT_BLS_RSP64_WQE = 0x08,
+ SLI4_CMD_ABORT_WQE = 0x08,
+ SLI4_CMD_XMIT_SEQUENCE64_WQE = 0x08,
+ SLI4_CMD_REQUEUE_XRI_WQE = 0x0a,
+ SLI4_CMD_SEND_FRAME_WQE = 0x0a,
+};
+
+#define SLI4_WQE_SIZE 0x05
+#define SLI4_WQE_EXT_SIZE 0x06
+
+#define SLI4_WQE_BYTES (16 * sizeof(u32))
+#define SLI4_WQE_EXT_BYTES (32 * sizeof(u32))
+
+/* Mask for ccp (CS_CTL) */
+#define SLI4_MASK_CCP 0xfe
+
+/* Generic WQE */
+enum sli4_gen_wqe_flags {
+ SLI4_GEN_WQE_EBDECNT = 0xf,
+ SLI4_GEN_WQE_LEN_LOC = 0x3 << 7,
+ SLI4_GEN_WQE_QOSD = 1 << 9,
+ SLI4_GEN_WQE_XBL = 1 << 11,
+ SLI4_GEN_WQE_HLM = 1 << 12,
+ SLI4_GEN_WQE_IOD = 1 << 13,
+ SLI4_GEN_WQE_DBDE = 1 << 14,
+ SLI4_GEN_WQE_WQES = 1 << 15,
+
+ SLI4_GEN_WQE_PRI = 0x7,
+ SLI4_GEN_WQE_PV = 1 << 3,
+ SLI4_GEN_WQE_EAT = 1 << 4,
+ SLI4_GEN_WQE_XC = 1 << 5,
+ SLI4_GEN_WQE_CCPE = 1 << 7,
+
+ SLI4_GEN_WQE_CMDTYPE = 0xf,
+ SLI4_GEN_WQE_WQEC = 1 << 7,
+};
+
+struct sli4_generic_wqe {
+ __le32 cmd_spec0_5[6];
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 ct_byte;
+ u8 command;
+ u8 class_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 rsvd34;
+ __le16 dw10w0_flags;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+ u8 cmdtype_wqec_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+};
+
+/* WQE used to abort exchanges. */
+enum sli4_abort_wqe_flags {
+ SLI4_ABRT_WQE_IR = 0x02,
+
+ SLI4_ABRT_WQE_EBDECNT = 0xf,
+ SLI4_ABRT_WQE_LEN_LOC = 0x3 << 7,
+ SLI4_ABRT_WQE_QOSD = 1 << 9,
+ SLI4_ABRT_WQE_XBL = 1 << 11,
+ SLI4_ABRT_WQE_IOD = 1 << 13,
+ SLI4_ABRT_WQE_DBDE = 1 << 14,
+ SLI4_ABRT_WQE_WQES = 1 << 15,
+
+ SLI4_ABRT_WQE_PRI = 0x7,
+ SLI4_ABRT_WQE_PV = 1 << 3,
+ SLI4_ABRT_WQE_EAT = 1 << 4,
+ SLI4_ABRT_WQE_XC = 1 << 5,
+ SLI4_ABRT_WQE_CCPE = 1 << 7,
+
+ SLI4_ABRT_WQE_CMDTYPE = 0xf,
+ SLI4_ABRT_WQE_WQEC = 1 << 7,
+};
+
+struct sli4_abort_wqe {
+ __le32 rsvd0;
+ __le32 rsvd4;
+ __le32 ext_t_tag;
+ u8 ia_ir_byte;
+ u8 criteria;
+ __le16 rsvd10;
+ __le32 ext_t_mask;
+ __le32 t_mask;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 ct_byte;
+ u8 command;
+ u8 class_byte;
+ u8 timer;
+ __le32 t_tag;
+ __le16 request_tag;
+ __le16 rsvd34;
+ __le16 dw10w0_flags;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+ u8 cmdtype_wqec_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+};
+
+enum sli4_abort_criteria {
+ SLI4_ABORT_CRITERIA_XRI_TAG = 0x01,
+ SLI4_ABORT_CRITERIA_ABORT_TAG,
+ SLI4_ABORT_CRITERIA_REQUEST_TAG,
+ SLI4_ABORT_CRITERIA_EXT_ABORT_TAG,
+};
+
+enum sli4_abort_type {
+ SLI4_ABORT_XRI,
+ SLI4_ABORT_ABORT_ID,
+ SLI4_ABORT_REQUEST_ID,
+ SLI4_ABORT_MAX, /* must be last */
+};
+
+/* WQE used to create an ELS request. */
+enum sli4_els_req_wqe_flags {
+ SLI4_REQ_WQE_QOSD = 0x2,
+ SLI4_REQ_WQE_DBDE = 0x40,
+ SLI4_REQ_WQE_XBL = 0x8,
+ SLI4_REQ_WQE_XC = 0x20,
+ SLI4_REQ_WQE_IOD = 0x20,
+ SLI4_REQ_WQE_HLM = 0x10,
+ SLI4_REQ_WQE_CCPE = 0x80,
+ SLI4_REQ_WQE_EAT = 0x10,
+ SLI4_REQ_WQE_WQES = 0x80,
+ SLI4_REQ_WQE_PU_SHFT = 4,
+ SLI4_REQ_WQE_CT_SHFT = 2,
+ SLI4_REQ_WQE_CT = 0xc,
+ SLI4_REQ_WQE_ELSID_SHFT = 4,
+ SLI4_REQ_WQE_SP_SHFT = 24,
+ SLI4_REQ_WQE_LEN_LOC_BIT1 = 0x80,
+ SLI4_REQ_WQE_LEN_LOC_BIT2 = 0x1,
+};
+
+struct sli4_els_request64_wqe {
+ struct sli4_bde els_request_payload;
+ __le32 els_request_payload_length;
+ __le32 sid_sp_dword;
+ __le32 remote_id_dword;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 ct_byte;
+ u8 command;
+ u8 class_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 temporary_rpi;
+ u8 len_loc1_byte;
+ u8 qosd_xbl_hlm_iod_dbde_wqes;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+ u8 cmdtype_elsid_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+ struct sli4_bde els_response_payload_bde;
+ __le32 max_response_payload_length;
+};
+
+/* WQE used to create an FCP initiator no data command. */
+enum sli4_icmd_wqe_flags {
+ SLI4_ICMD_WQE_DBDE = 0x40,
+ SLI4_ICMD_WQE_XBL = 0x8,
+ SLI4_ICMD_WQE_XC = 0x20,
+ SLI4_ICMD_WQE_IOD = 0x20,
+ SLI4_ICMD_WQE_HLM = 0x10,
+ SLI4_ICMD_WQE_CCPE = 0x80,
+ SLI4_ICMD_WQE_EAT = 0x10,
+ SLI4_ICMD_WQE_APPID = 0x10,
+ SLI4_ICMD_WQE_WQES = 0x80,
+ SLI4_ICMD_WQE_PU_SHFT = 4,
+ SLI4_ICMD_WQE_CT_SHFT = 2,
+ SLI4_ICMD_WQE_BS_SHFT = 4,
+ SLI4_ICMD_WQE_LEN_LOC_BIT1 = 0x80,
+ SLI4_ICMD_WQE_LEN_LOC_BIT2 = 0x1,
+};
+
+struct sli4_fcp_icmnd64_wqe {
+ struct sli4_bde bde;
+ __le16 payload_offset_length;
+ __le16 fcp_cmd_buffer_length;
+ __le32 rsvd12;
+ __le32 remote_n_port_id_dword;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 dif_ct_bs_byte;
+ u8 command;
+ u8 class_pu_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 rsvd34;
+ u8 len_loc1_byte;
+ u8 qosd_xbl_hlm_iod_dbde_wqes;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+ u8 cmd_type_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+ __le32 rsvd44;
+ __le32 rsvd48;
+ __le32 rsvd52;
+ __le32 rsvd56;
+};
+
+/* WQE used to create an FCP initiator read. */
+enum sli4_ir_wqe_flags {
+ SLI4_IR_WQE_DBDE = 0x40,
+ SLI4_IR_WQE_XBL = 0x8,
+ SLI4_IR_WQE_XC = 0x20,
+ SLI4_IR_WQE_IOD = 0x20,
+ SLI4_IR_WQE_HLM = 0x10,
+ SLI4_IR_WQE_CCPE = 0x80,
+ SLI4_IR_WQE_EAT = 0x10,
+ SLI4_IR_WQE_APPID = 0x10,
+ SLI4_IR_WQE_WQES = 0x80,
+ SLI4_IR_WQE_PU_SHFT = 4,
+ SLI4_IR_WQE_CT_SHFT = 2,
+ SLI4_IR_WQE_BS_SHFT = 4,
+ SLI4_IR_WQE_LEN_LOC_BIT1 = 0x80,
+ SLI4_IR_WQE_LEN_LOC_BIT2 = 0x1,
+};
+
+struct sli4_fcp_iread64_wqe {
+ struct sli4_bde bde;
+ __le16 payload_offset_length;
+ __le16 fcp_cmd_buffer_length;
+
+ __le32 total_transfer_length;
+
+ __le32 remote_n_port_id_dword;
+
+ __le16 xri_tag;
+ __le16 context_tag;
+
+ u8 dif_ct_bs_byte;
+ u8 command;
+ u8 class_pu_byte;
+ u8 timer;
+
+ __le32 abort_tag;
+
+ __le16 request_tag;
+ __le16 rsvd34;
+
+ u8 len_loc1_byte;
+ u8 qosd_xbl_hlm_iod_dbde_wqes;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+
+ u8 cmd_type_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+
+ __le32 rsvd44;
+ struct sli4_bde first_data_bde;
+};
+
+/* WQE used to create an FCP initiator write. */
+enum sli4_iwr_wqe_flags {
+ SLI4_IWR_WQE_DBDE = 0x40,
+ SLI4_IWR_WQE_XBL = 0x8,
+ SLI4_IWR_WQE_XC = 0x20,
+ SLI4_IWR_WQE_IOD = 0x20,
+ SLI4_IWR_WQE_HLM = 0x10,
+ SLI4_IWR_WQE_DNRX = 0x10,
+ SLI4_IWR_WQE_CCPE = 0x80,
+ SLI4_IWR_WQE_EAT = 0x10,
+ SLI4_IWR_WQE_APPID = 0x10,
+ SLI4_IWR_WQE_WQES = 0x80,
+ SLI4_IWR_WQE_PU_SHFT = 4,
+ SLI4_IWR_WQE_CT_SHFT = 2,
+ SLI4_IWR_WQE_BS_SHFT = 4,
+ SLI4_IWR_WQE_LEN_LOC_BIT1 = 0x80,
+ SLI4_IWR_WQE_LEN_LOC_BIT2 = 0x1,
+};
+
+struct sli4_fcp_iwrite64_wqe {
+ struct sli4_bde bde;
+ __le16 payload_offset_length;
+ __le16 fcp_cmd_buffer_length;
+ __le16 total_transfer_length;
+ __le16 initial_transfer_length;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 dif_ct_bs_byte;
+ u8 command;
+ u8 class_pu_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 rsvd34;
+ u8 len_loc1_byte;
+ u8 qosd_xbl_hlm_iod_dbde_wqes;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+ u8 cmd_type_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+ __le32 remote_n_port_id_dword;
+ struct sli4_bde first_data_bde;
+};
+
+struct sli4_fcp_128byte_wqe {
+ u32 dw[32];
+};
+
+/* WQE used to create an FCP target receive */
+enum sli4_trcv_wqe_flags {
+ SLI4_TRCV_WQE_DBDE = 0x40,
+ SLI4_TRCV_WQE_XBL = 0x8,
+ SLI4_TRCV_WQE_AR = 0x8,
+ SLI4_TRCV_WQE_XC = 0x20,
+ SLI4_TRCV_WQE_IOD = 0x20,
+ SLI4_TRCV_WQE_HLM = 0x10,
+ SLI4_TRCV_WQE_DNRX = 0x10,
+ SLI4_TRCV_WQE_CCPE = 0x80,
+ SLI4_TRCV_WQE_EAT = 0x10,
+ SLI4_TRCV_WQE_APPID = 0x10,
+ SLI4_TRCV_WQE_WQES = 0x80,
+ SLI4_TRCV_WQE_PU_SHFT = 4,
+ SLI4_TRCV_WQE_CT_SHFT = 2,
+ SLI4_TRCV_WQE_BS_SHFT = 4,
+ SLI4_TRCV_WQE_LEN_LOC_BIT2 = 0x1,
+};
+
+struct sli4_fcp_treceive64_wqe {
+ struct sli4_bde bde;
+ __le32 payload_offset_length;
+ __le32 relative_offset;
+ union {
+ __le16 sec_xri_tag;
+ __le16 rsvd;
+ __le32 dword;
+ } dword5;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 dif_ct_bs_byte;
+ u8 command;
+ u8 class_ar_pu_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 remote_xid;
+ u8 lloc1_appid;
+ u8 qosd_xbl_hlm_iod_dbde_wqes;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+ u8 cmd_type_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+ __le32 fcp_data_receive_length;
+ struct sli4_bde first_data_bde;
+};
+
+/* WQE used to create an FCP target response */
+enum sli4_trsp_wqe_flags {
+ SLI4_TRSP_WQE_AG = 0x8,
+ SLI4_TRSP_WQE_DBDE = 0x40,
+ SLI4_TRSP_WQE_XBL = 0x8,
+ SLI4_TRSP_WQE_XC = 0x20,
+ SLI4_TRSP_WQE_HLM = 0x10,
+ SLI4_TRSP_WQE_DNRX = 0x10,
+ SLI4_TRSP_WQE_CCPE = 0x80,
+ SLI4_TRSP_WQE_EAT = 0x10,
+ SLI4_TRSP_WQE_APPID = 0x10,
+ SLI4_TRSP_WQE_WQES = 0x80,
+};
+
+struct sli4_fcp_trsp64_wqe {
+ struct sli4_bde bde;
+ __le32 fcp_response_length;
+ __le32 rsvd12;
+ __le32 dword5;
+ __le16 xri_tag;
+ __le16 rpi;
+ u8 ct_dnrx_byte;
+ u8 command;
+ u8 class_ag_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 remote_xid;
+ u8 lloc1_appid;
+ u8 qosd_xbl_hlm_dbde_wqes;
+ u8 eat_xc_ccpe;
+ u8 ccp;
+ u8 cmd_type_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+ __le32 rsvd44;
+ __le32 rsvd48;
+ __le32 rsvd52;
+ __le32 rsvd56;
+};
+
+/* WQE used to create an FCP target send (DATA IN). */
+enum sli4_tsend_wqe_flags {
+ SLI4_TSEND_WQE_XBL = 0x8,
+ SLI4_TSEND_WQE_DBDE = 0x40,
+ SLI4_TSEND_WQE_IOD = 0x20,
+ SLI4_TSEND_WQE_QOSD = 0x2,
+ SLI4_TSEND_WQE_HLM = 0x10,
+ SLI4_TSEND_WQE_PU_SHFT = 4,
+ SLI4_TSEND_WQE_AR = 0x8,
+ SLI4_TSEND_CT_SHFT = 2,
+ SLI4_TSEND_BS_SHFT = 4,
+ SLI4_TSEND_LEN_LOC_BIT2 = 0x1,
+ SLI4_TSEND_CCPE = 0x80,
+ SLI4_TSEND_APPID_VALID = 0x20,
+ SLI4_TSEND_WQES = 0x80,
+ SLI4_TSEND_XC = 0x20,
+ SLI4_TSEND_EAT = 0x10,
+};
+
+struct sli4_fcp_tsend64_wqe {
+ struct sli4_bde bde;
+ __le32 payload_offset_length;
+ __le32 relative_offset;
+ __le32 dword5;
+ __le16 xri_tag;
+ __le16 rpi;
+ u8 ct_byte;
+ u8 command;
+ u8 class_pu_ar_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 remote_xid;
+ u8 dw10byte0;
+ u8 ll_qd_xbl_hlm_iod_dbde;
+ u8 dw10byte2;
+ u8 ccp;
+ u8 cmd_type_byte;
+ u8 rsvd45;
+ __le16 cq_id;
+ __le32 fcp_data_transmit_length;
+ struct sli4_bde first_data_bde;
+};
+
+/* WQE used to create a general request. */
+enum sli4_gen_req_wqe_flags {
+ SLI4_GEN_REQ64_WQE_XBL = 0x8,
+ SLI4_GEN_REQ64_WQE_DBDE = 0x40,
+ SLI4_GEN_REQ64_WQE_IOD = 0x20,
+ SLI4_GEN_REQ64_WQE_QOSD = 0x2,
+ SLI4_GEN_REQ64_WQE_HLM = 0x10,
+ SLI4_GEN_REQ64_CT_SHFT = 2,
+};
+
+struct sli4_gen_request64_wqe {
+ struct sli4_bde bde;
+ __le32 request_payload_length;
+ __le32 relative_offset;
+ u8 rsvd17;
+ u8 df_ctl;
+ u8 type;
+ u8 r_ctl;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 ct_byte;
+ u8 command;
+ u8 class_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 rsvd34;
+ u8 dw10flags0;
+ u8 dw10flags1;
+ u8 dw10flags2;
+ u8 ccp;
+ u8 cmd_type_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+ __le32 remote_n_port_id_dword;
+ __le32 rsvd48;
+ __le32 rsvd52;
+ __le32 max_response_payload_length;
+};
+
+/* WQE used to create a send frame request */
+enum sli4_sf_wqe_flags {
+ SLI4_SF_WQE_DBDE = 0x40,
+ SLI4_SF_PU = 0x30,
+ SLI4_SF_CT = 0xc,
+ SLI4_SF_QOSD = 0x2,
+ SLI4_SF_LEN_LOC_BIT1 = 0x80,
+ SLI4_SF_LEN_LOC_BIT2 = 0x1,
+ SLI4_SF_XC = 0x20,
+ SLI4_SF_XBL = 0x8,
+};
+
+struct sli4_send_frame_wqe {
+ struct sli4_bde bde;
+ __le32 frame_length;
+ __le32 fc_header_0_1[2];
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 ct_byte;
+ u8 command;
+ u8 dw7flags0;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ u8 eof;
+ u8 sof;
+ u8 dw10flags0;
+ u8 dw10flags1;
+ u8 dw10flags2;
+ u8 ccp;
+ u8 cmd_type_byte;
+ u8 rsvd41;
+ __le16 cq_id;
+ __le32 fc_header_2_5[4];
+};
+
+/* WQE used to create a transmit sequence */
+enum sli4_seq_wqe_flags {
+ SLI4_SEQ_WQE_DBDE = 0x4000,
+ SLI4_SEQ_WQE_XBL = 0x800,
+ SLI4_SEQ_WQE_SI = 0x4,
+ SLI4_SEQ_WQE_FT = 0x8,
+ SLI4_SEQ_WQE_XO = 0x40,
+ SLI4_SEQ_WQE_LS = 0x80,
+ SLI4_SEQ_WQE_DIF = 0x3,
+ SLI4_SEQ_WQE_BS = 0x70,
+ SLI4_SEQ_WQE_PU = 0x30,
+ SLI4_SEQ_WQE_HLM = 0x1000,
+ SLI4_SEQ_WQE_IOD_SHIFT = 13,
+ SLI4_SEQ_WQE_CT_SHIFT = 2,
+ SLI4_SEQ_WQE_LEN_LOC_SHIFT = 7,
+};
+
+struct sli4_xmit_sequence64_wqe {
+ struct sli4_bde bde;
+ __le32 remote_n_port_id_dword;
+ __le32 relative_offset;
+ u8 dw5flags0;
+ u8 df_ctl;
+ u8 type;
+ u8 r_ctl;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 dw7flags0;
+ u8 command;
+ u8 dw7flags1;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 remote_xid;
+ __le16 dw10w0;
+ u8 dw10flags0;
+ u8 ccp;
+ u8 cmd_type_wqec_byte;
+ u8 rsvd45;
+ __le16 cq_id;
+ __le32 sequence_payload_len;
+ __le32 rsvd48;
+ __le32 rsvd52;
+ __le32 rsvd56;
+};
+
+/*
+ * WQE used unblock the specified XRI and to release
+ * it to the SLI Port's free pool.
+ */
+enum sli4_requeue_wqe_flags {
+ SLI4_REQU_XRI_WQE_XC = 0x20,
+ SLI4_REQU_XRI_WQE_QOSD = 0x2,
+};
+
+struct sli4_requeue_xri_wqe {
+ __le32 rsvd0;
+ __le32 rsvd4;
+ __le32 rsvd8;
+ __le32 rsvd12;
+ __le32 rsvd16;
+ __le32 rsvd20;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 ct_byte;
+ u8 command;
+ u8 class_byte;
+ u8 timer;
+ __le32 rsvd32;
+ __le16 request_tag;
+ __le16 rsvd34;
+ __le16 flags0;
+ __le16 flags1;
+ __le16 flags2;
+ u8 ccp;
+ u8 cmd_type_wqec_byte;
+ u8 rsvd42;
+ __le16 cq_id;
+ __le32 rsvd44;
+ __le32 rsvd48;
+ __le32 rsvd52;
+ __le32 rsvd56;
+};
+
+/* WQE used to create a BLS response */
+enum sli4_bls_rsp_wqe_flags {
+ SLI4_BLS_RSP_RID = 0xffffff,
+ SLI4_BLS_RSP_WQE_AR = 0x40000000,
+ SLI4_BLS_RSP_WQE_CT_SHFT = 2,
+ SLI4_BLS_RSP_WQE_QOSD = 0x2,
+ SLI4_BLS_RSP_WQE_HLM = 0x10,
+};
+
+struct sli4_xmit_bls_rsp_wqe {
+ __le32 payload_word0;
+ __le16 rx_id;
+ __le16 ox_id;
+ __le16 high_seq_cnt;
+ __le16 low_seq_cnt;
+ __le32 rsvd12;
+ __le32 local_n_port_id_dword;
+ __le32 remote_id_dword;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 dw8flags0;
+ u8 command;
+ u8 dw8flags1;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 rsvd38;
+ u8 dw11flags0;
+ u8 dw11flags1;
+ u8 dw11flags2;
+ u8 ccp;
+ u8 dw12flags0;
+ u8 rsvd45;
+ __le16 cq_id;
+ __le16 temporary_rpi;
+ u8 rsvd50;
+ u8 rsvd51;
+ __le32 rsvd52;
+ __le32 rsvd56;
+ __le32 rsvd60;
+};
+
+enum sli_bls_type {
+ SLI4_SLI_BLS_ACC,
+ SLI4_SLI_BLS_RJT,
+ SLI4_SLI_BLS_MAX
+};
+
+struct sli_bls_payload {
+ enum sli_bls_type type;
+ __le16 ox_id;
+ __le16 rx_id;
+ union {
+ struct {
+ u8 seq_id_validity;
+ u8 seq_id_last;
+ u8 rsvd2;
+ u8 rsvd3;
+ u16 ox_id;
+ u16 rx_id;
+ __le16 low_seq_cnt;
+ __le16 high_seq_cnt;
+ } acc;
+ struct {
+ u8 vendor_unique;
+ u8 reason_explanation;
+ u8 reason_code;
+ u8 rsvd3;
+ } rjt;
+ } u;
+};
+
+/* WQE used to create an ELS response */
+
+enum sli4_els_rsp_flags {
+ SLI4_ELS_SID = 0xffffff,
+ SLI4_ELS_RID = 0xffffff,
+ SLI4_ELS_DBDE = 0x40,
+ SLI4_ELS_XBL = 0x8,
+ SLI4_ELS_IOD = 0x20,
+ SLI4_ELS_QOSD = 0x2,
+ SLI4_ELS_XC = 0x20,
+ SLI4_ELS_CT_OFFSET = 0X2,
+ SLI4_ELS_SP = 0X1000000,
+ SLI4_ELS_HLM = 0X10,
+};
+
+struct sli4_xmit_els_rsp64_wqe {
+ struct sli4_bde els_response_payload;
+ __le32 els_response_payload_length;
+ __le32 sid_dw;
+ __le32 rid_dw;
+ __le16 xri_tag;
+ __le16 context_tag;
+ u8 ct_byte;
+ u8 command;
+ u8 class_byte;
+ u8 timer;
+ __le32 abort_tag;
+ __le16 request_tag;
+ __le16 ox_id;
+ u8 flags1;
+ u8 flags2;
+ u8 flags3;
+ u8 flags4;
+ u8 cmd_type_wqec;
+ u8 rsvd34;
+ __le16 cq_id;
+ __le16 temporary_rpi;
+ __le16 rsvd38;
+ u32 rsvd40;
+ u32 rsvd44;
+ u32 rsvd48;
+};
+
+/* Local Reject Reason Codes */
+enum sli4_fc_local_rej_codes {
+ SLI4_FC_LOCAL_REJECT_UNKNOWN,
+ SLI4_FC_LOCAL_REJECT_MISSING_CONTINUE,
+ SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT,
+ SLI4_FC_LOCAL_REJECT_INTERNAL_ERROR,
+ SLI4_FC_LOCAL_REJECT_INVALID_RPI,
+ SLI4_FC_LOCAL_REJECT_NO_XRI,
+ SLI4_FC_LOCAL_REJECT_ILLEGAL_COMMAND,
+ SLI4_FC_LOCAL_REJECT_XCHG_DROPPED,
+ SLI4_FC_LOCAL_REJECT_ILLEGAL_FIELD,
+ SLI4_FC_LOCAL_REJECT_RPI_SUSPENDED,
+ SLI4_FC_LOCAL_REJECT_RSVD,
+ SLI4_FC_LOCAL_REJECT_RSVD1,
+ SLI4_FC_LOCAL_REJECT_NO_ABORT_MATCH,
+ SLI4_FC_LOCAL_REJECT_TX_DMA_FAILED,
+ SLI4_FC_LOCAL_REJECT_RX_DMA_FAILED,
+ SLI4_FC_LOCAL_REJECT_ILLEGAL_FRAME,
+ SLI4_FC_LOCAL_REJECT_RSVD2,
+ SLI4_FC_LOCAL_REJECT_NO_RESOURCES, //0x11
+ SLI4_FC_LOCAL_REJECT_FCP_CONF_FAILURE,
+ SLI4_FC_LOCAL_REJECT_ILLEGAL_LENGTH,
+ SLI4_FC_LOCAL_REJECT_UNSUPPORTED_FEATURE,
+ SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS,
+ SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED,
+ SLI4_FC_LOCAL_REJECT_RCV_BUFFER_TIMEOUT,
+ SLI4_FC_LOCAL_REJECT_LOOP_OPEN_FAILURE,
+ SLI4_FC_LOCAL_REJECT_RSVD3,
+ SLI4_FC_LOCAL_REJECT_LINK_DOWN,
+ SLI4_FC_LOCAL_REJECT_CORRUPTED_DATA,
+ SLI4_FC_LOCAL_REJECT_CORRUPTED_RPI,
+ SLI4_FC_LOCAL_REJECT_OUTOFORDER_DATA,
+ SLI4_FC_LOCAL_REJECT_OUTOFORDER_ACK,
+ SLI4_FC_LOCAL_REJECT_DUP_FRAME,
+ SLI4_FC_LOCAL_REJECT_LINK_CONTROL_FRAME, //0x20
+ SLI4_FC_LOCAL_REJECT_BAD_HOST_ADDRESS,
+ SLI4_FC_LOCAL_REJECT_RSVD4,
+ SLI4_FC_LOCAL_REJECT_MISSING_HDR_BUFFER,
+ SLI4_FC_LOCAL_REJECT_MSEQ_CHAIN_CORRUPTED,
+ SLI4_FC_LOCAL_REJECT_ABORTMULT_REQUESTED,
+ SLI4_FC_LOCAL_REJECT_BUFFER_SHORTAGE = 0x28,
+ SLI4_FC_LOCAL_REJECT_RCV_XRIBUF_WAITING,
+ SLI4_FC_LOCAL_REJECT_INVALID_VPI = 0x2e,
+ SLI4_FC_LOCAL_REJECT_NO_FPORT_DETECTED,
+ SLI4_FC_LOCAL_REJECT_MISSING_XRIBUF,
+ SLI4_FC_LOCAL_REJECT_RSVD5,
+ SLI4_FC_LOCAL_REJECT_INVALID_XRI,
+ SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET = 0x40,
+ SLI4_FC_LOCAL_REJECT_MISSING_RELOFFSET,
+ SLI4_FC_LOCAL_REJECT_INSUFF_BUFFERSPACE,
+ SLI4_FC_LOCAL_REJECT_MISSING_SI,
+ SLI4_FC_LOCAL_REJECT_MISSING_ES,
+ SLI4_FC_LOCAL_REJECT_INCOMPLETE_XFER,
+ SLI4_FC_LOCAL_REJECT_SLER_FAILURE,
+ SLI4_FC_LOCAL_REJECT_SLER_CMD_RCV_FAILURE,
+ SLI4_FC_LOCAL_REJECT_SLER_REC_RJT_ERR,
+ SLI4_FC_LOCAL_REJECT_SLER_REC_SRR_RETRY_ERR,
+ SLI4_FC_LOCAL_REJECT_SLER_SRR_RJT_ERR,
+ SLI4_FC_LOCAL_REJECT_RSVD6,
+ SLI4_FC_LOCAL_REJECT_SLER_RRQ_RJT_ERR,
+ SLI4_FC_LOCAL_REJECT_SLER_RRQ_RETRY_ERR,
+ SLI4_FC_LOCAL_REJECT_SLER_ABTS_ERR,
+};
+
+enum sli4_async_rcqe_flags {
+ SLI4_RACQE_RQ_EL_INDX = 0xfff,
+ SLI4_RACQE_FCFI = 0x3f,
+ SLI4_RACQE_HDPL = 0x3f,
+ SLI4_RACQE_RQ_ID = 0xffc0,
+};
+
+struct sli4_fc_async_rcqe {
+ u8 rsvd0;
+ u8 status;
+ __le16 rq_elmt_indx_word;
+ __le32 rsvd4;
+ __le16 fcfi_rq_id_word;
+ __le16 data_placement_length;
+ u8 sof_byte;
+ u8 eof_byte;
+ u8 code;
+ u8 hdpl_byte;
+};
+
+struct sli4_fc_async_rcqe_v1 {
+ u8 rsvd0;
+ u8 status;
+ __le16 rq_elmt_indx_word;
+ u8 fcfi_byte;
+ u8 rsvd5;
+ __le16 rsvd6;
+ __le16 rq_id;
+ __le16 data_placement_length;
+ u8 sof_byte;
+ u8 eof_byte;
+ u8 code;
+ u8 hdpl_byte;
+};
+
+enum sli4_fc_async_rq_status {
+ SLI4_FC_ASYNC_RQ_SUCCESS = 0x10,
+ SLI4_FC_ASYNC_RQ_BUF_LEN_EXCEEDED,
+ SLI4_FC_ASYNC_RQ_INSUFF_BUF_NEEDED,
+ SLI4_FC_ASYNC_RQ_INSUFF_BUF_FRM_DISC,
+ SLI4_FC_ASYNC_RQ_DMA_FAILURE,
+};
+
+#define SLI4_RCQE_RQ_EL_INDX 0xfff
+
+struct sli4_fc_coalescing_rcqe {
+ u8 rsvd0;
+ u8 status;
+ __le16 rq_elmt_indx_word;
+ __le32 rsvd4;
+ __le16 rq_id;
+ __le16 seq_placement_length;
+ __le16 rsvd14;
+ u8 code;
+ u8 vld_byte;
+};
+
+#define SLI4_FC_COALESCE_RQ_SUCCESS 0x10
+#define SLI4_FC_COALESCE_RQ_INSUFF_XRI_NEEDED 0x18
+
+enum sli4_optimized_write_cmd_cqe_flags {
+ SLI4_OCQE_RQ_EL_INDX = 0x7f, /* DW0 bits 16:30 */
+ SLI4_OCQE_FCFI = 0x3f, /* DW1 bits 0:6 */
+ SLI4_OCQE_OOX = 1 << 6, /* DW1 bit 15 */
+ SLI4_OCQE_AGXR = 1 << 7, /* DW1 bit 16 */
+ SLI4_OCQE_HDPL = 0x3f, /* DW3 bits 24:29*/
+};
+
+struct sli4_fc_optimized_write_cmd_cqe {
+ u8 rsvd0;
+ u8 status;
+ __le16 w1;
+ u8 flags0;
+ u8 flags1;
+ __le16 xri;
+ __le16 rq_id;
+ __le16 data_placement_length;
+ __le16 rpi;
+ u8 code;
+ u8 hdpl_vld;
+};
+
+#define SLI4_OCQE_XB 0x10
+
+struct sli4_fc_optimized_write_data_cqe {
+ u8 hw_status;
+ u8 status;
+ __le16 xri;
+ __le32 total_data_placed;
+ __le32 extended_status;
+ __le16 rsvd12;
+ u8 code;
+ u8 flags;
+};
+
+struct sli4_fc_xri_aborted_cqe {
+ u8 rsvd0;
+ u8 status;
+ __le16 rsvd2;
+ __le32 extended_status;
+ __le16 xri;
+ __le16 remote_xid;
+ __le16 rsvd12;
+ u8 code;
+ u8 flags;
+};
+
+enum sli4_generic_ctx {
+ SLI4_GENERIC_CONTEXT_RPI,
+ SLI4_GENERIC_CONTEXT_VPI,
+ SLI4_GENERIC_CONTEXT_VFI,
+ SLI4_GENERIC_CONTEXT_FCFI,
+};
+
+#define SLI4_GENERIC_CLASS_CLASS_2 0x1
+#define SLI4_GENERIC_CLASS_CLASS_3 0x2
+
+#define SLI4_ELS_REQUEST64_DIR_WRITE 0x0
+#define SLI4_ELS_REQUEST64_DIR_READ 0x1
+
+enum sli4_els_request {
+ SLI4_ELS_REQUEST64_OTHER,
+ SLI4_ELS_REQUEST64_LOGO,
+ SLI4_ELS_REQUEST64_FDISC,
+ SLI4_ELS_REQUEST64_FLOGIN,
+ SLI4_ELS_REQUEST64_PLOGI,
+};
+
+enum sli4_els_cmd_type {
+ SLI4_ELS_REQUEST64_CMD_GEN = 0x08,
+ SLI4_ELS_REQUEST64_CMD_NON_FABRIC = 0x0c,
+ SLI4_ELS_REQUEST64_CMD_FABRIC = 0x0d,
+};
+
+#define SLI_PAGE_SIZE SZ_4K
+
+#define SLI4_BMBX_TIMEOUT_MSEC 30000
+#define SLI4_FW_READY_TIMEOUT_MSEC 30000
+
+#define SLI4_BMBX_DELAY_US 1000 /* 1 ms */
+#define SLI4_INIT_PORT_DELAY_US 10000 /* 10 ms */
+
+static inline u32
+sli_page_count(size_t bytes, u32 page_size)
+{
+ if (!page_size)
+ return 0;
+
+ return (bytes + (page_size - 1)) >> __ffs(page_size);
+}
+
+/*************************************************************************
+ * SLI-4 mailbox command formats and definitions
+ */
+
+struct sli4_mbox_command_header {
+ u8 resvd0;
+ u8 command;
+ __le16 status; /* Port writes to indicate success/fail */
+};
+
+enum sli4_mbx_cmd_value {
+ SLI4_MBX_CMD_CONFIG_LINK = 0x07,
+ SLI4_MBX_CMD_DUMP = 0x17,
+ SLI4_MBX_CMD_DOWN_LINK = 0x06,
+ SLI4_MBX_CMD_INIT_LINK = 0x05,
+ SLI4_MBX_CMD_INIT_VFI = 0xa3,
+ SLI4_MBX_CMD_INIT_VPI = 0xa4,
+ SLI4_MBX_CMD_POST_XRI = 0xa7,
+ SLI4_MBX_CMD_RELEASE_XRI = 0xac,
+ SLI4_MBX_CMD_READ_CONFIG = 0x0b,
+ SLI4_MBX_CMD_READ_STATUS = 0x0e,
+ SLI4_MBX_CMD_READ_NVPARMS = 0x02,
+ SLI4_MBX_CMD_READ_REV = 0x11,
+ SLI4_MBX_CMD_READ_LNK_STAT = 0x12,
+ SLI4_MBX_CMD_READ_SPARM64 = 0x8d,
+ SLI4_MBX_CMD_READ_TOPOLOGY = 0x95,
+ SLI4_MBX_CMD_REG_FCFI = 0xa0,
+ SLI4_MBX_CMD_REG_FCFI_MRQ = 0xaf,
+ SLI4_MBX_CMD_REG_RPI = 0x93,
+ SLI4_MBX_CMD_REG_RX_RQ = 0xa6,
+ SLI4_MBX_CMD_REG_VFI = 0x9f,
+ SLI4_MBX_CMD_REG_VPI = 0x96,
+ SLI4_MBX_CMD_RQST_FEATURES = 0x9d,
+ SLI4_MBX_CMD_SLI_CONFIG = 0x9b,
+ SLI4_MBX_CMD_UNREG_FCFI = 0xa2,
+ SLI4_MBX_CMD_UNREG_RPI = 0x14,
+ SLI4_MBX_CMD_UNREG_VFI = 0xa1,
+ SLI4_MBX_CMD_UNREG_VPI = 0x97,
+ SLI4_MBX_CMD_WRITE_NVPARMS = 0x03,
+ SLI4_MBX_CMD_CFG_AUTO_XFER_RDY = 0xad,
+};
+
+enum sli4_mbx_status {
+ SLI4_MBX_STATUS_SUCCESS = 0x0000,
+ SLI4_MBX_STATUS_FAILURE = 0x0001,
+ SLI4_MBX_STATUS_RPI_NOT_REG = 0x1400,
+};
+
+/* CONFIG_LINK - configure link-oriented parameters,
+ * such as default N_Port_ID address and various timers
+ */
+enum sli4_cmd_config_link_flags {
+ SLI4_CFG_LINK_BBSCN = 0xf00,
+ SLI4_CFG_LINK_CSCN = 0x1000,
+};
+
+struct sli4_cmd_config_link {
+ struct sli4_mbox_command_header hdr;
+ u8 maxbbc;
+ u8 rsvd5;
+ u8 rsvd6;
+ u8 rsvd7;
+ u8 alpa;
+ __le16 n_port_id;
+ u8 rsvd11;
+ __le32 rsvd12;
+ __le32 e_d_tov;
+ __le32 lp_tov;
+ __le32 r_a_tov;
+ __le32 r_t_tov;
+ __le32 al_tov;
+ __le32 rsvd36;
+ __le32 bbscn_dword;
+};
+
+#define SLI4_DUMP4_TYPE 0xf
+
+#define SLI4_WKI_TAG_SAT_TEM 0x1040
+
+struct sli4_cmd_dump4 {
+ struct sli4_mbox_command_header hdr;
+ __le32 type_dword;
+ __le16 wki_selection;
+ __le16 rsvd10;
+ __le32 rsvd12;
+ __le32 returned_byte_cnt;
+ __le32 resp_data[59];
+};
+
+/* INIT_LINK - initialize the link for a FC port */
+enum sli4_init_link_flags {
+ SLI4_INIT_LINK_F_LOOPBACK = 1 << 0,
+
+ SLI4_INIT_LINK_F_P2P_ONLY = 1 << 1,
+ SLI4_INIT_LINK_F_FCAL_ONLY = 2 << 1,
+ SLI4_INIT_LINK_F_FCAL_FAIL_OVER = 0 << 1,
+ SLI4_INIT_LINK_F_P2P_FAIL_OVER = 1 << 1,
+
+ SLI4_INIT_LINK_F_UNFAIR = 1 << 6,
+ SLI4_INIT_LINK_F_NO_LIRP = 1 << 7,
+ SLI4_INIT_LINK_F_LOOP_VALID_CHK = 1 << 8,
+ SLI4_INIT_LINK_F_NO_LISA = 1 << 9,
+ SLI4_INIT_LINK_F_FAIL_OVER = 1 << 10,
+ SLI4_INIT_LINK_F_FIXED_SPEED = 1 << 11,
+ SLI4_INIT_LINK_F_PICK_HI_ALPA = 1 << 15,
+
+};
+
+enum sli4_fc_link_speed {
+ SLI4_LINK_SPEED_1G = 1,
+ SLI4_LINK_SPEED_2G,
+ SLI4_LINK_SPEED_AUTO_1_2,
+ SLI4_LINK_SPEED_4G,
+ SLI4_LINK_SPEED_AUTO_4_1,
+ SLI4_LINK_SPEED_AUTO_4_2,
+ SLI4_LINK_SPEED_AUTO_4_2_1,
+ SLI4_LINK_SPEED_8G,
+ SLI4_LINK_SPEED_AUTO_8_1,
+ SLI4_LINK_SPEED_AUTO_8_2,
+ SLI4_LINK_SPEED_AUTO_8_2_1,
+ SLI4_LINK_SPEED_AUTO_8_4,
+ SLI4_LINK_SPEED_AUTO_8_4_1,
+ SLI4_LINK_SPEED_AUTO_8_4_2,
+ SLI4_LINK_SPEED_10G,
+ SLI4_LINK_SPEED_16G,
+ SLI4_LINK_SPEED_AUTO_16_8_4,
+ SLI4_LINK_SPEED_AUTO_16_8,
+ SLI4_LINK_SPEED_32G,
+ SLI4_LINK_SPEED_AUTO_32_16_8,
+ SLI4_LINK_SPEED_AUTO_32_16,
+ SLI4_LINK_SPEED_64G,
+ SLI4_LINK_SPEED_AUTO_64_32_16,
+ SLI4_LINK_SPEED_AUTO_64_32,
+ SLI4_LINK_SPEED_128G,
+ SLI4_LINK_SPEED_AUTO_128_64_32,
+ SLI4_LINK_SPEED_AUTO_128_64,
+};
+
+struct sli4_cmd_init_link {
+ struct sli4_mbox_command_header hdr;
+ __le32 sel_reset_al_pa_dword;
+ __le32 flags0;
+ __le32 link_speed_sel_code;
+};
+
+/* INIT_VFI - initialize the VFI resource */
+enum sli4_init_vfi_flags {
+ SLI4_INIT_VFI_FLAG_VP = 0x1000,
+ SLI4_INIT_VFI_FLAG_VF = 0x2000,
+ SLI4_INIT_VFI_FLAG_VT = 0x4000,
+ SLI4_INIT_VFI_FLAG_VR = 0x8000,
+
+ SLI4_INIT_VFI_VFID = 0x1fff,
+ SLI4_INIT_VFI_PRI = 0xe000,
+
+ SLI4_INIT_VFI_HOP_COUNT = 0xff000000,
+};
+
+struct sli4_cmd_init_vfi {
+ struct sli4_mbox_command_header hdr;
+ __le16 vfi;
+ __le16 flags0_word;
+ __le16 fcfi;
+ __le16 vpi;
+ __le32 vf_id_pri_dword;
+ __le32 hop_cnt_dword;
+};
+
+/* INIT_VPI - initialize the VPI resource */
+struct sli4_cmd_init_vpi {
+ struct sli4_mbox_command_header hdr;
+ __le16 vpi;
+ __le16 vfi;
+};
+
+/* POST_XRI - post XRI resources to the SLI Port */
+enum sli4_post_xri_flags {
+ SLI4_POST_XRI_COUNT = 0xfff,
+ SLI4_POST_XRI_FLAG_ENX = 0x1000,
+ SLI4_POST_XRI_FLAG_DL = 0x2000,
+ SLI4_POST_XRI_FLAG_DI = 0x4000,
+ SLI4_POST_XRI_FLAG_VAL = 0x8000,
+};
+
+struct sli4_cmd_post_xri {
+ struct sli4_mbox_command_header hdr;
+ __le16 xri_base;
+ __le16 xri_count_flags;
+};
+
+/* RELEASE_XRI - Release XRI resources from the SLI Port */
+enum sli4_release_xri_flags {
+ SLI4_RELEASE_XRI_REL_XRI_CNT = 0x1f,
+ SLI4_RELEASE_XRI_COUNT = 0x1f,
+};
+
+struct sli4_cmd_release_xri {
+ struct sli4_mbox_command_header hdr;
+ __le16 rel_xri_count_word;
+ __le16 xri_count_word;
+
+ struct {
+ __le16 xri_tag0;
+ __le16 xri_tag1;
+ } xri_tbl[62];
+};
+
+/* READ_CONFIG - read SLI port configuration parameters */
+struct sli4_cmd_read_config {
+ struct sli4_mbox_command_header hdr;
+};
+
+enum sli4_read_cfg_resp_flags {
+ SLI4_READ_CFG_RESP_RESOURCE_EXT = 0x80000000, /* DW1 */
+ SLI4_READ_CFG_RESP_TOPOLOGY = 0xff000000, /* DW2 */
+};
+
+enum sli4_read_cfg_topo {
+ SLI4_READ_CFG_TOPO_FC = 0x1, /* FC topology unknown */
+ SLI4_READ_CFG_TOPO_NON_FC_AL = 0x2, /* FC point-to-point or fabric */
+ SLI4_READ_CFG_TOPO_FC_AL = 0x3, /* FC-AL topology */
+};
+
+/* Link Module Type */
+enum sli4_read_cfg_lmt {
+ SLI4_LINK_MODULE_TYPE_1GB = 0x0004,
+ SLI4_LINK_MODULE_TYPE_2GB = 0x0008,
+ SLI4_LINK_MODULE_TYPE_4GB = 0x0040,
+ SLI4_LINK_MODULE_TYPE_8GB = 0x0080,
+ SLI4_LINK_MODULE_TYPE_16GB = 0x0200,
+ SLI4_LINK_MODULE_TYPE_32GB = 0x0400,
+ SLI4_LINK_MODULE_TYPE_64GB = 0x0800,
+ SLI4_LINK_MODULE_TYPE_128GB = 0x1000,
+};
+
+struct sli4_rsp_read_config {
+ struct sli4_mbox_command_header hdr;
+ __le32 ext_dword;
+ __le32 topology_dword;
+ __le32 resvd8;
+ __le16 e_d_tov;
+ __le16 resvd14;
+ __le32 resvd16;
+ __le16 r_a_tov;
+ __le16 resvd22;
+ __le32 resvd24;
+ __le32 resvd28;
+ __le16 lmt;
+ __le16 resvd34;
+ __le32 resvd36;
+ __le32 resvd40;
+ __le16 xri_base;
+ __le16 xri_count;
+ __le16 rpi_base;
+ __le16 rpi_count;
+ __le16 vpi_base;
+ __le16 vpi_count;
+ __le16 vfi_base;
+ __le16 vfi_count;
+ __le16 resvd60;
+ __le16 fcfi_count;
+ __le16 rq_count;
+ __le16 eq_count;
+ __le16 wq_count;
+ __le16 cq_count;
+ __le32 pad[45];
+};
+
+/* READ_NVPARMS - read SLI port configuration parameters */
+enum sli4_read_nvparms_flags {
+ SLI4_READ_NVPARAMS_HARD_ALPA = 0xff,
+ SLI4_READ_NVPARAMS_PREFERRED_D_ID = 0xffffff00,
+};
+
+struct sli4_cmd_read_nvparms {
+ struct sli4_mbox_command_header hdr;
+ __le32 resvd0;
+ __le32 resvd4;
+ __le32 resvd8;
+ __le32 resvd12;
+ u8 wwpn[8];
+ u8 wwnn[8];
+ __le32 hard_alpa_d_id;
+};
+
+/* WRITE_NVPARMS - write SLI port configuration parameters */
+struct sli4_cmd_write_nvparms {
+ struct sli4_mbox_command_header hdr;
+ __le32 resvd0;
+ __le32 resvd4;
+ __le32 resvd8;
+ __le32 resvd12;
+ u8 wwpn[8];
+ u8 wwnn[8];
+ __le32 hard_alpa_d_id;
+};
+
+/* READ_REV - read the Port revision levels */
+enum {
+ SLI4_READ_REV_FLAG_SLI_LEVEL = 0xf,
+ SLI4_READ_REV_FLAG_FCOEM = 0x10,
+ SLI4_READ_REV_FLAG_CEEV = 0x60,
+ SLI4_READ_REV_FLAG_VPD = 0x2000,
+
+ SLI4_READ_REV_AVAILABLE_LENGTH = 0xffffff,
+};
+
+struct sli4_cmd_read_rev {
+ struct sli4_mbox_command_header hdr;
+ __le16 resvd0;
+ __le16 flags0_word;
+ __le32 first_hw_rev;
+ __le32 second_hw_rev;
+ __le32 resvd12;
+ __le32 third_hw_rev;
+ u8 fc_ph_low;
+ u8 fc_ph_high;
+ u8 feature_level_low;
+ u8 feature_level_high;
+ __le32 resvd24;
+ __le32 first_fw_id;
+ u8 first_fw_name[16];
+ __le32 second_fw_id;
+ u8 second_fw_name[16];
+ __le32 rsvd18[30];
+ __le32 available_length_dword;
+ struct sli4_dmaaddr hostbuf;
+ __le32 returned_vpd_length;
+ __le32 actual_vpd_length;
+};
+
+/* READ_SPARM64 - read the Port service parameters */
+#define SLI4_READ_SPARM64_WWPN_OFFSET (4 * sizeof(u32))
+#define SLI4_READ_SPARM64_WWNN_OFFSET (6 * sizeof(u32))
+
+struct sli4_cmd_read_sparm64 {
+ struct sli4_mbox_command_header hdr;
+ __le32 resvd0;
+ __le32 resvd4;
+ struct sli4_bde bde_64;
+ __le16 vpi;
+ __le16 resvd22;
+ __le16 port_name_start;
+ __le16 port_name_len;
+ __le16 node_name_start;
+ __le16 node_name_len;
+};
+
+/* READ_TOPOLOGY - read the link event information */
+enum sli4_read_topo_e {
+ SLI4_READTOPO_ATTEN_TYPE = 0xff,
+ SLI4_READTOPO_FLAG_IL = 0x100,
+ SLI4_READTOPO_FLAG_PB_RECVD = 0x200,
+
+ SLI4_READTOPO_LINKSTATE_RECV = 0x3,
+ SLI4_READTOPO_LINKSTATE_TRANS = 0xc,
+ SLI4_READTOPO_LINKSTATE_MACHINE = 0xf0,
+ SLI4_READTOPO_LINKSTATE_SPEED = 0xff00,
+ SLI4_READTOPO_LINKSTATE_TF = 0x40000000,
+ SLI4_READTOPO_LINKSTATE_LU = 0x80000000,
+
+ SLI4_READTOPO_SCN_BBSCN = 0xf,
+ SLI4_READTOPO_SCN_CBBSCN = 0xf0,
+
+ SLI4_READTOPO_R_T_TOV = 0x1ff,
+ SLI4_READTOPO_AL_TOV = 0xf000,
+
+ SLI4_READTOPO_PB_FLAG = 0x80,
+
+ SLI4_READTOPO_INIT_N_PORTID = 0xffffff,
+};
+
+#define SLI4_MIN_LOOP_MAP_BYTES 128
+
+struct sli4_cmd_read_topology {
+ struct sli4_mbox_command_header hdr;
+ __le32 event_tag;
+ __le32 dw2_attentype;
+ u8 topology;
+ u8 lip_type;
+ u8 lip_al_ps;
+ u8 al_pa_granted;
+ struct sli4_bde bde_loop_map;
+ __le32 linkdown_state;
+ __le32 currlink_state;
+ u8 max_bbc;
+ u8 init_bbc;
+ u8 scn_flags;
+ u8 rsvd39;
+ __le16 dw10w0_al_rt_tov;
+ __le16 lp_tov;
+ u8 acquired_al_pa;
+ u8 pb_flags;
+ __le16 specified_al_pa;
+ __le32 dw12_init_n_port_id;
+};
+
+enum sli4_read_topo_link {
+ SLI4_READ_TOPOLOGY_LINK_UP = 0x1,
+ SLI4_READ_TOPOLOGY_LINK_DOWN,
+ SLI4_READ_TOPOLOGY_LINK_NO_ALPA,
+};
+
+enum sli4_read_topo {
+ SLI4_READ_TOPO_UNKNOWN = 0x0,
+ SLI4_READ_TOPO_NON_FC_AL,
+ SLI4_READ_TOPO_FC_AL,
+};
+
+enum sli4_read_topo_speed {
+ SLI4_READ_TOPOLOGY_SPEED_NONE = 0x00,
+ SLI4_READ_TOPOLOGY_SPEED_1G = 0x04,
+ SLI4_READ_TOPOLOGY_SPEED_2G = 0x08,
+ SLI4_READ_TOPOLOGY_SPEED_4G = 0x10,
+ SLI4_READ_TOPOLOGY_SPEED_8G = 0x20,
+ SLI4_READ_TOPOLOGY_SPEED_10G = 0x40,
+ SLI4_READ_TOPOLOGY_SPEED_16G = 0x80,
+ SLI4_READ_TOPOLOGY_SPEED_32G = 0x90,
+ SLI4_READ_TOPOLOGY_SPEED_64G = 0xa0,
+ SLI4_READ_TOPOLOGY_SPEED_128G = 0xb0,
+};
+
+/* REG_FCFI - activate a FC Forwarder */
+struct sli4_cmd_reg_fcfi_rq_cfg {
+ u8 r_ctl_mask;
+ u8 r_ctl_match;
+ u8 type_mask;
+ u8 type_match;
+};
+
+enum sli4_regfcfi_tag {
+ SLI4_REGFCFI_VLAN_TAG = 0xfff,
+ SLI4_REGFCFI_VLANTAG_VALID = 0x1000,
+};
+
+#define SLI4_CMD_REG_FCFI_NUM_RQ_CFG 4
+struct sli4_cmd_reg_fcfi {
+ struct sli4_mbox_command_header hdr;
+ __le16 fcf_index;
+ __le16 fcfi;
+ __le16 rqid1;
+ __le16 rqid0;
+ __le16 rqid3;
+ __le16 rqid2;
+ struct sli4_cmd_reg_fcfi_rq_cfg
+ rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG];
+ __le32 dw8_vlan;
+};
+
+#define SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG 4
+#define SLI4_CMD_REG_FCFI_MRQ_MAX_NUM_RQ 32
+#define SLI4_CMD_REG_FCFI_SET_FCFI_MODE 0
+#define SLI4_CMD_REG_FCFI_SET_MRQ_MODE 1
+
+enum sli4_reg_fcfi_mrq {
+ SLI4_REGFCFI_MRQ_VLAN_TAG = 0xfff,
+ SLI4_REGFCFI_MRQ_VLANTAG_VALID = 0x1000,
+ SLI4_REGFCFI_MRQ_MODE = 0x2000,
+
+ SLI4_REGFCFI_MRQ_MASK_NUM_PAIRS = 0xff,
+ SLI4_REGFCFI_MRQ_FILTER_BITMASK = 0xf00,
+ SLI4_REGFCFI_MRQ_RQ_SEL_POLICY = 0xf000,
+};
+
+struct sli4_cmd_reg_fcfi_mrq {
+ struct sli4_mbox_command_header hdr;
+ __le16 fcf_index;
+ __le16 fcfi;
+ __le16 rqid1;
+ __le16 rqid0;
+ __le16 rqid3;
+ __le16 rqid2;
+ struct sli4_cmd_reg_fcfi_rq_cfg
+ rq_cfg[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG];
+ __le32 dw8_vlan;
+ __le32 dw9_mrqflags;
+};
+
+struct sli4_cmd_rq_cfg {
+ __le16 rq_id;
+ u8 r_ctl_mask;
+ u8 r_ctl_match;
+ u8 type_mask;
+ u8 type_match;
+};
+
+/* REG_RPI - register a Remote Port Indicator */
+enum sli4_reg_rpi {
+ SLI4_REGRPI_REMOTE_N_PORTID = 0xffffff, /* DW2 */
+ SLI4_REGRPI_UPD = 0x1000000,
+ SLI4_REGRPI_ETOW = 0x8000000,
+ SLI4_REGRPI_TERP = 0x20000000,
+ SLI4_REGRPI_CI = 0x80000000,
+};
+
+struct sli4_cmd_reg_rpi {
+ struct sli4_mbox_command_header hdr;
+ __le16 rpi;
+ __le16 rsvd2;
+ __le32 dw2_rportid_flags;
+ struct sli4_bde bde_64;
+ __le16 vpi;
+ __le16 rsvd26;
+};
+
+#define SLI4_REG_RPI_BUF_LEN 0x70
+
+/* REG_VFI - register a Virtual Fabric Indicator */
+enum sli_reg_vfi {
+ SLI4_REGVFI_VP = 0x1000, /* DW1 */
+ SLI4_REGVFI_UPD = 0x2000,
+
+ SLI4_REGVFI_LOCAL_N_PORTID = 0xffffff, /* DW10 */
+};
+
+struct sli4_cmd_reg_vfi {
+ struct sli4_mbox_command_header hdr;
+ __le16 vfi;
+ __le16 dw0w1_flags;
+ __le16 fcfi;
+ __le16 vpi;
+ u8 wwpn[8];
+ struct sli4_bde sparm;
+ __le32 e_d_tov;
+ __le32 r_a_tov;
+ __le32 dw10_lportid_flags;
+};
+
+/* REG_VPI - register a Virtual Port Indicator */
+enum sli4_reg_vpi {
+ SLI4_REGVPI_LOCAL_N_PORTID = 0xffffff,
+ SLI4_REGVPI_UPD = 0x1000000,
+};
+
+struct sli4_cmd_reg_vpi {
+ struct sli4_mbox_command_header hdr;
+ __le32 rsvd0;
+ __le32 dw2_lportid_flags;
+ u8 wwpn[8];
+ __le32 rsvd12;
+ __le16 vpi;
+ __le16 vfi;
+};
+
+/* REQUEST_FEATURES - request / query SLI features */
+enum sli4_req_features_flags {
+ SLI4_REQFEAT_QRY = 0x1, /* Dw1 */
+
+ SLI4_REQFEAT_IAAB = 1 << 0, /* DW2 & DW3 */
+ SLI4_REQFEAT_NPIV = 1 << 1,
+ SLI4_REQFEAT_DIF = 1 << 2,
+ SLI4_REQFEAT_VF = 1 << 3,
+ SLI4_REQFEAT_FCPI = 1 << 4,
+ SLI4_REQFEAT_FCPT = 1 << 5,
+ SLI4_REQFEAT_FCPC = 1 << 6,
+ SLI4_REQFEAT_RSVD = 1 << 7,
+ SLI4_REQFEAT_RQD = 1 << 8,
+ SLI4_REQFEAT_IAAR = 1 << 9,
+ SLI4_REQFEAT_HLM = 1 << 10,
+ SLI4_REQFEAT_PERFH = 1 << 11,
+ SLI4_REQFEAT_RXSEQ = 1 << 12,
+ SLI4_REQFEAT_RXRI = 1 << 13,
+ SLI4_REQFEAT_DCL2 = 1 << 14,
+ SLI4_REQFEAT_RSCO = 1 << 15,
+ SLI4_REQFEAT_MRQP = 1 << 16,
+};
+
+struct sli4_cmd_request_features {
+ struct sli4_mbox_command_header hdr;
+ __le32 dw1_qry;
+ __le32 cmd;
+ __le32 resp;
+};
+
+/*
+ * SLI_CONFIG - submit a configuration command to Port
+ *
+ * Command is either embedded as part of the payload (embed) or located
+ * in a separate memory buffer (mem)
+ */
+enum sli4_sli_config {
+ SLI4_SLICONF_EMB = 0x1, /* DW1 */
+ SLI4_SLICONF_PMDCMD_SHIFT = 3,
+ SLI4_SLICONF_PMDCMD_MASK = 0xf8,
+ SLI4_SLICONF_PMDCMD_VAL_1 = 8,
+ SLI4_SLICONF_PMDCNT = 0xf8,
+
+ SLI4_SLICONF_PMD_LEN = 0x00ffffff,
+};
+
+struct sli4_cmd_sli_config {
+ struct sli4_mbox_command_header hdr;
+ __le32 dw1_flags;
+ __le32 payload_len;
+ __le32 rsvd12[3];
+ union {
+ u8 embed[58 * sizeof(u32)];
+ struct sli4_bufptr mem;
+ } payload;
+};
+
+/* READ_STATUS - read tx/rx status of a particular port */
+#define SLI4_READSTATUS_CLEAR_COUNTERS 0x1
+
+struct sli4_cmd_read_status {
+ struct sli4_mbox_command_header hdr;
+ __le32 dw1_flags;
+ __le32 rsvd4;
+ __le32 trans_kbyte_cnt;
+ __le32 recv_kbyte_cnt;
+ __le32 trans_frame_cnt;
+ __le32 recv_frame_cnt;
+ __le32 trans_seq_cnt;
+ __le32 recv_seq_cnt;
+ __le32 tot_exchanges_orig;
+ __le32 tot_exchanges_resp;
+ __le32 recv_p_bsy_cnt;
+ __le32 recv_f_bsy_cnt;
+ __le32 no_rq_buf_dropped_frames_cnt;
+ __le32 empty_rq_timeout_cnt;
+ __le32 no_xri_dropped_frames_cnt;
+ __le32 empty_xri_pool_cnt;
+};
+
+/* READ_LNK_STAT - read link status of a particular port */
+enum sli4_read_link_stats_flags {
+ SLI4_READ_LNKSTAT_REC = 1u << 0,
+ SLI4_READ_LNKSTAT_GEC = 1u << 1,
+ SLI4_READ_LNKSTAT_W02OF = 1u << 2,
+ SLI4_READ_LNKSTAT_W03OF = 1u << 3,
+ SLI4_READ_LNKSTAT_W04OF = 1u << 4,
+ SLI4_READ_LNKSTAT_W05OF = 1u << 5,
+ SLI4_READ_LNKSTAT_W06OF = 1u << 6,
+ SLI4_READ_LNKSTAT_W07OF = 1u << 7,
+ SLI4_READ_LNKSTAT_W08OF = 1u << 8,
+ SLI4_READ_LNKSTAT_W09OF = 1u << 9,
+ SLI4_READ_LNKSTAT_W10OF = 1u << 10,
+ SLI4_READ_LNKSTAT_W11OF = 1u << 11,
+ SLI4_READ_LNKSTAT_W12OF = 1u << 12,
+ SLI4_READ_LNKSTAT_W13OF = 1u << 13,
+ SLI4_READ_LNKSTAT_W14OF = 1u << 14,
+ SLI4_READ_LNKSTAT_W15OF = 1u << 15,
+ SLI4_READ_LNKSTAT_W16OF = 1u << 16,
+ SLI4_READ_LNKSTAT_W17OF = 1u << 17,
+ SLI4_READ_LNKSTAT_W18OF = 1u << 18,
+ SLI4_READ_LNKSTAT_W19OF = 1u << 19,
+ SLI4_READ_LNKSTAT_W20OF = 1u << 20,
+ SLI4_READ_LNKSTAT_W21OF = 1u << 21,
+ SLI4_READ_LNKSTAT_CLRC = 1u << 30,
+ SLI4_READ_LNKSTAT_CLOF = 1u << 31,
+};
+
+struct sli4_cmd_read_link_stats {
+ struct sli4_mbox_command_header hdr;
+ __le32 dw1_flags;
+ __le32 linkfail_errcnt;
+ __le32 losssync_errcnt;
+ __le32 losssignal_errcnt;
+ __le32 primseq_errcnt;
+ __le32 inval_txword_errcnt;
+ __le32 crc_errcnt;
+ __le32 primseq_eventtimeout_cnt;
+ __le32 elastic_bufoverrun_errcnt;
+ __le32 arbit_fc_al_timeout_cnt;
+ __le32 adv_rx_buftor_to_buf_credit;
+ __le32 curr_rx_buf_to_buf_credit;
+ __le32 adv_tx_buf_to_buf_credit;
+ __le32 curr_tx_buf_to_buf_credit;
+ __le32 rx_eofa_cnt;
+ __le32 rx_eofdti_cnt;
+ __le32 rx_eofni_cnt;
+ __le32 rx_soff_cnt;
+ __le32 rx_dropped_no_aer_cnt;
+ __le32 rx_dropped_no_avail_rpi_rescnt;
+ __le32 rx_dropped_no_avail_xri_rescnt;
+};
+
+/* Format a WQE with WQ_ID Association performance hint */
+static inline void
+sli_set_wq_id_association(void *entry, u16 q_id)
+{
+ u32 *wqe = entry;
+
+ /*
+ * Set Word 10, bit 0 to zero
+ * Set Word 10, bits 15:1 to the WQ ID
+ */
+ wqe[10] &= ~0xffff;
+ wqe[10] |= q_id << 1;
+}
+
+/* UNREG_FCFI - unregister a FCFI */
+struct sli4_cmd_unreg_fcfi {
+ struct sli4_mbox_command_header hdr;
+ __le32 rsvd0;
+ __le16 fcfi;
+ __le16 rsvd6;
+};
+
+/* UNREG_RPI - unregister one or more RPI */
+enum sli4_unreg_rpi {
+ SLI4_UNREG_RPI_DP = 0x2000,
+ SLI4_UNREG_RPI_II_SHIFT = 14,
+ SLI4_UNREG_RPI_II_MASK = 0xc000,
+ SLI4_UNREG_RPI_II_RPI = 0x0000,
+ SLI4_UNREG_RPI_II_VPI = 0x4000,
+ SLI4_UNREG_RPI_II_VFI = 0x8000,
+ SLI4_UNREG_RPI_II_FCFI = 0xc000,
+
+ SLI4_UNREG_RPI_DEST_N_PORTID_MASK = 0x00ffffff,
+};
+
+struct sli4_cmd_unreg_rpi {
+ struct sli4_mbox_command_header hdr;
+ __le16 index;
+ __le16 dw1w1_flags;
+ __le32 dw2_dest_n_portid;
+};
+
+/* UNREG_VFI - unregister one or more VFI */
+enum sli4_unreg_vfi {
+ SLI4_UNREG_VFI_II_SHIFT = 14,
+ SLI4_UNREG_VFI_II_MASK = 0xc000,
+ SLI4_UNREG_VFI_II_VFI = 0x0000,
+ SLI4_UNREG_VFI_II_FCFI = 0xc000,
+};
+
+struct sli4_cmd_unreg_vfi {
+ struct sli4_mbox_command_header hdr;
+ __le32 rsvd0;
+ __le16 index;
+ __le16 dw2_flags;
+};
+
+enum sli4_unreg_type {
+ SLI4_UNREG_TYPE_PORT,
+ SLI4_UNREG_TYPE_DOMAIN,
+ SLI4_UNREG_TYPE_FCF,
+ SLI4_UNREG_TYPE_ALL
+};
+
+/* UNREG_VPI - unregister one or more VPI */
+enum sli4_unreg_vpi {
+ SLI4_UNREG_VPI_II_SHIFT = 14,
+ SLI4_UNREG_VPI_II_MASK = 0xc000,
+ SLI4_UNREG_VPI_II_VPI = 0x0000,
+ SLI4_UNREG_VPI_II_VFI = 0x8000,
+ SLI4_UNREG_VPI_II_FCFI = 0xc000,
+};
+
+struct sli4_cmd_unreg_vpi {
+ struct sli4_mbox_command_header hdr;
+ __le32 rsvd0;
+ __le16 index;
+ __le16 dw2w0_flags;
+};
+
+/* AUTO_XFER_RDY - Configure the auto-generate XFER-RDY feature */
+struct sli4_cmd_config_auto_xfer_rdy {
+ struct sli4_mbox_command_header hdr;
+ __le32 rsvd0;
+ __le32 max_burst_len;
+};
+
+#define SLI4_CONFIG_AUTO_XFERRDY_BLKSIZE 0xffff
+
+struct sli4_cmd_config_auto_xfer_rdy_hp {
+ struct sli4_mbox_command_header hdr;
+ __le32 rsvd0;
+ __le32 max_burst_len;
+ __le32 dw3_esoc_flags;
+ __le16 block_size;
+ __le16 rsvd14;
+};
+
+/*************************************************************************
+ * SLI-4 common configuration command formats and definitions
+ */
+
+/*
+ * Subsystem values.
+ */
+enum sli4_subsystem {
+ SLI4_SUBSYSTEM_COMMON = 0x01,
+ SLI4_SUBSYSTEM_LOWLEVEL = 0x0b,
+ SLI4_SUBSYSTEM_FC = 0x0c,
+ SLI4_SUBSYSTEM_DMTF = 0x11,
+};
+
+#define SLI4_OPC_LOWLEVEL_SET_WATCHDOG 0X36
+
+/*
+ * Common opcode (OPC) values.
+ */
+enum sli4_cmn_opcode {
+ SLI4_CMN_FUNCTION_RESET = 0x3d,
+ SLI4_CMN_CREATE_CQ = 0x0c,
+ SLI4_CMN_CREATE_CQ_SET = 0x1d,
+ SLI4_CMN_DESTROY_CQ = 0x36,
+ SLI4_CMN_MODIFY_EQ_DELAY = 0x29,
+ SLI4_CMN_CREATE_EQ = 0x0d,
+ SLI4_CMN_DESTROY_EQ = 0x37,
+ SLI4_CMN_CREATE_MQ_EXT = 0x5a,
+ SLI4_CMN_DESTROY_MQ = 0x35,
+ SLI4_CMN_GET_CNTL_ATTRIBUTES = 0x20,
+ SLI4_CMN_NOP = 0x21,
+ SLI4_CMN_GET_RSC_EXTENT_INFO = 0x9a,
+ SLI4_CMN_GET_SLI4_PARAMS = 0xb5,
+ SLI4_CMN_QUERY_FW_CONFIG = 0x3a,
+ SLI4_CMN_GET_PORT_NAME = 0x4d,
+
+ SLI4_CMN_WRITE_FLASHROM = 0x07,
+ /* TRANSCEIVER Data */
+ SLI4_CMN_READ_TRANS_DATA = 0x49,
+ SLI4_CMN_GET_CNTL_ADDL_ATTRS = 0x79,
+ SLI4_CMN_GET_FUNCTION_CFG = 0xa0,
+ SLI4_CMN_GET_PROFILE_CFG = 0xa4,
+ SLI4_CMN_SET_PROFILE_CFG = 0xa5,
+ SLI4_CMN_GET_PROFILE_LIST = 0xa6,
+ SLI4_CMN_GET_ACTIVE_PROFILE = 0xa7,
+ SLI4_CMN_SET_ACTIVE_PROFILE = 0xa8,
+ SLI4_CMN_READ_OBJECT = 0xab,
+ SLI4_CMN_WRITE_OBJECT = 0xac,
+ SLI4_CMN_DELETE_OBJECT = 0xae,
+ SLI4_CMN_READ_OBJECT_LIST = 0xad,
+ SLI4_CMN_SET_DUMP_LOCATION = 0xb8,
+ SLI4_CMN_SET_FEATURES = 0xbf,
+ SLI4_CMN_GET_RECFG_LINK_INFO = 0xc9,
+ SLI4_CMN_SET_RECNG_LINK_ID = 0xca,
+};
+
+/* DMTF opcode (OPC) values */
+#define DMTF_EXEC_CLP_CMD 0x01
+
+/*
+ * COMMON_FUNCTION_RESET
+ *
+ * Resets the Port, returning it to a power-on state. This configuration
+ * command does not have a payload and should set/expect the lengths to
+ * be zero.
+ */
+struct sli4_rqst_cmn_function_reset {
+ struct sli4_rqst_hdr hdr;
+};
+
+struct sli4_rsp_cmn_function_reset {
+ struct sli4_rsp_hdr hdr;
+};
+
+/*
+ * COMMON_GET_CNTL_ATTRIBUTES
+ *
+ * Query for information about the SLI Port
+ */
+enum sli4_cntrl_attr_flags {
+ SLI4_CNTL_ATTR_PORTNUM = 0x3f,
+ SLI4_CNTL_ATTR_PORTTYPE = 0xc0,
+};
+
+struct sli4_rsp_cmn_get_cntl_attributes {
+ struct sli4_rsp_hdr hdr;
+ u8 version_str[32];
+ u8 manufacturer_name[32];
+ __le32 supported_modes;
+ u8 eprom_version_lo;
+ u8 eprom_version_hi;
+ __le16 rsvd17;
+ __le32 mbx_ds_version;
+ __le32 ep_fw_ds_version;
+ u8 ncsi_version_str[12];
+ __le32 def_extended_timeout;
+ u8 model_number[32];
+ u8 description[64];
+ u8 serial_number[32];
+ u8 ip_version_str[32];
+ u8 fw_version_str[32];
+ u8 bios_version_str[32];
+ u8 redboot_version_str[32];
+ u8 driver_version_str[32];
+ u8 fw_on_flash_version_str[32];
+ __le32 functionalities_supported;
+ __le16 max_cdb_length;
+ u8 asic_revision;
+ u8 generational_guid0;
+ __le32 generational_guid1_12[3];
+ __le16 generational_guid13_14;
+ u8 generational_guid15;
+ u8 hba_port_count;
+ __le16 default_link_down_timeout;
+ u8 iscsi_version_min_max;
+ u8 multifunctional_device;
+ u8 cache_valid;
+ u8 hba_status;
+ u8 max_domains_supported;
+ u8 port_num_type_flags;
+ __le32 firmware_post_status;
+ __le32 hba_mtu;
+ u8 iscsi_features;
+ u8 rsvd121[3];
+ __le16 pci_vendor_id;
+ __le16 pci_device_id;
+ __le16 pci_sub_vendor_id;
+ __le16 pci_sub_system_id;
+ u8 pci_bus_number;
+ u8 pci_device_number;
+ u8 pci_function_number;
+ u8 interface_type;
+ __le64 unique_identifier;
+ u8 number_of_netfilters;
+ u8 rsvd122[3];
+};
+
+/*
+ * COMMON_GET_CNTL_ATTRIBUTES
+ *
+ * This command queries the controller information from the Flash ROM.
+ */
+struct sli4_rqst_cmn_get_cntl_addl_attributes {
+ struct sli4_rqst_hdr hdr;
+};
+
+struct sli4_rsp_cmn_get_cntl_addl_attributes {
+ struct sli4_rsp_hdr hdr;
+ __le16 ipl_file_number;
+ u8 ipl_file_version;
+ u8 rsvd4;
+ u8 on_die_temperature;
+ u8 rsvd5[3];
+ __le32 driver_advanced_features_supported;
+ __le32 rsvd7[4];
+ char universal_bios_version[32];
+ char x86_bios_version[32];
+ char efi_bios_version[32];
+ char fcode_version[32];
+ char uefi_bios_version[32];
+ char uefi_nic_version[32];
+ char uefi_fcode_version[32];
+ char uefi_iscsi_version[32];
+ char iscsi_x86_bios_version[32];
+ char pxe_x86_bios_version[32];
+ u8 default_wwpn[8];
+ u8 ext_phy_version[32];
+ u8 fc_universal_bios_version[32];
+ u8 fc_x86_bios_version[32];
+ u8 fc_efi_bios_version[32];
+ u8 fc_fcode_version[32];
+ u8 ext_phy_crc_label[8];
+ u8 ipl_file_name[16];
+ u8 rsvd139[72];
+};
+
+/*
+ * COMMON_NOP
+ *
+ * This command does not do anything; it only returns
+ * the payload in the completion.
+ */
+struct sli4_rqst_cmn_nop {
+ struct sli4_rqst_hdr hdr;
+ __le32 context[2];
+};
+
+struct sli4_rsp_cmn_nop {
+ struct sli4_rsp_hdr hdr;
+ __le32 context[2];
+};
+
+struct sli4_rqst_cmn_get_resource_extent_info {
+ struct sli4_rqst_hdr hdr;
+ __le16 resource_type;
+ __le16 rsvd16;
+};
+
+enum sli4_rsc_type {
+ SLI4_RSC_TYPE_VFI = 0x20,
+ SLI4_RSC_TYPE_VPI = 0x21,
+ SLI4_RSC_TYPE_RPI = 0x22,
+ SLI4_RSC_TYPE_XRI = 0x23,
+};
+
+struct sli4_rsp_cmn_get_resource_extent_info {
+ struct sli4_rsp_hdr hdr;
+ __le16 resource_extent_count;
+ __le16 resource_extent_size;
+};
+
+#define SLI4_128BYTE_WQE_SUPPORT 0x02
+
+#define GET_Q_CNT_METHOD(m) \
+ (((m) & SLI4_PARAM_Q_CNT_MTHD_MASK) >> SLI4_PARAM_Q_CNT_MTHD_SHFT)
+#define GET_Q_CREATE_VERSION(v) \
+ (((v) & SLI4_PARAM_QV_MASK) >> SLI4_PARAM_QV_SHIFT)
+
+enum sli4_rsp_get_params_e {
+ /*GENERIC*/
+ SLI4_PARAM_Q_CNT_MTHD_SHFT = 24,
+ SLI4_PARAM_Q_CNT_MTHD_MASK = 0xf << 24,
+ SLI4_PARAM_QV_SHIFT = 14,
+ SLI4_PARAM_QV_MASK = 3 << 14,
+
+ /* DW4 */
+ SLI4_PARAM_PROTO_TYPE_MASK = 0xff,
+ /* DW5 */
+ SLI4_PARAM_FT = 1 << 0,
+ SLI4_PARAM_SLI_REV_MASK = 0xf << 4,
+ SLI4_PARAM_SLI_FAM_MASK = 0xf << 8,
+ SLI4_PARAM_IF_TYPE_MASK = 0xf << 12,
+ SLI4_PARAM_SLI_HINT1_MASK = 0xff << 16,
+ SLI4_PARAM_SLI_HINT2_MASK = 0x1f << 24,
+ /* DW6 */
+ SLI4_PARAM_EQ_PAGE_CNT_MASK = 0xf << 0,
+ SLI4_PARAM_EQE_SZS_MASK = 0xf << 8,
+ SLI4_PARAM_EQ_PAGE_SZS_MASK = 0xff << 16,
+ /* DW8 */
+ SLI4_PARAM_CQ_PAGE_CNT_MASK = 0xf << 0,
+ SLI4_PARAM_CQE_SZS_MASK = 0xf << 8,
+ SLI4_PARAM_CQ_PAGE_SZS_MASK = 0xff << 16,
+ /* DW10 */
+ SLI4_PARAM_MQ_PAGE_CNT_MASK = 0xf << 0,
+ SLI4_PARAM_MQ_PAGE_SZS_MASK = 0xff << 16,
+ /* DW12 */
+ SLI4_PARAM_WQ_PAGE_CNT_MASK = 0xf << 0,
+ SLI4_PARAM_WQE_SZS_MASK = 0xf << 8,
+ SLI4_PARAM_WQ_PAGE_SZS_MASK = 0xff << 16,
+ /* DW14 */
+ SLI4_PARAM_RQ_PAGE_CNT_MASK = 0xf << 0,
+ SLI4_PARAM_RQE_SZS_MASK = 0xf << 8,
+ SLI4_PARAM_RQ_PAGE_SZS_MASK = 0xff << 16,
+ /* DW15W1*/
+ SLI4_PARAM_RQ_DB_WINDOW_MASK = 0xf000,
+ /* DW16 */
+ SLI4_PARAM_FC = 1 << 0,
+ SLI4_PARAM_EXT = 1 << 1,
+ SLI4_PARAM_HDRR = 1 << 2,
+ SLI4_PARAM_SGLR = 1 << 3,
+ SLI4_PARAM_FBRR = 1 << 4,
+ SLI4_PARAM_AREG = 1 << 5,
+ SLI4_PARAM_TGT = 1 << 6,
+ SLI4_PARAM_TERP = 1 << 7,
+ SLI4_PARAM_ASSI = 1 << 8,
+ SLI4_PARAM_WCHN = 1 << 9,
+ SLI4_PARAM_TCCA = 1 << 10,
+ SLI4_PARAM_TRTY = 1 << 11,
+ SLI4_PARAM_TRIR = 1 << 12,
+ SLI4_PARAM_PHOFF = 1 << 13,
+ SLI4_PARAM_PHON = 1 << 14,
+ SLI4_PARAM_PHWQ = 1 << 15,
+ SLI4_PARAM_BOUND_4GA = 1 << 16,
+ SLI4_PARAM_RXC = 1 << 17,
+ SLI4_PARAM_HLM = 1 << 18,
+ SLI4_PARAM_IPR = 1 << 19,
+ SLI4_PARAM_RXRI = 1 << 20,
+ SLI4_PARAM_SGLC = 1 << 21,
+ SLI4_PARAM_TIMM = 1 << 22,
+ SLI4_PARAM_TSMM = 1 << 23,
+ SLI4_PARAM_OAS = 1 << 25,
+ SLI4_PARAM_LC = 1 << 26,
+ SLI4_PARAM_AGXF = 1 << 27,
+ SLI4_PARAM_LOOPBACK_MASK = 0xf << 28,
+ /* DW18 */
+ SLI4_PARAM_SGL_PAGE_CNT_MASK = 0xf << 0,
+ SLI4_PARAM_SGL_PAGE_SZS_MASK = 0xff << 8,
+ SLI4_PARAM_SGL_PP_ALIGN_MASK = 0xff << 16,
+};
+
+struct sli4_rqst_cmn_get_sli4_params {
+ struct sli4_rqst_hdr hdr;
+};
+
+struct sli4_rsp_cmn_get_sli4_params {
+ struct sli4_rsp_hdr hdr;
+ __le32 dw4_protocol_type;
+ __le32 dw5_sli;
+ __le32 dw6_eq_page_cnt;
+ __le16 eqe_count_mask;
+ __le16 rsvd26;
+ __le32 dw8_cq_page_cnt;
+ __le16 cqe_count_mask;
+ __le16 rsvd34;
+ __le32 dw10_mq_page_cnt;
+ __le16 mqe_count_mask;
+ __le16 rsvd42;
+ __le32 dw12_wq_page_cnt;
+ __le16 wqe_count_mask;
+ __le16 rsvd50;
+ __le32 dw14_rq_page_cnt;
+ __le16 rqe_count_mask;
+ __le16 dw15w1_rq_db_window;
+ __le32 dw16_loopback_scope;
+ __le32 sge_supported_length;
+ __le32 dw18_sgl_page_cnt;
+ __le16 min_rq_buffer_size;
+ __le16 rsvd75;
+ __le32 max_rq_buffer_size;
+ __le16 physical_xri_max;
+ __le16 physical_rpi_max;
+ __le16 physical_vpi_max;
+ __le16 physical_vfi_max;
+ __le32 rsvd88;
+ __le16 frag_num_field_offset;
+ __le16 frag_num_field_size;
+ __le16 sgl_index_field_offset;
+ __le16 sgl_index_field_size;
+ __le32 chain_sge_initial_value_lo;
+ __le32 chain_sge_initial_value_hi;
+};
+
+/*Port Types*/
+enum sli4_port_types {
+ SLI4_PORT_TYPE_ETH = 0,
+ SLI4_PORT_TYPE_FC = 1,
+};
+
+struct sli4_rqst_cmn_get_port_name {
+ struct sli4_rqst_hdr hdr;
+ u8 port_type;
+ u8 rsvd4[3];
+};
+
+struct sli4_rsp_cmn_get_port_name {
+ struct sli4_rsp_hdr hdr;
+ char port_name[4];
+};
+
+struct sli4_rqst_cmn_write_flashrom {
+ struct sli4_rqst_hdr hdr;
+ __le32 flash_rom_access_opcode;
+ __le32 flash_rom_access_operation_type;
+ __le32 data_buffer_size;
+ __le32 offset;
+ u8 data_buffer[4];
+};
+
+/*
+ * COMMON_READ_TRANSCEIVER_DATA
+ *
+ * This command reads SFF transceiver data(Format is defined
+ * by the SFF-8472 specification).
+ */
+struct sli4_rqst_cmn_read_transceiver_data {
+ struct sli4_rqst_hdr hdr;
+ __le32 page_number;
+ __le32 port;
+};
+
+struct sli4_rsp_cmn_read_transceiver_data {
+ struct sli4_rsp_hdr hdr;
+ __le32 page_number;
+ __le32 port;
+ u8 page_data[128];
+ u8 page_data_2[128];
+};
+
+#define SLI4_REQ_DESIRE_READLEN 0xffffff
+
+struct sli4_rqst_cmn_read_object {
+ struct sli4_rqst_hdr hdr;
+ __le32 desired_read_length_dword;
+ __le32 read_offset;
+ u8 object_name[104];
+ __le32 host_buffer_descriptor_count;
+ struct sli4_bde host_buffer_descriptor[];
+};
+
+#define RSP_COM_READ_OBJ_EOF 0x80000000
+
+struct sli4_rsp_cmn_read_object {
+ struct sli4_rsp_hdr hdr;
+ __le32 actual_read_length;
+ __le32 eof_dword;
+};
+
+enum sli4_rqst_write_object_flags {
+ SLI4_RQ_DES_WRITE_LEN = 0xffffff,
+ SLI4_RQ_DES_WRITE_LEN_NOC = 0x40000000,
+ SLI4_RQ_DES_WRITE_LEN_EOF = 0x80000000,
+};
+
+struct sli4_rqst_cmn_write_object {
+ struct sli4_rqst_hdr hdr;
+ __le32 desired_write_len_dword;
+ __le32 write_offset;
+ u8 object_name[104];
+ __le32 host_buffer_descriptor_count;
+ struct sli4_bde host_buffer_descriptor[];
+};
+
+#define RSP_CHANGE_STATUS 0xff
+
+struct sli4_rsp_cmn_write_object {
+ struct sli4_rsp_hdr hdr;
+ __le32 actual_write_length;
+ __le32 change_status_dword;
+};
+
+struct sli4_rqst_cmn_delete_object {
+ struct sli4_rqst_hdr hdr;
+ __le32 rsvd4;
+ __le32 rsvd5;
+ u8 object_name[104];
+};
+
+#define SLI4_RQ_OBJ_LIST_READ_LEN 0xffffff
+
+struct sli4_rqst_cmn_read_object_list {
+ struct sli4_rqst_hdr hdr;
+ __le32 desired_read_length_dword;
+ __le32 read_offset;
+ u8 object_name[104];
+ __le32 host_buffer_descriptor_count;
+ struct sli4_bde host_buffer_descriptor[];
+};
+
+enum sli4_rqst_set_dump_flags {
+ SLI4_CMN_SET_DUMP_BUFFER_LEN = 0xffffff,
+ SLI4_CMN_SET_DUMP_FDB = 0x20000000,
+ SLI4_CMN_SET_DUMP_BLP = 0x40000000,
+ SLI4_CMN_SET_DUMP_QRY = 0x80000000,
+};
+
+struct sli4_rqst_cmn_set_dump_location {
+ struct sli4_rqst_hdr hdr;
+ __le32 buffer_length_dword;
+ __le32 buf_addr_low;
+ __le32 buf_addr_high;
+};
+
+struct sli4_rsp_cmn_set_dump_location {
+ struct sli4_rsp_hdr hdr;
+ __le32 buffer_length_dword;
+};
+
+enum sli4_dump_level {
+ SLI4_DUMP_LEVEL_NONE,
+ SLI4_CHIP_LEVEL_DUMP,
+ SLI4_FUNC_DESC_DUMP,
+};
+
+enum sli4_dump_state {
+ SLI4_DUMP_STATE_NONE,
+ SLI4_CHIP_DUMP_STATE_VALID,
+ SLI4_FUNC_DUMP_STATE_VALID,
+};
+
+enum sli4_dump_status {
+ SLI4_DUMP_READY_STATUS_NOT_READY,
+ SLI4_DUMP_READY_STATUS_DD_PRESENT,
+ SLI4_DUMP_READY_STATUS_FDB_PRESENT,
+ SLI4_DUMP_READY_STATUS_SKIP_DUMP,
+ SLI4_DUMP_READY_STATUS_FAILED = -1,
+};
+
+enum sli4_set_features {
+ SLI4_SET_FEATURES_DIF_SEED = 0x01,
+ SLI4_SET_FEATURES_XRI_TIMER = 0x03,
+ SLI4_SET_FEATURES_MAX_PCIE_SPEED = 0x04,
+ SLI4_SET_FEATURES_FCTL_CHECK = 0x05,
+ SLI4_SET_FEATURES_FEC = 0x06,
+ SLI4_SET_FEATURES_PCIE_RECV_DETECT = 0x07,
+ SLI4_SET_FEATURES_DIF_MEMORY_MODE = 0x08,
+ SLI4_SET_FEATURES_DISABLE_SLI_PORT_PAUSE_STATE = 0x09,
+ SLI4_SET_FEATURES_ENABLE_PCIE_OPTIONS = 0x0a,
+ SLI4_SET_FEAT_CFG_AUTO_XFER_RDY_T10PI = 0x0c,
+ SLI4_SET_FEATURES_ENABLE_MULTI_RECEIVE_QUEUE = 0x0d,
+ SLI4_SET_FEATURES_SET_FTD_XFER_HINT = 0x0f,
+ SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK = 0x11,
+};
+
+struct sli4_rqst_cmn_set_features {
+ struct sli4_rqst_hdr hdr;
+ __le32 feature;
+ __le32 param_len;
+ __le32 params[8];
+};
+
+struct sli4_rqst_cmn_set_features_dif_seed {
+ __le16 seed;
+ __le16 rsvd16;
+};
+
+enum sli4_rqst_set_mrq_features {
+ SLI4_RQ_MULTIRQ_ISR = 0x1,
+ SLI4_RQ_MULTIRQ_AUTOGEN_XFER_RDY = 0x2,
+
+ SLI4_RQ_MULTIRQ_NUM_RQS = 0xff,
+ SLI4_RQ_MULTIRQ_RQ_SELECT = 0xf00,
+};
+
+struct sli4_rqst_cmn_set_features_multirq {
+ __le32 auto_gen_xfer_dword;
+ __le32 num_rqs_dword;
+};
+
+enum sli4_rqst_health_check_flags {
+ SLI4_RQ_HEALTH_CHECK_ENABLE = 0x1,
+ SLI4_RQ_HEALTH_CHECK_QUERY = 0x2,
+};
+
+struct sli4_rqst_cmn_set_features_health_check {
+ __le32 health_check_dword;
+};
+
+struct sli4_rqst_cmn_set_features_set_fdt_xfer_hint {
+ __le32 fdt_xfer_hint;
+};
+
+struct sli4_rqst_dmtf_exec_clp_cmd {
+ struct sli4_rqst_hdr hdr;
+ __le32 cmd_buf_length;
+ __le32 resp_buf_length;
+ __le32 cmd_buf_addr_low;
+ __le32 cmd_buf_addr_high;
+ __le32 resp_buf_addr_low;
+ __le32 resp_buf_addr_high;
+};
+
+struct sli4_rsp_dmtf_exec_clp_cmd {
+ struct sli4_rsp_hdr hdr;
+ __le32 rsvd4;
+ __le32 resp_length;
+ __le32 rsvd6;
+ __le32 rsvd7;
+ __le32 rsvd8;
+ __le32 rsvd9;
+ __le32 clp_status;
+ __le32 clp_detailed_status;
+};
+
+#define SLI4_PROTOCOL_FC 0x10
+#define SLI4_PROTOCOL_DEFAULT 0xff
+
+struct sli4_rspource_descriptor_v1 {
+ u8 descriptor_type;
+ u8 descriptor_length;
+ __le16 rsvd16;
+ __le32 type_specific[];
+};
+
+enum sli4_pcie_desc_flags {
+ SLI4_PCIE_DESC_IMM = 0x4000,
+ SLI4_PCIE_DESC_NOSV = 0x8000,
+
+ SLI4_PCIE_DESC_PF_NO = 0x3ff0000,
+
+ SLI4_PCIE_DESC_MISSN_ROLE = 0xff,
+ SLI4_PCIE_DESC_PCHG = 0x8000000,
+ SLI4_PCIE_DESC_SCHG = 0x10000000,
+ SLI4_PCIE_DESC_XCHG = 0x20000000,
+ SLI4_PCIE_DESC_XROM = 0xc0000000
+};
+
+struct sli4_pcie_resource_descriptor_v1 {
+ u8 descriptor_type;
+ u8 descriptor_length;
+ __le16 imm_nosv_dword;
+ __le32 pf_number_dword;
+ __le32 rsvd3;
+ u8 sriov_state;
+ u8 pf_state;
+ u8 pf_type;
+ u8 rsvd4;
+ __le16 number_of_vfs;
+ __le16 rsvd5;
+ __le32 mission_roles_dword;
+ __le32 rsvd7[16];
+};
+
+struct sli4_rqst_cmn_get_function_config {
+ struct sli4_rqst_hdr hdr;
+};
+
+struct sli4_rsp_cmn_get_function_config {
+ struct sli4_rsp_hdr hdr;
+ __le32 desc_count;
+ __le32 desc[54];
+};
+
+/* Link Config Descriptor for link config functions */
+struct sli4_link_config_descriptor {
+ u8 link_config_id;
+ u8 rsvd1[3];
+ __le32 config_description[8];
+};
+
+#define MAX_LINK_DES 10
+
+struct sli4_rqst_cmn_get_reconfig_link_info {
+ struct sli4_rqst_hdr hdr;
+};
+
+struct sli4_rsp_cmn_get_reconfig_link_info {
+ struct sli4_rsp_hdr hdr;
+ u8 active_link_config_id;
+ u8 rsvd17;
+ u8 next_link_config_id;
+ u8 rsvd19;
+ __le32 link_configuration_descriptor_count;
+ struct sli4_link_config_descriptor
+ desc[MAX_LINK_DES];
+};
+
+enum sli4_set_reconfig_link_flags {
+ SLI4_SET_RECONFIG_LINKID_NEXT = 0xff,
+ SLI4_SET_RECONFIG_LINKID_FD = 1u << 31,
+};
+
+struct sli4_rqst_cmn_set_reconfig_link_id {
+ struct sli4_rqst_hdr hdr;
+ __le32 dw4_flags;
+};
+
+struct sli4_rsp_cmn_set_reconfig_link_id {
+ struct sli4_rsp_hdr hdr;
+};
+
+struct sli4_rqst_lowlevel_set_watchdog {
+ struct sli4_rqst_hdr hdr;
+ __le16 watchdog_timeout;
+ __le16 rsvd18;
+};
+
+struct sli4_rsp_lowlevel_set_watchdog {
+ struct sli4_rsp_hdr hdr;
+ __le32 rsvd;
+};
+
+/* FC opcode (OPC) values */
+enum sli4_fc_opcodes {
+ SLI4_OPC_WQ_CREATE = 0x1,
+ SLI4_OPC_WQ_DESTROY = 0x2,
+ SLI4_OPC_POST_SGL_PAGES = 0x3,
+ SLI4_OPC_RQ_CREATE = 0x5,
+ SLI4_OPC_RQ_DESTROY = 0x6,
+ SLI4_OPC_READ_FCF_TABLE = 0x8,
+ SLI4_OPC_POST_HDR_TEMPLATES = 0xb,
+ SLI4_OPC_REDISCOVER_FCF = 0x10,
+};
+
+/* Use the default CQ associated with the WQ */
+#define SLI4_CQ_DEFAULT 0xffff
+
+/*
+ * POST_SGL_PAGES
+ *
+ * Register the scatter gather list (SGL) memory and
+ * associate it with an XRI.
+ */
+struct sli4_rqst_post_sgl_pages {
+ struct sli4_rqst_hdr hdr;
+ __le16 xri_start;
+ __le16 xri_count;
+ struct {
+ __le32 page0_low;
+ __le32 page0_high;
+ __le32 page1_low;
+ __le32 page1_high;
+ } page_set[10];
+};
+
+struct sli4_rsp_post_sgl_pages {
+ struct sli4_rsp_hdr hdr;
+};
+
+struct sli4_rqst_post_hdr_templates {
+ struct sli4_rqst_hdr hdr;
+ __le16 rpi_offset;
+ __le16 page_count;
+ struct sli4_dmaaddr page_descriptor[];
+};
+
+#define SLI4_HDR_TEMPLATE_SIZE 64
+
+enum sli4_io_flags {
+/* The XRI associated with this IO is already active */
+ SLI4_IO_CONTINUATION = 1 << 0,
+/* Automatically generate a good RSP frame */
+ SLI4_IO_AUTO_GOOD_RESPONSE = 1 << 1,
+ SLI4_IO_NO_ABORT = 1 << 2,
+/* Set the DNRX bit because no auto xref rdy buffer is posted */
+ SLI4_IO_DNRX = 1 << 3,
+};
+
+enum sli4_callback {
+ SLI4_CB_LINK,
+ SLI4_CB_MAX,
+};
+
+enum sli4_link_status {
+ SLI4_LINK_STATUS_UP,
+ SLI4_LINK_STATUS_DOWN,
+ SLI4_LINK_STATUS_NO_ALPA,
+ SLI4_LINK_STATUS_MAX,
+};
+
+enum sli4_link_topology {
+ SLI4_LINK_TOPO_NON_FC_AL = 1,
+ SLI4_LINK_TOPO_FC_AL,
+ SLI4_LINK_TOPO_LOOPBACK_INTERNAL,
+ SLI4_LINK_TOPO_LOOPBACK_EXTERNAL,
+ SLI4_LINK_TOPO_NONE,
+ SLI4_LINK_TOPO_MAX,
+};
+
+enum sli4_link_medium {
+ SLI4_LINK_MEDIUM_ETHERNET,
+ SLI4_LINK_MEDIUM_FC,
+ SLI4_LINK_MEDIUM_MAX,
+};
+/******Driver specific structures******/
+
+struct sli4_queue {
+ /* Common to all queue types */
+ struct efc_dma dma;
+ spinlock_t lock; /* Lock to protect the doorbell register
+ * writes and queue reads
+ */
+ u32 index; /* current host entry index */
+ u16 size; /* entry size */
+ u16 length; /* number of entries */
+ u16 n_posted; /* number entries posted for CQ, EQ */
+ u16 id; /* Port assigned xQ_ID */
+ u8 type; /* queue type ie EQ, CQ, ... */
+ void __iomem *db_regaddr; /* register address for the doorbell */
+ u16 phase; /* For if_type = 6, this value toggle
+ * for each iteration of the queue,
+ * a queue entry is valid when a cqe
+ * valid bit matches this value
+ */
+ u32 proc_limit; /* limit CQE processed per iteration */
+ u32 posted_limit; /* CQE/EQE process before ring db */
+ u32 max_num_processed;
+ u64 max_process_time;
+ union {
+ u32 r_idx; /* "read" index (MQ only) */
+ u32 flag;
+ } u;
+};
+
+/* Parameters used to populate WQE*/
+struct sli_bls_params {
+ u32 s_id;
+ u32 d_id;
+ u16 ox_id;
+ u16 rx_id;
+ u32 rpi;
+ u32 vpi;
+ bool rpi_registered;
+ u8 payload[12];
+ u16 xri;
+ u16 tag;
+};
+
+struct sli_els_params {
+ u32 s_id;
+ u32 d_id;
+ u16 ox_id;
+ u32 rpi;
+ u32 vpi;
+ bool rpi_registered;
+ u32 xmit_len;
+ u32 rsp_len;
+ u8 timeout;
+ u8 cmd;
+ u16 xri;
+ u16 tag;
+};
+
+struct sli_ct_params {
+ u8 r_ctl;
+ u8 type;
+ u8 df_ctl;
+ u8 timeout;
+ u16 ox_id;
+ u32 d_id;
+ u32 rpi;
+ u32 vpi;
+ bool rpi_registered;
+ u32 xmit_len;
+ u32 rsp_len;
+ u16 xri;
+ u16 tag;
+};
+
+struct sli_fcp_tgt_params {
+ u32 s_id;
+ u32 d_id;
+ u32 rpi;
+ u32 vpi;
+ u32 offset;
+ u16 ox_id;
+ u16 flags;
+ u8 cs_ctl;
+ u8 timeout;
+ u32 app_id;
+ u32 xmit_len;
+ u16 xri;
+ u16 tag;
+};
+
+struct sli4_link_event {
+ enum sli4_link_status status;
+ enum sli4_link_topology topology;
+ enum sli4_link_medium medium;
+ u32 speed;
+ u8 *loop_map;
+ u32 fc_id;
+};
+
+enum sli4_resource {
+ SLI4_RSRC_VFI,
+ SLI4_RSRC_VPI,
+ SLI4_RSRC_RPI,
+ SLI4_RSRC_XRI,
+ SLI4_RSRC_FCFI,
+ SLI4_RSRC_MAX,
+};
+
+struct sli4_extent {
+ u32 number;
+ u32 size;
+ u32 n_alloc;
+ u32 *base;
+ unsigned long *use_map;
+ u32 map_size;
+};
+
+struct sli4_queue_info {
+ u16 max_qcount[SLI4_QTYPE_MAX];
+ u32 max_qentries[SLI4_QTYPE_MAX];
+ u16 count_mask[SLI4_QTYPE_MAX];
+ u16 count_method[SLI4_QTYPE_MAX];
+ u32 qpage_count[SLI4_QTYPE_MAX];
+};
+
+struct sli4_params {
+ u8 has_extents;
+ u8 auto_reg;
+ u8 auto_xfer_rdy;
+ u8 hdr_template_req;
+ u8 perf_hint;
+ u8 perf_wq_id_association;
+ u8 cq_create_version;
+ u8 mq_create_version;
+ u8 high_login_mode;
+ u8 sgl_pre_registered;
+ u8 sgl_pre_reg_required;
+ u8 t10_dif_inline_capable;
+ u8 t10_dif_separate_capable;
+};
+
+struct sli4 {
+ void *os;
+ struct pci_dev *pci;
+ void __iomem *reg[PCI_STD_NUM_BARS];
+
+ u32 sli_rev;
+ u32 sli_family;
+ u32 if_type;
+
+ u16 asic_type;
+ u16 asic_rev;
+
+ u16 e_d_tov;
+ u16 r_a_tov;
+ struct sli4_queue_info qinfo;
+ u16 link_module_type;
+ u8 rq_batch;
+ u8 port_number;
+ char port_name[2];
+ u16 rq_min_buf_size;
+ u32 rq_max_buf_size;
+ u8 topology;
+ u8 wwpn[8];
+ u8 wwnn[8];
+ u32 fw_rev[2];
+ u8 fw_name[2][16];
+ char ipl_name[16];
+ u32 hw_rev[3];
+ char modeldesc[64];
+ char bios_version_string[32];
+ u32 wqe_size;
+ u32 vpd_length;
+ /*
+ * Tracks the port resources using extents metaphor. For
+ * devices that don't implement extents (i.e.
+ * has_extents == FALSE), the code models each resource as
+ * a single large extent.
+ */
+ struct sli4_extent ext[SLI4_RSRC_MAX];
+ u32 features;
+ struct sli4_params params;
+ u32 sge_supported_length;
+ u32 sgl_page_sizes;
+ u32 max_sgl_pages;
+
+ /*
+ * Callback functions
+ */
+ int (*link)(void *ctx, void *event);
+ void *link_arg;
+
+ struct efc_dma bmbx;
+
+ /* Save pointer to physical memory descriptor for non-embedded
+ * SLI_CONFIG commands for BMBX dumping purposes
+ */
+ struct efc_dma *bmbx_non_emb_pmd;
+
+ struct efc_dma vpd_data;
+};
+
+static inline void
+sli_cmd_fill_hdr(struct sli4_rqst_hdr *hdr, u8 opc, u8 sub, u32 ver, __le32 len)
+{
+ hdr->opcode = opc;
+ hdr->subsystem = sub;
+ hdr->dw3_version = cpu_to_le32(ver);
+ hdr->request_length = len;
+}
+
+/**
+ * Get / set parameter functions
+ */
+
+static inline u32
+sli_get_max_sge(struct sli4 *sli4)
+{
+ return sli4->sge_supported_length;
+}
+
+static inline u32
+sli_get_max_sgl(struct sli4 *sli4)
+{
+ if (sli4->sgl_page_sizes != 1) {
+ efc_log_err(sli4, "unsupported SGL page sizes %#x\n",
+ sli4->sgl_page_sizes);
+ return 0;
+ }
+
+ return (sli4->max_sgl_pages * SLI_PAGE_SIZE) / sizeof(struct sli4_sge);
+}
+
+static inline enum sli4_link_medium
+sli_get_medium(struct sli4 *sli4)
+{
+ switch (sli4->topology) {
+ case SLI4_READ_CFG_TOPO_FC:
+ case SLI4_READ_CFG_TOPO_FC_AL:
+ case SLI4_READ_CFG_TOPO_NON_FC_AL:
+ return SLI4_LINK_MEDIUM_FC;
+ default:
+ return SLI4_LINK_MEDIUM_MAX;
+ }
+}
+
+static inline u32
+sli_get_lmt(struct sli4 *sli4)
+{
+ return sli4->link_module_type;
+}
+
+static inline int
+sli_set_topology(struct sli4 *sli4, u32 value)
+{
+ int rc = 0;
+
+ switch (value) {
+ case SLI4_READ_CFG_TOPO_FC:
+ case SLI4_READ_CFG_TOPO_FC_AL:
+ case SLI4_READ_CFG_TOPO_NON_FC_AL:
+ sli4->topology = value;
+ break;
+ default:
+ efc_log_err(sli4, "unsupported topology %#x\n", value);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static inline u32
+sli_convert_mask_to_count(u32 method, u32 mask)
+{
+ u32 count = 0;
+
+ if (method) {
+ count = 1 << (31 - __builtin_clz(mask));
+ count *= 16;
+ } else {
+ count = mask;
+ }
+
+ return count;
+}
+
+static inline u32
+sli_reg_read_status(struct sli4 *sli)
+{
+ return readl(sli->reg[0] + SLI4_PORT_STATUS_REGOFF);
+}
+
+static inline int
+sli_fw_error_status(struct sli4 *sli4)
+{
+ return (sli_reg_read_status(sli4) & SLI4_PORT_STATUS_ERR) ? 1 : 0;
+}
+
+static inline u32
+sli_reg_read_err1(struct sli4 *sli)
+{
+ return readl(sli->reg[0] + SLI4_PORT_ERROR1);
+}
+
+static inline u32
+sli_reg_read_err2(struct sli4 *sli)
+{
+ return readl(sli->reg[0] + SLI4_PORT_ERROR2);
+}
+
+static inline int
+sli_fc_rqe_length(struct sli4 *sli4, void *cqe, u32 *len_hdr,
+ u32 *len_data)
+{
+ struct sli4_fc_async_rcqe *rcqe = cqe;
+
+ *len_hdr = *len_data = 0;
+
+ if (rcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) {
+ *len_hdr = rcqe->hdpl_byte & SLI4_RACQE_HDPL;
+ *len_data = le16_to_cpu(rcqe->data_placement_length);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static inline u8
+sli_fc_rqe_fcfi(struct sli4 *sli4, void *cqe)
+{
+ u8 code = ((u8 *)cqe)[SLI4_CQE_CODE_OFFSET];
+ u8 fcfi = U8_MAX;
+
+ switch (code) {
+ case SLI4_CQE_CODE_RQ_ASYNC: {
+ struct sli4_fc_async_rcqe *rcqe = cqe;
+
+ fcfi = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_FCFI;
+ break;
+ }
+ case SLI4_CQE_CODE_RQ_ASYNC_V1: {
+ struct sli4_fc_async_rcqe_v1 *rcqev1 = cqe;
+
+ fcfi = rcqev1->fcfi_byte & SLI4_RACQE_FCFI;
+ break;
+ }
+ case SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD: {
+ struct sli4_fc_optimized_write_cmd_cqe *opt_wr = cqe;
+
+ fcfi = opt_wr->flags0 & SLI4_OCQE_FCFI;
+ break;
+ }
+ }
+
+ return fcfi;
+}
+
+/****************************************************************************
+ * Function prototypes
+ */
+int
+sli_cmd_config_link(struct sli4 *sli4, void *buf);
+int
+sli_cmd_down_link(struct sli4 *sli4, void *buf);
+int
+sli_cmd_dump_type4(struct sli4 *sli4, void *buf, u16 wki);
+int
+sli_cmd_common_read_transceiver_data(struct sli4 *sli4, void *buf,
+ u32 page_num, struct efc_dma *dma);
+int
+sli_cmd_read_link_stats(struct sli4 *sli4, void *buf, u8 req_stats,
+ u8 clear_overflow_flags, u8 clear_all_counters);
+int
+sli_cmd_read_status(struct sli4 *sli4, void *buf, u8 clear);
+int
+sli_cmd_init_link(struct sli4 *sli4, void *buf, u32 speed,
+ u8 reset_alpa);
+int
+sli_cmd_init_vfi(struct sli4 *sli4, void *buf, u16 vfi, u16 fcfi,
+ u16 vpi);
+int
+sli_cmd_init_vpi(struct sli4 *sli4, void *buf, u16 vpi, u16 vfi);
+int
+sli_cmd_post_xri(struct sli4 *sli4, void *buf, u16 base, u16 cnt);
+int
+sli_cmd_release_xri(struct sli4 *sli4, void *buf, u8 num_xri);
+int
+sli_cmd_read_sparm64(struct sli4 *sli4, void *buf,
+ struct efc_dma *dma, u16 vpi);
+int
+sli_cmd_read_topology(struct sli4 *sli4, void *buf, struct efc_dma *dma);
+int
+sli_cmd_read_nvparms(struct sli4 *sli4, void *buf);
+int
+sli_cmd_write_nvparms(struct sli4 *sli4, void *buf, u8 *wwpn,
+ u8 *wwnn, u8 hard_alpa, u32 preferred_d_id);
+int
+sli_cmd_reg_fcfi(struct sli4 *sli4, void *buf, u16 index,
+ struct sli4_cmd_rq_cfg *rq_cfg);
+int
+sli_cmd_reg_fcfi_mrq(struct sli4 *sli4, void *buf, u8 mode, u16 index,
+ u8 rq_selection_policy, u8 mrq_bit_mask, u16 num_mrqs,
+ struct sli4_cmd_rq_cfg *rq_cfg);
+int
+sli_cmd_reg_rpi(struct sli4 *sli4, void *buf, u32 rpi, u32 vpi, u32 fc_id,
+ struct efc_dma *dma, u8 update, u8 enable_t10_pi);
+int
+sli_cmd_unreg_fcfi(struct sli4 *sli4, void *buf, u16 indicator);
+int
+sli_cmd_unreg_rpi(struct sli4 *sli4, void *buf, u16 indicator,
+ enum sli4_resource which, u32 fc_id);
+int
+sli_cmd_reg_vpi(struct sli4 *sli4, void *buf, u32 fc_id,
+ __be64 sli_wwpn, u16 vpi, u16 vfi, bool update);
+int
+sli_cmd_reg_vfi(struct sli4 *sli4, void *buf, size_t size,
+ u16 vfi, u16 fcfi, struct efc_dma dma,
+ u16 vpi, __be64 sli_wwpn, u32 fc_id);
+int
+sli_cmd_unreg_vpi(struct sli4 *sli4, void *buf, u16 id, u32 type);
+int
+sli_cmd_unreg_vfi(struct sli4 *sli4, void *buf, u16 idx, u32 type);
+int
+sli_cmd_common_nop(struct sli4 *sli4, void *buf, uint64_t context);
+int
+sli_cmd_common_get_resource_extent_info(struct sli4 *sli4, void *buf,
+ u16 rtype);
+int
+sli_cmd_common_get_sli4_parameters(struct sli4 *sli4, void *buf);
+int
+sli_cmd_common_write_object(struct sli4 *sli4, void *buf, u16 noc,
+ u16 eof, u32 len, u32 offset, char *name, struct efc_dma *dma);
+int
+sli_cmd_common_delete_object(struct sli4 *sli4, void *buf, char *object_name);
+int
+sli_cmd_common_read_object(struct sli4 *sli4, void *buf,
+ u32 length, u32 offset, char *name, struct efc_dma *dma);
+int
+sli_cmd_dmtf_exec_clp_cmd(struct sli4 *sli4, void *buf,
+ struct efc_dma *cmd, struct efc_dma *resp);
+int
+sli_cmd_common_set_dump_location(struct sli4 *sli4, void *buf,
+ bool query, bool is_buffer_list, struct efc_dma *dma, u8 fdb);
+int
+sli_cmd_common_set_features(struct sli4 *sli4, void *buf,
+ u32 feature, u32 param_len, void *parameter);
+
+int sli_cqe_mq(struct sli4 *sli4, void *buf);
+int sli_cqe_async(struct sli4 *sli4, void *buf);
+
+int
+sli_setup(struct sli4 *sli4, void *os, struct pci_dev *pdev, void __iomem *r[]);
+void sli_calc_max_qentries(struct sli4 *sli4);
+int sli_init(struct sli4 *sli4);
+int sli_reset(struct sli4 *sli4);
+int sli_fw_reset(struct sli4 *sli4);
+void sli_teardown(struct sli4 *sli4);
+int
+sli_callback(struct sli4 *sli4, enum sli4_callback cb, void *func, void *arg);
+int
+sli_bmbx_command(struct sli4 *sli4);
+int
+__sli_queue_init(struct sli4 *sli4, struct sli4_queue *q, u32 qtype,
+ size_t size, u32 n_entries, u32 align);
+int
+__sli_create_queue(struct sli4 *sli4, struct sli4_queue *q);
+int
+sli_eq_modify_delay(struct sli4 *sli4, struct sli4_queue *eq, u32 num_eq,
+ u32 shift, u32 delay_mult);
+int
+sli_queue_alloc(struct sli4 *sli4, u32 qtype, struct sli4_queue *q,
+ u32 n_entries, struct sli4_queue *assoc);
+int
+sli_cq_alloc_set(struct sli4 *sli4, struct sli4_queue *qs[], u32 num_cqs,
+ u32 n_entries, struct sli4_queue *eqs[]);
+int
+sli_get_queue_entry_size(struct sli4 *sli4, u32 qtype);
+int
+sli_queue_free(struct sli4 *sli4, struct sli4_queue *q, u32 destroy_queues,
+ u32 free_memory);
+int
+sli_queue_eq_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm);
+int
+sli_queue_arm(struct sli4 *sli4, struct sli4_queue *q, bool arm);
+
+int
+sli_wq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry);
+int
+sli_mq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry);
+int
+sli_rq_write(struct sli4 *sli4, struct sli4_queue *q, u8 *entry);
+int
+sli_eq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry);
+int
+sli_cq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry);
+int
+sli_mq_read(struct sli4 *sli4, struct sli4_queue *q, u8 *entry);
+int
+sli_resource_alloc(struct sli4 *sli4, enum sli4_resource rtype, u32 *rid,
+ u32 *index);
+int
+sli_resource_free(struct sli4 *sli4, enum sli4_resource rtype, u32 rid);
+int
+sli_resource_reset(struct sli4 *sli4, enum sli4_resource rtype);
+int
+sli_eq_parse(struct sli4 *sli4, u8 *buf, u16 *cq_id);
+int
+sli_cq_parse(struct sli4 *sli4, struct sli4_queue *cq, u8 *cqe,
+ enum sli4_qentry *etype, u16 *q_id);
+
+int sli_raise_ue(struct sli4 *sli4, u8 dump);
+int sli_dump_is_ready(struct sli4 *sli4);
+bool sli_reset_required(struct sli4 *sli4);
+bool sli_fw_ready(struct sli4 *sli4);
+
+int
+sli_fc_process_link_attention(struct sli4 *sli4, void *acqe);
+int
+sli_fc_cqe_parse(struct sli4 *sli4, struct sli4_queue *cq,
+ u8 *cqe, enum sli4_qentry *etype,
+ u16 *rid);
+u32 sli_fc_response_length(struct sli4 *sli4, u8 *cqe);
+u32 sli_fc_io_length(struct sli4 *sli4, u8 *cqe);
+int sli_fc_els_did(struct sli4 *sli4, u8 *cqe, u32 *d_id);
+u32 sli_fc_ext_status(struct sli4 *sli4, u8 *cqe);
+int
+sli_fc_rqe_rqid_and_index(struct sli4 *sli4, u8 *cqe, u16 *rq_id, u32 *index);
+int
+sli_cmd_wq_create(struct sli4 *sli4, void *buf,
+ struct efc_dma *qmem, u16 cq_id);
+int sli_cmd_post_sgl_pages(struct sli4 *sli4, void *buf, u16 xri,
+ u32 xri_count, struct efc_dma *page0[], struct efc_dma *page1[],
+ struct efc_dma *dma);
+int
+sli_cmd_post_hdr_templates(struct sli4 *sli4, void *buf,
+ struct efc_dma *dma, u16 rpi, struct efc_dma *payload_dma);
+int
+sli_fc_rq_alloc(struct sli4 *sli4, struct sli4_queue *q, u32 n_entries,
+ u32 buffer_size, struct sli4_queue *cq, bool is_hdr);
+int
+sli_fc_rq_set_alloc(struct sli4 *sli4, u32 num_rq_pairs, struct sli4_queue *q[],
+ u32 base_cq_id, u32 num, u32 hdr_buf_size, u32 data_buf_size);
+u32 sli_fc_get_rpi_requirements(struct sli4 *sli4, u32 n_rpi);
+int
+sli_abort_wqe(struct sli4 *sli4, void *buf, enum sli4_abort_type type,
+ bool send_abts, u32 ids, u32 mask, u16 tag, u16 cq_id);
+
+int
+sli_send_frame_wqe(struct sli4 *sli4, void *buf, u8 sof, u8 eof,
+ u32 *hdr, struct efc_dma *payload, u32 req_len, u8 timeout,
+ u16 xri, u16 req_tag);
+
+int
+sli_xmit_els_rsp64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *rsp,
+ struct sli_els_params *params);
+
+int
+sli_els_request64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ struct sli_els_params *params);
+
+int
+sli_fcp_icmnd64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, u16 xri,
+ u16 tag, u16 cq_id, u32 rpi, u32 rnode_fcid, u8 timeout);
+
+int
+sli_fcp_iread64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u32 xfer_len, u16 xri,
+ u16 tag, u16 cq_id, u32 rpi, u32 rnode_fcid, u8 dif, u8 bs,
+ u8 timeout);
+
+int
+sli_fcp_iwrite64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u32 xfer_len,
+ u32 first_burst, u16 xri, u16 tag, u16 cq_id, u32 rpi,
+ u32 rnode_fcid, u8 dif, u8 bs, u8 timeout);
+
+int
+sli_fcp_treceive64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u16 cq_id, u8 dif, u8 bs,
+ struct sli_fcp_tgt_params *params);
+int
+sli_fcp_cont_treceive64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u16 sec_xri, u16 cq_id, u8 dif,
+ u8 bs, struct sli_fcp_tgt_params *params);
+
+int
+sli_fcp_trsp64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ u16 cq_id, u8 port_owned, struct sli_fcp_tgt_params *params);
+
+int
+sli_fcp_tsend64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ u32 first_data_sge, u16 cq_id, u8 dif, u8 bs,
+ struct sli_fcp_tgt_params *params);
+int
+sli_gen_request64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl,
+ struct sli_ct_params *params);
+
+int
+sli_xmit_bls_rsp64_wqe(struct sli4 *sli4, void *buf,
+ struct sli_bls_payload *payload, struct sli_bls_params *params);
+
+int
+sli_xmit_sequence64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *payload,
+ struct sli_ct_params *params);
+
+int
+sli_requeue_xri_wqe(struct sli4 *sli4, void *buf, u16 xri, u16 tag, u16 cq_id);
+void
+sli4_cmd_lowlevel_set_watchdog(struct sli4 *sli4, void *buf, size_t size,
+ u16 timeout);
+
+const char *sli_fc_get_status_string(u32 status);
+
+#endif /* !_SLI4_H */
diff --git a/drivers/scsi/esas2r/Kconfig b/drivers/scsi/esas2r/Kconfig
new file mode 100644
index 000000000000..c9b43f7fce31
--- /dev/null
+++ b/drivers/scsi/esas2r/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SCSI_ESAS2R
+ tristate "ATTO Technology's ExpressSAS RAID adapter driver"
+ depends on PCI && SCSI
+ help
+ This driver supports the ATTO ExpressSAS R6xx SAS/SATA RAID controllers.
diff --git a/drivers/scsi/esas2r/Makefile b/drivers/scsi/esas2r/Makefile
new file mode 100644
index 000000000000..279d9cb3ca69
--- /dev/null
+++ b/drivers/scsi/esas2r/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SCSI_ESAS2R) += esas2r.o
+
+esas2r-objs := esas2r_log.o esas2r_disc.o esas2r_flash.o esas2r_init.o \
+ esas2r_int.o esas2r_io.o esas2r_ioctl.o esas2r_targdb.o \
+ esas2r_vda.o esas2r_main.o
diff --git a/drivers/scsi/esas2r/atioctl.h b/drivers/scsi/esas2r/atioctl.h
new file mode 100644
index 000000000000..dd3437412ffc
--- /dev/null
+++ b/drivers/scsi/esas2r/atioctl.h
@@ -0,0 +1,1255 @@
+/* linux/drivers/scsi/esas2r/atioctl.h
+ * ATTO IOCTL Handling
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include "atvda.h"
+
+#ifndef ATIOCTL_H
+#define ATIOCTL_H
+
+#define EXPRESS_IOCTL_SIGNATURE "Express"
+#define EXPRESS_IOCTL_SIGNATURE_SIZE 8
+
+/* structure definitions for IOCTls */
+
+struct __packed atto_express_ioctl_header {
+ u8 signature[EXPRESS_IOCTL_SIGNATURE_SIZE];
+ u8 return_code;
+
+#define IOCTL_SUCCESS 0
+#define IOCTL_ERR_INVCMD 101
+#define IOCTL_INIT_FAILED 102
+#define IOCTL_NOT_IMPLEMENTED 103
+#define IOCTL_BAD_CHANNEL 104
+#define IOCTL_TARGET_OVERRUN 105
+#define IOCTL_TARGET_NOT_ENABLED 106
+#define IOCTL_BAD_FLASH_IMGTYPE 107
+#define IOCTL_OUT_OF_RESOURCES 108
+#define IOCTL_GENERAL_ERROR 109
+#define IOCTL_INVALID_PARAM 110
+
+ u8 channel;
+ u8 retries;
+ u8 pad[5];
+};
+
+/*
+ * NOTE - if channel == 0xFF, the request is
+ * handled on the adapter it came in on.
+ */
+#define MAX_NODE_NAMES 256
+
+struct __packed atto_firmware_rw_request {
+ u8 function;
+ #define FUNC_FW_DOWNLOAD 0x09
+ #define FUNC_FW_UPLOAD 0x12
+
+ u8 img_type;
+ #define FW_IMG_FW 0x01
+ #define FW_IMG_BIOS 0x02
+ #define FW_IMG_NVR 0x03
+ #define FW_IMG_RAW 0x04
+ #define FW_IMG_FM_API 0x05
+ #define FW_IMG_FS_API 0x06
+
+ u8 pad[2];
+ u32 img_offset;
+ u32 img_size;
+ u8 image[0x80000];
+};
+
+struct __packed atto_param_rw_request {
+ u16 code;
+ char data_buffer[512];
+};
+
+#define MAX_CHANNEL 256
+
+struct __packed atto_channel_list {
+ u32 num_channels;
+ u8 channel[MAX_CHANNEL];
+};
+
+struct __packed atto_channel_info {
+ u8 major_rev;
+ u8 minor_rev;
+ u8 IRQ;
+ u8 revision_id;
+ u8 pci_bus;
+ u8 pci_dev_func;
+ u8 core_rev;
+ u8 host_no;
+ u16 device_id;
+ u16 vendor_id;
+ u16 ven_dev_id;
+ u8 pad[3];
+ u32 hbaapi_rev;
+};
+
+/*
+ * CSMI control codes
+ * class independent
+ */
+#define CSMI_CC_GET_DRVR_INFO 1
+#define CSMI_CC_GET_CNTLR_CFG 2
+#define CSMI_CC_GET_CNTLR_STS 3
+#define CSMI_CC_FW_DOWNLOAD 4
+
+/* RAID class */
+#define CSMI_CC_GET_RAID_INFO 10
+#define CSMI_CC_GET_RAID_CFG 11
+
+/* HBA class */
+#define CSMI_CC_GET_PHY_INFO 20
+#define CSMI_CC_SET_PHY_INFO 21
+#define CSMI_CC_GET_LINK_ERRORS 22
+#define CSMI_CC_SMP_PASSTHRU 23
+#define CSMI_CC_SSP_PASSTHRU 24
+#define CSMI_CC_STP_PASSTHRU 25
+#define CSMI_CC_GET_SATA_SIG 26
+#define CSMI_CC_GET_SCSI_ADDR 27
+#define CSMI_CC_GET_DEV_ADDR 28
+#define CSMI_CC_TASK_MGT 29
+#define CSMI_CC_GET_CONN_INFO 30
+
+/* PHY class */
+#define CSMI_CC_PHY_CTRL 60
+
+/*
+ * CSMI status codes
+ * class independent
+ */
+#define CSMI_STS_SUCCESS 0
+#define CSMI_STS_FAILED 1
+#define CSMI_STS_BAD_CTRL_CODE 2
+#define CSMI_STS_INV_PARAM 3
+#define CSMI_STS_WRITE_ATTEMPTED 4
+
+/* RAID class */
+#define CSMI_STS_INV_RAID_SET 1000
+
+/* HBA class */
+#define CSMI_STS_PHY_CHANGED CSMI_STS_SUCCESS
+#define CSMI_STS_PHY_UNCHANGEABLE 2000
+#define CSMI_STS_INV_LINK_RATE 2001
+#define CSMI_STS_INV_PHY 2002
+#define CSMI_STS_INV_PHY_FOR_PORT 2003
+#define CSMI_STS_PHY_UNSELECTABLE 2004
+#define CSMI_STS_SELECT_PHY_OR_PORT 2005
+#define CSMI_STS_INV_PORT 2006
+#define CSMI_STS_PORT_UNSELECTABLE 2007
+#define CSMI_STS_CONNECTION_FAILED 2008
+#define CSMI_STS_NO_SATA_DEV 2009
+#define CSMI_STS_NO_SATA_SIGNATURE 2010
+#define CSMI_STS_SCSI_EMULATION 2011
+#define CSMI_STS_NOT_AN_END_DEV 2012
+#define CSMI_STS_NO_SCSI_ADDR 2013
+#define CSMI_STS_NO_DEV_ADDR 2014
+
+/* CSMI class independent structures */
+struct atto_csmi_get_driver_info {
+ char name[81];
+ char description[81];
+ u16 major_rev;
+ u16 minor_rev;
+ u16 build_rev;
+ u16 release_rev;
+ u16 csmi_major_rev;
+ u16 csmi_minor_rev;
+ #define CSMI_MAJOR_REV_0_81 0
+ #define CSMI_MINOR_REV_0_81 81
+
+ #define CSMI_MAJOR_REV CSMI_MAJOR_REV_0_81
+ #define CSMI_MINOR_REV CSMI_MINOR_REV_0_81
+};
+
+struct atto_csmi_get_pci_bus_addr {
+ u8 bus_num;
+ u8 device_num;
+ u8 function_num;
+ u8 reserved;
+};
+
+struct atto_csmi_get_cntlr_cfg {
+ u32 base_io_addr;
+
+ struct {
+ u32 base_memaddr_lo;
+ u32 base_memaddr_hi;
+ };
+
+ u32 board_id;
+ u16 slot_num;
+ #define CSMI_SLOT_NUM_UNKNOWN 0xFFFF
+
+ u8 cntlr_class;
+ #define CSMI_CNTLR_CLASS_HBA 5
+
+ u8 io_bus_type;
+ #define CSMI_BUS_TYPE_PCI 3
+ #define CSMI_BUS_TYPE_PCMCIA 4
+
+ union {
+ struct atto_csmi_get_pci_bus_addr pci_addr;
+ u8 reserved[32];
+ };
+
+ char serial_num[81];
+ u16 major_rev;
+ u16 minor_rev;
+ u16 build_rev;
+ u16 release_rev;
+ u16 bios_major_rev;
+ u16 bios_minor_rev;
+ u16 bios_build_rev;
+ u16 bios_release_rev;
+ u32 cntlr_flags;
+ #define CSMI_CNTLRF_SAS_HBA 0x00000001
+ #define CSMI_CNTLRF_SAS_RAID 0x00000002
+ #define CSMI_CNTLRF_SATA_HBA 0x00000004
+ #define CSMI_CNTLRF_SATA_RAID 0x00000008
+ #define CSMI_CNTLRF_FWD_SUPPORT 0x00010000
+ #define CSMI_CNTLRF_FWD_ONLINE 0x00020000
+ #define CSMI_CNTLRF_FWD_SRESET 0x00040000
+ #define CSMI_CNTLRF_FWD_HRESET 0x00080000
+ #define CSMI_CNTLRF_FWD_RROM 0x00100000
+
+ u16 rrom_major_rev;
+ u16 rrom_minor_rev;
+ u16 rrom_build_rev;
+ u16 rrom_release_rev;
+ u16 rrom_biosmajor_rev;
+ u16 rrom_biosminor_rev;
+ u16 rrom_biosbuild_rev;
+ u16 rrom_biosrelease_rev;
+ u8 reserved2[7];
+};
+
+struct atto_csmi_get_cntlr_sts {
+ u32 status;
+ #define CSMI_CNTLR_STS_GOOD 1
+ #define CSMI_CNTLR_STS_FAILED 2
+ #define CSMI_CNTLR_STS_OFFLINE 3
+ #define CSMI_CNTLR_STS_POWEROFF 4
+
+ u32 offline_reason;
+ #define CSMI_OFFLINE_NO_REASON 0
+ #define CSMI_OFFLINE_INITIALIZING 1
+ #define CSMI_OFFLINE_BUS_DEGRADED 2
+ #define CSMI_OFFLINE_BUS_FAILURE 3
+
+ u8 reserved[28];
+};
+
+struct atto_csmi_fw_download {
+ u32 buffer_len;
+ u32 download_flags;
+ #define CSMI_FWDF_VALIDATE 0x00000001
+ #define CSMI_FWDF_SOFT_RESET 0x00000002
+ #define CSMI_FWDF_HARD_RESET 0x00000004
+
+ u8 reserved[32];
+ u16 status;
+ #define CSMI_FWD_STS_SUCCESS 0
+ #define CSMI_FWD_STS_FAILED 1
+ #define CSMI_FWD_STS_USING_RROM 2
+ #define CSMI_FWD_STS_REJECT 3
+ #define CSMI_FWD_STS_DOWNREV 4
+
+ u16 severity;
+ #define CSMI_FWD_SEV_INFO 0
+ #define CSMI_FWD_SEV_WARNING 1
+ #define CSMI_FWD_SEV_ERROR 2
+ #define CSMI_FWD_SEV_FATAL 3
+
+};
+
+/* CSMI RAID class structures */
+struct atto_csmi_get_raid_info {
+ u32 num_raid_sets;
+ u32 max_drivesper_set;
+ u8 reserved[92];
+};
+
+struct atto_csmi_raid_drives {
+ char model[40];
+ char firmware[8];
+ char serial_num[40];
+ u8 sas_addr[8];
+ u8 lun[8];
+ u8 drive_sts;
+ #define CSMI_DRV_STS_OK 0
+ #define CSMI_DRV_STS_REBUILDING 1
+ #define CSMI_DRV_STS_FAILED 2
+ #define CSMI_DRV_STS_DEGRADED 3
+
+ u8 drive_usage;
+ #define CSMI_DRV_USE_NOT_USED 0
+ #define CSMI_DRV_USE_MEMBER 1
+ #define CSMI_DRV_USE_SPARE 2
+
+ u8 reserved[30]; /* spec says 22 */
+};
+
+struct atto_csmi_get_raid_cfg {
+ u32 raid_set_index;
+ u32 capacity;
+ u32 stripe_size;
+ u8 raid_type;
+ u8 status;
+ u8 information;
+ u8 drive_cnt;
+ u8 reserved[20];
+
+ struct atto_csmi_raid_drives drives[1];
+};
+
+/* CSMI HBA class structures */
+struct atto_csmi_phy_entity {
+ u8 ident_frame[0x1C];
+ u8 port_id;
+ u8 neg_link_rate;
+ u8 min_link_rate;
+ u8 max_link_rate;
+ u8 phy_change_cnt;
+ u8 auto_discover;
+ #define CSMI_DISC_NOT_SUPPORTED 0x00
+ #define CSMI_DISC_NOT_STARTED 0x01
+ #define CSMI_DISC_IN_PROGRESS 0x02
+ #define CSMI_DISC_COMPLETE 0x03
+ #define CSMI_DISC_ERROR 0x04
+
+ u8 reserved[2];
+ u8 attach_ident_frame[0x1C];
+};
+
+struct atto_csmi_get_phy_info {
+ u8 number_of_phys;
+ u8 reserved[3];
+ struct atto_csmi_phy_entity
+ phy[32];
+};
+
+struct atto_csmi_set_phy_info {
+ u8 phy_id;
+ u8 neg_link_rate;
+ #define CSMI_NEG_RATE_NEGOTIATE 0x00
+ #define CSMI_NEG_RATE_PHY_DIS 0x01
+
+ u8 prog_minlink_rate;
+ u8 prog_maxlink_rate;
+ u8 signal_class;
+ #define CSMI_SIG_CLASS_UNKNOWN 0x00
+ #define CSMI_SIG_CLASS_DIRECT 0x01
+ #define CSMI_SIG_CLASS_SERVER 0x02
+ #define CSMI_SIG_CLASS_ENCLOSURE 0x03
+
+ u8 reserved[3];
+};
+
+struct atto_csmi_get_link_errors {
+ u8 phy_id;
+ u8 reset_cnts;
+ #define CSMI_RESET_CNTS_NO 0x00
+ #define CSMI_RESET_CNTS_YES 0x01
+
+ u8 reserved[2];
+ u32 inv_dw_cnt;
+ u32 disp_err_cnt;
+ u32 loss_ofdw_sync_cnt;
+ u32 phy_reseterr_cnt;
+
+ /*
+ * The following field has been added by ATTO for ease of
+ * implementation of additional statistics. Drivers must validate
+ * the length of the IOCTL payload prior to filling them in so CSMI
+ * complaint applications function correctly.
+ */
+
+ u32 crc_err_cnt;
+};
+
+struct atto_csmi_smp_passthru {
+ u8 phy_id;
+ u8 port_id;
+ u8 conn_rate;
+ u8 reserved;
+ u8 dest_sas_addr[8];
+ u32 req_len;
+ u8 smp_req[1020];
+ u8 conn_sts;
+ u8 reserved2[3];
+ u32 rsp_len;
+ u8 smp_rsp[1020];
+};
+
+struct atto_csmi_ssp_passthru_sts {
+ u8 conn_sts;
+ u8 reserved[3];
+ u8 data_present;
+ u8 status;
+ u16 rsp_length;
+ u8 rsp[256];
+ u32 data_bytes;
+};
+
+struct atto_csmi_ssp_passthru {
+ u8 phy_id;
+ u8 port_id;
+ u8 conn_rate;
+ u8 reserved;
+ u8 dest_sas_addr[8];
+ u8 lun[8];
+ u8 cdb_len;
+ u8 add_cdb_len;
+ u8 reserved2[2];
+ u8 cdb[16];
+ u32 flags;
+ #define CSMI_SSPF_DD_READ 0x00000001
+ #define CSMI_SSPF_DD_WRITE 0x00000002
+ #define CSMI_SSPF_DD_UNSPECIFIED 0x00000004
+ #define CSMI_SSPF_TA_SIMPLE 0x00000000
+ #define CSMI_SSPF_TA_HEAD_OF_Q 0x00000010
+ #define CSMI_SSPF_TA_ORDERED 0x00000020
+ #define CSMI_SSPF_TA_ACA 0x00000040
+
+ u8 add_cdb[24];
+ u32 data_len;
+
+ struct atto_csmi_ssp_passthru_sts sts;
+};
+
+struct atto_csmi_stp_passthru_sts {
+ u8 conn_sts;
+ u8 reserved[3];
+ u8 sts_fis[20];
+ u32 scr[16];
+ u32 data_bytes;
+};
+
+struct atto_csmi_stp_passthru {
+ u8 phy_id;
+ u8 port_id;
+ u8 conn_rate;
+ u8 reserved;
+ u8 dest_sas_addr[8];
+ u8 reserved2[4];
+ u8 command_fis[20];
+ u32 flags;
+ #define CSMI_STPF_DD_READ 0x00000001
+ #define CSMI_STPF_DD_WRITE 0x00000002
+ #define CSMI_STPF_DD_UNSPECIFIED 0x00000004
+ #define CSMI_STPF_PIO 0x00000010
+ #define CSMI_STPF_DMA 0x00000020
+ #define CSMI_STPF_PACKET 0x00000040
+ #define CSMI_STPF_DMA_QUEUED 0x00000080
+ #define CSMI_STPF_EXECUTE_DIAG 0x00000100
+ #define CSMI_STPF_RESET_DEVICE 0x00000200
+
+ u32 data_len;
+
+ struct atto_csmi_stp_passthru_sts sts;
+};
+
+struct atto_csmi_get_sata_sig {
+ u8 phy_id;
+ u8 reserved[3];
+ u8 reg_dth_fis[20];
+};
+
+struct atto_csmi_get_scsi_addr {
+ u8 sas_addr[8];
+ u8 sas_lun[8];
+ u8 host_index;
+ u8 path_id;
+ u8 target_id;
+ u8 lun;
+};
+
+struct atto_csmi_get_dev_addr {
+ u8 host_index;
+ u8 path_id;
+ u8 target_id;
+ u8 lun;
+ u8 sas_addr[8];
+ u8 sas_lun[8];
+};
+
+struct atto_csmi_task_mgmt {
+ u8 host_index;
+ u8 path_id;
+ u8 target_id;
+ u8 lun;
+ u32 flags;
+ #define CSMI_TMF_TASK_IU 0x00000001
+ #define CSMI_TMF_HARD_RST 0x00000002
+ #define CSMI_TMF_SUPPRESS_RSLT 0x00000004
+
+ u32 queue_tag;
+ u32 reserved;
+ u8 task_mgt_func;
+ u8 reserved2[7];
+ u32 information;
+ #define CSMI_TM_INFO_TEST 1
+ #define CSMI_TM_INFO_EXCEEDED 2
+ #define CSMI_TM_INFO_DEMAND 3
+ #define CSMI_TM_INFO_TRIGGER 4
+
+ struct atto_csmi_ssp_passthru_sts sts;
+
+};
+
+struct atto_csmi_get_conn_info {
+ u32 pinout;
+ #define CSMI_CON_UNKNOWN 0x00000001
+ #define CSMI_CON_SFF_8482 0x00000002
+ #define CSMI_CON_SFF_8470_LANE_1 0x00000100
+ #define CSMI_CON_SFF_8470_LANE_2 0x00000200
+ #define CSMI_CON_SFF_8470_LANE_3 0x00000400
+ #define CSMI_CON_SFF_8470_LANE_4 0x00000800
+ #define CSMI_CON_SFF_8484_LANE_1 0x00010000
+ #define CSMI_CON_SFF_8484_LANE_2 0x00020000
+ #define CSMI_CON_SFF_8484_LANE_3 0x00040000
+ #define CSMI_CON_SFF_8484_LANE_4 0x00080000
+
+ u8 connector[16];
+ u8 location;
+ #define CSMI_CON_INTERNAL 0x02
+ #define CSMI_CON_EXTERNAL 0x04
+ #define CSMI_CON_SWITCHABLE 0x08
+ #define CSMI_CON_AUTO 0x10
+
+ u8 reserved[15];
+};
+
+/* CSMI PHY class structures */
+struct atto_csmi_character {
+ u8 type_flags;
+ #define CSMI_CTF_POS_DISP 0x01
+ #define CSMI_CTF_NEG_DISP 0x02
+ #define CSMI_CTF_CTRL_CHAR 0x04
+
+ u8 value;
+};
+
+struct atto_csmi_pc_ctrl {
+ u8 type;
+ #define CSMI_PC_TYPE_UNDEFINED 0x00
+ #define CSMI_PC_TYPE_SATA 0x01
+ #define CSMI_PC_TYPE_SAS 0x02
+ u8 rate;
+ u8 reserved[6];
+ u32 vendor_unique[8];
+ u32 tx_flags;
+ #define CSMI_PC_TXF_PREEMP_DIS 0x00000001
+
+ signed char tx_amplitude;
+ signed char tx_preemphasis;
+ signed char tx_slew_rate;
+ signed char tx_reserved[13];
+ u8 tx_vendor_unique[64];
+ u32 rx_flags;
+ #define CSMI_PC_RXF_EQ_DIS 0x00000001
+
+ signed char rx_threshold;
+ signed char rx_equalization_gain;
+ signed char rx_reserved[14];
+ u8 rx_vendor_unique[64];
+ u32 pattern_flags;
+ #define CSMI_PC_PATF_FIXED 0x00000001
+ #define CSMI_PC_PATF_DIS_SCR 0x00000002
+ #define CSMI_PC_PATF_DIS_ALIGN 0x00000004
+ #define CSMI_PC_PATF_DIS_SSC 0x00000008
+
+ u8 fixed_pattern;
+ #define CSMI_PC_FP_CJPAT 0x00000001
+ #define CSMI_PC_FP_ALIGN 0x00000002
+
+ u8 user_pattern_len;
+ u8 pattern_reserved[6];
+
+ struct atto_csmi_character user_pattern_buffer[16];
+};
+
+struct atto_csmi_phy_ctrl {
+ u32 function;
+ #define CSMI_PC_FUNC_GET_SETUP 0x00000100
+
+ u8 phy_id;
+ u16 len_of_cntl;
+ u8 num_of_cntls;
+ u8 reserved[4];
+ u32 link_flags;
+ #define CSMI_PHY_ACTIVATE_CTRL 0x00000001
+ #define CSMI_PHY_UPD_SPINUP_RATE 0x00000002
+ #define CSMI_PHY_AUTO_COMWAKE 0x00000004
+
+ u8 spinup_rate;
+ u8 link_reserved[7];
+ u32 vendor_unique[8];
+
+ struct atto_csmi_pc_ctrl control[1];
+};
+
+union atto_ioctl_csmi {
+ struct atto_csmi_get_driver_info drvr_info;
+ struct atto_csmi_get_cntlr_cfg cntlr_cfg;
+ struct atto_csmi_get_cntlr_sts cntlr_sts;
+ struct atto_csmi_fw_download fw_dwnld;
+ struct atto_csmi_get_raid_info raid_info;
+ struct atto_csmi_get_raid_cfg raid_cfg;
+ struct atto_csmi_get_phy_info get_phy_info;
+ struct atto_csmi_set_phy_info set_phy_info;
+ struct atto_csmi_get_link_errors link_errs;
+ struct atto_csmi_smp_passthru smp_pass_thru;
+ struct atto_csmi_ssp_passthru ssp_pass_thru;
+ struct atto_csmi_stp_passthru stp_pass_thru;
+ struct atto_csmi_task_mgmt tsk_mgt;
+ struct atto_csmi_get_sata_sig sata_sig;
+ struct atto_csmi_get_scsi_addr scsi_addr;
+ struct atto_csmi_get_dev_addr dev_addr;
+ struct atto_csmi_get_conn_info conn_info[32];
+ struct atto_csmi_phy_ctrl phy_ctrl;
+};
+
+struct atto_csmi {
+ u32 control_code;
+ u32 status;
+ union atto_ioctl_csmi data;
+};
+
+struct atto_module_info {
+ void *adapter;
+ void *pci_dev;
+ void *scsi_host;
+ unsigned short host_no;
+ union {
+ struct {
+ u64 node_name;
+ u64 port_name;
+ };
+ u64 sas_addr;
+ };
+};
+
+#define ATTO_FUNC_GET_ADAP_INFO 0x00
+#define ATTO_VER_GET_ADAP_INFO0 0
+#define ATTO_VER_GET_ADAP_INFO ATTO_VER_GET_ADAP_INFO0
+
+struct __packed atto_hba_get_adapter_info {
+
+ struct {
+ u16 vendor_id;
+ u16 device_id;
+ u16 ss_vendor_id;
+ u16 ss_device_id;
+ u8 class_code[3];
+ u8 rev_id;
+ u8 bus_num;
+ u8 dev_num;
+ u8 func_num;
+ u8 link_width_max;
+ u8 link_width_curr;
+ #define ATTO_GAI_PCILW_UNKNOWN 0x00
+
+ u8 link_speed_max;
+ u8 link_speed_curr;
+ #define ATTO_GAI_PCILS_UNKNOWN 0x00
+ #define ATTO_GAI_PCILS_GEN1 0x01
+ #define ATTO_GAI_PCILS_GEN2 0x02
+ #define ATTO_GAI_PCILS_GEN3 0x03
+
+ u8 interrupt_mode;
+ #define ATTO_GAI_PCIIM_UNKNOWN 0x00
+ #define ATTO_GAI_PCIIM_LEGACY 0x01
+ #define ATTO_GAI_PCIIM_MSI 0x02
+ #define ATTO_GAI_PCIIM_MSIX 0x03
+
+ u8 msi_vector_cnt;
+ u8 reserved[19];
+ } pci;
+
+ u8 adap_type;
+ #define ATTO_GAI_AT_EPCIU320 0x00
+ #define ATTO_GAI_AT_ESASRAID 0x01
+ #define ATTO_GAI_AT_ESASRAID2 0x02
+ #define ATTO_GAI_AT_ESASHBA 0x03
+ #define ATTO_GAI_AT_ESASHBA2 0x04
+ #define ATTO_GAI_AT_CELERITY 0x05
+ #define ATTO_GAI_AT_CELERITY8 0x06
+ #define ATTO_GAI_AT_FASTFRAME 0x07
+ #define ATTO_GAI_AT_ESASHBA3 0x08
+ #define ATTO_GAI_AT_CELERITY16 0x09
+ #define ATTO_GAI_AT_TLSASHBA 0x0A
+ #define ATTO_GAI_AT_ESASHBA4 0x0B
+
+ u8 adap_flags;
+ #define ATTO_GAI_AF_DEGRADED 0x01
+ #define ATTO_GAI_AF_SPT_SUPP 0x02
+ #define ATTO_GAI_AF_DEVADDR_SUPP 0x04
+ #define ATTO_GAI_AF_PHYCTRL_SUPP 0x08
+ #define ATTO_GAI_AF_TEST_SUPP 0x10
+ #define ATTO_GAI_AF_DIAG_SUPP 0x20
+ #define ATTO_GAI_AF_VIRT_SES 0x40
+ #define ATTO_GAI_AF_CONN_CTRL 0x80
+
+ u8 num_ports;
+ u8 num_phys;
+ u8 drvr_rev_major;
+ u8 drvr_rev_minor;
+ u8 drvr_revsub_minor;
+ u8 drvr_rev_build;
+ char drvr_rev_ascii[16];
+ char drvr_name[32];
+ char firmware_rev[16];
+ char flash_rev[16];
+ char model_name_short[16];
+ char model_name[32];
+ u32 num_targets;
+ u32 num_targsper_bus;
+ u32 num_lunsper_targ;
+ u8 num_busses;
+ u8 num_connectors;
+ u8 adap_flags2;
+ #define ATTO_GAI_AF2_FCOE_SUPP 0x01
+ #define ATTO_GAI_AF2_NIC_SUPP 0x02
+ #define ATTO_GAI_AF2_LOCATE_SUPP 0x04
+ #define ATTO_GAI_AF2_ADAP_CTRL_SUPP 0x08
+ #define ATTO_GAI_AF2_DEV_INFO_SUPP 0x10
+ #define ATTO_GAI_AF2_NPIV_SUPP 0x20
+ #define ATTO_GAI_AF2_MP_SUPP 0x40
+
+ u8 num_temp_sensors;
+ u32 num_targets_backend;
+ u32 tunnel_flags;
+ #define ATTO_GAI_TF_MEM_RW 0x00000001
+ #define ATTO_GAI_TF_TRACE 0x00000002
+ #define ATTO_GAI_TF_SCSI_PASS_THRU 0x00000004
+ #define ATTO_GAI_TF_GET_DEV_ADDR 0x00000008
+ #define ATTO_GAI_TF_PHY_CTRL 0x00000010
+ #define ATTO_GAI_TF_CONN_CTRL 0x00000020
+ #define ATTO_GAI_TF_GET_DEV_INFO 0x00000040
+
+ u8 reserved3[0x138];
+};
+
+#define ATTO_FUNC_GET_ADAP_ADDR 0x01
+#define ATTO_VER_GET_ADAP_ADDR0 0
+#define ATTO_VER_GET_ADAP_ADDR ATTO_VER_GET_ADAP_ADDR0
+
+struct __packed atto_hba_get_adapter_address {
+
+ u8 addr_type;
+ #define ATTO_GAA_AT_PORT 0x00
+ #define ATTO_GAA_AT_NODE 0x01
+ #define ATTO_GAA_AT_CURR_MAC 0x02
+ #define ATTO_GAA_AT_PERM_MAC 0x03
+ #define ATTO_GAA_AT_VNIC 0x04
+
+ u8 port_id;
+ u16 addr_len;
+ u8 address[256];
+};
+
+#define ATTO_FUNC_MEM_RW 0x02
+#define ATTO_VER_MEM_RW0 0
+#define ATTO_VER_MEM_RW ATTO_VER_MEM_RW0
+
+struct __packed atto_hba_memory_read_write {
+ u8 mem_func;
+ u8 mem_type;
+ union {
+ u8 pci_index;
+ u8 i2c_dev;
+ };
+ u8 i2c_status;
+ u32 length;
+ u64 address;
+ u8 reserved[48];
+
+};
+
+#define ATTO_FUNC_TRACE 0x03
+#define ATTO_VER_TRACE0 0
+#define ATTO_VER_TRACE1 1
+#define ATTO_VER_TRACE ATTO_VER_TRACE1
+
+struct __packed atto_hba_trace {
+ u8 trace_func;
+ #define ATTO_TRC_TF_GET_INFO 0x00
+ #define ATTO_TRC_TF_ENABLE 0x01
+ #define ATTO_TRC_TF_DISABLE 0x02
+ #define ATTO_TRC_TF_SET_MASK 0x03
+ #define ATTO_TRC_TF_UPLOAD 0x04
+ #define ATTO_TRC_TF_RESET 0x05
+
+ u8 trace_type;
+ #define ATTO_TRC_TT_DRIVER 0x00
+ #define ATTO_TRC_TT_FWCOREDUMP 0x01
+
+ u8 reserved[2];
+ u32 current_offset;
+ u32 total_length;
+ u32 trace_mask;
+ u8 reserved2[48];
+ u8 contents[];
+};
+
+#define ATTO_FUNC_SCSI_PASS_THRU 0x04
+#define ATTO_VER_SCSI_PASS_THRU0 0
+#define ATTO_VER_SCSI_PASS_THRU ATTO_VER_SCSI_PASS_THRU0
+
+struct __packed atto_hba_scsi_pass_thru {
+ u8 cdb[32];
+ u8 cdb_length;
+ u8 req_status;
+ #define ATTO_SPT_RS_SUCCESS 0x00
+ #define ATTO_SPT_RS_FAILED 0x01
+ #define ATTO_SPT_RS_OVERRUN 0x02
+ #define ATTO_SPT_RS_UNDERRUN 0x03
+ #define ATTO_SPT_RS_NO_DEVICE 0x04
+ #define ATTO_SPT_RS_NO_LUN 0x05
+ #define ATTO_SPT_RS_TIMEOUT 0x06
+ #define ATTO_SPT_RS_BUS_RESET 0x07
+ #define ATTO_SPT_RS_ABORTED 0x08
+ #define ATTO_SPT_RS_BUSY 0x09
+ #define ATTO_SPT_RS_DEGRADED 0x0A
+
+ u8 scsi_status;
+ u8 sense_length;
+ u32 flags;
+ #define ATTO_SPTF_DATA_IN 0x00000001
+ #define ATTO_SPTF_DATA_OUT 0x00000002
+ #define ATTO_SPTF_SIMPLE_Q 0x00000004
+ #define ATTO_SPTF_HEAD_OF_Q 0x00000008
+ #define ATTO_SPTF_ORDERED_Q 0x00000010
+
+ u32 timeout;
+ u32 target_id;
+ u8 lun[8];
+ u32 residual_length;
+ u8 sense_data[0xFC];
+ u8 reserved[0x28];
+};
+
+#define ATTO_FUNC_GET_DEV_ADDR 0x05
+#define ATTO_VER_GET_DEV_ADDR0 0
+#define ATTO_VER_GET_DEV_ADDR ATTO_VER_GET_DEV_ADDR0
+
+struct __packed atto_hba_get_device_address {
+ u8 addr_type;
+ #define ATTO_GDA_AT_PORT 0x00
+ #define ATTO_GDA_AT_NODE 0x01
+ #define ATTO_GDA_AT_MAC 0x02
+ #define ATTO_GDA_AT_PORTID 0x03
+ #define ATTO_GDA_AT_UNIQUE 0x04
+
+ u8 reserved;
+ u16 addr_len;
+ u32 target_id;
+ u8 address[256];
+};
+
+/* The following functions are supported by firmware but do not have any
+ * associated driver structures
+ */
+#define ATTO_FUNC_PHY_CTRL 0x06
+#define ATTO_FUNC_CONN_CTRL 0x0C
+#define ATTO_FUNC_ADAP_CTRL 0x0E
+#define ATTO_VER_ADAP_CTRL0 0
+#define ATTO_VER_ADAP_CTRL ATTO_VER_ADAP_CTRL0
+
+struct __packed atto_hba_adap_ctrl {
+ u8 adap_func;
+ #define ATTO_AC_AF_HARD_RST 0x00
+ #define ATTO_AC_AF_GET_STATE 0x01
+ #define ATTO_AC_AF_GET_TEMP 0x02
+
+ u8 adap_state;
+ #define ATTO_AC_AS_UNKNOWN 0x00
+ #define ATTO_AC_AS_OK 0x01
+ #define ATTO_AC_AS_RST_SCHED 0x02
+ #define ATTO_AC_AS_RST_IN_PROG 0x03
+ #define ATTO_AC_AS_RST_DISC 0x04
+ #define ATTO_AC_AS_DEGRADED 0x05
+ #define ATTO_AC_AS_DISABLED 0x06
+ #define ATTO_AC_AS_TEMP 0x07
+
+ u8 reserved[2];
+
+ union {
+ struct {
+ u8 temp_sensor;
+ u8 temp_state;
+
+ #define ATTO_AC_TS_UNSUPP 0x00
+ #define ATTO_AC_TS_UNKNOWN 0x01
+ #define ATTO_AC_TS_INIT_FAILED 0x02
+ #define ATTO_AC_TS_NORMAL 0x03
+ #define ATTO_AC_TS_OUT_OF_RANGE 0x04
+ #define ATTO_AC_TS_FAULT 0x05
+
+ signed short temp_value;
+ signed short temp_lower_lim;
+ signed short temp_upper_lim;
+ char temp_desc[32];
+ u8 reserved2[20];
+ };
+ };
+};
+
+#define ATTO_FUNC_GET_DEV_INFO 0x0F
+#define ATTO_VER_GET_DEV_INFO0 0
+#define ATTO_VER_GET_DEV_INFO ATTO_VER_GET_DEV_INFO0
+
+struct __packed atto_hba_sas_device_info {
+
+ #define ATTO_SDI_MAX_PHYS_WIDE_PORT 16
+
+ u8 phy_id[ATTO_SDI_MAX_PHYS_WIDE_PORT]; /* IDs of parent exp/adapt */
+ #define ATTO_SDI_PHY_ID_INV ATTO_SAS_PHY_ID_INV
+ u32 exp_target_id;
+ u32 sas_port_mask;
+ u8 sas_level;
+ #define ATTO_SDI_SAS_LVL_INV 0xFF
+
+ u8 slot_num;
+ #define ATTO_SDI_SLOT_NUM_INV ATTO_SLOT_NUM_INV
+
+ u8 dev_type;
+ #define ATTO_SDI_DT_END_DEVICE 0
+ #define ATTO_SDI_DT_EXPANDER 1
+ #define ATTO_SDI_DT_PORT_MULT 2
+
+ u8 ini_flags;
+ u8 tgt_flags;
+ u8 link_rate; /* SMP_RATE_XXX */
+ u8 loc_flags;
+ #define ATTO_SDI_LF_DIRECT 0x01
+ #define ATTO_SDI_LF_EXPANDER 0x02
+ #define ATTO_SDI_LF_PORT_MULT 0x04
+ u8 pm_port;
+ u8 reserved[0x60];
+};
+
+union atto_hba_device_info {
+ struct atto_hba_sas_device_info sas_dev_info;
+};
+
+struct __packed atto_hba_get_device_info {
+ u32 target_id;
+ u8 info_type;
+ #define ATTO_GDI_IT_UNKNOWN 0x00
+ #define ATTO_GDI_IT_SAS 0x01
+ #define ATTO_GDI_IT_FC 0x02
+ #define ATTO_GDI_IT_FCOE 0x03
+
+ u8 reserved[11];
+ union atto_hba_device_info dev_info;
+};
+
+struct atto_ioctl {
+ u8 version;
+ u8 function; /* ATTO_FUNC_XXX */
+ u8 status;
+#define ATTO_STS_SUCCESS 0x00
+#define ATTO_STS_FAILED 0x01
+#define ATTO_STS_INV_VERSION 0x02
+#define ATTO_STS_OUT_OF_RSRC 0x03
+#define ATTO_STS_INV_FUNC 0x04
+#define ATTO_STS_UNSUPPORTED 0x05
+#define ATTO_STS_INV_ADAPTER 0x06
+#define ATTO_STS_INV_DRVR_VER 0x07
+#define ATTO_STS_INV_PARAM 0x08
+#define ATTO_STS_TIMEOUT 0x09
+#define ATTO_STS_NOT_APPL 0x0A
+#define ATTO_STS_DEGRADED 0x0B
+
+ u8 flags;
+ #define HBAF_TUNNEL 0x01
+
+ u32 data_length;
+ u8 reserved2[56];
+
+ union {
+ u8 byte[1];
+ struct atto_hba_get_adapter_info get_adap_info;
+ struct atto_hba_get_adapter_address get_adap_addr;
+ struct atto_hba_scsi_pass_thru scsi_pass_thru;
+ struct atto_hba_get_device_address get_dev_addr;
+ struct atto_hba_adap_ctrl adap_ctrl;
+ struct atto_hba_get_device_info get_dev_info;
+ struct atto_hba_trace trace;
+ } data;
+
+};
+
+struct __packed atto_ioctl_vda_scsi_cmd {
+
+ #define ATTO_VDA_SCSI_VER0 0
+ #define ATTO_VDA_SCSI_VER ATTO_VDA_SCSI_VER0
+
+ u8 cdb[16];
+ u32 flags;
+ u32 data_length;
+ u32 residual_length;
+ u16 target_id;
+ u8 sense_len;
+ u8 scsi_stat;
+ u8 reserved[8];
+ u8 sense_data[80];
+};
+
+struct __packed atto_ioctl_vda_flash_cmd {
+
+ #define ATTO_VDA_FLASH_VER0 0
+ #define ATTO_VDA_FLASH_VER ATTO_VDA_FLASH_VER0
+
+ u32 flash_addr;
+ u32 data_length;
+ u8 sub_func;
+ u8 reserved[15];
+
+ union {
+ struct {
+ u32 flash_size;
+ u32 page_size;
+ u8 prod_info[32];
+ } info;
+
+ struct {
+ char file_name[16]; /* 8.3 fname, NULL term, wc=* */
+ u32 file_size;
+ } file;
+ } data;
+
+};
+
+struct __packed atto_ioctl_vda_diag_cmd {
+
+ #define ATTO_VDA_DIAG_VER0 0
+ #define ATTO_VDA_DIAG_VER ATTO_VDA_DIAG_VER0
+
+ u64 local_addr;
+ u32 data_length;
+ u8 sub_func;
+ u8 flags;
+ u8 reserved[3];
+};
+
+struct __packed atto_ioctl_vda_cli_cmd {
+
+ #define ATTO_VDA_CLI_VER0 0
+ #define ATTO_VDA_CLI_VER ATTO_VDA_CLI_VER0
+
+ u32 cmd_rsp_len;
+};
+
+struct __packed atto_ioctl_vda_smp_cmd {
+
+ #define ATTO_VDA_SMP_VER0 0
+ #define ATTO_VDA_SMP_VER ATTO_VDA_SMP_VER0
+
+ u64 dest;
+ u32 cmd_rsp_len;
+};
+
+struct __packed atto_ioctl_vda_cfg_cmd {
+
+ #define ATTO_VDA_CFG_VER0 0
+ #define ATTO_VDA_CFG_VER ATTO_VDA_CFG_VER0
+
+ u32 data_length;
+ u8 cfg_func;
+ u8 reserved[11];
+
+ union {
+ u8 bytes[112];
+ struct atto_vda_cfg_init init;
+ } data;
+
+};
+
+struct __packed atto_ioctl_vda_mgt_cmd {
+
+ #define ATTO_VDA_MGT_VER0 0
+ #define ATTO_VDA_MGT_VER ATTO_VDA_MGT_VER0
+
+ u8 mgt_func;
+ u8 scan_generation;
+ u16 dev_index;
+ u32 data_length;
+ u8 reserved[8];
+ union {
+ u8 bytes[112];
+ struct atto_vda_devinfo dev_info;
+ struct atto_vda_grp_info grp_info;
+ struct atto_vdapart_info part_info;
+ struct atto_vda_dh_info dh_info;
+ struct atto_vda_metrics_info metrics_info;
+ struct atto_vda_schedule_info sched_info;
+ struct atto_vda_n_vcache_info nvcache_info;
+ struct atto_vda_buzzer_info buzzer_info;
+ struct atto_vda_adapter_info adapter_info;
+ struct atto_vda_temp_info temp_info;
+ struct atto_vda_fan_info fan_info;
+ } data;
+};
+
+struct __packed atto_ioctl_vda_gsv_cmd {
+
+ #define ATTO_VDA_GSV_VER0 0
+ #define ATTO_VDA_GSV_VER ATTO_VDA_GSV_VER0
+
+ u8 rsp_len;
+ u8 reserved[7];
+ u8 version_info[];
+ #define ATTO_VDA_VER_UNSUPPORTED 0xFF
+
+};
+
+struct __packed atto_ioctl_vda {
+ u8 version;
+ u8 function; /* VDA_FUNC_XXXX */
+ u8 status; /* ATTO_STS_XXX */
+ u8 vda_status; /* RS_XXX (if status == ATTO_STS_SUCCESS) */
+ u32 data_length;
+ u8 reserved[8];
+
+ union {
+ struct atto_ioctl_vda_scsi_cmd scsi;
+ struct atto_ioctl_vda_flash_cmd flash;
+ struct atto_ioctl_vda_diag_cmd diag;
+ struct atto_ioctl_vda_cli_cmd cli;
+ struct atto_ioctl_vda_smp_cmd smp;
+ struct atto_ioctl_vda_cfg_cmd cfg;
+ struct atto_ioctl_vda_mgt_cmd mgt;
+ struct atto_ioctl_vda_gsv_cmd gsv;
+ u8 cmd_info[256];
+ } cmd;
+
+ union {
+ u8 data[1];
+ struct atto_vda_devinfo2 dev_info2;
+ } data;
+
+};
+
+struct __packed atto_ioctl_smp {
+ u8 version;
+ #define ATTO_SMP_VERSION0 0
+ #define ATTO_SMP_VERSION1 1
+ #define ATTO_SMP_VERSION2 2
+ #define ATTO_SMP_VERSION ATTO_SMP_VERSION2
+
+ u8 function;
+#define ATTO_SMP_FUNC_DISC_SMP 0x00
+#define ATTO_SMP_FUNC_DISC_TARG 0x01
+#define ATTO_SMP_FUNC_SEND_CMD 0x02
+#define ATTO_SMP_FUNC_DISC_TARG_DIRECT 0x03
+#define ATTO_SMP_FUNC_SEND_CMD_DIRECT 0x04
+#define ATTO_SMP_FUNC_DISC_SMP_DIRECT 0x05
+
+ u8 status; /* ATTO_STS_XXX */
+ u8 smp_status; /* if status == ATTO_STS_SUCCESS */
+ #define ATTO_SMP_STS_SUCCESS 0x00
+ #define ATTO_SMP_STS_FAILURE 0x01
+ #define ATTO_SMP_STS_RESCAN 0x02
+ #define ATTO_SMP_STS_NOT_FOUND 0x03
+
+ u16 target_id;
+ u8 phy_id;
+ u8 dev_index;
+ u64 smp_sas_addr;
+ u64 targ_sas_addr;
+ u32 req_length;
+ u32 rsp_length;
+ u8 flags;
+ #define ATTO_SMPF_ROOT_EXP 0x01 /* expander direct attached */
+
+ u8 reserved[31];
+
+ union {
+ u8 byte[1];
+ u32 dword[1];
+ } data;
+
+};
+
+struct __packed atto_express_ioctl {
+ struct atto_express_ioctl_header header;
+
+ union {
+ struct atto_firmware_rw_request fwrw;
+ struct atto_param_rw_request prw;
+ struct atto_channel_list chanlist;
+ struct atto_channel_info chaninfo;
+ struct atto_ioctl ioctl_hba;
+ struct atto_module_info modinfo;
+ struct atto_ioctl_vda ioctl_vda;
+ struct atto_ioctl_smp ioctl_smp;
+ struct atto_csmi csmi;
+
+ } data;
+};
+
+/* The struct associated with the code is listed after the definition */
+#define EXPRESS_IOCTL_MIN 0x4500
+#define EXPRESS_IOCTL_RW_FIRMWARE 0x4500 /* FIRMWARERW */
+#define EXPRESS_IOCTL_READ_PARAMS 0x4501 /* PARAMRW */
+#define EXPRESS_IOCTL_WRITE_PARAMS 0x4502 /* PARAMRW */
+#define EXPRESS_IOCTL_FC_API 0x4503 /* internal */
+#define EXPRESS_IOCTL_GET_CHANNELS 0x4504 /* CHANNELLIST */
+#define EXPRESS_IOCTL_CHAN_INFO 0x4505 /* CHANNELINFO */
+#define EXPRESS_IOCTL_DEFAULT_PARAMS 0x4506 /* PARAMRW */
+#define EXPRESS_ADDR_MEMORY 0x4507 /* MEMADDR */
+#define EXPRESS_RW_MEMORY 0x4508 /* MEMRW */
+#define EXPRESS_TSDK_DUMP 0x4509 /* TSDKDUMP */
+#define EXPRESS_IOCTL_SMP 0x450A /* IOCTL_SMP */
+#define EXPRESS_CSMI 0x450B /* CSMI */
+#define EXPRESS_IOCTL_HBA 0x450C /* IOCTL_HBA */
+#define EXPRESS_IOCTL_VDA 0x450D /* IOCTL_VDA */
+#define EXPRESS_IOCTL_GET_ID 0x450E /* GET_ID */
+#define EXPRESS_IOCTL_GET_MOD_INFO 0x450F /* MODULE_INFO */
+#define EXPRESS_IOCTL_MAX 0x450F
+
+#endif
diff --git a/drivers/scsi/esas2r/atvda.h b/drivers/scsi/esas2r/atvda.h
new file mode 100644
index 000000000000..5fc1f991d24e
--- /dev/null
+++ b/drivers/scsi/esas2r/atvda.h
@@ -0,0 +1,1319 @@
+/* linux/drivers/scsi/esas2r/atvda.h
+ * ATTO VDA interface definitions
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+
+#ifndef ATVDA_H
+#define ATVDA_H
+
+struct __packed atto_dev_addr {
+ u64 dev_port;
+ u64 hba_port;
+ u8 lun;
+ u8 flags;
+ #define VDA_DEVADDRF_SATA 0x01
+ #define VDA_DEVADDRF_SSD 0x02
+ u8 link_speed; /* VDALINKSPEED_xxx */
+ u8 pad[1];
+};
+
+/* dev_addr2 was added for 64-bit alignment */
+
+struct __packed atto_dev_addr2 {
+ u64 dev_port;
+ u64 hba_port;
+ u8 lun;
+ u8 flags;
+ u8 link_speed;
+ u8 pad[5];
+};
+
+struct __packed atto_vda_sge {
+ u32 length;
+ u64 address;
+};
+
+
+/* VDA request function codes */
+
+#define VDA_FUNC_SCSI 0x00
+#define VDA_FUNC_FLASH 0x01
+#define VDA_FUNC_DIAG 0x02
+#define VDA_FUNC_AE 0x03
+#define VDA_FUNC_CLI 0x04
+#define VDA_FUNC_IOCTL 0x05
+#define VDA_FUNC_CFG 0x06
+#define VDA_FUNC_MGT 0x07
+#define VDA_FUNC_GSV 0x08
+
+
+/* VDA request status values. for host driver considerations, values for
+ * SCSI requests start at zero. other requests may use these values as well. */
+
+#define RS_SUCCESS 0x00 /*! successful completion */
+#define RS_INV_FUNC 0x01 /*! invalid command function */
+#define RS_BUSY 0x02 /*! insufficient resources */
+#define RS_SEL 0x03 /*! no target at target_id */
+#define RS_NO_LUN 0x04 /*! invalid LUN */
+#define RS_TIMEOUT 0x05 /*! request timeout */
+#define RS_OVERRUN 0x06 /*! data overrun */
+#define RS_UNDERRUN 0x07 /*! data underrun */
+#define RS_SCSI_ERROR 0x08 /*! SCSI error occurred */
+#define RS_ABORTED 0x0A /*! command aborted */
+#define RS_RESID_MISM 0x0B /*! residual length incorrect */
+#define RS_TM_FAILED 0x0C /*! task management failed */
+#define RS_RESET 0x0D /*! aborted due to bus reset */
+#define RS_ERR_DMA_SG 0x0E /*! error reading SG list */
+#define RS_ERR_DMA_DATA 0x0F /*! error transferring data */
+#define RS_UNSUPPORTED 0x10 /*! unsupported request */
+#define RS_SEL2 0x70 /*! internal generated RS_SEL */
+#define RS_VDA_BASE 0x80 /*! base of VDA-specific errors */
+#define RS_MGT_BASE 0x80 /*! base of VDA management errors */
+#define RS_SCAN_FAIL (RS_MGT_BASE + 0x00)
+#define RS_DEV_INVALID (RS_MGT_BASE + 0x01)
+#define RS_DEV_ASSIGNED (RS_MGT_BASE + 0x02)
+#define RS_DEV_REMOVE (RS_MGT_BASE + 0x03)
+#define RS_DEV_LOST (RS_MGT_BASE + 0x04)
+#define RS_SCAN_GEN (RS_MGT_BASE + 0x05)
+#define RS_GRP_INVALID (RS_MGT_BASE + 0x08)
+#define RS_GRP_EXISTS (RS_MGT_BASE + 0x09)
+#define RS_GRP_LIMIT (RS_MGT_BASE + 0x0A)
+#define RS_GRP_INTLV (RS_MGT_BASE + 0x0B)
+#define RS_GRP_SPAN (RS_MGT_BASE + 0x0C)
+#define RS_GRP_TYPE (RS_MGT_BASE + 0x0D)
+#define RS_GRP_MEMBERS (RS_MGT_BASE + 0x0E)
+#define RS_GRP_COMMIT (RS_MGT_BASE + 0x0F)
+#define RS_GRP_REBUILD (RS_MGT_BASE + 0x10)
+#define RS_GRP_REBUILD_TYPE (RS_MGT_BASE + 0x11)
+#define RS_GRP_BLOCK_SIZE (RS_MGT_BASE + 0x12)
+#define RS_CFG_SAVE (RS_MGT_BASE + 0x14)
+#define RS_PART_LAST (RS_MGT_BASE + 0x18)
+#define RS_ELEM_INVALID (RS_MGT_BASE + 0x19)
+#define RS_PART_MAPPED (RS_MGT_BASE + 0x1A)
+#define RS_PART_TARGET (RS_MGT_BASE + 0x1B)
+#define RS_PART_LUN (RS_MGT_BASE + 0x1C)
+#define RS_PART_DUP (RS_MGT_BASE + 0x1D)
+#define RS_PART_NOMAP (RS_MGT_BASE + 0x1E)
+#define RS_PART_MAX (RS_MGT_BASE + 0x1F)
+#define RS_PART_CAP (RS_MGT_BASE + 0x20)
+#define RS_PART_STATE (RS_MGT_BASE + 0x21)
+#define RS_TEST_IN_PROG (RS_MGT_BASE + 0x22)
+#define RS_METRICS_ERROR (RS_MGT_BASE + 0x23)
+#define RS_HS_ERROR (RS_MGT_BASE + 0x24)
+#define RS_NO_METRICS_TEST (RS_MGT_BASE + 0x25)
+#define RS_BAD_PARAM (RS_MGT_BASE + 0x26)
+#define RS_GRP_MEMBER_SIZE (RS_MGT_BASE + 0x27)
+#define RS_FLS_BASE 0xB0 /*! base of VDA errors */
+#define RS_FLS_ERR_AREA (RS_FLS_BASE + 0x00)
+#define RS_FLS_ERR_BUSY (RS_FLS_BASE + 0x01)
+#define RS_FLS_ERR_RANGE (RS_FLS_BASE + 0x02)
+#define RS_FLS_ERR_BEGIN (RS_FLS_BASE + 0x03)
+#define RS_FLS_ERR_CHECK (RS_FLS_BASE + 0x04)
+#define RS_FLS_ERR_FAIL (RS_FLS_BASE + 0x05)
+#define RS_FLS_ERR_RSRC (RS_FLS_BASE + 0x06)
+#define RS_FLS_ERR_NOFILE (RS_FLS_BASE + 0x07)
+#define RS_FLS_ERR_FSIZE (RS_FLS_BASE + 0x08)
+#define RS_CFG_BASE 0xC0 /*! base of VDA configuration errors */
+#define RS_CFG_ERR_BUSY (RS_CFG_BASE + 0)
+#define RS_CFG_ERR_SGE (RS_CFG_BASE + 1)
+#define RS_CFG_ERR_DATE (RS_CFG_BASE + 2)
+#define RS_CFG_ERR_TIME (RS_CFG_BASE + 3)
+#define RS_DEGRADED 0xFB /*! degraded mode */
+#define RS_CLI_INTERNAL 0xFC /*! VDA CLI internal error */
+#define RS_VDA_INTERNAL 0xFD /*! catch-all */
+#define RS_PENDING 0xFE /*! pending, not started */
+#define RS_STARTED 0xFF /*! started */
+
+
+/* flash request subfunctions. these are used in both the IOCTL and the
+ * driver-firmware interface (VDA_FUNC_FLASH). */
+
+#define VDA_FLASH_BEGINW 0x00
+#define VDA_FLASH_READ 0x01
+#define VDA_FLASH_WRITE 0x02
+#define VDA_FLASH_COMMIT 0x03
+#define VDA_FLASH_CANCEL 0x04
+#define VDA_FLASH_INFO 0x05
+#define VDA_FLASH_FREAD 0x06
+#define VDA_FLASH_FWRITE 0x07
+#define VDA_FLASH_FINFO 0x08
+
+
+/* IOCTL request subfunctions. these identify the payload type for
+ * VDA_FUNC_IOCTL.
+ */
+
+#define VDA_IOCTL_HBA 0x00
+#define VDA_IOCTL_CSMI 0x01
+#define VDA_IOCTL_SMP 0x02
+
+struct __packed atto_vda_devinfo {
+ struct atto_dev_addr dev_addr;
+ u8 vendor_id[8];
+ u8 product_id[16];
+ u8 revision[4];
+ u64 capacity;
+ u32 block_size;
+ u8 dev_type;
+
+ union {
+ u8 dev_status;
+ #define VDADEVSTAT_INVALID 0x00
+ #define VDADEVSTAT_CORRUPT VDADEVSTAT_INVALID
+ #define VDADEVSTAT_ASSIGNED 0x01
+ #define VDADEVSTAT_SPARE 0x02
+ #define VDADEVSTAT_UNAVAIL 0x03
+ #define VDADEVSTAT_PT_MAINT 0x04
+ #define VDADEVSTAT_LCLSPARE 0x05
+ #define VDADEVSTAT_UNUSEABLE 0x06
+ #define VDADEVSTAT_AVAIL 0xFF
+
+ u8 op_ctrl;
+ #define VDA_DEV_OP_CTRL_START 0x01
+ #define VDA_DEV_OP_CTRL_HALT 0x02
+ #define VDA_DEV_OP_CTRL_RESUME 0x03
+ #define VDA_DEV_OP_CTRL_CANCEL 0x04
+ };
+
+ u8 member_state;
+ #define VDAMBRSTATE_ONLINE 0x00
+ #define VDAMBRSTATE_DEGRADED 0x01
+ #define VDAMBRSTATE_UNAVAIL 0x02
+ #define VDAMBRSTATE_FAULTED 0x03
+ #define VDAMBRSTATE_MISREAD 0x04
+ #define VDAMBRSTATE_INCOMPAT 0x05
+
+ u8 operation;
+ #define VDAOP_NONE 0x00
+ #define VDAOP_REBUILD 0x01
+ #define VDAOP_ERASE 0x02
+ #define VDAOP_PATTERN 0x03
+ #define VDAOP_CONVERSION 0x04
+ #define VDAOP_FULL_INIT 0x05
+ #define VDAOP_QUICK_INIT 0x06
+ #define VDAOP_SECT_SCAN 0x07
+ #define VDAOP_SECT_SCAN_PARITY 0x08
+ #define VDAOP_SECT_SCAN_PARITY_FIX 0x09
+ #define VDAOP_RECOV_REBUILD 0x0A
+
+ u8 op_status;
+ #define VDAOPSTAT_OK 0x00
+ #define VDAOPSTAT_FAULTED 0x01
+ #define VDAOPSTAT_HALTED 0x02
+ #define VDAOPSTAT_INT 0x03
+
+ u8 progress; /* 0 - 100% */
+ u16 ses_dev_index;
+ #define VDASESDI_INVALID 0xFFFF
+
+ u8 serial_no[32];
+
+ union {
+ u16 target_id;
+ #define VDATGTID_INVALID 0xFFFF
+
+ u16 features_mask;
+ };
+
+ u16 lun;
+ u16 features;
+ #define VDADEVFEAT_ENC_SERV 0x0001
+ #define VDADEVFEAT_IDENT 0x0002
+ #define VDADEVFEAT_DH_SUPP 0x0004
+ #define VDADEVFEAT_PHYS_ID 0x0008
+
+ u8 ses_element_id;
+ u8 link_speed;
+ #define VDALINKSPEED_UNKNOWN 0x00
+ #define VDALINKSPEED_1GB 0x01
+ #define VDALINKSPEED_1_5GB 0x02
+ #define VDALINKSPEED_2GB 0x03
+ #define VDALINKSPEED_3GB 0x04
+ #define VDALINKSPEED_4GB 0x05
+ #define VDALINKSPEED_6GB 0x06
+ #define VDALINKSPEED_8GB 0x07
+
+ u16 phys_target_id;
+ u8 reserved[2];
+};
+
+
+/*! struct atto_vda_devinfo2 is a replacement for atto_vda_devinfo. it
+ * extends beyond the 0x70 bytes allowed in atto_vda_mgmt_req; therefore,
+ * the entire structure is DMaed between the firmware and host buffer and
+ * the data will always be in little endian format.
+ */
+
+struct __packed atto_vda_devinfo2 {
+ struct atto_dev_addr dev_addr;
+ u8 vendor_id[8];
+ u8 product_id[16];
+ u8 revision[4];
+ u64 capacity;
+ u32 block_size;
+ u8 dev_type;
+ u8 dev_status;
+ u8 member_state;
+ u8 operation;
+ u8 op_status;
+ u8 progress;
+ u16 ses_dev_index;
+ u8 serial_no[32];
+ union {
+ u16 target_id;
+ u16 features_mask;
+ };
+
+ u16 lun;
+ u16 features;
+ u8 ses_element_id;
+ u8 link_speed;
+ u16 phys_target_id;
+ u8 reserved[2];
+
+/* This is where fields specific to struct atto_vda_devinfo2 begin. Note
+ * that the structure version started at one so applications that unionize this
+ * structure with atto_vda_dev_info can differentiate them if desired.
+ */
+
+ u8 version;
+ #define VDADEVINFO_VERSION0 0x00
+ #define VDADEVINFO_VERSION1 0x01
+ #define VDADEVINFO_VERSION2 0x02
+ #define VDADEVINFO_VERSION3 0x03
+ #define VDADEVINFO_VERSION VDADEVINFO_VERSION3
+
+ u8 reserved2[3];
+
+ /* sector scanning fields */
+
+ u32 ss_curr_errors;
+ u64 ss_curr_scanned;
+ u32 ss_curr_recvrd;
+ u32 ss_scan_length;
+ u32 ss_total_errors;
+ u32 ss_total_recvrd;
+ u32 ss_num_scans;
+
+ /* grp_name was added in version 2 of this structure. */
+
+ char grp_name[15];
+ u8 reserved3[4];
+
+ /* dev_addr_list was added in version 3 of this structure. */
+
+ u8 num_dev_addr;
+ struct atto_dev_addr2 dev_addr_list[8];
+};
+
+
+struct __packed atto_vda_grp_info {
+ u8 grp_index;
+ #define VDA_MAX_RAID_GROUPS 32
+
+ char grp_name[15];
+ u64 capacity;
+ u32 block_size;
+ u32 interleave;
+ u8 type;
+ #define VDA_GRP_TYPE_RAID0 0
+ #define VDA_GRP_TYPE_RAID1 1
+ #define VDA_GRP_TYPE_RAID4 4
+ #define VDA_GRP_TYPE_RAID5 5
+ #define VDA_GRP_TYPE_RAID6 6
+ #define VDA_GRP_TYPE_RAID10 10
+ #define VDA_GRP_TYPE_RAID40 40
+ #define VDA_GRP_TYPE_RAID50 50
+ #define VDA_GRP_TYPE_RAID60 60
+ #define VDA_GRP_TYPE_DVRAID_HS 252
+ #define VDA_GRP_TYPE_DVRAID_NOHS 253
+ #define VDA_GRP_TYPE_JBOD 254
+ #define VDA_GRP_TYPE_SPARE 255
+
+ union {
+ u8 status;
+ #define VDA_GRP_STAT_INVALID 0x00
+ #define VDA_GRP_STAT_NEW 0x01
+ #define VDA_GRP_STAT_WAITING 0x02
+ #define VDA_GRP_STAT_ONLINE 0x03
+ #define VDA_GRP_STAT_DEGRADED 0x04
+ #define VDA_GRP_STAT_OFFLINE 0x05
+ #define VDA_GRP_STAT_DELETED 0x06
+ #define VDA_GRP_STAT_RECOV_BASIC 0x07
+ #define VDA_GRP_STAT_RECOV_EXTREME 0x08
+
+ u8 op_ctrl;
+ #define VDA_GRP_OP_CTRL_START 0x01
+ #define VDA_GRP_OP_CTRL_HALT 0x02
+ #define VDA_GRP_OP_CTRL_RESUME 0x03
+ #define VDA_GRP_OP_CTRL_CANCEL 0x04
+ };
+
+ u8 rebuild_state;
+ #define VDA_RBLD_NONE 0x00
+ #define VDA_RBLD_REBUILD 0x01
+ #define VDA_RBLD_ERASE 0x02
+ #define VDA_RBLD_PATTERN 0x03
+ #define VDA_RBLD_CONV 0x04
+ #define VDA_RBLD_FULL_INIT 0x05
+ #define VDA_RBLD_QUICK_INIT 0x06
+ #define VDA_RBLD_SECT_SCAN 0x07
+ #define VDA_RBLD_SECT_SCAN_PARITY 0x08
+ #define VDA_RBLD_SECT_SCAN_PARITY_FIX 0x09
+ #define VDA_RBLD_RECOV_REBUILD 0x0A
+ #define VDA_RBLD_RECOV_BASIC 0x0B
+ #define VDA_RBLD_RECOV_EXTREME 0x0C
+
+ u8 span_depth;
+ u8 progress;
+ u8 mirror_width;
+ u8 stripe_width;
+ u8 member_cnt;
+
+ union {
+ u16 members[32];
+ #define VDA_MEMBER_MISSING 0xFFFF
+ #define VDA_MEMBER_NEW 0xFFFE
+ u16 features_mask;
+ };
+
+ u16 features;
+ #define VDA_GRP_FEAT_HOTSWAP 0x0001
+ #define VDA_GRP_FEAT_SPDRD_MASK 0x0006
+ #define VDA_GRP_FEAT_SPDRD_DIS 0x0000
+ #define VDA_GRP_FEAT_SPDRD_ENB 0x0002
+ #define VDA_GRP_FEAT_SPDRD_AUTO 0x0004
+ #define VDA_GRP_FEAT_IDENT 0x0008
+ #define VDA_GRP_FEAT_RBLDPRI_MASK 0x0030
+ #define VDA_GRP_FEAT_RBLDPRI_LOW 0x0010
+ #define VDA_GRP_FEAT_RBLDPRI_SAME 0x0020
+ #define VDA_GRP_FEAT_RBLDPRI_HIGH 0x0030
+ #define VDA_GRP_FEAT_WRITE_CACHE 0x0040
+ #define VDA_GRP_FEAT_RBLD_RESUME 0x0080
+ #define VDA_GRP_FEAT_SECT_RESUME 0x0100
+ #define VDA_GRP_FEAT_INIT_RESUME 0x0200
+ #define VDA_GRP_FEAT_SSD 0x0400
+ #define VDA_GRP_FEAT_BOOT_DEV 0x0800
+
+ /*
+ * for backward compatibility, a prefetch value of zero means the
+ * setting is ignored/unsupported. therefore, the firmware supported
+ * 0-6 values are incremented to 1-7.
+ */
+
+ u8 prefetch;
+ u8 op_status;
+ #define VDAGRPOPSTAT_MASK 0x0F
+ #define VDAGRPOPSTAT_INVALID 0x00
+ #define VDAGRPOPSTAT_OK 0x01
+ #define VDAGRPOPSTAT_FAULTED 0x02
+ #define VDAGRPOPSTAT_HALTED 0x03
+ #define VDAGRPOPSTAT_INT 0x04
+ #define VDAGRPOPPROC_MASK 0xF0
+ #define VDAGRPOPPROC_STARTABLE 0x10
+ #define VDAGRPOPPROC_CANCELABLE 0x20
+ #define VDAGRPOPPROC_RESUMABLE 0x40
+ #define VDAGRPOPPROC_HALTABLE 0x80
+ u8 over_provision;
+ u8 reserved[3];
+
+};
+
+
+struct __packed atto_vdapart_info {
+ u8 part_no;
+ #define VDA_MAX_PARTITIONS 128
+
+ char grp_name[15];
+ u64 part_size;
+ u64 start_lba;
+ u32 block_size;
+ u16 target_id;
+ u8 LUN;
+ char serial_no[41];
+ u8 features;
+ #define VDAPI_FEAT_WRITE_CACHE 0x01
+
+ u8 reserved[7];
+};
+
+
+struct __packed atto_vda_dh_info {
+ u8 req_type;
+ #define VDADH_RQTYPE_CACHE 0x01
+ #define VDADH_RQTYPE_FETCH 0x02
+ #define VDADH_RQTYPE_SET_STAT 0x03
+ #define VDADH_RQTYPE_GET_STAT 0x04
+
+ u8 req_qual;
+ #define VDADH_RQQUAL_SMART 0x01
+ #define VDADH_RQQUAL_MEDDEF 0x02
+ #define VDADH_RQQUAL_INFOEXC 0x04
+
+ u8 num_smart_attribs;
+ u8 status;
+ #define VDADH_STAT_DISABLE 0x00
+ #define VDADH_STAT_ENABLE 0x01
+
+ u32 med_defect_cnt;
+ u32 info_exc_cnt;
+ u8 smart_status;
+ #define VDADH_SMARTSTAT_OK 0x00
+ #define VDADH_SMARTSTAT_ERR 0x01
+
+ u8 reserved[35];
+ struct atto_vda_sge sge[1];
+};
+
+
+struct __packed atto_vda_dh_smart {
+ u8 attrib_id;
+ u8 current_val;
+ u8 worst;
+ u8 threshold;
+ u8 raw_data[6];
+ u8 raw_attrib_status;
+ #define VDADHSM_RAWSTAT_PREFAIL_WARRANTY 0x01
+ #define VDADHSM_RAWSTAT_ONLINE_COLLECTION 0x02
+ #define VDADHSM_RAWSTAT_PERFORMANCE_ATTR 0x04
+ #define VDADHSM_RAWSTAT_ERROR_RATE_ATTR 0x08
+ #define VDADHSM_RAWSTAT_EVENT_COUNT_ATTR 0x10
+ #define VDADHSM_RAWSTAT_SELF_PRESERVING_ATTR 0x20
+
+ u8 calc_attrib_status;
+ #define VDADHSM_CALCSTAT_UNKNOWN 0x00
+ #define VDADHSM_CALCSTAT_GOOD 0x01
+ #define VDADHSM_CALCSTAT_PREFAIL 0x02
+ #define VDADHSM_CALCSTAT_OLDAGE 0x03
+
+ u8 reserved[4];
+};
+
+
+struct __packed atto_vda_metrics_info {
+ u8 data_version;
+ #define VDAMET_VERSION0 0x00
+ #define VDAMET_VERSION VDAMET_VERSION0
+
+ u8 metrics_action;
+ #define VDAMET_METACT_NONE 0x00
+ #define VDAMET_METACT_START 0x01
+ #define VDAMET_METACT_STOP 0x02
+ #define VDAMET_METACT_RETRIEVE 0x03
+ #define VDAMET_METACT_CLEAR 0x04
+
+ u8 test_action;
+ #define VDAMET_TSTACT_NONE 0x00
+ #define VDAMET_TSTACT_STRT_INIT 0x01
+ #define VDAMET_TSTACT_STRT_READ 0x02
+ #define VDAMET_TSTACT_STRT_VERIFY 0x03
+ #define VDAMET_TSTACT_STRT_INIT_VERIFY 0x04
+ #define VDAMET_TSTACT_STOP 0x05
+
+ u8 num_dev_indexes;
+ #define VDAMET_ALL_DEVICES 0xFF
+
+ u16 dev_indexes[32];
+ u8 reserved[12];
+ struct atto_vda_sge sge[1];
+};
+
+
+struct __packed atto_vda_metrics_data {
+ u16 dev_index;
+ u16 length;
+ #define VDAMD_LEN_LAST 0x8000
+ #define VDAMD_LEN_MASK 0x0FFF
+
+ u32 flags;
+ #define VDAMDF_RUN 0x00000007
+ #define VDAMDF_RUN_READ 0x00000001
+ #define VDAMDF_RUN_WRITE 0x00000002
+ #define VDAMDF_RUN_ALL 0x00000004
+ #define VDAMDF_READ 0x00000010
+ #define VDAMDF_WRITE 0x00000020
+ #define VDAMDF_ALL 0x00000040
+ #define VDAMDF_DRIVETEST 0x40000000
+ #define VDAMDF_NEW 0x80000000
+
+ u64 total_read_data;
+ u64 total_write_data;
+ u64 total_read_io;
+ u64 total_write_io;
+ u64 read_start_time;
+ u64 read_stop_time;
+ u64 write_start_time;
+ u64 write_stop_time;
+ u64 read_maxio_time;
+ u64 wpvdadmetricsdatarite_maxio_time;
+ u64 read_totalio_time;
+ u64 write_totalio_time;
+ u64 read_total_errs;
+ u64 write_total_errs;
+ u64 read_recvrd_errs;
+ u64 write_recvrd_errs;
+ u64 miscompares;
+};
+
+
+struct __packed atto_vda_schedule_info {
+ u8 schedule_type;
+ #define VDASI_SCHTYPE_ONETIME 0x01
+ #define VDASI_SCHTYPE_DAILY 0x02
+ #define VDASI_SCHTYPE_WEEKLY 0x03
+
+ u8 operation;
+ #define VDASI_OP_NONE 0x00
+ #define VDASI_OP_CREATE 0x01
+ #define VDASI_OP_CANCEL 0x02
+
+ u8 hour;
+ u8 minute;
+ u8 day;
+ #define VDASI_DAY_NONE 0x00
+
+ u8 progress;
+ #define VDASI_PROG_NONE 0xFF
+
+ u8 event_type;
+ #define VDASI_EVTTYPE_SECT_SCAN 0x01
+ #define VDASI_EVTTYPE_SECT_SCAN_PARITY 0x02
+ #define VDASI_EVTTYPE_SECT_SCAN_PARITY_FIX 0x03
+
+ u8 recurrences;
+ #define VDASI_RECUR_FOREVER 0x00
+
+ u32 id;
+ #define VDASI_ID_NONE 0x00
+
+ char grp_name[15];
+ u8 reserved[85];
+};
+
+
+struct __packed atto_vda_n_vcache_info {
+ u8 super_cap_status;
+ #define VDANVCI_SUPERCAP_NOT_PRESENT 0x00
+ #define VDANVCI_SUPERCAP_FULLY_CHARGED 0x01
+ #define VDANVCI_SUPERCAP_NOT_CHARGED 0x02
+
+ u8 nvcache_module_status;
+ #define VDANVCI_NVCACHEMODULE_NOT_PRESENT 0x00
+ #define VDANVCI_NVCACHEMODULE_PRESENT 0x01
+
+ u8 protection_mode;
+ #define VDANVCI_PROTMODE_HI_PROTECT 0x00
+ #define VDANVCI_PROTMODE_HI_PERFORM 0x01
+
+ u8 reserved[109];
+};
+
+
+struct __packed atto_vda_buzzer_info {
+ u8 status;
+ #define VDABUZZI_BUZZER_OFF 0x00
+ #define VDABUZZI_BUZZER_ON 0x01
+ #define VDABUZZI_BUZZER_LAST 0x02
+
+ u8 reserved[3];
+ u32 duration;
+ #define VDABUZZI_DURATION_INDEFINITE 0xffffffff
+
+ u8 reserved2[104];
+};
+
+
+struct __packed atto_vda_adapter_info {
+ u8 version;
+ #define VDAADAPINFO_VERSION0 0x00
+ #define VDAADAPINFO_VERSION VDAADAPINFO_VERSION0
+
+ u8 reserved;
+ signed short utc_offset;
+ u32 utc_time;
+ u32 features;
+ #define VDA_ADAP_FEAT_IDENT 0x0001
+ #define VDA_ADAP_FEAT_BUZZ_ERR 0x0002
+ #define VDA_ADAP_FEAT_UTC_TIME 0x0004
+
+ u32 valid_features;
+ char active_config[33];
+ u8 temp_count;
+ u8 fan_count;
+ u8 reserved3[61];
+};
+
+
+struct __packed atto_vda_temp_info {
+ u8 temp_index;
+ u8 max_op_temp;
+ u8 min_op_temp;
+ u8 op_temp_warn;
+ u8 temperature;
+ u8 type;
+ #define VDA_TEMP_TYPE_CPU 1
+
+ u8 reserved[106];
+};
+
+
+struct __packed atto_vda_fan_info {
+ u8 fan_index;
+ u8 status;
+ #define VDA_FAN_STAT_UNKNOWN 0
+ #define VDA_FAN_STAT_NORMAL 1
+ #define VDA_FAN_STAT_FAIL 2
+
+ u16 crit_pvdafaninfothreshold;
+ u16 warn_threshold;
+ u16 speed;
+ u8 reserved[104];
+};
+
+
+/* VDA management commands */
+
+#define VDAMGT_DEV_SCAN 0x00
+#define VDAMGT_DEV_INFO 0x01
+#define VDAMGT_DEV_CLEAN 0x02
+#define VDAMGT_DEV_IDENTIFY 0x03
+#define VDAMGT_DEV_IDENTSTOP 0x04
+#define VDAMGT_DEV_PT_INFO 0x05
+#define VDAMGT_DEV_FEATURES 0x06
+#define VDAMGT_DEV_PT_FEATURES 0x07
+#define VDAMGT_DEV_HEALTH_REQ 0x08
+#define VDAMGT_DEV_METRICS 0x09
+#define VDAMGT_DEV_INFO2 0x0A
+#define VDAMGT_DEV_OPERATION 0x0B
+#define VDAMGT_DEV_INFO2_BYADDR 0x0C
+#define VDAMGT_GRP_INFO 0x10
+#define VDAMGT_GRP_CREATE 0x11
+#define VDAMGT_GRP_DELETE 0x12
+#define VDAMGT_ADD_STORAGE 0x13
+#define VDAMGT_MEMBER_ADD 0x14
+#define VDAMGT_GRP_COMMIT 0x15
+#define VDAMGT_GRP_REBUILD 0x16
+#define VDAMGT_GRP_COMMIT_INIT 0x17
+#define VDAMGT_QUICK_RAID 0x18
+#define VDAMGT_GRP_FEATURES 0x19
+#define VDAMGT_GRP_COMMIT_INIT_AUTOMAP 0x1A
+#define VDAMGT_QUICK_RAID_INIT_AUTOMAP 0x1B
+#define VDAMGT_GRP_OPERATION 0x1C
+#define VDAMGT_CFG_SAVE 0x20
+#define VDAMGT_LAST_ERROR 0x21
+#define VDAMGT_ADAP_INFO 0x22
+#define VDAMGT_ADAP_FEATURES 0x23
+#define VDAMGT_TEMP_INFO 0x24
+#define VDAMGT_FAN_INFO 0x25
+#define VDAMGT_PART_INFO 0x30
+#define VDAMGT_PART_MAP 0x31
+#define VDAMGT_PART_UNMAP 0x32
+#define VDAMGT_PART_AUTOMAP 0x33
+#define VDAMGT_PART_SPLIT 0x34
+#define VDAMGT_PART_MERGE 0x35
+#define VDAMGT_SPARE_LIST 0x40
+#define VDAMGT_SPARE_ADD 0x41
+#define VDAMGT_SPARE_REMOVE 0x42
+#define VDAMGT_LOCAL_SPARE_ADD 0x43
+#define VDAMGT_SCHEDULE_EVENT 0x50
+#define VDAMGT_SCHEDULE_INFO 0x51
+#define VDAMGT_NVCACHE_INFO 0x60
+#define VDAMGT_NVCACHE_SET 0x61
+#define VDAMGT_BUZZER_INFO 0x70
+#define VDAMGT_BUZZER_SET 0x71
+
+
+struct __packed atto_vda_ae_hdr {
+ u8 bylength;
+ u8 byflags;
+ #define VDAAE_HDRF_EVENT_ACK 0x01
+
+ u8 byversion;
+ #define VDAAE_HDR_VER_0 0
+
+ u8 bytype;
+ #define VDAAE_HDR_TYPE_RAID 1
+ #define VDAAE_HDR_TYPE_LU 2
+ #define VDAAE_HDR_TYPE_DISK 3
+ #define VDAAE_HDR_TYPE_RESET 4
+ #define VDAAE_HDR_TYPE_LOG_INFO 5
+ #define VDAAE_HDR_TYPE_LOG_WARN 6
+ #define VDAAE_HDR_TYPE_LOG_CRIT 7
+ #define VDAAE_HDR_TYPE_LOG_FAIL 8
+ #define VDAAE_HDR_TYPE_NVC 9
+ #define VDAAE_HDR_TYPE_TLG_INFO 10
+ #define VDAAE_HDR_TYPE_TLG_WARN 11
+ #define VDAAE_HDR_TYPE_TLG_CRIT 12
+ #define VDAAE_HDR_TYPE_PWRMGT 13
+ #define VDAAE_HDR_TYPE_MUTE 14
+ #define VDAAE_HDR_TYPE_DEV 15
+};
+
+
+struct __packed atto_vda_ae_raid {
+ struct atto_vda_ae_hdr hdr;
+ u32 dwflags;
+ #define VDAAE_GROUP_STATE 0x00000001
+ #define VDAAE_RBLD_STATE 0x00000002
+ #define VDAAE_RBLD_PROG 0x00000004
+ #define VDAAE_MEMBER_CHG 0x00000008
+ #define VDAAE_PART_CHG 0x00000010
+ #define VDAAE_MEM_STATE_CHG 0x00000020
+
+ u8 bygroup_state;
+ #define VDAAE_RAID_INVALID 0
+ #define VDAAE_RAID_NEW 1
+ #define VDAAE_RAID_WAITING 2
+ #define VDAAE_RAID_ONLINE 3
+ #define VDAAE_RAID_DEGRADED 4
+ #define VDAAE_RAID_OFFLINE 5
+ #define VDAAE_RAID_DELETED 6
+ #define VDAAE_RAID_BASIC 7
+ #define VDAAE_RAID_EXTREME 8
+ #define VDAAE_RAID_UNKNOWN 9
+
+ u8 byrebuild_state;
+ #define VDAAE_RBLD_NONE 0
+ #define VDAAE_RBLD_REBUILD 1
+ #define VDAAE_RBLD_ERASE 2
+ #define VDAAE_RBLD_PATTERN 3
+ #define VDAAE_RBLD_CONV 4
+ #define VDAAE_RBLD_FULL_INIT 5
+ #define VDAAE_RBLD_QUICK_INIT 6
+ #define VDAAE_RBLD_SECT_SCAN 7
+ #define VDAAE_RBLD_SECT_SCAN_PARITY 8
+ #define VDAAE_RBLD_SECT_SCAN_PARITY_FIX 9
+ #define VDAAE_RBLD_RECOV_REBUILD 10
+ #define VDAAE_RBLD_UNKNOWN 11
+
+ u8 byrebuild_progress;
+ u8 op_status;
+ #define VDAAE_GRPOPSTAT_MASK 0x0F
+ #define VDAAE_GRPOPSTAT_INVALID 0x00
+ #define VDAAE_GRPOPSTAT_OK 0x01
+ #define VDAAE_GRPOPSTAT_FAULTED 0x02
+ #define VDAAE_GRPOPSTAT_HALTED 0x03
+ #define VDAAE_GRPOPSTAT_INT 0x04
+ #define VDAAE_GRPOPPROC_MASK 0xF0
+ #define VDAAE_GRPOPPROC_STARTABLE 0x10
+ #define VDAAE_GRPOPPROC_CANCELABLE 0x20
+ #define VDAAE_GRPOPPROC_RESUMABLE 0x40
+ #define VDAAE_GRPOPPROC_HALTABLE 0x80
+ char acname[15];
+ u8 byreserved;
+ u8 byreserved2[0x80 - 0x1C];
+};
+
+
+struct __packed atto_vda_ae_lu_tgt_lun {
+ u16 wtarget_id;
+ u8 bylun;
+ u8 byreserved;
+};
+
+
+struct __packed atto_vda_ae_lu_tgt_lun_raid {
+ u16 wtarget_id;
+ u8 bylun;
+ u8 byreserved;
+ u32 dwinterleave;
+ u32 dwblock_size;
+};
+
+
+struct __packed atto_vda_ae_lu {
+ struct atto_vda_ae_hdr hdr;
+ u32 dwevent;
+ #define VDAAE_LU_DISC 0x00000001
+ #define VDAAE_LU_LOST 0x00000002
+ #define VDAAE_LU_STATE 0x00000004
+ #define VDAAE_LU_PASSTHROUGH 0x10000000
+ #define VDAAE_LU_PHYS_ID 0x20000000
+
+ u8 bystate;
+ #define VDAAE_LU_UNDEFINED 0
+ #define VDAAE_LU_NOT_PRESENT 1
+ #define VDAAE_LU_OFFLINE 2
+ #define VDAAE_LU_ONLINE 3
+ #define VDAAE_LU_DEGRADED 4
+ #define VDAAE_LU_FACTORY_DISABLED 5
+ #define VDAAE_LU_DELETED 6
+ #define VDAAE_LU_BUSSCAN 7
+ #define VDAAE_LU_UNKNOWN 8
+
+ u8 byreserved;
+ u16 wphys_target_id;
+
+ union {
+ struct atto_vda_ae_lu_tgt_lun tgtlun;
+ struct atto_vda_ae_lu_tgt_lun_raid tgtlun_raid;
+ } id;
+};
+
+
+struct __packed atto_vda_ae_disk {
+ struct atto_vda_ae_hdr hdr;
+};
+
+
+#define VDAAE_LOG_STRSZ 64
+
+struct __packed atto_vda_ae_log {
+ struct atto_vda_ae_hdr hdr;
+ char aclog_ascii[VDAAE_LOG_STRSZ];
+};
+
+
+#define VDAAE_TLG_STRSZ 56
+
+struct __packed atto_vda_ae_timestamp_log {
+ struct atto_vda_ae_hdr hdr;
+ u32 dwtimestamp;
+ char aclog_ascii[VDAAE_TLG_STRSZ];
+};
+
+
+struct __packed atto_vda_ae_nvc {
+ struct atto_vda_ae_hdr hdr;
+};
+
+
+struct __packed atto_vda_ae_dev {
+ struct atto_vda_ae_hdr hdr;
+ struct atto_dev_addr devaddr;
+};
+
+
+union atto_vda_ae {
+ struct atto_vda_ae_hdr hdr;
+ struct atto_vda_ae_disk disk;
+ struct atto_vda_ae_lu lu;
+ struct atto_vda_ae_raid raid;
+ struct atto_vda_ae_log log;
+ struct atto_vda_ae_timestamp_log tslog;
+ struct atto_vda_ae_nvc nvcache;
+ struct atto_vda_ae_dev dev;
+};
+
+
+struct __packed atto_vda_date_and_time {
+ u8 flags;
+ #define VDA_DT_DAY_MASK 0x07
+ #define VDA_DT_DAY_NONE 0x00
+ #define VDA_DT_DAY_SUN 0x01
+ #define VDA_DT_DAY_MON 0x02
+ #define VDA_DT_DAY_TUE 0x03
+ #define VDA_DT_DAY_WED 0x04
+ #define VDA_DT_DAY_THU 0x05
+ #define VDA_DT_DAY_FRI 0x06
+ #define VDA_DT_DAY_SAT 0x07
+ #define VDA_DT_PM 0x40
+ #define VDA_DT_MILITARY 0x80
+
+ u8 seconds;
+ u8 minutes;
+ u8 hours;
+ u8 day;
+ u8 month;
+ u16 year;
+};
+
+#define SGE_LEN_LIMIT 0x003FFFFF /*! mask of segment length */
+#define SGE_LEN_MAX 0x003FF000 /*! maximum segment length */
+#define SGE_LAST 0x01000000 /*! last entry */
+#define SGE_ADDR_64 0x04000000 /*! 64-bit addressing flag */
+#define SGE_CHAIN 0x80000000 /*! chain descriptor flag */
+#define SGE_CHAIN_LEN 0x0000FFFF /*! mask of length in chain entries */
+#define SGE_CHAIN_SZ 0x00FF0000 /*! mask of size of chained buffer */
+
+
+struct __packed atto_vda_cfg_init {
+ struct atto_vda_date_and_time date_time;
+ u32 sgl_page_size;
+ u32 vda_version;
+ u32 fw_version;
+ u32 fw_build;
+ u32 fw_release;
+ u32 epoch_time;
+ u32 ioctl_tunnel;
+ #define VDA_ITF_MEM_RW 0x00000001
+ #define VDA_ITF_TRACE 0x00000002
+ #define VDA_ITF_SCSI_PASS_THRU 0x00000004
+ #define VDA_ITF_GET_DEV_ADDR 0x00000008
+ #define VDA_ITF_PHY_CTRL 0x00000010
+ #define VDA_ITF_CONN_CTRL 0x00000020
+ #define VDA_ITF_GET_DEV_INFO 0x00000040
+
+ u32 num_targets_backend;
+ u8 reserved[0x48];
+};
+
+
+/* configuration commands */
+
+#define VDA_CFG_INIT 0x00
+#define VDA_CFG_GET_INIT 0x01
+#define VDA_CFG_GET_INIT2 0x02
+
+
+/*! physical region descriptor (PRD) aka scatter/gather entry */
+
+struct __packed atto_physical_region_description {
+ u64 address;
+ u32 ctl_len;
+ #define PRD_LEN_LIMIT 0x003FFFFF
+ #define PRD_LEN_MAX 0x003FF000
+ #define PRD_NXT_PRD_CNT 0x0000007F
+ #define PRD_CHAIN 0x01000000
+ #define PRD_DATA 0x00000000
+ #define PRD_INT_SEL 0xF0000000
+ #define PRD_INT_SEL_F0 0x00000000
+ #define PRD_INT_SEL_F1 0x40000000
+ #define PRD_INT_SEL_F2 0x80000000
+ #define PRD_INT_SEL_F3 0xc0000000
+ #define PRD_INT_SEL_SRAM 0x10000000
+ #define PRD_INT_SEL_PBSR 0x20000000
+
+};
+
+/* Request types. NOTE that ALL requests have the same layout for the first
+ * few bytes.
+ */
+struct __packed atto_vda_req_header {
+ u32 length;
+ u8 function;
+ u8 variable1;
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+};
+
+
+#define FCP_CDB_SIZE 16
+
+struct __packed atto_vda_scsi_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_SCSI */
+ u8 sense_len;
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+ u32 flags;
+ #define FCP_CMND_LUN_MASK 0x000000FF
+ #define FCP_CMND_TA_MASK 0x00000700
+ #define FCP_CMND_TA_SIMPL_Q 0x00000000
+ #define FCP_CMND_TA_HEAD_Q 0x00000100
+ #define FCP_CMND_TA_ORDRD_Q 0x00000200
+ #define FCP_CMND_TA_ACA 0x00000400
+ #define FCP_CMND_PRI_MASK 0x00007800
+ #define FCP_CMND_TM_MASK 0x00FF0000
+ #define FCP_CMND_ATS 0x00020000
+ #define FCP_CMND_CTS 0x00040000
+ #define FCP_CMND_LRS 0x00100000
+ #define FCP_CMND_TRS 0x00200000
+ #define FCP_CMND_CLA 0x00400000
+ #define FCP_CMND_TRM 0x00800000
+ #define FCP_CMND_DATA_DIR 0x03000000
+ #define FCP_CMND_WRD 0x01000000
+ #define FCP_CMND_RDD 0x02000000
+
+ u8 cdb[FCP_CDB_SIZE];
+ union {
+ struct __packed {
+ u64 ppsense_buf;
+ u16 target_id;
+ u8 iblk_cnt_prd;
+ u8 reserved;
+ };
+
+ struct atto_physical_region_description sense_buff_prd;
+ };
+
+ union {
+ struct atto_vda_sge sge[1];
+
+ u32 abort_handle;
+ u32 dwords[245];
+ struct atto_physical_region_description prd[1];
+ } u;
+};
+
+
+struct __packed atto_vda_flash_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_FLASH */
+ u8 sub_func;
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+ u32 flash_addr;
+ u8 checksum;
+ u8 rsvd[3];
+
+ union {
+ struct {
+ char file_name[16]; /* 8.3 fname, NULL term, wc=* */
+ struct atto_vda_sge sge[1];
+ } file;
+
+ struct atto_vda_sge sge[1];
+ struct atto_physical_region_description prde[2];
+ } data;
+};
+
+
+struct __packed atto_vda_diag_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_DIAG */
+ u8 sub_func;
+ #define VDA_DIAG_STATUS 0x00
+ #define VDA_DIAG_RESET 0x01
+ #define VDA_DIAG_PAUSE 0x02
+ #define VDA_DIAG_RESUME 0x03
+ #define VDA_DIAG_READ 0x04
+ #define VDA_DIAG_WRITE 0x05
+
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+ u32 rsvd;
+ u64 local_addr;
+ struct atto_vda_sge sge[1];
+};
+
+
+struct __packed atto_vda_ae_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_AE */
+ u8 reserved1;
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+
+ union {
+ struct atto_vda_sge sge[1];
+ struct atto_physical_region_description prde[1];
+ };
+};
+
+
+struct __packed atto_vda_cli_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_CLI */
+ u8 reserved1;
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+ u32 cmd_rsp_len;
+ struct atto_vda_sge sge[1];
+};
+
+
+struct __packed atto_vda_ioctl_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_IOCTL */
+ u8 sub_func;
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+
+ union {
+ struct atto_vda_sge reserved_sge;
+ struct atto_physical_region_description reserved_prde;
+ };
+
+ union {
+ struct {
+ u32 ctrl_code;
+ u16 target_id;
+ u8 lun;
+ u8 reserved;
+ } csmi;
+ };
+
+ union {
+ struct atto_vda_sge sge[1];
+ struct atto_physical_region_description prde[1];
+ };
+};
+
+
+struct __packed atto_vda_cfg_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_CFG */
+ u8 sub_func;
+ u8 rsvd1;
+ u8 sg_list_offset;
+ u32 handle;
+
+ union {
+ u8 bytes[116];
+ struct atto_vda_cfg_init init;
+ struct atto_vda_sge sge;
+ struct atto_physical_region_description prde;
+ } data;
+};
+
+
+struct __packed atto_vda_mgmt_req {
+ u32 length;
+ u8 function; /* VDA_FUNC_MGT */
+ u8 mgt_func;
+ u8 chain_offset;
+ u8 sg_list_offset;
+ u32 handle;
+ u8 scan_generation;
+ u8 payld_sglst_offset;
+ u16 dev_index;
+ u32 payld_length;
+ u32 pad;
+ union {
+ struct atto_vda_sge sge[2];
+ struct atto_physical_region_description prde[2];
+ };
+ struct atto_vda_sge payld_sge[1];
+};
+
+
+union atto_vda_req {
+ struct atto_vda_scsi_req scsi;
+ struct atto_vda_flash_req flash;
+ struct atto_vda_diag_req diag;
+ struct atto_vda_ae_req ae;
+ struct atto_vda_cli_req cli;
+ struct atto_vda_ioctl_req ioctl;
+ struct atto_vda_cfg_req cfg;
+ struct atto_vda_mgmt_req mgt;
+ u8 bytes[1024];
+};
+
+/* Outbound response structures */
+
+struct __packed atto_vda_scsi_rsp {
+ u8 scsi_stat;
+ u8 sense_len;
+ u8 rsvd[2];
+ u32 residual_length;
+};
+
+struct __packed atto_vda_flash_rsp {
+ u32 file_size;
+};
+
+struct __packed atto_vda_ae_rsp {
+ u32 length;
+};
+
+struct __packed atto_vda_cli_rsp {
+ u32 cmd_rsp_len;
+};
+
+struct __packed atto_vda_ioctl_rsp {
+ union {
+ struct {
+ u32 csmi_status;
+ u16 target_id;
+ u8 lun;
+ u8 reserved;
+ } csmi;
+ };
+};
+
+struct __packed atto_vda_cfg_rsp {
+ u16 vda_version;
+ u16 fw_release;
+ u32 fw_build;
+};
+
+struct __packed atto_vda_mgmt_rsp {
+ u32 length;
+ u16 dev_index;
+ u8 scan_generation;
+};
+
+union atto_vda_func_rsp {
+ struct atto_vda_scsi_rsp scsi_rsp;
+ struct atto_vda_flash_rsp flash_rsp;
+ struct atto_vda_ae_rsp ae_rsp;
+ struct atto_vda_cli_rsp cli_rsp;
+ struct atto_vda_ioctl_rsp ioctl_rsp;
+ struct atto_vda_cfg_rsp cfg_rsp;
+ struct atto_vda_mgmt_rsp mgt_rsp;
+ u32 dwords[2];
+};
+
+struct __packed atto_vda_ob_rsp {
+ u32 handle;
+ u8 req_stat;
+ u8 rsvd[3];
+
+ union atto_vda_func_rsp
+ func_rsp;
+};
+
+struct __packed atto_vda_ae_data {
+ u8 event_data[256];
+};
+
+struct __packed atto_vda_mgmt_data {
+ union {
+ u8 bytes[112];
+ struct atto_vda_devinfo dev_info;
+ struct atto_vda_grp_info grp_info;
+ struct atto_vdapart_info part_info;
+ struct atto_vda_dh_info dev_health_info;
+ struct atto_vda_metrics_info metrics_info;
+ struct atto_vda_schedule_info sched_info;
+ struct atto_vda_n_vcache_info nvcache_info;
+ struct atto_vda_buzzer_info buzzer_info;
+ } data;
+};
+
+union atto_vda_rsp_data {
+ struct atto_vda_ae_data ae_data;
+ struct atto_vda_mgmt_data mgt_data;
+ u8 sense_data[252];
+ #define SENSE_DATA_SZ 252;
+ u8 bytes[256];
+};
+
+#endif
diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
new file mode 100644
index 000000000000..c48275d53aef
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -0,0 +1,1421 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r.h
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
+
+#include "esas2r_log.h"
+#include "atioctl.h"
+#include "atvda.h"
+
+#ifndef ESAS2R_H
+#define ESAS2R_H
+
+/* Global Variables */
+extern struct esas2r_adapter *esas2r_adapters[];
+extern u8 *esas2r_buffered_ioctl;
+extern dma_addr_t esas2r_buffered_ioctl_addr;
+extern u32 esas2r_buffered_ioctl_size;
+extern struct pci_dev *esas2r_buffered_ioctl_pcid;
+#define SGL_PG_SZ_MIN 64
+#define SGL_PG_SZ_MAX 1024
+extern int sgl_page_size;
+#define NUM_SGL_MIN 8
+#define NUM_SGL_MAX 2048
+extern int num_sg_lists;
+#define NUM_REQ_MIN 4
+#define NUM_REQ_MAX 256
+extern int num_requests;
+#define NUM_AE_MIN 2
+#define NUM_AE_MAX 8
+extern int num_ae_requests;
+extern int cmd_per_lun;
+extern int can_queue;
+extern int esas2r_max_sectors;
+extern int sg_tablesize;
+extern int interrupt_mode;
+extern int num_io_requests;
+
+/* Macro defintions */
+#define ESAS2R_MAX_ID 255
+#define MAX_ADAPTERS 32
+#define ESAS2R_DRVR_NAME "esas2r"
+#define ESAS2R_LONGNAME "ATTO ExpressSAS 6GB RAID Adapter"
+#define ESAS2R_MAX_DEVICES 32
+#define ATTONODE_NAME "ATTONode"
+#define ESAS2R_MAJOR_REV 1
+#define ESAS2R_MINOR_REV 00
+#define ESAS2R_VERSION_STR DEFINED_NUM_TO_STR(ESAS2R_MAJOR_REV) "." \
+ DEFINED_NUM_TO_STR(ESAS2R_MINOR_REV)
+#define ESAS2R_COPYRIGHT_YEARS "2001-2013"
+#define ESAS2R_DEFAULT_SGL_PAGE_SIZE 384
+#define ESAS2R_DEFAULT_CMD_PER_LUN 64
+#define ESAS2R_DEFAULT_NUM_SG_LISTS 1024
+#define DEFINED_NUM_TO_STR(num) NUM_TO_STR(num)
+#define NUM_TO_STR(num) #num
+
+#define ESAS2R_SGL_ALIGN 16
+#define ESAS2R_LIST_ALIGN 16
+#define ESAS2R_LIST_EXTRA ESAS2R_NUM_EXTRA
+#define ESAS2R_DATA_BUF_LEN 256
+#define ESAS2R_DEFAULT_TMO 5000
+#define ESAS2R_DISC_BUF_LEN 512
+#define ESAS2R_FWCOREDUMP_SZ 0x80000
+#define ESAS2R_NUM_PHYS 8
+#define ESAS2R_TARG_ID_INV 0xFFFF
+#define ESAS2R_INT_STS_MASK MU_INTSTAT_MASK
+#define ESAS2R_INT_ENB_MASK MU_INTSTAT_MASK
+#define ESAS2R_INT_DIS_MASK 0
+#define ESAS2R_MAX_TARGETS 256
+#define ESAS2R_KOBJ_NAME_LEN 20
+
+/* u16 (WORD) component macros */
+#define LOBYTE(w) ((u8)(u16)(w))
+#define HIBYTE(w) ((u8)(((u16)(w)) >> 8))
+#define MAKEWORD(lo, hi) ((u16)((u8)(lo) | ((u16)(u8)(hi) << 8)))
+
+/* u32 (DWORD) component macros */
+#define LOWORD(d) ((u16)(u32)(d))
+#define HIWORD(d) ((u16)(((u32)(d)) >> 16))
+#define MAKEDWORD(lo, hi) ((u32)((u16)(lo) | ((u32)(u16)(hi) << 16)))
+
+/* macro to get the lowest nonzero bit of a value */
+#define LOBIT(x) ((x) & (0 - (x)))
+
+/* These functions are provided to access the chip's control registers.
+ * The register is specified by its byte offset from the register base
+ * for the adapter.
+ */
+#define esas2r_read_register_dword(a, reg) \
+ readl((void __iomem *)a->regs + (reg) + MW_REG_OFFSET_HWREG)
+
+#define esas2r_write_register_dword(a, reg, data) \
+ writel(data, (void __iomem *)(a->regs + (reg) + MW_REG_OFFSET_HWREG))
+
+#define esas2r_flush_register_dword(a, r) esas2r_read_register_dword(a, r)
+
+/* This function is provided to access the chip's data window. The
+ * register is specified by its byte offset from the window base
+ * for the adapter.
+ */
+#define esas2r_read_data_byte(a, reg) \
+ readb((void __iomem *)a->data_window + (reg))
+
+/* ATTO vendor and device Ids */
+#define ATTO_VENDOR_ID 0x117C
+#define ATTO_DID_INTEL_IOP348 0x002C
+#define ATTO_DID_MV_88RC9580 0x0049
+#define ATTO_DID_MV_88RC9580TS 0x0066
+#define ATTO_DID_MV_88RC9580TSE 0x0067
+#define ATTO_DID_MV_88RC9580TL 0x0068
+
+/* ATTO subsystem device Ids */
+#define ATTO_SSDID_TBT 0x4000
+#define ATTO_TSSC_3808 0x4066
+#define ATTO_TSSC_3808E 0x4067
+#define ATTO_TLSH_1068 0x4068
+#define ATTO_ESAS_R680 0x0049
+#define ATTO_ESAS_R608 0x004A
+#define ATTO_ESAS_R60F 0x004B
+#define ATTO_ESAS_R6F0 0x004C
+#define ATTO_ESAS_R644 0x004D
+#define ATTO_ESAS_R648 0x004E
+
+/*
+ * flash definitions & structures
+ * define the code types
+ */
+#define FBT_CPYR 0xAA00
+#define FBT_SETUP 0xAA02
+#define FBT_FLASH_VER 0xAA04
+
+/* offsets to various locations in flash */
+#define FLS_OFFSET_BOOT (u32)(0x00700000)
+#define FLS_OFFSET_NVR (u32)(0x007C0000)
+#define FLS_OFFSET_CPYR FLS_OFFSET_NVR
+#define FLS_LENGTH_BOOT (FLS_OFFSET_CPYR - FLS_OFFSET_BOOT)
+#define FLS_BLOCK_SIZE (u32)(0x00020000)
+#define FI_NVR_2KB 0x0800
+#define FI_NVR_8KB 0x2000
+#define FM_BUF_SZ 0x800
+
+/*
+ * marvell frey (88R9580) register definitions
+ * chip revision identifiers
+ */
+#define MVR_FREY_B2 0xB2
+
+/*
+ * memory window definitions. window 0 is the data window with definitions
+ * of MW_DATA_XXX. window 1 is the register window with definitions of
+ * MW_REG_XXX.
+ */
+#define MW_REG_WINDOW_SIZE (u32)(0x00040000)
+#define MW_REG_OFFSET_HWREG (u32)(0x00000000)
+#define MW_REG_OFFSET_PCI (u32)(0x00008000)
+#define MW_REG_PCI_HWREG_DELTA (MW_REG_OFFSET_PCI - MW_REG_OFFSET_HWREG)
+#define MW_DATA_WINDOW_SIZE (u32)(0x00020000)
+#define MW_DATA_ADDR_SER_FLASH (u32)(0xEC000000)
+#define MW_DATA_ADDR_SRAM (u32)(0xF4000000)
+#define MW_DATA_ADDR_PAR_FLASH (u32)(0xFC000000)
+
+/*
+ * the following registers are for the communication
+ * list interface (AKA message unit (MU))
+ */
+#define MU_IN_LIST_ADDR_LO (u32)(0x00004000)
+#define MU_IN_LIST_ADDR_HI (u32)(0x00004004)
+
+#define MU_IN_LIST_WRITE (u32)(0x00004018)
+ #define MU_ILW_TOGGLE (u32)(0x00004000)
+
+#define MU_IN_LIST_READ (u32)(0x0000401C)
+ #define MU_ILR_TOGGLE (u32)(0x00004000)
+ #define MU_ILIC_LIST (u32)(0x0000000F)
+ #define MU_ILIC_LIST_F0 (u32)(0x00000000)
+ #define MU_ILIC_DEST (u32)(0x00000F00)
+ #define MU_ILIC_DEST_DDR (u32)(0x00000200)
+#define MU_IN_LIST_IFC_CONFIG (u32)(0x00004028)
+
+#define MU_IN_LIST_CONFIG (u32)(0x0000402C)
+ #define MU_ILC_ENABLE (u32)(0x00000001)
+ #define MU_ILC_ENTRY_MASK (u32)(0x000000F0)
+ #define MU_ILC_ENTRY_4_DW (u32)(0x00000020)
+ #define MU_ILC_DYNAMIC_SRC (u32)(0x00008000)
+ #define MU_ILC_NUMBER_MASK (u32)(0x7FFF0000)
+ #define MU_ILC_NUMBER_SHIFT 16
+
+#define MU_OUT_LIST_ADDR_LO (u32)(0x00004050)
+#define MU_OUT_LIST_ADDR_HI (u32)(0x00004054)
+
+#define MU_OUT_LIST_COPY_PTR_LO (u32)(0x00004058)
+#define MU_OUT_LIST_COPY_PTR_HI (u32)(0x0000405C)
+
+#define MU_OUT_LIST_WRITE (u32)(0x00004068)
+ #define MU_OLW_TOGGLE (u32)(0x00004000)
+
+#define MU_OUT_LIST_COPY (u32)(0x0000406C)
+ #define MU_OLC_TOGGLE (u32)(0x00004000)
+ #define MU_OLC_WRT_PTR (u32)(0x00003FFF)
+
+#define MU_OUT_LIST_IFC_CONFIG (u32)(0x00004078)
+ #define MU_OLIC_LIST (u32)(0x0000000F)
+ #define MU_OLIC_LIST_F0 (u32)(0x00000000)
+ #define MU_OLIC_SOURCE (u32)(0x00000F00)
+ #define MU_OLIC_SOURCE_DDR (u32)(0x00000200)
+
+#define MU_OUT_LIST_CONFIG (u32)(0x0000407C)
+ #define MU_OLC_ENABLE (u32)(0x00000001)
+ #define MU_OLC_ENTRY_MASK (u32)(0x000000F0)
+ #define MU_OLC_ENTRY_4_DW (u32)(0x00000020)
+ #define MU_OLC_NUMBER_MASK (u32)(0x7FFF0000)
+ #define MU_OLC_NUMBER_SHIFT 16
+
+#define MU_OUT_LIST_INT_STAT (u32)(0x00004088)
+ #define MU_OLIS_INT (u32)(0x00000001)
+
+#define MU_OUT_LIST_INT_MASK (u32)(0x0000408C)
+ #define MU_OLIS_MASK (u32)(0x00000001)
+
+/*
+ * the maximum size of the communication lists is two greater than the
+ * maximum amount of VDA requests. the extra are to prevent queue overflow.
+ */
+#define ESAS2R_MAX_NUM_REQS 256
+#define ESAS2R_NUM_EXTRA 2
+#define ESAS2R_MAX_COMM_LIST_SIZE (ESAS2R_MAX_NUM_REQS + ESAS2R_NUM_EXTRA)
+
+/*
+ * the following registers are for the CPU interface
+ */
+#define MU_CTL_STATUS_IN (u32)(0x00010108)
+ #define MU_CTL_IN_FULL_RST (u32)(0x00000020)
+#define MU_CTL_STATUS_IN_B2 (u32)(0x00010130)
+ #define MU_CTL_IN_FULL_RST2 (u32)(0x80000000)
+#define MU_DOORBELL_IN (u32)(0x00010460)
+ #define DRBL_RESET_BUS (u32)(0x00000002)
+ #define DRBL_PAUSE_AE (u32)(0x00000004)
+ #define DRBL_RESUME_AE (u32)(0x00000008)
+ #define DRBL_MSG_IFC_DOWN (u32)(0x00000010)
+ #define DRBL_FLASH_REQ (u32)(0x00000020)
+ #define DRBL_FLASH_DONE (u32)(0x00000040)
+ #define DRBL_FORCE_INT (u32)(0x00000080)
+ #define DRBL_MSG_IFC_INIT (u32)(0x00000100)
+ #define DRBL_POWER_DOWN (u32)(0x00000200)
+ #define DRBL_DRV_VER_1 (u32)(0x00010000)
+ #define DRBL_DRV_VER DRBL_DRV_VER_1
+#define MU_DOORBELL_IN_ENB (u32)(0x00010464)
+#define MU_DOORBELL_OUT (u32)(0x00010480)
+ #define DRBL_PANIC_REASON_MASK (u32)(0x00F00000)
+ #define DRBL_UNUSED_HANDLER (u32)(0x00100000)
+ #define DRBL_UNDEF_INSTR (u32)(0x00200000)
+ #define DRBL_PREFETCH_ABORT (u32)(0x00300000)
+ #define DRBL_DATA_ABORT (u32)(0x00400000)
+ #define DRBL_JUMP_TO_ZERO (u32)(0x00500000)
+ #define DRBL_FW_RESET (u32)(0x00080000)
+ #define DRBL_FW_VER_MSK (u32)(0x00070000)
+ #define DRBL_FW_VER_0 (u32)(0x00000000)
+ #define DRBL_FW_VER_1 (u32)(0x00010000)
+ #define DRBL_FW_VER DRBL_FW_VER_1
+#define MU_DOORBELL_OUT_ENB (u32)(0x00010484)
+ #define DRBL_ENB_MASK (u32)(0x00F803FF)
+#define MU_INT_STATUS_OUT (u32)(0x00010200)
+ #define MU_INTSTAT_POST_OUT (u32)(0x00000010)
+ #define MU_INTSTAT_DRBL_IN (u32)(0x00000100)
+ #define MU_INTSTAT_DRBL (u32)(0x00001000)
+ #define MU_INTSTAT_MASK (u32)(0x00001010)
+#define MU_INT_MASK_OUT (u32)(0x0001020C)
+
+/* PCI express registers accessed via window 1 */
+#define MVR_PCI_WIN1_REMAP (u32)(0x00008438)
+ #define MVRPW1R_ENABLE (u32)(0x00000001)
+
+
+/* structures */
+
+/* inbound list dynamic source entry */
+struct esas2r_inbound_list_source_entry {
+ u64 address;
+ u32 length;
+ #define HWILSE_INTERFACE_F0 0x00000000
+ u32 reserved;
+};
+
+/* PCI data structure in expansion ROM images */
+struct __packed esas2r_boot_header {
+ char signature[4];
+ u16 vendor_id;
+ u16 device_id;
+ u16 VPD;
+ u16 struct_length;
+ u8 struct_revision;
+ u8 class_code[3];
+ u16 image_length;
+ u16 code_revision;
+ u8 code_type;
+ #define CODE_TYPE_PC 0
+ #define CODE_TYPE_OPEN 1
+ #define CODE_TYPE_EFI 3
+ u8 indicator;
+ #define INDICATOR_LAST 0x80
+ u8 reserved[2];
+};
+
+struct __packed esas2r_boot_image {
+ u16 signature;
+ u8 reserved[22];
+ u16 header_offset;
+ u16 pnp_offset;
+};
+
+struct __packed esas2r_pc_image {
+ u16 signature;
+ u8 length;
+ u8 entry_point[3];
+ u8 checksum;
+ u16 image_end;
+ u16 min_size;
+ u8 rom_flags;
+ u8 reserved[12];
+ u16 header_offset;
+ u16 pnp_offset;
+ struct esas2r_boot_header boot_image;
+};
+
+struct __packed esas2r_efi_image {
+ u16 signature;
+ u16 length;
+ u32 efi_signature;
+ #define EFI_ROM_SIG 0x00000EF1
+ u16 image_type;
+ #define EFI_IMAGE_APP 10
+ #define EFI_IMAGE_BSD 11
+ #define EFI_IMAGE_RTD 12
+ u16 machine_type;
+ #define EFI_MACHINE_IA32 0x014c
+ #define EFI_MACHINE_IA64 0x0200
+ #define EFI_MACHINE_X64 0x8664
+ #define EFI_MACHINE_EBC 0x0EBC
+ u16 compression;
+ #define EFI_UNCOMPRESSED 0x0000
+ #define EFI_COMPRESSED 0x0001
+ u8 reserved[8];
+ u16 efi_offset;
+ u16 header_offset;
+ u16 reserved2;
+ struct esas2r_boot_header boot_image;
+};
+
+struct esas2r_adapter;
+struct esas2r_sg_context;
+struct esas2r_request;
+
+typedef void (*RQCALLBK) (struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+typedef bool (*RQBUILDSGL) (struct esas2r_adapter *a,
+ struct esas2r_sg_context *sgc);
+
+struct esas2r_component_header {
+ u8 img_type;
+ #define CH_IT_FW 0x00
+ #define CH_IT_NVR 0x01
+ #define CH_IT_BIOS 0x02
+ #define CH_IT_MAC 0x03
+ #define CH_IT_CFG 0x04
+ #define CH_IT_EFI 0x05
+ u8 status;
+ #define CH_STAT_PENDING 0xff
+ #define CH_STAT_FAILED 0x00
+ #define CH_STAT_SUCCESS 0x01
+ #define CH_STAT_RETRY 0x02
+ #define CH_STAT_INVALID 0x03
+ u8 pad[2];
+ u32 version;
+ u32 length;
+ u32 image_offset;
+};
+
+#define FI_REL_VER_SZ 16
+
+struct esas2r_flash_img_v0 {
+ u8 fi_version;
+ #define FI_VERSION_0 00
+ u8 status;
+ u8 adap_typ;
+ u8 action;
+ u32 length;
+ u16 checksum;
+ u16 driver_error;
+ u16 flags;
+ u16 num_comps;
+ #define FI_NUM_COMPS_V0 5
+ u8 rel_version[FI_REL_VER_SZ];
+ struct esas2r_component_header cmp_hdr[FI_NUM_COMPS_V0];
+ u8 scratch_buf[FM_BUF_SZ];
+};
+
+struct esas2r_flash_img {
+ u8 fi_version;
+ #define FI_VERSION_1 01
+ u8 status;
+ #define FI_STAT_SUCCESS 0x00
+ #define FI_STAT_FAILED 0x01
+ #define FI_STAT_REBOOT 0x02
+ #define FI_STAT_ADAPTYP 0x03
+ #define FI_STAT_INVALID 0x04
+ #define FI_STAT_CHKSUM 0x05
+ #define FI_STAT_LENGTH 0x06
+ #define FI_STAT_UNKNOWN 0x07
+ #define FI_STAT_IMG_VER 0x08
+ #define FI_STAT_BUSY 0x09
+ #define FI_STAT_DUAL 0x0A
+ #define FI_STAT_MISSING 0x0B
+ #define FI_STAT_UNSUPP 0x0C
+ #define FI_STAT_ERASE 0x0D
+ #define FI_STAT_FLASH 0x0E
+ #define FI_STAT_DEGRADED 0x0F
+ u8 adap_typ;
+ #define FI_AT_UNKNWN 0xFF
+ #define FI_AT_SUN_LAKE 0x0B
+ #define FI_AT_MV_9580 0x0F
+ u8 action;
+ #define FI_ACT_DOWN 0x00
+ #define FI_ACT_UP 0x01
+ #define FI_ACT_UPSZ 0x02
+ #define FI_ACT_MAX 0x02
+ #define FI_ACT_DOWN1 0x80
+ u32 length;
+ u16 checksum;
+ u16 driver_error;
+ u16 flags;
+ #define FI_FLG_NVR_DEF 0x0001
+ u16 num_comps;
+ #define FI_NUM_COMPS_V1 6
+ u8 rel_version[FI_REL_VER_SZ];
+ struct esas2r_component_header cmp_hdr[FI_NUM_COMPS_V1];
+ u8 scratch_buf[FM_BUF_SZ];
+};
+
+/* definitions for flash script (FS) commands */
+struct esas2r_ioctlfs_command {
+ u8 command;
+ #define ESAS2R_FS_CMD_ERASE 0
+ #define ESAS2R_FS_CMD_READ 1
+ #define ESAS2R_FS_CMD_BEGINW 2
+ #define ESAS2R_FS_CMD_WRITE 3
+ #define ESAS2R_FS_CMD_COMMIT 4
+ #define ESAS2R_FS_CMD_CANCEL 5
+ u8 checksum;
+ u8 reserved[2];
+ u32 flash_addr;
+ u32 length;
+ u32 image_offset;
+};
+
+struct esas2r_ioctl_fs {
+ u8 version;
+ #define ESAS2R_FS_VER 0
+ u8 status;
+ u8 driver_error;
+ u8 adap_type;
+ #define ESAS2R_FS_AT_ESASRAID2 3
+ #define ESAS2R_FS_AT_TSSASRAID2 4
+ #define ESAS2R_FS_AT_TSSASRAID2E 5
+ #define ESAS2R_FS_AT_TLSASHBA 6
+ u8 driver_ver;
+ u8 reserved[11];
+ struct esas2r_ioctlfs_command command;
+ u8 data[1];
+};
+
+struct esas2r_sas_nvram {
+ u8 signature[4];
+ u8 version;
+ #define SASNVR_VERSION_0 0x00
+ #define SASNVR_VERSION SASNVR_VERSION_0
+ u8 checksum;
+ #define SASNVR_CKSUM_SEED 0x5A
+ u8 max_lun_for_target;
+ u8 pci_latency;
+ #define SASNVR_PCILAT_DIS 0x00
+ #define SASNVR_PCILAT_MIN 0x10
+ #define SASNVR_PCILAT_MAX 0xF8
+ u8 options1;
+ #define SASNVR1_BOOT_DRVR 0x01
+ #define SASNVR1_BOOT_SCAN 0x02
+ #define SASNVR1_DIS_PCI_MWI 0x04
+ #define SASNVR1_FORCE_ORD_Q 0x08
+ #define SASNVR1_CACHELINE_0 0x10
+ #define SASNVR1_DIS_DEVSORT 0x20
+ #define SASNVR1_PWR_MGT_EN 0x40
+ #define SASNVR1_WIDEPORT 0x80
+ u8 options2;
+ #define SASNVR2_SINGLE_BUS 0x01
+ #define SASNVR2_SLOT_BIND 0x02
+ #define SASNVR2_EXP_PROG 0x04
+ #define SASNVR2_CMDTHR_LUN 0x08
+ #define SASNVR2_HEARTBEAT 0x10
+ #define SASNVR2_INT_CONNECT 0x20
+ #define SASNVR2_SW_MUX_CTRL 0x40
+ #define SASNVR2_DISABLE_NCQ 0x80
+ u8 int_coalescing;
+ #define SASNVR_COAL_DIS 0x00
+ #define SASNVR_COAL_LOW 0x01
+ #define SASNVR_COAL_MED 0x02
+ #define SASNVR_COAL_HI 0x03
+ u8 cmd_throttle;
+ #define SASNVR_CMDTHR_NONE 0x00
+ u8 dev_wait_time;
+ u8 dev_wait_count;
+ u8 spin_up_delay;
+ #define SASNVR_SPINUP_MAX 0x14
+ u8 ssp_align_rate;
+ u8 sas_addr[8];
+ u8 phy_speed[16];
+ #define SASNVR_SPEED_AUTO 0x00
+ #define SASNVR_SPEED_1_5GB 0x01
+ #define SASNVR_SPEED_3GB 0x02
+ #define SASNVR_SPEED_6GB 0x03
+ #define SASNVR_SPEED_12GB 0x04
+ u8 phy_mux[16];
+ #define SASNVR_MUX_DISABLED 0x00
+ #define SASNVR_MUX_1_5GB 0x01
+ #define SASNVR_MUX_3GB 0x02
+ #define SASNVR_MUX_6GB 0x03
+ u8 phy_flags[16];
+ #define SASNVR_PHF_DISABLED 0x01
+ #define SASNVR_PHF_RD_ONLY 0x02
+ u8 sort_type;
+ #define SASNVR_SORT_SAS_ADDR 0x00
+ #define SASNVR_SORT_H308_CONN 0x01
+ #define SASNVR_SORT_PHY_ID 0x02
+ #define SASNVR_SORT_SLOT_ID 0x03
+ u8 dpm_reqcmd_lmt;
+ u8 dpm_stndby_time;
+ u8 dpm_active_time;
+ u8 phy_target_id[16];
+ #define SASNVR_PTI_DISABLED 0xFF
+ u8 virt_ses_mode;
+ #define SASNVR_VSMH_DISABLED 0x00
+ u8 read_write_mode;
+ #define SASNVR_RWM_DEFAULT 0x00
+ u8 link_down_to;
+ u8 reserved[0xA1];
+};
+
+typedef u32 (*PGETPHYSADDR) (struct esas2r_sg_context *sgc, u64 *addr);
+
+struct esas2r_sg_context {
+ struct esas2r_adapter *adapter;
+ struct esas2r_request *first_req;
+ u32 length;
+ u8 *cur_offset;
+ PGETPHYSADDR get_phys_addr;
+ union {
+ struct {
+ struct atto_vda_sge *curr;
+ struct atto_vda_sge *last;
+ struct atto_vda_sge *limit;
+ struct atto_vda_sge *chain;
+ } a64;
+ struct {
+ struct atto_physical_region_description *curr;
+ struct atto_physical_region_description *chain;
+ u32 sgl_max_cnt;
+ u32 sge_cnt;
+ } prd;
+ } sge;
+ struct scatterlist *cur_sgel;
+ u8 *exp_offset;
+ int num_sgel;
+ int sgel_count;
+};
+
+struct esas2r_target {
+ u8 flags;
+ #define TF_PASS_THRU 0x01
+ #define TF_USED 0x02
+ u8 new_target_state;
+ u8 target_state;
+ u8 buffered_target_state;
+#define TS_NOT_PRESENT 0x00
+#define TS_PRESENT 0x05
+#define TS_LUN_CHANGE 0x06
+#define TS_INVALID 0xFF
+ u32 block_size;
+ u32 inter_block;
+ u32 inter_byte;
+ u16 virt_targ_id;
+ u16 phys_targ_id;
+ u8 identifier_len;
+ u64 sas_addr;
+ u8 identifier[60];
+ struct atto_vda_ae_lu lu_event;
+};
+
+struct esas2r_request {
+ struct list_head comp_list;
+ struct list_head req_list;
+ union atto_vda_req *vrq;
+ struct esas2r_mem_desc *vrq_md;
+ union {
+ void *data_buf;
+ union atto_vda_rsp_data *vda_rsp_data;
+ };
+ u8 *sense_buf;
+ struct list_head sg_table_head;
+ struct esas2r_mem_desc *sg_table;
+ u32 timeout;
+ #define RQ_TIMEOUT_S1 0xFFFFFFFF
+ #define RQ_TIMEOUT_S2 0xFFFFFFFE
+ #define RQ_MAX_TIMEOUT 0xFFFFFFFD
+ u16 target_id;
+ u8 req_type;
+ #define RT_INI_REQ 0x01
+ #define RT_DISC_REQ 0x02
+ u8 sense_len;
+ union atto_vda_func_rsp func_rsp;
+ RQCALLBK comp_cb;
+ RQCALLBK interrupt_cb;
+ void *interrupt_cx;
+ u8 flags;
+ #define RF_1ST_IBLK_BASE 0x04
+ #define RF_FAILURE_OK 0x08
+ u8 req_stat;
+ u16 vda_req_sz;
+ #define RQ_SIZE_DEFAULT 0
+ u64 lba;
+ RQCALLBK aux_req_cb;
+ void *aux_req_cx;
+ u32 blk_len;
+ u32 max_blk_len;
+ union {
+ struct scsi_cmnd *cmd;
+ u8 *task_management_status_ptr;
+ };
+};
+
+struct esas2r_flash_context {
+ struct esas2r_flash_img *fi;
+ RQCALLBK interrupt_cb;
+ u8 *sgc_offset;
+ u8 *scratch;
+ u32 fi_hdr_len;
+ u8 task;
+ #define FMTSK_ERASE_BOOT 0
+ #define FMTSK_WRTBIOS 1
+ #define FMTSK_READBIOS 2
+ #define FMTSK_WRTMAC 3
+ #define FMTSK_READMAC 4
+ #define FMTSK_WRTEFI 5
+ #define FMTSK_READEFI 6
+ #define FMTSK_WRTCFG 7
+ #define FMTSK_READCFG 8
+ u8 func;
+ u16 num_comps;
+ u32 cmp_len;
+ u32 flsh_addr;
+ u32 curr_len;
+ u8 comp_typ;
+ struct esas2r_sg_context sgc;
+};
+
+struct esas2r_disc_context {
+ u8 disc_evt;
+ #define DCDE_DEV_CHANGE 0x01
+ #define DCDE_DEV_SCAN 0x02
+ u8 state;
+ #define DCS_DEV_RMV 0x00
+ #define DCS_DEV_ADD 0x01
+ #define DCS_BLOCK_DEV_SCAN 0x02
+ #define DCS_RAID_GRP_INFO 0x03
+ #define DCS_PART_INFO 0x04
+ #define DCS_PT_DEV_INFO 0x05
+ #define DCS_PT_DEV_ADDR 0x06
+ #define DCS_DISC_DONE 0xFF
+ u16 flags;
+ #define DCF_DEV_CHANGE 0x0001
+ #define DCF_DEV_SCAN 0x0002
+ #define DCF_POLLED 0x8000
+ u32 interleave;
+ u32 block_size;
+ u16 dev_ix;
+ u8 part_num;
+ u8 raid_grp_ix;
+ char raid_grp_name[16];
+ struct esas2r_target *curr_targ;
+ u16 curr_virt_id;
+ u16 curr_phys_id;
+ u8 scan_gen;
+ u8 dev_addr_type;
+ u64 sas_addr;
+};
+
+struct esas2r_mem_desc {
+ struct list_head next_desc;
+ void *virt_addr;
+ u64 phys_addr;
+ void *pad;
+ void *esas2r_data;
+ u32 esas2r_param;
+ u32 size;
+};
+
+enum fw_event_type {
+ fw_event_null,
+ fw_event_lun_change,
+ fw_event_present,
+ fw_event_not_present,
+ fw_event_vda_ae
+};
+
+struct esas2r_vda_ae {
+ u32 signature;
+#define ESAS2R_VDA_EVENT_SIG 0x4154544F
+ u8 bus_number;
+ u8 devfn;
+ u8 pad[2];
+ union atto_vda_ae vda_ae;
+};
+
+struct esas2r_fw_event_work {
+ struct list_head list;
+ struct delayed_work work;
+ struct esas2r_adapter *a;
+ enum fw_event_type type;
+ u8 data[sizeof(struct esas2r_vda_ae)];
+};
+
+enum state {
+ FW_INVALID_ST,
+ FW_STATUS_ST,
+ FW_COMMAND_ST
+};
+
+struct esas2r_firmware {
+ enum state state;
+ struct esas2r_flash_img header;
+ u8 *data;
+ u64 phys;
+ int orig_len;
+ void *header_buff;
+ u64 header_buff_phys;
+};
+
+struct esas2r_adapter {
+ struct esas2r_target targetdb[ESAS2R_MAX_TARGETS];
+ struct esas2r_target *targetdb_end;
+ unsigned char *regs;
+ unsigned char *data_window;
+ long flags;
+ #define AF_PORT_CHANGE 0
+ #define AF_CHPRST_NEEDED 1
+ #define AF_CHPRST_PENDING 2
+ #define AF_CHPRST_DETECTED 3
+ #define AF_BUSRST_NEEDED 4
+ #define AF_BUSRST_PENDING 5
+ #define AF_BUSRST_DETECTED 6
+ #define AF_DISABLED 7
+ #define AF_FLASH_LOCK 8
+ #define AF_OS_RESET 9
+ #define AF_FLASHING 10
+ #define AF_POWER_MGT 11
+ #define AF_NVR_VALID 12
+ #define AF_DEGRADED_MODE 13
+ #define AF_DISC_PENDING 14
+ #define AF_TASKLET_SCHEDULED 15
+ #define AF_HEARTBEAT 16
+ #define AF_HEARTBEAT_ENB 17
+ #define AF_NOT_PRESENT 18
+ #define AF_CHPRST_STARTED 19
+ #define AF_FIRST_INIT 20
+ #define AF_POWER_DOWN 21
+ #define AF_DISC_IN_PROG 22
+ #define AF_COMM_LIST_TOGGLE 23
+ #define AF_LEGACY_SGE_MODE 24
+ #define AF_DISC_POLLED 25
+ long flags2;
+ #define AF2_SERIAL_FLASH 0
+ #define AF2_DEV_SCAN 1
+ #define AF2_DEV_CNT_OK 2
+ #define AF2_COREDUMP_AVAIL 3
+ #define AF2_COREDUMP_SAVED 4
+ #define AF2_VDA_POWER_DOWN 5
+ #define AF2_THUNDERLINK 6
+ #define AF2_THUNDERBOLT 7
+ #define AF2_INIT_DONE 8
+ #define AF2_INT_PENDING 9
+ #define AF2_TIMER_TICK 10
+ #define AF2_IRQ_CLAIMED 11
+ #define AF2_MSI_ENABLED 12
+ atomic_t disable_cnt;
+ atomic_t dis_ints_cnt;
+ u32 int_stat;
+ u32 int_mask;
+ u32 volatile *outbound_copy;
+ struct list_head avail_request;
+ spinlock_t request_lock;
+ spinlock_t sg_list_lock;
+ spinlock_t queue_lock;
+ spinlock_t mem_lock;
+ struct list_head free_sg_list_head;
+ struct esas2r_mem_desc *sg_list_mds;
+ struct list_head active_list;
+ struct list_head defer_list;
+ struct esas2r_request **req_table;
+ union {
+ u16 prev_dev_cnt;
+ u32 heartbeat_time;
+ #define ESAS2R_HEARTBEAT_TIME (3000)
+ };
+ u32 chip_uptime;
+ #define ESAS2R_CHP_UPTIME_MAX (60000)
+ #define ESAS2R_CHP_UPTIME_CNT (20000)
+ u64 uncached_phys;
+ u8 *uncached;
+ struct esas2r_sas_nvram *nvram;
+ struct esas2r_request general_req;
+ u8 init_msg;
+ #define ESAS2R_INIT_MSG_START 1
+ #define ESAS2R_INIT_MSG_INIT 2
+ #define ESAS2R_INIT_MSG_GET_INIT 3
+ #define ESAS2R_INIT_MSG_REINIT 4
+ u16 cmd_ref_no;
+ u32 fw_version;
+ u32 fw_build;
+ u32 chip_init_time;
+ #define ESAS2R_CHPRST_TIME (180000)
+ #define ESAS2R_CHPRST_WAIT_TIME (2000)
+ u32 last_tick_time;
+ u32 window_base;
+ RQBUILDSGL build_sgl;
+ struct esas2r_request *first_ae_req;
+ u32 list_size;
+ u32 last_write;
+ u32 last_read;
+ u16 max_vdareq_size;
+ u16 disc_wait_cnt;
+ struct esas2r_mem_desc inbound_list_md;
+ struct esas2r_mem_desc outbound_list_md;
+ struct esas2r_disc_context disc_ctx;
+ u8 *disc_buffer;
+ u32 disc_start_time;
+ u32 disc_wait_time;
+ u32 flash_ver;
+ char flash_rev[16];
+ char fw_rev[16];
+ char image_type[16];
+ struct esas2r_flash_context flash_context;
+ u32 num_targets_backend;
+ u32 ioctl_tunnel;
+ struct tasklet_struct tasklet;
+ struct pci_dev *pcid;
+ struct Scsi_Host *host;
+ unsigned int index;
+ char name[32];
+ struct timer_list timer;
+ struct esas2r_firmware firmware;
+ wait_queue_head_t nvram_waiter;
+ int nvram_command_done;
+ wait_queue_head_t fm_api_waiter;
+ int fm_api_command_done;
+ wait_queue_head_t vda_waiter;
+ int vda_command_done;
+ u8 *vda_buffer;
+ u64 ppvda_buffer;
+#define VDA_BUFFER_HEADER_SZ (offsetof(struct atto_ioctl_vda, data))
+#define VDA_MAX_BUFFER_SIZE (0x40000 + VDA_BUFFER_HEADER_SZ)
+ wait_queue_head_t fs_api_waiter;
+ int fs_api_command_done;
+ u64 ppfs_api_buffer;
+ u8 *fs_api_buffer;
+ u32 fs_api_buffer_size;
+ wait_queue_head_t buffered_ioctl_waiter;
+ int buffered_ioctl_done;
+ int uncached_size;
+ struct workqueue_struct *fw_event_q;
+ struct list_head fw_event_list;
+ spinlock_t fw_event_lock;
+ u8 fw_events_off; /* if '1', then ignore events */
+ /*
+ * intr_mode stores the interrupt mode currently being used by this
+ * adapter. it is based on the interrupt_mode module parameter, but
+ * can be changed based on the ability (or not) to utilize the
+ * mode requested by the parameter.
+ */
+ int intr_mode;
+#define INTR_MODE_LEGACY 0
+#define INTR_MODE_MSI 1
+#define INTR_MODE_MSIX 2
+ struct esas2r_sg_context fm_api_sgc;
+ u8 *save_offset;
+ struct list_head vrq_mds_head;
+ struct esas2r_mem_desc *vrq_mds;
+ int num_vrqs;
+ struct mutex fm_api_mutex;
+ struct mutex fs_api_mutex;
+ struct semaphore nvram_semaphore;
+ struct atto_ioctl *local_atto_ioctl;
+ u8 fw_coredump_buff[ESAS2R_FWCOREDUMP_SZ];
+ unsigned int sysfs_fw_created:1;
+ unsigned int sysfs_fs_created:1;
+ unsigned int sysfs_vda_created:1;
+ unsigned int sysfs_hw_created:1;
+ unsigned int sysfs_live_nvram_created:1;
+ unsigned int sysfs_default_nvram_created:1;
+};
+
+/*
+ * Function Declarations
+ * SCSI functions
+ */
+const char *esas2r_info(struct Scsi_Host *);
+int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
+ struct esas2r_sas_nvram *data);
+int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg);
+int esas2r_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg);
+u8 handle_hba_ioctl(struct esas2r_adapter *a,
+ struct atto_ioctl *ioctl_hba);
+int esas2r_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd);
+int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh);
+long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
+
+/* SCSI error handler (eh) functions */
+int esas2r_eh_abort(struct scsi_cmnd *cmd);
+int esas2r_device_reset(struct scsi_cmnd *cmd);
+int esas2r_host_reset(struct scsi_cmnd *cmd);
+int esas2r_bus_reset(struct scsi_cmnd *cmd);
+int esas2r_target_reset(struct scsi_cmnd *cmd);
+
+/* Internal functions */
+int esas2r_init_adapter(struct Scsi_Host *host, struct pci_dev *pcid,
+ int index);
+int esas2r_read_fw(struct esas2r_adapter *a, char *buf, long off, int count);
+int esas2r_write_fw(struct esas2r_adapter *a, const char *buf, long off,
+ int count);
+int esas2r_read_vda(struct esas2r_adapter *a, char *buf, long off, int count);
+int esas2r_write_vda(struct esas2r_adapter *a, const char *buf, long off,
+ int count);
+int esas2r_read_fs(struct esas2r_adapter *a, char *buf, long off, int count);
+int esas2r_write_fs(struct esas2r_adapter *a, const char *buf, long off,
+ int count);
+void esas2r_adapter_tasklet(unsigned long context);
+irqreturn_t esas2r_interrupt(int irq, void *dev_id);
+irqreturn_t esas2r_msi_interrupt(int irq, void *dev_id);
+void esas2r_kickoff_timer(struct esas2r_adapter *a);
+
+extern const struct dev_pm_ops esas2r_pm_ops;
+
+void esas2r_fw_event_off(struct esas2r_adapter *a);
+void esas2r_fw_event_on(struct esas2r_adapter *a);
+bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq,
+ struct esas2r_sas_nvram *nvram);
+void esas2r_nvram_get_defaults(struct esas2r_adapter *a,
+ struct esas2r_sas_nvram *nvram);
+void esas2r_complete_request_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+void esas2r_reset_detected(struct esas2r_adapter *a);
+void esas2r_target_state_changed(struct esas2r_adapter *ha, u16 targ_id,
+ u8 state);
+int esas2r_req_status_to_error(u8 req_stat);
+void esas2r_kill_adapter(int i);
+void esas2r_free_request(struct esas2r_adapter *a, struct esas2r_request *rq);
+struct esas2r_request *esas2r_alloc_request(struct esas2r_adapter *a);
+u32 esas2r_get_uncached_size(struct esas2r_adapter *a);
+bool esas2r_init_adapter_struct(struct esas2r_adapter *a,
+ void **uncached_area);
+bool esas2r_check_adapter(struct esas2r_adapter *a);
+bool esas2r_init_adapter_hw(struct esas2r_adapter *a, bool init_poll);
+void esas2r_start_request(struct esas2r_adapter *a, struct esas2r_request *rq);
+bool esas2r_send_task_mgmt(struct esas2r_adapter *a,
+ struct esas2r_request *rqaux, u8 task_mgt_func);
+void esas2r_do_tasklet_tasks(struct esas2r_adapter *a);
+void esas2r_adapter_interrupt(struct esas2r_adapter *a);
+void esas2r_do_deferred_processes(struct esas2r_adapter *a);
+void esas2r_reset_bus(struct esas2r_adapter *a);
+void esas2r_reset_adapter(struct esas2r_adapter *a);
+void esas2r_timer_tick(struct esas2r_adapter *a);
+const char *esas2r_get_model_name(struct esas2r_adapter *a);
+const char *esas2r_get_model_name_short(struct esas2r_adapter *a);
+u32 esas2r_stall_execution(struct esas2r_adapter *a, u32 start_time,
+ u32 *delay);
+void esas2r_build_flash_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u8 sub_func,
+ u8 cksum,
+ u32 addr,
+ u32 length);
+void esas2r_build_mgt_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u8 sub_func,
+ u8 scan_gen,
+ u16 dev_index,
+ u32 length,
+ void *data);
+void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq);
+void esas2r_build_ioctl_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u32 length,
+ u8 sub_func);
+void esas2r_build_cfg_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u8 sub_func,
+ u32 length,
+ void *data);
+void esas2r_power_down(struct esas2r_adapter *a);
+bool esas2r_power_up(struct esas2r_adapter *a, bool init_poll);
+void esas2r_wait_request(struct esas2r_adapter *a, struct esas2r_request *rq);
+u32 esas2r_map_data_window(struct esas2r_adapter *a, u32 addr_lo);
+bool esas2r_process_fs_ioctl(struct esas2r_adapter *a,
+ struct esas2r_ioctl_fs *fs,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc);
+bool esas2r_read_flash_block(struct esas2r_adapter *a, void *to, u32 from,
+ u32 size);
+bool esas2r_read_mem_block(struct esas2r_adapter *a, void *to, u32 from,
+ u32 size);
+bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi,
+ struct esas2r_request *rq, struct esas2r_sg_context *sgc);
+void esas2r_force_interrupt(struct esas2r_adapter *a);
+void esas2r_local_start_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+void esas2r_process_adapter_reset(struct esas2r_adapter *a);
+void esas2r_complete_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+void esas2r_dummy_complete(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+void esas2r_ae_complete(struct esas2r_adapter *a, struct esas2r_request *rq);
+void esas2r_start_vda_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+bool esas2r_read_flash_rev(struct esas2r_adapter *a);
+bool esas2r_read_image_type(struct esas2r_adapter *a);
+bool esas2r_nvram_read_direct(struct esas2r_adapter *a);
+bool esas2r_nvram_validate(struct esas2r_adapter *a);
+void esas2r_nvram_set_defaults(struct esas2r_adapter *a);
+bool esas2r_print_flash_rev(struct esas2r_adapter *a);
+void esas2r_send_reset_ae(struct esas2r_adapter *a, bool pwr_mgt);
+bool esas2r_init_msgs(struct esas2r_adapter *a);
+bool esas2r_is_adapter_present(struct esas2r_adapter *a);
+void esas2r_nuxi_mgt_data(u8 function, void *data);
+void esas2r_nuxi_cfg_data(u8 function, void *data);
+void esas2r_nuxi_ae_data(union atto_vda_ae *ae);
+void esas2r_reset_chip(struct esas2r_adapter *a);
+void esas2r_log_request_failure(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+void esas2r_polled_interrupt(struct esas2r_adapter *a);
+bool esas2r_ioreq_aborted(struct esas2r_adapter *a, struct esas2r_request *rq,
+ u8 status);
+bool esas2r_build_sg_list_sge(struct esas2r_adapter *a,
+ struct esas2r_sg_context *sgc);
+bool esas2r_build_sg_list_prd(struct esas2r_adapter *a,
+ struct esas2r_sg_context *sgc);
+void esas2r_targ_db_initialize(struct esas2r_adapter *a);
+void esas2r_targ_db_remove_all(struct esas2r_adapter *a, bool notify);
+void esas2r_targ_db_report_changes(struct esas2r_adapter *a);
+struct esas2r_target *esas2r_targ_db_add_raid(struct esas2r_adapter *a,
+ struct esas2r_disc_context *dc);
+struct esas2r_target *esas2r_targ_db_add_pthru(struct esas2r_adapter *a,
+ struct esas2r_disc_context *dc,
+ u8 *ident,
+ u8 ident_len);
+void esas2r_targ_db_remove(struct esas2r_adapter *a, struct esas2r_target *t);
+struct esas2r_target *esas2r_targ_db_find_by_sas_addr(struct esas2r_adapter *a,
+ u64 *sas_addr);
+struct esas2r_target *esas2r_targ_db_find_by_ident(struct esas2r_adapter *a,
+ void *identifier,
+ u8 ident_len);
+u16 esas2r_targ_db_find_next_present(struct esas2r_adapter *a, u16 target_id);
+struct esas2r_target *esas2r_targ_db_find_by_virt_id(struct esas2r_adapter *a,
+ u16 virt_id);
+u16 esas2r_targ_db_get_tgt_cnt(struct esas2r_adapter *a);
+void esas2r_disc_initialize(struct esas2r_adapter *a);
+void esas2r_disc_start_waiting(struct esas2r_adapter *a);
+void esas2r_disc_check_for_work(struct esas2r_adapter *a);
+void esas2r_disc_check_complete(struct esas2r_adapter *a);
+void esas2r_disc_queue_event(struct esas2r_adapter *a, u8 disc_evt);
+bool esas2r_disc_start_port(struct esas2r_adapter *a);
+void esas2r_disc_local_start_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+bool esas2r_set_degraded_mode(struct esas2r_adapter *a, char *error_str);
+bool esas2r_process_vda_ioctl(struct esas2r_adapter *a,
+ struct atto_ioctl_vda *vi,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc);
+void esas2r_queue_fw_event(struct esas2r_adapter *a,
+ enum fw_event_type type,
+ void *data,
+ int data_sz);
+
+/* Inline functions */
+
+/* Allocate a chip scatter/gather list entry */
+static inline struct esas2r_mem_desc *esas2r_alloc_sgl(struct esas2r_adapter *a)
+{
+ unsigned long flags;
+ struct list_head *sgl;
+ struct esas2r_mem_desc *result = NULL;
+
+ spin_lock_irqsave(&a->sg_list_lock, flags);
+ if (likely(!list_empty(&a->free_sg_list_head))) {
+ sgl = a->free_sg_list_head.next;
+ result = list_entry(sgl, struct esas2r_mem_desc, next_desc);
+ list_del_init(sgl);
+ }
+ spin_unlock_irqrestore(&a->sg_list_lock, flags);
+
+ return result;
+}
+
+/* Initialize a scatter/gather context */
+static inline void esas2r_sgc_init(struct esas2r_sg_context *sgc,
+ struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ struct atto_vda_sge *first)
+{
+ sgc->adapter = a;
+ sgc->first_req = rq;
+
+ /*
+ * set the limit pointer such that an SGE pointer above this value
+ * would be the first one to overflow the SGL.
+ */
+ sgc->sge.a64.limit = (struct atto_vda_sge *)((u8 *)rq->vrq
+ + (sizeof(union
+ atto_vda_req) /
+ 8)
+ - sizeof(struct
+ atto_vda_sge));
+ if (first) {
+ sgc->sge.a64.last =
+ sgc->sge.a64.curr = first;
+ rq->vrq->scsi.sg_list_offset = (u8)
+ ((u8 *)first -
+ (u8 *)rq->vrq);
+ } else {
+ sgc->sge.a64.last =
+ sgc->sge.a64.curr = &rq->vrq->scsi.u.sge[0];
+ rq->vrq->scsi.sg_list_offset =
+ (u8)offsetof(struct atto_vda_scsi_req, u.sge);
+ }
+ sgc->sge.a64.chain = NULL;
+}
+
+static inline void esas2r_rq_init_request(struct esas2r_request *rq,
+ struct esas2r_adapter *a)
+{
+ union atto_vda_req *vrq = rq->vrq;
+
+ INIT_LIST_HEAD(&rq->sg_table_head);
+ rq->data_buf = (void *)(vrq + 1);
+ rq->interrupt_cb = NULL;
+ rq->comp_cb = esas2r_complete_request_cb;
+ rq->flags = 0;
+ rq->timeout = 0;
+ rq->req_stat = RS_PENDING;
+ rq->req_type = RT_INI_REQ;
+
+ /* clear the outbound response */
+ rq->func_rsp.dwords[0] = 0;
+ rq->func_rsp.dwords[1] = 0;
+
+ /*
+ * clear the size of the VDA request. esas2r_build_sg_list() will
+ * only allow the size of the request to grow. there are some
+ * management requests that go through there twice and the second
+ * time through sets a smaller request size. if this is not modified
+ * at all we'll set it to the size of the entire VDA request.
+ */
+ rq->vda_req_sz = RQ_SIZE_DEFAULT;
+
+ /* req_table entry should be NULL at this point - if not, halt */
+
+ if (a->req_table[LOWORD(vrq->scsi.handle)]) {
+ esas2r_bugon();
+ }
+
+ /* fill in the table for this handle so we can get back to the
+ * request.
+ */
+ a->req_table[LOWORD(vrq->scsi.handle)] = rq;
+
+ /*
+ * add a reference number to the handle to make it unique (until it
+ * wraps of course) while preserving the least significant word
+ */
+ vrq->scsi.handle = (a->cmd_ref_no++ << 16) | (u16)vrq->scsi.handle;
+
+ /*
+ * the following formats a SCSI request. the caller can override as
+ * necessary. clear_vda_request can be called to clear the VDA
+ * request for another type of request.
+ */
+ vrq->scsi.function = VDA_FUNC_SCSI;
+ vrq->scsi.sense_len = SENSE_DATA_SZ;
+
+ /* clear out sg_list_offset and chain_offset */
+ vrq->scsi.sg_list_offset = 0;
+ vrq->scsi.chain_offset = 0;
+ vrq->scsi.flags = 0;
+ vrq->scsi.reserved = 0;
+
+ /* set the sense buffer to be the data payload buffer */
+ vrq->scsi.ppsense_buf
+ = cpu_to_le64(rq->vrq_md->phys_addr +
+ sizeof(union atto_vda_req));
+}
+
+static inline void esas2r_rq_free_sg_lists(struct esas2r_request *rq,
+ struct esas2r_adapter *a)
+{
+ unsigned long flags;
+
+ if (list_empty(&rq->sg_table_head))
+ return;
+
+ spin_lock_irqsave(&a->sg_list_lock, flags);
+ list_splice_tail_init(&rq->sg_table_head, &a->free_sg_list_head);
+ spin_unlock_irqrestore(&a->sg_list_lock, flags);
+}
+
+static inline void esas2r_rq_destroy_request(struct esas2r_request *rq,
+ struct esas2r_adapter *a)
+
+{
+ esas2r_rq_free_sg_lists(rq, a);
+ a->req_table[LOWORD(rq->vrq->scsi.handle)] = NULL;
+ rq->data_buf = NULL;
+}
+
+static inline bool esas2r_is_tasklet_pending(struct esas2r_adapter *a)
+{
+
+ return test_bit(AF_BUSRST_NEEDED, &a->flags) ||
+ test_bit(AF_BUSRST_DETECTED, &a->flags) ||
+ test_bit(AF_CHPRST_NEEDED, &a->flags) ||
+ test_bit(AF_CHPRST_DETECTED, &a->flags) ||
+ test_bit(AF_PORT_CHANGE, &a->flags);
+
+}
+
+/*
+ * Build the scatter/gather list for an I/O request according to the
+ * specifications placed in the esas2r_sg_context. The caller must initialize
+ * struct esas2r_sg_context prior to the initial call by calling
+ * esas2r_sgc_init()
+ */
+static inline bool esas2r_build_sg_list(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc)
+{
+ if (unlikely(le32_to_cpu(rq->vrq->scsi.length) == 0))
+ return true;
+
+ return (*a->build_sgl)(a, sgc);
+}
+
+static inline void esas2r_disable_chip_interrupts(struct esas2r_adapter *a)
+{
+ if (atomic_inc_return(&a->dis_ints_cnt) == 1)
+ esas2r_write_register_dword(a, MU_INT_MASK_OUT,
+ ESAS2R_INT_DIS_MASK);
+}
+
+static inline void esas2r_enable_chip_interrupts(struct esas2r_adapter *a)
+{
+ if (atomic_dec_return(&a->dis_ints_cnt) == 0)
+ esas2r_write_register_dword(a, MU_INT_MASK_OUT,
+ ESAS2R_INT_ENB_MASK);
+}
+
+/* Schedule a TASKLET to perform non-interrupt tasks that may require delays
+ * or long completion times.
+ */
+static inline void esas2r_schedule_tasklet(struct esas2r_adapter *a)
+{
+ /* make sure we don't schedule twice */
+ if (!test_and_set_bit(AF_TASKLET_SCHEDULED, &a->flags))
+ tasklet_hi_schedule(&a->tasklet);
+}
+
+static inline void esas2r_enable_heartbeat(struct esas2r_adapter *a)
+{
+ if (!test_bit(AF_DEGRADED_MODE, &a->flags) &&
+ !test_bit(AF_CHPRST_PENDING, &a->flags) &&
+ (a->nvram->options2 & SASNVR2_HEARTBEAT))
+ set_bit(AF_HEARTBEAT_ENB, &a->flags);
+ else
+ clear_bit(AF_HEARTBEAT_ENB, &a->flags);
+}
+
+static inline void esas2r_disable_heartbeat(struct esas2r_adapter *a)
+{
+ clear_bit(AF_HEARTBEAT_ENB, &a->flags);
+ clear_bit(AF_HEARTBEAT, &a->flags);
+}
+
+/* Set the initial state for resetting the adapter on the next pass through
+ * esas2r_do_deferred.
+ */
+static inline void esas2r_local_reset_adapter(struct esas2r_adapter *a)
+{
+ esas2r_disable_heartbeat(a);
+
+ set_bit(AF_CHPRST_NEEDED, &a->flags);
+ set_bit(AF_CHPRST_PENDING, &a->flags);
+ set_bit(AF_DISC_PENDING, &a->flags);
+}
+
+/* See if an interrupt is pending on the adapter. */
+static inline bool esas2r_adapter_interrupt_pending(struct esas2r_adapter *a)
+{
+ u32 intstat;
+
+ if (a->int_mask == 0)
+ return false;
+
+ intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
+
+ if ((intstat & a->int_mask) == 0)
+ return false;
+
+ esas2r_disable_chip_interrupts(a);
+
+ a->int_stat = intstat;
+ a->int_mask = 0;
+
+ return true;
+}
+
+static inline u16 esas2r_targ_get_id(struct esas2r_target *t,
+ struct esas2r_adapter *a)
+{
+ return (u16)(uintptr_t)(t - a->targetdb);
+}
+
+/* Build and start an asynchronous event request */
+static inline void esas2r_start_ae_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ unsigned long flags;
+
+ esas2r_build_ae_req(a, rq);
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+ esas2r_start_vda_request(a, rq);
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+}
+
+static inline void esas2r_comp_list_drain(struct esas2r_adapter *a,
+ struct list_head *comp_list)
+{
+ struct esas2r_request *rq;
+ struct list_head *element, *next;
+
+ list_for_each_safe(element, next, comp_list) {
+ rq = list_entry(element, struct esas2r_request, comp_list);
+ list_del_init(element);
+ esas2r_complete_request(a, rq);
+ }
+}
+
+/* sysfs handlers */
+extern const struct bin_attribute bin_attr_fw;
+extern const struct bin_attribute bin_attr_fs;
+extern const struct bin_attribute bin_attr_vda;
+extern const struct bin_attribute bin_attr_hw;
+extern const struct bin_attribute bin_attr_live_nvram;
+extern const struct bin_attribute bin_attr_default_nvram;
+
+#endif /* ESAS2R_H */
diff --git a/drivers/scsi/esas2r/esas2r_disc.c b/drivers/scsi/esas2r/esas2r_disc.c
new file mode 100644
index 000000000000..ba42536d1e87
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_disc.c
@@ -0,0 +1,1185 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_disc.c
+ * esas2r device discovery routines
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include "esas2r.h"
+
+/* Miscellaneous internal discovery routines */
+static void esas2r_disc_abort(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static bool esas2r_disc_continue(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static void esas2r_disc_fix_curr_requests(struct esas2r_adapter *a);
+static u32 esas2r_disc_get_phys_addr(struct esas2r_sg_context *sgc, u64 *addr);
+static bool esas2r_disc_start_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+
+/* Internal discovery routines that process the states */
+static bool esas2r_disc_block_dev_scan(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static void esas2r_disc_block_dev_scan_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static bool esas2r_disc_dev_add(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static bool esas2r_disc_dev_remove(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static bool esas2r_disc_part_info(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static void esas2r_disc_part_info_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static bool esas2r_disc_passthru_dev_info(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static void esas2r_disc_passthru_dev_info_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static bool esas2r_disc_passthru_dev_addr(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static void esas2r_disc_passthru_dev_addr_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static bool esas2r_disc_raid_grp_info(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+static void esas2r_disc_raid_grp_info_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+
+void esas2r_disc_initialize(struct esas2r_adapter *a)
+{
+ struct esas2r_sas_nvram *nvr = a->nvram;
+
+ esas2r_trace_enter();
+
+ clear_bit(AF_DISC_IN_PROG, &a->flags);
+ clear_bit(AF2_DEV_SCAN, &a->flags2);
+ clear_bit(AF2_DEV_CNT_OK, &a->flags2);
+
+ a->disc_start_time = jiffies_to_msecs(jiffies);
+ a->disc_wait_time = nvr->dev_wait_time * 1000;
+ a->disc_wait_cnt = nvr->dev_wait_count;
+
+ if (a->disc_wait_cnt > ESAS2R_MAX_TARGETS)
+ a->disc_wait_cnt = ESAS2R_MAX_TARGETS;
+
+ /*
+ * If we are doing chip reset or power management processing, always
+ * wait for devices. use the NVRAM device count if it is greater than
+ * previously discovered devices.
+ */
+
+ esas2r_hdebug("starting discovery...");
+
+ a->general_req.interrupt_cx = NULL;
+
+ if (test_bit(AF_CHPRST_DETECTED, &a->flags) ||
+ test_bit(AF_POWER_MGT, &a->flags)) {
+ if (a->prev_dev_cnt == 0) {
+ /* Don't bother waiting if there is nothing to wait
+ * for.
+ */
+ a->disc_wait_time = 0;
+ } else {
+ /*
+ * Set the device wait count to what was previously
+ * found. We don't care if the user only configured
+ * a time because we know the exact count to wait for.
+ * There is no need to honor the user's wishes to
+ * always wait the full time.
+ */
+ a->disc_wait_cnt = a->prev_dev_cnt;
+
+ /*
+ * bump the minimum wait time to 15 seconds since the
+ * default is 3 (system boot or the boot driver usually
+ * buys us more time).
+ */
+ if (a->disc_wait_time < 15000)
+ a->disc_wait_time = 15000;
+ }
+ }
+
+ esas2r_trace("disc wait count: %d", a->disc_wait_cnt);
+ esas2r_trace("disc wait time: %d", a->disc_wait_time);
+
+ if (a->disc_wait_time == 0)
+ esas2r_disc_check_complete(a);
+
+ esas2r_trace_exit();
+}
+
+void esas2r_disc_start_waiting(struct esas2r_adapter *a)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+
+ if (a->disc_ctx.disc_evt)
+ esas2r_disc_start_port(a);
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+}
+
+void esas2r_disc_check_for_work(struct esas2r_adapter *a)
+{
+ struct esas2r_request *rq = &a->general_req;
+
+ /* service any pending interrupts first */
+
+ esas2r_polled_interrupt(a);
+
+ /*
+ * now, interrupt processing may have queued up a discovery event. go
+ * see if we have one to start. we couldn't start it in the ISR since
+ * polled discovery would cause a deadlock.
+ */
+
+ esas2r_disc_start_waiting(a);
+
+ if (rq->interrupt_cx == NULL)
+ return;
+
+ if (rq->req_stat == RS_STARTED
+ && rq->timeout <= RQ_MAX_TIMEOUT) {
+ /* wait for the current discovery request to complete. */
+ esas2r_wait_request(a, rq);
+
+ if (rq->req_stat == RS_TIMEOUT) {
+ esas2r_disc_abort(a, rq);
+ esas2r_local_reset_adapter(a);
+ return;
+ }
+ }
+
+ if (rq->req_stat == RS_PENDING
+ || rq->req_stat == RS_STARTED)
+ return;
+
+ esas2r_disc_continue(a, rq);
+}
+
+void esas2r_disc_check_complete(struct esas2r_adapter *a)
+{
+ unsigned long flags;
+
+ esas2r_trace_enter();
+
+ /* check to see if we should be waiting for devices */
+ if (a->disc_wait_time) {
+ u32 currtime = jiffies_to_msecs(jiffies);
+ u32 time = currtime - a->disc_start_time;
+
+ /*
+ * Wait until the device wait time is exhausted or the device
+ * wait count is satisfied.
+ */
+ if (time < a->disc_wait_time
+ && (esas2r_targ_db_get_tgt_cnt(a) < a->disc_wait_cnt
+ || a->disc_wait_cnt == 0)) {
+ /* After three seconds of waiting, schedule a scan. */
+ if (time >= 3000
+ && !test_and_set_bit(AF2_DEV_SCAN, &a->flags2)) {
+ spin_lock_irqsave(&a->mem_lock, flags);
+ esas2r_disc_queue_event(a, DCDE_DEV_SCAN);
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+ }
+
+ esas2r_trace_exit();
+ return;
+ }
+
+ /*
+ * We are done waiting...we think. Adjust the wait time to
+ * consume events after the count is met.
+ */
+ if (!test_and_set_bit(AF2_DEV_CNT_OK, &a->flags2))
+ a->disc_wait_time = time + 3000;
+
+ /* If we haven't done a full scan yet, do it now. */
+ if (!test_and_set_bit(AF2_DEV_SCAN, &a->flags2)) {
+ spin_lock_irqsave(&a->mem_lock, flags);
+ esas2r_disc_queue_event(a, DCDE_DEV_SCAN);
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+ esas2r_trace_exit();
+ return;
+ }
+
+ /*
+ * Now, if there is still time left to consume events, continue
+ * waiting.
+ */
+ if (time < a->disc_wait_time) {
+ esas2r_trace_exit();
+ return;
+ }
+ } else {
+ if (!test_and_set_bit(AF2_DEV_SCAN, &a->flags2)) {
+ spin_lock_irqsave(&a->mem_lock, flags);
+ esas2r_disc_queue_event(a, DCDE_DEV_SCAN);
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+ }
+ }
+
+ /* We want to stop waiting for devices. */
+ a->disc_wait_time = 0;
+
+ if (test_bit(AF_DISC_POLLED, &a->flags) &&
+ test_bit(AF_DISC_IN_PROG, &a->flags)) {
+ /*
+ * Polled discovery is still pending so continue the active
+ * discovery until it is done. At that point, we will stop
+ * polled discovery and transition to interrupt driven
+ * discovery.
+ */
+ } else {
+ /*
+ * Done waiting for devices. Note that we get here immediately
+ * after deferred waiting completes because that is interrupt
+ * driven; i.e. There is no transition.
+ */
+ esas2r_disc_fix_curr_requests(a);
+ clear_bit(AF_DISC_PENDING, &a->flags);
+
+ /*
+ * We have deferred target state changes until now because we
+ * don't want to report any removals (due to the first arrival)
+ * until the device wait time expires.
+ */
+ set_bit(AF_PORT_CHANGE, &a->flags);
+ }
+
+ esas2r_trace_exit();
+}
+
+void esas2r_disc_queue_event(struct esas2r_adapter *a, u8 disc_evt)
+{
+ struct esas2r_disc_context *dc = &a->disc_ctx;
+
+ esas2r_trace_enter();
+
+ esas2r_trace("disc_event: %d", disc_evt);
+
+ /* Initialize the discovery context */
+ dc->disc_evt |= disc_evt;
+
+ /*
+ * Don't start discovery before or during polled discovery. if we did,
+ * we would have a deadlock if we are in the ISR already.
+ */
+ if (!test_bit(AF_CHPRST_PENDING, &a->flags) &&
+ !test_bit(AF_DISC_POLLED, &a->flags))
+ esas2r_disc_start_port(a);
+
+ esas2r_trace_exit();
+}
+
+bool esas2r_disc_start_port(struct esas2r_adapter *a)
+{
+ struct esas2r_request *rq = &a->general_req;
+ struct esas2r_disc_context *dc = &a->disc_ctx;
+ bool ret;
+
+ esas2r_trace_enter();
+
+ if (test_bit(AF_DISC_IN_PROG, &a->flags)) {
+ esas2r_trace_exit();
+
+ return false;
+ }
+
+ /* If there is a discovery waiting, process it. */
+ if (dc->disc_evt) {
+ if (test_bit(AF_DISC_POLLED, &a->flags)
+ && a->disc_wait_time == 0) {
+ /*
+ * We are doing polled discovery, but we no longer want
+ * to wait for devices. Stop polled discovery and
+ * transition to interrupt driven discovery.
+ */
+
+ esas2r_trace_exit();
+
+ return false;
+ }
+ } else {
+ /* Discovery is complete. */
+
+ esas2r_hdebug("disc done");
+
+ set_bit(AF_PORT_CHANGE, &a->flags);
+
+ esas2r_trace_exit();
+
+ return false;
+ }
+
+ /* Handle the discovery context */
+ esas2r_trace("disc_evt: %d", dc->disc_evt);
+ set_bit(AF_DISC_IN_PROG, &a->flags);
+ dc->flags = 0;
+
+ if (test_bit(AF_DISC_POLLED, &a->flags))
+ dc->flags |= DCF_POLLED;
+
+ rq->interrupt_cx = dc;
+ rq->req_stat = RS_SUCCESS;
+
+ /* Decode the event code */
+ if (dc->disc_evt & DCDE_DEV_SCAN) {
+ dc->disc_evt &= ~DCDE_DEV_SCAN;
+
+ dc->flags |= DCF_DEV_SCAN;
+ dc->state = DCS_BLOCK_DEV_SCAN;
+ } else if (dc->disc_evt & DCDE_DEV_CHANGE) {
+ dc->disc_evt &= ~DCDE_DEV_CHANGE;
+
+ dc->flags |= DCF_DEV_CHANGE;
+ dc->state = DCS_DEV_RMV;
+ }
+
+ /* Continue interrupt driven discovery */
+ if (!test_bit(AF_DISC_POLLED, &a->flags))
+ ret = esas2r_disc_continue(a, rq);
+ else
+ ret = true;
+
+ esas2r_trace_exit();
+
+ return ret;
+}
+
+static bool esas2r_disc_continue(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ bool rslt;
+
+ /* Device discovery/removal */
+ while (dc->flags & (DCF_DEV_CHANGE | DCF_DEV_SCAN)) {
+ rslt = false;
+
+ switch (dc->state) {
+ case DCS_DEV_RMV:
+
+ rslt = esas2r_disc_dev_remove(a, rq);
+ break;
+
+ case DCS_DEV_ADD:
+
+ rslt = esas2r_disc_dev_add(a, rq);
+ break;
+
+ case DCS_BLOCK_DEV_SCAN:
+
+ rslt = esas2r_disc_block_dev_scan(a, rq);
+ break;
+
+ case DCS_RAID_GRP_INFO:
+
+ rslt = esas2r_disc_raid_grp_info(a, rq);
+ break;
+
+ case DCS_PART_INFO:
+
+ rslt = esas2r_disc_part_info(a, rq);
+ break;
+
+ case DCS_PT_DEV_INFO:
+
+ rslt = esas2r_disc_passthru_dev_info(a, rq);
+ break;
+ case DCS_PT_DEV_ADDR:
+
+ rslt = esas2r_disc_passthru_dev_addr(a, rq);
+ break;
+ case DCS_DISC_DONE:
+
+ dc->flags &= ~(DCF_DEV_CHANGE | DCF_DEV_SCAN);
+ break;
+
+ default:
+
+ esas2r_bugon();
+ dc->state = DCS_DISC_DONE;
+ break;
+ }
+
+ if (rslt)
+ return true;
+ }
+
+ /* Discovery is done...for now. */
+ rq->interrupt_cx = NULL;
+
+ if (!test_bit(AF_DISC_PENDING, &a->flags))
+ esas2r_disc_fix_curr_requests(a);
+
+ clear_bit(AF_DISC_IN_PROG, &a->flags);
+
+ /* Start the next discovery. */
+ return esas2r_disc_start_port(a);
+}
+
+static bool esas2r_disc_start_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ unsigned long flags;
+
+ /* Set the timeout to a minimum value. */
+ if (rq->timeout < ESAS2R_DEFAULT_TMO)
+ rq->timeout = ESAS2R_DEFAULT_TMO;
+
+ /*
+ * Override the request type to distinguish discovery requests. If we
+ * end up deferring the request, esas2r_disc_local_start_request()
+ * will be called to restart it.
+ */
+ rq->req_type = RT_DISC_REQ;
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ if (!test_bit(AF_CHPRST_PENDING, &a->flags) &&
+ !test_bit(AF_FLASHING, &a->flags))
+ esas2r_disc_local_start_request(a, rq);
+ else
+ list_add_tail(&rq->req_list, &a->defer_list);
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+
+ return true;
+}
+
+void esas2r_disc_local_start_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ esas2r_trace_enter();
+
+ list_add_tail(&rq->req_list, &a->active_list);
+
+ esas2r_start_vda_request(a, rq);
+
+ esas2r_trace_exit();
+
+ return;
+}
+
+static void esas2r_disc_abort(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+
+ esas2r_trace_enter();
+
+ /* abort the current discovery */
+
+ dc->state = DCS_DISC_DONE;
+
+ esas2r_trace_exit();
+}
+
+static bool esas2r_disc_block_dev_scan(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ bool rslt;
+
+ esas2r_trace_enter();
+
+ esas2r_rq_init_request(rq, a);
+
+ esas2r_build_mgt_req(a,
+ rq,
+ VDAMGT_DEV_SCAN,
+ 0,
+ 0,
+ 0,
+ NULL);
+
+ rq->comp_cb = esas2r_disc_block_dev_scan_cb;
+
+ rq->timeout = 30000;
+ rq->interrupt_cx = dc;
+
+ rslt = esas2r_disc_start_request(a, rq);
+
+ esas2r_trace_exit();
+
+ return rslt;
+}
+
+static void esas2r_disc_block_dev_scan_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ unsigned long flags;
+
+ esas2r_trace_enter();
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+
+ if (rq->req_stat == RS_SUCCESS)
+ dc->scan_gen = rq->func_rsp.mgt_rsp.scan_generation;
+
+ dc->state = DCS_RAID_GRP_INFO;
+ dc->raid_grp_ix = 0;
+
+ esas2r_rq_destroy_request(rq, a);
+
+ /* continue discovery if it's interrupt driven */
+
+ if (!(dc->flags & DCF_POLLED))
+ esas2r_disc_continue(a, rq);
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ esas2r_trace_exit();
+}
+
+static bool esas2r_disc_raid_grp_info(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ bool rslt;
+ struct atto_vda_grp_info *grpinfo;
+
+ esas2r_trace_enter();
+
+ esas2r_trace("raid_group_idx: %d", dc->raid_grp_ix);
+
+ if (dc->raid_grp_ix >= VDA_MAX_RAID_GROUPS) {
+ dc->state = DCS_DISC_DONE;
+
+ esas2r_trace_exit();
+
+ return false;
+ }
+
+ esas2r_rq_init_request(rq, a);
+
+ grpinfo = &rq->vda_rsp_data->mgt_data.data.grp_info;
+
+ memset(grpinfo, 0, sizeof(struct atto_vda_grp_info));
+
+ esas2r_build_mgt_req(a,
+ rq,
+ VDAMGT_GRP_INFO,
+ dc->scan_gen,
+ 0,
+ sizeof(struct atto_vda_grp_info),
+ NULL);
+
+ grpinfo->grp_index = dc->raid_grp_ix;
+
+ rq->comp_cb = esas2r_disc_raid_grp_info_cb;
+
+ rq->interrupt_cx = dc;
+
+ rslt = esas2r_disc_start_request(a, rq);
+
+ esas2r_trace_exit();
+
+ return rslt;
+}
+
+static void esas2r_disc_raid_grp_info_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ unsigned long flags;
+ struct atto_vda_grp_info *grpinfo;
+
+ esas2r_trace_enter();
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+
+ if (rq->req_stat == RS_SCAN_GEN) {
+ dc->scan_gen = rq->func_rsp.mgt_rsp.scan_generation;
+ dc->raid_grp_ix = 0;
+ goto done;
+ }
+
+ if (rq->req_stat == RS_SUCCESS) {
+ grpinfo = &rq->vda_rsp_data->mgt_data.data.grp_info;
+
+ if (grpinfo->status != VDA_GRP_STAT_ONLINE
+ && grpinfo->status != VDA_GRP_STAT_DEGRADED) {
+ /* go to the next group. */
+
+ dc->raid_grp_ix++;
+ } else {
+ memcpy(&dc->raid_grp_name[0],
+ &grpinfo->grp_name[0],
+ sizeof(grpinfo->grp_name));
+
+ dc->interleave = le32_to_cpu(grpinfo->interleave);
+ dc->block_size = le32_to_cpu(grpinfo->block_size);
+
+ dc->state = DCS_PART_INFO;
+ dc->part_num = 0;
+ }
+ } else {
+ if (!(rq->req_stat == RS_GRP_INVALID)) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "A request for RAID group info failed - "
+ "returned with %x",
+ rq->req_stat);
+ }
+
+ dc->dev_ix = 0;
+ dc->state = DCS_PT_DEV_INFO;
+ }
+
+done:
+
+ esas2r_rq_destroy_request(rq, a);
+
+ /* continue discovery if it's interrupt driven */
+
+ if (!(dc->flags & DCF_POLLED))
+ esas2r_disc_continue(a, rq);
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ esas2r_trace_exit();
+}
+
+static bool esas2r_disc_part_info(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ bool rslt;
+ struct atto_vdapart_info *partinfo;
+
+ esas2r_trace_enter();
+
+ esas2r_trace("part_num: %d", dc->part_num);
+
+ if (dc->part_num >= VDA_MAX_PARTITIONS) {
+ dc->state = DCS_RAID_GRP_INFO;
+ dc->raid_grp_ix++;
+
+ esas2r_trace_exit();
+
+ return false;
+ }
+
+ esas2r_rq_init_request(rq, a);
+
+ partinfo = &rq->vda_rsp_data->mgt_data.data.part_info;
+
+ memset(partinfo, 0, sizeof(struct atto_vdapart_info));
+
+ esas2r_build_mgt_req(a,
+ rq,
+ VDAMGT_PART_INFO,
+ dc->scan_gen,
+ 0,
+ sizeof(struct atto_vdapart_info),
+ NULL);
+
+ partinfo->part_no = dc->part_num;
+
+ memcpy(&partinfo->grp_name[0],
+ &dc->raid_grp_name[0],
+ sizeof(partinfo->grp_name));
+
+ rq->comp_cb = esas2r_disc_part_info_cb;
+
+ rq->interrupt_cx = dc;
+
+ rslt = esas2r_disc_start_request(a, rq);
+
+ esas2r_trace_exit();
+
+ return rslt;
+}
+
+static void esas2r_disc_part_info_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ unsigned long flags;
+ struct atto_vdapart_info *partinfo;
+
+ esas2r_trace_enter();
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+
+ if (rq->req_stat == RS_SCAN_GEN) {
+ dc->scan_gen = rq->func_rsp.mgt_rsp.scan_generation;
+ dc->raid_grp_ix = 0;
+ dc->state = DCS_RAID_GRP_INFO;
+ } else if (rq->req_stat == RS_SUCCESS) {
+ partinfo = &rq->vda_rsp_data->mgt_data.data.part_info;
+
+ dc->part_num = partinfo->part_no;
+
+ dc->curr_virt_id = le16_to_cpu(partinfo->target_id);
+
+ esas2r_targ_db_add_raid(a, dc);
+
+ dc->part_num++;
+ } else {
+ if (!(rq->req_stat == RS_PART_LAST)) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "A request for RAID group partition info "
+ "failed - status:%d", rq->req_stat);
+ }
+
+ dc->state = DCS_RAID_GRP_INFO;
+ dc->raid_grp_ix++;
+ }
+
+ esas2r_rq_destroy_request(rq, a);
+
+ /* continue discovery if it's interrupt driven */
+
+ if (!(dc->flags & DCF_POLLED))
+ esas2r_disc_continue(a, rq);
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ esas2r_trace_exit();
+}
+
+static bool esas2r_disc_passthru_dev_info(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ bool rslt;
+ struct atto_vda_devinfo *devinfo;
+
+ esas2r_trace_enter();
+
+ esas2r_trace("dev_ix: %d", dc->dev_ix);
+
+ esas2r_rq_init_request(rq, a);
+
+ devinfo = &rq->vda_rsp_data->mgt_data.data.dev_info;
+
+ memset(devinfo, 0, sizeof(struct atto_vda_devinfo));
+
+ esas2r_build_mgt_req(a,
+ rq,
+ VDAMGT_DEV_PT_INFO,
+ dc->scan_gen,
+ dc->dev_ix,
+ sizeof(struct atto_vda_devinfo),
+ NULL);
+
+ rq->comp_cb = esas2r_disc_passthru_dev_info_cb;
+
+ rq->interrupt_cx = dc;
+
+ rslt = esas2r_disc_start_request(a, rq);
+
+ esas2r_trace_exit();
+
+ return rslt;
+}
+
+static void esas2r_disc_passthru_dev_info_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ unsigned long flags;
+ struct atto_vda_devinfo *devinfo;
+
+ esas2r_trace_enter();
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+
+ if (rq->req_stat == RS_SCAN_GEN) {
+ dc->scan_gen = rq->func_rsp.mgt_rsp.scan_generation;
+ dc->dev_ix = 0;
+ dc->state = DCS_PT_DEV_INFO;
+ } else if (rq->req_stat == RS_SUCCESS) {
+ devinfo = &rq->vda_rsp_data->mgt_data.data.dev_info;
+
+ dc->dev_ix = le16_to_cpu(rq->func_rsp.mgt_rsp.dev_index);
+
+ dc->curr_virt_id = le16_to_cpu(devinfo->target_id);
+
+ if (le16_to_cpu(devinfo->features) & VDADEVFEAT_PHYS_ID) {
+ dc->curr_phys_id =
+ le16_to_cpu(devinfo->phys_target_id);
+ dc->dev_addr_type = ATTO_GDA_AT_PORT;
+ dc->state = DCS_PT_DEV_ADDR;
+
+ esas2r_trace("curr_virt_id: %d", dc->curr_virt_id);
+ esas2r_trace("curr_phys_id: %d", dc->curr_phys_id);
+ } else {
+ dc->dev_ix++;
+ }
+ } else {
+ if (!(rq->req_stat == RS_DEV_INVALID)) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "A request for device information failed - "
+ "status:%d", rq->req_stat);
+ }
+
+ dc->state = DCS_DISC_DONE;
+ }
+
+ esas2r_rq_destroy_request(rq, a);
+
+ /* continue discovery if it's interrupt driven */
+
+ if (!(dc->flags & DCF_POLLED))
+ esas2r_disc_continue(a, rq);
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ esas2r_trace_exit();
+}
+
+static bool esas2r_disc_passthru_dev_addr(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ bool rslt;
+ struct atto_ioctl *hi;
+ struct esas2r_sg_context sgc;
+
+ esas2r_trace_enter();
+
+ esas2r_rq_init_request(rq, a);
+
+ /* format the request. */
+
+ sgc.cur_offset = NULL;
+ sgc.get_phys_addr = (PGETPHYSADDR)esas2r_disc_get_phys_addr;
+ sgc.length = offsetof(struct atto_ioctl, data)
+ + sizeof(struct atto_hba_get_device_address);
+
+ esas2r_sgc_init(&sgc, a, rq, rq->vrq->ioctl.sge);
+
+ esas2r_build_ioctl_req(a, rq, sgc.length, VDA_IOCTL_HBA);
+
+ if (!esas2r_build_sg_list(a, rq, &sgc)) {
+ esas2r_rq_destroy_request(rq, a);
+
+ esas2r_trace_exit();
+
+ return false;
+ }
+
+ rq->comp_cb = esas2r_disc_passthru_dev_addr_cb;
+
+ rq->interrupt_cx = dc;
+
+ /* format the IOCTL data. */
+
+ hi = (struct atto_ioctl *)a->disc_buffer;
+
+ memset(a->disc_buffer, 0, ESAS2R_DISC_BUF_LEN);
+
+ hi->version = ATTO_VER_GET_DEV_ADDR0;
+ hi->function = ATTO_FUNC_GET_DEV_ADDR;
+ hi->flags = HBAF_TUNNEL;
+
+ hi->data.get_dev_addr.target_id = le32_to_cpu(dc->curr_phys_id);
+ hi->data.get_dev_addr.addr_type = dc->dev_addr_type;
+
+ /* start it up. */
+
+ rslt = esas2r_disc_start_request(a, rq);
+
+ esas2r_trace_exit();
+
+ return rslt;
+}
+
+static void esas2r_disc_passthru_dev_addr_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ struct esas2r_target *t = NULL;
+ unsigned long flags;
+ struct atto_ioctl *hi;
+ u16 addrlen;
+
+ esas2r_trace_enter();
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+
+ hi = (struct atto_ioctl *)a->disc_buffer;
+
+ if (rq->req_stat == RS_SUCCESS
+ && hi->status == ATTO_STS_SUCCESS) {
+ addrlen = le16_to_cpu(hi->data.get_dev_addr.addr_len);
+
+ if (dc->dev_addr_type == ATTO_GDA_AT_PORT) {
+ if (addrlen == sizeof(u64))
+ memcpy(&dc->sas_addr,
+ &hi->data.get_dev_addr.address[0],
+ addrlen);
+ else
+ memset(&dc->sas_addr, 0, sizeof(dc->sas_addr));
+
+ /* Get the unique identifier. */
+ dc->dev_addr_type = ATTO_GDA_AT_UNIQUE;
+
+ goto next_dev_addr;
+ } else {
+ /* Add the pass through target. */
+ if (HIBYTE(addrlen) == 0) {
+ t = esas2r_targ_db_add_pthru(a,
+ dc,
+ &hi->data.
+ get_dev_addr.
+ address[0],
+ (u8)hi->data.
+ get_dev_addr.
+ addr_len);
+
+ if (t)
+ memcpy(&t->sas_addr, &dc->sas_addr,
+ sizeof(t->sas_addr));
+ } else {
+ /* getting the back end data failed */
+
+ esas2r_log(ESAS2R_LOG_WARN,
+ "an error occurred retrieving the "
+ "back end data (%s:%d)",
+ __func__,
+ __LINE__);
+ }
+ }
+ } else {
+ /* getting the back end data failed */
+
+ esas2r_log(ESAS2R_LOG_WARN,
+ "an error occurred retrieving the back end data - "
+ "rq->req_stat:%d hi->status:%d",
+ rq->req_stat, hi->status);
+ }
+
+ /* proceed to the next device. */
+
+ if (dc->flags & DCF_DEV_SCAN) {
+ dc->dev_ix++;
+ dc->state = DCS_PT_DEV_INFO;
+ } else if (dc->flags & DCF_DEV_CHANGE) {
+ dc->curr_targ++;
+ dc->state = DCS_DEV_ADD;
+ } else {
+ esas2r_bugon();
+ }
+
+next_dev_addr:
+ esas2r_rq_destroy_request(rq, a);
+
+ /* continue discovery if it's interrupt driven */
+
+ if (!(dc->flags & DCF_POLLED))
+ esas2r_disc_continue(a, rq);
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ esas2r_trace_exit();
+}
+
+static u32 esas2r_disc_get_phys_addr(struct esas2r_sg_context *sgc, u64 *addr)
+{
+ struct esas2r_adapter *a = sgc->adapter;
+
+ if (sgc->length > ESAS2R_DISC_BUF_LEN) {
+ esas2r_bugon();
+ }
+
+ *addr = a->uncached_phys
+ + (u64)((u8 *)a->disc_buffer - a->uncached);
+
+ return sgc->length;
+}
+
+static bool esas2r_disc_dev_remove(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ struct esas2r_target *t;
+ struct esas2r_target *t2;
+
+ esas2r_trace_enter();
+
+ /* process removals. */
+
+ for (t = a->targetdb; t < a->targetdb_end; t++) {
+ if (t->new_target_state != TS_NOT_PRESENT)
+ continue;
+
+ t->new_target_state = TS_INVALID;
+
+ /* remove the right target! */
+
+ t2 =
+ esas2r_targ_db_find_by_virt_id(a,
+ esas2r_targ_get_id(t,
+ a));
+
+ if (t2)
+ esas2r_targ_db_remove(a, t2);
+ }
+
+ /* removals complete. process arrivals. */
+
+ dc->state = DCS_DEV_ADD;
+ dc->curr_targ = a->targetdb;
+
+ esas2r_trace_exit();
+
+ return false;
+}
+
+static bool esas2r_disc_dev_add(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_disc_context *dc =
+ (struct esas2r_disc_context *)rq->interrupt_cx;
+ struct esas2r_target *t = dc->curr_targ;
+
+ if (t >= a->targetdb_end) {
+ /* done processing state changes. */
+
+ dc->state = DCS_DISC_DONE;
+ } else if (t->new_target_state == TS_PRESENT) {
+ struct atto_vda_ae_lu *luevt = &t->lu_event;
+
+ esas2r_trace_enter();
+
+ /* clear this now in case more events come in. */
+
+ t->new_target_state = TS_INVALID;
+
+ /* setup the discovery context for adding this device. */
+
+ dc->curr_virt_id = esas2r_targ_get_id(t, a);
+
+ if ((luevt->hdr.bylength >= offsetof(struct atto_vda_ae_lu, id)
+ + sizeof(struct atto_vda_ae_lu_tgt_lun_raid))
+ && !(luevt->dwevent & VDAAE_LU_PASSTHROUGH)) {
+ dc->block_size = luevt->id.tgtlun_raid.dwblock_size;
+ dc->interleave = luevt->id.tgtlun_raid.dwinterleave;
+ } else {
+ dc->block_size = 0;
+ dc->interleave = 0;
+ }
+
+ /* determine the device type being added. */
+
+ if (luevt->dwevent & VDAAE_LU_PASSTHROUGH) {
+ if (luevt->dwevent & VDAAE_LU_PHYS_ID) {
+ dc->state = DCS_PT_DEV_ADDR;
+ dc->dev_addr_type = ATTO_GDA_AT_PORT;
+ dc->curr_phys_id = luevt->wphys_target_id;
+ } else {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "luevt->dwevent does not have the "
+ "VDAAE_LU_PHYS_ID bit set (%s:%d)",
+ __func__, __LINE__);
+ }
+ } else {
+ dc->raid_grp_name[0] = 0;
+
+ esas2r_targ_db_add_raid(a, dc);
+ }
+
+ esas2r_trace("curr_virt_id: %d", dc->curr_virt_id);
+ esas2r_trace("curr_phys_id: %d", dc->curr_phys_id);
+ esas2r_trace("dwevent: %d", luevt->dwevent);
+
+ esas2r_trace_exit();
+ }
+
+ if (dc->state == DCS_DEV_ADD) {
+ /* go to the next device. */
+
+ dc->curr_targ++;
+ }
+
+ return false;
+}
+
+/*
+ * When discovery is done, find all requests on defer queue and
+ * test if they need to be modified. If a target is no longer present
+ * then complete the request with RS_SEL. Otherwise, update the
+ * target_id since after a hibernate it can be a different value.
+ * VDA does not make passthrough target IDs persistent.
+ */
+static void esas2r_disc_fix_curr_requests(struct esas2r_adapter *a)
+{
+ unsigned long flags;
+ struct esas2r_target *t;
+ struct esas2r_request *rq;
+ struct list_head *element;
+
+ /* update virt_targ_id in any outstanding esas2r_requests */
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ list_for_each(element, &a->defer_list) {
+ rq = list_entry(element, struct esas2r_request, req_list);
+ if (rq->vrq->scsi.function == VDA_FUNC_SCSI) {
+ t = a->targetdb + rq->target_id;
+
+ if (t->target_state == TS_PRESENT)
+ rq->vrq->scsi.target_id = le16_to_cpu(
+ t->virt_targ_id);
+ else
+ rq->req_stat = RS_SEL;
+ }
+
+ }
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+}
diff --git a/drivers/scsi/esas2r/esas2r_flash.c b/drivers/scsi/esas2r/esas2r_flash.c
new file mode 100644
index 000000000000..f910e2553fbb
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_flash.c
@@ -0,0 +1,1522 @@
+
+/*
+ * linux/drivers/scsi/esas2r/esas2r_flash.c
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+/* local macro defs */
+#define esas2r_nvramcalc_cksum(n) \
+ (esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \
+ SASNVR_CKSUM_SEED))
+#define esas2r_nvramcalc_xor_cksum(n) \
+ (esas2r_calc_byte_xor_cksum((u8 *)(n), \
+ sizeof(struct esas2r_sas_nvram), 0))
+
+#define ESAS2R_FS_DRVR_VER 2
+
+static struct esas2r_sas_nvram default_sas_nvram = {
+ { 'E', 'S', 'A', 'S' }, /* signature */
+ SASNVR_VERSION, /* version */
+ 0, /* checksum */
+ 31, /* max_lun_for_target */
+ SASNVR_PCILAT_MAX, /* pci_latency */
+ SASNVR1_BOOT_DRVR, /* options1 */
+ SASNVR2_HEARTBEAT | SASNVR2_SINGLE_BUS /* options2 */
+ | SASNVR2_SW_MUX_CTRL,
+ SASNVR_COAL_DIS, /* int_coalescing */
+ SASNVR_CMDTHR_NONE, /* cmd_throttle */
+ 3, /* dev_wait_time */
+ 1, /* dev_wait_count */
+ 0, /* spin_up_delay */
+ 0, /* ssp_align_rate */
+ { 0x50, 0x01, 0x08, 0x60, /* sas_addr */
+ 0x00, 0x00, 0x00, 0x00 },
+ { SASNVR_SPEED_AUTO }, /* phy_speed */
+ { SASNVR_MUX_DISABLED }, /* SAS multiplexing */
+ { 0 }, /* phy_flags */
+ SASNVR_SORT_SAS_ADDR, /* sort_type */
+ 3, /* dpm_reqcmd_lmt */
+ 3, /* dpm_stndby_time */
+ 0, /* dpm_active_time */
+ { 0 }, /* phy_target_id */
+ SASNVR_VSMH_DISABLED, /* virt_ses_mode */
+ SASNVR_RWM_DEFAULT, /* read_write_mode */
+ 0, /* link down timeout */
+ { 0 } /* reserved */
+};
+
+static u8 cmd_to_fls_func[] = {
+ 0xFF,
+ VDA_FLASH_READ,
+ VDA_FLASH_BEGINW,
+ VDA_FLASH_WRITE,
+ VDA_FLASH_COMMIT,
+ VDA_FLASH_CANCEL
+};
+
+static u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed)
+{
+ u32 cksum = seed;
+ u8 *p = (u8 *)&cksum;
+
+ while (len) {
+ if (((uintptr_t)addr & 3) == 0)
+ break;
+
+ cksum = cksum ^ *addr;
+ addr++;
+ len--;
+ }
+ while (len >= sizeof(u32)) {
+ cksum = cksum ^ *(u32 *)addr;
+ addr += 4;
+ len -= 4;
+ }
+ while (len--) {
+ cksum = cksum ^ *addr;
+ addr++;
+ }
+ return p[0] ^ p[1] ^ p[2] ^ p[3];
+}
+
+static u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed)
+{
+ u8 *p = (u8 *)addr;
+ u8 cksum = seed;
+
+ while (len--)
+ cksum = cksum + p[len];
+ return cksum;
+}
+
+/* Interrupt callback to process FM API write requests. */
+static void esas2r_fmapi_callback(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct atto_vda_flash_req *vrq = &rq->vrq->flash;
+ struct esas2r_flash_context *fc =
+ (struct esas2r_flash_context *)rq->interrupt_cx;
+
+ if (rq->req_stat == RS_SUCCESS) {
+ /* Last request was successful. See what to do now. */
+ switch (vrq->sub_func) {
+ case VDA_FLASH_BEGINW:
+ if (fc->sgc.cur_offset == NULL)
+ goto commit;
+
+ vrq->sub_func = VDA_FLASH_WRITE;
+ rq->req_stat = RS_PENDING;
+ break;
+
+ case VDA_FLASH_WRITE:
+commit:
+ vrq->sub_func = VDA_FLASH_COMMIT;
+ rq->req_stat = RS_PENDING;
+ rq->interrupt_cb = fc->interrupt_cb;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (rq->req_stat != RS_PENDING)
+ /*
+ * All done. call the real callback to complete the FM API
+ * request. We should only get here if a BEGINW or WRITE
+ * operation failed.
+ */
+ (*fc->interrupt_cb)(a, rq);
+}
+
+/*
+ * Build a flash request based on the flash context. The request status
+ * is filled in on an error.
+ */
+static void build_flash_msg(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_flash_context *fc =
+ (struct esas2r_flash_context *)rq->interrupt_cx;
+ struct esas2r_sg_context *sgc = &fc->sgc;
+ u8 cksum = 0;
+
+ /* calculate the checksum */
+ if (fc->func == VDA_FLASH_BEGINW) {
+ if (sgc->cur_offset)
+ cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset,
+ sgc->length,
+ 0);
+ rq->interrupt_cb = esas2r_fmapi_callback;
+ } else {
+ rq->interrupt_cb = fc->interrupt_cb;
+ }
+ esas2r_build_flash_req(a,
+ rq,
+ fc->func,
+ cksum,
+ fc->flsh_addr,
+ sgc->length);
+
+ esas2r_rq_free_sg_lists(rq, a);
+
+ /*
+ * remember the length we asked for. we have to keep track of
+ * the current amount done so we know how much to compare when
+ * doing the verification phase.
+ */
+ fc->curr_len = fc->sgc.length;
+
+ if (sgc->cur_offset) {
+ /* setup the S/G context to build the S/G table */
+ esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]);
+
+ if (!esas2r_build_sg_list(a, rq, sgc)) {
+ rq->req_stat = RS_BUSY;
+ return;
+ }
+ } else {
+ fc->sgc.length = 0;
+ }
+
+ /* update the flsh_addr to the next one to write to */
+ fc->flsh_addr += fc->curr_len;
+}
+
+/* determine the method to process the flash request */
+static bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq)
+{
+ /*
+ * assume we have more to do. if we return with the status set to
+ * RS_PENDING, FM API tasks will continue.
+ */
+ rq->req_stat = RS_PENDING;
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ /* not supported for now */;
+ else
+ build_flash_msg(a, rq);
+
+ return rq->req_stat == RS_PENDING;
+}
+
+/* boot image fixer uppers called before downloading the image. */
+static void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
+{
+ struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS];
+ struct esas2r_pc_image *pi;
+ struct esas2r_boot_header *bh;
+
+ pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset);
+ bh =
+ (struct esas2r_boot_header *)((u8 *)pi +
+ le16_to_cpu(pi->header_offset));
+ bh->device_id = cpu_to_le16(a->pcid->device);
+
+ /* Recalculate the checksum in the PNP header if there */
+ if (pi->pnp_offset) {
+ u8 *pnp_header_bytes =
+ ((u8 *)pi + le16_to_cpu(pi->pnp_offset));
+
+ /* Identifier - dword that starts at byte 10 */
+ *((u32 *)&pnp_header_bytes[10]) =
+ cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor,
+ a->pcid->subsystem_device));
+
+ /* Checksum - byte 9 */
+ pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes,
+ 32, 0);
+ }
+
+ /* Recalculate the checksum needed by the PC */
+ pi->checksum = pi->checksum -
+ esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0);
+}
+
+static void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
+{
+ struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI];
+ u32 len = ch->length;
+ u32 offset = ch->image_offset;
+ struct esas2r_efi_image *ei;
+ struct esas2r_boot_header *bh;
+
+ while (len) {
+ u32 thislen;
+
+ ei = (struct esas2r_efi_image *)((u8 *)fi + offset);
+ bh = (struct esas2r_boot_header *)((u8 *)ei +
+ le16_to_cpu(
+ ei->header_offset));
+ bh->device_id = cpu_to_le16(a->pcid->device);
+ thislen = (u32)le16_to_cpu(bh->image_length) * 512;
+
+ if (thislen > len)
+ break;
+
+ len -= thislen;
+ offset += thislen;
+ }
+}
+
+/* Complete a FM API request with the specified status. */
+static bool complete_fmapi_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq, u8 fi_stat)
+{
+ struct esas2r_flash_context *fc =
+ (struct esas2r_flash_context *)rq->interrupt_cx;
+ struct esas2r_flash_img *fi = fc->fi;
+
+ fi->status = fi_stat;
+ fi->driver_error = rq->req_stat;
+ rq->interrupt_cb = NULL;
+ rq->req_stat = RS_SUCCESS;
+
+ if (fi_stat != FI_STAT_IMG_VER)
+ memset(fc->scratch, 0, FM_BUF_SZ);
+
+ esas2r_enable_heartbeat(a);
+ clear_bit(AF_FLASH_LOCK, &a->flags);
+ return false;
+}
+
+/* Process each phase of the flash download process. */
+static void fw_download_proc(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_flash_context *fc =
+ (struct esas2r_flash_context *)rq->interrupt_cx;
+ struct esas2r_flash_img *fi = fc->fi;
+ struct esas2r_component_header *ch;
+ u32 len;
+ u8 *p, *q;
+
+ /* If the previous operation failed, just return. */
+ if (rq->req_stat != RS_SUCCESS)
+ goto error;
+
+ /*
+ * If an upload just completed and the compare length is non-zero,
+ * then we just read back part of the image we just wrote. verify the
+ * section and continue reading until the entire image is verified.
+ */
+ if (fc->func == VDA_FLASH_READ
+ && fc->cmp_len) {
+ ch = &fi->cmp_hdr[fc->comp_typ];
+
+ p = fc->scratch;
+ q = (u8 *)fi /* start of the whole gob */
+ + ch->image_offset /* start of the current image */
+ + ch->length /* end of the current image */
+ - fc->cmp_len; /* where we are now */
+
+ /*
+ * NOTE - curr_len is the exact count of bytes for the read
+ * even when the end is read and its not a full buffer
+ */
+ for (len = fc->curr_len; len; len--)
+ if (*p++ != *q++)
+ goto error;
+
+ fc->cmp_len -= fc->curr_len; /* # left to compare */
+
+ /* Update fc and determine the length for the next upload */
+ if (fc->cmp_len > FM_BUF_SZ)
+ fc->sgc.length = FM_BUF_SZ;
+ else
+ fc->sgc.length = fc->cmp_len;
+
+ fc->sgc.cur_offset = fc->sgc_offset +
+ ((u8 *)fc->scratch - (u8 *)fi);
+ }
+
+ /*
+ * This code uses a 'while' statement since the next component may
+ * have a length = zero. This can happen since some components are
+ * not required. At the end of this 'while' we set up the length
+ * for the next request and therefore sgc.length can be = 0.
+ */
+ while (fc->sgc.length == 0) {
+ ch = &fi->cmp_hdr[fc->comp_typ];
+
+ switch (fc->task) {
+ case FMTSK_ERASE_BOOT:
+ /* the BIOS image is written next */
+ ch = &fi->cmp_hdr[CH_IT_BIOS];
+ if (ch->length == 0)
+ goto no_bios;
+
+ fc->task = FMTSK_WRTBIOS;
+ fc->func = VDA_FLASH_BEGINW;
+ fc->comp_typ = CH_IT_BIOS;
+ fc->flsh_addr = FLS_OFFSET_BOOT;
+ fc->sgc.length = ch->length;
+ fc->sgc.cur_offset = fc->sgc_offset +
+ ch->image_offset;
+ break;
+
+ case FMTSK_WRTBIOS:
+ /*
+ * The BIOS image has been written - read it and
+ * verify it
+ */
+ fc->task = FMTSK_READBIOS;
+ fc->func = VDA_FLASH_READ;
+ fc->flsh_addr = FLS_OFFSET_BOOT;
+ fc->cmp_len = ch->length;
+ fc->sgc.length = FM_BUF_SZ;
+ fc->sgc.cur_offset = fc->sgc_offset
+ + ((u8 *)fc->scratch -
+ (u8 *)fi);
+ break;
+
+ case FMTSK_READBIOS:
+no_bios:
+ /*
+ * Mark the component header status for the image
+ * completed
+ */
+ ch->status = CH_STAT_SUCCESS;
+
+ /* The MAC image is written next */
+ ch = &fi->cmp_hdr[CH_IT_MAC];
+ if (ch->length == 0)
+ goto no_mac;
+
+ fc->task = FMTSK_WRTMAC;
+ fc->func = VDA_FLASH_BEGINW;
+ fc->comp_typ = CH_IT_MAC;
+ fc->flsh_addr = FLS_OFFSET_BOOT
+ + fi->cmp_hdr[CH_IT_BIOS].length;
+ fc->sgc.length = ch->length;
+ fc->sgc.cur_offset = fc->sgc_offset +
+ ch->image_offset;
+ break;
+
+ case FMTSK_WRTMAC:
+ /* The MAC image has been written - read and verify */
+ fc->task = FMTSK_READMAC;
+ fc->func = VDA_FLASH_READ;
+ fc->flsh_addr -= ch->length;
+ fc->cmp_len = ch->length;
+ fc->sgc.length = FM_BUF_SZ;
+ fc->sgc.cur_offset = fc->sgc_offset
+ + ((u8 *)fc->scratch -
+ (u8 *)fi);
+ break;
+
+ case FMTSK_READMAC:
+no_mac:
+ /*
+ * Mark the component header status for the image
+ * completed
+ */
+ ch->status = CH_STAT_SUCCESS;
+
+ /* The EFI image is written next */
+ ch = &fi->cmp_hdr[CH_IT_EFI];
+ if (ch->length == 0)
+ goto no_efi;
+
+ fc->task = FMTSK_WRTEFI;
+ fc->func = VDA_FLASH_BEGINW;
+ fc->comp_typ = CH_IT_EFI;
+ fc->flsh_addr = FLS_OFFSET_BOOT
+ + fi->cmp_hdr[CH_IT_BIOS].length
+ + fi->cmp_hdr[CH_IT_MAC].length;
+ fc->sgc.length = ch->length;
+ fc->sgc.cur_offset = fc->sgc_offset +
+ ch->image_offset;
+ break;
+
+ case FMTSK_WRTEFI:
+ /* The EFI image has been written - read and verify */
+ fc->task = FMTSK_READEFI;
+ fc->func = VDA_FLASH_READ;
+ fc->flsh_addr -= ch->length;
+ fc->cmp_len = ch->length;
+ fc->sgc.length = FM_BUF_SZ;
+ fc->sgc.cur_offset = fc->sgc_offset
+ + ((u8 *)fc->scratch -
+ (u8 *)fi);
+ break;
+
+ case FMTSK_READEFI:
+no_efi:
+ /*
+ * Mark the component header status for the image
+ * completed
+ */
+ ch->status = CH_STAT_SUCCESS;
+
+ /* The CFG image is written next */
+ ch = &fi->cmp_hdr[CH_IT_CFG];
+
+ if (ch->length == 0)
+ goto no_cfg;
+ fc->task = FMTSK_WRTCFG;
+ fc->func = VDA_FLASH_BEGINW;
+ fc->comp_typ = CH_IT_CFG;
+ fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
+ fc->sgc.length = ch->length;
+ fc->sgc.cur_offset = fc->sgc_offset +
+ ch->image_offset;
+ break;
+
+ case FMTSK_WRTCFG:
+ /* The CFG image has been written - read and verify */
+ fc->task = FMTSK_READCFG;
+ fc->func = VDA_FLASH_READ;
+ fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
+ fc->cmp_len = ch->length;
+ fc->sgc.length = FM_BUF_SZ;
+ fc->sgc.cur_offset = fc->sgc_offset
+ + ((u8 *)fc->scratch -
+ (u8 *)fi);
+ break;
+
+ case FMTSK_READCFG:
+no_cfg:
+ /*
+ * Mark the component header status for the image
+ * completed
+ */
+ ch->status = CH_STAT_SUCCESS;
+
+ /*
+ * The download is complete. If in degraded mode,
+ * attempt a chip reset.
+ */
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ esas2r_local_reset_adapter(a);
+
+ a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version;
+ esas2r_print_flash_rev(a);
+
+ /* Update the type of boot image on the card */
+ memcpy(a->image_type, fi->rel_version,
+ sizeof(fi->rel_version));
+ complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
+ return;
+ }
+
+ /* If verifying, don't try reading more than what's there */
+ if (fc->func == VDA_FLASH_READ
+ && fc->sgc.length > fc->cmp_len)
+ fc->sgc.length = fc->cmp_len;
+ }
+
+ /* Build the request to perform the next action */
+ if (!load_image(a, rq)) {
+error:
+ if (fc->comp_typ < fi->num_comps) {
+ ch = &fi->cmp_hdr[fc->comp_typ];
+ ch->status = CH_STAT_FAILED;
+ }
+
+ complete_fmapi_req(a, rq, FI_STAT_FAILED);
+ }
+}
+
+/* Determine the flash image adaptyp for this adapter */
+static u8 get_fi_adap_type(struct esas2r_adapter *a)
+{
+ u8 type;
+
+ /* use the device ID to get the correct adap_typ for this HBA */
+ switch (a->pcid->device) {
+ case ATTO_DID_INTEL_IOP348:
+ type = FI_AT_SUN_LAKE;
+ break;
+
+ case ATTO_DID_MV_88RC9580:
+ case ATTO_DID_MV_88RC9580TS:
+ case ATTO_DID_MV_88RC9580TSE:
+ case ATTO_DID_MV_88RC9580TL:
+ type = FI_AT_MV_9580;
+ break;
+
+ default:
+ type = FI_AT_UNKNWN;
+ break;
+ }
+
+ return type;
+}
+
+/* Size of config + copyright + flash_ver images, 0 for failure. */
+static u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver)
+{
+ u16 *pw = (u16 *)cfg - 1;
+ u32 sz = 0;
+ u32 len = length;
+
+ if (len == 0)
+ len = FM_BUF_SZ;
+
+ if (flash_ver)
+ *flash_ver = 0;
+
+ while (true) {
+ u16 type;
+ u16 size;
+
+ type = le16_to_cpu(*pw--);
+ size = le16_to_cpu(*pw--);
+
+ if (type != FBT_CPYR
+ && type != FBT_SETUP
+ && type != FBT_FLASH_VER)
+ break;
+
+ if (type == FBT_FLASH_VER
+ && flash_ver)
+ *flash_ver = le32_to_cpu(*(u32 *)(pw - 1));
+
+ sz += size + (2 * sizeof(u16));
+ pw -= size / sizeof(u16);
+
+ if (sz > len - (2 * sizeof(u16)))
+ break;
+ }
+
+ /* See if we are comparing the size to the specified length */
+ if (length && sz != length)
+ return 0;
+
+ return sz;
+}
+
+/* Verify that the boot image is valid */
+static u8 chk_boot(u8 *boot_img, u32 length)
+{
+ struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img;
+ u16 hdroffset = le16_to_cpu(bi->header_offset);
+ struct esas2r_boot_header *bh;
+
+ if (bi->signature != le16_to_cpu(0xaa55)
+ || (long)hdroffset >
+ (long)(65536L - sizeof(struct esas2r_boot_header))
+ || (hdroffset & 3)
+ || (hdroffset < sizeof(struct esas2r_boot_image))
+ || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length))
+ return 0xff;
+
+ bh = (struct esas2r_boot_header *)((char *)bi + hdroffset);
+
+ if (bh->signature[0] != 'P'
+ || bh->signature[1] != 'C'
+ || bh->signature[2] != 'I'
+ || bh->signature[3] != 'R'
+ || le16_to_cpu(bh->struct_length) <
+ (u16)sizeof(struct esas2r_boot_header)
+ || bh->class_code[2] != 0x01
+ || bh->class_code[1] != 0x04
+ || bh->class_code[0] != 0x00
+ || (bh->code_type != CODE_TYPE_PC
+ && bh->code_type != CODE_TYPE_OPEN
+ && bh->code_type != CODE_TYPE_EFI))
+ return 0xff;
+
+ return bh->code_type;
+}
+
+/* The sum of all the WORDS of the image */
+static u16 calc_fi_checksum(struct esas2r_flash_context *fc)
+{
+ struct esas2r_flash_img *fi = fc->fi;
+ u16 cksum;
+ u32 len;
+ u16 *pw;
+
+ for (len = (fi->length - fc->fi_hdr_len) / 2,
+ pw = (u16 *)((u8 *)fi + fc->fi_hdr_len),
+ cksum = 0;
+ len;
+ len--, pw++)
+ cksum = cksum + le16_to_cpu(*pw);
+
+ return cksum;
+}
+
+/*
+ * Verify the flash image structure. The following verifications will
+ * be performed:
+ * 1) verify the fi_version is correct
+ * 2) verify the checksum of the entire image.
+ * 3) validate the adap_typ, action and length fields.
+ * 4) validate each component header. check the img_type and
+ * length fields
+ * 5) validate each component image. validate signatures and
+ * local checksums
+ */
+static bool verify_fi(struct esas2r_adapter *a,
+ struct esas2r_flash_context *fc)
+{
+ struct esas2r_flash_img *fi = fc->fi;
+ u8 type;
+ bool imgerr;
+ u16 i;
+ u32 len;
+ struct esas2r_component_header *ch;
+
+ /* Verify the length - length must even since we do a word checksum */
+ len = fi->length;
+
+ if ((len & 1)
+ || len < fc->fi_hdr_len) {
+ fi->status = FI_STAT_LENGTH;
+ return false;
+ }
+
+ /* Get adapter type and verify type in flash image */
+ type = get_fi_adap_type(a);
+ if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) {
+ fi->status = FI_STAT_ADAPTYP;
+ return false;
+ }
+
+ /*
+ * Loop through each component and verify the img_type and length
+ * fields. Keep a running count of the sizes sooze we can verify total
+ * size to additive size.
+ */
+ imgerr = false;
+
+ for (i = 0, len = 0, ch = fi->cmp_hdr;
+ i < fi->num_comps;
+ i++, ch++) {
+ bool cmperr = false;
+
+ /*
+ * Verify that the component header has the same index as the
+ * image type. The headers must be ordered correctly
+ */
+ if (i != ch->img_type) {
+ imgerr = true;
+ ch->status = CH_STAT_INVALID;
+ continue;
+ }
+
+ switch (ch->img_type) {
+ case CH_IT_BIOS:
+ type = CODE_TYPE_PC;
+ break;
+
+ case CH_IT_MAC:
+ type = CODE_TYPE_OPEN;
+ break;
+
+ case CH_IT_EFI:
+ type = CODE_TYPE_EFI;
+ break;
+ }
+
+ switch (ch->img_type) {
+ case CH_IT_FW:
+ case CH_IT_NVR:
+ break;
+
+ case CH_IT_BIOS:
+ case CH_IT_MAC:
+ case CH_IT_EFI:
+ if (ch->length & 0x1ff)
+ cmperr = true;
+
+ /* Test if component image is present */
+ if (ch->length == 0)
+ break;
+
+ /* Image is present - verify the image */
+ if (chk_boot((u8 *)fi + ch->image_offset, ch->length)
+ != type)
+ cmperr = true;
+
+ break;
+
+ case CH_IT_CFG:
+
+ /* Test if component image is present */
+ if (ch->length == 0) {
+ cmperr = true;
+ break;
+ }
+
+ /* Image is present - verify the image */
+ if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length,
+ ch->length, NULL))
+ cmperr = true;
+
+ break;
+
+ default:
+
+ fi->status = FI_STAT_UNKNOWN;
+ return false;
+ }
+
+ if (cmperr) {
+ imgerr = true;
+ ch->status = CH_STAT_INVALID;
+ } else {
+ ch->status = CH_STAT_PENDING;
+ len += ch->length;
+ }
+ }
+
+ if (imgerr) {
+ fi->status = FI_STAT_MISSING;
+ return false;
+ }
+
+ /* Compare fi->length to the sum of ch->length fields */
+ if (len != fi->length - fc->fi_hdr_len) {
+ fi->status = FI_STAT_LENGTH;
+ return false;
+ }
+
+ /* Compute the checksum - it should come out zero */
+ if (fi->checksum != calc_fi_checksum(fc)) {
+ fi->status = FI_STAT_CHKSUM;
+ return false;
+ }
+
+ return true;
+}
+
+/* Fill in the FS IOCTL response data from a completed request. */
+static void esas2r_complete_fs_ioctl(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_ioctl_fs *fs =
+ (struct esas2r_ioctl_fs *)rq->interrupt_cx;
+
+ if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
+ esas2r_enable_heartbeat(a);
+
+ fs->driver_error = rq->req_stat;
+
+ if (fs->driver_error == RS_SUCCESS)
+ fs->status = ATTO_STS_SUCCESS;
+ else
+ fs->status = ATTO_STS_FAILED;
+}
+
+/* Prepare an FS IOCTL request to be sent to the firmware. */
+bool esas2r_process_fs_ioctl(struct esas2r_adapter *a,
+ struct esas2r_ioctl_fs *fs,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc)
+{
+ u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func);
+ struct esas2r_ioctlfs_command *fsc = &fs->command;
+ u8 func = 0;
+ u32 datalen;
+
+ fs->status = ATTO_STS_FAILED;
+ fs->driver_error = RS_PENDING;
+
+ if (fs->version > ESAS2R_FS_VER) {
+ fs->status = ATTO_STS_INV_VERSION;
+ return false;
+ }
+
+ if (fsc->command >= cmdcnt) {
+ fs->status = ATTO_STS_INV_FUNC;
+ return false;
+ }
+
+ func = cmd_to_fls_func[fsc->command];
+ if (func == 0xFF) {
+ fs->status = ATTO_STS_INV_FUNC;
+ return false;
+ }
+
+ if (fsc->command != ESAS2R_FS_CMD_CANCEL) {
+ if ((a->pcid->device != ATTO_DID_MV_88RC9580
+ || fs->adap_type != ESAS2R_FS_AT_ESASRAID2)
+ && (a->pcid->device != ATTO_DID_MV_88RC9580TS
+ || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2)
+ && (a->pcid->device != ATTO_DID_MV_88RC9580TSE
+ || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E)
+ && (a->pcid->device != ATTO_DID_MV_88RC9580TL
+ || fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) {
+ fs->status = ATTO_STS_INV_ADAPTER;
+ return false;
+ }
+
+ if (fs->driver_ver > ESAS2R_FS_DRVR_VER) {
+ fs->status = ATTO_STS_INV_DRVR_VER;
+ return false;
+ }
+ }
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
+ fs->status = ATTO_STS_DEGRADED;
+ return false;
+ }
+
+ rq->interrupt_cb = esas2r_complete_fs_ioctl;
+ rq->interrupt_cx = fs;
+ datalen = le32_to_cpu(fsc->length);
+ esas2r_build_flash_req(a,
+ rq,
+ func,
+ fsc->checksum,
+ le32_to_cpu(fsc->flash_addr),
+ datalen);
+
+ if (func == VDA_FLASH_WRITE
+ || func == VDA_FLASH_READ) {
+ if (datalen == 0) {
+ fs->status = ATTO_STS_INV_FUNC;
+ return false;
+ }
+
+ esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge);
+ sgc->length = datalen;
+
+ if (!esas2r_build_sg_list(a, rq, sgc)) {
+ fs->status = ATTO_STS_OUT_OF_RSRC;
+ return false;
+ }
+ }
+
+ if (func == VDA_FLASH_COMMIT)
+ esas2r_disable_heartbeat(a);
+
+ esas2r_start_request(a, rq);
+
+ return true;
+}
+
+static bool esas2r_flash_access(struct esas2r_adapter *a, u32 function)
+{
+ u32 starttime;
+ u32 timeout;
+ u32 intstat;
+ u32 doorbell;
+
+ /* Disable chip interrupts awhile */
+ if (function == DRBL_FLASH_REQ)
+ esas2r_disable_chip_interrupts(a);
+
+ /* Issue the request to the firmware */
+ esas2r_write_register_dword(a, MU_DOORBELL_IN, function);
+
+ /* Now wait for the firmware to process it */
+ starttime = jiffies_to_msecs(jiffies);
+
+ if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
+ test_bit(AF_DISC_PENDING, &a->flags))
+ timeout = 40000;
+ else
+ timeout = 5000;
+
+ while (true) {
+ intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
+
+ if (intstat & MU_INTSTAT_DRBL) {
+ /* Got a doorbell interrupt. Check for the function */
+ doorbell =
+ esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+ doorbell);
+ if (doorbell & function)
+ break;
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if ((jiffies_to_msecs(jiffies) - starttime) > timeout) {
+ /*
+ * Iimeout. If we were requesting flash access,
+ * indicate we are done so the firmware knows we gave
+ * up. If this was a REQ, we also need to re-enable
+ * chip interrupts.
+ */
+ if (function == DRBL_FLASH_REQ) {
+ esas2r_hdebug("flash access timeout");
+ esas2r_write_register_dword(a, MU_DOORBELL_IN,
+ DRBL_FLASH_DONE);
+ esas2r_enable_chip_interrupts(a);
+ } else {
+ esas2r_hdebug("flash release timeout");
+ }
+
+ return false;
+ }
+ }
+
+ /* if we're done, re-enable chip interrupts */
+ if (function == DRBL_FLASH_DONE)
+ esas2r_enable_chip_interrupts(a);
+
+ return true;
+}
+
+#define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE)
+
+bool esas2r_read_flash_block(struct esas2r_adapter *a,
+ void *to,
+ u32 from,
+ u32 size)
+{
+ u8 *end = (u8 *)to;
+
+ /* Try to acquire access to the flash */
+ if (!esas2r_flash_access(a, DRBL_FLASH_REQ))
+ return false;
+
+ while (size) {
+ u32 len;
+ u32 offset;
+ u32 iatvr;
+
+ if (test_bit(AF2_SERIAL_FLASH, &a->flags2))
+ iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE);
+ else
+ iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE);
+
+ esas2r_map_data_window(a, iatvr);
+ offset = from & (WINDOW_SIZE - 1);
+ len = size;
+
+ if (len > WINDOW_SIZE - offset)
+ len = WINDOW_SIZE - offset;
+
+ from += len;
+ size -= len;
+
+ while (len--) {
+ *end++ = esas2r_read_data_byte(a, offset);
+ offset++;
+ }
+ }
+
+ /* Release flash access */
+ esas2r_flash_access(a, DRBL_FLASH_DONE);
+ return true;
+}
+
+bool esas2r_read_flash_rev(struct esas2r_adapter *a)
+{
+ u8 bytes[256];
+ u16 *pw;
+ u16 *pwstart;
+ u16 type;
+ u16 size;
+ u32 sz;
+
+ sz = sizeof(bytes);
+ pw = (u16 *)(bytes + sz);
+ pwstart = (u16 *)bytes + 2;
+
+ if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz))
+ goto invalid_rev;
+
+ while (pw >= pwstart) {
+ pw--;
+ type = le16_to_cpu(*pw);
+ pw--;
+ size = le16_to_cpu(*pw);
+ pw -= size / 2;
+
+ if (type == FBT_CPYR
+ || type == FBT_SETUP
+ || pw < pwstart)
+ continue;
+
+ if (type == FBT_FLASH_VER)
+ a->flash_ver = le32_to_cpu(*(u32 *)pw);
+
+ break;
+ }
+
+invalid_rev:
+ return esas2r_print_flash_rev(a);
+}
+
+bool esas2r_print_flash_rev(struct esas2r_adapter *a)
+{
+ u16 year = LOWORD(a->flash_ver);
+ u8 day = LOBYTE(HIWORD(a->flash_ver));
+ u8 month = HIBYTE(HIWORD(a->flash_ver));
+
+ if (day == 0
+ || month == 0
+ || day > 31
+ || month > 12
+ || year < 2006
+ || year > 9999) {
+ strcpy(a->flash_rev, "not found");
+ a->flash_ver = 0;
+ return false;
+ }
+
+ sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year);
+ esas2r_hdebug("flash version: %s", a->flash_rev);
+ return true;
+}
+
+/*
+ * Find the type of boot image type that is currently in the flash.
+ * The chip only has a 64 KB PCI-e expansion ROM
+ * size so only one image can be flashed at a time.
+ */
+bool esas2r_read_image_type(struct esas2r_adapter *a)
+{
+ u8 bytes[256];
+ struct esas2r_boot_image *bi;
+ struct esas2r_boot_header *bh;
+ u32 sz;
+ u32 len;
+ u32 offset;
+
+ /* Start at the base of the boot images and look for a valid image */
+ sz = sizeof(bytes);
+ len = FLS_LENGTH_BOOT;
+ offset = 0;
+
+ while (true) {
+ if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT +
+ offset,
+ sz))
+ goto invalid_rev;
+
+ bi = (struct esas2r_boot_image *)bytes;
+ bh = (struct esas2r_boot_header *)((u8 *)bi +
+ le16_to_cpu(
+ bi->header_offset));
+ if (bi->signature != cpu_to_le16(0xAA55))
+ goto invalid_rev;
+
+ if (bh->code_type == CODE_TYPE_PC) {
+ strcpy(a->image_type, "BIOS");
+
+ return true;
+ } else if (bh->code_type == CODE_TYPE_EFI) {
+ struct esas2r_efi_image *ei;
+
+ /*
+ * So we have an EFI image. There are several types
+ * so see which architecture we have.
+ */
+ ei = (struct esas2r_efi_image *)bytes;
+
+ switch (le16_to_cpu(ei->machine_type)) {
+ case EFI_MACHINE_IA32:
+ strcpy(a->image_type, "EFI 32-bit");
+ return true;
+
+ case EFI_MACHINE_IA64:
+ strcpy(a->image_type, "EFI itanium");
+ return true;
+
+ case EFI_MACHINE_X64:
+ strcpy(a->image_type, "EFI 64-bit");
+ return true;
+
+ case EFI_MACHINE_EBC:
+ strcpy(a->image_type, "EFI EBC");
+ return true;
+
+ default:
+ goto invalid_rev;
+ }
+ } else {
+ u32 thislen;
+
+ /* jump to the next image */
+ thislen = (u32)le16_to_cpu(bh->image_length) * 512;
+ if (thislen == 0
+ || thislen + offset > len
+ || bh->indicator == INDICATOR_LAST)
+ break;
+
+ offset += thislen;
+ }
+ }
+
+invalid_rev:
+ strcpy(a->image_type, "no boot images");
+ return false;
+}
+
+/*
+ * Read and validate current NVRAM parameters by accessing
+ * physical NVRAM directly. if currently stored parameters are
+ * invalid, use the defaults.
+ */
+bool esas2r_nvram_read_direct(struct esas2r_adapter *a)
+{
+ bool result;
+
+ if (down_interruptible(&a->nvram_semaphore))
+ return false;
+
+ if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR,
+ sizeof(struct esas2r_sas_nvram))) {
+ esas2r_hdebug("NVRAM read failed, using defaults");
+ up(&a->nvram_semaphore);
+ return false;
+ }
+
+ result = esas2r_nvram_validate(a);
+
+ up(&a->nvram_semaphore);
+
+ return result;
+}
+
+/* Interrupt callback to process NVRAM completions. */
+static void esas2r_nvram_callback(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct atto_vda_flash_req *vrq = &rq->vrq->flash;
+
+ if (rq->req_stat == RS_SUCCESS) {
+ /* last request was successful. see what to do now. */
+
+ switch (vrq->sub_func) {
+ case VDA_FLASH_BEGINW:
+ vrq->sub_func = VDA_FLASH_WRITE;
+ rq->req_stat = RS_PENDING;
+ break;
+
+ case VDA_FLASH_WRITE:
+ vrq->sub_func = VDA_FLASH_COMMIT;
+ rq->req_stat = RS_PENDING;
+ break;
+
+ case VDA_FLASH_READ:
+ esas2r_nvram_validate(a);
+ break;
+
+ case VDA_FLASH_COMMIT:
+ default:
+ break;
+ }
+ }
+
+ if (rq->req_stat != RS_PENDING) {
+ /* update the NVRAM state */
+ if (rq->req_stat == RS_SUCCESS)
+ set_bit(AF_NVR_VALID, &a->flags);
+ else
+ clear_bit(AF_NVR_VALID, &a->flags);
+
+ esas2r_enable_heartbeat(a);
+
+ up(&a->nvram_semaphore);
+ }
+}
+
+/*
+ * Write the contents of nvram to the adapter's physical NVRAM.
+ * The cached copy of the NVRAM is also updated.
+ */
+bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq,
+ struct esas2r_sas_nvram *nvram)
+{
+ struct esas2r_sas_nvram *n = nvram;
+ u8 sas_address_bytes[8];
+ u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0];
+ struct atto_vda_flash_req *vrq = &rq->vrq->flash;
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return false;
+
+ if (down_interruptible(&a->nvram_semaphore))
+ return false;
+
+ if (n == NULL)
+ n = a->nvram;
+
+ /* check the validity of the settings */
+ if (n->version > SASNVR_VERSION) {
+ up(&a->nvram_semaphore);
+ return false;
+ }
+
+ memcpy(&sas_address_bytes[0], n->sas_addr, 8);
+
+ if (sas_address_bytes[0] != 0x50
+ || sas_address_bytes[1] != 0x01
+ || sas_address_bytes[2] != 0x08
+ || (sas_address_bytes[3] & 0xF0) != 0x60
+ || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) {
+ up(&a->nvram_semaphore);
+ return false;
+ }
+
+ if (n->spin_up_delay > SASNVR_SPINUP_MAX)
+ n->spin_up_delay = SASNVR_SPINUP_MAX;
+
+ n->version = SASNVR_VERSION;
+ n->checksum = n->checksum - esas2r_nvramcalc_cksum(n);
+ memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram));
+
+ /* write the NVRAM */
+ n = a->nvram;
+ esas2r_disable_heartbeat(a);
+
+ esas2r_build_flash_req(a,
+ rq,
+ VDA_FLASH_BEGINW,
+ esas2r_nvramcalc_xor_cksum(n),
+ FLS_OFFSET_NVR,
+ sizeof(struct esas2r_sas_nvram));
+
+ if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
+
+ vrq->data.sge[0].length =
+ cpu_to_le32(SGE_LAST |
+ sizeof(struct esas2r_sas_nvram));
+ vrq->data.sge[0].address = cpu_to_le64(
+ a->uncached_phys + (u64)((u8 *)n - a->uncached));
+ } else {
+ vrq->data.prde[0].ctl_len =
+ cpu_to_le32(sizeof(struct esas2r_sas_nvram));
+ vrq->data.prde[0].address = cpu_to_le64(
+ a->uncached_phys
+ + (u64)((u8 *)n - a->uncached));
+ }
+ rq->interrupt_cb = esas2r_nvram_callback;
+ esas2r_start_request(a, rq);
+ return true;
+}
+
+/* Validate the cached NVRAM. if the NVRAM is invalid, load the defaults. */
+bool esas2r_nvram_validate(struct esas2r_adapter *a)
+{
+ struct esas2r_sas_nvram *n = a->nvram;
+ bool rslt = false;
+
+ if (n->signature[0] != 'E'
+ || n->signature[1] != 'S'
+ || n->signature[2] != 'A'
+ || n->signature[3] != 'S') {
+ esas2r_hdebug("invalid NVRAM signature");
+ } else if (esas2r_nvramcalc_cksum(n)) {
+ esas2r_hdebug("invalid NVRAM checksum");
+ } else if (n->version > SASNVR_VERSION) {
+ esas2r_hdebug("invalid NVRAM version");
+ } else {
+ set_bit(AF_NVR_VALID, &a->flags);
+ rslt = true;
+ }
+
+ if (rslt == false) {
+ esas2r_hdebug("using defaults");
+ esas2r_nvram_set_defaults(a);
+ }
+
+ return rslt;
+}
+
+/*
+ * Set the cached NVRAM to defaults. note that this function sets the default
+ * NVRAM when it has been determined that the physical NVRAM is invalid.
+ * In this case, the SAS address is fabricated.
+ */
+void esas2r_nvram_set_defaults(struct esas2r_adapter *a)
+{
+ struct esas2r_sas_nvram *n = a->nvram;
+ u32 time = jiffies_to_msecs(jiffies);
+
+ clear_bit(AF_NVR_VALID, &a->flags);
+ *n = default_sas_nvram;
+ n->sas_addr[3] |= 0x0F;
+ n->sas_addr[4] = HIBYTE(LOWORD(time));
+ n->sas_addr[5] = LOBYTE(LOWORD(time));
+ n->sas_addr[6] = a->pcid->bus->number;
+ n->sas_addr[7] = a->pcid->devfn;
+}
+
+void esas2r_nvram_get_defaults(struct esas2r_adapter *a,
+ struct esas2r_sas_nvram *nvram)
+{
+ u8 sas_addr[8];
+
+ /*
+ * in case we are copying the defaults into the adapter, copy the SAS
+ * address out first.
+ */
+ memcpy(&sas_addr[0], a->nvram->sas_addr, 8);
+ *nvram = default_sas_nvram;
+ memcpy(&nvram->sas_addr[0], &sas_addr[0], 8);
+}
+
+bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi,
+ struct esas2r_request *rq, struct esas2r_sg_context *sgc)
+{
+ struct esas2r_flash_context *fc = &a->flash_context;
+ u8 j;
+ struct esas2r_component_header *ch;
+
+ if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) {
+ /* flag was already set */
+ fi->status = FI_STAT_BUSY;
+ return false;
+ }
+
+ memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context));
+ sgc = &fc->sgc;
+ fc->fi = fi;
+ fc->sgc_offset = sgc->cur_offset;
+ rq->req_stat = RS_SUCCESS;
+ rq->interrupt_cx = fc;
+
+ switch (fi->fi_version) {
+ case FI_VERSION_1:
+ fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf;
+ fc->num_comps = FI_NUM_COMPS_V1;
+ fc->fi_hdr_len = sizeof(struct esas2r_flash_img);
+ break;
+
+ default:
+ return complete_fmapi_req(a, rq, FI_STAT_IMG_VER);
+ }
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return complete_fmapi_req(a, rq, FI_STAT_DEGRADED);
+
+ switch (fi->action) {
+ case FI_ACT_DOWN: /* Download the components */
+ /* Verify the format of the flash image */
+ if (!verify_fi(a, fc))
+ return complete_fmapi_req(a, rq, fi->status);
+
+ /* Adjust the BIOS fields that are dependent on the HBA */
+ ch = &fi->cmp_hdr[CH_IT_BIOS];
+
+ if (ch->length)
+ fix_bios(a, fi);
+
+ /* Adjust the EFI fields that are dependent on the HBA */
+ ch = &fi->cmp_hdr[CH_IT_EFI];
+
+ if (ch->length)
+ fix_efi(a, fi);
+
+ /*
+ * Since the image was just modified, compute the checksum on
+ * the modified image. First update the CRC for the composite
+ * expansion ROM image.
+ */
+ fi->checksum = calc_fi_checksum(fc);
+
+ /* Disable the heartbeat */
+ esas2r_disable_heartbeat(a);
+
+ /* Now start up the download sequence */
+ fc->task = FMTSK_ERASE_BOOT;
+ fc->func = VDA_FLASH_BEGINW;
+ fc->comp_typ = CH_IT_CFG;
+ fc->flsh_addr = FLS_OFFSET_BOOT;
+ fc->sgc.length = FLS_LENGTH_BOOT;
+ fc->sgc.cur_offset = NULL;
+
+ /* Setup the callback address */
+ fc->interrupt_cb = fw_download_proc;
+ break;
+
+ case FI_ACT_UPSZ: /* Get upload sizes */
+ fi->adap_typ = get_fi_adap_type(a);
+ fi->flags = 0;
+ fi->num_comps = fc->num_comps;
+ fi->length = fc->fi_hdr_len;
+
+ /* Report the type of boot image in the rel_version string */
+ memcpy(fi->rel_version, a->image_type,
+ sizeof(fi->rel_version));
+
+ /* Build the component headers */
+ for (j = 0, ch = fi->cmp_hdr;
+ j < fi->num_comps;
+ j++, ch++) {
+ ch->img_type = j;
+ ch->status = CH_STAT_PENDING;
+ ch->length = 0;
+ ch->version = 0xffffffff;
+ ch->image_offset = 0;
+ ch->pad[0] = 0;
+ ch->pad[1] = 0;
+ }
+
+ if (a->flash_ver != 0) {
+ fi->cmp_hdr[CH_IT_BIOS].version =
+ fi->cmp_hdr[CH_IT_MAC].version =
+ fi->cmp_hdr[CH_IT_EFI].version =
+ fi->cmp_hdr[CH_IT_CFG].version
+ = a->flash_ver;
+
+ fi->cmp_hdr[CH_IT_BIOS].status =
+ fi->cmp_hdr[CH_IT_MAC].status =
+ fi->cmp_hdr[CH_IT_EFI].status =
+ fi->cmp_hdr[CH_IT_CFG].status =
+ CH_STAT_SUCCESS;
+
+ return complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
+ }
+
+ fallthrough;
+
+ case FI_ACT_UP: /* Upload the components */
+ default:
+ return complete_fmapi_req(a, rq, FI_STAT_INVALID);
+ }
+
+ /*
+ * If we make it here, fc has been setup to do the first task. Call
+ * load_image to format the request, start it, and get out. The
+ * interrupt code will call the callback when the first message is
+ * complete.
+ */
+ if (!load_image(a, rq))
+ return complete_fmapi_req(a, rq, FI_STAT_FAILED);
+
+ esas2r_start_request(a, rq);
+
+ return true;
+}
diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c
new file mode 100644
index 000000000000..04a07fe57be2
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_init.c
@@ -0,0 +1,1698 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_init.c
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)mpt3sas/mpt3sas_trigger_diag.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+static bool esas2r_initmem_alloc(struct esas2r_adapter *a,
+ struct esas2r_mem_desc *mem_desc,
+ u32 align)
+{
+ mem_desc->esas2r_param = mem_desc->size + align;
+ mem_desc->virt_addr = NULL;
+ mem_desc->phys_addr = 0;
+ mem_desc->esas2r_data = dma_alloc_coherent(&a->pcid->dev,
+ (size_t)mem_desc->
+ esas2r_param,
+ (dma_addr_t *)&mem_desc->
+ phys_addr,
+ GFP_KERNEL);
+
+ if (mem_desc->esas2r_data == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "failed to allocate %lu bytes of consistent memory!",
+ (long
+ unsigned
+ int)mem_desc->esas2r_param);
+ return false;
+ }
+
+ mem_desc->virt_addr = PTR_ALIGN(mem_desc->esas2r_data, align);
+ mem_desc->phys_addr = ALIGN(mem_desc->phys_addr, align);
+ memset(mem_desc->virt_addr, 0, mem_desc->size);
+ return true;
+}
+
+static void esas2r_initmem_free(struct esas2r_adapter *a,
+ struct esas2r_mem_desc *mem_desc)
+{
+ if (mem_desc->virt_addr == NULL)
+ return;
+
+ /*
+ * Careful! phys_addr and virt_addr may have been adjusted from the
+ * original allocation in order to return the desired alignment. That
+ * means we have to use the original address (in esas2r_data) and size
+ * (esas2r_param) and calculate the original physical address based on
+ * the difference between the requested and actual allocation size.
+ */
+ if (mem_desc->phys_addr) {
+ int unalign = ((u8 *)mem_desc->virt_addr) -
+ ((u8 *)mem_desc->esas2r_data);
+
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)mem_desc->esas2r_param,
+ mem_desc->esas2r_data,
+ (dma_addr_t)(mem_desc->phys_addr - unalign));
+ } else {
+ kfree(mem_desc->esas2r_data);
+ }
+
+ mem_desc->virt_addr = NULL;
+}
+
+static bool alloc_vda_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_mem_desc *memdesc = kzalloc(
+ sizeof(struct esas2r_mem_desc), GFP_KERNEL);
+
+ if (memdesc == NULL) {
+ esas2r_hdebug("could not alloc mem for vda request memdesc\n");
+ return false;
+ }
+
+ memdesc->size = sizeof(union atto_vda_req) +
+ ESAS2R_DATA_BUF_LEN;
+
+ if (!esas2r_initmem_alloc(a, memdesc, 256)) {
+ esas2r_hdebug("could not alloc mem for vda request\n");
+ kfree(memdesc);
+ return false;
+ }
+
+ a->num_vrqs++;
+ list_add(&memdesc->next_desc, &a->vrq_mds_head);
+
+ rq->vrq_md = memdesc;
+ rq->vrq = (union atto_vda_req *)memdesc->virt_addr;
+ rq->vrq->scsi.handle = a->num_vrqs;
+
+ return true;
+}
+
+static void esas2r_unmap_regions(struct esas2r_adapter *a)
+{
+ if (a->regs)
+ iounmap((void __iomem *)a->regs);
+
+ a->regs = NULL;
+
+ pci_release_region(a->pcid, 2);
+
+ if (a->data_window)
+ iounmap((void __iomem *)a->data_window);
+
+ a->data_window = NULL;
+
+ pci_release_region(a->pcid, 0);
+}
+
+static int esas2r_map_regions(struct esas2r_adapter *a)
+{
+ int error;
+
+ a->regs = NULL;
+ a->data_window = NULL;
+
+ error = pci_request_region(a->pcid, 2, a->name);
+ if (error != 0) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "pci_request_region(2) failed, error %d",
+ error);
+
+ return error;
+ }
+
+ a->regs = (void __force *)ioremap(pci_resource_start(a->pcid, 2),
+ pci_resource_len(a->pcid, 2));
+ if (a->regs == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "ioremap failed for regs mem region\n");
+ pci_release_region(a->pcid, 2);
+ return -EFAULT;
+ }
+
+ error = pci_request_region(a->pcid, 0, a->name);
+ if (error != 0) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "pci_request_region(2) failed, error %d",
+ error);
+ esas2r_unmap_regions(a);
+ return error;
+ }
+
+ a->data_window = (void __force *)ioremap(pci_resource_start(a->pcid,
+ 0),
+ pci_resource_len(a->pcid, 0));
+ if (a->data_window == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "ioremap failed for data_window mem region\n");
+ esas2r_unmap_regions(a);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static void esas2r_setup_interrupts(struct esas2r_adapter *a, int intr_mode)
+{
+ int i;
+
+ /* Set up interrupt mode based on the requested value */
+ switch (intr_mode) {
+ case INTR_MODE_LEGACY:
+use_legacy_interrupts:
+ a->intr_mode = INTR_MODE_LEGACY;
+ break;
+
+ case INTR_MODE_MSI:
+ i = pci_enable_msi(a->pcid);
+ if (i != 0) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "failed to enable MSI for adapter %d, "
+ "falling back to legacy interrupts "
+ "(err=%d)", a->index,
+ i);
+ goto use_legacy_interrupts;
+ }
+ a->intr_mode = INTR_MODE_MSI;
+ set_bit(AF2_MSI_ENABLED, &a->flags2);
+ break;
+
+
+ default:
+ esas2r_log(ESAS2R_LOG_WARN,
+ "unknown interrupt_mode %d requested, "
+ "falling back to legacy interrupt",
+ interrupt_mode);
+ goto use_legacy_interrupts;
+ }
+}
+
+static void esas2r_claim_interrupts(struct esas2r_adapter *a)
+{
+ unsigned long flags = 0;
+
+ if (a->intr_mode == INTR_MODE_LEGACY)
+ flags |= IRQF_SHARED;
+
+ esas2r_log(ESAS2R_LOG_INFO,
+ "esas2r_claim_interrupts irq=%d (%p, %s, %lx)",
+ a->pcid->irq, a, a->name, flags);
+
+ if (request_irq(a->pcid->irq,
+ (a->intr_mode ==
+ INTR_MODE_LEGACY) ? esas2r_interrupt :
+ esas2r_msi_interrupt,
+ flags,
+ a->name,
+ a)) {
+ esas2r_log(ESAS2R_LOG_CRIT, "unable to request IRQ %02X",
+ a->pcid->irq);
+ return;
+ }
+
+ set_bit(AF2_IRQ_CLAIMED, &a->flags2);
+ esas2r_log(ESAS2R_LOG_INFO,
+ "claimed IRQ %d flags: 0x%lx",
+ a->pcid->irq, flags);
+}
+
+int esas2r_init_adapter(struct Scsi_Host *host, struct pci_dev *pcid,
+ int index)
+{
+ struct esas2r_adapter *a;
+ u64 bus_addr = 0;
+ int i;
+ void *next_uncached;
+ struct esas2r_request *first_request, *last_request;
+ bool dma64 = false;
+
+ if (index >= MAX_ADAPTERS) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "tried to init invalid adapter index %u!",
+ index);
+ return 0;
+ }
+
+ if (esas2r_adapters[index]) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "tried to init existing adapter index %u!",
+ index);
+ return 0;
+ }
+
+ a = (struct esas2r_adapter *)host->hostdata;
+ memset(a, 0, sizeof(struct esas2r_adapter));
+ a->pcid = pcid;
+ a->host = host;
+
+ if (sizeof(dma_addr_t) > 4 &&
+ dma_get_required_mask(&pcid->dev) > DMA_BIT_MASK(32) &&
+ !dma_set_mask_and_coherent(&pcid->dev, DMA_BIT_MASK(64)))
+ dma64 = true;
+
+ if (!dma64 && dma_set_mask_and_coherent(&pcid->dev, DMA_BIT_MASK(32))) {
+ esas2r_log(ESAS2R_LOG_CRIT, "failed to set DMA mask");
+ esas2r_kill_adapter(index);
+ return 0;
+ }
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &pcid->dev,
+ "%s-bit PCI addressing enabled\n", dma64 ? "64" : "32");
+
+ esas2r_adapters[index] = a;
+ sprintf(a->name, ESAS2R_DRVR_NAME "_%02d", index);
+ esas2r_debug("new adapter %p, name %s", a, a->name);
+ spin_lock_init(&a->request_lock);
+ spin_lock_init(&a->fw_event_lock);
+ mutex_init(&a->fm_api_mutex);
+ mutex_init(&a->fs_api_mutex);
+ sema_init(&a->nvram_semaphore, 1);
+
+ esas2r_fw_event_off(a);
+ a->fw_event_q =
+ alloc_ordered_workqueue("esas2r/%d", WQ_MEM_RECLAIM, a->index);
+
+ init_waitqueue_head(&a->buffered_ioctl_waiter);
+ init_waitqueue_head(&a->nvram_waiter);
+ init_waitqueue_head(&a->fm_api_waiter);
+ init_waitqueue_head(&a->fs_api_waiter);
+ init_waitqueue_head(&a->vda_waiter);
+
+ INIT_LIST_HEAD(&a->general_req.req_list);
+ INIT_LIST_HEAD(&a->active_list);
+ INIT_LIST_HEAD(&a->defer_list);
+ INIT_LIST_HEAD(&a->free_sg_list_head);
+ INIT_LIST_HEAD(&a->avail_request);
+ INIT_LIST_HEAD(&a->vrq_mds_head);
+ INIT_LIST_HEAD(&a->fw_event_list);
+
+ first_request = (struct esas2r_request *)((u8 *)(a + 1));
+
+ for (last_request = first_request, i = 1; i < num_requests;
+ last_request++, i++) {
+ INIT_LIST_HEAD(&last_request->req_list);
+ list_add_tail(&last_request->comp_list, &a->avail_request);
+ if (!alloc_vda_req(a, last_request)) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "failed to allocate a VDA request!");
+ esas2r_kill_adapter(index);
+ return 0;
+ }
+ }
+
+ esas2r_debug("requests: %p to %p (%d, %d)", first_request,
+ last_request,
+ sizeof(*first_request),
+ num_requests);
+
+ if (esas2r_map_regions(a) != 0) {
+ esas2r_log(ESAS2R_LOG_CRIT, "could not map PCI regions!");
+ esas2r_kill_adapter(index);
+ return 0;
+ }
+
+ a->index = index;
+
+ /* interrupts will be disabled until we are done with init */
+ atomic_inc(&a->dis_ints_cnt);
+ atomic_inc(&a->disable_cnt);
+ set_bit(AF_CHPRST_PENDING, &a->flags);
+ set_bit(AF_DISC_PENDING, &a->flags);
+ set_bit(AF_FIRST_INIT, &a->flags);
+ set_bit(AF_LEGACY_SGE_MODE, &a->flags);
+
+ a->init_msg = ESAS2R_INIT_MSG_START;
+ a->max_vdareq_size = 128;
+ a->build_sgl = esas2r_build_sg_list_sge;
+
+ esas2r_setup_interrupts(a, interrupt_mode);
+
+ a->uncached_size = esas2r_get_uncached_size(a);
+ a->uncached = dma_alloc_coherent(&pcid->dev,
+ (size_t)a->uncached_size,
+ (dma_addr_t *)&bus_addr,
+ GFP_KERNEL);
+ if (a->uncached == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "failed to allocate %d bytes of consistent memory!",
+ a->uncached_size);
+ esas2r_kill_adapter(index);
+ return 0;
+ }
+
+ a->uncached_phys = bus_addr;
+
+ esas2r_debug("%d bytes uncached memory allocated @ %p (%x:%x)",
+ a->uncached_size,
+ a->uncached,
+ upper_32_bits(bus_addr),
+ lower_32_bits(bus_addr));
+ memset(a->uncached, 0, a->uncached_size);
+ next_uncached = a->uncached;
+
+ if (!esas2r_init_adapter_struct(a,
+ &next_uncached)) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "failed to initialize adapter structure (2)!");
+ esas2r_kill_adapter(index);
+ return 0;
+ }
+
+ tasklet_init(&a->tasklet,
+ esas2r_adapter_tasklet,
+ (unsigned long)a);
+
+ /*
+ * Disable chip interrupts to prevent spurious interrupts
+ * until we claim the IRQ.
+ */
+ esas2r_disable_chip_interrupts(a);
+ esas2r_check_adapter(a);
+
+ if (!esas2r_init_adapter_hw(a, true)) {
+ esas2r_log(ESAS2R_LOG_CRIT, "failed to initialize hardware!");
+ } else {
+ esas2r_debug("esas2r_init_adapter ok");
+ }
+
+ esas2r_claim_interrupts(a);
+
+ if (test_bit(AF2_IRQ_CLAIMED, &a->flags2))
+ esas2r_enable_chip_interrupts(a);
+
+ set_bit(AF2_INIT_DONE, &a->flags2);
+ if (!test_bit(AF_DEGRADED_MODE, &a->flags))
+ esas2r_kickoff_timer(a);
+ esas2r_debug("esas2r_init_adapter done for %p (%d)",
+ a, a->disable_cnt);
+
+ return 1;
+}
+
+static void esas2r_adapter_power_down(struct esas2r_adapter *a,
+ int power_management)
+{
+ struct esas2r_mem_desc *memdesc, *next;
+
+ if ((test_bit(AF2_INIT_DONE, &a->flags2))
+ && (!test_bit(AF_DEGRADED_MODE, &a->flags))) {
+ if (!power_management) {
+ timer_delete_sync(&a->timer);
+ tasklet_kill(&a->tasklet);
+ }
+ esas2r_power_down(a);
+
+ /*
+ * There are versions of firmware that do not handle the sync
+ * cache command correctly. Stall here to ensure that the
+ * cache is lazily flushed.
+ */
+ mdelay(500);
+ esas2r_debug("chip halted");
+ }
+
+ /* Remove sysfs binary files */
+ if (a->sysfs_fw_created) {
+ sysfs_remove_bin_file(&a->host->shost_dev.kobj, &bin_attr_fw);
+ a->sysfs_fw_created = 0;
+ }
+
+ if (a->sysfs_fs_created) {
+ sysfs_remove_bin_file(&a->host->shost_dev.kobj, &bin_attr_fs);
+ a->sysfs_fs_created = 0;
+ }
+
+ if (a->sysfs_vda_created) {
+ sysfs_remove_bin_file(&a->host->shost_dev.kobj, &bin_attr_vda);
+ a->sysfs_vda_created = 0;
+ }
+
+ if (a->sysfs_hw_created) {
+ sysfs_remove_bin_file(&a->host->shost_dev.kobj, &bin_attr_hw);
+ a->sysfs_hw_created = 0;
+ }
+
+ if (a->sysfs_live_nvram_created) {
+ sysfs_remove_bin_file(&a->host->shost_dev.kobj,
+ &bin_attr_live_nvram);
+ a->sysfs_live_nvram_created = 0;
+ }
+
+ if (a->sysfs_default_nvram_created) {
+ sysfs_remove_bin_file(&a->host->shost_dev.kobj,
+ &bin_attr_default_nvram);
+ a->sysfs_default_nvram_created = 0;
+ }
+
+ /* Clean up interrupts */
+ if (test_bit(AF2_IRQ_CLAIMED, &a->flags2)) {
+ esas2r_log_dev(ESAS2R_LOG_INFO,
+ &(a->pcid->dev),
+ "free_irq(%d) called", a->pcid->irq);
+
+ free_irq(a->pcid->irq, a);
+ esas2r_debug("IRQ released");
+ clear_bit(AF2_IRQ_CLAIMED, &a->flags2);
+ }
+
+ if (test_bit(AF2_MSI_ENABLED, &a->flags2)) {
+ pci_disable_msi(a->pcid);
+ clear_bit(AF2_MSI_ENABLED, &a->flags2);
+ esas2r_debug("MSI disabled");
+ }
+
+ if (a->inbound_list_md.virt_addr)
+ esas2r_initmem_free(a, &a->inbound_list_md);
+
+ if (a->outbound_list_md.virt_addr)
+ esas2r_initmem_free(a, &a->outbound_list_md);
+
+ list_for_each_entry_safe(memdesc, next, &a->free_sg_list_head,
+ next_desc) {
+ esas2r_initmem_free(a, memdesc);
+ }
+
+ /* Following frees everything allocated via alloc_vda_req */
+ list_for_each_entry_safe(memdesc, next, &a->vrq_mds_head, next_desc) {
+ esas2r_initmem_free(a, memdesc);
+ list_del(&memdesc->next_desc);
+ kfree(memdesc);
+ }
+
+ kfree(a->first_ae_req);
+ a->first_ae_req = NULL;
+
+ kfree(a->sg_list_mds);
+ a->sg_list_mds = NULL;
+
+ kfree(a->req_table);
+ a->req_table = NULL;
+
+ if (a->regs) {
+ esas2r_unmap_regions(a);
+ a->regs = NULL;
+ a->data_window = NULL;
+ esas2r_debug("regions unmapped");
+ }
+}
+
+/* Release/free allocated resources for specified adapters. */
+void esas2r_kill_adapter(int i)
+{
+ struct esas2r_adapter *a = esas2r_adapters[i];
+
+ if (a) {
+ unsigned long flags;
+ struct workqueue_struct *wq;
+ esas2r_debug("killing adapter %p [%d] ", a, i);
+ esas2r_fw_event_off(a);
+ esas2r_adapter_power_down(a, 0);
+ if (esas2r_buffered_ioctl &&
+ (a->pcid == esas2r_buffered_ioctl_pcid)) {
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)esas2r_buffered_ioctl_size,
+ esas2r_buffered_ioctl,
+ esas2r_buffered_ioctl_addr);
+ esas2r_buffered_ioctl = NULL;
+ }
+
+ if (a->vda_buffer) {
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)VDA_MAX_BUFFER_SIZE,
+ a->vda_buffer,
+ (dma_addr_t)a->ppvda_buffer);
+ a->vda_buffer = NULL;
+ }
+ if (a->fs_api_buffer) {
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)a->fs_api_buffer_size,
+ a->fs_api_buffer,
+ (dma_addr_t)a->ppfs_api_buffer);
+ a->fs_api_buffer = NULL;
+ }
+
+ kfree(a->local_atto_ioctl);
+ a->local_atto_ioctl = NULL;
+
+ spin_lock_irqsave(&a->fw_event_lock, flags);
+ wq = a->fw_event_q;
+ a->fw_event_q = NULL;
+ spin_unlock_irqrestore(&a->fw_event_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+
+ if (a->uncached) {
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)a->uncached_size,
+ a->uncached,
+ (dma_addr_t)a->uncached_phys);
+ a->uncached = NULL;
+ esas2r_debug("uncached area freed");
+ }
+
+ esas2r_log_dev(ESAS2R_LOG_INFO,
+ &(a->pcid->dev),
+ "pci_disable_device() called. msix_enabled: %d "
+ "msi_enabled: %d irq: %d pin: %d",
+ a->pcid->msix_enabled,
+ a->pcid->msi_enabled,
+ a->pcid->irq,
+ a->pcid->pin);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO,
+ &(a->pcid->dev),
+ "before pci_disable_device() enable_cnt: %d",
+ a->pcid->enable_cnt.counter);
+
+ pci_disable_device(a->pcid);
+ esas2r_log_dev(ESAS2R_LOG_INFO,
+ &(a->pcid->dev),
+ "after pci_disable_device() enable_cnt: %d",
+ a->pcid->enable_cnt.counter);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO,
+ &(a->pcid->dev),
+ "pci_set_drv_data(%p, NULL) called",
+ a->pcid);
+
+ pci_set_drvdata(a->pcid, NULL);
+ esas2r_adapters[i] = NULL;
+
+ if (test_bit(AF2_INIT_DONE, &a->flags2)) {
+ clear_bit(AF2_INIT_DONE, &a->flags2);
+
+ set_bit(AF_DEGRADED_MODE, &a->flags);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO,
+ &(a->host->shost_gendev),
+ "scsi_remove_host() called");
+
+ scsi_remove_host(a->host);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO,
+ &(a->host->shost_gendev),
+ "scsi_host_put() called");
+
+ scsi_host_put(a->host);
+ }
+ }
+}
+
+static int __maybe_unused esas2r_suspend(struct device *dev)
+{
+ struct Scsi_Host *host = dev_get_drvdata(dev);
+ struct esas2r_adapter *a = (struct esas2r_adapter *)host->hostdata;
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, dev, "suspending adapter()");
+ if (!a)
+ return -ENODEV;
+
+ esas2r_adapter_power_down(a, 1);
+ esas2r_log_dev(ESAS2R_LOG_INFO, dev, "esas2r_suspend(): 0");
+ return 0;
+}
+
+static int __maybe_unused esas2r_resume(struct device *dev)
+{
+ struct Scsi_Host *host = dev_get_drvdata(dev);
+ struct esas2r_adapter *a = (struct esas2r_adapter *)host->hostdata;
+ int rez = 0;
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, dev, "resuming adapter()");
+
+ if (!a) {
+ rez = -ENODEV;
+ goto error_exit;
+ }
+
+ if (esas2r_map_regions(a) != 0) {
+ esas2r_log(ESAS2R_LOG_CRIT, "could not re-map PCI regions!");
+ rez = -ENOMEM;
+ goto error_exit;
+ }
+
+ /* Set up interupt mode */
+ esas2r_setup_interrupts(a, a->intr_mode);
+
+ /*
+ * Disable chip interrupts to prevent spurious interrupts until we
+ * claim the IRQ.
+ */
+ esas2r_disable_chip_interrupts(a);
+ if (!esas2r_power_up(a, true)) {
+ esas2r_debug("yikes, esas2r_power_up failed");
+ rez = -ENOMEM;
+ goto error_exit;
+ }
+
+ esas2r_claim_interrupts(a);
+
+ if (test_bit(AF2_IRQ_CLAIMED, &a->flags2)) {
+ /*
+ * Now that system interrupt(s) are claimed, we can enable
+ * chip interrupts.
+ */
+ esas2r_enable_chip_interrupts(a);
+ esas2r_kickoff_timer(a);
+ } else {
+ esas2r_debug("yikes, unable to claim IRQ");
+ esas2r_log(ESAS2R_LOG_CRIT, "could not re-claim IRQ!");
+ rez = -ENOMEM;
+ goto error_exit;
+ }
+
+error_exit:
+ esas2r_log_dev(ESAS2R_LOG_CRIT, dev, "esas2r_resume(): %d",
+ rez);
+ return rez;
+}
+
+SIMPLE_DEV_PM_OPS(esas2r_pm_ops, esas2r_suspend, esas2r_resume);
+
+bool esas2r_set_degraded_mode(struct esas2r_adapter *a, char *error_str)
+{
+ set_bit(AF_DEGRADED_MODE, &a->flags);
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "setting adapter to degraded mode: %s\n", error_str);
+ return false;
+}
+
+u32 esas2r_get_uncached_size(struct esas2r_adapter *a)
+{
+ return sizeof(struct esas2r_sas_nvram)
+ + ALIGN(ESAS2R_DISC_BUF_LEN, 8)
+ + ALIGN(sizeof(u32), 8) /* outbound list copy pointer */
+ + 8
+ + (num_sg_lists * (u16)sgl_page_size)
+ + ALIGN((num_requests + num_ae_requests + 1 +
+ ESAS2R_LIST_EXTRA) *
+ sizeof(struct esas2r_inbound_list_source_entry),
+ 8)
+ + ALIGN((num_requests + num_ae_requests + 1 +
+ ESAS2R_LIST_EXTRA) *
+ sizeof(struct atto_vda_ob_rsp), 8)
+ + 256; /* VDA request and buffer align */
+}
+
+static void esas2r_init_pci_cfg_space(struct esas2r_adapter *a)
+{
+ if (pci_is_pcie(a->pcid)) {
+ u16 devcontrol;
+
+ pcie_capability_read_word(a->pcid, PCI_EXP_DEVCTL, &devcontrol);
+
+ if ((devcontrol & PCI_EXP_DEVCTL_READRQ) >
+ PCI_EXP_DEVCTL_READRQ_512B) {
+ esas2r_log(ESAS2R_LOG_INFO,
+ "max read request size > 512B");
+
+ devcontrol &= ~PCI_EXP_DEVCTL_READRQ;
+ devcontrol |= PCI_EXP_DEVCTL_READRQ_512B;
+ pcie_capability_write_word(a->pcid, PCI_EXP_DEVCTL,
+ devcontrol);
+ }
+ }
+}
+
+/*
+ * Determine the organization of the uncached data area and
+ * finish initializing the adapter structure
+ */
+bool esas2r_init_adapter_struct(struct esas2r_adapter *a,
+ void **uncached_area)
+{
+ u32 i;
+ u8 *high;
+ struct esas2r_inbound_list_source_entry *element;
+ struct esas2r_request *rq;
+ struct esas2r_mem_desc *sgl;
+
+ spin_lock_init(&a->sg_list_lock);
+ spin_lock_init(&a->mem_lock);
+ spin_lock_init(&a->queue_lock);
+
+ a->targetdb_end = &a->targetdb[ESAS2R_MAX_TARGETS];
+
+ if (!alloc_vda_req(a, &a->general_req)) {
+ esas2r_hdebug(
+ "failed to allocate a VDA request for the general req!");
+ return false;
+ }
+
+ /* allocate requests for asynchronous events */
+ a->first_ae_req =
+ kcalloc(num_ae_requests, sizeof(struct esas2r_request),
+ GFP_KERNEL);
+
+ if (a->first_ae_req == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "failed to allocate memory for asynchronous events");
+ return false;
+ }
+
+ /* allocate the S/G list memory descriptors */
+ a->sg_list_mds = kcalloc(num_sg_lists, sizeof(struct esas2r_mem_desc),
+ GFP_KERNEL);
+
+ if (a->sg_list_mds == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "failed to allocate memory for s/g list descriptors");
+ return false;
+ }
+
+ /* allocate the request table */
+ a->req_table =
+ kcalloc(num_requests + num_ae_requests + 1,
+ sizeof(struct esas2r_request *),
+ GFP_KERNEL);
+
+ if (a->req_table == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "failed to allocate memory for the request table");
+ return false;
+ }
+
+ /* initialize PCI configuration space */
+ esas2r_init_pci_cfg_space(a);
+
+ /*
+ * the thunder_stream boards all have a serial flash part that has a
+ * different base address on the AHB bus.
+ */
+ if ((a->pcid->subsystem_vendor == ATTO_VENDOR_ID)
+ && (a->pcid->subsystem_device & ATTO_SSDID_TBT))
+ a->flags2 |= AF2_THUNDERBOLT;
+
+ if (test_bit(AF2_THUNDERBOLT, &a->flags2))
+ a->flags2 |= AF2_SERIAL_FLASH;
+
+ if (a->pcid->subsystem_device == ATTO_TLSH_1068)
+ a->flags2 |= AF2_THUNDERLINK;
+
+ /* Uncached Area */
+ high = (u8 *)*uncached_area;
+
+ /* initialize the scatter/gather table pages */
+
+ for (i = 0, sgl = a->sg_list_mds; i < num_sg_lists; i++, sgl++) {
+ sgl->size = sgl_page_size;
+
+ list_add_tail(&sgl->next_desc, &a->free_sg_list_head);
+
+ if (!esas2r_initmem_alloc(a, sgl, ESAS2R_SGL_ALIGN)) {
+ /* Allow the driver to load if the minimum count met. */
+ if (i < NUM_SGL_MIN)
+ return false;
+ break;
+ }
+ }
+
+ /* compute the size of the lists */
+ a->list_size = num_requests + ESAS2R_LIST_EXTRA;
+
+ /* allocate the inbound list */
+ a->inbound_list_md.size = a->list_size *
+ sizeof(struct
+ esas2r_inbound_list_source_entry);
+
+ if (!esas2r_initmem_alloc(a, &a->inbound_list_md, ESAS2R_LIST_ALIGN)) {
+ esas2r_hdebug("failed to allocate IB list");
+ return false;
+ }
+
+ /* allocate the outbound list */
+ a->outbound_list_md.size = a->list_size *
+ sizeof(struct atto_vda_ob_rsp);
+
+ if (!esas2r_initmem_alloc(a, &a->outbound_list_md,
+ ESAS2R_LIST_ALIGN)) {
+ esas2r_hdebug("failed to allocate IB list");
+ return false;
+ }
+
+ /* allocate the NVRAM structure */
+ a->nvram = (struct esas2r_sas_nvram *)high;
+ high += sizeof(struct esas2r_sas_nvram);
+
+ /* allocate the discovery buffer */
+ a->disc_buffer = high;
+ high += ESAS2R_DISC_BUF_LEN;
+ high = PTR_ALIGN(high, 8);
+
+ /* allocate the outbound list copy pointer */
+ a->outbound_copy = (u32 volatile *)high;
+ high += sizeof(u32);
+
+ if (!test_bit(AF_NVR_VALID, &a->flags))
+ esas2r_nvram_set_defaults(a);
+
+ /* update the caller's uncached memory area pointer */
+ *uncached_area = (void *)high;
+
+ /* initialize the allocated memory */
+ if (test_bit(AF_FIRST_INIT, &a->flags)) {
+ esas2r_targ_db_initialize(a);
+
+ /* prime parts of the inbound list */
+ element =
+ (struct esas2r_inbound_list_source_entry *)a->
+ inbound_list_md.
+ virt_addr;
+
+ for (i = 0; i < a->list_size; i++) {
+ element->address = 0;
+ element->reserved = 0;
+ element->length = cpu_to_le32(HWILSE_INTERFACE_F0
+ | (sizeof(union
+ atto_vda_req)
+ /
+ sizeof(u32)));
+ element++;
+ }
+
+ /* init the AE requests */
+ for (rq = a->first_ae_req, i = 0; i < num_ae_requests; rq++,
+ i++) {
+ INIT_LIST_HEAD(&rq->req_list);
+ if (!alloc_vda_req(a, rq)) {
+ esas2r_hdebug(
+ "failed to allocate a VDA request!");
+ return false;
+ }
+
+ esas2r_rq_init_request(rq, a);
+
+ /* override the completion function */
+ rq->comp_cb = esas2r_ae_complete;
+ }
+ }
+
+ return true;
+}
+
+/* This code will verify that the chip is operational. */
+bool esas2r_check_adapter(struct esas2r_adapter *a)
+{
+ u32 starttime;
+ u32 doorbell;
+ u64 ppaddr;
+ u32 dw;
+
+ /*
+ * if the chip reset detected flag is set, we can bypass a bunch of
+ * stuff.
+ */
+ if (test_bit(AF_CHPRST_DETECTED, &a->flags))
+ goto skip_chip_reset;
+
+ /*
+ * BEFORE WE DO ANYTHING, disable the chip interrupts! the boot driver
+ * may have left them enabled or we may be recovering from a fault.
+ */
+ esas2r_write_register_dword(a, MU_INT_MASK_OUT, ESAS2R_INT_DIS_MASK);
+ esas2r_flush_register_dword(a, MU_INT_MASK_OUT);
+
+ /*
+ * wait for the firmware to become ready by forcing an interrupt and
+ * waiting for a response.
+ */
+ starttime = jiffies_to_msecs(jiffies);
+
+ while (true) {
+ esas2r_force_interrupt(a);
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell == 0xFFFFFFFF) {
+ /*
+ * Give the firmware up to two seconds to enable
+ * register access after a reset.
+ */
+ if ((jiffies_to_msecs(jiffies) - starttime) > 2000)
+ return esas2r_set_degraded_mode(a,
+ "unable to access registers");
+ } else if (doorbell & DRBL_FORCE_INT) {
+ u32 ver = (doorbell & DRBL_FW_VER_MSK);
+
+ /*
+ * This driver supports version 0 and version 1 of
+ * the API
+ */
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+ doorbell);
+
+ if (ver == DRBL_FW_VER_0) {
+ set_bit(AF_LEGACY_SGE_MODE, &a->flags);
+
+ a->max_vdareq_size = 128;
+ a->build_sgl = esas2r_build_sg_list_sge;
+ } else if (ver == DRBL_FW_VER_1) {
+ clear_bit(AF_LEGACY_SGE_MODE, &a->flags);
+
+ a->max_vdareq_size = 1024;
+ a->build_sgl = esas2r_build_sg_list_prd;
+ } else {
+ return esas2r_set_degraded_mode(a,
+ "unknown firmware version");
+ }
+ break;
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if ((jiffies_to_msecs(jiffies) - starttime) > 180000) {
+ esas2r_hdebug("FW ready TMO");
+ esas2r_bugon();
+
+ return esas2r_set_degraded_mode(a,
+ "firmware start has timed out");
+ }
+ }
+
+ /* purge any asynchronous events since we will repost them later */
+ esas2r_write_register_dword(a, MU_DOORBELL_IN, DRBL_MSG_IFC_DOWN);
+ starttime = jiffies_to_msecs(jiffies);
+
+ while (true) {
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell & DRBL_MSG_IFC_DOWN) {
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+ doorbell);
+ break;
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(50));
+
+ if ((jiffies_to_msecs(jiffies) - starttime) > 3000) {
+ esas2r_hdebug("timeout waiting for interface down");
+ break;
+ }
+ }
+skip_chip_reset:
+ /*
+ * first things first, before we go changing any of these registers
+ * disable the communication lists.
+ */
+ dw = esas2r_read_register_dword(a, MU_IN_LIST_CONFIG);
+ dw &= ~MU_ILC_ENABLE;
+ esas2r_write_register_dword(a, MU_IN_LIST_CONFIG, dw);
+ dw = esas2r_read_register_dword(a, MU_OUT_LIST_CONFIG);
+ dw &= ~MU_OLC_ENABLE;
+ esas2r_write_register_dword(a, MU_OUT_LIST_CONFIG, dw);
+
+ /* configure the communication list addresses */
+ ppaddr = a->inbound_list_md.phys_addr;
+ esas2r_write_register_dword(a, MU_IN_LIST_ADDR_LO,
+ lower_32_bits(ppaddr));
+ esas2r_write_register_dword(a, MU_IN_LIST_ADDR_HI,
+ upper_32_bits(ppaddr));
+ ppaddr = a->outbound_list_md.phys_addr;
+ esas2r_write_register_dword(a, MU_OUT_LIST_ADDR_LO,
+ lower_32_bits(ppaddr));
+ esas2r_write_register_dword(a, MU_OUT_LIST_ADDR_HI,
+ upper_32_bits(ppaddr));
+ ppaddr = a->uncached_phys +
+ ((u8 *)a->outbound_copy - a->uncached);
+ esas2r_write_register_dword(a, MU_OUT_LIST_COPY_PTR_LO,
+ lower_32_bits(ppaddr));
+ esas2r_write_register_dword(a, MU_OUT_LIST_COPY_PTR_HI,
+ upper_32_bits(ppaddr));
+
+ /* reset the read and write pointers */
+ *a->outbound_copy =
+ a->last_write =
+ a->last_read = a->list_size - 1;
+ set_bit(AF_COMM_LIST_TOGGLE, &a->flags);
+ esas2r_write_register_dword(a, MU_IN_LIST_WRITE, MU_ILW_TOGGLE |
+ a->last_write);
+ esas2r_write_register_dword(a, MU_OUT_LIST_COPY, MU_OLC_TOGGLE |
+ a->last_write);
+ esas2r_write_register_dword(a, MU_IN_LIST_READ, MU_ILR_TOGGLE |
+ a->last_write);
+ esas2r_write_register_dword(a, MU_OUT_LIST_WRITE,
+ MU_OLW_TOGGLE | a->last_write);
+
+ /* configure the interface select fields */
+ dw = esas2r_read_register_dword(a, MU_IN_LIST_IFC_CONFIG);
+ dw &= ~(MU_ILIC_LIST | MU_ILIC_DEST);
+ esas2r_write_register_dword(a, MU_IN_LIST_IFC_CONFIG,
+ (dw | MU_ILIC_LIST_F0 | MU_ILIC_DEST_DDR));
+ dw = esas2r_read_register_dword(a, MU_OUT_LIST_IFC_CONFIG);
+ dw &= ~(MU_OLIC_LIST | MU_OLIC_SOURCE);
+ esas2r_write_register_dword(a, MU_OUT_LIST_IFC_CONFIG,
+ (dw | MU_OLIC_LIST_F0 |
+ MU_OLIC_SOURCE_DDR));
+
+ /* finish configuring the communication lists */
+ dw = esas2r_read_register_dword(a, MU_IN_LIST_CONFIG);
+ dw &= ~(MU_ILC_ENTRY_MASK | MU_ILC_NUMBER_MASK);
+ dw |= MU_ILC_ENTRY_4_DW | MU_ILC_DYNAMIC_SRC
+ | (a->list_size << MU_ILC_NUMBER_SHIFT);
+ esas2r_write_register_dword(a, MU_IN_LIST_CONFIG, dw);
+ dw = esas2r_read_register_dword(a, MU_OUT_LIST_CONFIG);
+ dw &= ~(MU_OLC_ENTRY_MASK | MU_OLC_NUMBER_MASK);
+ dw |= MU_OLC_ENTRY_4_DW | (a->list_size << MU_OLC_NUMBER_SHIFT);
+ esas2r_write_register_dword(a, MU_OUT_LIST_CONFIG, dw);
+
+ /*
+ * notify the firmware that we're done setting up the communication
+ * list registers. wait here until the firmware is done configuring
+ * its lists. it will signal that it is done by enabling the lists.
+ */
+ esas2r_write_register_dword(a, MU_DOORBELL_IN, DRBL_MSG_IFC_INIT);
+ starttime = jiffies_to_msecs(jiffies);
+
+ while (true) {
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell & DRBL_MSG_IFC_INIT) {
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+ doorbell);
+ break;
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if ((jiffies_to_msecs(jiffies) - starttime) > 3000) {
+ esas2r_hdebug(
+ "timeout waiting for communication list init");
+ esas2r_bugon();
+ return esas2r_set_degraded_mode(a,
+ "timeout waiting for communication list init");
+ }
+ }
+
+ /*
+ * flag whether the firmware supports the power down doorbell. we
+ * determine this by reading the inbound doorbell enable mask.
+ */
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_IN_ENB);
+ if (doorbell & DRBL_POWER_DOWN)
+ set_bit(AF2_VDA_POWER_DOWN, &a->flags2);
+ else
+ clear_bit(AF2_VDA_POWER_DOWN, &a->flags2);
+
+ /*
+ * enable assertion of outbound queue and doorbell interrupts in the
+ * main interrupt cause register.
+ */
+ esas2r_write_register_dword(a, MU_OUT_LIST_INT_MASK, MU_OLIS_MASK);
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT_ENB, DRBL_ENB_MASK);
+ return true;
+}
+
+/* Process the initialization message just completed and format the next one. */
+static bool esas2r_format_init_msg(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ u32 msg = a->init_msg;
+ struct atto_vda_cfg_init *ci;
+
+ a->init_msg = 0;
+
+ switch (msg) {
+ case ESAS2R_INIT_MSG_START:
+ case ESAS2R_INIT_MSG_REINIT:
+ {
+ esas2r_hdebug("CFG init");
+ esas2r_build_cfg_req(a,
+ rq,
+ VDA_CFG_INIT,
+ 0,
+ NULL);
+ ci = (struct atto_vda_cfg_init *)&rq->vrq->cfg.data.init;
+ ci->sgl_page_size = cpu_to_le32(sgl_page_size);
+ /* firmware interface overflows in y2106 */
+ ci->epoch_time = cpu_to_le32(ktime_get_real_seconds());
+ rq->flags |= RF_FAILURE_OK;
+ a->init_msg = ESAS2R_INIT_MSG_INIT;
+ break;
+ }
+
+ case ESAS2R_INIT_MSG_INIT:
+ if (rq->req_stat == RS_SUCCESS) {
+ u32 major;
+ u32 minor;
+ u16 fw_release;
+
+ a->fw_version = le16_to_cpu(
+ rq->func_rsp.cfg_rsp.vda_version);
+ a->fw_build = rq->func_rsp.cfg_rsp.fw_build;
+ fw_release = le16_to_cpu(
+ rq->func_rsp.cfg_rsp.fw_release);
+ major = LOBYTE(fw_release);
+ minor = HIBYTE(fw_release);
+ a->fw_version += (major << 16) + (minor << 24);
+ } else {
+ esas2r_hdebug("FAILED");
+ }
+
+ /*
+ * the 2.71 and earlier releases of R6xx firmware did not error
+ * unsupported config requests correctly.
+ */
+
+ if ((test_bit(AF2_THUNDERBOLT, &a->flags2))
+ || (be32_to_cpu(a->fw_version) > 0x00524702)) {
+ esas2r_hdebug("CFG get init");
+ esas2r_build_cfg_req(a,
+ rq,
+ VDA_CFG_GET_INIT2,
+ sizeof(struct atto_vda_cfg_init),
+ NULL);
+
+ rq->vrq->cfg.sg_list_offset = offsetof(
+ struct atto_vda_cfg_req,
+ data.sge);
+ rq->vrq->cfg.data.prde.ctl_len =
+ cpu_to_le32(sizeof(struct atto_vda_cfg_init));
+ rq->vrq->cfg.data.prde.address = cpu_to_le64(
+ rq->vrq_md->phys_addr +
+ sizeof(union atto_vda_req));
+ rq->flags |= RF_FAILURE_OK;
+ a->init_msg = ESAS2R_INIT_MSG_GET_INIT;
+ break;
+ }
+ fallthrough;
+
+ case ESAS2R_INIT_MSG_GET_INIT:
+ if (msg == ESAS2R_INIT_MSG_GET_INIT) {
+ ci = (struct atto_vda_cfg_init *)rq->data_buf;
+ if (rq->req_stat == RS_SUCCESS) {
+ a->num_targets_backend =
+ le32_to_cpu(ci->num_targets_backend);
+ a->ioctl_tunnel =
+ le32_to_cpu(ci->ioctl_tunnel);
+ } else {
+ esas2r_hdebug("FAILED");
+ }
+ }
+ fallthrough;
+
+ default:
+ rq->req_stat = RS_SUCCESS;
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Perform initialization messages via the request queue. Messages are
+ * performed with interrupts disabled.
+ */
+bool esas2r_init_msgs(struct esas2r_adapter *a)
+{
+ bool success = true;
+ struct esas2r_request *rq = &a->general_req;
+
+ esas2r_rq_init_request(rq, a);
+ rq->comp_cb = esas2r_dummy_complete;
+
+ if (a->init_msg == 0)
+ a->init_msg = ESAS2R_INIT_MSG_REINIT;
+
+ while (a->init_msg) {
+ if (esas2r_format_init_msg(a, rq)) {
+ unsigned long flags;
+ while (true) {
+ spin_lock_irqsave(&a->queue_lock, flags);
+ esas2r_start_vda_request(a, rq);
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+ esas2r_wait_request(a, rq);
+ if (rq->req_stat != RS_PENDING)
+ break;
+ }
+ }
+
+ if (rq->req_stat == RS_SUCCESS
+ || ((rq->flags & RF_FAILURE_OK)
+ && rq->req_stat != RS_TIMEOUT))
+ continue;
+
+ esas2r_log(ESAS2R_LOG_CRIT, "init message %x failed (%x, %x)",
+ a->init_msg, rq->req_stat, rq->flags);
+ a->init_msg = ESAS2R_INIT_MSG_START;
+ success = false;
+ break;
+ }
+
+ esas2r_rq_destroy_request(rq, a);
+ return success;
+}
+
+/* Initialize the adapter chip */
+bool esas2r_init_adapter_hw(struct esas2r_adapter *a, bool init_poll)
+{
+ bool rslt = false;
+ struct esas2r_request *rq;
+ u32 i;
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ goto exit;
+
+ if (!test_bit(AF_NVR_VALID, &a->flags)) {
+ if (!esas2r_nvram_read_direct(a))
+ esas2r_log(ESAS2R_LOG_WARN,
+ "invalid/missing NVRAM parameters");
+ }
+
+ if (!esas2r_init_msgs(a)) {
+ esas2r_set_degraded_mode(a, "init messages failed");
+ goto exit;
+ }
+
+ /* The firmware is ready. */
+ clear_bit(AF_DEGRADED_MODE, &a->flags);
+ clear_bit(AF_CHPRST_PENDING, &a->flags);
+
+ /* Post all the async event requests */
+ for (i = 0, rq = a->first_ae_req; i < num_ae_requests; i++, rq++)
+ esas2r_start_ae_request(a, rq);
+
+ if (!a->flash_rev[0])
+ esas2r_read_flash_rev(a);
+
+ if (!a->image_type[0])
+ esas2r_read_image_type(a);
+
+ if (a->fw_version == 0)
+ a->fw_rev[0] = 0;
+ else
+ sprintf(a->fw_rev, "%1d.%02d",
+ (int)LOBYTE(HIWORD(a->fw_version)),
+ (int)HIBYTE(HIWORD(a->fw_version)));
+
+ esas2r_hdebug("firmware revision: %s", a->fw_rev);
+
+ if (test_bit(AF_CHPRST_DETECTED, &a->flags)
+ && (test_bit(AF_FIRST_INIT, &a->flags))) {
+ esas2r_enable_chip_interrupts(a);
+ return true;
+ }
+
+ /* initialize discovery */
+ esas2r_disc_initialize(a);
+
+ /*
+ * wait for the device wait time to expire here if requested. this is
+ * usually requested during initial driver load and possibly when
+ * resuming from a low power state. deferred device waiting will use
+ * interrupts. chip reset recovery always defers device waiting to
+ * avoid being in a TASKLET too long.
+ */
+ if (init_poll) {
+ u32 currtime = a->disc_start_time;
+ u32 nexttick = 100;
+ u32 deltatime;
+
+ /*
+ * Block Tasklets from getting scheduled and indicate this is
+ * polled discovery.
+ */
+ set_bit(AF_TASKLET_SCHEDULED, &a->flags);
+ set_bit(AF_DISC_POLLED, &a->flags);
+
+ /*
+ * Temporarily bring the disable count to zero to enable
+ * deferred processing. Note that the count is already zero
+ * after the first initialization.
+ */
+ if (test_bit(AF_FIRST_INIT, &a->flags))
+ atomic_dec(&a->disable_cnt);
+
+ while (test_bit(AF_DISC_PENDING, &a->flags)) {
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ /*
+ * Determine the need for a timer tick based on the
+ * delta time between this and the last iteration of
+ * this loop. We don't use the absolute time because
+ * then we would have to worry about when nexttick
+ * wraps and currtime hasn't yet.
+ */
+ deltatime = jiffies_to_msecs(jiffies) - currtime;
+ currtime += deltatime;
+
+ /*
+ * Process any waiting discovery as long as the chip is
+ * up. If a chip reset happens during initial polling,
+ * we have to make sure the timer tick processes the
+ * doorbell indicating the firmware is ready.
+ */
+ if (!test_bit(AF_CHPRST_PENDING, &a->flags))
+ esas2r_disc_check_for_work(a);
+
+ /* Simulate a timer tick. */
+ if (nexttick <= deltatime) {
+
+ /* Time for a timer tick */
+ nexttick += 100;
+ esas2r_timer_tick(a);
+ }
+
+ if (nexttick > deltatime)
+ nexttick -= deltatime;
+
+ /* Do any deferred processing */
+ if (esas2r_is_tasklet_pending(a))
+ esas2r_do_tasklet_tasks(a);
+
+ }
+
+ if (test_bit(AF_FIRST_INIT, &a->flags))
+ atomic_inc(&a->disable_cnt);
+
+ clear_bit(AF_DISC_POLLED, &a->flags);
+ clear_bit(AF_TASKLET_SCHEDULED, &a->flags);
+ }
+
+
+ esas2r_targ_db_report_changes(a);
+
+ /*
+ * For cases where (a) the initialization messages processing may
+ * handle an interrupt for a port event and a discovery is waiting, but
+ * we are not waiting for devices, or (b) the device wait time has been
+ * exhausted but there is still discovery pending, start any leftover
+ * discovery in interrupt driven mode.
+ */
+ esas2r_disc_start_waiting(a);
+
+ /* Enable chip interrupts */
+ a->int_mask = ESAS2R_INT_STS_MASK;
+ esas2r_enable_chip_interrupts(a);
+ esas2r_enable_heartbeat(a);
+ rslt = true;
+
+exit:
+ /*
+ * Regardless of whether initialization was successful, certain things
+ * need to get done before we exit.
+ */
+
+ if (test_bit(AF_CHPRST_DETECTED, &a->flags) &&
+ test_bit(AF_FIRST_INIT, &a->flags)) {
+ /*
+ * Reinitialization was performed during the first
+ * initialization. Only clear the chip reset flag so the
+ * original device polling is not cancelled.
+ */
+ if (!rslt)
+ clear_bit(AF_CHPRST_PENDING, &a->flags);
+ } else {
+ /* First initialization or a subsequent re-init is complete. */
+ if (!rslt) {
+ clear_bit(AF_CHPRST_PENDING, &a->flags);
+ clear_bit(AF_DISC_PENDING, &a->flags);
+ }
+
+
+ /* Enable deferred processing after the first initialization. */
+ if (test_bit(AF_FIRST_INIT, &a->flags)) {
+ clear_bit(AF_FIRST_INIT, &a->flags);
+
+ if (atomic_dec_return(&a->disable_cnt) == 0)
+ esas2r_do_deferred_processes(a);
+ }
+ }
+
+ return rslt;
+}
+
+void esas2r_reset_adapter(struct esas2r_adapter *a)
+{
+ set_bit(AF_OS_RESET, &a->flags);
+ esas2r_local_reset_adapter(a);
+ esas2r_schedule_tasklet(a);
+}
+
+void esas2r_reset_chip(struct esas2r_adapter *a)
+{
+ if (!esas2r_is_adapter_present(a))
+ return;
+
+ /*
+ * Before we reset the chip, save off the VDA core dump. The VDA core
+ * dump is located in the upper 512KB of the onchip SRAM. Make sure
+ * to not overwrite a previous crash that was saved.
+ */
+ if (test_bit(AF2_COREDUMP_AVAIL, &a->flags2) &&
+ !test_bit(AF2_COREDUMP_SAVED, &a->flags2)) {
+ esas2r_read_mem_block(a,
+ a->fw_coredump_buff,
+ MW_DATA_ADDR_SRAM + 0x80000,
+ ESAS2R_FWCOREDUMP_SZ);
+
+ set_bit(AF2_COREDUMP_SAVED, &a->flags2);
+ }
+
+ clear_bit(AF2_COREDUMP_AVAIL, &a->flags2);
+
+ /* Reset the chip */
+ if (a->pcid->revision == MVR_FREY_B2)
+ esas2r_write_register_dword(a, MU_CTL_STATUS_IN_B2,
+ MU_CTL_IN_FULL_RST2);
+ else
+ esas2r_write_register_dword(a, MU_CTL_STATUS_IN,
+ MU_CTL_IN_FULL_RST);
+
+
+ /* Stall a little while to let the reset condition clear */
+ mdelay(10);
+}
+
+static void esas2r_power_down_notify_firmware(struct esas2r_adapter *a)
+{
+ u32 starttime;
+ u32 doorbell;
+
+ esas2r_write_register_dword(a, MU_DOORBELL_IN, DRBL_POWER_DOWN);
+ starttime = jiffies_to_msecs(jiffies);
+
+ while (true) {
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell & DRBL_POWER_DOWN) {
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+ doorbell);
+ break;
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if ((jiffies_to_msecs(jiffies) - starttime) > 30000) {
+ esas2r_hdebug("Timeout waiting for power down");
+ break;
+ }
+ }
+}
+
+/*
+ * Perform power management processing including managing device states, adapter
+ * states, interrupts, and I/O.
+ */
+void esas2r_power_down(struct esas2r_adapter *a)
+{
+ set_bit(AF_POWER_MGT, &a->flags);
+ set_bit(AF_POWER_DOWN, &a->flags);
+
+ if (!test_bit(AF_DEGRADED_MODE, &a->flags)) {
+ u32 starttime;
+ u32 doorbell;
+
+ /*
+ * We are currently running OK and will be reinitializing later.
+ * increment the disable count to coordinate with
+ * esas2r_init_adapter. We don't have to do this in degraded
+ * mode since we never enabled interrupts in the first place.
+ */
+ esas2r_disable_chip_interrupts(a);
+ esas2r_disable_heartbeat(a);
+
+ /* wait for any VDA activity to clear before continuing */
+ esas2r_write_register_dword(a, MU_DOORBELL_IN,
+ DRBL_MSG_IFC_DOWN);
+ starttime = jiffies_to_msecs(jiffies);
+
+ while (true) {
+ doorbell =
+ esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell & DRBL_MSG_IFC_DOWN) {
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+ doorbell);
+ break;
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if ((jiffies_to_msecs(jiffies) - starttime) > 3000) {
+ esas2r_hdebug(
+ "timeout waiting for interface down");
+ break;
+ }
+ }
+
+ /*
+ * For versions of firmware that support it tell them the driver
+ * is powering down.
+ */
+ if (test_bit(AF2_VDA_POWER_DOWN, &a->flags2))
+ esas2r_power_down_notify_firmware(a);
+ }
+
+ /* Suspend I/O processing. */
+ set_bit(AF_OS_RESET, &a->flags);
+ set_bit(AF_DISC_PENDING, &a->flags);
+ set_bit(AF_CHPRST_PENDING, &a->flags);
+
+ esas2r_process_adapter_reset(a);
+
+ /* Remove devices now that I/O is cleaned up. */
+ a->prev_dev_cnt = esas2r_targ_db_get_tgt_cnt(a);
+ esas2r_targ_db_remove_all(a, false);
+}
+
+/*
+ * Perform power management processing including managing device states, adapter
+ * states, interrupts, and I/O.
+ */
+bool esas2r_power_up(struct esas2r_adapter *a, bool init_poll)
+{
+ bool ret;
+
+ clear_bit(AF_POWER_DOWN, &a->flags);
+ esas2r_init_pci_cfg_space(a);
+ set_bit(AF_FIRST_INIT, &a->flags);
+ atomic_inc(&a->disable_cnt);
+
+ /* reinitialize the adapter */
+ ret = esas2r_check_adapter(a);
+ if (!esas2r_init_adapter_hw(a, init_poll))
+ ret = false;
+
+ /* send the reset asynchronous event */
+ esas2r_send_reset_ae(a, true);
+
+ /* clear this flag after initialization. */
+ clear_bit(AF_POWER_MGT, &a->flags);
+ return ret;
+}
+
+bool esas2r_is_adapter_present(struct esas2r_adapter *a)
+{
+ if (test_bit(AF_NOT_PRESENT, &a->flags))
+ return false;
+
+ if (esas2r_read_register_dword(a, MU_DOORBELL_OUT) == 0xFFFFFFFF) {
+ set_bit(AF_NOT_PRESENT, &a->flags);
+
+ return false;
+ }
+ return true;
+}
+
+const char *esas2r_get_model_name(struct esas2r_adapter *a)
+{
+ switch (a->pcid->subsystem_device) {
+ case ATTO_ESAS_R680:
+ return "ATTO ExpressSAS R680";
+
+ case ATTO_ESAS_R608:
+ return "ATTO ExpressSAS R608";
+
+ case ATTO_ESAS_R60F:
+ return "ATTO ExpressSAS R60F";
+
+ case ATTO_ESAS_R6F0:
+ return "ATTO ExpressSAS R6F0";
+
+ case ATTO_ESAS_R644:
+ return "ATTO ExpressSAS R644";
+
+ case ATTO_ESAS_R648:
+ return "ATTO ExpressSAS R648";
+
+ case ATTO_TSSC_3808:
+ return "ATTO ThunderStream SC 3808D";
+
+ case ATTO_TSSC_3808E:
+ return "ATTO ThunderStream SC 3808E";
+
+ case ATTO_TLSH_1068:
+ return "ATTO ThunderLink SH 1068";
+ }
+
+ return "ATTO SAS Controller";
+}
+
+const char *esas2r_get_model_name_short(struct esas2r_adapter *a)
+{
+ switch (a->pcid->subsystem_device) {
+ case ATTO_ESAS_R680:
+ return "R680";
+
+ case ATTO_ESAS_R608:
+ return "R608";
+
+ case ATTO_ESAS_R60F:
+ return "R60F";
+
+ case ATTO_ESAS_R6F0:
+ return "R6F0";
+
+ case ATTO_ESAS_R644:
+ return "R644";
+
+ case ATTO_ESAS_R648:
+ return "R648";
+
+ case ATTO_TSSC_3808:
+ return "SC 3808D";
+
+ case ATTO_TSSC_3808E:
+ return "SC 3808E";
+
+ case ATTO_TLSH_1068:
+ return "SH 1068";
+ }
+
+ return "unknown";
+}
diff --git a/drivers/scsi/esas2r/esas2r_int.c b/drivers/scsi/esas2r/esas2r_int.c
new file mode 100644
index 000000000000..5281d9356327
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_int.c
@@ -0,0 +1,944 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_int.c
+ * esas2r interrupt handling
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include "esas2r.h"
+
+/* Local function prototypes */
+static void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell);
+static void esas2r_get_outbound_responses(struct esas2r_adapter *a);
+static void esas2r_process_bus_reset(struct esas2r_adapter *a);
+
+/*
+ * Poll the adapter for interrupts and service them.
+ * This function handles both legacy interrupts and MSI.
+ */
+void esas2r_polled_interrupt(struct esas2r_adapter *a)
+{
+ u32 intstat;
+ u32 doorbell;
+
+ esas2r_disable_chip_interrupts(a);
+
+ intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
+
+ if (intstat & MU_INTSTAT_POST_OUT) {
+ /* clear the interrupt */
+
+ esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
+ MU_OLIS_INT);
+ esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
+
+ esas2r_get_outbound_responses(a);
+ }
+
+ if (intstat & MU_INTSTAT_DRBL) {
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell != 0)
+ esas2r_doorbell_interrupt(a, doorbell);
+ }
+
+ esas2r_enable_chip_interrupts(a);
+
+ if (atomic_read(&a->disable_cnt) == 0)
+ esas2r_do_deferred_processes(a);
+}
+
+/*
+ * Legacy and MSI interrupt handlers. Note that the legacy interrupt handler
+ * schedules a TASKLET to process events, whereas the MSI handler just
+ * processes interrupt events directly.
+ */
+irqreturn_t esas2r_interrupt(int irq, void *dev_id)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id;
+
+ if (!esas2r_adapter_interrupt_pending(a))
+ return IRQ_NONE;
+
+ set_bit(AF2_INT_PENDING, &a->flags2);
+ esas2r_schedule_tasklet(a);
+
+ return IRQ_HANDLED;
+}
+
+void esas2r_adapter_interrupt(struct esas2r_adapter *a)
+{
+ u32 doorbell;
+
+ if (likely(a->int_stat & MU_INTSTAT_POST_OUT)) {
+ /* clear the interrupt */
+ esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
+ MU_OLIS_INT);
+ esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
+ esas2r_get_outbound_responses(a);
+ }
+
+ if (unlikely(a->int_stat & MU_INTSTAT_DRBL)) {
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell != 0)
+ esas2r_doorbell_interrupt(a, doorbell);
+ }
+
+ a->int_mask = ESAS2R_INT_STS_MASK;
+
+ esas2r_enable_chip_interrupts(a);
+
+ if (likely(atomic_read(&a->disable_cnt) == 0))
+ esas2r_do_deferred_processes(a);
+}
+
+irqreturn_t esas2r_msi_interrupt(int irq, void *dev_id)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)dev_id;
+ u32 intstat;
+ u32 doorbell;
+
+ intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
+
+ if (likely(intstat & MU_INTSTAT_POST_OUT)) {
+ /* clear the interrupt */
+
+ esas2r_write_register_dword(a, MU_OUT_LIST_INT_STAT,
+ MU_OLIS_INT);
+ esas2r_flush_register_dword(a, MU_OUT_LIST_INT_STAT);
+
+ esas2r_get_outbound_responses(a);
+ }
+
+ if (unlikely(intstat & MU_INTSTAT_DRBL)) {
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell != 0)
+ esas2r_doorbell_interrupt(a, doorbell);
+ }
+
+ /*
+ * Work around a chip bug and force a new MSI to be sent if one is
+ * still pending.
+ */
+ esas2r_disable_chip_interrupts(a);
+ esas2r_enable_chip_interrupts(a);
+
+ if (likely(atomic_read(&a->disable_cnt) == 0))
+ esas2r_do_deferred_processes(a);
+
+ esas2r_do_tasklet_tasks(a);
+
+ return 1;
+}
+
+
+
+static void esas2r_handle_outbound_rsp_err(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ struct atto_vda_ob_rsp *rsp)
+{
+
+ /*
+ * For I/O requests, only copy the response if an error
+ * occurred and setup a callback to do error processing.
+ */
+ if (unlikely(rq->req_stat != RS_SUCCESS)) {
+ memcpy(&rq->func_rsp, &rsp->func_rsp, sizeof(rsp->func_rsp));
+
+ if (rq->req_stat == RS_ABORTED) {
+ if (rq->timeout > RQ_MAX_TIMEOUT)
+ rq->req_stat = RS_TIMEOUT;
+ } else if (rq->req_stat == RS_SCSI_ERROR) {
+ u8 scsistatus = rq->func_rsp.scsi_rsp.scsi_stat;
+
+ esas2r_trace("scsistatus: %x", scsistatus);
+
+ /* Any of these are a good result. */
+ if (scsistatus == SAM_STAT_GOOD || scsistatus ==
+ SAM_STAT_CONDITION_MET || scsistatus ==
+ SAM_STAT_INTERMEDIATE || scsistatus ==
+ SAM_STAT_INTERMEDIATE_CONDITION_MET) {
+ rq->req_stat = RS_SUCCESS;
+ rq->func_rsp.scsi_rsp.scsi_stat =
+ SAM_STAT_GOOD;
+ }
+ }
+ }
+}
+
+static void esas2r_get_outbound_responses(struct esas2r_adapter *a)
+{
+ struct atto_vda_ob_rsp *rsp;
+ u32 rspput_ptr;
+ u32 rspget_ptr;
+ struct esas2r_request *rq;
+ u32 handle;
+ unsigned long flags;
+
+ LIST_HEAD(comp_list);
+
+ esas2r_trace_enter();
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ /* Get the outbound limit and pointers */
+ rspput_ptr = le32_to_cpu(*a->outbound_copy) & MU_OLC_WRT_PTR;
+ rspget_ptr = a->last_read;
+
+ esas2r_trace("rspput_ptr: %x, rspget_ptr: %x", rspput_ptr, rspget_ptr);
+
+ /* If we don't have anything to process, get out */
+ if (unlikely(rspget_ptr == rspput_ptr)) {
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+ esas2r_trace_exit();
+ return;
+ }
+
+ /* Make sure the firmware is healthy */
+ if (unlikely(rspput_ptr >= a->list_size)) {
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+ esas2r_bugon();
+ esas2r_local_reset_adapter(a);
+ esas2r_trace_exit();
+ return;
+ }
+
+ do {
+ rspget_ptr++;
+
+ if (rspget_ptr >= a->list_size)
+ rspget_ptr = 0;
+
+ rsp = (struct atto_vda_ob_rsp *)a->outbound_list_md.virt_addr
+ + rspget_ptr;
+
+ handle = rsp->handle;
+
+ /* Verify the handle range */
+ if (unlikely(LOWORD(handle) == 0
+ || LOWORD(handle) > num_requests +
+ num_ae_requests + 1)) {
+ esas2r_bugon();
+ continue;
+ }
+
+ /* Get the request for this handle */
+ rq = a->req_table[LOWORD(handle)];
+
+ if (unlikely(rq == NULL || rq->vrq->scsi.handle != handle)) {
+ esas2r_bugon();
+ continue;
+ }
+
+ list_del(&rq->req_list);
+
+ /* Get the completion status */
+ rq->req_stat = rsp->req_stat;
+
+ esas2r_trace("handle: %x", handle);
+ esas2r_trace("rq: %p", rq);
+ esas2r_trace("req_status: %x", rq->req_stat);
+
+ if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) {
+ esas2r_handle_outbound_rsp_err(a, rq, rsp);
+ } else {
+ /*
+ * Copy the outbound completion struct for non-I/O
+ * requests.
+ */
+ memcpy(&rq->func_rsp, &rsp->func_rsp,
+ sizeof(rsp->func_rsp));
+ }
+
+ /* Queue the request for completion. */
+ list_add_tail(&rq->comp_list, &comp_list);
+
+ } while (rspget_ptr != rspput_ptr);
+
+ a->last_read = rspget_ptr;
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+
+ esas2r_comp_list_drain(a, &comp_list);
+ esas2r_trace_exit();
+}
+
+/*
+ * Perform all deferred processes for the adapter. Deferred
+ * processes can only be done while the current interrupt
+ * disable_cnt for the adapter is zero.
+ */
+void esas2r_do_deferred_processes(struct esas2r_adapter *a)
+{
+ int startreqs = 2;
+ struct esas2r_request *rq;
+ unsigned long flags;
+
+ /*
+ * startreqs is used to control starting requests
+ * that are on the deferred queue
+ * = 0 - do not start any requests
+ * = 1 - can start discovery requests
+ * = 2 - can start any request
+ */
+
+ if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
+ test_bit(AF_FLASHING, &a->flags))
+ startreqs = 0;
+ else if (test_bit(AF_DISC_PENDING, &a->flags))
+ startreqs = 1;
+
+ atomic_inc(&a->disable_cnt);
+
+ /* Clear off the completed list to be processed later. */
+
+ if (esas2r_is_tasklet_pending(a)) {
+ esas2r_schedule_tasklet(a);
+
+ startreqs = 0;
+ }
+
+ /*
+ * If we can start requests then traverse the defer queue
+ * looking for requests to start or complete
+ */
+ if (startreqs && !list_empty(&a->defer_list)) {
+ LIST_HEAD(comp_list);
+ struct list_head *element, *next;
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ list_for_each_safe(element, next, &a->defer_list) {
+ rq = list_entry(element, struct esas2r_request,
+ req_list);
+
+ if (rq->req_stat != RS_PENDING) {
+ list_del(element);
+ list_add_tail(&rq->comp_list, &comp_list);
+ }
+ /*
+ * Process discovery and OS requests separately. We
+ * can't hold up discovery requests when discovery is
+ * pending. In general, there may be different sets of
+ * conditions for starting different types of requests.
+ */
+ else if (rq->req_type == RT_DISC_REQ) {
+ list_del(element);
+ esas2r_disc_local_start_request(a, rq);
+ } else if (startreqs == 2) {
+ list_del(element);
+ esas2r_local_start_request(a, rq);
+
+ /*
+ * Flashing could have been set by last local
+ * start
+ */
+ if (test_bit(AF_FLASHING, &a->flags))
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+ esas2r_comp_list_drain(a, &comp_list);
+ }
+
+ atomic_dec(&a->disable_cnt);
+}
+
+/*
+ * Process an adapter reset (or one that is about to happen)
+ * by making sure all outstanding requests are completed that
+ * haven't been already.
+ */
+void esas2r_process_adapter_reset(struct esas2r_adapter *a)
+{
+ struct esas2r_request *rq = &a->general_req;
+ unsigned long flags;
+ struct esas2r_disc_context *dc;
+
+ LIST_HEAD(comp_list);
+ struct list_head *element;
+
+ esas2r_trace_enter();
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ /* abort the active discovery, if any. */
+
+ if (rq->interrupt_cx) {
+ dc = (struct esas2r_disc_context *)rq->interrupt_cx;
+
+ dc->disc_evt = 0;
+
+ clear_bit(AF_DISC_IN_PROG, &a->flags);
+ }
+
+ /*
+ * just clear the interrupt callback for now. it will be dequeued if
+ * and when we find it on the active queue and we don't want the
+ * callback called. also set the dummy completion callback in case we
+ * were doing an I/O request.
+ */
+
+ rq->interrupt_cx = NULL;
+ rq->interrupt_cb = NULL;
+
+ rq->comp_cb = esas2r_dummy_complete;
+
+ /* Reset the read and write pointers */
+
+ *a->outbound_copy =
+ a->last_write =
+ a->last_read = a->list_size - 1;
+
+ set_bit(AF_COMM_LIST_TOGGLE, &a->flags);
+
+ /* Kill all the requests on the active list */
+ list_for_each(element, &a->defer_list) {
+ rq = list_entry(element, struct esas2r_request, req_list);
+
+ if (rq->req_stat == RS_STARTED)
+ if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
+ list_add_tail(&rq->comp_list, &comp_list);
+ }
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+ esas2r_comp_list_drain(a, &comp_list);
+ esas2r_process_bus_reset(a);
+ esas2r_trace_exit();
+}
+
+static void esas2r_process_bus_reset(struct esas2r_adapter *a)
+{
+ struct esas2r_request *rq;
+ struct list_head *element;
+ unsigned long flags;
+
+ LIST_HEAD(comp_list);
+
+ esas2r_trace_enter();
+
+ esas2r_hdebug("reset detected");
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ /* kill all the requests on the deferred queue */
+ list_for_each(element, &a->defer_list) {
+ rq = list_entry(element, struct esas2r_request, req_list);
+ if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
+ list_add_tail(&rq->comp_list, &comp_list);
+ }
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+
+ esas2r_comp_list_drain(a, &comp_list);
+
+ if (atomic_read(&a->disable_cnt) == 0)
+ esas2r_do_deferred_processes(a);
+
+ clear_bit(AF_OS_RESET, &a->flags);
+
+ esas2r_trace_exit();
+}
+
+static void esas2r_chip_rst_needed_during_tasklet(struct esas2r_adapter *a)
+{
+
+ clear_bit(AF_CHPRST_NEEDED, &a->flags);
+ clear_bit(AF_BUSRST_NEEDED, &a->flags);
+ clear_bit(AF_BUSRST_DETECTED, &a->flags);
+ clear_bit(AF_BUSRST_PENDING, &a->flags);
+ /*
+ * Make sure we don't get attempt more than 3 resets
+ * when the uptime between resets does not exceed one
+ * minute. This will stop any situation where there is
+ * really something wrong with the hardware. The way
+ * this works is that we start with uptime ticks at 0.
+ * Each time we do a reset, we add 20 seconds worth to
+ * the count. Each time a timer tick occurs, as long
+ * as a chip reset is not pending, we decrement the
+ * tick count. If the uptime ticks ever gets to 60
+ * seconds worth, we disable the adapter from that
+ * point forward. Three strikes, you're out.
+ */
+ if (!esas2r_is_adapter_present(a) || (a->chip_uptime >=
+ ESAS2R_CHP_UPTIME_MAX)) {
+ esas2r_hdebug("*** adapter disabled ***");
+
+ /*
+ * Ok, some kind of hard failure. Make sure we
+ * exit this loop with chip interrupts
+ * permanently disabled so we don't lock up the
+ * entire system. Also flag degraded mode to
+ * prevent the heartbeat from trying to recover.
+ */
+
+ set_bit(AF_DEGRADED_MODE, &a->flags);
+ set_bit(AF_DISABLED, &a->flags);
+ clear_bit(AF_CHPRST_PENDING, &a->flags);
+ clear_bit(AF_DISC_PENDING, &a->flags);
+
+ esas2r_disable_chip_interrupts(a);
+ a->int_mask = 0;
+ esas2r_process_adapter_reset(a);
+
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "Adapter disabled because of hardware failure");
+ } else {
+ bool alrdyrst = test_and_set_bit(AF_CHPRST_STARTED, &a->flags);
+
+ if (!alrdyrst)
+ /*
+ * Only disable interrupts if this is
+ * the first reset attempt.
+ */
+ esas2r_disable_chip_interrupts(a);
+
+ if ((test_bit(AF_POWER_MGT, &a->flags)) &&
+ !test_bit(AF_FIRST_INIT, &a->flags) && !alrdyrst) {
+ /*
+ * Don't reset the chip on the first
+ * deferred power up attempt.
+ */
+ } else {
+ esas2r_hdebug("*** resetting chip ***");
+ esas2r_reset_chip(a);
+ }
+
+ /* Kick off the reinitialization */
+ a->chip_uptime += ESAS2R_CHP_UPTIME_CNT;
+ a->chip_init_time = jiffies_to_msecs(jiffies);
+ if (!test_bit(AF_POWER_MGT, &a->flags)) {
+ esas2r_process_adapter_reset(a);
+
+ if (!alrdyrst) {
+ /* Remove devices now that I/O is cleaned up. */
+ a->prev_dev_cnt =
+ esas2r_targ_db_get_tgt_cnt(a);
+ esas2r_targ_db_remove_all(a, false);
+ }
+ }
+
+ a->int_mask = 0;
+ }
+}
+
+static void esas2r_handle_chip_rst_during_tasklet(struct esas2r_adapter *a)
+{
+ while (test_bit(AF_CHPRST_DETECTED, &a->flags)) {
+ /*
+ * Balance the enable in esas2r_initadapter_hw.
+ * Esas2r_power_down already took care of it for power
+ * management.
+ */
+ if (!test_bit(AF_DEGRADED_MODE, &a->flags) &&
+ !test_bit(AF_POWER_MGT, &a->flags))
+ esas2r_disable_chip_interrupts(a);
+
+ /* Reinitialize the chip. */
+ esas2r_check_adapter(a);
+ esas2r_init_adapter_hw(a, 0);
+
+ if (test_bit(AF_CHPRST_NEEDED, &a->flags))
+ break;
+
+ if (test_bit(AF_POWER_MGT, &a->flags)) {
+ /* Recovery from power management. */
+ if (test_bit(AF_FIRST_INIT, &a->flags)) {
+ /* Chip reset during normal power up */
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "The firmware was reset during a normal power-up sequence");
+ } else {
+ /* Deferred power up complete. */
+ clear_bit(AF_POWER_MGT, &a->flags);
+ esas2r_send_reset_ae(a, true);
+ }
+ } else {
+ /* Recovery from online chip reset. */
+ if (test_bit(AF_FIRST_INIT, &a->flags)) {
+ /* Chip reset during driver load */
+ } else {
+ /* Chip reset after driver load */
+ esas2r_send_reset_ae(a, false);
+ }
+
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "Recovering from a chip reset while the chip was online");
+ }
+
+ clear_bit(AF_CHPRST_STARTED, &a->flags);
+ esas2r_enable_chip_interrupts(a);
+
+ /*
+ * Clear this flag last! this indicates that the chip has been
+ * reset already during initialization.
+ */
+ clear_bit(AF_CHPRST_DETECTED, &a->flags);
+ }
+}
+
+
+/* Perform deferred tasks when chip interrupts are disabled */
+void esas2r_do_tasklet_tasks(struct esas2r_adapter *a)
+{
+
+ if (test_bit(AF_CHPRST_NEEDED, &a->flags) ||
+ test_bit(AF_CHPRST_DETECTED, &a->flags)) {
+ if (test_bit(AF_CHPRST_NEEDED, &a->flags))
+ esas2r_chip_rst_needed_during_tasklet(a);
+
+ esas2r_handle_chip_rst_during_tasklet(a);
+ }
+
+ if (test_bit(AF_BUSRST_NEEDED, &a->flags)) {
+ esas2r_hdebug("hard resetting bus");
+
+ clear_bit(AF_BUSRST_NEEDED, &a->flags);
+
+ if (test_bit(AF_FLASHING, &a->flags))
+ set_bit(AF_BUSRST_DETECTED, &a->flags);
+ else
+ esas2r_write_register_dword(a, MU_DOORBELL_IN,
+ DRBL_RESET_BUS);
+ }
+
+ if (test_bit(AF_BUSRST_DETECTED, &a->flags)) {
+ esas2r_process_bus_reset(a);
+
+ esas2r_log_dev(ESAS2R_LOG_WARN,
+ &(a->host->shost_gendev),
+ "scsi_report_bus_reset() called");
+
+ scsi_report_bus_reset(a->host, 0);
+
+ clear_bit(AF_BUSRST_DETECTED, &a->flags);
+ clear_bit(AF_BUSRST_PENDING, &a->flags);
+
+ esas2r_log(ESAS2R_LOG_WARN, "Bus reset complete");
+ }
+
+ if (test_bit(AF_PORT_CHANGE, &a->flags)) {
+ clear_bit(AF_PORT_CHANGE, &a->flags);
+
+ esas2r_targ_db_report_changes(a);
+ }
+
+ if (atomic_read(&a->disable_cnt) == 0)
+ esas2r_do_deferred_processes(a);
+}
+
+static void esas2r_doorbell_interrupt(struct esas2r_adapter *a, u32 doorbell)
+{
+ if (!(doorbell & DRBL_FORCE_INT)) {
+ esas2r_trace_enter();
+ esas2r_trace("doorbell: %x", doorbell);
+ }
+
+ /* First clear the doorbell bits */
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT, doorbell);
+
+ if (doorbell & DRBL_RESET_BUS)
+ set_bit(AF_BUSRST_DETECTED, &a->flags);
+
+ if (doorbell & DRBL_FORCE_INT)
+ clear_bit(AF_HEARTBEAT, &a->flags);
+
+ if (doorbell & DRBL_PANIC_REASON_MASK) {
+ esas2r_hdebug("*** Firmware Panic ***");
+ esas2r_log(ESAS2R_LOG_CRIT, "The firmware has panicked");
+ }
+
+ if (doorbell & DRBL_FW_RESET) {
+ set_bit(AF2_COREDUMP_AVAIL, &a->flags2);
+ esas2r_local_reset_adapter(a);
+ }
+
+ if (!(doorbell & DRBL_FORCE_INT)) {
+ esas2r_trace_exit();
+ }
+}
+
+void esas2r_force_interrupt(struct esas2r_adapter *a)
+{
+ esas2r_write_register_dword(a, MU_DOORBELL_IN, DRBL_FORCE_INT |
+ DRBL_DRV_VER);
+}
+
+
+static void esas2r_lun_event(struct esas2r_adapter *a, union atto_vda_ae *ae,
+ u16 target, u32 length)
+{
+ struct esas2r_target *t = a->targetdb + target;
+ u32 cplen = length;
+ unsigned long flags;
+
+ if (cplen > sizeof(t->lu_event))
+ cplen = sizeof(t->lu_event);
+
+ esas2r_trace("ae->lu.dwevent: %x", ae->lu.dwevent);
+ esas2r_trace("ae->lu.bystate: %x", ae->lu.bystate);
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+
+ t->new_target_state = TS_INVALID;
+
+ if (ae->lu.dwevent & VDAAE_LU_LOST) {
+ t->new_target_state = TS_NOT_PRESENT;
+ } else {
+ switch (ae->lu.bystate) {
+ case VDAAE_LU_NOT_PRESENT:
+ case VDAAE_LU_OFFLINE:
+ case VDAAE_LU_DELETED:
+ case VDAAE_LU_FACTORY_DISABLED:
+ t->new_target_state = TS_NOT_PRESENT;
+ break;
+
+ case VDAAE_LU_ONLINE:
+ case VDAAE_LU_DEGRADED:
+ t->new_target_state = TS_PRESENT;
+ break;
+ }
+ }
+
+ if (t->new_target_state != TS_INVALID) {
+ memcpy(&t->lu_event, &ae->lu, cplen);
+
+ esas2r_disc_queue_event(a, DCDE_DEV_CHANGE);
+ }
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+}
+
+
+
+void esas2r_ae_complete(struct esas2r_adapter *a, struct esas2r_request *rq)
+{
+ union atto_vda_ae *ae =
+ (union atto_vda_ae *)rq->vda_rsp_data->ae_data.event_data;
+ u32 length = le32_to_cpu(rq->func_rsp.ae_rsp.length);
+ union atto_vda_ae *last =
+ (union atto_vda_ae *)(rq->vda_rsp_data->ae_data.event_data
+ + length);
+
+ esas2r_trace_enter();
+ esas2r_trace("length: %d", length);
+
+ if (length > sizeof(struct atto_vda_ae_data)
+ || (length & 3) != 0
+ || length == 0) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "The AE request response length (%p) is too long: %d",
+ rq, length);
+
+ esas2r_hdebug("aereq->length (0x%x) too long", length);
+ esas2r_bugon();
+
+ last = ae;
+ }
+
+ while (ae < last) {
+ u16 target;
+
+ esas2r_trace("ae: %p", ae);
+ esas2r_trace("ae->hdr: %p", &(ae->hdr));
+
+ length = ae->hdr.bylength;
+
+ if (length > (u32)((u8 *)last - (u8 *)ae)
+ || (length & 3) != 0
+ || length == 0) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "the async event length is invalid (%p): %d",
+ ae, length);
+
+ esas2r_hdebug("ae->hdr.length (0x%x) invalid", length);
+ esas2r_bugon();
+
+ break;
+ }
+
+ esas2r_nuxi_ae_data(ae);
+
+ esas2r_queue_fw_event(a, fw_event_vda_ae, ae,
+ sizeof(union atto_vda_ae));
+
+ switch (ae->hdr.bytype) {
+ case VDAAE_HDR_TYPE_RAID:
+
+ if (ae->raid.dwflags & (VDAAE_GROUP_STATE
+ | VDAAE_RBLD_STATE
+ | VDAAE_MEMBER_CHG
+ | VDAAE_PART_CHG)) {
+ esas2r_log(ESAS2R_LOG_INFO,
+ "RAID event received - name:%s rebuild_state:%d group_state:%d",
+ ae->raid.acname,
+ ae->raid.byrebuild_state,
+ ae->raid.bygroup_state);
+ }
+
+ break;
+
+ case VDAAE_HDR_TYPE_LU:
+ esas2r_log(ESAS2R_LOG_INFO,
+ "LUN event received: event:%d target_id:%d LUN:%d state:%d",
+ ae->lu.dwevent,
+ ae->lu.id.tgtlun.wtarget_id,
+ ae->lu.id.tgtlun.bylun,
+ ae->lu.bystate);
+
+ target = ae->lu.id.tgtlun.wtarget_id;
+
+ if (target < ESAS2R_MAX_TARGETS)
+ esas2r_lun_event(a, ae, target, length);
+
+ break;
+
+ case VDAAE_HDR_TYPE_DISK:
+ esas2r_log(ESAS2R_LOG_INFO, "Disk event received");
+ break;
+
+ default:
+
+ /* Silently ignore the rest and let the apps deal with
+ * them.
+ */
+
+ break;
+ }
+
+ ae = (union atto_vda_ae *)((u8 *)ae + length);
+ }
+
+ /* Now requeue it. */
+ esas2r_start_ae_request(a, rq);
+ esas2r_trace_exit();
+}
+
+/* Send an asynchronous event for a chip reset or power management. */
+void esas2r_send_reset_ae(struct esas2r_adapter *a, bool pwr_mgt)
+{
+ struct atto_vda_ae_hdr ae;
+
+ if (pwr_mgt)
+ ae.bytype = VDAAE_HDR_TYPE_PWRMGT;
+ else
+ ae.bytype = VDAAE_HDR_TYPE_RESET;
+
+ ae.byversion = VDAAE_HDR_VER_0;
+ ae.byflags = 0;
+ ae.bylength = (u8)sizeof(struct atto_vda_ae_hdr);
+
+ if (pwr_mgt) {
+ esas2r_hdebug("*** sending power management AE ***");
+ } else {
+ esas2r_hdebug("*** sending reset AE ***");
+ }
+
+ esas2r_queue_fw_event(a, fw_event_vda_ae, &ae,
+ sizeof(union atto_vda_ae));
+}
+
+void esas2r_dummy_complete(struct esas2r_adapter *a, struct esas2r_request *rq)
+{}
+
+static void esas2r_check_req_rsp_sense(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ u8 snslen, snslen2;
+
+ snslen = snslen2 = rq->func_rsp.scsi_rsp.sense_len;
+
+ if (snslen > rq->sense_len)
+ snslen = rq->sense_len;
+
+ if (snslen) {
+ if (rq->sense_buf)
+ memcpy(rq->sense_buf, rq->data_buf, snslen);
+ else
+ rq->sense_buf = (u8 *)rq->data_buf;
+
+ /* See about possible sense data */
+ if (snslen2 > 0x0c) {
+ u8 *s = (u8 *)rq->data_buf;
+
+ esas2r_trace_enter();
+
+ /* Report LUNS data has changed */
+ if (s[0x0c] == 0x3f && s[0x0d] == 0x0E) {
+ esas2r_trace("rq->target_id: %d",
+ rq->target_id);
+ esas2r_target_state_changed(a, rq->target_id,
+ TS_LUN_CHANGE);
+ }
+
+ esas2r_trace("add_sense_key=%x", s[0x0c]);
+ esas2r_trace("add_sense_qual=%x", s[0x0d]);
+ esas2r_trace_exit();
+ }
+ }
+
+ rq->sense_len = snslen;
+}
+
+
+void esas2r_complete_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ if (rq->vrq->scsi.function == VDA_FUNC_FLASH
+ && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
+ clear_bit(AF_FLASHING, &a->flags);
+
+ /* See if we setup a callback to do special processing */
+
+ if (rq->interrupt_cb) {
+ (*rq->interrupt_cb)(a, rq);
+
+ if (rq->req_stat == RS_PENDING) {
+ esas2r_start_request(a, rq);
+ return;
+ }
+ }
+
+ if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)
+ && unlikely(rq->req_stat != RS_SUCCESS)) {
+ esas2r_check_req_rsp_sense(a, rq);
+ esas2r_log_request_failure(a, rq);
+ }
+
+ (*rq->comp_cb)(a, rq);
+}
diff --git a/drivers/scsi/esas2r/esas2r_io.c b/drivers/scsi/esas2r/esas2r_io.c
new file mode 100644
index 000000000000..a8df916cd57a
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_io.c
@@ -0,0 +1,877 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_io.c
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)mpt3sas/mpt3sas_trigger_diag.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+void esas2r_start_request(struct esas2r_adapter *a, struct esas2r_request *rq)
+{
+ struct esas2r_target *t = NULL;
+ struct esas2r_request *startrq = rq;
+ unsigned long flags;
+
+ if (unlikely(test_bit(AF_DEGRADED_MODE, &a->flags) ||
+ test_bit(AF_POWER_DOWN, &a->flags))) {
+ if (rq->vrq->scsi.function == VDA_FUNC_SCSI)
+ rq->req_stat = RS_SEL2;
+ else
+ rq->req_stat = RS_DEGRADED;
+ } else if (likely(rq->vrq->scsi.function == VDA_FUNC_SCSI)) {
+ t = a->targetdb + rq->target_id;
+
+ if (unlikely(t >= a->targetdb_end
+ || !(t->flags & TF_USED))) {
+ rq->req_stat = RS_SEL;
+ } else {
+ /* copy in the target ID. */
+ rq->vrq->scsi.target_id = cpu_to_le16(t->virt_targ_id);
+
+ /*
+ * Test if we want to report RS_SEL for missing target.
+ * Note that if AF_DISC_PENDING is set than this will
+ * go on the defer queue.
+ */
+ if (unlikely(t->target_state != TS_PRESENT &&
+ !test_bit(AF_DISC_PENDING, &a->flags)))
+ rq->req_stat = RS_SEL;
+ }
+ }
+
+ if (unlikely(rq->req_stat != RS_PENDING)) {
+ esas2r_complete_request(a, rq);
+ return;
+ }
+
+ esas2r_trace("rq=%p", rq);
+ esas2r_trace("rq->vrq->scsi.handle=%x", rq->vrq->scsi.handle);
+
+ if (rq->vrq->scsi.function == VDA_FUNC_SCSI) {
+ esas2r_trace("rq->target_id=%d", rq->target_id);
+ esas2r_trace("rq->vrq->scsi.flags=%x", rq->vrq->scsi.flags);
+ }
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ if (likely(list_empty(&a->defer_list) &&
+ !test_bit(AF_CHPRST_PENDING, &a->flags) &&
+ !test_bit(AF_FLASHING, &a->flags) &&
+ !test_bit(AF_DISC_PENDING, &a->flags)))
+ esas2r_local_start_request(a, startrq);
+ else
+ list_add_tail(&startrq->req_list, &a->defer_list);
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+}
+
+/*
+ * Starts the specified request. all requests have RS_PENDING set when this
+ * routine is called. The caller is usually esas2r_start_request, but
+ * esas2r_do_deferred_processes will start request that are deferred.
+ *
+ * The caller must ensure that requests can be started.
+ *
+ * esas2r_start_request will defer a request if there are already requests
+ * waiting or there is a chip reset pending. once the reset condition clears,
+ * esas2r_do_deferred_processes will call this function to start the request.
+ *
+ * When a request is started, it is placed on the active list and queued to
+ * the controller.
+ */
+void esas2r_local_start_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ esas2r_trace_enter();
+ esas2r_trace("rq=%p", rq);
+ esas2r_trace("rq->vrq:%p", rq->vrq);
+ esas2r_trace("rq->vrq_md->phys_addr:%x", rq->vrq_md->phys_addr);
+
+ if (unlikely(rq->vrq->scsi.function == VDA_FUNC_FLASH
+ && rq->vrq->flash.sub_func == VDA_FLASH_COMMIT))
+ set_bit(AF_FLASHING, &a->flags);
+
+ list_add_tail(&rq->req_list, &a->active_list);
+ esas2r_start_vda_request(a, rq);
+ esas2r_trace_exit();
+ return;
+}
+
+void esas2r_start_vda_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct esas2r_inbound_list_source_entry *element;
+ u32 dw;
+
+ rq->req_stat = RS_STARTED;
+ /*
+ * Calculate the inbound list entry location and the current state of
+ * toggle bit.
+ */
+ a->last_write++;
+ if (a->last_write >= a->list_size) {
+ a->last_write = 0;
+ /* update the toggle bit */
+ if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags))
+ clear_bit(AF_COMM_LIST_TOGGLE, &a->flags);
+ else
+ set_bit(AF_COMM_LIST_TOGGLE, &a->flags);
+ }
+
+ element =
+ (struct esas2r_inbound_list_source_entry *)a->inbound_list_md.
+ virt_addr
+ + a->last_write;
+
+ /* Set the VDA request size if it was never modified */
+ if (rq->vda_req_sz == RQ_SIZE_DEFAULT)
+ rq->vda_req_sz = (u16)(a->max_vdareq_size / sizeof(u32));
+
+ element->address = cpu_to_le64(rq->vrq_md->phys_addr);
+ element->length = cpu_to_le32(rq->vda_req_sz);
+
+ /* Update the write pointer */
+ dw = a->last_write;
+
+ if (test_bit(AF_COMM_LIST_TOGGLE, &a->flags))
+ dw |= MU_ILW_TOGGLE;
+
+ esas2r_trace("rq->vrq->scsi.handle:%x", rq->vrq->scsi.handle);
+ esas2r_trace("dw:%x", dw);
+ esas2r_trace("rq->vda_req_sz:%x", rq->vda_req_sz);
+ esas2r_write_register_dword(a, MU_IN_LIST_WRITE, dw);
+}
+
+/*
+ * Build the scatter/gather list for an I/O request according to the
+ * specifications placed in the s/g context. The caller must initialize
+ * context prior to the initial call by calling esas2r_sgc_init().
+ */
+bool esas2r_build_sg_list_sge(struct esas2r_adapter *a,
+ struct esas2r_sg_context *sgc)
+{
+ struct esas2r_request *rq = sgc->first_req;
+ union atto_vda_req *vrq = rq->vrq;
+
+ while (sgc->length) {
+ u32 rem = 0;
+ u64 addr;
+ u32 len;
+
+ len = (*sgc->get_phys_addr)(sgc, &addr);
+
+ if (unlikely(len == 0))
+ return false;
+
+ /* if current length is more than what's left, stop there */
+ if (unlikely(len > sgc->length))
+ len = sgc->length;
+
+another_entry:
+ /* limit to a round number less than the maximum length */
+ if (len > SGE_LEN_MAX) {
+ /*
+ * Save the remainder of the split. Whenever we limit
+ * an entry we come back around to build entries out
+ * of the leftover. We do this to prevent multiple
+ * calls to the get_phys_addr() function for an SGE
+ * that is too large.
+ */
+ rem = len - SGE_LEN_MAX;
+ len = SGE_LEN_MAX;
+ }
+
+ /* See if we need to allocate a new SGL */
+ if (unlikely(sgc->sge.a64.curr > sgc->sge.a64.limit)) {
+ u8 sgelen;
+ struct esas2r_mem_desc *sgl;
+
+ /*
+ * If no SGls are available, return failure. The
+ * caller can call us later with the current context
+ * to pick up here.
+ */
+ sgl = esas2r_alloc_sgl(a);
+
+ if (unlikely(sgl == NULL))
+ return false;
+
+ /* Calculate the length of the last SGE filled in */
+ sgelen = (u8)((u8 *)sgc->sge.a64.curr
+ - (u8 *)sgc->sge.a64.last);
+
+ /*
+ * Copy the last SGE filled in to the first entry of
+ * the new SGL to make room for the chain entry.
+ */
+ memcpy(sgl->virt_addr, sgc->sge.a64.last, sgelen);
+
+ /* Figure out the new curr pointer in the new segment */
+ sgc->sge.a64.curr =
+ (struct atto_vda_sge *)((u8 *)sgl->virt_addr +
+ sgelen);
+
+ /* Set the limit pointer and build the chain entry */
+ sgc->sge.a64.limit =
+ (struct atto_vda_sge *)((u8 *)sgl->virt_addr
+ + sgl_page_size
+ - sizeof(struct
+ atto_vda_sge));
+ sgc->sge.a64.last->length = cpu_to_le32(
+ SGE_CHAIN | SGE_ADDR_64);
+ sgc->sge.a64.last->address =
+ cpu_to_le64(sgl->phys_addr);
+
+ /*
+ * Now, if there was a previous chain entry, then
+ * update it to contain the length of this segment
+ * and size of this chain. otherwise this is the
+ * first SGL, so set the chain_offset in the request.
+ */
+ if (sgc->sge.a64.chain) {
+ sgc->sge.a64.chain->length |=
+ cpu_to_le32(
+ ((u8 *)(sgc->sge.a64.
+ last + 1)
+ - (u8 *)rq->sg_table->
+ virt_addr)
+ + sizeof(struct atto_vda_sge) *
+ LOBIT(SGE_CHAIN_SZ));
+ } else {
+ vrq->scsi.chain_offset = (u8)
+ ((u8 *)sgc->
+ sge.a64.last -
+ (u8 *)vrq);
+
+ /*
+ * This is the first SGL, so set the
+ * chain_offset and the VDA request size in
+ * the request.
+ */
+ rq->vda_req_sz =
+ (vrq->scsi.chain_offset +
+ sizeof(struct atto_vda_sge) +
+ 3)
+ / sizeof(u32);
+ }
+
+ /*
+ * Remember this so when we get a new SGL filled in we
+ * can update the length of this chain entry.
+ */
+ sgc->sge.a64.chain = sgc->sge.a64.last;
+
+ /* Now link the new SGL onto the primary request. */
+ list_add(&sgl->next_desc, &rq->sg_table_head);
+ }
+
+ /* Update last one filled in */
+ sgc->sge.a64.last = sgc->sge.a64.curr;
+
+ /* Build the new SGE and update the S/G context */
+ sgc->sge.a64.curr->length = cpu_to_le32(SGE_ADDR_64 | len);
+ sgc->sge.a64.curr->address = cpu_to_le32(addr);
+ sgc->sge.a64.curr++;
+ sgc->cur_offset += len;
+ sgc->length -= len;
+
+ /*
+ * Check if we previously split an entry. If so we have to
+ * pick up where we left off.
+ */
+ if (rem) {
+ addr += len;
+ len = rem;
+ rem = 0;
+ goto another_entry;
+ }
+ }
+
+ /* Mark the end of the SGL */
+ sgc->sge.a64.last->length |= cpu_to_le32(SGE_LAST);
+
+ /*
+ * If there was a previous chain entry, update the length to indicate
+ * the length of this last segment.
+ */
+ if (sgc->sge.a64.chain) {
+ sgc->sge.a64.chain->length |= cpu_to_le32(
+ ((u8 *)(sgc->sge.a64.curr) -
+ (u8 *)rq->sg_table->virt_addr));
+ } else {
+ u16 reqsize;
+
+ /*
+ * The entire VDA request was not used so lets
+ * set the size of the VDA request to be DMA'd
+ */
+ reqsize =
+ ((u16)((u8 *)sgc->sge.a64.last - (u8 *)vrq)
+ + sizeof(struct atto_vda_sge) + 3) / sizeof(u32);
+
+ /*
+ * Only update the request size if it is bigger than what is
+ * already there. We can come in here twice for some management
+ * commands.
+ */
+ if (reqsize > rq->vda_req_sz)
+ rq->vda_req_sz = reqsize;
+ }
+ return true;
+}
+
+
+/*
+ * Create PRD list for each I-block consumed by the command. This routine
+ * determines how much data is required from each I-block being consumed
+ * by the command. The first and last I-blocks can be partials and all of
+ * the I-blocks in between are for a full I-block of data.
+ *
+ * The interleave size is used to determine the number of bytes in the 1st
+ * I-block and the remaining I-blocks are what remeains.
+ */
+static bool esas2r_build_prd_iblk(struct esas2r_adapter *a,
+ struct esas2r_sg_context *sgc)
+{
+ struct esas2r_request *rq = sgc->first_req;
+ u64 addr;
+ u32 len;
+ struct esas2r_mem_desc *sgl;
+ u32 numchain = 1;
+ u32 rem = 0;
+
+ while (sgc->length) {
+ /* Get the next address/length pair */
+
+ len = (*sgc->get_phys_addr)(sgc, &addr);
+
+ if (unlikely(len == 0))
+ return false;
+
+ /* If current length is more than what's left, stop there */
+
+ if (unlikely(len > sgc->length))
+ len = sgc->length;
+
+another_entry:
+ /* Limit to a round number less than the maximum length */
+
+ if (len > PRD_LEN_MAX) {
+ /*
+ * Save the remainder of the split. whenever we limit
+ * an entry we come back around to build entries out
+ * of the leftover. We do this to prevent multiple
+ * calls to the get_phys_addr() function for an SGE
+ * that is too large.
+ */
+ rem = len - PRD_LEN_MAX;
+ len = PRD_LEN_MAX;
+ }
+
+ /* See if we need to allocate a new SGL */
+ if (sgc->sge.prd.sge_cnt == 0) {
+ if (len == sgc->length) {
+ /*
+ * We only have 1 PRD entry left.
+ * It can be placed where the chain
+ * entry would have gone
+ */
+
+ /* Build the simple SGE */
+ sgc->sge.prd.curr->ctl_len = cpu_to_le32(
+ PRD_DATA | len);
+ sgc->sge.prd.curr->address = cpu_to_le64(addr);
+
+ /* Adjust length related fields */
+ sgc->cur_offset += len;
+ sgc->length -= len;
+
+ /* We use the reserved chain entry for data */
+ numchain = 0;
+
+ break;
+ }
+
+ if (sgc->sge.prd.chain) {
+ /*
+ * Fill # of entries of current SGL in previous
+ * chain the length of this current SGL may not
+ * full.
+ */
+
+ sgc->sge.prd.chain->ctl_len |= cpu_to_le32(
+ sgc->sge.prd.sgl_max_cnt);
+ }
+
+ /*
+ * If no SGls are available, return failure. The
+ * caller can call us later with the current context
+ * to pick up here.
+ */
+
+ sgl = esas2r_alloc_sgl(a);
+
+ if (unlikely(sgl == NULL))
+ return false;
+
+ /*
+ * Link the new SGL onto the chain
+ * They are in reverse order
+ */
+ list_add(&sgl->next_desc, &rq->sg_table_head);
+
+ /*
+ * An SGL was just filled in and we are starting
+ * a new SGL. Prime the chain of the ending SGL with
+ * info that points to the new SGL. The length gets
+ * filled in when the new SGL is filled or ended
+ */
+
+ sgc->sge.prd.chain = sgc->sge.prd.curr;
+
+ sgc->sge.prd.chain->ctl_len = cpu_to_le32(PRD_CHAIN);
+ sgc->sge.prd.chain->address =
+ cpu_to_le64(sgl->phys_addr);
+
+ /*
+ * Start a new segment.
+ * Take one away and save for chain SGE
+ */
+
+ sgc->sge.prd.curr =
+ (struct atto_physical_region_description *)sgl
+ ->
+ virt_addr;
+ sgc->sge.prd.sge_cnt = sgc->sge.prd.sgl_max_cnt - 1;
+ }
+
+ sgc->sge.prd.sge_cnt--;
+ /* Build the simple SGE */
+ sgc->sge.prd.curr->ctl_len = cpu_to_le32(PRD_DATA | len);
+ sgc->sge.prd.curr->address = cpu_to_le64(addr);
+
+ /* Used another element. Point to the next one */
+
+ sgc->sge.prd.curr++;
+
+ /* Adjust length related fields */
+
+ sgc->cur_offset += len;
+ sgc->length -= len;
+
+ /*
+ * Check if we previously split an entry. If so we have to
+ * pick up where we left off.
+ */
+
+ if (rem) {
+ addr += len;
+ len = rem;
+ rem = 0;
+ goto another_entry;
+ }
+ }
+
+ if (!list_empty(&rq->sg_table_head)) {
+ if (sgc->sge.prd.chain) {
+ sgc->sge.prd.chain->ctl_len |=
+ cpu_to_le32(sgc->sge.prd.sgl_max_cnt
+ - sgc->sge.prd.sge_cnt
+ - numchain);
+ }
+ }
+
+ return true;
+}
+
+bool esas2r_build_sg_list_prd(struct esas2r_adapter *a,
+ struct esas2r_sg_context *sgc)
+{
+ struct esas2r_request *rq = sgc->first_req;
+ u32 len = sgc->length;
+ struct esas2r_target *t = a->targetdb + rq->target_id;
+ u8 is_i_o = 0;
+ u16 reqsize;
+ struct atto_physical_region_description *curr_iblk_chn;
+ u8 *cdb = (u8 *)&rq->vrq->scsi.cdb[0];
+
+ /*
+ * extract LBA from command so we can determine
+ * the I-Block boundary
+ */
+
+ if (rq->vrq->scsi.function == VDA_FUNC_SCSI
+ && t->target_state == TS_PRESENT
+ && !(t->flags & TF_PASS_THRU)) {
+ u32 lbalo = 0;
+
+ switch (rq->vrq->scsi.cdb[0]) {
+ case READ_16:
+ case WRITE_16:
+ {
+ lbalo =
+ MAKEDWORD(MAKEWORD(cdb[9],
+ cdb[8]),
+ MAKEWORD(cdb[7],
+ cdb[6]));
+ is_i_o = 1;
+ break;
+ }
+
+ case READ_12:
+ case WRITE_12:
+ case READ_10:
+ case WRITE_10:
+ {
+ lbalo =
+ MAKEDWORD(MAKEWORD(cdb[5],
+ cdb[4]),
+ MAKEWORD(cdb[3],
+ cdb[2]));
+ is_i_o = 1;
+ break;
+ }
+
+ case READ_6:
+ case WRITE_6:
+ {
+ lbalo =
+ MAKEDWORD(MAKEWORD(cdb[3],
+ cdb[2]),
+ MAKEWORD(cdb[1] & 0x1F,
+ 0));
+ is_i_o = 1;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (is_i_o) {
+ u32 startlba;
+
+ rq->vrq->scsi.iblk_cnt_prd = 0;
+
+ /* Determine size of 1st I-block PRD list */
+ startlba = t->inter_block - (lbalo & (t->inter_block -
+ 1));
+ sgc->length = startlba * t->block_size;
+
+ /* Chk if the 1st iblk chain starts at base of Iblock */
+ if ((lbalo & (t->inter_block - 1)) == 0)
+ rq->flags |= RF_1ST_IBLK_BASE;
+
+ if (sgc->length > len)
+ sgc->length = len;
+ } else {
+ sgc->length = len;
+ }
+ } else {
+ sgc->length = len;
+ }
+
+ /* get our starting chain address */
+
+ curr_iblk_chn =
+ (struct atto_physical_region_description *)sgc->sge.a64.curr;
+
+ sgc->sge.prd.sgl_max_cnt = sgl_page_size /
+ sizeof(struct
+ atto_physical_region_description);
+
+ /* create all of the I-block PRD lists */
+
+ while (len) {
+ sgc->sge.prd.sge_cnt = 0;
+ sgc->sge.prd.chain = NULL;
+ sgc->sge.prd.curr = curr_iblk_chn;
+
+ /* increment to next I-Block */
+
+ len -= sgc->length;
+
+ /* go build the next I-Block PRD list */
+
+ if (unlikely(!esas2r_build_prd_iblk(a, sgc)))
+ return false;
+
+ curr_iblk_chn++;
+
+ if (is_i_o) {
+ rq->vrq->scsi.iblk_cnt_prd++;
+
+ if (len > t->inter_byte)
+ sgc->length = t->inter_byte;
+ else
+ sgc->length = len;
+ }
+ }
+
+ /* figure out the size used of the VDA request */
+
+ reqsize = ((u16)((u8 *)curr_iblk_chn - (u8 *)rq->vrq))
+ / sizeof(u32);
+
+ /*
+ * only update the request size if it is bigger than what is
+ * already there. we can come in here twice for some management
+ * commands.
+ */
+
+ if (reqsize > rq->vda_req_sz)
+ rq->vda_req_sz = reqsize;
+
+ return true;
+}
+
+static void esas2r_handle_pending_reset(struct esas2r_adapter *a, u32 currtime)
+{
+ u32 delta = currtime - a->chip_init_time;
+
+ if (delta <= ESAS2R_CHPRST_WAIT_TIME) {
+ /* Wait before accessing registers */
+ } else if (delta >= ESAS2R_CHPRST_TIME) {
+ /*
+ * The last reset failed so try again. Reset
+ * processing will give up after three tries.
+ */
+ esas2r_local_reset_adapter(a);
+ } else {
+ /* We can now see if the firmware is ready */
+ u32 doorbell;
+
+ doorbell = esas2r_read_register_dword(a, MU_DOORBELL_OUT);
+ if (doorbell == 0xFFFFFFFF || !(doorbell & DRBL_FORCE_INT)) {
+ esas2r_force_interrupt(a);
+ } else {
+ u32 ver = (doorbell & DRBL_FW_VER_MSK);
+
+ /* Driver supports API version 0 and 1 */
+ esas2r_write_register_dword(a, MU_DOORBELL_OUT,
+ doorbell);
+ if (ver == DRBL_FW_VER_0) {
+ set_bit(AF_CHPRST_DETECTED, &a->flags);
+ set_bit(AF_LEGACY_SGE_MODE, &a->flags);
+
+ a->max_vdareq_size = 128;
+ a->build_sgl = esas2r_build_sg_list_sge;
+ } else if (ver == DRBL_FW_VER_1) {
+ set_bit(AF_CHPRST_DETECTED, &a->flags);
+ clear_bit(AF_LEGACY_SGE_MODE, &a->flags);
+
+ a->max_vdareq_size = 1024;
+ a->build_sgl = esas2r_build_sg_list_prd;
+ } else {
+ esas2r_local_reset_adapter(a);
+ }
+ }
+ }
+}
+
+
+/* This function must be called once per timer tick */
+void esas2r_timer_tick(struct esas2r_adapter *a)
+{
+ u32 currtime = jiffies_to_msecs(jiffies);
+ u32 deltatime = currtime - a->last_tick_time;
+
+ a->last_tick_time = currtime;
+
+ /* count down the uptime */
+ if (a->chip_uptime &&
+ !test_bit(AF_CHPRST_PENDING, &a->flags) &&
+ !test_bit(AF_DISC_PENDING, &a->flags)) {
+ if (deltatime >= a->chip_uptime)
+ a->chip_uptime = 0;
+ else
+ a->chip_uptime -= deltatime;
+ }
+
+ if (test_bit(AF_CHPRST_PENDING, &a->flags)) {
+ if (!test_bit(AF_CHPRST_NEEDED, &a->flags) &&
+ !test_bit(AF_CHPRST_DETECTED, &a->flags))
+ esas2r_handle_pending_reset(a, currtime);
+ } else {
+ if (test_bit(AF_DISC_PENDING, &a->flags))
+ esas2r_disc_check_complete(a);
+ if (test_bit(AF_HEARTBEAT_ENB, &a->flags)) {
+ if (test_bit(AF_HEARTBEAT, &a->flags)) {
+ if ((currtime - a->heartbeat_time) >=
+ ESAS2R_HEARTBEAT_TIME) {
+ clear_bit(AF_HEARTBEAT, &a->flags);
+ esas2r_hdebug("heartbeat failed");
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "heartbeat failed");
+ esas2r_bugon();
+ esas2r_local_reset_adapter(a);
+ }
+ } else {
+ set_bit(AF_HEARTBEAT, &a->flags);
+ a->heartbeat_time = currtime;
+ esas2r_force_interrupt(a);
+ }
+ }
+ }
+
+ if (atomic_read(&a->disable_cnt) == 0)
+ esas2r_do_deferred_processes(a);
+}
+
+/*
+ * Send the specified task management function to the target and LUN
+ * specified in rqaux. in addition, immediately abort any commands that
+ * are queued but not sent to the device according to the rules specified
+ * by the task management function.
+ */
+bool esas2r_send_task_mgmt(struct esas2r_adapter *a,
+ struct esas2r_request *rqaux, u8 task_mgt_func)
+{
+ u16 targetid = rqaux->target_id;
+ u8 lun = (u8)le32_to_cpu(rqaux->vrq->scsi.flags);
+ bool ret = false;
+ struct esas2r_request *rq;
+ struct list_head *next, *element;
+ unsigned long flags;
+
+ LIST_HEAD(comp_list);
+
+ esas2r_trace_enter();
+ esas2r_trace("rqaux:%p", rqaux);
+ esas2r_trace("task_mgt_func:%x", task_mgt_func);
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ /* search the defer queue looking for requests for the device */
+ list_for_each_safe(element, next, &a->defer_list) {
+ rq = list_entry(element, struct esas2r_request, req_list);
+
+ if (rq->vrq->scsi.function == VDA_FUNC_SCSI
+ && rq->target_id == targetid
+ && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun
+ || task_mgt_func == 0x20)) { /* target reset */
+ /* Found a request affected by the task management */
+ if (rq->req_stat == RS_PENDING) {
+ /*
+ * The request is pending or waiting. We can
+ * safelycomplete the request now.
+ */
+ if (esas2r_ioreq_aborted(a, rq, RS_ABORTED))
+ list_add_tail(&rq->comp_list,
+ &comp_list);
+ }
+ }
+ }
+
+ /* Send the task management request to the firmware */
+ rqaux->sense_len = 0;
+ rqaux->vrq->scsi.length = 0;
+ rqaux->target_id = targetid;
+ rqaux->vrq->scsi.flags |= cpu_to_le32(lun);
+ memset(rqaux->vrq->scsi.cdb, 0, sizeof(rqaux->vrq->scsi.cdb));
+ rqaux->vrq->scsi.flags |=
+ cpu_to_le16(task_mgt_func * LOBIT(FCP_CMND_TM_MASK));
+
+ if (test_bit(AF_FLASHING, &a->flags)) {
+ /* Assume success. if there are active requests, return busy */
+ rqaux->req_stat = RS_SUCCESS;
+
+ list_for_each_safe(element, next, &a->active_list) {
+ rq = list_entry(element, struct esas2r_request,
+ req_list);
+ if (rq->vrq->scsi.function == VDA_FUNC_SCSI
+ && rq->target_id == targetid
+ && (((u8)le32_to_cpu(rq->vrq->scsi.flags)) == lun
+ || task_mgt_func == 0x20)) /* target reset */
+ rqaux->req_stat = RS_BUSY;
+ }
+
+ ret = true;
+ }
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+
+ if (!test_bit(AF_FLASHING, &a->flags))
+ esas2r_start_request(a, rqaux);
+
+ esas2r_comp_list_drain(a, &comp_list);
+
+ if (atomic_read(&a->disable_cnt) == 0)
+ esas2r_do_deferred_processes(a);
+
+ esas2r_trace_exit();
+
+ return ret;
+}
+
+void esas2r_reset_bus(struct esas2r_adapter *a)
+{
+ esas2r_log(ESAS2R_LOG_INFO, "performing a bus reset");
+
+ if (!test_bit(AF_DEGRADED_MODE, &a->flags) &&
+ !test_bit(AF_CHPRST_PENDING, &a->flags) &&
+ !test_bit(AF_DISC_PENDING, &a->flags)) {
+ set_bit(AF_BUSRST_NEEDED, &a->flags);
+ set_bit(AF_BUSRST_PENDING, &a->flags);
+ set_bit(AF_OS_RESET, &a->flags);
+
+ esas2r_schedule_tasklet(a);
+ }
+}
+
+bool esas2r_ioreq_aborted(struct esas2r_adapter *a, struct esas2r_request *rq,
+ u8 status)
+{
+ esas2r_trace_enter();
+ esas2r_trace("rq:%p", rq);
+ list_del_init(&rq->req_list);
+ if (rq->timeout > RQ_MAX_TIMEOUT) {
+ /*
+ * The request timed out, but we could not abort it because a
+ * chip reset occurred. Return busy status.
+ */
+ rq->req_stat = RS_BUSY;
+ esas2r_trace_exit();
+ return true;
+ }
+
+ rq->req_stat = status;
+ esas2r_trace_exit();
+ return true;
+}
diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c
new file mode 100644
index 000000000000..3f7c1d131ec3
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -0,0 +1,2083 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_ioctl.c
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/bitfield.h>
+
+#include "esas2r.h"
+
+/*
+ * Buffered ioctl handlers. A buffered ioctl is one which requires that we
+ * allocate a DMA-able memory area to communicate with the firmware. In
+ * order to prevent continually allocating and freeing consistent memory,
+ * we will allocate a global buffer the first time we need it and re-use
+ * it for subsequent ioctl calls that require it.
+ */
+
+u8 *esas2r_buffered_ioctl;
+dma_addr_t esas2r_buffered_ioctl_addr;
+u32 esas2r_buffered_ioctl_size;
+struct pci_dev *esas2r_buffered_ioctl_pcid;
+
+static DEFINE_SEMAPHORE(buffered_ioctl_semaphore, 1);
+typedef int (*BUFFERED_IOCTL_CALLBACK)(struct esas2r_adapter *,
+ struct esas2r_request *,
+ struct esas2r_sg_context *,
+ void *);
+typedef void (*BUFFERED_IOCTL_DONE_CALLBACK)(struct esas2r_adapter *,
+ struct esas2r_request *, void *);
+
+struct esas2r_buffered_ioctl {
+ struct esas2r_adapter *a;
+ void *ioctl;
+ u32 length;
+ u32 control_code;
+ u32 offset;
+ BUFFERED_IOCTL_CALLBACK
+ callback;
+ void *context;
+ BUFFERED_IOCTL_DONE_CALLBACK
+ done_callback;
+ void *done_context;
+
+};
+
+static void complete_fm_api_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ a->fm_api_command_done = 1;
+ wake_up_interruptible(&a->fm_api_waiter);
+}
+
+/* Callbacks for building scatter/gather lists for FM API requests */
+static u32 get_physaddr_fm_api(struct esas2r_sg_context *sgc, u64 *addr)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
+ int offset = sgc->cur_offset - a->save_offset;
+
+ (*addr) = a->firmware.phys + offset;
+ return a->firmware.orig_len - offset;
+}
+
+static u32 get_physaddr_fm_api_header(struct esas2r_sg_context *sgc, u64 *addr)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
+ int offset = sgc->cur_offset - a->save_offset;
+
+ (*addr) = a->firmware.header_buff_phys + offset;
+ return sizeof(struct esas2r_flash_img) - offset;
+}
+
+/* Handle EXPRESS_IOCTL_RW_FIRMWARE ioctl with img_type = FW_IMG_FM_API. */
+static void do_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
+{
+ struct esas2r_request *rq;
+
+ if (mutex_lock_interruptible(&a->fm_api_mutex)) {
+ fi->status = FI_STAT_BUSY;
+ return;
+ }
+
+ rq = esas2r_alloc_request(a);
+ if (rq == NULL) {
+ fi->status = FI_STAT_BUSY;
+ goto free_sem;
+ }
+
+ if (fi == &a->firmware.header) {
+ a->firmware.header_buff = dma_alloc_coherent(&a->pcid->dev,
+ (size_t)sizeof(
+ struct
+ esas2r_flash_img),
+ (dma_addr_t *)&a->
+ firmware.
+ header_buff_phys,
+ GFP_KERNEL);
+
+ if (a->firmware.header_buff == NULL) {
+ esas2r_debug("failed to allocate header buffer!");
+ fi->status = FI_STAT_BUSY;
+ goto free_req;
+ }
+
+ memcpy(a->firmware.header_buff, fi,
+ sizeof(struct esas2r_flash_img));
+ a->save_offset = a->firmware.header_buff;
+ a->fm_api_sgc.get_phys_addr =
+ (PGETPHYSADDR)get_physaddr_fm_api_header;
+ } else {
+ a->save_offset = (u8 *)fi;
+ a->fm_api_sgc.get_phys_addr =
+ (PGETPHYSADDR)get_physaddr_fm_api;
+ }
+
+ rq->comp_cb = complete_fm_api_req;
+ a->fm_api_command_done = 0;
+ a->fm_api_sgc.cur_offset = a->save_offset;
+
+ if (!esas2r_fm_api(a, (struct esas2r_flash_img *)a->save_offset, rq,
+ &a->fm_api_sgc))
+ goto all_done;
+
+ /* Now wait around for it to complete. */
+ while (!a->fm_api_command_done)
+ wait_event_interruptible(a->fm_api_waiter,
+ a->fm_api_command_done);
+all_done:
+ if (fi == &a->firmware.header) {
+ memcpy(fi, a->firmware.header_buff,
+ sizeof(struct esas2r_flash_img));
+
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)sizeof(struct esas2r_flash_img),
+ a->firmware.header_buff,
+ (dma_addr_t)a->firmware.header_buff_phys);
+ }
+free_req:
+ esas2r_free_request(a, (struct esas2r_request *)rq);
+free_sem:
+ mutex_unlock(&a->fm_api_mutex);
+ return;
+
+}
+
+static void complete_nvr_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ a->nvram_command_done = 1;
+ wake_up_interruptible(&a->nvram_waiter);
+}
+
+/* Callback for building scatter/gather lists for buffered ioctls */
+static u32 get_physaddr_buffered_ioctl(struct esas2r_sg_context *sgc,
+ u64 *addr)
+{
+ int offset = (u8 *)sgc->cur_offset - esas2r_buffered_ioctl;
+
+ (*addr) = esas2r_buffered_ioctl_addr + offset;
+ return esas2r_buffered_ioctl_size - offset;
+}
+
+static void complete_buffered_ioctl_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ a->buffered_ioctl_done = 1;
+ wake_up_interruptible(&a->buffered_ioctl_waiter);
+}
+
+static u8 handle_buffered_ioctl(struct esas2r_buffered_ioctl *bi)
+{
+ struct esas2r_adapter *a = bi->a;
+ struct esas2r_request *rq;
+ struct esas2r_sg_context sgc;
+ u8 result = IOCTL_SUCCESS;
+
+ if (down_interruptible(&buffered_ioctl_semaphore))
+ return IOCTL_OUT_OF_RESOURCES;
+
+ /* allocate a buffer or use the existing buffer. */
+ if (esas2r_buffered_ioctl) {
+ if (esas2r_buffered_ioctl_size < bi->length) {
+ /* free the too-small buffer and get a new one */
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)esas2r_buffered_ioctl_size,
+ esas2r_buffered_ioctl,
+ esas2r_buffered_ioctl_addr);
+
+ goto allocate_buffer;
+ }
+ } else {
+allocate_buffer:
+ esas2r_buffered_ioctl_size = bi->length;
+ esas2r_buffered_ioctl_pcid = a->pcid;
+ esas2r_buffered_ioctl = dma_alloc_coherent(&a->pcid->dev,
+ (size_t)
+ esas2r_buffered_ioctl_size,
+ &
+ esas2r_buffered_ioctl_addr,
+ GFP_KERNEL);
+ }
+
+ if (!esas2r_buffered_ioctl) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "could not allocate %d bytes of consistent memory "
+ "for a buffered ioctl!",
+ bi->length);
+
+ esas2r_debug("buffered ioctl alloc failure");
+ result = IOCTL_OUT_OF_RESOURCES;
+ goto exit_cleanly;
+ }
+
+ memcpy(esas2r_buffered_ioctl, bi->ioctl, bi->length);
+
+ rq = esas2r_alloc_request(a);
+ if (rq == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "could not allocate an internal request");
+
+ result = IOCTL_OUT_OF_RESOURCES;
+ esas2r_debug("buffered ioctl - no requests");
+ goto exit_cleanly;
+ }
+
+ a->buffered_ioctl_done = 0;
+ rq->comp_cb = complete_buffered_ioctl_req;
+ sgc.cur_offset = esas2r_buffered_ioctl + bi->offset;
+ sgc.get_phys_addr = (PGETPHYSADDR)get_physaddr_buffered_ioctl;
+ sgc.length = esas2r_buffered_ioctl_size;
+
+ if (!(*bi->callback)(a, rq, &sgc, bi->context)) {
+ /* completed immediately, no need to wait */
+ a->buffered_ioctl_done = 0;
+ goto free_andexit_cleanly;
+ }
+
+ /* now wait around for it to complete. */
+ while (!a->buffered_ioctl_done)
+ wait_event_interruptible(a->buffered_ioctl_waiter,
+ a->buffered_ioctl_done);
+
+free_andexit_cleanly:
+ if (result == IOCTL_SUCCESS && bi->done_callback)
+ (*bi->done_callback)(a, rq, bi->done_context);
+
+ esas2r_free_request(a, rq);
+
+exit_cleanly:
+ if (result == IOCTL_SUCCESS)
+ memcpy(bi->ioctl, esas2r_buffered_ioctl, bi->length);
+
+ up(&buffered_ioctl_semaphore);
+ return result;
+}
+
+/* SMP ioctl support */
+static int smp_ioctl_callback(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc, void *context)
+{
+ struct atto_ioctl_smp *si =
+ (struct atto_ioctl_smp *)esas2r_buffered_ioctl;
+
+ esas2r_sgc_init(sgc, a, rq, rq->vrq->ioctl.sge);
+ esas2r_build_ioctl_req(a, rq, sgc->length, VDA_IOCTL_SMP);
+
+ if (!esas2r_build_sg_list(a, rq, sgc)) {
+ si->status = ATTO_STS_OUT_OF_RSRC;
+ return false;
+ }
+
+ esas2r_start_request(a, rq);
+ return true;
+}
+
+static u8 handle_smp_ioctl(struct esas2r_adapter *a, struct atto_ioctl_smp *si)
+{
+ struct esas2r_buffered_ioctl bi;
+
+ memset(&bi, 0, sizeof(bi));
+
+ bi.a = a;
+ bi.ioctl = si;
+ bi.length = sizeof(struct atto_ioctl_smp)
+ + le32_to_cpu(si->req_length)
+ + le32_to_cpu(si->rsp_length);
+ bi.offset = 0;
+ bi.callback = smp_ioctl_callback;
+ return handle_buffered_ioctl(&bi);
+}
+
+
+/* CSMI ioctl support */
+static void esas2r_csmi_ioctl_tunnel_comp_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ rq->target_id = le16_to_cpu(rq->func_rsp.ioctl_rsp.csmi.target_id);
+ rq->vrq->scsi.flags |= cpu_to_le32(rq->func_rsp.ioctl_rsp.csmi.lun);
+
+ /* Now call the original completion callback. */
+ (*rq->aux_req_cb)(a, rq);
+}
+
+/* Tunnel a CSMI IOCTL to the back end driver for processing. */
+static bool csmi_ioctl_tunnel(struct esas2r_adapter *a,
+ union atto_ioctl_csmi *ci,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc,
+ u32 ctrl_code,
+ u16 target_id)
+{
+ struct atto_vda_ioctl_req *ioctl = &rq->vrq->ioctl;
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return false;
+
+ esas2r_sgc_init(sgc, a, rq, rq->vrq->ioctl.sge);
+ esas2r_build_ioctl_req(a, rq, sgc->length, VDA_IOCTL_CSMI);
+ ioctl->csmi.ctrl_code = cpu_to_le32(ctrl_code);
+ ioctl->csmi.target_id = cpu_to_le16(target_id);
+ ioctl->csmi.lun = (u8)le32_to_cpu(rq->vrq->scsi.flags);
+
+ /*
+ * Always usurp the completion callback since the interrupt callback
+ * mechanism may be used.
+ */
+ rq->aux_req_cx = ci;
+ rq->aux_req_cb = rq->comp_cb;
+ rq->comp_cb = esas2r_csmi_ioctl_tunnel_comp_cb;
+
+ if (!esas2r_build_sg_list(a, rq, sgc))
+ return false;
+
+ esas2r_start_request(a, rq);
+ return true;
+}
+
+static bool check_lun(struct scsi_lun lun)
+{
+ bool result;
+
+ result = ((lun.scsi_lun[7] == 0) &&
+ (lun.scsi_lun[6] == 0) &&
+ (lun.scsi_lun[5] == 0) &&
+ (lun.scsi_lun[4] == 0) &&
+ (lun.scsi_lun[3] == 0) &&
+ (lun.scsi_lun[2] == 0) &&
+/* Byte 1 is intentionally skipped */
+ (lun.scsi_lun[0] == 0));
+
+ return result;
+}
+
+static int csmi_ioctl_callback(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc, void *context)
+{
+ struct atto_csmi *ci = (struct atto_csmi *)context;
+ union atto_ioctl_csmi *ioctl_csmi =
+ (union atto_ioctl_csmi *)esas2r_buffered_ioctl;
+ u8 path = 0;
+ u8 tid = 0;
+ u8 lun = 0;
+ u32 sts = CSMI_STS_SUCCESS;
+ struct esas2r_target *t;
+ unsigned long flags;
+
+ if (ci->control_code == CSMI_CC_GET_DEV_ADDR) {
+ struct atto_csmi_get_dev_addr *gda = &ci->data.dev_addr;
+
+ path = gda->path_id;
+ tid = gda->target_id;
+ lun = gda->lun;
+ } else if (ci->control_code == CSMI_CC_TASK_MGT) {
+ struct atto_csmi_task_mgmt *tm = &ci->data.tsk_mgt;
+
+ path = tm->path_id;
+ tid = tm->target_id;
+ lun = tm->lun;
+ }
+
+ if (path > 0) {
+ rq->func_rsp.ioctl_rsp.csmi.csmi_status = cpu_to_le32(
+ CSMI_STS_INV_PARAM);
+ return false;
+ }
+
+ rq->target_id = tid;
+ rq->vrq->scsi.flags |= cpu_to_le32(lun);
+
+ switch (ci->control_code) {
+ case CSMI_CC_GET_DRVR_INFO:
+ {
+ struct atto_csmi_get_driver_info *gdi = &ioctl_csmi->drvr_info;
+
+ strcpy(gdi->description, esas2r_get_model_name(a));
+ gdi->csmi_major_rev = CSMI_MAJOR_REV;
+ gdi->csmi_minor_rev = CSMI_MINOR_REV;
+ break;
+ }
+
+ case CSMI_CC_GET_CNTLR_CFG:
+ {
+ struct atto_csmi_get_cntlr_cfg *gcc = &ioctl_csmi->cntlr_cfg;
+
+ gcc->base_io_addr = 0;
+ pci_read_config_dword(a->pcid, PCI_BASE_ADDRESS_2,
+ &gcc->base_memaddr_lo);
+ pci_read_config_dword(a->pcid, PCI_BASE_ADDRESS_3,
+ &gcc->base_memaddr_hi);
+ gcc->board_id = MAKEDWORD(a->pcid->subsystem_device,
+ a->pcid->subsystem_vendor);
+ gcc->slot_num = CSMI_SLOT_NUM_UNKNOWN;
+ gcc->cntlr_class = CSMI_CNTLR_CLASS_HBA;
+ gcc->io_bus_type = CSMI_BUS_TYPE_PCI;
+ gcc->pci_addr.bus_num = a->pcid->bus->number;
+ gcc->pci_addr.device_num = PCI_SLOT(a->pcid->devfn);
+ gcc->pci_addr.function_num = PCI_FUNC(a->pcid->devfn);
+
+ memset(gcc->serial_num, 0, sizeof(gcc->serial_num));
+
+ gcc->major_rev = LOBYTE(LOWORD(a->fw_version));
+ gcc->minor_rev = HIBYTE(LOWORD(a->fw_version));
+ gcc->build_rev = LOBYTE(HIWORD(a->fw_version));
+ gcc->release_rev = HIBYTE(HIWORD(a->fw_version));
+ gcc->bios_major_rev = HIBYTE(HIWORD(a->flash_ver));
+ gcc->bios_minor_rev = LOBYTE(HIWORD(a->flash_ver));
+ gcc->bios_build_rev = LOWORD(a->flash_ver);
+
+ if (test_bit(AF2_THUNDERLINK, &a->flags2))
+ gcc->cntlr_flags = CSMI_CNTLRF_SAS_HBA
+ | CSMI_CNTLRF_SATA_HBA;
+ else
+ gcc->cntlr_flags = CSMI_CNTLRF_SAS_RAID
+ | CSMI_CNTLRF_SATA_RAID;
+
+ gcc->rrom_major_rev = 0;
+ gcc->rrom_minor_rev = 0;
+ gcc->rrom_build_rev = 0;
+ gcc->rrom_release_rev = 0;
+ gcc->rrom_biosmajor_rev = 0;
+ gcc->rrom_biosminor_rev = 0;
+ gcc->rrom_biosbuild_rev = 0;
+ gcc->rrom_biosrelease_rev = 0;
+ break;
+ }
+
+ case CSMI_CC_GET_CNTLR_STS:
+ {
+ struct atto_csmi_get_cntlr_sts *gcs = &ioctl_csmi->cntlr_sts;
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ gcs->status = CSMI_CNTLR_STS_FAILED;
+ else
+ gcs->status = CSMI_CNTLR_STS_GOOD;
+
+ gcs->offline_reason = CSMI_OFFLINE_NO_REASON;
+ break;
+ }
+
+ case CSMI_CC_FW_DOWNLOAD:
+ case CSMI_CC_GET_RAID_INFO:
+ case CSMI_CC_GET_RAID_CFG:
+
+ sts = CSMI_STS_BAD_CTRL_CODE;
+ break;
+
+ case CSMI_CC_SMP_PASSTHRU:
+ case CSMI_CC_SSP_PASSTHRU:
+ case CSMI_CC_STP_PASSTHRU:
+ case CSMI_CC_GET_PHY_INFO:
+ case CSMI_CC_SET_PHY_INFO:
+ case CSMI_CC_GET_LINK_ERRORS:
+ case CSMI_CC_GET_SATA_SIG:
+ case CSMI_CC_GET_CONN_INFO:
+ case CSMI_CC_PHY_CTRL:
+
+ if (!csmi_ioctl_tunnel(a, ioctl_csmi, rq, sgc,
+ ci->control_code,
+ ESAS2R_TARG_ID_INV)) {
+ sts = CSMI_STS_FAILED;
+ break;
+ }
+
+ return true;
+
+ case CSMI_CC_GET_SCSI_ADDR:
+ {
+ struct atto_csmi_get_scsi_addr *gsa = &ioctl_csmi->scsi_addr;
+
+ struct scsi_lun lun;
+
+ memcpy(&lun, gsa->sas_lun, sizeof(struct scsi_lun));
+
+ if (!check_lun(lun)) {
+ sts = CSMI_STS_NO_SCSI_ADDR;
+ break;
+ }
+
+ /* make sure the device is present */
+ spin_lock_irqsave(&a->mem_lock, flags);
+ t = esas2r_targ_db_find_by_sas_addr(a, (u64 *)gsa->sas_addr);
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ if (t == NULL) {
+ sts = CSMI_STS_NO_SCSI_ADDR;
+ break;
+ }
+
+ gsa->host_index = 0xFF;
+ gsa->lun = gsa->sas_lun[1];
+ rq->target_id = esas2r_targ_get_id(t, a);
+ break;
+ }
+
+ case CSMI_CC_GET_DEV_ADDR:
+ {
+ struct atto_csmi_get_dev_addr *gda = &ioctl_csmi->dev_addr;
+
+ /* make sure the target is present */
+ t = a->targetdb + rq->target_id;
+
+ if (t >= a->targetdb_end
+ || t->target_state != TS_PRESENT
+ || t->sas_addr == 0) {
+ sts = CSMI_STS_NO_DEV_ADDR;
+ break;
+ }
+
+ /* fill in the result */
+ *(u64 *)gda->sas_addr = t->sas_addr;
+ memset(gda->sas_lun, 0, sizeof(gda->sas_lun));
+ gda->sas_lun[1] = (u8)le32_to_cpu(rq->vrq->scsi.flags);
+ break;
+ }
+
+ case CSMI_CC_TASK_MGT:
+
+ /* make sure the target is present */
+ t = a->targetdb + rq->target_id;
+
+ if (t >= a->targetdb_end
+ || t->target_state != TS_PRESENT
+ || !(t->flags & TF_PASS_THRU)) {
+ sts = CSMI_STS_NO_DEV_ADDR;
+ break;
+ }
+
+ if (!csmi_ioctl_tunnel(a, ioctl_csmi, rq, sgc,
+ ci->control_code,
+ t->phys_targ_id)) {
+ sts = CSMI_STS_FAILED;
+ break;
+ }
+
+ return true;
+
+ default:
+
+ sts = CSMI_STS_BAD_CTRL_CODE;
+ break;
+ }
+
+ rq->func_rsp.ioctl_rsp.csmi.csmi_status = cpu_to_le32(sts);
+
+ return false;
+}
+
+
+static void csmi_ioctl_done_callback(struct esas2r_adapter *a,
+ struct esas2r_request *rq, void *context)
+{
+ struct atto_csmi *ci = (struct atto_csmi *)context;
+ union atto_ioctl_csmi *ioctl_csmi =
+ (union atto_ioctl_csmi *)esas2r_buffered_ioctl;
+
+ switch (ci->control_code) {
+ case CSMI_CC_GET_DRVR_INFO:
+ {
+ struct atto_csmi_get_driver_info *gdi =
+ &ioctl_csmi->drvr_info;
+
+ strcpy(gdi->name, ESAS2R_VERSION_STR);
+
+ gdi->major_rev = ESAS2R_MAJOR_REV;
+ gdi->minor_rev = ESAS2R_MINOR_REV;
+ gdi->build_rev = 0;
+ gdi->release_rev = 0;
+ break;
+ }
+
+ case CSMI_CC_GET_SCSI_ADDR:
+ {
+ struct atto_csmi_get_scsi_addr *gsa = &ioctl_csmi->scsi_addr;
+
+ if (le32_to_cpu(rq->func_rsp.ioctl_rsp.csmi.csmi_status) ==
+ CSMI_STS_SUCCESS) {
+ gsa->target_id = rq->target_id;
+ gsa->path_id = 0;
+ }
+
+ break;
+ }
+ }
+
+ ci->status = le32_to_cpu(rq->func_rsp.ioctl_rsp.csmi.csmi_status);
+}
+
+
+static u8 handle_csmi_ioctl(struct esas2r_adapter *a, struct atto_csmi *ci)
+{
+ struct esas2r_buffered_ioctl bi;
+
+ memset(&bi, 0, sizeof(bi));
+
+ bi.a = a;
+ bi.ioctl = &ci->data;
+ bi.length = sizeof(union atto_ioctl_csmi);
+ bi.offset = 0;
+ bi.callback = csmi_ioctl_callback;
+ bi.context = ci;
+ bi.done_callback = csmi_ioctl_done_callback;
+ bi.done_context = ci;
+
+ return handle_buffered_ioctl(&bi);
+}
+
+/* ATTO HBA ioctl support */
+
+/* Tunnel an ATTO HBA IOCTL to the back end driver for processing. */
+static bool hba_ioctl_tunnel(struct esas2r_adapter *a,
+ struct atto_ioctl *hi,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc)
+{
+ esas2r_sgc_init(sgc, a, rq, rq->vrq->ioctl.sge);
+
+ esas2r_build_ioctl_req(a, rq, sgc->length, VDA_IOCTL_HBA);
+
+ if (!esas2r_build_sg_list(a, rq, sgc)) {
+ hi->status = ATTO_STS_OUT_OF_RSRC;
+
+ return false;
+ }
+
+ esas2r_start_request(a, rq);
+
+ return true;
+}
+
+static void scsi_passthru_comp_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct atto_ioctl *hi = (struct atto_ioctl *)rq->aux_req_cx;
+ struct atto_hba_scsi_pass_thru *spt = &hi->data.scsi_pass_thru;
+ u8 sts = ATTO_SPT_RS_FAILED;
+
+ spt->scsi_status = rq->func_rsp.scsi_rsp.scsi_stat;
+ spt->sense_length = rq->sense_len;
+ spt->residual_length =
+ le32_to_cpu(rq->func_rsp.scsi_rsp.residual_length);
+
+ switch (rq->req_stat) {
+ case RS_SUCCESS:
+ case RS_SCSI_ERROR:
+ sts = ATTO_SPT_RS_SUCCESS;
+ break;
+ case RS_UNDERRUN:
+ sts = ATTO_SPT_RS_UNDERRUN;
+ break;
+ case RS_OVERRUN:
+ sts = ATTO_SPT_RS_OVERRUN;
+ break;
+ case RS_SEL:
+ case RS_SEL2:
+ sts = ATTO_SPT_RS_NO_DEVICE;
+ break;
+ case RS_NO_LUN:
+ sts = ATTO_SPT_RS_NO_LUN;
+ break;
+ case RS_TIMEOUT:
+ sts = ATTO_SPT_RS_TIMEOUT;
+ break;
+ case RS_DEGRADED:
+ sts = ATTO_SPT_RS_DEGRADED;
+ break;
+ case RS_BUSY:
+ sts = ATTO_SPT_RS_BUSY;
+ break;
+ case RS_ABORTED:
+ sts = ATTO_SPT_RS_ABORTED;
+ break;
+ case RS_RESET:
+ sts = ATTO_SPT_RS_BUS_RESET;
+ break;
+ }
+
+ spt->req_status = sts;
+
+ /* Update the target ID to the next one present. */
+ spt->target_id =
+ esas2r_targ_db_find_next_present(a, (u16)spt->target_id);
+
+ /* Done, call the completion callback. */
+ (*rq->aux_req_cb)(a, rq);
+}
+
+static int hba_ioctl_callback(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc,
+ void *context)
+{
+ struct atto_ioctl *hi = (struct atto_ioctl *)esas2r_buffered_ioctl;
+
+ hi->status = ATTO_STS_SUCCESS;
+
+ switch (hi->function) {
+ case ATTO_FUNC_GET_ADAP_INFO:
+ {
+ u8 *class_code = (u8 *)&a->pcid->class;
+
+ struct atto_hba_get_adapter_info *gai =
+ &hi->data.get_adap_info;
+
+ if (hi->flags & HBAF_TUNNEL) {
+ hi->status = ATTO_STS_UNSUPPORTED;
+ break;
+ }
+
+ if (hi->version > ATTO_VER_GET_ADAP_INFO0) {
+ hi->status = ATTO_STS_INV_VERSION;
+ hi->version = ATTO_VER_GET_ADAP_INFO0;
+ break;
+ }
+
+ memset(gai, 0, sizeof(*gai));
+
+ gai->pci.vendor_id = a->pcid->vendor;
+ gai->pci.device_id = a->pcid->device;
+ gai->pci.ss_vendor_id = a->pcid->subsystem_vendor;
+ gai->pci.ss_device_id = a->pcid->subsystem_device;
+ gai->pci.class_code[0] = class_code[0];
+ gai->pci.class_code[1] = class_code[1];
+ gai->pci.class_code[2] = class_code[2];
+ gai->pci.rev_id = a->pcid->revision;
+ gai->pci.bus_num = a->pcid->bus->number;
+ gai->pci.dev_num = PCI_SLOT(a->pcid->devfn);
+ gai->pci.func_num = PCI_FUNC(a->pcid->devfn);
+
+ if (pci_is_pcie(a->pcid)) {
+ u16 stat;
+ u32 caps;
+
+ pcie_capability_read_word(a->pcid, PCI_EXP_LNKSTA,
+ &stat);
+ pcie_capability_read_dword(a->pcid, PCI_EXP_LNKCAP,
+ &caps);
+
+ gai->pci.link_speed_curr = FIELD_GET(PCI_EXP_LNKSTA_CLS, stat);
+ gai->pci.link_speed_max = FIELD_GET(PCI_EXP_LNKCAP_SLS, caps);
+ gai->pci.link_width_curr = FIELD_GET(PCI_EXP_LNKSTA_NLW, stat);
+ gai->pci.link_width_max = FIELD_GET(PCI_EXP_LNKCAP_MLW, caps);
+ }
+
+ gai->pci.msi_vector_cnt = 1;
+
+ if (a->pcid->msix_enabled)
+ gai->pci.interrupt_mode = ATTO_GAI_PCIIM_MSIX;
+ else if (a->pcid->msi_enabled)
+ gai->pci.interrupt_mode = ATTO_GAI_PCIIM_MSI;
+ else
+ gai->pci.interrupt_mode = ATTO_GAI_PCIIM_LEGACY;
+
+ gai->adap_type = ATTO_GAI_AT_ESASRAID2;
+
+ if (test_bit(AF2_THUNDERLINK, &a->flags2))
+ gai->adap_type = ATTO_GAI_AT_TLSASHBA;
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ gai->adap_flags |= ATTO_GAI_AF_DEGRADED;
+
+ gai->adap_flags |= ATTO_GAI_AF_SPT_SUPP |
+ ATTO_GAI_AF_DEVADDR_SUPP;
+
+ if (a->pcid->subsystem_device == ATTO_ESAS_R60F
+ || a->pcid->subsystem_device == ATTO_ESAS_R608
+ || a->pcid->subsystem_device == ATTO_ESAS_R644
+ || a->pcid->subsystem_device == ATTO_TSSC_3808E)
+ gai->adap_flags |= ATTO_GAI_AF_VIRT_SES;
+
+ gai->num_ports = ESAS2R_NUM_PHYS;
+ gai->num_phys = ESAS2R_NUM_PHYS;
+
+ strcpy(gai->firmware_rev, a->fw_rev);
+ strcpy(gai->flash_rev, a->flash_rev);
+ strcpy(gai->model_name_short, esas2r_get_model_name_short(a));
+ strcpy(gai->model_name, esas2r_get_model_name(a));
+
+ gai->num_targets = ESAS2R_MAX_TARGETS;
+
+ gai->num_busses = 1;
+ gai->num_targsper_bus = gai->num_targets;
+ gai->num_lunsper_targ = 256;
+
+ if (a->pcid->subsystem_device == ATTO_ESAS_R6F0
+ || a->pcid->subsystem_device == ATTO_ESAS_R60F)
+ gai->num_connectors = 4;
+ else
+ gai->num_connectors = 2;
+
+ gai->adap_flags2 |= ATTO_GAI_AF2_ADAP_CTRL_SUPP;
+
+ gai->num_targets_backend = a->num_targets_backend;
+
+ gai->tunnel_flags = a->ioctl_tunnel
+ & (ATTO_GAI_TF_MEM_RW
+ | ATTO_GAI_TF_TRACE
+ | ATTO_GAI_TF_SCSI_PASS_THRU
+ | ATTO_GAI_TF_GET_DEV_ADDR
+ | ATTO_GAI_TF_PHY_CTRL
+ | ATTO_GAI_TF_CONN_CTRL
+ | ATTO_GAI_TF_GET_DEV_INFO);
+ break;
+ }
+
+ case ATTO_FUNC_GET_ADAP_ADDR:
+ {
+ struct atto_hba_get_adapter_address *gaa =
+ &hi->data.get_adap_addr;
+
+ if (hi->flags & HBAF_TUNNEL) {
+ hi->status = ATTO_STS_UNSUPPORTED;
+ break;
+ }
+
+ if (hi->version > ATTO_VER_GET_ADAP_ADDR0) {
+ hi->status = ATTO_STS_INV_VERSION;
+ hi->version = ATTO_VER_GET_ADAP_ADDR0;
+ } else if (gaa->addr_type == ATTO_GAA_AT_PORT
+ || gaa->addr_type == ATTO_GAA_AT_NODE) {
+ if (gaa->addr_type == ATTO_GAA_AT_PORT
+ && gaa->port_id >= ESAS2R_NUM_PHYS) {
+ hi->status = ATTO_STS_NOT_APPL;
+ } else {
+ memcpy((u64 *)gaa->address,
+ &a->nvram->sas_addr[0], sizeof(u64));
+ gaa->addr_len = sizeof(u64);
+ }
+ } else {
+ hi->status = ATTO_STS_INV_PARAM;
+ }
+
+ break;
+ }
+
+ case ATTO_FUNC_MEM_RW:
+ {
+ if (hi->flags & HBAF_TUNNEL) {
+ if (hba_ioctl_tunnel(a, hi, rq, sgc))
+ return true;
+
+ break;
+ }
+
+ hi->status = ATTO_STS_UNSUPPORTED;
+
+ break;
+ }
+
+ case ATTO_FUNC_TRACE:
+ {
+ struct atto_hba_trace *trc = &hi->data.trace;
+
+ if (hi->flags & HBAF_TUNNEL) {
+ if (hba_ioctl_tunnel(a, hi, rq, sgc))
+ return true;
+
+ break;
+ }
+
+ if (hi->version > ATTO_VER_TRACE1) {
+ hi->status = ATTO_STS_INV_VERSION;
+ hi->version = ATTO_VER_TRACE1;
+ break;
+ }
+
+ if (trc->trace_type == ATTO_TRC_TT_FWCOREDUMP
+ && hi->version >= ATTO_VER_TRACE1) {
+ if (trc->trace_func == ATTO_TRC_TF_UPLOAD) {
+ u32 len = hi->data_length;
+ u32 offset = trc->current_offset;
+ u32 total_len = ESAS2R_FWCOREDUMP_SZ;
+
+ /* Size is zero if a core dump isn't present */
+ if (!test_bit(AF2_COREDUMP_SAVED, &a->flags2))
+ total_len = 0;
+
+ if (len > total_len)
+ len = total_len;
+
+ if (offset >= total_len
+ || offset + len > total_len
+ || len == 0) {
+ hi->status = ATTO_STS_INV_PARAM;
+ break;
+ }
+
+ memcpy(trc->contents,
+ a->fw_coredump_buff + offset,
+ len);
+ hi->data_length = len;
+ } else if (trc->trace_func == ATTO_TRC_TF_RESET) {
+ memset(a->fw_coredump_buff, 0,
+ ESAS2R_FWCOREDUMP_SZ);
+
+ clear_bit(AF2_COREDUMP_SAVED, &a->flags2);
+ } else if (trc->trace_func != ATTO_TRC_TF_GET_INFO) {
+ hi->status = ATTO_STS_UNSUPPORTED;
+ break;
+ }
+
+ /* Always return all the info we can. */
+ trc->trace_mask = 0;
+ trc->current_offset = 0;
+ trc->total_length = ESAS2R_FWCOREDUMP_SZ;
+
+ /* Return zero length buffer if core dump not present */
+ if (!test_bit(AF2_COREDUMP_SAVED, &a->flags2))
+ trc->total_length = 0;
+ } else {
+ hi->status = ATTO_STS_UNSUPPORTED;
+ }
+
+ break;
+ }
+
+ case ATTO_FUNC_SCSI_PASS_THRU:
+ {
+ struct atto_hba_scsi_pass_thru *spt = &hi->data.scsi_pass_thru;
+ struct scsi_lun lun;
+
+ memcpy(&lun, spt->lun, sizeof(struct scsi_lun));
+
+ if (hi->flags & HBAF_TUNNEL) {
+ if (hba_ioctl_tunnel(a, hi, rq, sgc))
+ return true;
+
+ break;
+ }
+
+ if (hi->version > ATTO_VER_SCSI_PASS_THRU0) {
+ hi->status = ATTO_STS_INV_VERSION;
+ hi->version = ATTO_VER_SCSI_PASS_THRU0;
+ break;
+ }
+
+ if (spt->target_id >= ESAS2R_MAX_TARGETS || !check_lun(lun)) {
+ hi->status = ATTO_STS_INV_PARAM;
+ break;
+ }
+
+ esas2r_sgc_init(sgc, a, rq, NULL);
+
+ sgc->length = hi->data_length;
+ sgc->cur_offset += offsetof(struct atto_ioctl, data.byte)
+ + sizeof(struct atto_hba_scsi_pass_thru);
+
+ /* Finish request initialization */
+ rq->target_id = (u16)spt->target_id;
+ rq->vrq->scsi.flags |= cpu_to_le32(spt->lun[1]);
+ memcpy(rq->vrq->scsi.cdb, spt->cdb, 16);
+ rq->vrq->scsi.length = cpu_to_le32(hi->data_length);
+ rq->sense_len = spt->sense_length;
+ rq->sense_buf = (u8 *)spt->sense_data;
+ /* NOTE: we ignore spt->timeout */
+
+ /*
+ * always usurp the completion callback since the interrupt
+ * callback mechanism may be used.
+ */
+
+ rq->aux_req_cx = hi;
+ rq->aux_req_cb = rq->comp_cb;
+ rq->comp_cb = scsi_passthru_comp_cb;
+
+ if (spt->flags & ATTO_SPTF_DATA_IN) {
+ rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_RDD);
+ } else if (spt->flags & ATTO_SPTF_DATA_OUT) {
+ rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_WRD);
+ } else {
+ if (sgc->length) {
+ hi->status = ATTO_STS_INV_PARAM;
+ break;
+ }
+ }
+
+ if (spt->flags & ATTO_SPTF_ORDERED_Q)
+ rq->vrq->scsi.flags |=
+ cpu_to_le32(FCP_CMND_TA_ORDRD_Q);
+ else if (spt->flags & ATTO_SPTF_HEAD_OF_Q)
+ rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_TA_HEAD_Q);
+
+
+ if (!esas2r_build_sg_list(a, rq, sgc)) {
+ hi->status = ATTO_STS_OUT_OF_RSRC;
+ break;
+ }
+
+ esas2r_start_request(a, rq);
+
+ return true;
+ }
+
+ case ATTO_FUNC_GET_DEV_ADDR:
+ {
+ struct atto_hba_get_device_address *gda =
+ &hi->data.get_dev_addr;
+ struct esas2r_target *t;
+
+ if (hi->flags & HBAF_TUNNEL) {
+ if (hba_ioctl_tunnel(a, hi, rq, sgc))
+ return true;
+
+ break;
+ }
+
+ if (hi->version > ATTO_VER_GET_DEV_ADDR0) {
+ hi->status = ATTO_STS_INV_VERSION;
+ hi->version = ATTO_VER_GET_DEV_ADDR0;
+ break;
+ }
+
+ if (gda->target_id >= ESAS2R_MAX_TARGETS) {
+ hi->status = ATTO_STS_INV_PARAM;
+ break;
+ }
+
+ t = a->targetdb + (u16)gda->target_id;
+
+ if (t->target_state != TS_PRESENT) {
+ hi->status = ATTO_STS_FAILED;
+ } else if (gda->addr_type == ATTO_GDA_AT_PORT) {
+ if (t->sas_addr == 0) {
+ hi->status = ATTO_STS_UNSUPPORTED;
+ } else {
+ *(u64 *)gda->address = t->sas_addr;
+
+ gda->addr_len = sizeof(u64);
+ }
+ } else if (gda->addr_type == ATTO_GDA_AT_NODE) {
+ hi->status = ATTO_STS_NOT_APPL;
+ } else {
+ hi->status = ATTO_STS_INV_PARAM;
+ }
+
+ /* update the target ID to the next one present. */
+
+ gda->target_id =
+ esas2r_targ_db_find_next_present(a,
+ (u16)gda->target_id);
+ break;
+ }
+
+ case ATTO_FUNC_PHY_CTRL:
+ case ATTO_FUNC_CONN_CTRL:
+ {
+ if (hba_ioctl_tunnel(a, hi, rq, sgc))
+ return true;
+
+ break;
+ }
+
+ case ATTO_FUNC_ADAP_CTRL:
+ {
+ struct atto_hba_adap_ctrl *ac = &hi->data.adap_ctrl;
+
+ if (hi->flags & HBAF_TUNNEL) {
+ hi->status = ATTO_STS_UNSUPPORTED;
+ break;
+ }
+
+ if (hi->version > ATTO_VER_ADAP_CTRL0) {
+ hi->status = ATTO_STS_INV_VERSION;
+ hi->version = ATTO_VER_ADAP_CTRL0;
+ break;
+ }
+
+ if (ac->adap_func == ATTO_AC_AF_HARD_RST) {
+ esas2r_reset_adapter(a);
+ } else if (ac->adap_func != ATTO_AC_AF_GET_STATE) {
+ hi->status = ATTO_STS_UNSUPPORTED;
+ break;
+ }
+
+ if (test_bit(AF_CHPRST_NEEDED, &a->flags))
+ ac->adap_state = ATTO_AC_AS_RST_SCHED;
+ else if (test_bit(AF_CHPRST_PENDING, &a->flags))
+ ac->adap_state = ATTO_AC_AS_RST_IN_PROG;
+ else if (test_bit(AF_DISC_PENDING, &a->flags))
+ ac->adap_state = ATTO_AC_AS_RST_DISC;
+ else if (test_bit(AF_DISABLED, &a->flags))
+ ac->adap_state = ATTO_AC_AS_DISABLED;
+ else if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ ac->adap_state = ATTO_AC_AS_DEGRADED;
+ else
+ ac->adap_state = ATTO_AC_AS_OK;
+
+ break;
+ }
+
+ case ATTO_FUNC_GET_DEV_INFO:
+ {
+ struct atto_hba_get_device_info *gdi = &hi->data.get_dev_info;
+ struct esas2r_target *t;
+
+ if (hi->flags & HBAF_TUNNEL) {
+ if (hba_ioctl_tunnel(a, hi, rq, sgc))
+ return true;
+
+ break;
+ }
+
+ if (hi->version > ATTO_VER_GET_DEV_INFO0) {
+ hi->status = ATTO_STS_INV_VERSION;
+ hi->version = ATTO_VER_GET_DEV_INFO0;
+ break;
+ }
+
+ if (gdi->target_id >= ESAS2R_MAX_TARGETS) {
+ hi->status = ATTO_STS_INV_PARAM;
+ break;
+ }
+
+ t = a->targetdb + (u16)gdi->target_id;
+
+ /* update the target ID to the next one present. */
+
+ gdi->target_id =
+ esas2r_targ_db_find_next_present(a,
+ (u16)gdi->target_id);
+
+ if (t->target_state != TS_PRESENT) {
+ hi->status = ATTO_STS_FAILED;
+ break;
+ }
+
+ hi->status = ATTO_STS_UNSUPPORTED;
+ break;
+ }
+
+ default:
+
+ hi->status = ATTO_STS_INV_FUNC;
+ break;
+ }
+
+ return false;
+}
+
+static void hba_ioctl_done_callback(struct esas2r_adapter *a,
+ struct esas2r_request *rq, void *context)
+{
+ struct atto_ioctl *ioctl_hba =
+ (struct atto_ioctl *)esas2r_buffered_ioctl;
+
+ esas2r_debug("hba_ioctl_done_callback %d", a->index);
+
+ if (ioctl_hba->function == ATTO_FUNC_GET_ADAP_INFO) {
+ struct atto_hba_get_adapter_info *gai =
+ &ioctl_hba->data.get_adap_info;
+
+ esas2r_debug("ATTO_FUNC_GET_ADAP_INFO");
+
+ gai->drvr_rev_major = ESAS2R_MAJOR_REV;
+ gai->drvr_rev_minor = ESAS2R_MINOR_REV;
+
+ strcpy(gai->drvr_rev_ascii, ESAS2R_VERSION_STR);
+ strcpy(gai->drvr_name, ESAS2R_DRVR_NAME);
+
+ gai->num_busses = 1;
+ gai->num_targsper_bus = ESAS2R_MAX_ID + 1;
+ gai->num_lunsper_targ = 1;
+ }
+}
+
+u8 handle_hba_ioctl(struct esas2r_adapter *a,
+ struct atto_ioctl *ioctl_hba)
+{
+ struct esas2r_buffered_ioctl bi;
+
+ memset(&bi, 0, sizeof(bi));
+
+ bi.a = a;
+ bi.ioctl = ioctl_hba;
+ bi.length = sizeof(struct atto_ioctl) + ioctl_hba->data_length;
+ bi.callback = hba_ioctl_callback;
+ bi.context = NULL;
+ bi.done_callback = hba_ioctl_done_callback;
+ bi.done_context = NULL;
+ bi.offset = 0;
+
+ return handle_buffered_ioctl(&bi);
+}
+
+
+int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
+ struct esas2r_sas_nvram *data)
+{
+ int result = 0;
+
+ a->nvram_command_done = 0;
+ rq->comp_cb = complete_nvr_req;
+
+ if (esas2r_nvram_write(a, rq, data)) {
+ /* now wait around for it to complete. */
+ while (!a->nvram_command_done)
+ wait_event_interruptible(a->nvram_waiter,
+ a->nvram_command_done);
+ ;
+
+ /* done, check the status. */
+ if (rq->req_stat == RS_SUCCESS)
+ result = 1;
+ }
+ return result;
+}
+
+
+/* This function only cares about ATTO-specific ioctls (atto_express_ioctl) */
+int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
+{
+ struct atto_express_ioctl *ioctl = NULL;
+ struct esas2r_adapter *a;
+ struct esas2r_request *rq;
+ u16 code;
+ int err;
+
+ esas2r_log(ESAS2R_LOG_DEBG, "ioctl (%p, %x, %p)", hostdata, cmd, arg);
+
+ if ((arg == NULL)
+ || (cmd < EXPRESS_IOCTL_MIN)
+ || (cmd > EXPRESS_IOCTL_MAX))
+ return -ENOTSUPP;
+
+ ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl));
+ if (IS_ERR(ioctl)) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "ioctl_handler access_ok failed for cmd %u, address %p",
+ cmd, arg);
+ return PTR_ERR(ioctl);
+ }
+
+ /* verify the signature */
+
+ if (memcmp(ioctl->header.signature,
+ EXPRESS_IOCTL_SIGNATURE,
+ EXPRESS_IOCTL_SIGNATURE_SIZE) != 0) {
+ esas2r_log(ESAS2R_LOG_WARN, "invalid signature");
+ kfree(ioctl);
+
+ return -ENOTSUPP;
+ }
+
+ /* assume success */
+
+ ioctl->header.return_code = IOCTL_SUCCESS;
+ err = 0;
+
+ /*
+ * handle EXPRESS_IOCTL_GET_CHANNELS
+ * without paying attention to channel
+ */
+
+ if (cmd == EXPRESS_IOCTL_GET_CHANNELS) {
+ int i = 0, k = 0;
+
+ ioctl->data.chanlist.num_channels = 0;
+
+ while (i < MAX_ADAPTERS) {
+ if (esas2r_adapters[i]) {
+ ioctl->data.chanlist.num_channels++;
+ ioctl->data.chanlist.channel[k] = i;
+ k++;
+ }
+ i++;
+ }
+
+ goto ioctl_done;
+ }
+
+ /* get the channel */
+
+ if (ioctl->header.channel == 0xFF) {
+ a = (struct esas2r_adapter *)hostdata;
+ } else {
+ if (ioctl->header.channel >= MAX_ADAPTERS ||
+ esas2r_adapters[ioctl->header.channel] == NULL) {
+ ioctl->header.return_code = IOCTL_BAD_CHANNEL;
+ esas2r_log(ESAS2R_LOG_WARN, "bad channel value");
+ kfree(ioctl);
+
+ return -ENOTSUPP;
+ }
+ a = esas2r_adapters[ioctl->header.channel];
+ }
+
+ switch (cmd) {
+ case EXPRESS_IOCTL_RW_FIRMWARE:
+
+ if (ioctl->data.fwrw.img_type == FW_IMG_FM_API) {
+ err = esas2r_write_fw(a,
+ (char *)ioctl->data.fwrw.image,
+ 0,
+ sizeof(struct
+ atto_express_ioctl));
+
+ if (err >= 0) {
+ err = esas2r_read_fw(a,
+ (char *)ioctl->data.fwrw.
+ image,
+ 0,
+ sizeof(struct
+ atto_express_ioctl));
+ }
+ } else if (ioctl->data.fwrw.img_type == FW_IMG_FS_API) {
+ err = esas2r_write_fs(a,
+ (char *)ioctl->data.fwrw.image,
+ 0,
+ sizeof(struct
+ atto_express_ioctl));
+
+ if (err >= 0) {
+ err = esas2r_read_fs(a,
+ (char *)ioctl->data.fwrw.
+ image,
+ 0,
+ sizeof(struct
+ atto_express_ioctl));
+ }
+ } else {
+ ioctl->header.return_code = IOCTL_BAD_FLASH_IMGTYPE;
+ }
+
+ break;
+
+ case EXPRESS_IOCTL_READ_PARAMS:
+
+ memcpy(ioctl->data.prw.data_buffer, a->nvram,
+ sizeof(struct esas2r_sas_nvram));
+ ioctl->data.prw.code = 1;
+ break;
+
+ case EXPRESS_IOCTL_WRITE_PARAMS:
+
+ rq = esas2r_alloc_request(a);
+ if (rq == NULL) {
+ kfree(ioctl);
+ esas2r_log(ESAS2R_LOG_WARN,
+ "could not allocate an internal request");
+ return -ENOMEM;
+ }
+
+ code = esas2r_write_params(a, rq,
+ (struct esas2r_sas_nvram *)ioctl->data.prw.data_buffer);
+ ioctl->data.prw.code = code;
+
+ esas2r_free_request(a, rq);
+
+ break;
+
+ case EXPRESS_IOCTL_DEFAULT_PARAMS:
+
+ esas2r_nvram_get_defaults(a,
+ (struct esas2r_sas_nvram *)ioctl->data.prw.data_buffer);
+ ioctl->data.prw.code = 1;
+ break;
+
+ case EXPRESS_IOCTL_CHAN_INFO:
+
+ ioctl->data.chaninfo.major_rev = ESAS2R_MAJOR_REV;
+ ioctl->data.chaninfo.minor_rev = ESAS2R_MINOR_REV;
+ ioctl->data.chaninfo.IRQ = a->pcid->irq;
+ ioctl->data.chaninfo.device_id = a->pcid->device;
+ ioctl->data.chaninfo.vendor_id = a->pcid->vendor;
+ ioctl->data.chaninfo.ven_dev_id = a->pcid->subsystem_device;
+ ioctl->data.chaninfo.revision_id = a->pcid->revision;
+ ioctl->data.chaninfo.pci_bus = a->pcid->bus->number;
+ ioctl->data.chaninfo.pci_dev_func = a->pcid->devfn;
+ ioctl->data.chaninfo.core_rev = 0;
+ ioctl->data.chaninfo.host_no = a->host->host_no;
+ ioctl->data.chaninfo.hbaapi_rev = 0;
+ break;
+
+ case EXPRESS_IOCTL_SMP:
+ ioctl->header.return_code = handle_smp_ioctl(a,
+ &ioctl->data.
+ ioctl_smp);
+ break;
+
+ case EXPRESS_CSMI:
+ ioctl->header.return_code =
+ handle_csmi_ioctl(a, &ioctl->data.csmi);
+ break;
+
+ case EXPRESS_IOCTL_HBA:
+ ioctl->header.return_code = handle_hba_ioctl(a,
+ &ioctl->data.
+ ioctl_hba);
+ break;
+
+ case EXPRESS_IOCTL_VDA:
+ err = esas2r_write_vda(a,
+ (char *)&ioctl->data.ioctl_vda,
+ 0,
+ sizeof(struct atto_ioctl_vda) +
+ ioctl->data.ioctl_vda.data_length);
+
+ if (err >= 0) {
+ err = esas2r_read_vda(a,
+ (char *)&ioctl->data.ioctl_vda,
+ 0,
+ sizeof(struct atto_ioctl_vda) +
+ ioctl->data.ioctl_vda.data_length);
+ }
+
+
+
+
+ break;
+
+ case EXPRESS_IOCTL_GET_MOD_INFO:
+
+ ioctl->data.modinfo.adapter = a;
+ ioctl->data.modinfo.pci_dev = a->pcid;
+ ioctl->data.modinfo.scsi_host = a->host;
+ ioctl->data.modinfo.host_no = a->host->host_no;
+
+ break;
+
+ default:
+ esas2r_debug("esas2r_ioctl invalid cmd %p!", cmd);
+ ioctl->header.return_code = IOCTL_ERR_INVCMD;
+ }
+
+ioctl_done:
+
+ if (err < 0) {
+ esas2r_log(ESAS2R_LOG_WARN, "err %d on ioctl cmd %u", err,
+ cmd);
+
+ switch (err) {
+ case -ENOMEM:
+ case -EBUSY:
+ ioctl->header.return_code = IOCTL_OUT_OF_RESOURCES;
+ break;
+
+ case -ENOSYS:
+ case -EINVAL:
+ ioctl->header.return_code = IOCTL_INVALID_PARAM;
+ break;
+
+ default:
+ ioctl->header.return_code = IOCTL_GENERAL_ERROR;
+ break;
+ }
+
+ }
+
+ /* Always copy the buffer back, if only to pick up the status */
+ err = copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl));
+ if (err != 0) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "ioctl_handler copy_to_user didn't copy everything (err %d, cmd %u)",
+ err, cmd);
+ kfree(ioctl);
+
+ return -EFAULT;
+ }
+
+ kfree(ioctl);
+
+ return 0;
+}
+
+int esas2r_ioctl(struct scsi_device *sd, unsigned int cmd, void __user *arg)
+{
+ return esas2r_ioctl_handler(sd->host->hostdata, cmd, arg);
+}
+
+static void free_fw_buffers(struct esas2r_adapter *a)
+{
+ if (a->firmware.data) {
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)a->firmware.orig_len,
+ a->firmware.data,
+ (dma_addr_t)a->firmware.phys);
+
+ a->firmware.data = NULL;
+ }
+}
+
+static int allocate_fw_buffers(struct esas2r_adapter *a, u32 length)
+{
+ free_fw_buffers(a);
+
+ a->firmware.orig_len = length;
+
+ a->firmware.data = dma_alloc_coherent(&a->pcid->dev,
+ (size_t)length,
+ (dma_addr_t *)&a->firmware.phys,
+ GFP_KERNEL);
+
+ if (!a->firmware.data) {
+ esas2r_debug("buffer alloc failed!");
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Handle a call to read firmware. */
+int esas2r_read_fw(struct esas2r_adapter *a, char *buf, long off, int count)
+{
+ esas2r_trace_enter();
+ /* if the cached header is a status, simply copy it over and return. */
+ if (a->firmware.state == FW_STATUS_ST) {
+ int size = min_t(int, count, sizeof(a->firmware.header));
+ esas2r_trace_exit();
+ memcpy(buf, &a->firmware.header, size);
+ esas2r_debug("esas2r_read_fw: STATUS size %d", size);
+ return size;
+ }
+
+ /*
+ * if the cached header is a command, do it if at
+ * offset 0, otherwise copy the pieces.
+ */
+
+ if (a->firmware.state == FW_COMMAND_ST) {
+ u32 length = a->firmware.header.length;
+ esas2r_trace_exit();
+
+ esas2r_debug("esas2r_read_fw: COMMAND length %d off %d",
+ length,
+ off);
+
+ if (off == 0) {
+ if (a->firmware.header.action == FI_ACT_UP) {
+ if (!allocate_fw_buffers(a, length))
+ return -ENOMEM;
+
+
+ /* copy header over */
+
+ memcpy(a->firmware.data,
+ &a->firmware.header,
+ sizeof(a->firmware.header));
+
+ do_fm_api(a,
+ (struct esas2r_flash_img *)a->firmware.data);
+ } else if (a->firmware.header.action == FI_ACT_UPSZ) {
+ int size =
+ min((int)count,
+ (int)sizeof(a->firmware.header));
+ do_fm_api(a, &a->firmware.header);
+ memcpy(buf, &a->firmware.header, size);
+ esas2r_debug("FI_ACT_UPSZ size %d", size);
+ return size;
+ } else {
+ esas2r_debug("invalid action %d",
+ a->firmware.header.action);
+ return -ENOSYS;
+ }
+ }
+
+ if (count + off > length)
+ count = length - off;
+
+ if (count < 0)
+ return 0;
+
+ if (!a->firmware.data) {
+ esas2r_debug(
+ "read: nonzero offset but no buffer available!");
+ return -ENOMEM;
+ }
+
+ esas2r_debug("esas2r_read_fw: off %d count %d length %d ", off,
+ count,
+ length);
+
+ memcpy(buf, &a->firmware.data[off], count);
+
+ /* when done, release the buffer */
+
+ if (length <= off + count) {
+ esas2r_debug("esas2r_read_fw: freeing buffer!");
+
+ free_fw_buffers(a);
+ }
+
+ return count;
+ }
+
+ esas2r_trace_exit();
+ esas2r_debug("esas2r_read_fw: invalid firmware state %d",
+ a->firmware.state);
+
+ return -EINVAL;
+}
+
+/* Handle a call to write firmware. */
+int esas2r_write_fw(struct esas2r_adapter *a, const char *buf, long off,
+ int count)
+{
+ u32 length;
+
+ if (off == 0) {
+ struct esas2r_flash_img *header =
+ (struct esas2r_flash_img *)buf;
+
+ /* assume version 0 flash image */
+
+ int min_size = sizeof(struct esas2r_flash_img_v0);
+
+ a->firmware.state = FW_INVALID_ST;
+
+ /* validate the version field first */
+
+ if (count < 4
+ || header->fi_version > FI_VERSION_1) {
+ esas2r_debug(
+ "esas2r_write_fw: short header or invalid version");
+ return -EINVAL;
+ }
+
+ /* See if its a version 1 flash image */
+
+ if (header->fi_version == FI_VERSION_1)
+ min_size = sizeof(struct esas2r_flash_img);
+
+ /* If this is the start, the header must be full and valid. */
+ if (count < min_size) {
+ esas2r_debug("esas2r_write_fw: short header, aborting");
+ return -EINVAL;
+ }
+
+ /* Make sure the size is reasonable. */
+ length = header->length;
+
+ if (length > 1024 * 1024) {
+ esas2r_debug(
+ "esas2r_write_fw: hosed, length %d fi_version %d",
+ length, header->fi_version);
+ return -EINVAL;
+ }
+
+ /*
+ * If this is a write command, allocate memory because
+ * we have to cache everything. otherwise, just cache
+ * the header, because the read op will do the command.
+ */
+
+ if (header->action == FI_ACT_DOWN) {
+ if (!allocate_fw_buffers(a, length))
+ return -ENOMEM;
+
+ /*
+ * Store the command, so there is context on subsequent
+ * calls.
+ */
+ memcpy(&a->firmware.header,
+ buf,
+ sizeof(*header));
+ } else if (header->action == FI_ACT_UP
+ || header->action == FI_ACT_UPSZ) {
+ /* Save the command, result will be picked up on read */
+ memcpy(&a->firmware.header,
+ buf,
+ sizeof(*header));
+
+ a->firmware.state = FW_COMMAND_ST;
+
+ esas2r_debug(
+ "esas2r_write_fw: COMMAND, count %d, action %d ",
+ count, header->action);
+
+ /*
+ * Pretend we took the whole buffer,
+ * so we don't get bothered again.
+ */
+
+ return count;
+ } else {
+ esas2r_debug("esas2r_write_fw: invalid action %d ",
+ a->firmware.header.action);
+ return -ENOSYS;
+ }
+ } else {
+ length = a->firmware.header.length;
+ }
+
+ /*
+ * We only get here on a download command, regardless of offset.
+ * the chunks written by the system need to be cached, and when
+ * the final one arrives, issue the fmapi command.
+ */
+
+ if (off + count > length)
+ count = length - off;
+
+ if (count > 0) {
+ esas2r_debug("esas2r_write_fw: off %d count %d length %d", off,
+ count,
+ length);
+
+ /*
+ * On a full upload, the system tries sending the whole buffer.
+ * there's nothing to do with it, so just drop it here, before
+ * trying to copy over into unallocated memory!
+ */
+ if (a->firmware.header.action == FI_ACT_UP)
+ return count;
+
+ if (!a->firmware.data) {
+ esas2r_debug(
+ "write: nonzero offset but no buffer available!");
+ return -ENOMEM;
+ }
+
+ memcpy(&a->firmware.data[off], buf, count);
+
+ if (length == off + count) {
+ do_fm_api(a,
+ (struct esas2r_flash_img *)a->firmware.data);
+
+ /*
+ * Now copy the header result to be picked up by the
+ * next read
+ */
+ memcpy(&a->firmware.header,
+ a->firmware.data,
+ sizeof(a->firmware.header));
+
+ a->firmware.state = FW_STATUS_ST;
+
+ esas2r_debug("write completed");
+
+ /*
+ * Since the system has the data buffered, the only way
+ * this can leak is if a root user writes a program
+ * that writes a shorter buffer than it claims, and the
+ * copyin fails.
+ */
+ free_fw_buffers(a);
+ }
+ }
+
+ return count;
+}
+
+/* Callback for the completion of a VDA request. */
+static void vda_complete_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ a->vda_command_done = 1;
+ wake_up_interruptible(&a->vda_waiter);
+}
+
+/* Scatter/gather callback for VDA requests */
+static u32 get_physaddr_vda(struct esas2r_sg_context *sgc, u64 *addr)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
+ int offset = (u8 *)sgc->cur_offset - (u8 *)a->vda_buffer;
+
+ (*addr) = a->ppvda_buffer + offset;
+ return VDA_MAX_BUFFER_SIZE - offset;
+}
+
+/* Handle a call to read a VDA command. */
+int esas2r_read_vda(struct esas2r_adapter *a, char *buf, long off, int count)
+{
+ if (!a->vda_buffer)
+ return -ENOMEM;
+
+ if (off == 0) {
+ struct esas2r_request *rq;
+ struct atto_ioctl_vda *vi =
+ (struct atto_ioctl_vda *)a->vda_buffer;
+ struct esas2r_sg_context sgc;
+ bool wait_for_completion;
+
+ /*
+ * Presumeably, someone has already written to the vda_buffer,
+ * and now they are reading the node the response, so now we
+ * will actually issue the request to the chip and reply.
+ */
+
+ /* allocate a request */
+ rq = esas2r_alloc_request(a);
+ if (rq == NULL) {
+ esas2r_debug("esas2r_read_vda: out of requests");
+ return -EBUSY;
+ }
+
+ rq->comp_cb = vda_complete_req;
+
+ sgc.first_req = rq;
+ sgc.adapter = a;
+ sgc.cur_offset = a->vda_buffer + VDA_BUFFER_HEADER_SZ;
+ sgc.get_phys_addr = (PGETPHYSADDR)get_physaddr_vda;
+
+ a->vda_command_done = 0;
+
+ wait_for_completion =
+ esas2r_process_vda_ioctl(a, vi, rq, &sgc);
+
+ if (wait_for_completion) {
+ /* now wait around for it to complete. */
+
+ while (!a->vda_command_done)
+ wait_event_interruptible(a->vda_waiter,
+ a->vda_command_done);
+ }
+
+ esas2r_free_request(a, (struct esas2r_request *)rq);
+ }
+
+ if (off > VDA_MAX_BUFFER_SIZE)
+ return 0;
+
+ if (count + off > VDA_MAX_BUFFER_SIZE)
+ count = VDA_MAX_BUFFER_SIZE - off;
+
+ if (count < 0)
+ return 0;
+
+ memcpy(buf, a->vda_buffer + off, count);
+
+ return count;
+}
+
+/* Handle a call to write a VDA command. */
+int esas2r_write_vda(struct esas2r_adapter *a, const char *buf, long off,
+ int count)
+{
+ /*
+ * allocate memory for it, if not already done. once allocated,
+ * we will keep it around until the driver is unloaded.
+ */
+
+ if (!a->vda_buffer) {
+ dma_addr_t dma_addr;
+ a->vda_buffer = dma_alloc_coherent(&a->pcid->dev,
+ (size_t)
+ VDA_MAX_BUFFER_SIZE,
+ &dma_addr,
+ GFP_KERNEL);
+
+ a->ppvda_buffer = dma_addr;
+ }
+
+ if (!a->vda_buffer)
+ return -ENOMEM;
+
+ if (off > VDA_MAX_BUFFER_SIZE)
+ return 0;
+
+ if (count + off > VDA_MAX_BUFFER_SIZE)
+ count = VDA_MAX_BUFFER_SIZE - off;
+
+ if (count < 1)
+ return 0;
+
+ memcpy(a->vda_buffer + off, buf, count);
+
+ return count;
+}
+
+/* Callback for the completion of an FS_API request.*/
+static void fs_api_complete_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ a->fs_api_command_done = 1;
+
+ wake_up_interruptible(&a->fs_api_waiter);
+}
+
+/* Scatter/gather callback for VDA requests */
+static u32 get_physaddr_fs_api(struct esas2r_sg_context *sgc, u64 *addr)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
+ struct esas2r_ioctl_fs *fs =
+ (struct esas2r_ioctl_fs *)a->fs_api_buffer;
+ u32 offset = (u8 *)sgc->cur_offset - (u8 *)fs;
+
+ (*addr) = a->ppfs_api_buffer + offset;
+
+ return a->fs_api_buffer_size - offset;
+}
+
+/* Handle a call to read firmware via FS_API. */
+int esas2r_read_fs(struct esas2r_adapter *a, char *buf, long off, int count)
+{
+ if (!a->fs_api_buffer)
+ return -ENOMEM;
+
+ if (off == 0) {
+ struct esas2r_request *rq;
+ struct esas2r_sg_context sgc;
+ struct esas2r_ioctl_fs *fs =
+ (struct esas2r_ioctl_fs *)a->fs_api_buffer;
+
+ /* If another flash request is already in progress, return. */
+ if (mutex_lock_interruptible(&a->fs_api_mutex)) {
+busy:
+ fs->status = ATTO_STS_OUT_OF_RSRC;
+ return -EBUSY;
+ }
+
+ /*
+ * Presumeably, someone has already written to the
+ * fs_api_buffer, and now they are reading the node the
+ * response, so now we will actually issue the request to the
+ * chip and reply. Allocate a request
+ */
+
+ rq = esas2r_alloc_request(a);
+ if (rq == NULL) {
+ esas2r_debug("esas2r_read_fs: out of requests");
+ mutex_unlock(&a->fs_api_mutex);
+ goto busy;
+ }
+
+ rq->comp_cb = fs_api_complete_req;
+
+ /* Set up the SGCONTEXT for to build the s/g table */
+
+ sgc.cur_offset = fs->data;
+ sgc.get_phys_addr = (PGETPHYSADDR)get_physaddr_fs_api;
+
+ a->fs_api_command_done = 0;
+
+ if (!esas2r_process_fs_ioctl(a, fs, rq, &sgc)) {
+ if (fs->status == ATTO_STS_OUT_OF_RSRC)
+ count = -EBUSY;
+
+ goto dont_wait;
+ }
+
+ /* Now wait around for it to complete. */
+
+ while (!a->fs_api_command_done)
+ wait_event_interruptible(a->fs_api_waiter,
+ a->fs_api_command_done);
+ ;
+dont_wait:
+ /* Free the request and keep going */
+ mutex_unlock(&a->fs_api_mutex);
+ esas2r_free_request(a, (struct esas2r_request *)rq);
+
+ /* Pick up possible error code from above */
+ if (count < 0)
+ return count;
+ }
+
+ if (off > a->fs_api_buffer_size)
+ return 0;
+
+ if (count + off > a->fs_api_buffer_size)
+ count = a->fs_api_buffer_size - off;
+
+ if (count < 0)
+ return 0;
+
+ memcpy(buf, a->fs_api_buffer + off, count);
+
+ return count;
+}
+
+/* Handle a call to write firmware via FS_API. */
+int esas2r_write_fs(struct esas2r_adapter *a, const char *buf, long off,
+ int count)
+{
+ if (off == 0) {
+ struct esas2r_ioctl_fs *fs = (struct esas2r_ioctl_fs *)buf;
+ u32 length = fs->command.length + offsetof(
+ struct esas2r_ioctl_fs,
+ data);
+
+ /*
+ * Special case, for BEGIN commands, the length field
+ * is lying to us, so just get enough for the header.
+ */
+
+ if (fs->command.command == ESAS2R_FS_CMD_BEGINW)
+ length = offsetof(struct esas2r_ioctl_fs, data);
+
+ /*
+ * Beginning a command. We assume we'll get at least
+ * enough in the first write so we can look at the
+ * header and see how much we need to alloc.
+ */
+
+ if (count < offsetof(struct esas2r_ioctl_fs, data))
+ return -EINVAL;
+
+ /* Allocate a buffer or use the existing buffer. */
+ if (a->fs_api_buffer) {
+ if (a->fs_api_buffer_size < length) {
+ /* Free too-small buffer and get a new one */
+ dma_free_coherent(&a->pcid->dev,
+ (size_t)a->fs_api_buffer_size,
+ a->fs_api_buffer,
+ (dma_addr_t)a->ppfs_api_buffer);
+
+ goto re_allocate_buffer;
+ }
+ } else {
+re_allocate_buffer:
+ a->fs_api_buffer_size = length;
+
+ a->fs_api_buffer = dma_alloc_coherent(&a->pcid->dev,
+ (size_t)a->fs_api_buffer_size,
+ (dma_addr_t *)&a->ppfs_api_buffer,
+ GFP_KERNEL);
+ }
+ }
+
+ if (!a->fs_api_buffer)
+ return -ENOMEM;
+
+ if (off > a->fs_api_buffer_size)
+ return 0;
+
+ if (count + off > a->fs_api_buffer_size)
+ count = a->fs_api_buffer_size - off;
+
+ if (count < 1)
+ return 0;
+
+ memcpy(a->fs_api_buffer + off, buf, count);
+
+ return count;
+}
diff --git a/drivers/scsi/esas2r/esas2r_log.c b/drivers/scsi/esas2r/esas2r_log.c
new file mode 100644
index 000000000000..d6c87a0bae09
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_log.c
@@ -0,0 +1,252 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_log.c
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+/*
+ * this module within the driver is tasked with providing logging functionality.
+ * the event_log_level module parameter controls the level of messages that are
+ * written to the system log. the default level of messages that are written
+ * are critical and warning messages. if other types of messages are desired,
+ * one simply needs to load the module with the correct value for the
+ * event_log_level module parameter. for example:
+ *
+ * insmod <module> event_log_level=1
+ *
+ * will load the module and only critical events will be written by this module
+ * to the system log. if critical, warning, and information-level messages are
+ * desired, the correct value for the event_log_level module parameter
+ * would be as follows:
+ *
+ * insmod <module> event_log_level=3
+ */
+
+#define EVENT_LOG_BUFF_SIZE 1024
+
+static long event_log_level = ESAS2R_LOG_DFLT;
+
+module_param(event_log_level, long, S_IRUGO | S_IRUSR);
+MODULE_PARM_DESC(event_log_level,
+ "Specifies the level of events to report to the system log. Critical and warning level events are logged by default.");
+
+/* A shared buffer to use for formatting messages. */
+static char event_buffer[EVENT_LOG_BUFF_SIZE];
+
+/* A lock to protect the shared buffer used for formatting messages. */
+static DEFINE_SPINLOCK(event_buffer_lock);
+
+/*
+ * translates an esas2r-defined logging event level to a kernel logging level.
+ *
+ * @param [in] level the esas2r-defined logging event level to translate
+ *
+ * @return the corresponding kernel logging level.
+ */
+static const char *translate_esas2r_event_level_to_kernel(const long level)
+{
+ switch (level) {
+ case ESAS2R_LOG_CRIT:
+ return KERN_CRIT;
+
+ case ESAS2R_LOG_WARN:
+ return KERN_WARNING;
+
+ case ESAS2R_LOG_INFO:
+ return KERN_INFO;
+
+ case ESAS2R_LOG_DEBG:
+ case ESAS2R_LOG_TRCE:
+ default:
+ return KERN_DEBUG;
+ }
+}
+
+#pragma GCC diagnostic push
+#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
+#endif
+
+/*
+ * the master logging function. this function will format the message as
+ * outlined by the formatting string, the input device information and the
+ * substitution arguments and output the resulting string to the system log.
+ *
+ * @param [in] level the event log level of the message
+ * @param [in] dev the device information
+ * @param [in] format the formatting string for the message
+ * @param [in] args the substition arguments to the formatting string
+ *
+ * @return 0 on success, or -1 if an error occurred.
+ */
+static int esas2r_log_master(const long level,
+ const struct device *dev,
+ const char *format,
+ va_list args)
+{
+ if (level <= event_log_level) {
+ unsigned long flags = 0;
+ int retval = 0;
+ char *buffer = event_buffer;
+ size_t buflen = EVENT_LOG_BUFF_SIZE;
+ const char *fmt_nodev = "%s%s: ";
+ const char *fmt_dev = "%s%s [%s, %s, %s]";
+ const char *slevel =
+ translate_esas2r_event_level_to_kernel(level);
+
+ spin_lock_irqsave(&event_buffer_lock, flags);
+
+ memset(buffer, 0, buflen);
+
+ /*
+ * format the level onto the beginning of the string and do
+ * some pointer arithmetic to move the pointer to the point
+ * where the actual message can be inserted.
+ */
+
+ if (dev == NULL) {
+ snprintf(buffer, buflen, fmt_nodev, slevel,
+ ESAS2R_DRVR_NAME);
+ } else {
+ snprintf(buffer, buflen, fmt_dev, slevel,
+ ESAS2R_DRVR_NAME,
+ (dev->driver ? dev->driver->name : "unknown"),
+ (dev->bus ? dev->bus->name : "unknown"),
+ dev_name(dev));
+ }
+
+ buffer += strlen(event_buffer);
+ buflen -= strlen(event_buffer);
+
+ retval = vsnprintf(buffer, buflen, format, args);
+ if (retval < 0) {
+ spin_unlock_irqrestore(&event_buffer_lock, flags);
+ return -1;
+ }
+
+ /*
+ * Put a line break at the end of the formatted string so that
+ * we don't wind up with run-on messages.
+ */
+ printk("%s\n", event_buffer);
+
+ spin_unlock_irqrestore(&event_buffer_lock, flags);
+ }
+
+ return 0;
+}
+
+#pragma GCC diagnostic pop
+
+/*
+ * formats and logs a message to the system log.
+ *
+ * @param [in] level the event level of the message
+ * @param [in] format the formating string for the message
+ * @param [in] ... the substitution arguments to the formatting string
+ *
+ * @return 0 on success, or -1 if an error occurred.
+ */
+int esas2r_log(const long level, const char *format, ...)
+{
+ int retval = 0;
+ va_list args;
+
+ va_start(args, format);
+
+ retval = esas2r_log_master(level, NULL, format, args);
+
+ va_end(args);
+
+ return retval;
+}
+
+/*
+ * formats and logs a message to the system log. this message will include
+ * device information.
+ *
+ * @param [in] level the event level of the message
+ * @param [in] dev the device information
+ * @param [in] format the formatting string for the message
+ * @param [in] ... the substitution arguments to the formatting string
+ *
+ * @return 0 on success, or -1 if an error occurred.
+ */
+int esas2r_log_dev(const long level,
+ const struct device *dev,
+ const char *format,
+ ...)
+{
+ int retval = 0;
+ va_list args;
+
+ va_start(args, format);
+
+ retval = esas2r_log_master(level, dev, format, args);
+
+ va_end(args);
+
+ return retval;
+}
+
+/*
+ * formats and logs a message to the system log. this message will include
+ * device information.
+ *
+ * @param [in] level the event level of the message
+ * @param [in] buf
+ * @param [in] len
+ *
+ * @return 0 on success, or -1 if an error occurred.
+ */
+int esas2r_log_hexdump(const long level,
+ const void *buf,
+ size_t len)
+{
+ if (level <= event_log_level) {
+ print_hex_dump(translate_esas2r_event_level_to_kernel(level),
+ "", DUMP_PREFIX_OFFSET, 16, 1, buf,
+ len, true);
+ }
+
+ return 1;
+}
diff --git a/drivers/scsi/esas2r/esas2r_log.h b/drivers/scsi/esas2r/esas2r_log.h
new file mode 100644
index 000000000000..75b9d23cd736
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_log.h
@@ -0,0 +1,118 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_log.h
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef __esas2r_log_h__
+#define __esas2r_log_h__
+
+struct device;
+
+enum {
+ ESAS2R_LOG_NONE = 0, /* no events logged */
+ ESAS2R_LOG_CRIT = 1, /* critical events */
+ ESAS2R_LOG_WARN = 2, /* warning events */
+ ESAS2R_LOG_INFO = 3, /* info events */
+ ESAS2R_LOG_DEBG = 4, /* debugging events */
+ ESAS2R_LOG_TRCE = 5, /* tracing events */
+
+#ifdef ESAS2R_TRACE
+ ESAS2R_LOG_DFLT = ESAS2R_LOG_TRCE
+#else
+ ESAS2R_LOG_DFLT = ESAS2R_LOG_WARN
+#endif
+};
+
+__printf(2, 3) int esas2r_log(const long level, const char *format, ...);
+__printf(3, 4) int esas2r_log_dev(const long level,
+ const struct device *dev,
+ const char *format,
+ ...);
+int esas2r_log_hexdump(const long level,
+ const void *buf,
+ size_t len);
+
+/*
+ * the following macros are provided specifically for debugging and tracing
+ * messages. esas2r_debug() is provided for generic non-hardware layer
+ * debugging and tracing events. esas2r_hdebug is provided specifically for
+ * hardware layer debugging and tracing events.
+ */
+
+#ifdef ESAS2R_DEBUG
+#define esas2r_debug(f, args ...) esas2r_log(ESAS2R_LOG_DEBG, f, ## args)
+#define esas2r_hdebug(f, args ...) esas2r_log(ESAS2R_LOG_DEBG, f, ## args)
+#else
+#define esas2r_debug(f, args ...)
+#define esas2r_hdebug(f, args ...)
+#endif /* ESAS2R_DEBUG */
+
+/*
+ * the following macros are provided in order to trace the driver and catch
+ * some more serious bugs. be warned, enabling these macros may *severely*
+ * impact performance.
+ */
+
+#ifdef ESAS2R_TRACE
+#define esas2r_bugon() \
+ do { \
+ esas2r_log(ESAS2R_LOG_TRCE, "esas2r_bugon() called in %s:%d" \
+ " - dumping stack and stopping kernel", __func__, \
+ __LINE__); \
+ dump_stack(); \
+ BUG(); \
+ } while (0)
+
+#define esas2r_trace_enter() esas2r_log(ESAS2R_LOG_TRCE, "entered %s (%s:%d)", \
+ __func__, __FILE__, __LINE__)
+#define esas2r_trace_exit() esas2r_log(ESAS2R_LOG_TRCE, "exited %s (%s:%d)", \
+ __func__, __FILE__, __LINE__)
+#define esas2r_trace(f, args ...) esas2r_log(ESAS2R_LOG_TRCE, "(%s:%s:%d): " \
+ f, __func__, __FILE__, __LINE__, \
+ ## args)
+#else
+#define esas2r_bugon()
+#define esas2r_trace_enter()
+#define esas2r_trace_exit()
+#define esas2r_trace(f, args ...)
+#endif /* ESAS2R_TRACE */
+
+#endif /* __esas2r_log_h__ */
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
new file mode 100644
index 000000000000..be6bf518eb7c
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -0,0 +1,1912 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_main.c
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+MODULE_DESCRIPTION(ESAS2R_DRVR_NAME ": " ESAS2R_LONGNAME " driver");
+MODULE_AUTHOR("ATTO Technology, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ESAS2R_VERSION_STR);
+
+/* global definitions */
+
+static int found_adapters;
+struct esas2r_adapter *esas2r_adapters[MAX_ADAPTERS];
+
+#define ESAS2R_VDA_EVENT_PORT1 54414
+#define ESAS2R_VDA_EVENT_PORT2 54415
+#define ESAS2R_VDA_EVENT_SOCK_COUNT 2
+
+static struct esas2r_adapter *esas2r_adapter_from_kobj(struct kobject *kobj)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct Scsi_Host *host = class_to_shost(dev);
+
+ return (struct esas2r_adapter *)host->hostdata;
+}
+
+static ssize_t read_fw(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+
+ return esas2r_read_fw(a, buf, off, count);
+}
+
+static ssize_t write_fw(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+
+ return esas2r_write_fw(a, buf, off, count);
+}
+
+static ssize_t read_fs(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+
+ return esas2r_read_fs(a, buf, off, count);
+}
+
+static ssize_t write_fs(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+ int length = min(sizeof(struct esas2r_ioctl_fs), count);
+ int result = 0;
+
+ result = esas2r_write_fs(a, buf, off, count);
+
+ if (result < 0)
+ result = 0;
+
+ return length;
+}
+
+static ssize_t read_vda(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+
+ return esas2r_read_vda(a, buf, off, count);
+}
+
+static ssize_t write_vda(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+
+ return esas2r_write_vda(a, buf, off, count);
+}
+
+static ssize_t read_live_nvram(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+ int length = min_t(size_t, sizeof(struct esas2r_sas_nvram), PAGE_SIZE);
+
+ memcpy(buf, a->nvram, length);
+ return length;
+}
+
+static ssize_t write_live_nvram(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+ struct esas2r_request *rq;
+ int result = -EFAULT;
+
+ rq = esas2r_alloc_request(a);
+ if (rq == NULL)
+ return -ENOMEM;
+
+ if (esas2r_write_params(a, rq, (struct esas2r_sas_nvram *)buf))
+ result = count;
+
+ esas2r_free_request(a, rq);
+
+ return result;
+}
+
+static ssize_t read_default_nvram(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+
+ esas2r_nvram_get_defaults(a, (struct esas2r_sas_nvram *)buf);
+
+ return sizeof(struct esas2r_sas_nvram);
+}
+
+static ssize_t read_hw(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+ int length = min_t(size_t, sizeof(struct atto_ioctl), PAGE_SIZE);
+
+ if (!a->local_atto_ioctl)
+ return -ENOMEM;
+
+ if (handle_hba_ioctl(a, a->local_atto_ioctl) != IOCTL_SUCCESS)
+ return -ENOMEM;
+
+ memcpy(buf, a->local_atto_ioctl, length);
+
+ return length;
+}
+
+static ssize_t write_hw(struct file *file, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct esas2r_adapter *a = esas2r_adapter_from_kobj(kobj);
+ int length = min(sizeof(struct atto_ioctl), count);
+
+ if (!a->local_atto_ioctl) {
+ a->local_atto_ioctl = kmalloc(sizeof(struct atto_ioctl),
+ GFP_KERNEL);
+ if (a->local_atto_ioctl == NULL) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "write_hw kzalloc failed for %zu bytes",
+ sizeof(struct atto_ioctl));
+ return -ENOMEM;
+ }
+ }
+
+ memset(a->local_atto_ioctl, 0, sizeof(struct atto_ioctl));
+ memcpy(a->local_atto_ioctl, buf, length);
+
+ return length;
+}
+
+#define ESAS2R_RW_BIN_ATTR(_name) \
+ const struct bin_attribute bin_attr_ ## _name = { \
+ .attr = \
+ { .name = __stringify(_name), .mode = S_IRUSR | S_IWUSR }, \
+ .size = 0, \
+ .read = read_ ## _name, \
+ .write = write_ ## _name }
+
+ESAS2R_RW_BIN_ATTR(fw);
+ESAS2R_RW_BIN_ATTR(fs);
+ESAS2R_RW_BIN_ATTR(vda);
+ESAS2R_RW_BIN_ATTR(hw);
+ESAS2R_RW_BIN_ATTR(live_nvram);
+
+const struct bin_attribute bin_attr_default_nvram = {
+ .attr = { .name = "default_nvram", .mode = S_IRUGO },
+ .size = 0,
+ .read = read_default_nvram,
+ .write = NULL
+};
+
+static const struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .show_info = esas2r_show_info,
+ .name = ESAS2R_LONGNAME,
+ .info = esas2r_info,
+ .ioctl = esas2r_ioctl,
+ .queuecommand = esas2r_queuecommand,
+ .eh_abort_handler = esas2r_eh_abort,
+ .eh_device_reset_handler = esas2r_device_reset,
+ .eh_bus_reset_handler = esas2r_bus_reset,
+ .eh_host_reset_handler = esas2r_host_reset,
+ .eh_target_reset_handler = esas2r_target_reset,
+ .can_queue = 128,
+ .this_id = -1,
+ .sg_tablesize = SG_CHUNK_SIZE,
+ .cmd_per_lun =
+ ESAS2R_DEFAULT_CMD_PER_LUN,
+ .proc_name = ESAS2R_DRVR_NAME,
+ .change_queue_depth = scsi_change_queue_depth,
+ .max_sectors = 0xFFFF,
+};
+
+int sgl_page_size = 512;
+module_param(sgl_page_size, int, 0);
+MODULE_PARM_DESC(sgl_page_size,
+ "Scatter/gather list (SGL) page size in number of S/G "
+ "entries. If your application is doing a lot of very large "
+ "transfers, you may want to increase the SGL page size. "
+ "Default 512.");
+
+int num_sg_lists = 1024;
+module_param(num_sg_lists, int, 0);
+MODULE_PARM_DESC(num_sg_lists,
+ "Number of scatter/gather lists. Default 1024.");
+
+int sg_tablesize = SG_CHUNK_SIZE;
+module_param(sg_tablesize, int, 0);
+MODULE_PARM_DESC(sg_tablesize,
+ "Maximum number of entries in a scatter/gather table.");
+
+int num_requests = 256;
+module_param(num_requests, int, 0);
+MODULE_PARM_DESC(num_requests,
+ "Number of requests. Default 256.");
+
+int num_ae_requests = 4;
+module_param(num_ae_requests, int, 0);
+MODULE_PARM_DESC(num_ae_requests,
+ "Number of VDA asynchronous event requests. Default 4.");
+
+int cmd_per_lun = ESAS2R_DEFAULT_CMD_PER_LUN;
+module_param(cmd_per_lun, int, 0);
+MODULE_PARM_DESC(cmd_per_lun,
+ "Maximum number of commands per LUN. Default "
+ DEFINED_NUM_TO_STR(ESAS2R_DEFAULT_CMD_PER_LUN) ".");
+
+int can_queue = 128;
+module_param(can_queue, int, 0);
+MODULE_PARM_DESC(can_queue,
+ "Maximum number of commands per adapter. Default 128.");
+
+int esas2r_max_sectors = 0xFFFF;
+module_param(esas2r_max_sectors, int, 0);
+MODULE_PARM_DESC(esas2r_max_sectors,
+ "Maximum number of disk sectors in a single data transfer. "
+ "Default 65535 (largest possible setting).");
+
+int interrupt_mode = 1;
+module_param(interrupt_mode, int, 0);
+MODULE_PARM_DESC(interrupt_mode,
+ "Defines the interrupt mode to use. 0 for legacy"
+ ", 1 for MSI. Default is MSI (1).");
+
+static const struct pci_device_id
+ esas2r_pci_table[] = {
+ { ATTO_VENDOR_ID, 0x0049, ATTO_VENDOR_ID, 0x0049,
+ 0,
+ 0, 0 },
+ { ATTO_VENDOR_ID, 0x0049, ATTO_VENDOR_ID, 0x004A,
+ 0,
+ 0, 0 },
+ { ATTO_VENDOR_ID, 0x0049, ATTO_VENDOR_ID, 0x004B,
+ 0,
+ 0, 0 },
+ { ATTO_VENDOR_ID, 0x0049, ATTO_VENDOR_ID, 0x004C,
+ 0,
+ 0, 0 },
+ { ATTO_VENDOR_ID, 0x0049, ATTO_VENDOR_ID, 0x004D,
+ 0,
+ 0, 0 },
+ { ATTO_VENDOR_ID, 0x0049, ATTO_VENDOR_ID, 0x004E,
+ 0,
+ 0, 0 },
+ { 0, 0, 0, 0,
+ 0,
+ 0, 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, esas2r_pci_table);
+
+static int
+esas2r_probe(struct pci_dev *pcid, const struct pci_device_id *id);
+
+static void
+esas2r_remove(struct pci_dev *pcid);
+
+static struct pci_driver
+ esas2r_pci_driver = {
+ .name = ESAS2R_DRVR_NAME,
+ .id_table = esas2r_pci_table,
+ .probe = esas2r_probe,
+ .remove = esas2r_remove,
+ .driver.pm = &esas2r_pm_ops,
+};
+
+static int esas2r_probe(struct pci_dev *pcid,
+ const struct pci_device_id *id)
+{
+ struct Scsi_Host *host = NULL;
+ struct esas2r_adapter *a;
+ int err;
+
+ size_t host_alloc_size = sizeof(struct esas2r_adapter)
+ + ((num_requests) +
+ 1) * sizeof(struct esas2r_request);
+
+ esas2r_log_dev(ESAS2R_LOG_DEBG, &(pcid->dev),
+ "esas2r_probe() 0x%02x 0x%02x 0x%02x 0x%02x",
+ pcid->vendor,
+ pcid->device,
+ pcid->subsystem_vendor,
+ pcid->subsystem_device);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(pcid->dev),
+ "before pci_enable_device() "
+ "enable_cnt: %d",
+ pcid->enable_cnt.counter);
+
+ err = pci_enable_device(pcid);
+ if (err != 0) {
+ esas2r_log_dev(ESAS2R_LOG_CRIT, &(pcid->dev),
+ "pci_enable_device() FAIL (%d)",
+ err);
+ return -ENODEV;
+ }
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(pcid->dev),
+ "pci_enable_device() OK");
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(pcid->dev),
+ "after pci_enable_device() enable_cnt: %d",
+ pcid->enable_cnt.counter);
+
+ host = scsi_host_alloc(&driver_template, host_alloc_size);
+ if (host == NULL) {
+ esas2r_log(ESAS2R_LOG_CRIT, "scsi_host_alloc() FAIL");
+ return -ENODEV;
+ }
+
+ memset(host->hostdata, 0, host_alloc_size);
+
+ a = (struct esas2r_adapter *)host->hostdata;
+
+ esas2r_log(ESAS2R_LOG_INFO, "scsi_host_alloc() OK host: %p", host);
+
+ /* override max LUN and max target id */
+
+ host->max_id = ESAS2R_MAX_ID + 1;
+ host->max_lun = 255;
+
+ /* we can handle 16-byte CDbs */
+
+ host->max_cmd_len = 16;
+
+ host->can_queue = can_queue;
+ host->cmd_per_lun = cmd_per_lun;
+ host->this_id = host->max_id + 1;
+ host->max_channel = 0;
+ host->unique_id = found_adapters;
+ host->sg_tablesize = sg_tablesize;
+ host->max_sectors = esas2r_max_sectors;
+
+ /* set to bus master for BIOses that don't do it for us */
+
+ esas2r_log(ESAS2R_LOG_INFO, "pci_set_master() called");
+
+ pci_set_master(pcid);
+
+ if (!esas2r_init_adapter(host, pcid, found_adapters)) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "unable to initialize device at PCI bus %x:%x",
+ pcid->bus->number,
+ pcid->devfn);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(host->shost_gendev),
+ "scsi_host_put() called");
+
+ scsi_host_put(host);
+
+ return 0;
+
+ }
+
+ esas2r_log(ESAS2R_LOG_INFO, "pci_set_drvdata(%p, %p) called", pcid,
+ host->hostdata);
+
+ pci_set_drvdata(pcid, host);
+
+ esas2r_log(ESAS2R_LOG_INFO, "scsi_add_host() called");
+
+ err = scsi_add_host(host, &pcid->dev);
+
+ if (err) {
+ esas2r_log(ESAS2R_LOG_CRIT, "scsi_add_host returned %d", err);
+ esas2r_log_dev(ESAS2R_LOG_CRIT, &(host->shost_gendev),
+ "scsi_add_host() FAIL");
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(host->shost_gendev),
+ "scsi_host_put() called");
+
+ scsi_host_put(host);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(host->shost_gendev),
+ "pci_set_drvdata(%p, NULL) called",
+ pcid);
+
+ pci_set_drvdata(pcid, NULL);
+
+ return -ENODEV;
+ }
+
+
+ esas2r_fw_event_on(a);
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(host->shost_gendev),
+ "scsi_scan_host() called");
+
+ scsi_scan_host(host);
+
+ /* Add sysfs binary files */
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &bin_attr_fw))
+ esas2r_log_dev(ESAS2R_LOG_WARN, &(host->shost_gendev),
+ "Failed to create sysfs binary file: fw");
+ else
+ a->sysfs_fw_created = 1;
+
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &bin_attr_fs))
+ esas2r_log_dev(ESAS2R_LOG_WARN, &(host->shost_gendev),
+ "Failed to create sysfs binary file: fs");
+ else
+ a->sysfs_fs_created = 1;
+
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &bin_attr_vda))
+ esas2r_log_dev(ESAS2R_LOG_WARN, &(host->shost_gendev),
+ "Failed to create sysfs binary file: vda");
+ else
+ a->sysfs_vda_created = 1;
+
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &bin_attr_hw))
+ esas2r_log_dev(ESAS2R_LOG_WARN, &(host->shost_gendev),
+ "Failed to create sysfs binary file: hw");
+ else
+ a->sysfs_hw_created = 1;
+
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &bin_attr_live_nvram))
+ esas2r_log_dev(ESAS2R_LOG_WARN, &(host->shost_gendev),
+ "Failed to create sysfs binary file: live_nvram");
+ else
+ a->sysfs_live_nvram_created = 1;
+
+ if (sysfs_create_bin_file(&host->shost_dev.kobj,
+ &bin_attr_default_nvram))
+ esas2r_log_dev(ESAS2R_LOG_WARN, &(host->shost_gendev),
+ "Failed to create sysfs binary file: default_nvram");
+ else
+ a->sysfs_default_nvram_created = 1;
+
+ found_adapters++;
+
+ return 0;
+}
+
+static void esas2r_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct esas2r_adapter *a = (struct esas2r_adapter *)host->hostdata;
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(pdev->dev),
+ "esas2r_remove(%p) called; "
+ "host:%p", pdev,
+ host);
+
+ esas2r_kill_adapter(a->index);
+ found_adapters--;
+}
+
+static int __init esas2r_init(void)
+{
+ int i;
+
+ esas2r_log(ESAS2R_LOG_INFO, "%s called", __func__);
+
+ /* verify valid parameters */
+
+ if (can_queue < 1) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "warning: can_queue must be at least 1, value "
+ "forced.");
+ can_queue = 1;
+ } else if (can_queue > 2048) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "warning: can_queue must be no larger than 2048, "
+ "value forced.");
+ can_queue = 2048;
+ }
+
+ if (cmd_per_lun < 1) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "warning: cmd_per_lun must be at least 1, value "
+ "forced.");
+ cmd_per_lun = 1;
+ } else if (cmd_per_lun > 2048) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "warning: cmd_per_lun must be no larger than "
+ "2048, value forced.");
+ cmd_per_lun = 2048;
+ }
+
+ if (sg_tablesize < 32) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "warning: sg_tablesize must be at least 32, "
+ "value forced.");
+ sg_tablesize = 32;
+ }
+
+ if (esas2r_max_sectors < 1) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "warning: esas2r_max_sectors must be at least "
+ "1, value forced.");
+ esas2r_max_sectors = 1;
+ } else if (esas2r_max_sectors > 0xffff) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "warning: esas2r_max_sectors must be no larger "
+ "than 0xffff, value forced.");
+ esas2r_max_sectors = 0xffff;
+ }
+
+ sgl_page_size &= ~(ESAS2R_SGL_ALIGN - 1);
+
+ if (sgl_page_size < SGL_PG_SZ_MIN)
+ sgl_page_size = SGL_PG_SZ_MIN;
+ else if (sgl_page_size > SGL_PG_SZ_MAX)
+ sgl_page_size = SGL_PG_SZ_MAX;
+
+ if (num_sg_lists < NUM_SGL_MIN)
+ num_sg_lists = NUM_SGL_MIN;
+ else if (num_sg_lists > NUM_SGL_MAX)
+ num_sg_lists = NUM_SGL_MAX;
+
+ if (num_requests < NUM_REQ_MIN)
+ num_requests = NUM_REQ_MIN;
+ else if (num_requests > NUM_REQ_MAX)
+ num_requests = NUM_REQ_MAX;
+
+ if (num_ae_requests < NUM_AE_MIN)
+ num_ae_requests = NUM_AE_MIN;
+ else if (num_ae_requests > NUM_AE_MAX)
+ num_ae_requests = NUM_AE_MAX;
+
+ /* set up other globals */
+
+ for (i = 0; i < MAX_ADAPTERS; i++)
+ esas2r_adapters[i] = NULL;
+
+ return pci_register_driver(&esas2r_pci_driver);
+}
+
+/* Handle ioctl calls to "/proc/scsi/esas2r/ATTOnode" */
+static const struct file_operations esas2r_proc_fops = {
+ .compat_ioctl = compat_ptr_ioctl,
+ .unlocked_ioctl = esas2r_proc_ioctl,
+};
+
+static const struct proc_ops esas2r_proc_ops = {
+ .proc_lseek = default_llseek,
+ .proc_ioctl = esas2r_proc_ioctl,
+#ifdef CONFIG_COMPAT
+ .proc_compat_ioctl = compat_ptr_ioctl,
+#endif
+};
+
+static struct Scsi_Host *esas2r_proc_host;
+static int esas2r_proc_major;
+
+long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ return esas2r_ioctl_handler(esas2r_proc_host->hostdata,
+ cmd, (void __user *)arg);
+}
+
+static void __exit esas2r_exit(void)
+{
+ esas2r_log(ESAS2R_LOG_INFO, "%s called", __func__);
+
+ if (esas2r_proc_major > 0) {
+ struct proc_dir_entry *proc_dir;
+
+ esas2r_log(ESAS2R_LOG_INFO, "unregister proc");
+
+ proc_dir = scsi_template_proc_dir(esas2r_proc_host->hostt);
+ if (proc_dir)
+ remove_proc_entry(ATTONODE_NAME, proc_dir);
+ unregister_chrdev(esas2r_proc_major, ESAS2R_DRVR_NAME);
+
+ esas2r_proc_major = 0;
+ }
+
+ esas2r_log(ESAS2R_LOG_INFO, "pci_unregister_driver() called");
+
+ pci_unregister_driver(&esas2r_pci_driver);
+}
+
+int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)sh->hostdata;
+
+ struct esas2r_target *t;
+ int dev_count = 0;
+
+ esas2r_log(ESAS2R_LOG_DEBG, "esas2r_show_info (%p,%d)", m, sh->host_no);
+
+ seq_printf(m, ESAS2R_LONGNAME "\n"
+ "Driver version: "ESAS2R_VERSION_STR "\n"
+ "Flash version: %s\n"
+ "Firmware version: %s\n"
+ "Copyright "ESAS2R_COPYRIGHT_YEARS "\n"
+ "http://www.attotech.com\n"
+ "\n",
+ a->flash_rev,
+ a->fw_rev[0] ? a->fw_rev : "(none)");
+
+
+ seq_printf(m, "Adapter information:\n"
+ "--------------------\n"
+ "Model: %s\n"
+ "SAS address: %02X%02X%02X%02X:%02X%02X%02X%02X\n",
+ esas2r_get_model_name(a),
+ a->nvram->sas_addr[0],
+ a->nvram->sas_addr[1],
+ a->nvram->sas_addr[2],
+ a->nvram->sas_addr[3],
+ a->nvram->sas_addr[4],
+ a->nvram->sas_addr[5],
+ a->nvram->sas_addr[6],
+ a->nvram->sas_addr[7]);
+
+ seq_puts(m, "\n"
+ "Discovered devices:\n"
+ "\n"
+ " # Target ID\n"
+ "---------------\n");
+
+ for (t = a->targetdb; t < a->targetdb_end; t++)
+ if (t->buffered_target_state == TS_PRESENT) {
+ seq_printf(m, " %3d %3d\n",
+ ++dev_count,
+ (u16)(uintptr_t)(t - a->targetdb));
+ }
+
+ if (dev_count == 0)
+ seq_puts(m, "none\n");
+
+ seq_putc(m, '\n');
+ return 0;
+
+}
+
+const char *esas2r_info(struct Scsi_Host *sh)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)sh->hostdata;
+ static char esas2r_info_str[512];
+
+ esas2r_log_dev(ESAS2R_LOG_INFO, &(sh->shost_gendev),
+ "esas2r_info() called");
+
+ /*
+ * if we haven't done so already, register as a char driver
+ * and stick a node under "/proc/scsi/esas2r/ATTOnode"
+ */
+
+ if (esas2r_proc_major <= 0) {
+ esas2r_proc_host = sh;
+
+ esas2r_proc_major = register_chrdev(0, ESAS2R_DRVR_NAME,
+ &esas2r_proc_fops);
+
+ esas2r_log_dev(ESAS2R_LOG_DEBG, &(sh->shost_gendev),
+ "register_chrdev (major %d)",
+ esas2r_proc_major);
+
+ if (esas2r_proc_major > 0) {
+ struct proc_dir_entry *proc_dir;
+ struct proc_dir_entry *pde = NULL;
+
+ proc_dir = scsi_template_proc_dir(sh->hostt);
+ if (proc_dir)
+ pde = proc_create(ATTONODE_NAME, 0, proc_dir,
+ &esas2r_proc_ops);
+
+ if (!pde) {
+ esas2r_log_dev(ESAS2R_LOG_WARN,
+ &(sh->shost_gendev),
+ "failed to create_proc_entry");
+ esas2r_proc_major = -1;
+ }
+ }
+ }
+
+ sprintf(esas2r_info_str,
+ ESAS2R_LONGNAME " (bus 0x%02X, device 0x%02X, IRQ 0x%02X)"
+ " driver version: "ESAS2R_VERSION_STR " firmware version: "
+ "%s\n",
+ a->pcid->bus->number, a->pcid->devfn, a->pcid->irq,
+ a->fw_rev[0] ? a->fw_rev : "(none)");
+
+ return esas2r_info_str;
+}
+
+/* Callback for building a request scatter/gather list */
+static u32 get_physaddr_from_sgc(struct esas2r_sg_context *sgc, u64 *addr)
+{
+ u32 len;
+
+ if (likely(sgc->cur_offset == sgc->exp_offset)) {
+ /*
+ * the normal case: caller used all bytes from previous call, so
+ * expected offset is the same as the current offset.
+ */
+
+ if (sgc->sgel_count < sgc->num_sgel) {
+ /* retrieve next segment, except for first time */
+ if (sgc->exp_offset > (u8 *)0) {
+ /* advance current segment */
+ sgc->cur_sgel = sg_next(sgc->cur_sgel);
+ ++(sgc->sgel_count);
+ }
+
+
+ len = sg_dma_len(sgc->cur_sgel);
+ (*addr) = sg_dma_address(sgc->cur_sgel);
+
+ /* save the total # bytes returned to caller so far */
+ sgc->exp_offset += len;
+
+ } else {
+ len = 0;
+ }
+ } else if (sgc->cur_offset < sgc->exp_offset) {
+ /*
+ * caller did not use all bytes from previous call. need to
+ * compute the address based on current segment.
+ */
+
+ len = sg_dma_len(sgc->cur_sgel);
+ (*addr) = sg_dma_address(sgc->cur_sgel);
+
+ sgc->exp_offset -= len;
+
+ /* calculate PA based on prev segment address and offsets */
+ *addr = *addr +
+ (sgc->cur_offset - sgc->exp_offset);
+
+ sgc->exp_offset += len;
+
+ /* re-calculate length based on offset */
+ len = lower_32_bits(
+ sgc->exp_offset - sgc->cur_offset);
+ } else { /* if ( sgc->cur_offset > sgc->exp_offset ) */
+ /*
+ * we don't expect the caller to skip ahead.
+ * cur_offset will never exceed the len we return
+ */
+ len = 0;
+ }
+
+ return len;
+}
+
+int esas2r_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
+{
+ struct esas2r_adapter *a =
+ (struct esas2r_adapter *)cmd->device->host->hostdata;
+ struct esas2r_request *rq;
+ struct esas2r_sg_context sgc;
+ unsigned bufflen;
+
+ /* Assume success, if it fails we will fix the result later. */
+ cmd->result = DID_OK << 16;
+
+ if (unlikely(test_bit(AF_DEGRADED_MODE, &a->flags))) {
+ cmd->result = DID_NO_CONNECT << 16;
+ scsi_done(cmd);
+ return 0;
+ }
+
+ rq = esas2r_alloc_request(a);
+ if (unlikely(rq == NULL)) {
+ esas2r_debug("esas2r_alloc_request failed");
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ rq->cmd = cmd;
+ bufflen = scsi_bufflen(cmd);
+
+ if (likely(bufflen != 0)) {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_WRD);
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_RDD);
+ }
+
+ memcpy(rq->vrq->scsi.cdb, cmd->cmnd, cmd->cmd_len);
+ rq->vrq->scsi.length = cpu_to_le32(bufflen);
+ rq->target_id = cmd->device->id;
+ rq->vrq->scsi.flags |= cpu_to_le32(cmd->device->lun);
+ rq->sense_buf = cmd->sense_buffer;
+ rq->sense_len = SCSI_SENSE_BUFFERSIZE;
+
+ esas2r_sgc_init(&sgc, a, rq, NULL);
+
+ sgc.length = bufflen;
+ sgc.cur_offset = NULL;
+
+ sgc.cur_sgel = scsi_sglist(cmd);
+ sgc.exp_offset = NULL;
+ sgc.num_sgel = scsi_dma_map(cmd);
+ sgc.sgel_count = 0;
+
+ if (unlikely(sgc.num_sgel < 0)) {
+ esas2r_free_request(a, rq);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ sgc.get_phys_addr = (PGETPHYSADDR)get_physaddr_from_sgc;
+
+ if (unlikely(!esas2r_build_sg_list(a, rq, &sgc))) {
+ scsi_dma_unmap(cmd);
+ esas2r_free_request(a, rq);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ esas2r_debug("start request %p to %d:%d\n", rq, (int)cmd->device->id,
+ (int)cmd->device->lun);
+
+ esas2r_start_request(a, rq);
+
+ return 0;
+}
+
+static void complete_task_management_request(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ (*rq->task_management_status_ptr) = rq->req_stat;
+ esas2r_free_request(a, rq);
+}
+
+/*
+ * Searches the specified queue for the specified queue for the command
+ * to abort.
+ *
+ * Return 0 on failure, 1 if command was not found, 2 if command was found
+ */
+static int esas2r_check_active_queue(struct esas2r_adapter *a,
+ struct esas2r_request **abort_request,
+ struct scsi_cmnd *cmd,
+ struct list_head *queue)
+{
+ bool found = false;
+ struct esas2r_request *ar = *abort_request;
+ struct esas2r_request *rq;
+ struct list_head *element, *next;
+
+ list_for_each_safe(element, next, queue) {
+
+ rq = list_entry(element, struct esas2r_request, req_list);
+
+ if (rq->cmd == cmd) {
+
+ /* Found the request. See what to do with it. */
+ if (queue == &a->active_list) {
+ /*
+ * We are searching the active queue, which
+ * means that we need to send an abort request
+ * to the firmware.
+ */
+ ar = esas2r_alloc_request(a);
+ if (ar == NULL) {
+ esas2r_log_dev(ESAS2R_LOG_WARN,
+ &(a->host->shost_gendev),
+ "unable to allocate an abort request for cmd %p",
+ cmd);
+ return 0; /* Failure */
+ }
+
+ /*
+ * Task management request must be formatted
+ * with a lock held.
+ */
+ ar->sense_len = 0;
+ ar->vrq->scsi.length = 0;
+ ar->target_id = rq->target_id;
+ ar->vrq->scsi.flags |= cpu_to_le32(
+ (u8)le32_to_cpu(rq->vrq->scsi.flags));
+
+ memset(ar->vrq->scsi.cdb, 0,
+ sizeof(ar->vrq->scsi.cdb));
+
+ ar->vrq->scsi.flags |= cpu_to_le32(
+ FCP_CMND_TRM);
+ ar->vrq->scsi.u.abort_handle =
+ rq->vrq->scsi.handle;
+ } else {
+ /*
+ * The request is pending but not active on
+ * the firmware. Just free it now and we'll
+ * report the successful abort below.
+ */
+ list_del_init(&rq->req_list);
+ esas2r_free_request(a, rq);
+ }
+
+ found = true;
+ break;
+ }
+
+ }
+
+ if (!found)
+ return 1; /* Not found */
+
+ return 2; /* found */
+
+
+}
+
+int esas2r_eh_abort(struct scsi_cmnd *cmd)
+{
+ struct esas2r_adapter *a =
+ (struct esas2r_adapter *)cmd->device->host->hostdata;
+ struct esas2r_request *abort_request = NULL;
+ unsigned long flags;
+ struct list_head *queue;
+ int result;
+
+ esas2r_log(ESAS2R_LOG_INFO, "eh_abort (%p)", cmd);
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
+ cmd->result = DID_ABORT << 16;
+
+ scsi_set_resid(cmd, 0);
+
+ scsi_done(cmd);
+
+ return SUCCESS;
+ }
+
+ spin_lock_irqsave(&a->queue_lock, flags);
+
+ /*
+ * Run through the defer and active queues looking for the request
+ * to abort.
+ */
+
+ queue = &a->defer_list;
+
+check_active_queue:
+
+ result = esas2r_check_active_queue(a, &abort_request, cmd, queue);
+
+ if (!result) {
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+ return FAILED;
+ } else if (result == 2 && (queue == &a->defer_list)) {
+ queue = &a->active_list;
+ goto check_active_queue;
+ }
+
+ spin_unlock_irqrestore(&a->queue_lock, flags);
+
+ if (abort_request) {
+ u8 task_management_status = RS_PENDING;
+
+ /*
+ * the request is already active, so we need to tell
+ * the firmware to abort it and wait for the response.
+ */
+
+ abort_request->comp_cb = complete_task_management_request;
+ abort_request->task_management_status_ptr =
+ &task_management_status;
+
+ esas2r_start_request(a, abort_request);
+
+ if (atomic_read(&a->disable_cnt) == 0)
+ esas2r_do_deferred_processes(a);
+
+ while (task_management_status == RS_PENDING)
+ msleep(10);
+
+ /*
+ * Once we get here, the original request will have been
+ * completed by the firmware and the abort request will have
+ * been cleaned up. we're done!
+ */
+
+ return SUCCESS;
+ }
+
+ /*
+ * If we get here, either we found the inactive request and
+ * freed it, or we didn't find it at all. Either way, success!
+ */
+
+ cmd->result = DID_ABORT << 16;
+
+ scsi_set_resid(cmd, 0);
+
+ scsi_done(cmd);
+
+ return SUCCESS;
+}
+
+static int esas2r_host_bus_reset(struct scsi_cmnd *cmd, bool host_reset)
+{
+ struct esas2r_adapter *a =
+ (struct esas2r_adapter *)cmd->device->host->hostdata;
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return FAILED;
+
+ if (host_reset)
+ esas2r_reset_adapter(a);
+ else
+ esas2r_reset_bus(a);
+
+ /* above call sets the AF_OS_RESET flag. wait for it to clear. */
+
+ while (test_bit(AF_OS_RESET, &a->flags)) {
+ msleep(10);
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return FAILED;
+ }
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return FAILED;
+
+ return SUCCESS;
+}
+
+int esas2r_host_reset(struct scsi_cmnd *cmd)
+{
+ esas2r_log(ESAS2R_LOG_INFO, "host_reset (%p)", cmd);
+
+ return esas2r_host_bus_reset(cmd, true);
+}
+
+int esas2r_bus_reset(struct scsi_cmnd *cmd)
+{
+ esas2r_log(ESAS2R_LOG_INFO, "bus_reset (%p)", cmd);
+
+ return esas2r_host_bus_reset(cmd, false);
+}
+
+static int esas2r_dev_targ_reset(struct scsi_cmnd *cmd, bool target_reset)
+{
+ struct esas2r_adapter *a =
+ (struct esas2r_adapter *)cmd->device->host->hostdata;
+ struct esas2r_request *rq;
+ u8 task_management_status = RS_PENDING;
+ bool completed;
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return FAILED;
+
+retry:
+ rq = esas2r_alloc_request(a);
+ if (rq == NULL) {
+ if (target_reset) {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "unable to allocate a request for a "
+ "target reset (%d)!",
+ cmd->device->id);
+ } else {
+ esas2r_log(ESAS2R_LOG_CRIT,
+ "unable to allocate a request for a "
+ "device reset (%d:%llu)!",
+ cmd->device->id,
+ cmd->device->lun);
+ }
+
+
+ return FAILED;
+ }
+
+ rq->target_id = cmd->device->id;
+ rq->vrq->scsi.flags |= cpu_to_le32(cmd->device->lun);
+ rq->req_stat = RS_PENDING;
+
+ rq->comp_cb = complete_task_management_request;
+ rq->task_management_status_ptr = &task_management_status;
+
+ if (target_reset) {
+ esas2r_debug("issuing target reset (%p) to id %d", rq,
+ cmd->device->id);
+ completed = esas2r_send_task_mgmt(a, rq, 0x20);
+ } else {
+ esas2r_debug("issuing device reset (%p) to id %d lun %d", rq,
+ cmd->device->id, cmd->device->lun);
+ completed = esas2r_send_task_mgmt(a, rq, 0x10);
+ }
+
+ if (completed) {
+ /* Task management cmd completed right away, need to free it. */
+
+ esas2r_free_request(a, rq);
+ } else {
+ /*
+ * Wait for firmware to complete the request. Completion
+ * callback will free it.
+ */
+ while (task_management_status == RS_PENDING)
+ msleep(10);
+ }
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags))
+ return FAILED;
+
+ if (task_management_status == RS_BUSY) {
+ /*
+ * Busy, probably because we are flashing. Wait a bit and
+ * try again.
+ */
+ msleep(100);
+ goto retry;
+ }
+
+ return SUCCESS;
+}
+
+int esas2r_device_reset(struct scsi_cmnd *cmd)
+{
+ esas2r_log(ESAS2R_LOG_INFO, "device_reset (%p)", cmd);
+
+ return esas2r_dev_targ_reset(cmd, false);
+
+}
+
+int esas2r_target_reset(struct scsi_cmnd *cmd)
+{
+ esas2r_log(ESAS2R_LOG_INFO, "target_reset (%p)", cmd);
+
+ return esas2r_dev_targ_reset(cmd, true);
+}
+
+void esas2r_log_request_failure(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ u8 reqstatus = rq->req_stat;
+
+ if (reqstatus == RS_SUCCESS)
+ return;
+
+ if (rq->vrq->scsi.function == VDA_FUNC_SCSI) {
+ if (reqstatus == RS_SCSI_ERROR) {
+ if (rq->func_rsp.scsi_rsp.sense_len >= 13) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "request failure - SCSI error %x ASC:%x ASCQ:%x CDB:%x",
+ rq->sense_buf[2], rq->sense_buf[12],
+ rq->sense_buf[13],
+ rq->vrq->scsi.cdb[0]);
+ } else {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "request failure - SCSI error CDB:%x\n",
+ rq->vrq->scsi.cdb[0]);
+ }
+ } else if ((rq->vrq->scsi.cdb[0] != INQUIRY
+ && rq->vrq->scsi.cdb[0] != REPORT_LUNS)
+ || (reqstatus != RS_SEL
+ && reqstatus != RS_SEL2)) {
+ if ((reqstatus == RS_UNDERRUN) &&
+ (rq->vrq->scsi.cdb[0] == INQUIRY)) {
+ /* Don't log inquiry underruns */
+ } else {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "request failure - cdb:%x reqstatus:%d target:%d",
+ rq->vrq->scsi.cdb[0], reqstatus,
+ rq->target_id);
+ }
+ }
+ }
+}
+
+void esas2r_wait_request(struct esas2r_adapter *a, struct esas2r_request *rq)
+{
+ u32 starttime;
+ u32 timeout;
+
+ starttime = jiffies_to_msecs(jiffies);
+ timeout = rq->timeout ? rq->timeout : 5000;
+
+ while (true) {
+ esas2r_polled_interrupt(a);
+
+ if (rq->req_stat != RS_STARTED)
+ break;
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if ((jiffies_to_msecs(jiffies) - starttime) > timeout) {
+ esas2r_hdebug("request TMO");
+ esas2r_bugon();
+
+ rq->req_stat = RS_TIMEOUT;
+
+ esas2r_local_reset_adapter(a);
+ return;
+ }
+ }
+}
+
+u32 esas2r_map_data_window(struct esas2r_adapter *a, u32 addr_lo)
+{
+ u32 offset = addr_lo & (MW_DATA_WINDOW_SIZE - 1);
+ u32 base = addr_lo & -(signed int)MW_DATA_WINDOW_SIZE;
+
+ if (a->window_base != base) {
+ esas2r_write_register_dword(a, MVR_PCI_WIN1_REMAP,
+ base | MVRPW1R_ENABLE);
+ esas2r_flush_register_dword(a, MVR_PCI_WIN1_REMAP);
+ a->window_base = base;
+ }
+
+ return offset;
+}
+
+/* Read a block of data from chip memory */
+bool esas2r_read_mem_block(struct esas2r_adapter *a,
+ void *to,
+ u32 from,
+ u32 size)
+{
+ u8 *end = (u8 *)to;
+
+ while (size) {
+ u32 len;
+ u32 offset;
+ u32 iatvr;
+
+ iatvr = (from & -(signed int)MW_DATA_WINDOW_SIZE);
+
+ esas2r_map_data_window(a, iatvr);
+
+ offset = from & (MW_DATA_WINDOW_SIZE - 1);
+ len = size;
+
+ if (len > MW_DATA_WINDOW_SIZE - offset)
+ len = MW_DATA_WINDOW_SIZE - offset;
+
+ from += len;
+ size -= len;
+
+ while (len--) {
+ *end++ = esas2r_read_data_byte(a, offset);
+ offset++;
+ }
+ }
+
+ return true;
+}
+
+void esas2r_nuxi_mgt_data(u8 function, void *data)
+{
+ struct atto_vda_grp_info *g;
+ struct atto_vda_devinfo *d;
+ struct atto_vdapart_info *p;
+ struct atto_vda_dh_info *h;
+ struct atto_vda_metrics_info *m;
+ struct atto_vda_schedule_info *s;
+ struct atto_vda_buzzer_info *b;
+ u8 i;
+
+ switch (function) {
+ case VDAMGT_BUZZER_INFO:
+ case VDAMGT_BUZZER_SET:
+
+ b = (struct atto_vda_buzzer_info *)data;
+
+ b->duration = le32_to_cpu(b->duration);
+ break;
+
+ case VDAMGT_SCHEDULE_INFO:
+ case VDAMGT_SCHEDULE_EVENT:
+
+ s = (struct atto_vda_schedule_info *)data;
+
+ s->id = le32_to_cpu(s->id);
+
+ break;
+
+ case VDAMGT_DEV_INFO:
+ case VDAMGT_DEV_CLEAN:
+ case VDAMGT_DEV_PT_INFO:
+ case VDAMGT_DEV_FEATURES:
+ case VDAMGT_DEV_PT_FEATURES:
+ case VDAMGT_DEV_OPERATION:
+
+ d = (struct atto_vda_devinfo *)data;
+
+ d->capacity = le64_to_cpu(d->capacity);
+ d->block_size = le32_to_cpu(d->block_size);
+ d->ses_dev_index = le16_to_cpu(d->ses_dev_index);
+ d->target_id = le16_to_cpu(d->target_id);
+ d->lun = le16_to_cpu(d->lun);
+ d->features = le16_to_cpu(d->features);
+ break;
+
+ case VDAMGT_GRP_INFO:
+ case VDAMGT_GRP_CREATE:
+ case VDAMGT_GRP_DELETE:
+ case VDAMGT_ADD_STORAGE:
+ case VDAMGT_MEMBER_ADD:
+ case VDAMGT_GRP_COMMIT:
+ case VDAMGT_GRP_REBUILD:
+ case VDAMGT_GRP_COMMIT_INIT:
+ case VDAMGT_QUICK_RAID:
+ case VDAMGT_GRP_FEATURES:
+ case VDAMGT_GRP_COMMIT_INIT_AUTOMAP:
+ case VDAMGT_QUICK_RAID_INIT_AUTOMAP:
+ case VDAMGT_SPARE_LIST:
+ case VDAMGT_SPARE_ADD:
+ case VDAMGT_SPARE_REMOVE:
+ case VDAMGT_LOCAL_SPARE_ADD:
+ case VDAMGT_GRP_OPERATION:
+
+ g = (struct atto_vda_grp_info *)data;
+
+ g->capacity = le64_to_cpu(g->capacity);
+ g->block_size = le32_to_cpu(g->block_size);
+ g->interleave = le32_to_cpu(g->interleave);
+ g->features = le16_to_cpu(g->features);
+
+ for (i = 0; i < 32; i++)
+ g->members[i] = le16_to_cpu(g->members[i]);
+
+ break;
+
+ case VDAMGT_PART_INFO:
+ case VDAMGT_PART_MAP:
+ case VDAMGT_PART_UNMAP:
+ case VDAMGT_PART_AUTOMAP:
+ case VDAMGT_PART_SPLIT:
+ case VDAMGT_PART_MERGE:
+
+ p = (struct atto_vdapart_info *)data;
+
+ p->part_size = le64_to_cpu(p->part_size);
+ p->start_lba = le32_to_cpu(p->start_lba);
+ p->block_size = le32_to_cpu(p->block_size);
+ p->target_id = le16_to_cpu(p->target_id);
+ break;
+
+ case VDAMGT_DEV_HEALTH_REQ:
+
+ h = (struct atto_vda_dh_info *)data;
+
+ h->med_defect_cnt = le32_to_cpu(h->med_defect_cnt);
+ h->info_exc_cnt = le32_to_cpu(h->info_exc_cnt);
+ break;
+
+ case VDAMGT_DEV_METRICS:
+
+ m = (struct atto_vda_metrics_info *)data;
+
+ for (i = 0; i < 32; i++)
+ m->dev_indexes[i] = le16_to_cpu(m->dev_indexes[i]);
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+void esas2r_nuxi_cfg_data(u8 function, void *data)
+{
+ struct atto_vda_cfg_init *ci;
+
+ switch (function) {
+ case VDA_CFG_INIT:
+ case VDA_CFG_GET_INIT:
+ case VDA_CFG_GET_INIT2:
+
+ ci = (struct atto_vda_cfg_init *)data;
+
+ ci->date_time.year = le16_to_cpu(ci->date_time.year);
+ ci->sgl_page_size = le32_to_cpu(ci->sgl_page_size);
+ ci->vda_version = le32_to_cpu(ci->vda_version);
+ ci->epoch_time = le32_to_cpu(ci->epoch_time);
+ ci->ioctl_tunnel = le32_to_cpu(ci->ioctl_tunnel);
+ ci->num_targets_backend = le32_to_cpu(ci->num_targets_backend);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void esas2r_nuxi_ae_data(union atto_vda_ae *ae)
+{
+ struct atto_vda_ae_raid *r = &ae->raid;
+ struct atto_vda_ae_lu *l = &ae->lu;
+
+ switch (ae->hdr.bytype) {
+ case VDAAE_HDR_TYPE_RAID:
+
+ r->dwflags = le32_to_cpu(r->dwflags);
+ break;
+
+ case VDAAE_HDR_TYPE_LU:
+
+ l->dwevent = le32_to_cpu(l->dwevent);
+ l->wphys_target_id = le16_to_cpu(l->wphys_target_id);
+ l->id.tgtlun.wtarget_id = le16_to_cpu(l->id.tgtlun.wtarget_id);
+
+ if (l->hdr.bylength >= offsetof(struct atto_vda_ae_lu, id)
+ + sizeof(struct atto_vda_ae_lu_tgt_lun_raid)) {
+ l->id.tgtlun_raid.dwinterleave
+ = le32_to_cpu(l->id.tgtlun_raid.dwinterleave);
+ l->id.tgtlun_raid.dwblock_size
+ = le32_to_cpu(l->id.tgtlun_raid.dwblock_size);
+ }
+
+ break;
+
+ case VDAAE_HDR_TYPE_DISK:
+ default:
+ break;
+ }
+}
+
+void esas2r_free_request(struct esas2r_adapter *a, struct esas2r_request *rq)
+{
+ unsigned long flags;
+
+ esas2r_rq_destroy_request(rq, a);
+ spin_lock_irqsave(&a->request_lock, flags);
+ list_add(&rq->comp_list, &a->avail_request);
+ spin_unlock_irqrestore(&a->request_lock, flags);
+}
+
+struct esas2r_request *esas2r_alloc_request(struct esas2r_adapter *a)
+{
+ struct esas2r_request *rq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&a->request_lock, flags);
+
+ if (unlikely(list_empty(&a->avail_request))) {
+ spin_unlock_irqrestore(&a->request_lock, flags);
+ return NULL;
+ }
+
+ rq = list_first_entry(&a->avail_request, struct esas2r_request,
+ comp_list);
+ list_del(&rq->comp_list);
+ spin_unlock_irqrestore(&a->request_lock, flags);
+ esas2r_rq_init_request(rq, a);
+
+ return rq;
+
+}
+
+void esas2r_complete_request_cb(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ esas2r_debug("completing request %p\n", rq);
+
+ scsi_dma_unmap(rq->cmd);
+
+ if (unlikely(rq->req_stat != RS_SUCCESS)) {
+ esas2r_debug("[%x STATUS %x:%x (%x)]", rq->target_id,
+ rq->req_stat,
+ rq->func_rsp.scsi_rsp.scsi_stat,
+ rq->cmd);
+
+ rq->cmd->result =
+ ((esas2r_req_status_to_error(rq->req_stat) << 16)
+ | rq->func_rsp.scsi_rsp.scsi_stat);
+
+ if (rq->req_stat == RS_UNDERRUN)
+ scsi_set_resid(rq->cmd,
+ le32_to_cpu(rq->func_rsp.scsi_rsp.
+ residual_length));
+ else
+ scsi_set_resid(rq->cmd, 0);
+ }
+
+ scsi_done(rq->cmd);
+
+ esas2r_free_request(a, rq);
+}
+
+/* Run tasklet to handle stuff outside of interrupt context. */
+void esas2r_adapter_tasklet(unsigned long context)
+{
+ struct esas2r_adapter *a = (struct esas2r_adapter *)context;
+
+ if (unlikely(test_bit(AF2_TIMER_TICK, &a->flags2))) {
+ clear_bit(AF2_TIMER_TICK, &a->flags2);
+ esas2r_timer_tick(a);
+ }
+
+ if (likely(test_bit(AF2_INT_PENDING, &a->flags2))) {
+ clear_bit(AF2_INT_PENDING, &a->flags2);
+ esas2r_adapter_interrupt(a);
+ }
+
+ if (esas2r_is_tasklet_pending(a))
+ esas2r_do_tasklet_tasks(a);
+
+ if (esas2r_is_tasklet_pending(a)
+ || (test_bit(AF2_INT_PENDING, &a->flags2))
+ || (test_bit(AF2_TIMER_TICK, &a->flags2))) {
+ clear_bit(AF_TASKLET_SCHEDULED, &a->flags);
+ esas2r_schedule_tasklet(a);
+ } else {
+ clear_bit(AF_TASKLET_SCHEDULED, &a->flags);
+ }
+}
+
+static void esas2r_timer_callback(struct timer_list *t);
+
+void esas2r_kickoff_timer(struct esas2r_adapter *a)
+{
+ timer_setup(&a->timer, esas2r_timer_callback, 0);
+
+ a->timer.expires = jiffies +
+ msecs_to_jiffies(100);
+
+ add_timer(&a->timer);
+}
+
+static void esas2r_timer_callback(struct timer_list *t)
+{
+ struct esas2r_adapter *a = timer_container_of(a, t, timer);
+
+ set_bit(AF2_TIMER_TICK, &a->flags2);
+
+ esas2r_schedule_tasklet(a);
+
+ esas2r_kickoff_timer(a);
+}
+
+/*
+ * Firmware events need to be handled outside of interrupt context
+ * so we schedule a delayed_work to handle them.
+ */
+
+static void
+esas2r_free_fw_event(struct esas2r_fw_event_work *fw_event)
+{
+ unsigned long flags;
+ struct esas2r_adapter *a = fw_event->a;
+
+ spin_lock_irqsave(&a->fw_event_lock, flags);
+ list_del(&fw_event->list);
+ kfree(fw_event);
+ spin_unlock_irqrestore(&a->fw_event_lock, flags);
+}
+
+void
+esas2r_fw_event_off(struct esas2r_adapter *a)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&a->fw_event_lock, flags);
+ a->fw_events_off = 1;
+ spin_unlock_irqrestore(&a->fw_event_lock, flags);
+}
+
+void
+esas2r_fw_event_on(struct esas2r_adapter *a)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&a->fw_event_lock, flags);
+ a->fw_events_off = 0;
+ spin_unlock_irqrestore(&a->fw_event_lock, flags);
+}
+
+static void esas2r_add_device(struct esas2r_adapter *a, u16 target_id)
+{
+ int ret;
+ struct scsi_device *scsi_dev;
+
+ scsi_dev = scsi_device_lookup(a->host, 0, target_id, 0);
+
+ if (scsi_dev) {
+ esas2r_log_dev(
+ ESAS2R_LOG_WARN,
+ &(scsi_dev->
+ sdev_gendev),
+ "scsi device already exists at id %d", target_id);
+
+ scsi_device_put(scsi_dev);
+ } else {
+ esas2r_log_dev(
+ ESAS2R_LOG_INFO,
+ &(a->host->
+ shost_gendev),
+ "scsi_add_device() called for 0:%d:0",
+ target_id);
+
+ ret = scsi_add_device(a->host, 0, target_id, 0);
+ if (ret) {
+ esas2r_log_dev(
+ ESAS2R_LOG_CRIT,
+ &(a->host->
+ shost_gendev),
+ "scsi_add_device failed with %d for id %d",
+ ret, target_id);
+ }
+ }
+}
+
+static void esas2r_remove_device(struct esas2r_adapter *a, u16 target_id)
+{
+ struct scsi_device *scsi_dev;
+
+ scsi_dev = scsi_device_lookup(a->host, 0, target_id, 0);
+
+ if (scsi_dev) {
+ scsi_device_set_state(scsi_dev, SDEV_OFFLINE);
+
+ esas2r_log_dev(
+ ESAS2R_LOG_INFO,
+ &(scsi_dev->
+ sdev_gendev),
+ "scsi_remove_device() called for 0:%d:0",
+ target_id);
+
+ scsi_remove_device(scsi_dev);
+
+ esas2r_log_dev(
+ ESAS2R_LOG_INFO,
+ &(scsi_dev->
+ sdev_gendev),
+ "scsi_device_put() called");
+
+ scsi_device_put(scsi_dev);
+ } else {
+ esas2r_log_dev(
+ ESAS2R_LOG_WARN,
+ &(a->host->shost_gendev),
+ "no target found at id %d",
+ target_id);
+ }
+}
+
+/*
+ * Sends a firmware asynchronous event to anyone who happens to be
+ * listening on the defined ATTO VDA event ports.
+ */
+static void esas2r_send_ae_event(struct esas2r_fw_event_work *fw_event)
+{
+ struct esas2r_vda_ae *ae = (struct esas2r_vda_ae *)fw_event->data;
+ char *type;
+
+ switch (ae->vda_ae.hdr.bytype) {
+ case VDAAE_HDR_TYPE_RAID:
+ type = "RAID group state change";
+ break;
+
+ case VDAAE_HDR_TYPE_LU:
+ type = "Mapped destination LU change";
+ break;
+
+ case VDAAE_HDR_TYPE_DISK:
+ type = "Physical disk inventory change";
+ break;
+
+ case VDAAE_HDR_TYPE_RESET:
+ type = "Firmware reset";
+ break;
+
+ case VDAAE_HDR_TYPE_LOG_INFO:
+ type = "Event Log message (INFO level)";
+ break;
+
+ case VDAAE_HDR_TYPE_LOG_WARN:
+ type = "Event Log message (WARN level)";
+ break;
+
+ case VDAAE_HDR_TYPE_LOG_CRIT:
+ type = "Event Log message (CRIT level)";
+ break;
+
+ case VDAAE_HDR_TYPE_LOG_FAIL:
+ type = "Event Log message (FAIL level)";
+ break;
+
+ case VDAAE_HDR_TYPE_NVC:
+ type = "NVCache change";
+ break;
+
+ case VDAAE_HDR_TYPE_TLG_INFO:
+ type = "Time stamped log message (INFO level)";
+ break;
+
+ case VDAAE_HDR_TYPE_TLG_WARN:
+ type = "Time stamped log message (WARN level)";
+ break;
+
+ case VDAAE_HDR_TYPE_TLG_CRIT:
+ type = "Time stamped log message (CRIT level)";
+ break;
+
+ case VDAAE_HDR_TYPE_PWRMGT:
+ type = "Power management";
+ break;
+
+ case VDAAE_HDR_TYPE_MUTE:
+ type = "Mute button pressed";
+ break;
+
+ case VDAAE_HDR_TYPE_DEV:
+ type = "Device attribute change";
+ break;
+
+ default:
+ type = "Unknown";
+ break;
+ }
+
+ esas2r_log(ESAS2R_LOG_WARN,
+ "An async event of type \"%s\" was received from the firmware. The event contents are:",
+ type);
+ esas2r_log_hexdump(ESAS2R_LOG_WARN, &ae->vda_ae,
+ ae->vda_ae.hdr.bylength);
+
+}
+
+static void
+esas2r_firmware_event_work(struct work_struct *work)
+{
+ struct esas2r_fw_event_work *fw_event =
+ container_of(work, struct esas2r_fw_event_work, work.work);
+
+ struct esas2r_adapter *a = fw_event->a;
+
+ u16 target_id = *(u16 *)&fw_event->data[0];
+
+ if (a->fw_events_off)
+ goto done;
+
+ switch (fw_event->type) {
+ case fw_event_null:
+ break; /* do nothing */
+
+ case fw_event_lun_change:
+ esas2r_remove_device(a, target_id);
+ esas2r_add_device(a, target_id);
+ break;
+
+ case fw_event_present:
+ esas2r_add_device(a, target_id);
+ break;
+
+ case fw_event_not_present:
+ esas2r_remove_device(a, target_id);
+ break;
+
+ case fw_event_vda_ae:
+ esas2r_send_ae_event(fw_event);
+ break;
+ }
+
+done:
+ esas2r_free_fw_event(fw_event);
+}
+
+void esas2r_queue_fw_event(struct esas2r_adapter *a,
+ enum fw_event_type type,
+ void *data,
+ int data_sz)
+{
+ struct esas2r_fw_event_work *fw_event;
+ unsigned long flags;
+
+ fw_event = kzalloc(sizeof(struct esas2r_fw_event_work), GFP_ATOMIC);
+ if (!fw_event) {
+ esas2r_log(ESAS2R_LOG_WARN,
+ "esas2r_queue_fw_event failed to alloc");
+ return;
+ }
+
+ if (type == fw_event_vda_ae) {
+ struct esas2r_vda_ae *ae =
+ (struct esas2r_vda_ae *)fw_event->data;
+
+ ae->signature = ESAS2R_VDA_EVENT_SIG;
+ ae->bus_number = a->pcid->bus->number;
+ ae->devfn = a->pcid->devfn;
+ memcpy(&ae->vda_ae, data, sizeof(ae->vda_ae));
+ } else {
+ memcpy(fw_event->data, data, data_sz);
+ }
+
+ fw_event->type = type;
+ fw_event->a = a;
+
+ spin_lock_irqsave(&a->fw_event_lock, flags);
+ list_add_tail(&fw_event->list, &a->fw_event_list);
+ INIT_DELAYED_WORK(&fw_event->work, esas2r_firmware_event_work);
+ queue_delayed_work_on(
+ smp_processor_id(), a->fw_event_q, &fw_event->work,
+ msecs_to_jiffies(1));
+ spin_unlock_irqrestore(&a->fw_event_lock, flags);
+}
+
+void esas2r_target_state_changed(struct esas2r_adapter *a, u16 targ_id,
+ u8 state)
+{
+ if (state == TS_LUN_CHANGE)
+ esas2r_queue_fw_event(a, fw_event_lun_change, &targ_id,
+ sizeof(targ_id));
+ else if (state == TS_PRESENT)
+ esas2r_queue_fw_event(a, fw_event_present, &targ_id,
+ sizeof(targ_id));
+ else if (state == TS_NOT_PRESENT)
+ esas2r_queue_fw_event(a, fw_event_not_present, &targ_id,
+ sizeof(targ_id));
+}
+
+/* Translate status to a Linux SCSI mid-layer error code */
+int esas2r_req_status_to_error(u8 req_stat)
+{
+ switch (req_stat) {
+ case RS_OVERRUN:
+ case RS_UNDERRUN:
+ case RS_SUCCESS:
+ /*
+ * NOTE: SCSI mid-layer wants a good status for a SCSI error, because
+ * it will check the scsi_stat value in the completion anyway.
+ */
+ case RS_SCSI_ERROR:
+ return DID_OK;
+
+ case RS_SEL:
+ case RS_SEL2:
+ return DID_NO_CONNECT;
+
+ case RS_RESET:
+ return DID_RESET;
+
+ case RS_ABORTED:
+ return DID_ABORT;
+
+ case RS_BUSY:
+ return DID_BUS_BUSY;
+ }
+
+ /* everything else is just an error. */
+
+ return DID_ERROR;
+}
+
+module_init(esas2r_init);
+module_exit(esas2r_exit);
diff --git a/drivers/scsi/esas2r/esas2r_targdb.c b/drivers/scsi/esas2r/esas2r_targdb.c
new file mode 100644
index 000000000000..bf45beaad439
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_targdb.c
@@ -0,0 +1,306 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_targdb.c
+ * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include "esas2r.h"
+
+void esas2r_targ_db_initialize(struct esas2r_adapter *a)
+{
+ struct esas2r_target *t;
+
+ for (t = a->targetdb; t < a->targetdb_end; t++) {
+ memset(t, 0, sizeof(struct esas2r_target));
+
+ t->target_state = TS_NOT_PRESENT;
+ t->buffered_target_state = TS_NOT_PRESENT;
+ t->new_target_state = TS_INVALID;
+ }
+}
+
+void esas2r_targ_db_remove_all(struct esas2r_adapter *a, bool notify)
+{
+ struct esas2r_target *t;
+ unsigned long flags;
+
+ for (t = a->targetdb; t < a->targetdb_end; t++) {
+ if (t->target_state != TS_PRESENT)
+ continue;
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+ esas2r_targ_db_remove(a, t);
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ if (notify) {
+ esas2r_trace("remove id:%d", esas2r_targ_get_id(t,
+ a));
+ esas2r_target_state_changed(a, esas2r_targ_get_id(t,
+ a),
+ TS_NOT_PRESENT);
+ }
+ }
+}
+
+void esas2r_targ_db_report_changes(struct esas2r_adapter *a)
+{
+ struct esas2r_target *t;
+ unsigned long flags;
+
+ esas2r_trace_enter();
+
+ if (test_bit(AF_DISC_PENDING, &a->flags)) {
+ esas2r_trace_exit();
+ return;
+ }
+
+ for (t = a->targetdb; t < a->targetdb_end; t++) {
+ u8 state = TS_INVALID;
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+ if (t->buffered_target_state != t->target_state)
+ state = t->buffered_target_state = t->target_state;
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+ if (state != TS_INVALID) {
+ esas2r_trace("targ_db_report_changes:%d",
+ esas2r_targ_get_id(
+ t,
+ a));
+ esas2r_trace("state:%d", state);
+
+ esas2r_target_state_changed(a,
+ esas2r_targ_get_id(t,
+ a),
+ state);
+ }
+ }
+
+ esas2r_trace_exit();
+}
+
+struct esas2r_target *esas2r_targ_db_add_raid(struct esas2r_adapter *a,
+ struct esas2r_disc_context *
+ dc)
+{
+ struct esas2r_target *t;
+
+ esas2r_trace_enter();
+
+ if (dc->curr_virt_id >= ESAS2R_MAX_TARGETS) {
+ esas2r_bugon();
+ esas2r_trace_exit();
+ return NULL;
+ }
+
+ t = a->targetdb + dc->curr_virt_id;
+
+ if (t->target_state == TS_PRESENT) {
+ esas2r_trace_exit();
+ return NULL;
+ }
+
+ esas2r_hdebug("add RAID %s, T:%d", dc->raid_grp_name,
+ esas2r_targ_get_id(
+ t,
+ a));
+
+ if (dc->interleave == 0
+ || dc->block_size == 0) {
+ /* these are invalid values, don't create the target entry. */
+
+ esas2r_hdebug("invalid RAID group dimensions");
+
+ esas2r_trace_exit();
+
+ return NULL;
+ }
+
+ t->block_size = dc->block_size;
+ t->inter_byte = dc->interleave;
+ t->inter_block = dc->interleave / dc->block_size;
+ t->virt_targ_id = dc->curr_virt_id;
+ t->phys_targ_id = ESAS2R_TARG_ID_INV;
+
+ t->flags &= ~TF_PASS_THRU;
+ t->flags |= TF_USED;
+
+ t->identifier_len = 0;
+
+ t->target_state = TS_PRESENT;
+
+ return t;
+}
+
+struct esas2r_target *esas2r_targ_db_add_pthru(struct esas2r_adapter *a,
+ struct esas2r_disc_context *dc,
+ u8 *ident,
+ u8 ident_len)
+{
+ struct esas2r_target *t;
+
+ esas2r_trace_enter();
+
+ if (dc->curr_virt_id >= ESAS2R_MAX_TARGETS) {
+ esas2r_bugon();
+ esas2r_trace_exit();
+ return NULL;
+ }
+
+ /* see if we found this device before. */
+
+ t = esas2r_targ_db_find_by_ident(a, ident, ident_len);
+
+ if (t == NULL) {
+ t = a->targetdb + dc->curr_virt_id;
+
+ if (ident_len > sizeof(t->identifier)
+ || t->target_state == TS_PRESENT) {
+ esas2r_trace_exit();
+ return NULL;
+ }
+ }
+
+ esas2r_hdebug("add PT; T:%d, V:%d, P:%d", esas2r_targ_get_id(t, a),
+ dc->curr_virt_id,
+ dc->curr_phys_id);
+
+ t->block_size = 0;
+ t->inter_byte = 0;
+ t->inter_block = 0;
+ t->virt_targ_id = dc->curr_virt_id;
+ t->phys_targ_id = dc->curr_phys_id;
+ t->identifier_len = ident_len;
+
+ memcpy(t->identifier, ident, ident_len);
+
+ t->flags |= TF_PASS_THRU | TF_USED;
+
+ t->target_state = TS_PRESENT;
+
+ return t;
+}
+
+void esas2r_targ_db_remove(struct esas2r_adapter *a, struct esas2r_target *t)
+{
+ esas2r_trace_enter();
+
+ t->target_state = TS_NOT_PRESENT;
+
+ esas2r_trace("remove id:%d", esas2r_targ_get_id(t, a));
+
+ esas2r_trace_exit();
+}
+
+struct esas2r_target *esas2r_targ_db_find_by_sas_addr(struct esas2r_adapter *a,
+ u64 *sas_addr)
+{
+ struct esas2r_target *t;
+
+ for (t = a->targetdb; t < a->targetdb_end; t++)
+ if (t->sas_addr == *sas_addr)
+ return t;
+
+ return NULL;
+}
+
+struct esas2r_target *esas2r_targ_db_find_by_ident(struct esas2r_adapter *a,
+ void *identifier,
+ u8 ident_len)
+{
+ struct esas2r_target *t;
+
+ for (t = a->targetdb; t < a->targetdb_end; t++) {
+ if (ident_len == t->identifier_len
+ && memcmp(&t->identifier[0], identifier,
+ ident_len) == 0)
+ return t;
+ }
+
+ return NULL;
+}
+
+u16 esas2r_targ_db_find_next_present(struct esas2r_adapter *a, u16 target_id)
+{
+ u16 id = target_id + 1;
+
+ while (id < ESAS2R_MAX_TARGETS) {
+ struct esas2r_target *t = a->targetdb + id;
+
+ if (t->target_state == TS_PRESENT)
+ break;
+
+ id++;
+ }
+
+ return id;
+}
+
+struct esas2r_target *esas2r_targ_db_find_by_virt_id(struct esas2r_adapter *a,
+ u16 virt_id)
+{
+ struct esas2r_target *t;
+
+ for (t = a->targetdb; t < a->targetdb_end; t++) {
+ if (t->target_state != TS_PRESENT)
+ continue;
+
+ if (t->virt_targ_id == virt_id)
+ return t;
+ }
+
+ return NULL;
+}
+
+u16 esas2r_targ_db_get_tgt_cnt(struct esas2r_adapter *a)
+{
+ u16 devcnt = 0;
+ struct esas2r_target *t;
+ unsigned long flags;
+
+ spin_lock_irqsave(&a->mem_lock, flags);
+ for (t = a->targetdb; t < a->targetdb_end; t++)
+ if (t->target_state == TS_PRESENT)
+ devcnt++;
+
+ spin_unlock_irqrestore(&a->mem_lock, flags);
+
+ return devcnt;
+}
diff --git a/drivers/scsi/esas2r/esas2r_vda.c b/drivers/scsi/esas2r/esas2r_vda.c
new file mode 100644
index 000000000000..5aa728704dfc
--- /dev/null
+++ b/drivers/scsi/esas2r/esas2r_vda.c
@@ -0,0 +1,507 @@
+/*
+ * linux/drivers/scsi/esas2r/esas2r_vda.c
+ * esas2r driver VDA firmware interface functions
+ *
+ * Copyright (c) 2001-2013 ATTO Technology, Inc.
+ * (mailto:linuxdrivers@attotech.com)
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include "esas2r.h"
+
+static u8 esas2r_vdaioctl_versions[] = {
+ ATTO_VDA_VER_UNSUPPORTED,
+ ATTO_VDA_FLASH_VER,
+ ATTO_VDA_VER_UNSUPPORTED,
+ ATTO_VDA_VER_UNSUPPORTED,
+ ATTO_VDA_CLI_VER,
+ ATTO_VDA_VER_UNSUPPORTED,
+ ATTO_VDA_CFG_VER,
+ ATTO_VDA_MGT_VER,
+ ATTO_VDA_GSV_VER
+};
+
+static void clear_vda_request(struct esas2r_request *rq);
+
+static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
+ struct esas2r_request *rq);
+
+/* Prepare a VDA IOCTL request to be sent to the firmware. */
+bool esas2r_process_vda_ioctl(struct esas2r_adapter *a,
+ struct atto_ioctl_vda *vi,
+ struct esas2r_request *rq,
+ struct esas2r_sg_context *sgc)
+{
+ u32 datalen = 0;
+ struct atto_vda_sge *firstsg = NULL;
+ u8 vercnt = (u8)ARRAY_SIZE(esas2r_vdaioctl_versions);
+
+ vi->status = ATTO_STS_SUCCESS;
+ vi->vda_status = RS_PENDING;
+
+ if (vi->function >= vercnt) {
+ vi->status = ATTO_STS_INV_FUNC;
+ return false;
+ }
+
+ if (vi->version > esas2r_vdaioctl_versions[vi->function]) {
+ vi->status = ATTO_STS_INV_VERSION;
+ return false;
+ }
+
+ if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
+ vi->status = ATTO_STS_DEGRADED;
+ return false;
+ }
+
+ if (vi->function != VDA_FUNC_SCSI)
+ clear_vda_request(rq);
+
+ rq->vrq->scsi.function = vi->function;
+ rq->interrupt_cb = esas2r_complete_vda_ioctl;
+ rq->interrupt_cx = vi;
+
+ switch (vi->function) {
+ case VDA_FUNC_FLASH:
+
+ if (vi->cmd.flash.sub_func != VDA_FLASH_FREAD
+ && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE
+ && vi->cmd.flash.sub_func != VDA_FLASH_FINFO) {
+ vi->status = ATTO_STS_INV_FUNC;
+ return false;
+ }
+
+ if (vi->cmd.flash.sub_func != VDA_FLASH_FINFO)
+ datalen = vi->data_length;
+
+ rq->vrq->flash.length = cpu_to_le32(datalen);
+ rq->vrq->flash.sub_func = vi->cmd.flash.sub_func;
+
+ memcpy(rq->vrq->flash.data.file.file_name,
+ vi->cmd.flash.data.file.file_name,
+ sizeof(vi->cmd.flash.data.file.file_name));
+
+ firstsg = rq->vrq->flash.data.file.sge;
+ break;
+
+ case VDA_FUNC_CLI:
+
+ datalen = vi->data_length;
+
+ rq->vrq->cli.cmd_rsp_len =
+ cpu_to_le32(vi->cmd.cli.cmd_rsp_len);
+ rq->vrq->cli.length = cpu_to_le32(datalen);
+
+ firstsg = rq->vrq->cli.sge;
+ break;
+
+ case VDA_FUNC_MGT:
+ {
+ u8 *cmdcurr_offset = sgc->cur_offset
+ - offsetof(struct atto_ioctl_vda, data)
+ + offsetof(struct atto_ioctl_vda, cmd)
+ + offsetof(struct atto_ioctl_vda_mgt_cmd,
+ data);
+ /*
+ * build the data payload SGL here first since
+ * esas2r_sgc_init() will modify the S/G list offset for the
+ * management SGL (which is built below where the data SGL is
+ * usually built).
+ */
+
+ if (vi->data_length) {
+ u32 payldlen = 0;
+
+ if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ
+ || vi->cmd.mgt.mgt_func == VDAMGT_DEV_METRICS) {
+ rq->vrq->mgt.payld_sglst_offset =
+ (u8)offsetof(struct atto_vda_mgmt_req,
+ payld_sge);
+
+ payldlen = vi->data_length;
+ datalen = vi->cmd.mgt.data_length;
+ } else if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_INFO2
+ || vi->cmd.mgt.mgt_func ==
+ VDAMGT_DEV_INFO2_BYADDR) {
+ datalen = vi->data_length;
+ cmdcurr_offset = sgc->cur_offset;
+ } else {
+ vi->status = ATTO_STS_INV_PARAM;
+ return false;
+ }
+
+ /* Setup the length so building the payload SGL works */
+ rq->vrq->mgt.length = cpu_to_le32(datalen);
+
+ if (payldlen) {
+ rq->vrq->mgt.payld_length =
+ cpu_to_le32(payldlen);
+
+ esas2r_sgc_init(sgc, a, rq,
+ rq->vrq->mgt.payld_sge);
+ sgc->length = payldlen;
+
+ if (!esas2r_build_sg_list(a, rq, sgc)) {
+ vi->status = ATTO_STS_OUT_OF_RSRC;
+ return false;
+ }
+ }
+ } else {
+ datalen = vi->cmd.mgt.data_length;
+
+ rq->vrq->mgt.length = cpu_to_le32(datalen);
+ }
+
+ /*
+ * Now that the payload SGL is built, if any, setup to build
+ * the management SGL.
+ */
+ firstsg = rq->vrq->mgt.sge;
+ sgc->cur_offset = cmdcurr_offset;
+
+ /* Finish initializing the management request. */
+ rq->vrq->mgt.mgt_func = vi->cmd.mgt.mgt_func;
+ rq->vrq->mgt.scan_generation = vi->cmd.mgt.scan_generation;
+ rq->vrq->mgt.dev_index =
+ cpu_to_le32(vi->cmd.mgt.dev_index);
+
+ esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
+ break;
+ }
+
+ case VDA_FUNC_CFG:
+
+ if (vi->data_length
+ || vi->cmd.cfg.data_length == 0) {
+ vi->status = ATTO_STS_INV_PARAM;
+ return false;
+ }
+
+ if (vi->cmd.cfg.cfg_func == VDA_CFG_INIT) {
+ vi->status = ATTO_STS_INV_FUNC;
+ return false;
+ }
+
+ rq->vrq->cfg.sub_func = vi->cmd.cfg.cfg_func;
+ rq->vrq->cfg.length = cpu_to_le32(vi->cmd.cfg.data_length);
+
+ if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
+ memcpy(&rq->vrq->cfg.data,
+ &vi->cmd.cfg.data,
+ vi->cmd.cfg.data_length);
+
+ esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
+ &rq->vrq->cfg.data);
+ } else {
+ vi->status = ATTO_STS_INV_FUNC;
+
+ return false;
+ }
+
+ break;
+
+ case VDA_FUNC_GSV:
+
+ vi->cmd.gsv.rsp_len = vercnt;
+
+ memcpy(vi->cmd.gsv.version_info, esas2r_vdaioctl_versions,
+ vercnt);
+
+ vi->vda_status = RS_SUCCESS;
+ break;
+
+ default:
+
+ vi->status = ATTO_STS_INV_FUNC;
+ return false;
+ }
+
+ if (datalen) {
+ esas2r_sgc_init(sgc, a, rq, firstsg);
+ sgc->length = datalen;
+
+ if (!esas2r_build_sg_list(a, rq, sgc)) {
+ vi->status = ATTO_STS_OUT_OF_RSRC;
+ return false;
+ }
+ }
+
+ esas2r_start_request(a, rq);
+
+ return true;
+}
+
+static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
+ struct esas2r_request *rq)
+{
+ struct atto_ioctl_vda *vi = (struct atto_ioctl_vda *)rq->interrupt_cx;
+
+ vi->vda_status = rq->req_stat;
+
+ switch (vi->function) {
+ case VDA_FUNC_FLASH:
+
+ if (vi->cmd.flash.sub_func == VDA_FLASH_FINFO
+ || vi->cmd.flash.sub_func == VDA_FLASH_FREAD)
+ vi->cmd.flash.data.file.file_size =
+ le32_to_cpu(rq->func_rsp.flash_rsp.file_size);
+
+ break;
+
+ case VDA_FUNC_MGT:
+
+ vi->cmd.mgt.scan_generation =
+ rq->func_rsp.mgt_rsp.scan_generation;
+ vi->cmd.mgt.dev_index = le16_to_cpu(
+ rq->func_rsp.mgt_rsp.dev_index);
+
+ if (vi->data_length == 0)
+ vi->cmd.mgt.data_length =
+ le32_to_cpu(rq->func_rsp.mgt_rsp.length);
+
+ esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
+ break;
+
+ case VDA_FUNC_CFG:
+
+ if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
+ struct atto_ioctl_vda_cfg_cmd *cfg = &vi->cmd.cfg;
+ struct atto_vda_cfg_rsp *rsp = &rq->func_rsp.cfg_rsp;
+ char buf[sizeof(cfg->data.init.fw_release) + 1];
+
+ cfg->data_length =
+ cpu_to_le32(sizeof(struct atto_vda_cfg_init));
+ cfg->data.init.vda_version =
+ le32_to_cpu(rsp->vda_version);
+ cfg->data.init.fw_build = rsp->fw_build;
+
+ snprintf(buf, sizeof(buf), "%1.1u.%2.2u",
+ (int)LOBYTE(le16_to_cpu(rsp->fw_release)),
+ (int)HIBYTE(le16_to_cpu(rsp->fw_release)));
+
+ memcpy(&cfg->data.init.fw_release, buf,
+ sizeof(cfg->data.init.fw_release));
+
+ if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')
+ cfg->data.init.fw_version =
+ cfg->data.init.fw_build;
+ else
+ cfg->data.init.fw_version =
+ cfg->data.init.fw_release;
+ } else {
+ esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
+ &vi->cmd.cfg.data);
+ }
+
+ break;
+
+ case VDA_FUNC_CLI:
+
+ vi->cmd.cli.cmd_rsp_len =
+ le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);
+ break;
+
+ default:
+
+ break;
+ }
+}
+
+/* Build a flash VDA request. */
+void esas2r_build_flash_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u8 sub_func,
+ u8 cksum,
+ u32 addr,
+ u32 length)
+{
+ struct atto_vda_flash_req *vrq = &rq->vrq->flash;
+
+ clear_vda_request(rq);
+
+ rq->vrq->scsi.function = VDA_FUNC_FLASH;
+
+ if (sub_func == VDA_FLASH_BEGINW
+ || sub_func == VDA_FLASH_WRITE
+ || sub_func == VDA_FLASH_READ)
+ vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,
+ data.sge);
+
+ vrq->length = cpu_to_le32(length);
+ vrq->flash_addr = cpu_to_le32(addr);
+ vrq->checksum = cksum;
+ vrq->sub_func = sub_func;
+}
+
+/* Build a VDA management request. */
+void esas2r_build_mgt_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u8 sub_func,
+ u8 scan_gen,
+ u16 dev_index,
+ u32 length,
+ void *data)
+{
+ struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;
+
+ clear_vda_request(rq);
+
+ rq->vrq->scsi.function = VDA_FUNC_MGT;
+
+ vrq->mgt_func = sub_func;
+ vrq->scan_generation = scan_gen;
+ vrq->dev_index = cpu_to_le16(dev_index);
+ vrq->length = cpu_to_le32(length);
+
+ if (vrq->length) {
+ if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
+ vrq->sg_list_offset = (u8)offsetof(
+ struct atto_vda_mgmt_req, sge);
+
+ vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);
+ vrq->sge[0].address = cpu_to_le64(
+ rq->vrq_md->phys_addr +
+ sizeof(union atto_vda_req));
+ } else {
+ vrq->sg_list_offset = (u8)offsetof(
+ struct atto_vda_mgmt_req, prde);
+
+ vrq->prde[0].ctl_len = cpu_to_le32(length);
+ vrq->prde[0].address = cpu_to_le64(
+ rq->vrq_md->phys_addr +
+ sizeof(union atto_vda_req));
+ }
+ }
+
+ if (data) {
+ esas2r_nuxi_mgt_data(sub_func, data);
+
+ memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,
+ length);
+ }
+}
+
+/* Build a VDA asyncronous event (AE) request. */
+void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)
+{
+ struct atto_vda_ae_req *vrq = &rq->vrq->ae;
+
+ clear_vda_request(rq);
+
+ rq->vrq->scsi.function = VDA_FUNC_AE;
+
+ vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));
+
+ if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
+ vrq->sg_list_offset =
+ (u8)offsetof(struct atto_vda_ae_req, sge);
+ vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);
+ vrq->sge[0].address = cpu_to_le64(
+ rq->vrq_md->phys_addr +
+ sizeof(union atto_vda_req));
+ } else {
+ vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,
+ prde);
+ vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);
+ vrq->prde[0].address = cpu_to_le64(
+ rq->vrq_md->phys_addr +
+ sizeof(union atto_vda_req));
+ }
+}
+
+/* Build a VDA IOCTL request. */
+void esas2r_build_ioctl_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u32 length,
+ u8 sub_func)
+{
+ struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;
+
+ clear_vda_request(rq);
+
+ rq->vrq->scsi.function = VDA_FUNC_IOCTL;
+
+ vrq->length = cpu_to_le32(length);
+ vrq->sub_func = sub_func;
+ vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);
+}
+
+/* Build a VDA configuration request. */
+void esas2r_build_cfg_req(struct esas2r_adapter *a,
+ struct esas2r_request *rq,
+ u8 sub_func,
+ u32 length,
+ void *data)
+{
+ struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;
+
+ clear_vda_request(rq);
+
+ rq->vrq->scsi.function = VDA_FUNC_CFG;
+
+ vrq->sub_func = sub_func;
+ vrq->length = cpu_to_le32(length);
+
+ if (data) {
+ esas2r_nuxi_cfg_data(sub_func, data);
+
+ memcpy(&vrq->data, data, length);
+ }
+}
+
+static void clear_vda_request(struct esas2r_request *rq)
+{
+ u32 handle = rq->vrq->scsi.handle;
+
+ memset(rq->vrq, 0, sizeof(*rq->vrq));
+
+ rq->vrq->scsi.handle = handle;
+
+ rq->req_stat = RS_PENDING;
+
+ /* since the data buffer is separate clear that too */
+
+ memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);
+
+ /*
+ * Setup next and prev pointer in case the request is not going through
+ * esas2r_start_request().
+ */
+
+ INIT_LIST_HEAD(&rq->req_list);
+}
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index 394ed9e79fd4..802718ffad84 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* esp_scsi.c: ESP SCSI driver.
*
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
@@ -49,55 +50,67 @@ static u32 esp_debug;
#define ESP_DEBUG_DATADONE 0x00000100
#define ESP_DEBUG_RECONNECT 0x00000200
#define ESP_DEBUG_AUTOSENSE 0x00000400
+#define ESP_DEBUG_EVENT 0x00000800
+#define ESP_DEBUG_COMMAND 0x00001000
#define esp_log_intr(f, a...) \
do { if (esp_debug & ESP_DEBUG_INTR) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_reset(f, a...) \
do { if (esp_debug & ESP_DEBUG_RESET) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_msgin(f, a...) \
do { if (esp_debug & ESP_DEBUG_MSGIN) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_msgout(f, a...) \
do { if (esp_debug & ESP_DEBUG_MSGOUT) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_cmddone(f, a...) \
do { if (esp_debug & ESP_DEBUG_CMDDONE) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_disconnect(f, a...) \
do { if (esp_debug & ESP_DEBUG_DISCONNECT) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_datastart(f, a...) \
do { if (esp_debug & ESP_DEBUG_DATASTART) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_datadone(f, a...) \
do { if (esp_debug & ESP_DEBUG_DATADONE) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_reconnect(f, a...) \
do { if (esp_debug & ESP_DEBUG_RECONNECT) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_autosense(f, a...) \
do { if (esp_debug & ESP_DEBUG_AUTOSENSE) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
+} while (0)
+
+#define esp_log_event(f, a...) \
+do { if (esp_debug & ESP_DEBUG_EVENT) \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
+} while (0)
+
+#define esp_log_command(f, a...) \
+do { if (esp_debug & ESP_DEBUG_COMMAND) \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_read8(REG) esp->ops->esp_read8(esp, REG)
@@ -126,10 +139,29 @@ void scsi_esp_cmd(struct esp *esp, u8 val)
esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+ esp_log_command("cmd[%02x]\n", val);
esp_write8(val, ESP_CMD);
}
EXPORT_SYMBOL(scsi_esp_cmd);
+static void esp_send_dma_cmd(struct esp *esp, int len, int max_len, int cmd)
+{
+ if (esp->flags & ESP_FLAG_USE_FIFO) {
+ int i;
+
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ for (i = 0; i < len; i++)
+ esp_write8(esp->command_block[i], ESP_FDATA);
+ scsi_esp_cmd(esp, cmd);
+ } else {
+ if (esp->rev == FASHME)
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ cmd |= ESP_CMD_DMA;
+ esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+ len, max_len, 0, cmd);
+ }
+}
+
static void esp_event(struct esp *esp, u8 val)
{
struct esp_event_ent *p;
@@ -150,19 +182,17 @@ static void esp_dump_cmd_log(struct esp *esp)
int idx = esp->esp_event_cur;
int stop = idx;
- printk(KERN_INFO PFX "esp%d: Dumping command log\n",
- esp->host->unique_id);
+ shost_printk(KERN_INFO, esp->host, "Dumping command log\n");
do {
struct esp_event_ent *p = &esp->esp_event_log[idx];
- printk(KERN_INFO PFX "esp%d: ent[%d] %s ",
- esp->host->unique_id, idx,
- p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT");
-
- printk("val[%02x] sreg[%02x] seqreg[%02x] "
- "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n",
- p->val, p->sreg, p->seqreg,
- p->sreg2, p->ireg, p->select_state, p->event);
+ shost_printk(KERN_INFO, esp->host,
+ "ent[%d] %s val[%02x] sreg[%02x] seqreg[%02x] "
+ "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n",
+ idx,
+ p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT",
+ p->val, p->sreg, p->seqreg,
+ p->sreg2, p->ireg, p->select_state, p->event);
idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
} while (idx != stop);
@@ -176,9 +206,8 @@ static void esp_flush_fifo(struct esp *esp)
while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) {
if (--lim == 0) {
- printk(KERN_ALERT PFX "esp%d: ESP_FF_BYTES "
- "will not clear!\n",
- esp->host->unique_id);
+ shost_printk(KERN_ALERT, esp->host,
+ "ESP_FF_BYTES will not clear!\n");
break;
}
udelay(1);
@@ -214,8 +243,6 @@ static void esp_set_all_config3(struct esp *esp, u8 val)
/* Reset the ESP chip, _not_ the SCSI bus. */
static void esp_reset_esp(struct esp *esp)
{
- u8 family_code, version;
-
/* Now reset the ESP chip */
scsi_esp_cmd(esp, ESP_CMD_RC);
scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
@@ -228,18 +255,36 @@ static void esp_reset_esp(struct esp *esp)
*/
esp->max_period = ((35 * esp->ccycle) / 1000);
if (esp->rev == FAST) {
- version = esp_read8(ESP_UID);
- family_code = (version & 0xf8) >> 3;
- if (family_code == 0x02)
+ u8 family_code = ESP_FAMILY(esp_read8(ESP_UID));
+
+ if (family_code == ESP_UID_F236) {
esp->rev = FAS236;
- else if (family_code == 0x0a)
+ } else if (family_code == ESP_UID_HME) {
esp->rev = FASHME; /* Version is usually '5'. */
- else
+ } else if (family_code == ESP_UID_FSC) {
+ esp->rev = FSC;
+ /* Enable Active Negation */
+ esp_write8(ESP_CONFIG4_RADE, ESP_CFG4);
+ } else {
esp->rev = FAS100A;
+ }
esp->min_period = ((4 * esp->ccycle) / 1000);
} else {
esp->min_period = ((5 * esp->ccycle) / 1000);
}
+ if (esp->rev == FAS236) {
+ /*
+ * The AM53c974 chip returns the same ID as FAS236;
+ * try to configure glitch eater.
+ */
+ u8 config4 = ESP_CONFIG4_GE1;
+ esp_write8(config4, ESP_CFG4);
+ config4 = esp_read8(ESP_CFG4);
+ if (config4 & ESP_CONFIG4_GE1) {
+ esp->rev = PCSCSI;
+ esp_write8(esp->config4, ESP_CFG4);
+ }
+ }
esp->max_period = (esp->max_period + 3)>>2;
esp->min_period = (esp->min_period + 3)>>2;
@@ -262,10 +307,11 @@ static void esp_reset_esp(struct esp *esp)
case FASHME:
esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
- /* fallthrough... */
+ fallthrough;
case FAS236:
- /* Fast 236 or HME */
+ case PCSCSI:
+ case FSC:
esp_write8(esp->config2, ESP_CFG2);
if (esp->rev == FASHME) {
u8 cfg3 = esp->target[0].esp_config3;
@@ -327,19 +373,31 @@ static void esp_map_dma(struct esp *esp, struct scsi_cmnd *cmd)
{
struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
struct scatterlist *sg = scsi_sglist(cmd);
- int dir = cmd->sc_data_direction;
- int total, i;
+ int total = 0, i;
+ struct scatterlist *s;
- if (dir == DMA_NONE)
+ if (cmd->sc_data_direction == DMA_NONE)
return;
- spriv->u.num_sg = esp->ops->map_sg(esp, sg, scsi_sg_count(cmd), dir);
+ if (esp->flags & ESP_FLAG_NO_DMA_MAP) {
+ /*
+ * For pseudo DMA and PIO we need the virtual address instead of
+ * a dma address, so perform an identity mapping.
+ */
+ spriv->num_sg = scsi_sg_count(cmd);
+
+ scsi_for_each_sg(cmd, s, spriv->num_sg, i) {
+ s->dma_address = (uintptr_t)sg_virt(s);
+ total += sg_dma_len(s);
+ }
+ } else {
+ spriv->num_sg = scsi_dma_map(cmd);
+ scsi_for_each_sg(cmd, s, spriv->num_sg, i)
+ total += sg_dma_len(s);
+ }
spriv->cur_residue = sg_dma_len(sg);
+ spriv->prv_sg = NULL;
spriv->cur_sg = sg;
-
- total = 0;
- for (i = 0; i < spriv->u.num_sg; i++)
- total += sg_dma_len(&sg[i]);
spriv->tot_residue = total;
}
@@ -383,30 +441,25 @@ static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent,
p->cur_residue -= len;
p->tot_residue -= len;
if (p->cur_residue < 0 || p->tot_residue < 0) {
- printk(KERN_ERR PFX "esp%d: Data transfer overflow.\n",
- esp->host->unique_id);
- printk(KERN_ERR PFX "esp%d: cur_residue[%d] tot_residue[%d] "
- "len[%u]\n",
- esp->host->unique_id,
- p->cur_residue, p->tot_residue, len);
+ shost_printk(KERN_ERR, esp->host,
+ "Data transfer overflow.\n");
+ shost_printk(KERN_ERR, esp->host,
+ "cur_residue[%d] tot_residue[%d] len[%u]\n",
+ p->cur_residue, p->tot_residue, len);
p->cur_residue = 0;
p->tot_residue = 0;
}
if (!p->cur_residue && p->tot_residue) {
- p->cur_sg++;
+ p->prv_sg = p->cur_sg;
+ p->cur_sg = sg_next(p->cur_sg);
p->cur_residue = sg_dma_len(p->cur_sg);
}
}
static void esp_unmap_dma(struct esp *esp, struct scsi_cmnd *cmd)
{
- struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
- int dir = cmd->sc_data_direction;
-
- if (dir == DMA_NONE)
- return;
-
- esp->ops->unmap_sg(esp, scsi_sglist(cmd), spriv->u.num_sg, dir);
+ if (!(esp->flags & ESP_FLAG_NO_DMA_MAP))
+ scsi_dma_unmap(cmd);
}
static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent)
@@ -419,6 +472,7 @@ static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent)
return;
}
ent->saved_cur_residue = spriv->cur_residue;
+ ent->saved_prv_sg = spriv->prv_sg;
ent->saved_cur_sg = spriv->cur_sg;
ent->saved_tot_residue = spriv->tot_residue;
}
@@ -433,21 +487,11 @@ static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent)
return;
}
spriv->cur_residue = ent->saved_cur_residue;
+ spriv->prv_sg = ent->saved_prv_sg;
spriv->cur_sg = ent->saved_cur_sg;
spriv->tot_residue = ent->saved_tot_residue;
}
-static void esp_check_command_len(struct esp *esp, struct scsi_cmnd *cmd)
-{
- if (cmd->cmd_len == 6 ||
- cmd->cmd_len == 10 ||
- cmd->cmd_len == 12) {
- esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
- } else {
- esp->flags |= ESP_FLAG_DOING_SLOWCMD;
- }
-}
-
static void esp_write_tgt_config3(struct esp *esp, int tgt)
{
if (esp->rev > ESP100A) {
@@ -530,7 +574,7 @@ static int esp_need_to_nego_sync(struct esp_target_data *tp)
static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
struct esp_lun_data *lp)
{
- if (!ent->tag[0]) {
+ if (!ent->orig_tag[0]) {
/* Non-tagged, slot already taken? */
if (lp->non_tagged_cmd)
return -EBUSY;
@@ -556,17 +600,15 @@ static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
lp->non_tagged_cmd = ent;
return 0;
- } else {
- /* Tagged command, see if blocked by a
- * non-tagged one.
- */
- if (lp->non_tagged_cmd || lp->hold)
- return -EBUSY;
}
- BUG_ON(lp->tagged_cmds[ent->tag[1]]);
+ /* Tagged command. Check that it isn't blocked by a non-tagged one. */
+ if (lp->non_tagged_cmd || lp->hold)
+ return -EBUSY;
- lp->tagged_cmds[ent->tag[1]] = ent;
+ BUG_ON(lp->tagged_cmds[ent->orig_tag[1]]);
+
+ lp->tagged_cmds[ent->orig_tag[1]] = ent;
lp->num_tagged++;
return 0;
@@ -575,9 +617,9 @@ static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
static void esp_free_lun_tag(struct esp_cmd_entry *ent,
struct esp_lun_data *lp)
{
- if (ent->tag[0]) {
- BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent);
- lp->tagged_cmds[ent->tag[1]] = NULL;
+ if (ent->orig_tag[0]) {
+ BUG_ON(lp->tagged_cmds[ent->orig_tag[1]] != ent);
+ lp->tagged_cmds[ent->orig_tag[1]] = NULL;
lp->num_tagged--;
} else {
BUG_ON(lp->non_tagged_cmd != ent);
@@ -585,7 +627,27 @@ static void esp_free_lun_tag(struct esp_cmd_entry *ent,
}
}
-/* When a contingent allegiance conditon is created, we force feed a
+static void esp_map_sense(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ ent->sense_ptr = ent->cmd->sense_buffer;
+ if (esp->flags & ESP_FLAG_NO_DMA_MAP) {
+ ent->sense_dma = (uintptr_t)ent->sense_ptr;
+ return;
+ }
+
+ ent->sense_dma = dma_map_single(esp->dev, ent->sense_ptr,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+}
+
+static void esp_unmap_sense(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ if (!(esp->flags & ESP_FLAG_NO_DMA_MAP))
+ dma_unmap_single(esp->dev, ent->sense_dma,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ ent->sense_ptr = NULL;
+}
+
+/* When a contingent allegiance condition is created, we force feed a
* REQUEST_SENSE command to the device to fetch the sense data. I
* tried many other schemes, relying on the scsi error handling layer
* to send out the REQUEST_SENSE automatically, but this was difficult
@@ -604,15 +666,9 @@ static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent)
if (!ent->sense_ptr) {
- esp_log_autosense("esp%d: Doing auto-sense for "
- "tgt[%d] lun[%d]\n",
- esp->host->unique_id, tgt, lun);
-
- ent->sense_ptr = cmd->sense_buffer;
- ent->sense_dma = esp->ops->map_single(esp,
- ent->sense_ptr,
- SCSI_SENSE_BUFFERSIZE,
- DMA_FROM_DEVICE);
+ esp_log_autosense("Doing auto-sense for tgt[%d] lun[%d]\n",
+ tgt, lun);
+ esp_map_sense(esp, ent);
}
ent->saved_sense_ptr = ent->sense_ptr;
@@ -642,10 +698,7 @@ static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent)
val = (p - esp->command_block);
- if (esp->rev == FASHME)
- scsi_esp_cmd(esp, ESP_CMD_FLUSH);
- esp->ops->send_dma_cmd(esp, esp->command_block_dma,
- val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA);
+ esp_send_dma_cmd(esp, val, 16, ESP_CMD_SELA);
}
static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
@@ -663,10 +716,12 @@ static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
return ent;
}
- if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) {
+ if (!spi_populate_tag_msg(&ent->tag[0], cmd)) {
ent->tag[0] = 0;
ent->tag[1] = 0;
}
+ ent->orig_tag[0] = ent->tag[0];
+ ent->orig_tag[1] = ent->tag[1];
if (esp_alloc_lun_tag(ent, lp) < 0)
continue;
@@ -680,10 +735,10 @@ static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
static void esp_maybe_execute_command(struct esp *esp)
{
struct esp_target_data *tp;
- struct esp_lun_data *lp;
struct scsi_device *dev;
struct scsi_cmnd *cmd;
struct esp_cmd_entry *ent;
+ bool select_and_stop = false;
int tgt, lun, i;
u32 val, start_cmd;
u8 *p;
@@ -706,7 +761,6 @@ static void esp_maybe_execute_command(struct esp *esp)
tgt = dev->id;
lun = dev->lun;
tp = &esp->target[tgt];
- lp = dev->hostdata;
list_move(&ent->list, &esp->active_cmds);
@@ -715,7 +769,8 @@ static void esp_maybe_execute_command(struct esp *esp)
esp_map_dma(esp, cmd);
esp_save_pointers(esp, ent);
- esp_check_command_len(esp, cmd);
+ if (!(cmd->cmd_len == 6 || cmd->cmd_len == 10 || cmd->cmd_len == 12))
+ select_and_stop = true;
p = esp->command_block;
@@ -756,42 +811,22 @@ static void esp_maybe_execute_command(struct esp *esp)
tp->flags &= ~ESP_TGT_CHECK_NEGO;
}
- /* Process it like a slow command. */
- if (tp->flags & (ESP_TGT_NEGO_WIDE | ESP_TGT_NEGO_SYNC))
- esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+ /* If there are multiple message bytes, use Select and Stop */
+ if (esp->msg_out_len)
+ select_and_stop = true;
}
build_identify:
- /* If we don't have a lun-data struct yet, we're probing
- * so do not disconnect. Also, do not disconnect unless
- * we have a tag on this command.
- */
- if (lp && (tp->flags & ESP_TGT_DISCONNECT) && ent->tag[0])
- *p++ = IDENTIFY(1, lun);
- else
- *p++ = IDENTIFY(0, lun);
+ *p++ = IDENTIFY(tp->flags & ESP_TGT_DISCONNECT, lun);
if (ent->tag[0] && esp->rev == ESP100) {
/* ESP100 lacks select w/atn3 command, use select
* and stop instead.
*/
- esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+ select_and_stop = true;
}
- if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) {
- start_cmd = ESP_CMD_DMA | ESP_CMD_SELA;
- if (ent->tag[0]) {
- *p++ = ent->tag[0];
- *p++ = ent->tag[1];
-
- start_cmd = ESP_CMD_DMA | ESP_CMD_SA3;
- }
-
- for (i = 0; i < cmd->cmd_len; i++)
- *p++ = cmd->cmnd[i];
-
- esp->select_state = ESP_SELECT_BASIC;
- } else {
+ if (select_and_stop) {
esp->cmd_bytes_left = cmd->cmd_len;
esp->cmd_bytes_ptr = &cmd->cmnd[0];
@@ -804,8 +839,21 @@ build_identify:
esp->msg_out_len += 2;
}
- start_cmd = ESP_CMD_DMA | ESP_CMD_SELAS;
+ start_cmd = ESP_CMD_SELAS;
esp->select_state = ESP_SELECT_MSGOUT;
+ } else {
+ start_cmd = ESP_CMD_SELA;
+ if (ent->tag[0]) {
+ *p++ = ent->tag[0];
+ *p++ = ent->tag[1];
+
+ start_cmd = ESP_CMD_SA3;
+ }
+
+ for (i = 0; i < cmd->cmd_len; i++)
+ *p++ = cmd->cmnd[i];
+
+ esp->select_state = ESP_SELECT_BASIC;
}
val = tgt;
if (esp->rev == FASHME)
@@ -824,10 +872,7 @@ build_identify:
printk("]\n");
}
- if (esp->rev == FASHME)
- scsi_esp_cmd(esp, ESP_CMD_FLUSH);
- esp->ops->send_dma_cmd(esp, esp->command_block_dma,
- val, 16, 0, start_cmd);
+ esp_send_dma_cmd(esp, val, 16, start_cmd);
}
static struct esp_cmd_entry *esp_get_ent(struct esp *esp)
@@ -851,7 +896,7 @@ static void esp_put_ent(struct esp *esp, struct esp_cmd_entry *ent)
}
static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
- struct scsi_cmnd *cmd, unsigned int result)
+ struct scsi_cmnd *cmd, unsigned char host_byte)
{
struct scsi_device *dev = cmd->device;
int tgt = dev->id;
@@ -860,7 +905,10 @@ static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
esp->active_cmd = NULL;
esp_unmap_dma(esp, cmd);
esp_free_lun_tag(ent, dev->hostdata);
- cmd->result = result;
+ cmd->result = 0;
+ set_host_byte(cmd, host_byte);
+ if (host_byte == DID_OK)
+ set_status_byte(cmd, ent->status);
if (ent->eh_done) {
complete(ent->eh_done);
@@ -868,18 +916,13 @@ static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
}
if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
- esp->ops->unmap_single(esp, ent->sense_dma,
- SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
- ent->sense_ptr = NULL;
+ esp_unmap_sense(esp, ent);
/* Restore the message/status bytes to what we actually
* saw originally. Also, report that we are providing
* the sense data.
*/
- cmd->result = ((DRIVER_SENSE << 24) |
- (DID_OK << 16) |
- (COMMAND_COMPLETE << 8) |
- (SAM_STAT_CHECK_CONDITION << 0));
+ cmd->result = SAM_STAT_CHECK_CONDITION;
ent->flags &= ~ESP_CMD_FLAG_AUTOSENSE;
if (esp_debug & ESP_DEBUG_AUTOSENSE) {
@@ -893,7 +936,7 @@ static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
}
}
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
list_del(&ent->list);
esp_put_ent(esp, ent);
@@ -901,12 +944,6 @@ static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
esp_maybe_execute_command(esp);
}
-static unsigned int compose_result(unsigned int status, unsigned int message,
- unsigned int driver_code)
-{
- return (status | (message << 8) | (driver_code << 16));
-}
-
static void esp_event_queue_full(struct esp *esp, struct esp_cmd_entry *ent)
{
struct scsi_device *dev = ent->cmd->device;
@@ -915,7 +952,7 @@ static void esp_event_queue_full(struct esp *esp, struct esp_cmd_entry *ent)
scsi_track_queue_full(dev, lp->num_tagged - 1);
}
-static int esp_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+static int esp_queuecommand_lck(struct scsi_cmnd *cmd)
{
struct scsi_device *dev = cmd->device;
struct esp *esp = shost_priv(dev->host);
@@ -928,10 +965,8 @@ static int esp_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_
ent->cmd = cmd;
- cmd->scsi_done = done;
-
spriv = ESP_CMD_PRIV(cmd);
- spriv->u.dma_addr = ~(dma_addr_t)0x0;
+ spriv->num_sg = 0;
list_add_tail(&ent->list, &esp->queued_cmds);
@@ -951,8 +986,8 @@ static int esp_check_gross_error(struct esp *esp)
* - DMA programmed with wrong direction
* - improper phase change
*/
- printk(KERN_ERR PFX "esp%d: Gross error sreg[%02x]\n",
- esp->host->unique_id, esp->sreg);
+ shost_printk(KERN_ERR, esp->host,
+ "Gross error sreg[%02x]\n", esp->sreg);
/* XXX Reset the chip. XXX */
return 1;
}
@@ -972,7 +1007,6 @@ static int esp_check_spur_intr(struct esp *esp)
default:
if (!(esp->sreg & ESP_STAT_INTR)) {
- esp->ireg = esp_read8(ESP_INTRPT);
if (esp->ireg & ESP_INTR_SR)
return 1;
@@ -980,14 +1014,13 @@ static int esp_check_spur_intr(struct esp *esp)
* ESP is not, the only possibility is a DMA error.
*/
if (!esp->ops->dma_error(esp)) {
- printk(KERN_ERR PFX "esp%d: Spurious irq, "
- "sreg=%02x.\n",
- esp->host->unique_id, esp->sreg);
+ shost_printk(KERN_ERR, esp->host,
+ "Spurious irq, sreg=%02x.\n",
+ esp->sreg);
return -1;
}
- printk(KERN_ERR PFX "esp%d: DMA error\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host, "DMA error\n");
/* XXX Reset the chip. XXX */
return -1;
@@ -1000,7 +1033,7 @@ static int esp_check_spur_intr(struct esp *esp)
static void esp_schedule_reset(struct esp *esp)
{
- esp_log_reset("ESP: esp_schedule_reset() from %p\n",
+ esp_log_reset("esp_schedule_reset() from %ps\n",
__builtin_return_address(0));
esp->flags |= ESP_FLAG_RESETTING;
esp_event(esp, ESP_EVENT_RESET);
@@ -1017,20 +1050,20 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp,
int i;
if (!lp->num_tagged) {
- printk(KERN_ERR PFX "esp%d: Reconnect w/num_tagged==0\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect w/num_tagged==0\n");
return NULL;
}
- esp_log_reconnect("ESP: reconnect tag, ");
+ esp_log_reconnect("reconnect tag, ");
for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) {
if (esp->ops->irq_pending(esp))
break;
}
if (i == ESP_QUICKIRQ_LIMIT) {
- printk(KERN_ERR PFX "esp%d: Reconnect IRQ1 timeout\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect IRQ1 timeout\n");
return NULL;
}
@@ -1041,14 +1074,14 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp,
i, esp->ireg, esp->sreg);
if (esp->ireg & ESP_INTR_DC) {
- printk(KERN_ERR PFX "esp%d: Reconnect, got disconnect.\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, got disconnect.\n");
return NULL;
}
if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) {
- printk(KERN_ERR PFX "esp%d: Reconnect, not MIP sreg[%02x].\n",
- esp->host->unique_id, esp->sreg);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, not MIP sreg[%02x].\n", esp->sreg);
return NULL;
}
@@ -1071,8 +1104,7 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp,
udelay(1);
}
if (i == ESP_RESELECT_TAG_LIMIT) {
- printk(KERN_ERR PFX "esp%d: Reconnect IRQ2 timeout\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host, "Reconnect IRQ2 timeout\n");
return NULL;
}
esp->ops->dma_drain(esp);
@@ -1085,17 +1117,17 @@ static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp,
if (esp->command_block[0] < SIMPLE_QUEUE_TAG ||
esp->command_block[0] > ORDERED_QUEUE_TAG) {
- printk(KERN_ERR PFX "esp%d: Reconnect, bad tag "
- "type %02x.\n",
- esp->host->unique_id, esp->command_block[0]);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, bad tag type %02x.\n",
+ esp->command_block[0]);
return NULL;
}
ent = lp->tagged_cmds[esp->command_block[1]];
if (!ent) {
- printk(KERN_ERR PFX "esp%d: Reconnect, no entry for "
- "tag %02x.\n",
- esp->host->unique_id, esp->command_block[1]);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, no entry for tag %02x.\n",
+ esp->command_block[1]);
return NULL;
}
@@ -1161,9 +1193,9 @@ static int esp_reconnect(struct esp *esp)
tp = &esp->target[target];
dev = __scsi_device_lookup_by_target(tp->starget, lun);
if (!dev) {
- printk(KERN_ERR PFX "esp%d: Reconnect, no lp "
- "tgt[%u] lun[%u]\n",
- esp->host->unique_id, target, lun);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, no lp tgt[%u] lun[%u]\n",
+ target, lun);
goto do_reset;
}
lp = dev->hostdata;
@@ -1177,12 +1209,6 @@ static int esp_reconnect(struct esp *esp)
esp->active_cmd = ent;
- if (ent->flags & ESP_CMD_FLAG_ABORT) {
- esp->msg_out[0] = ABORT_TASK_SET;
- esp->msg_out_len = 1;
- scsi_esp_cmd(esp, ESP_CMD_SATN);
- }
-
esp_event(esp, ESP_EVENT_CHECK_PHASE);
esp_restore_pointers(esp, ent);
esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
@@ -1197,9 +1223,6 @@ static int esp_finish_select(struct esp *esp)
{
struct esp_cmd_entry *ent;
struct scsi_cmnd *cmd;
- u8 orig_select_state;
-
- orig_select_state = esp->select_state;
/* No longer selecting. */
esp->select_state = ESP_SELECT_NONE;
@@ -1213,7 +1236,7 @@ static int esp_finish_select(struct esp *esp)
* all bets are off.
*/
esp_schedule_reset(esp);
- esp_cmd_is_done(esp, ent, cmd, (DID_ERROR << 16));
+ esp_cmd_is_done(esp, ent, cmd, DID_ERROR);
return 0;
}
@@ -1230,14 +1253,10 @@ static int esp_finish_select(struct esp *esp)
esp_unmap_dma(esp, cmd);
esp_free_lun_tag(ent, cmd->device->hostdata);
tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE);
- esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
esp->cmd_bytes_ptr = NULL;
esp->cmd_bytes_left = 0;
} else {
- esp->ops->unmap_single(esp, ent->sense_dma,
- SCSI_SENSE_BUFFERSIZE,
- DMA_FROM_DEVICE);
- ent->sense_ptr = NULL;
+ esp_unmap_sense(esp, ent);
}
/* Now that the state is unwound properly, put back onto
@@ -1262,7 +1281,7 @@ static int esp_finish_select(struct esp *esp)
esp->target[dev->id].flags |= ESP_TGT_CHECK_NEGO;
scsi_esp_cmd(esp, ESP_CMD_ESEL);
- esp_cmd_is_done(esp, ent, cmd, (DID_BAD_TARGET << 16));
+ esp_cmd_is_done(esp, ent, cmd, DID_BAD_TARGET);
return 1;
}
@@ -1281,16 +1300,15 @@ static int esp_finish_select(struct esp *esp)
esp_flush_fifo(esp);
}
- /* If we are doing a slow command, negotiation, etc.
- * we'll do the right thing as we transition to the
- * next phase.
+ /* If we are doing a Select And Stop command, negotiation, etc.
+ * we'll do the right thing as we transition to the next phase.
*/
esp_event(esp, ESP_EVENT_CHECK_PHASE);
return 0;
}
- printk("ESP: Unexpected selection completion ireg[%x].\n",
- esp->ireg);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected selection completion ireg[%x]\n", esp->ireg);
esp_schedule_reset(esp);
return 0;
}
@@ -1310,11 +1328,43 @@ static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent,
(((unsigned int)esp_read8(ESP_TCMED)) << 8));
if (esp->rev == FASHME)
ecount |= ((unsigned int)esp_read8(FAS_RLO)) << 16;
+ if (esp->rev == PCSCSI && (esp->config2 & ESP_CONFIG2_FENAB))
+ ecount |= ((unsigned int)esp_read8(ESP_TCHI)) << 16;
}
bytes_sent = esp->data_dma_len;
bytes_sent -= ecount;
-
+ bytes_sent -= esp->send_cmd_residual;
+
+ /*
+ * The am53c974 has a DMA 'peculiarity'. The doc states:
+ * In some odd byte conditions, one residual byte will
+ * be left in the SCSI FIFO, and the FIFO Flags will
+ * never count to '0 '. When this happens, the residual
+ * byte should be retrieved via PIO following completion
+ * of the BLAST operation.
+ */
+ if (fifo_cnt == 1 && ent->flags & ESP_CMD_FLAG_RESIDUAL) {
+ size_t count = 1;
+ size_t offset = bytes_sent;
+ u8 bval = esp_read8(ESP_FDATA);
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE)
+ ent->sense_ptr[bytes_sent] = bval;
+ else {
+ struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+ u8 *ptr;
+
+ ptr = scsi_kmap_atomic_sg(p->cur_sg, p->num_sg,
+ &offset, &count);
+ if (likely(ptr)) {
+ *(ptr + offset) = bval;
+ scsi_kunmap_atomic_sg(ptr);
+ }
+ }
+ bytes_sent += fifo_cnt;
+ ent->flags &= ~ESP_CMD_FLAG_RESIDUAL;
+ }
if (!(ent->flags & ESP_CMD_FLAG_WRITE))
bytes_sent -= fifo_cnt;
@@ -1432,9 +1482,8 @@ static void esp_msgin_reject(struct esp *esp)
return;
}
- esp->msg_out[0] = ABORT_TASK_SET;
- esp->msg_out_len = 1;
- scsi_esp_cmd(esp, ESP_CMD_SATN);
+ shost_printk(KERN_INFO, esp->host, "Unexpected MESSAGE REJECT\n");
+ esp_schedule_reset(esp);
}
static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
@@ -1554,10 +1603,10 @@ static void esp_msgin_extended(struct esp *esp)
return;
}
- printk("ESP: Unexpected extended msg type %x\n",
- esp->msg_in[2]);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected extended msg type %x\n", esp->msg_in[2]);
- esp->msg_out[0] = ABORT_TASK_SET;
+ esp->msg_out[0] = MESSAGE_REJECT;
esp->msg_out_len = 1;
scsi_esp_cmd(esp, ESP_CMD_SATN);
}
@@ -1572,7 +1621,8 @@ static int esp_msgin_process(struct esp *esp)
if (msg0 & 0x80) {
/* Identify */
- printk("ESP: Unexpected msgin identify\n");
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected msgin identify\n");
return 0;
}
@@ -1598,7 +1648,7 @@ static int esp_msgin_process(struct esp *esp)
spriv = ESP_CMD_PRIV(ent->cmd);
if (spriv->cur_residue == sg_dma_len(spriv->cur_sg)) {
- spriv->cur_sg--;
+ spriv->cur_sg = spriv->prv_sg;
spriv->cur_residue = 1;
} else
spriv->cur_residue++;
@@ -1638,10 +1688,12 @@ static int esp_msgin_process(struct esp *esp)
static int esp_process_event(struct esp *esp)
{
- int write;
+ int write, i;
again:
write = 0;
+ esp_log_event("process event %d phase %x\n",
+ esp->event, esp->sreg & ESP_STAT_PMASK);
switch (esp->event) {
case ESP_EVENT_CHECK_PHASE:
switch (esp->sreg & ESP_STAT_PMASK) {
@@ -1671,17 +1723,17 @@ again:
break;
default:
- printk("ESP: Unexpected phase, sreg=%02x\n",
- esp->sreg);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected phase, sreg=%02x\n",
+ esp->sreg);
esp_schedule_reset(esp);
return 0;
}
goto again;
- break;
case ESP_EVENT_DATA_IN:
write = 1;
- /* fallthru */
+ fallthrough;
case ESP_EVENT_DATA_OUT: {
struct esp_cmd_entry *ent = esp->active_cmd;
@@ -1706,18 +1758,17 @@ again:
esp->data_dma_len = dma_len;
if (!dma_len) {
- printk(KERN_ERR PFX "esp%d: DMA length is zero!\n",
- esp->host->unique_id);
- printk(KERN_ERR PFX "esp%d: cur adr[%08llx] len[%08x]\n",
- esp->host->unique_id,
- (unsigned long long)esp_cur_dma_addr(ent, cmd),
- esp_cur_dma_len(ent, cmd));
+ shost_printk(KERN_ERR, esp->host,
+ "DMA length is zero!\n");
+ shost_printk(KERN_ERR, esp->host,
+ "cur adr[%08llx] len[%08x]\n",
+ (unsigned long long)esp_cur_dma_addr(ent, cmd),
+ esp_cur_dma_len(ent, cmd));
esp_schedule_reset(esp);
return 0;
}
- esp_log_datastart("ESP: start data addr[%08llx] len[%u] "
- "write(%d)\n",
+ esp_log_datastart("start data addr[%08llx] len[%u] write(%d)\n",
(unsigned long long)dma_addr, dma_len, write);
esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len,
@@ -1731,7 +1782,8 @@ again:
int bytes_sent;
if (esp->ops->dma_error(esp)) {
- printk("ESP: data done, DMA error, resetting\n");
+ shost_printk(KERN_INFO, esp->host,
+ "data done, DMA error, resetting\n");
esp_schedule_reset(esp);
return 0;
}
@@ -1747,14 +1799,15 @@ again:
/* We should always see exactly a bus-service
* interrupt at the end of a successful transfer.
*/
- printk("ESP: data done, not BSERV, resetting\n");
+ shost_printk(KERN_INFO, esp->host,
+ "data done, not BSERV, resetting\n");
esp_schedule_reset(esp);
return 0;
}
bytes_sent = esp_data_bytes_sent(esp, ent, cmd);
- esp_log_datadone("ESP: data done flgs[%x] sent[%d]\n",
+ esp_log_datadone("data done flgs[%x] sent[%d]\n",
ent->flags, bytes_sent);
if (bytes_sent < 0) {
@@ -1783,8 +1836,9 @@ again:
}
if (ent->message != COMMAND_COMPLETE) {
- printk("ESP: Unexpected message %x in status\n",
- ent->message);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected message %x in status\n",
+ ent->message);
esp_schedule_reset(esp);
return 0;
}
@@ -1802,8 +1856,7 @@ again:
scsi_esp_cmd(esp, ESP_CMD_ESEL);
if (ent->message == COMMAND_COMPLETE) {
- esp_log_cmddone("ESP: Command done status[%x] "
- "message[%x]\n",
+ esp_log_cmddone("Command done status[%x] message[%x]\n",
ent->status, ent->message);
if (ent->status == SAM_STAT_TASK_SET_FULL)
esp_event_queue_full(esp, ent);
@@ -1813,22 +1866,19 @@ again:
ent->flags |= ESP_CMD_FLAG_AUTOSENSE;
esp_autosense(esp, ent);
} else {
- esp_cmd_is_done(esp, ent, cmd,
- compose_result(ent->status,
- ent->message,
- DID_OK));
+ esp_cmd_is_done(esp, ent, cmd, DID_OK);
}
} else if (ent->message == DISCONNECT) {
- esp_log_disconnect("ESP: Disconnecting tgt[%d] "
- "tag[%x:%x]\n",
+ esp_log_disconnect("Disconnecting tgt[%d] tag[%x:%x]\n",
cmd->device->id,
ent->tag[0], ent->tag[1]);
esp->active_cmd = NULL;
esp_maybe_execute_command(esp);
} else {
- printk("ESP: Unexpected message %x in freebus\n",
- ent->message);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected message %x in freebus\n",
+ ent->message);
esp_schedule_reset(esp);
return 0;
}
@@ -1860,6 +1910,10 @@ again:
if (esp->msg_out_len == 1) {
esp_write8(esp->msg_out[0], ESP_FDATA);
scsi_esp_cmd(esp, ESP_CMD_TI);
+ } else if (esp->flags & ESP_FLAG_USE_FIFO) {
+ for (i = 0; i < esp->msg_out_len; i++)
+ esp_write8(esp->msg_out[i], ESP_FDATA);
+ scsi_esp_cmd(esp, ESP_CMD_TI);
} else {
/* Use DMA. */
memcpy(esp->command_block,
@@ -1883,12 +1937,16 @@ again:
} else {
if (esp->msg_out_len > 1)
esp->ops->dma_invalidate(esp);
- }
- if (!(esp->ireg & ESP_INTR_DC)) {
- if (esp->rev != FASHME)
+ /* XXX if the chip went into disconnected mode,
+ * we can't run the phase state machine anyway.
+ */
+ if (!(esp->ireg & ESP_INTR_DC))
scsi_esp_cmd(esp, ESP_CMD_NULL);
}
+
+ esp->msg_out_len = 0;
+
esp_event(esp, ESP_EVENT_CHECK_PHASE);
goto again;
case ESP_EVENT_MSGIN:
@@ -1915,7 +1973,7 @@ again:
val = esp_read8(ESP_FDATA);
esp->msg_in[esp->msg_in_len++] = val;
- esp_log_msgin("ESP: Got msgin byte %x\n", val);
+ esp_log_msgin("Got msgin byte %x\n", val);
if (!esp_msgin_process(esp))
esp->msg_in_len = 0;
@@ -1925,10 +1983,15 @@ again:
scsi_esp_cmd(esp, ESP_CMD_MOK);
+ /* Check whether a bus reset is to be done next */
+ if (esp->event == ESP_EVENT_RESET)
+ return 0;
+
if (esp->event != ESP_EVENT_FREE_BUS)
esp_event(esp, ESP_EVENT_CHECK_PHASE);
} else {
- printk("ESP: MSGIN neither BSERV not FDON, resetting");
+ shost_printk(KERN_INFO, esp->host,
+ "MSGIN neither BSERV not FDON, resetting");
esp_schedule_reset(esp);
return 0;
}
@@ -1936,11 +1999,7 @@ again:
case ESP_EVENT_CMD_START:
memcpy(esp->command_block, esp->cmd_bytes_ptr,
esp->cmd_bytes_left);
- if (esp->rev == FASHME)
- scsi_esp_cmd(esp, ESP_CMD_FLUSH);
- esp->ops->send_dma_cmd(esp, esp->command_block_dma,
- esp->cmd_bytes_left, 16, 0,
- ESP_CMD_DMA | ESP_CMD_TI);
+ esp_send_dma_cmd(esp, esp->cmd_bytes_left, 16, ESP_CMD_TI);
esp_event(esp, ESP_EVENT_CMD_DONE);
esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
break;
@@ -1952,18 +2011,16 @@ again:
}
esp_schedule_reset(esp);
return 0;
- break;
case ESP_EVENT_RESET:
scsi_esp_cmd(esp, ESP_CMD_RS);
break;
default:
- printk("ESP: Unexpected event %x, resetting\n",
- esp->event);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected event %x, resetting\n", esp->event);
esp_schedule_reset(esp);
return 0;
- break;
}
return 1;
}
@@ -1976,13 +2033,10 @@ static void esp_reset_cleanup_one(struct esp *esp, struct esp_cmd_entry *ent)
esp_free_lun_tag(ent, cmd->device->hostdata);
cmd->result = DID_RESET << 16;
- if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
- esp->ops->unmap_single(esp, ent->sense_dma,
- SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
- ent->sense_ptr = NULL;
- }
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE)
+ esp_unmap_sense(esp, ent);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
list_del(&ent->list);
esp_put_ent(esp, ent);
}
@@ -2005,7 +2059,7 @@ static void esp_reset_cleanup(struct esp *esp)
list_del(&ent->list);
cmd->result = DID_RESET << 16;
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
esp_put_ent(esp, ent);
}
@@ -2042,7 +2096,12 @@ static void __esp_interrupt(struct esp *esp)
int finish_reset, intr_done;
u8 phase;
+ /*
+ * Once INTRPT is read STATUS and SSTEP are cleared.
+ */
esp->sreg = esp_read8(ESP_STATUS);
+ esp->seqreg = esp_read8(ESP_SSTEP);
+ esp->ireg = esp_read8(ESP_INTRPT);
if (esp->flags & ESP_FLAG_RESETTING) {
finish_reset = 1;
@@ -2055,8 +2114,6 @@ static void __esp_interrupt(struct esp *esp)
return;
}
- esp->ireg = esp_read8(ESP_INTRPT);
-
if (esp->ireg & ESP_INTR_SR)
finish_reset = 1;
@@ -2083,27 +2140,28 @@ static void __esp_interrupt(struct esp *esp)
}
}
- esp_log_intr("ESP: intr sreg[%02x] seqreg[%02x] "
+ esp_log_intr("intr sreg[%02x] seqreg[%02x] "
"sreg2[%02x] ireg[%02x]\n",
esp->sreg, esp->seqreg, esp->sreg2, esp->ireg);
intr_done = 0;
if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN | ESP_INTR_IC)) {
- printk("ESP: unexpected IREG %02x\n", esp->ireg);
+ shost_printk(KERN_INFO, esp->host,
+ "unexpected IREG %02x\n", esp->ireg);
if (esp->ireg & ESP_INTR_IC)
esp_dump_cmd_log(esp);
esp_schedule_reset(esp);
} else {
- if (!(esp->ireg & ESP_INTR_RSEL)) {
- /* Some combination of FDONE, BSERV, DC. */
- if (esp->select_state != ESP_SELECT_NONE)
- intr_done = esp_finish_select(esp);
- } else if (esp->ireg & ESP_INTR_RSEL) {
+ if (esp->ireg & ESP_INTR_RSEL) {
if (esp->active_cmd)
(void) esp_finish_select(esp);
intr_done = esp_reconnect(esp);
+ } else {
+ /* Some combination of FDONE, BSERV, DC. */
+ if (esp->select_state != ESP_SELECT_NONE)
+ intr_done = esp_finish_select(esp);
}
}
while (!intr_done)
@@ -2147,46 +2205,50 @@ static void esp_get_revision(struct esp *esp)
u8 val;
esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
- esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+ if (esp->config2 == 0) {
+ esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+ esp_write8(esp->config2, ESP_CFG2);
+
+ val = esp_read8(ESP_CFG2);
+ val &= ~ESP_CONFIG2_MAGIC;
+
+ esp->config2 = 0;
+ if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+ /*
+ * If what we write to cfg2 does not come back,
+ * cfg2 is not implemented.
+ * Therefore this must be a plain esp100.
+ */
+ esp->rev = ESP100;
+ return;
+ }
+ }
+
+ esp_set_all_config3(esp, 5);
+ esp->prev_cfg3 = 5;
esp_write8(esp->config2, ESP_CFG2);
+ esp_write8(0, ESP_CFG3);
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
- val = esp_read8(ESP_CFG2);
- val &= ~ESP_CONFIG2_MAGIC;
- if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
- /* If what we write to cfg2 does not come back, cfg2 is not
- * implemented, therefore this must be a plain esp100.
+ val = esp_read8(ESP_CFG3);
+ if (val != 5) {
+ /* The cfg2 register is implemented, however
+ * cfg3 is not, must be esp100a.
*/
- esp->rev = ESP100;
+ esp->rev = ESP100A;
} else {
- esp->config2 = 0;
- esp_set_all_config3(esp, 5);
- esp->prev_cfg3 = 5;
- esp_write8(esp->config2, ESP_CFG2);
- esp_write8(0, ESP_CFG3);
+ esp_set_all_config3(esp, 0);
+ esp->prev_cfg3 = 0;
esp_write8(esp->prev_cfg3, ESP_CFG3);
- val = esp_read8(ESP_CFG3);
- if (val != 5) {
- /* The cfg2 register is implemented, however
- * cfg3 is not, must be esp100a.
- */
- esp->rev = ESP100A;
+ /* All of cfg{1,2,3} implemented, must be one of
+ * the fas variants, figure out which one.
+ */
+ if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) {
+ esp->rev = FAST;
+ esp->sync_defp = SYNC_DEFP_FAST;
} else {
- esp_set_all_config3(esp, 0);
- esp->prev_cfg3 = 0;
- esp_write8(esp->prev_cfg3, ESP_CFG3);
-
- /* All of cfg{1,2,3} implemented, must be one of
- * the fas variants, figure out which one.
- */
- if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) {
- esp->rev = FAST;
- esp->sync_defp = SYNC_DEFP_FAST;
- } else {
- esp->rev = ESP236;
- }
- esp->config2 = 0;
- esp_write8(esp->config2, ESP_CFG2);
+ esp->rev = ESP236;
}
}
}
@@ -2199,7 +2261,7 @@ static void esp_init_swstate(struct esp *esp)
INIT_LIST_HEAD(&esp->active_cmds);
INIT_LIST_HEAD(&esp->esp_cmd_pool);
- /* Start with a clear state, domain validation (via ->slave_configure,
+ /* Start with a clear state, domain validation (via ->sdev_configure,
* spi_dv_device()) will attempt to enable SYNC, WIDE, and tagged
* commands.
*/
@@ -2303,6 +2365,8 @@ static const char *esp_chip_names[] = {
"ESP100A",
"ESP236",
"FAS236",
+ "AM53C974",
+ "53CF9x-2",
"FAS100A",
"FAST",
"FASHME",
@@ -2310,11 +2374,13 @@ static const char *esp_chip_names[] = {
static struct scsi_transport_template *esp_transport_template;
-int scsi_esp_register(struct esp *esp, struct device *dev)
+int scsi_esp_register(struct esp *esp)
{
static int instance;
int err;
+ if (!esp->num_tags)
+ esp->num_tags = ESP_DEFAULT_TAGS;
esp->host->transportt = esp_transport_template;
esp->host->max_lun = ESP_MAX_LUN;
esp->host->cmd_per_lun = 2;
@@ -2328,17 +2394,18 @@ int scsi_esp_register(struct esp *esp, struct device *dev)
esp_bootup_reset(esp);
- printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n",
- esp->host->unique_id, esp->regs, esp->dma_regs,
- esp->host->irq);
- printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
- esp->host->unique_id, esp_chip_names[esp->rev],
- esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
+ dev_printk(KERN_INFO, esp->dev, "esp%u: regs[%1p:%1p] irq[%u]\n",
+ esp->host->unique_id, esp->regs, esp->dma_regs,
+ esp->host->irq);
+ dev_printk(KERN_INFO, esp->dev,
+ "esp%u: is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
+ esp->host->unique_id, esp_chip_names[esp->rev],
+ esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
/* Let the SCSI bus reset settle. */
ssleep(esp_bus_reset_settle);
- err = scsi_add_host(esp->host, dev);
+ err = scsi_add_host(esp->host, esp->dev);
if (err)
return err;
@@ -2374,7 +2441,7 @@ static void esp_target_destroy(struct scsi_target *starget)
tp->starget = NULL;
}
-static int esp_slave_alloc(struct scsi_device *dev)
+static int esp_sdev_init(struct scsi_device *dev)
{
struct esp *esp = shost_priv(dev->host);
struct esp_target_data *tp = &esp->target[dev->id];
@@ -2396,32 +2463,14 @@ static int esp_slave_alloc(struct scsi_device *dev)
return 0;
}
-static int esp_slave_configure(struct scsi_device *dev)
+static int esp_sdev_configure(struct scsi_device *dev, struct queue_limits *lim)
{
struct esp *esp = shost_priv(dev->host);
struct esp_target_data *tp = &esp->target[dev->id];
- int goal_tags, queue_depth;
-
- goal_tags = 0;
-
- if (dev->tagged_supported) {
- /* XXX make this configurable somehow XXX */
- goal_tags = ESP_DEFAULT_TAGS;
-
- if (goal_tags > ESP_MAX_TAG)
- goal_tags = ESP_MAX_TAG;
- }
- queue_depth = goal_tags;
- if (queue_depth < dev->host->cmd_per_lun)
- queue_depth = dev->host->cmd_per_lun;
+ if (dev->tagged_supported)
+ scsi_change_queue_depth(dev, esp->num_tags);
- if (goal_tags) {
- scsi_set_tag_type(dev, MSG_ORDERED_TAG);
- scsi_activate_tcq(dev, queue_depth);
- } else {
- scsi_deactivate_tcq(dev, queue_depth);
- }
tp->flags |= ESP_TGT_DISCONNECT;
if (!spi_initial_dv(dev->sdev_target))
@@ -2430,7 +2479,7 @@ static int esp_slave_configure(struct scsi_device *dev)
return 0;
}
-static void esp_slave_destroy(struct scsi_device *dev)
+static void esp_sdev_destroy(struct scsi_device *dev)
{
struct esp_lun_data *lp = dev->hostdata;
@@ -2449,19 +2498,20 @@ static int esp_eh_abort_handler(struct scsi_cmnd *cmd)
* XXX much for the final driver.
*/
spin_lock_irqsave(esp->host->host_lock, flags);
- printk(KERN_ERR PFX "esp%d: Aborting command [%p:%02x]\n",
- esp->host->unique_id, cmd, cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host, "Aborting command [%p:%02x]\n",
+ cmd, cmd->cmnd[0]);
ent = esp->active_cmd;
if (ent)
- printk(KERN_ERR PFX "esp%d: Current command [%p:%02x]\n",
- esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host,
+ "Current command [%p:%02x]\n",
+ ent->cmd, ent->cmd->cmnd[0]);
list_for_each_entry(ent, &esp->queued_cmds, list) {
- printk(KERN_ERR PFX "esp%d: Queued command [%p:%02x]\n",
- esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host, "Queued command [%p:%02x]\n",
+ ent->cmd, ent->cmd->cmnd[0]);
}
list_for_each_entry(ent, &esp->active_cmds, list) {
- printk(KERN_ERR PFX "esp%d: Active command [%p:%02x]\n",
- esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host, " Active command [%p:%02x]\n",
+ ent->cmd, ent->cmd->cmnd[0]);
}
esp_dump_cmd_log(esp);
spin_unlock_irqrestore(esp->host->host_lock, flags);
@@ -2483,7 +2533,7 @@ static int esp_eh_abort_handler(struct scsi_cmnd *cmd)
list_del(&ent->list);
cmd->result = DID_ABORT << 16;
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
esp_put_ent(esp, ent);
@@ -2610,25 +2660,25 @@ static const char *esp_info(struct Scsi_Host *host)
return "esp";
}
-struct scsi_host_template scsi_esp_template = {
+const struct scsi_host_template scsi_esp_template = {
.module = THIS_MODULE,
.name = "esp",
.info = esp_info,
.queuecommand = esp_queuecommand,
.target_alloc = esp_target_alloc,
.target_destroy = esp_target_destroy,
- .slave_alloc = esp_slave_alloc,
- .slave_configure = esp_slave_configure,
- .slave_destroy = esp_slave_destroy,
+ .sdev_init = esp_sdev_init,
+ .sdev_configure = esp_sdev_configure,
+ .sdev_destroy = esp_sdev_destroy,
.eh_abort_handler = esp_eh_abort_handler,
.eh_bus_reset_handler = esp_eh_bus_reset_handler,
.eh_host_reset_handler = esp_eh_host_reset_handler,
.can_queue = 7,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .use_clustering = ENABLE_CLUSTERING,
.max_sectors = 0xffff,
.skip_settle_delay = 1,
+ .cmd_size = sizeof(struct esp_cmd_priv),
};
EXPORT_SYMBOL(scsi_esp_template);
@@ -2690,9 +2740,6 @@ static struct spi_function_template esp_transport_ops = {
static int __init esp_init(void)
{
- BUILD_BUG_ON(sizeof(struct scsi_pointer) <
- sizeof(struct esp_cmd_priv));
-
esp_transport_template = spi_attach_transport(&esp_transport_ops);
if (!esp_transport_template)
return -ENODEV;
@@ -2706,7 +2753,7 @@ static void __exit esp_exit(void)
}
MODULE_DESCRIPTION("ESP SCSI driver core");
-MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
@@ -2732,3 +2779,131 @@ MODULE_PARM_DESC(esp_debug,
module_init(esp_init);
module_exit(esp_exit);
+
+#ifdef CONFIG_SCSI_ESP_PIO
+static inline unsigned int esp_wait_for_fifo(struct esp *esp)
+{
+ int i = 500000;
+
+ do {
+ unsigned int fbytes = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+
+ if (fbytes)
+ return fbytes;
+
+ udelay(1);
+ } while (--i);
+
+ shost_printk(KERN_ERR, esp->host, "FIFO is empty. sreg [%02x]\n",
+ esp_read8(ESP_STATUS));
+ return 0;
+}
+
+static inline int esp_wait_for_intr(struct esp *esp)
+{
+ int i = 500000;
+
+ do {
+ esp->sreg = esp_read8(ESP_STATUS);
+ if (esp->sreg & ESP_STAT_INTR)
+ return 0;
+
+ udelay(1);
+ } while (--i);
+
+ shost_printk(KERN_ERR, esp->host, "IRQ timeout. sreg [%02x]\n",
+ esp->sreg);
+ return 1;
+}
+
+#define ESP_FIFO_SIZE 16
+
+void esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
+ u32 dma_count, int write, u8 cmd)
+{
+ u8 phase = esp->sreg & ESP_STAT_PMASK;
+
+ cmd &= ~ESP_CMD_DMA;
+ esp->send_cmd_error = 0;
+
+ if (write) {
+ u8 *dst = (u8 *)addr;
+ u8 mask = ~(phase == ESP_MIP ? ESP_INTR_FDONE : ESP_INTR_BSERV);
+
+ scsi_esp_cmd(esp, cmd);
+
+ while (1) {
+ if (!esp_wait_for_fifo(esp))
+ break;
+
+ *dst++ = readb(esp->fifo_reg);
+ --esp_count;
+
+ if (!esp_count)
+ break;
+
+ if (esp_wait_for_intr(esp)) {
+ esp->send_cmd_error = 1;
+ break;
+ }
+
+ if ((esp->sreg & ESP_STAT_PMASK) != phase)
+ break;
+
+ esp->ireg = esp_read8(ESP_INTRPT);
+ if (esp->ireg & mask) {
+ esp->send_cmd_error = 1;
+ break;
+ }
+
+ if (phase == ESP_MIP)
+ esp_write8(ESP_CMD_MOK, ESP_CMD);
+
+ esp_write8(ESP_CMD_TI, ESP_CMD);
+ }
+ } else {
+ unsigned int n = ESP_FIFO_SIZE;
+ u8 *src = (u8 *)addr;
+
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+ if (n > esp_count)
+ n = esp_count;
+ writesb(esp->fifo_reg, src, n);
+ src += n;
+ esp_count -= n;
+
+ scsi_esp_cmd(esp, cmd);
+
+ while (esp_count) {
+ if (esp_wait_for_intr(esp)) {
+ esp->send_cmd_error = 1;
+ break;
+ }
+
+ if ((esp->sreg & ESP_STAT_PMASK) != phase)
+ break;
+
+ esp->ireg = esp_read8(ESP_INTRPT);
+ if (esp->ireg & ~ESP_INTR_BSERV) {
+ esp->send_cmd_error = 1;
+ break;
+ }
+
+ n = ESP_FIFO_SIZE -
+ (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES);
+
+ if (n > esp_count)
+ n = esp_count;
+ writesb(esp->fifo_reg, src, n);
+ src += n;
+ esp_count -= n;
+
+ esp_write8(ESP_CMD_TI, ESP_CMD);
+ }
+ }
+
+ esp->send_cmd_residual = esp_count;
+}
+EXPORT_SYMBOL(esp_send_pio_cmd);
+#endif
diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h
index 28e22acf87ea..7bb0b69bff24 100644
--- a/drivers/scsi/esp_scsi.h
+++ b/drivers/scsi/esp_scsi.h
@@ -1,4 +1,5 @@
-/* esp_scsi.h: Defines and structures for the ESP drier.
+/* SPDX-License-Identifier: GPL-2.0 */
+/* esp_scsi.h: Defines and structures for the ESP driver.
*
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
@@ -25,6 +26,7 @@
#define ESP_CTEST 0x0aUL /* wo Chip test register 0x28 */
#define ESP_CFG2 0x0bUL /* rw Second cfg register 0x2c */
#define ESP_CFG3 0x0cUL /* rw Third cfg register 0x30 */
+#define ESP_CFG4 0x0dUL /* rw Fourth cfg register 0x34 */
#define ESP_TCHI 0x0eUL /* rw High bits transf count 0x38 */
#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */
#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */
@@ -76,6 +78,20 @@
#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */
#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */
+/* ESP config register 4 read-write */
+#define ESP_CONFIG4_BBTE 0x01 /* Back-to-back transfers (fsc) */
+#define ESP_CONFIG4_TEST 0x02 /* Transfer counter test mode (fsc) */
+#define ESP_CONFIG4_RADE 0x04 /* Active negation (am53c974/fsc) */
+#define ESP_CONFIG4_RAE 0x08 /* Act. negation REQ/ACK (am53c974) */
+#define ESP_CONFIG4_PWD 0x20 /* Reduced power feature (am53c974) */
+#define ESP_CONFIG4_GE0 0x40 /* Glitch eater bit 0 (am53c974) */
+#define ESP_CONFIG4_GE1 0x80 /* Glitch eater bit 1 (am53c974) */
+
+#define ESP_CONFIG_GE_12NS (0)
+#define ESP_CONFIG_GE_25NS (ESP_CONFIG_GE1)
+#define ESP_CONFIG_GE_35NS (ESP_CONFIG_GE0)
+#define ESP_CONFIG_GE_0NS (ESP_CONFIG_GE0 | ESP_CONFIG_GE1)
+
/* ESP command register read-write */
/* Group 1 commands: These may be sent at any point in time to the ESP
* chip. None of them can generate interrupts 'cept
@@ -195,10 +211,15 @@
#define ESP_TEST_TS 0x04 /* Tristate test mode */
/* ESP unique ID register read-only, found on fas236+fas100a only */
+#define ESP_UID_FAM 0xf8 /* ESP family bitmask */
+
+#define ESP_FAMILY(uid) (((uid) & ESP_UID_FAM) >> 3)
+
+/* Values for the ESP family bits */
#define ESP_UID_F100A 0x00 /* ESP FAS100A */
#define ESP_UID_F236 0x02 /* ESP FAS236 */
-#define ESP_UID_REV 0x07 /* ESP revision */
-#define ESP_UID_FAM 0xf8 /* ESP family */
+#define ESP_UID_HME 0x0a /* FAS HME */
+#define ESP_UID_FSC 0x14 /* NCR/Symbios Logic 53CF9x-2 */
/* ESP fifo flags register read-only */
/* Note that the following implies a 16 byte FIFO on the ESP. */
@@ -235,25 +256,26 @@
#define SYNC_DEFP_FAST 0x19 /* 10mb/s */
struct esp_cmd_priv {
- union {
- dma_addr_t dma_addr;
- int num_sg;
- } u;
-
+ int num_sg;
int cur_residue;
+ struct scatterlist *prv_sg;
struct scatterlist *cur_sg;
int tot_residue;
};
-#define ESP_CMD_PRIV(CMD) ((struct esp_cmd_priv *)(&(CMD)->SCp))
+#define ESP_CMD_PRIV(cmd) ((struct esp_cmd_priv *)scsi_cmd_priv(cmd))
+
+/* NOTE: this enum is ordered based on chip features! */
enum esp_rev {
- ESP100 = 0x00, /* NCR53C90 - very broken */
- ESP100A = 0x01, /* NCR53C90A */
- ESP236 = 0x02,
- FAS236 = 0x03,
- FAS100A = 0x04,
- FAST = 0x05,
- FASHME = 0x06,
+ ESP100, /* NCR53C90 - very broken */
+ ESP100A, /* NCR53C90A */
+ ESP236,
+ FAS236,
+ PCSCSI, /* AM53c974 */
+ FSC, /* NCR/Symbios Logic 53CF9x-2 */
+ FAS100A,
+ FAST,
+ FASHME,
};
struct esp_cmd_entry {
@@ -262,15 +284,17 @@ struct esp_cmd_entry {
struct scsi_cmnd *cmd;
unsigned int saved_cur_residue;
+ struct scatterlist *saved_prv_sg;
struct scatterlist *saved_cur_sg;
unsigned int saved_tot_residue;
u8 flags;
#define ESP_CMD_FLAG_WRITE 0x01 /* DMA is a write */
-#define ESP_CMD_FLAG_ABORT 0x02 /* being aborted */
#define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */
+#define ESP_CMD_FLAG_RESIDUAL 0x08 /* AM53c974 BLAST residual */
u8 tag[2];
+ u8 orig_tag[2];
u8 status;
u8 message;
@@ -282,7 +306,6 @@ struct esp_cmd_entry {
struct completion *eh_done;
};
-/* XXX make this configurable somehow XXX */
#define ESP_DEFAULT_TAGS 16
#define ESP_MAX_TARGET 16
@@ -348,19 +371,6 @@ struct esp_driver_ops {
void (*esp_write8)(struct esp *esp, u8 val, unsigned long reg);
u8 (*esp_read8)(struct esp *esp, unsigned long reg);
- /* Map and unmap DMA memory. Eventually the driver will be
- * converted to the generic DMA API as soon as SBUS is able to
- * cope with that. At such time we can remove this.
- */
- dma_addr_t (*map_single)(struct esp *esp, void *buf,
- size_t sz, int dir);
- int (*map_sg)(struct esp *esp, struct scatterlist *sg,
- int num_sg, int dir);
- void (*unmap_single)(struct esp *esp, dma_addr_t addr,
- size_t sz, int dir);
- void (*unmap_sg)(struct esp *esp, struct scatterlist *sg,
- int num_sg, int dir);
-
/* Return non-zero if there is an IRQ pending. Usually this
* status bit lives in the DMA controller sitting in front of
* the ESP. This has to be accurate or else the ESP interrupt
@@ -420,7 +430,7 @@ struct esp {
const struct esp_driver_ops *ops;
struct Scsi_Host *host;
- void *dev;
+ struct device *dev;
struct esp_cmd_entry *active_cmd;
@@ -444,7 +454,7 @@ struct esp {
u8 prev_soff;
u8 prev_stp;
u8 prev_cfg3;
- u8 __pad;
+ u8 num_tags;
struct list_head esp_cmd_pool;
@@ -465,6 +475,7 @@ struct esp {
u8 bursts;
u8 config1;
u8 config2;
+ u8 config4;
u8 scsi_id;
u32 scsi_id_mask;
@@ -474,10 +485,11 @@ struct esp {
u32 flags;
#define ESP_FLAG_DIFFERENTIAL 0x00000001
#define ESP_FLAG_RESETTING 0x00000002
-#define ESP_FLAG_DOING_SLOWCMD 0x00000004
#define ESP_FLAG_WIDE_CAPABLE 0x00000008
#define ESP_FLAG_QUICKIRQ_CHECK 0x00000010
#define ESP_FLAG_DISABLE_SYNC 0x00000020
+#define ESP_FLAG_USE_FIFO 0x00000040
+#define ESP_FLAG_NO_DMA_MAP 0x00000080
u8 select_state;
#define ESP_SELECT_NONE 0x00 /* Not selecting */
@@ -515,7 +527,7 @@ struct esp {
u32 min_period;
u32 radelay;
- /* Slow command state. */
+ /* ESP_CMD_SELAS command state */
u8 *cmd_bytes_ptr;
int cmd_bytes_left;
@@ -523,6 +535,11 @@ struct esp {
void *dma;
int dmarev;
+
+ /* These are used by esp_send_pio_cmd() */
+ u8 __iomem *fifo_reg;
+ int send_cmd_error;
+ u32 send_cmd_residual;
};
/* A front-end driver for the ESP chip should do the following in
@@ -551,16 +568,18 @@ struct esp {
* example, the DMA engine has to be reset before ESP can
* be programmed.
* 11) If necessary, call dev_set_drvdata() as needed.
- * 12) Call scsi_esp_register() with prepared 'esp' structure
- * and a device pointer if possible.
+ * 12) Call scsi_esp_register() with prepared 'esp' structure.
* 13) Check scsi_esp_register() return value, release all resources
* if an error was returned.
*/
-extern struct scsi_host_template scsi_esp_template;
-extern int scsi_esp_register(struct esp *, struct device *);
+extern const struct scsi_host_template scsi_esp_template;
+extern int scsi_esp_register(struct esp *);
extern void scsi_esp_unregister(struct esp *);
extern irqreturn_t scsi_esp_intr(int, void *);
extern void scsi_esp_cmd(struct esp *, u8);
+extern void esp_send_pio_cmd(struct esp *esp, u32 dma_addr, u32 esp_count,
+ u32 dma_count, int write, u8 cmd);
+
#endif /* !(_ESP_SCSI_H) */
diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index f6d37d0271f7..1183e80a09e7 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1,4 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_FCOE) += fcoe.o
obj-$(CONFIG_LIBFCOE) += libfcoe.o
-libfcoe-objs := fcoe_ctlr.o fcoe_transport.o
+libfcoe-objs := fcoe_ctlr.o fcoe_transport.o fcoe_sysfs.o
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 155d7b9bdeae..c8c5dfb3ba9a 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1,24 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -32,6 +19,8 @@
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/workqueue.h>
+#include <net/dcbnl.h>
+#include <net/dcbevent.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsicam.h>
#include <scsi/scsi_transport.h>
@@ -40,6 +29,7 @@
#include <scsi/fc/fc_encaps.h>
#include <scsi/fc/fc_fip.h>
+#include <scsi/fc/fc_fcoe.h>
#include <scsi/libfc.h>
#include <scsi/fc_frame.h>
@@ -52,35 +42,43 @@ MODULE_DESCRIPTION("FCoE");
MODULE_LICENSE("GPL v2");
/* Performance tuning parameters for fcoe */
-static unsigned int fcoe_ddp_min;
+static unsigned int fcoe_ddp_min = 4096;
module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \
"Direct Data Placement (DDP).");
-DEFINE_MUTEX(fcoe_config_mutex);
+unsigned int fcoe_debug_logging;
+module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-static struct workqueue_struct *fcoe_wq;
+static unsigned int fcoe_e_d_tov = 2 * 1000;
+module_param_named(e_d_tov, fcoe_e_d_tov, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(e_d_tov, "E_D_TOV in ms, default 2000");
-/* fcoe_percpu_clean completion. Waiter protected by fcoe_create_mutex */
-static DECLARE_COMPLETION(fcoe_flush_completion);
+static unsigned int fcoe_r_a_tov = 2 * 2 * 1000;
+module_param_named(r_a_tov, fcoe_r_a_tov, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(r_a_tov, "R_A_TOV in ms, default 4000");
+
+static DEFINE_MUTEX(fcoe_config_mutex);
+
+static struct workqueue_struct *fcoe_wq;
/* fcoe host list */
/* must only by accessed under the RTNL mutex */
-LIST_HEAD(fcoe_hostlist);
-DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
+static LIST_HEAD(fcoe_hostlist);
+static DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
/* Function Prototypes */
static int fcoe_reset(struct Scsi_Host *);
static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
static int fcoe_rcv(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
-static int fcoe_percpu_receive_thread(void *);
static void fcoe_percpu_clean(struct fc_lport *);
-static int fcoe_link_speed_update(struct fc_lport *);
static int fcoe_link_ok(struct fc_lport *);
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
static int fcoe_hostlist_add(const struct fc_lport *);
+static void fcoe_hostlist_del(const struct fc_lport *);
static int fcoe_device_notification(struct notifier_block *, ulong, void *);
static void fcoe_dev_setup(void);
@@ -90,6 +88,8 @@ static struct fcoe_interface
static int fcoe_fip_recv(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
+static int fcoe_fip_vlan_recv(struct sk_buff *, struct net_device *,
+ struct packet_type *, struct net_device *);
static void fcoe_fip_send(struct fcoe_ctlr *, struct sk_buff *);
static void fcoe_update_src_mac(struct fc_lport *, u8 *);
@@ -99,15 +99,22 @@ static void fcoe_destroy_work(struct work_struct *);
static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *,
unsigned int);
static int fcoe_ddp_done(struct fc_lport *, u16);
-
-static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *);
+static int fcoe_ddp_target(struct fc_lport *, u16, struct scatterlist *,
+ unsigned int);
+static int fcoe_dcb_app_notification(struct notifier_block *notifier,
+ ulong event, void *ptr);
static bool fcoe_match(struct net_device *netdev);
-static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode);
+static int fcoe_create(struct net_device *netdev, enum fip_mode fip_mode);
static int fcoe_destroy(struct net_device *netdev);
static int fcoe_enable(struct net_device *netdev);
static int fcoe_disable(struct net_device *netdev);
+/* fcoe_syfs control interface handlers */
+static int fcoe_ctlr_alloc(struct net_device *netdev);
+static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev);
+static void fcoe_ctlr_mode(struct fcoe_ctlr_device *ctlr_dev);
+
static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
u32 did, struct fc_frame *,
unsigned int op,
@@ -117,16 +124,14 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
void *, u32 timeout);
static void fcoe_recv_frame(struct sk_buff *skb);
-static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
-
/* notification function for packets from net device */
static struct notifier_block fcoe_notifier = {
.notifier_call = fcoe_device_notification,
};
-/* notification function for CPU hotplug events */
-static struct notifier_block fcoe_cpu_notifier = {
- .notifier_call = fcoe_cpu_callback,
+/* notification function for DCB events */
+static struct notifier_block dcb_notifier = {
+ .notifier_call = fcoe_dcb_app_notification,
};
static struct scsi_transport_template *fcoe_nport_scsi_transport;
@@ -137,24 +142,48 @@ static int fcoe_vport_create(struct fc_vport *, bool disabled);
static int fcoe_vport_disable(struct fc_vport *, bool disable);
static void fcoe_set_vport_symbolic_name(struct fc_vport *);
static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
-static int fcoe_validate_vport_create(struct fc_vport *);
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *);
+static void fcoe_vport_remove(struct fc_lport *);
+
+static struct fcoe_sysfs_function_template fcoe_sysfs_templ = {
+ .set_fcoe_ctlr_mode = fcoe_ctlr_mode,
+ .set_fcoe_ctlr_enabled = fcoe_ctlr_enabled,
+ .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb,
+ .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb,
+
+ .get_fcoe_fcf_selected = fcoe_fcf_get_selected,
+ .get_fcoe_fcf_vlan_id = fcoe_fcf_get_vlan_id,
+};
static struct libfc_function_template fcoe_libfc_fcn_templ = {
.frame_send = fcoe_xmit,
.ddp_setup = fcoe_ddp_setup,
.ddp_done = fcoe_ddp_done,
+ .ddp_target = fcoe_ddp_target,
.elsct_send = fcoe_elsct_send,
.get_lesb = fcoe_get_lesb,
.lport_set_port_id = fcoe_set_port_id,
};
-struct fc_function_template fcoe_nport_fc_functions = {
+static struct fc_function_template fcoe_nport_fc_functions = {
.show_host_node_name = 1,
.show_host_port_name = 1,
.show_host_supported_classes = 1,
.show_host_supported_fc4s = 1,
.show_host_active_fc4s = 1,
.show_host_maxframe_size = 1,
+ .show_host_serial_number = 1,
+ .show_host_manufacturer = 1,
+ .show_host_model = 1,
+ .show_host_model_description = 1,
+ .show_host_hardware_version = 1,
+ .show_host_driver_version = 1,
+ .show_host_firmware_version = 1,
+ .show_host_optionrom_version = 1,
.show_host_port_id = 1,
.show_host_supported_speeds = 1,
@@ -188,13 +217,21 @@ struct fc_function_template fcoe_nport_fc_functions = {
.bsg_request = fc_lport_bsg_request,
};
-struct fc_function_template fcoe_vport_fc_functions = {
+static struct fc_function_template fcoe_vport_fc_functions = {
.show_host_node_name = 1,
.show_host_port_name = 1,
.show_host_supported_classes = 1,
.show_host_supported_fc4s = 1,
.show_host_active_fc4s = 1,
.show_host_maxframe_size = 1,
+ .show_host_serial_number = 1,
+ .show_host_manufacturer = 1,
+ .show_host_model = 1,
+ .show_host_model_description = 1,
+ .show_host_hardware_version = 1,
+ .show_host_driver_version = 1,
+ .show_host_firmware_version = 1,
+ .show_host_optionrom_version = 1,
.show_host_port_id = 1,
.show_host_supported_speeds = 1,
@@ -223,23 +260,24 @@ struct fc_function_template fcoe_vport_fc_functions = {
.bsg_request = fc_lport_bsg_request,
};
-static struct scsi_host_template fcoe_shost_template = {
+static const struct scsi_host_template fcoe_shost_template = {
.module = THIS_MODULE,
.name = "FCoE Driver",
.proc_name = FCOE_NAME,
.queuecommand = fc_queuecommand,
+ .eh_timed_out = fc_eh_timed_out,
.eh_abort_handler = fc_eh_abort,
.eh_device_reset_handler = fc_eh_device_reset,
.eh_host_reset_handler = fc_eh_host_reset,
- .slave_alloc = fc_slave_alloc,
- .change_queue_depth = fc_change_queue_depth,
- .change_queue_type = fc_change_queue_type,
+ .sdev_init = fc_sdev_init,
+ .change_queue_depth = scsi_change_queue_depth,
.this_id = -1,
.cmd_per_lun = 3,
.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
- .use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = SG_ALL,
.max_sectors = 0xffff,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct libfc_cmd_priv),
};
/**
@@ -253,10 +291,10 @@ static struct scsi_host_template fcoe_shost_template = {
static int fcoe_interface_setup(struct fcoe_interface *fcoe,
struct net_device *netdev)
{
- struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
struct netdev_hw_addr *ha;
struct net_device *real_dev;
- u8 flogi_maddr[ETH_ALEN];
+ static const u8 flogi_maddr[ETH_ALEN] = FC_FCOE_FLOGI_MAC;
const struct net_device_ops *ops;
fcoe->netdev = netdev;
@@ -270,15 +308,15 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
}
/* Do not support for bonding device */
- if (netdev->priv_flags & IFF_BONDING && netdev->flags & IFF_MASTER) {
+ if (netif_is_bond_master(netdev)) {
FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");
return -EOPNOTSUPP;
}
/* look for SAN MAC address, if multiple SAN MACs exist, only
* use the first one for SPMA */
- real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
- vlan_dev_real_dev(netdev) : netdev;
+ real_dev = is_vlan_dev(netdev) ? vlan_dev_real_dev(netdev) : netdev;
+ fcoe->realdev = real_dev;
rcu_read_lock();
for_each_dev_addr(real_dev, ha) {
if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
@@ -299,7 +337,6 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
* or enter promiscuous mode if not capable of listening
* for multiple unicast MACs.
*/
- memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
dev_uc_add(netdev, flogi_maddr);
if (fip->spma)
dev_uc_add(netdev, fip->ctl_src_addr);
@@ -314,7 +351,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
* on the ethertype for the given device
*/
fcoe->fcoe_packet_type.func = fcoe_rcv;
- fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+ fcoe->fcoe_packet_type.type = htons(ETH_P_FCOE);
fcoe->fcoe_packet_type.dev = netdev;
dev_add_pack(&fcoe->fcoe_packet_type);
@@ -323,6 +360,12 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
fcoe->fip_packet_type.dev = netdev;
dev_add_pack(&fcoe->fip_packet_type);
+ if (netdev != real_dev) {
+ fcoe->fip_vlan_packet_type.func = fcoe_fip_vlan_recv;
+ fcoe->fip_vlan_packet_type.type = htons(ETH_P_FIP);
+ fcoe->fip_vlan_packet_type.dev = real_dev;
+ dev_add_pack(&fcoe->fip_vlan_packet_type);
+ }
return 0;
}
@@ -334,9 +377,12 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
* Returns: pointer to a struct fcoe_interface or NULL on error
*/
static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
- enum fip_state fip_mode)
+ enum fip_mode fip_mode)
{
+ struct fcoe_ctlr_device *ctlr_dev;
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
+ int size;
int err;
if (!try_module_get(THIS_MODULE)) {
@@ -346,104 +392,58 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
goto out;
}
- fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
- if (!fcoe) {
- FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
+ size = sizeof(struct fcoe_ctlr) + sizeof(struct fcoe_interface);
+ ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &fcoe_sysfs_templ,
+ size);
+ if (!ctlr_dev) {
+ FCOE_DBG("Failed to add fcoe_ctlr_device\n");
fcoe = ERR_PTR(-ENOMEM);
- goto out_nomod;
+ goto out_putmod;
}
+ ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ ctlr->cdev = ctlr_dev;
+ fcoe = fcoe_ctlr_priv(ctlr);
+
dev_hold(netdev);
- kref_init(&fcoe->kref);
/*
* Initialize FIP.
*/
- fcoe_ctlr_init(&fcoe->ctlr, fip_mode);
- fcoe->ctlr.send = fcoe_fip_send;
- fcoe->ctlr.update_mac = fcoe_update_src_mac;
- fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
+ fcoe_ctlr_init(ctlr, fip_mode);
+ ctlr->send = fcoe_fip_send;
+ ctlr->update_mac = fcoe_update_src_mac;
+ ctlr->get_src_addr = fcoe_get_src_mac;
err = fcoe_interface_setup(fcoe, netdev);
if (err) {
- fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
+ fcoe_ctlr_destroy(ctlr);
+ fcoe_ctlr_device_delete(ctlr_dev);
dev_put(netdev);
fcoe = ERR_PTR(err);
- goto out_nomod;
+ goto out_putmod;
}
goto out;
-out_nomod:
+out_putmod:
module_put(THIS_MODULE);
out:
return fcoe;
}
/**
- * fcoe_interface_release() - fcoe_port kref release function
- * @kref: Embedded reference count in an fcoe_interface struct
- */
-static void fcoe_interface_release(struct kref *kref)
-{
- struct fcoe_interface *fcoe;
- struct net_device *netdev;
-
- fcoe = container_of(kref, struct fcoe_interface, kref);
- netdev = fcoe->netdev;
- /* tear-down the FCoE controller */
- fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
- dev_put(netdev);
- module_put(THIS_MODULE);
-}
-
-/**
- * fcoe_interface_get() - Get a reference to a FCoE interface
- * @fcoe: The FCoE interface to be held
- */
-static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
-{
- kref_get(&fcoe->kref);
-}
-
-/**
- * fcoe_interface_put() - Put a reference to a FCoE interface
- * @fcoe: The FCoE interface to be released
- */
-static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
-{
- kref_put(&fcoe->kref, fcoe_interface_release);
-}
-
-/**
- * fcoe_interface_cleanup() - Clean up a FCoE interface
+ * fcoe_interface_remove() - remove FCoE interface from netdev
* @fcoe: The FCoE interface to be cleaned up
*
* Caller must be holding the RTNL mutex
*/
-void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
+static void fcoe_interface_remove(struct fcoe_interface *fcoe)
{
struct net_device *netdev = fcoe->netdev;
- struct fcoe_ctlr *fip = &fcoe->ctlr;
- u8 flogi_maddr[ETH_ALEN];
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
+ static const u8 flogi_maddr[ETH_ALEN] = FC_FCOE_FLOGI_MAC;
const struct net_device_ops *ops;
- struct fcoe_port *port = lport_priv(fcoe->ctlr.lp);
-
- FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
-
- /* Logout of the fabric */
- fc_fabric_logoff(fcoe->ctlr.lp);
-
- /* Cleanup the fc_lport */
- fc_lport_destroy(fcoe->ctlr.lp);
-
- /* Stop the transmit retry timer */
- del_timer_sync(&port->timer);
-
- /* Free existing transmit skbs */
- fcoe_clean_pending_queue(fcoe->ctlr.lp);
/*
* Don't listen for Ethernet packets anymore.
@@ -453,10 +453,11 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
*/
__dev_remove_pack(&fcoe->fcoe_packet_type);
__dev_remove_pack(&fcoe->fip_packet_type);
+ if (netdev != fcoe->realdev)
+ __dev_remove_pack(&fcoe->fip_vlan_packet_type);
synchronize_net();
/* Delete secondary MAC addresses */
- memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
dev_uc_del(netdev, flogi_maddr);
if (fip->spma)
dev_uc_del(netdev, fip->ctl_src_addr);
@@ -466,9 +467,6 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
} else
dev_mc_del(netdev, FIP_ALL_ENODE_MACS);
- if (!is_zero_ether_addr(port->data_src_addr))
- dev_uc_del(netdev, port->data_src_addr);
-
/* Tell the LLD we are done w/ FCoE */
ops = netdev->netdev_ops;
if (ops->ndo_fcoe_disable) {
@@ -476,7 +474,25 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"
" specific feature for LLD.\n");
}
- fcoe_interface_put(fcoe);
+ fcoe->removed = 1;
+}
+
+
+/**
+ * fcoe_interface_cleanup() - Clean up a FCoE interface
+ * @fcoe: The FCoE interface to be cleaned up
+ */
+static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
+{
+ struct net_device *netdev = fcoe->netdev;
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
+
+ /* Release the self-reference taken during fcoe_interface_create() */
+ /* tear-down the FCoE controller */
+ fcoe_ctlr_destroy(fip);
+ scsi_host_put(fip->lp->host);
+ dev_put(netdev);
+ module_put(THIS_MODULE);
}
/**
@@ -484,7 +500,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
* @skb: The receive skb
* @netdev: The associated net device
* @ptype: The packet_type structure which was used to register this handler
- * @orig_dev: The original net_device the the skb was received on.
+ * @orig_dev: The original net_device the skb was received on.
* (in case dev is a bond)
*
* Returns: 0 for success
@@ -494,21 +510,74 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
struct net_device *orig_dev)
{
struct fcoe_interface *fcoe;
+ struct fcoe_ctlr *ctlr;
fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type);
- fcoe_ctlr_recv(&fcoe->ctlr, skb);
+ ctlr = fcoe_to_ctlr(fcoe);
+ fcoe_ctlr_recv(ctlr, skb);
return 0;
}
/**
+ * fcoe_fip_vlan_recv() - Handler for received FIP VLAN discovery frames
+ * @skb: The receive skb
+ * @netdev: The associated net device
+ * @ptype: The packet_type structure which was used to register this handler
+ * @orig_dev: The original net_device the skb was received on.
+ * (in case dev is a bond)
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_fip_vlan_recv(struct sk_buff *skb, struct net_device *netdev,
+ struct packet_type *ptype,
+ struct net_device *orig_dev)
+{
+ struct fcoe_interface *fcoe;
+ struct fcoe_ctlr *ctlr;
+
+ fcoe = container_of(ptype, struct fcoe_interface, fip_vlan_packet_type);
+ ctlr = fcoe_to_ctlr(fcoe);
+ fcoe_ctlr_recv(ctlr, skb);
+ return 0;
+}
+
+/**
+ * fcoe_port_send() - Send an Ethernet-encapsulated FIP/FCoE frame
+ * @port: The FCoE port
+ * @skb: The FIP/FCoE packet to be sent
+ */
+static void fcoe_port_send(struct fcoe_port *port, struct sk_buff *skb)
+{
+ if (port->fcoe_pending_queue.qlen)
+ fcoe_check_wait_queue(port->lport, skb);
+ else if (fcoe_start_io(skb))
+ fcoe_check_wait_queue(port->lport, skb);
+}
+
+/**
* fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame
* @fip: The FCoE controller
* @skb: The FIP packet to be sent
*/
static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- skb->dev = fcoe_from_ctlr(fip)->netdev;
- dev_queue_xmit(skb);
+ struct fcoe_interface *fcoe = fcoe_from_ctlr(fip);
+ struct fip_frame {
+ struct ethhdr eth;
+ struct fip_header fip;
+ } __packed *frame;
+
+ /*
+ * Use default VLAN for FIP VLAN discovery protocol
+ */
+ frame = (struct fip_frame *)skb->data;
+ if (ntohs(frame->eth.h_proto) == ETH_P_FIP &&
+ ntohs(frame->fip.fip_op) == FIP_OP_VLAN &&
+ fcoe->realdev != fcoe->netdev)
+ skb->dev = fcoe->realdev;
+ else
+ skb->dev = fcoe->netdev;
+ fcoe_port_send(lport_priv(fip->lp), skb);
}
/**
@@ -524,13 +593,11 @@ static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->priv;
- rtnl_lock();
if (!is_zero_ether_addr(port->data_src_addr))
dev_uc_del(fcoe->netdev, port->data_src_addr);
if (!is_zero_ether_addr(addr))
dev_uc_add(fcoe->netdev, addr);
memcpy(port->data_src_addr, addr, ETH_ALEN);
- rtnl_unlock();
}
/**
@@ -556,8 +623,8 @@ static int fcoe_lport_config(struct fc_lport *lport)
lport->qfull = 0;
lport->max_retry_count = 3;
lport->max_rport_retry_count = 3;
- lport->e_d_tov = 2 * 1000; /* FC-FS default */
- lport->r_a_tov = 2 * 2 * 1000;
+ lport->e_d_tov = fcoe_e_d_tov;
+ lport->r_a_tov = fcoe_r_a_tov;
lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
lport->does_npiv = 1;
@@ -577,24 +644,7 @@ static int fcoe_lport_config(struct fc_lport *lport)
return 0;
}
-/**
- * fcoe_get_wwn() - Get the world wide name from LLD if it supports it
- * @netdev: the associated net device
- * @wwn: the output WWN
- * @type: the type of WWN (WWPN or WWNN)
- *
- * Returns: 0 for success
- */
-static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
-{
- const struct net_device_ops *ops = netdev->netdev_ops;
-
- if (ops->ndo_fcoe_get_wwn)
- return ops->ndo_fcoe_get_wwn(netdev, wwn, type);
- return -EINVAL;
-}
-
-/**
+/*
* fcoe_netdev_features_change - Updates the lport's offload flags based
* on the LLD netdev's FCoE feature flags
*/
@@ -617,7 +667,7 @@ static void fcoe_netdev_features_change(struct fc_lport *lport,
if (netdev->features & NETIF_F_FSO) {
lport->seq_offload = 1;
- lport->lso_max = netdev->gso_max_size;
+ lport->lso_max = min(netdev->gso_max_size, GSO_LEGACY_MAX_SIZE);
FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
lport->lso_max);
} else {
@@ -652,11 +702,19 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
u32 mfs;
u64 wwnn, wwpn;
struct fcoe_interface *fcoe;
+ struct fcoe_ctlr *ctlr;
struct fcoe_port *port;
/* Setup lport private data to point to fcoe softc */
port = lport_priv(lport);
fcoe = port->priv;
+ ctlr = fcoe_to_ctlr(fcoe);
+
+ /* Figure out the VLAN ID, if any */
+ if (is_vlan_dev(netdev))
+ lport->vlan = vlan_dev_vlan_id(netdev);
+ else
+ lport->vlan = 0;
/*
* Determine max frame size based on underlying device and optional
@@ -664,7 +722,7 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
* will return 0, so do this first.
*/
mfs = netdev->mtu;
- if (netdev->features & NETIF_F_FCOE_MTU) {
+ if (netdev->fcoe_mtu) {
mfs = FCOE_MTU;
FCOE_NETDEV_DBG(netdev, "Supports FCOE_MTU of %d bytes\n", mfs);
}
@@ -677,16 +735,16 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
skb_queue_head_init(&port->fcoe_pending_queue);
port->fcoe_pending_queue_active = 0;
- setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
+ timer_setup(&port->timer, fcoe_queue_timer, 0);
fcoe_link_speed_update(lport);
if (!lport->vport) {
if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
- wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+ wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0);
fc_set_wwnn(lport, wwnn);
if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
- wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr,
+ wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
2, 0);
fc_set_wwpn(lport, wwpn);
}
@@ -736,6 +794,86 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
return 0;
}
+
+/**
+ * fcoe_fdmi_info() - Get FDMI related info from net devive for SW FCoE
+ * @lport: The local port that is associated with the net device
+ * @netdev: The associated net device
+ *
+ * Must be called after fcoe_shost_config() as it will use local port mutex
+ *
+ */
+static void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev)
+{
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
+ struct net_device *realdev;
+ int rc;
+
+ port = lport_priv(lport);
+ fcoe = port->priv;
+ realdev = fcoe->realdev;
+
+ /* No FDMI state m/c for NPIV ports */
+ if (lport->vport)
+ return;
+
+ if (realdev->netdev_ops->ndo_fcoe_get_hbainfo) {
+ struct netdev_fcoe_hbainfo *fdmi;
+ fdmi = kzalloc(sizeof(*fdmi), GFP_KERNEL);
+ if (!fdmi)
+ return;
+
+ rc = realdev->netdev_ops->ndo_fcoe_get_hbainfo(realdev,
+ fdmi);
+ if (rc) {
+ printk(KERN_INFO "fcoe: Failed to retrieve FDMI "
+ "information from netdev.\n");
+ return;
+ }
+
+ snprintf(fc_host_serial_number(lport->host),
+ FC_SERIAL_NUMBER_SIZE,
+ "%s",
+ fdmi->serial_number);
+ snprintf(fc_host_manufacturer(lport->host),
+ FC_SERIAL_NUMBER_SIZE,
+ "%s",
+ fdmi->manufacturer);
+ snprintf(fc_host_model(lport->host),
+ FC_SYMBOLIC_NAME_SIZE,
+ "%s",
+ fdmi->model);
+ snprintf(fc_host_model_description(lport->host),
+ FC_SYMBOLIC_NAME_SIZE,
+ "%s",
+ fdmi->model_description);
+ snprintf(fc_host_hardware_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->hardware_version);
+ snprintf(fc_host_driver_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->driver_version);
+ snprintf(fc_host_optionrom_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->optionrom_version);
+ snprintf(fc_host_firmware_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi->firmware_version);
+
+ /* Enable FDMI lport states */
+ lport->fdmi_enabled = 1;
+ kfree(fdmi);
+ } else {
+ lport->fdmi_enabled = 0;
+ printk(KERN_INFO "fcoe: No FDMI support.\n");
+ }
+}
+
/**
* fcoe_oem_match() - The match routine for the offloaded exchange manager
* @fp: The I/O frame
@@ -749,12 +887,28 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
* The offload EM that this routine is associated with will handle any
* packets that are for SCSI read requests.
*
+ * This has been enhanced to work when FCoE stack is operating in target
+ * mode.
+ *
* Returns: True for read types I/O, otherwise returns false.
*/
-bool fcoe_oem_match(struct fc_frame *fp)
+static bool fcoe_oem_match(struct fc_frame *fp)
{
- return fc_fcp_is_read(fr_fsp(fp)) &&
- (fr_fsp(fp)->data_len > fcoe_ddp_min);
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fcp_cmnd *fcp;
+
+ if (fc_fcp_is_read(fr_fsp(fp)) &&
+ (fr_fsp(fp)->data_len > fcoe_ddp_min))
+ return true;
+ else if ((fr_fsp(fp) == NULL) &&
+ (fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) &&
+ (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)) {
+ fcp = fc_frame_payload_get(fp, sizeof(*fcp));
+ if ((fcp->fc_flags & FCP_CFL_WRDATA) &&
+ (ntohl(fcp->fc_dl) > fcoe_ddp_min))
+ return true;
+ }
+ return false;
}
/**
@@ -786,13 +940,13 @@ static inline int fcoe_em_config(struct fc_lport *lport)
* Reuse existing offload em instance in case
* it is already allocated on real eth device
*/
- if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+ if (is_vlan_dev(fcoe->netdev))
cur_real_dev = vlan_dev_real_dev(fcoe->netdev);
else
cur_real_dev = fcoe->netdev;
list_for_each_entry(oldfcoe, &fcoe_hostlist, list) {
- if (oldfcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+ if (is_vlan_dev(oldfcoe->netdev))
old_real_dev = vlan_dev_real_dev(oldfcoe->netdev);
else
old_real_dev = oldfcoe->netdev;
@@ -841,9 +995,36 @@ skip_oem:
* fcoe_if_destroy() - Tear down a SW FCoE instance
* @lport: The local port to be destroyed
*
+ * Locking: Must be called with the RTNL mutex held.
+ *
*/
static void fcoe_if_destroy(struct fc_lport *lport)
{
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->priv;
+ struct net_device *netdev = fcoe->netdev;
+
+ FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
+
+ /* Logout of the fabric */
+ fc_fabric_logoff(lport);
+
+ /* Cleanup the fc_lport */
+ fc_lport_destroy(lport);
+
+ /* Stop the transmit retry timer */
+ timer_delete_sync(&port->timer);
+
+ /* Free existing transmit skbs */
+ fcoe_clean_pending_queue(lport);
+
+ if (!is_zero_ether_addr(port->data_src_addr))
+ dev_uc_del(netdev, port->data_src_addr);
+ if (lport->vport)
+ synchronize_net();
+ else
+ fcoe_interface_remove(fcoe);
+
/* Free queued packets for the per-CPU receive threads */
fcoe_percpu_clean(lport);
@@ -860,8 +1041,12 @@ static void fcoe_if_destroy(struct fc_lport *lport)
/* Free memory used by statistical counters */
fc_lport_free_stats(lport);
- /* Release the Scsi_Host */
- scsi_host_put(lport->host);
+ /*
+ * Release the Scsi_Host for vport but hold on to
+ * master lport until it fcoe interface fully cleaned-up.
+ */
+ if (lport->vport)
+ scsi_host_put(lport->host);
}
/**
@@ -887,6 +1072,28 @@ static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid,
}
/**
+ * fcoe_ddp_target() - Call a LLD's ddp_target through the net device
+ * @lport: The local port to setup DDP for
+ * @xid: The exchange ID for this DDP transfer
+ * @sgl: The scatterlist describing this transfer
+ * @sgc: The number of sg items
+ *
+ * Returns: 0 if the DDP context was not configured
+ */
+static int fcoe_ddp_target(struct fc_lport *lport, u16 xid,
+ struct scatterlist *sgl, unsigned int sgc)
+{
+ struct net_device *netdev = fcoe_netdev(lport);
+
+ if (netdev->netdev_ops->ndo_fcoe_ddp_target)
+ return netdev->netdev_ops->ndo_fcoe_ddp_target(netdev, xid,
+ sgl, sgc);
+
+ return 0;
+}
+
+
+/**
* fcoe_ddp_done() - Call a LLD's ddp_done through the net device
* @lport: The local port to complete DDP on
* @xid: The exchange ID for this DDP transfer
@@ -915,6 +1122,7 @@ static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
struct device *parent, int npiv)
{
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
struct net_device *netdev = fcoe->netdev;
struct fc_lport *lport, *n_port;
struct fcoe_port *port;
@@ -941,10 +1149,17 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
port = lport_priv(lport);
port->lport = lport;
port->priv = fcoe;
+ port->get_netdev = fcoe_netdev;
port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH;
port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH;
INIT_WORK(&port->destroy_work, fcoe_destroy_work);
+ /*
+ * Need to add the lport to the hostlist
+ * so we catch NETDEV_CHANGE events.
+ */
+ fcoe_hostlist_add(lport);
+
/* configure a fc_lport including the exchange manager */
rc = fcoe_lport_config(lport);
if (rc) {
@@ -978,13 +1193,16 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
}
/* Initialize the library */
- rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1);
+ rc = fcoe_libfc_config(lport, ctlr, &fcoe_libfc_fcn_templ, 1);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
"interface\n");
goto out_lp_destroy;
}
+ /* Initialized FDMI information */
+ fcoe_fdmi_info(lport, netdev);
+
/*
* fcoe_em_alloc() and fcoe_hostlist_add() both
* need to be atomic with respect to other changes to the
@@ -1008,12 +1226,12 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
goto out_lp_destroy;
}
- fcoe_interface_get(fcoe);
return lport;
out_lp_destroy:
fc_exch_mgr_free(lport);
out_host_put:
+ fcoe_hostlist_del(lport);
scsi_host_put(lport->host);
out:
return ERR_PTR(rc);
@@ -1031,15 +1249,21 @@ static int __init fcoe_if_init(void)
/* attach to scsi transport */
fcoe_nport_scsi_transport =
fc_attach_transport(&fcoe_nport_fc_functions);
+ if (!fcoe_nport_scsi_transport)
+ goto err;
+
fcoe_vport_scsi_transport =
fc_attach_transport(&fcoe_vport_fc_functions);
-
- if (!fcoe_nport_scsi_transport) {
- printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
- return -ENODEV;
- }
+ if (!fcoe_vport_scsi_transport)
+ goto err_vport;
return 0;
+
+err_vport:
+ fc_release_transport(fcoe_nport_scsi_transport);
+err:
+ printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
+ return -ENODEV;
}
/**
@@ -1049,7 +1273,7 @@ static int __init fcoe_if_init(void)
*
* Returns: 0 on success
*/
-int __exit fcoe_if_exit(void)
+static int __exit fcoe_if_exit(void)
{
fc_release_transport(fcoe_nport_scsi_transport);
fc_release_transport(fcoe_vport_scsi_transport);
@@ -1058,151 +1282,21 @@ int __exit fcoe_if_exit(void)
return 0;
}
-/**
- * fcoe_percpu_thread_create() - Create a receive thread for an online CPU
- * @cpu: The CPU index of the CPU to create a receive thread for
- */
-static void fcoe_percpu_thread_create(unsigned int cpu)
-{
- struct fcoe_percpu_s *p;
- struct task_struct *thread;
-
- p = &per_cpu(fcoe_percpu, cpu);
-
- thread = kthread_create(fcoe_percpu_receive_thread,
- (void *)p, "fcoethread/%d", cpu);
-
- if (likely(!IS_ERR(thread))) {
- kthread_bind(thread, cpu);
- wake_up_process(thread);
-
- spin_lock_bh(&p->fcoe_rx_list.lock);
- p->thread = thread;
- spin_unlock_bh(&p->fcoe_rx_list.lock);
- }
-}
-
-/**
- * fcoe_percpu_thread_destroy() - Remove the receive thread of a CPU
- * @cpu: The CPU index of the CPU whose receive thread is to be destroyed
- *
- * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
- * current CPU's Rx thread. If the thread being destroyed is bound to
- * the CPU processing this context the skbs will be freed.
- */
-static void fcoe_percpu_thread_destroy(unsigned int cpu)
+static void fcoe_thread_cleanup_local(unsigned int cpu)
{
- struct fcoe_percpu_s *p;
- struct task_struct *thread;
struct page *crc_eof;
- struct sk_buff *skb;
-#ifdef CONFIG_SMP
- struct fcoe_percpu_s *p0;
- unsigned targ_cpu = get_cpu();
-#endif /* CONFIG_SMP */
-
- FCOE_DBG("Destroying receive thread for CPU %d\n", cpu);
+ struct fcoe_percpu_s *p;
- /* Prevent any new skbs from being queued for this CPU. */
- p = &per_cpu(fcoe_percpu, cpu);
+ p = per_cpu_ptr(&fcoe_percpu, cpu);
spin_lock_bh(&p->fcoe_rx_list.lock);
- thread = p->thread;
- p->thread = NULL;
crc_eof = p->crc_eof_page;
p->crc_eof_page = NULL;
p->crc_eof_offset = 0;
spin_unlock_bh(&p->fcoe_rx_list.lock);
-#ifdef CONFIG_SMP
- /*
- * Don't bother moving the skb's if this context is running
- * on the same CPU that is having its thread destroyed. This
- * can easily happen when the module is removed.
- */
- if (cpu != targ_cpu) {
- p0 = &per_cpu(fcoe_percpu, targ_cpu);
- spin_lock_bh(&p0->fcoe_rx_list.lock);
- if (p0->thread) {
- FCOE_DBG("Moving frames from CPU %d to CPU %d\n",
- cpu, targ_cpu);
-
- while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
- __skb_queue_tail(&p0->fcoe_rx_list, skb);
- spin_unlock_bh(&p0->fcoe_rx_list.lock);
- } else {
- /*
- * The targeted CPU is not initialized and cannot accept
- * new skbs. Unlock the targeted CPU and drop the skbs
- * on the CPU that is going offline.
- */
- while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
- kfree_skb(skb);
- spin_unlock_bh(&p0->fcoe_rx_list.lock);
- }
- } else {
- /*
- * This scenario occurs when the module is being removed
- * and all threads are being destroyed. skbs will continue
- * to be shifted from the CPU thread that is being removed
- * to the CPU thread associated with the CPU that is processing
- * the module removal. Once there is only one CPU Rx thread it
- * will reach this case and we will drop all skbs and later
- * stop the thread.
- */
- spin_lock_bh(&p->fcoe_rx_list.lock);
- while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
- kfree_skb(skb);
- spin_unlock_bh(&p->fcoe_rx_list.lock);
- }
- put_cpu();
-#else
- /*
- * This a non-SMP scenario where the singular Rx thread is
- * being removed. Free all skbs and stop the thread.
- */
- spin_lock_bh(&p->fcoe_rx_list.lock);
- while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
- kfree_skb(skb);
- spin_unlock_bh(&p->fcoe_rx_list.lock);
-#endif
-
- if (thread)
- kthread_stop(thread);
-
if (crc_eof)
put_page(crc_eof);
-}
-
-/**
- * fcoe_cpu_callback() - Handler for CPU hotplug events
- * @nfb: The callback data block
- * @action: The event triggering the callback
- * @hcpu: The index of the CPU that the event is for
- *
- * This creates or destroys per-CPU data for fcoe
- *
- * Returns NOTIFY_OK always.
- */
-static int fcoe_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
-{
- unsigned cpu = (unsigned long)hcpu;
-
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- FCOE_DBG("CPU %x online: Create Rx thread\n", cpu);
- fcoe_percpu_thread_create(cpu);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- FCOE_DBG("CPU %x offline: Remove Rx thread\n", cpu);
- fcoe_percpu_thread_destroy(cpu);
- break;
- default:
- break;
- }
- return NOTIFY_OK;
+ flush_work(&p->work);
}
/**
@@ -1217,11 +1311,12 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,
*
* Returns: 0 for success
*/
-int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
+static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *ptype, struct net_device *olddev)
{
struct fc_lport *lport;
struct fcoe_rcv_info *fr;
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
struct fc_frame_header *fh;
struct fcoe_percpu_s *fps;
@@ -1229,24 +1324,31 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
unsigned int cpu;
fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
- lport = fcoe->ctlr.lp;
+ ctlr = fcoe_to_ctlr(fcoe);
+ lport = ctlr->lp;
if (unlikely(!lport)) {
- FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
+ FCOE_NETDEV_DBG(netdev, "Cannot find hba structure\n");
goto err2;
}
if (!lport->link_up)
goto err2;
- FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p "
- "data:%p tail:%p end:%p sum:%d dev:%s",
+ FCOE_NETDEV_DBG(netdev,
+ "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",
skb->len, skb->data_len, skb->head, skb->data,
skb_tail_pointer(skb), skb_end_pointer(skb),
skb->csum, skb->dev ? skb->dev->name : "<NULL>");
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+
+ if (skb == NULL)
+ return NET_RX_DROP;
+
eh = eth_hdr(skb);
- if (is_fip_mode(&fcoe->ctlr) &&
- compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) {
+ if (is_fip_mode(ctlr) &&
+ !ether_addr_equal(eh->h_source, ctlr->dest_addr)) {
FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",
eh->h_source);
goto err;
@@ -1271,73 +1373,51 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
fr = fcoe_dev_from_skb(skb);
fr->fr_dev = lport;
- fr->ptype = ptype;
/*
* In case the incoming frame's exchange is originated from
* the initiator, then received frame's exchange id is ANDed
* with fc_cpu_mask bits to get the same cpu on which exchange
- * was originated, otherwise just use the current cpu.
+ * was originated, otherwise select cpu using rx exchange id
+ * or fcoe_select_cpu().
*/
if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)
cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask;
- else
- cpu = smp_processor_id();
-
- fps = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&fps->fcoe_rx_list.lock);
- if (unlikely(!fps->thread)) {
- /*
- * The targeted CPU is not ready, let's target
- * the first CPU now. For non-SMP systems this
- * will check the same CPU twice.
- */
- FCOE_NETDEV_DBG(netdev, "CPU is online, but no receive thread "
- "ready for incoming skb- using first online "
- "CPU.\n");
-
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- cpu = cpumask_first(cpu_online_mask);
- fps = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&fps->fcoe_rx_list.lock);
- if (!fps->thread) {
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- goto err;
- }
+ else {
+ if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)
+ cpu = skb->alloc_cpu;
+ else
+ cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask;
}
+ if (cpu >= nr_cpu_ids)
+ goto err;
+
+ fps = &per_cpu(fcoe_percpu, cpu);
+ spin_lock(&fps->fcoe_rx_list.lock);
/*
* We now have a valid CPU that we're targeting for
* this skb. We also have this receive thread locked,
* so we're free to queue skbs into it's queue.
*/
- /* If this is a SCSI-FCP frame, and this is already executing on the
- * correct CPU, and the queue for this CPU is empty, then go ahead
- * and process the frame directly in the softirq context.
- * This lets us process completions without context switching from the
- * NET_RX softirq, to our receive processing thread, and then back to
- * BLOCK softirq context.
+ /*
+ * Note: We used to have a set of conditions under which we would
+ * call fcoe_recv_frame directly, rather than queuing to the rx list
+ * as it could save a few cycles, but doing so is prohibited, as
+ * fcoe_recv_frame has several paths that may sleep, which is forbidden
+ * in softirq context.
*/
- if (fh->fh_type == FC_TYPE_FCP &&
- cpu == smp_processor_id() &&
- skb_queue_empty(&fps->fcoe_rx_list)) {
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- fcoe_recv_frame(skb);
- } else {
- __skb_queue_tail(&fps->fcoe_rx_list, skb);
- if (fps->fcoe_rx_list.qlen == 1)
- wake_up_process(fps->thread);
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- }
+ __skb_queue_tail(&fps->fcoe_rx_list, skb);
+ schedule_work_on(cpu, &fps->work);
+ spin_unlock(&fps->fcoe_rx_list.lock);
- return 0;
+ return NET_RX_SUCCESS;
err:
- per_cpu_ptr(lport->dev_stats, get_cpu())->ErrorFrames++;
- put_cpu();
+ this_cpu_inc(lport->stats->ErrorFrames);
err2:
kfree_skb(skb);
- return -1;
+ return NET_RX_DROP;
}
/**
@@ -1352,9 +1432,10 @@ static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen)
struct fcoe_percpu_s *fps;
int rc;
- fps = &get_cpu_var(fcoe_percpu);
+ local_lock(&fcoe_percpu.lock);
+ fps = this_cpu_ptr(&fcoe_percpu);
rc = fcoe_get_paged_crc_eof(skb, tlen, fps);
- put_cpu_var(fcoe_percpu);
+ local_unlock(&fcoe_percpu.lock);
return rc;
}
@@ -1366,20 +1447,20 @@ static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen)
*
* Return: 0 for success
*/
-int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
+static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
{
int wlen;
u32 crc;
struct ethhdr *eh;
struct fcoe_crc_eof *cp;
struct sk_buff *skb;
- struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
unsigned int hlen; /* header length implies the version */
unsigned int tlen; /* trailer length */
unsigned int elen; /* eth header, may include vlan */
struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->priv;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
u8 sof, eof;
struct fcoe_hdr *hp;
@@ -1387,7 +1468,6 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
fh = fc_frame_header_get(fp);
skb = fp_skb(fp);
- wlen = skb->len / FCOE_WORD_TO_BYTE;
if (!lport->link_up) {
kfree_skb(skb);
@@ -1395,7 +1475,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
}
if (unlikely(fh->fh_type == FC_TYPE_ELS) &&
- fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
+ fcoe_ctlr_els_send(ctlr, lport, skb))
return 0;
sof = fr_sof(fp);
@@ -1425,10 +1505,9 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
return -ENOMEM;
}
frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
- cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
- + frag->page_offset;
+ cp = kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag);
} else {
- cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+ cp = skb_put(skb, tlen);
}
memset(cp, 0, sizeof(*cp));
@@ -1436,7 +1515,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
cp->fcoe_crc32 = cpu_to_le32(~crc);
if (skb_is_nonlinear(skb)) {
- kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+ kunmap_atomic(cp);
cp = NULL;
}
@@ -1446,17 +1525,26 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
skb_reset_network_header(skb);
skb->mac_len = elen;
skb->protocol = htons(ETH_P_FCOE);
- skb->dev = fcoe->netdev;
+ skb->priority = fcoe->priority;
+
+ if (is_vlan_dev(fcoe->netdev) &&
+ fcoe->realdev->features & NETIF_F_HW_VLAN_CTAG_TX) {
+ /* must set skb->dev before calling vlan_put_tag */
+ skb->dev = fcoe->realdev;
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ vlan_dev_vlan_id(fcoe->netdev));
+ } else
+ skb->dev = fcoe->netdev;
/* fill up mac and fcoe headers */
eh = eth_hdr(skb);
eh->h_proto = htons(ETH_P_FCOE);
- memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
- if (fcoe->ctlr.map_dest)
+ memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
+ if (ctlr->map_dest)
memcpy(eh->h_dest + 3, fh->fh_d_id, 3);
- if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
- memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
+ if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
+ memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
else
memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
@@ -1475,31 +1563,16 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
skb_shinfo(skb)->gso_size = 0;
}
/* update tx stats: regardless if LLD fails */
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->TxFrames++;
- stats->TxWords += wlen;
- put_cpu();
+ this_cpu_inc(lport->stats->TxFrames);
+ this_cpu_add(lport->stats->TxWords, wlen);
/* send down to lld */
fr_dev(fp) = lport;
- if (port->fcoe_pending_queue.qlen)
- fcoe_check_wait_queue(lport, skb);
- else if (fcoe_start_io(skb))
- fcoe_check_wait_queue(lport, skb);
-
+ fcoe_port_send(port, skb);
return 0;
}
/**
- * fcoe_percpu_flush_done() - Indicate per-CPU queue flush completion
- * @skb: The completed skb (argument required by destructor)
- */
-static void fcoe_percpu_flush_done(struct sk_buff *skb)
-{
- complete(&fcoe_flush_completion);
-}
-
-/**
* fcoe_filter_frames() - filter out bad fcoe frames, i.e. bad CRC
* @lport: The local port the frame was received on
* @fp: The received frame
@@ -1509,10 +1582,10 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
static inline int fcoe_filter_frames(struct fc_lport *lport,
struct fc_frame *fp)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
struct fc_frame_header *fh;
struct sk_buff *skb = (struct sk_buff *)fp;
- struct fcoe_dev_stats *stats;
/*
* We only check CRC if no offload is available and if it is
@@ -1524,13 +1597,13 @@ static inline int fcoe_filter_frames(struct fc_lport *lport,
else
fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
- fh = (struct fc_frame_header *) skb_transport_header(skb);
fh = fc_frame_header_get(fp);
if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP)
return 0;
fcoe = ((struct fcoe_port *)lport_priv(lport))->priv;
- if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
+ ctlr = fcoe_to_ctlr(fcoe);
+ if (is_fip_mode(ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
return -EINVAL;
@@ -1542,9 +1615,7 @@ static inline int fcoe_filter_frames(struct fc_lport *lport,
return 0;
}
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->InvalidCRCCount++;
- if (stats->InvalidCRCCount < 5)
+ if (this_cpu_inc_return(lport->stats->InvalidCRCCount) < 5)
printk(KERN_WARNING "fcoe: dropping frame with CRC error\n");
return -EINVAL;
}
@@ -1558,31 +1629,26 @@ static void fcoe_recv_frame(struct sk_buff *skb)
u32 fr_len;
struct fc_lport *lport;
struct fcoe_rcv_info *fr;
- struct fcoe_dev_stats *stats;
struct fcoe_crc_eof crc_eof;
struct fc_frame *fp;
- struct fcoe_port *port;
struct fcoe_hdr *hp;
fr = fcoe_dev_from_skb(skb);
lport = fr->fr_dev;
if (unlikely(!lport)) {
- if (skb->destructor != fcoe_percpu_flush_done)
- FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
+ FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb\n");
kfree_skb(skb);
return;
}
- FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
- "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
+ FCOE_NETDEV_DBG(skb->dev,
+ "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n",
skb->len, skb->data_len,
skb->head, skb->data, skb_tail_pointer(skb),
skb_end_pointer(skb), skb->csum,
skb->dev ? skb->dev->name : "<NULL>");
- port = lport_priv(lport);
- if (skb_is_nonlinear(skb))
- skb_linearize(skb); /* not ideal */
+ skb_linearize(skb); /* check for skb_is_nonlinear is within skb_linearize */
/*
* Frame length checks and setting up the header pointers
@@ -1590,9 +1656,11 @@ static void fcoe_recv_frame(struct sk_buff *skb)
*/
hp = (struct fcoe_hdr *) skb_network_header(skb);
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
- if (stats->ErrorFrames < 5)
+ struct fc_stats *stats;
+
+ stats = per_cpu_ptr(lport->stats, raw_smp_processor_id());
+ if (READ_ONCE(stats->ErrorFrames) < 5)
printk(KERN_WARNING "fcoe: FCoE version "
"mismatch: The frame has "
"version %x, but the "
@@ -1605,8 +1673,8 @@ static void fcoe_recv_frame(struct sk_buff *skb)
skb_pull(skb, sizeof(struct fcoe_hdr));
fr_len = skb->len - sizeof(struct fcoe_crc_eof);
- stats->RxFrames++;
- stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+ this_cpu_inc(lport->stats->RxFrames);
+ this_cpu_add(lport->stats->RxWords, fr_len / FCOE_WORD_TO_BYTE);
fp = (struct fc_frame *)skb;
fc_frame_init(fp);
@@ -1622,45 +1690,37 @@ static void fcoe_recv_frame(struct sk_buff *skb)
goto drop;
if (!fcoe_filter_frames(lport, fp)) {
- put_cpu();
fc_exch_recv(lport, fp);
return;
}
drop:
- stats->ErrorFrames++;
- put_cpu();
+ this_cpu_inc(lport->stats->ErrorFrames);
kfree_skb(skb);
}
/**
- * fcoe_percpu_receive_thread() - The per-CPU packet receive thread
- * @arg: The per-CPU context
+ * fcoe_receive_work() - The per-CPU worker
+ * @work: The work struct
*
- * Return: 0 for success
*/
-int fcoe_percpu_receive_thread(void *arg)
+static void fcoe_receive_work(struct work_struct *work)
{
- struct fcoe_percpu_s *p = arg;
+ struct fcoe_percpu_s *p;
struct sk_buff *skb;
+ struct sk_buff_head tmp;
- set_user_nice(current, -20);
+ p = container_of(work, struct fcoe_percpu_s, work);
+ skb_queue_head_init(&tmp);
+
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ skb_queue_splice_init(&p->fcoe_rx_list, &tmp);
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
- while (!kthread_should_stop()) {
+ if (!skb_queue_len(&tmp))
+ return;
- spin_lock_bh(&p->fcoe_rx_list.lock);
- while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
- set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_bh(&p->fcoe_rx_list.lock);
- schedule();
- set_current_state(TASK_RUNNING);
- if (kthread_should_stop())
- return 0;
- spin_lock_bh(&p->fcoe_rx_list.lock);
- }
- spin_unlock_bh(&p->fcoe_rx_list.lock);
+ while ((skb = __skb_dequeue(&tmp)))
fcoe_recv_frame(skb);
- }
- return 0;
}
/**
@@ -1668,6 +1728,7 @@ int fcoe_percpu_receive_thread(void *arg)
*/
static void fcoe_dev_setup(void)
{
+ register_dcbevent_notifier(&dcb_notifier);
register_netdevice_notifier(&fcoe_notifier);
}
@@ -1676,9 +1737,69 @@ static void fcoe_dev_setup(void)
*/
static void fcoe_dev_cleanup(void)
{
+ unregister_dcbevent_notifier(&dcb_notifier);
unregister_netdevice_notifier(&fcoe_notifier);
}
+static struct fcoe_interface *
+fcoe_hostlist_lookup_realdev_port(struct net_device *netdev)
+{
+ struct fcoe_interface *fcoe;
+ struct net_device *real_dev;
+
+ list_for_each_entry(fcoe, &fcoe_hostlist, list) {
+ if (is_vlan_dev(fcoe->netdev))
+ real_dev = vlan_dev_real_dev(fcoe->netdev);
+ else
+ real_dev = fcoe->netdev;
+
+ if (netdev == real_dev)
+ return fcoe;
+ }
+ return NULL;
+}
+
+static int fcoe_dcb_app_notification(struct notifier_block *notifier,
+ ulong event, void *ptr)
+{
+ struct dcb_app_type *entry = ptr;
+ struct fcoe_ctlr *ctlr;
+ struct fcoe_interface *fcoe;
+ struct net_device *netdev;
+ int prio;
+
+ if (entry->app.selector != DCB_APP_IDTYPE_ETHTYPE)
+ return NOTIFY_OK;
+
+ netdev = dev_get_by_index(&init_net, entry->ifindex);
+ if (!netdev)
+ return NOTIFY_OK;
+
+ fcoe = fcoe_hostlist_lookup_realdev_port(netdev);
+ dev_put(netdev);
+ if (!fcoe)
+ return NOTIFY_OK;
+
+ ctlr = fcoe_to_ctlr(fcoe);
+
+ if (entry->dcbx & DCB_CAP_DCBX_VER_CEE)
+ prio = ffs(entry->app.priority) - 1;
+ else
+ prio = entry->app.priority;
+
+ if (prio < 0)
+ return NOTIFY_OK;
+
+ if (entry->app.protocol == ETH_P_FIP ||
+ entry->app.protocol == ETH_P_FCOE)
+ ctlr->priority = prio;
+
+ if (entry->app.protocol == ETH_P_FCOE)
+ fcoe->priority = prio;
+
+ return NOTIFY_OK;
+}
+
/**
* fcoe_device_notification() - Handler for net device events
* @notifier: The context of the notification
@@ -1692,18 +1813,19 @@ static void fcoe_dev_cleanup(void)
static int fcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr)
{
+ struct fcoe_ctlr_device *cdev;
struct fc_lport *lport = NULL;
- struct net_device *netdev = ptr;
+ struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
- struct fcoe_port *port;
- struct fcoe_dev_stats *stats;
u32 link_possible = 1;
u32 mfs;
int rc = NOTIFY_OK;
list_for_each_entry(fcoe, &fcoe_hostlist, list) {
if (fcoe->netdev == netdev) {
- lport = fcoe->ctlr.lp;
+ ctlr = fcoe_to_ctlr(fcoe);
+ lport = ctlr->lp;
break;
}
}
@@ -1721,7 +1843,7 @@ static int fcoe_device_notification(struct notifier_block *notifier,
case NETDEV_CHANGE:
break;
case NETDEV_CHANGEMTU:
- if (netdev->features & NETIF_F_FCOE_MTU)
+ if (netdev->fcoe_mtu)
break;
mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
sizeof(struct fcoe_crc_eof));
@@ -1732,11 +1854,15 @@ static int fcoe_device_notification(struct notifier_block *notifier,
break;
case NETDEV_UNREGISTER:
list_del(&fcoe->list);
- port = lport_priv(fcoe->ctlr.lp);
+ fcoe_vport_remove(lport);
+ mutex_lock(&fcoe_config_mutex);
+ fcoe_if_destroy(lport);
+ if (!fcoe->removed)
+ fcoe_interface_remove(fcoe);
fcoe_interface_cleanup(fcoe);
- queue_work(fcoe_wq, &port->destroy_work);
+ mutex_unlock(&fcoe_config_mutex);
+ fcoe_ctlr_device_delete(fcoe_ctlr_to_ctlr_dev(ctlr));
goto out;
- break;
case NETDEV_FEAT_CHANGE:
fcoe_netdev_features_change(lport, netdev);
break;
@@ -1747,13 +1873,27 @@ static int fcoe_device_notification(struct notifier_block *notifier,
fcoe_link_speed_update(lport);
- if (link_possible && !fcoe_link_ok(lport))
- fcoe_ctlr_link_up(&fcoe->ctlr);
- else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->LinkFailureCount++;
- put_cpu();
- fcoe_clean_pending_queue(lport);
+ cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
+
+ if (link_possible && !fcoe_link_ok(lport)) {
+ switch (cdev->enabled) {
+ case FCOE_CTLR_DISABLED:
+ pr_info("Link up while interface is disabled.\n");
+ break;
+ case FCOE_CTLR_ENABLED:
+ case FCOE_CTLR_UNUSED:
+ fcoe_ctlr_link_up(ctlr);
+ }
+ } else if (fcoe_ctlr_link_down(ctlr)) {
+ switch (cdev->enabled) {
+ case FCOE_CTLR_DISABLED:
+ pr_info("Link down while interface is disabled.\n");
+ break;
+ case FCOE_CTLR_ENABLED:
+ case FCOE_CTLR_UNUSED:
+ this_cpu_inc(lport->stats->LinkFailureCount);
+ fcoe_clean_pending_queue(lport);
+ }
}
out:
return rc;
@@ -1766,9 +1906,12 @@ out:
* Called from fcoe transport.
*
* Returns: 0 for success
+ *
+ * Deprecated: use fcoe_ctlr_enabled()
*/
static int fcoe_disable(struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
int rc = 0;
@@ -1779,8 +1922,9 @@ static int fcoe_disable(struct net_device *netdev)
rtnl_unlock();
if (fcoe) {
- fcoe_ctlr_link_down(&fcoe->ctlr);
- fcoe_clean_pending_queue(fcoe->ctlr.lp);
+ ctlr = fcoe_to_ctlr(fcoe);
+ fcoe_ctlr_link_down(ctlr);
+ fcoe_clean_pending_queue(ctlr->lp);
} else
rc = -ENODEV;
@@ -1798,6 +1942,7 @@ static int fcoe_disable(struct net_device *netdev)
*/
static int fcoe_enable(struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
int rc = 0;
@@ -1806,16 +1951,75 @@ static int fcoe_enable(struct net_device *netdev)
fcoe = fcoe_hostlist_lookup_port(netdev);
rtnl_unlock();
- if (!fcoe)
+ if (!fcoe) {
rc = -ENODEV;
- else if (!fcoe_link_ok(fcoe->ctlr.lp))
- fcoe_ctlr_link_up(&fcoe->ctlr);
+ goto out;
+ }
+
+ ctlr = fcoe_to_ctlr(fcoe);
+
+ if (!fcoe_link_ok(ctlr->lp))
+ fcoe_ctlr_link_up(ctlr);
+out:
mutex_unlock(&fcoe_config_mutex);
return rc;
}
/**
+ * fcoe_ctlr_enabled() - Enable or disable an FCoE Controller
+ * @cdev: The FCoE Controller that is being enabled or disabled
+ *
+ * fcoe_sysfs will ensure that the state of 'enabled' has
+ * changed, so no checking is necessary here. This routine simply
+ * calls fcoe_enable or fcoe_disable, both of which are deprecated.
+ * When those routines are removed the functionality can be merged
+ * here.
+ */
+static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev)
+{
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev);
+ struct fc_lport *lport = ctlr->lp;
+ struct net_device *netdev = fcoe_netdev(lport);
+
+ switch (cdev->enabled) {
+ case FCOE_CTLR_ENABLED:
+ return fcoe_enable(netdev);
+ case FCOE_CTLR_DISABLED:
+ return fcoe_disable(netdev);
+ case FCOE_CTLR_UNUSED:
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+/**
+ * fcoe_ctlr_mode() - Switch FIP mode
+ * @ctlr_dev: The FCoE Controller that is being modified
+ *
+ * When the FIP mode has been changed we need to update
+ * the multicast addresses to ensure we get the correct
+ * frames.
+ */
+static void fcoe_ctlr_mode(struct fcoe_ctlr_device *ctlr_dev)
+{
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr);
+
+ if (ctlr_dev->mode == FIP_CONN_TYPE_VN2VN &&
+ ctlr->mode != FIP_MODE_VN2VN) {
+ dev_mc_del(fcoe->netdev, FIP_ALL_ENODE_MACS);
+ dev_mc_add(fcoe->netdev, FIP_ALL_VN2VN_MACS);
+ dev_mc_add(fcoe->netdev, FIP_ALL_P2P_MACS);
+ } else if (ctlr->mode != FIP_MODE_FABRIC) {
+ dev_mc_del(fcoe->netdev, FIP_ALL_VN2VN_MACS);
+ dev_mc_del(fcoe->netdev, FIP_ALL_P2P_MACS);
+ dev_mc_add(fcoe->netdev, FIP_ALL_ENODE_MACS);
+ }
+ fcoe_ctlr_set_fip_mode(ctlr_dev);
+}
+
+/**
* fcoe_destroy() - Destroy a FCoE interface
* @netdev : The net_device object the Ethernet interface to create on
*
@@ -1825,24 +2029,26 @@ static int fcoe_enable(struct net_device *netdev)
*/
static int fcoe_destroy(struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
struct fc_lport *lport;
+ struct fcoe_port *port;
int rc = 0;
mutex_lock(&fcoe_config_mutex);
rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
if (!fcoe) {
- rtnl_unlock();
rc = -ENODEV;
goto out_nodev;
}
- lport = fcoe->ctlr.lp;
+ ctlr = fcoe_to_ctlr(fcoe);
+ lport = ctlr->lp;
+ port = lport_priv(lport);
list_del(&fcoe->list);
- fcoe_interface_cleanup(fcoe);
- rtnl_unlock();
- fcoe_if_destroy(lport);
+ queue_work(fcoe_wq, &port->destroy_work);
out_nodev:
+ rtnl_unlock();
mutex_unlock(&fcoe_config_mutex);
return rc;
}
@@ -1853,12 +2059,31 @@ out_nodev:
*/
static void fcoe_destroy_work(struct work_struct *work)
{
+ struct fcoe_ctlr_device *cdev;
+ struct fcoe_ctlr *ctlr;
struct fcoe_port *port;
+ struct fcoe_interface *fcoe;
port = container_of(work, struct fcoe_port, destroy_work);
+
+ fcoe_vport_remove(port->lport);
+
mutex_lock(&fcoe_config_mutex);
+
+ fcoe = port->priv;
+ ctlr = fcoe_to_ctlr(fcoe);
+ cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
+
+ rtnl_lock();
fcoe_if_destroy(port->lport);
+ if (!fcoe->removed)
+ fcoe_interface_remove(fcoe);
+ rtnl_unlock();
+ fcoe_interface_cleanup(fcoe);
+
mutex_unlock(&fcoe_config_mutex);
+
+ fcoe_ctlr_device_delete(cdev);
}
/**
@@ -1876,17 +2101,71 @@ static bool fcoe_match(struct net_device *netdev)
}
/**
- * fcoe_create() - Create a fcoe interface
- * @netdev : The net_device object the Ethernet interface to create on
- * @fip_mode: The FIP mode for this creation
+ * fcoe_dcb_create() - Initialize DCB attributes and hooks
+ * @fcoe: The new FCoE interface
+ */
+static void fcoe_dcb_create(struct fcoe_interface *fcoe)
+{
+ int ctlr_prio = TC_PRIO_BESTEFFORT;
+ int fcoe_prio = TC_PRIO_INTERACTIVE;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
+#ifdef CONFIG_DCB
+ int dcbx;
+ u8 fup, up;
+ struct net_device *netdev = fcoe->realdev;
+ struct dcb_app app = {
+ .priority = 0,
+ .protocol = ETH_P_FCOE
+ };
+
+ /* setup DCB priority attributes. */
+ if (netdev && netdev->dcbnl_ops && netdev->dcbnl_ops->getdcbx) {
+ dcbx = netdev->dcbnl_ops->getdcbx(netdev);
+
+ if (dcbx & DCB_CAP_DCBX_VER_IEEE) {
+ app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE;
+ up = dcb_ieee_getapp_mask(netdev, &app);
+ app.protocol = ETH_P_FIP;
+ fup = dcb_ieee_getapp_mask(netdev, &app);
+ } else {
+ app.selector = DCB_APP_IDTYPE_ETHTYPE;
+ up = dcb_getapp(netdev, &app);
+ app.protocol = ETH_P_FIP;
+ fup = dcb_getapp(netdev, &app);
+ }
+
+ fcoe_prio = ffs(up) ? ffs(up) - 1 : 0;
+ ctlr_prio = ffs(fup) ? ffs(fup) - 1 : fcoe_prio;
+ }
+#endif
+ fcoe->priority = fcoe_prio;
+ ctlr->priority = ctlr_prio;
+}
+
+enum fcoe_create_link_state {
+ FCOE_CREATE_LINK_DOWN,
+ FCOE_CREATE_LINK_UP,
+};
+
+/**
+ * _fcoe_create() - (internal) Create a fcoe interface
+ * @netdev : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
+ * @link_state: The ctlr link state on creation
*
- * Called from fcoe transport
+ * Called from either the libfcoe 'create' module parameter
+ * via fcoe_create or from fcoe_syfs's ctlr_create file.
*
- * Returns: 0 for success
+ * libfcoe's 'create' module parameter is deprecated so some
+ * consolidation of code can be done when that interface is
+ * removed.
*/
-static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
+static int _fcoe_create(struct net_device *netdev, enum fip_mode fip_mode,
+ enum fcoe_create_link_state link_state)
{
- int rc;
+ int rc = 0;
+ struct fcoe_ctlr_device *ctlr_dev;
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
struct fc_lport *lport;
@@ -1905,76 +2184,93 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
goto out_nodev;
}
- lport = fcoe_if_create(fcoe, &netdev->dev, 0);
+ ctlr = fcoe_to_ctlr(fcoe);
+ ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
+ lport = fcoe_if_create(fcoe, &ctlr_dev->dev, 0);
if (IS_ERR(lport)) {
printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
netdev->name);
rc = -EIO;
+ if (!fcoe->removed)
+ fcoe_interface_remove(fcoe);
+ rtnl_unlock();
fcoe_interface_cleanup(fcoe);
- goto out_free;
+ mutex_unlock(&fcoe_config_mutex);
+ fcoe_ctlr_device_delete(ctlr_dev);
+ return rc;
}
/* Make this the "master" N_Port */
- fcoe->ctlr.lp = lport;
+ ctlr->lp = lport;
- /* add to lports list */
- fcoe_hostlist_add(lport);
+ /* setup DCB priority attributes. */
+ fcoe_dcb_create(fcoe);
/* start FIP Discovery and FLOGI */
lport->boot_time = jiffies;
fc_fabric_login(lport);
- if (!fcoe_link_ok(lport))
- fcoe_ctlr_link_up(&fcoe->ctlr);
/*
- * Release from init in fcoe_interface_create(), on success lport
- * should be holding a reference taken in fcoe_if_create().
+ * If the fcoe_ctlr_device is to be set to DISABLED
+ * it must be done after the lport is added to the
+ * hostlist, but before the rtnl_lock is released.
+ * This is because the rtnl_lock protects the
+ * hostlist that fcoe_device_notification uses. If
+ * the FCoE Controller is intended to be created
+ * DISABLED then 'enabled' needs to be considered
+ * handling link events. 'enabled' must be set
+ * before the lport can be found in the hostlist
+ * when a link up event is received.
*/
- fcoe_interface_put(fcoe);
- rtnl_unlock();
- mutex_unlock(&fcoe_config_mutex);
+ if (link_state == FCOE_CREATE_LINK_UP)
+ ctlr_dev->enabled = FCOE_CTLR_ENABLED;
+ else
+ ctlr_dev->enabled = FCOE_CTLR_DISABLED;
+
+ if (link_state == FCOE_CREATE_LINK_UP &&
+ !fcoe_link_ok(lport)) {
+ rtnl_unlock();
+ fcoe_ctlr_link_up(ctlr);
+ mutex_unlock(&fcoe_config_mutex);
+ return rc;
+ }
- return 0;
-out_free:
- fcoe_interface_put(fcoe);
out_nodev:
rtnl_unlock();
mutex_unlock(&fcoe_config_mutex);
+
return rc;
}
/**
- * fcoe_link_speed_update() - Update the supported and actual link speeds
- * @lport: The local port to update speeds for
+ * fcoe_create() - Create a fcoe interface
+ * @netdev : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
+ *
+ * Called from fcoe transport
*
- * Returns: 0 if the ethtool query was successful
- * -1 if the ethtool query failed
+ * Returns: 0 for success
*/
-int fcoe_link_speed_update(struct fc_lport *lport)
+static int fcoe_create(struct net_device *netdev, enum fip_mode fip_mode)
{
- struct net_device *netdev = fcoe_netdev(lport);
- struct ethtool_cmd ecmd;
-
- if (!dev_ethtool_get_settings(netdev, &ecmd)) {
- lport->link_supported_speeds &=
- ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
- if (ecmd.supported & (SUPPORTED_1000baseT_Half |
- SUPPORTED_1000baseT_Full))
- lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
- if (ecmd.supported & SUPPORTED_10000baseT_Full)
- lport->link_supported_speeds |=
- FC_PORTSPEED_10GBIT;
- switch (ethtool_cmd_speed(&ecmd)) {
- case SPEED_1000:
- lport->link_speed = FC_PORTSPEED_1GBIT;
- break;
- case SPEED_10000:
- lport->link_speed = FC_PORTSPEED_10GBIT;
- break;
- }
- return 0;
- }
- return -1;
+ return _fcoe_create(netdev, fip_mode, FCOE_CREATE_LINK_UP);
+}
+
+/**
+ * fcoe_ctlr_alloc() - Allocate a fcoe interface from fcoe_sysfs
+ * @netdev: The net_device to be used by the allocated FCoE Controller
+ *
+ * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr
+ * in a link_down state. The allows the user an opportunity to configure
+ * the FCoE Controller from sysfs before enabling the FCoE Controller.
+ *
+ * Creating in with this routine starts the FCoE Controller in Fabric
+ * mode. The user can change to VN2VN or another mode before enabling.
+ */
+static int fcoe_ctlr_alloc(struct net_device *netdev)
+{
+ return _fcoe_create(netdev, FIP_MODE_FABRIC,
+ FCOE_CREATE_LINK_DOWN);
}
/**
@@ -1984,7 +2280,7 @@ int fcoe_link_speed_update(struct fc_lport *lport)
* Returns: 0 if link is UP and OK, -1 if not
*
*/
-int fcoe_link_ok(struct fc_lport *lport)
+static int fcoe_link_ok(struct fc_lport *lport)
{
struct net_device *netdev = fcoe_netdev(lport);
@@ -1999,53 +2295,19 @@ int fcoe_link_ok(struct fc_lport *lport)
*
* Must be called with fcoe_create_mutex held to single-thread completion.
*
- * This flushes the pending skbs by adding a new skb to each queue and
- * waiting until they are all freed. This assures us that not only are
- * there no packets that will be handled by the lport, but also that any
- * threads already handling packet have returned.
+ * This flushes the pending skbs by flush the work item for each CPU. The work
+ * item on each possible CPU is flushed because we may have used the per-CPU
+ * struct of an offline CPU.
*/
-void fcoe_percpu_clean(struct fc_lport *lport)
+static void fcoe_percpu_clean(struct fc_lport *lport)
{
struct fcoe_percpu_s *pp;
- struct fcoe_rcv_info *fr;
- struct sk_buff_head *list;
- struct sk_buff *skb, *next;
- struct sk_buff *head;
unsigned int cpu;
for_each_possible_cpu(cpu) {
pp = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&pp->fcoe_rx_list.lock);
- list = &pp->fcoe_rx_list;
- head = list->next;
- for (skb = head; skb != (struct sk_buff *)list;
- skb = next) {
- next = skb->next;
- fr = fcoe_dev_from_skb(skb);
- if (fr->fr_dev == lport) {
- __skb_unlink(skb, list);
- kfree_skb(skb);
- }
- }
-
- if (!pp->thread || !cpu_online(cpu)) {
- spin_unlock_bh(&pp->fcoe_rx_list.lock);
- continue;
- }
-
- skb = dev_alloc_skb(0);
- if (!skb) {
- spin_unlock_bh(&pp->fcoe_rx_list.lock);
- continue;
- }
- skb->destructor = fcoe_percpu_flush_done;
-
- __skb_queue_tail(&pp->fcoe_rx_list, skb);
- if (pp->fcoe_rx_list.qlen == 1)
- wake_up_process(pp->thread);
- spin_unlock_bh(&pp->fcoe_rx_list.lock);
- wait_for_completion(&fcoe_flush_completion);
+ flush_work(&pp->work);
}
}
@@ -2055,16 +2317,20 @@ void fcoe_percpu_clean(struct fc_lport *lport)
*
* Returns: Always 0 (return value required by FC transport template)
*/
-int fcoe_reset(struct Scsi_Host *shost)
+static int fcoe_reset(struct Scsi_Host *shost)
{
struct fc_lport *lport = shost_priv(shost);
struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->priv;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
+ struct fcoe_ctlr_device *cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
- fcoe_ctlr_link_down(&fcoe->ctlr);
- fcoe_clean_pending_queue(fcoe->ctlr.lp);
- if (!fcoe_link_ok(fcoe->ctlr.lp))
- fcoe_ctlr_link_up(&fcoe->ctlr);
+ fcoe_ctlr_link_down(ctlr);
+ fcoe_clean_pending_queue(ctlr->lp);
+
+ if (cdev->enabled != FCOE_CTLR_DISABLED &&
+ !fcoe_link_ok(ctlr->lp))
+ fcoe_ctlr_link_up(ctlr);
return 0;
}
@@ -2099,10 +2365,12 @@ fcoe_hostlist_lookup_port(const struct net_device *netdev)
*/
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
{
+ struct fcoe_ctlr *ctlr;
struct fcoe_interface *fcoe;
fcoe = fcoe_hostlist_lookup_port(netdev);
- return (fcoe) ? fcoe->ctlr.lp : NULL;
+ ctlr = fcoe_to_ctlr(fcoe);
+ return (fcoe) ? ctlr->lp : NULL;
}
/**
@@ -2128,12 +2396,31 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
return 0;
}
+/**
+ * fcoe_hostlist_del() - Remove the FCoE interface identified by a local
+ * port to the hostlist
+ * @lport: The local port that identifies the FCoE interface to be added
+ *
+ * Locking: must be called with the RTNL mutex held
+ *
+ */
+static void fcoe_hostlist_del(const struct fc_lport *lport)
+{
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
+
+ port = lport_priv(lport);
+ fcoe = port->priv;
+ list_del(&fcoe->list);
+ return;
+}
static struct fcoe_transport fcoe_sw_transport = {
.name = {FCOE_TRANSPORT_DEFAULT},
.attached = false,
.list = LIST_HEAD_INIT(fcoe_sw_transport.list),
.match = fcoe_match,
+ .alloc = fcoe_ctlr_alloc,
.create = fcoe_create,
.destroy = fcoe_destroy,
.enable = fcoe_enable,
@@ -2151,7 +2438,7 @@ static int __init fcoe_init(void)
unsigned int cpu;
int rc = 0;
- fcoe_wq = alloc_workqueue("fcoe", 0, 0);
+ fcoe_wq = alloc_workqueue("fcoe", WQ_PERCPU, 0);
if (!fcoe_wq)
return -ENOMEM;
@@ -2160,24 +2447,18 @@ static int __init fcoe_init(void)
if (rc) {
printk(KERN_ERR "failed to register an fcoe transport, check "
"if libfcoe is loaded\n");
- return rc;
+ goto out_destroy;
}
mutex_lock(&fcoe_config_mutex);
for_each_possible_cpu(cpu) {
- p = &per_cpu(fcoe_percpu, cpu);
+ p = per_cpu_ptr(&fcoe_percpu, cpu);
+ INIT_WORK(&p->work, fcoe_receive_work);
skb_queue_head_init(&p->fcoe_rx_list);
+ local_lock_init(&p->lock);
}
- for_each_online_cpu(cpu)
- fcoe_percpu_thread_create(cpu);
-
- /* Initialize per CPU interrupt thread */
- rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
- if (rc)
- goto out_free;
-
/* Setup link change notification */
fcoe_dev_setup();
@@ -2189,10 +2470,9 @@ static int __init fcoe_init(void)
return 0;
out_free:
- for_each_online_cpu(cpu) {
- fcoe_percpu_thread_destroy(cpu);
- }
mutex_unlock(&fcoe_config_mutex);
+ fcoe_transport_detach(&fcoe_sw_transport);
+out_destroy:
destroy_workqueue(fcoe_wq);
return rc;
}
@@ -2206,6 +2486,7 @@ module_init(fcoe_init);
static void __exit fcoe_exit(void)
{
struct fcoe_interface *fcoe, *tmp;
+ struct fcoe_ctlr *ctlr;
struct fcoe_port *port;
unsigned int cpu;
@@ -2216,17 +2497,15 @@ static void __exit fcoe_exit(void)
/* releases the associated fcoe hosts */
rtnl_lock();
list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) {
- list_del(&fcoe->list);
- port = lport_priv(fcoe->ctlr.lp);
- fcoe_interface_cleanup(fcoe);
+ ctlr = fcoe_to_ctlr(fcoe);
+ port = lport_priv(ctlr->lp);
+ fcoe_hostlist_del(port->lport);
queue_work(fcoe_wq, &port->destroy_work);
}
rtnl_unlock();
- unregister_hotcpu_notifier(&fcoe_cpu_notifier);
-
- for_each_online_cpu(cpu)
- fcoe_percpu_thread_destroy(cpu);
+ for_each_possible_cpu(cpu)
+ fcoe_thread_cleanup_local(cpu);
mutex_unlock(&fcoe_config_mutex);
@@ -2252,7 +2531,7 @@ module_exit(fcoe_exit);
* fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler
* @seq: active sequence in the FLOGI or FDISC exchange
* @fp: response frame, or error encoded in a pointer (timeout)
- * @arg: pointer the the fcoe_ctlr structure
+ * @arg: pointer to the fcoe_ctlr structure
*
* This handles MAC address management for FCoE, then passes control on to
* the libfc FLOGI response handler.
@@ -2268,14 +2547,11 @@ static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
goto done;
mac = fr_cb(fp)->granted_mac;
- if (is_zero_ether_addr(mac)) {
- /* pre-FIP */
- if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
- fc_frame_free(fp);
- return;
- }
- }
- fcoe_update_src_mac(lport, mac);
+ /* pre-FIP */
+ if (is_zero_ether_addr(mac))
+ fcoe_ctlr_recv_flogi(fip, lport, fp);
+ else
+ fcoe_update_src_mac(lport, mac);
done:
fc_lport_flogi_resp(seq, fp, lport);
}
@@ -2284,7 +2560,7 @@ done:
* fcoe_logo_resp() - FCoE specific LOGO response handler
* @seq: active sequence in the LOGO exchange
* @fp: response frame, or error encoded in a pointer (timeout)
- * @arg: pointer the the fcoe_ctlr structure
+ * @arg: pointer to the fcoe_ctlr structure
*
* This handles MAC address management for FCoE, then passes control on to
* the libfc LOGO response handler.
@@ -2299,7 +2575,7 @@ static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
fc_lport_logo_resp(seq, fp, lport);
}
-/**
+/*
* fcoe_elsct_send - FCoE specific ELS handler
*
* This does special case handling of FIP encapsualted ELS exchanges for FCoE,
@@ -2317,7 +2593,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
{
struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->priv;
- struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
struct fc_frame_header *fh = fc_frame_header_get(fp);
switch (op) {
@@ -2357,7 +2633,7 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
rc = fcoe_validate_vport_create(vport);
if (rc) {
- wwn_to_str(vport->port_name, buf, sizeof(buf));
+ fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
printk(KERN_ERR "fcoe: Failed to create vport, "
"WWPN (0x%s) already exists\n",
buf);
@@ -2365,7 +2641,9 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
}
mutex_lock(&fcoe_config_mutex);
+ rtnl_lock();
vn_port = fcoe_if_create(fcoe, &vport->dev, 1);
+ rtnl_unlock();
mutex_unlock(&fcoe_config_mutex);
if (IS_ERR(vn_port)) {
@@ -2395,16 +2673,52 @@ static int fcoe_vport_destroy(struct fc_vport *vport)
struct Scsi_Host *shost = vport_to_shost(vport);
struct fc_lport *n_port = shost_priv(shost);
struct fc_lport *vn_port = vport->dd_data;
- struct fcoe_port *port = lport_priv(vn_port);
mutex_lock(&n_port->lp_mutex);
list_del(&vn_port->list);
mutex_unlock(&n_port->lp_mutex);
- queue_work(fcoe_wq, &port->destroy_work);
+
+ mutex_lock(&fcoe_config_mutex);
+ rtnl_lock();
+ fcoe_if_destroy(vn_port);
+ rtnl_unlock();
+ mutex_unlock(&fcoe_config_mutex);
+
return 0;
}
/**
+ * fcoe_vport_remove() - remove attached vports
+ * @lport: lport for which the vports should be removed
+ */
+static void fcoe_vport_remove(struct fc_lport *lport)
+{
+ struct Scsi_Host *shost;
+ struct fc_host_attrs *fc_host;
+ unsigned long flags;
+ struct fc_vport *vport;
+ struct fc_vport *next_vport;
+
+ shost = lport->host;
+ fc_host = shost_to_fc_host(shost);
+
+ /* Loop through all the vports and mark them for deletion */
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_for_each_entry_safe(vport, next_vport, &fc_host->vports, peers) {
+ if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) {
+ continue;
+ } else {
+ vport->flags |= FC_VPORT_DELETING;
+ queue_work(fc_host_work_q(shost),
+ &vport->vport_delete_work);
+ }
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ flush_workqueue(fc_host_work_q(shost));
+}
+
+/**
* fcoe_vport_disable() - change vport state
* @vport: vport to bring online/offline
* @disable: should the vport be disabled?
@@ -2426,7 +2740,7 @@ static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
}
/**
- * fcoe_vport_set_symbolic_name() - append vport string to symbolic name
+ * fcoe_set_vport_symbolic_name() - append vport string to symbolic name
* @vport: fc_vport with a new symbolic name string
*
* After generating a new symbolic name string, a new RSPN_ID request is
@@ -2456,36 +2770,14 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
NULL, NULL, 3 * lport->r_a_tov);
}
-/**
- * fcoe_get_lesb() - Fill the FCoE Link Error Status Block
- * @lport: the local port
- * @fc_lesb: the link error status block
- */
-static void fcoe_get_lesb(struct fc_lport *lport,
- struct fc_els_lesb *fc_lesb)
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
{
- unsigned int cpu;
- u32 lfc, vlfc, mdac;
- struct fcoe_dev_stats *devst;
- struct fcoe_fc_els_lesb *lesb;
- struct rtnl_link_stats64 temp;
- struct net_device *netdev = fcoe_netdev(lport);
+ struct fcoe_ctlr_device *ctlr_dev =
+ fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr);
- lfc = 0;
- vlfc = 0;
- mdac = 0;
- lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
- memset(lesb, 0, sizeof(*lesb));
- for_each_possible_cpu(cpu) {
- devst = per_cpu_ptr(lport->dev_stats, cpu);
- lfc += devst->LinkFailureCount;
- vlfc += devst->VLinkFailureCount;
- mdac += devst->MissDiscAdvCount;
- }
- lesb->lesb_link_fail = htonl(lfc);
- lesb->lesb_vlink_fail = htonl(vlfc);
- lesb->lesb_miss_fka = htonl(mdac);
- lesb->lesb_fcs_error = htonl(dev_get_stats(netdev, &temp)->rx_crc_errors);
+ fcf_dev->vlan_id = vlan_dev_vlan_id(fcoe->netdev);
}
/**
@@ -2505,53 +2797,8 @@ static void fcoe_set_port_id(struct fc_lport *lport,
{
struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->priv;
+ struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
- fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
-}
-
-/**
- * fcoe_validate_vport_create() - Validate a vport before creating it
- * @vport: NPIV port to be created
- *
- * This routine is meant to add validation for a vport before creating it
- * via fcoe_vport_create().
- * Current validations are:
- * - WWPN supplied is unique for given lport
- *
- *
-*/
-static int fcoe_validate_vport_create(struct fc_vport *vport)
-{
- struct Scsi_Host *shost = vport_to_shost(vport);
- struct fc_lport *n_port = shost_priv(shost);
- struct fc_lport *vn_port;
- int rc = 0;
- char buf[32];
-
- mutex_lock(&n_port->lp_mutex);
-
- wwn_to_str(vport->port_name, buf, sizeof(buf));
- /* Check if the wwpn is not same as that of the lport */
- if (!memcmp(&n_port->wwpn, &vport->port_name, sizeof(u64))) {
- FCOE_DBG("vport WWPN 0x%s is same as that of the "
- "base port WWPN\n", buf);
- rc = -EINVAL;
- goto out;
- }
-
- /* Check if there is any existing vport with same wwpn */
- list_for_each_entry(vn_port, &n_port->vports, list) {
- if (!memcmp(&vn_port->wwpn, &vport->port_name, sizeof(u64))) {
- FCOE_DBG("vport with given WWPN 0x%s already "
- "exists\n", buf);
- rc = -EINVAL;
- break;
- }
- }
-
-out:
- mutex_unlock(&n_port->lp_mutex);
-
- return rc;
+ fcoe_ctlr_recv_flogi(ctlr, lport, fp);
}
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index c4a93993c0cf..520c53512925 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2009 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -40,9 +28,7 @@
#define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */
#define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */
-unsigned int fcoe_debug_logging;
-module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+extern unsigned int fcoe_debug_logging;
#define FCOE_LOGGING 0x01 /* General logging, not categorized */
#define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */
@@ -57,12 +43,12 @@ do { \
#define FCOE_DBG(fmt, args...) \
FCOE_CHECK_LOGGING(FCOE_LOGGING, \
- printk(KERN_INFO "fcoe: " fmt, ##args);)
+ pr_info("fcoe: " fmt, ##args);)
#define FCOE_NETDEV_DBG(netdev, fmt, args...) \
FCOE_CHECK_LOGGING(FCOE_NETDEV_LOGGING, \
- printk(KERN_INFO "fcoe: %s: " fmt, \
- netdev->name, ##args);)
+ pr_info("fcoe: %s: " fmt, \
+ netdev->name, ##args);)
/**
* struct fcoe_interface - A FCoE interface
@@ -70,24 +56,29 @@ do { \
* @netdev: The associated net device
* @fcoe_packet_type: FCoE packet type
* @fip_packet_type: FIP packet type
- * @ctlr: The FCoE controller (for FIP)
* @oem: The offload exchange manager for all local port
* instances associated with this port
- * @kref: The kernel reference
- *
- * This structure is 1:1 with a net devive.
+ * @removed: Indicates fcoe interface removed from net device
+ * @priority: Priority for the FCoE packet (DCB)
+ * This structure is 1:1 with a net device.
*/
struct fcoe_interface {
struct list_head list;
struct net_device *netdev;
+ struct net_device *realdev;
struct packet_type fcoe_packet_type;
struct packet_type fip_packet_type;
- struct fcoe_ctlr ctlr;
+ struct packet_type fip_vlan_packet_type;
struct fc_exch_mgr *oem;
- struct kref kref;
+ u8 removed;
+ u8 priority;
};
-#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
+#define fcoe_to_ctlr(x) \
+ (struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1)
+
+#define fcoe_from_ctlr(x) \
+ ((struct fcoe_interface *)((x) + 1))
/**
* fcoe_netdev() - Return the net device associated with a local port
@@ -99,14 +90,4 @@ static inline struct net_device *fcoe_netdev(const struct fc_lport *lport)
((struct fcoe_port *)lport_priv(lport))->priv)->netdev;
}
-static inline void wwn_to_str(u64 wwn, char *buf, int len)
-{
- u8 wwpn[8];
-
- u64_to_wwn(wwn, wwpn);
- snprintf(buf, len, "%02x%02x%02x%02x%02x%02x%02x%02x",
- wwpn[0], wwpn[1], wwpn[2], wwpn[3],
- wwpn[4], wwpn[5], wwpn[6], wwpn[7]);
-}
-
#endif /* _FCOE_H_ */
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index c74c4b8e71ef..8e4241c295e3 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2008-2009 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2009 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -49,7 +37,7 @@
#define FCOE_CTLR_MIN_FKA 500 /* min keep alive (mS) */
#define FCOE_CTLR_DEF_FKA FIP_DEF_FKA /* default keep alive (mS) */
-static void fcoe_ctlr_timeout(unsigned long);
+static void fcoe_ctlr_timeout(struct timer_list *);
static void fcoe_ctlr_timer_work(struct work_struct *);
static void fcoe_ctlr_recv_work(struct work_struct *);
static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *);
@@ -59,6 +47,8 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *);
static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *);
static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *);
+static int fcoe_ctlr_vlan_recv(struct fcoe_ctlr *, struct sk_buff *);
+
static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
@@ -144,16 +134,18 @@ static void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip)
/**
* fcoe_ctlr_init() - Initialize the FCoE Controller instance
* @fip: The FCoE controller to initialize
+ * @mode: FIP mode to set
*/
-void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
+void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_mode mode)
{
fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
fip->mode = mode;
+ fip->fip_resp = false;
INIT_LIST_HEAD(&fip->fcfs);
mutex_init(&fip->ctlr_mutex);
spin_lock_init(&fip->ctlr_lock);
fip->flogi_oxid = FC_XID_UNKNOWN;
- setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip);
+ timer_setup(&fip->timer, fcoe_ctlr_timeout, 0);
INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_work);
INIT_WORK(&fip->recv_work, fcoe_ctlr_recv_work);
skb_queue_head_init(&fip->fip_recv_list);
@@ -161,6 +153,115 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
EXPORT_SYMBOL(fcoe_ctlr_init);
/**
+ * fcoe_sysfs_fcf_add() - Add a fcoe_fcf{,_device} to a fcoe_ctlr{,_device}
+ * @new: The newly discovered FCF
+ *
+ * Called with fip->ctlr_mutex held
+ */
+static int fcoe_sysfs_fcf_add(struct fcoe_fcf *new)
+{
+ struct fcoe_ctlr *fip = new->fip;
+ struct fcoe_ctlr_device *ctlr_dev;
+ struct fcoe_fcf_device *temp, *fcf_dev;
+ int rc = -ENOMEM;
+
+ LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
+ new->fabric_name, new->fcf_mac);
+
+ temp = kzalloc(sizeof(*temp), GFP_KERNEL);
+ if (!temp)
+ goto out;
+
+ temp->fabric_name = new->fabric_name;
+ temp->switch_name = new->switch_name;
+ temp->fc_map = new->fc_map;
+ temp->vfid = new->vfid;
+ memcpy(temp->mac, new->fcf_mac, ETH_ALEN);
+ temp->priority = new->pri;
+ temp->fka_period = new->fka_period;
+ temp->selected = 0; /* default to unselected */
+
+ /*
+ * If ctlr_dev doesn't exist then it means we're a libfcoe user
+ * who doesn't use fcoe_syfs and didn't allocate a fcoe_ctlr_device.
+ * fnic would be an example of a driver with this behavior. In this
+ * case we want to add the fcoe_fcf to the fcoe_ctlr list, but we
+ * don't want to make sysfs changes.
+ */
+
+ ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
+ if (ctlr_dev) {
+ mutex_lock(&ctlr_dev->lock);
+ fcf_dev = fcoe_fcf_device_add(ctlr_dev, temp);
+ if (unlikely(!fcf_dev)) {
+ rc = -ENOMEM;
+ mutex_unlock(&ctlr_dev->lock);
+ goto out;
+ }
+
+ /*
+ * The fcoe_sysfs layer can return a CONNECTED fcf that
+ * has a priv (fcf was never deleted) or a CONNECTED fcf
+ * that doesn't have a priv (fcf was deleted). However,
+ * libfcoe will always delete FCFs before trying to add
+ * them. This is ensured because both recv_adv and
+ * age_fcfs are protected by the the fcoe_ctlr's mutex.
+ * This means that we should never get a FCF with a
+ * non-NULL priv pointer.
+ */
+ BUG_ON(fcf_dev->priv);
+
+ fcf_dev->priv = new;
+ new->fcf_dev = fcf_dev;
+ mutex_unlock(&ctlr_dev->lock);
+ }
+
+ list_add(&new->list, &fip->fcfs);
+ fip->fcf_count++;
+ rc = 0;
+
+out:
+ kfree(temp);
+ return rc;
+}
+
+/**
+ * fcoe_sysfs_fcf_del() - Remove a fcoe_fcf{,_device} to a fcoe_ctlr{,_device}
+ * @new: The FCF to be removed
+ *
+ * Called with fip->ctlr_mutex held
+ */
+static void fcoe_sysfs_fcf_del(struct fcoe_fcf *new)
+{
+ struct fcoe_ctlr *fip = new->fip;
+ struct fcoe_ctlr_device *cdev;
+ struct fcoe_fcf_device *fcf_dev;
+
+ list_del(&new->list);
+ fip->fcf_count--;
+
+ /*
+ * If ctlr_dev doesn't exist then it means we're a libfcoe user
+ * who doesn't use fcoe_syfs and didn't allocate a fcoe_ctlr_device
+ * or a fcoe_fcf_device.
+ *
+ * fnic would be an example of a driver with this behavior. In this
+ * case we want to remove the fcoe_fcf from the fcoe_ctlr list (above),
+ * but we don't want to make sysfs changes.
+ */
+ cdev = fcoe_ctlr_to_ctlr_dev(fip);
+ if (cdev) {
+ mutex_lock(&cdev->lock);
+ fcf_dev = fcoe_fcf_to_fcf_dev(new);
+ WARN_ON(!fcf_dev);
+ new->fcf_dev = NULL;
+ fcoe_fcf_device_delete(fcf_dev);
+ mutex_unlock(&cdev->lock);
+ }
+ kfree(new);
+}
+
+/**
* fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller
* @fip: The FCoE controller whose FCFs are to be reset
*
@@ -173,10 +274,10 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
fip->sel_fcf = NULL;
list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
- list_del(&fcf->list);
- kfree(fcf);
+ fcoe_sysfs_fcf_del(fcf);
}
- fip->fcf_count = 0;
+ WARN_ON(fip->fcf_count);
+
fip->sel_time = 0;
}
@@ -201,7 +302,7 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
fcoe_ctlr_set_state(fip, FIP_ST_DISABLED);
fcoe_ctlr_reset_fcfs(fip);
mutex_unlock(&fip->ctlr_mutex);
- del_timer_sync(&fip->timer);
+ timer_delete_sync(&fip->timer);
cancel_work_sync(&fip->timer_work);
}
EXPORT_SYMBOL(fcoe_ctlr_destroy);
@@ -230,19 +331,19 @@ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
spin_unlock_bh(&fip->ctlr_lock);
sel = fip->sel_fcf;
- if (sel && !compare_ether_addr(sel->fcf_mac, fip->dest_addr))
+ if (sel && ether_addr_equal(sel->fcf_mac, fip->dest_addr))
goto unlock;
if (!is_zero_ether_addr(fip->dest_addr)) {
printk(KERN_NOTICE "libfcoe: host%d: "
"FIP Fibre-Channel Forwarder MAC %pM deselected\n",
fip->lp->host->host_no, fip->dest_addr);
- memset(fip->dest_addr, 0, ETH_ALEN);
+ eth_zero_addr(fip->dest_addr);
}
if (sel) {
printk(KERN_INFO "libfcoe: host%d: FIP selected "
"Fibre-Channel Forwarder MAC %pM\n",
fip->lp->host->host_no, sel->fcf_mac);
- memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
+ memcpy(fip->dest_addr, sel->fcoe_mac, ETH_ALEN);
fip->map_dest = 0;
}
unlock:
@@ -320,6 +421,7 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
skb_put(skb, sizeof(*sol));
skb->protocol = htons(ETH_P_FIP);
+ skb->priority = fip->priority;
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
fip->send(fip, skb);
@@ -341,14 +443,17 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
mutex_unlock(&fip->ctlr_mutex);
fc_linkup(fip->lp);
} else if (fip->state == FIP_ST_LINK_WAIT) {
- fcoe_ctlr_set_state(fip, fip->mode);
+ if (fip->mode == FIP_MODE_NON_FIP)
+ fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
+ else
+ fcoe_ctlr_set_state(fip, FIP_ST_AUTO);
switch (fip->mode) {
default:
LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode);
- /* fall-through */
+ fallthrough;
case FIP_MODE_AUTO:
LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
- /* fall-through */
+ fallthrough;
case FIP_MODE_FABRIC:
case FIP_MODE_NON_FIP:
mutex_unlock(&fip->ctlr_mutex);
@@ -373,7 +478,7 @@ EXPORT_SYMBOL(fcoe_ctlr_link_up);
static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
{
fcoe_ctlr_reset_fcfs(fip);
- del_timer(&fip->timer);
+ timer_delete(&fip->timer);
fip->ctlr_ka_time = 0;
fip->port_ka_time = 0;
fip->sol_time = 0;
@@ -474,6 +579,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
}
skb_put(skb, len);
skb->protocol = htons(ETH_P_FIP);
+ skb->priority = fip->priority;
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
fip->send(fip, skb);
@@ -482,6 +588,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
/**
* fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it
* @fip: The FCoE controller for the ELS frame
+ * @lport: The local port
* @dtype: The FIP descriptor type for the frame
* @skb: The FCoE ELS frame including FC header but no FCoE headers
* @d_id: The destination port ID.
@@ -512,7 +619,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
fh = (struct fc_frame_header *)skb->data;
op = *(u8 *)(fh + 1);
dlen = sizeof(struct fip_encaps) + skb->len; /* len before push */
- cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap));
+ cap = skb_push(skb, sizeof(*cap));
memset(cap, 0, sizeof(*cap));
if (lport->point_to_multipoint) {
@@ -546,8 +653,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
if (op != ELS_LS_RJT) {
dlen += sizeof(*mac);
- mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac));
- memset(mac, 0, sizeof(*mac));
+ mac = skb_put_zero(skb, sizeof(*mac));
mac->fd_desc.fip_dtype = FIP_DT_MAC;
mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) {
@@ -566,6 +672,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
cap->fip.fip_dl_len = htons(dlen / FIP_BPW);
skb->protocol = htons(ETH_P_FIP);
+ skb->priority = fip->priority;
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
return 0;
@@ -640,9 +747,9 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
case ELS_LOGO:
if (fip->mode == FIP_MODE_VN2VN) {
if (fip->state != FIP_ST_VNMP_UP)
- return -EINVAL;
+ goto drop;
if (ntoh24(fh->fh_d_id) == FC_FID_FLOGI)
- return -EINVAL;
+ goto drop;
} else {
if (fip->state != FIP_ST_ENABLED)
return 0;
@@ -666,7 +773,7 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
fc_fcoe_set_mac(mac, fh->fh_d_id);
fip->update_mac(lport, mac);
}
- /* fall through */
+ fallthrough;
case ELS_LS_RJT:
op = fr_encaps(fp);
if (op)
@@ -685,6 +792,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
fip->send(fip, skb);
return -EINPROGRESS;
drop:
+ LIBFCOE_FIP_DBG(fip, "drop els_send op %u d_id %x\n",
+ op, ntoh24(fh->fh_d_id));
kfree_skb(skb);
return -EINVAL;
}
@@ -714,20 +823,22 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
unsigned long deadline;
unsigned long sel_time = 0;
- struct fcoe_dev_stats *stats;
+ struct list_head del_list;
- stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu());
+ INIT_LIST_HEAD(&del_list);
list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2;
if (fip->sel_fcf == fcf) {
if (time_after(jiffies, deadline)) {
- stats->MissDiscAdvCount++;
+ u64 miss_cnt;
+
+ miss_cnt = this_cpu_inc_return(fip->lp->stats->MissDiscAdvCount);
printk(KERN_INFO "libfcoe: host%d: "
"Missing Discovery Advertisement "
"for fab %16.16llx count %lld\n",
fip->lp->host->host_no, fcf->fabric_name,
- stats->MissDiscAdvCount);
+ miss_cnt);
} else if (time_after(next_timer, deadline))
next_timer = deadline;
}
@@ -736,11 +847,14 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
if (time_after_eq(jiffies, deadline)) {
if (fip->sel_fcf == fcf)
fip->sel_fcf = NULL;
+ /*
+ * Move to delete list so we can call
+ * fcoe_sysfs_fcf_del (which can sleep)
+ * after the put_cpu().
+ */
list_del(&fcf->list);
- WARN_ON(!fip->fcf_count);
- fip->fcf_count--;
- kfree(fcf);
- stats->VLinkFailureCount++;
+ list_add(&fcf->list, &del_list);
+ this_cpu_inc(fip->lp->stats->VLinkFailureCount);
} else {
if (time_after(next_timer, deadline))
next_timer = deadline;
@@ -749,7 +863,12 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
sel_time = fcf->time;
}
}
- put_cpu();
+
+ list_for_each_entry_safe(fcf, next, &del_list, list) {
+ /* Removes fcf from current list */
+ fcoe_sysfs_fcf_del(fcf);
+ }
+
if (sel_time && !fip->sel_fcf && !fip->sel_time) {
sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
fip->sel_time = sel_time;
@@ -821,6 +940,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
memcpy(fcf->fcf_mac,
((struct fip_mac_desc *)desc)->fd_mac,
ETH_ALEN);
+ memcpy(fcf->fcoe_mac, fcf->fcf_mac, ETH_ALEN);
if (!is_valid_ether_addr(fcf->fcf_mac)) {
LIBFCOE_FIP_DBG(fip,
"Invalid MAC addr %pM in FIP adv\n",
@@ -866,7 +986,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
"in FIP adv\n", desc->fip_dtype);
/* standard says ignore unknown descriptors >= 128 */
- if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
+ if (desc->fip_dtype < FIP_DT_NON_CRITICAL)
return -EINVAL;
break;
}
@@ -899,23 +1019,23 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
struct fcoe_fcf *fcf;
struct fcoe_fcf new;
- struct fcoe_fcf *found;
- unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV);
+ unsigned long sol_tov = msecs_to_jiffies(FCOE_CTLR_SOL_TOV);
int first = 0;
int mtu_valid;
+ int found = 0;
+ int rc = 0;
if (fcoe_ctlr_parse_adv(fip, skb, &new))
return;
mutex_lock(&fip->ctlr_mutex);
first = list_empty(&fip->fcfs);
- found = NULL;
list_for_each_entry(fcf, &fip->fcfs, list) {
if (fcf->switch_name == new.switch_name &&
fcf->fabric_name == new.fabric_name &&
fcf->fc_map == new.fc_map &&
- compare_ether_addr(fcf->fcf_mac, new.fcf_mac) == 0) {
- found = fcf;
+ ether_addr_equal(fcf->fcf_mac, new.fcf_mac)) {
+ found = 1;
break;
}
}
@@ -927,9 +1047,16 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (!fcf)
goto out;
- fip->fcf_count++;
memcpy(fcf, &new, sizeof(new));
- list_add(&fcf->list, &fip->fcfs);
+ fcf->fip = fip;
+ rc = fcoe_sysfs_fcf_add(fcf);
+ if (rc) {
+ printk(KERN_ERR "Failed to allocate sysfs instance "
+ "for FCF, fab %16.16llx mac %pM\n",
+ new.fabric_name, new.fcf_mac);
+ kfree(fcf);
+ goto out;
+ }
} else {
/*
* Update the FCF's keep-alive descriptor flags.
@@ -950,6 +1077,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
fcf->fka_period = new.fka_period;
memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN);
}
+
mtu_valid = fcoe_ctlr_mtu_valid(fcf);
fcf->time = jiffies;
if (!found)
@@ -985,13 +1113,15 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
* If this is the first validated FCF, note the time and
* set a timer to trigger selection.
*/
- if (mtu_valid && !fip->sel_fcf && fcoe_ctlr_fcf_usable(fcf)) {
+ if (mtu_valid && !fip->sel_fcf && !fip->sel_time &&
+ fcoe_ctlr_fcf_usable(fcf)) {
fip->sel_time = jiffies +
msecs_to_jiffies(FCOE_CTLR_START_DELAY);
if (!timer_pending(&fip->timer) ||
time_before(fip->sel_time, fip->timer.expires))
mod_timer(&fip->timer, fip->sel_time);
}
+
out:
mutex_unlock(&fip->ctlr_mutex);
}
@@ -1009,7 +1139,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
struct fc_frame_header *fh = NULL;
struct fip_desc *desc;
struct fip_encaps *els;
- struct fcoe_dev_stats *stats;
+ struct fcoe_fcf *sel;
enum fip_desc_type els_dtype = 0;
u8 els_op;
u8 sub;
@@ -1037,7 +1167,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
goto drop;
/* Drop ELS if there are duplicate critical descriptors */
if (desc->fip_dtype < 32) {
- if (desc_mask & 1U << desc->fip_dtype) {
+ if ((desc->fip_dtype != FIP_DT_MAC) &&
+ (desc_mask & 1U << desc->fip_dtype)) {
LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
"Descriptors in FIP ELS\n");
goto drop;
@@ -1046,17 +1177,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
}
switch (desc->fip_dtype) {
case FIP_DT_MAC:
+ sel = fip->sel_fcf;
if (desc_cnt == 1) {
LIBFCOE_FIP_DBG(fip, "FIP descriptors "
"received out of order\n");
goto drop;
}
+ /*
+ * Some switch implementations send two MAC descriptors,
+ * with first MAC(granted_mac) being the FPMA, and the
+ * second one(fcoe_mac) is used as destination address
+ * for sending/receiving FCoE packets. FIP traffic is
+ * sent using fip_mac. For regular switches, both
+ * fip_mac and fcoe_mac would be the same.
+ */
+ if (desc_cnt == 2)
+ memcpy(granted_mac,
+ ((struct fip_mac_desc *)desc)->fd_mac,
+ ETH_ALEN);
if (dlen != sizeof(struct fip_mac_desc))
goto len_err;
- memcpy(granted_mac,
- ((struct fip_mac_desc *)desc)->fd_mac,
- ETH_ALEN);
+
+ if ((desc_cnt == 3) && (sel))
+ memcpy(sel->fcoe_mac,
+ ((struct fip_mac_desc *)desc)->fd_mac,
+ ETH_ALEN);
break;
case FIP_DT_FLOGI:
case FIP_DT_FDISC:
@@ -1080,7 +1226,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
"in FIP adv\n", desc->fip_dtype);
/* standard says ignore unknown descriptors >= 128 */
- if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
+ if (desc->fip_dtype < FIP_DT_NON_CRITICAL)
goto drop;
if (desc_cnt <= 2) {
LIBFCOE_FIP_DBG(fip, "FIP descriptors "
@@ -1137,10 +1283,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
fr_dev(fp) = lport;
fr_encaps(fp) = els_dtype;
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->RxFrames++;
- stats->RxWords += skb->len / FIP_BPW;
- put_cpu();
+ this_cpu_inc(lport->stats->RxFrames);
+ this_cpu_add(lport->stats->RxWords, skb->len / FIP_BPW);
fc_exch_recv(lport, fp);
return;
@@ -1153,15 +1297,15 @@ drop:
}
/**
- * fcoe_ctlr_recv_els() - Handle an incoming link reset frame
+ * fcoe_ctlr_recv_clr_vlink() - Handle an incoming link reset frame
* @fip: The FCoE controller that received the frame
- * @fh: The received FIP header
+ * @skb: The received FIP packet
*
* There may be multiple VN_Port descriptors.
* The overall length has already been checked.
*/
static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
- struct fip_header *fh)
+ struct sk_buff *skb)
{
struct fip_desc *desc;
struct fip_mac_desc *mp;
@@ -1176,11 +1320,48 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
int num_vlink_desc;
int reset_phys_port = 0;
struct fip_vn_desc **vlink_desc_arr = NULL;
+ struct fip_header *fh = (struct fip_header *)skb->data;
+ struct ethhdr *eh = eth_hdr(skb);
LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
- if (!fcf || !lport->port_id)
+ if (!fcf) {
+ /*
+ * We are yet to select best FCF, but we got CVL in the
+ * meantime. reset the ctlr and let it rediscover the FCF
+ */
+ LIBFCOE_FIP_DBG(fip, "Resetting fcoe_ctlr as FCF has not been "
+ "selected yet\n");
+ mutex_lock(&fip->ctlr_mutex);
+ fcoe_ctlr_reset(fip);
+ mutex_unlock(&fip->ctlr_mutex);
return;
+ }
+
+ /*
+ * If we've selected an FCF check that the CVL is from there to avoid
+ * processing CVLs from an unexpected source. If it is from an
+ * unexpected source drop it on the floor.
+ */
+ if (!ether_addr_equal(eh->h_source, fcf->fcf_mac)) {
+ LIBFCOE_FIP_DBG(fip, "Dropping CVL due to source address "
+ "mismatch with FCF src=%pM\n", eh->h_source);
+ return;
+ }
+
+ /*
+ * If we haven't logged into the fabric but receive a CVL we should
+ * reset everything and go back to solicitation.
+ */
+ if (!lport->port_id) {
+ LIBFCOE_FIP_DBG(fip, "lport not logged in, resoliciting\n");
+ mutex_lock(&fip->ctlr_mutex);
+ fcoe_ctlr_reset(fip);
+ mutex_unlock(&fip->ctlr_mutex);
+ fc_lport_reset(fip->lp);
+ fcoe_ctlr_solicit(fip, NULL);
+ return;
+ }
/*
* mask of required descriptors. Validating each one clears its bit.
@@ -1193,12 +1374,12 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
/*
* Actually need to subtract 'sizeof(*mp) - sizeof(*wp)' from 'rlen'
* before determining max Vx_Port descriptor but a buggy FCF could have
- * omited either or both MAC Address and Name Identifier descriptors
+ * omitted either or both MAC Address and Name Identifier descriptors
*/
num_vlink_desc = rlen / sizeof(*vp);
if (num_vlink_desc)
- vlink_desc_arr = kmalloc(sizeof(vp) * num_vlink_desc,
- GFP_ATOMIC);
+ vlink_desc_arr = kmalloc_array(num_vlink_desc, sizeof(vp),
+ GFP_ATOMIC);
if (!vlink_desc_arr)
return;
num_vlink_desc = 0;
@@ -1220,7 +1401,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
mp = (struct fip_mac_desc *)desc;
if (dlen < sizeof(*mp))
goto err;
- if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac))
+ if (!ether_addr_equal(mp->fd_mac, fcf->fcf_mac))
goto err;
desc_mask &= ~BIT(FIP_DT_MAC);
break;
@@ -1241,16 +1422,14 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
ntoh24(vp->fd_fc_id));
if (vn_port && (vn_port == lport)) {
mutex_lock(&fip->ctlr_mutex);
- per_cpu_ptr(lport->dev_stats,
- get_cpu())->VLinkFailureCount++;
- put_cpu();
+ this_cpu_inc(lport->stats->VLinkFailureCount);
fcoe_ctlr_reset(fip);
mutex_unlock(&fip->ctlr_mutex);
}
break;
default:
/* standard says ignore unknown descriptors >= 128 */
- if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
+ if (desc->fip_dtype < FIP_DT_NON_CRITICAL)
goto err;
break;
}
@@ -1270,18 +1449,16 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
* No Vx_Port description. Clear all NPIV ports,
* followed by physical port
*/
+ mutex_lock(&fip->ctlr_mutex);
+ this_cpu_inc(lport->stats->VLinkFailureCount);
+ fcoe_ctlr_reset(fip);
+ mutex_unlock(&fip->ctlr_mutex);
+
mutex_lock(&lport->lp_mutex);
list_for_each_entry(vn_port, &lport->vports, list)
fc_lport_reset(vn_port);
mutex_unlock(&lport->lp_mutex);
- mutex_lock(&fip->ctlr_mutex);
- per_cpu_ptr(lport->dev_stats,
- get_cpu())->VLinkFailureCount++;
- put_cpu();
- fcoe_ctlr_reset(fip);
- mutex_unlock(&fip->ctlr_mutex);
-
fc_lport_reset(fip->lp);
fcoe_ctlr_solicit(fip, NULL);
} else {
@@ -1299,8 +1476,8 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
* 'port_id' is already validated, check MAC address and
* wwpn
*/
- if (compare_ether_addr(fip->get_src_addr(vn_port),
- vp->fd_mac) != 0 ||
+ if (!ether_addr_equal(fip->get_src_addr(vn_port),
+ vp->fd_mac) ||
get_unaligned_be64(&vp->fd_wwpn) !=
vn_port->wwpn)
continue;
@@ -1334,6 +1511,9 @@ err:
*/
void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return;
skb_queue_tail(&fip->fip_recv_list, skb);
schedule_work(&fip->recv_work);
}
@@ -1351,6 +1531,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
struct fip_header *fiph;
struct ethhdr *eh;
enum fip_state state;
+ bool fip_vlan_resp = false;
u16 op;
u8 sub;
@@ -1360,12 +1541,12 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
goto drop;
eh = eth_hdr(skb);
if (fip->mode == FIP_MODE_VN2VN) {
- if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
- compare_ether_addr(eh->h_dest, fcoe_all_vn2vn) &&
- compare_ether_addr(eh->h_dest, fcoe_all_p2p))
+ if (!ether_addr_equal(eh->h_dest, fip->ctl_src_addr) &&
+ !ether_addr_equal(eh->h_dest, fcoe_all_vn2vn) &&
+ !ether_addr_equal(eh->h_dest, fcoe_all_p2p))
goto drop;
- } else if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
- compare_ether_addr(eh->h_dest, fcoe_all_enode))
+ } else if (!ether_addr_equal(eh->h_dest, fip->ctl_src_addr) &&
+ !ether_addr_equal(eh->h_dest, fcoe_all_enode))
goto drop;
fiph = (struct fip_header *)skb->data;
op = ntohs(fiph->fip_op);
@@ -1384,11 +1565,17 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
state = FIP_ST_ENABLED;
LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
}
+ fip_vlan_resp = fip->fip_resp;
mutex_unlock(&fip->ctlr_mutex);
if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN)
return fcoe_ctlr_vn_recv(fip, skb);
+ if (fip_vlan_resp && op == FIP_OP_VLAN) {
+ LIBFCOE_FIP_DBG(fip, "fip vlan discovery\n");
+ return fcoe_ctlr_vlan_recv(fip, skb);
+ }
+
if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP &&
state != FIP_ST_VNMP_CLAIM)
goto drop;
@@ -1404,7 +1591,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (op == FIP_OP_DISC && sub == FIP_SC_ADV)
fcoe_ctlr_recv_adv(fip, skb);
else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK)
- fcoe_ctlr_recv_clr_vlink(fip, fiph);
+ fcoe_ctlr_recv_clr_vlink(fip, skb);
kfree_skb(skb);
return 0;
drop:
@@ -1429,9 +1616,6 @@ static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
{
struct fcoe_fcf *fcf;
struct fcoe_fcf *best = fip->sel_fcf;
- struct fcoe_fcf *first;
-
- first = list_first_entry(&fip->fcfs, struct fcoe_fcf, list);
list_for_each_entry(fcf, &fip->fcfs, list) {
LIBFCOE_FIP_DBG(fip, "consider FCF fab %16.16llx "
@@ -1440,15 +1624,6 @@ static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
fcf->fabric_name, fcf->vfid, fcf->fcf_mac,
fcf->fc_map, fcoe_ctlr_mtu_valid(fcf),
fcf->flogi_sent, fcf->pri);
- if (fcf->fabric_name != first->fabric_name ||
- fcf->vfid != first->vfid ||
- fcf->fc_map != first->fc_map) {
- LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
- "or FC-MAP\n");
- return NULL;
- }
- if (fcf->flogi_sent)
- continue;
if (!fcoe_ctlr_fcf_usable(fcf)) {
LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx "
"map %x %svalid %savailable\n",
@@ -1460,6 +1635,13 @@ static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
}
if (!best || fcf->pri < best->pri || best->flogi_sent)
best = fcf;
+ if (fcf->fabric_name != best->fabric_name ||
+ fcf->vfid != best->vfid ||
+ fcf->fc_map != best->fc_map) {
+ LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
+ "or FC-MAP\n");
+ return NULL;
+ }
}
fip->sel_fcf = best;
if (best) {
@@ -1587,11 +1769,11 @@ unlock:
/**
* fcoe_ctlr_timeout() - FIP timeout handler
- * @arg: The FCoE controller that timed out
+ * @t: Timer context use to obtain the controller reference
*/
-static void fcoe_ctlr_timeout(unsigned long arg)
+static void fcoe_ctlr_timeout(struct timer_list *t)
{
- struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg;
+ struct fcoe_ctlr *fip = timer_container_of(fip, t, timer);
schedule_work(&fip->timer_work);
}
@@ -1699,6 +1881,7 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
/**
* fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response
* @fip: The FCoE controller
+ * @lport: The local port
* @fp: The FC frame to snoop
*
* Snoop potential response to FLOGI or even incoming FLOGI.
@@ -1742,7 +1925,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
* address_mode flag to use FC_OUI-based Ethernet DA.
* Otherwise we use the FCoE gateway addr
*/
- if (!compare_ether_addr(sa, (u8[6])FC_FCOE_FLOGI_MAC)) {
+ if (ether_addr_equal(sa, (u8[6])FC_FCOE_FLOGI_MAC)) {
fcoe_ctlr_map_dest(fip);
} else {
memcpy(fip->dest_addr, sa, ETH_ALEN);
@@ -1778,7 +1961,7 @@ EXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
*
* Returns: u64 fc world wide name
*/
-u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+u64 fcoe_wwn_from_mac(unsigned char mac[ETH_ALEN],
unsigned int scheme, unsigned int port)
{
u64 wwn;
@@ -1817,7 +2000,7 @@ EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
*/
static inline struct fcoe_rport *fcoe_ctlr_rport(struct fc_rport_priv *rdata)
{
- return (struct fcoe_rport *)(rdata + 1);
+ return container_of(rdata, struct fcoe_rport, rdata);
}
/**
@@ -1832,7 +2015,7 @@ static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
const u8 *dest, size_t min_len)
{
struct sk_buff *skb;
- struct fip_frame {
+ struct fip_vn2vn_probe_frame {
struct ethhdr eth;
struct fip_header fip;
struct fip_mac_desc mac;
@@ -1859,10 +2042,16 @@ static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
if (!skb)
return;
- frame = (struct fip_frame *)skb->data;
+ frame = (struct fip_vn2vn_probe_frame *)skb->data;
memset(frame, 0, len);
memcpy(frame->eth.h_dest, dest, ETH_ALEN);
- memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+
+ if (sub == FIP_SC_VN_BEACON) {
+ hton24(frame->eth.h_source, FIP_VN_FC_MAP);
+ hton24(frame->eth.h_source + 3, fip->port_id);
+ } else {
+ memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+ }
frame->eth.h_proto = htons(ETH_P_FIP);
frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
@@ -1911,6 +2100,7 @@ static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
skb_put(skb, len);
skb->protocol = htons(ETH_P_FIP);
+ skb->priority = fip->priority;
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
@@ -1948,7 +2138,7 @@ static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport,
LIBFCOE_FIP_DBG(fip,
"rport FLOGI limited port_id %6.6x\n",
rdata->ids.port_id);
- lport->tt.rport_logoff(rdata);
+ fc_rport_logoff(rdata);
}
break;
default:
@@ -1963,20 +2153,28 @@ static struct fc_rport_operations fcoe_ctlr_vn_rport_ops = {
/**
* fcoe_ctlr_disc_stop_locked() - stop discovery in VN2VN mode
- * @fip: The FCoE controller
+ * @lport: The local port
*
* Called with ctlr_mutex held.
*/
static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
{
+ struct fc_rport_priv *rdata;
+
mutex_lock(&lport->disc.disc_mutex);
+ list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
+ if (kref_get_unless_zero(&rdata->kref)) {
+ fc_rport_logoff(rdata);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
+ }
lport->disc.disc_callback = NULL;
mutex_unlock(&lport->disc.disc_mutex);
}
/**
* fcoe_ctlr_disc_stop() - stop discovery in VN2VN mode
- * @fip: The FCoE controller
+ * @lport: The local port
*
* Called through the local port template for discovery.
* Called without the ctlr_mutex held.
@@ -1992,7 +2190,7 @@ static void fcoe_ctlr_disc_stop(struct fc_lport *lport)
/**
* fcoe_ctlr_disc_stop_final() - stop discovery for shutdown in VN2VN mode
- * @fip: The FCoE controller
+ * @lport: The local port
*
* Called through the local port template for discovery.
* Called without the ctlr_mutex held.
@@ -2000,7 +2198,7 @@ static void fcoe_ctlr_disc_stop(struct fc_lport *lport)
static void fcoe_ctlr_disc_stop_final(struct fc_lport *lport)
{
fcoe_ctlr_disc_stop(lport);
- lport->tt.rport_flush_queue();
+ fc_rport_flush_queue();
synchronize_rcu();
}
@@ -2026,7 +2224,7 @@ static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip)
*/
port_id = fip->port_id;
if (fip->probe_tries)
- port_id = prandom32(&fip->rnd_state) & 0xffff;
+ port_id = prandom_u32_state(&fip->rnd_state) & 0xffff;
else if (!port_id)
port_id = fip->lp->wwpn & 0xffff;
if (!port_id || port_id == 0xffff)
@@ -2035,7 +2233,7 @@ static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip)
if (fip->probe_tries < FIP_VN_RLIM_COUNT) {
fip->probe_tries++;
- wait = random32() % FIP_VN_PROBE_WAIT;
+ wait = get_random_u32_below(FIP_VN_PROBE_WAIT);
} else
wait = FIP_VN_RLIM_INT;
mod_timer(&fip->timer, jiffies + msecs_to_jiffies(wait));
@@ -2051,7 +2249,7 @@ static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip)
static void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip)
{
fip->probe_tries = 0;
- prandom32_seed(&fip->rnd_state, fip->lp->wwpn);
+ prandom_seed_state(&fip->rnd_state, fip->lp->wwpn);
fcoe_ctlr_vn_restart(fip);
}
@@ -2059,14 +2257,14 @@ static void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip)
* fcoe_ctlr_vn_parse - parse probe request or response
* @fip: The FCoE controller
* @skb: incoming packet
- * @rdata: buffer for resulting parsed VN entry plus fcoe_rport
+ * @frport: parsed FCoE rport from the probe request
*
* Returns non-zero error number on error.
* Does not consume the packet.
*/
static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
struct sk_buff *skb,
- struct fc_rport_priv *rdata)
+ struct fcoe_rport *frport)
{
struct fip_header *fiph;
struct fip_desc *desc = NULL;
@@ -2074,16 +2272,12 @@ static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
struct fip_wwn_desc *wwn = NULL;
struct fip_vn_desc *vn = NULL;
struct fip_size_desc *size = NULL;
- struct fcoe_rport *frport;
size_t rlen;
size_t dlen;
u32 desc_mask = 0;
u32 dtype;
u8 sub;
- memset(rdata, 0, sizeof(*rdata) + sizeof(*frport));
- frport = fcoe_ctlr_rport(rdata);
-
fiph = (struct fip_header *)skb->data;
frport->flags = ntohs(fiph->fip_flags);
@@ -2146,15 +2340,17 @@ static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
if (dlen != sizeof(struct fip_wwn_desc))
goto len_err;
wwn = (struct fip_wwn_desc *)desc;
- rdata->ids.node_name = get_unaligned_be64(&wwn->fd_wwn);
+ frport->rdata.ids.node_name =
+ get_unaligned_be64(&wwn->fd_wwn);
break;
case FIP_DT_VN_ID:
if (dlen != sizeof(struct fip_vn_desc))
goto len_err;
vn = (struct fip_vn_desc *)desc;
memcpy(frport->vn_mac, vn->fd_mac, ETH_ALEN);
- rdata->ids.port_id = ntoh24(vn->fd_fc_id);
- rdata->ids.port_name = get_unaligned_be64(&vn->fd_wwpn);
+ frport->rdata.ids.port_id = ntoh24(vn->fd_fc_id);
+ frport->rdata.ids.port_name =
+ get_unaligned_be64(&vn->fd_wwpn);
break;
case FIP_DT_FC4F:
if (dlen != sizeof(struct fip_fc4_feat))
@@ -2170,7 +2366,7 @@ static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
"in FIP probe\n", dtype);
/* standard says ignore unknown descriptors >= 128 */
- if (dtype < FIP_DT_VENDOR_BASE)
+ if (dtype < FIP_DT_NON_CRITICAL)
return -EINVAL;
break;
}
@@ -2200,21 +2396,21 @@ static void fcoe_ctlr_vn_send_claim(struct fcoe_ctlr *fip)
/**
* fcoe_ctlr_vn_probe_req() - handle incoming VN2VN probe request.
* @fip: The FCoE controller
- * @rdata: parsed remote port with frport from the probe request
+ * @frport: parsed FCoE rport from the probe request
*
* Called with ctlr_mutex held.
*/
static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip,
- struct fc_rport_priv *rdata)
+ struct fcoe_rport *frport)
{
- struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
-
- if (rdata->ids.port_id != fip->port_id)
+ if (frport->rdata.ids.port_id != fip->port_id)
return;
switch (fip->state) {
case FIP_ST_VNMP_CLAIM:
case FIP_ST_VNMP_UP:
+ LIBFCOE_FIP_DBG(fip, "vn_probe_req: send reply, state %x\n",
+ fip->state);
fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
frport->enode_mac, 0);
break;
@@ -2227,17 +2423,23 @@ static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip,
* Probe's REC bit is not set.
* If we don't reply, we will change our address.
*/
- if (fip->lp->wwpn > rdata->ids.port_name &&
+ if (fip->lp->wwpn > frport->rdata.ids.port_name &&
!(frport->flags & FIP_FL_REC_OR_P2P)) {
+ LIBFCOE_FIP_DBG(fip, "vn_probe_req: "
+ "port_id collision\n");
fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
frport->enode_mac, 0);
break;
}
- /* fall through */
+ fallthrough;
case FIP_ST_VNMP_START:
+ LIBFCOE_FIP_DBG(fip, "vn_probe_req: "
+ "restart VN2VN negotiation\n");
fcoe_ctlr_vn_restart(fip);
break;
default:
+ LIBFCOE_FIP_DBG(fip, "vn_probe_req: ignore state %x\n",
+ fip->state);
break;
}
}
@@ -2245,23 +2447,26 @@ static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip,
/**
* fcoe_ctlr_vn_probe_reply() - handle incoming VN2VN probe reply.
* @fip: The FCoE controller
- * @rdata: parsed remote port with frport from the probe request
+ * @frport: parsed FCoE rport from the probe request
*
* Called with ctlr_mutex held.
*/
static void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip,
- struct fc_rport_priv *rdata)
+ struct fcoe_rport *frport)
{
- if (rdata->ids.port_id != fip->port_id)
+ if (frport->rdata.ids.port_id != fip->port_id)
return;
switch (fip->state) {
case FIP_ST_VNMP_START:
case FIP_ST_VNMP_PROBE1:
case FIP_ST_VNMP_PROBE2:
case FIP_ST_VNMP_CLAIM:
+ LIBFCOE_FIP_DBG(fip, "vn_probe_reply: restart state %x\n",
+ fip->state);
fcoe_ctlr_vn_restart(fip);
break;
case FIP_ST_VNMP_UP:
+ LIBFCOE_FIP_DBG(fip, "vn_probe_reply: send claim notify\n");
fcoe_ctlr_vn_send_claim(fip);
break;
default:
@@ -2272,11 +2477,11 @@ static void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip,
/**
* fcoe_ctlr_vn_add() - Add a VN2VN entry to the list, based on a claim reply.
* @fip: The FCoE controller
- * @new: newly-parsed remote port with frport as a template for new rdata
+ * @new: newly-parsed FCoE rport as a template for new rdata
*
* Called with ctlr_mutex held.
*/
-static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new)
+static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fcoe_rport *new)
{
struct fc_lport *lport = fip->lp;
struct fc_rport_priv *rdata;
@@ -2284,32 +2489,45 @@ static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new)
struct fcoe_rport *frport;
u32 port_id;
- port_id = new->ids.port_id;
+ port_id = new->rdata.ids.port_id;
if (port_id == fip->port_id)
return;
mutex_lock(&lport->disc.disc_mutex);
- rdata = lport->tt.rport_create(lport, port_id);
+ rdata = fc_rport_create(lport, port_id);
if (!rdata) {
mutex_unlock(&lport->disc.disc_mutex);
return;
}
+ mutex_lock(&rdata->rp_mutex);
+ mutex_unlock(&lport->disc.disc_mutex);
rdata->ops = &fcoe_ctlr_vn_rport_ops;
rdata->disc_id = lport->disc.disc_id;
ids = &rdata->ids;
- if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) ||
- (ids->node_name != -1 && ids->node_name != new->ids.node_name))
- lport->tt.rport_logoff(rdata);
- ids->port_name = new->ids.port_name;
- ids->node_name = new->ids.node_name;
- mutex_unlock(&lport->disc.disc_mutex);
+ if ((ids->port_name != -1 &&
+ ids->port_name != new->rdata.ids.port_name) ||
+ (ids->node_name != -1 &&
+ ids->node_name != new->rdata.ids.node_name)) {
+ mutex_unlock(&rdata->rp_mutex);
+ LIBFCOE_FIP_DBG(fip, "vn_add rport logoff %6.6x\n", port_id);
+ fc_rport_logoff(rdata);
+ mutex_lock(&rdata->rp_mutex);
+ }
+ ids->port_name = new->rdata.ids.port_name;
+ ids->node_name = new->rdata.ids.node_name;
+ mutex_unlock(&rdata->rp_mutex);
frport = fcoe_ctlr_rport(rdata);
- LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n",
- port_id, frport->fcoe_len ? "old" : "new");
- *frport = *fcoe_ctlr_rport(new);
+ LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s state %d\n",
+ port_id, frport->fcoe_len ? "old" : "new",
+ rdata->rp_state);
+ frport->fcoe_len = new->fcoe_len;
+ frport->flags = new->flags;
+ frport->login_count = new->login_count;
+ memcpy(frport->enode_mac, new->enode_mac, ETH_ALEN);
+ memcpy(frport->vn_mac, new->vn_mac, ETH_ALEN);
frport->time = 0;
}
@@ -2328,30 +2546,28 @@ static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *fip, u32 port_id, u8 *mac)
struct fcoe_rport *frport;
int ret = -1;
- rcu_read_lock();
- rdata = lport->tt.rport_lookup(lport, port_id);
+ rdata = fc_rport_lookup(lport, port_id);
if (rdata) {
frport = fcoe_ctlr_rport(rdata);
memcpy(mac, frport->enode_mac, ETH_ALEN);
ret = 0;
+ kref_put(&rdata->kref, fc_rport_destroy);
}
- rcu_read_unlock();
return ret;
}
/**
* fcoe_ctlr_vn_claim_notify() - handle received FIP VN2VN Claim Notification
* @fip: The FCoE controller
- * @new: newly-parsed remote port with frport as a template for new rdata
+ * @new: newly-parsed FCoE rport as a template for new rdata
*
* Called with ctlr_mutex held.
*/
static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip,
- struct fc_rport_priv *new)
+ struct fcoe_rport *new)
{
- struct fcoe_rport *frport = fcoe_ctlr_rport(new);
-
- if (frport->flags & FIP_FL_REC_OR_P2P) {
+ if (new->flags & FIP_FL_REC_OR_P2P) {
+ LIBFCOE_FIP_DBG(fip, "send probe req for P2P/REC\n");
fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
return;
}
@@ -2359,25 +2575,38 @@ static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip,
case FIP_ST_VNMP_START:
case FIP_ST_VNMP_PROBE1:
case FIP_ST_VNMP_PROBE2:
- if (new->ids.port_id == fip->port_id)
+ if (new->rdata.ids.port_id == fip->port_id) {
+ LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
+ "restart, state %d\n",
+ fip->state);
fcoe_ctlr_vn_restart(fip);
+ }
break;
case FIP_ST_VNMP_CLAIM:
case FIP_ST_VNMP_UP:
- if (new->ids.port_id == fip->port_id) {
- if (new->ids.port_name > fip->lp->wwpn) {
+ if (new->rdata.ids.port_id == fip->port_id) {
+ if (new->rdata.ids.port_name > fip->lp->wwpn) {
+ LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
+ "restart, port_id collision\n");
fcoe_ctlr_vn_restart(fip);
break;
}
+ LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
+ "send claim notify\n");
fcoe_ctlr_vn_send_claim(fip);
break;
}
- fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, frport->enode_mac,
- min((u32)frport->fcoe_len,
+ LIBFCOE_FIP_DBG(fip, "vn_claim_notify: send reply to %x\n",
+ new->rdata.ids.port_id);
+ fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, new->enode_mac,
+ min((u32)new->fcoe_len,
fcoe_ctlr_fcoe_size(fip)));
fcoe_ctlr_vn_add(fip, new);
break;
default:
+ LIBFCOE_FIP_DBG(fip, "vn_claim_notify: "
+ "ignoring claim from %x\n",
+ new->rdata.ids.port_id);
break;
}
}
@@ -2385,15 +2614,15 @@ static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip,
/**
* fcoe_ctlr_vn_claim_resp() - handle received Claim Response
* @fip: The FCoE controller that received the frame
- * @new: newly-parsed remote port with frport from the Claim Response
+ * @new: newly-parsed FCoE rport from the Claim Response
*
* Called with ctlr_mutex held.
*/
static void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip,
- struct fc_rport_priv *new)
+ struct fcoe_rport *new)
{
LIBFCOE_FIP_DBG(fip, "claim resp from from rport %x - state %s\n",
- new->ids.port_id, fcoe_ctlr_state(fip->state));
+ new->rdata.ids.port_id, fcoe_ctlr_state(fip->state));
if (fip->state == FIP_ST_VNMP_UP || fip->state == FIP_ST_VNMP_CLAIM)
fcoe_ctlr_vn_add(fip, new);
}
@@ -2401,36 +2630,39 @@ static void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip,
/**
* fcoe_ctlr_vn_beacon() - handle received beacon.
* @fip: The FCoE controller that received the frame
- * @new: newly-parsed remote port with frport from the Beacon
+ * @new: newly-parsed FCoE rport from the Beacon
*
* Called with ctlr_mutex held.
*/
static void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip,
- struct fc_rport_priv *new)
+ struct fcoe_rport *new)
{
struct fc_lport *lport = fip->lp;
struct fc_rport_priv *rdata;
struct fcoe_rport *frport;
- frport = fcoe_ctlr_rport(new);
- if (frport->flags & FIP_FL_REC_OR_P2P) {
+ if (new->flags & FIP_FL_REC_OR_P2P) {
+ LIBFCOE_FIP_DBG(fip, "p2p beacon while in vn2vn mode\n");
fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
return;
}
- mutex_lock(&lport->disc.disc_mutex);
- rdata = lport->tt.rport_lookup(lport, new->ids.port_id);
- if (rdata)
- kref_get(&rdata->kref);
- mutex_unlock(&lport->disc.disc_mutex);
+ rdata = fc_rport_lookup(lport, new->rdata.ids.port_id);
if (rdata) {
- if (rdata->ids.node_name == new->ids.node_name &&
- rdata->ids.port_name == new->ids.port_name) {
+ if (rdata->ids.node_name == new->rdata.ids.node_name &&
+ rdata->ids.port_name == new->rdata.ids.port_name) {
frport = fcoe_ctlr_rport(rdata);
- if (!frport->time && fip->state == FIP_ST_VNMP_UP)
- lport->tt.rport_login(rdata);
+
+ LIBFCOE_FIP_DBG(fip, "beacon from rport %x\n",
+ rdata->ids.port_id);
+ if (!frport->time && fip->state == FIP_ST_VNMP_UP) {
+ LIBFCOE_FIP_DBG(fip, "beacon expired "
+ "for rport %x\n",
+ rdata->ids.port_id);
+ fc_rport_login(rdata);
+ }
frport->time = jiffies;
}
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ kref_put(&rdata->kref, fc_rport_destroy);
return;
}
if (fip->state != FIP_ST_VNMP_UP)
@@ -2442,7 +2674,7 @@ static void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip,
* Don't add the neighbor yet.
*/
LIBFCOE_FIP_DBG(fip, "beacon from new rport %x. sending claim notify\n",
- new->ids.port_id);
+ new->rdata.ids.port_id);
if (time_after(jiffies,
fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT)))
fcoe_ctlr_vn_send_claim(fip);
@@ -2467,9 +2699,13 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10);
mutex_lock(&lport->disc.disc_mutex);
list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
+ if (!kref_get_unless_zero(&rdata->kref))
+ continue;
frport = fcoe_ctlr_rport(rdata);
- if (!frport->time)
+ if (!frport->time) {
+ kref_put(&rdata->kref, fc_rport_destroy);
continue;
+ }
deadline = frport->time +
msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10);
if (time_after_eq(jiffies, deadline)) {
@@ -2477,9 +2713,10 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
LIBFCOE_FIP_DBG(fip,
"port %16.16llx fc_id %6.6x beacon expired\n",
rdata->ids.port_name, rdata->ids.port_id);
- lport->tt.rport_logoff(rdata);
+ fc_rport_logoff(rdata);
} else if (time_before(deadline, next_time))
next_time = deadline;
+ kref_put(&rdata->kref, fc_rport_destroy);
}
mutex_unlock(&lport->disc.disc_mutex);
return next_time;
@@ -2497,16 +2734,23 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
struct fip_header *fiph;
enum fip_vn2vn_subcode sub;
- struct {
- struct fc_rport_priv rdata;
- struct fcoe_rport frport;
- } buf;
- int rc;
+ struct fcoe_rport frport = { };
+ int rc, vlan_id = 0;
fiph = (struct fip_header *)skb->data;
sub = fiph->fip_subcode;
- rc = fcoe_ctlr_vn_parse(fip, skb, &buf.rdata);
+ if (fip->lp->vlan)
+ vlan_id = skb_vlan_tag_get_id(skb);
+
+ if (vlan_id && vlan_id != fip->lp->vlan) {
+ LIBFCOE_FIP_DBG(fip, "vn_recv drop frame sub %x vlan %d\n",
+ sub, vlan_id);
+ rc = -EAGAIN;
+ goto drop;
+ }
+
+ rc = fcoe_ctlr_vn_parse(fip, skb, &frport);
if (rc) {
LIBFCOE_FIP_DBG(fip, "vn_recv vn_parse error %d\n", rc);
goto drop;
@@ -2515,19 +2759,19 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
mutex_lock(&fip->ctlr_mutex);
switch (sub) {
case FIP_SC_VN_PROBE_REQ:
- fcoe_ctlr_vn_probe_req(fip, &buf.rdata);
+ fcoe_ctlr_vn_probe_req(fip, &frport);
break;
case FIP_SC_VN_PROBE_REP:
- fcoe_ctlr_vn_probe_reply(fip, &buf.rdata);
+ fcoe_ctlr_vn_probe_reply(fip, &frport);
break;
case FIP_SC_VN_CLAIM_NOTIFY:
- fcoe_ctlr_vn_claim_notify(fip, &buf.rdata);
+ fcoe_ctlr_vn_claim_notify(fip, &frport);
break;
case FIP_SC_VN_CLAIM_REP:
- fcoe_ctlr_vn_claim_resp(fip, &buf.rdata);
+ fcoe_ctlr_vn_claim_resp(fip, &frport);
break;
case FIP_SC_VN_BEACON:
- fcoe_ctlr_vn_beacon(fip, &buf.rdata);
+ fcoe_ctlr_vn_beacon(fip, &frport);
break;
default:
LIBFCOE_FIP_DBG(fip, "vn_recv unknown subcode %d\n", sub);
@@ -2541,6 +2785,212 @@ drop:
}
/**
+ * fcoe_ctlr_vlan_parse - parse vlan discovery request or response
+ * @fip: The FCoE controller
+ * @skb: incoming packet
+ * @frport: parsed FCoE rport from the probe request
+ *
+ * Returns non-zero error number on error.
+ * Does not consume the packet.
+ */
+static int fcoe_ctlr_vlan_parse(struct fcoe_ctlr *fip,
+ struct sk_buff *skb,
+ struct fcoe_rport *frport)
+{
+ struct fip_header *fiph;
+ struct fip_desc *desc = NULL;
+ struct fip_mac_desc *macd = NULL;
+ struct fip_wwn_desc *wwn = NULL;
+ size_t rlen;
+ size_t dlen;
+ u32 desc_mask = 0;
+ u32 dtype;
+ u8 sub;
+
+ fiph = (struct fip_header *)skb->data;
+ frport->flags = ntohs(fiph->fip_flags);
+
+ sub = fiph->fip_subcode;
+ switch (sub) {
+ case FIP_SC_VL_REQ:
+ desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME);
+ break;
+ default:
+ LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub);
+ return -EINVAL;
+ }
+
+ rlen = ntohs(fiph->fip_dl_len) * 4;
+ if (rlen + sizeof(*fiph) > skb->len)
+ return -EINVAL;
+
+ desc = (struct fip_desc *)(fiph + 1);
+ while (rlen > 0) {
+ dlen = desc->fip_dlen * FIP_BPW;
+ if (dlen < sizeof(*desc) || dlen > rlen)
+ return -EINVAL;
+
+ dtype = desc->fip_dtype;
+ if (dtype < 32) {
+ if (!(desc_mask & BIT(dtype))) {
+ LIBFCOE_FIP_DBG(fip,
+ "unexpected or duplicated desc "
+ "desc type %u in "
+ "FIP VN2VN subtype %u\n",
+ dtype, sub);
+ return -EINVAL;
+ }
+ desc_mask &= ~BIT(dtype);
+ }
+
+ switch (dtype) {
+ case FIP_DT_MAC:
+ if (dlen != sizeof(struct fip_mac_desc))
+ goto len_err;
+ macd = (struct fip_mac_desc *)desc;
+ if (!is_valid_ether_addr(macd->fd_mac)) {
+ LIBFCOE_FIP_DBG(fip,
+ "Invalid MAC addr %pM in FIP VN2VN\n",
+ macd->fd_mac);
+ return -EINVAL;
+ }
+ memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN);
+ break;
+ case FIP_DT_NAME:
+ if (dlen != sizeof(struct fip_wwn_desc))
+ goto len_err;
+ wwn = (struct fip_wwn_desc *)desc;
+ frport->rdata.ids.node_name =
+ get_unaligned_be64(&wwn->fd_wwn);
+ break;
+ default:
+ LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
+ "in FIP probe\n", dtype);
+ /* standard says ignore unknown descriptors >= 128 */
+ if (dtype < FIP_DT_NON_CRITICAL)
+ return -EINVAL;
+ break;
+ }
+ desc = (struct fip_desc *)((char *)desc + dlen);
+ rlen -= dlen;
+ }
+ return 0;
+
+len_err:
+ LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
+ dtype, dlen);
+ return -EINVAL;
+}
+
+/**
+ * fcoe_ctlr_vlan_send() - Send a FIP VLAN Notification
+ * @fip: The FCoE controller
+ * @sub: sub-opcode for vlan notification or vn2vn vlan notification
+ * @dest: The destination Ethernet MAC address
+ */
+static void fcoe_ctlr_vlan_send(struct fcoe_ctlr *fip,
+ enum fip_vlan_subcode sub,
+ const u8 *dest)
+{
+ struct sk_buff *skb;
+ struct fip_vlan_notify_frame {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct fip_mac_desc mac;
+ struct fip_vlan_desc vlan;
+ } __packed * frame;
+ size_t len;
+ size_t dlen;
+
+ len = sizeof(*frame);
+ dlen = sizeof(frame->mac) + sizeof(frame->vlan);
+ len = max(len, sizeof(struct ethhdr));
+
+ skb = dev_alloc_skb(len);
+ if (!skb)
+ return;
+
+ LIBFCOE_FIP_DBG(fip, "fip %s vlan notification, vlan %d\n",
+ fip->mode == FIP_MODE_VN2VN ? "vn2vn" : "fcf",
+ fip->lp->vlan);
+
+ frame = (struct fip_vlan_notify_frame *)skb->data;
+ memset(frame, 0, len);
+ memcpy(frame->eth.h_dest, dest, ETH_ALEN);
+
+ memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+ frame->eth.h_proto = htons(ETH_P_FIP);
+
+ frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
+ frame->fip.fip_op = htons(FIP_OP_VLAN);
+ frame->fip.fip_subcode = sub;
+ frame->fip.fip_dl_len = htons(dlen / FIP_BPW);
+
+ frame->mac.fd_desc.fip_dtype = FIP_DT_MAC;
+ frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW;
+ memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
+
+ frame->vlan.fd_desc.fip_dtype = FIP_DT_VLAN;
+ frame->vlan.fd_desc.fip_dlen = sizeof(frame->vlan) / FIP_BPW;
+ put_unaligned_be16(fip->lp->vlan, &frame->vlan.fd_vlan);
+
+ skb_put(skb, len);
+ skb->protocol = htons(ETH_P_FIP);
+ skb->priority = fip->priority;
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+
+ fip->send(fip, skb);
+}
+
+/**
+ * fcoe_ctlr_vlan_disc_reply() - send FIP VLAN Discovery Notification.
+ * @fip: The FCoE controller
+ * @frport: The newly-parsed FCoE rport from the Discovery Request
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vlan_disc_reply(struct fcoe_ctlr *fip,
+ struct fcoe_rport *frport)
+{
+ enum fip_vlan_subcode sub = FIP_SC_VL_NOTE;
+
+ if (fip->mode == FIP_MODE_VN2VN)
+ sub = FIP_SC_VL_VN2VN_NOTE;
+
+ fcoe_ctlr_vlan_send(fip, sub, frport->enode_mac);
+}
+
+/**
+ * fcoe_ctlr_vlan_recv - vlan request receive handler for VN2VN mode.
+ * @fip: The FCoE controller
+ * @skb: The received FIP packet
+ */
+static int fcoe_ctlr_vlan_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+ struct fip_header *fiph;
+ enum fip_vlan_subcode sub;
+ struct fcoe_rport frport = { };
+ int rc;
+
+ fiph = (struct fip_header *)skb->data;
+ sub = fiph->fip_subcode;
+ rc = fcoe_ctlr_vlan_parse(fip, skb, &frport);
+ if (rc) {
+ LIBFCOE_FIP_DBG(fip, "vlan_recv vlan_parse error %d\n", rc);
+ goto drop;
+ }
+ mutex_lock(&fip->ctlr_mutex);
+ if (sub == FIP_SC_VL_REQ)
+ fcoe_ctlr_vlan_disc_reply(fip, &frport);
+ mutex_unlock(&fip->ctlr_mutex);
+
+drop:
+ kfree_skb(skb);
+ return rc;
+}
+
+/**
* fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode.
* @lport: The local port
* @fp: The received frame
@@ -2554,13 +3004,12 @@ static void fcoe_ctlr_disc_recv(struct fc_lport *lport, struct fc_frame *fp)
rjt_data.reason = ELS_RJT_UNSUP;
rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
fc_frame_free(fp);
}
-/**
- * fcoe_ctlr_disc_recv - start discovery for VN2VN mode.
- * @fip: The FCoE controller
+/*
+ * fcoe_ctlr_disc_start - start discovery for VN2VN mode.
*
* This sets a flag indicating that remote ports should be created
* and started for the peers we discover. We use the disc_callback
@@ -2605,9 +3054,12 @@ static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
callback = disc->pending ? disc->disc_callback : NULL;
disc->pending = 0;
list_for_each_entry_rcu(rdata, &disc->rports, peers) {
+ if (!kref_get_unless_zero(&rdata->kref))
+ continue;
frport = fcoe_ctlr_rport(rdata);
if (frport->time)
- lport->tt.rport_login(rdata);
+ fc_rport_login(rdata);
+ kref_put(&rdata->kref, fc_rport_destroy);
}
mutex_unlock(&disc->disc_mutex);
if (callback)
@@ -2628,11 +3080,13 @@ static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
switch (fip->state) {
case FIP_ST_VNMP_START:
fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1);
+ LIBFCOE_FIP_DBG(fip, "vn_timeout: send 1st probe request\n");
fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
next_time = jiffies + msecs_to_jiffies(FIP_VN_PROBE_WAIT);
break;
case FIP_ST_VNMP_PROBE1:
fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE2);
+ LIBFCOE_FIP_DBG(fip, "vn_timeout: send 2nd probe request\n");
fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
break;
@@ -2643,6 +3097,7 @@ static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
hton24(mac + 3, new_port_id);
fcoe_ctlr_map_dest(fip);
fip->update_mac(fip->lp, mac);
+ LIBFCOE_FIP_DBG(fip, "vn_timeout: send claim notify\n");
fcoe_ctlr_vn_send_claim(fip);
next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
break;
@@ -2654,6 +3109,7 @@ static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
next_time = fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT);
if (time_after_eq(jiffies, next_time)) {
fcoe_ctlr_set_state(fip, FIP_ST_VNMP_UP);
+ LIBFCOE_FIP_DBG(fip, "vn_timeout: send vn2vn beacon\n");
fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
fcoe_all_vn2vn, 0);
next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
@@ -2664,11 +3120,12 @@ static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
case FIP_ST_VNMP_UP:
next_time = fcoe_ctlr_vn_age(fip);
if (time_after_eq(jiffies, fip->port_ka_time)) {
+ LIBFCOE_FIP_DBG(fip, "vn_timeout: send vn2vn beacon\n");
fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
fcoe_all_vn2vn, 0);
fip->port_ka_time = jiffies +
msecs_to_jiffies(FIP_VN_BEACON_INT +
- (random32() % FIP_VN_BEACON_FUZZ));
+ get_random_u32_below(FIP_VN_BEACON_FUZZ));
}
if (time_before(fip->port_ka_time, next_time))
next_time = fip->port_ka_time;
@@ -2689,10 +3146,51 @@ unlock:
}
/**
+ * fcoe_ctlr_mode_set() - Set or reset the ctlr's mode
+ * @lport: The local port to be (re)configured
+ * @fip: The FCoE controller whose mode is changing
+ * @fip_mode: The new fip mode
+ *
+ * Note that the we shouldn't be changing the libfc discovery settings
+ * (fc_disc_config) while an lport is going through the libfc state
+ * machine. The mode can only be changed when a fcoe_ctlr device is
+ * disabled, so that should ensure that this routine is only called
+ * when nothing is happening.
+ */
+static void fcoe_ctlr_mode_set(struct fc_lport *lport, struct fcoe_ctlr *fip,
+ enum fip_mode fip_mode)
+{
+ void *priv;
+
+ WARN_ON(lport->state != LPORT_ST_RESET &&
+ lport->state != LPORT_ST_DISABLED);
+
+ if (fip_mode == FIP_MODE_VN2VN) {
+ lport->rport_priv_size = sizeof(struct fcoe_rport);
+ lport->point_to_multipoint = 1;
+ lport->tt.disc_recv_req = fcoe_ctlr_disc_recv;
+ lport->tt.disc_start = fcoe_ctlr_disc_start;
+ lport->tt.disc_stop = fcoe_ctlr_disc_stop;
+ lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final;
+ priv = fip;
+ } else {
+ lport->rport_priv_size = 0;
+ lport->point_to_multipoint = 0;
+ lport->tt.disc_recv_req = NULL;
+ lport->tt.disc_start = NULL;
+ lport->tt.disc_stop = NULL;
+ lport->tt.disc_stop_final = NULL;
+ priv = lport;
+ }
+
+ fc_disc_config(lport, priv);
+}
+
+/**
* fcoe_libfc_config() - Sets up libfc related properties for local port
- * @lp: The local port to configure libfc for
- * @fip: The FCoE controller in use by the local port
- * @tt: The libfc function template
+ * @lport: The local port to configure libfc for
+ * @fip: The FCoE controller in use by the local port
+ * @tt: The libfc function template
* @init_fcp: If non-zero, the FCP portion of libfc should be initialized
*
* Returns : 0 for success
@@ -2707,21 +3205,50 @@ int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
fc_exch_init(lport);
fc_elsct_init(lport);
fc_lport_init(lport);
- if (fip->mode == FIP_MODE_VN2VN)
- lport->rport_priv_size = sizeof(struct fcoe_rport);
- fc_rport_init(lport);
- if (fip->mode == FIP_MODE_VN2VN) {
- lport->point_to_multipoint = 1;
- lport->tt.disc_recv_req = fcoe_ctlr_disc_recv;
- lport->tt.disc_start = fcoe_ctlr_disc_start;
- lport->tt.disc_stop = fcoe_ctlr_disc_stop;
- lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final;
- mutex_init(&lport->disc.disc_mutex);
- INIT_LIST_HEAD(&lport->disc.rports);
- lport->disc.priv = fip;
- } else {
- fc_disc_init(lport);
- }
+ fc_disc_init(lport);
+ fcoe_ctlr_mode_set(lport, fip, fip->mode);
return 0;
}
EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+void fcoe_fcf_get_selected(struct fcoe_fcf_device *fcf_dev)
+{
+ struct fcoe_ctlr_device *ctlr_dev = fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+ struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+ struct fcoe_fcf *fcf;
+
+ mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&ctlr_dev->lock);
+
+ fcf = fcoe_fcf_device_priv(fcf_dev);
+ if (fcf)
+ fcf_dev->selected = (fcf == fip->sel_fcf) ? 1 : 0;
+ else
+ fcf_dev->selected = 0;
+
+ mutex_unlock(&ctlr_dev->lock);
+ mutex_unlock(&fip->ctlr_mutex);
+}
+EXPORT_SYMBOL(fcoe_fcf_get_selected);
+
+void fcoe_ctlr_set_fip_mode(struct fcoe_ctlr_device *ctlr_dev)
+{
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+ struct fc_lport *lport = ctlr->lp;
+
+ mutex_lock(&ctlr->ctlr_mutex);
+ switch (ctlr_dev->mode) {
+ case FIP_CONN_TYPE_VN2VN:
+ ctlr->mode = FIP_MODE_VN2VN;
+ break;
+ case FIP_CONN_TYPE_FABRIC:
+ default:
+ ctlr->mode = FIP_MODE_FABRIC;
+ break;
+ }
+
+ mutex_unlock(&ctlr->ctlr_mutex);
+
+ fcoe_ctlr_mode_set(lport, ctlr, ctlr->mode);
+}
+EXPORT_SYMBOL(fcoe_ctlr_set_fip_mode);
diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c
new file mode 100644
index 000000000000..0609ca6b9353
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe_sysfs.c
@@ -0,0 +1,1042 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2011 - 2012 Intel Corporation. All rights reserved.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#include <scsi/fcoe_sysfs.h>
+#include <scsi/libfcoe.h>
+
+/*
+ * OK to include local libfcoe.h for debug_logging, but cannot include
+ * <scsi/libfcoe.h> otherwise non-netdev based fcoe solutions would have
+ * have to include more than fcoe_sysfs.h.
+ */
+#include "libfcoe.h"
+
+static atomic_t ctlr_num;
+static atomic_t fcf_num;
+
+/*
+ * fcoe_fcf_dev_loss_tmo: the default number of seconds that fcoe sysfs
+ * should insulate the loss of a fcf.
+ */
+static unsigned int fcoe_fcf_dev_loss_tmo = 1800; /* seconds */
+
+module_param_named(fcf_dev_loss_tmo, fcoe_fcf_dev_loss_tmo,
+ uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fcf_dev_loss_tmo,
+ "Maximum number of seconds that libfcoe should"
+ " insulate the loss of a fcf. Once this value is"
+ " exceeded, the fcf is removed.");
+
+/*
+ * These are used by the fcoe_*_show_function routines, they
+ * are intentionally placed in the .c file as they're not intended
+ * for use throughout the code.
+ */
+#define fcoe_ctlr_id(x) \
+ ((x)->id)
+#define fcoe_ctlr_work_q(x) \
+ ((x)->work_q)
+#define fcoe_ctlr_devloss_work_q(x) \
+ ((x)->devloss_work_q)
+#define fcoe_ctlr_mode(x) \
+ ((x)->mode)
+#define fcoe_ctlr_fcf_dev_loss_tmo(x) \
+ ((x)->fcf_dev_loss_tmo)
+#define fcoe_ctlr_link_fail(x) \
+ ((x)->lesb.lesb_link_fail)
+#define fcoe_ctlr_vlink_fail(x) \
+ ((x)->lesb.lesb_vlink_fail)
+#define fcoe_ctlr_miss_fka(x) \
+ ((x)->lesb.lesb_miss_fka)
+#define fcoe_ctlr_symb_err(x) \
+ ((x)->lesb.lesb_symb_err)
+#define fcoe_ctlr_err_block(x) \
+ ((x)->lesb.lesb_err_block)
+#define fcoe_ctlr_fcs_error(x) \
+ ((x)->lesb.lesb_fcs_error)
+#define fcoe_ctlr_enabled(x) \
+ ((x)->enabled)
+#define fcoe_fcf_state(x) \
+ ((x)->state)
+#define fcoe_fcf_fabric_name(x) \
+ ((x)->fabric_name)
+#define fcoe_fcf_switch_name(x) \
+ ((x)->switch_name)
+#define fcoe_fcf_fc_map(x) \
+ ((x)->fc_map)
+#define fcoe_fcf_vfid(x) \
+ ((x)->vfid)
+#define fcoe_fcf_mac(x) \
+ ((x)->mac)
+#define fcoe_fcf_priority(x) \
+ ((x)->priority)
+#define fcoe_fcf_fka_period(x) \
+ ((x)->fka_period)
+#define fcoe_fcf_dev_loss_tmo(x) \
+ ((x)->dev_loss_tmo)
+#define fcoe_fcf_selected(x) \
+ ((x)->selected)
+#define fcoe_fcf_vlan_id(x) \
+ ((x)->vlan_id)
+
+/*
+ * dev_loss_tmo attribute
+ */
+static int fcoe_str_to_dev_loss(const char *buf, unsigned long *val)
+{
+ int ret;
+
+ ret = kstrtoul(buf, 0, val);
+ if (ret)
+ return -EINVAL;
+ /*
+ * Check for overflow; dev_loss_tmo is u32
+ */
+ if (*val > UINT_MAX)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int fcoe_fcf_set_dev_loss_tmo(struct fcoe_fcf_device *fcf,
+ unsigned long val)
+{
+ if ((fcf->state == FCOE_FCF_STATE_UNKNOWN) ||
+ (fcf->state == FCOE_FCF_STATE_DISCONNECTED) ||
+ (fcf->state == FCOE_FCF_STATE_DELETED))
+ return -EBUSY;
+ /*
+ * Check for overflow; dev_loss_tmo is u32
+ */
+ if (val > UINT_MAX)
+ return -EINVAL;
+
+ fcoe_fcf_dev_loss_tmo(fcf) = val;
+ return 0;
+}
+
+#define FCOE_DEVICE_ATTR(_prefix, _name, _mode, _show, _store) \
+struct device_attribute device_attr_fcoe_##_prefix##_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+#define fcoe_ctlr_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \
+ if (ctlr->f->get_fcoe_ctlr_##field) \
+ ctlr->f->get_fcoe_ctlr_##field(ctlr); \
+ return snprintf(buf, sz, format_string, \
+ cast fcoe_ctlr_##field(ctlr)); \
+}
+
+#define fcoe_fcf_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \
+ struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); \
+ if (ctlr->f->get_fcoe_fcf_##field) \
+ ctlr->f->get_fcoe_fcf_##field(fcf); \
+ return snprintf(buf, sz, format_string, \
+ cast fcoe_fcf_##field(fcf)); \
+}
+
+#define fcoe_ctlr_private_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \
+ return snprintf(buf, sz, format_string, cast fcoe_ctlr_##field(ctlr)); \
+}
+
+#define fcoe_fcf_private_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \
+ return snprintf(buf, sz, format_string, cast fcoe_fcf_##field(fcf)); \
+}
+
+#define fcoe_ctlr_private_rd_attr(field, format_string, sz) \
+ fcoe_ctlr_private_show_function(field, format_string, sz, ) \
+ static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \
+ show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_ctlr_rd_attr(field, format_string, sz) \
+ fcoe_ctlr_show_function(field, format_string, sz, ) \
+ static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \
+ show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_fcf_rd_attr(field, format_string, sz) \
+ fcoe_fcf_show_function(field, format_string, sz, ) \
+ static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \
+ show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_fcf_private_rd_attr(field, format_string, sz) \
+ fcoe_fcf_private_show_function(field, format_string, sz, ) \
+ static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \
+ show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_ctlr_private_rd_attr_cast(field, format_string, sz, cast) \
+ fcoe_ctlr_private_show_function(field, format_string, sz, (cast)) \
+ static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \
+ show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_fcf_private_rd_attr_cast(field, format_string, sz, cast) \
+ fcoe_fcf_private_show_function(field, format_string, sz, (cast)) \
+ static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \
+ show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_enum_name_search(title, table_type, table) \
+static const char *get_fcoe_##title##_name(enum table_type table_key) \
+{ \
+ if (table_key < 0 || table_key >= ARRAY_SIZE(table)) \
+ return NULL; \
+ return table[table_key]; \
+}
+
+static const char * const fip_conn_type_names[] = {
+ [ FIP_CONN_TYPE_UNKNOWN ] = "Unknown",
+ [ FIP_CONN_TYPE_FABRIC ] = "Fabric",
+ [ FIP_CONN_TYPE_VN2VN ] = "VN2VN",
+};
+fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names)
+
+static char *fcf_state_names[] = {
+ [ FCOE_FCF_STATE_UNKNOWN ] = "Unknown",
+ [ FCOE_FCF_STATE_DISCONNECTED ] = "Disconnected",
+ [ FCOE_FCF_STATE_CONNECTED ] = "Connected",
+};
+fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names)
+#define FCOE_FCF_STATE_MAX_NAMELEN 50
+
+static ssize_t show_fcf_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+ const char *name;
+ name = get_fcoe_fcf_state_name(fcf->state);
+ if (!name)
+ return -EINVAL;
+ return snprintf(buf, FCOE_FCF_STATE_MAX_NAMELEN, "%s\n", name);
+}
+static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL);
+
+#define FCOE_MAX_MODENAME_LEN 20
+static ssize_t show_ctlr_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ const char *name;
+
+ name = get_fcoe_ctlr_mode_name(ctlr->mode);
+ if (!name)
+ return -EINVAL;
+ return snprintf(buf, FCOE_MAX_MODENAME_LEN,
+ "%s\n", name);
+}
+
+static ssize_t store_ctlr_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ int res;
+
+ if (count > FCOE_MAX_MODENAME_LEN)
+ return -EINVAL;
+
+
+ switch (ctlr->enabled) {
+ case FCOE_CTLR_ENABLED:
+ LIBFCOE_SYSFS_DBG(ctlr, "Cannot change mode when enabled.\n");
+ return -EBUSY;
+ case FCOE_CTLR_DISABLED:
+ if (!ctlr->f->set_fcoe_ctlr_mode) {
+ LIBFCOE_SYSFS_DBG(ctlr,
+ "Mode change not supported by LLD.\n");
+ return -ENOTSUPP;
+ }
+
+ res = sysfs_match_string(fip_conn_type_names, buf);
+ if (res < 0 || res == FIP_CONN_TYPE_UNKNOWN) {
+ LIBFCOE_SYSFS_DBG(ctlr, "Unknown mode %s provided.\n",
+ buf);
+ return -EINVAL;
+ }
+ ctlr->mode = res;
+
+ ctlr->f->set_fcoe_ctlr_mode(ctlr);
+ LIBFCOE_SYSFS_DBG(ctlr, "Mode changed to %s.\n", buf);
+
+ return count;
+ case FCOE_CTLR_UNUSED:
+ default:
+ LIBFCOE_SYSFS_DBG(ctlr, "Mode change not supported.\n");
+ return -ENOTSUPP;
+ }
+}
+
+static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO | S_IWUSR,
+ show_ctlr_mode, store_ctlr_mode);
+
+static ssize_t store_ctlr_enabled(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ bool enabled;
+ int rc;
+
+ if (*buf == '1')
+ enabled = true;
+ else if (*buf == '0')
+ enabled = false;
+ else
+ return -EINVAL;
+
+ switch (ctlr->enabled) {
+ case FCOE_CTLR_ENABLED:
+ if (enabled)
+ return count;
+ ctlr->enabled = FCOE_CTLR_DISABLED;
+ break;
+ case FCOE_CTLR_DISABLED:
+ if (!enabled)
+ return count;
+ ctlr->enabled = FCOE_CTLR_ENABLED;
+ break;
+ case FCOE_CTLR_UNUSED:
+ return -ENOTSUPP;
+ }
+
+ rc = ctlr->f->set_fcoe_ctlr_enabled(ctlr);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+static char *ctlr_enabled_state_names[] = {
+ [ FCOE_CTLR_ENABLED ] = "1",
+ [ FCOE_CTLR_DISABLED ] = "0",
+};
+fcoe_enum_name_search(ctlr_enabled_state, ctlr_enabled_state,
+ ctlr_enabled_state_names)
+#define FCOE_CTLR_ENABLED_MAX_NAMELEN 50
+
+static ssize_t show_ctlr_enabled_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ const char *name;
+
+ name = get_fcoe_ctlr_enabled_state_name(ctlr->enabled);
+ if (!name)
+ return -EINVAL;
+ return snprintf(buf, FCOE_CTLR_ENABLED_MAX_NAMELEN,
+ "%s\n", name);
+}
+
+static FCOE_DEVICE_ATTR(ctlr, enabled, S_IRUGO | S_IWUSR,
+ show_ctlr_enabled_state,
+ store_ctlr_enabled);
+
+static ssize_t store_ctlr_fip_resp(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr);
+
+ mutex_lock(&fip->ctlr_mutex);
+ if ((buf[1] == '\0') || ((buf[1] == '\n') && (buf[2] == '\0'))) {
+ if (buf[0] == '1') {
+ fip->fip_resp = 1;
+ mutex_unlock(&fip->ctlr_mutex);
+ return count;
+ }
+ if (buf[0] == '0') {
+ fip->fip_resp = 0;
+ mutex_unlock(&fip->ctlr_mutex);
+ return count;
+ }
+ }
+ mutex_unlock(&fip->ctlr_mutex);
+ return -EINVAL;
+}
+
+static ssize_t show_ctlr_fip_resp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr);
+
+ return sprintf(buf, "%d\n", fip->fip_resp ? 1 : 0);
+}
+
+static FCOE_DEVICE_ATTR(ctlr, fip_vlan_responder, S_IRUGO | S_IWUSR,
+ show_ctlr_fip_resp,
+ store_ctlr_fip_resp);
+
+static ssize_t
+fcoe_ctlr_var_store(u32 *var, const char *buf, size_t count)
+{
+ int err;
+ unsigned long v;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err || v > UINT_MAX)
+ return -EINVAL;
+
+ *var = v;
+
+ return count;
+}
+
+static ssize_t store_ctlr_r_a_tov(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+
+ if (ctlr_dev->enabled == FCOE_CTLR_ENABLED)
+ return -EBUSY;
+ if (ctlr_dev->enabled == FCOE_CTLR_DISABLED)
+ return fcoe_ctlr_var_store(&ctlr->lp->r_a_tov, buf, count);
+ return -ENOTSUPP;
+}
+
+static ssize_t show_ctlr_r_a_tov(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+
+ return sprintf(buf, "%d\n", ctlr->lp->r_a_tov);
+}
+
+static FCOE_DEVICE_ATTR(ctlr, r_a_tov, S_IRUGO | S_IWUSR,
+ show_ctlr_r_a_tov, store_ctlr_r_a_tov);
+
+static ssize_t store_ctlr_e_d_tov(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+
+ if (ctlr_dev->enabled == FCOE_CTLR_ENABLED)
+ return -EBUSY;
+ if (ctlr_dev->enabled == FCOE_CTLR_DISABLED)
+ return fcoe_ctlr_var_store(&ctlr->lp->e_d_tov, buf, count);
+ return -ENOTSUPP;
+}
+
+static ssize_t show_ctlr_e_d_tov(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fcoe_ctlr_device *ctlr_dev = dev_to_ctlr(dev);
+ struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+
+ return sprintf(buf, "%d\n", ctlr->lp->e_d_tov);
+}
+
+static FCOE_DEVICE_ATTR(ctlr, e_d_tov, S_IRUGO | S_IWUSR,
+ show_ctlr_e_d_tov, store_ctlr_e_d_tov);
+
+static ssize_t
+store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ struct fcoe_fcf_device *fcf;
+ unsigned long val;
+ int rc;
+
+ rc = fcoe_str_to_dev_loss(buf, &val);
+ if (rc)
+ return rc;
+
+ fcoe_ctlr_fcf_dev_loss_tmo(ctlr) = val;
+ mutex_lock(&ctlr->lock);
+ list_for_each_entry(fcf, &ctlr->fcfs, peers)
+ fcoe_fcf_set_dev_loss_tmo(fcf, val);
+ mutex_unlock(&ctlr->lock);
+ return count;
+}
+fcoe_ctlr_private_show_function(fcf_dev_loss_tmo, "%d\n", 20, );
+static FCOE_DEVICE_ATTR(ctlr, fcf_dev_loss_tmo, S_IRUGO | S_IWUSR,
+ show_fcoe_ctlr_device_fcf_dev_loss_tmo,
+ store_private_fcoe_ctlr_fcf_dev_loss_tmo);
+
+/* Link Error Status Block (LESB) */
+fcoe_ctlr_rd_attr(link_fail, "%u\n", 20);
+fcoe_ctlr_rd_attr(vlink_fail, "%u\n", 20);
+fcoe_ctlr_rd_attr(miss_fka, "%u\n", 20);
+fcoe_ctlr_rd_attr(symb_err, "%u\n", 20);
+fcoe_ctlr_rd_attr(err_block, "%u\n", 20);
+fcoe_ctlr_rd_attr(fcs_error, "%u\n", 20);
+
+fcoe_fcf_private_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
+fcoe_fcf_private_rd_attr_cast(switch_name, "0x%llx\n", 20, unsigned long long);
+fcoe_fcf_private_rd_attr(priority, "%u\n", 20);
+fcoe_fcf_private_rd_attr(fc_map, "0x%x\n", 20);
+fcoe_fcf_private_rd_attr(vfid, "%u\n", 20);
+fcoe_fcf_private_rd_attr(mac, "%pM\n", 20);
+fcoe_fcf_private_rd_attr(fka_period, "%u\n", 20);
+fcoe_fcf_rd_attr(selected, "%u\n", 20);
+fcoe_fcf_rd_attr(vlan_id, "%u\n", 20);
+
+fcoe_fcf_private_show_function(dev_loss_tmo, "%d\n", 20, )
+static ssize_t
+store_fcoe_fcf_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+ unsigned long val;
+ int rc;
+
+ rc = fcoe_str_to_dev_loss(buf, &val);
+ if (rc)
+ return rc;
+
+ rc = fcoe_fcf_set_dev_loss_tmo(fcf, val);
+ if (rc)
+ return rc;
+ return count;
+}
+static FCOE_DEVICE_ATTR(fcf, dev_loss_tmo, S_IRUGO | S_IWUSR,
+ show_fcoe_fcf_device_dev_loss_tmo,
+ store_fcoe_fcf_dev_loss_tmo);
+
+static struct attribute *fcoe_ctlr_lesb_attrs[] = {
+ &device_attr_fcoe_ctlr_link_fail.attr,
+ &device_attr_fcoe_ctlr_vlink_fail.attr,
+ &device_attr_fcoe_ctlr_miss_fka.attr,
+ &device_attr_fcoe_ctlr_symb_err.attr,
+ &device_attr_fcoe_ctlr_err_block.attr,
+ &device_attr_fcoe_ctlr_fcs_error.attr,
+ NULL,
+};
+
+static struct attribute_group fcoe_ctlr_lesb_attr_group = {
+ .name = "lesb",
+ .attrs = fcoe_ctlr_lesb_attrs,
+};
+
+static struct attribute *fcoe_ctlr_attrs[] = {
+ &device_attr_fcoe_ctlr_fip_vlan_responder.attr,
+ &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr,
+ &device_attr_fcoe_ctlr_r_a_tov.attr,
+ &device_attr_fcoe_ctlr_e_d_tov.attr,
+ &device_attr_fcoe_ctlr_enabled.attr,
+ &device_attr_fcoe_ctlr_mode.attr,
+ NULL,
+};
+
+static struct attribute_group fcoe_ctlr_attr_group = {
+ .attrs = fcoe_ctlr_attrs,
+};
+
+static const struct attribute_group *fcoe_ctlr_attr_groups[] = {
+ &fcoe_ctlr_attr_group,
+ &fcoe_ctlr_lesb_attr_group,
+ NULL,
+};
+
+static struct attribute *fcoe_fcf_attrs[] = {
+ &device_attr_fcoe_fcf_fabric_name.attr,
+ &device_attr_fcoe_fcf_switch_name.attr,
+ &device_attr_fcoe_fcf_dev_loss_tmo.attr,
+ &device_attr_fcoe_fcf_fc_map.attr,
+ &device_attr_fcoe_fcf_vfid.attr,
+ &device_attr_fcoe_fcf_mac.attr,
+ &device_attr_fcoe_fcf_priority.attr,
+ &device_attr_fcoe_fcf_fka_period.attr,
+ &device_attr_fcoe_fcf_state.attr,
+ &device_attr_fcoe_fcf_selected.attr,
+ &device_attr_fcoe_fcf_vlan_id.attr,
+ NULL
+};
+
+static struct attribute_group fcoe_fcf_attr_group = {
+ .attrs = fcoe_fcf_attrs,
+};
+
+static const struct attribute_group *fcoe_fcf_attr_groups[] = {
+ &fcoe_fcf_attr_group,
+ NULL,
+};
+
+static const struct bus_type fcoe_bus_type;
+
+static int fcoe_bus_match(struct device *dev,
+ const struct device_driver *drv)
+{
+ if (dev->bus == &fcoe_bus_type)
+ return 1;
+ return 0;
+}
+
+/**
+ * fcoe_ctlr_device_release() - Release the FIP ctlr memory
+ * @dev: Pointer to the FIP ctlr's embedded device
+ *
+ * Called when the last FIP ctlr reference is released.
+ */
+static void fcoe_ctlr_device_release(struct device *dev)
+{
+ struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+ kfree(ctlr);
+}
+
+/**
+ * fcoe_fcf_device_release() - Release the FIP fcf memory
+ * @dev: Pointer to the fcf's embedded device
+ *
+ * Called when the last FIP fcf reference is released.
+ */
+static void fcoe_fcf_device_release(struct device *dev)
+{
+ struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+ kfree(fcf);
+}
+
+static const struct device_type fcoe_ctlr_device_type = {
+ .name = "fcoe_ctlr",
+ .groups = fcoe_ctlr_attr_groups,
+ .release = fcoe_ctlr_device_release,
+};
+
+static const struct device_type fcoe_fcf_device_type = {
+ .name = "fcoe_fcf",
+ .groups = fcoe_fcf_attr_groups,
+ .release = fcoe_fcf_device_release,
+};
+
+static ssize_t ctlr_create_store(const struct bus_type *bus, const char *buf,
+ size_t count)
+{
+ return fcoe_ctlr_create_store(buf, count);
+}
+static BUS_ATTR_WO(ctlr_create);
+
+static ssize_t ctlr_destroy_store(const struct bus_type *bus, const char *buf,
+ size_t count)
+{
+ return fcoe_ctlr_destroy_store(buf, count);
+}
+static BUS_ATTR_WO(ctlr_destroy);
+
+static struct attribute *fcoe_bus_attrs[] = {
+ &bus_attr_ctlr_create.attr,
+ &bus_attr_ctlr_destroy.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(fcoe_bus);
+
+static const struct bus_type fcoe_bus_type = {
+ .name = "fcoe",
+ .match = &fcoe_bus_match,
+ .bus_groups = fcoe_bus_groups,
+};
+
+/**
+ * fcoe_ctlr_device_flush_work() - Flush a FIP ctlr's workqueue
+ * @ctlr: Pointer to the FIP ctlr whose workqueue is to be flushed
+ */
+static void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr)
+{
+ if (!fcoe_ctlr_work_q(ctlr)) {
+ printk(KERN_ERR
+ "ERROR: FIP Ctlr '%d' attempted to flush work, "
+ "when no workqueue created.\n", ctlr->id);
+ dump_stack();
+ return;
+ }
+
+ flush_workqueue(fcoe_ctlr_work_q(ctlr));
+}
+
+/**
+ * fcoe_ctlr_device_queue_work() - Schedule work for a FIP ctlr's workqueue
+ * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue
+ * @work: Work to queue for execution
+ *
+ * Return value:
+ * 1 on success / 0 already queued / < 0 for error
+ */
+static int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr,
+ struct work_struct *work)
+{
+ if (unlikely(!fcoe_ctlr_work_q(ctlr))) {
+ printk(KERN_ERR
+ "ERROR: FIP Ctlr '%d' attempted to queue work, "
+ "when no workqueue created.\n", ctlr->id);
+ dump_stack();
+
+ return -EINVAL;
+ }
+
+ return queue_work(fcoe_ctlr_work_q(ctlr), work);
+}
+
+/**
+ * fcoe_ctlr_device_flush_devloss() - Flush a FIP ctlr's devloss workqueue
+ * @ctlr: Pointer to FIP ctlr whose workqueue is to be flushed
+ */
+static void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr)
+{
+ if (!fcoe_ctlr_devloss_work_q(ctlr)) {
+ printk(KERN_ERR
+ "ERROR: FIP Ctlr '%d' attempted to flush work, "
+ "when no workqueue created.\n", ctlr->id);
+ dump_stack();
+ return;
+ }
+
+ flush_workqueue(fcoe_ctlr_devloss_work_q(ctlr));
+}
+
+/**
+ * fcoe_ctlr_device_queue_devloss_work() - Schedule work for a FIP ctlr's devloss workqueue
+ * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue
+ * @work: Work to queue for execution
+ * @delay: jiffies to delay the work queuing
+ *
+ * Return value:
+ * 1 on success / 0 already queued / < 0 for error
+ */
+static int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr,
+ struct delayed_work *work,
+ unsigned long delay)
+{
+ if (unlikely(!fcoe_ctlr_devloss_work_q(ctlr))) {
+ printk(KERN_ERR
+ "ERROR: FIP Ctlr '%d' attempted to queue work, "
+ "when no workqueue created.\n", ctlr->id);
+ dump_stack();
+
+ return -EINVAL;
+ }
+
+ return queue_delayed_work(fcoe_ctlr_devloss_work_q(ctlr), work, delay);
+}
+
+static int fcoe_fcf_device_match(struct fcoe_fcf_device *new,
+ struct fcoe_fcf_device *old)
+{
+ if (new->switch_name == old->switch_name &&
+ new->fabric_name == old->fabric_name &&
+ new->fc_map == old->fc_map &&
+ ether_addr_equal(new->mac, old->mac))
+ return 1;
+ return 0;
+}
+
+/**
+ * fcoe_ctlr_device_add() - Add a FIP ctlr to sysfs
+ * @parent: The parent device to which the fcoe_ctlr instance
+ * should be attached
+ * @f: The LLD's FCoE sysfs function template pointer
+ * @priv_size: Size to be allocated with the fcoe_ctlr_device for the LLD
+ *
+ * This routine allocates a FIP ctlr object with some additional memory
+ * for the LLD. The FIP ctlr is initialized, added to sysfs and then
+ * attributes are added to it.
+ */
+struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent,
+ struct fcoe_sysfs_function_template *f,
+ int priv_size)
+{
+ struct fcoe_ctlr_device *ctlr;
+ int error = 0;
+
+ ctlr = kzalloc(sizeof(struct fcoe_ctlr_device) + priv_size,
+ GFP_KERNEL);
+ if (!ctlr)
+ goto out;
+
+ ctlr->id = atomic_inc_return(&ctlr_num) - 1;
+ ctlr->f = f;
+ ctlr->mode = FIP_CONN_TYPE_FABRIC;
+ INIT_LIST_HEAD(&ctlr->fcfs);
+ mutex_init(&ctlr->lock);
+ ctlr->dev.parent = parent;
+ ctlr->dev.bus = &fcoe_bus_type;
+ ctlr->dev.type = &fcoe_ctlr_device_type;
+
+ ctlr->fcf_dev_loss_tmo = fcoe_fcf_dev_loss_tmo;
+
+ ctlr->work_q = alloc_ordered_workqueue("ctlr_wq_%d", WQ_MEM_RECLAIM,
+ ctlr->id);
+ if (!ctlr->work_q)
+ goto out_del;
+
+ ctlr->devloss_work_q = alloc_ordered_workqueue("ctlr_dl_wq_%d",
+ WQ_MEM_RECLAIM,
+ ctlr->id);
+ if (!ctlr->devloss_work_q)
+ goto out_del_q;
+
+ dev_set_name(&ctlr->dev, "ctlr_%d", ctlr->id);
+ error = device_register(&ctlr->dev);
+ if (error) {
+ destroy_workqueue(ctlr->devloss_work_q);
+ destroy_workqueue(ctlr->work_q);
+ put_device(&ctlr->dev);
+ return NULL;
+ }
+
+ return ctlr;
+
+out_del_q:
+ destroy_workqueue(ctlr->work_q);
+ ctlr->work_q = NULL;
+out_del:
+ kfree(ctlr);
+out:
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_ctlr_device_add);
+
+/**
+ * fcoe_ctlr_device_delete() - Delete a FIP ctlr and its subtree from sysfs
+ * @ctlr: A pointer to the ctlr to be deleted
+ *
+ * Deletes a FIP ctlr and any fcfs attached
+ * to it. Deleting fcfs will cause their childen
+ * to be deleted as well.
+ *
+ * The ctlr is detached from sysfs and it's resources
+ * are freed (work q), but the memory is not freed
+ * until its last reference is released.
+ *
+ * This routine expects no locks to be held before
+ * calling.
+ *
+ * TODO: Currently there are no callbacks to clean up LLD data
+ * for a fcoe_fcf_device. LLDs must keep this in mind as they need
+ * to clean up each of their LLD data for all fcoe_fcf_device before
+ * calling fcoe_ctlr_device_delete.
+ */
+void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *ctlr)
+{
+ struct fcoe_fcf_device *fcf, *next;
+ /* Remove any attached fcfs */
+ mutex_lock(&ctlr->lock);
+ list_for_each_entry_safe(fcf, next,
+ &ctlr->fcfs, peers) {
+ list_del(&fcf->peers);
+ fcf->state = FCOE_FCF_STATE_DELETED;
+ fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);
+ }
+ mutex_unlock(&ctlr->lock);
+
+ fcoe_ctlr_device_flush_work(ctlr);
+
+ destroy_workqueue(ctlr->devloss_work_q);
+ ctlr->devloss_work_q = NULL;
+ destroy_workqueue(ctlr->work_q);
+ ctlr->work_q = NULL;
+
+ device_unregister(&ctlr->dev);
+}
+EXPORT_SYMBOL_GPL(fcoe_ctlr_device_delete);
+
+/**
+ * fcoe_fcf_device_final_delete() - Final delete routine
+ * @work: The FIP fcf's embedded work struct
+ *
+ * It is expected that the fcf has been removed from
+ * the FIP ctlr's list before calling this routine.
+ */
+static void fcoe_fcf_device_final_delete(struct work_struct *work)
+{
+ struct fcoe_fcf_device *fcf =
+ container_of(work, struct fcoe_fcf_device, delete_work);
+ struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+
+ /*
+ * Cancel any outstanding timers. These should really exist
+ * only when rmmod'ing the LLDD and we're asking for
+ * immediate termination of the rports
+ */
+ if (!cancel_delayed_work(&fcf->dev_loss_work))
+ fcoe_ctlr_device_flush_devloss(ctlr);
+
+ device_unregister(&fcf->dev);
+}
+
+/**
+ * fip_timeout_deleted_fcf() - Delete a fcf when the devloss timer fires
+ * @work: The FIP fcf's embedded work struct
+ *
+ * Removes the fcf from the FIP ctlr's list of fcfs and
+ * queues the final deletion.
+ */
+static void fip_timeout_deleted_fcf(struct work_struct *work)
+{
+ struct fcoe_fcf_device *fcf =
+ container_of(work, struct fcoe_fcf_device, dev_loss_work.work);
+ struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+
+ mutex_lock(&ctlr->lock);
+
+ /*
+ * If the fcf is deleted or reconnected before the timer
+ * fires the devloss queue will be flushed, but the state will
+ * either be CONNECTED or DELETED. If that is the case we
+ * cancel deleting the fcf.
+ */
+ if (fcf->state != FCOE_FCF_STATE_DISCONNECTED)
+ goto out;
+
+ dev_printk(KERN_ERR, &fcf->dev,
+ "FIP fcf connection time out: removing fcf\n");
+
+ list_del(&fcf->peers);
+ fcf->state = FCOE_FCF_STATE_DELETED;
+ fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);
+
+out:
+ mutex_unlock(&ctlr->lock);
+}
+
+/**
+ * fcoe_fcf_device_delete() - Delete a FIP fcf
+ * @fcf: Pointer to the fcf which is to be deleted
+ *
+ * Queues the FIP fcf on the devloss workqueue
+ *
+ * Expects the ctlr_attrs mutex to be held for fcf
+ * state change.
+ */
+void fcoe_fcf_device_delete(struct fcoe_fcf_device *fcf)
+{
+ struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+ int timeout = fcf->dev_loss_tmo;
+
+ if (fcf->state != FCOE_FCF_STATE_CONNECTED)
+ return;
+
+ fcf->state = FCOE_FCF_STATE_DISCONNECTED;
+
+ /*
+ * FCF will only be re-connected by the LLD calling
+ * fcoe_fcf_device_add, and it should be setting up
+ * priv then.
+ */
+ fcf->priv = NULL;
+
+ fcoe_ctlr_device_queue_devloss_work(ctlr, &fcf->dev_loss_work,
+ timeout * HZ);
+}
+EXPORT_SYMBOL_GPL(fcoe_fcf_device_delete);
+
+/**
+ * fcoe_fcf_device_add() - Add a FCoE sysfs fcoe_fcf_device to the system
+ * @ctlr: The fcoe_ctlr_device that will be the fcoe_fcf_device parent
+ * @new_fcf: A temporary FCF used for lookups on the current list of fcfs
+ *
+ * Expects to be called with the ctlr->lock held
+ */
+struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *ctlr,
+ struct fcoe_fcf_device *new_fcf)
+{
+ struct fcoe_fcf_device *fcf;
+ int error = 0;
+
+ list_for_each_entry(fcf, &ctlr->fcfs, peers) {
+ if (fcoe_fcf_device_match(new_fcf, fcf)) {
+ if (fcf->state == FCOE_FCF_STATE_CONNECTED)
+ return fcf;
+
+ fcf->state = FCOE_FCF_STATE_CONNECTED;
+
+ if (!cancel_delayed_work(&fcf->dev_loss_work))
+ fcoe_ctlr_device_flush_devloss(ctlr);
+
+ return fcf;
+ }
+ }
+
+ fcf = kzalloc(sizeof(struct fcoe_fcf_device), GFP_ATOMIC);
+ if (unlikely(!fcf))
+ goto out;
+
+ INIT_WORK(&fcf->delete_work, fcoe_fcf_device_final_delete);
+ INIT_DELAYED_WORK(&fcf->dev_loss_work, fip_timeout_deleted_fcf);
+
+ fcf->dev.parent = &ctlr->dev;
+ fcf->dev.bus = &fcoe_bus_type;
+ fcf->dev.type = &fcoe_fcf_device_type;
+ fcf->id = atomic_inc_return(&fcf_num) - 1;
+ fcf->state = FCOE_FCF_STATE_UNKNOWN;
+
+ fcf->dev_loss_tmo = ctlr->fcf_dev_loss_tmo;
+
+ dev_set_name(&fcf->dev, "fcf_%d", fcf->id);
+
+ fcf->fabric_name = new_fcf->fabric_name;
+ fcf->switch_name = new_fcf->switch_name;
+ fcf->fc_map = new_fcf->fc_map;
+ fcf->vfid = new_fcf->vfid;
+ memcpy(fcf->mac, new_fcf->mac, ETH_ALEN);
+ fcf->priority = new_fcf->priority;
+ fcf->fka_period = new_fcf->fka_period;
+ fcf->selected = new_fcf->selected;
+
+ error = device_register(&fcf->dev);
+ if (error) {
+ put_device(&fcf->dev);
+ goto out;
+ }
+
+ fcf->state = FCOE_FCF_STATE_CONNECTED;
+ list_add_tail(&fcf->peers, &ctlr->fcfs);
+
+ return fcf;
+
+out:
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_fcf_device_add);
+
+int __init fcoe_sysfs_setup(void)
+{
+ atomic_set(&ctlr_num, 0);
+ atomic_set(&fcf_num, 0);
+
+ return bus_register(&fcoe_bus_type);
+}
+
+void __exit fcoe_sysfs_teardown(void)
+{
+ bus_unregister(&fcoe_bus_type);
+}
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 41068e8748e7..2f478426f16e 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -22,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
+#include <linux/ethtool.h>
#include <linux/errno.h>
#include <linux/crc32.h>
#include <scsi/libfcoe.h>
@@ -32,13 +21,13 @@ MODULE_AUTHOR("Open-FCoE.org");
MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs");
MODULE_LICENSE("GPL v2");
-static int fcoe_transport_create(const char *, struct kernel_param *);
-static int fcoe_transport_destroy(const char *, struct kernel_param *);
+static int fcoe_transport_create(const char *, const struct kernel_param *);
+static int fcoe_transport_destroy(const char *, const struct kernel_param *);
static int fcoe_transport_show(char *buffer, const struct kernel_param *kp);
static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device);
static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device);
-static int fcoe_transport_enable(const char *, struct kernel_param *);
-static int fcoe_transport_disable(const char *, struct kernel_param *);
+static int fcoe_transport_enable(const char *, const struct kernel_param *);
+static int fcoe_transport_disable(const char *, const struct kernel_param *);
static int libfcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr);
@@ -58,7 +47,7 @@ MODULE_PARM_DESC(show, " Show attached FCoE transports");
module_param_call(create, fcoe_transport_create, NULL,
(void *)FIP_MODE_FABRIC, S_IWUSR);
__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface");
+MODULE_PARM_DESC(create, " Creates fcoe instance on an ethernet interface");
module_param_call(create_vn2vn, fcoe_transport_create, NULL,
(void *)FIP_MODE_VN2VN, S_IWUSR);
@@ -68,21 +57,247 @@ MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance "
module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface");
+MODULE_PARM_DESC(destroy, " Destroys fcoe instance on an ethernet interface");
module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(enable, "string");
-MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface.");
+MODULE_PARM_DESC(enable, " Enables fcoe on an ethernet interface.");
module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(disable, "string");
-MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface.");
+MODULE_PARM_DESC(disable, " Disables fcoe on an ethernet interface.");
/* notification function for packets from net device */
static struct notifier_block libfcoe_notifier = {
.notifier_call = libfcoe_device_notification,
};
+static const struct {
+ u32 fc_port_speed;
+#define SPEED_2000 2000
+#define SPEED_4000 4000
+#define SPEED_8000 8000
+#define SPEED_16000 16000
+#define SPEED_32000 32000
+ u32 eth_port_speed;
+} fcoe_port_speed_mapping[] = {
+ { FC_PORTSPEED_1GBIT, SPEED_1000 },
+ { FC_PORTSPEED_2GBIT, SPEED_2000 },
+ { FC_PORTSPEED_4GBIT, SPEED_4000 },
+ { FC_PORTSPEED_8GBIT, SPEED_8000 },
+ { FC_PORTSPEED_10GBIT, SPEED_10000 },
+ { FC_PORTSPEED_16GBIT, SPEED_16000 },
+ { FC_PORTSPEED_20GBIT, SPEED_20000 },
+ { FC_PORTSPEED_25GBIT, SPEED_25000 },
+ { FC_PORTSPEED_32GBIT, SPEED_32000 },
+ { FC_PORTSPEED_40GBIT, SPEED_40000 },
+ { FC_PORTSPEED_50GBIT, SPEED_50000 },
+ { FC_PORTSPEED_100GBIT, SPEED_100000 },
+};
+
+static inline u32 eth2fc_speed(u32 eth_port_speed)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fcoe_port_speed_mapping); i++) {
+ if (fcoe_port_speed_mapping[i].eth_port_speed == eth_port_speed)
+ return fcoe_port_speed_mapping[i].fc_port_speed;
+ }
+
+ return FC_PORTSPEED_UNKNOWN;
+}
+
+/**
+ * fcoe_link_speed_update() - Update the supported and actual link speeds
+ * @lport: The local port to update speeds for
+ *
+ * Returns: 0 if the ethtool query was successful
+ * -1 if the ethtool query failed
+ */
+int fcoe_link_speed_update(struct fc_lport *lport)
+{
+ struct net_device *netdev = fcoe_get_netdev(lport);
+ struct ethtool_link_ksettings ecmd;
+
+ if (!__ethtool_get_link_ksettings(netdev, &ecmd)) {
+ lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT |
+ FC_PORTSPEED_10GBIT |
+ FC_PORTSPEED_20GBIT |
+ FC_PORTSPEED_40GBIT);
+
+ if (ecmd.link_modes.supported[0] & (
+ SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_1000baseKX_Full))
+ lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+
+ if (ecmd.link_modes.supported[0] & (
+ SUPPORTED_10000baseT_Full |
+ SUPPORTED_10000baseKX4_Full |
+ SUPPORTED_10000baseKR_Full |
+ SUPPORTED_10000baseR_FEC))
+ lport->link_supported_speeds |= FC_PORTSPEED_10GBIT;
+
+ if (ecmd.link_modes.supported[0] & (
+ SUPPORTED_20000baseMLD2_Full |
+ SUPPORTED_20000baseKR2_Full))
+ lport->link_supported_speeds |= FC_PORTSPEED_20GBIT;
+
+ if (ecmd.link_modes.supported[0] & (
+ SUPPORTED_40000baseKR4_Full |
+ SUPPORTED_40000baseCR4_Full |
+ SUPPORTED_40000baseSR4_Full |
+ SUPPORTED_40000baseLR4_Full))
+ lport->link_supported_speeds |= FC_PORTSPEED_40GBIT;
+
+ lport->link_speed = eth2fc_speed(ecmd.base.speed);
+ return 0;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(fcoe_link_speed_update);
+
+/**
+ * __fcoe_get_lesb() - Get the Link Error Status Block (LESB) for a given lport
+ * @lport: The local port to update speeds for
+ * @fc_lesb: Pointer to the LESB to be filled up
+ * @netdev: Pointer to the netdev that is associated with the lport
+ *
+ * Note, the Link Error Status Block (LESB) for FCoE is defined in FC-BB-6
+ * Clause 7.11 in v1.04.
+ */
+void __fcoe_get_lesb(struct fc_lport *lport,
+ struct fc_els_lesb *fc_lesb,
+ struct net_device *netdev)
+{
+ unsigned int cpu;
+ u32 lfc, vlfc, mdac;
+ struct fc_stats *stats;
+ struct fcoe_fc_els_lesb *lesb;
+ struct rtnl_link_stats64 temp;
+
+ lfc = 0;
+ vlfc = 0;
+ mdac = 0;
+ lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
+ memset(lesb, 0, sizeof(*lesb));
+ for_each_possible_cpu(cpu) {
+ stats = per_cpu_ptr(lport->stats, cpu);
+ lfc += READ_ONCE(stats->LinkFailureCount);
+ vlfc += READ_ONCE(stats->VLinkFailureCount);
+ mdac += READ_ONCE(stats->MissDiscAdvCount);
+ }
+ lesb->lesb_link_fail = htonl(lfc);
+ lesb->lesb_vlink_fail = htonl(vlfc);
+ lesb->lesb_miss_fka = htonl(mdac);
+ lesb->lesb_fcs_error =
+ htonl(dev_get_stats(netdev, &temp)->rx_crc_errors);
+}
+EXPORT_SYMBOL_GPL(__fcoe_get_lesb);
+
+/**
+ * fcoe_get_lesb() - Fill the FCoE Link Error Status Block
+ * @lport: the local port
+ * @fc_lesb: the link error status block
+ */
+void fcoe_get_lesb(struct fc_lport *lport,
+ struct fc_els_lesb *fc_lesb)
+{
+ struct net_device *netdev = fcoe_get_netdev(lport);
+
+ __fcoe_get_lesb(lport, fc_lesb, netdev);
+}
+EXPORT_SYMBOL_GPL(fcoe_get_lesb);
+
+/**
+ * fcoe_ctlr_get_lesb() - Get the Link Error Status Block (LESB) for a given
+ * fcoe controller device
+ * @ctlr_dev: The given fcoe controller device
+ *
+ */
+void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev)
+{
+ struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+ struct net_device *netdev = fcoe_get_netdev(fip->lp);
+ struct fc_els_lesb *fc_lesb;
+
+ fc_lesb = (struct fc_els_lesb *)(&ctlr_dev->lesb);
+ __fcoe_get_lesb(fip->lp, fc_lesb, netdev);
+}
+EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb);
+
+void fcoe_wwn_to_str(u64 wwn, char *buf, int len)
+{
+ u8 wwpn[8];
+
+ u64_to_wwn(wwn, wwpn);
+ snprintf(buf, len, "%02x%02x%02x%02x%02x%02x%02x%02x",
+ wwpn[0], wwpn[1], wwpn[2], wwpn[3],
+ wwpn[4], wwpn[5], wwpn[6], wwpn[7]);
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_to_str);
+
+/**
+ * fcoe_validate_vport_create() - Validate a vport before creating it
+ * @vport: NPIV port to be created
+ *
+ * This routine is meant to add validation for a vport before creating it
+ * via fcoe_vport_create().
+ * Current validations are:
+ * - WWPN supplied is unique for given lport
+ */
+int fcoe_validate_vport_create(struct fc_vport *vport)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fc_lport *vn_port;
+ int rc = 0;
+ char buf[32];
+
+ mutex_lock(&n_port->lp_mutex);
+
+ fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
+ /* Check if the wwpn is not same as that of the lport */
+ if (!memcmp(&n_port->wwpn, &vport->port_name, sizeof(u64))) {
+ LIBFCOE_TRANSPORT_DBG("vport WWPN 0x%s is same as that of the "
+ "base port WWPN\n", buf);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Check if there is any existing vport with same wwpn */
+ list_for_each_entry(vn_port, &n_port->vports, list) {
+ if (!memcmp(&vn_port->wwpn, &vport->port_name, sizeof(u64))) {
+ LIBFCOE_TRANSPORT_DBG("vport with given WWPN 0x%s "
+ "already exists\n", buf);
+ rc = -EINVAL;
+ break;
+ }
+ }
+out:
+ mutex_unlock(&n_port->lp_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(fcoe_validate_vport_create);
+
+/**
+ * fcoe_get_wwn() - Get the world wide name from LLD if it supports it
+ * @netdev: the associated net device
+ * @wwn: the output WWN
+ * @type: the type of WWN (WWPN or WWNN)
+ *
+ * Returns: 0 for success
+ */
+int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
+{
+ const struct net_device_ops *ops = netdev->netdev_ops;
+
+ if (ops->ndo_fcoe_get_wwn)
+ return ops->ndo_fcoe_get_wwn(netdev, wwn, type);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(fcoe_get_wwn);
+
/**
* fcoe_fc_crc() - Calculates the CRC for a given frame
* @fp: The frame to be checksumed
@@ -94,7 +309,7 @@ static struct notifier_block libfcoe_notifier = {
u32 fcoe_fc_crc(struct fc_frame *fp)
{
struct sk_buff *skb = fp_skb(fp);
- struct skb_frag_struct *frag;
+ skb_frag_t *frag;
unsigned char *data;
unsigned long off, len, clen;
u32 crc;
@@ -104,14 +319,14 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
frag = &skb_shinfo(skb)->frags[i];
- off = frag->page_offset;
- len = frag->size;
+ off = skb_frag_off(frag);
+ len = skb_frag_size(frag);
while (len > 0) {
clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
- data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
- KM_SKB_DATA_SOFTIRQ);
+ data = kmap_atomic(
+ skb_frag_page(frag) + (off >> PAGE_SHIFT));
crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
- kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
+ kunmap_atomic(data);
off += clen;
len -= clen;
}
@@ -168,6 +383,7 @@ EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
/**
* fcoe_check_wait_queue() - Attempt to clear the transmit backlog
* @lport: The local port whose backlog is to be cleared
+ * @skb: The received FIP packet
*
* This empties the wait_queue, dequeues the head of the wait_queue queue
* and calls fcoe_start_io() for each packet. If all skb have been
@@ -225,13 +441,15 @@ EXPORT_SYMBOL_GPL(fcoe_check_wait_queue);
/**
* fcoe_queue_timer() - The fcoe queue timer
- * @lport: The local port
+ * @t: Timer context use to obtain the FCoE port
*
* Calls fcoe_check_wait_queue on timeout
*/
-void fcoe_queue_timer(ulong lport)
+void fcoe_queue_timer(struct timer_list *t)
{
- fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
+ struct fcoe_port *port = timer_container_of(port, t, timer);
+
+ fcoe_check_wait_queue(port->lport, NULL);
}
EXPORT_SYMBOL_GPL(fcoe_queue_timer);
@@ -401,7 +619,7 @@ static int __init fcoe_transport_init(void)
return 0;
}
-static int __exit fcoe_transport_exit(void)
+static int fcoe_transport_exit(void)
{
struct fcoe_transport *ft;
@@ -456,6 +674,7 @@ static void fcoe_del_netdev_mapping(struct net_device *netdev)
/**
* fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which
* it was created
+ * @netdev: The net device that the FCoE interface is on
*
* Returns : ptr to the fcoe transport that supports this netdev or NULL
* if not found.
@@ -492,7 +711,7 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
char ifname[IFNAMSIZ + 2];
if (buffer) {
- strlcpy(ifname, buffer, IFNAMSIZ);
+ strscpy(ifname, buffer, IFNAMSIZ);
cp = ifname + strlen(ifname);
while (--cp >= ifname && *cp == '\n')
*cp = '\0';
@@ -514,18 +733,117 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
static int libfcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr)
{
- struct net_device *netdev = ptr;
+ struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
switch (event) {
case NETDEV_UNREGISTER:
- printk(KERN_ERR "libfcoe_device_notification: NETDEV_UNREGISTER %s\n",
- netdev->name);
+ LIBFCOE_TRANSPORT_DBG("NETDEV_UNREGISTER %s\n",
+ netdev->name);
fcoe_del_netdev_mapping(netdev);
break;
}
return NOTIFY_OK;
}
+ssize_t fcoe_ctlr_create_store(const char *buf, size_t count)
+{
+ struct net_device *netdev = NULL;
+ struct fcoe_transport *ft = NULL;
+ int rc = 0;
+ int err;
+
+ mutex_lock(&ft_mutex);
+
+ netdev = fcoe_if_to_netdev(buf);
+ if (!netdev) {
+ LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buf);
+ rc = -ENODEV;
+ goto out_nodev;
+ }
+
+ ft = fcoe_netdev_map_lookup(netdev);
+ if (ft) {
+ LIBFCOE_TRANSPORT_DBG("transport %s already has existing "
+ "FCoE instance on %s.\n",
+ ft->name, netdev->name);
+ rc = -EEXIST;
+ goto out_putdev;
+ }
+
+ ft = fcoe_transport_lookup(netdev);
+ if (!ft) {
+ LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n",
+ netdev->name);
+ rc = -ENODEV;
+ goto out_putdev;
+ }
+
+ /* pass to transport create */
+ err = ft->alloc ? ft->alloc(netdev) : -ENODEV;
+ if (err) {
+ fcoe_del_netdev_mapping(netdev);
+ rc = -ENOMEM;
+ goto out_putdev;
+ }
+
+ err = fcoe_add_netdev_mapping(netdev, ft);
+ if (err) {
+ LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping "
+ "for FCoE transport %s for %s.\n",
+ ft->name, netdev->name);
+ rc = -ENODEV;
+ goto out_putdev;
+ }
+
+ LIBFCOE_TRANSPORT_DBG("transport %s succeeded to create fcoe on %s.\n",
+ ft->name, netdev->name);
+
+out_putdev:
+ dev_put(netdev);
+out_nodev:
+ mutex_unlock(&ft_mutex);
+ if (rc)
+ return rc;
+ return count;
+}
+
+ssize_t fcoe_ctlr_destroy_store(const char *buf, size_t count)
+{
+ int rc = -ENODEV;
+ struct net_device *netdev = NULL;
+ struct fcoe_transport *ft = NULL;
+
+ mutex_lock(&ft_mutex);
+
+ netdev = fcoe_if_to_netdev(buf);
+ if (!netdev) {
+ LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buf);
+ goto out_nodev;
+ }
+
+ ft = fcoe_netdev_map_lookup(netdev);
+ if (!ft) {
+ LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n",
+ netdev->name);
+ goto out_putdev;
+ }
+
+ /* pass to transport destroy */
+ rc = ft->destroy(netdev);
+ if (rc)
+ goto out_putdev;
+
+ fcoe_del_netdev_mapping(netdev);
+ LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n",
+ ft->name, (rc) ? "failed" : "succeeded",
+ netdev->name);
+ rc = count; /* required for successful return */
+out_putdev:
+ dev_put(netdev);
+out_nodev:
+ mutex_unlock(&ft_mutex);
+ return rc;
+}
/**
* fcoe_transport_create() - Create a fcoe interface
@@ -537,12 +855,13 @@ static int libfcoe_device_notification(struct notifier_block *notifier,
*
* Returns: 0 for success
*/
-static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_create(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
+ enum fip_mode fip_mode = (enum fip_mode)(uintptr_t)kp->arg;
mutex_lock(&ft_mutex);
@@ -602,7 +921,8 @@ out_nodev:
*
* Returns: 0 for success
*/
-static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_destroy(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -646,7 +966,8 @@ out_nodev:
*
* Returns: 0 for success
*/
-static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_disable(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -668,11 +989,7 @@ out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
-
- if (rc == -ERESTARTSYS)
- return restart_syscall();
- else
- return rc;
+ return rc;
}
/**
@@ -684,7 +1001,8 @@ out_nodev:
*
* Returns: 0 for success
*/
-static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_enable(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -714,9 +1032,17 @@ out_nodev:
*/
static int __init libfcoe_init(void)
{
- fcoe_transport_init();
+ int rc = 0;
- return 0;
+ rc = fcoe_transport_init();
+ if (rc)
+ return rc;
+
+ rc = fcoe_sysfs_setup();
+ if (rc)
+ fcoe_transport_exit();
+
+ return rc;
}
module_init(libfcoe_init);
@@ -725,6 +1051,7 @@ module_init(libfcoe_init);
*/
static void __exit libfcoe_exit(void)
{
+ fcoe_sysfs_teardown();
fcoe_transport_exit();
}
module_exit(libfcoe_exit);
diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h
index 6af5fc3a17d8..b8bdfab51a58 100644
--- a/drivers/scsi/fcoe/libfcoe.h
+++ b/drivers/scsi/fcoe/libfcoe.h
@@ -1,10 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _FCOE_LIBFCOE_H_
#define _FCOE_LIBFCOE_H_
extern unsigned int libfcoe_debug_logging;
-#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
-#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
-#define LIBFCOE_TRANSPORT_LOGGING 0x04 /* FCoE transport logging */
+#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
+#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
+#define LIBFCOE_TRANSPORT_LOGGING 0x04 /* FCoE transport logging */
+#define LIBFCOE_SYSFS_LOGGING 0x08 /* fcoe_sysfs logging */
#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \
do { \
@@ -16,16 +18,19 @@ do { \
#define LIBFCOE_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \
- printk(KERN_INFO "libfcoe: " fmt, ##args);)
+ pr_info("libfcoe: " fmt, ##args);)
#define LIBFCOE_FIP_DBG(fip, fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \
- printk(KERN_INFO "host%d: fip: " fmt, \
- (fip)->lp->host->host_no, ##args);)
+ pr_info("host%d: fip: " fmt, \
+ (fip)->lp->host->host_no, ##args);)
#define LIBFCOE_TRANSPORT_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_TRANSPORT_LOGGING, \
- printk(KERN_INFO "%s: " fmt, \
- __func__, ##args);)
+ pr_info("%s: " fmt, __func__, ##args);)
+
+#define LIBFCOE_SYSFS_DBG(cdev, fmt, args...) \
+ LIBFCOE_CHECK_LOGGING(LIBFCOE_SYSFS_LOGGING, \
+ pr_info("ctlr_%d: " fmt, cdev->id, ##args);)
#endif /* _FCOE_LIBFCOE_H_ */
diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c
deleted file mode 100644
index a2c6135d337e..000000000000
--- a/drivers/scsi/fd_mcs.c
+++ /dev/null
@@ -1,1355 +0,0 @@
-/* fd_mcs.c -- Future Domain MCS 600/700 (or IBM OEM) driver
- *
- * FutureDomain MCS-600/700 v0.2 03/11/1998 by ZP Gu (zpg@castle.net)
- *
- * This driver is cloned from fdomain.* to specifically support
- * the Future Domain MCS 600/700 MCA SCSI adapters. Some PS/2s
- * also equipped with IBM Fast SCSI Adapter/A which is an OEM
- * of MCS 700.
- *
- * This driver also supports Reply SB16/SCSI card (the SCSI part).
- *
- * What makes this driver different is that this driver is MCA only
- * and it supports multiple adapters in the same system, IRQ
- * sharing, some driver statistics, and maps highest SCSI id to sda.
- * All cards are auto-detected.
- *
- * Assumptions: TMC-1800/18C50/18C30, BIOS >= 3.4
- *
- * LILO command-line options:
- * fd_mcs=<FIFO_COUNT>[,<FIFO_SIZE>]
- *
- * ********************************************************
- * Please see Copyrights/Comments in fdomain.* for credits.
- * Following is from fdomain.c for acknowledgement:
- *
- * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Wed Oct 2 11:10:55 1996 by r.faith@ieee.org
- * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
- *
- * $Id: fdomain.c,v 5.45 1996/10/02 15:13:06 root Exp $
-
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
-
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
-
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
-
- **************************************************************************
-
- NOTES ON USER DEFINABLE OPTIONS:
-
- DEBUG: This turns on the printing of various debug information.
-
- ENABLE_PARITY: This turns on SCSI parity checking. With the current
- driver, all attached devices must support SCSI parity. If none of your
- devices support parity, then you can probably get the driver to work by
- turning this option off. I have no way of testing this, however, and it
- would appear that no one ever uses this option.
-
- FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
- 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
- the SCSI device, an interrupt will be raised. Therefore, this could be as
- low as 0, or as high as 16. Note, however, that values which are too high
- or too low seem to prevent any interrupts from occurring, and thereby lock
- up the machine. I have found that 2 is a good number, but throughput may
- be increased by changing this value to values which are close to 2.
- Please let me know if you try any different values.
- [*****Now a runtime option*****]
-
- RESELECTION: This is no longer an option, since I gave up trying to
- implement it in version 4.x of this driver. It did not improve
- performance at all and made the driver unstable (because I never found one
- of the two race conditions which were introduced by the multiple
- outstanding command code). The instability seems a very high price to pay
- just so that you don't have to wait for the tape to rewind. If you want
- this feature implemented, send me patches. I'll be happy to send a copy
- of my (broken) driver to anyone who would like to see a copy.
-
- **************************************************************************/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/proc_fs.h>
-#include <linux/delay.h>
-#include <linux/mca.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <scsi/scsicam.h>
-#include <linux/mca-legacy.h>
-
-#include <asm/io.h>
-#include <asm/system.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-#define DRIVER_VERSION "v0.2 by ZP Gu<zpg@castle.net>"
-
-/* START OF USER DEFINABLE OPTIONS */
-
-#define DEBUG 0 /* Enable debugging output */
-#define ENABLE_PARITY 1 /* Enable SCSI Parity */
-
-/* END OF USER DEFINABLE OPTIONS */
-
-#if DEBUG
-#define EVERY_ACCESS 0 /* Write a line on every scsi access */
-#define ERRORS_ONLY 1 /* Only write a line if there is an error */
-#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
-#define DEBUG_ABORT 1 /* Debug abort() routine */
-#define DEBUG_RESET 1 /* Debug reset() routine */
-#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
-#else
-#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
-#define ERRORS_ONLY 0
-#define DEBUG_MESSAGES 0
-#define DEBUG_ABORT 0
-#define DEBUG_RESET 0
-#define DEBUG_RACE 0
-#endif
-
-/* Errors are reported on the line, so we don't need to report them again */
-#if EVERY_ACCESS
-#undef ERRORS_ONLY
-#define ERRORS_ONLY 0
-#endif
-
-#if ENABLE_PARITY
-#define PARITY_MASK 0x08
-#else
-#define PARITY_MASK 0x00
-#endif
-
-enum chip_type {
- unknown = 0x00,
- tmc1800 = 0x01,
- tmc18c50 = 0x02,
- tmc18c30 = 0x03,
-};
-
-enum {
- in_arbitration = 0x02,
- in_selection = 0x04,
- in_other = 0x08,
- disconnect = 0x10,
- aborted = 0x20,
- sent_ident = 0x40,
-};
-
-enum in_port_type {
- Read_SCSI_Data = 0,
- SCSI_Status = 1,
- TMC_Status = 2,
- FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
- Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
- LSB_ID_Code = 5,
- MSB_ID_Code = 6,
- Read_Loopback = 7,
- SCSI_Data_NoACK = 8,
- Interrupt_Status = 9,
- Configuration1 = 10,
- Configuration2 = 11, /* tmc18c50/tmc18c30 only */
- Read_FIFO = 12,
- FIFO_Data_Count = 14
-};
-
-enum out_port_type {
- Write_SCSI_Data = 0,
- SCSI_Cntl = 1,
- Interrupt_Cntl = 2,
- SCSI_Mode_Cntl = 3,
- TMC_Cntl = 4,
- Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
- Write_Loopback = 7,
- IO_Control = 11, /* tmc18c30 only */
- Write_FIFO = 12
-};
-
-struct fd_hostdata {
- unsigned long _bios_base;
- int _bios_major;
- int _bios_minor;
- volatile int _in_command;
- Scsi_Cmnd *_current_SC;
- enum chip_type _chip;
- int _adapter_mask;
- int _fifo_count; /* Number of 512 byte blocks before INTR */
-
- char _adapter_name[64];
-#if DEBUG_RACE
- volatile int _in_interrupt_flag;
-#endif
-
- int _SCSI_Mode_Cntl_port;
- int _FIFO_Data_Count_port;
- int _Interrupt_Cntl_port;
- int _Interrupt_Status_port;
- int _Interrupt_Cond_port;
- int _Read_FIFO_port;
- int _Read_SCSI_Data_port;
- int _SCSI_Cntl_port;
- int _SCSI_Data_NoACK_port;
- int _SCSI_Status_port;
- int _TMC_Cntl_port;
- int _TMC_Status_port;
- int _Write_FIFO_port;
- int _Write_SCSI_Data_port;
-
- int _FIFO_Size; /* = 0x2000; 8k FIFO for
- pre-tmc18c30 chips */
- /* simple stats */
- int _Bytes_Read;
- int _Bytes_Written;
- int _INTR_Processed;
-};
-
-#define FD_MAX_HOSTS 3 /* enough? */
-
-#define HOSTDATA(shpnt) ((struct fd_hostdata *) shpnt->hostdata)
-#define bios_base (HOSTDATA(shpnt)->_bios_base)
-#define bios_major (HOSTDATA(shpnt)->_bios_major)
-#define bios_minor (HOSTDATA(shpnt)->_bios_minor)
-#define in_command (HOSTDATA(shpnt)->_in_command)
-#define current_SC (HOSTDATA(shpnt)->_current_SC)
-#define chip (HOSTDATA(shpnt)->_chip)
-#define adapter_mask (HOSTDATA(shpnt)->_adapter_mask)
-#define FIFO_COUNT (HOSTDATA(shpnt)->_fifo_count)
-#define adapter_name (HOSTDATA(shpnt)->_adapter_name)
-#if DEBUG_RACE
-#define in_interrupt_flag (HOSTDATA(shpnt)->_in_interrupt_flag)
-#endif
-#define SCSI_Mode_Cntl_port (HOSTDATA(shpnt)->_SCSI_Mode_Cntl_port)
-#define FIFO_Data_Count_port (HOSTDATA(shpnt)->_FIFO_Data_Count_port)
-#define Interrupt_Cntl_port (HOSTDATA(shpnt)->_Interrupt_Cntl_port)
-#define Interrupt_Status_port (HOSTDATA(shpnt)->_Interrupt_Status_port)
-#define Interrupt_Cond_port (HOSTDATA(shpnt)->_Interrupt_Cond_port)
-#define Read_FIFO_port (HOSTDATA(shpnt)->_Read_FIFO_port)
-#define Read_SCSI_Data_port (HOSTDATA(shpnt)->_Read_SCSI_Data_port)
-#define SCSI_Cntl_port (HOSTDATA(shpnt)->_SCSI_Cntl_port)
-#define SCSI_Data_NoACK_port (HOSTDATA(shpnt)->_SCSI_Data_NoACK_port)
-#define SCSI_Status_port (HOSTDATA(shpnt)->_SCSI_Status_port)
-#define TMC_Cntl_port (HOSTDATA(shpnt)->_TMC_Cntl_port)
-#define TMC_Status_port (HOSTDATA(shpnt)->_TMC_Status_port)
-#define Write_FIFO_port (HOSTDATA(shpnt)->_Write_FIFO_port)
-#define Write_SCSI_Data_port (HOSTDATA(shpnt)->_Write_SCSI_Data_port)
-#define FIFO_Size (HOSTDATA(shpnt)->_FIFO_Size)
-#define Bytes_Read (HOSTDATA(shpnt)->_Bytes_Read)
-#define Bytes_Written (HOSTDATA(shpnt)->_Bytes_Written)
-#define INTR_Processed (HOSTDATA(shpnt)->_INTR_Processed)
-
-struct fd_mcs_adapters_struct {
- char *name;
- int id;
- enum chip_type fd_chip;
- int fifo_size;
- int fifo_count;
-};
-
-#define REPLY_ID 0x5137
-
-static struct fd_mcs_adapters_struct fd_mcs_adapters[] = {
- {"Future Domain SCSI Adapter MCS-700(18C50)",
- 0x60e9,
- tmc18c50,
- 0x2000,
- 4},
- {"Future Domain SCSI Adapter MCS-600/700(TMC-1800)",
- 0x6127,
- tmc1800,
- 0x2000,
- 4},
- {"Reply Sound Blaster/SCSI Adapter",
- REPLY_ID,
- tmc18c30,
- 0x800,
- 2},
-};
-
-#define FD_BRDS ARRAY_SIZE(fd_mcs_adapters)
-
-static irqreturn_t fd_mcs_intr(int irq, void *dev_id);
-
-static unsigned long addresses[] = { 0xc8000, 0xca000, 0xce000, 0xde000 };
-static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
-static unsigned short interrupts[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
-
-/* host information */
-static int found = 0;
-static struct Scsi_Host *hosts[FD_MAX_HOSTS + 1] = { NULL };
-
-static int user_fifo_count = 0;
-static int user_fifo_size = 0;
-
-#ifndef MODULE
-static int __init fd_mcs_setup(char *str)
-{
- static int done_setup = 0;
- int ints[3];
-
- get_options(str, 3, ints);
- if (done_setup++ || ints[0] < 1 || ints[0] > 2 || ints[1] < 1 || ints[1] > 16) {
- printk("fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE\n");
- return 0;
- }
-
- user_fifo_count = ints[0] >= 1 ? ints[1] : 0;
- user_fifo_size = ints[0] >= 2 ? ints[2] : 0;
- return 1;
-}
-
-__setup("fd_mcs=", fd_mcs_setup);
-#endif /* !MODULE */
-
-static void print_banner(struct Scsi_Host *shpnt)
-{
- printk("scsi%d <fd_mcs>: ", shpnt->host_no);
-
- if (bios_base) {
- printk("BIOS at 0x%lX", bios_base);
- } else {
- printk("No BIOS");
- }
-
- printk(", HostID %d, %s Chip, IRQ %d, IO 0x%lX\n", shpnt->this_id, chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? "TMC-18C30" : (chip == tmc1800 ? "TMC-1800" : "Unknown")), shpnt->irq, shpnt->io_port);
-}
-
-
-static void do_pause(unsigned amount)
-{ /* Pause for amount*10 milliseconds */
- do {
- mdelay(10);
- } while (--amount);
-}
-
-static void fd_mcs_make_bus_idle(struct Scsi_Host *shpnt)
-{
- outb(0, SCSI_Cntl_port);
- outb(0, SCSI_Mode_Cntl_port);
- if (chip == tmc18c50 || chip == tmc18c30)
- outb(0x21 | PARITY_MASK, TMC_Cntl_port); /* Clear forced intr. */
- else
- outb(0x01 | PARITY_MASK, TMC_Cntl_port);
-}
-
-static int fd_mcs_detect(struct scsi_host_template * tpnt)
-{
- int loop;
- struct Scsi_Host *shpnt;
-
- /* get id, port, bios, irq */
- int slot;
- u_char pos2, pos3, pos4;
- int id, port, irq;
- unsigned long bios;
-
- /* if not MCA machine, return */
- if (!MCA_bus)
- return 0;
-
- /* changeable? */
- id = 7;
-
- for (loop = 0; loop < FD_BRDS; loop++) {
- slot = 0;
- while (MCA_NOTFOUND != (slot = mca_find_adapter(fd_mcs_adapters[loop].id, slot))) {
-
- /* if we get this far, an adapter has been detected and is
- enabled */
-
- printk(KERN_INFO "scsi <fd_mcs>: %s at slot %d\n", fd_mcs_adapters[loop].name, slot + 1);
-
- pos2 = mca_read_stored_pos(slot, 2);
- pos3 = mca_read_stored_pos(slot, 3);
- pos4 = mca_read_stored_pos(slot, 4);
-
- /* ready for next probe */
- slot++;
-
- if (fd_mcs_adapters[loop].id == REPLY_ID) { /* reply card */
- static int reply_irq[] = { 10, 11, 14, 15 };
-
- bios = 0; /* no bios */
-
- if (pos2 & 0x2)
- port = ports[pos4 & 0x3];
- else
- continue;
-
- /* can't really disable it, same as irq=10 */
- irq = reply_irq[((pos4 >> 2) & 0x1) + 2 * ((pos4 >> 4) & 0x1)];
- } else {
- bios = addresses[pos2 >> 6];
- port = ports[(pos2 >> 4) & 0x03];
- irq = interrupts[(pos2 >> 1) & 0x07];
- }
-
- if (irq) {
- /* claim the slot */
- mca_set_adapter_name(slot - 1, fd_mcs_adapters[loop].name);
-
- /* check irq/region */
- if (request_irq(irq, fd_mcs_intr, IRQF_SHARED, "fd_mcs", hosts)) {
- printk(KERN_ERR "fd_mcs: interrupt is not available, skipping...\n");
- continue;
- }
-
- /* request I/O region */
- if (request_region(port, 0x10, "fd_mcs")) {
- printk(KERN_ERR "fd_mcs: I/O region is already in use, skipping...\n");
- continue;
- }
- /* register */
- if (!(shpnt = scsi_register(tpnt, sizeof(struct fd_hostdata)))) {
- printk(KERN_ERR "fd_mcs: scsi_register() failed\n");
- release_region(port, 0x10);
- free_irq(irq, hosts);
- continue;
- }
-
-
- /* save name */
- strcpy(adapter_name, fd_mcs_adapters[loop].name);
-
- /* chip/fifo */
- chip = fd_mcs_adapters[loop].fd_chip;
- /* use boot time value if available */
- FIFO_COUNT = user_fifo_count ? user_fifo_count : fd_mcs_adapters[loop].fifo_count;
- FIFO_Size = user_fifo_size ? user_fifo_size : fd_mcs_adapters[loop].fifo_size;
-
-/* FIXME: Do we need to keep this bit of code inside NOT_USED around at all? */
-#ifdef NOT_USED
- /* *************************************************** */
- /* Try to toggle 32-bit mode. This only
- works on an 18c30 chip. (User reports
- say this works, so we should switch to
- it in the near future.) */
- outb(0x80, port + IO_Control);
- if ((inb(port + Configuration2) & 0x80) == 0x80) {
- outb(0x00, port + IO_Control);
- if ((inb(port + Configuration2) & 0x80) == 0x00) {
- chip = tmc18c30;
- FIFO_Size = 0x800; /* 2k FIFO */
-
- printk("FIRST: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
- }
- }
-
- /* That should have worked, but appears to
- have problems. Let's assume it is an
- 18c30 if the RAM is disabled. */
-
- if (inb(port + Configuration2) & 0x02) {
- chip = tmc18c30;
- FIFO_Size = 0x800; /* 2k FIFO */
-
- printk("SECOND: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
- }
- /* *************************************************** */
-#endif
-
- /* IBM/ANSI scsi scan ordering */
- /* Stick this back in when the scsi.c changes are there */
- shpnt->reverse_ordering = 1;
-
-
- /* saving info */
- hosts[found++] = shpnt;
-
- shpnt->this_id = id;
- shpnt->irq = irq;
- shpnt->io_port = port;
- shpnt->n_io_port = 0x10;
-
- /* save */
- bios_base = bios;
- adapter_mask = (1 << id);
-
- /* save more */
- SCSI_Mode_Cntl_port = port + SCSI_Mode_Cntl;
- FIFO_Data_Count_port = port + FIFO_Data_Count;
- Interrupt_Cntl_port = port + Interrupt_Cntl;
- Interrupt_Status_port = port + Interrupt_Status;
- Interrupt_Cond_port = port + Interrupt_Cond;
- Read_FIFO_port = port + Read_FIFO;
- Read_SCSI_Data_port = port + Read_SCSI_Data;
- SCSI_Cntl_port = port + SCSI_Cntl;
- SCSI_Data_NoACK_port = port + SCSI_Data_NoACK;
- SCSI_Status_port = port + SCSI_Status;
- TMC_Cntl_port = port + TMC_Cntl;
- TMC_Status_port = port + TMC_Status;
- Write_FIFO_port = port + Write_FIFO;
- Write_SCSI_Data_port = port + Write_SCSI_Data;
-
- Bytes_Read = 0;
- Bytes_Written = 0;
- INTR_Processed = 0;
-
- /* say something */
- print_banner(shpnt);
-
- /* reset */
- outb(1, SCSI_Cntl_port);
- do_pause(2);
- outb(0, SCSI_Cntl_port);
- do_pause(115);
- outb(0, SCSI_Mode_Cntl_port);
- outb(PARITY_MASK, TMC_Cntl_port);
- /* done reset */
- }
- }
-
- if (found == FD_MAX_HOSTS) {
- printk("fd_mcs: detecting reached max=%d host adapters.\n", FD_MAX_HOSTS);
- break;
- }
- }
-
- return found;
-}
-
-static const char *fd_mcs_info(struct Scsi_Host *shpnt)
-{
- return adapter_name;
-}
-
-static int TOTAL_INTR = 0;
-
-/*
- * inout : decides on the direction of the dataflow and the meaning of the
- * variables
- * buffer: If inout==FALSE data is being written to it else read from it
- * *start: If inout==FALSE start of the valid data in the buffer
- * offset: If inout==FALSE offset from the beginning of the imaginary file
- * from which we start writing into the buffer
- * length: If inout==FALSE max number of bytes to be written into the buffer
- * else number of bytes in the buffer
- */
-static int fd_mcs_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout)
-{
- int len = 0;
-
- if (inout)
- return (-ENOSYS);
-
- *start = buffer + offset;
-
- len += sprintf(buffer + len, "Future Domain MCS-600/700 Driver %s\n", DRIVER_VERSION);
- len += sprintf(buffer + len, "HOST #%d: %s\n", shpnt->host_no, adapter_name);
- len += sprintf(buffer + len, "FIFO Size=0x%x, FIFO Count=%d\n", FIFO_Size, FIFO_COUNT);
- len += sprintf(buffer + len, "DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%d\n\n", TOTAL_INTR, INTR_Processed, Bytes_Read, Bytes_Written);
-
- if ((len -= offset) <= 0)
- return 0;
- if (len > length)
- len = length;
- return len;
-}
-
-static int fd_mcs_select(struct Scsi_Host *shpnt, int target)
-{
- int status;
- unsigned long timeout;
-
- outb(0x82, SCSI_Cntl_port); /* Bus Enable + Select */
- outb(adapter_mask | (1 << target), SCSI_Data_NoACK_port);
-
- /* Stop arbitration and enable parity */
- outb(PARITY_MASK, TMC_Cntl_port);
-
- timeout = 350; /* 350mS -- because of timeouts
- (was 250mS) */
-
- do {
- status = inb(SCSI_Status_port); /* Read adapter status */
- if (status & 1) { /* Busy asserted */
- /* Enable SCSI Bus (on error, should make bus idle with 0) */
- outb(0x80, SCSI_Cntl_port);
- return 0;
- }
- udelay(1000); /* wait one msec */
- } while (--timeout);
-
- /* Make bus idle */
- fd_mcs_make_bus_idle(shpnt);
-#if EVERY_ACCESS
- if (!target)
- printk("Selection failed\n");
-#endif
-#if ERRORS_ONLY
- if (!target) {
- static int flag = 0;
-
- if (!flag) /* Skip first failure for all chips. */
- ++flag;
- else
- printk("fd_mcs: Selection failed\n");
- }
-#endif
- return 1;
-}
-
-static void my_done(struct Scsi_Host *shpnt, int error)
-{
- if (in_command) {
- in_command = 0;
- outb(0x00, Interrupt_Cntl_port);
- fd_mcs_make_bus_idle(shpnt);
- current_SC->result = error;
- current_SC->scsi_done(current_SC);
- } else {
- panic("fd_mcs: my_done() called outside of command\n");
- }
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
-}
-
-/* only my_done needs to be protected */
-static irqreturn_t fd_mcs_intr(int irq, void *dev_id)
-{
- unsigned long flags;
- int status;
- int done = 0;
- unsigned data_count, tmp_count;
-
- int i = 0;
- struct Scsi_Host *shpnt;
-
- TOTAL_INTR++;
-
- /* search for one adapter-response on shared interrupt */
- while ((shpnt = hosts[i++])) {
- if ((inb(TMC_Status_port)) & 1)
- break;
- }
-
- /* return if some other device on this IRQ caused the interrupt */
- if (!shpnt) {
- return IRQ_NONE;
- }
-
- INTR_Processed++;
-
- outb(0x00, Interrupt_Cntl_port);
-
- /* Abort calls my_done, so we do nothing here. */
- if (current_SC->SCp.phase & aborted) {
-#if DEBUG_ABORT
- printk("Interrupt after abort, ignoring\n");
-#endif
- /* return IRQ_HANDLED; */
- }
-#if DEBUG_RACE
- ++in_interrupt_flag;
-#endif
-
- if (current_SC->SCp.phase & in_arbitration) {
- status = inb(TMC_Status_port); /* Read adapter status */
- if (!(status & 0x02)) {
-#if EVERY_ACCESS
- printk(" AFAIL ");
-#endif
- spin_lock_irqsave(shpnt->host_lock, flags);
- my_done(shpnt, DID_BUS_BUSY << 16);
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- }
- current_SC->SCp.phase = in_selection;
-
- outb(0x40 | FIFO_COUNT, Interrupt_Cntl_port);
-
- outb(0x82, SCSI_Cntl_port); /* Bus Enable + Select */
- outb(adapter_mask | (1 << scmd_id(current_SC)), SCSI_Data_NoACK_port);
-
- /* Stop arbitration and enable parity */
- outb(0x10 | PARITY_MASK, TMC_Cntl_port);
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
- } else if (current_SC->SCp.phase & in_selection) {
- status = inb(SCSI_Status_port);
- if (!(status & 0x01)) {
- /* Try again, for slow devices */
- if (fd_mcs_select(shpnt, scmd_id(current_SC))) {
-#if EVERY_ACCESS
- printk(" SFAIL ");
-#endif
- spin_lock_irqsave(shpnt->host_lock, flags);
- my_done(shpnt, DID_NO_CONNECT << 16);
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- } else {
-#if EVERY_ACCESS
- printk(" AltSel ");
-#endif
- /* Stop arbitration and enable parity */
- outb(0x10 | PARITY_MASK, TMC_Cntl_port);
- }
- }
- current_SC->SCp.phase = in_other;
- outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
- outb(0x80, SCSI_Cntl_port);
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
- }
-
- /* current_SC->SCp.phase == in_other: this is the body of the routine */
-
- status = inb(SCSI_Status_port);
-
- if (status & 0x10) { /* REQ */
-
- switch (status & 0x0e) {
-
- case 0x08: /* COMMAND OUT */
- outb(current_SC->cmnd[current_SC->SCp.sent_command++], Write_SCSI_Data_port);
-#if EVERY_ACCESS
- printk("CMD = %x,", current_SC->cmnd[current_SC->SCp.sent_command - 1]);
-#endif
- break;
- case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = -1;
- outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
- }
- break;
- case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = 1;
- outb(0x90 | PARITY_MASK, TMC_Cntl_port);
- }
- break;
- case 0x0c: /* STATUS IN */
- current_SC->SCp.Status = inb(Read_SCSI_Data_port);
-#if EVERY_ACCESS
- printk("Status = %x, ", current_SC->SCp.Status);
-#endif
-#if ERRORS_ONLY
- if (current_SC->SCp.Status && current_SC->SCp.Status != 2 && current_SC->SCp.Status != 8) {
- printk("ERROR fd_mcs: target = %d, command = %x, status = %x\n", current_SC->device->id, current_SC->cmnd[0], current_SC->SCp.Status);
- }
-#endif
- break;
- case 0x0a: /* MESSAGE OUT */
- outb(MESSAGE_REJECT, Write_SCSI_Data_port); /* Reject */
- break;
- case 0x0e: /* MESSAGE IN */
- current_SC->SCp.Message = inb(Read_SCSI_Data_port);
-#if EVERY_ACCESS
- printk("Message = %x, ", current_SC->SCp.Message);
-#endif
- if (!current_SC->SCp.Message)
- ++done;
-#if DEBUG_MESSAGES || EVERY_ACCESS
- if (current_SC->SCp.Message) {
- printk("fd_mcs: message = %x\n", current_SC->SCp.Message);
- }
-#endif
- break;
- }
- }
-
- if (chip == tmc1800 && !current_SC->SCp.have_data_in && (current_SC->SCp.sent_command >= current_SC->cmd_len)) {
- /* We have to get the FIFO direction
- correct, so I've made a table based
- on the SCSI Standard of which commands
- appear to require a DATA OUT phase.
- */
- /*
- p. 94: Command for all device types
- CHANGE DEFINITION 40 DATA OUT
- COMPARE 39 DATA OUT
- COPY 18 DATA OUT
- COPY AND VERIFY 3a DATA OUT
- INQUIRY 12
- LOG SELECT 4c DATA OUT
- LOG SENSE 4d
- MODE SELECT (6) 15 DATA OUT
- MODE SELECT (10) 55 DATA OUT
- MODE SENSE (6) 1a
- MODE SENSE (10) 5a
- READ BUFFER 3c
- RECEIVE DIAGNOSTIC RESULTS 1c
- REQUEST SENSE 03
- SEND DIAGNOSTIC 1d DATA OUT
- TEST UNIT READY 00
- WRITE BUFFER 3b DATA OUT
-
- p.178: Commands for direct-access devices (not listed on p. 94)
- FORMAT UNIT 04 DATA OUT
- LOCK-UNLOCK CACHE 36
- PRE-FETCH 34
- PREVENT-ALLOW MEDIUM REMOVAL 1e
- READ (6)/RECEIVE 08
- READ (10) 3c
- READ CAPACITY 25
- READ DEFECT DATA (10) 37
- READ LONG 3e
- REASSIGN BLOCKS 07 DATA OUT
- RELEASE 17
- RESERVE 16 DATA OUT
- REZERO UNIT/REWIND 01
- SEARCH DATA EQUAL (10) 31 DATA OUT
- SEARCH DATA HIGH (10) 30 DATA OUT
- SEARCH DATA LOW (10) 32 DATA OUT
- SEEK (6) 0b
- SEEK (10) 2b
- SET LIMITS (10) 33
- START STOP UNIT 1b
- SYNCHRONIZE CACHE 35
- VERIFY (10) 2f
- WRITE (6)/PRINT/SEND 0a DATA OUT
- WRITE (10)/SEND 2a DATA OUT
- WRITE AND VERIFY (10) 2e DATA OUT
- WRITE LONG 3f DATA OUT
- WRITE SAME 41 DATA OUT ?
-
- p. 261: Commands for sequential-access devices (not previously listed)
- ERASE 19
- LOAD UNLOAD 1b
- LOCATE 2b
- READ BLOCK LIMITS 05
- READ POSITION 34
- READ REVERSE 0f
- RECOVER BUFFERED DATA 14
- SPACE 11
- WRITE FILEMARKS 10 ?
-
- p. 298: Commands for printer devices (not previously listed)
- ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
- SLEW AND PRINT 0b DATA OUT -- same as seek
- STOP PRINT 1b
- SYNCHRONIZE BUFFER 10
-
- p. 315: Commands for processor devices (not previously listed)
-
- p. 321: Commands for write-once devices (not previously listed)
- MEDIUM SCAN 38
- READ (12) a8
- SEARCH DATA EQUAL (12) b1 DATA OUT
- SEARCH DATA HIGH (12) b0 DATA OUT
- SEARCH DATA LOW (12) b2 DATA OUT
- SET LIMITS (12) b3
- VERIFY (12) af
- WRITE (12) aa DATA OUT
- WRITE AND VERIFY (12) ae DATA OUT
-
- p. 332: Commands for CD-ROM devices (not previously listed)
- PAUSE/RESUME 4b
- PLAY AUDIO (10) 45
- PLAY AUDIO (12) a5
- PLAY AUDIO MSF 47
- PLAY TRACK RELATIVE (10) 49
- PLAY TRACK RELATIVE (12) a9
- READ HEADER 44
- READ SUB-CHANNEL 42
- READ TOC 43
-
- p. 370: Commands for scanner devices (not previously listed)
- GET DATA BUFFER STATUS 34
- GET WINDOW 25
- OBJECT POSITION 31
- SCAN 1b
- SET WINDOW 24 DATA OUT
-
- p. 391: Commands for optical memory devices (not listed)
- ERASE (10) 2c
- ERASE (12) ac
- MEDIUM SCAN 38 DATA OUT
- READ DEFECT DATA (12) b7
- READ GENERATION 29
- READ UPDATED BLOCK 2d
- UPDATE BLOCK 3d DATA OUT
-
- p. 419: Commands for medium changer devices (not listed)
- EXCHANGE MEDIUM 46
- INITIALIZE ELEMENT STATUS 07
- MOVE MEDIUM a5
- POSITION TO ELEMENT 2b
- READ ELEMENT STATUS b8
- REQUEST VOL. ELEMENT ADDRESS b5
- SEND VOLUME TAG b6 DATA OUT
-
- p. 454: Commands for communications devices (not listed previously)
- GET MESSAGE (6) 08
- GET MESSAGE (10) 28
- GET MESSAGE (12) a8
- */
-
- switch (current_SC->cmnd[0]) {
- case CHANGE_DEFINITION:
- case COMPARE:
- case COPY:
- case COPY_VERIFY:
- case LOG_SELECT:
- case MODE_SELECT:
- case MODE_SELECT_10:
- case SEND_DIAGNOSTIC:
- case WRITE_BUFFER:
-
- case FORMAT_UNIT:
- case REASSIGN_BLOCKS:
- case RESERVE:
- case SEARCH_EQUAL:
- case SEARCH_HIGH:
- case SEARCH_LOW:
- case WRITE_6:
- case WRITE_10:
- case WRITE_VERIFY:
- case 0x3f:
- case 0x41:
-
- case 0xb1:
- case 0xb0:
- case 0xb2:
- case 0xaa:
- case 0xae:
-
- case 0x24:
-
- case 0x38:
- case 0x3d:
-
- case 0xb6:
-
- case 0xea: /* alternate number for WRITE LONG */
-
- current_SC->SCp.have_data_in = -1;
- outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
- break;
-
- case 0x00:
- default:
-
- current_SC->SCp.have_data_in = 1;
- outb(0x90 | PARITY_MASK, TMC_Cntl_port);
- break;
- }
- }
-
- if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
- while ((data_count = FIFO_Size - inw(FIFO_Data_Count_port)) > 512) {
-#if EVERY_ACCESS
- printk("DC=%d, ", data_count);
-#endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count > 0) {
-#if EVERY_ACCESS
- printk("%d OUT, ", data_count);
-#endif
- if (data_count == 1) {
- Bytes_Written++;
-
- outb(*current_SC->SCp.ptr++, Write_FIFO_port);
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1;
- tmp_count = data_count << 1;
- outsw(Write_FIFO_port, current_SC->SCp.ptr, data_count);
- current_SC->SCp.ptr += tmp_count;
- Bytes_Written += tmp_count;
- current_SC->SCp.this_residual -= tmp_count;
- }
- }
- if (!current_SC->SCp.this_residual) {
- if (current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- } else
- break;
- }
- }
- } else if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
- while ((data_count = inw(FIFO_Data_Count_port)) > 0) {
-#if EVERY_ACCESS
- printk("DC=%d, ", data_count);
-#endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count) {
-#if EVERY_ACCESS
- printk("%d IN, ", data_count);
-#endif
- if (data_count == 1) {
- Bytes_Read++;
- *current_SC->SCp.ptr++ = inb(Read_FIFO_port);
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1; /* Number of words */
- tmp_count = data_count << 1;
- insw(Read_FIFO_port, current_SC->SCp.ptr, data_count);
- current_SC->SCp.ptr += tmp_count;
- Bytes_Read += tmp_count;
- current_SC->SCp.this_residual -= tmp_count;
- }
- }
- if (!current_SC->SCp.this_residual && current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- }
- }
- }
-
- if (done) {
-#if EVERY_ACCESS
- printk(" ** IN DONE %d ** ", current_SC->SCp.have_data_in);
-#endif
-
-#if EVERY_ACCESS
- printk("BEFORE MY_DONE. . .");
-#endif
- spin_lock_irqsave(shpnt->host_lock, flags);
- my_done(shpnt, (current_SC->SCp.Status & 0xff)
- | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16));
- spin_unlock_irqrestore(shpnt->host_lock, flags);
-#if EVERY_ACCESS
- printk("RETURNING.\n");
-#endif
-
- } else {
- if (current_SC->SCp.phase & disconnect) {
- outb(0xd0 | FIFO_COUNT, Interrupt_Cntl_port);
- outb(0x00, SCSI_Cntl_port);
- } else {
- outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
- }
- }
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
-}
-
-static int fd_mcs_release(struct Scsi_Host *shpnt)
-{
- int i, this_host, irq_usage;
-
- release_region(shpnt->io_port, shpnt->n_io_port);
-
- this_host = -1;
- irq_usage = 0;
- for (i = 0; i < found; i++) {
- if (shpnt == hosts[i])
- this_host = i;
- if (shpnt->irq == hosts[i]->irq)
- irq_usage++;
- }
-
- /* only for the last one */
- if (1 == irq_usage)
- free_irq(shpnt->irq, hosts);
-
- found--;
-
- for (i = this_host; i < found; i++)
- hosts[i] = hosts[i + 1];
-
- hosts[found] = NULL;
-
- return 0;
-}
-
-static int fd_mcs_queue_lck(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
-{
- struct Scsi_Host *shpnt = SCpnt->device->host;
-
- if (in_command) {
- panic("fd_mcs: fd_mcs_queue() NOT REENTRANT!\n");
- }
-#if EVERY_ACCESS
- printk("queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
- SCpnt->target, *(unsigned char *) SCpnt->cmnd,
- scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
-#endif
-
- fd_mcs_make_bus_idle(shpnt);
-
- SCpnt->scsi_done = done; /* Save this for the done function */
- current_SC = SCpnt;
-
- /* Initialize static data */
-
- if (scsi_bufflen(current_SC)) {
- current_SC->SCp.buffer = scsi_sglist(current_SC);
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- current_SC->SCp.buffers_residual = scsi_sg_count(current_SC) - 1;
- } else {
- current_SC->SCp.ptr = NULL;
- current_SC->SCp.this_residual = 0;
- current_SC->SCp.buffer = NULL;
- current_SC->SCp.buffers_residual = 0;
- }
-
-
- current_SC->SCp.Status = 0;
- current_SC->SCp.Message = 0;
- current_SC->SCp.have_data_in = 0;
- current_SC->SCp.sent_command = 0;
- current_SC->SCp.phase = in_arbitration;
-
- /* Start arbitration */
- outb(0x00, Interrupt_Cntl_port);
- outb(0x00, SCSI_Cntl_port); /* Disable data drivers */
- outb(adapter_mask, SCSI_Data_NoACK_port); /* Set our id bit */
- in_command = 1;
- outb(0x20, Interrupt_Cntl_port);
- outb(0x14 | PARITY_MASK, TMC_Cntl_port); /* Start arbitration */
-
- return 0;
-}
-
-static DEF_SCSI_QCMD(fd_mcs_queue)
-
-#if DEBUG_ABORT || DEBUG_RESET
-static void fd_mcs_print_info(Scsi_Cmnd * SCpnt)
-{
- unsigned int imr;
- unsigned int irr;
- unsigned int isr;
- struct Scsi_Host *shpnt = SCpnt->host;
-
- if (!SCpnt || !SCpnt->host) {
- printk("fd_mcs: cannot provide detailed information\n");
- }
-
- printk("%s\n", fd_mcs_info(SCpnt->host));
- print_banner(SCpnt->host);
- switch (SCpnt->SCp.phase) {
- case in_arbitration:
- printk("arbitration ");
- break;
- case in_selection:
- printk("selection ");
- break;
- case in_other:
- printk("other ");
- break;
- default:
- printk("unknown ");
- break;
- }
-
- printk("(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
- SCpnt->SCp.phase, SCpnt->device->id, *(unsigned char *) SCpnt->cmnd,
- scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
- printk("sent_command = %d, have_data_in = %d, timeout = %d\n", SCpnt->SCp.sent_command, SCpnt->SCp.have_data_in, SCpnt->timeout);
-#if DEBUG_RACE
- printk("in_interrupt_flag = %d\n", in_interrupt_flag);
-#endif
-
- imr = (inb(0x0a1) << 8) + inb(0x21);
- outb(0x0a, 0xa0);
- irr = inb(0xa0) << 8;
- outb(0x0a, 0x20);
- irr += inb(0x20);
- outb(0x0b, 0xa0);
- isr = inb(0xa0) << 8;
- outb(0x0b, 0x20);
- isr += inb(0x20);
-
- /* Print out interesting information */
- printk("IMR = 0x%04x", imr);
- if (imr & (1 << shpnt->irq))
- printk(" (masked)");
- printk(", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr);
-
- printk("SCSI Status = 0x%02x\n", inb(SCSI_Status_port));
- printk("TMC Status = 0x%02x", inb(TMC_Status_port));
- if (inb(TMC_Status_port) & 1)
- printk(" (interrupt)");
- printk("\n");
- printk("Interrupt Status = 0x%02x", inb(Interrupt_Status_port));
- if (inb(Interrupt_Status_port) & 0x08)
- printk(" (enabled)");
- printk("\n");
- if (chip == tmc18c50 || chip == tmc18c30) {
- printk("FIFO Status = 0x%02x\n", inb(shpnt->io_port + FIFO_Status));
- printk("Int. Condition = 0x%02x\n", inb(shpnt->io_port + Interrupt_Cond));
- }
- printk("Configuration 1 = 0x%02x\n", inb(shpnt->io_port + Configuration1));
- if (chip == tmc18c50 || chip == tmc18c30)
- printk("Configuration 2 = 0x%02x\n", inb(shpnt->io_port + Configuration2));
-}
-#endif
-
-static int fd_mcs_abort(Scsi_Cmnd * SCpnt)
-{
- struct Scsi_Host *shpnt = SCpnt->device->host;
-
- unsigned long flags;
-#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
- printk("fd_mcs: abort ");
-#endif
-
- spin_lock_irqsave(shpnt->host_lock, flags);
- if (!in_command) {
-#if EVERY_ACCESS || ERRORS_ONLY
- printk(" (not in command)\n");
-#endif
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return FAILED;
- } else
- printk("\n");
-
-#if DEBUG_ABORT
- fd_mcs_print_info(SCpnt);
-#endif
-
- fd_mcs_make_bus_idle(shpnt);
-
- current_SC->SCp.phase |= aborted;
-
- current_SC->result = DID_ABORT << 16;
-
- /* Aborts are not done well. . . */
- my_done(shpnt, DID_ABORT << 16);
-
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return SUCCESS;
-}
-
-static int fd_mcs_bus_reset(Scsi_Cmnd * SCpnt) {
- struct Scsi_Host *shpnt = SCpnt->device->host;
- unsigned long flags;
-
-#if DEBUG_RESET
- static int called_once = 0;
-#endif
-
-#if ERRORS_ONLY
- if (SCpnt)
- printk("fd_mcs: SCSI Bus Reset\n");
-#endif
-
-#if DEBUG_RESET
- if (called_once)
- fd_mcs_print_info(current_SC);
- called_once = 1;
-#endif
-
- spin_lock_irqsave(shpnt->host_lock, flags);
-
- outb(1, SCSI_Cntl_port);
- do_pause(2);
- outb(0, SCSI_Cntl_port);
- do_pause(115);
- outb(0, SCSI_Mode_Cntl_port);
- outb(PARITY_MASK, TMC_Cntl_port);
-
- spin_unlock_irqrestore(shpnt->host_lock, flags);
-
- /* Unless this is the very first call (i.e., SCPnt == NULL), everything
- is probably hosed at this point. We will, however, try to keep
- things going by informing the high-level code that we need help. */
- return SUCCESS;
-}
-
-#include <scsi/scsi_ioctl.h>
-
-static int fd_mcs_biosparam(struct scsi_device * disk, struct block_device *bdev,
- sector_t capacity, int *info_array)
-{
- unsigned char *p = scsi_bios_ptable(bdev);
- int size = capacity;
-
- /* BIOS >= 3.4 for MCA cards */
- /* This algorithm was provided by Future Domain (much thanks!). */
-
- if (p && p[65] == 0xaa && p[64] == 0x55 /* Partition table valid */
- && p[4]) { /* Partition type */
- /* The partition table layout is as follows:
-
- Start: 0x1b3h
- Offset: 0 = partition status
- 1 = starting head
- 2 = starting sector and cylinder (word, encoded)
- 4 = partition type
- 5 = ending head
- 6 = ending sector and cylinder (word, encoded)
- 8 = starting absolute sector (double word)
- c = number of sectors (double word)
- Signature: 0x1fe = 0x55aa
-
- So, this algorithm assumes:
- 1) the first partition table is in use,
- 2) the data in the first entry is correct, and
- 3) partitions never divide cylinders
-
- Note that (1) may be FALSE for NetBSD (and other BSD flavors),
- as well as for Linux. Note also, that Linux doesn't pay any
- attention to the fields that are used by this algorithm -- it
- only uses the absolute sector data. Recent versions of Linux's
- fdisk(1) will fill this data in correctly, and forthcoming
- versions will check for consistency.
-
- Checking for a non-zero partition type is not part of the
- Future Domain algorithm, but it seemed to be a reasonable thing
- to do, especially in the Linux and BSD worlds. */
-
- info_array[0] = p[5] + 1; /* heads */
- info_array[1] = p[6] & 0x3f; /* sectors */
- } else {
- /* Note that this new method guarantees that there will always be
- less than 1024 cylinders on a platter. This is good for drives
- up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
- if ((unsigned int) size >= 0x7e0000U)
- {
- info_array[0] = 0xff; /* heads = 255 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else if ((unsigned int) size >= 0x200000U) {
- info_array[0] = 0x80; /* heads = 128 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else {
- info_array[0] = 0x40; /* heads = 64 */
- info_array[1] = 0x20; /* sectors = 32 */
- }
- }
- /* For both methods, compute the cylinders */
- info_array[2] = (unsigned int) size / (info_array[0] * info_array[1]);
- kfree(p);
- return 0;
-}
-
-static struct scsi_host_template driver_template = {
- .proc_name = "fd_mcs",
- .proc_info = fd_mcs_proc_info,
- .detect = fd_mcs_detect,
- .release = fd_mcs_release,
- .info = fd_mcs_info,
- .queuecommand = fd_mcs_queue,
- .eh_abort_handler = fd_mcs_abort,
- .eh_bus_reset_handler = fd_mcs_bus_reset,
- .bios_param = fd_mcs_biosparam,
- .can_queue = 1,
- .this_id = 7,
- .sg_tablesize = 64,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
-};
-#include "scsi_module.c"
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 643f6d500fe7..c0b2a980db34 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -1,1785 +1,607 @@
-/* fdomain.c -- Future Domain TMC-16x0 SCSI driver
- * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Mon Dec 28 21:59:02 1998 by faith@acm.org
- * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
- * Shared IRQ supported added 7/7/2001 Alan Cox <alan@lxorguk.ukuu.org.uk>
-
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
-
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
-
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
-
- **************************************************************************
-
- SUMMARY:
-
- Future Domain BIOS versions supported for autodetect:
- 2.0, 3.0, 3.2, 3.4 (1.0), 3.5 (2.0), 3.6, 3.61
- Chips are supported:
- TMC-1800, TMC-18C50, TMC-18C30, TMC-36C70
- Boards supported:
- Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX
- Future Domain TMC-3260 (PCI)
- Quantum ISA-200S, ISA-250MG
- Adaptec AHA-2920A (PCI) [BUT *NOT* AHA-2920C -- use aic7xxx instead]
- IBM ?
- LILO/INSMOD command-line options:
- fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]
-
-
-
- NOTE:
-
- The Adaptec AHA-2920C has an Adaptec AIC-7850 chip on it.
- Use the aic7xxx driver for this board.
-
- The Adaptec AHA-2920A has a Future Domain chip on it, so this is the right
- driver for that card. Unfortunately, the boxes will probably just say
- "2920", so you'll have to look on the card for a Future Domain logo, or a
- letter after the 2920.
-
-
-
- THANKS:
-
- Thanks to Adaptec for providing PCI boards for testing. This finally
- enabled me to test the PCI detection and correct it for PCI boards that do
- not have a BIOS at a standard ISA location. For PCI boards, LILO/INSMOD
- command-line options should no longer be needed. --RF 18Nov98
-
-
-
- DESCRIPTION:
-
- This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
- TMC-1650/1670, and TMC-3260 SCSI host adapters. The 1650 and 1670 have a
- 25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
- high-density external connector. The 1670 and 1680 have floppy disk
- controllers built in. The TMC-3260 is a PCI bus card.
-
- Future Domain's older boards are based on the TMC-1800 chip, and this
- driver was originally written for a TMC-1680 board with the TMC-1800 chip.
- More recently, boards are being produced with the TMC-18C50 and TMC-18C30
- chips. The latest and greatest board may not work with this driver. If
- you have to patch this driver so that it will recognize your board's BIOS
- signature, then the driver may fail to function after the board is
- detected.
-
- Please note that the drive ordering that Future Domain implemented in BIOS
- versions 3.4 and 3.5 is the opposite of the order (currently) used by the
- rest of the SCSI industry. If you have BIOS version 3.4 or 3.5, and have
- more than one drive, then the drive ordering will be the reverse of that
- which you see under DOS. For example, under DOS SCSI ID 0 will be D: and
- SCSI ID 1 will be C: (the boot device). Under Linux, SCSI ID 0 will be
- /dev/sda and SCSI ID 1 will be /dev/sdb. The Linux ordering is consistent
- with that provided by all the other SCSI drivers for Linux. If you want
- this changed, you will probably have to patch the higher level SCSI code.
- If you do so, please send me patches that are protected by #ifdefs.
-
- If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
- your board. Please refer to the Seagate driver for more information and
- possible support.
-
-
-
- HISTORY:
-
- Linux Driver Driver
- Version Version Date Support/Notes
-
- 0.0 3 May 1992 V2.0 BIOS; 1800 chip
- 0.97 1.9 28 Jul 1992
- 0.98.6 3.1 27 Nov 1992
- 0.99 3.2 9 Dec 1992
-
- 0.99.3 3.3 10 Jan 1993 V3.0 BIOS
- 0.99.5 3.5 18 Feb 1993
- 0.99.10 3.6 15 May 1993 V3.2 BIOS; 18C50 chip
- 0.99.11 3.17 3 Jul 1993 (now under RCS)
- 0.99.12 3.18 13 Aug 1993
- 0.99.14 5.6 31 Oct 1993 (reselection code removed)
-
- 0.99.15 5.9 23 Jan 1994 V3.4 BIOS (preliminary)
- 1.0.8/1.1.1 5.15 1 Apr 1994 V3.4 BIOS; 18C30 chip (preliminary)
- 1.0.9/1.1.3 5.16 7 Apr 1994 V3.4 BIOS; 18C30 chip
- 1.1.38 5.18 30 Jul 1994 36C70 chip (PCI version of 18C30)
- 1.1.62 5.20 2 Nov 1994 V3.5 BIOS
- 1.1.73 5.22 7 Dec 1994 Quantum ISA-200S board; V2.0 BIOS
-
- 1.1.82 5.26 14 Jan 1995 V3.5 BIOS; TMC-1610M/MER/MEX board
- 1.2.10 5.28 5 Jun 1995 Quantum ISA-250MG board; V2.0, V2.01 BIOS
- 1.3.4 5.31 23 Jun 1995 PCI BIOS-32 detection (preliminary)
- 1.3.7 5.33 4 Jul 1995 PCI BIOS-32 detection
- 1.3.28 5.36 17 Sep 1995 V3.61 BIOS; LILO command-line support
- 1.3.34 5.39 12 Oct 1995 V3.60 BIOS; /proc
- 1.3.72 5.39 8 Feb 1996 Adaptec AHA-2920 board
- 1.3.85 5.41 4 Apr 1996
- 2.0.12 5.44 8 Aug 1996 Use ID 7 for all PCI cards
- 2.1.1 5.45 2 Oct 1996 Update ROM accesses for 2.1.x
- 2.1.97 5.46 23 Apr 1998 Rewritten PCI detection routines [mj]
- 2.1.11x 5.47 9 Aug 1998 Touched for 8 SCSI disk majors support
- 5.48 18 Nov 1998 BIOS no longer needed for PCI detection
- 2.2.0 5.50 28 Dec 1998 Support insmod parameters
-
-
- REFERENCES USED:
-
- "TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation,
- 1990.
-
- "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain
- Corporation, January 1992.
-
- "LXT SCSI Products: Specifications and OEM Technical Manual (Revision
- B/September 1991)", Maxtor Corporation, 1991.
-
- "7213S product Manual (Revision P3)", Maxtor Corporation, 1992.
-
- "Draft Proposed American National Standard: Small Computer System
- Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109,
- revision 10h, October 17, 1991)
-
- Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric
- Youngdale (ericy@cais.com), 1992.
-
- Private communication, Tuong Le (Future Domain Engineering department),
- 1994. (Disk geometry computations for Future Domain BIOS version 3.4, and
- TMC-18C30 detection.)
-
- Hogan, Thom. The Programmer's PC Sourcebook. Microsoft Press, 1988. Page
- 60 (2.39: Disk Partition Table Layout).
-
- "18C30 Technical Reference Manual", Future Domain Corporation, 1993, page
- 6-1.
-
-
-
- NOTES ON REFERENCES:
-
- The Maxtor manuals were free. Maxtor telephone technical support is
- great!
-
- The Future Domain manuals were $25 and $35. They document the chip, not
- the TMC-16x0 boards, so some information I had to guess at. In 1992,
- Future Domain sold DOS BIOS source for $250 and the UN*X driver source was
- $750, but these required a non-disclosure agreement, so even if I could
- have afforded them, they would *not* have been useful for writing this
- publicly distributable driver. Future Domain technical support has
- provided some information on the phone and have sent a few useful FAXs.
- They have been much more helpful since they started to recognize that the
- word "Linux" refers to an operating system :-).
-
-
-
- ALPHA TESTERS:
-
- There are many other alpha testers that come and go as the driver
- develops. The people listed here were most helpful in times of greatest
- need (mostly early on -- I've probably left out a few worthy people in
- more recent times):
-
- Todd Carrico (todd@wutc.wustl.edu), Dan Poirier (poirier@cs.unc.edu ), Ken
- Corey (kenc@sol.acs.unt.edu), C. de Bruin (bruin@bruin@sterbbs.nl), Sakari
- Aaltonen (sakaria@vipunen.hit.fi), John Rice (rice@xanth.cs.odu.edu), Brad
- Yearwood (brad@optilink.com), and Ray Toy (toy@soho.crd.ge.com).
-
- Special thanks to Tien-Wan Yang (twyang@cs.uh.edu), who graciously lent me
- his 18C50-based card for debugging. He is the sole reason that this
- driver works with the 18C50 chip.
-
- Thanks to Dave Newman (dnewman@crl.com) for providing initial patches for
- the version 3.4 BIOS.
-
- Thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for providing
- patches that support the TMC-3260, a PCI bus card with the 36C70 chip.
- The 36C70 chip appears to be "completely compatible" with the 18C30 chip.
-
- Thanks to Eric Kasten (tigger@petroglyph.cl.msu.edu) for providing the
- patch for the version 3.5 BIOS.
-
- Thanks for Stephen Henson (shenson@nyx10.cs.du.edu) for providing the
- patch for the Quantum ISA-200S SCSI adapter.
-
- Thanks to Adam Bowen for the signature to the 1610M/MER/MEX scsi cards, to
- Martin Andrews (andrewm@ccfadm.eeg.ccf.org) for the signature to some
- random TMC-1680 repackaged by IBM; and to Mintak Ng (mintak@panix.com) for
- the version 3.61 BIOS signature.
-
- Thanks for Mark Singer (elf@netcom.com) and Richard Simpson
- (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
- work on the Quantum RAM layout.
-
- Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for
- providing patches for proper PCI BIOS32-mediated detection of the TMC-3260
- card (a PCI bus card with the 36C70 chip). Please send James PCI-related
- bug reports.
-
- Thanks to Tom Cavin (tec@usa1.com) for preliminary command-line option
- patches.
-
- New PCI detection code written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
-
- Insmod parameter code based on patches from Daniel Graham
- <graham@balance.uoregon.edu>.
-
- All of the alpha testers deserve much thanks.
-
-
-
- NOTES ON USER DEFINABLE OPTIONS:
-
- DEBUG: This turns on the printing of various debug information.
-
- ENABLE_PARITY: This turns on SCSI parity checking. With the current
- driver, all attached devices must support SCSI parity. If none of your
- devices support parity, then you can probably get the driver to work by
- turning this option off. I have no way of testing this, however, and it
- would appear that no one ever uses this option.
-
- FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
- 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
- the SCSI device, an interrupt will be raised. Therefore, this could be as
- low as 0, or as high as 16. Note, however, that values which are too high
- or too low seem to prevent any interrupts from occurring, and thereby lock
- up the machine. I have found that 2 is a good number, but throughput may
- be increased by changing this value to values which are close to 2.
- Please let me know if you try any different values.
-
- RESELECTION: This is no longer an option, since I gave up trying to
- implement it in version 4.x of this driver. It did not improve
- performance at all and made the driver unstable (because I never found one
- of the two race conditions which were introduced by the multiple
- outstanding command code). The instability seems a very high price to pay
- just so that you don't have to wait for the tape to rewind. If you want
- this feature implemented, send me patches. I'll be happy to send a copy
- of my (broken) driver to anyone who would like to see a copy.
-
- **************************************************************************/
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Future Domain TMC-16x0 and TMC-3260 SCSI host adapters
+ * Copyright 2019 Ondrej Zary
+ *
+ * Original driver by
+ * Rickard E. Faith, faith@cs.unc.edu
+ *
+ * Future Domain BIOS versions supported for autodetect:
+ * 2.0, 3.0, 3.2, 3.4 (1.0), 3.5 (2.0), 3.6, 3.61
+ * Chips supported:
+ * TMC-1800, TMC-18C50, TMC-18C30, TMC-36C70
+ * Boards supported:
+ * Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX
+ * Future Domain TMC-3260 (PCI)
+ * Quantum ISA-200S, ISA-250MG
+ * Adaptec AHA-2920A (PCI) [BUT *NOT* AHA-2920C -- use aic7xxx instead]
+ * IBM ?
+ *
+ * NOTE:
+ *
+ * The Adaptec AHA-2920C has an Adaptec AIC-7850 chip on it.
+ * Use the aic7xxx driver for this board.
+ *
+ * The Adaptec AHA-2920A has a Future Domain chip on it, so this is the right
+ * driver for that card. Unfortunately, the boxes will probably just say
+ * "2920", so you'll have to look on the card for a Future Domain logo, or a
+ * letter after the 2920.
+ *
+ * If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
+ * your board.
+ *
+ * DESCRIPTION:
+ *
+ * This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
+ * TMC-1650/1670, and TMC-3260 SCSI host adapters. The 1650 and 1670 have a
+ * 25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
+ * high-density external connector. The 1670 and 1680 have floppy disk
+ * controllers built in. The TMC-3260 is a PCI bus card.
+ *
+ * Future Domain's older boards are based on the TMC-1800 chip, and this
+ * driver was originally written for a TMC-1680 board with the TMC-1800 chip.
+ * More recently, boards are being produced with the TMC-18C50 and TMC-18C30
+ * chips.
+ *
+ * Please note that the drive ordering that Future Domain implemented in BIOS
+ * versions 3.4 and 3.5 is the opposite of the order (currently) used by the
+ * rest of the SCSI industry.
+ *
+ *
+ * REFERENCES USED:
+ *
+ * "TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation,
+ * 1990.
+ *
+ * "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain
+ * Corporation, January 1992.
+ *
+ * "LXT SCSI Products: Specifications and OEM Technical Manual (Revision
+ * B/September 1991)", Maxtor Corporation, 1991.
+ *
+ * "7213S product Manual (Revision P3)", Maxtor Corporation, 1992.
+ *
+ * "Draft Proposed American National Standard: Small Computer System
+ * Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109,
+ * revision 10h, October 17, 1991)
+ *
+ * Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric
+ * Youngdale (ericy@cais.com), 1992.
+ *
+ * Private communication, Tuong Le (Future Domain Engineering department),
+ * 1994. (Disk geometry computations for Future Domain BIOS version 3.4, and
+ * TMC-18C30 detection.)
+ *
+ * Hogan, Thom. The Programmer's PC Sourcebook. Microsoft Press, 1988. Page
+ * 60 (2.39: Disk Partition Table Layout).
+ *
+ * "18C30 Technical Reference Manual", Future Domain Corporation, 1993, page
+ * 6-1.
+ */
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/proc_fs.h>
-#include <linux/pci.h>
-#include <linux/stat.h>
#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/workqueue.h>
#include <scsi/scsicam.h>
-
-#include <asm/system.h>
-
-#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
-#include <scsi/scsi_ioctl.h>
#include "fdomain.h"
-#ifndef PCMCIA
-MODULE_AUTHOR("Rickard E. Faith");
-MODULE_DESCRIPTION("Future domain SCSI driver");
-MODULE_LICENSE("GPL");
-#endif
-
-
-#define VERSION "$Revision: 5.51 $"
-
-/* START OF USER DEFINABLE OPTIONS */
-
-#define DEBUG 0 /* Enable debugging output */
-#define ENABLE_PARITY 1 /* Enable SCSI Parity */
-#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */
-
-/* END OF USER DEFINABLE OPTIONS */
-
-#if DEBUG
-#define EVERY_ACCESS 0 /* Write a line on every scsi access */
-#define ERRORS_ONLY 1 /* Only write a line if there is an error */
-#define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */
-#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
-#define DEBUG_ABORT 1 /* Debug abort() routine */
-#define DEBUG_RESET 1 /* Debug reset() routine */
-#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
-#else
-#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
-#define ERRORS_ONLY 0
-#define DEBUG_DETECT 0
-#define DEBUG_MESSAGES 0
-#define DEBUG_ABORT 0
-#define DEBUG_RESET 0
-#define DEBUG_RACE 0
-#endif
-
-/* Errors are reported on the line, so we don't need to report them again */
-#if EVERY_ACCESS
-#undef ERRORS_ONLY
-#define ERRORS_ONLY 0
-#endif
-
-#if ENABLE_PARITY
-#define PARITY_MASK 0x08
-#else
-#define PARITY_MASK 0x00
-#endif
+/*
+ * FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
+ * 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
+ * the SCSI device, an interrupt will be raised. Therefore, this could be as
+ * low as 0, or as high as 16. Note, however, that values which are too high
+ * or too low seem to prevent any interrupts from occurring, and thereby lock
+ * up the machine.
+ */
+#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */
+#define PARITY_MASK ACTL_PAREN /* Parity enabled, 0 = disabled */
enum chip_type {
- unknown = 0x00,
- tmc1800 = 0x01,
- tmc18c50 = 0x02,
- tmc18c30 = 0x03,
-};
-
-enum {
- in_arbitration = 0x02,
- in_selection = 0x04,
- in_other = 0x08,
- disconnect = 0x10,
- aborted = 0x20,
- sent_ident = 0x40,
+ unknown = 0x00,
+ tmc1800 = 0x01,
+ tmc18c50 = 0x02,
+ tmc18c30 = 0x03,
};
-enum in_port_type {
- Read_SCSI_Data = 0,
- SCSI_Status = 1,
- TMC_Status = 2,
- FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
- Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
- LSB_ID_Code = 5,
- MSB_ID_Code = 6,
- Read_Loopback = 7,
- SCSI_Data_NoACK = 8,
- Interrupt_Status = 9,
- Configuration1 = 10,
- Configuration2 = 11, /* tmc18c50/tmc18c30 only */
- Read_FIFO = 12,
- FIFO_Data_Count = 14
+struct fdomain {
+ int base;
+ struct scsi_cmnd *cur_cmd;
+ enum chip_type chip;
+ struct work_struct work;
};
-enum out_port_type {
- Write_SCSI_Data = 0,
- SCSI_Cntl = 1,
- Interrupt_Cntl = 2,
- SCSI_Mode_Cntl = 3,
- TMC_Cntl = 4,
- Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
- Write_Loopback = 7,
- IO_Control = 11, /* tmc18c30 only */
- Write_FIFO = 12
-};
-
-/* .bss will zero all the static variables below */
-static int port_base;
-static unsigned long bios_base;
-static void __iomem * bios_mem;
-static int bios_major;
-static int bios_minor;
-static int PCI_bus;
-#ifdef CONFIG_PCI
-static struct pci_dev *PCI_dev;
-#endif
-static int Quantum; /* Quantum board variant */
-static int interrupt_level;
-static volatile int in_command;
-static struct scsi_cmnd *current_SC;
-static enum chip_type chip = unknown;
-static int adapter_mask;
-static int this_id;
-static int setup_called;
-
-#if DEBUG_RACE
-static volatile int in_interrupt_flag;
-#endif
-
-static int FIFO_Size = 0x2000; /* 8k FIFO for
- pre-tmc18c30 chips */
-
-static irqreturn_t do_fdomain_16x0_intr( int irq, void *dev_id );
-/* Allow insmod parameters to be like LILO parameters. For example:
- insmod fdomain fdomain=0x140,11 */
-static char * fdomain = NULL;
-module_param(fdomain, charp, 0);
-
-#ifndef PCMCIA
-
-static unsigned long addresses[] = {
- 0xc8000,
- 0xca000,
- 0xce000,
- 0xde000,
- 0xcc000, /* Extra addresses for PCI boards */
- 0xd0000,
- 0xe0000,
-};
-#define ADDRESS_COUNT ARRAY_SIZE(addresses)
-
-static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
-#define PORT_COUNT ARRAY_SIZE(ports)
+static struct scsi_pointer *fdomain_scsi_pointer(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
-static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
+static inline void fdomain_make_bus_idle(struct fdomain *fd)
+{
+ outb(0, fd->base + REG_BCTL);
+ outb(0, fd->base + REG_MCTL);
+ if (fd->chip == tmc18c50 || fd->chip == tmc18c30)
+ /* Clear forced intr. */
+ outb(ACTL_RESET | ACTL_CLRFIRQ | PARITY_MASK,
+ fd->base + REG_ACTL);
+ else
+ outb(ACTL_RESET | PARITY_MASK, fd->base + REG_ACTL);
+}
-#endif /* !PCMCIA */
+static enum chip_type fdomain_identify(int port)
+{
+ u16 id = inb(port + REG_ID_LSB) | inb(port + REG_ID_MSB) << 8;
+
+ switch (id) {
+ case 0x6127:
+ return tmc1800;
+ case 0x60e9: /* 18c50 or 18c30 */
+ break;
+ default:
+ return unknown;
+ }
-/*
+ /* Try to toggle 32-bit mode. This only works on an 18c30 chip. */
+ outb(CFG2_32BIT, port + REG_CFG2);
+ if ((inb(port + REG_CFG2) & CFG2_32BIT)) {
+ outb(0, port + REG_CFG2);
+ if ((inb(port + REG_CFG2) & CFG2_32BIT) == 0)
+ return tmc18c30;
+ }
+ /* If that failed, we are an 18c50. */
+ return tmc18c50;
+}
- READ THIS BEFORE YOU ADD A SIGNATURE!
-
- READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME!
-
- READ EVERY WORD, ESPECIALLY THE WORD *NOT*
-
- This driver works *ONLY* for Future Domain cards using the TMC-1800,
- TMC-18C50, or TMC-18C30 chip. This includes models TMC-1650, 1660, 1670,
- and 1680. These are all 16-bit cards.
-
- The following BIOS signature signatures are for boards which do *NOT*
- work with this driver (these TMC-8xx and TMC-9xx boards may work with the
- Seagate driver):
-
- FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
- FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
- FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
- FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
- FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
- FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
- FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
-
- (The cards which do *NOT* work are all 8-bit cards -- although some of
- them have a 16-bit form-factor, the upper 8-bits are used only for IRQs
- and are *NOT* used for data. You can tell the difference by following
- the tracings on the circuit board -- if only the IRQ lines are involved,
- you have a "8-bit" card, and should *NOT* use this driver.)
-
-*/
-
-#ifndef PCMCIA
-
-static struct signature {
- const char *signature;
- int sig_offset;
- int sig_length;
- int major_bios_version;
- int minor_bios_version;
- int flag; /* 1 == PCI_bus, 2 == ISA_200S, 3 == ISA_250MG, 4 == ISA_200S */
-} signatures[] = {
- /* 1 2 3 4 5 6 */
- /* 123456789012345678901234567890123456789012345678901234567890 */
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0, 0 },
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50, 2, 0, 0 },
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50, 2, 0, 2 },
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 2, 0, 3 },
- { "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 2, 0, 4 },
- { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0, 0 },
- { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2, 0 },
- { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 },
- { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 },
- { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 },
- { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 },
- { "IBM F1 P264/32", 5, 14, 3, -1, 1 },
- /* This next signature may not be a 3.5 bios */
- { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 },
- { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
- { "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 3, 5, 0 },
- { "FUTURE DOMAIN CORP. V3.6008/18/93", 5, 34, 3, 6, 0 },
- { "FUTURE DOMAIN CORP. V3.6108/18/93", 5, 34, 3, 6, 0 },
- { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1, 0 },
-
- /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGNATURE
- Also, fix the disk geometry code for your signature and send your
- changes for faith@cs.unc.edu. Above all, do *NOT* change any old
- signatures!
-
- Note that the last line will match a "generic" 18XX bios. Because
- Future Domain has changed the host SCSI ID and/or the location of the
- geometry information in the on-board RAM area for each of the first
- three BIOS's, it is still important to enter a fully qualified
- signature in the table for any new BIOS's (after the host SCSI ID and
- geometry location are verified). */
-};
+static int fdomain_test_loopback(int base)
+{
+ int i;
-#define SIGNATURE_COUNT ARRAY_SIZE(signatures)
+ for (i = 0; i < 255; i++) {
+ outb(i, base + REG_LOOPBACK);
+ if (inb(base + REG_LOOPBACK) != i)
+ return 1;
+ }
-#endif /* !PCMCIA */
+ return 0;
+}
-static void print_banner( struct Scsi_Host *shpnt )
+static void fdomain_reset(int base)
{
- if (!shpnt) return; /* This won't ever happen */
-
- if (bios_major < 0 && bios_minor < 0) {
- printk(KERN_INFO "scsi%d: <fdomain> No BIOS; using scsi id %d\n",
- shpnt->host_no, shpnt->this_id);
- } else {
- printk(KERN_INFO "scsi%d: <fdomain> BIOS version ", shpnt->host_no);
-
- if (bios_major >= 0) printk("%d.", bios_major);
- else printk("?.");
-
- if (bios_minor >= 0) printk("%d", bios_minor);
- else printk("?.");
-
- printk( " at 0x%lx using scsi id %d\n",
- bios_base, shpnt->this_id );
- }
-
- /* If this driver works for later FD PCI
- boards, we will have to modify banner
- for additional PCI cards, but for now if
- it's PCI it's a TMC-3260 - JTM */
- printk(KERN_INFO "scsi%d: <fdomain> %s chip at 0x%x irq ",
- shpnt->host_no,
- chip == tmc1800 ? "TMC-1800" : (chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30") : "Unknown")),
- port_base);
-
- if (interrupt_level)
- printk("%d", interrupt_level);
- else
- printk("<none>");
-
- printk( "\n" );
+ outb(BCTL_RST, base + REG_BCTL);
+ mdelay(20);
+ outb(0, base + REG_BCTL);
+ mdelay(1150);
+ outb(0, base + REG_MCTL);
+ outb(PARITY_MASK, base + REG_ACTL);
}
-int fdomain_setup(char *str)
+static int fdomain_select(struct Scsi_Host *sh, int target)
{
- int ints[4];
+ int status;
+ unsigned long timeout;
+ struct fdomain *fd = shost_priv(sh);
- (void)get_options(str, ARRAY_SIZE(ints), ints);
+ outb(BCTL_BUSEN | BCTL_SEL, fd->base + REG_BCTL);
+ outb(BIT(sh->this_id) | BIT(target), fd->base + REG_SCSI_DATA_NOACK);
- if (setup_called++ || ints[0] < 2 || ints[0] > 3) {
- printk(KERN_INFO "scsi: <fdomain> Usage: fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]\n");
- printk(KERN_ERR "scsi: <fdomain> Bad LILO/INSMOD parameters?\n");
- return 0;
- }
+ /* Stop arbitration and enable parity */
+ outb(PARITY_MASK, fd->base + REG_ACTL);
+
+ timeout = 350; /* 350 msec */
- port_base = ints[0] >= 1 ? ints[1] : 0;
- interrupt_level = ints[0] >= 2 ? ints[2] : 0;
- this_id = ints[0] >= 3 ? ints[3] : 0;
-
- bios_major = bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */
- ++setup_called;
+ do {
+ status = inb(fd->base + REG_BSTAT);
+ if (status & BSTAT_BSY) {
+ /* Enable SCSI Bus */
+ /* (on error, should make bus idle with 0) */
+ outb(BCTL_BUSEN, fd->base + REG_BCTL);
+ return 0;
+ }
+ mdelay(1);
+ } while (--timeout);
+ fdomain_make_bus_idle(fd);
return 1;
}
-__setup("fdomain=", fdomain_setup);
-
-
-static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */
+static void fdomain_finish_cmd(struct fdomain *fd)
{
- mdelay(10*amount);
+ outb(0, fd->base + REG_ICTL);
+ fdomain_make_bus_idle(fd);
+ scsi_done(fd->cur_cmd);
+ fd->cur_cmd = NULL;
}
-static inline void fdomain_make_bus_idle( void )
+static void fdomain_read_data(struct scsi_cmnd *cmd)
{
- outb(0, port_base + SCSI_Cntl);
- outb(0, port_base + SCSI_Mode_Cntl);
- if (chip == tmc18c50 || chip == tmc18c30)
- outb(0x21 | PARITY_MASK, port_base + TMC_Cntl); /* Clear forced intr. */
- else
- outb(0x01 | PARITY_MASK, port_base + TMC_Cntl);
+ struct fdomain *fd = shost_priv(cmd->device->host);
+ unsigned char *virt, *ptr;
+ size_t offset, len;
+
+ while ((len = inw(fd->base + REG_FIFO_COUNT)) > 0) {
+ offset = scsi_bufflen(cmd) - scsi_get_resid(cmd);
+ virt = scsi_kmap_atomic_sg(scsi_sglist(cmd), scsi_sg_count(cmd),
+ &offset, &len);
+ ptr = virt + offset;
+ if (len & 1)
+ *ptr++ = inb(fd->base + REG_FIFO);
+ if (len > 1)
+ insw(fd->base + REG_FIFO, ptr, len >> 1);
+ scsi_set_resid(cmd, scsi_get_resid(cmd) - len);
+ scsi_kunmap_atomic_sg(virt);
+ }
}
-static int fdomain_is_valid_port( int port )
+static void fdomain_write_data(struct scsi_cmnd *cmd)
{
-#if DEBUG_DETECT
- printk( " (%x%x),",
- inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
-#endif
-
- /* The MCA ID is a unique id for each MCA compatible board. We
- are using ISA boards, but Future Domain provides the MCA ID
- anyway. We can use this ID to ensure that this is a Future
- Domain TMC-1660/TMC-1680.
- */
-
- if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
- if (inb( port + LSB_ID_Code ) != 0x27) return 0;
- if (inb( port + MSB_ID_Code ) != 0x61) return 0;
- chip = tmc1800;
- } else { /* test for 0xe960 id */
- if (inb( port + MSB_ID_Code ) != 0x60) return 0;
- chip = tmc18c50;
-
- /* Try to toggle 32-bit mode. This only
- works on an 18c30 chip. (User reports
- say this works, so we should switch to
- it in the near future.) */
-
- outb( 0x80, port + IO_Control );
- if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
- outb( 0x00, port + IO_Control );
- if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
- chip = tmc18c30;
- FIFO_Size = 0x800; /* 2k FIFO */
- }
- }
- /* If that failed, we are an 18c50. */
- }
-
- return 1;
+ struct fdomain *fd = shost_priv(cmd->device->host);
+ /* 8k FIFO for pre-tmc18c30 chips, 2k FIFO for tmc18c30 */
+ int FIFO_Size = fd->chip == tmc18c30 ? 0x800 : 0x2000;
+ unsigned char *virt, *ptr;
+ size_t offset, len;
+
+ while ((len = FIFO_Size - inw(fd->base + REG_FIFO_COUNT)) > 512) {
+ offset = scsi_bufflen(cmd) - scsi_get_resid(cmd);
+ if (len + offset > scsi_bufflen(cmd)) {
+ len = scsi_bufflen(cmd) - offset;
+ if (len == 0)
+ break;
+ }
+ virt = scsi_kmap_atomic_sg(scsi_sglist(cmd), scsi_sg_count(cmd),
+ &offset, &len);
+ ptr = virt + offset;
+ if (len & 1)
+ outb(*ptr++, fd->base + REG_FIFO);
+ if (len > 1)
+ outsw(fd->base + REG_FIFO, ptr, len >> 1);
+ scsi_set_resid(cmd, scsi_get_resid(cmd) - len);
+ scsi_kunmap_atomic_sg(virt);
+ }
}
-static int fdomain_test_loopback( void )
+static void fdomain_work(struct work_struct *work)
{
- int i;
- int result;
-
- for (i = 0; i < 255; i++) {
- outb( i, port_base + Write_Loopback );
- result = inb( port_base + Read_Loopback );
- if (i != result)
- return 1;
- }
- return 0;
-}
-
-#ifndef PCMCIA
-
-/* fdomain_get_irq assumes that we have a valid MCA ID for a
- TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the
- bios_base matches these ports. If someone was unlucky enough to have
- purchased more than one Future Domain board, then they will have to
- modify this code, as we only detect one board here. [The one with the
- lowest bios_base.]
-
- Note that this routine is only used for systems without a PCI BIOS32
- (e.g., ISA bus). For PCI bus systems, this routine will likely fail
- unless one of the IRQs listed in the ints array is used by the board.
- Sometimes it is possible to use the computer's BIOS setup screen to
- configure a PCI system so that one of these IRQs will be used by the
- Future Domain card. */
+ struct fdomain *fd = container_of(work, struct fdomain, work);
+ struct Scsi_Host *sh = container_of((void *)fd, struct Scsi_Host,
+ hostdata);
+ struct scsi_cmnd *cmd = fd->cur_cmd;
+ struct scsi_pointer *scsi_pointer = fdomain_scsi_pointer(cmd);
+ unsigned long flags;
+ int status;
+ int done = 0;
+
+ spin_lock_irqsave(sh->host_lock, flags);
+
+ if (scsi_pointer->phase & in_arbitration) {
+ status = inb(fd->base + REG_ASTAT);
+ if (!(status & ASTAT_ARB)) {
+ set_host_byte(cmd, DID_BUS_BUSY);
+ fdomain_finish_cmd(fd);
+ goto out;
+ }
+ scsi_pointer->phase = in_selection;
+
+ outb(ICTL_SEL | FIFO_COUNT, fd->base + REG_ICTL);
+ outb(BCTL_BUSEN | BCTL_SEL, fd->base + REG_BCTL);
+ outb(BIT(cmd->device->host->this_id) | BIT(scmd_id(cmd)),
+ fd->base + REG_SCSI_DATA_NOACK);
+ /* Stop arbitration and enable parity */
+ outb(ACTL_IRQEN | PARITY_MASK, fd->base + REG_ACTL);
+ goto out;
+ } else if (scsi_pointer->phase & in_selection) {
+ status = inb(fd->base + REG_BSTAT);
+ if (!(status & BSTAT_BSY)) {
+ /* Try again, for slow devices */
+ if (fdomain_select(cmd->device->host, scmd_id(cmd))) {
+ set_host_byte(cmd, DID_NO_CONNECT);
+ fdomain_finish_cmd(fd);
+ goto out;
+ }
+ /* Stop arbitration and enable parity */
+ outb(ACTL_IRQEN | PARITY_MASK, fd->base + REG_ACTL);
+ }
+ scsi_pointer->phase = in_other;
+ outb(ICTL_FIFO | ICTL_REQ | FIFO_COUNT, fd->base + REG_ICTL);
+ outb(BCTL_BUSEN, fd->base + REG_BCTL);
+ goto out;
+ }
-static int fdomain_get_irq( int base )
-{
- int options = inb(base + Configuration1);
-
-#if DEBUG_DETECT
- printk("scsi: <fdomain> Options = %x\n", options);
-#endif
-
- /* Check for board with lowest bios_base --
- this isn't valid for the 18c30 or for
- boards on the PCI bus, so just assume we
- have the right board. */
-
- if (chip != tmc18c30 && !PCI_bus && addresses[(options & 0xc0) >> 6 ] != bios_base)
- return 0;
- return ints[(options & 0x0e) >> 1];
-}
+ /* fdomain_scsi_pointer(cur_cmd)->phase == in_other: this is the body of the routine */
+ status = inb(fd->base + REG_BSTAT);
-static int fdomain_isa_detect( int *irq, int *iobase )
-{
- int i, j;
- int base = 0xdeadbeef;
- int flag = 0;
-
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> fdomain_isa_detect:" );
-#endif
-
- for (i = 0; i < ADDRESS_COUNT; i++) {
- void __iomem *p = ioremap(addresses[i], 0x2000);
- if (!p)
- continue;
-#if DEBUG_DETECT
- printk( " %lx(%lx),", addresses[i], bios_base );
-#endif
- for (j = 0; j < SIGNATURE_COUNT; j++) {
- if (check_signature(p + signatures[j].sig_offset,
- signatures[j].signature,
- signatures[j].sig_length )) {
- bios_major = signatures[j].major_bios_version;
- bios_minor = signatures[j].minor_bios_version;
- PCI_bus = (signatures[j].flag == 1);
- Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
- bios_base = addresses[i];
- bios_mem = p;
- goto found;
- }
- }
- iounmap(p);
- }
-
-found:
- if (bios_major == 2) {
- /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
- Assuming the ROM is enabled (otherwise we wouldn't have been
- able to read the ROM signature :-), then the ROM sets up the
- RAM area with some magic numbers, such as a list of port
- base addresses and a list of the disk "geometry" reported to
- DOS (this geometry has nothing to do with physical geometry).
- */
-
- switch (Quantum) {
- case 2: /* ISA_200S */
- case 3: /* ISA_250MG */
- base = readb(bios_mem + 0x1fa2) + (readb(bios_mem + 0x1fa3) << 8);
- break;
- case 4: /* ISA_200S (another one) */
- base = readb(bios_mem + 0x1fa3) + (readb(bios_mem + 0x1fa4) << 8);
- break;
- default:
- base = readb(bios_mem + 0x1fcc) + (readb(bios_mem + 0x1fcd) << 8);
- break;
- }
-
-#if DEBUG_DETECT
- printk( " %x,", base );
-#endif
-
- for (i = 0; i < PORT_COUNT; i++) {
- if (base == ports[i]) {
- if (!request_region(base, 0x10, "fdomain"))
+ if (status & BSTAT_REQ) {
+ switch (status & (BSTAT_MSG | BSTAT_CMD | BSTAT_IO)) {
+ case BSTAT_CMD: /* COMMAND OUT */
+ outb(cmd->cmnd[scsi_pointer->sent_command++],
+ fd->base + REG_SCSI_DATA);
+ break;
+ case 0: /* DATA OUT -- tmc18c50/tmc18c30 only */
+ if (fd->chip != tmc1800 && !scsi_pointer->have_data_in) {
+ scsi_pointer->have_data_in = -1;
+ outb(ACTL_IRQEN | ACTL_FIFOWR | ACTL_FIFOEN |
+ PARITY_MASK, fd->base + REG_ACTL);
+ }
+ break;
+ case BSTAT_IO: /* DATA IN -- tmc18c50/tmc18c30 only */
+ if (fd->chip != tmc1800 && !scsi_pointer->have_data_in) {
+ scsi_pointer->have_data_in = 1;
+ outb(ACTL_IRQEN | ACTL_FIFOEN | PARITY_MASK,
+ fd->base + REG_ACTL);
+ }
break;
- if (!fdomain_is_valid_port(base)) {
- release_region(base, 0x10);
+ case BSTAT_CMD | BSTAT_IO: /* STATUS IN */
+ scsi_pointer->Status = inb(fd->base + REG_SCSI_DATA);
+ break;
+ case BSTAT_MSG | BSTAT_CMD: /* MESSAGE OUT */
+ outb(MESSAGE_REJECT, fd->base + REG_SCSI_DATA);
+ break;
+ case BSTAT_MSG | BSTAT_CMD | BSTAT_IO: /* MESSAGE IN */
+ scsi_pointer->Message = inb(fd->base + REG_SCSI_DATA);
+ if (scsi_pointer->Message == COMMAND_COMPLETE)
+ ++done;
break;
}
- *irq = fdomain_get_irq( base );
- *iobase = base;
- return 1;
}
- }
-
- /* This is a bad sign. It usually means that someone patched the
- BIOS signature list (the signatures variable) to contain a BIOS
- signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */
-
-#if DEBUG_DETECT
- printk( " RAM FAILED, " );
-#endif
- }
-
- /* Anyway, the alternative to finding the address in the RAM is to just
- search through every possible port address for one that is attached
- to the Future Domain card. Don't panic, though, about reading all
- these random port addresses -- there are rumors that the Future
- Domain BIOS does something very similar.
-
- Do not, however, check ports which the kernel knows are being used by
- another driver. */
-
- for (i = 0; i < PORT_COUNT; i++) {
- base = ports[i];
- if (!request_region(base, 0x10, "fdomain")) {
-#if DEBUG_DETECT
- printk( " (%x inuse),", base );
-#endif
- continue;
- }
-#if DEBUG_DETECT
- printk( " %x,", base );
-#endif
- flag = fdomain_is_valid_port(base);
- if (flag)
- break;
- release_region(base, 0x10);
- }
-
-#if DEBUG_DETECT
- if (flag) printk( " SUCCESS\n" );
- else printk( " FAILURE\n" );
-#endif
-
- if (!flag) return 0; /* iobase not found */
-
- *irq = fdomain_get_irq( base );
- *iobase = base;
-
- return 1; /* success */
-}
-#else /* PCMCIA */
+ if (fd->chip == tmc1800 && !scsi_pointer->have_data_in &&
+ scsi_pointer->sent_command >= cmd->cmd_len) {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ scsi_pointer->have_data_in = -1;
+ outb(ACTL_IRQEN | ACTL_FIFOWR | ACTL_FIFOEN |
+ PARITY_MASK, fd->base + REG_ACTL);
+ } else {
+ scsi_pointer->have_data_in = 1;
+ outb(ACTL_IRQEN | ACTL_FIFOEN | PARITY_MASK,
+ fd->base + REG_ACTL);
+ }
+ }
-static int fdomain_isa_detect( int *irq, int *iobase )
-{
- if (irq)
- *irq = 0;
- if (iobase)
- *iobase = 0;
- return 0;
+ if (scsi_pointer->have_data_in == -1) /* DATA OUT */
+ fdomain_write_data(cmd);
+
+ if (scsi_pointer->have_data_in == 1) /* DATA IN */
+ fdomain_read_data(cmd);
+
+ if (done) {
+ set_status_byte(cmd, scsi_pointer->Status);
+ set_host_byte(cmd, DID_OK);
+ scsi_msg_to_host_byte(cmd, scsi_pointer->Message);
+ fdomain_finish_cmd(fd);
+ } else {
+ if (scsi_pointer->phase & disconnect) {
+ outb(ICTL_FIFO | ICTL_SEL | ICTL_REQ | FIFO_COUNT,
+ fd->base + REG_ICTL);
+ outb(0, fd->base + REG_BCTL);
+ } else
+ outb(ICTL_FIFO | ICTL_REQ | FIFO_COUNT,
+ fd->base + REG_ICTL);
+ }
+out:
+ spin_unlock_irqrestore(sh->host_lock, flags);
}
-#endif /* !PCMCIA */
+static irqreturn_t fdomain_irq(int irq, void *dev_id)
+{
+ struct fdomain *fd = dev_id;
+ /* Is it our IRQ? */
+ if ((inb(fd->base + REG_ASTAT) & ASTAT_IRQ) == 0)
+ return IRQ_NONE;
-/* PCI detection function: int fdomain_pci_bios_detect(int* irq, int*
- iobase) This function gets the Interrupt Level and I/O base address from
- the PCI configuration registers. */
+ outb(0, fd->base + REG_ICTL);
-#ifdef CONFIG_PCI
-static int fdomain_pci_bios_detect( int *irq, int *iobase, struct pci_dev **ret_pdev )
-{
- unsigned int pci_irq; /* PCI interrupt line */
- unsigned long pci_base; /* PCI I/O base address */
- struct pci_dev *pdev = NULL;
-
-#if DEBUG_DETECT
- /* Tell how to print a list of the known PCI devices from bios32 and
- list vendor and device IDs being used if in debug mode. */
-
- printk( "scsi: <fdomain> INFO: use lspci -v to see list of PCI devices\n" );
- printk( "scsi: <fdomain> TMC-3260 detect:"
- " Using Vendor ID: 0x%x and Device ID: 0x%x\n",
- PCI_VENDOR_ID_FD,
- PCI_DEVICE_ID_FD_36C70 );
-#endif
-
- if ((pdev = pci_get_device(PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70, pdev)) == NULL)
- return 0;
- if (pci_enable_device(pdev))
- goto fail;
-
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> TMC-3260 detect:"
- " PCI bus %u, device %u, function %u\n",
- pdev->bus->number,
- PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn));
-#endif
-
- /* We now have the appropriate device function for the FD board so we
- just read the PCI config info from the registers. */
-
- pci_base = pci_resource_start(pdev, 0);
- pci_irq = pdev->irq;
-
- if (!request_region( pci_base, 0x10, "fdomain" ))
- goto fail;
-
- /* Now we have the I/O base address and interrupt from the PCI
- configuration registers. */
-
- *irq = pci_irq;
- *iobase = pci_base;
- *ret_pdev = pdev;
-
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> TMC-3260 detect:"
- " IRQ = %d, I/O base = 0x%x [0x%lx]\n", *irq, *iobase, pci_base );
-#endif
-
- if (!fdomain_is_valid_port(pci_base)) {
- printk(KERN_ERR "scsi: <fdomain> PCI card detected, but driver not loaded (invalid port)\n" );
- release_region(pci_base, 0x10);
- goto fail;
- }
-
- /* Fill in a few global variables. Ugh. */
- bios_major = bios_minor = -1;
- PCI_bus = 1;
- PCI_dev = pdev;
- Quantum = 0;
- bios_base = 0;
-
- return 1;
-fail:
- pci_dev_put(pdev);
- return 0;
-}
+ /* We usually have one spurious interrupt after each command. */
+ if (!fd->cur_cmd) /* Spurious interrupt */
+ return IRQ_NONE;
-#endif
+ schedule_work(&fd->work);
-struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt )
-{
- int retcode;
- struct Scsi_Host *shpnt;
- struct pci_dev *pdev = NULL;
-
- if (setup_called) {
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> No BIOS, using port_base = 0x%x, irq = %d\n",
- port_base, interrupt_level );
-#endif
- if (!request_region(port_base, 0x10, "fdomain")) {
- printk( "scsi: <fdomain> port 0x%x is busy\n", port_base );
- printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
- return NULL;
- }
- if (!fdomain_is_valid_port( port_base )) {
- printk( "scsi: <fdomain> Cannot locate chip at port base 0x%x\n",
- port_base );
- printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
- release_region(port_base, 0x10);
- return NULL;
- }
- } else {
- int flag = 0;
-
-#ifdef CONFIG_PCI
- /* Try PCI detection first */
- flag = fdomain_pci_bios_detect( &interrupt_level, &port_base, &pdev );
-#endif
- if (!flag) {
- /* Then try ISA bus detection */
- flag = fdomain_isa_detect( &interrupt_level, &port_base );
-
- if (!flag) {
- printk( "scsi: <fdomain> Detection failed (no card)\n" );
- return NULL;
- }
- }
- }
-
- fdomain_16x0_bus_reset(NULL);
-
- if (fdomain_test_loopback()) {
- printk(KERN_ERR "scsi: <fdomain> Detection failed (loopback test failed at port base 0x%x)\n", port_base);
- if (setup_called) {
- printk(KERN_ERR "scsi: <fdomain> Bad LILO/INSMOD parameters?\n");
- }
- goto fail;
- }
-
- if (this_id) {
- tpnt->this_id = (this_id & 0x07);
- adapter_mask = (1 << tpnt->this_id);
- } else {
- if (PCI_bus || (bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
- tpnt->this_id = 7;
- adapter_mask = 0x80;
- } else {
- tpnt->this_id = 6;
- adapter_mask = 0x40;
- }
- }
-
-/* Print out a banner here in case we can't
- get resources. */
-
- shpnt = scsi_register( tpnt, 0 );
- if(shpnt == NULL) {
- release_region(port_base, 0x10);
- return NULL;
- }
- shpnt->irq = interrupt_level;
- shpnt->io_port = port_base;
- shpnt->n_io_port = 0x10;
- print_banner( shpnt );
-
- /* Log IRQ with kernel */
- if (!interrupt_level) {
- printk(KERN_ERR "scsi: <fdomain> Card Detected, but driver not loaded (no IRQ)\n" );
- goto fail;
- } else {
- /* Register the IRQ with the kernel */
-
- retcode = request_irq( interrupt_level,
- do_fdomain_16x0_intr, pdev?IRQF_SHARED:0, "fdomain", shpnt);
-
- if (retcode < 0) {
- if (retcode == -EINVAL) {
- printk(KERN_ERR "scsi: <fdomain> IRQ %d is bad!\n", interrupt_level );
- printk(KERN_ERR " This shouldn't happen!\n" );
- printk(KERN_ERR " Send mail to faith@acm.org\n" );
- } else if (retcode == -EBUSY) {
- printk(KERN_ERR "scsi: <fdomain> IRQ %d is already in use!\n", interrupt_level );
- printk(KERN_ERR " Please use another IRQ!\n" );
- } else {
- printk(KERN_ERR "scsi: <fdomain> Error getting IRQ %d\n", interrupt_level );
- printk(KERN_ERR " This shouldn't happen!\n" );
- printk(KERN_ERR " Send mail to faith@acm.org\n" );
- }
- printk(KERN_ERR "scsi: <fdomain> Detected, but driver not loaded (IRQ)\n" );
- goto fail;
- }
- }
- return shpnt;
-fail:
- pci_dev_put(pdev);
- release_region(port_base, 0x10);
- return NULL;
-}
-
-static int fdomain_16x0_detect(struct scsi_host_template *tpnt)
-{
- if (fdomain)
- fdomain_setup(fdomain);
- return (__fdomain_16x0_detect(tpnt) != NULL);
+ return IRQ_HANDLED;
}
-static const char *fdomain_16x0_info( struct Scsi_Host *ignore )
+static int fdomain_queue(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
{
- static char buffer[128];
- char *pt;
-
- strcpy( buffer, "Future Domain 16-bit SCSI Driver Version" );
- if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
- strcat( buffer, strchr( VERSION, ':' ) + 1 );
- pt = strrchr( buffer, '$') - 1;
- if (!pt) /* Stripped RCS Revision string? */
- pt = buffer + strlen( buffer ) - 1;
- if (*pt != ' ')
- ++pt;
- *pt = '\0';
- } else { /* Assume VERSION is a number */
- strcat( buffer, " " VERSION );
- }
-
- return buffer;
-}
+ struct scsi_pointer *scsi_pointer = fdomain_scsi_pointer(cmd);
+ struct fdomain *fd = shost_priv(cmd->device->host);
+ unsigned long flags;
-#if 0
-static int fdomain_arbitrate( void )
-{
- int status = 0;
- unsigned long timeout;
-
-#if EVERY_ACCESS
- printk( "fdomain_arbitrate()\n" );
-#endif
-
- outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */
- outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */
- outb(0x04 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */
-
- timeout = 500;
- do {
- status = inb(port_base + TMC_Status); /* Read adapter status */
- if (status & 0x02) /* Arbitration complete */
- return 0;
- mdelay(1); /* Wait one millisecond */
- } while (--timeout);
-
- /* Make bus idle */
- fdomain_make_bus_idle();
-
-#if EVERY_ACCESS
- printk( "Arbitration failed, status = %x\n", status );
-#endif
-#if ERRORS_ONLY
- printk( "scsi: <fdomain> Arbitration failed, status = %x\n", status );
-#endif
- return 1;
-}
-#endif
+ scsi_pointer->Status = 0;
+ scsi_pointer->Message = 0;
+ scsi_pointer->have_data_in = 0;
+ scsi_pointer->sent_command = 0;
+ scsi_pointer->phase = in_arbitration;
+ scsi_set_resid(cmd, scsi_bufflen(cmd));
-static int fdomain_select( int target )
-{
- int status;
- unsigned long timeout;
-#if ERRORS_ONLY
- static int flag = 0;
-#endif
-
- outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */
- outb(adapter_mask | (1 << target), port_base + SCSI_Data_NoACK);
-
- /* Stop arbitration and enable parity */
- outb(PARITY_MASK, port_base + TMC_Cntl);
-
- timeout = 350; /* 350 msec */
-
- do {
- status = inb(port_base + SCSI_Status); /* Read adapter status */
- if (status & 1) { /* Busy asserted */
- /* Enable SCSI Bus (on error, should make bus idle with 0) */
- outb(0x80, port_base + SCSI_Cntl);
- return 0;
- }
- mdelay(1); /* wait one msec */
- } while (--timeout);
- /* Make bus idle */
- fdomain_make_bus_idle();
-#if EVERY_ACCESS
- if (!target) printk( "Selection failed\n" );
-#endif
-#if ERRORS_ONLY
- if (!target) {
- if (!flag) /* Skip first failure for all chips. */
- ++flag;
- else
- printk( "scsi: <fdomain> Selection failed\n" );
- }
-#endif
- return 1;
-}
+ spin_lock_irqsave(sh->host_lock, flags);
-static void my_done(int error)
-{
- if (in_command) {
- in_command = 0;
- outb(0x00, port_base + Interrupt_Cntl);
- fdomain_make_bus_idle();
- current_SC->result = error;
- if (current_SC->scsi_done)
- current_SC->scsi_done( current_SC );
- else panic( "scsi: <fdomain> current_SC->scsi_done() == NULL" );
- } else {
- panic( "scsi: <fdomain> my_done() called outside of command\n" );
- }
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
-}
+ fd->cur_cmd = cmd;
-static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id)
-{
- unsigned long flags;
- int status;
- int done = 0;
- unsigned data_count;
-
- /* The fdomain_16x0_intr is only called via
- the interrupt handler. The goal of the
- sti() here is to allow other
- interruptions while this routine is
- running. */
-
- /* Check for other IRQ sources */
- if ((inb(port_base + TMC_Status) & 0x01) == 0)
- return IRQ_NONE;
-
- /* It is our IRQ */
- outb(0x00, port_base + Interrupt_Cntl);
-
- /* We usually have one spurious interrupt after each command. Ignore it. */
- if (!in_command || !current_SC) { /* Spurious interrupt */
-#if EVERY_ACCESS
- printk( "Spurious interrupt, in_command = %d, current_SC = %x\n",
- in_command, current_SC );
-#endif
- return IRQ_NONE;
- }
-
- /* Abort calls my_done, so we do nothing here. */
- if (current_SC->SCp.phase & aborted) {
-#if DEBUG_ABORT
- printk( "scsi: <fdomain> Interrupt after abort, ignoring\n" );
-#endif
- /*
- return IRQ_HANDLED; */
- }
-
-#if DEBUG_RACE
- ++in_interrupt_flag;
-#endif
-
- if (current_SC->SCp.phase & in_arbitration) {
- status = inb(port_base + TMC_Status); /* Read adapter status */
- if (!(status & 0x02)) {
-#if EVERY_ACCESS
- printk( " AFAIL " );
-#endif
- spin_lock_irqsave(current_SC->device->host->host_lock, flags);
- my_done( DID_BUS_BUSY << 16 );
- spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
- return IRQ_HANDLED;
- }
- current_SC->SCp.phase = in_selection;
-
- outb(0x40 | FIFO_COUNT, port_base + Interrupt_Cntl);
-
- outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */
- outb(adapter_mask | (1 << scmd_id(current_SC)), port_base + SCSI_Data_NoACK);
-
- /* Stop arbitration and enable parity */
- outb(0x10 | PARITY_MASK, port_base + TMC_Cntl);
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
- } else if (current_SC->SCp.phase & in_selection) {
- status = inb(port_base + SCSI_Status);
- if (!(status & 0x01)) {
- /* Try again, for slow devices */
- if (fdomain_select( scmd_id(current_SC) )) {
-#if EVERY_ACCESS
- printk( " SFAIL " );
-#endif
- spin_lock_irqsave(current_SC->device->host->host_lock, flags);
- my_done( DID_NO_CONNECT << 16 );
- spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
- return IRQ_HANDLED;
- } else {
-#if EVERY_ACCESS
- printk( " AltSel " );
-#endif
- /* Stop arbitration and enable parity */
- outb(0x10 | PARITY_MASK, port_base + TMC_Cntl);
- }
- }
- current_SC->SCp.phase = in_other;
- outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl);
- outb(0x80, port_base + SCSI_Cntl);
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
- }
-
- /* current_SC->SCp.phase == in_other: this is the body of the routine */
-
- status = inb(port_base + SCSI_Status);
-
- if (status & 0x10) { /* REQ */
-
- switch (status & 0x0e) {
-
- case 0x08: /* COMMAND OUT */
- outb(current_SC->cmnd[current_SC->SCp.sent_command++],
- port_base + Write_SCSI_Data);
-#if EVERY_ACCESS
- printk( "CMD = %x,",
- current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
-#endif
- break;
- case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = -1;
- outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl);
- }
- break;
- case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = 1;
- outb(0x90 | PARITY_MASK, port_base + TMC_Cntl);
- }
- break;
- case 0x0c: /* STATUS IN */
- current_SC->SCp.Status = inb(port_base + Read_SCSI_Data);
-#if EVERY_ACCESS
- printk( "Status = %x, ", current_SC->SCp.Status );
-#endif
-#if ERRORS_ONLY
- if (current_SC->SCp.Status
- && current_SC->SCp.Status != 2
- && current_SC->SCp.Status != 8) {
- printk( "scsi: <fdomain> target = %d, command = %x, status = %x\n",
- current_SC->device->id,
- current_SC->cmnd[0],
- current_SC->SCp.Status );
- }
-#endif
- break;
- case 0x0a: /* MESSAGE OUT */
- outb(MESSAGE_REJECT, port_base + Write_SCSI_Data); /* Reject */
- break;
- case 0x0e: /* MESSAGE IN */
- current_SC->SCp.Message = inb(port_base + Read_SCSI_Data);
-#if EVERY_ACCESS
- printk( "Message = %x, ", current_SC->SCp.Message );
-#endif
- if (!current_SC->SCp.Message) ++done;
-#if DEBUG_MESSAGES || EVERY_ACCESS
- if (current_SC->SCp.Message) {
- printk( "scsi: <fdomain> message = %x\n",
- current_SC->SCp.Message );
- }
-#endif
- break;
- }
- }
-
- if (chip == tmc1800 && !current_SC->SCp.have_data_in
- && (current_SC->SCp.sent_command >= current_SC->cmd_len)) {
-
- if(current_SC->sc_data_direction == DMA_TO_DEVICE)
- {
- current_SC->SCp.have_data_in = -1;
- outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl);
- }
- else
- {
- current_SC->SCp.have_data_in = 1;
- outb(0x90 | PARITY_MASK, port_base + TMC_Cntl);
- }
- }
-
- if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
- while ((data_count = FIFO_Size - inw(port_base + FIFO_Data_Count)) > 512) {
-#if EVERY_ACCESS
- printk( "DC=%d, ", data_count ) ;
-#endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count > 0) {
-#if EVERY_ACCESS
- printk( "%d OUT, ", data_count );
-#endif
- if (data_count == 1) {
- outb(*current_SC->SCp.ptr++, port_base + Write_FIFO);
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1;
- outsw(port_base + Write_FIFO, current_SC->SCp.ptr, data_count);
- current_SC->SCp.ptr += 2 * data_count;
- current_SC->SCp.this_residual -= 2 * data_count;
- }
- }
- if (!current_SC->SCp.this_residual) {
- if (current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- } else
- break;
- }
- }
- }
-
- if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
- while ((data_count = inw(port_base + FIFO_Data_Count)) > 0) {
-#if EVERY_ACCESS
- printk( "DC=%d, ", data_count );
-#endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count) {
-#if EVERY_ACCESS
- printk( "%d IN, ", data_count );
-#endif
- if (data_count == 1) {
- *current_SC->SCp.ptr++ = inb(port_base + Read_FIFO);
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1; /* Number of words */
- insw(port_base + Read_FIFO, current_SC->SCp.ptr, data_count);
- current_SC->SCp.ptr += 2 * data_count;
- current_SC->SCp.this_residual -= 2 * data_count;
- }
- }
- if (!current_SC->SCp.this_residual
- && current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- }
- }
- }
-
- if (done) {
-#if EVERY_ACCESS
- printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in );
-#endif
-
-#if ERRORS_ONLY
- if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
- char *buf = scsi_sglist(current_SC);
- if ((unsigned char)(*(buf + 2)) & 0x0f) {
- unsigned char key;
- unsigned char code;
- unsigned char qualifier;
-
- key = (unsigned char)(*(buf + 2)) & 0x0f;
- code = (unsigned char)(*(buf + 12));
- qualifier = (unsigned char)(*(buf + 13));
-
- if (key != UNIT_ATTENTION
- && !(key == NOT_READY
- && code == 0x04
- && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
- && !(key == ILLEGAL_REQUEST && (code == 0x25
- || code == 0x24
- || !code)))
-
- printk( "scsi: <fdomain> REQUEST SENSE"
- " Key = %x, Code = %x, Qualifier = %x\n",
- key, code, qualifier );
- }
- }
-#endif
-#if EVERY_ACCESS
- printk( "BEFORE MY_DONE. . ." );
-#endif
- spin_lock_irqsave(current_SC->device->host->host_lock, flags);
- my_done( (current_SC->SCp.Status & 0xff)
- | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
- spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
-#if EVERY_ACCESS
- printk( "RETURNING.\n" );
-#endif
-
- } else {
- if (current_SC->SCp.phase & disconnect) {
- outb(0xd0 | FIFO_COUNT, port_base + Interrupt_Cntl);
- outb(0x00, port_base + SCSI_Cntl);
- } else {
- outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl);
- }
- }
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
-}
+ fdomain_make_bus_idle(fd);
-static int fdomain_16x0_queue_lck(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
-{
- if (in_command) {
- panic( "scsi: <fdomain> fdomain_16x0_queue() NOT REENTRANT!\n" );
- }
-#if EVERY_ACCESS
- printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
- SCpnt->target,
- *(unsigned char *)SCpnt->cmnd,
- scsi_sg_count(SCpnt),
- scsi_bufflen(SCpnt));
-#endif
-
- fdomain_make_bus_idle();
-
- current_SC = SCpnt; /* Save this for the done function */
- current_SC->scsi_done = done;
-
- /* Initialize static data */
-
- if (scsi_sg_count(current_SC)) {
- current_SC->SCp.buffer = scsi_sglist(current_SC);
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- current_SC->SCp.buffers_residual = scsi_sg_count(current_SC) - 1;
- } else {
- current_SC->SCp.ptr = NULL;
- current_SC->SCp.this_residual = 0;
- current_SC->SCp.buffer = NULL;
- current_SC->SCp.buffers_residual = 0;
- }
-
- current_SC->SCp.Status = 0;
- current_SC->SCp.Message = 0;
- current_SC->SCp.have_data_in = 0;
- current_SC->SCp.sent_command = 0;
- current_SC->SCp.phase = in_arbitration;
-
- /* Start arbitration */
- outb(0x00, port_base + Interrupt_Cntl);
- outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */
- outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */
- ++in_command;
- outb(0x20, port_base + Interrupt_Cntl);
- outb(0x14 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */
-
- return 0;
-}
+ /* Start arbitration */
+ outb(0, fd->base + REG_ICTL);
+ outb(0, fd->base + REG_BCTL); /* Disable data drivers */
+ /* Set our id bit */
+ outb(BIT(cmd->device->host->this_id), fd->base + REG_SCSI_DATA_NOACK);
+ outb(ICTL_ARB, fd->base + REG_ICTL);
+ /* Start arbitration */
+ outb(ACTL_ARB | ACTL_IRQEN | PARITY_MASK, fd->base + REG_ACTL);
-static DEF_SCSI_QCMD(fdomain_16x0_queue)
+ spin_unlock_irqrestore(sh->host_lock, flags);
-#if DEBUG_ABORT
-static void print_info(struct scsi_cmnd *SCpnt)
-{
- unsigned int imr;
- unsigned int irr;
- unsigned int isr;
-
- if (!SCpnt || !SCpnt->device || !SCpnt->device->host) {
- printk(KERN_WARNING "scsi: <fdomain> Cannot provide detailed information\n");
- return;
- }
-
- printk(KERN_INFO "%s\n", fdomain_16x0_info( SCpnt->device->host ) );
- print_banner(SCpnt->device->host);
- switch (SCpnt->SCp.phase) {
- case in_arbitration: printk("arbitration"); break;
- case in_selection: printk("selection"); break;
- case in_other: printk("other"); break;
- default: printk("unknown"); break;
- }
-
- printk( " (%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
- SCpnt->SCp.phase,
- SCpnt->device->id,
- *(unsigned char *)SCpnt->cmnd,
- scsi_sg_count(SCpnt),
- scsi_bufflen(SCpnt));
- printk( "sent_command = %d, have_data_in = %d, timeout = %d\n",
- SCpnt->SCp.sent_command,
- SCpnt->SCp.have_data_in,
- SCpnt->timeout );
-#if DEBUG_RACE
- printk( "in_interrupt_flag = %d\n", in_interrupt_flag );
-#endif
-
- imr = (inb( 0x0a1 ) << 8) + inb( 0x21 );
- outb( 0x0a, 0xa0 );
- irr = inb( 0xa0 ) << 8;
- outb( 0x0a, 0x20 );
- irr += inb( 0x20 );
- outb( 0x0b, 0xa0 );
- isr = inb( 0xa0 ) << 8;
- outb( 0x0b, 0x20 );
- isr += inb( 0x20 );
-
- /* Print out interesting information */
- printk( "IMR = 0x%04x", imr );
- if (imr & (1 << interrupt_level))
- printk( " (masked)" );
- printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr );
-
- printk( "SCSI Status = 0x%02x\n", inb(port_base + SCSI_Status));
- printk( "TMC Status = 0x%02x", inb(port_base + TMC_Status));
- if (inb((port_base + TMC_Status) & 1))
- printk( " (interrupt)" );
- printk( "\n" );
- printk("Interrupt Status = 0x%02x", inb(port_base + Interrupt_Status));
- if (inb(port_base + Interrupt_Status) & 0x08)
- printk( " (enabled)" );
- printk( "\n" );
- if (chip == tmc18c50 || chip == tmc18c30) {
- printk("FIFO Status = 0x%02x\n", inb(port_base + FIFO_Status));
- printk( "Int. Condition = 0x%02x\n",
- inb( port_base + Interrupt_Cond ) );
- }
- printk( "Configuration 1 = 0x%02x\n", inb( port_base + Configuration1 ) );
- if (chip == tmc18c50 || chip == tmc18c30)
- printk( "Configuration 2 = 0x%02x\n",
- inb( port_base + Configuration2 ) );
+ return 0;
}
-#endif
-static int fdomain_16x0_abort(struct scsi_cmnd *SCpnt)
+static int fdomain_abort(struct scsi_cmnd *cmd)
{
-#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
- printk( "scsi: <fdomain> abort " );
-#endif
-
- if (!in_command) {
-#if EVERY_ACCESS || ERRORS_ONLY
- printk( " (not in command)\n" );
-#endif
- return FAILED;
- } else printk( "\n" );
-
-#if DEBUG_ABORT
- print_info( SCpnt );
-#endif
-
- fdomain_make_bus_idle();
- current_SC->SCp.phase |= aborted;
- current_SC->result = DID_ABORT << 16;
-
- /* Aborts are not done well. . . */
- my_done(DID_ABORT << 16);
- return SUCCESS;
-}
+ struct Scsi_Host *sh = cmd->device->host;
+ struct fdomain *fd = shost_priv(sh);
+ unsigned long flags;
-int fdomain_16x0_bus_reset(struct scsi_cmnd *SCpnt)
-{
- unsigned long flags;
+ if (!fd->cur_cmd)
+ return FAILED;
- local_irq_save(flags);
+ spin_lock_irqsave(sh->host_lock, flags);
- outb(1, port_base + SCSI_Cntl);
- do_pause( 2 );
- outb(0, port_base + SCSI_Cntl);
- do_pause( 115 );
- outb(0, port_base + SCSI_Mode_Cntl);
- outb(PARITY_MASK, port_base + TMC_Cntl);
+ fdomain_make_bus_idle(fd);
+ fdomain_scsi_pointer(fd->cur_cmd)->phase |= aborted;
- local_irq_restore(flags);
- return SUCCESS;
+ /* Aborts are not done well. . . */
+ set_host_byte(fd->cur_cmd, DID_ABORT);
+ fdomain_finish_cmd(fd);
+ spin_unlock_irqrestore(sh->host_lock, flags);
+ return SUCCESS;
}
-static int fdomain_16x0_biosparam(struct scsi_device *sdev,
- struct block_device *bdev,
- sector_t capacity, int *info_array)
+static int fdomain_host_reset(struct scsi_cmnd *cmd)
{
- int drive;
- int size = capacity;
- unsigned long offset;
- struct drive_info {
- unsigned short cylinders;
- unsigned char heads;
- unsigned char sectors;
- } i;
-
- /* NOTES:
- The RAM area starts at 0x1f00 from the bios_base address.
-
- For BIOS Version 2.0:
-
- The drive parameter table seems to start at 0x1f30.
- The first byte's purpose is not known.
- Next is the cylinder, head, and sector information.
- The last 4 bytes appear to be the drive's size in sectors.
- The other bytes in the drive parameter table are unknown.
- If anyone figures them out, please send me mail, and I will
- update these notes.
-
- Tape drives do not get placed in this table.
-
- There is another table at 0x1fea:
- If the byte is 0x01, then the SCSI ID is not in use.
- If the byte is 0x18 or 0x48, then the SCSI ID is in use,
- although tapes don't seem to be in this table. I haven't
- seen any other numbers (in a limited sample).
-
- 0x1f2d is a drive count (i.e., not including tapes)
-
- The table at 0x1fcc are I/O ports addresses for the various
- operations. I calculate these by hand in this driver code.
-
-
-
- For the ISA-200S version of BIOS Version 2.0:
-
- The drive parameter table starts at 0x1f33.
-
- WARNING: Assume that the table entry is 25 bytes long. Someone needs
- to check this for the Quantum ISA-200S card.
-
-
-
- For BIOS Version 3.2:
-
- The drive parameter table starts at 0x1f70. Each entry is
- 0x0a bytes long. Heads are one less than we need to report.
- */
-
- if (MAJOR(bdev->bd_dev) != SCSI_DISK0_MAJOR) {
- printk("scsi: <fdomain> fdomain_16x0_biosparam: too many disks");
- return 0;
- }
- drive = MINOR(bdev->bd_dev) >> 4;
-
- if (bios_major == 2) {
- switch (Quantum) {
- case 2: /* ISA_200S */
- /* The value of 25 has never been verified.
- It should probably be 15. */
- offset = 0x1f33 + drive * 25;
- break;
- case 3: /* ISA_250MG */
- offset = 0x1f36 + drive * 15;
- break;
- case 4: /* ISA_200S (another one) */
- offset = 0x1f34 + drive * 15;
- break;
- default:
- offset = 0x1f31 + drive * 25;
- break;
- }
- memcpy_fromio( &i, bios_mem + offset, sizeof( struct drive_info ) );
- info_array[0] = i.heads;
- info_array[1] = i.sectors;
- info_array[2] = i.cylinders;
- } else if (bios_major == 3
- && bios_minor >= 0
- && bios_minor < 4) { /* 3.0 and 3.2 BIOS */
- memcpy_fromio( &i, bios_mem + 0x1f71 + drive * 10,
- sizeof( struct drive_info ) );
- info_array[0] = i.heads + 1;
- info_array[1] = i.sectors;
- info_array[2] = i.cylinders;
- } else { /* 3.4 BIOS (and up?) */
- /* This algorithm was provided by Future Domain (much thanks!). */
- unsigned char *p = scsi_bios_ptable(bdev);
-
- if (p && p[65] == 0xaa && p[64] == 0x55 /* Partition table valid */
- && p[4]) { /* Partition type */
-
- /* The partition table layout is as follows:
-
- Start: 0x1b3h
- Offset: 0 = partition status
- 1 = starting head
- 2 = starting sector and cylinder (word, encoded)
- 4 = partition type
- 5 = ending head
- 6 = ending sector and cylinder (word, encoded)
- 8 = starting absolute sector (double word)
- c = number of sectors (double word)
- Signature: 0x1fe = 0x55aa
-
- So, this algorithm assumes:
- 1) the first partition table is in use,
- 2) the data in the first entry is correct, and
- 3) partitions never divide cylinders
-
- Note that (1) may be FALSE for NetBSD (and other BSD flavors),
- as well as for Linux. Note also, that Linux doesn't pay any
- attention to the fields that are used by this algorithm -- it
- only uses the absolute sector data. Recent versions of Linux's
- fdisk(1) will fill this data in correctly, and forthcoming
- versions will check for consistency.
-
- Checking for a non-zero partition type is not part of the
- Future Domain algorithm, but it seemed to be a reasonable thing
- to do, especially in the Linux and BSD worlds. */
-
- info_array[0] = p[5] + 1; /* heads */
- info_array[1] = p[6] & 0x3f; /* sectors */
- } else {
-
- /* Note that this new method guarantees that there will always be
- less than 1024 cylinders on a platter. This is good for drives
- up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
-
- if ((unsigned int)size >= 0x7e0000U) {
- info_array[0] = 0xff; /* heads = 255 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else if ((unsigned int)size >= 0x200000U) {
- info_array[0] = 0x80; /* heads = 128 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else {
- info_array[0] = 0x40; /* heads = 64 */
- info_array[1] = 0x20; /* sectors = 32 */
- }
- }
- /* For both methods, compute the cylinders */
- info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] );
- kfree(p);
- }
-
- return 0;
+ struct Scsi_Host *sh = cmd->device->host;
+ struct fdomain *fd = shost_priv(sh);
+ unsigned long flags;
+
+ spin_lock_irqsave(sh->host_lock, flags);
+ fdomain_reset(fd->base);
+ spin_unlock_irqrestore(sh->host_lock, flags);
+ return SUCCESS;
}
-static int fdomain_16x0_release(struct Scsi_Host *shpnt)
+static int fdomain_biosparam(struct scsi_device *sdev,
+ struct gendisk *disk, sector_t capacity,
+ int geom[])
{
- if (shpnt->irq)
- free_irq(shpnt->irq, shpnt);
- if (shpnt->io_port && shpnt->n_io_port)
- release_region(shpnt->io_port, shpnt->n_io_port);
- if (PCI_bus)
- pci_dev_put(PCI_dev);
+ unsigned char *p = scsi_bios_ptable(disk);
+
+ if (p && p[65] == 0xaa && p[64] == 0x55 /* Partition table valid */
+ && p[4]) { /* Partition type */
+ geom[0] = p[5] + 1; /* heads */
+ geom[1] = p[6] & 0x3f; /* sectors */
+ } else {
+ if (capacity >= 0x7e0000) {
+ geom[0] = 255; /* heads */
+ geom[1] = 63; /* sectors */
+ } else if (capacity >= 0x200000) {
+ geom[0] = 128; /* heads */
+ geom[1] = 63; /* sectors */
+ } else {
+ geom[0] = 64; /* heads */
+ geom[1] = 32; /* sectors */
+ }
+ }
+ geom[2] = sector_div(capacity, geom[0] * geom[1]);
+ kfree(p);
+
return 0;
}
-struct scsi_host_template fdomain_driver_template = {
+static const struct scsi_host_template fdomain_template = {
.module = THIS_MODULE,
- .name = "fdomain",
+ .name = "Future Domain TMC-16x0",
.proc_name = "fdomain",
- .detect = fdomain_16x0_detect,
- .info = fdomain_16x0_info,
- .queuecommand = fdomain_16x0_queue,
- .eh_abort_handler = fdomain_16x0_abort,
- .eh_bus_reset_handler = fdomain_16x0_bus_reset,
- .bios_param = fdomain_16x0_biosparam,
- .release = fdomain_16x0_release,
+ .queuecommand = fdomain_queue,
+ .eh_abort_handler = fdomain_abort,
+ .eh_host_reset_handler = fdomain_host_reset,
+ .bios_param = fdomain_biosparam,
.can_queue = 1,
- .this_id = 6,
+ .this_id = 7,
.sg_tablesize = 64,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct scsi_pointer),
};
-#ifndef PCMCIA
-#ifdef CONFIG_PCI
+struct Scsi_Host *fdomain_create(int base, int irq, int this_id,
+ struct device *dev)
+{
+ struct Scsi_Host *sh;
+ struct fdomain *fd;
+ enum chip_type chip;
+ static const char * const chip_names[] = {
+ "Unknown", "TMC-1800", "TMC-18C50", "TMC-18C30"
+ };
+ unsigned long irq_flags = 0;
+
+ chip = fdomain_identify(base);
+ if (!chip)
+ return NULL;
+
+ fdomain_reset(base);
+
+ if (fdomain_test_loopback(base))
+ return NULL;
+
+ if (!irq) {
+ dev_err(dev, "card has no IRQ assigned");
+ return NULL;
+ }
+
+ sh = scsi_host_alloc(&fdomain_template, sizeof(struct fdomain));
+ if (!sh)
+ return NULL;
-static struct pci_device_id fdomain_pci_tbl[] __devinitdata = {
- { PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { }
-};
-MODULE_DEVICE_TABLE(pci, fdomain_pci_tbl);
-#endif
-#define driver_template fdomain_driver_template
-#include "scsi_module.c"
+ if (this_id)
+ sh->this_id = this_id & 0x07;
+
+ sh->irq = irq;
+ sh->io_port = base;
+ sh->n_io_port = FDOMAIN_REGION_SIZE;
+
+ fd = shost_priv(sh);
+ fd->base = base;
+ fd->chip = chip;
+ INIT_WORK(&fd->work, fdomain_work);
+
+ if (dev_is_pci(dev) || !strcmp(dev->bus->name, "pcmcia"))
+ irq_flags = IRQF_SHARED;
+
+ if (request_irq(irq, fdomain_irq, irq_flags, "fdomain", fd))
+ goto fail_put;
+
+ shost_printk(KERN_INFO, sh, "%s chip at 0x%x irq %d SCSI ID %d\n",
+ dev_is_pci(dev) ? "TMC-36C70 (PCI bus)" : chip_names[chip],
+ base, irq, sh->this_id);
+
+ if (scsi_add_host(sh, dev))
+ goto fail_free_irq;
+
+ scsi_scan_host(sh);
+
+ return sh;
-#endif
+fail_free_irq:
+ free_irq(irq, fd);
+fail_put:
+ scsi_host_put(sh);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(fdomain_create);
+
+int fdomain_destroy(struct Scsi_Host *sh)
+{
+ struct fdomain *fd = shost_priv(sh);
+
+ cancel_work_sync(&fd->work);
+ scsi_remove_host(sh);
+ if (sh->irq)
+ free_irq(sh->irq, fd);
+ scsi_host_put(sh);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fdomain_destroy);
+
+#ifdef CONFIG_PM_SLEEP
+static int fdomain_resume(struct device *dev)
+{
+ struct fdomain *fd = shost_priv(dev_get_drvdata(dev));
+
+ fdomain_reset(fd->base);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(fdomain_pm_ops, NULL, fdomain_resume);
+#endif /* CONFIG_PM_SLEEP */
+
+MODULE_AUTHOR("Ondrej Zary, Rickard E. Faith");
+MODULE_DESCRIPTION("Future Domain TMC-16x0/TMC-3260 SCSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h
index 47021d9d4fe4..93afcee207ae 100644
--- a/drivers/scsi/fdomain.h
+++ b/drivers/scsi/fdomain.h
@@ -1,24 +1,114 @@
-/*
- * fdomain.c -- Future Domain TMC-16x0 SCSI driver
- * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- */
+/* SPDX-License-Identifier: GPL-2.0 */
-extern struct scsi_host_template fdomain_driver_template;
-extern int fdomain_setup(char *str);
-extern struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt );
-extern int fdomain_16x0_bus_reset(struct scsi_cmnd *SCpnt);
+#define FDOMAIN_REGION_SIZE 0x10
+#define FDOMAIN_BIOS_SIZE 0x2000
+
+enum {
+ in_arbitration = 0x02,
+ in_selection = 0x04,
+ in_other = 0x08,
+ disconnect = 0x10,
+ aborted = 0x20,
+ sent_ident = 0x40,
+};
+
+/* (@) = not present on TMC1800, (#) = not present on TMC1800 and TMC18C50 */
+#define REG_SCSI_DATA 0 /* R/W: SCSI Data (with ACK) */
+#define REG_BSTAT 1 /* R: SCSI Bus Status */
+#define BSTAT_BSY BIT(0) /* Busy */
+#define BSTAT_MSG BIT(1) /* Message */
+#define BSTAT_IO BIT(2) /* Input/Output */
+#define BSTAT_CMD BIT(3) /* Command/Data */
+#define BSTAT_REQ BIT(4) /* Request and Not Ack */
+#define BSTAT_SEL BIT(5) /* Select */
+#define BSTAT_ACK BIT(6) /* Acknowledge and Request */
+#define BSTAT_ATN BIT(7) /* Attention */
+#define REG_BCTL 1 /* W: SCSI Bus Control */
+#define BCTL_RST BIT(0) /* Bus Reset */
+#define BCTL_SEL BIT(1) /* Select */
+#define BCTL_BSY BIT(2) /* Busy */
+#define BCTL_ATN BIT(3) /* Attention */
+#define BCTL_IO BIT(4) /* Input/Output */
+#define BCTL_CMD BIT(5) /* Command/Data */
+#define BCTL_MSG BIT(6) /* Message */
+#define BCTL_BUSEN BIT(7) /* Enable bus drivers */
+#define REG_ASTAT 2 /* R: Adapter Status 1 */
+#define ASTAT_IRQ BIT(0) /* Interrupt active */
+#define ASTAT_ARB BIT(1) /* Arbitration complete */
+#define ASTAT_PARERR BIT(2) /* Parity error */
+#define ASTAT_RST BIT(3) /* SCSI reset occurred */
+#define ASTAT_FIFODIR BIT(4) /* FIFO direction */
+#define ASTAT_FIFOEN BIT(5) /* FIFO enabled */
+#define ASTAT_PAREN BIT(6) /* Parity enabled */
+#define ASTAT_BUSEN BIT(7) /* Bus drivers enabled */
+#define REG_ICTL 2 /* W: Interrupt Control */
+#define ICTL_FIFO_MASK 0x0f /* FIFO threshold, 1/16 FIFO size */
+#define ICTL_FIFO BIT(4) /* Int. on FIFO count */
+#define ICTL_ARB BIT(5) /* Int. on Arbitration complete */
+#define ICTL_SEL BIT(6) /* Int. on SCSI Select */
+#define ICTL_REQ BIT(7) /* Int. on SCSI Request */
+#define REG_FSTAT 3 /* R: Adapter Status 2 (FIFO) - (@) */
+#define FSTAT_ONOTEMPTY BIT(0) /* Output FIFO not empty */
+#define FSTAT_INOTEMPTY BIT(1) /* Input FIFO not empty */
+#define FSTAT_NOTEMPTY BIT(2) /* Main FIFO not empty */
+#define FSTAT_NOTFULL BIT(3) /* Main FIFO not full */
+#define REG_MCTL 3 /* W: SCSI Data Mode Control */
+#define MCTL_ACK_MASK 0x0f /* Acknowledge period */
+#define MCTL_ACTDEASS BIT(4) /* Active deassert of REQ and ACK */
+#define MCTL_TARGET BIT(5) /* Enable target mode */
+#define MCTL_FASTSYNC BIT(6) /* Enable Fast Synchronous */
+#define MCTL_SYNC BIT(7) /* Enable Synchronous */
+#define REG_INTCOND 4 /* R: Interrupt Condition - (@) */
+#define IRQ_FIFO BIT(1) /* FIFO interrupt */
+#define IRQ_REQ BIT(2) /* SCSI Request interrupt */
+#define IRQ_SEL BIT(3) /* SCSI Select interrupt */
+#define IRQ_ARB BIT(4) /* SCSI Arbitration interrupt */
+#define IRQ_RST BIT(5) /* SCSI Reset interrupt */
+#define IRQ_FORCED BIT(6) /* Forced interrupt */
+#define IRQ_TIMEOUT BIT(7) /* Bus timeout */
+#define REG_ACTL 4 /* W: Adapter Control 1 */
+#define ACTL_RESET BIT(0) /* Reset FIFO, parity, reset int. */
+#define ACTL_FIRQ BIT(1) /* Set Forced interrupt */
+#define ACTL_ARB BIT(2) /* Initiate Bus Arbitration */
+#define ACTL_PAREN BIT(3) /* Enable SCSI Parity */
+#define ACTL_IRQEN BIT(4) /* Enable interrupts */
+#define ACTL_CLRFIRQ BIT(5) /* Clear Forced interrupt */
+#define ACTL_FIFOWR BIT(6) /* FIFO Direction (1=write) */
+#define ACTL_FIFOEN BIT(7) /* Enable FIFO */
+#define REG_ID_LSB 5 /* R: ID Code (LSB) */
+#define REG_ACTL2 5 /* Adapter Control 2 - (@) */
+#define ACTL2_RAMOVRLY BIT(0) /* Enable RAM overlay */
+#define ACTL2_SLEEP BIT(7) /* Sleep mode */
+#define REG_ID_MSB 6 /* R: ID Code (MSB) */
+#define REG_LOOPBACK 7 /* R/W: Loopback */
+#define REG_SCSI_DATA_NOACK 8 /* R/W: SCSI Data (no ACK) */
+#define REG_ASTAT3 9 /* R: Adapter Status 3 */
+#define ASTAT3_ACTDEASS BIT(0) /* Active deassert enabled */
+#define ASTAT3_RAMOVRLY BIT(1) /* RAM overlay enabled */
+#define ASTAT3_TARGERR BIT(2) /* Target error */
+#define ASTAT3_IRQEN BIT(3) /* Interrupts enabled */
+#define ASTAT3_IRQMASK 0xf0 /* Enabled interrupts mask */
+#define REG_CFG1 10 /* R: Configuration Register 1 */
+#define CFG1_BUS BIT(0) /* 0 = ISA */
+#define CFG1_IRQ_MASK 0x0e /* IRQ jumpers */
+#define CFG1_IO_MASK 0x30 /* I/O base jumpers */
+#define CFG1_BIOS_MASK 0xc0 /* BIOS base jumpers */
+#define REG_CFG2 11 /* R/W: Configuration Register 2 (@) */
+#define CFG2_ROMDIS BIT(0) /* ROM disabled */
+#define CFG2_RAMDIS BIT(1) /* RAM disabled */
+#define CFG2_IRQEDGE BIT(2) /* Edge-triggered interrupts */
+#define CFG2_NOWS BIT(3) /* No wait states */
+#define CFG2_32BIT BIT(7) /* 32-bit mode */
+#define REG_FIFO 12 /* R/W: FIFO */
+#define REG_FIFO_COUNT 14 /* R: FIFO Data Count */
+
+#ifdef CONFIG_PM_SLEEP
+static const struct dev_pm_ops __maybe_unused fdomain_pm_ops;
+#define FDOMAIN_PM_OPS (&fdomain_pm_ops)
+#else
+#define FDOMAIN_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+struct Scsi_Host *fdomain_create(int base, int irq, int this_id,
+ struct device *dev);
+int fdomain_destroy(struct Scsi_Host *sh);
diff --git a/drivers/scsi/fdomain_isa.c b/drivers/scsi/fdomain_isa.c
new file mode 100644
index 000000000000..2b4280a43a53
--- /dev/null
+++ b/drivers/scsi/fdomain_isa.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/isa.h>
+#include <scsi/scsi_host.h>
+#include "fdomain.h"
+
+#define MAXBOARDS_PARAM 4
+static int io[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
+module_param_hw_array(io, int, ioport, NULL, 0);
+MODULE_PARM_DESC(io, "base I/O address of controller (0x140, 0x150, 0x160, 0x170)");
+
+static int irq[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
+module_param_hw_array(irq, int, irq, NULL, 0);
+MODULE_PARM_DESC(irq, "IRQ of controller (0=auto [default])");
+
+static int scsi_id[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
+module_param_hw_array(scsi_id, int, other, NULL, 0);
+MODULE_PARM_DESC(scsi_id, "SCSI ID of controller (default = 7)");
+
+static unsigned long addresses[] = {
+ 0xc8000,
+ 0xca000,
+ 0xce000,
+ 0xde000,
+};
+#define ADDRESS_COUNT ARRAY_SIZE(addresses)
+
+static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
+#define PORT_COUNT ARRAY_SIZE(ports)
+
+static unsigned short irqs[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
+
+/* This driver works *ONLY* for Future Domain cards using the TMC-1800,
+ * TMC-18C50, or TMC-18C30 chip. This includes models TMC-1650, 1660, 1670,
+ * and 1680. These are all 16-bit cards.
+ * BIOS versions prior to 3.2 assigned SCSI ID 6 to SCSI adapter.
+ *
+ * The following BIOS signature signatures are for boards which do *NOT*
+ * work with this driver (these TMC-8xx and TMC-9xx boards may work with the
+ * Seagate driver):
+ *
+ * FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
+ * FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
+ * FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
+ * FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
+ * FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
+ * FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
+ * FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
+ *
+ * (The cards which do *NOT* work are all 8-bit cards -- although some of
+ * them have a 16-bit form-factor, the upper 8-bits are used only for IRQs
+ * and are *NOT* used for data. You can tell the difference by following
+ * the tracings on the circuit board -- if only the IRQ lines are involved,
+ * you have a "8-bit" card, and should *NOT* use this driver.)
+ */
+
+static struct signature {
+ const char *signature;
+ int offset;
+ int length;
+ int this_id;
+ int base_offset;
+} signatures[] = {
+/* 1 2 3 4 5 6 */
+/* 123456789012345678901234567890123456789012345678901234567890 */
+{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 6, 0x1fcc },
+{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50, 6, 0x1fcc },
+{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50, 6, 0x1fa2 },
+{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 6, 0x1fa2 },
+{ "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 6, 0x1fa3 },
+{ "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 6, 0 },
+{ "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 7, 0 },
+{ "IBM F1 P2 BIOS v1.0011/09/92", 5, 28, 7, 0x1ff3 },
+{ "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 7, 0 },
+{ "Future Domain Corp. V1.0008/18/93", 5, 33, 7, 0 },
+{ "Future Domain Corp. V2.0108/18/93", 5, 33, 7, 0 },
+{ "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 7, 0 },
+{ "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 7, 0 },
+{ "FUTURE DOMAIN CORP. V3.6008/18/93", 5, 34, 7, 0 },
+{ "FUTURE DOMAIN CORP. V3.6108/18/93", 5, 34, 7, 0 },
+};
+#define SIGNATURE_COUNT ARRAY_SIZE(signatures)
+
+static int fdomain_isa_match(struct device *dev, unsigned int ndev)
+{
+ struct Scsi_Host *sh;
+ int i, base = 0, irq = 0;
+ unsigned long bios_base = 0;
+ struct signature *sig = NULL;
+ void __iomem *p;
+ static struct signature *saved_sig;
+ int this_id = 7;
+
+ if (ndev < ADDRESS_COUNT) { /* scan supported ISA BIOS addresses */
+ p = ioremap(addresses[ndev], FDOMAIN_BIOS_SIZE);
+ if (!p)
+ return 0;
+ for (i = 0; i < SIGNATURE_COUNT; i++)
+ if (check_signature(p + signatures[i].offset,
+ signatures[i].signature,
+ signatures[i].length))
+ break;
+ if (i == SIGNATURE_COUNT) /* no signature found */
+ goto fail_unmap;
+ sig = &signatures[i];
+ bios_base = addresses[ndev];
+ /* read I/O base from BIOS area */
+ if (sig->base_offset)
+ base = readb(p + sig->base_offset) +
+ (readb(p + sig->base_offset + 1) << 8);
+ iounmap(p);
+ if (base) {
+ dev_info(dev, "BIOS at 0x%lx specifies I/O base 0x%x\n",
+ bios_base, base);
+ } else { /* no I/O base in BIOS area */
+ dev_info(dev, "BIOS at 0x%lx\n", bios_base);
+ /* save BIOS signature for later use in port probing */
+ saved_sig = sig;
+ return 0;
+ }
+ } else /* scan supported I/O ports */
+ base = ports[ndev - ADDRESS_COUNT];
+
+ /* use saved BIOS signature if present */
+ if (!sig && saved_sig)
+ sig = saved_sig;
+
+ if (!request_region(base, FDOMAIN_REGION_SIZE, "fdomain_isa"))
+ return 0;
+
+ irq = irqs[(inb(base + REG_CFG1) & CFG1_IRQ_MASK) >> 1];
+
+ if (sig)
+ this_id = sig->this_id;
+
+ sh = fdomain_create(base, irq, this_id, dev);
+ if (!sh) {
+ release_region(base, FDOMAIN_REGION_SIZE);
+ return 0;
+ }
+
+ dev_set_drvdata(dev, sh);
+ return 1;
+fail_unmap:
+ iounmap(p);
+ return 0;
+}
+
+static int fdomain_isa_param_match(struct device *dev, unsigned int ndev)
+{
+ struct Scsi_Host *sh;
+ int irq_ = irq[ndev];
+
+ if (!io[ndev])
+ return 0;
+
+ if (!request_region(io[ndev], FDOMAIN_REGION_SIZE, "fdomain_isa")) {
+ dev_err(dev, "base 0x%x already in use", io[ndev]);
+ return 0;
+ }
+
+ if (irq_ <= 0)
+ irq_ = irqs[(inb(io[ndev] + REG_CFG1) & CFG1_IRQ_MASK) >> 1];
+
+ sh = fdomain_create(io[ndev], irq_, scsi_id[ndev], dev);
+ if (!sh) {
+ dev_err(dev, "controller not found at base 0x%x", io[ndev]);
+ release_region(io[ndev], FDOMAIN_REGION_SIZE);
+ return 0;
+ }
+
+ dev_set_drvdata(dev, sh);
+ return 1;
+}
+
+static void fdomain_isa_remove(struct device *dev, unsigned int ndev)
+{
+ struct Scsi_Host *sh = dev_get_drvdata(dev);
+ int base = sh->io_port;
+
+ fdomain_destroy(sh);
+ release_region(base, FDOMAIN_REGION_SIZE);
+ dev_set_drvdata(dev, NULL);
+}
+
+static struct isa_driver fdomain_isa_driver = {
+ .match = fdomain_isa_match,
+ .remove = fdomain_isa_remove,
+ .driver = {
+ .name = "fdomain_isa",
+ .pm = FDOMAIN_PM_OPS,
+ },
+};
+
+static int __init fdomain_isa_init(void)
+{
+ int isa_probe_count = ADDRESS_COUNT + PORT_COUNT;
+
+ if (io[0]) { /* use module parameters if present */
+ fdomain_isa_driver.match = fdomain_isa_param_match;
+ isa_probe_count = MAXBOARDS_PARAM;
+ }
+
+ return isa_register_driver(&fdomain_isa_driver, isa_probe_count);
+}
+
+static void __exit fdomain_isa_exit(void)
+{
+ isa_unregister_driver(&fdomain_isa_driver);
+}
+
+module_init(fdomain_isa_init);
+module_exit(fdomain_isa_exit);
+
+MODULE_AUTHOR("Ondrej Zary, Rickard E. Faith");
+MODULE_DESCRIPTION("Future Domain TMC-16x0 ISA SCSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/fdomain_pci.c b/drivers/scsi/fdomain_pci.c
new file mode 100644
index 000000000000..c15b2ce76e9f
--- /dev/null
+++ b/drivers/scsi/fdomain_pci.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "fdomain.h"
+
+static int fdomain_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *d)
+{
+ int err;
+ struct Scsi_Host *sh;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto fail;
+
+ err = pci_request_regions(pdev, "fdomain_pci");
+ if (err)
+ goto disable_device;
+
+ err = -ENODEV;
+ if (pci_resource_len(pdev, 0) == 0)
+ goto release_region;
+
+ sh = fdomain_create(pci_resource_start(pdev, 0), pdev->irq, 7,
+ &pdev->dev);
+ if (!sh)
+ goto release_region;
+
+ pci_set_drvdata(pdev, sh);
+ return 0;
+
+release_region:
+ pci_release_regions(pdev);
+disable_device:
+ pci_disable_device(pdev);
+fail:
+ return err;
+}
+
+static void fdomain_pci_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *sh = pci_get_drvdata(pdev);
+
+ fdomain_destroy(sh);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+static const struct pci_device_id fdomain_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70) },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, fdomain_pci_table);
+
+static struct pci_driver fdomain_pci_driver = {
+ .name = "fdomain_pci",
+ .id_table = fdomain_pci_table,
+ .probe = fdomain_pci_probe,
+ .remove = fdomain_pci_remove,
+ .driver.pm = FDOMAIN_PM_OPS,
+};
+
+module_pci_driver(fdomain_pci_driver);
+
+MODULE_AUTHOR("Ondrej Zary, Rickard E. Faith");
+MODULE_DESCRIPTION("Future Domain TMC-3260 PCI SCSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/fnic/Makefile b/drivers/scsi/fnic/Makefile
index 37c3440bc17c..c025e875009e 100644
--- a/drivers/scsi/fnic/Makefile
+++ b/drivers/scsi/fnic/Makefile
@@ -1,15 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FCOE_FNIC) += fnic.o
fnic-y := \
+ fip.o\
fnic_attrs.o \
fnic_isr.o \
fnic_main.o \
fnic_res.o \
fnic_fcs.o \
+ fdls_disc.o \
fnic_scsi.o \
+ fnic_trace.o \
+ fnic_debugfs.o \
vnic_cq.o \
vnic_dev.o \
vnic_intr.o \
vnic_rq.o \
vnic_wq_copy.o \
- vnic_wq.o
+ vnic_wq.o \
+ fnic_pci_subsys_devid.o
diff --git a/drivers/scsi/fnic/cq_desc.h b/drivers/scsi/fnic/cq_desc.h
index d1225cf6320e..0eb4ba277264 100644
--- a/drivers/scsi/fnic/cq_desc.h
+++ b/drivers/scsi/fnic/cq_desc.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _CQ_DESC_H_
#define _CQ_DESC_H_
diff --git a/drivers/scsi/fnic/cq_enet_desc.h b/drivers/scsi/fnic/cq_enet_desc.h
index a9fa26f82ddd..b6113291cf68 100644
--- a/drivers/scsi/fnic/cq_enet_desc.h
+++ b/drivers/scsi/fnic/cq_enet_desc.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _CQ_ENET_DESC_H_
#define _CQ_ENET_DESC_H_
diff --git a/drivers/scsi/fnic/cq_exch_desc.h b/drivers/scsi/fnic/cq_exch_desc.h
index 501660cfe228..4d94329c8ef5 100644
--- a/drivers/scsi/fnic/cq_exch_desc.h
+++ b/drivers/scsi/fnic/cq_exch_desc.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _CQ_EXCH_DESC_H_
#define _CQ_EXCH_DESC_H_
diff --git a/drivers/scsi/fnic/fcpio.h b/drivers/scsi/fnic/fcpio.h
index 12d770d885c5..54a0b2ba8f6f 100644
--- a/drivers/scsi/fnic/fcpio.h
+++ b/drivers/scsi/fnic/fcpio.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _FCPIO_H_
#define _FCPIO_H_
diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
new file mode 100644
index 000000000000..ae37f85f618b
--- /dev/null
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -0,0 +1,5093 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ */
+
+#include <linux/workqueue.h>
+#include "fnic.h"
+#include "fdls_fc.h"
+#include "fnic_fdls.h"
+#include <scsi/fc/fc_fcp.h>
+#include <scsi/scsi_transport_fc.h>
+#include <linux/utsname.h>
+
+#define FC_FC4_TYPE_SCSI 0x08
+#define PORT_SPEED_BIT_8 8
+#define PORT_SPEED_BIT_9 9
+#define PORT_SPEED_BIT_14 14
+#define PORT_SPEED_BIT_15 15
+
+/* FNIC FDMI Register HBA Macros */
+#define FNIC_FDMI_NUM_PORTS 1
+#define FNIC_FDMI_NUM_HBA_ATTRS 9
+#define FNIC_FDMI_TYPE_NODE_NAME 0X1
+#define FNIC_FDMI_TYPE_MANUFACTURER 0X2
+#define FNIC_FDMI_MANUFACTURER "Cisco Systems"
+#define FNIC_FDMI_TYPE_SERIAL_NUMBER 0X3
+#define FNIC_FDMI_TYPE_MODEL 0X4
+#define FNIC_FDMI_TYPE_MODEL_DES 0X5
+#define FNIC_FDMI_MODEL_DESCRIPTION "Cisco Virtual Interface Card"
+#define FNIC_FDMI_TYPE_HARDWARE_VERSION 0X6
+#define FNIC_FDMI_TYPE_DRIVER_VERSION 0X7
+#define FNIC_FDMI_TYPE_ROM_VERSION 0X8
+#define FNIC_FDMI_TYPE_FIRMWARE_VERSION 0X9
+#define FNIC_FDMI_NN_LEN 8
+#define FNIC_FDMI_MANU_LEN 20
+#define FNIC_FDMI_SERIAL_LEN 16
+#define FNIC_FDMI_MODEL_LEN 12
+#define FNIC_FDMI_MODEL_DES_LEN 56
+#define FNIC_FDMI_HW_VER_LEN 16
+#define FNIC_FDMI_DR_VER_LEN 28
+#define FNIC_FDMI_ROM_VER_LEN 8
+#define FNIC_FDMI_FW_VER_LEN 16
+
+/* FNIC FDMI Register PA Macros */
+#define FNIC_FDMI_TYPE_FC4_TYPES 0X1
+#define FNIC_FDMI_TYPE_SUPPORTED_SPEEDS 0X2
+#define FNIC_FDMI_TYPE_CURRENT_SPEED 0X3
+#define FNIC_FDMI_TYPE_MAX_FRAME_SIZE 0X4
+#define FNIC_FDMI_TYPE_OS_NAME 0X5
+#define FNIC_FDMI_TYPE_HOST_NAME 0X6
+#define FNIC_FDMI_NUM_PORT_ATTRS 6
+#define FNIC_FDMI_FC4_LEN 32
+#define FNIC_FDMI_SUPP_SPEED_LEN 4
+#define FNIC_FDMI_CUR_SPEED_LEN 4
+#define FNIC_FDMI_MFS_LEN 4
+#define FNIC_FDMI_MFS 0x800
+#define FNIC_FDMI_OS_NAME_LEN 16
+#define FNIC_FDMI_HN_LEN 24
+
+#define FDLS_FDMI_PLOGI_PENDING 0x1
+#define FDLS_FDMI_REG_HBA_PENDING 0x2
+#define FDLS_FDMI_RPA_PENDING 0x4
+#define FDLS_FDMI_ABORT_PENDING 0x8
+#define FDLS_FDMI_MAX_RETRY 3
+
+#define RETRIES_EXHAUSTED(iport) \
+ (iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)
+
+#define FNIC_TPORT_MAX_NEXUS_RESTART (8)
+
+#define SCHEDULE_OXID_FREE_RETRY_TIME (300)
+
+/* Private Functions */
+static void fdls_fdmi_register_hba(struct fnic_iport_s *iport);
+static void fdls_fdmi_register_pa(struct fnic_iport_s *iport);
+static void fdls_send_rpn_id(struct fnic_iport_s *iport);
+static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr,
+ void *rx_frame);
+static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
+static void fnic_fdls_start_flogi(struct fnic_iport_s *iport);
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+ uint32_t fcid,
+ uint64_t wwpn);
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport);
+static void fdls_start_tport_timer(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, int timeout);
+static void fdls_tport_timer_callback(struct timer_list *t);
+static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport);
+static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
+ int timeout);
+static void fdls_init_plogi_frame(uint8_t *frame, struct fnic_iport_s *iport);
+static void fdls_init_els_acc_frame(uint8_t *frame, struct fnic_iport_s *iport);
+static void fdls_init_els_rjt_frame(uint8_t *frame, struct fnic_iport_s *iport);
+static void fdls_init_logo_frame(uint8_t *frame, struct fnic_iport_s *iport);
+static void fdls_init_fabric_abts_frame(uint8_t *frame,
+ struct fnic_iport_s *iport);
+
+uint8_t *fdls_alloc_frame(struct fnic_iport_s *iport)
+{
+ struct fnic *fnic = iport->fnic;
+ uint8_t *frame = NULL;
+
+ frame = mempool_alloc(fnic->frame_pool, GFP_ATOMIC);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame");
+ return NULL;
+ }
+
+ memset(frame, 0, FNIC_FCOE_FRAME_MAXSZ);
+ return frame;
+}
+
+/**
+ * fdls_alloc_oxid - Allocate an oxid from the bitmap based oxid pool
+ * @iport: Handle to iport instance
+ * @oxid_frame_type: Type of frame to allocate
+ * @active_oxid: the oxid which is in use
+ *
+ * Called with fnic lock held
+ */
+uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type,
+ uint16_t *active_oxid)
+{
+ struct fnic *fnic = iport->fnic;
+ struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
+ int idx;
+ uint16_t oxid;
+
+ lockdep_assert_held(&fnic->fnic_lock);
+
+ /*
+ * Allocate next available oxid from bitmap
+ */
+ idx = find_next_zero_bit(oxid_pool->bitmap, FNIC_OXID_POOL_SZ, oxid_pool->next_idx);
+ if (idx == FNIC_OXID_POOL_SZ) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Alloc oxid: all oxid slots are busy iport state:%d\n",
+ iport->state);
+ return FNIC_UNASSIGNED_OXID;
+ }
+
+ WARN_ON(test_and_set_bit(idx, oxid_pool->bitmap));
+ oxid_pool->next_idx = (idx + 1) % FNIC_OXID_POOL_SZ; /* cycle through the bitmap */
+
+ oxid = FNIC_OXID_ENCODE(idx, oxid_frame_type);
+ *active_oxid = oxid;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "alloc oxid: 0x%x, iport state: %d\n",
+ oxid, iport->state);
+ return oxid;
+}
+
+/**
+ * fdls_free_oxid_idx - Free the oxid using the idx
+ * @iport: Handle to iport instance
+ * @oxid_idx: The index to free
+ *
+ * Free the oxid immediately and make it available for new requests
+ * Called with fnic lock held
+ */
+static void fdls_free_oxid_idx(struct fnic_iport_s *iport, uint16_t oxid_idx)
+{
+ struct fnic *fnic = iport->fnic;
+ struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
+
+ lockdep_assert_held(&fnic->fnic_lock);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "free oxid idx: 0x%x\n", oxid_idx);
+
+ WARN_ON(!test_and_clear_bit(oxid_idx, oxid_pool->bitmap));
+}
+
+/**
+ * fdls_reclaim_oxid_handler - Callback handler for delayed_oxid_work
+ * @work: Handle to work_struct
+ *
+ * Scheduled when an oxid is to be freed later
+ * After freeing expired oxid(s), the handler schedules
+ * another callback with the remaining time
+ * of next unexpired entry in the reclaim list.
+ */
+void fdls_reclaim_oxid_handler(struct work_struct *work)
+{
+ struct fnic_oxid_pool_s *oxid_pool = container_of(work,
+ struct fnic_oxid_pool_s, oxid_reclaim_work.work);
+ struct fnic_iport_s *iport = container_of(oxid_pool,
+ struct fnic_iport_s, oxid_pool);
+ struct fnic *fnic = iport->fnic;
+ struct reclaim_entry_s *reclaim_entry, *next;
+ unsigned long delay_j, cur_jiffies;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Reclaim oxid callback\n");
+
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+ /* Though the work was scheduled for one entry,
+ * walk through and free the expired entries which might have been scheduled
+ * at around the same time as the first entry
+ */
+ list_for_each_entry_safe(reclaim_entry, next,
+ &(oxid_pool->oxid_reclaim_list), links) {
+
+ /* The list is always maintained in the order of expiry time */
+ cur_jiffies = jiffies;
+ if (time_before(cur_jiffies, reclaim_entry->expires))
+ break;
+
+ list_del(&reclaim_entry->links);
+ fdls_free_oxid_idx(iport, reclaim_entry->oxid_idx);
+ kfree(reclaim_entry);
+ }
+
+ /* schedule to free up the next entry */
+ if (!list_empty(&oxid_pool->oxid_reclaim_list)) {
+ reclaim_entry = list_first_entry(&oxid_pool->oxid_reclaim_list,
+ struct reclaim_entry_s, links);
+
+ delay_j = reclaim_entry->expires - cur_jiffies;
+ schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Scheduling next callback at:%ld jiffies\n", delay_j);
+ }
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+/**
+ * fdls_free_oxid - Helper function to free the oxid
+ * @iport: Handle to iport instance
+ * @oxid: oxid to free
+ * @active_oxid: the oxid which is in use
+ *
+ * Called with fnic lock held
+ */
+void fdls_free_oxid(struct fnic_iport_s *iport,
+ uint16_t oxid, uint16_t *active_oxid)
+{
+ fdls_free_oxid_idx(iport, FNIC_OXID_IDX(oxid));
+ *active_oxid = FNIC_UNASSIGNED_OXID;
+}
+
+/**
+ * fdls_schedule_oxid_free - Schedule oxid to be freed later
+ * @iport: Handle to iport instance
+ * @active_oxid: the oxid which is in use
+ *
+ * Gets called in a rare case scenario when both a command
+ * (fdls or target discovery) timed out and the following ABTS
+ * timed out as well, without a link change.
+ *
+ * Called with fnic lock held
+ */
+void fdls_schedule_oxid_free(struct fnic_iport_s *iport, uint16_t *active_oxid)
+{
+ struct fnic *fnic = iport->fnic;
+ struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
+ struct reclaim_entry_s *reclaim_entry;
+ unsigned long delay_j = msecs_to_jiffies(OXID_RECLAIM_TOV(iport));
+ int oxid_idx = FNIC_OXID_IDX(*active_oxid);
+
+ lockdep_assert_held(&fnic->fnic_lock);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Schedule oxid free. oxid: 0x%x\n", *active_oxid);
+
+ *active_oxid = FNIC_UNASSIGNED_OXID;
+
+ reclaim_entry = (struct reclaim_entry_s *)
+ kzalloc(sizeof(struct reclaim_entry_s), GFP_ATOMIC);
+
+ if (!reclaim_entry) {
+ FNIC_FCS_DBG(KERN_WARNING, fnic->host, fnic->fnic_num,
+ "Failed to allocate memory for reclaim struct for oxid idx: %d\n",
+ oxid_idx);
+
+ /* Retry the scheduling */
+ WARN_ON(test_and_set_bit(oxid_idx, oxid_pool->pending_schedule_free));
+ schedule_delayed_work(&oxid_pool->schedule_oxid_free_retry, 0);
+ return;
+ }
+
+ reclaim_entry->oxid_idx = oxid_idx;
+ reclaim_entry->expires = round_jiffies(jiffies + delay_j);
+
+ list_add_tail(&reclaim_entry->links, &oxid_pool->oxid_reclaim_list);
+
+ schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
+}
+
+/**
+ * fdls_schedule_oxid_free_retry_work - Thread to schedule the
+ * oxid to be freed later
+ *
+ * @work: Handle to the work struct
+ */
+void fdls_schedule_oxid_free_retry_work(struct work_struct *work)
+{
+ struct fnic_oxid_pool_s *oxid_pool = container_of(work,
+ struct fnic_oxid_pool_s, schedule_oxid_free_retry.work);
+ struct fnic_iport_s *iport = container_of(oxid_pool,
+ struct fnic_iport_s, oxid_pool);
+ struct fnic *fnic = iport->fnic;
+ struct reclaim_entry_s *reclaim_entry;
+ unsigned long delay_j = msecs_to_jiffies(OXID_RECLAIM_TOV(iport));
+ unsigned long flags;
+ int idx;
+
+ for_each_set_bit(idx, oxid_pool->pending_schedule_free, FNIC_OXID_POOL_SZ) {
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Schedule oxid free. oxid idx: %d\n", idx);
+
+ reclaim_entry = kzalloc(sizeof(*reclaim_entry), GFP_KERNEL);
+ if (!reclaim_entry) {
+ schedule_delayed_work(&oxid_pool->schedule_oxid_free_retry,
+ msecs_to_jiffies(SCHEDULE_OXID_FREE_RETRY_TIME));
+ return;
+ }
+
+ clear_bit(idx, oxid_pool->pending_schedule_free);
+ reclaim_entry->oxid_idx = idx;
+ reclaim_entry->expires = round_jiffies(jiffies + delay_j);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_add_tail(&reclaim_entry->links, &oxid_pool->oxid_reclaim_list);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
+ }
+}
+
+static bool fdls_is_oxid_fabric_req(uint16_t oxid)
+{
+ int oxid_frame_type = FNIC_FRAME_TYPE(oxid);
+
+ switch (oxid_frame_type) {
+ case FNIC_FRAME_TYPE_FABRIC_FLOGI:
+ case FNIC_FRAME_TYPE_FABRIC_PLOGI:
+ case FNIC_FRAME_TYPE_FABRIC_RPN:
+ case FNIC_FRAME_TYPE_FABRIC_RFT:
+ case FNIC_FRAME_TYPE_FABRIC_RFF:
+ case FNIC_FRAME_TYPE_FABRIC_GPN_FT:
+ case FNIC_FRAME_TYPE_FABRIC_LOGO:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool fdls_is_oxid_fdmi_req(uint16_t oxid)
+{
+ int oxid_frame_type = FNIC_FRAME_TYPE(oxid);
+
+ switch (oxid_frame_type) {
+ case FNIC_FRAME_TYPE_FDMI_PLOGI:
+ case FNIC_FRAME_TYPE_FDMI_RHBA:
+ case FNIC_FRAME_TYPE_FDMI_RPA:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool fdls_is_oxid_tgt_req(uint16_t oxid)
+{
+ int oxid_frame_type = FNIC_FRAME_TYPE(oxid);
+
+ switch (oxid_frame_type) {
+ case FNIC_FRAME_TYPE_TGT_PLOGI:
+ case FNIC_FRAME_TYPE_TGT_PRLI:
+ case FNIC_FRAME_TYPE_TGT_ADISC:
+ case FNIC_FRAME_TYPE_TGT_LOGO:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static void fdls_reset_oxid_pool(struct fnic_iport_s *iport)
+{
+ struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
+
+ oxid_pool->next_idx = 0;
+}
+
+void fnic_del_fabric_timer_sync(struct fnic *fnic)
+{
+ fnic->iport.fabric.del_timer_inprogress = 1;
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ timer_delete_sync(&fnic->iport.fabric.retry_timer);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ fnic->iport.fabric.del_timer_inprogress = 0;
+}
+
+void fnic_del_tport_timer_sync(struct fnic *fnic,
+ struct fnic_tport_s *tport)
+{
+ tport->del_timer_inprogress = 1;
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ timer_delete_sync(&tport->retry_timer);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ tport->del_timer_inprogress = 0;
+}
+
+static void
+fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
+{
+ u64 fabric_tov;
+ struct fnic *fnic = iport->fnic;
+
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport fcid: 0x%x: Canceling fabric disc timer\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync(fnic);
+ iport->fabric.timer_pending = 0;
+ }
+
+ if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+ iport->fabric.retry_counter++;
+
+ fabric_tov = jiffies + msecs_to_jiffies(timeout);
+ mod_timer(&iport->fabric.retry_timer, round_jiffies(fabric_tov));
+ iport->fabric.timer_pending = 1;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fabric timer is %d ", timeout);
+}
+
+static void
+fdls_start_tport_timer(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, int timeout)
+{
+ u64 fabric_tov;
+ struct fnic *fnic = iport->fnic;
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync(fnic, tport);
+ tport->timer_pending = 0;
+ }
+
+ if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED))
+ tport->retry_counter++;
+
+ fabric_tov = jiffies + msecs_to_jiffies(timeout);
+ mod_timer(&tport->retry_timer, round_jiffies(fabric_tov));
+ tport->timer_pending = 1;
+}
+
+void fdls_init_plogi_frame(uint8_t *frame,
+ struct fnic_iport_s *iport)
+{
+ struct fc_std_flogi *pplogi;
+ uint8_t s_id[3];
+
+ pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pplogi = (struct fc_std_flogi) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_d_id = {0xFF, 0xFF, 0xFC},
+ .fh_type = FC_TYPE_ELS, .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .els = {
+ .fl_cmd = ELS_PLOGI,
+ .fl_csp = {.sp_hi_ver = FNIC_FC_PH_VER_HI,
+ .sp_lo_ver = FNIC_FC_PH_VER_LO,
+ .sp_bb_cred = cpu_to_be16(FNIC_FC_B2B_CREDIT),
+ .sp_features = cpu_to_be16(FC_SP_FT_CIRO),
+ .sp_bb_data = cpu_to_be16(FNIC_FC_B2B_RDF_SZ),
+ .sp_tot_seq = cpu_to_be16(FNIC_FC_CONCUR_SEQS),
+ .sp_rel_off = cpu_to_be16(FNIC_FC_RO_INFO),
+ .sp_e_d_tov = cpu_to_be32(FC_DEF_E_D_TOV)},
+ .fl_cssp[2].cp_class = cpu_to_be16(FC_CPC_VALID | FC_CPC_SEQ),
+ .fl_cssp[2].cp_rdfs = cpu_to_be16(0x800),
+ .fl_cssp[2].cp_con_seq = cpu_to_be16(0xFF),
+ .fl_cssp[2].cp_open_seq = 1}
+ };
+
+ FNIC_STD_SET_NPORT_NAME(&pplogi->els.fl_wwpn, iport->wwpn);
+ FNIC_STD_SET_NODE_NAME(&pplogi->els.fl_wwnn, iport->wwnn);
+ FNIC_LOGI_SET_RDF_SIZE(pplogi->els, iport->max_payload_size);
+
+ hton24(s_id, iport->fcid);
+ FNIC_STD_SET_S_ID(pplogi->fchdr, s_id);
+}
+
+static void fdls_init_els_acc_frame(uint8_t *frame,
+ struct fnic_iport_s *iport)
+{
+ struct fc_std_els_acc_rsp *pels_acc;
+ uint8_t s_id[3];
+
+ pels_acc = (struct fc_std_els_acc_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pels_acc = (struct fc_std_els_acc_rsp) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REP,
+ .fh_type = FC_TYPE_ELS, .fh_f_ctl = {FNIC_ELS_REP_FCTL, 0, 0}},
+ .acc.la_cmd = ELS_LS_ACC,
+ };
+
+ hton24(s_id, iport->fcid);
+ FNIC_STD_SET_S_ID(pels_acc->fchdr, s_id);
+ FNIC_STD_SET_RX_ID(pels_acc->fchdr, FNIC_UNASSIGNED_RXID);
+}
+
+static void fdls_init_els_rjt_frame(uint8_t *frame,
+ struct fnic_iport_s *iport)
+{
+ struct fc_std_els_rjt_rsp *pels_rjt;
+
+ pels_rjt = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pels_rjt = (struct fc_std_els_rjt_rsp) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REP, .fh_type = FC_TYPE_ELS,
+ .fh_f_ctl = {FNIC_ELS_REP_FCTL, 0, 0}},
+ .rej.er_cmd = ELS_LS_RJT,
+ };
+
+ FNIC_STD_SET_RX_ID(pels_rjt->fchdr, FNIC_UNASSIGNED_RXID);
+}
+
+static void fdls_init_logo_frame(uint8_t *frame,
+ struct fnic_iport_s *iport)
+{
+ struct fc_std_logo *plogo;
+ uint8_t s_id[3];
+
+ plogo = (struct fc_std_logo *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *plogo = (struct fc_std_logo) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}},
+ .els.fl_cmd = ELS_LOGO,
+ };
+
+ hton24(s_id, iport->fcid);
+ FNIC_STD_SET_S_ID(plogo->fchdr, s_id);
+ memcpy(plogo->els.fl_n_port_id, s_id, 3);
+
+ FNIC_STD_SET_NPORT_NAME(&plogo->els.fl_n_port_wwn,
+ iport->wwpn);
+}
+
+static void fdls_init_fabric_abts_frame(uint8_t *frame,
+ struct fnic_iport_s *iport)
+{
+ struct fc_frame_header *pfabric_abts;
+
+ pfabric_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pfabric_abts = (struct fc_frame_header) {
+ .fh_r_ctl = FC_RCTL_BA_ABTS, /* ABTS */
+ .fh_s_id = {0x00, 0x00, 0x00},
+ .fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS,
+ .fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00,
+ .fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000,
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID),
+ .fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */
+ };
+}
+
+static void
+fdls_send_rscn_resp(struct fnic_iport_s *iport,
+ struct fc_frame_header *rscn_fchdr)
+{
+ uint8_t *frame;
+ struct fc_std_els_acc_rsp *pels_acc;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_acc_rsp);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send RSCN response");
+ return;
+ }
+
+ pels_acc = (struct fc_std_els_acc_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_els_acc_frame(frame, iport);
+
+ FNIC_STD_SET_D_ID(pels_acc->fchdr, rscn_fchdr->fh_s_id);
+
+ oxid = FNIC_STD_GET_OX_ID(rscn_fchdr);
+ FNIC_STD_SET_OX_ID(pels_acc->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send RSCN response with oxid: 0x%x",
+ iport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+}
+
+static void
+fdls_send_logo_resp(struct fnic_iport_s *iport,
+ struct fc_frame_header *req_fchdr)
+{
+ uint8_t *frame;
+ struct fc_std_els_acc_rsp *plogo_resp;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_acc_rsp);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send LOGO response");
+ return;
+ }
+
+ plogo_resp = (struct fc_std_els_acc_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_els_acc_frame(frame, iport);
+
+ FNIC_STD_SET_D_ID(plogo_resp->fchdr, req_fchdr->fh_s_id);
+
+ oxid = FNIC_STD_GET_OX_ID(req_fchdr);
+ FNIC_STD_SET_OX_ID(plogo_resp->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send LOGO response with oxid: 0x%x",
+ iport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+}
+
+void
+fdls_send_tport_abts(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport)
+{
+ uint8_t *frame;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fnic *fnic = iport->fnic;
+ struct fc_frame_header *ptport_abts;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_frame_header);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send tport ABTS");
+ return;
+ }
+
+ ptport_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *ptport_abts = (struct fc_frame_header) {
+ .fh_r_ctl = FC_RCTL_BA_ABTS, /* ABTS */
+ .fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS,
+ .fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00,
+ .fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000,
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID),
+ .fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */
+ };
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+ FNIC_STD_SET_S_ID(*ptport_abts, s_id);
+ FNIC_STD_SET_D_ID(*ptport_abts, d_id);
+ tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ FNIC_STD_SET_OX_ID(*ptport_abts, tport->active_oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send tport abts: tport->state: %d ",
+ iport->fcid, tport->state);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fnic *fnic = iport->fnic;
+ struct fc_frame_header *pfabric_abts;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_frame_header);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send fabric ABTS");
+ return;
+ }
+
+ pfabric_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_fabric_abts_frame(frame, iport);
+
+ hton24(s_id, iport->fcid);
+
+ switch (iport->fabric.state) {
+ case FDLS_STATE_FABRIC_LOGO:
+ hton24(d_id, FC_FID_FLOGI);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+
+ case FDLS_STATE_FABRIC_FLOGI:
+ hton24(d_id, FC_FID_FLOGI);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+
+ case FDLS_STATE_FABRIC_PLOGI:
+ FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
+ hton24(d_id, FC_FID_DIR_SERV);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+
+ case FDLS_STATE_RPN_ID:
+ FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
+ hton24(d_id, FC_FID_DIR_SERV);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+
+ case FDLS_STATE_SCR:
+ FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
+ hton24(d_id, FC_FID_FCTRL);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+
+ case FDLS_STATE_REGISTER_FC4_TYPES:
+ FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
+ hton24(d_id, FC_FID_DIR_SERV);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+
+ case FDLS_STATE_REGISTER_FC4_FEATURES:
+ FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
+ hton24(d_id, FC_FID_DIR_SERV);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+
+ case FDLS_STATE_GPN_FT:
+ FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
+ hton24(d_id, FC_FID_DIR_SERV);
+ FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
+ break;
+ default:
+ return;
+ }
+
+ oxid = iport->active_oxid_fabric_req;
+ FNIC_STD_SET_OX_ID(*pfabric_abts, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send fabric abts. iport->fabric.state: %d oxid: 0x%x",
+ iport->fcid, iport->fabric.state, oxid);
+
+ iport->fabric.flags |= FNIC_FDLS_FABRIC_ABORT_ISSUED;
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+ iport->fabric.timer_pending = 1;
+}
+
+static uint8_t *fdls_alloc_init_fdmi_abts_frame(struct fnic_iport_s *iport,
+ uint16_t oxid)
+{
+ struct fc_frame_header *pfdmi_abts;
+ uint8_t d_id[3];
+ uint8_t *frame;
+ struct fnic *fnic = iport->fnic;
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send FDMI ABTS");
+ return NULL;
+ }
+
+ pfdmi_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_fabric_abts_frame(frame, iport);
+
+ hton24(d_id, FC_FID_MGMT_SERV);
+ FNIC_STD_SET_D_ID(*pfdmi_abts, d_id);
+ FNIC_STD_SET_OX_ID(*pfdmi_abts, oxid);
+
+ return frame;
+}
+
+static void fdls_send_fdmi_abts(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fnic *fnic = iport->fnic;
+ unsigned long fdmi_tov;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_frame_header);
+
+ if (iport->fabric.fdmi_pending & FDLS_FDMI_PLOGI_PENDING) {
+ frame = fdls_alloc_init_fdmi_abts_frame(iport,
+ iport->active_oxid_fdmi_plogi);
+ if (frame == NULL)
+ return;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send FDMI PLOGI abts. iport->fabric.state: %d oxid: 0x%x",
+ iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_plogi);
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ } else {
+ if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING) {
+ frame = fdls_alloc_init_fdmi_abts_frame(iport,
+ iport->active_oxid_fdmi_rhba);
+ if (frame == NULL)
+ return;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send FDMI RHBA abts. iport->fabric.state: %d oxid: 0x%x",
+ iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rhba);
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ }
+ if (iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING) {
+ frame = fdls_alloc_init_fdmi_abts_frame(iport,
+ iport->active_oxid_fdmi_rpa);
+ if (frame == NULL) {
+ if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING)
+ goto arm_timer;
+ else
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send FDMI RPA abts. iport->fabric.state: %d oxid: 0x%x",
+ iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rpa);
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ }
+ }
+
+arm_timer:
+ fdmi_tov = jiffies + msecs_to_jiffies(2 * iport->e_d_tov);
+ mod_timer(&iport->fabric.fdmi_timer, round_jiffies(fdmi_tov));
+ iport->fabric.fdmi_pending |= FDLS_FDMI_ABORT_PENDING;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: iport->fabric.fdmi_pending: 0x%x",
+ iport->fcid, iport->fabric.fdmi_pending);
+}
+
+static void fdls_send_fabric_flogi(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_flogi *pflogi;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_flogi);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send FLOGI");
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ pflogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pflogi = (struct fc_std_flogi) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_d_id = {0xFF, 0xFF, 0xFE},
+ .fh_type = FC_TYPE_ELS, .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .els.fl_cmd = ELS_FLOGI,
+ .els.fl_csp = {.sp_hi_ver = FNIC_FC_PH_VER_HI,
+ .sp_lo_ver = FNIC_FC_PH_VER_LO,
+ .sp_bb_cred = cpu_to_be16(FNIC_FC_B2B_CREDIT),
+ .sp_bb_data = cpu_to_be16(FNIC_FC_B2B_RDF_SZ)},
+ .els.fl_cssp[2].cp_class = cpu_to_be16(FC_CPC_VALID | FC_CPC_SEQ)
+ };
+
+ FNIC_STD_SET_NPORT_NAME(&pflogi->els.fl_wwpn, iport->wwpn);
+ FNIC_STD_SET_NODE_NAME(&pflogi->els.fl_wwnn, iport->wwnn);
+ FNIC_LOGI_SET_RDF_SIZE(pflogi->els, iport->max_payload_size);
+ FNIC_LOGI_SET_R_A_TOV(pflogi->els, iport->r_a_tov);
+ FNIC_LOGI_SET_E_D_TOV(pflogi->els, iport->e_d_tov);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_FLOGI,
+ &iport->active_oxid_fabric_req);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send FLOGI",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(pflogi->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send fabric FLOGI with oxid: 0x%x", iport->fcid,
+ oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ atomic64_inc(&iport->iport_stats.fabric_flogi_sent);
+err_out:
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+}
+
+static void fdls_send_fabric_plogi(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_flogi *pplogi;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_flogi);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send PLOGI");
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_plogi_frame(frame, iport);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_PLOGI,
+ &iport->active_oxid_fabric_req);
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send fabric PLOGI",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send fabric PLOGI with oxid: 0x%x", iport->fcid,
+ oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ atomic64_inc(&iport->iport_stats.fabric_plogi_sent);
+
+err_out:
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+}
+
+static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_flogi *pplogi;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_flogi);
+ uint8_t d_id[3];
+ u64 fdmi_tov;
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send FDMI PLOGI");
+ goto err_out;
+ }
+
+ pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_plogi_frame(frame, iport);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FDMI_PLOGI,
+ &iport->active_oxid_fdmi_plogi);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send FDMI PLOGI",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid);
+
+ hton24(d_id, FC_FID_MGMT_SERV);
+ FNIC_STD_SET_D_ID(pplogi->fchdr, d_id);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send FDMI PLOGI with oxid: 0x%x",
+ iport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+err_out:
+ fdmi_tov = jiffies + msecs_to_jiffies(2 * iport->e_d_tov);
+ mod_timer(&iport->fabric.fdmi_timer, round_jiffies(fdmi_tov));
+ iport->fabric.fdmi_pending = FDLS_FDMI_PLOGI_PENDING;
+}
+
+static void fdls_send_rpn_id(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_rpn_id *prpn_id;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_rpn_id);
+ uint8_t fcid[3];
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send RPN_ID");
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ prpn_id = (struct fc_std_rpn_id *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *prpn_id = (struct fc_std_rpn_id) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
+ .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
+ .ct_fs_subtype = FC_NS_SUBTYPE,
+ .ct_cmd = cpu_to_be16(FC_NS_RPN_ID)}
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(prpn_id->fchdr, fcid);
+
+ FNIC_STD_SET_PORT_ID(prpn_id->rpn_id, fcid);
+ FNIC_STD_SET_PORT_NAME(prpn_id->rpn_id, iport->wwpn);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_RPN,
+ &iport->active_oxid_fabric_req);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send RPN_ID",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(prpn_id->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send RPN ID with oxid: 0x%x", iport->fcid,
+ oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+err_out:
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+}
+
+static void fdls_send_scr(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_scr *pscr;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_scr);
+ uint8_t fcid[3];
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send SCR");
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ pscr = (struct fc_std_scr *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pscr = (struct fc_std_scr) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ,
+ .fh_d_id = {0xFF, 0xFF, 0xFD}, .fh_type = FC_TYPE_ELS,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .scr = {.scr_cmd = ELS_SCR,
+ .scr_reg_func = ELS_SCRF_FULL}
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(pscr->fchdr, fcid);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_SCR,
+ &iport->active_oxid_fabric_req);
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send SCR",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(pscr->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send SCR with oxid: 0x%x", iport->fcid,
+ oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ atomic64_inc(&iport->iport_stats.fabric_scr_sent);
+
+err_out:
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+}
+
+static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
+{
+ uint8_t *frame;
+ struct fc_std_gpn_ft *pgpn_ft;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_gpn_ft);
+ uint8_t fcid[3];
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send GPN FT");
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ pgpn_ft = (struct fc_std_gpn_ft *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pgpn_ft = (struct fc_std_gpn_ft) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
+ .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
+ .ct_fs_subtype = FC_NS_SUBTYPE,
+ .ct_cmd = cpu_to_be16(FC_NS_GPN_FT)},
+ .gpn_ft.fn_fc4_type = 0x08
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(pgpn_ft->fchdr, fcid);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_GPN_FT,
+ &iport->active_oxid_fabric_req);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send GPN FT",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(pgpn_ft->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send GPN FT with oxid: 0x%x", iport->fcid,
+ oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+err_out:
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+ fdls_set_state((&iport->fabric), fdls_state);
+}
+
+static void
+fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ uint8_t *frame;
+ struct fc_std_els_adisc *padisc;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_adisc);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send TGT ADISC");
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ padisc = (struct fc_std_els_adisc *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+ memcpy(padisc->els.adisc_port_id, s_id, 3);
+ FNIC_STD_SET_S_ID(padisc->fchdr, s_id);
+ FNIC_STD_SET_D_ID(padisc->fchdr, d_id);
+
+ FNIC_STD_SET_F_CTL(padisc->fchdr, FNIC_ELS_REQ_FCTL << 16);
+ FNIC_STD_SET_R_CTL(padisc->fchdr, FC_RCTL_ELS_REQ);
+ FNIC_STD_SET_TYPE(padisc->fchdr, FC_TYPE_ELS);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_ADISC, &tport->active_oxid);
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send TGT ADISC",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(padisc->fchdr, oxid);
+ FNIC_STD_SET_RX_ID(padisc->fchdr, FNIC_UNASSIGNED_RXID);
+
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ FNIC_STD_SET_NPORT_NAME(&padisc->els.adisc_wwpn,
+ iport->wwpn);
+ FNIC_STD_SET_NODE_NAME(&padisc->els.adisc_wwnn,
+ iport->wwnn);
+
+ padisc->els.adisc_cmd = ELS_ADISC;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send ADISC to tgt fcid: 0x%x",
+ iport->fcid, tport->fcid);
+
+ atomic64_inc(&iport->iport_stats.tport_adisc_sent);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+err_out:
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
+}
+
+bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ struct fnic_tport_event_s *tport_del_evt;
+ struct fnic *fnic = iport->fnic;
+
+ if ((tport->state == FDLS_TGT_STATE_OFFLINING)
+ || (tport->state == FDLS_TGT_STATE_OFFLINE)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport fcid 0x%x: tport state is offlining/offline\n",
+ tport->fcid);
+ return false;
+ }
+
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
+ /*
+ * By setting this flag, the tport will not be seen in a look-up
+ * in an RSCN. Even if we move to multithreaded model, this tport
+ * will be destroyed and a new RSCN will have to create a new one
+ */
+ tport->flags |= FNIC_FDLS_TPORT_TERMINATING;
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync(fnic, tport);
+ tport->timer_pending = 0;
+ }
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ fnic_rport_exch_reset(iport->fnic, tport->fcid);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+ if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) {
+ tport_del_evt =
+ kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+ if (!tport_del_evt) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Failed to allocate memory for tport fcid: 0x%0x\n",
+ tport->fcid);
+ return false;
+ }
+ tport_del_evt->event = TGT_EV_RPORT_DEL;
+ tport_del_evt->arg1 = (void *) tport;
+ list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+ queue_work(fnic_event_queue, &fnic->tport_work);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport 0x%x not reg with scsi_transport. Freeing locally",
+ tport->fcid);
+ list_del(&tport->links);
+ kfree(tport);
+ }
+ return true;
+}
+
+static void
+fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ uint8_t *frame;
+ struct fc_std_flogi *pplogi;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_flogi);
+ uint8_t d_id[3];
+ uint32_t timeout;
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send TGT PLOGI");
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_plogi_frame(frame, iport);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PLOGI, &tport->active_oxid);
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
+ iport->fcid, tport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+ FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid);
+
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ hton24(d_id, tport->fcid);
+ FNIC_STD_SET_D_ID(pplogi->fchdr, d_id);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send tgt PLOGI to tgt: 0x%x with oxid: 0x%x",
+ iport->fcid, tport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ atomic64_inc(&iport->iport_stats.tport_plogi_sent);
+
+err_out:
+ timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, timeout);
+}
+
+static uint16_t
+fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
+ struct fc_std_flogi *plogi_rsp)
+{
+ uint16_t b2b_rdf_size =
+ be16_to_cpu(FNIC_LOGI_RDF_SIZE(plogi_rsp->els));
+ uint16_t spc3_rdf_size =
+ be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & FNIC_FC_C3_RDF;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
+ b2b_rdf_size, spc3_rdf_size);
+
+ return min(b2b_rdf_size, spc3_rdf_size);
+}
+
+static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_rft_id *prft_id;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_rft_id);
+ uint8_t fcid[3];
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send RFT");
+ return;
+ }
+
+ prft_id = (struct fc_std_rft_id *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *prft_id = (struct fc_std_rft_id) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
+ .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
+ .ct_fs_subtype = FC_NS_SUBTYPE,
+ .ct_cmd = cpu_to_be16(FC_NS_RFT_ID)}
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(prft_id->fchdr, fcid);
+ FNIC_STD_SET_PORT_ID(prft_id->rft_id, fcid);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_RFT,
+ &iport->active_oxid_fabric_req);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send RFT",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ return;
+ }
+ FNIC_STD_SET_OX_ID(prft_id->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send RFT with oxid: 0x%x", iport->fcid,
+ oxid);
+
+ prft_id->rft_id.fr_fts.ff_type_map[0] =
+ cpu_to_be32(1 << FC_TYPE_FCP);
+
+ prft_id->rft_id.fr_fts.ff_type_map[1] =
+ cpu_to_be32(1 << (FC_TYPE_CT % FC_NS_BPW));
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+}
+
+static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_rff_id *prff_id;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_rff_id);
+ uint8_t fcid[3];
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send RFF");
+ return;
+ }
+
+ prff_id = (struct fc_std_rff_id *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *prff_id = (struct fc_std_rff_id) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
+ .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
+ .ct_fs_subtype = FC_NS_SUBTYPE,
+ .ct_cmd = cpu_to_be16(FC_NS_RFF_ID)},
+ .rff_id.fr_feat = 0x2,
+ .rff_id.fr_type = FC_TYPE_FCP
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(prff_id->fchdr, fcid);
+ FNIC_STD_SET_PORT_ID(prff_id->rff_id, fcid);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_RFF,
+ &iport->active_oxid_fabric_req);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send RFF",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ return;
+ }
+ FNIC_STD_SET_OX_ID(prff_id->fchdr, oxid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send RFF with oxid: 0x%x", iport->fcid,
+ oxid);
+
+ prff_id->rff_id.fr_type = FC_TYPE_FCP;
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+}
+
+static void
+fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ uint8_t *frame;
+ struct fc_std_els_prli *pprli;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_prli);
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ uint32_t timeout;
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send TGT PRLI");
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ pprli = (struct fc_std_els_prli *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pprli = (struct fc_std_els_prli) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .els_prli = {.prli_cmd = ELS_PRLI,
+ .prli_spp_len = 16,
+ .prli_len = cpu_to_be16(0x14)},
+ .sp = {.spp_type = 0x08, .spp_flags = 0x0020,
+ .spp_params = cpu_to_be32(0xA2)}
+ };
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PRLI, &tport->active_oxid);
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send TGT PRLI to 0x%x",
+ iport->fcid, tport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ goto err_out;
+ }
+
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+
+ hton24(s_id, iport->fcid);
+ hton24(d_id, tport->fcid);
+
+ FNIC_STD_SET_OX_ID(pprli->fchdr, oxid);
+ FNIC_STD_SET_S_ID(pprli->fchdr, s_id);
+ FNIC_STD_SET_D_ID(pprli->fchdr, d_id);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send PRLI to tgt: 0x%x with oxid: 0x%x",
+ iport->fcid, tport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ atomic64_inc(&iport->iport_stats.tport_prli_sent);
+
+err_out:
+ timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
+ /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
+ fdls_start_tport_timer(iport, tport, timeout);
+}
+
+/**
+ * fdls_send_fabric_logo - Send flogo to the fcf
+ * @iport: Handle to fnic iport
+ *
+ * This function does not change or check the fabric state.
+ * It the caller's responsibility to set the appropriate iport fabric
+ * state when this is called. Normally it is FDLS_STATE_FABRIC_LOGO.
+ * Currently this assumes to be called with fnic lock held.
+ */
+void fdls_send_fabric_logo(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_logo *plogo;
+ struct fnic *fnic = iport->fnic;
+ uint8_t d_id[3];
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_logo);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send fabric LOGO");
+ return;
+ }
+
+ plogo = (struct fc_std_logo *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_logo_frame(frame, iport);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_LOGO,
+ &iport->active_oxid_fabric_req);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send fabric LOGO",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ return;
+ }
+ FNIC_STD_SET_OX_ID(plogo->fchdr, oxid);
+
+ hton24(d_id, FC_FID_FLOGI);
+ FNIC_STD_SET_D_ID(plogo->fchdr, d_id);
+
+ iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send fabric LOGO with oxid: 0x%x",
+ iport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+ fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
+}
+
+/**
+ * fdls_tgt_logout - Send plogo to the remote port
+ * @iport: Handle to fnic iport
+ * @tport: Handle to remote port
+ *
+ * This function does not change or check the fabric/tport state.
+ * It the caller's responsibility to set the appropriate tport/fabric
+ * state when this is called. Normally that is fdls_tgt_state_plogo.
+ * This could be used to send plogo to nameserver process
+ * also not just target processes
+ */
+void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+ uint8_t *frame;
+ struct fc_std_logo *plogo;
+ struct fnic *fnic = iport->fnic;
+ uint8_t d_id[3];
+ uint16_t oxid;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_logo);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send fabric LOGO");
+ return;
+ }
+
+ plogo = (struct fc_std_logo *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_logo_frame(frame, iport);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_LOGO, &tport->active_oxid);
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send tgt LOGO",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ return;
+ }
+ FNIC_STD_SET_OX_ID(plogo->fchdr, oxid);
+
+ hton24(d_id, tport->fcid);
+ FNIC_STD_SET_D_ID(plogo->fchdr, d_id);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send tgt LOGO with oxid: 0x%x",
+ iport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+
+ atomic64_inc(&iport->iport_stats.tport_logo_sent);
+}
+
+static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
+{
+ struct fnic_tport_s *tport, *next;
+ u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Starting FDLS target discovery", iport->fcid);
+
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+ if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+ || (iport->state != FNIC_IPORT_STATE_READY)) {
+ break;
+ }
+ /* if we marked the tport as deleted due to GPN_FT
+ * We should not send ADISC anymore
+ */
+ if ((tport->state == FDLS_TGT_STATE_OFFLINING) ||
+ (tport->state == FDLS_TGT_STATE_OFFLINE))
+ continue;
+
+ /* For tports which have received RSCN */
+ if (tport->flags & FNIC_FDLS_TPORT_SEND_ADISC) {
+ tport->retry_counter = 0;
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_ADISC);
+ tport->flags &= ~FNIC_FDLS_TPORT_SEND_ADISC;
+ fdls_send_tgt_adisc(iport, tport);
+ continue;
+ }
+ if (fdls_get_tport_state(tport) != FDLS_TGT_STATE_INIT) {
+ /* Not a new port, skip */
+ continue;
+ }
+ tport->retry_counter = 0;
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+ fdls_send_tgt_plogi(iport, tport);
+ }
+ fdls_set_state((&iport->fabric), FDLS_STATE_TGT_DISCOVERY);
+}
+
+/*
+ * Function to restart the IT nexus if we received any out of
+ * sequence PLOGI/PRLI response from the target.
+ * The memory for the new tport structure is allocated
+ * inside fdls_create_tport and added to the iport's tport list.
+ * This will get freed later during tport_offline/linkdown
+ * or module unload. The new_tport pointer will go out of scope
+ * safely since the memory it is
+ * pointing to it will be freed later
+ */
+static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
+{
+ struct fnic_iport_s *iport = tport->iport;
+ struct fnic_tport_s *new_tport = NULL;
+ uint32_t fcid;
+ uint64_t wwpn;
+ int nexus_restart_count;
+ struct fnic *fnic = iport->fnic;
+ bool retval = true;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport fcid: 0x%x state: %d restart_count: %d",
+ tport->fcid, tport->state, tport->nexus_restart_count);
+
+ fcid = tport->fcid;
+ wwpn = tport->wwpn;
+ nexus_restart_count = tport->nexus_restart_count;
+
+ retval = fdls_delete_tport(iport, tport);
+ if (retval != true) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Error deleting tport: 0x%x", fcid);
+ return;
+ }
+
+ if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Exceeded nexus restart retries tport: 0x%x",
+ fcid);
+ return;
+ }
+
+ /*
+ * Allocate memory for the new tport and add it to
+ * iport's tport list.
+ * This memory will be freed during tport_offline/linkdown
+ * or module unload. The pointer new_tport is safe to go
+ * out of scope when this function returns, since the memory
+ * it is pointing to is guaranteed to be freed later
+ * as mentioned above.
+ */
+ new_tport = fdls_create_tport(iport, fcid, wwpn);
+ if (!new_tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Error creating new tport: 0x%x", fcid);
+ return;
+ }
+
+ new_tport->nexus_restart_count = nexus_restart_count + 1;
+ fdls_send_tgt_plogi(iport, new_tport);
+ fdls_set_tport_state(new_tport, FDLS_TGT_STATE_PLOGI);
+}
+
+struct fnic_tport_s *fnic_find_tport_by_fcid(struct fnic_iport_s *iport,
+ uint32_t fcid)
+{
+ struct fnic_tport_s *tport, *next;
+
+ list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+ if ((tport->fcid == fcid)
+ && !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+ return tport;
+ }
+ return NULL;
+}
+
+static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
+ uint32_t fcid, uint64_t wwpn)
+{
+ struct fnic_tport_s *tport;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);
+
+ tport = kzalloc(sizeof(struct fnic_tport_s), GFP_ATOMIC);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Memory allocation failure while creating tport: 0x%x\n",
+ fcid);
+ return NULL;
+ }
+
+ tport->max_payload_size = FNIC_FCOE_MAX_FRAME_SZ;
+ tport->r_a_tov = FC_DEF_R_A_TOV;
+ tport->e_d_tov = FC_DEF_E_D_TOV;
+ tport->fcid = fcid;
+ tport->wwpn = wwpn;
+ tport->iport = iport;
+
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Need to setup tport timer callback");
+
+ timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Added tport 0x%x", tport->fcid);
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
+ list_add_tail(&tport->links, &iport->tport_list);
+ atomic_set(&tport->in_flight, 0);
+ return tport;
+}
+
+struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
+ uint64_t wwpn)
+{
+ struct fnic_tport_s *tport, *next;
+
+ list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
+ if ((tport->wwpn == wwpn)
+ && !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
+ return tport;
+ }
+ return NULL;
+}
+
+static void
+fnic_fdmi_attr_set(void *attr_start, u16 type, u16 len,
+ void *data, u32 *off)
+{
+ u16 size = len + FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ struct fc_fdmi_attr_entry *fdmi_attr = (struct fc_fdmi_attr_entry *)
+ ((u8 *)attr_start + *off);
+
+ put_unaligned_be16(type, &fdmi_attr->type);
+ put_unaligned_be16(size, &fdmi_attr->len);
+ memcpy(fdmi_attr->value, data, len);
+ *off += size;
+}
+
+static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_fdmi_rhba *prhba;
+ struct fc_fdmi_attr_entry *fdmi_attr;
+ uint8_t fcid[3];
+ int err;
+ struct fnic *fnic = iport->fnic;
+ struct vnic_devcmd_fw_info *fw_info = NULL;
+ uint16_t oxid;
+ u32 attr_off_bytes, len;
+ u8 data[64];
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET;
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send FDMI RHBA");
+ return;
+ }
+
+ prhba = (struct fc_std_fdmi_rhba *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *prhba = (struct fc_std_fdmi_rhba) {
+ .fchdr = {
+ .fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
+ .fh_d_id = {0xFF, 0XFF, 0XFA},
+ .fh_type = FC_TYPE_CT,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)
+ },
+ .fc_std_ct_hdr = {
+ .ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_MGMT,
+ .ct_fs_subtype = FC_FDMI_SUBTYPE,
+ .ct_cmd = cpu_to_be16(FC_FDMI_RHBA)
+ },
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(prhba->fchdr, fcid);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FDMI_RHBA,
+ &iport->active_oxid_fdmi_rhba);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send FDMI RHBA",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ return;
+ }
+ FNIC_STD_SET_OX_ID(prhba->fchdr, oxid);
+
+ put_unaligned_be64(iport->wwpn, &prhba->rhba.hbaid.id);
+ put_unaligned_be32(FNIC_FDMI_NUM_PORTS, &prhba->rhba.port.numport);
+ put_unaligned_be64(iport->wwpn, &prhba->rhba.port.port[0].portname);
+ put_unaligned_be32(FNIC_FDMI_NUM_HBA_ATTRS,
+ &prhba->rhba.hba_attrs.numattrs);
+
+ fdmi_attr = prhba->rhba.hba_attrs.attr;
+ attr_off_bytes = 0;
+
+ put_unaligned_be64(iport->wwnn, data);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_NODE_NAME,
+ FNIC_FDMI_NN_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "NN set, off=%d", attr_off_bytes);
+
+ strscpy_pad(data, FNIC_FDMI_MANUFACTURER, FNIC_FDMI_MANU_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MANUFACTURER,
+ FNIC_FDMI_MANU_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "MFG set <%s>, off=%d", data, attr_off_bytes);
+
+ err = vnic_dev_fw_info(fnic->vdev, &fw_info);
+ if (!err) {
+ strscpy_pad(data, fw_info->hw_serial_number,
+ FNIC_FDMI_SERIAL_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_SERIAL_NUMBER,
+ FNIC_FDMI_SERIAL_LEN, data, &attr_off_bytes);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "SERIAL set <%s>, off=%d", data, attr_off_bytes);
+
+ }
+
+ if (fnic->subsys_desc_len >= FNIC_FDMI_MODEL_LEN)
+ fnic->subsys_desc_len = FNIC_FDMI_MODEL_LEN - 1;
+ strscpy_pad(data, fnic->subsys_desc, FNIC_FDMI_MODEL_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL, FNIC_FDMI_MODEL_LEN,
+ data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "MODEL set <%s>, off=%d", data, attr_off_bytes);
+
+ strscpy_pad(data, FNIC_FDMI_MODEL_DESCRIPTION, FNIC_FDMI_MODEL_DES_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL_DES,
+ FNIC_FDMI_MODEL_DES_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "MODEL_DESC set <%s>, off=%d", data, attr_off_bytes);
+
+ if (!err) {
+ strscpy_pad(data, fw_info->hw_version, FNIC_FDMI_HW_VER_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HARDWARE_VERSION,
+ FNIC_FDMI_HW_VER_LEN, data, &attr_off_bytes);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "HW_VER set <%s>, off=%d", data, attr_off_bytes);
+
+ }
+
+ strscpy_pad(data, DRV_VERSION, FNIC_FDMI_DR_VER_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_DRIVER_VERSION,
+ FNIC_FDMI_DR_VER_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "DRV_VER set <%s>, off=%d", data, attr_off_bytes);
+
+ strscpy_pad(data, "N/A", FNIC_FDMI_ROM_VER_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_ROM_VERSION,
+ FNIC_FDMI_ROM_VER_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ROM_VER set <%s>, off=%d", data, attr_off_bytes);
+
+ if (!err) {
+ strscpy_pad(data, fw_info->fw_version, FNIC_FDMI_FW_VER_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FIRMWARE_VERSION,
+ FNIC_FDMI_FW_VER_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FW_VER set <%s>, off=%d", data, attr_off_bytes);
+ }
+
+ len = sizeof(struct fc_std_fdmi_rhba) + attr_off_bytes;
+ frame_size += len;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send FDMI RHBA with oxid: 0x%x fs: %d", iport->fcid,
+ oxid, frame_size);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ iport->fabric.fdmi_pending |= FDLS_FDMI_REG_HBA_PENDING;
+}
+
+static void fdls_fdmi_register_pa(struct fnic_iport_s *iport)
+{
+ uint8_t *frame;
+ struct fc_std_fdmi_rpa *prpa;
+ struct fc_fdmi_attr_entry *fdmi_attr;
+ uint8_t fcid[3];
+ struct fnic *fnic = iport->fnic;
+ u32 port_speed_bm;
+ u32 port_speed = vnic_dev_port_speed(fnic->vdev);
+ uint16_t oxid;
+ u32 attr_off_bytes, len;
+ u8 tmp_data[16], data[64];
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET;
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send FDMI RPA");
+ return;
+ }
+
+ prpa = (struct fc_std_fdmi_rpa *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *prpa = (struct fc_std_fdmi_rpa) {
+ .fchdr = {
+ .fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
+ .fh_d_id = {0xFF, 0xFF, 0xFA},
+ .fh_type = FC_TYPE_CT,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)
+ },
+ .fc_std_ct_hdr = {
+ .ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_MGMT,
+ .ct_fs_subtype = FC_FDMI_SUBTYPE,
+ .ct_cmd = cpu_to_be16(FC_FDMI_RPA)
+ },
+ };
+
+ hton24(fcid, iport->fcid);
+ FNIC_STD_SET_S_ID(prpa->fchdr, fcid);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FDMI_RPA,
+ &iport->active_oxid_fdmi_rpa);
+
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate OXID to send FDMI RPA",
+ iport->fcid);
+ mempool_free(frame, fnic->frame_pool);
+ return;
+ }
+ FNIC_STD_SET_OX_ID(prpa->fchdr, oxid);
+
+ put_unaligned_be64(iport->wwpn, &prpa->rpa.port.portname);
+ put_unaligned_be32(FNIC_FDMI_NUM_PORT_ATTRS,
+ &prpa->rpa.hba_attrs.numattrs);
+
+ /* MDS does not support GIGE speed.
+ * Bit shift standard definitions from scsi_transport_fc.h to
+ * match FC spec.
+ */
+ switch (port_speed) {
+ case DCEM_PORTSPEED_10G:
+ case DCEM_PORTSPEED_20G:
+ /* There is no bit for 20G */
+ port_speed_bm = FC_PORTSPEED_10GBIT << PORT_SPEED_BIT_14;
+ break;
+ case DCEM_PORTSPEED_25G:
+ port_speed_bm = FC_PORTSPEED_25GBIT << PORT_SPEED_BIT_8;
+ break;
+ case DCEM_PORTSPEED_40G:
+ case DCEM_PORTSPEED_4x10G:
+ port_speed_bm = FC_PORTSPEED_40GBIT << PORT_SPEED_BIT_9;
+ break;
+ case DCEM_PORTSPEED_100G:
+ port_speed_bm = FC_PORTSPEED_100GBIT << PORT_SPEED_BIT_8;
+ break;
+ default:
+ port_speed_bm = FC_PORTSPEED_1GBIT << PORT_SPEED_BIT_15;
+ break;
+ }
+ attr_off_bytes = 0;
+
+ fdmi_attr = prpa->rpa.hba_attrs.attr;
+
+ put_unaligned_be64(iport->wwnn, data);
+
+ memset(data, 0, FNIC_FDMI_FC4_LEN);
+ data[2] = 1;
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FC4_TYPES,
+ FNIC_FDMI_FC4_LEN, data, &attr_off_bytes);
+
+ put_unaligned_be32(port_speed_bm, data);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_SUPPORTED_SPEEDS,
+ FNIC_FDMI_SUPP_SPEED_LEN, data, &attr_off_bytes);
+
+ put_unaligned_be32(port_speed_bm, data);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_CURRENT_SPEED,
+ FNIC_FDMI_CUR_SPEED_LEN, data, &attr_off_bytes);
+
+ put_unaligned_be32(FNIC_FDMI_MFS, data);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MAX_FRAME_SIZE,
+ FNIC_FDMI_MFS_LEN, data, &attr_off_bytes);
+
+ snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "host%d",
+ fnic->host->host_no);
+ strscpy_pad(data, tmp_data, FNIC_FDMI_OS_NAME_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_OS_NAME,
+ FNIC_FDMI_OS_NAME_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "OS name set <%s>, off=%d", data, attr_off_bytes);
+
+ sprintf(fc_host_system_hostname(fnic->host), "%s", utsname()->nodename);
+ strscpy_pad(data, fc_host_system_hostname(fnic->host),
+ FNIC_FDMI_HN_LEN);
+ fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HOST_NAME,
+ FNIC_FDMI_HN_LEN, data, &attr_off_bytes);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Host name set <%s>, off=%d", data, attr_off_bytes);
+
+ len = sizeof(struct fc_std_fdmi_rpa) + attr_off_bytes;
+ frame_size += len;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send FDMI RPA with oxid: 0x%x fs: %d", iport->fcid,
+ oxid, frame_size);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+ iport->fabric.fdmi_pending |= FDLS_FDMI_RPA_PENDING;
+}
+
+void fdls_fabric_timer_callback(struct timer_list *t)
+{
+ struct fnic_fdls_fabric_s *fabric = timer_container_of(fabric, t,
+ retry_timer);
+ struct fnic_iport_s *iport =
+ container_of(fabric, struct fnic_iport_s, fabric);
+ struct fnic *fnic = iport->fnic;
+ unsigned long flags;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tp: %d fab state: %d fab retry counter: %d max_flogi_retries: %d",
+ iport->fabric.timer_pending, iport->fabric.state,
+ iport->fabric.retry_counter, iport->max_flogi_retries);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ if (!iport->fabric.timer_pending) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ if (iport->fabric.del_timer_inprogress) {
+ iport->fabric.del_timer_inprogress = 0;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fabric_del_timer inprogress(%d). Skip timer cb",
+ iport->fabric.del_timer_inprogress);
+ return;
+ }
+
+ iport->fabric.timer_pending = 0;
+
+ /* The fabric state indicates which frames have time out, and we retry */
+ switch (iport->fabric.state) {
+ case FDLS_STATE_FABRIC_FLOGI:
+ /* Flogi received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < iport->max_flogi_retries)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_fabric_flogi(iport);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ /* Flogi has time out 2*ed_tov send abts */
+ fdls_send_fabric_abts(iport);
+ } else {
+ /* ABTS has timed out
+ * Mark the OXID to be freed after 2 * r_a_tov and retry the req
+ */
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ if (iport->fabric.retry_counter < iport->max_flogi_retries) {
+ iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+ fdls_send_fabric_flogi(iport);
+ } else
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Exceeded max FLOGI retries");
+ }
+ break;
+ case FDLS_STATE_FABRIC_PLOGI:
+ /* Plogi received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < iport->max_plogi_retries)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_fabric_plogi(iport);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ /* Plogi has timed out 2*ed_tov send abts */
+ fdls_send_fabric_abts(iport);
+ } else {
+ /* ABTS has timed out
+ * Mark the OXID to be freed after 2 * r_a_tov and retry the req
+ */
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ if (iport->fabric.retry_counter < iport->max_plogi_retries) {
+ iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+ fdls_send_fabric_plogi(iport);
+ } else
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Exceeded max PLOGI retries");
+ }
+ break;
+ case FDLS_STATE_RPN_ID:
+ /* Rpn_id received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_rpn_id(iport);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+ /* RPN has timed out. Send abts */
+ fdls_send_fabric_abts(iport);
+ else {
+ /* ABTS has timed out */
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FDLS_STATE_SCR:
+ /* scr received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_scr(iport);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+ /* scr has timed out. Send abts */
+ fdls_send_fabric_abts(iport);
+ else {
+ /* ABTS has timed out */
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ABTS timed out. Starting PLOGI: %p", iport);
+ fnic_fdls_start_plogi(iport);
+ }
+ break;
+ case FDLS_STATE_REGISTER_FC4_TYPES:
+ /* scr received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_register_fc4_types(iport);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ /* RFT_ID timed out send abts */
+ fdls_send_fabric_abts(iport);
+ } else {
+ /* ABTS has timed out */
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ABTS timed out. Starting PLOGI: %p", iport);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FDLS_STATE_REGISTER_FC4_FEATURES:
+ /* scr received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_register_fc4_features(iport);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
+ /* SCR has timed out. Send abts */
+ fdls_send_fabric_abts(iport);
+ else {
+ /* ABTS has timed out */
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ABTS timed out. Starting PLOGI %p", iport);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FDLS_STATE_RSCN_GPN_FT:
+ case FDLS_STATE_SEND_GPNFT:
+ case FDLS_STATE_GPN_FT:
+ /* GPN_FT received a LS_RJT with busy we retry from here */
+ if ((iport->fabric.flags & FNIC_FDLS_RETRY_FRAME)
+ && (iport->fabric.retry_counter < FDLS_RETRY_COUNT)) {
+ iport->fabric.flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_gpn_ft(iport, iport->fabric.state);
+ } else if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ /* gpn_ft has timed out. Send abts */
+ fdls_send_fabric_abts(iport);
+ } else {
+ /* ABTS has timed out */
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
+ fdls_send_gpn_ft(iport, iport->fabric.state);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ABTS timeout for fabric GPN_FT. Check name server: %p",
+ iport);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+void fdls_fdmi_retry_plogi(struct fnic_iport_s *iport)
+{
+ struct fnic *fnic = iport->fnic;
+
+ iport->fabric.fdmi_pending = 0;
+ /* If max retries not exhausted, start over from fdmi plogi */
+ if (iport->fabric.fdmi_retry < FDLS_FDMI_MAX_RETRY) {
+ iport->fabric.fdmi_retry++;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Retry FDMI PLOGI. FDMI retry: %d",
+ iport->fabric.fdmi_retry);
+ fdls_send_fdmi_plogi(iport);
+ }
+}
+
+void fdls_fdmi_timer_callback(struct timer_list *t)
+{
+ struct fnic_fdls_fabric_s *fabric = timer_container_of(fabric, t,
+ fdmi_timer);
+ struct fnic_iport_s *iport =
+ container_of(fabric, struct fnic_iport_s, fabric);
+ struct fnic *fnic = iport->fnic;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
+
+ if (!iport->fabric.fdmi_pending) {
+ /* timer expired after fdmi responses received. */
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
+
+ /* if not abort pending, send an abort */
+ if (!(iport->fabric.fdmi_pending & FDLS_FDMI_ABORT_PENDING)) {
+ fdls_send_fdmi_abts(iport);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
+
+ /* ABTS pending for an active fdmi request that is pending.
+ * That means FDMI ABTS timed out
+ * Schedule to free the OXID after 2*r_a_tov and proceed
+ */
+ if (iport->fabric.fdmi_pending & FDLS_FDMI_PLOGI_PENDING) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDMI PLOGI ABTS timed out. Schedule oxid free: 0x%x\n",
+ iport->active_oxid_fdmi_plogi);
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_plogi);
+ } else {
+ if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDMI RHBA ABTS timed out. Schedule oxid free: 0x%x\n",
+ iport->active_oxid_fdmi_rhba);
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rhba);
+ }
+ if (iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDMI RPA ABTS timed out. Schedule oxid free: 0x%x\n",
+ iport->active_oxid_fdmi_rpa);
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rpa);
+ }
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
+
+ fdls_fdmi_retry_plogi(iport);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
+{
+ struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+ struct fnic *fnic = iport->fnic;
+ struct fnic_tport_event_s *tport_del_evt;
+
+ tport_del_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+ if (!tport_del_evt) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Failed to allocate memory for tport event fcid: 0x%x",
+ tport->fcid);
+ return;
+ }
+ tport_del_evt->event = TGT_EV_TPORT_DELETE;
+ tport_del_evt->arg1 = (void *) tport;
+ list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
+ queue_work(fnic_event_queue, &fnic->tport_work);
+}
+
+static void fdls_tport_timer_callback(struct timer_list *t)
+{
+ struct fnic_tport_s *tport = timer_container_of(tport, t, retry_timer);
+ struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (!tport->timer_pending) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+
+ if (tport->del_timer_inprogress) {
+ tport->del_timer_inprogress = 0;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n",
+ tport->fcid);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d",
+ tport->fcid, tport->timer_pending, tport->state,
+ tport->retry_counter);
+
+ tport->timer_pending = 0;
+ oxid = tport->active_oxid;
+
+ /* We retry plogi/prli/adisc frames depending on the tport state */
+ switch (tport->state) {
+ case FDLS_TGT_STATE_PLOGI:
+ /* PLOGI frame received a LS_RJT with busy, we retry from here */
+ if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+ && (tport->retry_counter < iport->max_plogi_retries)) {
+ tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_tgt_plogi(iport, tport);
+ } else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ /* Plogi frame has timed out, send abts */
+ fdls_send_tport_abts(iport, tport);
+ } else if (tport->retry_counter < iport->max_plogi_retries) {
+ /*
+ * ABTS has timed out
+ */
+ fdls_schedule_oxid_free(iport, &tport->active_oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ } else {
+ /* exceeded plogi retry count */
+ fdls_schedule_oxid_free(iport, &tport->active_oxid);
+ fdls_send_delete_tport_msg(tport);
+ }
+ break;
+ case FDLS_TGT_STATE_PRLI:
+ /* PRLI received a LS_RJT with busy , hence we retry from here */
+ if ((tport->flags & FNIC_FDLS_RETRY_FRAME)
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ tport->flags &= ~FNIC_FDLS_RETRY_FRAME;
+ fdls_send_tgt_prli(iport, tport);
+ } else if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ /* PRLI has time out, send abts */
+ fdls_send_tport_abts(iport, tport);
+ } else {
+ /* ABTS has timed out for prli, we go back to PLOGI */
+ fdls_schedule_oxid_free(iport, &tport->active_oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+ }
+ break;
+ case FDLS_TGT_STATE_ADISC:
+ /* ADISC timed out send an ABTS */
+ if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ fdls_send_tport_abts(iport, tport);
+ } else if ((tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ /*
+ * ABTS has timed out
+ */
+ fdls_schedule_oxid_free(iport, &tport->active_oxid);
+ fdls_send_tgt_adisc(iport, tport);
+ } else {
+ /* exceeded retry count */
+ fdls_schedule_oxid_free(iport, &tport->active_oxid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ADISC not responding. Deleting target port: 0x%x",
+ tport->fcid);
+ fdls_send_delete_tport_msg(tport);
+ }
+ break;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "oxid: 0x%x Unknown tport state: 0x%x", oxid, tport->state);
+ break;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+static void fnic_fdls_start_flogi(struct fnic_iport_s *iport)
+{
+ iport->fabric.retry_counter = 0;
+ fdls_send_fabric_flogi(iport);
+ fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_FLOGI);
+ iport->fabric.flags = 0;
+}
+
+static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
+{
+ iport->fabric.retry_counter = 0;
+ fdls_send_fabric_plogi(iport);
+ fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
+ iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+
+ if ((fnic_fdmi_support == 1) && (!(iport->flags & FNIC_FDMI_ACTIVE))) {
+ /* we can do FDMI at the same time */
+ iport->fabric.fdmi_retry = 0;
+ timer_setup(&iport->fabric.fdmi_timer, fdls_fdmi_timer_callback,
+ 0);
+ fdls_send_fdmi_plogi(iport);
+ iport->flags |= FNIC_FDMI_ACTIVE;
+ }
+}
+static void
+fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint64_t frame_wwnn;
+ uint64_t frame_wwpn;
+ uint16_t oxid;
+ struct fc_std_els_adisc *adisc_rsp = (struct fc_std_els_adisc *)fchdr;
+ struct fc_std_els_rjt_rsp *els_rjt = (struct fc_std_els_rjt_rsp *)fchdr;
+ struct fnic *fnic = iport->fnic;
+
+ fcid = FNIC_STD_GET_S_ID(fchdr);
+ tgt_fcid = ntoh24(fcid);
+ tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Tgt ADISC response tport not found: 0x%x", tgt_fcid);
+ return;
+ }
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ || (tport->state != FDLS_TGT_STATE_ADISC)
+ || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping this ADISC response");
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport state: %d tport state: %d Is abort issued on PRLI? %d",
+ iport->state, tport->state,
+ (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED));
+ return;
+ }
+ if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping frame from target: 0x%x",
+ tgt_fcid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery");
+ return;
+ }
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+
+ switch (adisc_rsp->els.adisc_cmd) {
+ case ELS_LS_ACC:
+ atomic64_inc(&iport->iport_stats.tport_adisc_ls_accepts);
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport 0x%p Canceling fabric disc timer\n",
+ tport);
+ fnic_del_tport_timer_sync(fnic, tport);
+ }
+ tport->timer_pending = 0;
+ tport->retry_counter = 0;
+ frame_wwnn = get_unaligned_be64(&adisc_rsp->els.adisc_wwnn);
+ frame_wwpn = get_unaligned_be64(&adisc_rsp->els.adisc_wwpn);
+ if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ADISC accepted from target: 0x%x. Target logged in",
+ tgt_fcid);
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Error mismatch frame: ADISC");
+ }
+ break;
+
+ case ELS_LS_RJT:
+ atomic64_inc(&iport->iport_stats.tport_adisc_ls_rejects);
+ if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ADISC ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
+ tgt_fcid);
+
+ /* Retry ADISC again from the timer routine. */
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ADISC returned ELS_LS_RJT from target: 0x%x",
+ tgt_fcid);
+ fdls_delete_tport(iport, tport);
+ }
+ break;
+ }
+}
+static void
+fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint16_t oxid;
+ struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *)fchdr;
+ struct fc_std_els_rjt_rsp *els_rjt = (struct fc_std_els_rjt_rsp *)fchdr;
+ uint16_t max_payload_size;
+ struct fnic *fnic = iport->fnic;
+
+ fcid = FNIC_STD_GET_S_ID(fchdr);
+ tgt_fcid = ntoh24(fcid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS processing target PLOGI response: tgt_fcid: 0x%x",
+ tgt_fcid);
+
+ tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport not found: 0x%x", tgt_fcid);
+ return;
+ }
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping frame! iport state: %d tport state: %d",
+ iport->state, tport->state);
+ return;
+ }
+
+ if (tport->state != FDLS_TGT_STATE_PLOGI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI rsp recvd in wrong state. Drop the frame and restart nexus");
+ fdls_target_restart_nexus(tport);
+ return;
+ }
+
+ if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI response from target: 0x%x. Dropping frame",
+ tgt_fcid);
+ return;
+ }
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+
+ switch (plogi_rsp->els.fl_cmd) {
+ case ELS_LS_ACC:
+ atomic64_inc(&iport->iport_stats.tport_plogi_ls_accepts);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI accepted by target: 0x%x", tgt_fcid);
+ break;
+
+ case ELS_LS_RJT:
+ atomic64_inc(&iport->iport_stats.tport_plogi_ls_rejects);
+ if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
+ && (tport->retry_counter < iport->max_plogi_retries)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
+ tgt_fcid);
+ /* Retry plogi again from the timer routine. */
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ return;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI returned ELS_LS_RJT from target: 0x%x",
+ tgt_fcid);
+ fdls_delete_tport(iport, tport);
+ return;
+
+ default:
+ atomic64_inc(&iport->iport_stats.tport_plogi_misc_rejects);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI not accepted from target fcid: 0x%x",
+ tgt_fcid);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Found the PLOGI target: 0x%x and state: %d",
+ (unsigned int) tgt_fcid, tport->state);
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync(fnic, tport);
+ }
+
+ tport->timer_pending = 0;
+ tport->wwpn = get_unaligned_be64(&FNIC_LOGI_PORT_NAME(plogi_rsp->els));
+ tport->wwnn = get_unaligned_be64(&FNIC_LOGI_NODE_NAME(plogi_rsp->els));
+
+ /* Learn the Service Params */
+
+ /* Max frame size - choose the lowest */
+ max_payload_size = fnic_fc_plogi_rsp_rdf(iport, plogi_rsp);
+ tport->max_payload_size =
+ min(max_payload_size, iport->max_payload_size);
+
+ if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "MFS: tport max frame size below spec bounds: %d",
+ tport->max_payload_size);
+ tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "MAX frame size: %u iport max_payload_size: %d tport mfs: %d",
+ max_payload_size, iport->max_payload_size,
+ tport->max_payload_size);
+
+ tport->max_concur_seqs = FNIC_FC_PLOGI_RSP_CONCUR_SEQ(plogi_rsp);
+
+ tport->retry_counter = 0;
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PRLI);
+ fdls_send_tgt_prli(iport, tport);
+}
+static void
+fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint16_t oxid;
+ struct fc_std_els_prli *prli_rsp = (struct fc_std_els_prli *)fchdr;
+ struct fc_std_els_rjt_rsp *els_rjt = (struct fc_std_els_rjt_rsp *)fchdr;
+ struct fnic_tport_event_s *tport_add_evt;
+ struct fnic *fnic = iport->fnic;
+ bool mismatched_tgt = false;
+
+ fcid = FNIC_STD_GET_S_ID(fchdr);
+ tgt_fcid = ntoh24(fcid);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS process tgt PRLI response: 0x%x", tgt_fcid);
+
+ tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport not found: 0x%x", tgt_fcid);
+ /* Handle or just drop? */
+ return;
+ }
+
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x",
+ iport->state, tport->state, tport->fcid);
+ return;
+ }
+
+ if (tport->state != FDLS_TGT_STATE_PRLI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PRLI rsp recvd in wrong state. Drop frame. Restarting nexus");
+ fdls_target_restart_nexus(tport);
+ return;
+ }
+
+ if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping PRLI response from target: 0x%x ",
+ tgt_fcid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery");
+ return;
+ }
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+
+ switch (prli_rsp->els_prli.prli_cmd) {
+ case ELS_LS_ACC:
+ atomic64_inc(&iport->iport_stats.tport_prli_ls_accepts);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PRLI accepted from target: 0x%x", tgt_fcid);
+
+ if (prli_rsp->sp.spp_type != FC_FC4_TYPE_SCSI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "mismatched target zoned with FC SCSI initiator: 0x%x",
+ tgt_fcid);
+ mismatched_tgt = true;
+ }
+ if (mismatched_tgt) {
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
+ return;
+ }
+ break;
+ case ELS_LS_RJT:
+ atomic64_inc(&iport->iport_stats.tport_prli_ls_rejects);
+ if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
+ && (tport->retry_counter < FDLS_RETRY_COUNT)) {
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PRLI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
+ tgt_fcid);
+
+ /*Retry Plogi again from the timer routine. */
+ tport->flags |= FNIC_FDLS_RETRY_FRAME;
+ return;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PRLI returned ELS_LS_RJT from target: 0x%x",
+ tgt_fcid);
+
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
+ return;
+ default:
+ atomic64_inc(&iport->iport_stats.tport_prli_misc_rejects);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PRLI not accepted from target: 0x%x", tgt_fcid);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Found the PRLI target: 0x%x and state: %d",
+ (unsigned int) tgt_fcid, tport->state);
+
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync(fnic, tport);
+ }
+ tport->timer_pending = 0;
+
+ /* Learn Service Params */
+ tport->fcp_csp = be32_to_cpu(prli_rsp->sp.spp_params);
+ tport->retry_counter = 0;
+
+ if (tport->fcp_csp & FCP_SPPF_RETRY)
+ tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
+
+ /* Check if the device plays Target Mode Function */
+ if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Remote port(0x%x): no target support. Deleting it\n",
+ tgt_fcid);
+ fdls_tgt_logout(iport, tport);
+ fdls_delete_tport(iport, tport);
+ return;
+ }
+
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
+
+ /* Inform the driver about new target added */
+ tport_add_evt = kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
+ if (!tport_add_evt) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport event memory allocation failure: 0x%0x\n",
+ tport->fcid);
+ return;
+ }
+ tport_add_evt->event = TGT_EV_RPORT_ADD;
+ tport_add_evt->arg1 = (void *) tport;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport fcid: 0x%x add tport event fcid: 0x%x\n",
+ tport->fcid, iport->fcid);
+ list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
+ queue_work(fnic_event_queue, &fnic->tport_work);
+}
+
+
+static void
+fdls_process_rff_id_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fnic *fnic = iport->fnic;
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fc_std_rff_id *rff_rsp = (struct fc_std_rff_id *) fchdr;
+ uint16_t rsp;
+ uint8_t reason_code;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ if (fdls_get_state(fdls) != FDLS_STATE_REGISTER_FC4_FEATURES) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RFF_ID resp recvd in state(%d). Dropping.",
+ fdls_get_state(fdls));
+ return;
+ }
+
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
+ return;
+ }
+
+ rsp = FNIC_STD_GET_FC_CT_CMD((&rff_rsp->fc_std_ct_hdr));
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS process RFF ID response: 0x%04x", iport->fcid,
+ (uint32_t) rsp);
+
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ switch (rsp) {
+ case FC_FS_ACC:
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ fdls->retry_counter = 0;
+ fdls_set_state((&iport->fabric), FDLS_STATE_SCR);
+ fdls_send_scr(iport);
+ break;
+ case FC_FS_RJT:
+ reason_code = rff_rsp->fc_std_ct_hdr.ct_reason;
+ if (((reason_code == FC_FS_RJT_BSY)
+ || (reason_code == FC_FS_RJT_UNABL))
+ && (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RFF_ID ret ELS_LS_RJT BUSY. Retry from timer routine %p",
+ iport);
+
+ /* Retry again from the timer routine */
+ fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RFF_ID returned ELS_LS_RJT. Halting discovery %p",
+ iport);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ fdls->timer_pending = 0;
+ fdls->retry_counter = 0;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+fdls_process_rft_id_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fc_std_rft_id *rft_rsp = (struct fc_std_rft_id *) fchdr;
+ uint16_t rsp;
+ uint8_t reason_code;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ if (fdls_get_state(fdls) != FDLS_STATE_REGISTER_FC4_TYPES) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RFT_ID resp recvd in state(%d). Dropping.",
+ fdls_get_state(fdls));
+ return;
+ }
+
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
+ return;
+ }
+
+
+ rsp = FNIC_STD_GET_FC_CT_CMD((&rft_rsp->fc_std_ct_hdr));
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS process RFT ID response: 0x%04x", iport->fcid,
+ (uint32_t) rsp);
+
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ switch (rsp) {
+ case FC_FS_ACC:
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ fdls->retry_counter = 0;
+ fdls_send_register_fc4_features(iport);
+ fdls_set_state((&iport->fabric), FDLS_STATE_REGISTER_FC4_FEATURES);
+ break;
+ case FC_FS_RJT:
+ reason_code = rft_rsp->fc_std_ct_hdr.ct_reason;
+ if (((reason_code == FC_FS_RJT_BSY)
+ || (reason_code == FC_FS_RJT_UNABL))
+ && (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: RFT_ID ret ELS_LS_RJT BUSY. Retry from timer routine",
+ iport->fcid);
+
+ /* Retry again from the timer routine */
+ fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: RFT_ID REJ. Halting discovery reason %d expl %d",
+ iport->fcid, reason_code,
+ rft_rsp->fc_std_ct_hdr.ct_explan);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ fdls->timer_pending = 0;
+ fdls->retry_counter = 0;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+fdls_process_rpn_id_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fc_std_rpn_id *rpn_rsp = (struct fc_std_rpn_id *) fchdr;
+ uint16_t rsp;
+ uint8_t reason_code;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ if (fdls_get_state(fdls) != FDLS_STATE_RPN_ID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RPN_ID resp recvd in state(%d). Dropping.",
+ fdls_get_state(fdls));
+ return;
+ }
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
+ return;
+ }
+
+ rsp = FNIC_STD_GET_FC_CT_CMD((&rpn_rsp->fc_std_ct_hdr));
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS process RPN ID response: 0x%04x", iport->fcid,
+ (uint32_t) rsp);
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ switch (rsp) {
+ case FC_FS_ACC:
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ fdls->retry_counter = 0;
+ fdls_send_register_fc4_types(iport);
+ fdls_set_state((&iport->fabric), FDLS_STATE_REGISTER_FC4_TYPES);
+ break;
+ case FC_FS_RJT:
+ reason_code = rpn_rsp->fc_std_ct_hdr.ct_reason;
+ if (((reason_code == FC_FS_RJT_BSY)
+ || (reason_code == FC_FS_RJT_UNABL))
+ && (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RPN_ID returned REJ BUSY. Retry from timer routine %p",
+ iport);
+
+ /* Retry again from the timer routine */
+ fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RPN_ID ELS_LS_RJT. Halting discovery %p", iport);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ fdls->timer_pending = 0;
+ fdls->retry_counter = 0;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+fdls_process_scr_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fc_std_scr *scr_rsp = (struct fc_std_scr *) fchdr;
+ struct fc_std_els_rjt_rsp *els_rjt = (struct fc_std_els_rjt_rsp *) fchdr;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS process SCR response: 0x%04x",
+ (uint32_t) scr_rsp->scr.scr_cmd);
+
+ if (fdls_get_state(fdls) != FDLS_STATE_SCR) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "SCR resp recvd in state(%d). Dropping.",
+ fdls_get_state(fdls));
+ return;
+ }
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
+ }
+
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ switch (scr_rsp->scr.scr_cmd) {
+ case ELS_LS_ACC:
+ atomic64_inc(&iport->iport_stats.fabric_scr_ls_accepts);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ fdls_send_gpn_ft(iport, FDLS_STATE_GPN_FT);
+ break;
+
+ case ELS_LS_RJT:
+ atomic64_inc(&iport->iport_stats.fabric_scr_ls_rejects);
+ if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
+ && (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "SCR ELS_LS_RJT BUSY. Retry from timer routine %p",
+ iport);
+ /* Retry again from the timer routine */
+ fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "SCR returned ELS_LS_RJT. Halting discovery %p",
+ iport);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n",
+ iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ fdls->timer_pending = 0;
+ fdls->retry_counter = 0;
+ }
+ break;
+
+ default:
+ atomic64_inc(&iport->iport_stats.fabric_scr_misc_rejects);
+ break;
+ }
+}
+
+static void
+fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr, int len)
+{
+ struct fc_gpn_ft_rsp_iu *gpn_ft_tgt;
+ struct fnic_tport_s *tport, *next;
+ uint32_t fcid;
+ uint64_t wwpn;
+ int rem_len = len;
+ u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
+
+ gpn_ft_tgt =
+ (struct fc_gpn_ft_rsp_iu *)((uint8_t *) fchdr +
+ sizeof(struct fc_frame_header)
+ + sizeof(struct fc_ct_hdr));
+ len -= sizeof(struct fc_frame_header) + sizeof(struct fc_ct_hdr);
+
+ while (rem_len > 0) {
+
+ fcid = ntoh24(gpn_ft_tgt->fcid);
+ wwpn = be64_to_cpu(gpn_ft_tgt->wwpn);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
+
+ if (fcid == iport->fcid) {
+ if (gpn_ft_tgt->ctrl & FC_NS_FID_LAST)
+ break;
+ gpn_ft_tgt++;
+ rem_len -= sizeof(struct fc_gpn_ft_rsp_iu);
+ continue;
+ }
+
+ tport = fnic_find_tport_by_wwpn(iport, wwpn);
+ if (!tport) {
+ /*
+ * New port registered with the switch or first time query
+ */
+ tport = fdls_create_tport(iport, fcid, wwpn);
+ if (!tport)
+ return;
+ }
+ /*
+ * check if this was an existing tport with same fcid
+ * but whose wwpn has changed now ,then remove it and
+ * create a new one
+ */
+ if (tport->fcid != fcid) {
+ fdls_delete_tport(iport, tport);
+ tport = fdls_create_tport(iport, fcid, wwpn);
+ if (!tport)
+ return;
+ }
+
+ /*
+ * If this GPN_FT rsp is after RSCN then mark the tports which
+ * matches with the new GPN_FT list, if some tport is not
+ * found in GPN_FT we went to delete that tport later.
+ */
+ if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT)
+ tport->flags |= FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+
+ if (gpn_ft_tgt->ctrl & FC_NS_FID_LAST)
+ break;
+
+ gpn_ft_tgt++;
+ rem_len -= sizeof(struct fc_gpn_ft_rsp_iu);
+ }
+ if (rem_len <= 0) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
+ len, rem_len);
+}
+
+ /*remove those ports which was not listed in GPN_FT */
+ if (fdls_get_state((&iport->fabric)) == FDLS_STATE_RSCN_GPN_FT) {
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+
+ if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Remove port: 0x%x not found in GPN_FT list",
+ tport->fcid);
+ fdls_delete_tport(iport, tport);
+ } else {
+ tport->flags &= ~FNIC_FDLS_TPORT_IN_GPN_FT_LIST;
+ }
+ if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+ || (iport->state != FNIC_IPORT_STATE_READY)) {
+ return;
+ }
+ }
+ }
+}
+
+static void
+fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr, int len)
+{
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fc_std_gpn_ft *gpn_ft_rsp = (struct fc_std_gpn_ft *) fchdr;
+ uint16_t rsp;
+ uint8_t reason_code;
+ int count = 0;
+ struct fnic_tport_s *tport, *next;
+ u32 old_link_down_cnt = iport->fnic->link_down_cnt;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS process GPN_FT response: iport state: %d len: %d",
+ iport->state, len);
+
+ /*
+ * GPNFT response :-
+ * FDLS_STATE_GPN_FT : GPNFT send after SCR state
+ * during fabric discovery(FNIC_IPORT_STATE_FABRIC_DISC)
+ * FDLS_STATE_RSCN_GPN_FT : GPNFT send in response to RSCN
+ * FDLS_STATE_SEND_GPNFT : GPNFT send after deleting a Target,
+ * e.g. after receiving Target LOGO
+ * FDLS_STATE_TGT_DISCOVERY :Target discovery is currently in progress
+ * from previous GPNFT response,a new GPNFT response has come.
+ */
+ if (!(((iport->state == FNIC_IPORT_STATE_FABRIC_DISC)
+ && (fdls_get_state(fdls) == FDLS_STATE_GPN_FT))
+ || ((iport->state == FNIC_IPORT_STATE_READY)
+ && ((fdls_get_state(fdls) == FDLS_STATE_RSCN_GPN_FT)
+ || (fdls_get_state(fdls) == FDLS_STATE_SEND_GPNFT)
+ || (fdls_get_state(fdls) == FDLS_STATE_TGT_DISCOVERY))))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "GPNFT resp recvd in fab state(%d) iport_state(%d). Dropping.",
+ fdls_get_state(fdls), iport->state);
+ return;
+ }
+
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
+ }
+
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ iport->state = FNIC_IPORT_STATE_READY;
+ rsp = FNIC_STD_GET_FC_CT_CMD((&gpn_ft_rsp->fc_std_ct_hdr));
+
+ switch (rsp) {
+
+ case FC_FS_ACC:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: GPNFT_RSP accept", iport->fcid);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Canceling fabric disc timer\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ fdls_process_gpn_ft_tgt_list(iport, fchdr, len);
+
+ /*
+ * iport state can change only if link down event happened
+ * We don't need to undo fdls_process_gpn_ft_tgt_list,
+ * that will be taken care in next link up event
+ */
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Halting target discovery: fab st: %d iport st: %d ",
+ fdls_get_state(fdls), iport->state);
+ break;
+ }
+ fdls_tgt_discovery_start(iport);
+ break;
+
+ case FC_FS_RJT:
+ reason_code = gpn_ft_rsp->fc_std_ct_hdr.ct_reason;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: GPNFT_RSP Reject reason: %d", iport->fcid, reason_code);
+
+ if (((reason_code == FC_FS_RJT_BSY)
+ || (reason_code == FC_FS_RJT_UNABL))
+ && (fdls->retry_counter < FDLS_RETRY_COUNT)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine",
+ iport->fcid);
+ /* Retry again from the timer routine */
+ fdls->flags |= FNIC_FDLS_RETRY_FRAME;
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: GPNFT_RSP reject", iport->fcid);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Canceling fabric disc timer\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ /*
+ * If GPN_FT ls_rjt then we should delete
+ * all existing tports
+ */
+ count = 0;
+ list_for_each_entry_safe(tport, next, &iport->tport_list,
+ links) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "GPN_FT_REJECT: Remove port: 0x%x",
+ tport->fcid);
+ fdls_delete_tport(iport, tport);
+ if ((old_link_down_cnt != iport->fnic->link_down_cnt)
+ || (iport->state != FNIC_IPORT_STATE_READY)) {
+ return;
+ }
+ count++;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "GPN_FT_REJECT: Removed (0x%x) ports", count);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * fdls_process_fabric_logo_rsp - Handle an flogo response from the fcf
+ * @iport: Handle to fnic iport
+ * @fchdr: Incoming frame
+ */
+static void
+fdls_process_fabric_logo_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fc_std_flogi *flogo_rsp = (struct fc_std_flogi *) fchdr;
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
+ }
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ switch (flogo_rsp->els.fl_cmd) {
+ case ELS_LS_ACC:
+ if (iport->fabric.state != FDLS_STATE_FABRIC_LOGO) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Flogo response. Fabric not in LOGO state. Dropping! %p",
+ iport);
+ return;
+ }
+
+ iport->fabric.state = FDLS_STATE_FLOGO_DONE;
+ iport->state = FNIC_IPORT_STATE_LINK_WAIT;
+
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport 0x%p Canceling fabric disc timer\n",
+ iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Flogo response from Fabric for did: 0x%x",
+ ntoh24(fchdr->fh_d_id));
+ return;
+
+ case ELS_LS_RJT:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Flogo response from Fabric for did: 0x%x returned ELS_LS_RJT",
+ ntoh24(fchdr->fh_d_id));
+ return;
+
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGO response not accepted or rejected: 0x%x",
+ flogo_rsp->els.fl_cmd);
+ }
+}
+
+static void
+fdls_process_flogi_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr, void *rx_frame)
+{
+ struct fnic_fdls_fabric_s *fabric = &iport->fabric;
+ struct fc_std_flogi *flogi_rsp = (struct fc_std_flogi *) fchdr;
+ uint8_t *fcid;
+ uint16_t rdf_size;
+ uint8_t fcmac[6] = { 0x0E, 0XFC, 0x00, 0x00, 0x00, 0x00 };
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS processing FLOGI response", iport->fcid);
+
+ if (fdls_get_state(fabric) != FDLS_STATE_FABRIC_FLOGI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI response received in state (%d). Dropping frame",
+ fdls_get_state(fabric));
+ return;
+ }
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fabric), oxid, iport->active_oxid_fabric_req);
+ return;
+ }
+
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ switch (flogi_rsp->els.fl_cmd) {
+ case ELS_LS_ACC:
+ atomic64_inc(&iport->iport_stats.fabric_flogi_ls_accepts);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport fcid: 0x%x Canceling fabric disc timer\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ fcid = FNIC_STD_GET_D_ID(fchdr);
+ iport->fcid = ntoh24(fcid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FLOGI response accepted", iport->fcid);
+
+ /* Learn the Service Params */
+ rdf_size = be16_to_cpu(FNIC_LOGI_RDF_SIZE(flogi_rsp->els));
+ if ((rdf_size >= FNIC_MIN_DATA_FIELD_SIZE)
+ && (rdf_size < FNIC_FC_MAX_PAYLOAD_LEN))
+ iport->max_payload_size = min(rdf_size,
+ iport->max_payload_size);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "max_payload_size from fabric: %u set: %d", rdf_size,
+ iport->max_payload_size);
+
+ iport->r_a_tov = be32_to_cpu(FNIC_LOGI_R_A_TOV(flogi_rsp->els));
+ iport->e_d_tov = be32_to_cpu(FNIC_LOGI_E_D_TOV(flogi_rsp->els));
+
+ if (FNIC_LOGI_FEATURES(flogi_rsp->els) & FNIC_FC_EDTOV_NSEC)
+ iport->e_d_tov = iport->e_d_tov / FNIC_NSEC_TO_MSEC;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "From fabric: R_A_TOV: %d E_D_TOV: %d",
+ iport->r_a_tov, iport->e_d_tov);
+
+ fc_host_fabric_name(iport->fnic->host) =
+ get_unaligned_be64(&FNIC_LOGI_NODE_NAME(flogi_rsp->els));
+ fc_host_port_id(iport->fnic->host) = iport->fcid;
+
+ fnic_fdls_learn_fcoe_macs(iport, rx_frame, fcid);
+
+ if (fnic_fdls_register_portid(iport, iport->fcid, rx_frame) != 0) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FLOGI registration failed", iport->fcid);
+ break;
+ }
+
+ memcpy(&fcmac[3], fcid, 3);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Adding vNIC device MAC addr: %02x:%02x:%02x:%02x:%02x:%02x",
+ fcmac[0], fcmac[1], fcmac[2], fcmac[3], fcmac[4],
+ fcmac[5]);
+ vnic_dev_add_addr(iport->fnic->vdev, fcmac);
+
+ if (fdls_get_state(fabric) == FDLS_STATE_FABRIC_FLOGI) {
+ fnic_fdls_start_plogi(iport);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI response received. Starting PLOGI");
+ } else {
+ /* From FDLS_STATE_FABRIC_FLOGI state fabric can only go to
+ * FDLS_STATE_LINKDOWN
+ * state, hence we don't have to worry about undoing:
+ * the fnic_fdls_register_portid and vnic_dev_add_addr
+ */
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI response received in state (%d). Dropping frame",
+ fdls_get_state(fabric));
+ }
+ break;
+
+ case ELS_LS_RJT:
+ atomic64_inc(&iport->iport_stats.fabric_flogi_ls_rejects);
+ if (fabric->retry_counter < iport->max_flogi_retries) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI returned ELS_LS_RJT BUSY. Retry from timer routine %p",
+ iport);
+
+ /* Retry Flogi again from the timer routine. */
+ fabric->flags |= FNIC_FDLS_RETRY_FRAME;
+
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI returned ELS_LS_RJT. Halting discovery %p",
+ iport);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport 0x%p Canceling fabric disc timer\n",
+ iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ fabric->timer_pending = 0;
+ fabric->retry_counter = 0;
+ }
+ break;
+
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI response not accepted: 0x%x",
+ flogi_rsp->els.fl_cmd);
+ atomic64_inc(&iport->iport_stats.fabric_flogi_misc_rejects);
+ break;
+ }
+}
+
+static void
+fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *) fchdr;
+ struct fc_std_els_rjt_rsp *els_rjt = (struct fc_std_els_rjt_rsp *) fchdr;
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ if (fdls_get_state((&iport->fabric)) != FDLS_STATE_FABRIC_PLOGI) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Fabric PLOGI response received in state (%d). Dropping frame",
+ fdls_get_state(&iport->fabric));
+ return;
+ }
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
+ return;
+ }
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ switch (plogi_rsp->els.fl_cmd) {
+ case ELS_LS_ACC:
+ atomic64_inc(&iport->iport_stats.fabric_plogi_ls_accepts);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport fcid: 0x%x fabric PLOGI response: Accepted\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ fdls_set_state(&iport->fabric, FDLS_STATE_RPN_ID);
+ fdls_send_rpn_id(iport);
+ break;
+ case ELS_LS_RJT:
+ atomic64_inc(&iport->iport_stats.fabric_plogi_ls_rejects);
+ if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
+ && (iport->fabric.retry_counter < iport->max_plogi_retries)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Fabric PLOGI ELS_LS_RJT BUSY. Retry from timer routine",
+ iport->fcid);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Fabric PLOGI ELS_LS_RJT. Halting discovery",
+ iport->fcid);
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport fcid: 0x%x Canceling fabric disc timer\n",
+ iport->fcid);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.retry_counter = 0;
+ return;
+ }
+ break;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI response not accepted: 0x%x",
+ plogi_rsp->els.fl_cmd);
+ atomic64_inc(&iport->iport_stats.fabric_plogi_misc_rejects);
+ break;
+ }
+}
+
+static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *)fchdr;
+ struct fc_std_els_rjt_rsp *els_rjt = (struct fc_std_els_rjt_rsp *)fchdr;
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fnic *fnic = iport->fnic;
+ u64 fdmi_tov;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ if (iport->active_oxid_fdmi_plogi != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+ fdls_get_state(fdls), oxid, iport->active_oxid_fdmi_plogi);
+ return;
+ }
+
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_PLOGI_PENDING;
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_plogi);
+
+ if (ntoh24(fchdr->fh_s_id) == FC_FID_MGMT_SERV) {
+ timer_delete_sync(&iport->fabric.fdmi_timer);
+ iport->fabric.fdmi_pending = 0;
+ switch (plogi_rsp->els.fl_cmd) {
+ case ELS_LS_ACC:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS process fdmi PLOGI response status: ELS_LS_ACC\n");
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Sending fdmi registration for port 0x%x\n",
+ iport->fcid);
+
+ fdls_fdmi_register_hba(iport);
+ fdls_fdmi_register_pa(iport);
+ fdmi_tov = jiffies + msecs_to_jiffies(5000);
+ mod_timer(&iport->fabric.fdmi_timer,
+ round_jiffies(fdmi_tov));
+ break;
+ case ELS_LS_RJT:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Fabric FDMI PLOGI returned ELS_LS_RJT reason: 0x%x",
+ els_rjt->rej.er_reason);
+
+ if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
+ || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
+ && (iport->fabric.fdmi_retry < 7)) {
+ iport->fabric.fdmi_retry++;
+ fdls_send_fdmi_plogi(iport);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr,
+ int rsp_type)
+{
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+
+ if (!iport->fabric.fdmi_pending) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Received FDMI ack while not waiting: 0x%x\n",
+ FNIC_STD_GET_OX_ID(fchdr));
+ return;
+ }
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ if ((iport->active_oxid_fdmi_rhba != oxid) &&
+ (iport->active_oxid_fdmi_rpa != oxid)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Incorrect OXID in response. oxid recvd: 0x%x, active oxids(rhba,rpa): 0x%x, 0x%x\n",
+ oxid, iport->active_oxid_fdmi_rhba, iport->active_oxid_fdmi_rpa);
+ return;
+ }
+ if (FNIC_FRAME_TYPE(oxid) == FNIC_FRAME_TYPE_FDMI_RHBA) {
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_REG_HBA_PENDING;
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rhba);
+ } else {
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_RPA_PENDING;
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa);
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport fcid: 0x%x: Received FDMI registration ack\n",
+ iport->fcid);
+
+ if (!iport->fabric.fdmi_pending) {
+ timer_delete_sync(&iport->fabric.fdmi_timer);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport fcid: 0x%x: Canceling FDMI timer\n",
+ iport->fcid);
+ }
+}
+
+static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t s_id;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+
+ s_id = ntoh24(FNIC_STD_GET_S_ID(fchdr));
+
+ if (!(s_id != FC_FID_MGMT_SERV)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received abts rsp with invalid SID: 0x%x. Dropping frame",
+ s_id);
+ return;
+ }
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ switch (FNIC_FRAME_TYPE(oxid)) {
+ case FNIC_FRAME_TYPE_FDMI_PLOGI:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received FDMI PLOGI ABTS rsp with oxid: 0x%x", oxid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: iport->fabric.fdmi_pending: 0x%x",
+ iport->fcid, iport->fabric.fdmi_pending);
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_plogi);
+
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_PLOGI_PENDING;
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: iport->fabric.fdmi_pending: 0x%x",
+ iport->fcid, iport->fabric.fdmi_pending);
+ break;
+ case FNIC_FRAME_TYPE_FDMI_RHBA:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received FDMI RHBA ABTS rsp with oxid: 0x%x", oxid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: iport->fabric.fdmi_pending: 0x%x",
+ iport->fcid, iport->fabric.fdmi_pending);
+
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_REG_HBA_PENDING;
+
+ /* If RPA is still pending, don't turn off ABORT PENDING.
+ * We count on the timer to detect the ABTS timeout and take
+ * corrective action.
+ */
+ if (!(iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING))
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING;
+
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rhba);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: iport->fabric.fdmi_pending: 0x%x",
+ iport->fcid, iport->fabric.fdmi_pending);
+ break;
+ case FNIC_FRAME_TYPE_FDMI_RPA:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received FDMI RPA ABTS rsp with oxid: 0x%x", oxid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: iport->fabric.fdmi_pending: 0x%x",
+ iport->fcid, iport->fabric.fdmi_pending);
+
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_RPA_PENDING;
+
+ /* If RHBA is still pending, don't turn off ABORT PENDING.
+ * We count on the timer to detect the ABTS timeout and take
+ * corrective action.
+ */
+ if (!(iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING))
+ iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING;
+
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: iport->fabric.fdmi_pending: 0x%x",
+ iport->fcid, iport->fabric.fdmi_pending);
+ break;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received abts rsp with invalid oxid: 0x%x. Dropping frame",
+ oxid);
+ break;
+ }
+
+ /*
+ * Only if ABORT PENDING is off, delete the timer, and if no other
+ * operations are pending, retry FDMI.
+ * Otherwise, let the timer pop and take the appropriate action.
+ */
+ if (!(iport->fabric.fdmi_pending & FDLS_FDMI_ABORT_PENDING)) {
+ timer_delete_sync(&iport->fabric.fdmi_timer);
+ if (!iport->fabric.fdmi_pending)
+ fdls_fdmi_retry_plogi(iport);
+ }
+}
+
+static void
+fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t s_id;
+ struct fc_std_abts_ba_acc *ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
+ struct fc_std_abts_ba_rjt *ba_rjt;
+ uint32_t fabric_state = iport->fabric.state;
+ struct fnic *fnic = iport->fnic;
+ int frame_type;
+ uint16_t oxid;
+
+ s_id = ntoh24(fchdr->fh_s_id);
+ ba_rjt = (struct fc_std_abts_ba_rjt *) fchdr;
+
+ if (!((s_id == FC_FID_DIR_SERV) || (s_id == FC_FID_FLOGI)
+ || (s_id == FC_FID_FCTRL))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received abts rsp with invalid SID: 0x%x. Dropping frame",
+ s_id);
+ return;
+ }
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ if (iport->active_oxid_fabric_req != oxid) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received abts rsp with invalid oxid: 0x%x. Dropping frame",
+ oxid);
+ return;
+ }
+
+ if (iport->fabric.timer_pending) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Canceling fabric disc timer %p\n", iport);
+ fnic_del_fabric_timer_sync(fnic);
+ }
+ iport->fabric.timer_pending = 0;
+ iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+
+ if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received abts rsp BA_ACC for fabric_state: %d OX_ID: 0x%x",
+ fabric_state, be16_to_cpu(ba_acc->acc.ba_ox_id));
+ } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "BA_RJT fs: %d OX_ID: 0x%x rc: 0x%x rce: 0x%x",
+ fabric_state, FNIC_STD_GET_OX_ID(&ba_rjt->fchdr),
+ ba_rjt->rjt.br_reason, ba_rjt->rjt.br_explan);
+ }
+
+ frame_type = FNIC_FRAME_TYPE(oxid);
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+
+ /* currently error handling/retry logic is same for ABTS BA_ACC & BA_RJT */
+ switch (frame_type) {
+ case FNIC_FRAME_TYPE_FABRIC_FLOGI:
+ if (iport->fabric.retry_counter < iport->max_flogi_retries)
+ fdls_send_fabric_flogi(iport);
+ else
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Exceeded max FLOGI retries");
+ break;
+ case FNIC_FRAME_TYPE_FABRIC_LOGO:
+ if (iport->fabric.retry_counter < FABRIC_LOGO_MAX_RETRY)
+ fdls_send_fabric_logo(iport);
+ break;
+ case FNIC_FRAME_TYPE_FABRIC_PLOGI:
+ if (iport->fabric.retry_counter < iport->max_plogi_retries)
+ fdls_send_fabric_plogi(iport);
+ else
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Exceeded max PLOGI retries");
+ break;
+ case FNIC_FRAME_TYPE_FABRIC_RPN:
+ if (iport->fabric.retry_counter < FDLS_RETRY_COUNT)
+ fdls_send_rpn_id(iport);
+ else
+ /* go back to fabric Plogi */
+ fnic_fdls_start_plogi(iport);
+ break;
+ case FNIC_FRAME_TYPE_FABRIC_SCR:
+ if (iport->fabric.retry_counter < FDLS_RETRY_COUNT)
+ fdls_send_scr(iport);
+ else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "SCR exhausted retries. Start fabric PLOGI %p",
+ iport);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FNIC_FRAME_TYPE_FABRIC_RFT:
+ if (iport->fabric.retry_counter < FDLS_RETRY_COUNT)
+ fdls_send_register_fc4_types(iport);
+ else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RFT exhausted retries. Start fabric PLOGI %p",
+ iport);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FNIC_FRAME_TYPE_FABRIC_RFF:
+ if (iport->fabric.retry_counter < FDLS_RETRY_COUNT)
+ fdls_send_register_fc4_features(iport);
+ else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RFF exhausted retries. Start fabric PLOGI %p",
+ iport);
+ fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */
+ }
+ break;
+ case FNIC_FRAME_TYPE_FABRIC_GPN_FT:
+ if (iport->fabric.retry_counter <= FDLS_RETRY_COUNT)
+ fdls_send_gpn_ft(iport, fabric_state);
+ else
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "GPN FT exhausted retries. Start fabric PLOGI %p",
+ iport);
+ break;
+ default:
+ /*
+ * We should not be here since we already validated rx oxid with
+ * our active_oxid_fabric_req
+ */
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Invalid OXID/active oxid 0x%x\n", oxid);
+ WARN_ON(true);
+ return;
+ }
+}
+
+static void
+fdls_process_abts_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
+{
+ uint8_t *frame;
+ struct fc_std_abts_ba_acc *pba_acc;
+ uint32_t nport_id;
+ uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+ struct fnic_tport_s *tport;
+ struct fnic *fnic = iport->fnic;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_abts_ba_acc);
+
+ nport_id = ntoh24(fchdr->fh_s_id);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received abort from SID 0x%8x", nport_id);
+
+ tport = fnic_find_tport_by_fcid(iport, nport_id);
+ if (tport) {
+ if (tport->active_oxid == oxid) {
+ tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ }
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "0x%x: Failed to allocate frame to send response for ABTS req",
+ iport->fcid);
+ return;
+ }
+
+ pba_acc = (struct fc_std_abts_ba_acc *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ *pba_acc = (struct fc_std_abts_ba_acc) {
+ .fchdr = {.fh_r_ctl = FC_RCTL_BA_ACC,
+ .fh_f_ctl = {FNIC_FCP_RSP_FCTL, 0, 0}},
+ .acc = {.ba_low_seq_cnt = 0, .ba_high_seq_cnt = cpu_to_be16(0xFFFF)}
+ };
+
+ FNIC_STD_SET_S_ID(pba_acc->fchdr, fchdr->fh_d_id);
+ FNIC_STD_SET_D_ID(pba_acc->fchdr, fchdr->fh_s_id);
+ FNIC_STD_SET_OX_ID(pba_acc->fchdr, FNIC_STD_GET_OX_ID(fchdr));
+ FNIC_STD_SET_RX_ID(pba_acc->fchdr, FNIC_STD_GET_RX_ID(fchdr));
+
+ pba_acc->acc.ba_rx_id = cpu_to_be16(FNIC_STD_GET_RX_ID(fchdr));
+ pba_acc->acc.ba_ox_id = cpu_to_be16(FNIC_STD_GET_OX_ID(fchdr));
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS send BA ACC with oxid: 0x%x",
+ iport->fcid, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+}
+
+static void
+fdls_process_unsupported_els_req(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint8_t *frame;
+ struct fc_std_els_rjt_rsp *pls_rsp;
+ uint16_t oxid;
+ uint32_t d_id = ntoh24(fchdr->fh_d_id);
+ struct fnic *fnic = iport->fnic;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_rjt_rsp);
+
+ if (iport->fcid != d_id) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping unsupported ELS with illegal frame bits 0x%x\n",
+ d_id);
+ atomic64_inc(&iport->iport_stats.unsupported_frames_dropped);
+ return;
+ }
+
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ && (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping unsupported ELS request in iport state: %d",
+ iport->state);
+ atomic64_inc(&iport->iport_stats.unsupported_frames_dropped);
+ return;
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send response to unsupported ELS request");
+ return;
+ }
+
+ pls_rsp = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_els_rjt_frame(frame, iport);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Process unsupported ELS request from SID: 0x%x",
+ iport->fcid, ntoh24(fchdr->fh_s_id));
+
+ /* We don't support this ELS request, send a reject */
+ pls_rsp->rej.er_reason = 0x0B;
+ pls_rsp->rej.er_explan = 0x0;
+ pls_rsp->rej.er_vendor = 0x0;
+
+ FNIC_STD_SET_S_ID(pls_rsp->fchdr, fchdr->fh_d_id);
+ FNIC_STD_SET_D_ID(pls_rsp->fchdr, fchdr->fh_s_id);
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ FNIC_STD_SET_OX_ID(pls_rsp->fchdr, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+}
+
+static void
+fdls_process_rls_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
+{
+ uint8_t *frame;
+ struct fc_std_rls_acc *prls_acc_rsp;
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_rls_acc);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Process RLS request %d", iport->fnic->fnic_num);
+
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ && (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received RLS req in iport state: %d. Dropping the frame.",
+ iport->state);
+ return;
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send RLS accept");
+ return;
+ }
+ prls_acc_rsp = (struct fc_std_rls_acc *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+
+ FNIC_STD_SET_S_ID(prls_acc_rsp->fchdr, fchdr->fh_d_id);
+ FNIC_STD_SET_D_ID(prls_acc_rsp->fchdr, fchdr->fh_s_id);
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ FNIC_STD_SET_OX_ID(prls_acc_rsp->fchdr, oxid);
+ FNIC_STD_SET_RX_ID(prls_acc_rsp->fchdr, FNIC_UNASSIGNED_RXID);
+
+ FNIC_STD_SET_F_CTL(prls_acc_rsp->fchdr, FNIC_ELS_REP_FCTL << 16);
+ FNIC_STD_SET_R_CTL(prls_acc_rsp->fchdr, FC_RCTL_ELS_REP);
+ FNIC_STD_SET_TYPE(prls_acc_rsp->fchdr, FC_TYPE_ELS);
+
+ prls_acc_rsp->els.rls_cmd = ELS_LS_ACC;
+ prls_acc_rsp->els.rls_lesb.lesb_link_fail =
+ cpu_to_be32(iport->fnic->link_down_cnt);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+}
+
+static void
+fdls_process_els_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr,
+ uint32_t len)
+{
+ uint8_t *frame;
+ struct fc_std_els_acc_rsp *pels_acc;
+ uint16_t oxid;
+ uint8_t *fc_payload;
+ uint8_t type;
+ struct fnic *fnic = iport->fnic;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET;
+
+ fc_payload = (uint8_t *) fchdr + sizeof(struct fc_frame_header);
+ type = *fc_payload;
+
+ if ((iport->state != FNIC_IPORT_STATE_READY)
+ && (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping ELS frame type: 0x%x in iport state: %d",
+ type, iport->state);
+ return;
+ }
+ switch (type) {
+ case ELS_ECHO:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "sending LS_ACC for ECHO request %d\n",
+ iport->fnic->fnic_num);
+ break;
+
+ case ELS_RRQ:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "sending LS_ACC for RRQ request %d\n",
+ iport->fnic->fnic_num);
+ break;
+
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "sending LS_ACC for 0x%x ELS frame\n", type);
+ break;
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send ELS response for 0x%x",
+ type);
+ return;
+ }
+
+ if (type == ELS_ECHO) {
+ /* Brocade sends a longer payload, copy all frame back */
+ memcpy(frame, fchdr, len);
+ }
+
+ pels_acc = (struct fc_std_els_acc_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_els_acc_frame(frame, iport);
+
+ FNIC_STD_SET_D_ID(pels_acc->fchdr, fchdr->fh_s_id);
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ FNIC_STD_SET_OX_ID(pels_acc->fchdr, oxid);
+
+ if (type == ELS_ECHO)
+ frame_size += len;
+ else
+ frame_size += sizeof(struct fc_std_els_acc_rsp);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+}
+
+static void
+fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint32_t s_id;
+ struct fnic_tport_s *tport;
+ uint32_t tport_state;
+ struct fc_std_abts_ba_acc *ba_acc;
+ struct fc_std_abts_ba_rjt *ba_rjt;
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+ int frame_type;
+
+ s_id = ntoh24(fchdr->fh_s_id);
+ ba_acc = (struct fc_std_abts_ba_acc *)fchdr;
+ ba_rjt = (struct fc_std_abts_ba_rjt *)fchdr;
+
+ tport = fnic_find_tport_by_fcid(iport, s_id);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Received tgt abts rsp with invalid SID: 0x%x", s_id);
+ return;
+ }
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "tport 0x%p Canceling fabric disc timer\n", tport);
+ fnic_del_tport_timer_sync(fnic, tport);
+ }
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Received tgt abts rsp in iport state(%d). Dropping.",
+ iport->state);
+ return;
+ }
+ tport->timer_pending = 0;
+ tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;
+ tport_state = tport->state;
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+ /*This abort rsp is for ADISC */
+ frame_type = FNIC_FRAME_TYPE(oxid);
+ switch (frame_type) {
+ case FNIC_FRAME_TYPE_TGT_ADISC:
+ if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "OX_ID: 0x%x tgt_fcid: 0x%x rcvd tgt adisc abts resp BA_ACC",
+ be16_to_cpu(ba_acc->acc.ba_ox_id),
+ tport->fcid);
+ } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ",
+ tport->fcid, tport_state);
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation:0x%x ",
+ ba_rjt->rjt.br_reason,
+ ba_rjt->rjt.br_explan);
+ }
+ if ((tport->retry_counter < FDLS_RETRY_COUNT)
+ && (fchdr->fh_r_ctl == FC_RCTL_BA_ACC)) {
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ fdls_send_tgt_adisc(iport, tport);
+ return;
+ }
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "ADISC not responding. Deleting target port: 0x%x",
+ tport->fcid);
+ fdls_delete_tport(iport, tport);
+ /* Restart discovery of targets */
+ if ((iport->state == FNIC_IPORT_STATE_READY)
+ && (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+ && (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+ fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+ }
+ break;
+ case FNIC_FRAME_TYPE_TGT_PLOGI:
+ if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x",
+ tport->fcid);
+ } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x",
+ tport->fcid, FNIC_STD_GET_OX_ID(fchdr));
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation: 0x%x",
+ ba_rjt->rjt.br_reason,
+ ba_rjt->rjt.br_explan);
+ }
+ if ((tport->retry_counter < iport->max_plogi_retries)
+ && (fchdr->fh_r_ctl == FC_RCTL_BA_ACC)) {
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ fdls_send_tgt_plogi(iport, tport);
+ return;
+ }
+
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ fdls_delete_tport(iport, tport);
+ /* Restart discovery of targets */
+ if ((iport->state == FNIC_IPORT_STATE_READY)
+ && (iport->fabric.state != FDLS_STATE_SEND_GPNFT)
+ && (iport->fabric.state != FDLS_STATE_RSCN_GPN_FT)) {
+ fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+ }
+ break;
+ case FNIC_FRAME_TYPE_TGT_PRLI:
+ if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Received tgt PRLI abts response BA_ACC",
+ tport->fcid);
+ } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ",
+ tport->fcid, FNIC_STD_GET_OX_ID(fchdr));
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "reason code: 0x%x reason code explanation: 0x%x",
+ ba_rjt->rjt.br_reason,
+ ba_rjt->rjt.br_explan);
+ }
+ if ((tport->retry_counter < FDLS_RETRY_COUNT)
+ && (fchdr->fh_r_ctl == FC_RCTL_BA_ACC)) {
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ fdls_send_tgt_prli(iport, tport);
+ return;
+ }
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ fdls_send_tgt_plogi(iport, tport); /* go back to plogi */
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
+ break;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received ABTS response for unknown frame %p", iport);
+ break;
+ }
+
+}
+
+static void
+fdls_process_plogi_req(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint8_t *frame;
+ struct fc_std_els_rjt_rsp *pplogi_rsp;
+ uint16_t oxid;
+ uint32_t d_id = ntoh24(fchdr->fh_d_id);
+ struct fnic *fnic = iport->fnic;
+ uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_rjt_rsp);
+
+ if (iport->fcid != d_id) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received PLOGI with illegal frame bits. Dropping frame from 0x%x",
+ d_id);
+ return;
+ }
+
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received PLOGI request in iport state: %d Dropping frame",
+ iport->state);
+ return;
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send response to PLOGI request");
+ return;
+ }
+
+ pplogi_rsp = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_els_rjt_frame(frame, iport);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: Process PLOGI request from SID: 0x%x",
+ iport->fcid, ntoh24(fchdr->fh_s_id));
+
+ /* We don't support PLOGI request, send a reject */
+ pplogi_rsp->rej.er_reason = 0x0B;
+ pplogi_rsp->rej.er_explan = 0x0;
+ pplogi_rsp->rej.er_vendor = 0x0;
+
+ FNIC_STD_SET_S_ID(pplogi_rsp->fchdr, fchdr->fh_d_id);
+ FNIC_STD_SET_D_ID(pplogi_rsp->fchdr, fchdr->fh_s_id);
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ FNIC_STD_SET_OX_ID(pplogi_rsp->fchdr, oxid);
+
+ fnic_send_fcoe_frame(iport, frame, frame_size);
+}
+
+static void
+fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
+{
+ struct fc_std_logo *logo = (struct fc_std_logo *)fchdr;
+ uint32_t nport_id;
+ uint64_t nport_name;
+ struct fnic_tport_s *tport;
+ struct fnic *fnic = iport->fnic;
+ uint16_t oxid;
+
+ nport_id = ntoh24(logo->els.fl_n_port_id);
+ nport_name = be64_to_cpu(logo->els.fl_n_port_wwn);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Process LOGO request from fcid: 0x%x", nport_id);
+
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Dropping LOGO req from 0x%x in iport state: %d",
+ nport_id, iport->state);
+ return;
+ }
+
+ tport = fnic_find_tport_by_fcid(iport, nport_id);
+
+ if (!tport) {
+ /* We are not logged in with the nport, log and drop... */
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Received LOGO from an nport not logged in: 0x%x(0x%llx)",
+ nport_id, nport_name);
+ return;
+ }
+ if (tport->fcid != nport_id) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Received LOGO with invalid target port fcid: 0x%x(0x%llx)",
+ nport_id, nport_name);
+ return;
+ }
+ if (tport->timer_pending) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "tport fcid 0x%x: Canceling disc timer\n",
+ tport->fcid);
+ fnic_del_tport_timer_sync(fnic, tport);
+ tport->timer_pending = 0;
+ }
+
+ /* got a logo in response to adisc to a target which has logged out */
+ if (tport->state == FDLS_TGT_STATE_ADISC) {
+ tport->retry_counter = 0;
+ oxid = tport->active_oxid;
+ fdls_free_oxid(iport, oxid, &tport->active_oxid);
+ fdls_delete_tport(iport, tport);
+ fdls_send_logo_resp(iport, &logo->fchdr);
+ if ((iport->state == FNIC_IPORT_STATE_READY)
+ && (fdls_get_state(&iport->fabric) != FDLS_STATE_SEND_GPNFT)
+ && (fdls_get_state(&iport->fabric) != FDLS_STATE_RSCN_GPN_FT)) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Sending GPNFT in response to LOGO from Target:0x%x",
+ nport_id);
+ fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+ return;
+ }
+ } else {
+ fdls_delete_tport(iport, tport);
+ }
+ if (iport->state == FNIC_IPORT_STATE_READY) {
+ fdls_send_logo_resp(iport, &logo->fchdr);
+ if ((fdls_get_state(&iport->fabric) != FDLS_STATE_SEND_GPNFT) &&
+ (fdls_get_state(&iport->fabric) != FDLS_STATE_RSCN_GPN_FT)) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Sending GPNFT in response to LOGO from Target:0x%x",
+ nport_id);
+ fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
+ }
+ }
+}
+
+static void
+fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
+{
+ struct fc_std_rscn *rscn;
+ struct fc_els_rscn_page *rscn_port = NULL;
+ int num_ports;
+ struct fnic_tport_s *tport, *next;
+ uint32_t nport_id;
+ uint8_t fcid[3];
+ int newports = 0;
+ struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+ struct fnic *fnic = iport->fnic;
+ int rscn_type = NOT_PC_RSCN;
+ uint32_t sid = ntoh24(fchdr->fh_s_id);
+ unsigned long reset_fnic_list_lock_flags = 0;
+ uint16_t rscn_payload_len;
+
+ atomic64_inc(&iport->iport_stats.num_rscns);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS process RSCN %p", iport);
+
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS RSCN received in state(%d). Dropping",
+ fdls_get_state(fdls));
+ return;
+ }
+
+ rscn = (struct fc_std_rscn *)fchdr;
+ rscn_payload_len = be16_to_cpu(rscn->els.rscn_plen);
+
+ /* frame validation */
+ if ((rscn_payload_len % 4 != 0) || (rscn_payload_len < 8)
+ || (rscn_payload_len > 1024)
+ || (rscn->els.rscn_page_len != 4)) {
+ num_ports = 0;
+ if ((rscn_payload_len == 0xFFFF)
+ && (sid == FC_FID_FCTRL)) {
+ rscn_type = PC_RSCN;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "pcrscn: PCRSCN received. sid: 0x%x payload len: 0x%x",
+ sid, rscn_payload_len);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RSCN payload_len: 0x%x page_len: 0x%x",
+ rscn_payload_len, rscn->els.rscn_page_len);
+ /* if this happens then we need to send ADISC to all the tports. */
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+ if (tport->state == FDLS_TGT_STATE_READY)
+ tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RSCN for port id: 0x%x", tport->fcid);
+ }
+ } /* end else */
+ } else {
+ num_ports = (rscn_payload_len - 4) / rscn->els.rscn_page_len;
+ rscn_port = (struct fc_els_rscn_page *)(rscn + 1);
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RSCN received for num_ports: %d payload_len: %d page_len: %d ",
+ num_ports, rscn_payload_len, rscn->els.rscn_page_len);
+
+ /*
+ * RSCN have at least one Port_ID page , but may not have any port_id
+ * in it. If no port_id is specified in the Port_ID page , we send
+ * ADISC to all the tports
+ */
+
+ while (num_ports) {
+
+ memcpy(fcid, rscn_port->rscn_fid, 3);
+
+ nport_id = ntoh24(fcid);
+ rscn_port++;
+ num_ports--;
+ /* if this happens then we need to send ADISC to all the tports. */
+ if (nport_id == 0) {
+ list_for_each_entry_safe(tport, next, &iport->tport_list,
+ links) {
+ if (tport->state == FDLS_TGT_STATE_READY)
+ tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RSCN for port id: 0x%x", tport->fcid);
+ }
+ break;
+ }
+ tport = fnic_find_tport_by_fcid(iport, nport_id);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RSCN port id list: 0x%x", nport_id);
+
+ if (!tport) {
+ newports++;
+ continue;
+ }
+ if (tport->state == FDLS_TGT_STATE_READY)
+ tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
+ }
+
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON &&
+ rscn_type == PC_RSCN && fnic->role == FNIC_ROLE_FCP_INITIATOR) {
+
+ if (fnic->pc_rscn_handling_status == PC_RSCN_HANDLING_IN_PROGRESS) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PCRSCN handling already in progress. Skip host reset: %d",
+ iport->fnic->fnic_num);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Processing PCRSCN. Queuing fnic for host reset: %d",
+ iport->fnic->fnic_num);
+ fnic->pc_rscn_handling_status = PC_RSCN_HANDLING_IN_PROGRESS;
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+
+ spin_lock_irqsave(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+ list_add_tail(&fnic->links, &reset_fnic_list);
+ spin_unlock_irqrestore(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+
+ queue_work(reset_fnic_work_queue, &reset_fnic_work);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FDLS process RSCN sending GPN_FT: newports: %d", newports);
+ fdls_send_gpn_ft(iport, FDLS_STATE_RSCN_GPN_FT);
+ fdls_send_rscn_resp(iport, fchdr);
+ }
+}
+
+void fnic_fdls_disc_start(struct fnic_iport_s *iport)
+{
+ struct fnic *fnic = iport->fnic;
+
+ fc_host_fabric_name(iport->fnic->host) = 0;
+ fc_host_post_event(iport->fnic->host, fc_get_event_number(),
+ FCH_EVT_LIPRESET, 0);
+
+ if (!iport->usefip) {
+ if (iport->flags & FNIC_FIRST_LINK_UP) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ fnic_scsi_fcpio_reset(iport->fnic);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+ iport->flags &= ~FNIC_FIRST_LINK_UP;
+ }
+ fnic_fdls_start_flogi(iport);
+ } else
+ fnic_fdls_start_plogi(iport);
+}
+
+static void
+fdls_process_adisc_req(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ struct fc_std_els_adisc *padisc_acc;
+ struct fc_std_els_adisc *adisc_req = (struct fc_std_els_adisc *)fchdr;
+ uint64_t frame_wwnn;
+ uint64_t frame_wwpn;
+ uint32_t tgt_fcid;
+ struct fnic_tport_s *tport;
+ uint8_t *fcid;
+ uint8_t *rjt_frame;
+ uint8_t *acc_frame;
+ struct fc_std_els_rjt_rsp *prjts_rsp;
+ uint16_t oxid;
+ struct fnic *fnic = iport->fnic;
+ uint16_t rjt_frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_rjt_rsp);
+ uint16_t acc_frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+ sizeof(struct fc_std_els_adisc);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Process ADISC request %d", iport->fnic->fnic_num);
+
+ fcid = FNIC_STD_GET_S_ID(fchdr);
+ tgt_fcid = ntoh24(fcid);
+ tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
+ if (!tport) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "tport for fcid: 0x%x not found. Dropping ADISC req.",
+ tgt_fcid);
+ return;
+ }
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Dropping ADISC req from fcid: 0x%x in iport state: %d",
+ tgt_fcid, iport->state);
+ return;
+ }
+
+ frame_wwnn = be64_to_cpu(adisc_req->els.adisc_wwnn);
+ frame_wwpn = be64_to_cpu(adisc_req->els.adisc_wwpn);
+
+ if ((frame_wwnn != tport->wwnn) || (frame_wwpn != tport->wwpn)) {
+ /* send reject */
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "ADISC req from fcid: 0x%x mismatch wwpn: 0x%llx wwnn: 0x%llx",
+ tgt_fcid, frame_wwpn, frame_wwnn);
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "local tport wwpn: 0x%llx wwnn: 0x%llx. Sending RJT",
+ tport->wwpn, tport->wwnn);
+
+ rjt_frame = fdls_alloc_frame(iport);
+ if (rjt_frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate rjt_frame to send response to ADISC request");
+ return;
+ }
+
+ prjts_rsp = (struct fc_std_els_rjt_rsp *) (rjt_frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+ fdls_init_els_rjt_frame(rjt_frame, iport);
+
+ prjts_rsp->rej.er_reason = 0x03; /* logical error */
+ prjts_rsp->rej.er_explan = 0x1E; /* N_port login required */
+ prjts_rsp->rej.er_vendor = 0x0;
+
+ FNIC_STD_SET_S_ID(prjts_rsp->fchdr, fchdr->fh_d_id);
+ FNIC_STD_SET_D_ID(prjts_rsp->fchdr, fchdr->fh_s_id);
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ FNIC_STD_SET_OX_ID(prjts_rsp->fchdr, oxid);
+
+ fnic_send_fcoe_frame(iport, rjt_frame, rjt_frame_size);
+ return;
+ }
+
+ acc_frame = fdls_alloc_frame(iport);
+ if (acc_frame == NULL) {
+ FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send ADISC accept");
+ return;
+ }
+
+ padisc_acc = (struct fc_std_els_adisc *) (acc_frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+
+ FNIC_STD_SET_S_ID(padisc_acc->fchdr, fchdr->fh_d_id);
+ FNIC_STD_SET_D_ID(padisc_acc->fchdr, fchdr->fh_s_id);
+
+ FNIC_STD_SET_F_CTL(padisc_acc->fchdr, FNIC_ELS_REP_FCTL << 16);
+ FNIC_STD_SET_R_CTL(padisc_acc->fchdr, FC_RCTL_ELS_REP);
+ FNIC_STD_SET_TYPE(padisc_acc->fchdr, FC_TYPE_ELS);
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ FNIC_STD_SET_OX_ID(padisc_acc->fchdr, oxid);
+ FNIC_STD_SET_RX_ID(padisc_acc->fchdr, FNIC_UNASSIGNED_RXID);
+
+ padisc_acc->els.adisc_cmd = ELS_LS_ACC;
+
+ FNIC_STD_SET_NPORT_NAME(&padisc_acc->els.adisc_wwpn,
+ iport->wwpn);
+ FNIC_STD_SET_NODE_NAME(&padisc_acc->els.adisc_wwnn,
+ iport->wwnn);
+ memcpy(padisc_acc->els.adisc_port_id, fchdr->fh_d_id, 3);
+
+ fnic_send_fcoe_frame(iport, acc_frame, acc_frame_size);
+}
+
+/*
+ * Performs a validation for all FCOE frames and return the frame type
+ */
+int
+fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr)
+{
+ uint8_t type;
+ uint8_t *fc_payload;
+ uint16_t oxid;
+ uint32_t s_id;
+ uint32_t d_id;
+ struct fnic *fnic = iport->fnic;
+ struct fnic_fdls_fabric_s *fabric = &iport->fabric;
+ int oxid_frame_type;
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ fc_payload = (uint8_t *) fchdr + sizeof(struct fc_frame_header);
+ type = *fc_payload;
+ s_id = ntoh24(fchdr->fh_s_id);
+ d_id = ntoh24(fchdr->fh_d_id);
+
+ /* some common validation */
+ if (fdls_get_state(fabric) > FDLS_STATE_FABRIC_FLOGI) {
+ if (iport->fcid != d_id || (!FNIC_FC_FRAME_CS_CTL(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "invalid frame received. Dropping frame");
+ return -1;
+ }
+ }
+
+ /* BLS ABTS response */
+ if ((fchdr->fh_r_ctl == FC_RCTL_BA_ACC)
+ || (fchdr->fh_r_ctl == FC_RCTL_BA_RJT)) {
+ if (!(FNIC_FC_FRAME_TYPE_BLS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received ABTS invalid frame. Dropping frame");
+ return -1;
+
+ }
+ if (fdls_is_oxid_fabric_req(oxid)) {
+ if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unexpected ABTS RSP(oxid:0x%x) from 0x%x. Dropping frame",
+ oxid, s_id);
+ return -1;
+ }
+ return FNIC_FABRIC_BLS_ABTS_RSP;
+ } else if (fdls_is_oxid_fdmi_req(oxid)) {
+ return FNIC_FDMI_BLS_ABTS_RSP;
+ } else if (fdls_is_oxid_tgt_req(oxid)) {
+ return FNIC_TPORT_BLS_ABTS_RSP;
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received ABTS rsp with unknown oxid(0x%x) from 0x%x. Dropping frame",
+ oxid, s_id);
+ return -1;
+ }
+
+ /* BLS ABTS Req */
+ if ((fchdr->fh_r_ctl == FC_RCTL_BA_ABTS)
+ && (FNIC_FC_FRAME_TYPE_BLS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Receiving Abort Request from s_id: 0x%x", s_id);
+ return FNIC_BLS_ABTS_REQ;
+ }
+
+ /* unsolicited requests frames */
+ if (FNIC_FC_FRAME_UNSOLICITED(fchdr)) {
+ switch (type) {
+ case ELS_LOGO:
+ if ((!FNIC_FC_FRAME_FCTL_FIRST_LAST_SEQINIT(fchdr))
+ || (!FNIC_FC_FRAME_UNSOLICITED(fchdr))
+ || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received LOGO invalid frame. Dropping frame");
+ return -1;
+ }
+ return FNIC_ELS_LOGO_REQ;
+ case ELS_RSCN:
+ if ((!FNIC_FC_FRAME_FCTL_FIRST_LAST_SEQINIT(fchdr))
+ || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))
+ || (!FNIC_FC_FRAME_UNSOLICITED(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received RSCN invalid FCTL. Dropping frame");
+ return -1;
+ }
+ if (s_id != FC_FID_FCTRL)
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received RSCN from target FCTL: 0x%x type: 0x%x s_id: 0x%x.",
+ fchdr->fh_f_ctl[0], fchdr->fh_type, s_id);
+ return FNIC_ELS_RSCN_REQ;
+ case ELS_PLOGI:
+ return FNIC_ELS_PLOGI_REQ;
+ case ELS_ECHO:
+ return FNIC_ELS_ECHO_REQ;
+ case ELS_ADISC:
+ return FNIC_ELS_ADISC;
+ case ELS_RLS:
+ return FNIC_ELS_RLS;
+ case ELS_RRQ:
+ return FNIC_ELS_RRQ;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Unsupported frame (type:0x%02x) from fcid: 0x%x",
+ type, s_id);
+ return FNIC_ELS_UNSUPPORTED_REQ;
+ }
+ }
+
+ /* solicited response from fabric or target */
+ oxid_frame_type = FNIC_FRAME_TYPE(oxid);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "oxid frame code: 0x%x, oxid: 0x%x\n", oxid_frame_type, oxid);
+ switch (oxid_frame_type) {
+ case FNIC_FRAME_TYPE_FABRIC_FLOGI:
+ if (type == ELS_LS_ACC) {
+ if ((s_id != FC_FID_FLOGI)
+ || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown frame. Dropping frame");
+ return -1;
+ }
+ }
+ return FNIC_FABRIC_FLOGI_RSP;
+
+ case FNIC_FRAME_TYPE_FABRIC_PLOGI:
+ if (type == ELS_LS_ACC) {
+ if ((s_id != FC_FID_DIR_SERV)
+ || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown frame. Dropping frame");
+ return -1;
+ }
+ }
+ return FNIC_FABRIC_PLOGI_RSP;
+
+ case FNIC_FRAME_TYPE_FABRIC_SCR:
+ if (type == ELS_LS_ACC) {
+ if ((s_id != FC_FID_FCTRL)
+ || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown frame. Dropping frame");
+ return -1;
+ }
+ }
+ return FNIC_FABRIC_SCR_RSP;
+
+ case FNIC_FRAME_TYPE_FABRIC_RPN:
+ if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown frame. Dropping frame");
+ return -1;
+ }
+ return FNIC_FABRIC_RPN_RSP;
+
+ case FNIC_FRAME_TYPE_FABRIC_RFT:
+ if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown frame. Dropping frame");
+ return -1;
+ }
+ return FNIC_FABRIC_RFT_RSP;
+
+ case FNIC_FRAME_TYPE_FABRIC_RFF:
+ if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown frame. Dropping frame");
+ return -1;
+ }
+ return FNIC_FABRIC_RFF_RSP;
+
+ case FNIC_FRAME_TYPE_FABRIC_GPN_FT:
+ if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown frame. Dropping frame");
+ return -1;
+ }
+ return FNIC_FABRIC_GPN_FT_RSP;
+
+ case FNIC_FRAME_TYPE_FABRIC_LOGO:
+ return FNIC_FABRIC_LOGO_RSP;
+ case FNIC_FRAME_TYPE_FDMI_PLOGI:
+ return FNIC_FDMI_PLOGI_RSP;
+ case FNIC_FRAME_TYPE_FDMI_RHBA:
+ return FNIC_FDMI_REG_HBA_RSP;
+ case FNIC_FRAME_TYPE_FDMI_RPA:
+ return FNIC_FDMI_RPA_RSP;
+ case FNIC_FRAME_TYPE_TGT_PLOGI:
+ return FNIC_TPORT_PLOGI_RSP;
+ case FNIC_FRAME_TYPE_TGT_PRLI:
+ return FNIC_TPORT_PRLI_RSP;
+ case FNIC_FRAME_TYPE_TGT_ADISC:
+ return FNIC_TPORT_ADISC_RSP;
+ case FNIC_FRAME_TYPE_TGT_LOGO:
+ if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping Unknown frame in tport solicited exchange range type: 0x%x.",
+ fchdr->fh_type);
+ return -1;
+ }
+ return FNIC_TPORT_LOGO_RSP;
+ default:
+ /* Drop the Rx frame and log/stats it */
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Solicited response: unknown OXID: 0x%x", oxid);
+ return -1;
+ }
+
+ return -1;
+}
+
+void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
+ int len, int fchdr_offset)
+{
+ struct fc_frame_header *fchdr;
+ uint32_t s_id = 0;
+ uint32_t d_id = 0;
+ struct fnic *fnic = iport->fnic;
+ int frame_type;
+
+ fchdr = (struct fc_frame_header *) ((uint8_t *) rx_frame + fchdr_offset);
+ s_id = ntoh24(fchdr->fh_s_id);
+ d_id = ntoh24(fchdr->fh_d_id);
+
+ fnic_debug_dump_fc_frame(fnic, fchdr, len, "Incoming");
+
+ frame_type =
+ fnic_fdls_validate_and_get_frame_type(iport, fchdr);
+
+ /*if we are in flogo drop everything else */
+ if (iport->fabric.state == FDLS_STATE_FABRIC_LOGO &&
+ frame_type != FNIC_FABRIC_LOGO_RSP)
+ return;
+
+ switch (frame_type) {
+ case FNIC_FABRIC_FLOGI_RSP:
+ fdls_process_flogi_rsp(iport, fchdr, rx_frame);
+ break;
+ case FNIC_FABRIC_PLOGI_RSP:
+ fdls_process_fabric_plogi_rsp(iport, fchdr);
+ break;
+ case FNIC_FDMI_PLOGI_RSP:
+ fdls_process_fdmi_plogi_rsp(iport, fchdr);
+ break;
+ case FNIC_FABRIC_RPN_RSP:
+ fdls_process_rpn_id_rsp(iport, fchdr);
+ break;
+ case FNIC_FABRIC_RFT_RSP:
+ fdls_process_rft_id_rsp(iport, fchdr);
+ break;
+ case FNIC_FABRIC_RFF_RSP:
+ fdls_process_rff_id_rsp(iport, fchdr);
+ break;
+ case FNIC_FABRIC_SCR_RSP:
+ fdls_process_scr_rsp(iport, fchdr);
+ break;
+ case FNIC_FABRIC_GPN_FT_RSP:
+ fdls_process_gpn_ft_rsp(iport, fchdr, len);
+ break;
+ case FNIC_TPORT_PLOGI_RSP:
+ fdls_process_tgt_plogi_rsp(iport, fchdr);
+ break;
+ case FNIC_TPORT_PRLI_RSP:
+ fdls_process_tgt_prli_rsp(iport, fchdr);
+ break;
+ case FNIC_TPORT_ADISC_RSP:
+ fdls_process_tgt_adisc_rsp(iport, fchdr);
+ break;
+ case FNIC_TPORT_BLS_ABTS_RSP:
+ fdls_process_tgt_abts_rsp(iport, fchdr);
+ break;
+ case FNIC_TPORT_LOGO_RSP:
+ /* Logo response from tgt which we have deleted */
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Logo response from tgt: 0x%x",
+ ntoh24(fchdr->fh_s_id));
+ break;
+ case FNIC_FABRIC_LOGO_RSP:
+ fdls_process_fabric_logo_rsp(iport, fchdr);
+ break;
+ case FNIC_FABRIC_BLS_ABTS_RSP:
+ fdls_process_fabric_abts_rsp(iport, fchdr);
+ break;
+ case FNIC_FDMI_BLS_ABTS_RSP:
+ fdls_process_fdmi_abts_rsp(iport, fchdr);
+ break;
+ case FNIC_BLS_ABTS_REQ:
+ fdls_process_abts_req(iport, fchdr);
+ break;
+ case FNIC_ELS_UNSUPPORTED_REQ:
+ fdls_process_unsupported_els_req(iport, fchdr);
+ break;
+ case FNIC_ELS_PLOGI_REQ:
+ fdls_process_plogi_req(iport, fchdr);
+ break;
+ case FNIC_ELS_RSCN_REQ:
+ fdls_process_rscn(iport, fchdr);
+ break;
+ case FNIC_ELS_LOGO_REQ:
+ fdls_process_logo_req(iport, fchdr);
+ break;
+ case FNIC_ELS_RRQ:
+ case FNIC_ELS_ECHO_REQ:
+ fdls_process_els_req(iport, fchdr, len);
+ break;
+ case FNIC_ELS_ADISC:
+ fdls_process_adisc_req(iport, fchdr);
+ break;
+ case FNIC_ELS_RLS:
+ fdls_process_rls_req(iport, fchdr);
+ break;
+ case FNIC_FDMI_REG_HBA_RSP:
+ case FNIC_FDMI_RPA_RSP:
+ fdls_process_fdmi_reg_ack(iport, fchdr, frame_type);
+ break;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "s_id: 0x%x d_did: 0x%x", s_id, d_id);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Received unknown FCoE frame of len: %d. Dropping frame", len);
+ break;
+ }
+}
+
+void fnic_fdls_disc_init(struct fnic_iport_s *iport)
+{
+ fdls_reset_oxid_pool(iport);
+ fdls_set_state((&iport->fabric), FDLS_STATE_INIT);
+}
+
+void fnic_fdls_link_down(struct fnic_iport_s *iport)
+{
+ struct fnic_tport_s *tport, *next;
+ struct fnic *fnic = iport->fnic;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS processing link down", iport->fcid);
+
+ fdls_set_state((&iport->fabric), FDLS_STATE_LINKDOWN);
+ iport->fabric.flags = 0;
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ fnic_scsi_fcpio_reset(iport->fnic);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "removing rport: 0x%x", tport->fcid);
+ fdls_delete_tport(iport, tport);
+ }
+
+ if (fnic_fdmi_support == 1) {
+ if (iport->fabric.fdmi_pending > 0) {
+ timer_delete_sync(&iport->fabric.fdmi_timer);
+ iport->fabric.fdmi_pending = 0;
+ }
+ iport->flags &= ~FNIC_FDMI_ACTIVE;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "0x%x: FDLS finish processing link down", iport->fcid);
+}
diff --git a/drivers/scsi/fnic/fdls_fc.h b/drivers/scsi/fnic/fdls_fc.h
new file mode 100644
index 000000000000..012f43afd083
--- /dev/null
+++ b/drivers/scsi/fnic/fdls_fc.h
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ */
+
+#ifndef _FDLS_FC_H_
+#define _FDLS_FC_H_
+
+/* This file contains the declarations for FC fabric services
+ * and target discovery
+ *
+ * Request and Response for
+ * 1. FLOGI
+ * 2. PLOGI to Fabric Controller
+ * 3. GPN_ID, GPN_FT
+ * 4. RSCN
+ * 5. PLOGI to Target
+ * 6. PRLI to Target
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/fc/fc_els.h>
+#include <uapi/scsi/fc/fc_fs.h>
+#include <uapi/scsi/fc/fc_ns.h>
+#include <uapi/scsi/fc/fc_gs.h>
+#include <uapi/linux/if_ether.h>
+#include <scsi/fc/fc_ms.h>
+#include <linux/minmax.h>
+#include <linux/if_ether.h>
+#include <scsi/fc/fc_encaps.h>
+#include <scsi/fc/fc_fcoe.h>
+
+#define FDLS_MIN_FRAMES (32)
+#define FDLS_MIN_FRAME_ELEM (4)
+#define FNIC_FCP_SP_RD_XRDY_DIS 0x00000002
+#define FNIC_FCP_SP_TARGET 0x00000010
+#define FNIC_FCP_SP_INITIATOR 0x00000020
+#define FNIC_FCP_SP_CONF_CMPL 0x00000080
+#define FNIC_FCP_SP_RETRY 0x00000100
+
+#define FNIC_FC_CONCUR_SEQS (0xFF)
+#define FNIC_FC_RO_INFO (0x1F)
+
+/* Little Endian */
+#define FNIC_UNASSIGNED_OXID (0xffff)
+#define FNIC_UNASSIGNED_RXID (0xffff)
+#define FNIC_ELS_REQ_FCTL (0x000029)
+#define FNIC_ELS_REP_FCTL (0x000099)
+
+#define FNIC_FCP_RSP_FCTL (0x000099)
+#define FNIC_REQ_ABTS_FCTL (0x000009)
+
+#define FNIC_FC_PH_VER_HI (0x20)
+#define FNIC_FC_PH_VER_LO (0x20)
+#define FNIC_FC_PH_VER (0x2020)
+#define FNIC_FC_B2B_CREDIT (0x0A)
+#define FNIC_FC_B2B_RDF_SZ (0x0800)
+
+#define FNIC_LOGI_RDF_SIZE(_logi) ((_logi).fl_csp.sp_bb_data)
+#define FNIC_LOGI_R_A_TOV(_logi) ((_logi).fl_csp.sp_r_a_tov)
+#define FNIC_LOGI_E_D_TOV(_logi) ((_logi).fl_csp.sp_e_d_tov)
+#define FNIC_LOGI_FEATURES(_logi) (be16_to_cpu((_logi).fl_csp.sp_features))
+#define FNIC_LOGI_PORT_NAME(_logi) ((_logi).fl_wwpn)
+#define FNIC_LOGI_NODE_NAME(_logi) ((_logi).fl_wwnn)
+
+#define FNIC_LOGI_SET_RDF_SIZE(_logi, _rdf_size) \
+ (FNIC_LOGI_RDF_SIZE(_logi) = cpu_to_be16(_rdf_size))
+#define FNIC_LOGI_SET_E_D_TOV(_logi, _e_d_tov) \
+ (FNIC_LOGI_E_D_TOV(_logi) = cpu_to_be32(_e_d_tov))
+#define FNIC_LOGI_SET_R_A_TOV(_logi, _r_a_tov) \
+ (FNIC_LOGI_R_A_TOV(_logi) = cpu_to_be32(_r_a_tov))
+
+#define FNIC_STD_SET_S_ID(_fchdr, _sid) memcpy((_fchdr).fh_s_id, _sid, 3)
+#define FNIC_STD_SET_D_ID(_fchdr, _did) memcpy((_fchdr).fh_d_id, _did, 3)
+#define FNIC_STD_SET_OX_ID(_fchdr, _oxid) ((_fchdr).fh_ox_id = cpu_to_be16(_oxid))
+#define FNIC_STD_SET_RX_ID(_fchdr, _rxid) ((_fchdr).fh_rx_id = cpu_to_be16(_rxid))
+
+#define FNIC_STD_SET_R_CTL(_fchdr, _rctl) ((_fchdr).fh_r_ctl = _rctl)
+#define FNIC_STD_SET_TYPE(_fchdr, _type) ((_fchdr).fh_type = _type)
+#define FNIC_STD_SET_F_CTL(_fchdr, _fctl) \
+ put_unaligned_be24(_fctl, &((_fchdr).fh_f_ctl))
+
+#define FNIC_STD_SET_NPORT_NAME(_ptr, _wwpn) put_unaligned_be64(_wwpn, _ptr)
+#define FNIC_STD_SET_NODE_NAME(_ptr, _wwnn) put_unaligned_be64(_wwnn, _ptr)
+#define FNIC_STD_SET_PORT_ID(__req, __portid) \
+ memcpy(__req.fr_fid.fp_fid, __portid, 3)
+#define FNIC_STD_SET_PORT_NAME(_req, _pName) \
+ (put_unaligned_be64(_pName, &_req.fr_wwn))
+
+#define FNIC_STD_GET_OX_ID(_fchdr) (be16_to_cpu((_fchdr)->fh_ox_id))
+#define FNIC_STD_GET_RX_ID(_fchdr) (be16_to_cpu((_fchdr)->fh_rx_id))
+#define FNIC_STD_GET_S_ID(_fchdr) ((_fchdr)->fh_s_id)
+#define FNIC_STD_GET_D_ID(_fchdr) ((_fchdr)->fh_d_id)
+#define FNIC_STD_GET_TYPE(_fchdr) ((_fchdr)->fh_type)
+#define FNIC_STD_GET_F_CTL(_fchdr) ((_fchdr)->fh_f_ctl)
+#define FNIC_STD_GET_R_CTL(_fchdr) ((_fchdr)->fh_r_ctl)
+
+#define FNIC_STD_GET_FC_CT_CMD(__fcct_hdr) (be16_to_cpu(__fcct_hdr->ct_cmd))
+
+#define FNIC_FCOE_MAX_FRAME_SZ (2048)
+#define FNIC_FCOE_MIN_FRAME_SZ (280)
+#define FNIC_FC_MAX_PAYLOAD_LEN (2048)
+#define FNIC_MIN_DATA_FIELD_SIZE (256)
+
+#define FNIC_FC_EDTOV_NSEC (0x400)
+#define FNIC_NSEC_TO_MSEC (0x1000000)
+#define FCP_PRLI_FUNC_TARGET (0x0010)
+
+#define FNIC_FC_R_CTL_SOLICITED_DATA (0x21)
+#define FNIC_FC_F_CTL_LAST_END_SEQ (0x98)
+#define FNIC_FC_F_CTL_LAST_END_SEQ_INT (0x99)
+#define FNIC_FC_F_CTL_FIRST_LAST_SEQINIT (0x29)
+#define FNIC_FC_R_CTL_FC4_SCTL (0x03)
+#define FNIC_FC_CS_CTL (0x00)
+
+#define FNIC_FC_FRAME_UNSOLICITED(_fchdr) \
+ (_fchdr->fh_r_ctl == FC_RCTL_ELS_REQ)
+#define FNIC_FC_FRAME_SOLICITED_DATA(_fchdr) \
+ (_fchdr->fh_r_ctl == FNIC_FC_R_CTL_SOLICITED_DATA)
+#define FNIC_FC_FRAME_SOLICITED_CTRL_REPLY(_fchdr) \
+ (_fchdr->fh_r_ctl == FC_RCTL_ELS_REP)
+#define FNIC_FC_FRAME_FCTL_LAST_END_SEQ(_fchdr) \
+ (_fchdr->fh_f_ctl[0] == FNIC_FC_F_CTL_LAST_END_SEQ)
+#define FNIC_FC_FRAME_FCTL_LAST_END_SEQ_INT(_fchdr) \
+ (_fchdr->fh_f_ctl[0] == FNIC_FC_F_CTL_LAST_END_SEQ_INT)
+#define FNIC_FC_FRAME_FCTL_FIRST_LAST_SEQINIT(_fchdr) \
+ (_fchdr->fh_f_ctl[0] == FNIC_FC_F_CTL_FIRST_LAST_SEQINIT)
+#define FNIC_FC_FRAME_FC4_SCTL(_fchdr) \
+ (_fchdr->fh_r_ctl == FNIC_FC_R_CTL_FC4_SCTL)
+#define FNIC_FC_FRAME_TYPE_BLS(_fchdr) (_fchdr->fh_type == FC_TYPE_BLS)
+#define FNIC_FC_FRAME_TYPE_ELS(_fchdr) (_fchdr->fh_type == FC_TYPE_ELS)
+#define FNIC_FC_FRAME_TYPE_FC_GS(_fchdr) (_fchdr->fh_type == FC_TYPE_CT)
+#define FNIC_FC_FRAME_CS_CTL(_fchdr) (_fchdr->fh_cs_ctl == FNIC_FC_CS_CTL)
+
+#define FNIC_FC_C3_RDF (0xfff)
+#define FNIC_FC_PLOGI_RSP_RDF(_plogi_rsp) \
+ (min(_plogi_rsp->u.csp_plogi.b2b_rdf_size, \
+ (_plogi_rsp->spc3[4] & FNIC_FC_C3_RDF)))
+#define FNIC_FC_PLOGI_RSP_CONCUR_SEQ(_plogi_rsp) \
+ (min((uint16_t) (be16_to_cpu(_plogi_rsp->els.fl_csp.sp_tot_seq)), \
+ (uint16_t) (be16_to_cpu(_plogi_rsp->els.fl_cssp[2].cp_con_seq) & 0xff)))
+
+/* FLOGI/PLOGI struct */
+struct fc_std_flogi {
+ struct fc_frame_header fchdr;
+ struct fc_els_flogi els;
+} __packed;
+
+struct fc_std_els_acc_rsp {
+ struct fc_frame_header fchdr;
+ struct fc_els_ls_acc acc;
+} __packed;
+
+struct fc_std_els_rjt_rsp {
+ struct fc_frame_header fchdr;
+ struct fc_els_ls_rjt rej;
+} __packed;
+
+struct fc_std_els_adisc {
+ struct fc_frame_header fchdr;
+ struct fc_els_adisc els;
+} __packed;
+
+struct fc_std_rls_acc {
+ struct fc_frame_header fchdr;
+ struct fc_els_rls_resp els;
+} __packed;
+
+struct fc_std_abts_ba_acc {
+ struct fc_frame_header fchdr;
+ struct fc_ba_acc acc;
+} __packed;
+
+struct fc_std_abts_ba_rjt {
+ struct fc_frame_header fchdr;
+ struct fc_ba_rjt rjt;
+} __packed;
+
+struct fc_std_els_prli {
+ struct fc_frame_header fchdr;
+ struct fc_els_prli els_prli;
+ struct fc_els_spp sp;
+} __packed;
+
+struct fc_std_rpn_id {
+ struct fc_frame_header fchdr;
+ struct fc_ct_hdr fc_std_ct_hdr;
+ struct fc_ns_rn_id rpn_id;
+} __packed;
+
+struct fc_std_fdmi_rhba {
+ struct fc_frame_header fchdr;
+ struct fc_ct_hdr fc_std_ct_hdr;
+ struct fc_fdmi_rhba rhba;
+} __packed;
+
+struct fc_std_fdmi_rpa {
+ struct fc_frame_header fchdr;
+ struct fc_ct_hdr fc_std_ct_hdr;
+ struct fc_fdmi_rpa rpa;
+} __packed;
+
+struct fc_std_rft_id {
+ struct fc_frame_header fchdr;
+ struct fc_ct_hdr fc_std_ct_hdr;
+ struct fc_ns_rft_id rft_id;
+} __packed;
+
+struct fc_std_rff_id {
+ struct fc_frame_header fchdr;
+ struct fc_ct_hdr fc_std_ct_hdr;
+ struct fc_ns_rff_id rff_id;
+} __packed;
+
+struct fc_std_gpn_ft {
+ struct fc_frame_header fchdr;
+ struct fc_ct_hdr fc_std_ct_hdr;
+ struct fc_ns_gid_ft gpn_ft;
+} __packed;
+
+/* Accept CT_IU for GPN_FT */
+struct fc_gpn_ft_rsp_iu {
+ uint8_t ctrl;
+ uint8_t fcid[3];
+ uint32_t rsvd;
+ __be64 wwpn;
+} __packed;
+
+struct fc_std_rls {
+ struct fc_frame_header fchdr;
+ struct fc_els_rls els;
+} __packed;
+
+struct fc_std_scr {
+ struct fc_frame_header fchdr;
+ struct fc_els_scr scr;
+} __packed;
+
+struct fc_std_rscn {
+ struct fc_frame_header fchdr;
+ struct fc_els_rscn els;
+} __packed;
+
+struct fc_std_logo {
+ struct fc_frame_header fchdr;
+ struct fc_els_logo els;
+} __packed;
+
+#define FNIC_ETH_FCOE_HDRS_OFFSET \
+ (sizeof(struct ethhdr) + sizeof(struct fcoe_hdr))
+
+#endif /* _FDLS_FC_H */
diff --git a/drivers/scsi/fnic/fip.c b/drivers/scsi/fnic/fip.c
new file mode 100644
index 000000000000..ce62ab1180bd
--- /dev/null
+++ b/drivers/scsi/fnic/fip.c
@@ -0,0 +1,1005 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ */
+#include "fnic.h"
+#include "fip.h"
+#include <linux/etherdevice.h>
+
+#define FIP_FNIC_RESET_WAIT_COUNT 15
+
+/**
+ * fnic_fcoe_reset_vlans - Free up the list of discovered vlans
+ * @fnic: Handle to fnic driver instance
+ */
+void fnic_fcoe_reset_vlans(struct fnic *fnic)
+{
+ unsigned long flags;
+ struct fcoe_vlan *vlan, *next;
+
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+ if (!list_empty(&fnic->vlan_list)) {
+ list_for_each_entry_safe(vlan, next, &fnic->vlan_list, list) {
+ list_del(&vlan->list);
+ kfree(vlan);
+ }
+ }
+
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Reset vlan complete\n");
+}
+
+/**
+ * fnic_fcoe_send_vlan_req - Send FIP vlan request to all FCFs MAC
+ * @fnic: Handle to fnic driver instance
+ */
+void fnic_fcoe_send_vlan_req(struct fnic *fnic)
+{
+ uint8_t *frame;
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ u64 vlan_tov;
+ struct fip_vlan_req *pvlan_req;
+ uint16_t frame_size = sizeof(struct fip_vlan_req);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send VLAN req");
+ return;
+ }
+
+ fnic_fcoe_reset_vlans(fnic);
+
+ fnic->set_vlan(fnic, 0);
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "set vlan done\n");
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "got MAC 0x%x:%x:%x:%x:%x:%x\n", iport->hwmac[0],
+ iport->hwmac[1], iport->hwmac[2], iport->hwmac[3],
+ iport->hwmac[4], iport->hwmac[5]);
+
+ pvlan_req = (struct fip_vlan_req *) frame;
+ *pvlan_req = (struct fip_vlan_req) {
+ .eth = {.h_dest = FCOE_ALL_FCFS_MAC,
+ .h_proto = cpu_to_be16(ETH_P_FIP)},
+ .fip = {.fip_ver = FIP_VER_ENCAPS(FIP_VER),
+ .fip_op = cpu_to_be16(FIP_OP_VLAN),
+ .fip_subcode = FIP_SC_REQ,
+ .fip_dl_len = cpu_to_be16(FIP_VLAN_REQ_LEN)},
+ .mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC,
+ .fip_dlen = 2}}
+ };
+
+ memcpy(pvlan_req->eth.h_source, iport->hwmac, ETH_ALEN);
+ memcpy(pvlan_req->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
+
+ atomic64_inc(&fnic_stats->vlan_stats.vlan_disc_reqs);
+
+ iport->fip.state = FDLS_FIP_VLAN_DISCOVERY_STARTED;
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Send VLAN req\n");
+ fnic_send_fip_frame(iport, frame, frame_size);
+
+ vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV);
+ mod_timer(&fnic->retry_fip_timer, round_jiffies(vlan_tov));
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fip timer set\n");
+}
+
+/**
+ * fnic_fcoe_process_vlan_resp - Processes the vlan response from one FCF and
+ * populates VLAN list.
+ * @fnic: Handle to fnic driver instance
+ * @fiph: Received FIP frame
+ *
+ * Will wait for responses from multiple FCFs until timeout.
+ */
+void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph)
+{
+ struct fip_vlan_notif *vlan_notif = (struct fip_vlan_notif *)fiph;
+
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ u16 vid;
+ int num_vlan = 0;
+ int cur_desc, desc_len;
+ struct fcoe_vlan *vlan;
+ struct fip_vlan_desc *vlan_desc;
+ unsigned long flags;
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p got vlan resp\n", fnic);
+
+ desc_len = be16_to_cpu(vlan_notif->fip.fip_dl_len);
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "desc_len %d\n", desc_len);
+
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+
+ cur_desc = 0;
+ while (desc_len > 0) {
+ vlan_desc =
+ (struct fip_vlan_desc *)(((char *)vlan_notif->vlans_desc)
+ + cur_desc * 4);
+
+ if (vlan_desc->fd_desc.fip_dtype == FIP_DT_VLAN) {
+ if (vlan_desc->fd_desc.fip_dlen != 1) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "Invalid descriptor length(%x) in VLan response\n",
+ vlan_desc->fd_desc.fip_dlen);
+
+ }
+ num_vlan++;
+ vid = be16_to_cpu(vlan_desc->fd_vlan);
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "process_vlan_resp: FIP VLAN %d\n", vid);
+ vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
+
+ if (!vlan) {
+ /* retry from timer */
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "Mem Alloc failure\n");
+ spin_unlock_irqrestore(&fnic->vlans_lock,
+ flags);
+ goto out;
+ }
+ vlan->vid = vid & 0x0fff;
+ vlan->state = FIP_VLAN_AVAIL;
+ list_add_tail(&vlan->list, &fnic->vlan_list);
+ break;
+ }
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "Invalid descriptor type(%x) in VLan response\n",
+ vlan_desc->fd_desc.fip_dtype);
+ /*
+ * Note : received a type=2 descriptor here i.e. FIP
+ * MAC Address Descriptor
+ */
+ cur_desc += vlan_desc->fd_desc.fip_dlen;
+ desc_len -= vlan_desc->fd_desc.fip_dlen;
+ }
+
+ /* any VLAN descriptors present ? */
+ if (num_vlan == 0) {
+ atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID);
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p No VLAN descriptors in FIP VLAN response\n",
+ fnic);
+ }
+
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+
+ out:
+ return;
+}
+
+/**
+ * fnic_fcoe_start_fcf_discovery - Start FIP FCF discovery in a selected vlan
+ * @fnic: Handle to fnic driver instance
+ */
+void fnic_fcoe_start_fcf_discovery(struct fnic *fnic)
+{
+ uint8_t *frame;
+ struct fnic_iport_s *iport = &fnic->iport;
+ u64 fcs_tov;
+ struct fip_discovery *pdisc_sol;
+ uint16_t frame_size = sizeof(struct fip_discovery);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to start FCF discovery");
+ return;
+ }
+
+ eth_zero_addr(iport->selected_fcf.fcf_mac);
+
+ pdisc_sol = (struct fip_discovery *) frame;
+ *pdisc_sol = (struct fip_discovery) {
+ .eth = {.h_dest = FCOE_ALL_FCFS_MAC,
+ .h_proto = cpu_to_be16(ETH_P_FIP)},
+ .fip = {
+ .fip_ver = FIP_VER_ENCAPS(FIP_VER), .fip_op = cpu_to_be16(FIP_OP_DISC),
+ .fip_subcode = FIP_SC_REQ, .fip_dl_len = cpu_to_be16(FIP_DISC_SOL_LEN),
+ .fip_flags = cpu_to_be16(FIP_FL_FPMA)},
+ .mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}},
+ .name_desc = {.fd_desc = {.fip_dtype = FIP_DT_NAME, .fip_dlen = 3}},
+ .fcoe_desc = {.fd_desc = {.fip_dtype = FIP_DT_FCOE_SIZE, .fip_dlen = 1},
+ .fd_size = cpu_to_be16(FCOE_MAX_SIZE)}
+ };
+
+ memcpy(pdisc_sol->eth.h_source, iport->hwmac, ETH_ALEN);
+ memcpy(pdisc_sol->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
+ iport->selected_fcf.fcf_priority = 0xFF;
+
+ FNIC_STD_SET_NODE_NAME(&pdisc_sol->name_desc.fd_wwn, iport->wwnn);
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Start FCF discovery\n");
+ fnic_send_fip_frame(iport, frame, frame_size);
+
+ iport->fip.state = FDLS_FIP_FCF_DISCOVERY_STARTED;
+
+ fcs_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FCS_TOV);
+ mod_timer(&fnic->retry_fip_timer, round_jiffies(fcs_tov));
+}
+
+/**
+ * fnic_fcoe_fip_discovery_resp - Processes FCF advertisements.
+ * @fnic: Handle to fnic driver instance
+ * @fiph: Received frame
+ *
+ * FCF advertisements can be:
+ * solicited - Sent in response of a discover FCF FIP request
+ * Store the information of the FCF with highest priority.
+ * Wait until timeout in case of multiple FCFs.
+ *
+ * unsolicited - Sent periodically by the FCF for keep alive.
+ * If FLOGI is in progress or completed and the advertisement is
+ * received by our selected FCF, refresh the keep alive timer.
+ */
+void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph)
+{
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fip_disc_adv *disc_adv = (struct fip_disc_adv *)fiph;
+ u64 fcs_ka_tov;
+ u64 tov;
+ int fka_has_changed;
+
+ switch (iport->fip.state) {
+ case FDLS_FIP_FCF_DISCOVERY_STARTED:
+ if (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_SOL) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "fnic 0x%p Solicited adv\n", fnic);
+
+ if ((disc_adv->prio_desc.fd_pri <
+ iport->selected_fcf.fcf_priority)
+ && (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_AVAIL)) {
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "fnic 0x%p FCF Available\n", fnic);
+ memcpy(iport->selected_fcf.fcf_mac,
+ disc_adv->mac_desc.fd_mac, ETH_ALEN);
+ iport->selected_fcf.fcf_priority =
+ disc_adv->prio_desc.fd_pri;
+ iport->selected_fcf.fka_adv_period =
+ be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period);
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num, "adv time %d",
+ iport->selected_fcf.fka_adv_period);
+ iport->selected_fcf.ka_disabled =
+ (disc_adv->fka_adv_desc.fd_flags & 1);
+ }
+ }
+ break;
+ case FDLS_FIP_FLOGI_STARTED:
+ case FDLS_FIP_FLOGI_COMPLETE:
+ if (!(be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_SOL)) {
+ /* same fcf */
+ if (memcmp
+ (iport->selected_fcf.fcf_mac,
+ disc_adv->mac_desc.fd_mac, ETH_ALEN) == 0) {
+ if (iport->selected_fcf.fka_adv_period !=
+ be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period)) {
+ iport->selected_fcf.fka_adv_period =
+ be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period);
+ FNIC_FIP_DBG(KERN_INFO,
+ fnic->host,
+ fnic->fnic_num,
+ "change fka to %d",
+ iport->selected_fcf.fka_adv_period);
+ }
+
+ fka_has_changed =
+ (iport->selected_fcf.ka_disabled == 1)
+ && ((disc_adv->fka_adv_desc.fd_flags & 1) ==
+ 0);
+
+ iport->selected_fcf.ka_disabled =
+ (disc_adv->fka_adv_desc.fd_flags & 1);
+ if (!((iport->selected_fcf.ka_disabled)
+ || (iport->selected_fcf.fka_adv_period ==
+ 0))) {
+
+ fcs_ka_tov = jiffies
+ + 3
+ *
+ msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
+ mod_timer(&fnic->fcs_ka_timer,
+ round_jiffies(fcs_ka_tov));
+ } else {
+ if (timer_pending(&fnic->fcs_ka_timer))
+ timer_delete_sync(&fnic->fcs_ka_timer);
+ }
+
+ if (fka_has_changed) {
+ if (iport->selected_fcf.fka_adv_period != 0) {
+ tov =
+ jiffies +
+ msecs_to_jiffies(
+ iport->selected_fcf.fka_adv_period);
+ mod_timer(&fnic->enode_ka_timer,
+ round_jiffies(tov));
+
+ tov =
+ jiffies +
+ msecs_to_jiffies
+ (FIP_VN_KA_PERIOD);
+ mod_timer(&fnic->vn_ka_timer,
+ round_jiffies(tov));
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ } /* end switch */
+}
+
+/**
+ * fnic_fcoe_start_flogi - Send FIP FLOGI to the selected FCF
+ * @fnic: Handle to fnic driver instance
+ */
+void fnic_fcoe_start_flogi(struct fnic *fnic)
+{
+ uint8_t *frame;
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fip_flogi *pflogi_req;
+ u64 flogi_tov;
+ uint16_t oxid;
+ uint16_t frame_size = sizeof(struct fip_flogi);
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to start FIP FLOGI");
+ return;
+ }
+
+ pflogi_req = (struct fip_flogi *) frame;
+ *pflogi_req = (struct fip_flogi) {
+ .eth = {
+ .h_proto = cpu_to_be16(ETH_P_FIP)},
+ .fip = {
+ .fip_ver = FIP_VER_ENCAPS(FIP_VER),
+ .fip_op = cpu_to_be16(FIP_OP_LS),
+ .fip_subcode = FIP_SC_REQ,
+ .fip_dl_len = cpu_to_be16(FIP_FLOGI_LEN),
+ .fip_flags = cpu_to_be16(FIP_FL_FPMA)},
+ .flogi_desc = {
+ .fd_desc = {.fip_dtype = FIP_DT_FLOGI, .fip_dlen = 36},
+ .flogi = {
+ .fchdr = {
+ .fh_r_ctl = FC_RCTL_ELS_REQ,
+ .fh_d_id = {0xFF, 0xFF, 0xFE},
+ .fh_type = FC_TYPE_ELS,
+ .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+ .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
+ .els = {
+ .fl_cmd = ELS_FLOGI,
+ .fl_csp = {
+ .sp_hi_ver =
+ FNIC_FC_PH_VER_HI,
+ .sp_lo_ver =
+ FNIC_FC_PH_VER_LO,
+ .sp_bb_cred =
+ cpu_to_be16
+ (FNIC_FC_B2B_CREDIT),
+ .sp_bb_data =
+ cpu_to_be16
+ (FNIC_FC_B2B_RDF_SZ)},
+ .fl_cssp[2].cp_class =
+ cpu_to_be16(FC_CPC_VALID | FC_CPC_SEQ)
+ },
+ }
+ },
+ .mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}}
+ };
+
+ memcpy(pflogi_req->eth.h_source, iport->hwmac, ETH_ALEN);
+ if (iport->usefip)
+ memcpy(pflogi_req->eth.h_dest, iport->selected_fcf.fcf_mac,
+ ETH_ALEN);
+
+ oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_FLOGI,
+ &iport->active_oxid_fabric_req);
+ if (oxid == FNIC_UNASSIGNED_OXID) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Failed to allocate OXID to send FIP FLOGI");
+ mempool_free(frame, fnic->frame_pool);
+ return;
+ }
+ FNIC_STD_SET_OX_ID(pflogi_req->flogi_desc.flogi.fchdr, oxid);
+
+ FNIC_STD_SET_NPORT_NAME(&pflogi_req->flogi_desc.flogi.els.fl_wwpn,
+ iport->wwpn);
+ FNIC_STD_SET_NODE_NAME(&pflogi_req->flogi_desc.flogi.els.fl_wwnn,
+ iport->wwnn);
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FIP start FLOGI\n");
+ fnic_send_fip_frame(iport, frame, frame_size);
+ iport->fip.flogi_retry++;
+
+ iport->fip.state = FDLS_FIP_FLOGI_STARTED;
+ flogi_tov = jiffies + msecs_to_jiffies(fnic->config.flogi_timeout);
+ mod_timer(&fnic->retry_fip_timer, round_jiffies(flogi_tov));
+}
+
+/**
+ * fnic_fcoe_process_flogi_resp - Processes FLOGI response from FCF.
+ * @fnic: Handle to fnic driver instance
+ * @fiph: Received frame
+ *
+ * If successful save assigned fc_id and MAC, program firmware
+ * and start fdls discovery, else restart vlan discovery.
+ */
+void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
+{
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fip_flogi_rsp *flogi_rsp = (struct fip_flogi_rsp *)fiph;
+ int desc_len;
+ uint32_t s_id;
+ int frame_type;
+ uint16_t oxid;
+
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ struct fc_frame_header *fchdr = &flogi_rsp->rsp_desc.flogi.fchdr;
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p FIP FLOGI rsp\n", fnic);
+ desc_len = be16_to_cpu(flogi_rsp->fip.fip_dl_len);
+ if (desc_len != 38) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Invalid Descriptor List len (%x). Dropping frame\n",
+ desc_len);
+ return;
+ }
+
+ if (!((flogi_rsp->rsp_desc.fd_desc.fip_dtype == 7)
+ && (flogi_rsp->rsp_desc.fd_desc.fip_dlen == 36))
+ || !((flogi_rsp->mac_desc.fd_desc.fip_dtype == 2)
+ && (flogi_rsp->mac_desc.fd_desc.fip_dlen == 2))) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping frame invalid type and len mix\n");
+ return;
+ }
+
+ frame_type = fnic_fdls_validate_and_get_frame_type(iport, fchdr);
+
+ s_id = ntoh24(fchdr->fh_s_id);
+ if ((fchdr->fh_f_ctl[0] != 0x98)
+ || (fchdr->fh_r_ctl != 0x23)
+ || (s_id != FC_FID_FLOGI)
+ || (frame_type != FNIC_FABRIC_FLOGI_RSP)
+ || (fchdr->fh_type != 0x01)) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping invalid frame: s_id %x F %x R %x t %x OX_ID %x\n",
+ s_id, fchdr->fh_f_ctl[0], fchdr->fh_r_ctl,
+ fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr));
+ return;
+ }
+
+ if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p rsp for pending FLOGI\n", fnic);
+
+ oxid = FNIC_STD_GET_OX_ID(fchdr);
+ fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
+ timer_delete_sync(&fnic->retry_fip_timer);
+
+ if ((be16_to_cpu(flogi_rsp->fip.fip_dl_len) == FIP_FLOGI_LEN)
+ && (flogi_rsp->rsp_desc.flogi.els.fl_cmd == ELS_LS_ACC)) {
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "fnic 0x%p FLOGI success\n", fnic);
+ memcpy(iport->fpma, flogi_rsp->mac_desc.fd_mac, ETH_ALEN);
+ iport->fcid =
+ ntoh24(flogi_rsp->rsp_desc.flogi.fchdr.fh_d_id);
+
+ iport->r_a_tov =
+ be32_to_cpu(flogi_rsp->rsp_desc.flogi.els.fl_csp.sp_r_a_tov);
+ iport->e_d_tov =
+ be32_to_cpu(flogi_rsp->rsp_desc.flogi.els.fl_csp.sp_e_d_tov);
+ memcpy(fnic->iport.fcfmac, iport->selected_fcf.fcf_mac,
+ ETH_ALEN);
+ vnic_dev_add_addr(fnic->vdev, flogi_rsp->mac_desc.fd_mac);
+
+ if (fnic_fdls_register_portid(iport, iport->fcid, NULL)
+ != 0) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "fnic 0x%p flogi registration failed\n",
+ fnic);
+ return;
+ }
+
+ iport->fip.state = FDLS_FIP_FLOGI_COMPLETE;
+ iport->state = FNIC_IPORT_STATE_FABRIC_DISC;
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num, "iport->state:%d\n",
+ iport->state);
+ fnic_fdls_disc_start(iport);
+ if (!((iport->selected_fcf.ka_disabled)
+ || (iport->selected_fcf.fka_adv_period == 0))) {
+ u64 tov;
+
+ tov = jiffies
+ +
+ msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
+ mod_timer(&fnic->enode_ka_timer,
+ round_jiffies(tov));
+
+ tov =
+ jiffies +
+ msecs_to_jiffies(FIP_VN_KA_PERIOD);
+ mod_timer(&fnic->vn_ka_timer,
+ round_jiffies(tov));
+
+ }
+ } else {
+ /*
+ * If there's FLOGI rejects - clear all
+ * fcf's & restart from scratch
+ */
+ atomic64_inc(&fnic_stats->vlan_stats.flogi_rejects);
+ /* start FCoE VLAN discovery */
+ fnic_fcoe_send_vlan_req(fnic);
+
+ iport->fip.state = FDLS_FIP_VLAN_DISCOVERY_STARTED;
+ }
+ }
+}
+
+/**
+ * fnic_common_fip_cleanup - Clean up FCF info and timers in case of
+ * link down/CVL
+ * @fnic: Handle to fnic driver instance
+ */
+void fnic_common_fip_cleanup(struct fnic *fnic)
+{
+
+ struct fnic_iport_s *iport = &fnic->iport;
+
+ if (!iport->usefip)
+ return;
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p fip cleanup\n", fnic);
+
+ iport->fip.state = FDLS_FIP_INIT;
+
+ timer_delete_sync(&fnic->retry_fip_timer);
+ timer_delete_sync(&fnic->fcs_ka_timer);
+ timer_delete_sync(&fnic->enode_ka_timer);
+ timer_delete_sync(&fnic->vn_ka_timer);
+
+ if (!is_zero_ether_addr(iport->fpma))
+ vnic_dev_del_addr(fnic->vdev, iport->fpma);
+
+ eth_zero_addr(iport->fpma);
+ iport->fcid = 0;
+ iport->r_a_tov = 0;
+ iport->e_d_tov = 0;
+ eth_zero_addr(fnic->iport.fcfmac);
+ eth_zero_addr(iport->selected_fcf.fcf_mac);
+ iport->selected_fcf.fcf_priority = 0;
+ iport->selected_fcf.fka_adv_period = 0;
+ iport->selected_fcf.ka_disabled = 0;
+
+ fnic_fcoe_reset_vlans(fnic);
+}
+
+/**
+ * fnic_fcoe_process_cvl - Processes Clear Virtual Link from FCF.
+ * @fnic: Handle to fnic driver instance
+ * @fiph: Received frame
+ *
+ * Verify that cvl is received from our current FCF for our assigned MAC
+ * and clean up and restart the vlan discovery.
+ */
+void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
+{
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fip_cvl *cvl_msg = (struct fip_cvl *)fiph;
+ int i;
+ int found = false;
+ int max_count = 0;
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p clear virtual link handler\n", fnic);
+
+ if (!((cvl_msg->fcf_mac_desc.fd_desc.fip_dtype == 2)
+ && (cvl_msg->fcf_mac_desc.fd_desc.fip_dlen == 2))
+ || !((cvl_msg->name_desc.fd_desc.fip_dtype == 4)
+ && (cvl_msg->name_desc.fd_desc.fip_dlen == 3))) {
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "invalid mix: ft %x fl %x ndt %x ndl %x",
+ cvl_msg->fcf_mac_desc.fd_desc.fip_dtype,
+ cvl_msg->fcf_mac_desc.fd_desc.fip_dlen,
+ cvl_msg->name_desc.fd_desc.fip_dtype,
+ cvl_msg->name_desc.fd_desc.fip_dlen);
+ }
+
+ if (memcmp
+ (iport->selected_fcf.fcf_mac, cvl_msg->fcf_mac_desc.fd_mac, ETH_ALEN)
+ == 0) {
+ for (i = 0; i < ((be16_to_cpu(fiph->fip_dl_len) / 5) - 1); i++) {
+ if (!((cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype == 11)
+ && (cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen == 5))) {
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num,
+ "Invalid type and len mix type: %d len: %d\n",
+ cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype,
+ cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen);
+ }
+ if (memcmp
+ (iport->fpma, cvl_msg->vn_ports_desc[i].fd_mac,
+ ETH_ALEN) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return;
+ fnic_common_fip_cleanup(fnic);
+
+ while (fnic->reset_in_progress == IN_PROGRESS) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ wait_for_completion_timeout(&fnic->reset_completion_wait,
+ msecs_to_jiffies(5000));
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ max_count++;
+ if (max_count >= FIP_FNIC_RESET_WAIT_COUNT) {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Rthr waited too long. Skipping handle link event %p\n",
+ fnic);
+ return;
+ }
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic reset in progress. Link event needs to wait %p",
+ fnic);
+ }
+ fnic->reset_in_progress = IN_PROGRESS;
+ fnic_fdls_link_down(iport);
+ fnic->reset_in_progress = NOT_IN_PROGRESS;
+ complete(&fnic->reset_completion_wait);
+ fnic_fcoe_send_vlan_req(fnic);
+ }
+}
+
+/**
+ * fdls_fip_recv_frame - Demultiplexer for FIP frames
+ * @fnic: Handle to fnic driver instance
+ * @frame: Received ethernet frame
+ */
+int fdls_fip_recv_frame(struct fnic *fnic, void *frame)
+{
+ struct ethhdr *eth = (struct ethhdr *)frame;
+ struct fip_header *fiph;
+ u16 op;
+ u8 sub;
+ int len = 2048;
+
+ if (be16_to_cpu(eth->h_proto) == ETH_P_FIP) {
+ fiph = (struct fip_header *)(eth + 1);
+ op = be16_to_cpu(fiph->fip_op);
+ sub = fiph->fip_subcode;
+
+ fnic_debug_dump_fip_frame(fnic, eth, len, "Incoming");
+
+ if (op == FIP_OP_DISC && sub == FIP_SC_REP)
+ fnic_fcoe_fip_discovery_resp(fnic, fiph);
+ else if (op == FIP_OP_VLAN && sub == FIP_SC_REP)
+ fnic_fcoe_process_vlan_resp(fnic, fiph);
+ else if (op == FIP_OP_CTRL && sub == FIP_SC_REP)
+ fnic_fcoe_process_cvl(fnic, fiph);
+ else if (op == FIP_OP_LS && sub == FIP_SC_REP)
+ fnic_fcoe_process_flogi_resp(fnic, fiph);
+
+ /* Return true if the frame was a FIP frame */
+ return true;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Not a FIP Frame");
+ return false;
+}
+
+void fnic_work_on_fip_timer(struct work_struct *work)
+{
+ struct fnic *fnic = container_of(work, struct fnic, fip_timer_work);
+ struct fnic_iport_s *iport = &fnic->iport;
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FIP timeout\n");
+
+ if (iport->fip.state == FDLS_FIP_VLAN_DISCOVERY_STARTED) {
+ fnic_vlan_discovery_timeout(fnic);
+ } else if (iport->fip.state == FDLS_FIP_FCF_DISCOVERY_STARTED) {
+ u8 zmac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FCF Discovery timeout\n");
+ if (memcmp(iport->selected_fcf.fcf_mac, zmac, ETH_ALEN) != 0) {
+
+ if (iport->flags & FNIC_FIRST_LINK_UP) {
+ fnic_scsi_fcpio_reset(iport->fnic);
+ iport->flags &= ~FNIC_FIRST_LINK_UP;
+ }
+
+ fnic_fcoe_start_flogi(fnic);
+ if (!((iport->selected_fcf.ka_disabled)
+ || (iport->selected_fcf.fka_adv_period == 0))) {
+ u64 fcf_tov;
+
+ fcf_tov = jiffies
+ + 3
+ *
+ msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
+ mod_timer(&fnic->fcs_ka_timer,
+ round_jiffies(fcf_tov));
+ }
+ } else {
+ FNIC_FIP_DBG(KERN_INFO, fnic->host,
+ fnic->fnic_num, "FCF Discovery timeout\n");
+ fnic_vlan_discovery_timeout(fnic);
+ }
+ } else if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) {
+ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI timeout\n");
+ if (iport->fip.flogi_retry < fnic->config.flogi_retries)
+ fnic_fcoe_start_flogi(fnic);
+ else
+ fnic_vlan_discovery_timeout(fnic);
+ }
+}
+
+/**
+ * fnic_handle_fip_timer - Timeout handler for FIP discover phase.
+ * @t: Handle to the timer list
+ *
+ * Based on the current state, start next phase or restart discovery.
+ */
+void fnic_handle_fip_timer(struct timer_list *t)
+{
+ struct fnic *fnic = timer_container_of(fnic, t, retry_fip_timer);
+
+ INIT_WORK(&fnic->fip_timer_work, fnic_work_on_fip_timer);
+ queue_work(fnic_fip_queue, &fnic->fip_timer_work);
+}
+
+/**
+ * fnic_handle_enode_ka_timer - FIP node keep alive.
+ * @t: Handle to the timer list
+ */
+void fnic_handle_enode_ka_timer(struct timer_list *t)
+{
+ uint8_t *frame;
+ struct fnic *fnic = timer_container_of(fnic, t, enode_ka_timer);
+
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fip_enode_ka *penode_ka;
+ u64 enode_ka_tov;
+ uint16_t frame_size = sizeof(struct fip_enode_ka);
+
+ if (iport->fip.state != FDLS_FIP_FLOGI_COMPLETE)
+ return;
+
+ if ((iport->selected_fcf.ka_disabled)
+ || (iport->selected_fcf.fka_adv_period == 0)) {
+ return;
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send enode ka");
+ return;
+ }
+
+ penode_ka = (struct fip_enode_ka *) frame;
+ *penode_ka = (struct fip_enode_ka) {
+ .eth = {
+ .h_proto = cpu_to_be16(ETH_P_FIP)},
+ .fip = {
+ .fip_ver = FIP_VER_ENCAPS(FIP_VER),
+ .fip_op = cpu_to_be16(FIP_OP_CTRL),
+ .fip_subcode = FIP_SC_REQ,
+ .fip_dl_len = cpu_to_be16(FIP_ENODE_KA_LEN)},
+ .mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}}
+ };
+
+ memcpy(penode_ka->eth.h_source, iport->hwmac, ETH_ALEN);
+ memcpy(penode_ka->eth.h_dest, iport->selected_fcf.fcf_mac, ETH_ALEN);
+ memcpy(penode_ka->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
+
+ FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Handle enode KA timer\n");
+ fnic_send_fip_frame(iport, frame, frame_size);
+ enode_ka_tov = jiffies
+ + msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
+ mod_timer(&fnic->enode_ka_timer, round_jiffies(enode_ka_tov));
+}
+
+/**
+ * fnic_handle_vn_ka_timer - FIP virtual port keep alive.
+ * @t: Handle to the timer list
+ */
+void fnic_handle_vn_ka_timer(struct timer_list *t)
+{
+ uint8_t *frame;
+ struct fnic *fnic = timer_container_of(fnic, t, vn_ka_timer);
+
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fip_vn_port_ka *pvn_port_ka;
+ u64 vn_ka_tov;
+ uint8_t fcid[3];
+ uint16_t frame_size = sizeof(struct fip_vn_port_ka);
+
+ if (iport->fip.state != FDLS_FIP_FLOGI_COMPLETE)
+ return;
+
+ if ((iport->selected_fcf.ka_disabled)
+ || (iport->selected_fcf.fka_adv_period == 0)) {
+ return;
+ }
+
+ frame = fdls_alloc_frame(iport);
+ if (frame == NULL) {
+ FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to allocate frame to send vn ka");
+ return;
+ }
+
+ pvn_port_ka = (struct fip_vn_port_ka *) frame;
+ *pvn_port_ka = (struct fip_vn_port_ka) {
+ .eth = {
+ .h_proto = cpu_to_be16(ETH_P_FIP)},
+ .fip = {
+ .fip_ver = FIP_VER_ENCAPS(FIP_VER),
+ .fip_op = cpu_to_be16(FIP_OP_CTRL),
+ .fip_subcode = FIP_SC_REQ,
+ .fip_dl_len = cpu_to_be16(FIP_VN_KA_LEN)},
+ .mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}},
+ .vn_port_desc = {.fd_desc = {.fip_dtype = FIP_DT_VN_ID, .fip_dlen = 5}}
+ };
+
+ memcpy(pvn_port_ka->eth.h_source, iport->fpma, ETH_ALEN);
+ memcpy(pvn_port_ka->eth.h_dest, iport->selected_fcf.fcf_mac, ETH_ALEN);
+ memcpy(pvn_port_ka->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
+ memcpy(pvn_port_ka->vn_port_desc.fd_mac, iport->fpma, ETH_ALEN);
+ hton24(fcid, iport->fcid);
+ memcpy(pvn_port_ka->vn_port_desc.fd_fc_id, fcid, 3);
+ FNIC_STD_SET_NPORT_NAME(&pvn_port_ka->vn_port_desc.fd_wwpn, iport->wwpn);
+
+ FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Handle vnport KA timer\n");
+ fnic_send_fip_frame(iport, frame, frame_size);
+ vn_ka_tov = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
+ mod_timer(&fnic->vn_ka_timer, round_jiffies(vn_ka_tov));
+}
+
+/**
+ * fnic_vlan_discovery_timeout - Handle vlan discovery timeout
+ * @fnic: Handle to fnic driver instance
+ *
+ * End of VLAN discovery or FCF discovery time window.
+ * Start the FCF discovery if VLAN was never used.
+ */
+void fnic_vlan_discovery_timeout(struct fnic *fnic)
+{
+ struct fcoe_vlan *vlan;
+ struct fnic_iport_s *iport = &fnic->iport;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->stop_rx_link_events) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (!iport->usefip)
+ return;
+
+ spin_lock_irqsave(&fnic->vlans_lock, flags);
+ if (list_empty(&fnic->vlan_list)) {
+ /* no vlans available, try again */
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ fnic_fcoe_send_vlan_req(fnic);
+ return;
+ }
+
+ vlan = list_first_entry(&fnic->vlan_list, struct fcoe_vlan, list);
+
+ if (vlan->state == FIP_VLAN_SENT) {
+ if (vlan->sol_count >= FCOE_CTLR_MAX_SOL) {
+ /*
+ * no response on this vlan, remove from the list.
+ * Try the next vlan
+ */
+ list_del(&vlan->list);
+ kfree(vlan);
+ vlan = NULL;
+ if (list_empty(&fnic->vlan_list)) {
+ /* we exhausted all vlans, restart vlan disc */
+ spin_unlock_irqrestore(&fnic->vlans_lock,
+ flags);
+ fnic_fcoe_send_vlan_req(fnic);
+ return;
+ }
+ /* check the next vlan */
+ vlan =
+ list_first_entry(&fnic->vlan_list, struct fcoe_vlan,
+ list);
+
+ fnic->set_vlan(fnic, vlan->vid);
+ vlan->state = FIP_VLAN_SENT; /* sent now */
+
+ }
+ atomic64_inc(&fnic_stats->vlan_stats.sol_expiry_count);
+
+ } else {
+ fnic->set_vlan(fnic, vlan->vid);
+ vlan->state = FIP_VLAN_SENT; /* sent now */
+ }
+ vlan->sol_count++;
+ spin_unlock_irqrestore(&fnic->vlans_lock, flags);
+ fnic_fcoe_start_fcf_discovery(fnic);
+}
+
+/**
+ * fnic_work_on_fcs_ka_timer - Handle work on FCS keep alive timer.
+ * @work: the work queue to be serviced
+ *
+ * Finish handling fcs_ka_timer in process context.
+ * Clean up, bring the link down, and restart all FIP discovery.
+ */
+void fnic_work_on_fcs_ka_timer(struct work_struct *work)
+{
+ struct fnic
+ *fnic = container_of(work, struct fnic, fip_timer_work);
+ struct fnic_iport_s *iport = &fnic->iport;
+
+ FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p fcs ka timeout\n", fnic);
+
+ fnic_common_fip_cleanup(fnic);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ fnic_fdls_link_down(iport);
+ iport->state = FNIC_IPORT_STATE_FIP;
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+
+ fnic_fcoe_send_vlan_req(fnic);
+}
+
+/**
+ * fnic_handle_fcs_ka_timer - Handle FCS keep alive timer.
+ * @t: Handle to the timer list
+ *
+ * No keep alives received from FCF. Clean up, bring the link down
+ * and restart all the FIP discovery.
+ */
+void fnic_handle_fcs_ka_timer(struct timer_list *t)
+{
+ struct fnic *fnic = timer_container_of(fnic, t, fcs_ka_timer);
+
+ INIT_WORK(&fnic->fip_timer_work, fnic_work_on_fcs_ka_timer);
+ queue_work(fnic_fip_queue, &fnic->fip_timer_work);
+}
diff --git a/drivers/scsi/fnic/fip.h b/drivers/scsi/fnic/fip.h
new file mode 100644
index 000000000000..79fee7628870
--- /dev/null
+++ b/drivers/scsi/fnic/fip.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ */
+#ifndef _FIP_H_
+#define _FIP_H_
+
+#include "fdls_fc.h"
+#include "fnic_fdls.h"
+#include <scsi/fc/fc_fip.h>
+
+/* Drop the cast from the standard definition */
+#define FCOE_ALL_FCFS_MAC {0x01, 0x10, 0x18, 0x01, 0x00, 0x02}
+#define FCOE_MAX_SIZE 0x082E
+
+#define FCOE_CTLR_FIPVLAN_TOV (3*1000)
+#define FCOE_CTLR_FCS_TOV (3*1000)
+#define FCOE_CTLR_MAX_SOL (5*1000)
+
+#define FIP_DISC_SOL_LEN (6)
+#define FIP_VLAN_REQ_LEN (2)
+#define FIP_ENODE_KA_LEN (2)
+#define FIP_VN_KA_LEN (7)
+#define FIP_FLOGI_LEN (38)
+
+enum fdls_vlan_state {
+ FIP_VLAN_AVAIL,
+ FIP_VLAN_SENT
+};
+
+enum fdls_fip_state {
+ FDLS_FIP_INIT,
+ FDLS_FIP_VLAN_DISCOVERY_STARTED,
+ FDLS_FIP_FCF_DISCOVERY_STARTED,
+ FDLS_FIP_FLOGI_STARTED,
+ FDLS_FIP_FLOGI_COMPLETE,
+};
+
+/*
+ * VLAN entry.
+ */
+struct fcoe_vlan {
+ struct list_head list;
+ uint16_t vid; /* vlan ID */
+ uint16_t sol_count; /* no. of sols sent */
+ uint16_t state; /* state */
+};
+
+struct fip_vlan_req {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct fip_mac_desc mac_desc;
+} __packed;
+
+struct fip_vlan_notif {
+ struct fip_header fip;
+ struct fip_vlan_desc vlans_desc[];
+} __packed;
+
+struct fip_vn_port_ka {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct fip_mac_desc mac_desc;
+ struct fip_vn_desc vn_port_desc;
+} __packed;
+
+struct fip_enode_ka {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct fip_mac_desc mac_desc;
+} __packed;
+
+struct fip_cvl {
+ struct fip_header fip;
+ struct fip_mac_desc fcf_mac_desc;
+ struct fip_wwn_desc name_desc;
+ struct fip_vn_desc vn_ports_desc[];
+} __packed;
+
+struct fip_flogi_desc {
+ struct fip_desc fd_desc;
+ uint16_t rsvd;
+ struct fc_std_flogi flogi;
+} __packed;
+
+struct fip_flogi_rsp_desc {
+ struct fip_desc fd_desc;
+ uint16_t rsvd;
+ struct fc_std_flogi flogi;
+} __packed;
+
+struct fip_flogi {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct fip_flogi_desc flogi_desc;
+ struct fip_mac_desc mac_desc;
+} __packed;
+
+struct fip_flogi_rsp {
+ struct fip_header fip;
+ struct fip_flogi_rsp_desc rsp_desc;
+ struct fip_mac_desc mac_desc;
+} __packed;
+
+struct fip_discovery {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct fip_mac_desc mac_desc;
+ struct fip_wwn_desc name_desc;
+ struct fip_size_desc fcoe_desc;
+} __packed;
+
+struct fip_disc_adv {
+ struct fip_header fip;
+ struct fip_pri_desc prio_desc;
+ struct fip_mac_desc mac_desc;
+ struct fip_wwn_desc name_desc;
+ struct fip_fab_desc fabric_desc;
+ struct fip_fka_desc fka_adv_desc;
+} __packed;
+
+void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph);
+void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph);
+void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph);
+void fnic_work_on_fip_timer(struct work_struct *work);
+void fnic_work_on_fcs_ka_timer(struct work_struct *work);
+void fnic_fcoe_send_vlan_req(struct fnic *fnic);
+void fnic_fcoe_start_fcf_discovery(struct fnic *fnic);
+void fnic_fcoe_start_flogi(struct fnic *fnic);
+void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph);
+void fnic_vlan_discovery_timeout(struct fnic *fnic);
+
+extern struct workqueue_struct *fnic_fip_queue;
+
+#ifdef FNIC_DEBUG
+static inline void
+fnic_debug_dump_fip_frame(struct fnic *fnic, struct ethhdr *eth,
+ int len, char *pfx)
+{
+ struct fip_header *fiph = (struct fip_header *)(eth + 1);
+ u16 op = be16_to_cpu(fiph->fip_op);
+ u8 sub = fiph->fip_subcode;
+
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "FIP %s packet contents: op: 0x%x sub: 0x%x (len = %d)",
+ pfx, op, sub, len);
+
+ fnic_debug_dump(fnic, (uint8_t *)eth, len);
+}
+
+#else /* FNIC_DEBUG */
+
+static inline void
+fnic_debug_dump_fip_frame(struct fnic *fnic, struct ethhdr *eth,
+ int len, char *pfx) {}
+#endif /* FNIC_DEBUG */
+
+#endif /* _FIP_H_ */
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 671cde9d4060..1199d701c3f5 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _FNIC_H_
#define _FNIC_H_
@@ -22,10 +10,14 @@
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
-#include <scsi/libfc.h>
-#include <scsi/libfcoe.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc_frame.h>
#include "fnic_io.h"
#include "fnic_res.h"
+#include "fnic_trace.h"
+#include "fnic_stats.h"
#include "vnic_dev.h"
#include "vnic_wq.h"
#include "vnic_rq.h"
@@ -34,18 +26,23 @@
#include "vnic_intr.h"
#include "vnic_stats.h"
#include "vnic_scsi.h"
+#include "fnic_fdls.h"
#define DRV_NAME "fnic"
#define DRV_DESCRIPTION "Cisco FCoE HBA Driver"
-#define DRV_VERSION "1.5.0.1"
+#define DRV_VERSION "1.8.0.2"
#define PFX DRV_NAME ": "
#define DFX DRV_NAME "%d: "
+#define FABRIC_LOGO_MAX_RETRY 3
#define DESC_CLEAN_LOW_WATERMARK 8
-#define FNIC_MAX_IO_REQ 2048 /* scsi_cmnd tag map entries */
-#define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */
-#define FNIC_DFLT_QUEUE_DEPTH 32
+#define FNIC_UCSM_DFLT_THROTTLE_CNT_BLD 16 /* UCSM default throttle count */
+#define FNIC_MIN_IO_REQ 256 /* Min IO throttle count */
+#define FNIC_MAX_IO_REQ 1024 /* scsi_cmnd tag map entries */
+#define FNIC_DFLT_IO_REQ 256 /* Default scsi_cmnd tag map entries */
+#define FNIC_DFLT_QUEUE_DEPTH 256
#define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */
+#define LUN0_DELAY_TIME 9
/*
* Tag bits used for special requests.
@@ -56,14 +53,127 @@
#define FNIC_NO_TAG -1
/*
- * Usage of the scsi_cmnd scratchpad.
+ * Command flags to identify the type of command and for other future
+ * use.
+ */
+#define FNIC_NO_FLAGS 0
+#define FNIC_IO_INITIALIZED BIT(0)
+#define FNIC_IO_ISSUED BIT(1)
+#define FNIC_IO_DONE BIT(2)
+#define FNIC_IO_REQ_NULL BIT(3)
+#define FNIC_IO_ABTS_PENDING BIT(4)
+#define FNIC_IO_ABORTED BIT(5)
+#define FNIC_IO_ABTS_ISSUED BIT(6)
+#define FNIC_IO_TERM_ISSUED BIT(7)
+#define FNIC_IO_INTERNAL_TERM_ISSUED BIT(8)
+#define FNIC_IO_ABT_TERM_DONE BIT(9)
+#define FNIC_IO_ABT_TERM_REQ_NULL BIT(10)
+#define FNIC_IO_ABT_TERM_TIMED_OUT BIT(11)
+#define FNIC_DEVICE_RESET BIT(12) /* Device reset request */
+#define FNIC_DEV_RST_ISSUED BIT(13)
+#define FNIC_DEV_RST_TIMED_OUT BIT(14)
+#define FNIC_DEV_RST_ABTS_ISSUED BIT(15)
+#define FNIC_DEV_RST_TERM_ISSUED BIT(16)
+#define FNIC_DEV_RST_DONE BIT(17)
+#define FNIC_DEV_RST_REQ_NULL BIT(18)
+#define FNIC_DEV_RST_ABTS_DONE BIT(19)
+#define FNIC_DEV_RST_TERM_DONE BIT(20)
+#define FNIC_DEV_RST_ABTS_PENDING BIT(21)
+
+#define FNIC_FW_RESET_TIMEOUT 60000 /* mSec */
+#define FNIC_FCOE_MAX_CMD_LEN 16
+/* Retry supported by rport (returned by PRLI service parameters) */
+#define FNIC_FC_RP_FLAGS_RETRY 0x1
+
+/* Cisco vendor id */
+#define PCI_VENDOR_ID_CISCO 0x1137
+#define PCI_DEVICE_ID_CISCO_VIC_FC 0x0045 /* fc vnic */
+
+/* sereno pcie switch */
+#define PCI_DEVICE_ID_CISCO_SERENO 0x004e
+#define PCI_DEVICE_ID_CISCO_CRUZ 0x007a /* Cruz */
+#define PCI_DEVICE_ID_CISCO_BODEGA 0x0131 /* Bodega */
+#define PCI_DEVICE_ID_CISCO_BEVERLY 0x025f /* Beverly */
+
+/* Sereno */
+#define PCI_SUBDEVICE_ID_CISCO_VASONA 0x004f /* vasona mezz */
+#define PCI_SUBDEVICE_ID_CISCO_COTATI 0x0084 /* cotati mlom */
+#define PCI_SUBDEVICE_ID_CISCO_LEXINGTON 0x0085 /* lexington pcie */
+#define PCI_SUBDEVICE_ID_CISCO_ICEHOUSE 0x00cd /* Icehouse */
+#define PCI_SUBDEVICE_ID_CISCO_KIRKWOODLAKE 0x00ce /* KirkwoodLake pcie */
+#define PCI_SUBDEVICE_ID_CISCO_SUSANVILLE 0x012e /* Susanville MLOM */
+#define PCI_SUBDEVICE_ID_CISCO_TORRANCE 0x0139 /* Torrance MLOM */
+
+/* Cruz */
+#define PCI_SUBDEVICE_ID_CISCO_CALISTOGA 0x012c /* Calistoga MLOM */
+#define PCI_SUBDEVICE_ID_CISCO_MOUNTAINVIEW 0x0137 /* Cruz Mezz */
+/* Cruz MountTian SIOC */
+#define PCI_SUBDEVICE_ID_CISCO_MOUNTTIAN 0x014b
+#define PCI_SUBDEVICE_ID_CISCO_CLEARLAKE 0x014d /* ClearLake pcie */
+/* Cruz MountTian2 SIOC */
+#define PCI_SUBDEVICE_ID_CISCO_MOUNTTIAN2 0x0157
+#define PCI_SUBDEVICE_ID_CISCO_CLAREMONT 0x015d /* Claremont MLOM */
+
+/* Bodega */
+/* VIC 1457 PCIe mLOM */
+#define PCI_SUBDEVICE_ID_CISCO_BRADBURY 0x0218
+#define PCI_SUBDEVICE_ID_CISCO_BRENTWOOD 0x0217 /* VIC 1455 PCIe */
+/* VIC 1487 PCIe mLOM */
+#define PCI_SUBDEVICE_ID_CISCO_BURLINGAME 0x021a
+#define PCI_SUBDEVICE_ID_CISCO_BAYSIDE 0x0219 /* VIC 1485 PCIe */
+/* VIC 1440 Mezz mLOM */
+#define PCI_SUBDEVICE_ID_CISCO_BAKERSFIELD 0x0215
+#define PCI_SUBDEVICE_ID_CISCO_BOONVILLE 0x0216 /* VIC 1480 Mezz */
+#define PCI_SUBDEVICE_ID_CISCO_BENICIA 0x024a /* VIC 1495 */
+#define PCI_SUBDEVICE_ID_CISCO_BEAUMONT 0x024b /* VIC 1497 */
+#define PCI_SUBDEVICE_ID_CISCO_BRISBANE 0x02af /* VIC 1467 */
+#define PCI_SUBDEVICE_ID_CISCO_BENTON 0x02b0 /* VIC 1477 */
+#define PCI_SUBDEVICE_ID_CISCO_TWIN_RIVER 0x02cf /* VIC 14425 */
+#define PCI_SUBDEVICE_ID_CISCO_TWIN_PEAK 0x02d0 /* VIC 14825 */
+
+/* Beverly */
+#define PCI_SUBDEVICE_ID_CISCO_BERN 0x02de /* VIC 15420 */
+#define PCI_SUBDEVICE_ID_CISCO_STOCKHOLM 0x02dd /* VIC 15428 */
+#define PCI_SUBDEVICE_ID_CISCO_KRAKOW 0x02dc /* VIC 15411 */
+#define PCI_SUBDEVICE_ID_CISCO_LUCERNE 0x02db /* VIC 15231 */
+#define PCI_SUBDEVICE_ID_CISCO_TURKU 0x02e8 /* VIC 15238 */
+#define PCI_SUBDEVICE_ID_CISCO_TURKU_PLUS 0x02f3 /* VIC 15237 */
+#define PCI_SUBDEVICE_ID_CISCO_ZURICH 0x02df /* VIC 15230 */
+#define PCI_SUBDEVICE_ID_CISCO_RIGA 0x02e0 /* VIC 15427 */
+#define PCI_SUBDEVICE_ID_CISCO_GENEVA 0x02e1 /* VIC 15422 */
+#define PCI_SUBDEVICE_ID_CISCO_HELSINKI 0x02e4 /* VIC 15235 */
+#define PCI_SUBDEVICE_ID_CISCO_GOTHENBURG 0x02f2 /* VIC 15425 */
+
+struct fnic_pcie_device {
+ u32 device;
+ u8 *desc;
+ u32 subsystem_device;
+ u8 *subsys_desc;
+};
+
+/*
+ * fnic private data per SCSI command.
* These fields are locked by the hashed io_req_lock.
*/
-#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
-#define CMD_STATE(Cmnd) ((Cmnd)->SCp.phase)
-#define CMD_ABTS_STATUS(Cmnd) ((Cmnd)->SCp.Message)
-#define CMD_LR_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in)
-#define CMD_TAG(Cmnd) ((Cmnd)->SCp.sent_command)
+struct fnic_cmd_priv {
+ struct fnic_io_req *io_req;
+ enum fnic_ioreq_state state;
+ u32 flags;
+ u16 abts_status;
+ u16 lr_status;
+};
+
+static inline struct fnic_cmd_priv *fnic_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
+static inline u64 fnic_flags_and_state(struct scsi_cmnd *cmd)
+{
+ struct fnic_cmd_priv *fcmd = fnic_priv(cmd);
+
+ return ((u64)fcmd->flags << 32) | fcmd->state;
+}
#define FCPIO_INVALID_CODE 0x100 /* hdr_status value unused by firmware */
@@ -71,10 +181,60 @@
#define FNIC_HOST_RESET_TIMEOUT 10000 /* mSec */
#define FNIC_RMDEVICE_TIMEOUT 1000 /* mSec */
#define FNIC_HOST_RESET_SETTLE_TIME 30 /* Sec */
+#define FNIC_ABT_TERM_DELAY_TIMEOUT 500 /* mSec */
#define FNIC_MAX_FCP_TARGET 256
+#define FNIC_PCI_OFFSET 2
+/**
+ * state_flags to identify host state along along with fnic's state
+ **/
+#define __FNIC_FLAGS_FWRESET BIT(0) /* fwreset in progress */
+#define __FNIC_FLAGS_BLOCK_IO BIT(1) /* IOs are blocked */
+
+#define FNIC_FLAGS_NONE (0)
+#define FNIC_FLAGS_FWRESET (__FNIC_FLAGS_FWRESET | \
+ __FNIC_FLAGS_BLOCK_IO)
+
+#define FNIC_FLAGS_IO_BLOCKED (__FNIC_FLAGS_BLOCK_IO)
+
+#define fnic_set_state_flags(fnicp, st_flags) \
+ __fnic_set_state_flags(fnicp, st_flags, 0)
+#define fnic_clear_state_flags(fnicp, st_flags) \
+ __fnic_set_state_flags(fnicp, st_flags, 1)
+
+enum reset_states {
+ NOT_IN_PROGRESS = 0,
+ IN_PROGRESS,
+ RESET_ERROR
+};
+
+enum rscn_type {
+ NOT_PC_RSCN = 0,
+ PC_RSCN
+};
+
+enum pc_rscn_handling_status {
+ PC_RSCN_HANDLING_NOT_IN_PROGRESS = 0,
+ PC_RSCN_HANDLING_IN_PROGRESS
+};
+
+enum pc_rscn_handling_feature {
+ PC_RSCN_HANDLING_FEATURE_OFF = 0,
+ PC_RSCN_HANDLING_FEATURE_ON
+};
+
+extern unsigned int fnic_fdmi_support;
extern unsigned int fnic_log_level;
+extern unsigned int io_completions;
+extern struct workqueue_struct *fnic_event_queue;
+
+extern unsigned int pc_rscn_handling_feature_flag;
+extern spinlock_t reset_fnic_list_lock;
+extern struct list_head reset_fnic_list;
+extern struct workqueue_struct *reset_fnic_work_queue;
+extern struct work_struct reset_fnic_work;
+
#define FNIC_MAIN_LOGGING 0x01
#define FNIC_FCS_LOGGING 0x02
@@ -89,28 +249,54 @@ do { \
} while (0); \
} while (0)
-#define FNIC_MAIN_DBG(kern_level, host, fmt, args...) \
+#define FNIC_MAIN_DBG(kern_level, host, fnic_num, fmt, args...) \
FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
+ shost_printk(kern_level, host, \
+ "fnic<%d>: %s: %d: " fmt, fnic_num,\
+ __func__, __LINE__, ##args);)
+
+#define FNIC_FCS_DBG(kern_level, host, fnic_num, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \
+ shost_printk(kern_level, host, \
+ "fnic<%d>: %s: %d: " fmt, fnic_num,\
+ __func__, __LINE__, ##args);)
-#define FNIC_FCS_DBG(kern_level, host, fmt, args...) \
+#define FNIC_FIP_DBG(kern_level, host, fnic_num, fmt, args...) \
FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
+ shost_printk(kern_level, host, \
+ "fnic<%d>: %s: %d: " fmt, fnic_num,\
+ __func__, __LINE__, ##args);)
-#define FNIC_SCSI_DBG(kern_level, host, fmt, args...) \
+#define FNIC_SCSI_DBG(kern_level, host, fnic_num, fmt, args...) \
FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
+ shost_printk(kern_level, host, \
+ "fnic<%d>: %s: %d: " fmt, fnic_num,\
+ __func__, __LINE__, ##args);)
-#define FNIC_ISR_DBG(kern_level, host, fmt, args...) \
+#define FNIC_ISR_DBG(kern_level, host, fnic_num, fmt, args...) \
FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
+ shost_printk(kern_level, host, \
+ "fnic<%d>: %s: %d: " fmt, fnic_num,\
+ __func__, __LINE__, ##args);)
+
+#define FNIC_MAIN_NOTE(kern_level, host, fmt, args...) \
+ shost_printk(kern_level, host, fmt, ##args)
+
+#define FNIC_WQ_COPY_MAX 64
+#define FNIC_WQ_MAX 1
+#define FNIC_RQ_MAX 1
+#define FNIC_CQ_MAX (FNIC_WQ_COPY_MAX + FNIC_WQ_MAX + FNIC_RQ_MAX)
+#define FNIC_DFLT_IO_COMPLETIONS 256
+
+#define FNIC_MQ_CQ_INDEX 2
extern const char *fnic_state_str[];
enum fnic_intx_intr_index {
FNIC_INTX_WQ_RQ_COPYWQ,
- FNIC_INTX_ERR,
+ FNIC_INTX_DUMMY,
FNIC_INTX_NOTIFY,
+ FNIC_INTX_ERR,
FNIC_INTX_INTR_MAX,
};
@@ -118,15 +304,16 @@ enum fnic_msix_intr_index {
FNIC_MSIX_RQ,
FNIC_MSIX_WQ,
FNIC_MSIX_WQ_COPY,
- FNIC_MSIX_ERR_NOTIFY,
+ FNIC_MSIX_ERR_NOTIFY = FNIC_MSIX_WQ_COPY + FNIC_WQ_COPY_MAX,
FNIC_MSIX_INTR_MAX,
};
struct fnic_msix_entry {
int requested;
- char devname[IFNAMSIZ];
+ char devname[IFNAMSIZ + 11];
irqreturn_t (*isr)(int, void *);
void *devid;
+ int irq_num;
};
enum fnic_state {
@@ -136,51 +323,98 @@ enum fnic_state {
FNIC_IN_ETH_TRANS_FC_MODE,
};
-#define FNIC_WQ_COPY_MAX 1
-#define FNIC_WQ_MAX 1
-#define FNIC_RQ_MAX 1
-#define FNIC_CQ_MAX (FNIC_WQ_COPY_MAX + FNIC_WQ_MAX + FNIC_RQ_MAX)
+enum fnic_role_e {
+ FNIC_ROLE_FCP_INITIATOR = 0,
+};
+
+enum fnic_evt {
+ FNIC_EVT_START_VLAN_DISC = 1,
+ FNIC_EVT_START_FCF_DISC = 2,
+ FNIC_EVT_MAX,
+};
+
+struct fnic_frame_list {
+ /*
+ * Link to frame lists
+ */
+ struct list_head links;
+ void *fp;
+ int frame_len;
+ int rx_ethhdr_stripped;
+};
-struct mempool;
+struct fnic_event {
+ struct list_head list;
+ struct fnic *fnic;
+ enum fnic_evt event;
+};
+
+struct fnic_cpy_wq {
+ unsigned long hw_lock_flags;
+ u16 active_ioreq_count;
+ u16 ioreq_table_size;
+ ____cacheline_aligned struct fnic_io_req **io_req_table;
+};
/* Per-instance private data structure */
struct fnic {
- struct fc_lport *lport;
- struct fcoe_ctlr ctlr; /* FIP FCoE controller structure */
+ int fnic_num;
+ enum fnic_role_e role;
+ struct fnic_iport_s iport;
+ struct Scsi_Host *host;
struct vnic_dev_bar bar0;
- struct msix_entry msix_entry[FNIC_MSIX_INTR_MAX];
struct fnic_msix_entry msix[FNIC_MSIX_INTR_MAX];
struct vnic_stats *stats;
unsigned long stats_time; /* time of stats update */
+ unsigned long stats_reset_time; /* time of stats reset */
struct vnic_nic_cfg *nic_cfg;
char name[IFNAMSIZ];
struct timer_list notify_timer; /* used for MSI interrupts */
+ unsigned int fnic_max_tag_id;
unsigned int err_intr_offset;
unsigned int link_intr_offset;
unsigned int wq_count;
unsigned int cq_count;
+ struct completion reset_completion_wait;
+ struct mutex sgreset_mutex;
+ spinlock_t sgreset_lock; /* lock for sgreset */
+ struct scsi_cmnd *sgreset_sc;
+ struct dentry *fnic_stats_debugfs_host;
+ struct dentry *fnic_stats_debugfs_file;
+ struct dentry *fnic_reset_debugfs_file;
+ unsigned int reset_stats;
+ atomic64_t io_cmpl_skip;
+ struct fnic_stats fnic_stats;
+
u32 vlan_hw_insert:1; /* let hw insert the tag */
u32 in_remove:1; /* fnic device in removal */
u32 stop_rx_link_events:1; /* stop proc. rx frames, link events */
- struct completion *remove_wait; /* device remove thread blocks */
-
+ struct completion *fw_reset_done;
+ u32 reset_in_progress;
+ atomic_t in_flight; /* io counter */
+ bool internal_reset_inprogress;
+ u32 _reserved; /* fill hole */
+ unsigned long state_flags; /* protected by host lock */
enum fnic_state state;
spinlock_t fnic_lock;
+ unsigned long lock_flags;
u16 vlan_id; /* VLAN tag including priority */
u8 data_src_addr[ETH_ALEN];
u64 fcp_input_bytes; /* internal statistic */
u64 fcp_output_bytes; /* internal statistic */
u32 link_down_cnt;
+ u32 soft_reset_count;
int link_status;
struct list_head list;
+ struct list_head links;
struct pci_dev *pdev;
struct vnic_fc_config config;
struct vnic_dev *vdev;
@@ -195,15 +429,40 @@ struct fnic {
struct fnic_host_tag *tags;
mempool_t *io_req_pool;
mempool_t *io_sgl_pool[FNIC_SGL_NUM_CACHES];
- spinlock_t io_req_lock[FNIC_IO_LOCKS]; /* locks for scsi cmnds */
+ unsigned int copy_wq_base;
struct work_struct link_work;
struct work_struct frame_work;
- struct sk_buff_head frame_queue;
- struct sk_buff_head tx_queue;
+ struct work_struct flush_work;
+ struct list_head frame_queue;
+ struct list_head tx_queue;
+ mempool_t *frame_pool;
+ mempool_t *frame_elem_pool;
+ struct work_struct tport_work;
+ struct list_head tport_event_list;
+
+ char subsys_desc[14];
+ int subsys_desc_len;
+ int pc_rscn_handling_status;
+
+ /*** FIP related data members -- start ***/
+ void (*set_vlan)(struct fnic *, u16 vlan);
+ struct work_struct fip_frame_work;
+ struct work_struct fip_timer_work;
+ struct list_head fip_frame_queue;
+ struct timer_list fip_timer;
+ spinlock_t vlans_lock;
+ struct timer_list retry_fip_timer;
+ struct timer_list fcs_ka_timer;
+ struct timer_list enode_ka_timer;
+ struct timer_list vn_ka_timer;
+ struct list_head vlan_list;
+ /*** FIP related data members -- end ***/
/* copy work queue cache line section */
- ____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX];
+ ____cacheline_aligned struct vnic_wq_copy hw_copy_wq[FNIC_WQ_COPY_MAX];
+ ____cacheline_aligned struct fnic_cpy_wq sw_copy_wq[FNIC_WQ_COPY_MAX];
+
/* completion queue cache line section */
____cacheline_aligned struct vnic_cq cq[FNIC_CQ_MAX];
@@ -220,42 +479,40 @@ struct fnic {
____cacheline_aligned struct vnic_intr intr[FNIC_MSIX_INTR_MAX];
};
-static inline struct fnic *fnic_from_ctlr(struct fcoe_ctlr *fip)
-{
- return container_of(fip, struct fnic, ctlr);
-}
-
extern struct workqueue_struct *fnic_event_queue;
-extern struct device_attribute *fnic_attrs[];
+extern struct workqueue_struct *fnic_fip_queue;
+extern const struct attribute_group *fnic_host_groups[];
void fnic_clear_intr_mode(struct fnic *fnic);
int fnic_set_intr_mode(struct fnic *fnic);
+int fnic_set_intr_mode_msix(struct fnic *fnic);
void fnic_free_intr(struct fnic *fnic);
int fnic_request_intr(struct fnic *fnic);
-int fnic_send(struct fc_lport *, struct fc_frame *);
void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf);
void fnic_handle_frame(struct work_struct *work);
+void fnic_tport_event_handler(struct work_struct *work);
void fnic_handle_link(struct work_struct *work);
+void fnic_handle_event(struct work_struct *work);
+void fdls_reclaim_oxid_handler(struct work_struct *work);
+void fdls_schedule_oxid_free(struct fnic_iport_s *iport, uint16_t *active_oxid);
+void fdls_schedule_oxid_free_retry_work(struct work_struct *work);
int fnic_rq_cmpl_handler(struct fnic *fnic, int);
int fnic_alloc_rq_frame(struct vnic_rq *rq);
void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf);
-void fnic_flush_tx(struct fnic *);
-void fnic_eth_send(struct fcoe_ctlr *, struct sk_buff *skb);
-void fnic_set_port_id(struct fc_lport *, u32, struct fc_frame *);
-void fnic_update_mac(struct fc_lport *, u8 *new);
+void fnic_flush_tx(struct work_struct *work);
void fnic_update_mac_locked(struct fnic *, u8 *new);
int fnic_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
int fnic_abort_cmd(struct scsi_cmnd *);
int fnic_device_reset(struct scsi_cmnd *);
-int fnic_host_reset(struct scsi_cmnd *);
-int fnic_reset(struct Scsi_Host *);
-void fnic_scsi_cleanup(struct fc_lport *);
-void fnic_scsi_abort_io(struct fc_lport *);
-void fnic_empty_scsi_cleanup(struct fc_lport *);
-void fnic_exch_mgr_reset(struct fc_lport *, u32, u32);
-int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int);
+int fnic_eh_host_reset_handler(struct scsi_cmnd *sc);
+int fnic_host_reset(struct Scsi_Host *shost);
+void fnic_reset(struct Scsi_Host *shost);
+int fnic_issue_fc_host_lip(struct Scsi_Host *shost);
+void fnic_get_host_port_state(struct Scsi_Host *shost);
+void fnic_scsi_fcpio_reset(struct fnic *fnic);
+int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do, unsigned int cq_index);
int fnic_wq_cmpl_handler(struct fnic *fnic, int);
int fnic_flogi_reg_handler(struct fnic *fnic, u32);
void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
@@ -263,8 +520,110 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
int fnic_fw_reset_handler(struct fnic *fnic);
void fnic_terminate_rport_io(struct fc_rport *);
const char *fnic_state_to_str(unsigned int state);
-
+void fnic_mq_map_queues_cpus(struct Scsi_Host *host);
void fnic_log_q_error(struct fnic *fnic);
void fnic_handle_link_event(struct fnic *fnic);
+int fnic_stats_debugfs_init(struct fnic *fnic);
+void fnic_stats_debugfs_remove(struct fnic *fnic);
+int fnic_is_abts_pending(struct fnic *, struct scsi_cmnd *);
+
+void fnic_handle_fip_frame(struct work_struct *work);
+void fnic_reset_work_handler(struct work_struct *work);
+void fnic_handle_fip_event(struct fnic *fnic);
+void fnic_fcoe_reset_vlans(struct fnic *fnic);
+extern void fnic_handle_fip_timer(struct timer_list *t);
+
+static inline int
+fnic_chk_state_flags_locked(struct fnic *fnic, unsigned long st_flags)
+{
+ return ((fnic->state_flags & st_flags) == st_flags);
+}
+void __fnic_set_state_flags(struct fnic *, unsigned long, unsigned long);
+void fnic_dump_fchost_stats(struct Scsi_Host *, struct fc_host_statistics *);
+void fnic_free_txq(struct list_head *head);
+int fnic_get_desc_by_devid(struct pci_dev *pdev, char **desc,
+ char **subsys_desc);
+void fnic_fdls_link_status_change(struct fnic *fnic, int linkup);
+void fnic_delete_fcp_tports(struct fnic *fnic);
+void fnic_flush_tport_event_list(struct fnic *fnic);
+int fnic_count_ioreqs_wq(struct fnic *fnic, u32 hwq, u32 portid);
+unsigned int fnic_count_ioreqs(struct fnic *fnic, u32 portid);
+unsigned int fnic_count_all_ioreqs(struct fnic *fnic);
+unsigned int fnic_count_lun_ioreqs_wq(struct fnic *fnic, u32 hwq,
+ struct scsi_device *device);
+unsigned int fnic_count_lun_ioreqs(struct fnic *fnic,
+ struct scsi_device *device);
+void fnic_scsi_unload(struct fnic *fnic);
+void fnic_scsi_unload_cleanup(struct fnic *fnic);
+int fnic_get_debug_info(struct stats_debug_info *info,
+ struct fnic *fnic);
+
+struct fnic_scsi_iter_data {
+ struct fnic *fnic;
+ void *data1;
+ void *data2;
+ bool (*fn)(struct fnic *fnic, struct scsi_cmnd *sc,
+ void *data1, void *data2);
+};
+
+static inline bool
+fnic_io_iter_handler(struct scsi_cmnd *sc, void *iter_data)
+{
+ struct fnic_scsi_iter_data *iter = iter_data;
+
+ return iter->fn(iter->fnic, sc, iter->data1, iter->data2);
+}
+
+static inline void
+fnic_scsi_io_iter(struct fnic *fnic,
+ bool (*fn)(struct fnic *fnic, struct scsi_cmnd *sc,
+ void *data1, void *data2),
+ void *data1, void *data2)
+{
+ struct fnic_scsi_iter_data iter_data = {
+ .fn = fn,
+ .fnic = fnic,
+ .data1 = data1,
+ .data2 = data2,
+ };
+ scsi_host_busy_iter(fnic->host, fnic_io_iter_handler, &iter_data);
+}
+#ifdef FNIC_DEBUG
+static inline void
+fnic_debug_dump(struct fnic *fnic, uint8_t *u8arr, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i = i+8) {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "%d: %02x %02x %02x %02x %02x %02x %02x %02x", i / 8,
+ u8arr[i + 0], u8arr[i + 1], u8arr[i + 2], u8arr[i + 3],
+ u8arr[i + 4], u8arr[i + 5], u8arr[i + 6], u8arr[i + 7]);
+ }
+}
+
+static inline void
+fnic_debug_dump_fc_frame(struct fnic *fnic, struct fc_frame_header *fchdr,
+ int len, char *pfx)
+{
+ uint32_t s_id, d_id;
+
+ s_id = ntoh24(fchdr->fh_s_id);
+ d_id = ntoh24(fchdr->fh_d_id);
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "%s packet contents: sid/did/type/oxid = 0x%x/0x%x/0x%x/0x%x (len = %d)\n",
+ pfx, s_id, d_id, fchdr->fh_type,
+ FNIC_STD_GET_OX_ID(fchdr), len);
+
+ fnic_debug_dump(fnic, (uint8_t *)fchdr, len);
+
+}
+#else /* FNIC_DEBUG */
+static inline void
+fnic_debug_dump(struct fnic *fnic, uint8_t *u8arr, int len) {}
+static inline void
+fnic_debug_dump_fc_frame(struct fnic *fnic, struct fc_frame_header *fchdr,
+ uint32_t len, char *pfx) {}
+#endif /* FNIC_DEBUG */
#endif /* _FNIC_H_ */
diff --git a/drivers/scsi/fnic/fnic_attrs.c b/drivers/scsi/fnic/fnic_attrs.c
index aea0c3becfd4..705718f0809b 100644
--- a/drivers/scsi/fnic/fnic_attrs.c
+++ b/drivers/scsi/fnic/fnic_attrs.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/string.h>
#include <linux/device.h>
@@ -23,34 +11,46 @@
static ssize_t fnic_show_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct fc_lport *lp = shost_priv(class_to_shost(dev));
- struct fnic *fnic = lport_priv(lp);
+ struct fnic *fnic =
+ *((struct fnic **) shost_priv(class_to_shost(dev)));
- return snprintf(buf, PAGE_SIZE, "%s\n", fnic_state_str[fnic->state]);
+ return sysfs_emit(buf, "%s\n", fnic_state_str[fnic->state]);
}
static ssize_t fnic_show_drv_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%s\n", DRV_VERSION);
+ return sysfs_emit(buf, "%s\n", DRV_VERSION);
}
static ssize_t fnic_show_link_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct fc_lport *lp = shost_priv(class_to_shost(dev));
+ struct fnic *fnic =
+ *((struct fnic **) shost_priv(class_to_shost(dev)));
- return snprintf(buf, PAGE_SIZE, "%s\n", (lp->link_up)
- ? "Link Up" : "Link Down");
+ return sysfs_emit(buf, "%s\n",
+ ((fnic->iport.state != FNIC_IPORT_STATE_INIT) &&
+ (fnic->iport.state != FNIC_IPORT_STATE_LINK_WAIT)) ?
+ "Link Up" : "Link Down");
}
static DEVICE_ATTR(fnic_state, S_IRUGO, fnic_show_state, NULL);
static DEVICE_ATTR(drv_version, S_IRUGO, fnic_show_drv_version, NULL);
static DEVICE_ATTR(link_state, S_IRUGO, fnic_show_link_state, NULL);
-struct device_attribute *fnic_attrs[] = {
- &dev_attr_fnic_state,
- &dev_attr_drv_version,
- &dev_attr_link_state,
+static struct attribute *fnic_host_attrs[] = {
+ &dev_attr_fnic_state.attr,
+ &dev_attr_drv_version.attr,
+ &dev_attr_link_state.attr,
NULL,
};
+
+static const struct attribute_group fnic_host_attr_group = {
+ .attrs = fnic_host_attrs
+};
+
+const struct attribute_group *fnic_host_groups[] = {
+ &fnic_host_attr_group,
+ NULL
+};
diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c
new file mode 100644
index 000000000000..5767862ae42f
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_debugfs.c
@@ -0,0 +1,721 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright 2012 Cisco Systems, Inc. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+#include "fnic.h"
+
+extern int fnic_get_debug_info(struct stats_debug_info *debug_buffer,
+ struct fnic *fnic);
+
+static struct dentry *fnic_trace_debugfs_root;
+static struct dentry *fnic_trace_debugfs_file;
+static struct dentry *fnic_trace_enable;
+static struct dentry *fnic_stats_debugfs_root;
+
+static struct dentry *fnic_fc_trace_debugfs_file;
+static struct dentry *fnic_fc_rdata_trace_debugfs_file;
+static struct dentry *fnic_fc_trace_enable;
+static struct dentry *fnic_fc_trace_clear;
+
+struct fc_trace_flag_type {
+ u8 fc_row_file;
+ u8 fc_normal_file;
+ u8 fnic_trace;
+ u8 fc_trace;
+ u8 fc_clear;
+};
+
+static struct fc_trace_flag_type *fc_trc_flag;
+
+/*
+ * fnic_debugfs_init - Initialize debugfs for fnic debug logging
+ *
+ * Description:
+ * When Debugfs is configured this routine sets up the fnic debugfs
+ * file system. If not already created, this routine will create the
+ * fnic directory and statistics directory for trace buffer and
+ * stats logging.
+ */
+int fnic_debugfs_init(void)
+{
+ fnic_trace_debugfs_root = debugfs_create_dir("fnic", NULL);
+
+ fnic_stats_debugfs_root = debugfs_create_dir("statistics",
+ fnic_trace_debugfs_root);
+
+ /* Allocate memory to structure */
+ fc_trc_flag = vmalloc(sizeof(struct fc_trace_flag_type));
+
+ if (fc_trc_flag) {
+ fc_trc_flag->fc_row_file = 0;
+ fc_trc_flag->fc_normal_file = 1;
+ fc_trc_flag->fnic_trace = 2;
+ fc_trc_flag->fc_trace = 3;
+ fc_trc_flag->fc_clear = 4;
+ return 0;
+ }
+
+ return -ENOMEM;
+}
+
+/*
+ * fnic_debugfs_terminate - Tear down debugfs infrastructure
+ *
+ * Description:
+ * When Debugfs is configured this routine removes debugfs file system
+ * elements that are specific to fnic.
+ */
+void fnic_debugfs_terminate(void)
+{
+ debugfs_remove(fnic_stats_debugfs_root);
+ fnic_stats_debugfs_root = NULL;
+
+ debugfs_remove(fnic_trace_debugfs_root);
+ fnic_trace_debugfs_root = NULL;
+
+ vfree(fc_trc_flag);
+}
+
+/*
+ * fnic_trace_ctrl_read -
+ * Read trace_enable ,fc_trace_enable
+ * or fc_trace_clear debugfs file
+ * @filp: The file pointer to read from.
+ * @ubuf: The buffer to copy the data to.
+ * @cnt: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads value of variable fnic_tracing_enabled or
+ * fnic_fc_tracing_enabled or fnic_fc_trace_cleared
+ * and stores into local @buf.
+ * It will start reading file at @ppos and
+ * copy up to @cnt of data to @ubuf from @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read.
+ */
+static ssize_t fnic_trace_ctrl_read(struct file *filp,
+ char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[64];
+ int len;
+ u8 *trace_type;
+ len = 0;
+ trace_type = (u8 *)filp->private_data;
+ if (*trace_type == fc_trc_flag->fnic_trace)
+ len = sprintf(buf, "%d\n", fnic_tracing_enabled);
+ else if (*trace_type == fc_trc_flag->fc_trace)
+ len = sprintf(buf, "%d\n", fnic_fc_tracing_enabled);
+ else if (*trace_type == fc_trc_flag->fc_clear)
+ len = sprintf(buf, "%d\n", fnic_fc_trace_cleared);
+ else
+ pr_err("fnic: Cannot read to any debugfs file\n");
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
+}
+
+/*
+ * fnic_trace_ctrl_write -
+ * Write to trace_enable, fc_trace_enable or
+ * fc_trace_clear debugfs file
+ * @filp: The file pointer to write from.
+ * @ubuf: The buffer to copy the data from.
+ * @cnt: The number of bytes to write.
+ * @ppos: The position in the file to start writing to.
+ *
+ * Description:
+ * This routine writes data from user buffer @ubuf to buffer @buf and
+ * sets fc_trace_enable ,tracing_enable or fnic_fc_trace_cleared
+ * value as per user input.
+ *
+ * Returns:
+ * This function returns the amount of data that was written.
+ */
+static ssize_t fnic_trace_ctrl_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[64];
+ unsigned long val;
+ int ret;
+ u8 *trace_type;
+ trace_type = (u8 *)filp->private_data;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ if (*trace_type == fc_trc_flag->fnic_trace)
+ fnic_tracing_enabled = val;
+ else if (*trace_type == fc_trc_flag->fc_trace)
+ fnic_fc_tracing_enabled = val;
+ else if (*trace_type == fc_trc_flag->fc_clear)
+ fnic_fc_trace_cleared = val;
+ else
+ pr_err("fnic: cannot write to any debugfs file\n");
+
+ (*ppos)++;
+
+ return cnt;
+}
+
+static const struct file_operations fnic_trace_ctrl_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = fnic_trace_ctrl_read,
+ .write = fnic_trace_ctrl_write,
+};
+
+/*
+ * fnic_trace_debugfs_open - Open the fnic trace log
+ * @inode: The inode pointer
+ * @file: The file pointer to attach the log output
+ *
+ * Description:
+ * This routine is the entry point for the debugfs open file operation.
+ * It allocates the necessary buffer for the log, fills the buffer from
+ * the in-memory log and then returns a pointer to that log in
+ * the private_data field in @file.
+ *
+ * Returns:
+ * This function returns zero if successful. On error it will return
+ * a negative error value.
+ */
+static int fnic_trace_debugfs_open(struct inode *inode,
+ struct file *file)
+{
+ fnic_dbgfs_t *fnic_dbg_prt;
+ u8 *rdata_ptr;
+ rdata_ptr = (u8 *)inode->i_private;
+ fnic_dbg_prt = kzalloc(sizeof(fnic_dbgfs_t), GFP_KERNEL);
+ if (!fnic_dbg_prt)
+ return -ENOMEM;
+
+ if (*rdata_ptr == fc_trc_flag->fnic_trace) {
+ fnic_dbg_prt->buffer = vzalloc(array3_size(3, trace_max_pages,
+ PAGE_SIZE));
+ if (!fnic_dbg_prt->buffer) {
+ kfree(fnic_dbg_prt);
+ return -ENOMEM;
+ }
+ fnic_dbg_prt->buffer_len = fnic_get_trace_data(fnic_dbg_prt);
+ } else {
+ fnic_dbg_prt->buffer =
+ vzalloc(array3_size(3, fnic_fc_trace_max_pages,
+ PAGE_SIZE));
+ if (!fnic_dbg_prt->buffer) {
+ kfree(fnic_dbg_prt);
+ return -ENOMEM;
+ }
+ fnic_dbg_prt->buffer_len =
+ fnic_fc_trace_get_data(fnic_dbg_prt, *rdata_ptr);
+ }
+ file->private_data = fnic_dbg_prt;
+
+ return 0;
+}
+
+/*
+ * fnic_trace_debugfs_lseek - Seek through a debugfs file
+ * @file: The file pointer to seek through.
+ * @offset: The offset to seek to or the amount to seek by.
+ * @howto: Indicates how to seek.
+ *
+ * Description:
+ * This routine is the entry point for the debugfs lseek file operation.
+ * The @howto parameter indicates whether @offset is the offset to directly
+ * seek to, or if it is a value to seek forward or reverse by. This function
+ * figures out what the new offset of the debugfs file will be and assigns
+ * that value to the f_pos field of @file.
+ *
+ * Returns:
+ * This function returns the new offset if successful and returns a negative
+ * error if unable to process the seek.
+ */
+static loff_t fnic_trace_debugfs_lseek(struct file *file,
+ loff_t offset,
+ int howto)
+{
+ fnic_dbgfs_t *fnic_dbg_prt = file->private_data;
+ return fixed_size_llseek(file, offset, howto,
+ fnic_dbg_prt->buffer_len);
+}
+
+/*
+ * fnic_trace_debugfs_read - Read a debugfs file
+ * @file: The file pointer to read from.
+ * @ubuf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @pos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the buffer indicated in the private_data
+ * field of @file. It will start reading at @pos and copy up to @nbytes of
+ * data to @ubuf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be
+ * less than @nbytes if the end of the file was reached).
+ */
+static ssize_t fnic_trace_debugfs_read(struct file *file,
+ char __user *ubuf,
+ size_t nbytes,
+ loff_t *pos)
+{
+ fnic_dbgfs_t *fnic_dbg_prt = file->private_data;
+ int rc = 0;
+ rc = simple_read_from_buffer(ubuf, nbytes, pos,
+ fnic_dbg_prt->buffer,
+ fnic_dbg_prt->buffer_len);
+ return rc;
+}
+
+/*
+ * fnic_trace_debugfs_release - Release the buffer used to store
+ * debugfs file data
+ * @inode: The inode pointer
+ * @file: The file pointer that contains the buffer to release
+ *
+ * Description:
+ * This routine frees the buffer that was allocated when the debugfs
+ * file was opened.
+ *
+ * Returns:
+ * This function returns zero.
+ */
+static int fnic_trace_debugfs_release(struct inode *inode,
+ struct file *file)
+{
+ fnic_dbgfs_t *fnic_dbg_prt = file->private_data;
+
+ vfree(fnic_dbg_prt->buffer);
+ kfree(fnic_dbg_prt);
+ return 0;
+}
+
+static const struct file_operations fnic_trace_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = fnic_trace_debugfs_open,
+ .llseek = fnic_trace_debugfs_lseek,
+ .read = fnic_trace_debugfs_read,
+ .release = fnic_trace_debugfs_release,
+};
+
+/*
+ * fnic_trace_debugfs_init - Initialize debugfs for fnic trace logging
+ *
+ * Description:
+ * When Debugfs is configured this routine sets up the fnic debugfs
+ * file system. If not already created, this routine will create the
+ * create file trace to log fnic trace buffer output into debugfs and
+ * it will also create file trace_enable to control enable/disable of
+ * trace logging into trace buffer.
+ */
+void fnic_trace_debugfs_init(void)
+{
+ fnic_trace_enable = debugfs_create_file("tracing_enable",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic_trace_debugfs_root,
+ &(fc_trc_flag->fnic_trace),
+ &fnic_trace_ctrl_fops);
+
+ fnic_trace_debugfs_file = debugfs_create_file("trace",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic_trace_debugfs_root,
+ &(fc_trc_flag->fnic_trace),
+ &fnic_trace_debugfs_fops);
+}
+
+/*
+ * fnic_trace_debugfs_terminate - Tear down debugfs infrastructure
+ *
+ * Description:
+ * When Debugfs is configured this routine removes debugfs file system
+ * elements that are specific to fnic trace logging.
+ */
+void fnic_trace_debugfs_terminate(void)
+{
+ debugfs_remove(fnic_trace_debugfs_file);
+ fnic_trace_debugfs_file = NULL;
+
+ debugfs_remove(fnic_trace_enable);
+ fnic_trace_enable = NULL;
+}
+
+/*
+ * fnic_fc_trace_debugfs_init -
+ * Initialize debugfs for fnic control frame trace logging
+ *
+ * Description:
+ * When Debugfs is configured this routine sets up the fnic_fc debugfs
+ * file system. If not already created, this routine will create the
+ * create file trace to log fnic fc trace buffer output into debugfs and
+ * it will also create file fc_trace_enable to control enable/disable of
+ * trace logging into trace buffer.
+ */
+
+void fnic_fc_trace_debugfs_init(void)
+{
+ fnic_fc_trace_enable = debugfs_create_file("fc_trace_enable",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic_trace_debugfs_root,
+ &(fc_trc_flag->fc_trace),
+ &fnic_trace_ctrl_fops);
+
+ fnic_fc_trace_clear = debugfs_create_file("fc_trace_clear",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic_trace_debugfs_root,
+ &(fc_trc_flag->fc_clear),
+ &fnic_trace_ctrl_fops);
+
+ fnic_fc_rdata_trace_debugfs_file =
+ debugfs_create_file("fc_trace_rdata",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic_trace_debugfs_root,
+ &(fc_trc_flag->fc_normal_file),
+ &fnic_trace_debugfs_fops);
+
+ fnic_fc_trace_debugfs_file =
+ debugfs_create_file("fc_trace",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic_trace_debugfs_root,
+ &(fc_trc_flag->fc_row_file),
+ &fnic_trace_debugfs_fops);
+}
+
+/*
+ * fnic_fc_trace_debugfs_terminate - Tear down debugfs infrastructure
+ *
+ * Description:
+ * When Debugfs is configured this routine removes debugfs file system
+ * elements that are specific to fnic_fc trace logging.
+ */
+
+void fnic_fc_trace_debugfs_terminate(void)
+{
+ debugfs_remove(fnic_fc_trace_debugfs_file);
+ fnic_fc_trace_debugfs_file = NULL;
+
+ debugfs_remove(fnic_fc_rdata_trace_debugfs_file);
+ fnic_fc_rdata_trace_debugfs_file = NULL;
+
+ debugfs_remove(fnic_fc_trace_enable);
+ fnic_fc_trace_enable = NULL;
+
+ debugfs_remove(fnic_fc_trace_clear);
+ fnic_fc_trace_clear = NULL;
+}
+
+/*
+ * fnic_reset_stats_open - Open the reset_stats file
+ * @inode: The inode pointer.
+ * @file: The file pointer to attach the stats reset flag.
+ *
+ * Description:
+ * This routine opens a debugsfs file reset_stats and stores i_private data
+ * to debug structure to retrieve later for while performing other
+ * file oprations.
+ *
+ * Returns:
+ * This function returns zero if successful.
+ */
+static int fnic_reset_stats_open(struct inode *inode, struct file *file)
+{
+ struct stats_debug_info *debug;
+
+ debug = kzalloc(sizeof(struct stats_debug_info), GFP_KERNEL);
+ if (!debug)
+ return -ENOMEM;
+
+ debug->i_private = inode->i_private;
+
+ file->private_data = debug;
+
+ return 0;
+}
+
+/*
+ * fnic_reset_stats_read - Read a reset_stats debugfs file
+ * @filp: The file pointer to read from.
+ * @ubuf: The buffer to copy the data to.
+ * @cnt: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads value of variable reset_stats
+ * and stores into local @buf. It will start reading file at @ppos and
+ * copy up to @cnt of data to @ubuf from @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read.
+ */
+static ssize_t fnic_reset_stats_read(struct file *file,
+ char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct stats_debug_info *debug = file->private_data;
+ struct fnic *fnic = (struct fnic *)debug->i_private;
+ char buf[64];
+ int len;
+
+ len = sprintf(buf, "%u\n", fnic->reset_stats);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
+}
+
+/*
+ * fnic_reset_stats_write - Write to reset_stats debugfs file
+ * @filp: The file pointer to write from.
+ * @ubuf: The buffer to copy the data from.
+ * @cnt: The number of bytes to write.
+ * @ppos: The position in the file to start writing to.
+ *
+ * Description:
+ * This routine writes data from user buffer @ubuf to buffer @buf and
+ * resets cumulative stats of fnic.
+ *
+ * Returns:
+ * This function returns the amount of data that was written.
+ */
+static ssize_t fnic_reset_stats_write(struct file *file,
+ const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct stats_debug_info *debug = file->private_data;
+ struct fnic *fnic = (struct fnic *)debug->i_private;
+ struct fnic_stats *stats = &fnic->fnic_stats;
+ u64 *io_stats_p = (u64 *)&stats->io_stats;
+ u64 *fw_stats_p = (u64 *)&stats->fw_stats;
+ char buf[64];
+ unsigned long val;
+ int ret;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ fnic->reset_stats = val;
+
+ if (fnic->reset_stats) {
+ /* Skip variable is used to avoid descrepancies to Num IOs
+ * and IO Completions stats. Skip incrementing No IO Compls
+ * for pending active IOs after reset stats
+ */
+ atomic64_set(&fnic->io_cmpl_skip,
+ atomic64_read(&stats->io_stats.active_ios));
+ memset(&stats->abts_stats, 0, sizeof(struct abort_stats));
+ memset(&stats->term_stats, 0,
+ sizeof(struct terminate_stats));
+ memset(&stats->reset_stats, 0, sizeof(struct reset_stats));
+ memset(&stats->misc_stats, 0, sizeof(struct misc_stats));
+ memset(&stats->vlan_stats, 0, sizeof(struct vlan_stats));
+ memset(io_stats_p+1, 0,
+ sizeof(struct io_path_stats) - sizeof(u64));
+ memset(fw_stats_p+1, 0,
+ sizeof(struct fw_stats) - sizeof(u64));
+ ktime_get_real_ts64(&stats->stats_timestamps.last_reset_time);
+ }
+
+ (*ppos)++;
+ return cnt;
+}
+
+/*
+ * fnic_reset_stats_release - Release the buffer used to store
+ * debugfs file data
+ * @inode: The inode pointer
+ * @file: The file pointer that contains the buffer to release
+ *
+ * Description:
+ * This routine frees the buffer that was allocated when the debugfs
+ * file was opened.
+ *
+ * Returns:
+ * This function returns zero.
+ */
+static int fnic_reset_stats_release(struct inode *inode,
+ struct file *file)
+{
+ struct stats_debug_info *debug = file->private_data;
+ kfree(debug);
+ return 0;
+}
+
+/*
+ * fnic_stats_debugfs_open - Open the stats file for specific host
+ * and get fnic stats.
+ * @inode: The inode pointer.
+ * @file: The file pointer to attach the specific host statistics.
+ *
+ * Description:
+ * This routine opens a debugsfs file stats of specific host and print
+ * fnic stats.
+ *
+ * Returns:
+ * This function returns zero if successful.
+ */
+static int fnic_stats_debugfs_open(struct inode *inode,
+ struct file *file)
+{
+ struct fnic *fnic = inode->i_private;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ struct stats_debug_info *debug;
+ int buf_size = 2 * PAGE_SIZE;
+
+ debug = kzalloc(sizeof(struct stats_debug_info), GFP_KERNEL);
+ if (!debug)
+ return -ENOMEM;
+
+ debug->debug_buffer = vmalloc(buf_size);
+ if (!debug->debug_buffer) {
+ kfree(debug);
+ return -ENOMEM;
+ }
+
+ debug->buf_size = buf_size;
+ memset((void *)debug->debug_buffer, 0, buf_size);
+ debug->buffer_len = fnic_get_stats_data(debug, fnic_stats);
+ debug->buffer_len += fnic_get_debug_info(debug, fnic);
+
+ file->private_data = debug;
+
+ return 0;
+}
+
+/*
+ * fnic_stats_debugfs_read - Read a debugfs file
+ * @file: The file pointer to read from.
+ * @ubuf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @pos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the buffer indicated in the private_data
+ * field of @file. It will start reading at @pos and copy up to @nbytes of
+ * data to @ubuf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be
+ * less than @nbytes if the end of the file was reached).
+ */
+static ssize_t fnic_stats_debugfs_read(struct file *file,
+ char __user *ubuf,
+ size_t nbytes,
+ loff_t *pos)
+{
+ struct stats_debug_info *debug = file->private_data;
+ int rc = 0;
+ rc = simple_read_from_buffer(ubuf, nbytes, pos,
+ debug->debug_buffer,
+ debug->buffer_len);
+ return rc;
+}
+
+/*
+ * fnic_stats_stats_release - Release the buffer used to store
+ * debugfs file data
+ * @inode: The inode pointer
+ * @file: The file pointer that contains the buffer to release
+ *
+ * Description:
+ * This routine frees the buffer that was allocated when the debugfs
+ * file was opened.
+ *
+ * Returns:
+ * This function returns zero.
+ */
+static int fnic_stats_debugfs_release(struct inode *inode,
+ struct file *file)
+{
+ struct stats_debug_info *debug = file->private_data;
+ vfree(debug->debug_buffer);
+ kfree(debug);
+ return 0;
+}
+
+static const struct file_operations fnic_stats_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = fnic_stats_debugfs_open,
+ .read = fnic_stats_debugfs_read,
+ .release = fnic_stats_debugfs_release,
+};
+
+static const struct file_operations fnic_reset_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = fnic_reset_stats_open,
+ .read = fnic_reset_stats_read,
+ .write = fnic_reset_stats_write,
+ .release = fnic_reset_stats_release,
+};
+
+/*
+ * fnic_stats_init - Initialize stats struct and create stats file per fnic
+ *
+ * Description:
+ * When Debugfs is configured this routine sets up the stats file per fnic
+ * It will create file stats and reset_stats under statistics/host# directory
+ * to log per fnic stats.
+ */
+int fnic_stats_debugfs_init(struct fnic *fnic)
+{
+ char name[16];
+
+ snprintf(name, sizeof(name), "host%d", fnic->host->host_no);
+
+ fnic->fnic_stats_debugfs_host = debugfs_create_dir(name,
+ fnic_stats_debugfs_root);
+ fnic->fnic_stats_debugfs_file = debugfs_create_file("stats",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic->fnic_stats_debugfs_host,
+ fnic,
+ &fnic_stats_debugfs_fops);
+ fnic->fnic_reset_debugfs_file = debugfs_create_file("reset_stats",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ fnic->fnic_stats_debugfs_host,
+ fnic,
+ &fnic_reset_debugfs_fops);
+ return 0;
+}
+
+/*
+ * fnic_stats_debugfs_remove - Tear down debugfs infrastructure of stats
+ *
+ * Description:
+ * When Debugfs is configured this routine removes debugfs file system
+ * elements that are specific to fnic stats.
+ */
+void fnic_stats_debugfs_remove(struct fnic *fnic)
+{
+ if (!fnic)
+ return;
+
+ debugfs_remove(fnic->fnic_stats_debugfs_file);
+ fnic->fnic_stats_debugfs_file = NULL;
+
+ debugfs_remove(fnic->fnic_reset_debugfs_file);
+ fnic->fnic_reset_debugfs_file = NULL;
+
+ debugfs_remove(fnic->fnic_stats_debugfs_host);
+ fnic->fnic_stats_debugfs_host = NULL;
+}
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 3c53c3478ee7..103ab6f1f7cd 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/errno.h>
#include <linux/pci.h>
@@ -26,29 +14,182 @@
#include <linux/workqueue.h>
#include <scsi/fc/fc_fip.h>
#include <scsi/fc/fc_els.h>
-#include <scsi/fc/fc_fcoe.h>
#include <scsi/fc_frame.h>
-#include <scsi/libfc.h>
+#include <linux/etherdevice.h>
+#include <scsi/scsi_transport_fc.h>
#include "fnic_io.h"
#include "fnic.h"
+#include "fnic_fdls.h"
+#include "fdls_fc.h"
#include "cq_enet_desc.h"
#include "cq_exch_desc.h"
+#include "fip.h"
+
+#define MAX_RESET_WAIT_COUNT 64
struct workqueue_struct *fnic_event_queue;
-static void fnic_set_eth_mode(struct fnic *);
+static uint8_t FCOE_ALL_FCF_MAC[6] = FC_FCOE_FLOGI_MAC;
+
+/*
+ * Internal Functions
+ * This function will initialize the src_mac address to be
+ * used in outgoing frames
+ */
+static inline void fnic_fdls_set_fcoe_srcmac(struct fnic *fnic,
+ uint8_t *src_mac)
+{
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Setting src mac: %02x:%02x:%02x:%02x:%02x:%02x",
+ src_mac[0], src_mac[1], src_mac[2], src_mac[3],
+ src_mac[4], src_mac[5]);
+
+ memcpy(fnic->iport.fpma, src_mac, 6);
+}
+
+/*
+ * This function will initialize the dst_mac address to be
+ * used in outgoing frames
+ */
+static inline void fnic_fdls_set_fcoe_dstmac(struct fnic *fnic,
+ uint8_t *dst_mac)
+{
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Setting dst mac: %02x:%02x:%02x:%02x:%02x:%02x",
+ dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3],
+ dst_mac[4], dst_mac[5]);
+
+ memcpy(fnic->iport.fcfmac, dst_mac, 6);
+}
+
+void fnic_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct fnic *fnic = *((struct fnic **) shost_priv(shost));
+ struct fnic_iport_s *iport = &fnic->iport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (!fnic->link_status)
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ else if (iport->state == FNIC_IPORT_STATE_READY)
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+void fnic_fdls_link_status_change(struct fnic *fnic, int linkup)
+{
+ struct fnic_iport_s *iport = &fnic->iport;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "link up: %d, usefip: %d", linkup, iport->usefip);
+
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+ if (linkup) {
+ if (iport->usefip) {
+ iport->state = FNIC_IPORT_STATE_FIP;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "link up: %d, usefip: %d", linkup, iport->usefip);
+ fnic_fcoe_send_vlan_req(fnic);
+ } else {
+ iport->state = FNIC_IPORT_STATE_FABRIC_DISC;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport->state: %d", iport->state);
+ fnic_fdls_disc_start(iport);
+ }
+ } else {
+ iport->state = FNIC_IPORT_STATE_LINK_WAIT;
+ if (!is_zero_ether_addr(iport->fpma))
+ vnic_dev_del_addr(fnic->vdev, iport->fpma);
+ fnic_common_fip_cleanup(fnic);
+ fnic_fdls_link_down(iport);
+
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+
+/*
+ * FPMA can be either taken from ethhdr(dst_mac) or flogi resp
+ * or derive from FC_MAP and FCID combination. While it should be
+ * same, revisit this if there is any possibility of not-correct.
+ */
+void fnic_fdls_learn_fcoe_macs(struct fnic_iport_s *iport, void *rx_frame,
+ uint8_t *fcid)
+{
+ struct fnic *fnic = iport->fnic;
+ struct ethhdr *ethhdr = (struct ethhdr *) rx_frame;
+ uint8_t fcmac[6] = { 0x0E, 0xFC, 0x00, 0x00, 0x00, 0x00 };
+
+ memcpy(&fcmac[3], fcid, 3);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "learn fcoe: dst_mac: %02x:%02x:%02x:%02x:%02x:%02x",
+ ethhdr->h_dest[0], ethhdr->h_dest[1],
+ ethhdr->h_dest[2], ethhdr->h_dest[3],
+ ethhdr->h_dest[4], ethhdr->h_dest[5]);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "learn fcoe: fc_mac: %02x:%02x:%02x:%02x:%02x:%02x",
+ fcmac[0], fcmac[1], fcmac[2], fcmac[3], fcmac[4],
+ fcmac[5]);
+
+ fnic_fdls_set_fcoe_srcmac(fnic, fcmac);
+ fnic_fdls_set_fcoe_dstmac(fnic, ethhdr->h_source);
+}
+
+void fnic_fdls_init(struct fnic *fnic, int usefip)
+{
+ struct fnic_iport_s *iport = &fnic->iport;
+
+ /* Initialize iPort structure */
+ iport->state = FNIC_IPORT_STATE_INIT;
+ iport->fnic = fnic;
+ iport->usefip = usefip;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iportsrcmac: %02x:%02x:%02x:%02x:%02x:%02x",
+ iport->hwmac[0], iport->hwmac[1], iport->hwmac[2],
+ iport->hwmac[3], iport->hwmac[4], iport->hwmac[5]);
+
+ INIT_LIST_HEAD(&iport->tport_list);
+ INIT_LIST_HEAD(&iport->tport_list_pending_del);
+
+ fnic_fdls_disc_init(iport);
+}
void fnic_handle_link(struct work_struct *work)
{
struct fnic *fnic = container_of(work, struct fnic, link_work);
- unsigned long flags;
int old_link_status;
u32 old_link_down_cnt;
+ int max_count = 0;
- spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (vnic_dev_get_intr_mode(fnic->vdev) != VNIC_DEV_INTR_MODE_MSI)
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Interrupt mode is not MSI\n");
+
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
if (fnic->stop_rx_link_events) {
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Stop link rx events\n");
+ return;
+ }
+
+ /* Do not process if the fnic is already in transitional state */
+ if ((fnic->state != FNIC_IN_ETH_MODE)
+ && (fnic->state != FNIC_IN_FC_MODE)) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic in transitional state: %d. link up: %d ignored",
+ fnic->state, vnic_dev_link_status(fnic->vdev));
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Current link status: %d iport state: %d\n",
+ fnic->link_status, fnic->iport.state);
return;
}
@@ -57,125 +198,195 @@ void fnic_handle_link(struct work_struct *work)
fnic->link_status = vnic_dev_link_status(fnic->vdev);
fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
+ while (fnic->reset_in_progress == IN_PROGRESS) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic reset in progress. Link event needs to wait\n");
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "waiting for reset completion\n");
+ wait_for_completion_timeout(&fnic->reset_completion_wait,
+ msecs_to_jiffies(5000));
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "woken up from reset completion wait\n");
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+ max_count++;
+ if (max_count >= MAX_RESET_WAIT_COUNT) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Rstth waited for too long. Skipping handle link event\n");
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ return;
+ }
+ }
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Marking fnic reset in progress\n");
+ fnic->reset_in_progress = IN_PROGRESS;
+
+ if ((vnic_dev_get_intr_mode(fnic->vdev) != VNIC_DEV_INTR_MODE_MSI) ||
+ (fnic->link_status != old_link_status)) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "old link status: %d link status: %d\n",
+ old_link_status, (int) fnic->link_status);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "old down count %d down count: %d\n",
+ old_link_down_cnt, (int) fnic->link_down_cnt);
+ }
+
if (old_link_status == fnic->link_status) {
- if (!fnic->link_status)
+ if (!fnic->link_status) {
/* DOWN -> DOWN */
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- else {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "down->down\n");
+ } else {
if (old_link_down_cnt != fnic->link_down_cnt) {
/* UP -> DOWN -> UP */
- fnic->lport->host_stats.link_failure_count++;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "link down\n");
- fcoe_ctlr_link_down(&fnic->ctlr);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "link up\n");
- fcoe_ctlr_link_up(&fnic->ctlr);
- } else
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "up->down. Link down\n");
+ fnic_fdls_link_status_change(fnic, 0);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "down->up. Link up\n");
+ fnic_fdls_link_status_change(fnic, 1);
+ } else {
/* UP -> UP */
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "up->up\n");
+ }
}
} else if (fnic->link_status) {
/* DOWN -> UP */
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
- fcoe_ctlr_link_up(&fnic->ctlr);
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "down->up. Link up\n");
+ fnic_fdls_link_status_change(fnic, 1);
} else {
/* UP -> DOWN */
- fnic->lport->host_stats.link_failure_count++;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
- fcoe_ctlr_link_down(&fnic->ctlr);
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "up->down. Link down\n");
+ fnic_fdls_link_status_change(fnic, 0);
}
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ fnic->reset_in_progress = NOT_IN_PROGRESS;
+ complete(&fnic->reset_completion_wait);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Marking fnic reset completion\n");
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
}
-/*
- * This function passes incoming fabric frames to libFC
- */
void fnic_handle_frame(struct work_struct *work)
{
struct fnic *fnic = container_of(work, struct fnic, frame_work);
- struct fc_lport *lp = fnic->lport;
- unsigned long flags;
- struct sk_buff *skb;
- struct fc_frame *fp;
+ struct fnic_frame_list *cur_frame, *next;
+ int fchdr_offset = 0;
- while ((skb = skb_dequeue(&fnic->frame_queue))) {
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ list_for_each_entry_safe(cur_frame, next, &fnic->frame_queue, links) {
+ if (fnic->stop_rx_link_events) {
+ list_del(&cur_frame->links);
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ kfree(cur_frame->fp);
+ mempool_free(cur_frame, fnic->frame_elem_pool);
+ return;
+ }
- spin_lock_irqsave(&fnic->fnic_lock, flags);
+ /*
+ * If we're in a transitional state, just re-queue and return.
+ * The queue will be serviced when we get to a stable state.
+ */
+ if (fnic->state != FNIC_IN_FC_MODE &&
+ fnic->state != FNIC_IN_ETH_MODE) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Cannot process frame in transitional state\n");
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ return;
+ }
+
+ list_del(&cur_frame->links);
+
+ /* Frames from FCP_RQ will have ethhdrs stripped off */
+ fchdr_offset = (cur_frame->rx_ethhdr_stripped) ?
+ 0 : FNIC_ETH_FCOE_HDRS_OFFSET;
+
+ fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp,
+ cur_frame->frame_len, fchdr_offset);
+
+ kfree(cur_frame->fp);
+ mempool_free(cur_frame, fnic->frame_elem_pool);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+void fnic_handle_fip_frame(struct work_struct *work)
+{
+ struct fnic_frame_list *cur_frame, *next;
+ struct fnic *fnic = container_of(work, struct fnic, fip_frame_work);
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Processing FIP frame\n");
+
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ list_for_each_entry_safe(cur_frame, next, &fnic->fip_frame_queue,
+ links) {
if (fnic->stop_rx_link_events) {
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- dev_kfree_skb(skb);
+ list_del(&cur_frame->links);
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+ kfree(cur_frame->fp);
+ kfree(cur_frame);
return;
}
- fp = (struct fc_frame *)skb;
/*
* If we're in a transitional state, just re-queue and return.
* The queue will be serviced when we get to a stable state.
*/
if (fnic->state != FNIC_IN_FC_MODE &&
- fnic->state != FNIC_IN_ETH_MODE) {
- skb_queue_head(&fnic->frame_queue, skb);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ fnic->state != FNIC_IN_ETH_MODE) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
return;
}
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- fc_exch_recv(lp, fp);
+ list_del(&cur_frame->links);
+
+ if (fdls_fip_recv_frame(fnic, cur_frame->fp)) {
+ kfree(cur_frame->fp);
+ kfree(cur_frame);
+ }
}
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
}
/**
* fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame.
* @fnic: fnic instance.
- * @skb: Ethernet Frame.
+ * @fp: Ethernet Frame.
*/
-static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb)
+static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, void *fp)
{
- struct fc_frame *fp;
struct ethhdr *eh;
- struct fcoe_hdr *fcoe_hdr;
- struct fcoe_crc_eof *ft;
-
- /*
- * Undo VLAN encapsulation if present.
- */
- eh = (struct ethhdr *)skb->data;
- if (eh->h_proto == htons(ETH_P_8021Q)) {
- memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
- eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
- skb_reset_mac_header(skb);
- }
- if (eh->h_proto == htons(ETH_P_FIP)) {
- skb_pull(skb, sizeof(*eh));
- fcoe_ctlr_recv(&fnic->ctlr, skb);
- return 1; /* let caller know packet was used */
- }
- if (eh->h_proto != htons(ETH_P_FCOE))
- goto drop;
- skb_set_network_header(skb, sizeof(*eh));
- skb_pull(skb, sizeof(*eh));
-
- fcoe_hdr = (struct fcoe_hdr *)skb->data;
- if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER)
- goto drop;
-
- fp = (struct fc_frame *)skb;
- fc_frame_init(fp);
- fr_sof(fp) = fcoe_hdr->fcoe_sof;
- skb_pull(skb, sizeof(struct fcoe_hdr));
- skb_reset_transport_header(skb);
+ struct fnic_frame_list *fip_fr_elem;
+ unsigned long flags;
- ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft));
- fr_eof(fp) = ft->fcoe_eof;
- skb_trim(skb, skb->len - sizeof(*ft));
- return 0;
-drop:
- dev_kfree_skb_irq(skb);
- return -1;
+ eh = (struct ethhdr *) fp;
+ if ((eh->h_proto == cpu_to_be16(ETH_P_FIP)) && (fnic->iport.usefip)) {
+ fip_fr_elem = (struct fnic_frame_list *)
+ kzalloc(sizeof(struct fnic_frame_list), GFP_ATOMIC);
+ if (!fip_fr_elem)
+ return 0;
+ fip_fr_elem->fp = fp;
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_add_tail(&fip_fr_elem->links, &fnic->fip_frame_queue);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ queue_work(fnic_fip_queue, &fnic->fip_frame_work);
+ return 1; /* let caller know packet was used */
+ } else
+ return 0;
}
/**
@@ -187,103 +398,24 @@ drop:
*/
void fnic_update_mac_locked(struct fnic *fnic, u8 *new)
{
- u8 *ctl = fnic->ctlr.ctl_src_addr;
+ struct fnic_iport_s *iport = &fnic->iport;
+ u8 *ctl = iport->hwmac;
u8 *data = fnic->data_src_addr;
if (is_zero_ether_addr(new))
new = ctl;
- if (!compare_ether_addr(data, new))
+ if (ether_addr_equal(data, new))
return;
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
- if (!is_zero_ether_addr(data) && compare_ether_addr(data, ctl))
- vnic_dev_del_addr(fnic->vdev, data);
- memcpy(data, new, ETH_ALEN);
- if (compare_ether_addr(new, ctl))
- vnic_dev_add_addr(fnic->vdev, new);
-}
-/**
- * fnic_update_mac() - set data MAC address and filters.
- * @lport: local port.
- * @new: newly-assigned FCoE MAC address.
- */
-void fnic_update_mac(struct fc_lport *lport, u8 *new)
-{
- struct fnic *fnic = lport_priv(lport);
-
- spin_lock_irq(&fnic->fnic_lock);
- fnic_update_mac_locked(fnic, new);
- spin_unlock_irq(&fnic->fnic_lock);
-}
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Update MAC: %u\n", *new);
-/**
- * fnic_set_port_id() - set the port_ID after successful FLOGI.
- * @lport: local port.
- * @port_id: assigned FC_ID.
- * @fp: received frame containing the FLOGI accept or NULL.
- *
- * This is called from libfc when a new FC_ID has been assigned.
- * This causes us to reset the firmware to FC_MODE and setup the new MAC
- * address and FC_ID.
- *
- * It is also called with FC_ID 0 when we're logged off.
- *
- * If the FC_ID is due to point-to-point, fp may be NULL.
- */
-void fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
-{
- struct fnic *fnic = lport_priv(lport);
- u8 *mac;
- int ret;
-
- FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n",
- port_id, fp);
-
- /*
- * If we're clearing the FC_ID, change to use the ctl_src_addr.
- * Set ethernet mode to send FLOGI.
- */
- if (!port_id) {
- fnic_update_mac(lport, fnic->ctlr.ctl_src_addr);
- fnic_set_eth_mode(fnic);
- return;
- }
-
- if (fp) {
- mac = fr_cb(fp)->granted_mac;
- if (is_zero_ether_addr(mac)) {
- /* non-FIP - FLOGI already accepted - ignore return */
- fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp);
- }
- fnic_update_mac(lport, mac);
- }
-
- /* Change state to reflect transition to FC mode */
- spin_lock_irq(&fnic->fnic_lock);
- if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
- fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
- else {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "Unexpected fnic state %s while"
- " processing flogi resp\n",
- fnic_state_to_str(fnic->state));
- spin_unlock_irq(&fnic->fnic_lock);
- return;
- }
- spin_unlock_irq(&fnic->fnic_lock);
-
- /*
- * Send FLOGI registration to firmware to set up FC mode.
- * The new address will be set up when registration completes.
- */
- ret = fnic_flogi_reg_handler(fnic, port_id);
+ if (!is_zero_ether_addr(data) && !ether_addr_equal(data, ctl))
+ vnic_dev_del_addr(fnic->vdev, data);
- if (ret < 0) {
- spin_lock_irq(&fnic->fnic_lock);
- if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
- fnic->state = FNIC_IN_ETH_MODE;
- spin_unlock_irq(&fnic->fnic_lock);
- }
+ memcpy(data, new, ETH_ALEN);
+ if (!ether_addr_equal(new, ctl))
+ vnic_dev_add_addr(fnic->vdev, new);
}
static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
@@ -292,96 +424,121 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
void *opaque)
{
struct fnic *fnic = vnic_dev_priv(rq->vdev);
- struct sk_buff *skb;
- struct fc_frame *fp;
- unsigned int eth_hdrs_stripped;
+ uint8_t *fp;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ unsigned int ethhdr_stripped;
u8 type, color, eop, sop, ingress_port, vlan_stripped;
- u8 fcoe = 0, fcoe_sof, fcoe_eof;
- u8 fcoe_fc_crc_ok = 1, fcoe_enc_error = 0;
- u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok;
- u8 ipv6, ipv4, ipv4_fragment, rss_type, csum_not_calc;
+ u8 fcoe_fnic_crc_ok = 1, fcoe_enc_error = 0;
u8 fcs_ok = 1, packet_error = 0;
- u16 q_number, completed_index, bytes_written = 0, vlan, checksum;
+ u16 q_number, completed_index, vlan;
u32 rss_hash;
+ u16 checksum;
+ u8 csum_not_calc, rss_type, ipv4, ipv6, ipv4_fragment;
+ u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok;
+ u8 fcoe = 0, fcoe_sof, fcoe_eof;
u16 exchange_id, tmpl;
u8 sof = 0;
u8 eof = 0;
u32 fcp_bytes_written = 0;
+ u16 enet_bytes_written = 0;
+ u32 bytes_written = 0;
unsigned long flags;
+ struct fnic_frame_list *frame_elem = NULL;
+ struct ethhdr *eh;
- pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
- PCI_DMA_FROMDEVICE);
- skb = buf->os_buf;
- fp = (struct fc_frame *)skb;
+ dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
+ DMA_FROM_DEVICE);
+ fp = (uint8_t *) buf->os_buf;
buf->os_buf = NULL;
cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
if (type == CQ_DESC_TYPE_RQ_FCP) {
- cq_fcp_rq_desc_dec((struct cq_fcp_rq_desc *)cq_desc,
- &type, &color, &q_number, &completed_index,
- &eop, &sop, &fcoe_fc_crc_ok, &exchange_id,
- &tmpl, &fcp_bytes_written, &sof, &eof,
- &ingress_port, &packet_error,
- &fcoe_enc_error, &fcs_ok, &vlan_stripped,
- &vlan);
- eth_hdrs_stripped = 1;
- skb_trim(skb, fcp_bytes_written);
- fr_sof(fp) = sof;
- fr_eof(fp) = eof;
-
+ cq_fcp_rq_desc_dec((struct cq_fcp_rq_desc *) cq_desc, &type,
+ &color, &q_number, &completed_index, &eop, &sop,
+ &fcoe_fnic_crc_ok, &exchange_id, &tmpl,
+ &fcp_bytes_written, &sof, &eof, &ingress_port,
+ &packet_error, &fcoe_enc_error, &fcs_ok,
+ &vlan_stripped, &vlan);
+ ethhdr_stripped = 1;
+ bytes_written = fcp_bytes_written;
} else if (type == CQ_DESC_TYPE_RQ_ENET) {
- cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
- &type, &color, &q_number, &completed_index,
- &ingress_port, &fcoe, &eop, &sop,
- &rss_type, &csum_not_calc, &rss_hash,
- &bytes_written, &packet_error,
- &vlan_stripped, &vlan, &checksum,
- &fcoe_sof, &fcoe_fc_crc_ok,
- &fcoe_enc_error, &fcoe_eof,
- &tcp_udp_csum_ok, &udp, &tcp,
- &ipv4_csum_ok, &ipv6, &ipv4,
- &ipv4_fragment, &fcs_ok);
- eth_hdrs_stripped = 0;
- skb_trim(skb, bytes_written);
+ cq_enet_rq_desc_dec((struct cq_enet_rq_desc *) cq_desc, &type,
+ &color, &q_number, &completed_index,
+ &ingress_port, &fcoe, &eop, &sop, &rss_type,
+ &csum_not_calc, &rss_hash, &enet_bytes_written,
+ &packet_error, &vlan_stripped, &vlan,
+ &checksum, &fcoe_sof, &fcoe_fnic_crc_ok,
+ &fcoe_enc_error, &fcoe_eof, &tcp_udp_csum_ok,
+ &udp, &tcp, &ipv4_csum_ok, &ipv6, &ipv4,
+ &ipv4_fragment, &fcs_ok);
+
+ ethhdr_stripped = 0;
+ bytes_written = enet_bytes_written;
+
if (!fcs_ok) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "fcs error. dropping packet.\n");
+ atomic64_inc(&fnic_stats->misc_stats.frame_errors);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic 0x%p fcs error. Dropping packet.\n", fnic);
goto drop;
}
- if (fnic_import_rq_eth_pkt(fnic, skb))
- return;
+ eh = (struct ethhdr *) fp;
+ if (eh->h_proto != cpu_to_be16(ETH_P_FCOE)) {
+
+ if (fnic_import_rq_eth_pkt(fnic, fp))
+ return;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Dropping h_proto 0x%x",
+ be16_to_cpu(eh->h_proto));
+ goto drop;
+ }
} else {
- /* wrong CQ type*/
- shost_printk(KERN_ERR, fnic->lport->host,
- "fnic rq_cmpl wrong cq type x%x\n", type);
+ /* wrong CQ type */
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic rq_cmpl wrong cq type x%x\n", type);
goto drop;
}
- if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "fnic rq_cmpl fcoe x%x fcsok x%x"
- " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
- " x%x\n",
- fcoe, fcs_ok, packet_error,
- fcoe_fc_crc_ok, fcoe_enc_error);
+ if (!fcs_ok || packet_error || !fcoe_fnic_crc_ok || fcoe_enc_error) {
+ atomic64_inc(&fnic_stats->misc_stats.frame_errors);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fcoe %x fcsok %x pkterr %x ffco %x fee %x\n",
+ fcoe, fcs_ok, packet_error,
+ fcoe_fnic_crc_ok, fcoe_enc_error);
goto drop;
}
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (fnic->stop_rx_link_events) {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic->stop_rx_link_events: %d\n",
+ fnic->stop_rx_link_events);
goto drop;
}
- fr_dev(fp) = fnic->lport;
+
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- skb_queue_tail(&fnic->frame_queue, skb);
- queue_work(fnic_event_queue, &fnic->frame_work);
+ frame_elem = mempool_alloc(fnic->frame_elem_pool,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!frame_elem) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Failed to allocate memory for frame elem");
+ goto drop;
+ }
+ frame_elem->fp = fp;
+ frame_elem->rx_ethhdr_stripped = ethhdr_stripped;
+ frame_elem->frame_len = bytes_written;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_add_tail(&frame_elem->links, &fnic->frame_queue);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ queue_work(fnic_event_queue, &fnic->frame_work);
return;
+
drop:
- dev_kfree_skb_irq(skb);
+ kfree(fp);
}
static int fnic_rq_cmpl_handler_cont(struct vnic_dev *vdev,
@@ -407,10 +564,10 @@ int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
cur_work_done = vnic_cq_service(&fnic->cq[i], rq_work_to_do,
fnic_rq_cmpl_handler_cont,
NULL);
- if (cur_work_done) {
+ if (cur_work_done && fnic->stop_rx_link_events != 1) {
err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
if (err)
- shost_printk(KERN_ERR, fnic->lport->host,
+ shost_printk(KERN_ERR, fnic->host,
"fnic_alloc_rq_frame can't alloc"
" frame\n");
}
@@ -428,183 +585,186 @@ int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
int fnic_alloc_rq_frame(struct vnic_rq *rq)
{
struct fnic *fnic = vnic_dev_priv(rq->vdev);
- struct sk_buff *skb;
+ void *buf;
u16 len;
dma_addr_t pa;
+ int ret;
- len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM;
- skb = dev_alloc_skb(len);
- if (!skb) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "Unable to allocate RQ sk_buff\n");
+ len = FNIC_FRAME_HT_ROOM;
+ buf = kmalloc(len, GFP_ATOMIC);
+ if (!buf) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Unable to allocate RQ buffer of size: %d\n", len);
return -ENOMEM;
}
- skb_reset_mac_header(skb);
- skb_reset_transport_header(skb);
- skb_reset_network_header(skb);
- skb_put(skb, len);
- pa = pci_map_single(fnic->pdev, skb->data, len, PCI_DMA_FROMDEVICE);
- fnic_queue_rq_desc(rq, skb, pa, len);
+
+ pa = dma_map_single(&fnic->pdev->dev, buf, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&fnic->pdev->dev, pa)) {
+ ret = -ENOMEM;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PCI mapping failed with error %d\n", ret);
+ goto free_buf;
+ }
+
+ fnic_queue_rq_desc(rq, buf, pa, len);
return 0;
+free_buf:
+ kfree(buf);
+ return ret;
}
void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
{
- struct fc_frame *fp = buf->os_buf;
+ void *rq_buf = buf->os_buf;
struct fnic *fnic = vnic_dev_priv(rq->vdev);
- pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
- PCI_DMA_FROMDEVICE);
+ dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
+ DMA_FROM_DEVICE);
- dev_kfree_skb(fp_skb(fp));
+ kfree(rq_buf);
buf->os_buf = NULL;
}
-/**
- * fnic_eth_send() - Send Ethernet frame.
- * @fip: fcoe_ctlr instance.
- * @skb: Ethernet Frame, FIP, without VLAN encapsulation.
- */
-void fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
-{
- struct fnic *fnic = fnic_from_ctlr(fip);
- struct vnic_wq *wq = &fnic->wq[0];
- dma_addr_t pa;
- struct ethhdr *eth_hdr;
- struct vlan_ethhdr *vlan_hdr;
- unsigned long flags;
-
- if (!fnic->vlan_hw_insert) {
- eth_hdr = (struct ethhdr *)skb_mac_header(skb);
- vlan_hdr = (struct vlan_ethhdr *)skb_push(skb,
- sizeof(*vlan_hdr) - sizeof(*eth_hdr));
- memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN);
- vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
- vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto;
- vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
- }
-
- pa = pci_map_single(fnic->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
-
- spin_lock_irqsave(&fnic->wq_lock[0], flags);
- if (!vnic_wq_desc_avail(wq)) {
- pci_unmap_single(fnic->pdev, pa, skb->len, PCI_DMA_TODEVICE);
- spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
- kfree_skb(skb);
- return;
- }
-
- fnic_queue_wq_eth_desc(wq, skb, pa, skb->len,
- fnic->vlan_hw_insert, fnic->vlan_id, 1);
- spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
-}
-
/*
* Send FC frame.
*/
-static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
+static int fnic_send_frame(struct fnic *fnic, void *frame, int frame_len)
{
struct vnic_wq *wq = &fnic->wq[0];
- struct sk_buff *skb;
dma_addr_t pa;
- struct ethhdr *eth_hdr;
- struct vlan_ethhdr *vlan_hdr;
- struct fcoe_hdr *fcoe_hdr;
- struct fc_frame_header *fh;
- u32 tot_len, eth_hdr_len;
int ret = 0;
unsigned long flags;
- fh = fc_frame_header_get(fp);
- skb = fp_skb(fp);
-
- if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
- fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb))
- return 0;
+ pa = dma_map_single(&fnic->pdev->dev, frame, frame_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&fnic->pdev->dev, pa))
+ return -ENOMEM;
- if (!fnic->vlan_hw_insert) {
- eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr);
- vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, eth_hdr_len);
- eth_hdr = (struct ethhdr *)vlan_hdr;
- vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
- vlan_hdr->h_vlan_encapsulated_proto = htons(ETH_P_FCOE);
- vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
- fcoe_hdr = (struct fcoe_hdr *)(vlan_hdr + 1);
- } else {
- eth_hdr_len = sizeof(*eth_hdr) + sizeof(*fcoe_hdr);
- eth_hdr = (struct ethhdr *)skb_push(skb, eth_hdr_len);
- eth_hdr->h_proto = htons(ETH_P_FCOE);
- fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1);
+ if ((fnic_fc_trace_set_data(fnic->fnic_num,
+ FNIC_FC_SEND | 0x80, (char *) frame,
+ frame_len)) != 0) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic ctlr frame trace error");
}
- if (fnic->ctlr.map_dest)
- fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
- else
- memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN);
- memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
-
- tot_len = skb->len;
- BUG_ON(tot_len % 4);
-
- memset(fcoe_hdr, 0, sizeof(*fcoe_hdr));
- fcoe_hdr->fcoe_sof = fr_sof(fp);
- if (FC_FCOE_VER)
- FC_FCOE_ENCAPS_VER(fcoe_hdr, FC_FCOE_VER);
-
- pa = pci_map_single(fnic->pdev, eth_hdr, tot_len, PCI_DMA_TODEVICE);
-
spin_lock_irqsave(&fnic->wq_lock[0], flags);
if (!vnic_wq_desc_avail(wq)) {
- pci_unmap_single(fnic->pdev, pa,
- tot_len, PCI_DMA_TODEVICE);
+ dma_unmap_single(&fnic->pdev->dev, pa, frame_len, DMA_TO_DEVICE);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "vnic work queue descriptor is not available");
ret = -1;
goto fnic_send_frame_end;
}
- fnic_queue_wq_desc(wq, skb, pa, tot_len, fr_eof(fp),
- fnic->vlan_hw_insert, fnic->vlan_id, 1, 1, 1);
+ /* hw inserts cos value */
+ fnic_queue_wq_desc(wq, frame, pa, frame_len, FC_EOF_T,
+ 0, fnic->vlan_id, 1, 1, 1);
+
fnic_send_frame_end:
spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
-
- if (ret)
- dev_kfree_skb_any(fp_skb(fp));
-
return ret;
}
-/*
- * fnic_send
- * Routine to send a raw frame
+/**
+ * fdls_send_fcoe_frame - send a filled-in FC frame, filling in eth and FCoE
+ * info. This interface is used only in the non fast path. (login, fabric
+ * registrations etc.)
+ *
+ * @fnic: fnic instance
+ * @frame: frame structure with FC payload filled in
+ * @frame_size: length of the frame to be sent
+ * @srcmac: source mac address
+ * @dstmac: destination mac address
+ *
+ * Called with the fnic lock held.
*/
-int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
+static int
+fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size,
+ uint8_t *srcmac, uint8_t *dstmac)
{
- struct fnic *fnic = lport_priv(lp);
- unsigned long flags;
+ struct ethhdr *pethhdr;
+ struct fcoe_hdr *pfcoe_hdr;
+ struct fnic_frame_list *frame_elem;
+ int len = frame_size;
+ int ret;
+ struct fc_frame_header *fchdr = (struct fc_frame_header *) (frame +
+ FNIC_ETH_FCOE_HDRS_OFFSET);
- if (fnic->in_remove) {
- dev_kfree_skb(fp_skb(fp));
- return -1;
- }
+ pethhdr = (struct ethhdr *) frame;
+ pethhdr->h_proto = cpu_to_be16(ETH_P_FCOE);
+ memcpy(pethhdr->h_source, srcmac, ETH_ALEN);
+ memcpy(pethhdr->h_dest, dstmac, ETH_ALEN);
+
+ pfcoe_hdr = (struct fcoe_hdr *) (frame + sizeof(struct ethhdr));
+ pfcoe_hdr->fcoe_sof = FC_SOF_I3;
/*
* Queue frame if in a transitional state.
* This occurs while registering the Port_ID / MAC address after FLOGI.
*/
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) {
- skb_queue_tail(&fnic->tx_queue, fp_skb(fp));
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ if ((fnic->state != FNIC_IN_FC_MODE)
+ && (fnic->state != FNIC_IN_ETH_MODE)) {
+ frame_elem = mempool_alloc(fnic->frame_elem_pool,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!frame_elem) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Failed to allocate memory for frame elem");
+ return -ENOMEM;
+ }
+
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Queueing FC frame: sid/did/type/oxid = 0x%x/0x%x/0x%x/0x%x\n",
+ ntoh24(fchdr->fh_s_id), ntoh24(fchdr->fh_d_id),
+ fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr));
+ frame_elem->fp = frame;
+ frame_elem->frame_len = len;
+ list_add_tail(&frame_elem->links, &fnic->tx_queue);
return 0;
}
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- return fnic_send_frame(fnic, fp);
+ fnic_debug_dump_fc_frame(fnic, fchdr, frame_size, "Outgoing");
+
+ ret = fnic_send_frame(fnic, frame, len);
+ return ret;
+}
+
+void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
+ int frame_size)
+{
+ struct fnic *fnic = iport->fnic;
+ uint8_t *dstmac, *srcmac;
+
+ /* If module unload is in-progress, don't send */
+ if (fnic->in_remove)
+ return;
+
+ if (iport->fabric.flags & FNIC_FDLS_FPMA_LEARNT) {
+ srcmac = iport->fpma;
+ dstmac = iport->fcfmac;
+ } else {
+ srcmac = iport->hwmac;
+ dstmac = FCOE_ALL_FCF_MAC;
+ }
+
+ fdls_send_fcoe_frame(fnic, frame, frame_size, srcmac, dstmac);
+}
+
+int
+fnic_send_fip_frame(struct fnic_iport_s *iport, void *frame,
+ int frame_size)
+{
+ struct fnic *fnic = iport->fnic;
+
+ if (fnic->in_remove)
+ return -1;
+
+ fnic_debug_dump_fip_frame(fnic, frame, frame_size, "Outgoing");
+ return fnic_send_frame(fnic, frame, frame_size);
}
/**
* fnic_flush_tx() - send queued frames.
- * @fnic: fnic device
+ * @work: pointer to work element
*
* Send frames that were waiting to go out in FC or Ethernet mode.
* Whenever changing modes we purge queued frames, so these frames should
@@ -612,66 +772,90 @@ int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
*
* Called without fnic_lock held.
*/
-void fnic_flush_tx(struct fnic *fnic)
+void fnic_flush_tx(struct work_struct *work)
{
- struct sk_buff *skb;
+ struct fnic *fnic = container_of(work, struct fnic, flush_work);
struct fc_frame *fp;
+ struct fnic_frame_list *cur_frame, *next;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Flush queued frames");
- while ((skb = skb_dequeue(&fnic->tx_queue))) {
- fp = (struct fc_frame *)skb;
- fnic_send_frame(fnic, fp);
+ list_for_each_entry_safe(cur_frame, next, &fnic->tx_queue, links) {
+ fp = cur_frame->fp;
+ list_del(&cur_frame->links);
+ fnic_send_frame(fnic, fp, cur_frame->frame_len);
+ mempool_free(cur_frame, fnic->frame_elem_pool);
}
}
-/**
- * fnic_set_eth_mode() - put fnic into ethernet mode.
- * @fnic: fnic device
- *
- * Called without fnic lock held.
- */
-static void fnic_set_eth_mode(struct fnic *fnic)
+int
+fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id,
+ void *fp)
{
- unsigned long flags;
- enum fnic_state old_state;
+ struct fnic *fnic = iport->fnic;
+ struct ethhdr *ethhdr;
int ret;
- spin_lock_irqsave(&fnic->fnic_lock, flags);
-again:
- old_state = fnic->state;
- switch (old_state) {
- case FNIC_IN_FC_MODE:
- case FNIC_IN_ETH_TRANS_FC_MODE:
- default:
- fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Setting port id: 0x%x fp: 0x%p fnic state: %d", port_id,
+ fp, fnic->state);
- ret = fnic_fw_reset_handler(fnic);
+ if (fp) {
+ ethhdr = (struct ethhdr *) fp;
+ vnic_dev_add_addr(fnic->vdev, ethhdr->h_dest);
+ }
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE)
- goto again;
- if (ret)
- fnic->state = old_state;
- break;
-
- case FNIC_IN_FC_TRANS_ETH_MODE:
- case FNIC_IN_ETH_MODE:
- break;
+ /* Change state to reflect transition to FC mode */
+ if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
+ fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
+ else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Unexpected fnic state while processing FLOGI response\n");
+ return -1;
+ }
+
+ /*
+ * Send FLOGI registration to firmware to set up FC mode.
+ * The new address will be set up when registration completes.
+ */
+ ret = fnic_flogi_reg_handler(fnic, port_id);
+ if (ret < 0) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI registration error ret: %d fnic state: %d\n",
+ ret, fnic->state);
+ if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
+ fnic->state = FNIC_IN_ETH_MODE;
+
+ return -1;
+ }
+ iport->fabric.flags |= FNIC_FDLS_FPMA_LEARNT;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI registration success\n");
+ return 0;
+}
+
+void fnic_free_txq(struct list_head *head)
+{
+ struct fnic_frame_list *cur_frame, *next;
+
+ list_for_each_entry_safe(cur_frame, next, head, links) {
+ list_del(&cur_frame->links);
+ kfree(cur_frame->fp);
+ kfree(cur_frame);
}
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
}
static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
struct cq_desc *cq_desc,
struct vnic_wq_buf *buf, void *opaque)
{
- struct sk_buff *skb = buf->os_buf;
- struct fc_frame *fp = (struct fc_frame *)skb;
struct fnic *fnic = vnic_dev_priv(wq->vdev);
- pci_unmap_single(fnic->pdev, buf->dma_addr,
- buf->len, PCI_DMA_TODEVICE);
- dev_kfree_skb_irq(fp_skb(fp));
+ dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
+ DMA_TO_DEVICE);
+ mempool_free(buf->os_buf, fnic->frame_pool);
buf->os_buf = NULL;
}
@@ -709,12 +893,218 @@ int fnic_wq_cmpl_handler(struct fnic *fnic, int work_to_do)
void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
{
- struct fc_frame *fp = buf->os_buf;
struct fnic *fnic = vnic_dev_priv(wq->vdev);
- pci_unmap_single(fnic->pdev, buf->dma_addr,
- buf->len, PCI_DMA_TODEVICE);
+ dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
+ DMA_TO_DEVICE);
- dev_kfree_skb(fp_skb(fp));
+ kfree(buf->os_buf);
buf->os_buf = NULL;
}
+
+void
+fnic_fdls_add_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport,
+ unsigned long flags)
+{
+ struct fnic *fnic = iport->fnic;
+ struct fc_rport *rport;
+ struct fc_rport_identifiers ids;
+ struct rport_dd_data_s *rdd_data;
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Adding rport fcid: 0x%x", tport->fcid);
+
+ ids.node_name = tport->wwnn;
+ ids.port_name = tport->wwpn;
+ ids.port_id = tport->fcid;
+ ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ rport = fc_remote_port_add(fnic->host, 0, &ids);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (!rport) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Failed to add rport for tport: 0x%x", tport->fcid);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Added rport fcid: 0x%x", tport->fcid);
+
+ /* Mimic these assignments in queuecommand to avoid timing issues */
+ rport->maxframe_size = FNIC_FC_MAX_PAYLOAD_LEN;
+ rport->supported_classes = FC_COS_CLASS3 | FC_RPORT_ROLE_FCP_TARGET;
+ rdd_data = rport->dd_data;
+ rdd_data->tport = tport;
+ rdd_data->iport = iport;
+ tport->rport = rport;
+ tport->flags |= FNIC_FDLS_SCSI_REGISTERED;
+}
+
+void
+fnic_fdls_remove_tport(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, unsigned long flags)
+{
+ struct fnic *fnic = iport->fnic;
+ struct rport_dd_data_s *rdd_data;
+
+ struct fc_rport *rport;
+
+ if (!tport)
+ return;
+
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINE);
+ rport = tport->rport;
+
+ if (rport) {
+ /* tport resource release will be done
+ * after fnic_terminate_rport_io()
+ */
+ tport->flags |= FNIC_FDLS_TPORT_DELETED;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ /* Interface to scsi_fc_transport */
+ fc_remote_port_delete(rport);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Deregistered and freed tport fcid: 0x%x from scsi transport fc",
+ tport->fcid);
+
+ /*
+ * the dd_data is allocated by fc transport
+ * of size dd_fcrport_size
+ */
+ rdd_data = rport->dd_data;
+ rdd_data->tport = NULL;
+ rdd_data->iport = NULL;
+ list_del(&tport->links);
+ kfree(tport);
+ } else {
+ fnic_del_tport_timer_sync(fnic, tport);
+ list_del(&tport->links);
+ kfree(tport);
+ }
+}
+
+void fnic_delete_fcp_tports(struct fnic *fnic)
+{
+ struct fnic_tport_s *tport, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_for_each_entry_safe(tport, next, &fnic->iport.tport_list, links) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "removing fcp rport fcid: 0x%x", tport->fcid);
+ fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
+ fnic_del_tport_timer_sync(fnic, tport);
+ fnic_fdls_remove_tport(&fnic->iport, tport, flags);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+/**
+ * fnic_tport_event_handler() - Handler for remote port events
+ * in the tport_event_queue.
+ *
+ * @work: Handle to the remote port being dequeued
+ */
+void fnic_tport_event_handler(struct work_struct *work)
+{
+ struct fnic *fnic = container_of(work, struct fnic, tport_work);
+ struct fnic_tport_event_s *cur_evt, *next;
+ unsigned long flags;
+ struct fnic_tport_s *tport;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_for_each_entry_safe(cur_evt, next, &fnic->tport_event_list, links) {
+ tport = cur_evt->arg1;
+ switch (cur_evt->event) {
+ case TGT_EV_RPORT_ADD:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Add rport event");
+ if (tport->state == FDLS_TGT_STATE_READY) {
+ fnic_fdls_add_tport(&fnic->iport,
+ (struct fnic_tport_s *) cur_evt->arg1, flags);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Target not ready. Add rport event dropped: 0x%x",
+ tport->fcid);
+ }
+ break;
+ case TGT_EV_RPORT_DEL:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Remove rport event");
+ if (tport->state == FDLS_TGT_STATE_OFFLINING) {
+ fnic_fdls_remove_tport(&fnic->iport,
+ (struct fnic_tport_s *) cur_evt->arg1, flags);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "remove rport event dropped tport fcid: 0x%x",
+ tport->fcid);
+ }
+ break;
+ case TGT_EV_TPORT_DELETE:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Delete tport event");
+ fdls_delete_tport(tport->iport, tport);
+ break;
+ default:
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Unknown tport event");
+ break;
+ }
+ list_del(&cur_evt->links);
+ kfree(cur_evt);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+void fnic_flush_tport_event_list(struct fnic *fnic)
+{
+ struct fnic_tport_event_s *cur_evt, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_for_each_entry_safe(cur_evt, next, &fnic->tport_event_list, links) {
+ list_del(&cur_evt->links);
+ kfree(cur_evt);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+void fnic_reset_work_handler(struct work_struct *work)
+{
+ struct fnic *cur_fnic, *next_fnic;
+ unsigned long reset_fnic_list_lock_flags;
+ int host_reset_ret_code;
+
+ /*
+ * This is a single thread. It is per fnic module, not per fnic
+ * All the fnics that need to be reset
+ * have been serialized via the reset fnic list.
+ */
+ spin_lock_irqsave(&reset_fnic_list_lock, reset_fnic_list_lock_flags);
+ list_for_each_entry_safe(cur_fnic, next_fnic, &reset_fnic_list, links) {
+ list_del(&cur_fnic->links);
+ spin_unlock_irqrestore(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+
+ dev_err(&cur_fnic->pdev->dev, "fnic: <%d>: issuing a host reset\n",
+ cur_fnic->fnic_num);
+ host_reset_ret_code = fnic_host_reset(cur_fnic->host);
+ dev_err(&cur_fnic->pdev->dev,
+ "fnic: <%d>: returned from host reset with status: %d\n",
+ cur_fnic->fnic_num, host_reset_ret_code);
+
+ spin_lock_irqsave(&cur_fnic->fnic_lock, cur_fnic->lock_flags);
+ cur_fnic->pc_rscn_handling_status =
+ PC_RSCN_HANDLING_NOT_IN_PROGRESS;
+ spin_unlock_irqrestore(&cur_fnic->fnic_lock, cur_fnic->lock_flags);
+
+ spin_lock_irqsave(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+ }
+ spin_unlock_irqrestore(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+}
diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
new file mode 100644
index 000000000000..531d0b37e450
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_fdls.h
@@ -0,0 +1,435 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ */
+
+#ifndef _FNIC_FDLS_H_
+#define _FNIC_FDLS_H_
+
+#include "fnic_stats.h"
+#include "fdls_fc.h"
+
+/* FDLS - Fabric discovery and login services
+ * -> VLAN discovery
+ * -> retry every retry delay seconds until it succeeds.
+ * <- List of VLANs
+ *
+ * -> Solicitation
+ * <- Solicitation response (Advertisement)
+ *
+ * -> FCF selection & FLOGI ( FLOGI timeout - 2 * E_D_TOV)
+ * <- FLOGI response
+ *
+ * -> FCF keep alive
+ * <- FCF keep alive
+ *
+ * -> PLOGI to FFFFFC (DNS) (PLOGI timeout - 2 * R_A_TOV)
+ * -> ABTS if timeout (ABTS tomeout - 2 * R_A_TOV)
+ * <- PLOGI response
+ * -> Retry PLOGI to FFFFFC (DNS) - Number of retries from vnic.cfg
+ *
+ * -> SCR to FFFFFC (DNS) (SCR timeout - 2 * R_A_TOV)
+ * -> ABTS if timeout (ABTS tomeout - 2 * R_A_TOV)
+ * <- SCR response
+ * -> Retry SCR - Number of retries 2
+ *
+ * -> GPN_FT to FFFFFC (GPN_FT timeout - 2 * R_A_TOV)a
+ * -> Retry on BUSY until it succeeds
+ * -> Retry on BUSY until it succeeds
+ * -> 2 retries on timeout
+ *
+ * -> RFT_ID to FFFFFC (DNS) (RFT_ID timeout - 3 * R_A_TOV)
+ * -> ABTS if timeout (ABTS tomeout - 2 * R_A_TOV)
+ * -> Retry RFT_ID to FFFFFC (DNS) (Number of retries 2 )
+ * -> Ignore if both retires fail.
+ *
+ * Session establishment with targets
+ * For each PWWN
+ * -> PLOGI to FCID of that PWWN (PLOGI timeout 2 * R_A_TOV)
+ * -> ABTS if timeout (ABTS tomeout - 2 * R_A_TOV)
+ * <- PLOGI response
+ * -> Retry PLOGI. Num retries using vnic.cfg
+ *
+ * -> PRLI to FCID of that PWWN (PRLI timeout 2 * R_A_TOV)
+ * -> ABTS if timeout (ABTS tomeout - 2 * R_A_TOV)
+ * <- PRLI response
+ * -> Retry PRLI. Num retries using vnic.cfg
+ *
+ */
+
+#define FDLS_RETRY_COUNT 2
+
+/*
+ * OXID encoding:
+ * bits 0-8: oxid idx - allocated from poool
+ * bits 9-13: oxid frame code from fnic_oxid_frame_type_e
+ * bits 14-15: all zeros
+ */
+#define FNIC_OXID_POOL_SZ (512) /* always power of 2 */
+#define FNIC_OXID_ENCODE(idx, frame_type) (frame_type | idx)
+#define FNIC_FRAME_MASK 0xFE00
+#define FNIC_FRAME_TYPE(oxid) (oxid & FNIC_FRAME_MASK)
+#define FNIC_OXID_IDX(oxid) ((oxid) & (FNIC_OXID_POOL_SZ - 1))
+
+#define OXID_RECLAIM_TOV(iport) (2 * iport->r_a_tov) /* in milliseconds */
+
+#define FNIC_FDLS_FABRIC_ABORT_ISSUED 0x1
+#define FNIC_FDLS_FPMA_LEARNT 0x2
+
+/* tport flags */
+#define FNIC_FDLS_TPORT_IN_GPN_FT_LIST 0x1
+#define FNIC_FDLS_TGT_ABORT_ISSUED 0x2
+#define FNIC_FDLS_TPORT_SEND_ADISC 0x4
+#define FNIC_FDLS_RETRY_FRAME 0x8
+#define FNIC_FDLS_TPORT_BUSY 0x10
+#define FNIC_FDLS_TPORT_TERMINATING 0x20
+#define FNIC_FDLS_TPORT_DELETED 0x40
+#define FNIC_FDLS_SCSI_REGISTERED 0x200
+
+/* Retry supported by rport(returned by prli service parameters) */
+#define FDLS_FC_RP_FLAGS_RETRY 0x1
+
+#define fdls_set_state(_fdls_fabric, _state) ((_fdls_fabric)->state = _state)
+#define fdls_get_state(_fdls_fabric) ((_fdls_fabric)->state)
+
+#define FNIC_FDMI_ACTIVE 0x8
+#define FNIC_FIRST_LINK_UP 0x2
+
+#define fdls_set_tport_state(_tport, _state) (_tport->state = _state)
+#define fdls_get_tport_state(_tport) (_tport->state)
+
+#define FNIC_PORTSPEED_10GBIT 1
+#define FNIC_FRAME_HT_ROOM (2148)
+#define FNIC_FCOE_FRAME_MAXSZ (2112)
+
+
+#define FNIC_FRAME_TYPE_FABRIC_FLOGI 0x1000
+#define FNIC_FRAME_TYPE_FABRIC_PLOGI 0x1200
+#define FNIC_FRAME_TYPE_FABRIC_RPN 0x1400
+#define FNIC_FRAME_TYPE_FABRIC_RFT 0x1600
+#define FNIC_FRAME_TYPE_FABRIC_RFF 0x1800
+#define FNIC_FRAME_TYPE_FABRIC_SCR 0x1A00
+#define FNIC_FRAME_TYPE_FABRIC_GPN_FT 0x1C00
+#define FNIC_FRAME_TYPE_FABRIC_LOGO 0x1E00
+#define FNIC_FRAME_TYPE_FDMI_PLOGI 0x2000
+#define FNIC_FRAME_TYPE_FDMI_RHBA 0x2200
+#define FNIC_FRAME_TYPE_FDMI_RPA 0x2400
+#define FNIC_FRAME_TYPE_TGT_PLOGI 0x2600
+#define FNIC_FRAME_TYPE_TGT_PRLI 0x2800
+#define FNIC_FRAME_TYPE_TGT_ADISC 0x2A00
+#define FNIC_FRAME_TYPE_TGT_LOGO 0x2C00
+
+struct fnic_fip_fcf_s {
+ uint16_t vlan_id;
+ uint8_t fcf_mac[6];
+ uint8_t fcf_priority;
+ uint32_t fka_adv_period;
+ uint8_t ka_disabled;
+};
+
+enum fnic_fdls_state_e {
+ FDLS_STATE_INIT = 0,
+ FDLS_STATE_LINKDOWN,
+ FDLS_STATE_FABRIC_LOGO,
+ FDLS_STATE_FLOGO_DONE,
+ FDLS_STATE_FABRIC_FLOGI,
+ FDLS_STATE_FABRIC_PLOGI,
+ FDLS_STATE_RPN_ID,
+ FDLS_STATE_REGISTER_FC4_TYPES,
+ FDLS_STATE_REGISTER_FC4_FEATURES,
+ FDLS_STATE_SCR,
+ FDLS_STATE_GPN_FT,
+ FDLS_STATE_TGT_DISCOVERY,
+ FDLS_STATE_RSCN_GPN_FT,
+ FDLS_STATE_SEND_GPNFT
+};
+
+struct fnic_fdls_fabric_s {
+ enum fnic_fdls_state_e state;
+ uint32_t flags;
+ struct list_head tport_list; /* List of discovered tports */
+ struct timer_list retry_timer;
+ int del_timer_inprogress;
+ int del_fdmi_timer_inprogress;
+ int retry_counter;
+ int timer_pending;
+ int fdmi_retry;
+ struct timer_list fdmi_timer;
+ int fdmi_pending;
+};
+
+struct fnic_fdls_fip_s {
+ uint32_t state;
+ uint32_t flogi_retry;
+};
+
+/* Message to tport_event_handler */
+enum fnic_tgt_msg_id {
+ TGT_EV_NONE = 0,
+ TGT_EV_RPORT_ADD,
+ TGT_EV_RPORT_DEL,
+ TGT_EV_TPORT_DELETE,
+ TGT_EV_REMOVE
+};
+
+struct fnic_tport_event_s {
+ struct list_head links;
+ enum fnic_tgt_msg_id event;
+ void *arg1;
+};
+
+enum fdls_tgt_state_e {
+ FDLS_TGT_STATE_INIT = 0,
+ FDLS_TGT_STATE_PLOGI,
+ FDLS_TGT_STATE_PRLI,
+ FDLS_TGT_STATE_READY,
+ FDLS_TGT_STATE_LOGO_RECEIVED,
+ FDLS_TGT_STATE_ADISC,
+ FDL_TGT_STATE_PLOGO,
+ FDLS_TGT_STATE_OFFLINING,
+ FDLS_TGT_STATE_OFFLINE
+};
+
+struct fnic_tport_s {
+ struct list_head links; /* To link the tports */
+ enum fdls_tgt_state_e state;
+ uint32_t flags;
+ uint32_t fcid;
+ uint64_t wwpn;
+ uint64_t wwnn;
+ uint16_t active_oxid;
+ uint16_t tgt_flags;
+ atomic_t in_flight; /* io counter */
+ uint16_t max_payload_size;
+ uint16_t r_a_tov;
+ uint16_t e_d_tov;
+ uint16_t lun0_delay;
+ int max_concur_seqs;
+ uint32_t fcp_csp;
+ struct timer_list retry_timer;
+ int del_timer_inprogress;
+ int retry_counter;
+ int timer_pending;
+ unsigned int num_pending_cmds;
+ int nexus_restart_count;
+ int exch_reset_in_progress;
+ void *iport;
+ struct work_struct tport_del_work;
+ struct completion *tport_del_done;
+ struct fc_rport *rport;
+ char str_wwpn[20];
+ char str_wwnn[20];
+};
+
+/* OXID pool related structures */
+struct reclaim_entry_s {
+ struct list_head links;
+ /* oxid that needs to be freed after 2*r_a_tov */
+ uint16_t oxid_idx;
+ /* in jiffies. Use this to waiting time */
+ unsigned long expires;
+ unsigned long *bitmap;
+};
+
+/* used for allocating oxids for fabric and fdmi requests */
+struct fnic_oxid_pool_s {
+ DECLARE_BITMAP(bitmap, FNIC_OXID_POOL_SZ);
+ int sz; /* size of the pool or block */
+ int next_idx; /* used for cycling through the oxid pool */
+
+ /* retry schedule free */
+ DECLARE_BITMAP(pending_schedule_free, FNIC_OXID_POOL_SZ);
+ struct delayed_work schedule_oxid_free_retry;
+
+ /* List of oxids that need to be freed and reclaimed.
+ * This list is shared by all the oxid pools
+ */
+ struct list_head oxid_reclaim_list;
+ /* Work associated with reclaim list */
+ struct delayed_work oxid_reclaim_work;
+};
+
+/* iport */
+enum fnic_iport_state_e {
+ FNIC_IPORT_STATE_INIT = 0,
+ FNIC_IPORT_STATE_LINK_WAIT,
+ FNIC_IPORT_STATE_FIP,
+ FNIC_IPORT_STATE_FABRIC_DISC,
+ FNIC_IPORT_STATE_READY
+};
+
+struct fnic_iport_s {
+ enum fnic_iport_state_e state;
+ struct fnic *fnic;
+ uint64_t boot_time;
+ uint32_t flags;
+ int usefip;
+ uint8_t hwmac[6]; /* HW MAC Addr */
+ uint8_t fpma[6]; /* Fabric Provided MA */
+ uint8_t fcfmac[6]; /* MAC addr of Fabric */
+ uint16_t vlan_id;
+ uint32_t fcid;
+
+ /* oxid pool */
+ struct fnic_oxid_pool_s oxid_pool;
+
+ /*
+ * fabric reqs are serialized and only one req at a time.
+ * Tracking the oxid for sending abort
+ */
+ uint16_t active_oxid_fabric_req;
+ /* fdmi only */
+ uint16_t active_oxid_fdmi_plogi;
+ uint16_t active_oxid_fdmi_rhba;
+ uint16_t active_oxid_fdmi_rpa;
+
+ struct fnic_fip_fcf_s selected_fcf;
+ struct fnic_fdls_fip_s fip;
+ struct fnic_fdls_fabric_s fabric;
+ struct list_head tport_list;
+ struct list_head tport_list_pending_del;
+ /* list of tports for which we are yet to send PLOGO */
+ struct list_head inprocess_tport_list;
+ struct list_head deleted_tport_list;
+ struct work_struct tport_event_work;
+ uint32_t e_d_tov; /* msec */
+ uint32_t r_a_tov; /* msec */
+ uint32_t link_supported_speeds;
+ uint32_t max_flogi_retries;
+ uint32_t max_plogi_retries;
+ uint32_t plogi_timeout;
+ uint32_t service_params;
+ uint64_t wwpn;
+ uint64_t wwnn;
+ uint16_t max_payload_size;
+ spinlock_t deleted_tport_lst_lock;
+ struct completion *flogi_reg_done;
+ struct fnic_iport_stats iport_stats;
+ char str_wwpn[20];
+ char str_wwnn[20];
+};
+
+struct rport_dd_data_s {
+ struct fnic_tport_s *tport;
+ struct fnic_iport_s *iport;
+};
+
+enum fnic_recv_frame_type_e {
+ FNIC_FABRIC_FLOGI_RSP = 1,
+ FNIC_FABRIC_PLOGI_RSP,
+ FNIC_FABRIC_RPN_RSP,
+ FNIC_FABRIC_RFT_RSP,
+ FNIC_FABRIC_RFF_RSP,
+ FNIC_FABRIC_SCR_RSP,
+ FNIC_FABRIC_GPN_FT_RSP,
+ FNIC_FABRIC_BLS_ABTS_RSP,
+ FNIC_FDMI_PLOGI_RSP,
+ FNIC_FDMI_REG_HBA_RSP,
+ FNIC_FDMI_RPA_RSP,
+ FNIC_FDMI_BLS_ABTS_RSP,
+ FNIC_FABRIC_LOGO_RSP,
+
+ /* responses to target requests */
+ FNIC_TPORT_PLOGI_RSP,
+ FNIC_TPORT_PRLI_RSP,
+ FNIC_TPORT_ADISC_RSP,
+ FNIC_TPORT_BLS_ABTS_RSP,
+ FNIC_TPORT_LOGO_RSP,
+
+ /* unsolicited requests */
+ FNIC_BLS_ABTS_REQ,
+ FNIC_ELS_PLOGI_REQ,
+ FNIC_ELS_RSCN_REQ,
+ FNIC_ELS_LOGO_REQ,
+ FNIC_ELS_ECHO_REQ,
+ FNIC_ELS_ADISC,
+ FNIC_ELS_RLS,
+ FNIC_ELS_RRQ,
+ FNIC_ELS_UNSUPPORTED_REQ,
+};
+
+enum fnic_port_speeds {
+ DCEM_PORTSPEED_NONE = 0,
+ DCEM_PORTSPEED_1G = 1000,
+ DCEM_PORTSPEED_2G = 2000,
+ DCEM_PORTSPEED_4G = 4000,
+ DCEM_PORTSPEED_8G = 8000,
+ DCEM_PORTSPEED_10G = 10000,
+ DCEM_PORTSPEED_16G = 16000,
+ DCEM_PORTSPEED_20G = 20000,
+ DCEM_PORTSPEED_25G = 25000,
+ DCEM_PORTSPEED_32G = 32000,
+ DCEM_PORTSPEED_40G = 40000,
+ DCEM_PORTSPEED_4x10G = 41000,
+ DCEM_PORTSPEED_50G = 50000,
+ DCEM_PORTSPEED_64G = 64000,
+ DCEM_PORTSPEED_100G = 100000,
+ DCEM_PORTSPEED_128G = 128000,
+};
+
+/* Function Declarations */
+/* fdls_disc.c */
+void fnic_fdls_disc_init(struct fnic_iport_s *iport);
+void fnic_fdls_disc_start(struct fnic_iport_s *iport);
+void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
+ int len, int fchdr_offset);
+void fnic_fdls_link_down(struct fnic_iport_s *iport);
+int fdls_init_frame_pool(struct fnic_iport_s *iport);
+uint8_t *fdls_alloc_frame(struct fnic_iport_s *iport);
+uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type,
+ uint16_t *active_oxid);
+void fdls_free_oxid(struct fnic_iport_s *iport,
+ uint16_t oxid, uint16_t *active_oxid);
+void fdls_tgt_logout(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport);
+void fnic_del_fabric_timer_sync(struct fnic *fnic);
+void fnic_del_tport_timer_sync(struct fnic *fnic,
+ struct fnic_tport_s *tport);
+void fdls_send_fabric_logo(struct fnic_iport_s *iport);
+int fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
+ struct fc_frame_header *fchdr);
+void fdls_send_tport_abts(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport);
+bool fdls_delete_tport(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport);
+void fdls_fdmi_timer_callback(struct timer_list *t);
+void fdls_fdmi_retry_plogi(struct fnic_iport_s *iport);
+
+/* fnic_fcs.c */
+void fnic_fdls_init(struct fnic *fnic, int usefip);
+void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
+ int frame_size);
+void fnic_fcoe_send_vlan_req(struct fnic *fnic);
+int fnic_send_fip_frame(struct fnic_iport_s *iport,
+ void *frame, int frame_size);
+void fnic_fdls_learn_fcoe_macs(struct fnic_iport_s *iport, void *rx_frame,
+ uint8_t *fcid);
+void fnic_fdls_add_tport(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport, unsigned long flags);
+void fnic_fdls_remove_tport(struct fnic_iport_s *iport,
+ struct fnic_tport_s *tport,
+ unsigned long flags);
+
+/* fip.c */
+void fnic_fcoe_send_vlan_req(struct fnic *fnic);
+void fnic_common_fip_cleanup(struct fnic *fnic);
+int fdls_fip_recv_frame(struct fnic *fnic, void *frame);
+void fnic_handle_fcs_ka_timer(struct timer_list *t);
+void fnic_handle_enode_ka_timer(struct timer_list *t);
+void fnic_handle_vn_ka_timer(struct timer_list *t);
+void fnic_handle_fip_timer(struct timer_list *t);
+extern void fdls_fabric_timer_callback(struct timer_list *t);
+
+/* fnic_scsi.c */
+void fnic_scsi_fcpio_reset(struct fnic *fnic);
+extern void fdls_fabric_timer_callback(struct timer_list *t);
+void fnic_rport_exch_reset(struct fnic *fnic, u32 fcid);
+int fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id,
+ void *fp);
+struct fnic_tport_s *fnic_find_tport_by_fcid(struct fnic_iport_s *iport,
+ uint32_t fcid);
+struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
+ uint64_t wwpn);
+
+#endif /* _FNIC_FDLS_H_ */
diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h
index f0b896988cd5..0d974e040ab7 100644
--- a/drivers/scsi/fnic/fnic_io.h
+++ b/drivers/scsi/fnic/fnic_io.h
@@ -1,27 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _FNIC_IO_H_
#define _FNIC_IO_H_
#include <scsi/fc/fc_fcp.h>
+#include "fnic_fdls.h"
#define FNIC_DFLT_SG_DESC_CNT 32
-#define FNIC_MAX_SG_DESC_CNT 1024 /* Maximum descriptors per sgl */
+#define FNIC_MAX_SG_DESC_CNT 256 /* Maximum descriptors per sgl */
#define FNIC_SG_DESC_ALIGN 16 /* Descriptor address alignment */
struct host_sg_desc {
@@ -45,13 +34,16 @@ enum fnic_sgl_list_type {
};
enum fnic_ioreq_state {
- FNIC_IOREQ_CMD_PENDING = 0,
+ FNIC_IOREQ_NOT_INITED = 0,
+ FNIC_IOREQ_CMD_PENDING,
FNIC_IOREQ_ABTS_PENDING,
FNIC_IOREQ_ABTS_COMPLETE,
FNIC_IOREQ_CMD_COMPLETE,
};
struct fnic_io_req {
+ struct fnic_iport_s *iport;
+ struct fnic_tport_s *tport;
struct host_sg_desc *sgl_list; /* sgl list */
void *sgl_list_alloc; /* sgl list address used for free */
dma_addr_t sense_buf_pa; /* dma address for sense buffer*/
@@ -60,8 +52,10 @@ struct fnic_io_req {
u8 sgl_type; /* device DMA descriptor list type */
u8 io_completed:1; /* set to 1 when fw completes IO */
u32 port_id; /* remote port DID */
+ unsigned long start_time; /* in jiffies */
struct completion *abts_done; /* completion for abts */
struct completion *dr_done; /* completion for device reset */
+ unsigned int tag;
+ struct scsi_cmnd *sc; /* midlayer's cmd pointer */
};
-
#endif /* _FNIC_IO_H_ */
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index 5c1f223cabce..7ed50b11afa6 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -1,25 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
-#include <scsi/libfc.h>
+#include <scsi/scsi_transport_fc.h>
#include <scsi/fc_frame.h>
#include "vnic_dev.h"
#include "vnic_intr.h"
@@ -37,6 +25,9 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data)
if (!pba)
return IRQ_NONE;
+ fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
+ atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
+
if (pba & (1 << FNIC_INTX_NOTIFY)) {
vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]);
fnic_handle_link_event(fnic);
@@ -47,8 +38,13 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data)
fnic_log_q_error(fnic);
}
+ if (pba & (1 << FNIC_INTX_DUMMY)) {
+ atomic64_inc(&fnic->fnic_stats.misc_stats.intx_dummy);
+ vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_DUMMY]);
+ }
+
if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
- work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ work_done += fnic_wq_copy_cmpl_handler(fnic, io_completions, FNIC_MQ_CQ_INDEX);
work_done += fnic_wq_cmpl_handler(fnic, -1);
work_done += fnic_rq_cmpl_handler(fnic, -1);
@@ -66,7 +62,10 @@ static irqreturn_t fnic_isr_msi(int irq, void *data)
struct fnic *fnic = data;
unsigned long work_done = 0;
- work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
+ atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
+
+ work_done += fnic_wq_copy_cmpl_handler(fnic, io_completions, FNIC_MQ_CQ_INDEX);
work_done += fnic_wq_cmpl_handler(fnic, -1);
work_done += fnic_rq_cmpl_handler(fnic, -1);
@@ -83,6 +82,9 @@ static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
struct fnic *fnic = data;
unsigned long rq_work_done = 0;
+ fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
+ atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
+
rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
rq_work_done,
@@ -97,6 +99,9 @@ static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
struct fnic *fnic = data;
unsigned long wq_work_done = 0;
+ fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
+ atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
+
wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
wq_work_done,
@@ -109,9 +114,22 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
{
struct fnic *fnic = data;
unsigned long wq_copy_work_done = 0;
+ int i;
+
+ fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
+ atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
+
+ i = irq - fnic->msix[0].irq_num;
+ if (i >= fnic->wq_copy_count + fnic->copy_wq_base ||
+ i < 0 || fnic->msix[i].irq_num != irq) {
+ for (i = fnic->copy_wq_base; i < fnic->wq_copy_count + fnic->copy_wq_base ; i++) {
+ if (fnic->msix[i].irq_num == irq)
+ break;
+ }
+ }
- wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
- vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
+ wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, io_completions, i);
+ vnic_intr_return_credits(&fnic->intr[i],
wq_copy_work_done,
1 /* unmask intr */,
1 /* reset intr timer */);
@@ -122,7 +140,10 @@ static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data)
{
struct fnic *fnic = data;
- vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]);
+ fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
+ atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
+
+ vnic_intr_return_all_credits(&fnic->intr[fnic->err_intr_offset]);
fnic_log_q_error(fnic);
fnic_handle_link_event(fnic);
@@ -136,13 +157,13 @@ void fnic_free_intr(struct fnic *fnic)
switch (vnic_dev_get_intr_mode(fnic->vdev)) {
case VNIC_DEV_INTR_MODE_INTX:
case VNIC_DEV_INTR_MODE_MSI:
- free_irq(fnic->pdev->irq, fnic);
+ free_irq(pci_irq_vector(fnic->pdev, 0), fnic);
break;
case VNIC_DEV_INTR_MODE_MSIX:
for (i = 0; i < ARRAY_SIZE(fnic->msix); i++)
if (fnic->msix[i].requested)
- free_irq(fnic->msix_entry[i].vector,
+ free_irq(pci_irq_vector(fnic->pdev, i),
fnic->msix[i].devid);
break;
@@ -159,12 +180,12 @@ int fnic_request_intr(struct fnic *fnic)
switch (vnic_dev_get_intr_mode(fnic->vdev)) {
case VNIC_DEV_INTR_MODE_INTX:
- err = request_irq(fnic->pdev->irq, &fnic_isr_legacy,
- IRQF_SHARED, DRV_NAME, fnic);
+ err = request_irq(pci_irq_vector(fnic->pdev, 0),
+ &fnic_isr_legacy, IRQF_SHARED, DRV_NAME, fnic);
break;
case VNIC_DEV_INTR_MODE_MSI:
- err = request_irq(fnic->pdev->irq, &fnic_isr_msi,
+ err = request_irq(pci_irq_vector(fnic->pdev, 0), &fnic_isr_msi,
0, fnic->name, fnic);
break;
@@ -180,26 +201,30 @@ int fnic_request_intr(struct fnic *fnic)
fnic->msix[FNIC_MSIX_WQ].isr = fnic_isr_msix_wq;
fnic->msix[FNIC_MSIX_WQ].devid = fnic;
- sprintf(fnic->msix[FNIC_MSIX_WQ_COPY].devname,
- "%.11s-scsi-wq", fnic->name);
- fnic->msix[FNIC_MSIX_WQ_COPY].isr = fnic_isr_msix_wq_copy;
- fnic->msix[FNIC_MSIX_WQ_COPY].devid = fnic;
+ for (i = fnic->copy_wq_base; i < fnic->wq_copy_count + fnic->copy_wq_base; i++) {
+ sprintf(fnic->msix[i].devname,
+ "%.11s-scsi-wq-%d", fnic->name, i-FNIC_MSIX_WQ_COPY);
+ fnic->msix[i].isr = fnic_isr_msix_wq_copy;
+ fnic->msix[i].devid = fnic;
+ }
- sprintf(fnic->msix[FNIC_MSIX_ERR_NOTIFY].devname,
+ sprintf(fnic->msix[fnic->err_intr_offset].devname,
"%.11s-err-notify", fnic->name);
- fnic->msix[FNIC_MSIX_ERR_NOTIFY].isr =
+ fnic->msix[fnic->err_intr_offset].isr =
fnic_isr_msix_err_notify;
- fnic->msix[FNIC_MSIX_ERR_NOTIFY].devid = fnic;
+ fnic->msix[fnic->err_intr_offset].devid = fnic;
+
+ for (i = 0; i < fnic->intr_count; i++) {
+ fnic->msix[i].irq_num = pci_irq_vector(fnic->pdev, i);
- for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) {
- err = request_irq(fnic->msix_entry[i].vector,
- fnic->msix[i].isr, 0,
- fnic->msix[i].devname,
- fnic->msix[i].devid);
+ err = request_irq(fnic->msix[i].irq_num,
+ fnic->msix[i].isr, 0,
+ fnic->msix[i].devname,
+ fnic->msix[i].devid);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "MSIX: request_irq"
- " failed %d\n", err);
+ FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "request_irq failed with error: %d\n",
+ err);
fnic_free_intr(fnic);
break;
}
@@ -214,48 +239,99 @@ int fnic_request_intr(struct fnic *fnic)
return err;
}
-int fnic_set_intr_mode(struct fnic *fnic)
+int fnic_set_intr_mode_msix(struct fnic *fnic)
{
unsigned int n = ARRAY_SIZE(fnic->rq);
unsigned int m = ARRAY_SIZE(fnic->wq);
- unsigned int o = ARRAY_SIZE(fnic->wq_copy);
- unsigned int i;
+ unsigned int o = ARRAY_SIZE(fnic->hw_copy_wq);
+ unsigned int min_irqs = n + m + 1 + 1; /*rq, raw wq, wq, err*/
/*
- * Set interrupt mode (INTx, MSI, MSI-X) depending
- * system capabilities.
- *
- * Try MSI-X first
- *
* We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
* (last INTR is used for WQ/RQ errors and notification area)
*/
+ FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "rq-array size: %d wq-array size: %d copy-wq array size: %d\n",
+ n, m, o);
+ FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "rq_count: %d raw_wq_count: %d wq_copy_count: %d cq_count: %d\n",
+ fnic->rq_count, fnic->raw_wq_count,
+ fnic->wq_copy_count, fnic->cq_count);
+
+ if (fnic->rq_count <= n && fnic->raw_wq_count <= m &&
+ fnic->wq_copy_count <= o) {
+ int vec_count = 0;
+ int vecs = fnic->rq_count + fnic->raw_wq_count + fnic->wq_copy_count + 1;
+
+ vec_count = pci_alloc_irq_vectors(fnic->pdev, min_irqs, vecs,
+ PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
+ FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "allocated %d MSI-X vectors\n",
+ vec_count);
+
+ if (vec_count > 0) {
+ if (vec_count < vecs) {
+ FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "interrupts number mismatch: vec_count: %d vecs: %d\n",
+ vec_count, vecs);
+ if (vec_count < min_irqs) {
+ FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "no interrupts for copy wq\n");
+ return 1;
+ }
+ }
- BUG_ON(ARRAY_SIZE(fnic->msix_entry) < n + m + o + 1);
- for (i = 0; i < n + m + o + 1; i++)
- fnic->msix_entry[i].entry = i;
-
- if (fnic->rq_count >= n &&
- fnic->raw_wq_count >= m &&
- fnic->wq_copy_count >= o &&
- fnic->cq_count >= n + m + o) {
- if (!pci_enable_msix(fnic->pdev, fnic->msix_entry,
- n + m + o + 1)) {
fnic->rq_count = n;
fnic->raw_wq_count = m;
- fnic->wq_copy_count = o;
- fnic->wq_count = m + o;
- fnic->cq_count = n + m + o;
- fnic->intr_count = n + m + o + 1;
- fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY;
-
- FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
- "Using MSI-X Interrupts\n");
- vnic_dev_set_intr_mode(fnic->vdev,
- VNIC_DEV_INTR_MODE_MSIX);
+ fnic->copy_wq_base = fnic->rq_count + fnic->raw_wq_count;
+ fnic->wq_copy_count = vec_count - n - m - 1;
+ fnic->wq_count = fnic->raw_wq_count + fnic->wq_copy_count;
+ if (fnic->cq_count != vec_count - 1) {
+ FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "CQ count: %d does not match MSI-X vector count: %d\n",
+ fnic->cq_count, vec_count);
+ fnic->cq_count = vec_count - 1;
+ }
+ fnic->intr_count = vec_count;
+ fnic->err_intr_offset = fnic->rq_count + fnic->wq_count;
+
+ FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "rq_count: %d raw_wq_count: %d copy_wq_base: %d\n",
+ fnic->rq_count,
+ fnic->raw_wq_count, fnic->copy_wq_base);
+
+ FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "wq_copy_count: %d wq_count: %d cq_count: %d\n",
+ fnic->wq_copy_count,
+ fnic->wq_count, fnic->cq_count);
+
+ FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "intr_count: %d err_intr_offset: %u",
+ fnic->intr_count,
+ fnic->err_intr_offset);
+
+ vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSIX);
+ FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic using MSI-X\n");
return 0;
}
}
+ return 1;
+}
+
+int fnic_set_intr_mode(struct fnic *fnic)
+{
+ int ret_status = 0;
+
+ /*
+ * Set interrupt mode (INTx, MSI, MSI-X) depending
+ * system capabilities.
+ *
+ * Try MSI-X first
+ */
+ ret_status = fnic_set_intr_mode_msix(fnic);
+ if (ret_status == 0)
+ return ret_status;
/*
* Next try MSI
@@ -266,8 +342,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
fnic->wq_copy_count >= 1 &&
fnic->cq_count >= 3 &&
fnic->intr_count >= 1 &&
- !pci_enable_msi(fnic->pdev)) {
-
+ pci_alloc_irq_vectors(fnic->pdev, 1, 1, PCI_IRQ_MSI) == 1) {
fnic->rq_count = 1;
fnic->raw_wq_count = 1;
fnic->wq_copy_count = 1;
@@ -276,7 +351,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
fnic->intr_count = 1;
fnic->err_intr_offset = 0;
- FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"Using MSI Interrupts\n");
vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI);
@@ -302,7 +377,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
fnic->cq_count = 3;
fnic->intr_count = 3;
- FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"Using Legacy Interrupts\n");
vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
@@ -316,17 +391,6 @@ int fnic_set_intr_mode(struct fnic *fnic)
void fnic_clear_intr_mode(struct fnic *fnic)
{
- switch (vnic_dev_get_intr_mode(fnic->vdev)) {
- case VNIC_DEV_INTR_MODE_MSIX:
- pci_disable_msix(fnic->pdev);
- break;
- case VNIC_DEV_INTR_MODE_MSI:
- pci_disable_msi(fnic->pdev);
- break;
- default:
- break;
- }
-
+ pci_free_irq_vectors(fnic->pdev);
vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
}
-
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index bb63f1a1f808..4cc4077ea53c 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/module.h>
#include <linux/mempool.h>
@@ -24,6 +12,7 @@
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/if_ether.h>
@@ -32,7 +21,6 @@
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_tcq.h>
-#include <scsi/libfc.h>
#include <scsi/fc_frame.h>
#include "vnic_dev.h"
@@ -40,6 +28,8 @@
#include "vnic_stats.h"
#include "fnic_io.h"
#include "fnic.h"
+#include "fnic_fdls.h"
+#include "fdls_fc.h"
#define PCI_DEVICE_ID_CISCO_FNIC 0x0045
@@ -48,11 +38,18 @@
static struct kmem_cache *fnic_sgl_cache[FNIC_SGL_NUM_CACHES];
static struct kmem_cache *fnic_io_req_cache;
-LIST_HEAD(fnic_list);
-DEFINE_SPINLOCK(fnic_list_lock);
+static struct kmem_cache *fdls_frame_cache;
+static struct kmem_cache *fdls_frame_elem_cache;
+static LIST_HEAD(fnic_list);
+static DEFINE_SPINLOCK(fnic_list_lock);
+static DEFINE_IDA(fnic_ida);
+
+struct work_struct reset_fnic_work;
+LIST_HEAD(reset_fnic_list);
+DEFINE_SPINLOCK(reset_fnic_list_lock);
/* Supported devices by fnic module */
-static struct pci_device_id fnic_id_table[] = {
+static const struct pci_device_id fnic_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_CISCO_FNIC) },
{ 0, }
};
@@ -68,45 +65,71 @@ unsigned int fnic_log_level;
module_param(fnic_log_level, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
+unsigned int fnic_fdmi_support = 1;
+module_param(fnic_fdmi_support, int, 0644);
+MODULE_PARM_DESC(fnic_fdmi_support, "FDMI support");
-static struct libfc_function_template fnic_transport_template = {
- .frame_send = fnic_send,
- .lport_set_port_id = fnic_set_port_id,
- .fcp_abort_io = fnic_empty_scsi_cleanup,
- .fcp_cleanup = fnic_empty_scsi_cleanup,
- .exch_mgr_reset = fnic_exch_mgr_reset
-};
+static unsigned int fnic_tgt_id_binding = 1;
+module_param(fnic_tgt_id_binding, uint, 0644);
+MODULE_PARM_DESC(fnic_tgt_id_binding,
+ "Target ID binding (0 for none. 1 for binding by WWPN (default))");
+
+unsigned int io_completions = FNIC_DFLT_IO_COMPLETIONS;
+module_param(io_completions, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(io_completions, "Max CQ entries to process at a time");
+
+unsigned int fnic_trace_max_pages = 16;
+module_param(fnic_trace_max_pages, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fnic_trace_max_pages, "Total allocated memory pages "
+ "for fnic trace buffer");
+
+unsigned int fnic_fc_trace_max_pages = 64;
+module_param(fnic_fc_trace_max_pages, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fnic_fc_trace_max_pages,
+ "Total allocated memory pages for fc trace buffer");
+
+static unsigned int fnic_max_qdepth = FNIC_DFLT_QUEUE_DEPTH;
+module_param(fnic_max_qdepth, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fnic_max_qdepth, "Queue depth to report for each LUN");
+
+unsigned int pc_rscn_handling_feature_flag = PC_RSCN_HANDLING_FEATURE_ON;
+module_param(pc_rscn_handling_feature_flag, uint, 0644);
+MODULE_PARM_DESC(pc_rscn_handling_feature_flag,
+ "PCRSCN handling (0 for none. 1 to handle PCRSCN (default))");
-static int fnic_slave_alloc(struct scsi_device *sdev)
+struct workqueue_struct *reset_fnic_work_queue;
+struct workqueue_struct *fnic_fip_queue;
+
+static int fnic_sdev_init(struct scsi_device *sdev)
{
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
- sdev->tagged_supported = 1;
-
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
- scsi_activate_tcq(sdev, FNIC_DFLT_QUEUE_DEPTH);
+ scsi_change_queue_depth(sdev, fnic_max_qdepth);
return 0;
}
-static struct scsi_host_template fnic_host_template = {
+static const struct scsi_host_template fnic_host_template = {
.module = THIS_MODULE,
.name = DRV_NAME,
.queuecommand = fnic_queuecommand,
+ .eh_timed_out = fc_eh_timed_out,
.eh_abort_handler = fnic_abort_cmd,
.eh_device_reset_handler = fnic_device_reset,
- .eh_host_reset_handler = fnic_host_reset,
- .slave_alloc = fnic_slave_alloc,
- .change_queue_depth = fc_change_queue_depth,
- .change_queue_type = fc_change_queue_type,
+ .eh_host_reset_handler = fnic_eh_host_reset_handler,
+ .sdev_init = fnic_sdev_init,
+ .change_queue_depth = scsi_change_queue_depth,
.this_id = -1,
.cmd_per_lun = 3,
- .can_queue = FNIC_MAX_IO_REQ,
- .use_clustering = ENABLE_CLUSTERING,
+ .can_queue = FNIC_DFLT_IO_REQ,
.sg_tablesize = FNIC_MAX_SG_DESC_CNT,
.max_sectors = 0xffff,
- .shost_attrs = fnic_attrs,
+ .shost_groups = fnic_host_groups,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct fnic_cmd_priv),
+ .map_queues = fnic_mq_map_queues_cpus,
};
static void
@@ -121,6 +144,7 @@ fnic_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
static void fnic_get_host_speed(struct Scsi_Host *shost);
static struct scsi_transport_template *fnic_fc_transport;
static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *);
+static void fnic_reset_host_stats(struct Scsi_Host *);
static struct fc_function_template fnic_fc_functions = {
@@ -135,7 +159,7 @@ static struct fc_function_template fnic_fc_functions = {
.get_host_speed = fnic_get_host_speed,
.show_host_speed = 1,
.show_host_port_type = 1,
- .get_host_port_state = fc_get_host_port_state,
+ .get_host_port_state = fnic_get_host_port_state,
.show_host_port_state = 1,
.show_host_symbolic_name = 1,
.show_rport_maxframe_size = 1,
@@ -146,40 +170,88 @@ static struct fc_function_template fnic_fc_functions = {
.show_starget_port_id = 1,
.show_rport_dev_loss_tmo = 1,
.set_rport_dev_loss_tmo = fnic_set_rport_dev_loss_tmo,
- .issue_fc_host_lip = fnic_reset,
+ .issue_fc_host_lip = fnic_issue_fc_host_lip,
.get_fc_host_stats = fnic_get_stats,
- .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .reset_fc_host_stats = fnic_reset_host_stats,
+ .dd_fcrport_size = sizeof(struct rport_dd_data_s),
.terminate_rport_io = fnic_terminate_rport_io,
- .bsg_request = fc_lport_bsg_request,
+ .bsg_request = NULL,
};
static void fnic_get_host_speed(struct Scsi_Host *shost)
{
- struct fc_lport *lp = shost_priv(shost);
- struct fnic *fnic = lport_priv(lp);
+ struct fnic *fnic = *((struct fnic **) shost_priv(shost));
u32 port_speed = vnic_dev_port_speed(fnic->vdev);
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+
+ FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "port_speed: %d Mbps", port_speed);
+ atomic64_set(&fnic_stats->misc_stats.port_speed_in_mbps, port_speed);
/* Add in other values as they get defined in fw */
switch (port_speed) {
- case 10000:
+ case DCEM_PORTSPEED_1G:
+ fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+ break;
+ case DCEM_PORTSPEED_2G:
+ fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
+ break;
+ case DCEM_PORTSPEED_4G:
+ fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
+ break;
+ case DCEM_PORTSPEED_8G:
+ fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
+ break;
+ case DCEM_PORTSPEED_10G:
fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
break;
+ case DCEM_PORTSPEED_16G:
+ fc_host_speed(shost) = FC_PORTSPEED_16GBIT;
+ break;
+ case DCEM_PORTSPEED_20G:
+ fc_host_speed(shost) = FC_PORTSPEED_20GBIT;
+ break;
+ case DCEM_PORTSPEED_25G:
+ fc_host_speed(shost) = FC_PORTSPEED_25GBIT;
+ break;
+ case DCEM_PORTSPEED_32G:
+ fc_host_speed(shost) = FC_PORTSPEED_32GBIT;
+ break;
+ case DCEM_PORTSPEED_40G:
+ case DCEM_PORTSPEED_4x10G:
+ fc_host_speed(shost) = FC_PORTSPEED_40GBIT;
+ break;
+ case DCEM_PORTSPEED_50G:
+ fc_host_speed(shost) = FC_PORTSPEED_50GBIT;
+ break;
+ case DCEM_PORTSPEED_64G:
+ fc_host_speed(shost) = FC_PORTSPEED_64GBIT;
+ break;
+ case DCEM_PORTSPEED_100G:
+ fc_host_speed(shost) = FC_PORTSPEED_100GBIT;
+ break;
+ case DCEM_PORTSPEED_128G:
+ fc_host_speed(shost) = FC_PORTSPEED_128GBIT;
+ break;
default:
- fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+ FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Unknown FC speed: %d Mbps", port_speed);
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
break;
}
}
+/* Placeholder function */
static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
{
int ret;
- struct fc_lport *lp = shost_priv(host);
- struct fnic *fnic = lport_priv(lp);
- struct fc_host_statistics *stats = &lp->host_stats;
+ struct fnic *fnic = *((struct fnic **) shost_priv(host));
+ struct fc_host_statistics *stats = &fnic->fnic_stats.host_stats;
struct vnic_stats *vs;
unsigned long flags;
- if (time_before(jiffies, fnic->stats_time + HZ / FNIC_STATS_RATE_LIMIT))
+ if (time_before
+ (jiffies, fnic->stats_time + HZ / FNIC_STATS_RATE_LIMIT))
return stats;
fnic->stats_time = jiffies;
@@ -188,26 +260,126 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
if (ret) {
- FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host,
- "fnic: Get vnic stats failed"
- " 0x%x", ret);
+ FNIC_MAIN_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "fnic: Get vnic stats failed: 0x%x", ret);
return stats;
}
vs = fnic->stats;
stats->tx_frames = vs->tx.tx_unicast_frames_ok;
- stats->tx_words = vs->tx.tx_unicast_bytes_ok / 4;
+ stats->tx_words = vs->tx.tx_unicast_bytes_ok / 4;
stats->rx_frames = vs->rx.rx_unicast_frames_ok;
- stats->rx_words = vs->rx.rx_unicast_bytes_ok / 4;
+ stats->rx_words = vs->rx.rx_unicast_bytes_ok / 4;
stats->error_frames = vs->tx.tx_errors + vs->rx.rx_errors;
stats->dumped_frames = vs->tx.tx_drops + vs->rx.rx_drop;
stats->invalid_crc_count = vs->rx.rx_crc_errors;
- stats->seconds_since_last_reset = (jiffies - lp->boot_time) / HZ;
+ stats->seconds_since_last_reset =
+ (jiffies - fnic->stats_reset_time) / HZ;
stats->fcp_input_megabytes = div_u64(fnic->fcp_input_bytes, 1000000);
stats->fcp_output_megabytes = div_u64(fnic->fcp_output_bytes, 1000000);
-
return stats;
}
+/*
+ * fnic_dump_fchost_stats
+ * note : dumps fc_statistics into system logs
+ */
+void fnic_dump_fchost_stats(struct Scsi_Host *host,
+ struct fc_host_statistics *stats)
+{
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: seconds since last reset = %llu\n",
+ stats->seconds_since_last_reset);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: tx frames = %llu\n",
+ stats->tx_frames);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: tx words = %llu\n",
+ stats->tx_words);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: rx frames = %llu\n",
+ stats->rx_frames);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: rx words = %llu\n",
+ stats->rx_words);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: lip count = %llu\n",
+ stats->lip_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: nos count = %llu\n",
+ stats->nos_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: error frames = %llu\n",
+ stats->error_frames);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: dumped frames = %llu\n",
+ stats->dumped_frames);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: link failure count = %llu\n",
+ stats->link_failure_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: loss of sync count = %llu\n",
+ stats->loss_of_sync_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: loss of signal count = %llu\n",
+ stats->loss_of_signal_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: prim seq protocol err count = %llu\n",
+ stats->prim_seq_protocol_err_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: invalid tx word count= %llu\n",
+ stats->invalid_tx_word_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: invalid crc count = %llu\n",
+ stats->invalid_crc_count);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: fcp input requests = %llu\n",
+ stats->fcp_input_requests);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: fcp output requests = %llu\n",
+ stats->fcp_output_requests);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: fcp control requests = %llu\n",
+ stats->fcp_control_requests);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: fcp input megabytes = %llu\n",
+ stats->fcp_input_megabytes);
+ FNIC_MAIN_NOTE(KERN_NOTICE, host,
+ "fnic: fcp output megabytes = %llu\n",
+ stats->fcp_output_megabytes);
+ return;
+}
+
+/*
+ * fnic_reset_host_stats : clears host stats
+ * note : called when reset_statistics set under sysfs dir
+ */
+static void fnic_reset_host_stats(struct Scsi_Host *host)
+{
+ int ret;
+ struct fnic *fnic = *((struct fnic **) shost_priv(host));
+ struct fc_host_statistics *stats;
+ unsigned long flags;
+
+ /* dump current stats, before clearing them */
+ stats = fnic_get_stats(host);
+ fnic_dump_fchost_stats(host, stats);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ ret = vnic_dev_stats_clear(fnic->vdev);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (ret) {
+ FNIC_MAIN_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "fnic: Reset vnic stats failed"
+ " 0x%x", ret);
+ return;
+ }
+ fnic->stats_reset_time = jiffies;
+ memset(stats, 0, sizeof(*stats));
+
+ return;
+}
+
void fnic_log_q_error(struct fnic *fnic)
{
unsigned int i;
@@ -216,25 +388,19 @@ void fnic_log_q_error(struct fnic *fnic)
for (i = 0; i < fnic->raw_wq_count; i++) {
error_status = ioread32(&fnic->wq[i].ctrl->error_status);
if (error_status)
- shost_printk(KERN_ERR, fnic->lport->host,
- "WQ[%d] error_status"
- " %d\n", i, error_status);
+ dev_err(&fnic->pdev->dev, "WQ[%d] error_status %d\n", i, error_status);
}
for (i = 0; i < fnic->rq_count; i++) {
error_status = ioread32(&fnic->rq[i].ctrl->error_status);
if (error_status)
- shost_printk(KERN_ERR, fnic->lport->host,
- "RQ[%d] error_status"
- " %d\n", i, error_status);
+ dev_err(&fnic->pdev->dev, "RQ[%d] error_status %d\n", i, error_status);
}
for (i = 0; i < fnic->wq_copy_count; i++) {
- error_status = ioread32(&fnic->wq_copy[i].ctrl->error_status);
+ error_status = ioread32(&fnic->hw_copy_wq[i].ctrl->error_status);
if (error_status)
- shost_printk(KERN_ERR, fnic->lport->host,
- "CWQ[%d] error_status"
- " %d\n", i, error_status);
+ dev_err(&fnic->pdev->dev, "CWQ[%d] error_status %d\n", i, error_status);
}
}
@@ -265,11 +431,10 @@ static int fnic_notify_set(struct fnic *fnic)
err = vnic_dev_notify_set(fnic->vdev, -1);
break;
case VNIC_DEV_INTR_MODE_MSIX:
- err = vnic_dev_notify_set(fnic->vdev, FNIC_MSIX_ERR_NOTIFY);
+ err = vnic_dev_notify_set(fnic->vdev, fnic->wq_copy_count + fnic->copy_wq_base);
break;
default:
- shost_printk(KERN_ERR, fnic->lport->host,
- "Interrupt mode should be set up"
+ dev_err(&fnic->pdev->dev, "Interrupt mode should be set up"
" before devcmd notify set %d\n",
vnic_dev_get_intr_mode(fnic->vdev));
err = -1;
@@ -279,9 +444,9 @@ static int fnic_notify_set(struct fnic *fnic)
return err;
}
-static void fnic_notify_timer(unsigned long data)
+static void fnic_notify_timer(struct timer_list *t)
{
- struct fnic *fnic = (struct fnic *)data;
+ struct fnic *fnic = timer_container_of(fnic, t, notify_timer);
fnic_handle_link_event(fnic);
mod_timer(&fnic->notify_timer,
@@ -301,7 +466,7 @@ static void fnic_notify_timer_start(struct fnic *fnic)
default:
/* Using intr for notification for INTx/MSI-X */
break;
- };
+ }
}
static int fnic_dev_wait(struct vnic_dev *vdev,
@@ -312,21 +477,30 @@ static int fnic_dev_wait(struct vnic_dev *vdev,
unsigned long time;
int done;
int err;
+ int count;
+
+ count = 0;
err = start(vdev, arg);
if (err)
return err;
- /* Wait for func to complete...2 seconds max */
+ /* Wait for func to complete.
+ * Sometime schedule_timeout_uninterruptible take long time
+ * to wake up so we do not retry as we are only waiting for
+ * 2 seconds in while loop. By adding count, we make sure
+ * we try atleast three times before returning -ETIMEDOUT
+ */
time = jiffies + (HZ * 2);
do {
err = finished(vdev, &done);
+ count++;
if (err)
return err;
if (done)
return 0;
schedule_timeout_uninterruptible(HZ / 10);
- } while (time_after(time, jiffies));
+ } while (time_after(time, jiffies) || (count < 3));
return -ETIMEDOUT;
}
@@ -335,6 +509,7 @@ static int fnic_cleanup(struct fnic *fnic)
{
unsigned int i;
int err;
+ int raw_wq_rq_counts;
vnic_dev_disable(fnic->vdev);
for (i = 0; i < fnic->intr_count; i++)
@@ -351,13 +526,14 @@ static int fnic_cleanup(struct fnic *fnic)
return err;
}
for (i = 0; i < fnic->wq_copy_count; i++) {
- err = vnic_wq_copy_disable(&fnic->wq_copy[i]);
+ err = vnic_wq_copy_disable(&fnic->hw_copy_wq[i]);
if (err)
return err;
+ raw_wq_rq_counts = fnic->raw_wq_count + fnic->rq_count;
+ fnic_wq_copy_cmpl_handler(fnic, -1, i + raw_wq_rq_counts);
}
/* Clean up completed IOs and FCS frames */
- fnic_wq_copy_cmpl_handler(fnic, -1);
fnic_wq_cmpl_handler(fnic, -1);
fnic_rq_cmpl_handler(fnic, -1);
@@ -367,7 +543,7 @@ static int fnic_cleanup(struct fnic *fnic)
for (i = 0; i < fnic->rq_count; i++)
vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
for (i = 0; i < fnic->wq_copy_count; i++)
- vnic_wq_copy_clean(&fnic->wq_copy[i],
+ vnic_wq_copy_clean(&fnic->hw_copy_wq[i],
fnic_wq_copy_cleanup_handler);
for (i = 0; i < fnic->cq_count; i++)
@@ -376,6 +552,8 @@ static int fnic_cleanup(struct fnic *fnic)
vnic_intr_clean(&fnic->intr[i]);
mempool_destroy(fnic->io_req_pool);
+ mempool_destroy(fnic->frame_pool);
+ mempool_destroy(fnic->frame_elem_pool);
for (i = 0; i < FNIC_SGL_NUM_CACHES; i++)
mempool_destroy(fnic->io_sgl_pool[i]);
@@ -388,123 +566,223 @@ static void fnic_iounmap(struct fnic *fnic)
iounmap(fnic->bar0.vaddr);
}
-/*
- * Allocate element for mempools requiring GFP_DMA flag.
- * Otherwise, checks in kmem_flagcheck() hit BUG_ON().
- */
-static void *fnic_alloc_slab_dma(gfp_t gfp_mask, void *pool_data)
+static void fnic_set_vlan(struct fnic *fnic, u16 vlan_id)
+{
+ vnic_dev_set_default_vlan(fnic->vdev, vlan_id);
+}
+
+static void fnic_scsi_init(struct fnic *fnic)
+{
+ struct Scsi_Host *host = fnic->host;
+
+ snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
+ host->host_no);
+
+ host->transportt = fnic_fc_transport;
+}
+
+static void fnic_free_ioreq_tables_mq(struct fnic *fnic)
{
- struct kmem_cache *mem = pool_data;
+ int hwq;
- return kmem_cache_alloc(mem, gfp_mask | GFP_ATOMIC | GFP_DMA);
+ for (hwq = 0; hwq < fnic->wq_copy_count; hwq++)
+ kfree(fnic->sw_copy_wq[hwq].io_req_table);
}
-/**
- * fnic_get_mac() - get assigned data MAC address for FIP code.
- * @lport: local port.
- */
-static u8 *fnic_get_mac(struct fc_lport *lport)
+static int fnic_scsi_drv_init(struct fnic *fnic)
{
- struct fnic *fnic = lport_priv(lport);
+ struct Scsi_Host *host = fnic->host;
+ int err;
+ struct pci_dev *pdev = fnic->pdev;
+ struct fnic_iport_s *iport = &fnic->iport;
+ int hwq;
+
+ /* Configure maximum outstanding IO reqs*/
+ if (fnic->config.io_throttle_count != FNIC_UCSM_DFLT_THROTTLE_CNT_BLD)
+ host->can_queue = min_t(u32, FNIC_MAX_IO_REQ,
+ max_t(u32, FNIC_MIN_IO_REQ,
+ fnic->config.io_throttle_count));
+
+ fnic->fnic_max_tag_id = host->can_queue;
+ host->max_lun = fnic->config.luns_per_tgt;
+ host->max_id = FNIC_MAX_FCP_TARGET;
+ host->max_cmd_len = FNIC_FCOE_MAX_CMD_LEN;
+
+ host->nr_hw_queues = fnic->wq_copy_count;
+
+ dev_info(&fnic->pdev->dev, "fnic: can_queue: %d max_lun: %llu",
+ host->can_queue, host->max_lun);
+
+ dev_info(&fnic->pdev->dev, "fnic: max_id: %d max_cmd_len: %d nr_hw_queues: %d",
+ host->max_id, host->max_cmd_len, host->nr_hw_queues);
- return fnic->data_src_addr;
+ for (hwq = 0; hwq < fnic->wq_copy_count; hwq++) {
+ fnic->sw_copy_wq[hwq].ioreq_table_size = fnic->fnic_max_tag_id;
+ fnic->sw_copy_wq[hwq].io_req_table =
+ kzalloc((fnic->sw_copy_wq[hwq].ioreq_table_size + 1) *
+ sizeof(struct fnic_io_req *), GFP_KERNEL);
+
+ if (!fnic->sw_copy_wq[hwq].io_req_table) {
+ fnic_free_ioreq_tables_mq(fnic);
+ return -ENOMEM;
+ }
+ }
+
+ dev_info(&fnic->pdev->dev, "fnic copy wqs: %d, Q0 ioreq table size: %d\n",
+ fnic->wq_copy_count, fnic->sw_copy_wq[0].ioreq_table_size);
+
+ fnic_scsi_init(fnic);
+
+ err = scsi_add_host(fnic->host, &pdev->dev);
+ if (err) {
+ dev_err(&fnic->pdev->dev, "fnic: scsi add host failed: aborting\n");
+ return err;
+ }
+ fc_host_maxframe_size(fnic->host) = iport->max_payload_size;
+ fc_host_dev_loss_tmo(fnic->host) =
+ fnic->config.port_down_timeout / 1000;
+ sprintf(fc_host_symbolic_name(fnic->host),
+ DRV_NAME " v" DRV_VERSION " over %s", fnic->name);
+ fc_host_port_type(fnic->host) = FC_PORTTYPE_NPORT;
+ fc_host_node_name(fnic->host) = iport->wwnn;
+ fc_host_port_name(fnic->host) = iport->wwpn;
+ fc_host_supported_classes(fnic->host) = FC_COS_CLASS3;
+ memset(fc_host_supported_fc4s(fnic->host), 0,
+ sizeof(fc_host_supported_fc4s(fnic->host)));
+ fc_host_supported_fc4s(fnic->host)[2] = 1;
+ fc_host_supported_fc4s(fnic->host)[7] = 1;
+ fc_host_supported_speeds(fnic->host) = 0;
+ fc_host_supported_speeds(fnic->host) |= FC_PORTSPEED_8GBIT;
+
+ dev_info(&fnic->pdev->dev, "shost_data: 0x%p\n", fnic->host->shost_data);
+ if (fnic->host->shost_data != NULL) {
+ if (fnic_tgt_id_binding == 0) {
+ dev_info(&fnic->pdev->dev, "Setting target binding to NONE\n");
+ fc_host_tgtid_bind_type(fnic->host) = FC_TGTID_BIND_NONE;
+ } else {
+ dev_info(&fnic->pdev->dev, "Setting target binding to WWPN\n");
+ fc_host_tgtid_bind_type(fnic->host) = FC_TGTID_BIND_BY_WWPN;
+ }
+ }
+
+ fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache);
+ if (!fnic->io_req_pool) {
+ scsi_remove_host(fnic->host);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void fnic_mq_map_queues_cpus(struct Scsi_Host *host)
+{
+ struct fnic *fnic = *((struct fnic **) shost_priv(host));
+ struct pci_dev *l_pdev = fnic->pdev;
+ int intr_mode = fnic->config.intr_mode;
+ struct blk_mq_queue_map *qmap = &host->tag_set.map[HCTX_TYPE_DEFAULT];
+
+ if (intr_mode == VNIC_DEV_INTR_MODE_MSI || intr_mode == VNIC_DEV_INTR_MODE_INTX) {
+ FNIC_MAIN_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "intr_mode is not msix\n");
+ return;
+ }
+
+ FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "qmap->nr_queues: %d\n", qmap->nr_queues);
+
+ if (l_pdev == NULL) {
+ FNIC_MAIN_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "l_pdev is null\n");
+ return;
+ }
+
+ blk_mq_map_hw_queues(qmap, &l_pdev->dev, FNIC_PCI_OFFSET);
}
-static int __devinit fnic_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- struct Scsi_Host *host;
- struct fc_lport *lp;
+ struct Scsi_Host *host = NULL;
struct fnic *fnic;
mempool_t *pool;
- int err;
+ struct fnic_iport_s *iport;
+ int err = 0;
+ int fnic_id = 0;
int i;
unsigned long flags;
+ char *desc, *subsys_desc;
+ int len;
/*
- * Allocate SCSI Host and set up association between host,
- * local port, and fnic
+ * Allocate fnic
*/
- lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic));
- if (!lp) {
- printk(KERN_ERR PFX "Unable to alloc libfc local port\n");
+ fnic = kzalloc(sizeof(struct fnic), GFP_KERNEL);
+ if (!fnic) {
err = -ENOMEM;
- goto err_out;
+ goto err_out_fnic_alloc;
}
- host = lp->host;
- fnic = lport_priv(lp);
- fnic->lport = lp;
- fnic->ctlr.lp = lp;
- snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
- host->host_no);
-
- host->transportt = fnic_fc_transport;
+ iport = &fnic->iport;
- err = scsi_init_shared_tag_map(host, FNIC_MAX_IO_REQ);
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to alloc shared tag map\n");
- goto err_out_free_hba;
+ fnic_id = ida_alloc(&fnic_ida, GFP_KERNEL);
+ if (fnic_id < 0) {
+ dev_err(&pdev->dev, "Unable to alloc fnic ID\n");
+ err = fnic_id;
+ goto err_out_ida_alloc;
}
- /* Setup PCI resources */
- pci_set_drvdata(pdev, fnic);
-
fnic->pdev = pdev;
+ fnic->fnic_num = fnic_id;
+
+ /* Find model name from PCIe subsys ID */
+ if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0) {
+ dev_info(&fnic->pdev->dev, "Model: %s\n", subsys_desc);
+
+ /* Update FDMI model */
+ fnic->subsys_desc_len = strlen(subsys_desc);
+ len = ARRAY_SIZE(fnic->subsys_desc);
+ if (fnic->subsys_desc_len > len)
+ fnic->subsys_desc_len = len;
+ memcpy(fnic->subsys_desc, subsys_desc, fnic->subsys_desc_len);
+ dev_info(&fnic->pdev->dev, "FDMI Model: %s\n", fnic->subsys_desc);
+ } else {
+ fnic->subsys_desc_len = 0;
+ dev_info(&fnic->pdev->dev, "Model: %s subsys_id: 0x%04x\n", "Unknown",
+ pdev->subsystem_device);
+ }
err = pci_enable_device(pdev);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Cannot enable PCI device, aborting.\n");
- goto err_out_free_hba;
+ dev_err(&fnic->pdev->dev, "Cannot enable PCI device, aborting.\n");
+ goto err_out_pci_enable_device;
}
err = pci_request_regions(pdev, DRV_NAME);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Cannot enable PCI resources, aborting\n");
- goto err_out_disable_device;
+ dev_err(&fnic->pdev->dev, "Cannot enable PCI resources, aborting\n");
+ goto err_out_pci_request_regions;
}
pci_set_master(pdev);
/* Query PCI controller on system for DMA addressing
- * limitation for the device. Try 40-bit first, and
- * fail to 32-bit.
+ * limitation for the device. Try 47-bit first, and
+ * fail to 32-bit. Cisco VIC supports 47 bits only.
*/
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(40));
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(47));
if (err) {
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "No usable DMA configuration "
+ dev_err(&fnic->pdev->dev, "No usable DMA configuration "
"aborting\n");
- goto err_out_release_regions;
- }
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to obtain 32-bit DMA "
- "for consistent allocations, aborting.\n");
- goto err_out_release_regions;
- }
- } else {
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(40));
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to obtain 40-bit DMA "
- "for consistent allocations, aborting.\n");
- goto err_out_release_regions;
+ goto err_out_set_dma_mask;
}
}
/* Map vNIC resources from BAR0 */
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "BAR0 not memory-map'able, aborting.\n");
+ dev_err(&fnic->pdev->dev, "BAR0 not memory-map'able, aborting.\n");
err = -ENODEV;
- goto err_out_release_regions;
+ goto err_out_map_bar;
}
fnic->bar0.vaddr = pci_iomap(pdev, 0, 0);
@@ -512,76 +790,101 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fnic->bar0.len = pci_resource_len(pdev, 0);
if (!fnic->bar0.vaddr) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Cannot memory-map BAR0 res hdr, "
+ dev_err(&fnic->pdev->dev, "Cannot memory-map BAR0 res hdr, "
"aborting.\n");
err = -ENODEV;
- goto err_out_release_regions;
+ goto err_out_fnic_map_bar;
}
fnic->vdev = vnic_dev_register(NULL, fnic, pdev, &fnic->bar0);
if (!fnic->vdev) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC registration failed, "
+ dev_err(&fnic->pdev->dev, "vNIC registration failed, "
"aborting.\n");
err = -ENODEV;
- goto err_out_iounmap;
+ goto err_out_dev_register;
+ }
+
+ err = vnic_dev_cmd_init(fnic->vdev);
+ if (err) {
+ dev_err(&fnic->pdev->dev, "vnic_dev_cmd_init() returns %d, aborting\n",
+ err);
+ goto err_out_dev_cmd_init;
}
err = fnic_dev_wait(fnic->vdev, vnic_dev_open,
- vnic_dev_open_done, 0);
+ vnic_dev_open_done, CMD_OPENF_RQ_ENABLE_THEN_POST);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC dev open failed, aborting.\n");
- goto err_out_vnic_unregister;
+ dev_err(&fnic->pdev->dev, "vNIC dev open failed, aborting.\n");
+ goto err_out_dev_open;
}
err = vnic_dev_init(fnic->vdev, 0);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC dev init failed, aborting.\n");
- goto err_out_dev_close;
+ dev_err(&fnic->pdev->dev, "vNIC dev init failed, aborting.\n");
+ goto err_out_dev_init;
}
- err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
+ err = vnic_dev_mac_addr(fnic->vdev, iport->hwmac);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC get MAC addr failed \n");
- goto err_out_dev_close;
+ dev_err(&fnic->pdev->dev, "vNIC get MAC addr failed\n");
+ goto err_out_dev_mac_addr;
}
/* set data_src for point-to-point mode and to keep it non-zero */
- memcpy(fnic->data_src_addr, fnic->ctlr.ctl_src_addr, ETH_ALEN);
+ memcpy(fnic->data_src_addr, iport->hwmac, ETH_ALEN);
/* Get vNIC configuration */
err = fnic_get_vnic_config(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Get vNIC configuration failed, "
+ dev_err(&fnic->pdev->dev, "Get vNIC configuration failed, "
"aborting.\n");
- goto err_out_dev_close;
+ goto err_out_fnic_get_config;
}
- host->max_lun = fnic->config.luns_per_tgt;
- host->max_id = FNIC_MAX_FCP_TARGET;
- host->max_cmd_len = FCOE_MAX_CMD_LEN;
+
+ switch (fnic->config.flags & 0xff0) {
+ case VFCF_FC_INITIATOR:
+ {
+ host =
+ scsi_host_alloc(&fnic_host_template,
+ sizeof(struct fnic *));
+ if (!host) {
+ dev_err(&fnic->pdev->dev, "Unable to allocate scsi host\n");
+ err = -ENOMEM;
+ goto err_out_scsi_host_alloc;
+ }
+ *((struct fnic **) shost_priv(host)) = fnic;
+
+ fnic->host = host;
+ fnic->role = FNIC_ROLE_FCP_INITIATOR;
+ dev_info(&fnic->pdev->dev, "fnic: %d is scsi initiator\n",
+ fnic->fnic_num);
+ }
+ break;
+ default:
+ dev_info(&fnic->pdev->dev, "fnic: %d has no role defined\n", fnic->fnic_num);
+ err = -EINVAL;
+ goto err_out_fnic_role;
+ }
+
+ /* Setup PCI resources */
+ pci_set_drvdata(pdev, fnic);
fnic_get_res_counts(fnic);
err = fnic_set_intr_mode(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to set intr mode, "
+ dev_err(&fnic->pdev->dev, "Failed to set intr mode, "
"aborting.\n");
- goto err_out_dev_close;
+ goto err_out_fnic_set_intr_mode;
}
err = fnic_alloc_vnic_resources(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to alloc vNIC resources, "
+ dev_err(&fnic->pdev->dev, "Failed to alloc vNIC resources, "
"aborting.\n");
- goto err_out_clear_intr;
+ goto err_out_fnic_alloc_vnic_res;
}
-
+ dev_info(&fnic->pdev->dev, "fnic copy wqs: %d, Q0 ioreq table size: %d\n",
+ fnic->wq_copy_count, fnic->sw_copy_wq[0].ioreq_table_size);
/* initialize all fnic locks */
spin_lock_init(&fnic->fnic_lock);
@@ -596,237 +899,272 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fnic->fw_ack_index[i] = -1;
}
- for (i = 0; i < FNIC_IO_LOCKS; i++)
- spin_lock_init(&fnic->io_req_lock[i]);
-
- fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache);
- if (!fnic->io_req_pool)
+ pool = mempool_create_slab_pool(2, fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
+ if (!pool) {
+ err = -ENOMEM;
goto err_out_free_resources;
-
- pool = mempool_create(2, fnic_alloc_slab_dma, mempool_free_slab,
- fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
- if (!pool)
- goto err_out_free_ioreq_pool;
+ }
fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT] = pool;
- pool = mempool_create(2, fnic_alloc_slab_dma, mempool_free_slab,
- fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
- if (!pool)
+ pool = mempool_create_slab_pool(2, fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
+ if (!pool) {
+ err = -ENOMEM;
goto err_out_free_dflt_pool;
+ }
fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX] = pool;
+ pool = mempool_create_slab_pool(FDLS_MIN_FRAMES, fdls_frame_cache);
+ if (!pool) {
+ err = -ENOMEM;
+ goto err_out_fdls_frame_pool;
+ }
+ fnic->frame_pool = pool;
+
+ pool = mempool_create_slab_pool(FDLS_MIN_FRAME_ELEM,
+ fdls_frame_elem_cache);
+ if (!pool) {
+ err = -ENOMEM;
+ goto err_out_fdls_frame_elem_pool;
+ }
+ fnic->frame_elem_pool = pool;
+
/* setup vlan config, hw inserts vlan header */
fnic->vlan_hw_insert = 1;
fnic->vlan_id = 0;
- /* Initialize the FIP fcoe_ctrl struct */
- fnic->ctlr.send = fnic_eth_send;
- fnic->ctlr.update_mac = fnic_update_mac;
- fnic->ctlr.get_src_addr = fnic_get_mac;
if (fnic->config.flags & VFCF_FIP_CAPABLE) {
- shost_printk(KERN_INFO, fnic->lport->host,
- "firmware supports FIP\n");
+ dev_info(&fnic->pdev->dev, "firmware supports FIP\n");
/* enable directed and multicast */
vnic_dev_packet_filter(fnic->vdev, 1, 1, 0, 0, 0);
vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
- vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
- fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_AUTO);
+ vnic_dev_add_addr(fnic->vdev, iport->hwmac);
+ spin_lock_init(&fnic->vlans_lock);
+ INIT_WORK(&fnic->fip_frame_work, fnic_handle_fip_frame);
+ INIT_LIST_HEAD(&fnic->fip_frame_queue);
+ INIT_LIST_HEAD(&fnic->vlan_list);
+ timer_setup(&fnic->retry_fip_timer, fnic_handle_fip_timer, 0);
+ timer_setup(&fnic->fcs_ka_timer, fnic_handle_fcs_ka_timer, 0);
+ timer_setup(&fnic->enode_ka_timer, fnic_handle_enode_ka_timer, 0);
+ timer_setup(&fnic->vn_ka_timer, fnic_handle_vn_ka_timer, 0);
+ fnic->set_vlan = fnic_set_vlan;
} else {
- shost_printk(KERN_INFO, fnic->lport->host,
- "firmware uses non-FIP mode\n");
- fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_NON_FIP);
+ dev_info(&fnic->pdev->dev, "firmware uses non-FIP mode\n");
}
fnic->state = FNIC_IN_FC_MODE;
+ atomic_set(&fnic->in_flight, 0);
+ fnic->state_flags = FNIC_FLAGS_NONE;
+
/* Enable hardware stripping of vlan header on ingress */
fnic_set_nic_config(fnic, 0, 0, 0, 0, 0, 0, 1);
/* Setup notification buffer area */
err = fnic_notify_set(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to alloc notify buffer, aborting.\n");
- goto err_out_free_max_pool;
+ dev_err(&fnic->pdev->dev, "Failed to alloc notify buffer, aborting.\n");
+ goto err_out_fnic_notify_set;
}
/* Setup notify timer when using MSI interrupts */
if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
- setup_timer(&fnic->notify_timer,
- fnic_notify_timer, (unsigned long)fnic);
+ timer_setup(&fnic->notify_timer, fnic_notify_timer, 0);
/* allocate RQ buffers and post them to RQ*/
for (i = 0; i < fnic->rq_count; i++) {
err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "fnic_alloc_rq_frame can't alloc "
+ dev_err(&fnic->pdev->dev, "fnic_alloc_rq_frame can't alloc "
"frame\n");
- goto err_out_free_rq_buf;
+ goto err_out_alloc_rq_buf;
}
}
- /*
- * Initialization done with PCI system, hardware, firmware.
- * Add host to SCSI
- */
- err = scsi_add_host(lp->host, &pdev->dev);
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "fnic: scsi_add_host failed...exiting\n");
- goto err_out_free_rq_buf;
- }
-
- /* Start local port initiatialization */
+ init_completion(&fnic->reset_completion_wait);
- lp->link_up = 0;
-
- lp->max_retry_count = fnic->config.flogi_retries;
- lp->max_rport_retry_count = fnic->config.plogi_retries;
- lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
- FCP_SPPF_CONF_COMPL);
+ /* Start local port initialization */
+ iport->max_flogi_retries = fnic->config.flogi_retries;
+ iport->max_plogi_retries = fnic->config.plogi_retries;
+ iport->plogi_timeout = fnic->config.plogi_timeout;
+ iport->service_params =
+ (FNIC_FCP_SP_INITIATOR | FNIC_FCP_SP_RD_XRDY_DIS |
+ FNIC_FCP_SP_CONF_CMPL);
if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
- lp->service_params |= FCP_SPPF_RETRY;
+ iport->service_params |= FNIC_FCP_SP_RETRY;
- lp->boot_time = jiffies;
- lp->e_d_tov = fnic->config.ed_tov;
- lp->r_a_tov = fnic->config.ra_tov;
- lp->link_supported_speeds = FC_PORTSPEED_10GBIT;
- fc_set_wwnn(lp, fnic->config.node_wwn);
- fc_set_wwpn(lp, fnic->config.port_wwn);
+ iport->boot_time = jiffies;
+ iport->e_d_tov = fnic->config.ed_tov;
+ iport->r_a_tov = fnic->config.ra_tov;
+ iport->link_supported_speeds = FNIC_PORTSPEED_10GBIT;
+ iport->wwpn = fnic->config.port_wwn;
+ iport->wwnn = fnic->config.node_wwn;
- fcoe_libfc_config(lp, &fnic->ctlr, &fnic_transport_template, 0);
+ iport->max_payload_size = fnic->config.maxdatafieldsize;
- if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START,
- FCPIO_HOST_EXCH_RANGE_END, NULL)) {
- err = -ENOMEM;
- goto err_out_remove_scsi_host;
+ if ((iport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) ||
+ (iport->max_payload_size > FNIC_FC_MAX_PAYLOAD_LEN) ||
+ ((iport->max_payload_size % 4) != 0)) {
+ iport->max_payload_size = FNIC_FC_MAX_PAYLOAD_LEN;
}
- fc_lport_init_stats(lp);
+ iport->flags |= FNIC_FIRST_LINK_UP;
- fc_lport_config(lp);
+ timer_setup(&(iport->fabric.retry_timer), fdls_fabric_timer_callback,
+ 0);
- if (fc_set_mfs(lp, fnic->config.maxdatafieldsize +
- sizeof(struct fc_frame_header))) {
- err = -EINVAL;
- goto err_out_free_exch_mgr;
- }
- fc_host_maxframe_size(lp->host) = lp->mfs;
- fc_host_dev_loss_tmo(lp->host) = fnic->config.port_down_timeout / 1000;
-
- sprintf(fc_host_symbolic_name(lp->host),
- DRV_NAME " v" DRV_VERSION " over %s", fnic->name);
-
- spin_lock_irqsave(&fnic_list_lock, flags);
- list_add_tail(&fnic->list, &fnic_list);
- spin_unlock_irqrestore(&fnic_list_lock, flags);
+ fnic->stats_reset_time = jiffies;
INIT_WORK(&fnic->link_work, fnic_handle_link);
INIT_WORK(&fnic->frame_work, fnic_handle_frame);
- skb_queue_head_init(&fnic->frame_queue);
- skb_queue_head_init(&fnic->tx_queue);
+ INIT_WORK(&fnic->tport_work, fnic_tport_event_handler);
+ INIT_WORK(&fnic->flush_work, fnic_flush_tx);
+
+ INIT_LIST_HEAD(&fnic->frame_queue);
+ INIT_LIST_HEAD(&fnic->tx_queue);
+ INIT_LIST_HEAD(&fnic->tport_event_list);
+
+ INIT_DELAYED_WORK(&iport->oxid_pool.schedule_oxid_free_retry,
+ fdls_schedule_oxid_free_retry_work);
+
+ /* Initialize the oxid reclaim list and work struct */
+ INIT_LIST_HEAD(&iport->oxid_pool.oxid_reclaim_list);
+ INIT_DELAYED_WORK(&iport->oxid_pool.oxid_reclaim_work, fdls_reclaim_oxid_handler);
/* Enable all queues */
for (i = 0; i < fnic->raw_wq_count; i++)
vnic_wq_enable(&fnic->wq[i]);
- for (i = 0; i < fnic->rq_count; i++)
- vnic_rq_enable(&fnic->rq[i]);
+ for (i = 0; i < fnic->rq_count; i++) {
+ if (!ioread32(&fnic->rq[i].ctrl->enable))
+ vnic_rq_enable(&fnic->rq[i]);
+ }
for (i = 0; i < fnic->wq_copy_count; i++)
- vnic_wq_copy_enable(&fnic->wq_copy[i]);
-
- fc_fabric_login(lp);
+ vnic_wq_copy_enable(&fnic->hw_copy_wq[i]);
vnic_dev_enable(fnic->vdev);
err = fnic_request_intr(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to request irq.\n");
- goto err_out_free_exch_mgr;
+ dev_err(&fnic->pdev->dev, "Unable to request irq.\n");
+ goto err_out_fnic_request_intr;
+ }
+
+ fnic_notify_timer_start(fnic);
+
+ fnic_fdls_init(fnic, (fnic->config.flags & VFCF_FIP_CAPABLE));
+
+ err = fnic_scsi_drv_init(fnic);
+ if (err)
+ goto err_out_scsi_drv_init;
+
+ err = fnic_stats_debugfs_init(fnic);
+ if (err) {
+ dev_err(&fnic->pdev->dev, "Failed to initialize debugfs for stats\n");
+ goto err_out_free_stats_debugfs;
}
for (i = 0; i < fnic->intr_count; i++)
vnic_intr_unmask(&fnic->intr[i]);
- fnic_notify_timer_start(fnic);
+ spin_lock_irqsave(&fnic_list_lock, flags);
+ list_add_tail(&fnic->list, &fnic_list);
+ spin_unlock_irqrestore(&fnic_list_lock, flags);
return 0;
-err_out_free_exch_mgr:
- fc_exch_mgr_free(lp);
-err_out_remove_scsi_host:
- fc_remove_host(lp->host);
- scsi_remove_host(lp->host);
-err_out_free_rq_buf:
- for (i = 0; i < fnic->rq_count; i++)
+err_out_free_stats_debugfs:
+ fnic_stats_debugfs_remove(fnic);
+ fnic_free_ioreq_tables_mq(fnic);
+ scsi_remove_host(fnic->host);
+err_out_scsi_drv_init:
+ fnic_free_intr(fnic);
+err_out_fnic_request_intr:
+err_out_alloc_rq_buf:
+ for (i = 0; i < fnic->rq_count; i++) {
+ if (ioread32(&fnic->rq[i].ctrl->enable))
+ vnic_rq_disable(&fnic->rq[i]);
vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
+ }
vnic_dev_notify_unset(fnic->vdev);
-err_out_free_max_pool:
+err_out_fnic_notify_set:
+ mempool_destroy(fnic->frame_elem_pool);
+err_out_fdls_frame_elem_pool:
+ mempool_destroy(fnic->frame_pool);
+err_out_fdls_frame_pool:
mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX]);
err_out_free_dflt_pool:
mempool_destroy(fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT]);
-err_out_free_ioreq_pool:
- mempool_destroy(fnic->io_req_pool);
err_out_free_resources:
fnic_free_vnic_resources(fnic);
-err_out_clear_intr:
+err_out_fnic_alloc_vnic_res:
fnic_clear_intr_mode(fnic);
-err_out_dev_close:
+err_out_fnic_set_intr_mode:
+ scsi_host_put(fnic->host);
+err_out_fnic_role:
+err_out_scsi_host_alloc:
+err_out_fnic_get_config:
+err_out_dev_mac_addr:
+err_out_dev_init:
vnic_dev_close(fnic->vdev);
-err_out_vnic_unregister:
+err_out_dev_open:
+err_out_dev_cmd_init:
vnic_dev_unregister(fnic->vdev);
-err_out_iounmap:
+err_out_dev_register:
fnic_iounmap(fnic);
-err_out_release_regions:
+err_out_fnic_map_bar:
+err_out_map_bar:
+err_out_set_dma_mask:
pci_release_regions(pdev);
-err_out_disable_device:
+err_out_pci_request_regions:
pci_disable_device(pdev);
-err_out_free_hba:
- scsi_host_put(lp->host);
-err_out:
+err_out_pci_enable_device:
+ ida_free(&fnic_ida, fnic->fnic_num);
+err_out_ida_alloc:
+ kfree(fnic);
+err_out_fnic_alloc:
return err;
}
-static void __devexit fnic_remove(struct pci_dev *pdev)
+static void fnic_remove(struct pci_dev *pdev)
{
struct fnic *fnic = pci_get_drvdata(pdev);
- struct fc_lport *lp = fnic->lport;
unsigned long flags;
/*
- * Mark state so that the workqueue thread stops forwarding
- * received frames and link events to the local port. ISR and
- * other threads that can queue work items will also stop
- * creating work items on the fnic workqueue
+ * Sometimes when probe() fails and do not exit with an error code,
+ * remove() gets called with 'drvdata' not set. Avoid a crash by
+ * adding a defensive check.
*/
+ if (!fnic)
+ return;
+
spin_lock_irqsave(&fnic->fnic_lock, flags);
fnic->stop_rx_link_events = 1;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
- del_timer_sync(&fnic->notify_timer);
-
/*
* Flush the fnic event queue. After this call, there should
* be no event queued for this fnic device in the workqueue
*/
flush_workqueue(fnic_event_queue);
- skb_queue_purge(&fnic->frame_queue);
- skb_queue_purge(&fnic->tx_queue);
- /*
- * Log off the fabric. This stops all remote ports, dns port,
- * logs off the fabric. This flushes all rport, disc, lport work
- * before returning
- */
- fc_fabric_logoff(fnic->lport);
+ fnic_scsi_unload(fnic);
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- fnic->in_remove = 1;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
+ timer_delete_sync(&fnic->notify_timer);
+
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+ timer_delete_sync(&fnic->retry_fip_timer);
+ timer_delete_sync(&fnic->fcs_ka_timer);
+ timer_delete_sync(&fnic->enode_ka_timer);
+ timer_delete_sync(&fnic->vn_ka_timer);
+
+ fnic_free_txq(&fnic->fip_frame_queue);
+ fnic_fcoe_reset_vlans(fnic);
+ }
+
+ if ((fnic_fdmi_support == 1) && (fnic->iport.fabric.fdmi_pending > 0))
+ timer_delete_sync(&fnic->iport.fabric.fdmi_timer);
- fcoe_ctlr_destroy(&fnic->ctlr);
- fc_lport_destroy(lp);
+ fnic_stats_debugfs_remove(fnic);
/*
* This stops the fnic device, masks all interrupts. Completed
@@ -835,16 +1173,13 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
*/
fnic_cleanup(fnic);
- BUG_ON(!skb_queue_empty(&fnic->frame_queue));
- BUG_ON(!skb_queue_empty(&fnic->tx_queue));
-
spin_lock_irqsave(&fnic_list_lock, flags);
list_del(&fnic->list);
spin_unlock_irqrestore(&fnic_list_lock, flags);
- fc_remove_host(fnic->lport->host);
- scsi_remove_host(fnic->lport->host);
- fc_exch_mgr_free(fnic->lport);
+ fnic_free_txq(&fnic->frame_queue);
+ fnic_free_txq(&fnic->tx_queue);
+
vnic_dev_notify_unset(fnic->vdev);
fnic_free_intr(fnic);
fnic_free_vnic_resources(fnic);
@@ -855,14 +1190,17 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- scsi_host_put(lp->host);
+ ida_free(&fnic_ida, fnic->fnic_num);
+ fnic_scsi_unload_cleanup(fnic);
+ scsi_host_put(fnic->host);
+ kfree(fnic);
}
static struct pci_driver fnic_driver = {
.name = DRV_NAME,
.id_table = fnic_id_table,
.probe = fnic_probe,
- .remove = __devexit_p(fnic_remove),
+ .remove = fnic_remove,
};
static int __init fnic_init_module(void)
@@ -872,11 +1210,36 @@ static int __init fnic_init_module(void)
printk(KERN_INFO PFX "%s, ver %s\n", DRV_DESCRIPTION, DRV_VERSION);
+ /* Create debugfs entries for fnic */
+ err = fnic_debugfs_init();
+ if (err < 0) {
+ printk(KERN_ERR PFX "Failed to create fnic directory "
+ "for tracing and stats logging\n");
+ fnic_debugfs_terminate();
+ }
+
+ /* Allocate memory for trace buffer */
+ err = fnic_trace_buf_init();
+ if (err < 0) {
+ printk(KERN_ERR PFX
+ "Trace buffer initialization Failed. "
+ "Fnic Tracing utility is disabled\n");
+ fnic_trace_free();
+ }
+
+ /* Allocate memory for fc trace buffer */
+ err = fnic_fc_trace_init();
+ if (err < 0) {
+ printk(KERN_ERR PFX "FC trace buffer initialization Failed "
+ "FC frame tracing utility is disabled\n");
+ fnic_fc_trace_free();
+ }
+
/* Create a cache for allocation of default size sgls */
len = sizeof(struct fnic_dflt_sgl_list);
fnic_sgl_cache[FNIC_SGL_CACHE_DFLT] = kmem_cache_create
("fnic_sgl_dflt", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
- SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA,
+ SLAB_HWCACHE_ALIGN,
NULL);
if (!fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]) {
printk(KERN_ERR PFX "failed to create fnic dflt sgl slab\n");
@@ -888,8 +1251,8 @@ static int __init fnic_init_module(void)
len = sizeof(struct fnic_sgl_list);
fnic_sgl_cache[FNIC_SGL_CACHE_MAX] = kmem_cache_create
("fnic_sgl_max", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
- SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA,
- NULL);
+ SLAB_HWCACHE_ALIGN,
+ NULL);
if (!fnic_sgl_cache[FNIC_SGL_CACHE_MAX]) {
printk(KERN_ERR PFX "failed to create fnic max sgl slab\n");
err = -ENOMEM;
@@ -906,15 +1269,52 @@ static int __init fnic_init_module(void)
goto err_create_fnic_ioreq_slab;
}
- fnic_event_queue = create_singlethread_workqueue("fnic_event_wq");
+ fdls_frame_cache = kmem_cache_create("fdls_frames",
+ FNIC_FCOE_FRAME_MAXSZ,
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!fdls_frame_cache) {
+ pr_err("fnic fdls frame cache create failed\n");
+ err = -ENOMEM;
+ goto err_create_fdls_frame_cache;
+ }
+
+ fdls_frame_elem_cache = kmem_cache_create("fdls_frame_elem",
+ sizeof(struct fnic_frame_list),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!fdls_frame_elem_cache) {
+ pr_err("fnic fdls frame elem cache create failed\n");
+ err = -ENOMEM;
+ goto err_create_fdls_frame_cache_elem;
+ }
+
+ fnic_event_queue =
+ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, "fnic_event_wq");
if (!fnic_event_queue) {
printk(KERN_ERR PFX "fnic work queue create failed\n");
err = -ENOMEM;
goto err_create_fnic_workq;
}
- spin_lock_init(&fnic_list_lock);
- INIT_LIST_HEAD(&fnic_list);
+ fnic_fip_queue =
+ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, "fnic_fip_q");
+ if (!fnic_fip_queue) {
+ printk(KERN_ERR PFX "fnic FIP work queue create failed\n");
+ err = -ENOMEM;
+ goto err_create_fip_workq;
+ }
+
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON) {
+ reset_fnic_work_queue =
+ create_singlethread_workqueue("reset_fnic_work_queue");
+ if (!reset_fnic_work_queue) {
+ pr_err("reset fnic work queue create failed\n");
+ err = -ENOMEM;
+ goto err_create_reset_fnic_workq;
+ }
+ spin_lock_init(&reset_fnic_list_lock);
+ INIT_LIST_HEAD(&reset_fnic_list);
+ INIT_WORK(&reset_fnic_work, fnic_reset_work_handler);
+ }
fnic_fc_transport = fc_attach_transport(&fnic_fc_functions);
if (!fnic_fc_transport) {
@@ -934,14 +1334,26 @@ static int __init fnic_init_module(void)
err_pci_register:
fc_release_transport(fnic_fc_transport);
err_fc_transport:
+ destroy_workqueue(fnic_fip_queue);
+err_create_fip_workq:
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON)
+ destroy_workqueue(reset_fnic_work_queue);
+err_create_reset_fnic_workq:
destroy_workqueue(fnic_event_queue);
err_create_fnic_workq:
+ kmem_cache_destroy(fdls_frame_elem_cache);
+err_create_fdls_frame_cache_elem:
+ kmem_cache_destroy(fdls_frame_cache);
+err_create_fdls_frame_cache:
kmem_cache_destroy(fnic_io_req_cache);
err_create_fnic_ioreq_slab:
kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
err_create_fnic_sgl_slab_max:
kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
err_create_fnic_sgl_slab_dflt:
+ fnic_trace_free();
+ fnic_fc_trace_free();
+ fnic_debugfs_terminate();
return err;
}
@@ -949,12 +1361,23 @@ static void __exit fnic_cleanup_module(void)
{
pci_unregister_driver(&fnic_driver);
destroy_workqueue(fnic_event_queue);
+
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON)
+ destroy_workqueue(reset_fnic_work_queue);
+
+ if (fnic_fip_queue)
+ destroy_workqueue(fnic_fip_queue);
+
kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
kmem_cache_destroy(fnic_io_req_cache);
+ kmem_cache_destroy(fdls_frame_cache);
fc_release_transport(fnic_fc_transport);
+ fnic_trace_free();
+ fnic_fc_trace_free();
+ fnic_debugfs_terminate();
+ ida_destroy(&fnic_ida);
}
module_init(fnic_init_module);
module_exit(fnic_cleanup_module);
-
diff --git a/drivers/scsi/fnic/fnic_pci_subsys_devid.c b/drivers/scsi/fnic/fnic_pci_subsys_devid.c
new file mode 100644
index 000000000000..36a2c1268422
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_pci_subsys_devid.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2008 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <linux/if_ether.h>
+#include "fnic.h"
+
+static struct fnic_pcie_device fnic_pcie_device_table[] = {
+ {PCI_DEVICE_ID_CISCO_SERENO, "Sereno", PCI_SUBDEVICE_ID_CISCO_VASONA,
+ "VIC 1280"},
+ {PCI_DEVICE_ID_CISCO_SERENO, "Sereno", PCI_SUBDEVICE_ID_CISCO_COTATI,
+ "VIC 1240"},
+ {PCI_DEVICE_ID_CISCO_SERENO, "Sereno",
+ PCI_SUBDEVICE_ID_CISCO_LEXINGTON, "VIC 1225"},
+ {PCI_DEVICE_ID_CISCO_SERENO, "Sereno", PCI_SUBDEVICE_ID_CISCO_ICEHOUSE,
+ "VIC 1285"},
+ {PCI_DEVICE_ID_CISCO_SERENO, "Sereno",
+ PCI_SUBDEVICE_ID_CISCO_KIRKWOODLAKE, "VIC 1225T"},
+ {PCI_DEVICE_ID_CISCO_SERENO, "Sereno",
+ PCI_SUBDEVICE_ID_CISCO_SUSANVILLE, "VIC 1227"},
+ {PCI_DEVICE_ID_CISCO_SERENO, "Sereno", PCI_SUBDEVICE_ID_CISCO_TORRANCE,
+ "VIC 1227T"},
+
+ {PCI_DEVICE_ID_CISCO_CRUZ, "Cruz", PCI_SUBDEVICE_ID_CISCO_CALISTOGA,
+ "VIC 1340"},
+ {PCI_DEVICE_ID_CISCO_CRUZ, "Cruz", PCI_SUBDEVICE_ID_CISCO_MOUNTAINVIEW,
+ "VIC 1380"},
+ {PCI_DEVICE_ID_CISCO_CRUZ, "Cruz", PCI_SUBDEVICE_ID_CISCO_MOUNTTIAN,
+ "C3260-SIOC"},
+ {PCI_DEVICE_ID_CISCO_CRUZ, "Cruz", PCI_SUBDEVICE_ID_CISCO_CLEARLAKE,
+ "VIC 1385"},
+ {PCI_DEVICE_ID_CISCO_CRUZ, "Cruz", PCI_SUBDEVICE_ID_CISCO_MOUNTTIAN2,
+ "C3260-SIOC"},
+ {PCI_DEVICE_ID_CISCO_CRUZ, "Cruz", PCI_SUBDEVICE_ID_CISCO_CLAREMONT,
+ "VIC 1387"},
+
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega", PCI_SUBDEVICE_ID_CISCO_BRADBURY,
+ "VIC 1457"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega",
+ PCI_SUBDEVICE_ID_CISCO_BRENTWOOD, "VIC 1455"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega",
+ PCI_SUBDEVICE_ID_CISCO_BURLINGAME, "VIC 1487"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega", PCI_SUBDEVICE_ID_CISCO_BAYSIDE,
+ "VIC 1485"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega",
+ PCI_SUBDEVICE_ID_CISCO_BAKERSFIELD, "VIC 1440"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega",
+ PCI_SUBDEVICE_ID_CISCO_BOONVILLE, "VIC 1480"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega", PCI_SUBDEVICE_ID_CISCO_BENICIA,
+ "VIC 1495"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega", PCI_SUBDEVICE_ID_CISCO_BEAUMONT,
+ "VIC 1497"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega", PCI_SUBDEVICE_ID_CISCO_BRISBANE,
+ "VIC 1467"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega", PCI_SUBDEVICE_ID_CISCO_BENTON,
+ "VIC 1477"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega",
+ PCI_SUBDEVICE_ID_CISCO_TWIN_RIVER, "VIC 14425"},
+ {PCI_DEVICE_ID_CISCO_BODEGA, "Bodega",
+ PCI_SUBDEVICE_ID_CISCO_TWIN_PEAK, "VIC 14825"},
+
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly", PCI_SUBDEVICE_ID_CISCO_BERN,
+ "VIC 15420"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly",
+ PCI_SUBDEVICE_ID_CISCO_STOCKHOLM, "VIC 15428"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly", PCI_SUBDEVICE_ID_CISCO_KRAKOW,
+ "VIC 15411"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly",
+ PCI_SUBDEVICE_ID_CISCO_LUCERNE, "VIC 15231"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly", PCI_SUBDEVICE_ID_CISCO_TURKU,
+ "VIC 15238"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly", PCI_SUBDEVICE_ID_CISCO_GENEVA,
+ "VIC 15422"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly",
+ PCI_SUBDEVICE_ID_CISCO_HELSINKI, "VIC 15235"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly",
+ PCI_SUBDEVICE_ID_CISCO_GOTHENBURG, "VIC 15425"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly",
+ PCI_SUBDEVICE_ID_CISCO_TURKU_PLUS, "VIC 15237"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly", PCI_SUBDEVICE_ID_CISCO_ZURICH,
+ "VIC 15230"},
+ {PCI_DEVICE_ID_CISCO_BEVERLY, "Beverly", PCI_SUBDEVICE_ID_CISCO_RIGA,
+ "VIC 15427"},
+
+ {0,}
+};
+
+int fnic_get_desc_by_devid(struct pci_dev *pdev, char **desc,
+ char **subsys_desc)
+{
+ unsigned short device = PCI_DEVICE_ID_CISCO_VIC_FC;
+ int max = ARRAY_SIZE(fnic_pcie_device_table);
+ struct fnic_pcie_device *t = fnic_pcie_device_table;
+ int index = 0;
+
+ if (pdev->device != device)
+ return 1;
+
+ while (t->device != 0) {
+ if (memcmp
+ ((char *) &pdev->subsystem_device,
+ (char *) &t->subsystem_device, sizeof(short)) == 0)
+ break;
+ t++;
+ index++;
+ }
+
+ if (index >= max - 1) {
+ *desc = NULL;
+ *subsys_desc = NULL;
+ return 1;
+ }
+
+ *desc = fnic_pcie_device_table[index].desc;
+ *subsys_desc = fnic_pcie_device_table[index].subsys_desc;
+ return 0;
+}
diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c
index 50488f8e169d..9801e5fbb0dd 100644
--- a/drivers/scsi/fnic/fnic_res.c
+++ b/drivers/scsi/fnic/fnic_res.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/errno.h>
#include <linux/types.h>
@@ -42,9 +30,7 @@ int fnic_get_vnic_config(struct fnic *fnic)
offsetof(struct vnic_fc_config, m), \
sizeof(c->m), &c->m); \
if (err) { \
- shost_printk(KERN_ERR, fnic->lport->host, \
- "Error getting %s, %d\n", #m, \
- err); \
+ dev_err(&fnic->pdev->dev, "Error getting %s, %d\n", #m, err); \
return err; \
} \
} while (0);
@@ -69,6 +55,13 @@ int fnic_get_vnic_config(struct fnic *fnic)
GET_CONFIG(port_down_timeout);
GET_CONFIG(port_down_io_retries);
GET_CONFIG(luns_per_tgt);
+ GET_CONFIG(intr_mode);
+ GET_CONFIG(wq_copy_count);
+
+ if ((c->flags & (VFCF_FC_INITIATOR)) == 0) {
+ dev_info(&fnic->pdev->dev, "vNIC role not defined (def role: FC Init)\n");
+ c->flags |= VFCF_FC_INITIATOR;
+ }
c->wq_enet_desc_count =
min_t(u32, VNIC_FNIC_WQ_DESCS_MAX,
@@ -141,38 +134,35 @@ int fnic_get_vnic_config(struct fnic *fnic)
c->luns_per_tgt));
c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer);
- c->intr_timer_type = c->intr_timer_type;
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC MAC addr %pM "
- "wq/wq_copy/rq %d/%d/%d\n",
- fnic->ctlr.ctl_src_addr,
+ /* for older firmware, GET_CONFIG will not return anything */
+ if (c->wq_copy_count == 0)
+ c->wq_copy_count = 1;
+
+ c->wq_copy_count = min_t(u16, FNIC_WQ_COPY_MAX, c->wq_copy_count);
+
+ dev_info(&fnic->pdev->dev, "fNIC MAC addr %p wq/wq_copy/rq %d/%d/%d\n",
+ fnic->data_src_addr,
c->wq_enet_desc_count, c->wq_copy_desc_count,
c->rq_desc_count);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC node wwn %llx port wwn %llx\n",
+ dev_info(&fnic->pdev->dev, "fNIC node wwn 0x%llx port wwn 0x%llx\n",
c->node_wwn, c->port_wwn);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC ed_tov %d ra_tov %d\n",
+ dev_info(&fnic->pdev->dev, "fNIC ed_tov %d ra_tov %d\n",
c->ed_tov, c->ra_tov);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC mtu %d intr timer %d\n",
+ dev_info(&fnic->pdev->dev, "fNIC mtu %d intr timer %d\n",
c->maxdatafieldsize, c->intr_timer);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC flags 0x%x luns per tgt %d\n",
+ dev_info(&fnic->pdev->dev, "fNIC flags 0x%x luns per tgt %d\n",
c->flags, c->luns_per_tgt);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC flogi_retries %d flogi timeout %d\n",
+ dev_info(&fnic->pdev->dev, "fNIC flogi_retries %d flogi timeout %d\n",
c->flogi_retries, c->flogi_timeout);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC plogi retries %d plogi timeout %d\n",
+ dev_info(&fnic->pdev->dev, "fNIC plogi retries %d plogi timeout %d\n",
c->plogi_retries, c->plogi_timeout);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC io throttle count %d link dn timeout %d\n",
+ dev_info(&fnic->pdev->dev, "fNIC io throttle count %d link dn timeout %d\n",
c->io_throttle_count, c->link_down_timeout);
- shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC port dn io retries %d port dn timeout %d\n",
+ dev_info(&fnic->pdev->dev, "fNIC port dn io retries %d port dn timeout %d\n",
c->port_down_io_retries, c->port_down_timeout);
+ dev_info(&fnic->pdev->dev, "fNIC wq_copy_count: %d\n", c->wq_copy_count);
+ dev_info(&fnic->pdev->dev, "fNIC intr mode: %d\n", c->intr_mode);
return 0;
}
@@ -199,12 +189,19 @@ int fnic_set_nic_config(struct fnic *fnic, u8 rss_default_cpu,
void fnic_get_res_counts(struct fnic *fnic)
{
fnic->wq_count = vnic_dev_get_res_count(fnic->vdev, RES_TYPE_WQ);
- fnic->raw_wq_count = fnic->wq_count - 1;
- fnic->wq_copy_count = fnic->wq_count - fnic->raw_wq_count;
+ fnic->raw_wq_count = 1;
+ fnic->wq_copy_count = fnic->config.wq_copy_count;
fnic->rq_count = vnic_dev_get_res_count(fnic->vdev, RES_TYPE_RQ);
fnic->cq_count = vnic_dev_get_res_count(fnic->vdev, RES_TYPE_CQ);
fnic->intr_count = vnic_dev_get_res_count(fnic->vdev,
RES_TYPE_INTR_CTRL);
+
+ dev_info(&fnic->pdev->dev, "vNIC fw resources wq_count: %d\n", fnic->wq_count);
+ dev_info(&fnic->pdev->dev, "vNIC fw resources raw_wq_count: %d\n", fnic->raw_wq_count);
+ dev_info(&fnic->pdev->dev, "vNIC fw resources wq_copy_count: %d\n", fnic->wq_copy_count);
+ dev_info(&fnic->pdev->dev, "vNIC fw resources rq_count: %d\n", fnic->rq_count);
+ dev_info(&fnic->pdev->dev, "vNIC fw resources cq_count: %d\n", fnic->cq_count);
+ dev_info(&fnic->pdev->dev, "vNIC fw resources intr_count: %d\n", fnic->intr_count);
}
void fnic_free_vnic_resources(struct fnic *fnic)
@@ -215,7 +212,7 @@ void fnic_free_vnic_resources(struct fnic *fnic)
vnic_wq_free(&fnic->wq[i]);
for (i = 0; i < fnic->wq_copy_count; i++)
- vnic_wq_copy_free(&fnic->wq_copy[i]);
+ vnic_wq_copy_free(&fnic->hw_copy_wq[i]);
for (i = 0; i < fnic->rq_count; i++)
vnic_rq_free(&fnic->rq[i]);
@@ -240,16 +237,19 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
intr_mode = vnic_dev_get_intr_mode(fnic->vdev);
- shost_printk(KERN_INFO, fnic->lport->host, "vNIC interrupt mode: %s\n",
+ dev_info(&fnic->pdev->dev, "vNIC interrupt mode: %s\n",
intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" :
intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" :
intr_mode == VNIC_DEV_INTR_MODE_MSIX ?
"MSI-X" : "unknown");
- shost_printk(KERN_INFO, fnic->lport->host, "vNIC resources avail: "
- "wq %d cp_wq %d raw_wq %d rq %d cq %d intr %d\n",
- fnic->wq_count, fnic->wq_copy_count, fnic->raw_wq_count,
- fnic->rq_count, fnic->cq_count, fnic->intr_count);
+ dev_info(&fnic->pdev->dev, "res avail: wq %d cp_wq %d raw_wq %d rq %d",
+ fnic->wq_count, fnic->wq_copy_count,
+ fnic->raw_wq_count, fnic->rq_count);
+
+ dev_info(&fnic->pdev->dev, "res avail: cq %d intr %d cpy-wq desc count %d\n",
+ fnic->cq_count, fnic->intr_count,
+ fnic->config.wq_copy_desc_count);
/* Allocate Raw WQ used for FCS frames */
for (i = 0; i < fnic->raw_wq_count; i++) {
@@ -262,7 +262,7 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
/* Allocate Copy WQs used for SCSI IOs */
for (i = 0; i < fnic->wq_copy_count; i++) {
- err = vnic_wq_copy_alloc(fnic->vdev, &fnic->wq_copy[i],
+ err = vnic_wq_copy_alloc(fnic->vdev, &fnic->hw_copy_wq[i],
(fnic->raw_wq_count + i),
fnic->config.wq_copy_desc_count,
sizeof(struct fcpio_host_req));
@@ -322,8 +322,7 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
RES_TYPE_INTR_PBA_LEGACY, 0);
if (!fnic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to hook legacy pba resource\n");
+ dev_err(&fnic->pdev->dev, "Failed to hook legacy pba resource\n");
err = -ENODEV;
goto err_out_cleanup;
}
@@ -369,7 +368,7 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
}
for (i = 0; i < fnic->wq_copy_count; i++) {
- vnic_wq_copy_init(&fnic->wq_copy[i],
+ vnic_wq_copy_init(&fnic->hw_copy_wq[i],
0 /* cq_index 0 - always */,
error_interrupt_enable,
error_interrupt_offset);
@@ -426,8 +425,7 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
/* init the stats memory by making the first call here */
err = vnic_dev_stats_dump(fnic->vdev, &fnic->stats);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vnic_dev_stats_dump failed - x%x\n", err);
+ dev_err(&fnic->pdev->dev, "vnic_dev_stats_dump failed - x%x\n", err);
goto err_out_cleanup;
}
diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h
index ef8aaf2156dd..92a2fcfd3ea9 100644
--- a/drivers/scsi/fnic/fnic_res.h
+++ b/drivers/scsi/fnic/fnic_res.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _FNIC_RES_H_
#define _FNIC_RES_H_
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index 538b31c2cf58..75b29a018d1f 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/mempool.h>
#include <linux/errno.h>
@@ -23,6 +11,7 @@
#include <linux/scatterlist.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
+#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/delay.h>
@@ -34,11 +23,13 @@
#include <scsi/scsi_tcq.h>
#include <scsi/fc/fc_els.h>
#include <scsi/fc/fc_fcoe.h>
-#include <scsi/libfc.h>
#include <scsi/fc_frame.h>
+#include <scsi/scsi_transport_fc.h>
#include "fnic_io.h"
#include "fnic.h"
+static void fnic_cleanup_io(struct fnic *fnic, int exclude_id);
+
const char *fnic_state_str[] = {
[FNIC_IN_FC_MODE] = "FNIC_IN_FC_MODE",
[FNIC_IN_FC_TRANS_ETH_MODE] = "FNIC_IN_FC_TRANS_ETH_MODE",
@@ -47,6 +38,7 @@ const char *fnic_state_str[] = {
};
static const char *fnic_ioreq_state_str[] = {
+ [FNIC_IOREQ_NOT_INITED] = "FNIC_IOREQ_NOT_INITED",
[FNIC_IOREQ_CMD_PENDING] = "FNIC_IOREQ_CMD_PENDING",
[FNIC_IOREQ_ABTS_PENDING] = "FNIC_IOREQ_ABTS_PENDING",
[FNIC_IOREQ_ABTS_COMPLETE] = "FNIC_IOREQ_ABTS_COMPLETE",
@@ -75,6 +67,18 @@ static const char *fcpio_status_str[] = {
[FCPIO_LUNMAP_CHNG_PEND] = "FCPIO_LUNHMAP_CHNG_PEND",
};
+enum terminate_io_return {
+ TERM_SUCCESS = 0,
+ TERM_NO_SC = 1,
+ TERM_IO_REQ_NOT_FOUND,
+ TERM_ANOTHER_PORT,
+ TERM_GSTATE,
+ TERM_IO_BLOCKED,
+ TERM_OUT_OF_WQ_DESC,
+ TERM_TIMED_OUT,
+ TERM_MISC,
+};
+
const char *fnic_state_to_str(unsigned int state)
{
if (state >= ARRAY_SIZE(fnic_state_str) || !fnic_state_str[state])
@@ -100,16 +104,6 @@ static const char *fnic_fcpio_status_to_str(unsigned int status)
return fcpio_status_str[status];
}
-static void fnic_cleanup_io(struct fnic *fnic, int exclude_id);
-
-static inline spinlock_t *fnic_io_lock_hash(struct fnic *fnic,
- struct scsi_cmnd *sc)
-{
- u32 hash = sc->request->tag & (FNIC_IO_LOCKS - 1);
-
- return &fnic->io_req_lock[hash];
-}
-
/*
* Unmap the data buffer and sense buffer for an io_req,
* also unmap and free the device-private scatter/gather list.
@@ -119,37 +113,96 @@ static void fnic_release_ioreq_buf(struct fnic *fnic,
struct scsi_cmnd *sc)
{
if (io_req->sgl_list_pa)
- pci_unmap_single(fnic->pdev, io_req->sgl_list_pa,
+ dma_unmap_single(&fnic->pdev->dev, io_req->sgl_list_pa,
sizeof(io_req->sgl_list[0]) * io_req->sgl_cnt,
- PCI_DMA_TODEVICE);
+ DMA_TO_DEVICE);
scsi_dma_unmap(sc);
if (io_req->sgl_cnt)
mempool_free(io_req->sgl_list_alloc,
fnic->io_sgl_pool[io_req->sgl_type]);
if (io_req->sense_buf_pa)
- pci_unmap_single(fnic->pdev, io_req->sense_buf_pa,
- SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
+ dma_unmap_single(&fnic->pdev->dev, io_req->sense_buf_pa,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+}
+
+static bool
+fnic_count_portid_ioreqs_iter(struct fnic *fnic, struct scsi_cmnd *sc,
+ void *data1, void *data2)
+{
+ u32 *portid = data1;
+ unsigned int *count = data2;
+ struct fnic_io_req *io_req = fnic_priv(sc)->io_req;
+
+ if (!io_req || (*portid && (io_req->port_id != *portid)))
+ return true;
+
+ *count += 1;
+ return true;
+}
+
+unsigned int fnic_count_ioreqs(struct fnic *fnic, u32 portid)
+{
+ unsigned int count = 0;
+
+ fnic_scsi_io_iter(fnic, fnic_count_portid_ioreqs_iter,
+ &portid, &count);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "portid = 0x%x count = %u\n", portid, count);
+ return count;
+}
+
+unsigned int fnic_count_all_ioreqs(struct fnic *fnic)
+{
+ return fnic_count_ioreqs(fnic, 0);
+}
+
+static bool
+fnic_count_lun_ioreqs_iter(struct fnic *fnic, struct scsi_cmnd *sc,
+ void *data1, void *data2)
+{
+ struct scsi_device *scsi_device = data1;
+ unsigned int *count = data2;
+
+ if (sc->device != scsi_device || !fnic_priv(sc)->io_req)
+ return true;
+
+ *count += 1;
+ return true;
+}
+
+unsigned int
+fnic_count_lun_ioreqs(struct fnic *fnic, struct scsi_device *scsi_device)
+{
+ unsigned int count = 0;
+
+ fnic_scsi_io_iter(fnic, fnic_count_lun_ioreqs_iter,
+ scsi_device, &count);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "lun = %p count = %u\n", scsi_device, count);
+ return count;
}
/* Free up Copy Wq descriptors. Called with copy_wq lock held */
-static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq)
+static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq, unsigned int hwq)
{
/* if no Ack received from firmware, then nothing to clean */
- if (!fnic->fw_ack_recd[0])
+ if (!fnic->fw_ack_recd[hwq])
return 1;
/*
* Update desc_available count based on number of freed descriptors
* Account for wraparound
*/
- if (wq->to_clean_index <= fnic->fw_ack_index[0])
- wq->ring.desc_avail += (fnic->fw_ack_index[0]
+ if (wq->to_clean_index <= fnic->fw_ack_index[hwq])
+ wq->ring.desc_avail += (fnic->fw_ack_index[hwq]
- wq->to_clean_index + 1);
else
wq->ring.desc_avail += (wq->ring.desc_count
- wq->to_clean_index
- + fnic->fw_ack_index[0] + 1);
+ + fnic->fw_ack_index[hwq] + 1);
/*
* just bump clean index to ack_index+1 accounting for wraparound
@@ -157,45 +210,87 @@ static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq)
* to_clean_index and fw_ack_index, both inclusive
*/
wq->to_clean_index =
- (fnic->fw_ack_index[0] + 1) % wq->ring.desc_count;
+ (fnic->fw_ack_index[hwq] + 1) % wq->ring.desc_count;
/* we have processed the acks received so far */
- fnic->fw_ack_recd[0] = 0;
+ fnic->fw_ack_recd[hwq] = 0;
return 0;
}
/*
+ * __fnic_set_state_flags
+ * Sets/Clears bits in fnic's state_flags
+ **/
+void
+__fnic_set_state_flags(struct fnic *fnic, unsigned long st_flags,
+ unsigned long clearbits)
+{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ if (clearbits)
+ fnic->state_flags &= ~st_flags;
+ else
+ fnic->state_flags |= st_flags;
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ return;
+}
+
+
+/*
* fnic_fw_reset_handler
* Routine to send reset msg to fw
*/
int fnic_fw_reset_handler(struct fnic *fnic)
{
- struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ struct vnic_wq_copy *wq = &fnic->hw_copy_wq[0];
int ret = 0;
unsigned long flags;
+ unsigned int ioreq_count;
+
+ /* indicate fwreset to io path */
+ fnic_set_state_flags(fnic, FNIC_FLAGS_FWRESET);
+ ioreq_count = fnic_count_all_ioreqs(fnic);
- skb_queue_purge(&fnic->frame_queue);
- skb_queue_purge(&fnic->tx_queue);
+ /* wait for io cmpl */
+ while (atomic_read(&fnic->in_flight))
+ schedule_timeout(msecs_to_jiffies(1));
spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
- free_wq_copy_descs(fnic, wq);
+ free_wq_copy_descs(fnic, wq, 0);
if (!vnic_wq_copy_desc_avail(wq))
ret = -EAGAIN;
- else
+ else {
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "ioreq_count: %u\n", ioreq_count);
fnic_queue_wq_copy_desc_fw_reset(wq, SCSI_NO_TAG);
+ atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+ if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) >
+ atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs))
+ atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs,
+ atomic64_read(
+ &fnic->fnic_stats.fw_stats.active_fw_reqs));
+ }
spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
- if (!ret)
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Issued fw reset\n");
- else
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Failed to issue fw reset\n");
+ if (!ret) {
+ atomic64_inc(&fnic->fnic_stats.reset_stats.fw_resets);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Issued fw reset\n");
+ } else {
+ fnic_clear_state_flags(fnic, FNIC_FLAGS_FWRESET);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Failed to issue fw reset\n");
+ }
+
return ret;
}
@@ -206,47 +301,48 @@ int fnic_fw_reset_handler(struct fnic *fnic)
*/
int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id)
{
- struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ struct vnic_wq_copy *wq = &fnic->hw_copy_wq[0];
enum fcpio_flogi_reg_format_type format;
- struct fc_lport *lp = fnic->lport;
u8 gw_mac[ETH_ALEN];
int ret = 0;
unsigned long flags;
+ struct fnic_iport_s *iport = &fnic->iport;
spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
- free_wq_copy_descs(fnic, wq);
+ free_wq_copy_descs(fnic, wq, 0);
if (!vnic_wq_copy_desc_avail(wq)) {
ret = -EAGAIN;
goto flogi_reg_ioreq_end;
}
- if (fnic->ctlr.map_dest) {
- memset(gw_mac, 0xff, ETH_ALEN);
- format = FCPIO_FLOGI_REG_DEF_DEST;
- } else {
- memcpy(gw_mac, fnic->ctlr.dest_addr, ETH_ALEN);
- format = FCPIO_FLOGI_REG_GW_DEST;
- }
+ memcpy(gw_mac, fnic->iport.fcfmac, ETH_ALEN);
+ format = FCPIO_FLOGI_REG_GW_DEST;
- if ((fnic->config.flags & VFCF_FIP_CAPABLE) && !fnic->ctlr.map_dest) {
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
fnic_queue_wq_copy_desc_fip_reg(wq, SCSI_NO_TAG,
fc_id, gw_mac,
- fnic->data_src_addr,
- lp->r_a_tov, lp->e_d_tov);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "FLOGI FIP reg issued fcid %x src %pM dest %pM\n",
- fc_id, fnic->data_src_addr, gw_mac);
+ fnic->iport.fpma,
+ iport->r_a_tov, iport->e_d_tov);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI FIP reg issued fcid: 0x%x src %p dest %p\n",
+ fc_id, fnic->iport.fpma, gw_mac);
} else {
fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
format, fc_id, gw_mac);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "FLOGI reg issued fcid %x map %d dest %pM\n",
- fc_id, fnic->ctlr.map_dest, gw_mac);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FLOGI reg issued fcid 0x%x dest %p\n",
+ fc_id, gw_mac);
}
+ atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+ if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) >
+ atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs))
+ atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs,
+ atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs));
+
flogi_reg_ioreq_end:
spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
return ret;
@@ -260,19 +356,23 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
struct vnic_wq_copy *wq,
struct fnic_io_req *io_req,
struct scsi_cmnd *sc,
- int sg_count)
+ int sg_count,
+ uint32_t mqtag,
+ uint16_t hwq)
{
struct scatterlist *sg;
struct fc_rport *rport = starget_to_rport(scsi_target(sc->device));
- struct fc_rport_libfc_priv *rp = rport->dd_data;
struct host_sg_desc *desc;
- u8 pri_tag = 0;
+ struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
unsigned int i;
- unsigned long intr_flags;
int flags;
u8 exch_flags;
struct scsi_lun fc_lun;
- char msg[2];
+ struct fnic_tport_s *tport;
+ struct rport_dd_data_s *rdd_data;
+
+ rdd_data = rport->dd_data;
+ tport = rdd_data->tport;
if (sg_count) {
/* For each SGE, create a device desc entry */
@@ -284,34 +384,38 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
desc++;
}
- io_req->sgl_list_pa = pci_map_single
- (fnic->pdev,
- io_req->sgl_list,
- sizeof(io_req->sgl_list[0]) * sg_count,
- PCI_DMA_TODEVICE);
+ io_req->sgl_list_pa = dma_map_single(&fnic->pdev->dev,
+ io_req->sgl_list,
+ sizeof(io_req->sgl_list[0]) * sg_count,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&fnic->pdev->dev, io_req->sgl_list_pa)) {
+ printk(KERN_ERR "DMA mapping failed\n");
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
}
- io_req->sense_buf_pa = pci_map_single(fnic->pdev,
+ io_req->sense_buf_pa = dma_map_single(&fnic->pdev->dev,
sc->sense_buffer,
SCSI_SENSE_BUFFERSIZE,
- PCI_DMA_FROMDEVICE);
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&fnic->pdev->dev, io_req->sense_buf_pa)) {
+ dma_unmap_single(&fnic->pdev->dev, io_req->sgl_list_pa,
+ sizeof(io_req->sgl_list[0]) * sg_count,
+ DMA_TO_DEVICE);
+ printk(KERN_ERR "DMA mapping failed\n");
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
int_to_scsilun(sc->device->lun, &fc_lun);
- pri_tag = FCPIO_ICMND_PTA_SIMPLE;
- msg[0] = MSG_SIMPLE_TAG;
- scsi_populate_tag_msg(sc, msg);
- if (msg[0] == MSG_ORDERED_TAG)
- pri_tag = FCPIO_ICMND_PTA_ORDERED;
-
/* Enqueue the descriptor in the Copy WQ */
- spin_lock_irqsave(&fnic->wq_copy_lock[0], intr_flags);
-
- if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
- free_wq_copy_descs(fnic, wq);
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[hwq])
+ free_wq_copy_descs(fnic, wq, hwq);
if (unlikely(!vnic_wq_copy_desc_avail(wq))) {
- spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic_queue_wq_copy_desc failure - no descriptors\n");
+ atomic64_inc(&misc_stats->io_cpwq_alloc_failures);
return SCSI_MLQUEUE_HOST_BUSY;
}
@@ -323,68 +427,163 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
exch_flags = 0;
if ((fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR) &&
- (rp->flags & FC_RP_FLAGS_RETRY))
+ (tport->tgt_flags & FDLS_FC_RP_FLAGS_RETRY))
exch_flags |= FCPIO_ICMND_SRFLAG_RETRY;
- fnic_queue_wq_copy_desc_icmnd_16(wq, sc->request->tag,
+ fnic_queue_wq_copy_desc_icmnd_16(wq, mqtag,
0, exch_flags, io_req->sgl_cnt,
SCSI_SENSE_BUFFERSIZE,
io_req->sgl_list_pa,
io_req->sense_buf_pa,
0, /* scsi cmd ref, always 0 */
- pri_tag, /* scsi pri and tag */
+ FCPIO_ICMND_PTA_SIMPLE,
+ /* scsi pri and tag */
flags, /* command flags */
sc->cmnd, sc->cmd_len,
scsi_bufflen(sc),
fc_lun.scsi_lun, io_req->port_id,
- rport->maxframe_size, rp->r_a_tov,
- rp->e_d_tov);
+ tport->max_payload_size,
+ tport->r_a_tov, tport->e_d_tov);
+
+ atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+ if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) >
+ atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs))
+ atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs,
+ atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs));
- spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
return 0;
}
-/*
- * fnic_queuecommand
- * Routine to send a scsi cdb
- * Called with host_lock held and interrupts disabled.
- */
-static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+int fnic_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc)
{
- struct fc_lport *lp;
+ struct request *const rq = scsi_cmd_to_rq(sc);
+ uint32_t mqtag = 0;
+ void (*done)(struct scsi_cmnd *) = scsi_done;
struct fc_rport *rport;
- struct fnic_io_req *io_req;
- struct fnic *fnic;
+ struct fnic_io_req *io_req = NULL;
+ struct fnic *fnic = *((struct fnic **) shost_priv(sc->device->host));
+ struct fnic_iport_s *iport = NULL;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
struct vnic_wq_copy *wq;
- int ret;
- int sg_count;
- unsigned long flags;
+ int ret = 1;
+ u64 cmd_trace;
+ int sg_count = 0;
+ unsigned long flags = 0;
unsigned long ptr;
+ int io_lock_acquired = 0;
+ uint16_t hwq = 0;
+ struct fnic_tport_s *tport = NULL;
+ struct rport_dd_data_s *rdd_data;
+ uint16_t lun0_delay = 0;
rport = starget_to_rport(scsi_target(sc->device));
+ if (!rport) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "returning DID_NO_CONNECT for IO as rport is NULL\n");
+ sc->result = DID_NO_CONNECT << 16;
+ done(sc);
+ return 0;
+ }
+
ret = fc_remote_port_chkready(rport);
if (ret) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "rport is not ready\n");
+ atomic64_inc(&fnic_stats->misc_stats.tport_not_ready);
sc->result = ret;
done(sc);
return 0;
}
- lp = shost_priv(sc->device->host);
- if (lp->state != LPORT_ST_READY || !(lp->link_up))
- return SCSI_MLQUEUE_HOST_BUSY;
+ mqtag = blk_mq_unique_tag(rq);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ iport = &fnic->iport;
- /*
- * Release host lock, use driver resource specific locks from here.
- * Don't re-enable interrupts in case they were disabled prior to the
- * caller disabling them.
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "returning DID_NO_CONNECT for IO as iport state: %d\n",
+ iport->state);
+ sc->result = DID_NO_CONNECT << 16;
+ done(sc);
+ return 0;
+ }
+
+ /* fc_remote_port_add() may have added the tport to
+ * fc_transport but dd_data not yet set
*/
- spin_unlock(lp->host->host_lock);
+ rdd_data = rport->dd_data;
+ tport = rdd_data->tport;
+ if (!tport || (rdd_data->iport != iport)) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "dd_data not yet set in SCSI for rport portid: 0x%x\n",
+ rport->port_id);
+ tport = fnic_find_tport_by_fcid(iport, rport->port_id);
+ if (!tport) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "returning DID_BUS_BUSY for IO as tport not found for: 0x%x\n",
+ rport->port_id);
+ sc->result = DID_BUS_BUSY << 16;
+ done(sc);
+ return 0;
+ }
- /* Get a new io_req for this SCSI IO */
- fnic = lport_priv(lp);
+ /* Re-assign same params as in fnic_fdls_add_tport */
+ rport->maxframe_size = FNIC_FC_MAX_PAYLOAD_LEN;
+ rport->supported_classes =
+ FC_COS_CLASS3 | FC_RPORT_ROLE_FCP_TARGET;
+ /* the dd_data is allocated by fctransport of size dd_fcrport_size */
+ rdd_data = rport->dd_data;
+ rdd_data->tport = tport;
+ rdd_data->iport = iport;
+ tport->rport = rport;
+ tport->flags |= FNIC_FDLS_SCSI_REGISTERED;
+ }
+ if ((tport->state != FDLS_TGT_STATE_READY)
+ && (tport->state != FDLS_TGT_STATE_ADISC)) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "returning DID_NO_CONNECT for IO as tport state: %d\n",
+ tport->state);
+ sc->result = DID_NO_CONNECT << 16;
+ done(sc);
+ return 0;
+ }
+
+ atomic_inc(&fnic->in_flight);
+ atomic_inc(&tport->in_flight);
+
+ if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) {
+ atomic_dec(&fnic->in_flight);
+ atomic_dec(&tport->in_flight);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_FWRESET))) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "fnic flags FW reset: 0x%lx. Returning SCSI_MLQUEUE_HOST_BUSY\n",
+ fnic->state_flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ if (!tport->lun0_delay) {
+ lun0_delay = 1;
+ tport->lun0_delay++;
+ }
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ fnic_priv(sc)->state = FNIC_IOREQ_NOT_INITED;
+ fnic_priv(sc)->flags = FNIC_NO_FLAGS;
+
+ /* Get a new io_req for this SCSI IO */
io_req = mempool_alloc(fnic->io_req_pool, GFP_ATOMIC);
if (!io_req) {
+ atomic64_inc(&fnic_stats->io_stats.alloc_failures);
ret = SCSI_MLQUEUE_HOST_BUSY;
goto out;
}
@@ -393,10 +592,13 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_
/* Map the data buffer */
sg_count = scsi_dma_map(sc);
if (sg_count < 0) {
+ FNIC_TRACE(fnic_queuecommand, sc->device->host->host_no,
+ mqtag, sc, 0, sc->cmnd[0], sg_count, fnic_priv(sc)->state);
mempool_free(io_req, fnic->io_req_pool);
goto out;
}
+ io_req->tport = tport;
/* Determine the type of scatter/gather list we need */
io_req->sgl_cnt = sg_count;
io_req->sgl_type = FNIC_SGL_CACHE_DFLT;
@@ -406,8 +608,9 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_
if (sg_count) {
io_req->sgl_list =
mempool_alloc(fnic->io_sgl_pool[io_req->sgl_type],
- GFP_ATOMIC | GFP_DMA);
+ GFP_ATOMIC);
if (!io_req->sgl_list) {
+ atomic64_inc(&fnic_stats->io_stats.alloc_failures);
ret = SCSI_MLQUEUE_HOST_BUSY;
scsi_dma_unmap(sc);
mempool_free(io_req, fnic->io_req_pool);
@@ -425,39 +628,92 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_
}
}
+ /*
+ * Will acquire lock before setting to IO initialized.
+ */
+ hwq = blk_mq_unique_tag_to_hwq(mqtag);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+
/* initialize rest of io_req */
+ io_lock_acquired = 1;
io_req->port_id = rport->port_id;
- CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING;
- CMD_SP(sc) = (char *)io_req;
- sc->scsi_done = done;
+ io_req->start_time = jiffies;
+ fnic_priv(sc)->state = FNIC_IOREQ_CMD_PENDING;
+ fnic_priv(sc)->io_req = io_req;
+ fnic_priv(sc)->flags |= FNIC_IO_INITIALIZED;
+ io_req->sc = sc;
+
+ if (fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(mqtag)] != NULL) {
+ WARN(1, "fnic<%d>: %s: hwq: %d tag 0x%x already exists\n",
+ fnic->fnic_num, __func__, hwq, blk_mq_unique_tag_to_tag(mqtag));
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(mqtag)] = io_req;
+ io_req->tag = mqtag;
/* create copy wq desc and enqueue it */
- wq = &fnic->wq_copy[0];
- ret = fnic_queue_wq_copy_desc(fnic, wq, io_req, sc, sg_count);
+ wq = &fnic->hw_copy_wq[hwq];
+ atomic64_inc(&fnic_stats->io_stats.ios[hwq]);
+ ret = fnic_queue_wq_copy_desc(fnic, wq, io_req, sc, sg_count, mqtag, hwq);
if (ret) {
/*
* In case another thread cancelled the request,
* refetch the pointer under the lock.
*/
- spinlock_t *io_lock = fnic_io_lock_hash(fnic, sc);
-
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
- CMD_SP(sc) = NULL;
- CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
- spin_unlock_irqrestore(io_lock, flags);
+ FNIC_TRACE(fnic_queuecommand, sc->device->host->host_no,
+ mqtag, sc, 0, 0, 0, fnic_flags_and_state(sc));
+ io_req = fnic_priv(sc)->io_req;
+ fnic_priv(sc)->io_req = NULL;
+ if (io_req)
+ fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(mqtag)] = NULL;
+ fnic_priv(sc)->state = FNIC_IOREQ_CMD_COMPLETE;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
if (io_req) {
fnic_release_ioreq_buf(fnic, io_req, sc);
mempool_free(io_req, fnic->io_req_pool);
}
+ atomic_dec(&fnic->in_flight);
+ atomic_dec(&tport->in_flight);
+ return ret;
+ } else {
+ atomic64_inc(&fnic_stats->io_stats.active_ios);
+ atomic64_inc(&fnic_stats->io_stats.num_ios);
+ if (atomic64_read(&fnic_stats->io_stats.active_ios) >
+ atomic64_read(&fnic_stats->io_stats.max_active_ios))
+ atomic64_set(&fnic_stats->io_stats.max_active_ios,
+ atomic64_read(&fnic_stats->io_stats.active_ios));
+
+ /* REVISIT: Use per IO lock in the final code */
+ fnic_priv(sc)->flags |= FNIC_IO_ISSUED;
}
out:
- /* acquire host lock before returning to SCSI */
- spin_lock(lp->host->host_lock);
+ cmd_trace = ((u64)sc->cmnd[0] << 56 | (u64)sc->cmnd[7] << 40 |
+ (u64)sc->cmnd[8] << 32 | (u64)sc->cmnd[2] << 24 |
+ (u64)sc->cmnd[3] << 16 | (u64)sc->cmnd[4] << 8 |
+ sc->cmnd[5]);
+
+ FNIC_TRACE(fnic_queuecommand, sc->device->host->host_no,
+ mqtag, sc, io_req, sg_count, cmd_trace,
+ fnic_flags_and_state(sc));
+
+ /* if only we issued IO, will we have the io lock */
+ if (io_lock_acquired)
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+
+ atomic_dec(&fnic->in_flight);
+ atomic_dec(&tport->in_flight);
+
+ if (lun0_delay) {
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "LUN0 delay\n");
+ mdelay(LUN0_DELAY_TIME);
+ }
+
return ret;
}
-DEF_SCSI_QCMD(fnic_queuecommand)
/*
* fnic_fcpio_fw_reset_cmpl_handler
@@ -471,64 +727,66 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
struct fcpio_tag tag;
int ret = 0;
unsigned long flags;
+ struct reset_stats *reset_stats = &fnic->fnic_stats.reset_stats;
fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
+ atomic64_inc(&reset_stats->fw_reset_completions);
+
/* Clean up all outstanding io requests */
fnic_cleanup_io(fnic, SCSI_NO_TAG);
+ atomic64_set(&fnic->fnic_stats.fw_stats.active_fw_reqs, 0);
+ atomic64_set(&fnic->fnic_stats.io_stats.active_ios, 0);
+ atomic64_set(&fnic->io_cmpl_skip, 0);
+
spin_lock_irqsave(&fnic->fnic_lock, flags);
/* fnic should be in FC_TRANS_ETH_MODE */
if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
/* Check status of reset completion */
if (!hdr_status) {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "reset cmpl success\n");
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "reset cmpl success\n");
/* Ready to send flogi out */
fnic->state = FNIC_IN_ETH_MODE;
} else {
- FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
- "fnic fw_reset : failed %s\n",
- fnic_fcpio_status_to_str(hdr_status));
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "reset failed with header status: %s\n",
+ fnic_fcpio_status_to_str(hdr_status));
- /*
- * Unable to change to eth mode, cannot send out flogi
- * Change state to fc mode, so that subsequent Flogi
- * requests from libFC will cause more attempts to
- * reset the firmware. Free the cached flogi
- */
fnic->state = FNIC_IN_FC_MODE;
+ atomic64_inc(&reset_stats->fw_reset_failures);
ret = -1;
}
} else {
- FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
- "Unexpected state %s while processing"
- " reset cmpl\n", fnic_state_to_str(fnic->state));
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Unexpected state while processing reset completion: %s\n",
+ fnic_state_to_str(fnic->state));
+ atomic64_inc(&reset_stats->fw_reset_failures);
ret = -1;
}
- /* Thread removing device blocks till firmware reset is complete */
- if (fnic->remove_wait)
- complete(fnic->remove_wait);
+ if (fnic->fw_reset_done)
+ complete(fnic->fw_reset_done);
/*
* If fnic is being removed, or fw reset failed
* free the flogi frame. Else, send it out
*/
- if (fnic->remove_wait || ret) {
+ if (ret) {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- skb_queue_purge(&fnic->tx_queue);
+ fnic_free_txq(&fnic->tx_queue);
goto reset_cmpl_handler_end;
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- fnic_flush_tx(fnic);
+ queue_work(fnic_event_queue, &fnic->flush_work);
reset_cmpl_handler_end:
+ fnic_clear_state_flags(fnic, FNIC_FLAGS_FWRESET);
+
return ret;
}
@@ -554,19 +812,19 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
/* Check flogi registration completion status */
if (!hdr_status) {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "flog reg succeeded\n");
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "FLOGI reg succeeded\n");
fnic->state = FNIC_IN_FC_MODE;
} else {
FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
- "fnic flogi reg :failed %s\n",
+ fnic->host, fnic->fnic_num,
+ "fnic flogi reg failed: %s\n",
fnic_fcpio_status_to_str(hdr_status));
fnic->state = FNIC_IN_ETH_MODE;
ret = -1;
}
} else {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"Unexpected fnic state %s while"
" processing flogi reg completion\n",
fnic_state_to_str(fnic->state));
@@ -580,7 +838,7 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- fnic_flush_tx(fnic);
+ queue_work(fnic_event_queue, &fnic->flush_work);
queue_work(fnic_event_queue, &fnic->frame_work);
} else {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -622,71 +880,153 @@ static inline void fnic_fcpio_ack_handler(struct fnic *fnic,
struct vnic_wq_copy *wq;
u16 request_out = desc->u.ack.request_out;
unsigned long flags;
+ u64 *ox_id_tag = (u64 *)(void *)desc;
+ unsigned int wq_index = cq_index;
/* mark the ack state */
- wq = &fnic->wq_copy[cq_index - fnic->raw_wq_count - fnic->rq_count];
- spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
+ wq = &fnic->hw_copy_wq[cq_index];
+ spin_lock_irqsave(&fnic->wq_copy_lock[wq_index], flags);
+ fnic->fnic_stats.misc_stats.last_ack_time = jiffies;
if (is_ack_index_in_range(wq, request_out)) {
- fnic->fw_ack_index[0] = request_out;
- fnic->fw_ack_recd[0] = 1;
- }
- spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+ fnic->fw_ack_index[wq_index] = request_out;
+ fnic->fw_ack_recd[wq_index] = 1;
+ } else
+ atomic64_inc(
+ &fnic->fnic_stats.misc_stats.ack_index_out_of_range);
+
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[wq_index], flags);
+ FNIC_TRACE(fnic_fcpio_ack_handler,
+ fnic->host->host_no, 0, 0, ox_id_tag[2], ox_id_tag[3],
+ ox_id_tag[4], ox_id_tag[5]);
}
/*
* fnic_fcpio_icmnd_cmpl_handler
* Routine to handle icmnd completions
*/
-static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
+static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_index,
struct fcpio_fw_req *desc)
{
u8 type;
u8 hdr_status;
- struct fcpio_tag tag;
+ struct fcpio_tag ftag;
u32 id;
u64 xfer_len = 0;
struct fcpio_icmnd_cmpl *icmnd_cmpl;
struct fnic_io_req *io_req;
struct scsi_cmnd *sc;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
unsigned long flags;
- spinlock_t *io_lock;
+ u64 cmd_trace;
+ unsigned long start_time;
+ unsigned long io_duration_time;
+ unsigned int hwq = 0;
+ unsigned int mqtag = 0;
+ unsigned int tag = 0;
/* Decode the cmpl description to get the io_req id */
- fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
- fcpio_tag_id_dec(&tag, &id);
+ fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag);
+ fcpio_tag_id_dec(&ftag, &id);
+ icmnd_cmpl = &desc->u.icmnd_cmpl;
- if (id >= FNIC_MAX_IO_REQ)
+ mqtag = id;
+ tag = blk_mq_unique_tag_to_tag(mqtag);
+ hwq = blk_mq_unique_tag_to_hwq(mqtag);
+
+ if (hwq != cq_index) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
+ hwq, mqtag, tag, cq_index);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hdr status: %s icmnd completion on the wrong queue\n",
+ fnic_fcpio_status_to_str(hdr_status));
+ }
+
+ if (tag >= fnic->fnic_max_tag_id) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
+ hwq, mqtag, tag, cq_index);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hdr status: %s Out of range tag\n",
+ fnic_fcpio_status_to_str(hdr_status));
return;
+ }
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
- sc = scsi_host_find_tag(fnic->lport->host, id);
+ sc = scsi_host_find_tag(fnic->host, id);
WARN_ON_ONCE(!sc);
- if (!sc)
+ if (!sc) {
+ atomic64_inc(&fnic_stats->io_stats.sc_null);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ shost_printk(KERN_ERR, fnic->host,
+ "icmnd_cmpl sc is null - "
+ "hdr status = %s tag = 0x%x desc = 0x%p\n",
+ fnic_fcpio_status_to_str(hdr_status), id, desc);
+ FNIC_TRACE(fnic_fcpio_icmnd_cmpl_handler,
+ fnic->host->host_no, id,
+ ((u64)icmnd_cmpl->_resvd0[1] << 16 |
+ (u64)icmnd_cmpl->_resvd0[0]),
+ ((u64)hdr_status << 16 |
+ (u64)icmnd_cmpl->scsi_status << 8 |
+ (u64)icmnd_cmpl->flags), desc,
+ (u64)icmnd_cmpl->residual, 0);
return;
+ }
+
+ io_req = fnic_priv(sc)->io_req;
+ if (fnic->sw_copy_wq[hwq].io_req_table[tag] != io_req) {
+ WARN(1, "%s: %d: hwq: %d mqtag: 0x%x tag: 0x%x io_req tag mismatch\n",
+ __func__, __LINE__, hwq, mqtag, tag);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return;
+ }
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
WARN_ON_ONCE(!io_req);
if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
+ atomic64_inc(&fnic_stats->io_stats.ioreq_null);
+ fnic_priv(sc)->flags |= FNIC_IO_REQ_NULL;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ shost_printk(KERN_ERR, fnic->host,
+ "icmnd_cmpl io_req is null - "
+ "hdr status = %s tag = 0x%x sc 0x%p\n",
+ fnic_fcpio_status_to_str(hdr_status), id, sc);
return;
}
+ start_time = io_req->start_time;
/* firmware completed the io */
io_req->io_completed = 1;
/*
* if SCSI-ML has already issued abort on this command,
- * ignore completion of the IO. The abts path will clean it up
+ * set completion of the IO. The abts path will clean it up
*/
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
- spin_unlock_irqrestore(io_lock, flags);
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING) {
+
+ /*
+ * set the FNIC_IO_DONE so that this doesn't get
+ * flagged as 'out of order' if it was not aborted
+ */
+ fnic_priv(sc)->flags |= FNIC_IO_DONE;
+ fnic_priv(sc)->flags |= FNIC_IO_ABTS_PENDING;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ if(FCPIO_ABORTED == hdr_status)
+ fnic_priv(sc)->flags |= FNIC_IO_ABORTED;
+
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "icmnd_cmpl abts pending "
+ "hdr status = %s tag = 0x%x sc = 0x%p "
+ "scsi_status = %x residual = %d\n",
+ fnic_fcpio_status_to_str(hdr_status),
+ id, sc,
+ icmnd_cmpl->scsi_status,
+ icmnd_cmpl->residual);
return;
}
/* Mark the IO as complete */
- CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
+ fnic_priv(sc)->state = FNIC_IOREQ_CMD_COMPLETE;
icmnd_cmpl = &desc->u.icmnd_cmpl;
@@ -694,145 +1034,303 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
case FCPIO_SUCCESS:
sc->result = (DID_OK << 16) | icmnd_cmpl->scsi_status;
xfer_len = scsi_bufflen(sc);
- scsi_set_resid(sc, icmnd_cmpl->residual);
- if (icmnd_cmpl->flags & FCPIO_ICMND_CMPL_RESID_UNDER)
+ if (icmnd_cmpl->flags & FCPIO_ICMND_CMPL_RESID_UNDER) {
xfer_len -= icmnd_cmpl->residual;
-
- /*
- * If queue_full, then try to reduce queue depth for all
- * LUNS on the target. Todo: this should be accompanied
- * by a periodic queue_depth rampup based on successful
- * IO completion.
- */
- if (icmnd_cmpl->scsi_status == QUEUE_FULL) {
- struct scsi_device *t_sdev;
- int qd = 0;
-
- shost_for_each_device(t_sdev, sc->device->host) {
- if (t_sdev->id != sc->device->id)
- continue;
-
- if (t_sdev->queue_depth > 1) {
- qd = scsi_track_queue_full
- (t_sdev,
- t_sdev->queue_depth - 1);
- if (qd == -1)
- qd = t_sdev->host->cmd_per_lun;
- shost_printk(KERN_INFO,
- fnic->lport->host,
- "scsi[%d:%d:%d:%d"
- "] queue full detected,"
- "new depth = %d\n",
- t_sdev->host->host_no,
- t_sdev->channel,
- t_sdev->id, t_sdev->lun,
- t_sdev->queue_depth);
- }
- }
+ scsi_set_resid(sc, icmnd_cmpl->residual);
}
+
+ if (icmnd_cmpl->scsi_status == SAM_STAT_CHECK_CONDITION)
+ atomic64_inc(&fnic_stats->misc_stats.check_condition);
+
+ if (icmnd_cmpl->scsi_status == SAM_STAT_TASK_SET_FULL)
+ atomic64_inc(&fnic_stats->misc_stats.queue_fulls);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "xfer_len: %llu", xfer_len);
break;
case FCPIO_TIMEOUT: /* request was timed out */
+ atomic64_inc(&fnic_stats->misc_stats.fcpio_timeout);
sc->result = (DID_TIME_OUT << 16) | icmnd_cmpl->scsi_status;
break;
case FCPIO_ABORTED: /* request was aborted */
+ atomic64_inc(&fnic_stats->misc_stats.fcpio_aborted);
sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
break;
case FCPIO_DATA_CNT_MISMATCH: /* recv/sent more/less data than exp. */
+ atomic64_inc(&fnic_stats->misc_stats.data_count_mismatch);
scsi_set_resid(sc, icmnd_cmpl->residual);
sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
break;
case FCPIO_OUT_OF_RESOURCE: /* out of resources to complete request */
+ atomic64_inc(&fnic_stats->fw_stats.fw_out_of_resources);
sc->result = (DID_REQUEUE << 16) | icmnd_cmpl->scsi_status;
break;
- case FCPIO_INVALID_HEADER: /* header contains invalid data */
- case FCPIO_INVALID_PARAM: /* some parameter in request invalid */
- case FCPIO_REQ_NOT_SUPPORTED:/* request type is not supported */
+
case FCPIO_IO_NOT_FOUND: /* requested I/O was not found */
+ atomic64_inc(&fnic_stats->io_stats.io_not_found);
+ sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
+ break;
+
case FCPIO_SGL_INVALID: /* request was aborted due to sgl error */
- case FCPIO_MSS_INVALID: /* request was aborted due to mss error */
+ atomic64_inc(&fnic_stats->misc_stats.sgl_invalid);
+ sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
+ break;
+
case FCPIO_FW_ERR: /* request was terminated due fw error */
+ atomic64_inc(&fnic_stats->fw_stats.io_fw_errs);
+ sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
+ break;
+
+ case FCPIO_MSS_INVALID: /* request was aborted due to mss error */
+ atomic64_inc(&fnic_stats->misc_stats.mss_invalid);
+ sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
+ break;
+
+ case FCPIO_INVALID_HEADER: /* header contains invalid data */
+ case FCPIO_INVALID_PARAM: /* some parameter in request invalid */
+ case FCPIO_REQ_NOT_SUPPORTED:/* request type is not supported */
default:
- shost_printk(KERN_ERR, fnic->lport->host, "hdr status = %s\n",
- fnic_fcpio_status_to_str(hdr_status));
sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
break;
}
/* Break link with the SCSI command */
- CMD_SP(sc) = NULL;
+ fnic_priv(sc)->io_req = NULL;
+ io_req->sc = NULL;
+ fnic_priv(sc)->flags |= FNIC_IO_DONE;
+ fnic->sw_copy_wq[hwq].io_req_table[tag] = NULL;
+
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
- spin_unlock_irqrestore(io_lock, flags);
+ if (hdr_status != FCPIO_SUCCESS) {
+ atomic64_inc(&fnic_stats->io_stats.io_failures);
+ shost_printk(KERN_ERR, fnic->host, "hdr status = %s\n",
+ fnic_fcpio_status_to_str(hdr_status));
+ }
fnic_release_ioreq_buf(fnic, io_req, sc);
- mempool_free(io_req, fnic->io_req_pool);
+ cmd_trace = ((u64)hdr_status << 56) |
+ (u64)icmnd_cmpl->scsi_status << 48 |
+ (u64)icmnd_cmpl->flags << 40 | (u64)sc->cmnd[0] << 32 |
+ (u64)sc->cmnd[2] << 24 | (u64)sc->cmnd[3] << 16 |
+ (u64)sc->cmnd[4] << 8 | sc->cmnd[5];
+
+ FNIC_TRACE(fnic_fcpio_icmnd_cmpl_handler,
+ sc->device->host->host_no, id, sc,
+ ((u64)icmnd_cmpl->_resvd0[1] << 56 |
+ (u64)icmnd_cmpl->_resvd0[0] << 48 |
+ jiffies_to_msecs(jiffies - start_time)),
+ desc, cmd_trace, fnic_flags_and_state(sc));
if (sc->sc_data_direction == DMA_FROM_DEVICE) {
- fnic->lport->host_stats.fcp_input_requests++;
+ fnic_stats->host_stats.fcp_input_requests++;
fnic->fcp_input_bytes += xfer_len;
} else if (sc->sc_data_direction == DMA_TO_DEVICE) {
- fnic->lport->host_stats.fcp_output_requests++;
+ fnic_stats->host_stats.fcp_output_requests++;
fnic->fcp_output_bytes += xfer_len;
} else
- fnic->lport->host_stats.fcp_control_requests++;
+ fnic_stats->host_stats.fcp_control_requests++;
/* Call SCSI completion function to complete the IO */
- if (sc->scsi_done)
- sc->scsi_done(sc);
+ scsi_done(sc);
+ mempool_free(io_req, fnic->io_req_pool);
+
+ atomic64_dec(&fnic_stats->io_stats.active_ios);
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
+
+
+ io_duration_time = jiffies_to_msecs(jiffies) -
+ jiffies_to_msecs(start_time);
+
+ if(io_duration_time <= 10)
+ atomic64_inc(&fnic_stats->io_stats.io_btw_0_to_10_msec);
+ else if(io_duration_time <= 100)
+ atomic64_inc(&fnic_stats->io_stats.io_btw_10_to_100_msec);
+ else if(io_duration_time <= 500)
+ atomic64_inc(&fnic_stats->io_stats.io_btw_100_to_500_msec);
+ else if(io_duration_time <= 5000)
+ atomic64_inc(&fnic_stats->io_stats.io_btw_500_to_5000_msec);
+ else if(io_duration_time <= 10000)
+ atomic64_inc(&fnic_stats->io_stats.io_btw_5000_to_10000_msec);
+ else if(io_duration_time <= 30000)
+ atomic64_inc(&fnic_stats->io_stats.io_btw_10000_to_30000_msec);
+ else {
+ atomic64_inc(&fnic_stats->io_stats.io_greater_than_30000_msec);
+
+ if(io_duration_time > atomic64_read(&fnic_stats->io_stats.current_max_io_time))
+ atomic64_set(&fnic_stats->io_stats.current_max_io_time, io_duration_time);
+ }
}
/* fnic_fcpio_itmf_cmpl_handler
* Routine to handle itmf completions
*/
-static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
+static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_index,
struct fcpio_fw_req *desc)
{
u8 type;
u8 hdr_status;
- struct fcpio_tag tag;
+ struct fcpio_tag ftag;
u32 id;
- struct scsi_cmnd *sc;
+ struct scsi_cmnd *sc = NULL;
struct fnic_io_req *io_req;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ struct abort_stats *abts_stats = &fnic->fnic_stats.abts_stats;
+ struct terminate_stats *term_stats = &fnic->fnic_stats.term_stats;
+ struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
unsigned long flags;
- spinlock_t *io_lock;
-
- fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
- fcpio_tag_id_dec(&tag, &id);
+ unsigned long start_time;
+ unsigned int hwq = cq_index;
+ unsigned int mqtag;
+ unsigned int tag;
+
+ fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag);
+ fcpio_tag_id_dec(&ftag, &id);
+
+ mqtag = id & FNIC_TAG_MASK;
+ tag = blk_mq_unique_tag_to_tag(id & FNIC_TAG_MASK);
+ hwq = blk_mq_unique_tag_to_hwq(id & FNIC_TAG_MASK);
+
+ if (hwq != cq_index) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
+ hwq, mqtag, tag, cq_index);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hdr status: %s ITMF completion on the wrong queue\n",
+ fnic_fcpio_status_to_str(hdr_status));
+ }
- if ((id & FNIC_TAG_MASK) >= FNIC_MAX_IO_REQ)
+ if (tag > fnic->fnic_max_tag_id) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
+ hwq, mqtag, tag, cq_index);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hdr status: %s Tag out of range\n",
+ fnic_fcpio_status_to_str(hdr_status));
+ return;
+ } else if ((tag == fnic->fnic_max_tag_id) && !(id & FNIC_TAG_DEV_RST)) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
+ hwq, mqtag, tag, cq_index);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hdr status: %s Tag out of range\n",
+ fnic_fcpio_status_to_str(hdr_status));
return;
+ }
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+
+ /* If it is sg3utils allocated SC then tag_id
+ * is max_tag_id and SC is retrieved from io_req
+ */
+ if ((mqtag == fnic->fnic_max_tag_id) && (id & FNIC_TAG_DEV_RST)) {
+ io_req = fnic->sw_copy_wq[hwq].io_req_table[tag];
+ if (io_req)
+ sc = io_req->sc;
+ } else {
+ sc = scsi_host_find_tag(fnic->host, id & FNIC_TAG_MASK);
+ }
- sc = scsi_host_find_tag(fnic->lport->host, id & FNIC_TAG_MASK);
WARN_ON_ONCE(!sc);
- if (!sc)
+ if (!sc) {
+ atomic64_inc(&fnic_stats->io_stats.sc_null);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ shost_printk(KERN_ERR, fnic->host,
+ "itmf_cmpl sc is null - hdr status = %s tag = 0x%x\n",
+ fnic_fcpio_status_to_str(hdr_status), tag);
return;
+ }
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ io_req = fnic_priv(sc)->io_req;
WARN_ON_ONCE(!io_req);
if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
+ atomic64_inc(&fnic_stats->io_stats.ioreq_null);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_REQ_NULL;
+ shost_printk(KERN_ERR, fnic->host,
+ "itmf_cmpl io_req is null - "
+ "hdr status = %s tag = 0x%x sc 0x%p\n",
+ fnic_fcpio_status_to_str(hdr_status), tag, sc);
return;
}
-
- if (id & FNIC_TAG_ABORT) {
+ start_time = io_req->start_time;
+
+ if ((id & FNIC_TAG_ABORT) && (id & FNIC_TAG_DEV_RST)) {
+ /* Abort and terminate completion of device reset req */
+ /* REVISIT : Add asserts about various flags */
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Abt/term completion received\n",
+ hwq, mqtag, tag,
+ fnic_fcpio_status_to_str(hdr_status));
+ fnic_priv(sc)->state = FNIC_IOREQ_ABTS_COMPLETE;
+ fnic_priv(sc)->abts_status = hdr_status;
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_DONE;
+ if (io_req->abts_done)
+ complete(io_req->abts_done);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ } else if (id & FNIC_TAG_ABORT) {
/* Completion of abort cmd */
- if (CMD_STATE(sc) != FNIC_IOREQ_ABTS_PENDING) {
+ shost_printk(KERN_DEBUG, fnic->host,
+ "hwq: %d mqtag: 0x%x tag: 0x%x Abort header status: %s\n",
+ hwq, mqtag, tag,
+ fnic_fcpio_status_to_str(hdr_status));
+ switch (hdr_status) {
+ case FCPIO_SUCCESS:
+ break;
+ case FCPIO_TIMEOUT:
+ if (fnic_priv(sc)->flags & FNIC_IO_ABTS_ISSUED)
+ atomic64_inc(&abts_stats->abort_fw_timeouts);
+ else
+ atomic64_inc(
+ &term_stats->terminate_fw_timeouts);
+ break;
+ case FCPIO_ITMF_REJECTED:
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "abort reject recd. id %d\n",
+ (int)(id & FNIC_TAG_MASK));
+ break;
+ case FCPIO_IO_NOT_FOUND:
+ if (fnic_priv(sc)->flags & FNIC_IO_ABTS_ISSUED)
+ atomic64_inc(&abts_stats->abort_io_not_found);
+ else
+ atomic64_inc(
+ &term_stats->terminate_io_not_found);
+ break;
+ default:
+ if (fnic_priv(sc)->flags & FNIC_IO_ABTS_ISSUED)
+ atomic64_inc(&abts_stats->abort_failures);
+ else
+ atomic64_inc(
+ &term_stats->terminate_failures);
+ break;
+ }
+ if (fnic_priv(sc)->state != FNIC_IOREQ_ABTS_PENDING) {
/* This is a late completion. Ignore it */
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
return;
}
- CMD_STATE(sc) = FNIC_IOREQ_ABTS_COMPLETE;
- CMD_ABTS_STATUS(sc) = hdr_status;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_DONE;
+ fnic_priv(sc)->abts_status = hdr_status;
+
+ /* If the status is IO not found consider it as success */
+ if (hdr_status == FCPIO_IO_NOT_FOUND)
+ fnic_priv(sc)->abts_status = FCPIO_SUCCESS;
+
+ if (!(fnic_priv(sc)->flags & (FNIC_IO_ABORTED | FNIC_IO_DONE)))
+ atomic64_inc(&misc_stats->no_icmnd_itmf_cmpls);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"abts cmpl recd. id %d status %s\n",
(int)(id & FNIC_TAG_MASK),
fnic_fcpio_status_to_str(hdr_status));
@@ -844,38 +1342,89 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
*/
if (io_req->abts_done) {
complete(io_req->abts_done);
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ shost_printk(KERN_INFO, fnic->host,
+ "hwq: %d mqtag: 0x%x tag: 0x%x Waking up abort thread\n",
+ hwq, mqtag, tag);
} else {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "abts cmpl, completing IO\n");
- CMD_SP(sc) = NULL;
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Completing IO\n",
+ hwq, mqtag,
+ tag, fnic_fcpio_status_to_str(hdr_status));
+ fnic_priv(sc)->io_req = NULL;
sc->result = (DID_ERROR << 16);
-
- spin_unlock_irqrestore(io_lock, flags);
+ fnic->sw_copy_wq[hwq].io_req_table[tag] = NULL;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
fnic_release_ioreq_buf(fnic, io_req, sc);
mempool_free(io_req, fnic->io_req_pool);
- if (sc->scsi_done)
- sc->scsi_done(sc);
+ FNIC_TRACE(fnic_fcpio_itmf_cmpl_handler,
+ sc->device->host->host_no, id,
+ sc,
+ jiffies_to_msecs(jiffies - start_time),
+ desc,
+ (((u64)hdr_status << 40) |
+ (u64)sc->cmnd[0] << 32 |
+ (u64)sc->cmnd[2] << 24 |
+ (u64)sc->cmnd[3] << 16 |
+ (u64)sc->cmnd[4] << 8 | sc->cmnd[5]),
+ fnic_flags_and_state(sc));
+ scsi_done(sc);
+ atomic64_dec(&fnic_stats->io_stats.active_ios);
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
}
-
} else if (id & FNIC_TAG_DEV_RST) {
/* Completion of device reset */
- CMD_LR_STATUS(sc) = hdr_status;
- CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "dev reset cmpl recd. id %d status %s\n",
- (int)(id & FNIC_TAG_MASK),
- fnic_fcpio_status_to_str(hdr_status));
+ shost_printk(KERN_INFO, fnic->host,
+ "hwq: %d mqtag: 0x%x tag: 0x%x DR hst: %s\n",
+ hwq, mqtag,
+ tag, fnic_fcpio_status_to_str(hdr_status));
+ fnic_priv(sc)->lr_status = hdr_status;
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_ABTS_PENDING;
+ FNIC_TRACE(fnic_fcpio_itmf_cmpl_handler,
+ sc->device->host->host_no, id, sc,
+ jiffies_to_msecs(jiffies - start_time),
+ desc, 0, fnic_flags_and_state(sc));
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Terminate pending\n",
+ hwq, mqtag,
+ tag, fnic_fcpio_status_to_str(hdr_status));
+ return;
+ }
+ if (fnic_priv(sc)->flags & FNIC_DEV_RST_TIMED_OUT) {
+ /* Need to wait for terminate completion */
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ FNIC_TRACE(fnic_fcpio_itmf_cmpl_handler,
+ sc->device->host->host_no, id, sc,
+ jiffies_to_msecs(jiffies - start_time),
+ desc, 0, fnic_flags_and_state(sc));
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "dev reset cmpl recd after time out. "
+ "id %d status %s\n",
+ (int)(id & FNIC_TAG_MASK),
+ fnic_fcpio_status_to_str(hdr_status));
+ return;
+ }
+ fnic_priv(sc)->state = FNIC_IOREQ_CMD_COMPLETE;
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_DONE;
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s DR completion received\n",
+ hwq, mqtag,
+ tag, fnic_fcpio_status_to_str(hdr_status));
if (io_req->dr_done)
complete(io_req->dr_done);
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
} else {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unexpected itmf io state %s tag %x\n",
- fnic_ioreq_state_to_str(CMD_STATE(sc)), id);
- spin_unlock_irqrestore(io_lock, flags);
+ shost_printk(KERN_ERR, fnic->host,
+ "%s: Unexpected itmf io state: hwq: %d tag 0x%x %s\n",
+ __func__, hwq, id, fnic_ioreq_state_to_str(fnic_priv(sc)->state));
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
}
}
@@ -889,7 +1438,20 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
struct fcpio_fw_req *desc)
{
struct fnic *fnic = vnic_dev_priv(vdev);
- int ret = 0;
+
+ switch (desc->hdr.type) {
+ case FCPIO_ICMND_CMPL: /* fw completed a command */
+ case FCPIO_ITMF_CMPL: /* fw completed itmf (abort cmd, lun reset)*/
+ case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
+ case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */
+ case FCPIO_RESET_CMPL: /* fw completed reset */
+ atomic64_dec(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+ break;
+ default:
+ break;
+ }
+
+ cq_index -= fnic->copy_wq_base;
switch (desc->hdr.type) {
case FCPIO_ACK: /* fw copied copy wq desc to its queue */
@@ -897,95 +1459,185 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
break;
case FCPIO_ICMND_CMPL: /* fw completed a command */
- fnic_fcpio_icmnd_cmpl_handler(fnic, desc);
+ fnic_fcpio_icmnd_cmpl_handler(fnic, cq_index, desc);
break;
case FCPIO_ITMF_CMPL: /* fw completed itmf (abort cmd, lun reset)*/
- fnic_fcpio_itmf_cmpl_handler(fnic, desc);
+ fnic_fcpio_itmf_cmpl_handler(fnic, cq_index, desc);
break;
case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */
- ret = fnic_fcpio_flogi_reg_cmpl_handler(fnic, desc);
+ fnic_fcpio_flogi_reg_cmpl_handler(fnic, desc);
break;
case FCPIO_RESET_CMPL: /* fw completed reset */
- ret = fnic_fcpio_fw_reset_cmpl_handler(fnic, desc);
+ fnic_fcpio_fw_reset_cmpl_handler(fnic, desc);
break;
default:
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"firmware completion type %d\n",
desc->hdr.type);
break;
}
- return ret;
+ return 0;
}
/*
* fnic_wq_copy_cmpl_handler
* Routine to process wq copy
*/
-int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do)
+int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do, unsigned int cq_index)
{
- unsigned int wq_work_done = 0;
- unsigned int i, cq_index;
unsigned int cur_work_done;
-
- for (i = 0; i < fnic->wq_copy_count; i++) {
- cq_index = i + fnic->raw_wq_count + fnic->rq_count;
- cur_work_done = vnic_cq_copy_service(&fnic->cq[cq_index],
- fnic_fcpio_cmpl_handler,
- copy_work_to_do);
- wq_work_done += cur_work_done;
+ struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
+ u64 start_jiffies = 0;
+ u64 end_jiffies = 0;
+ u64 delta_jiffies = 0;
+ u64 delta_ms = 0;
+
+ start_jiffies = jiffies;
+ cur_work_done = vnic_cq_copy_service(&fnic->cq[cq_index],
+ fnic_fcpio_cmpl_handler,
+ copy_work_to_do);
+ end_jiffies = jiffies;
+ delta_jiffies = end_jiffies - start_jiffies;
+ if (delta_jiffies > (u64) atomic64_read(&misc_stats->max_isr_jiffies)) {
+ atomic64_set(&misc_stats->max_isr_jiffies, delta_jiffies);
+ delta_ms = jiffies_to_msecs(delta_jiffies);
+ atomic64_set(&misc_stats->max_isr_time_ms, delta_ms);
+ atomic64_set(&misc_stats->corr_work_done, cur_work_done);
}
- return wq_work_done;
+
+ return cur_work_done;
}
-static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
+static bool fnic_cleanup_io_iter(struct scsi_cmnd *sc, void *data)
{
- unsigned int i;
+ struct request *const rq = scsi_cmd_to_rq(sc);
+ struct fnic *fnic = data;
struct fnic_io_req *io_req;
- unsigned long flags = 0;
- struct scsi_cmnd *sc;
- spinlock_t *io_lock;
-
- for (i = 0; i < FNIC_MAX_IO_REQ; i++) {
- if (i == exclude_id)
- continue;
+ unsigned long start_time = 0;
+ unsigned long flags;
+ struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+ uint16_t hwq = 0;
+ int tag;
+ int mqtag;
- sc = scsi_host_find_tag(fnic->lport->host, i);
- if (!sc)
- continue;
+ mqtag = blk_mq_unique_tag(rq);
+ hwq = blk_mq_unique_tag_to_hwq(mqtag);
+ tag = blk_mq_unique_tag_to_tag(mqtag);
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
- if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
- goto cleanup_scsi_cmd;
- }
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
- CMD_SP(sc) = NULL;
+ fnic->sw_copy_wq[hwq].io_req_table[tag] = NULL;
- spin_unlock_irqrestore(io_lock, flags);
+ io_req = fnic_priv(sc)->io_req;
+ if (!io_req) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d mqtag: 0x%x tag: 0x%x flags: 0x%x No ioreq. Returning\n",
+ hwq, mqtag, tag, fnic_priv(sc)->flags);
+ return true;
+ }
+ if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET) &&
+ !(fnic_priv(sc)->flags & FNIC_DEV_RST_DONE)) {
/*
- * If there is a scsi_cmnd associated with this io_req, then
- * free the corresponding state
+ * We will be here only when FW completes reset
+ * without sending completions for outstanding ios.
*/
- fnic_release_ioreq_buf(fnic, io_req, sc);
- mempool_free(io_req, fnic->io_req_pool);
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_DONE;
+ if (io_req && io_req->dr_done)
+ complete(io_req->dr_done);
+ else if (io_req && io_req->abts_done)
+ complete(io_req->abts_done);
+
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ } else if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ }
-cleanup_scsi_cmd:
- sc->result = DID_TRANSPORT_DISRUPTED << 16;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "fnic_cleanup_io:"
- " DID_TRANSPORT_DISRUPTED\n");
+ fnic_priv(sc)->io_req = NULL;
+ io_req->sc = NULL;
+ start_time = io_req->start_time;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
- /* Complete the command to SCSI */
- if (sc->scsi_done)
- sc->scsi_done(sc);
+ /*
+ * If there is a scsi_cmnd associated with this io_req, then
+ * free the corresponding state
+ */
+ fnic_release_ioreq_buf(fnic, io_req, sc);
+ mempool_free(io_req, fnic->io_req_pool);
+
+ sc->result = DID_TRANSPORT_DISRUPTED << 16;
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "mqtag: 0x%x tag: 0x%x sc: 0x%p duration = %lu DID_TRANSPORT_DISRUPTED\n",
+ mqtag, tag, sc, (jiffies - start_time));
+
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
+
+ FNIC_TRACE(fnic_cleanup_io,
+ sc->device->host->host_no, tag, sc,
+ jiffies_to_msecs(jiffies - start_time),
+ 0, ((u64) sc->cmnd[0] << 32 |
+ (u64) sc->cmnd[2] << 24 |
+ (u64) sc->cmnd[3] << 16 |
+ (u64) sc->cmnd[4] << 8 | sc->cmnd[5]),
+ (((u64) fnic_priv(sc)->flags << 32) | fnic_priv(sc)->
+ state));
+
+ /* Complete the command to SCSI */
+ scsi_done(sc);
+ return true;
+}
+
+static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
+{
+ unsigned int io_count = 0;
+ unsigned long flags;
+ struct fnic_io_req *io_req = NULL;
+ struct scsi_cmnd *sc = NULL;
+
+ io_count = fnic_count_all_ioreqs(fnic);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Outstanding ioreq count: %d active io count: %lld Waiting\n",
+ io_count,
+ atomic64_read(&fnic->fnic_stats.io_stats.active_ios));
+
+ scsi_host_busy_iter(fnic->host,
+ fnic_cleanup_io_iter, fnic);
+
+ /* with sg3utils device reset, SC needs to be retrieved from ioreq */
+ spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
+ io_req = fnic->sw_copy_wq[0].io_req_table[fnic->fnic_max_tag_id];
+ if (io_req) {
+ sc = io_req->sc;
+ if (sc) {
+ if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET)
+ && !(fnic_priv(sc)->flags & FNIC_DEV_RST_DONE)) {
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_DONE;
+ if (io_req && io_req->dr_done)
+ complete(io_req->dr_done);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+
+ while ((io_count = fnic_count_all_ioreqs(fnic))) {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Outstanding ioreq count: %d active io count: %lld Waiting\n",
+ io_count,
+ atomic64_read(&fnic->fnic_stats.io_stats.active_ios));
+
+ schedule_timeout(msecs_to_jiffies(100));
}
}
@@ -997,230 +1649,349 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
struct fnic_io_req *io_req;
struct scsi_cmnd *sc;
unsigned long flags;
- spinlock_t *io_lock;
+ unsigned long start_time = 0;
+ uint16_t hwq;
/* get the tag reference */
fcpio_tag_id_dec(&desc->hdr.tag, &id);
id &= FNIC_TAG_MASK;
- if (id >= FNIC_MAX_IO_REQ)
+ if (id >= fnic->fnic_max_tag_id)
return;
- sc = scsi_host_find_tag(fnic->lport->host, id);
+ sc = scsi_host_find_tag(fnic->host, id);
if (!sc)
return;
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
+ hwq = blk_mq_unique_tag_to_hwq(id);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
/* Get the IO context which this desc refers to */
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ io_req = fnic_priv(sc)->io_req;
/* fnic interrupts are turned off by now */
if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
goto wq_copy_cleanup_scsi_cmd;
}
- CMD_SP(sc) = NULL;
+ fnic_priv(sc)->io_req = NULL;
+ io_req->sc = NULL;
+ fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(id)] = NULL;
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ start_time = io_req->start_time;
fnic_release_ioreq_buf(fnic, io_req, sc);
mempool_free(io_req, fnic->io_req_pool);
wq_copy_cleanup_scsi_cmd:
sc->result = DID_NO_CONNECT << 16;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "wq_copy_cleanup_handler:"
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, "wq_copy_cleanup_handler:"
" DID_NO_CONNECT\n");
- if (sc->scsi_done)
- sc->scsi_done(sc);
+ FNIC_TRACE(fnic_wq_copy_cleanup_handler,
+ sc->device->host->host_no, id, sc,
+ jiffies_to_msecs(jiffies - start_time),
+ 0, ((u64)sc->cmnd[0] << 32 |
+ (u64)sc->cmnd[2] << 24 | (u64)sc->cmnd[3] << 16 |
+ (u64)sc->cmnd[4] << 8 | sc->cmnd[5]),
+ fnic_flags_and_state(sc));
+
+ scsi_done(sc);
}
static inline int fnic_queue_abort_io_req(struct fnic *fnic, int tag,
u32 task_req, u8 *fc_lun,
- struct fnic_io_req *io_req)
+ struct fnic_io_req *io_req,
+ unsigned int hwq)
{
- struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ struct vnic_wq_copy *wq = &fnic->hw_copy_wq[hwq];
+ struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
unsigned long flags;
+ struct fnic_tport_s *tport = io_req->tport;
- spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (unlikely(fnic_chk_state_flags_locked(fnic,
+ FNIC_FLAGS_IO_BLOCKED))) {
+ atomic_dec(&fnic->in_flight);
+ atomic_dec(&tport->in_flight);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return 1;
+ } else
+ atomic_inc(&fnic->in_flight);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
- free_wq_copy_descs(fnic, wq);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[hwq])
+ free_wq_copy_descs(fnic, wq, hwq);
if (!vnic_wq_copy_desc_avail(wq)) {
- spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ atomic_dec(&fnic->in_flight);
+ atomic_dec(&tport->in_flight);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "fnic_queue_abort_io_req: failure: no descriptors\n");
+ atomic64_inc(&misc_stats->abts_cpwq_alloc_failures);
return 1;
}
fnic_queue_wq_copy_desc_itmf(wq, tag | FNIC_TAG_ABORT,
0, task_req, tag, fc_lun, io_req->port_id,
fnic->config.ra_tov, fnic->config.ed_tov);
- spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
+ atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+ if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) >
+ atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs))
+ atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs,
+ atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs));
+
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ atomic_dec(&fnic->in_flight);
+
return 0;
}
-void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
+struct fnic_rport_abort_io_iter_data {
+ struct fnic *fnic;
+ u32 port_id;
+ int term_cnt;
+};
+
+static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data)
{
- int tag;
+ struct request *const rq = scsi_cmd_to_rq(sc);
+ struct fnic_rport_abort_io_iter_data *iter_data = data;
+ struct fnic *fnic = iter_data->fnic;
+ int abt_tag = 0;
struct fnic_io_req *io_req;
- spinlock_t *io_lock;
- unsigned long flags;
- struct scsi_cmnd *sc;
+ struct reset_stats *reset_stats = &fnic->fnic_stats.reset_stats;
+ struct terminate_stats *term_stats = &fnic->fnic_stats.term_stats;
struct scsi_lun fc_lun;
enum fnic_ioreq_state old_ioreq_state;
+ uint16_t hwq = 0;
+ unsigned long flags;
- FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
- "fnic_rport_reset_exch called portid 0x%06x\n",
- port_id);
-
- if (fnic->in_remove)
- return;
-
- for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
- sc = scsi_host_find_tag(fnic->lport->host, tag);
- if (!sc)
- continue;
+ abt_tag = blk_mq_unique_tag(rq);
+ hwq = blk_mq_unique_tag_to_hwq(abt_tag);
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
+ if (!sc) {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "sc is NULL abt_tag: 0x%x hwq: %d\n", abt_tag, hwq);
+ return true;
+ }
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
+ if (!io_req || io_req->port_id != iter_data->port_id) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ }
- if (!io_req || io_req->port_id != port_id) {
- spin_unlock_irqrestore(io_lock, flags);
- continue;
- }
+ if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET) &&
+ !(fnic_priv(sc)->flags & FNIC_DEV_RST_ISSUED)) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d abt_tag: 0x%x flags: 0x%x Device reset is not pending\n",
+ hwq, abt_tag, fnic_priv(sc)->flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ }
- /*
- * Found IO that is still pending with firmware and
- * belongs to rport that went away
- */
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
- spin_unlock_irqrestore(io_lock, flags);
- continue;
- }
- old_ioreq_state = CMD_STATE(sc);
- CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
- CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+ /*
+ * Found IO that is still pending with firmware and
+ * belongs to rport that went away
+ */
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ }
- BUG_ON(io_req->abts_done);
+ if (io_req->abts_done) {
+ shost_printk(KERN_ERR, fnic->host,
+ "fnic_rport_exch_reset: io_req->abts_done is set state is %s\n",
+ fnic_ioreq_state_to_str(fnic_priv(sc)->state));
+ }
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "fnic_rport_reset_exch: Issuing abts\n");
+ if (!(fnic_priv(sc)->flags & FNIC_IO_ISSUED)) {
+ shost_printk(KERN_ERR, fnic->host,
+ "rport_exch_reset IO not yet issued %p abt_tag 0x%x",
+ sc, abt_tag);
+ shost_printk(KERN_ERR, fnic->host,
+ "flags %x state %d\n", fnic_priv(sc)->flags,
+ fnic_priv(sc)->state);
+ }
+ old_ioreq_state = fnic_priv(sc)->state;
+ fnic_priv(sc)->state = FNIC_IOREQ_ABTS_PENDING;
+ fnic_priv(sc)->abts_status = FCPIO_INVALID_CODE;
+
+ if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET) {
+ atomic64_inc(&reset_stats->device_reset_terminates);
+ abt_tag |= FNIC_TAG_DEV_RST;
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "dev reset sc 0x%p\n", sc);
+ }
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "fnic_rport_exch_reset: dev rst sc 0x%p\n", sc);
+ WARN_ON_ONCE(io_req->abts_done);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "fnic_rport_reset_exch: Issuing abts\n");
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
- /* Now queue the abort command to firmware */
- int_to_scsilun(sc->device->lun, &fc_lun);
+ /* Queue the abort command to firmware */
+ int_to_scsilun(sc->device->lun, &fc_lun);
- if (fnic_queue_abort_io_req(fnic, tag,
- FCPIO_ITMF_ABT_TASK_TERM,
- fc_lun.scsi_lun, io_req)) {
- /*
- * Revert the cmd state back to old state, if
- * it hasn't changed in between. This cmd will get
- * aborted later by scsi_eh, or cleaned up during
- * lun reset
- */
- io_lock = fnic_io_lock_hash(fnic, sc);
-
- spin_lock_irqsave(io_lock, flags);
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING)
- CMD_STATE(sc) = old_ioreq_state;
- spin_unlock_irqrestore(io_lock, flags);
- }
+ if (fnic_queue_abort_io_req(fnic, abt_tag,
+ FCPIO_ITMF_ABT_TASK_TERM,
+ fc_lun.scsi_lun, io_req, hwq)) {
+ /*
+ * Revert the cmd state back to old state, if
+ * it hasn't changed in between. This cmd will get
+ * aborted later by scsi_eh, or cleaned up during
+ * lun reset
+ */
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d abt_tag: 0x%x flags: 0x%x Queuing abort failed\n",
+ hwq, abt_tag, fnic_priv(sc)->flags);
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING)
+ fnic_priv(sc)->state = old_ioreq_state;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ } else {
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET)
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_TERM_ISSUED;
+ else
+ fnic_priv(sc)->flags |= FNIC_IO_INTERNAL_TERM_ISSUED;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ atomic64_inc(&term_stats->terminates);
+ iter_data->term_cnt++;
}
+ return true;
}
-void fnic_terminate_rport_io(struct fc_rport *rport)
+void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
{
- int tag;
- struct fnic_io_req *io_req;
- spinlock_t *io_lock;
+ unsigned int io_count = 0;
unsigned long flags;
- struct scsi_cmnd *sc;
- struct scsi_lun fc_lun;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
- struct fnic *fnic = lport_priv(lport);
- struct fc_rport *cmd_rport;
- enum fnic_ioreq_state old_ioreq_state;
+ struct terminate_stats *term_stats = &fnic->fnic_stats.term_stats;
+ struct fnic_rport_abort_io_iter_data iter_data = {
+ .fnic = fnic,
+ .port_id = port_id,
+ .term_cnt = 0,
+ };
- FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host, "fnic_terminate_rport_io called"
- " wwpn 0x%llx, wwnn0x%llx, portid 0x%06x\n",
- rport->port_name, rport->node_name,
- rport->port_id);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "fnic rport exchange reset for tport: 0x%06x\n",
+ port_id);
if (fnic->in_remove)
return;
- for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
- sc = scsi_host_find_tag(fnic->lport->host, tag);
- if (!sc)
- continue;
+ io_count = fnic_count_ioreqs(fnic, port_id);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Starting terminates: rport:0x%x portid-io-count: %d active-io-count: %lld\n",
+ port_id, io_count,
+ atomic64_read(&fnic->fnic_stats.io_stats.active_ios));
- cmd_rport = starget_to_rport(scsi_target(sc->device));
- if (rport != cmd_rport)
- continue;
-
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ /* Bump in_flight counter to hold off fnic_fw_reset_handler. */
+ atomic_inc(&fnic->in_flight);
+ if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) {
+ atomic_dec(&fnic->in_flight);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ scsi_host_busy_iter(fnic->host, fnic_rport_abort_io_iter,
+ &iter_data);
- if (!io_req || rport != cmd_rport) {
- spin_unlock_irqrestore(io_lock, flags);
- continue;
- }
+ if (iter_data.term_cnt > atomic64_read(&term_stats->max_terminates))
+ atomic64_set(&term_stats->max_terminates, iter_data.term_cnt);
- /*
- * Found IO that is still pending with firmware and
- * belongs to rport that went away
- */
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
- spin_unlock_irqrestore(io_lock, flags);
- continue;
- }
- old_ioreq_state = CMD_STATE(sc);
- CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
- CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+ atomic_dec(&fnic->in_flight);
- BUG_ON(io_req->abts_done);
+ while ((io_count = fnic_count_ioreqs(fnic, port_id)))
+ schedule_timeout(msecs_to_jiffies(1000));
- FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
- "fnic_terminate_rport_io: Issuing abts\n");
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "rport: 0x%x remaining portid-io-count: %d ",
+ port_id, io_count);
+}
- spin_unlock_irqrestore(io_lock, flags);
+void fnic_terminate_rport_io(struct fc_rport *rport)
+{
+ struct fnic_tport_s *tport;
+ struct rport_dd_data_s *rdd_data;
+ struct fnic_iport_s *iport = NULL;
+ struct fnic *fnic = NULL;
- /* Now queue the abort command to firmware */
- int_to_scsilun(sc->device->lun, &fc_lun);
+ if (!rport) {
+ pr_err("rport is NULL\n");
+ return;
+ }
- if (fnic_queue_abort_io_req(fnic, tag,
- FCPIO_ITMF_ABT_TASK_TERM,
- fc_lun.scsi_lun, io_req)) {
- /*
- * Revert the cmd state back to old state, if
- * it hasn't changed in between. This cmd will get
- * aborted later by scsi_eh, or cleaned up during
- * lun reset
- */
- io_lock = fnic_io_lock_hash(fnic, sc);
-
- spin_lock_irqsave(io_lock, flags);
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING)
- CMD_STATE(sc) = old_ioreq_state;
- spin_unlock_irqrestore(io_lock, flags);
+ rdd_data = rport->dd_data;
+ if (rdd_data) {
+ tport = rdd_data->tport;
+ if (!tport) {
+ pr_err(
+ "term rport io called after tport is deleted. Returning 0x%8x\n",
+ rport->port_id);
+ } else {
+ pr_err(
+ "term rport io called after tport is set 0x%8x\n",
+ rport->port_id);
+ pr_err(
+ "tport maybe rediscovered\n");
+
+ iport = (struct fnic_iport_s *) tport->iport;
+ fnic = iport->fnic;
+ fnic_rport_exch_reset(fnic, rport->port_id);
}
}
+}
+
+/*
+ * FCP-SCSI specific handling for module unload
+ *
+ */
+void fnic_scsi_unload(struct fnic *fnic)
+{
+ unsigned long flags;
+ /*
+ * Mark state so that the workqueue thread stops forwarding
+ * received frames and link events to the local port. ISR and
+ * other threads that can queue work items will also stop
+ * creating work items on the fnic workqueue
+ */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->iport.state = FNIC_IPORT_STATE_LINK_WAIT;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ if (fdls_get_state(&fnic->iport.fabric) != FDLS_STATE_INIT)
+ fnic_scsi_fcpio_reset(fnic);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->in_remove = 1;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ fnic_flush_tport_event_list(fnic);
+ fnic_delete_fcp_tports(fnic);
+}
+
+void fnic_scsi_unload_cleanup(struct fnic *fnic)
+{
+ int hwq = 0;
+
+ fc_remove_host(fnic->host);
+ scsi_remove_host(fnic->host);
+ for (hwq = 0; hwq < fnic->wq_copy_count; hwq++)
+ kfree(fnic->sw_copy_wq[hwq].io_req_table);
}
/*
@@ -1230,34 +2001,91 @@ void fnic_terminate_rport_io(struct fc_rport *rport)
*/
int fnic_abort_cmd(struct scsi_cmnd *sc)
{
- struct fc_lport *lp;
+ struct request *const rq = scsi_cmd_to_rq(sc);
+ struct fnic_iport_s *iport;
+ struct fnic_tport_s *tport;
struct fnic *fnic;
- struct fnic_io_req *io_req;
+ struct fnic_io_req *io_req = NULL;
struct fc_rport *rport;
- spinlock_t *io_lock;
+ struct rport_dd_data_s *rdd_data;
unsigned long flags;
+ unsigned long start_time = 0;
int ret = SUCCESS;
- u32 task_req;
+ u32 task_req = 0;
struct scsi_lun fc_lun;
+ struct fnic_stats *fnic_stats;
+ struct abort_stats *abts_stats;
+ struct terminate_stats *term_stats;
+ enum fnic_ioreq_state old_ioreq_state;
+ int mqtag;
+ unsigned long abt_issued_time;
+ uint16_t hwq = 0;
+
DECLARE_COMPLETION_ONSTACK(tm_done);
/* Wait for rport to unblock */
fc_block_scsi_eh(sc);
/* Get local-port, check ready and link up */
- lp = shost_priv(sc->device->host);
+ fnic = *((struct fnic **) shost_priv(sc->device->host));
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ iport = &fnic->iport;
+
+ fnic_stats = &fnic->fnic_stats;
+ abts_stats = &fnic->fnic_stats.abts_stats;
+ term_stats = &fnic->fnic_stats.term_stats;
- fnic = lport_priv(lp);
rport = starget_to_rport(scsi_target(sc->device));
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Abort Cmd called FCID 0x%x, LUN 0x%x TAG %d\n",
- rport->port_id, sc->device->lun, sc->request->tag);
+ mqtag = blk_mq_unique_tag(rq);
+ hwq = blk_mq_unique_tag_to_hwq(mqtag);
+
+ fnic_priv(sc)->flags = FNIC_NO_FLAGS;
+
+ rdd_data = rport->dd_data;
+ tport = rdd_data->tport;
+
+ if (!tport) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Abort cmd called after tport delete! rport fcid: 0x%x",
+ rport->port_id);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "lun: %llu hwq: 0x%x mqtag: 0x%x Op: 0x%x flags: 0x%x\n",
+ sc->device->lun, hwq, mqtag,
+ sc->cmnd[0], fnic_priv(sc)->flags);
+ ret = FAILED;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ goto fnic_abort_cmd_end;
+ }
+
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Abort cmd called rport fcid: 0x%x lun: %llu hwq: 0x%x mqtag: 0x%x",
+ rport->port_id, sc->device->lun, hwq, mqtag);
- if (lp->state != LPORT_ST_READY || !(lp->link_up)) {
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Op: 0x%x flags: 0x%x\n",
+ sc->cmnd[0],
+ fnic_priv(sc)->flags);
+
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ atomic64_inc(&fnic_stats->misc_stats.iport_not_ready);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport NOT in READY state");
ret = FAILED;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
goto fnic_abort_cmd_end;
}
+ if ((tport->state != FDLS_TGT_STATE_READY) &&
+ (tport->state != FDLS_TGT_STATE_ADISC)) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "tport state: %d\n", tport->state);
+ ret = FAILED;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ goto fnic_abort_cmd_end;
+ }
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
/*
* Avoid a race between SCSI issuing the abort and the device
* completing the command.
@@ -1268,32 +2096,53 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
* happened, the completion wont actually complete the command
* and it will be considered as an aborted command
*
- * The CMD_SP will not be cleared except while holding io_req_lock.
+ * .io_req will not be cleared except while holding io_req_lock.
*/
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
+ ret = FAILED;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
goto fnic_abort_cmd_end;
}
io_req->abts_done = &tm_done;
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
- spin_unlock_irqrestore(io_lock, flags);
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
goto wait_pending;
}
+
+ abt_issued_time = jiffies_to_msecs(jiffies) - jiffies_to_msecs(io_req->start_time);
+ if (abt_issued_time <= 6000)
+ atomic64_inc(&abts_stats->abort_issued_btw_0_to_6_sec);
+ else if (abt_issued_time > 6000 && abt_issued_time <= 20000)
+ atomic64_inc(&abts_stats->abort_issued_btw_6_to_20_sec);
+ else if (abt_issued_time > 20000 && abt_issued_time <= 30000)
+ atomic64_inc(&abts_stats->abort_issued_btw_20_to_30_sec);
+ else if (abt_issued_time > 30000 && abt_issued_time <= 40000)
+ atomic64_inc(&abts_stats->abort_issued_btw_30_to_40_sec);
+ else if (abt_issued_time > 40000 && abt_issued_time <= 50000)
+ atomic64_inc(&abts_stats->abort_issued_btw_40_to_50_sec);
+ else if (abt_issued_time > 50000 && abt_issued_time <= 60000)
+ atomic64_inc(&abts_stats->abort_issued_btw_50_to_60_sec);
+ else
+ atomic64_inc(&abts_stats->abort_issued_greater_than_60_sec);
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "CDB Opcode: 0x%02x Abort issued time: %lu msec\n",
+ sc->cmnd[0], abt_issued_time);
/*
* Command is still pending, need to abort it
* If the firmware completes the command after this point,
* the completion wont be done till mid-layer, since abort
* has already started.
*/
- CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
- CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+ old_ioreq_state = fnic_priv(sc)->state;
+ fnic_priv(sc)->state = FNIC_IOREQ_ABTS_PENDING;
+ fnic_priv(sc)->abts_status = FCPIO_INVALID_CODE;
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
/*
* Check readiness of the remote port. If the path to remote
@@ -1302,22 +2151,33 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
*/
if (fc_remote_port_chkready(rport) == 0)
task_req = FCPIO_ITMF_ABT_TASK;
- else
+ else {
+ atomic64_inc(&fnic_stats->misc_stats.tport_not_ready);
task_req = FCPIO_ITMF_ABT_TASK_TERM;
+ }
/* Now queue the abort command to firmware */
int_to_scsilun(sc->device->lun, &fc_lun);
- if (fnic_queue_abort_io_req(fnic, sc->request->tag, task_req,
- fc_lun.scsi_lun, io_req)) {
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ if (fnic_queue_abort_io_req(fnic, mqtag, task_req, fc_lun.scsi_lun,
+ io_req, hwq)) {
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING)
+ fnic_priv(sc)->state = old_ioreq_state;
+ io_req = fnic_priv(sc)->io_req;
if (io_req)
io_req->abts_done = NULL;
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
ret = FAILED;
goto fnic_abort_cmd_end;
}
+ if (task_req == FCPIO_ITMF_ABT_TASK) {
+ fnic_priv(sc)->flags |= FNIC_IO_ABTS_ISSUED;
+ atomic64_inc(&fnic_stats->abts_stats.aborts);
+ } else {
+ fnic_priv(sc)->flags |= FNIC_IO_TERM_ISSUED;
+ atomic64_inc(&fnic_stats->term_stats.terminates);
+ }
/*
* We queued an abort IO, wait for its completion.
@@ -1331,40 +2191,85 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
fnic->config.ed_tov));
/* Check the abort status */
- spin_lock_irqsave(io_lock, flags);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ io_req = fnic_priv(sc)->io_req;
if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
+ atomic64_inc(&fnic_stats->io_stats.ioreq_null);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_REQ_NULL;
ret = FAILED;
goto fnic_abort_cmd_end;
}
io_req->abts_done = NULL;
/* fw did not complete abort, timed out */
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
- spin_unlock_irqrestore(io_lock, flags);
+ if (fnic_priv(sc)->abts_status == FCPIO_INVALID_CODE) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ if (task_req == FCPIO_ITMF_ABT_TASK) {
+ atomic64_inc(&abts_stats->abort_drv_timeouts);
+ } else {
+ atomic64_inc(&term_stats->terminate_drv_timeouts);
+ }
+ fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_TIMED_OUT;
ret = FAILED;
goto fnic_abort_cmd_end;
}
+ /* IO out of order */
+
+ if (!(fnic_priv(sc)->flags & (FNIC_IO_ABORTED | FNIC_IO_DONE))) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Issuing host reset due to out of order IO\n");
+
+ ret = FAILED;
+ goto fnic_abort_cmd_end;
+ }
+
+ fnic_priv(sc)->state = FNIC_IOREQ_ABTS_COMPLETE;
+
+ start_time = io_req->start_time;
/*
* firmware completed the abort, check the status,
- * free the io_req irrespective of failure or success
+ * free the io_req if successful. If abort fails,
+ * Device reset will clean the I/O.
*/
- if (CMD_ABTS_STATUS(sc) != FCPIO_SUCCESS)
+ if (fnic_priv(sc)->abts_status == FCPIO_SUCCESS ||
+ (fnic_priv(sc)->abts_status == FCPIO_ABORTED)) {
+ fnic_priv(sc)->io_req = NULL;
+ io_req->sc = NULL;
+ } else {
ret = FAILED;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ goto fnic_abort_cmd_end;
+ }
- CMD_SP(sc) = NULL;
-
- spin_unlock_irqrestore(io_lock, flags);
+ fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(mqtag)] = NULL;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
fnic_release_ioreq_buf(fnic, io_req, sc);
mempool_free(io_req, fnic->io_req_pool);
+ /* Call SCSI completion function to complete the IO */
+ sc->result = DID_ABORT << 16;
+ scsi_done(sc);
+ atomic64_dec(&fnic_stats->io_stats.active_ios);
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
+
fnic_abort_cmd_end:
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Returning from abort cmd %s\n",
+ FNIC_TRACE(fnic_abort_cmd, sc->device->host->host_no, mqtag, sc,
+ jiffies_to_msecs(jiffies - start_time),
+ 0, ((u64)sc->cmnd[0] << 32 |
+ (u64)sc->cmnd[2] << 24 | (u64)sc->cmnd[3] << 16 |
+ (u64)sc->cmnd[4] << 8 | sc->cmnd[5]),
+ fnic_flags_and_state(sc));
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Returning from abort cmd type %x %s\n", task_req,
(ret == SUCCESS) ?
"SUCCESS" : "FAILED");
return ret;
@@ -1374,17 +2279,39 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic,
struct scsi_cmnd *sc,
struct fnic_io_req *io_req)
{
- struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ struct vnic_wq_copy *wq;
+ struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
struct scsi_lun fc_lun;
int ret = 0;
- unsigned long intr_flags;
+ unsigned long flags;
+ uint16_t hwq = 0;
+ uint32_t tag = 0;
+ struct fnic_tport_s *tport = io_req->tport;
- spin_lock_irqsave(&fnic->wq_copy_lock[0], intr_flags);
+ tag = io_req->tag;
+ hwq = blk_mq_unique_tag_to_hwq(tag);
+ wq = &fnic->hw_copy_wq[hwq];
- if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
- free_wq_copy_descs(fnic, wq);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (unlikely(fnic_chk_state_flags_locked(fnic,
+ FNIC_FLAGS_IO_BLOCKED))) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return FAILED;
+ } else {
+ atomic_inc(&fnic->in_flight);
+ atomic_inc(&tport->in_flight);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[hwq])
+ free_wq_copy_descs(fnic, wq, hwq);
if (!vnic_wq_copy_desc_avail(wq)) {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "queue_dr_io_req failure - no descriptors\n");
+ atomic64_inc(&misc_stats->devrst_cpwq_alloc_failures);
ret = -EAGAIN;
goto lr_io_req_end;
}
@@ -1392,113 +2319,213 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic,
/* fill in the lun info */
int_to_scsilun(sc->device->lun, &fc_lun);
- fnic_queue_wq_copy_desc_itmf(wq, sc->request->tag | FNIC_TAG_DEV_RST,
+ tag |= FNIC_TAG_DEV_RST;
+ fnic_queue_wq_copy_desc_itmf(wq, tag,
0, FCPIO_ITMF_LUN_RESET, SCSI_NO_TAG,
fc_lun.scsi_lun, io_req->port_id,
fnic->config.ra_tov, fnic->config.ed_tov);
+ atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+ if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) >
+ atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs))
+ atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs,
+ atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs));
+
lr_io_req_end:
- spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ atomic_dec(&fnic->in_flight);
+ atomic_dec(&tport->in_flight);
return ret;
}
-/*
- * Clean up any pending aborts on the lun
- * For each outstanding IO on this lun, whose abort is not completed by fw,
- * issue a local abort. Wait for abort to complete. Return 0 if all commands
- * successfully aborted, 1 otherwise
- */
-static int fnic_clean_pending_aborts(struct fnic *fnic,
- struct scsi_cmnd *lr_sc)
+struct fnic_pending_aborts_iter_data {
+ struct fnic *fnic;
+ struct scsi_cmnd *lr_sc;
+ struct scsi_device *lun_dev;
+ int ret;
+};
+
+static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data)
{
- int tag;
+ struct request *const rq = scsi_cmd_to_rq(sc);
+ struct fnic_pending_aborts_iter_data *iter_data = data;
+ struct fnic *fnic = iter_data->fnic;
+ struct scsi_device *lun_dev = iter_data->lun_dev;
+ unsigned long abt_tag = 0;
+ uint16_t hwq = 0;
struct fnic_io_req *io_req;
- spinlock_t *io_lock;
unsigned long flags;
- int ret = 0;
- struct scsi_cmnd *sc;
struct scsi_lun fc_lun;
- struct scsi_device *lun_dev = lr_sc->device;
DECLARE_COMPLETION_ONSTACK(tm_done);
+ enum fnic_ioreq_state old_ioreq_state;
- for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
- sc = scsi_host_find_tag(fnic->lport->host, tag);
- /*
- * ignore this lun reset cmd or cmds that do not belong to
- * this lun
- */
- if (!sc || sc == lr_sc || sc->device != lun_dev)
- continue;
+ if (sc == iter_data->lr_sc || sc->device != lun_dev)
+ return true;
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
+ abt_tag = blk_mq_unique_tag(rq);
+ hwq = blk_mq_unique_tag_to_hwq(abt_tag);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
+ if (!io_req) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ }
- if (!io_req || sc->device != lun_dev) {
- spin_unlock_irqrestore(io_lock, flags);
- continue;
- }
+ /*
+ * Found IO that is still pending with firmware and
+ * belongs to the LUN that we are resetting
+ */
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Found IO in %s on lun\n",
+ fnic_ioreq_state_to_str(fnic_priv(sc)->state));
- /*
- * Found IO that is still pending with firmware and
- * belongs to the LUN that we are resetting
- */
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Found IO in %s on lun\n",
- fnic_ioreq_state_to_str(CMD_STATE(sc)));
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ }
+ if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET) &&
+ (!(fnic_priv(sc)->flags & FNIC_DEV_RST_ISSUED))) {
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "dev rst not pending sc 0x%p\n", sc);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
+ }
- BUG_ON(CMD_STATE(sc) != FNIC_IOREQ_ABTS_PENDING);
+ if (io_req->abts_done)
+ shost_printk(KERN_ERR, fnic->host,
+ "%s: io_req->abts_done is set state is %s\n",
+ __func__, fnic_ioreq_state_to_str(fnic_priv(sc)->state));
+ old_ioreq_state = fnic_priv(sc)->state;
+ /*
+ * Any pending IO issued prior to reset is expected to be
+ * in abts pending state, if not we need to set
+ * FNIC_IOREQ_ABTS_PENDING to indicate the IO is abort pending.
+ * When IO is completed, the IO will be handed over and
+ * handled in this function.
+ */
+ fnic_priv(sc)->state = FNIC_IOREQ_ABTS_PENDING;
- CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
- io_req->abts_done = &tm_done;
- spin_unlock_irqrestore(io_lock, flags);
+ BUG_ON(io_req->abts_done);
- /* Now queue the abort command to firmware */
- int_to_scsilun(sc->device->lun, &fc_lun);
+ if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET) {
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "dev rst sc 0x%p\n", sc);
+ }
- if (fnic_queue_abort_io_req(fnic, tag,
- FCPIO_ITMF_ABT_TASK_TERM,
- fc_lun.scsi_lun, io_req)) {
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
- if (io_req)
- io_req->abts_done = NULL;
- spin_unlock_irqrestore(io_lock, flags);
- ret = 1;
- goto clean_pending_aborts_end;
- }
+ fnic_priv(sc)->abts_status = FCPIO_INVALID_CODE;
+ io_req->abts_done = &tm_done;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
- wait_for_completion_timeout(&tm_done,
- msecs_to_jiffies
- (fnic->config.ed_tov));
+ /* Now queue the abort command to firmware */
+ int_to_scsilun(sc->device->lun, &fc_lun);
- /* Recheck cmd state to check if it is now aborted */
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
- if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
- ret = 1;
- goto clean_pending_aborts_end;
- }
+ if (fnic_queue_abort_io_req(fnic, abt_tag,
+ FCPIO_ITMF_ABT_TASK_TERM,
+ fc_lun.scsi_lun, io_req, hwq)) {
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
+ if (io_req)
+ io_req->abts_done = NULL;
+ if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING)
+ fnic_priv(sc)->state = old_ioreq_state;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ iter_data->ret = FAILED;
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "hwq: %d abt_tag: 0x%lx Abort could not be queued\n",
+ hwq, abt_tag);
+ return false;
+ } else {
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET)
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_TERM_ISSUED;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ }
+ fnic_priv(sc)->flags |= FNIC_IO_INTERNAL_TERM_ISSUED;
- io_req->abts_done = NULL;
+ wait_for_completion_timeout(&tm_done, msecs_to_jiffies
+ (fnic->config.ed_tov));
- /* if abort is still pending with fw, fail */
- if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
- spin_unlock_irqrestore(io_lock, flags);
- ret = 1;
- goto clean_pending_aborts_end;
- }
- CMD_SP(sc) = NULL;
- spin_unlock_irqrestore(io_lock, flags);
+ /* Recheck cmd state to check if it is now aborted */
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
+ if (!io_req) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_REQ_NULL;
+ return true;
+ }
+
+ io_req->abts_done = NULL;
+
+ /* if abort is still pending with fw, fail */
+ if (fnic_priv(sc)->abts_status == FCPIO_INVALID_CODE) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_DONE;
+ iter_data->ret = FAILED;
+ return false;
+ }
+ fnic_priv(sc)->state = FNIC_IOREQ_ABTS_COMPLETE;
+ /* original sc used for lr is handled by dev reset code */
+ if (sc != iter_data->lr_sc) {
+ fnic_priv(sc)->io_req = NULL;
+ fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(abt_tag)] = NULL;
+ }
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+
+ /* original sc used for lr is handled by dev reset code */
+ if (sc != iter_data->lr_sc) {
fnic_release_ioreq_buf(fnic, io_req, sc);
mempool_free(io_req, fnic->io_req_pool);
}
+ /*
+ * Any IO is returned during reset, it needs to call scsi_done
+ * to return the scsi_cmnd to upper layer.
+ */
+ /* Set result to let upper SCSI layer retry */
+ sc->result = DID_RESET << 16;
+ scsi_done(sc);
+
+ return true;
+}
+
+/*
+ * Clean up any pending aborts on the lun
+ * For each outstanding IO on this lun, whose abort is not completed by fw,
+ * issue a local abort. Wait for abort to complete. Return 0 if all commands
+ * successfully aborted, 1 otherwise
+ */
+static int fnic_clean_pending_aborts(struct fnic *fnic,
+ struct scsi_cmnd *lr_sc,
+ bool new_sc)
+
+{
+ int ret = 0;
+ struct fnic_pending_aborts_iter_data iter_data = {
+ .fnic = fnic,
+ .lun_dev = lr_sc->device,
+ .ret = SUCCESS,
+ };
+
+ iter_data.lr_sc = lr_sc;
+
+ scsi_host_busy_iter(fnic->host,
+ fnic_pending_aborts_iter, &iter_data);
+ if (iter_data.ret == FAILED) {
+ ret = iter_data.ret;
+ goto clean_pending_aborts_end;
+ }
+ schedule_timeout(msecs_to_jiffies(2 * fnic->config.ed_tov));
+
+ /* walk again to check, if IOs are still pending in fw */
+ if (fnic_is_abts_pending(fnic, lr_sc))
+ ret = 1;
+
clean_pending_aborts_end:
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "exit status: %d\n", ret);
return ret;
}
@@ -1509,39 +2536,102 @@ clean_pending_aborts_end:
*/
int fnic_device_reset(struct scsi_cmnd *sc)
{
- struct fc_lport *lp;
+ struct request *rq = scsi_cmd_to_rq(sc);
struct fnic *fnic;
- struct fnic_io_req *io_req;
+ struct fnic_io_req *io_req = NULL;
struct fc_rport *rport;
int status;
+ int count = 0;
int ret = FAILED;
- spinlock_t *io_lock;
unsigned long flags;
+ unsigned long start_time = 0;
+ struct scsi_lun fc_lun;
+ struct fnic_stats *fnic_stats;
+ struct reset_stats *reset_stats;
+ int mqtag = rq->tag;
DECLARE_COMPLETION_ONSTACK(tm_done);
+ bool new_sc = 0;
+ uint16_t hwq = 0;
+ struct fnic_iport_s *iport = NULL;
+ struct rport_dd_data_s *rdd_data;
+ struct fnic_tport_s *tport;
+ u32 old_soft_reset_count;
+ u32 old_link_down_cnt;
+ int exit_dr = 0;
/* Wait for rport to unblock */
fc_block_scsi_eh(sc);
/* Get local-port, check ready and link up */
- lp = shost_priv(sc->device->host);
+ fnic = *((struct fnic **) shost_priv(sc->device->host));
+ iport = &fnic->iport;
+
+ fnic_stats = &fnic->fnic_stats;
+ reset_stats = &fnic_stats->reset_stats;
- fnic = lport_priv(lp);
+ atomic64_inc(&reset_stats->device_resets);
rport = starget_to_rport(scsi_target(sc->device));
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Device reset called FCID 0x%x, LUN 0x%x\n",
- rport->port_id, sc->device->lun);
- if (lp->state != LPORT_ST_READY || !(lp->link_up))
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "fcid: 0x%x lun: %llu hwq: %d mqtag: 0x%x flags: 0x%x Device reset\n",
+ rport->port_id, sc->device->lun, hwq, mqtag,
+ fnic_priv(sc)->flags);
+
+ rdd_data = rport->dd_data;
+ tport = rdd_data->tport;
+ if (!tport) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Dev rst called after tport delete! rport fcid: 0x%x lun: %llu\n",
+ rport->port_id, sc->device->lun);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ goto fnic_device_reset_end;
+ }
+
+ if (iport->state != FNIC_IPORT_STATE_READY) {
+ atomic64_inc(&fnic_stats->misc_stats.iport_not_ready);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "iport NOT in READY state");
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
goto fnic_device_reset_end;
+ }
+
+ if ((tport->state != FDLS_TGT_STATE_READY) &&
+ (tport->state != FDLS_TGT_STATE_ADISC)) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "tport state: %d\n", tport->state);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ goto fnic_device_reset_end;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
/* Check if remote port up */
- if (fc_remote_port_chkready(rport))
+ if (fc_remote_port_chkready(rport)) {
+ atomic64_inc(&fnic_stats->misc_stats.tport_not_ready);
goto fnic_device_reset_end;
+ }
+
+ fnic_priv(sc)->flags = FNIC_DEVICE_RESET;
+
+ if (unlikely(mqtag < 0)) {
+ /*
+ * For device reset issued through sg3utils, we let
+ * only one LUN_RESET to go through and use a special
+ * tag equal to max_tag_id so that we don't have to allocate
+ * or free it. It won't interact with tags
+ * allocated by mid layer.
+ */
+ mutex_lock(&fnic->sgreset_mutex);
+ mqtag = fnic->fnic_max_tag_id;
+ new_sc = 1;
+ } else {
+ mqtag = blk_mq_unique_tag(rq);
+ hwq = blk_mq_unique_tag_to_hwq(mqtag);
+ }
- io_lock = fnic_io_lock_hash(fnic, sc);
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
/*
* If there is a io_req attached to this command, then use it,
@@ -1550,32 +2640,49 @@ int fnic_device_reset(struct scsi_cmnd *sc)
if (!io_req) {
io_req = mempool_alloc(fnic->io_req_pool, GFP_ATOMIC);
if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
goto fnic_device_reset_end;
}
memset(io_req, 0, sizeof(*io_req));
io_req->port_id = rport->port_id;
- CMD_SP(sc) = (char *)io_req;
+ io_req->tag = mqtag;
+ fnic_priv(sc)->io_req = io_req;
+ io_req->tport = tport;
+ io_req->sc = sc;
+
+ if (fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(mqtag)] != NULL)
+ WARN(1, "fnic<%d>: %s: tag 0x%x already exists\n",
+ fnic->fnic_num, __func__, blk_mq_unique_tag_to_tag(mqtag));
+
+ fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(mqtag)] =
+ io_req;
}
io_req->dr_done = &tm_done;
- CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING;
- CMD_LR_STATUS(sc) = FCPIO_INVALID_CODE;
- spin_unlock_irqrestore(io_lock, flags);
+ fnic_priv(sc)->state = FNIC_IOREQ_CMD_PENDING;
+ fnic_priv(sc)->lr_status = FCPIO_INVALID_CODE;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "TAG %d\n",
- sc->request->tag);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, "TAG %x\n", mqtag);
/*
* issue the device reset, if enqueue failed, clean up the ioreq
* and break assoc with scsi cmd
*/
if (fnic_queue_dr_io_req(fnic, sc, io_req)) {
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
if (io_req)
io_req->dr_done = NULL;
goto fnic_device_reset_clean;
}
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_ISSUED;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ old_link_down_cnt = iport->fnic->link_down_cnt;
+ old_soft_reset_count = fnic->soft_reset_count;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
/*
* Wait on the local completion for LUN reset. The io_req may be
@@ -1584,34 +2691,65 @@ int fnic_device_reset(struct scsi_cmnd *sc)
wait_for_completion_timeout(&tm_done,
msecs_to_jiffies(FNIC_LUN_RESET_TIMEOUT));
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ /*
+ * Wake up can be due to the following reasons:
+ * 1) The device reset completed from target.
+ * 2) Device reset timed out.
+ * 3) A link-down/host_reset may have happened in between.
+ * 4) The device reset was aborted and io_req->dr_done was called.
+ */
+
+ exit_dr = 0;
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if ((old_link_down_cnt != fnic->link_down_cnt) ||
+ (fnic->reset_in_progress) ||
+ (fnic->soft_reset_count != old_soft_reset_count) ||
+ (iport->state != FNIC_IPORT_STATE_READY))
+ exit_dr = 1;
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
if (!io_req) {
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "io_req is null mqtag 0x%x sc 0x%p\n", mqtag, sc);
goto fnic_device_reset_end;
}
+
+ if (exit_dr) {
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Host reset called for fnic. Exit device reset\n");
+ io_req->dr_done = NULL;
+ goto fnic_device_reset_clean;
+ }
io_req->dr_done = NULL;
- status = CMD_LR_STATUS(sc);
- spin_unlock_irqrestore(io_lock, flags);
+ status = fnic_priv(sc)->lr_status;
/*
* If lun reset not completed, bail out with failed. io_req
* gets cleaned up during higher levels of EH
*/
if (status == FCPIO_INVALID_CODE) {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ atomic64_inc(&reset_stats->device_reset_timeouts);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"Device reset timed out\n");
- goto fnic_device_reset_end;
+ fnic_priv(sc)->flags |= FNIC_DEV_RST_TIMED_OUT;
+ int_to_scsilun(sc->device->lun, &fc_lun);
+ goto fnic_device_reset_clean;
+ } else {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
}
/* Completed, but not successful, clean up the io_req, return fail */
if (status != FCPIO_SUCCESS) {
- spin_lock_irqsave(io_lock, flags);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
+ fnic->host, fnic->fnic_num,
"Device reset completed - failed\n");
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ io_req = fnic_priv(sc)->io_req;
goto fnic_device_reset_clean;
}
@@ -1622,203 +2760,333 @@ int fnic_device_reset(struct scsi_cmnd *sc)
* the lun reset cmd. If all cmds get cleaned, the lun reset
* succeeds
*/
- if (fnic_clean_pending_aborts(fnic, sc)) {
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Device reset failed"
- " since could not abort all IOs\n");
+ if (fnic_clean_pending_aborts(fnic, sc, new_sc)) {
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+ "Device reset failed: Cannot abort all IOs\n");
goto fnic_device_reset_clean;
}
/* Clean lun reset command */
- spin_lock_irqsave(io_lock, flags);
- io_req = (struct fnic_io_req *)CMD_SP(sc);
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+ io_req = fnic_priv(sc)->io_req;
if (io_req)
/* Completed, and successful */
ret = SUCCESS;
fnic_device_reset_clean:
- if (io_req)
- CMD_SP(sc) = NULL;
+ if (io_req) {
+ fnic_priv(sc)->io_req = NULL;
+ io_req->sc = NULL;
+ fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(io_req->tag)] = NULL;
+ }
- spin_unlock_irqrestore(io_lock, flags);
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
if (io_req) {
+ start_time = io_req->start_time;
fnic_release_ioreq_buf(fnic, io_req, sc);
mempool_free(io_req, fnic->io_req_pool);
}
+ /*
+ * If link-event is seen while LUN reset is issued we need
+ * to complete the LUN reset here
+ */
+ if (!new_sc) {
+ sc->result = DID_RESET << 16;
+ scsi_done(sc);
+ }
+
fnic_device_reset_end:
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_TRACE(fnic_device_reset, sc->device->host->host_no, rq->tag, sc,
+ jiffies_to_msecs(jiffies - start_time),
+ 0, ((u64)sc->cmnd[0] << 32 |
+ (u64)sc->cmnd[2] << 24 | (u64)sc->cmnd[3] << 16 |
+ (u64)sc->cmnd[4] << 8 | sc->cmnd[5]),
+ fnic_flags_and_state(sc));
+
+ if (new_sc) {
+ fnic->sgreset_sc = NULL;
+ mutex_unlock(&fnic->sgreset_mutex);
+ }
+
+ while ((ret == SUCCESS) && fnic_count_lun_ioreqs(fnic, sc->device)) {
+ if (count >= 2) {
+ ret = FAILED;
+ break;
+ }
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "Cannot clean up all IOs for the LUN\n");
+ schedule_timeout(msecs_to_jiffies(1000));
+ count++;
+ }
+
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"Returning from device reset %s\n",
(ret == SUCCESS) ?
"SUCCESS" : "FAILED");
+
+ if (ret == FAILED)
+ atomic64_inc(&reset_stats->device_reset_failures);
+
return ret;
}
-/* Clean up all IOs, clean up libFC local port */
-int fnic_reset(struct Scsi_Host *shost)
+static void fnic_post_flogo_linkflap(struct fnic *fnic)
+{
+ unsigned long flags;
+
+ fnic_fdls_link_status_change(fnic, 0);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+ if (fnic->link_status) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ fnic_fdls_link_status_change(fnic, 1);
+ return;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+/* Logout from all the targets and simulate link flap */
+void fnic_reset(struct Scsi_Host *shost)
{
- struct fc_lport *lp;
struct fnic *fnic;
- int ret = SUCCESS;
+ struct reset_stats *reset_stats;
- lp = shost_priv(shost);
- fnic = lport_priv(lp);
+ fnic = *((struct fnic **) shost_priv(shost));
+ reset_stats = &fnic->fnic_stats.reset_stats;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "fnic_reset called\n");
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Issuing fnic reset\n");
- /*
- * Reset local port, this will clean up libFC exchanges,
- * reset remote port sessions, and if link is up, begin flogi
- */
- if (lp->tt.lport_reset(lp))
- ret = FAILED;
+ atomic64_inc(&reset_stats->fnic_resets);
+ fnic_post_flogo_linkflap(fnic);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Returning from fnic reset %s\n",
- (ret == SUCCESS) ?
- "SUCCESS" : "FAILED");
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Returning from fnic reset");
+ atomic64_inc(&reset_stats->fnic_reset_completions);
+}
+
+int fnic_issue_fc_host_lip(struct Scsi_Host *shost)
+{
+ int ret = 0;
+ struct fnic *fnic = *((struct fnic **) shost_priv(shost));
+
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FC host lip issued");
+
+ ret = fnic_host_reset(shost);
return ret;
}
-/*
- * SCSI Error handling calls driver's eh_host_reset if all prior
- * error handling levels return FAILED. If host reset completes
- * successfully, and if link is up, then Fabric login begins.
- *
- * Host Reset is the highest level of error recovery. If this fails, then
- * host is offlined by SCSI.
- *
- */
-int fnic_host_reset(struct scsi_cmnd *sc)
+int fnic_host_reset(struct Scsi_Host *shost)
{
- int ret;
+ int ret = SUCCESS;
unsigned long wait_host_tmo;
- struct Scsi_Host *shost = sc->device->host;
- struct fc_lport *lp = shost_priv(shost);
+ struct fnic *fnic = *((struct fnic **) shost_priv(shost));
+ unsigned long flags;
+ struct fnic_iport_s *iport = &fnic->iport;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->reset_in_progress == NOT_IN_PROGRESS) {
+ fnic->reset_in_progress = IN_PROGRESS;
+ } else {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ wait_for_completion_timeout(&fnic->reset_completion_wait,
+ msecs_to_jiffies(10000));
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->reset_in_progress == IN_PROGRESS) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_WARNING, fnic->host, fnic->fnic_num,
+ "Firmware reset in progress. Skipping another host reset\n");
+ return SUCCESS;
+ }
+ fnic->reset_in_progress = IN_PROGRESS;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
/*
* If fnic_reset is successful, wait for fabric login to complete
* scsi-ml tries to send a TUR to every device if host reset is
* successful, so before returning to scsi, fabric should be up
*/
- ret = fnic_reset(shost);
- if (ret == SUCCESS) {
+ fnic_reset(shost);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ fnic->reset_in_progress = NOT_IN_PROGRESS;
+ complete(&fnic->reset_completion_wait);
+ fnic->soft_reset_count++;
+
+ /* wait till the link is up */
+ if (fnic->link_status) {
wait_host_tmo = jiffies + FNIC_HOST_RESET_SETTLE_TIME * HZ;
ret = FAILED;
while (time_before(jiffies, wait_host_tmo)) {
- if ((lp->state == LPORT_ST_READY) &&
- (lp->link_up)) {
+ if (iport->state != FNIC_IPORT_STATE_READY
+ && fnic->link_status) {
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ ssleep(1);
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ } else {
ret = SUCCESS;
break;
}
- ssleep(1);
}
}
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "host reset return status: %d\n", ret);
return ret;
}
-/*
- * This fxn is called from libFC when host is removed
- */
-void fnic_scsi_abort_io(struct fc_lport *lp)
+static bool fnic_abts_pending_iter(struct scsi_cmnd *sc, void *data)
{
- int err = 0;
+ struct request *const rq = scsi_cmd_to_rq(sc);
+ struct fnic_pending_aborts_iter_data *iter_data = data;
+ struct fnic *fnic = iter_data->fnic;
+ int cmd_state;
+ struct fnic_io_req *io_req;
unsigned long flags;
- enum fnic_state old_state;
- struct fnic *fnic = lport_priv(lp);
- DECLARE_COMPLETION_ONSTACK(remove_wait);
+ uint16_t hwq = 0;
+ int tag;
- /* Issue firmware reset for fnic, wait for reset to complete */
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- fnic->remove_wait = &remove_wait;
- old_state = fnic->state;
- fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ tag = blk_mq_unique_tag(rq);
+ hwq = blk_mq_unique_tag_to_hwq(tag);
- err = fnic_fw_reset_handler(fnic);
- if (err) {
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)
- fnic->state = old_state;
- fnic->remove_wait = NULL;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- return;
+ /*
+ * ignore this lun reset cmd or cmds that do not belong to
+ * this lun
+ */
+ if (iter_data->lr_sc && sc == iter_data->lr_sc)
+ return true;
+ if (iter_data->lun_dev && sc->device != iter_data->lun_dev)
+ return true;
+
+ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
+
+ io_req = fnic_priv(sc)->io_req;
+ if (!io_req) {
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ return true;
}
- /* Wait for firmware reset to complete */
- wait_for_completion_timeout(&remove_wait,
- msecs_to_jiffies(FNIC_RMDEVICE_TIMEOUT));
+ /*
+ * Found IO that is still pending with firmware and
+ * belongs to the LUN that we are resetting
+ */
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "hwq: %d tag: 0x%x Found IO in state: %s on lun\n",
+ hwq, tag,
+ fnic_ioreq_state_to_str(fnic_priv(sc)->state));
+ cmd_state = fnic_priv(sc)->state;
+ spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
+ if (cmd_state == FNIC_IOREQ_ABTS_PENDING)
+ iter_data->ret = 1;
+
+ return iter_data->ret ? false : true;
+}
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- fnic->remove_wait = NULL;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "fnic_scsi_abort_io %s\n",
- (fnic->state == FNIC_IN_ETH_MODE) ?
- "SUCCESS" : "FAILED");
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+/*
+ * fnic_is_abts_pending() is a helper function that
+ * walks through tag map to check if there is any IOs pending,if there is one,
+ * then it returns 1 (true), otherwise 0 (false)
+ * if @lr_sc is non NULL, then it checks IOs specific to particular LUN,
+ * otherwise, it checks for all IOs.
+ */
+int fnic_is_abts_pending(struct fnic *fnic, struct scsi_cmnd *lr_sc)
+{
+ struct fnic_pending_aborts_iter_data iter_data = {
+ .fnic = fnic,
+ .lun_dev = NULL,
+ .ret = 0,
+ };
+
+ if (lr_sc) {
+ iter_data.lun_dev = lr_sc->device;
+ iter_data.lr_sc = lr_sc;
+ }
+ /* walk again to check, if IOs are still pending in fw */
+ scsi_host_busy_iter(fnic->host,
+ fnic_abts_pending_iter, &iter_data);
+
+ return iter_data.ret;
}
/*
- * This fxn called from libFC to clean up driver IO state on link down
+ * SCSI Error handling calls driver's eh_host_reset if all prior
+ * error handling levels return FAILED. If host reset completes
+ * successfully, and if link is up, then Fabric login begins.
+ *
+ * Host Reset is the highest level of error recovery. If this fails, then
+ * host is offlined by SCSI.
+ *
*/
-void fnic_scsi_cleanup(struct fc_lport *lp)
+int fnic_eh_host_reset_handler(struct scsi_cmnd *sc)
+{
+ int ret = 0;
+ struct Scsi_Host *shost = sc->device->host;
+ struct fnic *fnic = *((struct fnic **) shost_priv(shost));
+
+ FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+ "SCSI error handling: fnic host reset");
+
+ ret = fnic_host_reset(shost);
+ return ret;
+}
+
+
+void fnic_scsi_fcpio_reset(struct fnic *fnic)
{
unsigned long flags;
enum fnic_state old_state;
- struct fnic *fnic = lport_priv(lp);
+ struct fnic_iport_s *iport = &fnic->iport;
+ DECLARE_COMPLETION_ONSTACK(fw_reset_done);
+ int time_remain;
/* issue fw reset */
spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (unlikely(fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)) {
+ /* fw reset is in progress, poll for its completion */
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "fnic is in unexpected state: %d for fw_reset\n",
+ fnic->state);
+ return;
+ }
+
old_state = fnic->state;
fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
+
+ fnic_update_mac_locked(fnic, iport->hwmac);
+ fnic->fw_reset_done = &fw_reset_done;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Issuing fw reset\n");
if (fnic_fw_reset_handler(fnic)) {
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)
fnic->state = old_state;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ } else {
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Waiting for fw completion\n");
+ time_remain = wait_for_completion_timeout(&fw_reset_done,
+ msecs_to_jiffies(FNIC_FW_RESET_TIMEOUT));
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Woken up after fw completion timeout\n");
+ if (time_remain == 0) {
+ FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "FW reset completion timed out after %d ms)\n",
+ FNIC_FW_RESET_TIMEOUT);
+ }
+ atomic64_inc(&fnic->fnic_stats.reset_stats.fw_reset_timeouts);
}
-
-}
-
-void fnic_empty_scsi_cleanup(struct fc_lport *lp)
-{
-}
-
-void fnic_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did)
-{
- struct fnic *fnic = lport_priv(lp);
-
- /* Non-zero sid, nothing to do */
- if (sid)
- goto call_fc_exch_mgr_reset;
-
- if (did) {
- fnic_rport_exch_reset(fnic, did);
- goto call_fc_exch_mgr_reset;
- }
-
- /*
- * sid = 0, did = 0
- * link down or device being removed
- */
- if (!fnic->in_remove)
- fnic_scsi_cleanup(lp);
- else
- fnic_scsi_abort_io(lp);
-
- /* call libFC exch mgr reset to reset its exchanges */
-call_fc_exch_mgr_reset:
- fc_exch_mgr_reset(lp, sid, did);
-
+ fnic->fw_reset_done = NULL;
}
diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h
new file mode 100644
index 000000000000..8ddd20401a59
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_stats.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2013 Cisco Systems, Inc. All rights reserved. */
+#ifndef _FNIC_STATS_H_
+#define _FNIC_STATS_H_
+#define FNIC_MQ_MAX_QUEUES 64
+#include <scsi/scsi_transport_fc.h>
+
+struct stats_timestamps {
+ struct timespec64 last_reset_time;
+ struct timespec64 last_read_time;
+};
+
+struct io_path_stats {
+ atomic64_t active_ios;
+ atomic64_t max_active_ios;
+ atomic64_t io_completions;
+ atomic64_t io_failures;
+ atomic64_t ioreq_null;
+ atomic64_t alloc_failures;
+ atomic64_t sc_null;
+ atomic64_t io_not_found;
+ atomic64_t num_ios;
+ atomic64_t io_btw_0_to_10_msec;
+ atomic64_t io_btw_10_to_100_msec;
+ atomic64_t io_btw_100_to_500_msec;
+ atomic64_t io_btw_500_to_5000_msec;
+ atomic64_t io_btw_5000_to_10000_msec;
+ atomic64_t io_btw_10000_to_30000_msec;
+ atomic64_t io_greater_than_30000_msec;
+ atomic64_t current_max_io_time;
+ atomic64_t ios[FNIC_MQ_MAX_QUEUES];
+};
+
+struct abort_stats {
+ atomic64_t aborts;
+ atomic64_t abort_failures;
+ atomic64_t abort_drv_timeouts;
+ atomic64_t abort_fw_timeouts;
+ atomic64_t abort_io_not_found;
+ atomic64_t abort_issued_btw_0_to_6_sec;
+ atomic64_t abort_issued_btw_6_to_20_sec;
+ atomic64_t abort_issued_btw_20_to_30_sec;
+ atomic64_t abort_issued_btw_30_to_40_sec;
+ atomic64_t abort_issued_btw_40_to_50_sec;
+ atomic64_t abort_issued_btw_50_to_60_sec;
+ atomic64_t abort_issued_greater_than_60_sec;
+};
+
+struct terminate_stats {
+ atomic64_t terminates;
+ atomic64_t max_terminates;
+ atomic64_t terminate_drv_timeouts;
+ atomic64_t terminate_fw_timeouts;
+ atomic64_t terminate_io_not_found;
+ atomic64_t terminate_failures;
+};
+
+struct reset_stats {
+ atomic64_t device_resets;
+ atomic64_t device_reset_failures;
+ atomic64_t device_reset_aborts;
+ atomic64_t device_reset_timeouts;
+ atomic64_t device_reset_terminates;
+ atomic64_t fw_resets;
+ atomic64_t fw_reset_completions;
+ atomic64_t fw_reset_failures;
+ atomic64_t fw_reset_timeouts;
+ atomic64_t fnic_resets;
+ atomic64_t fnic_reset_completions;
+ atomic64_t fnic_reset_failures;
+};
+
+struct fw_stats {
+ atomic64_t active_fw_reqs;
+ atomic64_t max_fw_reqs;
+ atomic64_t fw_out_of_resources;
+ atomic64_t io_fw_errs;
+};
+
+struct vlan_stats {
+ atomic64_t vlan_disc_reqs;
+ atomic64_t resp_withno_vlanID;
+ atomic64_t sol_expiry_count;
+ atomic64_t flogi_rejects;
+};
+
+struct misc_stats {
+ u64 last_isr_time;
+ u64 last_ack_time;
+ atomic64_t max_isr_jiffies;
+ atomic64_t max_isr_time_ms;
+ atomic64_t corr_work_done;
+ atomic64_t isr_count;
+ atomic64_t max_cq_entries;
+ atomic64_t ack_index_out_of_range;
+ atomic64_t data_count_mismatch;
+ atomic64_t fcpio_timeout;
+ atomic64_t fcpio_aborted;
+ atomic64_t sgl_invalid;
+ atomic64_t mss_invalid;
+ atomic64_t abts_cpwq_alloc_failures;
+ atomic64_t devrst_cpwq_alloc_failures;
+ atomic64_t io_cpwq_alloc_failures;
+ atomic64_t no_icmnd_itmf_cmpls;
+ atomic64_t check_condition;
+ atomic64_t queue_fulls;
+ atomic64_t tport_not_ready;
+ atomic64_t iport_not_ready;
+ atomic64_t frame_errors;
+ atomic64_t current_port_speed;
+ atomic64_t intx_dummy;
+ atomic64_t port_speed_in_mbps;
+};
+
+struct fnic_iport_stats {
+ atomic64_t num_linkdn;
+ atomic64_t num_linkup;
+ atomic64_t link_failure_count;
+ atomic64_t num_rscns;
+ atomic64_t rscn_redisc;
+ atomic64_t rscn_not_redisc;
+ atomic64_t frame_err;
+ atomic64_t num_rnid;
+ atomic64_t fabric_flogi_sent;
+ atomic64_t fabric_flogi_ls_accepts;
+ atomic64_t fabric_flogi_ls_rejects;
+ atomic64_t fabric_flogi_misc_rejects;
+ atomic64_t fabric_plogi_sent;
+ atomic64_t fabric_plogi_ls_accepts;
+ atomic64_t fabric_plogi_ls_rejects;
+ atomic64_t fabric_plogi_misc_rejects;
+ atomic64_t fabric_scr_sent;
+ atomic64_t fabric_scr_ls_accepts;
+ atomic64_t fabric_scr_ls_rejects;
+ atomic64_t fabric_scr_misc_rejects;
+ atomic64_t fabric_logo_sent;
+ atomic64_t tport_alive;
+ atomic64_t tport_plogi_sent;
+ atomic64_t tport_plogi_ls_accepts;
+ atomic64_t tport_plogi_ls_rejects;
+ atomic64_t tport_plogi_misc_rejects;
+ atomic64_t tport_prli_sent;
+ atomic64_t tport_prli_ls_accepts;
+ atomic64_t tport_prli_ls_rejects;
+ atomic64_t tport_prli_misc_rejects;
+ atomic64_t tport_adisc_sent;
+ atomic64_t tport_adisc_ls_accepts;
+ atomic64_t tport_adisc_ls_rejects;
+ atomic64_t tport_logo_sent;
+ atomic64_t unsupported_frames_ls_rejects;
+ atomic64_t unsupported_frames_dropped;
+};
+
+struct fnic_stats {
+ struct stats_timestamps stats_timestamps;
+ struct io_path_stats io_stats;
+ struct abort_stats abts_stats;
+ struct terminate_stats term_stats;
+ struct reset_stats reset_stats;
+ struct fw_stats fw_stats;
+ struct vlan_stats vlan_stats;
+ struct fc_host_statistics host_stats;
+ struct misc_stats misc_stats;
+};
+
+struct stats_debug_info {
+ char *debug_buffer;
+ void *i_private;
+ int buf_size;
+ int buffer_len;
+};
+
+int fnic_get_stats_data(struct stats_debug_info *, struct fnic_stats *);
+const char *fnic_role_to_str(unsigned int role);
+#endif /* _FNIC_STATS_H_ */
diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c
new file mode 100644
index 000000000000..4ed57ea1f854
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_trace.c
@@ -0,0 +1,883 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright 2012 Cisco Systems, Inc. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <scsi/scsi_transport_fc.h>
+#include "fnic_io.h"
+#include "fnic.h"
+
+unsigned int trace_max_pages;
+static int fnic_max_trace_entries;
+
+static unsigned long fnic_trace_buf_p;
+static DEFINE_SPINLOCK(fnic_trace_lock);
+
+static fnic_trace_dbg_t fnic_trace_entries;
+int fnic_tracing_enabled = 1;
+
+/* static char *fnic_fc_ctlr_trace_buf_p; */
+
+static int fc_trace_max_entries;
+static unsigned long fnic_fc_ctlr_trace_buf_p;
+static fnic_trace_dbg_t fc_trace_entries;
+int fnic_fc_tracing_enabled = 1;
+int fnic_fc_trace_cleared = 1;
+static DEFINE_SPINLOCK(fnic_fc_trace_lock);
+
+static const char * const fnic_role_str[] = {
+ [FNIC_ROLE_FCP_INITIATOR] = "FCP_Initiator",
+};
+
+const char *fnic_role_to_str(unsigned int role)
+{
+ if (role >= ARRAY_SIZE(fnic_role_str) || !fnic_role_str[role])
+ return "Unknown";
+
+ return fnic_role_str[role];
+}
+
+/*
+ * fnic_trace_get_buf - Give buffer pointer to user to fill up trace information
+ *
+ * Description:
+ * This routine gets next available trace buffer entry location @wr_idx
+ * from allocated trace buffer pages and give that memory location
+ * to user to store the trace information.
+ *
+ * Return Value:
+ * This routine returns pointer to next available trace entry
+ * @fnic_buf_head for user to fill trace information.
+ */
+fnic_trace_data_t *fnic_trace_get_buf(void)
+{
+ unsigned long fnic_buf_head;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic_trace_lock, flags);
+
+ /*
+ * Get next available memory location for writing trace information
+ * at @wr_idx and increment @wr_idx
+ */
+ fnic_buf_head =
+ fnic_trace_entries.page_offset[fnic_trace_entries.wr_idx];
+ fnic_trace_entries.wr_idx++;
+
+ /*
+ * Verify if trace buffer is full then change wd_idx to
+ * start from zero
+ */
+ if (fnic_trace_entries.wr_idx >= fnic_max_trace_entries)
+ fnic_trace_entries.wr_idx = 0;
+
+ /*
+ * Verify if write index @wr_idx and read index @rd_idx are same then
+ * increment @rd_idx to move to next entry in trace buffer
+ */
+ if (fnic_trace_entries.wr_idx == fnic_trace_entries.rd_idx) {
+ fnic_trace_entries.rd_idx++;
+ if (fnic_trace_entries.rd_idx >= fnic_max_trace_entries)
+ fnic_trace_entries.rd_idx = 0;
+ }
+ spin_unlock_irqrestore(&fnic_trace_lock, flags);
+ return (fnic_trace_data_t *)fnic_buf_head;
+}
+
+/*
+ * fnic_get_trace_data - Copy trace buffer to a memory file
+ * @fnic_dbgfs_t: pointer to debugfs trace buffer
+ *
+ * Description:
+ * This routine gathers the fnic trace debugfs data from the fnic_trace_data_t
+ * buffer and dumps it to fnic_dbgfs_t. It will start at the rd_idx entry in
+ * the log and process the log until the end of the buffer. Then it will gather
+ * from the beginning of the log and process until the current entry @wr_idx.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into fnic_dbgfs_t
+ */
+int fnic_get_trace_data(fnic_dbgfs_t *fnic_dbgfs_prt)
+{
+ int rd_idx;
+ int wr_idx;
+ int len = 0;
+ unsigned long flags;
+ char str[KSYM_SYMBOL_LEN];
+ struct timespec64 val;
+ fnic_trace_data_t *tbp;
+
+ spin_lock_irqsave(&fnic_trace_lock, flags);
+ rd_idx = fnic_trace_entries.rd_idx;
+ wr_idx = fnic_trace_entries.wr_idx;
+ if (wr_idx < rd_idx) {
+ while (1) {
+ /* Start from read index @rd_idx */
+ tbp = (fnic_trace_data_t *)
+ fnic_trace_entries.page_offset[rd_idx];
+ if (!tbp) {
+ spin_unlock_irqrestore(&fnic_trace_lock, flags);
+ return 0;
+ }
+ /* Convert function pointer to function name */
+ if (sizeof(unsigned long) < 8) {
+ sprint_symbol(str, tbp->fnaddr.low);
+ jiffies_to_timespec64(tbp->timestamp.low, &val);
+ } else {
+ sprint_symbol(str, tbp->fnaddr.val);
+ jiffies_to_timespec64(tbp->timestamp.val, &val);
+ }
+ /*
+ * Dump trace buffer entry to memory file
+ * and increment read index @rd_idx
+ */
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ (trace_max_pages * PAGE_SIZE * 3) - len,
+ "%ptSp %-50s %8x %8x %16llx %16llx %16llx %16llx %16llx\n",
+ &val, str, tbp->host_no, tbp->tag,
+ tbp->data[0], tbp->data[1], tbp->data[2],
+ tbp->data[3], tbp->data[4]);
+ rd_idx++;
+ /*
+ * If rd_idx is reached to maximum trace entries
+ * then move rd_idx to zero
+ */
+ if (rd_idx > (fnic_max_trace_entries-1))
+ rd_idx = 0;
+ /*
+ * Continue dumping trace buffer entries into
+ * memory file till rd_idx reaches write index
+ */
+ if (rd_idx == wr_idx)
+ break;
+ }
+ } else if (wr_idx > rd_idx) {
+ while (1) {
+ /* Start from read index @rd_idx */
+ tbp = (fnic_trace_data_t *)
+ fnic_trace_entries.page_offset[rd_idx];
+ if (!tbp) {
+ spin_unlock_irqrestore(&fnic_trace_lock, flags);
+ return 0;
+ }
+ /* Convert function pointer to function name */
+ if (sizeof(unsigned long) < 8) {
+ sprint_symbol(str, tbp->fnaddr.low);
+ jiffies_to_timespec64(tbp->timestamp.low, &val);
+ } else {
+ sprint_symbol(str, tbp->fnaddr.val);
+ jiffies_to_timespec64(tbp->timestamp.val, &val);
+ }
+ /*
+ * Dump trace buffer entry to memory file
+ * and increment read index @rd_idx
+ */
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ (trace_max_pages * PAGE_SIZE * 3) - len,
+ "%ptSp %-50s %8x %8x %16llx %16llx %16llx %16llx %16llx\n",
+ &val, str, tbp->host_no, tbp->tag,
+ tbp->data[0], tbp->data[1], tbp->data[2],
+ tbp->data[3], tbp->data[4]);
+ rd_idx++;
+ /*
+ * Continue dumping trace buffer entries into
+ * memory file till rd_idx reaches write index
+ */
+ if (rd_idx == wr_idx)
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&fnic_trace_lock, flags);
+ return len;
+}
+
+/*
+ * fnic_get_stats_data - Copy fnic stats buffer to a memory file
+ * @fnic_dbgfs_t: pointer to debugfs fnic stats buffer
+ *
+ * Description:
+ * This routine gathers the fnic stats debugfs data from the fnic_stats struct
+ * and dumps it to stats_debug_info.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into
+ * stats_debug_info
+ */
+int fnic_get_stats_data(struct stats_debug_info *debug,
+ struct fnic_stats *stats)
+{
+ int len = 0;
+ int buf_size = debug->buf_size;
+ struct timespec64 val, val1, val2;
+ int i = 0;
+
+ ktime_get_real_ts64(&val);
+ len = scnprintf(debug->debug_buffer + len, buf_size - len,
+ "------------------------------------------\n"
+ "\t\tTime\n"
+ "------------------------------------------\n");
+
+ val1 = timespec64_sub(val, stats->stats_timestamps.last_reset_time);
+ val2 = timespec64_sub(val, stats->stats_timestamps.last_read_time);
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Current time : [%ptSp]\n"
+ "Last stats reset time: [%ptSp]\n"
+ "Last stats read time: [%ptSp]\n"
+ "delta since last reset: [%ptSp]\n"
+ "delta since last read: [%ptSp]\n",
+ &val,
+ &stats->stats_timestamps.last_reset_time,
+ &stats->stats_timestamps.last_read_time,
+ &val1, &val2);
+
+ stats->stats_timestamps.last_read_time = val;
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "------------------------------------------\n"
+ "\t\tIO Statistics\n"
+ "------------------------------------------\n");
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Number of Active IOs: %lld\nMaximum Active IOs: %lld\n"
+ "Number of IOs: %lld\nNumber of IO Completions: %lld\n"
+ "Number of IO Failures: %lld\nNumber of IO NOT Found: %lld\n"
+ "Number of Memory alloc Failures: %lld\n"
+ "Number of IOREQ Null: %lld\n"
+ "Number of SCSI cmd pointer Null: %lld\n"
+
+ "\nIO completion times: \n"
+ " < 10 ms : %lld\n"
+ " 10 ms - 100 ms : %lld\n"
+ " 100 ms - 500 ms : %lld\n"
+ " 500 ms - 5 sec: %lld\n"
+ " 5 sec - 10 sec: %lld\n"
+ " 10 sec - 30 sec: %lld\n"
+ " > 30 sec: %lld\n",
+ (u64)atomic64_read(&stats->io_stats.active_ios),
+ (u64)atomic64_read(&stats->io_stats.max_active_ios),
+ (u64)atomic64_read(&stats->io_stats.num_ios),
+ (u64)atomic64_read(&stats->io_stats.io_completions),
+ (u64)atomic64_read(&stats->io_stats.io_failures),
+ (u64)atomic64_read(&stats->io_stats.io_not_found),
+ (u64)atomic64_read(&stats->io_stats.alloc_failures),
+ (u64)atomic64_read(&stats->io_stats.ioreq_null),
+ (u64)atomic64_read(&stats->io_stats.sc_null),
+ (u64)atomic64_read(&stats->io_stats.io_btw_0_to_10_msec),
+ (u64)atomic64_read(&stats->io_stats.io_btw_10_to_100_msec),
+ (u64)atomic64_read(&stats->io_stats.io_btw_100_to_500_msec),
+ (u64)atomic64_read(&stats->io_stats.io_btw_500_to_5000_msec),
+ (u64)atomic64_read(&stats->io_stats.io_btw_5000_to_10000_msec),
+ (u64)atomic64_read(&stats->io_stats.io_btw_10000_to_30000_msec),
+ (u64)atomic64_read(&stats->io_stats.io_greater_than_30000_msec));
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "------------------------------------------\n"
+ "\t\tIO Queues and cumulative IOs\n"
+ "------------------------------------------\n");
+
+ for (i = 0; i < FNIC_MQ_MAX_QUEUES; i++) {
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Q:%d -> %lld\n", i, (u64)atomic64_read(&stats->io_stats.ios[i]));
+ }
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "\nCurrent Max IO time : %lld\n",
+ (u64)atomic64_read(&stats->io_stats.current_max_io_time));
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "\n------------------------------------------\n"
+ "\t\tAbort Statistics\n"
+ "------------------------------------------\n");
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Number of Aborts: %lld\n"
+ "Number of Abort Failures: %lld\n"
+ "Number of Abort Driver Timeouts: %lld\n"
+ "Number of Abort FW Timeouts: %lld\n"
+ "Number of Abort IO NOT Found: %lld\n"
+
+ "Abort issued times: \n"
+ " < 6 sec : %lld\n"
+ " 6 sec - 20 sec : %lld\n"
+ " 20 sec - 30 sec : %lld\n"
+ " 30 sec - 40 sec : %lld\n"
+ " 40 sec - 50 sec : %lld\n"
+ " 50 sec - 60 sec : %lld\n"
+ " > 60 sec: %lld\n",
+
+ (u64)atomic64_read(&stats->abts_stats.aborts),
+ (u64)atomic64_read(&stats->abts_stats.abort_failures),
+ (u64)atomic64_read(&stats->abts_stats.abort_drv_timeouts),
+ (u64)atomic64_read(&stats->abts_stats.abort_fw_timeouts),
+ (u64)atomic64_read(&stats->abts_stats.abort_io_not_found),
+ (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_0_to_6_sec),
+ (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_6_to_20_sec),
+ (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_20_to_30_sec),
+ (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_30_to_40_sec),
+ (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_40_to_50_sec),
+ (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_50_to_60_sec),
+ (u64)atomic64_read(&stats->abts_stats.abort_issued_greater_than_60_sec));
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "\n------------------------------------------\n"
+ "\t\tTerminate Statistics\n"
+ "------------------------------------------\n");
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Number of Terminates: %lld\n"
+ "Maximum Terminates: %lld\n"
+ "Number of Terminate Driver Timeouts: %lld\n"
+ "Number of Terminate FW Timeouts: %lld\n"
+ "Number of Terminate IO NOT Found: %lld\n"
+ "Number of Terminate Failures: %lld\n",
+ (u64)atomic64_read(&stats->term_stats.terminates),
+ (u64)atomic64_read(&stats->term_stats.max_terminates),
+ (u64)atomic64_read(&stats->term_stats.terminate_drv_timeouts),
+ (u64)atomic64_read(&stats->term_stats.terminate_fw_timeouts),
+ (u64)atomic64_read(&stats->term_stats.terminate_io_not_found),
+ (u64)atomic64_read(&stats->term_stats.terminate_failures));
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "\n------------------------------------------\n"
+ "\t\tReset Statistics\n"
+ "------------------------------------------\n");
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Number of Device Resets: %lld\n"
+ "Number of Device Reset Failures: %lld\n"
+ "Number of Device Reset Aborts: %lld\n"
+ "Number of Device Reset Timeouts: %lld\n"
+ "Number of Device Reset Terminates: %lld\n"
+ "Number of FW Resets: %lld\n"
+ "Number of FW Reset Completions: %lld\n"
+ "Number of FW Reset Failures: %lld\n"
+ "Number of Fnic Reset: %lld\n"
+ "Number of Fnic Reset Completions: %lld\n"
+ "Number of Fnic Reset Failures: %lld\n",
+ (u64)atomic64_read(&stats->reset_stats.device_resets),
+ (u64)atomic64_read(&stats->reset_stats.device_reset_failures),
+ (u64)atomic64_read(&stats->reset_stats.device_reset_aborts),
+ (u64)atomic64_read(&stats->reset_stats.device_reset_timeouts),
+ (u64)atomic64_read(
+ &stats->reset_stats.device_reset_terminates),
+ (u64)atomic64_read(&stats->reset_stats.fw_resets),
+ (u64)atomic64_read(&stats->reset_stats.fw_reset_completions),
+ (u64)atomic64_read(&stats->reset_stats.fw_reset_failures),
+ (u64)atomic64_read(&stats->reset_stats.fnic_resets),
+ (u64)atomic64_read(
+ &stats->reset_stats.fnic_reset_completions),
+ (u64)atomic64_read(&stats->reset_stats.fnic_reset_failures));
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "\n------------------------------------------\n"
+ "\t\tFirmware Statistics\n"
+ "------------------------------------------\n");
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Number of Active FW Requests %lld\n"
+ "Maximum FW Requests: %lld\n"
+ "Number of FW out of resources: %lld\n"
+ "Number of FW IO errors: %lld\n",
+ (u64)atomic64_read(&stats->fw_stats.active_fw_reqs),
+ (u64)atomic64_read(&stats->fw_stats.max_fw_reqs),
+ (u64)atomic64_read(&stats->fw_stats.fw_out_of_resources),
+ (u64)atomic64_read(&stats->fw_stats.io_fw_errs));
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "\n------------------------------------------\n"
+ "\t\tVlan Discovery Statistics\n"
+ "------------------------------------------\n");
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Number of Vlan Discovery Requests Sent %lld\n"
+ "Vlan Response Received with no FCF VLAN ID: %lld\n"
+ "No solicitations recvd after vlan set, expiry count: %lld\n"
+ "Flogi rejects count: %lld\n",
+ (u64)atomic64_read(&stats->vlan_stats.vlan_disc_reqs),
+ (u64)atomic64_read(&stats->vlan_stats.resp_withno_vlanID),
+ (u64)atomic64_read(&stats->vlan_stats.sol_expiry_count),
+ (u64)atomic64_read(&stats->vlan_stats.flogi_rejects));
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "\n------------------------------------------\n"
+ "\t\tOther Important Statistics\n"
+ "------------------------------------------\n");
+
+ jiffies_to_timespec64(stats->misc_stats.last_isr_time, &val1);
+ jiffies_to_timespec64(stats->misc_stats.last_ack_time, &val2);
+
+ len += scnprintf(debug->debug_buffer + len, buf_size - len,
+ "Last ISR time: %llu (%ptSp)\n"
+ "Last ACK time: %llu (%ptSp)\n"
+ "Max ISR jiffies: %llu\n"
+ "Max ISR time (ms) (0 denotes < 1 ms): %llu\n"
+ "Corr. work done: %llu\n"
+ "Number of ISRs: %lld\n"
+ "Maximum CQ Entries: %lld\n"
+ "Number of ACK index out of range: %lld\n"
+ "Number of data count mismatch: %lld\n"
+ "Number of FCPIO Timeouts: %lld\n"
+ "Number of FCPIO Aborted: %lld\n"
+ "Number of SGL Invalid: %lld\n"
+ "Number of Copy WQ Alloc Failures for ABTs: %lld\n"
+ "Number of Copy WQ Alloc Failures for Device Reset: %lld\n"
+ "Number of Copy WQ Alloc Failures for IOs: %lld\n"
+ "Number of no icmnd itmf Completions: %lld\n"
+ "Number of Check Conditions encountered: %lld\n"
+ "Number of QUEUE Fulls: %lld\n"
+ "Number of rport not ready: %lld\n"
+ "Number of receive frame errors: %lld\n"
+ "Port speed (in Mbps): %lld\n",
+ (u64)stats->misc_stats.last_isr_time, &val1,
+ (u64)stats->misc_stats.last_ack_time, &val2,
+ (u64)atomic64_read(&stats->misc_stats.max_isr_jiffies),
+ (u64)atomic64_read(&stats->misc_stats.max_isr_time_ms),
+ (u64)atomic64_read(&stats->misc_stats.corr_work_done),
+ (u64)atomic64_read(&stats->misc_stats.isr_count),
+ (u64)atomic64_read(&stats->misc_stats.max_cq_entries),
+ (u64)atomic64_read(&stats->misc_stats.ack_index_out_of_range),
+ (u64)atomic64_read(&stats->misc_stats.data_count_mismatch),
+ (u64)atomic64_read(&stats->misc_stats.fcpio_timeout),
+ (u64)atomic64_read(&stats->misc_stats.fcpio_aborted),
+ (u64)atomic64_read(&stats->misc_stats.sgl_invalid),
+ (u64)atomic64_read(
+ &stats->misc_stats.abts_cpwq_alloc_failures),
+ (u64)atomic64_read(
+ &stats->misc_stats.devrst_cpwq_alloc_failures),
+ (u64)atomic64_read(&stats->misc_stats.io_cpwq_alloc_failures),
+ (u64)atomic64_read(&stats->misc_stats.no_icmnd_itmf_cmpls),
+ (u64)atomic64_read(&stats->misc_stats.check_condition),
+ (u64)atomic64_read(&stats->misc_stats.queue_fulls),
+ (u64)atomic64_read(&stats->misc_stats.tport_not_ready),
+ (u64)atomic64_read(&stats->misc_stats.frame_errors),
+ (u64)atomic64_read(&stats->misc_stats.port_speed_in_mbps));
+
+ return len;
+
+}
+
+int fnic_get_debug_info(struct stats_debug_info *info, struct fnic *fnic)
+{
+ struct fnic_iport_s *iport = &fnic->iport;
+ int buf_size = info->buf_size;
+ int len = info->buffer_len;
+ struct fnic_tport_s *tport, *next;
+ unsigned long flags;
+
+ len += snprintf(info->debug_buffer + len, buf_size - len,
+ "------------------------------------------\n"
+ "\t\t Debug Info\n"
+ "------------------------------------------\n");
+ len += snprintf(info->debug_buffer + len, buf_size - len,
+ "fnic Name:%s number:%d Role:%s State:%s\n",
+ fnic->name, fnic->fnic_num,
+ fnic_role_to_str(fnic->role),
+ fnic_state_to_str(fnic->state));
+ len +=
+ snprintf(info->debug_buffer + len, buf_size - len,
+ "iport State:%d Flags:0x%x vlan_id:%d fcid:0x%x\n",
+ iport->state, iport->flags, iport->vlan_id, iport->fcid);
+ len +=
+ snprintf(info->debug_buffer + len, buf_size - len,
+ "usefip:%d fip_state:%d fip_flogi_retry:%d\n",
+ iport->usefip, iport->fip.state, iport->fip.flogi_retry);
+ len +=
+ snprintf(info->debug_buffer + len, buf_size - len,
+ "fpma %02x:%02x:%02x:%02x:%02x:%02x",
+ iport->fpma[5], iport->fpma[4], iport->fpma[3],
+ iport->fpma[2], iport->fpma[1], iport->fpma[0]);
+ len +=
+ snprintf(info->debug_buffer + len, buf_size - len,
+ "fcfmac %02x:%02x:%02x:%02x:%02x:%02x\n",
+ iport->fcfmac[5], iport->fcfmac[4], iport->fcfmac[3],
+ iport->fcfmac[2], iport->fcfmac[1], iport->fcfmac[0]);
+ len +=
+ snprintf(info->debug_buffer + len, buf_size - len,
+ "fabric state:%d flags:0x%x retry_counter:%d e_d_tov:%d r_a_tov:%d\n",
+ iport->fabric.state, iport->fabric.flags,
+ iport->fabric.retry_counter, iport->e_d_tov,
+ iport->r_a_tov);
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+ len += snprintf(info->debug_buffer + len, buf_size - len,
+ "tport fcid:0x%x state:%d flags:0x%x inflight:%d retry_counter:%d\n",
+ tport->fcid, tport->state, tport->flags,
+ atomic_read(&tport->in_flight),
+ tport->retry_counter);
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return len;
+}
+
+/*
+ * fnic_trace_buf_init - Initialize fnic trace buffer logging facility
+ *
+ * Description:
+ * Initialize trace buffer data structure by allocating required memory and
+ * setting page_offset information for every trace entry by adding trace entry
+ * length to previous page_offset value.
+ */
+int fnic_trace_buf_init(void)
+{
+ unsigned long fnic_buf_head;
+ int i;
+ int err = 0;
+
+ trace_max_pages = fnic_trace_max_pages;
+ fnic_max_trace_entries = (trace_max_pages * PAGE_SIZE)/
+ FNIC_ENTRY_SIZE_BYTES;
+
+ fnic_trace_buf_p = (unsigned long)vcalloc(trace_max_pages, PAGE_SIZE);
+ if (!fnic_trace_buf_p) {
+ printk(KERN_ERR PFX "Failed to allocate memory "
+ "for fnic_trace_buf_p\n");
+ err = -ENOMEM;
+ goto err_fnic_trace_buf_init;
+ }
+
+ fnic_trace_entries.page_offset =
+ vcalloc(fnic_max_trace_entries, sizeof(unsigned long));
+ if (!fnic_trace_entries.page_offset) {
+ printk(KERN_ERR PFX "Failed to allocate memory for"
+ " page_offset\n");
+ if (fnic_trace_buf_p) {
+ vfree((void *)fnic_trace_buf_p);
+ fnic_trace_buf_p = 0;
+ }
+ err = -ENOMEM;
+ goto err_fnic_trace_buf_init;
+ }
+ fnic_trace_entries.wr_idx = fnic_trace_entries.rd_idx = 0;
+ fnic_buf_head = fnic_trace_buf_p;
+
+ /*
+ * Set page_offset field of fnic_trace_entries struct by
+ * calculating memory location for every trace entry using
+ * length of each trace entry
+ */
+ for (i = 0; i < fnic_max_trace_entries; i++) {
+ fnic_trace_entries.page_offset[i] = fnic_buf_head;
+ fnic_buf_head += FNIC_ENTRY_SIZE_BYTES;
+ }
+ fnic_trace_debugfs_init();
+ pr_info("fnic: Successfully Initialized Trace Buffer\n");
+ return err;
+
+err_fnic_trace_buf_init:
+ return err;
+}
+
+/*
+ * fnic_trace_free - Free memory of fnic trace data structures.
+ */
+void fnic_trace_free(void)
+{
+ fnic_tracing_enabled = 0;
+ fnic_trace_debugfs_terminate();
+ if (fnic_trace_entries.page_offset) {
+ vfree((void *)fnic_trace_entries.page_offset);
+ fnic_trace_entries.page_offset = NULL;
+ }
+ if (fnic_trace_buf_p) {
+ vfree((void *)fnic_trace_buf_p);
+ fnic_trace_buf_p = 0;
+ }
+ printk(KERN_INFO PFX "Successfully Freed Trace Buffer\n");
+}
+
+/*
+ * fnic_fc_ctlr_trace_buf_init -
+ * Initialize trace buffer to log fnic control frames
+ * Description:
+ * Initialize trace buffer data structure by allocating
+ * required memory for trace data as well as for Indexes.
+ * Frame size is 256 bytes and
+ * memory is allocated for 1024 entries of 256 bytes.
+ * Page_offset(Index) is set to the address of trace entry
+ * and page_offset is initialized by adding frame size
+ * to the previous page_offset entry.
+ */
+
+int fnic_fc_trace_init(void)
+{
+ unsigned long fc_trace_buf_head;
+ int err = 0;
+ int i;
+
+ fc_trace_max_entries = (fnic_fc_trace_max_pages * PAGE_SIZE)/
+ FC_TRC_SIZE_BYTES;
+ fnic_fc_ctlr_trace_buf_p =
+ (unsigned long)vcalloc(fnic_fc_trace_max_pages, PAGE_SIZE);
+ if (!fnic_fc_ctlr_trace_buf_p) {
+ pr_err("fnic: Failed to allocate memory for "
+ "FC Control Trace Buf\n");
+ err = -ENOMEM;
+ goto err_fnic_fc_ctlr_trace_buf_init;
+ }
+
+ /* Allocate memory for page offset */
+ fc_trace_entries.page_offset =
+ vcalloc(fc_trace_max_entries, sizeof(unsigned long));
+ if (!fc_trace_entries.page_offset) {
+ pr_err("fnic:Failed to allocate memory for page_offset\n");
+ if (fnic_fc_ctlr_trace_buf_p) {
+ pr_err("fnic: Freeing FC Control Trace Buf\n");
+ vfree((void *)fnic_fc_ctlr_trace_buf_p);
+ fnic_fc_ctlr_trace_buf_p = 0;
+ }
+ err = -ENOMEM;
+ goto err_fnic_fc_ctlr_trace_buf_init;
+ }
+
+ fc_trace_entries.rd_idx = fc_trace_entries.wr_idx = 0;
+ fc_trace_buf_head = fnic_fc_ctlr_trace_buf_p;
+
+ /*
+ * Set up fc_trace_entries.page_offset field with memory location
+ * for every trace entry
+ */
+ for (i = 0; i < fc_trace_max_entries; i++) {
+ fc_trace_entries.page_offset[i] = fc_trace_buf_head;
+ fc_trace_buf_head += FC_TRC_SIZE_BYTES;
+ }
+ fnic_fc_trace_debugfs_init();
+ pr_info("fnic: Successfully Initialized FC_CTLR Trace Buffer\n");
+ return err;
+
+err_fnic_fc_ctlr_trace_buf_init:
+ return err;
+}
+
+/*
+ * Fnic_fc_ctlr_trace_free - Free memory of fnic_fc_ctlr trace data structures.
+ */
+void fnic_fc_trace_free(void)
+{
+ fnic_fc_tracing_enabled = 0;
+ fnic_fc_trace_debugfs_terminate();
+ if (fc_trace_entries.page_offset) {
+ vfree((void *)fc_trace_entries.page_offset);
+ fc_trace_entries.page_offset = NULL;
+ }
+ if (fnic_fc_ctlr_trace_buf_p) {
+ vfree((void *)fnic_fc_ctlr_trace_buf_p);
+ fnic_fc_ctlr_trace_buf_p = 0;
+ }
+ pr_info("fnic:Successfully FC_CTLR Freed Trace Buffer\n");
+}
+
+/*
+ * fnic_fc_ctlr_set_trace_data:
+ * Maintain rd & wr idx accordingly and set data
+ * Passed parameters:
+ * host_no: host number associated with fnic
+ * frame_type: send_frame, rece_frame or link event
+ * fc_frame: pointer to fc_frame
+ * frame_len: Length of the fc_frame
+ * Description:
+ * This routine will get next available wr_idx and
+ * copy all passed trace data to the buffer pointed by wr_idx
+ * and increment wr_idx. It will also make sure that we dont
+ * overwrite the entry which we are reading and also
+ * wrap around if we reach the maximum entries.
+ * Returned Value:
+ * It will return 0 for success or -1 for failure
+ */
+int fnic_fc_trace_set_data(u32 host_no, u8 frame_type,
+ char *frame, u32 fc_trc_frame_len)
+{
+ unsigned long flags;
+ struct fc_trace_hdr *fc_buf;
+ unsigned long eth_fcoe_hdr_len;
+ char *fc_trace;
+
+ if (fnic_fc_tracing_enabled == 0)
+ return 0;
+
+ spin_lock_irqsave(&fnic_fc_trace_lock, flags);
+
+ if (fnic_fc_trace_cleared == 1) {
+ fc_trace_entries.rd_idx = fc_trace_entries.wr_idx = 0;
+ pr_info("fnic: Resetting the read idx\n");
+ memset((void *)fnic_fc_ctlr_trace_buf_p, 0,
+ fnic_fc_trace_max_pages * PAGE_SIZE);
+ fnic_fc_trace_cleared = 0;
+ }
+
+ fc_buf = (struct fc_trace_hdr *)
+ fc_trace_entries.page_offset[fc_trace_entries.wr_idx];
+
+ fc_trace_entries.wr_idx++;
+
+ if (fc_trace_entries.wr_idx >= fc_trace_max_entries)
+ fc_trace_entries.wr_idx = 0;
+
+ if (fc_trace_entries.wr_idx == fc_trace_entries.rd_idx) {
+ fc_trace_entries.rd_idx++;
+ if (fc_trace_entries.rd_idx >= fc_trace_max_entries)
+ fc_trace_entries.rd_idx = 0;
+ }
+
+ ktime_get_real_ts64(&fc_buf->time_stamp);
+ fc_buf->host_no = host_no;
+ fc_buf->frame_type = frame_type;
+
+ fc_trace = (char *)FC_TRACE_ADDRESS(fc_buf);
+
+ /* During the receive path, we do not have eth hdr as well as fcoe hdr
+ * at trace entry point so we will stuff 0xff just to make it generic.
+ */
+ if (frame_type == FNIC_FC_RECV) {
+ eth_fcoe_hdr_len = sizeof(struct ethhdr) +
+ sizeof(struct fcoe_hdr);
+ memset((char *)fc_trace, 0xff, eth_fcoe_hdr_len);
+ /* Copy the rest of data frame */
+ memcpy((char *)(fc_trace + eth_fcoe_hdr_len), (void *)frame,
+ min_t(u8, fc_trc_frame_len,
+ (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE
+ - eth_fcoe_hdr_len)));
+ } else {
+ memcpy((char *)fc_trace, (void *)frame,
+ min_t(u8, fc_trc_frame_len,
+ (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE)));
+ }
+
+ /* Store the actual received length */
+ fc_buf->frame_len = fc_trc_frame_len;
+
+ spin_unlock_irqrestore(&fnic_fc_trace_lock, flags);
+ return 0;
+}
+
+/*
+ * fnic_fc_ctlr_get_trace_data: Copy trace buffer to a memory file
+ * Passed parameter:
+ * @fnic_dbgfs_t: pointer to debugfs trace buffer
+ * rdata_flag: 1 => Unformatted file
+ * 0 => formatted file
+ * Description:
+ * This routine will copy the trace data to memory file with
+ * proper formatting and also copy to another memory
+ * file without formatting for further processing.
+ * Return Value:
+ * Number of bytes that were dumped into fnic_dbgfs_t
+ */
+
+int fnic_fc_trace_get_data(fnic_dbgfs_t *fnic_dbgfs_prt, u8 rdata_flag)
+{
+ int rd_idx, wr_idx;
+ unsigned long flags;
+ int len = 0, j;
+ struct fc_trace_hdr *tdata;
+ char *fc_trace;
+
+ spin_lock_irqsave(&fnic_fc_trace_lock, flags);
+ if (fc_trace_entries.wr_idx == fc_trace_entries.rd_idx) {
+ spin_unlock_irqrestore(&fnic_fc_trace_lock, flags);
+ pr_info("fnic: Buffer is empty\n");
+ return 0;
+ }
+ rd_idx = fc_trace_entries.rd_idx;
+ wr_idx = fc_trace_entries.wr_idx;
+ if (rdata_flag == 0) {
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ (fnic_fc_trace_max_pages * PAGE_SIZE * 3) - len,
+ "Time Stamp (UTC)\t\t"
+ "Host No: F Type: len: FCoE_FRAME:\n");
+ }
+
+ while (rd_idx != wr_idx) {
+ tdata = (struct fc_trace_hdr *)
+ fc_trace_entries.page_offset[rd_idx];
+ if (!tdata) {
+ pr_info("fnic: Rd data is NULL\n");
+ spin_unlock_irqrestore(&fnic_fc_trace_lock, flags);
+ return 0;
+ }
+ if (rdata_flag == 0) {
+ copy_and_format_trace_data(tdata,
+ fnic_dbgfs_prt, &len, rdata_flag);
+ } else {
+ fc_trace = (char *)tdata;
+ for (j = 0; j < FC_TRC_SIZE_BYTES; j++) {
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ (fnic_fc_trace_max_pages * PAGE_SIZE * 3)
+ - len, "%02x", fc_trace[j] & 0xff);
+ } /* for loop */
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ (fnic_fc_trace_max_pages * PAGE_SIZE * 3) - len,
+ "\n");
+ }
+ rd_idx++;
+ if (rd_idx > (fc_trace_max_entries - 1))
+ rd_idx = 0;
+ }
+
+ spin_unlock_irqrestore(&fnic_fc_trace_lock, flags);
+ return len;
+}
+
+/*
+ * copy_and_format_trace_data: Copy formatted data to char * buffer
+ * Passed Parameter:
+ * @fc_trace_hdr_t: pointer to trace data
+ * @fnic_dbgfs_t: pointer to debugfs trace buffer
+ * @orig_len: pointer to len
+ * rdata_flag: 0 => Formatted file, 1 => Unformatted file
+ * Description:
+ * This routine will format and copy the passed trace data
+ * for formatted file or unformatted file accordingly.
+ */
+
+void copy_and_format_trace_data(struct fc_trace_hdr *tdata,
+ fnic_dbgfs_t *fnic_dbgfs_prt, int *orig_len,
+ u8 rdata_flag)
+{
+ int j, i = 1, len;
+ int ethhdr_len = sizeof(struct ethhdr) - 1;
+ int fcoehdr_len = sizeof(struct fcoe_hdr);
+ int fchdr_len = sizeof(struct fc_frame_header);
+ int max_size = fnic_fc_trace_max_pages * PAGE_SIZE * 3;
+ char *fc_trace;
+
+ tdata->frame_type = tdata->frame_type & 0x7F;
+
+ len = *orig_len;
+
+ len += scnprintf(fnic_dbgfs_prt->buffer + len, max_size - len,
+ "%ptSs ns%8x %c%8x\t",
+ &tdata->time_stamp,
+ tdata->host_no, tdata->frame_type, tdata->frame_len);
+
+ fc_trace = (char *)FC_TRACE_ADDRESS(tdata);
+
+ for (j = 0; j < min_t(u8, tdata->frame_len,
+ (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE)); j++) {
+ if (tdata->frame_type == FNIC_FC_LE) {
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ max_size - len, "%c", fc_trace[j]);
+ } else {
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ max_size - len, "%02x", fc_trace[j] & 0xff);
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ max_size - len, " ");
+ if (j == ethhdr_len ||
+ j == ethhdr_len + fcoehdr_len ||
+ j == ethhdr_len + fcoehdr_len + fchdr_len ||
+ (i > 3 && j%fchdr_len == 0)) {
+ len += scnprintf(fnic_dbgfs_prt->buffer
+ + len, max_size - len,
+ "\n\t\t\t\t\t\t\t\t");
+ i++;
+ }
+ } /* end of else*/
+ } /* End of for loop*/
+ len += scnprintf(fnic_dbgfs_prt->buffer + len,
+ max_size - len, "\n");
+ *orig_len = len;
+}
diff --git a/drivers/scsi/fnic/fnic_trace.h b/drivers/scsi/fnic/fnic_trace.h
new file mode 100644
index 000000000000..d1c301bf3fde
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_trace.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2012 Cisco Systems, Inc. All rights reserved. */
+
+#ifndef __FNIC_TRACE_H__
+#define __FNIC_TRACE_H__
+
+#define FNIC_ENTRY_SIZE_BYTES 64
+#define FC_TRC_SIZE_BYTES 256
+#define FC_TRC_HEADER_SIZE sizeof(struct fc_trace_hdr)
+
+/*
+ * Fisrt bit of FNIC_FC_RECV and FNIC_FC_SEND is used to represent the type
+ * of frame 1 => Eth frame, 0=> FC frame
+ */
+
+#define FNIC_FC_RECV 0x52 /* Character R */
+#define FNIC_FC_SEND 0x54 /* Character T */
+#define FNIC_FC_LE 0x4C /* Character L */
+
+extern ssize_t simple_read_from_buffer(void __user *to,
+ size_t count,
+ loff_t *ppos,
+ const void *from,
+ size_t available);
+
+extern unsigned int fnic_trace_max_pages;
+extern int fnic_tracing_enabled;
+extern unsigned int trace_max_pages;
+
+extern unsigned int fnic_fc_trace_max_pages;
+extern int fnic_fc_tracing_enabled;
+extern int fnic_fc_trace_cleared;
+
+typedef struct fnic_trace_dbg {
+ int wr_idx;
+ int rd_idx;
+ unsigned long *page_offset;
+} fnic_trace_dbg_t;
+
+typedef struct fnic_dbgfs {
+ int buffer_len;
+ char *buffer;
+} fnic_dbgfs_t;
+
+struct fnic_trace_data {
+ union {
+ struct {
+ u32 low;
+ u32 high;
+ };
+ u64 val;
+ } timestamp, fnaddr;
+ u32 host_no;
+ u32 tag;
+ u64 data[5];
+} __attribute__((__packed__));
+
+typedef struct fnic_trace_data fnic_trace_data_t;
+
+struct fc_trace_hdr {
+ struct timespec64 time_stamp;
+ u32 host_no;
+ u8 frame_type;
+ u8 frame_len;
+} __attribute__((__packed__));
+
+#define FC_TRACE_ADDRESS(a) \
+ ((unsigned long)(a) + sizeof(struct fc_trace_hdr))
+
+#define FNIC_TRACE_ENTRY_SIZE \
+ (FNIC_ENTRY_SIZE_BYTES - sizeof(fnic_trace_data_t))
+
+#define FNIC_TRACE(_fn, _hn, _t, _a, _b, _c, _d, _e) \
+ if (unlikely(fnic_tracing_enabled)) { \
+ fnic_trace_data_t *trace_buf = fnic_trace_get_buf(); \
+ if (trace_buf) { \
+ if (sizeof(unsigned long) < 8) { \
+ trace_buf->timestamp.low = jiffies; \
+ trace_buf->fnaddr.low = (u32)(unsigned long)_fn; \
+ } else { \
+ trace_buf->timestamp.val = jiffies; \
+ trace_buf->fnaddr.val = (u64)(unsigned long)_fn; \
+ } \
+ trace_buf->host_no = _hn; \
+ trace_buf->tag = _t; \
+ trace_buf->data[0] = (u64)(unsigned long)_a; \
+ trace_buf->data[1] = (u64)(unsigned long)_b; \
+ trace_buf->data[2] = (u64)(unsigned long)_c; \
+ trace_buf->data[3] = (u64)(unsigned long)_d; \
+ trace_buf->data[4] = (u64)(unsigned long)_e; \
+ } \
+ }
+
+fnic_trace_data_t *fnic_trace_get_buf(void);
+int fnic_get_trace_data(fnic_dbgfs_t *);
+int fnic_trace_buf_init(void);
+void fnic_trace_free(void);
+int fnic_debugfs_init(void);
+void fnic_debugfs_terminate(void);
+void fnic_trace_debugfs_init(void);
+void fnic_trace_debugfs_terminate(void);
+
+/* Fnic FC CTLR Trace releated function */
+int fnic_fc_trace_init(void);
+void fnic_fc_trace_free(void);
+int fnic_fc_trace_set_data(u32 host_no, u8 frame_type,
+ char *frame, u32 fc_frame_len);
+int fnic_fc_trace_get_data(fnic_dbgfs_t *fnic_dbgfs_prt, u8 rdata_flag);
+void copy_and_format_trace_data(struct fc_trace_hdr *tdata,
+ fnic_dbgfs_t *fnic_dbgfs_prt,
+ int *len, u8 rdata_flag);
+void fnic_fc_trace_debugfs_init(void);
+void fnic_fc_trace_debugfs_terminate(void);
+
+#endif
diff --git a/drivers/scsi/fnic/rq_enet_desc.h b/drivers/scsi/fnic/rq_enet_desc.h
index 92e80ae6b725..9bc509d355c4 100644
--- a/drivers/scsi/fnic/rq_enet_desc.h
+++ b/drivers/scsi/fnic/rq_enet_desc.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _RQ_ENET_DESC_H_
#define _RQ_ENET_DESC_H_
diff --git a/drivers/scsi/fnic/vnic_cq.c b/drivers/scsi/fnic/vnic_cq.c
index c5db32eda5ef..ed3dd443fe3e 100644
--- a/drivers/scsi/fnic/vnic_cq.c
+++ b/drivers/scsi/fnic/vnic_cq.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/errno.h>
#include <linux/types.h>
diff --git a/drivers/scsi/fnic/vnic_cq.h b/drivers/scsi/fnic/vnic_cq.h
index 4ede6809fb1e..e7cc1f165390 100644
--- a/drivers/scsi/fnic/vnic_cq.h
+++ b/drivers/scsi/fnic/vnic_cq.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_CQ_H_
#define _VNIC_CQ_H_
diff --git a/drivers/scsi/fnic/vnic_cq_copy.h b/drivers/scsi/fnic/vnic_cq_copy.h
index 7901ce255a81..1b198ee59dd6 100644
--- a/drivers/scsi/fnic/vnic_cq_copy.h
+++ b/drivers/scsi/fnic/vnic_cq_copy.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_CQ_COPY_H_
#define _VNIC_CQ_COPY_H_
diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
index b576be734e2e..e0b173cc9d5f 100644
--- a/drivers/scsi/fnic/vnic_dev.c
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/kernel.h>
@@ -27,6 +15,24 @@
#include "vnic_devcmd.h"
#include "vnic_dev.h"
#include "vnic_stats.h"
+#include "vnic_wq.h"
+
+struct devcmd2_controller {
+ struct vnic_wq_ctrl *wq_ctrl;
+ struct vnic_dev_ring results_ring;
+ struct vnic_wq wq;
+ struct vnic_devcmd2 *cmd_ring;
+ struct devcmd2_result *result;
+ u16 next_result;
+ u16 result_size;
+ int color;
+};
+
+enum vnic_proxy_type {
+ PROXY_NONE,
+ PROXY_BY_BDF,
+ PROXY_BY_INDEX,
+};
struct vnic_res {
void __iomem *vaddr;
@@ -48,6 +54,12 @@ struct vnic_dev {
dma_addr_t stats_pa;
struct vnic_devcmd_fw_info *fw_info;
dma_addr_t fw_info_pa;
+ enum vnic_proxy_type proxy;
+ u32 proxy_index;
+ u64 args[VNIC_DEVCMD_NARGS];
+ struct devcmd2_controller *devcmd2;
+ int (*devcmd_rtn)(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ int wait);
};
#define VNIC_MAX_RES_HDR_SIZE \
@@ -119,6 +131,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
}
break;
case RES_TYPE_INTR_PBA_LEGACY:
+ case RES_TYPE_DEVCMD2:
case RES_TYPE_DEVCMD:
len = count;
break;
@@ -130,6 +143,10 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
vdev->res[type].vaddr = (char __iomem *)bar->vaddr + bar_offset;
}
+ pr_info("res_type_wq: %d res_type_rq: %d res_type_cq: %d res_type_intr_ctrl: %d\n",
+ vdev->res[RES_TYPE_WQ].count, vdev->res[RES_TYPE_RQ].count,
+ vdev->res[RES_TYPE_CQ].count, vdev->res[RES_TYPE_INTR_CTRL].count);
+
return 0;
}
@@ -195,9 +212,9 @@ int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
{
vnic_dev_desc_ring_size(ring, desc_count, desc_size);
- ring->descs_unaligned = pci_alloc_consistent(vdev->pdev,
+ ring->descs_unaligned = dma_alloc_coherent(&vdev->pdev->dev,
ring->size_unaligned,
- &ring->base_addr_unaligned);
+ &ring->base_addr_unaligned, GFP_KERNEL);
if (!ring->descs_unaligned) {
printk(KERN_ERR
@@ -221,7 +238,7 @@ int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring)
{
if (ring->descs) {
- pci_free_consistent(vdev->pdev,
+ dma_free_coherent(&vdev->pdev->dev,
ring->size_unaligned,
ring->descs_unaligned,
ring->base_addr_unaligned);
@@ -229,13 +246,12 @@ void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring)
}
}
-int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
- u64 *a0, u64 *a1, int wait)
+static int vnic_dev_cmd1(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, int wait)
{
struct vnic_devcmd __iomem *devcmd = vdev->devcmd;
int delay;
u32 status;
- int dev_cmd_err[] = {
+ static const int dev_cmd_err[] = {
/* convert from fw's version of error.h to host's version */
0, /* ERR_SUCCESS */
EINVAL, /* ERR_EINVAL */
@@ -244,6 +260,8 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
EBUSY, /* ERR_EBUSY */
};
int err;
+ u64 *a0 = &vdev->args[0];
+ u64 *a1 = &vdev->args[1];
status = ioread32(&devcmd->status);
if (status & STAT_BUSY) {
@@ -290,6 +308,225 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
return -ETIMEDOUT;
}
+static int vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ int wait)
+{
+ struct devcmd2_controller *dc2c = vdev->devcmd2;
+ struct devcmd2_result *result;
+ u8 color;
+ unsigned int i;
+ int delay;
+ int err;
+ u32 fetch_index;
+ u32 posted;
+ u32 new_posted;
+
+ posted = ioread32(&dc2c->wq_ctrl->posted_index);
+ fetch_index = ioread32(&dc2c->wq_ctrl->fetch_index);
+
+ if (posted == 0xFFFFFFFF || fetch_index == 0xFFFFFFFF) {
+ /* Hardware surprise removal: return error */
+ pr_err("%s: devcmd2 invalid posted or fetch index on cmd %d\n",
+ pci_name(vdev->pdev), _CMD_N(cmd));
+ pr_err("%s: fetch index: %u, posted index: %u\n",
+ pci_name(vdev->pdev), fetch_index, posted);
+
+ return -ENODEV;
+
+ }
+
+ new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
+
+ if (new_posted == fetch_index) {
+ pr_err("%s: devcmd2 wq full while issuing cmd %d\n",
+ pci_name(vdev->pdev), _CMD_N(cmd));
+ pr_err("%s: fetch index: %u, posted index: %u\n",
+ pci_name(vdev->pdev), fetch_index, posted);
+ return -EBUSY;
+
+ }
+ dc2c->cmd_ring[posted].cmd = cmd;
+ dc2c->cmd_ring[posted].flags = 0;
+
+ if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
+ dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT;
+ if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) {
+ for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+ dc2c->cmd_ring[posted].args[i] = vdev->args[i];
+
+ }
+
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+ iowrite32(new_posted, &dc2c->wq_ctrl->posted_index);
+
+ if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT)
+ return 0;
+
+ result = dc2c->result + dc2c->next_result;
+ color = dc2c->color;
+
+ dc2c->next_result++;
+ if (dc2c->next_result == dc2c->result_size) {
+ dc2c->next_result = 0;
+ dc2c->color = dc2c->color ? 0 : 1;
+ }
+
+ for (delay = 0; delay < wait; delay++) {
+ udelay(100);
+ if (result->color == color) {
+ if (result->error) {
+ err = -(int) result->error;
+ if (err != ERR_ECMDUNKNOWN ||
+ cmd != CMD_CAPABILITY)
+ pr_err("%s:Error %d devcmd %d\n",
+ pci_name(vdev->pdev),
+ err, _CMD_N(cmd));
+ return err;
+ }
+ if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
+ rmb(); /*prevent reorder while reding result*/
+ for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+ vdev->args[i] = result->results[i];
+ }
+ return 0;
+ }
+ }
+
+ pr_err("%s:Timed out devcmd %d\n", pci_name(vdev->pdev), _CMD_N(cmd));
+
+ return -ETIMEDOUT;
+}
+
+
+static int vnic_dev_init_devcmd1(struct vnic_dev *vdev)
+{
+ vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0);
+ if (!vdev->devcmd)
+ return -ENODEV;
+
+ vdev->devcmd_rtn = &vnic_dev_cmd1;
+ return 0;
+}
+
+
+static int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
+{
+ int err;
+ unsigned int fetch_index;
+
+ if (vdev->devcmd2)
+ return 0;
+
+ vdev->devcmd2 = kzalloc(sizeof(*vdev->devcmd2), GFP_ATOMIC);
+ if (!vdev->devcmd2)
+ return -ENOMEM;
+
+ vdev->devcmd2->color = 1;
+ vdev->devcmd2->result_size = DEVCMD2_RING_SIZE;
+ err = vnic_wq_devcmd2_alloc(vdev, &vdev->devcmd2->wq,
+ DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE);
+ if (err)
+ goto err_free_devcmd2;
+
+ fetch_index = ioread32(&vdev->devcmd2->wq.ctrl->fetch_index);
+ if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone */
+ pr_err("error in devcmd2 init");
+ err = -ENODEV;
+ goto err_free_wq;
+ }
+
+ /*
+ * Don't change fetch_index ever and
+ * set posted_index same as fetch_index
+ * when setting up the WQ for devcmd2.
+ */
+ vnic_wq_init_start(&vdev->devcmd2->wq, 0, fetch_index,
+ fetch_index, 0, 0);
+
+ vnic_wq_enable(&vdev->devcmd2->wq);
+
+ err = vnic_dev_alloc_desc_ring(vdev, &vdev->devcmd2->results_ring,
+ DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE);
+ if (err)
+ goto err_disable_wq;
+
+ vdev->devcmd2->result =
+ (struct devcmd2_result *) vdev->devcmd2->results_ring.descs;
+ vdev->devcmd2->cmd_ring =
+ (struct vnic_devcmd2 *) vdev->devcmd2->wq.ring.descs;
+ vdev->devcmd2->wq_ctrl = vdev->devcmd2->wq.ctrl;
+ vdev->args[0] = (u64) vdev->devcmd2->results_ring.base_addr |
+ VNIC_PADDR_TARGET;
+ vdev->args[1] = DEVCMD2_RING_SIZE;
+
+ err = vnic_dev_cmd2(vdev, CMD_INITIALIZE_DEVCMD2, 1000);
+ if (err)
+ goto err_free_desc_ring;
+
+ vdev->devcmd_rtn = &vnic_dev_cmd2;
+
+ return 0;
+
+err_free_desc_ring:
+ vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
+err_disable_wq:
+ vnic_wq_disable(&vdev->devcmd2->wq);
+err_free_wq:
+ vnic_wq_free(&vdev->devcmd2->wq);
+err_free_devcmd2:
+ kfree(vdev->devcmd2);
+ vdev->devcmd2 = NULL;
+
+ return err;
+}
+
+
+static void vnic_dev_deinit_devcmd2(struct vnic_dev *vdev)
+{
+ vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
+ vnic_wq_disable(&vdev->devcmd2->wq);
+ vnic_wq_free(&vdev->devcmd2->wq);
+ kfree(vdev->devcmd2);
+ vdev->devcmd2 = NULL;
+ vdev->devcmd_rtn = &vnic_dev_cmd1;
+}
+
+
+static int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
+ enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait)
+{
+ int err;
+
+ vdev->args[0] = *a0;
+ vdev->args[1] = *a1;
+
+ err = (*vdev->devcmd_rtn)(vdev, cmd, wait);
+
+ *a0 = vdev->args[0];
+ *a1 = vdev->args[1];
+
+ return err;
+}
+
+
+int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ u64 *a0, u64 *a1, int wait)
+{
+ memset(vdev->args, 0, sizeof(vdev->args));
+
+ switch (vdev->proxy) {
+ case PROXY_NONE:
+ default:
+ return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait);
+ }
+}
+
+
int vnic_dev_fw_info(struct vnic_dev *vdev,
struct vnic_devcmd_fw_info **fw_info)
{
@@ -298,9 +535,9 @@ int vnic_dev_fw_info(struct vnic_dev *vdev,
int err = 0;
if (!vdev->fw_info) {
- vdev->fw_info = pci_alloc_consistent(vdev->pdev,
+ vdev->fw_info = dma_alloc_coherent(&vdev->pdev->dev,
sizeof(struct vnic_devcmd_fw_info),
- &vdev->fw_info_pa);
+ &vdev->fw_info_pa, GFP_KERNEL);
if (!vdev->fw_info)
return -ENOMEM;
@@ -361,8 +598,8 @@ int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats)
int wait = 1000;
if (!vdev->stats) {
- vdev->stats = pci_alloc_consistent(vdev->pdev,
- sizeof(struct vnic_stats), &vdev->stats_pa);
+ vdev->stats = dma_alloc_coherent(&vdev->pdev->dev,
+ sizeof(struct vnic_stats), &vdev->stats_pa, GFP_KERNEL);
if (!vdev->stats)
return -ENOMEM;
}
@@ -445,26 +682,26 @@ int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done)
int vnic_dev_hang_notify(struct vnic_dev *vdev)
{
- u64 a0, a1;
+ u64 a0 = 0, a1 = 0;
int wait = 1000;
return vnic_dev_cmd(vdev, CMD_HANG_NOTIFY, &a0, &a1, wait);
}
int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
{
- u64 a0, a1;
+ u64 a[2] = {};
int wait = 1000;
int err, i;
for (i = 0; i < ETH_ALEN; i++)
mac_addr[i] = 0;
- err = vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a0, &a1, wait);
+ err = vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a[0], &a[1], wait);
if (err)
return err;
for (i = 0; i < ETH_ALEN; i++)
- mac_addr[i] = ((u8 *)&a0)[i];
+ mac_addr[i] = ((u8 *)&a)[i];
return 0;
}
@@ -489,38 +726,32 @@ void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)
{
- u64 a0 = 0, a1 = 0;
+ u64 a[2] = {};
int wait = 1000;
int err;
int i;
for (i = 0; i < ETH_ALEN; i++)
- ((u8 *)&a0)[i] = addr[i];
+ ((u8 *)&a)[i] = addr[i];
- err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait);
+ err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a[0], &a[1], wait);
if (err)
- printk(KERN_ERR
- "Can't add addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
- err);
+ pr_err("Can't add addr [%pM], %d\n", addr, err);
}
void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr)
{
- u64 a0 = 0, a1 = 0;
+ u64 a[2] = {};
int wait = 1000;
int err;
int i;
for (i = 0; i < ETH_ALEN; i++)
- ((u8 *)&a0)[i] = addr[i];
+ ((u8 *)&a)[i] = addr[i];
- err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait);
+ err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a[0], &a[1], wait);
if (err)
- printk(KERN_ERR
- "Can't del addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
- err);
+ pr_err("Can't del addr [%pM], %d\n", addr, err);
}
int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
@@ -529,9 +760,9 @@ int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
int wait = 1000;
if (!vdev->notify) {
- vdev->notify = pci_alloc_consistent(vdev->pdev,
+ vdev->notify = dma_alloc_coherent(&vdev->pdev->dev,
sizeof(struct vnic_devcmd_notify),
- &vdev->notify_pa);
+ &vdev->notify_pa, GFP_KERNEL);
if (!vdev->notify)
return -ENOMEM;
}
@@ -584,6 +815,16 @@ int vnic_dev_init(struct vnic_dev *vdev, int arg)
return vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait);
}
+u16 vnic_dev_set_default_vlan(struct vnic_dev *vdev, u16 new_default_vlan)
+{
+ u64 a0 = new_default_vlan, a1 = 0;
+ int wait = 1000;
+ int old_vlan = 0;
+
+ old_vlan = vnic_dev_cmd(vdev, CMD_SET_DEFAULT_VLAN, &a0, &a1, wait);
+ return (u16)old_vlan;
+}
+
int vnic_dev_link_status(struct vnic_dev *vdev)
{
if (vdev->linkstatus)
@@ -643,23 +884,25 @@ void vnic_dev_unregister(struct vnic_dev *vdev)
{
if (vdev) {
if (vdev->notify)
- pci_free_consistent(vdev->pdev,
+ dma_free_coherent(&vdev->pdev->dev,
sizeof(struct vnic_devcmd_notify),
vdev->notify,
vdev->notify_pa);
if (vdev->linkstatus)
- pci_free_consistent(vdev->pdev,
+ dma_free_coherent(&vdev->pdev->dev,
sizeof(u32),
vdev->linkstatus,
vdev->linkstatus_pa);
if (vdev->stats)
- pci_free_consistent(vdev->pdev,
+ dma_free_coherent(&vdev->pdev->dev,
sizeof(struct vnic_stats),
vdev->stats, vdev->stats_pa);
if (vdev->fw_info)
- pci_free_consistent(vdev->pdev,
+ dma_free_coherent(&vdev->pdev->dev,
sizeof(struct vnic_devcmd_fw_info),
vdev->fw_info, vdev->fw_info_pa);
+ if (vdev->devcmd2)
+ vnic_dev_deinit_devcmd2(vdev);
kfree(vdev);
}
}
@@ -679,13 +922,26 @@ struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev,
if (vnic_dev_discover_res(vdev, bar))
goto err_out;
- vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0);
- if (!vdev->devcmd)
- goto err_out;
-
return vdev;
err_out:
vnic_dev_unregister(vdev);
return NULL;
}
+
+int vnic_dev_cmd_init(struct vnic_dev *vdev)
+{
+ int err;
+ void *p;
+
+ p = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0);
+ if (p) {
+ pr_err("fnic: DEVCMD2 resource found!\n");
+ err = vnic_dev_init_devcmd2(vdev);
+ } else {
+ pr_err("fnic: DEVCMD2 not found, fall back to Devcmd\n");
+ err = vnic_dev_init_devcmd1(vdev);
+ }
+
+ return err;
+}
diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h
index f9935a8a5a09..7a568d141cde 100644
--- a/drivers/scsi/fnic/vnic_dev.h
+++ b/drivers/scsi/fnic/vnic_dev.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_DEV_H_
#define _VNIC_DEV_H_
@@ -36,6 +24,7 @@
#define vnic_dev_fw_info fnic_dev_fw_info
#define vnic_dev_spec fnic_dev_spec
#define vnic_dev_stats_clear fnic_dev_stats_clear
+#define vnic_dev_cmd_init fnic_dev_cmd_init
#define vnic_dev_stats_dump fnic_dev_stats_dump
#define vnic_dev_hang_notify fnic_dev_hang_notify
#define vnic_dev_packet_filter fnic_dev_packet_filter
@@ -128,6 +117,7 @@ int vnic_dev_fw_info(struct vnic_dev *vdev,
int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset,
unsigned int size, void *value);
int vnic_dev_stats_clear(struct vnic_dev *vdev);
+int vnic_dev_cmd_init(struct vnic_dev *vdev);
int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);
int vnic_dev_hang_notify(struct vnic_dev *vdev);
void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
@@ -148,6 +138,8 @@ int vnic_dev_disable(struct vnic_dev *vdev);
int vnic_dev_open(struct vnic_dev *vdev, int arg);
int vnic_dev_open_done(struct vnic_dev *vdev, int *done);
int vnic_dev_init(struct vnic_dev *vdev, int arg);
+u16 vnic_dev_set_default_vlan(struct vnic_dev *vdev,
+ u16 new_default_vlan);
int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg);
int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done);
void vnic_dev_set_intr_mode(struct vnic_dev *vdev,
diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h
index 7c9ccbd4134b..f876d223b2b4 100644
--- a/drivers/scsi/fnic/vnic_devcmd.h
+++ b/drivers/scsi/fnic/vnic_devcmd.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_DEVCMD_H_
#define _VNIC_DEVCMD_H_
@@ -170,7 +158,8 @@ enum vnic_devcmd_cmd {
/* variant of CMD_INIT, with provisioning info
* (u64)a0=paddr of vnic_devcmd_provinfo
- * (u32)a1=sizeof provision info */
+ * (u32)a1=sizeof provision info
+ */
CMD_INIT_PROV_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 27),
/* enable virtual link */
@@ -196,11 +185,198 @@ enum vnic_devcmd_cmd {
/* undo initialize of virtual link */
CMD_DEINIT = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 34),
+
+ /* check fw capability of a cmd:
+ * in: (u32)a0=cmd
+ * out: (u32)a0=errno, 0:valid cmd, a1=supported VNIC_STF_* bits */
+ CMD_CAPABILITY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 36),
+
+ /* persistent binding info
+ * in: (u64)a0=paddr of arg
+ * (u32)a1=CMD_PERBI_XXX */
+ CMD_PERBI = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_FC, 37),
+
+ /* Interrupt Assert Register functionality
+ * in: (u16)a0=interrupt number to assert
+ */
+ CMD_IAR = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 38),
+
+ /* initiate hangreset, like softreset after hang detected */
+ CMD_HANG_RESET = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 39),
+
+ /* hangreset status:
+ * out: a0=0 reset complete, a0=1 reset in progress */
+ CMD_HANG_RESET_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 40),
+
+ /*
+ * Set hw ingress packet vlan rewrite mode:
+ * in: (u32)a0=new vlan rewrite mode
+ * out: (u32)a0=old vlan rewrite mode */
+ CMD_IG_VLAN_REWRITE_MODE = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 41),
+
+ /*
+ * in: (u16)a0=bdf of target vnic
+ * (u32)a1=cmd to proxy
+ * a2-a15=args to cmd in a1
+ * out: (u32)a0=status of proxied cmd
+ * a1-a15=out args of proxied cmd */
+ CMD_PROXY_BY_BDF = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 42),
+
+ /*
+ * As for BY_BDF except a0 is index of hvnlink subordinate vnic
+ * or SR-IOV virtual vnic
+ */
+ CMD_PROXY_BY_INDEX = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 43),
+
+ /*
+ * For HPP toggle:
+ * adapter-info-get
+ * in: (u64)a0=phsical address of buffer passed in from caller.
+ * (u16)a1=size of buffer specified in a0.
+ * out: (u64)a0=phsical address of buffer passed in from caller.
+ * (u16)a1=actual bytes from VIF-CONFIG-INFO TLV, or
+ * 0 if no VIF-CONFIG-INFO TLV was ever received. */
+ CMD_CONFIG_INFO_GET = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 44),
+
+ /*
+ * INT13 API: (u64)a0=paddr to vnic_int13_params struct
+ * (u32)a1=INT13_CMD_xxx
+ */
+ CMD_INT13_ALL = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 45),
+
+ /*
+ * Set default vlan:
+ * in: (u16)a0=new default vlan
+ * (u16)a1=zero for overriding vlan with param a0,
+ * non-zero for resetting vlan to the default
+ * out: (u16)a0=old default vlan
+ */
+ CMD_SET_DEFAULT_VLAN = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 46),
+
+ /* init_prov_info2:
+ * Variant of CMD_INIT_PROV_INFO, where it will not try to enable
+ * the vnic until CMD_ENABLE2 is issued.
+ * (u64)a0=paddr of vnic_devcmd_provinfo
+ * (u32)a1=sizeof provision info
+ */
+ CMD_INIT_PROV_INFO2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 47),
+
+ /* enable2:
+ * (u32)a0=0 ==> standby
+ * =CMD_ENABLE2_ACTIVE ==> active
+ */
+ CMD_ENABLE2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 48),
+
+ /*
+ * cmd_status:
+ * Returns the status of the specified command
+ * Input:
+ * a0 = command for which status is being queried.
+ * Possible values are:
+ * CMD_SOFT_RESET
+ * CMD_HANG_RESET
+ * CMD_OPEN
+ * CMD_INIT
+ * CMD_INIT_PROV_INFO
+ * CMD_DEINIT
+ * CMD_INIT_PROV_INFO2
+ * CMD_ENABLE2
+ * Output:
+ * if status == STAT_ERROR
+ * a0 = ERR_ENOTSUPPORTED - status for command in a0 is
+ * not supported
+ * if status == STAT_NONE
+ * a0 = status of the devcmd specified in a0 as follows.
+ * ERR_SUCCESS - command in a0 completed successfully
+ * ERR_EINPROGRESS - command in a0 is still in progress
+ */
+ CMD_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 49),
+
+ /*
+ * Returns interrupt coalescing timer conversion factors.
+ * After calling this devcmd, ENIC driver can convert
+ * interrupt coalescing timer in usec into CPU cycles as follows:
+ *
+ * intr_timer_cycles = intr_timer_usec * multiplier / divisor
+ *
+ * Interrupt coalescing timer in usecs can be be converted/obtained
+ * from CPU cycles as follows:
+ *
+ * intr_timer_usec = intr_timer_cycles * divisor / multiplier
+ *
+ * in: none
+ * out: (u32)a0 = multiplier
+ * (u32)a1 = divisor
+ * (u32)a2 = maximum timer value in usec
+ */
+ CMD_INTR_COAL_CONVERT = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 50),
+
+ /*
+ * ISCSI DUMP API:
+ * in: (u64)a0=paddr of the param or param itself
+ * (u32)a1=ISCSI_CMD_xxx
+ */
+ CMD_ISCSI_DUMP_REQ = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 51),
+
+ /*
+ * ISCSI DUMP STATUS API:
+ * in: (u32)a0=cmd tag
+ * in: (u32)a1=ISCSI_CMD_xxx
+ * out: (u32)a0=cmd status
+ */
+ CMD_ISCSI_DUMP_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 52),
+
+ /*
+ * Subvnic migration from MQ <--> VF.
+ * Enable the LIF migration from MQ to VF and vice versa. MQ and VF
+ * indexes are statically bound at the time of initialization.
+ * Based on the
+ * direction of migration, the resources of either MQ or the VF shall
+ * be attached to the LIF.
+ * in: (u32)a0=Direction of Migration
+ * 0=> Migrate to VF
+ * 1=> Migrate to MQ
+ * (u32)a1=VF index (MQ index)
+ */
+ CMD_MIGRATE_SUBVNIC = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 53),
+
+ /*
+ * Register / Deregister the notification block for MQ subvnics
+ * in:
+ * (u64)a0=paddr to notify (set paddr=0 to unset)
+ * (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify)
+ * (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr)
+ * out:
+ * (u32)a1 = effective size
+ */
+ CMD_SUBVNIC_NOTIFY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 54),
+
+ /*
+ * Set the predefined mac address as default
+ * in:
+ * (u48)a0=mac addr
+ */
+ CMD_SET_MAC_ADDR = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 55),
+
+ /* Update the provisioning info of the given VIF
+ * (u64)a0=paddr of vnic_devcmd_provinfo
+ * (u32)a1=sizeof provision info
+ */
+ CMD_PROV_INFO_UPDATE = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 56),
+
+ /*
+ * Initialization for the devcmd2 interface.
+ * in: (u64) a0=host result buffer physical address
+ * in: (u16) a1=number of entries in result buffer
+ */
+ CMD_INITIALIZE_DEVCMD2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 57)
};
/* flags for CMD_OPEN */
#define CMD_OPENF_OPROM 0x1 /* open coming from option rom */
+#define CMD_OPENF_RQ_ENABLE_THEN_POST 0x2
+
/* flags for CMD_INIT */
#define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */
@@ -254,7 +430,7 @@ struct vnic_devcmd_notify {
struct vnic_devcmd_provinfo {
u8 oui[3];
u8 type;
- u8 data[0];
+ u8 data[];
};
/*
@@ -278,4 +454,39 @@ struct vnic_devcmd {
u64 args[VNIC_DEVCMD_NARGS]; /* RW cmd args (little-endian) */
};
+/*
+ * Version 2 of the interface.
+ *
+ * Some things are carried over, notably the vnic_devcmd_cmd enum.
+ */
+
+/*
+ * Flags for vnic_devcmd2.flags
+ */
+
+#define DEVCMD2_FNORESULT 0x1 /* Don't copy result to host */
+
+#define VNIC_DEVCMD2_NARGS VNIC_DEVCMD_NARGS
+
+struct vnic_devcmd2 {
+ u16 pad;
+ u16 flags;
+ u32 cmd; /* same command #defines as original */
+ u64 args[VNIC_DEVCMD2_NARGS];
+};
+
+#define VNIC_DEVCMD2_NRESULTS VNIC_DEVCMD_NARGS
+struct devcmd2_result {
+ u64 results[VNIC_DEVCMD2_NRESULTS];
+ u32 pad;
+ u16 completed_index; /* into copy WQ */
+ u8 error; /* same error codes as original */
+ u8 color; /* 0 or 1 as with completion queues */
+};
+
+#define DEVCMD2_RING_SIZE 32
+#define DEVCMD2_DESC_SIZE 128
+
+#define DEVCMD2_RESULTS_SIZE_MAX ((1 << 16) - 1)
+
#endif /* _VNIC_DEVCMD_H_ */
diff --git a/drivers/scsi/fnic/vnic_intr.c b/drivers/scsi/fnic/vnic_intr.c
index 4f4dc8793d23..df7f63acd879 100644
--- a/drivers/scsi/fnic/vnic_intr.c
+++ b/drivers/scsi/fnic/vnic_intr.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/kernel.h>
diff --git a/drivers/scsi/fnic/vnic_intr.h b/drivers/scsi/fnic/vnic_intr.h
index d5fb40e7c98e..acc194c0f522 100644
--- a/drivers/scsi/fnic/vnic_intr.h
+++ b/drivers/scsi/fnic/vnic_intr.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_INTR_H_
#define _VNIC_INTR_H_
diff --git a/drivers/scsi/fnic/vnic_nic.h b/drivers/scsi/fnic/vnic_nic.h
index f15b83eeaced..6896f16d564b 100644
--- a/drivers/scsi/fnic/vnic_nic.h
+++ b/drivers/scsi/fnic/vnic_nic.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_NIC_H_
#define _VNIC_NIC_H_
diff --git a/drivers/scsi/fnic/vnic_resource.h b/drivers/scsi/fnic/vnic_resource.h
index 2d842f79d41a..3d260b831fc5 100644
--- a/drivers/scsi/fnic/vnic_resource.h
+++ b/drivers/scsi/fnic/vnic_resource.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_RESOURCE_H_
#define _VNIC_RESOURCE_H_
@@ -41,6 +29,13 @@ enum vnic_res_type {
RES_TYPE_RSVD7,
RES_TYPE_DEVCMD, /* Device command region */
RES_TYPE_PASS_THRU_PAGE, /* Pass-thru page */
+ RES_TYPE_SUBVNIC, /* subvnic resource type */
+ RES_TYPE_MQ_WQ, /* MQ Work queues */
+ RES_TYPE_MQ_RQ, /* MQ Receive queues */
+ RES_TYPE_MQ_CQ, /* MQ Completion queues */
+ RES_TYPE_DEPRECATED1, /* Old version of devcmd 2 */
+ RES_TYPE_DEPRECATED2, /* Old version of devcmd 2 */
+ RES_TYPE_DEVCMD2, /* Device control region */
RES_TYPE_MAX, /* Count of resource types */
};
diff --git a/drivers/scsi/fnic/vnic_rq.c b/drivers/scsi/fnic/vnic_rq.c
index fd2068f5ae16..350607d13c9a 100644
--- a/drivers/scsi/fnic/vnic_rq.c
+++ b/drivers/scsi/fnic/vnic_rq.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/errno.h>
@@ -27,12 +15,9 @@
static int vnic_rq_alloc_bufs(struct vnic_rq *rq)
{
struct vnic_rq_buf *buf;
- struct vnic_dev *vdev;
unsigned int i, j, count = rq->ring.desc_count;
unsigned int blks = VNIC_RQ_BUF_BLKS_NEEDED(count);
- vdev = rq->vdev;
-
for (i = 0; i < blks; i++) {
rq->bufs[i] = kzalloc(VNIC_RQ_BUF_BLK_SZ, GFP_ATOMIC);
if (!rq->bufs[i]) {
@@ -171,7 +156,7 @@ void vnic_rq_clean(struct vnic_rq *rq,
struct vnic_rq_buf *buf;
u32 fetch_index;
- BUG_ON(ioread32(&rq->ctrl->enable));
+ WARN_ON(ioread32(&rq->ctrl->enable));
buf = rq->to_clean;
@@ -194,4 +179,3 @@ void vnic_rq_clean(struct vnic_rq *rq,
vnic_dev_clear_desc_ring(&rq->ring);
}
-
diff --git a/drivers/scsi/fnic/vnic_rq.h b/drivers/scsi/fnic/vnic_rq.h
index aebdfbd6ad3c..1066255de808 100644
--- a/drivers/scsi/fnic/vnic_rq.h
+++ b/drivers/scsi/fnic/vnic_rq.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_RQ_H_
#define _VNIC_RQ_H_
diff --git a/drivers/scsi/fnic/vnic_scsi.h b/drivers/scsi/fnic/vnic_scsi.h
index fbb55364e272..f715f7942bfe 100644
--- a/drivers/scsi/fnic/vnic_scsi.h
+++ b/drivers/scsi/fnic/vnic_scsi.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_SCSI_H_
#define _VNIC_SCSI_H_
@@ -38,7 +26,7 @@
#define VNIC_FNIC_RATOV_MAX 255000
#define VNIC_FNIC_MAXDATAFIELDSIZE_MIN 256
-#define VNIC_FNIC_MAXDATAFIELDSIZE_MAX 2112
+#define VNIC_FNIC_MAXDATAFIELDSIZE_MAX 2048
#define VNIC_FNIC_FLOGI_RETRIES_MIN 0
#define VNIC_FNIC_FLOGI_RETRIES_MAX 0xffffffff
@@ -54,8 +42,8 @@
#define VNIC_FNIC_PLOGI_TIMEOUT_MIN 1000
#define VNIC_FNIC_PLOGI_TIMEOUT_MAX 255000
-#define VNIC_FNIC_IO_THROTTLE_COUNT_MIN 256
-#define VNIC_FNIC_IO_THROTTLE_COUNT_MAX 4096
+#define VNIC_FNIC_IO_THROTTLE_COUNT_MIN 1
+#define VNIC_FNIC_IO_THROTTLE_COUNT_MAX 2048
#define VNIC_FNIC_LINK_DOWN_TIMEOUT_MIN 0
#define VNIC_FNIC_LINK_DOWN_TIMEOUT_MAX 240000
@@ -67,7 +55,7 @@
#define VNIC_FNIC_PORT_DOWN_IO_RETRIES_MAX 255
#define VNIC_FNIC_LUNS_PER_TARGET_MIN 1
-#define VNIC_FNIC_LUNS_PER_TARGET_MAX 1024
+#define VNIC_FNIC_LUNS_PER_TARGET_MAX 4096
/* Device-specific region: scsi configuration */
struct vnic_fc_config {
@@ -91,10 +79,19 @@ struct vnic_fc_config {
u16 ra_tov;
u16 intr_timer;
u8 intr_timer_type;
+ u8 intr_mode;
+ u8 lun_queue_depth;
+ u8 io_timeout_retry;
+ u16 wq_copy_count;
};
#define VFCF_FCP_SEQ_LVL_ERR 0x1 /* Enable FCP-2 Error Recovery */
#define VFCF_PERBI 0x2 /* persistent binding info available */
#define VFCF_FIP_CAPABLE 0x4 /* firmware can handle FIP */
+#define VFCF_FC_INITIATOR 0x20 /* FC Initiator Mode */
+#define VFCF_FC_TARGET 0x40 /* FC Target Mode */
+#define VFCF_FC_NVME_INITIATOR 0x80 /* FC-NVMe Initiator Mode */
+#define VFCF_FC_NVME_TARGET 0x100 /* FC-NVMe Target Mode */
+
#endif /* _VNIC_SCSI_H_ */
diff --git a/drivers/scsi/fnic/vnic_stats.h b/drivers/scsi/fnic/vnic_stats.h
index 5372e23c1cb3..4396397b089d 100644
--- a/drivers/scsi/fnic/vnic_stats.h
+++ b/drivers/scsi/fnic/vnic_stats.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_STATS_H_
#define _VNIC_STATS_H_
diff --git a/drivers/scsi/fnic/vnic_wq.c b/drivers/scsi/fnic/vnic_wq.c
index a414135460db..426b901c80f3 100644
--- a/drivers/scsi/fnic/vnic_wq.c
+++ b/drivers/scsi/fnic/vnic_wq.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/errno.h>
@@ -24,15 +12,32 @@
#include "vnic_dev.h"
#include "vnic_wq.h"
+
+static int vnic_wq_get_ctrl(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, enum vnic_res_type res_type)
+{
+ wq->ctrl = vnic_dev_get_res(vdev, res_type, index);
+
+ if (!wq->ctrl)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int vnic_wq_alloc_ring(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ return vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size);
+}
+
+
static int vnic_wq_alloc_bufs(struct vnic_wq *wq)
{
struct vnic_wq_buf *buf;
- struct vnic_dev *vdev;
unsigned int i, j, count = wq->ring.desc_count;
unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count);
- vdev = wq->vdev;
-
for (i = 0; i < blks; i++) {
wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC);
if (!wq->bufs[i]) {
@@ -111,6 +116,52 @@ int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
return 0;
}
+
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ wq->index = 0;
+ wq->vdev = vdev;
+
+ err = vnic_wq_get_ctrl(vdev, wq, 0, RES_TYPE_DEVCMD2);
+ if (err) {
+ pr_err("Failed to get devcmd2 resource\n");
+ return err;
+ }
+ vnic_wq_disable(wq);
+
+ err = vnic_wq_alloc_ring(vdev, wq, desc_count, desc_size);
+ if (err)
+ return err;
+ return 0;
+}
+
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int fetch_index, unsigned int posted_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset)
+{
+ u64 paddr;
+ unsigned int count = wq->ring.desc_count;
+
+ paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &wq->ctrl->ring_base);
+ iowrite32(count, &wq->ctrl->ring_size);
+ iowrite32(fetch_index, &wq->ctrl->fetch_index);
+ iowrite32(posted_index, &wq->ctrl->posted_index);
+ iowrite32(cq_index, &wq->ctrl->cq_index);
+ iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable);
+ iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset);
+ iowrite32(0, &wq->ctrl->error_status);
+
+ wq->to_use = wq->to_clean =
+ &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES]
+ [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES];
+}
+
+
void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
unsigned int error_interrupt_enable,
unsigned int error_interrupt_offset)
diff --git a/drivers/scsi/fnic/vnic_wq.h b/drivers/scsi/fnic/vnic_wq.h
index 5cd094f79281..041618e13ce2 100644
--- a/drivers/scsi/fnic/vnic_wq.h
+++ b/drivers/scsi/fnic/vnic_wq.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_WQ_H_
#define _VNIC_WQ_H_
@@ -33,6 +21,8 @@
#define vnic_wq_service fnic_wq_service
#define vnic_wq_free fnic_wq_free
#define vnic_wq_alloc fnic_wq_alloc
+#define vnic_wq_devcmd2_alloc fnic_wq_devcmd2_alloc
+#define vnic_wq_init_start fnic_wq_init_start
#define vnic_wq_init fnic_wq_init
#define vnic_wq_error_status fnic_wq_error_status
#define vnic_wq_enable fnic_wq_enable
@@ -163,6 +153,12 @@ static inline void vnic_wq_service(struct vnic_wq *wq,
void vnic_wq_free(struct vnic_wq *wq);
int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
unsigned int desc_count, unsigned int desc_size);
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size);
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int fetch_index, unsigned int posted_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset);
void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
unsigned int error_interrupt_enable,
unsigned int error_interrupt_offset);
diff --git a/drivers/scsi/fnic/vnic_wq_copy.c b/drivers/scsi/fnic/vnic_wq_copy.c
index 9eab7e7caf38..96569d4ccc58 100644
--- a/drivers/scsi/fnic/vnic_wq_copy.c
+++ b/drivers/scsi/fnic/vnic_wq_copy.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include <linux/errno.h>
@@ -79,8 +67,6 @@ int vnic_wq_copy_alloc(struct vnic_dev *vdev, struct vnic_wq_copy *wq,
unsigned int index, unsigned int desc_count,
unsigned int desc_size)
{
- int err;
-
wq->index = index;
wq->vdev = vdev;
wq->to_use_index = wq->to_clean_index = 0;
@@ -92,11 +78,7 @@ int vnic_wq_copy_alloc(struct vnic_dev *vdev, struct vnic_wq_copy *wq,
vnic_wq_copy_disable(wq);
- err = vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size);
- if (err)
- return err;
-
- return 0;
+ return vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size);
}
void vnic_wq_copy_init(struct vnic_wq_copy *wq, unsigned int cq_index,
@@ -114,4 +96,3 @@ void vnic_wq_copy_init(struct vnic_wq_copy *wq, unsigned int cq_index,
iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable);
iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset);
}
-
diff --git a/drivers/scsi/fnic/vnic_wq_copy.h b/drivers/scsi/fnic/vnic_wq_copy.h
index 6aff9740c3df..2f8340144e79 100644
--- a/drivers/scsi/fnic/vnic_wq_copy.h
+++ b/drivers/scsi/fnic/vnic_wq_copy.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _VNIC_WQ_COPY_H_
#define _VNIC_WQ_COPY_H_
diff --git a/drivers/scsi/fnic/wq_enet_desc.h b/drivers/scsi/fnic/wq_enet_desc.h
index b121cbad18b8..9a933a5dee79 100644
--- a/drivers/scsi/fnic/wq_enet_desc.h
+++ b/drivers/scsi/fnic/wq_enet_desc.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifndef _WQ_ENET_DESC_H_
#define _WQ_ENET_DESC_H_
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index 81182badfeb1..270eae7ac427 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -1,17 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Generic Generic NCR5380 driver
- *
+ *
* Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 440-4894
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
*
* NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
- * K.Lentin@cs.monash.edu.au
+ * K.Lentin@cs.monash.edu.au
*
* NCR53C400A extensions (c) 1996, Ingmar Baumgart
- * ingmar@gonzo.schwaben.de
+ * ingmar@gonzo.schwaben.de
*
* DTC3181E extensions (c) 1997, Ronald van Cuijlenborg
* ronald.van.cuijlenborg@tip.nl or nutty@dds.nl
@@ -19,939 +20,800 @@
* Added ISAPNP support for DTC436 adapters,
* Thomas Sailer, sailer@ife.ee.ethz.ch
*
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * TODO : flesh out DMA support, find some one actually using this (I have
- * a memory mapped Trantor board that works fine)
+ * See Documentation/scsi/g_NCR5380.rst for more info.
*/
-/*
- * Options :
- *
- * PARITY - enable parity checking. Not supported.
- *
- * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- *
- * USLEEP - enable support for devices that don't disconnect. Untested.
- *
- * The card is detected and initialized in one of several ways :
- * 1. With command line overrides - NCR5380=port,irq may be
- * used on the LILO command line to override the defaults.
- *
- * 2. With the GENERIC_NCR5380_OVERRIDE compile time define. This is
- * specified as an array of address, irq, dma, board tuples. Ie, for
- * one board at 0x350, IRQ5, no dma, I could say
- * -DGENERIC_NCR5380_OVERRIDE={{0xcc000, 5, DMA_NONE, BOARD_NCR5380}}
- *
- * -1 should be specified for no or DMA interrupt, -2 to autoprobe for an
- * IRQ line if overridden on the command line.
- *
- * 3. When included as a module, with arguments passed on the command line:
- * ncr_irq=xx the interrupt
- * ncr_addr=xx the port or base address (for port or memory
- * mapped, resp.)
- * ncr_dma=xx the DMA
- * ncr_5380=1 to set up for a NCR5380 board
- * ncr_53c400=1 to set up for a NCR53C400 board
- * e.g.
- * modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
- * for a port mapped NCR5380 board or
- * modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
- * for a memory mapped NCR53C400 board with interrupts disabled.
- *
- * 255 should be specified for no or DMA interrupt, 254 to autoprobe for an
- * IRQ line if overridden on the command line.
- *
- */
-
-/*
- * $Log: generic_NCR5380.c,v $
- */
-
-/* settings for DTC3181E card with only Mustek scanner attached */
-#define USLEEP
-#define USLEEP_POLL 1
-#define USLEEP_SLEEP 20
-#define USLEEP_WAITLONG 500
-
-#define AUTOPROBE_IRQ
-#define AUTOSENSE
-
-
-#ifdef CONFIG_SCSI_GENERIC_NCR53C400
-#define NCR53C400_PSEUDO_DMA 1
-#define PSEUDO_DMA
-#define NCR53C400
-#define NCR5380_STATS
-#undef NCR5380_STAT_LIMIT
-#endif
-
-#include <asm/system.h>
#include <asm/io.h>
-#include <linux/signal.h>
#include <linux/blkdev.h>
-#include "scsi.h"
+#include <linux/module.h>
#include <scsi/scsi_host.h>
-#include "g_NCR5380.h"
-#include "NCR5380.h"
-#include <linux/stat.h>
#include <linux/init.h>
#include <linux/ioport.h>
-#include <linux/isapnp.h>
-#include <linux/delay.h>
+#include <linux/isa.h>
+#include <linux/pnp.h>
#include <linux/interrupt.h>
-#define NCR_NOT_SET 0
-static int ncr_irq = NCR_NOT_SET;
-static int ncr_dma = NCR_NOT_SET;
-static int ncr_addr = NCR_NOT_SET;
-static int ncr_5380 = NCR_NOT_SET;
-static int ncr_53c400 = NCR_NOT_SET;
-static int ncr_53c400a = NCR_NOT_SET;
-static int dtc_3181e = NCR_NOT_SET;
-
-static struct override {
- NCR5380_map_type NCR5380_map_name;
- int irq;
- int dma;
- int board; /* Use NCR53c400, Ricoh, etc. extensions ? */
-} overrides
-#ifdef GENERIC_NCR5380_OVERRIDE
-[] __initdata = GENERIC_NCR5380_OVERRIDE;
-#else
-[1] __initdata = { { 0,},};
-#endif
+/* Definitions for the core NCR5380 driver. */
-#define NO_OVERRIDES ARRAY_SIZE(overrides)
+#define NCR5380_read(reg) \
+ ioread8(hostdata->io + hostdata->offset + (reg))
+#define NCR5380_write(reg, value) \
+ iowrite8(value, hostdata->io + hostdata->offset + (reg))
-#ifndef MODULE
+#define NCR5380_implementation_fields \
+ int offset; \
+ int c400_ctl_status; \
+ int c400_blk_cnt; \
+ int c400_host_buf; \
+ int io_width; \
+ int pdma_residual; \
+ int board
-/**
- * internal_setup - handle lilo command string override
- * @board: BOARD_* identifier for the board
- * @str: unused
- * @ints: numeric parameters
- *
- * Do LILO command line initialization of the overrides array. Display
- * errors when needed
- *
- * Locks: none
- */
+#define NCR5380_dma_xfer_len generic_NCR5380_dma_xfer_len
+#define NCR5380_dma_recv_setup generic_NCR5380_precv
+#define NCR5380_dma_send_setup generic_NCR5380_psend
+#define NCR5380_dma_residual generic_NCR5380_dma_residual
-static void __init internal_setup(int board, char *str, int *ints)
-{
- static int commandline_current = 0;
- switch (board) {
- case BOARD_NCR5380:
- if (ints[0] != 2 && ints[0] != 3) {
- printk(KERN_ERR "generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
- return;
- }
- break;
- case BOARD_NCR53C400:
- if (ints[0] != 2) {
- printk(KERN_ERR "generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
- return;
- }
- break;
- case BOARD_NCR53C400A:
- if (ints[0] != 2) {
- printk(KERN_ERR "generic_NCR53C400A_setup : usage ncr53c400a=" STRVAL(NCR5380_map_name) ",irq\n");
- return;
- }
- break;
- case BOARD_DTC3181E:
- if (ints[0] != 2) {
- printk("generic_DTC3181E_setup : usage dtc3181e=" STRVAL(NCR5380_map_name) ",irq\n");
- return;
- }
- break;
- }
+#define NCR5380_intr generic_NCR5380_intr
+#define NCR5380_queue_command generic_NCR5380_queue_command
+#define NCR5380_abort generic_NCR5380_abort
+#define NCR5380_host_reset generic_NCR5380_host_reset
+#define NCR5380_info generic_NCR5380_info
- if (commandline_current < NO_OVERRIDES) {
- overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type) ints[1];
- overrides[commandline_current].irq = ints[2];
- if (ints[0] == 3)
- overrides[commandline_current].dma = ints[3];
- else
- overrides[commandline_current].dma = DMA_NONE;
- overrides[commandline_current].board = board;
- ++commandline_current;
- }
-}
+#define NCR5380_io_delay(x) udelay(x)
+#include "NCR5380.h"
-/**
- * do_NCR53C80_setup - set up entry point
- * @str: unused
- *
- * Setup function invoked at boot to parse the ncr5380= command
- * line.
- */
+#define DRV_MODULE_NAME "g_NCR5380"
+
+#define NCR53C400_mem_base 0x3880
+#define NCR53C400_host_buffer 0x3900
+#define NCR53C400_region_size 0x3a00
+
+#define BOARD_NCR5380 0
+#define BOARD_NCR53C400 1
+#define BOARD_NCR53C400A 2
+#define BOARD_DTC3181E 3
+#define BOARD_HP_C2502 4
+
+#define IRQ_AUTO 254
+
+#define MAX_CARDS 8
+#define DMA_MAX_SIZE 32768
+
+/* old-style parameters for compatibility */
+static int ncr_irq = -1;
+static int ncr_addr;
+static int ncr_5380;
+static int ncr_53c400;
+static int ncr_53c400a;
+static int dtc_3181e;
+static int hp_c2502;
+module_param_hw(ncr_irq, int, irq, 0);
+module_param_hw(ncr_addr, int, ioport, 0);
+module_param(ncr_5380, int, 0);
+module_param(ncr_53c400, int, 0);
+module_param(ncr_53c400a, int, 0);
+module_param(dtc_3181e, int, 0);
+module_param(hp_c2502, int, 0);
-static int __init do_NCR5380_setup(char *str)
-{
- int ints[10];
+static int irq[] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+module_param_hw_array(irq, int, irq, NULL, 0);
+MODULE_PARM_DESC(irq, "IRQ number(s) (0=none, 254=auto [default])");
- get_options(str, ARRAY_SIZE(ints), ints);
- internal_setup(BOARD_NCR5380, str, ints);
- return 1;
-}
+static int base[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+module_param_hw_array(base, int, ioport, NULL, 0);
+MODULE_PARM_DESC(base, "base address(es)");
-/**
- * do_NCR53C400_setup - set up entry point
- * @str: unused
- * @ints: integer parameters from kernel setup code
- *
- * Setup function invoked at boot to parse the ncr53c400= command
- * line.
- */
+static int card[] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+module_param_array(card, int, NULL, 0);
+MODULE_PARM_DESC(card, "card type (0=NCR5380, 1=NCR53C400, 2=NCR53C400A, 3=DTC3181E, 4=HP C2502)");
-static int __init do_NCR53C400_setup(char *str)
+MODULE_ALIAS("g_NCR5380_mmio");
+MODULE_DESCRIPTION("Generic NCR5380/NCR53C400 SCSI driver");
+MODULE_LICENSE("GPL");
+
+static void g_NCR5380_trigger_irq(struct Scsi_Host *instance)
{
- int ints[10];
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
- get_options(str, ARRAY_SIZE(ints), ints);
- internal_setup(BOARD_NCR53C400, str, ints);
- return 1;
+ /*
+ * An interrupt is triggered whenever BSY = false, SEL = true
+ * and a bit set in the SELECT_ENABLE_REG is asserted on the
+ * SCSI bus.
+ *
+ * Note that the bus is only driven when the phase control signals
+ * (I/O, C/D, and MSG) match those in the TCR.
+ */
+ NCR5380_write(TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(INITIATOR_COMMAND_REG,
+ ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL);
+
+ msleep(1);
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
}
/**
- * do_NCR53C400A_setup - set up entry point
- * @str: unused
- * @ints: integer parameters from kernel setup code
+ * g_NCR5380_probe_irq - find the IRQ of a NCR5380 or equivalent
+ * @instance: SCSI host instance
*
- * Setup function invoked at boot to parse the ncr53c400a= command
- * line.
+ * Autoprobe for the IRQ line used by the card by triggering an IRQ
+ * and then looking to see what interrupt actually turned up.
*/
-static int __init do_NCR53C400A_setup(char *str)
+static int g_NCR5380_probe_irq(struct Scsi_Host *instance)
{
- int ints[10];
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ int irq_mask, irq;
- get_options(str, ARRAY_SIZE(ints), ints);
- internal_setup(BOARD_NCR53C400A, str, ints);
- return 1;
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ irq_mask = probe_irq_on();
+ g_NCR5380_trigger_irq(instance);
+ irq = probe_irq_off(irq_mask);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+
+ if (irq <= 0)
+ return NO_IRQ;
+ return irq;
}
-/**
- * do_DTC3181E_setup - set up entry point
- * @str: unused
- * @ints: integer parameters from kernel setup code
- *
- * Setup function invoked at boot to parse the dtc3181e= command
- * line.
+/*
+ * Configure I/O address of 53C400A or DTC436 by writing magic numbers
+ * to ports 0x779 and 0x379.
*/
-
-static int __init do_DTC3181E_setup(char *str)
+static void magic_configure(int idx, u8 irq, u8 magic[])
{
- int ints[10];
+ u8 cfg = 0;
- get_options(str, ARRAY_SIZE(ints), ints);
- internal_setup(BOARD_DTC3181E, str, ints);
- return 1;
+ outb(magic[0], 0x779);
+ outb(magic[1], 0x379);
+ outb(magic[2], 0x379);
+ outb(magic[3], 0x379);
+ outb(magic[4], 0x379);
+
+ if (irq == 9)
+ irq = 2;
+
+ if (idx >= 0 && idx <= 7)
+ cfg = 0x80 | idx | (irq << 4);
+ outb(cfg, 0x379);
}
-#endif
+static irqreturn_t legacy_empty_irq_handler(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
-/**
- * generic_NCR5380_detect - look for NCR5380 controllers
- * @tpnt: the scsi template
- *
- * Scan for the present of NCR5380, NCR53C400, NCR53C400A, DTC3181E
- * and DTC436(ISAPnP) controllers. If overrides have been set we use
- * them.
- *
- * The caller supplied NCR5380_init function is invoked from here, before
- * the interrupt line is taken.
- *
- * Locks: none
- */
+static int legacy_find_free_irq(int *irq_table)
+{
+ while (*irq_table != -1) {
+ if (!request_irq(*irq_table, legacy_empty_irq_handler,
+ IRQF_PROBE_SHARED, "Test IRQ",
+ (void *)irq_table)) {
+ free_irq(*irq_table, (void *) irq_table);
+ return *irq_table;
+ }
+ irq_table++;
+ }
+ return -1;
+}
-int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
+static unsigned int ncr_53c400a_ports[] = {
+ 0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
+};
+static unsigned int dtc_3181e_ports[] = {
+ 0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0
+};
+static u8 ncr_53c400a_magic[] = { /* 53C400A & DTC436 */
+ 0x59, 0xb9, 0xc5, 0xae, 0xa6
+};
+static u8 hp_c2502_magic[] = { /* HP C2502 */
+ 0x0f, 0x22, 0xf0, 0x20, 0x80
+};
+static int hp_c2502_irqs[] = {
+ 9, 5, 7, 3, 4, -1
+};
+
+static int generic_NCR5380_init_one(const struct scsi_host_template *tpnt,
+ struct device *pdev, int base, int irq, int board)
{
- static int current_override = 0;
- int count;
- unsigned int *ports;
-#ifndef SCSI_G_NCR5380_MEM
- int i;
- unsigned long region_size = 16;
-#endif
- static unsigned int __initdata ncr_53c400a_ports[] = {
- 0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
- };
- static unsigned int __initdata dtc_3181e_ports[] = {
- 0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0
- };
+ bool is_pmio = base <= 0xffff;
+ int ret;
int flags = 0;
+ unsigned int *ports = NULL;
+ u8 *magic = NULL;
+ int i;
+ int port_idx = -1;
+ unsigned long region_size;
struct Scsi_Host *instance;
-#ifdef SCSI_G_NCR5380_MEM
- unsigned long base;
- void __iomem *iomem;
-#endif
+ struct NCR5380_hostdata *hostdata;
+ u8 __iomem *iomem;
- if (ncr_irq != NCR_NOT_SET)
- overrides[0].irq = ncr_irq;
- if (ncr_dma != NCR_NOT_SET)
- overrides[0].dma = ncr_dma;
- if (ncr_addr != NCR_NOT_SET)
- overrides[0].NCR5380_map_name = (NCR5380_map_type) ncr_addr;
- if (ncr_5380 != NCR_NOT_SET)
- overrides[0].board = BOARD_NCR5380;
- else if (ncr_53c400 != NCR_NOT_SET)
- overrides[0].board = BOARD_NCR53C400;
- else if (ncr_53c400a != NCR_NOT_SET)
- overrides[0].board = BOARD_NCR53C400A;
- else if (dtc_3181e != NCR_NOT_SET)
- overrides[0].board = BOARD_DTC3181E;
-#ifndef SCSI_G_NCR5380_MEM
- if (!current_override && isapnp_present()) {
- struct pnp_dev *dev = NULL;
- count = 0;
- while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
- if (count >= NO_OVERRIDES)
- break;
- if (pnp_device_attach(dev) < 0)
- continue;
- if (pnp_activate_dev(dev) < 0) {
- printk(KERN_ERR "dtc436e probe: activate failed\n");
- pnp_device_detach(dev);
- continue;
+ switch (board) {
+ case BOARD_NCR5380:
+ flags = FLAG_NO_PSEUDO_DMA | FLAG_DMA_FIXUP;
+ break;
+ case BOARD_NCR53C400A:
+ ports = ncr_53c400a_ports;
+ magic = ncr_53c400a_magic;
+ break;
+ case BOARD_HP_C2502:
+ ports = ncr_53c400a_ports;
+ magic = hp_c2502_magic;
+ break;
+ case BOARD_DTC3181E:
+ ports = dtc_3181e_ports;
+ magic = ncr_53c400a_magic;
+ break;
+ }
+
+ if (is_pmio && ports && magic) {
+ /* wakeup sequence for the NCR53C400A and DTC3181E */
+
+ /* Disable the adapter and look for a free io port */
+ magic_configure(-1, 0, magic);
+
+ region_size = 16;
+ if (base)
+ for (i = 0; ports[i]; i++) {
+ if (base == ports[i]) { /* index found */
+ if (!request_region(ports[i],
+ region_size,
+ "ncr53c80"))
+ return -EBUSY;
+ break;
+ }
+ }
+ else
+ for (i = 0; ports[i]; i++) {
+ if (!request_region(ports[i], region_size,
+ "ncr53c80"))
+ continue;
+ if (inb(ports[i]) == 0xff)
+ break;
+ release_region(ports[i], region_size);
}
- if (!pnp_port_valid(dev, 0)) {
- printk(KERN_ERR "dtc436e probe: no valid port\n");
- pnp_device_detach(dev);
- continue;
+ if (ports[i]) {
+ /* At this point we have our region reserved */
+ magic_configure(i, 0, magic); /* no IRQ yet */
+ base = ports[i];
+ outb(0xc0, base + 9);
+ if (inb(base + 9) != 0x80) {
+ ret = -ENODEV;
+ goto out_release;
}
- if (pnp_irq_valid(dev, 0))
- overrides[count].irq = pnp_irq(dev, 0);
- else
- overrides[count].irq = SCSI_IRQ_NONE;
- if (pnp_dma_valid(dev, 0))
- overrides[count].dma = pnp_dma(dev, 0);
- else
- overrides[count].dma = DMA_NONE;
- overrides[count].NCR5380_map_name = (NCR5380_map_type) pnp_port_start(dev, 0);
- overrides[count].board = BOARD_DTC3181E;
- count++;
- }
+ port_idx = i;
+ } else
+ return -EINVAL;
+ } else if (is_pmio) {
+ /* NCR5380 - no configuration, just grab */
+ region_size = 8;
+ if (!base || !request_region(base, region_size, "ncr5380"))
+ return -EBUSY;
+ } else { /* MMIO */
+ region_size = NCR53C400_region_size;
+ if (!request_mem_region(base, region_size, "ncr5380"))
+ return -EBUSY;
}
-#endif
- tpnt->proc_name = "g_NCR5380";
- for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
- if (!(overrides[current_override].NCR5380_map_name))
- continue;
+ if (is_pmio)
+ iomem = ioport_map(base, region_size);
+ else
+ iomem = ioremap(base, region_size);
- ports = NULL;
- switch (overrides[current_override].board) {
- case BOARD_NCR5380:
- flags = FLAG_NO_PSEUDO_DMA;
- break;
+ if (!iomem) {
+ ret = -ENOMEM;
+ goto out_release;
+ }
+
+ instance = scsi_host_alloc(tpnt, sizeof(struct NCR5380_hostdata));
+ if (instance == NULL) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+ hostdata = shost_priv(instance);
+
+ hostdata->board = board;
+ hostdata->io = iomem;
+ hostdata->region_size = region_size;
+
+ if (is_pmio) {
+ hostdata->io_port = base;
+ hostdata->io_width = 1; /* 8-bit PDMA by default */
+ hostdata->offset = 0;
+
+ /*
+ * On NCR53C400 boards, NCR5380 registers are mapped 8 past
+ * the base address.
+ */
+ switch (board) {
case BOARD_NCR53C400:
- flags = FLAG_NCR53C400;
+ hostdata->io_port += 8;
+ hostdata->c400_ctl_status = 0;
+ hostdata->c400_blk_cnt = 1;
+ hostdata->c400_host_buf = 4;
break;
+ case BOARD_DTC3181E:
+ hostdata->io_width = 2; /* 16-bit PDMA */
+ fallthrough;
case BOARD_NCR53C400A:
- flags = FLAG_NO_PSEUDO_DMA;
- ports = ncr_53c400a_ports;
+ case BOARD_HP_C2502:
+ hostdata->c400_ctl_status = 9;
+ hostdata->c400_blk_cnt = 10;
+ hostdata->c400_host_buf = 8;
break;
- case BOARD_DTC3181E:
- flags = FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E;
- ports = dtc_3181e_ports;
+ }
+ } else {
+ hostdata->base = base;
+ hostdata->offset = NCR53C400_mem_base;
+ switch (board) {
+ case BOARD_NCR53C400:
+ hostdata->c400_ctl_status = 0x100;
+ hostdata->c400_blk_cnt = 0x101;
+ hostdata->c400_host_buf = 0x104;
break;
+ case BOARD_DTC3181E:
+ case BOARD_NCR53C400A:
+ case BOARD_HP_C2502:
+ pr_err(DRV_MODULE_NAME ": unknown register offsets\n");
+ ret = -EINVAL;
+ goto out_unregister;
}
+ }
-#ifndef SCSI_G_NCR5380_MEM
- if (ports) {
- /* wakeup sequence for the NCR53C400A and DTC3181E */
-
- /* Disable the adapter and look for a free io port */
- outb(0x59, 0x779);
- outb(0xb9, 0x379);
- outb(0xc5, 0x379);
- outb(0xae, 0x379);
- outb(0xa6, 0x379);
- outb(0x00, 0x379);
-
- if (overrides[current_override].NCR5380_map_name != PORT_AUTO)
- for (i = 0; ports[i]; i++) {
- if (!request_region(ports[i], 16, "ncr53c80"))
- continue;
- if (overrides[current_override].NCR5380_map_name == ports[i])
- break;
- release_region(ports[i], 16);
- } else
- for (i = 0; ports[i]; i++) {
- if (!request_region(ports[i], 16, "ncr53c80"))
- continue;
- if (inb(ports[i]) == 0xff)
- break;
- release_region(ports[i], 16);
- }
- if (ports[i]) {
- /* At this point we have our region reserved */
- outb(0x59, 0x779);
- outb(0xb9, 0x379);
- outb(0xc5, 0x379);
- outb(0xae, 0x379);
- outb(0xa6, 0x379);
- outb(0x80 | i, 0x379); /* set io port to be used */
- outb(0xc0, ports[i] + 9);
- if (inb(ports[i] + 9) != 0x80)
- continue;
- else
- overrides[current_override].NCR5380_map_name = ports[i];
- } else
- continue;
- }
- else
- {
- /* Not a 53C400A style setup - just grab */
- if(!(request_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380")))
- continue;
- region_size = NCR5380_region_size;
- }
-#else
- base = overrides[current_override].NCR5380_map_name;
- if (!request_mem_region(base, NCR5380_region_size, "ncr5380"))
- continue;
- iomem = ioremap(base, NCR5380_region_size);
- if (!iomem) {
- release_mem_region(base, NCR5380_region_size);
- continue;
- }
-#endif
- instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
- if (instance == NULL) {
-#ifndef SCSI_G_NCR5380_MEM
- release_region(overrides[current_override].NCR5380_map_name, region_size);
-#else
- iounmap(iomem);
- release_mem_region(base, NCR5380_region_size);
-#endif
- continue;
- }
+ /* Check for vacant slot */
+ NCR5380_write(MODE_REG, 0);
+ if (NCR5380_read(MODE_REG) != 0) {
+ ret = -ENODEV;
+ goto out_unregister;
+ }
- instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
-#ifndef SCSI_G_NCR5380_MEM
- instance->n_io_port = region_size;
-#else
- ((struct NCR5380_hostdata *)instance->hostdata)->iomem = iomem;
-#endif
+ ret = NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP);
+ if (ret)
+ goto out_unregister;
- NCR5380_init(instance, flags);
+ switch (board) {
+ case BOARD_NCR53C400:
+ case BOARD_DTC3181E:
+ case BOARD_NCR53C400A:
+ case BOARD_HP_C2502:
+ NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
+ }
- if (overrides[current_override].irq != IRQ_AUTO)
- instance->irq = overrides[current_override].irq;
- else
- instance->irq = NCR5380_probe_irq(instance, 0xffff);
+ NCR5380_maybe_reset_bus(instance);
- if (instance->irq != SCSI_IRQ_NONE)
- if (request_irq(instance->irq, generic_NCR5380_intr,
- IRQF_DISABLED, "NCR5380", instance)) {
- printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
- }
+ /* Compatibility with documented NCR5380 kernel parameters */
+ if (irq == 255 || irq == 0)
+ irq = NO_IRQ;
+ else if (irq == -1)
+ irq = IRQ_AUTO;
+
+ if (board == BOARD_HP_C2502) {
+ int *irq_table = hp_c2502_irqs;
+ int board_irq = -1;
- if (instance->irq == SCSI_IRQ_NONE) {
- printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
- printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ switch (irq) {
+ case NO_IRQ:
+ board_irq = 0;
+ break;
+ case IRQ_AUTO:
+ board_irq = legacy_find_free_irq(irq_table);
+ break;
+ default:
+ while (*irq_table != -1)
+ if (*irq_table++ == irq)
+ board_irq = irq;
}
- printk(KERN_INFO "scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int) instance->NCR5380_instance_name);
- if (instance->irq == SCSI_IRQ_NONE)
- printk(" interrupts disabled");
- else
- printk(" irq %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, GENERIC_NCR5380_PUBLIC_RELEASE);
- NCR5380_print_options(instance);
- printk("\n");
+ if (board_irq <= 0) {
+ board_irq = 0;
+ irq = NO_IRQ;
+ }
- ++current_override;
- ++count;
+ magic_configure(port_idx, board_irq, magic);
}
- return count;
-}
-/**
- * generic_NCR5380_info - reporting string
- * @host: NCR5380 to report on
- *
- * Report driver information for the NCR5380
- */
-
-const char *generic_NCR5380_info(struct Scsi_Host *host)
-{
- static const char string[] = "Generic NCR5380/53C400 Driver";
- return string;
+ if (irq == IRQ_AUTO) {
+ instance->irq = g_NCR5380_probe_irq(instance);
+ if (instance->irq == NO_IRQ)
+ shost_printk(KERN_INFO, instance, "no irq detected\n");
+ } else {
+ instance->irq = irq;
+ if (instance->irq == NO_IRQ)
+ shost_printk(KERN_INFO, instance, "no irq provided\n");
+ }
+
+ if (instance->irq != NO_IRQ) {
+ if (request_irq(instance->irq, generic_NCR5380_intr,
+ 0, "NCR5380", instance)) {
+ instance->irq = NO_IRQ;
+ shost_printk(KERN_INFO, instance,
+ "irq %d denied\n", instance->irq);
+ } else {
+ shost_printk(KERN_INFO, instance,
+ "irq %d acquired\n", instance->irq);
+ }
+ }
+
+ ret = scsi_add_host(instance, pdev);
+ if (ret)
+ goto out_free_irq;
+ scsi_scan_host(instance);
+ dev_set_drvdata(pdev, instance);
+ return 0;
+
+out_free_irq:
+ if (instance->irq != NO_IRQ)
+ free_irq(instance->irq, instance);
+ NCR5380_exit(instance);
+out_unregister:
+ scsi_host_put(instance);
+out_unmap:
+ iounmap(iomem);
+out_release:
+ if (is_pmio)
+ release_region(base, region_size);
+ else
+ release_mem_region(base, region_size);
+ return ret;
}
-/**
- * generic_NCR5380_release_resources - free resources
- * @instance: host adapter to clean up
- *
- * Free the generic interface resources from this adapter.
- *
- * Locks: none
- */
-
-int generic_NCR5380_release_resources(struct Scsi_Host *instance)
+static void generic_NCR5380_release_resources(struct Scsi_Host *instance)
{
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- if (instance->irq != SCSI_IRQ_NONE)
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ void __iomem *iomem = hostdata->io;
+ unsigned long io_port = hostdata->io_port;
+ unsigned long base = hostdata->base;
+ unsigned long region_size = hostdata->region_size;
+
+ scsi_remove_host(instance);
+ if (instance->irq != NO_IRQ)
free_irq(instance->irq, instance);
NCR5380_exit(instance);
-
-#ifndef SCSI_G_NCR5380_MEM
- release_region(instance->NCR5380_instance_name, instance->n_io_port);
-#else
- iounmap(((struct NCR5380_hostdata *)instance->hostdata)->iomem);
- release_mem_region(instance->NCR5380_instance_name, NCR5380_region_size);
-#endif
-
-
- return 0;
+ scsi_host_put(instance);
+ iounmap(iomem);
+ if (io_port)
+ release_region(io_port, region_size);
+ else
+ release_mem_region(base, region_size);
}
-#ifdef BIOSPARAM
-/**
- * generic_NCR5380_biosparam
- * @disk: disk to compute geometry for
- * @dev: device identifier for this disk
- * @ip: sizes to fill in
- *
- * Generates a BIOS / DOS compatible H-C-S mapping for the specified
- * device / size.
- *
- * XXX Most SCSI boards use this mapping, I could be incorrect. Someone
- * using hard disks on a trantor should verify that this mapping
- * corresponds to that used by the BIOS / ASPI driver by running the linux
- * fdisk program and matching the H_C_S coordinates to what DOS uses.
+/* wait_for_53c80_access - wait for 53C80 registers to become accessible
+ * @hostdata: scsi host private data
*
- * Locks: none
+ * The registers within the 53C80 logic block are inaccessible until
+ * bit 7 in the 53C400 control status register gets asserted.
*/
-static int
-generic_NCR5380_biosparam(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int *ip)
+static void wait_for_53c80_access(struct NCR5380_hostdata *hostdata)
{
- ip[0] = 64;
- ip[1] = 32;
- ip[2] = capacity >> 11;
- return 0;
-}
-#endif
+ int count = 10000;
-#ifdef NCR53C400_PSEUDO_DMA
+ do {
+ if (hostdata->board == BOARD_DTC3181E)
+ udelay(4); /* DTC436 chip hangs without this */
+ if (NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG)
+ return;
+ } while (--count > 0);
+
+ scmd_printk(KERN_ERR, hostdata->connected,
+ "53c80 registers not accessible, device will be reset\n");
+ NCR5380_write(hostdata->c400_ctl_status, CSR_RESET);
+ NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
+}
/**
- * NCR5380_pread - pseudo DMA read
- * @instance: adapter to read from
- * @dst: buffer to read into
- * @len: buffer length
+ * generic_NCR5380_precv - pseudo DMA receive
+ * @hostdata: scsi host private data
+ * @dst: buffer to write into
+ * @len: transfer size
*
- * Perform a pseudo DMA mode read from an NCR53C400 or equivalent
- * controller
+ * Perform a pseudo DMA mode receive from a 53C400 or equivalent device.
*/
-
-static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, int len)
+
+static inline int generic_NCR5380_precv(struct NCR5380_hostdata *hostdata,
+ unsigned char *dst, int len)
{
- int blocks = len / 128;
+ int residual;
int start = 0;
- int bl;
- NCR5380_local_declare();
- NCR5380_setup(instance);
+ NCR5380_write(hostdata->c400_ctl_status, CSR_BASE | CSR_TRANS_DIR);
+ NCR5380_write(hostdata->c400_blk_cnt, len / 128);
- NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE | CSR_TRANS_DIR);
- NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
- while (1) {
- if ((bl = NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
- break;
- }
- if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
- printk(KERN_ERR "53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
- return -1;
+ do {
+ if (start == len - 128) {
+ /* Ignore End of DMA interrupt for the final buffer */
+ if (NCR5380_poll_politely(hostdata, hostdata->c400_ctl_status,
+ CSR_HOST_BUF_NOT_RDY, 0, 0) < 0)
+ break;
+ } else {
+ if (NCR5380_poll_politely2(hostdata, hostdata->c400_ctl_status,
+ CSR_HOST_BUF_NOT_RDY, 0,
+ hostdata->c400_ctl_status,
+ CSR_GATED_53C80_IRQ,
+ CSR_GATED_53C80_IRQ, 0) < 0 ||
+ NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY)
+ break;
}
- while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY);
-#ifndef SCSI_G_NCR5380_MEM
- {
- int i;
- for (i = 0; i < 128; i++)
- dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
- }
-#else
- /* implies SCSI_G_NCR5380_MEM */
- memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
-#endif
+ if (hostdata->io_port && hostdata->io_width == 2)
+ insw(hostdata->io_port + hostdata->c400_host_buf,
+ dst + start, 64);
+ else if (hostdata->io_port)
+ insb(hostdata->io_port + hostdata->c400_host_buf,
+ dst + start, 128);
+ else
+ memcpy_fromio(dst + start,
+ hostdata->io + NCR53C400_host_buffer, 128);
start += 128;
- blocks--;
- }
+ } while (start < len);
- if (blocks) {
- while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
- {
- // FIXME - no timeout
- }
+ residual = len - start;
-#ifndef SCSI_G_NCR5380_MEM
- {
- int i;
- for (i = 0; i < 128; i++)
- dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
- }
-#else
- /* implies SCSI_G_NCR5380_MEM */
- memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
-#endif
- start += 128;
- blocks--;
+ if (residual != 0) {
+ /* 53c80 interrupt or transfer timeout. Reset 53c400 logic. */
+ NCR5380_write(hostdata->c400_ctl_status, CSR_RESET);
+ NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
}
+ wait_for_53c80_access(hostdata);
- if (!(NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
- printk("53C400r: no 53C80 gated irq after transfer");
+ if (residual == 0 && NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
+ BASR_END_DMA_TRANSFER,
+ BASR_END_DMA_TRANSFER,
+ 0) < 0)
+ scmd_printk(KERN_ERR, hostdata->connected, "%s: End of DMA timeout\n",
+ __func__);
+
+ hostdata->pdma_residual = residual;
-#if 0
- /*
- * DON'T DO THIS - THEY NEVER ARRIVE!
- */
- printk("53C400r: Waiting for 53C80 registers\n");
- while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
- ;
-#endif
- if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER))
- printk(KERN_ERR "53C400r: no end dma signal\n");
-
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
return 0;
}
/**
- * NCR5380_write - pseudo DMA write
- * @instance: adapter to read from
- * @dst: buffer to read into
- * @len: buffer length
+ * generic_NCR5380_psend - pseudo DMA send
+ * @hostdata: scsi host private data
+ * @src: buffer to read from
+ * @len: transfer size
*
- * Perform a pseudo DMA mode read from an NCR53C400 or equivalent
- * controller
+ * Perform a pseudo DMA mode send to a 53C400 or equivalent device.
*/
-static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, int len)
+static inline int generic_NCR5380_psend(struct NCR5380_hostdata *hostdata,
+ unsigned char *src, int len)
{
- int blocks = len / 128;
+ int residual;
int start = 0;
- int bl;
- int i;
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
- NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
- while (1) {
- if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
- printk(KERN_ERR "53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
- return -1;
+ NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
+ NCR5380_write(hostdata->c400_blk_cnt, len / 128);
+
+ do {
+ if (NCR5380_poll_politely2(hostdata, hostdata->c400_ctl_status,
+ CSR_HOST_BUF_NOT_RDY, 0,
+ hostdata->c400_ctl_status,
+ CSR_GATED_53C80_IRQ,
+ CSR_GATED_53C80_IRQ, 0) < 0 ||
+ NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY) {
+ /* Both 128 B buffers are in use */
+ if (start >= 128)
+ start -= 128;
+ if (start >= 128)
+ start -= 128;
+ break;
}
- if ((bl = NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+ if (start >= len && NCR5380_read(hostdata->c400_blk_cnt) == 0)
+ break;
+
+ if (NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ) {
+ /* Host buffer is empty, other one is in use */
+ if (start >= 128)
+ start -= 128;
break;
}
- while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
- ; // FIXME - timeout
-#ifndef SCSI_G_NCR5380_MEM
- {
- for (i = 0; i < 128; i++)
- NCR5380_write(C400_HOST_BUFFER, src[start + i]);
- }
-#else
- /* implies SCSI_G_NCR5380_MEM */
- memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
-#endif
+
+ if (start >= len)
+ continue;
+
+ if (hostdata->io_port && hostdata->io_width == 2)
+ outsw(hostdata->io_port + hostdata->c400_host_buf,
+ src + start, 64);
+ else if (hostdata->io_port)
+ outsb(hostdata->io_port + hostdata->c400_host_buf,
+ src + start, 128);
+ else
+ memcpy_toio(hostdata->io + NCR53C400_host_buffer,
+ src + start, 128);
start += 128;
- blocks--;
+ } while (1);
+
+ residual = len - start;
+
+ if (residual != 0) {
+ /* 53c80 interrupt or transfer timeout. Reset 53c400 logic. */
+ NCR5380_write(hostdata->c400_ctl_status, CSR_RESET);
+ NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
}
- if (blocks) {
- while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
- ; // FIXME - no timeout
-
-#ifndef SCSI_G_NCR5380_MEM
- {
- for (i = 0; i < 128; i++)
- NCR5380_write(C400_HOST_BUFFER, src[start + i]);
- }
-#else
- /* implies SCSI_G_NCR5380_MEM */
- memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
-#endif
- start += 128;
- blocks--;
+ wait_for_53c80_access(hostdata);
+
+ if (residual == 0) {
+ if (NCR5380_poll_politely(hostdata, TARGET_COMMAND_REG,
+ TCR_LAST_BYTE_SENT, TCR_LAST_BYTE_SENT,
+ 0) < 0)
+ scmd_printk(KERN_ERR, hostdata->connected,
+ "%s: Last Byte Sent timeout\n", __func__);
+
+ if (NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
+ BASR_END_DMA_TRANSFER, BASR_END_DMA_TRANSFER,
+ 0) < 0)
+ scmd_printk(KERN_ERR, hostdata->connected, "%s: End of DMA timeout\n",
+ __func__);
}
-#if 0
- printk("53C400w: waiting for registers to be available\n");
- THEY NEVER DO ! while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG);
- printk("53C400w: Got em\n");
-#endif
+ hostdata->pdma_residual = residual;
- /* Let's wait for this instead - could be ugly */
- /* All documentation says to check for this. Maybe my hardware is too
- * fast. Waiting for it seems to work fine! KLL
- */
- while (!(i = NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
- ; // FIXME - no timeout
-
- /*
- * I know. i is certainly != 0 here but the loop is new. See previous
- * comment.
- */
- if (i) {
- if (!((i = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_END_DMA_TRANSFER))
- printk(KERN_ERR "53C400w: No END OF DMA bit - WHOOPS! BASR=%0x\n", i);
- } else
- printk(KERN_ERR "53C400w: no 53C80 gated irq after transfer (last block)\n");
-
-#if 0
- if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) {
- printk(KERN_ERR "53C400w: no end dma signal\n");
- }
-#endif
- while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
- ; // TIMEOUT
return 0;
}
-#endif /* PSEUDO_DMA */
-/*
- * Include the NCR5380 core code that we build our driver around
- */
-
-#include "NCR5380.c"
+static int generic_NCR5380_dma_xfer_len(struct NCR5380_hostdata *hostdata,
+ struct scsi_cmnd *cmd)
+{
+ int transfersize = NCR5380_to_ncmd(cmd)->this_residual;
-#define PRINTP(x) len += sprintf(buffer+len, x)
-#define ANDP ,
+ if (hostdata->flags & FLAG_NO_PSEUDO_DMA)
+ return 0;
-static int sprint_opcode(char *buffer, int len, int opcode)
-{
- int start = len;
- PRINTP("0x%02x " ANDP opcode);
- return len - start;
+ /* 53C400 datasheet: non-modulo-128-byte transfers should use PIO */
+ if (transfersize % 128)
+ return 0;
+
+ /* Limit PDMA send to 512 B to avoid random corruption on DTC3181E */
+ if (hostdata->board == BOARD_DTC3181E &&
+ cmd->sc_data_direction == DMA_TO_DEVICE)
+ transfersize = min(transfersize, 512);
+
+ return min(transfersize, DMA_MAX_SIZE);
}
-static int sprint_command(char *buffer, int len, unsigned char *command)
+static int generic_NCR5380_dma_residual(struct NCR5380_hostdata *hostdata)
{
- int i, s, start = len;
- len += sprint_opcode(buffer, len, command[0]);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- PRINTP("%02x " ANDP command[i]);
- PRINTP("\n");
- return len - start;
+ return hostdata->pdma_residual;
}
-/**
- * sprintf_Scsi_Cmnd - print a scsi command
- * @buffer: buffr to print into
- * @len: buffer length
- * @cmd: SCSI command block
- *
- * Print out the target and command data in hex
- */
+/* Include the core driver code. */
-static int sprint_Scsi_Cmnd(char *buffer, int len, Scsi_Cmnd * cmd)
-{
- int start = len;
- PRINTP("host number %d destination target %d, lun %d\n" ANDP cmd->device->host->host_no ANDP cmd->device->id ANDP cmd->device->lun);
- PRINTP(" command = ");
- len += sprint_command(buffer, len, cmd->cmnd);
- return len - start;
-}
+#include "NCR5380.c"
-/**
- * generic_NCR5380_proc_info - /proc for NCR5380 driver
- * @buffer: buffer to print into
- * @start: start position
- * @offset: offset into buffer
- * @len: length
- * @hostno: instance to affect
- * @inout: read/write
- *
- * Provide the procfs information for the 5380 controller. We fill
- * this with useful debugging information including the commands
- * being executed, disconnected command queue and the statistical
- * data
- *
- * Locks: global cli/lock for queue walk
- */
-
-static int generic_NCR5380_proc_info(struct Scsi_Host *scsi_ptr, char *buffer, char **start, off_t offset, int length, int inout)
-{
- int len = 0;
- NCR5380_local_declare();
- unsigned long flags;
- unsigned char status;
- int i;
- Scsi_Cmnd *ptr;
- struct NCR5380_hostdata *hostdata;
-#ifdef NCR5380_STATS
- struct scsi_device *dev;
-#endif
+static const struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .proc_name = DRV_MODULE_NAME,
+ .name = "Generic NCR5380/NCR53C400 SCSI",
+ .info = generic_NCR5380_info,
+ .queuecommand = generic_NCR5380_queue_command,
+ .eh_abort_handler = generic_NCR5380_abort,
+ .eh_host_reset_handler = generic_NCR5380_host_reset,
+ .can_queue = 16,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct NCR5380_cmd),
+ .max_sectors = 128,
+};
- NCR5380_setup(scsi_ptr);
- hostdata = (struct NCR5380_hostdata *) scsi_ptr->hostdata;
-
- spin_lock_irqsave(scsi_ptr->host_lock, flags);
- PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name);
- PRINTP("Generic NCR5380 driver version %d\n" ANDP GENERIC_NCR5380_PUBLIC_RELEASE);
- PRINTP("NCR5380 core version %d\n" ANDP NCR5380_PUBLIC_RELEASE);
-#ifdef NCR53C400
- PRINTP("NCR53C400 extension version %d\n" ANDP NCR53C400_PUBLIC_RELEASE);
- PRINTP("NCR53C400 card%s detected\n" ANDP(((struct NCR5380_hostdata *) scsi_ptr->hostdata)->flags & FLAG_NCR53C400) ? "" : " not");
-# if NCR53C400_PSEUDO_DMA
- PRINTP("NCR53C400 pseudo DMA used\n");
-# endif
-#else
- PRINTP("NO NCR53C400 driver extensions\n");
-#endif
- PRINTP("Using %s mapping at %s 0x%lx, " ANDP STRVAL(NCR5380_map_config) ANDP STRVAL(NCR5380_map_name) ANDP scsi_ptr->NCR5380_instance_name);
- if (scsi_ptr->irq == SCSI_IRQ_NONE)
- PRINTP("no interrupt\n");
- else
- PRINTP("on interrupt %d\n" ANDP scsi_ptr->irq);
-
-#ifdef NCR5380_STATS
- if (hostdata->connected || hostdata->issue_queue || hostdata->disconnected_queue)
- PRINTP("There are commands pending, transfer rates may be crud\n");
- if (hostdata->pendingr)
- PRINTP(" %d pending reads" ANDP hostdata->pendingr);
- if (hostdata->pendingw)
- PRINTP(" %d pending writes" ANDP hostdata->pendingw);
- if (hostdata->pendingr || hostdata->pendingw)
- PRINTP("\n");
- shost_for_each_device(dev, scsi_ptr) {
- unsigned long br = hostdata->bytes_read[dev->id];
- unsigned long bw = hostdata->bytes_write[dev->id];
- long tr = hostdata->time_read[dev->id] / HZ;
- long tw = hostdata->time_write[dev->id] / HZ;
-
- PRINTP(" T:%d %s " ANDP dev->id ANDP scsi_device_type(dev->type));
- for (i = 0; i < 8; i++)
- if (dev->vendor[i] >= 0x20)
- *(buffer + (len++)) = dev->vendor[i];
- *(buffer + (len++)) = ' ';
- for (i = 0; i < 16; i++)
- if (dev->model[i] >= 0x20)
- *(buffer + (len++)) = dev->model[i];
- *(buffer + (len++)) = ' ';
- for (i = 0; i < 4; i++)
- if (dev->rev[i] >= 0x20)
- *(buffer + (len++)) = dev->rev[i];
- *(buffer + (len++)) = ' ';
-
- PRINTP("\n%10ld kb read in %5ld secs" ANDP br / 1024 ANDP tr);
- if (tr)
- PRINTP(" @ %5ld bps" ANDP br / tr);
-
- PRINTP("\n%10ld kb written in %5ld secs" ANDP bw / 1024 ANDP tw);
- if (tw)
- PRINTP(" @ %5ld bps" ANDP bw / tw);
- PRINTP("\n");
+static int generic_NCR5380_isa_match(struct device *pdev, unsigned int ndev)
+{
+ int ret = generic_NCR5380_init_one(&driver_template, pdev, base[ndev],
+ irq[ndev], card[ndev]);
+ if (ret) {
+ if (base[ndev])
+ printk(KERN_WARNING "Card not found at address 0x%03x\n",
+ base[ndev]);
+ return 0;
}
-#endif
- status = NCR5380_read(STATUS_REG);
- if (!(status & SR_REQ))
- PRINTP("REQ not asserted, phase unknown.\n");
- else {
- for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i);
- PRINTP("Phase %s\n" ANDP phases[i].name);
- }
+ return 1;
+}
- if (!hostdata->connected) {
- PRINTP("No currently connected command\n");
- } else {
- len += sprint_Scsi_Cmnd(buffer, len, (Scsi_Cmnd *) hostdata->connected);
- }
+static void generic_NCR5380_isa_remove(struct device *pdev,
+ unsigned int ndev)
+{
+ generic_NCR5380_release_resources(dev_get_drvdata(pdev));
+ dev_set_drvdata(pdev, NULL);
+}
- PRINTP("issue_queue\n");
+static struct isa_driver generic_NCR5380_isa_driver = {
+ .match = generic_NCR5380_isa_match,
+ .remove = generic_NCR5380_isa_remove,
+ .driver = {
+ .name = DRV_MODULE_NAME
+ },
+};
+
+#ifdef CONFIG_PNP
+static const struct pnp_device_id generic_NCR5380_pnp_ids[] = {
+ { .id = "DTC436e", .driver_data = BOARD_DTC3181E },
+ { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, generic_NCR5380_pnp_ids);
- for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
- len += sprint_Scsi_Cmnd(buffer, len, ptr);
+static int generic_NCR5380_pnp_probe(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
+{
+ int base, irq;
- PRINTP("disconnected_queue\n");
+ if (pnp_activate_dev(pdev) < 0)
+ return -EBUSY;
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
- len += sprint_Scsi_Cmnd(buffer, len, ptr);
+ base = pnp_port_start(pdev, 0);
+ irq = pnp_irq(pdev, 0);
- *start = buffer + offset;
- len -= offset;
- if (len > length)
- len = length;
- spin_unlock_irqrestore(scsi_ptr->host_lock, flags);
- return len;
+ return generic_NCR5380_init_one(&driver_template, &pdev->dev, base, irq,
+ id->driver_data);
}
-#undef PRINTP
-#undef ANDP
+static void generic_NCR5380_pnp_remove(struct pnp_dev *pdev)
+{
+ generic_NCR5380_release_resources(pnp_get_drvdata(pdev));
+ pnp_set_drvdata(pdev, NULL);
+}
-static struct scsi_host_template driver_template = {
- .proc_info = generic_NCR5380_proc_info,
- .name = "Generic NCR5380/NCR53C400 Scsi Driver",
- .detect = generic_NCR5380_detect,
- .release = generic_NCR5380_release_resources,
- .info = generic_NCR5380_info,
- .queuecommand = generic_NCR5380_queue_command,
- .eh_abort_handler = generic_NCR5380_abort,
- .eh_bus_reset_handler = generic_NCR5380_bus_reset,
- .bios_param = NCR5380_BIOSPARAM,
- .can_queue = CAN_QUEUE,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = CMD_PER_LUN,
- .use_clustering = DISABLE_CLUSTERING,
+static struct pnp_driver generic_NCR5380_pnp_driver = {
+ .name = DRV_MODULE_NAME,
+ .id_table = generic_NCR5380_pnp_ids,
+ .probe = generic_NCR5380_pnp_probe,
+ .remove = generic_NCR5380_pnp_remove,
};
-#include <linux/module.h>
-#include "scsi_module.c"
+#endif /* defined(CONFIG_PNP) */
-module_param(ncr_irq, int, 0);
-module_param(ncr_dma, int, 0);
-module_param(ncr_addr, int, 0);
-module_param(ncr_5380, int, 0);
-module_param(ncr_53c400, int, 0);
-module_param(ncr_53c400a, int, 0);
-module_param(dtc_3181e, int, 0);
-MODULE_LICENSE("GPL");
+static int pnp_registered, isa_registered;
-#ifndef SCSI_G_NCR5380_MEM
-static struct isapnp_device_id id_table[] __devinitdata = {
- {
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e),
- 0},
- {0}
-};
+static int __init generic_NCR5380_init(void)
+{
+ int ret = 0;
+
+ /* compatibility with old-style parameters */
+ if (irq[0] == -1 && base[0] == 0 && card[0] == -1) {
+ irq[0] = ncr_irq;
+ base[0] = ncr_addr;
+ if (ncr_5380)
+ card[0] = BOARD_NCR5380;
+ if (ncr_53c400)
+ card[0] = BOARD_NCR53C400;
+ if (ncr_53c400a)
+ card[0] = BOARD_NCR53C400A;
+ if (dtc_3181e)
+ card[0] = BOARD_DTC3181E;
+ if (hp_c2502)
+ card[0] = BOARD_HP_C2502;
+ }
-MODULE_DEVICE_TABLE(isapnp, id_table);
+#ifdef CONFIG_PNP
+ if (!pnp_register_driver(&generic_NCR5380_pnp_driver))
+ pnp_registered = 1;
#endif
+ ret = isa_register_driver(&generic_NCR5380_isa_driver, MAX_CARDS);
+ if (!ret)
+ isa_registered = 1;
+
+ return (pnp_registered || isa_registered) ? 0 : ret;
+}
+
+static void __exit generic_NCR5380_exit(void)
+{
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ pnp_unregister_driver(&generic_NCR5380_pnp_driver);
+#endif
+ if (isa_registered)
+ isa_unregister_driver(&generic_NCR5380_isa_driver);
+}
-__setup("ncr5380=", do_NCR5380_setup);
-__setup("ncr53c400=", do_NCR53C400_setup);
-__setup("ncr53c400a=", do_NCR53C400A_setup);
-__setup("dtc3181e=", do_DTC3181E_setup);
+module_init(generic_NCR5380_init);
+module_exit(generic_NCR5380_exit);
diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h
deleted file mode 100644
index 1bcdb7beb77b..000000000000
--- a/drivers/scsi/g_NCR5380.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Generic Generic NCR5380 driver defines
- *
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 440-4894
- *
- * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
- * K.Lentin@cs.monash.edu.au
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * $Log: generic_NCR5380.h,v $
- */
-
-#ifndef GENERIC_NCR5380_H
-#define GENERIC_NCR5380_H
-
-
-#define GENERIC_NCR5380_PUBLIC_RELEASE 1
-
-#ifdef NCR53C400
-#define BIOSPARAM
-#define NCR5380_BIOSPARAM generic_NCR5380_biosparam
-#else
-#define NCR5380_BIOSPARAM NULL
-#endif
-
-#ifndef ASM
-static int generic_NCR5380_abort(Scsi_Cmnd *);
-static int generic_NCR5380_detect(struct scsi_host_template *);
-static int generic_NCR5380_release_resources(struct Scsi_Host *);
-static int generic_NCR5380_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int generic_NCR5380_bus_reset(Scsi_Cmnd *);
-static const char* generic_NCR5380_info(struct Scsi_Host *);
-
-#ifndef CMD_PER_LUN
-#define CMD_PER_LUN 2
-#endif
-
-#ifndef CAN_QUEUE
-#define CAN_QUEUE 16
-#endif
-
-#ifndef HOSTS_C
-
-#define __STRVAL(x) #x
-#define STRVAL(x) __STRVAL(x)
-
-#ifndef SCSI_G_NCR5380_MEM
-
-#define NCR5380_map_config port
-#define NCR5380_map_type int
-#define NCR5380_map_name port
-#define NCR5380_instance_name io_port
-#define NCR53C400_register_offset 0
-#define NCR53C400_address_adjust 8
-
-#ifdef NCR53C400
-#define NCR5380_region_size 16
-#else
-#define NCR5380_region_size 8
-#endif
-
-#define NCR5380_read(reg) (inb(NCR5380_map_name + (reg)))
-#define NCR5380_write(reg, value) (outb((value), (NCR5380_map_name + (reg))))
-
-#define NCR5380_implementation_fields \
- NCR5380_map_type NCR5380_map_name
-
-#define NCR5380_local_declare() \
- register NCR5380_implementation_fields
-
-#define NCR5380_setup(instance) \
- NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
-
-#else
-/* therefore SCSI_G_NCR5380_MEM */
-
-#define NCR5380_map_config memory
-#define NCR5380_map_type unsigned long
-#define NCR5380_map_name base
-#define NCR5380_instance_name base
-#define NCR53C400_register_offset 0x108
-#define NCR53C400_address_adjust 0
-#define NCR53C400_mem_base 0x3880
-#define NCR53C400_host_buffer 0x3900
-#define NCR5380_region_size 0x3a00
-
-#define NCR5380_read(reg) readb(iomem + NCR53C400_mem_base + (reg))
-#define NCR5380_write(reg, value) writeb(value, iomem + NCR53C400_mem_base + (reg))
-
-#define NCR5380_implementation_fields \
- NCR5380_map_type NCR5380_map_name; \
- void __iomem *iomem;
-
-#define NCR5380_local_declare() \
- register void __iomem *iomem
-
-#define NCR5380_setup(instance) \
- iomem = (((struct NCR5380_hostdata *)(instance)->hostdata)->iomem)
-
-#endif
-
-#define NCR5380_intr generic_NCR5380_intr
-#define NCR5380_queue_command generic_NCR5380_queue_command
-#define NCR5380_abort generic_NCR5380_abort
-#define NCR5380_bus_reset generic_NCR5380_bus_reset
-#define NCR5380_pread generic_NCR5380_pread
-#define NCR5380_pwrite generic_NCR5380_pwrite
-#define NCR5380_proc_info notyet_generic_proc_info
-
-#define BOARD_NCR5380 0
-#define BOARD_NCR53C400 1
-#define BOARD_NCR53C400A 2
-#define BOARD_DTC3181E 3
-
-#endif /* else def HOSTS_C */
-#endif /* ndef ASM */
-#endif /* GENERIC_NCR5380_H */
-
diff --git a/drivers/scsi/g_NCR5380_mmio.c b/drivers/scsi/g_NCR5380_mmio.c
deleted file mode 100644
index 8cdde71ba0c8..000000000000
--- a/drivers/scsi/g_NCR5380_mmio.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * There is probably a nicer way to do this but this one makes
- * pretty obvious what is happening. We rebuild the same file with
- * different options for mmio versus pio.
- */
-
-#define SCSI_G_NCR5380_MEM
-
-#include "g_NCR5380.c"
-
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
deleted file mode 100644
index 3242bcabad97..000000000000
--- a/drivers/scsi/gdth.c
+++ /dev/null
@@ -1,5244 +0,0 @@
-/************************************************************************
- * Linux driver for *
- * ICP vortex GmbH: GDT ISA/EISA/PCI Disk Array Controllers *
- * Intel Corporation: Storage RAID Controllers *
- * *
- * gdth.c *
- * Copyright (C) 1995-06 ICP vortex GmbH, Achim Leubner *
- * Copyright (C) 2002-04 Intel Corporation *
- * Copyright (C) 2003-06 Adaptec Inc. *
- * <achim_leubner@adaptec.com> *
- * *
- * Additions/Fixes: *
- * Boji Tony Kannanthanam <boji.t.kannanthanam@intel.com> *
- * Johannes Dinner <johannes_dinner@adaptec.com> *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published *
- * by the Free Software Foundation; either version 2 of the License, *
- * or (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this kernel; if not, write to the Free Software *
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- * Linux kernel 2.6.x supported *
- * *
- ************************************************************************/
-
-/* All GDT Disk Array Controllers are fully supported by this driver.
- * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the
- * PCI Fibre Channel Disk Array Controllers. See gdth.h for a complete
- * list of all controller types.
- *
- * If you have one or more GDT3000/3020 EISA controllers with
- * controller BIOS disabled, you have to set the IRQ values with the
- * command line option "gdth=irq1,irq2,...", where the irq1,irq2,... are
- * the IRQ values for the EISA controllers.
- *
- * After the optional list of IRQ values, other possible
- * command line options are:
- * disable:Y disable driver
- * disable:N enable driver
- * reserve_mode:0 reserve no drives for the raw service
- * reserve_mode:1 reserve all not init., removable drives
- * reserve_mode:2 reserve all not init. drives
- * reserve_list:h,b,t,l,h,b,t,l,... reserve particular drive(s) with
- * h- controller no., b- channel no.,
- * t- target ID, l- LUN
- * reverse_scan:Y reverse scan order for PCI controllers
- * reverse_scan:N scan PCI controllers like BIOS
- * max_ids:x x - target ID count per channel (1..MAXID)
- * rescan:Y rescan all channels/IDs
- * rescan:N use all devices found until now
- * hdr_channel:x x - number of virtual bus for host drives
- * shared_access:Y disable driver reserve/release protocol to
- * access a shared resource from several nodes,
- * appropriate controller firmware required
- * shared_access:N enable driver reserve/release protocol
- * probe_eisa_isa:Y scan for EISA/ISA controllers
- * probe_eisa_isa:N do not scan for EISA/ISA controllers
- * force_dma32:Y use only 32 bit DMA mode
- * force_dma32:N use 64 bit DMA mode, if supported
- *
- * The default values are: "gdth=disable:N,reserve_mode:1,reverse_scan:N,
- * max_ids:127,rescan:N,hdr_channel:0,
- * shared_access:Y,probe_eisa_isa:N,force_dma32:N".
- * Here is another example: "gdth=reserve_list:0,1,2,0,0,1,3,0,rescan:Y".
- *
- * When loading the gdth driver as a module, the same options are available.
- * You can set the IRQs with "IRQ=...". However, the syntax to specify the
- * options changes slightly. You must replace all ',' between options
- * with ' ' and all ':' with '=' and you must use
- * '1' in place of 'Y' and '0' in place of 'N'.
- *
- * Default: "modprobe gdth disable=0 reserve_mode=1 reverse_scan=0
- * max_ids=127 rescan=0 hdr_channel=0 shared_access=0
- * probe_eisa_isa=0 force_dma32=0"
- * The other example: "modprobe gdth reserve_list=0,1,2,0,0,1,3,0 rescan=1".
- */
-
-/* The meaning of the Scsi_Pointer members in this driver is as follows:
- * ptr: Chaining
- * this_residual: unused
- * buffer: unused
- * dma_handle: unused
- * buffers_residual: unused
- * Status: unused
- * Message: unused
- * have_data_in: unused
- * sent_command: unused
- * phase: unused
- */
-
-
-/* interrupt coalescing */
-/* #define INT_COAL */
-
-/* statistics */
-#define GDTH_STATISTICS
-
-#include <linux/module.h>
-
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/string.h>
-#include <linux/ctype.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/proc_fs.h>
-#include <linux/time.h>
-#include <linux/timer.h>
-#include <linux/dma-mapping.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-
-#ifdef GDTH_RTC
-#include <linux/mc146818rtc.h>
-#endif
-#include <linux/reboot.h>
-
-#include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/spinlock.h>
-#include <linux/blkdev.h>
-#include <linux/scatterlist.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "gdth.h"
-
-static DEFINE_MUTEX(gdth_mutex);
-static void gdth_delay(int milliseconds);
-static void gdth_eval_mapping(u32 size, u32 *cyls, int *heads, int *secs);
-static irqreturn_t gdth_interrupt(int irq, void *dev_id);
-static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
- int gdth_from_wait, int* pIndex);
-static int gdth_sync_event(gdth_ha_str *ha, int service, u8 index,
- Scsi_Cmnd *scp);
-static int gdth_async_event(gdth_ha_str *ha);
-static void gdth_log_event(gdth_evt_data *dvr, char *buffer);
-
-static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 priority);
-static void gdth_next(gdth_ha_str *ha);
-static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 b);
-static int gdth_special_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp);
-static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, u16 source,
- u16 idx, gdth_evt_data *evt);
-static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr);
-static void gdth_readapp_event(gdth_ha_str *ha, u8 application,
- gdth_evt_str *estr);
-static void gdth_clear_events(void);
-
-static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp,
- char *buffer, u16 count);
-static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp);
-static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u16 hdrive);
-
-static void gdth_enable_int(gdth_ha_str *ha);
-static int gdth_test_busy(gdth_ha_str *ha);
-static int gdth_get_cmd_index(gdth_ha_str *ha);
-static void gdth_release_event(gdth_ha_str *ha);
-static int gdth_wait(gdth_ha_str *ha, int index,u32 time);
-static int gdth_internal_cmd(gdth_ha_str *ha, u8 service, u16 opcode,
- u32 p1, u64 p2,u64 p3);
-static int gdth_search_drives(gdth_ha_str *ha);
-static int gdth_analyse_hdrive(gdth_ha_str *ha, u16 hdrive);
-
-static const char *gdth_ctr_name(gdth_ha_str *ha);
-
-static int gdth_open(struct inode *inode, struct file *filep);
-static int gdth_close(struct inode *inode, struct file *filep);
-static long gdth_unlocked_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg);
-
-static void gdth_flush(gdth_ha_str *ha);
-static int gdth_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *cmd);
-static int __gdth_queuecommand(gdth_ha_str *ha, struct scsi_cmnd *scp,
- struct gdth_cmndinfo *cmndinfo);
-static void gdth_scsi_done(struct scsi_cmnd *scp);
-
-#ifdef DEBUG_GDTH
-static u8 DebugState = DEBUG_GDTH;
-
-#ifdef __SERIAL__
-#define MAX_SERBUF 160
-static void ser_init(void);
-static void ser_puts(char *str);
-static void ser_putc(char c);
-static int ser_printk(const char *fmt, ...);
-static char strbuf[MAX_SERBUF+1];
-#ifdef __COM2__
-#define COM_BASE 0x2f8
-#else
-#define COM_BASE 0x3f8
-#endif
-static void ser_init()
-{
- unsigned port=COM_BASE;
-
- outb(0x80,port+3);
- outb(0,port+1);
- /* 19200 Baud, if 9600: outb(12,port) */
- outb(6, port);
- outb(3,port+3);
- outb(0,port+1);
- /*
- ser_putc('I');
- ser_putc(' ');
- */
-}
-
-static void ser_puts(char *str)
-{
- char *ptr;
-
- ser_init();
- for (ptr=str;*ptr;++ptr)
- ser_putc(*ptr);
-}
-
-static void ser_putc(char c)
-{
- unsigned port=COM_BASE;
-
- while ((inb(port+5) & 0x20)==0);
- outb(c,port);
- if (c==0x0a)
- {
- while ((inb(port+5) & 0x20)==0);
- outb(0x0d,port);
- }
-}
-
-static int ser_printk(const char *fmt, ...)
-{
- va_list args;
- int i;
-
- va_start(args,fmt);
- i = vsprintf(strbuf,fmt,args);
- ser_puts(strbuf);
- va_end(args);
- return i;
-}
-
-#define TRACE(a) {if (DebugState==1) {ser_printk a;}}
-#define TRACE2(a) {if (DebugState==1 || DebugState==2) {ser_printk a;}}
-#define TRACE3(a) {if (DebugState!=0) {ser_printk a;}}
-
-#else /* !__SERIAL__ */
-#define TRACE(a) {if (DebugState==1) {printk a;}}
-#define TRACE2(a) {if (DebugState==1 || DebugState==2) {printk a;}}
-#define TRACE3(a) {if (DebugState!=0) {printk a;}}
-#endif
-
-#else /* !DEBUG */
-#define TRACE(a)
-#define TRACE2(a)
-#define TRACE3(a)
-#endif
-
-#ifdef GDTH_STATISTICS
-static u32 max_rq=0, max_index=0, max_sg=0;
-#ifdef INT_COAL
-static u32 max_int_coal=0;
-#endif
-static u32 act_ints=0, act_ios=0, act_stats=0, act_rq=0;
-static struct timer_list gdth_timer;
-#endif
-
-#define PTR2USHORT(a) (u16)(unsigned long)(a)
-#define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b)
-#define INDEX_OK(i,t) ((i)<ARRAY_SIZE(t))
-
-#define BUS_L2P(a,b) ((b)>(a)->virt_bus ? (b-1):(b))
-
-#ifdef CONFIG_ISA
-static u8 gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */
-#endif
-#if defined(CONFIG_EISA) || defined(CONFIG_ISA)
-static u8 gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */
-#endif
-static u8 gdth_polling; /* polling if TRUE */
-static int gdth_ctr_count = 0; /* controller count */
-static LIST_HEAD(gdth_instances); /* controller list */
-static u8 gdth_write_through = FALSE; /* write through */
-static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */
-static int elastidx;
-static int eoldidx;
-static int major;
-
-#define DIN 1 /* IN data direction */
-#define DOU 2 /* OUT data direction */
-#define DNO DIN /* no data transfer */
-#define DUN DIN /* unknown data direction */
-static u8 gdth_direction_tab[0x100] = {
- DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN,
- DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN,
- DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DOU,DNO,DUN,DIN,DOU,DOU,
- DOU,DOU,DOU,DNO,DIN,DNO,DNO,DIN,DOU,DOU,DOU,DOU,DIN,DOU,DIN,DOU,
- DOU,DOU,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DOU,DIN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DIN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DIN,DUN,
- DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DOU,DUN,DNO,DUN,DOU,DOU,
- DOU,DOU,DOU,DNO,DUN,DIN,DOU,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
- DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
-};
-
-/* LILO and modprobe/insmod parameters */
-/* IRQ list for GDT3000/3020 EISA controllers */
-static int irq[MAXHA] __initdata =
-{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
-/* disable driver flag */
-static int disable __initdata = 0;
-/* reserve flag */
-static int reserve_mode = 1;
-/* reserve list */
-static int reserve_list[MAX_RES_ARGS] =
-{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
-/* scan order for PCI controllers */
-static int reverse_scan = 0;
-/* virtual channel for the host drives */
-static int hdr_channel = 0;
-/* max. IDs per channel */
-static int max_ids = MAXID;
-/* rescan all IDs */
-static int rescan = 0;
-/* shared access */
-static int shared_access = 1;
-/* enable support for EISA and ISA controllers */
-static int probe_eisa_isa = 0;
-/* 64 bit DMA mode, support for drives > 2 TB, if force_dma32 = 0 */
-static int force_dma32 = 0;
-
-/* parameters for modprobe/insmod */
-module_param_array(irq, int, NULL, 0);
-module_param(disable, int, 0);
-module_param(reserve_mode, int, 0);
-module_param_array(reserve_list, int, NULL, 0);
-module_param(reverse_scan, int, 0);
-module_param(hdr_channel, int, 0);
-module_param(max_ids, int, 0);
-module_param(rescan, int, 0);
-module_param(shared_access, int, 0);
-module_param(probe_eisa_isa, int, 0);
-module_param(force_dma32, int, 0);
-MODULE_AUTHOR("Achim Leubner");
-MODULE_LICENSE("GPL");
-
-/* ioctl interface */
-static const struct file_operations gdth_fops = {
- .unlocked_ioctl = gdth_unlocked_ioctl,
- .open = gdth_open,
- .release = gdth_close,
- .llseek = noop_llseek,
-};
-
-#include "gdth_proc.h"
-#include "gdth_proc.c"
-
-static gdth_ha_str *gdth_find_ha(int hanum)
-{
- gdth_ha_str *ha;
-
- list_for_each_entry(ha, &gdth_instances, list)
- if (hanum == ha->hanum)
- return ha;
-
- return NULL;
-}
-
-static struct gdth_cmndinfo *gdth_get_cmndinfo(gdth_ha_str *ha)
-{
- struct gdth_cmndinfo *priv = NULL;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- for (i=0; i<GDTH_MAXCMDS; ++i) {
- if (ha->cmndinfo[i].index == 0) {
- priv = &ha->cmndinfo[i];
- memset(priv, 0, sizeof(*priv));
- priv->index = i+1;
- break;
- }
- }
-
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-
- return priv;
-}
-
-static void gdth_put_cmndinfo(struct gdth_cmndinfo *priv)
-{
- BUG_ON(!priv);
- priv->index = 0;
-}
-
-static void gdth_delay(int milliseconds)
-{
- if (milliseconds == 0) {
- udelay(1);
- } else {
- mdelay(milliseconds);
- }
-}
-
-static void gdth_scsi_done(struct scsi_cmnd *scp)
-{
- struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
- int internal_command = cmndinfo->internal_command;
-
- TRACE2(("gdth_scsi_done()\n"));
-
- gdth_put_cmndinfo(cmndinfo);
- scp->host_scribble = NULL;
-
- if (internal_command)
- complete((struct completion *)scp->request);
- else
- scp->scsi_done(scp);
-}
-
-int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd,
- int timeout, u32 *info)
-{
- gdth_ha_str *ha = shost_priv(sdev->host);
- Scsi_Cmnd *scp;
- struct gdth_cmndinfo cmndinfo;
- DECLARE_COMPLETION_ONSTACK(wait);
- int rval;
-
- scp = kzalloc(sizeof(*scp), GFP_KERNEL);
- if (!scp)
- return -ENOMEM;
-
- scp->sense_buffer = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
- if (!scp->sense_buffer) {
- kfree(scp);
- return -ENOMEM;
- }
-
- scp->device = sdev;
- memset(&cmndinfo, 0, sizeof(cmndinfo));
-
- /* use request field to save the ptr. to completion struct. */
- scp->request = (struct request *)&wait;
- scp->cmd_len = 12;
- scp->cmnd = cmnd;
- cmndinfo.priority = IOCTL_PRI;
- cmndinfo.internal_cmd_str = gdtcmd;
- cmndinfo.internal_command = 1;
-
- TRACE(("__gdth_execute() cmd 0x%x\n", scp->cmnd[0]));
- __gdth_queuecommand(ha, scp, &cmndinfo);
-
- wait_for_completion(&wait);
-
- rval = cmndinfo.status;
- if (info)
- *info = cmndinfo.info;
- kfree(scp->sense_buffer);
- kfree(scp);
- return rval;
-}
-
-int gdth_execute(struct Scsi_Host *shost, gdth_cmd_str *gdtcmd, char *cmnd,
- int timeout, u32 *info)
-{
- struct scsi_device *sdev = scsi_get_host_dev(shost);
- int rval = __gdth_execute(sdev, gdtcmd, cmnd, timeout, info);
-
- scsi_free_host_dev(sdev);
- return rval;
-}
-
-static void gdth_eval_mapping(u32 size, u32 *cyls, int *heads, int *secs)
-{
- *cyls = size /HEADS/SECS;
- if (*cyls <= MAXCYLS) {
- *heads = HEADS;
- *secs = SECS;
- } else { /* too high for 64*32 */
- *cyls = size /MEDHEADS/MEDSECS;
- if (*cyls <= MAXCYLS) {
- *heads = MEDHEADS;
- *secs = MEDSECS;
- } else { /* too high for 127*63 */
- *cyls = size /BIGHEADS/BIGSECS;
- *heads = BIGHEADS;
- *secs = BIGSECS;
- }
- }
-}
-
-/* controller search and initialization functions */
-#ifdef CONFIG_EISA
-static int __init gdth_search_eisa(u16 eisa_adr)
-{
- u32 id;
-
- TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
- id = inl(eisa_adr+ID0REG);
- if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */
- if ((inb(eisa_adr+EISAREG) & 8) == 0)
- return 0; /* not EISA configured */
- return 1;
- }
- if (id == GDT3_ID) /* GDT3000 */
- return 1;
-
- return 0;
-}
-#endif /* CONFIG_EISA */
-
-#ifdef CONFIG_ISA
-static int __init gdth_search_isa(u32 bios_adr)
-{
- void __iomem *addr;
- u32 id;
-
- TRACE(("gdth_search_isa() bios adr. %x\n",bios_adr));
- if ((addr = ioremap(bios_adr+BIOS_ID_OFFS, sizeof(u32))) != NULL) {
- id = readl(addr);
- iounmap(addr);
- if (id == GDT2_ID) /* GDT2000 */
- return 1;
- }
- return 0;
-}
-#endif /* CONFIG_ISA */
-
-#ifdef CONFIG_PCI
-
-static bool gdth_search_vortex(u16 device)
-{
- if (device <= PCI_DEVICE_ID_VORTEX_GDT6555)
- return true;
- if (device >= PCI_DEVICE_ID_VORTEX_GDT6x17RP &&
- device <= PCI_DEVICE_ID_VORTEX_GDTMAXRP)
- return true;
- if (device == PCI_DEVICE_ID_VORTEX_GDTNEWRX ||
- device == PCI_DEVICE_ID_VORTEX_GDTNEWRX2)
- return true;
- return false;
-}
-
-static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out);
-static int gdth_pci_init_one(struct pci_dev *pdev,
- const struct pci_device_id *ent);
-static void gdth_pci_remove_one(struct pci_dev *pdev);
-static void gdth_remove_one(gdth_ha_str *ha);
-
-/* Vortex only makes RAID controllers.
- * We do not really want to specify all 550 ids here, so wildcard match.
- */
-static const struct pci_device_id gdthtable[] = {
- { PCI_VDEVICE(VORTEX, PCI_ANY_ID) },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SRC) },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SRC_XSCALE) },
- { } /* terminate list */
-};
-MODULE_DEVICE_TABLE(pci, gdthtable);
-
-static struct pci_driver gdth_pci_driver = {
- .name = "gdth",
- .id_table = gdthtable,
- .probe = gdth_pci_init_one,
- .remove = gdth_pci_remove_one,
-};
-
-static void __devexit gdth_pci_remove_one(struct pci_dev *pdev)
-{
- gdth_ha_str *ha = pci_get_drvdata(pdev);
-
- pci_set_drvdata(pdev, NULL);
-
- list_del(&ha->list);
- gdth_remove_one(ha);
-
- pci_disable_device(pdev);
-}
-
-static int __devinit gdth_pci_init_one(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- u16 vendor = pdev->vendor;
- u16 device = pdev->device;
- unsigned long base0, base1, base2;
- int rc;
- gdth_pci_str gdth_pcistr;
- gdth_ha_str *ha = NULL;
-
- TRACE(("gdth_search_dev() cnt %d vendor %x device %x\n",
- gdth_ctr_count, vendor, device));
-
- memset(&gdth_pcistr, 0, sizeof(gdth_pcistr));
-
- if (vendor == PCI_VENDOR_ID_VORTEX && !gdth_search_vortex(device))
- return -ENODEV;
-
- rc = pci_enable_device(pdev);
- if (rc)
- return rc;
-
- if (gdth_ctr_count >= MAXHA)
- return -EBUSY;
-
- /* GDT PCI controller found, resources are already in pdev */
- gdth_pcistr.pdev = pdev;
- base0 = pci_resource_flags(pdev, 0);
- base1 = pci_resource_flags(pdev, 1);
- base2 = pci_resource_flags(pdev, 2);
- if (device <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000/B */
- device >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */
- if (!(base0 & IORESOURCE_MEM))
- return -ENODEV;
- gdth_pcistr.dpmem = pci_resource_start(pdev, 0);
- } else { /* GDT6110, GDT6120, .. */
- if (!(base0 & IORESOURCE_MEM) ||
- !(base2 & IORESOURCE_MEM) ||
- !(base1 & IORESOURCE_IO))
- return -ENODEV;
- gdth_pcistr.dpmem = pci_resource_start(pdev, 2);
- gdth_pcistr.io = pci_resource_start(pdev, 1);
- }
- TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n",
- gdth_pcistr.pdev->bus->number,
- PCI_SLOT(gdth_pcistr.pdev->devfn),
- gdth_pcistr.irq,
- gdth_pcistr.dpmem));
-
- rc = gdth_pci_probe_one(&gdth_pcistr, &ha);
- if (rc)
- return rc;
-
- return 0;
-}
-#endif /* CONFIG_PCI */
-
-#ifdef CONFIG_EISA
-static int __init gdth_init_eisa(u16 eisa_adr,gdth_ha_str *ha)
-{
- u32 retries,id;
- u8 prot_ver,eisacf,i,irq_found;
-
- TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
-
- /* disable board interrupts, deinitialize services */
- outb(0xff,eisa_adr+EDOORREG);
- outb(0x00,eisa_adr+EDENABREG);
- outb(0x00,eisa_adr+EINTENABREG);
-
- outb(0xff,eisa_adr+LDOORREG);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (inb(eisa_adr+EDOORREG) != 0xff) {
- if (--retries == 0) {
- printk("GDT-EISA: Initialization error (DEINIT failed)\n");
- return 0;
- }
- gdth_delay(1);
- TRACE2(("wait for DEINIT: retries=%d\n",retries));
- }
- prot_ver = inb(eisa_adr+MAILBOXREG);
- outb(0xff,eisa_adr+EDOORREG);
- if (prot_ver != PROTOCOL_VERSION) {
- printk("GDT-EISA: Illegal protocol version\n");
- return 0;
- }
- ha->bmic = eisa_adr;
- ha->brd_phys = (u32)eisa_adr >> 12;
-
- outl(0,eisa_adr+MAILBOXREG);
- outl(0,eisa_adr+MAILBOXREG+4);
- outl(0,eisa_adr+MAILBOXREG+8);
- outl(0,eisa_adr+MAILBOXREG+12);
-
- /* detect IRQ */
- if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) {
- ha->oem_id = OEM_ID_ICP;
- ha->type = GDT_EISA;
- ha->stype = id;
- outl(1,eisa_adr+MAILBOXREG+8);
- outb(0xfe,eisa_adr+LDOORREG);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (inb(eisa_adr+EDOORREG) != 0xfe) {
- if (--retries == 0) {
- printk("GDT-EISA: Initialization error (get IRQ failed)\n");
- return 0;
- }
- gdth_delay(1);
- }
- ha->irq = inb(eisa_adr+MAILBOXREG);
- outb(0xff,eisa_adr+EDOORREG);
- TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq));
- /* check the result */
- if (ha->irq == 0) {
- TRACE2(("Unknown IRQ, use IRQ table from cmd line !\n"));
- for (i = 0, irq_found = FALSE;
- i < MAXHA && irq[i] != 0xff; ++i) {
- if (irq[i]==10 || irq[i]==11 || irq[i]==12 || irq[i]==14) {
- irq_found = TRUE;
- break;
- }
- }
- if (irq_found) {
- ha->irq = irq[i];
- irq[i] = 0;
- printk("GDT-EISA: Can not detect controller IRQ,\n");
- printk("Use IRQ setting from command line (IRQ = %d)\n",
- ha->irq);
- } else {
- printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n");
- printk("the controller BIOS or use command line parameters\n");
- return 0;
- }
- }
- } else {
- eisacf = inb(eisa_adr+EISAREG) & 7;
- if (eisacf > 4) /* level triggered */
- eisacf -= 4;
- ha->irq = gdth_irq_tab[eisacf];
- ha->oem_id = OEM_ID_ICP;
- ha->type = GDT_EISA;
- ha->stype = id;
- }
-
- ha->dma64_support = 0;
- return 1;
-}
-#endif /* CONFIG_EISA */
-
-#ifdef CONFIG_ISA
-static int __init gdth_init_isa(u32 bios_adr,gdth_ha_str *ha)
-{
- register gdt2_dpram_str __iomem *dp2_ptr;
- int i;
- u8 irq_drq,prot_ver;
- u32 retries;
-
- TRACE(("gdth_init_isa() bios adr. %x\n",bios_adr));
-
- ha->brd = ioremap(bios_adr, sizeof(gdt2_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- dp2_ptr = ha->brd;
- writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */
- /* reset interface area */
- memset_io(&dp2_ptr->u, 0, sizeof(dp2_ptr->u));
- if (readl(&dp2_ptr->u) != 0) {
- printk("GDT-ISA: Initialization error (DPMEM write error)\n");
- iounmap(ha->brd);
- return 0;
- }
-
- /* disable board interrupts, read DRQ and IRQ */
- writeb(0xff, &dp2_ptr->io.irqdel);
- writeb(0x00, &dp2_ptr->io.irqen);
- writeb(0x00, &dp2_ptr->u.ic.S_Status);
- writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
-
- irq_drq = readb(&dp2_ptr->io.rq);
- for (i=0; i<3; ++i) {
- if ((irq_drq & 1)==0)
- break;
- irq_drq >>= 1;
- }
- ha->drq = gdth_drq_tab[i];
-
- irq_drq = readb(&dp2_ptr->io.rq) >> 3;
- for (i=1; i<5; ++i) {
- if ((irq_drq & 1)==0)
- break;
- irq_drq >>= 1;
- }
- ha->irq = gdth_irq_tab[i];
-
- /* deinitialize services */
- writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
- writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
- writeb(0, &dp2_ptr->io.event);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
- if (--retries == 0) {
- printk("GDT-ISA: Initialization error (DEINIT failed)\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- prot_ver = (u8)readl(&dp2_ptr->u.ic.S_Info[0]);
- writeb(0, &dp2_ptr->u.ic.Status);
- writeb(0xff, &dp2_ptr->io.irqdel);
- if (prot_ver != PROTOCOL_VERSION) {
- printk("GDT-ISA: Illegal protocol version\n");
- iounmap(ha->brd);
- return 0;
- }
-
- ha->oem_id = OEM_ID_ICP;
- ha->type = GDT_ISA;
- ha->ic_all_size = sizeof(dp2_ptr->u);
- ha->stype= GDT2_ID;
- ha->brd_phys = bios_adr >> 4;
-
- /* special request to controller BIOS */
- writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
- writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
- writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
- writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
- writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
- writeb(0, &dp2_ptr->io.event);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
- if (--retries == 0) {
- printk("GDT-ISA: Initialization error\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- writeb(0, &dp2_ptr->u.ic.Status);
- writeb(0xff, &dp2_ptr->io.irqdel);
-
- ha->dma64_support = 0;
- return 1;
-}
-#endif /* CONFIG_ISA */
-
-#ifdef CONFIG_PCI
-static int __devinit gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr,
- gdth_ha_str *ha)
-{
- register gdt6_dpram_str __iomem *dp6_ptr;
- register gdt6c_dpram_str __iomem *dp6c_ptr;
- register gdt6m_dpram_str __iomem *dp6m_ptr;
- u32 retries;
- u8 prot_ver;
- u16 command;
- int i, found = FALSE;
-
- TRACE(("gdth_init_pci()\n"));
-
- if (pdev->vendor == PCI_VENDOR_ID_INTEL)
- ha->oem_id = OEM_ID_INTEL;
- else
- ha->oem_id = OEM_ID_ICP;
- ha->brd_phys = (pdev->bus->number << 8) | (pdev->devfn & 0xf8);
- ha->stype = (u32)pdev->device;
- ha->irq = pdev->irq;
- ha->pdev = pdev;
-
- if (ha->pdev->device <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000/B */
- TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
- ha->brd = ioremap(pcistr->dpmem, sizeof(gdt6_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- /* check and reset interface area */
- dp6_ptr = ha->brd;
- writel(DPMEM_MAGIC, &dp6_ptr->u);
- if (readl(&dp6_ptr->u) != DPMEM_MAGIC) {
- printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n",
- pcistr->dpmem);
- found = FALSE;
- for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
- iounmap(ha->brd);
- ha->brd = ioremap(i, sizeof(u16));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- if (readw(ha->brd) != 0xffff) {
- TRACE2(("init_pci_old() address 0x%x busy\n", i));
- continue;
- }
- iounmap(ha->brd);
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, i);
- ha->brd = ioremap(i, sizeof(gdt6_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- dp6_ptr = ha->brd;
- writel(DPMEM_MAGIC, &dp6_ptr->u);
- if (readl(&dp6_ptr->u) == DPMEM_MAGIC) {
- printk("GDT-PCI: Use free address at 0x%x\n", i);
- found = TRUE;
- break;
- }
- }
- if (!found) {
- printk("GDT-PCI: No free address found!\n");
- iounmap(ha->brd);
- return 0;
- }
- }
- memset_io(&dp6_ptr->u, 0, sizeof(dp6_ptr->u));
- if (readl(&dp6_ptr->u) != 0) {
- printk("GDT-PCI: Initialization error (DPMEM write error)\n");
- iounmap(ha->brd);
- return 0;
- }
-
- /* disable board interrupts, deinit services */
- writeb(0xff, &dp6_ptr->io.irqdel);
- writeb(0x00, &dp6_ptr->io.irqen);
- writeb(0x00, &dp6_ptr->u.ic.S_Status);
- writeb(0x00, &dp6_ptr->u.ic.Cmd_Index);
-
- writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]);
- writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx);
- writeb(0, &dp6_ptr->io.event);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) {
- if (--retries == 0) {
- printk("GDT-PCI: Initialization error (DEINIT failed)\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- prot_ver = (u8)readl(&dp6_ptr->u.ic.S_Info[0]);
- writeb(0, &dp6_ptr->u.ic.S_Status);
- writeb(0xff, &dp6_ptr->io.irqdel);
- if (prot_ver != PROTOCOL_VERSION) {
- printk("GDT-PCI: Illegal protocol version\n");
- iounmap(ha->brd);
- return 0;
- }
-
- ha->type = GDT_PCI;
- ha->ic_all_size = sizeof(dp6_ptr->u);
-
- /* special command to controller BIOS */
- writel(0x00, &dp6_ptr->u.ic.S_Info[0]);
- writel(0x00, &dp6_ptr->u.ic.S_Info[1]);
- writel(0x00, &dp6_ptr->u.ic.S_Info[2]);
- writel(0x00, &dp6_ptr->u.ic.S_Info[3]);
- writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx);
- writeb(0, &dp6_ptr->io.event);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) {
- if (--retries == 0) {
- printk("GDT-PCI: Initialization error\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- writeb(0, &dp6_ptr->u.ic.S_Status);
- writeb(0xff, &dp6_ptr->io.irqdel);
-
- ha->dma64_support = 0;
-
- } else if (ha->pdev->device <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, ... */
- ha->plx = (gdt6c_plx_regs *)pcistr->io;
- TRACE2(("init_pci_new() dpmem %lx irq %d\n",
- pcistr->dpmem,ha->irq));
- ha->brd = ioremap(pcistr->dpmem, sizeof(gdt6c_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- iounmap(ha->brd);
- return 0;
- }
- /* check and reset interface area */
- dp6c_ptr = ha->brd;
- writel(DPMEM_MAGIC, &dp6c_ptr->u);
- if (readl(&dp6c_ptr->u) != DPMEM_MAGIC) {
- printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n",
- pcistr->dpmem);
- found = FALSE;
- for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
- iounmap(ha->brd);
- ha->brd = ioremap(i, sizeof(u16));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- if (readw(ha->brd) != 0xffff) {
- TRACE2(("init_pci_plx() address 0x%x busy\n", i));
- continue;
- }
- iounmap(ha->brd);
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_2, i);
- ha->brd = ioremap(i, sizeof(gdt6c_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- dp6c_ptr = ha->brd;
- writel(DPMEM_MAGIC, &dp6c_ptr->u);
- if (readl(&dp6c_ptr->u) == DPMEM_MAGIC) {
- printk("GDT-PCI: Use free address at 0x%x\n", i);
- found = TRUE;
- break;
- }
- }
- if (!found) {
- printk("GDT-PCI: No free address found!\n");
- iounmap(ha->brd);
- return 0;
- }
- }
- memset_io(&dp6c_ptr->u, 0, sizeof(dp6c_ptr->u));
- if (readl(&dp6c_ptr->u) != 0) {
- printk("GDT-PCI: Initialization error (DPMEM write error)\n");
- iounmap(ha->brd);
- return 0;
- }
-
- /* disable board interrupts, deinit services */
- outb(0x00,PTR2USHORT(&ha->plx->control1));
- outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
-
- writeb(0x00, &dp6c_ptr->u.ic.S_Status);
- writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index);
-
- writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]);
- writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx);
-
- outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
-
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) {
- if (--retries == 0) {
- printk("GDT-PCI: Initialization error (DEINIT failed)\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- prot_ver = (u8)readl(&dp6c_ptr->u.ic.S_Info[0]);
- writeb(0, &dp6c_ptr->u.ic.Status);
- if (prot_ver != PROTOCOL_VERSION) {
- printk("GDT-PCI: Illegal protocol version\n");
- iounmap(ha->brd);
- return 0;
- }
-
- ha->type = GDT_PCINEW;
- ha->ic_all_size = sizeof(dp6c_ptr->u);
-
- /* special command to controller BIOS */
- writel(0x00, &dp6c_ptr->u.ic.S_Info[0]);
- writel(0x00, &dp6c_ptr->u.ic.S_Info[1]);
- writel(0x00, &dp6c_ptr->u.ic.S_Info[2]);
- writel(0x00, &dp6c_ptr->u.ic.S_Info[3]);
- writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx);
-
- outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
-
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) {
- if (--retries == 0) {
- printk("GDT-PCI: Initialization error\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- writeb(0, &dp6c_ptr->u.ic.S_Status);
-
- ha->dma64_support = 0;
-
- } else { /* MPR */
- TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
- ha->brd = ioremap(pcistr->dpmem, sizeof(gdt6m_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
-
- /* manipulate config. space to enable DPMEM, start RP controller */
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- command |= 6;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- if (pci_resource_start(pdev, 8) == 1UL)
- pci_resource_start(pdev, 8) = 0UL;
- i = 0xFEFF0001UL;
- pci_write_config_dword(pdev, PCI_ROM_ADDRESS, i);
- gdth_delay(1);
- pci_write_config_dword(pdev, PCI_ROM_ADDRESS,
- pci_resource_start(pdev, 8));
-
- dp6m_ptr = ha->brd;
-
- /* Ensure that it is safe to access the non HW portions of DPMEM.
- * Aditional check needed for Xscale based RAID controllers */
- while( ((int)readb(&dp6m_ptr->i960r.sema0_reg) ) & 3 )
- gdth_delay(1);
-
- /* check and reset interface area */
- writel(DPMEM_MAGIC, &dp6m_ptr->u);
- if (readl(&dp6m_ptr->u) != DPMEM_MAGIC) {
- printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n",
- pcistr->dpmem);
- found = FALSE;
- for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
- iounmap(ha->brd);
- ha->brd = ioremap(i, sizeof(u16));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- if (readw(ha->brd) != 0xffff) {
- TRACE2(("init_pci_mpr() address 0x%x busy\n", i));
- continue;
- }
- iounmap(ha->brd);
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, i);
- ha->brd = ioremap(i, sizeof(gdt6m_dpram_str));
- if (ha->brd == NULL) {
- printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
- return 0;
- }
- dp6m_ptr = ha->brd;
- writel(DPMEM_MAGIC, &dp6m_ptr->u);
- if (readl(&dp6m_ptr->u) == DPMEM_MAGIC) {
- printk("GDT-PCI: Use free address at 0x%x\n", i);
- found = TRUE;
- break;
- }
- }
- if (!found) {
- printk("GDT-PCI: No free address found!\n");
- iounmap(ha->brd);
- return 0;
- }
- }
- memset_io(&dp6m_ptr->u, 0, sizeof(dp6m_ptr->u));
-
- /* disable board interrupts, deinit services */
- writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4,
- &dp6m_ptr->i960r.edoor_en_reg);
- writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
- writeb(0x00, &dp6m_ptr->u.ic.S_Status);
- writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index);
-
- writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]);
- writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx);
- writeb(1, &dp6m_ptr->i960r.ldoor_reg);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp6m_ptr->u.ic.S_Status) != 0xff) {
- if (--retries == 0) {
- printk("GDT-PCI: Initialization error (DEINIT failed)\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- prot_ver = (u8)readl(&dp6m_ptr->u.ic.S_Info[0]);
- writeb(0, &dp6m_ptr->u.ic.S_Status);
- if (prot_ver != PROTOCOL_VERSION) {
- printk("GDT-PCI: Illegal protocol version\n");
- iounmap(ha->brd);
- return 0;
- }
-
- ha->type = GDT_PCIMPR;
- ha->ic_all_size = sizeof(dp6m_ptr->u);
-
- /* special command to controller BIOS */
- writel(0x00, &dp6m_ptr->u.ic.S_Info[0]);
- writel(0x00, &dp6m_ptr->u.ic.S_Info[1]);
- writel(0x00, &dp6m_ptr->u.ic.S_Info[2]);
- writel(0x00, &dp6m_ptr->u.ic.S_Info[3]);
- writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx);
- writeb(1, &dp6m_ptr->i960r.ldoor_reg);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) {
- if (--retries == 0) {
- printk("GDT-PCI: Initialization error\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- writeb(0, &dp6m_ptr->u.ic.S_Status);
-
- /* read FW version to detect 64-bit DMA support */
- writeb(0xfd, &dp6m_ptr->u.ic.S_Cmd_Indx);
- writeb(1, &dp6m_ptr->i960r.ldoor_reg);
- retries = INIT_RETRIES;
- gdth_delay(20);
- while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfd) {
- if (--retries == 0) {
- printk("GDT-PCI: Initialization error (DEINIT failed)\n");
- iounmap(ha->brd);
- return 0;
- }
- gdth_delay(1);
- }
- prot_ver = (u8)(readl(&dp6m_ptr->u.ic.S_Info[0]) >> 16);
- writeb(0, &dp6m_ptr->u.ic.S_Status);
- if (prot_ver < 0x2b) /* FW < x.43: no 64-bit DMA support */
- ha->dma64_support = 0;
- else
- ha->dma64_support = 1;
- }
-
- return 1;
-}
-#endif /* CONFIG_PCI */
-
-/* controller protocol functions */
-
-static void __devinit gdth_enable_int(gdth_ha_str *ha)
-{
- unsigned long flags;
- gdt2_dpram_str __iomem *dp2_ptr;
- gdt6_dpram_str __iomem *dp6_ptr;
- gdt6m_dpram_str __iomem *dp6m_ptr;
-
- TRACE(("gdth_enable_int() hanum %d\n",ha->hanum));
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- if (ha->type == GDT_EISA) {
- outb(0xff, ha->bmic + EDOORREG);
- outb(0xff, ha->bmic + EDENABREG);
- outb(0x01, ha->bmic + EINTENABREG);
- } else if (ha->type == GDT_ISA) {
- dp2_ptr = ha->brd;
- writeb(1, &dp2_ptr->io.irqdel);
- writeb(0, &dp2_ptr->u.ic.Cmd_Index);
- writeb(1, &dp2_ptr->io.irqen);
- } else if (ha->type == GDT_PCI) {
- dp6_ptr = ha->brd;
- writeb(1, &dp6_ptr->io.irqdel);
- writeb(0, &dp6_ptr->u.ic.Cmd_Index);
- writeb(1, &dp6_ptr->io.irqen);
- } else if (ha->type == GDT_PCINEW) {
- outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
- outb(0x03, PTR2USHORT(&ha->plx->control1));
- } else if (ha->type == GDT_PCIMPR) {
- dp6m_ptr = ha->brd;
- writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
- writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4,
- &dp6m_ptr->i960r.edoor_en_reg);
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-}
-
-/* return IStatus if interrupt was from this card else 0 */
-static u8 gdth_get_status(gdth_ha_str *ha)
-{
- u8 IStatus = 0;
-
- TRACE(("gdth_get_status() irq %d ctr_count %d\n", ha->irq, gdth_ctr_count));
-
- if (ha->type == GDT_EISA)
- IStatus = inb((u16)ha->bmic + EDOORREG);
- else if (ha->type == GDT_ISA)
- IStatus =
- readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index);
- else if (ha->type == GDT_PCI)
- IStatus =
- readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index);
- else if (ha->type == GDT_PCINEW)
- IStatus = inb(PTR2USHORT(&ha->plx->edoor_reg));
- else if (ha->type == GDT_PCIMPR)
- IStatus =
- readb(&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.edoor_reg);
-
- return IStatus;
-}
-
-static int gdth_test_busy(gdth_ha_str *ha)
-{
- register int gdtsema0 = 0;
-
- TRACE(("gdth_test_busy() hanum %d\n", ha->hanum));
-
- if (ha->type == GDT_EISA)
- gdtsema0 = (int)inb(ha->bmic + SEMA0REG);
- else if (ha->type == GDT_ISA)
- gdtsema0 = (int)readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
- else if (ha->type == GDT_PCI)
- gdtsema0 = (int)readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
- else if (ha->type == GDT_PCINEW)
- gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg));
- else if (ha->type == GDT_PCIMPR)
- gdtsema0 =
- (int)readb(&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.sema0_reg);
-
- return (gdtsema0 & 1);
-}
-
-
-static int gdth_get_cmd_index(gdth_ha_str *ha)
-{
- int i;
-
- TRACE(("gdth_get_cmd_index() hanum %d\n", ha->hanum));
-
- for (i=0; i<GDTH_MAXCMDS; ++i) {
- if (ha->cmd_tab[i].cmnd == UNUSED_CMND) {
- ha->cmd_tab[i].cmnd = ha->pccb->RequestBuffer;
- ha->cmd_tab[i].service = ha->pccb->Service;
- ha->pccb->CommandIndex = (u32)i+2;
- return (i+2);
- }
- }
- return 0;
-}
-
-
-static void gdth_set_sema0(gdth_ha_str *ha)
-{
- TRACE(("gdth_set_sema0() hanum %d\n", ha->hanum));
-
- if (ha->type == GDT_EISA) {
- outb(1, ha->bmic + SEMA0REG);
- } else if (ha->type == GDT_ISA) {
- writeb(1, &((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
- } else if (ha->type == GDT_PCI) {
- writeb(1, &((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
- } else if (ha->type == GDT_PCINEW) {
- outb(1, PTR2USHORT(&ha->plx->sema0_reg));
- } else if (ha->type == GDT_PCIMPR) {
- writeb(1, &((gdt6m_dpram_str __iomem *)ha->brd)->i960r.sema0_reg);
- }
-}
-
-
-static void gdth_copy_command(gdth_ha_str *ha)
-{
- register gdth_cmd_str *cmd_ptr;
- register gdt6m_dpram_str __iomem *dp6m_ptr;
- register gdt6c_dpram_str __iomem *dp6c_ptr;
- gdt6_dpram_str __iomem *dp6_ptr;
- gdt2_dpram_str __iomem *dp2_ptr;
- u16 cp_count,dp_offset,cmd_no;
-
- TRACE(("gdth_copy_command() hanum %d\n", ha->hanum));
-
- cp_count = ha->cmd_len;
- dp_offset= ha->cmd_offs_dpmem;
- cmd_no = ha->cmd_cnt;
- cmd_ptr = ha->pccb;
-
- ++ha->cmd_cnt;
- if (ha->type == GDT_EISA)
- return; /* no DPMEM, no copy */
-
- /* set cpcount dword aligned */
- if (cp_count & 3)
- cp_count += (4 - (cp_count & 3));
-
- ha->cmd_offs_dpmem += cp_count;
-
- /* set offset and service, copy command to DPMEM */
- if (ha->type == GDT_ISA) {
- dp2_ptr = ha->brd;
- writew(dp_offset + DPMEM_COMMAND_OFFSET,
- &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((u16)cmd_ptr->Service,
- &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
- memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
- } else if (ha->type == GDT_PCI) {
- dp6_ptr = ha->brd;
- writew(dp_offset + DPMEM_COMMAND_OFFSET,
- &dp6_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((u16)cmd_ptr->Service,
- &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id);
- memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
- } else if (ha->type == GDT_PCINEW) {
- dp6c_ptr = ha->brd;
- writew(dp_offset + DPMEM_COMMAND_OFFSET,
- &dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((u16)cmd_ptr->Service,
- &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id);
- memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
- } else if (ha->type == GDT_PCIMPR) {
- dp6m_ptr = ha->brd;
- writew(dp_offset + DPMEM_COMMAND_OFFSET,
- &dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
- writew((u16)cmd_ptr->Service,
- &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id);
- memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
- }
-}
-
-
-static void gdth_release_event(gdth_ha_str *ha)
-{
- TRACE(("gdth_release_event() hanum %d\n", ha->hanum));
-
-#ifdef GDTH_STATISTICS
- {
- u32 i,j;
- for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
- if (ha->cmd_tab[j].cmnd != UNUSED_CMND)
- ++i;
- }
- if (max_index < i) {
- max_index = i;
- TRACE3(("GDT: max_index = %d\n",(u16)i));
- }
- }
-#endif
-
- if (ha->pccb->OpCode == GDT_INIT)
- ha->pccb->Service |= 0x80;
-
- if (ha->type == GDT_EISA) {
- if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */
- outl(ha->ccb_phys, ha->bmic + MAILBOXREG);
- outb(ha->pccb->Service, ha->bmic + LDOORREG);
- } else if (ha->type == GDT_ISA) {
- writeb(0, &((gdt2_dpram_str __iomem *)ha->brd)->io.event);
- } else if (ha->type == GDT_PCI) {
- writeb(0, &((gdt6_dpram_str __iomem *)ha->brd)->io.event);
- } else if (ha->type == GDT_PCINEW) {
- outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
- } else if (ha->type == GDT_PCIMPR) {
- writeb(1, &((gdt6m_dpram_str __iomem *)ha->brd)->i960r.ldoor_reg);
- }
-}
-
-static int gdth_wait(gdth_ha_str *ha, int index, u32 time)
-{
- int answer_found = FALSE;
- int wait_index = 0;
-
- TRACE(("gdth_wait() hanum %d index %d time %d\n", ha->hanum, index, time));
-
- if (index == 0)
- return 1; /* no wait required */
-
- do {
- __gdth_interrupt(ha, true, &wait_index);
- if (wait_index == index) {
- answer_found = TRUE;
- break;
- }
- gdth_delay(1);
- } while (--time);
-
- while (gdth_test_busy(ha))
- gdth_delay(0);
-
- return (answer_found);
-}
-
-
-static int gdth_internal_cmd(gdth_ha_str *ha, u8 service, u16 opcode,
- u32 p1, u64 p2, u64 p3)
-{
- register gdth_cmd_str *cmd_ptr;
- int retries,index;
-
- TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode));
-
- cmd_ptr = ha->pccb;
- memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str));
-
- /* make command */
- for (retries = INIT_RETRIES;;) {
- cmd_ptr->Service = service;
- cmd_ptr->RequestBuffer = INTERNAL_CMND;
- if (!(index=gdth_get_cmd_index(ha))) {
- TRACE(("GDT: No free command index found\n"));
- return 0;
- }
- gdth_set_sema0(ha);
- cmd_ptr->OpCode = opcode;
- cmd_ptr->BoardNode = LOCALBOARD;
- if (service == CACHESERVICE) {
- if (opcode == GDT_IOCTL) {
- cmd_ptr->u.ioctl.subfunc = p1;
- cmd_ptr->u.ioctl.channel = (u32)p2;
- cmd_ptr->u.ioctl.param_size = (u16)p3;
- cmd_ptr->u.ioctl.p_param = ha->scratch_phys;
- } else {
- if (ha->cache_feat & GDT_64BIT) {
- cmd_ptr->u.cache64.DeviceNo = (u16)p1;
- cmd_ptr->u.cache64.BlockNo = p2;
- } else {
- cmd_ptr->u.cache.DeviceNo = (u16)p1;
- cmd_ptr->u.cache.BlockNo = (u32)p2;
- }
- }
- } else if (service == SCSIRAWSERVICE) {
- if (ha->raw_feat & GDT_64BIT) {
- cmd_ptr->u.raw64.direction = p1;
- cmd_ptr->u.raw64.bus = (u8)p2;
- cmd_ptr->u.raw64.target = (u8)p3;
- cmd_ptr->u.raw64.lun = (u8)(p3 >> 8);
- } else {
- cmd_ptr->u.raw.direction = p1;
- cmd_ptr->u.raw.bus = (u8)p2;
- cmd_ptr->u.raw.target = (u8)p3;
- cmd_ptr->u.raw.lun = (u8)(p3 >> 8);
- }
- } else if (service == SCREENSERVICE) {
- if (opcode == GDT_REALTIME) {
- *(u32 *)&cmd_ptr->u.screen.su.data[0] = p1;
- *(u32 *)&cmd_ptr->u.screen.su.data[4] = (u32)p2;
- *(u32 *)&cmd_ptr->u.screen.su.data[8] = (u32)p3;
- }
- }
- ha->cmd_len = sizeof(gdth_cmd_str);
- ha->cmd_offs_dpmem = 0;
- ha->cmd_cnt = 0;
- gdth_copy_command(ha);
- gdth_release_event(ha);
- gdth_delay(20);
- if (!gdth_wait(ha, index, INIT_TIMEOUT)) {
- printk("GDT: Initialization error (timeout service %d)\n",service);
- return 0;
- }
- if (ha->status != S_BSY || --retries == 0)
- break;
- gdth_delay(1);
- }
-
- return (ha->status != S_OK ? 0:1);
-}
-
-
-/* search for devices */
-
-static int __devinit gdth_search_drives(gdth_ha_str *ha)
-{
- u16 cdev_cnt, i;
- int ok;
- u32 bus_no, drv_cnt, drv_no, j;
- gdth_getch_str *chn;
- gdth_drlist_str *drl;
- gdth_iochan_str *ioc;
- gdth_raw_iochan_str *iocr;
- gdth_arcdl_str *alst;
- gdth_alist_str *alst2;
- gdth_oem_str_ioctl *oemstr;
-#ifdef INT_COAL
- gdth_perf_modes *pmod;
-#endif
-
-#ifdef GDTH_RTC
- u8 rtc[12];
- unsigned long flags;
-#endif
-
- TRACE(("gdth_search_drives() hanum %d\n", ha->hanum));
- ok = 0;
-
- /* initialize controller services, at first: screen service */
- ha->screen_feat = 0;
- if (!force_dma32) {
- ok = gdth_internal_cmd(ha, SCREENSERVICE, GDT_X_INIT_SCR, 0, 0, 0);
- if (ok)
- ha->screen_feat = GDT_64BIT;
- }
- if (force_dma32 || (!ok && ha->status == (u16)S_NOFUNC))
- ok = gdth_internal_cmd(ha, SCREENSERVICE, GDT_INIT, 0, 0, 0);
- if (!ok) {
- printk("GDT-HA %d: Initialization error screen service (code %d)\n",
- ha->hanum, ha->status);
- return 0;
- }
- TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
-
-#ifdef GDTH_RTC
- /* read realtime clock info, send to controller */
- /* 1. wait for the falling edge of update flag */
- spin_lock_irqsave(&rtc_lock, flags);
- for (j = 0; j < 1000000; ++j)
- if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
- break;
- for (j = 0; j < 1000000; ++j)
- if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
- break;
- /* 2. read info */
- do {
- for (j = 0; j < 12; ++j)
- rtc[j] = CMOS_READ(j);
- } while (rtc[0] != CMOS_READ(0));
- spin_unlock_irqrestore(&rtc_lock, flags);
- TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(u32 *)&rtc[0],
- *(u32 *)&rtc[4], *(u32 *)&rtc[8]));
- /* 3. send to controller firmware */
- gdth_internal_cmd(ha, SCREENSERVICE, GDT_REALTIME, *(u32 *)&rtc[0],
- *(u32 *)&rtc[4], *(u32 *)&rtc[8]);
-#endif
-
- /* unfreeze all IOs */
- gdth_internal_cmd(ha, CACHESERVICE, GDT_UNFREEZE_IO, 0, 0, 0);
-
- /* initialize cache service */
- ha->cache_feat = 0;
- if (!force_dma32) {
- ok = gdth_internal_cmd(ha, CACHESERVICE, GDT_X_INIT_HOST, LINUX_OS,
- 0, 0);
- if (ok)
- ha->cache_feat = GDT_64BIT;
- }
- if (force_dma32 || (!ok && ha->status == (u16)S_NOFUNC))
- ok = gdth_internal_cmd(ha, CACHESERVICE, GDT_INIT, LINUX_OS, 0, 0);
- if (!ok) {
- printk("GDT-HA %d: Initialization error cache service (code %d)\n",
- ha->hanum, ha->status);
- return 0;
- }
- TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
- cdev_cnt = (u16)ha->info;
- ha->fw_vers = ha->service;
-
-#ifdef INT_COAL
- if (ha->type == GDT_PCIMPR) {
- /* set perf. modes */
- pmod = (gdth_perf_modes *)ha->pscratch;
- pmod->version = 1;
- pmod->st_mode = 1; /* enable one status buffer */
- *((u64 *)&pmod->st_buff_addr1) = ha->coal_stat_phys;
- pmod->st_buff_indx1 = COALINDEX;
- pmod->st_buff_addr2 = 0;
- pmod->st_buff_u_addr2 = 0;
- pmod->st_buff_indx2 = 0;
- pmod->st_buff_size = sizeof(gdth_coal_status) * MAXOFFSETS;
- pmod->cmd_mode = 0; // disable all cmd buffers
- pmod->cmd_buff_addr1 = 0;
- pmod->cmd_buff_u_addr1 = 0;
- pmod->cmd_buff_indx1 = 0;
- pmod->cmd_buff_addr2 = 0;
- pmod->cmd_buff_u_addr2 = 0;
- pmod->cmd_buff_indx2 = 0;
- pmod->cmd_buff_size = 0;
- pmod->reserved1 = 0;
- pmod->reserved2 = 0;
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, SET_PERF_MODES,
- INVALID_CHANNEL,sizeof(gdth_perf_modes))) {
- printk("GDT-HA %d: Interrupt coalescing activated\n", ha->hanum);
- }
- }
-#endif
-
- /* detect number of buses - try new IOCTL */
- iocr = (gdth_raw_iochan_str *)ha->pscratch;
- iocr->hdr.version = 0xffffffff;
- iocr->hdr.list_entries = MAXBUS;
- iocr->hdr.first_chan = 0;
- iocr->hdr.last_chan = MAXBUS-1;
- iocr->hdr.list_offset = GDTOFFSOF(gdth_raw_iochan_str, list[0]);
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, IOCHAN_RAW_DESC,
- INVALID_CHANNEL,sizeof(gdth_raw_iochan_str))) {
- TRACE2(("IOCHAN_RAW_DESC supported!\n"));
- ha->bus_cnt = iocr->hdr.chan_count;
- for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
- if (iocr->list[bus_no].proc_id < MAXID)
- ha->bus_id[bus_no] = iocr->list[bus_no].proc_id;
- else
- ha->bus_id[bus_no] = 0xff;
- }
- } else {
- /* old method */
- chn = (gdth_getch_str *)ha->pscratch;
- for (bus_no = 0; bus_no < MAXBUS; ++bus_no) {
- chn->channel_no = bus_no;
- if (!gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL,
- SCSI_CHAN_CNT | L_CTRL_PATTERN,
- IO_CHANNEL | INVALID_CHANNEL,
- sizeof(gdth_getch_str))) {
- if (bus_no == 0) {
- printk("GDT-HA %d: Error detecting channel count (0x%x)\n",
- ha->hanum, ha->status);
- return 0;
- }
- break;
- }
- if (chn->siop_id < MAXID)
- ha->bus_id[bus_no] = chn->siop_id;
- else
- ha->bus_id[bus_no] = 0xff;
- }
- ha->bus_cnt = (u8)bus_no;
- }
- TRACE2(("gdth_search_drives() %d channels\n",ha->bus_cnt));
-
- /* read cache configuration */
- if (!gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, CACHE_INFO,
- INVALID_CHANNEL,sizeof(gdth_cinfo_str))) {
- printk("GDT-HA %d: Initialization error cache service (code %d)\n",
- ha->hanum, ha->status);
- return 0;
- }
- ha->cpar = ((gdth_cinfo_str *)ha->pscratch)->cpar;
- TRACE2(("gdth_search_drives() cinfo: vs %x sta %d str %d dw %d b %d\n",
- ha->cpar.version,ha->cpar.state,ha->cpar.strategy,
- ha->cpar.write_back,ha->cpar.block_size));
-
- /* read board info and features */
- ha->more_proc = FALSE;
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, BOARD_INFO,
- INVALID_CHANNEL,sizeof(gdth_binfo_str))) {
- memcpy(&ha->binfo, (gdth_binfo_str *)ha->pscratch,
- sizeof(gdth_binfo_str));
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, BOARD_FEATURES,
- INVALID_CHANNEL,sizeof(gdth_bfeat_str))) {
- TRACE2(("BOARD_INFO/BOARD_FEATURES supported\n"));
- ha->bfeat = *(gdth_bfeat_str *)ha->pscratch;
- ha->more_proc = TRUE;
- }
- } else {
- TRACE2(("BOARD_INFO requires firmware >= 1.10/2.08\n"));
- strcpy(ha->binfo.type_string, gdth_ctr_name(ha));
- }
- TRACE2(("Controller name: %s\n",ha->binfo.type_string));
-
- /* read more informations */
- if (ha->more_proc) {
- /* physical drives, channel addresses */
- ioc = (gdth_iochan_str *)ha->pscratch;
- ioc->hdr.version = 0xffffffff;
- ioc->hdr.list_entries = MAXBUS;
- ioc->hdr.first_chan = 0;
- ioc->hdr.last_chan = MAXBUS-1;
- ioc->hdr.list_offset = GDTOFFSOF(gdth_iochan_str, list[0]);
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, IOCHAN_DESC,
- INVALID_CHANNEL,sizeof(gdth_iochan_str))) {
- for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
- ha->raw[bus_no].address = ioc->list[bus_no].address;
- ha->raw[bus_no].local_no = ioc->list[bus_no].local_no;
- }
- } else {
- for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
- ha->raw[bus_no].address = IO_CHANNEL;
- ha->raw[bus_no].local_no = bus_no;
- }
- }
- for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
- chn = (gdth_getch_str *)ha->pscratch;
- chn->channel_no = ha->raw[bus_no].local_no;
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL,
- SCSI_CHAN_CNT | L_CTRL_PATTERN,
- ha->raw[bus_no].address | INVALID_CHANNEL,
- sizeof(gdth_getch_str))) {
- ha->raw[bus_no].pdev_cnt = chn->drive_cnt;
- TRACE2(("Channel %d: %d phys. drives\n",
- bus_no,chn->drive_cnt));
- }
- if (ha->raw[bus_no].pdev_cnt > 0) {
- drl = (gdth_drlist_str *)ha->pscratch;
- drl->sc_no = ha->raw[bus_no].local_no;
- drl->sc_cnt = ha->raw[bus_no].pdev_cnt;
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL,
- SCSI_DR_LIST | L_CTRL_PATTERN,
- ha->raw[bus_no].address | INVALID_CHANNEL,
- sizeof(gdth_drlist_str))) {
- for (j = 0; j < ha->raw[bus_no].pdev_cnt; ++j)
- ha->raw[bus_no].id_list[j] = drl->sc_list[j];
- } else {
- ha->raw[bus_no].pdev_cnt = 0;
- }
- }
- }
-
- /* logical drives */
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, CACHE_DRV_CNT,
- INVALID_CHANNEL,sizeof(u32))) {
- drv_cnt = *(u32 *)ha->pscratch;
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, CACHE_DRV_LIST,
- INVALID_CHANNEL,drv_cnt * sizeof(u32))) {
- for (j = 0; j < drv_cnt; ++j) {
- drv_no = ((u32 *)ha->pscratch)[j];
- if (drv_no < MAX_LDRIVES) {
- ha->hdr[drv_no].is_logdrv = TRUE;
- TRACE2(("Drive %d is log. drive\n",drv_no));
- }
- }
- }
- alst = (gdth_arcdl_str *)ha->pscratch;
- alst->entries_avail = MAX_LDRIVES;
- alst->first_entry = 0;
- alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]);
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL,
- ARRAY_DRV_LIST2 | LA_CTRL_PATTERN,
- INVALID_CHANNEL, sizeof(gdth_arcdl_str) +
- (alst->entries_avail-1) * sizeof(gdth_alist_str))) {
- for (j = 0; j < alst->entries_init; ++j) {
- ha->hdr[j].is_arraydrv = alst->list[j].is_arrayd;
- ha->hdr[j].is_master = alst->list[j].is_master;
- ha->hdr[j].is_parity = alst->list[j].is_parity;
- ha->hdr[j].is_hotfix = alst->list[j].is_hotfix;
- ha->hdr[j].master_no = alst->list[j].cd_handle;
- }
- } else if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL,
- ARRAY_DRV_LIST | LA_CTRL_PATTERN,
- 0, 35 * sizeof(gdth_alist_str))) {
- for (j = 0; j < 35; ++j) {
- alst2 = &((gdth_alist_str *)ha->pscratch)[j];
- ha->hdr[j].is_arraydrv = alst2->is_arrayd;
- ha->hdr[j].is_master = alst2->is_master;
- ha->hdr[j].is_parity = alst2->is_parity;
- ha->hdr[j].is_hotfix = alst2->is_hotfix;
- ha->hdr[j].master_no = alst2->cd_handle;
- }
- }
- }
- }
-
- /* initialize raw service */
- ha->raw_feat = 0;
- if (!force_dma32) {
- ok = gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_X_INIT_RAW, 0, 0, 0);
- if (ok)
- ha->raw_feat = GDT_64BIT;
- }
- if (force_dma32 || (!ok && ha->status == (u16)S_NOFUNC))
- ok = gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_INIT, 0, 0, 0);
- if (!ok) {
- printk("GDT-HA %d: Initialization error raw service (code %d)\n",
- ha->hanum, ha->status);
- return 0;
- }
- TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
-
- /* set/get features raw service (scatter/gather) */
- if (gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_SET_FEAT, SCATTER_GATHER,
- 0, 0)) {
- TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n"));
- if (gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_GET_FEAT, 0, 0, 0)) {
- TRACE2(("gdth_search_dr(): get feat RAWSERVICE %d\n",
- ha->info));
- ha->raw_feat |= (u16)ha->info;
- }
- }
-
- /* set/get features cache service (equal to raw service) */
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_SET_FEAT, 0,
- SCATTER_GATHER,0)) {
- TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n"));
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_GET_FEAT, 0, 0, 0)) {
- TRACE2(("gdth_search_dr(): get feat CACHESERV. %d\n",
- ha->info));
- ha->cache_feat |= (u16)ha->info;
- }
- }
-
- /* reserve drives for raw service */
- if (reserve_mode != 0) {
- gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_RESERVE_ALL,
- reserve_mode == 1 ? 1 : 3, 0, 0);
- TRACE2(("gdth_search_drives(): RESERVE_ALL code %d\n",
- ha->status));
- }
- for (i = 0; i < MAX_RES_ARGS; i += 4) {
- if (reserve_list[i] == ha->hanum && reserve_list[i+1] < ha->bus_cnt &&
- reserve_list[i+2] < ha->tid_cnt && reserve_list[i+3] < MAXLUN) {
- TRACE2(("gdth_search_drives(): reserve ha %d bus %d id %d lun %d\n",
- reserve_list[i], reserve_list[i+1],
- reserve_list[i+2], reserve_list[i+3]));
- if (!gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_RESERVE, 0,
- reserve_list[i+1], reserve_list[i+2] |
- (reserve_list[i+3] << 8))) {
- printk("GDT-HA %d: Error raw service (RESERVE, code %d)\n",
- ha->hanum, ha->status);
- }
- }
- }
-
- /* Determine OEM string using IOCTL */
- oemstr = (gdth_oem_str_ioctl *)ha->pscratch;
- oemstr->params.ctl_version = 0x01;
- oemstr->params.buffer_size = sizeof(oemstr->text);
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL,
- CACHE_READ_OEM_STRING_RECORD,INVALID_CHANNEL,
- sizeof(gdth_oem_str_ioctl))) {
- TRACE2(("gdth_search_drives(): CACHE_READ_OEM_STRING_RECORD OK\n"));
- printk("GDT-HA %d: Vendor: %s Name: %s\n",
- ha->hanum, oemstr->text.oem_company_name, ha->binfo.type_string);
- /* Save the Host Drive inquiry data */
- strlcpy(ha->oem_name,oemstr->text.scsi_host_drive_inquiry_vendor_id,
- sizeof(ha->oem_name));
- } else {
- /* Old method, based on PCI ID */
- TRACE2(("gdth_search_drives(): CACHE_READ_OEM_STRING_RECORD failed\n"));
- printk("GDT-HA %d: Name: %s\n",
- ha->hanum, ha->binfo.type_string);
- if (ha->oem_id == OEM_ID_INTEL)
- strlcpy(ha->oem_name,"Intel ", sizeof(ha->oem_name));
- else
- strlcpy(ha->oem_name,"ICP ", sizeof(ha->oem_name));
- }
-
- /* scanning for host drives */
- for (i = 0; i < cdev_cnt; ++i)
- gdth_analyse_hdrive(ha, i);
-
- TRACE(("gdth_search_drives() OK\n"));
- return 1;
-}
-
-static int gdth_analyse_hdrive(gdth_ha_str *ha, u16 hdrive)
-{
- u32 drv_cyls;
- int drv_hds, drv_secs;
-
- TRACE(("gdth_analyse_hdrive() hanum %d drive %d\n", ha->hanum, hdrive));
- if (hdrive >= MAX_HDRIVES)
- return 0;
-
- if (!gdth_internal_cmd(ha, CACHESERVICE, GDT_INFO, hdrive, 0, 0))
- return 0;
- ha->hdr[hdrive].present = TRUE;
- ha->hdr[hdrive].size = ha->info;
-
- /* evaluate mapping (sectors per head, heads per cylinder) */
- ha->hdr[hdrive].size &= ~SECS32;
- if (ha->info2 == 0) {
- gdth_eval_mapping(ha->hdr[hdrive].size,&drv_cyls,&drv_hds,&drv_secs);
- } else {
- drv_hds = ha->info2 & 0xff;
- drv_secs = (ha->info2 >> 8) & 0xff;
- drv_cyls = (u32)ha->hdr[hdrive].size / drv_hds / drv_secs;
- }
- ha->hdr[hdrive].heads = (u8)drv_hds;
- ha->hdr[hdrive].secs = (u8)drv_secs;
- /* round size */
- ha->hdr[hdrive].size = drv_cyls * drv_hds * drv_secs;
-
- if (ha->cache_feat & GDT_64BIT) {
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_X_INFO, hdrive, 0, 0)
- && ha->info2 != 0) {
- ha->hdr[hdrive].size = ((u64)ha->info2 << 32) | ha->info;
- }
- }
- TRACE2(("gdth_search_dr() cdr. %d size %d hds %d scs %d\n",
- hdrive,ha->hdr[hdrive].size,drv_hds,drv_secs));
-
- /* get informations about device */
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_DEVTYPE, hdrive, 0, 0)) {
- TRACE2(("gdth_search_dr() cache drive %d devtype %d\n",
- hdrive,ha->info));
- ha->hdr[hdrive].devtype = (u16)ha->info;
- }
-
- /* cluster info */
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_CLUST_INFO, hdrive, 0, 0)) {
- TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n",
- hdrive,ha->info));
- if (!shared_access)
- ha->hdr[hdrive].cluster_type = (u8)ha->info;
- }
-
- /* R/W attributes */
- if (gdth_internal_cmd(ha, CACHESERVICE, GDT_RW_ATTRIBS, hdrive, 0, 0)) {
- TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n",
- hdrive,ha->info));
- ha->hdr[hdrive].rw_attribs = (u8)ha->info;
- }
-
- return 1;
-}
-
-
-/* command queueing/sending functions */
-
-static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 priority)
-{
- struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
- register Scsi_Cmnd *pscp;
- register Scsi_Cmnd *nscp;
- unsigned long flags;
-
- TRACE(("gdth_putq() priority %d\n",priority));
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- if (!cmndinfo->internal_command)
- cmndinfo->priority = priority;
-
- if (ha->req_first==NULL) {
- ha->req_first = scp; /* queue was empty */
- scp->SCp.ptr = NULL;
- } else { /* queue not empty */
- pscp = ha->req_first;
- nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
- /* priority: 0-highest,..,0xff-lowest */
- while (nscp && gdth_cmnd_priv(nscp)->priority <= priority) {
- pscp = nscp;
- nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
- }
- pscp->SCp.ptr = (char *)scp;
- scp->SCp.ptr = (char *)nscp;
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-
-#ifdef GDTH_STATISTICS
- flags = 0;
- for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
- ++flags;
- if (max_rq < flags) {
- max_rq = flags;
- TRACE3(("GDT: max_rq = %d\n",(u16)max_rq));
- }
-#endif
-}
-
-static void gdth_next(gdth_ha_str *ha)
-{
- register Scsi_Cmnd *pscp;
- register Scsi_Cmnd *nscp;
- u8 b, t, l, firsttime;
- u8 this_cmd, next_cmd;
- unsigned long flags = 0;
- int cmd_index;
-
- TRACE(("gdth_next() hanum %d\n", ha->hanum));
- if (!gdth_polling)
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- ha->cmd_cnt = ha->cmd_offs_dpmem = 0;
- this_cmd = firsttime = TRUE;
- next_cmd = gdth_polling ? FALSE:TRUE;
- cmd_index = 0;
-
- for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) {
- struct gdth_cmndinfo *nscp_cmndinfo = gdth_cmnd_priv(nscp);
- if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr)
- pscp = (Scsi_Cmnd *)pscp->SCp.ptr;
- if (!nscp_cmndinfo->internal_command) {
- b = nscp->device->channel;
- t = nscp->device->id;
- l = nscp->device->lun;
- if (nscp_cmndinfo->priority >= DEFAULT_PRI) {
- if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha,b)].lock) ||
- (b == ha->virt_bus && t < MAX_HDRIVES && ha->hdr[t].lock))
- continue;
- }
- } else
- b = t = l = 0;
-
- if (firsttime) {
- if (gdth_test_busy(ha)) { /* controller busy ? */
- TRACE(("gdth_next() controller %d busy !\n", ha->hanum));
- if (!gdth_polling) {
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return;
- }
- while (gdth_test_busy(ha))
- gdth_delay(1);
- }
- firsttime = FALSE;
- }
-
- if (!nscp_cmndinfo->internal_command) {
- if (nscp_cmndinfo->phase == -1) {
- nscp_cmndinfo->phase = CACHESERVICE; /* default: cache svc. */
- if (nscp->cmnd[0] == TEST_UNIT_READY) {
- TRACE2(("TEST_UNIT_READY Bus %d Id %d LUN %d\n",
- b, t, l));
- /* TEST_UNIT_READY -> set scan mode */
- if ((ha->scan_mode & 0x0f) == 0) {
- if (b == 0 && t == 0 && l == 0) {
- ha->scan_mode |= 1;
- TRACE2(("Scan mode: 0x%x\n", ha->scan_mode));
- }
- } else if ((ha->scan_mode & 0x0f) == 1) {
- if (b == 0 && ((t == 0 && l == 1) ||
- (t == 1 && l == 0))) {
- nscp_cmndinfo->OpCode = GDT_SCAN_START;
- nscp_cmndinfo->phase = ((ha->scan_mode & 0x10 ? 1:0) << 8)
- | SCSIRAWSERVICE;
- ha->scan_mode = 0x12;
- TRACE2(("Scan mode: 0x%x (SCAN_START)\n",
- ha->scan_mode));
- } else {
- ha->scan_mode &= 0x10;
- TRACE2(("Scan mode: 0x%x\n", ha->scan_mode));
- }
- } else if (ha->scan_mode == 0x12) {
- if (b == ha->bus_cnt && t == ha->tid_cnt-1) {
- nscp_cmndinfo->phase = SCSIRAWSERVICE;
- nscp_cmndinfo->OpCode = GDT_SCAN_END;
- ha->scan_mode &= 0x10;
- TRACE2(("Scan mode: 0x%x (SCAN_END)\n",
- ha->scan_mode));
- }
- }
- }
- if (b == ha->virt_bus && nscp->cmnd[0] != INQUIRY &&
- nscp->cmnd[0] != READ_CAPACITY && nscp->cmnd[0] != MODE_SENSE &&
- (ha->hdr[t].cluster_type & CLUSTER_DRIVE)) {
- /* always GDT_CLUST_INFO! */
- nscp_cmndinfo->OpCode = GDT_CLUST_INFO;
- }
- }
- }
-
- if (nscp_cmndinfo->OpCode != -1) {
- if ((nscp_cmndinfo->phase & 0xff) == CACHESERVICE) {
- if (!(cmd_index=gdth_fill_cache_cmd(ha, nscp, t)))
- this_cmd = FALSE;
- next_cmd = FALSE;
- } else if ((nscp_cmndinfo->phase & 0xff) == SCSIRAWSERVICE) {
- if (!(cmd_index=gdth_fill_raw_cmd(ha, nscp, BUS_L2P(ha, b))))
- this_cmd = FALSE;
- next_cmd = FALSE;
- } else {
- memset((char*)nscp->sense_buffer,0,16);
- nscp->sense_buffer[0] = 0x70;
- nscp->sense_buffer[2] = NOT_READY;
- nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
- if (!nscp_cmndinfo->wait_for_completion)
- nscp_cmndinfo->wait_for_completion++;
- else
- gdth_scsi_done(nscp);
- }
- } else if (gdth_cmnd_priv(nscp)->internal_command) {
- if (!(cmd_index=gdth_special_cmd(ha, nscp)))
- this_cmd = FALSE;
- next_cmd = FALSE;
- } else if (b != ha->virt_bus) {
- if (ha->raw[BUS_L2P(ha,b)].io_cnt[t] >= GDTH_MAX_RAW ||
- !(cmd_index=gdth_fill_raw_cmd(ha, nscp, BUS_L2P(ha, b))))
- this_cmd = FALSE;
- else
- ha->raw[BUS_L2P(ha,b)].io_cnt[t]++;
- } else if (t >= MAX_HDRIVES || !ha->hdr[t].present || l != 0) {
- TRACE2(("Command 0x%x to bus %d id %d lun %d -> IGNORE\n",
- nscp->cmnd[0], b, t, l));
- nscp->result = DID_BAD_TARGET << 16;
- if (!nscp_cmndinfo->wait_for_completion)
- nscp_cmndinfo->wait_for_completion++;
- else
- gdth_scsi_done(nscp);
- } else {
- switch (nscp->cmnd[0]) {
- case TEST_UNIT_READY:
- case INQUIRY:
- case REQUEST_SENSE:
- case READ_CAPACITY:
- case VERIFY:
- case START_STOP:
- case MODE_SENSE:
- case SERVICE_ACTION_IN:
- TRACE(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
- nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
- nscp->cmnd[4],nscp->cmnd[5]));
- if (ha->hdr[t].media_changed && nscp->cmnd[0] != INQUIRY) {
- /* return UNIT_ATTENTION */
- TRACE2(("cmd 0x%x target %d: UNIT_ATTENTION\n",
- nscp->cmnd[0], t));
- ha->hdr[t].media_changed = FALSE;
- memset((char*)nscp->sense_buffer,0,16);
- nscp->sense_buffer[0] = 0x70;
- nscp->sense_buffer[2] = UNIT_ATTENTION;
- nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
- if (!nscp_cmndinfo->wait_for_completion)
- nscp_cmndinfo->wait_for_completion++;
- else
- gdth_scsi_done(nscp);
- } else if (gdth_internal_cache_cmd(ha, nscp))
- gdth_scsi_done(nscp);
- break;
-
- case ALLOW_MEDIUM_REMOVAL:
- TRACE(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
- nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
- nscp->cmnd[4],nscp->cmnd[5]));
- if ( (nscp->cmnd[4]&1) && !(ha->hdr[t].devtype&1) ) {
- TRACE(("Prevent r. nonremov. drive->do nothing\n"));
- nscp->result = DID_OK << 16;
- nscp->sense_buffer[0] = 0;
- if (!nscp_cmndinfo->wait_for_completion)
- nscp_cmndinfo->wait_for_completion++;
- else
- gdth_scsi_done(nscp);
- } else {
- nscp->cmnd[3] = (ha->hdr[t].devtype&1) ? 1:0;
- TRACE(("Prevent/allow r. %d rem. drive %d\n",
- nscp->cmnd[4],nscp->cmnd[3]));
- if (!(cmd_index=gdth_fill_cache_cmd(ha, nscp, t)))
- this_cmd = FALSE;
- }
- break;
-
- case RESERVE:
- case RELEASE:
- TRACE2(("cache cmd %s\n",nscp->cmnd[0] == RESERVE ?
- "RESERVE" : "RELEASE"));
- if (!(cmd_index=gdth_fill_cache_cmd(ha, nscp, t)))
- this_cmd = FALSE;
- break;
-
- case READ_6:
- case WRITE_6:
- case READ_10:
- case WRITE_10:
- case READ_16:
- case WRITE_16:
- if (ha->hdr[t].media_changed) {
- /* return UNIT_ATTENTION */
- TRACE2(("cmd 0x%x target %d: UNIT_ATTENTION\n",
- nscp->cmnd[0], t));
- ha->hdr[t].media_changed = FALSE;
- memset((char*)nscp->sense_buffer,0,16);
- nscp->sense_buffer[0] = 0x70;
- nscp->sense_buffer[2] = UNIT_ATTENTION;
- nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
- if (!nscp_cmndinfo->wait_for_completion)
- nscp_cmndinfo->wait_for_completion++;
- else
- gdth_scsi_done(nscp);
- } else if (!(cmd_index=gdth_fill_cache_cmd(ha, nscp, t)))
- this_cmd = FALSE;
- break;
-
- default:
- TRACE2(("cache cmd %x/%x/%x/%x/%x/%x unknown\n",nscp->cmnd[0],
- nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
- nscp->cmnd[4],nscp->cmnd[5]));
- printk("GDT-HA %d: Unknown SCSI command 0x%x to cache service !\n",
- ha->hanum, nscp->cmnd[0]);
- nscp->result = DID_ABORT << 16;
- if (!nscp_cmndinfo->wait_for_completion)
- nscp_cmndinfo->wait_for_completion++;
- else
- gdth_scsi_done(nscp);
- break;
- }
- }
-
- if (!this_cmd)
- break;
- if (nscp == ha->req_first)
- ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr;
- else
- pscp->SCp.ptr = nscp->SCp.ptr;
- if (!next_cmd)
- break;
- }
-
- if (ha->cmd_cnt > 0) {
- gdth_release_event(ha);
- }
-
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-
- if (gdth_polling && ha->cmd_cnt > 0) {
- if (!gdth_wait(ha, cmd_index, POLL_TIMEOUT))
- printk("GDT-HA %d: Command %d timed out !\n",
- ha->hanum, cmd_index);
- }
-}
-
-/*
- * gdth_copy_internal_data() - copy to/from a buffer onto a scsi_cmnd's
- * buffers, kmap_atomic() as needed.
- */
-static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp,
- char *buffer, u16 count)
-{
- u16 cpcount,i, max_sg = scsi_sg_count(scp);
- u16 cpsum,cpnow;
- struct scatterlist *sl;
- char *address;
-
- cpcount = min_t(u16, count, scsi_bufflen(scp));
-
- if (cpcount) {
- cpsum=0;
- scsi_for_each_sg(scp, sl, max_sg, i) {
- unsigned long flags;
- cpnow = (u16)sl->length;
- TRACE(("copy_internal() now %d sum %d count %d %d\n",
- cpnow, cpsum, cpcount, scsi_bufflen(scp)));
- if (cpsum+cpnow > cpcount)
- cpnow = cpcount - cpsum;
- cpsum += cpnow;
- if (!sg_page(sl)) {
- printk("GDT-HA %d: invalid sc/gt element in gdth_copy_internal_data()\n",
- ha->hanum);
- return;
- }
- local_irq_save(flags);
- address = kmap_atomic(sg_page(sl), KM_BIO_SRC_IRQ) + sl->offset;
- memcpy(address, buffer, cpnow);
- flush_dcache_page(sg_page(sl));
- kunmap_atomic(address, KM_BIO_SRC_IRQ);
- local_irq_restore(flags);
- if (cpsum == cpcount)
- break;
- buffer += cpnow;
- }
- } else if (count) {
- printk("GDT-HA %d: SCSI command with no buffers but data transfer expected!\n",
- ha->hanum);
- WARN_ON(1);
- }
-}
-
-static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp)
-{
- u8 t;
- gdth_inq_data inq;
- gdth_rdcap_data rdc;
- gdth_sense_data sd;
- gdth_modep_data mpd;
- struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
-
- t = scp->device->id;
- TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n",
- scp->cmnd[0],t));
-
- scp->result = DID_OK << 16;
- scp->sense_buffer[0] = 0;
-
- switch (scp->cmnd[0]) {
- case TEST_UNIT_READY:
- case VERIFY:
- case START_STOP:
- TRACE2(("Test/Verify/Start hdrive %d\n",t));
- break;
-
- case INQUIRY:
- TRACE2(("Inquiry hdrive %d devtype %d\n",
- t,ha->hdr[t].devtype));
- inq.type_qual = (ha->hdr[t].devtype&4) ? TYPE_ROM:TYPE_DISK;
- /* you can here set all disks to removable, if you want to do
- a flush using the ALLOW_MEDIUM_REMOVAL command */
- inq.modif_rmb = 0x00;
- if ((ha->hdr[t].devtype & 1) ||
- (ha->hdr[t].cluster_type & CLUSTER_DRIVE))
- inq.modif_rmb = 0x80;
- inq.version = 2;
- inq.resp_aenc = 2;
- inq.add_length= 32;
- strcpy(inq.vendor,ha->oem_name);
- sprintf(inq.product,"Host Drive #%02d",t);
- strcpy(inq.revision," ");
- gdth_copy_internal_data(ha, scp, (char*)&inq, sizeof(gdth_inq_data));
- break;
-
- case REQUEST_SENSE:
- TRACE2(("Request sense hdrive %d\n",t));
- sd.errorcode = 0x70;
- sd.segno = 0x00;
- sd.key = NO_SENSE;
- sd.info = 0;
- sd.add_length= 0;
- gdth_copy_internal_data(ha, scp, (char*)&sd, sizeof(gdth_sense_data));
- break;
-
- case MODE_SENSE:
- TRACE2(("Mode sense hdrive %d\n",t));
- memset((char*)&mpd,0,sizeof(gdth_modep_data));
- mpd.hd.data_length = sizeof(gdth_modep_data);
- mpd.hd.dev_par = (ha->hdr[t].devtype&2) ? 0x80:0;
- mpd.hd.bd_length = sizeof(mpd.bd);
- mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16;
- mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8;
- mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff);
- gdth_copy_internal_data(ha, scp, (char*)&mpd, sizeof(gdth_modep_data));
- break;
-
- case READ_CAPACITY:
- TRACE2(("Read capacity hdrive %d\n",t));
- if (ha->hdr[t].size > (u64)0xffffffff)
- rdc.last_block_no = 0xffffffff;
- else
- rdc.last_block_no = cpu_to_be32(ha->hdr[t].size-1);
- rdc.block_length = cpu_to_be32(SECTOR_SIZE);
- gdth_copy_internal_data(ha, scp, (char*)&rdc, sizeof(gdth_rdcap_data));
- break;
-
- case SERVICE_ACTION_IN:
- if ((scp->cmnd[1] & 0x1f) == SAI_READ_CAPACITY_16 &&
- (ha->cache_feat & GDT_64BIT)) {
- gdth_rdcap16_data rdc16;
-
- TRACE2(("Read capacity (16) hdrive %d\n",t));
- rdc16.last_block_no = cpu_to_be64(ha->hdr[t].size-1);
- rdc16.block_length = cpu_to_be32(SECTOR_SIZE);
- gdth_copy_internal_data(ha, scp, (char*)&rdc16,
- sizeof(gdth_rdcap16_data));
- } else {
- scp->result = DID_ABORT << 16;
- }
- break;
-
- default:
- TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
- break;
- }
-
- if (!cmndinfo->wait_for_completion)
- cmndinfo->wait_for_completion++;
- else
- return 1;
-
- return 0;
-}
-
-static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u16 hdrive)
-{
- register gdth_cmd_str *cmdp;
- struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
- u32 cnt, blockcnt;
- u64 no, blockno;
- int i, cmd_index, read_write, sgcnt, mode64;
-
- cmdp = ha->pccb;
- TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n",
- scp->cmnd[0],scp->cmd_len,hdrive));
-
- if (ha->type==GDT_EISA && ha->cmd_cnt>0)
- return 0;
-
- mode64 = (ha->cache_feat & GDT_64BIT) ? TRUE : FALSE;
- /* test for READ_16, WRITE_16 if !mode64 ? ---
- not required, should not occur due to error return on
- READ_CAPACITY_16 */
-
- cmdp->Service = CACHESERVICE;
- cmdp->RequestBuffer = scp;
- /* search free command index */
- if (!(cmd_index=gdth_get_cmd_index(ha))) {
- TRACE(("GDT: No free command index found\n"));
- return 0;
- }
- /* if it's the first command, set command semaphore */
- if (ha->cmd_cnt == 0)
- gdth_set_sema0(ha);
-
- /* fill command */
- read_write = 0;
- if (cmndinfo->OpCode != -1)
- cmdp->OpCode = cmndinfo->OpCode; /* special cache cmd. */
- else if (scp->cmnd[0] == RESERVE)
- cmdp->OpCode = GDT_RESERVE_DRV;
- else if (scp->cmnd[0] == RELEASE)
- cmdp->OpCode = GDT_RELEASE_DRV;
- else if (scp->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
- if (scp->cmnd[4] & 1) /* prevent ? */
- cmdp->OpCode = GDT_MOUNT;
- else if (scp->cmnd[3] & 1) /* removable drive ? */
- cmdp->OpCode = GDT_UNMOUNT;
- else
- cmdp->OpCode = GDT_FLUSH;
- } else if (scp->cmnd[0] == WRITE_6 || scp->cmnd[0] == WRITE_10 ||
- scp->cmnd[0] == WRITE_12 || scp->cmnd[0] == WRITE_16
- ) {
- read_write = 1;
- if (gdth_write_through || ((ha->hdr[hdrive].rw_attribs & 1) &&
- (ha->cache_feat & GDT_WR_THROUGH)))
- cmdp->OpCode = GDT_WRITE_THR;
- else
- cmdp->OpCode = GDT_WRITE;
- } else {
- read_write = 2;
- cmdp->OpCode = GDT_READ;
- }
-
- cmdp->BoardNode = LOCALBOARD;
- if (mode64) {
- cmdp->u.cache64.DeviceNo = hdrive;
- cmdp->u.cache64.BlockNo = 1;
- cmdp->u.cache64.sg_canz = 0;
- } else {
- cmdp->u.cache.DeviceNo = hdrive;
- cmdp->u.cache.BlockNo = 1;
- cmdp->u.cache.sg_canz = 0;
- }
-
- if (read_write) {
- if (scp->cmd_len == 16) {
- memcpy(&no, &scp->cmnd[2], sizeof(u64));
- blockno = be64_to_cpu(no);
- memcpy(&cnt, &scp->cmnd[10], sizeof(u32));
- blockcnt = be32_to_cpu(cnt);
- } else if (scp->cmd_len == 10) {
- memcpy(&no, &scp->cmnd[2], sizeof(u32));
- blockno = be32_to_cpu(no);
- memcpy(&cnt, &scp->cmnd[7], sizeof(u16));
- blockcnt = be16_to_cpu(cnt);
- } else {
- memcpy(&no, &scp->cmnd[0], sizeof(u32));
- blockno = be32_to_cpu(no) & 0x001fffffUL;
- blockcnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
- }
- if (mode64) {
- cmdp->u.cache64.BlockNo = blockno;
- cmdp->u.cache64.BlockCnt = blockcnt;
- } else {
- cmdp->u.cache.BlockNo = (u32)blockno;
- cmdp->u.cache.BlockCnt = blockcnt;
- }
-
- if (scsi_bufflen(scp)) {
- cmndinfo->dma_dir = (read_write == 1 ?
- PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
- sgcnt = pci_map_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp),
- cmndinfo->dma_dir);
- if (mode64) {
- struct scatterlist *sl;
-
- cmdp->u.cache64.DestAddr= (u64)-1;
- cmdp->u.cache64.sg_canz = sgcnt;
- scsi_for_each_sg(scp, sl, sgcnt, i) {
- cmdp->u.cache64.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- if (cmdp->u.cache64.sg_lst[i].sg_ptr > (u64)0xffffffff)
- ha->dma64_cnt++;
- else
- ha->dma32_cnt++;
-#endif
- cmdp->u.cache64.sg_lst[i].sg_len = sg_dma_len(sl);
- }
- } else {
- struct scatterlist *sl;
-
- cmdp->u.cache.DestAddr= 0xffffffff;
- cmdp->u.cache.sg_canz = sgcnt;
- scsi_for_each_sg(scp, sl, sgcnt, i) {
- cmdp->u.cache.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- ha->dma32_cnt++;
-#endif
- cmdp->u.cache.sg_lst[i].sg_len = sg_dma_len(sl);
- }
- }
-
-#ifdef GDTH_STATISTICS
- if (max_sg < (u32)sgcnt) {
- max_sg = (u32)sgcnt;
- TRACE3(("GDT: max_sg = %d\n",max_sg));
- }
-#endif
-
- }
- }
- /* evaluate command size, check space */
- if (mode64) {
- TRACE(("cache cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
- cmdp->u.cache64.DestAddr,cmdp->u.cache64.sg_canz,
- cmdp->u.cache64.sg_lst[0].sg_ptr,
- cmdp->u.cache64.sg_lst[0].sg_len));
- TRACE(("cache cmd: cmd %d blockno. %d, blockcnt %d\n",
- cmdp->OpCode,cmdp->u.cache64.BlockNo,cmdp->u.cache64.BlockCnt));
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache64.sg_lst) +
- (u16)cmdp->u.cache64.sg_canz * sizeof(gdth_sg64_str);
- } else {
- TRACE(("cache cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
- cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz,
- cmdp->u.cache.sg_lst[0].sg_ptr,
- cmdp->u.cache.sg_lst[0].sg_len));
- TRACE(("cache cmd: cmd %d blockno. %d, blockcnt %d\n",
- cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) +
- (u16)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
- }
- if (ha->cmd_len & 3)
- ha->cmd_len += (4 - (ha->cmd_len & 3));
-
- if (ha->cmd_cnt > 0) {
- if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
- ha->ic_all_size) {
- TRACE2(("gdth_fill_cache() DPMEM overflow\n"));
- ha->cmd_tab[cmd_index-2].cmnd = UNUSED_CMND;
- return 0;
- }
- }
-
- /* copy command */
- gdth_copy_command(ha);
- return cmd_index;
-}
-
-static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, u8 b)
-{
- register gdth_cmd_str *cmdp;
- u16 i;
- dma_addr_t sense_paddr;
- int cmd_index, sgcnt, mode64;
- u8 t,l;
- struct page *page;
- unsigned long offset;
- struct gdth_cmndinfo *cmndinfo;
-
- t = scp->device->id;
- l = scp->device->lun;
- cmdp = ha->pccb;
- TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n",
- scp->cmnd[0],b,t,l));
-
- if (ha->type==GDT_EISA && ha->cmd_cnt>0)
- return 0;
-
- mode64 = (ha->raw_feat & GDT_64BIT) ? TRUE : FALSE;
-
- cmdp->Service = SCSIRAWSERVICE;
- cmdp->RequestBuffer = scp;
- /* search free command index */
- if (!(cmd_index=gdth_get_cmd_index(ha))) {
- TRACE(("GDT: No free command index found\n"));
- return 0;
- }
- /* if it's the first command, set command semaphore */
- if (ha->cmd_cnt == 0)
- gdth_set_sema0(ha);
-
- cmndinfo = gdth_cmnd_priv(scp);
- /* fill command */
- if (cmndinfo->OpCode != -1) {
- cmdp->OpCode = cmndinfo->OpCode; /* special raw cmd. */
- cmdp->BoardNode = LOCALBOARD;
- if (mode64) {
- cmdp->u.raw64.direction = (cmndinfo->phase >> 8);
- TRACE2(("special raw cmd 0x%x param 0x%x\n",
- cmdp->OpCode, cmdp->u.raw64.direction));
- /* evaluate command size */
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw64.sg_lst);
- } else {
- cmdp->u.raw.direction = (cmndinfo->phase >> 8);
- TRACE2(("special raw cmd 0x%x param 0x%x\n",
- cmdp->OpCode, cmdp->u.raw.direction));
- /* evaluate command size */
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst);
- }
-
- } else {
- page = virt_to_page(scp->sense_buffer);
- offset = (unsigned long)scp->sense_buffer & ~PAGE_MASK;
- sense_paddr = pci_map_page(ha->pdev,page,offset,
- 16,PCI_DMA_FROMDEVICE);
-
- cmndinfo->sense_paddr = sense_paddr;
- cmdp->OpCode = GDT_WRITE; /* always */
- cmdp->BoardNode = LOCALBOARD;
- if (mode64) {
- cmdp->u.raw64.reserved = 0;
- cmdp->u.raw64.mdisc_time = 0;
- cmdp->u.raw64.mcon_time = 0;
- cmdp->u.raw64.clen = scp->cmd_len;
- cmdp->u.raw64.target = t;
- cmdp->u.raw64.lun = l;
- cmdp->u.raw64.bus = b;
- cmdp->u.raw64.priority = 0;
- cmdp->u.raw64.sdlen = scsi_bufflen(scp);
- cmdp->u.raw64.sense_len = 16;
- cmdp->u.raw64.sense_data = sense_paddr;
- cmdp->u.raw64.direction =
- gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN;
- memcpy(cmdp->u.raw64.cmd,scp->cmnd,16);
- cmdp->u.raw64.sg_ranz = 0;
- } else {
- cmdp->u.raw.reserved = 0;
- cmdp->u.raw.mdisc_time = 0;
- cmdp->u.raw.mcon_time = 0;
- cmdp->u.raw.clen = scp->cmd_len;
- cmdp->u.raw.target = t;
- cmdp->u.raw.lun = l;
- cmdp->u.raw.bus = b;
- cmdp->u.raw.priority = 0;
- cmdp->u.raw.link_p = 0;
- cmdp->u.raw.sdlen = scsi_bufflen(scp);
- cmdp->u.raw.sense_len = 16;
- cmdp->u.raw.sense_data = sense_paddr;
- cmdp->u.raw.direction =
- gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN;
- memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
- cmdp->u.raw.sg_ranz = 0;
- }
-
- if (scsi_bufflen(scp)) {
- cmndinfo->dma_dir = PCI_DMA_BIDIRECTIONAL;
- sgcnt = pci_map_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp),
- cmndinfo->dma_dir);
- if (mode64) {
- struct scatterlist *sl;
-
- cmdp->u.raw64.sdata = (u64)-1;
- cmdp->u.raw64.sg_ranz = sgcnt;
- scsi_for_each_sg(scp, sl, sgcnt, i) {
- cmdp->u.raw64.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- if (cmdp->u.raw64.sg_lst[i].sg_ptr > (u64)0xffffffff)
- ha->dma64_cnt++;
- else
- ha->dma32_cnt++;
-#endif
- cmdp->u.raw64.sg_lst[i].sg_len = sg_dma_len(sl);
- }
- } else {
- struct scatterlist *sl;
-
- cmdp->u.raw.sdata = 0xffffffff;
- cmdp->u.raw.sg_ranz = sgcnt;
- scsi_for_each_sg(scp, sl, sgcnt, i) {
- cmdp->u.raw.sg_lst[i].sg_ptr = sg_dma_address(sl);
-#ifdef GDTH_DMA_STATISTICS
- ha->dma32_cnt++;
-#endif
- cmdp->u.raw.sg_lst[i].sg_len = sg_dma_len(sl);
- }
- }
-
-#ifdef GDTH_STATISTICS
- if (max_sg < sgcnt) {
- max_sg = sgcnt;
- TRACE3(("GDT: max_sg = %d\n",sgcnt));
- }
-#endif
-
- }
- if (mode64) {
- TRACE(("raw cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
- cmdp->u.raw64.sdata,cmdp->u.raw64.sg_ranz,
- cmdp->u.raw64.sg_lst[0].sg_ptr,
- cmdp->u.raw64.sg_lst[0].sg_len));
- /* evaluate command size */
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw64.sg_lst) +
- (u16)cmdp->u.raw64.sg_ranz * sizeof(gdth_sg64_str);
- } else {
- TRACE(("raw cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
- cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz,
- cmdp->u.raw.sg_lst[0].sg_ptr,
- cmdp->u.raw.sg_lst[0].sg_len));
- /* evaluate command size */
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
- (u16)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
- }
- }
- /* check space */
- if (ha->cmd_len & 3)
- ha->cmd_len += (4 - (ha->cmd_len & 3));
-
- if (ha->cmd_cnt > 0) {
- if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
- ha->ic_all_size) {
- TRACE2(("gdth_fill_raw() DPMEM overflow\n"));
- ha->cmd_tab[cmd_index-2].cmnd = UNUSED_CMND;
- return 0;
- }
- }
-
- /* copy command */
- gdth_copy_command(ha);
- return cmd_index;
-}
-
-static int gdth_special_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp)
-{
- register gdth_cmd_str *cmdp;
- struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
- int cmd_index;
-
- cmdp= ha->pccb;
- TRACE2(("gdth_special_cmd(): "));
-
- if (ha->type==GDT_EISA && ha->cmd_cnt>0)
- return 0;
-
- *cmdp = *cmndinfo->internal_cmd_str;
- cmdp->RequestBuffer = scp;
-
- /* search free command index */
- if (!(cmd_index=gdth_get_cmd_index(ha))) {
- TRACE(("GDT: No free command index found\n"));
- return 0;
- }
-
- /* if it's the first command, set command semaphore */
- if (ha->cmd_cnt == 0)
- gdth_set_sema0(ha);
-
- /* evaluate command size, check space */
- if (cmdp->OpCode == GDT_IOCTL) {
- TRACE2(("IOCTL\n"));
- ha->cmd_len =
- GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(u64);
- } else if (cmdp->Service == CACHESERVICE) {
- TRACE2(("cache command %d\n",cmdp->OpCode));
- if (ha->cache_feat & GDT_64BIT)
- ha->cmd_len =
- GDTOFFSOF(gdth_cmd_str,u.cache64.sg_lst) + sizeof(gdth_sg64_str);
- else
- ha->cmd_len =
- GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str);
- } else if (cmdp->Service == SCSIRAWSERVICE) {
- TRACE2(("raw command %d\n",cmdp->OpCode));
- if (ha->raw_feat & GDT_64BIT)
- ha->cmd_len =
- GDTOFFSOF(gdth_cmd_str,u.raw64.sg_lst) + sizeof(gdth_sg64_str);
- else
- ha->cmd_len =
- GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str);
- }
-
- if (ha->cmd_len & 3)
- ha->cmd_len += (4 - (ha->cmd_len & 3));
-
- if (ha->cmd_cnt > 0) {
- if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
- ha->ic_all_size) {
- TRACE2(("gdth_special_cmd() DPMEM overflow\n"));
- ha->cmd_tab[cmd_index-2].cmnd = UNUSED_CMND;
- return 0;
- }
- }
-
- /* copy command */
- gdth_copy_command(ha);
- return cmd_index;
-}
-
-
-/* Controller event handling functions */
-static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, u16 source,
- u16 idx, gdth_evt_data *evt)
-{
- gdth_evt_str *e;
- struct timeval tv;
-
- /* no GDTH_LOCK_HA() ! */
- TRACE2(("gdth_store_event() source %d idx %d\n", source, idx));
- if (source == 0) /* no source -> no event */
- return NULL;
-
- if (ebuffer[elastidx].event_source == source &&
- ebuffer[elastidx].event_idx == idx &&
- ((evt->size != 0 && ebuffer[elastidx].event_data.size != 0 &&
- !memcmp((char *)&ebuffer[elastidx].event_data.eu,
- (char *)&evt->eu, evt->size)) ||
- (evt->size == 0 && ebuffer[elastidx].event_data.size == 0 &&
- !strcmp((char *)&ebuffer[elastidx].event_data.event_string,
- (char *)&evt->event_string)))) {
- e = &ebuffer[elastidx];
- do_gettimeofday(&tv);
- e->last_stamp = tv.tv_sec;
- ++e->same_count;
- } else {
- if (ebuffer[elastidx].event_source != 0) { /* entry not free ? */
- ++elastidx;
- if (elastidx == MAX_EVENTS)
- elastidx = 0;
- if (elastidx == eoldidx) { /* reached mark ? */
- ++eoldidx;
- if (eoldidx == MAX_EVENTS)
- eoldidx = 0;
- }
- }
- e = &ebuffer[elastidx];
- e->event_source = source;
- e->event_idx = idx;
- do_gettimeofday(&tv);
- e->first_stamp = e->last_stamp = tv.tv_sec;
- e->same_count = 1;
- e->event_data = *evt;
- e->application = 0;
- }
- return e;
-}
-
-static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr)
-{
- gdth_evt_str *e;
- int eindex;
- unsigned long flags;
-
- TRACE2(("gdth_read_event() handle %d\n", handle));
- spin_lock_irqsave(&ha->smp_lock, flags);
- if (handle == -1)
- eindex = eoldidx;
- else
- eindex = handle;
- estr->event_source = 0;
-
- if (eindex < 0 || eindex >= MAX_EVENTS) {
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return eindex;
- }
- e = &ebuffer[eindex];
- if (e->event_source != 0) {
- if (eindex != elastidx) {
- if (++eindex == MAX_EVENTS)
- eindex = 0;
- } else {
- eindex = -1;
- }
- memcpy(estr, e, sizeof(gdth_evt_str));
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return eindex;
-}
-
-static void gdth_readapp_event(gdth_ha_str *ha,
- u8 application, gdth_evt_str *estr)
-{
- gdth_evt_str *e;
- int eindex;
- unsigned long flags;
- u8 found = FALSE;
-
- TRACE2(("gdth_readapp_event() app. %d\n", application));
- spin_lock_irqsave(&ha->smp_lock, flags);
- eindex = eoldidx;
- for (;;) {
- e = &ebuffer[eindex];
- if (e->event_source == 0)
- break;
- if ((e->application & application) == 0) {
- e->application |= application;
- found = TRUE;
- break;
- }
- if (eindex == elastidx)
- break;
- if (++eindex == MAX_EVENTS)
- eindex = 0;
- }
- if (found)
- memcpy(estr, e, sizeof(gdth_evt_str));
- else
- estr->event_source = 0;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-}
-
-static void gdth_clear_events(void)
-{
- TRACE(("gdth_clear_events()"));
-
- eoldidx = elastidx = 0;
- ebuffer[0].event_source = 0;
-}
-
-
-/* SCSI interface functions */
-
-static irqreturn_t __gdth_interrupt(gdth_ha_str *ha,
- int gdth_from_wait, int* pIndex)
-{
- gdt6m_dpram_str __iomem *dp6m_ptr = NULL;
- gdt6_dpram_str __iomem *dp6_ptr;
- gdt2_dpram_str __iomem *dp2_ptr;
- Scsi_Cmnd *scp;
- int rval, i;
- u8 IStatus;
- u16 Service;
- unsigned long flags = 0;
-#ifdef INT_COAL
- int coalesced = FALSE;
- int next = FALSE;
- gdth_coal_status *pcs = NULL;
- int act_int_coal = 0;
-#endif
-
- TRACE(("gdth_interrupt() IRQ %d\n", ha->irq));
-
- /* if polling and not from gdth_wait() -> return */
- if (gdth_polling) {
- if (!gdth_from_wait) {
- return IRQ_HANDLED;
- }
- }
-
- if (!gdth_polling)
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- /* search controller */
- IStatus = gdth_get_status(ha);
- if (IStatus == 0) {
- /* spurious interrupt */
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return IRQ_HANDLED;
- }
-
-#ifdef GDTH_STATISTICS
- ++act_ints;
-#endif
-
-#ifdef INT_COAL
- /* See if the fw is returning coalesced status */
- if (IStatus == COALINDEX) {
- /* Coalesced status. Setup the initial status
- buffer pointer and flags */
- pcs = ha->coal_stat;
- coalesced = TRUE;
- next = TRUE;
- }
-
- do {
- if (coalesced) {
- /* For coalesced requests all status
- information is found in the status buffer */
- IStatus = (u8)(pcs->status & 0xff);
- }
-#endif
-
- if (ha->type == GDT_EISA) {
- if (IStatus & 0x80) { /* error flag */
- IStatus &= ~0x80;
- ha->status = inw(ha->bmic + MAILBOXREG+8);
- TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
- } else /* no error */
- ha->status = S_OK;
- ha->info = inl(ha->bmic + MAILBOXREG+12);
- ha->service = inw(ha->bmic + MAILBOXREG+10);
- ha->info2 = inl(ha->bmic + MAILBOXREG+4);
-
- outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */
- outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */
- } else if (ha->type == GDT_ISA) {
- dp2_ptr = ha->brd;
- if (IStatus & 0x80) { /* error flag */
- IStatus &= ~0x80;
- ha->status = readw(&dp2_ptr->u.ic.Status);
- TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
- } else /* no error */
- ha->status = S_OK;
- ha->info = readl(&dp2_ptr->u.ic.Info[0]);
- ha->service = readw(&dp2_ptr->u.ic.Service);
- ha->info2 = readl(&dp2_ptr->u.ic.Info[1]);
-
- writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */
- writeb(0, &dp2_ptr->u.ic.Cmd_Index);/* reset command index */
- writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */
- } else if (ha->type == GDT_PCI) {
- dp6_ptr = ha->brd;
- if (IStatus & 0x80) { /* error flag */
- IStatus &= ~0x80;
- ha->status = readw(&dp6_ptr->u.ic.Status);
- TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
- } else /* no error */
- ha->status = S_OK;
- ha->info = readl(&dp6_ptr->u.ic.Info[0]);
- ha->service = readw(&dp6_ptr->u.ic.Service);
- ha->info2 = readl(&dp6_ptr->u.ic.Info[1]);
-
- writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */
- writeb(0, &dp6_ptr->u.ic.Cmd_Index);/* reset command index */
- writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */
- } else if (ha->type == GDT_PCINEW) {
- if (IStatus & 0x80) { /* error flag */
- IStatus &= ~0x80;
- ha->status = inw(PTR2USHORT(&ha->plx->status));
- TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
- } else
- ha->status = S_OK;
- ha->info = inl(PTR2USHORT(&ha->plx->info[0]));
- ha->service = inw(PTR2USHORT(&ha->plx->service));
- ha->info2 = inl(PTR2USHORT(&ha->plx->info[1]));
-
- outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
- outb(0x00, PTR2USHORT(&ha->plx->sema1_reg));
- } else if (ha->type == GDT_PCIMPR) {
- dp6m_ptr = ha->brd;
- if (IStatus & 0x80) { /* error flag */
- IStatus &= ~0x80;
-#ifdef INT_COAL
- if (coalesced)
- ha->status = pcs->ext_status & 0xffff;
- else
-#endif
- ha->status = readw(&dp6m_ptr->i960r.status);
- TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
- } else /* no error */
- ha->status = S_OK;
-#ifdef INT_COAL
- /* get information */
- if (coalesced) {
- ha->info = pcs->info0;
- ha->info2 = pcs->info1;
- ha->service = (pcs->ext_status >> 16) & 0xffff;
- } else
-#endif
- {
- ha->info = readl(&dp6m_ptr->i960r.info[0]);
- ha->service = readw(&dp6m_ptr->i960r.service);
- ha->info2 = readl(&dp6m_ptr->i960r.info[1]);
- }
- /* event string */
- if (IStatus == ASYNCINDEX) {
- if (ha->service != SCREENSERVICE &&
- (ha->fw_vers & 0xff) >= 0x1a) {
- ha->dvr.severity = readb
- (&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.severity);
- for (i = 0; i < 256; ++i) {
- ha->dvr.event_string[i] = readb
- (&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.evt_str[i]);
- if (ha->dvr.event_string[i] == 0)
- break;
- }
- }
- }
-#ifdef INT_COAL
- /* Make sure that non coalesced interrupts get cleared
- before being handled by gdth_async_event/gdth_sync_event */
- if (!coalesced)
-#endif
- {
- writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
- writeb(0, &dp6m_ptr->i960r.sema1_reg);
- }
- } else {
- TRACE2(("gdth_interrupt() unknown controller type\n"));
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return IRQ_HANDLED;
- }
-
- TRACE(("gdth_interrupt() index %d stat %d info %d\n",
- IStatus,ha->status,ha->info));
-
- if (gdth_from_wait) {
- *pIndex = (int)IStatus;
- }
-
- if (IStatus == ASYNCINDEX) {
- TRACE2(("gdth_interrupt() async. event\n"));
- gdth_async_event(ha);
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- gdth_next(ha);
- return IRQ_HANDLED;
- }
-
- if (IStatus == SPEZINDEX) {
- TRACE2(("Service unknown or not initialized !\n"));
- ha->dvr.size = sizeof(ha->dvr.eu.driver);
- ha->dvr.eu.driver.ionode = ha->hanum;
- gdth_store_event(ha, ES_DRIVER, 4, &ha->dvr);
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return IRQ_HANDLED;
- }
- scp = ha->cmd_tab[IStatus-2].cmnd;
- Service = ha->cmd_tab[IStatus-2].service;
- ha->cmd_tab[IStatus-2].cmnd = UNUSED_CMND;
- if (scp == UNUSED_CMND) {
- TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
- ha->dvr.size = sizeof(ha->dvr.eu.driver);
- ha->dvr.eu.driver.ionode = ha->hanum;
- ha->dvr.eu.driver.index = IStatus;
- gdth_store_event(ha, ES_DRIVER, 1, &ha->dvr);
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return IRQ_HANDLED;
- }
- if (scp == INTERNAL_CMND) {
- TRACE(("gdth_interrupt() answer to internal command\n"));
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return IRQ_HANDLED;
- }
-
- TRACE(("gdth_interrupt() sync. status\n"));
- rval = gdth_sync_event(ha,Service,IStatus,scp);
- if (!gdth_polling)
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- if (rval == 2) {
- gdth_putq(ha, scp, gdth_cmnd_priv(scp)->priority);
- } else if (rval == 1) {
- gdth_scsi_done(scp);
- }
-
-#ifdef INT_COAL
- if (coalesced) {
- /* go to the next status in the status buffer */
- ++pcs;
-#ifdef GDTH_STATISTICS
- ++act_int_coal;
- if (act_int_coal > max_int_coal) {
- max_int_coal = act_int_coal;
- printk("GDT: max_int_coal = %d\n",(u16)max_int_coal);
- }
-#endif
- /* see if there is another status */
- if (pcs->status == 0)
- /* Stop the coalesce loop */
- next = FALSE;
- }
- } while (next);
-
- /* coalescing only for new GDT_PCIMPR controllers available */
- if (ha->type == GDT_PCIMPR && coalesced) {
- writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
- writeb(0, &dp6m_ptr->i960r.sema1_reg);
- }
-#endif
-
- gdth_next(ha);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t gdth_interrupt(int irq, void *dev_id)
-{
- gdth_ha_str *ha = dev_id;
-
- return __gdth_interrupt(ha, false, NULL);
-}
-
-static int gdth_sync_event(gdth_ha_str *ha, int service, u8 index,
- Scsi_Cmnd *scp)
-{
- gdth_msg_str *msg;
- gdth_cmd_str *cmdp;
- u8 b, t;
- struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
-
- cmdp = ha->pccb;
- TRACE(("gdth_sync_event() serv %d status %d\n",
- service,ha->status));
-
- if (service == SCREENSERVICE) {
- msg = ha->pmsg;
- TRACE(("len: %d, answer: %d, ext: %d, alen: %d\n",
- msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen));
- if (msg->msg_len > MSGLEN+1)
- msg->msg_len = MSGLEN+1;
- if (msg->msg_len)
- if (!(msg->msg_answer && msg->msg_ext)) {
- msg->msg_text[msg->msg_len] = '\0';
- printk("%s",msg->msg_text);
- }
-
- if (msg->msg_ext && !msg->msg_answer) {
- while (gdth_test_busy(ha))
- gdth_delay(0);
- cmdp->Service = SCREENSERVICE;
- cmdp->RequestBuffer = SCREEN_CMND;
- gdth_get_cmd_index(ha);
- gdth_set_sema0(ha);
- cmdp->OpCode = GDT_READ;
- cmdp->BoardNode = LOCALBOARD;
- cmdp->u.screen.reserved = 0;
- cmdp->u.screen.su.msg.msg_handle= msg->msg_handle;
- cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
- ha->cmd_offs_dpmem = 0;
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
- + sizeof(u64);
- ha->cmd_cnt = 0;
- gdth_copy_command(ha);
- gdth_release_event(ha);
- return 0;
- }
-
- if (msg->msg_answer && msg->msg_alen) {
- /* default answers (getchar() not possible) */
- if (msg->msg_alen == 1) {
- msg->msg_alen = 0;
- msg->msg_len = 1;
- msg->msg_text[0] = 0;
- } else {
- msg->msg_alen -= 2;
- msg->msg_len = 2;
- msg->msg_text[0] = 1;
- msg->msg_text[1] = 0;
- }
- msg->msg_ext = 0;
- msg->msg_answer = 0;
- while (gdth_test_busy(ha))
- gdth_delay(0);
- cmdp->Service = SCREENSERVICE;
- cmdp->RequestBuffer = SCREEN_CMND;
- gdth_get_cmd_index(ha);
- gdth_set_sema0(ha);
- cmdp->OpCode = GDT_WRITE;
- cmdp->BoardNode = LOCALBOARD;
- cmdp->u.screen.reserved = 0;
- cmdp->u.screen.su.msg.msg_handle= msg->msg_handle;
- cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
- ha->cmd_offs_dpmem = 0;
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
- + sizeof(u64);
- ha->cmd_cnt = 0;
- gdth_copy_command(ha);
- gdth_release_event(ha);
- return 0;
- }
- printk("\n");
-
- } else {
- b = scp->device->channel;
- t = scp->device->id;
- if (cmndinfo->OpCode == -1 && b != ha->virt_bus) {
- ha->raw[BUS_L2P(ha,b)].io_cnt[t]--;
- }
- /* cache or raw service */
- if (ha->status == S_BSY) {
- TRACE2(("Controller busy -> retry !\n"));
- if (cmndinfo->OpCode == GDT_MOUNT)
- cmndinfo->OpCode = GDT_CLUST_INFO;
- /* retry */
- return 2;
- }
- if (scsi_bufflen(scp))
- pci_unmap_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp),
- cmndinfo->dma_dir);
-
- if (cmndinfo->sense_paddr)
- pci_unmap_page(ha->pdev, cmndinfo->sense_paddr, 16,
- PCI_DMA_FROMDEVICE);
-
- if (ha->status == S_OK) {
- cmndinfo->status = S_OK;
- cmndinfo->info = ha->info;
- if (cmndinfo->OpCode != -1) {
- TRACE2(("gdth_sync_event(): special cmd 0x%x OK\n",
- cmndinfo->OpCode));
- /* special commands GDT_CLUST_INFO/GDT_MOUNT ? */
- if (cmndinfo->OpCode == GDT_CLUST_INFO) {
- ha->hdr[t].cluster_type = (u8)ha->info;
- if (!(ha->hdr[t].cluster_type &
- CLUSTER_MOUNTED)) {
- /* NOT MOUNTED -> MOUNT */
- cmndinfo->OpCode = GDT_MOUNT;
- if (ha->hdr[t].cluster_type &
- CLUSTER_RESERVED) {
- /* cluster drive RESERVED (on the other node) */
- cmndinfo->phase = -2; /* reservation conflict */
- }
- } else {
- cmndinfo->OpCode = -1;
- }
- } else {
- if (cmndinfo->OpCode == GDT_MOUNT) {
- ha->hdr[t].cluster_type |= CLUSTER_MOUNTED;
- ha->hdr[t].media_changed = TRUE;
- } else if (cmndinfo->OpCode == GDT_UNMOUNT) {
- ha->hdr[t].cluster_type &= ~CLUSTER_MOUNTED;
- ha->hdr[t].media_changed = TRUE;
- }
- cmndinfo->OpCode = -1;
- }
- /* retry */
- cmndinfo->priority = HIGH_PRI;
- return 2;
- } else {
- /* RESERVE/RELEASE ? */
- if (scp->cmnd[0] == RESERVE) {
- ha->hdr[t].cluster_type |= CLUSTER_RESERVED;
- } else if (scp->cmnd[0] == RELEASE) {
- ha->hdr[t].cluster_type &= ~CLUSTER_RESERVED;
- }
- scp->result = DID_OK << 16;
- scp->sense_buffer[0] = 0;
- }
- } else {
- cmndinfo->status = ha->status;
- cmndinfo->info = ha->info;
-
- if (cmndinfo->OpCode != -1) {
- TRACE2(("gdth_sync_event(): special cmd 0x%x error 0x%x\n",
- cmndinfo->OpCode, ha->status));
- if (cmndinfo->OpCode == GDT_SCAN_START ||
- cmndinfo->OpCode == GDT_SCAN_END) {
- cmndinfo->OpCode = -1;
- /* retry */
- cmndinfo->priority = HIGH_PRI;
- return 2;
- }
- memset((char*)scp->sense_buffer,0,16);
- scp->sense_buffer[0] = 0x70;
- scp->sense_buffer[2] = NOT_READY;
- scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
- } else if (service == CACHESERVICE) {
- if (ha->status == S_CACHE_UNKNOWN &&
- (ha->hdr[t].cluster_type &
- CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) {
- /* bus reset -> force GDT_CLUST_INFO */
- ha->hdr[t].cluster_type &= ~CLUSTER_RESERVED;
- }
- memset((char*)scp->sense_buffer,0,16);
- if (ha->status == (u16)S_CACHE_RESERV) {
- scp->result = (DID_OK << 16) | (RESERVATION_CONFLICT << 1);
- } else {
- scp->sense_buffer[0] = 0x70;
- scp->sense_buffer[2] = NOT_READY;
- scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
- }
- if (!cmndinfo->internal_command) {
- ha->dvr.size = sizeof(ha->dvr.eu.sync);
- ha->dvr.eu.sync.ionode = ha->hanum;
- ha->dvr.eu.sync.service = service;
- ha->dvr.eu.sync.status = ha->status;
- ha->dvr.eu.sync.info = ha->info;
- ha->dvr.eu.sync.hostdrive = t;
- if (ha->status >= 0x8000)
- gdth_store_event(ha, ES_SYNC, 0, &ha->dvr);
- else
- gdth_store_event(ha, ES_SYNC, service, &ha->dvr);
- }
- } else {
- /* sense buffer filled from controller firmware (DMA) */
- if (ha->status != S_RAW_SCSI || ha->info >= 0x100) {
- scp->result = DID_BAD_TARGET << 16;
- } else {
- scp->result = (DID_OK << 16) | ha->info;
- }
- }
- }
- if (!cmndinfo->wait_for_completion)
- cmndinfo->wait_for_completion++;
- else
- return 1;
- }
-
- return 0;
-}
-
-static char *async_cache_tab[] = {
-/* 0*/ "\011\000\002\002\002\004\002\006\004"
- "GDT HA %u, service %u, async. status %u/%lu unknown",
-/* 1*/ "\011\000\002\002\002\004\002\006\004"
- "GDT HA %u, service %u, async. status %u/%lu unknown",
-/* 2*/ "\005\000\002\006\004"
- "GDT HA %u, Host Drive %lu not ready",
-/* 3*/ "\005\000\002\006\004"
- "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
-/* 4*/ "\005\000\002\006\004"
- "GDT HA %u, mirror update on Host Drive %lu failed",
-/* 5*/ "\005\000\002\006\004"
- "GDT HA %u, Mirror Drive %lu failed",
-/* 6*/ "\005\000\002\006\004"
- "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
-/* 7*/ "\005\000\002\006\004"
- "GDT HA %u, Host Drive %lu write protected",
-/* 8*/ "\005\000\002\006\004"
- "GDT HA %u, media changed in Host Drive %lu",
-/* 9*/ "\005\000\002\006\004"
- "GDT HA %u, Host Drive %lu is offline",
-/*10*/ "\005\000\002\006\004"
- "GDT HA %u, media change of Mirror Drive %lu",
-/*11*/ "\005\000\002\006\004"
- "GDT HA %u, Mirror Drive %lu is write protected",
-/*12*/ "\005\000\002\006\004"
- "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!",
-/*13*/ "\007\000\002\006\002\010\002"
- "GDT HA %u, Array Drive %u: Cache Drive %u failed",
-/*14*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: FAIL state entered",
-/*15*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: error",
-/*16*/ "\007\000\002\006\002\010\002"
- "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u",
-/*17*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: parity build failed",
-/*18*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: drive rebuild failed",
-/*19*/ "\005\000\002\010\002"
- "GDT HA %u, Test of Hot Fix %u failed",
-/*20*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: drive build finished successfully",
-/*21*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: drive rebuild finished successfully",
-/*22*/ "\007\000\002\006\002\010\002"
- "GDT HA %u, Array Drive %u: Hot Fix %u activated",
-/*23*/ "\005\000\002\006\002"
- "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error",
-/*24*/ "\005\000\002\010\002"
- "GDT HA %u, mirror update on Cache Drive %u completed",
-/*25*/ "\005\000\002\010\002"
- "GDT HA %u, mirror update on Cache Drive %lu failed",
-/*26*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: drive rebuild started",
-/*27*/ "\005\000\002\012\001"
- "GDT HA %u, Fault bus %u: SHELF OK detected",
-/*28*/ "\005\000\002\012\001"
- "GDT HA %u, Fault bus %u: SHELF not OK detected",
-/*29*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started",
-/*30*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: new disk detected",
-/*31*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: old disk detected",
-/*32*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is invalid",
-/*33*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: invalid device detected",
-/*34*/ "\011\000\002\012\001\013\001\006\004"
- "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)",
-/*35*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: disk write protected",
-/*36*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: disk not available",
-/*37*/ "\007\000\002\012\001\006\004"
- "GDT HA %u, Fault bus %u: swap detected (%lu)",
-/*38*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully",
-/*39*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug",
-/*40*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted",
-/*41*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started",
-/*42*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: drive build started",
-/*43*/ "\003\000\002"
- "GDT HA %u, DRAM parity error detected",
-/*44*/ "\005\000\002\006\002"
- "GDT HA %u, Mirror Drive %u: update started",
-/*45*/ "\007\000\002\006\002\010\002"
- "GDT HA %u, Mirror Drive %u: Hot Fix %u activated",
-/*46*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available",
-/*47*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available",
-/*48*/ "\005\000\002\006\002"
- "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available",
-/*49*/ "\005\000\002\006\002"
- "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available",
-/*50*/ "\007\000\002\012\001\013\001"
- "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received",
-/*51*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: expand started",
-/*52*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: expand finished successfully",
-/*53*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: expand failed",
-/*54*/ "\003\000\002"
- "GDT HA %u, CPU temperature critical",
-/*55*/ "\003\000\002"
- "GDT HA %u, CPU temperature OK",
-/*56*/ "\005\000\002\006\004"
- "GDT HA %u, Host drive %lu created",
-/*57*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: expand restarted",
-/*58*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: expand stopped",
-/*59*/ "\005\000\002\010\002"
- "GDT HA %u, Mirror Drive %u: drive build quited",
-/*60*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: parity build quited",
-/*61*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: drive rebuild quited",
-/*62*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: parity verify started",
-/*63*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: parity verify done",
-/*64*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: parity verify failed",
-/*65*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: parity error detected",
-/*66*/ "\005\000\002\006\002"
- "GDT HA %u, Array Drive %u: parity verify quited",
-/*67*/ "\005\000\002\006\002"
- "GDT HA %u, Host Drive %u reserved",
-/*68*/ "\005\000\002\006\002"
- "GDT HA %u, Host Drive %u mounted and released",
-/*69*/ "\005\000\002\006\002"
- "GDT HA %u, Host Drive %u released",
-/*70*/ "\003\000\002"
- "GDT HA %u, DRAM error detected and corrected with ECC",
-/*71*/ "\003\000\002"
- "GDT HA %u, Uncorrectable DRAM error detected with ECC",
-/*72*/ "\011\000\002\012\001\013\001\014\001"
- "GDT HA %u, SCSI bus %u, ID %u, LUN %u: reassigning block",
-/*73*/ "\005\000\002\006\002"
- "GDT HA %u, Host drive %u resetted locally",
-/*74*/ "\005\000\002\006\002"
- "GDT HA %u, Host drive %u resetted remotely",
-/*75*/ "\003\000\002"
- "GDT HA %u, async. status 75 unknown",
-};
-
-
-static int gdth_async_event(gdth_ha_str *ha)
-{
- gdth_cmd_str *cmdp;
- int cmd_index;
-
- cmdp= ha->pccb;
- TRACE2(("gdth_async_event() ha %d serv %d\n",
- ha->hanum, ha->service));
-
- if (ha->service == SCREENSERVICE) {
- if (ha->status == MSG_REQUEST) {
- while (gdth_test_busy(ha))
- gdth_delay(0);
- cmdp->Service = SCREENSERVICE;
- cmdp->RequestBuffer = SCREEN_CMND;
- cmd_index = gdth_get_cmd_index(ha);
- gdth_set_sema0(ha);
- cmdp->OpCode = GDT_READ;
- cmdp->BoardNode = LOCALBOARD;
- cmdp->u.screen.reserved = 0;
- cmdp->u.screen.su.msg.msg_handle= MSG_INV_HANDLE;
- cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
- ha->cmd_offs_dpmem = 0;
- ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
- + sizeof(u64);
- ha->cmd_cnt = 0;
- gdth_copy_command(ha);
- if (ha->type == GDT_EISA)
- printk("[EISA slot %d] ",(u16)ha->brd_phys);
- else if (ha->type == GDT_ISA)
- printk("[DPMEM 0x%4X] ",(u16)ha->brd_phys);
- else
- printk("[PCI %d/%d] ",(u16)(ha->brd_phys>>8),
- (u16)((ha->brd_phys>>3)&0x1f));
- gdth_release_event(ha);
- }
-
- } else {
- if (ha->type == GDT_PCIMPR &&
- (ha->fw_vers & 0xff) >= 0x1a) {
- ha->dvr.size = 0;
- ha->dvr.eu.async.ionode = ha->hanum;
- ha->dvr.eu.async.status = ha->status;
- /* severity and event_string already set! */
- } else {
- ha->dvr.size = sizeof(ha->dvr.eu.async);
- ha->dvr.eu.async.ionode = ha->hanum;
- ha->dvr.eu.async.service = ha->service;
- ha->dvr.eu.async.status = ha->status;
- ha->dvr.eu.async.info = ha->info;
- *(u32 *)ha->dvr.eu.async.scsi_coord = ha->info2;
- }
- gdth_store_event( ha, ES_ASYNC, ha->service, &ha->dvr );
- gdth_log_event( &ha->dvr, NULL );
-
- /* new host drive from expand? */
- if (ha->service == CACHESERVICE && ha->status == 56) {
- TRACE2(("gdth_async_event(): new host drive %d created\n",
- (u16)ha->info));
- /* gdth_analyse_hdrive(hanum, (u16)ha->info); */
- }
- }
- return 1;
-}
-
-static void gdth_log_event(gdth_evt_data *dvr, char *buffer)
-{
- gdth_stackframe stack;
- char *f = NULL;
- int i,j;
-
- TRACE2(("gdth_log_event()\n"));
- if (dvr->size == 0) {
- if (buffer == NULL) {
- printk("Adapter %d: %s\n",dvr->eu.async.ionode,dvr->event_string);
- } else {
- sprintf(buffer,"Adapter %d: %s\n",
- dvr->eu.async.ionode,dvr->event_string);
- }
- } else if (dvr->eu.async.service == CACHESERVICE &&
- INDEX_OK(dvr->eu.async.status, async_cache_tab)) {
- TRACE2(("GDT: Async. event cache service, event no.: %d\n",
- dvr->eu.async.status));
-
- f = async_cache_tab[dvr->eu.async.status];
-
- /* i: parameter to push, j: stack element to fill */
- for (j=0,i=1; i < f[0]; i+=2) {
- switch (f[i+1]) {
- case 4:
- stack.b[j++] = *(u32*)&dvr->eu.stream[(int)f[i]];
- break;
- case 2:
- stack.b[j++] = *(u16*)&dvr->eu.stream[(int)f[i]];
- break;
- case 1:
- stack.b[j++] = *(u8*)&dvr->eu.stream[(int)f[i]];
- break;
- default:
- break;
- }
- }
-
- if (buffer == NULL) {
- printk(&f[(int)f[0]],stack);
- printk("\n");
- } else {
- sprintf(buffer,&f[(int)f[0]],stack);
- }
-
- } else {
- if (buffer == NULL) {
- printk("GDT HA %u, Unknown async. event service %d event no. %d\n",
- dvr->eu.async.ionode,dvr->eu.async.service,dvr->eu.async.status);
- } else {
- sprintf(buffer,"GDT HA %u, Unknown async. event service %d event no. %d",
- dvr->eu.async.ionode,dvr->eu.async.service,dvr->eu.async.status);
- }
- }
-}
-
-#ifdef GDTH_STATISTICS
-static u8 gdth_timer_running;
-
-static void gdth_timeout(unsigned long data)
-{
- u32 i;
- Scsi_Cmnd *nscp;
- gdth_ha_str *ha;
- unsigned long flags;
-
- if(unlikely(list_empty(&gdth_instances))) {
- gdth_timer_running = 0;
- return;
- }
-
- ha = list_first_entry(&gdth_instances, gdth_ha_str, list);
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- for (act_stats=0,i=0; i<GDTH_MAXCMDS; ++i)
- if (ha->cmd_tab[i].cmnd != UNUSED_CMND)
- ++act_stats;
-
- for (act_rq=0,nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
- ++act_rq;
-
- TRACE2(("gdth_to(): ints %d, ios %d, act_stats %d, act_rq %d\n",
- act_ints, act_ios, act_stats, act_rq));
- act_ints = act_ios = 0;
-
- gdth_timer.expires = jiffies + 30 * HZ;
- add_timer(&gdth_timer);
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-}
-
-static void gdth_timer_init(void)
-{
- if (gdth_timer_running)
- return;
- gdth_timer_running = 1;
- TRACE2(("gdth_detect(): Initializing timer !\n"));
- gdth_timer.expires = jiffies + HZ;
- gdth_timer.data = 0L;
- gdth_timer.function = gdth_timeout;
- add_timer(&gdth_timer);
-}
-#else
-static inline void gdth_timer_init(void)
-{
-}
-#endif
-
-static void __init internal_setup(char *str,int *ints)
-{
- int i, argc;
- char *cur_str, *argv;
-
- TRACE2(("internal_setup() str %s ints[0] %d\n",
- str ? str:"NULL", ints ? ints[0]:0));
-
- /* read irq[] from ints[] */
- if (ints) {
- argc = ints[0];
- if (argc > 0) {
- if (argc > MAXHA)
- argc = MAXHA;
- for (i = 0; i < argc; ++i)
- irq[i] = ints[i+1];
- }
- }
-
- /* analyse string */
- argv = str;
- while (argv && (cur_str = strchr(argv, ':'))) {
- int val = 0, c = *++cur_str;
-
- if (c == 'n' || c == 'N')
- val = 0;
- else if (c == 'y' || c == 'Y')
- val = 1;
- else
- val = (int)simple_strtoul(cur_str, NULL, 0);
-
- if (!strncmp(argv, "disable:", 8))
- disable = val;
- else if (!strncmp(argv, "reserve_mode:", 13))
- reserve_mode = val;
- else if (!strncmp(argv, "reverse_scan:", 13))
- reverse_scan = val;
- else if (!strncmp(argv, "hdr_channel:", 12))
- hdr_channel = val;
- else if (!strncmp(argv, "max_ids:", 8))
- max_ids = val;
- else if (!strncmp(argv, "rescan:", 7))
- rescan = val;
- else if (!strncmp(argv, "shared_access:", 14))
- shared_access = val;
- else if (!strncmp(argv, "probe_eisa_isa:", 15))
- probe_eisa_isa = val;
- else if (!strncmp(argv, "reserve_list:", 13)) {
- reserve_list[0] = val;
- for (i = 1; i < MAX_RES_ARGS; i++) {
- cur_str = strchr(cur_str, ',');
- if (!cur_str)
- break;
- if (!isdigit((int)*++cur_str)) {
- --cur_str;
- break;
- }
- reserve_list[i] =
- (int)simple_strtoul(cur_str, NULL, 0);
- }
- if (!cur_str)
- break;
- argv = ++cur_str;
- continue;
- }
-
- if ((argv = strchr(argv, ',')))
- ++argv;
- }
-}
-
-int __init option_setup(char *str)
-{
- int ints[MAXHA];
- char *cur = str;
- int i = 1;
-
- TRACE2(("option_setup() str %s\n", str ? str:"NULL"));
-
- while (cur && isdigit(*cur) && i < MAXHA) {
- ints[i++] = simple_strtoul(cur, NULL, 0);
- if ((cur = strchr(cur, ',')) != NULL) cur++;
- }
-
- ints[0] = i - 1;
- internal_setup(cur, ints);
- return 1;
-}
-
-static const char *gdth_ctr_name(gdth_ha_str *ha)
-{
- TRACE2(("gdth_ctr_name()\n"));
-
- if (ha->type == GDT_EISA) {
- switch (ha->stype) {
- case GDT3_ID:
- return("GDT3000/3020");
- case GDT3A_ID:
- return("GDT3000A/3020A/3050A");
- case GDT3B_ID:
- return("GDT3000B/3010A");
- }
- } else if (ha->type == GDT_ISA) {
- return("GDT2000/2020");
- } else if (ha->type == GDT_PCI) {
- switch (ha->pdev->device) {
- case PCI_DEVICE_ID_VORTEX_GDT60x0:
- return("GDT6000/6020/6050");
- case PCI_DEVICE_ID_VORTEX_GDT6000B:
- return("GDT6000B/6010");
- }
- }
- /* new controllers (GDT_PCINEW, GDT_PCIMPR, ..) use board_info IOCTL! */
-
- return("");
-}
-
-static const char *gdth_info(struct Scsi_Host *shp)
-{
- gdth_ha_str *ha = shost_priv(shp);
-
- TRACE2(("gdth_info()\n"));
- return ((const char *)ha->binfo.type_string);
-}
-
-static enum blk_eh_timer_return gdth_timed_out(struct scsi_cmnd *scp)
-{
- gdth_ha_str *ha = shost_priv(scp->device->host);
- struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
- u8 b, t;
- unsigned long flags;
- enum blk_eh_timer_return retval = BLK_EH_NOT_HANDLED;
-
- TRACE(("%s() cmd 0x%x\n", scp->cmnd[0], __func__));
- b = scp->device->channel;
- t = scp->device->id;
-
- /*
- * We don't really honor the command timeout, but we try to
- * honor 6 times of the actual command timeout! So reset the
- * timer if this is less than 6th timeout on this command!
- */
- if (++cmndinfo->timeout_count < 6)
- retval = BLK_EH_RESET_TIMER;
-
- /* Reset the timeout if it is locked IO */
- spin_lock_irqsave(&ha->smp_lock, flags);
- if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha, b)].lock) ||
- (b == ha->virt_bus && t < MAX_HDRIVES && ha->hdr[t].lock)) {
- TRACE2(("%s(): locked IO, reset timeout\n", __func__));
- retval = BLK_EH_RESET_TIMER;
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-
- return retval;
-}
-
-
-static int gdth_eh_bus_reset(Scsi_Cmnd *scp)
-{
- gdth_ha_str *ha = shost_priv(scp->device->host);
- int i;
- unsigned long flags;
- Scsi_Cmnd *cmnd;
- u8 b;
-
- TRACE2(("gdth_eh_bus_reset()\n"));
-
- b = scp->device->channel;
-
- /* clear command tab */
- spin_lock_irqsave(&ha->smp_lock, flags);
- for (i = 0; i < GDTH_MAXCMDS; ++i) {
- cmnd = ha->cmd_tab[i].cmnd;
- if (!SPECIAL_SCP(cmnd) && cmnd->device->channel == b)
- ha->cmd_tab[i].cmnd = UNUSED_CMND;
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-
- if (b == ha->virt_bus) {
- /* host drives */
- for (i = 0; i < MAX_HDRIVES; ++i) {
- if (ha->hdr[i].present) {
- spin_lock_irqsave(&ha->smp_lock, flags);
- gdth_polling = TRUE;
- while (gdth_test_busy(ha))
- gdth_delay(0);
- if (gdth_internal_cmd(ha, CACHESERVICE,
- GDT_CLUST_RESET, i, 0, 0))
- ha->hdr[i].cluster_type &= ~CLUSTER_RESERVED;
- gdth_polling = FALSE;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- }
- }
- } else {
- /* raw devices */
- spin_lock_irqsave(&ha->smp_lock, flags);
- for (i = 0; i < MAXID; ++i)
- ha->raw[BUS_L2P(ha,b)].io_cnt[i] = 0;
- gdth_polling = TRUE;
- while (gdth_test_busy(ha))
- gdth_delay(0);
- gdth_internal_cmd(ha, SCSIRAWSERVICE, GDT_RESET_BUS,
- BUS_L2P(ha,b), 0, 0);
- gdth_polling = FALSE;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- }
- return SUCCESS;
-}
-
-static int gdth_bios_param(struct scsi_device *sdev,struct block_device *bdev,sector_t cap,int *ip)
-{
- u8 b, t;
- gdth_ha_str *ha = shost_priv(sdev->host);
- struct scsi_device *sd;
- unsigned capacity;
-
- sd = sdev;
- capacity = cap;
- b = sd->channel;
- t = sd->id;
- TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", ha->hanum, b, t));
-
- if (b != ha->virt_bus || ha->hdr[t].heads == 0) {
- /* raw device or host drive without mapping information */
- TRACE2(("Evaluate mapping\n"));
- gdth_eval_mapping(capacity,&ip[2],&ip[0],&ip[1]);
- } else {
- ip[0] = ha->hdr[t].heads;
- ip[1] = ha->hdr[t].secs;
- ip[2] = capacity / ip[0] / ip[1];
- }
-
- TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
- ip[0],ip[1],ip[2]));
- return 0;
-}
-
-
-static int gdth_queuecommand_lck(struct scsi_cmnd *scp,
- void (*done)(struct scsi_cmnd *))
-{
- gdth_ha_str *ha = shost_priv(scp->device->host);
- struct gdth_cmndinfo *cmndinfo;
-
- TRACE(("gdth_queuecommand() cmd 0x%x\n", scp->cmnd[0]));
-
- cmndinfo = gdth_get_cmndinfo(ha);
- BUG_ON(!cmndinfo);
-
- scp->scsi_done = done;
- cmndinfo->timeout_count = 0;
- cmndinfo->priority = DEFAULT_PRI;
-
- return __gdth_queuecommand(ha, scp, cmndinfo);
-}
-
-static DEF_SCSI_QCMD(gdth_queuecommand)
-
-static int __gdth_queuecommand(gdth_ha_str *ha, struct scsi_cmnd *scp,
- struct gdth_cmndinfo *cmndinfo)
-{
- scp->host_scribble = (unsigned char *)cmndinfo;
- cmndinfo->wait_for_completion = 1;
- cmndinfo->phase = -1;
- cmndinfo->OpCode = -1;
-
-#ifdef GDTH_STATISTICS
- ++act_ios;
-#endif
-
- gdth_putq(ha, scp, cmndinfo->priority);
- gdth_next(ha);
- return 0;
-}
-
-
-static int gdth_open(struct inode *inode, struct file *filep)
-{
- gdth_ha_str *ha;
-
- mutex_lock(&gdth_mutex);
- list_for_each_entry(ha, &gdth_instances, list) {
- if (!ha->sdev)
- ha->sdev = scsi_get_host_dev(ha->shost);
- }
- mutex_unlock(&gdth_mutex);
-
- TRACE(("gdth_open()\n"));
- return 0;
-}
-
-static int gdth_close(struct inode *inode, struct file *filep)
-{
- TRACE(("gdth_close()\n"));
- return 0;
-}
-
-static int ioc_event(void __user *arg)
-{
- gdth_ioctl_event evt;
- gdth_ha_str *ha;
- unsigned long flags;
-
- if (copy_from_user(&evt, arg, sizeof(gdth_ioctl_event)))
- return -EFAULT;
- ha = gdth_find_ha(evt.ionode);
- if (!ha)
- return -EFAULT;
-
- if (evt.erase == 0xff) {
- if (evt.event.event_source == ES_TEST)
- evt.event.event_data.size=sizeof(evt.event.event_data.eu.test);
- else if (evt.event.event_source == ES_DRIVER)
- evt.event.event_data.size=sizeof(evt.event.event_data.eu.driver);
- else if (evt.event.event_source == ES_SYNC)
- evt.event.event_data.size=sizeof(evt.event.event_data.eu.sync);
- else
- evt.event.event_data.size=sizeof(evt.event.event_data.eu.async);
- spin_lock_irqsave(&ha->smp_lock, flags);
- gdth_store_event(ha, evt.event.event_source, evt.event.event_idx,
- &evt.event.event_data);
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- } else if (evt.erase == 0xfe) {
- gdth_clear_events();
- } else if (evt.erase == 0) {
- evt.handle = gdth_read_event(ha, evt.handle, &evt.event);
- } else {
- gdth_readapp_event(ha, evt.erase, &evt.event);
- }
- if (copy_to_user(arg, &evt, sizeof(gdth_ioctl_event)))
- return -EFAULT;
- return 0;
-}
-
-static int ioc_lockdrv(void __user *arg)
-{
- gdth_ioctl_lockdrv ldrv;
- u8 i, j;
- unsigned long flags;
- gdth_ha_str *ha;
-
- if (copy_from_user(&ldrv, arg, sizeof(gdth_ioctl_lockdrv)))
- return -EFAULT;
- ha = gdth_find_ha(ldrv.ionode);
- if (!ha)
- return -EFAULT;
-
- for (i = 0; i < ldrv.drive_cnt && i < MAX_HDRIVES; ++i) {
- j = ldrv.drives[i];
- if (j >= MAX_HDRIVES || !ha->hdr[j].present)
- continue;
- if (ldrv.lock) {
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->hdr[j].lock = 1;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- gdth_wait_completion(ha, ha->bus_cnt, j);
- } else {
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->hdr[j].lock = 0;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- gdth_next(ha);
- }
- }
- return 0;
-}
-
-static int ioc_resetdrv(void __user *arg, char *cmnd)
-{
- gdth_ioctl_reset res;
- gdth_cmd_str cmd;
- gdth_ha_str *ha;
- int rval;
-
- if (copy_from_user(&res, arg, sizeof(gdth_ioctl_reset)) ||
- res.number >= MAX_HDRIVES)
- return -EFAULT;
- ha = gdth_find_ha(res.ionode);
- if (!ha)
- return -EFAULT;
-
- if (!ha->hdr[res.number].present)
- return 0;
- memset(&cmd, 0, sizeof(gdth_cmd_str));
- cmd.Service = CACHESERVICE;
- cmd.OpCode = GDT_CLUST_RESET;
- if (ha->cache_feat & GDT_64BIT)
- cmd.u.cache64.DeviceNo = res.number;
- else
- cmd.u.cache.DeviceNo = res.number;
-
- rval = __gdth_execute(ha->sdev, &cmd, cmnd, 30, NULL);
- if (rval < 0)
- return rval;
- res.status = rval;
-
- if (copy_to_user(arg, &res, sizeof(gdth_ioctl_reset)))
- return -EFAULT;
- return 0;
-}
-
-static int ioc_general(void __user *arg, char *cmnd)
-{
- gdth_ioctl_general gen;
- char *buf = NULL;
- u64 paddr;
- gdth_ha_str *ha;
- int rval;
-
- if (copy_from_user(&gen, arg, sizeof(gdth_ioctl_general)))
- return -EFAULT;
- ha = gdth_find_ha(gen.ionode);
- if (!ha)
- return -EFAULT;
-
- if (gen.data_len > INT_MAX)
- return -EINVAL;
- if (gen.sense_len > INT_MAX)
- return -EINVAL;
- if (gen.data_len + gen.sense_len > INT_MAX)
- return -EINVAL;
-
- if (gen.data_len + gen.sense_len != 0) {
- if (!(buf = gdth_ioctl_alloc(ha, gen.data_len + gen.sense_len,
- FALSE, &paddr)))
- return -EFAULT;
- if (copy_from_user(buf, arg + sizeof(gdth_ioctl_general),
- gen.data_len + gen.sense_len)) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
-
- if (gen.command.OpCode == GDT_IOCTL) {
- gen.command.u.ioctl.p_param = paddr;
- } else if (gen.command.Service == CACHESERVICE) {
- if (ha->cache_feat & GDT_64BIT) {
- /* copy elements from 32-bit IOCTL structure */
- gen.command.u.cache64.BlockCnt = gen.command.u.cache.BlockCnt;
- gen.command.u.cache64.BlockNo = gen.command.u.cache.BlockNo;
- gen.command.u.cache64.DeviceNo = gen.command.u.cache.DeviceNo;
- /* addresses */
- if (ha->cache_feat & SCATTER_GATHER) {
- gen.command.u.cache64.DestAddr = (u64)-1;
- gen.command.u.cache64.sg_canz = 1;
- gen.command.u.cache64.sg_lst[0].sg_ptr = paddr;
- gen.command.u.cache64.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.cache64.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.cache64.DestAddr = paddr;
- gen.command.u.cache64.sg_canz = 0;
- }
- } else {
- if (ha->cache_feat & SCATTER_GATHER) {
- gen.command.u.cache.DestAddr = 0xffffffff;
- gen.command.u.cache.sg_canz = 1;
- gen.command.u.cache.sg_lst[0].sg_ptr = (u32)paddr;
- gen.command.u.cache.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.cache.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.cache.DestAddr = paddr;
- gen.command.u.cache.sg_canz = 0;
- }
- }
- } else if (gen.command.Service == SCSIRAWSERVICE) {
- if (ha->raw_feat & GDT_64BIT) {
- /* copy elements from 32-bit IOCTL structure */
- char cmd[16];
- gen.command.u.raw64.sense_len = gen.command.u.raw.sense_len;
- gen.command.u.raw64.bus = gen.command.u.raw.bus;
- gen.command.u.raw64.lun = gen.command.u.raw.lun;
- gen.command.u.raw64.target = gen.command.u.raw.target;
- memcpy(cmd, gen.command.u.raw.cmd, 16);
- memcpy(gen.command.u.raw64.cmd, cmd, 16);
- gen.command.u.raw64.clen = gen.command.u.raw.clen;
- gen.command.u.raw64.sdlen = gen.command.u.raw.sdlen;
- gen.command.u.raw64.direction = gen.command.u.raw.direction;
- /* addresses */
- if (ha->raw_feat & SCATTER_GATHER) {
- gen.command.u.raw64.sdata = (u64)-1;
- gen.command.u.raw64.sg_ranz = 1;
- gen.command.u.raw64.sg_lst[0].sg_ptr = paddr;
- gen.command.u.raw64.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.raw64.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.raw64.sdata = paddr;
- gen.command.u.raw64.sg_ranz = 0;
- }
- gen.command.u.raw64.sense_data = paddr + gen.data_len;
- } else {
- if (ha->raw_feat & SCATTER_GATHER) {
- gen.command.u.raw.sdata = 0xffffffff;
- gen.command.u.raw.sg_ranz = 1;
- gen.command.u.raw.sg_lst[0].sg_ptr = (u32)paddr;
- gen.command.u.raw.sg_lst[0].sg_len = gen.data_len;
- gen.command.u.raw.sg_lst[1].sg_len = 0;
- } else {
- gen.command.u.raw.sdata = paddr;
- gen.command.u.raw.sg_ranz = 0;
- }
- gen.command.u.raw.sense_data = (u32)paddr + gen.data_len;
- }
- } else {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
- }
-
- rval = __gdth_execute(ha->sdev, &gen.command, cmnd, gen.timeout, &gen.info);
- if (rval < 0) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return rval;
- }
- gen.status = rval;
-
- if (copy_to_user(arg + sizeof(gdth_ioctl_general), buf,
- gen.data_len + gen.sense_len)) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
- if (copy_to_user(arg, &gen,
- sizeof(gdth_ioctl_general) - sizeof(gdth_cmd_str))) {
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return -EFAULT;
- }
- gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr);
- return 0;
-}
-
-static int ioc_hdrlist(void __user *arg, char *cmnd)
-{
- gdth_ioctl_rescan *rsc;
- gdth_cmd_str *cmd;
- gdth_ha_str *ha;
- u8 i;
- int rc = -ENOMEM;
- u32 cluster_type = 0;
-
- rsc = kmalloc(sizeof(*rsc), GFP_KERNEL);
- cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
- if (!rsc || !cmd)
- goto free_fail;
-
- if (copy_from_user(rsc, arg, sizeof(gdth_ioctl_rescan)) ||
- (NULL == (ha = gdth_find_ha(rsc->ionode)))) {
- rc = -EFAULT;
- goto free_fail;
- }
- memset(cmd, 0, sizeof(gdth_cmd_str));
-
- for (i = 0; i < MAX_HDRIVES; ++i) {
- if (!ha->hdr[i].present) {
- rsc->hdr_list[i].bus = 0xff;
- continue;
- }
- rsc->hdr_list[i].bus = ha->virt_bus;
- rsc->hdr_list[i].target = i;
- rsc->hdr_list[i].lun = 0;
- rsc->hdr_list[i].cluster_type = ha->hdr[i].cluster_type;
- if (ha->hdr[i].cluster_type & CLUSTER_DRIVE) {
- cmd->Service = CACHESERVICE;
- cmd->OpCode = GDT_CLUST_INFO;
- if (ha->cache_feat & GDT_64BIT)
- cmd->u.cache64.DeviceNo = i;
- else
- cmd->u.cache.DeviceNo = i;
- if (__gdth_execute(ha->sdev, cmd, cmnd, 30, &cluster_type) == S_OK)
- rsc->hdr_list[i].cluster_type = cluster_type;
- }
- }
-
- if (copy_to_user(arg, rsc, sizeof(gdth_ioctl_rescan)))
- rc = -EFAULT;
- else
- rc = 0;
-
-free_fail:
- kfree(rsc);
- kfree(cmd);
- return rc;
-}
-
-static int ioc_rescan(void __user *arg, char *cmnd)
-{
- gdth_ioctl_rescan *rsc;
- gdth_cmd_str *cmd;
- u16 i, status, hdr_cnt;
- u32 info;
- int cyls, hds, secs;
- int rc = -ENOMEM;
- unsigned long flags;
- gdth_ha_str *ha;
-
- rsc = kmalloc(sizeof(*rsc), GFP_KERNEL);
- cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
- if (!cmd || !rsc)
- goto free_fail;
-
- if (copy_from_user(rsc, arg, sizeof(gdth_ioctl_rescan)) ||
- (NULL == (ha = gdth_find_ha(rsc->ionode)))) {
- rc = -EFAULT;
- goto free_fail;
- }
- memset(cmd, 0, sizeof(gdth_cmd_str));
-
- if (rsc->flag == 0) {
- /* old method: re-init. cache service */
- cmd->Service = CACHESERVICE;
- if (ha->cache_feat & GDT_64BIT) {
- cmd->OpCode = GDT_X_INIT_HOST;
- cmd->u.cache64.DeviceNo = LINUX_OS;
- } else {
- cmd->OpCode = GDT_INIT;
- cmd->u.cache.DeviceNo = LINUX_OS;
- }
-
- status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
- i = 0;
- hdr_cnt = (status == S_OK ? (u16)info : 0);
- } else {
- i = rsc->hdr_no;
- hdr_cnt = i + 1;
- }
-
- for (; i < hdr_cnt && i < MAX_HDRIVES; ++i) {
- cmd->Service = CACHESERVICE;
- cmd->OpCode = GDT_INFO;
- if (ha->cache_feat & GDT_64BIT)
- cmd->u.cache64.DeviceNo = i;
- else
- cmd->u.cache.DeviceNo = i;
-
- status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
-
- spin_lock_irqsave(&ha->smp_lock, flags);
- rsc->hdr_list[i].bus = ha->virt_bus;
- rsc->hdr_list[i].target = i;
- rsc->hdr_list[i].lun = 0;
- if (status != S_OK) {
- ha->hdr[i].present = FALSE;
- } else {
- ha->hdr[i].present = TRUE;
- ha->hdr[i].size = info;
- /* evaluate mapping */
- ha->hdr[i].size &= ~SECS32;
- gdth_eval_mapping(ha->hdr[i].size,&cyls,&hds,&secs);
- ha->hdr[i].heads = hds;
- ha->hdr[i].secs = secs;
- /* round size */
- ha->hdr[i].size = cyls * hds * secs;
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- if (status != S_OK)
- continue;
-
- /* extended info, if GDT_64BIT, for drives > 2 TB */
- /* but we need ha->info2, not yet stored in scp->SCp */
-
- /* devtype, cluster info, R/W attribs */
- cmd->Service = CACHESERVICE;
- cmd->OpCode = GDT_DEVTYPE;
- if (ha->cache_feat & GDT_64BIT)
- cmd->u.cache64.DeviceNo = i;
- else
- cmd->u.cache.DeviceNo = i;
-
- status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
-
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->hdr[i].devtype = (status == S_OK ? (u16)info : 0);
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-
- cmd->Service = CACHESERVICE;
- cmd->OpCode = GDT_CLUST_INFO;
- if (ha->cache_feat & GDT_64BIT)
- cmd->u.cache64.DeviceNo = i;
- else
- cmd->u.cache.DeviceNo = i;
-
- status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
-
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->hdr[i].cluster_type =
- ((status == S_OK && !shared_access) ? (u16)info : 0);
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- rsc->hdr_list[i].cluster_type = ha->hdr[i].cluster_type;
-
- cmd->Service = CACHESERVICE;
- cmd->OpCode = GDT_RW_ATTRIBS;
- if (ha->cache_feat & GDT_64BIT)
- cmd->u.cache64.DeviceNo = i;
- else
- cmd->u.cache.DeviceNo = i;
-
- status = __gdth_execute(ha->sdev, cmd, cmnd, 30, &info);
-
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->hdr[i].rw_attribs = (status == S_OK ? (u16)info : 0);
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- }
-
- if (copy_to_user(arg, rsc, sizeof(gdth_ioctl_rescan)))
- rc = -EFAULT;
- else
- rc = 0;
-
-free_fail:
- kfree(rsc);
- kfree(cmd);
- return rc;
-}
-
-static int gdth_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
-{
- gdth_ha_str *ha;
- Scsi_Cmnd *scp;
- unsigned long flags;
- char cmnd[MAX_COMMAND_SIZE];
- void __user *argp = (void __user *)arg;
-
- memset(cmnd, 0xff, 12);
-
- TRACE(("gdth_ioctl() cmd 0x%x\n", cmd));
-
- switch (cmd) {
- case GDTIOCTL_CTRCNT:
- {
- int cnt = gdth_ctr_count;
- if (put_user(cnt, (int __user *)argp))
- return -EFAULT;
- break;
- }
-
- case GDTIOCTL_DRVERS:
- {
- int ver = (GDTH_VERSION<<8) | GDTH_SUBVERSION;
- if (put_user(ver, (int __user *)argp))
- return -EFAULT;
- break;
- }
-
- case GDTIOCTL_OSVERS:
- {
- gdth_ioctl_osvers osv;
-
- osv.version = (u8)(LINUX_VERSION_CODE >> 16);
- osv.subversion = (u8)(LINUX_VERSION_CODE >> 8);
- osv.revision = (u16)(LINUX_VERSION_CODE & 0xff);
- if (copy_to_user(argp, &osv, sizeof(gdth_ioctl_osvers)))
- return -EFAULT;
- break;
- }
-
- case GDTIOCTL_CTRTYPE:
- {
- gdth_ioctl_ctrtype ctrt;
-
- if (copy_from_user(&ctrt, argp, sizeof(gdth_ioctl_ctrtype)) ||
- (NULL == (ha = gdth_find_ha(ctrt.ionode))))
- return -EFAULT;
-
- if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
- ctrt.type = (u8)((ha->stype>>20) - 0x10);
- } else {
- if (ha->type != GDT_PCIMPR) {
- ctrt.type = (u8)((ha->stype<<4) + 6);
- } else {
- ctrt.type =
- (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe);
- if (ha->stype >= 0x300)
- ctrt.ext_type = 0x6000 | ha->pdev->subsystem_device;
- else
- ctrt.ext_type = 0x6000 | ha->stype;
- }
- ctrt.device_id = ha->pdev->device;
- ctrt.sub_device_id = ha->pdev->subsystem_device;
- }
- ctrt.info = ha->brd_phys;
- ctrt.oem_id = ha->oem_id;
- if (copy_to_user(argp, &ctrt, sizeof(gdth_ioctl_ctrtype)))
- return -EFAULT;
- break;
- }
-
- case GDTIOCTL_GENERAL:
- return ioc_general(argp, cmnd);
-
- case GDTIOCTL_EVENT:
- return ioc_event(argp);
-
- case GDTIOCTL_LOCKDRV:
- return ioc_lockdrv(argp);
-
- case GDTIOCTL_LOCKCHN:
- {
- gdth_ioctl_lockchn lchn;
- u8 i, j;
-
- if (copy_from_user(&lchn, argp, sizeof(gdth_ioctl_lockchn)) ||
- (NULL == (ha = gdth_find_ha(lchn.ionode))))
- return -EFAULT;
-
- i = lchn.channel;
- if (i < ha->bus_cnt) {
- if (lchn.lock) {
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->raw[i].lock = 1;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- for (j = 0; j < ha->tid_cnt; ++j)
- gdth_wait_completion(ha, i, j);
- } else {
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->raw[i].lock = 0;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- for (j = 0; j < ha->tid_cnt; ++j)
- gdth_next(ha);
- }
- }
- break;
- }
-
- case GDTIOCTL_RESCAN:
- return ioc_rescan(argp, cmnd);
-
- case GDTIOCTL_HDRLIST:
- return ioc_hdrlist(argp, cmnd);
-
- case GDTIOCTL_RESET_BUS:
- {
- gdth_ioctl_reset res;
- int rval;
-
- if (copy_from_user(&res, argp, sizeof(gdth_ioctl_reset)) ||
- (NULL == (ha = gdth_find_ha(res.ionode))))
- return -EFAULT;
-
- scp = kzalloc(sizeof(*scp), GFP_KERNEL);
- if (!scp)
- return -ENOMEM;
- scp->device = ha->sdev;
- scp->cmd_len = 12;
- scp->device->channel = res.number;
- rval = gdth_eh_bus_reset(scp);
- res.status = (rval == SUCCESS ? S_OK : S_GENERR);
- kfree(scp);
-
- if (copy_to_user(argp, &res, sizeof(gdth_ioctl_reset)))
- return -EFAULT;
- break;
- }
-
- case GDTIOCTL_RESET_DRV:
- return ioc_resetdrv(argp, cmnd);
-
- default:
- break;
- }
- return 0;
-}
-
-static long gdth_unlocked_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
-
- mutex_lock(&gdth_mutex);
- ret = gdth_ioctl(file, cmd, arg);
- mutex_unlock(&gdth_mutex);
-
- return ret;
-}
-
-/* flush routine */
-static void gdth_flush(gdth_ha_str *ha)
-{
- int i;
- gdth_cmd_str gdtcmd;
- char cmnd[MAX_COMMAND_SIZE];
- memset(cmnd, 0xff, MAX_COMMAND_SIZE);
-
- TRACE2(("gdth_flush() hanum %d\n", ha->hanum));
-
- for (i = 0; i < MAX_HDRIVES; ++i) {
- if (ha->hdr[i].present) {
- gdtcmd.BoardNode = LOCALBOARD;
- gdtcmd.Service = CACHESERVICE;
- gdtcmd.OpCode = GDT_FLUSH;
- if (ha->cache_feat & GDT_64BIT) {
- gdtcmd.u.cache64.DeviceNo = i;
- gdtcmd.u.cache64.BlockNo = 1;
- gdtcmd.u.cache64.sg_canz = 0;
- } else {
- gdtcmd.u.cache.DeviceNo = i;
- gdtcmd.u.cache.BlockNo = 1;
- gdtcmd.u.cache.sg_canz = 0;
- }
- TRACE2(("gdth_flush(): flush ha %d drive %d\n", ha->hanum, i));
-
- gdth_execute(ha->shost, &gdtcmd, cmnd, 30, NULL);
- }
- }
-}
-
-/* configure lun */
-static int gdth_slave_configure(struct scsi_device *sdev)
-{
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
- sdev->skip_ms_page_3f = 1;
- sdev->skip_ms_page_8 = 1;
- return 0;
-}
-
-static struct scsi_host_template gdth_template = {
- .name = "GDT SCSI Disk Array Controller",
- .info = gdth_info,
- .queuecommand = gdth_queuecommand,
- .eh_bus_reset_handler = gdth_eh_bus_reset,
- .slave_configure = gdth_slave_configure,
- .bios_param = gdth_bios_param,
- .proc_info = gdth_proc_info,
- .eh_timed_out = gdth_timed_out,
- .proc_name = "gdth",
- .can_queue = GDTH_MAXCMDS,
- .this_id = -1,
- .sg_tablesize = GDTH_MAXSG,
- .cmd_per_lun = GDTH_MAXC_P_L,
- .unchecked_isa_dma = 1,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-#ifdef CONFIG_ISA
-static int __init gdth_isa_probe_one(u32 isa_bios)
-{
- struct Scsi_Host *shp;
- gdth_ha_str *ha;
- dma_addr_t scratch_dma_handle = 0;
- int error, i;
-
- if (!gdth_search_isa(isa_bios))
- return -ENXIO;
-
- shp = scsi_host_alloc(&gdth_template, sizeof(gdth_ha_str));
- if (!shp)
- return -ENOMEM;
- ha = shost_priv(shp);
-
- error = -ENODEV;
- if (!gdth_init_isa(isa_bios,ha))
- goto out_host_put;
-
- /* controller found and initialized */
- printk("Configuring GDT-ISA HA at BIOS 0x%05X IRQ %u DRQ %u\n",
- isa_bios, ha->irq, ha->drq);
-
- error = request_irq(ha->irq, gdth_interrupt, IRQF_DISABLED, "gdth", ha);
- if (error) {
- printk("GDT-ISA: Unable to allocate IRQ\n");
- goto out_host_put;
- }
-
- error = request_dma(ha->drq, "gdth");
- if (error) {
- printk("GDT-ISA: Unable to allocate DMA channel\n");
- goto out_free_irq;
- }
-
- set_dma_mode(ha->drq,DMA_MODE_CASCADE);
- enable_dma(ha->drq);
- shp->unchecked_isa_dma = 1;
- shp->irq = ha->irq;
- shp->dma_channel = ha->drq;
-
- ha->hanum = gdth_ctr_count++;
- ha->shost = shp;
-
- ha->pccb = &ha->cmdext;
- ha->ccb_phys = 0L;
- ha->pdev = NULL;
-
- error = -ENOMEM;
-
- ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
- &scratch_dma_handle);
- if (!ha->pscratch)
- goto out_dec_counters;
- ha->scratch_phys = scratch_dma_handle;
-
- ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
- &scratch_dma_handle);
- if (!ha->pmsg)
- goto out_free_pscratch;
- ha->msg_phys = scratch_dma_handle;
-
-#ifdef INT_COAL
- ha->coal_stat = pci_alloc_consistent(ha->pdev,
- sizeof(gdth_coal_status) * MAXOFFSETS,
- &scratch_dma_handle);
- if (!ha->coal_stat)
- goto out_free_pmsg;
- ha->coal_stat_phys = scratch_dma_handle;
-#endif
-
- ha->scratch_busy = FALSE;
- ha->req_first = NULL;
- ha->tid_cnt = MAX_HDRIVES;
- if (max_ids > 0 && max_ids < ha->tid_cnt)
- ha->tid_cnt = max_ids;
- for (i = 0; i < GDTH_MAXCMDS; ++i)
- ha->cmd_tab[i].cmnd = UNUSED_CMND;
- ha->scan_mode = rescan ? 0x10 : 0;
-
- error = -ENODEV;
- if (!gdth_search_drives(ha)) {
- printk("GDT-ISA: Error during device scan\n");
- goto out_free_coal_stat;
- }
-
- if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
- hdr_channel = ha->bus_cnt;
- ha->virt_bus = hdr_channel;
-
- if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT)
- shp->max_cmd_len = 16;
-
- shp->max_id = ha->tid_cnt;
- shp->max_lun = MAXLUN;
- shp->max_channel = ha->bus_cnt;
-
- spin_lock_init(&ha->smp_lock);
- gdth_enable_int(ha);
-
- error = scsi_add_host(shp, NULL);
- if (error)
- goto out_free_coal_stat;
- list_add_tail(&ha->list, &gdth_instances);
- gdth_timer_init();
-
- scsi_scan_host(shp);
-
- return 0;
-
- out_free_coal_stat:
-#ifdef INT_COAL
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS,
- ha->coal_stat, ha->coal_stat_phys);
- out_free_pmsg:
-#endif
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
- ha->pmsg, ha->msg_phys);
- out_free_pscratch:
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
- ha->pscratch, ha->scratch_phys);
- out_dec_counters:
- gdth_ctr_count--;
- out_free_irq:
- free_irq(ha->irq, ha);
- out_host_put:
- scsi_host_put(shp);
- return error;
-}
-#endif /* CONFIG_ISA */
-
-#ifdef CONFIG_EISA
-static int __init gdth_eisa_probe_one(u16 eisa_slot)
-{
- struct Scsi_Host *shp;
- gdth_ha_str *ha;
- dma_addr_t scratch_dma_handle = 0;
- int error, i;
-
- if (!gdth_search_eisa(eisa_slot))
- return -ENXIO;
-
- shp = scsi_host_alloc(&gdth_template, sizeof(gdth_ha_str));
- if (!shp)
- return -ENOMEM;
- ha = shost_priv(shp);
-
- error = -ENODEV;
- if (!gdth_init_eisa(eisa_slot,ha))
- goto out_host_put;
-
- /* controller found and initialized */
- printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n",
- eisa_slot >> 12, ha->irq);
-
- error = request_irq(ha->irq, gdth_interrupt, IRQF_DISABLED, "gdth", ha);
- if (error) {
- printk("GDT-EISA: Unable to allocate IRQ\n");
- goto out_host_put;
- }
-
- shp->unchecked_isa_dma = 0;
- shp->irq = ha->irq;
- shp->dma_channel = 0xff;
-
- ha->hanum = gdth_ctr_count++;
- ha->shost = shp;
-
- TRACE2(("EISA detect Bus 0: hanum %d\n", ha->hanum));
-
- ha->pccb = &ha->cmdext;
- ha->ccb_phys = 0L;
-
- error = -ENOMEM;
-
- ha->pdev = NULL;
- ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
- &scratch_dma_handle);
- if (!ha->pscratch)
- goto out_free_irq;
- ha->scratch_phys = scratch_dma_handle;
-
- ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
- &scratch_dma_handle);
- if (!ha->pmsg)
- goto out_free_pscratch;
- ha->msg_phys = scratch_dma_handle;
-
-#ifdef INT_COAL
- ha->coal_stat = pci_alloc_consistent(ha->pdev,
- sizeof(gdth_coal_status) * MAXOFFSETS,
- &scratch_dma_handle);
- if (!ha->coal_stat)
- goto out_free_pmsg;
- ha->coal_stat_phys = scratch_dma_handle;
-#endif
-
- ha->ccb_phys = pci_map_single(ha->pdev,ha->pccb,
- sizeof(gdth_cmd_str), PCI_DMA_BIDIRECTIONAL);
- if (!ha->ccb_phys)
- goto out_free_coal_stat;
-
- ha->scratch_busy = FALSE;
- ha->req_first = NULL;
- ha->tid_cnt = MAX_HDRIVES;
- if (max_ids > 0 && max_ids < ha->tid_cnt)
- ha->tid_cnt = max_ids;
- for (i = 0; i < GDTH_MAXCMDS; ++i)
- ha->cmd_tab[i].cmnd = UNUSED_CMND;
- ha->scan_mode = rescan ? 0x10 : 0;
-
- if (!gdth_search_drives(ha)) {
- printk("GDT-EISA: Error during device scan\n");
- error = -ENODEV;
- goto out_free_ccb_phys;
- }
-
- if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
- hdr_channel = ha->bus_cnt;
- ha->virt_bus = hdr_channel;
-
- if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT)
- shp->max_cmd_len = 16;
-
- shp->max_id = ha->tid_cnt;
- shp->max_lun = MAXLUN;
- shp->max_channel = ha->bus_cnt;
-
- spin_lock_init(&ha->smp_lock);
- gdth_enable_int(ha);
-
- error = scsi_add_host(shp, NULL);
- if (error)
- goto out_free_ccb_phys;
- list_add_tail(&ha->list, &gdth_instances);
- gdth_timer_init();
-
- scsi_scan_host(shp);
-
- return 0;
-
- out_free_ccb_phys:
- pci_unmap_single(ha->pdev,ha->ccb_phys, sizeof(gdth_cmd_str),
- PCI_DMA_BIDIRECTIONAL);
- out_free_coal_stat:
-#ifdef INT_COAL
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS,
- ha->coal_stat, ha->coal_stat_phys);
- out_free_pmsg:
-#endif
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
- ha->pmsg, ha->msg_phys);
- out_free_pscratch:
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
- ha->pscratch, ha->scratch_phys);
- out_free_irq:
- free_irq(ha->irq, ha);
- gdth_ctr_count--;
- out_host_put:
- scsi_host_put(shp);
- return error;
-}
-#endif /* CONFIG_EISA */
-
-#ifdef CONFIG_PCI
-static int __devinit gdth_pci_probe_one(gdth_pci_str *pcistr,
- gdth_ha_str **ha_out)
-{
- struct Scsi_Host *shp;
- gdth_ha_str *ha;
- dma_addr_t scratch_dma_handle = 0;
- int error, i;
- struct pci_dev *pdev = pcistr->pdev;
-
- *ha_out = NULL;
-
- shp = scsi_host_alloc(&gdth_template, sizeof(gdth_ha_str));
- if (!shp)
- return -ENOMEM;
- ha = shost_priv(shp);
-
- error = -ENODEV;
- if (!gdth_init_pci(pdev, pcistr, ha))
- goto out_host_put;
-
- /* controller found and initialized */
- printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n",
- pdev->bus->number,
- PCI_SLOT(pdev->devfn),
- ha->irq);
-
- error = request_irq(ha->irq, gdth_interrupt,
- IRQF_DISABLED|IRQF_SHARED, "gdth", ha);
- if (error) {
- printk("GDT-PCI: Unable to allocate IRQ\n");
- goto out_host_put;
- }
-
- shp->unchecked_isa_dma = 0;
- shp->irq = ha->irq;
- shp->dma_channel = 0xff;
-
- ha->hanum = gdth_ctr_count++;
- ha->shost = shp;
-
- ha->pccb = &ha->cmdext;
- ha->ccb_phys = 0L;
-
- error = -ENOMEM;
-
- ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
- &scratch_dma_handle);
- if (!ha->pscratch)
- goto out_free_irq;
- ha->scratch_phys = scratch_dma_handle;
-
- ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
- &scratch_dma_handle);
- if (!ha->pmsg)
- goto out_free_pscratch;
- ha->msg_phys = scratch_dma_handle;
-
-#ifdef INT_COAL
- ha->coal_stat = pci_alloc_consistent(ha->pdev,
- sizeof(gdth_coal_status) * MAXOFFSETS,
- &scratch_dma_handle);
- if (!ha->coal_stat)
- goto out_free_pmsg;
- ha->coal_stat_phys = scratch_dma_handle;
-#endif
-
- ha->scratch_busy = FALSE;
- ha->req_first = NULL;
- ha->tid_cnt = pdev->device >= 0x200 ? MAXID : MAX_HDRIVES;
- if (max_ids > 0 && max_ids < ha->tid_cnt)
- ha->tid_cnt = max_ids;
- for (i = 0; i < GDTH_MAXCMDS; ++i)
- ha->cmd_tab[i].cmnd = UNUSED_CMND;
- ha->scan_mode = rescan ? 0x10 : 0;
-
- error = -ENODEV;
- if (!gdth_search_drives(ha)) {
- printk("GDT-PCI %d: Error during device scan\n", ha->hanum);
- goto out_free_coal_stat;
- }
-
- if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
- hdr_channel = ha->bus_cnt;
- ha->virt_bus = hdr_channel;
-
- /* 64-bit DMA only supported from FW >= x.43 */
- if (!(ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT) ||
- !ha->dma64_support) {
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
- printk(KERN_WARNING "GDT-PCI %d: "
- "Unable to set 32-bit DMA\n", ha->hanum);
- goto out_free_coal_stat;
- }
- } else {
- shp->max_cmd_len = 16;
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
- printk("GDT-PCI %d: 64-bit DMA enabled\n", ha->hanum);
- } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
- printk(KERN_WARNING "GDT-PCI %d: "
- "Unable to set 64/32-bit DMA\n", ha->hanum);
- goto out_free_coal_stat;
- }
- }
-
- shp->max_id = ha->tid_cnt;
- shp->max_lun = MAXLUN;
- shp->max_channel = ha->bus_cnt;
-
- spin_lock_init(&ha->smp_lock);
- gdth_enable_int(ha);
-
- error = scsi_add_host(shp, &pdev->dev);
- if (error)
- goto out_free_coal_stat;
- list_add_tail(&ha->list, &gdth_instances);
-
- pci_set_drvdata(ha->pdev, ha);
- gdth_timer_init();
-
- scsi_scan_host(shp);
-
- *ha_out = ha;
-
- return 0;
-
- out_free_coal_stat:
-#ifdef INT_COAL
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS,
- ha->coal_stat, ha->coal_stat_phys);
- out_free_pmsg:
-#endif
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
- ha->pmsg, ha->msg_phys);
- out_free_pscratch:
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
- ha->pscratch, ha->scratch_phys);
- out_free_irq:
- free_irq(ha->irq, ha);
- gdth_ctr_count--;
- out_host_put:
- scsi_host_put(shp);
- return error;
-}
-#endif /* CONFIG_PCI */
-
-static void gdth_remove_one(gdth_ha_str *ha)
-{
- struct Scsi_Host *shp = ha->shost;
-
- TRACE2(("gdth_remove_one()\n"));
-
- scsi_remove_host(shp);
-
- gdth_flush(ha);
-
- if (ha->sdev) {
- scsi_free_host_dev(ha->sdev);
- ha->sdev = NULL;
- }
-
- if (shp->irq)
- free_irq(shp->irq,ha);
-
-#ifdef CONFIG_ISA
- if (shp->dma_channel != 0xff)
- free_dma(shp->dma_channel);
-#endif
-#ifdef INT_COAL
- if (ha->coal_stat)
- pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) *
- MAXOFFSETS, ha->coal_stat, ha->coal_stat_phys);
-#endif
- if (ha->pscratch)
- pci_free_consistent(ha->pdev, GDTH_SCRATCH,
- ha->pscratch, ha->scratch_phys);
- if (ha->pmsg)
- pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
- ha->pmsg, ha->msg_phys);
- if (ha->ccb_phys)
- pci_unmap_single(ha->pdev,ha->ccb_phys,
- sizeof(gdth_cmd_str),PCI_DMA_BIDIRECTIONAL);
-
- scsi_host_put(shp);
-}
-
-static int gdth_halt(struct notifier_block *nb, unsigned long event, void *buf)
-{
- gdth_ha_str *ha;
-
- TRACE2(("gdth_halt() event %d\n", (int)event));
- if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
- return NOTIFY_DONE;
-
- list_for_each_entry(ha, &gdth_instances, list)
- gdth_flush(ha);
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block gdth_notifier = {
- gdth_halt, NULL, 0
-};
-
-static int __init gdth_init(void)
-{
- if (disable) {
- printk("GDT-HA: Controller driver disabled from"
- " command line !\n");
- return 0;
- }
-
- printk("GDT-HA: Storage RAID Controller Driver. Version: %s\n",
- GDTH_VERSION_STR);
-
- /* initializations */
- gdth_polling = TRUE;
- gdth_clear_events();
- init_timer(&gdth_timer);
-
- /* As default we do not probe for EISA or ISA controllers */
- if (probe_eisa_isa) {
- /* scanning for controllers, at first: ISA controller */
-#ifdef CONFIG_ISA
- u32 isa_bios;
- for (isa_bios = 0xc8000UL; isa_bios <= 0xd8000UL;
- isa_bios += 0x8000UL)
- gdth_isa_probe_one(isa_bios);
-#endif
-#ifdef CONFIG_EISA
- {
- u16 eisa_slot;
- for (eisa_slot = 0x1000; eisa_slot <= 0x8000;
- eisa_slot += 0x1000)
- gdth_eisa_probe_one(eisa_slot);
- }
-#endif
- }
-
-#ifdef CONFIG_PCI
- /* scanning for PCI controllers */
- if (pci_register_driver(&gdth_pci_driver)) {
- gdth_ha_str *ha;
-
- list_for_each_entry(ha, &gdth_instances, list)
- gdth_remove_one(ha);
- return -ENODEV;
- }
-#endif /* CONFIG_PCI */
-
- TRACE2(("gdth_detect() %d controller detected\n", gdth_ctr_count));
-
- major = register_chrdev(0,"gdth", &gdth_fops);
- register_reboot_notifier(&gdth_notifier);
- gdth_polling = FALSE;
- return 0;
-}
-
-static void __exit gdth_exit(void)
-{
- gdth_ha_str *ha;
-
- unregister_chrdev(major, "gdth");
- unregister_reboot_notifier(&gdth_notifier);
-
-#ifdef GDTH_STATISTICS
- del_timer_sync(&gdth_timer);
-#endif
-
-#ifdef CONFIG_PCI
- pci_unregister_driver(&gdth_pci_driver);
-#endif
-
- list_for_each_entry(ha, &gdth_instances, list)
- gdth_remove_one(ha);
-}
-
-module_init(gdth_init);
-module_exit(gdth_exit);
-
-#ifndef MODULE
-__setup("gdth=", option_setup);
-#endif
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
deleted file mode 100644
index d969855ac64a..000000000000
--- a/drivers/scsi/gdth.h
+++ /dev/null
@@ -1,1021 +0,0 @@
-#ifndef _GDTH_H
-#define _GDTH_H
-
-/*
- * Header file for the GDT Disk Array/Storage RAID controllers driver for Linux
- *
- * gdth.h Copyright (C) 1995-06 ICP vortex, Achim Leubner
- * See gdth.c for further informations and
- * below for supported controller types
- *
- * <achim_leubner@adaptec.com>
- *
- * $Id: gdth.h,v 1.58 2006/01/11 16:14:09 achim Exp $
- */
-
-#include <linux/types.h>
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-/* defines, macros */
-
-/* driver version */
-#define GDTH_VERSION_STR "3.05"
-#define GDTH_VERSION 3
-#define GDTH_SUBVERSION 5
-
-/* protocol version */
-#define PROTOCOL_VERSION 1
-
-/* OEM IDs */
-#define OEM_ID_ICP 0x941c
-#define OEM_ID_INTEL 0x8000
-
-/* controller classes */
-#define GDT_ISA 0x01 /* ISA controller */
-#define GDT_EISA 0x02 /* EISA controller */
-#define GDT_PCI 0x03 /* PCI controller */
-#define GDT_PCINEW 0x04 /* new PCI controller */
-#define GDT_PCIMPR 0x05 /* PCI MPR controller */
-/* GDT_EISA, controller subtypes EISA */
-#define GDT3_ID 0x0130941c /* GDT3000/3020 */
-#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */
-#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */
-/* GDT_ISA */
-#define GDT2_ID 0x0120941c /* GDT2000/2020 */
-
-/* vendor ID, device IDs (PCI) */
-/* these defines should already exist in <linux/pci.h> */
-#ifndef PCI_VENDOR_ID_VORTEX
-#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */
-#endif
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL 0x8086
-#endif
-
-#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
-/* GDT_PCI */
-#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */
-#define PCI_DEVICE_ID_VORTEX_GDT6000B 1 /* GDT6000B/6010 */
-/* GDT_PCINEW */
-#define PCI_DEVICE_ID_VORTEX_GDT6x10 2 /* GDT6110/6510 */
-#define PCI_DEVICE_ID_VORTEX_GDT6x20 3 /* GDT6120/6520 */
-#define PCI_DEVICE_ID_VORTEX_GDT6530 4 /* GDT6530 */
-#define PCI_DEVICE_ID_VORTEX_GDT6550 5 /* GDT6550 */
-/* GDT_PCINEW, wide/ultra SCSI controllers */
-#define PCI_DEVICE_ID_VORTEX_GDT6x17 6 /* GDT6117/6517 */
-#define PCI_DEVICE_ID_VORTEX_GDT6x27 7 /* GDT6127/6527 */
-#define PCI_DEVICE_ID_VORTEX_GDT6537 8 /* GDT6537 */
-#define PCI_DEVICE_ID_VORTEX_GDT6557 9 /* GDT6557/6557-ECC */
-/* GDT_PCINEW, wide SCSI controllers */
-#define PCI_DEVICE_ID_VORTEX_GDT6x15 10 /* GDT6115/6515 */
-#define PCI_DEVICE_ID_VORTEX_GDT6x25 11 /* GDT6125/6525 */
-#define PCI_DEVICE_ID_VORTEX_GDT6535 12 /* GDT6535 */
-#define PCI_DEVICE_ID_VORTEX_GDT6555 13 /* GDT6555/6555-ECC */
-#endif
-
-#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP
-/* GDT_MPR, RP series, wide/ultra SCSI */
-#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x100 /* GDT6117RP/GDT6517RP */
-#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x101 /* GDT6127RP/GDT6527RP */
-#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x102 /* GDT6537RP */
-#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x103 /* GDT6557RP */
-/* GDT_MPR, RP series, narrow/ultra SCSI */
-#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x104 /* GDT6111RP/GDT6511RP */
-#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x105 /* GDT6121RP/GDT6521RP */
-#endif
-#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RD
-/* GDT_MPR, RD series, wide/ultra SCSI */
-#define PCI_DEVICE_ID_VORTEX_GDT6x17RD 0x110 /* GDT6117RD/GDT6517RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6x27RD 0x111 /* GDT6127RD/GDT6527RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6537RD 0x112 /* GDT6537RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6557RD 0x113 /* GDT6557RD */
-/* GDT_MPR, RD series, narrow/ultra SCSI */
-#define PCI_DEVICE_ID_VORTEX_GDT6x11RD 0x114 /* GDT6111RD/GDT6511RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6x21RD 0x115 /* GDT6121RD/GDT6521RD */
-/* GDT_MPR, RD series, wide/ultra2 SCSI */
-#define PCI_DEVICE_ID_VORTEX_GDT6x18RD 0x118 /* GDT6118RD/GDT6518RD/
- GDT6618RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6x28RD 0x119 /* GDT6128RD/GDT6528RD/
- GDT6628RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6x38RD 0x11A /* GDT6538RD/GDT6638RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6x58RD 0x11B /* GDT6558RD/GDT6658RD */
-/* GDT_MPR, RN series (64-bit PCI), wide/ultra2 SCSI */
-#define PCI_DEVICE_ID_VORTEX_GDT7x18RN 0x168 /* GDT7118RN/GDT7518RN/
- GDT7618RN */
-#define PCI_DEVICE_ID_VORTEX_GDT7x28RN 0x169 /* GDT7128RN/GDT7528RN/
- GDT7628RN */
-#define PCI_DEVICE_ID_VORTEX_GDT7x38RN 0x16A /* GDT7538RN/GDT7638RN */
-#define PCI_DEVICE_ID_VORTEX_GDT7x58RN 0x16B /* GDT7558RN/GDT7658RN */
-#endif
-
-#ifndef PCI_DEVICE_ID_VORTEX_GDT6x19RD
-/* GDT_MPR, RD series, Fibre Channel */
-#define PCI_DEVICE_ID_VORTEX_GDT6x19RD 0x210 /* GDT6519RD/GDT6619RD */
-#define PCI_DEVICE_ID_VORTEX_GDT6x29RD 0x211 /* GDT6529RD/GDT6629RD */
-/* GDT_MPR, RN series (64-bit PCI), Fibre Channel */
-#define PCI_DEVICE_ID_VORTEX_GDT7x19RN 0x260 /* GDT7519RN/GDT7619RN */
-#define PCI_DEVICE_ID_VORTEX_GDT7x29RN 0x261 /* GDT7529RN/GDT7629RN */
-#endif
-
-#ifndef PCI_DEVICE_ID_VORTEX_GDTMAXRP
-/* GDT_MPR, last device ID */
-#define PCI_DEVICE_ID_VORTEX_GDTMAXRP 0x2ff
-#endif
-
-#ifndef PCI_DEVICE_ID_VORTEX_GDTNEWRX
-/* new GDT Rx Controller */
-#define PCI_DEVICE_ID_VORTEX_GDTNEWRX 0x300
-#endif
-
-#ifndef PCI_DEVICE_ID_VORTEX_GDTNEWRX2
-/* new(2) GDT Rx Controller */
-#define PCI_DEVICE_ID_VORTEX_GDTNEWRX2 0x301
-#endif
-
-#ifndef PCI_DEVICE_ID_INTEL_SRC
-/* Intel Storage RAID Controller */
-#define PCI_DEVICE_ID_INTEL_SRC 0x600
-#endif
-
-#ifndef PCI_DEVICE_ID_INTEL_SRC_XSCALE
-/* Intel Storage RAID Controller */
-#define PCI_DEVICE_ID_INTEL_SRC_XSCALE 0x601
-#endif
-
-/* limits */
-#define GDTH_SCRATCH PAGE_SIZE /* 4KB scratch buffer */
-#define GDTH_MAXCMDS 120
-#define GDTH_MAXC_P_L 16 /* max. cmds per lun */
-#define GDTH_MAX_RAW 2 /* max. cmds per raw device */
-#define MAXOFFSETS 128
-#define MAXHA 16
-#define MAXID 127
-#define MAXLUN 8
-#define MAXBUS 6
-#define MAX_EVENTS 100 /* event buffer count */
-#define MAX_RES_ARGS 40 /* device reservation,
- must be a multiple of 4 */
-#define MAXCYLS 1024
-#define HEADS 64
-#define SECS 32 /* mapping 64*32 */
-#define MEDHEADS 127
-#define MEDSECS 63 /* mapping 127*63 */
-#define BIGHEADS 255
-#define BIGSECS 63 /* mapping 255*63 */
-
-/* special command ptr. */
-#define UNUSED_CMND ((Scsi_Cmnd *)-1)
-#define INTERNAL_CMND ((Scsi_Cmnd *)-2)
-#define SCREEN_CMND ((Scsi_Cmnd *)-3)
-#define SPECIAL_SCP(p) (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND)
-
-/* controller services */
-#define SCSIRAWSERVICE 3
-#define CACHESERVICE 9
-#define SCREENSERVICE 11
-
-/* screenservice defines */
-#define MSG_INV_HANDLE -1 /* special message handle */
-#define MSGLEN 16 /* size of message text */
-#define MSG_SIZE 34 /* size of message structure */
-#define MSG_REQUEST 0 /* async. event: message */
-
-/* cacheservice defines */
-#define SECTOR_SIZE 0x200 /* always 512 bytes per sec. */
-
-/* DPMEM constants */
-#define DPMEM_MAGIC 0xC0FFEE11
-#define IC_HEADER_BYTES 48
-#define IC_QUEUE_BYTES 4
-#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
-
-/* cluster_type constants */
-#define CLUSTER_DRIVE 1
-#define CLUSTER_MOUNTED 2
-#define CLUSTER_RESERVED 4
-#define CLUSTER_RESERVE_STATE (CLUSTER_DRIVE|CLUSTER_MOUNTED|CLUSTER_RESERVED)
-
-/* commands for all services, cache service */
-#define GDT_INIT 0 /* service initialization */
-#define GDT_READ 1 /* read command */
-#define GDT_WRITE 2 /* write command */
-#define GDT_INFO 3 /* information about devices */
-#define GDT_FLUSH 4 /* flush dirty cache buffers */
-#define GDT_IOCTL 5 /* ioctl command */
-#define GDT_DEVTYPE 9 /* additional information */
-#define GDT_MOUNT 10 /* mount cache device */
-#define GDT_UNMOUNT 11 /* unmount cache device */
-#define GDT_SET_FEAT 12 /* set feat. (scatter/gather) */
-#define GDT_GET_FEAT 13 /* get features */
-#define GDT_WRITE_THR 16 /* write through */
-#define GDT_READ_THR 17 /* read through */
-#define GDT_EXT_INFO 18 /* extended info */
-#define GDT_RESET 19 /* controller reset */
-#define GDT_RESERVE_DRV 20 /* reserve host drive */
-#define GDT_RELEASE_DRV 21 /* release host drive */
-#define GDT_CLUST_INFO 22 /* cluster info */
-#define GDT_RW_ATTRIBS 23 /* R/W attribs (write thru,..)*/
-#define GDT_CLUST_RESET 24 /* releases the cluster drives*/
-#define GDT_FREEZE_IO 25 /* freezes all IOs */
-#define GDT_UNFREEZE_IO 26 /* unfreezes all IOs */
-#define GDT_X_INIT_HOST 29 /* ext. init: 64 bit support */
-#define GDT_X_INFO 30 /* ext. info for drives>2TB */
-
-/* raw service commands */
-#define GDT_RESERVE 14 /* reserve dev. to raw serv. */
-#define GDT_RELEASE 15 /* release device */
-#define GDT_RESERVE_ALL 16 /* reserve all devices */
-#define GDT_RELEASE_ALL 17 /* release all devices */
-#define GDT_RESET_BUS 18 /* reset bus */
-#define GDT_SCAN_START 19 /* start device scan */
-#define GDT_SCAN_END 20 /* stop device scan */
-#define GDT_X_INIT_RAW 21 /* ext. init: 64 bit support */
-
-/* screen service commands */
-#define GDT_REALTIME 3 /* realtime clock to screens. */
-#define GDT_X_INIT_SCR 4 /* ext. init: 64 bit support */
-
-/* IOCTL command defines */
-#define SCSI_DR_INFO 0x00 /* SCSI drive info */
-#define SCSI_CHAN_CNT 0x05 /* SCSI channel count */
-#define SCSI_DR_LIST 0x06 /* SCSI drive list */
-#define SCSI_DEF_CNT 0x15 /* grown/primary defects */
-#define DSK_STATISTICS 0x4b /* SCSI disk statistics */
-#define IOCHAN_DESC 0x5d /* description of IO channel */
-#define IOCHAN_RAW_DESC 0x5e /* description of raw IO chn. */
-#define L_CTRL_PATTERN 0x20000000L /* SCSI IOCTL mask */
-#define ARRAY_INFO 0x12 /* array drive info */
-#define ARRAY_DRV_LIST 0x0f /* array drive list */
-#define ARRAY_DRV_LIST2 0x34 /* array drive list (new) */
-#define LA_CTRL_PATTERN 0x10000000L /* array IOCTL mask */
-#define CACHE_DRV_CNT 0x01 /* cache drive count */
-#define CACHE_DRV_LIST 0x02 /* cache drive list */
-#define CACHE_INFO 0x04 /* cache info */
-#define CACHE_CONFIG 0x05 /* cache configuration */
-#define CACHE_DRV_INFO 0x07 /* cache drive info */
-#define BOARD_FEATURES 0x15 /* controller features */
-#define BOARD_INFO 0x28 /* controller info */
-#define SET_PERF_MODES 0x82 /* set mode (coalescing,..) */
-#define GET_PERF_MODES 0x83 /* get mode */
-#define CACHE_READ_OEM_STRING_RECORD 0x84 /* read OEM string record */
-#define HOST_GET 0x10001L /* get host drive list */
-#define IO_CHANNEL 0x00020000L /* default IO channel */
-#define INVALID_CHANNEL 0x0000ffffL /* invalid channel */
-
-/* service errors */
-#define S_OK 1 /* no error */
-#define S_GENERR 6 /* general error */
-#define S_BSY 7 /* controller busy */
-#define S_CACHE_UNKNOWN 12 /* cache serv.: drive unknown */
-#define S_RAW_SCSI 12 /* raw serv.: target error */
-#define S_RAW_ILL 0xff /* raw serv.: illegal */
-#define S_NOFUNC -2 /* unknown function */
-#define S_CACHE_RESERV -24 /* cache: reserv. conflict */
-
-/* timeout values */
-#define INIT_RETRIES 100000 /* 100000 * 1ms = 100s */
-#define INIT_TIMEOUT 100000 /* 100000 * 1ms = 100s */
-#define POLL_TIMEOUT 10000 /* 10000 * 1ms = 10s */
-
-/* priorities */
-#define DEFAULT_PRI 0x20
-#define IOCTL_PRI 0x10
-#define HIGH_PRI 0x08
-
-/* data directions */
-#define GDTH_DATA_IN 0x01000000L /* data from target */
-#define GDTH_DATA_OUT 0x00000000L /* data to target */
-
-/* BMIC registers (EISA controllers) */
-#define ID0REG 0x0c80 /* board ID */
-#define EINTENABREG 0x0c89 /* interrupt enable */
-#define SEMA0REG 0x0c8a /* command semaphore */
-#define SEMA1REG 0x0c8b /* status semaphore */
-#define LDOORREG 0x0c8d /* local doorbell */
-#define EDENABREG 0x0c8e /* EISA system doorbell enab. */
-#define EDOORREG 0x0c8f /* EISA system doorbell */
-#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */
-#define EISAREG 0x0cc0 /* EISA configuration */
-
-/* other defines */
-#define LINUX_OS 8 /* used for cache optim. */
-#define SECS32 0x1f /* round capacity */
-#define BIOS_ID_OFFS 0x10 /* offset contr-ID in ISABIOS */
-#define LOCALBOARD 0 /* board node always 0 */
-#define ASYNCINDEX 0 /* cmd index async. event */
-#define SPEZINDEX 1 /* cmd index unknown service */
-#define COALINDEX (GDTH_MAXCMDS + 2)
-
-/* features */
-#define SCATTER_GATHER 1 /* s/g feature */
-#define GDT_WR_THROUGH 0x100 /* WRITE_THROUGH supported */
-#define GDT_64BIT 0x200 /* 64bit / drv>2TB support */
-
-#include "gdth_ioctl.h"
-
-/* screenservice message */
-typedef struct {
- u32 msg_handle; /* message handle */
- u32 msg_len; /* size of message */
- u32 msg_alen; /* answer length */
- u8 msg_answer; /* answer flag */
- u8 msg_ext; /* more messages */
- u8 msg_reserved[2];
- char msg_text[MSGLEN+2]; /* the message text */
-} __attribute__((packed)) gdth_msg_str;
-
-
-/* IOCTL data structures */
-
-/* Status coalescing buffer for returning multiple requests per interrupt */
-typedef struct {
- u32 status;
- u32 ext_status;
- u32 info0;
- u32 info1;
-} __attribute__((packed)) gdth_coal_status;
-
-/* performance mode data structure */
-typedef struct {
- u32 version; /* The version of this IOCTL structure. */
- u32 st_mode; /* 0=dis., 1=st_buf_addr1 valid, 2=both */
- u32 st_buff_addr1; /* physical address of status buffer 1 */
- u32 st_buff_u_addr1; /* reserved for 64 bit addressing */
- u32 st_buff_indx1; /* reserved command idx. for this buffer */
- u32 st_buff_addr2; /* physical address of status buffer 1 */
- u32 st_buff_u_addr2; /* reserved for 64 bit addressing */
- u32 st_buff_indx2; /* reserved command idx. for this buffer */
- u32 st_buff_size; /* size of each buffer in bytes */
- u32 cmd_mode; /* 0 = mode disabled, 1 = cmd_buff_addr1 */
- u32 cmd_buff_addr1; /* physical address of cmd buffer 1 */
- u32 cmd_buff_u_addr1; /* reserved for 64 bit addressing */
- u32 cmd_buff_indx1; /* cmd buf addr1 unique identifier */
- u32 cmd_buff_addr2; /* physical address of cmd buffer 1 */
- u32 cmd_buff_u_addr2; /* reserved for 64 bit addressing */
- u32 cmd_buff_indx2; /* cmd buf addr1 unique identifier */
- u32 cmd_buff_size; /* size of each cmd bufer in bytes */
- u32 reserved1;
- u32 reserved2;
-} __attribute__((packed)) gdth_perf_modes;
-
-/* SCSI drive info */
-typedef struct {
- u8 vendor[8]; /* vendor string */
- u8 product[16]; /* product string */
- u8 revision[4]; /* revision */
- u32 sy_rate; /* current rate for sync. tr. */
- u32 sy_max_rate; /* max. rate for sync. tr. */
- u32 no_ldrive; /* belongs to this log. drv.*/
- u32 blkcnt; /* number of blocks */
- u16 blksize; /* size of block in bytes */
- u8 available; /* flag: access is available */
- u8 init; /* medium is initialized */
- u8 devtype; /* SCSI devicetype */
- u8 rm_medium; /* medium is removable */
- u8 wp_medium; /* medium is write protected */
- u8 ansi; /* SCSI I/II or III? */
- u8 protocol; /* same as ansi */
- u8 sync; /* flag: sync. transfer enab. */
- u8 disc; /* flag: disconnect enabled */
- u8 queueing; /* flag: command queing enab. */
- u8 cached; /* flag: caching enabled */
- u8 target_id; /* target ID of device */
- u8 lun; /* LUN id of device */
- u8 orphan; /* flag: drive fragment */
- u32 last_error; /* sense key or drive state */
- u32 last_result; /* result of last command */
- u32 check_errors; /* err. in last surface check */
- u8 percent; /* progress for surface check */
- u8 last_check; /* IOCTRL operation */
- u8 res[2];
- u32 flags; /* from 1.19/2.19: raw reserv.*/
- u8 multi_bus; /* multi bus dev? (fibre ch.) */
- u8 mb_status; /* status: available? */
- u8 res2[2];
- u8 mb_alt_status; /* status on second bus */
- u8 mb_alt_bid; /* number of second bus */
- u8 mb_alt_tid; /* target id on second bus */
- u8 res3;
- u8 fc_flag; /* from 1.22/2.22: info valid?*/
- u8 res4;
- u16 fc_frame_size; /* frame size (bytes) */
- char wwn[8]; /* world wide name */
-} __attribute__((packed)) gdth_diskinfo_str;
-
-/* get SCSI channel count */
-typedef struct {
- u32 channel_no; /* number of channel */
- u32 drive_cnt; /* drive count */
- u8 siop_id; /* SCSI processor ID */
- u8 siop_state; /* SCSI processor state */
-} __attribute__((packed)) gdth_getch_str;
-
-/* get SCSI drive numbers */
-typedef struct {
- u32 sc_no; /* SCSI channel */
- u32 sc_cnt; /* sc_list[] elements */
- u32 sc_list[MAXID]; /* minor device numbers */
-} __attribute__((packed)) gdth_drlist_str;
-
-/* get grown/primary defect count */
-typedef struct {
- u8 sddc_type; /* 0x08: grown, 0x10: prim. */
- u8 sddc_format; /* list entry format */
- u8 sddc_len; /* list entry length */
- u8 sddc_res;
- u32 sddc_cnt; /* entry count */
-} __attribute__((packed)) gdth_defcnt_str;
-
-/* disk statistics */
-typedef struct {
- u32 bid; /* SCSI channel */
- u32 first; /* first SCSI disk */
- u32 entries; /* number of elements */
- u32 count; /* (R) number of init. el. */
- u32 mon_time; /* time stamp */
- struct {
- u8 tid; /* target ID */
- u8 lun; /* LUN */
- u8 res[2];
- u32 blk_size; /* block size in bytes */
- u32 rd_count; /* bytes read */
- u32 wr_count; /* bytes written */
- u32 rd_blk_count; /* blocks read */
- u32 wr_blk_count; /* blocks written */
- u32 retries; /* retries */
- u32 reassigns; /* reassigns */
- } __attribute__((packed)) list[1];
-} __attribute__((packed)) gdth_dskstat_str;
-
-/* IO channel header */
-typedef struct {
- u32 version; /* version (-1UL: newest) */
- u8 list_entries; /* list entry count */
- u8 first_chan; /* first channel number */
- u8 last_chan; /* last channel number */
- u8 chan_count; /* (R) channel count */
- u32 list_offset; /* offset of list[0] */
-} __attribute__((packed)) gdth_iochan_header;
-
-/* get IO channel description */
-typedef struct {
- gdth_iochan_header hdr;
- struct {
- u32 address; /* channel address */
- u8 type; /* type (SCSI, FCAL) */
- u8 local_no; /* local number */
- u16 features; /* channel features */
- } __attribute__((packed)) list[MAXBUS];
-} __attribute__((packed)) gdth_iochan_str;
-
-/* get raw IO channel description */
-typedef struct {
- gdth_iochan_header hdr;
- struct {
- u8 proc_id; /* processor id */
- u8 proc_defect; /* defect ? */
- u8 reserved[2];
- } __attribute__((packed)) list[MAXBUS];
-} __attribute__((packed)) gdth_raw_iochan_str;
-
-/* array drive component */
-typedef struct {
- u32 al_controller; /* controller ID */
- u8 al_cache_drive; /* cache drive number */
- u8 al_status; /* cache drive state */
- u8 al_res[2];
-} __attribute__((packed)) gdth_arraycomp_str;
-
-/* array drive information */
-typedef struct {
- u8 ai_type; /* array type (RAID0,4,5) */
- u8 ai_cache_drive_cnt; /* active cachedrives */
- u8 ai_state; /* array drive state */
- u8 ai_master_cd; /* master cachedrive */
- u32 ai_master_controller; /* ID of master controller */
- u32 ai_size; /* user capacity [sectors] */
- u32 ai_striping_size; /* striping size [sectors] */
- u32 ai_secsize; /* sector size [bytes] */
- u32 ai_err_info; /* failed cache drive */
- u8 ai_name[8]; /* name of the array drive */
- u8 ai_controller_cnt; /* number of controllers */
- u8 ai_removable; /* flag: removable */
- u8 ai_write_protected; /* flag: write protected */
- u8 ai_devtype; /* type: always direct access */
- gdth_arraycomp_str ai_drives[35]; /* drive components: */
- u8 ai_drive_entries; /* number of drive components */
- u8 ai_protected; /* protection flag */
- u8 ai_verify_state; /* state of a parity verify */
- u8 ai_ext_state; /* extended array drive state */
- u8 ai_expand_state; /* array expand state (>=2.18)*/
- u8 ai_reserved[3];
-} __attribute__((packed)) gdth_arrayinf_str;
-
-/* get array drive list */
-typedef struct {
- u32 controller_no; /* controller no. */
- u8 cd_handle; /* master cachedrive */
- u8 is_arrayd; /* Flag: is array drive? */
- u8 is_master; /* Flag: is array master? */
- u8 is_parity; /* Flag: is parity drive? */
- u8 is_hotfix; /* Flag: is hotfix drive? */
- u8 res[3];
-} __attribute__((packed)) gdth_alist_str;
-
-typedef struct {
- u32 entries_avail; /* allocated entries */
- u32 entries_init; /* returned entries */
- u32 first_entry; /* first entry number */
- u32 list_offset; /* offset of following list */
- gdth_alist_str list[1]; /* list */
-} __attribute__((packed)) gdth_arcdl_str;
-
-/* cache info/config IOCTL */
-typedef struct {
- u32 version; /* firmware version */
- u16 state; /* cache state (on/off) */
- u16 strategy; /* cache strategy */
- u16 write_back; /* write back state (on/off) */
- u16 block_size; /* cache block size */
-} __attribute__((packed)) gdth_cpar_str;
-
-typedef struct {
- u32 csize; /* cache size */
- u32 read_cnt; /* read/write counter */
- u32 write_cnt;
- u32 tr_hits; /* hits */
- u32 sec_hits;
- u32 sec_miss; /* misses */
-} __attribute__((packed)) gdth_cstat_str;
-
-typedef struct {
- gdth_cpar_str cpar;
- gdth_cstat_str cstat;
-} __attribute__((packed)) gdth_cinfo_str;
-
-/* cache drive info */
-typedef struct {
- u8 cd_name[8]; /* cache drive name */
- u32 cd_devtype; /* SCSI devicetype */
- u32 cd_ldcnt; /* number of log. drives */
- u32 cd_last_error; /* last error */
- u8 cd_initialized; /* drive is initialized */
- u8 cd_removable; /* media is removable */
- u8 cd_write_protected; /* write protected */
- u8 cd_flags; /* Pool Hot Fix? */
- u32 ld_blkcnt; /* number of blocks */
- u32 ld_blksize; /* blocksize */
- u32 ld_dcnt; /* number of disks */
- u32 ld_slave; /* log. drive index */
- u32 ld_dtype; /* type of logical drive */
- u32 ld_last_error; /* last error */
- u8 ld_name[8]; /* log. drive name */
- u8 ld_error; /* error */
-} __attribute__((packed)) gdth_cdrinfo_str;
-
-/* OEM string */
-typedef struct {
- u32 ctl_version;
- u32 file_major_version;
- u32 file_minor_version;
- u32 buffer_size;
- u32 cpy_count;
- u32 ext_error;
- u32 oem_id;
- u32 board_id;
-} __attribute__((packed)) gdth_oem_str_params;
-
-typedef struct {
- u8 product_0_1_name[16];
- u8 product_4_5_name[16];
- u8 product_cluster_name[16];
- u8 product_reserved[16];
- u8 scsi_cluster_target_vendor_id[16];
- u8 cluster_raid_fw_name[16];
- u8 oem_brand_name[16];
- u8 oem_raid_type[16];
- u8 bios_type[13];
- u8 bios_title[50];
- u8 oem_company_name[37];
- u32 pci_id_1;
- u32 pci_id_2;
- u8 validation_status[80];
- u8 reserved_1[4];
- u8 scsi_host_drive_inquiry_vendor_id[16];
- u8 library_file_template[16];
- u8 reserved_2[16];
- u8 tool_name_1[32];
- u8 tool_name_2[32];
- u8 tool_name_3[32];
- u8 oem_contact_1[84];
- u8 oem_contact_2[84];
- u8 oem_contact_3[84];
-} __attribute__((packed)) gdth_oem_str;
-
-typedef struct {
- gdth_oem_str_params params;
- gdth_oem_str text;
-} __attribute__((packed)) gdth_oem_str_ioctl;
-
-/* board features */
-typedef struct {
- u8 chaining; /* Chaining supported */
- u8 striping; /* Striping (RAID-0) supp. */
- u8 mirroring; /* Mirroring (RAID-1) supp. */
- u8 raid; /* RAID-4/5/10 supported */
-} __attribute__((packed)) gdth_bfeat_str;
-
-/* board info IOCTL */
-typedef struct {
- u32 ser_no; /* serial no. */
- u8 oem_id[2]; /* OEM ID */
- u16 ep_flags; /* eprom flags */
- u32 proc_id; /* processor ID */
- u32 memsize; /* memory size (bytes) */
- u8 mem_banks; /* memory banks */
- u8 chan_type; /* channel type */
- u8 chan_count; /* channel count */
- u8 rdongle_pres; /* dongle present? */
- u32 epr_fw_ver; /* (eprom) firmware version */
- u32 upd_fw_ver; /* (update) firmware version */
- u32 upd_revision; /* update revision */
- char type_string[16]; /* controller name */
- char raid_string[16]; /* RAID firmware name */
- u8 update_pres; /* update present? */
- u8 xor_pres; /* XOR engine present? */
- u8 prom_type; /* ROM type (eprom/flash) */
- u8 prom_count; /* number of ROM devices */
- u32 dup_pres; /* duplexing module present? */
- u32 chan_pres; /* number of expansion chn. */
- u32 mem_pres; /* memory expansion inst. ? */
- u8 ft_bus_system; /* fault bus supported? */
- u8 subtype_valid; /* board_subtype valid? */
- u8 board_subtype; /* subtype/hardware level */
- u8 ramparity_pres; /* RAM parity check hardware? */
-} __attribute__((packed)) gdth_binfo_str;
-
-/* get host drive info */
-typedef struct {
- char name[8]; /* host drive name */
- u32 size; /* size (sectors) */
- u8 host_drive; /* host drive number */
- u8 log_drive; /* log. drive (master) */
- u8 reserved;
- u8 rw_attribs; /* r/w attribs */
- u32 start_sec; /* start sector */
-} __attribute__((packed)) gdth_hentry_str;
-
-typedef struct {
- u32 entries; /* entry count */
- u32 offset; /* offset of entries */
- u8 secs_p_head; /* sectors/head */
- u8 heads_p_cyl; /* heads/cylinder */
- u8 reserved;
- u8 clust_drvtype; /* cluster drive type */
- u32 location; /* controller number */
- gdth_hentry_str entry[MAX_HDRIVES]; /* entries */
-} __attribute__((packed)) gdth_hget_str;
-
-
-/* DPRAM structures */
-
-/* interface area ISA/PCI */
-typedef struct {
- u8 S_Cmd_Indx; /* special command */
- u8 volatile S_Status; /* status special command */
- u16 reserved1;
- u32 S_Info[4]; /* add. info special command */
- u8 volatile Sema0; /* command semaphore */
- u8 reserved2[3];
- u8 Cmd_Index; /* command number */
- u8 reserved3[3];
- u16 volatile Status; /* command status */
- u16 Service; /* service(for async.events) */
- u32 Info[2]; /* additional info */
- struct {
- u16 offset; /* command offs. in the DPRAM*/
- u16 serv_id; /* service */
- } __attribute__((packed)) comm_queue[MAXOFFSETS]; /* command queue */
- u32 bios_reserved[2];
- u8 gdt_dpr_cmd[1]; /* commands */
-} __attribute__((packed)) gdt_dpr_if;
-
-/* SRAM structure PCI controllers */
-typedef struct {
- u32 magic; /* controller ID from BIOS */
- u16 need_deinit; /* switch betw. BIOS/driver */
- u8 switch_support; /* see need_deinit */
- u8 padding[9];
- u8 os_used[16]; /* OS code per service */
- u8 unused[28];
- u8 fw_magic; /* contr. ID from firmware */
-} __attribute__((packed)) gdt_pci_sram;
-
-/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
-typedef struct {
- u8 os_used[16]; /* OS code per service */
- u16 need_deinit; /* switch betw. BIOS/driver */
- u8 switch_support; /* see need_deinit */
- u8 padding;
-} __attribute__((packed)) gdt_eisa_sram;
-
-
-/* DPRAM ISA controllers */
-typedef struct {
- union {
- struct {
- u8 bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
- u32 magic; /* controller (EISA) ID */
- u16 need_deinit; /* switch betw. BIOS/driver */
- u8 switch_support; /* see need_deinit */
- u8 padding[9];
- u8 os_used[16]; /* OS code per service */
- } __attribute__((packed)) dp_sram;
- u8 bios_area[0x4000]; /* 16KB reserved for BIOS */
- } bu;
- union {
- gdt_dpr_if ic; /* interface area */
- u8 if_area[0x3000]; /* 12KB for interface */
- } u;
- struct {
- u8 memlock; /* write protection DPRAM */
- u8 event; /* release event */
- u8 irqen; /* board interrupts enable */
- u8 irqdel; /* acknowledge board int. */
- u8 volatile Sema1; /* status semaphore */
- u8 rq; /* IRQ/DRQ configuration */
- } __attribute__((packed)) io;
-} __attribute__((packed)) gdt2_dpram_str;
-
-/* DPRAM PCI controllers */
-typedef struct {
- union {
- gdt_dpr_if ic; /* interface area */
- u8 if_area[0xff0-sizeof(gdt_pci_sram)];
- } u;
- gdt_pci_sram gdt6sr; /* SRAM structure */
- struct {
- u8 unused0[1];
- u8 volatile Sema1; /* command semaphore */
- u8 unused1[3];
- u8 irqen; /* board interrupts enable */
- u8 unused2[2];
- u8 event; /* release event */
- u8 unused3[3];
- u8 irqdel; /* acknowledge board int. */
- u8 unused4[3];
- } __attribute__((packed)) io;
-} __attribute__((packed)) gdt6_dpram_str;
-
-/* PLX register structure (new PCI controllers) */
-typedef struct {
- u8 cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
- u8 unused1[0x3f];
- u8 volatile sema0_reg; /* command semaphore */
- u8 volatile sema1_reg; /* status semaphore */
- u8 unused2[2];
- u16 volatile status; /* command status */
- u16 service; /* service */
- u32 info[2]; /* additional info */
- u8 unused3[0x10];
- u8 ldoor_reg; /* PCI to local doorbell */
- u8 unused4[3];
- u8 volatile edoor_reg; /* local to PCI doorbell */
- u8 unused5[3];
- u8 control0; /* control0 register(unused) */
- u8 control1; /* board interrupts enable */
- u8 unused6[0x16];
-} __attribute__((packed)) gdt6c_plx_regs;
-
-/* DPRAM new PCI controllers */
-typedef struct {
- union {
- gdt_dpr_if ic; /* interface area */
- u8 if_area[0x4000-sizeof(gdt_pci_sram)];
- } u;
- gdt_pci_sram gdt6sr; /* SRAM structure */
-} __attribute__((packed)) gdt6c_dpram_str;
-
-/* i960 register structure (PCI MPR controllers) */
-typedef struct {
- u8 unused1[16];
- u8 volatile sema0_reg; /* command semaphore */
- u8 unused2;
- u8 volatile sema1_reg; /* status semaphore */
- u8 unused3;
- u16 volatile status; /* command status */
- u16 service; /* service */
- u32 info[2]; /* additional info */
- u8 ldoor_reg; /* PCI to local doorbell */
- u8 unused4[11];
- u8 volatile edoor_reg; /* local to PCI doorbell */
- u8 unused5[7];
- u8 edoor_en_reg; /* board interrupts enable */
- u8 unused6[27];
- u32 unused7[939];
- u32 severity;
- char evt_str[256]; /* event string */
-} __attribute__((packed)) gdt6m_i960_regs;
-
-/* DPRAM PCI MPR controllers */
-typedef struct {
- gdt6m_i960_regs i960r; /* 4KB i960 registers */
- union {
- gdt_dpr_if ic; /* interface area */
- u8 if_area[0x3000-sizeof(gdt_pci_sram)];
- } u;
- gdt_pci_sram gdt6sr; /* SRAM structure */
-} __attribute__((packed)) gdt6m_dpram_str;
-
-
-/* PCI resources */
-typedef struct {
- struct pci_dev *pdev;
- unsigned long dpmem; /* DPRAM address */
- unsigned long io; /* IO address */
-} gdth_pci_str;
-
-
-/* controller information structure */
-typedef struct {
- struct Scsi_Host *shost;
- struct list_head list;
- u16 hanum;
- u16 oem_id; /* OEM */
- u16 type; /* controller class */
- u32 stype; /* subtype (PCI: device ID) */
- u16 fw_vers; /* firmware version */
- u16 cache_feat; /* feat. cache serv. (s/g,..)*/
- u16 raw_feat; /* feat. raw service (s/g,..)*/
- u16 screen_feat; /* feat. raw service (s/g,..)*/
- u16 bmic; /* BMIC address (EISA) */
- void __iomem *brd; /* DPRAM address */
- u32 brd_phys; /* slot number/BIOS address */
- gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */
- gdth_cmd_str cmdext;
- gdth_cmd_str *pccb; /* address command structure */
- u32 ccb_phys; /* phys. address */
-#ifdef INT_COAL
- gdth_coal_status *coal_stat; /* buffer for coalescing int.*/
- u64 coal_stat_phys; /* phys. address */
-#endif
- char *pscratch; /* scratch (DMA) buffer */
- u64 scratch_phys; /* phys. address */
- u8 scratch_busy; /* in use? */
- u8 dma64_support; /* 64-bit DMA supported? */
- gdth_msg_str *pmsg; /* message buffer */
- u64 msg_phys; /* phys. address */
- u8 scan_mode; /* current scan mode */
- u8 irq; /* IRQ */
- u8 drq; /* DRQ (ISA controllers) */
- u16 status; /* command status */
- u16 service; /* service/firmware ver./.. */
- u32 info;
- u32 info2; /* additional info */
- Scsi_Cmnd *req_first; /* top of request queue */
- struct {
- u8 present; /* Flag: host drive present? */
- u8 is_logdrv; /* Flag: log. drive (master)? */
- u8 is_arraydrv; /* Flag: array drive? */
- u8 is_master; /* Flag: array drive master? */
- u8 is_parity; /* Flag: parity drive? */
- u8 is_hotfix; /* Flag: hotfix drive? */
- u8 master_no; /* number of master drive */
- u8 lock; /* drive locked? (hot plug) */
- u8 heads; /* mapping */
- u8 secs;
- u16 devtype; /* further information */
- u64 size; /* capacity */
- u8 ldr_no; /* log. drive no. */
- u8 rw_attribs; /* r/w attributes */
- u8 cluster_type; /* cluster properties */
- u8 media_changed; /* Flag:MOUNT/UNMOUNT occurred */
- u32 start_sec; /* start sector */
- } hdr[MAX_LDRIVES]; /* host drives */
- struct {
- u8 lock; /* channel locked? (hot plug) */
- u8 pdev_cnt; /* physical device count */
- u8 local_no; /* local channel number */
- u8 io_cnt[MAXID]; /* current IO count */
- u32 address; /* channel address */
- u32 id_list[MAXID]; /* IDs of the phys. devices */
- } raw[MAXBUS]; /* SCSI channels */
- struct {
- Scsi_Cmnd *cmnd; /* pending request */
- u16 service; /* service */
- } cmd_tab[GDTH_MAXCMDS]; /* table of pend. requests */
- struct gdth_cmndinfo { /* per-command private info */
- int index;
- int internal_command; /* don't call scsi_done */
- gdth_cmd_str *internal_cmd_str; /* crier for internal messages*/
- dma_addr_t sense_paddr; /* sense dma-addr */
- u8 priority;
- int timeout_count; /* # of timeout calls */
- volatile int wait_for_completion;
- u16 status;
- u32 info;
- enum dma_data_direction dma_dir;
- int phase; /* ???? */
- int OpCode;
- } cmndinfo[GDTH_MAXCMDS]; /* index==0 is free */
- u8 bus_cnt; /* SCSI bus count */
- u8 tid_cnt; /* Target ID count */
- u8 bus_id[MAXBUS]; /* IOP IDs */
- u8 virt_bus; /* number of virtual bus */
- u8 more_proc; /* more /proc info supported */
- u16 cmd_cnt; /* command count in DPRAM */
- u16 cmd_len; /* length of actual command */
- u16 cmd_offs_dpmem; /* actual offset in DPRAM */
- u16 ic_all_size; /* sizeof DPRAM interf. area */
- gdth_cpar_str cpar; /* controller cache par. */
- gdth_bfeat_str bfeat; /* controller features */
- gdth_binfo_str binfo; /* controller info */
- gdth_evt_data dvr; /* event structure */
- spinlock_t smp_lock;
- struct pci_dev *pdev;
- char oem_name[8];
-#ifdef GDTH_DMA_STATISTICS
- unsigned long dma32_cnt, dma64_cnt; /* statistics: DMA buffer */
-#endif
- struct scsi_device *sdev;
-} gdth_ha_str;
-
-static inline struct gdth_cmndinfo *gdth_cmnd_priv(struct scsi_cmnd* cmd)
-{
- return (struct gdth_cmndinfo *)cmd->host_scribble;
-}
-
-/* INQUIRY data format */
-typedef struct {
- u8 type_qual;
- u8 modif_rmb;
- u8 version;
- u8 resp_aenc;
- u8 add_length;
- u8 reserved1;
- u8 reserved2;
- u8 misc;
- u8 vendor[8];
- u8 product[16];
- u8 revision[4];
-} __attribute__((packed)) gdth_inq_data;
-
-/* READ_CAPACITY data format */
-typedef struct {
- u32 last_block_no;
- u32 block_length;
-} __attribute__((packed)) gdth_rdcap_data;
-
-/* READ_CAPACITY (16) data format */
-typedef struct {
- u64 last_block_no;
- u32 block_length;
-} __attribute__((packed)) gdth_rdcap16_data;
-
-/* REQUEST_SENSE data format */
-typedef struct {
- u8 errorcode;
- u8 segno;
- u8 key;
- u32 info;
- u8 add_length;
- u32 cmd_info;
- u8 adsc;
- u8 adsq;
- u8 fruc;
- u8 key_spec[3];
-} __attribute__((packed)) gdth_sense_data;
-
-/* MODE_SENSE data format */
-typedef struct {
- struct {
- u8 data_length;
- u8 med_type;
- u8 dev_par;
- u8 bd_length;
- } __attribute__((packed)) hd;
- struct {
- u8 dens_code;
- u8 block_count[3];
- u8 reserved;
- u8 block_length[3];
- } __attribute__((packed)) bd;
-} __attribute__((packed)) gdth_modep_data;
-
-/* stack frame */
-typedef struct {
- unsigned long b[10]; /* 32/64 bit compiler ! */
-} __attribute__((packed)) gdth_stackframe;
-
-
-/* function prototyping */
-
-int gdth_proc_info(struct Scsi_Host *, char *,char **,off_t,int,int);
-
-#endif
diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h
deleted file mode 100644
index b004c6165887..000000000000
--- a/drivers/scsi/gdth_ioctl.h
+++ /dev/null
@@ -1,339 +0,0 @@
-#ifndef _GDTH_IOCTL_H
-#define _GDTH_IOCTL_H
-
-/* gdth_ioctl.h
- * $Id: gdth_ioctl.h,v 1.14 2004/02/19 15:43:15 achim Exp $
- */
-
-/* IOCTLs */
-#define GDTIOCTL_MASK ('J'<<8)
-#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */
-#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */
-#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */
-#define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */
-#define GDTIOCTL_HDRLIST (GDTIOCTL_MASK | 4) /* get host drive list */
-#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */
-#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */
-#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */
-#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */
-#define GDTIOCTL_SCSI (GDTIOCTL_MASK | 9) /* SCSI command */
-#define GDTIOCTL_RESET_BUS (GDTIOCTL_MASK |10) /* reset SCSI bus */
-#define GDTIOCTL_RESCAN (GDTIOCTL_MASK |11) /* rescan host drives */
-#define GDTIOCTL_RESET_DRV (GDTIOCTL_MASK |12) /* reset (remote) drv. res. */
-
-#define GDTIOCTL_MAGIC 0xaffe0004
-#define EVENT_SIZE 294
-#define GDTH_MAXSG 32 /* max. s/g elements */
-
-#define MAX_LDRIVES 255 /* max. log. drive count */
-#ifdef GDTH_IOCTL_PROC
-#define MAX_HDRIVES 100 /* max. host drive count */
-#else
-#define MAX_HDRIVES MAX_LDRIVES /* max. host drive count */
-#endif
-
-/* scatter/gather element */
-typedef struct {
- u32 sg_ptr; /* address */
- u32 sg_len; /* length */
-} __attribute__((packed)) gdth_sg_str;
-
-/* scatter/gather element - 64bit addresses */
-typedef struct {
- u64 sg_ptr; /* address */
- u32 sg_len; /* length */
-} __attribute__((packed)) gdth_sg64_str;
-
-/* command structure */
-typedef struct {
- u32 BoardNode; /* board node (always 0) */
- u32 CommandIndex; /* command number */
- u16 OpCode; /* the command (READ,..) */
- union {
- struct {
- u16 DeviceNo; /* number of cache drive */
- u32 BlockNo; /* block number */
- u32 BlockCnt; /* block count */
- u32 DestAddr; /* dest. addr. (if s/g: -1) */
- u32 sg_canz; /* s/g element count */
- gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } __attribute__((packed)) cache; /* cache service cmd. str. */
- struct {
- u16 DeviceNo; /* number of cache drive */
- u64 BlockNo; /* block number */
- u32 BlockCnt; /* block count */
- u64 DestAddr; /* dest. addr. (if s/g: -1) */
- u32 sg_canz; /* s/g element count */
- gdth_sg64_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } __attribute__((packed)) cache64; /* cache service cmd. str. */
- struct {
- u16 param_size; /* size of p_param buffer */
- u32 subfunc; /* IOCTL function */
- u32 channel; /* device */
- u64 p_param; /* buffer */
- } __attribute__((packed)) ioctl; /* IOCTL command structure */
- struct {
- u16 reserved;
- union {
- struct {
- u32 msg_handle; /* message handle */
- u64 msg_addr; /* message buffer address */
- } __attribute__((packed)) msg;
- u8 data[12]; /* buffer for rtc data, ... */
- } su;
- } __attribute__((packed)) screen; /* screen service cmd. str. */
- struct {
- u16 reserved;
- u32 direction; /* data direction */
- u32 mdisc_time; /* disc. time (0: no timeout)*/
- u32 mcon_time; /* connect time(0: no to.) */
- u32 sdata; /* dest. addr. (if s/g: -1) */
- u32 sdlen; /* data length (bytes) */
- u32 clen; /* SCSI cmd. length(6,10,12) */
- u8 cmd[12]; /* SCSI command */
- u8 target; /* target ID */
- u8 lun; /* LUN */
- u8 bus; /* SCSI bus number */
- u8 priority; /* only 0 used */
- u32 sense_len; /* sense data length */
- u32 sense_data; /* sense data addr. */
- u32 link_p; /* linked cmds (not supp.) */
- u32 sg_ranz; /* s/g element count */
- gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } __attribute__((packed)) raw; /* raw service cmd. struct. */
- struct {
- u16 reserved;
- u32 direction; /* data direction */
- u32 mdisc_time; /* disc. time (0: no timeout)*/
- u32 mcon_time; /* connect time(0: no to.) */
- u64 sdata; /* dest. addr. (if s/g: -1) */
- u32 sdlen; /* data length (bytes) */
- u32 clen; /* SCSI cmd. length(6,..,16) */
- u8 cmd[16]; /* SCSI command */
- u8 target; /* target ID */
- u8 lun; /* LUN */
- u8 bus; /* SCSI bus number */
- u8 priority; /* only 0 used */
- u32 sense_len; /* sense data length */
- u64 sense_data; /* sense data addr. */
- u32 sg_ranz; /* s/g element count */
- gdth_sg64_str sg_lst[GDTH_MAXSG]; /* s/g list */
- } __attribute__((packed)) raw64; /* raw service cmd. struct. */
- } u;
- /* additional variables */
- u8 Service; /* controller service */
- u8 reserved;
- u16 Status; /* command result */
- u32 Info; /* additional information */
- void *RequestBuffer; /* request buffer */
-} __attribute__((packed)) gdth_cmd_str;
-
-/* controller event structure */
-#define ES_ASYNC 1
-#define ES_DRIVER 2
-#define ES_TEST 3
-#define ES_SYNC 4
-typedef struct {
- u16 size; /* size of structure */
- union {
- char stream[16];
- struct {
- u16 ionode;
- u16 service;
- u32 index;
- } __attribute__((packed)) driver;
- struct {
- u16 ionode;
- u16 service;
- u16 status;
- u32 info;
- u8 scsi_coord[3];
- } __attribute__((packed)) async;
- struct {
- u16 ionode;
- u16 service;
- u16 status;
- u32 info;
- u16 hostdrive;
- u8 scsi_coord[3];
- u8 sense_key;
- } __attribute__((packed)) sync;
- struct {
- u32 l1, l2, l3, l4;
- } __attribute__((packed)) test;
- } eu;
- u32 severity;
- u8 event_string[256];
-} __attribute__((packed)) gdth_evt_data;
-
-typedef struct {
- u32 first_stamp;
- u32 last_stamp;
- u16 same_count;
- u16 event_source;
- u16 event_idx;
- u8 application;
- u8 reserved;
- gdth_evt_data event_data;
-} __attribute__((packed)) gdth_evt_str;
-
-
-#ifdef GDTH_IOCTL_PROC
-/* IOCTL structure (write) */
-typedef struct {
- u32 magic; /* IOCTL magic */
- u16 ioctl; /* IOCTL */
- u16 ionode; /* controller number */
- u16 service; /* controller service */
- u16 timeout; /* timeout */
- union {
- struct {
- u8 command[512]; /* controller command */
- u8 data[1]; /* add. data */
- } general;
- struct {
- u8 lock; /* lock/unlock */
- u8 drive_cnt; /* drive count */
- u16 drives[MAX_HDRIVES];/* drives */
- } lockdrv;
- struct {
- u8 lock; /* lock/unlock */
- u8 channel; /* channel */
- } lockchn;
- struct {
- int erase; /* erase event ? */
- int handle;
- u8 evt[EVENT_SIZE]; /* event structure */
- } event;
- struct {
- u8 bus; /* SCSI bus */
- u8 target; /* target ID */
- u8 lun; /* LUN */
- u8 cmd_len; /* command length */
- u8 cmd[12]; /* SCSI command */
- } scsi;
- struct {
- u16 hdr_no; /* host drive number */
- u8 flag; /* old meth./add/remove */
- } rescan;
- } iu;
-} gdth_iowr_str;
-
-/* IOCTL structure (read) */
-typedef struct {
- u32 size; /* buffer size */
- u32 status; /* IOCTL error code */
- union {
- struct {
- u8 data[1]; /* data */
- } general;
- struct {
- u16 version; /* driver version */
- } drvers;
- struct {
- u8 type; /* controller type */
- u16 info; /* slot etc. */
- u16 oem_id; /* OEM ID */
- u16 bios_ver; /* not used */
- u16 access; /* not used */
- u16 ext_type; /* extended type */
- u16 device_id; /* device ID */
- u16 sub_device_id; /* sub device ID */
- } ctrtype;
- struct {
- u8 version; /* OS version */
- u8 subversion; /* OS subversion */
- u16 revision; /* revision */
- } osvers;
- struct {
- u16 count; /* controller count */
- } ctrcnt;
- struct {
- int handle;
- u8 evt[EVENT_SIZE]; /* event structure */
- } event;
- struct {
- u8 bus; /* SCSI bus, 0xff: invalid */
- u8 target; /* target ID */
- u8 lun; /* LUN */
- u8 cluster_type; /* cluster properties */
- } hdr_list[MAX_HDRIVES]; /* index is host drive number */
- } iu;
-} gdth_iord_str;
-#endif
-
-/* GDTIOCTL_GENERAL */
-typedef struct {
- u16 ionode; /* controller number */
- u16 timeout; /* timeout */
- u32 info; /* error info */
- u16 status; /* status */
- unsigned long data_len; /* data buffer size */
- unsigned long sense_len; /* sense buffer size */
- gdth_cmd_str command; /* command */
-} gdth_ioctl_general;
-
-/* GDTIOCTL_LOCKDRV */
-typedef struct {
- u16 ionode; /* controller number */
- u8 lock; /* lock/unlock */
- u8 drive_cnt; /* drive count */
- u16 drives[MAX_HDRIVES]; /* drives */
-} gdth_ioctl_lockdrv;
-
-/* GDTIOCTL_LOCKCHN */
-typedef struct {
- u16 ionode; /* controller number */
- u8 lock; /* lock/unlock */
- u8 channel; /* channel */
-} gdth_ioctl_lockchn;
-
-/* GDTIOCTL_OSVERS */
-typedef struct {
- u8 version; /* OS version */
- u8 subversion; /* OS subversion */
- u16 revision; /* revision */
-} gdth_ioctl_osvers;
-
-/* GDTIOCTL_CTRTYPE */
-typedef struct {
- u16 ionode; /* controller number */
- u8 type; /* controller type */
- u16 info; /* slot etc. */
- u16 oem_id; /* OEM ID */
- u16 bios_ver; /* not used */
- u16 access; /* not used */
- u16 ext_type; /* extended type */
- u16 device_id; /* device ID */
- u16 sub_device_id; /* sub device ID */
-} gdth_ioctl_ctrtype;
-
-/* GDTIOCTL_EVENT */
-typedef struct {
- u16 ionode;
- int erase; /* erase event? */
- int handle; /* event handle */
- gdth_evt_str event;
-} gdth_ioctl_event;
-
-/* GDTIOCTL_RESCAN/GDTIOCTL_HDRLIST */
-typedef struct {
- u16 ionode; /* controller number */
- u8 flag; /* add/remove */
- u16 hdr_no; /* drive no. */
- struct {
- u8 bus; /* SCSI bus */
- u8 target; /* target ID */
- u8 lun; /* LUN */
- u8 cluster_type; /* cluster properties */
- } hdr_list[MAX_HDRIVES]; /* index is host drive number */
-} gdth_ioctl_rescan;
-
-/* GDTIOCTL_RESET_BUS/GDTIOCTL_RESET_DRV */
-typedef struct {
- u16 ionode; /* controller number */
- u16 number; /* bus/host drive number */
- u16 status; /* status */
-} gdth_ioctl_reset;
-
-#endif
diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
deleted file mode 100644
index 652754319a4b..000000000000
--- a/drivers/scsi/gdth_proc.c
+++ /dev/null
@@ -1,760 +0,0 @@
-/* gdth_proc.c
- * $Id: gdth_proc.c,v 1.43 2006/01/11 16:15:00 achim Exp $
- */
-
-#include <linux/completion.h>
-#include <linux/slab.h>
-
-int gdth_proc_info(struct Scsi_Host *host, char *buffer,char **start,off_t offset,int length,
- int inout)
-{
- gdth_ha_str *ha = shost_priv(host);
-
- TRACE2(("gdth_proc_info() length %d offs %d inout %d\n",
- length,(int)offset,inout));
-
- if (inout)
- return(gdth_set_info(buffer,length,host,ha));
- else
- return(gdth_get_info(buffer,start,offset,length,host,ha));
-}
-
-static int gdth_set_info(char *buffer,int length,struct Scsi_Host *host,
- gdth_ha_str *ha)
-{
- int ret_val = -EINVAL;
-
- TRACE2(("gdth_set_info() ha %d\n",ha->hanum,));
-
- if (length >= 4) {
- if (strncmp(buffer,"gdth",4) == 0) {
- buffer += 5;
- length -= 5;
- ret_val = gdth_set_asc_info(host, buffer, length, ha);
- }
- }
-
- return ret_val;
-}
-
-static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
- int length, gdth_ha_str *ha)
-{
- int orig_length, drive, wb_mode;
- int i, found;
- gdth_cmd_str gdtcmd;
- gdth_cpar_str *pcpar;
- u64 paddr;
-
- char cmnd[MAX_COMMAND_SIZE];
- memset(cmnd, 0xff, 12);
- memset(&gdtcmd, 0, sizeof(gdth_cmd_str));
-
- TRACE2(("gdth_set_asc_info() ha %d\n",ha->hanum));
- orig_length = length + 5;
- drive = -1;
- wb_mode = 0;
- found = FALSE;
-
- if (length >= 5 && strncmp(buffer,"flush",5)==0) {
- buffer += 6;
- length -= 6;
- if (length && *buffer>='0' && *buffer<='9') {
- drive = (int)(*buffer-'0');
- ++buffer; --length;
- if (length && *buffer>='0' && *buffer<='9') {
- drive = drive*10 + (int)(*buffer-'0');
- ++buffer; --length;
- }
- printk("GDT: Flushing host drive %d .. ",drive);
- } else {
- printk("GDT: Flushing all host drives .. ");
- }
- for (i = 0; i < MAX_HDRIVES; ++i) {
- if (ha->hdr[i].present) {
- if (drive != -1 && i != drive)
- continue;
- found = TRUE;
- gdtcmd.Service = CACHESERVICE;
- gdtcmd.OpCode = GDT_FLUSH;
- if (ha->cache_feat & GDT_64BIT) {
- gdtcmd.u.cache64.DeviceNo = i;
- gdtcmd.u.cache64.BlockNo = 1;
- } else {
- gdtcmd.u.cache.DeviceNo = i;
- gdtcmd.u.cache.BlockNo = 1;
- }
-
- gdth_execute(host, &gdtcmd, cmnd, 30, NULL);
- }
- }
- if (!found)
- printk("\nNo host drive found !\n");
- else
- printk("Done.\n");
- return(orig_length);
- }
-
- if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) {
- buffer += 8;
- length -= 8;
- printk("GDT: Disabling write back permanently .. ");
- wb_mode = 1;
- } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) {
- buffer += 7;
- length -= 7;
- printk("GDT: Enabling write back permanently .. ");
- wb_mode = 2;
- } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) {
- buffer += 7;
- length -= 7;
- printk("GDT: Disabling write back commands .. ");
- if (ha->cache_feat & GDT_WR_THROUGH) {
- gdth_write_through = TRUE;
- printk("Done.\n");
- } else {
- printk("Not supported !\n");
- }
- return(orig_length);
- } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) {
- buffer += 6;
- length -= 6;
- printk("GDT: Enabling write back commands .. ");
- gdth_write_through = FALSE;
- printk("Done.\n");
- return(orig_length);
- }
-
- if (wb_mode) {
- if (!gdth_ioctl_alloc(ha, sizeof(gdth_cpar_str), TRUE, &paddr))
- return(-EBUSY);
- pcpar = (gdth_cpar_str *)ha->pscratch;
- memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
- gdtcmd.Service = CACHESERVICE;
- gdtcmd.OpCode = GDT_IOCTL;
- gdtcmd.u.ioctl.p_param = paddr;
- gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
- gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
- gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
- pcpar->write_back = wb_mode==1 ? 0:1;
-
- gdth_execute(host, &gdtcmd, cmnd, 30, NULL);
-
- gdth_ioctl_free(ha, GDTH_SCRATCH, ha->pscratch, paddr);
- printk("Done.\n");
- return(orig_length);
- }
-
- printk("GDT: Unknown command: %s Length: %d\n",buffer,length);
- return(-EINVAL);
-}
-
-static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
- struct Scsi_Host *host, gdth_ha_str *ha)
-{
- int size = 0,len = 0;
- int hlen;
- off_t begin = 0,pos = 0;
- int id, i, j, k, sec, flag;
- int no_mdrv = 0, drv_no, is_mirr;
- u32 cnt;
- u64 paddr;
- int rc = -ENOMEM;
-
- gdth_cmd_str *gdtcmd;
- gdth_evt_str *estr;
- char hrec[161];
- struct timeval tv;
-
- char *buf;
- gdth_dskstat_str *pds;
- gdth_diskinfo_str *pdi;
- gdth_arrayinf_str *pai;
- gdth_defcnt_str *pdef;
- gdth_cdrinfo_str *pcdi;
- gdth_hget_str *phg;
- char cmnd[MAX_COMMAND_SIZE];
-
- gdtcmd = kmalloc(sizeof(*gdtcmd), GFP_KERNEL);
- estr = kmalloc(sizeof(*estr), GFP_KERNEL);
- if (!gdtcmd || !estr)
- goto free_fail;
-
- memset(cmnd, 0xff, 12);
- memset(gdtcmd, 0, sizeof(gdth_cmd_str));
-
- TRACE2(("gdth_get_info() ha %d\n",ha->hanum));
-
-
- /* request is i.e. "cat /proc/scsi/gdth/0" */
- /* format: %-15s\t%-10s\t%-15s\t%s */
- /* driver parameters */
- size = sprintf(buffer+len,"Driver Parameters:\n");
- len += size; pos = begin + len;
- if (reserve_list[0] == 0xff)
- strcpy(hrec, "--");
- else {
- hlen = sprintf(hrec, "%d", reserve_list[0]);
- for (i = 1; i < MAX_RES_ARGS; i++) {
- if (reserve_list[i] == 0xff)
- break;
- hlen += snprintf(hrec + hlen , 161 - hlen, ",%d", reserve_list[i]);
- }
- }
- size = sprintf(buffer+len,
- " reserve_mode: \t%d \treserve_list: \t%s\n",
- reserve_mode, hrec);
- len += size; pos = begin + len;
- size = sprintf(buffer+len,
- " max_ids: \t%-3d \thdr_channel: \t%d\n",
- max_ids, hdr_channel);
- len += size; pos = begin + len;
-
- /* controller information */
- size = sprintf(buffer+len,"\nDisk Array Controller Information:\n");
- len += size; pos = begin + len;
- strcpy(hrec, ha->binfo.type_string);
- size = sprintf(buffer+len,
- " Number: \t%d \tName: \t%s\n",
- ha->hanum, hrec);
- len += size; pos = begin + len;
-
- if (ha->more_proc)
- sprintf(hrec, "%d.%02d.%02d-%c%03X",
- (u8)(ha->binfo.upd_fw_ver>>24),
- (u8)(ha->binfo.upd_fw_ver>>16),
- (u8)(ha->binfo.upd_fw_ver),
- ha->bfeat.raid ? 'R':'N',
- ha->binfo.upd_revision);
- else
- sprintf(hrec, "%d.%02d", (u8)(ha->cpar.version>>8),
- (u8)(ha->cpar.version));
-
- size = sprintf(buffer+len,
- " Driver Ver.: \t%-10s\tFirmware Ver.: \t%s\n",
- GDTH_VERSION_STR, hrec);
- len += size; pos = begin + len;
-
- if (ha->more_proc) {
- /* more information: 1. about controller */
- size = sprintf(buffer+len,
- " Serial No.: \t0x%8X\tCache RAM size:\t%d KB\n",
- ha->binfo.ser_no, ha->binfo.memsize / 1024);
- len += size; pos = begin + len;
- }
-
-#ifdef GDTH_DMA_STATISTICS
- /* controller statistics */
- size = sprintf(buffer+len,"\nController Statistics:\n");
- len += size; pos = begin + len;
- size = sprintf(buffer+len,
- " 32-bit DMA buffer:\t%lu\t64-bit DMA buffer:\t%lu\n",
- ha->dma32_cnt, ha->dma64_cnt);
- len += size; pos = begin + len;
-#endif
-
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length)
- goto stop_output;
-
- if (ha->more_proc) {
- /* more information: 2. about physical devices */
- size = sprintf(buffer+len,"\nPhysical Devices:");
- len += size; pos = begin + len;
- flag = FALSE;
-
- buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
- if (!buf)
- goto stop_output;
- for (i = 0; i < ha->bus_cnt; ++i) {
- /* 2.a statistics (and retries/reassigns) */
- TRACE2(("pdr_statistics() chn %d\n",i));
- pds = (gdth_dskstat_str *)(buf + GDTH_SCRATCH/4);
- gdtcmd->Service = CACHESERVICE;
- gdtcmd->OpCode = GDT_IOCTL;
- gdtcmd->u.ioctl.p_param = paddr + GDTH_SCRATCH/4;
- gdtcmd->u.ioctl.param_size = 3*GDTH_SCRATCH/4;
- gdtcmd->u.ioctl.subfunc = DSK_STATISTICS | L_CTRL_PATTERN;
- gdtcmd->u.ioctl.channel = ha->raw[i].address | INVALID_CHANNEL;
- pds->bid = ha->raw[i].local_no;
- pds->first = 0;
- pds->entries = ha->raw[i].pdev_cnt;
- cnt = (3*GDTH_SCRATCH/4 - 5 * sizeof(u32)) /
- sizeof(pds->list[0]);
- if (pds->entries > cnt)
- pds->entries = cnt;
-
- if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) != S_OK)
- pds->count = 0;
-
- /* other IOCTLs must fit into area GDTH_SCRATCH/4 */
- for (j = 0; j < ha->raw[i].pdev_cnt; ++j) {
- /* 2.b drive info */
- TRACE2(("scsi_drv_info() chn %d dev %d\n",
- i, ha->raw[i].id_list[j]));
- pdi = (gdth_diskinfo_str *)buf;
- gdtcmd->Service = CACHESERVICE;
- gdtcmd->OpCode = GDT_IOCTL;
- gdtcmd->u.ioctl.p_param = paddr;
- gdtcmd->u.ioctl.param_size = sizeof(gdth_diskinfo_str);
- gdtcmd->u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN;
- gdtcmd->u.ioctl.channel =
- ha->raw[i].address | ha->raw[i].id_list[j];
-
- if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
- strncpy(hrec,pdi->vendor,8);
- strncpy(hrec+8,pdi->product,16);
- strncpy(hrec+24,pdi->revision,4);
- hrec[28] = 0;
- size = sprintf(buffer+len,
- "\n Chn/ID/LUN: \t%c/%02d/%d \tName: \t%s\n",
- 'A'+i,pdi->target_id,pdi->lun,hrec);
- len += size; pos = begin + len;
- flag = TRUE;
- pdi->no_ldrive &= 0xffff;
- if (pdi->no_ldrive == 0xffff)
- strcpy(hrec,"--");
- else
- sprintf(hrec,"%d",pdi->no_ldrive);
- size = sprintf(buffer+len,
- " Capacity [MB]:\t%-6d \tTo Log. Drive: \t%s\n",
- pdi->blkcnt/(1024*1024/pdi->blksize),
- hrec);
- len += size; pos = begin + len;
- } else {
- pdi->devtype = 0xff;
- }
-
- if (pdi->devtype == 0) {
- /* search retries/reassigns */
- for (k = 0; k < pds->count; ++k) {
- if (pds->list[k].tid == pdi->target_id &&
- pds->list[k].lun == pdi->lun) {
- size = sprintf(buffer+len,
- " Retries: \t%-6d \tReassigns: \t%d\n",
- pds->list[k].retries,
- pds->list[k].reassigns);
- len += size; pos = begin + len;
- break;
- }
- }
- /* 2.c grown defects */
- TRACE2(("scsi_drv_defcnt() chn %d dev %d\n",
- i, ha->raw[i].id_list[j]));
- pdef = (gdth_defcnt_str *)buf;
- gdtcmd->Service = CACHESERVICE;
- gdtcmd->OpCode = GDT_IOCTL;
- gdtcmd->u.ioctl.p_param = paddr;
- gdtcmd->u.ioctl.param_size = sizeof(gdth_defcnt_str);
- gdtcmd->u.ioctl.subfunc = SCSI_DEF_CNT | L_CTRL_PATTERN;
- gdtcmd->u.ioctl.channel =
- ha->raw[i].address | ha->raw[i].id_list[j];
- pdef->sddc_type = 0x08;
-
- if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
- size = sprintf(buffer+len,
- " Grown Defects:\t%d\n",
- pdef->sddc_cnt);
- len += size; pos = begin + len;
- }
- }
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length) {
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
- goto stop_output;
- }
- }
- }
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
-
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
-
- /* 3. about logical drives */
- size = sprintf(buffer+len,"\nLogical Drives:");
- len += size; pos = begin + len;
- flag = FALSE;
-
- buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
- if (!buf)
- goto stop_output;
- for (i = 0; i < MAX_LDRIVES; ++i) {
- if (!ha->hdr[i].is_logdrv)
- continue;
- drv_no = i;
- j = k = 0;
- is_mirr = FALSE;
- do {
- /* 3.a log. drive info */
- TRACE2(("cache_drv_info() drive no %d\n",drv_no));
- pcdi = (gdth_cdrinfo_str *)buf;
- gdtcmd->Service = CACHESERVICE;
- gdtcmd->OpCode = GDT_IOCTL;
- gdtcmd->u.ioctl.p_param = paddr;
- gdtcmd->u.ioctl.param_size = sizeof(gdth_cdrinfo_str);
- gdtcmd->u.ioctl.subfunc = CACHE_DRV_INFO;
- gdtcmd->u.ioctl.channel = drv_no;
- if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) != S_OK)
- break;
- pcdi->ld_dtype >>= 16;
- j++;
- if (pcdi->ld_dtype > 2) {
- strcpy(hrec, "missing");
- } else if (pcdi->ld_error & 1) {
- strcpy(hrec, "fault");
- } else if (pcdi->ld_error & 2) {
- strcpy(hrec, "invalid");
- k++; j--;
- } else {
- strcpy(hrec, "ok");
- }
-
- if (drv_no == i) {
- size = sprintf(buffer+len,
- "\n Number: \t%-2d \tStatus: \t%s\n",
- drv_no, hrec);
- len += size; pos = begin + len;
- flag = TRUE;
- no_mdrv = pcdi->cd_ldcnt;
- if (no_mdrv > 1 || pcdi->ld_slave != -1) {
- is_mirr = TRUE;
- strcpy(hrec, "RAID-1");
- } else if (pcdi->ld_dtype == 0) {
- strcpy(hrec, "Disk");
- } else if (pcdi->ld_dtype == 1) {
- strcpy(hrec, "RAID-0");
- } else if (pcdi->ld_dtype == 2) {
- strcpy(hrec, "Chain");
- } else {
- strcpy(hrec, "???");
- }
- size = sprintf(buffer+len,
- " Capacity [MB]:\t%-6d \tType: \t%s\n",
- pcdi->ld_blkcnt/(1024*1024/pcdi->ld_blksize),
- hrec);
- len += size; pos = begin + len;
- } else {
- size = sprintf(buffer+len,
- " Slave Number: \t%-2d \tStatus: \t%s\n",
- drv_no & 0x7fff, hrec);
- len += size; pos = begin + len;
- }
- drv_no = pcdi->ld_slave;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length) {
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
- goto stop_output;
- }
- } while (drv_no != -1);
-
- if (is_mirr) {
- size = sprintf(buffer+len,
- " Missing Drv.: \t%-2d \tInvalid Drv.: \t%d\n",
- no_mdrv - j - k, k);
- len += size; pos = begin + len;
- }
-
- if (!ha->hdr[i].is_arraydrv)
- strcpy(hrec, "--");
- else
- sprintf(hrec, "%d", ha->hdr[i].master_no);
- size = sprintf(buffer+len,
- " To Array Drv.:\t%s\n", hrec);
- len += size; pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length) {
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
- goto stop_output;
- }
- }
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
-
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
-
- /* 4. about array drives */
- size = sprintf(buffer+len,"\nArray Drives:");
- len += size; pos = begin + len;
- flag = FALSE;
-
- buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
- if (!buf)
- goto stop_output;
- for (i = 0; i < MAX_LDRIVES; ++i) {
- if (!(ha->hdr[i].is_arraydrv && ha->hdr[i].is_master))
- continue;
- /* 4.a array drive info */
- TRACE2(("array_info() drive no %d\n",i));
- pai = (gdth_arrayinf_str *)buf;
- gdtcmd->Service = CACHESERVICE;
- gdtcmd->OpCode = GDT_IOCTL;
- gdtcmd->u.ioctl.p_param = paddr;
- gdtcmd->u.ioctl.param_size = sizeof(gdth_arrayinf_str);
- gdtcmd->u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN;
- gdtcmd->u.ioctl.channel = i;
- if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
- if (pai->ai_state == 0)
- strcpy(hrec, "idle");
- else if (pai->ai_state == 2)
- strcpy(hrec, "build");
- else if (pai->ai_state == 4)
- strcpy(hrec, "ready");
- else if (pai->ai_state == 6)
- strcpy(hrec, "fail");
- else if (pai->ai_state == 8 || pai->ai_state == 10)
- strcpy(hrec, "rebuild");
- else
- strcpy(hrec, "error");
- if (pai->ai_ext_state & 0x10)
- strcat(hrec, "/expand");
- else if (pai->ai_ext_state & 0x1)
- strcat(hrec, "/patch");
- size = sprintf(buffer+len,
- "\n Number: \t%-2d \tStatus: \t%s\n",
- i,hrec);
- len += size; pos = begin + len;
- flag = TRUE;
-
- if (pai->ai_type == 0)
- strcpy(hrec, "RAID-0");
- else if (pai->ai_type == 4)
- strcpy(hrec, "RAID-4");
- else if (pai->ai_type == 5)
- strcpy(hrec, "RAID-5");
- else
- strcpy(hrec, "RAID-10");
- size = sprintf(buffer+len,
- " Capacity [MB]:\t%-6d \tType: \t%s\n",
- pai->ai_size/(1024*1024/pai->ai_secsize),
- hrec);
- len += size; pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length) {
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
- goto stop_output;
- }
- }
- }
- gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
-
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
-
- /* 5. about host drives */
- size = sprintf(buffer+len,"\nHost Drives:");
- len += size; pos = begin + len;
- flag = FALSE;
-
- buf = gdth_ioctl_alloc(ha, sizeof(gdth_hget_str), FALSE, &paddr);
- if (!buf)
- goto stop_output;
- for (i = 0; i < MAX_LDRIVES; ++i) {
- if (!ha->hdr[i].is_logdrv ||
- (ha->hdr[i].is_arraydrv && !ha->hdr[i].is_master))
- continue;
- /* 5.a get host drive list */
- TRACE2(("host_get() drv_no %d\n",i));
- phg = (gdth_hget_str *)buf;
- gdtcmd->Service = CACHESERVICE;
- gdtcmd->OpCode = GDT_IOCTL;
- gdtcmd->u.ioctl.p_param = paddr;
- gdtcmd->u.ioctl.param_size = sizeof(gdth_hget_str);
- gdtcmd->u.ioctl.subfunc = HOST_GET | LA_CTRL_PATTERN;
- gdtcmd->u.ioctl.channel = i;
- phg->entries = MAX_HDRIVES;
- phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]);
- if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
- ha->hdr[i].ldr_no = i;
- ha->hdr[i].rw_attribs = 0;
- ha->hdr[i].start_sec = 0;
- } else {
- for (j = 0; j < phg->entries; ++j) {
- k = phg->entry[j].host_drive;
- if (k >= MAX_LDRIVES)
- continue;
- ha->hdr[k].ldr_no = phg->entry[j].log_drive;
- ha->hdr[k].rw_attribs = phg->entry[j].rw_attribs;
- ha->hdr[k].start_sec = phg->entry[j].start_sec;
- }
- }
- }
- gdth_ioctl_free(ha, sizeof(gdth_hget_str), buf, paddr);
-
- for (i = 0; i < MAX_HDRIVES; ++i) {
- if (!(ha->hdr[i].present))
- continue;
-
- size = sprintf(buffer+len,
- "\n Number: \t%-2d \tArr/Log. Drive:\t%d\n",
- i, ha->hdr[i].ldr_no);
- len += size; pos = begin + len;
- flag = TRUE;
-
- size = sprintf(buffer+len,
- " Capacity [MB]:\t%-6d \tStart Sector: \t%d\n",
- (u32)(ha->hdr[i].size/2048), ha->hdr[i].start_sec);
- len += size; pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length)
- goto stop_output;
- }
-
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
- }
-
- /* controller events */
- size = sprintf(buffer+len,"\nController Events:\n");
- len += size; pos = begin + len;
-
- for (id = -1;;) {
- id = gdth_read_event(ha, id, estr);
- if (estr->event_source == 0)
- break;
- if (estr->event_data.eu.driver.ionode == ha->hanum &&
- estr->event_source == ES_ASYNC) {
- gdth_log_event(&estr->event_data, hrec);
- do_gettimeofday(&tv);
- sec = (int)(tv.tv_sec - estr->first_stamp);
- if (sec < 0) sec = 0;
- size = sprintf(buffer+len," date- %02d:%02d:%02d\t%s\n",
- sec/3600, sec%3600/60, sec%60, hrec);
- len += size; pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length)
- goto stop_output;
- }
- if (id == -1)
- break;
- }
-
-stop_output:
- *start = buffer +(offset-begin);
- len -= (offset-begin);
- if (len > length)
- len = length;
- TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n",
- len,(int)pos,(int)begin,(int)offset,length,size));
- rc = len;
-
-free_fail:
- kfree(gdtcmd);
- kfree(estr);
- return rc;
-}
-
-static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
- u64 *paddr)
-{
- unsigned long flags;
- char *ret_val;
-
- if (size == 0)
- return NULL;
-
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- if (!ha->scratch_busy && size <= GDTH_SCRATCH) {
- ha->scratch_busy = TRUE;
- ret_val = ha->pscratch;
- *paddr = ha->scratch_phys;
- } else if (scratch) {
- ret_val = NULL;
- } else {
- dma_addr_t dma_addr;
-
- ret_val = pci_alloc_consistent(ha->pdev, size, &dma_addr);
- *paddr = dma_addr;
- }
-
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return ret_val;
-}
-
-static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr)
-{
- unsigned long flags;
-
- if (buf == ha->pscratch) {
- spin_lock_irqsave(&ha->smp_lock, flags);
- ha->scratch_busy = FALSE;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- } else {
- pci_free_consistent(ha->pdev, size, buf, paddr);
- }
-}
-
-#ifdef GDTH_IOCTL_PROC
-static int gdth_ioctl_check_bin(gdth_ha_str *ha, u16 size)
-{
- unsigned long flags;
- int ret_val;
-
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- ret_val = FALSE;
- if (ha->scratch_busy) {
- if (((gdth_iord_str *)ha->pscratch)->size == (u32)size)
- ret_val = TRUE;
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- return ret_val;
-}
-#endif
-
-static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id)
-{
- unsigned long flags;
- int i;
- Scsi_Cmnd *scp;
- struct gdth_cmndinfo *cmndinfo;
- u8 b, t;
-
- spin_lock_irqsave(&ha->smp_lock, flags);
-
- for (i = 0; i < GDTH_MAXCMDS; ++i) {
- scp = ha->cmd_tab[i].cmnd;
- cmndinfo = gdth_cmnd_priv(scp);
-
- b = scp->device->channel;
- t = scp->device->id;
- if (!SPECIAL_SCP(scp) && t == (u8)id &&
- b == (u8)busnum) {
- cmndinfo->wait_for_completion = 0;
- spin_unlock_irqrestore(&ha->smp_lock, flags);
- while (!cmndinfo->wait_for_completion)
- barrier();
- spin_lock_irqsave(&ha->smp_lock, flags);
- }
- }
- spin_unlock_irqrestore(&ha->smp_lock, flags);
-}
diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h
deleted file mode 100644
index dab15f59f2cc..000000000000
--- a/drivers/scsi/gdth_proc.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef _GDTH_PROC_H
-#define _GDTH_PROC_H
-
-/* gdth_proc.h
- * $Id: gdth_proc.h,v 1.16 2004/01/14 13:09:01 achim Exp $
- */
-
-int gdth_execute(struct Scsi_Host *shost, gdth_cmd_str *gdtcmd, char *cmnd,
- int timeout, u32 *info);
-
-static int gdth_set_info(char *buffer,int length,struct Scsi_Host *host,
- gdth_ha_str *ha);
-static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
- struct Scsi_Host *host, gdth_ha_str *ha);
-
-static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
- int length, gdth_ha_str *ha);
-
-static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
- u64 *paddr);
-static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr);
-static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id);
-
-#endif
-
diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c
index 50bb54150a78..0420bfe9bd42 100644
--- a/drivers/scsi/gvp11.c
+++ b/drivers/scsi/gvp11.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -5,13 +6,17 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/zorro.h>
+#include <linux/module.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
#include "wd33c93.h"
#include "gvp11.h"
@@ -21,8 +26,12 @@
struct gvp11_hostdata {
struct WD33C93_hostdata wh;
struct gvp11_scsiregs *regs;
+ struct device *dev;
};
+#define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)
+#define TO_DMA_MASK(m) (~((unsigned long long)m & 0xffffffff))
+
static irqreturn_t gvp11_intr(int irq, void *data)
{
struct Scsi_Host *instance = data;
@@ -41,25 +50,37 @@ static irqreturn_t gvp11_intr(int irq, void *data)
static int gvp11_xfer_mask = 0;
-void gvp11_setup(char *str, int *ints)
-{
- gvp11_xfer_mask = ints[1];
-}
-
static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
{
+ struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
+ unsigned long len = scsi_pointer->this_residual;
struct Scsi_Host *instance = cmd->device->host;
struct gvp11_hostdata *hdata = shost_priv(instance);
struct WD33C93_hostdata *wh = &hdata->wh;
struct gvp11_scsiregs *regs = hdata->regs;
unsigned short cntr = GVP11_DMAC_INT_ENABLE;
- unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+ dma_addr_t addr;
int bank_mask;
static int scsi_alloc_out_of_range = 0;
+ addr = dma_map_single(hdata->dev, scsi_pointer->ptr,
+ len, DMA_DIR(dir_in));
+ if (dma_mapping_error(hdata->dev, addr)) {
+ dev_warn(hdata->dev, "cannot map SCSI data block %p\n",
+ scsi_pointer->ptr);
+ return 1;
+ }
+ scsi_pointer->dma_handle = addr;
+
/* use bounce buffer if the physical address is bad */
if (addr & wh->dma_xfer_mask) {
- wh->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff;
+ /* drop useless mapping */
+ dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
+ scsi_pointer->this_residual,
+ DMA_DIR(dir_in));
+ scsi_pointer->dma_handle = (dma_addr_t) NULL;
+
+ wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff;
if (!scsi_alloc_out_of_range) {
wh->dma_bounce_buffer =
@@ -81,10 +102,32 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
wh->dma_buffer_pool = BUF_CHIP_ALLOCED;
}
- /* check if the address of the bounce buffer is OK */
- addr = virt_to_bus(wh->dma_bounce_buffer);
+ if (!dir_in) {
+ /* copy to bounce buffer for a write */
+ memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr,
+ scsi_pointer->this_residual);
+ }
+
+ if (wh->dma_buffer_pool == BUF_SCSI_ALLOCED) {
+ /* will flush/invalidate cache for us */
+ addr = dma_map_single(hdata->dev,
+ wh->dma_bounce_buffer,
+ wh->dma_bounce_len,
+ DMA_DIR(dir_in));
+ /* can't map buffer; use PIO */
+ if (dma_mapping_error(hdata->dev, addr)) {
+ dev_warn(hdata->dev,
+ "cannot map bounce buffer %p\n",
+ wh->dma_bounce_buffer);
+ return 1;
+ }
+ }
if (addr & wh->dma_xfer_mask) {
+ /* drop useless mapping */
+ dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
+ scsi_pointer->this_residual,
+ DMA_DIR(dir_in));
/* fall back to Chip RAM if address out of range */
if (wh->dma_buffer_pool == BUF_SCSI_ALLOCED) {
kfree(wh->dma_bounce_buffer);
@@ -102,15 +145,19 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
return 1;
}
- addr = virt_to_bus(wh->dma_bounce_buffer);
+ if (!dir_in) {
+ /* copy to bounce buffer for a write */
+ memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr,
+ scsi_pointer->this_residual);
+ }
+ /* chip RAM can be mapped to phys. address directly */
+ addr = virt_to_phys(wh->dma_bounce_buffer);
+ /* no need to flush/invalidate cache */
wh->dma_buffer_pool = BUF_CHIP_ALLOCED;
}
+ /* finally, have OK mapping (punted for PIO else) */
+ scsi_pointer->dma_handle = addr;
- if (!dir_in) {
- /* copy to bounce buffer for a write */
- memcpy(wh->dma_bounce_buffer, cmd->SCp.ptr,
- cmd->SCp.this_residual);
- }
}
/* setup dma direction */
@@ -123,13 +170,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
/* setup DMA *physical* address */
regs->ACR = addr;
- if (dir_in) {
- /* invalidate any cache */
- cache_clear(addr, cmd->SCp.this_residual);
- } else {
- /* push any dirty cache */
- cache_push(addr, cmd->SCp.this_residual);
- }
+ /* no more cache flush here - dma_map_single() takes care */
bank_mask = (~wh->dma_xfer_mask >> 18) & 0x01c0;
if (bank_mask)
@@ -145,6 +186,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
int status)
{
+ struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(SCpnt);
struct gvp11_hostdata *hdata = shost_priv(instance);
struct WD33C93_hostdata *wh = &hdata->wh;
struct gvp11_scsiregs *regs = hdata->regs;
@@ -154,11 +196,16 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
/* remove write bit from CONTROL bits */
regs->CNTR = GVP11_DMAC_INT_ENABLE;
+ if (wh->dma_buffer_pool == BUF_SCSI_ALLOCED)
+ dma_unmap_single(hdata->dev, scsi_pointer->dma_handle,
+ scsi_pointer->this_residual,
+ DMA_DIR(wh->dma_dir));
+
/* copy from a bounce buffer, if necessary */
if (status && wh->dma_bounce_buffer) {
if (wh->dma_dir && SCpnt)
- memcpy(SCpnt->SCp.ptr, wh->dma_bounce_buffer,
- SCpnt->SCp.this_residual);
+ memcpy(scsi_pointer->ptr, wh->dma_bounce_buffer,
+ scsi_pointer->this_residual);
if (wh->dma_buffer_pool == BUF_SCSI_ALLOCED)
kfree(wh->dma_bounce_buffer);
@@ -170,40 +217,24 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
}
}
-static int gvp11_bus_reset(struct scsi_cmnd *cmd)
-{
- struct Scsi_Host *instance = cmd->device->host;
-
- /* FIXME perform bus-specific reset */
-
- /* FIXME 2: shouldn't we no-op this function (return
- FAILED), and fall back to host reset function,
- wd33c93_host_reset ? */
-
- spin_lock_irq(instance->host_lock);
- wd33c93_host_reset(cmd);
- spin_unlock_irq(instance->host_lock);
-
- return SUCCESS;
-}
-
-static struct scsi_host_template gvp11_scsi_template = {
+static const struct scsi_host_template gvp11_scsi_template = {
.module = THIS_MODULE,
.name = "GVP Series II SCSI",
- .proc_info = wd33c93_proc_info,
+ .show_info = wd33c93_show_info,
+ .write_info = wd33c93_write_info,
.proc_name = "GVP11",
.queuecommand = wd33c93_queuecommand,
.eh_abort_handler = wd33c93_abort,
- .eh_bus_reset_handler = gvp11_bus_reset,
.eh_host_reset_handler = wd33c93_host_reset,
.can_queue = CAN_QUEUE,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = CMD_PER_LUN,
- .use_clustering = DISABLE_CLUSTERING
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct scsi_pointer),
};
-static int __devinit check_wd33c93(struct gvp11_scsiregs *regs)
+static int check_wd33c93(struct gvp11_scsiregs *regs)
{
#ifdef CHECK_WD33C93
volatile unsigned char *sasr_3393, *scmd_3393;
@@ -283,8 +314,7 @@ static int __devinit check_wd33c93(struct gvp11_scsiregs *regs)
return 0;
}
-static int __devinit gvp11_probe(struct zorro_dev *z,
- const struct zorro_device_id *ent)
+static int gvp11_probe(struct zorro_dev *z, const struct zorro_device_id *ent)
{
struct Scsi_Host *instance;
unsigned long address;
@@ -297,6 +327,13 @@ static int __devinit gvp11_probe(struct zorro_dev *z,
default_dma_xfer_mask = ent->driver_data;
+ if (dma_set_mask_and_coherent(&z->dev,
+ TO_DMA_MASK(default_dma_xfer_mask))) {
+ dev_warn(&z->dev, "cannot use DMA mask %llx\n",
+ TO_DMA_MASK(default_dma_xfer_mask));
+ return -ENODEV;
+ }
+
/*
* Rumors state that some GVP ram boards use the same product
* code as the SCSI controllers. Therefore if the board-size
@@ -309,7 +346,7 @@ static int __devinit gvp11_probe(struct zorro_dev *z,
if (!request_mem_region(address, 256, "wd33c93"))
return -EBUSY;
- regs = (struct gvp11_scsiregs *)(ZTWO_VADDR(address));
+ regs = ZTWO_VADDR(address);
error = check_wd33c93(regs);
if (error)
@@ -337,9 +374,16 @@ static int __devinit gvp11_probe(struct zorro_dev *z,
wdregs.SCMD = &regs->SCMD;
hdata = shost_priv(instance);
- if (gvp11_xfer_mask)
+ if (gvp11_xfer_mask) {
hdata->wh.dma_xfer_mask = gvp11_xfer_mask;
- else
+ if (dma_set_mask_and_coherent(&z->dev,
+ TO_DMA_MASK(gvp11_xfer_mask))) {
+ dev_warn(&z->dev, "cannot use DMA mask %llx\n",
+ TO_DMA_MASK(gvp11_xfer_mask));
+ error = -ENODEV;
+ goto fail_check_or_alloc;
+ }
+ } else
hdata->wh.dma_xfer_mask = default_dma_xfer_mask;
hdata->wh.no_sync = 0xff;
@@ -379,7 +423,7 @@ fail_check_or_alloc:
return error;
}
-static void __devexit gvp11_remove(struct zorro_dev *z)
+static void gvp11_remove(struct zorro_dev *z)
{
struct Scsi_Host *instance = zorro_get_drvdata(z);
struct gvp11_hostdata *hdata = shost_priv(instance);
@@ -397,7 +441,7 @@ static void __devexit gvp11_remove(struct zorro_dev *z)
* SERIES I though).
*/
-static struct zorro_device_id gvp11_zorro_tbl[] __devinitdata = {
+static struct zorro_device_id gvp11_zorro_tbl[] = {
{ ZORRO_PROD_GVP_COMBO_030_R3_SCSI, ~0x00ffffff },
{ ZORRO_PROD_GVP_SERIES_II, ~0x00ffffff },
{ ZORRO_PROD_GVP_GFORCE_030_SCSI, ~0x01ffffff },
@@ -413,7 +457,7 @@ static struct zorro_driver gvp11_driver = {
.name = "gvp11",
.id_table = gvp11_zorro_tbl,
.probe = gvp11_probe,
- .remove = __devexit_p(gvp11_remove),
+ .remove = gvp11_remove,
};
static int __init gvp11_init(void)
diff --git a/drivers/scsi/gvp11.h b/drivers/scsi/gvp11.h
index 852913cde5dd..61c1a3584461 100644
--- a/drivers/scsi/gvp11.h
+++ b/drivers/scsi/gvp11.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef GVP11_H
/* $Id: gvp11.h,v 1.4 1997/01/19 23:07:12 davem Exp $
diff --git a/drivers/scsi/hisi_sas/Kconfig b/drivers/scsi/hisi_sas/Kconfig
new file mode 100644
index 000000000000..4ba3a8eadb77
--- /dev/null
+++ b/drivers/scsi/hisi_sas/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SCSI_HISI_SAS
+ tristate "HiSilicon SAS"
+ depends on HAS_IOMEM
+ depends on ARM64 || COMPILE_TEST
+ select SCSI_SAS_LIBSAS
+ select BLK_DEV_INTEGRITY
+ depends on ATA
+ select SATA_HOST
+ help
+ This driver supports HiSilicon's SAS HBA, including support based
+ on platform device
+
+config SCSI_HISI_SAS_PCI
+ tristate "HiSilicon SAS on PCI bus"
+ depends on SCSI_HISI_SAS
+ depends on PCI
+ depends on ACPI
+ help
+ This driver supports HiSilicon's SAS HBA based on PCI device
+
+config SCSI_HISI_SAS_DEBUGFS_DEFAULT_ENABLE
+ bool "HiSilicon SAS debugging default enable"
+ depends on SCSI_HISI_SAS
+ help
+ Set Y to default enable DEBUGFS for SCSI_HISI_SAS
diff --git a/drivers/scsi/hisi_sas/Makefile b/drivers/scsi/hisi_sas/Makefile
new file mode 100644
index 000000000000..742e732cd51d
--- /dev/null
+++ b/drivers/scsi/hisi_sas/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas_main.o
+obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas_v1_hw.o hisi_sas_v2_hw.o
+obj-$(CONFIG_SCSI_HISI_SAS_PCI) += hisi_sas_v3_hw.o
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
new file mode 100644
index 000000000000..1323ed8aa717
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -0,0 +1,692 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2015 Linaro Ltd.
+ * Copyright (c) 2015 Hisilicon Limited.
+ */
+
+#ifndef _HISI_SAS_H_
+#define _HISI_SAS_H_
+
+#include <linux/acpi.h>
+#include <linux/blk-mq.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/dmapool.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/lcm.h>
+#include <linux/libata.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/timer.h>
+#include <scsi/sas_ata.h>
+#include <scsi/libsas.h>
+
+#define HISI_SAS_MAX_PHYS 9
+#define HISI_SAS_MAX_QUEUES 32
+#define HISI_SAS_QUEUE_SLOTS 4096
+#define HISI_SAS_MAX_ITCT_ENTRIES 1024
+#define HISI_SAS_MAX_DEVICES HISI_SAS_MAX_ITCT_ENTRIES
+#define HISI_SAS_RESETTING_BIT 0
+#define HISI_SAS_REJECT_CMD_BIT 1
+#define HISI_SAS_PM_BIT 2
+#define HISI_SAS_HW_FAULT_BIT 3
+#define HISI_SAS_MAX_COMMANDS (HISI_SAS_QUEUE_SLOTS)
+#define HISI_SAS_RESERVED_IPTT 96
+#define HISI_SAS_UNRESERVED_IPTT \
+ (HISI_SAS_MAX_COMMANDS - HISI_SAS_RESERVED_IPTT)
+
+#define HISI_SAS_IOST_ITCT_CACHE_NUM 64
+#define HISI_SAS_IOST_ITCT_CACHE_DW_SZ 10
+#define HISI_SAS_FIFO_DATA_DW_SIZE 32
+
+#define HISI_SAS_REG_MEM_SIZE 4
+#define HISI_SAS_MAX_CDB_LEN 16
+#define HISI_SAS_BLK_QUEUE_DEPTH 64
+
+#define BYTE_TO_DW 4
+#define BYTE_TO_DDW 8
+
+#define HISI_SAS_STATUS_BUF_SZ (sizeof(struct hisi_sas_status_buffer))
+#define HISI_SAS_COMMAND_TABLE_SZ (sizeof(union hisi_sas_command_table))
+
+#define hisi_sas_status_buf_addr(buf) \
+ ((buf) + offsetof(struct hisi_sas_slot_buf_table, status_buffer))
+#define hisi_sas_status_buf_addr_mem(slot) hisi_sas_status_buf_addr((slot)->buf)
+#define hisi_sas_status_buf_addr_dma(slot) \
+ hisi_sas_status_buf_addr((slot)->buf_dma)
+
+#define hisi_sas_cmd_hdr_addr(buf) \
+ ((buf) + offsetof(struct hisi_sas_slot_buf_table, command_header))
+#define hisi_sas_cmd_hdr_addr_mem(slot) hisi_sas_cmd_hdr_addr((slot)->buf)
+#define hisi_sas_cmd_hdr_addr_dma(slot) hisi_sas_cmd_hdr_addr((slot)->buf_dma)
+
+#define hisi_sas_sge_addr(buf) \
+ ((buf) + offsetof(struct hisi_sas_slot_buf_table, sge_page))
+#define hisi_sas_sge_addr_mem(slot) hisi_sas_sge_addr((slot)->buf)
+#define hisi_sas_sge_addr_dma(slot) hisi_sas_sge_addr((slot)->buf_dma)
+
+#define hisi_sas_sge_dif_addr(buf) \
+ ((buf) + offsetof(struct hisi_sas_slot_dif_buf_table, sge_dif_page))
+#define hisi_sas_sge_dif_addr_mem(slot) hisi_sas_sge_dif_addr((slot)->buf)
+#define hisi_sas_sge_dif_addr_dma(slot) hisi_sas_sge_dif_addr((slot)->buf_dma)
+
+#define HISI_SAS_MAX_SSP_RESP_SZ (sizeof(struct ssp_frame_hdr) + 1024)
+#define HISI_SAS_MAX_SMP_RESP_SZ 1028
+#define HISI_SAS_MAX_STP_RESP_SZ 28
+
+#define HISI_SAS_SATA_PROTOCOL_NONDATA 0x1
+#define HISI_SAS_SATA_PROTOCOL_PIO 0x2
+#define HISI_SAS_SATA_PROTOCOL_DMA 0x4
+#define HISI_SAS_SATA_PROTOCOL_FPDMA 0x8
+#define HISI_SAS_SATA_PROTOCOL_ATAPI 0x10
+
+#define HISI_SAS_DIF_PROT_MASK (SHOST_DIF_TYPE1_PROTECTION | \
+ SHOST_DIF_TYPE2_PROTECTION | \
+ SHOST_DIF_TYPE3_PROTECTION)
+
+#define HISI_SAS_DIX_PROT_MASK (SHOST_DIX_TYPE1_PROTECTION | \
+ SHOST_DIX_TYPE2_PROTECTION | \
+ SHOST_DIX_TYPE3_PROTECTION)
+
+#define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK | HISI_SAS_DIX_PROT_MASK)
+
+#define HISI_SAS_WAIT_PHYUP_TIMEOUT (30 * HZ)
+#define HISI_SAS_CLEAR_ITCT_TIMEOUT (20 * HZ)
+#define HISI_SAS_DELAY_FOR_PHY_DISABLE 100
+#define NAME_BUF_SIZE 256
+
+struct hisi_hba;
+
+enum {
+ PORT_TYPE_SAS = (1U << 1),
+ PORT_TYPE_SATA = (1U << 0),
+};
+
+enum dev_status {
+ HISI_SAS_DEV_INIT,
+ HISI_SAS_DEV_NORMAL,
+ HISI_SAS_DEV_NCQ_ERR,
+};
+
+enum {
+ HISI_SAS_INT_ABT_CMD = 0,
+ HISI_SAS_INT_ABT_DEV = 1,
+};
+
+enum hisi_sas_dev_type {
+ HISI_SAS_DEV_TYPE_STP = 0,
+ HISI_SAS_DEV_TYPE_SSP,
+ HISI_SAS_DEV_TYPE_SATA,
+};
+
+struct hisi_sas_hw_error {
+ u32 irq_msk;
+ u32 msk;
+ int shift;
+ const char *msg;
+ int reg;
+ const struct hisi_sas_hw_error *sub;
+};
+
+struct hisi_sas_rst {
+ struct hisi_hba *hisi_hba;
+ struct completion *completion;
+ struct work_struct work;
+ bool done;
+};
+
+#define HISI_SAS_RST_WORK_INIT(r, c) \
+ { .hisi_hba = hisi_hba, \
+ .completion = &c, \
+ .work = __WORK_INITIALIZER(r.work, \
+ hisi_sas_sync_rst_work_handler), \
+ .done = false, \
+ }
+
+#define HISI_SAS_DECLARE_RST_WORK_ON_STACK(r) \
+ DECLARE_COMPLETION_ONSTACK(c); \
+ struct hisi_sas_rst r = HISI_SAS_RST_WORK_INIT(r, c)
+
+enum hisi_sas_bit_err_type {
+ HISI_SAS_ERR_SINGLE_BIT_ECC = 0x0,
+ HISI_SAS_ERR_MULTI_BIT_ECC = 0x1,
+};
+
+enum hisi_sas_phy_event {
+ HISI_PHYE_PHY_UP = 0U,
+ HISI_PHYE_LINK_RESET,
+ HISI_PHYE_PHY_UP_PM,
+ HISI_PHYES_NUM,
+};
+
+struct hisi_sas_debugfs_fifo {
+ u32 signal_sel;
+ u32 dump_msk;
+ u32 dump_mode;
+ u32 trigger;
+ u32 trigger_msk;
+ u32 trigger_mode;
+ u32 rd_data[HISI_SAS_FIFO_DATA_DW_SIZE];
+};
+
+#define FRAME_RCVD_BUF 32
+#define SAS_PHY_RESV_SIZE 2
+struct hisi_sas_phy {
+ struct work_struct works[HISI_PHYES_NUM];
+ struct hisi_hba *hisi_hba;
+ struct hisi_sas_port *port;
+ struct asd_sas_phy sas_phy;
+ struct sas_identify identify;
+ struct completion *reset_completion;
+ struct timer_list timer;
+ spinlock_t lock;
+ u64 port_id; /* from hw */
+ u64 frame_rcvd_size;
+ u8 frame_rcvd[FRAME_RCVD_BUF];
+ u8 phy_attached;
+ u8 in_reset;
+ u8 reserved[SAS_PHY_RESV_SIZE];
+ u32 phy_type;
+ u32 code_violation_err_count;
+ enum sas_linkrate minimum_linkrate;
+ enum sas_linkrate maximum_linkrate;
+ int enable;
+ int wait_phyup_cnt;
+ atomic_t down_cnt;
+
+ /* Trace FIFO */
+ struct hisi_sas_debugfs_fifo fifo;
+};
+
+struct hisi_sas_port {
+ struct asd_sas_port sas_port;
+ u8 port_attached;
+ u8 id; /* from hw */
+};
+
+struct hisi_sas_cq {
+ struct hisi_hba *hisi_hba;
+ const struct cpumask *irq_mask;
+ int rd_point;
+ int id;
+ int irq_no;
+ spinlock_t poll_lock;
+};
+
+struct hisi_sas_dq {
+ struct hisi_hba *hisi_hba;
+ struct list_head list;
+ spinlock_t lock;
+ int wr_point;
+ int id;
+};
+
+struct hisi_sas_device {
+ struct hisi_hba *hisi_hba;
+ struct domain_device *sas_device;
+ struct completion *completion;
+ struct hisi_sas_dq *dq;
+ struct list_head list;
+ enum sas_device_type dev_type;
+ enum dev_status dev_status;
+ int device_id;
+ int sata_idx;
+ spinlock_t lock; /* For protecting slots */
+};
+
+struct hisi_sas_slot {
+ struct list_head entry;
+ struct list_head delivery;
+ struct sas_task *task;
+ struct hisi_sas_port *port;
+ u64 n_elem;
+ u64 n_elem_dif;
+ int dlvry_queue;
+ int dlvry_queue_slot;
+ int cmplt_queue;
+ int cmplt_queue_slot;
+ int abort;
+ int ready;
+ int device_id;
+ void *cmd_hdr;
+ dma_addr_t cmd_hdr_dma;
+ struct timer_list internal_abort_timer;
+ bool is_internal;
+ struct sas_tmf_task *tmf;
+ /* Do not reorder/change members after here */
+ void *buf;
+ dma_addr_t buf_dma;
+ u16 idx;
+};
+
+struct hisi_sas_iost_itct_cache {
+ u32 data[HISI_SAS_IOST_ITCT_CACHE_DW_SZ];
+};
+
+enum hisi_sas_debugfs_reg_array_member {
+ DEBUGFS_GLOBAL = 0,
+ DEBUGFS_AXI,
+ DEBUGFS_RAS,
+ DEBUGFS_REGS_NUM
+};
+
+enum hisi_sas_debugfs_cache_type {
+ HISI_SAS_ITCT_CACHE,
+ HISI_SAS_IOST_CACHE,
+};
+
+enum hisi_sas_debugfs_bist_ffe_cfg {
+ FFE_SAS_1_5_GBPS,
+ FFE_SAS_3_0_GBPS,
+ FFE_SAS_6_0_GBPS,
+ FFE_SAS_12_0_GBPS,
+ FFE_RESV,
+ FFE_SATA_1_5_GBPS,
+ FFE_SATA_3_0_GBPS,
+ FFE_SATA_6_0_GBPS,
+ FFE_CFG_MAX
+};
+
+enum hisi_sas_debugfs_bist_fixed_code {
+ FIXED_CODE,
+ FIXED_CODE_1,
+ FIXED_CODE_MAX
+};
+
+enum {
+ HISI_SAS_BIST_CODE_MODE_PRBS7,
+ HISI_SAS_BIST_CODE_MODE_PRBS23,
+ HISI_SAS_BIST_CODE_MODE_PRBS31,
+ HISI_SAS_BIST_CODE_MODE_JTPAT,
+ HISI_SAS_BIST_CODE_MODE_CJTPAT,
+ HISI_SAS_BIST_CODE_MODE_SCRAMBED_0,
+ HISI_SAS_BIST_CODE_MODE_TRAIN,
+ HISI_SAS_BIST_CODE_MODE_TRAIN_DONE,
+ HISI_SAS_BIST_CODE_MODE_HFTP,
+ HISI_SAS_BIST_CODE_MODE_MFTP,
+ HISI_SAS_BIST_CODE_MODE_LFTP,
+ HISI_SAS_BIST_CODE_MODE_FIXED_DATA,
+};
+
+struct hisi_sas_hw {
+ int (*hw_init)(struct hisi_hba *hisi_hba);
+ int (*fw_info_check)(struct hisi_hba *hisi_hba);
+ int (*interrupt_preinit)(struct hisi_hba *hisi_hba);
+ void (*setup_itct)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *device);
+ int (*slot_index_alloc)(struct hisi_hba *hisi_hba,
+ struct domain_device *device);
+ struct hisi_sas_device *(*alloc_dev)(struct domain_device *device);
+ void (*sl_notify_ssp)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*start_delivery)(struct hisi_sas_dq *dq);
+ void (*prep_ssp)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot);
+ void (*prep_smp)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot);
+ void (*prep_stp)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot);
+ void (*prep_abort)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot);
+ void (*phys_init)(struct hisi_hba *hisi_hba);
+ void (*phy_start)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*phy_disable)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*phy_hard_reset)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*get_events)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*phy_set_linkrate)(struct hisi_hba *hisi_hba, int phy_no,
+ struct sas_phy_linkrates *linkrates);
+ enum sas_linkrate (*phy_get_max_linkrate)(void);
+ int (*clear_itct)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *dev);
+ void (*free_device)(struct hisi_sas_device *sas_dev);
+ int (*get_wideport_bitmap)(struct hisi_hba *hisi_hba, int port_id);
+ void (*dereg_device)(struct hisi_hba *hisi_hba,
+ struct domain_device *device);
+ int (*soft_reset)(struct hisi_hba *hisi_hba);
+ u32 (*get_phys_state)(struct hisi_hba *hisi_hba);
+ int (*write_gpio)(struct hisi_hba *hisi_hba, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data);
+ void (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba,
+ int delay_ms, int timeout_ms);
+ int (*debugfs_snapshot_regs)(struct hisi_hba *hisi_hba);
+ int complete_hdr_size;
+ const struct scsi_host_template *sht;
+};
+
+#define HISI_SAS_MAX_DEBUGFS_DUMP 50
+#define HISI_SAS_DEFAULT_DEBUGFS_DUMP 1
+
+struct hisi_sas_debugfs_cq {
+ struct hisi_sas_cq *cq;
+ void *complete_hdr;
+};
+
+struct hisi_sas_debugfs_dq {
+ struct hisi_sas_dq *dq;
+ struct hisi_sas_cmd_hdr *hdr;
+};
+
+struct hisi_sas_debugfs_regs {
+ struct hisi_hba *hisi_hba;
+ u32 *data;
+};
+
+struct hisi_sas_debugfs_port {
+ struct hisi_sas_phy *phy;
+ u32 *data;
+};
+
+struct hisi_sas_debugfs_iost {
+ struct hisi_sas_iost *iost;
+};
+
+struct hisi_sas_debugfs_itct {
+ struct hisi_sas_itct *itct;
+};
+
+struct hisi_sas_debugfs_iost_cache {
+ struct hisi_sas_iost_itct_cache *cache;
+};
+
+struct hisi_sas_debugfs_itct_cache {
+ struct hisi_sas_iost_itct_cache *cache;
+};
+
+struct hisi_hba {
+ /* This must be the first element, used by SHOST_TO_SAS_HA */
+ struct sas_ha_struct *p;
+
+ struct platform_device *platform_dev;
+ struct pci_dev *pci_dev;
+ struct device *dev;
+
+ int prot_mask;
+
+ void __iomem *regs;
+ void __iomem *sgpio_regs;
+ struct regmap *ctrl;
+ u32 ctrl_reset_reg;
+ u32 ctrl_reset_sts_reg;
+ u32 ctrl_clock_ena_reg;
+ u32 refclk_frequency_mhz;
+ u8 sas_addr[SAS_ADDR_SIZE];
+
+ int *irq_map; /* v2 hw */
+
+ int n_phy;
+ spinlock_t lock;
+ struct semaphore sem;
+
+ struct timer_list timer;
+ struct workqueue_struct *wq;
+
+ int slot_index_count;
+ int last_slot_index;
+ int last_dev_id;
+ unsigned long *slot_index_tags;
+ unsigned long reject_stp_links_msk;
+
+ /* SCSI/SAS glue */
+ struct sas_ha_struct sha;
+ struct Scsi_Host *shost;
+
+ struct hisi_sas_cq cq[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_dq dq[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_phy phy[HISI_SAS_MAX_PHYS];
+ struct hisi_sas_port port[HISI_SAS_MAX_PHYS];
+
+ int queue_count;
+
+ struct hisi_sas_device devices[HISI_SAS_MAX_DEVICES];
+ struct hisi_sas_cmd_hdr *cmd_hdr[HISI_SAS_MAX_QUEUES];
+ dma_addr_t cmd_hdr_dma[HISI_SAS_MAX_QUEUES];
+ void *complete_hdr[HISI_SAS_MAX_QUEUES];
+ dma_addr_t complete_hdr_dma[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_initial_fis *initial_fis;
+ dma_addr_t initial_fis_dma;
+ struct hisi_sas_itct *itct;
+ dma_addr_t itct_dma;
+ struct hisi_sas_iost *iost;
+ dma_addr_t iost_dma;
+ struct hisi_sas_breakpoint *breakpoint;
+ dma_addr_t breakpoint_dma;
+ struct hisi_sas_breakpoint *sata_breakpoint;
+ dma_addr_t sata_breakpoint_dma;
+ struct hisi_sas_slot *slot_info;
+ unsigned long flags;
+ const struct hisi_sas_hw *hw; /* Low level hw interface */
+ unsigned long sata_dev_bitmap[BITS_TO_LONGS(HISI_SAS_MAX_DEVICES)];
+ struct work_struct rst_work;
+ u32 phy_state;
+ u32 intr_coal_ticks; /* Time of interrupt coalesce in us */
+ u32 intr_coal_count; /* Interrupt count to coalesce */
+
+ int cq_nvecs;
+
+ /* bist */
+ enum sas_linkrate debugfs_bist_linkrate;
+ int debugfs_bist_code_mode;
+ int debugfs_bist_phy_no;
+ int debugfs_bist_mode;
+ u32 debugfs_bist_cnt;
+ int debugfs_bist_enable;
+ u32 debugfs_bist_ffe[HISI_SAS_MAX_PHYS][FFE_CFG_MAX];
+ u32 debugfs_bist_fixed_code[FIXED_CODE_MAX];
+
+ /* debugfs memories */
+ /* Put Global AXI and RAS Register into register array */
+ struct hisi_sas_debugfs_regs debugfs_regs[HISI_SAS_MAX_DEBUGFS_DUMP][DEBUGFS_REGS_NUM];
+ struct hisi_sas_debugfs_port debugfs_port_reg[HISI_SAS_MAX_DEBUGFS_DUMP][HISI_SAS_MAX_PHYS];
+ struct hisi_sas_debugfs_cq debugfs_cq[HISI_SAS_MAX_DEBUGFS_DUMP][HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_debugfs_dq debugfs_dq[HISI_SAS_MAX_DEBUGFS_DUMP][HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_debugfs_iost debugfs_iost[HISI_SAS_MAX_DEBUGFS_DUMP];
+ struct hisi_sas_debugfs_itct debugfs_itct[HISI_SAS_MAX_DEBUGFS_DUMP];
+ struct hisi_sas_debugfs_iost_cache debugfs_iost_cache[HISI_SAS_MAX_DEBUGFS_DUMP];
+ struct hisi_sas_debugfs_itct_cache debugfs_itct_cache[HISI_SAS_MAX_DEBUGFS_DUMP];
+
+ u64 debugfs_timestamp[HISI_SAS_MAX_DEBUGFS_DUMP];
+ int debugfs_dump_index;
+ struct dentry *debugfs_dir;
+ struct dentry *debugfs_dump_dentry;
+ struct dentry *debugfs_bist_dentry;
+ struct dentry *debugfs_fifo_dentry;
+
+ int iopoll_q_cnt;
+};
+
+/* Generic HW DMA host memory structures */
+/* Delivery queue header */
+struct hisi_sas_cmd_hdr {
+ /* dw0 */
+ __le32 dw0;
+
+ /* dw1 */
+ __le32 dw1;
+
+ /* dw2 */
+ __le32 dw2;
+
+ /* dw3 */
+ __le32 transfer_tags;
+
+ /* dw4 */
+ __le32 data_transfer_len;
+
+ /* dw5 */
+ __le32 first_burst_num;
+
+ /* dw6 */
+ __le32 sg_len;
+
+ /* dw7 */
+ __le32 dw7;
+
+ /* dw8-9 */
+ __le64 cmd_table_addr;
+
+ /* dw10-11 */
+ __le64 sts_buffer_addr;
+
+ /* dw12-13 */
+ __le64 prd_table_addr;
+
+ /* dw14-15 */
+ __le64 dif_prd_table_addr;
+};
+
+#define ITCT_RESV_DDW 12
+struct hisi_sas_itct {
+ __le64 qw0;
+ __le64 sas_addr;
+ __le64 qw2;
+ __le64 qw3;
+ __le64 qw4_15[ITCT_RESV_DDW];
+};
+
+struct hisi_sas_iost {
+ __le64 qw0;
+ __le64 qw1;
+ __le64 qw2;
+ __le64 qw3;
+};
+
+#define ERROR_RECORD_BUF_DW 4
+struct hisi_sas_err_record {
+ u32 data[ERROR_RECORD_BUF_DW];
+};
+
+#define FIS_RESV_DW 3
+struct hisi_sas_initial_fis {
+ struct hisi_sas_err_record err_record;
+ struct dev_to_host_fis fis;
+ u32 rsvd[FIS_RESV_DW];
+};
+
+#define BREAKPOINT_DATA_SIZE 128
+struct hisi_sas_breakpoint {
+ u8 data[BREAKPOINT_DATA_SIZE];
+};
+
+#define BREAKPOINT_TAG_NUM 32
+struct hisi_sas_sata_breakpoint {
+ struct hisi_sas_breakpoint tag[BREAKPOINT_TAG_NUM];
+};
+
+struct hisi_sas_sge {
+ __le64 addr;
+ __le32 page_ctrl_0;
+ __le32 page_ctrl_1;
+ __le32 data_len;
+ __le32 data_off;
+};
+
+#define SMP_CMD_TABLE_SIZE 44
+struct hisi_sas_command_table_smp {
+ u8 bytes[SMP_CMD_TABLE_SIZE];
+};
+
+#define DUMMY_BUF_SIZE 12
+struct hisi_sas_command_table_stp {
+ struct host_to_dev_fis command_fis;
+ u8 dummy[DUMMY_BUF_SIZE];
+ u8 atapi_cdb[ATAPI_CDB_LEN];
+};
+
+#define HISI_SAS_SGE_PAGE_CNT (124)
+struct hisi_sas_sge_page {
+ struct hisi_sas_sge sge[HISI_SAS_SGE_PAGE_CNT];
+} __aligned(16);
+
+#define HISI_SAS_SGE_DIF_PAGE_CNT HISI_SAS_SGE_PAGE_CNT
+struct hisi_sas_sge_dif_page {
+ struct hisi_sas_sge sge[HISI_SAS_SGE_DIF_PAGE_CNT];
+} __aligned(16);
+
+#define PROT_BUF_SIZE 7
+struct hisi_sas_command_table_ssp {
+ struct ssp_frame_hdr hdr;
+ union {
+ struct {
+ struct ssp_command_iu task;
+ u32 prot[PROT_BUF_SIZE];
+ };
+ struct ssp_tmf_iu ssp_task;
+ struct xfer_rdy_iu xfer_rdy;
+ struct ssp_response_iu ssp_res;
+ } u;
+};
+
+union hisi_sas_command_table {
+ struct hisi_sas_command_table_ssp ssp;
+ struct hisi_sas_command_table_smp smp;
+ struct hisi_sas_command_table_stp stp;
+} __aligned(16);
+
+#define IU_BUF_SIZE 1024
+struct hisi_sas_status_buffer {
+ struct hisi_sas_err_record err;
+ u8 iu[IU_BUF_SIZE];
+} __aligned(16);
+
+struct hisi_sas_slot_buf_table {
+ struct hisi_sas_status_buffer status_buffer;
+ union hisi_sas_command_table command_header;
+ struct hisi_sas_sge_page sge_page;
+};
+
+struct hisi_sas_slot_dif_buf_table {
+ struct hisi_sas_slot_buf_table slot_buf;
+ struct hisi_sas_sge_dif_page sge_dif_page;
+};
+
+extern struct scsi_transport_template *hisi_sas_stt;
+
+extern bool hisi_sas_debugfs_enable;
+extern u32 hisi_sas_debugfs_dump_count;
+extern struct dentry *hisi_sas_debugfs_dir;
+
+extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba);
+extern int hisi_sas_alloc(struct hisi_hba *hisi_hba);
+extern void hisi_sas_free(struct hisi_hba *hisi_hba);
+extern u8 hisi_sas_get_ata_protocol(struct sas_task *task);
+extern struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port);
+extern void hisi_sas_sata_done(struct sas_task *task,
+ struct hisi_sas_slot *slot);
+extern int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba);
+extern int hisi_sas_probe(struct platform_device *pdev,
+ const struct hisi_sas_hw *ops);
+extern void hisi_sas_remove(struct platform_device *pdev);
+
+int hisi_sas_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim);
+extern int hisi_sas_sdev_init(struct scsi_device *sdev);
+extern int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time);
+extern void hisi_sas_scan_start(struct Scsi_Host *shost);
+extern int hisi_sas_host_reset(struct Scsi_Host *shost, int reset_type);
+extern void hisi_sas_phy_enable(struct hisi_hba *hisi_hba, int phy_no,
+ int enable);
+extern void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy,
+ gfp_t gfp_flags);
+extern void hisi_sas_phy_bcast(struct hisi_sas_phy *phy);
+extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba,
+ struct sas_task *task,
+ struct hisi_sas_slot *slot,
+ bool need_lock);
+extern void hisi_sas_init_mem(struct hisi_hba *hisi_hba);
+extern void hisi_sas_rst_work_handler(struct work_struct *work);
+extern void hisi_sas_sync_rst_work_handler(struct work_struct *work);
+extern void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no);
+extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,
+ enum hisi_sas_phy_event event);
+extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba);
+extern u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max);
+extern void hisi_sas_sync_cqs(struct hisi_hba *hisi_hba);
+extern void hisi_sas_sync_poll_cqs(struct hisi_hba *hisi_hba);
+extern void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba);
+extern void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba);
+#endif
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
new file mode 100644
index 000000000000..30a9c6612651
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -0,0 +1,2702 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2015 Linaro Ltd.
+ * Copyright (c) 2015 Hisilicon Limited.
+ */
+
+#include "hisi_sas.h"
+#define DRV_NAME "hisi_sas"
+
+#define LINK_RATE_BIT_MASK 2
+#define FIS_BUF_SIZE 20
+#define WAIT_CMD_COMPLETE_DELAY 100
+#define WAIT_CMD_COMPLETE_TMROUT 5000
+#define DELAY_FOR_LINK_READY 2000
+#define BLK_CNT_OPTIMIZE_MARK 64
+#define HZ_TO_MHZ 1000000
+#define DELAY_FOR_SOFTRESET_MAX 1000
+#define DELAY_FOR_SOFTRESET_MIN 900
+
+#define DEV_IS_GONE(dev) \
+ ((!dev) || (dev->dev_type == SAS_PHY_UNUSED))
+
+static int hisi_sas_softreset_ata_disk(struct domain_device *device);
+static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata);
+static void hisi_sas_release_task(struct hisi_hba *hisi_hba,
+ struct domain_device *device);
+static void hisi_sas_dev_gone(struct domain_device *device);
+
+struct hisi_sas_internal_abort_data {
+ bool rst_ha_timeout; /* reset the HA for timeout */
+};
+
+static u8 hisi_sas_get_ata_protocol_from_tf(struct ata_queued_cmd *qc)
+{
+ if (!qc)
+ return HISI_SAS_SATA_PROTOCOL_PIO;
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_NODATA:
+ return HISI_SAS_SATA_PROTOCOL_NONDATA;
+ case ATA_PROT_PIO:
+ return HISI_SAS_SATA_PROTOCOL_PIO;
+ case ATA_PROT_DMA:
+ return HISI_SAS_SATA_PROTOCOL_DMA;
+ case ATA_PROT_NCQ_NODATA:
+ case ATA_PROT_NCQ:
+ return HISI_SAS_SATA_PROTOCOL_FPDMA;
+ default:
+ return HISI_SAS_SATA_PROTOCOL_PIO;
+ }
+}
+
+u8 hisi_sas_get_ata_protocol(struct sas_task *task)
+{
+ struct host_to_dev_fis *fis = &task->ata_task.fis;
+ struct ata_queued_cmd *qc = task->uldd_task;
+ int direction = task->data_dir;
+
+ switch (fis->command) {
+ case ATA_CMD_FPDMA_WRITE:
+ case ATA_CMD_FPDMA_READ:
+ case ATA_CMD_FPDMA_RECV:
+ case ATA_CMD_FPDMA_SEND:
+ case ATA_CMD_NCQ_NON_DATA:
+ return HISI_SAS_SATA_PROTOCOL_FPDMA;
+
+ case ATA_CMD_DOWNLOAD_MICRO:
+ case ATA_CMD_ID_ATA:
+ case ATA_CMD_PMP_READ:
+ case ATA_CMD_READ_LOG_EXT:
+ case ATA_CMD_PIO_READ:
+ case ATA_CMD_PIO_READ_EXT:
+ case ATA_CMD_PMP_WRITE:
+ case ATA_CMD_WRITE_LOG_EXT:
+ case ATA_CMD_PIO_WRITE:
+ case ATA_CMD_PIO_WRITE_EXT:
+ return HISI_SAS_SATA_PROTOCOL_PIO;
+
+ case ATA_CMD_DSM:
+ case ATA_CMD_DOWNLOAD_MICRO_DMA:
+ case ATA_CMD_PMP_READ_DMA:
+ case ATA_CMD_PMP_WRITE_DMA:
+ case ATA_CMD_READ:
+ case ATA_CMD_READ_EXT:
+ case ATA_CMD_READ_LOG_DMA_EXT:
+ case ATA_CMD_READ_STREAM_DMA_EXT:
+ case ATA_CMD_TRUSTED_RCV_DMA:
+ case ATA_CMD_TRUSTED_SND_DMA:
+ case ATA_CMD_WRITE:
+ case ATA_CMD_WRITE_EXT:
+ case ATA_CMD_WRITE_FUA_EXT:
+ case ATA_CMD_WRITE_QUEUED:
+ case ATA_CMD_WRITE_LOG_DMA_EXT:
+ case ATA_CMD_WRITE_STREAM_DMA_EXT:
+ case ATA_CMD_ZAC_MGMT_IN:
+ return HISI_SAS_SATA_PROTOCOL_DMA;
+
+ case ATA_CMD_CHK_POWER:
+ case ATA_CMD_DEV_RESET:
+ case ATA_CMD_EDD:
+ case ATA_CMD_FLUSH:
+ case ATA_CMD_FLUSH_EXT:
+ case ATA_CMD_VERIFY:
+ case ATA_CMD_VERIFY_EXT:
+ case ATA_CMD_SET_FEATURES:
+ case ATA_CMD_STANDBY:
+ case ATA_CMD_STANDBYNOW1:
+ case ATA_CMD_ZAC_MGMT_OUT:
+ return HISI_SAS_SATA_PROTOCOL_NONDATA;
+
+ case ATA_CMD_SET_MAX:
+ switch (fis->features) {
+ case ATA_SET_MAX_PASSWD:
+ case ATA_SET_MAX_LOCK:
+ return HISI_SAS_SATA_PROTOCOL_PIO;
+
+ case ATA_SET_MAX_PASSWD_DMA:
+ case ATA_SET_MAX_UNLOCK_DMA:
+ return HISI_SAS_SATA_PROTOCOL_DMA;
+
+ default:
+ return HISI_SAS_SATA_PROTOCOL_NONDATA;
+ }
+
+ default:
+ if (direction == DMA_NONE)
+ return HISI_SAS_SATA_PROTOCOL_NONDATA;
+ return hisi_sas_get_ata_protocol_from_tf(qc);
+ }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_get_ata_protocol);
+
+void hisi_sas_sata_done(struct sas_task *task,
+ struct hisi_sas_slot *slot)
+{
+ struct task_status_struct *ts = &task->task_status;
+ struct ata_task_resp *resp = (struct ata_task_resp *)ts->buf;
+ struct hisi_sas_status_buffer *status_buf =
+ hisi_sas_status_buf_addr_mem(slot);
+ u8 *iu = &status_buf->iu[0];
+ struct dev_to_host_fis *d2h = (struct dev_to_host_fis *)iu;
+
+ resp->frame_len = sizeof(struct dev_to_host_fis);
+ memcpy(&resp->ending_fis[0], d2h, sizeof(struct dev_to_host_fis));
+
+ ts->buf_valid_size = sizeof(*resp);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sata_done);
+
+/*
+ * This function assumes linkrate mask fits in 8 bits, which it
+ * does for all HW versions supported.
+ */
+u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max)
+{
+ u8 rate = 0;
+ int i;
+
+ max -= SAS_LINK_RATE_1_5_GBPS;
+ for (i = 0; i <= max; i++)
+ rate |= 1 << (i * LINK_RATE_BIT_MASK);
+ return rate;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_get_prog_phy_linkrate_mask);
+
+static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device)
+{
+ return device->port->ha->lldd_ha;
+}
+
+struct hisi_sas_port *to_hisi_sas_port(struct asd_sas_port *sas_port)
+{
+ return container_of(sas_port, struct hisi_sas_port, sas_port);
+}
+EXPORT_SYMBOL_GPL(to_hisi_sas_port);
+
+void hisi_sas_stop_phys(struct hisi_hba *hisi_hba)
+{
+ int phy_no;
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++)
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_stop_phys);
+
+static void hisi_sas_slot_index_clear(struct hisi_hba *hisi_hba, int slot_idx)
+{
+ void *bitmap = hisi_hba->slot_index_tags;
+
+ __clear_bit(slot_idx, bitmap);
+}
+
+static void hisi_sas_slot_index_free(struct hisi_hba *hisi_hba, int slot_idx)
+{
+ if (hisi_hba->hw->slot_index_alloc ||
+ slot_idx < HISI_SAS_RESERVED_IPTT) {
+ spin_lock(&hisi_hba->lock);
+ hisi_sas_slot_index_clear(hisi_hba, slot_idx);
+ spin_unlock(&hisi_hba->lock);
+ }
+}
+
+static void hisi_sas_slot_index_set(struct hisi_hba *hisi_hba, int slot_idx)
+{
+ void *bitmap = hisi_hba->slot_index_tags;
+
+ __set_bit(slot_idx, bitmap);
+}
+
+static int hisi_sas_slot_index_alloc(struct hisi_hba *hisi_hba,
+ struct request *rq)
+{
+ int index;
+ void *bitmap = hisi_hba->slot_index_tags;
+
+ if (rq)
+ return rq->tag + HISI_SAS_RESERVED_IPTT;
+
+ spin_lock(&hisi_hba->lock);
+ index = find_next_zero_bit(bitmap, HISI_SAS_RESERVED_IPTT,
+ hisi_hba->last_slot_index + 1);
+ if (index >= HISI_SAS_RESERVED_IPTT) {
+ index = find_next_zero_bit(bitmap,
+ HISI_SAS_RESERVED_IPTT,
+ 0);
+ if (index >= HISI_SAS_RESERVED_IPTT) {
+ spin_unlock(&hisi_hba->lock);
+ return -SAS_QUEUE_FULL;
+ }
+ }
+ hisi_sas_slot_index_set(hisi_hba, index);
+ hisi_hba->last_slot_index = index;
+ spin_unlock(&hisi_hba->lock);
+
+ return index;
+}
+
+void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
+ struct hisi_sas_slot *slot, bool need_lock)
+{
+ int device_id = slot->device_id;
+ struct hisi_sas_device *sas_dev = &hisi_hba->devices[device_id];
+
+ if (task) {
+ struct device *dev = hisi_hba->dev;
+
+ if (!task->lldd_task)
+ return;
+
+ task->lldd_task = NULL;
+
+ if (!sas_protocol_ata(task->task_proto)) {
+ if (slot->n_elem) {
+ if (task->task_proto & SAS_PROTOCOL_SSP)
+ dma_unmap_sg(dev, task->scatter,
+ task->num_scatter,
+ task->data_dir);
+ else
+ dma_unmap_sg(dev, &task->smp_task.smp_req,
+ 1, DMA_TO_DEVICE);
+ }
+ if (slot->n_elem_dif) {
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+
+ dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ task->data_dir);
+ }
+ }
+ }
+
+ if (need_lock) {
+ spin_lock(&sas_dev->lock);
+ list_del_init(&slot->entry);
+ spin_unlock(&sas_dev->lock);
+ } else {
+ list_del_init(&slot->entry);
+ }
+
+ memset(slot, 0, offsetof(struct hisi_sas_slot, buf));
+
+ hisi_sas_slot_index_free(hisi_hba, slot->idx);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_slot_task_free);
+
+static void hisi_sas_task_prep_smp(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ hisi_hba->hw->prep_smp(hisi_hba, slot);
+}
+
+static void hisi_sas_task_prep_ssp(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ hisi_hba->hw->prep_ssp(hisi_hba, slot);
+}
+
+static void hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ hisi_hba->hw->prep_stp(hisi_hba, slot);
+}
+
+static void hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ hisi_hba->hw->prep_abort(hisi_hba, slot);
+}
+
+static void hisi_sas_dma_unmap(struct hisi_hba *hisi_hba,
+ struct sas_task *task, int n_elem)
+{
+ struct device *dev = hisi_hba->dev;
+
+ if (!sas_protocol_ata(task->task_proto) && n_elem) {
+ if (task->num_scatter) {
+ dma_unmap_sg(dev, task->scatter, task->num_scatter,
+ task->data_dir);
+ } else if (task->task_proto & SAS_PROTOCOL_SMP) {
+ dma_unmap_sg(dev, &task->smp_task.smp_req,
+ 1, DMA_TO_DEVICE);
+ }
+ }
+}
+
+static int hisi_sas_dma_map(struct hisi_hba *hisi_hba,
+ struct sas_task *task, int *n_elem)
+{
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ if (sas_protocol_ata(task->task_proto)) {
+ *n_elem = task->num_scatter;
+ } else {
+ unsigned int req_len;
+
+ if (task->num_scatter) {
+ *n_elem = dma_map_sg(dev, task->scatter,
+ task->num_scatter, task->data_dir);
+ if (!*n_elem) {
+ rc = -ENOMEM;
+ goto prep_out;
+ }
+ } else if (task->task_proto & SAS_PROTOCOL_SMP) {
+ *n_elem = dma_map_sg(dev, &task->smp_task.smp_req,
+ 1, DMA_TO_DEVICE);
+ if (!*n_elem) {
+ rc = -ENOMEM;
+ goto prep_out;
+ }
+ req_len = sg_dma_len(&task->smp_task.smp_req);
+ if (req_len & 0x3) {
+ rc = -EINVAL;
+ goto err_out_dma_unmap;
+ }
+ }
+ }
+
+ if (*n_elem > HISI_SAS_SGE_PAGE_CNT) {
+ dev_err(dev, "task prep: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT\n",
+ *n_elem);
+ rc = -EINVAL;
+ goto err_out_dma_unmap;
+ }
+ return 0;
+
+err_out_dma_unmap:
+ /* It would be better to call dma_unmap_sg() here, but it's messy */
+ hisi_sas_dma_unmap(hisi_hba, task, *n_elem);
+prep_out:
+ return rc;
+}
+
+static void hisi_sas_dif_dma_unmap(struct hisi_hba *hisi_hba,
+ struct sas_task *task, int n_elem_dif)
+{
+ struct device *dev = hisi_hba->dev;
+
+ if (n_elem_dif) {
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+
+ dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ task->data_dir);
+ }
+}
+
+static int hisi_sas_dif_dma_map(struct hisi_hba *hisi_hba,
+ int *n_elem_dif, struct sas_task *task)
+{
+ struct device *dev = hisi_hba->dev;
+ struct sas_ssp_task *ssp_task;
+ struct scsi_cmnd *scsi_cmnd;
+ int rc;
+
+ if (task->num_scatter) {
+ ssp_task = &task->ssp_task;
+ scsi_cmnd = ssp_task->cmd;
+
+ if (scsi_prot_sg_count(scsi_cmnd)) {
+ *n_elem_dif = dma_map_sg(dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ task->data_dir);
+
+ if (!*n_elem_dif)
+ return -ENOMEM;
+
+ if (*n_elem_dif > HISI_SAS_SGE_DIF_PAGE_CNT) {
+ dev_err(dev, "task prep: n_elem_dif(%d) too large\n",
+ *n_elem_dif);
+ rc = -EINVAL;
+ goto err_out_dif_dma_unmap;
+ }
+ }
+ }
+
+ return 0;
+
+err_out_dif_dma_unmap:
+ dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd), task->data_dir);
+ return rc;
+}
+
+static
+void hisi_sas_task_deliver(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_dq *dq,
+ struct hisi_sas_device *sas_dev)
+{
+ struct hisi_sas_cmd_hdr *cmd_hdr_base;
+ int dlvry_queue_slot, dlvry_queue;
+ struct sas_task *task = slot->task;
+ int wr_q_index;
+
+ spin_lock(&dq->lock);
+ wr_q_index = dq->wr_point;
+ dq->wr_point = (dq->wr_point + 1) % HISI_SAS_QUEUE_SLOTS;
+ list_add_tail(&slot->delivery, &dq->list);
+ spin_unlock(&dq->lock);
+ spin_lock(&sas_dev->lock);
+ list_add_tail(&slot->entry, &sas_dev->list);
+ spin_unlock(&sas_dev->lock);
+
+ dlvry_queue = dq->id;
+ dlvry_queue_slot = wr_q_index;
+
+ slot->device_id = sas_dev->device_id;
+ slot->dlvry_queue = dlvry_queue;
+ slot->dlvry_queue_slot = dlvry_queue_slot;
+ cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue];
+ slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot];
+
+ task->lldd_task = slot;
+
+ memset(slot->cmd_hdr, 0, sizeof(struct hisi_sas_cmd_hdr));
+ memset(hisi_sas_cmd_hdr_addr_mem(slot), 0, HISI_SAS_COMMAND_TABLE_SZ);
+ memset(hisi_sas_status_buf_addr_mem(slot), 0,
+ sizeof(struct hisi_sas_err_record));
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ hisi_sas_task_prep_smp(hisi_hba, slot);
+ break;
+ case SAS_PROTOCOL_SSP:
+ hisi_sas_task_prep_ssp(hisi_hba, slot);
+ break;
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_STP_ALL:
+ hisi_sas_task_prep_ata(hisi_hba, slot);
+ break;
+ case SAS_PROTOCOL_INTERNAL_ABORT:
+ hisi_sas_task_prep_abort(hisi_hba, slot);
+ break;
+ default:
+ return;
+ }
+
+ /* Make slot memories observable before marking as ready */
+ smp_wmb();
+ WRITE_ONCE(slot->ready, 1);
+
+ spin_lock(&dq->lock);
+ hisi_hba->hw->start_delivery(dq);
+ spin_unlock(&dq->lock);
+}
+
+static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
+{
+ int n_elem = 0, n_elem_dif = 0;
+ struct domain_device *device = task->dev;
+ struct asd_sas_port *sas_port = device->port;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ bool internal_abort = sas_is_internal_abort(task);
+ struct hisi_sas_dq *dq = NULL;
+ struct hisi_sas_port *port;
+ struct hisi_hba *hisi_hba;
+ struct hisi_sas_slot *slot;
+ struct request *rq = NULL;
+ struct device *dev;
+ int rc;
+
+ if (!sas_port) {
+ struct task_status_struct *ts = &task->task_status;
+
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ /*
+ * libsas will use dev->port, should
+ * not call task_done for sata
+ */
+ if (device->dev_type != SAS_SATA_DEV && !internal_abort)
+ task->task_done(task);
+ return -ECOMM;
+ }
+
+ hisi_hba = dev_to_hisi_hba(device);
+ dev = hisi_hba->dev;
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ case SAS_PROTOCOL_SMP:
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_STP_ALL:
+ if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags))) {
+ if (!gfpflags_allow_blocking(gfp_flags))
+ return -EINVAL;
+
+ down(&hisi_hba->sem);
+ up(&hisi_hba->sem);
+ }
+
+ if (DEV_IS_GONE(sas_dev)) {
+ if (sas_dev)
+ dev_info(dev, "task prep: device %d not ready\n",
+ sas_dev->device_id);
+ else
+ dev_info(dev, "task prep: device %016llx not ready\n",
+ SAS_ADDR(device->sas_addr));
+
+ return -ECOMM;
+ }
+
+ port = to_hisi_sas_port(sas_port);
+ if (!port->port_attached) {
+ dev_info(dev, "task prep: %s port%d not attach device\n",
+ dev_is_sata(device) ? "SATA/STP" : "SAS",
+ device->port->id);
+
+ return -ECOMM;
+ }
+
+ rq = sas_task_find_rq(task);
+ if (rq) {
+ unsigned int dq_index;
+ u32 blk_tag;
+
+ blk_tag = blk_mq_unique_tag(rq);
+ dq_index = blk_mq_unique_tag_to_hwq(blk_tag);
+ dq = &hisi_hba->dq[dq_index];
+ } else {
+ int queue;
+
+ if (hisi_hba->iopoll_q_cnt) {
+ /*
+ * Use interrupt queue (queue 0) to deliver and complete
+ * internal IOs of libsas or libata when there is at least
+ * one iopoll queue
+ */
+ queue = 0;
+ } else {
+ struct Scsi_Host *shost = hisi_hba->shost;
+ struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
+
+ queue = qmap->mq_map[raw_smp_processor_id()];
+ }
+ dq = &hisi_hba->dq[queue];
+ }
+ break;
+ case SAS_PROTOCOL_INTERNAL_ABORT:
+ if (!hisi_hba->hw->prep_abort)
+ return TMF_RESP_FUNC_FAILED;
+
+ if (test_bit(HISI_SAS_HW_FAULT_BIT, &hisi_hba->flags))
+ return -EIO;
+
+ hisi_hba = dev_to_hisi_hba(device);
+
+ if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags)))
+ return -EINVAL;
+
+ port = to_hisi_sas_port(sas_port);
+ dq = &hisi_hba->dq[task->abort_task.qid];
+ break;
+ default:
+ dev_err(hisi_hba->dev, "task prep: unknown/unsupported proto (0x%x)\n",
+ task->task_proto);
+ return -EINVAL;
+ }
+
+ rc = hisi_sas_dma_map(hisi_hba, task, &n_elem);
+ if (rc < 0)
+ goto prep_out;
+
+ if (!sas_protocol_ata(task->task_proto)) {
+ rc = hisi_sas_dif_dma_map(hisi_hba, &n_elem_dif, task);
+ if (rc < 0)
+ goto err_out_dma_unmap;
+ }
+
+ if (!internal_abort && hisi_hba->hw->slot_index_alloc)
+ rc = hisi_hba->hw->slot_index_alloc(hisi_hba, device);
+ else
+ rc = hisi_sas_slot_index_alloc(hisi_hba, rq);
+
+ if (rc < 0)
+ goto err_out_dif_dma_unmap;
+
+ slot = &hisi_hba->slot_info[rc];
+ slot->n_elem = n_elem;
+ slot->n_elem_dif = n_elem_dif;
+ slot->task = task;
+ slot->port = port;
+
+ slot->tmf = task->tmf;
+ slot->is_internal = !!task->tmf || internal_abort;
+
+ /* protect task_prep and start_delivery sequence */
+ hisi_sas_task_deliver(hisi_hba, slot, dq, sas_dev);
+
+ return 0;
+
+err_out_dif_dma_unmap:
+ if (!sas_protocol_ata(task->task_proto))
+ hisi_sas_dif_dma_unmap(hisi_hba, task, n_elem_dif);
+err_out_dma_unmap:
+ hisi_sas_dma_unmap(hisi_hba, task, n_elem);
+prep_out:
+ dev_err(dev, "task exec: failed[%d]!\n", rc);
+ return rc;
+}
+
+static void hisi_sas_bytes_dmaed(struct hisi_hba *hisi_hba, int phy_no,
+ gfp_t gfp_flags)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ if (!phy->phy_attached)
+ return;
+
+ sas_notify_phy_event(sas_phy, PHYE_OOB_DONE, gfp_flags);
+
+ if (sas_phy->phy) {
+ struct sas_phy *sphy = sas_phy->phy;
+
+ sphy->negotiated_linkrate = sas_phy->linkrate;
+ sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sphy->maximum_linkrate_hw =
+ hisi_hba->hw->phy_get_max_linkrate();
+ if (sphy->minimum_linkrate == SAS_LINK_RATE_UNKNOWN)
+ sphy->minimum_linkrate = phy->minimum_linkrate;
+
+ if (sphy->maximum_linkrate == SAS_LINK_RATE_UNKNOWN)
+ sphy->maximum_linkrate = phy->maximum_linkrate;
+ }
+
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ struct sas_identify_frame *id;
+
+ id = (struct sas_identify_frame *)phy->frame_rcvd;
+ id->dev_type = phy->identify.device_type;
+ id->initiator_bits = SAS_PROTOCOL_ALL;
+ id->target_bits = phy->identify.target_port_protocols;
+ } else if (phy->phy_type & PORT_TYPE_SATA) {
+ /* Nothing */
+ }
+
+ sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
+ sas_notify_port_event(sas_phy, PORTE_BYTES_DMAED, gfp_flags);
+}
+
+static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device)
+{
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct hisi_sas_device *sas_dev = NULL;
+ int last = hisi_hba->last_dev_id;
+ int first = (hisi_hba->last_dev_id + 1) % HISI_SAS_MAX_DEVICES;
+ int i;
+
+ spin_lock(&hisi_hba->lock);
+ for (i = first; i != last; i %= HISI_SAS_MAX_DEVICES) {
+ if (hisi_hba->devices[i].dev_type == SAS_PHY_UNUSED) {
+ int queue = i % hisi_hba->queue_count;
+ struct hisi_sas_dq *dq = &hisi_hba->dq[queue];
+
+ hisi_hba->devices[i].device_id = i;
+ sas_dev = &hisi_hba->devices[i];
+ sas_dev->dev_status = HISI_SAS_DEV_INIT;
+ sas_dev->dev_type = device->dev_type;
+ sas_dev->hisi_hba = hisi_hba;
+ sas_dev->sas_device = device;
+ sas_dev->dq = dq;
+ spin_lock_init(&sas_dev->lock);
+ INIT_LIST_HEAD(&hisi_hba->devices[i].list);
+ break;
+ }
+ i++;
+ }
+ hisi_hba->last_dev_id = i;
+ spin_unlock(&hisi_hba->lock);
+
+ return sas_dev;
+}
+
+static void hisi_sas_sync_poll_cq(struct hisi_sas_cq *cq)
+{
+ /* make sure CQ entries being processed are processed to completion */
+ spin_lock(&cq->poll_lock);
+ spin_unlock(&cq->poll_lock);
+}
+
+static bool hisi_sas_queue_is_poll(struct hisi_sas_cq *cq)
+{
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+
+ if (cq->id < hisi_hba->queue_count - hisi_hba->iopoll_q_cnt)
+ return false;
+ return true;
+}
+
+static void hisi_sas_sync_cq(struct hisi_sas_cq *cq)
+{
+ if (hisi_sas_queue_is_poll(cq))
+ hisi_sas_sync_poll_cq(cq);
+ else
+ synchronize_irq(cq->irq_no);
+}
+
+void hisi_sas_sync_poll_cqs(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+
+ if (hisi_sas_queue_is_poll(cq))
+ hisi_sas_sync_poll_cq(cq);
+ }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sync_poll_cqs);
+
+void hisi_sas_sync_cqs(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+
+ hisi_sas_sync_cq(cq);
+ }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sync_cqs);
+
+static void hisi_sas_tmf_aborted(struct sas_task *task)
+{
+ struct hisi_sas_slot *slot = task->lldd_task;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = sas_dev->hisi_hba;
+
+ if (slot) {
+ struct hisi_sas_cq *cq =
+ &hisi_hba->cq[slot->dlvry_queue];
+ /*
+ * sync irq or poll queue to avoid free'ing task
+ * before using task in IO completion
+ */
+ hisi_sas_sync_cq(cq);
+ slot->task = NULL;
+ }
+}
+
+#define HISI_SAS_DISK_RECOVER_CNT 3
+static int hisi_sas_init_device(struct domain_device *device)
+{
+ int rc = TMF_RESP_FUNC_COMPLETE;
+ struct scsi_lun lun;
+ int retry = HISI_SAS_DISK_RECOVER_CNT;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+
+ switch (device->dev_type) {
+ case SAS_END_DEVICE:
+ int_to_scsilun(0, &lun);
+
+ while (retry-- > 0) {
+ rc = sas_abort_task_set(device, lun.scsi_lun);
+ if (rc == TMF_RESP_FUNC_COMPLETE) {
+ hisi_sas_release_task(hisi_hba, device);
+ break;
+ }
+ }
+ break;
+ case SAS_SATA_DEV:
+ case SAS_SATA_PM:
+ case SAS_SATA_PM_PORT:
+ case SAS_SATA_PENDING:
+ /*
+ * If an expander is swapped when a SATA disk is attached then
+ * we should issue a hard reset to clear previous affiliation
+ * of STP target port, see SPL (chapter 6.19.4).
+ *
+ * However we don't need to issue a hard reset here for these
+ * reasons:
+ * a. When probing the device, libsas/libata already issues a
+ * hard reset in sas_probe_sata() -> ata_port_probe().
+ * Note that in hisi_sas_debug_I_T_nexus_reset() we take care
+ * to issue a hard reset by checking the dev status (== INIT).
+ * b. When resetting the controller, this is simply unnecessary.
+ */
+ while (retry-- > 0) {
+ rc = hisi_sas_softreset_ata_disk(device);
+ if (!rc)
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+int hisi_sas_sdev_init(struct scsi_device *sdev)
+{
+ struct domain_device *ddev = sdev_to_domain_dev(sdev);
+ struct hisi_sas_device *sas_dev = ddev->lldd_dev;
+ int rc;
+
+ rc = sas_sdev_init(sdev);
+ if (rc)
+ return rc;
+
+ rc = hisi_sas_init_device(ddev);
+ if (rc)
+ return rc;
+ sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sdev_init);
+
+static int hisi_sas_dev_found(struct domain_device *device)
+{
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct domain_device *parent_dev = device->parent;
+ struct hisi_sas_device *sas_dev;
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ if (hisi_hba->hw->alloc_dev)
+ sas_dev = hisi_hba->hw->alloc_dev(device);
+ else
+ sas_dev = hisi_sas_alloc_dev(device);
+ if (!sas_dev) {
+ dev_err(dev, "fail alloc dev: max support %d devices\n",
+ HISI_SAS_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+ device->lldd_dev = sas_dev;
+ hisi_hba->hw->setup_itct(hisi_hba, sas_dev);
+
+ if (dev_parent_is_expander(device)) {
+ int phy_no;
+
+ phy_no = sas_find_attached_phy_id(&parent_dev->ex_dev, device);
+ if (phy_no < 0) {
+ dev_info(dev, "dev found: no attached "
+ "dev:%016llx at ex:%016llx\n",
+ SAS_ADDR(device->sas_addr),
+ SAS_ADDR(parent_dev->sas_addr));
+ rc = phy_no;
+ goto err_out;
+ }
+ }
+
+ dev_info(dev, "dev[%d:%x] found\n",
+ sas_dev->device_id, sas_dev->dev_type);
+
+ return 0;
+
+err_out:
+ hisi_sas_dev_gone(device);
+ return rc;
+}
+
+int hisi_sas_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
+{
+ struct domain_device *dev = sdev_to_domain_dev(sdev);
+ int ret = sas_sdev_configure(sdev, lim);
+
+ if (ret)
+ return ret;
+ if (!dev_is_sata(dev))
+ sas_change_queue_depth(sdev, HISI_SAS_BLK_QUEUE_DEPTH);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sdev_configure);
+
+void hisi_sas_scan_start(struct Scsi_Host *shost)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+
+ hisi_hba->hw->phys_init(hisi_hba);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_scan_start);
+
+int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ struct sas_ha_struct *sha = &hisi_hba->sha;
+
+ /* Wait for PHY up interrupt to occur */
+ if (time < HZ)
+ return 0;
+
+ sas_drain_work(sha);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_scan_finished);
+
+static void hisi_sas_phyup_work_common(struct work_struct *work,
+ enum hisi_sas_phy_event event)
+{
+ struct hisi_sas_phy *phy =
+ container_of(work, typeof(*phy), works[event]);
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct asd_sas_port *sas_port = sas_phy->port;
+ struct hisi_sas_port *port = phy->port;
+ struct device *dev = hisi_hba->dev;
+ struct domain_device *port_dev;
+ int phy_no = sas_phy->id;
+
+ if (!test_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags) &&
+ sas_port && port && (port->id != phy->port_id)) {
+ dev_info(dev, "phy%d's hw port id changed from %d to %llu\n",
+ phy_no, port->id, phy->port_id);
+ port_dev = sas_port->port_dev;
+ if (port_dev && !dev_is_expander(port_dev->dev_type)) {
+ /*
+ * Set the device state to gone to block
+ * sending IO to the device.
+ */
+ set_bit(SAS_DEV_GONE, &port_dev->state);
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+ return;
+ }
+ }
+
+ phy->wait_phyup_cnt = 0;
+ if (phy->identify.target_port_protocols == SAS_PROTOCOL_SSP)
+ hisi_hba->hw->sl_notify_ssp(hisi_hba, phy_no);
+ hisi_sas_bytes_dmaed(hisi_hba, phy_no, GFP_KERNEL);
+}
+
+static void hisi_sas_phyup_work(struct work_struct *work)
+{
+ hisi_sas_phyup_work_common(work, HISI_PHYE_PHY_UP);
+}
+
+static void hisi_sas_linkreset_work(struct work_struct *work)
+{
+ struct hisi_sas_phy *phy =
+ container_of(work, typeof(*phy), works[HISI_PHYE_LINK_RESET]);
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ hisi_sas_control_phy(sas_phy, PHY_FUNC_LINK_RESET, NULL);
+}
+
+static void hisi_sas_phyup_pm_work(struct work_struct *work)
+{
+ struct hisi_sas_phy *phy =
+ container_of(work, typeof(*phy), works[HISI_PHYE_PHY_UP_PM]);
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = hisi_hba->dev;
+
+ hisi_sas_phyup_work_common(work, HISI_PHYE_PHY_UP_PM);
+ pm_runtime_put_sync(dev);
+}
+
+static const work_func_t hisi_sas_phye_fns[HISI_PHYES_NUM] = {
+ [HISI_PHYE_PHY_UP] = hisi_sas_phyup_work,
+ [HISI_PHYE_LINK_RESET] = hisi_sas_linkreset_work,
+ [HISI_PHYE_PHY_UP_PM] = hisi_sas_phyup_pm_work,
+};
+
+bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,
+ enum hisi_sas_phy_event event)
+{
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+
+ if (WARN_ON(event >= HISI_PHYES_NUM))
+ return false;
+
+ return queue_work(hisi_hba->wq, &phy->works[event]);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event);
+
+static void hisi_sas_wait_phyup_timedout(struct timer_list *t)
+{
+ struct hisi_sas_phy *phy = timer_container_of(phy, t, timer);
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = hisi_hba->dev;
+ int phy_no = phy->sas_phy.id;
+
+ dev_warn(dev, "phy%d wait phyup timeout, issuing link reset\n", phy_no);
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+}
+
+#define HISI_SAS_WAIT_PHYUP_RETRIES 10
+
+void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct device *dev = hisi_hba->dev;
+ unsigned long flags;
+
+ dev_dbg(dev, "phy%d OOB ready\n", phy_no);
+ spin_lock_irqsave(&phy->lock, flags);
+ if (phy->phy_attached) {
+ spin_unlock_irqrestore(&phy->lock, flags);
+ return;
+ }
+
+ if (!timer_pending(&phy->timer)) {
+ if (phy->wait_phyup_cnt < HISI_SAS_WAIT_PHYUP_RETRIES) {
+ phy->wait_phyup_cnt++;
+ phy->timer.expires = jiffies +
+ HISI_SAS_WAIT_PHYUP_TIMEOUT;
+ add_timer(&phy->timer);
+ spin_unlock_irqrestore(&phy->lock, flags);
+ return;
+ }
+
+ dev_warn(dev, "phy%d failed to come up %d times, giving up\n",
+ phy_no, phy->wait_phyup_cnt);
+ phy->wait_phyup_cnt = 0;
+ }
+ spin_unlock_irqrestore(&phy->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(hisi_sas_phy_oob_ready);
+
+static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ int i;
+
+ phy->hisi_hba = hisi_hba;
+ phy->port = NULL;
+ phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ phy->maximum_linkrate = hisi_hba->hw->phy_get_max_linkrate();
+ sas_phy->enabled = (phy_no < hisi_hba->n_phy) ? 1 : 0;
+ sas_phy->iproto = SAS_PROTOCOL_ALL;
+ sas_phy->tproto = 0;
+ sas_phy->role = PHY_ROLE_INITIATOR;
+ sas_phy->oob_mode = OOB_NOT_CONNECTED;
+ sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
+ sas_phy->id = phy_no;
+ sas_phy->sas_addr = &hisi_hba->sas_addr[0];
+ sas_phy->frame_rcvd = &phy->frame_rcvd[0];
+ sas_phy->ha = (struct sas_ha_struct *)hisi_hba->shost->hostdata;
+ sas_phy->lldd_phy = phy;
+
+ for (i = 0; i < HISI_PHYES_NUM; i++)
+ INIT_WORK(&phy->works[i], hisi_sas_phye_fns[i]);
+
+ spin_lock_init(&phy->lock);
+
+ timer_setup(&phy->timer, hisi_sas_wait_phyup_timedout, 0);
+}
+
+/* Wrapper to ensure we track hisi_sas_phy.enable properly */
+void hisi_sas_phy_enable(struct hisi_hba *hisi_hba, int phy_no, int enable)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *aphy = &phy->sas_phy;
+ struct sas_phy *sphy = aphy->phy;
+ unsigned long flags;
+
+ spin_lock_irqsave(&phy->lock, flags);
+
+ if (enable) {
+ /* We may have been enabled already; if so, don't touch */
+ if (!phy->enable)
+ sphy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
+ hisi_hba->hw->phy_start(hisi_hba, phy_no);
+ } else {
+ sphy->negotiated_linkrate = SAS_PHY_DISABLED;
+ hisi_hba->hw->phy_disable(hisi_hba, phy_no);
+ }
+ phy->enable = enable;
+ spin_unlock_irqrestore(&phy->lock, flags);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_phy_enable);
+
+static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy)
+{
+ struct hisi_sas_phy *phy = sas_phy->lldd_phy;
+ struct asd_sas_port *sas_port = sas_phy->port;
+ struct hisi_sas_port *port;
+
+ if (!sas_port)
+ return;
+
+ port = to_hisi_sas_port(sas_port);
+ port->port_attached = 1;
+ port->id = phy->port_id;
+ phy->port = port;
+ sas_port->lldd_port = port;
+}
+
+static void hisi_sas_do_release_task(struct hisi_hba *hisi_hba, struct sas_task *task,
+ struct hisi_sas_slot *slot, bool need_lock)
+{
+ if (task) {
+ unsigned long flags;
+ struct task_status_struct *ts;
+
+ ts = &task->task_status;
+
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ if (!slot->is_internal && task->task_proto != SAS_PROTOCOL_SMP)
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ }
+
+ hisi_sas_slot_task_free(hisi_hba, task, slot, need_lock);
+}
+
+static void hisi_sas_release_task(struct hisi_hba *hisi_hba,
+ struct domain_device *device)
+{
+ struct hisi_sas_slot *slot, *slot2;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+
+ spin_lock(&sas_dev->lock);
+ list_for_each_entry_safe(slot, slot2, &sas_dev->list, entry)
+ hisi_sas_do_release_task(hisi_hba, slot->task, slot, false);
+
+ spin_unlock(&sas_dev->lock);
+}
+
+void hisi_sas_release_tasks(struct hisi_hba *hisi_hba)
+{
+ struct hisi_sas_device *sas_dev;
+ struct domain_device *device;
+ int i;
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ sas_dev = &hisi_hba->devices[i];
+ device = sas_dev->sas_device;
+
+ if ((sas_dev->dev_type == SAS_PHY_UNUSED) ||
+ !device)
+ continue;
+
+ hisi_sas_release_task(hisi_hba, device);
+ }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_release_tasks);
+
+static void hisi_sas_dereg_device(struct hisi_hba *hisi_hba,
+ struct domain_device *device)
+{
+ if (hisi_hba->hw->dereg_device)
+ hisi_hba->hw->dereg_device(hisi_hba, device);
+}
+
+static int
+hisi_sas_internal_task_abort_dev(struct hisi_sas_device *sas_dev,
+ bool rst_ha_timeout)
+{
+ struct hisi_sas_internal_abort_data data = { rst_ha_timeout };
+ struct domain_device *device = sas_dev->sas_device;
+ struct hisi_hba *hisi_hba = sas_dev->hisi_hba;
+ int i, rc;
+
+ for (i = 0; i < hisi_hba->cq_nvecs; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+ const struct cpumask *mask = cq->irq_mask;
+
+ if (mask && !cpumask_intersects(cpu_online_mask, mask))
+ continue;
+ rc = sas_execute_internal_abort_dev(device, i, &data);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static void hisi_sas_dev_gone(struct domain_device *device)
+{
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = hisi_hba->dev;
+ int ret = 0;
+
+ dev_info(dev, "dev[%d:%x] is gone\n",
+ sas_dev->device_id, sas_dev->dev_type);
+
+ down(&hisi_hba->sem);
+ if (!test_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags)) {
+ hisi_sas_internal_task_abort_dev(sas_dev, true);
+
+ hisi_sas_dereg_device(hisi_hba, device);
+
+ ret = hisi_hba->hw->clear_itct(hisi_hba, sas_dev);
+ device->lldd_dev = NULL;
+ }
+
+ if (hisi_hba->hw->free_device)
+ hisi_hba->hw->free_device(sas_dev);
+
+ /* Don't mark it as SAS_PHY_UNUSED if failed to clear ITCT */
+ if (!ret)
+ sas_dev->dev_type = SAS_PHY_UNUSED;
+ sas_dev->sas_device = NULL;
+ up(&hisi_hba->sem);
+}
+
+static int hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no,
+ struct sas_phy_linkrates *r)
+{
+ struct sas_phy_linkrates _r;
+
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ enum sas_linkrate min, max;
+
+ if (r->minimum_linkrate > SAS_LINK_RATE_1_5_GBPS)
+ return -EINVAL;
+
+ if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) {
+ max = sas_phy->phy->maximum_linkrate;
+ min = r->minimum_linkrate;
+ } else if (r->minimum_linkrate == SAS_LINK_RATE_UNKNOWN) {
+ max = r->maximum_linkrate;
+ min = sas_phy->phy->minimum_linkrate;
+ } else
+ return -EINVAL;
+
+ _r.maximum_linkrate = max;
+ _r.minimum_linkrate = min;
+
+ sas_phy->phy->maximum_linkrate = max;
+ sas_phy->phy->minimum_linkrate = min;
+
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+ msleep(HISI_SAS_DELAY_FOR_PHY_DISABLE);
+ hisi_hba->hw->phy_set_linkrate(hisi_hba, phy_no, &_r);
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+
+ return 0;
+}
+
+static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata)
+{
+ struct hisi_sas_phy *phy = container_of(sas_phy,
+ struct hisi_sas_phy, sas_phy);
+ struct sas_ha_struct *sas_ha = sas_phy->ha;
+ struct hisi_hba *hisi_hba = sas_ha->lldd_ha;
+ struct device *dev = hisi_hba->dev;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ int phy_no = sas_phy->id;
+ u8 sts = phy->phy_attached;
+ int ret = 0;
+
+ down(&hisi_hba->sem);
+ phy->reset_completion = &completion;
+
+ switch (func) {
+ case PHY_FUNC_HARD_RESET:
+ hisi_hba->hw->phy_hard_reset(hisi_hba, phy_no);
+ break;
+
+ case PHY_FUNC_LINK_RESET:
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+ msleep(HISI_SAS_DELAY_FOR_PHY_DISABLE);
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+ break;
+
+ case PHY_FUNC_DISABLE:
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+ goto out;
+
+ case PHY_FUNC_SET_LINK_RATE:
+ ret = hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata);
+ break;
+
+ case PHY_FUNC_GET_EVENTS:
+ if (hisi_hba->hw->get_events) {
+ hisi_hba->hw->get_events(hisi_hba, phy_no);
+ goto out;
+ }
+ fallthrough;
+ case PHY_FUNC_RELEASE_SPINUP_HOLD:
+ default:
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (sts && !wait_for_completion_timeout(&completion,
+ HISI_SAS_WAIT_PHYUP_TIMEOUT)) {
+ dev_warn(dev, "phy%d wait phyup timed out for func %d\n",
+ phy_no, func);
+ if (phy->in_reset)
+ ret = -ETIMEDOUT;
+ }
+
+out:
+ phy->reset_completion = NULL;
+
+ up(&hisi_hba->sem);
+ return ret;
+}
+
+static void hisi_sas_fill_ata_reset_cmd(struct ata_device *dev,
+ bool reset, int pmp, u8 *fis)
+{
+ struct ata_taskfile tf;
+
+ ata_tf_init(dev, &tf);
+ if (reset)
+ tf.ctl |= ATA_SRST;
+ else
+ tf.ctl &= ~ATA_SRST;
+ tf.command = ATA_CMD_DEV_RESET;
+ ata_tf_to_fis(&tf, pmp, 0, fis);
+}
+
+static int hisi_sas_softreset_ata_disk(struct domain_device *device)
+{
+ u8 fis[FIS_BUF_SIZE] = {0};
+ struct ata_port *ap = device->sata_dev.ap;
+ struct ata_link *link;
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = hisi_hba->dev;
+
+ ata_for_each_link(link, ap, EDGE) {
+ int pmp = sata_srst_pmp(link);
+
+ hisi_sas_fill_ata_reset_cmd(link->device, 1, pmp, fis);
+ rc = sas_execute_ata_cmd(device, fis, -1);
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ break;
+ }
+
+ if (rc == TMF_RESP_FUNC_COMPLETE) {
+ usleep_range(DELAY_FOR_SOFTRESET_MIN, DELAY_FOR_SOFTRESET_MAX);
+ ata_for_each_link(link, ap, EDGE) {
+ int pmp = sata_srst_pmp(link);
+
+ hisi_sas_fill_ata_reset_cmd(link->device, 0, pmp, fis);
+ rc = sas_execute_ata_cmd(device, fis, -1);
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ dev_err(dev, "ata disk %016llx de-reset failed\n",
+ SAS_ADDR(device->sas_addr));
+ }
+ } else {
+ dev_err(dev, "ata disk %016llx reset failed\n",
+ SAS_ADDR(device->sas_addr));
+ }
+
+ if (rc == TMF_RESP_FUNC_COMPLETE)
+ hisi_sas_release_task(hisi_hba, device);
+
+ return rc;
+}
+
+static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba)
+{
+ u32 state = hisi_hba->hw->get_phys_state(hisi_hba);
+ int i;
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ struct hisi_sas_device *sas_dev = &hisi_hba->devices[i];
+ struct domain_device *device = sas_dev->sas_device;
+ struct asd_sas_port *sas_port;
+ struct hisi_sas_port *port;
+ struct hisi_sas_phy *phy = NULL;
+ struct asd_sas_phy *sas_phy;
+
+ if ((sas_dev->dev_type == SAS_PHY_UNUSED)
+ || !device || !device->port)
+ continue;
+
+ sas_port = device->port;
+ port = to_hisi_sas_port(sas_port);
+
+ spin_lock(&sas_port->phy_list_lock);
+ list_for_each_entry(sas_phy, &sas_port->phy_list, port_phy_el)
+ if (state & BIT(sas_phy->id)) {
+ phy = sas_phy->lldd_phy;
+ break;
+ }
+ spin_unlock(&sas_port->phy_list_lock);
+
+ if (phy) {
+ port->id = phy->port_id;
+
+ /* Update linkrate of directly attached device. */
+ if (!device->parent)
+ device->linkrate = phy->sas_phy.linkrate;
+
+ hisi_hba->hw->setup_itct(hisi_hba, sas_dev);
+ } else if (!port->port_attached)
+ port->id = 0xff;
+ }
+}
+
+static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 state)
+{
+ u32 new_state = hisi_hba->hw->get_phys_state(hisi_hba);
+ struct asd_sas_port *_sas_port = NULL;
+ int phy_no;
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct asd_sas_port *sas_port = sas_phy->port;
+ bool do_port_check = _sas_port != sas_port;
+
+ if (!sas_phy->phy->enabled)
+ continue;
+
+ /* Report PHY state change to libsas */
+ if (new_state & BIT(phy_no)) {
+ if (do_port_check && sas_port && sas_port->port_dev) {
+ struct domain_device *dev = sas_port->port_dev;
+
+ _sas_port = sas_port;
+
+ if (dev_is_expander(dev->dev_type))
+ sas_notify_port_event(sas_phy,
+ PORTE_BROADCAST_RCVD,
+ GFP_KERNEL);
+ }
+ } else {
+ hisi_sas_phy_down(hisi_hba, phy_no, 0, GFP_KERNEL);
+
+ /*
+ * The new_state is not ready but old_state is ready,
+ * the two possible causes:
+ * 1. The connected device is removed
+ * 2. Device exists but phyup timed out
+ */
+ if (state & BIT(phy_no))
+ hisi_sas_notify_phy_event(phy,
+ HISI_PHYE_LINK_RESET);
+ }
+ }
+}
+
+static void hisi_sas_reset_init_all_devices(struct hisi_hba *hisi_hba)
+{
+ struct hisi_sas_device *sas_dev;
+ struct domain_device *device;
+ int i;
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ sas_dev = &hisi_hba->devices[i];
+ device = sas_dev->sas_device;
+
+ if ((sas_dev->dev_type == SAS_PHY_UNUSED) || !device)
+ continue;
+
+ hisi_sas_init_device(device);
+ }
+}
+
+static void hisi_sas_send_ata_reset_each_phy(struct hisi_hba *hisi_hba,
+ struct asd_sas_port *sas_port,
+ struct domain_device *device)
+{
+ struct ata_port *ap = device->sata_dev.ap;
+ struct device *dev = hisi_hba->dev;
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct ata_link *link;
+ u8 fis[FIS_BUF_SIZE] = {0};
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ if (!(sas_port->phy_mask & BIT(i)))
+ continue;
+
+ ata_for_each_link(link, ap, EDGE) {
+ int pmp = sata_srst_pmp(link);
+
+ hisi_sas_fill_ata_reset_cmd(link->device, 1, pmp, fis);
+ rc = sas_execute_ata_cmd(device, fis, i);
+ if (rc != TMF_RESP_FUNC_COMPLETE) {
+ dev_err(dev, "phy%d ata reset failed rc=%d\n",
+ i, rc);
+ break;
+ }
+ }
+ }
+}
+
+static void hisi_sas_terminate_stp_reject(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ int port_no, rc, i;
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ struct hisi_sas_device *sas_dev = &hisi_hba->devices[i];
+ struct domain_device *device = sas_dev->sas_device;
+
+ if ((sas_dev->dev_type == SAS_PHY_UNUSED) || !device)
+ continue;
+
+ rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
+ if (rc < 0)
+ dev_err(dev, "STP reject: abort dev failed %d\n", rc);
+ }
+
+ for (port_no = 0; port_no < hisi_hba->n_phy; port_no++) {
+ struct hisi_sas_port *port = &hisi_hba->port[port_no];
+ struct asd_sas_port *sas_port = &port->sas_port;
+ struct domain_device *port_dev = sas_port->port_dev;
+ struct domain_device *device;
+
+ if (!port_dev || !dev_is_expander(port_dev->dev_type))
+ continue;
+
+ /* Try to find a SATA device */
+ list_for_each_entry(device, &sas_port->dev_list,
+ dev_list_node) {
+ if (dev_is_sata(device)) {
+ hisi_sas_send_ata_reset_each_phy(hisi_hba,
+ sas_port,
+ device);
+ break;
+ }
+ }
+ }
+}
+
+void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba)
+{
+ struct Scsi_Host *shost = hisi_hba->shost;
+
+ hisi_hba->phy_state = hisi_hba->hw->get_phys_state(hisi_hba);
+
+ scsi_block_requests(shost);
+ hisi_hba->hw->wait_cmds_complete_timeout(hisi_hba,
+ WAIT_CMD_COMPLETE_DELAY,
+ WAIT_CMD_COMPLETE_TMROUT);
+
+ /*
+ * hisi_hba->timer is only used for v1/v2 hw, and check hw->sht
+ * which is also only used for v1/v2 hw to skip it for v3 hw
+ */
+ if (hisi_hba->hw->sht)
+ timer_delete_sync(&hisi_hba->timer);
+
+ set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_controller_reset_prepare);
+
+static void hisi_sas_async_init_wait_phyup(void *data, async_cookie_t cookie)
+{
+ struct hisi_sas_phy *phy = data;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = hisi_hba->dev;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ int phy_no = phy->sas_phy.id;
+
+ phy->reset_completion = &completion;
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+ if (!wait_for_completion_timeout(&completion,
+ HISI_SAS_WAIT_PHYUP_TIMEOUT))
+ dev_warn(dev, "phy%d wait phyup timed out\n", phy_no);
+
+ phy->reset_completion = NULL;
+}
+
+void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba)
+{
+ struct Scsi_Host *shost = hisi_hba->shost;
+ ASYNC_DOMAIN_EXCLUSIVE(async);
+ int phy_no;
+
+ /* Init and wait for PHYs to come up and all libsas event finished. */
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ if (!sas_phy->phy->enabled)
+ continue;
+
+ if (!(hisi_hba->phy_state & BIT(phy_no))) {
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+ continue;
+ }
+
+ async_schedule_domain(hisi_sas_async_init_wait_phyup,
+ phy, &async);
+ }
+
+ async_synchronize_full_domain(&async);
+ hisi_sas_refresh_port_id(hisi_hba);
+ clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+
+ if (hisi_hba->reject_stp_links_msk)
+ hisi_sas_terminate_stp_reject(hisi_hba);
+ hisi_sas_reset_init_all_devices(hisi_hba);
+ scsi_unblock_requests(shost);
+ clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+ up(&hisi_hba->sem);
+
+ hisi_sas_rescan_topology(hisi_hba, hisi_hba->phy_state);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_controller_reset_done);
+
+static int hisi_sas_controller_prereset(struct hisi_hba *hisi_hba)
+{
+ if (!hisi_hba->hw->soft_reset)
+ return -ENOENT;
+
+ down(&hisi_hba->sem);
+ if (test_and_set_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags)) {
+ up(&hisi_hba->sem);
+ return -EPERM;
+ }
+
+ if (hisi_sas_debugfs_enable)
+ hisi_hba->hw->debugfs_snapshot_regs(hisi_hba);
+
+ return 0;
+}
+
+static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ struct Scsi_Host *shost = hisi_hba->shost;
+ int rc;
+
+ dev_info(dev, "controller resetting...\n");
+ hisi_sas_controller_reset_prepare(hisi_hba);
+
+ rc = hisi_hba->hw->soft_reset(hisi_hba);
+ if (rc) {
+ dev_warn(dev, "controller reset failed (%d)\n", rc);
+ clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+ up(&hisi_hba->sem);
+ scsi_unblock_requests(shost);
+ clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+ return rc;
+ }
+ clear_bit(HISI_SAS_HW_FAULT_BIT, &hisi_hba->flags);
+
+ hisi_sas_controller_reset_done(hisi_hba);
+ dev_info(dev, "controller reset complete\n");
+
+ return 0;
+}
+
+static int hisi_sas_abort_task(struct sas_task *task)
+{
+ struct hisi_sas_internal_abort_data internal_abort_data = { false };
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_slot *slot = task->lldd_task;
+ struct hisi_hba *hisi_hba;
+ struct device *dev;
+ int rc = TMF_RESP_FUNC_FAILED;
+ unsigned long flags;
+
+ if (!sas_dev)
+ return TMF_RESP_FUNC_FAILED;
+
+ hisi_hba = dev_to_hisi_hba(task->dev);
+ dev = hisi_hba->dev;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+ struct hisi_sas_cq *cq;
+
+ if (slot) {
+ /*
+ * sync irq or poll queue to avoid free'ing task
+ * before using task in IO completion
+ */
+ cq = &hisi_hba->cq[slot->dlvry_queue];
+ hisi_sas_sync_cq(cq);
+ }
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ rc = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ }
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ if (!slot)
+ goto out;
+
+ if (task->task_proto & SAS_PROTOCOL_SSP) {
+ u16 tag = slot->idx;
+ int rc2;
+
+ rc = sas_abort_task(task, tag);
+ rc2 = sas_execute_internal_abort_single(device, tag,
+ slot->dlvry_queue, &internal_abort_data);
+ if (rc2 < 0) {
+ dev_err(dev, "abort task: internal abort (%d)\n", rc2);
+ return TMF_RESP_FUNC_FAILED;
+ }
+
+ /*
+ * If the TMF finds that the IO is not in the device and also
+ * the internal abort does not succeed, then it is safe to
+ * free the slot.
+ * Note: if the internal abort succeeds then the slot
+ * will have already been completed
+ */
+ if (rc == TMF_RESP_FUNC_COMPLETE && rc2 != TMF_RESP_FUNC_SUCC) {
+ if (task->lldd_task)
+ hisi_sas_do_release_task(hisi_hba, task, slot, true);
+ }
+ } else if (task->task_proto & SAS_PROTOCOL_SATA ||
+ task->task_proto & SAS_PROTOCOL_STP) {
+ if (task->dev->dev_type == SAS_SATA_DEV) {
+ struct ata_queued_cmd *qc = task->uldd_task;
+
+ rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
+ if (rc < 0) {
+ dev_err(dev, "abort task: internal abort failed\n");
+ goto out;
+ }
+ hisi_sas_dereg_device(hisi_hba, device);
+
+ /*
+ * If an ATA internal command times out in ATA EH, it
+ * need to execute soft reset, so check the scsicmd
+ */
+ if ((sas_dev->dev_status == HISI_SAS_DEV_NCQ_ERR) &&
+ qc && qc->scsicmd) {
+ hisi_sas_do_release_task(hisi_hba, task, slot, true);
+ rc = TMF_RESP_FUNC_COMPLETE;
+ } else {
+ rc = hisi_sas_softreset_ata_disk(device);
+ }
+ }
+ } else if (task->task_proto & SAS_PROTOCOL_SMP) {
+ /* SMP */
+ u32 tag = slot->idx;
+ struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue];
+
+ rc = sas_execute_internal_abort_single(device,
+ tag, slot->dlvry_queue,
+ &internal_abort_data);
+ if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
+ task->lldd_task) {
+ /*
+ * sync irq or poll queue to avoid free'ing task
+ * before using task in IO completion
+ */
+ hisi_sas_sync_cq(cq);
+ slot->task = NULL;
+ }
+ }
+
+out:
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ dev_notice(dev, "abort task: rc=%d\n", rc);
+ return rc;
+}
+
+static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun)
+{
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
+ if (rc < 0) {
+ dev_err(dev, "abort task set: internal abort rc=%d\n", rc);
+ return TMF_RESP_FUNC_FAILED;
+ }
+ hisi_sas_dereg_device(hisi_hba, device);
+
+ rc = sas_abort_task_set(device, lun);
+ if (rc == TMF_RESP_FUNC_COMPLETE)
+ hisi_sas_release_task(hisi_hba, device);
+
+ return rc;
+}
+
+static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
+{
+ struct sas_phy *local_phy = sas_get_local_phy(device);
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct sas_ha_struct *sas_ha = &hisi_hba->sha;
+ int rc, reset_type;
+
+ if (!local_phy->enabled) {
+ sas_put_local_phy(local_phy);
+ return -ENODEV;
+ }
+
+ if (scsi_is_sas_phy_local(local_phy)) {
+ struct asd_sas_phy *sas_phy =
+ sas_ha->sas_phy[local_phy->number];
+ struct hisi_sas_phy *phy =
+ container_of(sas_phy, struct hisi_sas_phy, sas_phy);
+ unsigned long flags;
+
+ spin_lock_irqsave(&phy->lock, flags);
+ phy->in_reset = 1;
+ spin_unlock_irqrestore(&phy->lock, flags);
+ }
+
+ reset_type = (sas_dev->dev_status == HISI_SAS_DEV_INIT ||
+ !dev_is_sata(device)) ? true : false;
+
+ rc = sas_phy_reset(local_phy, reset_type);
+ sas_put_local_phy(local_phy);
+
+ if (scsi_is_sas_phy_local(local_phy)) {
+ struct asd_sas_phy *sas_phy =
+ sas_ha->sas_phy[local_phy->number];
+ struct hisi_sas_phy *phy =
+ container_of(sas_phy, struct hisi_sas_phy, sas_phy);
+ unsigned long flags;
+
+ spin_lock_irqsave(&phy->lock, flags);
+ phy->in_reset = 0;
+ spin_unlock_irqrestore(&phy->lock, flags);
+
+ /* report PHY down if timed out */
+ if (rc == -ETIMEDOUT)
+ hisi_sas_phy_down(hisi_hba, sas_phy->id, 0, GFP_KERNEL);
+ return rc;
+ }
+
+ /* Remote phy */
+ if (rc)
+ return rc;
+
+ if (dev_is_sata(device)) {
+ struct ata_link *link = &device->sata_dev.ap->link;
+
+ rc = ata_wait_after_reset(link, jiffies + HISI_SAS_WAIT_PHYUP_TIMEOUT,
+ smp_ata_check_ready_type);
+ } else {
+ msleep(DELAY_FOR_LINK_READY);
+ }
+
+ return rc;
+}
+
+static int hisi_sas_I_T_nexus_reset(struct domain_device *device)
+{
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ if (sas_dev->dev_status == HISI_SAS_DEV_NCQ_ERR)
+ sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+
+ rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
+ if (rc < 0) {
+ dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc);
+ return TMF_RESP_FUNC_FAILED;
+ }
+ hisi_sas_dereg_device(hisi_hba, device);
+
+ if (dev_is_sata(device)) {
+ rc = hisi_sas_softreset_ata_disk(device);
+ if (rc == TMF_RESP_FUNC_FAILED)
+ dev_err(dev, "ata disk %016llx reset (%d)\n",
+ SAS_ADDR(device->sas_addr), rc);
+ }
+
+ rc = hisi_sas_debug_I_T_nexus_reset(device);
+ if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV))
+ hisi_sas_release_task(hisi_hba, device);
+
+ return rc;
+}
+
+static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
+{
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = hisi_hba->dev;
+ int rc = TMF_RESP_FUNC_FAILED;
+
+ /* Clear internal IO and then lu reset */
+ rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
+ if (rc < 0) {
+ dev_err(dev, "lu_reset: internal abort failed\n");
+ goto out;
+ }
+ hisi_sas_dereg_device(hisi_hba, device);
+
+ if (dev_is_sata(device)) {
+ struct sas_phy *phy = sas_get_local_phy(device);
+
+ rc = sas_phy_reset(phy, true);
+ if (rc == 0)
+ hisi_sas_release_task(hisi_hba, device);
+ sas_put_local_phy(phy);
+ } else {
+ rc = sas_lu_reset(device, lun);
+ if (rc == TMF_RESP_FUNC_COMPLETE)
+ hisi_sas_release_task(hisi_hba, device);
+ }
+out:
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ dev_err(dev, "lu_reset: for device[%d]:rc= %d\n",
+ sas_dev->device_id, rc);
+ return rc;
+}
+
+static void hisi_sas_async_I_T_nexus_reset(void *data, async_cookie_t cookie)
+{
+ struct domain_device *device = data;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ int rc;
+
+ rc = hisi_sas_debug_I_T_nexus_reset(device);
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ dev_info(hisi_hba->dev, "I_T_nexus reset fail for dev:%016llx rc=%d\n",
+ SAS_ADDR(device->sas_addr), rc);
+}
+
+static int hisi_sas_clear_nexus_ha(struct sas_ha_struct *sas_ha)
+{
+ struct hisi_hba *hisi_hba = sas_ha->lldd_ha;
+ HISI_SAS_DECLARE_RST_WORK_ON_STACK(r);
+ ASYNC_DOMAIN_EXCLUSIVE(async);
+ int i;
+
+ queue_work(hisi_hba->wq, &r.work);
+ wait_for_completion(r.completion);
+ if (!r.done)
+ return TMF_RESP_FUNC_FAILED;
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ struct hisi_sas_device *sas_dev = &hisi_hba->devices[i];
+ struct domain_device *device = sas_dev->sas_device;
+
+ if ((sas_dev->dev_type == SAS_PHY_UNUSED) || !device ||
+ dev_is_expander(device->dev_type))
+ continue;
+
+ async_schedule_domain(hisi_sas_async_I_T_nexus_reset,
+ device, &async);
+ }
+
+ async_synchronize_full_domain(&async);
+ hisi_sas_release_tasks(hisi_hba);
+
+ return TMF_RESP_FUNC_COMPLETE;
+}
+
+static int hisi_sas_query_task(struct sas_task *task)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+
+ if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
+ struct hisi_sas_slot *slot = task->lldd_task;
+ u32 tag = slot->idx;
+
+ rc = sas_query_task(task, tag);
+ switch (rc) {
+ /* The task is still in Lun, release it then */
+ case TMF_RESP_FUNC_SUCC:
+ /* The task is not in Lun or failed, reset the phy */
+ case TMF_RESP_FUNC_FAILED:
+ case TMF_RESP_FUNC_COMPLETE:
+ break;
+ default:
+ rc = TMF_RESP_FUNC_FAILED;
+ break;
+ }
+ }
+ return rc;
+}
+
+static bool hisi_sas_internal_abort_timeout(struct sas_task *task,
+ void *data)
+{
+ struct domain_device *device = task->dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct hisi_sas_internal_abort_data *timeout = data;
+
+ if (hisi_sas_debugfs_enable) {
+ /*
+ * If timeout occurs in device gone scenario, to avoid
+ * circular dependency like:
+ * hisi_sas_dev_gone() -> down() -> ... ->
+ * hisi_sas_internal_abort_timeout() -> down().
+ */
+ if (!timeout->rst_ha_timeout)
+ down(&hisi_hba->sem);
+ hisi_hba->hw->debugfs_snapshot_regs(hisi_hba);
+ if (!timeout->rst_ha_timeout)
+ up(&hisi_hba->sem);
+ }
+
+ if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+ pr_err("Internal abort: timeout %016llx\n",
+ SAS_ADDR(device->sas_addr));
+ } else {
+ struct hisi_sas_slot *slot = task->lldd_task;
+
+ set_bit(HISI_SAS_HW_FAULT_BIT, &hisi_hba->flags);
+
+ if (slot) {
+ struct hisi_sas_cq *cq =
+ &hisi_hba->cq[slot->dlvry_queue];
+ /*
+ * sync irq or poll queue to avoid free'ing task
+ * before using task in IO completion
+ */
+ hisi_sas_sync_cq(cq);
+ slot->task = NULL;
+ }
+
+ if (timeout->rst_ha_timeout) {
+ pr_err("Internal abort: timeout and not done %016llx. Queuing reset.\n",
+ SAS_ADDR(device->sas_addr));
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ } else {
+ pr_err("Internal abort: timeout and not done %016llx.\n",
+ SAS_ADDR(device->sas_addr));
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy)
+{
+ hisi_sas_port_notify_formed(sas_phy);
+}
+
+static int hisi_sas_write_gpio(struct sas_ha_struct *sha, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data)
+{
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+
+ if (!hisi_hba->hw->write_gpio)
+ return -EOPNOTSUPP;
+
+ return hisi_hba->hw->write_gpio(hisi_hba, reg_type,
+ reg_index, reg_count, write_data);
+}
+
+static void hisi_sas_phy_disconnected(struct hisi_sas_phy *phy)
+{
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_phy *sphy = sas_phy->phy;
+ unsigned long flags;
+
+ phy->phy_attached = 0;
+ phy->phy_type = 0;
+ phy->port = NULL;
+
+ spin_lock_irqsave(&phy->lock, flags);
+ if (phy->enable)
+ sphy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
+ else
+ sphy->negotiated_linkrate = SAS_PHY_DISABLED;
+ spin_unlock_irqrestore(&phy->lock, flags);
+}
+
+void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy,
+ gfp_t gfp_flags)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct device *dev = hisi_hba->dev;
+
+ if (rdy) {
+ /* Phy down but ready */
+ hisi_sas_bytes_dmaed(hisi_hba, phy_no, gfp_flags);
+ hisi_sas_port_notify_formed(sas_phy);
+ } else {
+ struct hisi_sas_port *port = phy->port;
+
+ if (test_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags) ||
+ phy->in_reset) {
+ dev_info(dev, "ignore flutter phy%d down\n", phy_no);
+ return;
+ }
+ /* Phy down and not ready */
+ sas_notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL, gfp_flags);
+ sas_phy_disconnected(sas_phy);
+
+ if (port) {
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ int port_id = port->id;
+
+ if (!hisi_hba->hw->get_wideport_bitmap(hisi_hba,
+ port_id))
+ port->port_attached = 0;
+ } else if (phy->phy_type & PORT_TYPE_SATA)
+ port->port_attached = 0;
+ }
+ hisi_sas_phy_disconnected(phy);
+ }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_phy_down);
+
+void hisi_sas_phy_bcast(struct hisi_sas_phy *phy)
+{
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+
+ if (test_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags))
+ return;
+
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD, GFP_ATOMIC);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_phy_bcast);
+
+int hisi_sas_host_reset(struct Scsi_Host *shost, int reset_type)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+
+ if (reset_type != SCSI_ADAPTER_RESET)
+ return -EOPNOTSUPP;
+
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_host_reset);
+
+struct scsi_transport_template *hisi_sas_stt;
+EXPORT_SYMBOL_GPL(hisi_sas_stt);
+
+static struct sas_domain_function_template hisi_sas_transport_ops = {
+ .lldd_dev_found = hisi_sas_dev_found,
+ .lldd_dev_gone = hisi_sas_dev_gone,
+ .lldd_execute_task = hisi_sas_queue_command,
+ .lldd_control_phy = hisi_sas_control_phy,
+ .lldd_abort_task = hisi_sas_abort_task,
+ .lldd_abort_task_set = hisi_sas_abort_task_set,
+ .lldd_I_T_nexus_reset = hisi_sas_I_T_nexus_reset,
+ .lldd_lu_reset = hisi_sas_lu_reset,
+ .lldd_query_task = hisi_sas_query_task,
+ .lldd_clear_nexus_ha = hisi_sas_clear_nexus_ha,
+ .lldd_port_formed = hisi_sas_port_formed,
+ .lldd_write_gpio = hisi_sas_write_gpio,
+ .lldd_tmf_aborted = hisi_sas_tmf_aborted,
+ .lldd_abort_timeout = hisi_sas_internal_abort_timeout,
+};
+
+void hisi_sas_init_mem(struct hisi_hba *hisi_hba)
+{
+ int i, s, j, max_command_entries = HISI_SAS_MAX_COMMANDS;
+ struct hisi_sas_breakpoint *sata_breakpoint = hisi_hba->sata_breakpoint;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+ struct hisi_sas_dq *dq = &hisi_hba->dq[i];
+ struct hisi_sas_cmd_hdr *cmd_hdr = hisi_hba->cmd_hdr[i];
+
+ s = sizeof(struct hisi_sas_cmd_hdr);
+ for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++)
+ memset(&cmd_hdr[j], 0, s);
+
+ dq->wr_point = 0;
+
+ s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
+ memset(hisi_hba->complete_hdr[i], 0, s);
+ cq->rd_point = 0;
+ }
+
+ s = sizeof(struct hisi_sas_initial_fis) * hisi_hba->n_phy;
+ memset(hisi_hba->initial_fis, 0, s);
+
+ s = max_command_entries * sizeof(struct hisi_sas_iost);
+ memset(hisi_hba->iost, 0, s);
+
+ s = max_command_entries * sizeof(struct hisi_sas_breakpoint);
+ memset(hisi_hba->breakpoint, 0, s);
+
+ s = sizeof(struct hisi_sas_sata_breakpoint);
+ for (j = 0; j < HISI_SAS_MAX_ITCT_ENTRIES; j++)
+ memset(&sata_breakpoint[j], 0, s);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_init_mem);
+
+int hisi_sas_alloc(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ int i, j, s, max_command_entries = HISI_SAS_MAX_COMMANDS;
+ int max_command_entries_ru, sz_slot_buf_ru;
+ int blk_cnt, slots_per_blk;
+
+ sema_init(&hisi_hba->sem, 1);
+ spin_lock_init(&hisi_hba->lock);
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_init(hisi_hba, i);
+ hisi_hba->port[i].port_attached = 0;
+ hisi_hba->port[i].id = -1;
+ }
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED;
+ hisi_hba->devices[i].device_id = i;
+ hisi_hba->devices[i].dev_status = HISI_SAS_DEV_INIT;
+ }
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+ struct hisi_sas_dq *dq = &hisi_hba->dq[i];
+
+ /* Completion queue structure */
+ cq->id = i;
+ cq->hisi_hba = hisi_hba;
+ spin_lock_init(&cq->poll_lock);
+
+ /* Delivery queue structure */
+ spin_lock_init(&dq->lock);
+ INIT_LIST_HEAD(&dq->list);
+ dq->id = i;
+ dq->hisi_hba = hisi_hba;
+
+ /* Delivery queue */
+ s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
+ hisi_hba->cmd_hdr[i] = dmam_alloc_coherent(dev, s,
+ &hisi_hba->cmd_hdr_dma[i],
+ GFP_KERNEL);
+ if (!hisi_hba->cmd_hdr[i])
+ goto err_out;
+
+ /* Completion queue */
+ s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
+ hisi_hba->complete_hdr[i] = dmam_alloc_coherent(dev, s,
+ &hisi_hba->complete_hdr_dma[i],
+ GFP_KERNEL);
+ if (!hisi_hba->complete_hdr[i])
+ goto err_out;
+ }
+
+ s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
+ hisi_hba->itct = dmam_alloc_coherent(dev, s, &hisi_hba->itct_dma,
+ GFP_KERNEL);
+ if (!hisi_hba->itct)
+ goto err_out;
+
+ hisi_hba->slot_info = devm_kcalloc(dev, max_command_entries,
+ sizeof(struct hisi_sas_slot),
+ GFP_KERNEL);
+ if (!hisi_hba->slot_info)
+ goto err_out;
+
+ /* roundup to avoid overly large block size */
+ max_command_entries_ru = roundup(max_command_entries,
+ BLK_CNT_OPTIMIZE_MARK);
+ if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK)
+ sz_slot_buf_ru = sizeof(struct hisi_sas_slot_dif_buf_table);
+ else
+ sz_slot_buf_ru = sizeof(struct hisi_sas_slot_buf_table);
+
+ sz_slot_buf_ru = roundup(sz_slot_buf_ru, BLK_CNT_OPTIMIZE_MARK);
+ s = max(lcm(max_command_entries_ru, sz_slot_buf_ru), PAGE_SIZE);
+ blk_cnt = (max_command_entries_ru * sz_slot_buf_ru) / s;
+ slots_per_blk = s / sz_slot_buf_ru;
+
+ for (i = 0; i < blk_cnt; i++) {
+ int slot_index = i * slots_per_blk;
+ dma_addr_t buf_dma;
+ void *buf;
+
+ buf = dmam_alloc_coherent(dev, s, &buf_dma,
+ GFP_KERNEL);
+ if (!buf)
+ goto err_out;
+
+ for (j = 0; j < slots_per_blk; j++, slot_index++) {
+ struct hisi_sas_slot *slot;
+
+ slot = &hisi_hba->slot_info[slot_index];
+ slot->buf = buf;
+ slot->buf_dma = buf_dma;
+ slot->idx = slot_index;
+
+ buf += sz_slot_buf_ru;
+ buf_dma += sz_slot_buf_ru;
+ }
+ }
+
+ s = max_command_entries * sizeof(struct hisi_sas_iost);
+ hisi_hba->iost = dmam_alloc_coherent(dev, s, &hisi_hba->iost_dma,
+ GFP_KERNEL);
+ if (!hisi_hba->iost)
+ goto err_out;
+
+ s = max_command_entries * sizeof(struct hisi_sas_breakpoint);
+ hisi_hba->breakpoint = dmam_alloc_coherent(dev, s,
+ &hisi_hba->breakpoint_dma,
+ GFP_KERNEL);
+ if (!hisi_hba->breakpoint)
+ goto err_out;
+
+ s = hisi_hba->slot_index_count = max_command_entries;
+ hisi_hba->slot_index_tags = devm_bitmap_zalloc(dev, s, GFP_KERNEL);
+ if (!hisi_hba->slot_index_tags)
+ goto err_out;
+
+ s = sizeof(struct hisi_sas_initial_fis) * HISI_SAS_MAX_PHYS;
+ hisi_hba->initial_fis = dmam_alloc_coherent(dev, s,
+ &hisi_hba->initial_fis_dma,
+ GFP_KERNEL);
+ if (!hisi_hba->initial_fis)
+ goto err_out;
+
+ s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_sata_breakpoint);
+ hisi_hba->sata_breakpoint = dmam_alloc_coherent(dev, s,
+ &hisi_hba->sata_breakpoint_dma,
+ GFP_KERNEL);
+ if (!hisi_hba->sata_breakpoint)
+ goto err_out;
+
+ hisi_hba->last_slot_index = 0;
+
+ hisi_hba->wq =
+ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, dev_name(dev));
+ if (!hisi_hba->wq) {
+ dev_err(dev, "sas_alloc: failed to create workqueue\n");
+ goto err_out;
+ }
+
+ return 0;
+err_out:
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_alloc);
+
+void hisi_sas_free(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[i];
+
+ timer_delete_sync(&phy->timer);
+ }
+
+ if (hisi_hba->wq)
+ destroy_workqueue(hisi_hba->wq);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_free);
+
+void hisi_sas_rst_work_handler(struct work_struct *work)
+{
+ struct hisi_hba *hisi_hba =
+ container_of(work, struct hisi_hba, rst_work);
+
+ if (hisi_sas_controller_prereset(hisi_hba))
+ return;
+
+ hisi_sas_controller_reset(hisi_hba);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_rst_work_handler);
+
+void hisi_sas_sync_rst_work_handler(struct work_struct *work)
+{
+ struct hisi_sas_rst *rst =
+ container_of(work, struct hisi_sas_rst, work);
+
+ if (hisi_sas_controller_prereset(rst->hisi_hba))
+ goto rst_complete;
+
+ if (!hisi_sas_controller_reset(rst->hisi_hba))
+ rst->done = true;
+rst_complete:
+ complete(rst->completion);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sync_rst_work_handler);
+
+int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ struct platform_device *pdev = hisi_hba->platform_dev;
+ struct device_node *np = pdev ? pdev->dev.of_node : NULL;
+ struct clk *refclk;
+
+ if (device_property_read_u8_array(dev, "sas-addr", hisi_hba->sas_addr,
+ SAS_ADDR_SIZE)) {
+ dev_err(dev, "could not get property sas-addr\n");
+ return -ENOENT;
+ }
+
+ if (np) {
+ /*
+ * These properties are only required for platform device-based
+ * controller with DT firmware.
+ */
+ hisi_hba->ctrl = syscon_regmap_lookup_by_phandle(np,
+ "hisilicon,sas-syscon");
+ if (IS_ERR(hisi_hba->ctrl)) {
+ dev_err(dev, "could not get syscon\n");
+ return -ENOENT;
+ }
+
+ if (device_property_read_u32(dev, "ctrl-reset-reg",
+ &hisi_hba->ctrl_reset_reg)) {
+ dev_err(dev, "could not get property ctrl-reset-reg\n");
+ return -ENOENT;
+ }
+
+ if (device_property_read_u32(dev, "ctrl-reset-sts-reg",
+ &hisi_hba->ctrl_reset_sts_reg)) {
+ dev_err(dev, "could not get property ctrl-reset-sts-reg\n");
+ return -ENOENT;
+ }
+
+ if (device_property_read_u32(dev, "ctrl-clock-ena-reg",
+ &hisi_hba->ctrl_clock_ena_reg)) {
+ dev_err(dev, "could not get property ctrl-clock-ena-reg\n");
+ return -ENOENT;
+ }
+ }
+
+ refclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(refclk))
+ dev_dbg(dev, "no ref clk property\n");
+ else
+ hisi_hba->refclk_frequency_mhz = clk_get_rate(refclk) /
+ HZ_TO_MHZ;
+
+ if (device_property_read_u32(dev, "phy-count", &hisi_hba->n_phy)) {
+ dev_err(dev, "could not get property phy-count\n");
+ return -ENOENT;
+ }
+
+ if (device_property_read_u32(dev, "queue-count",
+ &hisi_hba->queue_count)) {
+ dev_err(dev, "could not get property queue-count\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_get_fw_info);
+
+static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev,
+ const struct hisi_sas_hw *hw)
+{
+ struct resource *res;
+ struct Scsi_Host *shost;
+ struct hisi_hba *hisi_hba;
+ struct device *dev = &pdev->dev;
+ int error;
+
+ shost = scsi_host_alloc(hw->sht, sizeof(*hisi_hba));
+ if (!shost) {
+ dev_err(dev, "scsi host alloc failed\n");
+ return NULL;
+ }
+ hisi_hba = shost_priv(shost);
+
+ INIT_WORK(&hisi_hba->rst_work, hisi_sas_rst_work_handler);
+ hisi_hba->hw = hw;
+ hisi_hba->dev = dev;
+ hisi_hba->platform_dev = pdev;
+ hisi_hba->shost = shost;
+ SHOST_TO_SAS_HA(shost) = &hisi_hba->sha;
+
+ timer_setup(&hisi_hba->timer, NULL, 0);
+
+ if (hisi_sas_get_fw_info(hisi_hba) < 0)
+ goto err_out;
+
+ if (hisi_hba->hw->fw_info_check) {
+ if (hisi_hba->hw->fw_info_check(hisi_hba))
+ goto err_out;
+ }
+
+ error = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (error) {
+ dev_err(dev, "No usable DMA addressing method\n");
+ goto err_out;
+ }
+
+ hisi_hba->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hisi_hba->regs))
+ goto err_out;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ hisi_hba->sgpio_regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(hisi_hba->sgpio_regs))
+ goto err_out;
+ }
+
+ if (hisi_sas_alloc(hisi_hba)) {
+ hisi_sas_free(hisi_hba);
+ goto err_out;
+ }
+
+ return shost;
+err_out:
+ scsi_host_put(shost);
+ dev_err(dev, "shost alloc failed\n");
+ return NULL;
+}
+
+static int hisi_sas_interrupt_preinit(struct hisi_hba *hisi_hba)
+{
+ if (hisi_hba->hw->interrupt_preinit)
+ return hisi_hba->hw->interrupt_preinit(hisi_hba);
+ return 0;
+}
+
+int hisi_sas_probe(struct platform_device *pdev,
+ const struct hisi_sas_hw *hw)
+{
+ struct Scsi_Host *shost;
+ struct hisi_hba *hisi_hba;
+ struct device *dev = &pdev->dev;
+ struct asd_sas_phy **arr_phy;
+ struct asd_sas_port **arr_port;
+ struct sas_ha_struct *sha;
+ int rc, phy_nr, port_nr, i;
+
+ shost = hisi_sas_shost_alloc(pdev, hw);
+ if (!shost)
+ return -ENOMEM;
+
+ sha = SHOST_TO_SAS_HA(shost);
+ hisi_hba = shost_priv(shost);
+ platform_set_drvdata(pdev, sha);
+
+ phy_nr = port_nr = hisi_hba->n_phy;
+
+ arr_phy = devm_kcalloc(dev, phy_nr, sizeof(void *), GFP_KERNEL);
+ arr_port = devm_kcalloc(dev, port_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_phy || !arr_port) {
+ rc = -ENOMEM;
+ goto err_out_ha;
+ }
+
+ sha->sas_phy = arr_phy;
+ sha->sas_port = arr_port;
+ sha->lldd_ha = hisi_hba;
+
+ shost->transportt = hisi_sas_stt;
+ shost->max_id = HISI_SAS_MAX_DEVICES;
+ shost->max_lun = ~0;
+ shost->max_channel = 1;
+ shost->max_cmd_len = HISI_SAS_MAX_CDB_LEN;
+ if (hisi_hba->hw->slot_index_alloc) {
+ shost->can_queue = HISI_SAS_MAX_COMMANDS;
+ shost->cmd_per_lun = HISI_SAS_MAX_COMMANDS;
+ } else {
+ shost->can_queue = HISI_SAS_UNRESERVED_IPTT;
+ shost->cmd_per_lun = HISI_SAS_UNRESERVED_IPTT;
+ }
+
+ sha->sas_ha_name = DRV_NAME;
+ sha->dev = hisi_hba->dev;
+ sha->sas_addr = &hisi_hba->sas_addr[0];
+ sha->num_phys = hisi_hba->n_phy;
+ sha->shost = hisi_hba->shost;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy;
+ sha->sas_port[i] = &hisi_hba->port[i].sas_port;
+ }
+
+ rc = hisi_sas_interrupt_preinit(hisi_hba);
+ if (rc)
+ goto err_out_ha;
+
+ rc = scsi_add_host(shost, &pdev->dev);
+ if (rc)
+ goto err_out_ha;
+
+ rc = sas_register_ha(sha);
+ if (rc)
+ goto err_out_register_ha;
+
+ rc = hisi_hba->hw->hw_init(hisi_hba);
+ if (rc)
+ goto err_out_hw_init;
+
+ scsi_scan_host(shost);
+
+ return 0;
+
+err_out_hw_init:
+ sas_unregister_ha(sha);
+err_out_register_ha:
+ scsi_remove_host(shost);
+err_out_ha:
+ hisi_sas_free(hisi_hba);
+ scsi_host_put(shost);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_probe);
+
+void hisi_sas_remove(struct platform_device *pdev)
+{
+ struct sas_ha_struct *sha = platform_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ struct Scsi_Host *shost = sha->shost;
+
+ timer_delete_sync(&hisi_hba->timer);
+
+ sas_unregister_ha(sha);
+ sas_remove_host(shost);
+
+ hisi_sas_free(hisi_hba);
+ scsi_host_put(shost);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_remove);
+
+#if IS_ENABLED(CONFIG_SCSI_HISI_SAS_DEBUGFS_DEFAULT_ENABLE)
+#define DEBUGFS_ENABLE_DEFAULT "enabled"
+bool hisi_sas_debugfs_enable = true;
+u32 hisi_sas_debugfs_dump_count = 50;
+#else
+#define DEBUGFS_ENABLE_DEFAULT "disabled"
+bool hisi_sas_debugfs_enable;
+u32 hisi_sas_debugfs_dump_count = 1;
+#endif
+
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_enable);
+module_param_named(debugfs_enable, hisi_sas_debugfs_enable, bool, 0444);
+MODULE_PARM_DESC(hisi_sas_debugfs_enable,
+ "Enable driver debugfs (default "DEBUGFS_ENABLE_DEFAULT")");
+
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_dump_count);
+module_param_named(debugfs_dump_count, hisi_sas_debugfs_dump_count, uint, 0444);
+MODULE_PARM_DESC(hisi_sas_debugfs_dump_count, "Number of debugfs dumps to allow");
+
+struct dentry *hisi_sas_debugfs_dir;
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_dir);
+
+static __init int hisi_sas_init(void)
+{
+ hisi_sas_stt = sas_domain_attach_transport(&hisi_sas_transport_ops);
+ if (!hisi_sas_stt)
+ return -ENOMEM;
+
+ if (hisi_sas_debugfs_enable) {
+ hisi_sas_debugfs_dir = debugfs_create_dir("hisi_sas", NULL);
+ if (hisi_sas_debugfs_dump_count > HISI_SAS_MAX_DEBUGFS_DUMP) {
+ pr_info("hisi_sas: Limiting debugfs dump count\n");
+ hisi_sas_debugfs_dump_count = HISI_SAS_MAX_DEBUGFS_DUMP;
+ }
+ }
+
+ return 0;
+}
+
+static __exit void hisi_sas_exit(void)
+{
+ if (hisi_sas_debugfs_enable)
+ debugfs_remove(hisi_sas_debugfs_dir);
+
+ sas_release_transport(hisi_sas_stt);
+}
+
+module_init(hisi_sas_init);
+module_exit(hisi_sas_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
+MODULE_DESCRIPTION("HISILICON SAS controller driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
new file mode 100644
index 000000000000..fa94d7110714
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
@@ -0,0 +1,1818 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2015 Linaro Ltd.
+ * Copyright (c) 2015 Hisilicon Limited.
+ */
+
+#include "hisi_sas.h"
+#define DRV_NAME "hisi_sas_v1_hw"
+
+/* global registers need init*/
+#define DLVRY_QUEUE_ENABLE 0x0
+#define IOST_BASE_ADDR_LO 0x8
+#define IOST_BASE_ADDR_HI 0xc
+#define ITCT_BASE_ADDR_LO 0x10
+#define ITCT_BASE_ADDR_HI 0x14
+#define BROKEN_MSG_ADDR_LO 0x18
+#define BROKEN_MSG_ADDR_HI 0x1c
+#define PHY_CONTEXT 0x20
+#define PHY_STATE 0x24
+#define PHY_PORT_NUM_MA 0x28
+#define PORT_STATE 0x2c
+#define PHY_CONN_RATE 0x30
+#define HGC_TRANS_TASK_CNT_LIMIT 0x38
+#define AXI_AHB_CLK_CFG 0x3c
+#define HGC_SAS_TXFAIL_RETRY_CTRL 0x84
+#define HGC_GET_ITV_TIME 0x90
+#define DEVICE_MSG_WORK_MODE 0x94
+#define I_T_NEXUS_LOSS_TIME 0xa0
+#define BUS_INACTIVE_LIMIT_TIME 0xa8
+#define REJECT_TO_OPEN_LIMIT_TIME 0xac
+#define CFG_AGING_TIME 0xbc
+#define CFG_AGING_TIME_ITCT_REL_OFF 0
+#define CFG_AGING_TIME_ITCT_REL_MSK (0x1 << CFG_AGING_TIME_ITCT_REL_OFF)
+#define HGC_DFX_CFG2 0xc0
+#define FIS_LIST_BADDR_L 0xc4
+#define CFG_1US_TIMER_TRSH 0xcc
+#define CFG_SAS_CONFIG 0xd4
+#define HGC_IOST_ECC_ADDR 0x140
+#define HGC_IOST_ECC_ADDR_BAD_OFF 16
+#define HGC_IOST_ECC_ADDR_BAD_MSK (0x3ff << HGC_IOST_ECC_ADDR_BAD_OFF)
+#define HGC_DQ_ECC_ADDR 0x144
+#define HGC_DQ_ECC_ADDR_BAD_OFF 16
+#define HGC_DQ_ECC_ADDR_BAD_MSK (0xfff << HGC_DQ_ECC_ADDR_BAD_OFF)
+#define HGC_INVLD_DQE_INFO 0x148
+#define HGC_INVLD_DQE_INFO_DQ_OFF 0
+#define HGC_INVLD_DQE_INFO_DQ_MSK (0xffff << HGC_INVLD_DQE_INFO_DQ_OFF)
+#define HGC_INVLD_DQE_INFO_TYPE_OFF 16
+#define HGC_INVLD_DQE_INFO_TYPE_MSK (0x1 << HGC_INVLD_DQE_INFO_TYPE_OFF)
+#define HGC_INVLD_DQE_INFO_FORCE_OFF 17
+#define HGC_INVLD_DQE_INFO_FORCE_MSK (0x1 << HGC_INVLD_DQE_INFO_FORCE_OFF)
+#define HGC_INVLD_DQE_INFO_PHY_OFF 18
+#define HGC_INVLD_DQE_INFO_PHY_MSK (0x1 << HGC_INVLD_DQE_INFO_PHY_OFF)
+#define HGC_INVLD_DQE_INFO_ABORT_OFF 19
+#define HGC_INVLD_DQE_INFO_ABORT_MSK (0x1 << HGC_INVLD_DQE_INFO_ABORT_OFF)
+#define HGC_INVLD_DQE_INFO_IPTT_OF_OFF 20
+#define HGC_INVLD_DQE_INFO_IPTT_OF_MSK (0x1 << HGC_INVLD_DQE_INFO_IPTT_OF_OFF)
+#define HGC_INVLD_DQE_INFO_SSP_ERR_OFF 21
+#define HGC_INVLD_DQE_INFO_SSP_ERR_MSK (0x1 << HGC_INVLD_DQE_INFO_SSP_ERR_OFF)
+#define HGC_INVLD_DQE_INFO_OFL_OFF 22
+#define HGC_INVLD_DQE_INFO_OFL_MSK (0x1 << HGC_INVLD_DQE_INFO_OFL_OFF)
+#define HGC_ITCT_ECC_ADDR 0x150
+#define HGC_ITCT_ECC_ADDR_BAD_OFF 16
+#define HGC_ITCT_ECC_ADDR_BAD_MSK (0x3ff << HGC_ITCT_ECC_ADDR_BAD_OFF)
+#define HGC_AXI_FIFO_ERR_INFO 0x154
+#define INT_COAL_EN 0x1bc
+#define OQ_INT_COAL_TIME 0x1c0
+#define OQ_INT_COAL_CNT 0x1c4
+#define ENT_INT_COAL_TIME 0x1c8
+#define ENT_INT_COAL_CNT 0x1cc
+#define OQ_INT_SRC 0x1d0
+#define OQ_INT_SRC_MSK 0x1d4
+#define ENT_INT_SRC1 0x1d8
+#define ENT_INT_SRC2 0x1dc
+#define ENT_INT_SRC2_DQ_CFG_ERR_OFF 25
+#define ENT_INT_SRC2_DQ_CFG_ERR_MSK (0x1 << ENT_INT_SRC2_DQ_CFG_ERR_OFF)
+#define ENT_INT_SRC2_CQ_CFG_ERR_OFF 27
+#define ENT_INT_SRC2_CQ_CFG_ERR_MSK (0x1 << ENT_INT_SRC2_CQ_CFG_ERR_OFF)
+#define ENT_INT_SRC2_AXI_WRONG_INT_OFF 28
+#define ENT_INT_SRC2_AXI_WRONG_INT_MSK (0x1 << ENT_INT_SRC2_AXI_WRONG_INT_OFF)
+#define ENT_INT_SRC2_AXI_OVERLF_INT_OFF 29
+#define ENT_INT_SRC2_AXI_OVERLF_INT_MSK (0x1 << ENT_INT_SRC2_AXI_OVERLF_INT_OFF)
+#define ENT_INT_SRC_MSK1 0x1e0
+#define ENT_INT_SRC_MSK2 0x1e4
+#define SAS_ECC_INTR 0x1e8
+#define SAS_ECC_INTR_DQ_ECC1B_OFF 0
+#define SAS_ECC_INTR_DQ_ECC1B_MSK (0x1 << SAS_ECC_INTR_DQ_ECC1B_OFF)
+#define SAS_ECC_INTR_DQ_ECCBAD_OFF 1
+#define SAS_ECC_INTR_DQ_ECCBAD_MSK (0x1 << SAS_ECC_INTR_DQ_ECCBAD_OFF)
+#define SAS_ECC_INTR_IOST_ECC1B_OFF 2
+#define SAS_ECC_INTR_IOST_ECC1B_MSK (0x1 << SAS_ECC_INTR_IOST_ECC1B_OFF)
+#define SAS_ECC_INTR_IOST_ECCBAD_OFF 3
+#define SAS_ECC_INTR_IOST_ECCBAD_MSK (0x1 << SAS_ECC_INTR_IOST_ECCBAD_OFF)
+#define SAS_ECC_INTR_ITCT_ECC1B_OFF 4
+#define SAS_ECC_INTR_ITCT_ECC1B_MSK (0x1 << SAS_ECC_INTR_ITCT_ECC1B_OFF)
+#define SAS_ECC_INTR_ITCT_ECCBAD_OFF 5
+#define SAS_ECC_INTR_ITCT_ECCBAD_MSK (0x1 << SAS_ECC_INTR_ITCT_ECCBAD_OFF)
+#define SAS_ECC_INTR_MSK 0x1ec
+#define HGC_ERR_STAT_EN 0x238
+#define DLVRY_Q_0_BASE_ADDR_LO 0x260
+#define DLVRY_Q_0_BASE_ADDR_HI 0x264
+#define DLVRY_Q_0_DEPTH 0x268
+#define DLVRY_Q_0_WR_PTR 0x26c
+#define DLVRY_Q_0_RD_PTR 0x270
+#define COMPL_Q_0_BASE_ADDR_LO 0x4e0
+#define COMPL_Q_0_BASE_ADDR_HI 0x4e4
+#define COMPL_Q_0_DEPTH 0x4e8
+#define COMPL_Q_0_WR_PTR 0x4ec
+#define COMPL_Q_0_RD_PTR 0x4f0
+#define HGC_ECC_ERR 0x7d0
+
+/* phy registers need init */
+#define PORT_BASE (0x800)
+
+#define PHY_CFG (PORT_BASE + 0x0)
+#define PHY_CFG_ENA_OFF 0
+#define PHY_CFG_ENA_MSK (0x1 << PHY_CFG_ENA_OFF)
+#define PHY_CFG_DC_OPT_OFF 2
+#define PHY_CFG_DC_OPT_MSK (0x1 << PHY_CFG_DC_OPT_OFF)
+#define PROG_PHY_LINK_RATE (PORT_BASE + 0xc)
+#define PROG_PHY_LINK_RATE_MAX_OFF 0
+#define PROG_PHY_LINK_RATE_MAX_MSK (0xf << PROG_PHY_LINK_RATE_MAX_OFF)
+#define PROG_PHY_LINK_RATE_MIN_OFF 4
+#define PROG_PHY_LINK_RATE_MIN_MSK (0xf << PROG_PHY_LINK_RATE_MIN_OFF)
+#define PROG_PHY_LINK_RATE_OOB_OFF 8
+#define PROG_PHY_LINK_RATE_OOB_MSK (0xf << PROG_PHY_LINK_RATE_OOB_OFF)
+#define PHY_CTRL (PORT_BASE + 0x14)
+#define PHY_CTRL_RESET_OFF 0
+#define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF)
+#define PHY_RATE_NEGO (PORT_BASE + 0x30)
+#define PHY_PCN (PORT_BASE + 0x44)
+#define SL_TOUT_CFG (PORT_BASE + 0x8c)
+#define SL_CONTROL (PORT_BASE + 0x94)
+#define SL_CONTROL_NOTIFY_EN_OFF 0
+#define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF)
+#define TX_ID_DWORD0 (PORT_BASE + 0x9c)
+#define TX_ID_DWORD1 (PORT_BASE + 0xa0)
+#define TX_ID_DWORD2 (PORT_BASE + 0xa4)
+#define TX_ID_DWORD3 (PORT_BASE + 0xa8)
+#define TX_ID_DWORD4 (PORT_BASE + 0xaC)
+#define TX_ID_DWORD5 (PORT_BASE + 0xb0)
+#define TX_ID_DWORD6 (PORT_BASE + 0xb4)
+#define RX_IDAF_DWORD0 (PORT_BASE + 0xc4)
+#define RX_IDAF_DWORD1 (PORT_BASE + 0xc8)
+#define RX_IDAF_DWORD2 (PORT_BASE + 0xcc)
+#define RX_IDAF_DWORD3 (PORT_BASE + 0xd0)
+#define RX_IDAF_DWORD4 (PORT_BASE + 0xd4)
+#define RX_IDAF_DWORD5 (PORT_BASE + 0xd8)
+#define RX_IDAF_DWORD6 (PORT_BASE + 0xdc)
+#define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc)
+#define DONE_RECEIVED_TIME (PORT_BASE + 0x12c)
+#define CON_CFG_DRIVER (PORT_BASE + 0x130)
+#define PHY_CONFIG2 (PORT_BASE + 0x1a8)
+#define PHY_CONFIG2_FORCE_TXDEEMPH_OFF 3
+#define PHY_CONFIG2_FORCE_TXDEEMPH_MSK (0x1 << PHY_CONFIG2_FORCE_TXDEEMPH_OFF)
+#define PHY_CONFIG2_TX_TRAIN_COMP_OFF 24
+#define PHY_CONFIG2_TX_TRAIN_COMP_MSK (0x1 << PHY_CONFIG2_TX_TRAIN_COMP_OFF)
+#define CHL_INT0 (PORT_BASE + 0x1b0)
+#define CHL_INT0_PHYCTRL_NOTRDY_OFF 0
+#define CHL_INT0_PHYCTRL_NOTRDY_MSK (0x1 << CHL_INT0_PHYCTRL_NOTRDY_OFF)
+#define CHL_INT0_SN_FAIL_NGR_OFF 2
+#define CHL_INT0_SN_FAIL_NGR_MSK (0x1 << CHL_INT0_SN_FAIL_NGR_OFF)
+#define CHL_INT0_DWS_LOST_OFF 4
+#define CHL_INT0_DWS_LOST_MSK (0x1 << CHL_INT0_DWS_LOST_OFF)
+#define CHL_INT0_SL_IDAF_FAIL_OFF 10
+#define CHL_INT0_SL_IDAF_FAIL_MSK (0x1 << CHL_INT0_SL_IDAF_FAIL_OFF)
+#define CHL_INT0_ID_TIMEOUT_OFF 11
+#define CHL_INT0_ID_TIMEOUT_MSK (0x1 << CHL_INT0_ID_TIMEOUT_OFF)
+#define CHL_INT0_SL_OPAF_FAIL_OFF 12
+#define CHL_INT0_SL_OPAF_FAIL_MSK (0x1 << CHL_INT0_SL_OPAF_FAIL_OFF)
+#define CHL_INT0_SL_PS_FAIL_OFF 21
+#define CHL_INT0_SL_PS_FAIL_MSK (0x1 << CHL_INT0_SL_PS_FAIL_OFF)
+#define CHL_INT1 (PORT_BASE + 0x1b4)
+#define CHL_INT2 (PORT_BASE + 0x1b8)
+#define CHL_INT2_SL_RX_BC_ACK_OFF 2
+#define CHL_INT2_SL_RX_BC_ACK_MSK (0x1 << CHL_INT2_SL_RX_BC_ACK_OFF)
+#define CHL_INT2_SL_PHY_ENA_OFF 6
+#define CHL_INT2_SL_PHY_ENA_MSK (0x1 << CHL_INT2_SL_PHY_ENA_OFF)
+#define CHL_INT0_MSK (PORT_BASE + 0x1bc)
+#define CHL_INT0_MSK_PHYCTRL_NOTRDY_OFF 0
+#define CHL_INT0_MSK_PHYCTRL_NOTRDY_MSK (0x1 << CHL_INT0_MSK_PHYCTRL_NOTRDY_OFF)
+#define CHL_INT1_MSK (PORT_BASE + 0x1c0)
+#define CHL_INT2_MSK (PORT_BASE + 0x1c4)
+#define CHL_INT_COAL_EN (PORT_BASE + 0x1d0)
+#define DMA_TX_STATUS (PORT_BASE + 0x2d0)
+#define DMA_TX_STATUS_BUSY_OFF 0
+#define DMA_TX_STATUS_BUSY_MSK (0x1 << DMA_TX_STATUS_BUSY_OFF)
+#define DMA_RX_STATUS (PORT_BASE + 0x2e8)
+#define DMA_RX_STATUS_BUSY_OFF 0
+#define DMA_RX_STATUS_BUSY_MSK (0x1 << DMA_RX_STATUS_BUSY_OFF)
+
+#define AXI_CFG 0x5100
+#define RESET_VALUE 0x7ffff
+
+/* HW dma structures */
+/* Delivery queue header */
+/* dw0 */
+#define CMD_HDR_RESP_REPORT_OFF 5
+#define CMD_HDR_RESP_REPORT_MSK 0x20
+#define CMD_HDR_TLR_CTRL_OFF 6
+#define CMD_HDR_TLR_CTRL_MSK 0xc0
+#define CMD_HDR_PORT_OFF 17
+#define CMD_HDR_PORT_MSK 0xe0000
+#define CMD_HDR_PRIORITY_OFF 27
+#define CMD_HDR_PRIORITY_MSK 0x8000000
+#define CMD_HDR_MODE_OFF 28
+#define CMD_HDR_MODE_MSK 0x10000000
+#define CMD_HDR_CMD_OFF 29
+#define CMD_HDR_CMD_MSK 0xe0000000
+/* dw1 */
+#define CMD_HDR_VERIFY_DTL_OFF 10
+#define CMD_HDR_VERIFY_DTL_MSK 0x400
+#define CMD_HDR_SSP_FRAME_TYPE_OFF 13
+#define CMD_HDR_SSP_FRAME_TYPE_MSK 0xe000
+#define CMD_HDR_DEVICE_ID_OFF 16
+#define CMD_HDR_DEVICE_ID_MSK 0xffff0000
+/* dw2 */
+#define CMD_HDR_CFL_OFF 0
+#define CMD_HDR_CFL_MSK 0x1ff
+#define CMD_HDR_MRFL_OFF 15
+#define CMD_HDR_MRFL_MSK 0xff8000
+#define CMD_HDR_FIRST_BURST_OFF 25
+#define CMD_HDR_FIRST_BURST_MSK 0x2000000
+/* dw3 */
+#define CMD_HDR_IPTT_OFF 0
+#define CMD_HDR_IPTT_MSK 0xffff
+/* dw6 */
+#define CMD_HDR_DATA_SGL_LEN_OFF 16
+#define CMD_HDR_DATA_SGL_LEN_MSK 0xffff0000
+
+/* Completion header */
+#define CMPLT_HDR_IPTT_OFF 0
+#define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF)
+#define CMPLT_HDR_CMD_CMPLT_OFF 17
+#define CMPLT_HDR_CMD_CMPLT_MSK (0x1 << CMPLT_HDR_CMD_CMPLT_OFF)
+#define CMPLT_HDR_ERR_RCRD_XFRD_OFF 18
+#define CMPLT_HDR_ERR_RCRD_XFRD_MSK (0x1 << CMPLT_HDR_ERR_RCRD_XFRD_OFF)
+#define CMPLT_HDR_RSPNS_XFRD_OFF 19
+#define CMPLT_HDR_RSPNS_XFRD_MSK (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF)
+#define CMPLT_HDR_IO_CFG_ERR_OFF 27
+#define CMPLT_HDR_IO_CFG_ERR_MSK (0x1 << CMPLT_HDR_IO_CFG_ERR_OFF)
+
+/* ITCT header */
+/* qw0 */
+#define ITCT_HDR_DEV_TYPE_OFF 0
+#define ITCT_HDR_DEV_TYPE_MSK (0x3ULL << ITCT_HDR_DEV_TYPE_OFF)
+#define ITCT_HDR_VALID_OFF 2
+#define ITCT_HDR_VALID_MSK (0x1ULL << ITCT_HDR_VALID_OFF)
+#define ITCT_HDR_AWT_CONTROL_OFF 4
+#define ITCT_HDR_AWT_CONTROL_MSK (0x1ULL << ITCT_HDR_AWT_CONTROL_OFF)
+#define ITCT_HDR_MAX_CONN_RATE_OFF 5
+#define ITCT_HDR_MAX_CONN_RATE_MSK (0xfULL << ITCT_HDR_MAX_CONN_RATE_OFF)
+#define ITCT_HDR_VALID_LINK_NUM_OFF 9
+#define ITCT_HDR_VALID_LINK_NUM_MSK (0xfULL << ITCT_HDR_VALID_LINK_NUM_OFF)
+#define ITCT_HDR_PORT_ID_OFF 13
+#define ITCT_HDR_PORT_ID_MSK (0x7ULL << ITCT_HDR_PORT_ID_OFF)
+#define ITCT_HDR_SMP_TIMEOUT_OFF 16
+#define ITCT_HDR_SMP_TIMEOUT_MSK (0xffffULL << ITCT_HDR_SMP_TIMEOUT_OFF)
+/* qw1 */
+#define ITCT_HDR_MAX_SAS_ADDR_OFF 0
+#define ITCT_HDR_MAX_SAS_ADDR_MSK (0xffffffffffffffff << \
+ ITCT_HDR_MAX_SAS_ADDR_OFF)
+/* qw2 */
+#define ITCT_HDR_IT_NEXUS_LOSS_TL_OFF 0
+#define ITCT_HDR_IT_NEXUS_LOSS_TL_MSK (0xffffULL << \
+ ITCT_HDR_IT_NEXUS_LOSS_TL_OFF)
+#define ITCT_HDR_BUS_INACTIVE_TL_OFF 16
+#define ITCT_HDR_BUS_INACTIVE_TL_MSK (0xffffULL << \
+ ITCT_HDR_BUS_INACTIVE_TL_OFF)
+#define ITCT_HDR_MAX_CONN_TL_OFF 32
+#define ITCT_HDR_MAX_CONN_TL_MSK (0xffffULL << \
+ ITCT_HDR_MAX_CONN_TL_OFF)
+#define ITCT_HDR_REJ_OPEN_TL_OFF 48
+#define ITCT_HDR_REJ_OPEN_TL_MSK (0xffffULL << \
+ ITCT_HDR_REJ_OPEN_TL_OFF)
+
+/* Err record header */
+#define ERR_HDR_DMA_TX_ERR_TYPE_OFF 0
+#define ERR_HDR_DMA_TX_ERR_TYPE_MSK (0xffff << ERR_HDR_DMA_TX_ERR_TYPE_OFF)
+#define ERR_HDR_DMA_RX_ERR_TYPE_OFF 16
+#define ERR_HDR_DMA_RX_ERR_TYPE_MSK (0xffff << ERR_HDR_DMA_RX_ERR_TYPE_OFF)
+
+struct hisi_sas_complete_v1_hdr {
+ __le32 data;
+};
+
+struct hisi_sas_err_record_v1 {
+ /* dw0 */
+ __le32 dma_err_type;
+
+ /* dw1 */
+ __le32 trans_tx_fail_type;
+
+ /* dw2 */
+ __le32 trans_rx_fail_type;
+
+ /* dw3 */
+ u32 rsvd;
+};
+
+enum {
+ HISI_SAS_PHY_BCAST_ACK = 0,
+ HISI_SAS_PHY_SL_PHY_ENABLED,
+ HISI_SAS_PHY_INT_ABNORMAL,
+ HISI_SAS_PHY_INT_NR
+};
+
+enum {
+ DMA_TX_ERR_BASE = 0x0,
+ DMA_RX_ERR_BASE = 0x100,
+ TRANS_TX_FAIL_BASE = 0x200,
+ TRANS_RX_FAIL_BASE = 0x300,
+
+ /* dma tx */
+ DMA_TX_DIF_CRC_ERR = DMA_TX_ERR_BASE, /* 0x0 */
+ DMA_TX_DIF_APP_ERR, /* 0x1 */
+ DMA_TX_DIF_RPP_ERR, /* 0x2 */
+ DMA_TX_AXI_BUS_ERR, /* 0x3 */
+ DMA_TX_DATA_SGL_OVERFLOW_ERR, /* 0x4 */
+ DMA_TX_DIF_SGL_OVERFLOW_ERR, /* 0x5 */
+ DMA_TX_UNEXP_XFER_RDY_ERR, /* 0x6 */
+ DMA_TX_XFER_RDY_OFFSET_ERR, /* 0x7 */
+ DMA_TX_DATA_UNDERFLOW_ERR, /* 0x8 */
+ DMA_TX_XFER_RDY_LENGTH_OVERFLOW_ERR, /* 0x9 */
+
+ /* dma rx */
+ DMA_RX_BUFFER_ECC_ERR = DMA_RX_ERR_BASE, /* 0x100 */
+ DMA_RX_DIF_CRC_ERR, /* 0x101 */
+ DMA_RX_DIF_APP_ERR, /* 0x102 */
+ DMA_RX_DIF_RPP_ERR, /* 0x103 */
+ DMA_RX_RESP_BUFFER_OVERFLOW_ERR, /* 0x104 */
+ DMA_RX_AXI_BUS_ERR, /* 0x105 */
+ DMA_RX_DATA_SGL_OVERFLOW_ERR, /* 0x106 */
+ DMA_RX_DIF_SGL_OVERFLOW_ERR, /* 0x107 */
+ DMA_RX_DATA_OFFSET_ERR, /* 0x108 */
+ DMA_RX_UNEXP_RX_DATA_ERR, /* 0x109 */
+ DMA_RX_DATA_OVERFLOW_ERR, /* 0x10a */
+ DMA_RX_DATA_UNDERFLOW_ERR, /* 0x10b */
+ DMA_RX_UNEXP_RETRANS_RESP_ERR, /* 0x10c */
+
+ /* trans tx */
+ TRANS_TX_RSVD0_ERR = TRANS_TX_FAIL_BASE, /* 0x200 */
+ TRANS_TX_PHY_NOT_ENABLE_ERR, /* 0x201 */
+ TRANS_TX_OPEN_REJCT_WRONG_DEST_ERR, /* 0x202 */
+ TRANS_TX_OPEN_REJCT_ZONE_VIOLATION_ERR, /* 0x203 */
+ TRANS_TX_OPEN_REJCT_BY_OTHER_ERR, /* 0x204 */
+ TRANS_TX_RSVD1_ERR, /* 0x205 */
+ TRANS_TX_OPEN_REJCT_AIP_TIMEOUT_ERR, /* 0x206 */
+ TRANS_TX_OPEN_REJCT_STP_BUSY_ERR, /* 0x207 */
+ TRANS_TX_OPEN_REJCT_PROTOCOL_NOT_SUPPORT_ERR, /* 0x208 */
+ TRANS_TX_OPEN_REJCT_RATE_NOT_SUPPORT_ERR, /* 0x209 */
+ TRANS_TX_OPEN_REJCT_BAD_DEST_ERR, /* 0x20a */
+ TRANS_TX_OPEN_BREAK_RECEIVE_ERR, /* 0x20b */
+ TRANS_TX_LOW_PHY_POWER_ERR, /* 0x20c */
+ TRANS_TX_OPEN_REJCT_PATHWAY_BLOCKED_ERR, /* 0x20d */
+ TRANS_TX_OPEN_TIMEOUT_ERR, /* 0x20e */
+ TRANS_TX_OPEN_REJCT_NO_DEST_ERR, /* 0x20f */
+ TRANS_TX_OPEN_RETRY_ERR, /* 0x210 */
+ TRANS_TX_RSVD2_ERR, /* 0x211 */
+ TRANS_TX_BREAK_TIMEOUT_ERR, /* 0x212 */
+ TRANS_TX_BREAK_REQUEST_ERR, /* 0x213 */
+ TRANS_TX_BREAK_RECEIVE_ERR, /* 0x214 */
+ TRANS_TX_CLOSE_TIMEOUT_ERR, /* 0x215 */
+ TRANS_TX_CLOSE_NORMAL_ERR, /* 0x216 */
+ TRANS_TX_CLOSE_PHYRESET_ERR, /* 0x217 */
+ TRANS_TX_WITH_CLOSE_DWS_TIMEOUT_ERR, /* 0x218 */
+ TRANS_TX_WITH_CLOSE_COMINIT_ERR, /* 0x219 */
+ TRANS_TX_NAK_RECEIVE_ERR, /* 0x21a */
+ TRANS_TX_ACK_NAK_TIMEOUT_ERR, /* 0x21b */
+ TRANS_TX_CREDIT_TIMEOUT_ERR, /* 0x21c */
+ TRANS_TX_IPTT_CONFLICT_ERR, /* 0x21d */
+ TRANS_TX_TXFRM_TYPE_ERR, /* 0x21e */
+ TRANS_TX_TXSMP_LENGTH_ERR, /* 0x21f */
+
+ /* trans rx */
+ TRANS_RX_FRAME_CRC_ERR = TRANS_RX_FAIL_BASE, /* 0x300 */
+ TRANS_RX_FRAME_DONE_ERR, /* 0x301 */
+ TRANS_RX_FRAME_ERRPRM_ERR, /* 0x302 */
+ TRANS_RX_FRAME_NO_CREDIT_ERR, /* 0x303 */
+ TRANS_RX_RSVD0_ERR, /* 0x304 */
+ TRANS_RX_FRAME_OVERRUN_ERR, /* 0x305 */
+ TRANS_RX_FRAME_NO_EOF_ERR, /* 0x306 */
+ TRANS_RX_LINK_BUF_OVERRUN_ERR, /* 0x307 */
+ TRANS_RX_BREAK_TIMEOUT_ERR, /* 0x308 */
+ TRANS_RX_BREAK_REQUEST_ERR, /* 0x309 */
+ TRANS_RX_BREAK_RECEIVE_ERR, /* 0x30a */
+ TRANS_RX_CLOSE_TIMEOUT_ERR, /* 0x30b */
+ TRANS_RX_CLOSE_NORMAL_ERR, /* 0x30c */
+ TRANS_RX_CLOSE_PHYRESET_ERR, /* 0x30d */
+ TRANS_RX_WITH_CLOSE_DWS_TIMEOUT_ERR, /* 0x30e */
+ TRANS_RX_WITH_CLOSE_COMINIT_ERR, /* 0x30f */
+ TRANS_RX_DATA_LENGTH0_ERR, /* 0x310 */
+ TRANS_RX_BAD_HASH_ERR, /* 0x311 */
+ TRANS_RX_XRDY_ZERO_ERR, /* 0x312 */
+ TRANS_RX_SSP_FRAME_LEN_ERR, /* 0x313 */
+ TRANS_RX_TRANS_RX_RSVD1_ERR, /* 0x314 */
+ TRANS_RX_NO_BALANCE_ERR, /* 0x315 */
+ TRANS_RX_TRANS_RX_RSVD2_ERR, /* 0x316 */
+ TRANS_RX_TRANS_RX_RSVD3_ERR, /* 0x317 */
+ TRANS_RX_BAD_FRAME_TYPE_ERR, /* 0x318 */
+ TRANS_RX_SMP_FRAME_LEN_ERR, /* 0x319 */
+ TRANS_RX_SMP_RESP_TIMEOUT_ERR, /* 0x31a */
+};
+
+#define HISI_SAS_PHY_MAX_INT_NR (HISI_SAS_PHY_INT_NR * HISI_SAS_MAX_PHYS)
+#define HISI_SAS_CQ_MAX_INT_NR (HISI_SAS_MAX_QUEUES)
+#define HISI_SAS_FATAL_INT_NR (2)
+
+#define HISI_SAS_MAX_INT_NR \
+ (HISI_SAS_PHY_MAX_INT_NR + HISI_SAS_CQ_MAX_INT_NR +\
+ HISI_SAS_FATAL_INT_NR)
+
+static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ return readl(regs);
+}
+
+static void hisi_sas_write32(struct hisi_hba *hisi_hba,
+ u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ writel(val, regs);
+}
+
+static void hisi_sas_phy_write32(struct hisi_hba *hisi_hba,
+ int phy_no, u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ writel(val, regs);
+}
+
+static u32 hisi_sas_phy_read32(struct hisi_hba *hisi_hba,
+ int phy_no, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ return readl(regs);
+}
+
+static void config_phy_opt_mode_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg &= ~PHY_CFG_DC_OPT_MSK;
+ cfg |= 1 << PHY_CFG_DC_OPT_OFF;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void config_tx_tfe_autoneg_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CONFIG2);
+
+ cfg &= ~PHY_CONFIG2_FORCE_TXDEEMPH_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CONFIG2, cfg);
+}
+
+static void config_id_frame_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct sas_identify_frame identify_frame;
+ u32 *identify_buffer;
+
+ memset(&identify_frame, 0, sizeof(identify_frame));
+ identify_frame.dev_type = SAS_END_DEVICE;
+ identify_frame.frame_type = 0;
+ identify_frame._un1 = 1;
+ identify_frame.initiator_bits = SAS_PROTOCOL_ALL;
+ identify_frame.target_bits = SAS_PROTOCOL_NONE;
+ memcpy(&identify_frame._un4_11[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ memcpy(&identify_frame.sas_addr[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ identify_frame.phy_id = phy_no;
+ identify_buffer = (u32 *)(&identify_frame);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0,
+ __swab32(identify_buffer[0]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1,
+ __swab32(identify_buffer[1]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2,
+ __swab32(identify_buffer[2]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3,
+ __swab32(identify_buffer[3]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4,
+ __swab32(identify_buffer[4]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5,
+ __swab32(identify_buffer[5]));
+}
+
+static void setup_itct_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ struct domain_device *device = sas_dev->sas_device;
+ struct device *dev = hisi_hba->dev;
+ u64 qw0, device_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[device_id];
+ struct asd_sas_port *sas_port = device->port;
+ struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+ u64 sas_addr;
+
+ memset(itct, 0, sizeof(*itct));
+
+ /* qw0 */
+ qw0 = 0;
+ switch (sas_dev->dev_type) {
+ case SAS_END_DEVICE:
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF;
+ break;
+ default:
+ dev_warn(dev, "setup itct: unsupported dev type (%d)\n",
+ sas_dev->dev_type);
+ }
+
+ qw0 |= ((1 << ITCT_HDR_VALID_OFF) |
+ (1 << ITCT_HDR_AWT_CONTROL_OFF) |
+ (device->max_linkrate << ITCT_HDR_MAX_CONN_RATE_OFF) |
+ (1 << ITCT_HDR_VALID_LINK_NUM_OFF) |
+ (port->id << ITCT_HDR_PORT_ID_OFF));
+ itct->qw0 = cpu_to_le64(qw0);
+
+ /* qw1 */
+ memcpy(&sas_addr, device->sas_addr, SAS_ADDR_SIZE);
+ itct->sas_addr = cpu_to_le64(__swab64(sas_addr));
+
+ /* qw2 */
+ itct->qw2 = cpu_to_le64((500ULL << ITCT_HDR_IT_NEXUS_LOSS_TL_OFF) |
+ (0xff00ULL << ITCT_HDR_BUS_INACTIVE_TL_OFF) |
+ (0xff00ULL << ITCT_HDR_MAX_CONN_TL_OFF) |
+ (0xff00ULL << ITCT_HDR_REJ_OPEN_TL_OFF));
+}
+
+static int clear_itct_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ u64 dev_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[dev_id];
+ u64 qw0;
+ u32 reg_val = hisi_sas_read32(hisi_hba, CFG_AGING_TIME);
+
+ reg_val |= CFG_AGING_TIME_ITCT_REL_MSK;
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, reg_val);
+
+ /* free itct */
+ udelay(1);
+ reg_val = hisi_sas_read32(hisi_hba, CFG_AGING_TIME);
+ reg_val &= ~CFG_AGING_TIME_ITCT_REL_MSK;
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, reg_val);
+
+ qw0 = le64_to_cpu(itct->qw0);
+ qw0 &= ~ITCT_HDR_VALID_MSK;
+ itct->qw0 = cpu_to_le64(qw0);
+
+ return 0;
+}
+
+static int reset_hw_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+ unsigned long end_time;
+ u32 val;
+ struct device *dev = hisi_hba->dev;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ u32 phy_ctrl = hisi_sas_phy_read32(hisi_hba, i, PHY_CTRL);
+
+ phy_ctrl |= PHY_CTRL_RESET_MSK;
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL, phy_ctrl);
+ }
+ msleep(1); /* It is safe to wait for 50us */
+
+ /* Ensure DMA tx & rx idle */
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ u32 dma_tx_status, dma_rx_status;
+
+ end_time = jiffies + msecs_to_jiffies(1000);
+
+ while (1) {
+ dma_tx_status = hisi_sas_phy_read32(hisi_hba, i,
+ DMA_TX_STATUS);
+ dma_rx_status = hisi_sas_phy_read32(hisi_hba, i,
+ DMA_RX_STATUS);
+
+ if (!(dma_tx_status & DMA_TX_STATUS_BUSY_MSK) &&
+ !(dma_rx_status & DMA_RX_STATUS_BUSY_MSK))
+ break;
+
+ msleep(20);
+ if (time_after(jiffies, end_time))
+ return -EIO;
+ }
+ }
+
+ /* Ensure axi bus idle */
+ end_time = jiffies + msecs_to_jiffies(1000);
+ while (1) {
+ u32 axi_status =
+ hisi_sas_read32(hisi_hba, AXI_CFG);
+
+ if (axi_status == 0)
+ break;
+
+ msleep(20);
+ if (time_after(jiffies, end_time))
+ return -EIO;
+ }
+
+ if (ACPI_HANDLE(dev)) {
+ acpi_status s;
+
+ s = acpi_evaluate_object(ACPI_HANDLE(dev), "_RST", NULL, NULL);
+ if (ACPI_FAILURE(s)) {
+ dev_err(dev, "Reset failed\n");
+ return -EIO;
+ }
+ } else if (hisi_hba->ctrl) {
+ /* Apply reset and disable clock */
+ /* clk disable reg is offset by +4 bytes from clk enable reg */
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_reset_reg,
+ RESET_VALUE);
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_clock_ena_reg + 4,
+ RESET_VALUE);
+ msleep(1);
+ regmap_read(hisi_hba->ctrl, hisi_hba->ctrl_reset_sts_reg, &val);
+ if (RESET_VALUE != (val & RESET_VALUE)) {
+ dev_err(dev, "Reset failed\n");
+ return -EIO;
+ }
+
+ /* De-reset and enable clock */
+ /* deassert rst reg is offset by +4 bytes from assert reg */
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_reset_reg + 4,
+ RESET_VALUE);
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_clock_ena_reg,
+ RESET_VALUE);
+ msleep(1);
+ regmap_read(hisi_hba->ctrl, hisi_hba->ctrl_reset_sts_reg, &val);
+ if (val & RESET_VALUE) {
+ dev_err(dev, "De-reset failed\n");
+ return -EIO;
+ }
+ } else {
+ dev_warn(dev, "no reset method\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void init_reg_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ /* Global registers init*/
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+ (u32)((1ULL << hisi_hba->queue_count) - 1));
+ hisi_sas_write32(hisi_hba, HGC_TRANS_TASK_CNT_LIMIT, 0x11);
+ hisi_sas_write32(hisi_hba, DEVICE_MSG_WORK_MODE, 0x1);
+ hisi_sas_write32(hisi_hba, HGC_SAS_TXFAIL_RETRY_CTRL, 0x1ff);
+ hisi_sas_write32(hisi_hba, HGC_ERR_STAT_EN, 0x401);
+ hisi_sas_write32(hisi_hba, CFG_1US_TIMER_TRSH, 0x64);
+ hisi_sas_write32(hisi_hba, HGC_GET_ITV_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, I_T_NEXUS_LOSS_TIME, 0x64);
+ hisi_sas_write32(hisi_hba, BUS_INACTIVE_LIMIT_TIME, 0x2710);
+ hisi_sas_write32(hisi_hba, REJECT_TO_OPEN_LIMIT_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, 0x7a12);
+ hisi_sas_write32(hisi_hba, HGC_DFX_CFG2, 0x9c40);
+ hisi_sas_write32(hisi_hba, FIS_LIST_BADDR_L, 0x2);
+ hisi_sas_write32(hisi_hba, INT_COAL_EN, 0xc);
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0x186a0);
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 1);
+ hisi_sas_write32(hisi_hba, ENT_INT_COAL_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, ENT_INT_COAL_CNT, 0x1);
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 0xffffffff);
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC_MSK, 0);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC1, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC2, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0);
+ hisi_sas_write32(hisi_hba, AXI_AHB_CLK_CFG, 0x2);
+ hisi_sas_write32(hisi_hba, CFG_SAS_CONFIG, 0x22000000);
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x88a);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CONFIG2, 0x7c080);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_RATE_NEGO, 0x415ee00);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_PCN, 0x80a80000);
+ hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d);
+ hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000);
+ hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0);
+ hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER, 0x13f0a);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT_COAL_EN, 3);
+ hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 8);
+ }
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ /* Delivery queue */
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+
+ /* Completion queue */
+ hisi_sas_write32(hisi_hba,
+ COMPL_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba,
+ COMPL_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+ }
+
+ /* itct */
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->itct_dma));
+
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->itct_dma));
+
+ /* iost */
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->iost_dma));
+
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->iost_dma));
+
+ /* breakpoint */
+ hisi_sas_write32(hisi_hba, BROKEN_MSG_ADDR_LO,
+ lower_32_bits(hisi_hba->breakpoint_dma));
+
+ hisi_sas_write32(hisi_hba, BROKEN_MSG_ADDR_HI,
+ upper_32_bits(hisi_hba->breakpoint_dma));
+}
+
+static int hw_init_v1_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ rc = reset_hw_v1_hw(hisi_hba);
+ if (rc) {
+ dev_err(dev, "hisi_sas_reset_hw failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ msleep(100);
+ init_reg_v1_hw(hisi_hba);
+
+ return 0;
+}
+
+static void enable_phy_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg |= PHY_CFG_ENA_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void disable_phy_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg &= ~PHY_CFG_ENA_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void start_phy_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ config_id_frame_v1_hw(hisi_hba, phy_no);
+ config_phy_opt_mode_v1_hw(hisi_hba, phy_no);
+ config_tx_tfe_autoneg_v1_hw(hisi_hba, phy_no);
+ enable_phy_v1_hw(hisi_hba, phy_no);
+}
+
+static void phy_hard_reset_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+ msleep(100);
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+}
+
+static void start_phys_v1_hw(struct timer_list *t)
+{
+ struct hisi_hba *hisi_hba = timer_container_of(hisi_hba, t, timer);
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x12a);
+ hisi_sas_phy_enable(hisi_hba, i, 1);
+ }
+}
+
+static void phys_init_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+ struct timer_list *timer = &hisi_hba->timer;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x6a);
+ hisi_sas_phy_read32(hisi_hba, i, CHL_INT2_MSK);
+ }
+
+ timer_setup(timer, start_phys_v1_hw, 0);
+ mod_timer(timer, jiffies + HZ);
+}
+
+static void sl_notify_ssp_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 sl_control;
+
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control |= SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+ msleep(1);
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control &= ~SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+}
+
+static enum sas_linkrate phy_get_max_linkrate_v1_hw(void)
+{
+ return SAS_LINK_RATE_6_0_GBPS;
+}
+
+static void phy_set_linkrate_v1_hw(struct hisi_hba *hisi_hba, int phy_no,
+ struct sas_phy_linkrates *r)
+{
+ enum sas_linkrate max = r->maximum_linkrate;
+ u32 prog_phy_link_rate = 0x800;
+
+ prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE,
+ prog_phy_link_rate);
+}
+
+static int get_wideport_bitmap_v1_hw(struct hisi_hba *hisi_hba, int port_id)
+{
+ int i, bitmap = 0;
+ u32 phy_port_num_ma = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+
+ for (i = 0; i < hisi_hba->n_phy; i++)
+ if (((phy_port_num_ma >> (i * 4)) & 0xf) == port_id)
+ bitmap |= 1 << i;
+
+ return bitmap;
+}
+
+/* DQ lock must be taken here */
+static void start_delivery_v1_hw(struct hisi_sas_dq *dq)
+{
+ struct hisi_hba *hisi_hba = dq->hisi_hba;
+ struct hisi_sas_slot *s, *s1, *s2 = NULL;
+ int dlvry_queue = dq->id;
+ int wp;
+
+ list_for_each_entry_safe(s, s1, &dq->list, delivery) {
+ if (!s->ready)
+ break;
+ s2 = s;
+ list_del(&s->delivery);
+ }
+
+ if (!s2)
+ return;
+
+ /*
+ * Ensure that memories for slots built on other CPUs is observed.
+ */
+ smp_rmb();
+ wp = (s2->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS;
+
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), wp);
+}
+
+static void prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_cmd_hdr *hdr,
+ struct scatterlist *scatter,
+ int n_elem)
+{
+ struct hisi_sas_sge_page *sge_page = hisi_sas_sge_addr_mem(slot);
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(scatter, sg, n_elem, i) {
+ struct hisi_sas_sge *entry = &sge_page->sge[i];
+
+ entry->addr = cpu_to_le64(sg_dma_address(sg));
+ entry->page_ctrl_0 = entry->page_ctrl_1 = 0;
+ entry->data_len = cpu_to_le32(sg_dma_len(sg));
+ entry->data_off = 0;
+ }
+
+ hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot));
+
+ hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+}
+
+static void prep_smp_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_port *port = slot->port;
+ struct scatterlist *sg_req;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ dma_addr_t req_dma_addr;
+ unsigned int req_len;
+
+ /* req */
+ sg_req = &task->smp_task.smp_req;
+ req_len = sg_dma_len(sg_req);
+ req_dma_addr = sg_dma_address(sg_req);
+
+ /* create header */
+ /* dw0 */
+ hdr->dw0 = cpu_to_le32((port->id << CMD_HDR_PORT_OFF) |
+ (1 << CMD_HDR_PRIORITY_OFF) | /* high pri */
+ (1 << CMD_HDR_MODE_OFF) | /* ini mode */
+ (2 << CMD_HDR_CMD_OFF)); /* smp */
+
+ /* map itct entry */
+ hdr->dw1 = cpu_to_le32(sas_dev->device_id << CMD_HDR_DEVICE_ID_OFF);
+
+ /* dw2 */
+ hdr->dw2 = cpu_to_le32((((req_len-4)/4) << CMD_HDR_CFL_OFF) |
+ (HISI_SAS_MAX_SMP_RESP_SZ/4 <<
+ CMD_HDR_MRFL_OFF));
+
+ hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF);
+
+ hdr->cmd_table_addr = cpu_to_le64(req_dma_addr);
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+}
+
+static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_port *port = slot->port;
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+ struct sas_tmf_task *tmf = slot->tmf;
+ int has_data = 0, priority = !!tmf;
+ u8 *buf_cmd;
+ u32 dw1, dw2;
+
+ /* create header */
+ hdr->dw0 = cpu_to_le32((1 << CMD_HDR_RESP_REPORT_OFF) |
+ (0x2 << CMD_HDR_TLR_CTRL_OFF) |
+ (port->id << CMD_HDR_PORT_OFF) |
+ (priority << CMD_HDR_PRIORITY_OFF) |
+ (1 << CMD_HDR_MODE_OFF) | /* ini mode */
+ (1 << CMD_HDR_CMD_OFF)); /* ssp */
+
+ dw1 = 1 << CMD_HDR_VERIFY_DTL_OFF;
+
+ if (tmf) {
+ dw1 |= 3 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ } else {
+ switch (scsi_cmnd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ dw1 |= 2 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ has_data = 1;
+ break;
+ case DMA_FROM_DEVICE:
+ dw1 |= 1 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ has_data = 1;
+ break;
+ default:
+ dw1 |= 0 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ }
+ }
+
+ /* map itct entry */
+ dw1 |= sas_dev->device_id << CMD_HDR_DEVICE_ID_OFF;
+ hdr->dw1 = cpu_to_le32(dw1);
+
+ if (tmf) {
+ dw2 = ((sizeof(struct ssp_tmf_iu) +
+ sizeof(struct ssp_frame_hdr)+3)/4) <<
+ CMD_HDR_CFL_OFF;
+ } else {
+ dw2 = ((sizeof(struct ssp_command_iu) +
+ sizeof(struct ssp_frame_hdr)+3)/4) <<
+ CMD_HDR_CFL_OFF;
+ }
+
+ dw2 |= (HISI_SAS_MAX_SSP_RESP_SZ/4) << CMD_HDR_MRFL_OFF;
+
+ hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF);
+
+ if (has_data)
+ prep_prd_sge_v1_hw(hisi_hba, slot, hdr, task->scatter,
+ slot->n_elem);
+
+ hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len);
+ hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot));
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+
+ buf_cmd = hisi_sas_cmd_hdr_addr_mem(slot) +
+ sizeof(struct ssp_frame_hdr);
+ hdr->dw2 = cpu_to_le32(dw2);
+
+ memcpy(buf_cmd, &task->ssp_task.LUN, 8);
+ if (!tmf) {
+ buf_cmd[9] = task->ssp_task.task_attr;
+ memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
+ } else {
+ buf_cmd[10] = tmf->tmf;
+ switch (tmf->tmf) {
+ case TMF_ABORT_TASK:
+ case TMF_QUERY_TASK:
+ buf_cmd[12] =
+ (tmf->tag_of_task_to_be_managed >> 8) & 0xff;
+ buf_cmd[13] =
+ tmf->tag_of_task_to_be_managed & 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* by default, task resp is complete */
+static void slot_err_v1_hw(struct hisi_hba *hisi_hba,
+ struct sas_task *task,
+ struct hisi_sas_slot *slot)
+{
+ struct task_status_struct *ts = &task->task_status;
+ struct hisi_sas_err_record_v1 *err_record =
+ hisi_sas_status_buf_addr_mem(slot);
+ struct device *dev = hisi_hba->dev;
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ {
+ int error = -1;
+ u32 dma_err_type = le32_to_cpu(err_record->dma_err_type);
+ u32 dma_tx_err_type = ((dma_err_type &
+ ERR_HDR_DMA_TX_ERR_TYPE_MSK)) >>
+ ERR_HDR_DMA_TX_ERR_TYPE_OFF;
+ u32 dma_rx_err_type = ((dma_err_type &
+ ERR_HDR_DMA_RX_ERR_TYPE_MSK)) >>
+ ERR_HDR_DMA_RX_ERR_TYPE_OFF;
+ u32 trans_tx_fail_type =
+ le32_to_cpu(err_record->trans_tx_fail_type);
+ u32 trans_rx_fail_type =
+ le32_to_cpu(err_record->trans_rx_fail_type);
+
+ if (dma_tx_err_type) {
+ /* dma tx err */
+ error = ffs(dma_tx_err_type)
+ - 1 + DMA_TX_ERR_BASE;
+ } else if (dma_rx_err_type) {
+ /* dma rx err */
+ error = ffs(dma_rx_err_type)
+ - 1 + DMA_RX_ERR_BASE;
+ } else if (trans_tx_fail_type) {
+ /* trans tx err */
+ error = ffs(trans_tx_fail_type)
+ - 1 + TRANS_TX_FAIL_BASE;
+ } else if (trans_rx_fail_type) {
+ /* trans rx err */
+ error = ffs(trans_rx_fail_type)
+ - 1 + TRANS_RX_FAIL_BASE;
+ }
+
+ switch (error) {
+ case DMA_TX_DATA_UNDERFLOW_ERR:
+ case DMA_RX_DATA_UNDERFLOW_ERR:
+ {
+ ts->residual = 0;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ }
+ case DMA_TX_DATA_SGL_OVERFLOW_ERR:
+ case DMA_TX_DIF_SGL_OVERFLOW_ERR:
+ case DMA_TX_XFER_RDY_LENGTH_OVERFLOW_ERR:
+ case DMA_RX_DATA_OVERFLOW_ERR:
+ case TRANS_RX_FRAME_OVERRUN_ERR:
+ case TRANS_RX_LINK_BUF_OVERRUN_ERR:
+ {
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ break;
+ }
+ case TRANS_TX_PHY_NOT_ENABLE_ERR:
+ {
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ }
+ case TRANS_TX_OPEN_REJCT_WRONG_DEST_ERR:
+ case TRANS_TX_OPEN_REJCT_ZONE_VIOLATION_ERR:
+ case TRANS_TX_OPEN_REJCT_BY_OTHER_ERR:
+ case TRANS_TX_OPEN_REJCT_AIP_TIMEOUT_ERR:
+ case TRANS_TX_OPEN_REJCT_STP_BUSY_ERR:
+ case TRANS_TX_OPEN_REJCT_PROTOCOL_NOT_SUPPORT_ERR:
+ case TRANS_TX_OPEN_REJCT_RATE_NOT_SUPPORT_ERR:
+ case TRANS_TX_OPEN_REJCT_BAD_DEST_ERR:
+ case TRANS_TX_OPEN_BREAK_RECEIVE_ERR:
+ case TRANS_TX_OPEN_REJCT_PATHWAY_BLOCKED_ERR:
+ case TRANS_TX_OPEN_REJCT_NO_DEST_ERR:
+ case TRANS_TX_OPEN_RETRY_ERR:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ }
+ case TRANS_TX_OPEN_TIMEOUT_ERR:
+ {
+ ts->stat = SAS_OPEN_TO;
+ break;
+ }
+ case TRANS_TX_NAK_RECEIVE_ERR:
+ case TRANS_TX_ACK_NAK_TIMEOUT_ERR:
+ {
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ }
+ case TRANS_TX_CREDIT_TIMEOUT_ERR:
+ case TRANS_TX_CLOSE_NORMAL_ERR:
+ {
+ /* This will request a retry */
+ ts->stat = SAS_QUEUE_FULL;
+ slot->abort = 1;
+ break;
+ }
+ default:
+ {
+ ts->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ break;
+ }
+ }
+ }
+ break;
+ case SAS_PROTOCOL_SMP:
+ ts->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ break;
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ {
+ dev_err(dev, "slot err: SATA/STP not supported\n");
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void slot_complete_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_device *sas_dev;
+ struct device *dev = hisi_hba->dev;
+ struct task_status_struct *ts;
+ struct domain_device *device;
+ struct hisi_sas_complete_v1_hdr *complete_queue =
+ hisi_hba->complete_hdr[slot->cmplt_queue];
+ struct hisi_sas_complete_v1_hdr *complete_hdr;
+ unsigned long flags;
+ u32 cmplt_hdr_data;
+
+ complete_hdr = &complete_queue[slot->cmplt_queue_slot];
+ cmplt_hdr_data = le32_to_cpu(complete_hdr->data);
+
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return;
+
+ ts = &task->task_status;
+ device = task->dev;
+ sas_dev = device->lldd_dev;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ memset(ts, 0, sizeof(*ts));
+ ts->resp = SAS_TASK_COMPLETE;
+
+ if (unlikely(!sas_dev)) {
+ dev_dbg(dev, "slot complete: port has no device\n");
+ ts->stat = SAS_PHY_DOWN;
+ goto out;
+ }
+
+ if (cmplt_hdr_data & CMPLT_HDR_IO_CFG_ERR_MSK) {
+ u32 info_reg = hisi_sas_read32(hisi_hba, HGC_INVLD_DQE_INFO);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_DQ_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq IPTT err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_TYPE_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq type err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_FORCE_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq force phy err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_PHY_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq phy id err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_ABORT_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq abort flag err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_IPTT_OF_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq IPTT or ICT err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_SSP_ERR_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq SSP frame type err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_OFL_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq order frame len err\n",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ goto out;
+ }
+
+ if (cmplt_hdr_data & CMPLT_HDR_ERR_RCRD_XFRD_MSK &&
+ !(cmplt_hdr_data & CMPLT_HDR_RSPNS_XFRD_MSK)) {
+
+ slot_err_v1_hw(hisi_hba, task, slot);
+ if (unlikely(slot->abort)) {
+ if (dev_is_sata(device) && task->ata_task.use_ncq)
+ sas_ata_device_link_abort(device, true);
+ else
+ sas_task_abort(task);
+
+ return;
+ }
+ goto out;
+ }
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ {
+ struct hisi_sas_status_buffer *status_buffer =
+ hisi_sas_status_buf_addr_mem(slot);
+ struct ssp_response_iu *iu = (struct ssp_response_iu *)
+ &status_buffer->iu[0];
+
+ sas_ssp_task_response(dev, task, iu);
+ break;
+ }
+ case SAS_PROTOCOL_SMP:
+ {
+ struct scatterlist *sg_resp = &task->smp_task.smp_resp;
+ void *to = page_address(sg_page(sg_resp));
+
+ ts->stat = SAS_SAM_STAT_GOOD;
+
+ memcpy(to + sg_resp->offset,
+ hisi_sas_status_buf_addr_mem(slot) +
+ sizeof(struct hisi_sas_err_record),
+ sg_resp->length);
+ break;
+ }
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ dev_err(dev, "slot complete: SATA/STP not supported\n");
+ break;
+
+ default:
+ ts->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ break;
+ }
+
+ if (!slot->port->port_attached) {
+ dev_err(dev, "slot complete: port %d has removed\n",
+ slot->port->sas_port.id);
+ ts->stat = SAS_PHY_DOWN;
+ }
+
+out:
+ hisi_sas_slot_task_free(hisi_hba, task, slot, true);
+
+ if (task->task_done)
+ task->task_done(task);
+}
+
+/* Interrupts */
+static irqreturn_t int_phyup_v1_hw(int irq_no, void *p)
+{
+ struct hisi_sas_phy *phy = p;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = hisi_hba->dev;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ int i, phy_no = sas_phy->id;
+ u32 irq_value, context, port_id, link_rate;
+ u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd;
+ struct sas_identify_frame *id = (struct sas_identify_frame *)frame_rcvd;
+ irqreturn_t res = IRQ_HANDLED;
+
+ irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2);
+ if (!(irq_value & CHL_INT2_SL_PHY_ENA_MSK)) {
+ dev_dbg(dev, "phyup: irq_value = %x not set enable bit\n",
+ irq_value);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ context = hisi_sas_read32(hisi_hba, PHY_CONTEXT);
+ if (context & 1 << phy_no) {
+ dev_err(dev, "phyup: phy%d SATA attached equipment\n",
+ phy_no);
+ goto end;
+ }
+
+ port_id = (hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA) >> (4 * phy_no))
+ & 0xf;
+ if (port_id == 0xf) {
+ dev_err(dev, "phyup: phy%d invalid portid\n", phy_no);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ for (i = 0; i < 6; i++) {
+ u32 idaf = hisi_sas_phy_read32(hisi_hba, phy_no,
+ RX_IDAF_DWORD0 + (i * 4));
+ frame_rcvd[i] = __swab32(idaf);
+ }
+
+ /* Get the linkrate */
+ link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE);
+ link_rate = (link_rate >> (phy_no * 4)) & 0xf;
+ sas_phy->linkrate = link_rate;
+ sas_phy->oob_mode = SAS_OOB_MODE;
+ memcpy(sas_phy->attached_sas_addr,
+ &id->sas_addr, SAS_ADDR_SIZE);
+ dev_info(dev, "phyup: phy%d link_rate=%d\n",
+ phy_no, link_rate);
+ phy->port_id = port_id;
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+ phy->phy_type |= PORT_TYPE_SAS;
+ phy->phy_attached = 1;
+ phy->identify.device_type = id->dev_type;
+ phy->frame_rcvd_size = sizeof(struct sas_identify_frame);
+ if (phy->identify.device_type == SAS_END_DEVICE)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != SAS_PHY_UNUSED)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SMP;
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);
+end:
+ if (phy->reset_completion)
+ complete(phy->reset_completion);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2,
+ CHL_INT2_SL_PHY_ENA_MSK);
+
+ if (irq_value & CHL_INT2_SL_PHY_ENA_MSK) {
+ u32 chl_int0 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0);
+
+ chl_int0 &= ~CHL_INT0_PHYCTRL_NOTRDY_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, chl_int0);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK, 0x3ce3ee);
+ }
+
+ return res;
+}
+
+static irqreturn_t int_bcast_v1_hw(int irq, void *p)
+{
+ struct hisi_sas_phy *phy = p;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct device *dev = hisi_hba->dev;
+ int phy_no = sas_phy->id;
+ u32 irq_value;
+ irqreturn_t res = IRQ_HANDLED;
+
+ irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2);
+
+ if (!(irq_value & CHL_INT2_SL_RX_BC_ACK_MSK)) {
+ dev_err(dev, "bcast: irq_value = %x not set enable bit\n",
+ irq_value);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ hisi_sas_phy_bcast(phy);
+
+end:
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2,
+ CHL_INT2_SL_RX_BC_ACK_MSK);
+
+ return res;
+}
+
+static irqreturn_t int_abnormal_v1_hw(int irq, void *p)
+{
+ struct hisi_sas_phy *phy = p;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = hisi_hba->dev;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ u32 irq_value, irq_mask_old;
+ int phy_no = sas_phy->id;
+
+ /* mask_int0 */
+ irq_mask_old = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK, 0x3fffff);
+
+ /* read int0 */
+ irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0);
+
+ if (irq_value & CHL_INT0_PHYCTRL_NOTRDY_MSK) {
+ u32 phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+
+ hisi_sas_phy_down(hisi_hba, phy_no,
+ (phy_state & 1 << phy_no) ? 1 : 0,
+ GFP_ATOMIC);
+ }
+
+ if (irq_value & CHL_INT0_ID_TIMEOUT_MSK)
+ dev_dbg(dev, "abnormal: ID_TIMEOUT phy%d identify timeout\n",
+ phy_no);
+
+ if (irq_value & CHL_INT0_DWS_LOST_MSK)
+ dev_dbg(dev, "abnormal: DWS_LOST phy%d dws lost\n", phy_no);
+
+ if (irq_value & CHL_INT0_SN_FAIL_NGR_MSK)
+ dev_dbg(dev, "abnormal: SN_FAIL_NGR phy%d sn fail ngr\n",
+ phy_no);
+
+ if (irq_value & CHL_INT0_SL_IDAF_FAIL_MSK ||
+ irq_value & CHL_INT0_SL_OPAF_FAIL_MSK)
+ dev_dbg(dev, "abnormal: SL_ID/OPAF_FAIL phy%d check adr frm err\n",
+ phy_no);
+
+ if (irq_value & CHL_INT0_SL_PS_FAIL_OFF)
+ dev_dbg(dev, "abnormal: SL_PS_FAIL phy%d fail\n", phy_no);
+
+ /* write to zero */
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, irq_value);
+
+ if (irq_value & CHL_INT0_PHYCTRL_NOTRDY_MSK)
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK,
+ 0x3fffff & ~CHL_INT0_MSK_PHYCTRL_NOTRDY_MSK);
+ else
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK,
+ irq_mask_old);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cq_interrupt_v1_hw(int irq, void *p)
+{
+ struct hisi_sas_cq *cq = p;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ struct hisi_sas_slot *slot;
+ int queue = cq->id;
+ struct hisi_sas_complete_v1_hdr *complete_queue =
+ (struct hisi_sas_complete_v1_hdr *)
+ hisi_hba->complete_hdr[queue];
+ u32 rd_point = cq->rd_point, wr_point;
+
+ spin_lock(&hisi_hba->lock);
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue);
+ wr_point = hisi_sas_read32(hisi_hba,
+ COMPL_Q_0_WR_PTR + (0x14 * queue));
+
+ while (rd_point != wr_point) {
+ struct hisi_sas_complete_v1_hdr *complete_hdr;
+ int idx;
+ u32 cmplt_hdr_data;
+
+ complete_hdr = &complete_queue[rd_point];
+ cmplt_hdr_data = le32_to_cpu(complete_hdr->data);
+ idx = (cmplt_hdr_data & CMPLT_HDR_IPTT_MSK) >>
+ CMPLT_HDR_IPTT_OFF;
+ slot = &hisi_hba->slot_info[idx];
+
+ /* The completion queue and queue slot index are not
+ * necessarily the same as the delivery queue and
+ * queue slot index.
+ */
+ slot->cmplt_queue_slot = rd_point;
+ slot->cmplt_queue = queue;
+ slot_complete_v1_hw(hisi_hba, slot);
+
+ if (++rd_point >= HISI_SAS_QUEUE_SLOTS)
+ rd_point = 0;
+ }
+
+ /* update rd_point */
+ cq->rd_point = rd_point;
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);
+ spin_unlock(&hisi_hba->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fatal_ecc_int_v1_hw(int irq, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ struct device *dev = hisi_hba->dev;
+ u32 ecc_int = hisi_sas_read32(hisi_hba, SAS_ECC_INTR);
+
+ if (ecc_int & SAS_ECC_INTR_DQ_ECC1B_MSK) {
+ u32 ecc_err = hisi_sas_read32(hisi_hba, HGC_ECC_ERR);
+
+ panic("%s: Fatal DQ 1b ECC interrupt (0x%x)\n",
+ dev_name(dev), ecc_err);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_DQ_ECCBAD_MSK) {
+ u32 addr = (hisi_sas_read32(hisi_hba, HGC_DQ_ECC_ADDR) &
+ HGC_DQ_ECC_ADDR_BAD_MSK) >>
+ HGC_DQ_ECC_ADDR_BAD_OFF;
+
+ panic("%s: Fatal DQ RAM ECC interrupt @ 0x%08x\n",
+ dev_name(dev), addr);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_IOST_ECC1B_MSK) {
+ u32 ecc_err = hisi_sas_read32(hisi_hba, HGC_ECC_ERR);
+
+ panic("%s: Fatal IOST 1b ECC interrupt (0x%x)\n",
+ dev_name(dev), ecc_err);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_IOST_ECCBAD_MSK) {
+ u32 addr = (hisi_sas_read32(hisi_hba, HGC_IOST_ECC_ADDR) &
+ HGC_IOST_ECC_ADDR_BAD_MSK) >>
+ HGC_IOST_ECC_ADDR_BAD_OFF;
+
+ panic("%s: Fatal IOST RAM ECC interrupt @ 0x%08x\n",
+ dev_name(dev), addr);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_ITCT_ECCBAD_MSK) {
+ u32 addr = (hisi_sas_read32(hisi_hba, HGC_ITCT_ECC_ADDR) &
+ HGC_ITCT_ECC_ADDR_BAD_MSK) >>
+ HGC_ITCT_ECC_ADDR_BAD_OFF;
+
+ panic("%s: Fatal TCT RAM ECC interrupt @ 0x%08x\n",
+ dev_name(dev), addr);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_ITCT_ECC1B_MSK) {
+ u32 ecc_err = hisi_sas_read32(hisi_hba, HGC_ECC_ERR);
+
+ panic("%s: Fatal ITCT 1b ECC interrupt (0x%x)\n",
+ dev_name(dev), ecc_err);
+ }
+
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR, ecc_int | 0x3f);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fatal_axi_int_v1_hw(int irq, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ struct device *dev = hisi_hba->dev;
+ u32 axi_int = hisi_sas_read32(hisi_hba, ENT_INT_SRC2);
+ u32 axi_info = hisi_sas_read32(hisi_hba, HGC_AXI_FIFO_ERR_INFO);
+
+ if (axi_int & ENT_INT_SRC2_DQ_CFG_ERR_MSK)
+ panic("%s: Fatal DQ_CFG_ERR interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ if (axi_int & ENT_INT_SRC2_CQ_CFG_ERR_MSK)
+ panic("%s: Fatal CQ_CFG_ERR interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ if (axi_int & ENT_INT_SRC2_AXI_WRONG_INT_MSK)
+ panic("%s: Fatal AXI_WRONG_INT interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ if (axi_int & ENT_INT_SRC2_AXI_OVERLF_INT_MSK)
+ panic("%s: Fatal AXI_OVERLF_INT incorrect interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC2, axi_int | 0x30000000);
+
+ return IRQ_HANDLED;
+}
+
+static irq_handler_t phy_interrupts[HISI_SAS_PHY_INT_NR] = {
+ int_bcast_v1_hw,
+ int_phyup_v1_hw,
+ int_abnormal_v1_hw
+};
+
+static irq_handler_t fatal_interrupts[HISI_SAS_MAX_QUEUES] = {
+ fatal_ecc_int_v1_hw,
+ fatal_axi_int_v1_hw
+};
+
+static int interrupt_init_v1_hw(struct hisi_hba *hisi_hba)
+{
+ struct platform_device *pdev = hisi_hba->platform_dev;
+ struct device *dev = &pdev->dev;
+ int i, j, irq, rc, idx;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[i];
+
+ idx = i * HISI_SAS_PHY_INT_NR;
+ for (j = 0; j < HISI_SAS_PHY_INT_NR; j++, idx++) {
+ irq = platform_get_irq(pdev, idx);
+ if (irq < 0)
+ return irq;
+
+ rc = devm_request_irq(dev, irq, phy_interrupts[j], 0,
+ DRV_NAME " phy", phy);
+ if (rc) {
+ dev_err(dev, "irq init: could not request phy interrupt %d, rc=%d\n",
+ irq, rc);
+ return rc;
+ }
+ }
+ }
+
+ idx = hisi_hba->n_phy * HISI_SAS_PHY_INT_NR;
+ for (i = 0; i < hisi_hba->queue_count; i++, idx++) {
+ irq = platform_get_irq(pdev, idx);
+ if (irq < 0)
+ return irq;
+
+ rc = devm_request_irq(dev, irq, cq_interrupt_v1_hw, 0,
+ DRV_NAME " cq", &hisi_hba->cq[i]);
+ if (rc) {
+ dev_err(dev, "irq init: could not request cq interrupt %d, rc=%d\n",
+ irq, rc);
+ return rc;
+ }
+ }
+
+ idx = (hisi_hba->n_phy * HISI_SAS_PHY_INT_NR) + hisi_hba->queue_count;
+ for (i = 0; i < HISI_SAS_FATAL_INT_NR; i++, idx++) {
+ irq = platform_get_irq(pdev, idx);
+ if (irq < 0)
+ return irq;
+
+ rc = devm_request_irq(dev, irq, fatal_interrupts[i], 0,
+ DRV_NAME " fatal", hisi_hba);
+ if (rc) {
+ dev_err(dev, "irq init: could not request fatal interrupt %d, rc=%d\n",
+ irq, rc);
+ return rc;
+ }
+ }
+
+ hisi_hba->cq_nvecs = hisi_hba->queue_count;
+
+ return 0;
+}
+
+static int interrupt_openall_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ /* Clear interrupt status */
+ val = hisi_sas_phy_read32(hisi_hba, i, CHL_INT0);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, val);
+ val = hisi_sas_phy_read32(hisi_hba, i, CHL_INT1);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, val);
+ val = hisi_sas_phy_read32(hisi_hba, i, CHL_INT2);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, val);
+
+ /* Unmask interrupt */
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0_MSK, 0x3ce3ee);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0x17fff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8000012a);
+
+ /* bypass chip bug mask abnormal intr */
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0_MSK,
+ 0x3fffff & ~CHL_INT0_MSK_PHYCTRL_NOTRDY_MSK);
+ }
+
+ return 0;
+}
+
+static int hisi_sas_v1_init(struct hisi_hba *hisi_hba)
+{
+ int rc;
+
+ rc = hw_init_v1_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ rc = interrupt_init_v1_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ rc = interrupt_openall_v1_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static struct attribute *host_v1_hw_attrs[] = {
+ &dev_attr_phy_event_threshold.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(host_v1_hw);
+
+static int check_fw_info_v1_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+
+ if (hisi_hba->n_phy < 0 || hisi_hba->n_phy > 9) {
+ dev_err(dev, "invalid phy number from FW\n");
+ return -EINVAL;
+ }
+
+ if (hisi_hba->queue_count < 0 || hisi_hba->queue_count > 32) {
+ dev_err(dev, "invalid queue count from FW\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct scsi_host_template sht_v1_hw = {
+ LIBSAS_SHT_BASE_NO_SLAVE_INIT
+ .sdev_configure = hisi_sas_sdev_configure,
+ .scan_finished = hisi_sas_scan_finished,
+ .scan_start = hisi_sas_scan_start,
+ .sg_tablesize = HISI_SAS_SGE_PAGE_CNT,
+ .sdev_init = hisi_sas_sdev_init,
+ .shost_groups = host_v1_hw_groups,
+ .host_reset = hisi_sas_host_reset,
+};
+
+static const struct hisi_sas_hw hisi_sas_v1_hw = {
+ .hw_init = hisi_sas_v1_init,
+ .fw_info_check = check_fw_info_v1_hw,
+ .setup_itct = setup_itct_v1_hw,
+ .sl_notify_ssp = sl_notify_ssp_v1_hw,
+ .clear_itct = clear_itct_v1_hw,
+ .prep_smp = prep_smp_v1_hw,
+ .prep_ssp = prep_ssp_v1_hw,
+ .start_delivery = start_delivery_v1_hw,
+ .phys_init = phys_init_v1_hw,
+ .phy_start = start_phy_v1_hw,
+ .phy_disable = disable_phy_v1_hw,
+ .phy_hard_reset = phy_hard_reset_v1_hw,
+ .phy_set_linkrate = phy_set_linkrate_v1_hw,
+ .phy_get_max_linkrate = phy_get_max_linkrate_v1_hw,
+ .get_wideport_bitmap = get_wideport_bitmap_v1_hw,
+ .complete_hdr_size = sizeof(struct hisi_sas_complete_v1_hdr),
+ .sht = &sht_v1_hw,
+};
+
+static int hisi_sas_v1_probe(struct platform_device *pdev)
+{
+ return hisi_sas_probe(pdev, &hisi_sas_v1_hw);
+}
+
+static const struct of_device_id sas_v1_of_match[] = {
+ { .compatible = "hisilicon,hip05-sas-v1",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sas_v1_of_match);
+
+static const struct acpi_device_id sas_v1_acpi_match[] = {
+ { "HISI0161", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(acpi, sas_v1_acpi_match);
+
+static struct platform_driver hisi_sas_v1_driver = {
+ .probe = hisi_sas_v1_probe,
+ .remove = hisi_sas_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = sas_v1_of_match,
+ .acpi_match_table = sas_v1_acpi_match,
+ },
+};
+
+module_platform_driver(hisi_sas_v1_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
+MODULE_DESCRIPTION("HISILICON SAS controller v1 hw driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
new file mode 100644
index 000000000000..f3516a0611dd
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -0,0 +1,3667 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2016 Linaro Ltd.
+ * Copyright (c) 2016 Hisilicon Limited.
+ */
+
+#include "hisi_sas.h"
+#define DRV_NAME "hisi_sas_v2_hw"
+
+/* global registers need init*/
+#define DLVRY_QUEUE_ENABLE 0x0
+#define IOST_BASE_ADDR_LO 0x8
+#define IOST_BASE_ADDR_HI 0xc
+#define ITCT_BASE_ADDR_LO 0x10
+#define ITCT_BASE_ADDR_HI 0x14
+#define IO_BROKEN_MSG_ADDR_LO 0x18
+#define IO_BROKEN_MSG_ADDR_HI 0x1c
+#define PHY_CONTEXT 0x20
+#define PHY_STATE 0x24
+#define PHY_PORT_NUM_MA 0x28
+#define PORT_STATE 0x2c
+#define PORT_STATE_PHY8_PORT_NUM_OFF 16
+#define PORT_STATE_PHY8_PORT_NUM_MSK (0xf << PORT_STATE_PHY8_PORT_NUM_OFF)
+#define PORT_STATE_PHY8_CONN_RATE_OFF 20
+#define PORT_STATE_PHY8_CONN_RATE_MSK (0xf << PORT_STATE_PHY8_CONN_RATE_OFF)
+#define PHY_CONN_RATE 0x30
+#define HGC_TRANS_TASK_CNT_LIMIT 0x38
+#define AXI_AHB_CLK_CFG 0x3c
+#define ITCT_CLR 0x44
+#define ITCT_CLR_EN_OFF 16
+#define ITCT_CLR_EN_MSK (0x1 << ITCT_CLR_EN_OFF)
+#define ITCT_DEV_OFF 0
+#define ITCT_DEV_MSK (0x7ff << ITCT_DEV_OFF)
+#define AXI_USER1 0x48
+#define AXI_USER2 0x4c
+#define IO_SATA_BROKEN_MSG_ADDR_LO 0x58
+#define IO_SATA_BROKEN_MSG_ADDR_HI 0x5c
+#define SATA_INITI_D2H_STORE_ADDR_LO 0x60
+#define SATA_INITI_D2H_STORE_ADDR_HI 0x64
+#define HGC_SAS_TX_OPEN_FAIL_RETRY_CTRL 0x84
+#define HGC_SAS_TXFAIL_RETRY_CTRL 0x88
+#define HGC_GET_ITV_TIME 0x90
+#define DEVICE_MSG_WORK_MODE 0x94
+#define OPENA_WT_CONTI_TIME 0x9c
+#define I_T_NEXUS_LOSS_TIME 0xa0
+#define MAX_CON_TIME_LIMIT_TIME 0xa4
+#define BUS_INACTIVE_LIMIT_TIME 0xa8
+#define REJECT_TO_OPEN_LIMIT_TIME 0xac
+#define CFG_AGING_TIME 0xbc
+#define HGC_DFX_CFG2 0xc0
+#define HGC_IOMB_PROC1_STATUS 0x104
+#define CFG_1US_TIMER_TRSH 0xcc
+#define HGC_LM_DFX_STATUS2 0x128
+#define HGC_LM_DFX_STATUS2_IOSTLIST_OFF 0
+#define HGC_LM_DFX_STATUS2_IOSTLIST_MSK (0xfff << \
+ HGC_LM_DFX_STATUS2_IOSTLIST_OFF)
+#define HGC_LM_DFX_STATUS2_ITCTLIST_OFF 12
+#define HGC_LM_DFX_STATUS2_ITCTLIST_MSK (0x7ff << \
+ HGC_LM_DFX_STATUS2_ITCTLIST_OFF)
+#define HGC_CQE_ECC_ADDR 0x13c
+#define HGC_CQE_ECC_1B_ADDR_OFF 0
+#define HGC_CQE_ECC_1B_ADDR_MSK (0x3f << HGC_CQE_ECC_1B_ADDR_OFF)
+#define HGC_CQE_ECC_MB_ADDR_OFF 8
+#define HGC_CQE_ECC_MB_ADDR_MSK (0x3f << HGC_CQE_ECC_MB_ADDR_OFF)
+#define HGC_IOST_ECC_ADDR 0x140
+#define HGC_IOST_ECC_1B_ADDR_OFF 0
+#define HGC_IOST_ECC_1B_ADDR_MSK (0x3ff << HGC_IOST_ECC_1B_ADDR_OFF)
+#define HGC_IOST_ECC_MB_ADDR_OFF 16
+#define HGC_IOST_ECC_MB_ADDR_MSK (0x3ff << HGC_IOST_ECC_MB_ADDR_OFF)
+#define HGC_DQE_ECC_ADDR 0x144
+#define HGC_DQE_ECC_1B_ADDR_OFF 0
+#define HGC_DQE_ECC_1B_ADDR_MSK (0xfff << HGC_DQE_ECC_1B_ADDR_OFF)
+#define HGC_DQE_ECC_MB_ADDR_OFF 16
+#define HGC_DQE_ECC_MB_ADDR_MSK (0xfff << HGC_DQE_ECC_MB_ADDR_OFF)
+#define HGC_INVLD_DQE_INFO 0x148
+#define HGC_INVLD_DQE_INFO_FB_CH0_OFF 9
+#define HGC_INVLD_DQE_INFO_FB_CH0_MSK (0x1 << HGC_INVLD_DQE_INFO_FB_CH0_OFF)
+#define HGC_INVLD_DQE_INFO_FB_CH3_OFF 18
+#define HGC_ITCT_ECC_ADDR 0x150
+#define HGC_ITCT_ECC_1B_ADDR_OFF 0
+#define HGC_ITCT_ECC_1B_ADDR_MSK (0x3ff << \
+ HGC_ITCT_ECC_1B_ADDR_OFF)
+#define HGC_ITCT_ECC_MB_ADDR_OFF 16
+#define HGC_ITCT_ECC_MB_ADDR_MSK (0x3ff << \
+ HGC_ITCT_ECC_MB_ADDR_OFF)
+#define HGC_AXI_FIFO_ERR_INFO 0x154
+#define AXI_ERR_INFO_OFF 0
+#define AXI_ERR_INFO_MSK (0xff << AXI_ERR_INFO_OFF)
+#define FIFO_ERR_INFO_OFF 8
+#define FIFO_ERR_INFO_MSK (0xff << FIFO_ERR_INFO_OFF)
+#define INT_COAL_EN 0x19c
+#define OQ_INT_COAL_TIME 0x1a0
+#define OQ_INT_COAL_CNT 0x1a4
+#define ENT_INT_COAL_TIME 0x1a8
+#define ENT_INT_COAL_CNT 0x1ac
+#define OQ_INT_SRC 0x1b0
+#define OQ_INT_SRC_MSK 0x1b4
+#define ENT_INT_SRC1 0x1b8
+#define ENT_INT_SRC1_D2H_FIS_CH0_OFF 0
+#define ENT_INT_SRC1_D2H_FIS_CH0_MSK (0x1 << ENT_INT_SRC1_D2H_FIS_CH0_OFF)
+#define ENT_INT_SRC1_D2H_FIS_CH1_OFF 8
+#define ENT_INT_SRC1_D2H_FIS_CH1_MSK (0x1 << ENT_INT_SRC1_D2H_FIS_CH1_OFF)
+#define ENT_INT_SRC2 0x1bc
+#define ENT_INT_SRC3 0x1c0
+#define ENT_INT_SRC3_WP_DEPTH_OFF 8
+#define ENT_INT_SRC3_IPTT_SLOT_NOMATCH_OFF 9
+#define ENT_INT_SRC3_RP_DEPTH_OFF 10
+#define ENT_INT_SRC3_AXI_OFF 11
+#define ENT_INT_SRC3_FIFO_OFF 12
+#define ENT_INT_SRC3_LM_OFF 14
+#define ENT_INT_SRC3_ITC_INT_OFF 15
+#define ENT_INT_SRC3_ITC_INT_MSK (0x1 << ENT_INT_SRC3_ITC_INT_OFF)
+#define ENT_INT_SRC3_ABT_OFF 16
+#define ENT_INT_SRC_MSK1 0x1c4
+#define ENT_INT_SRC_MSK2 0x1c8
+#define ENT_INT_SRC_MSK3 0x1cc
+#define ENT_INT_SRC_MSK3_ENT95_MSK_OFF 31
+#define ENT_INT_SRC_MSK3_ENT95_MSK_MSK (0x1 << ENT_INT_SRC_MSK3_ENT95_MSK_OFF)
+#define SAS_ECC_INTR 0x1e8
+#define SAS_ECC_INTR_DQE_ECC_1B_OFF 0
+#define SAS_ECC_INTR_DQE_ECC_MB_OFF 1
+#define SAS_ECC_INTR_IOST_ECC_1B_OFF 2
+#define SAS_ECC_INTR_IOST_ECC_MB_OFF 3
+#define SAS_ECC_INTR_ITCT_ECC_MB_OFF 4
+#define SAS_ECC_INTR_ITCT_ECC_1B_OFF 5
+#define SAS_ECC_INTR_IOSTLIST_ECC_MB_OFF 6
+#define SAS_ECC_INTR_IOSTLIST_ECC_1B_OFF 7
+#define SAS_ECC_INTR_ITCTLIST_ECC_1B_OFF 8
+#define SAS_ECC_INTR_ITCTLIST_ECC_MB_OFF 9
+#define SAS_ECC_INTR_CQE_ECC_1B_OFF 10
+#define SAS_ECC_INTR_CQE_ECC_MB_OFF 11
+#define SAS_ECC_INTR_NCQ_MEM0_ECC_MB_OFF 12
+#define SAS_ECC_INTR_NCQ_MEM0_ECC_1B_OFF 13
+#define SAS_ECC_INTR_NCQ_MEM1_ECC_MB_OFF 14
+#define SAS_ECC_INTR_NCQ_MEM1_ECC_1B_OFF 15
+#define SAS_ECC_INTR_NCQ_MEM2_ECC_MB_OFF 16
+#define SAS_ECC_INTR_NCQ_MEM2_ECC_1B_OFF 17
+#define SAS_ECC_INTR_NCQ_MEM3_ECC_MB_OFF 18
+#define SAS_ECC_INTR_NCQ_MEM3_ECC_1B_OFF 19
+#define SAS_ECC_INTR_MSK 0x1ec
+#define HGC_ERR_STAT_EN 0x238
+#define CQE_SEND_CNT 0x248
+#define DLVRY_Q_0_BASE_ADDR_LO 0x260
+#define DLVRY_Q_0_BASE_ADDR_HI 0x264
+#define DLVRY_Q_0_DEPTH 0x268
+#define DLVRY_Q_0_WR_PTR 0x26c
+#define DLVRY_Q_0_RD_PTR 0x270
+#define HYPER_STREAM_ID_EN_CFG 0xc80
+#define OQ0_INT_SRC_MSK 0xc90
+#define COMPL_Q_0_BASE_ADDR_LO 0x4e0
+#define COMPL_Q_0_BASE_ADDR_HI 0x4e4
+#define COMPL_Q_0_DEPTH 0x4e8
+#define COMPL_Q_0_WR_PTR 0x4ec
+#define COMPL_Q_0_RD_PTR 0x4f0
+#define HGC_RXM_DFX_STATUS14 0xae8
+#define HGC_RXM_DFX_STATUS14_MEM0_OFF 0
+#define HGC_RXM_DFX_STATUS14_MEM0_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS14_MEM0_OFF)
+#define HGC_RXM_DFX_STATUS14_MEM1_OFF 9
+#define HGC_RXM_DFX_STATUS14_MEM1_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS14_MEM1_OFF)
+#define HGC_RXM_DFX_STATUS14_MEM2_OFF 18
+#define HGC_RXM_DFX_STATUS14_MEM2_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS14_MEM2_OFF)
+#define HGC_RXM_DFX_STATUS15 0xaec
+#define HGC_RXM_DFX_STATUS15_MEM3_OFF 0
+#define HGC_RXM_DFX_STATUS15_MEM3_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS15_MEM3_OFF)
+/* phy registers need init */
+#define PORT_BASE (0x2000)
+
+#define PHY_CFG (PORT_BASE + 0x0)
+#define HARD_PHY_LINKRATE (PORT_BASE + 0x4)
+#define PHY_CFG_ENA_OFF 0
+#define PHY_CFG_ENA_MSK (0x1 << PHY_CFG_ENA_OFF)
+#define PHY_CFG_DC_OPT_OFF 2
+#define PHY_CFG_DC_OPT_MSK (0x1 << PHY_CFG_DC_OPT_OFF)
+#define PROG_PHY_LINK_RATE (PORT_BASE + 0x8)
+#define PROG_PHY_LINK_RATE_MAX_OFF 0
+#define PROG_PHY_LINK_RATE_MAX_MSK (0xff << PROG_PHY_LINK_RATE_MAX_OFF)
+#define PHY_CTRL (PORT_BASE + 0x14)
+#define PHY_CTRL_RESET_OFF 0
+#define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF)
+#define SAS_PHY_CTRL (PORT_BASE + 0x20)
+#define SL_CFG (PORT_BASE + 0x84)
+#define PHY_PCN (PORT_BASE + 0x44)
+#define SL_TOUT_CFG (PORT_BASE + 0x8c)
+#define SL_CONTROL (PORT_BASE + 0x94)
+#define SL_CONTROL_NOTIFY_EN_OFF 0
+#define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF)
+#define SL_CONTROL_CTA_OFF 17
+#define SL_CONTROL_CTA_MSK (0x1 << SL_CONTROL_CTA_OFF)
+#define RX_PRIMS_STATUS (PORT_BASE + 0x98)
+#define RX_BCAST_CHG_OFF 1
+#define RX_BCAST_CHG_MSK (0x1 << RX_BCAST_CHG_OFF)
+#define TX_ID_DWORD0 (PORT_BASE + 0x9c)
+#define TX_ID_DWORD1 (PORT_BASE + 0xa0)
+#define TX_ID_DWORD2 (PORT_BASE + 0xa4)
+#define TX_ID_DWORD3 (PORT_BASE + 0xa8)
+#define TX_ID_DWORD4 (PORT_BASE + 0xaC)
+#define TX_ID_DWORD5 (PORT_BASE + 0xb0)
+#define TX_ID_DWORD6 (PORT_BASE + 0xb4)
+#define TXID_AUTO (PORT_BASE + 0xb8)
+#define TXID_AUTO_CT3_OFF 1
+#define TXID_AUTO_CT3_MSK (0x1 << TXID_AUTO_CT3_OFF)
+#define TXID_AUTO_CTB_OFF 11
+#define TXID_AUTO_CTB_MSK (0x1 << TXID_AUTO_CTB_OFF)
+#define TX_HARDRST_OFF 2
+#define TX_HARDRST_MSK (0x1 << TX_HARDRST_OFF)
+#define RX_IDAF_DWORD0 (PORT_BASE + 0xc4)
+#define RX_IDAF_DWORD1 (PORT_BASE + 0xc8)
+#define RX_IDAF_DWORD2 (PORT_BASE + 0xcc)
+#define RX_IDAF_DWORD3 (PORT_BASE + 0xd0)
+#define RX_IDAF_DWORD4 (PORT_BASE + 0xd4)
+#define RX_IDAF_DWORD5 (PORT_BASE + 0xd8)
+#define RX_IDAF_DWORD6 (PORT_BASE + 0xdc)
+#define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc)
+#define CON_CONTROL (PORT_BASE + 0x118)
+#define CON_CONTROL_CFG_OPEN_ACC_STP_OFF 0
+#define CON_CONTROL_CFG_OPEN_ACC_STP_MSK \
+ (0x01 << CON_CONTROL_CFG_OPEN_ACC_STP_OFF)
+#define DONE_RECEIVED_TIME (PORT_BASE + 0x11c)
+#define CHL_INT0 (PORT_BASE + 0x1b4)
+#define CHL_INT0_HOTPLUG_TOUT_OFF 0
+#define CHL_INT0_HOTPLUG_TOUT_MSK (0x1 << CHL_INT0_HOTPLUG_TOUT_OFF)
+#define CHL_INT0_SL_RX_BCST_ACK_OFF 1
+#define CHL_INT0_SL_RX_BCST_ACK_MSK (0x1 << CHL_INT0_SL_RX_BCST_ACK_OFF)
+#define CHL_INT0_SL_PHY_ENABLE_OFF 2
+#define CHL_INT0_SL_PHY_ENABLE_MSK (0x1 << CHL_INT0_SL_PHY_ENABLE_OFF)
+#define CHL_INT0_NOT_RDY_OFF 4
+#define CHL_INT0_NOT_RDY_MSK (0x1 << CHL_INT0_NOT_RDY_OFF)
+#define CHL_INT0_PHY_RDY_OFF 5
+#define CHL_INT0_PHY_RDY_MSK (0x1 << CHL_INT0_PHY_RDY_OFF)
+#define CHL_INT1 (PORT_BASE + 0x1b8)
+#define CHL_INT1_DMAC_TX_ECC_ERR_OFF 15
+#define CHL_INT1_DMAC_TX_ECC_ERR_MSK (0x1 << CHL_INT1_DMAC_TX_ECC_ERR_OFF)
+#define CHL_INT1_DMAC_RX_ECC_ERR_OFF 17
+#define CHL_INT1_DMAC_RX_ECC_ERR_MSK (0x1 << CHL_INT1_DMAC_RX_ECC_ERR_OFF)
+#define CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF 19
+#define CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF 20
+#define CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF 21
+#define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF 22
+#define CHL_INT2 (PORT_BASE + 0x1bc)
+#define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0
+#define CHL_INT0_MSK (PORT_BASE + 0x1c0)
+#define CHL_INT1_MSK (PORT_BASE + 0x1c4)
+#define CHL_INT2_MSK (PORT_BASE + 0x1c8)
+#define CHL_INT_COAL_EN (PORT_BASE + 0x1d0)
+#define DMA_TX_DFX0 (PORT_BASE + 0x200)
+#define DMA_TX_DFX1 (PORT_BASE + 0x204)
+#define DMA_TX_DFX1_IPTT_OFF 0
+#define DMA_TX_DFX1_IPTT_MSK (0xffff << DMA_TX_DFX1_IPTT_OFF)
+#define DMA_TX_FIFO_DFX0 (PORT_BASE + 0x240)
+#define PORT_DFX0 (PORT_BASE + 0x258)
+#define LINK_DFX2 (PORT_BASE + 0X264)
+#define LINK_DFX2_RCVR_HOLD_STS_OFF 9
+#define LINK_DFX2_RCVR_HOLD_STS_MSK (0x1 << LINK_DFX2_RCVR_HOLD_STS_OFF)
+#define LINK_DFX2_SEND_HOLD_STS_OFF 10
+#define LINK_DFX2_SEND_HOLD_STS_MSK (0x1 << LINK_DFX2_SEND_HOLD_STS_OFF)
+#define SAS_ERR_CNT4_REG (PORT_BASE + 0x290)
+#define SAS_ERR_CNT6_REG (PORT_BASE + 0x298)
+#define PHY_CTRL_RDY_MSK (PORT_BASE + 0x2b0)
+#define PHYCTRL_NOT_RDY_MSK (PORT_BASE + 0x2b4)
+#define PHYCTRL_DWS_RESET_MSK (PORT_BASE + 0x2b8)
+#define PHYCTRL_PHY_ENA_MSK (PORT_BASE + 0x2bc)
+#define SL_RX_BCAST_CHK_MSK (PORT_BASE + 0x2c0)
+#define PHYCTRL_OOB_RESTART_MSK (PORT_BASE + 0x2c4)
+#define DMA_TX_STATUS (PORT_BASE + 0x2d0)
+#define DMA_TX_STATUS_BUSY_OFF 0
+#define DMA_TX_STATUS_BUSY_MSK (0x1 << DMA_TX_STATUS_BUSY_OFF)
+#define DMA_RX_STATUS (PORT_BASE + 0x2e8)
+#define DMA_RX_STATUS_BUSY_OFF 0
+#define DMA_RX_STATUS_BUSY_MSK (0x1 << DMA_RX_STATUS_BUSY_OFF)
+
+#define AXI_CFG (0x5100)
+#define AM_CFG_MAX_TRANS (0x5010)
+#define AM_CFG_SINGLE_PORT_MAX_TRANS (0x5014)
+
+#define AXI_MASTER_CFG_BASE (0x5000)
+#define AM_CTRL_GLOBAL (0x0)
+#define AM_CURR_TRANS_RETURN (0x150)
+
+/* HW dma structures */
+/* Delivery queue header */
+/* dw0 */
+#define CMD_HDR_ABORT_FLAG_OFF 0
+#define CMD_HDR_ABORT_FLAG_MSK (0x3 << CMD_HDR_ABORT_FLAG_OFF)
+#define CMD_HDR_ABORT_DEVICE_TYPE_OFF 2
+#define CMD_HDR_ABORT_DEVICE_TYPE_MSK (0x1 << CMD_HDR_ABORT_DEVICE_TYPE_OFF)
+#define CMD_HDR_RESP_REPORT_OFF 5
+#define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF)
+#define CMD_HDR_TLR_CTRL_OFF 6
+#define CMD_HDR_TLR_CTRL_MSK (0x3 << CMD_HDR_TLR_CTRL_OFF)
+#define CMD_HDR_PHY_ID_OFF 8
+#define CMD_HDR_PHY_ID_MSK (0x1ff << CMD_HDR_PHY_ID_OFF)
+#define CMD_HDR_FORCE_PHY_OFF 17
+#define CMD_HDR_FORCE_PHY_MSK (0x1 << CMD_HDR_FORCE_PHY_OFF)
+#define CMD_HDR_PORT_OFF 18
+#define CMD_HDR_PORT_MSK (0xf << CMD_HDR_PORT_OFF)
+#define CMD_HDR_PRIORITY_OFF 27
+#define CMD_HDR_PRIORITY_MSK (0x1 << CMD_HDR_PRIORITY_OFF)
+#define CMD_HDR_CMD_OFF 29
+#define CMD_HDR_CMD_MSK (0x7 << CMD_HDR_CMD_OFF)
+/* dw1 */
+#define CMD_HDR_DIR_OFF 5
+#define CMD_HDR_DIR_MSK (0x3 << CMD_HDR_DIR_OFF)
+#define CMD_HDR_RESET_OFF 7
+#define CMD_HDR_RESET_MSK (0x1 << CMD_HDR_RESET_OFF)
+#define CMD_HDR_VDTL_OFF 10
+#define CMD_HDR_VDTL_MSK (0x1 << CMD_HDR_VDTL_OFF)
+#define CMD_HDR_FRAME_TYPE_OFF 11
+#define CMD_HDR_FRAME_TYPE_MSK (0x1f << CMD_HDR_FRAME_TYPE_OFF)
+#define CMD_HDR_DEV_ID_OFF 16
+#define CMD_HDR_DEV_ID_MSK (0xffff << CMD_HDR_DEV_ID_OFF)
+/* dw2 */
+#define CMD_HDR_CFL_OFF 0
+#define CMD_HDR_CFL_MSK (0x1ff << CMD_HDR_CFL_OFF)
+#define CMD_HDR_NCQ_TAG_OFF 10
+#define CMD_HDR_NCQ_TAG_MSK (0x1f << CMD_HDR_NCQ_TAG_OFF)
+#define CMD_HDR_MRFL_OFF 15
+#define CMD_HDR_MRFL_MSK (0x1ff << CMD_HDR_MRFL_OFF)
+#define CMD_HDR_SG_MOD_OFF 24
+#define CMD_HDR_SG_MOD_MSK (0x3 << CMD_HDR_SG_MOD_OFF)
+#define CMD_HDR_FIRST_BURST_OFF 26
+#define CMD_HDR_FIRST_BURST_MSK (0x1 << CMD_HDR_SG_MOD_OFF)
+/* dw3 */
+#define CMD_HDR_IPTT_OFF 0
+#define CMD_HDR_IPTT_MSK (0xffff << CMD_HDR_IPTT_OFF)
+/* dw6 */
+#define CMD_HDR_DIF_SGL_LEN_OFF 0
+#define CMD_HDR_DIF_SGL_LEN_MSK (0xffff << CMD_HDR_DIF_SGL_LEN_OFF)
+#define CMD_HDR_DATA_SGL_LEN_OFF 16
+#define CMD_HDR_DATA_SGL_LEN_MSK (0xffff << CMD_HDR_DATA_SGL_LEN_OFF)
+#define CMD_HDR_ABORT_IPTT_OFF 16
+#define CMD_HDR_ABORT_IPTT_MSK (0xffff << CMD_HDR_ABORT_IPTT_OFF)
+
+/* Completion header */
+/* dw0 */
+#define CMPLT_HDR_ERR_PHASE_OFF 2
+#define CMPLT_HDR_ERR_PHASE_MSK (0xff << CMPLT_HDR_ERR_PHASE_OFF)
+#define CMPLT_HDR_RSPNS_XFRD_OFF 10
+#define CMPLT_HDR_RSPNS_XFRD_MSK (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF)
+#define CMPLT_HDR_ERX_OFF 12
+#define CMPLT_HDR_ERX_MSK (0x1 << CMPLT_HDR_ERX_OFF)
+#define CMPLT_HDR_ABORT_STAT_OFF 13
+#define CMPLT_HDR_ABORT_STAT_MSK (0x7 << CMPLT_HDR_ABORT_STAT_OFF)
+/* abort_stat */
+#define STAT_IO_NOT_VALID 0x1
+#define STAT_IO_NO_DEVICE 0x2
+#define STAT_IO_COMPLETE 0x3
+#define STAT_IO_ABORTED 0x4
+/* dw1 */
+#define CMPLT_HDR_IPTT_OFF 0
+#define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF)
+#define CMPLT_HDR_DEV_ID_OFF 16
+#define CMPLT_HDR_DEV_ID_MSK (0xffff << CMPLT_HDR_DEV_ID_OFF)
+
+/* ITCT header */
+/* qw0 */
+#define ITCT_HDR_DEV_TYPE_OFF 0
+#define ITCT_HDR_DEV_TYPE_MSK (0x3 << ITCT_HDR_DEV_TYPE_OFF)
+#define ITCT_HDR_VALID_OFF 2
+#define ITCT_HDR_VALID_MSK (0x1 << ITCT_HDR_VALID_OFF)
+#define ITCT_HDR_MCR_OFF 5
+#define ITCT_HDR_MCR_MSK (0xf << ITCT_HDR_MCR_OFF)
+#define ITCT_HDR_VLN_OFF 9
+#define ITCT_HDR_VLN_MSK (0xf << ITCT_HDR_VLN_OFF)
+#define ITCT_HDR_SMP_TIMEOUT_OFF 16
+#define ITCT_HDR_SMP_TIMEOUT_8US 1
+#define ITCT_HDR_SMP_TIMEOUT (ITCT_HDR_SMP_TIMEOUT_8US * \
+ 250) /* 2ms */
+#define ITCT_HDR_AWT_CONTINUE_OFF 25
+#define ITCT_HDR_PORT_ID_OFF 28
+#define ITCT_HDR_PORT_ID_MSK (0xf << ITCT_HDR_PORT_ID_OFF)
+/* qw2 */
+#define ITCT_HDR_INLT_OFF 0
+#define ITCT_HDR_INLT_MSK (0xffffULL << ITCT_HDR_INLT_OFF)
+#define ITCT_HDR_BITLT_OFF 16
+#define ITCT_HDR_BITLT_MSK (0xffffULL << ITCT_HDR_BITLT_OFF)
+#define ITCT_HDR_MCTLT_OFF 32
+#define ITCT_HDR_MCTLT_MSK (0xffffULL << ITCT_HDR_MCTLT_OFF)
+#define ITCT_HDR_RTOLT_OFF 48
+#define ITCT_HDR_RTOLT_MSK (0xffffULL << ITCT_HDR_RTOLT_OFF)
+
+#define HISI_SAS_FATAL_INT_NR 2
+
+struct hisi_sas_complete_v2_hdr {
+ __le32 dw0;
+ __le32 dw1;
+ __le32 act;
+ __le32 dw3;
+};
+
+struct hisi_sas_err_record_v2 {
+ /* dw0 */
+ __le32 trans_tx_fail_type;
+
+ /* dw1 */
+ __le32 trans_rx_fail_type;
+
+ /* dw2 */
+ __le16 dma_tx_err_type;
+ __le16 sipc_rx_err_type;
+
+ /* dw3 */
+ __le32 dma_rx_err_type;
+};
+
+struct signal_attenuation_s {
+ u32 de_emphasis;
+ u32 preshoot;
+ u32 boost;
+};
+
+struct sig_atten_lu_s {
+ const struct signal_attenuation_s *att;
+ u32 sas_phy_ctrl;
+};
+
+static const struct hisi_sas_hw_error one_bit_ecc_errors[] = {
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_DQE_ECC_1B_OFF),
+ .msk = HGC_DQE_ECC_1B_ADDR_MSK,
+ .shift = HGC_DQE_ECC_1B_ADDR_OFF,
+ .msg = "hgc_dqe_ecc1b_intr",
+ .reg = HGC_DQE_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_IOST_ECC_1B_OFF),
+ .msk = HGC_IOST_ECC_1B_ADDR_MSK,
+ .shift = HGC_IOST_ECC_1B_ADDR_OFF,
+ .msg = "hgc_iost_ecc1b_intr",
+ .reg = HGC_IOST_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_ITCT_ECC_1B_OFF),
+ .msk = HGC_ITCT_ECC_1B_ADDR_MSK,
+ .shift = HGC_ITCT_ECC_1B_ADDR_OFF,
+ .msg = "hgc_itct_ecc1b_intr",
+ .reg = HGC_ITCT_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_IOSTLIST_ECC_1B_OFF),
+ .msk = HGC_LM_DFX_STATUS2_IOSTLIST_MSK,
+ .shift = HGC_LM_DFX_STATUS2_IOSTLIST_OFF,
+ .msg = "hgc_iostl_ecc1b_intr",
+ .reg = HGC_LM_DFX_STATUS2,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_ITCTLIST_ECC_1B_OFF),
+ .msk = HGC_LM_DFX_STATUS2_ITCTLIST_MSK,
+ .shift = HGC_LM_DFX_STATUS2_ITCTLIST_OFF,
+ .msg = "hgc_itctl_ecc1b_intr",
+ .reg = HGC_LM_DFX_STATUS2,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_CQE_ECC_1B_OFF),
+ .msk = HGC_CQE_ECC_1B_ADDR_MSK,
+ .shift = HGC_CQE_ECC_1B_ADDR_OFF,
+ .msg = "hgc_cqe_ecc1b_intr",
+ .reg = HGC_CQE_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM0_ECC_1B_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM0_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM0_OFF,
+ .msg = "rxm_mem0_ecc1b_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM1_ECC_1B_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM1_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM1_OFF,
+ .msg = "rxm_mem1_ecc1b_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM2_ECC_1B_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM2_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM2_OFF,
+ .msg = "rxm_mem2_ecc1b_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM3_ECC_1B_OFF),
+ .msk = HGC_RXM_DFX_STATUS15_MEM3_MSK,
+ .shift = HGC_RXM_DFX_STATUS15_MEM3_OFF,
+ .msg = "rxm_mem3_ecc1b_intr",
+ .reg = HGC_RXM_DFX_STATUS15,
+ },
+};
+
+static const struct hisi_sas_hw_error multi_bit_ecc_errors[] = {
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_DQE_ECC_MB_OFF),
+ .msk = HGC_DQE_ECC_MB_ADDR_MSK,
+ .shift = HGC_DQE_ECC_MB_ADDR_OFF,
+ .msg = "hgc_dqe_eccbad_intr",
+ .reg = HGC_DQE_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_IOST_ECC_MB_OFF),
+ .msk = HGC_IOST_ECC_MB_ADDR_MSK,
+ .shift = HGC_IOST_ECC_MB_ADDR_OFF,
+ .msg = "hgc_iost_eccbad_intr",
+ .reg = HGC_IOST_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_ITCT_ECC_MB_OFF),
+ .msk = HGC_ITCT_ECC_MB_ADDR_MSK,
+ .shift = HGC_ITCT_ECC_MB_ADDR_OFF,
+ .msg = "hgc_itct_eccbad_intr",
+ .reg = HGC_ITCT_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_IOSTLIST_ECC_MB_OFF),
+ .msk = HGC_LM_DFX_STATUS2_IOSTLIST_MSK,
+ .shift = HGC_LM_DFX_STATUS2_IOSTLIST_OFF,
+ .msg = "hgc_iostl_eccbad_intr",
+ .reg = HGC_LM_DFX_STATUS2,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_ITCTLIST_ECC_MB_OFF),
+ .msk = HGC_LM_DFX_STATUS2_ITCTLIST_MSK,
+ .shift = HGC_LM_DFX_STATUS2_ITCTLIST_OFF,
+ .msg = "hgc_itctl_eccbad_intr",
+ .reg = HGC_LM_DFX_STATUS2,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_CQE_ECC_MB_OFF),
+ .msk = HGC_CQE_ECC_MB_ADDR_MSK,
+ .shift = HGC_CQE_ECC_MB_ADDR_OFF,
+ .msg = "hgc_cqe_eccbad_intr",
+ .reg = HGC_CQE_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM0_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM0_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM0_OFF,
+ .msg = "rxm_mem0_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM1_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM1_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM1_OFF,
+ .msg = "rxm_mem1_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM2_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM2_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM2_OFF,
+ .msg = "rxm_mem2_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM3_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS15_MEM3_MSK,
+ .shift = HGC_RXM_DFX_STATUS15_MEM3_OFF,
+ .msg = "rxm_mem3_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS15,
+ },
+};
+
+enum {
+ HISI_SAS_PHY_PHY_UPDOWN,
+ HISI_SAS_PHY_CHNL_INT,
+ HISI_SAS_PHY_INT_NR
+};
+
+enum {
+ TRANS_TX_FAIL_BASE = 0x0, /* dw0 */
+ TRANS_RX_FAIL_BASE = 0x20, /* dw1 */
+ DMA_TX_ERR_BASE = 0x40, /* dw2 bit 15-0 */
+ SIPC_RX_ERR_BASE = 0x50, /* dw2 bit 31-16*/
+ DMA_RX_ERR_BASE = 0x60, /* dw3 */
+
+ /* trans tx*/
+ TRANS_TX_OPEN_FAIL_WITH_IT_NEXUS_LOSS = TRANS_TX_FAIL_BASE, /* 0x0 */
+ TRANS_TX_ERR_PHY_NOT_ENABLE, /* 0x1 */
+ TRANS_TX_OPEN_CNX_ERR_WRONG_DESTINATION, /* 0x2 */
+ TRANS_TX_OPEN_CNX_ERR_ZONE_VIOLATION, /* 0x3 */
+ TRANS_TX_OPEN_CNX_ERR_BY_OTHER, /* 0x4 */
+ RESERVED0, /* 0x5 */
+ TRANS_TX_OPEN_CNX_ERR_AIP_TIMEOUT, /* 0x6 */
+ TRANS_TX_OPEN_CNX_ERR_STP_RESOURCES_BUSY, /* 0x7 */
+ TRANS_TX_OPEN_CNX_ERR_PROTOCOL_NOT_SUPPORTED, /* 0x8 */
+ TRANS_TX_OPEN_CNX_ERR_CONNECTION_RATE_NOT_SUPPORTED, /* 0x9 */
+ TRANS_TX_OPEN_CNX_ERR_BAD_DESTINATION, /* 0xa */
+ TRANS_TX_OPEN_CNX_ERR_BREAK_RCVD, /* 0xb */
+ TRANS_TX_OPEN_CNX_ERR_LOW_PHY_POWER, /* 0xc */
+ TRANS_TX_OPEN_CNX_ERR_PATHWAY_BLOCKED, /* 0xd */
+ TRANS_TX_OPEN_CNX_ERR_OPEN_TIMEOUT, /* 0xe */
+ TRANS_TX_OPEN_CNX_ERR_NO_DESTINATION, /* 0xf */
+ TRANS_TX_OPEN_RETRY_ERR_THRESHOLD_REACHED, /* 0x10 */
+ TRANS_TX_ERR_FRAME_TXED, /* 0x11 */
+ TRANS_TX_ERR_WITH_BREAK_TIMEOUT, /* 0x12 */
+ TRANS_TX_ERR_WITH_BREAK_REQUEST, /* 0x13 */
+ TRANS_TX_ERR_WITH_BREAK_RECEVIED, /* 0x14 */
+ TRANS_TX_ERR_WITH_CLOSE_TIMEOUT, /* 0x15 */
+ TRANS_TX_ERR_WITH_CLOSE_NORMAL, /* 0x16 for ssp*/
+ TRANS_TX_ERR_WITH_CLOSE_PHYDISALE, /* 0x17 */
+ TRANS_TX_ERR_WITH_CLOSE_DWS_TIMEOUT, /* 0x18 */
+ TRANS_TX_ERR_WITH_CLOSE_COMINIT, /* 0x19 */
+ TRANS_TX_ERR_WITH_NAK_RECEVIED, /* 0x1a for ssp*/
+ TRANS_TX_ERR_WITH_ACK_NAK_TIMEOUT, /* 0x1b for ssp*/
+ /*IO_TX_ERR_WITH_R_ERR_RECEVIED, [> 0x1b for sata/stp<] */
+ TRANS_TX_ERR_WITH_CREDIT_TIMEOUT, /* 0x1c for ssp */
+ /*IO_RX_ERR_WITH_SATA_DEVICE_LOST 0x1c for sata/stp */
+ TRANS_TX_ERR_WITH_IPTT_CONFLICT, /* 0x1d for ssp/smp */
+ TRANS_TX_ERR_WITH_OPEN_BY_DES_OR_OTHERS, /* 0x1e */
+ /*IO_TX_ERR_WITH_SYNC_RXD, [> 0x1e <] for sata/stp */
+ TRANS_TX_ERR_WITH_WAIT_RECV_TIMEOUT, /* 0x1f for sata/stp */
+
+ /* trans rx */
+ TRANS_RX_ERR_WITH_RXFRAME_CRC_ERR = TRANS_RX_FAIL_BASE, /* 0x20 */
+ TRANS_RX_ERR_WITH_RXFIS_8B10B_DISP_ERR, /* 0x21 for sata/stp */
+ TRANS_RX_ERR_WITH_RXFRAME_HAVE_ERRPRM, /* 0x22 for ssp/smp */
+ /*IO_ERR_WITH_RXFIS_8B10B_CODE_ERR, [> 0x22 <] for sata/stp */
+ TRANS_RX_ERR_WITH_RXFIS_DECODE_ERROR, /* 0x23 for sata/stp */
+ TRANS_RX_ERR_WITH_RXFIS_CRC_ERR, /* 0x24 for sata/stp */
+ TRANS_RX_ERR_WITH_RXFRAME_LENGTH_OVERRUN, /* 0x25 for smp */
+ /*IO_ERR_WITH_RXFIS_TX SYNCP, [> 0x25 <] for sata/stp */
+ TRANS_RX_ERR_WITH_RXFIS_RX_SYNCP, /* 0x26 for sata/stp*/
+ TRANS_RX_ERR_WITH_LINK_BUF_OVERRUN, /* 0x27 */
+ TRANS_RX_ERR_WITH_BREAK_TIMEOUT, /* 0x28 */
+ TRANS_RX_ERR_WITH_BREAK_REQUEST, /* 0x29 */
+ TRANS_RX_ERR_WITH_BREAK_RECEVIED, /* 0x2a */
+ RESERVED1, /* 0x2b */
+ TRANS_RX_ERR_WITH_CLOSE_NORMAL, /* 0x2c */
+ TRANS_RX_ERR_WITH_CLOSE_PHY_DISABLE, /* 0x2d */
+ TRANS_RX_ERR_WITH_CLOSE_DWS_TIMEOUT, /* 0x2e */
+ TRANS_RX_ERR_WITH_CLOSE_COMINIT, /* 0x2f */
+ TRANS_RX_ERR_WITH_DATA_LEN0, /* 0x30 for ssp/smp */
+ TRANS_RX_ERR_WITH_BAD_HASH, /* 0x31 for ssp */
+ /*IO_RX_ERR_WITH_FIS_TOO_SHORT, [> 0x31 <] for sata/stp */
+ TRANS_RX_XRDY_WLEN_ZERO_ERR, /* 0x32 for ssp*/
+ /*IO_RX_ERR_WITH_FIS_TOO_LONG, [> 0x32 <] for sata/stp */
+ TRANS_RX_SSP_FRM_LEN_ERR, /* 0x33 for ssp */
+ /*IO_RX_ERR_WITH_SATA_DEVICE_LOST, [> 0x33 <] for sata */
+ RESERVED2, /* 0x34 */
+ RESERVED3, /* 0x35 */
+ RESERVED4, /* 0x36 */
+ RESERVED5, /* 0x37 */
+ TRANS_RX_ERR_WITH_BAD_FRM_TYPE, /* 0x38 */
+ TRANS_RX_SMP_FRM_LEN_ERR, /* 0x39 */
+ TRANS_RX_SMP_RESP_TIMEOUT_ERR, /* 0x3a */
+ RESERVED6, /* 0x3b */
+ RESERVED7, /* 0x3c */
+ RESERVED8, /* 0x3d */
+ RESERVED9, /* 0x3e */
+ TRANS_RX_R_ERR, /* 0x3f */
+
+ /* dma tx */
+ DMA_TX_DIF_CRC_ERR = DMA_TX_ERR_BASE, /* 0x40 */
+ DMA_TX_DIF_APP_ERR, /* 0x41 */
+ DMA_TX_DIF_RPP_ERR, /* 0x42 */
+ DMA_TX_DATA_SGL_OVERFLOW, /* 0x43 */
+ DMA_TX_DIF_SGL_OVERFLOW, /* 0x44 */
+ DMA_TX_UNEXP_XFER_ERR, /* 0x45 */
+ DMA_TX_UNEXP_RETRANS_ERR, /* 0x46 */
+ DMA_TX_XFER_LEN_OVERFLOW, /* 0x47 */
+ DMA_TX_XFER_OFFSET_ERR, /* 0x48 */
+ DMA_TX_RAM_ECC_ERR, /* 0x49 */
+ DMA_TX_DIF_LEN_ALIGN_ERR, /* 0x4a */
+ DMA_TX_MAX_ERR_CODE,
+
+ /* sipc rx */
+ SIPC_RX_FIS_STATUS_ERR_BIT_VLD = SIPC_RX_ERR_BASE, /* 0x50 */
+ SIPC_RX_PIO_WRSETUP_STATUS_DRQ_ERR, /* 0x51 */
+ SIPC_RX_FIS_STATUS_BSY_BIT_ERR, /* 0x52 */
+ SIPC_RX_WRSETUP_LEN_ODD_ERR, /* 0x53 */
+ SIPC_RX_WRSETUP_LEN_ZERO_ERR, /* 0x54 */
+ SIPC_RX_WRDATA_LEN_NOT_MATCH_ERR, /* 0x55 */
+ SIPC_RX_NCQ_WRSETUP_OFFSET_ERR, /* 0x56 */
+ SIPC_RX_NCQ_WRSETUP_AUTO_ACTIVE_ERR, /* 0x57 */
+ SIPC_RX_SATA_UNEXP_FIS_ERR, /* 0x58 */
+ SIPC_RX_WRSETUP_ESTATUS_ERR, /* 0x59 */
+ SIPC_RX_DATA_UNDERFLOW_ERR, /* 0x5a */
+ SIPC_RX_MAX_ERR_CODE,
+
+ /* dma rx */
+ DMA_RX_DIF_CRC_ERR = DMA_RX_ERR_BASE, /* 0x60 */
+ DMA_RX_DIF_APP_ERR, /* 0x61 */
+ DMA_RX_DIF_RPP_ERR, /* 0x62 */
+ DMA_RX_DATA_SGL_OVERFLOW, /* 0x63 */
+ DMA_RX_DIF_SGL_OVERFLOW, /* 0x64 */
+ DMA_RX_DATA_LEN_OVERFLOW, /* 0x65 */
+ DMA_RX_DATA_LEN_UNDERFLOW, /* 0x66 */
+ DMA_RX_DATA_OFFSET_ERR, /* 0x67 */
+ RESERVED10, /* 0x68 */
+ DMA_RX_SATA_FRAME_TYPE_ERR, /* 0x69 */
+ DMA_RX_RESP_BUF_OVERFLOW, /* 0x6a */
+ DMA_RX_UNEXP_RETRANS_RESP_ERR, /* 0x6b */
+ DMA_RX_UNEXP_NORM_RESP_ERR, /* 0x6c */
+ DMA_RX_UNEXP_RDFRAME_ERR, /* 0x6d */
+ DMA_RX_PIO_DATA_LEN_ERR, /* 0x6e */
+ DMA_RX_RDSETUP_STATUS_ERR, /* 0x6f */
+ DMA_RX_RDSETUP_STATUS_DRQ_ERR, /* 0x70 */
+ DMA_RX_RDSETUP_STATUS_BSY_ERR, /* 0x71 */
+ DMA_RX_RDSETUP_LEN_ODD_ERR, /* 0x72 */
+ DMA_RX_RDSETUP_LEN_ZERO_ERR, /* 0x73 */
+ DMA_RX_RDSETUP_LEN_OVER_ERR, /* 0x74 */
+ DMA_RX_RDSETUP_OFFSET_ERR, /* 0x75 */
+ DMA_RX_RDSETUP_ACTIVE_ERR, /* 0x76 */
+ DMA_RX_RDSETUP_ESTATUS_ERR, /* 0x77 */
+ DMA_RX_RAM_ECC_ERR, /* 0x78 */
+ DMA_RX_UNKNOWN_FRM_ERR, /* 0x79 */
+ DMA_RX_MAX_ERR_CODE,
+};
+
+#define HISI_SAS_COMMAND_ENTRIES_V2_HW 4096
+#define HISI_MAX_SATA_SUPPORT_V2_HW (HISI_SAS_COMMAND_ENTRIES_V2_HW/64 - 1)
+
+#define DIR_NO_DATA 0
+#define DIR_TO_INI 1
+#define DIR_TO_DEVICE 2
+#define DIR_RESERVED 3
+
+#define ERR_ON_TX_PHASE(err_phase) (err_phase == 0x2 || \
+ err_phase == 0x4 || err_phase == 0x8 ||\
+ err_phase == 0x6 || err_phase == 0xa)
+#define ERR_ON_RX_PHASE(err_phase) (err_phase == 0x10 || \
+ err_phase == 0x20 || err_phase == 0x40)
+
+static void link_timeout_disable_link(struct timer_list *t);
+
+static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ return readl(regs);
+}
+
+static u32 hisi_sas_read32_relaxed(struct hisi_hba *hisi_hba, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ return readl_relaxed(regs);
+}
+
+static void hisi_sas_write32(struct hisi_hba *hisi_hba, u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ writel(val, regs);
+}
+
+static void hisi_sas_phy_write32(struct hisi_hba *hisi_hba, int phy_no,
+ u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ writel(val, regs);
+}
+
+static u32 hisi_sas_phy_read32(struct hisi_hba *hisi_hba,
+ int phy_no, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ return readl(regs);
+}
+
+/* This function needs to be protected from pre-emption. */
+static int
+slot_index_alloc_quirk_v2_hw(struct hisi_hba *hisi_hba,
+ struct domain_device *device)
+{
+ int sata_dev = dev_is_sata(device);
+ void *bitmap = hisi_hba->slot_index_tags;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ int sata_idx = sas_dev->sata_idx;
+ int start, end;
+
+ if (!sata_dev) {
+ /*
+ * STP link SoC bug workaround: index starts from 1.
+ * additionally, we can only allocate odd IPTT(1~4095)
+ * for SAS/SMP device.
+ */
+ start = 1;
+ end = hisi_hba->slot_index_count;
+ } else {
+ if (sata_idx >= HISI_MAX_SATA_SUPPORT_V2_HW)
+ return -EINVAL;
+
+ /*
+ * For SATA device: allocate even IPTT in this interval
+ * [64*(sata_idx+1), 64*(sata_idx+2)], then each SATA device
+ * own 32 IPTTs. IPTT 0 shall not be used duing to STP link
+ * SoC bug workaround. So we ignore the first 32 even IPTTs.
+ */
+ start = 64 * (sata_idx + 1);
+ end = 64 * (sata_idx + 2);
+ }
+
+ spin_lock(&hisi_hba->lock);
+ while (1) {
+ start = find_next_zero_bit(bitmap,
+ hisi_hba->slot_index_count, start);
+ if (start >= end) {
+ spin_unlock(&hisi_hba->lock);
+ return -SAS_QUEUE_FULL;
+ }
+ /*
+ * SAS IPTT bit0 should be 1, and SATA IPTT bit0 should be 0.
+ */
+ if (sata_dev ^ (start & 1))
+ break;
+ start++;
+ }
+
+ set_bit(start, bitmap);
+ spin_unlock(&hisi_hba->lock);
+ return start;
+}
+
+static bool sata_index_alloc_v2_hw(struct hisi_hba *hisi_hba, int *idx)
+{
+ unsigned int index;
+ struct device *dev = hisi_hba->dev;
+ void *bitmap = hisi_hba->sata_dev_bitmap;
+
+ index = find_first_zero_bit(bitmap, HISI_MAX_SATA_SUPPORT_V2_HW);
+ if (index >= HISI_MAX_SATA_SUPPORT_V2_HW) {
+ dev_warn(dev, "alloc sata index failed, index=%d\n", index);
+ return false;
+ }
+
+ set_bit(index, bitmap);
+ *idx = index;
+ return true;
+}
+
+
+static struct
+hisi_sas_device *alloc_dev_quirk_v2_hw(struct domain_device *device)
+{
+ struct hisi_hba *hisi_hba = device->port->ha->lldd_ha;
+ struct hisi_sas_device *sas_dev = NULL;
+ int i, sata_dev = dev_is_sata(device);
+ int sata_idx = -1;
+
+ spin_lock(&hisi_hba->lock);
+
+ if (sata_dev)
+ if (!sata_index_alloc_v2_hw(hisi_hba, &sata_idx))
+ goto out;
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ /*
+ * SATA device id bit0 should be 0
+ */
+ if (sata_dev && (i & 1))
+ continue;
+ if (hisi_hba->devices[i].dev_type == SAS_PHY_UNUSED) {
+ int queue = i % hisi_hba->queue_count;
+ struct hisi_sas_dq *dq = &hisi_hba->dq[queue];
+
+ hisi_hba->devices[i].device_id = i;
+ sas_dev = &hisi_hba->devices[i];
+ sas_dev->dev_status = HISI_SAS_DEV_INIT;
+ sas_dev->dev_type = device->dev_type;
+ sas_dev->hisi_hba = hisi_hba;
+ sas_dev->sas_device = device;
+ sas_dev->sata_idx = sata_idx;
+ sas_dev->dq = dq;
+ spin_lock_init(&sas_dev->lock);
+ INIT_LIST_HEAD(&hisi_hba->devices[i].list);
+ break;
+ }
+ }
+
+out:
+ spin_unlock(&hisi_hba->lock);
+
+ return sas_dev;
+}
+
+static void config_phy_opt_mode_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg &= ~PHY_CFG_DC_OPT_MSK;
+ cfg |= 1 << PHY_CFG_DC_OPT_OFF;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void config_id_frame_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct sas_identify_frame identify_frame;
+ u32 *identify_buffer;
+
+ memset(&identify_frame, 0, sizeof(identify_frame));
+ identify_frame.dev_type = SAS_END_DEVICE;
+ identify_frame.frame_type = 0;
+ identify_frame._un1 = 1;
+ identify_frame.initiator_bits = SAS_PROTOCOL_ALL;
+ identify_frame.target_bits = SAS_PROTOCOL_NONE;
+ memcpy(&identify_frame._un4_11[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ memcpy(&identify_frame.sas_addr[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ identify_frame.phy_id = phy_no;
+ identify_buffer = (u32 *)(&identify_frame);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0,
+ __swab32(identify_buffer[0]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1,
+ __swab32(identify_buffer[1]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2,
+ __swab32(identify_buffer[2]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3,
+ __swab32(identify_buffer[3]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4,
+ __swab32(identify_buffer[4]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5,
+ __swab32(identify_buffer[5]));
+}
+
+static void setup_itct_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ struct domain_device *device = sas_dev->sas_device;
+ struct device *dev = hisi_hba->dev;
+ u64 qw0, device_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[device_id];
+ struct asd_sas_port *sas_port = device->port;
+ struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+ u64 sas_addr;
+
+ memset(itct, 0, sizeof(*itct));
+
+ /* qw0 */
+ qw0 = 0;
+ switch (sas_dev->dev_type) {
+ case SAS_END_DEVICE:
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF;
+ break;
+ case SAS_SATA_DEV:
+ case SAS_SATA_PENDING:
+ if (dev_parent_is_expander(device))
+ qw0 = HISI_SAS_DEV_TYPE_STP << ITCT_HDR_DEV_TYPE_OFF;
+ else
+ qw0 = HISI_SAS_DEV_TYPE_SATA << ITCT_HDR_DEV_TYPE_OFF;
+ break;
+ default:
+ dev_warn(dev, "setup itct: unsupported dev type (%d)\n",
+ sas_dev->dev_type);
+ }
+
+ qw0 |= ((1 << ITCT_HDR_VALID_OFF) |
+ (device->linkrate << ITCT_HDR_MCR_OFF) |
+ (1 << ITCT_HDR_VLN_OFF) |
+ (ITCT_HDR_SMP_TIMEOUT << ITCT_HDR_SMP_TIMEOUT_OFF) |
+ (1 << ITCT_HDR_AWT_CONTINUE_OFF) |
+ (port->id << ITCT_HDR_PORT_ID_OFF));
+ itct->qw0 = cpu_to_le64(qw0);
+
+ /* qw1 */
+ memcpy(&sas_addr, device->sas_addr, SAS_ADDR_SIZE);
+ itct->sas_addr = cpu_to_le64(__swab64(sas_addr));
+
+ /* qw2 */
+ if (!dev_is_sata(device))
+ itct->qw2 = cpu_to_le64((5000ULL << ITCT_HDR_INLT_OFF) |
+ (0x1ULL << ITCT_HDR_BITLT_OFF) |
+ (0x32ULL << ITCT_HDR_MCTLT_OFF) |
+ (0x1ULL << ITCT_HDR_RTOLT_OFF));
+}
+
+static int clear_itct_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u64 dev_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[dev_id];
+ u32 reg_val = hisi_sas_read32(hisi_hba, ENT_INT_SRC3);
+ struct device *dev = hisi_hba->dev;
+ int i;
+
+ sas_dev->completion = &completion;
+
+ /* clear the itct interrupt state */
+ if (ENT_INT_SRC3_ITC_INT_MSK & reg_val)
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC3,
+ ENT_INT_SRC3_ITC_INT_MSK);
+
+ /* need to set register twice to clear ITCT for v2 hw */
+ for (i = 0; i < 2; i++) {
+ reg_val = ITCT_CLR_EN_MSK | (dev_id & ITCT_DEV_MSK);
+ hisi_sas_write32(hisi_hba, ITCT_CLR, reg_val);
+ if (!wait_for_completion_timeout(sas_dev->completion,
+ HISI_SAS_CLEAR_ITCT_TIMEOUT)) {
+ dev_warn(dev, "failed to clear ITCT\n");
+ return -ETIMEDOUT;
+ }
+
+ memset(itct, 0, sizeof(struct hisi_sas_itct));
+ }
+ return 0;
+}
+
+static void free_device_v2_hw(struct hisi_sas_device *sas_dev)
+{
+ struct hisi_hba *hisi_hba = sas_dev->hisi_hba;
+
+ /* SoC bug workaround */
+ if (dev_is_sata(sas_dev->sas_device))
+ clear_bit(sas_dev->sata_idx, hisi_hba->sata_dev_bitmap);
+}
+
+static int reset_hw_v2_hw(struct hisi_hba *hisi_hba)
+{
+ int i, reset_val;
+ u32 val;
+ unsigned long end_time;
+ struct device *dev = hisi_hba->dev;
+
+ /* The mask needs to be set depending on the number of phys */
+ if (hisi_hba->n_phy == 9)
+ reset_val = 0x1fffff;
+ else
+ reset_val = 0x7ffff;
+
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0);
+
+ /* Disable all of the PHYs */
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ u32 phy_cfg = hisi_sas_phy_read32(hisi_hba, i, PHY_CFG);
+
+ phy_cfg &= ~PHY_CTRL_RESET_MSK;
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CFG, phy_cfg);
+ }
+ udelay(50);
+
+ /* Ensure DMA tx & rx idle */
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ u32 dma_tx_status, dma_rx_status;
+
+ end_time = jiffies + msecs_to_jiffies(1000);
+
+ while (1) {
+ dma_tx_status = hisi_sas_phy_read32(hisi_hba, i,
+ DMA_TX_STATUS);
+ dma_rx_status = hisi_sas_phy_read32(hisi_hba, i,
+ DMA_RX_STATUS);
+
+ if (!(dma_tx_status & DMA_TX_STATUS_BUSY_MSK) &&
+ !(dma_rx_status & DMA_RX_STATUS_BUSY_MSK))
+ break;
+
+ msleep(20);
+ if (time_after(jiffies, end_time))
+ return -EIO;
+ }
+ }
+
+ /* Ensure axi bus idle */
+ end_time = jiffies + msecs_to_jiffies(1000);
+ while (1) {
+ u32 axi_status =
+ hisi_sas_read32(hisi_hba, AXI_CFG);
+
+ if (axi_status == 0)
+ break;
+
+ msleep(20);
+ if (time_after(jiffies, end_time))
+ return -EIO;
+ }
+
+ if (ACPI_HANDLE(dev)) {
+ acpi_status s;
+
+ s = acpi_evaluate_object(ACPI_HANDLE(dev), "_RST", NULL, NULL);
+ if (ACPI_FAILURE(s)) {
+ dev_err(dev, "Reset failed\n");
+ return -EIO;
+ }
+ } else if (hisi_hba->ctrl) {
+ /* reset and disable clock*/
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_reset_reg,
+ reset_val);
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_clock_ena_reg + 4,
+ reset_val);
+ msleep(1);
+ regmap_read(hisi_hba->ctrl, hisi_hba->ctrl_reset_sts_reg, &val);
+ if (reset_val != (val & reset_val)) {
+ dev_err(dev, "SAS reset fail.\n");
+ return -EIO;
+ }
+
+ /* De-reset and enable clock*/
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_reset_reg + 4,
+ reset_val);
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_clock_ena_reg,
+ reset_val);
+ msleep(1);
+ regmap_read(hisi_hba->ctrl, hisi_hba->ctrl_reset_sts_reg,
+ &val);
+ if (val & reset_val) {
+ dev_err(dev, "SAS de-reset fail.\n");
+ return -EIO;
+ }
+ } else {
+ dev_err(dev, "no reset method\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* This function needs to be called after resetting SAS controller. */
+static void phys_reject_stp_links_v2_hw(struct hisi_hba *hisi_hba)
+{
+ u32 cfg;
+ int phy_no;
+
+ hisi_hba->reject_stp_links_msk = (1 << hisi_hba->n_phy) - 1;
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ cfg = hisi_sas_phy_read32(hisi_hba, phy_no, CON_CONTROL);
+ if (!(cfg & CON_CONTROL_CFG_OPEN_ACC_STP_MSK))
+ continue;
+
+ cfg &= ~CON_CONTROL_CFG_OPEN_ACC_STP_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, CON_CONTROL, cfg);
+ }
+}
+
+static void phys_try_accept_stp_links_v2_hw(struct hisi_hba *hisi_hba)
+{
+ int phy_no;
+ u32 dma_tx_dfx1;
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ if (!(hisi_hba->reject_stp_links_msk & BIT(phy_no)))
+ continue;
+
+ dma_tx_dfx1 = hisi_sas_phy_read32(hisi_hba, phy_no,
+ DMA_TX_DFX1);
+ if (dma_tx_dfx1 & DMA_TX_DFX1_IPTT_MSK) {
+ u32 cfg = hisi_sas_phy_read32(hisi_hba,
+ phy_no, CON_CONTROL);
+
+ cfg |= CON_CONTROL_CFG_OPEN_ACC_STP_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ CON_CONTROL, cfg);
+ clear_bit(phy_no, &hisi_hba->reject_stp_links_msk);
+ }
+ }
+}
+
+static const struct signal_attenuation_s x6000 = {9200, 0, 10476};
+static const struct sig_atten_lu_s sig_atten_lu[] = {
+ { &x6000, 0x3016a68 },
+};
+
+static void init_reg_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ u32 sas_phy_ctrl = 0x30b9908;
+ u32 signal[3];
+ int i;
+
+ /* Global registers init */
+
+ /* Deal with am-max-transmissions quirk */
+ if (device_property_present(dev, "hip06-sas-v2-quirk-amt")) {
+ hisi_sas_write32(hisi_hba, AM_CFG_MAX_TRANS, 0x2020);
+ hisi_sas_write32(hisi_hba, AM_CFG_SINGLE_PORT_MAX_TRANS,
+ 0x2020);
+ } /* Else, use defaults -> do nothing */
+
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+ (u32)((1ULL << hisi_hba->queue_count) - 1));
+ hisi_sas_write32(hisi_hba, AXI_USER1, 0xc0000000);
+ hisi_sas_write32(hisi_hba, AXI_USER2, 0x10000);
+ hisi_sas_write32(hisi_hba, HGC_SAS_TXFAIL_RETRY_CTRL, 0x0);
+ hisi_sas_write32(hisi_hba, HGC_SAS_TX_OPEN_FAIL_RETRY_CTRL, 0x7FF);
+ hisi_sas_write32(hisi_hba, OPENA_WT_CONTI_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, I_T_NEXUS_LOSS_TIME, 0x1F4);
+ hisi_sas_write32(hisi_hba, MAX_CON_TIME_LIMIT_TIME, 0x32);
+ hisi_sas_write32(hisi_hba, BUS_INACTIVE_LIMIT_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, HGC_ERR_STAT_EN, 0x1);
+ hisi_sas_write32(hisi_hba, HGC_GET_ITV_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, INT_COAL_EN, 0xc);
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0x60);
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0x3);
+ hisi_sas_write32(hisi_hba, ENT_INT_COAL_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, ENT_INT_COAL_CNT, 0x1);
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 0x0);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC1, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC2, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC3, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0x7efefefe);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0x7efefefe);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0x7ffe20fe);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0xfff00c30);
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ hisi_sas_write32(hisi_hba, OQ0_INT_SRC_MSK + 0x4 * i, 0);
+
+ hisi_sas_write32(hisi_hba, AXI_AHB_CLK_CFG, 1);
+ hisi_sas_write32(hisi_hba, HYPER_STREAM_ID_EN_CFG, 1);
+
+ /* Get sas_phy_ctrl value to deal with TX FFE issue. */
+ if (!device_property_read_u32_array(dev, "hisilicon,signal-attenuation",
+ signal, ARRAY_SIZE(signal))) {
+ for (i = 0; i < ARRAY_SIZE(sig_atten_lu); i++) {
+ const struct sig_atten_lu_s *lookup = &sig_atten_lu[i];
+ const struct signal_attenuation_s *att = lookup->att;
+
+ if ((signal[0] == att->de_emphasis) &&
+ (signal[1] == att->preshoot) &&
+ (signal[2] == att->boost)) {
+ sas_phy_ctrl = lookup->sas_phy_ctrl;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(sig_atten_lu))
+ dev_warn(dev, "unknown signal attenuation values, using default PHY ctrl config\n");
+ }
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ u32 prog_phy_link_rate = 0x800;
+
+ if (!sas_phy->phy || (sas_phy->phy->maximum_linkrate <
+ SAS_LINK_RATE_1_5_GBPS)) {
+ prog_phy_link_rate = 0x855;
+ } else {
+ enum sas_linkrate max = sas_phy->phy->maximum_linkrate;
+
+ prog_phy_link_rate =
+ hisi_sas_get_prog_phy_linkrate_mask(max) |
+ 0x800;
+ }
+ hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE,
+ prog_phy_link_rate);
+ hisi_sas_phy_write32(hisi_hba, i, SAS_PHY_CTRL, sas_phy_ctrl);
+ hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d);
+ hisi_sas_phy_write32(hisi_hba, i, SL_CONTROL, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, TXID_AUTO, 0x2);
+ hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0x8);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xfff87fff);
+ hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xff857fff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8ffffbfe);
+ hisi_sas_phy_write32(hisi_hba, i, SL_CFG, 0x13f801fc);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_DWS_RESET_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_PHY_ENA_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, SL_RX_BCAST_CHK_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT_COAL_EN, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_OOB_RESTART_MSK, 0x0);
+ if (hisi_hba->refclk_frequency_mhz == 66)
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL, 0x199B694);
+ /* else, do nothing -> leave it how you found it */
+ }
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ /* Delivery queue */
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+
+ /* Completion queue */
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+ }
+
+ /* itct */
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->itct_dma));
+
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->itct_dma));
+
+ /* iost */
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->iost_dma));
+
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->iost_dma));
+
+ /* breakpoint */
+ hisi_sas_write32(hisi_hba, IO_BROKEN_MSG_ADDR_LO,
+ lower_32_bits(hisi_hba->breakpoint_dma));
+
+ hisi_sas_write32(hisi_hba, IO_BROKEN_MSG_ADDR_HI,
+ upper_32_bits(hisi_hba->breakpoint_dma));
+
+ /* SATA broken msg */
+ hisi_sas_write32(hisi_hba, IO_SATA_BROKEN_MSG_ADDR_LO,
+ lower_32_bits(hisi_hba->sata_breakpoint_dma));
+
+ hisi_sas_write32(hisi_hba, IO_SATA_BROKEN_MSG_ADDR_HI,
+ upper_32_bits(hisi_hba->sata_breakpoint_dma));
+
+ /* SATA initial fis */
+ hisi_sas_write32(hisi_hba, SATA_INITI_D2H_STORE_ADDR_LO,
+ lower_32_bits(hisi_hba->initial_fis_dma));
+
+ hisi_sas_write32(hisi_hba, SATA_INITI_D2H_STORE_ADDR_HI,
+ upper_32_bits(hisi_hba->initial_fis_dma));
+}
+
+static void link_timeout_enable_link(struct timer_list *t)
+{
+ struct hisi_hba *hisi_hba = timer_container_of(hisi_hba, t, timer);
+ int i, reg_val;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ if (hisi_hba->reject_stp_links_msk & BIT(i))
+ continue;
+
+ reg_val = hisi_sas_phy_read32(hisi_hba, i, CON_CONTROL);
+ if (!(reg_val & BIT(0))) {
+ hisi_sas_phy_write32(hisi_hba, i,
+ CON_CONTROL, 0x7);
+ break;
+ }
+ }
+
+ hisi_hba->timer.function = link_timeout_disable_link;
+ mod_timer(&hisi_hba->timer, jiffies + msecs_to_jiffies(900));
+}
+
+static void link_timeout_disable_link(struct timer_list *t)
+{
+ struct hisi_hba *hisi_hba = timer_container_of(hisi_hba, t, timer);
+ int i, reg_val;
+
+ reg_val = hisi_sas_read32(hisi_hba, PHY_STATE);
+ for (i = 0; i < hisi_hba->n_phy && reg_val; i++) {
+ if (hisi_hba->reject_stp_links_msk & BIT(i))
+ continue;
+
+ if (reg_val & BIT(i)) {
+ hisi_sas_phy_write32(hisi_hba, i,
+ CON_CONTROL, 0x6);
+ break;
+ }
+ }
+
+ hisi_hba->timer.function = link_timeout_enable_link;
+ mod_timer(&hisi_hba->timer, jiffies + msecs_to_jiffies(100));
+}
+
+static void set_link_timer_quirk(struct hisi_hba *hisi_hba)
+{
+ hisi_hba->timer.function = link_timeout_disable_link;
+ hisi_hba->timer.expires = jiffies + msecs_to_jiffies(1000);
+ add_timer(&hisi_hba->timer);
+}
+
+static int hw_init_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ rc = reset_hw_v2_hw(hisi_hba);
+ if (rc) {
+ dev_err(dev, "hisi_sas_reset_hw failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ msleep(100);
+ init_reg_v2_hw(hisi_hba);
+
+ return 0;
+}
+
+static void enable_phy_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg |= PHY_CFG_ENA_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static bool is_sata_phy_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 context;
+
+ context = hisi_sas_read32(hisi_hba, PHY_CONTEXT);
+ if (context & (1 << phy_no))
+ return true;
+
+ return false;
+}
+
+static bool tx_fifo_is_empty_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 dfx_val;
+
+ dfx_val = hisi_sas_phy_read32(hisi_hba, phy_no, DMA_TX_DFX1);
+
+ if (dfx_val & BIT(16))
+ return false;
+
+ return true;
+}
+
+static bool axi_bus_is_idle_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ int i, max_loop = 1000;
+ struct device *dev = hisi_hba->dev;
+ u32 status, axi_status, dfx_val, dfx_tx_val;
+
+ for (i = 0; i < max_loop; i++) {
+ status = hisi_sas_read32_relaxed(hisi_hba,
+ AXI_MASTER_CFG_BASE + AM_CURR_TRANS_RETURN);
+
+ axi_status = hisi_sas_read32(hisi_hba, AXI_CFG);
+ dfx_val = hisi_sas_phy_read32(hisi_hba, phy_no, DMA_TX_DFX1);
+ dfx_tx_val = hisi_sas_phy_read32(hisi_hba,
+ phy_no, DMA_TX_FIFO_DFX0);
+
+ if ((status == 0x3) && (axi_status == 0x0) &&
+ (dfx_val & BIT(20)) && (dfx_tx_val & BIT(10)))
+ return true;
+ udelay(10);
+ }
+ dev_err(dev, "bus is not idle phy%d, axi150:0x%x axi100:0x%x port204:0x%x port240:0x%x\n",
+ phy_no, status, axi_status,
+ dfx_val, dfx_tx_val);
+ return false;
+}
+
+static bool wait_io_done_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ int i, max_loop = 1000;
+ struct device *dev = hisi_hba->dev;
+ u32 status, tx_dfx0;
+
+ for (i = 0; i < max_loop; i++) {
+ status = hisi_sas_phy_read32(hisi_hba, phy_no, LINK_DFX2);
+ status = (status & 0x3fc0) >> 6;
+
+ if (status != 0x1)
+ return true;
+
+ tx_dfx0 = hisi_sas_phy_read32(hisi_hba, phy_no, DMA_TX_DFX0);
+ if ((tx_dfx0 & 0x1ff) == 0x2)
+ return true;
+ udelay(10);
+ }
+ dev_err(dev, "IO not done phy%d, port264:0x%x port200:0x%x\n",
+ phy_no, status, tx_dfx0);
+ return false;
+}
+
+static bool allowed_disable_phy_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ if (tx_fifo_is_empty_v2_hw(hisi_hba, phy_no))
+ return true;
+
+ if (!axi_bus_is_idle_v2_hw(hisi_hba, phy_no))
+ return false;
+
+ if (!wait_io_done_v2_hw(hisi_hba, phy_no))
+ return false;
+
+ return true;
+}
+
+
+static void disable_phy_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg, axi_val, dfx0_val, txid_auto;
+ struct device *dev = hisi_hba->dev;
+
+ /* Close axi bus. */
+ axi_val = hisi_sas_read32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL);
+ axi_val |= 0x1;
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL, axi_val);
+
+ if (is_sata_phy_v2_hw(hisi_hba, phy_no)) {
+ if (allowed_disable_phy_v2_hw(hisi_hba, phy_no))
+ goto do_disable;
+
+ /* Reset host controller. */
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ return;
+ }
+
+ dfx0_val = hisi_sas_phy_read32(hisi_hba, phy_no, PORT_DFX0);
+ dfx0_val = (dfx0_val & 0x1fc0) >> 6;
+ if (dfx0_val != 0x4)
+ goto do_disable;
+
+ if (!tx_fifo_is_empty_v2_hw(hisi_hba, phy_no)) {
+ dev_warn(dev, "phy%d, wait tx fifo need send break\n",
+ phy_no);
+ txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no,
+ TXID_AUTO);
+ txid_auto |= TXID_AUTO_CTB_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+ txid_auto);
+ }
+
+do_disable:
+ cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+ cfg &= ~PHY_CFG_ENA_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+
+ /* Open axi bus. */
+ axi_val &= ~0x1;
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL, axi_val);
+}
+
+static void start_phy_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ config_id_frame_v2_hw(hisi_hba, phy_no);
+ config_phy_opt_mode_v2_hw(hisi_hba, phy_no);
+ enable_phy_v2_hw(hisi_hba, phy_no);
+}
+
+static void phy_hard_reset_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ u32 txid_auto;
+
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+ if (phy->identify.device_type == SAS_END_DEVICE) {
+ txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+ txid_auto | TX_HARDRST_MSK);
+ }
+ msleep(100);
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+}
+
+static void phy_get_events_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_phy *sphy = sas_phy->phy;
+ u32 err4_reg_val, err6_reg_val;
+
+ /* loss dword syn, phy reset problem */
+ err4_reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, SAS_ERR_CNT4_REG);
+
+ /* disparity err, invalid dword */
+ err6_reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, SAS_ERR_CNT6_REG);
+
+ sphy->loss_of_dword_sync_count += (err4_reg_val >> 16) & 0xFFFF;
+ sphy->phy_reset_problem_count += err4_reg_val & 0xFFFF;
+ sphy->invalid_dword_count += (err6_reg_val & 0xFF0000) >> 16;
+ sphy->running_disparity_error_count += err6_reg_val & 0xFF;
+}
+
+static void phys_init_v2_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ if (!sas_phy->phy->enabled)
+ continue;
+
+ hisi_sas_phy_enable(hisi_hba, i, 1);
+ }
+}
+
+static void sl_notify_ssp_v2_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 sl_control;
+
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control |= SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+ msleep(1);
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control &= ~SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+}
+
+static enum sas_linkrate phy_get_max_linkrate_v2_hw(void)
+{
+ return SAS_LINK_RATE_12_0_GBPS;
+}
+
+static void phy_set_linkrate_v2_hw(struct hisi_hba *hisi_hba, int phy_no,
+ struct sas_phy_linkrates *r)
+{
+ enum sas_linkrate max = r->maximum_linkrate;
+ u32 prog_phy_link_rate = 0x800;
+
+ prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE,
+ prog_phy_link_rate);
+}
+
+static int get_wideport_bitmap_v2_hw(struct hisi_hba *hisi_hba, int port_id)
+{
+ int i, bitmap = 0;
+ u32 phy_port_num_ma = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+ u32 phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+
+ for (i = 0; i < (hisi_hba->n_phy < 9 ? hisi_hba->n_phy : 8); i++)
+ if (phy_state & 1 << i)
+ if (((phy_port_num_ma >> (i * 4)) & 0xf) == port_id)
+ bitmap |= 1 << i;
+
+ if (hisi_hba->n_phy == 9) {
+ u32 port_state = hisi_sas_read32(hisi_hba, PORT_STATE);
+
+ if (phy_state & 1 << 8)
+ if (((port_state & PORT_STATE_PHY8_PORT_NUM_MSK) >>
+ PORT_STATE_PHY8_PORT_NUM_OFF) == port_id)
+ bitmap |= 1 << 9;
+ }
+
+ return bitmap;
+}
+
+/* DQ lock must be taken here */
+static void start_delivery_v2_hw(struct hisi_sas_dq *dq)
+{
+ struct hisi_hba *hisi_hba = dq->hisi_hba;
+ struct hisi_sas_slot *s, *s1, *s2 = NULL;
+ int dlvry_queue = dq->id;
+ int wp;
+
+ list_for_each_entry_safe(s, s1, &dq->list, delivery) {
+ if (!s->ready)
+ break;
+ s2 = s;
+ list_del(&s->delivery);
+ }
+
+ if (!s2)
+ return;
+
+ /*
+ * Ensure that memories for slots built on other CPUs is observed.
+ */
+ smp_rmb();
+ wp = (s2->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS;
+
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), wp);
+}
+
+static void prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_cmd_hdr *hdr,
+ struct scatterlist *scatter,
+ int n_elem)
+{
+ struct hisi_sas_sge_page *sge_page = hisi_sas_sge_addr_mem(slot);
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(scatter, sg, n_elem, i) {
+ struct hisi_sas_sge *entry = &sge_page->sge[i];
+
+ entry->addr = cpu_to_le64(sg_dma_address(sg));
+ entry->page_ctrl_0 = entry->page_ctrl_1 = 0;
+ entry->data_len = cpu_to_le32(sg_dma_len(sg));
+ entry->data_off = 0;
+ }
+
+ hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot));
+
+ hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+}
+
+static void prep_smp_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_port *port = slot->port;
+ struct scatterlist *sg_req;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ dma_addr_t req_dma_addr;
+ unsigned int req_len;
+
+ /* req */
+ sg_req = &task->smp_task.smp_req;
+ req_dma_addr = sg_dma_address(sg_req);
+ req_len = sg_dma_len(&task->smp_task.smp_req);
+
+ /* create header */
+ /* dw0 */
+ hdr->dw0 = cpu_to_le32((port->id << CMD_HDR_PORT_OFF) |
+ (1 << CMD_HDR_PRIORITY_OFF) | /* high pri */
+ (2 << CMD_HDR_CMD_OFF)); /* smp */
+
+ /* map itct entry */
+ hdr->dw1 = cpu_to_le32((sas_dev->device_id << CMD_HDR_DEV_ID_OFF) |
+ (1 << CMD_HDR_FRAME_TYPE_OFF) |
+ (DIR_NO_DATA << CMD_HDR_DIR_OFF));
+
+ /* dw2 */
+ hdr->dw2 = cpu_to_le32((((req_len - 4) / 4) << CMD_HDR_CFL_OFF) |
+ (HISI_SAS_MAX_SMP_RESP_SZ / 4 <<
+ CMD_HDR_MRFL_OFF));
+
+ hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF);
+
+ hdr->cmd_table_addr = cpu_to_le64(req_dma_addr);
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+}
+
+static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_port *port = slot->port;
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+ struct sas_tmf_task *tmf = slot->tmf;
+ int has_data = 0, priority = !!tmf;
+ u8 *buf_cmd;
+ u32 dw1 = 0, dw2 = 0;
+
+ hdr->dw0 = cpu_to_le32((1 << CMD_HDR_RESP_REPORT_OFF) |
+ (2 << CMD_HDR_TLR_CTRL_OFF) |
+ (port->id << CMD_HDR_PORT_OFF) |
+ (priority << CMD_HDR_PRIORITY_OFF) |
+ (1 << CMD_HDR_CMD_OFF)); /* ssp */
+
+ dw1 = 1 << CMD_HDR_VDTL_OFF;
+ if (tmf) {
+ dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF;
+ dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF;
+ } else {
+ dw1 |= 1 << CMD_HDR_FRAME_TYPE_OFF;
+ switch (scsi_cmnd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_DEVICE << CMD_HDR_DIR_OFF;
+ break;
+ case DMA_FROM_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_INI << CMD_HDR_DIR_OFF;
+ break;
+ default:
+ dw1 &= ~CMD_HDR_DIR_MSK;
+ }
+ }
+
+ /* map itct entry */
+ dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF;
+ hdr->dw1 = cpu_to_le32(dw1);
+
+ dw2 = (((sizeof(struct ssp_command_iu) + sizeof(struct ssp_frame_hdr)
+ + 3) / 4) << CMD_HDR_CFL_OFF) |
+ ((HISI_SAS_MAX_SSP_RESP_SZ / 4) << CMD_HDR_MRFL_OFF) |
+ (2 << CMD_HDR_SG_MOD_OFF);
+ hdr->dw2 = cpu_to_le32(dw2);
+
+ hdr->transfer_tags = cpu_to_le32(slot->idx);
+
+ if (has_data)
+ prep_prd_sge_v2_hw(hisi_hba, slot, hdr, task->scatter,
+ slot->n_elem);
+
+ hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len);
+ hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot));
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+
+ buf_cmd = hisi_sas_cmd_hdr_addr_mem(slot) +
+ sizeof(struct ssp_frame_hdr);
+
+ memcpy(buf_cmd, &task->ssp_task.LUN, 8);
+ if (!tmf) {
+ buf_cmd[9] = task->ssp_task.task_attr;
+ memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
+ } else {
+ buf_cmd[10] = tmf->tmf;
+ switch (tmf->tmf) {
+ case TMF_ABORT_TASK:
+ case TMF_QUERY_TASK:
+ buf_cmd[12] =
+ (tmf->tag_of_task_to_be_managed >> 8) & 0xff;
+ buf_cmd[13] =
+ tmf->tag_of_task_to_be_managed & 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+#define TRANS_TX_ERR 0
+#define TRANS_RX_ERR 1
+#define DMA_TX_ERR 2
+#define SIPC_RX_ERR 3
+#define DMA_RX_ERR 4
+
+#define DMA_TX_ERR_OFF 0
+#define DMA_TX_ERR_MSK (0xffff << DMA_TX_ERR_OFF)
+#define SIPC_RX_ERR_OFF 16
+#define SIPC_RX_ERR_MSK (0xffff << SIPC_RX_ERR_OFF)
+
+static int parse_trans_tx_err_code_v2_hw(u32 err_msk)
+{
+ static const u8 trans_tx_err_code_prio[] = {
+ TRANS_TX_OPEN_FAIL_WITH_IT_NEXUS_LOSS,
+ TRANS_TX_ERR_PHY_NOT_ENABLE,
+ TRANS_TX_OPEN_CNX_ERR_WRONG_DESTINATION,
+ TRANS_TX_OPEN_CNX_ERR_ZONE_VIOLATION,
+ TRANS_TX_OPEN_CNX_ERR_BY_OTHER,
+ RESERVED0,
+ TRANS_TX_OPEN_CNX_ERR_AIP_TIMEOUT,
+ TRANS_TX_OPEN_CNX_ERR_STP_RESOURCES_BUSY,
+ TRANS_TX_OPEN_CNX_ERR_PROTOCOL_NOT_SUPPORTED,
+ TRANS_TX_OPEN_CNX_ERR_CONNECTION_RATE_NOT_SUPPORTED,
+ TRANS_TX_OPEN_CNX_ERR_BAD_DESTINATION,
+ TRANS_TX_OPEN_CNX_ERR_BREAK_RCVD,
+ TRANS_TX_OPEN_CNX_ERR_LOW_PHY_POWER,
+ TRANS_TX_OPEN_CNX_ERR_PATHWAY_BLOCKED,
+ TRANS_TX_OPEN_CNX_ERR_OPEN_TIMEOUT,
+ TRANS_TX_OPEN_CNX_ERR_NO_DESTINATION,
+ TRANS_TX_OPEN_RETRY_ERR_THRESHOLD_REACHED,
+ TRANS_TX_ERR_WITH_CLOSE_PHYDISALE,
+ TRANS_TX_ERR_WITH_CLOSE_DWS_TIMEOUT,
+ TRANS_TX_ERR_WITH_CLOSE_COMINIT,
+ TRANS_TX_ERR_WITH_BREAK_TIMEOUT,
+ TRANS_TX_ERR_WITH_BREAK_REQUEST,
+ TRANS_TX_ERR_WITH_BREAK_RECEVIED,
+ TRANS_TX_ERR_WITH_CLOSE_TIMEOUT,
+ TRANS_TX_ERR_WITH_CLOSE_NORMAL,
+ TRANS_TX_ERR_WITH_NAK_RECEVIED,
+ TRANS_TX_ERR_WITH_ACK_NAK_TIMEOUT,
+ TRANS_TX_ERR_WITH_CREDIT_TIMEOUT,
+ TRANS_TX_ERR_WITH_IPTT_CONFLICT,
+ TRANS_TX_ERR_WITH_OPEN_BY_DES_OR_OTHERS,
+ TRANS_TX_ERR_WITH_WAIT_RECV_TIMEOUT,
+ };
+ int index, i;
+
+ for (i = 0; i < ARRAY_SIZE(trans_tx_err_code_prio); i++) {
+ index = trans_tx_err_code_prio[i] - TRANS_TX_FAIL_BASE;
+ if (err_msk & (1 << index))
+ return trans_tx_err_code_prio[i];
+ }
+ return -1;
+}
+
+static int parse_trans_rx_err_code_v2_hw(u32 err_msk)
+{
+ static const u8 trans_rx_err_code_prio[] = {
+ TRANS_RX_ERR_WITH_RXFRAME_CRC_ERR,
+ TRANS_RX_ERR_WITH_RXFIS_8B10B_DISP_ERR,
+ TRANS_RX_ERR_WITH_RXFRAME_HAVE_ERRPRM,
+ TRANS_RX_ERR_WITH_RXFIS_DECODE_ERROR,
+ TRANS_RX_ERR_WITH_RXFIS_CRC_ERR,
+ TRANS_RX_ERR_WITH_RXFRAME_LENGTH_OVERRUN,
+ TRANS_RX_ERR_WITH_RXFIS_RX_SYNCP,
+ TRANS_RX_ERR_WITH_LINK_BUF_OVERRUN,
+ TRANS_RX_ERR_WITH_CLOSE_PHY_DISABLE,
+ TRANS_RX_ERR_WITH_CLOSE_DWS_TIMEOUT,
+ TRANS_RX_ERR_WITH_CLOSE_COMINIT,
+ TRANS_RX_ERR_WITH_BREAK_TIMEOUT,
+ TRANS_RX_ERR_WITH_BREAK_REQUEST,
+ TRANS_RX_ERR_WITH_BREAK_RECEVIED,
+ RESERVED1,
+ TRANS_RX_ERR_WITH_CLOSE_NORMAL,
+ TRANS_RX_ERR_WITH_DATA_LEN0,
+ TRANS_RX_ERR_WITH_BAD_HASH,
+ TRANS_RX_XRDY_WLEN_ZERO_ERR,
+ TRANS_RX_SSP_FRM_LEN_ERR,
+ RESERVED2,
+ RESERVED3,
+ RESERVED4,
+ RESERVED5,
+ TRANS_RX_ERR_WITH_BAD_FRM_TYPE,
+ TRANS_RX_SMP_FRM_LEN_ERR,
+ TRANS_RX_SMP_RESP_TIMEOUT_ERR,
+ RESERVED6,
+ RESERVED7,
+ RESERVED8,
+ RESERVED9,
+ TRANS_RX_R_ERR,
+ };
+ int index, i;
+
+ for (i = 0; i < ARRAY_SIZE(trans_rx_err_code_prio); i++) {
+ index = trans_rx_err_code_prio[i] - TRANS_RX_FAIL_BASE;
+ if (err_msk & (1 << index))
+ return trans_rx_err_code_prio[i];
+ }
+ return -1;
+}
+
+static int parse_dma_tx_err_code_v2_hw(u32 err_msk)
+{
+ static const u8 dma_tx_err_code_prio[] = {
+ DMA_TX_UNEXP_XFER_ERR,
+ DMA_TX_UNEXP_RETRANS_ERR,
+ DMA_TX_XFER_LEN_OVERFLOW,
+ DMA_TX_XFER_OFFSET_ERR,
+ DMA_TX_RAM_ECC_ERR,
+ DMA_TX_DIF_LEN_ALIGN_ERR,
+ DMA_TX_DIF_CRC_ERR,
+ DMA_TX_DIF_APP_ERR,
+ DMA_TX_DIF_RPP_ERR,
+ DMA_TX_DATA_SGL_OVERFLOW,
+ DMA_TX_DIF_SGL_OVERFLOW,
+ };
+ int index, i;
+
+ for (i = 0; i < ARRAY_SIZE(dma_tx_err_code_prio); i++) {
+ index = dma_tx_err_code_prio[i] - DMA_TX_ERR_BASE;
+ err_msk = err_msk & DMA_TX_ERR_MSK;
+ if (err_msk & (1 << index))
+ return dma_tx_err_code_prio[i];
+ }
+ return -1;
+}
+
+static int parse_sipc_rx_err_code_v2_hw(u32 err_msk)
+{
+ static const u8 sipc_rx_err_code_prio[] = {
+ SIPC_RX_FIS_STATUS_ERR_BIT_VLD,
+ SIPC_RX_PIO_WRSETUP_STATUS_DRQ_ERR,
+ SIPC_RX_FIS_STATUS_BSY_BIT_ERR,
+ SIPC_RX_WRSETUP_LEN_ODD_ERR,
+ SIPC_RX_WRSETUP_LEN_ZERO_ERR,
+ SIPC_RX_WRDATA_LEN_NOT_MATCH_ERR,
+ SIPC_RX_NCQ_WRSETUP_OFFSET_ERR,
+ SIPC_RX_NCQ_WRSETUP_AUTO_ACTIVE_ERR,
+ SIPC_RX_SATA_UNEXP_FIS_ERR,
+ SIPC_RX_WRSETUP_ESTATUS_ERR,
+ SIPC_RX_DATA_UNDERFLOW_ERR,
+ };
+ int index, i;
+
+ for (i = 0; i < ARRAY_SIZE(sipc_rx_err_code_prio); i++) {
+ index = sipc_rx_err_code_prio[i] - SIPC_RX_ERR_BASE;
+ err_msk = err_msk & SIPC_RX_ERR_MSK;
+ if (err_msk & (1 << (index + 0x10)))
+ return sipc_rx_err_code_prio[i];
+ }
+ return -1;
+}
+
+static int parse_dma_rx_err_code_v2_hw(u32 err_msk)
+{
+ static const u8 dma_rx_err_code_prio[] = {
+ DMA_RX_UNKNOWN_FRM_ERR,
+ DMA_RX_DATA_LEN_OVERFLOW,
+ DMA_RX_DATA_LEN_UNDERFLOW,
+ DMA_RX_DATA_OFFSET_ERR,
+ RESERVED10,
+ DMA_RX_SATA_FRAME_TYPE_ERR,
+ DMA_RX_RESP_BUF_OVERFLOW,
+ DMA_RX_UNEXP_RETRANS_RESP_ERR,
+ DMA_RX_UNEXP_NORM_RESP_ERR,
+ DMA_RX_UNEXP_RDFRAME_ERR,
+ DMA_RX_PIO_DATA_LEN_ERR,
+ DMA_RX_RDSETUP_STATUS_ERR,
+ DMA_RX_RDSETUP_STATUS_DRQ_ERR,
+ DMA_RX_RDSETUP_STATUS_BSY_ERR,
+ DMA_RX_RDSETUP_LEN_ODD_ERR,
+ DMA_RX_RDSETUP_LEN_ZERO_ERR,
+ DMA_RX_RDSETUP_LEN_OVER_ERR,
+ DMA_RX_RDSETUP_OFFSET_ERR,
+ DMA_RX_RDSETUP_ACTIVE_ERR,
+ DMA_RX_RDSETUP_ESTATUS_ERR,
+ DMA_RX_RAM_ECC_ERR,
+ DMA_RX_DIF_CRC_ERR,
+ DMA_RX_DIF_APP_ERR,
+ DMA_RX_DIF_RPP_ERR,
+ DMA_RX_DATA_SGL_OVERFLOW,
+ DMA_RX_DIF_SGL_OVERFLOW,
+ };
+ int index, i;
+
+ for (i = 0; i < ARRAY_SIZE(dma_rx_err_code_prio); i++) {
+ index = dma_rx_err_code_prio[i] - DMA_RX_ERR_BASE;
+ if (err_msk & (1 << index))
+ return dma_rx_err_code_prio[i];
+ }
+ return -1;
+}
+
+/* by default, task resp is complete */
+static void slot_err_v2_hw(struct hisi_hba *hisi_hba,
+ struct sas_task *task,
+ struct hisi_sas_slot *slot,
+ int err_phase)
+{
+ struct task_status_struct *ts = &task->task_status;
+ struct hisi_sas_err_record_v2 *err_record =
+ hisi_sas_status_buf_addr_mem(slot);
+ u32 trans_tx_fail_type = le32_to_cpu(err_record->trans_tx_fail_type);
+ u32 trans_rx_fail_type = le32_to_cpu(err_record->trans_rx_fail_type);
+ u16 dma_tx_err_type = le16_to_cpu(err_record->dma_tx_err_type);
+ u16 sipc_rx_err_type = le16_to_cpu(err_record->sipc_rx_err_type);
+ u32 dma_rx_err_type = le32_to_cpu(err_record->dma_rx_err_type);
+ struct hisi_sas_complete_v2_hdr *complete_queue =
+ hisi_hba->complete_hdr[slot->cmplt_queue];
+ struct hisi_sas_complete_v2_hdr *complete_hdr =
+ &complete_queue[slot->cmplt_queue_slot];
+ u32 dw0 = le32_to_cpu(complete_hdr->dw0);
+ int error = -1;
+
+ if (err_phase == 1) {
+ /* error in TX phase, the priority of error is: DW2 > DW0 */
+ error = parse_dma_tx_err_code_v2_hw(dma_tx_err_type);
+ if (error == -1)
+ error = parse_trans_tx_err_code_v2_hw(
+ trans_tx_fail_type);
+ } else if (err_phase == 2) {
+ /* error in RX phase, the priority is: DW1 > DW3 > DW2 */
+ error = parse_trans_rx_err_code_v2_hw(trans_rx_fail_type);
+ if (error == -1) {
+ error = parse_dma_rx_err_code_v2_hw(
+ dma_rx_err_type);
+ if (error == -1)
+ error = parse_sipc_rx_err_code_v2_hw(
+ sipc_rx_err_type);
+ }
+ }
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ {
+ switch (error) {
+ case TRANS_TX_OPEN_CNX_ERR_NO_DESTINATION:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_NO_DEST;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_PROTOCOL_NOT_SUPPORTED:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_CONNECTION_RATE_NOT_SUPPORTED:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_BAD_DESTINATION:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_WRONG_DESTINATION:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ }
+ case DMA_RX_UNEXP_NORM_RESP_ERR:
+ case TRANS_TX_OPEN_CNX_ERR_ZONE_VIOLATION:
+ case DMA_RX_RESP_BUF_OVERFLOW:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_LOW_PHY_POWER:
+ {
+ /* not sure */
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ }
+ case DMA_RX_DATA_LEN_OVERFLOW:
+ {
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ break;
+ }
+ case DMA_RX_DATA_LEN_UNDERFLOW:
+ {
+ ts->residual = trans_tx_fail_type;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ }
+ case TRANS_TX_OPEN_FAIL_WITH_IT_NEXUS_LOSS:
+ case TRANS_TX_ERR_PHY_NOT_ENABLE:
+ case TRANS_TX_OPEN_CNX_ERR_BY_OTHER:
+ case TRANS_TX_OPEN_CNX_ERR_AIP_TIMEOUT:
+ case TRANS_TX_OPEN_CNX_ERR_BREAK_RCVD:
+ case TRANS_TX_OPEN_CNX_ERR_PATHWAY_BLOCKED:
+ case TRANS_TX_OPEN_CNX_ERR_OPEN_TIMEOUT:
+ case TRANS_TX_OPEN_RETRY_ERR_THRESHOLD_REACHED:
+ case TRANS_TX_ERR_WITH_BREAK_TIMEOUT:
+ case TRANS_TX_ERR_WITH_BREAK_REQUEST:
+ case TRANS_TX_ERR_WITH_BREAK_RECEVIED:
+ case TRANS_TX_ERR_WITH_CLOSE_TIMEOUT:
+ case TRANS_TX_ERR_WITH_CLOSE_NORMAL:
+ case TRANS_TX_ERR_WITH_CLOSE_PHYDISALE:
+ case TRANS_TX_ERR_WITH_CLOSE_DWS_TIMEOUT:
+ case TRANS_TX_ERR_WITH_CLOSE_COMINIT:
+ case TRANS_TX_ERR_WITH_NAK_RECEVIED:
+ case TRANS_TX_ERR_WITH_ACK_NAK_TIMEOUT:
+ case TRANS_TX_ERR_WITH_CREDIT_TIMEOUT:
+ case TRANS_TX_ERR_WITH_IPTT_CONFLICT:
+ case TRANS_RX_ERR_WITH_RXFRAME_CRC_ERR:
+ case TRANS_RX_ERR_WITH_RXFIS_8B10B_DISP_ERR:
+ case TRANS_RX_ERR_WITH_RXFRAME_HAVE_ERRPRM:
+ case TRANS_RX_ERR_WITH_LINK_BUF_OVERRUN:
+ case TRANS_RX_ERR_WITH_BREAK_TIMEOUT:
+ case TRANS_RX_ERR_WITH_BREAK_REQUEST:
+ case TRANS_RX_ERR_WITH_BREAK_RECEVIED:
+ case TRANS_RX_ERR_WITH_CLOSE_NORMAL:
+ case TRANS_RX_ERR_WITH_CLOSE_DWS_TIMEOUT:
+ case TRANS_RX_ERR_WITH_CLOSE_COMINIT:
+ case TRANS_TX_ERR_FRAME_TXED:
+ case TRANS_RX_ERR_WITH_CLOSE_PHY_DISABLE:
+ case TRANS_RX_ERR_WITH_DATA_LEN0:
+ case TRANS_RX_ERR_WITH_BAD_HASH:
+ case TRANS_RX_XRDY_WLEN_ZERO_ERR:
+ case TRANS_RX_SSP_FRM_LEN_ERR:
+ case TRANS_RX_ERR_WITH_BAD_FRM_TYPE:
+ case DMA_TX_DATA_SGL_OVERFLOW:
+ case DMA_TX_UNEXP_XFER_ERR:
+ case DMA_TX_UNEXP_RETRANS_ERR:
+ case DMA_TX_XFER_LEN_OVERFLOW:
+ case DMA_TX_XFER_OFFSET_ERR:
+ case SIPC_RX_DATA_UNDERFLOW_ERR:
+ case DMA_RX_DATA_SGL_OVERFLOW:
+ case DMA_RX_DATA_OFFSET_ERR:
+ case DMA_RX_RDSETUP_LEN_ODD_ERR:
+ case DMA_RX_RDSETUP_LEN_ZERO_ERR:
+ case DMA_RX_RDSETUP_LEN_OVER_ERR:
+ case DMA_RX_SATA_FRAME_TYPE_ERR:
+ case DMA_RX_UNKNOWN_FRM_ERR:
+ {
+ /* This will request a retry */
+ ts->stat = SAS_QUEUE_FULL;
+ slot->abort = 1;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ case SAS_PROTOCOL_SMP:
+ ts->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ break;
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ {
+ switch (error) {
+ case TRANS_TX_OPEN_CNX_ERR_NO_DESTINATION:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_NO_DEST;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_LOW_PHY_POWER:
+ {
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_PROTOCOL_NOT_SUPPORTED:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_CONNECTION_RATE_NOT_SUPPORTED:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_BAD_DESTINATION:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ }
+ case TRANS_TX_OPEN_CNX_ERR_WRONG_DESTINATION:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ }
+ case DMA_RX_RESP_BUF_OVERFLOW:
+ case DMA_RX_UNEXP_NORM_RESP_ERR:
+ case TRANS_TX_OPEN_CNX_ERR_ZONE_VIOLATION:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ }
+ case DMA_RX_DATA_LEN_OVERFLOW:
+ {
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ break;
+ }
+ case DMA_RX_DATA_LEN_UNDERFLOW:
+ {
+ ts->residual = trans_tx_fail_type;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ }
+ case TRANS_TX_OPEN_FAIL_WITH_IT_NEXUS_LOSS:
+ case TRANS_TX_ERR_PHY_NOT_ENABLE:
+ case TRANS_TX_OPEN_CNX_ERR_BY_OTHER:
+ case TRANS_TX_OPEN_CNX_ERR_AIP_TIMEOUT:
+ case TRANS_TX_OPEN_CNX_ERR_BREAK_RCVD:
+ case TRANS_TX_OPEN_CNX_ERR_PATHWAY_BLOCKED:
+ case TRANS_TX_OPEN_CNX_ERR_OPEN_TIMEOUT:
+ case TRANS_TX_OPEN_RETRY_ERR_THRESHOLD_REACHED:
+ case TRANS_TX_ERR_WITH_BREAK_TIMEOUT:
+ case TRANS_TX_ERR_WITH_BREAK_REQUEST:
+ case TRANS_TX_ERR_WITH_BREAK_RECEVIED:
+ case TRANS_TX_ERR_WITH_CLOSE_TIMEOUT:
+ case TRANS_TX_ERR_WITH_CLOSE_NORMAL:
+ case TRANS_TX_ERR_WITH_CLOSE_PHYDISALE:
+ case TRANS_TX_ERR_WITH_CLOSE_DWS_TIMEOUT:
+ case TRANS_TX_ERR_WITH_CLOSE_COMINIT:
+ case TRANS_TX_ERR_WITH_ACK_NAK_TIMEOUT:
+ case TRANS_TX_ERR_WITH_CREDIT_TIMEOUT:
+ case TRANS_TX_ERR_WITH_OPEN_BY_DES_OR_OTHERS:
+ case TRANS_TX_ERR_WITH_WAIT_RECV_TIMEOUT:
+ case TRANS_RX_ERR_WITH_RXFRAME_HAVE_ERRPRM:
+ case TRANS_RX_ERR_WITH_RXFIS_8B10B_DISP_ERR:
+ case TRANS_RX_ERR_WITH_RXFIS_DECODE_ERROR:
+ case TRANS_RX_ERR_WITH_RXFIS_CRC_ERR:
+ case TRANS_RX_ERR_WITH_RXFRAME_LENGTH_OVERRUN:
+ case TRANS_RX_ERR_WITH_RXFIS_RX_SYNCP:
+ case TRANS_RX_ERR_WITH_LINK_BUF_OVERRUN:
+ case TRANS_RX_ERR_WITH_BREAK_TIMEOUT:
+ case TRANS_RX_ERR_WITH_BREAK_REQUEST:
+ case TRANS_RX_ERR_WITH_BREAK_RECEVIED:
+ case TRANS_RX_ERR_WITH_CLOSE_NORMAL:
+ case TRANS_RX_ERR_WITH_CLOSE_PHY_DISABLE:
+ case TRANS_RX_ERR_WITH_CLOSE_DWS_TIMEOUT:
+ case TRANS_RX_ERR_WITH_CLOSE_COMINIT:
+ case TRANS_RX_ERR_WITH_DATA_LEN0:
+ case TRANS_RX_ERR_WITH_BAD_HASH:
+ case TRANS_RX_XRDY_WLEN_ZERO_ERR:
+ case TRANS_RX_ERR_WITH_BAD_FRM_TYPE:
+ case DMA_TX_DATA_SGL_OVERFLOW:
+ case DMA_TX_UNEXP_XFER_ERR:
+ case DMA_TX_UNEXP_RETRANS_ERR:
+ case DMA_TX_XFER_LEN_OVERFLOW:
+ case DMA_TX_XFER_OFFSET_ERR:
+ case SIPC_RX_FIS_STATUS_ERR_BIT_VLD:
+ case SIPC_RX_PIO_WRSETUP_STATUS_DRQ_ERR:
+ case SIPC_RX_FIS_STATUS_BSY_BIT_ERR:
+ case SIPC_RX_WRSETUP_LEN_ODD_ERR:
+ case SIPC_RX_WRSETUP_LEN_ZERO_ERR:
+ case SIPC_RX_WRDATA_LEN_NOT_MATCH_ERR:
+ case SIPC_RX_SATA_UNEXP_FIS_ERR:
+ case DMA_RX_DATA_SGL_OVERFLOW:
+ case DMA_RX_DATA_OFFSET_ERR:
+ case DMA_RX_SATA_FRAME_TYPE_ERR:
+ case DMA_RX_UNEXP_RDFRAME_ERR:
+ case DMA_RX_PIO_DATA_LEN_ERR:
+ case DMA_RX_RDSETUP_STATUS_ERR:
+ case DMA_RX_RDSETUP_STATUS_DRQ_ERR:
+ case DMA_RX_RDSETUP_STATUS_BSY_ERR:
+ case DMA_RX_RDSETUP_LEN_ODD_ERR:
+ case DMA_RX_RDSETUP_LEN_ZERO_ERR:
+ case DMA_RX_RDSETUP_LEN_OVER_ERR:
+ case DMA_RX_RDSETUP_OFFSET_ERR:
+ case DMA_RX_RDSETUP_ACTIVE_ERR:
+ case DMA_RX_RDSETUP_ESTATUS_ERR:
+ case DMA_RX_UNKNOWN_FRM_ERR:
+ case TRANS_RX_SSP_FRM_LEN_ERR:
+ case TRANS_TX_OPEN_CNX_ERR_STP_RESOURCES_BUSY:
+ {
+ slot->abort = 1;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ }
+ default:
+ {
+ ts->stat = SAS_PROTO_RESPONSE;
+ break;
+ }
+ }
+ if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK)
+ hisi_sas_sata_done(task, slot);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void slot_complete_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_device *sas_dev;
+ struct device *dev = hisi_hba->dev;
+ struct task_status_struct *ts;
+ struct domain_device *device;
+ struct sas_ha_struct *ha;
+ struct hisi_sas_complete_v2_hdr *complete_queue =
+ hisi_hba->complete_hdr[slot->cmplt_queue];
+ struct hisi_sas_complete_v2_hdr *complete_hdr =
+ &complete_queue[slot->cmplt_queue_slot];
+ unsigned long flags;
+ bool is_internal = slot->is_internal;
+ u32 dw0;
+
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return;
+
+ ts = &task->task_status;
+ device = task->dev;
+ ha = device->port->ha;
+ sas_dev = device->lldd_dev;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ memset(ts, 0, sizeof(*ts));
+ ts->resp = SAS_TASK_COMPLETE;
+
+ if (unlikely(!sas_dev)) {
+ dev_dbg(dev, "slot complete: port has no device\n");
+ ts->stat = SAS_PHY_DOWN;
+ goto out;
+ }
+
+ /* Use SAS+TMF status codes */
+ dw0 = le32_to_cpu(complete_hdr->dw0);
+ switch ((dw0 & CMPLT_HDR_ABORT_STAT_MSK) >>
+ CMPLT_HDR_ABORT_STAT_OFF) {
+ case STAT_IO_ABORTED:
+ /* this io has been aborted by abort command */
+ ts->stat = SAS_ABORTED_TASK;
+ goto out;
+ case STAT_IO_COMPLETE:
+ /* internal abort command complete */
+ ts->stat = TMF_RESP_FUNC_SUCC;
+ timer_delete_sync(&slot->internal_abort_timer);
+ goto out;
+ case STAT_IO_NO_DEVICE:
+ ts->stat = TMF_RESP_FUNC_COMPLETE;
+ timer_delete_sync(&slot->internal_abort_timer);
+ goto out;
+ case STAT_IO_NOT_VALID:
+ /* abort single io, controller don't find
+ * the io need to abort
+ */
+ ts->stat = TMF_RESP_FUNC_FAILED;
+ timer_delete_sync(&slot->internal_abort_timer);
+ goto out;
+ default:
+ break;
+ }
+
+ if ((dw0 & CMPLT_HDR_ERX_MSK) && (!(dw0 & CMPLT_HDR_RSPNS_XFRD_MSK))) {
+ u32 err_phase = (dw0 & CMPLT_HDR_ERR_PHASE_MSK)
+ >> CMPLT_HDR_ERR_PHASE_OFF;
+ u32 *error_info = hisi_sas_status_buf_addr_mem(slot);
+
+ /* Analyse error happens on which phase TX or RX */
+ if (ERR_ON_TX_PHASE(err_phase))
+ slot_err_v2_hw(hisi_hba, task, slot, 1);
+ else if (ERR_ON_RX_PHASE(err_phase))
+ slot_err_v2_hw(hisi_hba, task, slot, 2);
+
+ if (ts->stat != SAS_DATA_UNDERRUN)
+ dev_info(dev, "erroneous completion iptt=%d task=%p dev id=%d CQ hdr: 0x%x 0x%x 0x%x 0x%x Error info: 0x%x 0x%x 0x%x 0x%x\n",
+ slot->idx, task, sas_dev->device_id,
+ complete_hdr->dw0, complete_hdr->dw1,
+ complete_hdr->act, complete_hdr->dw3,
+ error_info[0], error_info[1],
+ error_info[2], error_info[3]);
+
+ if (unlikely(slot->abort)) {
+ if (dev_is_sata(device) && task->ata_task.use_ncq)
+ sas_ata_device_link_abort(device, true);
+ else
+ sas_task_abort(task);
+
+ return;
+ }
+ goto out;
+ }
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ {
+ struct hisi_sas_status_buffer *status_buffer =
+ hisi_sas_status_buf_addr_mem(slot);
+ struct ssp_response_iu *iu = (struct ssp_response_iu *)
+ &status_buffer->iu[0];
+
+ sas_ssp_task_response(dev, task, iu);
+ break;
+ }
+ case SAS_PROTOCOL_SMP:
+ {
+ struct scatterlist *sg_resp = &task->smp_task.smp_resp;
+ void *to = page_address(sg_page(sg_resp));
+
+ ts->stat = SAS_SAM_STAT_GOOD;
+
+ memcpy(to + sg_resp->offset,
+ hisi_sas_status_buf_addr_mem(slot) +
+ sizeof(struct hisi_sas_err_record),
+ sg_resp->length);
+ break;
+ }
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ {
+ ts->stat = SAS_SAM_STAT_GOOD;
+ if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK)
+ hisi_sas_sata_done(task, slot);
+ break;
+ }
+ default:
+ ts->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ break;
+ }
+
+ if (!slot->port->port_attached) {
+ dev_warn(dev, "slot complete: port %d has removed\n",
+ slot->port->sas_port.id);
+ ts->stat = SAS_PHY_DOWN;
+ }
+
+out:
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ dev_info(dev, "slot complete: task(%p) aborted\n", task);
+ return;
+ }
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ hisi_sas_slot_task_free(hisi_hba, task, slot, true);
+
+ if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) {
+ spin_lock_irqsave(&device->done_lock, flags);
+ if (test_bit(SAS_HA_FROZEN, &ha->state)) {
+ spin_unlock_irqrestore(&device->done_lock, flags);
+ dev_info(dev, "slot complete: task(%p) ignored\n",
+ task);
+ return;
+ }
+ spin_unlock_irqrestore(&device->done_lock, flags);
+ }
+
+ if (task->task_done)
+ task->task_done(task);
+}
+
+static void prep_ata_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct asd_sas_port *sas_port = device->port;
+ struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+ struct sas_ata_task *ata_task = &task->ata_task;
+ struct sas_tmf_task *tmf = slot->tmf;
+ int phy_id;
+ u8 *buf_cmd;
+ int has_data = 0, hdr_tag = 0;
+ u32 dw0, dw1 = 0, dw2 = 0;
+
+ /* create header */
+ /* dw0 */
+ dw0 = port->id << CMD_HDR_PORT_OFF;
+ if (dev_parent_is_expander(device)) {
+ dw0 |= 3 << CMD_HDR_CMD_OFF;
+ } else {
+ phy_id = device->phy->identify.phy_identifier;
+ dw0 |= (1U << phy_id) << CMD_HDR_PHY_ID_OFF;
+ dw0 |= CMD_HDR_FORCE_PHY_MSK;
+ dw0 |= 4 << CMD_HDR_CMD_OFF;
+ }
+
+ if (tmf && ata_task->force_phy) {
+ dw0 |= CMD_HDR_FORCE_PHY_MSK;
+ dw0 |= (1 << ata_task->force_phy_id) << CMD_HDR_PHY_ID_OFF;
+ }
+
+ hdr->dw0 = cpu_to_le32(dw0);
+
+ /* dw1 */
+ switch (task->data_dir) {
+ case DMA_TO_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_DEVICE << CMD_HDR_DIR_OFF;
+ break;
+ case DMA_FROM_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_INI << CMD_HDR_DIR_OFF;
+ break;
+ default:
+ dw1 &= ~CMD_HDR_DIR_MSK;
+ }
+
+ if ((task->ata_task.fis.command == ATA_CMD_DEV_RESET) &&
+ (task->ata_task.fis.control & ATA_SRST))
+ dw1 |= 1 << CMD_HDR_RESET_OFF;
+
+ dw1 |= (hisi_sas_get_ata_protocol(task)) << CMD_HDR_FRAME_TYPE_OFF;
+ dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF;
+ hdr->dw1 = cpu_to_le32(dw1);
+
+ /* dw2 */
+ if (task->ata_task.use_ncq) {
+ struct ata_queued_cmd *qc = task->uldd_task;
+
+ hdr_tag = qc->tag;
+ task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
+ dw2 |= hdr_tag << CMD_HDR_NCQ_TAG_OFF;
+ }
+
+ dw2 |= (HISI_SAS_MAX_STP_RESP_SZ / 4) << CMD_HDR_CFL_OFF |
+ 2 << CMD_HDR_SG_MOD_OFF;
+ hdr->dw2 = cpu_to_le32(dw2);
+
+ /* dw3 */
+ hdr->transfer_tags = cpu_to_le32(slot->idx);
+
+ if (has_data)
+ prep_prd_sge_v2_hw(hisi_hba, slot, hdr, task->scatter,
+ slot->n_elem);
+
+ hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len);
+ hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot));
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+
+ buf_cmd = hisi_sas_cmd_hdr_addr_mem(slot);
+
+ if (likely(!task->ata_task.device_control_reg_update))
+ task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
+ /* fill in command FIS */
+ memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
+}
+
+static void hisi_sas_internal_abort_quirk_timeout(struct timer_list *t)
+{
+ struct hisi_sas_slot *slot = timer_container_of(slot, t,
+ internal_abort_timer);
+ struct hisi_sas_port *port = slot->port;
+ struct asd_sas_port *asd_sas_port;
+ struct asd_sas_phy *sas_phy;
+
+ if (!port)
+ return;
+
+ asd_sas_port = &port->sas_port;
+
+ /* Kick the hardware - send break command */
+ list_for_each_entry(sas_phy, &asd_sas_port->phy_list, port_phy_el) {
+ struct hisi_sas_phy *phy = sas_phy->lldd_phy;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ int phy_no = sas_phy->id;
+ u32 link_dfx2;
+
+ link_dfx2 = hisi_sas_phy_read32(hisi_hba, phy_no, LINK_DFX2);
+ if ((link_dfx2 == LINK_DFX2_RCVR_HOLD_STS_MSK) ||
+ (link_dfx2 & LINK_DFX2_SEND_HOLD_STS_MSK)) {
+ u32 txid_auto;
+
+ txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no,
+ TXID_AUTO);
+ txid_auto |= TXID_AUTO_CTB_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+ txid_auto);
+ return;
+ }
+ }
+}
+
+static void prep_abort_v2_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct sas_internal_abort_task *abort = &task->abort_task;
+ struct domain_device *dev = task->dev;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct hisi_sas_port *port = slot->port;
+ struct timer_list *timer = &slot->internal_abort_timer;
+ struct hisi_sas_device *sas_dev = dev->lldd_dev;
+
+ /* setup the quirk timer */
+ timer_setup(timer, hisi_sas_internal_abort_quirk_timeout, 0);
+ /* Set the timeout to 10ms less than internal abort timeout */
+ mod_timer(timer, jiffies + msecs_to_jiffies(100));
+
+ /* dw0 */
+ hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/
+ (port->id << CMD_HDR_PORT_OFF) |
+ (dev_is_sata(dev) <<
+ CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
+ (abort->type << CMD_HDR_ABORT_FLAG_OFF));
+
+ /* dw1 */
+ hdr->dw1 = cpu_to_le32(sas_dev->device_id << CMD_HDR_DEV_ID_OFF);
+
+ /* dw7 */
+ hdr->dw7 = cpu_to_le32(abort->tag << CMD_HDR_ABORT_IPTT_OFF);
+ hdr->transfer_tags = cpu_to_le32(slot->idx);
+}
+
+static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
+{
+ int i, res = IRQ_HANDLED;
+ u32 port_id, link_rate;
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct device *dev = hisi_hba->dev;
+ u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd;
+ struct sas_identify_frame *id = (struct sas_identify_frame *)frame_rcvd;
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1);
+
+ if (is_sata_phy_v2_hw(hisi_hba, phy_no))
+ goto end;
+
+ timer_delete(&phy->timer);
+
+ if (phy_no == 8) {
+ u32 port_state = hisi_sas_read32(hisi_hba, PORT_STATE);
+
+ port_id = (port_state & PORT_STATE_PHY8_PORT_NUM_MSK) >>
+ PORT_STATE_PHY8_PORT_NUM_OFF;
+ link_rate = (port_state & PORT_STATE_PHY8_CONN_RATE_MSK) >>
+ PORT_STATE_PHY8_CONN_RATE_OFF;
+ } else {
+ port_id = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+ port_id = (port_id >> (4 * phy_no)) & 0xf;
+ link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE);
+ link_rate = (link_rate >> (phy_no * 4)) & 0xf;
+ }
+
+ if (port_id == 0xf) {
+ dev_err(dev, "phyup: phy%d invalid portid\n", phy_no);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ for (i = 0; i < 6; i++) {
+ u32 idaf = hisi_sas_phy_read32(hisi_hba, phy_no,
+ RX_IDAF_DWORD0 + (i * 4));
+ frame_rcvd[i] = __swab32(idaf);
+ }
+
+ sas_phy->linkrate = link_rate;
+ sas_phy->oob_mode = SAS_OOB_MODE;
+ memcpy(sas_phy->attached_sas_addr, &id->sas_addr, SAS_ADDR_SIZE);
+ dev_info(dev, "phyup: phy%d link_rate=%d\n", phy_no, link_rate);
+ phy->port_id = port_id;
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+ phy->phy_type |= PORT_TYPE_SAS;
+ phy->phy_attached = 1;
+ phy->identify.device_type = id->dev_type;
+ phy->frame_rcvd_size = sizeof(struct sas_identify_frame);
+ if (phy->identify.device_type == SAS_END_DEVICE)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != SAS_PHY_UNUSED) {
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SMP;
+ if (!timer_pending(&hisi_hba->timer))
+ set_link_timer_quirk(hisi_hba);
+ }
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);
+end:
+ if (phy->reset_completion)
+ complete(phy->reset_completion);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+ CHL_INT0_SL_PHY_ENABLE_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 0);
+
+ return res;
+}
+
+static bool check_any_wideports_v2_hw(struct hisi_hba *hisi_hba)
+{
+ u32 port_state;
+
+ port_state = hisi_sas_read32(hisi_hba, PORT_STATE);
+ if (port_state & 0x1ff)
+ return true;
+
+ return false;
+}
+
+static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
+{
+ u32 phy_state, sl_ctrl, txid_auto;
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct hisi_sas_port *port = phy->port;
+ struct device *dev = hisi_hba->dev;
+
+ timer_delete(&phy->timer);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1);
+
+ phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+ dev_info(dev, "phydown: phy%d phy_state=0x%x\n", phy_no, phy_state);
+ hisi_sas_phy_down(hisi_hba, phy_no, (phy_state & 1 << phy_no) ? 1 : 0,
+ GFP_ATOMIC);
+
+ sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL,
+ sl_ctrl & ~SL_CONTROL_CTA_MSK);
+ if (port && !get_wideport_bitmap_v2_hw(hisi_hba, port->id))
+ if (!check_any_wideports_v2_hw(hisi_hba) &&
+ timer_pending(&hisi_hba->timer))
+ timer_delete(&hisi_hba->timer);
+
+ txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+ txid_auto | TXID_AUTO_CT3_MSK);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, CHL_INT0_NOT_RDY_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t int_phy_updown_v2_hw(int irq_no, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ u32 irq_msk;
+ int phy_no = 0;
+ irqreturn_t res = IRQ_NONE;
+
+ irq_msk = (hisi_sas_read32(hisi_hba, HGC_INVLD_DQE_INFO)
+ >> HGC_INVLD_DQE_INFO_FB_CH0_OFF) & 0x1ff;
+ while (irq_msk) {
+ if (irq_msk & 1) {
+ u32 reg_value = hisi_sas_phy_read32(hisi_hba, phy_no,
+ CHL_INT0);
+
+ switch (reg_value & (CHL_INT0_NOT_RDY_MSK |
+ CHL_INT0_SL_PHY_ENABLE_MSK)) {
+
+ case CHL_INT0_SL_PHY_ENABLE_MSK:
+ /* phy up */
+ if (phy_up_v2_hw(phy_no, hisi_hba) ==
+ IRQ_HANDLED)
+ res = IRQ_HANDLED;
+ break;
+
+ case CHL_INT0_NOT_RDY_MSK:
+ /* phy down */
+ if (phy_down_v2_hw(phy_no, hisi_hba) ==
+ IRQ_HANDLED)
+ res = IRQ_HANDLED;
+ break;
+
+ case (CHL_INT0_NOT_RDY_MSK |
+ CHL_INT0_SL_PHY_ENABLE_MSK):
+ reg_value = hisi_sas_read32(hisi_hba,
+ PHY_STATE);
+ if (reg_value & BIT(phy_no)) {
+ /* phy up */
+ if (phy_up_v2_hw(phy_no, hisi_hba) ==
+ IRQ_HANDLED)
+ res = IRQ_HANDLED;
+ } else {
+ /* phy down */
+ if (phy_down_v2_hw(phy_no, hisi_hba) ==
+ IRQ_HANDLED)
+ res = IRQ_HANDLED;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ }
+ irq_msk >>= 1;
+ phy_no++;
+ }
+
+ return res;
+}
+
+static void phy_bcast_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ u32 bcast_status;
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 1);
+ bcast_status = hisi_sas_phy_read32(hisi_hba, phy_no, RX_PRIMS_STATUS);
+ if (bcast_status & RX_BCAST_CHG_MSK)
+ hisi_sas_phy_bcast(phy);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+ CHL_INT0_SL_RX_BCST_ACK_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 0);
+}
+
+static const struct hisi_sas_hw_error port_ecc_axi_error[] = {
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_ECC_ERR_OFF),
+ .msg = "dmac_tx_ecc_bad_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_ECC_ERR_OFF),
+ .msg = "dmac_rx_ecc_bad_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF),
+ .msg = "dma_tx_axi_wr_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF),
+ .msg = "dma_tx_axi_rd_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF),
+ .msg = "dma_rx_axi_wr_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF),
+ .msg = "dma_rx_axi_rd_err",
+ },
+};
+
+static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ struct device *dev = hisi_hba->dev;
+ u32 ent_msk, ent_tmp, irq_msk;
+ int phy_no = 0;
+
+ ent_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3);
+ ent_tmp = ent_msk;
+ ent_msk |= ENT_INT_SRC_MSK3_ENT95_MSK_MSK;
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, ent_msk);
+
+ irq_msk = (hisi_sas_read32(hisi_hba, HGC_INVLD_DQE_INFO) >>
+ HGC_INVLD_DQE_INFO_FB_CH3_OFF) & 0x1ff;
+
+ while (irq_msk) {
+ u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no,
+ CHL_INT0);
+ u32 irq_value1 = hisi_sas_phy_read32(hisi_hba, phy_no,
+ CHL_INT1);
+ u32 irq_value2 = hisi_sas_phy_read32(hisi_hba, phy_no,
+ CHL_INT2);
+
+ if ((irq_msk & (1 << phy_no)) && irq_value1) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port_ecc_axi_error); i++) {
+ const struct hisi_sas_hw_error *error =
+ &port_ecc_axi_error[i];
+
+ if (!(irq_value1 & error->irq_msk))
+ continue;
+
+ dev_warn(dev, "%s error (phy%d 0x%x) found!\n",
+ error->msg, phy_no, irq_value1);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ CHL_INT1, irq_value1);
+ }
+
+ if ((irq_msk & (1 << phy_no)) && irq_value2) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+
+ if (irq_value2 & BIT(CHL_INT2_SL_IDAF_TOUT_CONF_OFF)) {
+ dev_warn(dev, "phy%d identify timeout\n",
+ phy_no);
+ hisi_sas_notify_phy_event(phy,
+ HISI_PHYE_LINK_RESET);
+ }
+
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ CHL_INT2, irq_value2);
+ }
+
+ if ((irq_msk & (1 << phy_no)) && irq_value0) {
+ if (irq_value0 & CHL_INT0_SL_RX_BCST_ACK_MSK)
+ phy_bcast_v2_hw(phy_no, hisi_hba);
+
+ if (irq_value0 & CHL_INT0_PHY_RDY_MSK)
+ hisi_sas_phy_oob_ready(hisi_hba, phy_no);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ CHL_INT0, irq_value0
+ & (~CHL_INT0_HOTPLUG_TOUT_MSK)
+ & (~CHL_INT0_SL_PHY_ENABLE_MSK)
+ & (~CHL_INT0_NOT_RDY_MSK));
+ }
+ irq_msk &= ~(1 << phy_no);
+ phy_no++;
+ }
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, ent_tmp);
+
+ return IRQ_HANDLED;
+}
+
+static void
+one_bit_ecc_error_process_v2_hw(struct hisi_hba *hisi_hba, u32 irq_value)
+{
+ struct device *dev = hisi_hba->dev;
+ const struct hisi_sas_hw_error *ecc_error;
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(one_bit_ecc_errors); i++) {
+ ecc_error = &one_bit_ecc_errors[i];
+ if (irq_value & ecc_error->irq_msk) {
+ val = hisi_sas_read32(hisi_hba, ecc_error->reg);
+ val &= ecc_error->msk;
+ val >>= ecc_error->shift;
+ dev_warn(dev, "%s found: mem addr is 0x%08X\n",
+ ecc_error->msg, val);
+ }
+ }
+}
+
+static void multi_bit_ecc_error_process_v2_hw(struct hisi_hba *hisi_hba,
+ u32 irq_value)
+{
+ struct device *dev = hisi_hba->dev;
+ const struct hisi_sas_hw_error *ecc_error;
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(multi_bit_ecc_errors); i++) {
+ ecc_error = &multi_bit_ecc_errors[i];
+ if (irq_value & ecc_error->irq_msk) {
+ val = hisi_sas_read32(hisi_hba, ecc_error->reg);
+ val &= ecc_error->msk;
+ val >>= ecc_error->shift;
+ dev_err(dev, "%s (0x%x) found: mem addr is 0x%08X\n",
+ ecc_error->msg, irq_value, val);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+ }
+
+ return;
+}
+
+static irqreturn_t fatal_ecc_int_v2_hw(int irq_no, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ u32 irq_value, irq_msk;
+
+ irq_msk = hisi_sas_read32(hisi_hba, SAS_ECC_INTR_MSK);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, irq_msk | 0xffffffff);
+
+ irq_value = hisi_sas_read32(hisi_hba, SAS_ECC_INTR);
+ if (irq_value) {
+ one_bit_ecc_error_process_v2_hw(hisi_hba, irq_value);
+ multi_bit_ecc_error_process_v2_hw(hisi_hba, irq_value);
+ }
+
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR, irq_value);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, irq_msk);
+
+ return IRQ_HANDLED;
+}
+
+static const struct hisi_sas_hw_error axi_error[] = {
+ { .msk = BIT(0), .msg = "IOST_AXI_W_ERR" },
+ { .msk = BIT(1), .msg = "IOST_AXI_R_ERR" },
+ { .msk = BIT(2), .msg = "ITCT_AXI_W_ERR" },
+ { .msk = BIT(3), .msg = "ITCT_AXI_R_ERR" },
+ { .msk = BIT(4), .msg = "SATA_AXI_W_ERR" },
+ { .msk = BIT(5), .msg = "SATA_AXI_R_ERR" },
+ { .msk = BIT(6), .msg = "DQE_AXI_R_ERR" },
+ { .msk = BIT(7), .msg = "CQE_AXI_W_ERR" },
+ {}
+};
+
+static const struct hisi_sas_hw_error fifo_error[] = {
+ { .msk = BIT(8), .msg = "CQE_WINFO_FIFO" },
+ { .msk = BIT(9), .msg = "CQE_MSG_FIFIO" },
+ { .msk = BIT(10), .msg = "GETDQE_FIFO" },
+ { .msk = BIT(11), .msg = "CMDP_FIFO" },
+ { .msk = BIT(12), .msg = "AWTCTRL_FIFO" },
+ {}
+};
+
+static const struct hisi_sas_hw_error fatal_axi_errors[] = {
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_WP_DEPTH_OFF),
+ .msg = "write pointer and depth",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_IPTT_SLOT_NOMATCH_OFF),
+ .msg = "iptt no match slot",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_RP_DEPTH_OFF),
+ .msg = "read pointer and depth",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_AXI_OFF),
+ .reg = HGC_AXI_FIFO_ERR_INFO,
+ .sub = axi_error,
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_FIFO_OFF),
+ .reg = HGC_AXI_FIFO_ERR_INFO,
+ .sub = fifo_error,
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_LM_OFF),
+ .msg = "LM add/fetch list",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_ABT_OFF),
+ .msg = "SAS_HGC_ABT fetch LM list",
+ },
+};
+
+static irqreturn_t fatal_axi_int_v2_hw(int irq_no, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ u32 irq_value, irq_msk, err_value;
+ struct device *dev = hisi_hba->dev;
+ const struct hisi_sas_hw_error *axi_error;
+ int i;
+
+ irq_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, irq_msk | 0xfffffffe);
+
+ irq_value = hisi_sas_read32(hisi_hba, ENT_INT_SRC3);
+
+ for (i = 0; i < ARRAY_SIZE(fatal_axi_errors); i++) {
+ axi_error = &fatal_axi_errors[i];
+ if (!(irq_value & axi_error->irq_msk))
+ continue;
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC3,
+ 1 << axi_error->shift);
+ if (axi_error->sub) {
+ const struct hisi_sas_hw_error *sub = axi_error->sub;
+
+ err_value = hisi_sas_read32(hisi_hba, axi_error->reg);
+ for (; sub->msk || sub->msg; sub++) {
+ if (!(err_value & sub->msk))
+ continue;
+ dev_err(dev, "%s (0x%x) found!\n",
+ sub->msg, irq_value);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+ } else {
+ dev_err(dev, "%s (0x%x) found!\n",
+ axi_error->msg, irq_value);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+ }
+
+ if (irq_value & BIT(ENT_INT_SRC3_ITC_INT_OFF)) {
+ u32 reg_val = hisi_sas_read32(hisi_hba, ITCT_CLR);
+ u32 dev_id = reg_val & ITCT_DEV_MSK;
+ struct hisi_sas_device *sas_dev = &hisi_hba->devices[dev_id];
+
+ hisi_sas_write32(hisi_hba, ITCT_CLR, 0);
+ dev_dbg(dev, "clear ITCT ok\n");
+ complete(sas_dev->completion);
+ }
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC3, irq_value);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, irq_msk);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cq_thread_v2_hw(int irq_no, void *p)
+{
+ struct hisi_sas_cq *cq = p;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ struct hisi_sas_slot *slot;
+ struct hisi_sas_itct *itct;
+ struct hisi_sas_complete_v2_hdr *complete_queue;
+ u32 rd_point = cq->rd_point, wr_point, dev_id;
+ int queue = cq->id;
+
+ if (unlikely(hisi_hba->reject_stp_links_msk))
+ phys_try_accept_stp_links_v2_hw(hisi_hba);
+
+ complete_queue = hisi_hba->complete_hdr[queue];
+
+ wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR +
+ (0x14 * queue));
+
+ while (rd_point != wr_point) {
+ struct hisi_sas_complete_v2_hdr *complete_hdr;
+ int iptt;
+
+ complete_hdr = &complete_queue[rd_point];
+
+ /* Check for NCQ completion */
+ if (complete_hdr->act) {
+ u32 act_tmp = le32_to_cpu(complete_hdr->act);
+ int ncq_tag_count = ffs(act_tmp);
+ u32 dw1 = le32_to_cpu(complete_hdr->dw1);
+
+ dev_id = (dw1 & CMPLT_HDR_DEV_ID_MSK) >>
+ CMPLT_HDR_DEV_ID_OFF;
+ itct = &hisi_hba->itct[dev_id];
+
+ /* The NCQ tags are held in the itct header */
+ while (ncq_tag_count) {
+ __le64 *_ncq_tag = &itct->qw4_15[0], __ncq_tag;
+ u64 ncq_tag;
+
+ ncq_tag_count--;
+ __ncq_tag = _ncq_tag[ncq_tag_count / 5];
+ ncq_tag = le64_to_cpu(__ncq_tag);
+ iptt = (ncq_tag >> (ncq_tag_count % 5) * 12) &
+ 0xfff;
+
+ slot = &hisi_hba->slot_info[iptt];
+ slot->cmplt_queue_slot = rd_point;
+ slot->cmplt_queue = queue;
+ slot_complete_v2_hw(hisi_hba, slot);
+
+ act_tmp &= ~(1 << ncq_tag_count);
+ ncq_tag_count = ffs(act_tmp);
+ }
+ } else {
+ u32 dw1 = le32_to_cpu(complete_hdr->dw1);
+
+ iptt = dw1 & CMPLT_HDR_IPTT_MSK;
+ slot = &hisi_hba->slot_info[iptt];
+ slot->cmplt_queue_slot = rd_point;
+ slot->cmplt_queue = queue;
+ slot_complete_v2_hw(hisi_hba, slot);
+ }
+
+ if (++rd_point >= HISI_SAS_QUEUE_SLOTS)
+ rd_point = 0;
+ }
+
+ /* update rd_point */
+ cq->rd_point = rd_point;
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p)
+{
+ struct hisi_sas_cq *cq = p;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ int queue = cq->id;
+
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t sata_int_v2_hw(int irq_no, void *p)
+{
+ struct hisi_sas_phy *phy = p;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct device *dev = hisi_hba->dev;
+ struct hisi_sas_initial_fis *initial_fis;
+ struct dev_to_host_fis *fis;
+ u32 ent_tmp, ent_msk, ent_int, port_id, link_rate, hard_phy_linkrate;
+ irqreturn_t res = IRQ_HANDLED;
+ u8 attached_sas_addr[SAS_ADDR_SIZE] = {0};
+ int phy_no, offset;
+
+ timer_delete(&phy->timer);
+
+ phy_no = sas_phy->id;
+ initial_fis = &hisi_hba->initial_fis[phy_no];
+ fis = &initial_fis->fis;
+
+ offset = 4 * (phy_no / 4);
+ ent_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK1 + offset);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1 + offset,
+ ent_msk | 1 << ((phy_no % 4) * 8));
+
+ ent_int = hisi_sas_read32(hisi_hba, ENT_INT_SRC1 + offset);
+ ent_tmp = ent_int & (1 << (ENT_INT_SRC1_D2H_FIS_CH1_OFF *
+ (phy_no % 4)));
+ ent_int >>= ENT_INT_SRC1_D2H_FIS_CH1_OFF * (phy_no % 4);
+ if ((ent_int & ENT_INT_SRC1_D2H_FIS_CH0_MSK) == 0) {
+ dev_warn(dev, "sata int: phy%d did not receive FIS\n", phy_no);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ /* check ERR bit of Status Register */
+ if (fis->status & ATA_ERR) {
+ dev_warn(dev, "sata int: phy%d FIS status: 0x%x\n", phy_no,
+ fis->status);
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ if (unlikely(phy_no == 8)) {
+ u32 port_state = hisi_sas_read32(hisi_hba, PORT_STATE);
+
+ port_id = (port_state & PORT_STATE_PHY8_PORT_NUM_MSK) >>
+ PORT_STATE_PHY8_PORT_NUM_OFF;
+ link_rate = (port_state & PORT_STATE_PHY8_CONN_RATE_MSK) >>
+ PORT_STATE_PHY8_CONN_RATE_OFF;
+ } else {
+ port_id = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+ port_id = (port_id >> (4 * phy_no)) & 0xf;
+ link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE);
+ link_rate = (link_rate >> (phy_no * 4)) & 0xf;
+ }
+
+ if (port_id == 0xf) {
+ dev_err(dev, "sata int: phy%d invalid portid\n", phy_no);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ sas_phy->linkrate = link_rate;
+ hard_phy_linkrate = hisi_sas_phy_read32(hisi_hba, phy_no,
+ HARD_PHY_LINKRATE);
+ phy->maximum_linkrate = hard_phy_linkrate & 0xf;
+ phy->minimum_linkrate = (hard_phy_linkrate >> 4) & 0xf;
+
+ sas_phy->oob_mode = SATA_OOB_MODE;
+ /* Make up some unique SAS address */
+ attached_sas_addr[0] = 0x50;
+ attached_sas_addr[6] = hisi_hba->shost->host_no;
+ attached_sas_addr[7] = phy_no;
+ memcpy(sas_phy->attached_sas_addr, attached_sas_addr, SAS_ADDR_SIZE);
+ memcpy(sas_phy->frame_rcvd, fis, sizeof(struct dev_to_host_fis));
+ dev_info(dev, "sata int phyup: phy%d link_rate=%d\n", phy_no, link_rate);
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+ phy->port_id = port_id;
+ phy->phy_type |= PORT_TYPE_SATA;
+ phy->phy_attached = 1;
+ phy->identify.device_type = SAS_SATA_DEV;
+ phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);
+
+ if (phy->reset_completion)
+ complete(phy->reset_completion);
+end:
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC1 + offset, ent_tmp);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1 + offset, ent_msk);
+
+ return res;
+}
+
+static irq_handler_t phy_interrupts[HISI_SAS_PHY_INT_NR] = {
+ int_phy_updown_v2_hw,
+ int_chnl_int_v2_hw,
+};
+
+static irq_handler_t fatal_interrupts[HISI_SAS_FATAL_INT_NR] = {
+ fatal_ecc_int_v2_hw,
+ fatal_axi_int_v2_hw
+};
+
+#define CQ0_IRQ_INDEX (96)
+
+static int hisi_sas_v2_interrupt_preinit(struct hisi_hba *hisi_hba)
+{
+ struct platform_device *pdev = hisi_hba->platform_dev;
+ struct Scsi_Host *shost = hisi_hba->shost;
+ struct irq_affinity desc = {
+ .pre_vectors = CQ0_IRQ_INDEX,
+ .post_vectors = 16,
+ };
+ int resv = desc.pre_vectors + desc.post_vectors, minvec = resv + 1, nvec;
+
+ nvec = devm_platform_get_irqs_affinity(pdev, &desc, minvec, 128,
+ &hisi_hba->irq_map);
+ if (nvec < 0)
+ return nvec;
+
+ shost->nr_hw_queues = hisi_hba->cq_nvecs = nvec - resv;
+
+ return 0;
+}
+
+/*
+ * There is a limitation in the hip06 chipset that we need
+ * to map in all mbigen interrupts, even if they are not used.
+ */
+static int interrupt_init_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct platform_device *pdev = hisi_hba->platform_dev;
+ struct device *dev = &pdev->dev;
+ int irq, rc = 0;
+ int i, phy_no, fatal_no, queue_no;
+
+ for (i = 0; i < HISI_SAS_PHY_INT_NR; i++) {
+ irq = hisi_hba->irq_map[i + 1]; /* Phy up/down is irq1 */
+ rc = devm_request_irq(dev, irq, phy_interrupts[i], 0,
+ DRV_NAME " phy", hisi_hba);
+ if (rc) {
+ dev_err(dev, "irq init: could not request phy interrupt %d, rc=%d\n",
+ irq, rc);
+ rc = -ENOENT;
+ goto err_out;
+ }
+ }
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+
+ irq = hisi_hba->irq_map[phy_no + 72];
+ rc = devm_request_irq(dev, irq, sata_int_v2_hw, 0,
+ DRV_NAME " sata", phy);
+ if (rc) {
+ dev_err(dev, "irq init: could not request sata interrupt %d, rc=%d\n",
+ irq, rc);
+ rc = -ENOENT;
+ goto err_out;
+ }
+ }
+
+ for (fatal_no = 0; fatal_no < HISI_SAS_FATAL_INT_NR; fatal_no++) {
+ irq = hisi_hba->irq_map[fatal_no + 81];
+ rc = devm_request_irq(dev, irq, fatal_interrupts[fatal_no], 0,
+ DRV_NAME " fatal", hisi_hba);
+ if (rc) {
+ dev_err(dev, "irq init: could not request fatal interrupt %d, rc=%d\n",
+ irq, rc);
+ rc = -ENOENT;
+ goto err_out;
+ }
+ }
+
+ for (queue_no = 0; queue_no < hisi_hba->cq_nvecs; queue_no++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[queue_no];
+
+ cq->irq_no = hisi_hba->irq_map[queue_no + 96];
+ rc = devm_request_threaded_irq(dev, cq->irq_no,
+ cq_interrupt_v2_hw,
+ cq_thread_v2_hw, IRQF_ONESHOT,
+ DRV_NAME " cq", cq);
+ if (rc) {
+ dev_err(dev, "irq init: could not request cq interrupt %d, rc=%d\n",
+ cq->irq_no, rc);
+ rc = -ENOENT;
+ goto err_out;
+ }
+ cq->irq_mask = irq_get_affinity_mask(cq->irq_no);
+ }
+err_out:
+ return rc;
+}
+
+static int hisi_sas_v2_init(struct hisi_hba *hisi_hba)
+{
+ int rc;
+
+ memset(hisi_hba->sata_dev_bitmap, 0, sizeof(hisi_hba->sata_dev_bitmap));
+
+ rc = hw_init_v2_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ rc = interrupt_init_v2_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static void interrupt_disable_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct platform_device *pdev = hisi_hba->platform_dev;
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ hisi_sas_write32(hisi_hba, OQ0_INT_SRC_MSK + 0x4 * i, 0x1);
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0xffffffff);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0xffffffff);
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0xffffffff);
+ }
+
+ for (i = 0; i < 128; i++)
+ synchronize_irq(platform_get_irq(pdev, i));
+}
+
+
+static u32 get_phys_state_v2_hw(struct hisi_hba *hisi_hba)
+{
+ return hisi_sas_read32(hisi_hba, PHY_STATE);
+}
+
+static int soft_reset_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ int rc, cnt;
+
+ interrupt_disable_v2_hw(hisi_hba);
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0);
+
+ hisi_sas_stop_phys(hisi_hba);
+
+ mdelay(10);
+
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + AM_CTRL_GLOBAL, 0x1);
+
+ /* wait until bus idle */
+ cnt = 0;
+ while (1) {
+ u32 status = hisi_sas_read32_relaxed(hisi_hba,
+ AXI_MASTER_CFG_BASE + AM_CURR_TRANS_RETURN);
+
+ if (status == 0x3)
+ break;
+
+ udelay(10);
+ if (cnt++ > 10) {
+ dev_err(dev, "wait axi bus state to idle timeout!\n");
+ return -1;
+ }
+ }
+
+ hisi_sas_init_mem(hisi_hba);
+
+ rc = hw_init_v2_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ phys_reject_stp_links_v2_hw(hisi_hba);
+
+ return 0;
+}
+
+static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data)
+{
+ struct device *dev = hisi_hba->dev;
+ int phy_no, count;
+
+ if (!hisi_hba->sgpio_regs)
+ return -EOPNOTSUPP;
+
+ switch (reg_type) {
+ case SAS_GPIO_REG_TX:
+ count = reg_count * 4;
+ count = min(count, hisi_hba->n_phy);
+
+ for (phy_no = 0; phy_no < count; phy_no++) {
+ /*
+ * GPIO_TX[n] register has the highest numbered drive
+ * of the four in the first byte and the lowest
+ * numbered drive in the fourth byte.
+ * See SFF-8485 Rev. 0.7 Table 24.
+ */
+ void __iomem *reg_addr = hisi_hba->sgpio_regs +
+ reg_index * 4 + phy_no;
+ int data_idx = phy_no + 3 - (phy_no % 4) * 2;
+
+ writeb(write_data[data_idx], reg_addr);
+ }
+
+ break;
+ default:
+ dev_err(dev, "write gpio: unsupported or bad reg type %d\n",
+ reg_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
+ int delay_ms, int timeout_ms)
+{
+ struct device *dev = hisi_hba->dev;
+ int entries, entries_old = 0, time;
+
+ for (time = 0; time < timeout_ms; time += delay_ms) {
+ entries = hisi_sas_read32(hisi_hba, CQE_SEND_CNT);
+ if (entries == entries_old)
+ break;
+
+ entries_old = entries;
+ msleep(delay_ms);
+ }
+
+ if (time >= timeout_ms) {
+ dev_dbg(dev, "Wait commands complete timeout!\n");
+ return;
+ }
+
+ dev_dbg(dev, "wait commands complete %dms\n", time);
+
+}
+
+static struct attribute *host_v2_hw_attrs[] = {
+ &dev_attr_phy_event_threshold.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(host_v2_hw);
+
+static const struct attribute_group *sdev_groups_v2_hw[] = {
+ &sas_ata_sdev_attr_group,
+ NULL
+};
+
+static void map_queues_v2_hw(struct Scsi_Host *shost)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
+ const struct cpumask *mask;
+ unsigned int queue, cpu;
+
+ for (queue = 0; queue < qmap->nr_queues; queue++) {
+ mask = irq_get_affinity_mask(hisi_hba->irq_map[96 + queue]);
+ if (!mask)
+ continue;
+
+ for_each_cpu(cpu, mask)
+ qmap->mq_map[cpu] = qmap->queue_offset + queue;
+ }
+}
+
+static int check_fw_info_v2_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+
+ if (hisi_hba->n_phy < 0 || hisi_hba->n_phy > 9) {
+ dev_err(dev, "invalid phy number from FW\n");
+ return -EINVAL;
+ }
+
+ if (hisi_hba->queue_count < 0 || hisi_hba->queue_count > 16) {
+ dev_err(dev, "invalid queue count from FW\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct scsi_host_template sht_v2_hw = {
+ LIBSAS_SHT_BASE_NO_SLAVE_INIT
+ .sdev_configure = hisi_sas_sdev_configure,
+ .scan_finished = hisi_sas_scan_finished,
+ .scan_start = hisi_sas_scan_start,
+ .sg_tablesize = HISI_SAS_SGE_PAGE_CNT,
+ .sdev_init = hisi_sas_sdev_init,
+ .shost_groups = host_v2_hw_groups,
+ .sdev_groups = sdev_groups_v2_hw,
+ .host_reset = hisi_sas_host_reset,
+ .map_queues = map_queues_v2_hw,
+ .host_tagset = 1,
+};
+
+static const struct hisi_sas_hw hisi_sas_v2_hw = {
+ .hw_init = hisi_sas_v2_init,
+ .fw_info_check = check_fw_info_v2_hw,
+ .interrupt_preinit = hisi_sas_v2_interrupt_preinit,
+ .setup_itct = setup_itct_v2_hw,
+ .slot_index_alloc = slot_index_alloc_quirk_v2_hw,
+ .alloc_dev = alloc_dev_quirk_v2_hw,
+ .sl_notify_ssp = sl_notify_ssp_v2_hw,
+ .get_wideport_bitmap = get_wideport_bitmap_v2_hw,
+ .clear_itct = clear_itct_v2_hw,
+ .free_device = free_device_v2_hw,
+ .prep_smp = prep_smp_v2_hw,
+ .prep_ssp = prep_ssp_v2_hw,
+ .prep_stp = prep_ata_v2_hw,
+ .prep_abort = prep_abort_v2_hw,
+ .start_delivery = start_delivery_v2_hw,
+ .phys_init = phys_init_v2_hw,
+ .phy_start = start_phy_v2_hw,
+ .phy_disable = disable_phy_v2_hw,
+ .phy_hard_reset = phy_hard_reset_v2_hw,
+ .get_events = phy_get_events_v2_hw,
+ .phy_set_linkrate = phy_set_linkrate_v2_hw,
+ .phy_get_max_linkrate = phy_get_max_linkrate_v2_hw,
+ .complete_hdr_size = sizeof(struct hisi_sas_complete_v2_hdr),
+ .soft_reset = soft_reset_v2_hw,
+ .get_phys_state = get_phys_state_v2_hw,
+ .write_gpio = write_gpio_v2_hw,
+ .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v2_hw,
+ .sht = &sht_v2_hw,
+};
+
+static int hisi_sas_v2_probe(struct platform_device *pdev)
+{
+ return hisi_sas_probe(pdev, &hisi_sas_v2_hw);
+}
+
+static const struct of_device_id sas_v2_of_match[] = {
+ { .compatible = "hisilicon,hip06-sas-v2",},
+ { .compatible = "hisilicon,hip07-sas-v2",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sas_v2_of_match);
+
+static const struct acpi_device_id sas_v2_acpi_match[] = {
+ { "HISI0162", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(acpi, sas_v2_acpi_match);
+
+static struct platform_driver hisi_sas_v2_driver = {
+ .probe = hisi_sas_v2_probe,
+ .remove = hisi_sas_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = sas_v2_of_match,
+ .acpi_match_table = sas_v2_acpi_match,
+ },
+};
+
+module_platform_driver(hisi_sas_v2_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
+MODULE_DESCRIPTION("HISILICON SAS controller v2 hw driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
new file mode 100644
index 000000000000..2f9e01717ef3
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -0,0 +1,5329 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017 Hisilicon Limited.
+ */
+
+#include <linux/sched/clock.h>
+#include "hisi_sas.h"
+#define DRV_NAME "hisi_sas_v3_hw"
+
+/* global registers need init */
+#define DLVRY_QUEUE_ENABLE 0x0
+#define IOST_BASE_ADDR_LO 0x8
+#define IOST_BASE_ADDR_HI 0xc
+#define ITCT_BASE_ADDR_LO 0x10
+#define ITCT_BASE_ADDR_HI 0x14
+#define IO_BROKEN_MSG_ADDR_LO 0x18
+#define IO_BROKEN_MSG_ADDR_HI 0x1c
+#define PHY_CONTEXT 0x20
+#define PHY_STATE 0x24
+#define PHY_PORT_NUM_MA 0x28
+#define PHY_CONN_RATE 0x30
+#define ITCT_CLR 0x44
+#define ITCT_CLR_EN_OFF 16
+#define ITCT_CLR_EN_MSK (0x1 << ITCT_CLR_EN_OFF)
+#define ITCT_DEV_OFF 0
+#define ITCT_DEV_MSK (0x7ff << ITCT_DEV_OFF)
+#define SAS_AXI_USER3 0x50
+#define IO_SATA_BROKEN_MSG_ADDR_LO 0x58
+#define IO_SATA_BROKEN_MSG_ADDR_HI 0x5c
+#define SATA_INITI_D2H_STORE_ADDR_LO 0x60
+#define SATA_INITI_D2H_STORE_ADDR_HI 0x64
+#define CFG_MAX_TAG 0x68
+#define TRANS_LOCK_ICT_TIME 0X70
+#define HGC_SAS_TX_OPEN_FAIL_RETRY_CTRL 0x84
+#define HGC_SAS_TXFAIL_RETRY_CTRL 0x88
+#define HGC_GET_ITV_TIME 0x90
+#define DEVICE_MSG_WORK_MODE 0x94
+#define OPENA_WT_CONTI_TIME 0x9c
+#define I_T_NEXUS_LOSS_TIME 0xa0
+#define MAX_CON_TIME_LIMIT_TIME 0xa4
+#define BUS_INACTIVE_LIMIT_TIME 0xa8
+#define REJECT_TO_OPEN_LIMIT_TIME 0xac
+#define CQ_INT_CONVERGE_EN 0xb0
+#define CFG_AGING_TIME 0xbc
+#define HGC_DFX_CFG2 0xc0
+#define CFG_ICT_TIMER_STEP_TRSH 0xc8
+#define CFG_ABT_SET_QUERY_IPTT 0xd4
+#define CFG_SET_ABORTED_IPTT_OFF 0
+#define CFG_SET_ABORTED_IPTT_MSK (0xfff << CFG_SET_ABORTED_IPTT_OFF)
+#define CFG_SET_ABORTED_EN_OFF 12
+#define CFG_ABT_SET_IPTT_DONE 0xd8
+#define CFG_ABT_SET_IPTT_DONE_OFF 0
+#define HGC_IOMB_PROC1_STATUS 0x104
+#define HGC_LM_DFX_STATUS2 0x128
+#define HGC_LM_DFX_STATUS2_IOSTLIST_OFF 0
+#define HGC_LM_DFX_STATUS2_IOSTLIST_MSK (0xfff << \
+ HGC_LM_DFX_STATUS2_IOSTLIST_OFF)
+#define HGC_LM_DFX_STATUS2_ITCTLIST_OFF 12
+#define HGC_LM_DFX_STATUS2_ITCTLIST_MSK (0x7ff << \
+ HGC_LM_DFX_STATUS2_ITCTLIST_OFF)
+#define HGC_CQE_ECC_ADDR 0x13c
+#define HGC_CQE_ECC_1B_ADDR_OFF 0
+#define HGC_CQE_ECC_1B_ADDR_MSK (0x3f << HGC_CQE_ECC_1B_ADDR_OFF)
+#define HGC_CQE_ECC_MB_ADDR_OFF 8
+#define HGC_CQE_ECC_MB_ADDR_MSK (0x3f << HGC_CQE_ECC_MB_ADDR_OFF)
+#define HGC_IOST_ECC_ADDR 0x140
+#define HGC_IOST_ECC_1B_ADDR_OFF 0
+#define HGC_IOST_ECC_1B_ADDR_MSK (0x3ff << HGC_IOST_ECC_1B_ADDR_OFF)
+#define HGC_IOST_ECC_MB_ADDR_OFF 16
+#define HGC_IOST_ECC_MB_ADDR_MSK (0x3ff << HGC_IOST_ECC_MB_ADDR_OFF)
+#define HGC_DQE_ECC_ADDR 0x144
+#define HGC_DQE_ECC_1B_ADDR_OFF 0
+#define HGC_DQE_ECC_1B_ADDR_MSK (0xfff << HGC_DQE_ECC_1B_ADDR_OFF)
+#define HGC_DQE_ECC_MB_ADDR_OFF 16
+#define HGC_DQE_ECC_MB_ADDR_MSK (0xfff << HGC_DQE_ECC_MB_ADDR_OFF)
+#define CHNL_INT_STATUS 0x148
+#define TAB_DFX 0x14c
+#define HGC_ITCT_ECC_ADDR 0x150
+#define HGC_ITCT_ECC_1B_ADDR_OFF 0
+#define HGC_ITCT_ECC_1B_ADDR_MSK (0x3ff << \
+ HGC_ITCT_ECC_1B_ADDR_OFF)
+#define HGC_ITCT_ECC_MB_ADDR_OFF 16
+#define HGC_ITCT_ECC_MB_ADDR_MSK (0x3ff << \
+ HGC_ITCT_ECC_MB_ADDR_OFF)
+#define HGC_AXI_FIFO_ERR_INFO 0x154
+#define AXI_ERR_INFO_OFF 0
+#define AXI_ERR_INFO_MSK (0xff << AXI_ERR_INFO_OFF)
+#define FIFO_ERR_INFO_OFF 8
+#define FIFO_ERR_INFO_MSK (0xff << FIFO_ERR_INFO_OFF)
+#define TAB_RD_TYPE 0x15c
+#define INT_COAL_EN 0x19c
+#define OQ_INT_COAL_TIME 0x1a0
+#define OQ_INT_COAL_CNT 0x1a4
+#define ENT_INT_COAL_TIME 0x1a8
+#define ENT_INT_COAL_CNT 0x1ac
+#define OQ_INT_SRC 0x1b0
+#define OQ_INT_SRC_MSK 0x1b4
+#define ENT_INT_SRC1 0x1b8
+#define ENT_INT_SRC1_D2H_FIS_CH0_OFF 0
+#define ENT_INT_SRC1_D2H_FIS_CH0_MSK (0x1 << ENT_INT_SRC1_D2H_FIS_CH0_OFF)
+#define ENT_INT_SRC1_D2H_FIS_CH1_OFF 8
+#define ENT_INT_SRC1_D2H_FIS_CH1_MSK (0x1 << ENT_INT_SRC1_D2H_FIS_CH1_OFF)
+#define ENT_INT_SRC2 0x1bc
+#define ENT_INT_SRC3 0x1c0
+#define ENT_INT_SRC3_WP_DEPTH_OFF 8
+#define ENT_INT_SRC3_IPTT_SLOT_NOMATCH_OFF 9
+#define ENT_INT_SRC3_RP_DEPTH_OFF 10
+#define ENT_INT_SRC3_AXI_OFF 11
+#define ENT_INT_SRC3_FIFO_OFF 12
+#define ENT_INT_SRC3_LM_OFF 14
+#define ENT_INT_SRC3_ITC_INT_OFF 15
+#define ENT_INT_SRC3_ITC_INT_MSK (0x1 << ENT_INT_SRC3_ITC_INT_OFF)
+#define ENT_INT_SRC3_ABT_OFF 16
+#define ENT_INT_SRC3_DQE_POISON_OFF 18
+#define ENT_INT_SRC3_IOST_POISON_OFF 19
+#define ENT_INT_SRC3_ITCT_POISON_OFF 20
+#define ENT_INT_SRC3_ITCT_NCQ_POISON_OFF 21
+#define ENT_INT_SRC_MSK1 0x1c4
+#define ENT_INT_SRC_MSK2 0x1c8
+#define ENT_INT_SRC_MSK3 0x1cc
+#define ENT_INT_SRC_MSK3_ENT95_MSK_OFF 31
+#define CHNL_PHYUPDOWN_INT_MSK 0x1d0
+#define CHNL_ENT_INT_MSK 0x1d4
+#define HGC_COM_INT_MSK 0x1d8
+#define ENT_INT_SRC_MSK3_ENT95_MSK_MSK (0x1 << ENT_INT_SRC_MSK3_ENT95_MSK_OFF)
+#define SAS_ECC_INTR 0x1e8
+#define SAS_ECC_INTR_DQE_ECC_1B_OFF 0
+#define SAS_ECC_INTR_DQE_ECC_MB_OFF 1
+#define SAS_ECC_INTR_IOST_ECC_1B_OFF 2
+#define SAS_ECC_INTR_IOST_ECC_MB_OFF 3
+#define SAS_ECC_INTR_ITCT_ECC_1B_OFF 4
+#define SAS_ECC_INTR_ITCT_ECC_MB_OFF 5
+#define SAS_ECC_INTR_ITCTLIST_ECC_1B_OFF 6
+#define SAS_ECC_INTR_ITCTLIST_ECC_MB_OFF 7
+#define SAS_ECC_INTR_IOSTLIST_ECC_1B_OFF 8
+#define SAS_ECC_INTR_IOSTLIST_ECC_MB_OFF 9
+#define SAS_ECC_INTR_CQE_ECC_1B_OFF 10
+#define SAS_ECC_INTR_CQE_ECC_MB_OFF 11
+#define SAS_ECC_INTR_NCQ_MEM0_ECC_1B_OFF 12
+#define SAS_ECC_INTR_NCQ_MEM0_ECC_MB_OFF 13
+#define SAS_ECC_INTR_NCQ_MEM1_ECC_1B_OFF 14
+#define SAS_ECC_INTR_NCQ_MEM1_ECC_MB_OFF 15
+#define SAS_ECC_INTR_NCQ_MEM2_ECC_1B_OFF 16
+#define SAS_ECC_INTR_NCQ_MEM2_ECC_MB_OFF 17
+#define SAS_ECC_INTR_NCQ_MEM3_ECC_1B_OFF 18
+#define SAS_ECC_INTR_NCQ_MEM3_ECC_MB_OFF 19
+#define SAS_ECC_INTR_OOO_RAM_ECC_1B_OFF 20
+#define SAS_ECC_INTR_OOO_RAM_ECC_MB_OFF 21
+#define SAS_ECC_INTR_MSK 0x1ec
+#define HGC_ERR_STAT_EN 0x238
+#define CQE_SEND_CNT 0x248
+#define DLVRY_Q_0_BASE_ADDR_LO 0x260
+#define DLVRY_Q_0_BASE_ADDR_HI 0x264
+#define DLVRY_Q_0_DEPTH 0x268
+#define DLVRY_Q_0_WR_PTR 0x26c
+#define DLVRY_Q_0_RD_PTR 0x270
+#define HYPER_STREAM_ID_EN_CFG 0xc80
+#define OQ0_INT_SRC_MSK 0xc90
+#define COMPL_Q_0_BASE_ADDR_LO 0x4e0
+#define COMPL_Q_0_BASE_ADDR_HI 0x4e4
+#define COMPL_Q_0_DEPTH 0x4e8
+#define COMPL_Q_0_WR_PTR 0x4ec
+#define COMPL_Q_0_RD_PTR 0x4f0
+#define HGC_RXM_DFX_STATUS14 0xae8
+#define HGC_RXM_DFX_STATUS14_MEM0_OFF 0
+#define HGC_RXM_DFX_STATUS14_MEM0_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS14_MEM0_OFF)
+#define HGC_RXM_DFX_STATUS14_MEM1_OFF 9
+#define HGC_RXM_DFX_STATUS14_MEM1_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS14_MEM1_OFF)
+#define HGC_RXM_DFX_STATUS14_MEM2_OFF 18
+#define HGC_RXM_DFX_STATUS14_MEM2_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS14_MEM2_OFF)
+#define HGC_RXM_DFX_STATUS15 0xaec
+#define HGC_RXM_DFX_STATUS15_MEM3_OFF 0
+#define HGC_RXM_DFX_STATUS15_MEM3_MSK (0x1ff << \
+ HGC_RXM_DFX_STATUS15_MEM3_OFF)
+#define AWQOS_AWCACHE_CFG 0xc84
+#define ARQOS_ARCACHE_CFG 0xc88
+#define HILINK_ERR_DFX 0xe04
+#define SAS_GPIO_CFG_0 0x1000
+#define SAS_GPIO_CFG_1 0x1004
+#define SAS_GPIO_TX_0_1 0x1040
+#define SAS_CFG_DRIVE_VLD 0x1070
+
+/* phy registers requiring init */
+#define PORT_BASE (0x2000)
+#define PHY_CFG (PORT_BASE + 0x0)
+#define HARD_PHY_LINKRATE (PORT_BASE + 0x4)
+#define PHY_CFG_ENA_OFF 0
+#define PHY_CFG_ENA_MSK (0x1 << PHY_CFG_ENA_OFF)
+#define PHY_CFG_DC_OPT_OFF 2
+#define PHY_CFG_DC_OPT_MSK (0x1 << PHY_CFG_DC_OPT_OFF)
+#define PHY_CFG_PHY_RST_OFF 3
+#define PHY_CFG_PHY_RST_MSK (0x1 << PHY_CFG_PHY_RST_OFF)
+#define PROG_PHY_LINK_RATE (PORT_BASE + 0x8)
+#define CFG_PROG_PHY_LINK_RATE_OFF 0
+#define CFG_PROG_PHY_LINK_RATE_MSK (0xff << CFG_PROG_PHY_LINK_RATE_OFF)
+#define CFG_PROG_OOB_PHY_LINK_RATE_OFF 8
+#define CFG_PROG_OOB_PHY_LINK_RATE_MSK (0xf << CFG_PROG_OOB_PHY_LINK_RATE_OFF)
+#define PHY_CTRL (PORT_BASE + 0x14)
+#define PHY_CTRL_RESET_OFF 0
+#define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF)
+#define CMD_HDR_PIR_OFF 8
+#define CMD_HDR_PIR_MSK (0x1 << CMD_HDR_PIR_OFF)
+#define SERDES_CFG (PORT_BASE + 0x1c)
+#define CFG_ALOS_CHK_DISABLE_OFF 9
+#define CFG_ALOS_CHK_DISABLE_MSK (0x1 << CFG_ALOS_CHK_DISABLE_OFF)
+#define SAS_PHY_BIST_CTRL (PORT_BASE + 0x2c)
+#define CFG_BIST_MODE_SEL_OFF 0
+#define CFG_BIST_MODE_SEL_MSK (0xf << CFG_BIST_MODE_SEL_OFF)
+#define CFG_LOOP_TEST_MODE_OFF 14
+#define CFG_LOOP_TEST_MODE_MSK (0x3 << CFG_LOOP_TEST_MODE_OFF)
+#define CFG_RX_BIST_EN_OFF 16
+#define CFG_RX_BIST_EN_MSK (0x1 << CFG_RX_BIST_EN_OFF)
+#define CFG_TX_BIST_EN_OFF 17
+#define CFG_TX_BIST_EN_MSK (0x1 << CFG_TX_BIST_EN_OFF)
+#define CFG_BIST_TEST_OFF 18
+#define CFG_BIST_TEST_MSK (0x1 << CFG_BIST_TEST_OFF)
+#define SAS_PHY_BIST_CODE (PORT_BASE + 0x30)
+#define SAS_PHY_BIST_CODE1 (PORT_BASE + 0x34)
+#define SAS_BIST_ERR_CNT (PORT_BASE + 0x38)
+#define SL_CFG (PORT_BASE + 0x84)
+#define AIP_LIMIT (PORT_BASE + 0x90)
+#define SL_CONTROL (PORT_BASE + 0x94)
+#define SL_CONTROL_NOTIFY_EN_OFF 0
+#define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF)
+#define SL_CTA_OFF 17
+#define SL_CTA_MSK (0x1 << SL_CTA_OFF)
+#define RX_PRIMS_STATUS (PORT_BASE + 0x98)
+#define RX_BCAST_CHG_OFF 1
+#define RX_BCAST_CHG_MSK (0x1 << RX_BCAST_CHG_OFF)
+#define TX_ID_DWORD0 (PORT_BASE + 0x9c)
+#define TX_ID_DWORD1 (PORT_BASE + 0xa0)
+#define TX_ID_DWORD2 (PORT_BASE + 0xa4)
+#define TX_ID_DWORD3 (PORT_BASE + 0xa8)
+#define TX_ID_DWORD4 (PORT_BASE + 0xaC)
+#define TX_ID_DWORD5 (PORT_BASE + 0xb0)
+#define TX_ID_DWORD6 (PORT_BASE + 0xb4)
+#define TXID_AUTO (PORT_BASE + 0xb8)
+#define CT3_OFF 1
+#define CT3_MSK (0x1 << CT3_OFF)
+#define TX_HARDRST_OFF 2
+#define TX_HARDRST_MSK (0x1 << TX_HARDRST_OFF)
+#define RX_IDAF_DWORD0 (PORT_BASE + 0xc4)
+#define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc)
+#define STP_LINK_TIMER (PORT_BASE + 0x120)
+#define STP_LINK_TIMEOUT_STATE (PORT_BASE + 0x124)
+#define CON_CFG_DRIVER (PORT_BASE + 0x130)
+#define SAS_SSP_CON_TIMER_CFG (PORT_BASE + 0x134)
+#define SAS_SMP_CON_TIMER_CFG (PORT_BASE + 0x138)
+#define SAS_STP_CON_TIMER_CFG (PORT_BASE + 0x13c)
+#define CHL_INT0 (PORT_BASE + 0x1b4)
+#define CHL_INT0_HOTPLUG_TOUT_OFF 0
+#define CHL_INT0_HOTPLUG_TOUT_MSK (0x1 << CHL_INT0_HOTPLUG_TOUT_OFF)
+#define CHL_INT0_SL_RX_BCST_ACK_OFF 1
+#define CHL_INT0_SL_RX_BCST_ACK_MSK (0x1 << CHL_INT0_SL_RX_BCST_ACK_OFF)
+#define CHL_INT0_SL_PHY_ENABLE_OFF 2
+#define CHL_INT0_SL_PHY_ENABLE_MSK (0x1 << CHL_INT0_SL_PHY_ENABLE_OFF)
+#define CHL_INT0_NOT_RDY_OFF 4
+#define CHL_INT0_NOT_RDY_MSK (0x1 << CHL_INT0_NOT_RDY_OFF)
+#define CHL_INT0_PHY_RDY_OFF 5
+#define CHL_INT0_PHY_RDY_MSK (0x1 << CHL_INT0_PHY_RDY_OFF)
+#define CHL_INT1 (PORT_BASE + 0x1b8)
+#define CHL_INT1_DMAC_TX_ECC_MB_ERR_OFF 15
+#define CHL_INT1_DMAC_TX_ECC_1B_ERR_OFF 16
+#define CHL_INT1_DMAC_RX_ECC_MB_ERR_OFF 17
+#define CHL_INT1_DMAC_RX_ECC_1B_ERR_OFF 18
+#define CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF 19
+#define CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF 20
+#define CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF 21
+#define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF 22
+#define CHL_INT1_DMAC_TX_FIFO_ERR_OFF 23
+#define CHL_INT1_DMAC_RX_FIFO_ERR_OFF 24
+#define CHL_INT1_DMAC_TX_AXI_RUSER_ERR_OFF 26
+#define CHL_INT1_DMAC_RX_AXI_RUSER_ERR_OFF 27
+#define CHL_INT2 (PORT_BASE + 0x1bc)
+#define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0
+#define CHL_INT2_RX_DISP_ERR_OFF 28
+#define CHL_INT2_RX_CODE_ERR_OFF 29
+#define CHL_INT2_RX_INVLD_DW_OFF 30
+#define CHL_INT2_STP_LINK_TIMEOUT_OFF 31
+#define CHL_INT0_MSK (PORT_BASE + 0x1c0)
+#define CHL_INT1_MSK (PORT_BASE + 0x1c4)
+#define CHL_INT2_MSK (PORT_BASE + 0x1c8)
+#define SAS_EC_INT_COAL_TIME (PORT_BASE + 0x1cc)
+#define CHL_INT_COAL_EN (PORT_BASE + 0x1d0)
+#define SAS_RX_TRAIN_TIMER (PORT_BASE + 0x2a4)
+#define PHY_CTRL_RDY_MSK (PORT_BASE + 0x2b0)
+#define PHYCTRL_NOT_RDY_MSK (PORT_BASE + 0x2b4)
+#define PHYCTRL_DWS_RESET_MSK (PORT_BASE + 0x2b8)
+#define PHYCTRL_PHY_ENA_MSK (PORT_BASE + 0x2bc)
+#define SL_RX_BCAST_CHK_MSK (PORT_BASE + 0x2c0)
+#define PHYCTRL_OOB_RESTART_MSK (PORT_BASE + 0x2c4)
+#define DMA_TX_STATUS (PORT_BASE + 0x2d0)
+#define DMA_TX_STATUS_BUSY_OFF 0
+#define DMA_TX_STATUS_BUSY_MSK (0x1 << DMA_TX_STATUS_BUSY_OFF)
+#define DMA_RX_STATUS (PORT_BASE + 0x2e8)
+#define DMA_RX_STATUS_BUSY_OFF 0
+#define DMA_RX_STATUS_BUSY_MSK (0x1 << DMA_RX_STATUS_BUSY_OFF)
+
+#define COARSETUNE_TIME (PORT_BASE + 0x304)
+#define TXDEEMPH_G1 (PORT_BASE + 0x350)
+#define ERR_CNT_DWS_LOST (PORT_BASE + 0x380)
+#define ERR_CNT_RESET_PROB (PORT_BASE + 0x384)
+#define ERR_CNT_INVLD_DW (PORT_BASE + 0x390)
+#define ERR_CNT_CODE_ERR (PORT_BASE + 0x394)
+#define ERR_CNT_DISP_ERR (PORT_BASE + 0x398)
+#define DFX_FIFO_CTRL (PORT_BASE + 0x3a0)
+#define DFX_FIFO_CTRL_TRIGGER_MODE_OFF 0
+#define DFX_FIFO_CTRL_TRIGGER_MODE_MSK (0x7 << DFX_FIFO_CTRL_TRIGGER_MODE_OFF)
+#define DFX_FIFO_CTRL_DUMP_MODE_OFF 3
+#define DFX_FIFO_CTRL_DUMP_MODE_MSK (0x7 << DFX_FIFO_CTRL_DUMP_MODE_OFF)
+#define DFX_FIFO_CTRL_SIGNAL_SEL_OFF 6
+#define DFX_FIFO_CTRL_SIGNAL_SEL_MSK (0xF << DFX_FIFO_CTRL_SIGNAL_SEL_OFF)
+#define DFX_FIFO_CTRL_DUMP_DISABLE_OFF 10
+#define DFX_FIFO_CTRL_DUMP_DISABLE_MSK (0x1 << DFX_FIFO_CTRL_DUMP_DISABLE_OFF)
+#define DFX_FIFO_TRIGGER (PORT_BASE + 0x3a4)
+#define DFX_FIFO_TRIGGER_MSK (PORT_BASE + 0x3a8)
+#define DFX_FIFO_DUMP_MSK (PORT_BASE + 0x3aC)
+#define DFX_FIFO_RD_DATA (PORT_BASE + 0x3b0)
+
+#define DEFAULT_ITCT_HW 2048 /* reset value, not reprogrammed */
+#if (HISI_SAS_MAX_DEVICES > DEFAULT_ITCT_HW)
+#error Max ITCT exceeded
+#endif
+
+#define AXI_MASTER_CFG_BASE (0x5000)
+#define AM_CTRL_GLOBAL (0x0)
+#define AM_CTRL_SHUTDOWN_REQ_OFF 0
+#define AM_CTRL_SHUTDOWN_REQ_MSK (0x1 << AM_CTRL_SHUTDOWN_REQ_OFF)
+#define AM_CURR_TRANS_RETURN (0x150)
+
+#define AM_CFG_MAX_TRANS (0x5010)
+#define AM_CFG_SINGLE_PORT_MAX_TRANS (0x5014)
+#define AXI_CFG (0x5100)
+#define AM_ROB_ECC_ERR_ADDR (0x510c)
+#define AM_ROB_ECC_ERR_ADDR_OFF 0
+#define AM_ROB_ECC_ERR_ADDR_MSK 0xffffffff
+
+/* RAS registers need init */
+#define RAS_BASE (0x6000)
+#define SAS_RAS_INTR0 (RAS_BASE)
+#define SAS_RAS_INTR1 (RAS_BASE + 0x04)
+#define SAS_RAS_INTR0_MASK (RAS_BASE + 0x08)
+#define SAS_RAS_INTR1_MASK (RAS_BASE + 0x0c)
+#define CFG_SAS_RAS_INTR_MASK (RAS_BASE + 0x1c)
+#define SAS_RAS_INTR2 (RAS_BASE + 0x20)
+#define SAS_RAS_INTR2_MASK (RAS_BASE + 0x24)
+
+/* HW dma structures */
+/* Delivery queue header */
+/* dw0 */
+#define CMD_HDR_ABORT_FLAG_OFF 0
+#define CMD_HDR_ABORT_FLAG_MSK (0x3 << CMD_HDR_ABORT_FLAG_OFF)
+#define CMD_HDR_ABORT_DEVICE_TYPE_OFF 2
+#define CMD_HDR_ABORT_DEVICE_TYPE_MSK (0x1 << CMD_HDR_ABORT_DEVICE_TYPE_OFF)
+#define CMD_HDR_RESP_REPORT_OFF 5
+#define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF)
+#define CMD_HDR_TLR_CTRL_OFF 6
+#define CMD_HDR_TLR_CTRL_MSK (0x3 << CMD_HDR_TLR_CTRL_OFF)
+#define CMD_HDR_PHY_ID_OFF 8
+#define CMD_HDR_PHY_ID_MSK (0x1ff << CMD_HDR_PHY_ID_OFF)
+#define CMD_HDR_FORCE_PHY_OFF 17
+#define CMD_HDR_FORCE_PHY_MSK (0x1U << CMD_HDR_FORCE_PHY_OFF)
+#define CMD_HDR_PORT_OFF 18
+#define CMD_HDR_PORT_MSK (0xf << CMD_HDR_PORT_OFF)
+#define CMD_HDR_PRIORITY_OFF 27
+#define CMD_HDR_PRIORITY_MSK (0x1 << CMD_HDR_PRIORITY_OFF)
+#define CMD_HDR_CMD_OFF 29
+#define CMD_HDR_CMD_MSK (0x7 << CMD_HDR_CMD_OFF)
+/* dw1 */
+#define CMD_HDR_UNCON_CMD_OFF 3
+#define CMD_HDR_DIR_OFF 5
+#define CMD_HDR_DIR_MSK (0x3 << CMD_HDR_DIR_OFF)
+#define CMD_HDR_RESET_OFF 7
+#define CMD_HDR_RESET_MSK (0x1 << CMD_HDR_RESET_OFF)
+#define CMD_HDR_VDTL_OFF 10
+#define CMD_HDR_VDTL_MSK (0x1 << CMD_HDR_VDTL_OFF)
+#define CMD_HDR_FRAME_TYPE_OFF 11
+#define CMD_HDR_FRAME_TYPE_MSK (0x1f << CMD_HDR_FRAME_TYPE_OFF)
+#define CMD_HDR_DEV_ID_OFF 16
+#define CMD_HDR_DEV_ID_MSK (0xffff << CMD_HDR_DEV_ID_OFF)
+/* dw2 */
+#define CMD_HDR_CFL_OFF 0
+#define CMD_HDR_CFL_MSK (0x1ff << CMD_HDR_CFL_OFF)
+#define CMD_HDR_NCQ_TAG_OFF 10
+#define CMD_HDR_NCQ_TAG_MSK (0x1f << CMD_HDR_NCQ_TAG_OFF)
+#define CMD_HDR_MRFL_OFF 15
+#define CMD_HDR_MRFL_MSK (0x1ff << CMD_HDR_MRFL_OFF)
+#define CMD_HDR_SG_MOD_OFF 24
+#define CMD_HDR_SG_MOD_MSK (0x3 << CMD_HDR_SG_MOD_OFF)
+/* dw3 */
+#define CMD_HDR_IPTT_OFF 0
+#define CMD_HDR_IPTT_MSK (0xffff << CMD_HDR_IPTT_OFF)
+/* dw6 */
+#define CMD_HDR_DIF_SGL_LEN_OFF 0
+#define CMD_HDR_DIF_SGL_LEN_MSK (0xffff << CMD_HDR_DIF_SGL_LEN_OFF)
+#define CMD_HDR_DATA_SGL_LEN_OFF 16
+#define CMD_HDR_DATA_SGL_LEN_MSK (0xffff << CMD_HDR_DATA_SGL_LEN_OFF)
+/* dw7 */
+#define CMD_HDR_ADDR_MODE_SEL_OFF 15
+#define CMD_HDR_ADDR_MODE_SEL_MSK (1 << CMD_HDR_ADDR_MODE_SEL_OFF)
+#define CMD_HDR_ABORT_IPTT_OFF 16
+#define CMD_HDR_ABORT_IPTT_MSK (0xffff << CMD_HDR_ABORT_IPTT_OFF)
+
+/* Completion header */
+/* dw0 */
+#define CMPLT_HDR_CMPLT_OFF 0
+#define CMPLT_HDR_CMPLT_MSK (0x3 << CMPLT_HDR_CMPLT_OFF)
+#define CMPLT_HDR_ERROR_PHASE_OFF 2
+#define CMPLT_HDR_ERROR_PHASE_MSK (0xff << CMPLT_HDR_ERROR_PHASE_OFF)
+/* bit[9:2] Error Phase */
+#define ERR_PHASE_RESPONSE_FRAME_REV_STAGE_OFF \
+ 8
+#define ERR_PHASE_RESPONSE_FRAME_REV_STAGE_MSK \
+ (0x1 << ERR_PHASE_RESPONSE_FRAME_REV_STAGE_OFF)
+#define CMPLT_HDR_RSPNS_XFRD_OFF 10
+#define CMPLT_HDR_RSPNS_XFRD_MSK (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF)
+#define CMPLT_HDR_RSPNS_GOOD_OFF 11
+#define CMPLT_HDR_RSPNS_GOOD_MSK (0x1 << CMPLT_HDR_RSPNS_GOOD_OFF)
+#define CMPLT_HDR_ERX_OFF 12
+#define CMPLT_HDR_ERX_MSK (0x1 << CMPLT_HDR_ERX_OFF)
+#define CMPLT_HDR_ABORT_STAT_OFF 13
+#define CMPLT_HDR_ABORT_STAT_MSK (0x7 << CMPLT_HDR_ABORT_STAT_OFF)
+/* abort_stat */
+#define STAT_IO_NOT_VALID 0x1
+#define STAT_IO_NO_DEVICE 0x2
+#define STAT_IO_COMPLETE 0x3
+#define STAT_IO_ABORTED 0x4
+/* dw1 */
+#define CMPLT_HDR_IPTT_OFF 0
+#define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF)
+#define CMPLT_HDR_DEV_ID_OFF 16
+#define CMPLT_HDR_DEV_ID_MSK (0xffff << CMPLT_HDR_DEV_ID_OFF)
+/* dw3 */
+#define SATA_DISK_IN_ERROR_STATUS_OFF 8
+#define SATA_DISK_IN_ERROR_STATUS_MSK (0x1 << SATA_DISK_IN_ERROR_STATUS_OFF)
+#define CMPLT_HDR_SATA_DISK_ERR_OFF 16
+#define CMPLT_HDR_SATA_DISK_ERR_MSK (0x1 << CMPLT_HDR_SATA_DISK_ERR_OFF)
+#define CMPLT_HDR_IO_IN_TARGET_OFF 17
+#define CMPLT_HDR_IO_IN_TARGET_MSK (0x1 << CMPLT_HDR_IO_IN_TARGET_OFF)
+/* bit[23:18] ERR_FIS_ATA_STATUS */
+#define FIS_ATA_STATUS_ERR_OFF 18
+#define FIS_ATA_STATUS_ERR_MSK (0x1 << FIS_ATA_STATUS_ERR_OFF)
+#define FIS_TYPE_SDB_OFF 31
+#define FIS_TYPE_SDB_MSK (0x1 << FIS_TYPE_SDB_OFF)
+
+/* ITCT header */
+/* qw0 */
+#define ITCT_HDR_DEV_TYPE_OFF 0
+#define ITCT_HDR_DEV_TYPE_MSK (0x3 << ITCT_HDR_DEV_TYPE_OFF)
+#define ITCT_HDR_VALID_OFF 2
+#define ITCT_HDR_VALID_MSK (0x1 << ITCT_HDR_VALID_OFF)
+#define ITCT_HDR_MCR_OFF 5
+#define ITCT_HDR_MCR_MSK (0xf << ITCT_HDR_MCR_OFF)
+#define ITCT_HDR_VLN_OFF 9
+#define ITCT_HDR_VLN_MSK (0xf << ITCT_HDR_VLN_OFF)
+#define ITCT_HDR_SMP_TIMEOUT_OFF 16
+#define ITCT_HDR_AWT_CONTINUE_OFF 25
+#define ITCT_HDR_PORT_ID_OFF 28
+#define ITCT_HDR_PORT_ID_MSK (0xf << ITCT_HDR_PORT_ID_OFF)
+/* qw2 */
+#define ITCT_HDR_INLT_OFF 0
+#define ITCT_HDR_INLT_MSK (0xffffULL << ITCT_HDR_INLT_OFF)
+#define ITCT_HDR_RTOLT_OFF 48
+#define ITCT_HDR_RTOLT_MSK (0xffffULL << ITCT_HDR_RTOLT_OFF)
+
+/*debugfs*/
+#define TWO_PARA_PER_LINE 2
+#define FOUR_PARA_PER_LINE 4
+#define DUMP_BUF_SIZE 8
+#define BIST_BUF_SIZE 16
+
+struct hisi_sas_protect_iu_v3_hw {
+ u32 dw0;
+ u32 lbrtcv;
+ u32 lbrtgv;
+ u32 dw3;
+ u32 dw4;
+ u32 dw5;
+ u32 rsv;
+};
+
+struct hisi_sas_complete_v3_hdr {
+ __le32 dw0;
+ __le32 dw1;
+ __le32 act;
+ __le32 dw3;
+};
+
+struct hisi_sas_err_record_v3 {
+ /* dw0 */
+ __le32 trans_tx_fail_type;
+
+ /* dw1 */
+ __le32 trans_rx_fail_type;
+
+ /* dw2 */
+ __le16 dma_tx_err_type;
+ __le16 sipc_rx_err_type;
+
+ /* dw3 */
+ __le32 dma_rx_err_type;
+};
+
+#define RX_DATA_LEN_UNDERFLOW_OFF 6
+#define RX_DATA_LEN_UNDERFLOW_MSK (1 << RX_DATA_LEN_UNDERFLOW_OFF)
+
+#define RX_FIS_STATUS_ERR_OFF 0
+#define RX_FIS_STATUS_ERR_MSK (1 << RX_FIS_STATUS_ERR_OFF)
+
+#define HISI_SAS_COMMAND_ENTRIES_V3_HW 4096
+#define HISI_SAS_MSI_COUNT_V3_HW 32
+
+#define DIR_NO_DATA 0
+#define DIR_TO_INI 1
+#define DIR_TO_DEVICE 2
+#define DIR_RESERVED 3
+
+#define FIS_CMD_IS_UNCONSTRAINED(fis) \
+ ((fis.command == ATA_CMD_READ_LOG_EXT) || \
+ (fis.command == ATA_CMD_READ_LOG_DMA_EXT) || \
+ ((fis.command == ATA_CMD_DEV_RESET) && \
+ ((fis.control & ATA_SRST) != 0)))
+
+#define T10_INSRT_EN_OFF 0
+#define T10_INSRT_EN_MSK (1 << T10_INSRT_EN_OFF)
+#define T10_RMV_EN_OFF 1
+#define T10_RMV_EN_MSK (1 << T10_RMV_EN_OFF)
+#define T10_RPLC_EN_OFF 2
+#define T10_RPLC_EN_MSK (1 << T10_RPLC_EN_OFF)
+#define T10_CHK_EN_OFF 3
+#define T10_CHK_EN_MSK (1 << T10_CHK_EN_OFF)
+#define INCR_LBRT_OFF 5
+#define INCR_LBRT_MSK (1 << INCR_LBRT_OFF)
+#define USR_DATA_BLOCK_SZ_OFF 20
+#define USR_DATA_BLOCK_SZ_MSK (0x3 << USR_DATA_BLOCK_SZ_OFF)
+#define T10_CHK_MSK_OFF 16
+#define T10_CHK_REF_TAG_MSK (0xf0 << T10_CHK_MSK_OFF)
+#define T10_CHK_APP_TAG_MSK (0xc << T10_CHK_MSK_OFF)
+
+#define BASE_VECTORS_V3_HW 16
+#define MIN_AFFINE_VECTORS_V3_HW (BASE_VECTORS_V3_HW + 1)
+#define IRQ_PHY_UP_DOWN_INDEX 1
+#define IRQ_CHL_INDEX 2
+#define IRQ_AXI_INDEX 11
+
+#define DELAY_FOR_RESET_HW 100
+#define HDR_SG_MOD 0x2
+#define LUN_SIZE 8
+#define ATTR_PRIO_REGION 9
+#define CDB_REGION 12
+#define PRIO_OFF 3
+#define TMF_REGION 10
+#define TAG_MSB 12
+#define TAG_LSB 13
+#define SMP_FRAME_TYPE 2
+#define SMP_CRC_SIZE 4
+#define HDR_TAG_OFF 3
+#define HOST_NO_OFF 6
+#define PHY_NO_OFF 7
+#define IDENTIFY_REG_READ 6
+#define LINK_RESET_TIMEOUT_OFF 4
+#define DECIMALISM_FLAG 10
+#define WAIT_RETRY 100
+#define WAIT_TMROUT 5000
+
+#define ID_DWORD0_INDEX 0
+#define ID_DWORD1_INDEX 1
+#define ID_DWORD2_INDEX 2
+#define ID_DWORD3_INDEX 3
+#define ID_DWORD4_INDEX 4
+#define ID_DWORD5_INDEX 5
+#define TICKS_BIT_INDEX 24
+#define COUNT_BIT_INDEX 8
+
+#define PORT_REG_LENGTH 0x100
+#define GLOBAL_REG_LENGTH 0x800
+#define AXI_REG_LENGTH 0x61
+#define RAS_REG_LENGTH 0x10
+
+#define CHNL_INT_STS_MSK 0xeeeeeeee
+#define CHNL_INT_STS_PHY_MSK 0xe
+#define CHNL_INT_STS_INT0_MSK BIT(1)
+#define CHNL_INT_STS_INT1_MSK BIT(2)
+#define CHNL_INT_STS_INT2_MSK BIT(3)
+#define CHNL_WIDTH 4
+
+#define BAR_NO_V3_HW 5
+
+enum {
+ DSM_FUNC_ERR_HANDLE_MSI = 0,
+};
+
+static bool hisi_sas_intr_conv;
+MODULE_PARM_DESC(intr_conv, "interrupt converge enable (0-1)");
+
+/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */
+static int prot_mask;
+module_param(prot_mask, int, 0444);
+MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=0x0 ");
+
+/* the index of iopoll queues are bigger than interrupt queues' */
+static int experimental_iopoll_q_cnt;
+module_param(experimental_iopoll_q_cnt, int, 0444);
+MODULE_PARM_DESC(experimental_iopoll_q_cnt, "number of queues to be used as poll mode, def=0");
+
+static int debugfs_snapshot_regs_v3_hw(struct hisi_hba *hisi_hba);
+
+static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ return readl(regs);
+}
+
+static void hisi_sas_write32(struct hisi_hba *hisi_hba, u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ writel(val, regs);
+}
+
+static void hisi_sas_phy_write32(struct hisi_hba *hisi_hba, int phy_no,
+ u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ writel(val, regs);
+}
+
+static u32 hisi_sas_phy_read32(struct hisi_hba *hisi_hba,
+ int phy_no, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ return readl(regs);
+}
+
+#define hisi_sas_read32_poll_timeout(off, val, cond, delay_us, \
+ timeout_us) \
+({ \
+ void __iomem *regs = hisi_hba->regs + off; \
+ readl_poll_timeout(regs, val, cond, delay_us, timeout_us); \
+})
+
+#define hisi_sas_read32_poll_timeout_atomic(off, val, cond, delay_us, \
+ timeout_us) \
+({ \
+ void __iomem *regs = hisi_hba->regs + off; \
+ readl_poll_timeout_atomic(regs, val, cond, delay_us, timeout_us);\
+})
+
+static void interrupt_enable_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ hisi_sas_write32(hisi_hba, OQ0_INT_SRC_MSK + 0x4 * i, 0);
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0xfefefefe);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0xfefefefe);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0xffc220ff);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0x155555);
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xf2057fff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0xffffbfe);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_PHY_ENA_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, SL_RX_BCAST_CHK_MSK, 0x0);
+ }
+}
+
+static void init_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct pci_dev *pdev = hisi_hba->pci_dev;
+ int i, j;
+
+ /* Global registers init */
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+ (u32)((1ULL << hisi_hba->queue_count) - 1));
+ hisi_sas_write32(hisi_hba, CFG_MAX_TAG, 0xfff0400);
+ /* time / CLK_AHB = 2.5s / 2ns = 0x4A817C80 */
+ hisi_sas_write32(hisi_hba, TRANS_LOCK_ICT_TIME, 0x4A817C80);
+ hisi_sas_write32(hisi_hba, HGC_SAS_TXFAIL_RETRY_CTRL, 0x108);
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, CFG_ICT_TIMER_STEP_TRSH, 0xf4240);
+ hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x3);
+ /* configure the interrupt coalescing timeout period 10us */
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0xa);
+ /* configure the count of CQ entries 10 */
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0xa);
+ hisi_sas_write32(hisi_hba, CQ_INT_CONVERGE_EN,
+ hisi_sas_intr_conv);
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 0xffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC1, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC2, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC3, 0xffffffff);
+ hisi_sas_write32(hisi_hba, CHNL_PHYUPDOWN_INT_MSK, 0x0);
+ hisi_sas_write32(hisi_hba, CHNL_ENT_INT_MSK, 0x0);
+ hisi_sas_write32(hisi_hba, HGC_COM_INT_MSK, 0x0);
+ hisi_sas_write32(hisi_hba, AWQOS_AWCACHE_CFG, 0xf0f0);
+ hisi_sas_write32(hisi_hba, ARQOS_ARCACHE_CFG, 0xf0f0);
+ hisi_sas_write32(hisi_hba, HYPER_STREAM_ID_EN_CFG, 1);
+
+ if (pdev->revision < 0x30)
+ hisi_sas_write32(hisi_hba, SAS_AXI_USER3, 0);
+
+ interrupt_enable_v3_hw(hisi_hba);
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ enum sas_linkrate max;
+ struct hisi_sas_phy *phy = &hisi_hba->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ u32 prog_phy_link_rate = hisi_sas_phy_read32(hisi_hba, i,
+ PROG_PHY_LINK_RATE);
+
+ prog_phy_link_rate &= ~CFG_PROG_PHY_LINK_RATE_MSK;
+ if (!sas_phy->phy || (sas_phy->phy->maximum_linkrate <
+ SAS_LINK_RATE_1_5_GBPS))
+ max = SAS_LINK_RATE_12_0_GBPS;
+ else
+ max = sas_phy->phy->maximum_linkrate;
+ prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max);
+ hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE,
+ prog_phy_link_rate);
+ hisi_sas_phy_write32(hisi_hba, i, SAS_RX_TRAIN_TIMER, 0x13e80);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_DWS_RESET_MSK, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_OOB_RESTART_MSK, 0x1);
+ hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER, 0x7ffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER, 0x2a0a01);
+ hisi_sas_phy_write32(hisi_hba, i, SAS_EC_INT_COAL_TIME,
+ 0x30f4240);
+ hisi_sas_phy_write32(hisi_hba, i, AIP_LIMIT, 0x2ffff);
+
+ /* set value through firmware for 920B and later version */
+ if (pdev->revision < 0x30) {
+ hisi_sas_phy_write32(hisi_hba, i, SAS_SSP_CON_TIMER_CFG, 0x32);
+ hisi_sas_phy_write32(hisi_hba, i, SERDES_CFG, 0xffc00);
+ /* used for 12G negotiate */
+ hisi_sas_phy_write32(hisi_hba, i, COARSETUNE_TIME, 0x1e);
+ }
+
+ /* get default FFE configuration for BIST */
+ for (j = 0; j < FFE_CFG_MAX; j++) {
+ u32 val = hisi_sas_phy_read32(hisi_hba, i,
+ TXDEEMPH_G1 + (j * 0x4));
+ hisi_hba->debugfs_bist_ffe[i][j] = val;
+ }
+ }
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ /* Delivery queue */
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+
+ /* Completion queue */
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+ }
+
+ /* itct */
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->itct_dma));
+
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->itct_dma));
+
+ /* iost */
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->iost_dma));
+
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->iost_dma));
+
+ /* breakpoint */
+ hisi_sas_write32(hisi_hba, IO_BROKEN_MSG_ADDR_LO,
+ lower_32_bits(hisi_hba->breakpoint_dma));
+
+ hisi_sas_write32(hisi_hba, IO_BROKEN_MSG_ADDR_HI,
+ upper_32_bits(hisi_hba->breakpoint_dma));
+
+ /* SATA broken msg */
+ hisi_sas_write32(hisi_hba, IO_SATA_BROKEN_MSG_ADDR_LO,
+ lower_32_bits(hisi_hba->sata_breakpoint_dma));
+
+ hisi_sas_write32(hisi_hba, IO_SATA_BROKEN_MSG_ADDR_HI,
+ upper_32_bits(hisi_hba->sata_breakpoint_dma));
+
+ /* SATA initial fis */
+ hisi_sas_write32(hisi_hba, SATA_INITI_D2H_STORE_ADDR_LO,
+ lower_32_bits(hisi_hba->initial_fis_dma));
+
+ hisi_sas_write32(hisi_hba, SATA_INITI_D2H_STORE_ADDR_HI,
+ upper_32_bits(hisi_hba->initial_fis_dma));
+
+ /* RAS registers init */
+ hisi_sas_write32(hisi_hba, SAS_RAS_INTR0_MASK, 0x0);
+ hisi_sas_write32(hisi_hba, SAS_RAS_INTR1_MASK, 0x0);
+ hisi_sas_write32(hisi_hba, SAS_RAS_INTR2_MASK, 0x0);
+ hisi_sas_write32(hisi_hba, CFG_SAS_RAS_INTR_MASK, 0x0);
+
+ /* LED registers init */
+ hisi_sas_write32(hisi_hba, SAS_CFG_DRIVE_VLD, 0x80000ff);
+ hisi_sas_write32(hisi_hba, SAS_GPIO_TX_0_1, 0x80808080);
+ hisi_sas_write32(hisi_hba, SAS_GPIO_TX_0_1 + 0x4, 0x80808080);
+ /* Configure blink generator rate A to 1Hz and B to 4Hz */
+ hisi_sas_write32(hisi_hba, SAS_GPIO_CFG_1, 0x121700);
+ hisi_sas_write32(hisi_hba, SAS_GPIO_CFG_0, 0x800000);
+}
+
+static void config_phy_opt_mode_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg &= ~PHY_CFG_DC_OPT_MSK;
+ cfg |= 1 << PHY_CFG_DC_OPT_OFF;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void config_id_frame_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct sas_identify_frame identify_frame;
+ u32 *identify_buffer;
+
+ memset(&identify_frame, 0, sizeof(identify_frame));
+ identify_frame.dev_type = SAS_END_DEVICE;
+ identify_frame.frame_type = 0;
+ identify_frame._un1 = 1;
+ identify_frame.initiator_bits = SAS_PROTOCOL_ALL;
+ identify_frame.target_bits = SAS_PROTOCOL_NONE;
+ memcpy(&identify_frame._un4_11[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ memcpy(&identify_frame.sas_addr[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ identify_frame.phy_id = phy_no;
+ identify_buffer = (u32 *)(&identify_frame);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0,
+ __swab32(identify_buffer[ID_DWORD0_INDEX]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1,
+ __swab32(identify_buffer[ID_DWORD1_INDEX]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2,
+ __swab32(identify_buffer[ID_DWORD2_INDEX]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3,
+ __swab32(identify_buffer[ID_DWORD3_INDEX]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4,
+ __swab32(identify_buffer[ID_DWORD4_INDEX]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5,
+ __swab32(identify_buffer[ID_DWORD5_INDEX]));
+}
+
+static void setup_itct_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ struct domain_device *device = sas_dev->sas_device;
+ struct device *dev = hisi_hba->dev;
+ u64 qw0, device_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[device_id];
+ struct asd_sas_port *sas_port = device->port;
+ struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+ u64 sas_addr;
+
+ memset(itct, 0, sizeof(*itct));
+
+ /* qw0 */
+ qw0 = 0;
+ switch (sas_dev->dev_type) {
+ case SAS_END_DEVICE:
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF;
+ break;
+ case SAS_SATA_DEV:
+ case SAS_SATA_PENDING:
+ if (dev_parent_is_expander(device))
+ qw0 = HISI_SAS_DEV_TYPE_STP << ITCT_HDR_DEV_TYPE_OFF;
+ else
+ qw0 = HISI_SAS_DEV_TYPE_SATA << ITCT_HDR_DEV_TYPE_OFF;
+ break;
+ default:
+ dev_warn(dev, "setup itct: unsupported dev type (%d)\n",
+ sas_dev->dev_type);
+ }
+
+ qw0 |= ((1 << ITCT_HDR_VALID_OFF) |
+ (device->linkrate << ITCT_HDR_MCR_OFF) |
+ (1 << ITCT_HDR_VLN_OFF) |
+ (0xfa << ITCT_HDR_SMP_TIMEOUT_OFF) |
+ (1 << ITCT_HDR_AWT_CONTINUE_OFF) |
+ (port->id << ITCT_HDR_PORT_ID_OFF));
+ itct->qw0 = cpu_to_le64(qw0);
+
+ /* qw1 */
+ memcpy(&sas_addr, device->sas_addr, SAS_ADDR_SIZE);
+ itct->sas_addr = cpu_to_le64(__swab64(sas_addr));
+
+ /* qw2 */
+ if (!dev_is_sata(device))
+ itct->qw2 = cpu_to_le64((5000ULL << ITCT_HDR_INLT_OFF) |
+ (0x1ULL << ITCT_HDR_RTOLT_OFF));
+}
+
+static int clear_itct_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u64 dev_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[dev_id];
+ u32 reg_val = hisi_sas_read32(hisi_hba, ENT_INT_SRC3);
+ struct device *dev = hisi_hba->dev;
+
+ sas_dev->completion = &completion;
+
+ /* clear the itct interrupt state */
+ if (ENT_INT_SRC3_ITC_INT_MSK & reg_val)
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC3,
+ ENT_INT_SRC3_ITC_INT_MSK);
+
+ /* clear the itct table */
+ reg_val = ITCT_CLR_EN_MSK | (dev_id & ITCT_DEV_MSK);
+ hisi_sas_write32(hisi_hba, ITCT_CLR, reg_val);
+
+ if (!wait_for_completion_timeout(sas_dev->completion,
+ HISI_SAS_CLEAR_ITCT_TIMEOUT)) {
+ dev_warn(dev, "failed to clear ITCT\n");
+ return -ETIMEDOUT;
+ }
+
+ memset(itct, 0, sizeof(struct hisi_sas_itct));
+ return 0;
+}
+
+static void dereg_device_v3_hw(struct hisi_hba *hisi_hba,
+ struct domain_device *device)
+{
+ struct hisi_sas_slot *slot, *slot2;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ u32 cfg_abt_set_query_iptt;
+
+ cfg_abt_set_query_iptt = hisi_sas_read32(hisi_hba,
+ CFG_ABT_SET_QUERY_IPTT);
+ spin_lock(&sas_dev->lock);
+ list_for_each_entry_safe(slot, slot2, &sas_dev->list, entry) {
+ cfg_abt_set_query_iptt &= ~CFG_SET_ABORTED_IPTT_MSK;
+ cfg_abt_set_query_iptt |= (1 << CFG_SET_ABORTED_EN_OFF) |
+ (slot->idx << CFG_SET_ABORTED_IPTT_OFF);
+ hisi_sas_write32(hisi_hba, CFG_ABT_SET_QUERY_IPTT,
+ cfg_abt_set_query_iptt);
+ }
+ spin_unlock(&sas_dev->lock);
+ cfg_abt_set_query_iptt &= ~(1 << CFG_SET_ABORTED_EN_OFF);
+ hisi_sas_write32(hisi_hba, CFG_ABT_SET_QUERY_IPTT,
+ cfg_abt_set_query_iptt);
+ hisi_sas_write32(hisi_hba, CFG_ABT_SET_IPTT_DONE,
+ 1 << CFG_ABT_SET_IPTT_DONE_OFF);
+}
+
+static int reset_hw_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ int ret;
+ u32 val;
+
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0);
+
+ /* Disable all of the PHYs */
+ hisi_sas_stop_phys(hisi_hba);
+ udelay(HISI_SAS_DELAY_FOR_PHY_DISABLE);
+
+ /* Ensure axi bus idle */
+ ret = hisi_sas_read32_poll_timeout(AXI_CFG, val, !val,
+ 20000, 1000000);
+ if (ret) {
+ dev_err(dev, "axi bus is not idle, ret = %d!\n", ret);
+ return -EIO;
+ }
+
+ if (ACPI_HANDLE(dev)) {
+ acpi_status s;
+
+ s = acpi_evaluate_object(ACPI_HANDLE(dev), "_RST", NULL, NULL);
+ if (ACPI_FAILURE(s)) {
+ dev_err(dev, "Reset failed\n");
+ return -EIO;
+ }
+ } else {
+ dev_err(dev, "no reset method!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hw_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ struct acpi_device *acpi_dev;
+ union acpi_object *obj;
+ guid_t guid;
+ int rc;
+
+ rc = reset_hw_v3_hw(hisi_hba);
+ if (rc) {
+ dev_err(dev, "hisi_sas_reset_hw failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ msleep(DELAY_FOR_RESET_HW);
+ init_reg_v3_hw(hisi_hba);
+
+ if (guid_parse("D5918B4B-37AE-4E10-A99F-E5E8A6EF4C1F", &guid)) {
+ dev_err(dev, "Parse GUID failed\n");
+ return -EINVAL;
+ }
+
+ /*
+ * This DSM handles some hardware-related configurations:
+ * 1. Switch over to MSI error handling in kernel
+ * 2. BIOS *may* reset some register values through this method
+ */
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &guid, 0,
+ DSM_FUNC_ERR_HANDLE_MSI, NULL);
+ if (!obj)
+ dev_warn(dev, "can not find DSM method, ignore\n");
+ else
+ ACPI_FREE(obj);
+
+ acpi_dev = ACPI_COMPANION(dev);
+ if (!acpi_device_power_manageable(acpi_dev))
+ dev_notice(dev, "neither _PS0 nor _PR0 is defined\n");
+ return 0;
+}
+
+static void enable_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg |= PHY_CFG_ENA_MSK;
+ cfg &= ~PHY_CFG_PHY_RST_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void disable_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+ u32 irq_msk = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2_MSK);
+ static const u32 msk = BIT(CHL_INT2_RX_DISP_ERR_OFF) |
+ BIT(CHL_INT2_RX_CODE_ERR_OFF) |
+ BIT(CHL_INT2_RX_INVLD_DW_OFF);
+ u32 state;
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2_MSK, msk | irq_msk);
+
+ cfg &= ~PHY_CFG_ENA_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+
+ mdelay(HISI_SAS_DELAY_FOR_PHY_DISABLE);
+
+ state = hisi_sas_read32(hisi_hba, PHY_STATE);
+ if (state & BIT(phy_no)) {
+ cfg |= PHY_CFG_PHY_RST_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+ }
+
+ udelay(1);
+
+ hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_INVLD_DW);
+ hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DISP_ERR);
+ hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_CODE_ERR);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, msk);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2_MSK, irq_msk);
+}
+
+static void start_phy_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ config_id_frame_v3_hw(hisi_hba, phy_no);
+ config_phy_opt_mode_v3_hw(hisi_hba, phy_no);
+ enable_phy_v3_hw(hisi_hba, phy_no);
+}
+
+static void phy_hard_reset_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ u32 txid_auto;
+
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+ if (phy->identify.device_type == SAS_END_DEVICE) {
+ txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+ txid_auto | TX_HARDRST_MSK);
+ }
+ msleep(HISI_SAS_DELAY_FOR_PHY_DISABLE);
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+}
+
+static enum sas_linkrate phy_get_max_linkrate_v3_hw(void)
+{
+ return SAS_LINK_RATE_12_0_GBPS;
+}
+
+static void phys_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ if (!sas_phy->phy->enabled)
+ continue;
+
+ hisi_sas_phy_enable(hisi_hba, i, 1);
+ }
+}
+
+static void sl_notify_ssp_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 sl_control;
+
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control |= SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+ msleep(1);
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control &= ~SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+}
+
+static int get_wideport_bitmap_v3_hw(struct hisi_hba *hisi_hba, int port_id)
+{
+ int i, bitmap = 0;
+ u32 phy_port_num_ma = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+ u32 phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+
+ for (i = 0; i < hisi_hba->n_phy; i++)
+ if (phy_state & BIT(i))
+ if (((phy_port_num_ma >> (i * HISI_SAS_REG_MEM_SIZE)) & 0xf) ==
+ port_id)
+ bitmap |= BIT(i);
+
+ return bitmap;
+}
+
+static void start_delivery_v3_hw(struct hisi_sas_dq *dq)
+{
+ struct hisi_hba *hisi_hba = dq->hisi_hba;
+ struct hisi_sas_slot *s, *s1, *s2 = NULL;
+ int dlvry_queue = dq->id;
+ int wp;
+
+ list_for_each_entry_safe(s, s1, &dq->list, delivery) {
+ if (!s->ready)
+ break;
+ s2 = s;
+ list_del(&s->delivery);
+ }
+
+ if (!s2)
+ return;
+
+ /*
+ * Ensure that memories for slots built on other CPUs is observed.
+ */
+ smp_rmb();
+ wp = (s2->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS;
+
+ hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), wp);
+}
+
+static void prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_cmd_hdr *hdr,
+ struct scatterlist *scatter,
+ int n_elem)
+{
+ struct hisi_sas_sge_page *sge_page = hisi_sas_sge_addr_mem(slot);
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(scatter, sg, n_elem, i) {
+ struct hisi_sas_sge *entry = &sge_page->sge[i];
+
+ entry->addr = cpu_to_le64(sg_dma_address(sg));
+ entry->page_ctrl_0 = entry->page_ctrl_1 = 0;
+ entry->data_len = cpu_to_le32(sg_dma_len(sg));
+ entry->data_off = 0;
+ }
+
+ hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot));
+
+ hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+}
+
+static void prep_prd_sge_dif_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_cmd_hdr *hdr,
+ struct scatterlist *scatter,
+ int n_elem)
+{
+ struct hisi_sas_sge_dif_page *sge_dif_page;
+ struct scatterlist *sg;
+ int i;
+
+ sge_dif_page = hisi_sas_sge_dif_addr_mem(slot);
+
+ for_each_sg(scatter, sg, n_elem, i) {
+ struct hisi_sas_sge *entry = &sge_dif_page->sge[i];
+
+ entry->addr = cpu_to_le64(sg_dma_address(sg));
+ entry->page_ctrl_0 = 0;
+ entry->page_ctrl_1 = 0;
+ entry->data_len = cpu_to_le32(sg_dma_len(sg));
+ entry->data_off = 0;
+ }
+
+ hdr->dif_prd_table_addr =
+ cpu_to_le64(hisi_sas_sge_dif_addr_dma(slot));
+
+ hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DIF_SGL_LEN_OFF);
+}
+
+static u32 get_prot_chk_msk_v3_hw(struct scsi_cmnd *scsi_cmnd)
+{
+ unsigned char prot_flags = scsi_cmnd->prot_flags;
+
+ if (prot_flags & SCSI_PROT_REF_CHECK)
+ return T10_CHK_APP_TAG_MSK;
+ return T10_CHK_REF_TAG_MSK | T10_CHK_APP_TAG_MSK;
+}
+
+static void fill_prot_v3_hw(struct scsi_cmnd *scsi_cmnd,
+ struct hisi_sas_protect_iu_v3_hw *prot)
+{
+ unsigned char prot_op = scsi_get_prot_op(scsi_cmnd);
+ unsigned int interval = scsi_prot_interval(scsi_cmnd);
+ u32 lbrt_chk_val = t10_pi_ref_tag(scsi_cmd_to_rq(scsi_cmnd));
+
+ switch (prot_op) {
+ case SCSI_PROT_READ_INSERT:
+ prot->dw0 |= T10_INSRT_EN_MSK;
+ prot->lbrtgv = lbrt_chk_val;
+ break;
+ case SCSI_PROT_READ_STRIP:
+ prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK);
+ prot->lbrtcv = lbrt_chk_val;
+ prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
+ break;
+ case SCSI_PROT_READ_PASS:
+ prot->dw0 |= T10_CHK_EN_MSK;
+ prot->lbrtcv = lbrt_chk_val;
+ prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ prot->dw0 |= T10_INSRT_EN_MSK;
+ prot->lbrtgv = lbrt_chk_val;
+ break;
+ case SCSI_PROT_WRITE_STRIP:
+ prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK);
+ prot->lbrtcv = lbrt_chk_val;
+ break;
+ case SCSI_PROT_WRITE_PASS:
+ prot->dw0 |= T10_CHK_EN_MSK;
+ prot->lbrtcv = lbrt_chk_val;
+ prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd);
+ break;
+ default:
+ WARN(1, "prot_op(0x%x) is not valid\n", prot_op);
+ break;
+ }
+
+ switch (interval) {
+ case 512:
+ break;
+ case 4096:
+ prot->dw0 |= (0x1 << USR_DATA_BLOCK_SZ_OFF);
+ break;
+ case 520:
+ prot->dw0 |= (0x2 << USR_DATA_BLOCK_SZ_OFF);
+ break;
+ default:
+ WARN(1, "protection interval (0x%x) invalid\n",
+ interval);
+ break;
+ }
+
+ prot->dw0 |= INCR_LBRT_MSK;
+}
+
+static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_port *port = slot->port;
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+ struct sas_tmf_task *tmf = slot->tmf;
+ int has_data = 0, priority = !!tmf;
+ unsigned char prot_op;
+ u8 *buf_cmd;
+ u32 dw1 = 0, dw2 = 0, len = 0;
+
+ hdr->dw0 = cpu_to_le32((1 << CMD_HDR_RESP_REPORT_OFF) |
+ (2 << CMD_HDR_TLR_CTRL_OFF) |
+ (port->id << CMD_HDR_PORT_OFF) |
+ (priority << CMD_HDR_PRIORITY_OFF) |
+ (1 << CMD_HDR_CMD_OFF)); /* ssp */
+
+ dw1 = 1 << CMD_HDR_VDTL_OFF;
+ if (tmf) {
+ dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF;
+ dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF;
+ } else {
+ prot_op = scsi_get_prot_op(scsi_cmnd);
+ dw1 |= 1 << CMD_HDR_FRAME_TYPE_OFF;
+ switch (scsi_cmnd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_DEVICE << CMD_HDR_DIR_OFF;
+ break;
+ case DMA_FROM_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_INI << CMD_HDR_DIR_OFF;
+ break;
+ default:
+ dw1 &= ~CMD_HDR_DIR_MSK;
+ }
+ }
+
+ /* map itct entry */
+ dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF;
+
+ dw2 = (((sizeof(struct ssp_command_iu) + sizeof(struct ssp_frame_hdr) +
+ 3) / BYTE_TO_DW) << CMD_HDR_CFL_OFF) |
+ ((HISI_SAS_MAX_SSP_RESP_SZ / BYTE_TO_DW) << CMD_HDR_MRFL_OFF) |
+ (HDR_SG_MOD << CMD_HDR_SG_MOD_OFF);
+ hdr->dw2 = cpu_to_le32(dw2);
+ hdr->transfer_tags = cpu_to_le32(slot->idx);
+
+ if (has_data) {
+ prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter,
+ slot->n_elem);
+
+ if (scsi_prot_sg_count(scsi_cmnd))
+ prep_prd_sge_dif_v3_hw(hisi_hba, slot, hdr,
+ scsi_prot_sglist(scsi_cmnd),
+ slot->n_elem_dif);
+ }
+
+ hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot));
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+
+ buf_cmd = hisi_sas_cmd_hdr_addr_mem(slot) +
+ sizeof(struct ssp_frame_hdr);
+
+ memcpy(buf_cmd, &task->ssp_task.LUN, LUN_SIZE);
+ if (!tmf) {
+ buf_cmd[ATTR_PRIO_REGION] = ssp_task->task_attr;
+ memcpy(buf_cmd + CDB_REGION, scsi_cmnd->cmnd,
+ scsi_cmnd->cmd_len);
+ } else {
+ buf_cmd[TMF_REGION] = tmf->tmf;
+ switch (tmf->tmf) {
+ case TMF_ABORT_TASK:
+ case TMF_QUERY_TASK:
+ buf_cmd[TAG_MSB] =
+ (tmf->tag_of_task_to_be_managed >> 8) & 0xff;
+ buf_cmd[TAG_LSB] =
+ tmf->tag_of_task_to_be_managed & 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (has_data && (prot_op != SCSI_PROT_NORMAL)) {
+ struct hisi_sas_protect_iu_v3_hw prot;
+ u8 *buf_cmd_prot;
+
+ hdr->dw7 |= cpu_to_le32(1 << CMD_HDR_ADDR_MODE_SEL_OFF);
+ dw1 |= CMD_HDR_PIR_MSK;
+ buf_cmd_prot = hisi_sas_cmd_hdr_addr_mem(slot) +
+ sizeof(struct ssp_frame_hdr) +
+ sizeof(struct ssp_command_iu);
+
+ memset(&prot, 0, sizeof(struct hisi_sas_protect_iu_v3_hw));
+ fill_prot_v3_hw(scsi_cmnd, &prot);
+ memcpy(buf_cmd_prot, &prot,
+ sizeof(struct hisi_sas_protect_iu_v3_hw));
+ /*
+ * For READ, we need length of info read to memory, while for
+ * WRITE we need length of data written to the disk.
+ */
+ if (prot_op == SCSI_PROT_WRITE_INSERT ||
+ prot_op == SCSI_PROT_READ_INSERT ||
+ prot_op == SCSI_PROT_WRITE_PASS ||
+ prot_op == SCSI_PROT_READ_PASS) {
+ unsigned int interval = scsi_prot_interval(scsi_cmnd);
+ unsigned int ilog2_interval = ilog2(interval);
+
+ len = (task->total_xfer_len >> ilog2_interval) *
+ BYTE_TO_DDW;
+ }
+ }
+
+ hdr->dw1 = cpu_to_le32(dw1);
+
+ hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len + len);
+}
+
+static void prep_smp_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_port *port = slot->port;
+ struct scatterlist *sg_req;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ dma_addr_t req_dma_addr;
+ unsigned int req_len;
+ u32 cfl;
+
+ /* req */
+ sg_req = &task->smp_task.smp_req;
+ req_len = sg_dma_len(sg_req);
+ req_dma_addr = sg_dma_address(sg_req);
+
+ /* create header */
+ /* dw0 */
+ hdr->dw0 = cpu_to_le32((port->id << CMD_HDR_PORT_OFF) |
+ (1 << CMD_HDR_PRIORITY_OFF) | /* high pri */
+ (SMP_FRAME_TYPE << CMD_HDR_CMD_OFF)); /* smp */
+
+ /* map itct entry */
+ hdr->dw1 = cpu_to_le32((sas_dev->device_id << CMD_HDR_DEV_ID_OFF) |
+ (1 << CMD_HDR_FRAME_TYPE_OFF) |
+ (DIR_NO_DATA << CMD_HDR_DIR_OFF));
+
+ /* dw2 */
+ cfl = (req_len - SMP_CRC_SIZE) / BYTE_TO_DW;
+ hdr->dw2 = cpu_to_le32((cfl << CMD_HDR_CFL_OFF) |
+ (HISI_SAS_MAX_SMP_RESP_SZ / BYTE_TO_DW <<
+ CMD_HDR_MRFL_OFF));
+
+ hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF);
+
+ hdr->cmd_table_addr = cpu_to_le64(req_dma_addr);
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+}
+
+static void prep_ata_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct asd_sas_port *sas_port = device->port;
+ struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
+ int phy_id;
+ u8 *buf_cmd;
+ int has_data = 0, hdr_tag = 0;
+ u32 dw1 = 0, dw2 = 0;
+
+ hdr->dw0 = cpu_to_le32(port->id << CMD_HDR_PORT_OFF);
+ if (dev_parent_is_expander(device)) {
+ hdr->dw0 |= cpu_to_le32(3 << CMD_HDR_CMD_OFF);
+ } else {
+ phy_id = device->phy->identify.phy_identifier;
+ hdr->dw0 |= cpu_to_le32((1U << phy_id)
+ << CMD_HDR_PHY_ID_OFF);
+ hdr->dw0 |= CMD_HDR_FORCE_PHY_MSK;
+ hdr->dw0 |= cpu_to_le32(4U << CMD_HDR_CMD_OFF);
+ }
+
+ switch (task->data_dir) {
+ case DMA_TO_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_DEVICE << CMD_HDR_DIR_OFF;
+ break;
+ case DMA_FROM_DEVICE:
+ has_data = 1;
+ dw1 |= DIR_TO_INI << CMD_HDR_DIR_OFF;
+ break;
+ default:
+ dw1 &= ~CMD_HDR_DIR_MSK;
+ }
+
+ if ((task->ata_task.fis.command == ATA_CMD_DEV_RESET) &&
+ (task->ata_task.fis.control & ATA_SRST))
+ dw1 |= 1 << CMD_HDR_RESET_OFF;
+
+ dw1 |= (hisi_sas_get_ata_protocol(task)) << CMD_HDR_FRAME_TYPE_OFF;
+ dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF;
+
+ if (FIS_CMD_IS_UNCONSTRAINED(task->ata_task.fis))
+ dw1 |= 1 << CMD_HDR_UNCON_CMD_OFF;
+
+ hdr->dw1 = cpu_to_le32(dw1);
+
+ /* dw2 */
+ if (task->ata_task.use_ncq) {
+ struct ata_queued_cmd *qc = task->uldd_task;
+
+ hdr_tag = qc->tag;
+ task->ata_task.fis.sector_count |=
+ (u8)(hdr_tag << HDR_TAG_OFF);
+ dw2 |= hdr_tag << CMD_HDR_NCQ_TAG_OFF;
+ }
+
+ dw2 |= (HISI_SAS_MAX_STP_RESP_SZ / BYTE_TO_DW) << CMD_HDR_CFL_OFF |
+ HDR_SG_MOD << CMD_HDR_SG_MOD_OFF;
+ hdr->dw2 = cpu_to_le32(dw2);
+
+ /* dw3 */
+ hdr->transfer_tags = cpu_to_le32(slot->idx);
+
+ if (has_data)
+ prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter,
+ slot->n_elem);
+
+ hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len);
+ hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot));
+ hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot));
+
+ buf_cmd = hisi_sas_cmd_hdr_addr_mem(slot);
+
+ if (likely(!task->ata_task.device_control_reg_update))
+ task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
+ /* fill in command FIS */
+ memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
+}
+
+static void prep_abort_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct sas_internal_abort_task *abort = &task->abort_task;
+ struct domain_device *dev = task->dev;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct hisi_sas_port *port = slot->port;
+ struct hisi_sas_device *sas_dev = dev->lldd_dev;
+ bool sata = dev_is_sata(dev);
+
+ /* dw0 */
+ hdr->dw0 = cpu_to_le32((5U << CMD_HDR_CMD_OFF) | /* abort */
+ (port->id << CMD_HDR_PORT_OFF) |
+ (sata << CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
+ (abort->type << CMD_HDR_ABORT_FLAG_OFF));
+
+ /* dw1 */
+ hdr->dw1 = cpu_to_le32(sas_dev->device_id
+ << CMD_HDR_DEV_ID_OFF);
+
+ /* dw7 */
+ hdr->dw7 = cpu_to_le32(abort->tag << CMD_HDR_ABORT_IPTT_OFF);
+ hdr->transfer_tags = cpu_to_le32(slot->idx);
+}
+
+static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
+{
+ int i;
+ irqreturn_t res;
+ u32 context, port_id, link_rate;
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct device *dev = hisi_hba->dev;
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1);
+
+ port_id = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+ port_id = (port_id >> (HISI_SAS_REG_MEM_SIZE * phy_no)) & 0xf;
+ link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE);
+ link_rate = (link_rate >> (phy_no * HISI_SAS_REG_MEM_SIZE)) & 0xf;
+
+ if (port_id == 0xf) {
+ dev_err(dev, "phyup: phy%d invalid portid\n", phy_no);
+ res = IRQ_NONE;
+ goto end;
+ }
+ sas_phy->linkrate = link_rate;
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+
+ /* Check for SATA dev */
+ context = hisi_sas_read32(hisi_hba, PHY_CONTEXT);
+ if (context & (1 << phy_no)) {
+ struct hisi_sas_initial_fis *initial_fis;
+ struct dev_to_host_fis *fis;
+ u8 attached_sas_addr[SAS_ADDR_SIZE] = {0};
+ struct Scsi_Host *shost = hisi_hba->shost;
+
+ dev_info(dev, "phyup: phy%d link_rate=%d(sata)\n", phy_no, link_rate);
+ initial_fis = &hisi_hba->initial_fis[phy_no];
+ fis = &initial_fis->fis;
+
+ /* check ERR bit of Status Register */
+ if (fis->status & ATA_ERR) {
+ dev_warn(dev, "sata int: phy%d FIS status: 0x%x\n",
+ phy_no, fis->status);
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ sas_phy->oob_mode = SATA_OOB_MODE;
+ attached_sas_addr[0] = 0x50;
+ attached_sas_addr[HOST_NO_OFF] = shost->host_no;
+ attached_sas_addr[PHY_NO_OFF] = phy_no;
+ memcpy(sas_phy->attached_sas_addr,
+ attached_sas_addr,
+ SAS_ADDR_SIZE);
+ memcpy(sas_phy->frame_rcvd, fis,
+ sizeof(struct dev_to_host_fis));
+ phy->phy_type |= PORT_TYPE_SATA;
+ phy->identify.device_type = SAS_SATA_DEV;
+ phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+ } else {
+ u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd;
+ struct sas_identify_frame *id =
+ (struct sas_identify_frame *)frame_rcvd;
+
+ dev_info(dev, "phyup: phy%d link_rate=%d\n", phy_no, link_rate);
+ for (i = 0; i < IDENTIFY_REG_READ; i++) {
+ u32 idaf = hisi_sas_phy_read32(hisi_hba, phy_no,
+ RX_IDAF_DWORD0 + (i * 4));
+ frame_rcvd[i] = __swab32(idaf);
+ }
+ sas_phy->oob_mode = SAS_OOB_MODE;
+ memcpy(sas_phy->attached_sas_addr,
+ &id->sas_addr,
+ SAS_ADDR_SIZE);
+ phy->phy_type |= PORT_TYPE_SAS;
+ phy->identify.device_type = id->dev_type;
+ phy->frame_rcvd_size = sizeof(struct sas_identify_frame);
+ if (phy->identify.device_type == SAS_END_DEVICE)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != SAS_PHY_UNUSED)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SMP;
+ }
+
+ phy->port_id = port_id;
+ spin_lock(&phy->lock);
+ /* Delete timer and set phy_attached atomically */
+ timer_delete(&phy->timer);
+ phy->phy_attached = 1;
+ spin_unlock(&phy->lock);
+
+ /*
+ * Call pm_runtime_get_noresume() which pairs with
+ * hisi_sas_phyup_pm_work() -> pm_runtime_put_sync().
+ * For failure call pm_runtime_put() as we are in a hardirq context.
+ */
+ pm_runtime_get_noresume(dev);
+ res = hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP_PM);
+ if (!res)
+ pm_runtime_put(dev);
+
+ res = IRQ_HANDLED;
+
+end:
+ if (phy->reset_completion)
+ complete(phy->reset_completion);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+ CHL_INT0_SL_PHY_ENABLE_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 0);
+
+ return res;
+}
+
+static irqreturn_t phy_down_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ u32 phy_state, sl_ctrl, txid_auto;
+ struct device *dev = hisi_hba->dev;
+
+ atomic_inc(&phy->down_cnt);
+
+ timer_delete(&phy->timer);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1);
+
+ phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+ dev_info(dev, "phydown: phy%d phy_state=0x%x\n", phy_no, phy_state);
+ hisi_sas_phy_down(hisi_hba, phy_no, (phy_state & 1 << phy_no) ? 1 : 0,
+ GFP_ATOMIC);
+
+ sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL,
+ sl_ctrl&(~SL_CTA_MSK));
+
+ txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
+ txid_auto | CT3_MSK);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, CHL_INT0_NOT_RDY_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t phy_bcast_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ u32 bcast_status;
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 1);
+ bcast_status = hisi_sas_phy_read32(hisi_hba, phy_no, RX_PRIMS_STATUS);
+ if (bcast_status & RX_BCAST_CHG_MSK)
+ hisi_sas_phy_bcast(phy);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+ CHL_INT0_SL_RX_BCST_ACK_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t int_phy_up_down_bcast_v3_hw(int irq_no, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ u32 irq_msk;
+ int phy_no = 0;
+ irqreturn_t res = IRQ_NONE;
+
+ irq_msk = hisi_sas_read32(hisi_hba, CHNL_INT_STATUS)
+ & 0x11111111;
+ while (irq_msk) {
+ if (irq_msk & 1) {
+ u32 irq_value = hisi_sas_phy_read32(hisi_hba, phy_no,
+ CHL_INT0);
+ u32 phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+ int rdy = phy_state & (1 << phy_no);
+
+ if (rdy) {
+ if (irq_value & CHL_INT0_SL_PHY_ENABLE_MSK)
+ /* phy up */
+ if (phy_up_v3_hw(phy_no, hisi_hba)
+ == IRQ_HANDLED)
+ res = IRQ_HANDLED;
+ if (irq_value & CHL_INT0_SL_RX_BCST_ACK_MSK)
+ /* phy bcast */
+ if (phy_bcast_v3_hw(phy_no, hisi_hba)
+ == IRQ_HANDLED)
+ res = IRQ_HANDLED;
+ } else {
+ if (irq_value & CHL_INT0_NOT_RDY_MSK)
+ /* phy down */
+ if (phy_down_v3_hw(phy_no, hisi_hba)
+ == IRQ_HANDLED)
+ res = IRQ_HANDLED;
+ }
+ }
+ irq_msk >>= 4;
+ phy_no++;
+ }
+
+ return res;
+}
+
+static const struct hisi_sas_hw_error port_axi_error[] = {
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_ECC_MB_ERR_OFF),
+ .msg = "dmac_tx_ecc_bad_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_ECC_MB_ERR_OFF),
+ .msg = "dmac_rx_ecc_bad_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_WR_ERR_OFF),
+ .msg = "dma_tx_axi_wr_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_RD_ERR_OFF),
+ .msg = "dma_tx_axi_rd_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_WR_ERR_OFF),
+ .msg = "dma_rx_axi_wr_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF),
+ .msg = "dma_rx_axi_rd_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_FIFO_ERR_OFF),
+ .msg = "dma_tx_fifo_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_FIFO_ERR_OFF),
+ .msg = "dma_rx_fifo_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_TX_AXI_RUSER_ERR_OFF),
+ .msg = "dma_tx_axi_ruser_err",
+ },
+ {
+ .irq_msk = BIT(CHL_INT1_DMAC_RX_AXI_RUSER_ERR_OFF),
+ .msg = "dma_rx_axi_ruser_err",
+ },
+};
+
+static void handle_chl_int1_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT1);
+ u32 irq_msk = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT1_MSK);
+ struct device *dev = hisi_hba->dev;
+ int i;
+
+ irq_value &= ~irq_msk;
+ if (!irq_value) {
+ dev_warn(dev, "phy%d channel int 1 received with status bits cleared\n",
+ phy_no);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(port_axi_error); i++) {
+ const struct hisi_sas_hw_error *error = &port_axi_error[i];
+
+ if (!(irq_value & error->irq_msk))
+ continue;
+
+ dev_err(dev, "%s error (phy%d 0x%x) found!\n",
+ error->msg, phy_no, irq_value);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT1, irq_value);
+}
+
+static void phy_get_events_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_phy *sphy = sas_phy->phy;
+ unsigned long flags;
+ u32 reg_value;
+
+ spin_lock_irqsave(&phy->lock, flags);
+
+ /* loss dword sync */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DWS_LOST);
+ sphy->loss_of_dword_sync_count += reg_value;
+
+ /* phy reset problem */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_RESET_PROB);
+ sphy->phy_reset_problem_count += reg_value;
+
+ /* invalid dword */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_INVLD_DW);
+ sphy->invalid_dword_count += reg_value;
+
+ /* disparity err */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_DISP_ERR);
+ sphy->running_disparity_error_count += reg_value;
+
+ /* code violation error */
+ reg_value = hisi_sas_phy_read32(hisi_hba, phy_no, ERR_CNT_CODE_ERR);
+ phy->code_violation_err_count += reg_value;
+
+ spin_unlock_irqrestore(&phy->lock, flags);
+}
+
+static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 irq_msk = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2_MSK);
+ u32 irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2);
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct pci_dev *pci_dev = hisi_hba->pci_dev;
+ struct device *dev = hisi_hba->dev;
+ static const u32 msk = BIT(CHL_INT2_RX_DISP_ERR_OFF) |
+ BIT(CHL_INT2_RX_CODE_ERR_OFF) |
+ BIT(CHL_INT2_RX_INVLD_DW_OFF);
+
+ irq_value &= ~irq_msk;
+ if (!irq_value) {
+ dev_warn(dev, "phy%d channel int 2 received with status bits cleared\n",
+ phy_no);
+ return;
+ }
+
+ if (irq_value & BIT(CHL_INT2_SL_IDAF_TOUT_CONF_OFF)) {
+ dev_warn(dev, "phy%d identify timeout\n", phy_no);
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+ }
+
+ if (irq_value & BIT(CHL_INT2_STP_LINK_TIMEOUT_OFF)) {
+ u32 reg_value = hisi_sas_phy_read32(hisi_hba, phy_no,
+ STP_LINK_TIMEOUT_STATE);
+
+ dev_warn(dev, "phy%d stp link timeout (0x%x)\n",
+ phy_no, reg_value);
+ if (reg_value & BIT(LINK_RESET_TIMEOUT_OFF))
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+ }
+
+ if (pci_dev->revision > 0x20 && (irq_value & msk)) {
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_phy *sphy = sas_phy->phy;
+
+ phy_get_events_v3_hw(hisi_hba, phy_no);
+
+ if (irq_value & BIT(CHL_INT2_RX_INVLD_DW_OFF))
+ dev_info(dev, "phy%d invalid dword cnt: %u\n", phy_no,
+ sphy->invalid_dword_count);
+
+ if (irq_value & BIT(CHL_INT2_RX_CODE_ERR_OFF))
+ dev_info(dev, "phy%d code violation cnt: %u\n", phy_no,
+ phy->code_violation_err_count);
+
+ if (irq_value & BIT(CHL_INT2_RX_DISP_ERR_OFF))
+ dev_info(dev, "phy%d disparity error cnt: %u\n", phy_no,
+ sphy->running_disparity_error_count);
+ }
+
+ if ((irq_value & BIT(CHL_INT2_RX_INVLD_DW_OFF)) &&
+ (pci_dev->revision == 0x20)) {
+ u32 reg_value;
+ int rc;
+
+ rc = hisi_sas_read32_poll_timeout_atomic(
+ HILINK_ERR_DFX, reg_value,
+ !((reg_value >> 8) & BIT(phy_no)),
+ 1000, 10000);
+ if (rc)
+ hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET);
+ }
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, irq_value);
+}
+
+static void handle_chl_int0_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0);
+
+ if (irq_value0 & CHL_INT0_PHY_RDY_MSK)
+ hisi_sas_phy_oob_ready(hisi_hba, phy_no);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
+ irq_value0 & (~CHL_INT0_SL_RX_BCST_ACK_MSK)
+ & (~CHL_INT0_SL_PHY_ENABLE_MSK)
+ & (~CHL_INT0_NOT_RDY_MSK));
+}
+
+static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ u32 irq_msk;
+ int phy_no = 0;
+
+ irq_msk = hisi_sas_read32(hisi_hba, CHNL_INT_STATUS) & CHNL_INT_STS_MSK;
+
+ while (irq_msk) {
+ if (irq_msk & (CHNL_INT_STS_INT0_MSK << (phy_no * CHNL_WIDTH)))
+ handle_chl_int0_v3_hw(hisi_hba, phy_no);
+
+ if (irq_msk & (CHNL_INT_STS_INT1_MSK << (phy_no * CHNL_WIDTH)))
+ handle_chl_int1_v3_hw(hisi_hba, phy_no);
+
+ if (irq_msk & (CHNL_INT_STS_INT2_MSK << (phy_no * CHNL_WIDTH)))
+ handle_chl_int2_v3_hw(hisi_hba, phy_no);
+
+ irq_msk &= ~(CHNL_INT_STS_PHY_MSK << (phy_no * CHNL_WIDTH));
+ phy_no++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct hisi_sas_hw_error multi_bit_ecc_errors[] = {
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_DQE_ECC_MB_OFF),
+ .msk = HGC_DQE_ECC_MB_ADDR_MSK,
+ .shift = HGC_DQE_ECC_MB_ADDR_OFF,
+ .msg = "hgc_dqe_eccbad_intr",
+ .reg = HGC_DQE_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_IOST_ECC_MB_OFF),
+ .msk = HGC_IOST_ECC_MB_ADDR_MSK,
+ .shift = HGC_IOST_ECC_MB_ADDR_OFF,
+ .msg = "hgc_iost_eccbad_intr",
+ .reg = HGC_IOST_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_ITCT_ECC_MB_OFF),
+ .msk = HGC_ITCT_ECC_MB_ADDR_MSK,
+ .shift = HGC_ITCT_ECC_MB_ADDR_OFF,
+ .msg = "hgc_itct_eccbad_intr",
+ .reg = HGC_ITCT_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_IOSTLIST_ECC_MB_OFF),
+ .msk = HGC_LM_DFX_STATUS2_IOSTLIST_MSK,
+ .shift = HGC_LM_DFX_STATUS2_IOSTLIST_OFF,
+ .msg = "hgc_iostl_eccbad_intr",
+ .reg = HGC_LM_DFX_STATUS2,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_ITCTLIST_ECC_MB_OFF),
+ .msk = HGC_LM_DFX_STATUS2_ITCTLIST_MSK,
+ .shift = HGC_LM_DFX_STATUS2_ITCTLIST_OFF,
+ .msg = "hgc_itctl_eccbad_intr",
+ .reg = HGC_LM_DFX_STATUS2,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_CQE_ECC_MB_OFF),
+ .msk = HGC_CQE_ECC_MB_ADDR_MSK,
+ .shift = HGC_CQE_ECC_MB_ADDR_OFF,
+ .msg = "hgc_cqe_eccbad_intr",
+ .reg = HGC_CQE_ECC_ADDR,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM0_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM0_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM0_OFF,
+ .msg = "rxm_mem0_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM1_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM1_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM1_OFF,
+ .msg = "rxm_mem1_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM2_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS14_MEM2_MSK,
+ .shift = HGC_RXM_DFX_STATUS14_MEM2_OFF,
+ .msg = "rxm_mem2_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS14,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_NCQ_MEM3_ECC_MB_OFF),
+ .msk = HGC_RXM_DFX_STATUS15_MEM3_MSK,
+ .shift = HGC_RXM_DFX_STATUS15_MEM3_OFF,
+ .msg = "rxm_mem3_eccbad_intr",
+ .reg = HGC_RXM_DFX_STATUS15,
+ },
+ {
+ .irq_msk = BIT(SAS_ECC_INTR_OOO_RAM_ECC_MB_OFF),
+ .msk = AM_ROB_ECC_ERR_ADDR_MSK,
+ .shift = AM_ROB_ECC_ERR_ADDR_OFF,
+ .msg = "ooo_ram_eccbad_intr",
+ .reg = AM_ROB_ECC_ERR_ADDR,
+ },
+};
+
+static void multi_bit_ecc_error_process_v3_hw(struct hisi_hba *hisi_hba,
+ u32 irq_value)
+{
+ struct device *dev = hisi_hba->dev;
+ const struct hisi_sas_hw_error *ecc_error;
+ u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(multi_bit_ecc_errors); i++) {
+ ecc_error = &multi_bit_ecc_errors[i];
+ if (irq_value & ecc_error->irq_msk) {
+ val = hisi_sas_read32(hisi_hba, ecc_error->reg);
+ val &= ecc_error->msk;
+ val >>= ecc_error->shift;
+ dev_err(dev, "%s (0x%x) found: mem addr is 0x%08X\n",
+ ecc_error->msg, irq_value, val);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+ }
+}
+
+static void fatal_ecc_int_v3_hw(struct hisi_hba *hisi_hba)
+{
+ u32 irq_value, irq_msk;
+
+ irq_msk = hisi_sas_read32(hisi_hba, SAS_ECC_INTR_MSK);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0xffffffff);
+
+ irq_value = hisi_sas_read32(hisi_hba, SAS_ECC_INTR);
+ if (irq_value)
+ multi_bit_ecc_error_process_v3_hw(hisi_hba, irq_value);
+
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR, irq_value);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, irq_msk);
+}
+
+static const struct hisi_sas_hw_error axi_error[] = {
+ { .msk = BIT(0), .msg = "IOST_AXI_W_ERR" },
+ { .msk = BIT(1), .msg = "IOST_AXI_R_ERR" },
+ { .msk = BIT(2), .msg = "ITCT_AXI_W_ERR" },
+ { .msk = BIT(3), .msg = "ITCT_AXI_R_ERR" },
+ { .msk = BIT(4), .msg = "SATA_AXI_W_ERR" },
+ { .msk = BIT(5), .msg = "SATA_AXI_R_ERR" },
+ { .msk = BIT(6), .msg = "DQE_AXI_R_ERR" },
+ { .msk = BIT(7), .msg = "CQE_AXI_W_ERR" },
+ {}
+};
+
+static const struct hisi_sas_hw_error fifo_error[] = {
+ { .msk = BIT(8), .msg = "CQE_WINFO_FIFO" },
+ { .msk = BIT(9), .msg = "CQE_MSG_FIFIO" },
+ { .msk = BIT(10), .msg = "GETDQE_FIFO" },
+ { .msk = BIT(11), .msg = "CMDP_FIFO" },
+ { .msk = BIT(12), .msg = "AWTCTRL_FIFO" },
+ {}
+};
+
+static const struct hisi_sas_hw_error fatal_axi_error[] = {
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_WP_DEPTH_OFF),
+ .msg = "write pointer and depth",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_IPTT_SLOT_NOMATCH_OFF),
+ .msg = "iptt no match slot",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_RP_DEPTH_OFF),
+ .msg = "read pointer and depth",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_AXI_OFF),
+ .reg = HGC_AXI_FIFO_ERR_INFO,
+ .sub = axi_error,
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_FIFO_OFF),
+ .reg = HGC_AXI_FIFO_ERR_INFO,
+ .sub = fifo_error,
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_LM_OFF),
+ .msg = "LM add/fetch list",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_ABT_OFF),
+ .msg = "SAS_HGC_ABT fetch LM list",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_DQE_POISON_OFF),
+ .msg = "read dqe poison",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_IOST_POISON_OFF),
+ .msg = "read iost poison",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_ITCT_POISON_OFF),
+ .msg = "read itct poison",
+ },
+ {
+ .irq_msk = BIT(ENT_INT_SRC3_ITCT_NCQ_POISON_OFF),
+ .msg = "read itct ncq poison",
+ },
+
+};
+
+static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
+{
+ u32 irq_value, irq_msk;
+ struct hisi_hba *hisi_hba = p;
+ struct device *dev = hisi_hba->dev;
+ struct pci_dev *pdev = hisi_hba->pci_dev;
+ int i;
+
+ irq_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, irq_msk | 0x1df00);
+
+ irq_value = hisi_sas_read32(hisi_hba, ENT_INT_SRC3);
+ irq_value &= ~irq_msk;
+
+ for (i = 0; i < ARRAY_SIZE(fatal_axi_error); i++) {
+ const struct hisi_sas_hw_error *error = &fatal_axi_error[i];
+
+ if (!(irq_value & error->irq_msk))
+ continue;
+
+ if (error->sub) {
+ const struct hisi_sas_hw_error *sub = error->sub;
+ u32 err_value = hisi_sas_read32(hisi_hba, error->reg);
+
+ for (; sub->msk || sub->msg; sub++) {
+ if (!(err_value & sub->msk))
+ continue;
+
+ dev_err(dev, "%s error (0x%x) found!\n",
+ sub->msg, irq_value);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+ } else {
+ dev_err(dev, "%s error (0x%x) found!\n",
+ error->msg, irq_value);
+ queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+ }
+
+ if (pdev->revision < 0x21) {
+ u32 reg_val;
+
+ reg_val = hisi_sas_read32(hisi_hba,
+ AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL);
+ reg_val |= AM_CTRL_SHUTDOWN_REQ_MSK;
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL, reg_val);
+ }
+ }
+
+ fatal_ecc_int_v3_hw(hisi_hba);
+
+ if (irq_value & BIT(ENT_INT_SRC3_ITC_INT_OFF)) {
+ u32 reg_val = hisi_sas_read32(hisi_hba, ITCT_CLR);
+ u32 dev_id = reg_val & ITCT_DEV_MSK;
+ struct hisi_sas_device *sas_dev =
+ &hisi_hba->devices[dev_id];
+
+ hisi_sas_write32(hisi_hba, ITCT_CLR, 0);
+ dev_dbg(dev, "clear ITCT ok\n");
+ complete(sas_dev->completion);
+ }
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC3, irq_value & 0x1df00);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, irq_msk);
+
+ return IRQ_HANDLED;
+}
+
+static bool is_ncq_err_v3_hw(struct hisi_sas_complete_v3_hdr *complete_hdr)
+{
+ u32 dw0, dw3;
+
+ dw0 = le32_to_cpu(complete_hdr->dw0);
+ dw3 = le32_to_cpu(complete_hdr->dw3);
+
+ return (dw0 & ERR_PHASE_RESPONSE_FRAME_REV_STAGE_MSK) &&
+ (dw3 & FIS_TYPE_SDB_MSK) &&
+ (dw3 & FIS_ATA_STATUS_ERR_MSK);
+}
+
+static bool
+slot_err_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task,
+ struct hisi_sas_slot *slot)
+{
+ struct task_status_struct *ts = &task->task_status;
+ struct hisi_sas_complete_v3_hdr *complete_queue =
+ hisi_hba->complete_hdr[slot->cmplt_queue];
+ struct hisi_sas_complete_v3_hdr *complete_hdr =
+ &complete_queue[slot->cmplt_queue_slot];
+ struct hisi_sas_err_record_v3 *record =
+ hisi_sas_status_buf_addr_mem(slot);
+ u32 dma_rx_err_type = le32_to_cpu(record->dma_rx_err_type);
+ u32 trans_tx_fail_type = le32_to_cpu(record->trans_tx_fail_type);
+ u16 sipc_rx_err_type = le16_to_cpu(record->sipc_rx_err_type);
+ u32 dw3 = le32_to_cpu(complete_hdr->dw3);
+ u32 dw0 = le32_to_cpu(complete_hdr->dw0);
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ if (dma_rx_err_type & RX_DATA_LEN_UNDERFLOW_MSK) {
+ /*
+ * If returned response frame is incorrect because of data underflow,
+ * but I/O information has been written to the host memory, we examine
+ * response IU.
+ */
+ if (!(dw0 & CMPLT_HDR_RSPNS_GOOD_MSK) &&
+ (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK))
+ return false;
+
+ ts->residual = trans_tx_fail_type;
+ ts->stat = SAS_DATA_UNDERRUN;
+ } else if (dw3 & CMPLT_HDR_IO_IN_TARGET_MSK) {
+ ts->stat = SAS_QUEUE_FULL;
+ slot->abort = 1;
+ } else {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ }
+ break;
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ if ((dw0 & CMPLT_HDR_RSPNS_XFRD_MSK) &&
+ (sipc_rx_err_type & RX_FIS_STATUS_ERR_MSK)) {
+ if (task->ata_task.use_ncq) {
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+
+ sas_dev->dev_status = HISI_SAS_DEV_NCQ_ERR;
+ slot->abort = 1;
+ } else {
+ ts->stat = SAS_PROTO_RESPONSE;
+ }
+ } else if (dma_rx_err_type & RX_DATA_LEN_UNDERFLOW_MSK) {
+ ts->residual = trans_tx_fail_type;
+ ts->stat = SAS_DATA_UNDERRUN;
+ } else if ((dw3 & CMPLT_HDR_IO_IN_TARGET_MSK) ||
+ (dw3 & SATA_DISK_IN_ERROR_STATUS_MSK)) {
+ ts->stat = SAS_PHY_DOWN;
+ slot->abort = 1;
+ } else {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ }
+ if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK)
+ hisi_sas_sata_done(task, slot);
+ break;
+ case SAS_PROTOCOL_SMP:
+ ts->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+static void slot_complete_v3_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_device *sas_dev;
+ struct device *dev = hisi_hba->dev;
+ struct task_status_struct *ts;
+ struct domain_device *device;
+ struct sas_ha_struct *ha;
+ struct hisi_sas_complete_v3_hdr *complete_queue =
+ hisi_hba->complete_hdr[slot->cmplt_queue];
+ struct hisi_sas_complete_v3_hdr *complete_hdr =
+ &complete_queue[slot->cmplt_queue_slot];
+ unsigned long flags;
+ bool is_internal = slot->is_internal;
+ u32 dw0, dw1, dw3;
+
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return;
+
+ ts = &task->task_status;
+ device = task->dev;
+ ha = device->port->ha;
+ sas_dev = device->lldd_dev;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ memset(ts, 0, sizeof(*ts));
+ ts->resp = SAS_TASK_COMPLETE;
+
+ if (unlikely(!sas_dev)) {
+ dev_dbg(dev, "slot complete: port has not device\n");
+ ts->stat = SAS_PHY_DOWN;
+ goto out;
+ }
+
+ dw0 = le32_to_cpu(complete_hdr->dw0);
+ dw1 = le32_to_cpu(complete_hdr->dw1);
+ dw3 = le32_to_cpu(complete_hdr->dw3);
+
+ /*
+ * Use SAS+TMF status codes
+ */
+ switch ((dw0 & CMPLT_HDR_ABORT_STAT_MSK) >> CMPLT_HDR_ABORT_STAT_OFF) {
+ case STAT_IO_ABORTED:
+ /* this IO has been aborted by abort command */
+ ts->stat = SAS_ABORTED_TASK;
+ goto out;
+ case STAT_IO_COMPLETE:
+ /* internal abort command complete */
+ ts->stat = TMF_RESP_FUNC_SUCC;
+ goto out;
+ case STAT_IO_NO_DEVICE:
+ ts->stat = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ case STAT_IO_NOT_VALID:
+ /*
+ * abort single IO, the controller can't find the IO
+ */
+ ts->stat = TMF_RESP_FUNC_FAILED;
+ goto out;
+ default:
+ break;
+ }
+
+ /* check for erroneous completion */
+ if ((dw0 & CMPLT_HDR_CMPLT_MSK) == 0x3) {
+ u32 *error_info = hisi_sas_status_buf_addr_mem(slot);
+
+ if (slot_err_v3_hw(hisi_hba, task, slot)) {
+ if (ts->stat != SAS_DATA_UNDERRUN)
+ dev_info(dev, "erroneous completion iptt=%d task=%p dev id=%d addr=%016llx CQ hdr: 0x%x 0x%x 0x%x 0x%x Error info: 0x%x 0x%x 0x%x 0x%x\n",
+ slot->idx, task, sas_dev->device_id,
+ SAS_ADDR(device->sas_addr),
+ dw0, dw1, complete_hdr->act, dw3,
+ error_info[0], error_info[1],
+ error_info[2], error_info[3]);
+ if (unlikely(slot->abort)) {
+ if (dev_is_sata(device) && task->ata_task.use_ncq)
+ sas_ata_device_link_abort(device, true);
+ else
+ sas_task_abort(task);
+
+ return;
+ }
+ goto out;
+ }
+ }
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP: {
+ struct ssp_response_iu *iu =
+ hisi_sas_status_buf_addr_mem(slot) +
+ sizeof(struct hisi_sas_err_record);
+
+ sas_ssp_task_response(dev, task, iu);
+ break;
+ }
+ case SAS_PROTOCOL_SMP: {
+ struct scatterlist *sg_resp = &task->smp_task.smp_resp;
+ void *to = page_address(sg_page(sg_resp));
+
+ ts->stat = SAS_SAM_STAT_GOOD;
+
+ memcpy(to + sg_resp->offset,
+ hisi_sas_status_buf_addr_mem(slot) +
+ sizeof(struct hisi_sas_err_record),
+ sg_resp->length);
+ break;
+ }
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ ts->stat = SAS_SAM_STAT_GOOD;
+ if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK)
+ hisi_sas_sata_done(task, slot);
+ break;
+ default:
+ ts->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ break;
+ }
+
+ if (!slot->port->port_attached) {
+ dev_warn(dev, "slot complete: port %d has removed\n",
+ slot->port->sas_port.id);
+ ts->stat = SAS_PHY_DOWN;
+ }
+
+out:
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ dev_info(dev, "slot complete: task(%p) aborted\n", task);
+ return;
+ }
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ hisi_sas_slot_task_free(hisi_hba, task, slot, true);
+
+ if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) {
+ spin_lock_irqsave(&device->done_lock, flags);
+ if (test_bit(SAS_HA_FROZEN, &ha->state)) {
+ spin_unlock_irqrestore(&device->done_lock, flags);
+ dev_info(dev, "slot complete: task(%p) ignored\n",
+ task);
+ return;
+ }
+ spin_unlock_irqrestore(&device->done_lock, flags);
+ }
+
+ if (task->task_done)
+ task->task_done(task);
+}
+
+static int complete_v3_hw(struct hisi_sas_cq *cq)
+{
+ struct hisi_sas_complete_v3_hdr *complete_queue;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ u32 rd_point, wr_point;
+ int queue = cq->id;
+ int completed;
+
+ rd_point = cq->rd_point;
+ complete_queue = hisi_hba->complete_hdr[queue];
+
+ wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR +
+ (0x14 * queue));
+ completed = (wr_point + HISI_SAS_QUEUE_SLOTS - rd_point) % HISI_SAS_QUEUE_SLOTS;
+
+ while (rd_point != wr_point) {
+ struct hisi_sas_complete_v3_hdr *complete_hdr;
+ struct device *dev = hisi_hba->dev;
+ struct hisi_sas_slot *slot;
+ u32 dw0, dw1, dw3;
+ int iptt;
+
+ complete_hdr = &complete_queue[rd_point];
+ dw0 = le32_to_cpu(complete_hdr->dw0);
+ dw1 = le32_to_cpu(complete_hdr->dw1);
+ dw3 = le32_to_cpu(complete_hdr->dw3);
+
+ iptt = dw1 & CMPLT_HDR_IPTT_MSK;
+ if (unlikely((dw0 & CMPLT_HDR_CMPLT_MSK) == 0x3) &&
+ (dw3 & CMPLT_HDR_SATA_DISK_ERR_MSK)) {
+ int device_id = (dw1 & CMPLT_HDR_DEV_ID_MSK) >>
+ CMPLT_HDR_DEV_ID_OFF;
+ struct hisi_sas_itct *itct =
+ &hisi_hba->itct[device_id];
+ struct hisi_sas_device *sas_dev =
+ &hisi_hba->devices[device_id];
+ struct domain_device *device = sas_dev->sas_device;
+
+ dev_err(dev, "erroneous completion disk err dev id=%d sas_addr=0x%llx CQ hdr: 0x%x 0x%x 0x%x 0x%x\n",
+ device_id, itct->sas_addr, dw0, dw1,
+ complete_hdr->act, dw3);
+
+ if (is_ncq_err_v3_hw(complete_hdr))
+ sas_dev->dev_status = HISI_SAS_DEV_NCQ_ERR;
+
+ sas_ata_device_link_abort(device, true);
+ } else if (likely(iptt < HISI_SAS_COMMAND_ENTRIES_V3_HW)) {
+ slot = &hisi_hba->slot_info[iptt];
+ slot->cmplt_queue_slot = rd_point;
+ slot->cmplt_queue = queue;
+ slot_complete_v3_hw(hisi_hba, slot);
+ } else
+ dev_err(dev, "IPTT %d is invalid, discard it.\n", iptt);
+
+ if (++rd_point >= HISI_SAS_QUEUE_SLOTS)
+ rd_point = 0;
+ }
+
+ /* update rd_point */
+ cq->rd_point = rd_point;
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);
+ cond_resched();
+
+ return completed;
+}
+
+static int queue_complete_v3_hw(struct Scsi_Host *shost, unsigned int queue)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ struct hisi_sas_cq *cq = &hisi_hba->cq[queue];
+ int completed;
+
+ spin_lock(&cq->poll_lock);
+ completed = complete_v3_hw(cq);
+ spin_unlock(&cq->poll_lock);
+
+ return completed;
+}
+
+static irqreturn_t cq_thread_v3_hw(int irq_no, void *p)
+{
+ struct hisi_sas_cq *cq = p;
+
+ complete_v3_hw(cq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cq_interrupt_v3_hw(int irq_no, void *p)
+{
+ struct hisi_sas_cq *cq = p;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ int queue = cq->id;
+
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static void hisi_sas_v3_free_vectors(void *data)
+{
+ struct pci_dev *pdev = data;
+
+ pci_free_irq_vectors(pdev);
+}
+
+static int interrupt_preinit_v3_hw(struct hisi_hba *hisi_hba)
+{
+ /* Allocate all MSI vectors to avoid re-insertion issue */
+ int max_msi = HISI_SAS_MSI_COUNT_V3_HW;
+ int vectors, min_msi;
+ struct Scsi_Host *shost = hisi_hba->shost;
+ struct pci_dev *pdev = hisi_hba->pci_dev;
+ struct irq_affinity desc = {
+ .pre_vectors = BASE_VECTORS_V3_HW,
+ };
+
+ min_msi = MIN_AFFINE_VECTORS_V3_HW;
+ vectors = pci_alloc_irq_vectors_affinity(pdev,
+ min_msi, max_msi,
+ PCI_IRQ_MSI |
+ PCI_IRQ_AFFINITY,
+ &desc);
+ if (vectors < 0)
+ return -ENOENT;
+
+ hisi_hba->cq_nvecs = vectors - BASE_VECTORS_V3_HW - hisi_hba->iopoll_q_cnt;
+ shost->nr_hw_queues = hisi_hba->cq_nvecs + hisi_hba->iopoll_q_cnt;
+
+ return devm_add_action(&pdev->dev, hisi_sas_v3_free_vectors, pdev);
+}
+
+static int interrupt_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ struct pci_dev *pdev = hisi_hba->pci_dev;
+ int rc, i;
+
+ rc = devm_request_irq(dev, pci_irq_vector(pdev, IRQ_PHY_UP_DOWN_INDEX),
+ int_phy_up_down_bcast_v3_hw, 0,
+ DRV_NAME " phy", hisi_hba);
+ if (rc) {
+ dev_err(dev, "could not request phy interrupt, rc=%d\n", rc);
+ return -ENOENT;
+ }
+
+ rc = devm_request_irq(dev, pci_irq_vector(pdev, IRQ_CHL_INDEX),
+ int_chnl_int_v3_hw, 0,
+ DRV_NAME " channel", hisi_hba);
+ if (rc) {
+ dev_err(dev, "could not request chnl interrupt, rc=%d\n", rc);
+ return -ENOENT;
+ }
+
+ rc = devm_request_irq(dev, pci_irq_vector(pdev, IRQ_AXI_INDEX),
+ fatal_axi_int_v3_hw, 0,
+ DRV_NAME " fatal", hisi_hba);
+ if (rc) {
+ dev_err(dev, "could not request fatal interrupt, rc=%d\n", rc);
+ return -ENOENT;
+ }
+
+ if (hisi_sas_intr_conv)
+ dev_info(dev, "Enable interrupt converge\n");
+
+ for (i = 0; i < hisi_hba->cq_nvecs; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+ int nr = hisi_sas_intr_conv ? BASE_VECTORS_V3_HW :
+ BASE_VECTORS_V3_HW + i;
+ unsigned long irqflags = hisi_sas_intr_conv ? IRQF_SHARED :
+ IRQF_ONESHOT;
+
+ cq->irq_no = pci_irq_vector(pdev, nr);
+ rc = devm_request_threaded_irq(dev, cq->irq_no,
+ cq_interrupt_v3_hw,
+ cq_thread_v3_hw,
+ irqflags,
+ DRV_NAME " cq", cq);
+ if (rc) {
+ dev_err(dev, "could not request cq%d interrupt, rc=%d\n",
+ i, rc);
+ return -ENOENT;
+ }
+ cq->irq_mask = pci_irq_get_affinity(pdev, i + BASE_VECTORS_V3_HW);
+ if (!cq->irq_mask) {
+ dev_err(dev, "could not get cq%d irq affinity!\n", i);
+ return -ENOENT;
+ }
+ }
+
+ return 0;
+}
+
+static int hisi_sas_v3_init(struct hisi_hba *hisi_hba)
+{
+ int rc;
+
+ rc = hw_init_v3_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ rc = interrupt_init_v3_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static void phy_set_linkrate_v3_hw(struct hisi_hba *hisi_hba, int phy_no,
+ struct sas_phy_linkrates *r)
+{
+ enum sas_linkrate max = r->maximum_linkrate;
+ u32 prog_phy_link_rate = hisi_sas_phy_read32(hisi_hba, phy_no,
+ PROG_PHY_LINK_RATE);
+
+ prog_phy_link_rate &= ~CFG_PROG_PHY_LINK_RATE_MSK;
+ prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE,
+ prog_phy_link_rate);
+}
+
+static void interrupt_disable_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct pci_dev *pdev = hisi_hba->pci_dev;
+ int i;
+
+ synchronize_irq(pci_irq_vector(pdev, IRQ_PHY_UP_DOWN_INDEX));
+ synchronize_irq(pci_irq_vector(pdev, IRQ_CHL_INDEX));
+ synchronize_irq(pci_irq_vector(pdev, IRQ_AXI_INDEX));
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ hisi_sas_write32(hisi_hba, OQ0_INT_SRC_MSK + 0x4 * i, 0x1);
+
+ for (i = 0; i < hisi_hba->cq_nvecs; i++)
+ synchronize_irq(pci_irq_vector(pdev, i + BASE_VECTORS_V3_HW));
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0xffffffff);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0xffffffff);
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0xffffffff);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x1);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_PHY_ENA_MSK, 0x1);
+ hisi_sas_phy_write32(hisi_hba, i, SL_RX_BCAST_CHK_MSK, 0x1);
+ }
+}
+
+static u32 get_phys_state_v3_hw(struct hisi_hba *hisi_hba)
+{
+ return hisi_sas_read32(hisi_hba, PHY_STATE);
+}
+
+static int disable_host_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ u32 status, reg_val;
+ int rc;
+
+ hisi_sas_sync_poll_cqs(hisi_hba);
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0x0);
+
+ hisi_sas_stop_phys(hisi_hba);
+
+ mdelay(HISI_SAS_DELAY_FOR_PHY_DISABLE);
+
+ reg_val = hisi_sas_read32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL);
+ reg_val |= AM_CTRL_SHUTDOWN_REQ_MSK;
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL, reg_val);
+
+ /* wait until bus idle */
+ rc = hisi_sas_read32_poll_timeout(AXI_MASTER_CFG_BASE +
+ AM_CURR_TRANS_RETURN, status,
+ status == 0x3, 10, 100);
+ if (rc) {
+ dev_err(dev, "axi bus is not idle, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int soft_reset_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ interrupt_disable_v3_hw(hisi_hba);
+ rc = disable_host_v3_hw(hisi_hba);
+ if (rc) {
+ dev_err(dev, "soft reset: disable host failed rc=%d\n", rc);
+ return rc;
+ }
+
+ hisi_sas_init_mem(hisi_hba);
+
+ return hw_init_v3_hw(hisi_hba);
+}
+
+static int write_gpio_v3_hw(struct hisi_hba *hisi_hba, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data)
+{
+ struct device *dev = hisi_hba->dev;
+ u32 *data = (u32 *)write_data;
+ int i;
+
+ switch (reg_type) {
+ case SAS_GPIO_REG_TX:
+ if ((reg_index + reg_count) > ((hisi_hba->n_phy + 3) / 4)) {
+ dev_err(dev, "write gpio: invalid reg range[%d, %d]\n",
+ reg_index, reg_index + reg_count - 1);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < reg_count; i++)
+ hisi_sas_write32(hisi_hba,
+ SAS_GPIO_TX_0_1 + (reg_index + i) * 4,
+ data[i]);
+ break;
+ default:
+ dev_err(dev, "write gpio: unsupported or bad reg type %d\n",
+ reg_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba,
+ int delay_ms, int timeout_ms)
+{
+ struct device *dev = hisi_hba->dev;
+ int entries, entries_old = 0, time;
+
+ for (time = 0; time < timeout_ms; time += delay_ms) {
+ entries = hisi_sas_read32(hisi_hba, CQE_SEND_CNT);
+ if (entries == entries_old)
+ break;
+
+ entries_old = entries;
+ msleep(delay_ms);
+ }
+
+ if (time >= timeout_ms) {
+ dev_dbg(dev, "Wait commands complete timeout!\n");
+ return;
+ }
+
+ dev_dbg(dev, "wait commands complete %dms\n", time);
+}
+
+static ssize_t intr_conv_v3_hw_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%u\n", hisi_sas_intr_conv);
+}
+static DEVICE_ATTR_RO(intr_conv_v3_hw);
+
+static void config_intr_coal_v3_hw(struct hisi_hba *hisi_hba)
+{
+ /* config those registers between enable and disable PHYs */
+ hisi_sas_stop_phys(hisi_hba);
+ hisi_sas_write32(hisi_hba, INT_COAL_EN, 0x3);
+
+ if (hisi_hba->intr_coal_ticks == 0 ||
+ hisi_hba->intr_coal_count == 0) {
+ /* configure the interrupt coalescing timeout period 10us */
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0xa);
+ /* configure the count of CQ entries 10 */
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 0xa);
+ } else {
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME,
+ hisi_hba->intr_coal_ticks);
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT,
+ hisi_hba->intr_coal_count);
+ }
+ phys_init_v3_hw(hisi_hba);
+}
+
+static ssize_t intr_coal_ticks_v3_hw_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ hisi_hba->intr_coal_ticks);
+}
+
+static ssize_t intr_coal_ticks_v3_hw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ u32 intr_coal_ticks;
+ int ret;
+
+ ret = kstrtou32(buf, DECIMALISM_FLAG, &intr_coal_ticks);
+ if (ret) {
+ dev_err(dev, "Input data of interrupt coalesce unmatch\n");
+ return -EINVAL;
+ }
+
+ if (intr_coal_ticks >= BIT(TICKS_BIT_INDEX)) {
+ dev_err(dev, "intr_coal_ticks must be less than 2^24!\n");
+ return -EINVAL;
+ }
+
+ hisi_hba->intr_coal_ticks = intr_coal_ticks;
+
+ config_intr_coal_v3_hw(hisi_hba);
+
+ return count;
+}
+static DEVICE_ATTR_RW(intr_coal_ticks_v3_hw);
+
+static ssize_t intr_coal_count_v3_hw_show(struct device *dev,
+ struct device_attribute
+ *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ hisi_hba->intr_coal_count);
+}
+
+static ssize_t intr_coal_count_v3_hw_store(struct device *dev,
+ struct device_attribute
+ *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ u32 intr_coal_count;
+ int ret;
+
+ ret = kstrtou32(buf, DECIMALISM_FLAG, &intr_coal_count);
+ if (ret) {
+ dev_err(dev, "Input data of interrupt coalesce unmatch\n");
+ return -EINVAL;
+ }
+
+ if (intr_coal_count >= BIT(COUNT_BIT_INDEX)) {
+ dev_err(dev, "intr_coal_count must be less than 2^8!\n");
+ return -EINVAL;
+ }
+
+ hisi_hba->intr_coal_count = intr_coal_count;
+
+ config_intr_coal_v3_hw(hisi_hba);
+
+ return count;
+}
+static DEVICE_ATTR_RW(intr_coal_count_v3_hw);
+
+static ssize_t iopoll_q_cnt_v3_hw_show(struct device *dev,
+ struct device_attribute
+ *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ hisi_hba->iopoll_q_cnt);
+}
+static DEVICE_ATTR_RO(iopoll_q_cnt_v3_hw);
+
+static int sdev_configure_v3_hw(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev);
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ int ret = hisi_sas_sdev_configure(sdev, lim);
+ struct device *dev = hisi_hba->dev;
+
+ if (ret)
+ return ret;
+
+ if (sdev->type == TYPE_ENCLOSURE)
+ return 0;
+
+ if (!device_link_add(&sdev->sdev_gendev, dev,
+ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) {
+ if (pm_runtime_enabled(dev)) {
+ dev_info(dev, "add device link failed, disable runtime PM for the host\n");
+ pm_runtime_disable(dev);
+ }
+ }
+
+ return 0;
+}
+
+static struct attribute *host_v3_hw_attrs[] = {
+ &dev_attr_phy_event_threshold.attr,
+ &dev_attr_intr_conv_v3_hw.attr,
+ &dev_attr_intr_coal_ticks_v3_hw.attr,
+ &dev_attr_intr_coal_count_v3_hw.attr,
+ &dev_attr_iopoll_q_cnt_v3_hw.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(host_v3_hw);
+
+static const struct attribute_group *sdev_groups_v3_hw[] = {
+ &sas_ata_sdev_attr_group,
+ NULL
+};
+
+#define HISI_SAS_DEBUGFS_REG(x) {#x, x}
+
+struct hisi_sas_debugfs_reg_lu {
+ char *name;
+ int off;
+};
+
+struct hisi_sas_debugfs_reg {
+ const struct hisi_sas_debugfs_reg_lu *lu;
+ int count;
+ int base_off;
+};
+
+static const struct hisi_sas_debugfs_reg_lu debugfs_port_reg_lu[] = {
+ HISI_SAS_DEBUGFS_REG(PHY_CFG),
+ HISI_SAS_DEBUGFS_REG(HARD_PHY_LINKRATE),
+ HISI_SAS_DEBUGFS_REG(PROG_PHY_LINK_RATE),
+ HISI_SAS_DEBUGFS_REG(PHY_CTRL),
+ HISI_SAS_DEBUGFS_REG(SL_CFG),
+ HISI_SAS_DEBUGFS_REG(AIP_LIMIT),
+ HISI_SAS_DEBUGFS_REG(SL_CONTROL),
+ HISI_SAS_DEBUGFS_REG(RX_PRIMS_STATUS),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD0),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD1),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD2),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD3),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD4),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD5),
+ HISI_SAS_DEBUGFS_REG(TX_ID_DWORD6),
+ HISI_SAS_DEBUGFS_REG(TXID_AUTO),
+ HISI_SAS_DEBUGFS_REG(RX_IDAF_DWORD0),
+ HISI_SAS_DEBUGFS_REG(RXOP_CHECK_CFG_H),
+ HISI_SAS_DEBUGFS_REG(STP_LINK_TIMER),
+ HISI_SAS_DEBUGFS_REG(STP_LINK_TIMEOUT_STATE),
+ HISI_SAS_DEBUGFS_REG(CON_CFG_DRIVER),
+ HISI_SAS_DEBUGFS_REG(SAS_SSP_CON_TIMER_CFG),
+ HISI_SAS_DEBUGFS_REG(SAS_SMP_CON_TIMER_CFG),
+ HISI_SAS_DEBUGFS_REG(SAS_STP_CON_TIMER_CFG),
+ HISI_SAS_DEBUGFS_REG(CHL_INT0),
+ HISI_SAS_DEBUGFS_REG(CHL_INT1),
+ HISI_SAS_DEBUGFS_REG(CHL_INT2),
+ HISI_SAS_DEBUGFS_REG(CHL_INT0_MSK),
+ HISI_SAS_DEBUGFS_REG(CHL_INT1_MSK),
+ HISI_SAS_DEBUGFS_REG(CHL_INT2_MSK),
+ HISI_SAS_DEBUGFS_REG(SAS_EC_INT_COAL_TIME),
+ HISI_SAS_DEBUGFS_REG(CHL_INT_COAL_EN),
+ HISI_SAS_DEBUGFS_REG(SAS_RX_TRAIN_TIMER),
+ HISI_SAS_DEBUGFS_REG(PHY_CTRL_RDY_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_NOT_RDY_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_DWS_RESET_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_PHY_ENA_MSK),
+ HISI_SAS_DEBUGFS_REG(SL_RX_BCAST_CHK_MSK),
+ HISI_SAS_DEBUGFS_REG(PHYCTRL_OOB_RESTART_MSK),
+ HISI_SAS_DEBUGFS_REG(DMA_TX_STATUS),
+ HISI_SAS_DEBUGFS_REG(DMA_RX_STATUS),
+ HISI_SAS_DEBUGFS_REG(COARSETUNE_TIME),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_DWS_LOST),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_RESET_PROB),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_INVLD_DW),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_CODE_ERR),
+ HISI_SAS_DEBUGFS_REG(ERR_CNT_DISP_ERR),
+ {}
+};
+
+static const struct hisi_sas_debugfs_reg debugfs_port_reg = {
+ .lu = debugfs_port_reg_lu,
+ .count = PORT_REG_LENGTH,
+ .base_off = PORT_BASE,
+};
+
+static const struct hisi_sas_debugfs_reg_lu debugfs_global_reg_lu[] = {
+ HISI_SAS_DEBUGFS_REG(DLVRY_QUEUE_ENABLE),
+ HISI_SAS_DEBUGFS_REG(PHY_CONTEXT),
+ HISI_SAS_DEBUGFS_REG(PHY_STATE),
+ HISI_SAS_DEBUGFS_REG(PHY_PORT_NUM_MA),
+ HISI_SAS_DEBUGFS_REG(PHY_CONN_RATE),
+ HISI_SAS_DEBUGFS_REG(ITCT_CLR),
+ HISI_SAS_DEBUGFS_REG(IO_SATA_BROKEN_MSG_ADDR_LO),
+ HISI_SAS_DEBUGFS_REG(IO_SATA_BROKEN_MSG_ADDR_HI),
+ HISI_SAS_DEBUGFS_REG(SATA_INITI_D2H_STORE_ADDR_LO),
+ HISI_SAS_DEBUGFS_REG(SATA_INITI_D2H_STORE_ADDR_HI),
+ HISI_SAS_DEBUGFS_REG(CFG_MAX_TAG),
+ HISI_SAS_DEBUGFS_REG(TRANS_LOCK_ICT_TIME),
+ HISI_SAS_DEBUGFS_REG(HGC_SAS_TX_OPEN_FAIL_RETRY_CTRL),
+ HISI_SAS_DEBUGFS_REG(HGC_SAS_TXFAIL_RETRY_CTRL),
+ HISI_SAS_DEBUGFS_REG(HGC_GET_ITV_TIME),
+ HISI_SAS_DEBUGFS_REG(DEVICE_MSG_WORK_MODE),
+ HISI_SAS_DEBUGFS_REG(OPENA_WT_CONTI_TIME),
+ HISI_SAS_DEBUGFS_REG(I_T_NEXUS_LOSS_TIME),
+ HISI_SAS_DEBUGFS_REG(MAX_CON_TIME_LIMIT_TIME),
+ HISI_SAS_DEBUGFS_REG(BUS_INACTIVE_LIMIT_TIME),
+ HISI_SAS_DEBUGFS_REG(REJECT_TO_OPEN_LIMIT_TIME),
+ HISI_SAS_DEBUGFS_REG(CQ_INT_CONVERGE_EN),
+ HISI_SAS_DEBUGFS_REG(CFG_AGING_TIME),
+ HISI_SAS_DEBUGFS_REG(HGC_DFX_CFG2),
+ HISI_SAS_DEBUGFS_REG(CFG_ABT_SET_QUERY_IPTT),
+ HISI_SAS_DEBUGFS_REG(CFG_ABT_SET_IPTT_DONE),
+ HISI_SAS_DEBUGFS_REG(HGC_IOMB_PROC1_STATUS),
+ HISI_SAS_DEBUGFS_REG(CHNL_INT_STATUS),
+ HISI_SAS_DEBUGFS_REG(HGC_AXI_FIFO_ERR_INFO),
+ HISI_SAS_DEBUGFS_REG(INT_COAL_EN),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_COAL_TIME),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_COAL_CNT),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_COAL_TIME),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_COAL_CNT),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_SRC),
+ HISI_SAS_DEBUGFS_REG(OQ_INT_SRC_MSK),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC1),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC2),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC3),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK1),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK2),
+ HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK3),
+ HISI_SAS_DEBUGFS_REG(CHNL_PHYUPDOWN_INT_MSK),
+ HISI_SAS_DEBUGFS_REG(CHNL_ENT_INT_MSK),
+ HISI_SAS_DEBUGFS_REG(HGC_COM_INT_MSK),
+ HISI_SAS_DEBUGFS_REG(SAS_ECC_INTR),
+ HISI_SAS_DEBUGFS_REG(SAS_ECC_INTR_MSK),
+ HISI_SAS_DEBUGFS_REG(HGC_ERR_STAT_EN),
+ HISI_SAS_DEBUGFS_REG(CQE_SEND_CNT),
+ HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_DEPTH),
+ HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_WR_PTR),
+ HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_RD_PTR),
+ HISI_SAS_DEBUGFS_REG(HYPER_STREAM_ID_EN_CFG),
+ HISI_SAS_DEBUGFS_REG(OQ0_INT_SRC_MSK),
+ HISI_SAS_DEBUGFS_REG(COMPL_Q_0_DEPTH),
+ HISI_SAS_DEBUGFS_REG(COMPL_Q_0_WR_PTR),
+ HISI_SAS_DEBUGFS_REG(COMPL_Q_0_RD_PTR),
+ HISI_SAS_DEBUGFS_REG(AWQOS_AWCACHE_CFG),
+ HISI_SAS_DEBUGFS_REG(ARQOS_ARCACHE_CFG),
+ HISI_SAS_DEBUGFS_REG(HILINK_ERR_DFX),
+ HISI_SAS_DEBUGFS_REG(SAS_GPIO_CFG_0),
+ HISI_SAS_DEBUGFS_REG(SAS_GPIO_CFG_1),
+ HISI_SAS_DEBUGFS_REG(SAS_GPIO_TX_0_1),
+ HISI_SAS_DEBUGFS_REG(SAS_CFG_DRIVE_VLD),
+ {}
+};
+
+static const struct hisi_sas_debugfs_reg debugfs_global_reg = {
+ .lu = debugfs_global_reg_lu,
+ .count = GLOBAL_REG_LENGTH,
+};
+
+static const struct hisi_sas_debugfs_reg_lu debugfs_axi_reg_lu[] = {
+ HISI_SAS_DEBUGFS_REG(AM_CFG_MAX_TRANS),
+ HISI_SAS_DEBUGFS_REG(AM_CFG_SINGLE_PORT_MAX_TRANS),
+ HISI_SAS_DEBUGFS_REG(AXI_CFG),
+ HISI_SAS_DEBUGFS_REG(AM_ROB_ECC_ERR_ADDR),
+ {}
+};
+
+static const struct hisi_sas_debugfs_reg debugfs_axi_reg = {
+ .lu = debugfs_axi_reg_lu,
+ .count = AXI_REG_LENGTH,
+ .base_off = AXI_MASTER_CFG_BASE,
+};
+
+static const struct hisi_sas_debugfs_reg_lu debugfs_ras_reg_lu[] = {
+ HISI_SAS_DEBUGFS_REG(SAS_RAS_INTR0),
+ HISI_SAS_DEBUGFS_REG(SAS_RAS_INTR1),
+ HISI_SAS_DEBUGFS_REG(SAS_RAS_INTR0_MASK),
+ HISI_SAS_DEBUGFS_REG(SAS_RAS_INTR1_MASK),
+ HISI_SAS_DEBUGFS_REG(CFG_SAS_RAS_INTR_MASK),
+ HISI_SAS_DEBUGFS_REG(SAS_RAS_INTR2),
+ HISI_SAS_DEBUGFS_REG(SAS_RAS_INTR2_MASK),
+ {}
+};
+
+static const struct hisi_sas_debugfs_reg debugfs_ras_reg = {
+ .lu = debugfs_ras_reg_lu,
+ .count = RAS_REG_LENGTH,
+ .base_off = RAS_BASE,
+};
+
+static void debugfs_snapshot_prepare_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct Scsi_Host *shost = hisi_hba->shost;
+
+ scsi_block_requests(shost);
+ wait_cmds_complete_timeout_v3_hw(hisi_hba, WAIT_RETRY, WAIT_TMROUT);
+
+ set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+ hisi_sas_sync_cqs(hisi_hba);
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0);
+}
+
+static void debugfs_snapshot_restore_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct Scsi_Host *shost = hisi_hba->shost;
+
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+ (u32)((1ULL << hisi_hba->queue_count) - 1));
+
+ clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+ scsi_unblock_requests(shost);
+}
+
+static void read_iost_itct_cache_v3_hw(struct hisi_hba *hisi_hba,
+ enum hisi_sas_debugfs_cache_type type,
+ u32 *cache)
+{
+ u32 cache_dw_size = HISI_SAS_IOST_ITCT_CACHE_DW_SZ *
+ HISI_SAS_IOST_ITCT_CACHE_NUM;
+ struct device *dev = hisi_hba->dev;
+ u32 *buf = cache;
+ u32 i, val;
+
+ hisi_sas_write32(hisi_hba, TAB_RD_TYPE, type);
+
+ for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_DW_SZ; i++) {
+ val = hisi_sas_read32(hisi_hba, TAB_DFX);
+ if (val == 0xffffffff)
+ break;
+ }
+
+ if (val != 0xffffffff) {
+ dev_err(dev, "Issue occurred in reading IOST/ITCT cache!\n");
+ return;
+ }
+
+ memset(buf, 0, cache_dw_size * BYTE_TO_DW);
+ buf[0] = val;
+
+ for (i = 1; i < cache_dw_size; i++)
+ buf[i] = hisi_sas_read32(hisi_hba, TAB_DFX);
+}
+
+static void hisi_sas_bist_test_prep_v3_hw(struct hisi_hba *hisi_hba)
+{
+ u32 reg_val;
+ int phy_no = hisi_hba->debugfs_bist_phy_no;
+ int i;
+
+ /* disable PHY */
+ hisi_sas_phy_enable(hisi_hba, phy_no, 0);
+
+ /* update FFE */
+ for (i = 0; i < FFE_CFG_MAX; i++)
+ hisi_sas_phy_write32(hisi_hba, phy_no, TXDEEMPH_G1 + (i * 0x4),
+ hisi_hba->debugfs_bist_ffe[phy_no][i]);
+
+ /* disable ALOS */
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, SERDES_CFG);
+ reg_val |= CFG_ALOS_CHK_DISABLE_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SERDES_CFG, reg_val);
+}
+
+static void hisi_sas_bist_test_restore_v3_hw(struct hisi_hba *hisi_hba)
+{
+ u32 reg_val;
+ int phy_no = hisi_hba->debugfs_bist_phy_no;
+
+ /* disable loopback */
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, SAS_PHY_BIST_CTRL);
+ reg_val &= ~(CFG_RX_BIST_EN_MSK | CFG_TX_BIST_EN_MSK |
+ CFG_BIST_TEST_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SAS_PHY_BIST_CTRL, reg_val);
+
+ /* enable ALOS */
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, SERDES_CFG);
+ reg_val &= ~CFG_ALOS_CHK_DISABLE_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SERDES_CFG, reg_val);
+
+ /* restore the linkrate */
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, PROG_PHY_LINK_RATE);
+ /* init OOB link rate as 1.5 Gbits */
+ reg_val &= ~CFG_PROG_OOB_PHY_LINK_RATE_MSK;
+ reg_val |= (SAS_LINK_RATE_1_5_GBPS << CFG_PROG_OOB_PHY_LINK_RATE_OFF);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE, reg_val);
+
+ /* enable PHY */
+ hisi_sas_phy_enable(hisi_hba, phy_no, 1);
+}
+
+#define SAS_PHY_BIST_CODE_INIT 0x1
+#define SAS_PHY_BIST_CODE1_INIT 0X80
+#define SAS_PHY_BIST_INIT_DELAY 100
+#define SAS_PHY_BIST_LOOP_TEST_0 1
+#define SAS_PHY_BIST_LOOP_TEST_1 2
+static int debugfs_set_bist_v3_hw(struct hisi_hba *hisi_hba, bool enable)
+{
+ u32 reg_val, mode_tmp;
+ u32 linkrate = hisi_hba->debugfs_bist_linkrate;
+ u32 phy_no = hisi_hba->debugfs_bist_phy_no;
+ u32 *ffe = hisi_hba->debugfs_bist_ffe[phy_no];
+ u32 code_mode = hisi_hba->debugfs_bist_code_mode;
+ u32 path_mode = hisi_hba->debugfs_bist_mode;
+ u32 *fix_code = &hisi_hba->debugfs_bist_fixed_code[0];
+ struct device *dev = hisi_hba->dev;
+
+ dev_info(dev, "BIST info:phy%d link_rate=%d code_mode=%d path_mode=%d ffe={0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x} fixed_code={0x%x, 0x%x}\n",
+ phy_no, linkrate, code_mode, path_mode,
+ ffe[FFE_SAS_1_5_GBPS], ffe[FFE_SAS_3_0_GBPS],
+ ffe[FFE_SAS_6_0_GBPS], ffe[FFE_SAS_12_0_GBPS],
+ ffe[FFE_SATA_1_5_GBPS], ffe[FFE_SATA_3_0_GBPS],
+ ffe[FFE_SATA_6_0_GBPS], fix_code[FIXED_CODE],
+ fix_code[FIXED_CODE_1]);
+ mode_tmp = path_mode ? SAS_PHY_BIST_LOOP_TEST_1 :
+ SAS_PHY_BIST_LOOP_TEST_0;
+ if (enable) {
+ /* some preparations before bist test */
+ hisi_sas_bist_test_prep_v3_hw(hisi_hba);
+
+ /* set linkrate of bit test */
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no,
+ PROG_PHY_LINK_RATE);
+ reg_val &= ~CFG_PROG_OOB_PHY_LINK_RATE_MSK;
+ reg_val |= (linkrate << CFG_PROG_OOB_PHY_LINK_RATE_OFF);
+ hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE,
+ reg_val);
+
+ /* set code mode of bit test */
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no,
+ SAS_PHY_BIST_CTRL);
+ reg_val &= ~(CFG_BIST_MODE_SEL_MSK | CFG_LOOP_TEST_MODE_MSK |
+ CFG_RX_BIST_EN_MSK | CFG_TX_BIST_EN_MSK |
+ CFG_BIST_TEST_MSK);
+ reg_val |= ((code_mode << CFG_BIST_MODE_SEL_OFF) |
+ (mode_tmp << CFG_LOOP_TEST_MODE_OFF) |
+ CFG_BIST_TEST_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SAS_PHY_BIST_CTRL,
+ reg_val);
+
+ /* set the bist init value */
+ if (code_mode == HISI_SAS_BIST_CODE_MODE_FIXED_DATA) {
+ reg_val = hisi_hba->debugfs_bist_fixed_code[0];
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ SAS_PHY_BIST_CODE, reg_val);
+
+ reg_val = hisi_hba->debugfs_bist_fixed_code[1];
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ SAS_PHY_BIST_CODE1, reg_val);
+ } else {
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ SAS_PHY_BIST_CODE,
+ SAS_PHY_BIST_CODE_INIT);
+ hisi_sas_phy_write32(hisi_hba, phy_no,
+ SAS_PHY_BIST_CODE1,
+ SAS_PHY_BIST_CODE1_INIT);
+ }
+
+ mdelay(SAS_PHY_BIST_INIT_DELAY);
+ reg_val |= (CFG_RX_BIST_EN_MSK | CFG_TX_BIST_EN_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, SAS_PHY_BIST_CTRL,
+ reg_val);
+
+ /* clear error bit */
+ mdelay(SAS_PHY_BIST_INIT_DELAY);
+ hisi_sas_phy_read32(hisi_hba, phy_no, SAS_BIST_ERR_CNT);
+ } else {
+ /* disable bist test and recover it */
+ hisi_hba->debugfs_bist_cnt += hisi_sas_phy_read32(hisi_hba,
+ phy_no, SAS_BIST_ERR_CNT);
+ hisi_sas_bist_test_restore_v3_hw(hisi_hba);
+ }
+
+ return 0;
+}
+
+static void hisi_sas_map_queues(struct Scsi_Host *shost)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ struct blk_mq_queue_map *qmap;
+ int i, qoff;
+
+ for (i = 0, qoff = 0; i < shost->nr_maps; i++) {
+ qmap = &shost->tag_set.map[i];
+ if (i == HCTX_TYPE_DEFAULT) {
+ qmap->nr_queues = hisi_hba->cq_nvecs;
+ } else if (i == HCTX_TYPE_POLL) {
+ qmap->nr_queues = hisi_hba->iopoll_q_cnt;
+ } else {
+ qmap->nr_queues = 0;
+ continue;
+ }
+
+ /* At least one interrupt hardware queue */
+ if (!qmap->nr_queues)
+ WARN_ON(i == HCTX_TYPE_DEFAULT);
+ qmap->queue_offset = qoff;
+ if (i == HCTX_TYPE_POLL)
+ blk_mq_map_queues(qmap);
+ else
+ blk_mq_map_hw_queues(qmap, hisi_hba->dev,
+ BASE_VECTORS_V3_HW);
+ qoff += qmap->nr_queues;
+ }
+}
+
+static const struct scsi_host_template sht_v3_hw = {
+ LIBSAS_SHT_BASE_NO_SLAVE_INIT
+ .sdev_configure = sdev_configure_v3_hw,
+ .scan_finished = hisi_sas_scan_finished,
+ .scan_start = hisi_sas_scan_start,
+ .map_queues = hisi_sas_map_queues,
+ .sg_tablesize = HISI_SAS_SGE_PAGE_CNT,
+ .sg_prot_tablesize = HISI_SAS_SGE_PAGE_CNT,
+ .sdev_init = hisi_sas_sdev_init,
+ .shost_groups = host_v3_hw_groups,
+ .sdev_groups = sdev_groups_v3_hw,
+ .tag_alloc_policy_rr = true,
+ .host_reset = hisi_sas_host_reset,
+ .host_tagset = 1,
+ .mq_poll = queue_complete_v3_hw,
+};
+
+static const struct hisi_sas_hw hisi_sas_v3_hw = {
+ .setup_itct = setup_itct_v3_hw,
+ .get_wideport_bitmap = get_wideport_bitmap_v3_hw,
+ .complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr),
+ .clear_itct = clear_itct_v3_hw,
+ .sl_notify_ssp = sl_notify_ssp_v3_hw,
+ .prep_ssp = prep_ssp_v3_hw,
+ .prep_smp = prep_smp_v3_hw,
+ .prep_stp = prep_ata_v3_hw,
+ .prep_abort = prep_abort_v3_hw,
+ .start_delivery = start_delivery_v3_hw,
+ .phys_init = phys_init_v3_hw,
+ .phy_start = start_phy_v3_hw,
+ .phy_disable = disable_phy_v3_hw,
+ .phy_hard_reset = phy_hard_reset_v3_hw,
+ .phy_get_max_linkrate = phy_get_max_linkrate_v3_hw,
+ .phy_set_linkrate = phy_set_linkrate_v3_hw,
+ .dereg_device = dereg_device_v3_hw,
+ .soft_reset = soft_reset_v3_hw,
+ .get_phys_state = get_phys_state_v3_hw,
+ .get_events = phy_get_events_v3_hw,
+ .write_gpio = write_gpio_v3_hw,
+ .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v3_hw,
+ .debugfs_snapshot_regs = debugfs_snapshot_regs_v3_hw,
+};
+
+static int check_fw_info_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+
+ if (hisi_hba->n_phy < 0 || hisi_hba->n_phy > 8) {
+ dev_err(dev, "invalid phy number from FW\n");
+ return -EINVAL;
+ }
+
+ if (hisi_hba->queue_count < 0 || hisi_hba->queue_count > 16) {
+ dev_err(dev, "invalid queue count from FW\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct Scsi_Host *
+hisi_sas_shost_alloc_pci(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct hisi_hba *hisi_hba;
+ struct device *dev = &pdev->dev;
+
+ shost = scsi_host_alloc(&sht_v3_hw, sizeof(*hisi_hba));
+ if (!shost) {
+ dev_err(dev, "shost alloc failed\n");
+ return NULL;
+ }
+ hisi_hba = shost_priv(shost);
+
+ INIT_WORK(&hisi_hba->rst_work, hisi_sas_rst_work_handler);
+ hisi_hba->hw = &hisi_sas_v3_hw;
+ hisi_hba->pci_dev = pdev;
+ hisi_hba->dev = dev;
+ hisi_hba->shost = shost;
+ SHOST_TO_SAS_HA(shost) = &hisi_hba->sha;
+
+ if (prot_mask & ~HISI_SAS_PROT_MASK)
+ dev_err(dev, "unsupported protection mask 0x%x, using default (0x0)\n",
+ prot_mask);
+ else
+ hisi_hba->prot_mask = prot_mask;
+
+ if (hisi_sas_get_fw_info(hisi_hba) < 0)
+ goto err_out;
+
+ if (check_fw_info_v3_hw(hisi_hba) < 0)
+ goto err_out;
+
+ if (experimental_iopoll_q_cnt < 0 ||
+ experimental_iopoll_q_cnt >= hisi_hba->queue_count)
+ dev_err(dev, "iopoll queue count %d cannot exceed or equal 16, using default 0\n",
+ experimental_iopoll_q_cnt);
+ else
+ hisi_hba->iopoll_q_cnt = experimental_iopoll_q_cnt;
+
+ if (hisi_sas_alloc(hisi_hba)) {
+ hisi_sas_free(hisi_hba);
+ goto err_out;
+ }
+
+ return shost;
+err_out:
+ scsi_host_put(shost);
+ dev_err(dev, "shost alloc failed\n");
+ return NULL;
+}
+
+static void debugfs_snapshot_cq_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int queue_entry_size = hisi_hba->hw->complete_hdr_size;
+ int dump_index = hisi_hba->debugfs_dump_index;
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ memcpy(hisi_hba->debugfs_cq[dump_index][i].complete_hdr,
+ hisi_hba->complete_hdr[i],
+ HISI_SAS_QUEUE_SLOTS * queue_entry_size);
+}
+
+static void debugfs_snapshot_dq_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int queue_entry_size = sizeof(struct hisi_sas_cmd_hdr);
+ int dump_index = hisi_hba->debugfs_dump_index;
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cmd_hdr *debugfs_cmd_hdr, *cmd_hdr;
+ int j;
+
+ debugfs_cmd_hdr = hisi_hba->debugfs_dq[dump_index][i].hdr;
+ cmd_hdr = hisi_hba->cmd_hdr[i];
+
+ for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++)
+ memcpy(&debugfs_cmd_hdr[j], &cmd_hdr[j],
+ queue_entry_size);
+ }
+}
+
+static void debugfs_snapshot_port_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int dump_index = hisi_hba->debugfs_dump_index;
+ const struct hisi_sas_debugfs_reg *port = &debugfs_port_reg;
+ int i, phy_cnt;
+ u32 offset;
+ u32 *databuf;
+
+ for (phy_cnt = 0; phy_cnt < hisi_hba->n_phy; phy_cnt++) {
+ databuf = hisi_hba->debugfs_port_reg[dump_index][phy_cnt].data;
+ for (i = 0; i < port->count; i++, databuf++) {
+ offset = port->base_off + HISI_SAS_REG_MEM_SIZE * i;
+ *databuf = hisi_sas_phy_read32(hisi_hba, phy_cnt,
+ offset);
+ }
+ }
+}
+
+static void debugfs_snapshot_global_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int dump_index = hisi_hba->debugfs_dump_index;
+ u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_GLOBAL].data;
+ int i;
+
+ for (i = 0; i < debugfs_global_reg.count; i++, databuf++)
+ *databuf = hisi_sas_read32(hisi_hba,
+ HISI_SAS_REG_MEM_SIZE * i);
+}
+
+static void debugfs_snapshot_axi_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int dump_index = hisi_hba->debugfs_dump_index;
+ u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_AXI].data;
+ const struct hisi_sas_debugfs_reg *axi = &debugfs_axi_reg;
+ int i;
+
+ for (i = 0; i < axi->count; i++, databuf++)
+ *databuf = hisi_sas_read32(hisi_hba,
+ HISI_SAS_REG_MEM_SIZE * i +
+ axi->base_off);
+}
+
+static void debugfs_snapshot_ras_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int dump_index = hisi_hba->debugfs_dump_index;
+ u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_RAS].data;
+ const struct hisi_sas_debugfs_reg *ras = &debugfs_ras_reg;
+ int i;
+
+ for (i = 0; i < ras->count; i++, databuf++)
+ *databuf = hisi_sas_read32(hisi_hba,
+ HISI_SAS_REG_MEM_SIZE * i +
+ ras->base_off);
+}
+
+static void debugfs_snapshot_itct_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int dump_index = hisi_hba->debugfs_dump_index;
+ void *cachebuf = hisi_hba->debugfs_itct_cache[dump_index].cache;
+ void *databuf = hisi_hba->debugfs_itct[dump_index].itct;
+ struct hisi_sas_itct *itct;
+ int i;
+
+ read_iost_itct_cache_v3_hw(hisi_hba, HISI_SAS_ITCT_CACHE, cachebuf);
+
+ itct = hisi_hba->itct;
+
+ for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
+ memcpy(databuf, itct, sizeof(struct hisi_sas_itct));
+ databuf += sizeof(struct hisi_sas_itct);
+ }
+}
+
+static void debugfs_snapshot_iost_reg_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int dump_index = hisi_hba->debugfs_dump_index;
+ int max_command_entries = HISI_SAS_MAX_COMMANDS;
+ void *cachebuf = hisi_hba->debugfs_iost_cache[dump_index].cache;
+ void *databuf = hisi_hba->debugfs_iost[dump_index].iost;
+ struct hisi_sas_iost *iost;
+ int i;
+
+ read_iost_itct_cache_v3_hw(hisi_hba, HISI_SAS_IOST_CACHE, cachebuf);
+
+ iost = hisi_hba->iost;
+
+ for (i = 0; i < max_command_entries; i++, iost++) {
+ memcpy(databuf, iost, sizeof(struct hisi_sas_iost));
+ databuf += sizeof(struct hisi_sas_iost);
+ }
+}
+
+static const char *
+debugfs_to_reg_name_v3_hw(int off, int base_off,
+ const struct hisi_sas_debugfs_reg_lu *lu)
+{
+ for (; lu->name; lu++) {
+ if (off == lu->off - base_off)
+ return lu->name;
+ }
+
+ return NULL;
+}
+
+static bool debugfs_dump_is_generated_v3_hw(void *p)
+{
+ return p ? true : false;
+}
+
+static void debugfs_print_reg_v3_hw(u32 *regs_val, struct seq_file *s,
+ const struct hisi_sas_debugfs_reg *reg)
+{
+ int i;
+
+ for (i = 0; i < reg->count; i++) {
+ int off = i * HISI_SAS_REG_MEM_SIZE;
+ const char *name;
+
+ name = debugfs_to_reg_name_v3_hw(off, reg->base_off,
+ reg->lu);
+ if (name)
+ seq_printf(s, "0x%08x 0x%08x %s\n", off,
+ regs_val[i], name);
+ else
+ seq_printf(s, "0x%08x 0x%08x\n", off,
+ regs_val[i]);
+ }
+}
+
+static int debugfs_global_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_regs *global = s->private;
+
+ if (!debugfs_dump_is_generated_v3_hw(global->data))
+ return -EPERM;
+
+ debugfs_print_reg_v3_hw(global->data, s,
+ &debugfs_global_reg);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_global_v3_hw);
+
+static int debugfs_axi_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_regs *axi = s->private;
+
+ if (!debugfs_dump_is_generated_v3_hw(axi->data))
+ return -EPERM;
+
+ debugfs_print_reg_v3_hw(axi->data, s,
+ &debugfs_axi_reg);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_axi_v3_hw);
+
+static int debugfs_ras_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_regs *ras = s->private;
+
+ if (!debugfs_dump_is_generated_v3_hw(ras->data))
+ return -EPERM;
+
+ debugfs_print_reg_v3_hw(ras->data, s,
+ &debugfs_ras_reg);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_ras_v3_hw);
+
+static int debugfs_port_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_port *port = s->private;
+ const struct hisi_sas_debugfs_reg *reg_port = &debugfs_port_reg;
+
+ if (!debugfs_dump_is_generated_v3_hw(port->data))
+ return -EPERM;
+
+ debugfs_print_reg_v3_hw(port->data, s, reg_port);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_port_v3_hw);
+
+static void debugfs_show_row_64_v3_hw(struct seq_file *s, int index,
+ int sz, __le64 *ptr)
+{
+ int i;
+
+ /* completion header size not fixed per HW version */
+ seq_printf(s, "index %04d:\n\t", index);
+ for (i = 1; i <= sz / BYTE_TO_DDW; i++, ptr++) {
+ seq_printf(s, " 0x%016llx", le64_to_cpu(*ptr));
+ if (!(i % TWO_PARA_PER_LINE))
+ seq_puts(s, "\n\t");
+ }
+
+ seq_puts(s, "\n");
+}
+
+static void debugfs_show_row_32_v3_hw(struct seq_file *s, int index,
+ int sz, __le32 *ptr)
+{
+ int i;
+
+ /* completion header size not fixed per HW version */
+ seq_printf(s, "index %04d:\n\t", index);
+ for (i = 1; i <= sz / BYTE_TO_DW; i++, ptr++) {
+ seq_printf(s, " 0x%08x", le32_to_cpu(*ptr));
+ if (!(i % FOUR_PARA_PER_LINE))
+ seq_puts(s, "\n\t");
+ }
+ seq_puts(s, "\n");
+}
+
+static void debugfs_cq_show_slot_v3_hw(struct seq_file *s, int slot,
+ struct hisi_sas_debugfs_cq *debugfs_cq)
+{
+ struct hisi_sas_cq *cq = debugfs_cq->cq;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ __le32 *complete_hdr = debugfs_cq->complete_hdr +
+ (hisi_hba->hw->complete_hdr_size * slot);
+
+ debugfs_show_row_32_v3_hw(s, slot,
+ hisi_hba->hw->complete_hdr_size,
+ complete_hdr);
+}
+
+static int debugfs_cq_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_cq *debugfs_cq = s->private;
+ int slot;
+
+ if (!debugfs_dump_is_generated_v3_hw(debugfs_cq->complete_hdr))
+ return -EPERM;
+
+ for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++)
+ debugfs_cq_show_slot_v3_hw(s, slot, debugfs_cq);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_cq_v3_hw);
+
+static void debugfs_dq_show_slot_v3_hw(struct seq_file *s, int slot,
+ void *dq_ptr)
+{
+ struct hisi_sas_debugfs_dq *debugfs_dq = dq_ptr;
+ void *cmd_queue = debugfs_dq->hdr;
+ __le32 *cmd_hdr = cmd_queue +
+ sizeof(struct hisi_sas_cmd_hdr) * slot;
+
+ debugfs_show_row_32_v3_hw(s, slot, sizeof(struct hisi_sas_cmd_hdr),
+ cmd_hdr);
+}
+
+static int debugfs_dq_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_dq *debugfs_dq = s->private;
+ int slot;
+
+ if (!debugfs_dump_is_generated_v3_hw(debugfs_dq->hdr))
+ return -EPERM;
+
+ for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++)
+ debugfs_dq_show_slot_v3_hw(s, slot, s->private);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_dq_v3_hw);
+
+static int debugfs_iost_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_iost *debugfs_iost = s->private;
+ struct hisi_sas_iost *iost = debugfs_iost->iost;
+ int i, max_command_entries = HISI_SAS_MAX_COMMANDS;
+
+ if (!debugfs_dump_is_generated_v3_hw(iost))
+ return -EPERM;
+
+ for (i = 0; i < max_command_entries; i++, iost++) {
+ __le64 *data = &iost->qw0;
+
+ debugfs_show_row_64_v3_hw(s, i, sizeof(*iost), data);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_iost_v3_hw);
+
+static int debugfs_iost_cache_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_iost_cache *debugfs_iost_cache = s->private;
+ struct hisi_sas_iost_itct_cache *iost_cache =
+ debugfs_iost_cache->cache;
+ u32 cache_size = HISI_SAS_IOST_ITCT_CACHE_DW_SZ * BYTE_TO_DW;
+ int i, tab_idx;
+ __le64 *iost;
+
+ if (!debugfs_dump_is_generated_v3_hw(iost_cache))
+ return -EPERM;
+
+ for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, iost_cache++) {
+ /*
+ * Data struct of IOST cache:
+ * Data[1]: BIT0~15: Table index
+ * Bit16: Valid mask
+ * Data[2]~[9]: IOST table
+ */
+ tab_idx = (iost_cache->data[1] & 0xffff);
+ iost = (__le64 *)iost_cache;
+
+ debugfs_show_row_64_v3_hw(s, tab_idx, cache_size, iost);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_iost_cache_v3_hw);
+
+static int debugfs_itct_v3_hw_show(struct seq_file *s, void *p)
+{
+ int i;
+ struct hisi_sas_debugfs_itct *debugfs_itct = s->private;
+ struct hisi_sas_itct *itct = debugfs_itct->itct;
+
+ if (!debugfs_dump_is_generated_v3_hw(itct))
+ return -EPERM;
+
+ for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
+ __le64 *data = &itct->qw0;
+
+ debugfs_show_row_64_v3_hw(s, i, sizeof(*itct), data);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_itct_v3_hw);
+
+static int debugfs_itct_cache_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_debugfs_itct_cache *debugfs_itct_cache = s->private;
+ struct hisi_sas_iost_itct_cache *itct_cache =
+ debugfs_itct_cache->cache;
+ u32 cache_size = HISI_SAS_IOST_ITCT_CACHE_DW_SZ * BYTE_TO_DW;
+ int i, tab_idx;
+ __le64 *itct;
+
+ if (!debugfs_dump_is_generated_v3_hw(itct_cache))
+ return -EPERM;
+
+ for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, itct_cache++) {
+ /*
+ * Data struct of ITCT cache:
+ * Data[1]: BIT0~15: Table index
+ * Bit16: Valid mask
+ * Data[2]~[9]: ITCT table
+ */
+ tab_idx = itct_cache->data[1] & 0xffff;
+ itct = (__le64 *)itct_cache;
+
+ debugfs_show_row_64_v3_hw(s, tab_idx, cache_size, itct);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_itct_cache_v3_hw);
+
+static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba, int index)
+{
+ u64 *debugfs_timestamp;
+ struct dentry *dump_dentry;
+ struct dentry *dentry;
+ char name[NAME_BUF_SIZE];
+ int p;
+ int c;
+ int d;
+
+ snprintf(name, NAME_BUF_SIZE, "%d", index);
+
+ dump_dentry = debugfs_create_dir(name, hisi_hba->debugfs_dump_dentry);
+
+ debugfs_timestamp = &hisi_hba->debugfs_timestamp[index];
+
+ debugfs_create_u64("timestamp", 0400, dump_dentry,
+ debugfs_timestamp);
+
+ debugfs_create_file("global", 0400, dump_dentry,
+ &hisi_hba->debugfs_regs[index][DEBUGFS_GLOBAL],
+ &debugfs_global_v3_hw_fops);
+
+ /* Create port dir and files */
+ dentry = debugfs_create_dir("port", dump_dentry);
+ for (p = 0; p < hisi_hba->n_phy; p++) {
+ snprintf(name, NAME_BUF_SIZE, "%d", p);
+
+ debugfs_create_file(name, 0400, dentry,
+ &hisi_hba->debugfs_port_reg[index][p],
+ &debugfs_port_v3_hw_fops);
+ }
+
+ /* Create CQ dir and files */
+ dentry = debugfs_create_dir("cq", dump_dentry);
+ for (c = 0; c < hisi_hba->queue_count; c++) {
+ snprintf(name, NAME_BUF_SIZE, "%d", c);
+
+ debugfs_create_file(name, 0400, dentry,
+ &hisi_hba->debugfs_cq[index][c],
+ &debugfs_cq_v3_hw_fops);
+ }
+
+ /* Create DQ dir and files */
+ dentry = debugfs_create_dir("dq", dump_dentry);
+ for (d = 0; d < hisi_hba->queue_count; d++) {
+ snprintf(name, NAME_BUF_SIZE, "%d", d);
+
+ debugfs_create_file(name, 0400, dentry,
+ &hisi_hba->debugfs_dq[index][d],
+ &debugfs_dq_v3_hw_fops);
+ }
+
+ debugfs_create_file("iost", 0400, dump_dentry,
+ &hisi_hba->debugfs_iost[index],
+ &debugfs_iost_v3_hw_fops);
+
+ debugfs_create_file("iost_cache", 0400, dump_dentry,
+ &hisi_hba->debugfs_iost_cache[index],
+ &debugfs_iost_cache_v3_hw_fops);
+
+ debugfs_create_file("itct", 0400, dump_dentry,
+ &hisi_hba->debugfs_itct[index],
+ &debugfs_itct_v3_hw_fops);
+
+ debugfs_create_file("itct_cache", 0400, dump_dentry,
+ &hisi_hba->debugfs_itct_cache[index],
+ &debugfs_itct_cache_v3_hw_fops);
+
+ debugfs_create_file("axi", 0400, dump_dentry,
+ &hisi_hba->debugfs_regs[index][DEBUGFS_AXI],
+ &debugfs_axi_v3_hw_fops);
+
+ debugfs_create_file("ras", 0400, dump_dentry,
+ &hisi_hba->debugfs_regs[index][DEBUGFS_RAS],
+ &debugfs_ras_v3_hw_fops);
+}
+
+static ssize_t debugfs_trigger_dump_v3_hw_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hisi_hba *hisi_hba = file->f_inode->i_private;
+ char buf[DUMP_BUF_SIZE];
+
+ if (count > DUMP_BUF_SIZE)
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if (buf[0] != '1')
+ return -EFAULT;
+
+ down(&hisi_hba->sem);
+ if (debugfs_snapshot_regs_v3_hw(hisi_hba)) {
+ up(&hisi_hba->sem);
+ return -EFAULT;
+ }
+ up(&hisi_hba->sem);
+
+ return count;
+}
+
+static const struct file_operations debugfs_trigger_dump_v3_hw_fops = {
+ .write = &debugfs_trigger_dump_v3_hw_write,
+ .owner = THIS_MODULE,
+};
+
+enum {
+ HISI_SAS_BIST_LOOPBACK_MODE_DIGITAL = 0,
+ HISI_SAS_BIST_LOOPBACK_MODE_SERDES,
+ HISI_SAS_BIST_LOOPBACK_MODE_REMOTE,
+};
+
+static const struct {
+ int value;
+ char *name;
+} debugfs_loop_linkrate_v3_hw[] = {
+ { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" },
+ { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" },
+ { SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" },
+ { SAS_LINK_RATE_12_0_GBPS, "12.0 Gbit" },
+};
+
+static int debugfs_bist_linkrate_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_linkrate_v3_hw); i++) {
+ int match = (hisi_hba->debugfs_bist_linkrate ==
+ debugfs_loop_linkrate_v3_hw[i].value);
+
+ seq_printf(s, "%s%s%s ", match ? "[" : "",
+ debugfs_loop_linkrate_v3_hw[i].name,
+ match ? "]" : "");
+ }
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static ssize_t debugfs_bist_linkrate_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ char kbuf[BIST_BUF_SIZE] = {}, *pkbuf;
+ bool found = false;
+ int i;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ if (count >= sizeof(kbuf))
+ return -EOVERFLOW;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EINVAL;
+
+ pkbuf = strstrip(kbuf);
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_linkrate_v3_hw); i++) {
+ if (!strncmp(debugfs_loop_linkrate_v3_hw[i].name,
+ pkbuf, BIST_BUF_SIZE)) {
+ hisi_hba->debugfs_bist_linkrate =
+ debugfs_loop_linkrate_v3_hw[i].value;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return count;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_bist_linkrate_v3_hw);
+
+static const struct {
+ int value;
+ char *name;
+} debugfs_loop_code_mode_v3_hw[] = {
+ { HISI_SAS_BIST_CODE_MODE_PRBS7, "PRBS7" },
+ { HISI_SAS_BIST_CODE_MODE_PRBS23, "PRBS23" },
+ { HISI_SAS_BIST_CODE_MODE_PRBS31, "PRBS31" },
+ { HISI_SAS_BIST_CODE_MODE_JTPAT, "JTPAT" },
+ { HISI_SAS_BIST_CODE_MODE_CJTPAT, "CJTPAT" },
+ { HISI_SAS_BIST_CODE_MODE_SCRAMBED_0, "SCRAMBED_0" },
+ { HISI_SAS_BIST_CODE_MODE_TRAIN, "TRAIN" },
+ { HISI_SAS_BIST_CODE_MODE_TRAIN_DONE, "TRAIN_DONE" },
+ { HISI_SAS_BIST_CODE_MODE_HFTP, "HFTP" },
+ { HISI_SAS_BIST_CODE_MODE_MFTP, "MFTP" },
+ { HISI_SAS_BIST_CODE_MODE_LFTP, "LFTP" },
+ { HISI_SAS_BIST_CODE_MODE_FIXED_DATA, "FIXED_DATA" },
+};
+
+static int debugfs_bist_code_mode_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_code_mode_v3_hw); i++) {
+ int match = (hisi_hba->debugfs_bist_code_mode ==
+ debugfs_loop_code_mode_v3_hw[i].value);
+
+ seq_printf(s, "%s%s%s ", match ? "[" : "",
+ debugfs_loop_code_mode_v3_hw[i].name,
+ match ? "]" : "");
+ }
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static ssize_t debugfs_bist_code_mode_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ char kbuf[BIST_BUF_SIZE] = {}, *pkbuf;
+ bool found = false;
+ int i;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EOVERFLOW;
+
+ pkbuf = strstrip(kbuf);
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_code_mode_v3_hw); i++) {
+ if (!strncmp(debugfs_loop_code_mode_v3_hw[i].name,
+ pkbuf, BIST_BUF_SIZE)) {
+ hisi_hba->debugfs_bist_code_mode =
+ debugfs_loop_code_mode_v3_hw[i].value;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return count;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_bist_code_mode_v3_hw);
+
+static ssize_t debugfs_bist_phy_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ unsigned int phy_no;
+ int val;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ val = kstrtouint_from_user(buf, count, 0, &phy_no);
+ if (val)
+ return val;
+
+ if (phy_no >= hisi_hba->n_phy)
+ return -EINVAL;
+
+ hisi_hba->debugfs_bist_phy_no = phy_no;
+
+ return count;
+}
+
+static int debugfs_bist_phy_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+
+ seq_printf(s, "%d\n", hisi_hba->debugfs_bist_phy_no);
+
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_bist_phy_v3_hw);
+
+static ssize_t debugfs_bist_cnt_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ unsigned int cnt;
+ int val;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ val = kstrtouint_from_user(buf, count, 0, &cnt);
+ if (val)
+ return val;
+
+ if (cnt)
+ return -EINVAL;
+
+ hisi_hba->debugfs_bist_cnt = 0;
+ return count;
+}
+
+static int debugfs_bist_cnt_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+
+ seq_printf(s, "%u\n", hisi_hba->debugfs_bist_cnt);
+
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_bist_cnt_v3_hw);
+
+static const struct {
+ int value;
+ char *name;
+} debugfs_loop_modes_v3_hw[] = {
+ { HISI_SAS_BIST_LOOPBACK_MODE_DIGITAL, "digital" },
+ { HISI_SAS_BIST_LOOPBACK_MODE_SERDES, "serdes" },
+ { HISI_SAS_BIST_LOOPBACK_MODE_REMOTE, "remote" },
+};
+
+static int debugfs_bist_mode_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_modes_v3_hw); i++) {
+ int match = (hisi_hba->debugfs_bist_mode ==
+ debugfs_loop_modes_v3_hw[i].value);
+
+ seq_printf(s, "%s%s%s ", match ? "[" : "",
+ debugfs_loop_modes_v3_hw[i].name,
+ match ? "]" : "");
+ }
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static ssize_t debugfs_bist_mode_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ char kbuf[BIST_BUF_SIZE] = {}, *pkbuf;
+ bool found = false;
+ int i;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EOVERFLOW;
+
+ pkbuf = strstrip(kbuf);
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_modes_v3_hw); i++) {
+ if (!strncmp(debugfs_loop_modes_v3_hw[i].name, pkbuf,
+ BIST_BUF_SIZE)) {
+ hisi_hba->debugfs_bist_mode =
+ debugfs_loop_modes_v3_hw[i].value;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return count;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_bist_mode_v3_hw);
+
+static ssize_t debugfs_bist_enable_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ unsigned int enable;
+ int val;
+
+ val = kstrtouint_from_user(buf, count, 0, &enable);
+ if (val)
+ return val;
+
+ if (enable > 1)
+ return -EINVAL;
+
+ if (enable == hisi_hba->debugfs_bist_enable)
+ return count;
+
+ val = debugfs_set_bist_v3_hw(hisi_hba, enable);
+ if (val < 0)
+ return val;
+
+ hisi_hba->debugfs_bist_enable = enable;
+
+ return count;
+}
+
+static int debugfs_bist_enable_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_hba *hisi_hba = s->private;
+
+ seq_printf(s, "%d\n", hisi_hba->debugfs_bist_enable);
+
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_bist_enable_v3_hw);
+
+static const struct {
+ char *name;
+} debugfs_ffe_name_v3_hw[FFE_CFG_MAX] = {
+ { "SAS_1_5_GBPS" },
+ { "SAS_3_0_GBPS" },
+ { "SAS_6_0_GBPS" },
+ { "SAS_12_0_GBPS" },
+ { "FFE_RESV" },
+ { "SATA_1_5_GBPS" },
+ { "SATA_3_0_GBPS" },
+ { "SATA_6_0_GBPS" },
+};
+
+static ssize_t debugfs_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ u32 *val = m->private;
+ int res;
+
+ res = kstrtouint_from_user(buf, count, 0, val);
+ if (res)
+ return res;
+
+ return count;
+}
+
+static int debugfs_v3_hw_show(struct seq_file *s, void *p)
+{
+ u32 *val = s->private;
+
+ seq_printf(s, "0x%x\n", *val);
+
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_v3_hw);
+
+static ssize_t debugfs_phy_down_cnt_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *s = filp->private_data;
+ struct hisi_sas_phy *phy = s->private;
+ unsigned int set_val;
+ int res;
+
+ res = kstrtouint_from_user(buf, count, 0, &set_val);
+ if (res)
+ return res;
+
+ if (set_val > 0)
+ return -EINVAL;
+
+ atomic_set(&phy->down_cnt, 0);
+
+ return count;
+}
+
+static int debugfs_phy_down_cnt_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_phy *phy = s->private;
+
+ seq_printf(s, "%d\n", atomic_read(&phy->down_cnt));
+
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_phy_down_cnt_v3_hw);
+
+enum fifo_dump_mode_v3_hw {
+ FIFO_DUMP_FORVER = (1U << 0),
+ FIFO_DUMP_AFTER_TRIGGER = (1U << 1),
+ FIFO_DUMP_UNTILL_TRIGGER = (1U << 2),
+};
+
+enum fifo_trigger_mode_v3_hw {
+ FIFO_TRIGGER_EDGE = (1U << 0),
+ FIFO_TRIGGER_SAME_LEVEL = (1U << 1),
+ FIFO_TRIGGER_DIFF_LEVEL = (1U << 2),
+};
+
+static int debugfs_is_fifo_config_valid_v3_hw(struct hisi_sas_phy *phy)
+{
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+
+ if (phy->fifo.signal_sel > 0xf) {
+ dev_info(hisi_hba->dev, "Invalid signal select: %u\n",
+ phy->fifo.signal_sel);
+ return -EINVAL;
+ }
+
+ switch (phy->fifo.dump_mode) {
+ case FIFO_DUMP_FORVER:
+ case FIFO_DUMP_AFTER_TRIGGER:
+ case FIFO_DUMP_UNTILL_TRIGGER:
+ break;
+ default:
+ dev_info(hisi_hba->dev, "Invalid dump mode: %u\n",
+ phy->fifo.dump_mode);
+ return -EINVAL;
+ }
+
+ /* when FIFO_DUMP_FORVER, no need to check trigger_mode */
+ if (phy->fifo.dump_mode == FIFO_DUMP_FORVER)
+ return 0;
+
+ switch (phy->fifo.trigger_mode) {
+ case FIFO_TRIGGER_EDGE:
+ case FIFO_TRIGGER_SAME_LEVEL:
+ case FIFO_TRIGGER_DIFF_LEVEL:
+ break;
+ default:
+ dev_info(hisi_hba->dev, "Invalid trigger mode: %u\n",
+ phy->fifo.trigger_mode);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int debugfs_update_fifo_config_v3_hw(struct hisi_sas_phy *phy)
+{
+ u32 trigger_mode = phy->fifo.trigger_mode;
+ u32 signal_sel = phy->fifo.signal_sel;
+ u32 dump_mode = phy->fifo.dump_mode;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ int phy_no = phy->sas_phy.id;
+ u32 reg_val;
+ int res;
+
+ /* Check the validity of trace FIFO configuration */
+ res = debugfs_is_fifo_config_valid_v3_hw(phy);
+ if (res)
+ return res;
+
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_CTRL);
+ /* Disable trace FIFO before update configuration */
+ reg_val |= DFX_FIFO_CTRL_DUMP_DISABLE_MSK;
+
+ /* Update trace FIFO configuration */
+ reg_val &= ~(DFX_FIFO_CTRL_DUMP_MODE_MSK |
+ DFX_FIFO_CTRL_SIGNAL_SEL_MSK |
+ DFX_FIFO_CTRL_TRIGGER_MODE_MSK);
+
+ reg_val |= ((trigger_mode << DFX_FIFO_CTRL_TRIGGER_MODE_OFF) |
+ (dump_mode << DFX_FIFO_CTRL_DUMP_MODE_OFF) |
+ (signal_sel << DFX_FIFO_CTRL_SIGNAL_SEL_OFF));
+ hisi_sas_phy_write32(hisi_hba, phy_no, DFX_FIFO_CTRL, reg_val);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, DFX_FIFO_DUMP_MSK,
+ phy->fifo.dump_msk);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, DFX_FIFO_TRIGGER,
+ phy->fifo.trigger);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, DFX_FIFO_TRIGGER_MSK,
+ phy->fifo.trigger_msk);
+
+ /* Enable trace FIFO after updated configuration */
+ reg_val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_CTRL);
+ reg_val &= ~DFX_FIFO_CTRL_DUMP_DISABLE_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, DFX_FIFO_CTRL, reg_val);
+
+ return 0;
+}
+
+static ssize_t debugfs_fifo_update_cfg_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hisi_sas_phy *phy = filp->private_data;
+ bool update;
+ int val;
+
+ val = kstrtobool_from_user(buf, count, &update);
+ if (val)
+ return val;
+
+ if (update != 1)
+ return -EINVAL;
+
+ val = debugfs_update_fifo_config_v3_hw(phy);
+ if (val)
+ return val;
+
+ return count;
+}
+
+static const struct file_operations debugfs_fifo_update_cfg_v3_hw_fops = {
+ .open = simple_open,
+ .write = debugfs_fifo_update_cfg_v3_hw_write,
+ .owner = THIS_MODULE,
+};
+
+static void debugfs_read_fifo_data_v3_hw(struct hisi_sas_phy *phy)
+{
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ u32 *buf = phy->fifo.rd_data;
+ int phy_no = phy->sas_phy.id;
+ u32 val;
+ int i;
+
+ memset(buf, 0, sizeof(phy->fifo.rd_data));
+
+ /* Disable trace FIFO before read data */
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_CTRL);
+ val |= DFX_FIFO_CTRL_DUMP_DISABLE_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, DFX_FIFO_CTRL, val);
+
+ for (i = 0; i < HISI_SAS_FIFO_DATA_DW_SIZE; i++) {
+ val = hisi_sas_phy_read32(hisi_hba, phy_no,
+ DFX_FIFO_RD_DATA);
+ buf[i] = val;
+ }
+
+ /* Enable trace FIFO after read data */
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_CTRL);
+ val &= ~DFX_FIFO_CTRL_DUMP_DISABLE_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, DFX_FIFO_CTRL, val);
+}
+
+static int debugfs_fifo_data_v3_hw_show(struct seq_file *s, void *p)
+{
+ struct hisi_sas_phy *phy = s->private;
+
+ debugfs_read_fifo_data_v3_hw(phy);
+
+ debugfs_show_row_32_v3_hw(s, 0,
+ HISI_SAS_FIFO_DATA_DW_SIZE * HISI_SAS_REG_MEM_SIZE,
+ (__le32 *)phy->fifo.rd_data);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_fifo_data_v3_hw);
+
+static void debugfs_fifo_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int phy_no;
+
+ hisi_hba->debugfs_fifo_dentry =
+ debugfs_create_dir("fifo", hisi_hba->debugfs_dir);
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct dentry *port_dentry;
+ char name[256];
+ u32 val;
+
+ /* get default configuration for trace FIFO */
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_CTRL);
+ val &= DFX_FIFO_CTRL_DUMP_MODE_MSK;
+ val >>= DFX_FIFO_CTRL_DUMP_MODE_OFF;
+ phy->fifo.dump_mode = val;
+
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_CTRL);
+ val &= DFX_FIFO_CTRL_TRIGGER_MODE_MSK;
+ val >>= DFX_FIFO_CTRL_TRIGGER_MODE_OFF;
+ phy->fifo.trigger_mode = val;
+
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_CTRL);
+ val &= DFX_FIFO_CTRL_SIGNAL_SEL_MSK;
+ val >>= DFX_FIFO_CTRL_SIGNAL_SEL_OFF;
+ phy->fifo.signal_sel = val;
+
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_DUMP_MSK);
+ phy->fifo.dump_msk = val;
+
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_TRIGGER);
+ phy->fifo.trigger = val;
+ val = hisi_sas_phy_read32(hisi_hba, phy_no, DFX_FIFO_TRIGGER_MSK);
+ phy->fifo.trigger_msk = val;
+
+ snprintf(name, 256, "%d", phy_no);
+ port_dentry = debugfs_create_dir(name,
+ hisi_hba->debugfs_fifo_dentry);
+
+ debugfs_create_file("update_config", 0200, port_dentry, phy,
+ &debugfs_fifo_update_cfg_v3_hw_fops);
+
+ debugfs_create_file("signal_sel", 0600, port_dentry,
+ &phy->fifo.signal_sel,
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("dump_msk", 0600, port_dentry,
+ &phy->fifo.dump_msk,
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("dump_mode", 0600, port_dentry,
+ &phy->fifo.dump_mode,
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("trigger_mode", 0600, port_dentry,
+ &phy->fifo.trigger_mode,
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("trigger", 0600, port_dentry,
+ &phy->fifo.trigger,
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("trigger_msk", 0600, port_dentry,
+ &phy->fifo.trigger_msk,
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("fifo_data", 0400, port_dentry, phy,
+ &debugfs_fifo_data_v3_hw_fops);
+ }
+}
+
+static void debugfs_release_v3_hw(struct hisi_hba *hisi_hba, int dump_index)
+{
+ struct device *dev = hisi_hba->dev;
+ int i;
+
+ devm_kfree(dev, hisi_hba->debugfs_iost_cache[dump_index].cache);
+ hisi_hba->debugfs_iost_cache[dump_index].cache = NULL;
+ devm_kfree(dev, hisi_hba->debugfs_itct_cache[dump_index].cache);
+ hisi_hba->debugfs_itct_cache[dump_index].cache = NULL;
+ devm_kfree(dev, hisi_hba->debugfs_iost[dump_index].iost);
+ hisi_hba->debugfs_iost[dump_index].iost = NULL;
+ devm_kfree(dev, hisi_hba->debugfs_itct[dump_index].itct);
+ hisi_hba->debugfs_itct[dump_index].itct = NULL;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ devm_kfree(dev, hisi_hba->debugfs_dq[dump_index][i].hdr);
+ hisi_hba->debugfs_dq[dump_index][i].hdr = NULL;
+ }
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ devm_kfree(dev,
+ hisi_hba->debugfs_cq[dump_index][i].complete_hdr);
+ hisi_hba->debugfs_cq[dump_index][i].complete_hdr = NULL;
+ }
+
+ for (i = 0; i < DEBUGFS_REGS_NUM; i++) {
+ devm_kfree(dev, hisi_hba->debugfs_regs[dump_index][i].data);
+ hisi_hba->debugfs_regs[dump_index][i].data = NULL;
+ }
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ devm_kfree(dev, hisi_hba->debugfs_port_reg[dump_index][i].data);
+ hisi_hba->debugfs_port_reg[dump_index][i].data = NULL;
+ }
+}
+
+static const struct hisi_sas_debugfs_reg *debugfs_reg_array_v3_hw[DEBUGFS_REGS_NUM] = {
+ [DEBUGFS_GLOBAL] = &debugfs_global_reg,
+ [DEBUGFS_AXI] = &debugfs_axi_reg,
+ [DEBUGFS_RAS] = &debugfs_ras_reg,
+};
+
+static int debugfs_alloc_v3_hw(struct hisi_hba *hisi_hba, int dump_index)
+{
+ const struct hisi_sas_hw *hw = hisi_hba->hw;
+ struct device *dev = hisi_hba->dev;
+ int p, c, d, r;
+ size_t sz;
+
+ for (r = 0; r < DEBUGFS_REGS_NUM; r++) {
+ struct hisi_sas_debugfs_regs *regs =
+ &hisi_hba->debugfs_regs[dump_index][r];
+
+ sz = debugfs_reg_array_v3_hw[r]->count * HISI_SAS_REG_MEM_SIZE;
+ regs->data = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!regs->data)
+ goto fail;
+ regs->hisi_hba = hisi_hba;
+ }
+
+ sz = debugfs_port_reg.count * HISI_SAS_REG_MEM_SIZE;
+ for (p = 0; p < hisi_hba->n_phy; p++) {
+ struct hisi_sas_debugfs_port *port =
+ &hisi_hba->debugfs_port_reg[dump_index][p];
+
+ port->data = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!port->data)
+ goto fail;
+ port->phy = &hisi_hba->phy[p];
+ }
+
+ sz = hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
+ for (c = 0; c < hisi_hba->queue_count; c++) {
+ struct hisi_sas_debugfs_cq *cq =
+ &hisi_hba->debugfs_cq[dump_index][c];
+
+ cq->complete_hdr = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!cq->complete_hdr)
+ goto fail;
+ cq->cq = &hisi_hba->cq[c];
+ }
+
+ sz = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
+ for (d = 0; d < hisi_hba->queue_count; d++) {
+ struct hisi_sas_debugfs_dq *dq =
+ &hisi_hba->debugfs_dq[dump_index][d];
+
+ dq->hdr = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!dq->hdr)
+ goto fail;
+ dq->dq = &hisi_hba->dq[d];
+ }
+
+ sz = HISI_SAS_MAX_COMMANDS * sizeof(struct hisi_sas_iost);
+
+ hisi_hba->debugfs_iost[dump_index].iost =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_iost[dump_index].iost)
+ goto fail;
+
+ sz = HISI_SAS_IOST_ITCT_CACHE_NUM *
+ sizeof(struct hisi_sas_iost_itct_cache);
+
+ hisi_hba->debugfs_iost_cache[dump_index].cache =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_iost_cache[dump_index].cache)
+ goto fail;
+
+ sz = HISI_SAS_IOST_ITCT_CACHE_NUM *
+ sizeof(struct hisi_sas_iost_itct_cache);
+
+ hisi_hba->debugfs_itct_cache[dump_index].cache =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_itct_cache[dump_index].cache)
+ goto fail;
+
+ /* New memory allocation must be locate before itct */
+ sz = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
+
+ hisi_hba->debugfs_itct[dump_index].itct =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_itct[dump_index].itct)
+ goto fail;
+
+ return 0;
+fail:
+ debugfs_release_v3_hw(hisi_hba, dump_index);
+ return -ENOMEM;
+}
+
+static int debugfs_snapshot_regs_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int debugfs_dump_index = hisi_hba->debugfs_dump_index;
+ struct device *dev = hisi_hba->dev;
+ u64 timestamp = local_clock();
+
+ if (debugfs_dump_index >= hisi_sas_debugfs_dump_count) {
+ dev_warn(dev, "dump count exceeded!\n");
+ return -EINVAL;
+ }
+
+ if (debugfs_alloc_v3_hw(hisi_hba, debugfs_dump_index)) {
+ dev_warn(dev, "failed to alloc memory\n");
+ return -ENOMEM;
+ }
+
+ do_div(timestamp, NSEC_PER_MSEC);
+ hisi_hba->debugfs_timestamp[debugfs_dump_index] = timestamp;
+
+ debugfs_snapshot_prepare_v3_hw(hisi_hba);
+
+ debugfs_snapshot_global_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_port_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_axi_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_ras_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_cq_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_dq_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_itct_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_iost_reg_v3_hw(hisi_hba);
+
+ debugfs_snapshot_restore_v3_hw(hisi_hba);
+ hisi_hba->debugfs_dump_index++;
+
+ return 0;
+}
+
+static void debugfs_phy_down_cnt_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct dentry *dir = debugfs_create_dir("phy_down_cnt",
+ hisi_hba->debugfs_dir);
+ char name[NAME_BUF_SIZE];
+ int phy_no;
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ snprintf(name, NAME_BUF_SIZE, "%d", phy_no);
+ debugfs_create_file(name, 0600, dir,
+ &hisi_hba->phy[phy_no],
+ &debugfs_phy_down_cnt_v3_hw_fops);
+ }
+}
+
+static void debugfs_bist_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct dentry *ports_dentry;
+ int phy_no;
+
+ hisi_hba->debugfs_bist_dentry =
+ debugfs_create_dir("bist", hisi_hba->debugfs_dir);
+ debugfs_create_file("link_rate", 0600,
+ hisi_hba->debugfs_bist_dentry, hisi_hba,
+ &debugfs_bist_linkrate_v3_hw_fops);
+
+ debugfs_create_file("code_mode", 0600,
+ hisi_hba->debugfs_bist_dentry, hisi_hba,
+ &debugfs_bist_code_mode_v3_hw_fops);
+
+ debugfs_create_file("fixed_code", 0600,
+ hisi_hba->debugfs_bist_dentry,
+ &hisi_hba->debugfs_bist_fixed_code[0],
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("fixed_code_1", 0600,
+ hisi_hba->debugfs_bist_dentry,
+ &hisi_hba->debugfs_bist_fixed_code[1],
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("phy_id", 0600, hisi_hba->debugfs_bist_dentry,
+ hisi_hba, &debugfs_bist_phy_v3_hw_fops);
+
+ debugfs_create_file("cnt", 0600, hisi_hba->debugfs_bist_dentry,
+ hisi_hba, &debugfs_bist_cnt_v3_hw_fops);
+
+ debugfs_create_file("loopback_mode", 0600,
+ hisi_hba->debugfs_bist_dentry,
+ hisi_hba, &debugfs_bist_mode_v3_hw_fops);
+
+ debugfs_create_file("enable", 0600, hisi_hba->debugfs_bist_dentry,
+ hisi_hba, &debugfs_bist_enable_v3_hw_fops);
+
+ ports_dentry = debugfs_create_dir("port", hisi_hba->debugfs_bist_dentry);
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ struct dentry *port_dentry;
+ struct dentry *ffe_dentry;
+ char name[256];
+ int i;
+
+ snprintf(name, 256, "%d", phy_no);
+ port_dentry = debugfs_create_dir(name, ports_dentry);
+ ffe_dentry = debugfs_create_dir("ffe", port_dentry);
+ for (i = 0; i < FFE_CFG_MAX; i++) {
+ if (i == FFE_RESV)
+ continue;
+ debugfs_create_file(debugfs_ffe_name_v3_hw[i].name,
+ 0600, ffe_dentry,
+ &hisi_hba->debugfs_bist_ffe[phy_no][i],
+ &debugfs_v3_hw_fops);
+ }
+ }
+
+ hisi_hba->debugfs_bist_linkrate = SAS_LINK_RATE_1_5_GBPS;
+}
+
+static int debugfs_dump_index_v3_hw_show(struct seq_file *s, void *p)
+{
+ int *debugfs_dump_index = s->private;
+
+ if (*debugfs_dump_index > 0)
+ seq_printf(s, "%d\n", *debugfs_dump_index - 1);
+ else
+ seq_puts(s, "dump not triggered\n");
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(debugfs_dump_index_v3_hw);
+
+static void debugfs_dump_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ hisi_hba->debugfs_dump_dentry =
+ debugfs_create_dir("dump", hisi_hba->debugfs_dir);
+
+ debugfs_create_file("latest_dump", 0400, hisi_hba->debugfs_dump_dentry,
+ &hisi_hba->debugfs_dump_index,
+ &debugfs_dump_index_v3_hw_fops);
+
+ for (i = 0; i < hisi_sas_debugfs_dump_count; i++)
+ debugfs_create_files_v3_hw(hisi_hba, i);
+}
+
+static void debugfs_exit_v3_hw(struct hisi_hba *hisi_hba)
+{
+ debugfs_remove_recursive(hisi_hba->debugfs_dir);
+ hisi_hba->debugfs_dir = NULL;
+}
+
+static void debugfs_init_v3_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = hisi_hba->dev;
+
+ hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev),
+ hisi_sas_debugfs_dir);
+ /* create bist structures */
+ debugfs_bist_init_v3_hw(hisi_hba);
+
+ debugfs_dump_init_v3_hw(hisi_hba);
+
+ debugfs_phy_down_cnt_init_v3_hw(hisi_hba);
+ debugfs_fifo_init_v3_hw(hisi_hba);
+ debugfs_create_file("trigger_dump", 0200,
+ hisi_hba->debugfs_dir,
+ hisi_hba,
+ &debugfs_trigger_dump_v3_hw_fops);
+}
+
+static int
+hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct Scsi_Host *shost;
+ struct hisi_hba *hisi_hba;
+ struct device *dev = &pdev->dev;
+ struct asd_sas_phy **arr_phy;
+ struct asd_sas_port **arr_port;
+ struct sas_ha_struct *sha;
+ int rc, phy_nr, port_nr, i;
+
+ rc = pcim_enable_device(pdev);
+ if (rc)
+ goto err_out;
+
+ pci_set_master(pdev);
+
+ rc = pcim_iomap_regions(pdev, 1 << BAR_NO_V3_HW, DRV_NAME);
+ if (rc)
+ goto err_out;
+
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (rc) {
+ dev_err(dev, "No usable DMA addressing method\n");
+ rc = -ENODEV;
+ goto err_out;
+ }
+
+ shost = hisi_sas_shost_alloc_pci(pdev);
+ if (!shost) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ sha = SHOST_TO_SAS_HA(shost);
+ hisi_hba = shost_priv(shost);
+ dev_set_drvdata(dev, sha);
+
+ hisi_hba->regs = pcim_iomap_table(pdev)[BAR_NO_V3_HW];
+ if (!hisi_hba->regs) {
+ dev_err(dev, "cannot map register\n");
+ rc = -ENOMEM;
+ goto err_out_free_host;
+ }
+
+ phy_nr = port_nr = hisi_hba->n_phy;
+
+ arr_phy = devm_kcalloc(dev, phy_nr, sizeof(void *), GFP_KERNEL);
+ arr_port = devm_kcalloc(dev, port_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_phy || !arr_port) {
+ rc = -ENOMEM;
+ goto err_out_free_host;
+ }
+
+ sha->sas_phy = arr_phy;
+ sha->sas_port = arr_port;
+ sha->shost = shost;
+ sha->lldd_ha = hisi_hba;
+
+ shost->transportt = hisi_sas_stt;
+ shost->max_id = HISI_SAS_MAX_DEVICES;
+ shost->max_lun = ~0;
+ shost->max_channel = 1;
+ shost->max_cmd_len = HISI_SAS_MAX_CDB_LEN;
+ shost->can_queue = HISI_SAS_UNRESERVED_IPTT;
+ shost->cmd_per_lun = HISI_SAS_UNRESERVED_IPTT;
+ if (hisi_hba->iopoll_q_cnt)
+ shost->nr_maps = 3;
+ else
+ shost->nr_maps = 1;
+
+ sha->sas_ha_name = DRV_NAME;
+ sha->dev = dev;
+ sha->sas_addr = &hisi_hba->sas_addr[0];
+ sha->num_phys = hisi_hba->n_phy;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy;
+ sha->sas_port[i] = &hisi_hba->port[i].sas_port;
+ }
+
+ if (hisi_hba->prot_mask) {
+ dev_info(dev, "Registering for DIF/DIX prot_mask=0x%x\n",
+ prot_mask);
+ scsi_host_set_prot(hisi_hba->shost, prot_mask);
+ if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK)
+ scsi_host_set_guard(hisi_hba->shost,
+ SHOST_DIX_GUARD_CRC);
+ }
+
+ rc = interrupt_preinit_v3_hw(hisi_hba);
+ if (rc)
+ goto err_out_free_host;
+
+ rc = scsi_add_host(shost, dev);
+ if (rc)
+ goto err_out_free_host;
+
+ rc = sas_register_ha(sha);
+ if (rc)
+ goto err_out_remove_host;
+
+ rc = hisi_sas_v3_init(hisi_hba);
+ if (rc)
+ goto err_out_unregister_ha;
+
+ scsi_scan_host(shost);
+ if (hisi_sas_debugfs_enable)
+ debugfs_init_v3_hw(hisi_hba);
+
+ pm_runtime_set_autosuspend_delay(dev, 5000);
+ pm_runtime_use_autosuspend(dev);
+ /*
+ * For the situation that there are ATA disks connected with SAS
+ * controller, it additionally creates ata_port which will affect the
+ * child_count of hisi_hba->dev. Even if suspended all the disks,
+ * ata_port is still and the child_count of hisi_hba->dev is not 0.
+ * So use pm_suspend_ignore_children() to ignore the effect to
+ * hisi_hba->dev.
+ */
+ pm_suspend_ignore_children(dev, true);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+
+err_out_unregister_ha:
+ sas_unregister_ha(sha);
+err_out_remove_host:
+ scsi_remove_host(shost);
+err_out_free_host:
+ hisi_sas_free(hisi_hba);
+ scsi_host_put(shost);
+err_out:
+ return rc;
+}
+
+static void
+hisi_sas_v3_destroy_irqs(struct pci_dev *pdev, struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ devm_free_irq(&pdev->dev, pci_irq_vector(pdev, IRQ_PHY_UP_DOWN_INDEX), hisi_hba);
+ devm_free_irq(&pdev->dev, pci_irq_vector(pdev, IRQ_CHL_INDEX), hisi_hba);
+ devm_free_irq(&pdev->dev, pci_irq_vector(pdev, IRQ_AXI_INDEX), hisi_hba);
+ for (i = 0; i < hisi_hba->cq_nvecs; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+ int nr = hisi_sas_intr_conv ? BASE_VECTORS_V3_HW :
+ BASE_VECTORS_V3_HW + i;
+
+ devm_free_irq(&pdev->dev, pci_irq_vector(pdev, nr), cq);
+ }
+}
+
+static void hisi_sas_v3_remove(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sas_ha_struct *sha = dev_get_drvdata(dev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ struct Scsi_Host *shost = sha->shost;
+
+ pm_runtime_get_noresume(dev);
+ if (hisi_sas_debugfs_enable)
+ debugfs_exit_v3_hw(hisi_hba);
+
+ sas_unregister_ha(sha);
+ flush_workqueue(hisi_hba->wq);
+ sas_remove_host(shost);
+
+ hisi_sas_v3_destroy_irqs(pdev, hisi_hba);
+ hisi_sas_free(hisi_hba);
+ scsi_host_put(shost);
+}
+
+static void hisi_sas_reset_prepare_v3_hw(struct pci_dev *pdev)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ struct Scsi_Host *shost = hisi_hba->shost;
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ wait_event(shost->host_wait, !scsi_host_in_recovery(shost));
+ dev_info(dev, "FLR prepare\n");
+ down(&hisi_hba->sem);
+ set_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+ hisi_sas_controller_reset_prepare(hisi_hba);
+
+ interrupt_disable_v3_hw(hisi_hba);
+ rc = disable_host_v3_hw(hisi_hba);
+ if (rc)
+ dev_err(dev, "FLR: disable host failed rc=%d\n", rc);
+}
+
+static void hisi_sas_reset_done_v3_hw(struct pci_dev *pdev)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ struct Scsi_Host *shost = hisi_hba->shost;
+ struct device *dev = hisi_hba->dev;
+ int rc;
+
+ hisi_sas_init_mem(hisi_hba);
+
+ rc = hw_init_v3_hw(hisi_hba);
+ if (rc) {
+ dev_err(dev, "FLR: hw init failed rc=%d\n", rc);
+ clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+ scsi_unblock_requests(shost);
+ clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+ up(&hisi_hba->sem);
+ return;
+ }
+
+ hisi_sas_controller_reset_done(hisi_hba);
+ dev_info(dev, "FLR done\n");
+}
+
+enum {
+ /* instances of the controller */
+ hip08,
+};
+
+static void enable_host_v3_hw(struct hisi_hba *hisi_hba)
+{
+ u32 reg_val;
+
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+ (u32)((1ULL << hisi_hba->queue_count) - 1));
+
+ phys_init_v3_hw(hisi_hba);
+ reg_val = hisi_sas_read32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL);
+ reg_val &= ~AM_CTRL_SHUTDOWN_REQ_MSK;
+ hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE +
+ AM_CTRL_GLOBAL, reg_val);
+}
+
+static int _suspend_v3_hw(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ struct device *dev = hisi_hba->dev;
+ struct Scsi_Host *shost = hisi_hba->shost;
+ int rc;
+
+ if (!pdev->pm_cap) {
+ dev_err(dev, "PCI PM not supported\n");
+ return -ENODEV;
+ }
+
+ if (test_and_set_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags))
+ return -EPERM;
+
+ dev_warn(dev, "entering suspend state\n");
+
+ scsi_block_requests(shost);
+ set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+ flush_workqueue(hisi_hba->wq);
+ interrupt_disable_v3_hw(hisi_hba);
+
+#ifdef CONFIG_PM
+ if ((device->power.runtime_status == RPM_SUSPENDING) &&
+ atomic_read(&device->power.usage_count)) {
+ dev_err(dev, "PM suspend: host status cannot be suspended\n");
+ rc = -EBUSY;
+ goto err_out;
+ }
+#endif
+
+ rc = disable_host_v3_hw(hisi_hba);
+ if (rc) {
+ dev_err(dev, "PM suspend: disable host failed rc=%d\n", rc);
+ goto err_out_recover_host;
+ }
+
+ hisi_sas_init_mem(hisi_hba);
+
+ hisi_sas_release_tasks(hisi_hba);
+
+ sas_suspend_ha(sha);
+
+ dev_warn(dev, "end of suspending controller\n");
+ return 0;
+
+err_out_recover_host:
+ enable_host_v3_hw(hisi_hba);
+#ifdef CONFIG_PM
+err_out:
+#endif
+ interrupt_enable_v3_hw(hisi_hba);
+ clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+ clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+ scsi_unblock_requests(shost);
+ return rc;
+}
+
+static int _resume_v3_hw(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ struct Scsi_Host *shost = hisi_hba->shost;
+ struct device *dev = hisi_hba->dev;
+ unsigned int rc;
+ pci_power_t device_state = pdev->current_state;
+
+ dev_warn(dev, "resuming from operating state [D%d]\n",
+ device_state);
+
+ scsi_unblock_requests(shost);
+ clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+
+ sas_prep_resume_ha(sha);
+ rc = hw_init_v3_hw(hisi_hba);
+ if (rc) {
+ scsi_remove_host(shost);
+ return rc;
+ }
+ phys_init_v3_hw(hisi_hba);
+
+ /*
+ * If a directly-attached disk is removed during suspend, a deadlock
+ * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
+ * hisi_hba->device to be active, which can only happen when resume
+ * completes. So don't wait for the HA event workqueue to drain upon
+ * resume.
+ */
+ sas_resume_ha_no_sync(sha);
+ clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
+
+ dev_warn(dev, "end of resuming controller\n");
+
+ return 0;
+}
+
+static int __maybe_unused suspend_v3_hw(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ int rc;
+
+ set_bit(HISI_SAS_PM_BIT, &hisi_hba->flags);
+
+ rc = _suspend_v3_hw(device);
+ if (rc)
+ clear_bit(HISI_SAS_PM_BIT, &hisi_hba->flags);
+
+ return rc;
+}
+
+static int __maybe_unused resume_v3_hw(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ int rc = _resume_v3_hw(device);
+
+ clear_bit(HISI_SAS_PM_BIT, &hisi_hba->flags);
+
+ return rc;
+}
+
+static const struct pci_device_id sas_v3_pci_table[] = {
+ { PCI_VDEVICE(HUAWEI, 0xa230), hip08 },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, sas_v3_pci_table);
+
+static const struct pci_error_handlers hisi_sas_err_handler = {
+ .reset_prepare = hisi_sas_reset_prepare_v3_hw,
+ .reset_done = hisi_sas_reset_done_v3_hw,
+};
+
+static UNIVERSAL_DEV_PM_OPS(hisi_sas_v3_pm_ops,
+ suspend_v3_hw,
+ resume_v3_hw,
+ NULL);
+
+static struct pci_driver sas_v3_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = sas_v3_pci_table,
+ .probe = hisi_sas_v3_probe,
+ .remove = hisi_sas_v3_remove,
+ .err_handler = &hisi_sas_err_handler,
+ .driver.pm = &hisi_sas_v3_pm_ops,
+};
+
+module_pci_driver(sas_v3_pci_driver);
+module_param_named(intr_conv, hisi_sas_intr_conv, bool, 0444);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
+MODULE_DESCRIPTION("HISILICON SAS controller v3 hw driver based on pci device");
+MODULE_ALIAS("pci:" DRV_NAME);
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 4f7a5829ea4c..1b3fbd328277 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* hosts.c Copyright (C) 1992 Drew Eckhardt
* Copyright (C) 1993, 1994, 1995 Eric Youngdale
@@ -33,16 +34,23 @@
#include <linux/transport_class.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-
+#include <linux/idr.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
+#include <scsi/scsi_cmnd.h>
#include "scsi_priv.h"
#include "scsi_logging.h"
-static atomic_t scsi_host_next_hn; /* host_no for next new host */
+static int shost_eh_deadline = -1;
+
+module_param_named(eh_deadline, shost_eh_deadline, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(eh_deadline,
+ "SCSI EH timeout in seconds (should be between 0 and 2^31-1)");
+
+static DEFINE_IDA(host_index_ida);
static void scsi_host_cls_release(struct device *dev)
@@ -53,6 +61,7 @@ static void scsi_host_cls_release(struct device *dev)
static struct class shost_class = {
.name = "scsi_host",
.dev_release = scsi_host_cls_release,
+ .dev_groups = scsi_shost_groups,
};
/**
@@ -148,7 +157,6 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
scsi_host_state_name(state)));
return -EINVAL;
}
-EXPORT_SYMBOL(scsi_host_set_state);
/**
* scsi_remove_host - remove a scsi host
@@ -169,9 +177,20 @@ void scsi_remove_host(struct Scsi_Host *shost)
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_autopm_get_host(shost);
+ flush_workqueue(shost->tmf_work_q);
scsi_forget_host(shost);
mutex_unlock(&shost->scan_mutex);
scsi_proc_host_rm(shost);
+ scsi_proc_hostdir_rm(shost->hostt);
+
+ /*
+ * New SCSI devices cannot be attached anymore because of the SCSI host
+ * state so drop the tag set refcnt. Wait until the tag set refcnt drops
+ * to zero because .exit_cmd_priv implementations may need the host
+ * pointer.
+ */
+ kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
+ wait_for_completion(&shost->tagset_freed);
spin_lock_irqsave(shost->host_lock, flags);
if (scsi_host_set_state(shost, SHOST_DEL))
@@ -200,45 +219,75 @@ EXPORT_SYMBOL(scsi_remove_host);
int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
struct device *dma_dev)
{
- struct scsi_host_template *sht = shost->hostt;
+ const struct scsi_host_template *sht = shost->hostt;
int error = -EINVAL;
- printk(KERN_INFO "scsi%d : %s\n", shost->host_no,
+ shost_printk(KERN_INFO, shost, "%s\n",
sht->info ? sht->info(shost) : sht->name);
if (!shost->can_queue) {
- printk(KERN_ERR "%s: can_queue = 0 no longer supported\n",
- sht->name);
+ shost_printk(KERN_ERR, shost,
+ "can_queue = 0 no longer supported\n");
goto fail;
}
- error = scsi_setup_command_freelist(shost);
+ if (shost->nr_reserved_cmds && !sht->queue_reserved_command) {
+ shost_printk(KERN_ERR, shost,
+ "nr_reserved_cmds set but no method to queue\n");
+ goto fail;
+ }
+
+ /* Use min_t(int, ...) in case shost->can_queue exceeds SHRT_MAX */
+ shost->cmd_per_lun = min_t(int, shost->cmd_per_lun,
+ shost->can_queue);
+
+ error = scsi_init_sense_cache(shost);
if (error)
goto fail;
if (!shost->shost_gendev.parent)
shost->shost_gendev.parent = dev ? dev : &platform_bus;
+ if (!dma_dev)
+ dma_dev = shost->shost_gendev.parent;
+
shost->dma_dev = dma_dev;
- error = device_add(&shost->shost_gendev);
+ if (dma_dev->dma_mask) {
+ shost->max_sectors = min_t(unsigned int, shost->max_sectors,
+ dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
+ }
+
+ error = scsi_mq_setup_tags(shost);
if (error)
- goto out;
+ goto fail;
+ kref_init(&shost->tagset_refcnt);
+ init_completion(&shost->tagset_freed);
+
+ /*
+ * Increase usage count temporarily here so that calling
+ * scsi_autopm_put_host() will trigger runtime idle if there is
+ * nothing else preventing suspending the device.
+ */
+ pm_runtime_get_noresume(&shost->shost_gendev);
pm_runtime_set_active(&shost->shost_gendev);
pm_runtime_enable(&shost->shost_gendev);
device_enable_async_suspend(&shost->shost_gendev);
+ error = device_add(&shost->shost_gendev);
+ if (error)
+ goto out_disable_runtime_pm;
+
scsi_host_set_state(shost, SHOST_RUNNING);
get_device(shost->shost_gendev.parent);
device_enable_async_suspend(&shost->shost_dev);
+ get_device(&shost->shost_gendev);
error = device_add(&shost->shost_dev);
if (error)
goto out_del_gendev;
- get_device(&shost->shost_gendev);
-
if (shost->transportt->host_size) {
shost->shost_data = kzalloc(shost->transportt->host_size,
GFP_KERNEL);
@@ -249,34 +298,52 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
}
if (shost->transportt->create_work_queue) {
- snprintf(shost->work_q_name, sizeof(shost->work_q_name),
- "scsi_wq_%d", shost->host_no);
- shost->work_q = create_singlethread_workqueue(
- shost->work_q_name);
+ shost->work_q = alloc_workqueue(
+ "scsi_wq_%d",
+ WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, 1,
+ shost->host_no);
+
if (!shost->work_q) {
error = -EINVAL;
- goto out_free_shost_data;
+ goto out_del_dev;
}
}
error = scsi_sysfs_add_host(shost);
if (error)
- goto out_destroy_host;
+ goto out_del_dev;
+
+ if (shost->nr_reserved_cmds) {
+ shost->pseudo_sdev = scsi_get_pseudo_sdev(shost);
+ if (!shost->pseudo_sdev) {
+ error = -ENOMEM;
+ goto out_del_dev;
+ }
+ }
scsi_proc_host_add(shost);
+ scsi_autopm_put_host(shost);
return error;
- out_destroy_host:
- if (shost->work_q)
- destroy_workqueue(shost->work_q);
- out_free_shost_data:
- kfree(shost->shost_data);
+ /*
+ * Any host allocation in this function will be freed in
+ * scsi_host_dev_release().
+ */
out_del_dev:
device_del(&shost->shost_dev);
out_del_gendev:
+ /*
+ * Host state is SHOST_RUNNING so we have to explicitly release
+ * ->shost_dev.
+ */
+ put_device(&shost->shost_dev);
device_del(&shost->shost_gendev);
- out:
- scsi_destroy_command_freelist(shost);
+ out_disable_runtime_pm:
+ device_disable_async_suspend(&shost->shost_gendev);
+ pm_runtime_disable(&shost->shost_gendev);
+ pm_runtime_set_suspended(&shost->shost_gendev);
+ pm_runtime_put_noidle(&shost->shost_gendev);
+ kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
fail:
return error;
}
@@ -287,29 +354,38 @@ static void scsi_host_dev_release(struct device *dev)
struct Scsi_Host *shost = dev_to_shost(dev);
struct device *parent = dev->parent;
- scsi_proc_hostdir_rm(shost->hostt);
+ /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */
+ rcu_barrier();
+ if (shost->tmf_work_q)
+ destroy_workqueue(shost->tmf_work_q);
if (shost->ehandler)
kthread_stop(shost->ehandler);
if (shost->work_q)
destroy_workqueue(shost->work_q);
- if (shost->uspace_req_q) {
- kfree(shost->uspace_req_q->queuedata);
- scsi_free_queue(shost->uspace_req_q);
- }
- scsi_destroy_command_freelist(shost);
- if (shost->bqt)
- blk_free_tags(shost->bqt);
+ if (shost->shost_state == SHOST_CREATED) {
+ /*
+ * Free the shost_dev device name and remove the proc host dir
+ * here if scsi_host_{alloc,put}() have been called but neither
+ * scsi_host_add() nor scsi_remove_host() has been called.
+ * This avoids that the memory allocated for the shost_dev
+ * name as well as the proc dir structure are leaked.
+ */
+ scsi_proc_hostdir_rm(shost->hostt);
+ kfree(dev_name(&shost->shost_dev));
+ }
kfree(shost->shost_data);
- if (parent)
+ ida_free(&host_index_ida, shost->host_no);
+
+ if (shost->shost_state != SHOST_CREATED)
put_device(parent);
kfree(shost);
}
-static struct device_type scsi_host_type = {
+static const struct device_type scsi_host_type = {
.name = "scsi_host",
.release = scsi_host_dev_release,
};
@@ -327,15 +403,12 @@ static struct device_type scsi_host_type = {
* Return value:
* Pointer to a new Scsi_Host
**/
-struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
+struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int privsize)
{
struct Scsi_Host *shost;
- gfp_t gfp_mask = GFP_KERNEL;
+ int index;
- if (sht->unchecked_isa_dma && privsize)
- gfp_mask |= __GFP_DMA;
-
- shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask);
+ shost = kzalloc(sizeof(struct Scsi_Host) + privsize, GFP_KERNEL);
if (!shost)
return NULL;
@@ -344,17 +417,19 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
shost->shost_state = SHOST_CREATED;
INIT_LIST_HEAD(&shost->__devices);
INIT_LIST_HEAD(&shost->__targets);
+ INIT_LIST_HEAD(&shost->eh_abort_list);
INIT_LIST_HEAD(&shost->eh_cmd_q);
INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait);
-
mutex_init(&shost->scan_mutex);
- /*
- * subtract one because we increment first then return, but we need to
- * know what the next host number was before increment
- */
- shost->host_no = atomic_inc_return(&scsi_host_next_hn) - 1;
+ index = ida_alloc(&host_index_ida, GFP_KERNEL);
+ if (index < 0) {
+ kfree(shost);
+ return NULL;
+ }
+ shost->host_no = index;
+
shost->dma_channel = 0xff;
/* These three are default values which can be overridden */
@@ -375,12 +450,23 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
shost->hostt = sht;
shost->this_id = sht->this_id;
shost->can_queue = sht->can_queue;
+ shost->nr_reserved_cmds = sht->nr_reserved_cmds;
shost->sg_tablesize = sht->sg_tablesize;
shost->sg_prot_tablesize = sht->sg_prot_tablesize;
shost->cmd_per_lun = sht->cmd_per_lun;
- shost->unchecked_isa_dma = sht->unchecked_isa_dma;
- shost->use_clustering = sht->use_clustering;
- shost->ordered_tag = sht->ordered_tag;
+ shost->no_write_same = sht->no_write_same;
+ shost->host_tagset = sht->host_tagset;
+ shost->queuecommand_may_block = sht->queuecommand_may_block;
+
+ if (shost_eh_deadline == -1 || !sht->eh_host_reset_handler)
+ shost->eh_deadline = -1;
+ else if ((ulong) shost_eh_deadline * HZ > INT_MAX) {
+ shost_printk(KERN_WARNING, shost,
+ "eh_deadline %u too large, setting to %u\n",
+ shost_eh_deadline, INT_MAX / HZ);
+ shost->eh_deadline = INT_MAX;
+ } else
+ shost->eh_deadline = shost_eh_deadline * HZ;
if (sht->supported_mode == MODE_UNKNOWN)
/* means we didn't set it ... default to INITIATOR */
@@ -402,6 +488,24 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
else
shost->max_sectors = SCSI_DEFAULT_MAX_SECTORS;
+ shost->virt_boundary_mask = sht->virt_boundary_mask;
+ if (shost->virt_boundary_mask) {
+ WARN_ON_ONCE(sht->max_segment_size &&
+ sht->max_segment_size != UINT_MAX);
+ shost->max_segment_size = UINT_MAX;
+ } else {
+ if (sht->max_segment_size)
+ shost->max_segment_size = sht->max_segment_size;
+ else
+ shost->max_segment_size = BLK_MAX_SEGMENT_SIZE;
+ }
+
+ /* 32-byte (dword) is a common minimum for HBAs. */
+ if (sht->dma_alignment)
+ shost->dma_alignment = sht->dma_alignment;
+ else
+ shost->dma_alignment = 3;
+
/*
* assume a 4GB boundary, if not set
*/
@@ -414,57 +518,51 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
dev_set_name(&shost->shost_gendev, "host%d", shost->host_no);
shost->shost_gendev.bus = &scsi_bus_type;
shost->shost_gendev.type = &scsi_host_type;
+ scsi_enable_async_suspend(&shost->shost_gendev);
device_initialize(&shost->shost_dev);
shost->shost_dev.parent = &shost->shost_gendev;
shost->shost_dev.class = &shost_class;
dev_set_name(&shost->shost_dev, "host%d", shost->host_no);
- shost->shost_dev.groups = scsi_sysfs_shost_attr_groups;
+ shost->shost_dev.groups = sht->shost_groups;
shost->ehandler = kthread_run(scsi_error_handler, shost,
"scsi_eh_%d", shost->host_no);
if (IS_ERR(shost->ehandler)) {
- printk(KERN_WARNING "scsi%d: error handler thread failed to spawn, error = %ld\n",
- shost->host_no, PTR_ERR(shost->ehandler));
- goto fail_kfree;
+ shost_printk(KERN_WARNING, shost,
+ "error handler thread failed to spawn, error = %ld\n",
+ PTR_ERR(shost->ehandler));
+ shost->ehandler = NULL;
+ goto fail;
}
- scsi_proc_hostdir_add(shost->hostt);
+ shost->tmf_work_q = alloc_workqueue("scsi_tmf_%d",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS,
+ 1, shost->host_no);
+ if (!shost->tmf_work_q) {
+ shost_printk(KERN_WARNING, shost,
+ "failed to create tmf workq\n");
+ goto fail;
+ }
+ if (scsi_proc_hostdir_add(shost->hostt) < 0)
+ goto fail;
return shost;
+ fail:
+ /*
+ * Host state is still SHOST_CREATED and that is enough to release
+ * ->shost_gendev. scsi_host_dev_release() will free
+ * dev_name(&shost->shost_dev).
+ */
+ put_device(&shost->shost_gendev);
- fail_kfree:
- kfree(shost);
return NULL;
}
EXPORT_SYMBOL(scsi_host_alloc);
-struct Scsi_Host *scsi_register(struct scsi_host_template *sht, int privsize)
-{
- struct Scsi_Host *shost = scsi_host_alloc(sht, privsize);
-
- if (!sht->detect) {
- printk(KERN_WARNING "scsi_register() called on new-style "
- "template for driver %s\n", sht->name);
- dump_stack();
- }
-
- if (shost)
- list_add_tail(&shost->sht_legacy_list, &sht->legacy_hosts);
- return shost;
-}
-EXPORT_SYMBOL(scsi_register);
-
-void scsi_unregister(struct Scsi_Host *shost)
-{
- list_del(&shost->sht_legacy_list);
- scsi_host_put(shost);
-}
-EXPORT_SYMBOL(scsi_unregister);
-
-static int __scsi_host_match(struct device *dev, void *data)
+static int __scsi_host_match(struct device *dev, const void *data)
{
struct Scsi_Host *p;
- unsigned short *hostnum = (unsigned short *)data;
+ const unsigned int *hostnum = data;
p = class_to_shost(dev);
return p->host_no == *hostnum;
@@ -481,7 +579,7 @@ static int __scsi_host_match(struct device *dev, void *data)
* that scsi_host_get() took. The put_device() below dropped
* the reference from class_find_device().
**/
-struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
+struct Scsi_Host *scsi_host_lookup(unsigned int hostnum)
{
struct device *cdev;
struct Scsi_Host *shost = NULL;
@@ -509,6 +607,32 @@ struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(scsi_host_get);
+static bool scsi_host_check_in_flight(struct request *rq, void *data)
+{
+ int *count = data;
+ struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
+
+ if (test_bit(SCMD_STATE_INFLIGHT, &cmd->state))
+ (*count)++;
+
+ return true;
+}
+
+/**
+ * scsi_host_busy - Return the count of in-flight commands
+ * @shost: Pointer to Scsi_Host
+ **/
+int scsi_host_busy(struct Scsi_Host *shost)
+{
+ int cnt = 0;
+
+ if (shost->tag_set.ops)
+ blk_mq_tagset_busy_iter(&shost->tag_set,
+ scsi_host_check_in_flight, &cnt);
+ return cnt;
+}
+EXPORT_SYMBOL(scsi_host_busy);
+
/**
* scsi_host_put - dec a Scsi_Host ref count
* @shost: Pointer to Scsi_Host to dec.
@@ -527,6 +651,7 @@ int scsi_init_hosts(void)
void scsi_exit_hosts(void)
{
class_unregister(&shost_class);
+ ida_destroy(&host_index_ida);
}
int scsi_is_host_device(const struct device *dev)
@@ -548,7 +673,7 @@ EXPORT_SYMBOL(scsi_is_host_device);
int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work)
{
if (unlikely(!shost->work_q)) {
- printk(KERN_ERR
+ shost_printk(KERN_ERR, shost,
"ERROR: Scsi host '%s' attempted to queue scsi-work, "
"when no workqueue created.\n", shost->hostt->name);
dump_stack();
@@ -567,7 +692,7 @@ EXPORT_SYMBOL_GPL(scsi_queue_work);
void scsi_flush_work(struct Scsi_Host *shost)
{
if (!shost->work_q) {
- printk(KERN_ERR
+ shost_printk(KERN_ERR, shost,
"ERROR: Scsi host '%s' attempted to flush scsi-work, "
"when no workqueue created.\n", shost->hostt->name);
dump_stack();
@@ -577,3 +702,69 @@ void scsi_flush_work(struct Scsi_Host *shost)
flush_workqueue(shost->work_q);
}
EXPORT_SYMBOL_GPL(scsi_flush_work);
+
+static bool complete_all_cmds_iter(struct request *rq, void *data)
+{
+ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
+ enum scsi_host_status status = *(enum scsi_host_status *)data;
+
+ scsi_dma_unmap(scmd);
+ scmd->result = 0;
+ set_host_byte(scmd, status);
+ scsi_done(scmd);
+ return true;
+}
+
+/**
+ * scsi_host_complete_all_commands - Terminate all running commands
+ * @shost: Scsi Host on which commands should be terminated
+ * @status: Status to be set for the terminated commands
+ *
+ * There is no protection against modification of the number
+ * of outstanding commands. It is the responsibility of the
+ * caller to ensure that concurrent I/O submission and/or
+ * completion is stopped when calling this function.
+ */
+void scsi_host_complete_all_commands(struct Scsi_Host *shost,
+ enum scsi_host_status status)
+{
+ blk_mq_tagset_busy_iter(&shost->tag_set, complete_all_cmds_iter,
+ &status);
+}
+EXPORT_SYMBOL_GPL(scsi_host_complete_all_commands);
+
+struct scsi_host_busy_iter_data {
+ bool (*fn)(struct scsi_cmnd *, void *);
+ void *priv;
+};
+
+static bool __scsi_host_busy_iter_fn(struct request *req, void *priv)
+{
+ struct scsi_host_busy_iter_data *iter_data = priv;
+ struct scsi_cmnd *sc = blk_mq_rq_to_pdu(req);
+
+ return iter_data->fn(sc, iter_data->priv);
+}
+
+/**
+ * scsi_host_busy_iter - Iterate over all busy commands
+ * @shost: Pointer to Scsi_Host.
+ * @fn: Function to call on each busy command
+ * @priv: Data pointer passed to @fn
+ *
+ * If locking against concurrent command completions is required
+ * ithas to be provided by the caller
+ **/
+void scsi_host_busy_iter(struct Scsi_Host *shost,
+ bool (*fn)(struct scsi_cmnd *, void *),
+ void *priv)
+{
+ struct scsi_host_busy_iter_data iter_data = {
+ .fn = fn,
+ .priv = priv,
+ };
+
+ blk_mq_tagset_busy_iter(&shost->tag_set, __scsi_host_busy_iter_fn,
+ &iter_data);
+}
+EXPORT_SYMBOL_GPL(scsi_host_busy_iter);
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 6bba23a26303..3654b12c5d5a 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -1,6 +1,9 @@
/*
* Disk Array driver for HP Smart Array SAS controllers
- * Copyright 2000, 2009 Hewlett-Packard Development Company, L.P.
+ * Copyright (c) 2019-2020 Microchip Technology Inc. and its subsidiaries
+ * Copyright 2016 Microsemi Corporation
+ * Copyright 2014-2015 PMC-Sierra, Inc.
+ * Copyright 2000,2009-2015 Hewlett-Packard Development Company, L.P.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,11 +14,7 @@
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Questions/Comments/Bugfixes to iss_storagedev@hp.com
+ * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com
*
*/
@@ -28,7 +27,6 @@
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/timer.h>
-#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/compat.h>
@@ -43,37 +41,49 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_transport_sas.h>
+#include <scsi/scsi_dbg.h>
#include <linux/cciss_ioctl.h>
#include <linux/string.h>
#include <linux/bitmap.h>
-#include <asm/atomic.h>
-#include <linux/kthread.h>
+#include <linux/atomic.h>
+#include <linux/jiffies.h>
+#include <linux/percpu-defs.h>
+#include <linux/percpu.h>
+#include <linux/unaligned.h>
+#include <asm/div64.h>
#include "hpsa_cmd.h"
#include "hpsa.h"
-/* HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.' */
-#define HPSA_DRIVER_VERSION "2.0.2-1"
+/*
+ * HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.'
+ * with an optional trailing '-' followed by a byte value (0-255).
+ */
+#define HPSA_DRIVER_VERSION "3.4.20-200"
#define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
+#define HPSA "hpsa"
-/* How long to wait (in milliseconds) for board to go into simple mode */
-#define MAX_CONFIG_WAIT 30000
+/* How long to wait for CISS doorbell communication */
+#define CLEAR_EVENT_WAIT_INTERVAL 20 /* ms for each msleep() call */
+#define MODE_CHANGE_WAIT_INTERVAL 10 /* ms for each msleep() call */
+#define MAX_CLEAR_EVENT_WAIT 30000 /* times 20 ms = 600 s */
+#define MAX_MODE_CHANGE_WAIT 2000 /* times 10 ms = 20 s */
#define MAX_IOCTL_CONFIG_WAIT 1000
/*define how many times we will try a command because of bus resets */
#define MAX_CMD_RETRIES 3
+/* How long to wait before giving up on a command */
+#define HPSA_EH_PTRAID_TIMEOUT (240 * HZ)
/* Embedded module documentation macros - see modules.h */
MODULE_AUTHOR("Hewlett-Packard Company");
MODULE_DESCRIPTION("Driver for HP Smart Array Controller version " \
HPSA_DRIVER_VERSION);
-MODULE_SUPPORTED_DEVICE("HP Smart Array Controllers");
MODULE_VERSION(HPSA_DRIVER_VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("cciss");
-static int hpsa_allow_any;
-module_param(hpsa_allow_any, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(hpsa_allow_any,
- "Allow hpsa driver to access unknown HP Smart Array hardware");
static int hpsa_simple_mode;
module_param(hpsa_simple_mode, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(hpsa_simple_mode,
@@ -86,8 +96,8 @@ static const struct pci_device_id hpsa_pci_device_id[] = {
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3245},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3247},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3249},
- {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324a},
- {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324b},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324A},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324B},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3233},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3350},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3351},
@@ -96,8 +106,48 @@ static const struct pci_device_id hpsa_pci_device_id[] = {
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3354},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3355},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3356},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103c, 0x1920},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1921},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1922},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1923},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1924},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103c, 0x1925},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1926},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1928},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1929},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21BD},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21BE},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21BF},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C0},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C1},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C2},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C3},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C4},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C5},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C6},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C7},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C8},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21C9},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CA},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CB},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CC},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CD},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CE},
+ {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0580},
+ {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0581},
+ {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0582},
+ {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0583},
+ {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0584},
+ {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0585},
+ {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0076},
+ {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0087},
+ {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x007D},
+ {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0088},
+ {PCI_VENDOR_ID_HP, 0x333f, 0x103c, 0x333f},
{PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
+ {PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
{0,}
};
@@ -108,73 +158,180 @@ MODULE_DEVICE_TABLE(pci, hpsa_pci_device_id);
* access = Address of the struct of function pointers
*/
static struct board_type products[] = {
+ {0x40700E11, "Smart Array 5300", &SA5A_access},
+ {0x40800E11, "Smart Array 5i", &SA5B_access},
+ {0x40820E11, "Smart Array 532", &SA5B_access},
+ {0x40830E11, "Smart Array 5312", &SA5B_access},
+ {0x409A0E11, "Smart Array 641", &SA5A_access},
+ {0x409B0E11, "Smart Array 642", &SA5A_access},
+ {0x409C0E11, "Smart Array 6400", &SA5A_access},
+ {0x409D0E11, "Smart Array 6400 EM", &SA5A_access},
+ {0x40910E11, "Smart Array 6i", &SA5A_access},
+ {0x3225103C, "Smart Array P600", &SA5A_access},
+ {0x3223103C, "Smart Array P800", &SA5A_access},
+ {0x3234103C, "Smart Array P400", &SA5A_access},
+ {0x3235103C, "Smart Array P400i", &SA5A_access},
+ {0x3211103C, "Smart Array E200i", &SA5A_access},
+ {0x3212103C, "Smart Array E200", &SA5A_access},
+ {0x3213103C, "Smart Array E200i", &SA5A_access},
+ {0x3214103C, "Smart Array E200i", &SA5A_access},
+ {0x3215103C, "Smart Array E200i", &SA5A_access},
+ {0x3237103C, "Smart Array E500", &SA5A_access},
+ {0x323D103C, "Smart Array P700m", &SA5A_access},
{0x3241103C, "Smart Array P212", &SA5_access},
{0x3243103C, "Smart Array P410", &SA5_access},
{0x3245103C, "Smart Array P410i", &SA5_access},
{0x3247103C, "Smart Array P411", &SA5_access},
{0x3249103C, "Smart Array P812", &SA5_access},
- {0x324a103C, "Smart Array P712m", &SA5_access},
- {0x324b103C, "Smart Array P711m", &SA5_access},
- {0x3350103C, "Smart Array", &SA5_access},
- {0x3351103C, "Smart Array", &SA5_access},
- {0x3352103C, "Smart Array", &SA5_access},
- {0x3353103C, "Smart Array", &SA5_access},
- {0x3354103C, "Smart Array", &SA5_access},
- {0x3355103C, "Smart Array", &SA5_access},
- {0x3356103C, "Smart Array", &SA5_access},
+ {0x324A103C, "Smart Array P712m", &SA5_access},
+ {0x324B103C, "Smart Array P711m", &SA5_access},
+ {0x3233103C, "HP StorageWorks 1210m", &SA5_access}, /* alias of 333f */
+ {0x3350103C, "Smart Array P222", &SA5_access},
+ {0x3351103C, "Smart Array P420", &SA5_access},
+ {0x3352103C, "Smart Array P421", &SA5_access},
+ {0x3353103C, "Smart Array P822", &SA5_access},
+ {0x3354103C, "Smart Array P420i", &SA5_access},
+ {0x3355103C, "Smart Array P220i", &SA5_access},
+ {0x3356103C, "Smart Array P721m", &SA5_access},
+ {0x1920103C, "Smart Array P430i", &SA5_access},
+ {0x1921103C, "Smart Array P830i", &SA5_access},
+ {0x1922103C, "Smart Array P430", &SA5_access},
+ {0x1923103C, "Smart Array P431", &SA5_access},
+ {0x1924103C, "Smart Array P830", &SA5_access},
+ {0x1925103C, "Smart Array P831", &SA5_access},
+ {0x1926103C, "Smart Array P731m", &SA5_access},
+ {0x1928103C, "Smart Array P230i", &SA5_access},
+ {0x1929103C, "Smart Array P530", &SA5_access},
+ {0x21BD103C, "Smart Array P244br", &SA5_access},
+ {0x21BE103C, "Smart Array P741m", &SA5_access},
+ {0x21BF103C, "Smart HBA H240ar", &SA5_access},
+ {0x21C0103C, "Smart Array P440ar", &SA5_access},
+ {0x21C1103C, "Smart Array P840ar", &SA5_access},
+ {0x21C2103C, "Smart Array P440", &SA5_access},
+ {0x21C3103C, "Smart Array P441", &SA5_access},
+ {0x21C4103C, "Smart Array", &SA5_access},
+ {0x21C5103C, "Smart Array P841", &SA5_access},
+ {0x21C6103C, "Smart HBA H244br", &SA5_access},
+ {0x21C7103C, "Smart HBA H240", &SA5_access},
+ {0x21C8103C, "Smart HBA H241", &SA5_access},
+ {0x21C9103C, "Smart Array", &SA5_access},
+ {0x21CA103C, "Smart Array P246br", &SA5_access},
+ {0x21CB103C, "Smart Array P840", &SA5_access},
+ {0x21CC103C, "Smart Array", &SA5_access},
+ {0x21CD103C, "Smart Array", &SA5_access},
+ {0x21CE103C, "Smart HBA", &SA5_access},
+ {0x05809005, "SmartHBA-SA", &SA5_access},
+ {0x05819005, "SmartHBA-SA 8i", &SA5_access},
+ {0x05829005, "SmartHBA-SA 8i8e", &SA5_access},
+ {0x05839005, "SmartHBA-SA 8e", &SA5_access},
+ {0x05849005, "SmartHBA-SA 16i", &SA5_access},
+ {0x05859005, "SmartHBA-SA 4i4e", &SA5_access},
+ {0x00761590, "HP Storage P1224 Array Controller", &SA5_access},
+ {0x00871590, "HP Storage P1224e Array Controller", &SA5_access},
+ {0x007D1590, "HP Storage P1228 Array Controller", &SA5_access},
+ {0x00881590, "HP Storage P1228e Array Controller", &SA5_access},
+ {0x333f103c, "HP StorageWorks 1210m Array Controller", &SA5_access},
{0xFFFF103C, "Unknown Smart Array", &SA5_access},
};
+static struct scsi_transport_template *hpsa_sas_transport_template;
+static int hpsa_add_sas_host(struct ctlr_info *h);
+static void hpsa_delete_sas_host(struct ctlr_info *h);
+static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node,
+ struct hpsa_scsi_dev_t *device);
+static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device);
+static struct hpsa_scsi_dev_t
+ *hpsa_find_device_by_sas_rphy(struct ctlr_info *h,
+ struct sas_rphy *rphy);
+
+#define SCSI_CMD_BUSY ((struct scsi_cmnd *)&hpsa_cmd_busy)
+static const struct scsi_cmnd hpsa_cmd_busy;
+#define SCSI_CMD_IDLE ((struct scsi_cmnd *)&hpsa_cmd_idle)
+static const struct scsi_cmnd hpsa_cmd_idle;
static int number_of_controllers;
static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id);
-static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg);
-static void start_io(struct ctlr_info *h);
+static int hpsa_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg);
+static int hpsa_passthru_ioctl(struct ctlr_info *h,
+ IOCTL_Command_struct *iocommand);
+static int hpsa_big_passthru_ioctl(struct ctlr_info *h,
+ BIG_IOCTL_Command_struct *ioc);
#ifdef CONFIG_COMPAT
-static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg);
+static int hpsa_compat_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg);
#endif
static void cmd_free(struct ctlr_info *h, struct CommandList *c);
-static void cmd_special_free(struct ctlr_info *h, struct CommandList *c);
static struct CommandList *cmd_alloc(struct ctlr_info *h);
-static struct CommandList *cmd_special_alloc(struct ctlr_info *h);
-static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
- void *buff, size_t size, u8 page_code, unsigned char *scsi3addr,
+static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c);
+static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
+ struct scsi_cmnd *scmd);
+static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
+ void *buff, size_t size, u16 page_code, unsigned char *scsi3addr,
int cmd_type);
+static void hpsa_free_cmd_pool(struct ctlr_info *h);
+#define VPD_PAGE (1 << 8)
+#define HPSA_SIMPLE_ERROR_BITS 0x03
static int hpsa_scsi_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd);
static void hpsa_scan_start(struct Scsi_Host *);
static int hpsa_scan_finished(struct Scsi_Host *sh,
unsigned long elapsed_time);
-static int hpsa_change_queue_depth(struct scsi_device *sdev,
- int qdepth, int reason);
+static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth);
static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
-static int hpsa_slave_alloc(struct scsi_device *sdev);
-static void hpsa_slave_destroy(struct scsi_device *sdev);
+static int hpsa_sdev_init(struct scsi_device *sdev);
+static int hpsa_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim);
+static void hpsa_sdev_destroy(struct scsi_device *sdev);
-static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno);
+static void hpsa_update_scsi_devices(struct ctlr_info *h);
static int check_for_unit_attention(struct ctlr_info *h,
struct CommandList *c);
static void check_ioctl_unit_attention(struct ctlr_info *h,
struct CommandList *c);
/* performant mode helper functions */
static void calc_bucket_map(int *bucket, int num_buckets,
- int nsgs, int *bucket_map);
-static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
-static inline u32 next_command(struct ctlr_info *h);
-static int __devinit hpsa_find_cfg_addrs(struct pci_dev *pdev,
- void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index,
- u64 *cfg_offset);
-static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
- unsigned long *memory_bar);
-static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id);
-static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
- void __iomem *vaddr, int wait_for_ready);
+ int nsgs, int min_blocks, u32 *bucket_map);
+static void hpsa_free_performant_mode(struct ctlr_info *h);
+static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
+static inline u32 next_command(struct ctlr_info *h, u8 q);
+static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
+ u32 *cfg_base_addr, u64 *cfg_base_addr_index,
+ u64 *cfg_offset);
+static int hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
+ unsigned long *memory_bar);
+static int hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id,
+ bool *legacy_board);
+static int wait_for_device_to_become_ready(struct ctlr_info *h,
+ unsigned char lunaddr[],
+ int reply_queue);
+static int hpsa_wait_for_board_state(struct pci_dev *pdev, void __iomem *vaddr,
+ int wait_for_ready);
+static inline void finish_cmd(struct CommandList *c);
+static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h);
#define BOARD_NOT_READY 0
#define BOARD_READY 1
+static void hpsa_drain_accel_commands(struct ctlr_info *h);
+static void hpsa_flush_cache(struct ctlr_info *h);
+static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h,
+ struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len,
+ u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk);
+static void hpsa_command_resubmit_worker(struct work_struct *work);
+static u32 lockup_detected(struct ctlr_info *h);
+static int detect_controller_lockup(struct ctlr_info *h);
+static void hpsa_disable_rld_caching(struct ctlr_info *h);
+static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h,
+ struct ReportExtendedLUNdata *buf, int bufsize);
+static bool hpsa_vpd_page_supported(struct ctlr_info *h,
+ unsigned char scsi3addr[], u8 page);
+static int hpsa_luns_changed(struct ctlr_info *h);
+static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c,
+ struct hpsa_scsi_dev_t *dev,
+ unsigned char *scsi3addr);
static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
{
@@ -188,44 +345,151 @@ static inline struct ctlr_info *shost_to_hba(struct Scsi_Host *sh)
return (struct ctlr_info *) *priv;
}
+static inline bool hpsa_is_cmd_idle(struct CommandList *c)
+{
+ return c->scsi_cmd == SCSI_CMD_IDLE;
+}
+
+/* extract sense key, asc, and ascq from sense data. -1 means invalid. */
+static void decode_sense_data(const u8 *sense_data, int sense_data_len,
+ u8 *sense_key, u8 *asc, u8 *ascq)
+{
+ struct scsi_sense_hdr sshdr;
+ bool rc;
+
+ *sense_key = -1;
+ *asc = -1;
+ *ascq = -1;
+
+ if (sense_data_len < 1)
+ return;
+
+ rc = scsi_normalize_sense(sense_data, sense_data_len, &sshdr);
+ if (rc) {
+ *sense_key = sshdr.sense_key;
+ *asc = sshdr.asc;
+ *ascq = sshdr.ascq;
+ }
+}
+
static int check_for_unit_attention(struct ctlr_info *h,
struct CommandList *c)
{
- if (c->err_info->SenseInfo[2] != UNIT_ATTENTION)
+ u8 sense_key, asc, ascq;
+ int sense_len;
+
+ if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo))
+ sense_len = sizeof(c->err_info->SenseInfo);
+ else
+ sense_len = c->err_info->SenseLen;
+
+ decode_sense_data(c->err_info->SenseInfo, sense_len,
+ &sense_key, &asc, &ascq);
+ if (sense_key != UNIT_ATTENTION || asc == 0xff)
return 0;
- switch (c->err_info->SenseInfo[12]) {
+ switch (asc) {
case STATE_CHANGED:
- dev_warn(&h->pdev->dev, "hpsa%d: a state change "
- "detected, command retried\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: a state change detected, command retried\n",
+ h->devname);
break;
case LUN_FAILED:
- dev_warn(&h->pdev->dev, "hpsa%d: LUN failure "
- "detected, action required\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: LUN failure detected\n", h->devname);
break;
case REPORT_LUNS_CHANGED:
- dev_warn(&h->pdev->dev, "hpsa%d: report LUN data "
- "changed, action required\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: report LUN data changed\n", h->devname);
/*
- * Note: this REPORT_LUNS_CHANGED condition only occurs on the MSA2012.
+ * Note: this REPORT_LUNS_CHANGED condition only occurs on the external
+ * target (array) devices.
*/
break;
case POWER_OR_RESET:
- dev_warn(&h->pdev->dev, "hpsa%d: a power on "
- "or device reset detected\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: a power on or device reset detected\n",
+ h->devname);
break;
case UNIT_ATTENTION_CLEARED:
- dev_warn(&h->pdev->dev, "hpsa%d: unit attention "
- "cleared by another initiator\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: unit attention cleared by another initiator\n",
+ h->devname);
break;
default:
- dev_warn(&h->pdev->dev, "hpsa%d: unknown "
- "unit attention detected\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: unknown unit attention detected\n",
+ h->devname);
break;
}
return 1;
}
+static int check_for_busy(struct ctlr_info *h, struct CommandList *c)
+{
+ if (c->err_info->CommandStatus != CMD_TARGET_STATUS ||
+ (c->err_info->ScsiStatus != SAM_STAT_BUSY &&
+ c->err_info->ScsiStatus != SAM_STAT_TASK_SET_FULL))
+ return 0;
+ dev_warn(&h->pdev->dev, HPSA "device busy");
+ return 1;
+}
+
+static u32 lockup_detected(struct ctlr_info *h);
+static ssize_t host_show_lockup_detected(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ld;
+ struct ctlr_info *h;
+ struct Scsi_Host *shost = class_to_shost(dev);
+
+ h = shost_to_hba(shost);
+ ld = lockup_detected(h);
+
+ return sprintf(buf, "ld=%d\n", ld);
+}
+
+static ssize_t host_store_hp_ssd_smart_path_status(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int status;
+ struct ctlr_info *h;
+ struct Scsi_Host *shost = class_to_shost(dev);
+
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ if (kstrtoint(buf, 10, &status))
+ return -EINVAL;
+ h = shost_to_hba(shost);
+ h->acciopath_status = !!status;
+ dev_warn(&h->pdev->dev,
+ "hpsa: HP SSD Smart Path %s via sysfs update.\n",
+ h->acciopath_status ? "enabled" : "disabled");
+ return count;
+}
+
+static ssize_t host_store_raid_offload_debug(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int debug_level;
+ struct ctlr_info *h;
+ struct Scsi_Host *shost = class_to_shost(dev);
+
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ if (kstrtoint(buf, 10, &debug_level))
+ return -EINVAL;
+ if (debug_level < 0)
+ debug_level = 0;
+ h = shost_to_hba(shost);
+ h->raid_offload_debug = debug_level;
+ dev_warn(&h->pdev->dev, "hpsa: Set raid_offload_debug level = %d\n",
+ h->raid_offload_debug);
+ return count;
+}
+
static ssize_t host_store_rescan(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -237,6 +501,12 @@ static ssize_t host_store_rescan(struct device *dev,
return count;
}
+static void hpsa_turn_off_ioaccel_for_device(struct hpsa_scsi_dev_t *device)
+{
+ device->offload_enabled = 0;
+ device->offload_to_be_enabled = 0;
+}
+
static ssize_t host_show_firmware_revision(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -258,7 +528,8 @@ static ssize_t host_show_commands_outstanding(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct ctlr_info *h = shost_to_hba(shost);
- return snprintf(buf, 20, "%d\n", h->commands_outstanding);
+ return snprintf(buf, 20, "%d\n",
+ atomic_read(&h->commands_outstanding));
}
static ssize_t host_show_transport_mode(struct device *dev,
@@ -273,10 +544,21 @@ static ssize_t host_show_transport_mode(struct device *dev,
"performant" : "simple");
}
+static ssize_t host_show_hp_ssd_smart_path_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctlr_info *h;
+ struct Scsi_Host *shost = class_to_shost(dev);
+
+ h = shost_to_hba(shost);
+ return snprintf(buf, 30, "HP SSD Smart Path %s\n",
+ (h->acciopath_status == 1) ? "enabled" : "disabled");
+}
+
/* List of controllers which cannot be hard reset on kexec with reset_devices */
static u32 unresettable_controller[] = {
0x324a103C, /* Smart Array P712m */
- 0x324b103C, /* SmartArray P711m */
+ 0x324b103C, /* Smart Array P711m */
0x3223103C, /* Smart Array P800 */
0x3234103C, /* Smart Array P400 */
0x3235103C, /* Smart Array P400i */
@@ -287,12 +569,26 @@ static u32 unresettable_controller[] = {
0x3215103C, /* Smart Array E200i */
0x3237103C, /* Smart Array E500 */
0x323D103C, /* Smart Array P700m */
+ 0x40800E11, /* Smart Array 5i */
0x409C0E11, /* Smart Array 6400 */
0x409D0E11, /* Smart Array 6400 EM */
+ 0x40700E11, /* Smart Array 5300 */
+ 0x40820E11, /* Smart Array 532 */
+ 0x40830E11, /* Smart Array 5312 */
+ 0x409A0E11, /* Smart Array 641 */
+ 0x409B0E11, /* Smart Array 642 */
+ 0x40910E11, /* Smart Array 6i */
};
/* List of controllers which cannot even be soft reset */
static u32 soft_unresettable_controller[] = {
+ 0x40800E11, /* Smart Array 5i */
+ 0x40700E11, /* Smart Array 5300 */
+ 0x40820E11, /* Smart Array 532 */
+ 0x40830E11, /* Smart Array 5312 */
+ 0x409A0E11, /* Smart Array 641 */
+ 0x409B0E11, /* Smart Array 642 */
+ 0x40910E11, /* Smart Array 6i */
/* Exclude 640x boards. These are two pci devices in one slot
* which share a battery backed cache module. One controls the
* cache, the other accesses the cache through the one that controls
@@ -304,24 +600,26 @@ static u32 soft_unresettable_controller[] = {
0x409D0E11, /* Smart Array 6400 EM */
};
-static int ctlr_is_hard_resettable(u32 board_id)
+static int board_id_in_array(u32 a[], int nelems, u32 board_id)
{
int i;
- for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++)
- if (unresettable_controller[i] == board_id)
- return 0;
- return 1;
+ for (i = 0; i < nelems; i++)
+ if (a[i] == board_id)
+ return 1;
+ return 0;
}
-static int ctlr_is_soft_resettable(u32 board_id)
+static int ctlr_is_hard_resettable(u32 board_id)
{
- int i;
+ return !board_id_in_array(unresettable_controller,
+ ARRAY_SIZE(unresettable_controller), board_id);
+}
- for (i = 0; i < ARRAY_SIZE(soft_unresettable_controller); i++)
- if (soft_unresettable_controller[i] == board_id)
- return 0;
- return 1;
+static int ctlr_is_soft_resettable(u32 board_id)
+{
+ return !board_id_in_array(soft_unresettable_controller,
+ ARRAY_SIZE(soft_unresettable_controller), board_id);
}
static int ctlr_is_resettable(u32 board_id)
@@ -345,10 +643,23 @@ static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[])
return (scsi3addr[3] & 0xC0) == 0x40;
}
-static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
- "UNKNOWN"
+static const char * const raid_label[] = { "0", "4", "1(+0)", "5", "5+1", "6",
+ "1(+0)ADM", "UNKNOWN", "PHYS DRV"
};
-#define RAID_UNKNOWN (ARRAY_SIZE(raid_label) - 1)
+#define HPSA_RAID_0 0
+#define HPSA_RAID_4 1
+#define HPSA_RAID_1 2 /* also used for RAID 10 */
+#define HPSA_RAID_5 3 /* also used for RAID 50 */
+#define HPSA_RAID_51 4
+#define HPSA_RAID_6 5 /* also used for RAID 60 */
+#define HPSA_RAID_ADM 6 /* also used for RAID 1+0 ADM */
+#define RAID_UNKNOWN (ARRAY_SIZE(raid_label) - 2)
+#define PHYSICAL_DRIVE (ARRAY_SIZE(raid_label) - 1)
+
+static inline bool is_logical_device(struct hpsa_scsi_dev_t *device)
+{
+ return !device->physical_device;
+}
static ssize_t raid_level_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -370,7 +681,7 @@ static ssize_t raid_level_show(struct device *dev,
}
/* Is this even a logical drive? */
- if (!is_logical_dev_addr_mode(hdev->scsi3addr)) {
+ if (!is_logical_device(hdev)) {
spin_unlock_irqrestore(&h->lock, flags);
l = snprintf(buf, PAGE_SIZE, "N/A\n");
return l;
@@ -403,9 +714,7 @@ static ssize_t lunid_show(struct device *dev,
}
memcpy(lunid, hdev->scsi3addr, sizeof(lunid));
spin_unlock_irqrestore(&h->lock, flags);
- return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
- lunid[0], lunid[1], lunid[2], lunid[3],
- lunid[4], lunid[5], lunid[6], lunid[7]);
+ return snprintf(buf, 20, "0x%8phN\n", lunid);
}
static ssize_t unique_id_show(struct device *dev,
@@ -436,10 +745,175 @@ static ssize_t unique_id_show(struct device *dev,
sn[12], sn[13], sn[14], sn[15]);
}
-static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL);
-static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
-static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL);
+static ssize_t sas_address_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctlr_info *h;
+ struct scsi_device *sdev;
+ struct hpsa_scsi_dev_t *hdev;
+ unsigned long flags;
+ u64 sas_address;
+
+ sdev = to_scsi_device(dev);
+ h = sdev_to_hba(sdev);
+ spin_lock_irqsave(&h->lock, flags);
+ hdev = sdev->hostdata;
+ if (!hdev || is_logical_device(hdev) || !hdev->expose_device) {
+ spin_unlock_irqrestore(&h->lock, flags);
+ return -ENODEV;
+ }
+ sas_address = hdev->sas_address;
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n", sas_address);
+}
+
+static ssize_t host_show_hp_ssd_smart_path_enabled(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctlr_info *h;
+ struct scsi_device *sdev;
+ struct hpsa_scsi_dev_t *hdev;
+ unsigned long flags;
+ int offload_enabled;
+
+ sdev = to_scsi_device(dev);
+ h = sdev_to_hba(sdev);
+ spin_lock_irqsave(&h->lock, flags);
+ hdev = sdev->hostdata;
+ if (!hdev) {
+ spin_unlock_irqrestore(&h->lock, flags);
+ return -ENODEV;
+ }
+ offload_enabled = hdev->offload_enabled;
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ if (hdev->devtype == TYPE_DISK || hdev->devtype == TYPE_ZBC)
+ return snprintf(buf, 20, "%d\n", offload_enabled);
+ else
+ return snprintf(buf, 40, "%s\n",
+ "Not applicable for a controller");
+}
+
+#define MAX_PATHS 8
+static ssize_t path_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctlr_info *h;
+ struct scsi_device *sdev;
+ struct hpsa_scsi_dev_t *hdev;
+ unsigned long flags;
+ int i;
+ int output_len = 0;
+ u8 box;
+ u8 bay;
+ u8 path_map_index = 0;
+ char *active;
+ unsigned char phys_connector[2];
+
+ sdev = to_scsi_device(dev);
+ h = sdev_to_hba(sdev);
+ spin_lock_irqsave(&h->devlock, flags);
+ hdev = sdev->hostdata;
+ if (!hdev) {
+ spin_unlock_irqrestore(&h->devlock, flags);
+ return -ENODEV;
+ }
+
+ bay = hdev->bay;
+ for (i = 0; i < MAX_PATHS; i++) {
+ path_map_index = 1<<i;
+ if (i == hdev->active_path_index)
+ active = "Active";
+ else if (hdev->path_map & path_map_index)
+ active = "Inactive";
+ else
+ continue;
+
+ output_len += scnprintf(buf + output_len,
+ PAGE_SIZE - output_len,
+ "[%d:%d:%d:%d] %20.20s ",
+ h->scsi_host->host_no,
+ hdev->bus, hdev->target, hdev->lun,
+ scsi_device_type(hdev->devtype));
+
+ if (hdev->devtype == TYPE_RAID || is_logical_device(hdev)) {
+ output_len += scnprintf(buf + output_len,
+ PAGE_SIZE - output_len,
+ "%s\n", active);
+ continue;
+ }
+
+ box = hdev->box[i];
+ memcpy(&phys_connector, &hdev->phys_connector[i],
+ sizeof(phys_connector));
+ if (phys_connector[0] < '0')
+ phys_connector[0] = '0';
+ if (phys_connector[1] < '0')
+ phys_connector[1] = '0';
+ output_len += scnprintf(buf + output_len,
+ PAGE_SIZE - output_len,
+ "PORT: %.2s ",
+ phys_connector);
+ if ((hdev->devtype == TYPE_DISK || hdev->devtype == TYPE_ZBC) &&
+ hdev->expose_device) {
+ if (box == 0 || box == 0xFF) {
+ output_len += scnprintf(buf + output_len,
+ PAGE_SIZE - output_len,
+ "BAY: %hhu %s\n",
+ bay, active);
+ } else {
+ output_len += scnprintf(buf + output_len,
+ PAGE_SIZE - output_len,
+ "BOX: %hhu BAY: %hhu %s\n",
+ box, bay, active);
+ }
+ } else if (box != 0 && box != 0xFF) {
+ output_len += scnprintf(buf + output_len,
+ PAGE_SIZE - output_len, "BOX: %hhu %s\n",
+ box, active);
+ } else
+ output_len += scnprintf(buf + output_len,
+ PAGE_SIZE - output_len, "%s\n", active);
+ }
+
+ spin_unlock_irqrestore(&h->devlock, flags);
+ return output_len;
+}
+
+static ssize_t host_show_ctlr_num(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctlr_info *h;
+ struct Scsi_Host *shost = class_to_shost(dev);
+
+ h = shost_to_hba(shost);
+ return snprintf(buf, 20, "%d\n", h->ctlr);
+}
+
+static ssize_t host_show_legacy_board(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctlr_info *h;
+ struct Scsi_Host *shost = class_to_shost(dev);
+
+ h = shost_to_hba(shost);
+ return snprintf(buf, 20, "%d\n", h->legacy_board ? 1 : 0);
+}
+
+static DEVICE_ATTR_RO(raid_level);
+static DEVICE_ATTR_RO(lunid);
+static DEVICE_ATTR_RO(unique_id);
static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
+static DEVICE_ATTR_RO(sas_address);
+static DEVICE_ATTR(hp_ssd_smart_path_enabled, S_IRUGO,
+ host_show_hp_ssd_smart_path_enabled, NULL);
+static DEVICE_ATTR_RO(path_info);
+static DEVICE_ATTR(hp_ssd_smart_path_status, S_IWUSR|S_IRUGO|S_IROTH,
+ host_show_hp_ssd_smart_path_status,
+ host_store_hp_ssd_smart_path_status);
+static DEVICE_ATTR(raid_offload_debug, S_IWUSR, NULL,
+ host_store_raid_offload_debug);
static DEVICE_ATTR(firmware_revision, S_IRUGO,
host_show_firmware_revision, NULL);
static DEVICE_ATTR(commands_outstanding, S_IRUGO,
@@ -448,101 +922,260 @@ static DEVICE_ATTR(transport_mode, S_IRUGO,
host_show_transport_mode, NULL);
static DEVICE_ATTR(resettable, S_IRUGO,
host_show_resettable, NULL);
-
-static struct device_attribute *hpsa_sdev_attrs[] = {
- &dev_attr_raid_level,
- &dev_attr_lunid,
- &dev_attr_unique_id,
+static DEVICE_ATTR(lockup_detected, S_IRUGO,
+ host_show_lockup_detected, NULL);
+static DEVICE_ATTR(ctlr_num, S_IRUGO,
+ host_show_ctlr_num, NULL);
+static DEVICE_ATTR(legacy_board, S_IRUGO,
+ host_show_legacy_board, NULL);
+
+static struct attribute *hpsa_sdev_attrs[] = {
+ &dev_attr_raid_level.attr,
+ &dev_attr_lunid.attr,
+ &dev_attr_unique_id.attr,
+ &dev_attr_hp_ssd_smart_path_enabled.attr,
+ &dev_attr_path_info.attr,
+ &dev_attr_sas_address.attr,
NULL,
};
-static struct device_attribute *hpsa_shost_attrs[] = {
- &dev_attr_rescan,
- &dev_attr_firmware_revision,
- &dev_attr_commands_outstanding,
- &dev_attr_transport_mode,
- &dev_attr_resettable,
+ATTRIBUTE_GROUPS(hpsa_sdev);
+
+static struct attribute *hpsa_shost_attrs[] = {
+ &dev_attr_rescan.attr,
+ &dev_attr_firmware_revision.attr,
+ &dev_attr_commands_outstanding.attr,
+ &dev_attr_transport_mode.attr,
+ &dev_attr_resettable.attr,
+ &dev_attr_hp_ssd_smart_path_status.attr,
+ &dev_attr_raid_offload_debug.attr,
+ &dev_attr_lockup_detected.attr,
+ &dev_attr_ctlr_num.attr,
+ &dev_attr_legacy_board.attr,
NULL,
};
-static struct scsi_host_template hpsa_driver_template = {
+ATTRIBUTE_GROUPS(hpsa_shost);
+
+#define HPSA_NRESERVED_CMDS (HPSA_CMDS_RESERVED_FOR_DRIVER +\
+ HPSA_MAX_CONCURRENT_PASSTHRUS)
+
+static const struct scsi_host_template hpsa_driver_template = {
.module = THIS_MODULE,
- .name = "hpsa",
- .proc_name = "hpsa",
+ .name = HPSA,
+ .proc_name = HPSA,
.queuecommand = hpsa_scsi_queue_command,
.scan_start = hpsa_scan_start,
.scan_finished = hpsa_scan_finished,
.change_queue_depth = hpsa_change_queue_depth,
.this_id = -1,
- .use_clustering = ENABLE_CLUSTERING,
.eh_device_reset_handler = hpsa_eh_device_reset_handler,
.ioctl = hpsa_ioctl,
- .slave_alloc = hpsa_slave_alloc,
- .slave_destroy = hpsa_slave_destroy,
+ .sdev_init = hpsa_sdev_init,
+ .sdev_configure = hpsa_sdev_configure,
+ .sdev_destroy = hpsa_sdev_destroy,
#ifdef CONFIG_COMPAT
.compat_ioctl = hpsa_compat_ioctl,
#endif
- .sdev_attrs = hpsa_sdev_attrs,
- .shost_attrs = hpsa_shost_attrs,
+ .sdev_groups = hpsa_sdev_groups,
+ .shost_groups = hpsa_shost_groups,
+ .max_sectors = 2048,
+ .no_write_same = 1,
};
-
-/* Enqueuing and dequeuing functions for cmdlists. */
-static inline void addQ(struct list_head *list, struct CommandList *c)
-{
- list_add_tail(&c->list, list);
-}
-
-static inline u32 next_command(struct ctlr_info *h)
+static inline u32 next_command(struct ctlr_info *h, u8 q)
{
u32 a;
+ struct reply_queue_buffer *rq = &h->reply_queue[q];
+
+ if (h->transMethod & CFGTBL_Trans_io_accel1)
+ return h->access.command_completed(h, q);
if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
- return h->access.command_completed(h);
+ return h->access.command_completed(h, q);
- if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
- a = *(h->reply_pool_head); /* Next cmd in ring buffer */
- (h->reply_pool_head)++;
- h->commands_outstanding--;
+ if ((rq->head[rq->current_entry] & 1) == rq->wraparound) {
+ a = rq->head[rq->current_entry];
+ rq->current_entry++;
+ atomic_dec(&h->commands_outstanding);
} else {
a = FIFO_EMPTY;
}
/* Check for wraparound */
- if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
- h->reply_pool_head = h->reply_pool;
- h->reply_pool_wraparound ^= 1;
+ if (rq->current_entry == h->max_commands) {
+ rq->current_entry = 0;
+ rq->wraparound ^= 1;
}
return a;
}
-/* set_performant_mode: Modify the tag for cciss performant
+/*
+ * There are some special bits in the bus address of the
+ * command that we have to set for the controller to know
+ * how to process the command:
+ *
+ * Normal performant mode:
+ * bit 0: 1 means performant mode, 0 means simple mode.
+ * bits 1-3 = block fetch table entry
+ * bits 4-6 = command type (== 0)
+ *
+ * ioaccel1 mode:
+ * bit 0 = "performant mode" bit.
+ * bits 1-3 = block fetch table entry
+ * bits 4-6 = command type (== 110)
+ * (command type is needed because ioaccel1 mode
+ * commands are submitted through the same register as normal
+ * mode commands, so this is how the controller knows whether
+ * the command is normal mode or ioaccel1 mode.)
+ *
+ * ioaccel2 mode:
+ * bit 0 = "performant mode" bit.
+ * bits 1-4 = block fetch table entry (note extra bit)
+ * bits 4-6 = not needed, because ioaccel2 mode has
+ * a separate special register for submitting commands.
+ */
+
+/*
+ * set_performant_mode: Modify the tag for cciss performant
* set bit 0 for pull model, bits 3-1 for block fetch
* register number
*/
-static void set_performant_mode(struct ctlr_info *h, struct CommandList *c)
+#define DEFAULT_REPLY_QUEUE (-1)
+static void set_performant_mode(struct ctlr_info *h, struct CommandList *c,
+ int reply_queue)
{
- if (likely(h->transMethod & CFGTBL_Trans_Performant))
+ if (likely(h->transMethod & CFGTBL_Trans_Performant)) {
c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
+ if (unlikely(!h->msix_vectors))
+ return;
+ c->Header.ReplyQueue = reply_queue;
+ }
}
-static void enqueue_cmd_and_start_io(struct ctlr_info *h,
- struct CommandList *c)
+static void set_ioaccel1_performant_mode(struct ctlr_info *h,
+ struct CommandList *c,
+ int reply_queue)
{
- unsigned long flags;
+ struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[c->cmdindex];
- set_performant_mode(h, c);
- spin_lock_irqsave(&h->lock, flags);
- addQ(&h->reqQ, c);
- h->Qdepth++;
- start_io(h);
- spin_unlock_irqrestore(&h->lock, flags);
+ /*
+ * Tell the controller to post the reply to the queue for this
+ * processor. This seems to give the best I/O throughput.
+ */
+ cp->ReplyQueue = reply_queue;
+ /*
+ * Set the bits in the address sent down to include:
+ * - performant mode bit (bit 0)
+ * - pull count (bits 1-3)
+ * - command type (bits 4-6)
+ */
+ c->busaddr |= 1 | (h->ioaccel1_blockFetchTable[c->Header.SGList] << 1) |
+ IOACCEL1_BUSADDR_CMDTYPE;
+}
+
+static void set_ioaccel2_tmf_performant_mode(struct ctlr_info *h,
+ struct CommandList *c,
+ int reply_queue)
+{
+ struct hpsa_tmf_struct *cp = (struct hpsa_tmf_struct *)
+ &h->ioaccel2_cmd_pool[c->cmdindex];
+
+ /* Tell the controller to post the reply to the queue for this
+ * processor. This seems to give the best I/O throughput.
+ */
+ cp->reply_queue = reply_queue;
+ /* Set the bits in the address sent down to include:
+ * - performant mode bit not used in ioaccel mode 2
+ * - pull count (bits 0-3)
+ * - command type isn't needed for ioaccel2
+ */
+ c->busaddr |= h->ioaccel2_blockFetchTable[0];
+}
+
+static void set_ioaccel2_performant_mode(struct ctlr_info *h,
+ struct CommandList *c,
+ int reply_queue)
+{
+ struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex];
+
+ /*
+ * Tell the controller to post the reply to the queue for this
+ * processor. This seems to give the best I/O throughput.
+ */
+ cp->reply_queue = reply_queue;
+ /*
+ * Set the bits in the address sent down to include:
+ * - performant mode bit not used in ioaccel mode 2
+ * - pull count (bits 0-3)
+ * - command type isn't needed for ioaccel2
+ */
+ c->busaddr |= (h->ioaccel2_blockFetchTable[cp->sg_count]);
}
-static inline void removeQ(struct CommandList *c)
+static int is_firmware_flash_cmd(u8 *cdb)
{
- if (WARN_ON(list_empty(&c->list)))
+ return cdb[0] == BMIC_WRITE && cdb[6] == BMIC_FLASH_FIRMWARE;
+}
+
+/*
+ * During firmware flash, the heartbeat register may not update as frequently
+ * as it should. So we dial down lockup detection during firmware flash. and
+ * dial it back up when firmware flash completes.
+ */
+#define HEARTBEAT_SAMPLE_INTERVAL_DURING_FLASH (240 * HZ)
+#define HEARTBEAT_SAMPLE_INTERVAL (30 * HZ)
+#define HPSA_EVENT_MONITOR_INTERVAL (15 * HZ)
+static void dial_down_lockup_detection_during_fw_flash(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ if (!is_firmware_flash_cmd(c->Request.CDB))
return;
- list_del_init(&c->list);
+ atomic_inc(&h->firmware_flash_in_progress);
+ h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL_DURING_FLASH;
+}
+
+static void dial_up_lockup_detection_on_fw_flash_complete(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ if (is_firmware_flash_cmd(c->Request.CDB) &&
+ atomic_dec_and_test(&h->firmware_flash_in_progress))
+ h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
+}
+
+static void __enqueue_cmd_and_start_io(struct ctlr_info *h,
+ struct CommandList *c, int reply_queue)
+{
+ dial_down_lockup_detection_during_fw_flash(h, c);
+ atomic_inc(&h->commands_outstanding);
+ /*
+ * Check to see if the command is being retried.
+ */
+ if (c->device && !c->retry_pending)
+ atomic_inc(&c->device->commands_outstanding);
+
+ reply_queue = h->reply_map[raw_smp_processor_id()];
+ switch (c->cmd_type) {
+ case CMD_IOACCEL1:
+ set_ioaccel1_performant_mode(h, c, reply_queue);
+ writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
+ break;
+ case CMD_IOACCEL2:
+ set_ioaccel2_performant_mode(h, c, reply_queue);
+ writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
+ break;
+ case IOACCEL2_TMF:
+ set_ioaccel2_tmf_performant_mode(h, c, reply_queue);
+ writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
+ break;
+ default:
+ set_performant_mode(h, c, reply_queue);
+ h->access.submit_command(h, c);
+ }
+}
+
+static void enqueue_cmd_and_start_io(struct ctlr_info *h, struct CommandList *c)
+{
+ __enqueue_cmd_and_start_io(h, c, DEFAULT_REPLY_QUEUE);
}
static inline int is_hba_lunid(unsigned char scsi3addr[])
@@ -566,29 +1199,82 @@ static int hpsa_find_target_lun(struct ctlr_info *h,
* assumes h->devlock is held
*/
int i, found = 0;
- DECLARE_BITMAP(lun_taken, HPSA_MAX_SCSI_DEVS_PER_HBA);
+ DECLARE_BITMAP(lun_taken, HPSA_MAX_DEVICES);
- memset(&lun_taken[0], 0, HPSA_MAX_SCSI_DEVS_PER_HBA >> 3);
+ bitmap_zero(lun_taken, HPSA_MAX_DEVICES);
for (i = 0; i < h->ndevices; i++) {
if (h->dev[i]->bus == bus && h->dev[i]->target != -1)
- set_bit(h->dev[i]->target, lun_taken);
+ __set_bit(h->dev[i]->target, lun_taken);
}
- for (i = 0; i < HPSA_MAX_SCSI_DEVS_PER_HBA; i++) {
- if (!test_bit(i, lun_taken)) {
- /* *bus = 1; */
- *target = i;
- *lun = 0;
- found = 1;
- break;
- }
+ i = find_first_zero_bit(lun_taken, HPSA_MAX_DEVICES);
+ if (i < HPSA_MAX_DEVICES) {
+ /* *bus = 1; */
+ *target = i;
+ *lun = 0;
+ found = 1;
}
return !found;
}
+static void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *dev, char *description)
+{
+#define LABEL_SIZE 25
+ char label[LABEL_SIZE];
+
+ if (h == NULL || h->pdev == NULL || h->scsi_host == NULL)
+ return;
+
+ switch (dev->devtype) {
+ case TYPE_RAID:
+ snprintf(label, LABEL_SIZE, "controller");
+ break;
+ case TYPE_ENCLOSURE:
+ snprintf(label, LABEL_SIZE, "enclosure");
+ break;
+ case TYPE_DISK:
+ case TYPE_ZBC:
+ if (dev->external)
+ snprintf(label, LABEL_SIZE, "external");
+ else if (!is_logical_dev_addr_mode(dev->scsi3addr))
+ snprintf(label, LABEL_SIZE, "%s",
+ raid_label[PHYSICAL_DRIVE]);
+ else
+ snprintf(label, LABEL_SIZE, "RAID-%s",
+ dev->raid_level > RAID_UNKNOWN ? "?" :
+ raid_label[dev->raid_level]);
+ break;
+ case TYPE_ROM:
+ snprintf(label, LABEL_SIZE, "rom");
+ break;
+ case TYPE_TAPE:
+ snprintf(label, LABEL_SIZE, "tape");
+ break;
+ case TYPE_MEDIUM_CHANGER:
+ snprintf(label, LABEL_SIZE, "changer");
+ break;
+ default:
+ snprintf(label, LABEL_SIZE, "UNKNOWN");
+ break;
+ }
+
+ dev_printk(level, &h->pdev->dev,
+ "scsi %d:%d:%d:%d: %s %s %.8s %.16s %s SSDSmartPathCap%c En%c Exp=%d\n",
+ h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
+ description,
+ scsi_device_type(dev->devtype),
+ dev->vendor,
+ dev->model,
+ label,
+ dev->offload_config ? '+' : '-',
+ dev->offload_to_be_enabled ? '+' : '-',
+ dev->expose_device);
+}
+
/* Add an entry into h->dev[] array. */
-static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
+static int hpsa_scsi_add_entry(struct ctlr_info *h,
struct hpsa_scsi_dev_t *device,
struct hpsa_scsi_dev_t *added[], int *nadded)
{
@@ -598,7 +1284,7 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
unsigned char addr1[8], addr2[8];
struct hpsa_scsi_dev_t *sd;
- if (n >= HPSA_MAX_SCSI_DEVS_PER_HBA) {
+ if (n >= HPSA_MAX_DEVICES) {
dev_err(&h->pdev->dev, "too many devices, some will be "
"inaccessible.\n");
return -1;
@@ -611,7 +1297,7 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
/* If this device a non-zero lun of a multi-lun device
* byte 4 of the 8-byte LUN addr will contain the logical
- * unit no, zero otherise.
+ * unit no, zero otherwise.
*/
if (device->scsi3addr[4] == 0) {
/* This is not a non-zero lun of a multi-lun device */
@@ -623,17 +1309,19 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
/* This is a non-zero lun of a multi-lun device.
* Search through our list and find the device which
- * has the same 8 byte LUN address, excepting byte 4.
+ * has the same 8 byte LUN address, excepting byte 4 and 5.
* Assign the same bus and target for this new LUN.
* Use the logical unit number from the firmware.
*/
memcpy(addr1, device->scsi3addr, 8);
addr1[4] = 0;
+ addr1[5] = 0;
for (i = 0; i < n; i++) {
sd = h->dev[i];
memcpy(addr2, sd->scsi3addr, 8);
addr2[4] = 0;
- /* differ only in byte 4? */
+ addr2[5] = 0;
+ /* differ only in byte 4 and 5? */
if (memcmp(addr1, addr2, 8) == 0) {
device->bus = sd->bus;
device->target = sd->target;
@@ -645,7 +1333,7 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
dev_warn(&h->pdev->dev, "physical device with no LUN=0,"
" suspect firmware bug or unsupported hardware "
"configuration.\n");
- return -1;
+ return -1;
}
lun_assigned:
@@ -654,45 +1342,104 @@ lun_assigned:
h->ndevices++;
added[*nadded] = device;
(*nadded)++;
+ hpsa_show_dev_msg(KERN_INFO, h, device,
+ device->expose_device ? "added" : "masked");
+ return 0;
+}
+
+/*
+ * Called during a scan operation.
+ *
+ * Update an entry in h->dev[] array.
+ */
+static void hpsa_scsi_update_entry(struct ctlr_info *h,
+ int entry, struct hpsa_scsi_dev_t *new_entry)
+{
+ /* assumes h->devlock is held */
+ BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);
+
+ /* Raid level changed. */
+ h->dev[entry]->raid_level = new_entry->raid_level;
- /* initially, (before registering with scsi layer) we don't
- * know our hostno and we don't want to print anything first
- * time anyway (the scsi layer's inquiries will show that info)
+ /*
+ * ioacccel_handle may have changed for a dual domain disk
*/
- /* if (hostno != -1) */
- dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n",
- scsi_device_type(device->devtype), hostno,
- device->bus, device->target, device->lun);
- return 0;
+ h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
+
+ /* Raid offload parameters changed. Careful about the ordering. */
+ if (new_entry->offload_config && new_entry->offload_to_be_enabled) {
+ /*
+ * if drive is newly offload_enabled, we want to copy the
+ * raid map data first. If previously offload_enabled and
+ * offload_config were set, raid map data had better be
+ * the same as it was before. If raid map data has changed
+ * then it had better be the case that
+ * h->dev[entry]->offload_enabled is currently 0.
+ */
+ h->dev[entry]->raid_map = new_entry->raid_map;
+ h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
+ }
+ if (new_entry->offload_to_be_enabled) {
+ h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
+ wmb(); /* set ioaccel_handle *before* hba_ioaccel_enabled */
+ }
+ h->dev[entry]->hba_ioaccel_enabled = new_entry->hba_ioaccel_enabled;
+ h->dev[entry]->offload_config = new_entry->offload_config;
+ h->dev[entry]->offload_to_mirror = new_entry->offload_to_mirror;
+ h->dev[entry]->queue_depth = new_entry->queue_depth;
+
+ /*
+ * We can turn off ioaccel offload now, but need to delay turning
+ * ioaccel on until we can update h->dev[entry]->phys_disk[], but we
+ * can't do that until all the devices are updated.
+ */
+ h->dev[entry]->offload_to_be_enabled = new_entry->offload_to_be_enabled;
+
+ /*
+ * turn ioaccel off immediately if told to do so.
+ */
+ if (!new_entry->offload_to_be_enabled)
+ h->dev[entry]->offload_enabled = 0;
+
+ hpsa_show_dev_msg(KERN_INFO, h, h->dev[entry], "updated");
}
/* Replace an entry from h->dev[] array. */
-static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno,
+static void hpsa_scsi_replace_entry(struct ctlr_info *h,
int entry, struct hpsa_scsi_dev_t *new_entry,
struct hpsa_scsi_dev_t *added[], int *nadded,
struct hpsa_scsi_dev_t *removed[], int *nremoved)
{
/* assumes h->devlock is held */
- BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA);
+ BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);
removed[*nremoved] = h->dev[entry];
(*nremoved)++;
+
+ /*
+ * New physical devices won't have target/lun assigned yet
+ * so we need to preserve the values in the slot we are replacing.
+ */
+ if (new_entry->target == -1) {
+ new_entry->target = h->dev[entry]->target;
+ new_entry->lun = h->dev[entry]->lun;
+ }
+
h->dev[entry] = new_entry;
added[*nadded] = new_entry;
(*nadded)++;
- dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d changed.\n",
- scsi_device_type(new_entry->devtype), hostno, new_entry->bus,
- new_entry->target, new_entry->lun);
+
+ hpsa_show_dev_msg(KERN_INFO, h, new_entry, "replaced");
}
/* Remove an entry from h->dev[] array. */
-static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry,
+static void hpsa_scsi_remove_entry(struct ctlr_info *h, int entry,
struct hpsa_scsi_dev_t *removed[], int *nremoved)
{
/* assumes h->devlock is held */
int i;
struct hpsa_scsi_dev_t *sd;
- BUG_ON(entry < 0 || entry >= HPSA_MAX_SCSI_DEVS_PER_HBA);
+ BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);
sd = h->dev[entry];
removed[*nremoved] = h->dev[entry];
@@ -701,9 +1448,7 @@ static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry,
for (i = entry; i < h->ndevices-1; i++)
h->dev[i] = h->dev[i+1];
h->ndevices--;
- dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d removed.\n",
- scsi_device_type(sd->devtype), hostno, sd->bus, sd->target,
- sd->lun);
+ hpsa_show_dev_msg(KERN_INFO, h, sd, "removed");
}
#define SCSI3ADDR_EQ(a, b) ( \
@@ -762,10 +1507,43 @@ static inline int device_is_the_same(struct hpsa_scsi_dev_t *dev1,
return 1;
}
+static inline int device_updated(struct hpsa_scsi_dev_t *dev1,
+ struct hpsa_scsi_dev_t *dev2)
+{
+ /* Device attributes that can change, but don't mean
+ * that the device is a different device, nor that the OS
+ * needs to be told anything about the change.
+ */
+ if (dev1->raid_level != dev2->raid_level)
+ return 1;
+ if (dev1->offload_config != dev2->offload_config)
+ return 1;
+ if (dev1->offload_to_be_enabled != dev2->offload_to_be_enabled)
+ return 1;
+ if (!is_logical_dev_addr_mode(dev1->scsi3addr))
+ if (dev1->queue_depth != dev2->queue_depth)
+ return 1;
+ /*
+ * This can happen for dual domain devices. An active
+ * path change causes the ioaccel handle to change
+ *
+ * for example note the handle differences between p0 and p1
+ * Device WWN ,WWN hash,Handle
+ * D016 p0|0x3 [02]P2E:01:01,0x5000C5005FC4DACA,0x9B5616,0x01030003
+ * p1 0x5000C5005FC4DAC9,0x6798C0,0x00040004
+ */
+ if (dev1->ioaccel_handle != dev2->ioaccel_handle)
+ return 1;
+ return 0;
+}
+
/* Find needle in haystack. If exact match found, return DEVICE_SAME,
* and return needle location in *index. If scsi3addr matches, but not
* vendor, model, serial num, etc. return DEVICE_CHANGED, and return needle
- * location in *index. If needle not found, return DEVICE_NOT_FOUND.
+ * location in *index.
+ * In the case of a minor device attribute change, such as RAID level, just
+ * return DEVICE_UPDATED, along with the updated device's location in index.
+ * If needle not found, return DEVICE_NOT_FOUND.
*/
static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
struct hpsa_scsi_dev_t *haystack[], int haystack_size,
@@ -775,22 +1553,364 @@ static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
#define DEVICE_NOT_FOUND 0
#define DEVICE_CHANGED 1
#define DEVICE_SAME 2
+#define DEVICE_UPDATED 3
+ if (needle == NULL)
+ return DEVICE_NOT_FOUND;
+
for (i = 0; i < haystack_size; i++) {
if (haystack[i] == NULL) /* previously removed. */
continue;
if (SCSI3ADDR_EQ(needle->scsi3addr, haystack[i]->scsi3addr)) {
*index = i;
- if (device_is_the_same(needle, haystack[i]))
+ if (device_is_the_same(needle, haystack[i])) {
+ if (device_updated(needle, haystack[i]))
+ return DEVICE_UPDATED;
return DEVICE_SAME;
- else
+ } else {
+ /* Keep offline devices offline */
+ if (needle->volume_offline)
+ return DEVICE_NOT_FOUND;
return DEVICE_CHANGED;
+ }
}
}
*index = -1;
return DEVICE_NOT_FOUND;
}
-static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
+static void hpsa_monitor_offline_device(struct ctlr_info *h,
+ unsigned char scsi3addr[])
+{
+ struct offline_device_entry *device;
+ unsigned long flags;
+
+ /* Check to see if device is already on the list */
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_for_each_entry(device, &h->offline_device_list, offline_list) {
+ if (memcmp(device->scsi3addr, scsi3addr,
+ sizeof(device->scsi3addr)) == 0) {
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+
+ /* Device is not on the list, add it. */
+ device = kmalloc(sizeof(*device), GFP_KERNEL);
+ if (!device)
+ return;
+
+ memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr));
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_add_tail(&device->offline_list, &h->offline_device_list);
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+}
+
+/* Print a message explaining various offline volume states */
+static void hpsa_show_volume_status(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *sd)
+{
+ if (sd->volume_offline == HPSA_VPD_LV_STATUS_UNSUPPORTED)
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume status is not available through vital product data pages.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ switch (sd->volume_offline) {
+ case HPSA_LV_OK:
+ break;
+ case HPSA_LV_UNDERGOING_ERASE:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is undergoing background erase process.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_NOT_AVAILABLE:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is waiting for transforming volume.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_UNDERGOING_RPI:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is undergoing rapid parity init.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_PENDING_RPI:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is queued for rapid parity initialization process.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_ENCRYPTED_NO_KEY:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is encrypted and cannot be accessed because key is not present.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_PLAINTEXT_IN_ENCRYPT_ONLY_CONTROLLER:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is not encrypted and cannot be accessed because controller is in encryption-only mode.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_UNDERGOING_ENCRYPTION:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is undergoing encryption process.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_UNDERGOING_ENCRYPTION_REKEYING:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is undergoing encryption re-keying process.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is encrypted and cannot be accessed because controller does not have encryption enabled.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_PENDING_ENCRYPTION:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is pending migration to encrypted state, but process has not started.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ case HPSA_LV_PENDING_ENCRYPTION_REKEYING:
+ dev_info(&h->pdev->dev,
+ "C%d:B%d:T%d:L%d Volume is encrypted and is pending encryption rekeying.\n",
+ h->scsi_host->host_no,
+ sd->bus, sd->target, sd->lun);
+ break;
+ }
+}
+
+/*
+ * Figure the list of physical drive pointers for a logical drive with
+ * raid offload configured.
+ */
+static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *dev[], int ndevices,
+ struct hpsa_scsi_dev_t *logical_drive)
+{
+ struct raid_map_data *map = &logical_drive->raid_map;
+ struct raid_map_disk_data *dd = &map->data[0];
+ int i, j;
+ int total_disks_per_row = le16_to_cpu(map->data_disks_per_row) +
+ le16_to_cpu(map->metadata_disks_per_row);
+ int nraid_map_entries = le16_to_cpu(map->row_cnt) *
+ le16_to_cpu(map->layout_map_count) *
+ total_disks_per_row;
+ int nphys_disk = le16_to_cpu(map->layout_map_count) *
+ total_disks_per_row;
+ int qdepth;
+
+ if (nraid_map_entries > RAID_MAP_MAX_ENTRIES)
+ nraid_map_entries = RAID_MAP_MAX_ENTRIES;
+
+ logical_drive->nphysical_disks = nraid_map_entries;
+
+ qdepth = 0;
+ for (i = 0; i < nraid_map_entries; i++) {
+ logical_drive->phys_disk[i] = NULL;
+ if (!logical_drive->offload_config)
+ continue;
+ for (j = 0; j < ndevices; j++) {
+ if (dev[j] == NULL)
+ continue;
+ if (dev[j]->devtype != TYPE_DISK &&
+ dev[j]->devtype != TYPE_ZBC)
+ continue;
+ if (is_logical_device(dev[j]))
+ continue;
+ if (dev[j]->ioaccel_handle != dd[i].ioaccel_handle)
+ continue;
+
+ logical_drive->phys_disk[i] = dev[j];
+ if (i < nphys_disk)
+ qdepth = min(h->nr_cmds, qdepth +
+ logical_drive->phys_disk[i]->queue_depth);
+ break;
+ }
+
+ /*
+ * This can happen if a physical drive is removed and
+ * the logical drive is degraded. In that case, the RAID
+ * map data will refer to a physical disk which isn't actually
+ * present. And in that case offload_enabled should already
+ * be 0, but we'll turn it off here just in case
+ */
+ if (!logical_drive->phys_disk[i]) {
+ dev_warn(&h->pdev->dev,
+ "%s: [%d:%d:%d:%d] A phys disk component of LV is missing, turning off offload_enabled for LV.\n",
+ __func__,
+ h->scsi_host->host_no, logical_drive->bus,
+ logical_drive->target, logical_drive->lun);
+ hpsa_turn_off_ioaccel_for_device(logical_drive);
+ logical_drive->queue_depth = 8;
+ }
+ }
+ if (nraid_map_entries)
+ /*
+ * This is correct for reads, too high for full stripe writes,
+ * way too high for partial stripe writes
+ */
+ logical_drive->queue_depth = qdepth;
+ else {
+ if (logical_drive->external)
+ logical_drive->queue_depth = EXTERNAL_QD;
+ else
+ logical_drive->queue_depth = h->nr_cmds;
+ }
+}
+
+static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *dev[], int ndevices)
+{
+ int i;
+
+ for (i = 0; i < ndevices; i++) {
+ if (dev[i] == NULL)
+ continue;
+ if (dev[i]->devtype != TYPE_DISK &&
+ dev[i]->devtype != TYPE_ZBC)
+ continue;
+ if (!is_logical_device(dev[i]))
+ continue;
+
+ /*
+ * If offload is currently enabled, the RAID map and
+ * phys_disk[] assignment *better* not be changing
+ * because we would be changing ioaccel phsy_disk[] pointers
+ * on a ioaccel volume processing I/O requests.
+ *
+ * If an ioaccel volume status changed, initially because it was
+ * re-configured and thus underwent a transformation, or
+ * a drive failed, we would have received a state change
+ * request and ioaccel should have been turned off. When the
+ * transformation completes, we get another state change
+ * request to turn ioaccel back on. In this case, we need
+ * to update the ioaccel information.
+ *
+ * Thus: If it is not currently enabled, but will be after
+ * the scan completes, make sure the ioaccel pointers
+ * are up to date.
+ */
+
+ if (!dev[i]->offload_enabled && dev[i]->offload_to_be_enabled)
+ hpsa_figure_phys_disk_ptrs(h, dev, ndevices, dev[i]);
+ }
+}
+
+static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
+{
+ int rc = 0;
+
+ if (!h->scsi_host)
+ return 1;
+
+ if (is_logical_device(device)) /* RAID */
+ rc = scsi_add_device(h->scsi_host, device->bus,
+ device->target, device->lun);
+ else /* HBA */
+ rc = hpsa_add_sas_device(h->sas_host, device);
+
+ return rc;
+}
+
+static int hpsa_find_outstanding_commands_for_dev(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *dev)
+{
+ int i;
+ int count = 0;
+
+ for (i = 0; i < h->nr_cmds; i++) {
+ struct CommandList *c = h->cmd_pool + i;
+ int refcount = atomic_inc_return(&c->refcount);
+
+ if (refcount > 1 && hpsa_cmd_dev_match(h, c, dev,
+ dev->scsi3addr)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&h->lock, flags); /* Implied MB */
+ if (!hpsa_is_cmd_idle(c))
+ ++count;
+ spin_unlock_irqrestore(&h->lock, flags);
+ }
+
+ cmd_free(h, c);
+ }
+
+ return count;
+}
+
+#define NUM_WAIT 20
+static void hpsa_wait_for_outstanding_commands_for_dev(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *device)
+{
+ int cmds = 0;
+ int waits = 0;
+ int num_wait = NUM_WAIT;
+
+ if (device->external)
+ num_wait = HPSA_EH_PTRAID_TIMEOUT;
+
+ while (1) {
+ cmds = hpsa_find_outstanding_commands_for_dev(h, device);
+ if (cmds == 0)
+ break;
+ if (++waits > num_wait)
+ break;
+ msleep(1000);
+ }
+
+ if (waits > num_wait) {
+ dev_warn(&h->pdev->dev,
+ "%s: removing device [%d:%d:%d:%d] with %d outstanding commands!\n",
+ __func__,
+ h->scsi_host->host_no,
+ device->bus, device->target, device->lun, cmds);
+ }
+}
+
+static void hpsa_remove_device(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *device)
+{
+ struct scsi_device *sdev = NULL;
+
+ if (!h->scsi_host)
+ return;
+
+ /*
+ * Allow for commands to drain
+ */
+ device->removed = 1;
+ hpsa_wait_for_outstanding_commands_for_dev(h, device);
+
+ if (is_logical_device(device)) { /* RAID */
+ sdev = scsi_device_lookup(h->scsi_host, device->bus,
+ device->target, device->lun);
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ } else {
+ /*
+ * We don't expect to get here. Future commands
+ * to this device will get a selection timeout as
+ * if the device were gone.
+ */
+ hpsa_show_dev_msg(KERN_WARNING, h, device,
+ "didn't find device for removal.");
+ }
+ } else { /* HBA */
+
+ hpsa_remove_sas_device(device);
+ }
+}
+
+static void adjust_hpsa_scsi_table(struct ctlr_info *h,
struct hpsa_scsi_dev_t *sd[], int nsds)
{
/* sd contains scsi3 addresses and devtypes, and inquiry
@@ -802,12 +1922,21 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
unsigned long flags;
struct hpsa_scsi_dev_t **added, **removed;
int nadded, nremoved;
- struct Scsi_Host *sh = NULL;
- added = kzalloc(sizeof(*added) * HPSA_MAX_SCSI_DEVS_PER_HBA,
- GFP_KERNEL);
- removed = kzalloc(sizeof(*removed) * HPSA_MAX_SCSI_DEVS_PER_HBA,
- GFP_KERNEL);
+ /*
+ * A reset can cause a device status to change
+ * re-schedule the scan to see what happened.
+ */
+ spin_lock_irqsave(&h->reset_lock, flags);
+ if (h->reset_in_progress) {
+ h->drv_req_rescan = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+
+ added = kcalloc(HPSA_MAX_DEVICES, sizeof(*added), GFP_KERNEL);
+ removed = kcalloc(HPSA_MAX_DEVICES, sizeof(*removed), GFP_KERNEL);
if (!added || !removed) {
dev_warn(&h->pdev->dev, "out of memory in "
@@ -821,6 +1950,8 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
* sd[] and remove them from h->dev[], and for any
* devices which have changed, remove the old device
* info and add the new device info.
+ * If minor device attributes change, just update
+ * the existing device structure.
*/
i = 0;
nremoved = 0;
@@ -830,17 +1961,18 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
device_change = hpsa_scsi_find_entry(csd, sd, nsds, &entry);
if (device_change == DEVICE_NOT_FOUND) {
changes++;
- hpsa_scsi_remove_entry(h, hostno, i,
- removed, &nremoved);
+ hpsa_scsi_remove_entry(h, i, removed, &nremoved);
continue; /* remove ^^^, hence i not incremented */
} else if (device_change == DEVICE_CHANGED) {
changes++;
- hpsa_scsi_replace_entry(h, hostno, i, sd[entry],
+ hpsa_scsi_replace_entry(h, i, sd[entry],
added, &nadded, removed, &nremoved);
/* Set it to NULL to prevent it from being freed
* at the bottom of hpsa_update_scsi_devices()
*/
sd[entry] = NULL;
+ } else if (device_change == DEVICE_UPDATED) {
+ hpsa_scsi_update_entry(h, i, sd[entry]);
}
i++;
}
@@ -852,12 +1984,23 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
for (i = 0; i < nsds; i++) {
if (!sd[i]) /* if already added above. */
continue;
+
+ /* Don't add devices which are NOT READY, FORMAT IN PROGRESS
+ * as the SCSI mid-layer does not handle such devices well.
+ * It relentlessly loops sending TUR at 3Hz, then READ(10)
+ * at 160Hz, and prevents the system from coming up.
+ */
+ if (sd[i]->volume_offline) {
+ hpsa_show_volume_status(h, sd[i]);
+ hpsa_show_dev_msg(KERN_INFO, h, sd[i], "offline");
+ continue;
+ }
+
device_change = hpsa_scsi_find_entry(sd[i], h->dev,
h->ndevices, &entry);
if (device_change == DEVICE_NOT_FOUND) {
changes++;
- if (hpsa_scsi_add_entry(h, hostno, sd[i],
- added, &nadded) != 0)
+ if (hpsa_scsi_add_entry(h, sd[i], added, &nadded) != 0)
break;
sd[i] = NULL; /* prevent from being freed later. */
} else if (device_change == DEVICE_CHANGED) {
@@ -868,49 +2011,70 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
/* but if it does happen, we just ignore that device */
}
}
+ hpsa_update_log_drive_phys_drive_ptrs(h, h->dev, h->ndevices);
+
+ /*
+ * Now that h->dev[]->phys_disk[] is coherent, we can enable
+ * any logical drives that need it enabled.
+ *
+ * The raid map should be current by now.
+ *
+ * We are updating the device list used for I/O requests.
+ */
+ for (i = 0; i < h->ndevices; i++) {
+ if (h->dev[i] == NULL)
+ continue;
+ h->dev[i]->offload_enabled = h->dev[i]->offload_to_be_enabled;
+ }
+
spin_unlock_irqrestore(&h->devlock, flags);
+ /* Monitor devices which are in one of several NOT READY states to be
+ * brought online later. This must be done without holding h->devlock,
+ * so don't touch h->dev[]
+ */
+ for (i = 0; i < nsds; i++) {
+ if (!sd[i]) /* if already added above. */
+ continue;
+ if (sd[i]->volume_offline)
+ hpsa_monitor_offline_device(h, sd[i]->scsi3addr);
+ }
+
/* Don't notify scsi mid layer of any changes the first time through
* (or if there are no changes) scsi_scan_host will do it later the
* first time through.
*/
- if (hostno == -1 || !changes)
+ if (!changes)
goto free_and_out;
- sh = h->scsi_host;
/* Notify scsi mid layer of any removed devices */
for (i = 0; i < nremoved; i++) {
- struct scsi_device *sdev =
- scsi_device_lookup(sh, removed[i]->bus,
- removed[i]->target, removed[i]->lun);
- if (sdev != NULL) {
- scsi_remove_device(sdev);
- scsi_device_put(sdev);
- } else {
- /* We don't expect to get here.
- * future cmds to this device will get selection
- * timeout as if the device was gone.
- */
- dev_warn(&h->pdev->dev, "didn't find c%db%dt%dl%d "
- " for removal.", hostno, removed[i]->bus,
- removed[i]->target, removed[i]->lun);
- }
+ if (removed[i] == NULL)
+ continue;
+ if (removed[i]->expose_device)
+ hpsa_remove_device(h, removed[i]);
kfree(removed[i]);
removed[i] = NULL;
}
/* Notify scsi mid layer of any added devices */
for (i = 0; i < nadded; i++) {
- if (scsi_add_device(sh, added[i]->bus,
- added[i]->target, added[i]->lun) == 0)
+ int rc = 0;
+
+ if (added[i] == NULL)
+ continue;
+ if (!(added[i]->expose_device))
continue;
- dev_warn(&h->pdev->dev, "scsi_add_device c%db%dt%dl%d failed, "
- "device not added.\n", hostno, added[i]->bus,
- added[i]->target, added[i]->lun);
+ rc = hpsa_add_device(h, added[i]);
+ if (!rc)
+ continue;
+ dev_warn(&h->pdev->dev,
+ "addition failed %d, device not added.", rc);
/* now we have to remove it from h->dev,
* since it didn't get added to scsi mid layer
*/
fixup_botched_add(h, added[i]);
+ h->drv_req_rescan = 1;
}
free_and_out:
@@ -919,7 +2083,7 @@ free_and_out:
}
/*
- * Lookup bus/target/lun and retrun corresponding struct hpsa_scsi_dev_t *
+ * Lookup bus/target/lun and return corresponding struct hpsa_scsi_dev_t *
* Assume's h->devlock is held.
*/
static struct hpsa_scsi_dev_t *lookup_hpsa_scsi_dev(struct ctlr_info *h,
@@ -936,26 +2100,122 @@ static struct hpsa_scsi_dev_t *lookup_hpsa_scsi_dev(struct ctlr_info *h,
return NULL;
}
-/* link sdev->hostdata to our per-device structure. */
-static int hpsa_slave_alloc(struct scsi_device *sdev)
+static int hpsa_sdev_init(struct scsi_device *sdev)
{
- struct hpsa_scsi_dev_t *sd;
+ struct hpsa_scsi_dev_t *sd = NULL;
unsigned long flags;
struct ctlr_info *h;
h = sdev_to_hba(sdev);
spin_lock_irqsave(&h->devlock, flags);
- sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev),
- sdev_id(sdev), sdev->lun);
- if (sd != NULL)
+ if (sdev_channel(sdev) == HPSA_PHYSICAL_DEVICE_BUS) {
+ struct scsi_target *starget;
+ struct sas_rphy *rphy;
+
+ starget = scsi_target(sdev);
+ rphy = target_to_rphy(starget);
+ sd = hpsa_find_device_by_sas_rphy(h, rphy);
+ if (sd) {
+ sd->target = sdev_id(sdev);
+ sd->lun = sdev->lun;
+ }
+ }
+ if (!sd)
+ sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev),
+ sdev_id(sdev), sdev->lun);
+
+ if (sd && sd->expose_device) {
+ atomic_set(&sd->ioaccel_cmds_out, 0);
sdev->hostdata = sd;
+ } else
+ sdev->hostdata = NULL;
spin_unlock_irqrestore(&h->devlock, flags);
return 0;
}
-static void hpsa_slave_destroy(struct scsi_device *sdev)
+/* configure scsi device based on internal per-device structure */
+#define CTLR_TIMEOUT (120 * HZ)
+static int hpsa_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ struct hpsa_scsi_dev_t *sd;
+ int queue_depth;
+
+ sd = sdev->hostdata;
+ sdev->no_uld_attach = !sd || !sd->expose_device;
+
+ if (sd) {
+ sd->was_removed = 0;
+ queue_depth = sd->queue_depth != 0 ?
+ sd->queue_depth : sdev->host->can_queue;
+ if (sd->external) {
+ queue_depth = EXTERNAL_QD;
+ sdev->eh_timeout = HPSA_EH_PTRAID_TIMEOUT;
+ blk_queue_rq_timeout(sdev->request_queue,
+ HPSA_EH_PTRAID_TIMEOUT);
+ }
+ if (is_hba_lunid(sd->scsi3addr)) {
+ sdev->eh_timeout = CTLR_TIMEOUT;
+ blk_queue_rq_timeout(sdev->request_queue, CTLR_TIMEOUT);
+ }
+ } else {
+ queue_depth = sdev->host->can_queue;
+ }
+
+ scsi_change_queue_depth(sdev, queue_depth);
+
+ return 0;
+}
+
+static void hpsa_sdev_destroy(struct scsi_device *sdev)
{
- /* nothing to do. */
+ struct hpsa_scsi_dev_t *hdev = NULL;
+
+ hdev = sdev->hostdata;
+
+ if (hdev)
+ hdev->was_removed = 1;
+}
+
+static void hpsa_free_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+ int i;
+
+ if (!h->ioaccel2_cmd_sg_list)
+ return;
+ for (i = 0; i < h->nr_cmds; i++) {
+ kfree(h->ioaccel2_cmd_sg_list[i]);
+ h->ioaccel2_cmd_sg_list[i] = NULL;
+ }
+ kfree(h->ioaccel2_cmd_sg_list);
+ h->ioaccel2_cmd_sg_list = NULL;
+}
+
+static int hpsa_allocate_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+ int i;
+
+ if (h->chainsize <= 0)
+ return 0;
+
+ h->ioaccel2_cmd_sg_list =
+ kcalloc(h->nr_cmds, sizeof(*h->ioaccel2_cmd_sg_list),
+ GFP_KERNEL);
+ if (!h->ioaccel2_cmd_sg_list)
+ return -ENOMEM;
+ for (i = 0; i < h->nr_cmds; i++) {
+ h->ioaccel2_cmd_sg_list[i] =
+ kmalloc_array(h->maxsgentries,
+ sizeof(*h->ioaccel2_cmd_sg_list[i]),
+ GFP_KERNEL);
+ if (!h->ioaccel2_cmd_sg_list[i])
+ goto clean;
+ }
+ return 0;
+
+clean:
+ hpsa_free_ioaccel2_sg_chain_blocks(h);
+ return -ENOMEM;
}
static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
@@ -972,22 +2232,25 @@ static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
h->cmd_sg_list = NULL;
}
-static int hpsa_allocate_sg_chain_blocks(struct ctlr_info *h)
+static int hpsa_alloc_sg_chain_blocks(struct ctlr_info *h)
{
int i;
if (h->chainsize <= 0)
return 0;
- h->cmd_sg_list = kzalloc(sizeof(*h->cmd_sg_list) * h->nr_cmds,
- GFP_KERNEL);
+ h->cmd_sg_list = kcalloc(h->nr_cmds, sizeof(*h->cmd_sg_list),
+ GFP_KERNEL);
if (!h->cmd_sg_list)
return -ENOMEM;
+
for (i = 0; i < h->nr_cmds; i++) {
- h->cmd_sg_list[i] = kmalloc(sizeof(*h->cmd_sg_list[i]) *
- h->chainsize, GFP_KERNEL);
+ h->cmd_sg_list[i] = kmalloc_array(h->chainsize,
+ sizeof(*h->cmd_sg_list[i]),
+ GFP_KERNEL);
if (!h->cmd_sg_list[i])
goto clean;
+
}
return 0;
@@ -996,36 +2259,303 @@ clean:
return -ENOMEM;
}
-static void hpsa_map_sg_chain_block(struct ctlr_info *h,
+static int hpsa_map_ioaccel2_sg_chain_block(struct ctlr_info *h,
+ struct io_accel2_cmd *cp, struct CommandList *c)
+{
+ struct ioaccel2_sg_element *chain_block;
+ u64 temp64;
+ u32 chain_size;
+
+ chain_block = h->ioaccel2_cmd_sg_list[c->cmdindex];
+ chain_size = le32_to_cpu(cp->sg[0].length);
+ temp64 = dma_map_single(&h->pdev->dev, chain_block, chain_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&h->pdev->dev, temp64)) {
+ /* prevent subsequent unmapping */
+ cp->sg->address = 0;
+ return -1;
+ }
+ cp->sg->address = cpu_to_le64(temp64);
+ return 0;
+}
+
+static void hpsa_unmap_ioaccel2_sg_chain_block(struct ctlr_info *h,
+ struct io_accel2_cmd *cp)
+{
+ struct ioaccel2_sg_element *chain_sg;
+ u64 temp64;
+ u32 chain_size;
+
+ chain_sg = cp->sg;
+ temp64 = le64_to_cpu(chain_sg->address);
+ chain_size = le32_to_cpu(cp->sg[0].length);
+ dma_unmap_single(&h->pdev->dev, temp64, chain_size, DMA_TO_DEVICE);
+}
+
+static int hpsa_map_sg_chain_block(struct ctlr_info *h,
struct CommandList *c)
{
struct SGDescriptor *chain_sg, *chain_block;
u64 temp64;
+ u32 chain_len;
chain_sg = &c->SG[h->max_cmd_sg_entries - 1];
chain_block = h->cmd_sg_list[c->cmdindex];
- chain_sg->Ext = HPSA_SG_CHAIN;
- chain_sg->Len = sizeof(*chain_sg) *
- (c->Header.SGTotal - h->max_cmd_sg_entries);
- temp64 = pci_map_single(h->pdev, chain_block, chain_sg->Len,
- PCI_DMA_TODEVICE);
- chain_sg->Addr.lower = (u32) (temp64 & 0x0FFFFFFFFULL);
- chain_sg->Addr.upper = (u32) ((temp64 >> 32) & 0x0FFFFFFFFULL);
+ chain_sg->Ext = cpu_to_le32(HPSA_SG_CHAIN);
+ chain_len = sizeof(*chain_sg) *
+ (le16_to_cpu(c->Header.SGTotal) - h->max_cmd_sg_entries);
+ chain_sg->Len = cpu_to_le32(chain_len);
+ temp64 = dma_map_single(&h->pdev->dev, chain_block, chain_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&h->pdev->dev, temp64)) {
+ /* prevent subsequent unmapping */
+ chain_sg->Addr = cpu_to_le64(0);
+ return -1;
+ }
+ chain_sg->Addr = cpu_to_le64(temp64);
+ return 0;
}
static void hpsa_unmap_sg_chain_block(struct ctlr_info *h,
struct CommandList *c)
{
struct SGDescriptor *chain_sg;
- union u64bit temp64;
- if (c->Header.SGTotal <= h->max_cmd_sg_entries)
+ if (le16_to_cpu(c->Header.SGTotal) <= h->max_cmd_sg_entries)
return;
chain_sg = &c->SG[h->max_cmd_sg_entries - 1];
- temp64.val32.lower = chain_sg->Addr.lower;
- temp64.val32.upper = chain_sg->Addr.upper;
- pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE);
+ dma_unmap_single(&h->pdev->dev, le64_to_cpu(chain_sg->Addr),
+ le32_to_cpu(chain_sg->Len), DMA_TO_DEVICE);
+}
+
+
+/* Decode the various types of errors on ioaccel2 path.
+ * Return 1 for any error that should generate a RAID path retry.
+ * Return 0 for errors that don't require a RAID path retry.
+ */
+static int handle_ioaccel_mode2_error(struct ctlr_info *h,
+ struct CommandList *c,
+ struct scsi_cmnd *cmd,
+ struct io_accel2_cmd *c2,
+ struct hpsa_scsi_dev_t *dev)
+{
+ int data_len;
+ int retry = 0;
+ u32 ioaccel2_resid = 0;
+
+ switch (c2->error_data.serv_response) {
+ case IOACCEL2_SERV_RESPONSE_COMPLETE:
+ switch (c2->error_data.status) {
+ case IOACCEL2_STATUS_SR_TASK_COMP_GOOD:
+ if (cmd)
+ cmd->result = 0;
+ break;
+ case IOACCEL2_STATUS_SR_TASK_COMP_CHK_COND:
+ cmd->result |= SAM_STAT_CHECK_CONDITION;
+ if (c2->error_data.data_present !=
+ IOACCEL2_SENSE_DATA_PRESENT) {
+ memset(cmd->sense_buffer, 0,
+ SCSI_SENSE_BUFFERSIZE);
+ break;
+ }
+ /* copy the sense data */
+ data_len = c2->error_data.sense_data_len;
+ if (data_len > SCSI_SENSE_BUFFERSIZE)
+ data_len = SCSI_SENSE_BUFFERSIZE;
+ if (data_len > sizeof(c2->error_data.sense_data_buff))
+ data_len =
+ sizeof(c2->error_data.sense_data_buff);
+ memcpy(cmd->sense_buffer,
+ c2->error_data.sense_data_buff, data_len);
+ retry = 1;
+ break;
+ case IOACCEL2_STATUS_SR_TASK_COMP_BUSY:
+ retry = 1;
+ break;
+ case IOACCEL2_STATUS_SR_TASK_COMP_RES_CON:
+ retry = 1;
+ break;
+ case IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL:
+ retry = 1;
+ break;
+ case IOACCEL2_STATUS_SR_TASK_COMP_ABORTED:
+ retry = 1;
+ break;
+ default:
+ retry = 1;
+ break;
+ }
+ break;
+ case IOACCEL2_SERV_RESPONSE_FAILURE:
+ switch (c2->error_data.status) {
+ case IOACCEL2_STATUS_SR_IO_ERROR:
+ case IOACCEL2_STATUS_SR_IO_ABORTED:
+ case IOACCEL2_STATUS_SR_OVERRUN:
+ retry = 1;
+ break;
+ case IOACCEL2_STATUS_SR_UNDERRUN:
+ cmd->result = (DID_OK << 16); /* host byte */
+ ioaccel2_resid = get_unaligned_le32(
+ &c2->error_data.resid_cnt[0]);
+ scsi_set_resid(cmd, ioaccel2_resid);
+ break;
+ case IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE:
+ case IOACCEL2_STATUS_SR_INVALID_DEVICE:
+ case IOACCEL2_STATUS_SR_IOACCEL_DISABLED:
+ /*
+ * Did an HBA disk disappear? We will eventually
+ * get a state change event from the controller but
+ * in the meantime, we need to tell the OS that the
+ * HBA disk is no longer there and stop I/O
+ * from going down. This allows the potential re-insert
+ * of the disk to get the same device node.
+ */
+ if (dev->physical_device && dev->expose_device) {
+ cmd->result = DID_NO_CONNECT << 16;
+ dev->removed = 1;
+ h->drv_req_rescan = 1;
+ dev_warn(&h->pdev->dev,
+ "%s: device is gone!\n", __func__);
+ } else
+ /*
+ * Retry by sending down the RAID path.
+ * We will get an event from ctlr to
+ * trigger rescan regardless.
+ */
+ retry = 1;
+ break;
+ default:
+ retry = 1;
+ }
+ break;
+ case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE:
+ break;
+ case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS:
+ break;
+ case IOACCEL2_SERV_RESPONSE_TMF_REJECTED:
+ retry = 1;
+ break;
+ case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN:
+ break;
+ default:
+ retry = 1;
+ break;
+ }
+
+ if (dev->in_reset)
+ retry = 0;
+
+ return retry; /* retry on raid path? */
+}
+
+static void hpsa_cmd_resolve_events(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ struct hpsa_scsi_dev_t *dev = c->device;
+
+ /*
+ * Reset c->scsi_cmd here so that the reset handler will know
+ * this command has completed. Then, check to see if the handler is
+ * waiting for this command, and, if so, wake it.
+ */
+ c->scsi_cmd = SCSI_CMD_IDLE;
+ mb(); /* Declare command idle before checking for pending events. */
+ if (dev) {
+ atomic_dec(&dev->commands_outstanding);
+ if (dev->in_reset &&
+ atomic_read(&dev->commands_outstanding) <= 0)
+ wake_up_all(&h->event_sync_wait_queue);
+ }
+}
+
+static void hpsa_cmd_resolve_and_free(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ hpsa_cmd_resolve_events(h, c);
+ cmd_tagged_free(h, c);
+}
+
+static void hpsa_cmd_free_and_done(struct ctlr_info *h,
+ struct CommandList *c, struct scsi_cmnd *cmd)
+{
+ hpsa_cmd_resolve_and_free(h, c);
+ if (cmd)
+ scsi_done(cmd);
+}
+
+static void hpsa_retry_cmd(struct ctlr_info *h, struct CommandList *c)
+{
+ INIT_WORK(&c->work, hpsa_command_resubmit_worker);
+ queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work);
+}
+
+static void process_ioaccel2_completion(struct ctlr_info *h,
+ struct CommandList *c, struct scsi_cmnd *cmd,
+ struct hpsa_scsi_dev_t *dev)
+{
+ struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+
+ /* check for good status */
+ if (likely(c2->error_data.serv_response == 0 &&
+ c2->error_data.status == 0)) {
+ cmd->result = 0;
+ return hpsa_cmd_free_and_done(h, c, cmd);
+ }
+
+ /*
+ * Any RAID offload error results in retry which will use
+ * the normal I/O path so the controller can handle whatever is
+ * wrong.
+ */
+ if (is_logical_device(dev) &&
+ c2->error_data.serv_response ==
+ IOACCEL2_SERV_RESPONSE_FAILURE) {
+ if (c2->error_data.status ==
+ IOACCEL2_STATUS_SR_IOACCEL_DISABLED) {
+ hpsa_turn_off_ioaccel_for_device(dev);
+ }
+
+ if (dev->in_reset) {
+ cmd->result = DID_RESET << 16;
+ return hpsa_cmd_free_and_done(h, c, cmd);
+ }
+
+ return hpsa_retry_cmd(h, c);
+ }
+
+ if (handle_ioaccel_mode2_error(h, c, cmd, c2, dev))
+ return hpsa_retry_cmd(h, c);
+
+ return hpsa_cmd_free_and_done(h, c, cmd);
+}
+
+/* Returns 0 on success, < 0 otherwise. */
+static int hpsa_evaluate_tmf_status(struct ctlr_info *h,
+ struct CommandList *cp)
+{
+ u8 tmf_status = cp->err_info->ScsiStatus;
+
+ switch (tmf_status) {
+ case CISS_TMF_COMPLETE:
+ /*
+ * CISS_TMF_COMPLETE never happens, instead,
+ * ei->CommandStatus == 0 for this case.
+ */
+ case CISS_TMF_SUCCESS:
+ return 0;
+ case CISS_TMF_INVALID_FRAME:
+ case CISS_TMF_NOT_SUPPORTED:
+ case CISS_TMF_FAILED:
+ case CISS_TMF_WRONG_LUN:
+ case CISS_TMF_OVERLAPPED_TAG:
+ break;
+ default:
+ dev_warn(&h->pdev->dev, "Unknown TMF status: 0x%02x\n",
+ tmf_status);
+ break;
+ }
+ return -tmf_status;
}
static void complete_scsi_command(struct CommandList *cp)
@@ -1033,121 +2563,131 @@ static void complete_scsi_command(struct CommandList *cp)
struct scsi_cmnd *cmd;
struct ctlr_info *h;
struct ErrorInfo *ei;
+ struct hpsa_scsi_dev_t *dev;
+ struct io_accel2_cmd *c2;
- unsigned char sense_key;
- unsigned char asc; /* additional sense code */
- unsigned char ascq; /* additional sense code qualifier */
+ u8 sense_key;
+ u8 asc; /* additional sense code */
+ u8 ascq; /* additional sense code qualifier */
unsigned long sense_data_size;
ei = cp->err_info;
- cmd = (struct scsi_cmnd *) cp->scsi_cmd;
+ cmd = cp->scsi_cmd;
h = cp->h;
+ if (!cmd->device) {
+ cmd->result = DID_NO_CONNECT << 16;
+ return hpsa_cmd_free_and_done(h, cp, cmd);
+ }
+
+ dev = cmd->device->hostdata;
+ if (!dev) {
+ cmd->result = DID_NO_CONNECT << 16;
+ return hpsa_cmd_free_and_done(h, cp, cmd);
+ }
+ c2 = &h->ioaccel2_cmd_pool[cp->cmdindex];
+
scsi_dma_unmap(cmd); /* undo the DMA mappings */
- if (cp->Header.SGTotal > h->max_cmd_sg_entries)
+ if ((cp->cmd_type == CMD_SCSI) &&
+ (le16_to_cpu(cp->Header.SGTotal) > h->max_cmd_sg_entries))
hpsa_unmap_sg_chain_block(h, cp);
- cmd->result = (DID_OK << 16); /* host byte */
- cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
- cmd->result |= ei->ScsiStatus;
+ if ((cp->cmd_type == CMD_IOACCEL2) &&
+ (c2->sg[0].chain_indicator == IOACCEL2_CHAIN))
+ hpsa_unmap_ioaccel2_sg_chain_block(h, c2);
- /* copy the sense data whether we need to or not. */
- if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo))
- sense_data_size = SCSI_SENSE_BUFFERSIZE;
- else
- sense_data_size = sizeof(ei->SenseInfo);
- if (ei->SenseLen < sense_data_size)
- sense_data_size = ei->SenseLen;
+ cmd->result = (DID_OK << 16); /* host byte */
+
+ /* SCSI command has already been cleaned up in SML */
+ if (dev->was_removed) {
+ hpsa_cmd_resolve_and_free(h, cp);
+ return;
+ }
+
+ if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1) {
+ if (dev->physical_device && dev->expose_device &&
+ dev->removed) {
+ cmd->result = DID_NO_CONNECT << 16;
+ return hpsa_cmd_free_and_done(h, cp, cmd);
+ }
+ if (likely(cp->phys_disk != NULL))
+ atomic_dec(&cp->phys_disk->ioaccel_cmds_out);
+ }
+
+ /*
+ * We check for lockup status here as it may be set for
+ * CMD_SCSI, CMD_IOACCEL1 and CMD_IOACCEL2 commands by
+ * fail_all_oustanding_cmds()
+ */
+ if (unlikely(ei->CommandStatus == CMD_CTLR_LOCKUP)) {
+ /* DID_NO_CONNECT will prevent a retry */
+ cmd->result = DID_NO_CONNECT << 16;
+ return hpsa_cmd_free_and_done(h, cp, cmd);
+ }
+
+ if (cp->cmd_type == CMD_IOACCEL2)
+ return process_ioaccel2_completion(h, cp, cmd, dev);
- memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size);
scsi_set_resid(cmd, ei->ResidualCnt);
+ if (ei->CommandStatus == 0)
+ return hpsa_cmd_free_and_done(h, cp, cmd);
- if (ei->CommandStatus == 0) {
- cmd->scsi_done(cmd);
- cmd_free(h, cp);
- return;
+ /* For I/O accelerator commands, copy over some fields to the normal
+ * CISS header used below for error handling.
+ */
+ if (cp->cmd_type == CMD_IOACCEL1) {
+ struct io_accel1_cmd *c = &h->ioaccel_cmd_pool[cp->cmdindex];
+ cp->Header.SGList = scsi_sg_count(cmd);
+ cp->Header.SGTotal = cpu_to_le16(cp->Header.SGList);
+ cp->Request.CDBLen = le16_to_cpu(c->io_flags) &
+ IOACCEL1_IOFLAGS_CDBLEN_MASK;
+ cp->Header.tag = c->tag;
+ memcpy(cp->Header.LUN.LunAddrBytes, c->CISS_LUN, 8);
+ memcpy(cp->Request.CDB, c->CDB, cp->Request.CDBLen);
+
+ /* Any RAID offload error results in retry which will use
+ * the normal I/O path so the controller can handle whatever's
+ * wrong.
+ */
+ if (is_logical_device(dev)) {
+ if (ei->CommandStatus == CMD_IOACCEL_DISABLED)
+ dev->offload_enabled = 0;
+ return hpsa_retry_cmd(h, cp);
+ }
}
/* an error has occurred */
switch (ei->CommandStatus) {
case CMD_TARGET_STATUS:
- if (ei->ScsiStatus) {
- /* Get sense key */
- sense_key = 0xf & ei->SenseInfo[2];
- /* Get additional sense code */
- asc = ei->SenseInfo[12];
- /* Get addition sense code qualifier */
- ascq = ei->SenseInfo[13];
- }
-
+ cmd->result |= ei->ScsiStatus;
+ /* copy the sense data */
+ sense_data_size = min_t(unsigned long, SCSI_SENSE_BUFFERSIZE,
+ sizeof(ei->SenseInfo));
+ if (ei->SenseLen < sense_data_size)
+ sense_data_size = ei->SenseLen;
+ memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size);
+ if (ei->ScsiStatus)
+ decode_sense_data(ei->SenseInfo, sense_data_size,
+ &sense_key, &asc, &ascq);
if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) {
- if (check_for_unit_attention(h, cp)) {
- cmd->result = DID_SOFT_ERROR << 16;
+ switch (sense_key) {
+ case ABORTED_COMMAND:
+ cmd->result |= DID_SOFT_ERROR << 16;
break;
- }
- if (sense_key == ILLEGAL_REQUEST) {
- /*
- * SCSI REPORT_LUNS is commonly unsupported on
- * Smart Array. Suppress noisy complaint.
- */
- if (cp->Request.CDB[0] == REPORT_LUNS)
- break;
-
- /* If ASC/ASCQ indicate Logical Unit
- * Not Supported condition,
- */
- if ((asc == 0x25) && (ascq == 0x0)) {
- dev_warn(&h->pdev->dev, "cp %p "
- "has check condition\n", cp);
- break;
- }
- }
-
- if (sense_key == NOT_READY) {
- /* If Sense is Not Ready, Logical Unit
- * Not ready, Manual Intervention
- * required
- */
- if ((asc == 0x04) && (ascq == 0x03)) {
- dev_warn(&h->pdev->dev, "cp %p "
- "has check condition: unit "
- "not ready, manual "
- "intervention required\n", cp);
- break;
+ case UNIT_ATTENTION:
+ if (asc == 0x3F && ascq == 0x0E)
+ h->drv_req_rescan = 1;
+ break;
+ case ILLEGAL_REQUEST:
+ if (asc == 0x25 && ascq == 0x00) {
+ dev->removed = 1;
+ cmd->result = DID_NO_CONNECT << 16;
}
- }
- if (sense_key == ABORTED_COMMAND) {
- /* Aborted command is retryable */
- dev_warn(&h->pdev->dev, "cp %p "
- "has check condition: aborted command: "
- "ASC: 0x%x, ASCQ: 0x%x\n",
- cp, asc, ascq);
- cmd->result = DID_SOFT_ERROR << 16;
break;
}
- /* Must be some other type of check condition */
- dev_warn(&h->pdev->dev, "cp %p has check condition: "
- "unknown type: "
- "Sense: 0x%x, ASC: 0x%x, ASCQ: 0x%x, "
- "Returning result: 0x%x, "
- "cmd=[%02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x]\n",
- cp, sense_key, asc, ascq,
- cmd->result,
- cmd->cmnd[0], cmd->cmnd[1],
- cmd->cmnd[2], cmd->cmnd[3],
- cmd->cmnd[4], cmd->cmnd[5],
- cmd->cmnd[6], cmd->cmnd[7],
- cmd->cmnd[8], cmd->cmnd[9],
- cmd->cmnd[10], cmd->cmnd[11],
- cmd->cmnd[12], cmd->cmnd[13],
- cmd->cmnd[14], cmd->cmnd[15]);
break;
}
-
-
/* Problem was not a check condition
* Pass it up to the upper layers...
*/
@@ -1181,9 +2721,8 @@ static void complete_scsi_command(struct CommandList *cp)
case CMD_DATA_UNDERRUN: /* let mid layer handle it. */
break;
case CMD_DATA_OVERRUN:
- dev_warn(&h->pdev->dev, "cp %p has"
- " completed with data overrun "
- "reported\n", cp);
+ dev_warn(&h->pdev->dev,
+ "CDB %16phN data overrun\n", cp->Request.CDB);
break;
case CMD_INVALID: {
/* print_bytes(cp, sizeof(*cp), 1, 0);
@@ -1198,160 +2737,208 @@ static void complete_scsi_command(struct CommandList *cp)
}
break;
case CMD_PROTOCOL_ERR:
- dev_warn(&h->pdev->dev, "cp %p has "
- "protocol error \n", cp);
+ cmd->result = DID_ERROR << 16;
+ dev_warn(&h->pdev->dev, "CDB %16phN : protocol error\n",
+ cp->Request.CDB);
break;
case CMD_HARDWARE_ERR:
cmd->result = DID_ERROR << 16;
- dev_warn(&h->pdev->dev, "cp %p had hardware error\n", cp);
+ dev_warn(&h->pdev->dev, "CDB %16phN : hardware error\n",
+ cp->Request.CDB);
break;
case CMD_CONNECTION_LOST:
cmd->result = DID_ERROR << 16;
- dev_warn(&h->pdev->dev, "cp %p had connection lost\n", cp);
+ dev_warn(&h->pdev->dev, "CDB %16phN : connection lost\n",
+ cp->Request.CDB);
break;
case CMD_ABORTED:
cmd->result = DID_ABORT << 16;
- dev_warn(&h->pdev->dev, "cp %p was aborted with status 0x%x\n",
- cp, ei->ScsiStatus);
break;
case CMD_ABORT_FAILED:
cmd->result = DID_ERROR << 16;
- dev_warn(&h->pdev->dev, "cp %p reports abort failed\n", cp);
+ dev_warn(&h->pdev->dev, "CDB %16phN : abort failed\n",
+ cp->Request.CDB);
break;
case CMD_UNSOLICITED_ABORT:
- cmd->result = DID_RESET << 16;
- dev_warn(&h->pdev->dev, "cp %p aborted do to an unsolicited "
- "abort\n", cp);
+ cmd->result = DID_SOFT_ERROR << 16; /* retry the command */
+ dev_warn(&h->pdev->dev, "CDB %16phN : unsolicited abort\n",
+ cp->Request.CDB);
break;
case CMD_TIMEOUT:
cmd->result = DID_TIME_OUT << 16;
- dev_warn(&h->pdev->dev, "cp %p timedout\n", cp);
+ dev_warn(&h->pdev->dev, "CDB %16phN timed out\n",
+ cp->Request.CDB);
break;
case CMD_UNABORTABLE:
cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "Command unabortable\n");
break;
+ case CMD_TMF_STATUS:
+ if (hpsa_evaluate_tmf_status(h, cp)) /* TMF failed? */
+ cmd->result = DID_ERROR << 16;
+ break;
+ case CMD_IOACCEL_DISABLED:
+ /* This only handles the direct pass-through case since RAID
+ * offload is handled above. Just attempt a retry.
+ */
+ cmd->result = DID_SOFT_ERROR << 16;
+ dev_warn(&h->pdev->dev,
+ "cp %p had HP SSD Smart Path error\n", cp);
+ break;
default:
cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "cp %p returned unknown status %x\n",
cp, ei->CommandStatus);
}
- cmd->scsi_done(cmd);
- cmd_free(h, cp);
-}
-
-static int hpsa_scsi_detect(struct ctlr_info *h)
-{
- struct Scsi_Host *sh;
- int error;
-
- sh = scsi_host_alloc(&hpsa_driver_template, sizeof(h));
- if (sh == NULL)
- goto fail;
- sh->io_port = 0;
- sh->n_io_port = 0;
- sh->this_id = -1;
- sh->max_channel = 3;
- sh->max_cmd_len = MAX_COMMAND_SIZE;
- sh->max_lun = HPSA_MAX_LUN;
- sh->max_id = HPSA_MAX_LUN;
- sh->can_queue = h->nr_cmds;
- sh->cmd_per_lun = h->nr_cmds;
- sh->sg_tablesize = h->maxsgentries;
- h->scsi_host = sh;
- sh->hostdata[0] = (unsigned long) h;
- sh->irq = h->intr[h->intr_mode];
- sh->unique_id = sh->irq;
- error = scsi_add_host(sh, &h->pdev->dev);
- if (error)
- goto fail_host_put;
- scsi_scan_host(sh);
- return 0;
-
- fail_host_put:
- dev_err(&h->pdev->dev, "hpsa_scsi_detect: scsi_add_host"
- " failed for controller %d\n", h->ctlr);
- scsi_host_put(sh);
- return error;
- fail:
- dev_err(&h->pdev->dev, "hpsa_scsi_detect: scsi_host_alloc"
- " failed for controller %d\n", h->ctlr);
- return -ENOMEM;
+ return hpsa_cmd_free_and_done(h, cp, cmd);
}
-static void hpsa_pci_unmap(struct pci_dev *pdev,
- struct CommandList *c, int sg_used, int data_direction)
+static void hpsa_pci_unmap(struct pci_dev *pdev, struct CommandList *c,
+ int sg_used, enum dma_data_direction data_direction)
{
int i;
- union u64bit addr64;
- for (i = 0; i < sg_used; i++) {
- addr64.val32.lower = c->SG[i].Addr.lower;
- addr64.val32.upper = c->SG[i].Addr.upper;
- pci_unmap_single(pdev, (dma_addr_t) addr64.val, c->SG[i].Len,
- data_direction);
- }
+ for (i = 0; i < sg_used; i++)
+ dma_unmap_single(&pdev->dev, le64_to_cpu(c->SG[i].Addr),
+ le32_to_cpu(c->SG[i].Len),
+ data_direction);
}
-static void hpsa_map_one(struct pci_dev *pdev,
+static int hpsa_map_one(struct pci_dev *pdev,
struct CommandList *cp,
unsigned char *buf,
size_t buflen,
- int data_direction)
+ enum dma_data_direction data_direction)
{
u64 addr64;
- if (buflen == 0 || data_direction == PCI_DMA_NONE) {
+ if (buflen == 0 || data_direction == DMA_NONE) {
cp->Header.SGList = 0;
- cp->Header.SGTotal = 0;
- return;
+ cp->Header.SGTotal = cpu_to_le16(0);
+ return 0;
}
- addr64 = (u64) pci_map_single(pdev, buf, buflen, data_direction);
- cp->SG[0].Addr.lower =
- (u32) (addr64 & (u64) 0x00000000FFFFFFFF);
- cp->SG[0].Addr.upper =
- (u32) ((addr64 >> 32) & (u64) 0x00000000FFFFFFFF);
- cp->SG[0].Len = buflen;
- cp->Header.SGList = (u8) 1; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (u16) 1; /* total sgs in this cmd list */
+ addr64 = dma_map_single(&pdev->dev, buf, buflen, data_direction);
+ if (dma_mapping_error(&pdev->dev, addr64)) {
+ /* Prevent subsequent unmap of something never mapped */
+ cp->Header.SGList = 0;
+ cp->Header.SGTotal = cpu_to_le16(0);
+ return -1;
+ }
+ cp->SG[0].Addr = cpu_to_le64(addr64);
+ cp->SG[0].Len = cpu_to_le32(buflen);
+ cp->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* we are not chaining */
+ cp->Header.SGList = 1; /* no. SGs contig in this cmd */
+ cp->Header.SGTotal = cpu_to_le16(1); /* total sgs in cmd list */
+ return 0;
}
-static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
- struct CommandList *c)
+#define NO_TIMEOUT ((unsigned long) -1)
+#define DEFAULT_TIMEOUT 30000 /* milliseconds */
+static int hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
+ struct CommandList *c, int reply_queue, unsigned long timeout_msecs)
{
DECLARE_COMPLETION_ONSTACK(wait);
c->waiting = &wait;
- enqueue_cmd_and_start_io(h, c);
- wait_for_completion(&wait);
+ __enqueue_cmd_and_start_io(h, c, reply_queue);
+ if (timeout_msecs == NO_TIMEOUT) {
+ /* TODO: get rid of this no-timeout thing */
+ wait_for_completion_io(&wait);
+ return IO_OK;
+ }
+ if (!wait_for_completion_io_timeout(&wait,
+ msecs_to_jiffies(timeout_msecs))) {
+ dev_warn(&h->pdev->dev, "Command timed out.\n");
+ return -ETIMEDOUT;
+ }
+ return IO_OK;
}
-static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
- struct CommandList *c, int data_direction)
+static int hpsa_scsi_do_simple_cmd(struct ctlr_info *h, struct CommandList *c,
+ int reply_queue, unsigned long timeout_msecs)
{
- int retry_count = 0;
+ if (unlikely(lockup_detected(h))) {
+ c->err_info->CommandStatus = CMD_CTLR_LOCKUP;
+ return IO_OK;
+ }
+ return hpsa_scsi_do_simple_cmd_core(h, c, reply_queue, timeout_msecs);
+}
+
+static u32 lockup_detected(struct ctlr_info *h)
+{
+ int cpu;
+ u32 rc, *lockup_detected;
+
+ cpu = get_cpu();
+ lockup_detected = per_cpu_ptr(h->lockup_detected, cpu);
+ rc = *lockup_detected;
+ put_cpu();
+ return rc;
+}
+
+#define MAX_DRIVER_CMD_RETRIES 25
+static int hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
+ struct CommandList *c, enum dma_data_direction data_direction,
+ unsigned long timeout_msecs)
+{
+ int backoff_time = 10, retry_count = 0;
+ int rc;
do {
memset(c->err_info, 0, sizeof(*c->err_info));
- hpsa_scsi_do_simple_cmd_core(h, c);
+ rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE,
+ timeout_msecs);
+ if (rc)
+ break;
retry_count++;
- } while (check_for_unit_attention(h, c) && retry_count <= 3);
+ if (retry_count > 3) {
+ msleep(backoff_time);
+ if (backoff_time < 1000)
+ backoff_time *= 2;
+ }
+ } while ((check_for_unit_attention(h, c) ||
+ check_for_busy(h, c)) &&
+ retry_count <= MAX_DRIVER_CMD_RETRIES);
hpsa_pci_unmap(h->pdev, c, 1, data_direction);
+ if (retry_count > MAX_DRIVER_CMD_RETRIES)
+ rc = -EIO;
+ return rc;
}
-static void hpsa_scsi_interpret_error(struct CommandList *cp)
+static void hpsa_print_cmd(struct ctlr_info *h, char *txt,
+ struct CommandList *c)
{
- struct ErrorInfo *ei;
+ const u8 *cdb = c->Request.CDB;
+ const u8 *lun = c->Header.LUN.LunAddrBytes;
+
+ dev_warn(&h->pdev->dev, "%s: LUN:%8phN CDB:%16phN\n",
+ txt, lun, cdb);
+}
+
+static void hpsa_scsi_interpret_error(struct ctlr_info *h,
+ struct CommandList *cp)
+{
+ const struct ErrorInfo *ei = cp->err_info;
struct device *d = &cp->h->pdev->dev;
+ u8 sense_key, asc, ascq;
+ int sense_len;
- ei = cp->err_info;
switch (ei->CommandStatus) {
case CMD_TARGET_STATUS:
- dev_warn(d, "cmd %p has completed with errors\n", cp);
- dev_warn(d, "cmd %p has SCSI Status = %x\n", cp,
- ei->ScsiStatus);
+ if (ei->SenseLen > sizeof(ei->SenseInfo))
+ sense_len = sizeof(ei->SenseInfo);
+ else
+ sense_len = ei->SenseLen;
+ decode_sense_data(ei->SenseInfo, sense_len,
+ &sense_key, &asc, &ascq);
+ hpsa_print_cmd(h, "SCSI status", cp);
+ if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION)
+ dev_warn(d, "SCSI Status = 02, Sense key = 0x%02x, ASC = 0x%02x, ASCQ = 0x%02x\n",
+ sense_key, asc, ascq);
+ else
+ dev_warn(d, "SCSI Status = 0x%02x\n", ei->ScsiStatus);
if (ei->ScsiStatus == 0)
dev_warn(d, "SCSI status is abnormally zero. "
"(probably indicates selection timeout "
@@ -1359,101 +2946,250 @@ static void hpsa_scsi_interpret_error(struct CommandList *cp)
"firmware bug, circa July, 2001.)\n");
break;
case CMD_DATA_UNDERRUN: /* let mid layer handle it. */
- dev_info(d, "UNDERRUN\n");
break;
case CMD_DATA_OVERRUN:
- dev_warn(d, "cp %p has completed with data overrun\n", cp);
+ hpsa_print_cmd(h, "overrun condition", cp);
break;
case CMD_INVALID: {
/* controller unfortunately reports SCSI passthru's
* to non-existent targets as invalid commands.
*/
- dev_warn(d, "cp %p is reported invalid (probably means "
- "target device no longer present)\n", cp);
- /* print_bytes((unsigned char *) cp, sizeof(*cp), 1, 0);
- print_cmd(cp); */
+ hpsa_print_cmd(h, "invalid command", cp);
+ dev_warn(d, "probably means device no longer present\n");
}
break;
case CMD_PROTOCOL_ERR:
- dev_warn(d, "cp %p has protocol error \n", cp);
+ hpsa_print_cmd(h, "protocol error", cp);
break;
case CMD_HARDWARE_ERR:
- /* cmd->result = DID_ERROR << 16; */
- dev_warn(d, "cp %p had hardware error\n", cp);
+ hpsa_print_cmd(h, "hardware error", cp);
break;
case CMD_CONNECTION_LOST:
- dev_warn(d, "cp %p had connection lost\n", cp);
+ hpsa_print_cmd(h, "connection lost", cp);
break;
case CMD_ABORTED:
- dev_warn(d, "cp %p was aborted\n", cp);
+ hpsa_print_cmd(h, "aborted", cp);
break;
case CMD_ABORT_FAILED:
- dev_warn(d, "cp %p reports abort failed\n", cp);
+ hpsa_print_cmd(h, "abort failed", cp);
break;
case CMD_UNSOLICITED_ABORT:
- dev_warn(d, "cp %p aborted due to an unsolicited abort\n", cp);
+ hpsa_print_cmd(h, "unsolicited abort", cp);
break;
case CMD_TIMEOUT:
- dev_warn(d, "cp %p timed out\n", cp);
+ hpsa_print_cmd(h, "timed out", cp);
break;
case CMD_UNABORTABLE:
- dev_warn(d, "Command unabortable\n");
+ hpsa_print_cmd(h, "unabortable", cp);
+ break;
+ case CMD_CTLR_LOCKUP:
+ hpsa_print_cmd(h, "controller lockup detected", cp);
break;
default:
- dev_warn(d, "cp %p returned unknown status %x\n", cp,
+ hpsa_print_cmd(h, "unknown status", cp);
+ dev_warn(d, "Unknown command status %x\n",
ei->CommandStatus);
}
}
+static int hpsa_do_receive_diagnostic(struct ctlr_info *h, u8 *scsi3addr,
+ u8 page, u8 *buf, size_t bufsize)
+{
+ int rc = IO_OK;
+ struct CommandList *c;
+ struct ErrorInfo *ei;
+
+ c = cmd_alloc(h);
+ if (fill_cmd(c, RECEIVE_DIAGNOSTIC, h, buf, bufsize,
+ page, scsi3addr, TYPE_CMD)) {
+ rc = -1;
+ goto out;
+ }
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if (rc)
+ goto out;
+ ei = c->err_info;
+ if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
+ hpsa_scsi_interpret_error(h, c);
+ rc = -1;
+ }
+out:
+ cmd_free(h, c);
+ return rc;
+}
+
+static u64 hpsa_get_enclosure_logical_identifier(struct ctlr_info *h,
+ u8 *scsi3addr)
+{
+ u8 *buf;
+ u64 sa = 0;
+ int rc = 0;
+
+ buf = kzalloc(1024, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ rc = hpsa_do_receive_diagnostic(h, scsi3addr, RECEIVE_DIAGNOSTIC,
+ buf, 1024);
+
+ if (rc)
+ goto out;
+
+ sa = get_unaligned_be64(buf+12);
+
+out:
+ kfree(buf);
+ return sa;
+}
+
static int hpsa_scsi_do_inquiry(struct ctlr_info *h, unsigned char *scsi3addr,
- unsigned char page, unsigned char *buf,
+ u16 page, unsigned char *buf,
unsigned char bufsize)
{
int rc = IO_OK;
struct CommandList *c;
struct ErrorInfo *ei;
- c = cmd_special_alloc(h);
+ c = cmd_alloc(h);
- if (c == NULL) { /* trouble... */
- dev_warn(&h->pdev->dev, "cmd_special_alloc returned NULL!\n");
- return -ENOMEM;
+ if (fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize,
+ page, scsi3addr, TYPE_CMD)) {
+ rc = -1;
+ goto out;
}
-
- fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize, page, scsi3addr, TYPE_CMD);
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if (rc)
+ goto out;
ei = c->err_info;
if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
- hpsa_scsi_interpret_error(c);
+ hpsa_scsi_interpret_error(h, c);
rc = -1;
}
- cmd_special_free(h, c);
+out:
+ cmd_free(h, c);
return rc;
}
-static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr)
+static int hpsa_send_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev,
+ u8 reset_type, int reply_queue)
{
int rc = IO_OK;
struct CommandList *c;
struct ErrorInfo *ei;
- c = cmd_special_alloc(h);
+ c = cmd_alloc(h);
+ c->device = dev;
- if (c == NULL) { /* trouble... */
- dev_warn(&h->pdev->dev, "cmd_special_alloc returned NULL!\n");
- return -ENOMEM;
+ /* fill_cmd can't fail here, no data buffer to map. */
+ (void) fill_cmd(c, reset_type, h, NULL, 0, 0, dev->scsi3addr, TYPE_MSG);
+ rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+ if (rc) {
+ dev_warn(&h->pdev->dev, "Failed to send reset command\n");
+ goto out;
}
-
- fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0, scsi3addr, TYPE_MSG);
- hpsa_scsi_do_simple_cmd_core(h, c);
/* no unmap needed here because no data xfer. */
ei = c->err_info;
if (ei->CommandStatus != 0) {
- hpsa_scsi_interpret_error(c);
+ hpsa_scsi_interpret_error(h, c);
rc = -1;
}
- cmd_special_free(h, c);
+out:
+ cmd_free(h, c);
+ return rc;
+}
+
+static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c,
+ struct hpsa_scsi_dev_t *dev,
+ unsigned char *scsi3addr)
+{
+ int i;
+ bool match = false;
+ struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+ struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2;
+
+ if (hpsa_is_cmd_idle(c))
+ return false;
+
+ switch (c->cmd_type) {
+ case CMD_SCSI:
+ case CMD_IOCTL_PEND:
+ match = !memcmp(scsi3addr, &c->Header.LUN.LunAddrBytes,
+ sizeof(c->Header.LUN.LunAddrBytes));
+ break;
+
+ case CMD_IOACCEL1:
+ case CMD_IOACCEL2:
+ if (c->phys_disk == dev) {
+ /* HBA mode match */
+ match = true;
+ } else {
+ /* Possible RAID mode -- check each phys dev. */
+ /* FIXME: Do we need to take out a lock here? If
+ * so, we could just call hpsa_get_pdisk_of_ioaccel2()
+ * instead. */
+ for (i = 0; i < dev->nphysical_disks && !match; i++) {
+ /* FIXME: an alternate test might be
+ *
+ * match = dev->phys_disk[i]->ioaccel_handle
+ * == c2->scsi_nexus; */
+ match = dev->phys_disk[i] == c->phys_disk;
+ }
+ }
+ break;
+
+ case IOACCEL2_TMF:
+ for (i = 0; i < dev->nphysical_disks && !match; i++) {
+ match = dev->phys_disk[i]->ioaccel_handle ==
+ le32_to_cpu(ac->it_nexus);
+ }
+ break;
+
+ case 0: /* The command is in the middle of being initialized. */
+ match = false;
+ break;
+
+ default:
+ dev_err(&h->pdev->dev, "unexpected cmd_type: %d\n",
+ c->cmd_type);
+ BUG();
+ }
+
+ return match;
+}
+
+static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev,
+ u8 reset_type, int reply_queue)
+{
+ int rc = 0;
+
+ /* We can really only handle one reset at a time */
+ if (mutex_lock_interruptible(&h->reset_mutex) == -EINTR) {
+ dev_warn(&h->pdev->dev, "concurrent reset wait interrupted.\n");
+ return -EINTR;
+ }
+
+ rc = hpsa_send_reset(h, dev, reset_type, reply_queue);
+ if (!rc) {
+ /* incremented by sending the reset request */
+ atomic_dec(&dev->commands_outstanding);
+ wait_event(h->event_sync_wait_queue,
+ atomic_read(&dev->commands_outstanding) <= 0 ||
+ lockup_detected(h));
+ }
+
+ if (unlikely(lockup_detected(h))) {
+ dev_warn(&h->pdev->dev,
+ "Controller lockup detected during reset wait\n");
+ rc = -ENODEV;
+ }
+
+ if (!rc)
+ rc = wait_for_device_to_become_ready(h, dev->scsi3addr, 0);
+
+ mutex_unlock(&h->reset_mutex);
return rc;
}
@@ -1467,36 +3203,533 @@ static void hpsa_get_raid_level(struct ctlr_info *h,
buf = kzalloc(64, GFP_KERNEL);
if (!buf)
return;
- rc = hpsa_scsi_do_inquiry(h, scsi3addr, 0xC1, buf, 64);
+
+ if (!hpsa_vpd_page_supported(h, scsi3addr,
+ HPSA_VPD_LV_DEVICE_GEOMETRY))
+ goto exit;
+
+ rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE |
+ HPSA_VPD_LV_DEVICE_GEOMETRY, buf, 64);
+
if (rc == 0)
*raid_level = buf[8];
if (*raid_level > RAID_UNKNOWN)
*raid_level = RAID_UNKNOWN;
+exit:
+ kfree(buf);
+ return;
+}
+
+#define HPSA_MAP_DEBUG
+#ifdef HPSA_MAP_DEBUG
+static void hpsa_debug_map_buff(struct ctlr_info *h, int rc,
+ struct raid_map_data *map_buff)
+{
+ struct raid_map_disk_data *dd = &map_buff->data[0];
+ int map, row, col;
+ u16 map_cnt, row_cnt, disks_per_row;
+
+ if (rc != 0)
+ return;
+
+ /* Show details only if debugging has been activated. */
+ if (h->raid_offload_debug < 2)
+ return;
+
+ dev_info(&h->pdev->dev, "structure_size = %u\n",
+ le32_to_cpu(map_buff->structure_size));
+ dev_info(&h->pdev->dev, "volume_blk_size = %u\n",
+ le32_to_cpu(map_buff->volume_blk_size));
+ dev_info(&h->pdev->dev, "volume_blk_cnt = 0x%llx\n",
+ le64_to_cpu(map_buff->volume_blk_cnt));
+ dev_info(&h->pdev->dev, "physicalBlockShift = %u\n",
+ map_buff->phys_blk_shift);
+ dev_info(&h->pdev->dev, "parity_rotation_shift = %u\n",
+ map_buff->parity_rotation_shift);
+ dev_info(&h->pdev->dev, "strip_size = %u\n",
+ le16_to_cpu(map_buff->strip_size));
+ dev_info(&h->pdev->dev, "disk_starting_blk = 0x%llx\n",
+ le64_to_cpu(map_buff->disk_starting_blk));
+ dev_info(&h->pdev->dev, "disk_blk_cnt = 0x%llx\n",
+ le64_to_cpu(map_buff->disk_blk_cnt));
+ dev_info(&h->pdev->dev, "data_disks_per_row = %u\n",
+ le16_to_cpu(map_buff->data_disks_per_row));
+ dev_info(&h->pdev->dev, "metadata_disks_per_row = %u\n",
+ le16_to_cpu(map_buff->metadata_disks_per_row));
+ dev_info(&h->pdev->dev, "row_cnt = %u\n",
+ le16_to_cpu(map_buff->row_cnt));
+ dev_info(&h->pdev->dev, "layout_map_count = %u\n",
+ le16_to_cpu(map_buff->layout_map_count));
+ dev_info(&h->pdev->dev, "flags = 0x%x\n",
+ le16_to_cpu(map_buff->flags));
+ dev_info(&h->pdev->dev, "encryption = %s\n",
+ le16_to_cpu(map_buff->flags) &
+ RAID_MAP_FLAG_ENCRYPT_ON ? "ON" : "OFF");
+ dev_info(&h->pdev->dev, "dekindex = %u\n",
+ le16_to_cpu(map_buff->dekindex));
+ map_cnt = le16_to_cpu(map_buff->layout_map_count);
+ for (map = 0; map < map_cnt; map++) {
+ dev_info(&h->pdev->dev, "Map%u:\n", map);
+ row_cnt = le16_to_cpu(map_buff->row_cnt);
+ for (row = 0; row < row_cnt; row++) {
+ dev_info(&h->pdev->dev, " Row%u:\n", row);
+ disks_per_row =
+ le16_to_cpu(map_buff->data_disks_per_row);
+ for (col = 0; col < disks_per_row; col++, dd++)
+ dev_info(&h->pdev->dev,
+ " D%02u: h=0x%04x xor=%u,%u\n",
+ col, dd->ioaccel_handle,
+ dd->xor_mult[0], dd->xor_mult[1]);
+ disks_per_row =
+ le16_to_cpu(map_buff->metadata_disks_per_row);
+ for (col = 0; col < disks_per_row; col++, dd++)
+ dev_info(&h->pdev->dev,
+ " M%02u: h=0x%04x xor=%u,%u\n",
+ col, dd->ioaccel_handle,
+ dd->xor_mult[0], dd->xor_mult[1]);
+ }
+ }
+}
+#else
+static void hpsa_debug_map_buff(__attribute__((unused)) struct ctlr_info *h,
+ __attribute__((unused)) int rc,
+ __attribute__((unused)) struct raid_map_data *map_buff)
+{
+}
+#endif
+
+static int hpsa_get_raid_map(struct ctlr_info *h,
+ unsigned char *scsi3addr, struct hpsa_scsi_dev_t *this_device)
+{
+ int rc = 0;
+ struct CommandList *c;
+ struct ErrorInfo *ei;
+
+ c = cmd_alloc(h);
+
+ if (fill_cmd(c, HPSA_GET_RAID_MAP, h, &this_device->raid_map,
+ sizeof(this_device->raid_map), 0,
+ scsi3addr, TYPE_CMD)) {
+ dev_warn(&h->pdev->dev, "hpsa_get_raid_map fill_cmd failed\n");
+ cmd_free(h, c);
+ return -1;
+ }
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if (rc)
+ goto out;
+ ei = c->err_info;
+ if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
+ hpsa_scsi_interpret_error(h, c);
+ rc = -1;
+ goto out;
+ }
+ cmd_free(h, c);
+
+ /* @todo in the future, dynamically allocate RAID map memory */
+ if (le32_to_cpu(this_device->raid_map.structure_size) >
+ sizeof(this_device->raid_map)) {
+ dev_warn(&h->pdev->dev, "RAID map size is too large!\n");
+ rc = -1;
+ }
+ hpsa_debug_map_buff(h, rc, &this_device->raid_map);
+ return rc;
+out:
+ cmd_free(h, c);
+ return rc;
+}
+
+static int hpsa_bmic_sense_subsystem_information(struct ctlr_info *h,
+ unsigned char scsi3addr[], u16 bmic_device_index,
+ struct bmic_sense_subsystem_info *buf, size_t bufsize)
+{
+ int rc = IO_OK;
+ struct CommandList *c;
+ struct ErrorInfo *ei;
+
+ c = cmd_alloc(h);
+
+ rc = fill_cmd(c, BMIC_SENSE_SUBSYSTEM_INFORMATION, h, buf, bufsize,
+ 0, RAID_CTLR_LUNID, TYPE_CMD);
+ if (rc)
+ goto out;
+
+ c->Request.CDB[2] = bmic_device_index & 0xff;
+ c->Request.CDB[9] = (bmic_device_index >> 8) & 0xff;
+
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if (rc)
+ goto out;
+ ei = c->err_info;
+ if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
+ hpsa_scsi_interpret_error(h, c);
+ rc = -1;
+ }
+out:
+ cmd_free(h, c);
+ return rc;
+}
+
+static int hpsa_bmic_id_controller(struct ctlr_info *h,
+ struct bmic_identify_controller *buf, size_t bufsize)
+{
+ int rc = IO_OK;
+ struct CommandList *c;
+ struct ErrorInfo *ei;
+
+ c = cmd_alloc(h);
+
+ rc = fill_cmd(c, BMIC_IDENTIFY_CONTROLLER, h, buf, bufsize,
+ 0, RAID_CTLR_LUNID, TYPE_CMD);
+ if (rc)
+ goto out;
+
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if (rc)
+ goto out;
+ ei = c->err_info;
+ if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
+ hpsa_scsi_interpret_error(h, c);
+ rc = -1;
+ }
+out:
+ cmd_free(h, c);
+ return rc;
+}
+
+static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
+ unsigned char scsi3addr[], u16 bmic_device_index,
+ struct bmic_identify_physical_device *buf, size_t bufsize)
+{
+ int rc = IO_OK;
+ struct CommandList *c;
+ struct ErrorInfo *ei;
+
+ c = cmd_alloc(h);
+ rc = fill_cmd(c, BMIC_IDENTIFY_PHYSICAL_DEVICE, h, buf, bufsize,
+ 0, RAID_CTLR_LUNID, TYPE_CMD);
+ if (rc)
+ goto out;
+
+ c->Request.CDB[2] = bmic_device_index & 0xff;
+ c->Request.CDB[9] = (bmic_device_index >> 8) & 0xff;
+
+ hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ ei = c->err_info;
+ if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
+ hpsa_scsi_interpret_error(h, c);
+ rc = -1;
+ }
+out:
+ cmd_free(h, c);
+
+ return rc;
+}
+
+/*
+ * get enclosure information
+ * struct ReportExtendedLUNdata *rlep - Used for BMIC drive number
+ * struct hpsa_scsi_dev_t *encl_dev - device entry for enclosure
+ * Uses id_physical_device to determine the box_index.
+ */
+static void hpsa_get_enclosure_info(struct ctlr_info *h,
+ unsigned char *scsi3addr,
+ struct ReportExtendedLUNdata *rlep, int rle_index,
+ struct hpsa_scsi_dev_t *encl_dev)
+{
+ int rc = -1;
+ struct CommandList *c = NULL;
+ struct ErrorInfo *ei = NULL;
+ struct bmic_sense_storage_box_params *bssbp = NULL;
+ struct bmic_identify_physical_device *id_phys = NULL;
+ struct ext_report_lun_entry *rle;
+ u16 bmic_device_index = 0;
+
+ if (rle_index < 0 || rle_index >= HPSA_MAX_PHYS_LUN)
+ return;
+
+ rle = &rlep->LUN[rle_index];
+
+ encl_dev->eli =
+ hpsa_get_enclosure_logical_identifier(h, scsi3addr);
+
+ bmic_device_index = GET_BMIC_DRIVE_NUMBER(&rle->lunid[0]);
+
+ if (encl_dev->target == -1 || encl_dev->lun == -1) {
+ rc = IO_OK;
+ goto out;
+ }
+
+ if (bmic_device_index == 0xFF00 || MASKED_DEVICE(&rle->lunid[0])) {
+ rc = IO_OK;
+ goto out;
+ }
+
+ bssbp = kzalloc(sizeof(*bssbp), GFP_KERNEL);
+ if (!bssbp)
+ goto out;
+
+ id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL);
+ if (!id_phys)
+ goto out;
+
+ rc = hpsa_bmic_id_physical_device(h, scsi3addr, bmic_device_index,
+ id_phys, sizeof(*id_phys));
+ if (rc) {
+ dev_warn(&h->pdev->dev, "%s: id_phys failed %d bdi[0x%x]\n",
+ __func__, encl_dev->external, bmic_device_index);
+ goto out;
+ }
+
+ c = cmd_alloc(h);
+
+ rc = fill_cmd(c, BMIC_SENSE_STORAGE_BOX_PARAMS, h, bssbp,
+ sizeof(*bssbp), 0, RAID_CTLR_LUNID, TYPE_CMD);
+
+ if (rc)
+ goto out;
+
+ if (id_phys->phys_connector[1] == 'E')
+ c->Request.CDB[5] = id_phys->box_index;
+ else
+ c->Request.CDB[5] = 0;
+
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if (rc)
+ goto out;
+
+ ei = c->err_info;
+ if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
+ rc = -1;
+ goto out;
+ }
+
+ encl_dev->box[id_phys->active_path_number] = bssbp->phys_box_on_port;
+ memcpy(&encl_dev->phys_connector[id_phys->active_path_number],
+ bssbp->phys_connector, sizeof(bssbp->phys_connector));
+
+ rc = IO_OK;
+out:
+ kfree(bssbp);
+ kfree(id_phys);
+
+ if (c)
+ cmd_free(h, c);
+
+ if (rc != IO_OK)
+ hpsa_show_dev_msg(KERN_INFO, h, encl_dev,
+ "Error, could not get enclosure information");
+}
+
+static u64 hpsa_get_sas_address_from_report_physical(struct ctlr_info *h,
+ unsigned char *scsi3addr)
+{
+ struct ReportExtendedLUNdata *physdev;
+ u32 nphysicals;
+ u64 sa = 0;
+ int i;
+
+ physdev = kzalloc(sizeof(*physdev), GFP_KERNEL);
+ if (!physdev)
+ return 0;
+
+ if (hpsa_scsi_do_report_phys_luns(h, physdev, sizeof(*physdev))) {
+ dev_err(&h->pdev->dev, "report physical LUNs failed.\n");
+ kfree(physdev);
+ return 0;
+ }
+ nphysicals = get_unaligned_be32(physdev->LUNListLength) / 24;
+
+ for (i = 0; i < nphysicals; i++)
+ if (!memcmp(&physdev->LUN[i].lunid[0], scsi3addr, 8)) {
+ sa = get_unaligned_be64(&physdev->LUN[i].wwid[0]);
+ break;
+ }
+
+ kfree(physdev);
+
+ return sa;
+}
+
+static void hpsa_get_sas_address(struct ctlr_info *h, unsigned char *scsi3addr,
+ struct hpsa_scsi_dev_t *dev)
+{
+ int rc;
+ u64 sa = 0;
+
+ if (is_hba_lunid(scsi3addr)) {
+ struct bmic_sense_subsystem_info *ssi;
+
+ ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
+ if (!ssi)
+ return;
+
+ rc = hpsa_bmic_sense_subsystem_information(h,
+ scsi3addr, 0, ssi, sizeof(*ssi));
+ if (rc == 0) {
+ sa = get_unaligned_be64(ssi->primary_world_wide_id);
+ h->sas_address = sa;
+ }
+
+ kfree(ssi);
+ } else
+ sa = hpsa_get_sas_address_from_report_physical(h, scsi3addr);
+
+ dev->sas_address = sa;
+}
+
+static void hpsa_ext_ctrl_present(struct ctlr_info *h,
+ struct ReportExtendedLUNdata *physdev)
+{
+ u32 nphysicals;
+ int i;
+
+ if (h->discovery_polling)
+ return;
+
+ nphysicals = (get_unaligned_be32(physdev->LUNListLength) / 24) + 1;
+
+ for (i = 0; i < nphysicals; i++) {
+ if (physdev->LUN[i].device_type ==
+ BMIC_DEVICE_TYPE_CONTROLLER
+ && !is_hba_lunid(physdev->LUN[i].lunid)) {
+ dev_info(&h->pdev->dev,
+ "External controller present, activate discovery polling and disable rld caching\n");
+ hpsa_disable_rld_caching(h);
+ h->discovery_polling = 1;
+ break;
+ }
+ }
+}
+
+/* Get a device id from inquiry page 0x83 */
+static bool hpsa_vpd_page_supported(struct ctlr_info *h,
+ unsigned char scsi3addr[], u8 page)
+{
+ int rc;
+ int i;
+ int pages;
+ unsigned char *buf, bufsize;
+
+ buf = kzalloc(256, GFP_KERNEL);
+ if (!buf)
+ return false;
+
+ /* Get the size of the page list first */
+ rc = hpsa_scsi_do_inquiry(h, scsi3addr,
+ VPD_PAGE | HPSA_VPD_SUPPORTED_PAGES,
+ buf, HPSA_VPD_HEADER_SZ);
+ if (rc != 0)
+ goto exit_unsupported;
+ pages = buf[3];
+ bufsize = min(pages + HPSA_VPD_HEADER_SZ, 255);
+
+ /* Get the whole VPD page list */
+ rc = hpsa_scsi_do_inquiry(h, scsi3addr,
+ VPD_PAGE | HPSA_VPD_SUPPORTED_PAGES,
+ buf, bufsize);
+ if (rc != 0)
+ goto exit_unsupported;
+
+ pages = buf[3];
+ for (i = 1; i <= pages; i++)
+ if (buf[3 + i] == page)
+ goto exit_supported;
+exit_unsupported:
+ kfree(buf);
+ return false;
+exit_supported:
+ kfree(buf);
+ return true;
+}
+
+/*
+ * Called during a scan operation.
+ * Sets ioaccel status on the new device list, not the existing device list
+ *
+ * The device list used during I/O will be updated later in
+ * adjust_hpsa_scsi_table.
+ */
+static void hpsa_get_ioaccel_status(struct ctlr_info *h,
+ unsigned char *scsi3addr, struct hpsa_scsi_dev_t *this_device)
+{
+ int rc;
+ unsigned char *buf;
+ u8 ioaccel_status;
+
+ this_device->offload_config = 0;
+ this_device->offload_enabled = 0;
+ this_device->offload_to_be_enabled = 0;
+
+ buf = kzalloc(64, GFP_KERNEL);
+ if (!buf)
+ return;
+ if (!hpsa_vpd_page_supported(h, scsi3addr, HPSA_VPD_LV_IOACCEL_STATUS))
+ goto out;
+ rc = hpsa_scsi_do_inquiry(h, scsi3addr,
+ VPD_PAGE | HPSA_VPD_LV_IOACCEL_STATUS, buf, 64);
+ if (rc != 0)
+ goto out;
+
+#define IOACCEL_STATUS_BYTE 4
+#define OFFLOAD_CONFIGURED_BIT 0x01
+#define OFFLOAD_ENABLED_BIT 0x02
+ ioaccel_status = buf[IOACCEL_STATUS_BYTE];
+ this_device->offload_config =
+ !!(ioaccel_status & OFFLOAD_CONFIGURED_BIT);
+ if (this_device->offload_config) {
+ bool offload_enabled =
+ !!(ioaccel_status & OFFLOAD_ENABLED_BIT);
+ /*
+ * Check to see if offload can be enabled.
+ */
+ if (offload_enabled) {
+ rc = hpsa_get_raid_map(h, scsi3addr, this_device);
+ if (rc) /* could not load raid_map */
+ goto out;
+ this_device->offload_to_be_enabled = 1;
+ }
+ }
+
+out:
kfree(buf);
return;
}
/* Get the device id from inquiry page 0x83 */
static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr,
- unsigned char *device_id, int buflen)
+ unsigned char *device_id, int index, int buflen)
{
int rc;
unsigned char *buf;
- if (buflen > 16)
- buflen = 16;
+ /* Does controller have VPD for device id? */
+ if (!hpsa_vpd_page_supported(h, scsi3addr, HPSA_VPD_LV_DEVICE_ID))
+ return 1; /* not supported */
+
buf = kzalloc(64, GFP_KERNEL);
if (!buf)
- return -1;
- rc = hpsa_scsi_do_inquiry(h, scsi3addr, 0x83, buf, 64);
- if (rc == 0)
+ return -ENOMEM;
+
+ rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE |
+ HPSA_VPD_LV_DEVICE_ID, buf, 64);
+ if (rc == 0) {
+ if (buflen > 16)
+ buflen = 16;
memcpy(device_id, &buf[8], buflen);
+ }
+
kfree(buf);
- return rc != 0;
+
+ return rc; /*0 - got id, otherwise, didn't */
}
static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical,
- struct ReportLUNdata *buf, int bufsize,
+ void *buf, int bufsize,
int extended_response)
{
int rc = IO_OK;
@@ -1504,33 +3737,74 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical,
unsigned char scsi3addr[8];
struct ErrorInfo *ei;
- c = cmd_special_alloc(h);
- if (c == NULL) { /* trouble... */
- dev_err(&h->pdev->dev, "cmd_special_alloc returned NULL!\n");
- return -1;
- }
+ c = cmd_alloc(h);
+
/* address the controller */
memset(scsi3addr, 0, sizeof(scsi3addr));
- fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h,
- buf, bufsize, 0, scsi3addr, TYPE_CMD);
+ if (fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h,
+ buf, bufsize, 0, scsi3addr, TYPE_CMD)) {
+ rc = -EAGAIN;
+ goto out;
+ }
if (extended_response)
c->Request.CDB[1] = extended_response;
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if (rc)
+ goto out;
ei = c->err_info;
if (ei->CommandStatus != 0 &&
ei->CommandStatus != CMD_DATA_UNDERRUN) {
- hpsa_scsi_interpret_error(c);
- rc = -1;
+ hpsa_scsi_interpret_error(h, c);
+ rc = -EIO;
+ } else {
+ struct ReportLUNdata *rld = buf;
+
+ if (rld->extended_response_flag != extended_response) {
+ if (!h->legacy_board) {
+ dev_err(&h->pdev->dev,
+ "report luns requested format %u, got %u\n",
+ extended_response,
+ rld->extended_response_flag);
+ rc = -EINVAL;
+ } else
+ rc = -EOPNOTSUPP;
+ }
}
- cmd_special_free(h, c);
+out:
+ cmd_free(h, c);
return rc;
}
static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h,
- struct ReportLUNdata *buf,
- int bufsize, int extended_response)
+ struct ReportExtendedLUNdata *buf, int bufsize)
{
- return hpsa_scsi_do_report_luns(h, 0, buf, bufsize, extended_response);
+ int rc;
+ struct ReportLUNdata *lbuf;
+
+ rc = hpsa_scsi_do_report_luns(h, 0, buf, bufsize,
+ HPSA_REPORT_PHYS_EXTENDED);
+ if (!rc || rc != -EOPNOTSUPP)
+ return rc;
+
+ /* REPORT PHYS EXTENDED is not supported */
+ lbuf = kzalloc(sizeof(*lbuf), GFP_KERNEL);
+ if (!lbuf)
+ return -ENOMEM;
+
+ rc = hpsa_scsi_do_report_luns(h, 0, lbuf, sizeof(*lbuf), 0);
+ if (!rc) {
+ int i;
+ u32 nphys;
+
+ /* Copy ReportLUNdata header */
+ memcpy(buf, lbuf, 8);
+ nphys = be32_to_cpu(*((__be32 *)lbuf->LUNListLength)) / 8;
+ for (i = 0; i < nphys; i++)
+ memcpy(buf->LUN[i].lunid, lbuf->LUN[i], 8);
+ }
+ kfree(lbuf);
+ return rc;
}
static inline int hpsa_scsi_do_report_log_luns(struct ctlr_info *h,
@@ -1547,181 +3821,278 @@ static inline void hpsa_set_bus_target_lun(struct hpsa_scsi_dev_t *device,
device->lun = lun;
}
+/* Use VPD inquiry to get details of volume status */
+static int hpsa_get_volume_status(struct ctlr_info *h,
+ unsigned char scsi3addr[])
+{
+ int rc;
+ int status;
+ int size;
+ unsigned char *buf;
+
+ buf = kzalloc(64, GFP_KERNEL);
+ if (!buf)
+ return HPSA_VPD_LV_STATUS_UNSUPPORTED;
+
+ /* Does controller have VPD for logical volume status? */
+ if (!hpsa_vpd_page_supported(h, scsi3addr, HPSA_VPD_LV_STATUS))
+ goto exit_failed;
+
+ /* Get the size of the VPD return buffer */
+ rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | HPSA_VPD_LV_STATUS,
+ buf, HPSA_VPD_HEADER_SZ);
+ if (rc != 0)
+ goto exit_failed;
+ size = buf[3];
+
+ /* Now get the whole VPD buffer */
+ rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | HPSA_VPD_LV_STATUS,
+ buf, size + HPSA_VPD_HEADER_SZ);
+ if (rc != 0)
+ goto exit_failed;
+ status = buf[4]; /* status byte */
+
+ kfree(buf);
+ return status;
+exit_failed:
+ kfree(buf);
+ return HPSA_VPD_LV_STATUS_UNSUPPORTED;
+}
+
+/* Determine offline status of a volume.
+ * Return either:
+ * 0 (not offline)
+ * 0xff (offline for unknown reasons)
+ * # (integer code indicating one of several NOT READY states
+ * describing why a volume is to be kept offline)
+ */
+static unsigned char hpsa_volume_offline(struct ctlr_info *h,
+ unsigned char scsi3addr[])
+{
+ struct CommandList *c;
+ unsigned char *sense;
+ u8 sense_key, asc, ascq;
+ int sense_len;
+ int rc, ldstat = 0;
+#define ASC_LUN_NOT_READY 0x04
+#define ASCQ_LUN_NOT_READY_FORMAT_IN_PROGRESS 0x04
+#define ASCQ_LUN_NOT_READY_INITIALIZING_CMD_REQ 0x02
+
+ c = cmd_alloc(h);
+
+ (void) fill_cmd(c, TEST_UNIT_READY, h, NULL, 0, 0, scsi3addr, TYPE_CMD);
+ rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE,
+ NO_TIMEOUT);
+ if (rc) {
+ cmd_free(h, c);
+ return HPSA_VPD_LV_STATUS_UNSUPPORTED;
+ }
+ sense = c->err_info->SenseInfo;
+ if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo))
+ sense_len = sizeof(c->err_info->SenseInfo);
+ else
+ sense_len = c->err_info->SenseLen;
+ decode_sense_data(sense, sense_len, &sense_key, &asc, &ascq);
+ cmd_free(h, c);
+
+ /* Determine the reason for not ready state */
+ ldstat = hpsa_get_volume_status(h, scsi3addr);
+
+ /* Keep volume offline in certain cases: */
+ switch (ldstat) {
+ case HPSA_LV_FAILED:
+ case HPSA_LV_UNDERGOING_ERASE:
+ case HPSA_LV_NOT_AVAILABLE:
+ case HPSA_LV_UNDERGOING_RPI:
+ case HPSA_LV_PENDING_RPI:
+ case HPSA_LV_ENCRYPTED_NO_KEY:
+ case HPSA_LV_PLAINTEXT_IN_ENCRYPT_ONLY_CONTROLLER:
+ case HPSA_LV_UNDERGOING_ENCRYPTION:
+ case HPSA_LV_UNDERGOING_ENCRYPTION_REKEYING:
+ case HPSA_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER:
+ return ldstat;
+ case HPSA_VPD_LV_STATUS_UNSUPPORTED:
+ /* If VPD status page isn't available,
+ * use ASC/ASCQ to determine state
+ */
+ if ((ascq == ASCQ_LUN_NOT_READY_FORMAT_IN_PROGRESS) ||
+ (ascq == ASCQ_LUN_NOT_READY_INITIALIZING_CMD_REQ))
+ return ldstat;
+ break;
+ default:
+ break;
+ }
+ return HPSA_LV_OK;
+}
+
static int hpsa_update_device_info(struct ctlr_info *h,
- unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device)
+ unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device,
+ unsigned char *is_OBDR_device)
{
-#define OBDR_TAPE_INQ_SIZE 49
+
+#define OBDR_SIG_OFFSET 43
+#define OBDR_TAPE_SIG "$DR-10"
+#define OBDR_SIG_LEN (sizeof(OBDR_TAPE_SIG) - 1)
+#define OBDR_TAPE_INQ_SIZE (OBDR_SIG_OFFSET + OBDR_SIG_LEN)
+
unsigned char *inq_buff;
+ unsigned char *obdr_sig;
+ int rc = 0;
inq_buff = kzalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
- if (!inq_buff)
+ if (!inq_buff) {
+ rc = -ENOMEM;
goto bail_out;
+ }
/* Do an inquiry to the device to see what it is. */
if (hpsa_scsi_do_inquiry(h, scsi3addr, 0, inq_buff,
(unsigned char) OBDR_TAPE_INQ_SIZE) != 0) {
- /* Inquiry failed (msg printed already) */
dev_err(&h->pdev->dev,
- "hpsa_update_device_info: inquiry failed\n");
+ "%s: inquiry failed, device will be skipped.\n",
+ __func__);
+ rc = HPSA_INQUIRY_FAILED;
goto bail_out;
}
+ scsi_sanitize_inquiry_string(&inq_buff[8], 8);
+ scsi_sanitize_inquiry_string(&inq_buff[16], 16);
+
this_device->devtype = (inq_buff[0] & 0x1f);
memcpy(this_device->scsi3addr, scsi3addr, 8);
memcpy(this_device->vendor, &inq_buff[8],
sizeof(this_device->vendor));
memcpy(this_device->model, &inq_buff[16],
sizeof(this_device->model));
+ this_device->rev = inq_buff[2];
memset(this_device->device_id, 0,
sizeof(this_device->device_id));
- hpsa_get_device_id(h, scsi3addr, this_device->device_id,
- sizeof(this_device->device_id));
+ if (hpsa_get_device_id(h, scsi3addr, this_device->device_id, 8,
+ sizeof(this_device->device_id)) < 0) {
+ dev_err(&h->pdev->dev,
+ "hpsa%d: %s: can't get device id for [%d:%d:%d:%d]\t%s\t%.16s\n",
+ h->ctlr, __func__,
+ h->scsi_host->host_no,
+ this_device->bus, this_device->target,
+ this_device->lun,
+ scsi_device_type(this_device->devtype),
+ this_device->model);
+ rc = HPSA_LV_FAILED;
+ goto bail_out;
+ }
+
+ if ((this_device->devtype == TYPE_DISK ||
+ this_device->devtype == TYPE_ZBC) &&
+ is_logical_dev_addr_mode(scsi3addr)) {
+ unsigned char volume_offline;
- if (this_device->devtype == TYPE_DISK &&
- is_logical_dev_addr_mode(scsi3addr))
hpsa_get_raid_level(h, scsi3addr, &this_device->raid_level);
- else
+ if (h->fw_support & MISC_FW_RAID_OFFLOAD_BASIC)
+ hpsa_get_ioaccel_status(h, scsi3addr, this_device);
+ volume_offline = hpsa_volume_offline(h, scsi3addr);
+ if (volume_offline == HPSA_VPD_LV_STATUS_UNSUPPORTED &&
+ h->legacy_board) {
+ /*
+ * Legacy boards might not support volume status
+ */
+ dev_info(&h->pdev->dev,
+ "C0:T%d:L%d Volume status not available, assuming online.\n",
+ this_device->target, this_device->lun);
+ volume_offline = 0;
+ }
+ this_device->volume_offline = volume_offline;
+ if (volume_offline == HPSA_LV_FAILED) {
+ rc = HPSA_LV_FAILED;
+ dev_err(&h->pdev->dev,
+ "%s: LV failed, device will be skipped.\n",
+ __func__);
+ goto bail_out;
+ }
+ } else {
this_device->raid_level = RAID_UNKNOWN;
+ this_device->offload_config = 0;
+ hpsa_turn_off_ioaccel_for_device(this_device);
+ this_device->hba_ioaccel_enabled = 0;
+ this_device->volume_offline = 0;
+ this_device->queue_depth = h->nr_cmds;
+ }
+ if (this_device->external)
+ this_device->queue_depth = EXTERNAL_QD;
+
+ if (is_OBDR_device) {
+ /* See if this is a One-Button-Disaster-Recovery device
+ * by looking for "$DR-10" at offset 43 in inquiry data.
+ */
+ obdr_sig = &inq_buff[OBDR_SIG_OFFSET];
+ *is_OBDR_device = (this_device->devtype == TYPE_ROM &&
+ strncmp(obdr_sig, OBDR_TAPE_SIG,
+ OBDR_SIG_LEN) == 0);
+ }
kfree(inq_buff);
return 0;
bail_out:
kfree(inq_buff);
- return 1;
-}
-
-static unsigned char *msa2xxx_model[] = {
- "MSA2012",
- "MSA2024",
- "MSA2312",
- "MSA2324",
- "P2000 G3 SAS",
- NULL,
-};
-
-static int is_msa2xxx(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
-{
- int i;
-
- for (i = 0; msa2xxx_model[i]; i++)
- if (strncmp(device->model, msa2xxx_model[i],
- strlen(msa2xxx_model[i])) == 0)
- return 1;
- return 0;
+ return rc;
}
-/* Helper function to assign bus, target, lun mapping of devices.
- * Puts non-msa2xxx logical volumes on bus 0, msa2xxx logical
- * volumes on bus 1, physical devices on bus 2. and the hba on bus 3.
+/*
+ * Helper function to assign bus, target, lun mapping of devices.
* Logical drive target and lun are assigned at this time, but
* physical device lun and target assignment are deferred (assigned
* in hpsa_find_target_lun, called by hpsa_scsi_add_entry.)
- */
+*/
static void figure_bus_target_lun(struct ctlr_info *h,
- u8 *lunaddrbytes, int *bus, int *target, int *lun,
- struct hpsa_scsi_dev_t *device)
+ u8 *lunaddrbytes, struct hpsa_scsi_dev_t *device)
{
- u32 lunid;
+ u32 lunid = get_unaligned_le32(lunaddrbytes);
- if (is_logical_dev_addr_mode(lunaddrbytes)) {
- /* logical device */
- if (unlikely(is_scsi_rev_5(h))) {
- /* p1210m, logical drives lun assignments
- * match SCSI REPORT LUNS data.
- */
- lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
- *bus = 0;
- *target = 0;
- *lun = (lunid & 0x3fff) + 1;
- } else {
- /* not p1210m... */
- lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
- if (is_msa2xxx(h, device)) {
- /* msa2xxx way, put logicals on bus 1
- * and match target/lun numbers box
- * reports.
- */
- *bus = 1;
- *target = (lunid >> 16) & 0x3fff;
- *lun = lunid & 0x00ff;
- } else {
- /* Traditional smart array way. */
- *bus = 0;
- *lun = 0;
- *target = lunid & 0x3fff;
- }
- }
- } else {
- /* physical device */
- if (is_hba_lunid(lunaddrbytes))
- if (unlikely(is_scsi_rev_5(h))) {
- *bus = 0; /* put p1210m ctlr at 0,0,0 */
- *target = 0;
- *lun = 0;
- return;
- } else
- *bus = 3; /* traditional smartarray */
- else
- *bus = 2; /* physical disk */
- *target = -1;
- *lun = -1; /* we will fill these in later. */
+ if (!is_logical_dev_addr_mode(lunaddrbytes)) {
+ /* physical device, target and lun filled in later */
+ if (is_hba_lunid(lunaddrbytes)) {
+ int bus = HPSA_HBA_BUS;
+
+ if (!device->rev)
+ bus = HPSA_LEGACY_HBA_BUS;
+ hpsa_set_bus_target_lun(device,
+ bus, 0, lunid & 0x3fff);
+ } else
+ /* defer target, lun assignment for physical devices */
+ hpsa_set_bus_target_lun(device,
+ HPSA_PHYSICAL_DEVICE_BUS, -1, -1);
+ return;
}
+ /* It's a logical device */
+ if (device->external) {
+ hpsa_set_bus_target_lun(device,
+ HPSA_EXTERNAL_RAID_VOLUME_BUS, (lunid >> 16) & 0x3fff,
+ lunid & 0x00ff);
+ return;
+ }
+ hpsa_set_bus_target_lun(device, HPSA_RAID_VOLUME_BUS,
+ 0, lunid & 0x3fff);
}
-/*
- * If there is no lun 0 on a target, linux won't find any devices.
- * For the MSA2xxx boxes, we have to manually detect the enclosure
- * which is at lun zero, as CCISS_REPORT_PHYSICAL_LUNS doesn't report
- * it for some reason. *tmpdevice is the target we're adding,
- * this_device is a pointer into the current element of currentsd[]
- * that we're building up in update_scsi_devices(), below.
- * lunzerobits is a bitmap that tracks which targets already have a
- * lun 0 assigned.
- * Returns 1 if an enclosure was added, 0 if not.
- */
-static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
- struct hpsa_scsi_dev_t *tmpdevice,
- struct hpsa_scsi_dev_t *this_device, u8 *lunaddrbytes,
- int bus, int target, int lun, unsigned long lunzerobits[],
- int *nmsa2xxx_enclosures)
+static int figure_external_status(struct ctlr_info *h, int raid_ctlr_position,
+ int i, int nphysicals, int nlocal_logicals)
{
- unsigned char scsi3addr[8];
-
- if (test_bit(target, lunzerobits))
- return 0; /* There is already a lun 0 on this target. */
-
- if (!is_logical_dev_addr_mode(lunaddrbytes))
- return 0; /* It's the logical targets that may lack lun 0. */
-
- if (!is_msa2xxx(h, tmpdevice))
- return 0; /* It's only the MSA2xxx that have this problem. */
+ /* In report logicals, local logicals are listed first,
+ * then any externals.
+ */
+ int logicals_start = nphysicals + (raid_ctlr_position == 0);
- if (lun == 0) /* if lun is 0, then obviously we have a lun 0. */
+ if (i == raid_ctlr_position)
return 0;
- memset(scsi3addr, 0, 8);
- scsi3addr[3] = target;
- if (is_hba_lunid(scsi3addr))
- return 0; /* Don't add the RAID controller here. */
-
- if (is_scsi_rev_5(h))
- return 0; /* p1210m doesn't need to do this. */
-
-#define MAX_MSA2XXX_ENCLOSURES 32
- if (*nmsa2xxx_enclosures >= MAX_MSA2XXX_ENCLOSURES) {
- dev_warn(&h->pdev->dev, "Maximum number of MSA2XXX "
- "enclosures exceeded. Check your hardware "
- "configuration.");
+ if (i < logicals_start)
return 0;
- }
- if (hpsa_update_device_info(h, scsi3addr, this_device))
+ /* i is in logicals range, but still within local logicals */
+ if ((i - nphysicals - (raid_ctlr_position == 0)) < nlocal_logicals)
return 0;
- (*nmsa2xxx_enclosures)++;
- hpsa_set_bus_target_lun(this_device, bus, target, 0);
- set_bit(target, lunzerobits);
- return 1;
+
+ return 1; /* it's an external lun */
}
/*
@@ -1731,22 +4102,20 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
* Returns 0 on success, -1 otherwise.
*/
static int hpsa_gather_lun_info(struct ctlr_info *h,
- int reportlunsize,
- struct ReportLUNdata *physdev, u32 *nphysicals,
+ struct ReportExtendedLUNdata *physdev, u32 *nphysicals,
struct ReportLUNdata *logdev, u32 *nlogicals)
{
- if (hpsa_scsi_do_report_phys_luns(h, physdev, reportlunsize, 0)) {
+ if (hpsa_scsi_do_report_phys_luns(h, physdev, sizeof(*physdev))) {
dev_err(&h->pdev->dev, "report physical LUNs failed.\n");
return -1;
}
- *nphysicals = be32_to_cpu(*((__be32 *)physdev->LUNListLength)) / 8;
+ *nphysicals = be32_to_cpu(*((__be32 *)physdev->LUNListLength)) / 24;
if (*nphysicals > HPSA_MAX_PHYS_LUN) {
- dev_warn(&h->pdev->dev, "maximum physical LUNs (%d) exceeded."
- " %d LUNs ignored.\n", HPSA_MAX_PHYS_LUN,
- *nphysicals - HPSA_MAX_PHYS_LUN);
+ dev_warn(&h->pdev->dev, "maximum physical LUNs (%d) exceeded. %d LUNs ignored.\n",
+ HPSA_MAX_PHYS_LUN, *nphysicals - HPSA_MAX_PHYS_LUN);
*nphysicals = HPSA_MAX_PHYS_LUN;
}
- if (hpsa_scsi_do_report_log_luns(h, logdev, reportlunsize)) {
+ if (hpsa_scsi_do_report_log_luns(h, logdev, sizeof(*logdev))) {
dev_err(&h->pdev->dev, "report logical LUNs failed.\n");
return -1;
}
@@ -1757,7 +4126,7 @@ static int hpsa_gather_lun_info(struct ctlr_info *h,
"maximum logical LUNs (%d) exceeded. "
"%d LUNs ignored.\n", HPSA_MAX_LUN,
*nlogicals - HPSA_MAX_LUN);
- *nlogicals = HPSA_MAX_LUN;
+ *nlogicals = HPSA_MAX_LUN;
}
if (*nlogicals + *nphysicals > HPSA_MAX_PHYS_LUN) {
dev_warn(&h->pdev->dev,
@@ -1769,8 +4138,9 @@ static int hpsa_gather_lun_info(struct ctlr_info *h,
return 0;
}
-u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, int i,
- int nphysicals, int nlogicals, struct ReportLUNdata *physdev_list,
+static u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position,
+ int i, int nphysicals, int nlogicals,
+ struct ReportExtendedLUNdata *physdev_list,
struct ReportLUNdata *logdev_list)
{
/* Helper function, figure out where the LUN ID info is coming from
@@ -1785,7 +4155,8 @@ u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, int i,
return RAID_CTLR_LUNID;
if (i < logicals_start)
- return &physdev_list->LUN[i - (raid_ctlr_position == 0)][0];
+ return &physdev_list->LUN[i -
+ (raid_ctlr_position == 0)].lunid[0];
if (i < last_device)
return &logdev_list->LUN[i - nphysicals -
@@ -1794,7 +4165,160 @@ u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, int i,
return NULL;
}
-static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
+/* get physical drive ioaccel handle and queue depth */
+static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *dev,
+ struct ReportExtendedLUNdata *rlep, int rle_index,
+ struct bmic_identify_physical_device *id_phys)
+{
+ int rc;
+ struct ext_report_lun_entry *rle;
+
+ if (rle_index < 0 || rle_index >= HPSA_MAX_PHYS_LUN)
+ return;
+
+ rle = &rlep->LUN[rle_index];
+
+ dev->ioaccel_handle = rle->ioaccel_handle;
+ if ((rle->device_flags & 0x08) && dev->ioaccel_handle)
+ dev->hba_ioaccel_enabled = 1;
+ memset(id_phys, 0, sizeof(*id_phys));
+ rc = hpsa_bmic_id_physical_device(h, &rle->lunid[0],
+ GET_BMIC_DRIVE_NUMBER(&rle->lunid[0]), id_phys,
+ sizeof(*id_phys));
+ if (!rc)
+ /* Reserve space for FW operations */
+#define DRIVE_CMDS_RESERVED_FOR_FW 2
+#define DRIVE_QUEUE_DEPTH 7
+ dev->queue_depth =
+ le16_to_cpu(id_phys->current_queue_depth_limit) -
+ DRIVE_CMDS_RESERVED_FOR_FW;
+ else
+ dev->queue_depth = DRIVE_QUEUE_DEPTH; /* conservative */
+}
+
+static void hpsa_get_path_info(struct hpsa_scsi_dev_t *this_device,
+ struct ReportExtendedLUNdata *rlep, int rle_index,
+ struct bmic_identify_physical_device *id_phys)
+{
+ struct ext_report_lun_entry *rle;
+
+ if (rle_index < 0 || rle_index >= HPSA_MAX_PHYS_LUN)
+ return;
+
+ rle = &rlep->LUN[rle_index];
+
+ if ((rle->device_flags & 0x08) && this_device->ioaccel_handle)
+ this_device->hba_ioaccel_enabled = 1;
+
+ memcpy(&this_device->active_path_index,
+ &id_phys->active_path_number,
+ sizeof(this_device->active_path_index));
+ memcpy(&this_device->path_map,
+ &id_phys->redundant_path_present_map,
+ sizeof(this_device->path_map));
+ memcpy(&this_device->box,
+ &id_phys->alternate_paths_phys_box_on_port,
+ sizeof(this_device->box));
+ memcpy(&this_device->phys_connector,
+ &id_phys->alternate_paths_phys_connector,
+ sizeof(this_device->phys_connector));
+ memcpy(&this_device->bay,
+ &id_phys->phys_bay_in_box,
+ sizeof(this_device->bay));
+}
+
+/* get number of local logical disks. */
+static int hpsa_set_local_logical_count(struct ctlr_info *h,
+ struct bmic_identify_controller *id_ctlr,
+ u32 *nlocals)
+{
+ int rc;
+
+ if (!id_ctlr) {
+ dev_warn(&h->pdev->dev, "%s: id_ctlr buffer is NULL.\n",
+ __func__);
+ return -ENOMEM;
+ }
+ memset(id_ctlr, 0, sizeof(*id_ctlr));
+ rc = hpsa_bmic_id_controller(h, id_ctlr, sizeof(*id_ctlr));
+ if (!rc)
+ if (id_ctlr->configured_logical_drive_count < 255)
+ *nlocals = id_ctlr->configured_logical_drive_count;
+ else
+ *nlocals = le16_to_cpu(
+ id_ctlr->extended_logical_unit_count);
+ else
+ *nlocals = -1;
+ return rc;
+}
+
+static bool hpsa_is_disk_spare(struct ctlr_info *h, u8 *lunaddrbytes)
+{
+ struct bmic_identify_physical_device *id_phys;
+ bool is_spare = false;
+ int rc;
+
+ id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL);
+ if (!id_phys)
+ return false;
+
+ rc = hpsa_bmic_id_physical_device(h,
+ lunaddrbytes,
+ GET_BMIC_DRIVE_NUMBER(lunaddrbytes),
+ id_phys, sizeof(*id_phys));
+ if (rc == 0)
+ is_spare = (id_phys->more_flags >> 6) & 0x01;
+
+ kfree(id_phys);
+ return is_spare;
+}
+
+#define RPL_DEV_FLAG_NON_DISK 0x1
+#define RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED 0x2
+#define RPL_DEV_FLAG_UNCONFIG_DISK 0x4
+
+#define BMIC_DEVICE_TYPE_ENCLOSURE 6
+
+static bool hpsa_skip_device(struct ctlr_info *h, u8 *lunaddrbytes,
+ struct ext_report_lun_entry *rle)
+{
+ u8 device_flags;
+ u8 device_type;
+
+ if (!MASKED_DEVICE(lunaddrbytes))
+ return false;
+
+ device_flags = rle->device_flags;
+ device_type = rle->device_type;
+
+ if (device_flags & RPL_DEV_FLAG_NON_DISK) {
+ if (device_type == BMIC_DEVICE_TYPE_ENCLOSURE)
+ return false;
+ return true;
+ }
+
+ if (!(device_flags & RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED))
+ return false;
+
+ if (device_flags & RPL_DEV_FLAG_UNCONFIG_DISK)
+ return false;
+
+ /*
+ * Spares may be spun down, we do not want to
+ * do an Inquiry to a RAID set spare drive as
+ * that would have them spun up, that is a
+ * performance hit because I/O to the RAID device
+ * stops while the spin up occurs which can take
+ * over 50 seconds.
+ */
+ if (hpsa_is_disk_spare(h, lunaddrbytes))
+ return true;
+
+ return false;
+}
+
+static void hpsa_update_scsi_devices(struct ctlr_info *h)
{
/* the idea here is we could get notified
* that some devices have changed, so we do a report
@@ -1806,99 +4330,147 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
* tell which devices we already know about, vs. new
* devices, vs. disappearing devices.
*/
- struct ReportLUNdata *physdev_list = NULL;
+ struct ReportExtendedLUNdata *physdev_list = NULL;
struct ReportLUNdata *logdev_list = NULL;
- unsigned char *inq_buff = NULL;
+ struct bmic_identify_physical_device *id_phys = NULL;
+ struct bmic_identify_controller *id_ctlr = NULL;
u32 nphysicals = 0;
u32 nlogicals = 0;
+ u32 nlocal_logicals = 0;
u32 ndev_allocated = 0;
struct hpsa_scsi_dev_t **currentsd, *this_device, *tmpdevice;
int ncurrent = 0;
- int reportlunsize = sizeof(*physdev_list) + HPSA_MAX_PHYS_LUN * 8;
- int i, nmsa2xxx_enclosures, ndevs_to_allocate;
- int bus, target, lun;
+ int i, ndevs_to_allocate;
int raid_ctlr_position;
- DECLARE_BITMAP(lunzerobits, HPSA_MAX_TARGETS_PER_CTLR);
+ bool physical_device;
- currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_SCSI_DEVS_PER_HBA,
- GFP_KERNEL);
- physdev_list = kzalloc(reportlunsize, GFP_KERNEL);
- logdev_list = kzalloc(reportlunsize, GFP_KERNEL);
- inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
+ currentsd = kcalloc(HPSA_MAX_DEVICES, sizeof(*currentsd), GFP_KERNEL);
+ physdev_list = kzalloc(sizeof(*physdev_list), GFP_KERNEL);
+ logdev_list = kzalloc(sizeof(*logdev_list), GFP_KERNEL);
tmpdevice = kzalloc(sizeof(*tmpdevice), GFP_KERNEL);
+ id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL);
+ id_ctlr = kzalloc(sizeof(*id_ctlr), GFP_KERNEL);
if (!currentsd || !physdev_list || !logdev_list ||
- !inq_buff || !tmpdevice) {
+ !tmpdevice || !id_phys || !id_ctlr) {
dev_err(&h->pdev->dev, "out of memory\n");
goto out;
}
- memset(lunzerobits, 0, sizeof(lunzerobits));
- if (hpsa_gather_lun_info(h, reportlunsize, physdev_list, &nphysicals,
- logdev_list, &nlogicals))
+ h->drv_req_rescan = 0; /* cancel scheduled rescan - we're doing it. */
+
+ if (hpsa_gather_lun_info(h, physdev_list, &nphysicals,
+ logdev_list, &nlogicals)) {
+ h->drv_req_rescan = 1;
goto out;
+ }
+
+ /* Set number of local logicals (non PTRAID) */
+ if (hpsa_set_local_logical_count(h, id_ctlr, &nlocal_logicals)) {
+ dev_warn(&h->pdev->dev,
+ "%s: Can't determine number of local logical devices.\n",
+ __func__);
+ }
- /* We might see up to 32 MSA2xxx enclosures, actually 8 of them
- * but each of them 4 times through different paths. The plus 1
- * is for the RAID controller.
+ /* We might see up to the maximum number of logical and physical disks
+ * plus external target devices, and a device for the local RAID
+ * controller.
*/
- ndevs_to_allocate = nphysicals + nlogicals + MAX_MSA2XXX_ENCLOSURES + 1;
+ ndevs_to_allocate = nphysicals + nlogicals + MAX_EXT_TARGETS + 1;
+
+ hpsa_ext_ctrl_present(h, physdev_list);
/* Allocate the per device structures */
for (i = 0; i < ndevs_to_allocate; i++) {
+ if (i >= HPSA_MAX_DEVICES) {
+ dev_warn(&h->pdev->dev, "maximum devices (%d) exceeded."
+ " %d devices ignored.\n", HPSA_MAX_DEVICES,
+ ndevs_to_allocate - HPSA_MAX_DEVICES);
+ break;
+ }
+
currentsd[i] = kzalloc(sizeof(*currentsd[i]), GFP_KERNEL);
if (!currentsd[i]) {
- dev_warn(&h->pdev->dev, "out of memory at %s:%d\n",
- __FILE__, __LINE__);
+ h->drv_req_rescan = 1;
goto out;
}
ndev_allocated++;
}
- if (unlikely(is_scsi_rev_5(h)))
+ if (is_scsi_rev_5(h))
raid_ctlr_position = 0;
else
raid_ctlr_position = nphysicals + nlogicals;
/* adjust our table of devices */
- nmsa2xxx_enclosures = 0;
for (i = 0; i < nphysicals + nlogicals + 1; i++) {
- u8 *lunaddrbytes;
+ u8 *lunaddrbytes, is_OBDR = 0;
+ int rc = 0;
+ int phys_dev_index = i - (raid_ctlr_position == 0);
+ bool skip_device = false;
+
+ memset(tmpdevice, 0, sizeof(*tmpdevice));
+
+ physical_device = i < nphysicals + (raid_ctlr_position == 0);
/* Figure out where the LUN ID info is coming from */
lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
i, nphysicals, nlogicals, physdev_list, logdev_list);
- /* skip masked physical devices. */
- if (lunaddrbytes[3] & 0xC0 &&
- i < nphysicals + (raid_ctlr_position == 0))
- continue;
- /* Get device type, vendor, model, device id */
- if (hpsa_update_device_info(h, lunaddrbytes, tmpdevice))
- continue; /* skip it if we can't talk to it. */
- figure_bus_target_lun(h, lunaddrbytes, &bus, &target, &lun,
- tmpdevice);
- this_device = currentsd[ncurrent];
+ /* Determine if this is a lun from an external target array */
+ tmpdevice->external =
+ figure_external_status(h, raid_ctlr_position, i,
+ nphysicals, nlocal_logicals);
/*
- * For the msa2xxx boxes, we have to insert a LUN 0 which
- * doesn't show up in CCISS_REPORT_PHYSICAL data, but there
- * is nonetheless an enclosure device there. We have to
- * present that otherwise linux won't find anything if
- * there is no lun 0.
+ * Skip over some devices such as a spare.
*/
- if (add_msa2xxx_enclosure_device(h, tmpdevice, this_device,
- lunaddrbytes, bus, target, lun, lunzerobits,
- &nmsa2xxx_enclosures)) {
- ncurrent++;
- this_device = currentsd[ncurrent];
+ if (phys_dev_index >= 0 && !tmpdevice->external &&
+ physical_device) {
+ skip_device = hpsa_skip_device(h, lunaddrbytes,
+ &physdev_list->LUN[phys_dev_index]);
+ if (skip_device)
+ continue;
}
+ /* Get device type, vendor, model, device id, raid_map */
+ rc = hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
+ &is_OBDR);
+ if (rc == -ENOMEM) {
+ dev_warn(&h->pdev->dev,
+ "Out of memory, rescan deferred.\n");
+ h->drv_req_rescan = 1;
+ goto out;
+ }
+ if (rc) {
+ h->drv_req_rescan = 1;
+ continue;
+ }
+
+ figure_bus_target_lun(h, lunaddrbytes, tmpdevice);
+ this_device = currentsd[ncurrent];
+
*this_device = *tmpdevice;
- hpsa_set_bus_target_lun(this_device, bus, target, lun);
+ this_device->physical_device = physical_device;
+
+ /*
+ * Expose all devices except for physical devices that
+ * are masked.
+ */
+ if (MASKED_DEVICE(lunaddrbytes) && this_device->physical_device)
+ this_device->expose_device = 0;
+ else
+ this_device->expose_device = 1;
+
+
+ /*
+ * Get the SAS address for physical devices that are exposed.
+ */
+ if (this_device->physical_device && this_device->expose_device)
+ hpsa_get_sas_address(h, lunaddrbytes, this_device);
switch (this_device->devtype) {
- case TYPE_ROM: {
+ case TYPE_ROM:
/* We don't *really* support actual CD-ROM devices,
* just "One Button Disaster Recovery" tape drive
* which temporarily pretends to be a CD-ROM drive.
@@ -1906,25 +4478,33 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
* device by checking for "$DR-10" in bytes 43-48 of
* the inquiry data.
*/
- char obdr_sig[7];
-#define OBDR_TAPE_SIG "$DR-10"
- strncpy(obdr_sig, &inq_buff[43], 6);
- obdr_sig[6] = '\0';
- if (strncmp(obdr_sig, OBDR_TAPE_SIG, 6) != 0)
- /* Not OBDR device, ignore it. */
- break;
- }
- ncurrent++;
+ if (is_OBDR)
+ ncurrent++;
break;
case TYPE_DISK:
- if (i < nphysicals)
- break;
+ case TYPE_ZBC:
+ if (this_device->physical_device) {
+ /* The disk is in HBA mode. */
+ /* Never use RAID mapper in HBA mode. */
+ this_device->offload_enabled = 0;
+ hpsa_get_ioaccel_drive_info(h, this_device,
+ physdev_list, phys_dev_index, id_phys);
+ hpsa_get_path_info(this_device,
+ physdev_list, phys_dev_index, id_phys);
+ }
ncurrent++;
break;
case TYPE_TAPE:
case TYPE_MEDIUM_CHANGER:
ncurrent++;
break;
+ case TYPE_ENCLOSURE:
+ if (!this_device->external)
+ hpsa_get_enclosure_info(h, lunaddrbytes,
+ physdev_list, phys_dev_index,
+ this_device);
+ ncurrent++;
+ break;
case TYPE_RAID:
/* Only present the Smartarray HBA as a RAID controller.
* If it's a RAID controller other than the HBA itself
@@ -1938,21 +4518,46 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
default:
break;
}
- if (ncurrent >= HPSA_MAX_SCSI_DEVS_PER_HBA)
+ if (ncurrent >= HPSA_MAX_DEVICES)
break;
}
- adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent);
+
+ if (h->sas_host == NULL) {
+ int rc = 0;
+
+ rc = hpsa_add_sas_host(h);
+ if (rc) {
+ dev_warn(&h->pdev->dev,
+ "Could not add sas host %d\n", rc);
+ goto out;
+ }
+ }
+
+ adjust_hpsa_scsi_table(h, currentsd, ncurrent);
out:
kfree(tmpdevice);
for (i = 0; i < ndev_allocated; i++)
kfree(currentsd[i]);
kfree(currentsd);
- kfree(inq_buff);
kfree(physdev_list);
kfree(logdev_list);
+ kfree(id_ctlr);
+ kfree(id_phys);
}
-/* hpsa_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci
+static void hpsa_set_sg_descriptor(struct SGDescriptor *desc,
+ struct scatterlist *sg)
+{
+ u64 addr64 = (u64) sg_dma_address(sg);
+ unsigned int len = sg_dma_len(sg);
+
+ desc->Addr = cpu_to_le64(addr64);
+ desc->Len = cpu_to_le32(len);
+ desc->Ext = 0;
+}
+
+/*
+ * hpsa_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci
* dma mapping and fills in the scatter gather entries of the
* hpsa command, cp.
*/
@@ -1960,10 +4565,8 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
struct CommandList *cp,
struct scsi_cmnd *cmd)
{
- unsigned int len;
struct scatterlist *sg;
- u64 addr64;
- int use_sg, i, sg_index, chained;
+ int use_sg, i, sg_limit, chained;
struct SGDescriptor *curr_sg;
BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
@@ -1975,103 +4578,899 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
if (!use_sg)
goto sglist_finished;
+ /*
+ * If the number of entries is greater than the max for a single list,
+ * then we have a chained list; we will set up all but one entry in the
+ * first list (the last entry is saved for link information);
+ * otherwise, we don't have a chained list and we'll set up at each of
+ * the entries in the one list.
+ */
curr_sg = cp->SG;
- chained = 0;
- sg_index = 0;
- scsi_for_each_sg(cmd, sg, use_sg, i) {
- if (i == h->max_cmd_sg_entries - 1 &&
- use_sg > h->max_cmd_sg_entries) {
- chained = 1;
- curr_sg = h->cmd_sg_list[cp->cmdindex];
- sg_index = 0;
- }
- addr64 = (u64) sg_dma_address(sg);
- len = sg_dma_len(sg);
- curr_sg->Addr.lower = (u32) (addr64 & 0x0FFFFFFFFULL);
- curr_sg->Addr.upper = (u32) ((addr64 >> 32) & 0x0FFFFFFFFULL);
- curr_sg->Len = len;
- curr_sg->Ext = 0; /* we are not chaining */
+ chained = use_sg > h->max_cmd_sg_entries;
+ sg_limit = chained ? h->max_cmd_sg_entries - 1 : use_sg;
+ scsi_for_each_sg(cmd, sg, sg_limit, i) {
+ hpsa_set_sg_descriptor(curr_sg, sg);
curr_sg++;
}
+ if (chained) {
+ /*
+ * Continue with the chained list. Set curr_sg to the chained
+ * list. Modify the limit to the total count less the entries
+ * we've already set up. Resume the scan at the list entry
+ * where the previous loop left off.
+ */
+ curr_sg = h->cmd_sg_list[cp->cmdindex];
+ sg_limit = use_sg - sg_limit;
+ for_each_sg(sg, sg, sg_limit, i) {
+ hpsa_set_sg_descriptor(curr_sg, sg);
+ curr_sg++;
+ }
+ }
+
+ /* Back the pointer up to the last entry and mark it as "last". */
+ (curr_sg - 1)->Ext = cpu_to_le32(HPSA_SG_LAST);
+
if (use_sg + chained > h->maxSG)
h->maxSG = use_sg + chained;
if (chained) {
cp->Header.SGList = h->max_cmd_sg_entries;
- cp->Header.SGTotal = (u16) (use_sg + 1);
- hpsa_map_sg_chain_block(h, cp);
+ cp->Header.SGTotal = cpu_to_le16(use_sg + 1);
+ if (hpsa_map_sg_chain_block(h, cp)) {
+ scsi_dma_unmap(cmd);
+ return -1;
+ }
return 0;
}
sglist_finished:
cp->Header.SGList = (u8) use_sg; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (u16) use_sg; /* total sgs in this cmd list */
+ cp->Header.SGTotal = cpu_to_le16(use_sg); /* total sgs in cmd list */
return 0;
}
+static inline void warn_zero_length_transfer(struct ctlr_info *h,
+ u8 *cdb, int cdb_len,
+ const char *func)
+{
+ dev_warn(&h->pdev->dev,
+ "%s: Blocking zero-length request: CDB:%*phN\n",
+ func, cdb_len, cdb);
+}
-static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
+#define IO_ACCEL_INELIGIBLE 1
+/* zero-length transfers trigger hardware errors. */
+static bool is_zero_length_transfer(u8 *cdb)
{
- struct ctlr_info *h;
- struct hpsa_scsi_dev_t *dev;
- unsigned char scsi3addr[8];
- struct CommandList *c;
- unsigned long flags;
+ u32 block_cnt;
- /* Get the ptr to our adapter structure out of cmd->host. */
- h = sdev_to_hba(cmd->device);
- dev = cmd->device->hostdata;
- if (!dev) {
- cmd->result = DID_NO_CONNECT << 16;
- done(cmd);
- return 0;
+ /* Block zero-length transfer sizes on certain commands. */
+ switch (cdb[0]) {
+ case READ_10:
+ case WRITE_10:
+ case VERIFY: /* 0x2F */
+ case WRITE_VERIFY: /* 0x2E */
+ block_cnt = get_unaligned_be16(&cdb[7]);
+ break;
+ case READ_12:
+ case WRITE_12:
+ case VERIFY_12: /* 0xAF */
+ case WRITE_VERIFY_12: /* 0xAE */
+ block_cnt = get_unaligned_be32(&cdb[6]);
+ break;
+ case READ_16:
+ case WRITE_16:
+ case VERIFY_16: /* 0x8F */
+ block_cnt = get_unaligned_be32(&cdb[10]);
+ break;
+ default:
+ return false;
}
- memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr));
- /* Need a lock as this is being allocated from the pool */
- spin_lock_irqsave(&h->lock, flags);
- c = cmd_alloc(h);
- spin_unlock_irqrestore(&h->lock, flags);
- if (c == NULL) { /* trouble... */
- dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return SCSI_MLQUEUE_HOST_BUSY;
+ return block_cnt == 0;
+}
+
+static int fixup_ioaccel_cdb(u8 *cdb, int *cdb_len)
+{
+ int is_write = 0;
+ u32 block;
+ u32 block_cnt;
+
+ /* Perform some CDB fixups if needed using 10 byte reads/writes only */
+ switch (cdb[0]) {
+ case WRITE_6:
+ case WRITE_12:
+ is_write = 1;
+ fallthrough;
+ case READ_6:
+ case READ_12:
+ if (*cdb_len == 6) {
+ block = (((cdb[1] & 0x1F) << 16) |
+ (cdb[2] << 8) |
+ cdb[3]);
+ block_cnt = cdb[4];
+ if (block_cnt == 0)
+ block_cnt = 256;
+ } else {
+ BUG_ON(*cdb_len != 12);
+ block = get_unaligned_be32(&cdb[2]);
+ block_cnt = get_unaligned_be32(&cdb[6]);
+ }
+ if (block_cnt > 0xffff)
+ return IO_ACCEL_INELIGIBLE;
+
+ cdb[0] = is_write ? WRITE_10 : READ_10;
+ cdb[1] = 0;
+ cdb[2] = (u8) (block >> 24);
+ cdb[3] = (u8) (block >> 16);
+ cdb[4] = (u8) (block >> 8);
+ cdb[5] = (u8) (block);
+ cdb[6] = 0;
+ cdb[7] = (u8) (block_cnt >> 8);
+ cdb[8] = (u8) (block_cnt);
+ cdb[9] = 0;
+ *cdb_len = 10;
+ break;
}
+ return 0;
+}
- /* Fill in the command list header */
+static int hpsa_scsi_ioaccel1_queue_command(struct ctlr_info *h,
+ struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len,
+ u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk)
+{
+ struct scsi_cmnd *cmd = c->scsi_cmd;
+ struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[c->cmdindex];
+ unsigned int len;
+ unsigned int total_len = 0;
+ struct scatterlist *sg;
+ u64 addr64;
+ int use_sg, i;
+ struct SGDescriptor *curr_sg;
+ u32 control = IOACCEL1_CONTROL_SIMPLEQUEUE;
- cmd->scsi_done = done; /* save this for use by completion code */
+ /* TODO: implement chaining support */
+ if (scsi_sg_count(cmd) > h->ioaccel_maxsg) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
- /* save c in case we have to abort it */
- cmd->host_scribble = (unsigned char *) c;
+ BUG_ON(cmd->cmd_len > IOACCEL1_IOFLAGS_CDBLEN_MAX);
+
+ if (is_zero_length_transfer(cdb)) {
+ warn_zero_length_transfer(h, cdb, cdb_len, __func__);
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
+
+ if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
+
+ c->cmd_type = CMD_IOACCEL1;
+
+ /* Adjust the DMA address to point to the accelerated command buffer */
+ c->busaddr = (u32) h->ioaccel_cmd_pool_dhandle +
+ (c->cmdindex * sizeof(*cp));
+ BUG_ON(c->busaddr & 0x0000007F);
+
+ use_sg = scsi_dma_map(cmd);
+ if (use_sg < 0) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return use_sg;
+ }
+
+ if (use_sg) {
+ curr_sg = cp->SG;
+ scsi_for_each_sg(cmd, sg, use_sg, i) {
+ addr64 = (u64) sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ total_len += len;
+ curr_sg->Addr = cpu_to_le64(addr64);
+ curr_sg->Len = cpu_to_le32(len);
+ curr_sg->Ext = cpu_to_le32(0);
+ curr_sg++;
+ }
+ (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST);
+
+ switch (cmd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ control |= IOACCEL1_CONTROL_DATA_OUT;
+ break;
+ case DMA_FROM_DEVICE:
+ control |= IOACCEL1_CONTROL_DATA_IN;
+ break;
+ case DMA_NONE:
+ control |= IOACCEL1_CONTROL_NODATAXFER;
+ break;
+ default:
+ dev_err(&h->pdev->dev, "unknown data direction: %d\n",
+ cmd->sc_data_direction);
+ BUG();
+ break;
+ }
+ } else {
+ control |= IOACCEL1_CONTROL_NODATAXFER;
+ }
+
+ c->Header.SGList = use_sg;
+ /* Fill out the command structure to submit */
+ cp->dev_handle = cpu_to_le16(ioaccel_handle & 0xFFFF);
+ cp->transfer_len = cpu_to_le32(total_len);
+ cp->io_flags = cpu_to_le16(IOACCEL1_IOFLAGS_IO_REQ |
+ (cdb_len & IOACCEL1_IOFLAGS_CDBLEN_MASK));
+ cp->control = cpu_to_le32(control);
+ memcpy(cp->CDB, cdb, cdb_len);
+ memcpy(cp->CISS_LUN, scsi3addr, 8);
+ /* Tag was already set at init time. */
+ enqueue_cmd_and_start_io(h, c);
+ return 0;
+}
+
+/*
+ * Queue a command directly to a device behind the controller using the
+ * I/O accelerator path.
+ */
+static int hpsa_scsi_ioaccel_direct_map(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ struct scsi_cmnd *cmd = c->scsi_cmd;
+ struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
+
+ if (!dev)
+ return -1;
+
+ c->phys_disk = dev;
+
+ if (dev->in_reset)
+ return -1;
+
+ return hpsa_scsi_ioaccel_queue_command(h, c, dev->ioaccel_handle,
+ cmd->cmnd, cmd->cmd_len, dev->scsi3addr, dev);
+}
+
+/*
+ * Set encryption parameters for the ioaccel2 request
+ */
+static void set_encrypt_ioaccel2(struct ctlr_info *h,
+ struct CommandList *c, struct io_accel2_cmd *cp)
+{
+ struct scsi_cmnd *cmd = c->scsi_cmd;
+ struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
+ struct raid_map_data *map = &dev->raid_map;
+ u64 first_block;
+
+ /* Are we doing encryption on this device */
+ if (!(le16_to_cpu(map->flags) & RAID_MAP_FLAG_ENCRYPT_ON))
+ return;
+ /* Set the data encryption key index. */
+ cp->dekindex = map->dekindex;
+
+ /* Set the encryption enable flag, encoded into direction field. */
+ cp->direction |= IOACCEL2_DIRECTION_ENCRYPT_MASK;
+
+ /* Set encryption tweak values based on logical block address
+ * If block size is 512, tweak value is LBA.
+ * For other block sizes, tweak is (LBA * block size)/ 512)
+ */
+ switch (cmd->cmnd[0]) {
+ /* Required? 6-byte cdbs eliminated by fixup_ioaccel_cdb */
+ case READ_6:
+ case WRITE_6:
+ first_block = (((cmd->cmnd[1] & 0x1F) << 16) |
+ (cmd->cmnd[2] << 8) |
+ cmd->cmnd[3]);
+ break;
+ case WRITE_10:
+ case READ_10:
+ /* Required? 12-byte cdbs eliminated by fixup_ioaccel_cdb */
+ case WRITE_12:
+ case READ_12:
+ first_block = get_unaligned_be32(&cmd->cmnd[2]);
+ break;
+ case WRITE_16:
+ case READ_16:
+ first_block = get_unaligned_be64(&cmd->cmnd[2]);
+ break;
+ default:
+ dev_err(&h->pdev->dev,
+ "ERROR: %s: size (0x%x) not supported for encryption\n",
+ __func__, cmd->cmnd[0]);
+ BUG();
+ break;
+ }
+
+ if (le32_to_cpu(map->volume_blk_size) != 512)
+ first_block = first_block *
+ le32_to_cpu(map->volume_blk_size)/512;
+
+ cp->tweak_lower = cpu_to_le32(first_block);
+ cp->tweak_upper = cpu_to_le32(first_block >> 32);
+}
+
+static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
+ struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len,
+ u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk)
+{
+ struct scsi_cmnd *cmd = c->scsi_cmd;
+ struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex];
+ struct ioaccel2_sg_element *curr_sg;
+ int use_sg, i;
+ struct scatterlist *sg;
+ u64 addr64;
+ u32 len;
+ u32 total_len = 0;
+
+ if (!cmd->device)
+ return -1;
+
+ if (!cmd->device->hostdata)
+ return -1;
+
+ BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
+
+ if (is_zero_length_transfer(cdb)) {
+ warn_zero_length_transfer(h, cdb, cdb_len, __func__);
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
+
+ if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
+
+ c->cmd_type = CMD_IOACCEL2;
+ /* Adjust the DMA address to point to the accelerated command buffer */
+ c->busaddr = (u32) h->ioaccel2_cmd_pool_dhandle +
+ (c->cmdindex * sizeof(*cp));
+ BUG_ON(c->busaddr & 0x0000007F);
+
+ memset(cp, 0, sizeof(*cp));
+ cp->IU_type = IOACCEL2_IU_TYPE;
+
+ use_sg = scsi_dma_map(cmd);
+ if (use_sg < 0) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return use_sg;
+ }
+
+ if (use_sg) {
+ curr_sg = cp->sg;
+ if (use_sg > h->ioaccel_maxsg) {
+ addr64 = le64_to_cpu(
+ h->ioaccel2_cmd_sg_list[c->cmdindex]->address);
+ curr_sg->address = cpu_to_le64(addr64);
+ curr_sg->length = 0;
+ curr_sg->reserved[0] = 0;
+ curr_sg->reserved[1] = 0;
+ curr_sg->reserved[2] = 0;
+ curr_sg->chain_indicator = IOACCEL2_CHAIN;
+
+ curr_sg = h->ioaccel2_cmd_sg_list[c->cmdindex];
+ }
+ scsi_for_each_sg(cmd, sg, use_sg, i) {
+ addr64 = (u64) sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ total_len += len;
+ curr_sg->address = cpu_to_le64(addr64);
+ curr_sg->length = cpu_to_le32(len);
+ curr_sg->reserved[0] = 0;
+ curr_sg->reserved[1] = 0;
+ curr_sg->reserved[2] = 0;
+ curr_sg->chain_indicator = 0;
+ curr_sg++;
+ }
+
+ /*
+ * Set the last s/g element bit
+ */
+ (curr_sg - 1)->chain_indicator = IOACCEL2_LAST_SG;
+
+ switch (cmd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ cp->direction &= ~IOACCEL2_DIRECTION_MASK;
+ cp->direction |= IOACCEL2_DIR_DATA_OUT;
+ break;
+ case DMA_FROM_DEVICE:
+ cp->direction &= ~IOACCEL2_DIRECTION_MASK;
+ cp->direction |= IOACCEL2_DIR_DATA_IN;
+ break;
+ case DMA_NONE:
+ cp->direction &= ~IOACCEL2_DIRECTION_MASK;
+ cp->direction |= IOACCEL2_DIR_NO_DATA;
+ break;
+ default:
+ dev_err(&h->pdev->dev, "unknown data direction: %d\n",
+ cmd->sc_data_direction);
+ BUG();
+ break;
+ }
+ } else {
+ cp->direction &= ~IOACCEL2_DIRECTION_MASK;
+ cp->direction |= IOACCEL2_DIR_NO_DATA;
+ }
+
+ /* Set encryption parameters, if necessary */
+ set_encrypt_ioaccel2(h, c, cp);
+
+ cp->scsi_nexus = cpu_to_le32(ioaccel_handle);
+ cp->Tag = cpu_to_le32(c->cmdindex << DIRECT_LOOKUP_SHIFT);
+ memcpy(cp->cdb, cdb, sizeof(cp->cdb));
+
+ cp->data_len = cpu_to_le32(total_len);
+ cp->err_ptr = cpu_to_le64(c->busaddr +
+ offsetof(struct io_accel2_cmd, error_data));
+ cp->err_len = cpu_to_le32(sizeof(cp->error_data));
+
+ /* fill in sg elements */
+ if (use_sg > h->ioaccel_maxsg) {
+ cp->sg_count = 1;
+ cp->sg[0].length = cpu_to_le32(use_sg * sizeof(cp->sg[0]));
+ if (hpsa_map_ioaccel2_sg_chain_block(h, cp, c)) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ scsi_dma_unmap(cmd);
+ return -1;
+ }
+ } else
+ cp->sg_count = (u8) use_sg;
+ if (phys_disk->in_reset) {
+ cmd->result = DID_RESET << 16;
+ return -1;
+ }
+
+ enqueue_cmd_and_start_io(h, c);
+ return 0;
+}
+
+/*
+ * Queue a command to the correct I/O accelerator path.
+ */
+static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h,
+ struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len,
+ u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk)
+{
+ if (!c->scsi_cmd->device)
+ return -1;
+
+ if (!c->scsi_cmd->device->hostdata)
+ return -1;
+
+ if (phys_disk->in_reset)
+ return -1;
+
+ /* Try to honor the device's queue depth */
+ if (atomic_inc_return(&phys_disk->ioaccel_cmds_out) >
+ phys_disk->queue_depth) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ return IO_ACCEL_INELIGIBLE;
+ }
+ if (h->transMethod & CFGTBL_Trans_io_accel1)
+ return hpsa_scsi_ioaccel1_queue_command(h, c, ioaccel_handle,
+ cdb, cdb_len, scsi3addr,
+ phys_disk);
+ else
+ return hpsa_scsi_ioaccel2_queue_command(h, c, ioaccel_handle,
+ cdb, cdb_len, scsi3addr,
+ phys_disk);
+}
+
+static void raid_map_helper(struct raid_map_data *map,
+ int offload_to_mirror, u32 *map_index, u32 *current_group)
+{
+ if (offload_to_mirror == 0) {
+ /* use physical disk in the first mirrored group. */
+ *map_index %= le16_to_cpu(map->data_disks_per_row);
+ return;
+ }
+ do {
+ /* determine mirror group that *map_index indicates */
+ *current_group = *map_index /
+ le16_to_cpu(map->data_disks_per_row);
+ if (offload_to_mirror == *current_group)
+ continue;
+ if (*current_group < le16_to_cpu(map->layout_map_count) - 1) {
+ /* select map index from next group */
+ *map_index += le16_to_cpu(map->data_disks_per_row);
+ (*current_group)++;
+ } else {
+ /* select map index from first group */
+ *map_index %= le16_to_cpu(map->data_disks_per_row);
+ *current_group = 0;
+ }
+ } while (offload_to_mirror != *current_group);
+}
+
+/*
+ * Attempt to perform offload RAID mapping for a logical volume I/O.
+ */
+static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ struct scsi_cmnd *cmd = c->scsi_cmd;
+ struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
+ struct raid_map_data *map = &dev->raid_map;
+ struct raid_map_disk_data *dd = &map->data[0];
+ int is_write = 0;
+ u32 map_index;
+ u64 first_block, last_block;
+ u32 block_cnt;
+ u32 blocks_per_row;
+ u64 first_row, last_row;
+ u32 first_row_offset, last_row_offset;
+ u32 first_column, last_column;
+ u64 r0_first_row, r0_last_row;
+ u32 r5or6_blocks_per_row;
+ u64 r5or6_first_row, r5or6_last_row;
+ u32 r5or6_first_row_offset, r5or6_last_row_offset;
+ u32 r5or6_first_column, r5or6_last_column;
+ u32 total_disks_per_row;
+ u32 stripesize;
+ u32 first_group, last_group, current_group;
+ u32 map_row;
+ u32 disk_handle;
+ u64 disk_block;
+ u32 disk_block_cnt;
+ u8 cdb[16];
+ u8 cdb_len;
+ u16 strip_size;
+#if BITS_PER_LONG == 32
+ u64 tmpdiv;
+#endif
+ int offload_to_mirror;
+
+ if (!dev)
+ return -1;
+
+ if (dev->in_reset)
+ return -1;
+
+ /* check for valid opcode, get LBA and block count */
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ is_write = 1;
+ fallthrough;
+ case READ_6:
+ first_block = (((cmd->cmnd[1] & 0x1F) << 16) |
+ (cmd->cmnd[2] << 8) |
+ cmd->cmnd[3]);
+ block_cnt = cmd->cmnd[4];
+ if (block_cnt == 0)
+ block_cnt = 256;
+ break;
+ case WRITE_10:
+ is_write = 1;
+ fallthrough;
+ case READ_10:
+ first_block =
+ (((u64) cmd->cmnd[2]) << 24) |
+ (((u64) cmd->cmnd[3]) << 16) |
+ (((u64) cmd->cmnd[4]) << 8) |
+ cmd->cmnd[5];
+ block_cnt =
+ (((u32) cmd->cmnd[7]) << 8) |
+ cmd->cmnd[8];
+ break;
+ case WRITE_12:
+ is_write = 1;
+ fallthrough;
+ case READ_12:
+ first_block =
+ (((u64) cmd->cmnd[2]) << 24) |
+ (((u64) cmd->cmnd[3]) << 16) |
+ (((u64) cmd->cmnd[4]) << 8) |
+ cmd->cmnd[5];
+ block_cnt =
+ (((u32) cmd->cmnd[6]) << 24) |
+ (((u32) cmd->cmnd[7]) << 16) |
+ (((u32) cmd->cmnd[8]) << 8) |
+ cmd->cmnd[9];
+ break;
+ case WRITE_16:
+ is_write = 1;
+ fallthrough;
+ case READ_16:
+ first_block =
+ (((u64) cmd->cmnd[2]) << 56) |
+ (((u64) cmd->cmnd[3]) << 48) |
+ (((u64) cmd->cmnd[4]) << 40) |
+ (((u64) cmd->cmnd[5]) << 32) |
+ (((u64) cmd->cmnd[6]) << 24) |
+ (((u64) cmd->cmnd[7]) << 16) |
+ (((u64) cmd->cmnd[8]) << 8) |
+ cmd->cmnd[9];
+ block_cnt =
+ (((u32) cmd->cmnd[10]) << 24) |
+ (((u32) cmd->cmnd[11]) << 16) |
+ (((u32) cmd->cmnd[12]) << 8) |
+ cmd->cmnd[13];
+ break;
+ default:
+ return IO_ACCEL_INELIGIBLE; /* process via normal I/O path */
+ }
+ last_block = first_block + block_cnt - 1;
+
+ /* check for write to non-RAID-0 */
+ if (is_write && dev->raid_level != 0)
+ return IO_ACCEL_INELIGIBLE;
+
+ /* check for invalid block or wraparound */
+ if (last_block >= le64_to_cpu(map->volume_blk_cnt) ||
+ last_block < first_block)
+ return IO_ACCEL_INELIGIBLE;
+
+ /* calculate stripe information for the request */
+ blocks_per_row = le16_to_cpu(map->data_disks_per_row) *
+ le16_to_cpu(map->strip_size);
+ strip_size = le16_to_cpu(map->strip_size);
+#if BITS_PER_LONG == 32
+ tmpdiv = first_block;
+ (void) do_div(tmpdiv, blocks_per_row);
+ first_row = tmpdiv;
+ tmpdiv = last_block;
+ (void) do_div(tmpdiv, blocks_per_row);
+ last_row = tmpdiv;
+ first_row_offset = (u32) (first_block - (first_row * blocks_per_row));
+ last_row_offset = (u32) (last_block - (last_row * blocks_per_row));
+ tmpdiv = first_row_offset;
+ (void) do_div(tmpdiv, strip_size);
+ first_column = tmpdiv;
+ tmpdiv = last_row_offset;
+ (void) do_div(tmpdiv, strip_size);
+ last_column = tmpdiv;
+#else
+ first_row = first_block / blocks_per_row;
+ last_row = last_block / blocks_per_row;
+ first_row_offset = (u32) (first_block - (first_row * blocks_per_row));
+ last_row_offset = (u32) (last_block - (last_row * blocks_per_row));
+ first_column = first_row_offset / strip_size;
+ last_column = last_row_offset / strip_size;
+#endif
+
+ /* if this isn't a single row/column then give to the controller */
+ if ((first_row != last_row) || (first_column != last_column))
+ return IO_ACCEL_INELIGIBLE;
+
+ /* proceeding with driver mapping */
+ total_disks_per_row = le16_to_cpu(map->data_disks_per_row) +
+ le16_to_cpu(map->metadata_disks_per_row);
+ map_row = ((u32)(first_row >> map->parity_rotation_shift)) %
+ le16_to_cpu(map->row_cnt);
+ map_index = (map_row * total_disks_per_row) + first_column;
+
+ switch (dev->raid_level) {
+ case HPSA_RAID_0:
+ break; /* nothing special to do */
+ case HPSA_RAID_1:
+ /* Handles load balance across RAID 1 members.
+ * (2-drive R1 and R10 with even # of drives.)
+ * Appropriate for SSDs, not optimal for HDDs
+ * Ensure we have the correct raid_map.
+ */
+ if (le16_to_cpu(map->layout_map_count) != 2) {
+ hpsa_turn_off_ioaccel_for_device(dev);
+ return IO_ACCEL_INELIGIBLE;
+ }
+ if (dev->offload_to_mirror)
+ map_index += le16_to_cpu(map->data_disks_per_row);
+ dev->offload_to_mirror = !dev->offload_to_mirror;
+ break;
+ case HPSA_RAID_ADM:
+ /* Handles N-way mirrors (R1-ADM)
+ * and R10 with # of drives divisible by 3.)
+ * Ensure we have the correct raid_map.
+ */
+ if (le16_to_cpu(map->layout_map_count) != 3) {
+ hpsa_turn_off_ioaccel_for_device(dev);
+ return IO_ACCEL_INELIGIBLE;
+ }
+
+ offload_to_mirror = dev->offload_to_mirror;
+ raid_map_helper(map, offload_to_mirror,
+ &map_index, &current_group);
+ /* set mirror group to use next time */
+ offload_to_mirror =
+ (offload_to_mirror >=
+ le16_to_cpu(map->layout_map_count) - 1)
+ ? 0 : offload_to_mirror + 1;
+ dev->offload_to_mirror = offload_to_mirror;
+ /* Avoid direct use of dev->offload_to_mirror within this
+ * function since multiple threads might simultaneously
+ * increment it beyond the range of dev->layout_map_count -1.
+ */
+ break;
+ case HPSA_RAID_5:
+ case HPSA_RAID_6:
+ if (le16_to_cpu(map->layout_map_count) <= 1)
+ break;
+
+ /* Verify first and last block are in same RAID group */
+ r5or6_blocks_per_row =
+ le16_to_cpu(map->strip_size) *
+ le16_to_cpu(map->data_disks_per_row);
+ if (r5or6_blocks_per_row == 0) {
+ hpsa_turn_off_ioaccel_for_device(dev);
+ return IO_ACCEL_INELIGIBLE;
+ }
+ stripesize = r5or6_blocks_per_row *
+ le16_to_cpu(map->layout_map_count);
+#if BITS_PER_LONG == 32
+ tmpdiv = first_block;
+ first_group = do_div(tmpdiv, stripesize);
+ tmpdiv = first_group;
+ (void) do_div(tmpdiv, r5or6_blocks_per_row);
+ first_group = tmpdiv;
+ tmpdiv = last_block;
+ last_group = do_div(tmpdiv, stripesize);
+ tmpdiv = last_group;
+ (void) do_div(tmpdiv, r5or6_blocks_per_row);
+ last_group = tmpdiv;
+#else
+ first_group = (first_block % stripesize) / r5or6_blocks_per_row;
+ last_group = (last_block % stripesize) / r5or6_blocks_per_row;
+#endif
+ if (first_group != last_group)
+ return IO_ACCEL_INELIGIBLE;
+
+ /* Verify request is in a single row of RAID 5/6 */
+#if BITS_PER_LONG == 32
+ tmpdiv = first_block;
+ (void) do_div(tmpdiv, stripesize);
+ first_row = r5or6_first_row = r0_first_row = tmpdiv;
+ tmpdiv = last_block;
+ (void) do_div(tmpdiv, stripesize);
+ r5or6_last_row = r0_last_row = tmpdiv;
+#else
+ first_row = r5or6_first_row = r0_first_row =
+ first_block / stripesize;
+ r5or6_last_row = r0_last_row = last_block / stripesize;
+#endif
+ if (r5or6_first_row != r5or6_last_row)
+ return IO_ACCEL_INELIGIBLE;
+
+
+ /* Verify request is in a single column */
+#if BITS_PER_LONG == 32
+ tmpdiv = first_block;
+ first_row_offset = do_div(tmpdiv, stripesize);
+ tmpdiv = first_row_offset;
+ first_row_offset = (u32) do_div(tmpdiv, r5or6_blocks_per_row);
+ r5or6_first_row_offset = first_row_offset;
+ tmpdiv = last_block;
+ r5or6_last_row_offset = do_div(tmpdiv, stripesize);
+ tmpdiv = r5or6_last_row_offset;
+ r5or6_last_row_offset = do_div(tmpdiv, r5or6_blocks_per_row);
+ tmpdiv = r5or6_first_row_offset;
+ (void) do_div(tmpdiv, map->strip_size);
+ first_column = r5or6_first_column = tmpdiv;
+ tmpdiv = r5or6_last_row_offset;
+ (void) do_div(tmpdiv, map->strip_size);
+ r5or6_last_column = tmpdiv;
+#else
+ first_row_offset = r5or6_first_row_offset =
+ (u32)((first_block % stripesize) %
+ r5or6_blocks_per_row);
+
+ r5or6_last_row_offset =
+ (u32)((last_block % stripesize) %
+ r5or6_blocks_per_row);
+
+ first_column = r5or6_first_column =
+ r5or6_first_row_offset / le16_to_cpu(map->strip_size);
+ r5or6_last_column =
+ r5or6_last_row_offset / le16_to_cpu(map->strip_size);
+#endif
+ if (r5or6_first_column != r5or6_last_column)
+ return IO_ACCEL_INELIGIBLE;
+
+ /* Request is eligible */
+ map_row = ((u32)(first_row >> map->parity_rotation_shift)) %
+ le16_to_cpu(map->row_cnt);
+
+ map_index = (first_group *
+ (le16_to_cpu(map->row_cnt) * total_disks_per_row)) +
+ (map_row * total_disks_per_row) + first_column;
+ break;
+ default:
+ return IO_ACCEL_INELIGIBLE;
+ }
+
+ if (unlikely(map_index >= RAID_MAP_MAX_ENTRIES))
+ return IO_ACCEL_INELIGIBLE;
+
+ c->phys_disk = dev->phys_disk[map_index];
+ if (!c->phys_disk)
+ return IO_ACCEL_INELIGIBLE;
+
+ disk_handle = dd[map_index].ioaccel_handle;
+ disk_block = le64_to_cpu(map->disk_starting_blk) +
+ first_row * le16_to_cpu(map->strip_size) +
+ (first_row_offset - first_column *
+ le16_to_cpu(map->strip_size));
+ disk_block_cnt = block_cnt;
+
+ /* handle differing logical/physical block sizes */
+ if (map->phys_blk_shift) {
+ disk_block <<= map->phys_blk_shift;
+ disk_block_cnt <<= map->phys_blk_shift;
+ }
+ BUG_ON(disk_block_cnt > 0xffff);
+
+ /* build the new CDB for the physical disk I/O */
+ if (disk_block > 0xffffffff) {
+ cdb[0] = is_write ? WRITE_16 : READ_16;
+ cdb[1] = 0;
+ cdb[2] = (u8) (disk_block >> 56);
+ cdb[3] = (u8) (disk_block >> 48);
+ cdb[4] = (u8) (disk_block >> 40);
+ cdb[5] = (u8) (disk_block >> 32);
+ cdb[6] = (u8) (disk_block >> 24);
+ cdb[7] = (u8) (disk_block >> 16);
+ cdb[8] = (u8) (disk_block >> 8);
+ cdb[9] = (u8) (disk_block);
+ cdb[10] = (u8) (disk_block_cnt >> 24);
+ cdb[11] = (u8) (disk_block_cnt >> 16);
+ cdb[12] = (u8) (disk_block_cnt >> 8);
+ cdb[13] = (u8) (disk_block_cnt);
+ cdb[14] = 0;
+ cdb[15] = 0;
+ cdb_len = 16;
+ } else {
+ cdb[0] = is_write ? WRITE_10 : READ_10;
+ cdb[1] = 0;
+ cdb[2] = (u8) (disk_block >> 24);
+ cdb[3] = (u8) (disk_block >> 16);
+ cdb[4] = (u8) (disk_block >> 8);
+ cdb[5] = (u8) (disk_block);
+ cdb[6] = 0;
+ cdb[7] = (u8) (disk_block_cnt >> 8);
+ cdb[8] = (u8) (disk_block_cnt);
+ cdb[9] = 0;
+ cdb_len = 10;
+ }
+ return hpsa_scsi_ioaccel_queue_command(h, c, disk_handle, cdb, cdb_len,
+ dev->scsi3addr,
+ dev->phys_disk[map_index]);
+}
+
+/*
+ * Submit commands down the "normal" RAID stack path
+ * All callers to hpsa_ciss_submit must check lockup_detected
+ * beforehand, before (opt.) and after calling cmd_alloc
+ */
+static int hpsa_ciss_submit(struct ctlr_info *h,
+ struct CommandList *c, struct scsi_cmnd *cmd,
+ struct hpsa_scsi_dev_t *dev)
+{
+ cmd->host_scribble = (unsigned char *) c;
c->cmd_type = CMD_SCSI;
c->scsi_cmd = cmd;
c->Header.ReplyQueue = 0; /* unused in simple mode */
- memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8);
- c->Header.Tag.lower = (c->cmdindex << DIRECT_LOOKUP_SHIFT);
- c->Header.Tag.lower |= DIRECT_LOOKUP_BIT;
+ memcpy(&c->Header.LUN.LunAddrBytes[0], &dev->scsi3addr[0], 8);
+ c->Header.tag = cpu_to_le64((c->cmdindex << DIRECT_LOOKUP_SHIFT));
/* Fill in the request block... */
c->Request.Timeout = 0;
- memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB));
c->Request.CDBLen = cmd->cmd_len;
memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len);
- c->Request.Type.Type = TYPE_CMD;
- c->Request.Type.Attribute = ATTR_SIMPLE;
switch (cmd->sc_data_direction) {
case DMA_TO_DEVICE:
- c->Request.Type.Direction = XFER_WRITE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_WRITE);
break;
case DMA_FROM_DEVICE:
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_READ);
break;
case DMA_NONE:
- c->Request.Type.Direction = XFER_NONE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_NONE);
break;
case DMA_BIDIRECTIONAL:
/* This can happen if a buggy application does a scsi passthru
@@ -2079,7 +5478,8 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
* ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() )
*/
- c->Request.Type.Direction = XFER_RSVD;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_RSVD);
/* This is technically wrong, and hpsa controllers should
* reject it with CMD_INVALID, which is the most correct
* response, but non-fibre backends appear to let it
@@ -2098,26 +5498,283 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
}
if (hpsa_scatter_gather(h, c, cmd) < 0) { /* Fill SG list */
- cmd_free(h, c);
+ hpsa_cmd_resolve_and_free(h, c);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ if (dev->in_reset) {
+ hpsa_cmd_resolve_and_free(h, c);
return SCSI_MLQUEUE_HOST_BUSY;
}
+
+ c->device = dev;
+
enqueue_cmd_and_start_io(h, c);
/* the cmd'll come back via intr handler in complete_scsi_command() */
return 0;
}
-static DEF_SCSI_QCMD(hpsa_scsi_queue_command)
+static void hpsa_cmd_init(struct ctlr_info *h, int index,
+ struct CommandList *c)
+{
+ dma_addr_t cmd_dma_handle, err_dma_handle;
+
+ /* Zero out all of commandlist except the last field, refcount */
+ memset(c, 0, offsetof(struct CommandList, refcount));
+ c->Header.tag = cpu_to_le64((u64) (index << DIRECT_LOOKUP_SHIFT));
+ cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c);
+ c->err_info = h->errinfo_pool + index;
+ memset(c->err_info, 0, sizeof(*c->err_info));
+ err_dma_handle = h->errinfo_pool_dhandle
+ + index * sizeof(*c->err_info);
+ c->cmdindex = index;
+ c->busaddr = (u32) cmd_dma_handle;
+ c->ErrDesc.Addr = cpu_to_le64((u64) err_dma_handle);
+ c->ErrDesc.Len = cpu_to_le32((u32) sizeof(*c->err_info));
+ c->h = h;
+ c->scsi_cmd = SCSI_CMD_IDLE;
+}
+
+static void hpsa_preinitialize_commands(struct ctlr_info *h)
+{
+ int i;
+
+ for (i = 0; i < h->nr_cmds; i++) {
+ struct CommandList *c = h->cmd_pool + i;
+
+ hpsa_cmd_init(h, i, c);
+ atomic_set(&c->refcount, 0);
+ }
+}
+
+static inline void hpsa_cmd_partial_init(struct ctlr_info *h, int index,
+ struct CommandList *c)
+{
+ dma_addr_t cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c);
+
+ BUG_ON(c->cmdindex != index);
+
+ memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
+ memset(c->err_info, 0, sizeof(*c->err_info));
+ c->busaddr = (u32) cmd_dma_handle;
+}
+
+static int hpsa_ioaccel_submit(struct ctlr_info *h,
+ struct CommandList *c, struct scsi_cmnd *cmd,
+ bool retry)
+{
+ struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
+ int rc = IO_ACCEL_INELIGIBLE;
+
+ if (!dev)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ if (dev->in_reset)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ if (hpsa_simple_mode)
+ return IO_ACCEL_INELIGIBLE;
+
+ cmd->host_scribble = (unsigned char *) c;
+
+ if (dev->offload_enabled) {
+ hpsa_cmd_init(h, c->cmdindex, c); /* Zeroes out all fields */
+ c->cmd_type = CMD_SCSI;
+ c->scsi_cmd = cmd;
+ c->device = dev;
+ if (retry) /* Resubmit but do not increment device->commands_outstanding. */
+ c->retry_pending = true;
+ rc = hpsa_scsi_ioaccel_raid_map(h, c);
+ if (rc < 0) /* scsi_dma_map failed. */
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ } else if (dev->hba_ioaccel_enabled) {
+ hpsa_cmd_init(h, c->cmdindex, c); /* Zeroes out all fields */
+ c->cmd_type = CMD_SCSI;
+ c->scsi_cmd = cmd;
+ c->device = dev;
+ if (retry) /* Resubmit but do not increment device->commands_outstanding. */
+ c->retry_pending = true;
+ rc = hpsa_scsi_ioaccel_direct_map(h, c);
+ if (rc < 0) /* scsi_dma_map failed. */
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ }
+ return rc;
+}
+
+static void hpsa_command_resubmit_worker(struct work_struct *work)
+{
+ struct scsi_cmnd *cmd;
+ struct hpsa_scsi_dev_t *dev;
+ struct CommandList *c = container_of(work, struct CommandList, work);
+
+ cmd = c->scsi_cmd;
+ dev = cmd->device->hostdata;
+ if (!dev) {
+ cmd->result = DID_NO_CONNECT << 16;
+ return hpsa_cmd_free_and_done(c->h, c, cmd);
+ }
+
+ if (dev->in_reset) {
+ cmd->result = DID_RESET << 16;
+ return hpsa_cmd_free_and_done(c->h, c, cmd);
+ }
+
+ if (c->cmd_type == CMD_IOACCEL2) {
+ struct ctlr_info *h = c->h;
+ struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+ int rc;
+
+ if (c2->error_data.serv_response ==
+ IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL) {
+ /* Resubmit with the retry_pending flag set. */
+ rc = hpsa_ioaccel_submit(h, c, cmd, true);
+ if (rc == 0)
+ return;
+ if (rc == SCSI_MLQUEUE_HOST_BUSY) {
+ /*
+ * If we get here, it means dma mapping failed.
+ * Try again via scsi mid layer, which will
+ * then get SCSI_MLQUEUE_HOST_BUSY.
+ */
+ cmd->result = DID_IMM_RETRY << 16;
+ return hpsa_cmd_free_and_done(h, c, cmd);
+ }
+ /* else, fall thru and resubmit down CISS path */
+ }
+ }
+ hpsa_cmd_partial_init(c->h, c->cmdindex, c);
+ /*
+ * Here we have not come in though queue_command, so we
+ * can set the retry_pending flag to true for a driver initiated
+ * retry attempt (I.E. not a SML retry).
+ * I.E. We are submitting a driver initiated retry.
+ * Note: hpsa_ciss_submit does not zero out the command fields like
+ * ioaccel submit does.
+ */
+ c->retry_pending = true;
+ if (hpsa_ciss_submit(c->h, c, cmd, dev)) {
+ /*
+ * If we get here, it means dma mapping failed. Try
+ * again via scsi mid layer, which will then get
+ * SCSI_MLQUEUE_HOST_BUSY.
+ *
+ * hpsa_ciss_submit will have already freed c
+ * if it encountered a dma mapping failure.
+ */
+ cmd->result = DID_IMM_RETRY << 16;
+ scsi_done(cmd);
+ }
+}
+
+/* Running in struct Scsi_Host->host_lock less mode */
+static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
+{
+ struct ctlr_info *h;
+ struct hpsa_scsi_dev_t *dev;
+ struct CommandList *c;
+ int rc = 0;
+
+ /* Get the ptr to our adapter structure out of cmd->host. */
+ h = sdev_to_hba(cmd->device);
+
+ BUG_ON(scsi_cmd_to_rq(cmd)->tag < 0);
+
+ dev = cmd->device->hostdata;
+ if (!dev) {
+ cmd->result = DID_NO_CONNECT << 16;
+ scsi_done(cmd);
+ return 0;
+ }
+
+ if (dev->removed) {
+ cmd->result = DID_NO_CONNECT << 16;
+ scsi_done(cmd);
+ return 0;
+ }
+
+ if (unlikely(lockup_detected(h))) {
+ cmd->result = DID_NO_CONNECT << 16;
+ scsi_done(cmd);
+ return 0;
+ }
+
+ if (dev->in_reset)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+ c = cmd_tagged_alloc(h, cmd);
+ if (c == NULL)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+ /*
+ * This is necessary because the SML doesn't zero out this field during
+ * error recovery.
+ */
+ cmd->result = 0;
+
+ /*
+ * Call alternate submit routine for I/O accelerated commands.
+ * Retries always go down the normal I/O path.
+ * Note: If cmd->retries is non-zero, then this is a SML
+ * initiated retry and not a driver initiated retry.
+ * This command has been obtained from cmd_tagged_alloc
+ * and is therefore a brand-new command.
+ */
+ if (likely(cmd->retries == 0 &&
+ !blk_rq_is_passthrough(scsi_cmd_to_rq(cmd)) &&
+ h->acciopath_status)) {
+ /* Submit with the retry_pending flag unset. */
+ rc = hpsa_ioaccel_submit(h, c, cmd, false);
+ if (rc == 0)
+ return 0;
+ if (rc == SCSI_MLQUEUE_HOST_BUSY) {
+ hpsa_cmd_resolve_and_free(h, c);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ }
+ return hpsa_ciss_submit(h, c, cmd, dev);
+}
+
+static void hpsa_scan_complete(struct ctlr_info *h)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&h->scan_lock, flags);
+ h->scan_finished = 1;
+ wake_up(&h->scan_wait_queue);
+ spin_unlock_irqrestore(&h->scan_lock, flags);
+}
static void hpsa_scan_start(struct Scsi_Host *sh)
{
struct ctlr_info *h = shost_to_hba(sh);
unsigned long flags;
+ /*
+ * Don't let rescans be initiated on a controller known to be locked
+ * up. If the controller locks up *during* a rescan, that thread is
+ * probably hosed, but at least we can prevent new rescan threads from
+ * piling up on a locked up controller.
+ */
+ if (unlikely(lockup_detected(h)))
+ return hpsa_scan_complete(h);
+
+ /*
+ * If a scan is already waiting to run, no need to add another
+ */
+ spin_lock_irqsave(&h->scan_lock, flags);
+ if (h->scan_waiting) {
+ spin_unlock_irqrestore(&h->scan_lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&h->scan_lock, flags);
+
/* wait until any scan already in progress is finished. */
while (1) {
spin_lock_irqsave(&h->scan_lock, flags);
if (h->scan_finished)
break;
+ h->scan_waiting = 1;
spin_unlock_irqrestore(&h->scan_lock, flags);
wait_event(h->scan_wait_queue, h->scan_finished);
/* Note: We don't need to worry about a race between this
@@ -2127,14 +5784,42 @@ static void hpsa_scan_start(struct Scsi_Host *sh)
*/
}
h->scan_finished = 0; /* mark scan as in progress */
+ h->scan_waiting = 0;
spin_unlock_irqrestore(&h->scan_lock, flags);
- hpsa_update_scsi_devices(h, h->scsi_host->host_no);
+ if (unlikely(lockup_detected(h)))
+ return hpsa_scan_complete(h);
- spin_lock_irqsave(&h->scan_lock, flags);
- h->scan_finished = 1; /* mark scan as finished. */
- wake_up_all(&h->scan_wait_queue);
- spin_unlock_irqrestore(&h->scan_lock, flags);
+ /*
+ * Do the scan after a reset completion
+ */
+ spin_lock_irqsave(&h->reset_lock, flags);
+ if (h->reset_in_progress) {
+ h->drv_req_rescan = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+ hpsa_scan_complete(h);
+ return;
+ }
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+
+ hpsa_update_scsi_devices(h);
+
+ hpsa_scan_complete(h);
+}
+
+static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ struct hpsa_scsi_dev_t *logical_drive = sdev->hostdata;
+
+ if (!logical_drive)
+ return -ENODEV;
+
+ if (qdepth < 1)
+ qdepth = 1;
+ else if (qdepth > logical_drive->queue_depth)
+ qdepth = logical_drive->queue_depth;
+
+ return scsi_change_queue_depth(sdev, qdepth);
}
static int hpsa_scan_finished(struct Scsi_Host *sh,
@@ -2150,87 +5835,167 @@ static int hpsa_scan_finished(struct Scsi_Host *sh,
return finished;
}
-static int hpsa_change_queue_depth(struct scsi_device *sdev,
- int qdepth, int reason)
+static int hpsa_scsi_host_alloc(struct ctlr_info *h)
{
- struct ctlr_info *h = sdev_to_hba(sdev);
+ struct Scsi_Host *sh;
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -ENOTSUPP;
+ sh = scsi_host_alloc(&hpsa_driver_template, sizeof(struct ctlr_info *));
+ if (sh == NULL) {
+ dev_err(&h->pdev->dev, "scsi_host_alloc failed\n");
+ return -ENOMEM;
+ }
- if (qdepth < 1)
- qdepth = 1;
- else
- if (qdepth > h->nr_cmds)
- qdepth = h->nr_cmds;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- return sdev->queue_depth;
+ sh->io_port = 0;
+ sh->n_io_port = 0;
+ sh->this_id = -1;
+ sh->max_channel = 3;
+ sh->max_cmd_len = MAX_COMMAND_SIZE;
+ sh->max_lun = HPSA_MAX_LUN;
+ sh->max_id = HPSA_MAX_LUN;
+ sh->can_queue = h->nr_cmds - HPSA_NRESERVED_CMDS;
+ sh->cmd_per_lun = sh->can_queue;
+ sh->sg_tablesize = h->maxsgentries;
+ sh->transportt = hpsa_sas_transport_template;
+ sh->hostdata[0] = (unsigned long) h;
+ sh->irq = pci_irq_vector(h->pdev, 0);
+ sh->unique_id = sh->irq;
+
+ h->scsi_host = sh;
+ return 0;
}
-static void hpsa_unregister_scsi(struct ctlr_info *h)
+static int hpsa_scsi_add_host(struct ctlr_info *h)
{
- /* we are being forcibly unloaded, and may not refuse. */
- scsi_remove_host(h->scsi_host);
- scsi_host_put(h->scsi_host);
- h->scsi_host = NULL;
+ int rv;
+
+ rv = scsi_add_host(h->scsi_host, &h->pdev->dev);
+ if (rv) {
+ dev_err(&h->pdev->dev, "scsi_add_host failed\n");
+ return rv;
+ }
+ scsi_scan_host(h->scsi_host);
+ return 0;
}
-static int hpsa_register_scsi(struct ctlr_info *h)
+/*
+ * The block layer has already gone to the trouble of picking out a unique,
+ * small-integer tag for this request. We use an offset from that value as
+ * an index to select our command block. (The offset allows us to reserve the
+ * low-numbered entries for our own uses.)
+ */
+static int hpsa_get_cmd_index(struct scsi_cmnd *scmd)
+{
+ int idx = scsi_cmd_to_rq(scmd)->tag;
+
+ if (idx < 0)
+ return idx;
+
+ /* Offset to leave space for internal cmds. */
+ return idx += HPSA_NRESERVED_CMDS;
+}
+
+/*
+ * Send a TEST_UNIT_READY command to the specified LUN using the specified
+ * reply queue; returns zero if the unit is ready, and non-zero otherwise.
+ */
+static int hpsa_send_test_unit_ready(struct ctlr_info *h,
+ struct CommandList *c, unsigned char lunaddr[],
+ int reply_queue)
{
int rc;
- rc = hpsa_scsi_detect(h);
- if (rc != 0)
- dev_err(&h->pdev->dev, "hpsa_register_scsi: failed"
- " hpsa_scsi_detect(), rc is %d\n", rc);
- return rc;
+ /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */
+ (void) fill_cmd(c, TEST_UNIT_READY, h,
+ NULL, 0, 0, lunaddr, TYPE_CMD);
+ rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+ if (rc)
+ return rc;
+ /* no unmap needed here because no data xfer. */
+
+ /* Check if the unit is already ready. */
+ if (c->err_info->CommandStatus == CMD_SUCCESS)
+ return 0;
+
+ /*
+ * The first command sent after reset will receive "unit attention" to
+ * indicate that the LUN has been reset...this is actually what we're
+ * looking for (but, success is good too).
+ */
+ if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
+ c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION &&
+ (c->err_info->SenseInfo[2] == NO_SENSE ||
+ c->err_info->SenseInfo[2] == UNIT_ATTENTION))
+ return 0;
+
+ return 1;
}
-static int wait_for_device_to_become_ready(struct ctlr_info *h,
- unsigned char lunaddr[])
+/*
+ * Wait for a TEST_UNIT_READY command to complete, retrying as necessary;
+ * returns zero when the unit is ready, and non-zero when giving up.
+ */
+static int hpsa_wait_for_test_unit_ready(struct ctlr_info *h,
+ struct CommandList *c,
+ unsigned char lunaddr[], int reply_queue)
{
- int rc = 0;
+ int rc;
int count = 0;
int waittime = 1; /* seconds */
- struct CommandList *c;
-
- c = cmd_special_alloc(h);
- if (!c) {
- dev_warn(&h->pdev->dev, "out of memory in "
- "wait_for_device_to_become_ready.\n");
- return IO_ERROR;
- }
/* Send test unit ready until device ready, or give up. */
- while (count < HPSA_TUR_RETRY_LIMIT) {
+ for (count = 0; count < HPSA_TUR_RETRY_LIMIT; count++) {
- /* Wait for a bit. do this first, because if we send
+ /*
+ * Wait for a bit. do this first, because if we send
* the TUR right away, the reset will just abort it.
*/
msleep(1000 * waittime);
- count++;
+
+ rc = hpsa_send_test_unit_ready(h, c, lunaddr, reply_queue);
+ if (!rc)
+ break;
/* Increase wait time with each try, up to a point. */
if (waittime < HPSA_MAX_WAIT_INTERVAL_SECS)
- waittime = waittime * 2;
+ waittime *= 2;
- /* Send the Test Unit Ready */
- fill_cmd(c, TEST_UNIT_READY, h, NULL, 0, 0, lunaddr, TYPE_CMD);
- hpsa_scsi_do_simple_cmd_core(h, c);
- /* no unmap needed here because no data xfer. */
+ dev_warn(&h->pdev->dev,
+ "waiting %d secs for device to become ready.\n",
+ waittime);
+ }
- if (c->err_info->CommandStatus == CMD_SUCCESS)
- break;
+ return rc;
+}
- if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
- c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION &&
- (c->err_info->SenseInfo[2] == NO_SENSE ||
- c->err_info->SenseInfo[2] == UNIT_ATTENTION))
- break;
+static int wait_for_device_to_become_ready(struct ctlr_info *h,
+ unsigned char lunaddr[],
+ int reply_queue)
+{
+ int first_queue;
+ int last_queue;
+ int rq;
+ int rc = 0;
+ struct CommandList *c;
+
+ c = cmd_alloc(h);
+
+ /*
+ * If no specific reply queue was requested, then send the TUR
+ * repeatedly, requesting a reply on each reply queue; otherwise execute
+ * the loop exactly once using only the specified queue.
+ */
+ if (reply_queue == DEFAULT_REPLY_QUEUE) {
+ first_queue = 0;
+ last_queue = h->nreply_queues - 1;
+ } else {
+ first_queue = reply_queue;
+ last_queue = reply_queue;
+ }
- dev_warn(&h->pdev->dev, "waiting %d secs "
- "for device to become ready.\n", waittime);
- rc = 1; /* device not ready. */
+ for (rq = first_queue; rq <= last_queue; rq++) {
+ rc = hpsa_wait_for_test_unit_ready(h, c, lunaddr, rq);
+ if (rc)
+ break;
}
if (rc)
@@ -2238,7 +6003,7 @@ static int wait_for_device_to_become_ready(struct ctlr_info *h,
else
dev_warn(&h->pdev->dev, "device is ready.\n");
- cmd_special_free(h, c);
+ cmd_free(h, c);
return rc;
}
@@ -2247,210 +6012,313 @@ static int wait_for_device_to_become_ready(struct ctlr_info *h,
*/
static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
{
- int rc;
+ int rc = SUCCESS;
+ int i;
struct ctlr_info *h;
- struct hpsa_scsi_dev_t *dev;
+ struct hpsa_scsi_dev_t *dev = NULL;
+ u8 reset_type;
+ char msg[48];
+ unsigned long flags;
/* find the controller to which the command to be aborted was sent */
h = sdev_to_hba(scsicmd->device);
if (h == NULL) /* paranoia */
return FAILED;
+
+ spin_lock_irqsave(&h->reset_lock, flags);
+ h->reset_in_progress = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+
+ if (lockup_detected(h)) {
+ rc = FAILED;
+ goto return_reset_status;
+ }
+
dev = scsicmd->device->hostdata;
if (!dev) {
- dev_err(&h->pdev->dev, "hpsa_eh_device_reset_handler: "
- "device lookup failed.\n");
- return FAILED;
+ dev_err(&h->pdev->dev, "%s: device lookup failed\n", __func__);
+ rc = FAILED;
+ goto return_reset_status;
}
- dev_warn(&h->pdev->dev, "resetting device %d:%d:%d:%d\n",
- h->scsi_host->host_no, dev->bus, dev->target, dev->lun);
- /* send a reset to the SCSI LUN which the command was sent to */
- rc = hpsa_send_reset(h, dev->scsi3addr);
- if (rc == 0 && wait_for_device_to_become_ready(h, dev->scsi3addr) == 0)
- return SUCCESS;
- dev_warn(&h->pdev->dev, "resetting device failed.\n");
- return FAILED;
+ if (dev->devtype == TYPE_ENCLOSURE) {
+ rc = SUCCESS;
+ goto return_reset_status;
+ }
+
+ /* if controller locked up, we can guarantee command won't complete */
+ if (lockup_detected(h)) {
+ snprintf(msg, sizeof(msg),
+ "cmd %d RESET FAILED, lockup detected",
+ hpsa_get_cmd_index(scsicmd));
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+ rc = FAILED;
+ goto return_reset_status;
+ }
+
+ /* this reset request might be the result of a lockup; check */
+ if (detect_controller_lockup(h)) {
+ snprintf(msg, sizeof(msg),
+ "cmd %d RESET FAILED, new lockup detected",
+ hpsa_get_cmd_index(scsicmd));
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+ rc = FAILED;
+ goto return_reset_status;
+ }
+
+ /* Do not attempt on controller */
+ if (is_hba_lunid(dev->scsi3addr)) {
+ rc = SUCCESS;
+ goto return_reset_status;
+ }
+
+ if (is_logical_dev_addr_mode(dev->scsi3addr))
+ reset_type = HPSA_DEVICE_RESET_MSG;
+ else
+ reset_type = HPSA_PHYS_TARGET_RESET;
+
+ sprintf(msg, "resetting %s",
+ reset_type == HPSA_DEVICE_RESET_MSG ? "logical " : "physical ");
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+
+ /*
+ * wait to see if any commands will complete before sending reset
+ */
+ dev->in_reset = true; /* block any new cmds from OS for this device */
+ for (i = 0; i < 10; i++) {
+ if (atomic_read(&dev->commands_outstanding) > 0)
+ msleep(1000);
+ else
+ break;
+ }
+
+ /* send a reset to the SCSI LUN which the command was sent to */
+ rc = hpsa_do_reset(h, dev, reset_type, DEFAULT_REPLY_QUEUE);
+ if (rc == 0)
+ rc = SUCCESS;
+ else
+ rc = FAILED;
+
+ sprintf(msg, "reset %s %s",
+ reset_type == HPSA_DEVICE_RESET_MSG ? "logical " : "physical ",
+ rc == SUCCESS ? "completed successfully" : "failed");
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+
+return_reset_status:
+ spin_lock_irqsave(&h->reset_lock, flags);
+ h->reset_in_progress = 0;
+ if (dev)
+ dev->in_reset = false;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+ return rc;
}
/*
- * For operations that cannot sleep, a command block is allocated at init,
- * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
- * which ones are free or in use. Lock must be held when calling this.
- * cmd_free() is the complement.
+ * For operations with an associated SCSI command, a command block is allocated
+ * at init, and managed by cmd_tagged_alloc() and cmd_tagged_free() using the
+ * block request tag as an index into a table of entries. cmd_tagged_free() is
+ * the complement, although cmd_free() may be called instead.
+ * This function is only called for new requests from queue_command.
*/
-static struct CommandList *cmd_alloc(struct ctlr_info *h)
+static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
+ struct scsi_cmnd *scmd)
{
- struct CommandList *c;
- int i;
- union u64bit temp64;
- dma_addr_t cmd_dma_handle, err_dma_handle;
+ int idx = hpsa_get_cmd_index(scmd);
+ struct CommandList *c = h->cmd_pool + idx;
- do {
- i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds);
- if (i == h->nr_cmds)
- return NULL;
- } while (test_and_set_bit
- (i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0);
- c = h->cmd_pool + i;
- memset(c, 0, sizeof(*c));
- cmd_dma_handle = h->cmd_pool_dhandle
- + i * sizeof(*c);
- c->err_info = h->errinfo_pool + i;
- memset(c->err_info, 0, sizeof(*c->err_info));
- err_dma_handle = h->errinfo_pool_dhandle
- + i * sizeof(*c->err_info);
- h->nr_allocs++;
+ if (idx < HPSA_NRESERVED_CMDS || idx >= h->nr_cmds) {
+ dev_err(&h->pdev->dev, "Bad block tag: %d not in [%d..%d]\n",
+ idx, HPSA_NRESERVED_CMDS, h->nr_cmds - 1);
+ /* The index value comes from the block layer, so if it's out of
+ * bounds, it's probably not our bug.
+ */
+ BUG();
+ }
- c->cmdindex = i;
+ if (unlikely(!hpsa_is_cmd_idle(c))) {
+ /*
+ * We expect that the SCSI layer will hand us a unique tag
+ * value. Thus, there should never be a collision here between
+ * two requests...because if the selected command isn't idle
+ * then someone is going to be very disappointed.
+ */
+ if (idx != h->last_collision_tag) { /* Print once per tag */
+ dev_warn(&h->pdev->dev,
+ "%s: tag collision (tag=%d)\n", __func__, idx);
+ if (scmd)
+ scsi_print_command(scmd);
+ h->last_collision_tag = idx;
+ }
+ return NULL;
+ }
- INIT_LIST_HEAD(&c->list);
- c->busaddr = (u32) cmd_dma_handle;
- temp64.val = (u64) err_dma_handle;
- c->ErrDesc.Addr.lower = temp64.val32.lower;
- c->ErrDesc.Addr.upper = temp64.val32.upper;
- c->ErrDesc.Len = sizeof(*c->err_info);
+ atomic_inc(&c->refcount);
+ hpsa_cmd_partial_init(h, idx, c);
+
+ /*
+ * This is a new command obtained from queue_command so
+ * there have not been any driver initiated retry attempts.
+ */
+ c->retry_pending = false;
- c->h = h;
return c;
}
-/* For operations that can wait for kmalloc to possibly sleep,
- * this routine can be called. Lock need not be held to call
- * cmd_special_alloc. cmd_special_free() is the complement.
- */
-static struct CommandList *cmd_special_alloc(struct ctlr_info *h)
+static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c)
{
- struct CommandList *c;
- union u64bit temp64;
- dma_addr_t cmd_dma_handle, err_dma_handle;
+ /*
+ * Release our reference to the block. We don't need to do anything
+ * else to free it, because it is accessed by index.
+ */
+ (void)atomic_dec(&c->refcount);
+}
- c = pci_alloc_consistent(h->pdev, sizeof(*c), &cmd_dma_handle);
- if (c == NULL)
- return NULL;
- memset(c, 0, sizeof(*c));
+/*
+ * For operations that cannot sleep, a command block is allocated at init,
+ * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
+ * which ones are free or in use. Lock must be held when calling this.
+ * cmd_free() is the complement.
+ * This function never gives up and returns NULL. If it hangs,
+ * another thread must call cmd_free() to free some tags.
+ */
- c->cmdindex = -1;
+static struct CommandList *cmd_alloc(struct ctlr_info *h)
+{
+ struct CommandList *c;
+ int refcount, i;
+ int offset = 0;
- c->err_info = pci_alloc_consistent(h->pdev, sizeof(*c->err_info),
- &err_dma_handle);
+ /*
+ * There is some *extremely* small but non-zero chance that that
+ * multiple threads could get in here, and one thread could
+ * be scanning through the list of bits looking for a free
+ * one, but the free ones are always behind him, and other
+ * threads sneak in behind him and eat them before he can
+ * get to them, so that while there is always a free one, a
+ * very unlucky thread might be starved anyway, never able to
+ * beat the other threads. In reality, this happens so
+ * infrequently as to be indistinguishable from never.
+ *
+ * Note that we start allocating commands before the SCSI host structure
+ * is initialized. Since the search starts at bit zero, this
+ * all works, since we have at least one command structure available;
+ * however, it means that the structures with the low indexes have to be
+ * reserved for driver-initiated requests, while requests from the block
+ * layer will use the higher indexes.
+ */
- if (c->err_info == NULL) {
- pci_free_consistent(h->pdev,
- sizeof(*c), c, cmd_dma_handle);
- return NULL;
+ for (;;) {
+ i = find_next_zero_bit(h->cmd_pool_bits,
+ HPSA_NRESERVED_CMDS,
+ offset);
+ if (unlikely(i >= HPSA_NRESERVED_CMDS)) {
+ offset = 0;
+ continue;
+ }
+ c = h->cmd_pool + i;
+ refcount = atomic_inc_return(&c->refcount);
+ if (unlikely(refcount > 1)) {
+ cmd_free(h, c); /* already in use */
+ offset = (i + 1) % HPSA_NRESERVED_CMDS;
+ continue;
+ }
+ set_bit(i, h->cmd_pool_bits);
+ break; /* it's ours now. */
}
- memset(c->err_info, 0, sizeof(*c->err_info));
+ hpsa_cmd_partial_init(h, i, c);
+ c->device = NULL;
- INIT_LIST_HEAD(&c->list);
- c->busaddr = (u32) cmd_dma_handle;
- temp64.val = (u64) err_dma_handle;
- c->ErrDesc.Addr.lower = temp64.val32.lower;
- c->ErrDesc.Addr.upper = temp64.val32.upper;
- c->ErrDesc.Len = sizeof(*c->err_info);
+ /*
+ * cmd_alloc is for "internal" commands and they are never
+ * retried.
+ */
+ c->retry_pending = false;
- c->h = h;
return c;
}
+/*
+ * This is the complementary operation to cmd_alloc(). Note, however, in some
+ * corner cases it may also be used to free blocks allocated by
+ * cmd_tagged_alloc() in which case the ref-count decrement does the trick and
+ * the clear-bit is harmless.
+ */
static void cmd_free(struct ctlr_info *h, struct CommandList *c)
{
- int i;
-
- i = c - h->cmd_pool;
- clear_bit(i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG));
- h->nr_frees++;
-}
-
-static void cmd_special_free(struct ctlr_info *h, struct CommandList *c)
-{
- union u64bit temp64;
+ if (atomic_dec_and_test(&c->refcount)) {
+ int i;
- temp64.val32.lower = c->ErrDesc.Addr.lower;
- temp64.val32.upper = c->ErrDesc.Addr.upper;
- pci_free_consistent(h->pdev, sizeof(*c->err_info),
- c->err_info, (dma_addr_t) temp64.val);
- pci_free_consistent(h->pdev, sizeof(*c),
- c, (dma_addr_t) (c->busaddr & DIRECT_LOOKUP_MASK));
+ i = c - h->cmd_pool;
+ clear_bit(i, h->cmd_pool_bits);
+ }
}
#ifdef CONFIG_COMPAT
-static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg)
+static int hpsa_ioctl32_passthru(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg)
{
- IOCTL32_Command_struct __user *arg32 =
- (IOCTL32_Command_struct __user *) arg;
+ struct ctlr_info *h = sdev_to_hba(dev);
+ IOCTL32_Command_struct __user *arg32 = arg;
IOCTL_Command_struct arg64;
- IOCTL_Command_struct __user *p = compat_alloc_user_space(sizeof(arg64));
int err;
u32 cp;
- memset(&arg64, 0, sizeof(arg64));
- err = 0;
- err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
- sizeof(arg64.LUN_info));
- err |= copy_from_user(&arg64.Request, &arg32->Request,
- sizeof(arg64.Request));
- err |= copy_from_user(&arg64.error_info, &arg32->error_info,
- sizeof(arg64.error_info));
- err |= get_user(arg64.buf_size, &arg32->buf_size);
- err |= get_user(cp, &arg32->buf);
- arg64.buf = compat_ptr(cp);
- err |= copy_to_user(p, &arg64, sizeof(arg64));
+ if (!arg)
+ return -EINVAL;
- if (err)
+ memset(&arg64, 0, sizeof(arg64));
+ if (copy_from_user(&arg64, arg32, offsetof(IOCTL_Command_struct, buf)))
+ return -EFAULT;
+ if (get_user(cp, &arg32->buf))
return -EFAULT;
+ arg64.buf = compat_ptr(cp);
- err = hpsa_ioctl(dev, CCISS_PASSTHRU, (void *)p);
+ if (atomic_dec_if_positive(&h->passthru_cmds_avail) < 0)
+ return -EAGAIN;
+ err = hpsa_passthru_ioctl(h, &arg64);
+ atomic_inc(&h->passthru_cmds_avail);
if (err)
return err;
- err |= copy_in_user(&arg32->error_info, &p->error_info,
- sizeof(arg32->error_info));
- if (err)
+ if (copy_to_user(&arg32->error_info, &arg64.error_info,
+ sizeof(arg32->error_info)))
return -EFAULT;
- return err;
+ return 0;
}
static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
- int cmd, void *arg)
+ unsigned int cmd, void __user *arg)
{
- BIG_IOCTL32_Command_struct __user *arg32 =
- (BIG_IOCTL32_Command_struct __user *) arg;
+ struct ctlr_info *h = sdev_to_hba(dev);
+ BIG_IOCTL32_Command_struct __user *arg32 = arg;
BIG_IOCTL_Command_struct arg64;
- BIG_IOCTL_Command_struct __user *p =
- compat_alloc_user_space(sizeof(arg64));
int err;
u32 cp;
+ if (!arg)
+ return -EINVAL;
memset(&arg64, 0, sizeof(arg64));
- err = 0;
- err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
- sizeof(arg64.LUN_info));
- err |= copy_from_user(&arg64.Request, &arg32->Request,
- sizeof(arg64.Request));
- err |= copy_from_user(&arg64.error_info, &arg32->error_info,
- sizeof(arg64.error_info));
- err |= get_user(arg64.buf_size, &arg32->buf_size);
- err |= get_user(arg64.malloc_size, &arg32->malloc_size);
- err |= get_user(cp, &arg32->buf);
- arg64.buf = compat_ptr(cp);
- err |= copy_to_user(p, &arg64, sizeof(arg64));
-
- if (err)
+ if (copy_from_user(&arg64, arg32,
+ offsetof(BIG_IOCTL32_Command_struct, buf)))
+ return -EFAULT;
+ if (get_user(cp, &arg32->buf))
return -EFAULT;
+ arg64.buf = compat_ptr(cp);
- err = hpsa_ioctl(dev, CCISS_BIG_PASSTHRU, (void *)p);
+ if (atomic_dec_if_positive(&h->passthru_cmds_avail) < 0)
+ return -EAGAIN;
+ err = hpsa_big_passthru_ioctl(h, &arg64);
+ atomic_inc(&h->passthru_cmds_avail);
if (err)
return err;
- err |= copy_in_user(&arg32->error_info, &p->error_info,
- sizeof(arg32->error_info));
- if (err)
+ if (copy_to_user(&arg32->error_info, &arg64.error_info,
+ sizeof(arg32->error_info)))
return -EFAULT;
- return err;
+ return 0;
}
-static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg)
+static int hpsa_compat_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *arg)
{
switch (cmd) {
case CCISS_GETPCIINFO:
@@ -2519,146 +6387,122 @@ static int hpsa_getdrivver_ioctl(struct ctlr_info *h, void __user *argp)
return 0;
}
-static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
+static int hpsa_passthru_ioctl(struct ctlr_info *h,
+ IOCTL_Command_struct *iocommand)
{
- IOCTL_Command_struct iocommand;
struct CommandList *c;
char *buff = NULL;
- union u64bit temp64;
+ u64 temp64;
+ int rc = 0;
- if (!argp)
- return -EINVAL;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- if (copy_from_user(&iocommand, argp, sizeof(iocommand)))
- return -EFAULT;
- if ((iocommand.buf_size < 1) &&
- (iocommand.Request.Type.Direction != XFER_NONE)) {
+ if ((iocommand->buf_size < 1) &&
+ (iocommand->Request.Type.Direction != XFER_NONE)) {
return -EINVAL;
}
- if (iocommand.buf_size > 0) {
- buff = kmalloc(iocommand.buf_size, GFP_KERNEL);
- if (buff == NULL)
- return -EFAULT;
- if (iocommand.Request.Type.Direction == XFER_WRITE) {
- /* Copy the data into the buffer we created */
- if (copy_from_user(buff, iocommand.buf,
- iocommand.buf_size)) {
- kfree(buff);
- return -EFAULT;
- }
+ if (iocommand->buf_size > 0) {
+ if (iocommand->Request.Type.Direction & XFER_WRITE) {
+ buff = memdup_user(iocommand->buf, iocommand->buf_size);
+ if (IS_ERR(buff))
+ return PTR_ERR(buff);
} else {
- memset(buff, 0, iocommand.buf_size);
+ buff = kzalloc(iocommand->buf_size, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
}
}
- c = cmd_special_alloc(h);
- if (c == NULL) {
- kfree(buff);
- return -ENOMEM;
- }
+ c = cmd_alloc(h);
+
/* Fill in the command type */
c->cmd_type = CMD_IOCTL_PEND;
+ c->scsi_cmd = SCSI_CMD_BUSY;
/* Fill in Command Header */
c->Header.ReplyQueue = 0; /* unused in simple mode */
- if (iocommand.buf_size > 0) { /* buffer to fill */
+ if (iocommand->buf_size > 0) { /* buffer to fill */
c->Header.SGList = 1;
- c->Header.SGTotal = 1;
+ c->Header.SGTotal = cpu_to_le16(1);
} else { /* no buffers to fill */
c->Header.SGList = 0;
- c->Header.SGTotal = 0;
+ c->Header.SGTotal = cpu_to_le16(0);
}
- memcpy(&c->Header.LUN, &iocommand.LUN_info, sizeof(c->Header.LUN));
- /* use the kernel address the cmd block for tag */
- c->Header.Tag.lower = c->busaddr;
+ memcpy(&c->Header.LUN, &iocommand->LUN_info, sizeof(c->Header.LUN));
/* Fill in Request block */
- memcpy(&c->Request, &iocommand.Request,
+ memcpy(&c->Request, &iocommand->Request,
sizeof(c->Request));
/* Fill in the scatter gather information */
- if (iocommand.buf_size > 0) {
- temp64.val = pci_map_single(h->pdev, buff,
- iocommand.buf_size, PCI_DMA_BIDIRECTIONAL);
- c->SG[0].Addr.lower = temp64.val32.lower;
- c->SG[0].Addr.upper = temp64.val32.upper;
- c->SG[0].Len = iocommand.buf_size;
- c->SG[0].Ext = 0; /* we are not chaining*/
- }
- hpsa_scsi_do_simple_cmd_core(h, c);
- if (iocommand.buf_size > 0)
- hpsa_pci_unmap(h->pdev, c, 1, PCI_DMA_BIDIRECTIONAL);
+ if (iocommand->buf_size > 0) {
+ temp64 = dma_map_single(&h->pdev->dev, buff,
+ iocommand->buf_size, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&h->pdev->dev, (dma_addr_t) temp64)) {
+ c->SG[0].Addr = cpu_to_le64(0);
+ c->SG[0].Len = cpu_to_le32(0);
+ rc = -ENOMEM;
+ goto out;
+ }
+ c->SG[0].Addr = cpu_to_le64(temp64);
+ c->SG[0].Len = cpu_to_le32(iocommand->buf_size);
+ c->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* not chaining */
+ }
+ rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE,
+ NO_TIMEOUT);
+ if (iocommand->buf_size > 0)
+ hpsa_pci_unmap(h->pdev, c, 1, DMA_BIDIRECTIONAL);
check_ioctl_unit_attention(h, c);
+ if (rc) {
+ rc = -EIO;
+ goto out;
+ }
/* Copy the error information out */
- memcpy(&iocommand.error_info, c->err_info,
- sizeof(iocommand.error_info));
- if (copy_to_user(argp, &iocommand, sizeof(iocommand))) {
- kfree(buff);
- cmd_special_free(h, c);
- return -EFAULT;
- }
- if (iocommand.Request.Type.Direction == XFER_READ &&
- iocommand.buf_size > 0) {
+ memcpy(&iocommand->error_info, c->err_info,
+ sizeof(iocommand->error_info));
+ if ((iocommand->Request.Type.Direction & XFER_READ) &&
+ iocommand->buf_size > 0) {
/* Copy the data out of the buffer we created */
- if (copy_to_user(iocommand.buf, buff, iocommand.buf_size)) {
- kfree(buff);
- cmd_special_free(h, c);
- return -EFAULT;
+ if (copy_to_user(iocommand->buf, buff, iocommand->buf_size)) {
+ rc = -EFAULT;
+ goto out;
}
}
+out:
+ cmd_free(h, c);
kfree(buff);
- cmd_special_free(h, c);
- return 0;
+ return rc;
}
-static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
+static int hpsa_big_passthru_ioctl(struct ctlr_info *h,
+ BIG_IOCTL_Command_struct *ioc)
{
- BIG_IOCTL_Command_struct *ioc;
struct CommandList *c;
unsigned char **buff = NULL;
int *buff_size = NULL;
- union u64bit temp64;
+ u64 temp64;
BYTE sg_used = 0;
int status = 0;
- int i;
u32 left;
u32 sz;
BYTE __user *data_ptr;
- if (!argp)
- return -EINVAL;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- ioc = (BIG_IOCTL_Command_struct *)
- kmalloc(sizeof(*ioc), GFP_KERNEL);
- if (!ioc) {
- status = -ENOMEM;
- goto cleanup1;
- }
- if (copy_from_user(ioc, argp, sizeof(*ioc))) {
- status = -EFAULT;
- goto cleanup1;
- }
+
if ((ioc->buf_size < 1) &&
- (ioc->Request.Type.Direction != XFER_NONE)) {
- status = -EINVAL;
- goto cleanup1;
- }
+ (ioc->Request.Type.Direction != XFER_NONE))
+ return -EINVAL;
/* Check kmalloc limits using all SGs */
- if (ioc->malloc_size > MAX_KMALLOC_SIZE) {
- status = -EINVAL;
- goto cleanup1;
- }
- if (ioc->buf_size > ioc->malloc_size * MAXSGENTRIES) {
- status = -EINVAL;
- goto cleanup1;
- }
- buff = kzalloc(MAXSGENTRIES * sizeof(char *), GFP_KERNEL);
+ if (ioc->malloc_size > MAX_KMALLOC_SIZE)
+ return -EINVAL;
+ if (ioc->buf_size > ioc->malloc_size * SG_ENTRIES_IN_CMD)
+ return -EINVAL;
+ buff = kcalloc(SG_ENTRIES_IN_CMD, sizeof(char *), GFP_KERNEL);
if (!buff) {
status = -ENOMEM;
goto cleanup1;
}
- buff_size = kmalloc(MAXSGENTRIES * sizeof(int), GFP_KERNEL);
+ buff_size = kmalloc_array(SG_ENTRIES_IN_CMD, sizeof(int), GFP_KERNEL);
if (!buff_size) {
status = -ENOMEM;
goto cleanup1;
@@ -2668,78 +6512,91 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
while (left) {
sz = (left > ioc->malloc_size) ? ioc->malloc_size : left;
buff_size[sg_used] = sz;
- buff[sg_used] = kmalloc(sz, GFP_KERNEL);
- if (buff[sg_used] == NULL) {
- status = -ENOMEM;
- goto cleanup1;
- }
- if (ioc->Request.Type.Direction == XFER_WRITE) {
- if (copy_from_user(buff[sg_used], data_ptr, sz)) {
+
+ if (ioc->Request.Type.Direction & XFER_WRITE) {
+ buff[sg_used] = memdup_user(data_ptr, sz);
+ if (IS_ERR(buff[sg_used])) {
+ status = PTR_ERR(buff[sg_used]);
+ goto cleanup1;
+ }
+ } else {
+ buff[sg_used] = kzalloc(sz, GFP_KERNEL);
+ if (!buff[sg_used]) {
status = -ENOMEM;
goto cleanup1;
}
- } else
- memset(buff[sg_used], 0, sz);
+ }
+
left -= sz;
data_ptr += sz;
sg_used++;
}
- c = cmd_special_alloc(h);
- if (c == NULL) {
- status = -ENOMEM;
- goto cleanup1;
- }
+ c = cmd_alloc(h);
+
c->cmd_type = CMD_IOCTL_PEND;
+ c->scsi_cmd = SCSI_CMD_BUSY;
c->Header.ReplyQueue = 0;
- c->Header.SGList = c->Header.SGTotal = sg_used;
+ c->Header.SGList = (u8) sg_used;
+ c->Header.SGTotal = cpu_to_le16(sg_used);
memcpy(&c->Header.LUN, &ioc->LUN_info, sizeof(c->Header.LUN));
- c->Header.Tag.lower = c->busaddr;
memcpy(&c->Request, &ioc->Request, sizeof(c->Request));
if (ioc->buf_size > 0) {
int i;
for (i = 0; i < sg_used; i++) {
- temp64.val = pci_map_single(h->pdev, buff[i],
- buff_size[i], PCI_DMA_BIDIRECTIONAL);
- c->SG[i].Addr.lower = temp64.val32.lower;
- c->SG[i].Addr.upper = temp64.val32.upper;
- c->SG[i].Len = buff_size[i];
- /* we are not chaining */
- c->SG[i].Ext = 0;
+ temp64 = dma_map_single(&h->pdev->dev, buff[i],
+ buff_size[i], DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&h->pdev->dev,
+ (dma_addr_t) temp64)) {
+ c->SG[i].Addr = cpu_to_le64(0);
+ c->SG[i].Len = cpu_to_le32(0);
+ hpsa_pci_unmap(h->pdev, c, i,
+ DMA_BIDIRECTIONAL);
+ status = -ENOMEM;
+ goto cleanup0;
+ }
+ c->SG[i].Addr = cpu_to_le64(temp64);
+ c->SG[i].Len = cpu_to_le32(buff_size[i]);
+ c->SG[i].Ext = cpu_to_le32(0);
}
+ c->SG[--i].Ext = cpu_to_le32(HPSA_SG_LAST);
}
- hpsa_scsi_do_simple_cmd_core(h, c);
+ status = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE,
+ NO_TIMEOUT);
if (sg_used)
- hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL);
+ hpsa_pci_unmap(h->pdev, c, sg_used, DMA_BIDIRECTIONAL);
check_ioctl_unit_attention(h, c);
+ if (status) {
+ status = -EIO;
+ goto cleanup0;
+ }
+
/* Copy the error information out */
memcpy(&ioc->error_info, c->err_info, sizeof(ioc->error_info));
- if (copy_to_user(argp, ioc, sizeof(*ioc))) {
- cmd_special_free(h, c);
- status = -EFAULT;
- goto cleanup1;
- }
- if (ioc->Request.Type.Direction == XFER_READ && ioc->buf_size > 0) {
+ if ((ioc->Request.Type.Direction & XFER_READ) && ioc->buf_size > 0) {
+ int i;
+
/* Copy the data out of the buffer we created */
BYTE __user *ptr = ioc->buf;
for (i = 0; i < sg_used; i++) {
if (copy_to_user(ptr, buff[i], buff_size[i])) {
- cmd_special_free(h, c);
status = -EFAULT;
- goto cleanup1;
+ goto cleanup0;
}
ptr += buff_size[i];
}
}
- cmd_special_free(h, c);
status = 0;
+cleanup0:
+ cmd_free(h, c);
cleanup1:
if (buff) {
+ int i;
+
for (i = 0; i < sg_used; i++)
kfree(buff[i]);
kfree(buff);
}
kfree(buff_size);
- kfree(ioc);
return status;
}
@@ -2750,15 +6607,15 @@ static void check_ioctl_unit_attention(struct ctlr_info *h,
c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION)
(void) check_for_unit_attention(h, c);
}
+
/*
* ioctl
*/
-static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg)
+static int hpsa_ioctl(struct scsi_device *dev, unsigned int cmd,
+ void __user *argp)
{
- struct ctlr_info *h;
- void __user *argp = (void __user *)arg;
-
- h = sdev_to_hba(dev);
+ struct ctlr_info *h = sdev_to_hba(dev);
+ int rc;
switch (cmd) {
case CCISS_DEREGDISK:
@@ -2770,24 +6627,48 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg)
return hpsa_getpciinfo_ioctl(h, argp);
case CCISS_GETDRIVVER:
return hpsa_getdrivver_ioctl(h, argp);
- case CCISS_PASSTHRU:
- return hpsa_passthru_ioctl(h, argp);
- case CCISS_BIG_PASSTHRU:
- return hpsa_big_passthru_ioctl(h, argp);
+ case CCISS_PASSTHRU: {
+ IOCTL_Command_struct iocommand;
+
+ if (!argp)
+ return -EINVAL;
+ if (copy_from_user(&iocommand, argp, sizeof(iocommand)))
+ return -EFAULT;
+ if (atomic_dec_if_positive(&h->passthru_cmds_avail) < 0)
+ return -EAGAIN;
+ rc = hpsa_passthru_ioctl(h, &iocommand);
+ atomic_inc(&h->passthru_cmds_avail);
+ if (!rc && copy_to_user(argp, &iocommand, sizeof(iocommand)))
+ rc = -EFAULT;
+ return rc;
+ }
+ case CCISS_BIG_PASSTHRU: {
+ BIG_IOCTL_Command_struct ioc;
+ if (!argp)
+ return -EINVAL;
+ if (copy_from_user(&ioc, argp, sizeof(ioc)))
+ return -EFAULT;
+ if (atomic_dec_if_positive(&h->passthru_cmds_avail) < 0)
+ return -EAGAIN;
+ rc = hpsa_big_passthru_ioctl(h, &ioc);
+ atomic_inc(&h->passthru_cmds_avail);
+ if (!rc && copy_to_user(argp, &ioc, sizeof(ioc)))
+ rc = -EFAULT;
+ return rc;
+ }
default:
return -ENOTTY;
}
}
-static int __devinit hpsa_send_host_reset(struct ctlr_info *h,
- unsigned char *scsi3addr, u8 reset_type)
+static void hpsa_send_host_reset(struct ctlr_info *h, u8 reset_type)
{
struct CommandList *c;
c = cmd_alloc(h);
- if (!c)
- return -ENOMEM;
- fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0,
+
+ /* fill_cmd can't fail here, no data buffer to map */
+ (void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0,
RAID_CTLR_LUNID, TYPE_MSG);
c->Request.CDB[1] = reset_type; /* fill_cmd defaults to target reset */
c->waiting = NULL;
@@ -2796,51 +6677,61 @@ static int __devinit hpsa_send_host_reset(struct ctlr_info *h,
* the command either. This is the last command we will send before
* re-initializing everything, so it doesn't matter and won't leak.
*/
- return 0;
+ return;
}
-static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
- void *buff, size_t size, u8 page_code, unsigned char *scsi3addr,
+static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
+ void *buff, size_t size, u16 page_code, unsigned char *scsi3addr,
int cmd_type)
{
- int pci_dir = XFER_NONE;
+ enum dma_data_direction dir = DMA_NONE;
c->cmd_type = CMD_IOCTL_PEND;
+ c->scsi_cmd = SCSI_CMD_BUSY;
c->Header.ReplyQueue = 0;
if (buff != NULL && size > 0) {
c->Header.SGList = 1;
- c->Header.SGTotal = 1;
+ c->Header.SGTotal = cpu_to_le16(1);
} else {
c->Header.SGList = 0;
- c->Header.SGTotal = 0;
+ c->Header.SGTotal = cpu_to_le16(0);
}
- c->Header.Tag.lower = c->busaddr;
memcpy(c->Header.LUN.LunAddrBytes, scsi3addr, 8);
- c->Request.Type.Type = cmd_type;
if (cmd_type == TYPE_CMD) {
switch (cmd) {
case HPSA_INQUIRY:
/* are we trying to read a vital product page */
- if (page_code != 0) {
+ if (page_code & VPD_PAGE) {
c->Request.CDB[1] = 0x01;
- c->Request.CDB[2] = page_code;
+ c->Request.CDB[2] = (page_code & 0xff);
}
c->Request.CDBLen = 6;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
c->Request.Timeout = 0;
c->Request.CDB[0] = HPSA_INQUIRY;
c->Request.CDB[4] = size & 0xFF;
break;
+ case RECEIVE_DIAGNOSTIC:
+ c->Request.CDBLen = 6;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = cmd;
+ c->Request.CDB[1] = 1;
+ c->Request.CDB[2] = 1;
+ c->Request.CDB[3] = (size >> 8) & 0xFF;
+ c->Request.CDB[4] = size & 0xFF;
+ break;
case HPSA_REPORT_LOG:
case HPSA_REPORT_PHYS:
/* Talking to controller so It's a physical command
mode = 00 target = 0. Nothing to write.
*/
c->Request.CDBLen = 12;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
c->Request.Timeout = 0;
c->Request.CDB[0] = cmd;
c->Request.CDB[6] = (size >> 24) & 0xFF; /* MSB */
@@ -2848,45 +6739,145 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
c->Request.CDB[8] = (size >> 8) & 0xFF;
c->Request.CDB[9] = size & 0xFF;
break;
+ case BMIC_SENSE_DIAG_OPTIONS:
+ c->Request.CDBLen = 16;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ /* Spec says this should be BMIC_WRITE */
+ c->Request.CDB[0] = BMIC_READ;
+ c->Request.CDB[6] = BMIC_SENSE_DIAG_OPTIONS;
+ break;
+ case BMIC_SET_DIAG_OPTIONS:
+ c->Request.CDBLen = 16;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type,
+ ATTR_SIMPLE, XFER_WRITE);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = BMIC_WRITE;
+ c->Request.CDB[6] = BMIC_SET_DIAG_OPTIONS;
+ break;
case HPSA_CACHE_FLUSH:
c->Request.CDBLen = 12;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_WRITE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type,
+ ATTR_SIMPLE, XFER_WRITE);
c->Request.Timeout = 0;
c->Request.CDB[0] = BMIC_WRITE;
c->Request.CDB[6] = BMIC_CACHE_FLUSH;
+ c->Request.CDB[7] = (size >> 8) & 0xFF;
+ c->Request.CDB[8] = size & 0xFF;
break;
case TEST_UNIT_READY:
c->Request.CDBLen = 6;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_NONE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE);
+ c->Request.Timeout = 0;
+ break;
+ case HPSA_GET_RAID_MAP:
+ c->Request.CDBLen = 12;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = HPSA_CISS_READ;
+ c->Request.CDB[1] = cmd;
+ c->Request.CDB[6] = (size >> 24) & 0xFF; /* MSB */
+ c->Request.CDB[7] = (size >> 16) & 0xFF;
+ c->Request.CDB[8] = (size >> 8) & 0xFF;
+ c->Request.CDB[9] = size & 0xFF;
+ break;
+ case BMIC_SENSE_CONTROLLER_PARAMETERS:
+ c->Request.CDBLen = 10;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = BMIC_READ;
+ c->Request.CDB[6] = BMIC_SENSE_CONTROLLER_PARAMETERS;
+ c->Request.CDB[7] = (size >> 16) & 0xFF;
+ c->Request.CDB[8] = (size >> 8) & 0xFF;
+ break;
+ case BMIC_IDENTIFY_PHYSICAL_DEVICE:
+ c->Request.CDBLen = 10;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = BMIC_READ;
+ c->Request.CDB[6] = BMIC_IDENTIFY_PHYSICAL_DEVICE;
+ c->Request.CDB[7] = (size >> 16) & 0xFF;
+ c->Request.CDB[8] = (size >> 8) & 0XFF;
+ break;
+ case BMIC_SENSE_SUBSYSTEM_INFORMATION:
+ c->Request.CDBLen = 10;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = BMIC_READ;
+ c->Request.CDB[6] = BMIC_SENSE_SUBSYSTEM_INFORMATION;
+ c->Request.CDB[7] = (size >> 16) & 0xFF;
+ c->Request.CDB[8] = (size >> 8) & 0XFF;
+ break;
+ case BMIC_SENSE_STORAGE_BOX_PARAMS:
+ c->Request.CDBLen = 10;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = BMIC_READ;
+ c->Request.CDB[6] = BMIC_SENSE_STORAGE_BOX_PARAMS;
+ c->Request.CDB[7] = (size >> 16) & 0xFF;
+ c->Request.CDB[8] = (size >> 8) & 0XFF;
+ break;
+ case BMIC_IDENTIFY_CONTROLLER:
+ c->Request.CDBLen = 10;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
c->Request.Timeout = 0;
+ c->Request.CDB[0] = BMIC_READ;
+ c->Request.CDB[1] = 0;
+ c->Request.CDB[2] = 0;
+ c->Request.CDB[3] = 0;
+ c->Request.CDB[4] = 0;
+ c->Request.CDB[5] = 0;
+ c->Request.CDB[6] = BMIC_IDENTIFY_CONTROLLER;
+ c->Request.CDB[7] = (size >> 16) & 0xFF;
+ c->Request.CDB[8] = (size >> 8) & 0XFF;
+ c->Request.CDB[9] = 0;
break;
default:
dev_warn(&h->pdev->dev, "unknown command 0x%c\n", cmd);
BUG();
- return;
}
} else if (cmd_type == TYPE_MSG) {
switch (cmd) {
+ case HPSA_PHYS_TARGET_RESET:
+ c->Request.CDBLen = 16;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE);
+ c->Request.Timeout = 0; /* Don't time out */
+ memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
+ c->Request.CDB[0] = HPSA_RESET;
+ c->Request.CDB[1] = HPSA_TARGET_RESET_TYPE;
+ /* Physical target reset needs no control bytes 4-7*/
+ c->Request.CDB[4] = 0x00;
+ c->Request.CDB[5] = 0x00;
+ c->Request.CDB[6] = 0x00;
+ c->Request.CDB[7] = 0x00;
+ break;
case HPSA_DEVICE_RESET_MSG:
c->Request.CDBLen = 16;
- c->Request.Type.Type = 1; /* It is a MSG not a CMD */
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_NONE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE);
c->Request.Timeout = 0; /* Don't time out */
memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
c->Request.CDB[0] = cmd;
- c->Request.CDB[1] = 0x03; /* Reset target above */
+ c->Request.CDB[1] = HPSA_RESET_TYPE_LUN;
/* If bytes 4-7 are zero, it means reset the */
/* LunID device */
c->Request.CDB[4] = 0x00;
c->Request.CDB[5] = 0x00;
c->Request.CDB[6] = 0x00;
c->Request.CDB[7] = 0x00;
- break;
-
+ break;
default:
dev_warn(&h->pdev->dev, "unknown message type %d\n",
cmd);
@@ -2897,23 +6888,22 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
BUG();
}
- switch (c->Request.Type.Direction) {
+ switch (GET_DIR(c->Request.type_attr_dir)) {
case XFER_READ:
- pci_dir = PCI_DMA_FROMDEVICE;
+ dir = DMA_FROM_DEVICE;
break;
case XFER_WRITE:
- pci_dir = PCI_DMA_TODEVICE;
+ dir = DMA_TO_DEVICE;
break;
case XFER_NONE:
- pci_dir = PCI_DMA_NONE;
+ dir = DMA_NONE;
break;
default:
- pci_dir = PCI_DMA_BIDIRECTIONAL;
+ dir = DMA_BIDIRECTIONAL;
}
-
- hpsa_map_one(h->pdev, c, buff, size, pci_dir);
-
- return;
+ if (hpsa_map_one(h->pdev, c, buff, size, dir))
+ return -1;
+ return 0;
}
/*
@@ -2923,41 +6913,15 @@ static void __iomem *remap_pci_mem(ulong base, ulong size)
{
ulong page_base = ((ulong) base) & PAGE_MASK;
ulong page_offs = ((ulong) base) - page_base;
- void __iomem *page_remapped = ioremap(page_base, page_offs + size);
+ void __iomem *page_remapped = ioremap(page_base,
+ page_offs + size);
return page_remapped ? (page_remapped + page_offs) : NULL;
}
-/* Takes cmds off the submission queue and sends them to the hardware,
- * then puts them on the queue of cmds waiting for completion.
- */
-static void start_io(struct ctlr_info *h)
-{
- struct CommandList *c;
-
- while (!list_empty(&h->reqQ)) {
- c = list_entry(h->reqQ.next, struct CommandList, list);
- /* can't do anything if fifo is full */
- if ((h->access.fifo_full(h))) {
- dev_warn(&h->pdev->dev, "fifo full\n");
- break;
- }
-
- /* Get the first entry from the Request Q */
- removeQ(c);
- h->Qdepth--;
-
- /* Tell the controller execute command */
- h->access.submit_command(h, c);
-
- /* Put job onto the completed Q */
- addQ(&h->cmpQ, c);
- }
-}
-
-static inline unsigned long get_next_completion(struct ctlr_info *h)
+static inline unsigned long get_next_completion(struct ctlr_info *h, u8 q)
{
- return h->access.command_completed(h);
+ return h->access.command_completed(h, q);
}
static inline bool interrupt_pending(struct ctlr_info *h)
@@ -2981,66 +6945,28 @@ static inline int bad_tag(struct ctlr_info *h, u32 tag_index,
return 0;
}
-static inline void finish_cmd(struct CommandList *c, u32 raw_tag)
+static inline void finish_cmd(struct CommandList *c)
{
- removeQ(c);
- if (likely(c->cmd_type == CMD_SCSI))
+ dial_up_lockup_detection_on_fw_flash_complete(c->h, c);
+ if (likely(c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_SCSI
+ || c->cmd_type == CMD_IOACCEL2))
complete_scsi_command(c);
- else if (c->cmd_type == CMD_IOCTL_PEND)
+ else if (c->cmd_type == CMD_IOCTL_PEND || c->cmd_type == IOACCEL2_TMF)
complete(c->waiting);
}
-static inline u32 hpsa_tag_contains_index(u32 tag)
-{
- return tag & DIRECT_LOOKUP_BIT;
-}
-
-static inline u32 hpsa_tag_to_index(u32 tag)
-{
- return tag >> DIRECT_LOOKUP_SHIFT;
-}
-
-
-static inline u32 hpsa_tag_discard_error_bits(struct ctlr_info *h, u32 tag)
-{
-#define HPSA_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1)
-#define HPSA_SIMPLE_ERROR_BITS 0x03
- if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
- return tag & ~HPSA_SIMPLE_ERROR_BITS;
- return tag & ~HPSA_PERF_ERROR_BITS;
-}
-
/* process completion of an indexed ("direct lookup") command */
-static inline u32 process_indexed_cmd(struct ctlr_info *h,
+static inline void process_indexed_cmd(struct ctlr_info *h,
u32 raw_tag)
{
u32 tag_index;
struct CommandList *c;
- tag_index = hpsa_tag_to_index(raw_tag);
- if (bad_tag(h, tag_index, raw_tag))
- return next_command(h);
- c = h->cmd_pool + tag_index;
- finish_cmd(c, raw_tag);
- return next_command(h);
-}
-
-/* process completion of a non-indexed command */
-static inline u32 process_nonindexed_cmd(struct ctlr_info *h,
- u32 raw_tag)
-{
- u32 tag;
- struct CommandList *c = NULL;
-
- tag = hpsa_tag_discard_error_bits(h, raw_tag);
- list_for_each_entry(c, &h->cmpQ, list) {
- if ((c->busaddr & 0xFFFFFFE0) == (tag & 0xFFFFFFE0)) {
- finish_cmd(c, raw_tag);
- return next_command(h);
- }
+ tag_index = raw_tag >> DIRECT_LOOKUP_SHIFT;
+ if (!bad_tag(h, tag_index, raw_tag)) {
+ c = h->cmd_pool + tag_index;
+ finish_cmd(c);
}
- bad_tag(h, h->nr_cmds + 1, raw_tag);
- return next_command(h);
}
/* Some controllers, like p400, will give us one interrupt
@@ -3062,10 +6988,20 @@ static int ignore_bogus_interrupt(struct ctlr_info *h)
return 1;
}
-static irqreturn_t hpsa_intx_discard_completions(int irq, void *dev_id)
+/*
+ * Convert &h->q[x] (passed to interrupt handlers) back to h.
+ * Relies on (h-q[x] == x) being true for x such that
+ * 0 <= x < MAX_REPLY_QUEUES.
+ */
+static struct ctlr_info *queue_to_hba(u8 *queue)
{
- struct ctlr_info *h = dev_id;
- unsigned long flags;
+ return container_of((queue - *queue), struct ctlr_info, q[0]);
+}
+
+static irqreturn_t hpsa_intx_discard_completions(int irq, void *queue)
+{
+ struct ctlr_info *h = queue_to_hba(queue);
+ u8 q = *(u8 *) queue;
u32 raw_tag;
if (ignore_bogus_interrupt(h))
@@ -3073,70 +7009,62 @@ static irqreturn_t hpsa_intx_discard_completions(int irq, void *dev_id)
if (interrupt_not_for_us(h))
return IRQ_NONE;
- spin_lock_irqsave(&h->lock, flags);
+ h->last_intr_timestamp = get_jiffies_64();
while (interrupt_pending(h)) {
- raw_tag = get_next_completion(h);
+ raw_tag = get_next_completion(h, q);
while (raw_tag != FIFO_EMPTY)
- raw_tag = next_command(h);
+ raw_tag = next_command(h, q);
}
- spin_unlock_irqrestore(&h->lock, flags);
return IRQ_HANDLED;
}
-static irqreturn_t hpsa_msix_discard_completions(int irq, void *dev_id)
+static irqreturn_t hpsa_msix_discard_completions(int irq, void *queue)
{
- struct ctlr_info *h = dev_id;
- unsigned long flags;
+ struct ctlr_info *h = queue_to_hba(queue);
u32 raw_tag;
+ u8 q = *(u8 *) queue;
if (ignore_bogus_interrupt(h))
return IRQ_NONE;
- spin_lock_irqsave(&h->lock, flags);
- raw_tag = get_next_completion(h);
+ h->last_intr_timestamp = get_jiffies_64();
+ raw_tag = get_next_completion(h, q);
while (raw_tag != FIFO_EMPTY)
- raw_tag = next_command(h);
- spin_unlock_irqrestore(&h->lock, flags);
+ raw_tag = next_command(h, q);
return IRQ_HANDLED;
}
-static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id)
+static irqreturn_t do_hpsa_intr_intx(int irq, void *queue)
{
- struct ctlr_info *h = dev_id;
- unsigned long flags;
+ struct ctlr_info *h = queue_to_hba((u8 *) queue);
u32 raw_tag;
+ u8 q = *(u8 *) queue;
if (interrupt_not_for_us(h))
return IRQ_NONE;
- spin_lock_irqsave(&h->lock, flags);
+ h->last_intr_timestamp = get_jiffies_64();
while (interrupt_pending(h)) {
- raw_tag = get_next_completion(h);
+ raw_tag = get_next_completion(h, q);
while (raw_tag != FIFO_EMPTY) {
- if (hpsa_tag_contains_index(raw_tag))
- raw_tag = process_indexed_cmd(h, raw_tag);
- else
- raw_tag = process_nonindexed_cmd(h, raw_tag);
+ process_indexed_cmd(h, raw_tag);
+ raw_tag = next_command(h, q);
}
}
- spin_unlock_irqrestore(&h->lock, flags);
return IRQ_HANDLED;
}
-static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id)
+static irqreturn_t do_hpsa_intr_msi(int irq, void *queue)
{
- struct ctlr_info *h = dev_id;
- unsigned long flags;
+ struct ctlr_info *h = queue_to_hba(queue);
u32 raw_tag;
+ u8 q = *(u8 *) queue;
- spin_lock_irqsave(&h->lock, flags);
- raw_tag = get_next_completion(h);
+ h->last_intr_timestamp = get_jiffies_64();
+ raw_tag = get_next_completion(h, q);
while (raw_tag != FIFO_EMPTY) {
- if (hpsa_tag_contains_index(raw_tag))
- raw_tag = process_indexed_cmd(h, raw_tag);
- else
- raw_tag = process_nonindexed_cmd(h, raw_tag);
+ process_indexed_cmd(h, raw_tag);
+ raw_tag = next_command(h, q);
}
- spin_unlock_irqrestore(&h->lock, flags);
return IRQ_HANDLED;
}
@@ -3144,8 +7072,8 @@ static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id)
* in simple mode, not performant mode due to the tag lookup.
* We only ever use this immediately after a controller reset.
*/
-static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
- unsigned char type)
+static int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
+ unsigned char type)
{
struct Command {
struct CommandListHeader CommandHeader;
@@ -3156,7 +7084,8 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
static const size_t cmd_sz = sizeof(*cmd) +
sizeof(cmd->ErrorDescriptor);
dma_addr_t paddr64;
- uint32_t paddr32, tag;
+ __le32 paddr32;
+ u32 tag;
void __iomem *vaddr;
int i, err;
@@ -3168,13 +7097,13 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
* CCISS commands, so they must be allocated from the lower 4GiB of
* memory.
*/
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (err) {
iounmap(vaddr);
- return -ENOMEM;
+ return err;
}
- cmd = pci_alloc_consistent(pdev, cmd_sz, &paddr64);
+ cmd = dma_alloc_coherent(&pdev->dev, cmd_sz, &paddr64, GFP_KERNEL);
if (cmd == NULL) {
iounmap(vaddr);
return -ENOMEM;
@@ -3184,32 +7113,30 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
* although there's no guarantee, we assume that the address is at
* least 4-byte aligned (most likely, it's page-aligned).
*/
- paddr32 = paddr64;
+ paddr32 = cpu_to_le32(paddr64);
cmd->CommandHeader.ReplyQueue = 0;
cmd->CommandHeader.SGList = 0;
- cmd->CommandHeader.SGTotal = 0;
- cmd->CommandHeader.Tag.lower = paddr32;
- cmd->CommandHeader.Tag.upper = 0;
+ cmd->CommandHeader.SGTotal = cpu_to_le16(0);
+ cmd->CommandHeader.tag = cpu_to_le64(paddr64);
memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8);
cmd->Request.CDBLen = 16;
- cmd->Request.Type.Type = TYPE_MSG;
- cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE;
- cmd->Request.Type.Direction = XFER_NONE;
+ cmd->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_MSG, ATTR_HEADOFQUEUE, XFER_NONE);
cmd->Request.Timeout = 0; /* Don't time out */
cmd->Request.CDB[0] = opcode;
cmd->Request.CDB[1] = type;
memset(&cmd->Request.CDB[2], 0, 14); /* rest of the CDB is reserved */
- cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(*cmd);
- cmd->ErrorDescriptor.Addr.upper = 0;
- cmd->ErrorDescriptor.Len = sizeof(struct ErrorInfo);
+ cmd->ErrorDescriptor.Addr =
+ cpu_to_le64((le32_to_cpu(paddr32) + sizeof(*cmd)));
+ cmd->ErrorDescriptor.Len = cpu_to_le32(sizeof(struct ErrorInfo));
- writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET);
+ writel(le32_to_cpu(paddr32), vaddr + SA5_REQUEST_PORT_OFFSET);
for (i = 0; i < HPSA_MSG_SEND_RETRY_LIMIT; i++) {
tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
- if ((tag & ~HPSA_SIMPLE_ERROR_BITS) == paddr32)
+ if ((tag & ~HPSA_SIMPLE_ERROR_BITS) == paddr64)
break;
msleep(HPSA_MSG_SEND_RETRY_INTERVAL_MSECS);
}
@@ -3225,7 +7152,7 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
return -ETIMEDOUT;
}
- pci_free_consistent(pdev, cmd_sz, cmd, paddr64);
+ dma_free_coherent(&pdev->dev, cmd_sz, cmd, paddr64);
if (tag & HPSA_ERROR_BIT) {
dev_err(&pdev->dev, "controller message %02x:%02x failed\n",
@@ -3241,10 +7168,8 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
#define hpsa_noop(p) hpsa_message(p, 3, 0)
static int hpsa_controller_hard_reset(struct pci_dev *pdev,
- void * __iomem vaddr, u32 use_doorbell)
+ void __iomem *vaddr, u32 use_doorbell)
{
- u16 pmcsr;
- int pos;
if (use_doorbell) {
/* For everything after the P600, the PCI power state method
@@ -3253,6 +7178,13 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
*/
dev_info(&pdev->dev, "using doorbell to reset controller\n");
writel(use_doorbell, vaddr + SA5_DOORBELL);
+
+ /* PMC hardware guys tell us we need a 10 second delay after
+ * doorbell reset and before any attempt to talk to the board
+ * at all to ensure that this actually works and doesn't fall
+ * over in some weird corner cases.
+ */
+ msleep(10000);
} else { /* Try to do it the PCI power state way */
/* Quoting from the Open CISS Specification: "The Power
@@ -3263,38 +7195,38 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
* this causes a secondary PCI reset which will reset the
* controller." */
- pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pos == 0) {
- dev_err(&pdev->dev,
- "hpsa_reset_controller: "
- "PCI PM not supported\n");
- return -ENODEV;
- }
+ int rc = 0;
+
dev_info(&pdev->dev, "using PCI PM to reset controller\n");
+
/* enter the D3hot power management state */
- pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pmcsr |= PCI_D3hot;
- pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+ rc = pci_set_power_state(pdev, PCI_D3hot);
+ if (rc)
+ return rc;
msleep(500);
/* enter the D0 power management state */
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pmcsr |= PCI_D0;
- pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+ rc = pci_set_power_state(pdev, PCI_D0);
+ if (rc)
+ return rc;
+
+ /*
+ * The P600 requires a small delay when changing states.
+ * Otherwise we may think the board did not reset and we bail.
+ * This for kdump only and is particular to the P600.
+ */
+ msleep(500);
}
return 0;
}
-static __devinit void init_driver_version(char *driver_version, int len)
+static void init_driver_version(char *driver_version, int len)
{
- memset(driver_version, 0, len);
- strncpy(driver_version, "hpsa " HPSA_DRIVER_VERSION, len - 1);
+ strscpy_pad(driver_version, HPSA " " HPSA_DRIVER_VERSION, len);
}
-static __devinit int write_driver_ver_to_cfgtable(
- struct CfgTable __iomem *cfgtable)
+static int write_driver_ver_to_cfgtable(struct CfgTable __iomem *cfgtable)
{
char *driver_version;
int i, size = sizeof(cfgtable->driver_version);
@@ -3310,8 +7242,8 @@ static __devinit int write_driver_ver_to_cfgtable(
return 0;
}
-static __devinit void read_driver_ver_from_cfgtable(
- struct CfgTable __iomem *cfgtable, unsigned char *driver_ver)
+static void read_driver_ver_from_cfgtable(struct CfgTable __iomem *cfgtable,
+ unsigned char *driver_ver)
{
int i;
@@ -3319,14 +7251,13 @@ static __devinit void read_driver_ver_from_cfgtable(
driver_ver[i] = readb(&cfgtable->driver_version[i]);
}
-static __devinit int controller_reset_failed(
- struct CfgTable __iomem *cfgtable)
+static int controller_reset_failed(struct CfgTable __iomem *cfgtable)
{
char *driver_ver, *old_driver_ver;
int rc, size = sizeof(cfgtable->driver_version);
- old_driver_ver = kmalloc(2 * size, GFP_KERNEL);
+ old_driver_ver = kmalloc_array(2, size, GFP_KERNEL);
if (!old_driver_ver)
return -ENOMEM;
driver_ver = old_driver_ver + size;
@@ -3343,7 +7274,7 @@ static __devinit int controller_reset_failed(
/* This does a hard reset of the controller using PCI power management
* states or the using the doorbell register.
*/
-static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
+static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev, u32 board_id)
{
u64 cfg_offset;
u32 cfg_base_addr;
@@ -3354,7 +7285,6 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
int rc;
struct CfgTable __iomem *cfgtable;
u32 use_doorbell;
- u32 board_id;
u16 command_register;
/* For controllers as old as the P600, this is very nearly
@@ -3370,9 +7300,8 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
* using the doorbell register.
*/
- rc = hpsa_lookup_board_id(pdev, &board_id);
- if (rc < 0 || !ctlr_is_resettable(board_id)) {
- dev_warn(&pdev->dev, "Not resetting device.\n");
+ if (!ctlr_is_resettable(board_id)) {
+ dev_warn(&pdev->dev, "Controller not resettable\n");
return -ENODEV;
}
@@ -3382,10 +7311,6 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
/* Save the PCI command register */
pci_read_config_word(pdev, 4, &command_register);
- /* Turn the board off. This is so that later pci_restore_state()
- * won't turn the board on before the rest of config space is ready.
- */
- pci_disable_device(pdev);
pci_save_state(pdev);
/* find the first memory BAR, so we can find the cfg table */
@@ -3409,7 +7334,7 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
}
rc = write_driver_ver_to_cfgtable(cfgtable);
if (rc)
- goto unmap_vaddr;
+ goto unmap_cfgtable;
/* If reset via doorbell register is supported, use that.
* There are two such methods. Favor the newest method.
@@ -3421,10 +7346,8 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
} else {
use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
if (use_doorbell) {
- dev_warn(&pdev->dev, "Controller claims that "
- "'Bit 2 doorbell reset' is "
- "supported, but not 'bit 5 doorbell reset'. "
- "Firmware update is recommended.\n");
+ dev_warn(&pdev->dev,
+ "Soft reset not supported. Firmware update is required.\n");
rc = -ENOTSUPP; /* try soft reset */
goto unmap_cfgtable;
}
@@ -3435,32 +7358,16 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
goto unmap_cfgtable;
pci_restore_state(pdev);
- rc = pci_enable_device(pdev);
- if (rc) {
- dev_warn(&pdev->dev, "failed to enable device.\n");
- goto unmap_cfgtable;
- }
pci_write_config_word(pdev, 4, command_register);
/* Some devices (notably the HP Smart Array 5i Controller)
need a little pause here */
msleep(HPSA_POST_RESET_PAUSE_MSECS);
- /* Wait for board to become not ready, then ready. */
- dev_info(&pdev->dev, "Waiting for board to reset.\n");
- rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY);
- if (rc) {
- dev_warn(&pdev->dev,
- "failed waiting for board to reset."
- " Will try soft reset.\n");
- rc = -ENOTSUPP; /* Not expected, but try soft reset later */
- goto unmap_cfgtable;
- }
rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_READY);
if (rc) {
dev_warn(&pdev->dev,
- "failed waiting for board to become ready "
- "after hard reset\n");
+ "Failed waiting for board to become ready after hard reset\n");
goto unmap_cfgtable;
}
@@ -3488,7 +7395,7 @@ unmap_vaddr:
* the io functions.
* This is for debug only.
*/
-static void print_cfg_table(struct device *dev, struct CfgTable *tb)
+static void print_cfg_table(struct device *dev, struct CfgTable __iomem *tb)
{
#ifdef HPSA_DEBUG
int i;
@@ -3511,7 +7418,7 @@ static void print_cfg_table(struct device *dev, struct CfgTable *tb)
readl(&(tb->HostWrite.CoalIntDelay)));
dev_info(dev, " Coalesce Interrupt Count = 0x%x\n",
readl(&(tb->HostWrite.CoalIntCount)));
- dev_info(dev, " Max outstanding commands = 0x%d\n",
+ dev_info(dev, " Max outstanding commands = %d\n",
readl(&(tb->CmdsOutMax)));
dev_info(dev, " Bus Types = 0x%x\n", readl(&(tb->BusTypes)));
for (i = 0; i < 16; i++)
@@ -3549,7 +7456,6 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
dev_warn(&pdev->dev,
"base address is invalid\n");
return -1;
- break;
}
}
if (offset == pci_bar_addr - PCI_BASE_ADDRESS_0)
@@ -3558,57 +7464,67 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
return -1;
}
+static void hpsa_disable_interrupt_mode(struct ctlr_info *h)
+{
+ pci_free_irq_vectors(h->pdev);
+ h->msix_vectors = 0;
+}
+
+static void hpsa_setup_reply_map(struct ctlr_info *h)
+{
+ const struct cpumask *mask;
+ unsigned int queue, cpu;
+
+ for (queue = 0; queue < h->msix_vectors; queue++) {
+ mask = pci_irq_get_affinity(h->pdev, queue);
+ if (!mask)
+ goto fallback;
+
+ for_each_cpu(cpu, mask)
+ h->reply_map[cpu] = queue;
+ }
+ return;
+
+fallback:
+ for_each_possible_cpu(cpu)
+ h->reply_map[cpu] = 0;
+}
+
/* If MSI/MSI-X is supported by the kernel we will try to enable it on
- * controllers that are capable. If not, we use IO-APIC mode.
+ * controllers that are capable. If not, we use legacy INTx mode.
*/
-
-static void __devinit hpsa_interrupt_mode(struct ctlr_info *h)
+static int hpsa_interrupt_mode(struct ctlr_info *h)
{
-#ifdef CONFIG_PCI_MSI
- int err;
- struct msix_entry hpsa_msix_entries[4] = { {0, 0}, {0, 1},
- {0, 2}, {0, 3}
- };
+ unsigned int flags = PCI_IRQ_INTX;
+ int ret;
/* Some boards advertise MSI but don't really support it */
- if ((h->board_id == 0x40700E11) || (h->board_id == 0x40800E11) ||
- (h->board_id == 0x40820E11) || (h->board_id == 0x40830E11))
- goto default_int_mode;
- if (pci_find_capability(h->pdev, PCI_CAP_ID_MSIX)) {
- dev_info(&h->pdev->dev, "MSIX\n");
- err = pci_enable_msix(h->pdev, hpsa_msix_entries, 4);
- if (!err) {
- h->intr[0] = hpsa_msix_entries[0].vector;
- h->intr[1] = hpsa_msix_entries[1].vector;
- h->intr[2] = hpsa_msix_entries[2].vector;
- h->intr[3] = hpsa_msix_entries[3].vector;
- h->msix_vector = 1;
- return;
- }
- if (err > 0) {
- dev_warn(&h->pdev->dev, "only %d MSI-X vectors "
- "available\n", err);
- goto default_int_mode;
- } else {
- dev_warn(&h->pdev->dev, "MSI-X init failed %d\n",
- err);
- goto default_int_mode;
+ switch (h->board_id) {
+ case 0x40700E11:
+ case 0x40800E11:
+ case 0x40820E11:
+ case 0x40830E11:
+ break;
+ default:
+ ret = pci_alloc_irq_vectors(h->pdev, 1, MAX_REPLY_QUEUES,
+ PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
+ if (ret > 0) {
+ h->msix_vectors = ret;
+ return 0;
}
+
+ flags |= PCI_IRQ_MSI;
+ break;
}
- if (pci_find_capability(h->pdev, PCI_CAP_ID_MSI)) {
- dev_info(&h->pdev->dev, "MSI\n");
- if (!pci_enable_msi(h->pdev))
- h->msi_vector = 1;
- else
- dev_warn(&h->pdev->dev, "MSI init failed\n");
- }
-default_int_mode:
-#endif /* CONFIG_PCI_MSI */
- /* if we get here we're going to use the default interrupt mode */
- h->intr[h->intr_mode] = h->pdev->irq;
+
+ ret = pci_alloc_irq_vectors(h->pdev, 1, 1, flags);
+ if (ret < 0)
+ return ret;
+ return 0;
}
-static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id)
+static int hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id,
+ bool *legacy_board)
{
int i;
u32 subsystem_vendor_id, subsystem_device_id;
@@ -3618,30 +7534,29 @@ static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id)
*board_id = ((subsystem_device_id << 16) & 0xffff0000) |
subsystem_vendor_id;
+ if (legacy_board)
+ *legacy_board = false;
for (i = 0; i < ARRAY_SIZE(products); i++)
- if (*board_id == products[i].board_id)
+ if (*board_id == products[i].board_id) {
+ if (products[i].access != &SA5A_access &&
+ products[i].access != &SA5B_access)
+ return i;
+ dev_warn(&pdev->dev,
+ "legacy board ID: 0x%08x\n",
+ *board_id);
+ if (legacy_board)
+ *legacy_board = true;
return i;
+ }
- if ((subsystem_vendor_id != PCI_VENDOR_ID_HP &&
- subsystem_vendor_id != PCI_VENDOR_ID_COMPAQ) ||
- !hpsa_allow_any) {
- dev_warn(&pdev->dev, "unrecognized board ID: "
- "0x%08x, ignoring.\n", *board_id);
- return -ENODEV;
- }
+ dev_warn(&pdev->dev, "unrecognized board ID: 0x%08x\n", *board_id);
+ if (legacy_board)
+ *legacy_board = true;
return ARRAY_SIZE(products) - 1; /* generic unknown smart array */
}
-static inline bool hpsa_board_disabled(struct pci_dev *pdev)
-{
- u16 command;
-
- (void) pci_read_config_word(pdev, PCI_COMMAND, &command);
- return ((command & PCI_COMMAND_MEMORY) == 0);
-}
-
-static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
- unsigned long *memory_bar)
+static int hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
+ unsigned long *memory_bar)
{
int i;
@@ -3657,8 +7572,8 @@ static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
return -ENODEV;
}
-static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
- void __iomem *vaddr, int wait_for_ready)
+static int hpsa_wait_for_board_state(struct pci_dev *pdev, void __iomem *vaddr,
+ int wait_for_ready)
{
int i, iterations;
u32 scratchpad;
@@ -3682,9 +7597,9 @@ static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
return -ENODEV;
}
-static int __devinit hpsa_find_cfg_addrs(struct pci_dev *pdev,
- void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index,
- u64 *cfg_offset)
+static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
+ u32 *cfg_base_addr, u64 *cfg_base_addr_index,
+ u64 *cfg_offset)
{
*cfg_base_addr = readl(vaddr + SA5_CTCFG_OFFSET);
*cfg_offset = readl(vaddr + SA5_CTMEM_OFFSET);
@@ -3697,7 +7612,22 @@ static int __devinit hpsa_find_cfg_addrs(struct pci_dev *pdev,
return 0;
}
-static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)
+static void hpsa_free_cfgtables(struct ctlr_info *h)
+{
+ if (h->transtable) {
+ iounmap(h->transtable);
+ h->transtable = NULL;
+ }
+ if (h->cfgtable) {
+ iounmap(h->cfgtable);
+ h->cfgtable = NULL;
+ }
+}
+
+/* Find and map CISS config table and transfer table
+ * several items must be unmapped (freed) later
+ */
+static int hpsa_find_cfgtables(struct ctlr_info *h)
{
u64 cfg_offset;
u32 cfg_base_addr;
@@ -3711,8 +7641,10 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)
return rc;
h->cfgtable = remap_pci_mem(pci_resource_start(h->pdev,
cfg_base_addr_index) + cfg_offset, sizeof(*h->cfgtable));
- if (!h->cfgtable)
+ if (!h->cfgtable) {
+ dev_err(&h->pdev->dev, "Failed mapping cfgtable\n");
return -ENOMEM;
+ }
rc = write_driver_ver_to_cfgtable(h->cfgtable);
if (rc)
return rc;
@@ -3721,74 +7653,99 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)
h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
cfg_base_addr_index)+cfg_offset+trans_offset,
sizeof(*h->transtable));
- if (!h->transtable)
+ if (!h->transtable) {
+ dev_err(&h->pdev->dev, "Failed mapping transfer table\n");
+ hpsa_free_cfgtables(h);
return -ENOMEM;
+ }
return 0;
}
-static void __devinit hpsa_get_max_perf_mode_cmds(struct ctlr_info *h)
+static void hpsa_get_max_perf_mode_cmds(struct ctlr_info *h)
{
- h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+#define MIN_MAX_COMMANDS 16
+ BUILD_BUG_ON(MIN_MAX_COMMANDS <= HPSA_NRESERVED_CMDS);
+
+ h->max_commands = readl(&h->cfgtable->MaxPerformantModeCommands);
/* Limit commands in memory limited kdump scenario. */
if (reset_devices && h->max_commands > 32)
h->max_commands = 32;
- if (h->max_commands < 16) {
- dev_warn(&h->pdev->dev, "Controller reports "
- "max supported commands of %d, an obvious lie. "
- "Using 16. Ensure that firmware is up to date.\n",
- h->max_commands);
- h->max_commands = 16;
+ if (h->max_commands < MIN_MAX_COMMANDS) {
+ dev_warn(&h->pdev->dev,
+ "Controller reports max supported commands of %d Using %d instead. Ensure that firmware is up to date.\n",
+ h->max_commands,
+ MIN_MAX_COMMANDS);
+ h->max_commands = MIN_MAX_COMMANDS;
}
}
+/* If the controller reports that the total max sg entries is greater than 512,
+ * then we know that chained SG blocks work. (Original smart arrays did not
+ * support chained SG blocks and would return zero for max sg entries.)
+ */
+static int hpsa_supports_chained_sg_blocks(struct ctlr_info *h)
+{
+ return h->maxsgentries > 512;
+}
+
/* Interrogate the hardware for some limits:
* max commands, max SG elements without chaining, and with chaining,
* SG chain block size, etc.
*/
-static void __devinit hpsa_find_board_params(struct ctlr_info *h)
+static void hpsa_find_board_params(struct ctlr_info *h)
{
hpsa_get_max_perf_mode_cmds(h);
- h->nr_cmds = h->max_commands - 4; /* Allow room for some ioctls */
+ h->nr_cmds = h->max_commands;
h->maxsgentries = readl(&(h->cfgtable->MaxScatterGatherElements));
- /*
- * Limit in-command s/g elements to 32 save dma'able memory.
- * Howvever spec says if 0, use 31
- */
- h->max_cmd_sg_entries = 31;
- if (h->maxsgentries > 512) {
+ h->fw_support = readl(&(h->cfgtable->misc_fw_support));
+ if (hpsa_supports_chained_sg_blocks(h)) {
+ /* Limit in-command s/g elements to 32 save dma'able memory. */
h->max_cmd_sg_entries = 32;
- h->chainsize = h->maxsgentries - h->max_cmd_sg_entries + 1;
+ h->chainsize = h->maxsgentries - h->max_cmd_sg_entries;
h->maxsgentries--; /* save one for chain pointer */
} else {
+ /*
+ * Original smart arrays supported at most 31 s/g entries
+ * embedded inline in the command (trying to use more
+ * would lock up the controller)
+ */
+ h->max_cmd_sg_entries = 31;
h->maxsgentries = 31; /* default to traditional values */
h->chainsize = 0;
}
+
+ /* Find out what task management functions are supported and cache */
+ h->TMFSupportFlags = readl(&(h->cfgtable->TMFSupportFlags));
+ if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags))
+ dev_warn(&h->pdev->dev, "Physical aborts not supported\n");
+ if (!(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
+ dev_warn(&h->pdev->dev, "Logical aborts not supported\n");
+ if (!(HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags))
+ dev_warn(&h->pdev->dev, "HP SSD Smart Path aborts not supported\n");
}
static inline bool hpsa_CISS_signature_present(struct ctlr_info *h)
{
- if ((readb(&h->cfgtable->Signature[0]) != 'C') ||
- (readb(&h->cfgtable->Signature[1]) != 'I') ||
- (readb(&h->cfgtable->Signature[2]) != 'S') ||
- (readb(&h->cfgtable->Signature[3]) != 'S')) {
- dev_warn(&h->pdev->dev, "not a valid CISS config table\n");
+ if (!check_signature(h->cfgtable->Signature, "CISS", 4)) {
+ dev_err(&h->pdev->dev, "not a valid CISS config table\n");
return false;
}
return true;
}
-/* Need to enable prefetch in the SCSI core for 6400 in x86 */
-static inline void hpsa_enable_scsi_prefetch(struct ctlr_info *h)
+static inline void hpsa_set_driver_support_bits(struct ctlr_info *h)
{
-#ifdef CONFIG_X86
- u32 prefetch;
+ u32 driver_support;
- prefetch = readl(&(h->cfgtable->SCSI_Prefetch));
- prefetch |= 0x100;
- writel(prefetch, &(h->cfgtable->SCSI_Prefetch));
+ driver_support = readl(&(h->cfgtable->driver_support));
+ /* Need to enable prefetch in the SCSI core for 6400 in x86 */
+#ifdef CONFIG_X86
+ driver_support |= ENABLE_SCSI_PREFETCH;
#endif
+ driver_support |= ENABLE_UNIT_ATTN;
+ writel(driver_support, &(h->cfgtable->driver_support));
}
/* Disable DMA prefetch for the P600. Otherwise an ASIC bug may result
@@ -3805,7 +7762,27 @@ static inline void hpsa_p600_dma_prefetch_quirk(struct ctlr_info *h)
writel(dma_prefetch, h->vaddr + I2O_DMA1_CFG);
}
-static void __devinit hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
+static int hpsa_wait_for_clear_event_notify_ack(struct ctlr_info *h)
+{
+ int i;
+ u32 doorbell_value;
+ unsigned long flags;
+ /* wait until the clear_event_notify bit 6 is cleared by controller. */
+ for (i = 0; i < MAX_CLEAR_EVENT_WAIT; i++) {
+ spin_lock_irqsave(&h->lock, flags);
+ doorbell_value = readl(h->vaddr + SA5_DOORBELL);
+ spin_unlock_irqrestore(&h->lock, flags);
+ if (!(doorbell_value & DOORBELL_CLEAR_EVENTS))
+ goto done;
+ /* delay and try again */
+ msleep(CLEAR_EVENT_WAIT_INTERVAL);
+ }
+ return -ENODEV;
+done:
+ return 0;
+}
+
+static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
{
int i;
u32 doorbell_value;
@@ -3815,18 +7792,24 @@ static void __devinit hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
* (e.g.: hot replace a failed 144GB drive in a RAID 5 set right
* as we enter this code.)
*/
- for (i = 0; i < MAX_CONFIG_WAIT; i++) {
+ for (i = 0; i < MAX_MODE_CHANGE_WAIT; i++) {
+ if (h->remove_in_progress)
+ goto done;
spin_lock_irqsave(&h->lock, flags);
doorbell_value = readl(h->vaddr + SA5_DOORBELL);
spin_unlock_irqrestore(&h->lock, flags);
if (!(doorbell_value & CFGTBL_ChangeReq))
- break;
+ goto done;
/* delay and try again */
- usleep_range(10000, 20000);
+ msleep(MODE_CHANGE_WAIT_INTERVAL);
}
+ return -ENODEV;
+done:
+ return 0;
}
-static int __devinit hpsa_enter_simple_mode(struct ctlr_info *h)
+/* return -ENODEV or other reason on error, 0 on success */
+static int hpsa_enter_simple_mode(struct ctlr_info *h)
{
u32 trans_support;
@@ -3835,90 +7818,123 @@ static int __devinit hpsa_enter_simple_mode(struct ctlr_info *h)
return -ENOTSUPP;
h->max_commands = readl(&(h->cfgtable->CmdsOutMax));
+
/* Update the field, and then ring the doorbell */
writel(CFGTBL_Trans_Simple, &(h->cfgtable->HostWrite.TransportRequest));
+ writel(0, &h->cfgtable->HostWrite.command_pool_addr_hi);
writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
- hpsa_wait_for_mode_change_ack(h);
+ if (hpsa_wait_for_mode_change_ack(h))
+ goto error;
print_cfg_table(&h->pdev->dev, h->cfgtable);
- if (!(readl(&(h->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) {
- dev_warn(&h->pdev->dev,
- "unable to get board into simple mode\n");
- return -ENODEV;
- }
+ if (!(readl(&(h->cfgtable->TransportActive)) & CFGTBL_Trans_Simple))
+ goto error;
h->transMethod = CFGTBL_Trans_Simple;
return 0;
+error:
+ dev_err(&h->pdev->dev, "failed to enter simple mode\n");
+ return -ENODEV;
+}
+
+/* free items allocated or mapped by hpsa_pci_init */
+static void hpsa_free_pci_init(struct ctlr_info *h)
+{
+ hpsa_free_cfgtables(h); /* pci_init 4 */
+ iounmap(h->vaddr); /* pci_init 3 */
+ h->vaddr = NULL;
+ hpsa_disable_interrupt_mode(h); /* pci_init 2 */
+ /*
+ * call pci_disable_device before pci_release_regions per
+ * Documentation/driver-api/pci/pci.rst
+ */
+ pci_disable_device(h->pdev); /* pci_init 1 */
+ pci_release_regions(h->pdev); /* pci_init 2 */
}
-static int __devinit hpsa_pci_init(struct ctlr_info *h)
+/* several items must be freed later */
+static int hpsa_pci_init(struct ctlr_info *h)
{
int prod_index, err;
+ bool legacy_board;
- prod_index = hpsa_lookup_board_id(h->pdev, &h->board_id);
+ prod_index = hpsa_lookup_board_id(h->pdev, &h->board_id, &legacy_board);
if (prod_index < 0)
- return -ENODEV;
+ return prod_index;
h->product_name = products[prod_index].product_name;
h->access = *(products[prod_index].access);
+ h->legacy_board = legacy_board;
+ pci_disable_link_state(h->pdev, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
- if (hpsa_board_disabled(h->pdev)) {
- dev_warn(&h->pdev->dev, "controller appears to be disabled\n");
- return -ENODEV;
- }
err = pci_enable_device(h->pdev);
if (err) {
- dev_warn(&h->pdev->dev, "unable to enable PCI device\n");
+ dev_err(&h->pdev->dev, "failed to enable PCI device\n");
+ pci_disable_device(h->pdev);
return err;
}
- err = pci_request_regions(h->pdev, "hpsa");
+ err = pci_request_regions(h->pdev, HPSA);
if (err) {
dev_err(&h->pdev->dev,
- "cannot obtain PCI resources, aborting\n");
+ "failed to obtain PCI resources\n");
+ pci_disable_device(h->pdev);
return err;
}
- hpsa_interrupt_mode(h);
+
+ pci_set_master(h->pdev);
+
+ err = hpsa_interrupt_mode(h);
+ if (err)
+ goto clean1;
+
+ /* setup mapping between CPU and reply queue */
+ hpsa_setup_reply_map(h);
+
err = hpsa_pci_find_memory_BAR(h->pdev, &h->paddr);
if (err)
- goto err_out_free_res;
+ goto clean2; /* intmode+region, pci */
h->vaddr = remap_pci_mem(h->paddr, 0x250);
if (!h->vaddr) {
+ dev_err(&h->pdev->dev, "failed to remap PCI mem\n");
err = -ENOMEM;
- goto err_out_free_res;
+ goto clean2; /* intmode+region, pci */
}
err = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
if (err)
- goto err_out_free_res;
+ goto clean3; /* vaddr, intmode+region, pci */
err = hpsa_find_cfgtables(h);
if (err)
- goto err_out_free_res;
+ goto clean3; /* vaddr, intmode+region, pci */
hpsa_find_board_params(h);
if (!hpsa_CISS_signature_present(h)) {
err = -ENODEV;
- goto err_out_free_res;
+ goto clean4; /* cfgtables, vaddr, intmode+region, pci */
}
- hpsa_enable_scsi_prefetch(h);
+ hpsa_set_driver_support_bits(h);
hpsa_p600_dma_prefetch_quirk(h);
err = hpsa_enter_simple_mode(h);
if (err)
- goto err_out_free_res;
+ goto clean4; /* cfgtables, vaddr, intmode+region, pci */
return 0;
-err_out_free_res:
- if (h->transtable)
- iounmap(h->transtable);
- if (h->cfgtable)
- iounmap(h->cfgtable);
- if (h->vaddr)
- iounmap(h->vaddr);
+clean4: /* cfgtables, vaddr, intmode+region, pci */
+ hpsa_free_cfgtables(h);
+clean3: /* vaddr, intmode+region, pci */
+ iounmap(h->vaddr);
+ h->vaddr = NULL;
+clean2: /* intmode+region, pci */
+ hpsa_disable_interrupt_mode(h);
+clean1:
/*
- * Deliberately omit pci_disable_device(): it does something nasty to
- * Smart Array controllers that pci_enable_device does not undo
+ * call pci_disable_device before pci_release_regions per
+ * Documentation/driver-api/pci/pci.rst
*/
+ pci_disable_device(h->pdev);
pci_release_regions(h->pdev);
return err;
}
-static void __devinit hpsa_hba_inquiry(struct ctlr_info *h)
+static void hpsa_hba_inquiry(struct ctlr_info *h)
{
int rc;
@@ -3934,28 +7950,54 @@ static void __devinit hpsa_hba_inquiry(struct ctlr_info *h)
}
}
-static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev)
+static int hpsa_init_reset_devices(struct pci_dev *pdev, u32 board_id)
{
int rc, i;
+ void __iomem *vaddr;
if (!reset_devices)
return 0;
+ /* kdump kernel is loading, we don't know in which state is
+ * the pci interface. The dev->enable_cnt is equal zero
+ * so we call enable+disable, wait a while and switch it on.
+ */
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ dev_warn(&pdev->dev, "Failed to enable PCI device\n");
+ return -ENODEV;
+ }
+ pci_disable_device(pdev);
+ msleep(260); /* a randomly chosen number */
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ dev_warn(&pdev->dev, "failed to enable device.\n");
+ return -ENODEV;
+ }
+
+ pci_set_master(pdev);
+
+ vaddr = pci_ioremap_bar(pdev, 0);
+ if (vaddr == NULL) {
+ rc = -ENOMEM;
+ goto out_disable;
+ }
+ writel(SA5_INTR_OFF, vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ iounmap(vaddr);
+
/* Reset the controller with a PCI power-cycle or via doorbell */
- rc = hpsa_kdump_hard_reset_controller(pdev);
+ rc = hpsa_kdump_hard_reset_controller(pdev, board_id);
/* -ENOTSUPP here means we cannot reset the controller
* but it's already (and still) up and running in
* "performant mode". Or, it might be 640x, which can't reset
* due to concerns about shared bbwc between 6402/6404 pair.
*/
- if (rc == -ENOTSUPP)
- return rc; /* just try to do the kdump anyhow. */
if (rc)
- return -ENODEV;
+ goto out_disable;
/* Now try to get the controller to respond to a no-op */
- dev_warn(&pdev->dev, "Waiting for controller to respond to no-op\n");
+ dev_info(&pdev->dev, "Waiting for controller to respond to no-op\n");
for (i = 0; i < HPSA_POST_RESET_NOOP_RETRIES; i++) {
if (hpsa_noop(pdev) == 0)
break;
@@ -3963,123 +8005,664 @@ static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev)
dev_warn(&pdev->dev, "no-op failed%s\n",
(i < 11 ? "; re-trying" : ""));
}
- return 0;
+
+out_disable:
+
+ pci_disable_device(pdev);
+ return rc;
+}
+
+static void hpsa_free_cmd_pool(struct ctlr_info *h)
+{
+ bitmap_free(h->cmd_pool_bits);
+ h->cmd_pool_bits = NULL;
+ if (h->cmd_pool) {
+ dma_free_coherent(&h->pdev->dev,
+ h->nr_cmds * sizeof(struct CommandList),
+ h->cmd_pool,
+ h->cmd_pool_dhandle);
+ h->cmd_pool = NULL;
+ h->cmd_pool_dhandle = 0;
+ }
+ if (h->errinfo_pool) {
+ dma_free_coherent(&h->pdev->dev,
+ h->nr_cmds * sizeof(struct ErrorInfo),
+ h->errinfo_pool,
+ h->errinfo_pool_dhandle);
+ h->errinfo_pool = NULL;
+ h->errinfo_pool_dhandle = 0;
+ }
}
-static __devinit int hpsa_allocate_cmd_pool(struct ctlr_info *h)
+static int hpsa_alloc_cmd_pool(struct ctlr_info *h)
{
- h->cmd_pool_bits = kzalloc(
- DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) *
- sizeof(unsigned long), GFP_KERNEL);
- h->cmd_pool = pci_alloc_consistent(h->pdev,
+ h->cmd_pool_bits = bitmap_zalloc(h->nr_cmds, GFP_KERNEL);
+ h->cmd_pool = dma_alloc_coherent(&h->pdev->dev,
h->nr_cmds * sizeof(*h->cmd_pool),
- &(h->cmd_pool_dhandle));
- h->errinfo_pool = pci_alloc_consistent(h->pdev,
+ &h->cmd_pool_dhandle, GFP_KERNEL);
+ h->errinfo_pool = dma_alloc_coherent(&h->pdev->dev,
h->nr_cmds * sizeof(*h->errinfo_pool),
- &(h->errinfo_pool_dhandle));
+ &h->errinfo_pool_dhandle, GFP_KERNEL);
if ((h->cmd_pool_bits == NULL)
|| (h->cmd_pool == NULL)
|| (h->errinfo_pool == NULL)) {
dev_err(&h->pdev->dev, "out of memory in %s", __func__);
- return -ENOMEM;
+ goto clean_up;
}
+ hpsa_preinitialize_commands(h);
return 0;
+clean_up:
+ hpsa_free_cmd_pool(h);
+ return -ENOMEM;
}
-static void hpsa_free_cmd_pool(struct ctlr_info *h)
+/* clear affinity hints and free MSI-X, MSI, or legacy INTx vectors */
+static void hpsa_free_irqs(struct ctlr_info *h)
{
- kfree(h->cmd_pool_bits);
- if (h->cmd_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct CommandList),
- h->cmd_pool, h->cmd_pool_dhandle);
- if (h->errinfo_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct ErrorInfo),
- h->errinfo_pool,
- h->errinfo_pool_dhandle);
+ int i;
+ int irq_vector = 0;
+
+ if (hpsa_simple_mode)
+ irq_vector = h->intr_mode;
+
+ if (!h->msix_vectors || h->intr_mode != PERF_MODE_INT) {
+ /* Single reply queue, only one irq to free */
+ free_irq(pci_irq_vector(h->pdev, irq_vector),
+ &h->q[h->intr_mode]);
+ h->q[h->intr_mode] = 0;
+ return;
+ }
+
+ for (i = 0; i < h->msix_vectors; i++) {
+ free_irq(pci_irq_vector(h->pdev, i), &h->q[i]);
+ h->q[i] = 0;
+ }
+ for (; i < MAX_REPLY_QUEUES; i++)
+ h->q[i] = 0;
}
-static int hpsa_request_irq(struct ctlr_info *h,
+/* returns 0 on success; cleans up and returns -Enn on error */
+static int hpsa_request_irqs(struct ctlr_info *h,
irqreturn_t (*msixhandler)(int, void *),
irqreturn_t (*intxhandler)(int, void *))
{
- int rc;
+ int rc, i;
+ int irq_vector = 0;
- if (h->msix_vector || h->msi_vector)
- rc = request_irq(h->intr[h->intr_mode], msixhandler,
- IRQF_DISABLED, h->devname, h);
- else
- rc = request_irq(h->intr[h->intr_mode], intxhandler,
- IRQF_DISABLED, h->devname, h);
+ if (hpsa_simple_mode)
+ irq_vector = h->intr_mode;
+
+ /*
+ * initialize h->q[x] = x so that interrupt handlers know which
+ * queue to process.
+ */
+ for (i = 0; i < MAX_REPLY_QUEUES; i++)
+ h->q[i] = (u8) i;
+
+ if (h->intr_mode == PERF_MODE_INT && h->msix_vectors > 0) {
+ /* If performant mode and MSI-X, use multiple reply queues */
+ for (i = 0; i < h->msix_vectors; i++) {
+ sprintf(h->intrname[i], "%s-msix%d", h->devname, i);
+ rc = request_irq(pci_irq_vector(h->pdev, i), msixhandler,
+ 0, h->intrname[i],
+ &h->q[i]);
+ if (rc) {
+ int j;
+
+ dev_err(&h->pdev->dev,
+ "failed to get irq %d for %s\n",
+ pci_irq_vector(h->pdev, i), h->devname);
+ for (j = 0; j < i; j++) {
+ free_irq(pci_irq_vector(h->pdev, j), &h->q[j]);
+ h->q[j] = 0;
+ }
+ for (; j < MAX_REPLY_QUEUES; j++)
+ h->q[j] = 0;
+ return rc;
+ }
+ }
+ } else {
+ /* Use single reply pool */
+ if (h->msix_vectors > 0 || h->pdev->msi_enabled) {
+ sprintf(h->intrname[0], "%s-msi%s", h->devname,
+ h->msix_vectors ? "x" : "");
+ rc = request_irq(pci_irq_vector(h->pdev, irq_vector),
+ msixhandler, 0,
+ h->intrname[0],
+ &h->q[h->intr_mode]);
+ } else {
+ sprintf(h->intrname[h->intr_mode],
+ "%s-intx", h->devname);
+ rc = request_irq(pci_irq_vector(h->pdev, irq_vector),
+ intxhandler, IRQF_SHARED,
+ h->intrname[0],
+ &h->q[h->intr_mode]);
+ }
+ }
if (rc) {
- dev_err(&h->pdev->dev, "unable to get irq %d for %s\n",
- h->intr[h->intr_mode], h->devname);
+ dev_err(&h->pdev->dev, "failed to get irq %d for %s\n",
+ pci_irq_vector(h->pdev, irq_vector), h->devname);
+ hpsa_free_irqs(h);
return -ENODEV;
}
return 0;
}
-static int __devinit hpsa_kdump_soft_reset(struct ctlr_info *h)
+static int hpsa_kdump_soft_reset(struct ctlr_info *h)
{
- if (hpsa_send_host_reset(h, RAID_CTLR_LUNID,
- HPSA_RESET_TYPE_CONTROLLER)) {
- dev_warn(&h->pdev->dev, "Resetting array controller failed.\n");
- return -EIO;
- }
+ int rc;
+ hpsa_send_host_reset(h, HPSA_RESET_TYPE_CONTROLLER);
dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n");
- if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) {
+ rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY);
+ if (rc) {
dev_warn(&h->pdev->dev, "Soft reset had no effect.\n");
- return -1;
+ return rc;
}
dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n");
- if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) {
+ rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
+ if (rc) {
dev_warn(&h->pdev->dev, "Board failed to become ready "
"after soft reset.\n");
- return -1;
+ return rc;
}
return 0;
}
+static void hpsa_free_reply_queues(struct ctlr_info *h)
+{
+ int i;
+
+ for (i = 0; i < h->nreply_queues; i++) {
+ if (!h->reply_queue[i].head)
+ continue;
+ dma_free_coherent(&h->pdev->dev,
+ h->reply_queue_size,
+ h->reply_queue[i].head,
+ h->reply_queue[i].busaddr);
+ h->reply_queue[i].head = NULL;
+ h->reply_queue[i].busaddr = 0;
+ }
+ h->reply_queue_size = 0;
+}
+
static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
{
- free_irq(h->intr[h->intr_mode], h);
-#ifdef CONFIG_PCI_MSI
- if (h->msix_vector)
- pci_disable_msix(h->pdev);
- else if (h->msi_vector)
- pci_disable_msi(h->pdev);
-#endif /* CONFIG_PCI_MSI */
- hpsa_free_sg_chain_blocks(h);
- hpsa_free_cmd_pool(h);
- kfree(h->blockFetchTable);
- pci_free_consistent(h->pdev, h->reply_pool_size,
- h->reply_pool, h->reply_pool_dhandle);
- if (h->vaddr)
- iounmap(h->vaddr);
- if (h->transtable)
- iounmap(h->transtable);
- if (h->cfgtable)
- iounmap(h->cfgtable);
- pci_release_regions(h->pdev);
+ hpsa_free_performant_mode(h); /* init_one 7 */
+ hpsa_free_sg_chain_blocks(h); /* init_one 6 */
+ hpsa_free_cmd_pool(h); /* init_one 5 */
+ hpsa_free_irqs(h); /* init_one 4 */
+ scsi_host_put(h->scsi_host); /* init_one 3 */
+ h->scsi_host = NULL; /* init_one 3 */
+ hpsa_free_pci_init(h); /* init_one 2_5 */
+ free_percpu(h->lockup_detected); /* init_one 2 */
+ h->lockup_detected = NULL; /* init_one 2 */
+ if (h->resubmit_wq) {
+ destroy_workqueue(h->resubmit_wq); /* init_one 1 */
+ h->resubmit_wq = NULL;
+ }
+ if (h->rescan_ctlr_wq) {
+ destroy_workqueue(h->rescan_ctlr_wq);
+ h->rescan_ctlr_wq = NULL;
+ }
+ if (h->monitor_ctlr_wq) {
+ destroy_workqueue(h->monitor_ctlr_wq);
+ h->monitor_ctlr_wq = NULL;
+ }
+
+ kfree(h); /* init_one 1 */
+}
+
+/* Called when controller lockup detected. */
+static void fail_all_outstanding_cmds(struct ctlr_info *h)
+{
+ int i, refcount;
+ struct CommandList *c;
+ int failcount = 0;
+
+ flush_workqueue(h->resubmit_wq); /* ensure all cmds are fully built */
+ for (i = 0; i < h->nr_cmds; i++) {
+ c = h->cmd_pool + i;
+ refcount = atomic_inc_return(&c->refcount);
+ if (refcount > 1) {
+ c->err_info->CommandStatus = CMD_CTLR_LOCKUP;
+ finish_cmd(c);
+ atomic_dec(&h->commands_outstanding);
+ failcount++;
+ }
+ cmd_free(h, c);
+ }
+ dev_warn(&h->pdev->dev,
+ "failed %d commands in fail_all\n", failcount);
+}
+
+static void set_lockup_detected_for_all_cpus(struct ctlr_info *h, u32 value)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ u32 *lockup_detected;
+ lockup_detected = per_cpu_ptr(h->lockup_detected, cpu);
+ *lockup_detected = value;
+ }
+ wmb(); /* be sure the per-cpu variables are out to memory */
+}
+
+static void controller_lockup_detected(struct ctlr_info *h)
+{
+ unsigned long flags;
+ u32 lockup_detected;
+
+ h->access.set_intr_mask(h, HPSA_INTR_OFF);
+ spin_lock_irqsave(&h->lock, flags);
+ lockup_detected = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
+ if (!lockup_detected) {
+ /* no heartbeat, but controller gave us a zero. */
+ dev_warn(&h->pdev->dev,
+ "lockup detected after %d but scratchpad register is zero\n",
+ h->heartbeat_sample_interval / HZ);
+ lockup_detected = 0xffffffff;
+ }
+ set_lockup_detected_for_all_cpus(h, lockup_detected);
+ spin_unlock_irqrestore(&h->lock, flags);
+ dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x after %d\n",
+ lockup_detected, h->heartbeat_sample_interval / HZ);
+ if (lockup_detected == 0xffff0000) {
+ dev_warn(&h->pdev->dev, "Telling controller to do a CHKPT\n");
+ writel(DOORBELL_GENERATE_CHKPT, h->vaddr + SA5_DOORBELL);
+ }
+ pci_disable_device(h->pdev);
+ fail_all_outstanding_cmds(h);
+}
+
+static int detect_controller_lockup(struct ctlr_info *h)
+{
+ u64 now;
+ u32 heartbeat;
+ unsigned long flags;
+
+ now = get_jiffies_64();
+ /* If we've received an interrupt recently, we're ok. */
+ if (time_after64(h->last_intr_timestamp +
+ (h->heartbeat_sample_interval), now))
+ return false;
+
+ /*
+ * If we've already checked the heartbeat recently, we're ok.
+ * This could happen if someone sends us a signal. We
+ * otherwise don't care about signals in this thread.
+ */
+ if (time_after64(h->last_heartbeat_timestamp +
+ (h->heartbeat_sample_interval), now))
+ return false;
+
+ /* If heartbeat has not changed since we last looked, we're not ok. */
+ spin_lock_irqsave(&h->lock, flags);
+ heartbeat = readl(&h->cfgtable->HeartBeat);
+ spin_unlock_irqrestore(&h->lock, flags);
+ if (h->last_heartbeat == heartbeat) {
+ controller_lockup_detected(h);
+ return true;
+ }
+
+ /* We're ok. */
+ h->last_heartbeat = heartbeat;
+ h->last_heartbeat_timestamp = now;
+ return false;
+}
+
+/*
+ * Set ioaccel status for all ioaccel volumes.
+ *
+ * Called from monitor controller worker (hpsa_event_monitor_worker)
+ *
+ * A Volume (or Volumes that comprise an Array set) may be undergoing a
+ * transformation, so we will be turning off ioaccel for all volumes that
+ * make up the Array.
+ */
+static void hpsa_set_ioaccel_status(struct ctlr_info *h)
+{
+ int rc;
+ int i;
+ u8 ioaccel_status;
+ unsigned char *buf;
+ struct hpsa_scsi_dev_t *device;
+
+ if (!h)
+ return;
+
+ buf = kmalloc(64, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ /*
+ * Run through current device list used during I/O requests.
+ */
+ for (i = 0; i < h->ndevices; i++) {
+ int offload_to_be_enabled = 0;
+ int offload_config = 0;
+
+ device = h->dev[i];
+
+ if (!device)
+ continue;
+ if (!hpsa_vpd_page_supported(h, device->scsi3addr,
+ HPSA_VPD_LV_IOACCEL_STATUS))
+ continue;
+
+ memset(buf, 0, 64);
+
+ rc = hpsa_scsi_do_inquiry(h, device->scsi3addr,
+ VPD_PAGE | HPSA_VPD_LV_IOACCEL_STATUS,
+ buf, 64);
+ if (rc != 0)
+ continue;
+
+ ioaccel_status = buf[IOACCEL_STATUS_BYTE];
+
+ /*
+ * Check if offload is still configured on
+ */
+ offload_config =
+ !!(ioaccel_status & OFFLOAD_CONFIGURED_BIT);
+ /*
+ * If offload is configured on, check to see if ioaccel
+ * needs to be enabled.
+ */
+ if (offload_config)
+ offload_to_be_enabled =
+ !!(ioaccel_status & OFFLOAD_ENABLED_BIT);
+
+ /*
+ * If ioaccel is to be re-enabled, re-enable later during the
+ * scan operation so the driver can get a fresh raidmap
+ * before turning ioaccel back on.
+ */
+ if (offload_to_be_enabled)
+ continue;
+
+ /*
+ * Immediately turn off ioaccel for any volume the
+ * controller tells us to. Some of the reasons could be:
+ * transformation - change to the LVs of an Array.
+ * degraded volume - component failure
+ */
+ hpsa_turn_off_ioaccel_for_device(device);
+ }
+
+ kfree(buf);
+}
+
+static void hpsa_ack_ctlr_events(struct ctlr_info *h)
+{
+ char *event_type;
+
+ if (!(h->fw_support & MISC_FW_EVENT_NOTIFY))
+ return;
+
+ /* Ask the controller to clear the events we're handling. */
+ if ((h->transMethod & (CFGTBL_Trans_io_accel1
+ | CFGTBL_Trans_io_accel2)) &&
+ (h->events & HPSA_EVENT_NOTIFY_ACCEL_IO_PATH_STATE_CHANGE ||
+ h->events & HPSA_EVENT_NOTIFY_ACCEL_IO_PATH_CONFIG_CHANGE)) {
+
+ if (h->events & HPSA_EVENT_NOTIFY_ACCEL_IO_PATH_STATE_CHANGE)
+ event_type = "state change";
+ if (h->events & HPSA_EVENT_NOTIFY_ACCEL_IO_PATH_CONFIG_CHANGE)
+ event_type = "configuration change";
+ /* Stop sending new RAID offload reqs via the IO accelerator */
+ scsi_block_requests(h->scsi_host);
+ hpsa_set_ioaccel_status(h);
+ hpsa_drain_accel_commands(h);
+ /* Set 'accelerator path config change' bit */
+ dev_warn(&h->pdev->dev,
+ "Acknowledging event: 0x%08x (HP SSD Smart Path %s)\n",
+ h->events, event_type);
+ writel(h->events, &(h->cfgtable->clear_event_notify));
+ /* Set the "clear event notify field update" bit 6 */
+ writel(DOORBELL_CLEAR_EVENTS, h->vaddr + SA5_DOORBELL);
+ /* Wait until ctlr clears 'clear event notify field', bit 6 */
+ hpsa_wait_for_clear_event_notify_ack(h);
+ scsi_unblock_requests(h->scsi_host);
+ } else {
+ /* Acknowledge controller notification events. */
+ writel(h->events, &(h->cfgtable->clear_event_notify));
+ writel(DOORBELL_CLEAR_EVENTS, h->vaddr + SA5_DOORBELL);
+ hpsa_wait_for_clear_event_notify_ack(h);
+ }
+ return;
+}
+
+/* Check a register on the controller to see if there are configuration
+ * changes (added/changed/removed logical drives, etc.) which mean that
+ * we should rescan the controller for devices.
+ * Also check flag for driver-initiated rescan.
+ */
+static int hpsa_ctlr_needs_rescan(struct ctlr_info *h)
+{
+ if (h->drv_req_rescan) {
+ h->drv_req_rescan = 0;
+ return 1;
+ }
+
+ if (!(h->fw_support & MISC_FW_EVENT_NOTIFY))
+ return 0;
+
+ h->events = readl(&(h->cfgtable->event_notify));
+ return h->events & RESCAN_REQUIRED_EVENT_BITS;
+}
+
+/*
+ * Check if any of the offline devices have become ready
+ */
+static int hpsa_offline_devices_ready(struct ctlr_info *h)
+{
+ unsigned long flags;
+ struct offline_device_entry *d;
+ struct list_head *this, *tmp;
+
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_for_each_safe(this, tmp, &h->offline_device_list) {
+ d = list_entry(this, struct offline_device_entry,
+ offline_list);
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ if (!hpsa_volume_offline(h, d->scsi3addr)) {
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_del(&d->offline_list);
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ return 1;
+ }
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ }
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ return 0;
+}
+
+static int hpsa_luns_changed(struct ctlr_info *h)
+{
+ int rc = 1; /* assume there are changes */
+ struct ReportLUNdata *logdev = NULL;
+
+ /* if we can't find out if lun data has changed,
+ * assume that it has.
+ */
+
+ if (!h->lastlogicals)
+ return rc;
+
+ logdev = kzalloc(sizeof(*logdev), GFP_KERNEL);
+ if (!logdev)
+ return rc;
+
+ if (hpsa_scsi_do_report_luns(h, 1, logdev, sizeof(*logdev), 0)) {
+ dev_warn(&h->pdev->dev,
+ "report luns failed, can't track lun changes.\n");
+ goto out;
+ }
+ if (memcmp(logdev, h->lastlogicals, sizeof(*logdev))) {
+ dev_info(&h->pdev->dev,
+ "Lun changes detected.\n");
+ memcpy(h->lastlogicals, logdev, sizeof(*logdev));
+ goto out;
+ } else
+ rc = 0; /* no changes detected. */
+out:
+ kfree(logdev);
+ return rc;
+}
+
+static void hpsa_perform_rescan(struct ctlr_info *h)
+{
+ struct Scsi_Host *sh = NULL;
+ unsigned long flags;
+
+ /*
+ * Do the scan after the reset
+ */
+ spin_lock_irqsave(&h->reset_lock, flags);
+ if (h->reset_in_progress) {
+ h->drv_req_rescan = 1;
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&h->reset_lock, flags);
+
+ sh = scsi_host_get(h->scsi_host);
+ if (sh != NULL) {
+ hpsa_scan_start(sh);
+ scsi_host_put(sh);
+ h->drv_req_rescan = 0;
+ }
+}
+
+/*
+ * watch for controller events
+ */
+static void hpsa_event_monitor_worker(struct work_struct *work)
+{
+ struct ctlr_info *h = container_of(to_delayed_work(work),
+ struct ctlr_info, event_monitor_work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&h->lock, flags);
+ if (h->remove_in_progress) {
+ spin_unlock_irqrestore(&h->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ if (hpsa_ctlr_needs_rescan(h)) {
+ hpsa_ack_ctlr_events(h);
+ hpsa_perform_rescan(h);
+ }
+
+ spin_lock_irqsave(&h->lock, flags);
+ if (!h->remove_in_progress)
+ queue_delayed_work(h->monitor_ctlr_wq, &h->event_monitor_work,
+ HPSA_EVENT_MONITOR_INTERVAL);
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static void hpsa_rescan_ctlr_worker(struct work_struct *work)
+{
+ unsigned long flags;
+ struct ctlr_info *h = container_of(to_delayed_work(work),
+ struct ctlr_info, rescan_ctlr_work);
+
+ spin_lock_irqsave(&h->lock, flags);
+ if (h->remove_in_progress) {
+ spin_unlock_irqrestore(&h->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ if (h->drv_req_rescan || hpsa_offline_devices_ready(h)) {
+ hpsa_perform_rescan(h);
+ } else if (h->discovery_polling) {
+ if (hpsa_luns_changed(h)) {
+ dev_info(&h->pdev->dev,
+ "driver discovery polling rescan.\n");
+ hpsa_perform_rescan(h);
+ }
+ }
+ spin_lock_irqsave(&h->lock, flags);
+ if (!h->remove_in_progress)
+ queue_delayed_work(h->rescan_ctlr_wq, &h->rescan_ctlr_work,
+ h->heartbeat_sample_interval);
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static void hpsa_monitor_ctlr_worker(struct work_struct *work)
+{
+ unsigned long flags;
+ struct ctlr_info *h = container_of(to_delayed_work(work),
+ struct ctlr_info, monitor_ctlr_work);
+
+ detect_controller_lockup(h);
+ if (lockup_detected(h))
+ return;
+
+ spin_lock_irqsave(&h->lock, flags);
+ if (!h->remove_in_progress)
+ queue_delayed_work(h->monitor_ctlr_wq, &h->monitor_ctlr_work,
+ h->heartbeat_sample_interval);
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static struct workqueue_struct *hpsa_create_controller_wq(struct ctlr_info *h,
+ char *name)
+{
+ struct workqueue_struct *wq = NULL;
+
+ wq = alloc_ordered_workqueue("%s_%d_hpsa", 0, name, h->ctlr);
+ if (!wq)
+ dev_err(&h->pdev->dev, "failed to create %s workqueue\n", name);
+
+ return wq;
+}
+
+static void hpda_free_ctlr_info(struct ctlr_info *h)
+{
+ kfree(h->reply_map);
kfree(h);
}
-static int __devinit hpsa_init_one(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static struct ctlr_info *hpda_alloc_ctlr_info(void)
{
- int dac, rc;
+ struct ctlr_info *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return NULL;
+
+ h->reply_map = kcalloc(nr_cpu_ids, sizeof(*h->reply_map), GFP_KERNEL);
+ if (!h->reply_map) {
+ kfree(h);
+ return NULL;
+ }
+ return h;
+}
+
+static int hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int rc;
struct ctlr_info *h;
int try_soft_reset = 0;
unsigned long flags;
+ u32 board_id;
if (number_of_controllers == 0)
printk(KERN_INFO DRIVER_NAME "\n");
- rc = hpsa_init_reset_devices(pdev);
+ rc = hpsa_lookup_board_id(pdev, &board_id, NULL);
+ if (rc < 0) {
+ dev_warn(&pdev->dev, "Board ID not found\n");
+ return rc;
+ }
+
+ rc = hpsa_init_reset_devices(pdev, board_id);
if (rc) {
if (rc != -ENOTSUPP)
return rc;
@@ -4098,63 +8681,103 @@ reinit_after_soft_reset:
* the 5 lower bits of the address are used by the hardware. and by
* the driver. See comments in hpsa.h for more info.
*/
-#define COMMANDLIST_ALIGNMENT 32
BUILD_BUG_ON(sizeof(struct CommandList) % COMMANDLIST_ALIGNMENT);
- h = kzalloc(sizeof(*h), GFP_KERNEL);
- if (!h)
+ h = hpda_alloc_ctlr_info();
+ if (!h) {
+ dev_err(&pdev->dev, "Failed to allocate controller head\n");
return -ENOMEM;
+ }
h->pdev = pdev;
- h->busy_initializing = 1;
+
h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
- INIT_LIST_HEAD(&h->cmpQ);
- INIT_LIST_HEAD(&h->reqQ);
+ INIT_LIST_HEAD(&h->offline_device_list);
spin_lock_init(&h->lock);
+ spin_lock_init(&h->offline_device_lock);
spin_lock_init(&h->scan_lock);
+ spin_lock_init(&h->reset_lock);
+ atomic_set(&h->passthru_cmds_avail, HPSA_MAX_CONCURRENT_PASSTHRUS);
+
+ /* Allocate and clear per-cpu variable lockup_detected */
+ h->lockup_detected = alloc_percpu(u32);
+ if (!h->lockup_detected) {
+ dev_err(&h->pdev->dev, "Failed to allocate lockup detector\n");
+ rc = -ENOMEM;
+ goto clean1; /* aer/h */
+ }
+ set_lockup_detected_for_all_cpus(h, 0);
+
rc = hpsa_pci_init(h);
- if (rc != 0)
- goto clean1;
+ if (rc)
+ goto clean2; /* lu, aer/h */
- sprintf(h->devname, "hpsa%d", number_of_controllers);
+ /* relies on h-> settings made by hpsa_pci_init, including
+ * interrupt_mode h->intr */
+ rc = hpsa_scsi_host_alloc(h);
+ if (rc)
+ goto clean2_5; /* pci, lu, aer/h */
+
+ sprintf(h->devname, HPSA "%d", h->scsi_host->host_no);
h->ctlr = number_of_controllers;
number_of_controllers++;
/* configure PCI DMA stuff */
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
- if (rc == 0) {
- dac = 1;
- } else {
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc == 0) {
- dac = 0;
- } else {
+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+ if (rc != 0) {
+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (rc != 0) {
dev_err(&pdev->dev, "no suitable DMA available\n");
- goto clean1;
+ goto clean3; /* shost, pci, lu, aer/h */
}
}
/* make sure the board interrupts are off */
h->access.set_intr_mask(h, HPSA_INTR_OFF);
- if (hpsa_request_irq(h, do_hpsa_intr_msi, do_hpsa_intr_intx))
- goto clean2;
- dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n",
- h->devname, pdev->device,
- h->intr[h->intr_mode], dac ? "" : " not");
- if (hpsa_allocate_cmd_pool(h))
- goto clean4;
- if (hpsa_allocate_sg_chain_blocks(h))
- goto clean4;
+ rc = hpsa_request_irqs(h, do_hpsa_intr_msi, do_hpsa_intr_intx);
+ if (rc)
+ goto clean3; /* shost, pci, lu, aer/h */
+ rc = hpsa_alloc_cmd_pool(h);
+ if (rc)
+ goto clean4; /* irq, shost, pci, lu, aer/h */
+ rc = hpsa_alloc_sg_chain_blocks(h);
+ if (rc)
+ goto clean5; /* cmd, irq, shost, pci, lu, aer/h */
init_waitqueue_head(&h->scan_wait_queue);
+ init_waitqueue_head(&h->event_sync_wait_queue);
+ mutex_init(&h->reset_mutex);
h->scan_finished = 1; /* no scan currently in progress */
+ h->scan_waiting = 0;
pci_set_drvdata(pdev, h);
h->ndevices = 0;
- h->scsi_host = NULL;
+
spin_lock_init(&h->devlock);
- hpsa_put_ctlr_into_performant_mode(h);
+ rc = hpsa_put_ctlr_into_performant_mode(h);
+ if (rc)
+ goto clean6; /* sg, cmd, irq, shost, pci, lu, aer/h */
- /* At this point, the controller is ready to take commands.
+ /* create the resubmit workqueue */
+ h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan");
+ if (!h->rescan_ctlr_wq) {
+ rc = -ENOMEM;
+ goto clean7;
+ }
+
+ h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit");
+ if (!h->resubmit_wq) {
+ rc = -ENOMEM;
+ goto clean7; /* aer/h */
+ }
+
+ h->monitor_ctlr_wq = hpsa_create_controller_wq(h, "monitor");
+ if (!h->monitor_ctlr_wq) {
+ rc = -ENOMEM;
+ goto clean7;
+ }
+
+ /*
+ * At this point, the controller is ready to take commands.
* Now, if reset_devices and the hard reset didn't work, try
* the soft reset and see if that works.
*/
@@ -4170,19 +8793,30 @@ reinit_after_soft_reset:
spin_lock_irqsave(&h->lock, flags);
h->access.set_intr_mask(h, HPSA_INTR_OFF);
spin_unlock_irqrestore(&h->lock, flags);
- free_irq(h->intr[h->intr_mode], h);
- rc = hpsa_request_irq(h, hpsa_msix_discard_completions,
+ hpsa_free_irqs(h);
+ rc = hpsa_request_irqs(h, hpsa_msix_discard_completions,
hpsa_intx_discard_completions);
if (rc) {
- dev_warn(&h->pdev->dev, "Failed to request_irq after "
- "soft reset.\n");
- goto clean4;
+ dev_warn(&h->pdev->dev,
+ "Failed to request_irq after soft reset.\n");
+ /*
+ * cannot goto clean7 or free_irqs will be called
+ * again. Instead, do its work
+ */
+ hpsa_free_performant_mode(h); /* clean7 */
+ hpsa_free_sg_chain_blocks(h); /* clean6 */
+ hpsa_free_cmd_pool(h); /* clean5 */
+ /*
+ * skip hpsa_free_irqs(h) clean4 since that
+ * was just called before request_irqs failed
+ */
+ goto clean3;
}
rc = hpsa_kdump_soft_reset(h);
if (rc)
/* Neither hard nor soft reset worked, we're hosed. */
- goto clean4;
+ goto clean7;
dev_info(&h->pdev->dev, "Board READY.\n");
dev_info(&h->pdev->dev,
@@ -4203,28 +8837,81 @@ reinit_after_soft_reset:
hpsa_undo_allocations_after_kdump_soft_reset(h);
try_soft_reset = 0;
if (rc)
- /* don't go to clean4, we already unallocated */
+ /* don't goto clean, we already unallocated */
return -ENODEV;
goto reinit_after_soft_reset;
}
+ /* Enable Accelerated IO path at driver layer */
+ h->acciopath_status = 1;
+ /* Disable discovery polling.*/
+ h->discovery_polling = 0;
+
+
/* Turn the interrupts on so we can service requests */
h->access.set_intr_mask(h, HPSA_INTR_ON);
hpsa_hba_inquiry(h);
- hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */
- h->busy_initializing = 0;
- return 1;
-clean4:
+ h->lastlogicals = kzalloc(sizeof(*(h->lastlogicals)), GFP_KERNEL);
+ if (!h->lastlogicals)
+ dev_info(&h->pdev->dev,
+ "Can't track change to report lun data\n");
+
+ /* hook into SCSI subsystem */
+ rc = hpsa_scsi_add_host(h);
+ if (rc)
+ goto clean8; /* lastlogicals, perf, sg, cmd, irq, shost, pci, lu, aer/h */
+
+ /* Monitor the controller for firmware lockups */
+ h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
+ INIT_DELAYED_WORK(&h->monitor_ctlr_work, hpsa_monitor_ctlr_worker);
+ schedule_delayed_work(&h->monitor_ctlr_work,
+ h->heartbeat_sample_interval);
+ INIT_DELAYED_WORK(&h->rescan_ctlr_work, hpsa_rescan_ctlr_worker);
+ queue_delayed_work(h->rescan_ctlr_wq, &h->rescan_ctlr_work,
+ h->heartbeat_sample_interval);
+ INIT_DELAYED_WORK(&h->event_monitor_work, hpsa_event_monitor_worker);
+ schedule_delayed_work(&h->event_monitor_work,
+ HPSA_EVENT_MONITOR_INTERVAL);
+ return 0;
+
+clean8: /* lastlogicals, perf, sg, cmd, irq, shost, pci, lu, aer/h */
+ kfree(h->lastlogicals);
+clean7: /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
+ hpsa_free_performant_mode(h);
+ h->access.set_intr_mask(h, HPSA_INTR_OFF);
+clean6: /* sg, cmd, irq, pci, lockup, wq/aer/h */
hpsa_free_sg_chain_blocks(h);
+clean5: /* cmd, irq, shost, pci, lu, aer/h */
hpsa_free_cmd_pool(h);
- free_irq(h->intr[h->intr_mode], h);
-clean2:
-clean1:
- h->busy_initializing = 0;
- kfree(h);
+clean4: /* irq, shost, pci, lu, aer/h */
+ hpsa_free_irqs(h);
+clean3: /* shost, pci, lu, aer/h */
+ scsi_host_put(h->scsi_host);
+ h->scsi_host = NULL;
+clean2_5: /* pci, lu, aer/h */
+ hpsa_free_pci_init(h);
+clean2: /* lu, aer/h */
+ if (h->lockup_detected) {
+ free_percpu(h->lockup_detected);
+ h->lockup_detected = NULL;
+ }
+clean1: /* wq/aer/h */
+ if (h->resubmit_wq) {
+ destroy_workqueue(h->resubmit_wq);
+ h->resubmit_wq = NULL;
+ }
+ if (h->rescan_ctlr_wq) {
+ destroy_workqueue(h->rescan_ctlr_wq);
+ h->rescan_ctlr_wq = NULL;
+ }
+ if (h->monitor_ctlr_wq) {
+ destroy_workqueue(h->monitor_ctlr_wq);
+ h->monitor_ctlr_wq = NULL;
+ }
+ hpda_free_ctlr_info(h);
return rc;
}
@@ -4232,28 +8919,95 @@ static void hpsa_flush_cache(struct ctlr_info *h)
{
char *flush_buf;
struct CommandList *c;
+ int rc;
+ if (unlikely(lockup_detected(h)))
+ return;
flush_buf = kzalloc(4, GFP_KERNEL);
if (!flush_buf)
return;
- c = cmd_special_alloc(h);
- if (!c) {
- dev_warn(&h->pdev->dev, "cmd_special_alloc returned NULL!\n");
- goto out_of_memory;
+ c = cmd_alloc(h);
+
+ if (fill_cmd(c, HPSA_CACHE_FLUSH, h, flush_buf, 4, 0,
+ RAID_CTLR_LUNID, TYPE_CMD)) {
+ goto out;
}
- fill_cmd(c, HPSA_CACHE_FLUSH, h, flush_buf, 4, 0,
- RAID_CTLR_LUNID, TYPE_CMD);
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_TODEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_TO_DEVICE,
+ DEFAULT_TIMEOUT);
+ if (rc)
+ goto out;
if (c->err_info->CommandStatus != 0)
+out:
dev_warn(&h->pdev->dev,
"error flushing cache on controller\n");
- cmd_special_free(h, c);
-out_of_memory:
+ cmd_free(h, c);
kfree(flush_buf);
}
-static void hpsa_shutdown(struct pci_dev *pdev)
+/* Make controller gather fresh report lun data each time we
+ * send down a report luns request
+ */
+static void hpsa_disable_rld_caching(struct ctlr_info *h)
+{
+ u32 *options;
+ struct CommandList *c;
+ int rc;
+
+ /* Don't bother trying to set diag options if locked up */
+ if (unlikely(h->lockup_detected))
+ return;
+
+ options = kzalloc(sizeof(*options), GFP_KERNEL);
+ if (!options)
+ return;
+
+ c = cmd_alloc(h);
+
+ /* first, get the current diag options settings */
+ if (fill_cmd(c, BMIC_SENSE_DIAG_OPTIONS, h, options, 4, 0,
+ RAID_CTLR_LUNID, TYPE_CMD))
+ goto errout;
+
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if ((rc != 0) || (c->err_info->CommandStatus != 0))
+ goto errout;
+
+ /* Now, set the bit for disabling the RLD caching */
+ *options |= HPSA_DIAG_OPTS_DISABLE_RLD_CACHING;
+
+ if (fill_cmd(c, BMIC_SET_DIAG_OPTIONS, h, options, 4, 0,
+ RAID_CTLR_LUNID, TYPE_CMD))
+ goto errout;
+
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_TO_DEVICE,
+ NO_TIMEOUT);
+ if ((rc != 0) || (c->err_info->CommandStatus != 0))
+ goto errout;
+
+ /* Now verify that it got set: */
+ if (fill_cmd(c, BMIC_SENSE_DIAG_OPTIONS, h, options, 4, 0,
+ RAID_CTLR_LUNID, TYPE_CMD))
+ goto errout;
+
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, DMA_FROM_DEVICE,
+ NO_TIMEOUT);
+ if ((rc != 0) || (c->err_info->CommandStatus != 0))
+ goto errout;
+
+ if (*options & HPSA_DIAG_OPTS_DISABLE_RLD_CACHING)
+ goto out;
+
+errout:
+ dev_err(&h->pdev->dev,
+ "Error: failed to disable report lun data caching.\n");
+out:
+ cmd_free(h, c);
+ kfree(options);
+}
+
+static void __hpsa_shutdown(struct pci_dev *pdev)
{
struct ctlr_info *h;
@@ -4264,69 +9018,107 @@ static void hpsa_shutdown(struct pci_dev *pdev)
*/
hpsa_flush_cache(h);
h->access.set_intr_mask(h, HPSA_INTR_OFF);
- free_irq(h->intr[h->intr_mode], h);
-#ifdef CONFIG_PCI_MSI
- if (h->msix_vector)
- pci_disable_msix(h->pdev);
- else if (h->msi_vector)
- pci_disable_msi(h->pdev);
-#endif /* CONFIG_PCI_MSI */
+ hpsa_free_irqs(h); /* init_one 4 */
+ hpsa_disable_interrupt_mode(h); /* pci_init 2 */
+}
+
+static void hpsa_shutdown(struct pci_dev *pdev)
+{
+ __hpsa_shutdown(pdev);
+ pci_disable_device(pdev);
}
-static void __devexit hpsa_remove_one(struct pci_dev *pdev)
+static void hpsa_free_device_info(struct ctlr_info *h)
+{
+ int i;
+
+ for (i = 0; i < h->ndevices; i++) {
+ kfree(h->dev[i]);
+ h->dev[i] = NULL;
+ }
+}
+
+static void hpsa_remove_one(struct pci_dev *pdev)
{
struct ctlr_info *h;
+ unsigned long flags;
if (pci_get_drvdata(pdev) == NULL) {
- dev_err(&pdev->dev, "unable to remove device \n");
+ dev_err(&pdev->dev, "unable to remove device\n");
return;
}
h = pci_get_drvdata(pdev);
- hpsa_unregister_scsi(h); /* unhook from SCSI subsystem */
- hpsa_shutdown(pdev);
- iounmap(h->vaddr);
- iounmap(h->transtable);
- iounmap(h->cfgtable);
- hpsa_free_sg_chain_blocks(h);
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct CommandList),
- h->cmd_pool, h->cmd_pool_dhandle);
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct ErrorInfo),
- h->errinfo_pool, h->errinfo_pool_dhandle);
- pci_free_consistent(h->pdev, h->reply_pool_size,
- h->reply_pool, h->reply_pool_dhandle);
- kfree(h->cmd_pool_bits);
- kfree(h->blockFetchTable);
- kfree(h->hba_inquiry_data);
+
+ /* Get rid of any controller monitoring work items */
+ spin_lock_irqsave(&h->lock, flags);
+ h->remove_in_progress = 1;
+ spin_unlock_irqrestore(&h->lock, flags);
+ cancel_delayed_work_sync(&h->monitor_ctlr_work);
+ cancel_delayed_work_sync(&h->rescan_ctlr_work);
+ cancel_delayed_work_sync(&h->event_monitor_work);
+ destroy_workqueue(h->rescan_ctlr_wq);
+ destroy_workqueue(h->resubmit_wq);
+ destroy_workqueue(h->monitor_ctlr_wq);
+
+ hpsa_delete_sas_host(h);
+
/*
- * Deliberately omit pci_disable_device(): it does something nasty to
- * Smart Array controllers that pci_enable_device does not undo
+ * Call before disabling interrupts.
+ * scsi_remove_host can trigger I/O operations especially
+ * when multipath is enabled. There can be SYNCHRONIZE CACHE
+ * operations which cannot complete and will hang the system.
*/
- pci_release_regions(pdev);
- pci_set_drvdata(pdev, NULL);
- kfree(h);
+ if (h->scsi_host)
+ scsi_remove_host(h->scsi_host); /* init_one 8 */
+ /* includes hpsa_free_irqs - init_one 4 */
+ /* includes hpsa_disable_interrupt_mode - pci_init 2 */
+ __hpsa_shutdown(pdev);
+
+ hpsa_free_device_info(h); /* scan */
+
+ kfree(h->hba_inquiry_data); /* init_one 10 */
+ h->hba_inquiry_data = NULL; /* init_one 10 */
+ hpsa_free_ioaccel2_sg_chain_blocks(h);
+ hpsa_free_performant_mode(h); /* init_one 7 */
+ hpsa_free_sg_chain_blocks(h); /* init_one 6 */
+ hpsa_free_cmd_pool(h); /* init_one 5 */
+ kfree(h->lastlogicals);
+
+ /* hpsa_free_irqs already called via hpsa_shutdown init_one 4 */
+
+ scsi_host_put(h->scsi_host); /* init_one 3 */
+ h->scsi_host = NULL; /* init_one 3 */
+
+ /* includes hpsa_disable_interrupt_mode - pci_init 2 */
+ hpsa_free_pci_init(h); /* init_one 2.5 */
+
+ free_percpu(h->lockup_detected); /* init_one 2 */
+ h->lockup_detected = NULL; /* init_one 2 */
+
+ hpda_free_ctlr_info(h); /* init_one 1 */
}
-static int hpsa_suspend(__attribute__((unused)) struct pci_dev *pdev,
- __attribute__((unused)) pm_message_t state)
+static int __maybe_unused hpsa_suspend(
+ __attribute__((unused)) struct device *dev)
{
return -ENOSYS;
}
-static int hpsa_resume(__attribute__((unused)) struct pci_dev *pdev)
+static int __maybe_unused hpsa_resume
+ (__attribute__((unused)) struct device *dev)
{
return -ENOSYS;
}
+static SIMPLE_DEV_PM_OPS(hpsa_pm_ops, hpsa_suspend, hpsa_resume);
+
static struct pci_driver hpsa_pci_driver = {
- .name = "hpsa",
+ .name = HPSA,
.probe = hpsa_init_one,
- .remove = __devexit_p(hpsa_remove_one),
+ .remove = hpsa_remove_one,
.id_table = hpsa_pci_device_id, /* id_table */
.shutdown = hpsa_shutdown,
- .suspend = hpsa_suspend,
- .resume = hpsa_resume,
+ .driver.pm = &hpsa_pm_ops,
};
/* Fill in bucket_map[], given nsgs (the max number of
@@ -4342,20 +9134,17 @@ static struct pci_driver hpsa_pci_driver = {
* bits of the command address.
*/
static void calc_bucket_map(int bucket[], int num_buckets,
- int nsgs, int *bucket_map)
+ int nsgs, int min_blocks, u32 *bucket_map)
{
int i, j, b, size;
- /* even a command with 0 SGs requires 4 blocks */
-#define MINIMUM_TRANSFER_BLOCKS 4
-#define NUM_BUCKETS 8
/* Note, bucket_map must have nsgs+1 entries. */
for (i = 0; i <= nsgs; i++) {
/* Compute size of a command with i SG entries */
- size = i + MINIMUM_TRANSFER_BLOCKS;
+ size = i + min_blocks;
b = num_buckets; /* Assume the biggest bucket */
/* Find the bucket that is just big enough */
- for (j = 0; j < 8; j++) {
+ for (j = 0; j < num_buckets; j++) {
if (bucket[j] >= size) {
b = j;
break;
@@ -4366,11 +9155,20 @@ static void calc_bucket_map(int bucket[], int num_buckets,
}
}
-static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h,
- u32 use_short_tags)
+/*
+ * return -ENODEV on err, 0 on success (or no action)
+ * allocates numerous items that must be freed later
+ */
+static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
{
int i;
unsigned long register_value;
+ unsigned long transMethod = CFGTBL_Trans_Performant |
+ (trans_support & CFGTBL_Trans_use_short_tags) |
+ CFGTBL_Trans_enable_directed_msix |
+ (trans_support & (CFGTBL_Trans_io_accel1 |
+ CFGTBL_Trans_io_accel2));
+ struct access_method access = SA5_performant_access;
/* This is a bit complicated. There are 8 registers on
* the controller which we write to to tell it 8 different
@@ -4382,104 +9180,828 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h,
* Each SG entry requires 16 bytes. The eight registers are programmed
* with the number of 16-byte blocks a command of that size requires.
* The smallest command possible requires 5 such 16 byte blocks.
- * the largest command possible requires MAXSGENTRIES + 4 16-byte
+ * the largest command possible requires SG_ENTRIES_IN_CMD + 4 16-byte
* blocks. Note, this only extends to the SG entries contained
* within the command block, and does not extend to chained blocks
* of SG elements. bft[] contains the eight values we write to
* the registers. They are not evenly distributed, but have more
* sizes for small commands, and fewer sizes for larger commands.
*/
- int bft[8] = {5, 6, 8, 10, 12, 20, 28, MAXSGENTRIES + 4};
- BUILD_BUG_ON(28 > MAXSGENTRIES + 4);
+ int bft[8] = {5, 6, 8, 10, 12, 20, 28, SG_ENTRIES_IN_CMD + 4};
+#define MIN_IOACCEL2_BFT_ENTRY 5
+#define HPSA_IOACCEL2_HEADER_SZ 4
+ int bft2[16] = {MIN_IOACCEL2_BFT_ENTRY, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19,
+ HPSA_IOACCEL2_HEADER_SZ + IOACCEL2_MAXSGENTRIES};
+ BUILD_BUG_ON(ARRAY_SIZE(bft2) != 16);
+ BUILD_BUG_ON(ARRAY_SIZE(bft) != 8);
+ BUILD_BUG_ON(offsetof(struct io_accel2_cmd, sg) >
+ 16 * MIN_IOACCEL2_BFT_ENTRY);
+ BUILD_BUG_ON(sizeof(struct ioaccel2_sg_element) != 16);
+ BUILD_BUG_ON(28 > SG_ENTRIES_IN_CMD + 4);
/* 5 = 1 s/g entry or 4k
* 6 = 2 s/g entry or 8k
* 8 = 4 s/g entry or 16k
* 10 = 6 s/g entry or 24k
*/
- h->reply_pool_wraparound = 1; /* spec: init to 1 */
+ /* If the controller supports either ioaccel method then
+ * we can also use the RAID stack submit path that does not
+ * perform the superfluous readl() after each command submission.
+ */
+ if (trans_support & (CFGTBL_Trans_io_accel1 | CFGTBL_Trans_io_accel2))
+ access = SA5_performant_access_no_read;
/* Controller spec: zero out this buffer. */
- memset(h->reply_pool, 0, h->reply_pool_size);
- h->reply_pool_head = h->reply_pool;
+ for (i = 0; i < h->nreply_queues; i++)
+ memset(h->reply_queue[i].head, 0, h->reply_queue_size);
- bft[7] = h->max_sg_entries + 4;
- calc_bucket_map(bft, ARRAY_SIZE(bft), 32, h->blockFetchTable);
+ bft[7] = SG_ENTRIES_IN_CMD + 4;
+ calc_bucket_map(bft, ARRAY_SIZE(bft),
+ SG_ENTRIES_IN_CMD, 4, h->blockFetchTable);
for (i = 0; i < 8; i++)
writel(bft[i], &h->transtable->BlockFetch[i]);
/* size of controller ring buffer */
writel(h->max_commands, &h->transtable->RepQSize);
- writel(1, &h->transtable->RepQCount);
+ writel(h->nreply_queues, &h->transtable->RepQCount);
writel(0, &h->transtable->RepQCtrAddrLow32);
writel(0, &h->transtable->RepQCtrAddrHigh32);
- writel(h->reply_pool_dhandle, &h->transtable->RepQAddr0Low32);
- writel(0, &h->transtable->RepQAddr0High32);
- writel(CFGTBL_Trans_Performant | use_short_tags,
- &(h->cfgtable->HostWrite.TransportRequest));
+
+ for (i = 0; i < h->nreply_queues; i++) {
+ writel(0, &h->transtable->RepQAddr[i].upper);
+ writel(h->reply_queue[i].busaddr,
+ &h->transtable->RepQAddr[i].lower);
+ }
+
+ writel(0, &h->cfgtable->HostWrite.command_pool_addr_hi);
+ writel(transMethod, &(h->cfgtable->HostWrite.TransportRequest));
+ /*
+ * enable outbound interrupt coalescing in accelerator mode;
+ */
+ if (trans_support & CFGTBL_Trans_io_accel1) {
+ access = SA5_ioaccel_mode1_access;
+ writel(10, &h->cfgtable->HostWrite.CoalIntDelay);
+ writel(4, &h->cfgtable->HostWrite.CoalIntCount);
+ } else
+ if (trans_support & CFGTBL_Trans_io_accel2)
+ access = SA5_ioaccel_mode2_access;
writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
- hpsa_wait_for_mode_change_ack(h);
+ if (hpsa_wait_for_mode_change_ack(h)) {
+ dev_err(&h->pdev->dev,
+ "performant mode problem - doorbell timeout\n");
+ return -ENODEV;
+ }
register_value = readl(&(h->cfgtable->TransportActive));
if (!(register_value & CFGTBL_Trans_Performant)) {
- dev_warn(&h->pdev->dev, "unable to get board into"
- " performant mode\n");
- return;
+ dev_err(&h->pdev->dev,
+ "performant mode problem - transport not active\n");
+ return -ENODEV;
}
/* Change the access methods to the performant access methods */
- h->access = SA5_performant_access;
- h->transMethod = CFGTBL_Trans_Performant;
+ h->access = access;
+ h->transMethod = transMethod;
+
+ if (!((trans_support & CFGTBL_Trans_io_accel1) ||
+ (trans_support & CFGTBL_Trans_io_accel2)))
+ return 0;
+
+ if (trans_support & CFGTBL_Trans_io_accel1) {
+ /* Set up I/O accelerator mode */
+ for (i = 0; i < h->nreply_queues; i++) {
+ writel(i, h->vaddr + IOACCEL_MODE1_REPLY_QUEUE_INDEX);
+ h->reply_queue[i].current_entry =
+ readl(h->vaddr + IOACCEL_MODE1_PRODUCER_INDEX);
+ }
+ bft[7] = h->ioaccel_maxsg + 8;
+ calc_bucket_map(bft, ARRAY_SIZE(bft), h->ioaccel_maxsg, 8,
+ h->ioaccel1_blockFetchTable);
+
+ /* initialize all reply queue entries to unused */
+ for (i = 0; i < h->nreply_queues; i++)
+ memset(h->reply_queue[i].head,
+ (u8) IOACCEL_MODE1_REPLY_UNUSED,
+ h->reply_queue_size);
+
+ /* set all the constant fields in the accelerator command
+ * frames once at init time to save CPU cycles later.
+ */
+ for (i = 0; i < h->nr_cmds; i++) {
+ struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[i];
+
+ cp->function = IOACCEL1_FUNCTION_SCSIIO;
+ cp->err_info = (u32) (h->errinfo_pool_dhandle +
+ (i * sizeof(struct ErrorInfo)));
+ cp->err_info_len = sizeof(struct ErrorInfo);
+ cp->sgl_offset = IOACCEL1_SGLOFFSET;
+ cp->host_context_flags =
+ cpu_to_le16(IOACCEL1_HCFLAGS_CISS_FORMAT);
+ cp->timeout_sec = 0;
+ cp->ReplyQueue = 0;
+ cp->tag =
+ cpu_to_le64((i << DIRECT_LOOKUP_SHIFT));
+ cp->host_addr =
+ cpu_to_le64(h->ioaccel_cmd_pool_dhandle +
+ (i * sizeof(struct io_accel1_cmd)));
+ }
+ } else if (trans_support & CFGTBL_Trans_io_accel2) {
+ u64 cfg_offset, cfg_base_addr_index;
+ u32 bft2_offset, cfg_base_addr;
+
+ hpsa_find_cfg_addrs(h->pdev, h->vaddr, &cfg_base_addr,
+ &cfg_base_addr_index, &cfg_offset);
+ BUILD_BUG_ON(offsetof(struct io_accel2_cmd, sg) != 64);
+ bft2[15] = h->ioaccel_maxsg + HPSA_IOACCEL2_HEADER_SZ;
+ calc_bucket_map(bft2, ARRAY_SIZE(bft2), h->ioaccel_maxsg,
+ 4, h->ioaccel2_blockFetchTable);
+ bft2_offset = readl(&h->cfgtable->io_accel_request_size_offset);
+ BUILD_BUG_ON(offsetof(struct CfgTable,
+ io_accel_request_size_offset) != 0xb8);
+ h->ioaccel2_bft2_regs =
+ remap_pci_mem(pci_resource_start(h->pdev,
+ cfg_base_addr_index) +
+ cfg_offset + bft2_offset,
+ ARRAY_SIZE(bft2) *
+ sizeof(*h->ioaccel2_bft2_regs));
+ for (i = 0; i < ARRAY_SIZE(bft2); i++)
+ writel(bft2[i], &h->ioaccel2_bft2_regs[i]);
+ }
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
+ if (hpsa_wait_for_mode_change_ack(h)) {
+ dev_err(&h->pdev->dev,
+ "performant mode problem - enabling ioaccel mode\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* Free ioaccel1 mode command blocks and block fetch table */
+static void hpsa_free_ioaccel1_cmd_and_bft(struct ctlr_info *h)
+{
+ if (h->ioaccel_cmd_pool) {
+ dma_free_coherent(&h->pdev->dev,
+ h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
+ h->ioaccel_cmd_pool,
+ h->ioaccel_cmd_pool_dhandle);
+ h->ioaccel_cmd_pool = NULL;
+ h->ioaccel_cmd_pool_dhandle = 0;
+ }
+ kfree(h->ioaccel1_blockFetchTable);
+ h->ioaccel1_blockFetchTable = NULL;
+}
+
+/* Allocate ioaccel1 mode command blocks and block fetch table */
+static int hpsa_alloc_ioaccel1_cmd_and_bft(struct ctlr_info *h)
+{
+ h->ioaccel_maxsg =
+ readl(&(h->cfgtable->io_accel_max_embedded_sg_count));
+ if (h->ioaccel_maxsg > IOACCEL1_MAXSGENTRIES)
+ h->ioaccel_maxsg = IOACCEL1_MAXSGENTRIES;
+
+ /* Command structures must be aligned on a 128-byte boundary
+ * because the 7 lower bits of the address are used by the
+ * hardware.
+ */
+ BUILD_BUG_ON(sizeof(struct io_accel1_cmd) %
+ IOACCEL1_COMMANDLIST_ALIGNMENT);
+ h->ioaccel_cmd_pool =
+ dma_alloc_coherent(&h->pdev->dev,
+ h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
+ &h->ioaccel_cmd_pool_dhandle, GFP_KERNEL);
+
+ h->ioaccel1_blockFetchTable =
+ kmalloc(((h->ioaccel_maxsg + 1) *
+ sizeof(u32)), GFP_KERNEL);
+
+ if ((h->ioaccel_cmd_pool == NULL) ||
+ (h->ioaccel1_blockFetchTable == NULL))
+ goto clean_up;
+
+ memset(h->ioaccel_cmd_pool, 0,
+ h->nr_cmds * sizeof(*h->ioaccel_cmd_pool));
+ return 0;
+
+clean_up:
+ hpsa_free_ioaccel1_cmd_and_bft(h);
+ return -ENOMEM;
+}
+
+/* Free ioaccel2 mode command blocks and block fetch table */
+static void hpsa_free_ioaccel2_cmd_and_bft(struct ctlr_info *h)
+{
+ hpsa_free_ioaccel2_sg_chain_blocks(h);
+
+ if (h->ioaccel2_cmd_pool) {
+ dma_free_coherent(&h->pdev->dev,
+ h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
+ h->ioaccel2_cmd_pool,
+ h->ioaccel2_cmd_pool_dhandle);
+ h->ioaccel2_cmd_pool = NULL;
+ h->ioaccel2_cmd_pool_dhandle = 0;
+ }
+ kfree(h->ioaccel2_blockFetchTable);
+ h->ioaccel2_blockFetchTable = NULL;
}
-static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
+/* Allocate ioaccel2 mode command blocks and block fetch table */
+static int hpsa_alloc_ioaccel2_cmd_and_bft(struct ctlr_info *h)
+{
+ int rc;
+
+ /* Allocate ioaccel2 mode command blocks and block fetch table */
+
+ h->ioaccel_maxsg =
+ readl(&(h->cfgtable->io_accel_max_embedded_sg_count));
+ if (h->ioaccel_maxsg > IOACCEL2_MAXSGENTRIES)
+ h->ioaccel_maxsg = IOACCEL2_MAXSGENTRIES;
+
+ BUILD_BUG_ON(sizeof(struct io_accel2_cmd) %
+ IOACCEL2_COMMANDLIST_ALIGNMENT);
+ h->ioaccel2_cmd_pool =
+ dma_alloc_coherent(&h->pdev->dev,
+ h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
+ &h->ioaccel2_cmd_pool_dhandle, GFP_KERNEL);
+
+ h->ioaccel2_blockFetchTable =
+ kmalloc(((h->ioaccel_maxsg + 1) *
+ sizeof(u32)), GFP_KERNEL);
+
+ if ((h->ioaccel2_cmd_pool == NULL) ||
+ (h->ioaccel2_blockFetchTable == NULL)) {
+ rc = -ENOMEM;
+ goto clean_up;
+ }
+
+ rc = hpsa_allocate_ioaccel2_sg_chain_blocks(h);
+ if (rc)
+ goto clean_up;
+
+ memset(h->ioaccel2_cmd_pool, 0,
+ h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool));
+ return 0;
+
+clean_up:
+ hpsa_free_ioaccel2_cmd_and_bft(h);
+ return rc;
+}
+
+/* Free items allocated by hpsa_put_ctlr_into_performant_mode */
+static void hpsa_free_performant_mode(struct ctlr_info *h)
+{
+ kfree(h->blockFetchTable);
+ h->blockFetchTable = NULL;
+ hpsa_free_reply_queues(h);
+ hpsa_free_ioaccel1_cmd_and_bft(h);
+ hpsa_free_ioaccel2_cmd_and_bft(h);
+}
+
+/* return -ENODEV on error, 0 on success (or no action)
+ * allocates numerous items that must be freed later
+ */
+static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
{
u32 trans_support;
+ int i, rc;
if (hpsa_simple_mode)
- return;
+ return 0;
trans_support = readl(&(h->cfgtable->TransportSupport));
if (!(trans_support & PERFORMANT_MODE))
- return;
+ return 0;
+ /* Check for I/O accelerator mode support */
+ if (trans_support & CFGTBL_Trans_io_accel1) {
+ rc = hpsa_alloc_ioaccel1_cmd_and_bft(h);
+ if (rc)
+ return rc;
+ } else if (trans_support & CFGTBL_Trans_io_accel2) {
+ rc = hpsa_alloc_ioaccel2_cmd_and_bft(h);
+ if (rc)
+ return rc;
+ }
+
+ h->nreply_queues = h->msix_vectors > 0 ? h->msix_vectors : 1;
hpsa_get_max_perf_mode_cmds(h);
- h->max_sg_entries = 32;
/* Performant mode ring buffer and supporting data structures */
- h->reply_pool_size = h->max_commands * sizeof(u64);
- h->reply_pool = pci_alloc_consistent(h->pdev, h->reply_pool_size,
- &(h->reply_pool_dhandle));
+ h->reply_queue_size = h->max_commands * sizeof(u64);
+
+ for (i = 0; i < h->nreply_queues; i++) {
+ h->reply_queue[i].head = dma_alloc_coherent(&h->pdev->dev,
+ h->reply_queue_size,
+ &h->reply_queue[i].busaddr,
+ GFP_KERNEL);
+ if (!h->reply_queue[i].head) {
+ rc = -ENOMEM;
+ goto clean1; /* rq, ioaccel */
+ }
+ h->reply_queue[i].size = h->max_commands;
+ h->reply_queue[i].wraparound = 1; /* spec: init to 1 */
+ h->reply_queue[i].current_entry = 0;
+ }
/* Need a block fetch table for performant mode */
- h->blockFetchTable = kmalloc(((h->max_sg_entries+1) *
+ h->blockFetchTable = kmalloc(((SG_ENTRIES_IN_CMD + 1) *
sizeof(u32)), GFP_KERNEL);
+ if (!h->blockFetchTable) {
+ rc = -ENOMEM;
+ goto clean1; /* rq, ioaccel */
+ }
- if ((h->reply_pool == NULL)
- || (h->blockFetchTable == NULL))
- goto clean_up;
+ rc = hpsa_enter_performant_mode(h, trans_support);
+ if (rc)
+ goto clean2; /* bft, rq, ioaccel */
+ return 0;
- hpsa_enter_performant_mode(h,
- trans_support & CFGTBL_Trans_use_short_tags);
+clean2: /* bft, rq, ioaccel */
+ kfree(h->blockFetchTable);
+ h->blockFetchTable = NULL;
+clean1: /* rq, ioaccel */
+ hpsa_free_reply_queues(h);
+ hpsa_free_ioaccel1_cmd_and_bft(h);
+ hpsa_free_ioaccel2_cmd_and_bft(h);
+ return rc;
+}
- return;
+static int is_accelerated_cmd(struct CommandList *c)
+{
+ return c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_IOACCEL2;
+}
-clean_up:
- if (h->reply_pool)
- pci_free_consistent(h->pdev, h->reply_pool_size,
- h->reply_pool, h->reply_pool_dhandle);
- kfree(h->blockFetchTable);
+static void hpsa_drain_accel_commands(struct ctlr_info *h)
+{
+ struct CommandList *c = NULL;
+ int i, accel_cmds_out;
+ int refcount;
+
+ do { /* wait for all outstanding ioaccel commands to drain out */
+ accel_cmds_out = 0;
+ for (i = 0; i < h->nr_cmds; i++) {
+ c = h->cmd_pool + i;
+ refcount = atomic_inc_return(&c->refcount);
+ if (refcount > 1) /* Command is allocated */
+ accel_cmds_out += is_accelerated_cmd(c);
+ cmd_free(h, c);
+ }
+ if (accel_cmds_out <= 0)
+ break;
+ msleep(100);
+ } while (1);
+}
+
+static struct hpsa_sas_phy *hpsa_alloc_sas_phy(
+ struct hpsa_sas_port *hpsa_sas_port)
+{
+ struct hpsa_sas_phy *hpsa_sas_phy;
+ struct sas_phy *phy;
+
+ hpsa_sas_phy = kzalloc(sizeof(*hpsa_sas_phy), GFP_KERNEL);
+ if (!hpsa_sas_phy)
+ return NULL;
+
+ phy = sas_phy_alloc(hpsa_sas_port->parent_node->parent_dev,
+ hpsa_sas_port->next_phy_index);
+ if (!phy) {
+ kfree(hpsa_sas_phy);
+ return NULL;
+ }
+
+ hpsa_sas_port->next_phy_index++;
+ hpsa_sas_phy->phy = phy;
+ hpsa_sas_phy->parent_port = hpsa_sas_port;
+
+ return hpsa_sas_phy;
+}
+
+static void hpsa_free_sas_phy(struct hpsa_sas_phy *hpsa_sas_phy)
+{
+ struct sas_phy *phy = hpsa_sas_phy->phy;
+
+ sas_port_delete_phy(hpsa_sas_phy->parent_port->port, phy);
+ if (hpsa_sas_phy->added_to_port)
+ list_del(&hpsa_sas_phy->phy_list_entry);
+ sas_phy_delete(phy);
+ kfree(hpsa_sas_phy);
+}
+
+static int hpsa_sas_port_add_phy(struct hpsa_sas_phy *hpsa_sas_phy)
+{
+ int rc;
+ struct hpsa_sas_port *hpsa_sas_port;
+ struct sas_phy *phy;
+ struct sas_identify *identify;
+
+ hpsa_sas_port = hpsa_sas_phy->parent_port;
+ phy = hpsa_sas_phy->phy;
+
+ identify = &phy->identify;
+ memset(identify, 0, sizeof(*identify));
+ identify->sas_address = hpsa_sas_port->sas_address;
+ identify->device_type = SAS_END_DEVICE;
+ identify->initiator_port_protocols = SAS_PROTOCOL_STP;
+ identify->target_port_protocols = SAS_PROTOCOL_STP;
+ phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
+ phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
+ phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
+ phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
+ phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
+
+ rc = sas_phy_add(hpsa_sas_phy->phy);
+ if (rc)
+ return rc;
+
+ sas_port_add_phy(hpsa_sas_port->port, hpsa_sas_phy->phy);
+ list_add_tail(&hpsa_sas_phy->phy_list_entry,
+ &hpsa_sas_port->phy_list_head);
+ hpsa_sas_phy->added_to_port = true;
+
+ return 0;
+}
+
+static int
+ hpsa_sas_port_add_rphy(struct hpsa_sas_port *hpsa_sas_port,
+ struct sas_rphy *rphy)
+{
+ struct sas_identify *identify;
+
+ identify = &rphy->identify;
+ identify->sas_address = hpsa_sas_port->sas_address;
+ identify->initiator_port_protocols = SAS_PROTOCOL_STP;
+ identify->target_port_protocols = SAS_PROTOCOL_STP;
+
+ return sas_rphy_add(rphy);
+}
+
+static struct hpsa_sas_port
+ *hpsa_alloc_sas_port(struct hpsa_sas_node *hpsa_sas_node,
+ u64 sas_address)
+{
+ int rc;
+ struct hpsa_sas_port *hpsa_sas_port;
+ struct sas_port *port;
+
+ hpsa_sas_port = kzalloc(sizeof(*hpsa_sas_port), GFP_KERNEL);
+ if (!hpsa_sas_port)
+ return NULL;
+
+ INIT_LIST_HEAD(&hpsa_sas_port->phy_list_head);
+ hpsa_sas_port->parent_node = hpsa_sas_node;
+
+ port = sas_port_alloc_num(hpsa_sas_node->parent_dev);
+ if (!port)
+ goto free_hpsa_port;
+
+ rc = sas_port_add(port);
+ if (rc)
+ goto free_sas_port;
+
+ hpsa_sas_port->port = port;
+ hpsa_sas_port->sas_address = sas_address;
+ list_add_tail(&hpsa_sas_port->port_list_entry,
+ &hpsa_sas_node->port_list_head);
+
+ return hpsa_sas_port;
+
+free_sas_port:
+ sas_port_free(port);
+free_hpsa_port:
+ kfree(hpsa_sas_port);
+
+ return NULL;
+}
+
+static void hpsa_free_sas_port(struct hpsa_sas_port *hpsa_sas_port)
+{
+ struct hpsa_sas_phy *hpsa_sas_phy;
+ struct hpsa_sas_phy *next;
+
+ list_for_each_entry_safe(hpsa_sas_phy, next,
+ &hpsa_sas_port->phy_list_head, phy_list_entry)
+ hpsa_free_sas_phy(hpsa_sas_phy);
+
+ sas_port_delete(hpsa_sas_port->port);
+ list_del(&hpsa_sas_port->port_list_entry);
+ kfree(hpsa_sas_port);
+}
+
+static struct hpsa_sas_node *hpsa_alloc_sas_node(struct device *parent_dev)
+{
+ struct hpsa_sas_node *hpsa_sas_node;
+
+ hpsa_sas_node = kzalloc(sizeof(*hpsa_sas_node), GFP_KERNEL);
+ if (hpsa_sas_node) {
+ hpsa_sas_node->parent_dev = parent_dev;
+ INIT_LIST_HEAD(&hpsa_sas_node->port_list_head);
+ }
+
+ return hpsa_sas_node;
+}
+
+static void hpsa_free_sas_node(struct hpsa_sas_node *hpsa_sas_node)
+{
+ struct hpsa_sas_port *hpsa_sas_port;
+ struct hpsa_sas_port *next;
+
+ if (!hpsa_sas_node)
+ return;
+
+ list_for_each_entry_safe(hpsa_sas_port, next,
+ &hpsa_sas_node->port_list_head, port_list_entry)
+ hpsa_free_sas_port(hpsa_sas_port);
+
+ kfree(hpsa_sas_node);
+}
+
+static struct hpsa_scsi_dev_t
+ *hpsa_find_device_by_sas_rphy(struct ctlr_info *h,
+ struct sas_rphy *rphy)
+{
+ int i;
+ struct hpsa_scsi_dev_t *device;
+
+ for (i = 0; i < h->ndevices; i++) {
+ device = h->dev[i];
+ if (!device->sas_port)
+ continue;
+ if (device->sas_port->rphy == rphy)
+ return device;
+ }
+
+ return NULL;
+}
+
+static int hpsa_add_sas_host(struct ctlr_info *h)
+{
+ int rc;
+ struct device *parent_dev;
+ struct hpsa_sas_node *hpsa_sas_node;
+ struct hpsa_sas_port *hpsa_sas_port;
+ struct hpsa_sas_phy *hpsa_sas_phy;
+
+ parent_dev = &h->scsi_host->shost_dev;
+
+ hpsa_sas_node = hpsa_alloc_sas_node(parent_dev);
+ if (!hpsa_sas_node)
+ return -ENOMEM;
+
+ hpsa_sas_port = hpsa_alloc_sas_port(hpsa_sas_node, h->sas_address);
+ if (!hpsa_sas_port) {
+ rc = -ENODEV;
+ goto free_sas_node;
+ }
+
+ hpsa_sas_phy = hpsa_alloc_sas_phy(hpsa_sas_port);
+ if (!hpsa_sas_phy) {
+ rc = -ENODEV;
+ goto free_sas_port;
+ }
+
+ rc = hpsa_sas_port_add_phy(hpsa_sas_phy);
+ if (rc)
+ goto free_sas_phy;
+
+ h->sas_host = hpsa_sas_node;
+
+ return 0;
+
+free_sas_phy:
+ sas_phy_free(hpsa_sas_phy->phy);
+ kfree(hpsa_sas_phy);
+free_sas_port:
+ hpsa_free_sas_port(hpsa_sas_port);
+free_sas_node:
+ hpsa_free_sas_node(hpsa_sas_node);
+
+ return rc;
+}
+
+static void hpsa_delete_sas_host(struct ctlr_info *h)
+{
+ hpsa_free_sas_node(h->sas_host);
+}
+
+static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node,
+ struct hpsa_scsi_dev_t *device)
+{
+ int rc;
+ struct hpsa_sas_port *hpsa_sas_port;
+ struct sas_rphy *rphy;
+
+ hpsa_sas_port = hpsa_alloc_sas_port(hpsa_sas_node, device->sas_address);
+ if (!hpsa_sas_port)
+ return -ENOMEM;
+
+ rphy = sas_end_device_alloc(hpsa_sas_port->port);
+ if (!rphy) {
+ rc = -ENODEV;
+ goto free_sas_port;
+ }
+
+ hpsa_sas_port->rphy = rphy;
+ device->sas_port = hpsa_sas_port;
+
+ rc = hpsa_sas_port_add_rphy(hpsa_sas_port, rphy);
+ if (rc)
+ goto free_sas_rphy;
+
+ return 0;
+
+free_sas_rphy:
+ sas_rphy_free(rphy);
+free_sas_port:
+ hpsa_free_sas_port(hpsa_sas_port);
+ device->sas_port = NULL;
+
+ return rc;
+}
+
+static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device)
+{
+ if (device->sas_port) {
+ hpsa_free_sas_port(device->sas_port);
+ device->sas_port = NULL;
+ }
}
+static int
+hpsa_sas_get_linkerrors(struct sas_phy *phy)
+{
+ return 0;
+}
+
+static int
+hpsa_sas_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
+{
+ struct Scsi_Host *shost = phy_to_shost(rphy);
+ struct ctlr_info *h;
+ struct hpsa_scsi_dev_t *sd;
+
+ if (!shost)
+ return -ENXIO;
+
+ h = shost_to_hba(shost);
+
+ if (!h)
+ return -ENXIO;
+
+ sd = hpsa_find_device_by_sas_rphy(h, rphy);
+ if (!sd)
+ return -ENXIO;
+
+ *identifier = sd->eli;
+
+ return 0;
+}
+
+static int
+hpsa_sas_get_bay_identifier(struct sas_rphy *rphy)
+{
+ return -ENXIO;
+}
+
+static int
+hpsa_sas_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ return 0;
+}
+
+static int
+hpsa_sas_phy_enable(struct sas_phy *phy, int enable)
+{
+ return 0;
+}
+
+static int
+hpsa_sas_phy_setup(struct sas_phy *phy)
+{
+ return 0;
+}
+
+static void
+hpsa_sas_phy_release(struct sas_phy *phy)
+{
+}
+
+static int
+hpsa_sas_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
+{
+ return -EINVAL;
+}
+
+static struct sas_function_template hpsa_sas_transport_functions = {
+ .get_linkerrors = hpsa_sas_get_linkerrors,
+ .get_enclosure_identifier = hpsa_sas_get_enclosure_identifier,
+ .get_bay_identifier = hpsa_sas_get_bay_identifier,
+ .phy_reset = hpsa_sas_phy_reset,
+ .phy_enable = hpsa_sas_phy_enable,
+ .phy_setup = hpsa_sas_phy_setup,
+ .phy_release = hpsa_sas_phy_release,
+ .set_phy_speed = hpsa_sas_phy_speed,
+};
+
/*
* This is it. Register the PCI driver information for the cards we control
* the OS will call our registered routines when it finds one of our cards.
*/
static int __init hpsa_init(void)
{
- return pci_register_driver(&hpsa_pci_driver);
+ int rc;
+
+ hpsa_sas_transport_template =
+ sas_attach_transport(&hpsa_sas_transport_functions);
+ if (!hpsa_sas_transport_template)
+ return -ENODEV;
+
+ rc = pci_register_driver(&hpsa_pci_driver);
+
+ if (rc)
+ sas_release_transport(hpsa_sas_transport_template);
+
+ return rc;
}
static void __exit hpsa_cleanup(void)
{
pci_unregister_driver(&hpsa_pci_driver);
+ sas_release_transport(hpsa_sas_transport_template);
+}
+
+static void __attribute__((unused)) verify_offsets(void)
+{
+#define VERIFY_OFFSET(member, offset) \
+ BUILD_BUG_ON(offsetof(struct raid_map_data, member) != offset)
+
+ VERIFY_OFFSET(structure_size, 0);
+ VERIFY_OFFSET(volume_blk_size, 4);
+ VERIFY_OFFSET(volume_blk_cnt, 8);
+ VERIFY_OFFSET(phys_blk_shift, 16);
+ VERIFY_OFFSET(parity_rotation_shift, 17);
+ VERIFY_OFFSET(strip_size, 18);
+ VERIFY_OFFSET(disk_starting_blk, 20);
+ VERIFY_OFFSET(disk_blk_cnt, 28);
+ VERIFY_OFFSET(data_disks_per_row, 36);
+ VERIFY_OFFSET(metadata_disks_per_row, 38);
+ VERIFY_OFFSET(row_cnt, 40);
+ VERIFY_OFFSET(layout_map_count, 42);
+ VERIFY_OFFSET(flags, 44);
+ VERIFY_OFFSET(dekindex, 46);
+ /* VERIFY_OFFSET(reserved, 48 */
+ VERIFY_OFFSET(data, 64);
+
+#undef VERIFY_OFFSET
+
+#define VERIFY_OFFSET(member, offset) \
+ BUILD_BUG_ON(offsetof(struct io_accel2_cmd, member) != offset)
+
+ VERIFY_OFFSET(IU_type, 0);
+ VERIFY_OFFSET(direction, 1);
+ VERIFY_OFFSET(reply_queue, 2);
+ /* VERIFY_OFFSET(reserved1, 3); */
+ VERIFY_OFFSET(scsi_nexus, 4);
+ VERIFY_OFFSET(Tag, 8);
+ VERIFY_OFFSET(cdb, 16);
+ VERIFY_OFFSET(cciss_lun, 32);
+ VERIFY_OFFSET(data_len, 40);
+ VERIFY_OFFSET(cmd_priority_task_attr, 44);
+ VERIFY_OFFSET(sg_count, 45);
+ /* VERIFY_OFFSET(reserved3 */
+ VERIFY_OFFSET(err_ptr, 48);
+ VERIFY_OFFSET(err_len, 56);
+ /* VERIFY_OFFSET(reserved4 */
+ VERIFY_OFFSET(sg, 64);
+
+#undef VERIFY_OFFSET
+
+#define VERIFY_OFFSET(member, offset) \
+ BUILD_BUG_ON(offsetof(struct io_accel1_cmd, member) != offset)
+
+ VERIFY_OFFSET(dev_handle, 0x00);
+ VERIFY_OFFSET(reserved1, 0x02);
+ VERIFY_OFFSET(function, 0x03);
+ VERIFY_OFFSET(reserved2, 0x04);
+ VERIFY_OFFSET(err_info, 0x0C);
+ VERIFY_OFFSET(reserved3, 0x10);
+ VERIFY_OFFSET(err_info_len, 0x12);
+ VERIFY_OFFSET(reserved4, 0x13);
+ VERIFY_OFFSET(sgl_offset, 0x14);
+ VERIFY_OFFSET(reserved5, 0x15);
+ VERIFY_OFFSET(transfer_len, 0x1C);
+ VERIFY_OFFSET(reserved6, 0x20);
+ VERIFY_OFFSET(io_flags, 0x24);
+ VERIFY_OFFSET(reserved7, 0x26);
+ VERIFY_OFFSET(LUN, 0x34);
+ VERIFY_OFFSET(control, 0x3C);
+ VERIFY_OFFSET(CDB, 0x40);
+ VERIFY_OFFSET(reserved8, 0x50);
+ VERIFY_OFFSET(host_context_flags, 0x60);
+ VERIFY_OFFSET(timeout_sec, 0x62);
+ VERIFY_OFFSET(ReplyQueue, 0x64);
+ VERIFY_OFFSET(reserved9, 0x65);
+ VERIFY_OFFSET(tag, 0x68);
+ VERIFY_OFFSET(host_addr, 0x70);
+ VERIFY_OFFSET(CISS_LUN, 0x78);
+ VERIFY_OFFSET(SG, 0x78 + 8);
+#undef VERIFY_OFFSET
}
module_init(hpsa_init);
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 6d8dcd4dd06b..99b0750850b2 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -1,6 +1,9 @@
/*
* Disk Array driver for HP Smart Array SAS controllers
- * Copyright 2000, 2009 Hewlett-Packard Development Company, L.P.
+ * Copyright (c) 2019-2020 Microchip Technology Inc. and its subsidiaries
+ * Copyright 2016 Microsemi Corporation
+ * Copyright 2014-2015 PMC-Sierra, Inc.
+ * Copyright 2000,2009-2015 Hewlett-Packard Development Company, L.P.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,11 +14,7 @@
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Questions/Comments/Bugfixes to iss_storagedev@hp.com
+ * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com
*
*/
#ifndef HPSA_H
@@ -32,105 +31,299 @@ struct access_method {
void (*submit_command)(struct ctlr_info *h,
struct CommandList *c);
void (*set_intr_mask)(struct ctlr_info *h, unsigned long val);
- unsigned long (*fifo_full)(struct ctlr_info *h);
bool (*intr_pending)(struct ctlr_info *h);
- unsigned long (*command_completed)(struct ctlr_info *h);
+ unsigned long (*command_completed)(struct ctlr_info *h, u8 q);
+};
+
+/* for SAS hosts and SAS expanders */
+struct hpsa_sas_node {
+ struct device *parent_dev;
+ struct list_head port_list_head;
};
+struct hpsa_sas_port {
+ struct list_head port_list_entry;
+ u64 sas_address;
+ struct sas_port *port;
+ int next_phy_index;
+ struct list_head phy_list_head;
+ struct hpsa_sas_node *parent_node;
+ struct sas_rphy *rphy;
+};
+
+struct hpsa_sas_phy {
+ struct list_head phy_list_entry;
+ struct sas_phy *phy;
+ struct hpsa_sas_port *parent_port;
+ bool added_to_port;
+};
+
+#define EXTERNAL_QD 128
struct hpsa_scsi_dev_t {
- int devtype;
+ unsigned int devtype;
int bus, target, lun; /* as presented to the OS */
unsigned char scsi3addr[8]; /* as presented to the HW */
+ u8 physical_device : 1;
+ u8 expose_device;
+ u8 removed : 1; /* device is marked for death */
+ u8 was_removed : 1; /* device actually removed */
#define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0"
unsigned char device_id[16]; /* from inquiry pg. 0x83 */
+ u64 sas_address;
+ u64 eli; /* from report diags. */
unsigned char vendor[8]; /* bytes 8-15 of inquiry data */
unsigned char model[16]; /* bytes 16-31 of inquiry data */
+ unsigned char rev; /* byte 2 of inquiry data */
unsigned char raid_level; /* from inquiry page 0xC1 */
+ unsigned char volume_offline; /* discovered via TUR or VPD */
+ u16 queue_depth; /* max queue_depth for this device */
+ atomic_t commands_outstanding; /* track commands sent to device */
+ atomic_t ioaccel_cmds_out; /* Only used for physical devices
+ * counts commands sent to physical
+ * device via "ioaccel" path.
+ */
+ bool in_reset;
+ u32 ioaccel_handle;
+ u8 active_path_index;
+ u8 path_map;
+ u8 bay;
+ u8 box[8];
+ u16 phys_connector[8];
+ int offload_config; /* I/O accel RAID offload configured */
+ int offload_enabled; /* I/O accel RAID offload enabled */
+ int offload_to_be_enabled;
+ int hba_ioaccel_enabled;
+ int offload_to_mirror; /* Send next I/O accelerator RAID
+ * offload request to mirror drive
+ */
+ struct raid_map_data raid_map; /* I/O accelerator RAID map */
+
+ /*
+ * Pointers from logical drive map indices to the phys drives that
+ * make those logical drives. Note, multiple logical drives may
+ * share physical drives. You can have for instance 5 physical
+ * drives with 3 logical drives each using those same 5 physical
+ * disks. We need these pointers for counting i/o's out to physical
+ * devices in order to honor physical device queue depth limits.
+ */
+ struct hpsa_scsi_dev_t *phys_disk[RAID_MAP_MAX_ENTRIES];
+ int nphysical_disks;
+ int supports_aborts;
+ struct hpsa_sas_port *sas_port;
+ int external; /* 1-from external array 0-not <0-unknown */
+};
+
+struct reply_queue_buffer {
+ u64 *head;
+ size_t size;
+ u8 wraparound;
+ u32 current_entry;
+ dma_addr_t busaddr;
+};
+
+#pragma pack(1)
+struct bmic_controller_parameters {
+ u8 led_flags;
+ u8 enable_command_list_verification;
+ u8 backed_out_write_drives;
+ u16 stripes_for_parity;
+ u8 parity_distribution_mode_flags;
+ u16 max_driver_requests;
+ u16 elevator_trend_count;
+ u8 disable_elevator;
+ u8 force_scan_complete;
+ u8 scsi_transfer_mode;
+ u8 force_narrow;
+ u8 rebuild_priority;
+ u8 expand_priority;
+ u8 host_sdb_asic_fix;
+ u8 pdpi_burst_from_host_disabled;
+ char software_name[64];
+ char hardware_name[32];
+ u8 bridge_revision;
+ u8 snapshot_priority;
+ u32 os_specific;
+ u8 post_prompt_timeout;
+ u8 automatic_drive_slamming;
+ u8 reserved1;
+ u8 nvram_flags;
+ u8 cache_nvram_flags;
+ u8 drive_config_flags;
+ u16 reserved2;
+ u8 temp_warning_level;
+ u8 temp_shutdown_level;
+ u8 temp_condition_reset;
+ u8 max_coalesce_commands;
+ u32 max_coalesce_delay;
+ u8 orca_password[4];
+ u8 access_id[16];
+ u8 reserved[356];
};
+#pragma pack()
struct ctlr_info {
+ unsigned int *reply_map;
int ctlr;
char devname[8];
char *product_name;
struct pci_dev *pdev;
u32 board_id;
+ u64 sas_address;
void __iomem *vaddr;
unsigned long paddr;
int nr_cmds; /* Number of commands allowed on this controller */
+#define HPSA_CMDS_RESERVED_FOR_ABORTS 2
+#define HPSA_CMDS_RESERVED_FOR_DRIVER 1
struct CfgTable __iomem *cfgtable;
- int max_sg_entries;
int interrupts_enabled;
- int major;
int max_commands;
- int commands_outstanding;
- int max_outstanding; /* Debug */
- int usage_count; /* number of opens all all minor devices */
+ int last_collision_tag; /* tags are global */
+ atomic_t commands_outstanding;
# define PERF_MODE_INT 0
# define DOORBELL_INT 1
# define SIMPLE_MODE_INT 2
# define MEMQ_MODE_INT 3
- unsigned int intr[4];
- unsigned int msix_vector;
- unsigned int msi_vector;
+ unsigned int msix_vectors;
int intr_mode; /* either PERF_MODE_INT or SIMPLE_MODE_INT */
struct access_method access;
/* queue and queue Info */
- struct list_head reqQ;
- struct list_head cmpQ;
unsigned int Qdepth;
- unsigned int maxQsinceinit;
unsigned int maxSG;
spinlock_t lock;
int maxsgentries;
u8 max_cmd_sg_entries;
int chainsize;
struct SGDescriptor **cmd_sg_list;
+ struct ioaccel2_sg_element **ioaccel2_cmd_sg_list;
/* pointers to command and error info pool */
struct CommandList *cmd_pool;
dma_addr_t cmd_pool_dhandle;
+ struct io_accel1_cmd *ioaccel_cmd_pool;
+ dma_addr_t ioaccel_cmd_pool_dhandle;
+ struct io_accel2_cmd *ioaccel2_cmd_pool;
+ dma_addr_t ioaccel2_cmd_pool_dhandle;
struct ErrorInfo *errinfo_pool;
dma_addr_t errinfo_pool_dhandle;
unsigned long *cmd_pool_bits;
- int nr_allocs;
- int nr_frees;
- int busy_initializing;
- int busy_scanning;
int scan_finished;
+ u8 scan_waiting : 1;
spinlock_t scan_lock;
wait_queue_head_t scan_wait_queue;
struct Scsi_Host *scsi_host;
spinlock_t devlock; /* to protect hba[ctlr]->dev[]; */
int ndevices; /* number of used elements in .dev[] array. */
-#define HPSA_MAX_SCSI_DEVS_PER_HBA 256
- struct hpsa_scsi_dev_t *dev[HPSA_MAX_SCSI_DEVS_PER_HBA];
+ struct hpsa_scsi_dev_t *dev[HPSA_MAX_DEVICES];
/*
* Performant mode tables.
*/
u32 trans_support;
u32 trans_offset;
- struct TransTable_struct *transtable;
+ struct TransTable_struct __iomem *transtable;
unsigned long transMethod;
+ /* cap concurrent passthrus at some reasonable maximum */
+#define HPSA_MAX_CONCURRENT_PASSTHRUS (10)
+ atomic_t passthru_cmds_avail;
+
/*
- * Performant mode completion buffer
+ * Performant mode completion buffers
*/
- u64 *reply_pool;
- dma_addr_t reply_pool_dhandle;
- u64 *reply_pool_head;
- size_t reply_pool_size;
- unsigned char reply_pool_wraparound;
+ size_t reply_queue_size;
+ struct reply_queue_buffer reply_queue[MAX_REPLY_QUEUES];
+ u8 nreply_queues;
u32 *blockFetchTable;
+ u32 *ioaccel1_blockFetchTable;
+ u32 *ioaccel2_blockFetchTable;
+ u32 __iomem *ioaccel2_bft2_regs;
unsigned char *hba_inquiry_data;
+ u32 driver_support;
+ u32 fw_support;
+ int ioaccel_support;
+ int ioaccel_maxsg;
+ u64 last_intr_timestamp;
+ u32 last_heartbeat;
+ u64 last_heartbeat_timestamp;
+ u32 heartbeat_sample_interval;
+ atomic_t firmware_flash_in_progress;
+ u32 __percpu *lockup_detected;
+ struct delayed_work monitor_ctlr_work;
+ struct delayed_work rescan_ctlr_work;
+ struct delayed_work event_monitor_work;
+ int remove_in_progress;
+ /* Address of h->q[x] is passed to intr handler to know which queue */
+ u8 q[MAX_REPLY_QUEUES];
+ char intrname[MAX_REPLY_QUEUES][16]; /* "hpsa0-msix00" names */
+ u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */
+#define HPSATMF_BITS_SUPPORTED (1 << 0)
+#define HPSATMF_PHYS_LUN_RESET (1 << 1)
+#define HPSATMF_PHYS_NEX_RESET (1 << 2)
+#define HPSATMF_PHYS_TASK_ABORT (1 << 3)
+#define HPSATMF_PHYS_TSET_ABORT (1 << 4)
+#define HPSATMF_PHYS_CLEAR_ACA (1 << 5)
+#define HPSATMF_PHYS_CLEAR_TSET (1 << 6)
+#define HPSATMF_PHYS_QRY_TASK (1 << 7)
+#define HPSATMF_PHYS_QRY_TSET (1 << 8)
+#define HPSATMF_PHYS_QRY_ASYNC (1 << 9)
+#define HPSATMF_IOACCEL_ENABLED (1 << 15)
+#define HPSATMF_MASK_SUPPORTED (1 << 16)
+#define HPSATMF_LOG_LUN_RESET (1 << 17)
+#define HPSATMF_LOG_NEX_RESET (1 << 18)
+#define HPSATMF_LOG_TASK_ABORT (1 << 19)
+#define HPSATMF_LOG_TSET_ABORT (1 << 20)
+#define HPSATMF_LOG_CLEAR_ACA (1 << 21)
+#define HPSATMF_LOG_CLEAR_TSET (1 << 22)
+#define HPSATMF_LOG_QRY_TASK (1 << 23)
+#define HPSATMF_LOG_QRY_TSET (1 << 24)
+#define HPSATMF_LOG_QRY_ASYNC (1 << 25)
+ u32 events;
+#define CTLR_STATE_CHANGE_EVENT (1 << 0)
+#define CTLR_ENCLOSURE_HOT_PLUG_EVENT (1 << 1)
+#define CTLR_STATE_CHANGE_EVENT_PHYSICAL_DRV (1 << 4)
+#define CTLR_STATE_CHANGE_EVENT_LOGICAL_DRV (1 << 5)
+#define CTLR_STATE_CHANGE_EVENT_REDUNDANT_CNTRL (1 << 6)
+#define CTLR_STATE_CHANGE_EVENT_AIO_ENABLED_DISABLED (1 << 30)
+#define CTLR_STATE_CHANGE_EVENT_AIO_CONFIG_CHANGE (1 << 31)
+
+#define RESCAN_REQUIRED_EVENT_BITS \
+ (CTLR_ENCLOSURE_HOT_PLUG_EVENT | \
+ CTLR_STATE_CHANGE_EVENT_PHYSICAL_DRV | \
+ CTLR_STATE_CHANGE_EVENT_LOGICAL_DRV | \
+ CTLR_STATE_CHANGE_EVENT_AIO_ENABLED_DISABLED | \
+ CTLR_STATE_CHANGE_EVENT_AIO_CONFIG_CHANGE)
+ spinlock_t offline_device_lock;
+ struct list_head offline_device_list;
+ int acciopath_status;
+ int drv_req_rescan;
+ int raid_offload_debug;
+ int discovery_polling;
+ int legacy_board;
+ struct ReportLUNdata *lastlogicals;
+ int needs_abort_tags_swizzled;
+ struct workqueue_struct *resubmit_wq;
+ struct workqueue_struct *rescan_ctlr_wq;
+ struct workqueue_struct *monitor_ctlr_wq;
+ atomic_t abort_cmds_available;
+ wait_queue_head_t event_sync_wait_queue;
+ struct mutex reset_mutex;
+ u8 reset_in_progress;
+ struct hpsa_sas_node *sas_host;
+ spinlock_t reset_lock;
+};
+
+struct offline_device_entry {
+ unsigned char scsi3addr[8];
+ struct list_head offline_list;
};
+
#define HPSA_ABORT_MSG 0
#define HPSA_DEVICE_RESET_MSG 1
#define HPSA_RESET_TYPE_CONTROLLER 0x00
#define HPSA_RESET_TYPE_BUS 0x01
-#define HPSA_RESET_TYPE_TARGET 0x03
#define HPSA_RESET_TYPE_LUN 0x04
+#define HPSA_PHYS_TARGET_RESET 0x99 /* not defined by cciss spec */
#define HPSA_MSG_SEND_RETRY_LIMIT 10
#define HPSA_MSG_SEND_RETRY_INTERVAL_MSECS (10000)
@@ -176,6 +369,8 @@ struct ctlr_info {
*/
#define SA5_DOORBELL 0x20
#define SA5_REQUEST_PORT_OFFSET 0x40
+#define SA5_REQUEST_PORT64_LO_OFFSET 0xC0
+#define SA5_REQUEST_PORT64_HI_OFFSET 0xC4
#define SA5_REPLY_INTR_MASK_OFFSET 0x34
#define SA5_REPLY_PORT_OFFSET 0x44
#define SA5_INTR_STATUS 0x30
@@ -205,19 +400,40 @@ struct ctlr_info {
#define HPSA_INTR_ON 1
#define HPSA_INTR_OFF 0
+
+/*
+ * Inbound Post Queue offsets for IO Accelerator Mode 2
+ */
+#define IOACCEL2_INBOUND_POSTQ_32 0x48
+#define IOACCEL2_INBOUND_POSTQ_64_LOW 0xd0
+#define IOACCEL2_INBOUND_POSTQ_64_HI 0xd4
+
+#define HPSA_PHYSICAL_DEVICE_BUS 0
+#define HPSA_RAID_VOLUME_BUS 1
+#define HPSA_EXTERNAL_RAID_VOLUME_BUS 2
+#define HPSA_HBA_BUS 0
+#define HPSA_LEGACY_HBA_BUS 3
+
/*
Send the command to the hardware
*/
static void SA5_submit_command(struct ctlr_info *h,
struct CommandList *c)
{
- dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr,
- c->Header.Tag.lower);
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
- (void) readl(h->vaddr + SA5_REQUEST_PORT_OFFSET);
- h->commands_outstanding++;
- if (h->commands_outstanding > h->max_outstanding)
- h->max_outstanding = h->commands_outstanding;
+ (void) readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
+}
+
+static void SA5_submit_command_no_read(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
+}
+
+static void SA5_submit_command_ioaccel2(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
}
/*
@@ -239,6 +455,23 @@ static void SA5_intr_mask(struct ctlr_info *h, unsigned long val)
}
}
+/*
+ * Variant of the above; 0x04 turns interrupts off...
+ */
+static void SA5B_intr_mask(struct ctlr_info *h, unsigned long val)
+{
+ if (val) { /* Turn interrupts on */
+ h->interrupts_enabled = 1;
+ writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ (void) readl(h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ } else { /* Turn them off */
+ h->interrupts_enabled = 0;
+ writel(SA5B_INTR_OFF,
+ h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ (void) readl(h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ }
+}
+
static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val)
{
if (val) { /* turn on interrupts */
@@ -253,69 +486,58 @@ static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val)
}
}
-static unsigned long SA5_performant_completed(struct ctlr_info *h)
+static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q)
{
+ struct reply_queue_buffer *rq = &h->reply_queue[q];
unsigned long register_value = FIFO_EMPTY;
- /* flush the controller write of the reply queue by reading
- * outbound doorbell status register.
- */
- register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
/* msi auto clears the interrupt pending bit. */
- if (!(h->msi_vector || h->msix_vector)) {
+ if (unlikely(!(h->pdev->msi_enabled || h->msix_vectors))) {
+ /* flush the controller write of the reply queue by reading
+ * outbound doorbell status register.
+ */
+ (void) readl(h->vaddr + SA5_OUTDB_STATUS);
writel(SA5_OUTDB_CLEAR_PERF_BIT, h->vaddr + SA5_OUTDB_CLEAR);
/* Do a read in order to flush the write to the controller
* (as per spec.)
*/
- register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
+ (void) readl(h->vaddr + SA5_OUTDB_STATUS);
}
- if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
- register_value = *(h->reply_pool_head);
- (h->reply_pool_head)++;
- h->commands_outstanding--;
+ if ((((u32) rq->head[rq->current_entry]) & 1) == rq->wraparound) {
+ register_value = rq->head[rq->current_entry];
+ rq->current_entry++;
+ atomic_dec(&h->commands_outstanding);
} else {
register_value = FIFO_EMPTY;
}
/* Check for wraparound */
- if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
- h->reply_pool_head = h->reply_pool;
- h->reply_pool_wraparound ^= 1;
+ if (rq->current_entry == h->max_commands) {
+ rq->current_entry = 0;
+ rq->wraparound ^= 1;
}
-
return register_value;
}
/*
- * Returns true if fifo is full.
- *
- */
-static unsigned long SA5_fifo_full(struct ctlr_info *h)
-{
- if (h->commands_outstanding >= h->max_commands)
- return 1;
- else
- return 0;
-
-}
-/*
* returns value read from hardware.
* returns FIFO_EMPTY if there is nothing to read
*/
-static unsigned long SA5_completed(struct ctlr_info *h)
+static unsigned long SA5_completed(struct ctlr_info *h,
+ __attribute__((unused)) u8 q)
{
unsigned long register_value
= readl(h->vaddr + SA5_REPLY_PORT_OFFSET);
if (register_value != FIFO_EMPTY)
- h->commands_outstanding--;
+ atomic_dec(&h->commands_outstanding);
#ifdef HPSA_DEBUG
if (register_value != FIFO_EMPTY)
dev_dbg(&h->pdev->dev, "Read %lx back from board\n",
register_value);
else
- dev_dbg(&h->pdev->dev, "hpsa: FIFO Empty read\n");
+ dev_dbg(&h->pdev->dev, "FIFO Empty read\n");
#endif
return register_value;
@@ -327,7 +549,6 @@ static bool SA5_intr_pending(struct ctlr_info *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
- dev_dbg(&h->pdev->dev, "intr_pending %lx\n", register_value);
return register_value & SA5_INTR_PENDING;
}
@@ -338,28 +559,108 @@ static bool SA5_performant_intr_pending(struct ctlr_info *h)
if (!register_value)
return false;
- if (h->msi_vector || h->msix_vector)
- return true;
-
/* Read outbound doorbell to flush */
register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
return register_value & SA5_OUTDB_STATUS_PERF_BIT;
}
+#define SA5_IOACCEL_MODE1_INTR_STATUS_CMP_BIT 0x100
+
+static bool SA5_ioaccel_mode1_intr_pending(struct ctlr_info *h)
+{
+ unsigned long register_value = readl(h->vaddr + SA5_INTR_STATUS);
+
+ return (register_value & SA5_IOACCEL_MODE1_INTR_STATUS_CMP_BIT) ?
+ true : false;
+}
+
+/*
+ * Returns true if an interrupt is pending..
+ */
+static bool SA5B_intr_pending(struct ctlr_info *h)
+{
+ return readl(h->vaddr + SA5_INTR_STATUS) & SA5B_INTR_PENDING;
+}
+
+#define IOACCEL_MODE1_REPLY_QUEUE_INDEX 0x1A0
+#define IOACCEL_MODE1_PRODUCER_INDEX 0x1B8
+#define IOACCEL_MODE1_CONSUMER_INDEX 0x1BC
+#define IOACCEL_MODE1_REPLY_UNUSED 0xFFFFFFFFFFFFFFFFULL
+
+static unsigned long SA5_ioaccel_mode1_completed(struct ctlr_info *h, u8 q)
+{
+ u64 register_value;
+ struct reply_queue_buffer *rq = &h->reply_queue[q];
+
+ BUG_ON(q >= h->nreply_queues);
+
+ register_value = rq->head[rq->current_entry];
+ if (register_value != IOACCEL_MODE1_REPLY_UNUSED) {
+ rq->head[rq->current_entry] = IOACCEL_MODE1_REPLY_UNUSED;
+ if (++rq->current_entry == rq->size)
+ rq->current_entry = 0;
+ /*
+ * @todo
+ *
+ * Don't really need to write the new index after each command,
+ * but with current driver design this is easiest.
+ */
+ wmb();
+ writel((q << 24) | rq->current_entry, h->vaddr +
+ IOACCEL_MODE1_CONSUMER_INDEX);
+ atomic_dec(&h->commands_outstanding);
+ }
+ return (unsigned long) register_value;
+}
+
static struct access_method SA5_access = {
- SA5_submit_command,
- SA5_intr_mask,
- SA5_fifo_full,
- SA5_intr_pending,
- SA5_completed,
+ .submit_command = SA5_submit_command,
+ .set_intr_mask = SA5_intr_mask,
+ .intr_pending = SA5_intr_pending,
+ .command_completed = SA5_completed,
+};
+
+/* Duplicate entry of the above to mark unsupported boards */
+static struct access_method SA5A_access = {
+ .submit_command = SA5_submit_command,
+ .set_intr_mask = SA5_intr_mask,
+ .intr_pending = SA5_intr_pending,
+ .command_completed = SA5_completed,
+};
+
+static struct access_method SA5B_access = {
+ .submit_command = SA5_submit_command,
+ .set_intr_mask = SA5B_intr_mask,
+ .intr_pending = SA5B_intr_pending,
+ .command_completed = SA5_completed,
+};
+
+static struct access_method SA5_ioaccel_mode1_access = {
+ .submit_command = SA5_submit_command,
+ .set_intr_mask = SA5_performant_intr_mask,
+ .intr_pending = SA5_ioaccel_mode1_intr_pending,
+ .command_completed = SA5_ioaccel_mode1_completed,
+};
+
+static struct access_method SA5_ioaccel_mode2_access = {
+ .submit_command = SA5_submit_command_ioaccel2,
+ .set_intr_mask = SA5_performant_intr_mask,
+ .intr_pending = SA5_performant_intr_pending,
+ .command_completed = SA5_performant_completed,
};
static struct access_method SA5_performant_access = {
- SA5_submit_command,
- SA5_performant_intr_mask,
- SA5_fifo_full,
- SA5_performant_intr_pending,
- SA5_performant_completed,
+ .submit_command = SA5_submit_command,
+ .set_intr_mask = SA5_performant_intr_mask,
+ .intr_pending = SA5_performant_intr_pending,
+ .command_completed = SA5_performant_completed,
+};
+
+static struct access_method SA5_performant_access_no_read = {
+ .submit_command = SA5_submit_command_no_read,
+ .set_intr_mask = SA5_performant_intr_mask,
+ .intr_pending = SA5_performant_intr_pending,
+ .command_completed = SA5_performant_completed,
};
struct board_type {
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index 55d741b019db..ba6a3aa8d954 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -1,6 +1,9 @@
/*
* Disk Array driver for HP Smart Array SAS controllers
- * Copyright 2000, 2009 Hewlett-Packard Development Company, L.P.
+ * Copyright (c) 2019-2020 Microchip Technology Inc. and its subsidiaries
+ * Copyright 2016 Microsemi Corporation
+ * Copyright 2014-2015 PMC-Sierra, Inc.
+ * Copyright 2000,2009-2015 Hewlett-Packard Development Company, L.P.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,20 +14,22 @@
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Questions/Comments/Bugfixes to iss_storagedev@hp.com
+ * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com
*
*/
#ifndef HPSA_CMD_H
#define HPSA_CMD_H
+#include <linux/compiler.h>
+
+#include <linux/build_bug.h> /* static_assert */
+#include <linux/stddef.h> /* offsetof */
+
/* general boundary defintions */
#define SENSEINFOBYTES 32 /* may vary between hbas */
-#define MAXSGENTRIES 32
+#define SG_ENTRIES_IN_CMD 32 /* Max SG entries excluding chain blocks */
#define HPSA_SG_CHAIN 0x80000000
+#define HPSA_SG_LAST 0x40000000
#define MAXREPLYQS 256
/* Command Status value */
@@ -41,6 +46,22 @@
#define CMD_UNSOLICITED_ABORT 0x000A
#define CMD_TIMEOUT 0x000B
#define CMD_UNABORTABLE 0x000C
+#define CMD_TMF_STATUS 0x000D
+#define CMD_IOACCEL_DISABLED 0x000E
+#define CMD_CTLR_LOCKUP 0xffff
+/* Note: CMD_CTLR_LOCKUP is not a value defined by the CISS spec
+ * it is a value defined by the driver that commands can be marked
+ * with when a controller lockup has been detected by the driver
+ */
+
+/* TMF function status values */
+#define CISS_TMF_COMPLETE 0x00
+#define CISS_TMF_INVALID_FRAME 0x02
+#define CISS_TMF_NOT_SUPPORTED 0x04
+#define CISS_TMF_FAILED 0x05
+#define CISS_TMF_SUCCESS 0x08
+#define CISS_TMF_WRONG_LUN 0x09
+#define CISS_TMF_OVERLAPPED_TAG 0x0a
/* Unit Attentions ASC's as defined for the MSA2012sa */
#define POWER_OR_RESET 0x29
@@ -79,8 +100,32 @@
#define ATTR_ACA 0x07
/* cdb type */
-#define TYPE_CMD 0x00
-#define TYPE_MSG 0x01
+#define TYPE_CMD 0x00
+#define TYPE_MSG 0x01
+#define TYPE_IOACCEL2_CMD 0x81 /* 0x81 is not used by hardware */
+
+/* Message Types */
+#define HPSA_TASK_MANAGEMENT 0x00
+#define HPSA_RESET 0x01
+#define HPSA_SCAN 0x02
+#define HPSA_NOOP 0x03
+
+#define HPSA_CTLR_RESET_TYPE 0x00
+#define HPSA_BUS_RESET_TYPE 0x01
+#define HPSA_TARGET_RESET_TYPE 0x03
+#define HPSA_LUN_RESET_TYPE 0x04
+#define HPSA_NEXUS_RESET_TYPE 0x05
+
+/* Task Management Functions */
+#define HPSA_TMF_ABORT_TASK 0x00
+#define HPSA_TMF_ABORT_TASK_SET 0x01
+#define HPSA_TMF_CLEAR_ACA 0x02
+#define HPSA_TMF_CLEAR_TASK_SET 0x03
+#define HPSA_TMF_QUERY_TASK 0x04
+#define HPSA_TMF_QUERY_TASK_SET 0x05
+#define HPSA_TMF_QUERY_ASYNCEVENT 0x06
+
+
/* config space register offsets */
#define CFG_VENDORID 0x00
@@ -102,15 +147,46 @@
#define CFGTBL_AccCmds 0x00000001l
#define DOORBELL_CTLR_RESET 0x00000004l
#define DOORBELL_CTLR_RESET2 0x00000020l
+#define DOORBELL_CLEAR_EVENTS 0x00000040l
+#define DOORBELL_GENERATE_CHKPT 0x00000080l
#define CFGTBL_Trans_Simple 0x00000002l
#define CFGTBL_Trans_Performant 0x00000004l
+#define CFGTBL_Trans_io_accel1 0x00000080l
+#define CFGTBL_Trans_io_accel2 0x00000100l
#define CFGTBL_Trans_use_short_tags 0x20000000l
+#define CFGTBL_Trans_enable_directed_msix (1 << 30)
#define CFGTBL_BusType_Ultra2 0x00000001l
#define CFGTBL_BusType_Ultra3 0x00000002l
#define CFGTBL_BusType_Fibre1G 0x00000100l
#define CFGTBL_BusType_Fibre2G 0x00000200l
+
+/* VPD Inquiry types */
+#define HPSA_INQUIRY_FAILED 0x02
+#define HPSA_VPD_SUPPORTED_PAGES 0x00
+#define HPSA_VPD_LV_DEVICE_ID 0x83
+#define HPSA_VPD_LV_DEVICE_GEOMETRY 0xC1
+#define HPSA_VPD_LV_IOACCEL_STATUS 0xC2
+#define HPSA_VPD_LV_STATUS 0xC3
+#define HPSA_VPD_HEADER_SZ 4
+
+/* Logical volume states */
+#define HPSA_VPD_LV_STATUS_UNSUPPORTED 0xff
+#define HPSA_LV_OK 0x0
+#define HPSA_LV_FAILED 0x01
+#define HPSA_LV_NOT_AVAILABLE 0x0b
+#define HPSA_LV_UNDERGOING_ERASE 0x0F
+#define HPSA_LV_UNDERGOING_RPI 0x12
+#define HPSA_LV_PENDING_RPI 0x13
+#define HPSA_LV_ENCRYPTED_NO_KEY 0x14
+#define HPSA_LV_PLAINTEXT_IN_ENCRYPT_ONLY_CONTROLLER 0x15
+#define HPSA_LV_UNDERGOING_ENCRYPTION 0x16
+#define HPSA_LV_UNDERGOING_ENCRYPTION_REKEYING 0x17
+#define HPSA_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER 0x18
+#define HPSA_LV_PENDING_ENCRYPTION 0x19
+#define HPSA_LV_PENDING_ENCRYPTION_REKEYING 0x1A
+
struct vals32 {
u32 lower;
u32 upper;
@@ -122,44 +198,109 @@ union u64bit {
};
/* FIXME this is a per controller value (barf!) */
-#define HPSA_MAX_TARGETS_PER_CTLR 16
-#define HPSA_MAX_LUN 256
+#define HPSA_MAX_LUN 1024
#define HPSA_MAX_PHYS_LUN 1024
+#define MAX_EXT_TARGETS 32
+#define HPSA_MAX_DEVICES (HPSA_MAX_PHYS_LUN + HPSA_MAX_LUN + \
+ MAX_EXT_TARGETS + 1) /* + 1 is for the controller itself */
/* SCSI-3 Commands */
-#pragma pack(1)
-
#define HPSA_INQUIRY 0x12
struct InquiryData {
u8 data_byte[36];
-};
+} __packed;
#define HPSA_REPORT_LOG 0xc2 /* Report Logical LUNs */
#define HPSA_REPORT_PHYS 0xc3 /* Report Physical LUNs */
+#define HPSA_REPORT_PHYS_EXTENDED 0x02
+#define HPSA_CISS_READ 0xc0 /* CISS Read */
+#define HPSA_GET_RAID_MAP 0xc8 /* CISS Get RAID Layout Map */
+
+#define RAID_MAP_MAX_ENTRIES 256
+
+struct raid_map_disk_data {
+ u32 ioaccel_handle; /**< Handle to access this disk via the
+ * I/O accelerator */
+ u8 xor_mult[2]; /**< XOR multipliers for this position,
+ * valid for data disks only */
+ u8 reserved[2];
+} __packed;
+
+struct raid_map_data {
+ __le32 structure_size; /* Size of entire structure in bytes */
+ __le32 volume_blk_size; /* bytes / block in the volume */
+ __le64 volume_blk_cnt; /* logical blocks on the volume */
+ u8 phys_blk_shift; /* Shift factor to convert between
+ * units of logical blocks and physical
+ * disk blocks */
+ u8 parity_rotation_shift; /* Shift factor to convert between units
+ * of logical stripes and physical
+ * stripes */
+ __le16 strip_size; /* blocks used on each disk / stripe */
+ __le64 disk_starting_blk; /* First disk block used in volume */
+ __le64 disk_blk_cnt; /* disk blocks used by volume / disk */
+ __le16 data_disks_per_row; /* data disk entries / row in the map */
+ __le16 metadata_disks_per_row;/* mirror/parity disk entries / row
+ * in the map */
+ __le16 row_cnt; /* rows in each layout map */
+ __le16 layout_map_count; /* layout maps (1 map per mirror/parity
+ * group) */
+ __le16 flags; /* Bit 0 set if encryption enabled */
+#define RAID_MAP_FLAG_ENCRYPT_ON 0x01
+ __le16 dekindex; /* Data encryption key index. */
+ u8 reserved[16];
+ struct raid_map_disk_data data[RAID_MAP_MAX_ENTRIES];
+} __packed;
+
struct ReportLUNdata {
u8 LUNListLength[4];
- u32 reserved;
+ u8 extended_response_flag;
+ u8 reserved[3];
u8 LUN[HPSA_MAX_LUN][8];
-};
+} __packed;
+
+struct ext_report_lun_entry {
+ u8 lunid[8];
+#define MASKED_DEVICE(x) ((x)[3] & 0xC0)
+#define GET_BMIC_BUS(lunid) ((lunid)[7] & 0x3F)
+#define GET_BMIC_LEVEL_TWO_TARGET(lunid) ((lunid)[6])
+#define GET_BMIC_DRIVE_NUMBER(lunid) (((GET_BMIC_BUS((lunid)) - 1) << 8) + \
+ GET_BMIC_LEVEL_TWO_TARGET((lunid)))
+ u8 wwid[8];
+ u8 device_type;
+ u8 device_flags;
+ u8 lun_count; /* multi-lun device, how many luns */
+ u8 redundant_paths;
+ u32 ioaccel_handle; /* ioaccel1 only uses lower 16 bits */
+} __packed;
struct ReportExtendedLUNdata {
u8 LUNListLength[4];
u8 extended_response_flag;
u8 reserved[3];
- u8 LUN[HPSA_MAX_LUN][24];
-};
+ struct ext_report_lun_entry LUN[HPSA_MAX_PHYS_LUN];
+} __packed;
struct SenseSubsystem_info {
u8 reserved[36];
u8 portname[8];
u8 reserved1[1108];
-};
+} __packed;
/* BMIC commands */
#define BMIC_READ 0x26
#define BMIC_WRITE 0x27
#define BMIC_CACHE_FLUSH 0xc2
#define HPSA_CACHE_FLUSH 0x01 /* C2 was already being used by HPSA */
+#define BMIC_FLASH_FIRMWARE 0xF7
+#define BMIC_SENSE_CONTROLLER_PARAMETERS 0x64
+#define BMIC_IDENTIFY_PHYSICAL_DEVICE 0x15
+#define BMIC_IDENTIFY_CONTROLLER 0x11
+#define BMIC_SET_DIAG_OPTIONS 0xF4
+#define BMIC_SENSE_DIAG_OPTIONS 0xF5
+#define HPSA_DIAG_OPTS_DISABLE_RLD_CACHING 0x80000000
+#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66
+#define BMIC_SENSE_STORAGE_BOX_PARAMS 0x65
/* Command List Structure */
union SCSI3Addr {
@@ -179,7 +320,7 @@ union SCSI3Addr {
u8 Targ:6;
u8 Mode:2; /* b10 */
} LogUnit;
-};
+} __packed;
struct PhysDevAddr {
u32 TargetId:24;
@@ -187,50 +328,58 @@ struct PhysDevAddr {
u32 Mode:2;
/* 2 level target device addr */
union SCSI3Addr Target[2];
-};
+} __packed;
struct LogDevAddr {
u32 VolId:30;
u32 Mode:2;
u8 reserved[4];
-};
+} __packed;
union LUNAddr {
u8 LunAddrBytes[8];
union SCSI3Addr SCSI3Lun[4];
struct PhysDevAddr PhysDev;
struct LogDevAddr LogDev;
-};
+} __packed;
struct CommandListHeader {
u8 ReplyQueue;
u8 SGList;
- u16 SGTotal;
- struct vals32 Tag;
+ __le16 SGTotal;
+ __le64 tag;
union LUNAddr LUN;
-};
+} __packed;
struct RequestBlock {
u8 CDBLen;
- struct {
- u8 Type:3;
- u8 Attribute:3;
- u8 Direction:2;
- } Type;
+ /*
+ * type_attr_dir:
+ * type: low 3 bits
+ * attr: middle 3 bits
+ * dir: high 2 bits
+ */
+ u8 type_attr_dir;
+#define TYPE_ATTR_DIR(t, a, d) ((((d) & 0x03) << 6) |\
+ (((a) & 0x07) << 3) |\
+ ((t) & 0x07))
+#define GET_TYPE(tad) ((tad) & 0x07)
+#define GET_ATTR(tad) (((tad) >> 3) & 0x07)
+#define GET_DIR(tad) (((tad) >> 6) & 0x03)
u16 Timeout;
u8 CDB[16];
-};
+} __packed;
struct ErrDescriptor {
- struct vals32 Addr;
- u32 Len;
-};
+ __le64 Addr;
+ __le32 Len;
+} __packed;
struct SGDescriptor {
- struct vals32 Addr;
- u32 Len;
- u32 Ext;
-};
+ __le64 Addr;
+ __le32 Len;
+ __le32 Ext;
+} __packed;
union MoreErrInfo {
struct {
@@ -244,7 +393,8 @@ union MoreErrInfo {
u8 offense_num; /* byte # of offense 0-base */
u32 offense_value;
} Invalid_Cmd;
-};
+} __packed;
+
struct ErrorInfo {
u8 ScsiStatus;
u8 SenseLen;
@@ -252,113 +402,487 @@ struct ErrorInfo {
u32 ResidualCnt;
union MoreErrInfo MoreErrInfo;
u8 SenseInfo[SENSEINFOBYTES];
-};
+} __packed;
/* Command types */
#define CMD_IOCTL_PEND 0x01
#define CMD_SCSI 0x03
+#define CMD_IOACCEL1 0x04
+#define CMD_IOACCEL2 0x05
+#define IOACCEL2_TMF 0x06
-#define DIRECT_LOOKUP_SHIFT 5
-#define DIRECT_LOOKUP_BIT 0x10
+#define DIRECT_LOOKUP_SHIFT 4
#define DIRECT_LOOKUP_MASK (~((1 << DIRECT_LOOKUP_SHIFT) - 1))
#define HPSA_ERROR_BIT 0x02
struct ctlr_info; /* defined in hpsa.h */
-/* The size of this structure needs to be divisible by 32
- * on all architectures because low 5 bits of the addresses
+/* The size of this structure needs to be divisible by 128
+ * on all architectures. The low 4 bits of the addresses
* are used as follows:
*
* bit 0: to device, used to indicate "performant mode" command
* from device, indidcates error status.
* bit 1-3: to device, indicates block fetch table entry for
* reducing DMA in fetching commands from host memory.
- * bit 4: used to indicate whether tag is "direct lookup" (index),
- * or a bus address.
*/
+#define COMMANDLIST_ALIGNMENT 128
struct CommandList {
struct CommandListHeader Header;
struct RequestBlock Request;
struct ErrDescriptor ErrDesc;
- struct SGDescriptor SG[MAXSGENTRIES];
+ struct SGDescriptor SG[SG_ENTRIES_IN_CMD];
/* information associated with the command */
u32 busaddr; /* physical addr of this record */
struct ErrorInfo *err_info; /* pointer to the allocated mem */
struct ctlr_info *h;
int cmd_type;
long cmdindex;
- struct list_head list;
- struct request *rq;
struct completion *waiting;
- void *scsi_cmd;
-
-/* on 64 bit architectures, to get this to be 32-byte-aligned
- * it so happens we need PAD_64 bytes of padding, on 32 bit systems,
- * we need PAD_32 bytes of padding (see below). This does that.
- * If it happens that 64 bit and 32 bit systems need different
- * padding, PAD_32 and PAD_64 can be set independently, and.
- * the code below will do the right thing.
+ struct scsi_cmnd *scsi_cmd;
+ struct work_struct work;
+
+ /*
+ * For commands using either of the two "ioaccel" paths to
+ * bypass the RAID stack and go directly to the physical disk
+ * phys_disk is a pointer to the hpsa_scsi_dev_t to which the
+ * i/o is destined. We need to store that here because the command
+ * may potentially encounter TASK SET FULL and need to be resubmitted
+ * For "normal" i/o's not using the "ioaccel" paths, phys_disk is
+ * not used.
+ */
+ struct hpsa_scsi_dev_t *phys_disk;
+
+ bool retry_pending;
+ struct hpsa_scsi_dev_t *device;
+ atomic_t refcount; /* Must be last to avoid memset in hpsa_cmd_init() */
+} __aligned(COMMANDLIST_ALIGNMENT);
+
+/*
+ * Make sure our embedded atomic variable is aligned. Otherwise we break atomic
+ * operations on architectures that don't support unaligned atomics like IA64.
+ *
+ * The assert guards against reintroductin against unwanted __packed to
+ * the struct CommandList.
*/
-#define IS_32_BIT ((8 - sizeof(long))/4)
-#define IS_64_BIT (!IS_32_BIT)
-#define PAD_32 (4)
-#define PAD_64 (4)
-#define COMMANDLIST_PAD (IS_32_BIT * PAD_32 + IS_64_BIT * PAD_64)
- u8 pad[COMMANDLIST_PAD];
-};
+static_assert(offsetof(struct CommandList, refcount) % __alignof__(atomic_t) == 0);
+
+/* Max S/G elements in I/O accelerator command */
+#define IOACCEL1_MAXSGENTRIES 24
+#define IOACCEL2_MAXSGENTRIES 28
+
+/*
+ * Structure for I/O accelerator (mode 1) commands.
+ * Note that this structure must be 128-byte aligned in size.
+ */
+#define IOACCEL1_COMMANDLIST_ALIGNMENT 128
+struct io_accel1_cmd {
+ __le16 dev_handle; /* 0x00 - 0x01 */
+ u8 reserved1; /* 0x02 */
+ u8 function; /* 0x03 */
+ u8 reserved2[8]; /* 0x04 - 0x0B */
+ u32 err_info; /* 0x0C - 0x0F */
+ u8 reserved3[2]; /* 0x10 - 0x11 */
+ u8 err_info_len; /* 0x12 */
+ u8 reserved4; /* 0x13 */
+ u8 sgl_offset; /* 0x14 */
+ u8 reserved5[7]; /* 0x15 - 0x1B */
+ __le32 transfer_len; /* 0x1C - 0x1F */
+ u8 reserved6[4]; /* 0x20 - 0x23 */
+ __le16 io_flags; /* 0x24 - 0x25 */
+ u8 reserved7[14]; /* 0x26 - 0x33 */
+ u8 LUN[8]; /* 0x34 - 0x3B */
+ __le32 control; /* 0x3C - 0x3F */
+ u8 CDB[16]; /* 0x40 - 0x4F */
+ u8 reserved8[16]; /* 0x50 - 0x5F */
+ __le16 host_context_flags; /* 0x60 - 0x61 */
+ __le16 timeout_sec; /* 0x62 - 0x63 */
+ u8 ReplyQueue; /* 0x64 */
+ u8 reserved9[3]; /* 0x65 - 0x67 */
+ __le64 tag; /* 0x68 - 0x6F */
+ __le64 host_addr; /* 0x70 - 0x77 */
+ u8 CISS_LUN[8]; /* 0x78 - 0x7F */
+ struct SGDescriptor SG[IOACCEL1_MAXSGENTRIES];
+} __packed __aligned(IOACCEL1_COMMANDLIST_ALIGNMENT);
+
+#define IOACCEL1_FUNCTION_SCSIIO 0x00
+#define IOACCEL1_SGLOFFSET 32
+
+#define IOACCEL1_IOFLAGS_IO_REQ 0x4000
+#define IOACCEL1_IOFLAGS_CDBLEN_MASK 0x001F
+#define IOACCEL1_IOFLAGS_CDBLEN_MAX 16
+
+#define IOACCEL1_CONTROL_NODATAXFER 0x00000000
+#define IOACCEL1_CONTROL_DATA_OUT 0x01000000
+#define IOACCEL1_CONTROL_DATA_IN 0x02000000
+#define IOACCEL1_CONTROL_TASKPRIO_MASK 0x00007800
+#define IOACCEL1_CONTROL_TASKPRIO_SHIFT 11
+#define IOACCEL1_CONTROL_SIMPLEQUEUE 0x00000000
+#define IOACCEL1_CONTROL_HEADOFQUEUE 0x00000100
+#define IOACCEL1_CONTROL_ORDEREDQUEUE 0x00000200
+#define IOACCEL1_CONTROL_ACA 0x00000400
+
+#define IOACCEL1_HCFLAGS_CISS_FORMAT 0x0013
+
+#define IOACCEL1_BUSADDR_CMDTYPE 0x00000060
+
+struct ioaccel2_sg_element {
+ __le64 address;
+ __le32 length;
+ u8 reserved[3];
+ u8 chain_indicator;
+#define IOACCEL2_CHAIN 0x80
+#define IOACCEL2_LAST_SG 0x40
+} __packed;
+
+/*
+ * SCSI Response Format structure for IO Accelerator Mode 2
+ */
+struct io_accel2_scsi_response {
+ u8 IU_type;
+#define IOACCEL2_IU_TYPE_SRF 0x60
+ u8 reserved1[3];
+ u8 req_id[4]; /* request identifier */
+ u8 reserved2[4];
+ u8 serv_response; /* service response */
+#define IOACCEL2_SERV_RESPONSE_COMPLETE 0x000
+#define IOACCEL2_SERV_RESPONSE_FAILURE 0x001
+#define IOACCEL2_SERV_RESPONSE_TMF_COMPLETE 0x002
+#define IOACCEL2_SERV_RESPONSE_TMF_SUCCESS 0x003
+#define IOACCEL2_SERV_RESPONSE_TMF_REJECTED 0x004
+#define IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN 0x005
+ u8 status; /* status */
+#define IOACCEL2_STATUS_SR_TASK_COMP_GOOD 0x00
+#define IOACCEL2_STATUS_SR_TASK_COMP_CHK_COND 0x02
+#define IOACCEL2_STATUS_SR_TASK_COMP_BUSY 0x08
+#define IOACCEL2_STATUS_SR_TASK_COMP_RES_CON 0x18
+#define IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL 0x28
+#define IOACCEL2_STATUS_SR_TASK_COMP_ABORTED 0x40
+#define IOACCEL2_STATUS_SR_IOACCEL_DISABLED 0x0E
+#define IOACCEL2_STATUS_SR_IO_ERROR 0x01
+#define IOACCEL2_STATUS_SR_IO_ABORTED 0x02
+#define IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE 0x03
+#define IOACCEL2_STATUS_SR_INVALID_DEVICE 0x04
+#define IOACCEL2_STATUS_SR_UNDERRUN 0x51
+#define IOACCEL2_STATUS_SR_OVERRUN 0x75
+ u8 data_present; /* low 2 bits */
+#define IOACCEL2_NO_DATAPRESENT 0x000
+#define IOACCEL2_RESPONSE_DATAPRESENT 0x001
+#define IOACCEL2_SENSE_DATA_PRESENT 0x002
+#define IOACCEL2_RESERVED 0x003
+ u8 sense_data_len; /* sense/response data length */
+ u8 resid_cnt[4]; /* residual count */
+ u8 sense_data_buff[32]; /* sense/response data buffer */
+} __packed;
+
+/*
+ * Structure for I/O accelerator (mode 2 or m2) commands.
+ * Note that this structure must be 128-byte aligned in size.
+ */
+#define IOACCEL2_COMMANDLIST_ALIGNMENT 128
+struct io_accel2_cmd {
+ u8 IU_type; /* IU Type */
+ u8 direction; /* direction, memtype, and encryption */
+#define IOACCEL2_DIRECTION_MASK 0x03 /* bits 0,1: direction */
+#define IOACCEL2_DIRECTION_MEMTYPE_MASK 0x04 /* bit 2: memtype source/dest */
+ /* 0b=PCIe, 1b=DDR */
+#define IOACCEL2_DIRECTION_ENCRYPT_MASK 0x08 /* bit 3: encryption flag */
+ /* 0=off, 1=on */
+ u8 reply_queue; /* Reply Queue ID */
+ u8 reserved1; /* Reserved */
+ __le32 scsi_nexus; /* Device Handle */
+ __le32 Tag; /* cciss tag, lower 4 bytes only */
+ __le32 tweak_lower; /* Encryption tweak, lower 4 bytes */
+ u8 cdb[16]; /* SCSI Command Descriptor Block */
+ u8 cciss_lun[8]; /* 8 byte SCSI address */
+ __le32 data_len; /* Total bytes to transfer */
+ u8 cmd_priority_task_attr; /* priority and task attrs */
+#define IOACCEL2_PRIORITY_MASK 0x78
+#define IOACCEL2_ATTR_MASK 0x07
+ u8 sg_count; /* Number of sg elements */
+ __le16 dekindex; /* Data encryption key index */
+ __le64 err_ptr; /* Error Pointer */
+ __le32 err_len; /* Error Length*/
+ __le32 tweak_upper; /* Encryption tweak, upper 4 bytes */
+ struct ioaccel2_sg_element sg[IOACCEL2_MAXSGENTRIES];
+ struct io_accel2_scsi_response error_data;
+} __packed __aligned(IOACCEL2_COMMANDLIST_ALIGNMENT);
+
+/*
+ * defines for Mode 2 command struct
+ * FIXME: this can't be all I need mfm
+ */
+#define IOACCEL2_IU_TYPE 0x40
+#define IOACCEL2_IU_TMF_TYPE 0x41
+#define IOACCEL2_DIR_NO_DATA 0x00
+#define IOACCEL2_DIR_DATA_IN 0x01
+#define IOACCEL2_DIR_DATA_OUT 0x02
+#define IOACCEL2_TMF_ABORT 0x01
+/*
+ * SCSI Task Management Request format for Accelerator Mode 2
+ */
+struct hpsa_tmf_struct {
+ u8 iu_type; /* Information Unit Type */
+ u8 reply_queue; /* Reply Queue ID */
+ u8 tmf; /* Task Management Function */
+ u8 reserved1; /* byte 3 Reserved */
+ __le32 it_nexus; /* SCSI I-T Nexus */
+ u8 lun_id[8]; /* LUN ID for TMF request */
+ __le64 tag; /* cciss tag associated w/ request */
+ __le64 abort_tag; /* cciss tag of SCSI cmd or TMF to abort */
+ __le64 error_ptr; /* Error Pointer */
+ __le32 error_len; /* Error Length */
+} __packed __aligned(IOACCEL2_COMMANDLIST_ALIGNMENT);
/* Configuration Table Structure */
struct HostWrite {
- u32 TransportRequest;
- u32 Reserved;
- u32 CoalIntDelay;
- u32 CoalIntCount;
-};
+ __le32 TransportRequest;
+ __le32 command_pool_addr_hi;
+ __le32 CoalIntDelay;
+ __le32 CoalIntCount;
+} __packed;
#define SIMPLE_MODE 0x02
#define PERFORMANT_MODE 0x04
#define MEMQ_MODE 0x08
+#define IOACCEL_MODE_1 0x80
+
+#define DRIVER_SUPPORT_UA_ENABLE 0x00000001
struct CfgTable {
- u8 Signature[4];
- u32 SpecValence;
- u32 TransportSupport;
- u32 TransportActive;
- struct HostWrite HostWrite;
- u32 CmdsOutMax;
- u32 BusTypes;
- u32 TransMethodOffset;
- u8 ServerName[16];
- u32 HeartBeat;
- u32 SCSI_Prefetch;
- u32 MaxScatterGatherElements;
- u32 MaxLogicalUnits;
- u32 MaxPhysicalDevices;
- u32 MaxPhysicalDrivesPerLogicalUnit;
- u32 MaxPerformantModeCommands;
- u8 reserved[0x78 - 0x58];
- u32 misc_fw_support; /* offset 0x78 */
-#define MISC_FW_DOORBELL_RESET (0x02)
-#define MISC_FW_DOORBELL_RESET2 (0x010)
+ u8 Signature[4];
+ __le32 SpecValence;
+ __le32 TransportSupport;
+ __le32 TransportActive;
+ struct HostWrite HostWrite;
+ __le32 CmdsOutMax;
+ __le32 BusTypes;
+ __le32 TransMethodOffset;
+ u8 ServerName[16];
+ __le32 HeartBeat;
+ __le32 driver_support;
+#define ENABLE_SCSI_PREFETCH 0x100
+#define ENABLE_UNIT_ATTN 0x01
+ __le32 MaxScatterGatherElements;
+ __le32 MaxLogicalUnits;
+ __le32 MaxPhysicalDevices;
+ __le32 MaxPhysicalDrivesPerLogicalUnit;
+ __le32 MaxPerformantModeCommands;
+ __le32 MaxBlockFetch;
+ __le32 PowerConservationSupport;
+ __le32 PowerConservationEnable;
+ __le32 TMFSupportFlags;
+ u8 TMFTagMask[8];
+ u8 reserved[0x78 - 0x70];
+ __le32 misc_fw_support; /* offset 0x78 */
+#define MISC_FW_DOORBELL_RESET 0x02
+#define MISC_FW_DOORBELL_RESET2 0x010
+#define MISC_FW_RAID_OFFLOAD_BASIC 0x020
+#define MISC_FW_EVENT_NOTIFY 0x080
u8 driver_version[32];
-};
+ __le32 max_cached_write_size;
+ u8 driver_scratchpad[16];
+ __le32 max_error_info_length;
+ __le32 io_accel_max_embedded_sg_count;
+ __le32 io_accel_request_size_offset;
+ __le32 event_notify;
+#define HPSA_EVENT_NOTIFY_ACCEL_IO_PATH_STATE_CHANGE (1 << 30)
+#define HPSA_EVENT_NOTIFY_ACCEL_IO_PATH_CONFIG_CHANGE (1 << 31)
+ __le32 clear_event_notify;
+} __packed;
#define NUM_BLOCKFETCH_ENTRIES 8
struct TransTable_struct {
- u32 BlockFetch[NUM_BLOCKFETCH_ENTRIES];
- u32 RepQSize;
- u32 RepQCount;
- u32 RepQCtrAddrLow32;
- u32 RepQCtrAddrHigh32;
- u32 RepQAddr0Low32;
- u32 RepQAddr0High32;
-};
+ __le32 BlockFetch[NUM_BLOCKFETCH_ENTRIES];
+ __le32 RepQSize;
+ __le32 RepQCount;
+ __le32 RepQCtrAddrLow32;
+ __le32 RepQCtrAddrHigh32;
+#define MAX_REPLY_QUEUES 64
+ struct vals32 RepQAddr[MAX_REPLY_QUEUES];
+} __packed;
struct hpsa_pci_info {
unsigned char bus;
unsigned char dev_fn;
unsigned short domain;
u32 board_id;
-};
+} __packed;
+
+struct bmic_identify_controller {
+ u8 configured_logical_drive_count; /* offset 0 */
+ u8 pad1[153];
+ __le16 extended_logical_unit_count; /* offset 154 */
+ u8 pad2[136];
+ u8 controller_mode; /* offset 292 */
+ u8 pad3[32];
+} __packed;
+
+
+struct bmic_identify_physical_device {
+ u8 scsi_bus; /* SCSI Bus number on controller */
+ u8 scsi_id; /* SCSI ID on this bus */
+ __le16 block_size; /* sector size in bytes */
+ __le32 total_blocks; /* number for sectors on drive */
+ __le32 reserved_blocks; /* controller reserved (RIS) */
+ u8 model[40]; /* Physical Drive Model */
+ u8 serial_number[40]; /* Drive Serial Number */
+ u8 firmware_revision[8]; /* drive firmware revision */
+ u8 scsi_inquiry_bits; /* inquiry byte 7 bits */
+ u8 compaq_drive_stamp; /* 0 means drive not stamped */
+ u8 last_failure_reason;
+#define BMIC_LAST_FAILURE_TOO_SMALL_IN_LOAD_CONFIG 0x01
+#define BMIC_LAST_FAILURE_ERROR_ERASING_RIS 0x02
+#define BMIC_LAST_FAILURE_ERROR_SAVING_RIS 0x03
+#define BMIC_LAST_FAILURE_FAIL_DRIVE_COMMAND 0x04
+#define BMIC_LAST_FAILURE_MARK_BAD_FAILED 0x05
+#define BMIC_LAST_FAILURE_MARK_BAD_FAILED_IN_FINISH_REMAP 0x06
+#define BMIC_LAST_FAILURE_TIMEOUT 0x07
+#define BMIC_LAST_FAILURE_AUTOSENSE_FAILED 0x08
+#define BMIC_LAST_FAILURE_MEDIUM_ERROR_1 0x09
+#define BMIC_LAST_FAILURE_MEDIUM_ERROR_2 0x0a
+#define BMIC_LAST_FAILURE_NOT_READY_BAD_SENSE 0x0b
+#define BMIC_LAST_FAILURE_NOT_READY 0x0c
+#define BMIC_LAST_FAILURE_HARDWARE_ERROR 0x0d
+#define BMIC_LAST_FAILURE_ABORTED_COMMAND 0x0e
+#define BMIC_LAST_FAILURE_WRITE_PROTECTED 0x0f
+#define BMIC_LAST_FAILURE_SPIN_UP_FAILURE_IN_RECOVER 0x10
+#define BMIC_LAST_FAILURE_REBUILD_WRITE_ERROR 0x11
+#define BMIC_LAST_FAILURE_TOO_SMALL_IN_HOT_PLUG 0x12
+#define BMIC_LAST_FAILURE_BUS_RESET_RECOVERY_ABORTED 0x13
+#define BMIC_LAST_FAILURE_REMOVED_IN_HOT_PLUG 0x14
+#define BMIC_LAST_FAILURE_INIT_REQUEST_SENSE_FAILED 0x15
+#define BMIC_LAST_FAILURE_INIT_START_UNIT_FAILED 0x16
+#define BMIC_LAST_FAILURE_INQUIRY_FAILED 0x17
+#define BMIC_LAST_FAILURE_NON_DISK_DEVICE 0x18
+#define BMIC_LAST_FAILURE_READ_CAPACITY_FAILED 0x19
+#define BMIC_LAST_FAILURE_INVALID_BLOCK_SIZE 0x1a
+#define BMIC_LAST_FAILURE_HOT_PLUG_REQUEST_SENSE_FAILED 0x1b
+#define BMIC_LAST_FAILURE_HOT_PLUG_START_UNIT_FAILED 0x1c
+#define BMIC_LAST_FAILURE_WRITE_ERROR_AFTER_REMAP 0x1d
+#define BMIC_LAST_FAILURE_INIT_RESET_RECOVERY_ABORTED 0x1e
+#define BMIC_LAST_FAILURE_DEFERRED_WRITE_ERROR 0x1f
+#define BMIC_LAST_FAILURE_MISSING_IN_SAVE_RIS 0x20
+#define BMIC_LAST_FAILURE_WRONG_REPLACE 0x21
+#define BMIC_LAST_FAILURE_GDP_VPD_INQUIRY_FAILED 0x22
+#define BMIC_LAST_FAILURE_GDP_MODE_SENSE_FAILED 0x23
+#define BMIC_LAST_FAILURE_DRIVE_NOT_IN_48BIT_MODE 0x24
+#define BMIC_LAST_FAILURE_DRIVE_TYPE_MIX_IN_HOT_PLUG 0x25
+#define BMIC_LAST_FAILURE_DRIVE_TYPE_MIX_IN_LOAD_CFG 0x26
+#define BMIC_LAST_FAILURE_PROTOCOL_ADAPTER_FAILED 0x27
+#define BMIC_LAST_FAILURE_FAULTY_ID_BAY_EMPTY 0x28
+#define BMIC_LAST_FAILURE_FAULTY_ID_BAY_OCCUPIED 0x29
+#define BMIC_LAST_FAILURE_FAULTY_ID_INVALID_BAY 0x2a
+#define BMIC_LAST_FAILURE_WRITE_RETRIES_FAILED 0x2b
+
+#define BMIC_LAST_FAILURE_SMART_ERROR_REPORTED 0x37
+#define BMIC_LAST_FAILURE_PHY_RESET_FAILED 0x38
+#define BMIC_LAST_FAILURE_ONLY_ONE_CTLR_CAN_SEE_DRIVE 0x40
+#define BMIC_LAST_FAILURE_KC_VOLUME_FAILED 0x41
+#define BMIC_LAST_FAILURE_UNEXPECTED_REPLACEMENT 0x42
+#define BMIC_LAST_FAILURE_OFFLINE_ERASE 0x80
+#define BMIC_LAST_FAILURE_OFFLINE_TOO_SMALL 0x81
+#define BMIC_LAST_FAILURE_OFFLINE_DRIVE_TYPE_MIX 0x82
+#define BMIC_LAST_FAILURE_OFFLINE_ERASE_COMPLETE 0x83
+
+ u8 flags;
+ u8 more_flags;
+ u8 scsi_lun; /* SCSI LUN for phys drive */
+ u8 yet_more_flags;
+ u8 even_more_flags;
+ __le32 spi_speed_rules;/* SPI Speed data:Ultra disable diagnose */
+ u8 phys_connector[2]; /* connector number on controller */
+ u8 phys_box_on_bus; /* phys enclosure this drive resides */
+ u8 phys_bay_in_box; /* phys drv bay this drive resides */
+ __le32 rpm; /* Drive rotational speed in rpm */
+ u8 device_type; /* type of drive */
+#define BMIC_DEVICE_TYPE_CONTROLLER 0x07
+
+ u8 sata_version; /* only valid when drive_type is SATA */
+ __le64 big_total_block_count;
+ __le64 ris_starting_lba;
+ __le32 ris_size;
+ u8 wwid[20];
+ u8 controller_phy_map[32];
+ __le16 phy_count;
+ u8 phy_connected_dev_type[256];
+ u8 phy_to_drive_bay_num[256];
+ __le16 phy_to_attached_dev_index[256];
+ u8 box_index;
+ u8 reserved;
+ __le16 extra_physical_drive_flags;
+#define BMIC_PHYS_DRIVE_SUPPORTS_GAS_GAUGE(idphydrv) \
+ (idphydrv->extra_physical_drive_flags & (1 << 10))
+ u8 negotiated_link_rate[256];
+ u8 phy_to_phy_map[256];
+ u8 redundant_path_present_map;
+ u8 redundant_path_failure_map;
+ u8 active_path_number;
+ __le16 alternate_paths_phys_connector[8];
+ u8 alternate_paths_phys_box_on_port[8];
+ u8 multi_lun_device_lun_count;
+ u8 minimum_good_fw_revision[8];
+ u8 unique_inquiry_bytes[20];
+ u8 current_temperature_degreesC;
+ u8 temperature_threshold_degreesC;
+ u8 max_temperature_degreesC;
+ u8 logical_blocks_per_phys_block_exp; /* phyblocksize = 512*2^exp */
+ __le16 current_queue_depth_limit;
+ u8 reserved_switch_stuff[60];
+ __le16 power_on_hours; /* valid only if gas gauge supported */
+ __le16 percent_endurance_used; /* valid only if gas gauge supported. */
+#define BMIC_PHYS_DRIVE_SSD_WEAROUT(idphydrv) \
+ ((idphydrv->percent_endurance_used & 0x80) || \
+ (idphydrv->percent_endurance_used > 10000))
+ u8 drive_authentication;
+#define BMIC_PHYS_DRIVE_AUTHENTICATED(idphydrv) \
+ (idphydrv->drive_authentication == 0x80)
+ u8 smart_carrier_authentication;
+#define BMIC_SMART_CARRIER_AUTHENTICATION_SUPPORTED(idphydrv) \
+ (idphydrv->smart_carrier_authentication != 0x0)
+#define BMIC_SMART_CARRIER_AUTHENTICATED(idphydrv) \
+ (idphydrv->smart_carrier_authentication == 0x01)
+ u8 smart_carrier_app_fw_version;
+ u8 smart_carrier_bootloader_fw_version;
+ u8 sanitize_support_flags;
+ u8 drive_key_flags;
+ u8 encryption_key_name[64];
+ __le32 misc_drive_flags;
+ __le16 dek_index;
+ __le16 hba_drive_encryption_flags;
+ __le16 max_overwrite_time;
+ __le16 max_block_erase_time;
+ __le16 max_crypto_erase_time;
+ u8 device_connector_info[5];
+ u8 connector_name[8][8];
+ u8 page_83_id[16];
+ u8 max_link_rate[256];
+ u8 neg_phys_link_rate[256];
+ u8 box_conn_name[8];
+} __packed __attribute((aligned(512)));
+
+struct bmic_sense_subsystem_info {
+ u8 primary_slot_number;
+ u8 reserved[3];
+ u8 chasis_serial_number[32];
+ u8 primary_world_wide_id[8];
+ u8 primary_array_serial_number[32]; /* NULL terminated */
+ u8 primary_cache_serial_number[32]; /* NULL terminated */
+ u8 reserved_2[8];
+ u8 secondary_array_serial_number[32];
+ u8 secondary_cache_serial_number[32];
+ u8 pad[332];
+} __packed;
+
+struct bmic_sense_storage_box_params {
+ u8 reserved[36];
+ u8 inquiry_valid;
+ u8 reserved_1[68];
+ u8 phys_box_on_port;
+ u8 reserved_2[22];
+ u16 connection_info;
+ u8 reserver_3[84];
+ u8 phys_connector[2];
+ u8 reserved_4[296];
+} __packed;
-#pragma pack()
#endif /* HPSA_CMD_H */
diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c
index 10b65556937b..21f1d9871a33 100644
--- a/drivers/scsi/hptiop.c
+++ b/drivers/scsi/hptiop.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HighPoint RR3xxx/4xxx controller driver for Linux
- * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2006-2015 HighPoint Technologies, Inc. All Rights Reserved.
*
* Please report bugs/comments/suggestions to linux@highpoint-tech.com
*
@@ -26,7 +18,7 @@
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/gfp.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/div64.h>
#include <scsi/scsi_cmnd.h>
@@ -42,7 +34,7 @@ MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver");
static char driver_name[] = "hptiop";
static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver";
-static const char driver_ver[] = "v1.6 (090910)";
+static const char driver_ver[] = "v1.10.0";
static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec);
static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
@@ -77,6 +69,11 @@ static int iop_wait_ready_mv(struct hptiop_hba *hba, u32 millisec)
return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
}
+static int iop_wait_ready_mvfrey(struct hptiop_hba *hba, u32 millisec)
+{
+ return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
+}
+
static void hptiop_request_callback_itl(struct hptiop_hba *hba, u32 tag)
{
if (tag & IOPMU_QUEUE_ADDR_HOST_BIT)
@@ -230,6 +227,74 @@ static int iop_intr_mv(struct hptiop_hba *hba)
return ret;
}
+static void hptiop_request_callback_mvfrey(struct hptiop_hba *hba, u32 _tag)
+{
+ u32 req_type = _tag & 0xf;
+ struct hpt_iop_request_scsi_command *req;
+
+ switch (req_type) {
+ case IOP_REQUEST_TYPE_GET_CONFIG:
+ case IOP_REQUEST_TYPE_SET_CONFIG:
+ hba->msg_done = 1;
+ break;
+
+ case IOP_REQUEST_TYPE_SCSI_COMMAND:
+ req = hba->reqs[(_tag >> 4) & 0xff].req_virt;
+ if (likely(_tag & IOPMU_QUEUE_REQUEST_RESULT_BIT))
+ req->header.result = IOP_RESULT_SUCCESS;
+ hptiop_finish_scsi_req(hba, (_tag >> 4) & 0xff, req);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int iop_intr_mvfrey(struct hptiop_hba *hba)
+{
+ u32 _tag, status, cptr, cur_rptr;
+ int ret = 0;
+
+ if (hba->initialized)
+ writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+
+ status = readl(&(hba->u.mvfrey.mu->f0_doorbell));
+ if (status) {
+ writel(status, &(hba->u.mvfrey.mu->f0_doorbell));
+ if (status & CPU_TO_F0_DRBL_MSG_BIT) {
+ u32 msg = readl(&(hba->u.mvfrey.mu->cpu_to_f0_msg_a));
+ dprintk("received outbound msg %x\n", msg);
+ hptiop_message_callback(hba, msg);
+ }
+ ret = 1;
+ }
+
+ status = readl(&(hba->u.mvfrey.mu->isr_cause));
+ if (status) {
+ writel(status, &(hba->u.mvfrey.mu->isr_cause));
+ do {
+ cptr = *hba->u.mvfrey.outlist_cptr & 0xff;
+ cur_rptr = hba->u.mvfrey.outlist_rptr;
+ while (cur_rptr != cptr) {
+ cur_rptr++;
+ if (cur_rptr == hba->u.mvfrey.list_count)
+ cur_rptr = 0;
+
+ _tag = hba->u.mvfrey.outlist[cur_rptr].val;
+ BUG_ON(!(_tag & IOPMU_QUEUE_MASK_HOST_BITS));
+ hptiop_request_callback_mvfrey(hba, _tag);
+ ret = 1;
+ }
+ hba->u.mvfrey.outlist_rptr = cur_rptr;
+ } while (cptr != (*hba->u.mvfrey.outlist_cptr & 0xff));
+ }
+
+ if (hba->initialized)
+ writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+
+ return ret;
+}
+
static int iop_send_sync_request_itl(struct hptiop_hba *hba,
void __iomem *_req, u32 millisec)
{
@@ -272,6 +337,26 @@ static int iop_send_sync_request_mv(struct hptiop_hba *hba,
return -1;
}
+static int iop_send_sync_request_mvfrey(struct hptiop_hba *hba,
+ u32 size_bits, u32 millisec)
+{
+ struct hpt_iop_request_header *reqhdr =
+ hba->u.mvfrey.internal_req.req_virt;
+ u32 i;
+
+ hba->msg_done = 0;
+ reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_SYNC_REQUEST);
+ hba->ops->post_req(hba, &(hba->u.mvfrey.internal_req));
+
+ for (i = 0; i < millisec; i++) {
+ iop_intr_mvfrey(hba);
+ if (hba->msg_done)
+ break;
+ msleep(1);
+ }
+ return hba->msg_done ? 0 : -1;
+}
+
static void hptiop_post_msg_itl(struct hptiop_hba *hba, u32 msg)
{
writel(msg, &hba->u.itl.iop->inbound_msgaddr0);
@@ -285,11 +370,18 @@ static void hptiop_post_msg_mv(struct hptiop_hba *hba, u32 msg)
readl(&hba->u.mv.regs->inbound_doorbell);
}
+static void hptiop_post_msg_mvfrey(struct hptiop_hba *hba, u32 msg)
+{
+ writel(msg, &(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
+ readl(&(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
+}
+
static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec)
{
u32 i;
hba->msg_done = 0;
+ hba->ops->disable_intr(hba);
hba->ops->post_msg(hba, msg);
for (i = 0; i < millisec; i++) {
@@ -301,6 +393,7 @@ static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec)
msleep(1);
}
+ hba->ops->enable_intr(hba);
return hba->msg_done? 0 : -1;
}
@@ -354,6 +447,28 @@ static int iop_get_config_mv(struct hptiop_hba *hba,
return 0;
}
+static int iop_get_config_mvfrey(struct hptiop_hba *hba,
+ struct hpt_iop_request_get_config *config)
+{
+ struct hpt_iop_request_get_config *info = hba->u.mvfrey.config;
+
+ if (info->header.size != sizeof(struct hpt_iop_request_get_config) ||
+ info->header.type != IOP_REQUEST_TYPE_GET_CONFIG)
+ return -1;
+
+ config->interface_version = info->interface_version;
+ config->firmware_version = info->firmware_version;
+ config->max_requests = info->max_requests;
+ config->request_size = info->request_size;
+ config->max_sg_count = info->max_sg_count;
+ config->data_transfer_length = info->data_transfer_length;
+ config->alignment_mask = info->alignment_mask;
+ config->max_devices = info->max_devices;
+ config->sdram_size = info->sdram_size;
+
+ return 0;
+}
+
static int iop_set_config_itl(struct hptiop_hba *hba,
struct hpt_iop_request_set_config *config)
{
@@ -408,6 +523,29 @@ static int iop_set_config_mv(struct hptiop_hba *hba,
return 0;
}
+static int iop_set_config_mvfrey(struct hptiop_hba *hba,
+ struct hpt_iop_request_set_config *config)
+{
+ struct hpt_iop_request_set_config *req =
+ hba->u.mvfrey.internal_req.req_virt;
+
+ memcpy(req, config, sizeof(struct hpt_iop_request_set_config));
+ req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
+ req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG);
+ req->header.size =
+ cpu_to_le32(sizeof(struct hpt_iop_request_set_config));
+ req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
+ req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG<<5);
+ req->header.context_hi32 = 0;
+
+ if (iop_send_sync_request_mvfrey(hba, 0, 20000)) {
+ dprintk("Set config send cmd failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
static void hptiop_enable_intr_itl(struct hptiop_hba *hba)
{
writel(~(IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0),
@@ -420,6 +558,13 @@ static void hptiop_enable_intr_mv(struct hptiop_hba *hba)
&hba->u.mv.regs->outbound_intmask);
}
+static void hptiop_enable_intr_mvfrey(struct hptiop_hba *hba)
+{
+ writel(CPU_TO_F0_DRBL_MSG_BIT, &(hba->u.mvfrey.mu->f0_doorbell_enable));
+ writel(0x1, &(hba->u.mvfrey.mu->isr_enable));
+ writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+}
+
static int hptiop_initialize_iop(struct hptiop_hba *hba)
{
/* enable interrupts */
@@ -502,17 +647,39 @@ static int hptiop_map_pci_bar_mv(struct hptiop_hba *hba)
return 0;
}
+static int hptiop_map_pci_bar_mvfrey(struct hptiop_hba *hba)
+{
+ hba->u.mvfrey.config = hptiop_map_pci_bar(hba, 0);
+ if (hba->u.mvfrey.config == NULL)
+ return -1;
+
+ hba->u.mvfrey.mu = hptiop_map_pci_bar(hba, 2);
+ if (hba->u.mvfrey.mu == NULL) {
+ iounmap(hba->u.mvfrey.config);
+ return -1;
+ }
+
+ return 0;
+}
+
static void hptiop_unmap_pci_bar_mv(struct hptiop_hba *hba)
{
iounmap(hba->u.mv.regs);
iounmap(hba->u.mv.mu);
}
+static void hptiop_unmap_pci_bar_mvfrey(struct hptiop_hba *hba)
+{
+ iounmap(hba->u.mvfrey.config);
+ iounmap(hba->u.mvfrey.mu);
+}
+
static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg)
{
dprintk("iop message 0x%x\n", msg);
- if (msg == IOPMU_INBOUND_MSG0_NOP)
+ if (msg == IOPMU_INBOUND_MSG0_NOP ||
+ msg == IOPMU_INBOUND_MSG0_RESET_COMM)
hba->msg_done = 1;
if (!hba->initialized)
@@ -589,18 +756,20 @@ static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
scsi_set_resid(scp,
scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length));
scp->result = SAM_STAT_CHECK_CONDITION;
- memcpy(scp->sense_buffer, &req->sg_list,
- min_t(size_t, SCSI_SENSE_BUFFERSIZE,
- le32_to_cpu(req->dataxfer_length)));
- break;
+ memcpy(scp->sense_buffer, &req->sg_list, SCSI_SENSE_BUFFERSIZE);
+ goto skip_resid;
default:
- scp->result = DRIVER_INVALID << 24 | DID_ABORT << 16;
+ scp->result = DID_ABORT << 16;
break;
}
+ scsi_set_resid(scp,
+ scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length));
+
+skip_resid:
dprintk("scsi_done(%p)\n", scp);
- scp->scsi_done(scp);
+ scsi_done(scp);
free_req(hba, &hba->reqs[tag]);
}
@@ -622,7 +791,7 @@ static void hptiop_host_request_callback_itl(struct hptiop_hba *hba, u32 _tag)
hptiop_finish_scsi_req(hba, tag, req);
}
-void hptiop_iop_request_callback_itl(struct hptiop_hba *hba, u32 tag)
+static void hptiop_iop_request_callback_itl(struct hptiop_hba *hba, u32 tag)
{
struct hpt_iop_request_header __iomem *req;
struct hpt_iop_request_ioctl_command __iomem *p;
@@ -692,7 +861,8 @@ static int hptiop_buildsgl(struct scsi_cmnd *scp, struct hpt_iopsg *psg)
BUG_ON(HPT_SCP(scp)->sgcnt > hba->max_sg_descriptors);
scsi_for_each_sg(scp, sg, HPT_SCP(scp)->sgcnt, idx) {
- psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg));
+ psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg)) |
+ hba->ops->host_phy_flag;
psg[idx].size = cpu_to_le32(sg_dma_len(sg));
psg[idx].eot = (idx == HPT_SCP(scp)->sgcnt - 1) ?
cpu_to_le32(1) : 0;
@@ -751,8 +921,79 @@ static void hptiop_post_req_mv(struct hptiop_hba *hba,
MVIOP_MU_QUEUE_ADDR_HOST_BIT | size_bit, hba);
}
-static int hptiop_queuecommand_lck(struct scsi_cmnd *scp,
- void (*done)(struct scsi_cmnd *))
+static void hptiop_post_req_mvfrey(struct hptiop_hba *hba,
+ struct hptiop_request *_req)
+{
+ struct hpt_iop_request_header *reqhdr = _req->req_virt;
+ u32 index;
+
+ reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT |
+ IOP_REQUEST_FLAG_ADDR_BITS |
+ ((_req->req_shifted_phy >> 11) & 0xffff0000));
+ reqhdr->context = cpu_to_le32(IOPMU_QUEUE_ADDR_HOST_BIT |
+ (_req->index << 4) | reqhdr->type);
+ reqhdr->context_hi32 = cpu_to_le32((_req->req_shifted_phy << 5) &
+ 0xffffffff);
+
+ hba->u.mvfrey.inlist_wptr++;
+ index = hba->u.mvfrey.inlist_wptr & 0x3fff;
+
+ if (index == hba->u.mvfrey.list_count) {
+ index = 0;
+ hba->u.mvfrey.inlist_wptr &= ~0x3fff;
+ hba->u.mvfrey.inlist_wptr ^= CL_POINTER_TOGGLE;
+ }
+
+ hba->u.mvfrey.inlist[index].addr =
+ (dma_addr_t)_req->req_shifted_phy << 5;
+ hba->u.mvfrey.inlist[index].intrfc_len = (reqhdr->size + 3) / 4;
+ writel(hba->u.mvfrey.inlist_wptr,
+ &(hba->u.mvfrey.mu->inbound_write_ptr));
+ readl(&(hba->u.mvfrey.mu->inbound_write_ptr));
+}
+
+static int hptiop_reset_comm_itl(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
+static int hptiop_reset_comm_mv(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
+static int hptiop_reset_comm_mvfrey(struct hptiop_hba *hba)
+{
+ u32 list_count = hba->u.mvfrey.list_count;
+
+ if (iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET_COMM, 3000))
+ return -1;
+
+ /* wait 100ms for MCU ready */
+ msleep(100);
+
+ writel(cpu_to_le32(hba->u.mvfrey.inlist_phy & 0xffffffff),
+ &(hba->u.mvfrey.mu->inbound_base));
+ writel(cpu_to_le32((hba->u.mvfrey.inlist_phy >> 16) >> 16),
+ &(hba->u.mvfrey.mu->inbound_base_high));
+
+ writel(cpu_to_le32(hba->u.mvfrey.outlist_phy & 0xffffffff),
+ &(hba->u.mvfrey.mu->outbound_base));
+ writel(cpu_to_le32((hba->u.mvfrey.outlist_phy >> 16) >> 16),
+ &(hba->u.mvfrey.mu->outbound_base_high));
+
+ writel(cpu_to_le32(hba->u.mvfrey.outlist_cptr_phy & 0xffffffff),
+ &(hba->u.mvfrey.mu->outbound_shadow_base));
+ writel(cpu_to_le32((hba->u.mvfrey.outlist_cptr_phy >> 16) >> 16),
+ &(hba->u.mvfrey.mu->outbound_shadow_base_high));
+
+ hba->u.mvfrey.inlist_wptr = (list_count - 1) | CL_POINTER_TOGGLE;
+ *hba->u.mvfrey.outlist_cptr = (list_count - 1) | CL_POINTER_TOGGLE;
+ hba->u.mvfrey.outlist_rptr = list_count - 1;
+ return 0;
+}
+
+static int hptiop_queuecommand_lck(struct scsi_cmnd *scp)
{
struct Scsi_Host *host = scp->device->host;
struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata;
@@ -760,9 +1001,6 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp,
int sg_count = 0;
struct hptiop_request *_req;
- BUG_ON(!done);
- scp->scsi_done = done;
-
_req = get_req(hba);
if (_req == NULL) {
dprintk("hptiop_queuecmd : no free req\n");
@@ -771,20 +1009,22 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp,
_req->scp = scp;
- dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%x-%x-%x) "
+ dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%llu cdb=(%08x-%08x-%08x-%08x) "
"req_index=%d, req=%p\n",
scp,
host->host_no, scp->device->channel,
scp->device->id, scp->device->lun,
- ((u32 *)scp->cmnd)[0],
- ((u32 *)scp->cmnd)[1],
- ((u32 *)scp->cmnd)[2],
+ cpu_to_be32(((u32 *)scp->cmnd)[0]),
+ cpu_to_be32(((u32 *)scp->cmnd)[1]),
+ cpu_to_be32(((u32 *)scp->cmnd)[2]),
+ cpu_to_be32(((u32 *)scp->cmnd)[3]),
_req->index, _req->req_virt);
scp->result = 0;
- if (scp->device->channel || scp->device->lun ||
- scp->device->id > hba->max_devices) {
+ if (scp->device->channel ||
+ (scp->device->id > hba->max_devices) ||
+ ((scp->device->id == (hba->max_devices-1)) && scp->device->lun)) {
scp->result = DID_BAD_TARGET << 16;
free_req(hba, _req);
goto cmd_done;
@@ -804,10 +1044,7 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp,
req->channel = scp->device->channel;
req->target = scp->device->id;
req->lun = scp->device->lun;
- req->header.size = cpu_to_le32(
- sizeof(struct hpt_iop_request_scsi_command)
- - sizeof(struct hpt_iopsg)
- + sg_count * sizeof(struct hpt_iopsg));
+ req->header.size = cpu_to_le32(struct_size(req, sg_list, sg_count));
memcpy(req->cdb, scp->cmnd, sizeof(req->cdb));
hba->ops->post_req(hba, _req);
@@ -815,7 +1052,7 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp,
cmd_done:
dprintk("scsi_done(scp=%p)\n", scp);
- scp->scsi_done(scp);
+ scsi_done(scp);
return 0;
}
@@ -853,28 +1090,22 @@ static int hptiop_reset_hba(struct hptiop_hba *hba)
static int hptiop_reset(struct scsi_cmnd *scp)
{
- struct Scsi_Host * host = scp->device->host;
- struct hptiop_hba * hba = (struct hptiop_hba *)host->hostdata;
+ struct hptiop_hba * hba = (struct hptiop_hba *)scp->device->host->hostdata;
- printk(KERN_WARNING "hptiop_reset(%d/%d/%d) scp=%p\n",
- scp->device->host->host_no, scp->device->channel,
- scp->device->id, scp);
+ printk(KERN_WARNING "hptiop_reset(%d/%d/%d)\n",
+ scp->device->host->host_no, -1, -1);
return hptiop_reset_hba(hba)? FAILED : SUCCESS;
}
static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth, int reason)
+ int queue_depth)
{
struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata;
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (queue_depth > hba->max_requests)
queue_depth = hba->max_requests;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
+ return scsi_change_queue_depth(sdev, queue_depth);
}
static ssize_t hptiop_show_version(struct device *dev,
@@ -912,27 +1143,42 @@ static struct device_attribute hptiop_attr_fw_version = {
.show = hptiop_show_fw_version,
};
-static struct device_attribute *hptiop_attrs[] = {
- &hptiop_attr_version,
- &hptiop_attr_fw_version,
+static struct attribute *hptiop_host_attrs[] = {
+ &hptiop_attr_version.attr,
+ &hptiop_attr_fw_version.attr,
NULL
};
-static struct scsi_host_template driver_template = {
+ATTRIBUTE_GROUPS(hptiop_host);
+
+static int hptiop_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ if (sdev->type == TYPE_TAPE)
+ lim->max_hw_sectors = 8192;
+ return 0;
+}
+
+static const struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = driver_name,
.queuecommand = hptiop_queuecommand,
- .eh_device_reset_handler = hptiop_reset,
- .eh_bus_reset_handler = hptiop_reset,
+ .eh_host_reset_handler = hptiop_reset,
.info = hptiop_info,
.emulated = 0,
- .use_clustering = ENABLE_CLUSTERING,
.proc_name = driver_name,
- .shost_attrs = hptiop_attrs,
+ .shost_groups = hptiop_host_groups,
+ .sdev_configure = hptiop_sdev_configure,
.this_id = -1,
.change_queue_depth = hptiop_adjust_disk_queue_depth,
+ .cmd_size = sizeof(struct hpt_cmd_priv),
};
+static int hptiop_internal_memalloc_itl(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
static int hptiop_internal_memalloc_mv(struct hptiop_hba *hba)
{
hba->u.mv.internal_req = dma_alloc_coherent(&hba->pcidev->dev,
@@ -943,6 +1189,63 @@ static int hptiop_internal_memalloc_mv(struct hptiop_hba *hba)
return -1;
}
+static int hptiop_internal_memalloc_mvfrey(struct hptiop_hba *hba)
+{
+ u32 list_count = readl(&hba->u.mvfrey.mu->inbound_conf_ctl);
+ char *p;
+ dma_addr_t phy;
+
+ BUG_ON(hba->max_request_size == 0);
+
+ if (list_count == 0) {
+ BUG_ON(1);
+ return -1;
+ }
+
+ list_count >>= 16;
+
+ hba->u.mvfrey.list_count = list_count;
+ hba->u.mvfrey.internal_mem_size = 0x800 +
+ list_count * sizeof(struct mvfrey_inlist_entry) +
+ list_count * sizeof(struct mvfrey_outlist_entry) +
+ sizeof(int);
+
+ p = dma_alloc_coherent(&hba->pcidev->dev,
+ hba->u.mvfrey.internal_mem_size, &phy, GFP_KERNEL);
+ if (!p)
+ return -1;
+
+ hba->u.mvfrey.internal_req.req_virt = p;
+ hba->u.mvfrey.internal_req.req_shifted_phy = phy >> 5;
+ hba->u.mvfrey.internal_req.scp = NULL;
+ hba->u.mvfrey.internal_req.next = NULL;
+
+ p += 0x800;
+ phy += 0x800;
+
+ hba->u.mvfrey.inlist = (struct mvfrey_inlist_entry *)p;
+ hba->u.mvfrey.inlist_phy = phy;
+
+ p += list_count * sizeof(struct mvfrey_inlist_entry);
+ phy += list_count * sizeof(struct mvfrey_inlist_entry);
+
+ hba->u.mvfrey.outlist = (struct mvfrey_outlist_entry *)p;
+ hba->u.mvfrey.outlist_phy = phy;
+
+ p += list_count * sizeof(struct mvfrey_outlist_entry);
+ phy += list_count * sizeof(struct mvfrey_outlist_entry);
+
+ hba->u.mvfrey.outlist_cptr = (__le32 *)p;
+ hba->u.mvfrey.outlist_cptr_phy = phy;
+
+ return 0;
+}
+
+static int hptiop_internal_memfree_itl(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
static int hptiop_internal_memfree_mv(struct hptiop_hba *hba)
{
if (hba->u.mv.internal_req) {
@@ -953,16 +1256,30 @@ static int hptiop_internal_memfree_mv(struct hptiop_hba *hba)
return -1;
}
-static int __devinit hptiop_probe(struct pci_dev *pcidev,
- const struct pci_device_id *id)
+static int hptiop_internal_memfree_mvfrey(struct hptiop_hba *hba)
+{
+ if (hba->u.mvfrey.internal_req.req_virt) {
+ dma_free_coherent(&hba->pcidev->dev,
+ hba->u.mvfrey.internal_mem_size,
+ hba->u.mvfrey.internal_req.req_virt,
+ (dma_addr_t)
+ hba->u.mvfrey.internal_req.req_shifted_phy << 5);
+ return 0;
+ } else
+ return -1;
+}
+
+static int hptiop_probe(struct pci_dev *pcidev, const struct pci_device_id *id)
{
struct Scsi_Host *host = NULL;
struct hptiop_hba *hba;
+ struct hptiop_adapter_ops *iop_ops;
struct hpt_iop_request_get_config iop_config;
struct hpt_iop_request_set_config set_config;
dma_addr_t start_phy;
void *start_virt;
u32 offset, i, req_size;
+ int rc;
dprintk("hptiop_probe(%p)\n", pcidev);
@@ -978,11 +1295,15 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
pci_set_master(pcidev);
/* Enable 64bit DMA if possible */
- if (pci_set_dma_mask(pcidev, DMA_BIT_MASK(64))) {
- if (pci_set_dma_mask(pcidev, DMA_BIT_MASK(32))) {
- printk(KERN_ERR "hptiop: fail to set dma_mask\n");
- goto disable_pci_device;
- }
+ iop_ops = (struct hptiop_adapter_ops *)id->driver_data;
+ rc = dma_set_mask(&pcidev->dev,
+ DMA_BIT_MASK(iop_ops->hw_dma_bit_mask));
+ if (rc)
+ rc = dma_set_mask(&pcidev->dev, DMA_BIT_MASK(32));
+
+ if (rc) {
+ printk(KERN_ERR "hptiop: fail to set dma_mask\n");
+ goto disable_pci_device;
}
if (pci_request_regions(pcidev, driver_name)) {
@@ -997,8 +1318,9 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
}
hba = (struct hptiop_hba *)host->hostdata;
+ memset(hba, 0, sizeof(struct hptiop_hba));
- hba->ops = (struct hptiop_adapter_ops *)id->driver_data;
+ hba->ops = iop_ops;
hba->pcidev = pcidev;
hba->host = host;
hba->initialized = 0;
@@ -1010,7 +1332,7 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
init_waitqueue_head(&hba->reset_wq);
init_waitqueue_head(&hba->ioctl_wq);
- host->max_lun = 1;
+ host->max_lun = 128;
host->max_channel = 0;
host->io_port = 0;
host->n_io_port = 0;
@@ -1025,7 +1347,7 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
goto unmap_pci_bar;
}
- if (hba->ops->internal_memalloc) {
+ if (hba->ops->family == MV_BASED_IOP) {
if (hba->ops->internal_memalloc(hba)) {
printk(KERN_ERR "scsi%d: internal_memalloc failed\n",
hba->host->host_no);
@@ -1048,6 +1370,19 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
hba->interface_version = le32_to_cpu(iop_config.interface_version);
hba->sdram_size = le32_to_cpu(iop_config.sdram_size);
+ if (hba->ops->family == MVFREY_BASED_IOP) {
+ if (hba->ops->internal_memalloc(hba)) {
+ printk(KERN_ERR "scsi%d: internal_memalloc failed\n",
+ hba->host->host_no);
+ goto unmap_pci_bar;
+ }
+ if (hba->ops->reset_comm(hba)) {
+ printk(KERN_ERR "scsi%d: reset comm failed\n",
+ hba->host->host_no);
+ goto unmap_pci_bar;
+ }
+ }
+
if (hba->firmware_version > 0x01020000 ||
hba->interface_version > 0x01020000)
hba->iopintf_v2 = 1;
@@ -1059,8 +1394,8 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
host->cmd_per_lun = le32_to_cpu(iop_config.max_requests);
host->max_cmd_len = 16;
- req_size = sizeof(struct hpt_iop_request_scsi_command)
- + sizeof(struct hpt_iopsg) * (hba->max_sg_descriptors - 1);
+ req_size = struct_size_t(struct hpt_iop_request_scsi_command,
+ sg_list, hba->max_sg_descriptors);
if ((req_size & 0x1f) != 0)
req_size = (req_size + 0x1f) & ~0x1f;
@@ -1089,35 +1424,33 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
dprintk("req_size=%d, max_requests=%d\n", req_size, hba->max_requests);
hba->req_size = req_size;
- start_virt = dma_alloc_coherent(&pcidev->dev,
- hba->req_size*hba->max_requests + 0x20,
- &start_phy, GFP_KERNEL);
+ hba->req_list = NULL;
- if (!start_virt) {
- printk(KERN_ERR "scsi%d: fail to alloc request mem\n",
- hba->host->host_no);
- goto free_request_irq;
- }
+ for (i = 0; i < hba->max_requests; i++) {
+ start_virt = dma_alloc_coherent(&pcidev->dev,
+ hba->req_size + 0x20,
+ &start_phy, GFP_KERNEL);
+
+ if (!start_virt) {
+ printk(KERN_ERR "scsi%d: fail to alloc request mem\n",
+ hba->host->host_no);
+ goto free_request_mem;
+ }
- hba->dma_coherent = start_virt;
- hba->dma_coherent_handle = start_phy;
+ hba->dma_coherent[i] = start_virt;
+ hba->dma_coherent_handle[i] = start_phy;
- if ((start_phy & 0x1f) != 0)
- {
- offset = ((start_phy + 0x1f) & ~0x1f) - start_phy;
- start_phy += offset;
- start_virt += offset;
- }
+ if ((start_phy & 0x1f) != 0) {
+ offset = ((start_phy + 0x1f) & ~0x1f) - start_phy;
+ start_phy += offset;
+ start_virt += offset;
+ }
- hba->req_list = start_virt;
- for (i = 0; i < hba->max_requests; i++) {
hba->reqs[i].next = NULL;
hba->reqs[i].req_virt = start_virt;
hba->reqs[i].req_shifted_phy = start_phy >> 5;
hba->reqs[i].index = i;
free_req(hba, &hba->reqs[i]);
- start_virt = (char *)start_virt + hba->req_size;
- start_phy = start_phy + hba->req_size;
}
/* Enable Interrupt and start background task */
@@ -1130,23 +1463,26 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
goto free_request_mem;
}
-
scsi_scan_host(host);
dprintk("scsi%d: hptiop_probe successfully\n", hba->host->host_no);
return 0;
free_request_mem:
- dma_free_coherent(&hba->pcidev->dev,
- hba->req_size * hba->max_requests + 0x20,
- hba->dma_coherent, hba->dma_coherent_handle);
+ for (i = 0; i < hba->max_requests; i++) {
+ if (hba->dma_coherent[i] && hba->dma_coherent_handle[i])
+ dma_free_coherent(&hba->pcidev->dev,
+ hba->req_size + 0x20,
+ hba->dma_coherent[i],
+ hba->dma_coherent_handle[i]);
+ else
+ break;
+ }
-free_request_irq:
free_irq(hba->pcidev->irq, hba);
unmap_pci_bar:
- if (hba->ops->internal_memfree)
- hba->ops->internal_memfree(hba);
+ hba->ops->internal_memfree(hba);
hba->ops->unmap_pci_bar(hba);
@@ -1196,10 +1532,21 @@ static void hptiop_disable_intr_mv(struct hptiop_hba *hba)
readl(&hba->u.mv.regs->outbound_intmask);
}
+static void hptiop_disable_intr_mvfrey(struct hptiop_hba *hba)
+{
+ writel(0, &(hba->u.mvfrey.mu->f0_doorbell_enable));
+ readl(&(hba->u.mvfrey.mu->f0_doorbell_enable));
+ writel(0, &(hba->u.mvfrey.mu->isr_enable));
+ readl(&(hba->u.mvfrey.mu->isr_enable));
+ writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+ readl(&(hba->u.mvfrey.mu->pcie_f0_int_enable));
+}
+
static void hptiop_remove(struct pci_dev *pcidev)
{
struct Scsi_Host *host = pci_get_drvdata(pcidev);
struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata;
+ u32 i;
dprintk("scsi%d: hptiop_remove\n", hba->host->host_no);
@@ -1209,13 +1556,17 @@ static void hptiop_remove(struct pci_dev *pcidev)
free_irq(hba->pcidev->irq, hba);
- dma_free_coherent(&hba->pcidev->dev,
- hba->req_size * hba->max_requests + 0x20,
- hba->dma_coherent,
- hba->dma_coherent_handle);
+ for (i = 0; i < hba->max_requests; i++) {
+ if (hba->dma_coherent[i] && hba->dma_coherent_handle[i])
+ dma_free_coherent(&hba->pcidev->dev,
+ hba->req_size + 0x20,
+ hba->dma_coherent[i],
+ hba->dma_coherent_handle[i]);
+ else
+ break;
+ }
- if (hba->ops->internal_memfree)
- hba->ops->internal_memfree(hba);
+ hba->ops->internal_memfree(hba);
hba->ops->unmap_pci_bar(hba);
@@ -1227,9 +1578,10 @@ static void hptiop_remove(struct pci_dev *pcidev)
}
static struct hptiop_adapter_ops hptiop_itl_ops = {
+ .family = INTEL_BASED_IOP,
.iop_wait_ready = iop_wait_ready_itl,
- .internal_memalloc = NULL,
- .internal_memfree = NULL,
+ .internal_memalloc = hptiop_internal_memalloc_itl,
+ .internal_memfree = hptiop_internal_memfree_itl,
.map_pci_bar = hptiop_map_pci_bar_itl,
.unmap_pci_bar = hptiop_unmap_pci_bar_itl,
.enable_intr = hptiop_enable_intr_itl,
@@ -1239,9 +1591,13 @@ static struct hptiop_adapter_ops hptiop_itl_ops = {
.iop_intr = iop_intr_itl,
.post_msg = hptiop_post_msg_itl,
.post_req = hptiop_post_req_itl,
+ .hw_dma_bit_mask = 64,
+ .reset_comm = hptiop_reset_comm_itl,
+ .host_phy_flag = cpu_to_le64(0),
};
static struct hptiop_adapter_ops hptiop_mv_ops = {
+ .family = MV_BASED_IOP,
.iop_wait_ready = iop_wait_ready_mv,
.internal_memalloc = hptiop_internal_memalloc_mv,
.internal_memfree = hptiop_internal_memfree_mv,
@@ -1254,9 +1610,31 @@ static struct hptiop_adapter_ops hptiop_mv_ops = {
.iop_intr = iop_intr_mv,
.post_msg = hptiop_post_msg_mv,
.post_req = hptiop_post_req_mv,
+ .hw_dma_bit_mask = 33,
+ .reset_comm = hptiop_reset_comm_mv,
+ .host_phy_flag = cpu_to_le64(0),
+};
+
+static struct hptiop_adapter_ops hptiop_mvfrey_ops = {
+ .family = MVFREY_BASED_IOP,
+ .iop_wait_ready = iop_wait_ready_mvfrey,
+ .internal_memalloc = hptiop_internal_memalloc_mvfrey,
+ .internal_memfree = hptiop_internal_memfree_mvfrey,
+ .map_pci_bar = hptiop_map_pci_bar_mvfrey,
+ .unmap_pci_bar = hptiop_unmap_pci_bar_mvfrey,
+ .enable_intr = hptiop_enable_intr_mvfrey,
+ .disable_intr = hptiop_disable_intr_mvfrey,
+ .get_config = iop_get_config_mvfrey,
+ .set_config = iop_set_config_mvfrey,
+ .iop_intr = iop_intr_mvfrey,
+ .post_msg = hptiop_post_msg_mvfrey,
+ .post_req = hptiop_post_req_mvfrey,
+ .hw_dma_bit_mask = 64,
+ .reset_comm = hptiop_reset_comm_mvfrey,
+ .host_phy_flag = cpu_to_le64(1),
};
-static struct pci_device_id hptiop_id_table[] = {
+static const struct pci_device_id hptiop_id_table[] = {
{ PCI_VDEVICE(TTI, 0x3220), (kernel_ulong_t)&hptiop_itl_ops },
{ PCI_VDEVICE(TTI, 0x3320), (kernel_ulong_t)&hptiop_itl_ops },
{ PCI_VDEVICE(TTI, 0x3410), (kernel_ulong_t)&hptiop_itl_ops },
@@ -1279,6 +1657,16 @@ static struct pci_device_id hptiop_id_table[] = {
{ PCI_VDEVICE(TTI, 0x3120), (kernel_ulong_t)&hptiop_mv_ops },
{ PCI_VDEVICE(TTI, 0x3122), (kernel_ulong_t)&hptiop_mv_ops },
{ PCI_VDEVICE(TTI, 0x3020), (kernel_ulong_t)&hptiop_mv_ops },
+ { PCI_VDEVICE(TTI, 0x4520), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x4522), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3610), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3611), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3620), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3622), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3640), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3660), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3680), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x3690), (kernel_ulong_t)&hptiop_mvfrey_ops },
{},
};
diff --git a/drivers/scsi/hptiop.h b/drivers/scsi/hptiop.h
index 0b871c0ae568..394ef6aa469e 100644
--- a/drivers/scsi/hptiop.h
+++ b/drivers/scsi/hptiop.h
@@ -1,15 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* HighPoint RR3xxx/4xxx controller driver for Linux
- * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2006-2015 HighPoint Technologies, Inc. All Rights Reserved.
*
* Please report bugs/comments/suggestions to linux@highpoint-tech.com
*
@@ -75,6 +67,45 @@ struct hpt_iopmv_regs {
__le32 outbound_intmask;
};
+#pragma pack(1)
+struct hpt_iopmu_mvfrey {
+ __le32 reserved0[(0x4000 - 0) / 4];
+ __le32 inbound_base;
+ __le32 inbound_base_high;
+ __le32 reserved1[(0x4018 - 0x4008) / 4];
+ __le32 inbound_write_ptr;
+ __le32 reserved2[(0x402c - 0x401c) / 4];
+ __le32 inbound_conf_ctl;
+ __le32 reserved3[(0x4050 - 0x4030) / 4];
+ __le32 outbound_base;
+ __le32 outbound_base_high;
+ __le32 outbound_shadow_base;
+ __le32 outbound_shadow_base_high;
+ __le32 reserved4[(0x4088 - 0x4060) / 4];
+ __le32 isr_cause;
+ __le32 isr_enable;
+ __le32 reserved5[(0x1020c - 0x4090) / 4];
+ __le32 pcie_f0_int_enable;
+ __le32 reserved6[(0x10400 - 0x10210) / 4];
+ __le32 f0_to_cpu_msg_a;
+ __le32 reserved7[(0x10420 - 0x10404) / 4];
+ __le32 cpu_to_f0_msg_a;
+ __le32 reserved8[(0x10480 - 0x10424) / 4];
+ __le32 f0_doorbell;
+ __le32 f0_doorbell_enable;
+};
+
+struct mvfrey_inlist_entry {
+ dma_addr_t addr;
+ __le32 intrfc_len;
+ __le32 reserved;
+};
+
+struct mvfrey_outlist_entry {
+ __le32 val;
+};
+#pragma pack()
+
#define MVIOP_MU_QUEUE_ADDR_HOST_MASK (~(0x1full))
#define MVIOP_MU_QUEUE_ADDR_HOST_BIT 4
@@ -87,6 +118,9 @@ struct hpt_iopmv_regs {
#define MVIOP_MU_OUTBOUND_INT_MSG 1
#define MVIOP_MU_OUTBOUND_INT_POSTQUEUE 2
+#define CL_POINTER_TOGGLE 0x00004000
+#define CPU_TO_F0_DRBL_MSG_BIT 0x02000000
+
enum hpt_iopmu_message {
/* host-to-iop messages */
IOPMU_INBOUND_MSG0_NOP = 0,
@@ -95,6 +129,7 @@ enum hpt_iopmu_message {
IOPMU_INBOUND_MSG0_SHUTDOWN,
IOPMU_INBOUND_MSG0_STOP_BACKGROUND_TASK,
IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK,
+ IOPMU_INBOUND_MSG0_RESET_COMM,
IOPMU_INBOUND_MSG0_MAX = 0xff,
/* iop-to-host messages */
IOPMU_OUTBOUND_MSG0_REGISTER_DEVICE_0 = 0x100,
@@ -118,6 +153,7 @@ struct hpt_iop_request_header {
#define IOP_REQUEST_FLAG_BIST_REQUEST 2
#define IOP_REQUEST_FLAG_REMAPPED 4
#define IOP_REQUEST_FLAG_OUTPUT_CONTEXT 8
+#define IOP_REQUEST_FLAG_ADDR_BITS 0x40 /* flags[31:16] is phy_addr[47:32] */
enum hpt_iop_request_type {
IOP_REQUEST_TYPE_GET_CONFIG = 0,
@@ -192,7 +228,7 @@ struct hpt_iop_request_scsi_command {
u8 pad1;
u8 cdb[16];
__le32 dataxfer_length;
- struct hpt_iopsg sg_list[1];
+ struct hpt_iopsg sg_list[];
};
struct hpt_iop_request_ioctl_command {
@@ -201,7 +237,7 @@ struct hpt_iop_request_ioctl_command {
__le32 inbuf_size;
__le32 outbuf_size;
__le32 bytes_returned;
- u8 buf[1];
+ u8 buf[];
/* out data should be put at buf[(inbuf_size+3)&~3] */
};
@@ -215,13 +251,20 @@ struct hptiop_request {
int index;
};
-struct hpt_scsi_pointer {
+struct hpt_cmd_priv {
int mapped;
int sgcnt;
dma_addr_t dma_handle;
};
-#define HPT_SCP(scp) ((struct hpt_scsi_pointer *)&(scp)->SCp)
+#define HPT_SCP(scp) ((struct hpt_cmd_priv *)scsi_cmd_priv(scp))
+
+enum hptiop_family {
+ UNKNOWN_BASED_IOP,
+ INTEL_BASED_IOP,
+ MV_BASED_IOP,
+ MVFREY_BASED_IOP
+} ;
struct hptiop_hba {
struct hptiop_adapter_ops *ops;
@@ -236,6 +279,22 @@ struct hptiop_hba {
void *internal_req;
dma_addr_t internal_req_phy;
} mv;
+ struct {
+ struct hpt_iop_request_get_config __iomem *config;
+ struct hpt_iopmu_mvfrey __iomem *mu;
+
+ int internal_mem_size;
+ struct hptiop_request internal_req;
+ int list_count;
+ struct mvfrey_inlist_entry *inlist;
+ dma_addr_t inlist_phy;
+ __le32 inlist_wptr;
+ struct mvfrey_outlist_entry *outlist;
+ dma_addr_t outlist_phy;
+ __le32 *outlist_cptr; /* copy pointer shadow */
+ dma_addr_t outlist_cptr_phy;
+ __le32 outlist_rptr;
+ } mvfrey;
} u;
struct Scsi_Host *host;
@@ -260,8 +319,8 @@ struct hptiop_hba {
struct hptiop_request reqs[HPTIOP_MAX_REQUESTS];
/* used to free allocated dma area */
- void *dma_coherent;
- dma_addr_t dma_coherent_handle;
+ void *dma_coherent[HPTIOP_MAX_REQUESTS];
+ dma_addr_t dma_coherent_handle[HPTIOP_MAX_REQUESTS];
atomic_t reset_count;
atomic_t resetting;
@@ -283,6 +342,7 @@ struct hpt_ioctl_k {
};
struct hptiop_adapter_ops {
+ enum hptiop_family family;
int (*iop_wait_ready)(struct hptiop_hba *hba, u32 millisec);
int (*internal_memalloc)(struct hptiop_hba *hba);
int (*internal_memfree)(struct hptiop_hba *hba);
@@ -297,6 +357,9 @@ struct hptiop_adapter_ops {
int (*iop_intr)(struct hptiop_hba *hba);
void (*post_msg)(struct hptiop_hba *hba, u32 msg);
void (*post_req)(struct hptiop_hba *hba, struct hptiop_request *_req);
+ int hw_dma_bit_mask;
+ int (*reset_comm)(struct hptiop_hba *hba);
+ __le64 host_phy_flag;
};
#define HPT_IOCTL_RESULT_OK 0
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
deleted file mode 100644
index 67fc8ffd52e6..000000000000
--- a/drivers/scsi/ibmmca.c
+++ /dev/null
@@ -1,2380 +0,0 @@
-/*
- Low Level Linux Driver for the IBM Microchannel SCSI Subsystem for
- Linux Kernel >= 2.4.0.
- Copyright (c) 1995 Strom Systems, Inc. under the terms of the GNU
- General Public License. Written by Martin Kolinek, December 1995.
- Further development by: Chris Beauregard, Klaus Kudielka, Michael Lang
- See the file Documentation/scsi/ibmmca.txt for a detailed description
- of this driver, the commandline arguments and the history of its
- development.
- See the WWW-page: http://www.uni-mainz.de/~langm000/linux.html for latest
- updates, info and ADF-files for adapters supported by this driver.
-
- Alan Cox <alan@lxorguk.ukuu.org.uk>
- Updated for Linux 2.5.45 to use the new error handler, cleaned up the
- lock macros and did a few unavoidable locking tweaks, plus one locking
- fix in the irq and completion path.
-
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/blkdev.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/mca.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-/* Common forward declarations for all Linux-versions: */
-static int ibmmca_queuecommand (struct Scsi_Host *, struct scsi_cmnd *);
-static int ibmmca_abort (Scsi_Cmnd *);
-static int ibmmca_host_reset (Scsi_Cmnd *);
-static int ibmmca_biosparam (struct scsi_device *, struct block_device *, sector_t, int *);
-static int ibmmca_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout);
-
-
-
-/* current version of this driver-source: */
-#define IBMMCA_SCSI_DRIVER_VERSION "4.0b-ac"
-
-/* driver configuration */
-#define IM_MAX_HOSTS 8 /* maximum number of host adapters */
-#define IM_RESET_DELAY 60 /* seconds allowed for a reset */
-
-/* driver debugging - #undef all for normal operation */
-/* if defined: count interrupts and ignore this special one: */
-#undef IM_DEBUG_TIMEOUT //50
-#define TIMEOUT_PUN 0
-#define TIMEOUT_LUN 0
-/* verbose interrupt: */
-#undef IM_DEBUG_INT
-/* verbose queuecommand: */
-#undef IM_DEBUG_CMD
-/* verbose queucommand for specific SCSI-device type: */
-#undef IM_DEBUG_CMD_SPEC_DEV
-/* verbose device probing */
-#undef IM_DEBUG_PROBE
-
-/* device type that shall be displayed on syslog (only during debugging): */
-#define IM_DEBUG_CMD_DEVICE TYPE_TAPE
-
-/* relative addresses of hardware registers on a subsystem */
-#define IM_CMD_REG(h) ((h)->io_port) /*Command Interface, (4 bytes long) */
-#define IM_ATTN_REG(h) ((h)->io_port+4) /*Attention (1 byte) */
-#define IM_CTR_REG(h) ((h)->io_port+5) /*Basic Control (1 byte) */
-#define IM_INTR_REG(h) ((h)->io_port+6) /*Interrupt Status (1 byte, r/o) */
-#define IM_STAT_REG(h) ((h)->io_port+7) /*Basic Status (1 byte, read only) */
-
-/* basic I/O-port of first adapter */
-#define IM_IO_PORT 0x3540
-/* maximum number of hosts that can be found */
-#define IM_N_IO_PORT 8
-
-/*requests going into the upper nibble of the Attention register */
-/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
-#define IM_IMM_CMD 0x10 /*immediate command */
-#define IM_SCB 0x30 /*Subsystem Control Block command */
-#define IM_LONG_SCB 0x40 /*long Subsystem Control Block command */
-#define IM_EOI 0xe0 /*end-of-interrupt request */
-
-/*values for bits 7,1,0 of Basic Control reg. (bits 6-2 reserved) */
-#define IM_HW_RESET 0x80 /*hardware reset */
-#define IM_ENABLE_DMA 0x02 /*enable subsystem's busmaster DMA */
-#define IM_ENABLE_INTR 0x01 /*enable interrupts to the system */
-
-/*to interpret the upper nibble of Interrupt Status register */
-/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
-#define IM_SCB_CMD_COMPLETED 0x10
-#define IM_SCB_CMD_COMPLETED_WITH_RETRIES 0x50
-#define IM_LOOP_SCATTER_BUFFER_FULL 0x60
-#define IM_ADAPTER_HW_FAILURE 0x70
-#define IM_IMMEDIATE_CMD_COMPLETED 0xa0
-#define IM_CMD_COMPLETED_WITH_FAILURE 0xc0
-#define IM_CMD_ERROR 0xe0
-#define IM_SOFTWARE_SEQUENCING_ERROR 0xf0
-
-/*to interpret bits 3-0 of Basic Status register (bits 7-4 reserved) */
-#define IM_CMD_REG_FULL 0x08
-#define IM_CMD_REG_EMPTY 0x04
-#define IM_INTR_REQUEST 0x02
-#define IM_BUSY 0x01
-
-/*immediate commands (word written into low 2 bytes of command reg) */
-#define IM_RESET_IMM_CMD 0x0400
-#define IM_FEATURE_CTR_IMM_CMD 0x040c
-#define IM_DMA_PACING_IMM_CMD 0x040d
-#define IM_ASSIGN_IMM_CMD 0x040e
-#define IM_ABORT_IMM_CMD 0x040f
-#define IM_FORMAT_PREP_IMM_CMD 0x0417
-
-/*SCB (Subsystem Control Block) structure */
-struct im_scb {
- unsigned short command; /*command word (read, etc.) */
- unsigned short enable; /*enable word, modifies cmd */
- union {
- unsigned long log_blk_adr; /*block address on SCSI device */
- unsigned char scsi_cmd_length; /*6,10,12, for other scsi cmd */
- } u1;
- unsigned long sys_buf_adr; /*physical system memory adr */
- unsigned long sys_buf_length; /*size of sys mem buffer */
- unsigned long tsb_adr; /*Termination Status Block adr */
- unsigned long scb_chain_adr; /*optional SCB chain address */
- union {
- struct {
- unsigned short count; /*block count, on SCSI device */
- unsigned short length; /*block length, on SCSI device */
- } blk;
- unsigned char scsi_command[12]; /*other scsi command */
- } u2;
-};
-
-/*structure scatter-gather element (for list of system memory areas) */
-struct im_sge {
- void *address;
- unsigned long byte_length;
-};
-
-/*structure returned by a get_pos_info command: */
-struct im_pos_info {
- unsigned short pos_id; /* adapter id */
- unsigned char pos_3a; /* pos 3 (if pos 6 = 0) */
- unsigned char pos_2; /* pos 2 */
- unsigned char int_level; /* interrupt level IRQ 11 or 14 */
- unsigned char pos_4a; /* pos 4 (if pos 6 = 0) */
- unsigned short connector_size; /* MCA connector size: 16 or 32 Bit */
- unsigned char num_luns; /* number of supported luns per device */
- unsigned char num_puns; /* number of supported puns */
- unsigned char pacing_factor; /* pacing factor */
- unsigned char num_ldns; /* number of ldns available */
- unsigned char eoi_off; /* time EOI and interrupt inactive */
- unsigned char max_busy; /* time between reset and busy on */
- unsigned short cache_stat; /* ldn cachestat. Bit=1 = not cached */
- unsigned short retry_stat; /* retry status of ldns. Bit=1=disabled */
- unsigned char pos_4b; /* pos 4 (if pos 6 = 1) */
- unsigned char pos_3b; /* pos 3 (if pos 6 = 1) */
- unsigned char pos_6; /* pos 6 */
- unsigned char pos_5; /* pos 5 */
- unsigned short max_overlap; /* maximum overlapping requests */
- unsigned short num_bus; /* number of SCSI-busses */
-};
-
-/*values for SCB command word */
-#define IM_NO_SYNCHRONOUS 0x0040 /*flag for any command */
-#define IM_NO_DISCONNECT 0x0080 /*flag for any command */
-#define IM_READ_DATA_CMD 0x1c01
-#define IM_WRITE_DATA_CMD 0x1c02
-#define IM_READ_VERIFY_CMD 0x1c03
-#define IM_WRITE_VERIFY_CMD 0x1c04
-#define IM_REQUEST_SENSE_CMD 0x1c08
-#define IM_READ_CAPACITY_CMD 0x1c09
-#define IM_DEVICE_INQUIRY_CMD 0x1c0b
-#define IM_READ_LOGICAL_CMD 0x1c2a
-#define IM_OTHER_SCSI_CMD_CMD 0x241f
-
-/* unused, but supported, SCB commands */
-#define IM_GET_COMMAND_COMPLETE_STATUS_CMD 0x1c07 /* command status */
-#define IM_GET_POS_INFO_CMD 0x1c0a /* returns neat stuff */
-#define IM_READ_PREFETCH_CMD 0x1c31 /* caching controller only */
-#define IM_FOMAT_UNIT_CMD 0x1c16 /* format unit */
-#define IM_REASSIGN_BLOCK_CMD 0x1c18 /* in case of error */
-
-/*values to set bits in the enable word of SCB */
-#define IM_READ_CONTROL 0x8000
-#define IM_REPORT_TSB_ONLY_ON_ERROR 0x4000
-#define IM_RETRY_ENABLE 0x2000
-#define IM_POINTER_TO_LIST 0x1000
-#define IM_SUPRESS_EXCEPTION_SHORT 0x0400
-#define IM_BYPASS_BUFFER 0x0200
-#define IM_CHAIN_ON_NO_ERROR 0x0001
-
-/*TSB (Termination Status Block) structure */
-struct im_tsb {
- unsigned short end_status;
- unsigned short reserved1;
- unsigned long residual_byte_count;
- unsigned long sg_list_element_adr;
- unsigned short status_length;
- unsigned char dev_status;
- unsigned char cmd_status;
- unsigned char dev_error;
- unsigned char cmd_error;
- unsigned short reserved2;
- unsigned short reserved3;
- unsigned short low_of_last_scb_adr;
- unsigned short high_of_last_scb_adr;
-};
-
-/*subsystem uses interrupt request level 14 */
-#define IM_IRQ 14
-/*SCSI-2 F/W may evade to interrupt 11 */
-#define IM_IRQ_FW 11
-
-/* Model 95 has an additional alphanumeric display, which can be used
- to display SCSI-activities. 8595 models do not have any disk led, which
- makes this feature quite useful.
- The regular PS/2 disk led is turned on/off by bits 6,7 of system
- control port. */
-
-/* LED display-port (actually, last LED on display) */
-#define MOD95_LED_PORT 0x108
-/* system-control-register of PS/2s with diskindicator */
-#define PS2_SYS_CTR 0x92
-/* activity displaying methods */
-#define LED_DISP 1
-#define LED_ADISP 2
-#define LED_ACTIVITY 4
-/* failed intr */
-#define CMD_FAIL 255
-
-/* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED
- displays. ldn is no longer displayed here, because the ldn mapping is now
- done dynamically and the ldn <-> pun,lun maps can be looked-up at boottime
- or during uptime in /proc/scsi/ibmmca/<host_no> in case of trouble,
- interest, debugging or just for having fun. The left number gives the
- host-adapter number and the right shows the accessed SCSI-ID. */
-
-/* display_mode is set by the ibmmcascsi= command line arg */
-static int display_mode = 0;
-/* set default adapter timeout */
-static unsigned int adapter_timeout = 45;
-/* for probing on feature-command: */
-static unsigned int global_command_error_excuse = 0;
-/* global setting by command line for adapter_speed */
-static int global_adapter_speed = 0; /* full speed by default */
-
-/* Panel / LED on, do it right for F/W addressin, too. adisplay will
- * just ignore ids>7, as the panel has only 7 digits available */
-#define PS2_DISK_LED_ON(ad,id) { if (display_mode & LED_DISP) { if (id>9) \
- outw((ad+48)|((id+55)<<8), MOD95_LED_PORT ); else \
- outw((ad+48)|((id+48)<<8), MOD95_LED_PORT ); } else \
- if (display_mode & LED_ADISP) { if (id<7) outb((char)(id+48),MOD95_LED_PORT+1+id); \
- outb((char)(ad+48), MOD95_LED_PORT); } \
- if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
- outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); }
-
-/* Panel / LED off */
-/* bug fixed, Dec 15, 1997, where | was replaced by & here */
-#define PS2_DISK_LED_OFF() { if (display_mode & LED_DISP) \
- outw(0x2020, MOD95_LED_PORT ); else if (display_mode & LED_ADISP) { \
- outl(0x20202020,MOD95_LED_PORT); outl(0x20202020,MOD95_LED_PORT+4); } \
- if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
- outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); }
-
-/* types of different supported hardware that goes to hostdata special */
-#define IBM_SCSI2_FW 0
-#define IBM_7568_WCACHE 1
-#define IBM_EXP_UNIT 2
-#define IBM_SCSI_WCACHE 3
-#define IBM_SCSI 4
-#define IBM_INTEGSCSI 5
-
-/* other special flags for hostdata structure */
-#define FORCED_DETECTION 100
-#define INTEGRATED_SCSI 101
-
-/* List of possible IBM-SCSI-adapters */
-static short ibmmca_id_table[] = {
- 0x8efc,
- 0x8efd,
- 0x8ef8,
- 0x8eff,
- 0x8efe,
- /* No entry for integrated SCSI, that's part of the register */
- 0
-};
-
-static const char *ibmmca_description[] = {
- "IBM SCSI-2 F/W Adapter", /* special = 0 */
- "IBM 7568 Industrial Computer SCSI Adapter w/Cache", /* special = 1 */
- "IBM Expansion Unit SCSI Controller", /* special = 2 */
- "IBM SCSI Adapter w/Cache", /* special = 3 */
- "IBM SCSI Adapter", /* special = 4 */
- "IBM Integrated SCSI Controller", /* special = 5 */
-};
-
-/* Max number of logical devices (can be up from 0 to 14). 15 is the address
-of the adapter itself. */
-#define MAX_LOG_DEV 15
-
-/*local data for a logical device */
-struct logical_device {
- struct im_scb scb; /* SCSI-subsystem-control-block structure */
- struct im_tsb tsb; /* SCSI command complete status block structure */
- struct im_sge sge[16]; /* scatter gather list structure */
- unsigned char buf[256]; /* SCSI command return data buffer */
- Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */
- int device_type; /* type of the SCSI-device. See include/scsi/scsi.h
- for interpretation of the possible values */
- int block_length; /* blocksize of a particular logical SCSI-device */
- int cache_flag; /* 1 if this is uncached, 0 if cache is present for ldn */
- int retry_flag; /* 1 if adapter retry is disabled, 0 if enabled */
-};
-
-/* statistics of the driver during operations (for proc_info) */
-struct Driver_Statistics {
- /* SCSI statistics on the adapter */
- int ldn_access[MAX_LOG_DEV + 1]; /* total accesses on a ldn */
- int ldn_read_access[MAX_LOG_DEV + 1]; /* total read-access on a ldn */
- int ldn_write_access[MAX_LOG_DEV + 1]; /* total write-access on a ldn */
- int ldn_inquiry_access[MAX_LOG_DEV + 1]; /* total inquiries on a ldn */
- int ldn_modeselect_access[MAX_LOG_DEV + 1]; /* total mode selects on ldn */
- int scbs; /* short SCBs queued */
- int long_scbs; /* long SCBs queued */
- int total_accesses; /* total accesses on all ldns */
- int total_interrupts; /* total interrupts (should be
- same as total_accesses) */
- int total_errors; /* command completed with error */
- /* dynamical assignment statistics */
- int total_scsi_devices; /* number of physical pun,lun */
- int dyn_flag; /* flag showing dynamical mode */
- int dynamical_assignments; /* number of remappings of ldns */
- int ldn_assignments[MAX_LOG_DEV + 1]; /* number of remappings of each
- ldn */
-};
-
-/* data structure for each host adapter */
-struct ibmmca_hostdata {
- /* array of logical devices: */
- struct logical_device _ld[MAX_LOG_DEV + 1];
- /* array to convert (pun, lun) into logical device number: */
- unsigned char _get_ldn[16][8];
- /*array that contains the information about the physical SCSI-devices
- attached to this host adapter: */
- unsigned char _get_scsi[16][8];
- /* used only when checking logical devices: */
- int _local_checking_phase_flag;
- /* report received interrupt: */
- int _got_interrupt;
- /* report termination-status of SCSI-command: */
- int _stat_result;
- /* reset status (used only when doing reset): */
- int _reset_status;
- /* code of the last SCSI command (needed for panic info): */
- int _last_scsi_command[MAX_LOG_DEV + 1];
- /* identifier of the last SCSI-command type */
- int _last_scsi_type[MAX_LOG_DEV + 1];
- /* last blockcount */
- int _last_scsi_blockcount[MAX_LOG_DEV + 1];
- /* last locgical block address */
- unsigned long _last_scsi_logical_block[MAX_LOG_DEV + 1];
- /* Counter that points on the next reassignable ldn for dynamical
- remapping. The default value is 7, that is the first reassignable
- number in the list at boottime: */
- int _next_ldn;
- /* Statistics-structure for this IBM-SCSI-host: */
- struct Driver_Statistics _IBM_DS;
- /* This hostadapters pos-registers pos2 until pos6 */
- unsigned int _pos[8];
- /* assign a special variable, that contains dedicated info about the
- adaptertype */
- int _special;
- /* connector size on the MCA bus */
- int _connector_size;
- /* synchronous SCSI transfer rate bitpattern */
- int _adapter_speed;
-};
-
-/* macros to access host data structure */
-#define subsystem_pun(h) ((h)->this_id)
-#define subsystem_maxid(h) ((h)->max_id)
-#define ld(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_ld)
-#define get_ldn(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_get_ldn)
-#define get_scsi(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_get_scsi)
-#define local_checking_phase_flag(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_local_checking_phase_flag)
-#define got_interrupt(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_got_interrupt)
-#define stat_result(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_stat_result)
-#define reset_status(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_reset_status)
-#define last_scsi_command(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_command)
-#define last_scsi_type(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_type)
-#define last_scsi_blockcount(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_blockcount)
-#define last_scsi_logical_block(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_logical_block)
-#define last_scsi_type(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_type)
-#define next_ldn(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_next_ldn)
-#define IBM_DS(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_IBM_DS)
-#define special(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_special)
-#define subsystem_connector_size(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_connector_size)
-#define adapter_speed(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_adapter_speed)
-#define pos2(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[2])
-#define pos3(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[3])
-#define pos4(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[4])
-#define pos5(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[5])
-#define pos6(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[6])
-
-/* Define a arbitrary number as subsystem-marker-type. This number is, as
- described in the ANSI-SCSI-standard, not occupied by other device-types. */
-#define TYPE_IBM_SCSI_ADAPTER 0x2F
-
-/* Define 0xFF for no device type, because this type is not defined within
- the ANSI-SCSI-standard, therefore, it can be used and should not cause any
- harm. */
-#define TYPE_NO_DEVICE 0xFF
-
-/* define medium-changer. If this is not defined previously, e.g. Linux
- 2.0.x, define this type here. */
-#ifndef TYPE_MEDIUM_CHANGER
-#define TYPE_MEDIUM_CHANGER 0x08
-#endif
-
-/* define possible operations for the immediate_assign command */
-#define SET_LDN 0
-#define REMOVE_LDN 1
-
-/* ldn which is used to probe the SCSI devices */
-#define PROBE_LDN 0
-
-/* reset status flag contents */
-#define IM_RESET_NOT_IN_PROGRESS 0
-#define IM_RESET_IN_PROGRESS 1
-#define IM_RESET_FINISHED_OK 2
-#define IM_RESET_FINISHED_FAIL 3
-#define IM_RESET_NOT_IN_PROGRESS_NO_INT 4
-#define IM_RESET_FINISHED_OK_NO_INT 5
-
-/* define undefined SCSI-command */
-#define NO_SCSI 0xffff
-
-/*-----------------------------------------------------------------------*/
-
-/* if this is nonzero, ibmmcascsi option has been passed to the kernel */
-static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
-static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 };
-
-/* fill module-parameters only, when this define is present.
- (that is kernel version 2.1.x) */
-#if defined(MODULE)
-static char *boot_options = NULL;
-module_param(boot_options, charp, 0);
-module_param_array(io_port, int, NULL, 0);
-module_param_array(scsi_id, int, NULL, 0);
-
-MODULE_LICENSE("GPL");
-#endif
-/*counter of concurrent disk read/writes, to turn on/off disk led */
-static int disk_rw_in_progress = 0;
-
-static unsigned int pos[8]; /* whole pos register-line for diagnosis */
-/* Taking into account the additions, made by ZP Gu.
- * This selects now the preset value from the configfile and
- * offers the 'normal' commandline option to be accepted */
-#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD
-static char ibm_ansi_order = 1;
-#else
-static char ibm_ansi_order = 0;
-#endif
-
-static void issue_cmd(struct Scsi_Host *, unsigned long, unsigned char);
-static void internal_done(Scsi_Cmnd * cmd);
-static void check_devices(struct Scsi_Host *, int);
-static int immediate_assign(struct Scsi_Host *, unsigned int, unsigned int, unsigned int, unsigned int);
-static int immediate_feature(struct Scsi_Host *, unsigned int, unsigned int);
-#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
-static int immediate_reset(struct Scsi_Host *, unsigned int);
-#endif
-static int device_inquiry(struct Scsi_Host *, int);
-static int read_capacity(struct Scsi_Host *, int);
-static int get_pos_info(struct Scsi_Host *);
-static char *ti_p(int);
-static char *ti_l(int);
-static char *ibmrate(unsigned int, int);
-static int probe_display(int);
-static int probe_bus_mode(struct Scsi_Host *);
-static int device_exists(struct Scsi_Host *, int, int *, int *);
-static int option_setup(char *);
-/* local functions needed for proc_info */
-static int ldn_access_load(struct Scsi_Host *, int);
-static int ldn_access_total_read_write(struct Scsi_Host *);
-
-static irqreturn_t interrupt_handler(int irq, void *dev_id)
-{
- unsigned int intr_reg;
- unsigned int cmd_result;
- unsigned int ldn;
- unsigned long flags;
- Scsi_Cmnd *cmd;
- int lastSCSI;
- struct device *dev = dev_id;
- struct Scsi_Host *shpnt = dev_get_drvdata(dev);
-
- spin_lock_irqsave(shpnt->host_lock, flags);
-
- if(!(inb(IM_STAT_REG(shpnt)) & IM_INTR_REQUEST)) {
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_NONE;
- }
-
- /* the reset-function already did all the job, even ints got
- renabled on the subsystem, so just return */
- if ((reset_status(shpnt) == IM_RESET_NOT_IN_PROGRESS_NO_INT) || (reset_status(shpnt) == IM_RESET_FINISHED_OK_NO_INT)) {
- reset_status(shpnt) = IM_RESET_NOT_IN_PROGRESS;
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- }
-
- /*must wait for attention reg not busy, then send EOI to subsystem */
- while (1) {
- if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
- break;
- cpu_relax();
- }
-
- /*get command result and logical device */
- intr_reg = (unsigned char) (inb(IM_INTR_REG(shpnt)));
- cmd_result = intr_reg & 0xf0;
- ldn = intr_reg & 0x0f;
- /* get the last_scsi_command here */
- lastSCSI = last_scsi_command(shpnt)[ldn];
- outb(IM_EOI | ldn, IM_ATTN_REG(shpnt));
-
- /*these should never happen (hw fails, or a local programming bug) */
- if (!global_command_error_excuse) {
- switch (cmd_result) {
- /* Prevent from Ooopsing on error to show the real reason */
- case IM_ADAPTER_HW_FAILURE:
- case IM_SOFTWARE_SEQUENCING_ERROR:
- case IM_CMD_ERROR:
- printk(KERN_ERR "IBM MCA SCSI: Fatal Subsystem ERROR!\n");
- printk(KERN_ERR " Last cmd=0x%x, ena=%x, len=", lastSCSI, ld(shpnt)[ldn].scb.enable);
- if (ld(shpnt)[ldn].cmd)
- printk("%ld/%ld,", (long) (scsi_bufflen(ld(shpnt)[ldn].cmd)), (long) (ld(shpnt)[ldn].scb.sys_buf_length));
- else
- printk("none,");
- if (ld(shpnt)[ldn].cmd)
- printk("Blocksize=%d", ld(shpnt)[ldn].scb.u2.blk.length);
- else
- printk("Blocksize=none");
- printk(", host=%p, ldn=0x%x\n", shpnt, ldn);
- if (ld(shpnt)[ldn].cmd) {
- printk(KERN_ERR "Blockcount=%d/%d\n", last_scsi_blockcount(shpnt)[ldn], ld(shpnt)[ldn].scb.u2.blk.count);
- printk(KERN_ERR "Logical block=%lx/%lx\n", last_scsi_logical_block(shpnt)[ldn], ld(shpnt)[ldn].scb.u1.log_blk_adr);
- }
- printk(KERN_ERR "Reason given: %s\n", (cmd_result == IM_ADAPTER_HW_FAILURE) ? "HARDWARE FAILURE" : (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) ? "SOFTWARE SEQUENCING ERROR" : (cmd_result == IM_CMD_ERROR) ? "COMMAND ERROR" : "UNKNOWN");
- /* if errors appear, enter this section to give detailed info */
- printk(KERN_ERR "IBM MCA SCSI: Subsystem Error-Status follows:\n");
- printk(KERN_ERR " Command Type................: %x\n", last_scsi_type(shpnt)[ldn]);
- printk(KERN_ERR " Attention Register..........: %x\n", inb(IM_ATTN_REG(shpnt)));
- printk(KERN_ERR " Basic Control Register......: %x\n", inb(IM_CTR_REG(shpnt)));
- printk(KERN_ERR " Interrupt Status Register...: %x\n", intr_reg);
- printk(KERN_ERR " Basic Status Register.......: %x\n", inb(IM_STAT_REG(shpnt)));
- if ((last_scsi_type(shpnt)[ldn] == IM_SCB) || (last_scsi_type(shpnt)[ldn] == IM_LONG_SCB)) {
- printk(KERN_ERR " SCB-Command.................: %x\n", ld(shpnt)[ldn].scb.command);
- printk(KERN_ERR " SCB-Enable..................: %x\n", ld(shpnt)[ldn].scb.enable);
- printk(KERN_ERR " SCB-logical block address...: %lx\n", ld(shpnt)[ldn].scb.u1.log_blk_adr);
- printk(KERN_ERR " SCB-system buffer address...: %lx\n", ld(shpnt)[ldn].scb.sys_buf_adr);
- printk(KERN_ERR " SCB-system buffer length....: %lx\n", ld(shpnt)[ldn].scb.sys_buf_length);
- printk(KERN_ERR " SCB-tsb address.............: %lx\n", ld(shpnt)[ldn].scb.tsb_adr);
- printk(KERN_ERR " SCB-Chain address...........: %lx\n", ld(shpnt)[ldn].scb.scb_chain_adr);
- printk(KERN_ERR " SCB-block count.............: %x\n", ld(shpnt)[ldn].scb.u2.blk.count);
- printk(KERN_ERR " SCB-block length............: %x\n", ld(shpnt)[ldn].scb.u2.blk.length);
- }
- printk(KERN_ERR " Send this report to the maintainer.\n");
- panic("IBM MCA SCSI: Fatal error message from the subsystem (0x%X,0x%X)!\n", lastSCSI, cmd_result);
- break;
- }
- } else {
- /* The command error handling is made silent, but we tell the
- * calling function, that there is a reported error from the
- * adapter. */
- switch (cmd_result) {
- case IM_ADAPTER_HW_FAILURE:
- case IM_SOFTWARE_SEQUENCING_ERROR:
- case IM_CMD_ERROR:
- global_command_error_excuse = CMD_FAIL;
- break;
- default:
- global_command_error_excuse = 0;
- break;
- }
- }
- /* if no panic appeared, increase the interrupt-counter */
- IBM_DS(shpnt).total_interrupts++;
- /*only for local checking phase */
- if (local_checking_phase_flag(shpnt)) {
- stat_result(shpnt) = cmd_result;
- got_interrupt(shpnt) = 1;
- reset_status(shpnt) = IM_RESET_FINISHED_OK;
- last_scsi_command(shpnt)[ldn] = NO_SCSI;
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- }
- /* handling of commands coming from upper level of scsi driver */
- if (last_scsi_type(shpnt)[ldn] == IM_IMM_CMD) {
- /* verify ldn, and may handle rare reset immediate command */
- if ((reset_status(shpnt) == IM_RESET_IN_PROGRESS) && (last_scsi_command(shpnt)[ldn] == IM_RESET_IMM_CMD)) {
- if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {
- disk_rw_in_progress = 0;
- PS2_DISK_LED_OFF();
- reset_status(shpnt) = IM_RESET_FINISHED_FAIL;
- } else {
- /*reset disk led counter, turn off disk led */
- disk_rw_in_progress = 0;
- PS2_DISK_LED_OFF();
- reset_status(shpnt) = IM_RESET_FINISHED_OK;
- }
- stat_result(shpnt) = cmd_result;
- last_scsi_command(shpnt)[ldn] = NO_SCSI;
- last_scsi_type(shpnt)[ldn] = 0;
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- } else if (last_scsi_command(shpnt)[ldn] == IM_ABORT_IMM_CMD) {
- /* react on SCSI abort command */
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: Interrupt from SCSI-abort.\n");
-#endif
- disk_rw_in_progress = 0;
- PS2_DISK_LED_OFF();
- cmd = ld(shpnt)[ldn].cmd;
- ld(shpnt)[ldn].cmd = NULL;
- if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
- cmd->result = DID_NO_CONNECT << 16;
- else
- cmd->result = DID_ABORT << 16;
- stat_result(shpnt) = cmd_result;
- last_scsi_command(shpnt)[ldn] = NO_SCSI;
- last_scsi_type(shpnt)[ldn] = 0;
- if (cmd->scsi_done)
- (cmd->scsi_done) (cmd); /* should be the internal_done */
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- } else {
- disk_rw_in_progress = 0;
- PS2_DISK_LED_OFF();
- reset_status(shpnt) = IM_RESET_FINISHED_OK;
- stat_result(shpnt) = cmd_result;
- last_scsi_command(shpnt)[ldn] = NO_SCSI;
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- }
- }
- last_scsi_command(shpnt)[ldn] = NO_SCSI;
- last_scsi_type(shpnt)[ldn] = 0;
- cmd = ld(shpnt)[ldn].cmd;
- ld(shpnt)[ldn].cmd = NULL;
-#ifdef IM_DEBUG_TIMEOUT
- if (cmd) {
- if ((cmd->target == TIMEOUT_PUN) && (cmd->device->lun == TIMEOUT_LUN)) {
- spin_unlock_irqsave(shpnt->host_lock, flags);
- printk("IBM MCA SCSI: Ignoring interrupt from pun=%x, lun=%x.\n", cmd->target, cmd->device->lun);
- return IRQ_HANDLED;
- }
- }
-#endif
- /*if no command structure, just return, else clear cmd */
- if (!cmd)
- {
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
- }
-
-#ifdef IM_DEBUG_INT
- printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", cmd->cmnd[0], intr_reg, ld(shpnt)[ldn].tsb.dev_status, ld(shpnt)[ldn].tsb.cmd_status, ld(shpnt)[ldn].tsb.dev_error, ld(shpnt)[ldn].tsb.cmd_error);
-#endif
- /*if this is end of media read/write, may turn off PS/2 disk led */
- if ((ld(shpnt)[ldn].device_type != TYPE_NO_LUN) && (ld(shpnt)[ldn].device_type != TYPE_NO_DEVICE)) {
- /* only access this, if there was a valid device addressed */
- if (--disk_rw_in_progress == 0)
- PS2_DISK_LED_OFF();
- }
-
- /* IBM describes the status-mask to be 0x1e, but this is not conform
- * with SCSI-definition, I suppose, the reason for it is that IBM
- * adapters do not support CMD_TERMINATED, TASK_SET_FULL and
- * ACA_ACTIVE as returning statusbyte information. (ML) */
- if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {
- cmd->result = (unsigned char) (ld(shpnt)[ldn].tsb.dev_status & 0x1e);
- IBM_DS(shpnt).total_errors++;
- } else
- cmd->result = 0;
- /* write device status into cmd->result, and call done function */
- if (lastSCSI == NO_SCSI) { /* unexpected interrupt :-( */
- cmd->result |= DID_BAD_INTR << 16;
- printk("IBM MCA SCSI: WARNING - Interrupt from non-pending SCSI-command!\n");
- } else /* things went right :-) */
- cmd->result |= DID_OK << 16;
- if (cmd->scsi_done)
- (cmd->scsi_done) (cmd);
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return IRQ_HANDLED;
-}
-
-static void issue_cmd(struct Scsi_Host *shpnt, unsigned long cmd_reg,
- unsigned char attn_reg)
-{
- unsigned long flags;
- /* must wait for attention reg not busy */
- while (1) {
- spin_lock_irqsave(shpnt->host_lock, flags);
- if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
- break;
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- }
- /* write registers and enable system interrupts */
- outl(cmd_reg, IM_CMD_REG(shpnt));
- outb(attn_reg, IM_ATTN_REG(shpnt));
- spin_unlock_irqrestore(shpnt->host_lock, flags);
-}
-
-static void internal_done(Scsi_Cmnd * cmd)
-{
- cmd->SCp.Status++;
- return;
-}
-
-/* SCSI-SCB-command for device_inquiry */
-static int device_inquiry(struct Scsi_Host *shpnt, int ldn)
-{
- int retr;
- struct im_scb *scb;
- struct im_tsb *tsb;
- unsigned char *buf;
-
- scb = &(ld(shpnt)[ldn].scb);
- tsb = &(ld(shpnt)[ldn].tsb);
- buf = (unsigned char *) (&(ld(shpnt)[ldn].buf));
- ld(shpnt)[ldn].tsb.dev_status = 0; /* prepare statusblock */
- for (retr = 0; retr < 3; retr++) {
- /* fill scb with inquiry command */
- scb->command = IM_DEVICE_INQUIRY_CMD | IM_NO_DISCONNECT;
- scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
- last_scsi_command(shpnt)[ldn] = IM_DEVICE_INQUIRY_CMD;
- last_scsi_type(shpnt)[ldn] = IM_SCB;
- scb->sys_buf_adr = isa_virt_to_bus(buf);
- scb->sys_buf_length = 255; /* maximum bufferlength gives max info */
- scb->tsb_adr = isa_virt_to_bus(tsb);
- /* issue scb to passed ldn, and busy wait for interrupt */
- got_interrupt(shpnt) = 0;
- issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | ldn);
- while (!got_interrupt(shpnt))
- barrier();
-
- /*if command successful, break */
- if ((stat_result(shpnt) == IM_SCB_CMD_COMPLETED) || (stat_result(shpnt) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
- return 1;
- }
- /*if all three retries failed, return "no device at this ldn" */
- if (retr >= 3)
- return 0;
- else
- return 1;
-}
-
-static int read_capacity(struct Scsi_Host *shpnt, int ldn)
-{
- int retr;
- struct im_scb *scb;
- struct im_tsb *tsb;
- unsigned char *buf;
-
- scb = &(ld(shpnt)[ldn].scb);
- tsb = &(ld(shpnt)[ldn].tsb);
- buf = (unsigned char *) (&(ld(shpnt)[ldn].buf));
- ld(shpnt)[ldn].tsb.dev_status = 0;
- for (retr = 0; retr < 3; retr++) {
- /*fill scb with read capacity command */
- scb->command = IM_READ_CAPACITY_CMD;
- scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
- last_scsi_command(shpnt)[ldn] = IM_READ_CAPACITY_CMD;
- last_scsi_type(shpnt)[ldn] = IM_SCB;
- scb->sys_buf_adr = isa_virt_to_bus(buf);
- scb->sys_buf_length = 8;
- scb->tsb_adr = isa_virt_to_bus(tsb);
- /*issue scb to passed ldn, and busy wait for interrupt */
- got_interrupt(shpnt) = 0;
- issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | ldn);
- while (!got_interrupt(shpnt))
- barrier();
-
- /*if got capacity, get block length and return one device found */
- if ((stat_result(shpnt) == IM_SCB_CMD_COMPLETED) || (stat_result(shpnt) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
- return 1;
- }
- /*if all three retries failed, return "no device at this ldn" */
- if (retr >= 3)
- return 0;
- else
- return 1;
-}
-
-static int get_pos_info(struct Scsi_Host *shpnt)
-{
- int retr;
- struct im_scb *scb;
- struct im_tsb *tsb;
- unsigned char *buf;
-
- scb = &(ld(shpnt)[MAX_LOG_DEV].scb);
- tsb = &(ld(shpnt)[MAX_LOG_DEV].tsb);
- buf = (unsigned char *) (&(ld(shpnt)[MAX_LOG_DEV].buf));
- ld(shpnt)[MAX_LOG_DEV].tsb.dev_status = 0;
- for (retr = 0; retr < 3; retr++) {
- /*fill scb with get_pos_info command */
- scb->command = IM_GET_POS_INFO_CMD;
- scb->enable = IM_READ_CONTROL | IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
- last_scsi_command(shpnt)[MAX_LOG_DEV] = IM_GET_POS_INFO_CMD;
- last_scsi_type(shpnt)[MAX_LOG_DEV] = IM_SCB;
- scb->sys_buf_adr = isa_virt_to_bus(buf);
- if (special(shpnt) == IBM_SCSI2_FW)
- scb->sys_buf_length = 256; /* get all info from F/W adapter */
- else
- scb->sys_buf_length = 18; /* get exactly 18 bytes for other SCSI */
- scb->tsb_adr = isa_virt_to_bus(tsb);
- /*issue scb to ldn=15, and busy wait for interrupt */
- got_interrupt(shpnt) = 0;
- issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | MAX_LOG_DEV);
-
- /* FIXME: timeout */
- while (!got_interrupt(shpnt))
- barrier();
-
- /*if got POS-stuff, get block length and return one device found */
- if ((stat_result(shpnt) == IM_SCB_CMD_COMPLETED) || (stat_result(shpnt) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
- return 1;
- }
- /* if all three retries failed, return "no device at this ldn" */
- if (retr >= 3)
- return 0;
- else
- return 1;
-}
-
-/* SCSI-immediate-command for assign. This functions maps/unmaps specific
- ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the
- subsystem and for dynamical remapping od ldns. */
-static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun,
- unsigned int lun, unsigned int ldn,
- unsigned int operation)
-{
- int retr;
- unsigned long imm_cmd;
-
- for (retr = 0; retr < 3; retr++) {
- /* select mutation level of the SCSI-adapter */
- switch (special(shpnt)) {
- case IBM_SCSI2_FW:
- imm_cmd = (unsigned long) (IM_ASSIGN_IMM_CMD);
- imm_cmd |= (unsigned long) ((lun & 7) << 24);
- imm_cmd |= (unsigned long) ((operation & 1) << 23);
- imm_cmd |= (unsigned long) ((pun & 7) << 20) | ((pun & 8) << 24);
- imm_cmd |= (unsigned long) ((ldn & 15) << 16);
- break;
- default:
- imm_cmd = inl(IM_CMD_REG(shpnt));
- imm_cmd &= (unsigned long) (0xF8000000); /* keep reserved bits */
- imm_cmd |= (unsigned long) (IM_ASSIGN_IMM_CMD);
- imm_cmd |= (unsigned long) ((lun & 7) << 24);
- imm_cmd |= (unsigned long) ((operation & 1) << 23);
- imm_cmd |= (unsigned long) ((pun & 7) << 20);
- imm_cmd |= (unsigned long) ((ldn & 15) << 16);
- break;
- }
- last_scsi_command(shpnt)[MAX_LOG_DEV] = IM_ASSIGN_IMM_CMD;
- last_scsi_type(shpnt)[MAX_LOG_DEV] = IM_IMM_CMD;
- got_interrupt(shpnt) = 0;
- issue_cmd(shpnt, (unsigned long) (imm_cmd), IM_IMM_CMD | MAX_LOG_DEV);
- while (!got_interrupt(shpnt))
- barrier();
-
- /*if command successful, break */
- if (stat_result(shpnt) == IM_IMMEDIATE_CMD_COMPLETED)
- return 1;
- }
- if (retr >= 3)
- return 0;
- else
- return 1;
-}
-
-static int immediate_feature(struct Scsi_Host *shpnt, unsigned int speed, unsigned int timeout)
-{
- int retr;
- unsigned long imm_cmd;
-
- for (retr = 0; retr < 3; retr++) {
- /* select mutation level of the SCSI-adapter */
- imm_cmd = IM_FEATURE_CTR_IMM_CMD;
- imm_cmd |= (unsigned long) ((speed & 0x7) << 29);
- imm_cmd |= (unsigned long) ((timeout & 0x1fff) << 16);
- last_scsi_command(shpnt)[MAX_LOG_DEV] = IM_FEATURE_CTR_IMM_CMD;
- last_scsi_type(shpnt)[MAX_LOG_DEV] = IM_IMM_CMD;
- got_interrupt(shpnt) = 0;
- /* we need to run into command errors in order to probe for the
- * right speed! */
- global_command_error_excuse = 1;
- issue_cmd(shpnt, (unsigned long) (imm_cmd), IM_IMM_CMD | MAX_LOG_DEV);
-
- /* FIXME: timeout */
- while (!got_interrupt(shpnt))
- barrier();
- if (global_command_error_excuse == CMD_FAIL) {
- global_command_error_excuse = 0;
- return 2;
- } else
- global_command_error_excuse = 0;
- /*if command successful, break */
- if (stat_result(shpnt) == IM_IMMEDIATE_CMD_COMPLETED)
- return 1;
- }
- if (retr >= 3)
- return 0;
- else
- return 1;
-}
-
-#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
-static int immediate_reset(struct Scsi_Host *shpnt, unsigned int ldn)
-{
- int retries;
- int ticks;
- unsigned long imm_command;
-
- for (retries = 0; retries < 3; retries++) {
- imm_command = inl(IM_CMD_REG(shpnt));
- imm_command &= (unsigned long) (0xFFFF0000); /* keep reserved bits */
- imm_command |= (unsigned long) (IM_RESET_IMM_CMD);
- last_scsi_command(shpnt)[ldn] = IM_RESET_IMM_CMD;
- last_scsi_type(shpnt)[ldn] = IM_IMM_CMD;
- got_interrupt(shpnt) = 0;
- reset_status(shpnt) = IM_RESET_IN_PROGRESS;
- issue_cmd(shpnt, (unsigned long) (imm_command), IM_IMM_CMD | ldn);
- ticks = IM_RESET_DELAY * HZ;
- while (reset_status(shpnt) == IM_RESET_IN_PROGRESS && --ticks) {
- udelay((1 + 999 / HZ) * 1000);
- barrier();
- }
- /* if reset did not complete, just complain */
- if (!ticks) {
- printk(KERN_ERR "IBM MCA SCSI: reset did not complete within %d seconds.\n", IM_RESET_DELAY);
- reset_status(shpnt) = IM_RESET_FINISHED_OK;
- /* did not work, finish */
- return 1;
- }
- /*if command successful, break */
- if (stat_result(shpnt) == IM_IMMEDIATE_CMD_COMPLETED)
- return 1;
- }
- if (retries >= 3)
- return 0;
- else
- return 1;
-}
-#endif
-
-/* type-interpreter for physical device numbers */
-static char *ti_p(int dev)
-{
- switch (dev) {
- case TYPE_IBM_SCSI_ADAPTER:
- return ("A");
- case TYPE_DISK:
- return ("D");
- case TYPE_TAPE:
- return ("T");
- case TYPE_PROCESSOR:
- return ("P");
- case TYPE_WORM:
- return ("W");
- case TYPE_ROM:
- return ("R");
- case TYPE_SCANNER:
- return ("S");
- case TYPE_MOD:
- return ("M");
- case TYPE_MEDIUM_CHANGER:
- return ("C");
- case TYPE_NO_LUN:
- return ("+"); /* show NO_LUN */
- }
- return ("-"); /* TYPE_NO_DEVICE and others */
-}
-
-/* interpreter for logical device numbers (ldn) */
-static char *ti_l(int val)
-{
- const char hex[16] = "0123456789abcdef";
- static char answer[2];
-
- answer[1] = (char) (0x0);
- if (val <= MAX_LOG_DEV)
- answer[0] = hex[val];
- else
- answer[0] = '-';
- return (char *) &answer;
-}
-
-/* transfers bitpattern of the feature command to values in MHz */
-static char *ibmrate(unsigned int speed, int i)
-{
- switch (speed) {
- case 0:
- return i ? "5.00" : "10.00";
- case 1:
- return i ? "4.00" : "8.00";
- case 2:
- return i ? "3.33" : "6.66";
- case 3:
- return i ? "2.86" : "5.00";
- case 4:
- return i ? "2.50" : "4.00";
- case 5:
- return i ? "2.22" : "3.10";
- case 6:
- return i ? "2.00" : "2.50";
- case 7:
- return i ? "1.82" : "2.00";
- }
- return "---";
-}
-
-static int probe_display(int what)
-{
- static int rotator = 0;
- const char rotor[] = "|/-\\";
-
- if (!(display_mode & LED_DISP))
- return 0;
- if (!what) {
- outl(0x20202020, MOD95_LED_PORT);
- outl(0x20202020, MOD95_LED_PORT + 4);
- } else {
- outb('S', MOD95_LED_PORT + 7);
- outb('C', MOD95_LED_PORT + 6);
- outb('S', MOD95_LED_PORT + 5);
- outb('I', MOD95_LED_PORT + 4);
- outb('i', MOD95_LED_PORT + 3);
- outb('n', MOD95_LED_PORT + 2);
- outb('i', MOD95_LED_PORT + 1);
- outb((char) (rotor[rotator]), MOD95_LED_PORT);
- rotator++;
- if (rotator > 3)
- rotator = 0;
- }
- return 0;
-}
-
-static int probe_bus_mode(struct Scsi_Host *shpnt)
-{
- struct im_pos_info *info;
- int num_bus = 0;
- int ldn;
-
- info = (struct im_pos_info *) (&(ld(shpnt)[MAX_LOG_DEV].buf));
- if (get_pos_info(shpnt)) {
- if (info->connector_size & 0xf000)
- subsystem_connector_size(shpnt) = 16;
- else
- subsystem_connector_size(shpnt) = 32;
- num_bus |= (info->pos_4b & 8) >> 3;
- for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
- if ((special(shpnt) == IBM_SCSI_WCACHE) || (special(shpnt) == IBM_7568_WCACHE)) {
- if (!((info->cache_stat >> ldn) & 1))
- ld(shpnt)[ldn].cache_flag = 0;
- }
- if (!((info->retry_stat >> ldn) & 1))
- ld(shpnt)[ldn].retry_flag = 0;
- }
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: SCSI-Cache bits: ");
- for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
- printk("%d", ld(shpnt)[ldn].cache_flag);
- }
- printk("\nIBM MCA SCSI: SCSI-Retry bits: ");
- for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
- printk("%d", ld(shpnt)[ldn].retry_flag);
- }
- printk("\n");
-#endif
- }
- return num_bus;
-}
-
-/* probing scsi devices */
-static void check_devices(struct Scsi_Host *shpnt, int adaptertype)
-{
- int id, lun, ldn, ticks;
- int count_devices; /* local counter for connected device */
- int max_pun;
- int num_bus;
- int speedrun; /* local adapter_speed check variable */
-
- /* assign default values to certain variables */
- ticks = 0;
- count_devices = 0;
- IBM_DS(shpnt).dyn_flag = 0; /* normally no need for dynamical ldn management */
- IBM_DS(shpnt).total_errors = 0; /* set errorcounter to 0 */
- next_ldn(shpnt) = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired' */
-
- /* initialize the very important driver-informational arrays/structs */
- memset(ld(shpnt), 0, sizeof(ld(shpnt)));
- for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
- last_scsi_command(shpnt)[ldn] = NO_SCSI; /* emptify last SCSI-command storage */
- last_scsi_type(shpnt)[ldn] = 0;
- ld(shpnt)[ldn].cache_flag = 1;
- ld(shpnt)[ldn].retry_flag = 1;
- }
- memset(get_ldn(shpnt), TYPE_NO_DEVICE, sizeof(get_ldn(shpnt))); /* this is essential ! */
- memset(get_scsi(shpnt), TYPE_NO_DEVICE, sizeof(get_scsi(shpnt))); /* this is essential ! */
- for (lun = 0; lun < 8; lun++) {
- /* mark the adapter at its pun on all luns */
- get_scsi(shpnt)[subsystem_pun(shpnt)][lun] = TYPE_IBM_SCSI_ADAPTER;
- get_ldn(shpnt)[subsystem_pun(shpnt)][lun] = MAX_LOG_DEV; /* make sure, the subsystem
- ldn is active for all
- luns. */
- }
- probe_display(0); /* Supercool display usage during SCSI-probing. */
- /* This makes sense, when booting without any */
- /* monitor connected on model XX95. */
-
- /* STEP 1: */
- adapter_speed(shpnt) = global_adapter_speed;
- speedrun = adapter_speed(shpnt);
- while (immediate_feature(shpnt, speedrun, adapter_timeout) == 2) {
- probe_display(1);
- if (speedrun == 7)
- panic("IBM MCA SCSI: Cannot set Synchronous-Transfer-Rate!\n");
- speedrun++;
- if (speedrun > 7)
- speedrun = 7;
- }
- adapter_speed(shpnt) = speedrun;
- /* Get detailed information about the current adapter, necessary for
- * device operations: */
- num_bus = probe_bus_mode(shpnt);
-
- /* num_bus contains only valid data for the F/W adapter! */
- if (adaptertype == IBM_SCSI2_FW) { /* F/W SCSI adapter: */
- /* F/W adapter PUN-space extension evaluation: */
- if (num_bus) {
- printk(KERN_INFO "IBM MCA SCSI: Separate bus mode (wide-addressing enabled)\n");
- subsystem_maxid(shpnt) = 16;
- } else {
- printk(KERN_INFO "IBM MCA SCSI: Combined bus mode (wide-addressing disabled)\n");
- subsystem_maxid(shpnt) = 8;
- }
- printk(KERN_INFO "IBM MCA SCSI: Sync.-Rate (F/W: 20, Int.: 10, Ext.: %s) MBytes/s\n", ibmrate(speedrun, adaptertype));
- } else /* all other IBM SCSI adapters: */
- printk(KERN_INFO "IBM MCA SCSI: Synchronous-SCSI-Transfer-Rate: %s MBytes/s\n", ibmrate(speedrun, adaptertype));
-
- /* assign correct PUN device space */
- max_pun = subsystem_maxid(shpnt);
-
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: Current SCSI-host index: %d\n", shpnt);
- printk("IBM MCA SCSI: Removing default logical SCSI-device mapping.");
-#else
- printk(KERN_INFO "IBM MCA SCSI: Dev. Order: %s, Mapping (takes <2min): ", (ibm_ansi_order) ? "ANSI" : "New");
-#endif
- for (ldn = 0; ldn < MAX_LOG_DEV; ldn++) {
- probe_display(1);
-#ifdef IM_DEBUG_PROBE
- printk(".");
-#endif
- immediate_assign(shpnt, 0, 0, ldn, REMOVE_LDN); /* remove ldn (wherever) */
- }
- lun = 0; /* default lun is 0 */
-#ifndef IM_DEBUG_PROBE
- printk("cleared,");
-#endif
- /* STEP 2: */
-#ifdef IM_DEBUG_PROBE
- printk("\nIBM MCA SCSI: Scanning SCSI-devices.");
-#endif
- for (id = 0; id < max_pun; id++)
-#ifdef CONFIG_SCSI_MULTI_LUN
- for (lun = 0; lun < 8; lun++)
-#endif
- {
- probe_display(1);
-#ifdef IM_DEBUG_PROBE
- printk(".");
-#endif
- if (id != subsystem_pun(shpnt)) {
- /* if pun is not the adapter: */
- /* set ldn=0 to pun,lun */
- immediate_assign(shpnt, id, lun, PROBE_LDN, SET_LDN);
- if (device_inquiry(shpnt, PROBE_LDN)) { /* probe device */
- get_scsi(shpnt)[id][lun] = (unsigned char) (ld(shpnt)[PROBE_LDN].buf[0]);
- /* entry, even for NO_LUN */
- if (ld(shpnt)[PROBE_LDN].buf[0] != TYPE_NO_LUN)
- count_devices++; /* a existing device is found */
- }
- /* remove ldn */
- immediate_assign(shpnt, id, lun, PROBE_LDN, REMOVE_LDN);
- }
- }
-#ifndef IM_DEBUG_PROBE
- printk("scanned,");
-#endif
- /* STEP 3: */
-#ifdef IM_DEBUG_PROBE
- printk("\nIBM MCA SCSI: Mapping SCSI-devices.");
-#endif
- ldn = 0;
- lun = 0;
-#ifdef CONFIG_SCSI_MULTI_LUN
- for (lun = 0; lun < 8 && ldn < MAX_LOG_DEV; lun++)
-#endif
- for (id = 0; id < max_pun && ldn < MAX_LOG_DEV; id++) {
- probe_display(1);
-#ifdef IM_DEBUG_PROBE
- printk(".");
-#endif
- if (id != subsystem_pun(shpnt)) {
- if (get_scsi(shpnt)[id][lun] != TYPE_NO_LUN && get_scsi(shpnt)[id][lun] != TYPE_NO_DEVICE) {
- /* Only map if accepted type. Always enter for
- lun == 0 to get no gaps into ldn-mapping for ldn<7. */
- immediate_assign(shpnt, id, lun, ldn, SET_LDN);
- get_ldn(shpnt)[id][lun] = ldn; /* map ldn */
- if (device_exists(shpnt, ldn, &ld(shpnt)[ldn].block_length, &ld(shpnt)[ldn].device_type)) {
-#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
- printk("resetting device at ldn=%x ... ", ldn);
- immediate_reset(shpnt, ldn);
-#endif
- ldn++;
- } else {
- /* device vanished, probably because we don't know how to
- * handle it or because it has problems */
- if (lun > 0) {
- /* remove mapping */
- get_ldn(shpnt)[id][lun] = TYPE_NO_DEVICE;
- immediate_assign(shpnt, 0, 0, ldn, REMOVE_LDN);
- } else
- ldn++;
- }
- } else if (lun == 0) {
- /* map lun == 0, even if no device exists */
- immediate_assign(shpnt, id, lun, ldn, SET_LDN);
- get_ldn(shpnt)[id][lun] = ldn; /* map ldn */
- ldn++;
- }
- }
- }
- /* STEP 4: */
-
- /* map remaining ldns to non-existing devices */
- for (lun = 1; lun < 8 && ldn < MAX_LOG_DEV; lun++)
- for (id = 0; id < max_pun && ldn < MAX_LOG_DEV; id++) {
- if (get_scsi(shpnt)[id][lun] == TYPE_NO_LUN || get_scsi(shpnt)[id][lun] == TYPE_NO_DEVICE) {
- probe_display(1);
- /* Map remaining ldns only to NON-existing pun,lun
- combinations to make sure an inquiry will fail.
- For MULTI_LUN, it is needed to avoid adapter autonome
- SCSI-remapping. */
- immediate_assign(shpnt, id, lun, ldn, SET_LDN);
- get_ldn(shpnt)[id][lun] = ldn;
- ldn++;
- }
- }
-#ifndef IM_DEBUG_PROBE
- printk("mapped.");
-#endif
- printk("\n");
-#ifdef IM_DEBUG_PROBE
- if (ibm_ansi_order)
- printk("IBM MCA SCSI: Device order: IBM/ANSI (pun=7 is first).\n");
- else
- printk("IBM MCA SCSI: Device order: New Industry Standard (pun=0 is first).\n");
-#endif
-
-#ifdef IM_DEBUG_PROBE
- /* Show the physical and logical mapping during boot. */
- printk("IBM MCA SCSI: Determined SCSI-device-mapping:\n");
- printk(" Physical SCSI-Device Map Logical SCSI-Device Map\n");
- printk("ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n");
- for (id = 0; id < max_pun; id++) {
- printk("%2d ", id);
- for (lun = 0; lun < 8; lun++)
- printk("%2s ", ti_p(get_scsi(shpnt)[id][lun]));
- printk(" %2d ", id);
- for (lun = 0; lun < 8; lun++)
- printk("%2s ", ti_l(get_ldn(shpnt)[id][lun]));
- printk("\n");
- }
-#endif
-
- /* assign total number of found SCSI-devices to the statistics struct */
- IBM_DS(shpnt).total_scsi_devices = count_devices;
-
- /* decide for output in /proc-filesystem, if the configuration of
- SCSI-devices makes dynamical reassignment of devices necessary */
- if (count_devices >= MAX_LOG_DEV)
- IBM_DS(shpnt).dyn_flag = 1; /* dynamical assignment is necessary */
- else
- IBM_DS(shpnt).dyn_flag = 0; /* dynamical assignment is not necessary */
-
- /* If no SCSI-devices are assigned, return 1 in order to cause message. */
- if (ldn == 0)
- printk("IBM MCA SCSI: Warning: No SCSI-devices found/assigned!\n");
-
- /* reset the counters for statistics on the current adapter */
- IBM_DS(shpnt).scbs = 0;
- IBM_DS(shpnt).long_scbs = 0;
- IBM_DS(shpnt).total_accesses = 0;
- IBM_DS(shpnt).total_interrupts = 0;
- IBM_DS(shpnt).dynamical_assignments = 0;
- memset(IBM_DS(shpnt).ldn_access, 0x0, sizeof(IBM_DS(shpnt).ldn_access));
- memset(IBM_DS(shpnt).ldn_read_access, 0x0, sizeof(IBM_DS(shpnt).ldn_read_access));
- memset(IBM_DS(shpnt).ldn_write_access, 0x0, sizeof(IBM_DS(shpnt).ldn_write_access));
- memset(IBM_DS(shpnt).ldn_inquiry_access, 0x0, sizeof(IBM_DS(shpnt).ldn_inquiry_access));
- memset(IBM_DS(shpnt).ldn_modeselect_access, 0x0, sizeof(IBM_DS(shpnt).ldn_modeselect_access));
- memset(IBM_DS(shpnt).ldn_assignments, 0x0, sizeof(IBM_DS(shpnt).ldn_assignments));
- probe_display(0);
- return;
-}
-
-static int device_exists(struct Scsi_Host *shpnt, int ldn, int *block_length, int *device_type)
-{
- unsigned char *buf;
- /* if no valid device found, return immediately with 0 */
- if (!(device_inquiry(shpnt, ldn)))
- return 0;
- buf = (unsigned char *) (&(ld(shpnt)[ldn].buf));
- if (*buf == TYPE_ROM) {
- *device_type = TYPE_ROM;
- *block_length = 2048; /* (standard blocksize for yellow-/red-book) */
- return 1;
- }
- if (*buf == TYPE_WORM) {
- *device_type = TYPE_WORM;
- *block_length = 2048;
- return 1;
- }
- if (*buf == TYPE_DISK) {
- *device_type = TYPE_DISK;
- if (read_capacity(shpnt, ldn)) {
- *block_length = *(buf + 7) + (*(buf + 6) << 8) + (*(buf + 5) << 16) + (*(buf + 4) << 24);
- return 1;
- } else
- return 0;
- }
- if (*buf == TYPE_MOD) {
- *device_type = TYPE_MOD;
- if (read_capacity(shpnt, ldn)) {
- *block_length = *(buf + 7) + (*(buf + 6) << 8) + (*(buf + 5) << 16) + (*(buf + 4) << 24);
- return 1;
- } else
- return 0;
- }
- if (*buf == TYPE_TAPE) {
- *device_type = TYPE_TAPE;
- *block_length = 0; /* not in use (setting by mt and mtst in op.) */
- return 1;
- }
- if (*buf == TYPE_PROCESSOR) {
- *device_type = TYPE_PROCESSOR;
- *block_length = 0; /* they set their stuff on drivers */
- return 1;
- }
- if (*buf == TYPE_SCANNER) {
- *device_type = TYPE_SCANNER;
- *block_length = 0; /* they set their stuff on drivers */
- return 1;
- }
- if (*buf == TYPE_MEDIUM_CHANGER) {
- *device_type = TYPE_MEDIUM_CHANGER;
- *block_length = 0; /* One never knows, what to expect on a medium
- changer device. */
- return 1;
- }
- return 0;
-}
-
-static void internal_ibmmca_scsi_setup(char *str, int *ints)
-{
- int i, j, io_base, id_base;
- char *token;
-
- io_base = 0;
- id_base = 0;
- if (str) {
- j = 0;
- while ((token = strsep(&str, ",")) != NULL) {
- if (!strcmp(token, "activity"))
- display_mode |= LED_ACTIVITY;
- if (!strcmp(token, "display"))
- display_mode |= LED_DISP;
- if (!strcmp(token, "adisplay"))
- display_mode |= LED_ADISP;
- if (!strcmp(token, "normal"))
- ibm_ansi_order = 0;
- if (!strcmp(token, "ansi"))
- ibm_ansi_order = 1;
- if (!strcmp(token, "fast"))
- global_adapter_speed = 0;
- if (!strcmp(token, "medium"))
- global_adapter_speed = 4;
- if (!strcmp(token, "slow"))
- global_adapter_speed = 7;
- if ((*token == '-') || (isdigit(*token))) {
- if (!(j % 2) && (io_base < IM_MAX_HOSTS))
- io_port[io_base++] = simple_strtoul(token, NULL, 0);
- if ((j % 2) && (id_base < IM_MAX_HOSTS))
- scsi_id[id_base++] = simple_strtoul(token, NULL, 0);
- j++;
- }
- }
- } else if (ints) {
- for (i = 0; i < IM_MAX_HOSTS && 2 * i + 2 < ints[0]; i++) {
- io_port[i] = ints[2 * i + 2];
- scsi_id[i] = ints[2 * i + 2];
- }
- }
- return;
-}
-
-#if 0
- FIXME NEED TO MOVE TO SYSFS
-
-static int ibmmca_getinfo(char *buf, int slot, void *dev_id)
-{
- struct Scsi_Host *shpnt;
- int len, speciale, connectore, k;
- unsigned int pos[8];
- unsigned long flags;
- struct Scsi_Host *dev = dev_id;
-
- spin_lock_irqsave(dev->host_lock, flags);
-
- shpnt = dev; /* assign host-structure to local pointer */
- len = 0; /* set filled text-buffer index to 0 */
- /* get the _special contents of the hostdata structure */
- speciale = ((struct ibmmca_hostdata *) shpnt->hostdata)->_special;
- connectore = ((struct ibmmca_hostdata *) shpnt->hostdata)->_connector_size;
- for (k = 2; k < 4; k++)
- pos[k] = ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k];
- if (speciale == FORCED_DETECTION) { /* forced detection */
- len += sprintf(buf + len,
- "Adapter category: forced detected\n" "***************************************\n" "*** Forced detected SCSI Adapter ***\n" "*** No chip-information available ***\n" "***************************************\n");
- } else if (speciale == INTEGRATED_SCSI) {
- /* if the integrated subsystem has been found automatically: */
- len += sprintf(buf + len,
- "Adapter category: integrated\n" "Chip revision level: %d\n" "Chip status: %s\n" "8 kByte NVRAM status: %s\n", ((pos[2] & 0xf0) >> 4), (pos[2] & 1) ? "enabled" : "disabled", (pos[2] & 2) ? "locked" : "accessible");
- } else if ((speciale >= 0) && (speciale < ARRAY_SIZE(subsys_list))) {
- /* if the subsystem is a slot adapter */
- len += sprintf(buf + len, "Adapter category: slot-card\n" "ROM Segment Address: ");
- if ((pos[2] & 0xf0) == 0xf0)
- len += sprintf(buf + len, "off\n");
- else
- len += sprintf(buf + len, "0x%x\n", ((pos[2] & 0xf0) << 13) + 0xc0000);
- len += sprintf(buf + len, "Chip status: %s\n", (pos[2] & 1) ? "enabled" : "disabled");
- len += sprintf(buf + len, "Adapter I/O Offset: 0x%x\n", ((pos[2] & 0x0e) << 2));
- } else {
- len += sprintf(buf + len, "Adapter category: unknown\n");
- }
- /* common subsystem information to write to the slotn file */
- len += sprintf(buf + len, "Subsystem PUN: %d\n", shpnt->this_id);
- len += sprintf(buf + len, "I/O base address range: 0x%x-0x%x\n", (unsigned int) (shpnt->io_port), (unsigned int) (shpnt->io_port + 7));
- len += sprintf(buf + len, "MCA-slot size: %d bits", connectore);
- /* Now make sure, the bufferlength is devidable by 4 to avoid
- * paging problems of the buffer. */
- while (len % sizeof(int) != (sizeof(int) - 1))
- len += sprintf(buf + len, " ");
- len += sprintf(buf + len, "\n");
-
- spin_unlock_irqrestore(shpnt->host_lock, flags);
-
- return len;
-}
-#endif
-
-static struct scsi_host_template ibmmca_driver_template = {
- .proc_name = "ibmmca",
- .proc_info = ibmmca_proc_info,
- .name = "IBM SCSI-Subsystem",
- .queuecommand = ibmmca_queuecommand,
- .eh_abort_handler = ibmmca_abort,
- .eh_host_reset_handler = ibmmca_host_reset,
- .bios_param = ibmmca_biosparam,
- .can_queue = 16,
- .this_id = 7,
- .sg_tablesize = 16,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-static int ibmmca_probe(struct device *dev)
-{
- struct Scsi_Host *shpnt;
- int port, id, i, j, k, irq, enabled, ret = -EINVAL;
- struct mca_device *mca_dev = to_mca_device(dev);
- const char *description = ibmmca_description[mca_dev->index];
-
- /* First of all, print the version number of the driver. This is
- * important to allow better user bugreports in case of already
- * having problems with the MCA_bus probing. */
- printk(KERN_INFO "IBM MCA SCSI: Version %s\n", IBMMCA_SCSI_DRIVER_VERSION);
- /* The POS2-register of all PS/2 model SCSI-subsystems has the following
- * interpretation of bits:
- * Bit 7 - 4 : Chip Revision ID (Release)
- * Bit 3 - 2 : Reserved
- * Bit 1 : 8k NVRAM Disabled
- * Bit 0 : Chip Enable (EN-Signal)
- * The POS3-register is interpreted as follows:
- * Bit 7 - 5 : SCSI ID
- * Bit 4 : Reserved = 0
- * Bit 3 - 0 : Reserved = 0
- * (taken from "IBM, PS/2 Hardware Interface Technical Reference, Common
- * Interfaces (1991)").
- * In short words, this means, that IBM PS/2 machines only support
- * 1 single subsystem by default. The slot-adapters must have another
- * configuration on pos2. Here, one has to assume the following
- * things for POS2-register:
- * Bit 7 - 4 : Chip Revision ID (Release)
- * Bit 3 - 1 : port offset factor
- * Bit 0 : Chip Enable (EN-Signal)
- * As I found a patch here, setting the IO-registers to 0x3540 forced,
- * as there was a 0x05 in POS2 on a model 56, I assume, that the
- * port 0x3540 must be fix for integrated SCSI-controllers.
- * Ok, this discovery leads to the following implementation: (M.Lang) */
-
- /* first look for the IBM SCSI integrated subsystem on the motherboard */
- for (j = 0; j < 8; j++) /* read the pos-information */
- pos[j] = mca_device_read_pos(mca_dev, j);
- id = (pos[3] & 0xe0) >> 5; /* this is correct and represents the PUN */
- enabled = (pos[2] &0x01);
- if (!enabled) {
- printk(KERN_WARNING "IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n");
- printk(KERN_WARNING " SCSI-operations may not work.\n");
- }
-
- /* pos2 = pos3 = 0xff if there is no integrated SCSI-subsystem present, but
- * if we ignore the settings of all surrounding pos registers, it is not
- * completely sufficient to only check pos2 and pos3. */
- /* Therefore, now the following if statement is used to
- * make sure, we see a real integrated onboard SCSI-interface and no
- * internal system information, which gets mapped to some pos registers
- * on models 95xx. */
- if (mca_dev->slot == MCA_INTEGSCSI &&
- ((!pos[0] && !pos[1] && pos[2] > 0 &&
- pos[3] > 0 && !pos[4] && !pos[5] &&
- !pos[6] && !pos[7]) ||
- (pos[0] == 0xff && pos[1] == 0xff &&
- pos[2] < 0xff && pos[3] < 0xff &&
- pos[4] == 0xff && pos[5] == 0xff &&
- pos[6] == 0xff && pos[7] == 0xff))) {
- irq = IM_IRQ;
- port = IM_IO_PORT;
- } else {
- irq = IM_IRQ;
- port = IM_IO_PORT + ((pos[2] &0x0e) << 2);
- if ((mca_dev->index == IBM_SCSI2_FW) && (pos[6] != 0)) {
- printk(KERN_ERR "IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n");
- printk(KERN_ERR " Impossible to determine adapter PUN!\n");
- printk(KERN_ERR " Guessing adapter PUN = 7.\n");
- id = 7;
- } else {
- id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */
- if (mca_dev->index == IBM_SCSI2_FW) {
- id |= (pos[3] & 0x10) >> 1; /* get subsystem PUN high-bit
- * for F/W adapters */
- }
- }
- if ((mca_dev->index == IBM_SCSI2_FW) &&
- (pos[4] & 0x01) && (pos[6] == 0)) {
- /* IRQ11 is used by SCSI-2 F/W Adapter/A */
- printk(KERN_DEBUG "IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n");
- irq = IM_IRQ_FW;
- }
- }
-
-
-
- /* give detailed information on the subsystem. This helps me
- * additionally during debugging and analyzing bug-reports. */
- printk(KERN_INFO "IBM MCA SCSI: %s found, io=0x%x, scsi id=%d,\n",
- description, port, id);
- if (mca_dev->slot == MCA_INTEGSCSI)
- printk(KERN_INFO " chip rev.=%d, 8K NVRAM=%s, subsystem=%s\n", ((pos[2] & 0xf0) >> 4), (pos[2] & 2) ? "locked" : "accessible", (pos[2] & 1) ? "enabled." : "disabled.");
- else {
- if ((pos[2] & 0xf0) == 0xf0)
- printk(KERN_DEBUG " ROM Addr.=off,");
- else
- printk(KERN_DEBUG " ROM Addr.=0x%x,", ((pos[2] & 0xf0) << 13) + 0xc0000);
-
- printk(KERN_DEBUG " port-offset=0x%x, subsystem=%s\n", ((pos[2] & 0x0e) << 2), (pos[2] & 1) ? "enabled." : "disabled.");
- }
-
- /* check I/O region */
- if (!request_region(port, IM_N_IO_PORT, description)) {
- printk(KERN_ERR "IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x (%d ports).\n", port, port + IM_N_IO_PORT - 1, IM_N_IO_PORT);
- goto out_fail;
- }
-
- /* register host */
- shpnt = scsi_host_alloc(&ibmmca_driver_template,
- sizeof(struct ibmmca_hostdata));
- if (!shpnt) {
- printk(KERN_ERR "IBM MCA SCSI: Unable to register host.\n");
- goto out_release;
- }
-
- dev_set_drvdata(dev, shpnt);
- if(request_irq(irq, interrupt_handler, IRQF_SHARED, description, dev)) {
- printk(KERN_ERR "IBM MCA SCSI: failed to request interrupt %d\n", irq);
- goto out_free_host;
- }
-
- /* request I/O region */
- special(shpnt) = mca_dev->index; /* important assignment or else crash! */
- subsystem_connector_size(shpnt) = 0; /* preset slot-size */
- shpnt->irq = irq; /* assign necessary stuff for the adapter */
- shpnt->io_port = port;
- shpnt->n_io_port = IM_N_IO_PORT;
- shpnt->this_id = id;
- shpnt->max_id = 8; /* 8 PUNs are default */
- /* now, the SCSI-subsystem is connected to Linux */
-
-#ifdef IM_DEBUG_PROBE
- ctrl = (unsigned int) (inb(IM_CTR_REG(found))); /* get control-register status */
- printk("IBM MCA SCSI: Control Register contents: %x, status: %x\n", ctrl, inb(IM_STAT_REG(found)));
- printk("IBM MCA SCSI: This adapters' POS-registers: ");
- for (i = 0; i < 8; i++)
- printk("%x ", pos[i]);
- printk("\n");
-#endif
- reset_status(shpnt) = IM_RESET_NOT_IN_PROGRESS;
-
- for (i = 0; i < 16; i++) /* reset the tables */
- for (j = 0; j < 8; j++)
- get_ldn(shpnt)[i][j] = MAX_LOG_DEV;
-
- /* check which logical devices exist */
- /* after this line, local interrupting is possible: */
- local_checking_phase_flag(shpnt) = 1;
- check_devices(shpnt, mca_dev->index); /* call by value, using the global variable hosts */
- local_checking_phase_flag(shpnt) = 0;
-
- /* an ibm mca subsystem has been detected */
-
- for (k = 2; k < 7; k++)
- ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k] = pos[k];
- ((struct ibmmca_hostdata *) shpnt->hostdata)->_special = INTEGRATED_SCSI;
- mca_device_set_name(mca_dev, description);
- /* FIXME: NEED TO REPLUMB TO SYSFS
- mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, shpnt);
- */
- mca_device_set_claim(mca_dev, 1);
- if (scsi_add_host(shpnt, dev)) {
- dev_printk(KERN_ERR, dev, "IBM MCA SCSI: scsi_add_host failed\n");
- goto out_free_host;
- }
- scsi_scan_host(shpnt);
-
- return 0;
- out_free_host:
- scsi_host_put(shpnt);
- out_release:
- release_region(port, IM_N_IO_PORT);
- out_fail:
- return ret;
-}
-
-static int __devexit ibmmca_remove(struct device *dev)
-{
- struct Scsi_Host *shpnt = dev_get_drvdata(dev);
- scsi_remove_host(shpnt);
- release_region(shpnt->io_port, shpnt->n_io_port);
- free_irq(shpnt->irq, dev);
- scsi_host_put(shpnt);
- return 0;
-}
-
-/* The following routine is the SCSI command queue for the midlevel driver */
-static int ibmmca_queuecommand_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-{
- unsigned int ldn;
- unsigned int scsi_cmd;
- struct im_scb *scb;
- struct Scsi_Host *shpnt;
- int current_ldn;
- int id, lun;
- int target;
- int max_pun;
- int i;
- struct scatterlist *sg;
-
- shpnt = cmd->device->host;
-
- max_pun = subsystem_maxid(shpnt);
- if (ibm_ansi_order) {
- target = max_pun - 1 - cmd->device->id;
- if ((target <= subsystem_pun(shpnt)) && (cmd->device->id <= subsystem_pun(shpnt)))
- target--;
- else if ((target >= subsystem_pun(shpnt)) && (cmd->device->id >= subsystem_pun(shpnt)))
- target++;
- } else
- target = cmd->device->id;
-
- /* if (target,lun) is NO LUN or not existing at all, return error */
- if ((get_scsi(shpnt)[target][cmd->device->lun] == TYPE_NO_LUN) || (get_scsi(shpnt)[target][cmd->device->lun] == TYPE_NO_DEVICE)) {
- cmd->result = DID_NO_CONNECT << 16;
- if (done)
- done(cmd);
- return 0;
- }
-
- /*if (target,lun) unassigned, do further checks... */
- ldn = get_ldn(shpnt)[target][cmd->device->lun];
- if (ldn >= MAX_LOG_DEV) { /* on invalid ldn do special stuff */
- if (ldn > MAX_LOG_DEV) { /* dynamical remapping if ldn unassigned */
- current_ldn = next_ldn(shpnt); /* stop-value for one circle */
- while (ld(shpnt)[next_ldn(shpnt)].cmd) { /* search for a occupied, but not in */
- /* command-processing ldn. */
- next_ldn(shpnt)++;
- if (next_ldn(shpnt) >= MAX_LOG_DEV)
- next_ldn(shpnt) = 7;
- if (current_ldn == next_ldn(shpnt)) { /* One circle done ? */
- /* no non-processing ldn found */
- scmd_printk(KERN_WARNING, cmd,
- "IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n"
- " On ldn 7-14 SCSI-commands everywhere in progress.\n"
- " Reporting DID_NO_CONNECT for device.\n");
- cmd->result = DID_NO_CONNECT << 16; /* return no connect */
- if (done)
- done(cmd);
- return 0;
- }
- }
-
- /* unmap non-processing ldn */
- for (id = 0; id < max_pun; id++)
- for (lun = 0; lun < 8; lun++) {
- if (get_ldn(shpnt)[id][lun] == next_ldn(shpnt)) {
- get_ldn(shpnt)[id][lun] = TYPE_NO_DEVICE;
- get_scsi(shpnt)[id][lun] = TYPE_NO_DEVICE;
- /* unmap entry */
- }
- }
- /* set reduced interrupt_handler-mode for checking */
- local_checking_phase_flag(shpnt) = 1;
- /* map found ldn to pun,lun */
- get_ldn(shpnt)[target][cmd->device->lun] = next_ldn(shpnt);
- /* change ldn to the right value, that is now next_ldn */
- ldn = next_ldn(shpnt);
- /* unassign all ldns (pun,lun,ldn does not matter for remove) */
- immediate_assign(shpnt, 0, 0, 0, REMOVE_LDN);
- /* set only LDN for remapped device */
- immediate_assign(shpnt, target, cmd->device->lun, ldn, SET_LDN);
- /* get device information for ld[ldn] */
- if (device_exists(shpnt, ldn, &ld(shpnt)[ldn].block_length, &ld(shpnt)[ldn].device_type)) {
- ld(shpnt)[ldn].cmd = NULL; /* To prevent panic set 0, because
- devices that were not assigned,
- should have nothing in progress. */
- get_scsi(shpnt)[target][cmd->device->lun] = ld(shpnt)[ldn].device_type;
- /* increase assignment counters for statistics in /proc */
- IBM_DS(shpnt).dynamical_assignments++;
- IBM_DS(shpnt).ldn_assignments[ldn]++;
- } else
- /* panic here, because a device, found at boottime has
- vanished */
- panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n", ldn, target, cmd->device->lun);
- /* unassign again all ldns (pun,lun,ldn does not matter for remove) */
- immediate_assign(shpnt, 0, 0, 0, REMOVE_LDN);
- /* remap all ldns, as written in the pun/lun table */
- lun = 0;
-#ifdef CONFIG_SCSI_MULTI_LUN
- for (lun = 0; lun < 8; lun++)
-#endif
- for (id = 0; id < max_pun; id++) {
- if (get_ldn(shpnt)[id][lun] <= MAX_LOG_DEV)
- immediate_assign(shpnt, id, lun, get_ldn(shpnt)[id][lun], SET_LDN);
- }
- /* set back to normal interrupt_handling */
- local_checking_phase_flag(shpnt) = 0;
-#ifdef IM_DEBUG_PROBE
- /* Information on syslog terminal */
- printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n", ldn, target, cmd->device->lun);
-#endif
- /* increase next_ldn for next dynamical assignment */
- next_ldn(shpnt)++;
- if (next_ldn(shpnt) >= MAX_LOG_DEV)
- next_ldn(shpnt) = 7;
- } else { /* wall against Linux accesses to the subsystem adapter */
- cmd->result = DID_BAD_TARGET << 16;
- if (done)
- done(cmd);
- return 0;
- }
- }
-
- /*verify there is no command already in progress for this log dev */
- if (ld(shpnt)[ldn].cmd)
- panic("IBM MCA SCSI: cmd already in progress for this ldn.\n");
-
- /*save done in cmd, and save cmd for the interrupt handler */
- cmd->scsi_done = done;
- ld(shpnt)[ldn].cmd = cmd;
-
- /*fill scb information independent of the scsi command */
- scb = &(ld(shpnt)[ldn].scb);
- ld(shpnt)[ldn].tsb.dev_status = 0;
- scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE;
- scb->tsb_adr = isa_virt_to_bus(&(ld(shpnt)[ldn].tsb));
- scsi_cmd = cmd->cmnd[0];
-
- if (scsi_sg_count(cmd)) {
- BUG_ON(scsi_sg_count(cmd) > 16);
-
- scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
- ld(shpnt)[ldn].sge[i].address = (void *) (isa_page_to_bus(sg_page(sg)) + sg->offset);
- ld(shpnt)[ldn].sge[i].byte_length = sg->length;
- }
- scb->enable |= IM_POINTER_TO_LIST;
- scb->sys_buf_adr = isa_virt_to_bus(&(ld(shpnt)[ldn].sge[0]));
- scb->sys_buf_length = scsi_sg_count(cmd) * sizeof(struct im_sge);
- } else {
- scb->sys_buf_adr = isa_virt_to_bus(scsi_sglist(cmd));
- /* recent Linux midlevel SCSI places 1024 byte for inquiry
- * command. Far too much for old PS/2 hardware. */
- switch (scsi_cmd) {
- /* avoid command errors by setting bufferlengths to
- * ANSI-standard. Beware of forcing it to 255,
- * this could SEGV the kernel!!! */
- case INQUIRY:
- case REQUEST_SENSE:
- case MODE_SENSE:
- case MODE_SELECT:
- if (scsi_bufflen(cmd) > 255)
- scb->sys_buf_length = 255;
- else
- scb->sys_buf_length = scsi_bufflen(cmd);
- break;
- case TEST_UNIT_READY:
- scb->sys_buf_length = 0;
- break;
- default:
- scb->sys_buf_length = scsi_bufflen(cmd);
- break;
- }
- }
- /*fill scb information dependent on scsi command */
-
-#ifdef IM_DEBUG_CMD
- printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn);
-#endif
-
- /* for specific device-type debugging: */
-#ifdef IM_DEBUG_CMD_SPEC_DEV
- if (ld(shpnt)[ldn].device_type == IM_DEBUG_CMD_DEVICE)
- printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", ld(shpnt)[ldn].device_type, scsi_cmd, ldn);
-#endif
-
- /* for possible panics store current command */
- last_scsi_command(shpnt)[ldn] = scsi_cmd;
- last_scsi_type(shpnt)[ldn] = IM_SCB;
- /* update statistical info */
- IBM_DS(shpnt).total_accesses++;
- IBM_DS(shpnt).ldn_access[ldn]++;
-
- switch (scsi_cmd) {
- case READ_6:
- case WRITE_6:
- case READ_10:
- case WRITE_10:
- case READ_12:
- case WRITE_12:
- /* Distinguish between disk and other devices. Only disks (that are the
- most frequently accessed devices) should be supported by the
- IBM-SCSI-Subsystem commands. */
- switch (ld(shpnt)[ldn].device_type) {
- case TYPE_DISK: /* for harddisks enter here ... */
- case TYPE_MOD: /* ... try it also for MO-drives (send flames as */
- /* you like, if this won't work.) */
- if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12) {
- /* read command preparations */
- scb->enable |= IM_READ_CONTROL;
- IBM_DS(shpnt).ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */
- scb->command = IM_READ_DATA_CMD | IM_NO_DISCONNECT;
- } else { /* write command preparations */
- IBM_DS(shpnt).ldn_write_access[ldn]++; /* increase write-count on ldn stat. */
- scb->command = IM_WRITE_DATA_CMD | IM_NO_DISCONNECT;
- }
- if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) {
- scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | (((unsigned) cmd->cmnd[2]) << 8) | ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
- scb->u2.blk.count = (unsigned) cmd->cmnd[4];
- } else {
- scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | (((unsigned) cmd->cmnd[4]) << 8) | (((unsigned) cmd->cmnd[3]) << 16) | (((unsigned) cmd->cmnd[2]) << 24);
- scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | (((unsigned) cmd->cmnd[7]) << 8);
- }
- last_scsi_logical_block(shpnt)[ldn] = scb->u1.log_blk_adr;
- last_scsi_blockcount(shpnt)[ldn] = scb->u2.blk.count;
- scb->u2.blk.length = ld(shpnt)[ldn].block_length;
- break;
- /* for other devices, enter here. Other types are not known by
- Linux! TYPE_NO_LUN is forbidden as valid device. */
- case TYPE_ROM:
- case TYPE_TAPE:
- case TYPE_PROCESSOR:
- case TYPE_WORM:
- case TYPE_SCANNER:
- case TYPE_MEDIUM_CHANGER:
- /* If there is a sequential-device, IBM recommends to use
- IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE.
- This includes CD-ROM devices, too, due to the partial sequential
- read capabilities. */
- scb->command = IM_OTHER_SCSI_CMD_CMD;
- if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12)
- /* enable READ */
- scb->enable |= IM_READ_CONTROL;
- scb->enable |= IM_BYPASS_BUFFER;
- scb->u1.scsi_cmd_length = cmd->cmd_len;
- memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
- last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
- /* Read/write on this non-disk devices is also displayworthy,
- so flash-up the LED/display. */
- break;
- }
- break;
- case INQUIRY:
- IBM_DS(shpnt).ldn_inquiry_access[ldn]++;
- scb->command = IM_DEVICE_INQUIRY_CMD;
- scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
- scb->u1.log_blk_adr = 0;
- break;
- case TEST_UNIT_READY:
- scb->command = IM_OTHER_SCSI_CMD_CMD;
- scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
- scb->u1.log_blk_adr = 0;
- scb->u1.scsi_cmd_length = 6;
- memcpy(scb->u2.scsi_command, cmd->cmnd, 6);
- last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
- break;
- case READ_CAPACITY:
- /* the length of system memory buffer must be exactly 8 bytes */
- scb->command = IM_READ_CAPACITY_CMD;
- scb->enable |= IM_READ_CONTROL | IM_BYPASS_BUFFER;
- if (scb->sys_buf_length > 8)
- scb->sys_buf_length = 8;
- break;
- /* Commands that need read-only-mode (system <- device): */
- case REQUEST_SENSE:
- scb->command = IM_REQUEST_SENSE_CMD;
- scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
- break;
- /* Commands that need write-only-mode (system -> device): */
- case MODE_SELECT:
- case MODE_SELECT_10:
- IBM_DS(shpnt).ldn_modeselect_access[ldn]++;
- scb->command = IM_OTHER_SCSI_CMD_CMD;
- scb->enable |= IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; /*Select needs WRITE-enabled */
- scb->u1.scsi_cmd_length = cmd->cmd_len;
- memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
- last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
- break;
- /* For other commands, read-only is useful. Most other commands are
- running without an input-data-block. */
- default:
- scb->command = IM_OTHER_SCSI_CMD_CMD;
- scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
- scb->u1.scsi_cmd_length = cmd->cmd_len;
- memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
- last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
- break;
- }
- /*issue scb command, and return */
- if (++disk_rw_in_progress == 1)
- PS2_DISK_LED_ON(shpnt->host_no, target);
-
- if (last_scsi_type(shpnt)[ldn] == IM_LONG_SCB) {
- issue_cmd(shpnt, isa_virt_to_bus(scb), IM_LONG_SCB | ldn);
- IBM_DS(shpnt).long_scbs++;
- } else {
- issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | ldn);
- IBM_DS(shpnt).scbs++;
- }
- return 0;
-}
-
-static DEF_SCSI_QCMD(ibmmca_queuecommand)
-
-static int __ibmmca_abort(Scsi_Cmnd * cmd)
-{
- /* Abort does not work, as the adapter never generates an interrupt on
- * whatever situation is simulated, even when really pending commands
- * are running on the adapters' hardware ! */
-
- struct Scsi_Host *shpnt;
- unsigned int ldn;
- void (*saved_done) (Scsi_Cmnd *);
- int target;
- int max_pun;
- unsigned long imm_command;
-
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: Abort subroutine called...\n");
-#endif
-
- shpnt = cmd->device->host;
-
- max_pun = subsystem_maxid(shpnt);
- if (ibm_ansi_order) {
- target = max_pun - 1 - cmd->device->id;
- if ((target <= subsystem_pun(shpnt)) && (cmd->device->id <= subsystem_pun(shpnt)))
- target--;
- else if ((target >= subsystem_pun(shpnt)) && (cmd->device->id >= subsystem_pun(shpnt)))
- target++;
- } else
- target = cmd->device->id;
-
- /* get logical device number, and disable system interrupts */
- printk(KERN_WARNING "IBM MCA SCSI: Sending abort to device pun=%d, lun=%d.\n", target, cmd->device->lun);
- ldn = get_ldn(shpnt)[target][cmd->device->lun];
-
- /*if cmd for this ldn has already finished, no need to abort */
- if (!ld(shpnt)[ldn].cmd) {
- return SUCCESS;
- }
-
- /* Clear ld.cmd, save done function, install internal done,
- * send abort immediate command (this enables sys. interrupts),
- * and wait until the interrupt arrives.
- */
- saved_done = cmd->scsi_done;
- cmd->scsi_done = internal_done;
- cmd->SCp.Status = 0;
- last_scsi_command(shpnt)[ldn] = IM_ABORT_IMM_CMD;
- last_scsi_type(shpnt)[ldn] = IM_IMM_CMD;
- imm_command = inl(IM_CMD_REG(shpnt));
- imm_command &= (unsigned long) (0xffff0000); /* mask reserved stuff */
- imm_command |= (unsigned long) (IM_ABORT_IMM_CMD);
- /* must wait for attention reg not busy */
- /* FIXME - timeout, politeness */
- while (1) {
- if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
- break;
- }
- /* write registers and enable system interrupts */
- outl(imm_command, IM_CMD_REG(shpnt));
- outb(IM_IMM_CMD | ldn, IM_ATTN_REG(shpnt));
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: Abort queued to adapter...\n");
-#endif
- spin_unlock_irq(shpnt->host_lock);
- while (!cmd->SCp.Status)
- yield();
- spin_lock_irq(shpnt->host_lock);
- cmd->scsi_done = saved_done;
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: Abort returned with adapter response...\n");
-#endif
-
- /*if abort went well, call saved done, then return success or error */
- if (cmd->result == (DID_ABORT << 16))
- {
- cmd->result |= DID_ABORT << 16;
- if (cmd->scsi_done)
- (cmd->scsi_done) (cmd);
- ld(shpnt)[ldn].cmd = NULL;
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: Abort finished with success.\n");
-#endif
- return SUCCESS;
- } else {
- cmd->result |= DID_NO_CONNECT << 16;
- if (cmd->scsi_done)
- (cmd->scsi_done) (cmd);
- ld(shpnt)[ldn].cmd = NULL;
-#ifdef IM_DEBUG_PROBE
- printk("IBM MCA SCSI: Abort failed.\n");
-#endif
- return FAILED;
- }
-}
-
-static int ibmmca_abort(Scsi_Cmnd * cmd)
-{
- struct Scsi_Host *shpnt = cmd->device->host;
- int rc;
-
- spin_lock_irq(shpnt->host_lock);
- rc = __ibmmca_abort(cmd);
- spin_unlock_irq(shpnt->host_lock);
-
- return rc;
-}
-
-static int __ibmmca_host_reset(Scsi_Cmnd * cmd)
-{
- struct Scsi_Host *shpnt;
- Scsi_Cmnd *cmd_aid;
- int ticks, i;
- unsigned long imm_command;
-
- BUG_ON(cmd == NULL);
-
- ticks = IM_RESET_DELAY * HZ;
- shpnt = cmd->device->host;
-
- if (local_checking_phase_flag(shpnt)) {
- printk(KERN_WARNING "IBM MCA SCSI: unable to reset while checking devices.\n");
- return FAILED;
- }
-
- /* issue reset immediate command to subsystem, and wait for interrupt */
- printk("IBM MCA SCSI: resetting all devices.\n");
- reset_status(shpnt) = IM_RESET_IN_PROGRESS;
- last_scsi_command(shpnt)[0xf] = IM_RESET_IMM_CMD;
- last_scsi_type(shpnt)[0xf] = IM_IMM_CMD;
- imm_command = inl(IM_CMD_REG(shpnt));
- imm_command &= (unsigned long) (0xffff0000); /* mask reserved stuff */
- imm_command |= (unsigned long) (IM_RESET_IMM_CMD);
- /* must wait for attention reg not busy */
- while (1) {
- if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
- break;
- spin_unlock_irq(shpnt->host_lock);
- yield();
- spin_lock_irq(shpnt->host_lock);
- }
- /*write registers and enable system interrupts */
- outl(imm_command, IM_CMD_REG(shpnt));
- outb(IM_IMM_CMD | 0xf, IM_ATTN_REG(shpnt));
- /* wait for interrupt finished or intr_stat register to be set, as the
- * interrupt will not be executed, while we are in here! */
-
- /* FIXME: This is really really icky we so want a sleeping version of this ! */
- while (reset_status(shpnt) == IM_RESET_IN_PROGRESS && --ticks && ((inb(IM_INTR_REG(shpnt)) & 0x8f) != 0x8f)) {
- udelay((1 + 999 / HZ) * 1000);
- barrier();
- }
- /* if reset did not complete, just return an error */
- if (!ticks) {
- printk(KERN_ERR "IBM MCA SCSI: reset did not complete within %d seconds.\n", IM_RESET_DELAY);
- reset_status(shpnt) = IM_RESET_FINISHED_FAIL;
- return FAILED;
- }
-
- if ((inb(IM_INTR_REG(shpnt)) & 0x8f) == 0x8f) {
- /* analysis done by this routine and not by the intr-routine */
- if (inb(IM_INTR_REG(shpnt)) == 0xaf)
- reset_status(shpnt) = IM_RESET_FINISHED_OK_NO_INT;
- else if (inb(IM_INTR_REG(shpnt)) == 0xcf)
- reset_status(shpnt) = IM_RESET_FINISHED_FAIL;
- else /* failed, 4get it */
- reset_status(shpnt) = IM_RESET_NOT_IN_PROGRESS_NO_INT;
- outb(IM_EOI | 0xf, IM_ATTN_REG(shpnt));
- }
-
- /* if reset failed, just return an error */
- if (reset_status(shpnt) == IM_RESET_FINISHED_FAIL) {
- printk(KERN_ERR "IBM MCA SCSI: reset failed.\n");
- return FAILED;
- }
-
- /* so reset finished ok - call outstanding done's, and return success */
- printk(KERN_INFO "IBM MCA SCSI: Reset successfully completed.\n");
- for (i = 0; i < MAX_LOG_DEV; i++) {
- cmd_aid = ld(shpnt)[i].cmd;
- if (cmd_aid && cmd_aid->scsi_done) {
- ld(shpnt)[i].cmd = NULL;
- cmd_aid->result = DID_RESET << 16;
- }
- }
- return SUCCESS;
-}
-
-static int ibmmca_host_reset(Scsi_Cmnd * cmd)
-{
- struct Scsi_Host *shpnt = cmd->device->host;
- int rc;
-
- spin_lock_irq(shpnt->host_lock);
- rc = __ibmmca_host_reset(cmd);
- spin_unlock_irq(shpnt->host_lock);
-
- return rc;
-}
-
-static int ibmmca_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *info)
-{
- int size = capacity;
- info[0] = 64;
- info[1] = 32;
- info[2] = size / (info[0] * info[1]);
- if (info[2] >= 1024) {
- info[0] = 128;
- info[1] = 63;
- info[2] = size / (info[0] * info[1]);
- if (info[2] >= 1024) {
- info[0] = 255;
- info[1] = 63;
- info[2] = size / (info[0] * info[1]);
- if (info[2] >= 1024)
- info[2] = 1023;
- }
- }
- return 0;
-}
-
-/* calculate percentage of total accesses on a ldn */
-static int ldn_access_load(struct Scsi_Host *shpnt, int ldn)
-{
- if (IBM_DS(shpnt).total_accesses == 0)
- return (0);
- if (IBM_DS(shpnt).ldn_access[ldn] == 0)
- return (0);
- return (IBM_DS(shpnt).ldn_access[ldn] * 100) / IBM_DS(shpnt).total_accesses;
-}
-
-/* calculate total amount of r/w-accesses */
-static int ldn_access_total_read_write(struct Scsi_Host *shpnt)
-{
- int a;
- int i;
-
- a = 0;
- for (i = 0; i <= MAX_LOG_DEV; i++)
- a += IBM_DS(shpnt).ldn_read_access[i] + IBM_DS(shpnt).ldn_write_access[i];
- return (a);
-}
-
-static int ldn_access_total_inquiry(struct Scsi_Host *shpnt)
-{
- int a;
- int i;
-
- a = 0;
- for (i = 0; i <= MAX_LOG_DEV; i++)
- a += IBM_DS(shpnt).ldn_inquiry_access[i];
- return (a);
-}
-
-static int ldn_access_total_modeselect(struct Scsi_Host *shpnt)
-{
- int a;
- int i;
-
- a = 0;
- for (i = 0; i <= MAX_LOG_DEV; i++)
- a += IBM_DS(shpnt).ldn_modeselect_access[i];
- return (a);
-}
-
-/* routine to display info in the proc-fs-structure (a deluxe feature) */
-static int ibmmca_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout)
-{
- int len = 0;
- int i, id, lun;
- unsigned long flags;
- int max_pun;
-
-
- spin_lock_irqsave(shpnt->host_lock, flags); /* Check it */
-
- max_pun = subsystem_maxid(shpnt);
-
- len += sprintf(buffer + len, "\n IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n", IBMMCA_SCSI_DRIVER_VERSION);
- len += sprintf(buffer + len, " SCSI Access-Statistics:\n");
- len += sprintf(buffer + len, " Device Scanning Order....: %s\n", (ibm_ansi_order) ? "IBM/ANSI" : "New Industry Standard");
-#ifdef CONFIG_SCSI_MULTI_LUN
- len += sprintf(buffer + len, " Multiple LUN probing.....: Yes\n");
-#else
- len += sprintf(buffer + len, " Multiple LUN probing.....: No\n");
-#endif
- len += sprintf(buffer + len, " This Hostnumber..........: %d\n", shpnt->host_no);
- len += sprintf(buffer + len, " Base I/O-Port............: 0x%x\n", (unsigned int) (IM_CMD_REG(shpnt)));
- len += sprintf(buffer + len, " (Shared) IRQ.............: %d\n", IM_IRQ);
- len += sprintf(buffer + len, " Total Interrupts.........: %d\n", IBM_DS(shpnt).total_interrupts);
- len += sprintf(buffer + len, " Total SCSI Accesses......: %d\n", IBM_DS(shpnt).total_accesses);
- len += sprintf(buffer + len, " Total short SCBs.........: %d\n", IBM_DS(shpnt).scbs);
- len += sprintf(buffer + len, " Total long SCBs..........: %d\n", IBM_DS(shpnt).long_scbs);
- len += sprintf(buffer + len, " Total SCSI READ/WRITE..: %d\n", ldn_access_total_read_write(shpnt));
- len += sprintf(buffer + len, " Total SCSI Inquiries...: %d\n", ldn_access_total_inquiry(shpnt));
- len += sprintf(buffer + len, " Total SCSI Modeselects.: %d\n", ldn_access_total_modeselect(shpnt));
- len += sprintf(buffer + len, " Total SCSI other cmds..: %d\n", IBM_DS(shpnt).total_accesses - ldn_access_total_read_write(shpnt)
- - ldn_access_total_modeselect(shpnt)
- - ldn_access_total_inquiry(shpnt));
- len += sprintf(buffer + len, " Total SCSI command fails.: %d\n\n", IBM_DS(shpnt).total_errors);
- len += sprintf(buffer + len, " Logical-Device-Number (LDN) Access-Statistics:\n");
- len += sprintf(buffer + len, " LDN | Accesses [%%] | READ | WRITE | ASSIGNMENTS\n");
- len += sprintf(buffer + len, " -----|--------------|-----------|-----------|--------------\n");
- for (i = 0; i <= MAX_LOG_DEV; i++)
- len += sprintf(buffer + len, " %2X | %3d | %8d | %8d | %8d\n", i, ldn_access_load(shpnt, i), IBM_DS(shpnt).ldn_read_access[i], IBM_DS(shpnt).ldn_write_access[i], IBM_DS(shpnt).ldn_assignments[i]);
- len += sprintf(buffer + len, " -----------------------------------------------------------\n\n");
- len += sprintf(buffer + len, " Dynamical-LDN-Assignment-Statistics:\n");
- len += sprintf(buffer + len, " Number of physical SCSI-devices..: %d (+ Adapter)\n", IBM_DS(shpnt).total_scsi_devices);
- len += sprintf(buffer + len, " Dynamical Assignment necessary...: %s\n", IBM_DS(shpnt).dyn_flag ? "Yes" : "No ");
- len += sprintf(buffer + len, " Next LDN to be assigned..........: 0x%x\n", next_ldn(shpnt));
- len += sprintf(buffer + len, " Dynamical assignments done yet...: %d\n", IBM_DS(shpnt).dynamical_assignments);
- len += sprintf(buffer + len, "\n Current SCSI-Device-Mapping:\n");
- len += sprintf(buffer + len, " Physical SCSI-Device Map Logical SCSI-Device Map\n");
- len += sprintf(buffer + len, " ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n");
- for (id = 0; id < max_pun; id++) {
- len += sprintf(buffer + len, " %2d ", id);
- for (lun = 0; lun < 8; lun++)
- len += sprintf(buffer + len, "%2s ", ti_p(get_scsi(shpnt)[id][lun]));
- len += sprintf(buffer + len, " %2d ", id);
- for (lun = 0; lun < 8; lun++)
- len += sprintf(buffer + len, "%2s ", ti_l(get_ldn(shpnt)[id][lun]));
- len += sprintf(buffer + len, "\n");
- }
-
- len += sprintf(buffer + len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n");
- len += sprintf(buffer + len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n");
- len += sprintf(buffer + len, " - = nothing found, nothing assigned or unprobed LUN)\n\n");
-
- *start = buffer + offset;
- len -= offset;
- if (len > length)
- len = length;
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- return len;
-}
-
-static int option_setup(char *str)
-{
- int ints[IM_MAX_HOSTS];
- char *cur = str;
- int i = 1;
-
- while (cur && isdigit(*cur) && i < IM_MAX_HOSTS) {
- ints[i++] = simple_strtoul(cur, NULL, 0);
- if ((cur = strchr(cur, ',')) != NULL)
- cur++;
- }
- ints[0] = i - 1;
- internal_ibmmca_scsi_setup(cur, ints);
- return 1;
-}
-
-__setup("ibmmcascsi=", option_setup);
-
-static struct mca_driver ibmmca_driver = {
- .id_table = ibmmca_id_table,
- .driver = {
- .name = "ibmmca",
- .bus = &mca_bus_type,
- .probe = ibmmca_probe,
- .remove = __devexit_p(ibmmca_remove),
- },
-};
-
-static int __init ibmmca_init(void)
-{
-#ifdef MODULE
- /* If the driver is run as module, read from conf.modules or cmd-line */
- if (boot_options)
- option_setup(boot_options);
-#endif
-
- return mca_register_driver_integrated(&ibmmca_driver, MCA_INTEGSCSI);
-}
-
-static void __exit ibmmca_exit(void)
-{
- mca_unregister_driver(&ibmmca_driver);
-}
-
-module_init(ibmmca_init);
-module_exit(ibmmca_exit);
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index a423d9633625..5eb1cb1a0028 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -1,8 +1,3 @@
-obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic.o
-
-ibmvscsic-y += ibmvscsi.o
-ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o
-ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o
-
-obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvstgt.o
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi.o
obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index bdfa223a7dbb..228daffb286d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
*
* Copyright (C) IBM Corporation, 2008
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/module.h>
@@ -27,11 +13,13 @@
#include <linux/dmapool.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/stringify.h>
+#include <linux/bsg-lib.h>
#include <asm/firmware.h>
#include <asm/irq.h>
#include <asm/vio.h>
@@ -46,12 +34,21 @@
static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
-static unsigned int max_lun = IBMVFC_MAX_LUN;
+static u64 max_lun = IBMVFC_MAX_LUN;
static unsigned int max_targets = IBMVFC_MAX_TARGETS;
static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
+static u16 max_sectors = IBMVFC_MAX_SECTORS;
+static u16 scsi_qdepth = IBMVFC_SCSI_QDEPTH;
static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
+static unsigned int cls3_error = IBMVFC_CLS3_ERROR;
+static unsigned int mq_enabled = IBMVFC_MQ;
+static unsigned int nr_scsi_hw_queues = IBMVFC_SCSI_HW_QUEUES;
+static unsigned int nr_scsi_channels = IBMVFC_SCSI_CHANNELS;
+static unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ;
+static unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M;
+
static LIST_HEAD(ibmvfc_head);
static DEFINE_SPINLOCK(ibmvfc_driver_lock);
static struct scsi_transport_template *ibmvfc_transport_template;
@@ -61,6 +58,22 @@ MODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION(IBMVFC_DRIVER_VERSION);
+module_param_named(mq, mq_enabled, uint, S_IRUGO);
+MODULE_PARM_DESC(mq, "Enable multiqueue support. "
+ "[Default=" __stringify(IBMVFC_MQ) "]");
+module_param_named(scsi_host_queues, nr_scsi_hw_queues, uint, S_IRUGO);
+MODULE_PARM_DESC(scsi_host_queues, "Number of SCSI Host submission queues. "
+ "[Default=" __stringify(IBMVFC_SCSI_HW_QUEUES) "]");
+module_param_named(scsi_hw_channels, nr_scsi_channels, uint, S_IRUGO);
+MODULE_PARM_DESC(scsi_hw_channels, "Number of hw scsi channels to request. "
+ "[Default=" __stringify(IBMVFC_SCSI_CHANNELS) "]");
+module_param_named(mig_channels_only, mig_channels_only, uint, S_IRUGO);
+MODULE_PARM_DESC(mig_channels_only, "Prevent migration to non-channelized system. "
+ "[Default=" __stringify(IBMVFC_MIG_NO_SUB_TO_CRQ) "]");
+module_param_named(mig_no_less_channels, mig_no_less_channels, uint, S_IRUGO);
+MODULE_PARM_DESC(mig_no_less_channels, "Prevent migration to system with less channels. "
+ "[Default=" __stringify(IBMVFC_MIG_NO_N_TO_M) "]");
+
module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
"[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
@@ -71,7 +84,13 @@ MODULE_PARM_DESC(default_timeout,
module_param_named(max_requests, max_requests, uint, S_IRUGO);
MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter. "
"[Default=" __stringify(IBMVFC_MAX_REQUESTS_DEFAULT) "]");
-module_param_named(max_lun, max_lun, uint, S_IRUGO);
+module_param_named(max_sectors, max_sectors, ushort, S_IRUGO);
+MODULE_PARM_DESC(max_sectors, "Maximum sectors for this adapter. "
+ "[Default=" __stringify(IBMVFC_MAX_SECTORS) "]");
+module_param_named(scsi_qdepth, scsi_qdepth, ushort, S_IRUGO);
+MODULE_PARM_DESC(scsi_qdepth, "Maximum scsi command depth per adapter queue. "
+ "[Default=" __stringify(IBMVFC_SCSI_QDEPTH) "]");
+module_param_named(max_lun, max_lun, ullong, S_IRUGO);
MODULE_PARM_DESC(max_lun, "Maximum allowed LUN. "
"[Default=" __stringify(IBMVFC_MAX_LUN) "]");
module_param_named(max_targets, max_targets, uint, S_IRUGO);
@@ -86,6 +105,9 @@ MODULE_PARM_DESC(debug, "Enable driver debug information. "
module_param_named(log_level, log_level, uint, 0);
MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. "
"[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]");
+module_param_named(cls3_error, cls3_error, uint, 0);
+MODULE_PARM_DESC(cls3_error, "Enable FC Class 3 Error Recovery. "
+ "[Default=" __stringify(IBMVFC_CLS3_ERROR) "]");
static const struct {
u16 status;
@@ -134,6 +156,7 @@ static const struct {
{ IBMVFC_FC_FAILURE, IBMVFC_VENDOR_SPECIFIC, DID_ERROR, 1, 1, "vendor specific" },
{ IBMVFC_FC_SCSI_ERROR, 0, DID_OK, 1, 0, "SCSI error" },
+ { IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." },
};
static void ibmvfc_npiv_login(struct ibmvfc_host *);
@@ -141,9 +164,53 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
static void ibmvfc_tgt_query_target(struct ibmvfc_target *);
static void ibmvfc_npiv_logout(struct ibmvfc_host *);
+static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *);
+static void ibmvfc_tgt_move_login(struct ibmvfc_target *);
+
+static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *, struct ibmvfc_channels *);
+static void ibmvfc_reg_sub_crqs(struct ibmvfc_host *, struct ibmvfc_channels *);
static const char *unknown_error = "unknown error";
+static long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba,
+ unsigned long length, unsigned long *cookie,
+ unsigned long *irq)
+{
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+ long rc;
+
+ rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, ioba, length);
+ *cookie = retbuf[0];
+ *irq = retbuf[1];
+
+ return rc;
+}
+
+static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
+{
+ u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
+
+ return (host_caps & cap_flags) ? 1 : 0;
+}
+
+static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
+ struct ibmvfc_cmd *vfc_cmd)
+{
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ return &vfc_cmd->v2.iu;
+ else
+ return &vfc_cmd->v1.iu;
+}
+
+static struct ibmvfc_fcp_rsp *ibmvfc_get_fcp_rsp(struct ibmvfc_host *vhost,
+ struct ibmvfc_cmd *vfc_cmd)
+{
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ return &vfc_cmd->v2.rsp;
+ else
+ return &vfc_cmd->v1.rsp;
+}
+
#ifdef CONFIG_SCSI_IBMVFC_TRACE
/**
* ibmvfc_trc_start - Log a start trace entry
@@ -155,9 +222,11 @@ static void ibmvfc_trc_start(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
struct ibmvfc_mad_common *mad = &evt->iu.mad_common;
+ struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
struct ibmvfc_trace_entry *entry;
+ int index = atomic_inc_return(&vhost->trace_index) & IBMVFC_TRACE_INDEX_MASK;
- entry = &vhost->trace[vhost->trace_index++];
+ entry = &vhost->trace[index];
entry->evt = evt;
entry->time = jiffies;
entry->fmt = evt->crq.format;
@@ -165,18 +234,18 @@ static void ibmvfc_trc_start(struct ibmvfc_event *evt)
switch (entry->fmt) {
case IBMVFC_CMD_FORMAT:
- entry->op_code = vfc_cmd->iu.cdb[0];
- entry->scsi_id = vfc_cmd->tgt_scsi_id;
- entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
- entry->tmf_flags = vfc_cmd->iu.tmf_flags;
- entry->u.start.xfer_len = vfc_cmd->iu.xfer_len;
+ entry->op_code = iu->cdb[0];
+ entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id);
+ entry->lun = scsilun_to_int(&iu->lun);
+ entry->tmf_flags = iu->tmf_flags;
+ entry->u.start.xfer_len = be32_to_cpu(iu->xfer_len);
break;
case IBMVFC_MAD_FORMAT:
- entry->op_code = mad->opcode;
+ entry->op_code = be32_to_cpu(mad->opcode);
break;
default:
break;
- };
+ }
}
/**
@@ -189,8 +258,12 @@ static void ibmvfc_trc_end(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
struct ibmvfc_mad_common *mad = &evt->xfer_iu->mad_common;
- struct ibmvfc_trace_entry *entry = &vhost->trace[vhost->trace_index++];
+ struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
+ struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
+ struct ibmvfc_trace_entry *entry;
+ int index = atomic_inc_return(&vhost->trace_index) & IBMVFC_TRACE_INDEX_MASK;
+ entry = &vhost->trace[index];
entry->evt = evt;
entry->time = jiffies;
entry->fmt = evt->crq.format;
@@ -198,24 +271,24 @@ static void ibmvfc_trc_end(struct ibmvfc_event *evt)
switch (entry->fmt) {
case IBMVFC_CMD_FORMAT:
- entry->op_code = vfc_cmd->iu.cdb[0];
- entry->scsi_id = vfc_cmd->tgt_scsi_id;
- entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
- entry->tmf_flags = vfc_cmd->iu.tmf_flags;
- entry->u.end.status = vfc_cmd->status;
- entry->u.end.error = vfc_cmd->error;
- entry->u.end.fcp_rsp_flags = vfc_cmd->rsp.flags;
- entry->u.end.rsp_code = vfc_cmd->rsp.data.info.rsp_code;
- entry->u.end.scsi_status = vfc_cmd->rsp.scsi_status;
+ entry->op_code = iu->cdb[0];
+ entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id);
+ entry->lun = scsilun_to_int(&iu->lun);
+ entry->tmf_flags = iu->tmf_flags;
+ entry->u.end.status = be16_to_cpu(vfc_cmd->status);
+ entry->u.end.error = be16_to_cpu(vfc_cmd->error);
+ entry->u.end.fcp_rsp_flags = rsp->flags;
+ entry->u.end.rsp_code = rsp->data.info.rsp_code;
+ entry->u.end.scsi_status = rsp->scsi_status;
break;
case IBMVFC_MAD_FORMAT:
- entry->op_code = mad->opcode;
- entry->u.end.status = mad->status;
+ entry->op_code = be32_to_cpu(mad->opcode);
+ entry->u.end.status = be16_to_cpu(mad->status);
break;
default:
break;
- };
+ }
}
#else
@@ -261,23 +334,24 @@ static const char *ibmvfc_get_cmd_error(u16 status, u16 error)
/**
* ibmvfc_get_err_result - Find the scsi status to return for the fcp response
+ * @vhost: ibmvfc host struct
* @vfc_cmd: ibmvfc command struct
*
* Return value:
* SCSI result value to return for completed command
**/
-static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
+static int ibmvfc_get_err_result(struct ibmvfc_host *vhost, struct ibmvfc_cmd *vfc_cmd)
{
int err;
- struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
- int fc_rsp_len = rsp->fcp_rsp_len;
+ struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
+ int fc_rsp_len = be32_to_cpu(rsp->fcp_rsp_len);
if ((rsp->flags & FCP_RSP_LEN_VALID) &&
((fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
rsp->data.info.rsp_code))
return DID_ERROR << 16;
- err = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
+ err = ibmvfc_get_err_index(be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error));
if (err >= 0)
return rsp->scsi_status | (cmd_status[err].result << 16);
return rsp->scsi_status | (DID_ERROR << 16);
@@ -421,22 +495,59 @@ static const char *ibmvfc_get_fc_type(u16 status)
* @tgt: ibmvfc target struct
* @action: action to perform
*
+ * Returns:
+ * 0 if action changed / non-zero if not changed
**/
-static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
+static int ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
enum ibmvfc_target_action action)
{
+ int rc = -EINVAL;
+
switch (tgt->action) {
+ case IBMVFC_TGT_ACTION_LOGOUT_RPORT:
+ if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT ||
+ action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+ tgt->action = action;
+ rc = 0;
+ }
+ break;
+ case IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT:
+ if (action == IBMVFC_TGT_ACTION_DEL_RPORT ||
+ action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+ tgt->action = action;
+ rc = 0;
+ }
+ break;
+ case IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT:
+ if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
+ tgt->action = action;
+ rc = 0;
+ }
+ break;
+ case IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT:
+ if (action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
+ tgt->action = action;
+ rc = 0;
+ }
+ break;
case IBMVFC_TGT_ACTION_DEL_RPORT:
- if (action == IBMVFC_TGT_ACTION_DELETED_RPORT)
+ if (action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
tgt->action = action;
+ rc = 0;
+ }
+ break;
case IBMVFC_TGT_ACTION_DELETED_RPORT:
break;
default:
- if (action == IBMVFC_TGT_ACTION_DEL_RPORT)
- tgt->add_rport = 0;
tgt->action = action;
+ rc = 0;
break;
}
+
+ if (action >= IBMVFC_TGT_ACTION_LOGOUT_RPORT)
+ tgt->add_rport = 0;
+
+ return rc;
}
/**
@@ -459,7 +570,7 @@ static int ibmvfc_set_host_state(struct ibmvfc_host *vhost,
default:
vhost->state = state;
break;
- };
+ }
return rc;
}
@@ -495,14 +606,23 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
break;
default:
break;
- };
+ }
break;
case IBMVFC_HOST_ACTION_TGT_INIT:
if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
vhost->action = action;
break;
+ case IBMVFC_HOST_ACTION_REENABLE:
+ case IBMVFC_HOST_ACTION_RESET:
+ vhost->action = action;
+ break;
case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_TGT_DEL:
+ case IBMVFC_HOST_ACTION_LOGO:
+ case IBMVFC_HOST_ACTION_QUERY_TGTS:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
+ case IBMVFC_HOST_ACTION_NONE:
+ default:
switch (vhost->action) {
case IBMVFC_HOST_ACTION_RESET:
case IBMVFC_HOST_ACTION_REENABLE:
@@ -510,18 +630,9 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
default:
vhost->action = action;
break;
- };
- break;
- case IBMVFC_HOST_ACTION_LOGO:
- case IBMVFC_HOST_ACTION_QUERY_TGTS:
- case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
- case IBMVFC_HOST_ACTION_NONE:
- case IBMVFC_HOST_ACTION_RESET:
- case IBMVFC_HOST_ACTION_REENABLE:
- default:
- vhost->action = action;
+ }
break;
- };
+ }
}
/**
@@ -533,7 +644,8 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
**/
static void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
{
- if (vhost->action == IBMVFC_HOST_ACTION_NONE) {
+ if (vhost->action == IBMVFC_HOST_ACTION_NONE &&
+ vhost->state == IBMVFC_ACTIVE) {
if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
scsi_block_requests(vhost->host);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
@@ -545,6 +657,19 @@ static void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
}
/**
+ * ibmvfc_del_tgt - Schedule cleanup and removal of the target
+ * @tgt: ibmvfc target struct
+ **/
+static void ibmvfc_del_tgt(struct ibmvfc_target *tgt)
+{
+ if (!ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT)) {
+ tgt->job_step = ibmvfc_tgt_implicit_logout_and_del;
+ tgt->init_retries = 0;
+ }
+ wake_up(&tgt->vhost->work_wait_q);
+}
+
+/**
* ibmvfc_link_down - Handle a link down event from the adapter
* @vhost: ibmvfc host struct
* @state: ibmvfc host state to enter
@@ -558,7 +683,7 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
ENTER;
scsi_block_requests(vhost->host);
list_for_each_entry(tgt, &vhost->targets, queue)
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
ibmvfc_set_host_state(vhost, state);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
vhost->events_to_log |= IBMVFC_AE_LINKDOWN;
@@ -587,11 +712,16 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost)
}
if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
+ memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE);
vhost->async_crq.cur = 0;
- list_for_each_entry(tgt, &vhost->targets, queue)
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (vhost->client_migrated)
+ tgt->need_login = 1;
+ else
+ ibmvfc_del_tgt(tgt);
+ }
+
scsi_block_requests(vhost->host);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
vhost->job_step = ibmvfc_npiv_login;
@@ -614,6 +744,15 @@ static int ibmvfc_send_crq(struct ibmvfc_host *vhost, u64 word1, u64 word2)
return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
}
+static int ibmvfc_send_sub_crq(struct ibmvfc_host *vhost, u64 cookie, u64 word1,
+ u64 word2, u64 word3, u64 word4)
+{
+ struct vio_dev *vdev = to_vio_dev(vhost->dev);
+
+ return plpar_hcall_norets(H_SEND_SUB_CRQ, vdev->unit_address, cookie,
+ word1, word2, word3, word4);
+}
+
/**
* ibmvfc_send_crq_init - Send a CRQ init message
* @vhost: ibmvfc host struct
@@ -641,6 +780,115 @@ static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost)
}
/**
+ * ibmvfc_init_event_pool - Allocates and initializes the event pool for a host
+ * @vhost: ibmvfc host who owns the event pool
+ * @queue: ibmvfc queue struct
+ *
+ * Returns zero on success.
+ **/
+static int ibmvfc_init_event_pool(struct ibmvfc_host *vhost,
+ struct ibmvfc_queue *queue)
+{
+ int i;
+ struct ibmvfc_event_pool *pool = &queue->evt_pool;
+
+ ENTER;
+ if (!queue->total_depth)
+ return 0;
+
+ pool->size = queue->total_depth;
+ pool->events = kcalloc(pool->size, sizeof(*pool->events), GFP_KERNEL);
+ if (!pool->events)
+ return -ENOMEM;
+
+ pool->iu_storage = dma_alloc_coherent(vhost->dev,
+ pool->size * sizeof(*pool->iu_storage),
+ &pool->iu_token, 0);
+
+ if (!pool->iu_storage) {
+ kfree(pool->events);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&queue->sent);
+ INIT_LIST_HEAD(&queue->free);
+ queue->evt_free = queue->evt_depth;
+ queue->reserved_free = queue->reserved_depth;
+ spin_lock_init(&queue->l_lock);
+
+ for (i = 0; i < pool->size; ++i) {
+ struct ibmvfc_event *evt = &pool->events[i];
+
+ /*
+ * evt->active states
+ * 1 = in flight
+ * 0 = being completed
+ * -1 = free/freed
+ */
+ atomic_set(&evt->active, -1);
+ atomic_set(&evt->free, 1);
+ evt->crq.valid = 0x80;
+ evt->crq.ioba = cpu_to_be64(pool->iu_token + (sizeof(*evt->xfer_iu) * i));
+ evt->xfer_iu = pool->iu_storage + i;
+ evt->vhost = vhost;
+ evt->queue = queue;
+ evt->ext_list = NULL;
+ list_add_tail(&evt->queue_list, &queue->free);
+ }
+
+ LEAVE;
+ return 0;
+}
+
+/**
+ * ibmvfc_free_event_pool - Frees memory of the event pool of a host
+ * @vhost: ibmvfc host who owns the event pool
+ * @queue: ibmvfc queue struct
+ *
+ **/
+static void ibmvfc_free_event_pool(struct ibmvfc_host *vhost,
+ struct ibmvfc_queue *queue)
+{
+ int i;
+ struct ibmvfc_event_pool *pool = &queue->evt_pool;
+
+ ENTER;
+ for (i = 0; i < pool->size; ++i) {
+ list_del(&pool->events[i].queue_list);
+ BUG_ON(atomic_read(&pool->events[i].free) != 1);
+ if (pool->events[i].ext_list)
+ dma_pool_free(vhost->sg_pool,
+ pool->events[i].ext_list,
+ pool->events[i].ext_list_token);
+ }
+
+ kfree(pool->events);
+ dma_free_coherent(vhost->dev,
+ pool->size * sizeof(*pool->iu_storage),
+ pool->iu_storage, pool->iu_token);
+ LEAVE;
+}
+
+/**
+ * ibmvfc_free_queue - Deallocate queue
+ * @vhost: ibmvfc host struct
+ * @queue: ibmvfc queue struct
+ *
+ * Unmaps dma and deallocates page for messages
+ **/
+static void ibmvfc_free_queue(struct ibmvfc_host *vhost,
+ struct ibmvfc_queue *queue)
+{
+ struct device *dev = vhost->dev;
+
+ dma_unmap_single(dev, queue->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)queue->msgs.handle);
+ queue->msgs.handle = NULL;
+
+ ibmvfc_free_event_pool(vhost, queue);
+}
+
+/**
* ibmvfc_release_crq_queue - Deallocates data and unregisters CRQ
* @vhost: ibmvfc host struct
*
@@ -651,7 +899,7 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
{
long rc = 0;
struct vio_dev *vdev = to_vio_dev(vhost->dev);
- struct ibmvfc_crq_queue *crq = &vhost->crq;
+ struct ibmvfc_queue *crq = &vhost->crq;
ibmvfc_dbg(vhost, "Releasing CRQ\n");
free_irq(vdev->irq, vhost);
@@ -664,8 +912,8 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
vhost->state = IBMVFC_NO_CRQ;
vhost->logged_in = 0;
- dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
- free_page((unsigned long)crq->msgs);
+
+ ibmvfc_free_queue(vhost, crq);
}
/**
@@ -679,6 +927,9 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
{
int rc = 0;
struct vio_dev *vdev = to_vio_dev(vhost->dev);
+ unsigned long flags;
+
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
/* Re-enable the CRQ */
do {
@@ -690,6 +941,15 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
if (rc)
dev_err(vhost->dev, "Error enabling adapter (rc=%d)\n", rc);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ spin_lock(vhost->crq.q_lock);
+ vhost->do_enquiry = 1;
+ vhost->using_channels = 0;
+ spin_unlock(vhost->crq.q_lock);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+
return rc;
}
@@ -705,7 +965,9 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
int rc = 0;
unsigned long flags;
struct vio_dev *vdev = to_vio_dev(vhost->dev);
- struct ibmvfc_crq_queue *crq = &vhost->crq;
+ struct ibmvfc_queue *crq = &vhost->crq;
+
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
/* Close the CRQ */
do {
@@ -715,12 +977,14 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
spin_lock_irqsave(vhost->host->host_lock, flags);
+ spin_lock(vhost->crq.q_lock);
vhost->state = IBMVFC_NO_CRQ;
vhost->logged_in = 0;
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+ vhost->do_enquiry = 1;
+ vhost->using_channels = 0;
/* Clean out the queue */
- memset(crq->msgs, 0, PAGE_SIZE);
+ memset(crq->msgs.crq, 0, PAGE_SIZE);
crq->cur = 0;
/* And re-open it again */
@@ -732,8 +996,12 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
dev_warn(vhost->dev, "Partner adapter not ready\n");
else if (rc != 0)
dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
+
+ spin_unlock(vhost->crq.q_lock);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+
return rc;
}
@@ -763,12 +1031,24 @@ static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
**/
static void ibmvfc_free_event(struct ibmvfc_event *evt)
{
- struct ibmvfc_host *vhost = evt->vhost;
- struct ibmvfc_event_pool *pool = &vhost->pool;
+ struct ibmvfc_event_pool *pool = &evt->queue->evt_pool;
+ unsigned long flags;
BUG_ON(!ibmvfc_valid_event(pool, evt));
BUG_ON(atomic_inc_return(&evt->free) != 1);
- list_add_tail(&evt->queue, &vhost->free);
+ BUG_ON(atomic_dec_and_test(&evt->active));
+
+ spin_lock_irqsave(&evt->queue->l_lock, flags);
+ list_add_tail(&evt->queue_list, &evt->queue->free);
+ if (evt->reserved) {
+ evt->reserved = 0;
+ evt->queue->reserved_free++;
+ } else {
+ evt->queue->evt_free++;
+ }
+ if (evt->eh_comp)
+ complete(evt->eh_comp);
+ spin_unlock_irqrestore(&evt->queue->l_lock, flags);
}
/**
@@ -784,16 +1064,31 @@ static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
if (cmnd) {
scsi_dma_unmap(cmnd);
- cmnd->scsi_done(cmnd);
+ scsi_done(cmnd);
}
- if (evt->eh_comp)
- complete(evt->eh_comp);
-
ibmvfc_free_event(evt);
}
/**
+ * ibmvfc_complete_purge - Complete failed command list
+ * @purge_list: list head of failed commands
+ *
+ * This function runs completions on commands to fail as a result of a
+ * host reset or platform migration.
+ **/
+static void ibmvfc_complete_purge(struct list_head *purge_list)
+{
+ struct ibmvfc_event *evt, *pos;
+
+ list_for_each_entry_safe(evt, pos, purge_list, queue_list) {
+ list_del(&evt->queue_list);
+ ibmvfc_trc_end(evt);
+ evt->done(evt);
+ }
+}
+
+/**
* ibmvfc_fail_request - Fail request with specified error code
* @evt: ibmvfc event struct
* @error_code: error code to fail request with
@@ -803,16 +1098,19 @@ static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
**/
static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
{
+ /*
+ * Anything we are failing should still be active. Otherwise, it
+ * implies we already got a response for the command and are doing
+ * something bad like double completing it.
+ */
+ BUG_ON(!atomic_dec_and_test(&evt->active));
if (evt->cmnd) {
evt->cmnd->result = (error_code << 16);
evt->done = ibmvfc_scsi_eh_done;
} else
- evt->xfer_iu->mad_common.status = IBMVFC_MAD_DRIVER_FAILED;
+ evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED);
- list_del(&evt->queue);
- del_timer(&evt->timer);
- ibmvfc_trc_end(evt);
- evt->done(evt);
+ timer_delete(&evt->timer);
}
/**
@@ -826,10 +1124,30 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
{
struct ibmvfc_event *evt, *pos;
+ struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
+ unsigned long flags;
+ int hwqs = 0;
+ int i;
+
+ if (vhost->using_channels)
+ hwqs = vhost->scsi_scrqs.active_queues;
ibmvfc_dbg(vhost, "Purging all requests\n");
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue)
+ spin_lock_irqsave(&vhost->crq.l_lock, flags);
+ list_for_each_entry_safe(evt, pos, &vhost->crq.sent, queue_list)
ibmvfc_fail_request(evt, error_code);
+ list_splice_init(&vhost->crq.sent, &vhost->purge);
+ spin_unlock_irqrestore(&vhost->crq.l_lock, flags);
+
+ for (i = 0; i < hwqs; i++) {
+ spin_lock_irqsave(queues[i].q_lock, flags);
+ spin_lock(&queues[i].l_lock);
+ list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
+ ibmvfc_fail_request(evt, error_code);
+ list_splice_init(&queues[i].sent, &vhost->purge);
+ spin_unlock(&queues[i].l_lock);
+ spin_unlock_irqrestore(queues[i].q_lock, flags);
+ }
}
/**
@@ -955,7 +1273,7 @@ static void ibmvfc_get_host_speed(struct Scsi_Host *shost)
spin_lock_irqsave(shost->host_lock, flags);
if (vhost->state == IBMVFC_ACTIVE) {
- switch (vhost->login_buf->resp.link_speed / 100) {
+ switch (be64_to_cpu(vhost->login_buf->resp.link_speed) / 100) {
case 1:
fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
break;
@@ -976,7 +1294,7 @@ static void ibmvfc_get_host_speed(struct Scsi_Host *shost)
break;
default:
ibmvfc_log(vhost, 3, "Unknown port speed: %lld Gbit\n",
- vhost->login_buf->resp.link_speed / 100);
+ be64_to_cpu(vhost->login_buf->resp.link_speed) / 100);
fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
break;
}
@@ -1133,6 +1451,7 @@ static int ibmvfc_issue_fc_host_lip(struct Scsi_Host *shost)
/**
* ibmvfc_gather_partition_info - Gather info about the LPAR
+ * @vhost: ibmvfc host struct
*
* Return value:
* none
@@ -1149,7 +1468,7 @@ static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
name = of_get_property(rootdn, "ibm,partition-name", NULL);
if (name)
- strncpy(vhost->partition_name, name, sizeof(vhost->partition_name));
+ strscpy(vhost->partition_name, name, sizeof(vhost->partition_name));
num = of_get_property(rootdn, "ibm,partition-no", NULL);
if (num)
vhost->partition_number = *num;
@@ -1166,118 +1485,98 @@ static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
{
struct ibmvfc_npiv_login *login_info = &vhost->login_info;
+ struct ibmvfc_queue *async_crq = &vhost->async_crq;
struct device_node *of_node = vhost->dev->of_node;
const char *location;
+ u16 max_cmds;
+
+ max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
+ if (mq_enabled)
+ max_cmds += (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) *
+ vhost->scsi_scrqs.desired_queues;
memset(login_info, 0, sizeof(*login_info));
- login_info->ostype = IBMVFC_OS_LINUX;
- login_info->max_dma_len = IBMVFC_MAX_SECTORS << 9;
- login_info->max_payload = sizeof(struct ibmvfc_fcp_cmd_iu);
- login_info->max_response = sizeof(struct ibmvfc_fcp_rsp);
- login_info->partition_num = vhost->partition_number;
- login_info->vfc_frame_version = 1;
- login_info->fcp_version = 3;
- login_info->flags = IBMVFC_FLUSH_ON_HALT;
+ login_info->ostype = cpu_to_be32(IBMVFC_OS_LINUX);
+ login_info->max_dma_len = cpu_to_be64(max_sectors << 9);
+ login_info->max_payload = cpu_to_be32(sizeof(struct ibmvfc_fcp_cmd_iu));
+ login_info->max_response = cpu_to_be32(sizeof(struct ibmvfc_fcp_rsp));
+ login_info->partition_num = cpu_to_be32(vhost->partition_number);
+ login_info->vfc_frame_version = cpu_to_be32(1);
+ login_info->fcp_version = cpu_to_be16(3);
+ login_info->flags = cpu_to_be16(IBMVFC_FLUSH_ON_HALT);
if (vhost->client_migrated)
- login_info->flags |= IBMVFC_CLIENT_MIGRATED;
+ login_info->flags |= cpu_to_be16(IBMVFC_CLIENT_MIGRATED);
- login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
- login_info->capabilities = IBMVFC_CAN_MIGRATE;
- login_info->async.va = vhost->async_crq.msg_token;
- login_info->async.len = vhost->async_crq.size * sizeof(*vhost->async_crq.msgs);
- strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME);
- strncpy(login_info->device_name,
- dev_name(&vhost->host->shost_gendev), IBMVFC_MAX_NAME);
+ login_info->max_cmds = cpu_to_be32(max_cmds);
+ login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN);
- location = of_get_property(of_node, "ibm,loc-code", NULL);
- location = location ? location : dev_name(vhost->dev);
- strncpy(login_info->drc_name, location, IBMVFC_MAX_NAME);
-}
+ if (vhost->mq_enabled || vhost->using_channels)
+ login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
-/**
- * ibmvfc_init_event_pool - Allocates and initializes the event pool for a host
- * @vhost: ibmvfc host who owns the event pool
- *
- * Returns zero on success.
- **/
-static int ibmvfc_init_event_pool(struct ibmvfc_host *vhost)
-{
- int i;
- struct ibmvfc_event_pool *pool = &vhost->pool;
-
- ENTER;
- pool->size = max_requests + IBMVFC_NUM_INTERNAL_REQ;
- pool->events = kcalloc(pool->size, sizeof(*pool->events), GFP_KERNEL);
- if (!pool->events)
- return -ENOMEM;
+ login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token);
+ login_info->async.len = cpu_to_be32(async_crq->size *
+ sizeof(*async_crq->msgs.async));
+ strscpy(login_info->partition_name, vhost->partition_name,
+ sizeof(login_info->partition_name));
- pool->iu_storage = dma_alloc_coherent(vhost->dev,
- pool->size * sizeof(*pool->iu_storage),
- &pool->iu_token, 0);
+ strscpy(login_info->device_name,
+ dev_name(&vhost->host->shost_gendev), sizeof(login_info->device_name));
- if (!pool->iu_storage) {
- kfree(pool->events);
- return -ENOMEM;
- }
-
- for (i = 0; i < pool->size; ++i) {
- struct ibmvfc_event *evt = &pool->events[i];
- atomic_set(&evt->free, 1);
- evt->crq.valid = 0x80;
- evt->crq.ioba = pool->iu_token + (sizeof(*evt->xfer_iu) * i);
- evt->xfer_iu = pool->iu_storage + i;
- evt->vhost = vhost;
- evt->ext_list = NULL;
- list_add_tail(&evt->queue, &vhost->free);
- }
-
- LEAVE;
- return 0;
+ location = of_get_property(of_node, "ibm,loc-code", NULL);
+ location = location ? location : dev_name(vhost->dev);
+ strscpy(login_info->drc_name, location, sizeof(login_info->drc_name));
}
/**
- * ibmvfc_free_event_pool - Frees memory of the event pool of a host
- * @vhost: ibmvfc host who owns the event pool
+ * __ibmvfc_get_event - Gets the next free event in pool
+ * @queue: ibmvfc queue struct
+ * @reserved: event is for a reserved management command
*
+ * Returns a free event from the pool.
**/
-static void ibmvfc_free_event_pool(struct ibmvfc_host *vhost)
+static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
{
- int i;
- struct ibmvfc_event_pool *pool = &vhost->pool;
+ struct ibmvfc_event *evt = NULL;
+ unsigned long flags;
- ENTER;
- for (i = 0; i < pool->size; ++i) {
- list_del(&pool->events[i].queue);
- BUG_ON(atomic_read(&pool->events[i].free) != 1);
- if (pool->events[i].ext_list)
- dma_pool_free(vhost->sg_pool,
- pool->events[i].ext_list,
- pool->events[i].ext_list_token);
+ spin_lock_irqsave(&queue->l_lock, flags);
+ if (reserved && queue->reserved_free) {
+ evt = list_entry(queue->free.next, struct ibmvfc_event, queue_list);
+ evt->reserved = 1;
+ queue->reserved_free--;
+ } else if (queue->evt_free) {
+ evt = list_entry(queue->free.next, struct ibmvfc_event, queue_list);
+ queue->evt_free--;
+ } else {
+ goto out;
}
- kfree(pool->events);
- dma_free_coherent(vhost->dev,
- pool->size * sizeof(*pool->iu_storage),
- pool->iu_storage, pool->iu_token);
- LEAVE;
+ atomic_set(&evt->free, 0);
+ list_del(&evt->queue_list);
+out:
+ spin_unlock_irqrestore(&queue->l_lock, flags);
+ return evt;
}
+#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0)
+#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1)
+
/**
- * ibmvfc_get_event - Gets the next free event in pool
- * @vhost: ibmvfc host struct
+ * ibmvfc_locked_done - Calls evt completion with host_lock held
+ * @evt: ibmvfc evt to complete
*
- * Returns a free event from the pool.
+ * All non-scsi command completion callbacks have the expectation that the
+ * host_lock is held. This callback is used by ibmvfc_init_event to wrap a
+ * MAD evt with the host_lock.
**/
-static struct ibmvfc_event *ibmvfc_get_event(struct ibmvfc_host *vhost)
+static void ibmvfc_locked_done(struct ibmvfc_event *evt)
{
- struct ibmvfc_event *evt;
+ unsigned long flags;
- BUG_ON(list_empty(&vhost->free));
- evt = list_entry(vhost->free.next, struct ibmvfc_event, queue);
- atomic_set(&evt->free, 0);
- list_del(&evt->queue);
- return evt;
+ spin_lock_irqsave(evt->vhost->host->host_lock, flags);
+ evt->_done(evt);
+ spin_unlock_irqrestore(evt->vhost->host->host_lock, flags);
}
/**
@@ -1292,9 +1591,15 @@ static void ibmvfc_init_event(struct ibmvfc_event *evt,
{
evt->cmnd = NULL;
evt->sync_iu = NULL;
- evt->crq.format = format;
- evt->done = done;
evt->eh_comp = NULL;
+ evt->crq.format = format;
+ if (format == IBMVFC_CMD_FORMAT)
+ evt->done = done;
+ else {
+ evt->_done = done;
+ evt->done = ibmvfc_locked_done;
+ }
+ evt->hwq = 0;
}
/**
@@ -1310,15 +1615,15 @@ static void ibmvfc_map_sg_list(struct scsi_cmnd *scmd, int nseg,
struct scatterlist *sg;
scsi_for_each_sg(scmd, sg, nseg, i) {
- md[i].va = sg_dma_address(sg);
- md[i].len = sg_dma_len(sg);
+ md[i].va = cpu_to_be64(sg_dma_address(sg));
+ md[i].len = cpu_to_be32(sg_dma_len(sg));
md[i].key = 0;
}
}
/**
- * ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes decriptor fields
- * @scmd: Scsi_Cmnd with the scatterlist
+ * ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes descriptor fields
+ * @scmd: struct scsi_cmnd with the scatterlist
* @evt: ibmvfc event struct
* @vfc_cmd: vfc_cmd that contains the memory descriptor
* @dev: device for which to map dma memory
@@ -1334,10 +1639,14 @@ static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
int sg_mapped;
struct srp_direct_buf *data = &vfc_cmd->ioba;
struct ibmvfc_host *vhost = dev_get_drvdata(dev);
+ struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(evt->vhost, vfc_cmd);
+
+ if (cls3_error)
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_CLASS_3_ERR);
sg_mapped = scsi_dma_map(scmd);
if (!sg_mapped) {
- vfc_cmd->flags |= IBMVFC_NO_MEM_DESC;
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC);
return 0;
} else if (unlikely(sg_mapped < 0)) {
if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
@@ -1346,11 +1655,11 @@ static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
}
if (scmd->sc_data_direction == DMA_TO_DEVICE) {
- vfc_cmd->flags |= IBMVFC_WRITE;
- vfc_cmd->iu.add_cdb_len |= IBMVFC_WRDATA;
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE);
+ iu->add_cdb_len |= IBMVFC_WRDATA;
} else {
- vfc_cmd->flags |= IBMVFC_READ;
- vfc_cmd->iu.add_cdb_len |= IBMVFC_RDDATA;
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ);
+ iu->add_cdb_len |= IBMVFC_RDDATA;
}
if (sg_mapped == 1) {
@@ -1358,7 +1667,7 @@ static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
return 0;
}
- vfc_cmd->flags |= IBMVFC_SCATTERLIST;
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST);
if (!evt->ext_list) {
evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
@@ -1374,20 +1683,21 @@ static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
ibmvfc_map_sg_list(scmd, sg_mapped, evt->ext_list);
- data->va = evt->ext_list_token;
- data->len = sg_mapped * sizeof(struct srp_direct_buf);
+ data->va = cpu_to_be64(evt->ext_list_token);
+ data->len = cpu_to_be32(sg_mapped * sizeof(struct srp_direct_buf));
data->key = 0;
return 0;
}
/**
* ibmvfc_timeout - Internal command timeout handler
- * @evt: struct ibmvfc_event that timed out
+ * @t: struct ibmvfc_event that timed out
*
* Called when an internally generated command times out
**/
-static void ibmvfc_timeout(struct ibmvfc_event *evt)
+static void ibmvfc_timeout(struct timer_list *t)
{
+ struct ibmvfc_event *evt = timer_container_of(evt, t, timer);
struct ibmvfc_host *vhost = evt->vhost;
dev_err(vhost->dev, "Command timed out (%p). Resetting connection\n", evt);
ibmvfc_reset_host(vhost);
@@ -1404,33 +1714,47 @@ static void ibmvfc_timeout(struct ibmvfc_event *evt)
static int ibmvfc_send_event(struct ibmvfc_event *evt,
struct ibmvfc_host *vhost, unsigned long timeout)
{
- u64 *crq_as_u64 = (u64 *) &evt->crq;
+ __be64 *crq_as_u64 = (__be64 *) &evt->crq;
+ unsigned long flags;
int rc;
/* Copy the IU into the transfer area */
*evt->xfer_iu = evt->iu;
if (evt->crq.format == IBMVFC_CMD_FORMAT)
- evt->xfer_iu->cmd.tag = (u64)evt;
+ evt->xfer_iu->cmd.tag = cpu_to_be64((u64)evt);
else if (evt->crq.format == IBMVFC_MAD_FORMAT)
- evt->xfer_iu->mad_common.tag = (u64)evt;
+ evt->xfer_iu->mad_common.tag = cpu_to_be64((u64)evt);
else
BUG();
- list_add_tail(&evt->queue, &vhost->sent);
- init_timer(&evt->timer);
+ timer_setup(&evt->timer, ibmvfc_timeout, 0);
if (timeout) {
- evt->timer.data = (unsigned long) evt;
evt->timer.expires = jiffies + (timeout * HZ);
- evt->timer.function = (void (*)(unsigned long))ibmvfc_timeout;
add_timer(&evt->timer);
}
+ spin_lock_irqsave(&evt->queue->l_lock, flags);
+ list_add_tail(&evt->queue_list, &evt->queue->sent);
+ atomic_set(&evt->active, 1);
+
mb();
- if ((rc = ibmvfc_send_crq(vhost, crq_as_u64[0], crq_as_u64[1]))) {
- list_del(&evt->queue);
- del_timer(&evt->timer);
+ if (evt->queue->fmt == IBMVFC_SUB_CRQ_FMT)
+ rc = ibmvfc_send_sub_crq(vhost,
+ evt->queue->vios_cookie,
+ be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]),
+ 0, 0);
+ else
+ rc = ibmvfc_send_crq(vhost, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+
+ if (rc) {
+ atomic_set(&evt->active, 0);
+ list_del(&evt->queue_list);
+ spin_unlock_irqrestore(&evt->queue->l_lock, flags);
+ timer_delete(&evt->timer);
/* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY.
* Firmware will send a CRQ with a transport event (0xFF) to
@@ -1451,11 +1775,13 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
evt->cmnd->result = DID_ERROR << 16;
evt->done = ibmvfc_scsi_eh_done;
} else
- evt->xfer_iu->mad_common.status = IBMVFC_MAD_CRQ_ERROR;
+ evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
evt->done(evt);
- } else
+ } else {
+ spin_unlock_irqrestore(&evt->queue->l_lock, flags);
ibmvfc_trc_start(evt);
+ }
return 0;
}
@@ -1469,10 +1795,10 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
{
struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
struct ibmvfc_host *vhost = evt->vhost;
- struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
+ struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
struct scsi_cmnd *cmnd = evt->cmnd;
const char *err = unknown_error;
- int index = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
+ int index = ibmvfc_get_err_index(be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error));
int logerr = 0;
int rsp_code = 0;
@@ -1487,9 +1813,9 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
if (rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = rsp->data.info.rsp_code;
- scmd_printk(KERN_ERR, cmnd, "Command (%02X) failed: %s (%x:%x) "
+ scmd_printk(KERN_ERR, cmnd, "Command (%02X) : %s (%x:%x) "
"flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x\n",
- cmnd->cmnd[0], err, vfc_cmd->status, vfc_cmd->error,
+ cmnd->cmnd[0], err, be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error),
rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status);
}
@@ -1503,15 +1829,18 @@ static void ibmvfc_relogin(struct scsi_device *sdev)
struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
struct ibmvfc_target *tgt;
+ unsigned long flags;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
list_for_each_entry(tgt, &vhost->targets, queue) {
if (rport == tgt->rport) {
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
break;
}
}
ibmvfc_reinit_host(vhost);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
}
/**
@@ -1523,29 +1852,30 @@ static void ibmvfc_relogin(struct scsi_device *sdev)
static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
{
struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
- struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
+ struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(evt->vhost, vfc_cmd);
struct scsi_cmnd *cmnd = evt->cmnd;
u32 rsp_len = 0;
- u32 sense_len = rsp->fcp_sense_len;
+ u32 sense_len = be32_to_cpu(rsp->fcp_sense_len);
if (cmnd) {
- if (vfc_cmd->response_flags & IBMVFC_ADAPTER_RESID_VALID)
- scsi_set_resid(cmnd, vfc_cmd->adapter_resid);
+ if (be16_to_cpu(vfc_cmd->response_flags) & IBMVFC_ADAPTER_RESID_VALID)
+ scsi_set_resid(cmnd, be32_to_cpu(vfc_cmd->adapter_resid));
else if (rsp->flags & FCP_RESID_UNDER)
- scsi_set_resid(cmnd, rsp->fcp_resid);
+ scsi_set_resid(cmnd, be32_to_cpu(rsp->fcp_resid));
else
scsi_set_resid(cmnd, 0);
if (vfc_cmd->status) {
- cmnd->result = ibmvfc_get_err_result(vfc_cmd);
+ cmnd->result = ibmvfc_get_err_result(evt->vhost, vfc_cmd);
if (rsp->flags & FCP_RSP_LEN_VALID)
- rsp_len = rsp->fcp_rsp_len;
+ rsp_len = be32_to_cpu(rsp->fcp_rsp_len);
if ((sense_len + rsp_len) > SCSI_SENSE_BUFFERSIZE)
sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
- if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
+ if ((be16_to_cpu(vfc_cmd->status) & IBMVFC_VIOS_FAILURE) &&
+ (be16_to_cpu(vfc_cmd->error) == IBMVFC_PLOGI_REQUIRED))
ibmvfc_relogin(cmnd->device);
if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
@@ -1559,12 +1889,9 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
cmnd->result = (DID_ERROR << 16);
scsi_dma_unmap(cmnd);
- cmnd->scsi_done(cmnd);
+ scsi_done(cmnd);
}
- if (evt->eh_comp)
- complete(evt->eh_comp);
-
ibmvfc_free_event(evt);
}
@@ -1593,69 +1920,95 @@ static inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost)
case IBMVFC_ACTIVE:
result = 0;
break;
- };
+ }
return result;
}
+static struct ibmvfc_cmd *ibmvfc_init_vfc_cmd(struct ibmvfc_event *evt, struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
+ struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
+ struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
+ size_t offset;
+
+ memset(vfc_cmd, 0, sizeof(*vfc_cmd));
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+ offset = offsetof(struct ibmvfc_cmd, v2.rsp);
+ vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
+ } else
+ offset = offsetof(struct ibmvfc_cmd, v1.rsp);
+ vfc_cmd->resp.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + offset);
+ vfc_cmd->resp.len = cpu_to_be32(sizeof(*rsp));
+ vfc_cmd->frame_type = cpu_to_be32(IBMVFC_SCSI_FCP_TYPE);
+ vfc_cmd->payload_len = cpu_to_be32(sizeof(*iu));
+ vfc_cmd->resp_len = cpu_to_be32(sizeof(*rsp));
+ vfc_cmd->cancel_key = cpu_to_be32((unsigned long)sdev->hostdata);
+ vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id);
+ int_to_scsilun(sdev->lun, &iu->lun);
+
+ return vfc_cmd;
+}
+
/**
* ibmvfc_queuecommand - The queuecommand function of the scsi template
+ * @shost: scsi host struct
* @cmnd: struct scsi_cmnd to be executed
- * @done: Callback function to be called when cmnd is completed
*
* Returns:
* 0 on success / other on failure
**/
-static int ibmvfc_queuecommand_lck(struct scsi_cmnd *cmnd,
- void (*done) (struct scsi_cmnd *))
+static int ibmvfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
{
- struct ibmvfc_host *vhost = shost_priv(cmnd->device->host);
+ struct ibmvfc_host *vhost = shost_priv(shost);
struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
struct ibmvfc_cmd *vfc_cmd;
+ struct ibmvfc_fcp_cmd_iu *iu;
struct ibmvfc_event *evt;
- u8 tag[2];
+ u32 tag_and_hwq = blk_mq_unique_tag(scsi_cmd_to_rq(cmnd));
+ u16 hwq = blk_mq_unique_tag_to_hwq(tag_and_hwq);
+ u16 scsi_channel;
int rc;
if (unlikely((rc = fc_remote_port_chkready(rport))) ||
unlikely((rc = ibmvfc_host_chkready(vhost)))) {
cmnd->result = rc;
- done(cmnd);
+ scsi_done(cmnd);
return 0;
}
cmnd->result = (DID_OK << 16);
- evt = ibmvfc_get_event(vhost);
+ if (vhost->using_channels) {
+ scsi_channel = hwq % vhost->scsi_scrqs.active_queues;
+ evt = ibmvfc_get_event(&vhost->scsi_scrqs.scrqs[scsi_channel]);
+ if (!evt)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ evt->hwq = hwq % vhost->scsi_scrqs.active_queues;
+ } else {
+ evt = ibmvfc_get_event(&vhost->crq);
+ if (!evt)
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
ibmvfc_init_event(evt, ibmvfc_scsi_done, IBMVFC_CMD_FORMAT);
evt->cmnd = cmnd;
- cmnd->scsi_done = done;
- vfc_cmd = &evt->iu.cmd;
- memset(vfc_cmd, 0, sizeof(*vfc_cmd));
- vfc_cmd->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
- vfc_cmd->resp.len = sizeof(vfc_cmd->rsp);
- vfc_cmd->frame_type = IBMVFC_SCSI_FCP_TYPE;
- vfc_cmd->payload_len = sizeof(vfc_cmd->iu);
- vfc_cmd->resp_len = sizeof(vfc_cmd->rsp);
- vfc_cmd->cancel_key = (unsigned long)cmnd->device->hostdata;
- vfc_cmd->tgt_scsi_id = rport->port_id;
- vfc_cmd->iu.xfer_len = scsi_bufflen(cmnd);
- int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun);
- memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len);
-
- if (scsi_populate_tag_msg(cmnd, tag)) {
- vfc_cmd->task_tag = tag[1];
- switch (tag[0]) {
- case MSG_SIMPLE_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK;
- break;
- case MSG_HEAD_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_HEAD_OF_QUEUE;
- break;
- case MSG_ORDERED_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_ORDERED_TASK;
- break;
- };
+
+ vfc_cmd = ibmvfc_init_vfc_cmd(evt, cmnd->device);
+ iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
+
+ iu->xfer_len = cpu_to_be32(scsi_bufflen(cmnd));
+ memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len);
+
+ if (cmnd->flags & SCMD_TAGGED) {
+ vfc_cmd->task_tag = cpu_to_be64(scsi_cmd_to_rq(cmnd)->tag);
+ iu->pri_task_attr = IBMVFC_SIMPLE_TASK;
}
+ vfc_cmd->correlation = cpu_to_be64((u64)evt);
+
if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev))))
return ibmvfc_send_event(evt, vhost, 0);
@@ -1668,12 +2021,10 @@ static int ibmvfc_queuecommand_lck(struct scsi_cmnd *cmnd,
"Failed to map DMA buffer for command. rc=%d\n", rc);
cmnd->result = DID_ERROR << 16;
- done(cmnd);
+ scsi_done(cmnd);
return 0;
}
-static DEF_SCSI_QCMD(ibmvfc_queuecommand)
-
/**
* ibmvfc_sync_completion - Signal that a synchronous command has completed
* @evt: ibmvfc event struct
@@ -1704,14 +2055,14 @@ static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt)
/**
* ibmvfc_bsg_timeout - Handle a BSG timeout
- * @job: struct fc_bsg_job that timed out
+ * @job: struct bsg_job that timed out
*
* Returns:
* 0 on success / other on failure
**/
-static int ibmvfc_bsg_timeout(struct fc_bsg_job *job)
+static int ibmvfc_bsg_timeout(struct bsg_job *job)
{
- struct ibmvfc_host *vhost = shost_priv(job->shost);
+ struct ibmvfc_host *vhost = shost_priv(fc_bsg_to_shost(job));
unsigned long port_id = (unsigned long)job->dd_data;
struct ibmvfc_event *evt;
struct ibmvfc_tmf *tmf;
@@ -1727,17 +2078,22 @@ static int ibmvfc_bsg_timeout(struct fc_bsg_job *job)
}
vhost->aborting_passthru = 1;
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return -ENOMEM;
+ }
+
ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT);
tmf = &evt->iu.tmf;
memset(tmf, 0, sizeof(*tmf));
- tmf->common.version = 1;
- tmf->common.opcode = IBMVFC_TMF_MAD;
- tmf->common.length = sizeof(*tmf);
- tmf->scsi_id = port_id;
- tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
- tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY;
+ tmf->common.version = cpu_to_be32(1);
+ tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD);
+ tmf->common.length = cpu_to_be16(sizeof(*tmf));
+ tmf->scsi_id = cpu_to_be64(port_id);
+ tmf->cancel_key = cpu_to_be32(IBMVFC_PASSTHRU_CANCEL_KEY);
+ tmf->my_cancel_key = cpu_to_be32(IBMVFC_INTERNAL_CANCEL_KEY);
rc = ibmvfc_send_event(evt, vhost, default_timeout);
if (rc != 0) {
@@ -1785,14 +2141,18 @@ static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
if (unlikely((rc = ibmvfc_host_chkready(vhost))))
goto unlock_out;
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ rc = -ENOMEM;
+ goto unlock_out;
+ }
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
plogi = &evt->iu.plogi;
memset(plogi, 0, sizeof(*plogi));
- plogi->common.version = 1;
- plogi->common.opcode = IBMVFC_PORT_LOGIN;
- plogi->common.length = sizeof(*plogi);
- plogi->scsi_id = port_id;
+ plogi->common.version = cpu_to_be32(1);
+ plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+ plogi->common.length = cpu_to_be16(sizeof(*plogi));
+ plogi->scsi_id = cpu_to_be64(port_id);
evt->sync_iu = &rsp_iu;
init_completion(&evt->comp);
@@ -1817,47 +2177,51 @@ unlock_out:
/**
* ibmvfc_bsg_request - Handle a BSG request
- * @job: struct fc_bsg_job to be executed
+ * @job: struct bsg_job to be executed
*
* Returns:
* 0 on success / other on failure
**/
-static int ibmvfc_bsg_request(struct fc_bsg_job *job)
+static int ibmvfc_bsg_request(struct bsg_job *job)
{
- struct ibmvfc_host *vhost = shost_priv(job->shost);
- struct fc_rport *rport = job->rport;
+ struct ibmvfc_host *vhost = shost_priv(fc_bsg_to_shost(job));
+ struct fc_rport *rport = fc_bsg_to_rport(job);
struct ibmvfc_passthru_mad *mad;
struct ibmvfc_event *evt;
union ibmvfc_iu rsp_iu;
unsigned long flags, port_id = -1;
- unsigned int code = job->request->msgcode;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ unsigned int code = bsg_request->msgcode;
int rc = 0, req_seg, rsp_seg, issue_login = 0;
u32 fc_flags, rsp_len;
ENTER;
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
if (rport)
port_id = rport->port_id;
switch (code) {
case FC_BSG_HST_ELS_NOLOGIN:
- port_id = (job->request->rqst_data.h_els.port_id[0] << 16) |
- (job->request->rqst_data.h_els.port_id[1] << 8) |
- job->request->rqst_data.h_els.port_id[2];
+ port_id = (bsg_request->rqst_data.h_els.port_id[0] << 16) |
+ (bsg_request->rqst_data.h_els.port_id[1] << 8) |
+ bsg_request->rqst_data.h_els.port_id[2];
+ fallthrough;
case FC_BSG_RPT_ELS:
fc_flags = IBMVFC_FC_ELS;
break;
case FC_BSG_HST_CT:
issue_login = 1;
- port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) |
- (job->request->rqst_data.h_ct.port_id[1] << 8) |
- job->request->rqst_data.h_ct.port_id[2];
+ port_id = (bsg_request->rqst_data.h_ct.port_id[0] << 16) |
+ (bsg_request->rqst_data.h_ct.port_id[1] << 8) |
+ bsg_request->rqst_data.h_ct.port_id[2];
+ fallthrough;
case FC_BSG_RPT_CT:
fc_flags = IBMVFC_FC_CT_IU;
break;
default:
return -ENOTSUPP;
- };
+ }
if (port_id == -1)
return -EINVAL;
@@ -1899,31 +2263,36 @@ static int ibmvfc_bsg_request(struct fc_bsg_job *job)
goto out;
}
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ rc = -ENOMEM;
+ goto out;
+ }
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
mad = &evt->iu.passthru;
memset(mad, 0, sizeof(*mad));
- mad->common.version = 1;
- mad->common.opcode = IBMVFC_PASSTHRU;
- mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
-
- mad->cmd_ioba.va = (u64)evt->crq.ioba +
- offsetof(struct ibmvfc_passthru_mad, iu);
- mad->cmd_ioba.len = sizeof(mad->iu);
-
- mad->iu.cmd_len = job->request_payload.payload_len;
- mad->iu.rsp_len = job->reply_payload.payload_len;
- mad->iu.flags = fc_flags;
- mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
-
- mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list);
- mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list);
- mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list);
- mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list);
- mad->iu.scsi_id = port_id;
- mad->iu.tag = (u64)evt;
- rsp_len = mad->iu.rsp.len;
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_PASSTHRU);
+ mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu));
+
+ mad->cmd_ioba.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) +
+ offsetof(struct ibmvfc_passthru_mad, iu));
+ mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu));
+
+ mad->iu.cmd_len = cpu_to_be32(job->request_payload.payload_len);
+ mad->iu.rsp_len = cpu_to_be32(job->reply_payload.payload_len);
+ mad->iu.flags = cpu_to_be32(fc_flags);
+ mad->iu.cancel_key = cpu_to_be32(IBMVFC_PASSTHRU_CANCEL_KEY);
+
+ mad->iu.cmd.va = cpu_to_be64(sg_dma_address(job->request_payload.sg_list));
+ mad->iu.cmd.len = cpu_to_be32(sg_dma_len(job->request_payload.sg_list));
+ mad->iu.rsp.va = cpu_to_be64(sg_dma_address(job->reply_payload.sg_list));
+ mad->iu.rsp.len = cpu_to_be32(sg_dma_len(job->reply_payload.sg_list));
+ mad->iu.scsi_id = cpu_to_be64(port_id);
+ mad->iu.tag = cpu_to_be64((u64)evt);
+ rsp_len = be32_to_cpu(mad->iu.rsp.len);
evt->sync_iu = &rsp_iu;
init_completion(&evt->comp);
@@ -1940,13 +2309,14 @@ static int ibmvfc_bsg_request(struct fc_bsg_job *job)
if (rsp_iu.passthru.common.status)
rc = -EIO;
else
- job->reply->reply_payload_rcv_len = rsp_len;
+ bsg_reply->reply_payload_rcv_len = rsp_len;
spin_lock_irqsave(vhost->host->host_lock, flags);
ibmvfc_free_event(evt);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
- job->reply->result = rc;
- job->job_done(job);
+ bsg_reply->result = rc;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
rc = 0;
out:
dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
@@ -1974,28 +2344,32 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
struct ibmvfc_cmd *tmf;
struct ibmvfc_event *evt = NULL;
union ibmvfc_iu rsp_iu;
- struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
+ struct ibmvfc_fcp_cmd_iu *iu;
+ struct ibmvfc_fcp_rsp *fc_rsp = ibmvfc_get_fcp_rsp(vhost, &rsp_iu.cmd);
int rsp_rc = -EBUSY;
unsigned long flags;
int rsp_code = 0;
spin_lock_irqsave(vhost->host->host_lock, flags);
if (vhost->state == IBMVFC_ACTIVE) {
- evt = ibmvfc_get_event(vhost);
+ if (vhost->using_channels)
+ evt = ibmvfc_get_event(&vhost->scsi_scrqs.scrqs[0]);
+ else
+ evt = ibmvfc_get_event(&vhost->crq);
+
+ if (!evt) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return -ENOMEM;
+ }
+
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
+ tmf = ibmvfc_init_vfc_cmd(evt, sdev);
+ iu = ibmvfc_get_fcp_iu(vhost, tmf);
- tmf = &evt->iu.cmd;
- memset(tmf, 0, sizeof(*tmf));
- tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
- tmf->resp.len = sizeof(tmf->rsp);
- tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
- tmf->payload_len = sizeof(tmf->iu);
- tmf->resp_len = sizeof(tmf->rsp);
- tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->tgt_scsi_id = rport->port_id;
- int_to_scsilun(sdev->lun, &tmf->iu.lun);
- tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
- tmf->iu.tmf_flags = type;
+ tmf->flags = cpu_to_be16((IBMVFC_NO_MEM_DESC | IBMVFC_TMF));
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ tmf->target_wwpn = cpu_to_be64(rport->port_name);
+ iu->tmf_flags = type;
evt->sync_iu = &rsp_iu;
init_completion(&evt->comp);
@@ -2013,16 +2387,16 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
wait_for_completion(&evt->comp);
if (rsp_iu.cmd.status)
- rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+ rsp_code = ibmvfc_get_err_result(vhost, &rsp_iu.cmd);
if (rsp_code) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = fc_rsp->data.info.rsp_code;
sdev_printk(KERN_ERR, sdev, "%s reset failed: %s (%x:%x) "
- "flags: %x fcp_rsp: %x, scsi_status: %x\n",
- desc, ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
- rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
+ "flags: %x fcp_rsp: %x, scsi_status: %x\n", desc,
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)),
+ be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code,
fc_rsp->scsi_status);
rsp_rc = -EIO;
} else
@@ -2037,7 +2411,7 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
/**
* ibmvfc_match_rport - Match function for specified remote port
* @evt: ibmvfc event struct
- * @device: device to match (rport)
+ * @rport: device to match
*
* Returns:
* 1 if event matches rport / 0 if event does not match rport
@@ -2085,6 +2459,24 @@ static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
}
/**
+ * ibmvfc_event_is_free - Check if event is free or not
+ * @evt: ibmvfc event struct
+ *
+ * Returns:
+ * true / false
+ **/
+static bool ibmvfc_event_is_free(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_event *loop_evt;
+
+ list_for_each_entry(loop_evt, &evt->queue->free, queue_list)
+ if (loop_evt == evt)
+ return true;
+
+ return false;
+}
+
+/**
* ibmvfc_wait_for_ops - Wait for ops to complete
* @vhost: ibmvfc host struct
* @device: device to match (starget or sdev)
@@ -2098,19 +2490,35 @@ static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
{
struct ibmvfc_event *evt;
DECLARE_COMPLETION_ONSTACK(comp);
- int wait;
+ int wait, i, q_index, q_size;
unsigned long flags;
signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ;
+ struct ibmvfc_queue *queues;
ENTER;
+ if (vhost->mq_enabled && vhost->using_channels) {
+ queues = vhost->scsi_scrqs.scrqs;
+ q_size = vhost->scsi_scrqs.active_queues;
+ } else {
+ queues = &vhost->crq;
+ q_size = 1;
+ }
+
do {
wait = 0;
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(evt, &vhost->sent, queue) {
- if (match(evt, device)) {
- evt->eh_comp = &comp;
- wait++;
+ for (q_index = 0; q_index < q_size; q_index++) {
+ spin_lock(&queues[q_index].l_lock);
+ for (i = 0; i < queues[q_index].evt_pool.size; i++) {
+ evt = &queues[q_index].evt_pool.events[i];
+ if (!ibmvfc_event_is_free(evt)) {
+ if (match(evt, device)) {
+ evt->eh_comp = &comp;
+ wait++;
+ }
+ }
}
+ spin_unlock(&queues[q_index].l_lock);
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -2120,11 +2528,18 @@ static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
if (!timeout) {
wait = 0;
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(evt, &vhost->sent, queue) {
- if (match(evt, device)) {
- evt->eh_comp = NULL;
- wait++;
+ for (q_index = 0; q_index < q_size; q_index++) {
+ spin_lock(&queues[q_index].l_lock);
+ for (i = 0; i < queues[q_index].evt_pool.size; i++) {
+ evt = &queues[q_index].evt_pool.events[i];
+ if (!ibmvfc_event_is_free(evt)) {
+ if (match(evt, device)) {
+ evt->eh_comp = NULL;
+ wait++;
+ }
+ }
}
+ spin_unlock(&queues[q_index].l_lock);
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
if (wait)
@@ -2139,23 +2554,130 @@ static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
return SUCCESS;
}
-/**
- * ibmvfc_cancel_all - Cancel all outstanding commands to the device
- * @sdev: scsi device to cancel commands
- * @type: type of error recovery being performed
- *
- * This sends a cancel to the VIOS for the specified device. This does
- * NOT send any abort to the actual device. That must be done separately.
- *
- * Returns:
- * 0 on success / other on failure
- **/
-static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
+static struct ibmvfc_event *ibmvfc_init_tmf(struct ibmvfc_queue *queue,
+ struct scsi_device *sdev,
+ int type)
{
struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct scsi_target *starget = scsi_target(sdev);
struct fc_rport *rport = starget_to_rport(starget);
+ struct ibmvfc_event *evt;
struct ibmvfc_tmf *tmf;
+
+ evt = ibmvfc_get_reserved_event(queue);
+ if (!evt)
+ return NULL;
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+ tmf->common.version = cpu_to_be32(2);
+ tmf->target_wwpn = cpu_to_be64(rport->port_name);
+ } else {
+ tmf->common.version = cpu_to_be32(1);
+ }
+ tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD);
+ tmf->common.length = cpu_to_be16(sizeof(*tmf));
+ tmf->scsi_id = cpu_to_be64(rport->port_id);
+ int_to_scsilun(sdev->lun, &tmf->lun);
+ if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS))
+ type &= ~IBMVFC_TMF_SUPPRESS_ABTS;
+ if (vhost->state == IBMVFC_ACTIVE)
+ tmf->flags = cpu_to_be32((type | IBMVFC_TMF_LUA_VALID));
+ else
+ tmf->flags = cpu_to_be32(((type & IBMVFC_TMF_SUPPRESS_ABTS) | IBMVFC_TMF_LUA_VALID));
+ tmf->cancel_key = cpu_to_be32((unsigned long)sdev->hostdata);
+ tmf->my_cancel_key = cpu_to_be32((unsigned long)starget->hostdata);
+
+ init_completion(&evt->comp);
+
+ return evt;
+}
+
+static int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type)
+{
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
+ struct ibmvfc_event *evt, *found_evt, *temp;
+ struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
+ unsigned long flags;
+ int num_hwq, i;
+ int fail = 0;
+ LIST_HEAD(cancelq);
+ u16 status;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ num_hwq = vhost->scsi_scrqs.active_queues;
+ for (i = 0; i < num_hwq; i++) {
+ spin_lock(queues[i].q_lock);
+ spin_lock(&queues[i].l_lock);
+ found_evt = NULL;
+ list_for_each_entry(evt, &queues[i].sent, queue_list) {
+ if (evt->cmnd && evt->cmnd->device == sdev) {
+ found_evt = evt;
+ break;
+ }
+ }
+ spin_unlock(&queues[i].l_lock);
+
+ if (found_evt && vhost->logged_in) {
+ evt = ibmvfc_init_tmf(&queues[i], sdev, type);
+ if (!evt) {
+ spin_unlock(queues[i].q_lock);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return -ENOMEM;
+ }
+ evt->sync_iu = &queues[i].cancel_rsp;
+ ibmvfc_send_event(evt, vhost, default_timeout);
+ list_add_tail(&evt->cancel, &cancelq);
+ }
+
+ spin_unlock(queues[i].q_lock);
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (list_empty(&cancelq)) {
+ if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
+ sdev_printk(KERN_INFO, sdev, "No events found to cancel\n");
+ return 0;
+ }
+
+ sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
+
+ list_for_each_entry_safe(evt, temp, &cancelq, cancel) {
+ wait_for_completion(&evt->comp);
+ status = be16_to_cpu(evt->queue->cancel_rsp.mad_common.status);
+ list_del(&evt->cancel);
+ ibmvfc_free_event(evt);
+
+ if (status != IBMVFC_MAD_SUCCESS) {
+ sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status);
+ switch (status) {
+ case IBMVFC_MAD_DRIVER_FAILED:
+ case IBMVFC_MAD_CRQ_ERROR:
+ /* Host adapter most likely going through reset, return success to
+ * the caller will wait for the command being cancelled to get returned
+ */
+ break;
+ default:
+ fail = 1;
+ break;
+ }
+ }
+ }
+
+ if (fail)
+ return -EIO;
+
+ sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n");
+ LEAVE;
+ return 0;
+}
+
+static int ibmvfc_cancel_all_sq(struct scsi_device *sdev, int type)
+{
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct ibmvfc_event *evt, *found_evt;
union ibmvfc_iu rsp;
int rsp_rc = -EBUSY;
@@ -2163,14 +2685,16 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
u16 status;
ENTER;
- spin_lock_irqsave(vhost->host->host_lock, flags);
found_evt = NULL;
- list_for_each_entry(evt, &vhost->sent, queue) {
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ spin_lock(&vhost->crq.l_lock);
+ list_for_each_entry(evt, &vhost->crq.sent, queue_list) {
if (evt->cmnd && evt->cmnd->device == sdev) {
found_evt = evt;
break;
}
}
+ spin_unlock(&vhost->crq.l_lock);
if (!found_evt) {
if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
@@ -2179,23 +2703,9 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
return 0;
}
- if (vhost->state == IBMVFC_ACTIVE) {
- evt = ibmvfc_get_event(vhost);
- ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
-
- tmf = &evt->iu.tmf;
- memset(tmf, 0, sizeof(*tmf));
- tmf->common.version = 1;
- tmf->common.opcode = IBMVFC_TMF_MAD;
- tmf->common.length = sizeof(*tmf);
- tmf->scsi_id = rport->port_id;
- int_to_scsilun(sdev->lun, &tmf->lun);
- tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
- tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->my_cancel_key = (unsigned long)starget->hostdata;
-
+ if (vhost->logged_in) {
+ evt = ibmvfc_init_tmf(&vhost->crq, sdev, type);
evt->sync_iu = &rsp;
- init_completion(&evt->comp);
rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
}
@@ -2203,20 +2713,31 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
if (rsp_rc != 0) {
sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rsp_rc);
- return -EIO;
+ /* If failure is received, the host adapter is most likely going
+ through reset, return success so the caller will wait for the command
+ being cancelled to get returned */
+ return 0;
}
sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
wait_for_completion(&evt->comp);
- status = rsp.mad_common.status;
+ status = be16_to_cpu(rsp.mad_common.status);
spin_lock_irqsave(vhost->host->host_lock, flags);
ibmvfc_free_event(evt);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
if (status != IBMVFC_MAD_SUCCESS) {
sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status);
- return -EIO;
+ switch (status) {
+ case IBMVFC_MAD_DRIVER_FAILED:
+ case IBMVFC_MAD_CRQ_ERROR:
+ /* Host adapter most likely going through reset, return success to
+ the caller will wait for the command being cancelled to get returned */
+ return 0;
+ default:
+ return -EIO;
+ };
}
sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n");
@@ -2224,6 +2745,27 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
}
/**
+ * ibmvfc_cancel_all - Cancel all outstanding commands to the device
+ * @sdev: scsi device to cancel commands
+ * @type: type of error recovery being performed
+ *
+ * This sends a cancel to the VIOS for the specified device. This does
+ * NOT send any abort to the actual device. That must be done separately.
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
+{
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
+
+ if (vhost->mq_enabled && vhost->using_channels)
+ return ibmvfc_cancel_all_mq(sdev, type);
+ else
+ return ibmvfc_cancel_all_sq(sdev, type);
+}
+
+/**
* ibmvfc_match_key - Match function for specified cancel key
* @evt: ibmvfc event struct
* @key: cancel key to match
@@ -2236,7 +2778,22 @@ static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key)
unsigned long cancel_key = (unsigned long)key;
if (evt->crq.format == IBMVFC_CMD_FORMAT &&
- evt->iu.cmd.cancel_key == cancel_key)
+ be32_to_cpu(evt->iu.cmd.cancel_key) == cancel_key)
+ return 1;
+ return 0;
+}
+
+/**
+ * ibmvfc_match_evt - Match function for specified event
+ * @evt: ibmvfc event struct
+ * @match: event to match
+ *
+ * Returns:
+ * 1 if event matches key / 0 if event does not match key
+ **/
+static int ibmvfc_match_evt(struct ibmvfc_event *evt, void *match)
+{
+ if (evt == match)
return 1;
return 0;
}
@@ -2258,19 +2815,22 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
struct ibmvfc_cmd *tmf;
struct ibmvfc_event *evt, *found_evt;
union ibmvfc_iu rsp_iu;
- struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
+ struct ibmvfc_fcp_cmd_iu *iu;
+ struct ibmvfc_fcp_rsp *fc_rsp = ibmvfc_get_fcp_rsp(vhost, &rsp_iu.cmd);
int rc, rsp_rc = -EBUSY;
unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT;
int rsp_code = 0;
- spin_lock_irqsave(vhost->host->host_lock, flags);
found_evt = NULL;
- list_for_each_entry(evt, &vhost->sent, queue) {
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ spin_lock(&vhost->crq.l_lock);
+ list_for_each_entry(evt, &vhost->crq.sent, queue_list) {
if (evt->cmnd && evt->cmnd->device == sdev) {
found_evt = evt;
break;
}
}
+ spin_unlock(&vhost->crq.l_lock);
if (!found_evt) {
if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
@@ -2280,23 +2840,23 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
}
if (vhost->state == IBMVFC_ACTIVE) {
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_event(&vhost->crq);
+ if (!evt) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return -ENOMEM;
+ }
ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
+ tmf = ibmvfc_init_vfc_cmd(evt, sdev);
+ iu = ibmvfc_get_fcp_iu(vhost, tmf);
- tmf = &evt->iu.cmd;
- memset(tmf, 0, sizeof(*tmf));
- tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
- tmf->resp.len = sizeof(tmf->rsp);
- tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
- tmf->payload_len = sizeof(tmf->iu);
- tmf->resp_len = sizeof(tmf->rsp);
- tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->tgt_scsi_id = rport->port_id;
- int_to_scsilun(sdev->lun, &tmf->iu.lun);
- tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
- tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ tmf->target_wwpn = cpu_to_be64(rport->port_name);
+ iu->tmf_flags = IBMVFC_ABORT_TASK_SET;
+ tmf->flags = cpu_to_be16((IBMVFC_NO_MEM_DESC | IBMVFC_TMF));
evt->sync_iu = &rsp_iu;
+ tmf->correlation = cpu_to_be64((u64)evt);
+
init_completion(&evt->comp);
rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
}
@@ -2312,7 +2872,7 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
timeout = wait_for_completion_timeout(&evt->comp, timeout);
if (!timeout) {
- rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ rc = ibmvfc_cancel_all(sdev, 0);
if (!rc) {
rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
if (rc == SUCCESS)
@@ -2322,13 +2882,26 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
if (rc) {
sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n");
ibmvfc_reset_host(vhost);
- rsp_rc = 0;
+ rsp_rc = -EIO;
+ rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
+
+ if (rc == SUCCESS)
+ rsp_rc = 0;
+
+ rc = ibmvfc_wait_for_ops(vhost, evt, ibmvfc_match_evt);
+ if (rc != SUCCESS) {
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_hard_reset_host(vhost);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ rsp_rc = 0;
+ }
+
goto out;
}
}
if (rsp_iu.cmd.status)
- rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+ rsp_code = ibmvfc_get_err_result(vhost, &rsp_iu.cmd);
if (rsp_code) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID)
@@ -2336,8 +2909,8 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
"flags: %x fcp_rsp: %x, scsi_status: %x\n",
- ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
- rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)),
+ be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code,
fc_rsp->scsi_status);
rsp_rc = -EIO;
} else
@@ -2355,24 +2928,30 @@ out:
* @cmd: scsi command to abort
*
* Returns:
- * SUCCESS / FAILED
+ * SUCCESS / FAST_IO_FAIL / FAILED
**/
static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct ibmvfc_host *vhost = shost_priv(sdev->host);
- int cancel_rc, abort_rc;
+ int cancel_rc, block_rc;
int rc = FAILED;
ENTER;
- fc_block_scsi_eh(cmd);
+ block_rc = fc_block_scsi_eh(cmd);
ibmvfc_wait_while_resetting(vhost);
- cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
- abort_rc = ibmvfc_abort_task_set(sdev);
+ if (block_rc != FAST_IO_FAIL) {
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ ibmvfc_abort_task_set(sdev);
+ } else
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
- if (!cancel_rc && !abort_rc)
+ if (!cancel_rc)
rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
+ if (block_rc == FAST_IO_FAIL && rc != FAILED)
+ rc = FAST_IO_FAIL;
+
LEAVE;
return rc;
}
@@ -2382,38 +2961,44 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
* @cmd: scsi command struct
*
* Returns:
- * SUCCESS / FAILED
+ * SUCCESS / FAST_IO_FAIL / FAILED
**/
static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct ibmvfc_host *vhost = shost_priv(sdev->host);
- int cancel_rc, reset_rc;
+ int cancel_rc, block_rc, reset_rc = 0;
int rc = FAILED;
ENTER;
- fc_block_scsi_eh(cmd);
+ block_rc = fc_block_scsi_eh(cmd);
ibmvfc_wait_while_resetting(vhost);
- cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
- reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
+ if (block_rc != FAST_IO_FAIL) {
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
+ } else
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
if (!cancel_rc && !reset_rc)
rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
+ if (block_rc == FAST_IO_FAIL && rc != FAILED)
+ rc = FAST_IO_FAIL;
+
LEAVE;
return rc;
}
/**
- * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
+ * ibmvfc_dev_cancel_all_noreset - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
*
**/
-static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
+static void ibmvfc_dev_cancel_all_noreset(struct scsi_device *sdev, void *data)
{
unsigned long *rc = data;
- *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
+ *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
}
/**
@@ -2421,26 +3006,49 @@ static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
* @cmd: scsi command struct
*
* Returns:
- * SUCCESS / FAILED
+ * SUCCESS / FAST_IO_FAIL / FAILED
**/
static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
{
- struct scsi_device *sdev = cmd->device;
- struct ibmvfc_host *vhost = shost_priv(sdev->host);
- struct scsi_target *starget = scsi_target(sdev);
- int reset_rc;
+ struct scsi_target *starget = scsi_target(cmd->device);
+ struct fc_rport *rport = starget_to_rport(starget);
+ struct Scsi_Host *shost = rport_to_shost(rport);
+ struct ibmvfc_host *vhost = shost_priv(shost);
+ int block_rc;
+ int reset_rc = 0;
int rc = FAILED;
unsigned long cancel_rc = 0;
+ bool tgt_reset = false;
ENTER;
- fc_block_scsi_eh(cmd);
+ block_rc = fc_block_rport(rport);
ibmvfc_wait_while_resetting(vhost);
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
- reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
+ if (block_rc != FAST_IO_FAIL) {
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, shost) {
+ if ((sdev->channel != starget->channel) ||
+ (sdev->id != starget->id))
+ continue;
+
+ cancel_rc |= ibmvfc_cancel_all(sdev,
+ IBMVFC_TMF_TGT_RESET);
+ if (!tgt_reset) {
+ reset_rc = ibmvfc_reset_device(sdev,
+ IBMVFC_TARGET_RESET, "target");
+ tgt_reset = true;
+ }
+ }
+ } else
+ starget_for_each_device(starget, &cancel_rc,
+ ibmvfc_dev_cancel_all_noreset);
if (!cancel_rc && !reset_rc)
rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
+ if (block_rc == FAST_IO_FAIL && rc != FAILED)
+ rc = FAST_IO_FAIL;
+
LEAVE;
return rc;
}
@@ -2455,9 +3063,9 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
int rc;
struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- fc_block_scsi_eh(cmd);
dev_err(vhost->dev, "Resetting connection due to error recovery\n");
rc = ibmvfc_issue_fc_host_lip(vhost->host);
+
return rc ? FAILED : SUCCESS;
}
@@ -2474,21 +3082,43 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
struct ibmvfc_host *vhost = shost_priv(shost);
struct fc_rport *dev_rport;
struct scsi_device *sdev;
- unsigned long rc;
+ struct ibmvfc_target *tgt;
+ unsigned long rc, flags;
+ unsigned int found;
ENTER;
shost_for_each_device(sdev, shost) {
dev_rport = starget_to_rport(scsi_target(sdev));
if (dev_rport != rport)
continue;
- ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
- ibmvfc_abort_task_set(sdev);
+ ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
}
rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
if (rc == FAILED)
ibmvfc_issue_fc_host_lip(shost);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ found = 0;
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->scsi_id == rport->port_id) {
+ found++;
+ break;
+ }
+ }
+
+ if (found && tgt->action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
+ /*
+ * If we get here, that means we previously attempted to send
+ * an implicit logout to the target but it failed, most likely
+ * due to I/O being pending, so we need to send it again
+ */
+ ibmvfc_del_tgt(tgt);
+ ibmvfc_reinit_host(vhost);
+ }
+
+ spin_unlock_irqrestore(shost->host_lock, flags);
LEAVE;
}
@@ -2563,14 +3193,15 @@ static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)
static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
struct ibmvfc_host *vhost)
{
- const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(crq->event);
+ const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(be64_to_cpu(crq->event));
struct ibmvfc_target *tgt;
ibmvfc_log(vhost, desc->log_level, "%s event received. scsi_id: %llx, wwpn: %llx,"
- " node_name: %llx%s\n", desc->desc, crq->scsi_id, crq->wwpn, crq->node_name,
+ " node_name: %llx%s\n", desc->desc, be64_to_cpu(crq->scsi_id),
+ be64_to_cpu(crq->wwpn), be64_to_cpu(crq->node_name),
ibmvfc_get_link_state(crq->link_state));
- switch (crq->event) {
+ switch (be64_to_cpu(crq->event)) {
case IBMVFC_AE_RESUME:
switch (crq->link_state) {
case IBMVFC_AE_LS_LINK_DOWN:
@@ -2586,7 +3217,7 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
vhost->delay_init = 1;
__ibmvfc_reset_host(vhost);
break;
- };
+ }
break;
case IBMVFC_AE_LINK_UP:
@@ -2597,8 +3228,10 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_SCN_FABRIC:
case IBMVFC_AE_SCN_DOMAIN:
vhost->events_to_log |= IBMVFC_AE_RSCN;
- vhost->delay_init = 1;
- __ibmvfc_reset_host(vhost);
+ if (vhost->state < IBMVFC_HALTED) {
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
+ }
break;
case IBMVFC_AE_SCN_NPORT:
case IBMVFC_AE_SCN_GROUP:
@@ -2611,16 +3244,16 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
list_for_each_entry(tgt, &vhost->targets, queue) {
if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
break;
- if (crq->scsi_id && tgt->scsi_id != crq->scsi_id)
+ if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
continue;
- if (crq->wwpn && tgt->ids.port_name != crq->wwpn)
+ if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
continue;
- if (crq->node_name && tgt->ids.node_name != crq->node_name)
+ if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
continue;
- if (tgt->need_login && crq->event == IBMVFC_AE_ELS_LOGO)
+ if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO)
tgt->logo_rcvd = 1;
- if (!tgt->need_login || crq->event == IBMVFC_AE_ELS_PLOGI) {
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) {
+ ibmvfc_del_tgt(tgt);
ibmvfc_reinit_host(vhost);
}
}
@@ -2638,19 +3271,21 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
default:
dev_err(vhost->dev, "Unknown async event received: %lld\n", crq->event);
break;
- };
+ }
}
/**
* ibmvfc_handle_crq - Handles and frees received events in the CRQ
* @crq: Command/Response queue
* @vhost: ibmvfc host struct
+ * @evt_doneq: Event done queue
*
- **/
-static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
+**/
+static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost,
+ struct list_head *evt_doneq)
{
long rc;
- struct ibmvfc_event *evt = (struct ibmvfc_event *)crq->ioba;
+ struct ibmvfc_event *evt = (struct ibmvfc_event *)be64_to_cpu(crq->ioba);
switch (crq->valid) {
case IBMVFC_CRQ_INIT_RSP:
@@ -2678,16 +3313,21 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
if (crq->format == IBMVFC_PARTITION_MIGRATED) {
/* We need to re-setup the interpartition connection */
- dev_info(vhost->dev, "Re-enabling adapter\n");
+ dev_info(vhost->dev, "Partition migrated, Re-enabling adapter\n");
vhost->client_migrated = 1;
+
+ scsi_block_requests(vhost->host);
ibmvfc_purge_requests(vhost, DID_REQUEUE);
- ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ ibmvfc_set_host_state(vhost, IBMVFC_LINK_DOWN);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE);
- } else {
- dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format);
+ wake_up(&vhost->work_wait_q);
+ } else if (crq->format == IBMVFC_PARTNER_FAILED || crq->format == IBMVFC_PARTNER_DEREGISTER) {
+ dev_err(vhost->dev, "Host partner adapter deregistered or failed (rc=%d)\n", crq->format);
ibmvfc_purge_requests(vhost, DID_ERROR);
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
+ } else {
+ dev_err(vhost->dev, "Received unknown transport event from partner (rc=%d)\n", crq->format);
}
return;
case IBMVFC_CRQ_CMD_RSP:
@@ -2704,22 +3344,21 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
* things we send. Make sure this response is to something we
* actually sent
*/
- if (unlikely(!ibmvfc_valid_event(&vhost->pool, evt))) {
+ if (unlikely(!ibmvfc_valid_event(&vhost->crq.evt_pool, evt))) {
dev_err(vhost->dev, "Returned correlation_token 0x%08llx is invalid!\n",
crq->ioba);
return;
}
- if (unlikely(atomic_read(&evt->free))) {
+ if (unlikely(atomic_dec_if_positive(&evt->active))) {
dev_err(vhost->dev, "Received duplicate correlation_token 0x%08llx!\n",
crq->ioba);
return;
}
- del_timer(&evt->timer);
- list_del(&evt->queue);
- ibmvfc_trc_end(evt);
- evt->done(evt);
+ spin_lock(&evt->queue->l_lock);
+ list_move_tail(&evt->queue_list, evt_doneq);
+ spin_unlock(&evt->queue->l_lock);
}
/**
@@ -2737,20 +3376,24 @@ static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
int done = 0;
spin_lock_irqsave(shost->host_lock, flags);
- if (time >= (init_timeout * HZ)) {
+ if (!vhost->scan_timeout)
+ done = 1;
+ else if (time >= (vhost->scan_timeout * HZ)) {
dev_info(vhost->dev, "Scan taking longer than %d seconds, "
- "continuing initialization\n", init_timeout);
+ "continuing initialization\n", vhost->scan_timeout);
done = 1;
}
- if (vhost->scan_complete)
+ if (vhost->scan_complete) {
+ vhost->scan_timeout = init_timeout;
done = 1;
+ }
spin_unlock_irqrestore(shost->host_lock, flags);
return done;
}
/**
- * ibmvfc_slave_alloc - Setup the device's task set value
+ * ibmvfc_sdev_init - Setup the device's task set value
* @sdev: struct scsi_device device to configure
*
* Set the device's task set value so that error handling works as
@@ -2759,7 +3402,7 @@ static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
* Returns:
* 0 on success / -ENXIO if device does not exist
**/
-static int ibmvfc_slave_alloc(struct scsi_device *sdev)
+static int ibmvfc_sdev_init(struct scsi_device *sdev)
{
struct Scsi_Host *shost = sdev->host;
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
@@ -2798,8 +3441,9 @@ static int ibmvfc_target_alloc(struct scsi_target *starget)
}
/**
- * ibmvfc_slave_configure - Configure the device
+ * ibmvfc_sdev_configure - Configure the device
* @sdev: struct scsi_device device to configure
+ * @lim: Request queue limits
*
* Enable allow_restart for a device if it is a disk. Adjust the
* queue_depth here also.
@@ -2807,20 +3451,17 @@ static int ibmvfc_target_alloc(struct scsi_target *starget)
* Returns:
* 0
**/
-static int ibmvfc_slave_configure(struct scsi_device *sdev)
+static int ibmvfc_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct Scsi_Host *shost = sdev->host;
unsigned long flags = 0;
spin_lock_irqsave(shost->host_lock, flags);
- if (sdev->type == TYPE_DISK)
+ if (sdev->type == TYPE_DISK) {
sdev->allow_restart = 1;
-
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
- scsi_activate_tcq(sdev, sdev->queue_depth);
- } else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ blk_queue_rq_timeout(sdev->request_queue, 120 * HZ);
+ }
spin_unlock_irqrestore(shost->host_lock, flags);
return 0;
}
@@ -2829,45 +3470,16 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev)
* ibmvfc_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
- * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
+static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
qdepth = IBMVFC_MAX_CMDS_PER_LUN;
- scsi_adjust_queue_depth(sdev, 0, qdepth);
- return sdev->queue_depth;
-}
-
-/**
- * ibmvfc_change_queue_type - Change the device's queue type
- * @sdev: scsi device struct
- * @tag_type: type of tags to use
- *
- * Return value:
- * actual queue type set
- **/
-static int ibmvfc_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
-
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
+ return scsi_change_queue_depth(sdev, qdepth);
}
static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
@@ -2876,8 +3488,7 @@ static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct ibmvfc_host *vhost = shost_priv(shost);
- return snprintf(buf, PAGE_SIZE, "%s\n",
- vhost->login_buf->resp.partition_name);
+ return sysfs_emit(buf, "%s\n", vhost->login_buf->resp.partition_name);
}
static ssize_t ibmvfc_show_host_device_name(struct device *dev,
@@ -2886,8 +3497,7 @@ static ssize_t ibmvfc_show_host_device_name(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct ibmvfc_host *vhost = shost_priv(shost);
- return snprintf(buf, PAGE_SIZE, "%s\n",
- vhost->login_buf->resp.device_name);
+ return sysfs_emit(buf, "%s\n", vhost->login_buf->resp.device_name);
}
static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
@@ -2896,8 +3506,7 @@ static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct ibmvfc_host *vhost = shost_priv(shost);
- return snprintf(buf, PAGE_SIZE, "%s\n",
- vhost->login_buf->resp.port_loc_code);
+ return sysfs_emit(buf, "%s\n", vhost->login_buf->resp.port_loc_code);
}
static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
@@ -2906,8 +3515,7 @@ static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct ibmvfc_host *vhost = shost_priv(shost);
- return snprintf(buf, PAGE_SIZE, "%s\n",
- vhost->login_buf->resp.drc_name);
+ return sysfs_emit(buf, "%s\n", vhost->login_buf->resp.drc_name);
}
static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
@@ -2915,7 +3523,8 @@ static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ibmvfc_host *vhost = shost_priv(shost);
- return snprintf(buf, PAGE_SIZE, "%d\n", vhost->login_buf->resp.version);
+ return sysfs_emit(buf, "%d\n",
+ be32_to_cpu(vhost->login_buf->resp.version));
}
static ssize_t ibmvfc_show_host_capabilities(struct device *dev,
@@ -2923,12 +3532,14 @@ static ssize_t ibmvfc_show_host_capabilities(struct device *dev,
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ibmvfc_host *vhost = shost_priv(shost);
- return snprintf(buf, PAGE_SIZE, "%llx\n", vhost->login_buf->resp.capabilities);
+ return sysfs_emit(buf, "%llx\n",
+ be64_to_cpu(vhost->login_buf->resp.capabilities));
}
/**
* ibmvfc_show_log_level - Show the adapter's error logging level
* @dev: class device struct
+ * @attr: unused
* @buf: buffer
*
* Return value:
@@ -2943,7 +3554,7 @@ static ssize_t ibmvfc_show_log_level(struct device *dev,
int len;
spin_lock_irqsave(shost->host_lock, flags);
- len = snprintf(buf, PAGE_SIZE, "%d\n", vhost->log_level);
+ len = sysfs_emit(buf, "%d\n", vhost->log_level);
spin_unlock_irqrestore(shost->host_lock, flags);
return len;
}
@@ -2951,7 +3562,9 @@ static ssize_t ibmvfc_show_log_level(struct device *dev,
/**
* ibmvfc_store_log_level - Change the adapter's error logging level
* @dev: class device struct
+ * @attr: unused
* @buf: buffer
+ * @count: buffer size
*
* Return value:
* number of bytes printed to buffer
@@ -2970,6 +3583,39 @@ static ssize_t ibmvfc_store_log_level(struct device *dev,
return strlen(buf);
}
+static ssize_t ibmvfc_show_scsi_channels(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ibmvfc_host *vhost = shost_priv(shost);
+ struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+ unsigned long flags = 0;
+ int len;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ len = sysfs_emit(buf, "%d\n", scsi->desired_queues);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ return len;
+}
+
+static ssize_t ibmvfc_store_scsi_channels(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ibmvfc_host *vhost = shost_priv(shost);
+ struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+ unsigned long flags = 0;
+ unsigned int channels;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ channels = simple_strtoul(buf, NULL, 10);
+ scsi->desired_queues = min(channels, shost->nr_hw_queues);
+ ibmvfc_hard_reset_host(vhost);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ return strlen(buf);
+}
+
static DEVICE_ATTR(partition_name, S_IRUGO, ibmvfc_show_host_partition_name, NULL);
static DEVICE_ATTR(device_name, S_IRUGO, ibmvfc_show_host_device_name, NULL);
static DEVICE_ATTR(port_loc_code, S_IRUGO, ibmvfc_show_host_loc_code, NULL);
@@ -2978,6 +3624,8 @@ static DEVICE_ATTR(npiv_version, S_IRUGO, ibmvfc_show_host_npiv_version, NULL);
static DEVICE_ATTR(capabilities, S_IRUGO, ibmvfc_show_host_capabilities, NULL);
static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR,
ibmvfc_show_log_level, ibmvfc_store_log_level);
+static DEVICE_ATTR(nr_scsi_channels, S_IRUGO | S_IWUSR,
+ ibmvfc_show_scsi_channels, ibmvfc_store_scsi_channels);
#ifdef CONFIG_SCSI_IBMVFC_TRACE
/**
@@ -2993,10 +3641,10 @@ static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR,
* number of bytes printed to buffer
**/
static ssize_t ibmvfc_read_trace(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct Scsi_Host *shost = class_to_shost(dev);
struct ibmvfc_host *vhost = shost_priv(shost);
unsigned long flags = 0;
@@ -3016,7 +3664,7 @@ static ssize_t ibmvfc_read_trace(struct file *filp, struct kobject *kobj,
return count;
}
-static struct bin_attribute ibmvfc_trace_attr = {
+static const struct bin_attribute ibmvfc_trace_attr = {
.attr = {
.name = "trace",
.mode = S_IRUGO,
@@ -3026,39 +3674,42 @@ static struct bin_attribute ibmvfc_trace_attr = {
};
#endif
-static struct device_attribute *ibmvfc_attrs[] = {
- &dev_attr_partition_name,
- &dev_attr_device_name,
- &dev_attr_port_loc_code,
- &dev_attr_drc_name,
- &dev_attr_npiv_version,
- &dev_attr_capabilities,
- &dev_attr_log_level,
+static struct attribute *ibmvfc_host_attrs[] = {
+ &dev_attr_partition_name.attr,
+ &dev_attr_device_name.attr,
+ &dev_attr_port_loc_code.attr,
+ &dev_attr_drc_name.attr,
+ &dev_attr_npiv_version.attr,
+ &dev_attr_capabilities.attr,
+ &dev_attr_log_level.attr,
+ &dev_attr_nr_scsi_channels.attr,
NULL
};
-static struct scsi_host_template driver_template = {
+ATTRIBUTE_GROUPS(ibmvfc_host);
+
+static const struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = "IBM POWER Virtual FC Adapter",
.proc_name = IBMVFC_NAME,
.queuecommand = ibmvfc_queuecommand,
+ .eh_timed_out = fc_eh_timed_out,
.eh_abort_handler = ibmvfc_eh_abort_handler,
.eh_device_reset_handler = ibmvfc_eh_device_reset_handler,
.eh_target_reset_handler = ibmvfc_eh_target_reset_handler,
.eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
- .slave_alloc = ibmvfc_slave_alloc,
- .slave_configure = ibmvfc_slave_configure,
+ .sdev_init = ibmvfc_sdev_init,
+ .sdev_configure = ibmvfc_sdev_configure,
.target_alloc = ibmvfc_target_alloc,
.scan_finished = ibmvfc_scan_finished,
.change_queue_depth = ibmvfc_change_queue_depth,
- .change_queue_type = ibmvfc_change_queue_type,
.cmd_per_lun = 16,
.can_queue = IBMVFC_MAX_REQUESTS_DEFAULT,
.this_id = -1,
.sg_tablesize = SG_ALL,
.max_sectors = IBMVFC_MAX_SECTORS,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = ibmvfc_attrs,
+ .shost_groups = ibmvfc_host_groups,
+ .track_queue_depth = 1,
};
/**
@@ -3070,10 +3721,10 @@ static struct scsi_host_template driver_template = {
**/
static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost)
{
- struct ibmvfc_async_crq_queue *async_crq = &vhost->async_crq;
+ struct ibmvfc_queue *async_crq = &vhost->async_crq;
struct ibmvfc_async_crq *crq;
- crq = &async_crq->msgs[async_crq->cur];
+ crq = &async_crq->msgs.async[async_crq->cur];
if (crq->valid & 0x80) {
if (++async_crq->cur == async_crq->size)
async_crq->cur = 0;
@@ -3093,10 +3744,10 @@ static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost)
**/
static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost)
{
- struct ibmvfc_crq_queue *queue = &vhost->crq;
+ struct ibmvfc_queue *queue = &vhost->crq;
struct ibmvfc_crq *crq;
- crq = &queue->msgs[queue->cur];
+ crq = &queue->msgs.crq[queue->cur];
if (crq->valid & 0x80) {
if (++queue->cur == queue->size)
queue->cur = 0;
@@ -3140,10 +3791,13 @@ static void ibmvfc_tasklet(void *data)
struct vio_dev *vdev = to_vio_dev(vhost->dev);
struct ibmvfc_crq *crq;
struct ibmvfc_async_crq *async;
+ struct ibmvfc_event *evt, *temp;
unsigned long flags;
int done = 0;
+ LIST_HEAD(evt_doneq);
spin_lock_irqsave(vhost->host->host_lock, flags);
+ spin_lock(vhost->crq.q_lock);
while (!done) {
/* Pull all the valid messages off the async CRQ */
while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
@@ -3154,7 +3808,7 @@ static void ibmvfc_tasklet(void *data)
/* Pull all the valid messages off the CRQ */
while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
- ibmvfc_handle_crq(crq, vhost);
+ ibmvfc_handle_crq(crq, vhost, &evt_doneq);
crq->valid = 0;
wmb();
}
@@ -3167,14 +3821,138 @@ static void ibmvfc_tasklet(void *data)
wmb();
} else if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
- ibmvfc_handle_crq(crq, vhost);
+ ibmvfc_handle_crq(crq, vhost, &evt_doneq);
crq->valid = 0;
wmb();
} else
done = 1;
}
+ spin_unlock(vhost->crq.q_lock);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) {
+ timer_delete(&evt->timer);
+ list_del(&evt->queue_list);
+ ibmvfc_trc_end(evt);
+ evt->done(evt);
+ }
+}
+
+static int ibmvfc_toggle_scrq_irq(struct ibmvfc_queue *scrq, int enable)
+{
+ struct device *dev = scrq->vhost->dev;
+ struct vio_dev *vdev = to_vio_dev(dev);
+ unsigned long rc;
+ int irq_action = H_ENABLE_VIO_INTERRUPT;
+
+ if (!enable)
+ irq_action = H_DISABLE_VIO_INTERRUPT;
+
+ rc = plpar_hcall_norets(H_VIOCTL, vdev->unit_address, irq_action,
+ scrq->hw_irq, 0, 0);
+
+ if (rc)
+ dev_err(dev, "Couldn't %s sub-crq[%lu] irq. rc=%ld\n",
+ enable ? "enable" : "disable", scrq->hwq_id, rc);
+
+ return rc;
+}
+
+static void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost,
+ struct list_head *evt_doneq)
+{
+ struct ibmvfc_event *evt = (struct ibmvfc_event *)be64_to_cpu(crq->ioba);
+
+ switch (crq->valid) {
+ case IBMVFC_CRQ_CMD_RSP:
+ break;
+ case IBMVFC_CRQ_XPORT_EVENT:
+ return;
+ default:
+ dev_err(vhost->dev, "Got and invalid message type 0x%02x\n", crq->valid);
+ return;
+ }
+
+ /* The only kind of payload CRQs we should get are responses to
+ * things we send. Make sure this response is to something we
+ * actually sent
+ */
+ if (unlikely(!ibmvfc_valid_event(&evt->queue->evt_pool, evt))) {
+ dev_err(vhost->dev, "Returned correlation_token 0x%08llx is invalid!\n",
+ crq->ioba);
+ return;
+ }
+
+ if (unlikely(atomic_dec_if_positive(&evt->active))) {
+ dev_err(vhost->dev, "Received duplicate correlation_token 0x%08llx!\n",
+ crq->ioba);
+ return;
+ }
+
+ spin_lock(&evt->queue->l_lock);
+ list_move_tail(&evt->queue_list, evt_doneq);
+ spin_unlock(&evt->queue->l_lock);
+}
+
+static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)
+{
+ struct ibmvfc_crq *crq;
+
+ crq = &scrq->msgs.scrq[scrq->cur].crq;
+ if (crq->valid & 0x80) {
+ if (++scrq->cur == scrq->size)
+ scrq->cur = 0;
+ rmb();
+ } else
+ crq = NULL;
+
+ return crq;
+}
+
+static void ibmvfc_drain_sub_crq(struct ibmvfc_queue *scrq)
+{
+ struct ibmvfc_crq *crq;
+ struct ibmvfc_event *evt, *temp;
+ unsigned long flags;
+ int done = 0;
+ LIST_HEAD(evt_doneq);
+
+ spin_lock_irqsave(scrq->q_lock, flags);
+ while (!done) {
+ while ((crq = ibmvfc_next_scrq(scrq)) != NULL) {
+ ibmvfc_handle_scrq(crq, scrq->vhost, &evt_doneq);
+ crq->valid = 0;
+ wmb();
+ }
+
+ ibmvfc_toggle_scrq_irq(scrq, 1);
+ if ((crq = ibmvfc_next_scrq(scrq)) != NULL) {
+ ibmvfc_toggle_scrq_irq(scrq, 0);
+ ibmvfc_handle_scrq(crq, scrq->vhost, &evt_doneq);
+ crq->valid = 0;
+ wmb();
+ } else
+ done = 1;
+ }
+ spin_unlock_irqrestore(scrq->q_lock, flags);
+
+ list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) {
+ timer_delete(&evt->timer);
+ list_del(&evt->queue_list);
+ ibmvfc_trc_end(evt);
+ evt->done(evt);
+ }
+}
+
+static irqreturn_t ibmvfc_interrupt_mq(int irq, void *scrq_instance)
+{
+ struct ibmvfc_queue *scrq = (struct ibmvfc_queue *)scrq_instance;
+
+ ibmvfc_toggle_scrq_irq(scrq, 0);
+ ibmvfc_drain_sub_crq(scrq);
+
+ return IRQ_HANDLED;
}
/**
@@ -3186,8 +3964,8 @@ static void ibmvfc_tasklet(void *data)
static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
void (*job_step) (struct ibmvfc_target *))
{
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT);
- tgt->job_step = job_step;
+ if (!ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT))
+ tgt->job_step = job_step;
wake_up(&tgt->vhost->work_wait_q);
}
@@ -3203,7 +3981,7 @@ static int ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
void (*job_step) (struct ibmvfc_target *))
{
if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
wake_up(&tgt->vhost->work_wait_q);
return 0;
} else
@@ -3256,7 +4034,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
struct ibmvfc_prli_svc_parms *parms = &rsp->parms;
- u32 status = rsp->common.status;
+ u32 status = be16_to_cpu(rsp->common.status);
int index, level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
@@ -3267,24 +4045,24 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
parms->type, parms->flags, parms->service_parms);
if (parms->type == IBMVFC_SCSI_FCP_TYPE) {
- index = ibmvfc_get_prli_rsp(parms->flags);
+ index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
if (prli_rsp[index].logged_in) {
- if (parms->flags & IBMVFC_PRLI_EST_IMG_PAIR) {
+ if (be16_to_cpu(parms->flags) & IBMVFC_PRLI_EST_IMG_PAIR) {
tgt->need_login = 0;
tgt->ids.roles = 0;
- if (parms->service_parms & IBMVFC_PRLI_TARGET_FUNC)
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_TARGET_FUNC)
tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET;
- if (parms->service_parms & IBMVFC_PRLI_INITIATOR_FUNC)
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_INITIATOR_FUNC)
tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR;
tgt->add_rport = 1;
} else
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
} else if (prli_rsp[index].retry)
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
else
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
} else
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
@@ -3293,20 +4071,21 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- if ((rsp->status & IBMVFC_VIOS_FAILURE) && rsp->error == IBMVFC_PLOGI_REQUIRED)
+ if ((be16_to_cpu(rsp->status) & IBMVFC_VIOS_FAILURE) &&
+ be16_to_cpu(rsp->error) == IBMVFC_PLOGI_REQUIRED)
level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
else if (tgt->logo_rcvd)
level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
- else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+ else if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
else
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error),
- rsp->status, rsp->error, status);
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status);
break;
- };
+ }
kref_put(&tgt->kref, ibmvfc_release_tgt);
ibmvfc_free_event(evt);
@@ -3328,20 +4107,35 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt)
return;
kref_get(&tgt->kref);
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
vhost->discovery_threads++;
ibmvfc_init_event(evt, ibmvfc_tgt_prli_done, IBMVFC_MAD_FORMAT);
evt->tgt = tgt;
prli = &evt->iu.prli;
memset(prli, 0, sizeof(*prli));
- prli->common.version = 1;
- prli->common.opcode = IBMVFC_PROCESS_LOGIN;
- prli->common.length = sizeof(*prli);
- prli->scsi_id = tgt->scsi_id;
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+ prli->common.version = cpu_to_be32(2);
+ prli->target_wwpn = cpu_to_be64(tgt->wwpn);
+ } else {
+ prli->common.version = cpu_to_be32(1);
+ }
+ prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
+ prli->common.length = cpu_to_be16(sizeof(*prli));
+ prli->scsi_id = cpu_to_be64(tgt->scsi_id);
prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
- prli->parms.flags = IBMVFC_PRLI_EST_IMG_PAIR;
- prli->parms.service_parms = IBMVFC_PRLI_INITIATOR_FUNC;
+ prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
+ prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
+ prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
+
+ if (cls3_error)
+ prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
@@ -3362,7 +4156,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
struct ibmvfc_target *tgt = evt->tgt;
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi;
- u32 status = rsp->common.status;
+ u32 status = be16_to_cpu(rsp->common.status);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
@@ -3392,17 +4186,18 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+ if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
else
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
- ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
- ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
+ ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status);
break;
- };
+ }
kref_put(&tgt->kref, ibmvfc_release_tgt);
ibmvfc_free_event(evt);
@@ -3425,17 +4220,28 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
kref_get(&tgt->kref);
tgt->logo_rcvd = 0;
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
vhost->discovery_threads++;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
ibmvfc_init_event(evt, ibmvfc_tgt_plogi_done, IBMVFC_MAD_FORMAT);
evt->tgt = tgt;
plogi = &evt->iu.plogi;
memset(plogi, 0, sizeof(*plogi));
- plogi->common.version = 1;
- plogi->common.opcode = IBMVFC_PORT_LOGIN;
- plogi->common.length = sizeof(*plogi);
- plogi->scsi_id = tgt->scsi_id;
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+ plogi->common.version = cpu_to_be32(2);
+ plogi->target_wwpn = cpu_to_be64(tgt->wwpn);
+ } else {
+ plogi->common.version = cpu_to_be32(1);
+ }
+ plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+ plogi->common.length = cpu_to_be16(sizeof(*plogi));
+ plogi->scsi_id = cpu_to_be64(tgt->scsi_id);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
vhost->discovery_threads--;
@@ -3455,7 +4261,7 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
struct ibmvfc_target *tgt = evt->tgt;
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_implicit_logout *rsp = &evt->xfer_iu->implicit_logout;
- u32 status = rsp->common.status;
+ u32 status = be16_to_cpu(rsp->common.status);
vhost->discovery_threads--;
ibmvfc_free_event(evt);
@@ -3473,42 +4279,66 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
default:
tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status);
break;
- };
+ }
- if (vhost->action == IBMVFC_HOST_ACTION_TGT_INIT)
- ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_plogi);
- else if (vhost->action == IBMVFC_HOST_ACTION_QUERY_TGTS &&
- tgt->scsi_id != tgt->new_scsi_id)
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_plogi);
kref_put(&tgt->kref, ibmvfc_release_tgt);
wake_up(&vhost->work_wait_q);
}
/**
+ * __ibmvfc_tgt_get_implicit_logout_evt - Allocate and init an event for implicit logout
+ * @tgt: ibmvfc target struct
+ * @done: Routine to call when the event is responded to
+ *
+ * Returns:
+ * Allocated and initialized ibmvfc_event struct
+ **/
+static struct ibmvfc_event *__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_target *tgt,
+ void (*done) (struct ibmvfc_event *))
+{
+ struct ibmvfc_implicit_logout *mad;
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct ibmvfc_event *evt;
+
+ kref_get(&tgt->kref);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt)
+ return NULL;
+ ibmvfc_init_event(evt, done, IBMVFC_MAD_FORMAT);
+ evt->tgt = tgt;
+ mad = &evt->iu.implicit_logout;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+ mad->old_scsi_id = cpu_to_be64(tgt->scsi_id);
+ return evt;
+}
+
+/**
* ibmvfc_tgt_implicit_logout - Initiate an Implicit Logout for specified target
* @tgt: ibmvfc target struct
*
**/
static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
{
- struct ibmvfc_implicit_logout *mad;
struct ibmvfc_host *vhost = tgt->vhost;
struct ibmvfc_event *evt;
if (vhost->discovery_threads >= disc_threads)
return;
- kref_get(&tgt->kref);
- evt = ibmvfc_get_event(vhost);
vhost->discovery_threads++;
- ibmvfc_init_event(evt, ibmvfc_tgt_implicit_logout_done, IBMVFC_MAD_FORMAT);
- evt->tgt = tgt;
- mad = &evt->iu.implicit_logout;
- memset(mad, 0, sizeof(*mad));
- mad->common.version = 1;
- mad->common.opcode = IBMVFC_IMPLICIT_LOGOUT;
- mad->common.length = sizeof(*mad);
- mad->old_scsi_id = tgt->scsi_id;
+ evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
+ ibmvfc_tgt_implicit_logout_done);
+ if (!evt) {
+ vhost->discovery_threads--;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
@@ -3520,6 +4350,164 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
}
/**
+ * ibmvfc_tgt_implicit_logout_and_del_done - Completion handler for Implicit Logout MAD
+ * @evt: ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_target *tgt = evt->tgt;
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
+ u32 status = be16_to_cpu(mad->common.status);
+
+ vhost->discovery_threads--;
+ ibmvfc_free_event(evt);
+
+ /*
+ * If our state is IBMVFC_HOST_OFFLINE, we could be unloading the
+ * driver in which case we need to free up all the targets. If we are
+ * not unloading, we will still go through a hard reset to get out of
+ * offline state, so there is no need to track the old targets in that
+ * case.
+ */
+ if (status == IBMVFC_MAD_SUCCESS || vhost->state == IBMVFC_HOST_OFFLINE)
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT);
+
+ tgt_dbg(tgt, "Implicit Logout %s\n", (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_tgt_implicit_logout_and_del - Initiate an Implicit Logout for specified target
+ * @tgt: ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
+{
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct ibmvfc_event *evt;
+
+ if (!vhost->logged_in) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ return;
+ }
+
+ if (vhost->discovery_threads >= disc_threads)
+ return;
+
+ vhost->discovery_threads++;
+ evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
+ ibmvfc_tgt_implicit_logout_and_del_done);
+
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT);
+ if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+ vhost->discovery_threads--;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ } else
+ tgt_dbg(tgt, "Sent Implicit Logout\n");
+}
+
+/**
+ * ibmvfc_tgt_move_login_done - Completion handler for Move Login
+ * @evt: ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_target *tgt = evt->tgt;
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_move_login *rsp = &evt->xfer_iu->move_login;
+ u32 status = be16_to_cpu(rsp->common.status);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ vhost->discovery_threads--;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ switch (status) {
+ case IBMVFC_MAD_SUCCESS:
+ tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id);
+ tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
+ tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
+ tgt->scsi_id = tgt->new_scsi_id;
+ tgt->ids.port_id = tgt->scsi_id;
+ memcpy(&tgt->service_parms, &rsp->service_parms,
+ sizeof(tgt->service_parms));
+ memcpy(&tgt->service_parms_change, &rsp->service_parms_change,
+ sizeof(tgt->service_parms_change));
+ ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli);
+ break;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ break;
+ case IBMVFC_MAD_CRQ_ERROR:
+ ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login);
+ break;
+ case IBMVFC_MAD_FAILED:
+ default:
+ level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login);
+
+ tgt_log(tgt, level,
+ "Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
+ tgt->new_scsi_id, be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
+ status);
+ break;
+ }
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ ibmvfc_free_event(evt);
+ wake_up(&vhost->work_wait_q);
+}
+
+
+/**
+ * ibmvfc_tgt_move_login - Initiate a move login for specified target
+ * @tgt: ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
+{
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct ibmvfc_move_login *move;
+ struct ibmvfc_event *evt;
+
+ if (vhost->discovery_threads >= disc_threads)
+ return;
+
+ kref_get(&tgt->kref);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
+ vhost->discovery_threads++;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
+ ibmvfc_init_event(evt, ibmvfc_tgt_move_login_done, IBMVFC_MAD_FORMAT);
+ evt->tgt = tgt;
+ move = &evt->iu.move_login;
+ memset(move, 0, sizeof(*move));
+ move->common.version = cpu_to_be32(1);
+ move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
+ move->common.length = cpu_to_be16(sizeof(*move));
+
+ move->old_scsi_id = cpu_to_be64(tgt->scsi_id);
+ move->new_scsi_id = cpu_to_be64(tgt->new_scsi_id);
+ move->wwpn = cpu_to_be64(tgt->wwpn);
+ move->node_name = cpu_to_be64(tgt->ids.node_name);
+
+ if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+ vhost->discovery_threads--;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ } else
+ tgt_dbg(tgt, "Sent Move Login for new scsi_id: %llX\n", tgt->new_scsi_id);
+}
+
+/**
* ibmvfc_adisc_needs_plogi - Does device need PLOGI?
* @mad: ibmvfc passthru mad struct
* @tgt: ibmvfc target struct
@@ -3530,13 +4518,11 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
static int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad,
struct ibmvfc_target *tgt)
{
- if (memcmp(&mad->fc_iu.response[2], &tgt->ids.port_name,
- sizeof(tgt->ids.port_name)))
+ if (wwn_to_u64((u8 *)&mad->fc_iu.response[2]) != tgt->ids.port_name)
return 1;
- if (memcmp(&mad->fc_iu.response[4], &tgt->ids.node_name,
- sizeof(tgt->ids.node_name)))
+ if (wwn_to_u64((u8 *)&mad->fc_iu.response[4]) != tgt->ids.node_name)
return 1;
- if (mad->fc_iu.response[6] != tgt->scsi_id)
+ if (be32_to_cpu(mad->fc_iu.response[6]) != tgt->scsi_id)
return 1;
return 0;
}
@@ -3551,33 +4537,33 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
struct ibmvfc_target *tgt = evt->tgt;
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
- u32 status = mad->common.status;
+ u32 status = be16_to_cpu(mad->common.status);
u8 fc_reason, fc_explain;
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
- del_timer(&tgt->timer);
+ timer_delete(&tgt->timer);
switch (status) {
case IBMVFC_MAD_SUCCESS:
tgt_dbg(tgt, "ADISC succeeded\n");
if (ibmvfc_adisc_needs_plogi(mad, tgt))
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
case IBMVFC_MAD_FAILED:
default:
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
- fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16;
- fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8;
+ ibmvfc_del_tgt(tgt);
+ fc_reason = (be32_to_cpu(mad->fc_iu.response[1]) & 0x00ff0000) >> 16;
+ fc_explain = (be32_to_cpu(mad->fc_iu.response[1]) & 0x0000ff00) >> 8;
tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(mad->iu.status, mad->iu.error),
- mad->iu.status, mad->iu.error,
+ ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)),
+ be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error),
ibmvfc_get_fc_type(fc_reason), fc_reason,
ibmvfc_get_ls_explain(fc_explain), fc_explain, status);
break;
- };
+ }
kref_put(&tgt->kref, ibmvfc_release_tgt);
ibmvfc_free_event(evt);
@@ -3594,22 +4580,22 @@ static void ibmvfc_init_passthru(struct ibmvfc_event *evt)
struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
memset(mad, 0, sizeof(*mad));
- mad->common.version = 1;
- mad->common.opcode = IBMVFC_PASSTHRU;
- mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
- mad->cmd_ioba.va = (u64)evt->crq.ioba +
- offsetof(struct ibmvfc_passthru_mad, iu);
- mad->cmd_ioba.len = sizeof(mad->iu);
- mad->iu.cmd_len = sizeof(mad->fc_iu.payload);
- mad->iu.rsp_len = sizeof(mad->fc_iu.response);
- mad->iu.cmd.va = (u64)evt->crq.ioba +
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_PASSTHRU);
+ mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu));
+ mad->cmd_ioba.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
+ offsetof(struct ibmvfc_passthru_mad, iu));
+ mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu));
+ mad->iu.cmd_len = cpu_to_be32(sizeof(mad->fc_iu.payload));
+ mad->iu.rsp_len = cpu_to_be32(sizeof(mad->fc_iu.response));
+ mad->iu.cmd.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
offsetof(struct ibmvfc_passthru_mad, fc_iu) +
- offsetof(struct ibmvfc_passthru_fc_iu, payload);
- mad->iu.cmd.len = sizeof(mad->fc_iu.payload);
- mad->iu.rsp.va = (u64)evt->crq.ioba +
+ offsetof(struct ibmvfc_passthru_fc_iu, payload));
+ mad->iu.cmd.len = cpu_to_be32(sizeof(mad->fc_iu.payload));
+ mad->iu.rsp.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
offsetof(struct ibmvfc_passthru_mad, fc_iu) +
- offsetof(struct ibmvfc_passthru_fc_iu, response);
- mad->iu.rsp.len = sizeof(mad->fc_iu.response);
+ offsetof(struct ibmvfc_passthru_fc_iu, response));
+ mad->iu.rsp.len = cpu_to_be32(sizeof(mad->fc_iu.response));
}
/**
@@ -3636,14 +4622,15 @@ static void ibmvfc_tgt_adisc_cancel_done(struct ibmvfc_event *evt)
/**
* ibmvfc_adisc_timeout - Handle an ADISC timeout
- * @tgt: ibmvfc target struct
+ * @t: ibmvfc target struct
*
* If an ADISC times out, send a cancel. If the cancel times
* out, reset the CRQ. When the ADISC comes back as cancelled,
* log back into the target.
**/
-static void ibmvfc_adisc_timeout(struct ibmvfc_target *tgt)
+static void ibmvfc_adisc_timeout(struct timer_list *t)
{
+ struct ibmvfc_target *tgt = timer_container_of(tgt, t, timer);
struct ibmvfc_host *vhost = tgt->vhost;
struct ibmvfc_event *evt;
struct ibmvfc_tmf *tmf;
@@ -3662,17 +4649,30 @@ static void ibmvfc_adisc_timeout(struct ibmvfc_target *tgt)
vhost->abort_threads++;
kref_get(&tgt->kref);
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ tgt_err(tgt, "Failed to get cancel event for ADISC.\n");
+ vhost->abort_threads--;
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return;
+ }
ibmvfc_init_event(evt, ibmvfc_tgt_adisc_cancel_done, IBMVFC_MAD_FORMAT);
evt->tgt = tgt;
tmf = &evt->iu.tmf;
memset(tmf, 0, sizeof(*tmf));
- tmf->common.version = 1;
- tmf->common.opcode = IBMVFC_TMF_MAD;
- tmf->common.length = sizeof(*tmf);
- tmf->scsi_id = tgt->scsi_id;
- tmf->cancel_key = tgt->cancel_key;
+ if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+ tmf->common.version = cpu_to_be32(2);
+ tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
+ } else {
+ tmf->common.version = cpu_to_be32(1);
+ }
+ tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD);
+ tmf->common.length = cpu_to_be16(sizeof(*tmf));
+ tmf->scsi_id = cpu_to_be64(tgt->scsi_id);
+ tmf->cancel_key = cpu_to_be32(tgt->cancel_key);
rc = ibmvfc_send_event(evt, vhost, default_timeout);
@@ -3707,37 +4707,41 @@ static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
return;
kref_get(&tgt->kref);
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
vhost->discovery_threads++;
ibmvfc_init_event(evt, ibmvfc_tgt_adisc_done, IBMVFC_MAD_FORMAT);
evt->tgt = tgt;
ibmvfc_init_passthru(evt);
mad = &evt->iu.passthru;
- mad->iu.flags = IBMVFC_FC_ELS;
- mad->iu.scsi_id = tgt->scsi_id;
- mad->iu.cancel_key = tgt->cancel_key;
+ mad->iu.flags = cpu_to_be32(IBMVFC_FC_ELS);
+ mad->iu.scsi_id = cpu_to_be64(tgt->scsi_id);
+ mad->iu.cancel_key = cpu_to_be32(tgt->cancel_key);
- mad->fc_iu.payload[0] = IBMVFC_ADISC;
+ mad->fc_iu.payload[0] = cpu_to_be32(IBMVFC_ADISC);
memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name,
sizeof(vhost->login_buf->resp.port_name));
memcpy(&mad->fc_iu.payload[4], &vhost->login_buf->resp.node_name,
sizeof(vhost->login_buf->resp.node_name));
- mad->fc_iu.payload[6] = vhost->login_buf->resp.scsi_id & 0x00ffffff;
+ mad->fc_iu.payload[6] = cpu_to_be32(be64_to_cpu(vhost->login_buf->resp.scsi_id) & 0x00ffffff);
if (timer_pending(&tgt->timer))
mod_timer(&tgt->timer, jiffies + (IBMVFC_ADISC_TIMEOUT * HZ));
else {
- tgt->timer.data = (unsigned long) tgt;
tgt->timer.expires = jiffies + (IBMVFC_ADISC_TIMEOUT * HZ);
- tgt->timer.function = (void (*)(unsigned long))ibmvfc_adisc_timeout;
add_timer(&tgt->timer);
}
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
if (ibmvfc_send_event(evt, vhost, IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT)) {
vhost->discovery_threads--;
- del_timer(&tgt->timer);
+ timer_delete(&tgt->timer);
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
@@ -3754,7 +4758,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
struct ibmvfc_target *tgt = evt->tgt;
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt;
- u32 status = rsp->common.status;
+ u32 status = be16_to_cpu(rsp->common.status);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
vhost->discovery_threads--;
@@ -3762,9 +4766,8 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
switch (status) {
case IBMVFC_MAD_SUCCESS:
tgt_dbg(tgt, "Query Target succeeded\n");
- tgt->new_scsi_id = rsp->scsi_id;
- if (rsp->scsi_id != tgt->scsi_id)
- ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+ if (be64_to_cpu(rsp->scsi_id) != tgt->scsi_id)
+ ibmvfc_del_tgt(tgt);
else
ibmvfc_init_tgt(tgt, ibmvfc_tgt_adisc);
break;
@@ -3775,21 +4778,23 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
break;
case IBMVFC_MAD_FAILED:
default:
- if ((rsp->status & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
- rsp->error == IBMVFC_UNABLE_TO_PERFORM_REQ &&
- rsp->fc_explain == IBMVFC_PORT_NAME_NOT_REG)
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
- else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+ if ((be16_to_cpu(rsp->status) & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
+ be16_to_cpu(rsp->error) == IBMVFC_UNABLE_TO_PERFORM_REQ &&
+ be16_to_cpu(rsp->fc_explain) == IBMVFC_PORT_NAME_NOT_REG)
+ ibmvfc_del_tgt(tgt);
+ else if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
else
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+ ibmvfc_del_tgt(tgt);
tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
- ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
- ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
+ ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain),
+ status);
break;
- };
+ }
kref_put(&tgt->kref, ibmvfc_release_tgt);
ibmvfc_free_event(evt);
@@ -3811,16 +4816,22 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
return;
kref_get(&tgt->kref);
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
vhost->discovery_threads++;
evt->tgt = tgt;
ibmvfc_init_event(evt, ibmvfc_tgt_query_target_done, IBMVFC_MAD_FORMAT);
query_tgt = &evt->iu.query_tgt;
memset(query_tgt, 0, sizeof(*query_tgt));
- query_tgt->common.version = 1;
- query_tgt->common.opcode = IBMVFC_QUERY_TARGET;
- query_tgt->common.length = sizeof(*query_tgt);
- query_tgt->wwpn = tgt->ids.port_name;
+ query_tgt->common.version = cpu_to_be32(1);
+ query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
+ query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt));
+ query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name);
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
@@ -3834,43 +4845,87 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
/**
* ibmvfc_alloc_target - Allocate and initialize an ibmvfc target
* @vhost: ibmvfc host struct
- * @scsi_id: SCSI ID to allocate target for
+ * @target: Holds SCSI ID to allocate target forand the WWPN
*
* Returns:
* 0 on success / other on failure
**/
-static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id)
+static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
+ struct ibmvfc_discover_targets_entry *target)
{
+ struct ibmvfc_target *stgt = NULL;
+ struct ibmvfc_target *wtgt = NULL;
struct ibmvfc_target *tgt;
unsigned long flags;
+ u64 scsi_id = be32_to_cpu(target->scsi_id) & IBMVFC_DISC_TGT_SCSI_ID_MASK;
+ u64 wwpn = be64_to_cpu(target->wwpn);
+ /* Look to see if we already have a target allocated for this SCSI ID or WWPN */
spin_lock_irqsave(vhost->host->host_lock, flags);
list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->wwpn == wwpn) {
+ wtgt = tgt;
+ break;
+ }
+ }
+
+ list_for_each_entry(tgt, &vhost->targets, queue) {
if (tgt->scsi_id == scsi_id) {
- if (tgt->need_login)
- ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+ stgt = tgt;
+ break;
+ }
+ }
+
+ if (wtgt && !stgt) {
+ /*
+ * A WWPN target has moved and we still are tracking the old
+ * SCSI ID. The only way we should be able to get here is if
+ * we attempted to send an implicit logout for the old SCSI ID
+ * and it failed for some reason, such as there being I/O
+ * pending to the target. In this case, we will have already
+ * deleted the rport from the FC transport so we do a move
+ * login, which works even with I/O pending, however, if
+ * there is still I/O pending, it will stay outstanding, so
+ * we only do this if fast fail is disabled for the rport,
+ * otherwise we let terminate_rport_io clean up the port
+ * before we login at the new location.
+ */
+ if (wtgt->action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
+ if (wtgt->move_login) {
+ /*
+ * Do a move login here. The old target is no longer
+ * known to the transport layer We don't use the
+ * normal ibmvfc_set_tgt_action to set this, as we
+ * don't normally want to allow this state change.
+ */
+ wtgt->new_scsi_id = scsi_id;
+ wtgt->action = IBMVFC_TGT_ACTION_INIT;
+ wtgt->init_retries = 0;
+ ibmvfc_init_tgt(wtgt, ibmvfc_tgt_move_login);
+ }
goto unlock_out;
+ } else {
+ tgt_err(wtgt, "Unexpected target state: %d, %p\n",
+ wtgt->action, wtgt->rport);
}
+ } else if (stgt) {
+ if (tgt->need_login)
+ ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+ goto unlock_out;
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
- if (!tgt) {
- dev_err(vhost->dev, "Target allocation failure for scsi id %08llx\n",
- scsi_id);
- return -ENOMEM;
- }
-
memset(tgt, 0, sizeof(*tgt));
tgt->scsi_id = scsi_id;
- tgt->new_scsi_id = scsi_id;
+ tgt->wwpn = wwpn;
tgt->vhost = vhost;
tgt->need_login = 1;
- tgt->cancel_key = vhost->task_set++;
- init_timer(&tgt->timer);
+ timer_setup(&tgt->timer, ibmvfc_adisc_timeout, 0);
kref_init(&tgt->kref);
ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
spin_lock_irqsave(vhost->host->host_lock, flags);
+ tgt->cancel_key = vhost->task_set++;
list_add_tail(&tgt->queue, &vhost->targets);
unlock_out:
@@ -3890,8 +4945,7 @@ static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
int i, rc;
for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++)
- rc = ibmvfc_alloc_target(vhost,
- vhost->disc_buf->scsi_id[i] & IBMVFC_DISC_TGT_SCSI_ID_MASK);
+ rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]);
return rc;
}
@@ -3905,19 +4959,20 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
- u32 mad_status = rsp->common.status;
+ u32 mad_status = be16_to_cpu(rsp->common.status);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
- vhost->num_targets = rsp->num_written;
+ vhost->num_targets = be32_to_cpu(rsp->num_written);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
break;
case IBMVFC_MAD_FAILED:
level += ibmvfc_retry_host_init(vhost);
ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
break;
case IBMVFC_MAD_DRIVER_FAILED:
break;
@@ -3939,17 +4994,25 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
{
struct ibmvfc_discover_targets *mad;
- struct ibmvfc_event *evt = ibmvfc_get_event(vhost);
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ if (!evt) {
+ ibmvfc_log(vhost, level, "Discover Targets failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
mad = &evt->iu.discover_targets;
memset(mad, 0, sizeof(*mad));
- mad->common.version = 1;
- mad->common.opcode = IBMVFC_DISC_TARGETS;
- mad->common.length = sizeof(*mad);
- mad->bufflen = vhost->disc_buf_sz;
- mad->buffer.va = vhost->disc_buf_dma;
- mad->buffer.len = vhost->disc_buf_sz;
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+ mad->bufflen = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
+ mad->buffer.va = cpu_to_be64(vhost->scsi_scrqs.disc_buf_dma);
+ mad->buffer.len = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
+ mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
if (!ibmvfc_send_event(evt, vhost, default_timeout))
@@ -3958,6 +5021,162 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
}
+static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf;
+ struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
+ u32 mad_status = be16_to_cpu(evt->xfer_iu->channel_setup.common.status);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+ int flags, active_queues, i;
+
+ ibmvfc_free_event(evt);
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ ibmvfc_dbg(vhost, "Channel Setup succeeded\n");
+ flags = be32_to_cpu(setup->flags);
+ vhost->do_enquiry = 0;
+ active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
+ scrqs->active_queues = active_queues;
+
+ if (flags & IBMVFC_CHANNELS_CANCELED) {
+ ibmvfc_dbg(vhost, "Channels Canceled\n");
+ vhost->using_channels = 0;
+ } else {
+ if (active_queues)
+ vhost->using_channels = 1;
+ for (i = 0; i < active_queues; i++)
+ scrqs->scrqs[i].vios_cookie =
+ be64_to_cpu(setup->channel_handles[i]);
+
+ ibmvfc_dbg(vhost, "Using %u channels\n",
+ vhost->scsi_scrqs.active_queues);
+ }
+ break;
+ case IBMVFC_MAD_FAILED:
+ level += ibmvfc_retry_host_init(vhost);
+ ibmvfc_log(vhost, level, "Channel Setup failed\n");
+ fallthrough;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ return;
+ default:
+ dev_err(vhost->dev, "Invalid Channel Setup response: 0x%x\n",
+ mad_status);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ return;
+ }
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+}
+
+static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_channel_setup_mad *mad;
+ struct ibmvfc_channel_setup *setup_buf = vhost->channel_setup_buf;
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+ struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
+ unsigned int num_channels =
+ min(scrqs->desired_queues, vhost->max_vios_scsi_channels);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+ int i;
+
+ if (!evt) {
+ ibmvfc_log(vhost, level, "Channel Setup failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
+
+ memset(setup_buf, 0, sizeof(*setup_buf));
+ if (num_channels == 0)
+ setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS);
+ else {
+ setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels);
+ for (i = 0; i < num_channels; i++)
+ setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie);
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.channel_setup;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_CHANNEL_SETUP);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+ mad->buffer.va = cpu_to_be64(vhost->channel_setup_dma);
+ mad->buffer.len = cpu_to_be32(sizeof(*vhost->channel_setup_buf));
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Sent channel setup\n");
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+}
+
+static void ibmvfc_channel_enquiry_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_channel_enquiry *rsp = &evt->xfer_iu->channel_enquiry;
+ u32 mad_status = be16_to_cpu(rsp->common.status);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ ibmvfc_dbg(vhost, "Channel Enquiry succeeded\n");
+ vhost->max_vios_scsi_channels = be32_to_cpu(rsp->num_scsi_subq_channels);
+ ibmvfc_free_event(evt);
+ break;
+ case IBMVFC_MAD_FAILED:
+ level += ibmvfc_retry_host_init(vhost);
+ ibmvfc_log(vhost, level, "Channel Enquiry failed\n");
+ fallthrough;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ ibmvfc_free_event(evt);
+ return;
+ default:
+ dev_err(vhost->dev, "Invalid Channel Enquiry response: 0x%x\n",
+ mad_status);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_free_event(evt);
+ return;
+ }
+
+ ibmvfc_channel_setup(vhost);
+}
+
+static void ibmvfc_channel_enquiry(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_channel_enquiry *mad;
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ if (!evt) {
+ ibmvfc_log(vhost, level, "Channel Enquiry failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_channel_enquiry_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.channel_enquiry;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_CHANNEL_ENQUIRY);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+
+ if (mig_channels_only)
+ mad->flags |= cpu_to_be32(IBMVFC_NO_CHANNELS_TO_CRQ_SUPPORT);
+ if (mig_no_less_channels)
+ mad->flags |= cpu_to_be32(IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT);
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Send channel enquiry\n");
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
/**
* ibmvfc_npiv_login_done - Completion handler for NPIV Login
* @evt: ibmvfc event struct
@@ -3966,7 +5185,7 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
- u32 mad_status = evt->xfer_iu->npiv_login.common.status;
+ u32 mad_status = be16_to_cpu(evt->xfer_iu->npiv_login.common.status);
struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp;
unsigned int npiv_max_sectors;
int level = IBMVFC_DEFAULT_LOG_LEVEL;
@@ -3976,16 +5195,18 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
ibmvfc_free_event(evt);
break;
case IBMVFC_MAD_FAILED:
- if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+ if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
level += ibmvfc_retry_host_init(vhost);
else
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n",
- ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
ibmvfc_free_event(evt);
return;
case IBMVFC_MAD_CRQ_ERROR:
ibmvfc_retry_host_init(vhost);
+ fallthrough;
case IBMVFC_MAD_DRIVER_FAILED:
ibmvfc_free_event(evt);
return;
@@ -3998,7 +5219,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
vhost->client_migrated = 0;
- if (!(rsp->flags & IBMVFC_NATIVE_FC)) {
+ if (!(be32_to_cpu(rsp->flags) & IBMVFC_NATIVE_FC)) {
dev_err(vhost->dev, "Virtual adapter does not support FC. %x\n",
rsp->flags);
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
@@ -4006,7 +5227,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
return;
}
- if (rsp->max_cmds <= IBMVFC_NUM_INTERNAL_REQ) {
+ if (be32_to_cpu(rsp->max_cmds) <= IBMVFC_NUM_INTERNAL_REQ) {
dev_err(vhost->dev, "Virtual adapter supported queue depth too small: %d\n",
rsp->max_cmds);
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
@@ -4015,30 +5236,36 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
}
vhost->logged_in = 1;
- npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS);
+ npiv_max_sectors = min((uint)(be64_to_cpu(rsp->max_dma_len) >> 9), max_sectors);
dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n",
rsp->partition_name, rsp->device_name, rsp->port_loc_code,
rsp->drc_name, npiv_max_sectors);
- fc_host_fabric_name(vhost->host) = rsp->node_name;
- fc_host_node_name(vhost->host) = rsp->node_name;
- fc_host_port_name(vhost->host) = rsp->port_name;
- fc_host_port_id(vhost->host) = rsp->scsi_id;
+ fc_host_fabric_name(vhost->host) = be64_to_cpu(rsp->node_name);
+ fc_host_node_name(vhost->host) = be64_to_cpu(rsp->node_name);
+ fc_host_port_name(vhost->host) = be64_to_cpu(rsp->port_name);
+ fc_host_port_id(vhost->host) = be64_to_cpu(rsp->scsi_id);
fc_host_port_type(vhost->host) = FC_PORTTYPE_NPIV;
fc_host_supported_classes(vhost->host) = 0;
- if (rsp->service_parms.class1_parms[0] & 0x80000000)
+ if (be32_to_cpu(rsp->service_parms.class1_parms[0]) & 0x80000000)
fc_host_supported_classes(vhost->host) |= FC_COS_CLASS1;
- if (rsp->service_parms.class2_parms[0] & 0x80000000)
+ if (be32_to_cpu(rsp->service_parms.class2_parms[0]) & 0x80000000)
fc_host_supported_classes(vhost->host) |= FC_COS_CLASS2;
- if (rsp->service_parms.class3_parms[0] & 0x80000000)
+ if (be32_to_cpu(rsp->service_parms.class3_parms[0]) & 0x80000000)
fc_host_supported_classes(vhost->host) |= FC_COS_CLASS3;
fc_host_maxframe_size(vhost->host) =
- rsp->service_parms.common.bb_rcv_sz & 0x0fff;
+ be16_to_cpu(rsp->service_parms.common.bb_rcv_sz) & 0x0fff;
- vhost->host->can_queue = rsp->max_cmds - IBMVFC_NUM_INTERNAL_REQ;
+ vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ;
vhost->host->max_sectors = npiv_max_sectors;
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
- wake_up(&vhost->work_wait_q);
+
+ if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) {
+ ibmvfc_channel_enquiry(vhost);
+ } else {
+ vhost->do_enquiry = 0;
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+ }
}
/**
@@ -4049,7 +5276,13 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
{
struct ibmvfc_npiv_login_mad *mad;
- struct ibmvfc_event *evt = ibmvfc_get_event(vhost);
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+ if (!evt) {
+ ibmvfc_dbg(vhost, "NPIV Login failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
ibmvfc_gather_partition_info(vhost);
ibmvfc_set_login_info(vhost);
@@ -4058,11 +5291,11 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
memcpy(vhost->login_buf, &vhost->login_info, sizeof(vhost->login_info));
mad = &evt->iu.npiv_login;
memset(mad, 0, sizeof(struct ibmvfc_npiv_login_mad));
- mad->common.version = 1;
- mad->common.opcode = IBMVFC_NPIV_LOGIN;
- mad->common.length = sizeof(struct ibmvfc_npiv_login_mad);
- mad->buffer.va = vhost->login_buf_dma;
- mad->buffer.len = sizeof(*vhost->login_buf);
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_NPIV_LOGIN);
+ mad->common.length = cpu_to_be16(sizeof(struct ibmvfc_npiv_login_mad));
+ mad->buffer.va = cpu_to_be64(vhost->login_buf_dma);
+ mad->buffer.len = cpu_to_be32(sizeof(*vhost->login_buf));
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
@@ -4070,23 +5303,23 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
ibmvfc_dbg(vhost, "Sent NPIV login\n");
else
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
-};
+}
/**
* ibmvfc_npiv_logout_done - Completion handler for NPIV Logout
- * @vhost: ibmvfc host struct
+ * @evt: ibmvfc event struct
*
**/
static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
- u32 mad_status = evt->xfer_iu->npiv_logout.common.status;
+ u32 mad_status = be16_to_cpu(evt->xfer_iu->npiv_logout.common.status);
ibmvfc_free_event(evt);
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
- if (list_empty(&vhost->sent) &&
+ if (list_empty(&vhost->crq.sent) &&
vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
ibmvfc_init_host(vhost);
return;
@@ -4114,14 +5347,20 @@ static void ibmvfc_npiv_logout(struct ibmvfc_host *vhost)
struct ibmvfc_npiv_logout_mad *mad;
struct ibmvfc_event *evt;
- evt = ibmvfc_get_event(vhost);
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ ibmvfc_dbg(vhost, "NPIV Logout failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
+
ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT);
mad = &evt->iu.npiv_logout;
memset(mad, 0, sizeof(*mad));
- mad->common.version = 1;
- mad->common.opcode = IBMVFC_NPIV_LOGOUT;
- mad->common.length = sizeof(struct ibmvfc_npiv_logout_mad);
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_NPIV_LOGOUT);
+ mad->common.length = cpu_to_be16(sizeof(struct ibmvfc_npiv_logout_mad));
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT);
@@ -4152,6 +5391,25 @@ static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
}
/**
+ * ibmvfc_dev_logo_to_do - Is there target logout work to do?
+ * @vhost: ibmvfc host struct
+ *
+ * Returns:
+ * 1 if work to do / 0 if not
+ **/
+static int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_target *tgt;
+
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
+ tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+ return 1;
+ }
+ return 0;
+}
+
+/**
* __ibmvfc_work_to_do - Is there task level work to do? (no locking)
* @vhost: ibmvfc host struct
*
@@ -4180,17 +5438,26 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 0;
return 1;
+ case IBMVFC_HOST_ACTION_TGT_DEL:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
+ if (vhost->discovery_threads == disc_threads)
+ return 0;
+ list_for_each_entry(tgt, &vhost->targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
+ return 1;
+ list_for_each_entry(tgt, &vhost->targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+ return 0;
+ return 1;
case IBMVFC_HOST_ACTION_LOGO:
case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_ALLOC_TGTS:
- case IBMVFC_HOST_ACTION_TGT_DEL:
- case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_QUERY:
case IBMVFC_HOST_ACTION_RESET:
case IBMVFC_HOST_ACTION_REENABLE:
default:
break;
- };
+ }
return 1;
}
@@ -4252,9 +5519,17 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
fc_remote_port_delete(rport);
- del_timer_sync(&tgt->timer);
+ timer_delete_sync(&tgt->timer);
kref_put(&tgt->kref, ibmvfc_release_tgt);
return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+ tgt_dbg(tgt, "Deleting rport with outstanding I/O\n");
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+ tgt->rport = NULL;
+ tgt->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ fc_remote_port_delete(rport);
+ return;
} else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
spin_unlock_irqrestore(vhost->host->host_lock, flags);
return;
@@ -4263,17 +5538,15 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
if (rport) {
tgt_dbg(tgt, "rport add succeeded\n");
tgt->rport = rport;
- rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
+ rport->maxframe_size = be16_to_cpu(tgt->service_parms.common.bb_rcv_sz) & 0x0fff;
rport->supported_classes = 0;
tgt->target_id = rport->scsi_target_id;
- if (tgt->service_parms.class1_parms[0] & 0x80000000)
+ if (be32_to_cpu(tgt->service_parms.class1_parms[0]) & 0x80000000)
rport->supported_classes |= FC_COS_CLASS1;
- if (tgt->service_parms.class2_parms[0] & 0x80000000)
+ if (be32_to_cpu(tgt->service_parms.class2_parms[0]) & 0x80000000)
rport->supported_classes |= FC_COS_CLASS2;
- if (tgt->service_parms.class3_parms[0] & 0x80000000)
+ if (be32_to_cpu(tgt->service_parms.class3_parms[0]) & 0x80000000)
rport->supported_classes |= FC_COS_CLASS3;
- if (rport->rqst_q)
- blk_queue_max_segments(rport->rqst_q, 1);
} else
tgt_dbg(tgt, "rport add failed\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4289,6 +5562,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
struct ibmvfc_target *tgt;
unsigned long flags;
struct fc_rport *rport;
+ LIST_HEAD(purge);
int rc;
ibmvfc_log_ae(vhost, vhost->events_to_log);
@@ -4300,26 +5574,49 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_INIT_WAIT:
break;
case IBMVFC_HOST_ACTION_RESET:
- vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
+ list_splice_init(&vhost->purge, &purge);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_complete_purge(&purge);
rc = ibmvfc_reset_crq(vhost);
+
spin_lock_irqsave(vhost->host->host_lock, flags);
- if (rc == H_CLOSED)
+ if (!rc || rc == H_CLOSED)
vio_enable_interrupts(to_vio_dev(vhost->dev));
- if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
- (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
- ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
- dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc);
+ if (vhost->action == IBMVFC_HOST_ACTION_RESET) {
+ /*
+ * The only action we could have changed to would have
+ * been reenable, in which case, we skip the rest of
+ * this path and wait until we've done the re-enable
+ * before sending the crq init.
+ */
+ vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
+
+ if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
+ (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc);
+ }
}
break;
case IBMVFC_HOST_ACTION_REENABLE:
- vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
+ list_splice_init(&vhost->purge, &purge);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_complete_purge(&purge);
rc = ibmvfc_reenable_crq_queue(vhost);
+
spin_lock_irqsave(vhost->host->host_lock, flags);
- if (rc || (rc = ibmvfc_send_crq_init(vhost))) {
- ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
- dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc);
+ if (vhost->action == IBMVFC_HOST_ACTION_REENABLE) {
+ /*
+ * The only action we could have changed to would have
+ * been reset, in which case, we skip the rest of this
+ * path and wait until we've done the reset before
+ * sending the crq init.
+ */
+ vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
+ if (rc || (rc = ibmvfc_send_crq_init(vhost))) {
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc);
+ }
}
break;
case IBMVFC_HOST_ACTION_LOGO:
@@ -4354,6 +5651,18 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
+ tgt->job_step(tgt);
+ break;
+ }
+ }
+
+ if (ibmvfc_dev_logo_to_do(vhost)) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return;
+ }
+
+ list_for_each_entry(tgt, &vhost->targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt_dbg(tgt, "Deleting rport\n");
rport = tgt->rport;
@@ -4363,9 +5672,31 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
if (rport)
fc_remote_port_delete(rport);
- del_timer_sync(&tgt->timer);
+ timer_delete_sync(&tgt->timer);
kref_put(&tgt->kref, ibmvfc_release_tgt);
return;
+ } else if (tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+ tgt_dbg(tgt, "Deleting rport with I/O outstanding\n");
+ rport = tgt->rport;
+ tgt->rport = NULL;
+ tgt->init_retries = 0;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+
+ /*
+ * If fast fail is enabled, we wait for it to fire and then clean up
+ * the old port, since we expect the fast fail timer to clean up the
+ * outstanding I/O faster than waiting for normal command timeouts.
+ * However, if fast fail is disabled, any I/O outstanding to the
+ * rport LUNs will stay outstanding indefinitely, since the EH handlers
+ * won't get invoked for I/O's timing out. If this is a NPIV failover
+ * scenario, the better alternative is to use the move login.
+ */
+ if (rport && rport->fast_io_fail_tmo == -1)
+ tgt->move_login = 1;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (rport)
+ fc_remote_port_delete(rport);
+ return;
}
}
@@ -4418,7 +5749,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
default:
break;
- };
+ }
spin_unlock_irqrestore(vhost->host->host_lock, flags);
}
@@ -4435,7 +5766,7 @@ static int ibmvfc_work(void *data)
struct ibmvfc_host *vhost = data;
int rc;
- set_user_nice(current, -20);
+ set_user_nice(current, MIN_NICE);
while (1) {
rc = wait_event_interruptible(vhost->work_wait_q,
@@ -4454,6 +5785,74 @@ static int ibmvfc_work(void *data)
}
/**
+ * ibmvfc_alloc_queue - Allocate queue
+ * @vhost: ibmvfc host struct
+ * @queue: ibmvfc queue to allocate
+ * @fmt: queue format to allocate
+ *
+ * Returns:
+ * 0 on success / non-zero on failure
+ **/
+static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost,
+ struct ibmvfc_queue *queue,
+ enum ibmvfc_msg_fmt fmt)
+{
+ struct device *dev = vhost->dev;
+ size_t fmt_size;
+
+ ENTER;
+ spin_lock_init(&queue->_lock);
+ queue->q_lock = &queue->_lock;
+
+ switch (fmt) {
+ case IBMVFC_CRQ_FMT:
+ fmt_size = sizeof(*queue->msgs.crq);
+ queue->total_depth = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
+ queue->evt_depth = scsi_qdepth;
+ queue->reserved_depth = IBMVFC_NUM_INTERNAL_REQ;
+ break;
+ case IBMVFC_ASYNC_FMT:
+ fmt_size = sizeof(*queue->msgs.async);
+ break;
+ case IBMVFC_SUB_CRQ_FMT:
+ fmt_size = sizeof(*queue->msgs.scrq);
+ /* We need one extra event for Cancel Commands */
+ queue->total_depth = scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ;
+ queue->evt_depth = scsi_qdepth;
+ queue->reserved_depth = IBMVFC_NUM_INTERNAL_SUBQ_REQ;
+ break;
+ default:
+ dev_warn(dev, "Unknown command/response queue message format: %d\n", fmt);
+ return -EINVAL;
+ }
+
+ queue->fmt = fmt;
+ if (ibmvfc_init_event_pool(vhost, queue)) {
+ dev_err(dev, "Couldn't initialize event pool.\n");
+ return -ENOMEM;
+ }
+
+ queue->msgs.handle = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!queue->msgs.handle)
+ return -ENOMEM;
+
+ queue->msg_token = dma_map_single(dev, queue->msgs.handle, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(dev, queue->msg_token)) {
+ free_page((unsigned long)queue->msgs.handle);
+ queue->msgs.handle = NULL;
+ return -ENOMEM;
+ }
+
+ queue->cur = 0;
+ queue->size = PAGE_SIZE / fmt_size;
+
+ queue->vhost = vhost;
+ return 0;
+}
+
+/**
* ibmvfc_init_crq - Initializes and registers CRQ with hypervisor
* @vhost: ibmvfc host struct
*
@@ -4468,21 +5867,12 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
int rc, retrc = -ENOMEM;
struct device *dev = vhost->dev;
struct vio_dev *vdev = to_vio_dev(dev);
- struct ibmvfc_crq_queue *crq = &vhost->crq;
+ struct ibmvfc_queue *crq = &vhost->crq;
ENTER;
- crq->msgs = (struct ibmvfc_crq *)get_zeroed_page(GFP_KERNEL);
-
- if (!crq->msgs)
+ if (ibmvfc_alloc_queue(vhost, crq, IBMVFC_CRQ_FMT))
return -ENOMEM;
- crq->size = PAGE_SIZE / sizeof(*crq->msgs);
- crq->msg_token = dma_map_single(dev, crq->msgs,
- PAGE_SIZE, DMA_BIDIRECTIONAL);
-
- if (dma_mapping_error(dev, crq->msg_token))
- goto map_failed;
-
retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
crq->msg_token, PAGE_SIZE);
@@ -4511,7 +5901,6 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
goto req_irq_failed;
}
- crq->cur = 0;
LEAVE;
return retrc;
@@ -4521,12 +5910,229 @@ req_irq_failed:
rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
reg_crq_failed:
- dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
-map_failed:
- free_page((unsigned long)crq->msgs);
+ ibmvfc_free_queue(vhost, crq);
return retrc;
}
+static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels,
+ int index)
+{
+ struct device *dev = vhost->dev;
+ struct vio_dev *vdev = to_vio_dev(dev);
+ struct ibmvfc_queue *scrq = &channels->scrqs[index];
+ int rc = -ENOMEM;
+
+ ENTER;
+
+ rc = h_reg_sub_crq(vdev->unit_address, scrq->msg_token, PAGE_SIZE,
+ &scrq->cookie, &scrq->hw_irq);
+
+ /* H_CLOSED indicates successful register, but no CRQ partner */
+ if (rc && rc != H_CLOSED) {
+ dev_warn(dev, "Error registering sub-crq: %d\n", rc);
+ if (rc == H_PARAMETER)
+ dev_warn_once(dev, "Firmware may not support MQ\n");
+ goto reg_failed;
+ }
+
+ scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+
+ if (!scrq->irq) {
+ rc = -EINVAL;
+ dev_err(dev, "Error mapping sub-crq[%d] irq\n", index);
+ goto irq_failed;
+ }
+
+ switch (channels->protocol) {
+ case IBMVFC_PROTO_SCSI:
+ snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-scsi%d",
+ vdev->unit_address, index);
+ scrq->handler = ibmvfc_interrupt_mq;
+ break;
+ case IBMVFC_PROTO_NVME:
+ snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-nvmf%d",
+ vdev->unit_address, index);
+ scrq->handler = ibmvfc_interrupt_mq;
+ break;
+ default:
+ dev_err(dev, "Unknown channel protocol (%d)\n",
+ channels->protocol);
+ goto irq_failed;
+ }
+
+ rc = request_irq(scrq->irq, scrq->handler, 0, scrq->name, scrq);
+
+ if (rc) {
+ dev_err(dev, "Couldn't register sub-crq[%d] irq\n", index);
+ irq_dispose_mapping(scrq->irq);
+ goto irq_failed;
+ }
+
+ scrq->hwq_id = index;
+
+ LEAVE;
+ return 0;
+
+irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_failed:
+ LEAVE;
+ return rc;
+}
+
+static void ibmvfc_deregister_channel(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels,
+ int index)
+{
+ struct device *dev = vhost->dev;
+ struct vio_dev *vdev = to_vio_dev(dev);
+ struct ibmvfc_queue *scrq = &channels->scrqs[index];
+ long rc;
+
+ ENTER;
+
+ free_irq(scrq->irq, scrq);
+ irq_dispose_mapping(scrq->irq);
+ scrq->irq = 0;
+
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address,
+ scrq->cookie);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ if (rc)
+ dev_err(dev, "Failed to free sub-crq[%d]: rc=%ld\n", index, rc);
+
+ /* Clean out the queue */
+ memset(scrq->msgs.crq, 0, PAGE_SIZE);
+ scrq->cur = 0;
+
+ LEAVE;
+}
+
+static void ibmvfc_reg_sub_crqs(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels)
+{
+ int i, j;
+
+ ENTER;
+ if (!vhost->mq_enabled || !channels->scrqs)
+ return;
+
+ for (i = 0; i < channels->max_queues; i++) {
+ if (ibmvfc_register_channel(vhost, channels, i)) {
+ for (j = i; j > 0; j--)
+ ibmvfc_deregister_channel(vhost, channels, j - 1);
+ vhost->do_enquiry = 0;
+ return;
+ }
+ }
+
+ LEAVE;
+}
+
+static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels)
+{
+ int i;
+
+ ENTER;
+ if (!vhost->mq_enabled || !channels->scrqs)
+ return;
+
+ for (i = 0; i < channels->max_queues; i++)
+ ibmvfc_deregister_channel(vhost, channels, i);
+
+ LEAVE;
+}
+
+static int ibmvfc_alloc_channels(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels)
+{
+ struct ibmvfc_queue *scrq;
+ int i, j;
+ int rc = 0;
+
+ channels->scrqs = kcalloc(channels->max_queues,
+ sizeof(*channels->scrqs),
+ GFP_KERNEL);
+ if (!channels->scrqs)
+ return -ENOMEM;
+
+ for (i = 0; i < channels->max_queues; i++) {
+ scrq = &channels->scrqs[i];
+ rc = ibmvfc_alloc_queue(vhost, scrq, IBMVFC_SUB_CRQ_FMT);
+ if (rc) {
+ for (j = i; j > 0; j--) {
+ scrq = &channels->scrqs[j - 1];
+ ibmvfc_free_queue(vhost, scrq);
+ }
+ kfree(channels->scrqs);
+ channels->scrqs = NULL;
+ channels->active_queues = 0;
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
+{
+ ENTER;
+ if (!vhost->mq_enabled)
+ return;
+
+ if (ibmvfc_alloc_channels(vhost, &vhost->scsi_scrqs)) {
+ vhost->do_enquiry = 0;
+ vhost->mq_enabled = 0;
+ return;
+ }
+
+ ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+
+ LEAVE;
+}
+
+static void ibmvfc_release_channels(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels)
+{
+ struct ibmvfc_queue *scrq;
+ int i;
+
+ if (channels->scrqs) {
+ for (i = 0; i < channels->max_queues; i++) {
+ scrq = &channels->scrqs[i];
+ ibmvfc_free_queue(vhost, scrq);
+ }
+
+ kfree(channels->scrqs);
+ channels->scrqs = NULL;
+ channels->active_queues = 0;
+ }
+}
+
+static void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost)
+{
+ ENTER;
+ if (!vhost->scsi_scrqs.scrqs)
+ return;
+
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
+
+ ibmvfc_release_channels(vhost, &vhost->scsi_scrqs);
+ LEAVE;
+}
+
+static void ibmvfc_free_disc_buf(struct device *dev, struct ibmvfc_channels *channels)
+{
+ dma_free_coherent(dev, channels->disc_buf_sz, channels->disc_buf,
+ channels->disc_buf_dma);
+}
+
/**
* ibmvfc_free_mem - Free memory for vhost
* @vhost: ibmvfc host struct
@@ -4536,22 +6142,36 @@ map_failed:
**/
static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
{
- struct ibmvfc_async_crq_queue *async_q = &vhost->async_crq;
+ struct ibmvfc_queue *async_q = &vhost->async_crq;
ENTER;
mempool_destroy(vhost->tgt_pool);
kfree(vhost->trace);
- dma_free_coherent(vhost->dev, vhost->disc_buf_sz, vhost->disc_buf,
- vhost->disc_buf_dma);
+ ibmvfc_free_disc_buf(vhost->dev, &vhost->scsi_scrqs);
dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf),
vhost->login_buf, vhost->login_buf_dma);
+ dma_free_coherent(vhost->dev, sizeof(*vhost->channel_setup_buf),
+ vhost->channel_setup_buf, vhost->channel_setup_dma);
dma_pool_destroy(vhost->sg_pool);
- dma_unmap_single(vhost->dev, async_q->msg_token,
- async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL);
- free_page((unsigned long)async_q->msgs);
+ ibmvfc_free_queue(vhost, async_q);
LEAVE;
}
+static int ibmvfc_alloc_disc_buf(struct device *dev, struct ibmvfc_channels *channels)
+{
+ channels->disc_buf_sz = sizeof(*channels->disc_buf) * max_targets;
+ channels->disc_buf = dma_alloc_coherent(dev, channels->disc_buf_sz,
+ &channels->disc_buf_dma, GFP_KERNEL);
+
+ if (!channels->disc_buf) {
+ dev_err(dev, "Couldn't allocate %s Discover Targets buffer\n",
+ (channels->protocol == IBMVFC_PROTO_SCSI) ? "SCSI" : "NVMe");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
/**
* ibmvfc_alloc_mem - Allocate memory for vhost
* @vhost: ibmvfc host struct
@@ -4561,26 +6181,15 @@ static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
**/
static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
{
- struct ibmvfc_async_crq_queue *async_q = &vhost->async_crq;
+ struct ibmvfc_queue *async_q = &vhost->async_crq;
struct device *dev = vhost->dev;
ENTER;
- async_q->msgs = (struct ibmvfc_async_crq *)get_zeroed_page(GFP_KERNEL);
- if (!async_q->msgs) {
- dev_err(dev, "Couldn't allocate async queue.\n");
+ if (ibmvfc_alloc_queue(vhost, async_q, IBMVFC_ASYNC_FMT)) {
+ dev_err(dev, "Couldn't allocate/map async queue.\n");
goto nomem;
}
- async_q->size = PAGE_SIZE / sizeof(struct ibmvfc_async_crq);
- async_q->msg_token = dma_map_single(dev, async_q->msgs,
- async_q->size * sizeof(*async_q->msgs),
- DMA_BIDIRECTIONAL);
-
- if (dma_mapping_error(dev, async_q->msg_token)) {
- dev_err(dev, "Failed to map async queue\n");
- goto free_async_crq;
- }
-
vhost->sg_pool = dma_pool_create(IBMVFC_NAME, dev,
SG_ALL * sizeof(struct srp_direct_buf),
sizeof(struct srp_direct_buf), 0);
@@ -4598,20 +6207,15 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
goto free_sg_pool;
}
- vhost->disc_buf_sz = sizeof(vhost->disc_buf->scsi_id[0]) * max_targets;
- vhost->disc_buf = dma_alloc_coherent(dev, vhost->disc_buf_sz,
- &vhost->disc_buf_dma, GFP_KERNEL);
-
- if (!vhost->disc_buf) {
- dev_err(dev, "Couldn't allocate Discover Targets buffer\n");
+ if (ibmvfc_alloc_disc_buf(dev, &vhost->scsi_scrqs))
goto free_login_buffer;
- }
vhost->trace = kcalloc(IBMVFC_NUM_TRACE_ENTRIES,
sizeof(struct ibmvfc_trace_entry), GFP_KERNEL);
+ atomic_set(&vhost->trace_index, -1);
if (!vhost->trace)
- goto free_disc_buffer;
+ goto free_scsi_disc_buffer;
vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
sizeof(struct ibmvfc_target));
@@ -4621,24 +6225,31 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
goto free_trace;
}
+ vhost->channel_setup_buf = dma_alloc_coherent(dev, sizeof(*vhost->channel_setup_buf),
+ &vhost->channel_setup_dma,
+ GFP_KERNEL);
+
+ if (!vhost->channel_setup_buf) {
+ dev_err(dev, "Couldn't allocate Channel Setup buffer\n");
+ goto free_tgt_pool;
+ }
+
LEAVE;
return 0;
+free_tgt_pool:
+ mempool_destroy(vhost->tgt_pool);
free_trace:
kfree(vhost->trace);
-free_disc_buffer:
- dma_free_coherent(dev, vhost->disc_buf_sz, vhost->disc_buf,
- vhost->disc_buf_dma);
+free_scsi_disc_buffer:
+ ibmvfc_free_disc_buf(dev, &vhost->scsi_scrqs);
free_login_buffer:
dma_free_coherent(dev, sizeof(*vhost->login_buf),
vhost->login_buf, vhost->login_buf_dma);
free_sg_pool:
dma_pool_destroy(vhost->sg_pool);
unmap_async_crq:
- dma_unmap_single(dev, async_q->msg_token,
- async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL);
-free_async_crq:
- free_page((unsigned long)async_q->msgs);
+ ibmvfc_free_queue(vhost, async_q);
nomem:
LEAVE;
return -ENOMEM;
@@ -4679,6 +6290,8 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
tgt_dbg(tgt, "Setting rport roles\n");
fc_remote_port_rolechg(rport, tgt->ids.roles);
put_device(&rport->dev);
+ } else {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
}
kref_put(&tgt->kref, ibmvfc_release_tgt);
@@ -4708,6 +6321,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
struct Scsi_Host *shost;
struct device *dev = &vdev->dev;
int rc = -ENOMEM;
+ unsigned int online_cpus = num_online_cpus();
+ unsigned int max_scsi_queues = min((unsigned int)IBMVFC_MAX_SCSI_QUEUES, online_cpus);
ENTER;
shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
@@ -4717,23 +6332,32 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
}
shost->transportt = ibmvfc_transport_template;
- shost->can_queue = max_requests;
+ shost->can_queue = scsi_qdepth;
shost->max_lun = max_lun;
shost->max_id = max_targets;
- shost->max_sectors = IBMVFC_MAX_SECTORS;
+ shost->max_sectors = max_sectors;
shost->max_cmd_len = IBMVFC_MAX_CDB_LEN;
shost->unique_id = shost->host_no;
+ shost->nr_hw_queues = mq_enabled ? min(max_scsi_queues, nr_scsi_hw_queues) : 1;
vhost = shost_priv(shost);
- INIT_LIST_HEAD(&vhost->sent);
- INIT_LIST_HEAD(&vhost->free);
INIT_LIST_HEAD(&vhost->targets);
+ INIT_LIST_HEAD(&vhost->purge);
sprintf(vhost->name, IBMVFC_NAME);
vhost->host = shost;
vhost->dev = dev;
vhost->partition_number = -1;
vhost->log_level = log_level;
vhost->task_set = 1;
+
+ vhost->mq_enabled = mq_enabled;
+ vhost->scsi_scrqs.desired_queues = min(shost->nr_hw_queues, nr_scsi_channels);
+ vhost->scsi_scrqs.max_queues = shost->nr_hw_queues;
+ vhost->scsi_scrqs.protocol = IBMVFC_PROTO_SCSI;
+ vhost->using_channels = 0;
+ vhost->do_enquiry = 1;
+ vhost->scan_timeout = 0;
+
strcpy(vhost->partition_name, "UNKNOWN");
init_waitqueue_head(&vhost->work_wait_q);
init_waitqueue_head(&vhost->init_wait_q);
@@ -4749,6 +6373,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
if (IS_ERR(vhost->work_thread)) {
dev_err(dev, "Couldn't create kernel thread: %ld\n",
PTR_ERR(vhost->work_thread));
+ rc = PTR_ERR(vhost->work_thread);
goto free_host_mem;
}
@@ -4757,13 +6382,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto kill_kthread;
}
- if ((rc = ibmvfc_init_event_pool(vhost))) {
- dev_err(dev, "Couldn't initialize event pool. rc=%d\n", rc);
- goto release_crq;
- }
-
if ((rc = scsi_add_host(shost, dev)))
- goto release_event_pool;
+ goto release_crq;
fc_host_dev_loss_tmo(shost) = IBMVFC_DEV_LOSS_TMO;
@@ -4773,8 +6393,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto remove_shost;
}
- if (shost_to_fc_host(shost)->rqst_q)
- blk_queue_max_segments(shost_to_fc_host(shost)->rqst_q, 1);
+ ibmvfc_init_sub_crqs(vhost);
+
dev_set_drvdata(dev, vhost);
spin_lock(&ibmvfc_driver_lock);
list_add_tail(&vhost->queue, &ibmvfc_head);
@@ -4786,8 +6406,6 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
remove_shost:
scsi_remove_host(shost);
-release_event_pool:
- ibmvfc_free_event_pool(vhost);
release_crq:
ibmvfc_release_crq_queue(vhost);
kill_kthread:
@@ -4808,9 +6426,10 @@ out:
* Return value:
* 0
**/
-static int ibmvfc_remove(struct vio_dev *vdev)
+static void ibmvfc_remove(struct vio_dev *vdev)
{
struct ibmvfc_host *vhost = dev_get_drvdata(&vdev->dev);
+ LIST_HEAD(purge);
unsigned long flags;
ENTER;
@@ -4821,15 +6440,17 @@ static int ibmvfc_remove(struct vio_dev *vdev)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_wait_while_resetting(vhost);
- ibmvfc_release_crq_queue(vhost);
kthread_stop(vhost->work_thread);
fc_remove_host(vhost->host);
scsi_remove_host(vhost->host);
spin_lock_irqsave(vhost->host->host_lock, flags);
ibmvfc_purge_requests(vhost, DID_ERROR);
- ibmvfc_free_event_pool(vhost);
+ list_splice_init(&vhost->purge, &purge);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_complete_purge(&purge);
+ ibmvfc_release_sub_crqs(vhost);
+ ibmvfc_release_crq_queue(vhost);
ibmvfc_free_mem(vhost);
spin_lock(&ibmvfc_driver_lock);
@@ -4837,7 +6458,6 @@ static int ibmvfc_remove(struct vio_dev *vdev)
spin_unlock(&ibmvfc_driver_lock);
scsi_host_put(vhost->host);
LEAVE;
- return 0;
}
/**
@@ -4871,17 +6491,19 @@ static int ibmvfc_resume(struct device *dev)
*/
static unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev)
{
- unsigned long pool_dma = max_requests * sizeof(union ibmvfc_iu);
+ unsigned long pool_dma;
+
+ pool_dma = (IBMVFC_MAX_SCSI_QUEUES * scsi_qdepth) * sizeof(union ibmvfc_iu);
return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun);
}
-static struct vio_device_id ibmvfc_device_table[] __devinitdata = {
+static const struct vio_device_id ibmvfc_device_table[] = {
{"fcp", "IBM,vfc-client"},
{ "", "" }
};
MODULE_DEVICE_TABLE(vio, ibmvfc_device_table);
-static struct dev_pm_ops ibmvfc_pm_ops = {
+static const struct dev_pm_ops ibmvfc_pm_ops = {
.resume = ibmvfc_resume
};
@@ -4890,11 +6512,8 @@ static struct vio_driver ibmvfc_driver = {
.probe = ibmvfc_probe,
.remove = ibmvfc_remove,
.get_desired_dma = ibmvfc_get_desired_dma,
- .driver = {
- .name = IBMVFC_NAME,
- .owner = THIS_MODULE,
- .pm = &ibmvfc_pm_ops,
- }
+ .name = IBMVFC_NAME,
+ .pm = &ibmvfc_pm_ops,
};
static struct fc_function_template ibmvfc_transport_functions = {
@@ -4930,6 +6549,7 @@ static struct fc_function_template ibmvfc_transport_functions = {
.get_starget_port_id = ibmvfc_get_starget_port_id,
.show_starget_port_id = 1,
+ .max_bsg_segments = 1,
.bsg_request = ibmvfc_bsg_request,
.bsg_timeout = ibmvfc_bsg_timeout,
};
@@ -4942,6 +6562,7 @@ static struct fc_function_template ibmvfc_transport_functions = {
**/
static int __init ibmvfc_module_init(void)
{
+ int min_max_sectors = PAGE_SIZE >> 9;
int rc;
if (!firmware_has_feature(FW_FEATURE_VIO))
@@ -4950,6 +6571,16 @@ static int __init ibmvfc_module_init(void)
printk(KERN_INFO IBMVFC_NAME": IBM Virtual Fibre Channel Driver version: %s %s\n",
IBMVFC_DRIVER_VERSION, IBMVFC_DRIVER_DATE);
+ /*
+ * Range check the max_sectors module parameter. The upper bounds is
+ * implicity checked since the parameter is a ushort.
+ */
+ if (max_sectors < min_max_sectors) {
+ printk(KERN_ERR IBMVFC_NAME ": max_sectors must be at least %d.\n",
+ min_max_sectors);
+ max_sectors = min_max_sectors;
+ }
+
ibmvfc_transport_template = fc_attach_transport(&ibmvfc_transport_functions);
if (!ibmvfc_transport_template)
return -ENOMEM;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 834c37fc7ce9..c73ed2314ad0 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1,24 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ibmvfc.h -- driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
*
* Copyright (C) IBM Corporation, 2008
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef _IBMVFC_H
@@ -26,11 +12,11 @@
#include <linux/list.h>
#include <linux/types.h>
-#include "viosrp.h"
+#include <scsi/viosrp.h>
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.9"
-#define IBMVFC_DRIVER_DATE "(August 5, 2010)"
+#define IBMVFC_DRIVER_VERSION "1.0.11"
+#define IBMVFC_DRIVER_DATE "(April 12, 2013)"
#define IBMVFC_DEFAULT_TIMEOUT 60
#define IBMVFC_ADISC_CANCEL_TIMEOUT 45
@@ -41,11 +27,12 @@
#define IBMVFC_ABORT_TIMEOUT 8
#define IBMVFC_ABORT_WAIT_TIMEOUT 40
#define IBMVFC_MAX_REQUESTS_DEFAULT 100
+#define IBMVFC_SCSI_QDEPTH 128
#define IBMVFC_DEBUG 0
#define IBMVFC_MAX_TARGETS 1024
#define IBMVFC_MAX_LUN 0xffffffff
-#define IBMVFC_MAX_SECTORS 0xffffu
+#define IBMVFC_MAX_SECTORS 2048
#define IBMVFC_MAX_DISC_THREADS 4
#define IBMVFC_TGT_MEMPOOL_SZ 64
#define IBMVFC_MAX_CMDS_PER_LUN 64
@@ -54,6 +41,13 @@
#define IBMVFC_DEV_LOSS_TMO (5 * 60)
#define IBMVFC_DEFAULT_LOG_LEVEL 2
#define IBMVFC_MAX_CDB_LEN 16
+#define IBMVFC_CLS3_ERROR 0
+#define IBMVFC_MQ 1
+#define IBMVFC_SCSI_CHANNELS 8
+#define IBMVFC_MAX_SCSI_QUEUES 16
+#define IBMVFC_SCSI_HW_QUEUES 8
+#define IBMVFC_MIG_NO_SUB_TO_CRQ 0
+#define IBMVFC_MIG_NO_N_TO_M 0
/*
* Ensure we have resources for ERP and initialization:
@@ -64,9 +58,12 @@
* 2 for each discovery thread
*/
#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + 2 + (disc_threads * 2))
+/* Reserved suset of events for cancelling channelized IO commands */
+#define IBMVFC_NUM_INTERNAL_SUBQ_REQ 4
#define IBMVFC_MAD_SUCCESS 0x00
#define IBMVFC_MAD_NOT_SUPPORTED 0xF1
+#define IBMVFC_MAD_VERSION_NOT_SUPP 0xF2
#define IBMVFC_MAD_FAILED 0xF7
#define IBMVFC_MAD_DRIVER_FAILED 0xEE
#define IBMVFC_MAD_CRQ_ERROR 0xEF
@@ -77,9 +74,14 @@ enum ibmvfc_crq_valid {
IBMVFC_CRQ_XPORT_EVENT = 0xFF,
};
-enum ibmvfc_crq_format {
+enum ibmvfc_crq_init_msg {
IBMVFC_CRQ_INIT = 0x01,
IBMVFC_CRQ_INIT_COMPLETE = 0x02,
+};
+
+enum ibmvfc_crq_xport_evts {
+ IBMVFC_PARTNER_FAILED = 0x01,
+ IBMVFC_PARTNER_DEREGISTER = 0x02,
IBMVFC_PARTITION_MIGRATED = 0x06,
};
@@ -128,126 +130,141 @@ enum ibmvfc_mad_types {
IBMVFC_PORT_LOGIN = 0x0004,
IBMVFC_PROCESS_LOGIN = 0x0008,
IBMVFC_QUERY_TARGET = 0x0010,
+ IBMVFC_MOVE_LOGIN = 0x0020,
IBMVFC_IMPLICIT_LOGOUT = 0x0040,
IBMVFC_PASSTHRU = 0x0200,
IBMVFC_TMF_MAD = 0x0100,
IBMVFC_NPIV_LOGOUT = 0x0800,
+ IBMVFC_CHANNEL_ENQUIRY = 0x1000,
+ IBMVFC_CHANNEL_SETUP = 0x2000,
+ IBMVFC_CONNECTION_INFO = 0x4000,
};
struct ibmvfc_mad_common {
- u32 version;
- u32 reserved;
- u32 opcode;
- u16 status;
- u16 length;
- u64 tag;
-}__attribute__((packed, aligned (8)));
+ __be32 version;
+ __be32 reserved;
+ __be32 opcode;
+ __be16 status;
+ __be16 length;
+ __be64 tag;
+} __packed __aligned(8);
struct ibmvfc_npiv_login_mad {
struct ibmvfc_mad_common common;
struct srp_direct_buf buffer;
-}__attribute__((packed, aligned (8)));
+} __packed __aligned(8);
struct ibmvfc_npiv_logout_mad {
struct ibmvfc_mad_common common;
-}__attribute__((packed, aligned (8)));
+} __packed __aligned(8);
#define IBMVFC_MAX_NAME 256
struct ibmvfc_npiv_login {
- u32 ostype;
+ __be32 ostype;
#define IBMVFC_OS_LINUX 0x02
- u32 pad;
- u64 max_dma_len;
- u32 max_payload;
- u32 max_response;
- u32 partition_num;
- u32 vfc_frame_version;
- u16 fcp_version;
- u16 flags;
+ __be32 pad;
+ __be64 max_dma_len;
+ __be32 max_payload;
+ __be32 max_response;
+ __be32 partition_num;
+ __be32 vfc_frame_version;
+ __be16 fcp_version;
+ __be16 flags;
#define IBMVFC_CLIENT_MIGRATED 0x01
#define IBMVFC_FLUSH_ON_HALT 0x02
- u32 max_cmds;
- u64 capabilities;
+ __be32 max_cmds;
+ __be64 capabilities;
#define IBMVFC_CAN_MIGRATE 0x01
- u64 node_name;
+#define IBMVFC_CAN_USE_CHANNELS 0x02
+#define IBMVFC_CAN_HANDLE_FPIN 0x04
+#define IBMVFC_CAN_USE_MAD_VERSION 0x08
+#define IBMVFC_CAN_SEND_VF_WWPN 0x10
+ __be64 node_name;
struct srp_direct_buf async;
u8 partition_name[IBMVFC_MAX_NAME];
u8 device_name[IBMVFC_MAX_NAME];
u8 drc_name[IBMVFC_MAX_NAME];
- u64 reserved2[2];
-}__attribute__((packed, aligned (8)));
+ __be64 reserved2[2];
+} __packed __aligned(8);
struct ibmvfc_common_svc_parms {
- u16 fcph_version;
- u16 b2b_credit;
- u16 features;
- u16 bb_rcv_sz; /* upper nibble is BB_SC_N */
- u32 ratov;
- u32 edtov;
-}__attribute__((packed, aligned (4)));
+ __be16 fcph_version;
+ __be16 b2b_credit;
+ __be16 features;
+ __be16 bb_rcv_sz; /* upper nibble is BB_SC_N */
+ __be32 ratov;
+ __be32 edtov;
+} __packed __aligned(4);
struct ibmvfc_service_parms {
struct ibmvfc_common_svc_parms common;
u8 port_name[8];
u8 node_name[8];
- u32 class1_parms[4];
- u32 class2_parms[4];
- u32 class3_parms[4];
- u32 obsolete[4];
- u32 vendor_version[4];
- u32 services_avail[2];
- u32 ext_len;
- u32 reserved[30];
- u32 clk_sync_qos[2];
-}__attribute__((packed, aligned (4)));
+ __be32 class1_parms[4];
+ __be32 class2_parms[4];
+ __be32 class3_parms[4];
+ __be32 obsolete[4];
+ __be32 vendor_version[4];
+ __be32 services_avail[2];
+ __be32 ext_len;
+ __be32 reserved[30];
+ __be32 clk_sync_qos[2];
+ __be32 reserved2;
+} __packed __aligned(4);
struct ibmvfc_npiv_login_resp {
- u32 version;
- u16 status;
- u16 error;
- u32 flags;
+ __be32 version;
+ __be16 status;
+ __be16 error;
+ __be32 flags;
#define IBMVFC_NATIVE_FC 0x01
+ __be32 reserved;
+ __be64 capabilities;
#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
- u32 reserved;
- u64 capabilities;
-#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
- u32 max_cmds;
- u32 scsi_id_sz;
- u64 max_dma_len;
- u64 scsi_id;
- u64 port_name;
- u64 node_name;
- u64 link_speed;
+#define IBMVFC_CAN_SUPPRESS_ABTS 0x10
+#define IBMVFC_MAD_VERSION_CAP 0x20
+#define IBMVFC_HANDLE_VF_WWPN 0x40
+#define IBMVFC_CAN_SUPPORT_CHANNELS 0x80
+ __be32 max_cmds;
+ __be32 scsi_id_sz;
+ __be64 max_dma_len;
+ __be64 scsi_id;
+ __be64 port_name;
+ __be64 node_name;
+ __be64 link_speed;
u8 partition_name[IBMVFC_MAX_NAME];
u8 device_name[IBMVFC_MAX_NAME];
u8 port_loc_code[IBMVFC_MAX_NAME];
u8 drc_name[IBMVFC_MAX_NAME];
struct ibmvfc_service_parms service_parms;
- u64 reserved2;
-}__attribute__((packed, aligned (8)));
+ __be64 reserved2;
+} __packed __aligned(8);
union ibmvfc_npiv_login_data {
struct ibmvfc_npiv_login login;
struct ibmvfc_npiv_login_resp resp;
-}__attribute__((packed, aligned (8)));
+} __packed __aligned(8);
-struct ibmvfc_discover_targets_buf {
- u32 scsi_id[1];
+struct ibmvfc_discover_targets_entry {
+ __be32 scsi_id;
+ __be32 pad;
+ __be64 wwpn;
#define IBMVFC_DISC_TGT_SCSI_ID_MASK 0x00ffffff
-};
+} __packed __aligned(8);
struct ibmvfc_discover_targets {
struct ibmvfc_mad_common common;
struct srp_direct_buf buffer;
- u32 flags;
- u16 status;
- u16 error;
- u32 bufflen;
- u32 num_avail;
- u32 num_written;
- u64 reserved[2];
-}__attribute__((packed, aligned (8)));
+ __be32 flags;
+#define IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST 0x02
+ __be16 status;
+ __be16 error;
+ __be32 bufflen;
+ __be32 num_avail;
+ __be32 num_written;
+ __be64 reserved[2];
+} __packed __aligned(8);
enum ibmvfc_fc_reason {
IBMVFC_INVALID_ELS_CMD_CODE = 0x01,
@@ -278,32 +295,53 @@ enum ibmvfc_gs_explain {
struct ibmvfc_port_login {
struct ibmvfc_mad_common common;
- u64 scsi_id;
- u16 reserved;
- u16 fc_service_class;
- u32 blksz;
- u32 hdr_per_blk;
- u16 status;
- u16 error; /* also fc_reason */
- u16 fc_explain;
- u16 fc_type;
- u32 reserved2;
+ __be64 scsi_id;
+ __be16 reserved;
+ __be16 fc_service_class;
+ __be32 blksz;
+ __be32 hdr_per_blk;
+ __be16 status;
+ __be16 error; /* also fc_reason */
+ __be16 fc_explain;
+ __be16 fc_type;
+ __be32 reserved2;
+ struct ibmvfc_service_parms service_parms;
+ struct ibmvfc_service_parms service_parms_change;
+ __be64 target_wwpn;
+ __be64 reserved3[2];
+} __packed __aligned(8);
+
+struct ibmvfc_move_login {
+ struct ibmvfc_mad_common common;
+ __be64 old_scsi_id;
+ __be64 new_scsi_id;
+ __be64 wwpn;
+ __be64 node_name;
+ __be32 flags;
+#define IBMVFC_MOVE_LOGIN_IMPLICIT_OLD_FAILED 0x01
+#define IBMVFC_MOVE_LOGIN_IMPLICIT_NEW_FAILED 0x02
+#define IBMVFC_MOVE_LOGIN_PORT_LOGIN_FAILED 0x04
+ __be32 reserved;
struct ibmvfc_service_parms service_parms;
struct ibmvfc_service_parms service_parms_change;
- u64 reserved3[2];
-}__attribute__((packed, aligned (8)));
+ __be32 reserved2;
+ __be16 service_class;
+ __be16 vios_flags;
+#define IBMVFC_MOVE_LOGIN_VF_NOT_SENT_ADAPTER 0x01
+ __be64 reserved3;
+} __packed __aligned(8);
struct ibmvfc_prli_svc_parms {
u8 type;
#define IBMVFC_SCSI_FCP_TYPE 0x08
u8 type_ext;
- u16 flags;
+ __be16 flags;
#define IBMVFC_PRLI_ORIG_PA_VALID 0x8000
#define IBMVFC_PRLI_RESP_PA_VALID 0x4000
#define IBMVFC_PRLI_EST_IMG_PAIR 0x2000
- u32 orig_pa;
- u32 resp_pa;
- u32 service_parms;
+ __be32 orig_pa;
+ __be32 resp_pa;
+ __be32 service_parms;
#define IBMVFC_PRLI_TASK_RETRY 0x00000200
#define IBMVFC_PRLI_RETRY 0x00000100
#define IBMVFC_PRLI_DATA_OVERLAY 0x00000040
@@ -311,51 +349,55 @@ struct ibmvfc_prli_svc_parms {
#define IBMVFC_PRLI_TARGET_FUNC 0x00000010
#define IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED 0x00000002
#define IBMVFC_PRLI_WR_FCP_XFER_RDY_DISABLED 0x00000001
-}__attribute__((packed, aligned (4)));
+} __packed __aligned(4);
struct ibmvfc_process_login {
struct ibmvfc_mad_common common;
- u64 scsi_id;
+ __be64 scsi_id;
struct ibmvfc_prli_svc_parms parms;
u8 reserved[48];
- u16 status;
- u16 error; /* also fc_reason */
- u32 reserved2;
- u64 reserved3[2];
-}__attribute__((packed, aligned (8)));
+ __be16 status;
+ __be16 error; /* also fc_reason */
+ __be32 reserved2;
+ __be64 target_wwpn;
+ __be64 reserved3[2];
+} __packed __aligned(8);
struct ibmvfc_query_tgt {
struct ibmvfc_mad_common common;
- u64 wwpn;
- u64 scsi_id;
- u16 status;
- u16 error;
- u16 fc_explain;
- u16 fc_type;
- u64 reserved[2];
-}__attribute__((packed, aligned (8)));
+ __be64 wwpn;
+ __be64 scsi_id;
+ __be16 status;
+ __be16 error;
+ __be16 fc_explain;
+ __be16 fc_type;
+ __be64 reserved[2];
+} __packed __aligned(8);
struct ibmvfc_implicit_logout {
struct ibmvfc_mad_common common;
- u64 old_scsi_id;
- u64 reserved[2];
-}__attribute__((packed, aligned (8)));
+ __be64 old_scsi_id;
+ __be64 reserved[2];
+} __packed __aligned(8);
struct ibmvfc_tmf {
struct ibmvfc_mad_common common;
- u64 scsi_id;
+ __be64 scsi_id;
struct scsi_lun lun;
- u32 flags;
+ __be32 flags;
#define IBMVFC_TMF_ABORT_TASK 0x02
#define IBMVFC_TMF_ABORT_TASK_SET 0x04
#define IBMVFC_TMF_LUN_RESET 0x10
#define IBMVFC_TMF_TGT_RESET 0x20
#define IBMVFC_TMF_LUA_VALID 0x40
- u32 cancel_key;
- u32 my_cancel_key;
- u32 pad;
- u64 reserved[2];
-}__attribute__((packed, aligned (8)));
+#define IBMVFC_TMF_SUPPRESS_ABTS 0x80
+ __be32 cancel_key;
+ __be32 my_cancel_key;
+ __be32 pad;
+ __be64 target_wwpn;
+ __be64 task_tag;
+ __be64 reserved[2];
+} __packed __aligned(8);
enum ibmvfc_fcp_rsp_info_codes {
RSP_NO_FAILURE = 0x00,
@@ -365,10 +407,10 @@ enum ibmvfc_fcp_rsp_info_codes {
};
struct ibmvfc_fcp_rsp_info {
- u16 reserved;
+ u8 reserved[3];
u8 rsp_code;
u8 reserved2[4];
-}__attribute__((packed, aligned (2)));
+} __packed __aligned(2);
enum ibmvfc_fcp_rsp_flags {
FCP_BIDI_RSP = 0x80,
@@ -384,18 +426,18 @@ enum ibmvfc_fcp_rsp_flags {
union ibmvfc_fcp_rsp_data {
struct ibmvfc_fcp_rsp_info info;
u8 sense[SCSI_SENSE_BUFFERSIZE + sizeof(struct ibmvfc_fcp_rsp_info)];
-}__attribute__((packed, aligned (8)));
+} __packed __aligned(8);
struct ibmvfc_fcp_rsp {
- u64 reserved;
- u16 retry_delay_timer;
+ __be64 reserved;
+ __be16 retry_delay_timer;
u8 flags;
u8 scsi_status;
- u32 fcp_resid;
- u32 fcp_sense_len;
- u32 fcp_rsp_len;
+ __be32 fcp_resid;
+ __be32 fcp_sense_len;
+ __be32 fcp_rsp_len;
union ibmvfc_fcp_rsp_data data;
-}__attribute__((packed, aligned (8)));
+} __packed __aligned(8);
enum ibmvfc_cmd_flags {
IBMVFC_SCATTERLIST = 0x0001,
@@ -428,70 +470,127 @@ struct ibmvfc_fcp_cmd_iu {
#define IBMVFC_RDDATA 0x02
#define IBMVFC_WRDATA 0x01
u8 cdb[IBMVFC_MAX_CDB_LEN];
- u32 xfer_len;
-}__attribute__((packed, aligned (4)));
+ __be32 xfer_len;
+} __packed __aligned(4);
struct ibmvfc_cmd {
- u64 task_tag;
- u32 frame_type;
- u32 payload_len;
- u32 resp_len;
- u32 adapter_resid;
- u16 status;
- u16 error;
- u16 flags;
- u16 response_flags;
+ __be64 task_tag;
+ __be32 frame_type;
+ __be32 payload_len;
+ __be32 resp_len;
+ __be32 adapter_resid;
+ __be16 status;
+ __be16 error;
+ __be16 flags;
+ __be16 response_flags;
#define IBMVFC_ADAPTER_RESID_VALID 0x01
- u32 cancel_key;
- u32 exchange_id;
+ __be32 cancel_key;
+ __be32 exchange_id;
struct srp_direct_buf ext_func;
struct srp_direct_buf ioba;
struct srp_direct_buf resp;
- u64 correlation;
- u64 tgt_scsi_id;
- u64 tag;
- u64 reserved3[2];
- struct ibmvfc_fcp_cmd_iu iu;
- struct ibmvfc_fcp_rsp rsp;
-}__attribute__((packed, aligned (8)));
+ __be64 correlation;
+ __be64 tgt_scsi_id;
+ __be64 tag;
+ __be64 target_wwpn;
+ __be64 reserved3;
+ union {
+ struct {
+ struct ibmvfc_fcp_cmd_iu iu;
+ struct ibmvfc_fcp_rsp rsp;
+ } v1;
+ struct {
+ __be64 reserved4;
+ struct ibmvfc_fcp_cmd_iu iu;
+ struct ibmvfc_fcp_rsp rsp;
+ } v2;
+ };
+} __packed __aligned(8);
struct ibmvfc_passthru_fc_iu {
- u32 payload[7];
+ __be32 payload[7];
#define IBMVFC_ADISC 0x52000000
- u32 response[7];
+ __be32 response[7];
};
struct ibmvfc_passthru_iu {
- u64 task_tag;
- u32 cmd_len;
- u32 rsp_len;
- u16 status;
- u16 error;
- u32 flags;
+ __be64 task_tag;
+ __be32 cmd_len;
+ __be32 rsp_len;
+ __be16 status;
+ __be16 error;
+ __be32 flags;
#define IBMVFC_FC_ELS 0x01
#define IBMVFC_FC_CT_IU 0x02
- u32 cancel_key;
+ __be32 cancel_key;
#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000
#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001
- u32 reserved;
+ __be32 reserved;
struct srp_direct_buf cmd;
struct srp_direct_buf rsp;
- u64 correlation;
- u64 scsi_id;
- u64 tag;
- u64 reserved2[2];
-}__attribute__((packed, aligned (8)));
+ __be64 correlation;
+ __be64 scsi_id;
+ __be64 tag;
+ __be64 target_wwpn;
+ __be64 reserved2[2];
+} __packed __aligned(8);
struct ibmvfc_passthru_mad {
struct ibmvfc_mad_common common;
struct srp_direct_buf cmd_ioba;
struct ibmvfc_passthru_iu iu;
struct ibmvfc_passthru_fc_iu fc_iu;
-}__attribute__((packed, aligned (8)));
+} __packed __aligned(8);
+
+struct ibmvfc_channel_enquiry {
+ struct ibmvfc_mad_common common;
+ __be32 flags;
+#define IBMVFC_NO_CHANNELS_TO_CRQ_SUPPORT 0x01
+#define IBMVFC_SUPPORT_VARIABLE_SUBQ_MSG 0x02
+#define IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT 0x04
+ __be32 num_scsi_subq_channels;
+ __be32 num_nvmeof_subq_channels;
+ __be32 num_scsi_vas_channels;
+ __be32 num_nvmeof_vas_channels;
+} __packed __aligned(8);
+
+struct ibmvfc_channel_setup_mad {
+ struct ibmvfc_mad_common common;
+ struct srp_direct_buf buffer;
+} __packed __aligned(8);
+
+#define IBMVFC_MAX_CHANNELS 502
+
+struct ibmvfc_channel_setup {
+ __be32 flags;
+#define IBMVFC_CANCEL_CHANNELS 0x01
+#define IBMVFC_USE_BUFFER 0x02
+#define IBMVFC_CHANNELS_CANCELED 0x04
+ __be32 reserved;
+ __be32 num_scsi_subq_channels;
+ __be32 num_nvmeof_subq_channels;
+ __be32 num_scsi_vas_channels;
+ __be32 num_nvmeof_vas_channels;
+ struct srp_direct_buf buffer;
+ __be64 reserved2[5];
+ __be64 channel_handles[IBMVFC_MAX_CHANNELS];
+} __packed __aligned(8);
+
+struct ibmvfc_connection_info {
+ struct ibmvfc_mad_common common;
+ __be64 information_bits;
+#define IBMVFC_NO_FC_IO_CHANNEL 0x01
+#define IBMVFC_NO_PHYP_VAS 0x02
+#define IBMVFC_NO_PHYP_SUBQ 0x04
+#define IBMVFC_PHYP_DEPRECATED_SUBQ 0x08
+#define IBMVFC_PHYP_PRESERVED_SUBQ 0x10
+#define IBMVFC_PHYP_FULL_SUBQ 0x20
+ __be64 reserved[16];
+} __packed __aligned(8);
struct ibmvfc_trace_start_entry {
u32 xfer_len;
-}__attribute__((packed));
+} __packed;
struct ibmvfc_trace_end_entry {
u16 status;
@@ -500,7 +599,7 @@ struct ibmvfc_trace_end_entry {
u8 rsp_code;
u8 scsi_status;
u8 reserved;
-}__attribute__((packed));
+} __packed;
struct ibmvfc_trace_entry {
struct ibmvfc_event *evt;
@@ -517,7 +616,7 @@ struct ibmvfc_trace_entry {
struct ibmvfc_trace_start_entry start;
struct ibmvfc_trace_end_entry end;
} u;
-}__attribute__((packed, aligned (8)));
+} __packed __aligned(8);
enum ibmvfc_crq_formats {
IBMVFC_CMD_FORMAT = 0x01,
@@ -539,6 +638,7 @@ enum ibmvfc_async_event {
IBMVFC_AE_HALT = 0x0400,
IBMVFC_AE_RESUME = 0x0800,
IBMVFC_AE_ADAPTER_FAILED = 0x1000,
+ IBMVFC_AE_FPIN = 0x2000,
};
struct ibmvfc_async_desc {
@@ -551,14 +651,13 @@ struct ibmvfc_crq {
volatile u8 valid;
volatile u8 format;
u8 reserved[6];
- volatile u64 ioba;
-}__attribute__((packed, aligned (8)));
+ volatile __be64 ioba;
+} __packed __aligned(8);
-struct ibmvfc_crq_queue {
- struct ibmvfc_crq *msgs;
- int size, cur;
- dma_addr_t msg_token;
-};
+struct ibmvfc_sub_crq {
+ struct ibmvfc_crq crq;
+ __be64 reserved[2];
+} __packed __aligned(8);
enum ibmvfc_ae_link_state {
IBMVFC_AE_LS_LINK_UP = 0x01,
@@ -567,23 +666,25 @@ enum ibmvfc_ae_link_state {
IBMVFC_AE_LS_LINK_DEAD = 0x08,
};
+enum ibmvfc_ae_fpin_status {
+ IBMVFC_AE_FPIN_LINK_CONGESTED = 0x1,
+ IBMVFC_AE_FPIN_PORT_CONGESTED = 0x2,
+ IBMVFC_AE_FPIN_PORT_CLEARED = 0x3,
+ IBMVFC_AE_FPIN_PORT_DEGRADED = 0x4,
+};
+
struct ibmvfc_async_crq {
volatile u8 valid;
u8 link_state;
- u8 pad[2];
- u32 pad2;
- volatile u64 event;
- volatile u64 scsi_id;
- volatile u64 wwpn;
- volatile u64 node_name;
- u64 reserved;
-}__attribute__((packed, aligned (8)));
-
-struct ibmvfc_async_crq_queue {
- struct ibmvfc_async_crq *msgs;
- int size, cur;
- dma_addr_t msg_token;
-};
+ u8 fpin_status;
+ u8 pad;
+ __be32 pad2;
+ volatile __be64 event;
+ volatile __be64 scsi_id;
+ volatile __be64 wwpn;
+ volatile __be64 node_name;
+ __be64 reserved;
+} __packed __aligned(8);
union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
@@ -592,25 +693,40 @@ union ibmvfc_iu {
struct ibmvfc_discover_targets discover_targets;
struct ibmvfc_port_login plogi;
struct ibmvfc_process_login prli;
+ struct ibmvfc_move_login move_login;
struct ibmvfc_query_tgt query_tgt;
struct ibmvfc_implicit_logout implicit_logout;
struct ibmvfc_tmf tmf;
struct ibmvfc_cmd cmd;
struct ibmvfc_passthru_mad passthru;
-}__attribute__((packed, aligned (8)));
+ struct ibmvfc_channel_enquiry channel_enquiry;
+ struct ibmvfc_channel_setup_mad channel_setup;
+ struct ibmvfc_connection_info connection_info;
+} __packed __aligned(8);
enum ibmvfc_target_action {
IBMVFC_TGT_ACTION_NONE = 0,
IBMVFC_TGT_ACTION_INIT,
IBMVFC_TGT_ACTION_INIT_WAIT,
+ IBMVFC_TGT_ACTION_LOGOUT_RPORT,
+ IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT,
IBMVFC_TGT_ACTION_DEL_RPORT,
IBMVFC_TGT_ACTION_DELETED_RPORT,
+ IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT,
+ IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT,
+};
+
+enum ibmvfc_protocol {
+ IBMVFC_PROTO_SCSI = 0,
+ IBMVFC_PROTO_NVME = 1,
};
struct ibmvfc_target {
struct list_head queue;
struct ibmvfc_host *vhost;
+ enum ibmvfc_protocol protocol;
u64 scsi_id;
+ u64 wwpn;
u64 new_scsi_id;
struct fc_rport *rport;
int target_id;
@@ -619,6 +735,7 @@ struct ibmvfc_target {
int add_rport;
int init_retries;
int logo_rcvd;
+ int move_login;
u32 cancel_key;
struct ibmvfc_service_parms service_parms;
struct ibmvfc_service_parms service_parms_change;
@@ -630,13 +747,17 @@ struct ibmvfc_target {
/* a unit of work for the hosting partition */
struct ibmvfc_event {
- struct list_head queue;
+ struct list_head queue_list;
+ struct list_head cancel;
struct ibmvfc_host *vhost;
+ struct ibmvfc_queue *queue;
struct ibmvfc_target *tgt;
struct scsi_cmnd *cmnd;
atomic_t free;
+ atomic_t active;
union ibmvfc_iu *xfer_iu;
- void (*done) (struct ibmvfc_event *);
+ void (*done)(struct ibmvfc_event *evt);
+ void (*_done)(struct ibmvfc_event *evt);
struct ibmvfc_crq crq;
union ibmvfc_iu iu;
union ibmvfc_iu *sync_iu;
@@ -645,6 +766,8 @@ struct ibmvfc_event {
struct completion comp;
struct completion *eh_comp;
struct timer_list timer;
+ u16 hwq;
+ u8 reserved;
};
/* a pool of event structs for use */
@@ -655,6 +778,61 @@ struct ibmvfc_event_pool {
dma_addr_t iu_token;
};
+enum ibmvfc_msg_fmt {
+ IBMVFC_CRQ_FMT = 0,
+ IBMVFC_ASYNC_FMT,
+ IBMVFC_SUB_CRQ_FMT,
+};
+
+union ibmvfc_msgs {
+ void *handle;
+ struct ibmvfc_crq *crq;
+ struct ibmvfc_async_crq *async;
+ struct ibmvfc_sub_crq *scrq;
+};
+
+struct ibmvfc_queue {
+ union ibmvfc_msgs msgs;
+ dma_addr_t msg_token;
+ enum ibmvfc_msg_fmt fmt;
+ int size, cur;
+ spinlock_t _lock;
+ spinlock_t *q_lock;
+
+ struct ibmvfc_host *vhost;
+ struct ibmvfc_event_pool evt_pool;
+ struct list_head sent;
+ struct list_head free;
+ u16 total_depth;
+ u16 evt_depth;
+ u16 reserved_depth;
+ u16 evt_free;
+ u16 reserved_free;
+ spinlock_t l_lock;
+
+ union ibmvfc_iu cancel_rsp;
+
+ /* Sub-CRQ fields */
+ unsigned long cookie;
+ unsigned long vios_cookie;
+ unsigned long hw_irq;
+ unsigned long irq;
+ unsigned long hwq_id;
+ char name[32];
+ irq_handler_t handler;
+};
+
+struct ibmvfc_channels {
+ struct ibmvfc_queue *scrqs;
+ enum ibmvfc_protocol protocol;
+ unsigned int active_queues;
+ unsigned int desired_queues;
+ unsigned int max_queues;
+ int disc_buf_sz;
+ struct ibmvfc_discover_targets_entry *disc_buf;
+ dma_addr_t disc_buf_dma;
+};
+
enum ibmvfc_host_action {
IBMVFC_HOST_ACTION_NONE = 0,
IBMVFC_HOST_ACTION_RESET,
@@ -689,41 +867,45 @@ struct ibmvfc_host {
enum ibmvfc_host_action action;
#define IBMVFC_NUM_TRACE_INDEX_BITS 8
#define IBMVFC_NUM_TRACE_ENTRIES (1 << IBMVFC_NUM_TRACE_INDEX_BITS)
+#define IBMVFC_TRACE_INDEX_MASK (IBMVFC_NUM_TRACE_ENTRIES - 1)
#define IBMVFC_TRACE_SIZE (sizeof(struct ibmvfc_trace_entry) * IBMVFC_NUM_TRACE_ENTRIES)
struct ibmvfc_trace_entry *trace;
- u32 trace_index:IBMVFC_NUM_TRACE_INDEX_BITS;
+ atomic_t trace_index;
int num_targets;
struct list_head targets;
- struct list_head sent;
- struct list_head free;
+ struct list_head purge;
struct device *dev;
- struct ibmvfc_event_pool pool;
struct dma_pool *sg_pool;
mempool_t *tgt_pool;
- struct ibmvfc_crq_queue crq;
- struct ibmvfc_async_crq_queue async_crq;
+ struct ibmvfc_queue crq;
+ struct ibmvfc_queue async_crq;
+ struct ibmvfc_channels scsi_scrqs;
struct ibmvfc_npiv_login login_info;
union ibmvfc_npiv_login_data *login_buf;
dma_addr_t login_buf_dma;
- int disc_buf_sz;
+ struct ibmvfc_channel_setup *channel_setup_buf;
+ dma_addr_t channel_setup_dma;
int log_level;
- struct ibmvfc_discover_targets_buf *disc_buf;
struct mutex passthru_mutex;
+ unsigned int max_vios_scsi_channels;
int task_set;
int init_retries;
int discovery_threads;
int abort_threads;
- int client_migrated;
- int reinit;
- int delay_init;
- int scan_complete;
- int logged_in;
- int aborting_passthru;
+ unsigned int client_migrated:1;
+ unsigned int reinit:1;
+ unsigned int delay_init:1;
+ unsigned int logged_in:1;
+ unsigned int mq_enabled:1;
+ unsigned int using_channels:1;
+ unsigned int do_enquiry:1;
+ unsigned int aborting_passthru:1;
+ unsigned int scan_complete:1;
+ int scan_timeout;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002
#define IBMVFC_AE_RSCN 0x0004
- dma_addr_t disc_buf_dma;
unsigned int partition_number;
char partition_name[97];
void (*job_step) (struct ibmvfc_host *);
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index 3d391dc3f11f..3d65a498b701 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* ------------------------------------------------------------
* ibmvscsi.c
* (C) Copyright IBM Corporation 1994, 2004
@@ -5,21 +6,6 @@
* Santiago Leon (santil@us.ibm.com)
* Dave Boutcher (sleddog@us.ibm.com)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
* ------------------------------------------------------------
* Emulation of a SCSI host adapter for Virtual I/O devices
*
@@ -55,13 +41,7 @@
* and sends a CRQ message back to inform the client that the request has
* completed.
*
- * Note that some of the underlying infrastructure is different between
- * machines conforming to the "RS/6000 Platform Architecture" (RPA) and
- * the older iSeries hypervisor models. To support both, some low level
- * routines have been broken out into rpa_vscsi.c and iseries_vscsi.c.
- * The Makefile should pick one, not two, not zero, of these.
- *
- * TODO: This is currently pretty tied to the IBM i/pSeries hypervisor
+ * TODO: This is currently pretty tied to the IBM pSeries hypervisor
* interfaces. It would be really nice to abstract this above an RDMA
* layer.
*/
@@ -99,22 +79,24 @@ static int max_requests = IBMVSCSI_MAX_REQUESTS_DEFAULT;
static int max_events = IBMVSCSI_MAX_REQUESTS_DEFAULT + 2;
static int fast_fail = 1;
static int client_reserve = 1;
+static char partition_name[96] = "UNKNOWN";
+static unsigned int partition_number = -1;
+static LIST_HEAD(ibmvscsi_head);
+static DEFINE_SPINLOCK(ibmvscsi_driver_lock);
static struct scsi_transport_template *ibmvscsi_transport_template;
#define IBMVSCSI_VERSION "1.5.9"
-static struct ibmvscsi_ops *ibmvscsi_ops;
-
MODULE_DESCRIPTION("IBM Virtual SCSI");
MODULE_AUTHOR("Dave Boutcher");
MODULE_LICENSE("GPL");
MODULE_VERSION(IBMVSCSI_VERSION);
module_param_named(max_id, max_id, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(max_id, "Largest ID value for each channel");
+MODULE_PARM_DESC(max_id, "Largest ID value for each channel [Default=64]");
module_param_named(max_channel, max_channel, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(max_channel, "Largest channel value");
+MODULE_PARM_DESC(max_channel, "Largest channel value [Default=3]");
module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds");
module_param_named(max_requests, max_requests, int, S_IRUGO);
@@ -124,6 +106,329 @@ MODULE_PARM_DESC(fast_fail, "Enable fast fail. [Default=1]");
module_param_named(client_reserve, client_reserve, int, S_IRUGO );
MODULE_PARM_DESC(client_reserve, "Attempt client managed reserve/release");
+static void ibmvscsi_handle_crq(struct viosrp_crq *crq,
+ struct ibmvscsi_host_data *hostdata);
+
+/* ------------------------------------------------------------
+ * Routines for managing the command/response queue
+ */
+/**
+ * ibmvscsi_handle_event: - Interrupt handler for crq events
+ * @irq: number of irq to handle, not used
+ * @dev_instance: ibmvscsi_host_data of host that received interrupt
+ *
+ * Disables interrupts and schedules srp_task
+ * Always returns IRQ_HANDLED
+ */
+static irqreturn_t ibmvscsi_handle_event(int irq, void *dev_instance)
+{
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)dev_instance;
+ vio_disable_interrupts(to_vio_dev(hostdata->dev));
+ tasklet_schedule(&hostdata->srp_task);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ibmvscsi_release_crq_queue() - Deallocates data and unregisters CRQ
+ * @queue: crq_queue to initialize and register
+ * @hostdata: ibmvscsi_host_data of host
+ * @max_requests: maximum requests (unused)
+ *
+ * Frees irq, deallocates a page for messages, unmaps dma, and unregisters
+ * the crq with the hypervisor.
+ */
+static void ibmvscsi_release_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests)
+{
+ long rc = 0;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+ free_irq(vdev->irq, (void *)hostdata);
+ tasklet_kill(&hostdata->srp_task);
+ do {
+ if (rc)
+ msleep(100);
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
+ dma_unmap_single(hostdata->dev,
+ queue->msg_token,
+ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+ free_page((unsigned long)queue->msgs);
+}
+
+/**
+ * crq_queue_next_crq: - Returns the next entry in message queue
+ * @queue: crq_queue to use
+ *
+ * Returns pointer to next entry in queue, or NULL if there are no new
+ * entried in the CRQ.
+ */
+static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue)
+{
+ struct viosrp_crq *crq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ crq = &queue->msgs[queue->cur];
+ if (crq->valid != VIOSRP_CRQ_FREE) {
+ if (++queue->cur == queue->size)
+ queue->cur = 0;
+
+ /* Ensure the read of the valid bit occurs before reading any
+ * other bits of the CRQ entry
+ */
+ rmb();
+ } else
+ crq = NULL;
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ return crq;
+}
+
+/**
+ * ibmvscsi_send_crq: - Send a CRQ
+ * @hostdata: the adapter
+ * @word1: the first 64 bits of the data
+ * @word2: the second 64 bits of the data
+ */
+static int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata,
+ u64 word1, u64 word2)
+{
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+
+ /*
+ * Ensure the command buffer is flushed to memory before handing it
+ * over to the VIOS to prevent it from fetching any stale data.
+ */
+ mb();
+ return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
+}
+
+/**
+ * ibmvscsi_task: - Process srps asynchronously
+ * @data: ibmvscsi_host_data of host
+ */
+static void ibmvscsi_task(void *data)
+{
+ struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)data;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+ struct viosrp_crq *crq;
+ int done = 0;
+
+ while (!done) {
+ /* Pull all the valid messages off the CRQ */
+ while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) {
+ ibmvscsi_handle_crq(crq, hostdata);
+ crq->valid = VIOSRP_CRQ_FREE;
+ wmb();
+ }
+
+ vio_enable_interrupts(vdev);
+ crq = crq_queue_next_crq(&hostdata->queue);
+ if (crq != NULL) {
+ vio_disable_interrupts(vdev);
+ ibmvscsi_handle_crq(crq, hostdata);
+ crq->valid = VIOSRP_CRQ_FREE;
+ wmb();
+ } else {
+ done = 1;
+ }
+ }
+}
+
+static void gather_partition_info(void)
+{
+ const char *ppartition_name;
+ const __be32 *p_number_ptr;
+
+ /* Retrieve information about this partition */
+ if (!of_root)
+ return;
+
+ of_node_get(of_root);
+
+ ppartition_name = of_get_property(of_root, "ibm,partition-name", NULL);
+ if (ppartition_name)
+ strscpy(partition_name, ppartition_name,
+ sizeof(partition_name));
+ p_number_ptr = of_get_property(of_root, "ibm,partition-no", NULL);
+ if (p_number_ptr)
+ partition_number = of_read_number(p_number_ptr, 1);
+ of_node_put(of_root);
+}
+
+static void set_adapter_info(struct ibmvscsi_host_data *hostdata)
+{
+ memset(&hostdata->madapter_info, 0x00,
+ sizeof(hostdata->madapter_info));
+
+ dev_info(hostdata->dev, "SRP_VERSION: %s\n", SRP_VERSION);
+ strcpy(hostdata->madapter_info.srp_version, SRP_VERSION);
+
+ strscpy(hostdata->madapter_info.partition_name, partition_name,
+ sizeof(hostdata->madapter_info.partition_name));
+
+ hostdata->madapter_info.partition_number =
+ cpu_to_be32(partition_number);
+
+ hostdata->madapter_info.mad_version = cpu_to_be32(SRP_MAD_VERSION_1);
+ hostdata->madapter_info.os_type = cpu_to_be32(SRP_MAD_OS_LINUX);
+}
+
+/**
+ * ibmvscsi_reset_crq_queue() - resets a crq after a failure
+ * @queue: crq_queue to initialize and register
+ * @hostdata: ibmvscsi_host_data of host
+ */
+static int ibmvscsi_reset_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata)
+{
+ int rc = 0;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+
+ /* Close the CRQ */
+ do {
+ if (rc)
+ msleep(100);
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
+
+ /* Clean out the queue */
+ memset(queue->msgs, 0x00, PAGE_SIZE);
+ queue->cur = 0;
+
+ set_adapter_info(hostdata);
+
+ /* And re-open it again */
+ rc = plpar_hcall_norets(H_REG_CRQ,
+ vdev->unit_address,
+ queue->msg_token, PAGE_SIZE);
+ if (rc == H_CLOSED) {
+ /* Adapter is good, but other end is not ready */
+ dev_warn(hostdata->dev, "Partner adapter not ready\n");
+ } else if (rc != 0) {
+ dev_warn(hostdata->dev, "couldn't register crq--rc 0x%x\n", rc);
+ }
+ return rc;
+}
+
+/**
+ * ibmvscsi_init_crq_queue() - Initializes and registers CRQ with hypervisor
+ * @queue: crq_queue to initialize and register
+ * @hostdata: ibmvscsi_host_data of host
+ * @max_requests: maximum requests (unused)
+ *
+ * Allocates a page for messages, maps it for dma, and registers
+ * the crq with the hypervisor.
+ * Returns zero on success.
+ */
+static int ibmvscsi_init_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests)
+{
+ int rc;
+ int retrc;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+
+ queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
+
+ if (!queue->msgs)
+ goto malloc_failed;
+ queue->size = PAGE_SIZE / sizeof(*queue->msgs);
+
+ queue->msg_token = dma_map_single(hostdata->dev, queue->msgs,
+ queue->size * sizeof(*queue->msgs),
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(hostdata->dev, queue->msg_token))
+ goto map_failed;
+
+ gather_partition_info();
+ set_adapter_info(hostdata);
+
+ retrc = rc = plpar_hcall_norets(H_REG_CRQ,
+ vdev->unit_address,
+ queue->msg_token, PAGE_SIZE);
+ if (rc == H_RESOURCE)
+ /* maybe kexecing and resource is busy. try a reset */
+ rc = ibmvscsi_reset_crq_queue(queue,
+ hostdata);
+
+ if (rc == H_CLOSED) {
+ /* Adapter is good, but other end is not ready */
+ dev_warn(hostdata->dev, "Partner adapter not ready\n");
+ retrc = 0;
+ } else if (rc != 0) {
+ dev_warn(hostdata->dev, "Error %d opening adapter\n", rc);
+ goto reg_crq_failed;
+ }
+
+ queue->cur = 0;
+ spin_lock_init(&queue->lock);
+
+ tasklet_init(&hostdata->srp_task, (void *)ibmvscsi_task,
+ (unsigned long)hostdata);
+
+ if (request_irq(vdev->irq,
+ ibmvscsi_handle_event,
+ 0, "ibmvscsi", (void *)hostdata) != 0) {
+ dev_err(hostdata->dev, "couldn't register irq 0x%x\n",
+ vdev->irq);
+ goto req_irq_failed;
+ }
+
+ rc = vio_enable_interrupts(vdev);
+ if (rc != 0) {
+ dev_err(hostdata->dev, "Error %d enabling interrupts!!!\n", rc);
+ goto req_irq_failed;
+ }
+
+ return retrc;
+
+ req_irq_failed:
+ tasklet_kill(&hostdata->srp_task);
+ rc = 0;
+ do {
+ if (rc)
+ msleep(100);
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
+ reg_crq_failed:
+ dma_unmap_single(hostdata->dev,
+ queue->msg_token,
+ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+ map_failed:
+ free_page((unsigned long)queue->msgs);
+ malloc_failed:
+ return -1;
+}
+
+/**
+ * ibmvscsi_reenable_crq_queue() - reenables a crq after
+ * @queue: crq_queue to initialize and register
+ * @hostdata: ibmvscsi_host_data of host
+ */
+static int ibmvscsi_reenable_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata)
+{
+ int rc = 0;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+
+ set_adapter_info(hostdata);
+
+ /* Re-enable the CRQ */
+ do {
+ if (rc)
+ msleep(100);
+ rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
+ } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
+
+ if (rc)
+ dev_err(hostdata->dev, "Error %d enabling adapter\n", rc);
+ return rc;
+}
+
/* ------------------------------------------------------------
* Routines for the event pool and event structs
*/
@@ -134,7 +439,7 @@ MODULE_PARM_DESC(client_reserve, "Attempt client managed reserve/release");
* @hostdata: ibmvscsi_host_data who owns the event pool
*
* Returns zero on success.
-*/
+ */
static int initialize_event_pool(struct event_pool *pool,
int size, struct ibmvscsi_host_data *hostdata)
{
@@ -149,7 +454,7 @@ static int initialize_event_pool(struct event_pool *pool,
pool->iu_storage =
dma_alloc_coherent(hostdata->dev,
pool->size * sizeof(*pool->iu_storage),
- &pool->iu_token, 0);
+ &pool->iu_token, GFP_KERNEL);
if (!pool->iu_storage) {
kfree(pool->events);
return -ENOMEM;
@@ -159,10 +464,10 @@ static int initialize_event_pool(struct event_pool *pool,
struct srp_event_struct *evt = &pool->events[i];
memset(&evt->crq, 0x00, sizeof(evt->crq));
atomic_set(&evt->free, 1);
- evt->crq.valid = 0x80;
- evt->crq.IU_length = sizeof(*evt->xfer_iu);
- evt->crq.IU_data_ptr = pool->iu_token +
- sizeof(*evt->xfer_iu) * i;
+ evt->crq.valid = VIOSRP_CRQ_CMD_RSP;
+ evt->crq.IU_length = cpu_to_be16(sizeof(*evt->xfer_iu));
+ evt->crq.IU_data_ptr = cpu_to_be64(pool->iu_token +
+ sizeof(*evt->xfer_iu) * i);
evt->xfer_iu = pool->iu_storage + i;
evt->hostdata = hostdata;
evt->ext_list = NULL;
@@ -173,12 +478,12 @@ static int initialize_event_pool(struct event_pool *pool,
}
/**
- * release_event_pool: - Frees memory of an event pool of a host
+ * release_event_pool() - Frees memory of an event pool of a host
* @pool: event_pool to be released
* @hostdata: ibmvscsi_host_data who owns the even pool
*
* Returns zero on success.
-*/
+ */
static void release_event_pool(struct event_pool *pool,
struct ibmvscsi_host_data *hostdata)
{
@@ -221,11 +526,10 @@ static int valid_event_struct(struct event_pool *pool,
}
/**
- * ibmvscsi_free-event_struct: - Changes status of event to "free"
+ * free_event_struct() - Changes status of event to "free"
* @pool: event_pool that contains the event
* @evt: srp_event_struct to be modified
- *
-*/
+ */
static void free_event_struct(struct event_pool *pool,
struct srp_event_struct *evt)
{
@@ -242,7 +546,7 @@ static void free_event_struct(struct event_pool *pool,
}
/**
- * get_evt_struct: - Gets the next free event in pool
+ * get_event_struct() - Gets the next free event in pool
* @pool: event_pool that contains the events to be searched
*
* Returns the next event in "free" state, and NULL if none are free.
@@ -270,7 +574,7 @@ static struct srp_event_struct *get_event_struct(struct event_pool *pool)
/**
* init_event_struct: Initialize fields in an event struct that are always
* required.
- * @evt: The event
+ * @evt_struct: The event
* @done: Routine to call when the event is responded to
* @format: SRP or MAD format
* @timeout: timeout value set in the CRQ
@@ -284,7 +588,7 @@ static void init_event_struct(struct srp_event_struct *evt_struct,
evt_struct->cmnd_done = NULL;
evt_struct->sync_srp = NULL;
evt_struct->crq.format = format;
- evt_struct->crq.timeout = timeout;
+ evt_struct->crq.timeout = cpu_to_be16(timeout);
evt_struct->done = done;
}
@@ -292,7 +596,7 @@ static void init_event_struct(struct srp_event_struct *evt_struct,
* Routines for receiving SCSI responses from the hosting partition
*/
-/**
+/*
* set_srp_direction: Set the fields in the srp related to data
* direction and number of buffers based on the direction in
* the scsi_cmnd and the number of buffers
@@ -327,9 +631,9 @@ static void set_srp_direction(struct scsi_cmnd *cmd,
/**
* unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format
* @cmd: srp_cmd whose additional_data member will be unmapped
+ * @evt_struct: the event
* @dev: device for which the memory is mapped
- *
-*/
+ */
static void unmap_cmd_data(struct srp_cmd *cmd,
struct srp_event_struct *evt_struct,
struct device *dev)
@@ -355,8 +659,8 @@ static int map_sg_list(struct scsi_cmnd *cmd, int nseg,
scsi_for_each_sg(cmd, sg, nseg, i) {
struct srp_direct_buf *descr = md + i;
- descr->va = sg_dma_address(sg);
- descr->len = sg_dma_len(sg);
+ descr->va = cpu_to_be64(sg_dma_address(sg));
+ descr->len = cpu_to_be32(sg_dma_len(sg));
descr->key = 0;
total_length += sg_dma_len(sg);
}
@@ -364,8 +668,9 @@ static int map_sg_list(struct scsi_cmnd *cmd, int nseg,
}
/**
- * map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields
- * @cmd: Scsi_Cmnd with the scatterlist
+ * map_sg_data: - Maps dma for a scatterlist and initializes descriptor fields
+ * @cmd: struct scsi_cmnd with the scatterlist
+ * @evt_struct: struct srp_event_struct to map
* @srp_cmd: srp_cmd that contains the memory descriptor
* @dev: device for which to map dma memory
*
@@ -399,20 +704,20 @@ static int map_sg_data(struct scsi_cmnd *cmd,
}
indirect->table_desc.va = 0;
- indirect->table_desc.len = sg_mapped * sizeof(struct srp_direct_buf);
+ indirect->table_desc.len = cpu_to_be32(sg_mapped *
+ sizeof(struct srp_direct_buf));
indirect->table_desc.key = 0;
if (sg_mapped <= MAX_INDIRECT_BUFS) {
total_length = map_sg_list(cmd, sg_mapped,
&indirect->desc_list[0]);
- indirect->len = total_length;
+ indirect->len = cpu_to_be32(total_length);
return 1;
}
/* get indirect table */
if (!evt_struct->ext_list) {
- evt_struct->ext_list = (struct srp_direct_buf *)
- dma_alloc_coherent(dev,
+ evt_struct->ext_list = dma_alloc_coherent(dev,
SG_ALL * sizeof(struct srp_direct_buf),
&evt_struct->ext_list_token, 0);
if (!evt_struct->ext_list) {
@@ -427,9 +732,10 @@ static int map_sg_data(struct scsi_cmnd *cmd,
total_length = map_sg_list(cmd, sg_mapped, evt_struct->ext_list);
- indirect->len = total_length;
- indirect->table_desc.va = evt_struct->ext_list_token;
- indirect->table_desc.len = sg_mapped * sizeof(indirect->desc_list[0]);
+ indirect->len = cpu_to_be32(total_length);
+ indirect->table_desc.va = cpu_to_be64(evt_struct->ext_list_token);
+ indirect->table_desc.len = cpu_to_be32(sg_mapped *
+ sizeof(indirect->desc_list[0]));
memcpy(indirect->desc_list, evt_struct->ext_list,
MAX_INDIRECT_BUFS * sizeof(struct srp_direct_buf));
return 1;
@@ -438,6 +744,7 @@ static int map_sg_data(struct scsi_cmnd *cmd,
/**
* map_data_for_srp_cmd: - Calls functions to map data for srp cmds
* @cmd: struct scsi_cmnd with the memory to be mapped
+ * @evt_struct: struct srp_event_struct to map
* @srp_cmd: srp_cmd that contains the memory descriptor
* @dev: dma device for which to map dma memory
*
@@ -471,6 +778,7 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd,
/**
* purge_requests: Our virtual adapter just shut down. purge any sent requests
* @hostdata: the adapter
+ * @error_code: error code to return as the 'result'
*/
static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code)
{
@@ -481,7 +789,7 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code)
while (!list_empty(&hostdata->sent)) {
evt = list_first_entry(&hostdata->sent, struct srp_event_struct, list);
list_del(&evt->list);
- del_timer(&evt->timer);
+ timer_delete(&evt->timer);
spin_unlock_irqrestore(hostdata->host->host_lock, flags);
if (evt->cmnd) {
@@ -490,7 +798,8 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code)
evt->hostdata->dev);
if (evt->cmnd_done)
evt->cmnd_done(evt->cmnd);
- } else if (evt->done)
+ } else if (evt->done && evt->crq.format != VIOSRP_MAD_FORMAT &&
+ evt->iu.srp.login_req.opcode != SRP_LOGIN_REQ)
evt->done(evt);
free_event_struct(&evt->hostdata->pool, evt);
spin_lock_irqsave(hostdata->host->host_lock, flags);
@@ -499,27 +808,45 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code)
}
/**
+ * ibmvscsi_set_request_limit - Set the adapter request_limit in response to
+ * an adapter failure, reset, or SRP Login. Done under host lock to prevent
+ * race with SCSI command submission.
+ * @hostdata: adapter to adjust
+ * @limit: new request limit
+ */
+static void ibmvscsi_set_request_limit(struct ibmvscsi_host_data *hostdata, int limit)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ atomic_set(&hostdata->request_limit, limit);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+}
+
+/**
* ibmvscsi_reset_host - Reset the connection to the server
* @hostdata: struct ibmvscsi_host_data to reset
*/
static void ibmvscsi_reset_host(struct ibmvscsi_host_data *hostdata)
{
scsi_block_requests(hostdata->host);
- atomic_set(&hostdata->request_limit, 0);
+ ibmvscsi_set_request_limit(hostdata, 0);
purge_requests(hostdata, DID_ERROR);
- hostdata->reset_crq = 1;
+ hostdata->action = IBMVSCSI_HOST_ACTION_RESET;
wake_up(&hostdata->work_wait_q);
}
/**
* ibmvscsi_timeout - Internal command timeout handler
- * @evt_struct: struct srp_event_struct that timed out
+ * @t: struct srp_event_struct that timed out
*
* Called when an internally generated command times out
*/
-static void ibmvscsi_timeout(struct srp_event_struct *evt_struct)
+static void ibmvscsi_timeout(struct timer_list *t)
{
+ struct srp_event_struct *evt_struct = timer_container_of(evt_struct,
+ t, timer);
struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
dev_err(hostdata->dev, "Command timed out (%x). Resetting connection\n",
@@ -545,7 +872,7 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct,
struct ibmvscsi_host_data *hostdata,
unsigned long timeout)
{
- u64 *crq_as_u64 = (u64 *) &evt_struct->crq;
+ __be64 *crq_as_u64 = (__be64 *)&evt_struct->crq;
int request_status = 0;
int rc;
int srp_req = 0;
@@ -608,18 +935,17 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct,
*/
list_add_tail(&evt_struct->list, &hostdata->sent);
- init_timer(&evt_struct->timer);
+ timer_setup(&evt_struct->timer, ibmvscsi_timeout, 0);
if (timeout) {
- evt_struct->timer.data = (unsigned long) evt_struct;
evt_struct->timer.expires = jiffies + (timeout * HZ);
- evt_struct->timer.function = (void (*)(unsigned long))ibmvscsi_timeout;
add_timer(&evt_struct->timer);
}
- if ((rc =
- ibmvscsi_ops->send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) {
+ rc = ibmvscsi_send_crq(hostdata, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+ if (rc != 0) {
list_del(&evt_struct->list);
- del_timer(&evt_struct->timer);
+ timer_delete(&evt_struct->timer);
/* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY.
* Firmware will send a CRQ with a transport event (0xFF) to
@@ -675,23 +1001,24 @@ static void handle_cmd_rsp(struct srp_event_struct *evt_struct)
if (unlikely(rsp->opcode != SRP_RSP)) {
if (printk_ratelimit())
dev_warn(evt_struct->hostdata->dev,
- "bad SRP RSP type %d\n", rsp->opcode);
+ "bad SRP RSP type %#02x\n", rsp->opcode);
}
if (cmnd) {
cmnd->result |= rsp->status;
- if (((cmnd->result >> 1) & 0x1f) == CHECK_CONDITION)
+ if (scsi_status_is_check_condition(cmnd->result))
memcpy(cmnd->sense_buffer,
rsp->data,
- rsp->sense_data_len);
+ be32_to_cpu(rsp->sense_data_len));
unmap_cmd_data(&evt_struct->iu.srp.cmd,
evt_struct,
evt_struct->hostdata->dev);
if (rsp->flags & SRP_RSP_FLAG_DOOVER)
- scsi_set_resid(cmnd, rsp->data_out_res_cnt);
+ scsi_set_resid(cmnd,
+ be32_to_cpu(rsp->data_out_res_cnt));
else if (rsp->flags & SRP_RSP_FLAG_DIOVER)
- scsi_set_resid(cmnd, rsp->data_in_res_cnt);
+ scsi_set_resid(cmnd, be32_to_cpu(rsp->data_in_res_cnt));
}
if (evt_struct->cmnd_done)
@@ -709,13 +1036,13 @@ static inline u16 lun_from_dev(struct scsi_device *dev)
}
/**
- * ibmvscsi_queue: - The queuecommand function of the scsi template
- * @cmd: struct scsi_cmnd to be executed
+ * ibmvscsi_queuecommand_lck() - The queuecommand function of the scsi template
+ * @cmnd: struct scsi_cmnd to be executed
* @done: Callback function to be called when cmd is completed
*/
-static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd,
- void (*done) (struct scsi_cmnd *))
+static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
struct srp_cmd *srp_cmd;
struct srp_event_struct *evt_struct;
struct srp_indirect_buf *indirect;
@@ -729,11 +1056,12 @@ static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd,
return SCSI_MLQUEUE_HOST_BUSY;
/* Set up the actual SRP IU */
+ BUILD_BUG_ON(sizeof(evt_struct->iu.srp) != SRP_MAX_IU_LEN);
+ memset(&evt_struct->iu.srp, 0x00, sizeof(evt_struct->iu.srp));
srp_cmd = &evt_struct->iu.srp.cmd;
- memset(srp_cmd, 0x00, SRP_MAX_IU_LEN);
srp_cmd->opcode = SRP_CMD;
memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(srp_cmd->cdb));
- srp_cmd->lun = ((u64) lun) << 48;
+ int_to_scsilun(lun, &srp_cmd->lun);
if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) {
if (!firmware_has_feature(FW_FEATURE_CMO))
@@ -746,7 +1074,7 @@ static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd,
init_event_struct(evt_struct,
handle_cmd_rsp,
VIOSRP_SRP_FORMAT,
- cmnd->request->timeout/HZ);
+ scsi_cmd_to_rq(cmnd)->timeout / HZ);
evt_struct->cmnd = cmnd;
evt_struct->cmnd_done = done;
@@ -758,9 +1086,10 @@ static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd,
if ((in_fmt == SRP_DATA_DESC_INDIRECT ||
out_fmt == SRP_DATA_DESC_INDIRECT) &&
indirect->table_desc.va == 0) {
- indirect->table_desc.va = evt_struct->crq.IU_data_ptr +
+ indirect->table_desc.va =
+ cpu_to_be64(be64_to_cpu(evt_struct->crq.IU_data_ptr) +
offsetof(struct srp_cmd, add_data) +
- offsetof(struct srp_indirect_buf, desc_list);
+ offsetof(struct srp_indirect_buf, desc_list));
}
return ibmvscsi_send_srp_event(evt_struct, hostdata, 0);
@@ -836,13 +1165,13 @@ static void login_rsp(struct srp_event_struct *evt_struct)
dev_info(hostdata->dev, "SRP_LOGIN_REJ reason %u\n",
evt_struct->xfer_iu->srp.login_rej.reason);
/* Login failed. */
- atomic_set(&hostdata->request_limit, -1);
+ ibmvscsi_set_request_limit(hostdata, -1);
return;
default:
dev_err(hostdata->dev, "Invalid login response typecode 0x%02x!\n",
evt_struct->xfer_iu->srp.login_rsp.opcode);
/* Login failed. */
- atomic_set(&hostdata->request_limit, -1);
+ ibmvscsi_set_request_limit(hostdata, -1);
return;
}
@@ -853,11 +1182,12 @@ static void login_rsp(struct srp_event_struct *evt_struct)
* This value is set rather than added to request_limit because
* request_limit could have been set to -1 by this client.
*/
- atomic_set(&hostdata->request_limit,
- evt_struct->xfer_iu->srp.login_rsp.req_lim_delta);
+ ibmvscsi_set_request_limit(hostdata,
+ be32_to_cpu(evt_struct->xfer_iu->srp.login_rsp.req_lim_delta));
/* If we had any pending I/Os, kick them */
- scsi_unblock_requests(hostdata->host);
+ hostdata->action = IBMVSCSI_HOST_ACTION_UNBLOCK;
+ wake_up(&hostdata->work_wait_q);
}
/**
@@ -880,16 +1210,17 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata)
login = &evt_struct->iu.srp.login_req;
memset(login, 0, sizeof(*login));
login->opcode = SRP_LOGIN_REQ;
- login->req_it_iu_len = sizeof(union srp_iu);
- login->req_buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT;
+ login->req_it_iu_len = cpu_to_be32(sizeof(union srp_iu));
+ login->req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT |
+ SRP_BUF_FORMAT_INDIRECT);
- spin_lock_irqsave(hostdata->host->host_lock, flags);
/* Start out with a request limit of 0, since this is negotiated in
* the login request we are just sending and login requests always
* get sent by the driver regardless of request_limit.
*/
- atomic_set(&hostdata->request_limit, 0);
+ ibmvscsi_set_request_limit(hostdata, 0);
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
rc = ibmvscsi_send_srp_event(evt_struct, hostdata, login_timeout * 2);
spin_unlock_irqrestore(hostdata->host->host_lock, flags);
dev_info(hostdata->dev, "sent SRP login\n");
@@ -910,12 +1241,13 @@ static void capabilities_rsp(struct srp_event_struct *evt_struct)
dev_err(hostdata->dev, "error 0x%X getting capabilities info\n",
evt_struct->xfer_iu->mad.capabilities.common.status);
} else {
- if (hostdata->caps.migration.common.server_support != SERVER_SUPPORTS_CAP)
+ if (hostdata->caps.migration.common.server_support !=
+ cpu_to_be16(SERVER_SUPPORTS_CAP))
dev_info(hostdata->dev, "Partition migration not supported\n");
if (client_reserve) {
if (hostdata->caps.reserve.common.server_support ==
- SERVER_SUPPORTS_CAP)
+ cpu_to_be16(SERVER_SUPPORTS_CAP))
dev_info(hostdata->dev, "Client reserve enabled\n");
else
dev_info(hostdata->dev, "Client reserve not supported\n");
@@ -947,35 +1279,42 @@ static void send_mad_capabilities(struct ibmvscsi_host_data *hostdata)
req = &evt_struct->iu.mad.capabilities;
memset(req, 0, sizeof(*req));
- hostdata->caps.flags = CAP_LIST_SUPPORTED;
+ hostdata->caps.flags = cpu_to_be32(CAP_LIST_SUPPORTED);
if (hostdata->client_migrated)
- hostdata->caps.flags |= CLIENT_MIGRATED;
+ hostdata->caps.flags |= cpu_to_be32(CLIENT_MIGRATED);
- strncpy(hostdata->caps.name, dev_name(&hostdata->host->shost_gendev),
+ strscpy(hostdata->caps.name, dev_name(&hostdata->host->shost_gendev),
sizeof(hostdata->caps.name));
- hostdata->caps.name[sizeof(hostdata->caps.name) - 1] = '\0';
location = of_get_property(of_node, "ibm,loc-code", NULL);
location = location ? location : dev_name(hostdata->dev);
- strncpy(hostdata->caps.loc, location, sizeof(hostdata->caps.loc));
- hostdata->caps.loc[sizeof(hostdata->caps.loc) - 1] = '\0';
+ strscpy(hostdata->caps.loc, location, sizeof(hostdata->caps.loc));
- req->common.type = VIOSRP_CAPABILITIES_TYPE;
- req->buffer = hostdata->caps_addr;
+ req->common.type = cpu_to_be32(VIOSRP_CAPABILITIES_TYPE);
+ req->buffer = cpu_to_be64(hostdata->caps_addr);
- hostdata->caps.migration.common.cap_type = MIGRATION_CAPABILITIES;
- hostdata->caps.migration.common.length = sizeof(hostdata->caps.migration);
- hostdata->caps.migration.common.server_support = SERVER_SUPPORTS_CAP;
- hostdata->caps.migration.ecl = 1;
+ hostdata->caps.migration.common.cap_type =
+ cpu_to_be32(MIGRATION_CAPABILITIES);
+ hostdata->caps.migration.common.length =
+ cpu_to_be16(sizeof(hostdata->caps.migration));
+ hostdata->caps.migration.common.server_support =
+ cpu_to_be16(SERVER_SUPPORTS_CAP);
+ hostdata->caps.migration.ecl = cpu_to_be32(1);
if (client_reserve) {
- hostdata->caps.reserve.common.cap_type = RESERVATION_CAPABILITIES;
- hostdata->caps.reserve.common.length = sizeof(hostdata->caps.reserve);
- hostdata->caps.reserve.common.server_support = SERVER_SUPPORTS_CAP;
- hostdata->caps.reserve.type = CLIENT_RESERVE_SCSI_2;
- req->common.length = sizeof(hostdata->caps);
+ hostdata->caps.reserve.common.cap_type =
+ cpu_to_be32(RESERVATION_CAPABILITIES);
+ hostdata->caps.reserve.common.length =
+ cpu_to_be16(sizeof(hostdata->caps.reserve));
+ hostdata->caps.reserve.common.server_support =
+ cpu_to_be16(SERVER_SUPPORTS_CAP);
+ hostdata->caps.reserve.type =
+ cpu_to_be32(CLIENT_RESERVE_SCSI_2);
+ req->common.length =
+ cpu_to_be16(sizeof(hostdata->caps));
} else
- req->common.length = sizeof(hostdata->caps) - sizeof(hostdata->caps.reserve);
+ req->common.length = cpu_to_be16(sizeof(hostdata->caps) -
+ sizeof(hostdata->caps.reserve));
spin_lock_irqsave(hostdata->host->host_lock, flags);
if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2))
@@ -993,7 +1332,7 @@ static void send_mad_capabilities(struct ibmvscsi_host_data *hostdata)
static void fast_fail_rsp(struct srp_event_struct *evt_struct)
{
struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
- u8 status = evt_struct->xfer_iu->mad.fast_fail.common.status;
+ u16 status = be16_to_cpu(evt_struct->xfer_iu->mad.fast_fail.common.status);
if (status == VIOSRP_MAD_NOT_SUPPORTED)
dev_err(hostdata->dev, "fast_fail not supported in server\n");
@@ -1006,7 +1345,7 @@ static void fast_fail_rsp(struct srp_event_struct *evt_struct)
}
/**
- * init_host - Start host initialization
+ * enable_fast_fail() - Start host initialization
* @hostdata: ibmvscsi_host_data of host
*
* Returns zero if successful.
@@ -1030,8 +1369,8 @@ static int enable_fast_fail(struct ibmvscsi_host_data *hostdata)
fast_fail_mad = &evt_struct->iu.mad.fast_fail;
memset(fast_fail_mad, 0, sizeof(*fast_fail_mad));
- fast_fail_mad->common.type = VIOSRP_ENABLE_FAST_FAIL;
- fast_fail_mad->common.length = sizeof(*fast_fail_mad);
+ fast_fail_mad->common.type = cpu_to_be32(VIOSRP_ENABLE_FAST_FAIL);
+ fast_fail_mad->common.length = cpu_to_be16(sizeof(*fast_fail_mad));
spin_lock_irqsave(hostdata->host->host_lock, flags);
rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2);
@@ -1058,15 +1397,15 @@ static void adapter_info_rsp(struct srp_event_struct *evt_struct)
"host partition %s (%d), OS %d, max io %u\n",
hostdata->madapter_info.srp_version,
hostdata->madapter_info.partition_name,
- hostdata->madapter_info.partition_number,
- hostdata->madapter_info.os_type,
- hostdata->madapter_info.port_max_txu[0]);
+ be32_to_cpu(hostdata->madapter_info.partition_number),
+ be32_to_cpu(hostdata->madapter_info.os_type),
+ be32_to_cpu(hostdata->madapter_info.port_max_txu[0]));
if (hostdata->madapter_info.port_max_txu[0])
hostdata->host->max_sectors =
- hostdata->madapter_info.port_max_txu[0] >> 9;
+ be32_to_cpu(hostdata->madapter_info.port_max_txu[0]) >> 9;
- if (hostdata->madapter_info.os_type == 3 &&
+ if (be32_to_cpu(hostdata->madapter_info.os_type) == SRP_MAD_OS_AIX &&
strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) {
dev_err(hostdata->dev, "host (Ver. %s) doesn't support large transfers\n",
hostdata->madapter_info.srp_version);
@@ -1075,7 +1414,7 @@ static void adapter_info_rsp(struct srp_event_struct *evt_struct)
hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS;
}
- if (hostdata->madapter_info.os_type == 3) {
+ if (be32_to_cpu(hostdata->madapter_info.os_type) == SRP_MAD_OS_AIX) {
enable_fast_fail(hostdata);
return;
}
@@ -1110,9 +1449,9 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata)
req = &evt_struct->iu.mad.adapter_info;
memset(req, 0x00, sizeof(*req));
- req->common.type = VIOSRP_ADAPTER_INFO_TYPE;
- req->common.length = sizeof(hostdata->madapter_info);
- req->buffer = hostdata->adapter_info_addr;
+ req->common.type = cpu_to_be32(VIOSRP_ADAPTER_INFO_TYPE);
+ req->common.length = cpu_to_be16(sizeof(hostdata->madapter_info));
+ req->buffer = cpu_to_be64(hostdata->adapter_info_addr);
spin_lock_irqsave(hostdata->host->host_lock, flags);
if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2))
@@ -1120,16 +1459,15 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata)
spin_unlock_irqrestore(hostdata->host->host_lock, flags);
};
-/**
- * init_adapter: Start virtual adapter initialization sequence
- *
+/*
+ * init_adapter() - Start virtual adapter initialization sequence
*/
static void init_adapter(struct ibmvscsi_host_data *hostdata)
{
send_mad_adapter_info(hostdata);
}
-/**
+/*
* sync_completion: Signal that a synchronous command has completed
* Note that after returning from this call, the evt_struct is freed.
* the caller waiting on this completion shouldn't touch the evt_struct
@@ -1144,8 +1482,8 @@ static void sync_completion(struct srp_event_struct *evt_struct)
complete(&evt_struct->comp);
}
-/**
- * ibmvscsi_abort: Abort a command...from scsi host template
+/*
+ * ibmvscsi_eh_abort_handler: Abort a command...from scsi host template
* send this over to the server and wait synchronously for the response
*/
static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
@@ -1197,7 +1535,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
/* Set up an abort SRP command */
memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
tsk_mgmt->opcode = SRP_TSK_MGMT;
- tsk_mgmt->lun = ((u64) lun) << 48;
+ int_to_scsilun(lun, &tsk_mgmt->lun);
tsk_mgmt->tsk_mgmt_func = SRP_TSK_ABORT_TASK;
tsk_mgmt->task_tag = (u64) found_evt;
@@ -1282,7 +1620,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
return SUCCESS;
}
-/**
+/*
* ibmvscsi_eh_device_reset_handler: Reset a single LUN...from scsi host
* template send this over to the server and wait synchronously for the
* response
@@ -1320,7 +1658,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
/* Set up a lun reset SRP command */
memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
tsk_mgmt->opcode = SRP_TSK_MGMT;
- tsk_mgmt->lun = ((u64) lun) << 48;
+ int_to_scsilun(lun, &tsk_mgmt->lun);
tsk_mgmt->tsk_mgmt_func = SRP_TSK_LUN_RESET;
evt->sync_srp = &srp_rsp;
@@ -1426,21 +1764,22 @@ static int ibmvscsi_eh_host_reset_handler(struct scsi_cmnd *cmd)
* @hostdata: ibmvscsi_host_data of host
*
*/
-void ibmvscsi_handle_crq(struct viosrp_crq *crq,
- struct ibmvscsi_host_data *hostdata)
+static void ibmvscsi_handle_crq(struct viosrp_crq *crq,
+ struct ibmvscsi_host_data *hostdata)
{
long rc;
unsigned long flags;
+ /* The hypervisor copies our tag value here so no byteswapping */
struct srp_event_struct *evt_struct =
- (struct srp_event_struct *)crq->IU_data_ptr;
+ (__force struct srp_event_struct *)crq->IU_data_ptr;
switch (crq->valid) {
- case 0xC0: /* initialization */
+ case VIOSRP_CRQ_INIT_RSP: /* initialization */
switch (crq->format) {
- case 0x01: /* Initialization message */
+ case VIOSRP_CRQ_INIT: /* Initialization message */
dev_info(hostdata->dev, "partner initialized\n");
/* Send back a response */
- if ((rc = ibmvscsi_ops->send_crq(hostdata,
- 0xC002000000000000LL, 0)) == 0) {
+ rc = ibmvscsi_send_crq(hostdata, 0xC002000000000000LL, 0);
+ if (rc == 0) {
/* Now login */
init_adapter(hostdata);
} else {
@@ -1448,7 +1787,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
}
break;
- case 0x02: /* Initialization response */
+ case VIOSRP_CRQ_INIT_COMPLETE: /* Initialization response */
dev_info(hostdata->dev, "partner initialization complete\n");
/* Now login */
@@ -1458,14 +1797,14 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
dev_err(hostdata->dev, "unknown crq message type: %d\n", crq->format);
}
return;
- case 0xFF: /* Hypervisor telling us the connection is closed */
+ case VIOSRP_CRQ_XPORT_EVENT: /* Hypervisor telling us the connection is closed */
scsi_block_requests(hostdata->host);
- atomic_set(&hostdata->request_limit, 0);
+ ibmvscsi_set_request_limit(hostdata, 0);
if (crq->format == 0x06) {
/* We need to re-setup the interpartition connection */
dev_info(hostdata->dev, "Re-enabling adapter!\n");
hostdata->client_migrated = 1;
- hostdata->reenable_crq = 1;
+ hostdata->action = IBMVSCSI_HOST_ACTION_REENABLE;
purge_requests(hostdata, DID_REQUEUE);
wake_up(&hostdata->work_wait_q);
} else {
@@ -1474,7 +1813,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
ibmvscsi_reset_host(hostdata);
}
return;
- case 0x80: /* real payload */
+ case VIOSRP_CRQ_CMD_RSP: /* real payload */
break;
default:
dev_err(hostdata->dev, "got an invalid message type 0x%02x\n",
@@ -1488,21 +1827,21 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
*/
if (!valid_event_struct(&hostdata->pool, evt_struct)) {
dev_err(hostdata->dev, "returned correlation_token 0x%p is invalid!\n",
- (void *)crq->IU_data_ptr);
+ evt_struct);
return;
}
if (atomic_read(&evt_struct->free)) {
dev_err(hostdata->dev, "received duplicate correlation_token 0x%p!\n",
- (void *)crq->IU_data_ptr);
+ evt_struct);
return;
}
if (crq->format == VIOSRP_SRP_FORMAT)
- atomic_add(evt_struct->xfer_iu->srp.rsp.req_lim_delta,
+ atomic_add(be32_to_cpu(evt_struct->xfer_iu->srp.rsp.req_lim_delta),
&hostdata->request_limit);
- del_timer(&evt_struct->timer);
+ timer_delete(&evt_struct->timer);
if ((crq->status != VIOSRP_OK && crq->status != VIOSRP_OK2) && evt_struct->cmnd)
evt_struct->cmnd->result = DID_ERROR << 16;
@@ -1522,67 +1861,16 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
}
/**
- * ibmvscsi_get_host_config: Send the command to the server to get host
- * configuration data. The data is opaque to us.
- */
-static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata,
- unsigned char *buffer, int length)
-{
- struct viosrp_host_config *host_config;
- struct srp_event_struct *evt_struct;
- unsigned long flags;
- dma_addr_t addr;
- int rc;
-
- evt_struct = get_event_struct(&hostdata->pool);
- if (!evt_struct) {
- dev_err(hostdata->dev, "couldn't allocate event for HOST_CONFIG!\n");
- return -1;
- }
-
- init_event_struct(evt_struct,
- sync_completion,
- VIOSRP_MAD_FORMAT,
- info_timeout);
-
- host_config = &evt_struct->iu.mad.host_config;
-
- /* Set up a lun reset SRP command */
- memset(host_config, 0x00, sizeof(*host_config));
- host_config->common.type = VIOSRP_HOST_CONFIG_TYPE;
- host_config->common.length = length;
- host_config->buffer = addr = dma_map_single(hostdata->dev, buffer,
- length,
- DMA_BIDIRECTIONAL);
-
- if (dma_mapping_error(hostdata->dev, host_config->buffer)) {
- if (!firmware_has_feature(FW_FEATURE_CMO))
- dev_err(hostdata->dev,
- "dma_mapping error getting host config\n");
- free_event_struct(&hostdata->pool, evt_struct);
- return -1;
- }
-
- init_completion(&evt_struct->comp);
- spin_lock_irqsave(hostdata->host->host_lock, flags);
- rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2);
- spin_unlock_irqrestore(hostdata->host->host_lock, flags);
- if (rc == 0)
- wait_for_completion(&evt_struct->comp);
- dma_unmap_single(hostdata->dev, addr, length, DMA_BIDIRECTIONAL);
-
- return rc;
-}
-
-/**
- * ibmvscsi_slave_configure: Set the "allow_restart" flag for each disk.
+ * ibmvscsi_sdev_configure: Set the "allow_restart" flag for each disk.
* @sdev: struct scsi_device device to configure
+ * @lim: Request queue limits
*
* Enable allow_restart for a device if it is a disk. Adjust the
* queue_depth here also as is required by the documentation for
* struct scsi_host_template.
*/
-static int ibmvscsi_slave_configure(struct scsi_device *sdev)
+static int ibmvscsi_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct Scsi_Host *shost = sdev->host;
unsigned long lock_flags = 0;
@@ -1592,7 +1880,6 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev)
sdev->allow_restart = 1;
blk_queue_rq_timeout(sdev->request_queue, 120 * HZ);
}
- scsi_adjust_queue_depth(sdev, 0, shost->cmd_per_lun);
spin_unlock_irqrestore(shost->host_lock, lock_flags);
return 0;
}
@@ -1601,22 +1888,15 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev)
* ibmvscsi_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
- * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
+static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN)
qdepth = IBMVSCSI_MAX_CMDS_PER_LUN;
-
- scsi_adjust_queue_depth(sdev, 0, qdepth);
- return sdev->queue_depth;
+ return scsi_change_queue_depth(sdev, qdepth);
}
/* ------------------------------------------------------------
@@ -1712,7 +1992,7 @@ static ssize_t show_host_partition_number(struct device *dev,
int len;
len = snprintf(buf, PAGE_SIZE, "%d\n",
- hostdata->madapter_info.partition_number);
+ be32_to_cpu(hostdata->madapter_info.partition_number));
return len;
}
@@ -1732,7 +2012,7 @@ static ssize_t show_host_mad_version(struct device *dev,
int len;
len = snprintf(buf, PAGE_SIZE, "%d\n",
- hostdata->madapter_info.mad_version);
+ be32_to_cpu(hostdata->madapter_info.mad_version));
return len;
}
@@ -1751,7 +2031,8 @@ static ssize_t show_host_os_type(struct device *dev,
struct ibmvscsi_host_data *hostdata = shost_priv(shost);
int len;
- len = snprintf(buf, PAGE_SIZE, "%d\n", hostdata->madapter_info.os_type);
+ len = snprintf(buf, PAGE_SIZE, "%d\n",
+ be32_to_cpu(hostdata->madapter_info.os_type));
return len;
}
@@ -1766,36 +2047,41 @@ static struct device_attribute ibmvscsi_host_os_type = {
static ssize_t show_host_config(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct ibmvscsi_host_data *hostdata = shost_priv(shost);
-
- /* returns null-terminated host config data */
- if (ibmvscsi_do_host_config(hostdata, buf, PAGE_SIZE) == 0)
- return strlen(buf);
- else
- return 0;
+ return 0;
}
static struct device_attribute ibmvscsi_host_config = {
.attr = {
- .name = "config",
- .mode = S_IRUGO,
- },
+ .name = "config",
+ .mode = S_IRUGO,
+ },
.show = show_host_config,
};
-static struct device_attribute *ibmvscsi_attrs[] = {
- &ibmvscsi_host_vhost_loc,
- &ibmvscsi_host_vhost_name,
- &ibmvscsi_host_srp_version,
- &ibmvscsi_host_partition_name,
- &ibmvscsi_host_partition_number,
- &ibmvscsi_host_mad_version,
- &ibmvscsi_host_os_type,
- &ibmvscsi_host_config,
+static int ibmvscsi_host_reset(struct Scsi_Host *shost, int reset_type)
+{
+ struct ibmvscsi_host_data *hostdata = shost_priv(shost);
+
+ dev_info(hostdata->dev, "Initiating adapter reset!\n");
+ ibmvscsi_reset_host(hostdata);
+
+ return 0;
+}
+
+static struct attribute *ibmvscsi_host_attrs[] = {
+ &ibmvscsi_host_vhost_loc.attr,
+ &ibmvscsi_host_vhost_name.attr,
+ &ibmvscsi_host_srp_version.attr,
+ &ibmvscsi_host_partition_name.attr,
+ &ibmvscsi_host_partition_number.attr,
+ &ibmvscsi_host_mad_version.attr,
+ &ibmvscsi_host_os_type.attr,
+ &ibmvscsi_host_config.attr,
NULL
};
+ATTRIBUTE_GROUPS(ibmvscsi_host);
+
/* ------------------------------------------------------------
* SCSI driver registration
*/
@@ -1804,17 +2090,18 @@ static struct scsi_host_template driver_template = {
.name = "IBM POWER Virtual SCSI Adapter " IBMVSCSI_VERSION,
.proc_name = "ibmvscsi",
.queuecommand = ibmvscsi_queuecommand,
+ .eh_timed_out = srp_timed_out,
.eh_abort_handler = ibmvscsi_eh_abort_handler,
.eh_device_reset_handler = ibmvscsi_eh_device_reset_handler,
.eh_host_reset_handler = ibmvscsi_eh_host_reset_handler,
- .slave_configure = ibmvscsi_slave_configure,
+ .sdev_configure = ibmvscsi_sdev_configure,
.change_queue_depth = ibmvscsi_change_queue_depth,
+ .host_reset = ibmvscsi_host_reset,
.cmd_per_lun = IBMVSCSI_CMDS_PER_LUN_DEFAULT,
.can_queue = IBMVSCSI_MAX_REQUESTS_DEFAULT,
.this_id = -1,
.sg_tablesize = SG_ALL,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = ibmvscsi_attrs,
+ .shost_groups = ibmvscsi_host_groups,
};
/**
@@ -1839,48 +2126,75 @@ static unsigned long ibmvscsi_get_desired_dma(struct vio_dev *vdev)
static void ibmvscsi_do_work(struct ibmvscsi_host_data *hostdata)
{
+ unsigned long flags;
int rc;
char *action = "reset";
- if (hostdata->reset_crq) {
- smp_rmb();
- hostdata->reset_crq = 0;
-
- rc = ibmvscsi_ops->reset_crq_queue(&hostdata->queue, hostdata);
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ switch (hostdata->action) {
+ case IBMVSCSI_HOST_ACTION_UNBLOCK:
+ rc = 0;
+ break;
+ case IBMVSCSI_HOST_ACTION_RESET:
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ rc = ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata);
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
if (!rc)
- rc = ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0);
+ rc = ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0);
vio_enable_interrupts(to_vio_dev(hostdata->dev));
- } else if (hostdata->reenable_crq) {
- smp_rmb();
+ break;
+ case IBMVSCSI_HOST_ACTION_REENABLE:
action = "enable";
- rc = ibmvscsi_ops->reenable_crq_queue(&hostdata->queue, hostdata);
- hostdata->reenable_crq = 0;
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ rc = ibmvscsi_reenable_crq_queue(&hostdata->queue, hostdata);
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
if (!rc)
- rc = ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0);
- } else
+ rc = ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0);
+ break;
+ case IBMVSCSI_HOST_ACTION_NONE:
+ default:
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
return;
+ }
+
+ hostdata->action = IBMVSCSI_HOST_ACTION_NONE;
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
if (rc) {
- atomic_set(&hostdata->request_limit, -1);
+ ibmvscsi_set_request_limit(hostdata, -1);
dev_err(hostdata->dev, "error after %s\n", action);
}
scsi_unblock_requests(hostdata->host);
}
-static int ibmvscsi_work_to_do(struct ibmvscsi_host_data *hostdata)
+static int __ibmvscsi_work_to_do(struct ibmvscsi_host_data *hostdata)
{
if (kthread_should_stop())
return 1;
- else if (hostdata->reset_crq) {
- smp_rmb();
- return 1;
- } else if (hostdata->reenable_crq) {
- smp_rmb();
- return 1;
+ switch (hostdata->action) {
+ case IBMVSCSI_HOST_ACTION_NONE:
+ return 0;
+ case IBMVSCSI_HOST_ACTION_RESET:
+ case IBMVSCSI_HOST_ACTION_REENABLE:
+ case IBMVSCSI_HOST_ACTION_UNBLOCK:
+ default:
+ break;
}
- return 0;
+ return 1;
+}
+
+static int ibmvscsi_work_to_do(struct ibmvscsi_host_data *hostdata)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ rc = __ibmvscsi_work_to_do(hostdata);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+
+ return rc;
}
static int ibmvscsi_work(void *data)
@@ -1888,7 +2202,7 @@ static int ibmvscsi_work(void *data)
struct ibmvscsi_host_data *hostdata = data;
int rc;
- set_user_nice(current, -20);
+ set_user_nice(current, MIN_NICE);
while (1) {
rc = wait_event_interruptible(hostdata->work_wait_q,
@@ -1905,7 +2219,7 @@ static int ibmvscsi_work(void *data)
return 0;
}
-/**
+/*
* Called by bus code for each adapter
*/
static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
@@ -1933,7 +2247,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
init_waitqueue_head(&hostdata->work_wait_q);
hostdata->host = host;
hostdata->dev = dev;
- atomic_set(&hostdata->request_limit, -1);
+ ibmvscsi_set_request_limit(hostdata, -1);
hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT;
if (map_persist_bufs(hostdata)) {
@@ -1950,7 +2264,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto init_crq_failed;
}
- rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_events);
+ rc = ibmvscsi_init_crq_queue(&hostdata->queue, hostdata, max_events);
if (rc != 0 && rc != H_RESOURCE) {
dev_err(&vdev->dev, "couldn't initialize crq. rc=%d\n", rc);
goto kill_kthread;
@@ -1960,11 +2274,15 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto init_pool_failed;
}
- host->max_lun = 8;
+ host->max_lun = IBMVSCSI_MAX_LUN;
host->max_id = max_id;
host->max_channel = max_channel;
host->max_cmd_len = 16;
+ dev_info(dev,
+ "Maximum ID: %d Maximum LUN: %llu Maximum Channel: %d\n",
+ host->max_id, host->max_lun, host->max_channel);
+
if (scsi_add_host(hostdata->host, hostdata->dev))
goto add_host_failed;
@@ -1980,7 +2298,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
* to fail if the other end is not acive. In that case we don't
* want to scan
*/
- if (ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0) == 0
+ if (ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0) == 0
|| rc == H_RESOURCE) {
/*
* Wait around max init_timeout secs for the adapter to finish
@@ -2001,6 +2319,9 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
}
dev_set_drvdata(&vdev->dev, hostdata);
+ spin_lock(&ibmvscsi_driver_lock);
+ list_add_tail(&hostdata->host_list, &ibmvscsi_head);
+ spin_unlock(&ibmvscsi_driver_lock);
return 0;
add_srp_port_failed:
@@ -2008,7 +2329,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
add_host_failed:
release_event_pool(&hostdata->pool, hostdata);
init_pool_failed:
- ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_events);
+ ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, max_events);
kill_kthread:
kthread_stop(hostdata->work_thread);
init_crq_failed:
@@ -2019,20 +2340,27 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
return -1;
}
-static int ibmvscsi_remove(struct vio_dev *vdev)
+static void ibmvscsi_remove(struct vio_dev *vdev)
{
struct ibmvscsi_host_data *hostdata = dev_get_drvdata(&vdev->dev);
- unmap_persist_bufs(hostdata);
+
+ srp_remove_host(hostdata->host);
+ scsi_remove_host(hostdata->host);
+
+ purge_requests(hostdata, DID_ERROR);
release_event_pool(&hostdata->pool, hostdata);
- ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata,
+
+ ibmvscsi_release_crq_queue(&hostdata->queue, hostdata,
max_events);
kthread_stop(hostdata->work_thread);
- srp_remove_host(hostdata->host);
- scsi_remove_host(hostdata->host);
- scsi_host_put(hostdata->host);
+ unmap_persist_bufs(hostdata);
- return 0;
+ spin_lock(&ibmvscsi_driver_lock);
+ list_del(&hostdata->host_list);
+ spin_unlock(&ibmvscsi_driver_lock);
+
+ scsi_host_put(hostdata->host);
}
/**
@@ -2045,20 +2373,23 @@ static int ibmvscsi_remove(struct vio_dev *vdev)
static int ibmvscsi_resume(struct device *dev)
{
struct ibmvscsi_host_data *hostdata = dev_get_drvdata(dev);
- return ibmvscsi_ops->resume(hostdata);
+ vio_disable_interrupts(to_vio_dev(hostdata->dev));
+ tasklet_schedule(&hostdata->srp_task);
+
+ return 0;
}
-/**
+/*
* ibmvscsi_device_table: Used by vio.c to match devices in the device tree we
* support.
*/
-static struct vio_device_id ibmvscsi_device_table[] __devinitdata = {
+static const struct vio_device_id ibmvscsi_device_table[] = {
{"vscsi", "IBM,v-scsi"},
{ "", "" }
};
MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table);
-static struct dev_pm_ops ibmvscsi_pm_ops = {
+static const struct dev_pm_ops ibmvscsi_pm_ops = {
.resume = ibmvscsi_resume
};
@@ -2067,17 +2398,14 @@ static struct vio_driver ibmvscsi_driver = {
.probe = ibmvscsi_probe,
.remove = ibmvscsi_remove,
.get_desired_dma = ibmvscsi_get_desired_dma,
- .driver = {
- .name = "ibmvscsi",
- .owner = THIS_MODULE,
- .pm = &ibmvscsi_pm_ops,
- }
+ .name = "ibmvscsi",
+ .pm = &ibmvscsi_pm_ops,
};
static struct srp_function_template ibmvscsi_transport_functions = {
};
-int __init ibmvscsi_module_init(void)
+static int __init ibmvscsi_module_init(void)
{
int ret;
@@ -2085,11 +2413,7 @@ int __init ibmvscsi_module_init(void)
driver_template.can_queue = max_requests;
max_events = max_requests + 2;
- if (firmware_has_feature(FW_FEATURE_ISERIES))
- ibmvscsi_ops = &iseriesvscsi_ops;
- else if (firmware_has_feature(FW_FEATURE_VIO))
- ibmvscsi_ops = &rpavscsi_ops;
- else
+ if (!firmware_has_feature(FW_FEATURE_VIO))
return -ENODEV;
ibmvscsi_transport_template =
@@ -2103,7 +2427,7 @@ int __init ibmvscsi_module_init(void)
return ret;
}
-void __exit ibmvscsi_module_exit(void)
+static void __exit ibmvscsi_module_exit(void)
{
vio_unregister_driver(&ibmvscsi_driver);
srp_release_transport(ibmvscsi_transport_template);
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h
index 02197a2b22b9..e60916ef7a49 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.h
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* ------------------------------------------------------------
* ibmvscsi.h
* (C) Copyright IBM Corporation 1994, 2003
@@ -5,21 +6,6 @@
* Santiago Leon (santil@us.ibm.com)
* Dave Boutcher (sleddog@us.ibm.com)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
* ------------------------------------------------------------
* Emulation of a SCSI host adapter for Virtual I/O devices
*
@@ -33,7 +19,7 @@
#include <linux/list.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
-#include "viosrp.h"
+#include <scsi/viosrp.h>
struct scsi_cmnd;
struct Scsi_Host;
@@ -48,6 +34,7 @@ struct Scsi_Host;
#define IBMVSCSI_CMDS_PER_LUN_DEFAULT 16
#define IBMVSCSI_MAX_SECTORS_DEFAULT 256 /* 32 * 8 = default max I/O 32 pages */
#define IBMVSCSI_MAX_CMDS_PER_LUN 64
+#define IBMVSCSI_MAX_LUN 32
/* ------------------------------------------------------------
* Data Structures
@@ -87,12 +74,19 @@ struct event_pool {
dma_addr_t iu_token;
};
+enum ibmvscsi_host_action {
+ IBMVSCSI_HOST_ACTION_NONE = 0,
+ IBMVSCSI_HOST_ACTION_RESET,
+ IBMVSCSI_HOST_ACTION_REENABLE,
+ IBMVSCSI_HOST_ACTION_UNBLOCK,
+};
+
/* all driver data associated with a host adapter */
struct ibmvscsi_host_data {
+ struct list_head host_list;
atomic_t request_limit;
int client_migrated;
- int reset_crq;
- int reenable_crq;
+ enum ibmvscsi_host_action action;
struct device *dev;
struct event_pool pool;
struct crq_queue queue;
@@ -107,27 +101,4 @@ struct ibmvscsi_host_data {
dma_addr_t adapter_info_addr;
};
-/* routines for managing a command/response queue */
-void ibmvscsi_handle_crq(struct viosrp_crq *crq,
- struct ibmvscsi_host_data *hostdata);
-
-struct ibmvscsi_ops {
- int (*init_crq_queue)(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata,
- int max_requests);
- void (*release_crq_queue)(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata,
- int max_requests);
- int (*reset_crq_queue)(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata);
- int (*reenable_crq_queue)(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata);
- int (*send_crq)(struct ibmvscsi_host_data *hostdata,
- u64 word1, u64 word2);
- int (*resume) (struct ibmvscsi_host_data *hostdata);
-};
-
-extern struct ibmvscsi_ops iseriesvscsi_ops;
-extern struct ibmvscsi_ops rpavscsi_ops;
-
#endif /* IBMVSCSI_H */
diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c
deleted file mode 100644
index 2256babe0474..000000000000
--- a/drivers/scsi/ibmvscsi/ibmvstgt.c
+++ /dev/null
@@ -1,1004 +0,0 @@
-/*
- * IBM eServer i/pSeries Virtual SCSI Target Driver
- * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp.
- * Santiago Leon (santil@us.ibm.com) IBM Corp.
- * Linda Xie (lxie@us.ibm.com) IBM Corp.
- *
- * Copyright (C) 2005-2006 FUJITA Tomonori <tomof@acm.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_transport_srp.h>
-#include <scsi/scsi_tgt.h>
-#include <scsi/libsrp.h>
-#include <asm/hvcall.h>
-#include <asm/iommu.h>
-#include <asm/prom.h>
-#include <asm/vio.h>
-
-#include "ibmvscsi.h"
-
-#define INITIAL_SRP_LIMIT 16
-#define DEFAULT_MAX_SECTORS 256
-
-#define TGT_NAME "ibmvstgt"
-
-/*
- * Hypervisor calls.
- */
-#define h_copy_rdma(l, sa, sb, da, db) \
- plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db)
-#define h_send_crq(ua, l, h) \
- plpar_hcall_norets(H_SEND_CRQ, ua, l, h)
-#define h_reg_crq(ua, tok, sz)\
- plpar_hcall_norets(H_REG_CRQ, ua, tok, sz);
-#define h_free_crq(ua) \
- plpar_hcall_norets(H_FREE_CRQ, ua);
-
-/* tmp - will replace with SCSI logging stuff */
-#define eprintk(fmt, args...) \
-do { \
- printk("%s(%d) " fmt, __func__, __LINE__, ##args); \
-} while (0)
-/* #define dprintk eprintk */
-#define dprintk(fmt, args...)
-
-struct vio_port {
- struct vio_dev *dma_dev;
-
- struct crq_queue crq_queue;
- struct work_struct crq_work;
-
- unsigned long liobn;
- unsigned long riobn;
- struct srp_target *target;
-
- struct srp_rport *rport;
-};
-
-static struct workqueue_struct *vtgtd;
-static struct scsi_transport_template *ibmvstgt_transport_template;
-
-/*
- * These are fixed for the system and come from the Open Firmware device tree.
- * We just store them here to save getting them every time.
- */
-static char system_id[64] = "";
-static char partition_name[97] = "UNKNOWN";
-static unsigned int partition_number = -1;
-
-static struct vio_port *target_to_port(struct srp_target *target)
-{
- return (struct vio_port *) target->ldata;
-}
-
-static inline union viosrp_iu *vio_iu(struct iu_entry *iue)
-{
- return (union viosrp_iu *) (iue->sbuf->buf);
-}
-
-static int send_iu(struct iu_entry *iue, uint64_t length, uint8_t format)
-{
- struct srp_target *target = iue->target;
- struct vio_port *vport = target_to_port(target);
- long rc, rc1;
- union {
- struct viosrp_crq cooked;
- uint64_t raw[2];
- } crq;
-
- /* First copy the SRP */
- rc = h_copy_rdma(length, vport->liobn, iue->sbuf->dma,
- vport->riobn, iue->remote_token);
-
- if (rc)
- eprintk("Error %ld transferring data\n", rc);
-
- crq.cooked.valid = 0x80;
- crq.cooked.format = format;
- crq.cooked.reserved = 0x00;
- crq.cooked.timeout = 0x00;
- crq.cooked.IU_length = length;
- crq.cooked.IU_data_ptr = vio_iu(iue)->srp.rsp.tag;
-
- if (rc == 0)
- crq.cooked.status = 0x99; /* Just needs to be non-zero */
- else
- crq.cooked.status = 0x00;
-
- rc1 = h_send_crq(vport->dma_dev->unit_address, crq.raw[0], crq.raw[1]);
-
- if (rc1) {
- eprintk("%ld sending response\n", rc1);
- return rc1;
- }
-
- return rc;
-}
-
-#define SRP_RSP_SENSE_DATA_LEN 18
-
-static int send_rsp(struct iu_entry *iue, struct scsi_cmnd *sc,
- unsigned char status, unsigned char asc)
-{
- union viosrp_iu *iu = vio_iu(iue);
- uint64_t tag = iu->srp.rsp.tag;
-
- /* If the linked bit is on and status is good */
- if (test_bit(V_LINKED, &iue->flags) && (status == NO_SENSE))
- status = 0x10;
-
- memset(iu, 0, sizeof(struct srp_rsp));
- iu->srp.rsp.opcode = SRP_RSP;
- iu->srp.rsp.req_lim_delta = 1;
- iu->srp.rsp.tag = tag;
-
- if (test_bit(V_DIOVER, &iue->flags))
- iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER;
-
- iu->srp.rsp.data_in_res_cnt = 0;
- iu->srp.rsp.data_out_res_cnt = 0;
-
- iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID;
-
- iu->srp.rsp.resp_data_len = 0;
- iu->srp.rsp.status = status;
- if (status) {
- uint8_t *sense = iu->srp.rsp.data;
-
- if (sc) {
- iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
- iu->srp.rsp.sense_data_len = SCSI_SENSE_BUFFERSIZE;
- memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE);
- } else {
- iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION;
- iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
- iu->srp.rsp.sense_data_len = SRP_RSP_SENSE_DATA_LEN;
-
- /* Valid bit and 'current errors' */
- sense[0] = (0x1 << 7 | 0x70);
- /* Sense key */
- sense[2] = status;
- /* Additional sense length */
- sense[7] = 0xa; /* 10 bytes */
- /* Additional sense code */
- sense[12] = asc;
- }
- }
-
- send_iu(iue, sizeof(iu->srp.rsp) + SRP_RSP_SENSE_DATA_LEN,
- VIOSRP_SRP_FORMAT);
-
- return 0;
-}
-
-static void handle_cmd_queue(struct srp_target *target)
-{
- struct Scsi_Host *shost = target->shost;
- struct srp_rport *rport = target_to_port(target)->rport;
- struct iu_entry *iue;
- struct srp_cmd *cmd;
- unsigned long flags;
- int err;
-
-retry:
- spin_lock_irqsave(&target->lock, flags);
-
- list_for_each_entry(iue, &target->cmd_queue, ilist) {
- if (!test_and_set_bit(V_FLYING, &iue->flags)) {
- spin_unlock_irqrestore(&target->lock, flags);
- cmd = iue->sbuf->buf;
- err = srp_cmd_queue(shost, cmd, iue,
- (unsigned long)rport, 0);
- if (err) {
- eprintk("cannot queue cmd %p %d\n", cmd, err);
- srp_iu_put(iue);
- }
- goto retry;
- }
- }
-
- spin_unlock_irqrestore(&target->lock, flags);
-}
-
-static int ibmvstgt_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg,
- struct srp_direct_buf *md, int nmd,
- enum dma_data_direction dir, unsigned int rest)
-{
- struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
- struct srp_target *target = iue->target;
- struct vio_port *vport = target_to_port(target);
- dma_addr_t token;
- long err;
- unsigned int done = 0;
- int i, sidx, soff;
-
- sidx = soff = 0;
- token = sg_dma_address(sg + sidx);
-
- for (i = 0; i < nmd && rest; i++) {
- unsigned int mdone, mlen;
-
- mlen = min(rest, md[i].len);
- for (mdone = 0; mlen;) {
- int slen = min(sg_dma_len(sg + sidx) - soff, mlen);
-
- if (dir == DMA_TO_DEVICE)
- err = h_copy_rdma(slen,
- vport->riobn,
- md[i].va + mdone,
- vport->liobn,
- token + soff);
- else
- err = h_copy_rdma(slen,
- vport->liobn,
- token + soff,
- vport->riobn,
- md[i].va + mdone);
-
- if (err != H_SUCCESS) {
- eprintk("rdma error %d %d %ld\n", dir, slen, err);
- return -EIO;
- }
-
- mlen -= slen;
- mdone += slen;
- soff += slen;
- done += slen;
-
- if (soff == sg_dma_len(sg + sidx)) {
- sidx++;
- soff = 0;
- token = sg_dma_address(sg + sidx);
-
- if (sidx > nsg) {
- eprintk("out of sg %p %d %d\n",
- iue, sidx, nsg);
- return -EIO;
- }
- }
- };
-
- rest -= mlen;
- }
- return 0;
-}
-
-static int ibmvstgt_cmd_done(struct scsi_cmnd *sc,
- void (*done)(struct scsi_cmnd *))
-{
- unsigned long flags;
- struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
- struct srp_target *target = iue->target;
- int err = 0;
-
- dprintk("%p %p %x %u\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0],
- scsi_sg_count(sc));
-
- if (scsi_sg_count(sc))
- err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, ibmvstgt_rdma, 1, 1);
-
- spin_lock_irqsave(&target->lock, flags);
- list_del(&iue->ilist);
- spin_unlock_irqrestore(&target->lock, flags);
-
- if (err|| sc->result != SAM_STAT_GOOD) {
- eprintk("operation failed %p %d %x\n",
- iue, sc->result, vio_iu(iue)->srp.cmd.cdb[0]);
- send_rsp(iue, sc, HARDWARE_ERROR, 0x00);
- } else
- send_rsp(iue, sc, NO_SENSE, 0x00);
-
- done(sc);
- srp_iu_put(iue);
- return 0;
-}
-
-int send_adapter_info(struct iu_entry *iue,
- dma_addr_t remote_buffer, uint16_t length)
-{
- struct srp_target *target = iue->target;
- struct vio_port *vport = target_to_port(target);
- struct Scsi_Host *shost = target->shost;
- dma_addr_t data_token;
- struct mad_adapter_info_data *info;
- int err;
-
- info = dma_alloc_coherent(target->dev, sizeof(*info), &data_token,
- GFP_KERNEL);
- if (!info) {
- eprintk("bad dma_alloc_coherent %p\n", target);
- return 1;
- }
-
- /* Get remote info */
- err = h_copy_rdma(sizeof(*info), vport->riobn, remote_buffer,
- vport->liobn, data_token);
- if (err == H_SUCCESS) {
- dprintk("Client connect: %s (%d)\n",
- info->partition_name, info->partition_number);
- }
-
- memset(info, 0, sizeof(*info));
-
- strcpy(info->srp_version, "16.a");
- strncpy(info->partition_name, partition_name,
- sizeof(info->partition_name));
- info->partition_number = partition_number;
- info->mad_version = 1;
- info->os_type = 2;
- info->port_max_txu[0] = shost->hostt->max_sectors << 9;
-
- /* Send our info to remote */
- err = h_copy_rdma(sizeof(*info), vport->liobn, data_token,
- vport->riobn, remote_buffer);
-
- dma_free_coherent(target->dev, sizeof(*info), info, data_token);
-
- if (err != H_SUCCESS) {
- eprintk("Error sending adapter info %d\n", err);
- return 1;
- }
-
- return 0;
-}
-
-static void process_login(struct iu_entry *iue)
-{
- union viosrp_iu *iu = vio_iu(iue);
- struct srp_login_rsp *rsp = &iu->srp.login_rsp;
- uint64_t tag = iu->srp.rsp.tag;
- struct Scsi_Host *shost = iue->target->shost;
- struct srp_target *target = host_to_srp_target(shost);
- struct vio_port *vport = target_to_port(target);
- struct srp_rport_identifiers ids;
-
- memset(&ids, 0, sizeof(ids));
- sprintf(ids.port_id, "%x", vport->dma_dev->unit_address);
- ids.roles = SRP_RPORT_ROLE_INITIATOR;
- if (!vport->rport)
- vport->rport = srp_rport_add(shost, &ids);
-
- /* TODO handle case that requested size is wrong and
- * buffer format is wrong
- */
- memset(iu, 0, sizeof(struct srp_login_rsp));
- rsp->opcode = SRP_LOGIN_RSP;
- rsp->req_lim_delta = INITIAL_SRP_LIMIT;
- rsp->tag = tag;
- rsp->max_it_iu_len = sizeof(union srp_iu);
- rsp->max_ti_iu_len = sizeof(union srp_iu);
- /* direct and indirect */
- rsp->buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT;
-
- send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT);
-}
-
-static inline void queue_cmd(struct iu_entry *iue)
-{
- struct srp_target *target = iue->target;
- unsigned long flags;
-
- spin_lock_irqsave(&target->lock, flags);
- list_add_tail(&iue->ilist, &target->cmd_queue);
- spin_unlock_irqrestore(&target->lock, flags);
-}
-
-static int process_tsk_mgmt(struct iu_entry *iue)
-{
- union viosrp_iu *iu = vio_iu(iue);
- int fn;
-
- dprintk("%p %u\n", iue, iu->srp.tsk_mgmt.tsk_mgmt_func);
-
- switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
- case SRP_TSK_ABORT_TASK:
- fn = ABORT_TASK;
- break;
- case SRP_TSK_ABORT_TASK_SET:
- fn = ABORT_TASK_SET;
- break;
- case SRP_TSK_CLEAR_TASK_SET:
- fn = CLEAR_TASK_SET;
- break;
- case SRP_TSK_LUN_RESET:
- fn = LOGICAL_UNIT_RESET;
- break;
- case SRP_TSK_CLEAR_ACA:
- fn = CLEAR_ACA;
- break;
- default:
- fn = 0;
- }
- if (fn)
- scsi_tgt_tsk_mgmt_request(iue->target->shost,
- (unsigned long)iue->target->shost,
- fn,
- iu->srp.tsk_mgmt.task_tag,
- (struct scsi_lun *) &iu->srp.tsk_mgmt.lun,
- iue);
- else
- send_rsp(iue, NULL, ILLEGAL_REQUEST, 0x20);
-
- return !fn;
-}
-
-static int process_mad_iu(struct iu_entry *iue)
-{
- union viosrp_iu *iu = vio_iu(iue);
- struct viosrp_adapter_info *info;
- struct viosrp_host_config *conf;
-
- switch (iu->mad.empty_iu.common.type) {
- case VIOSRP_EMPTY_IU_TYPE:
- eprintk("%s\n", "Unsupported EMPTY MAD IU");
- break;
- case VIOSRP_ERROR_LOG_TYPE:
- eprintk("%s\n", "Unsupported ERROR LOG MAD IU");
- iu->mad.error_log.common.status = 1;
- send_iu(iue, sizeof(iu->mad.error_log), VIOSRP_MAD_FORMAT);
- break;
- case VIOSRP_ADAPTER_INFO_TYPE:
- info = &iu->mad.adapter_info;
- info->common.status = send_adapter_info(iue, info->buffer,
- info->common.length);
- send_iu(iue, sizeof(*info), VIOSRP_MAD_FORMAT);
- break;
- case VIOSRP_HOST_CONFIG_TYPE:
- conf = &iu->mad.host_config;
- conf->common.status = 1;
- send_iu(iue, sizeof(*conf), VIOSRP_MAD_FORMAT);
- break;
- default:
- eprintk("Unknown type %u\n", iu->srp.rsp.opcode);
- }
-
- return 1;
-}
-
-static int process_srp_iu(struct iu_entry *iue)
-{
- union viosrp_iu *iu = vio_iu(iue);
- int done = 1;
- u8 opcode = iu->srp.rsp.opcode;
-
- switch (opcode) {
- case SRP_LOGIN_REQ:
- process_login(iue);
- break;
- case SRP_TSK_MGMT:
- done = process_tsk_mgmt(iue);
- break;
- case SRP_CMD:
- queue_cmd(iue);
- done = 0;
- break;
- case SRP_LOGIN_RSP:
- case SRP_I_LOGOUT:
- case SRP_T_LOGOUT:
- case SRP_RSP:
- case SRP_CRED_REQ:
- case SRP_CRED_RSP:
- case SRP_AER_REQ:
- case SRP_AER_RSP:
- eprintk("Unsupported type %u\n", opcode);
- break;
- default:
- eprintk("Unknown type %u\n", opcode);
- }
-
- return done;
-}
-
-static void process_iu(struct viosrp_crq *crq, struct srp_target *target)
-{
- struct vio_port *vport = target_to_port(target);
- struct iu_entry *iue;
- long err;
- int done = 1;
-
- iue = srp_iu_get(target);
- if (!iue) {
- eprintk("Error getting IU from pool, %p\n", target);
- return;
- }
-
- iue->remote_token = crq->IU_data_ptr;
-
- err = h_copy_rdma(crq->IU_length, vport->riobn,
- iue->remote_token, vport->liobn, iue->sbuf->dma);
-
- if (err != H_SUCCESS) {
- eprintk("%ld transferring data error %p\n", err, iue);
- goto out;
- }
-
- if (crq->format == VIOSRP_MAD_FORMAT)
- done = process_mad_iu(iue);
- else
- done = process_srp_iu(iue);
-out:
- if (done)
- srp_iu_put(iue);
-}
-
-static irqreturn_t ibmvstgt_interrupt(int dummy, void *data)
-{
- struct srp_target *target = data;
- struct vio_port *vport = target_to_port(target);
-
- vio_disable_interrupts(vport->dma_dev);
- queue_work(vtgtd, &vport->crq_work);
-
- return IRQ_HANDLED;
-}
-
-static int crq_queue_create(struct crq_queue *queue, struct srp_target *target)
-{
- int err;
- struct vio_port *vport = target_to_port(target);
-
- queue->msgs = (struct viosrp_crq *) get_zeroed_page(GFP_KERNEL);
- if (!queue->msgs)
- goto malloc_failed;
- queue->size = PAGE_SIZE / sizeof(*queue->msgs);
-
- queue->msg_token = dma_map_single(target->dev, queue->msgs,
- queue->size * sizeof(*queue->msgs),
- DMA_BIDIRECTIONAL);
-
- if (dma_mapping_error(target->dev, queue->msg_token))
- goto map_failed;
-
- err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token,
- PAGE_SIZE);
-
- /* If the adapter was left active for some reason (like kexec)
- * try freeing and re-registering
- */
- if (err == H_RESOURCE) {
- do {
- err = h_free_crq(vport->dma_dev->unit_address);
- } while (err == H_BUSY || H_IS_LONG_BUSY(err));
-
- err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token,
- PAGE_SIZE);
- }
-
- if (err != H_SUCCESS && err != 2) {
- eprintk("Error 0x%x opening virtual adapter\n", err);
- goto reg_crq_failed;
- }
-
- err = request_irq(vport->dma_dev->irq, &ibmvstgt_interrupt,
- IRQF_DISABLED, "ibmvstgt", target);
- if (err)
- goto req_irq_failed;
-
- vio_enable_interrupts(vport->dma_dev);
-
- h_send_crq(vport->dma_dev->unit_address, 0xC001000000000000, 0);
-
- queue->cur = 0;
- spin_lock_init(&queue->lock);
-
- return 0;
-
-req_irq_failed:
- do {
- err = h_free_crq(vport->dma_dev->unit_address);
- } while (err == H_BUSY || H_IS_LONG_BUSY(err));
-
-reg_crq_failed:
- dma_unmap_single(target->dev, queue->msg_token,
- queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
-map_failed:
- free_page((unsigned long) queue->msgs);
-
-malloc_failed:
- return -ENOMEM;
-}
-
-static void crq_queue_destroy(struct srp_target *target)
-{
- struct vio_port *vport = target_to_port(target);
- struct crq_queue *queue = &vport->crq_queue;
- int err;
-
- free_irq(vport->dma_dev->irq, target);
- do {
- err = h_free_crq(vport->dma_dev->unit_address);
- } while (err == H_BUSY || H_IS_LONG_BUSY(err));
-
- dma_unmap_single(target->dev, queue->msg_token,
- queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
-
- free_page((unsigned long) queue->msgs);
-}
-
-static void process_crq(struct viosrp_crq *crq, struct srp_target *target)
-{
- struct vio_port *vport = target_to_port(target);
- dprintk("%x %x\n", crq->valid, crq->format);
-
- switch (crq->valid) {
- case 0xC0:
- /* initialization */
- switch (crq->format) {
- case 0x01:
- h_send_crq(vport->dma_dev->unit_address,
- 0xC002000000000000, 0);
- break;
- case 0x02:
- break;
- default:
- eprintk("Unknown format %u\n", crq->format);
- }
- break;
- case 0xFF:
- /* transport event */
- break;
- case 0x80:
- /* real payload */
- switch (crq->format) {
- case VIOSRP_SRP_FORMAT:
- case VIOSRP_MAD_FORMAT:
- process_iu(crq, target);
- break;
- case VIOSRP_OS400_FORMAT:
- case VIOSRP_AIX_FORMAT:
- case VIOSRP_LINUX_FORMAT:
- case VIOSRP_INLINE_FORMAT:
- eprintk("Unsupported format %u\n", crq->format);
- break;
- default:
- eprintk("Unknown format %u\n", crq->format);
- }
- break;
- default:
- eprintk("unknown message type 0x%02x!?\n", crq->valid);
- }
-}
-
-static inline struct viosrp_crq *next_crq(struct crq_queue *queue)
-{
- struct viosrp_crq *crq;
- unsigned long flags;
-
- spin_lock_irqsave(&queue->lock, flags);
- crq = &queue->msgs[queue->cur];
- if (crq->valid & 0x80) {
- if (++queue->cur == queue->size)
- queue->cur = 0;
- } else
- crq = NULL;
- spin_unlock_irqrestore(&queue->lock, flags);
-
- return crq;
-}
-
-static void handle_crq(struct work_struct *work)
-{
- struct vio_port *vport = container_of(work, struct vio_port, crq_work);
- struct srp_target *target = vport->target;
- struct viosrp_crq *crq;
- int done = 0;
-
- while (!done) {
- while ((crq = next_crq(&vport->crq_queue)) != NULL) {
- process_crq(crq, target);
- crq->valid = 0x00;
- }
-
- vio_enable_interrupts(vport->dma_dev);
-
- crq = next_crq(&vport->crq_queue);
- if (crq) {
- vio_disable_interrupts(vport->dma_dev);
- process_crq(crq, target);
- crq->valid = 0x00;
- } else
- done = 1;
- }
-
- handle_cmd_queue(target);
-}
-
-
-static int ibmvstgt_eh_abort_handler(struct scsi_cmnd *sc)
-{
- unsigned long flags;
- struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
- struct srp_target *target = iue->target;
-
- dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]);
-
- spin_lock_irqsave(&target->lock, flags);
- list_del(&iue->ilist);
- spin_unlock_irqrestore(&target->lock, flags);
-
- srp_iu_put(iue);
-
- return 0;
-}
-
-static int ibmvstgt_tsk_mgmt_response(struct Scsi_Host *shost,
- u64 itn_id, u64 mid, int result)
-{
- struct iu_entry *iue = (struct iu_entry *) ((void *) mid);
- union viosrp_iu *iu = vio_iu(iue);
- unsigned char status, asc;
-
- eprintk("%p %d\n", iue, result);
- status = NO_SENSE;
- asc = 0;
-
- switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
- case SRP_TSK_ABORT_TASK:
- asc = 0x14;
- if (result)
- status = ABORTED_COMMAND;
- break;
- default:
- break;
- }
-
- send_rsp(iue, NULL, status, asc);
- srp_iu_put(iue);
-
- return 0;
-}
-
-static int ibmvstgt_it_nexus_response(struct Scsi_Host *shost, u64 itn_id,
- int result)
-{
- struct srp_target *target = host_to_srp_target(shost);
- struct vio_port *vport = target_to_port(target);
-
- if (result) {
- eprintk("%p %d\n", shost, result);
- srp_rport_del(vport->rport);
- vport->rport = NULL;
- }
- return 0;
-}
-
-static ssize_t system_id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%s\n", system_id);
-}
-
-static ssize_t partition_number_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%x\n", partition_number);
-}
-
-static ssize_t unit_address_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct srp_target *target = host_to_srp_target(shost);
- struct vio_port *vport = target_to_port(target);
- return snprintf(buf, PAGE_SIZE, "%x\n", vport->dma_dev->unit_address);
-}
-
-static DEVICE_ATTR(system_id, S_IRUGO, system_id_show, NULL);
-static DEVICE_ATTR(partition_number, S_IRUGO, partition_number_show, NULL);
-static DEVICE_ATTR(unit_address, S_IRUGO, unit_address_show, NULL);
-
-static struct device_attribute *ibmvstgt_attrs[] = {
- &dev_attr_system_id,
- &dev_attr_partition_number,
- &dev_attr_unit_address,
- NULL,
-};
-
-static struct scsi_host_template ibmvstgt_sht = {
- .name = TGT_NAME,
- .module = THIS_MODULE,
- .can_queue = INITIAL_SRP_LIMIT,
- .sg_tablesize = SG_ALL,
- .use_clustering = DISABLE_CLUSTERING,
- .max_sectors = DEFAULT_MAX_SECTORS,
- .transfer_response = ibmvstgt_cmd_done,
- .eh_abort_handler = ibmvstgt_eh_abort_handler,
- .shost_attrs = ibmvstgt_attrs,
- .proc_name = TGT_NAME,
- .supported_mode = MODE_TARGET,
-};
-
-static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id)
-{
- struct Scsi_Host *shost;
- struct srp_target *target;
- struct vio_port *vport;
- unsigned int *dma, dma_size;
- int err = -ENOMEM;
-
- vport = kzalloc(sizeof(struct vio_port), GFP_KERNEL);
- if (!vport)
- return err;
- shost = scsi_host_alloc(&ibmvstgt_sht, sizeof(struct srp_target));
- if (!shost)
- goto free_vport;
- shost->transportt = ibmvstgt_transport_template;
-
- target = host_to_srp_target(shost);
- target->shost = shost;
- vport->dma_dev = dev;
- target->ldata = vport;
- vport->target = target;
- err = srp_target_alloc(target, &dev->dev, INITIAL_SRP_LIMIT,
- SRP_MAX_IU_LEN);
- if (err)
- goto put_host;
-
- dma = (unsigned int *) vio_get_attribute(dev, "ibm,my-dma-window",
- &dma_size);
- if (!dma || dma_size != 40) {
- eprintk("Couldn't get window property %d\n", dma_size);
- err = -EIO;
- goto free_srp_target;
- }
- vport->liobn = dma[0];
- vport->riobn = dma[5];
-
- INIT_WORK(&vport->crq_work, handle_crq);
-
- err = scsi_add_host(shost, target->dev);
- if (err)
- goto free_srp_target;
-
- err = scsi_tgt_alloc_queue(shost);
- if (err)
- goto remove_host;
-
- err = crq_queue_create(&vport->crq_queue, target);
- if (err)
- goto free_queue;
-
- return 0;
-free_queue:
- scsi_tgt_free_queue(shost);
-remove_host:
- scsi_remove_host(shost);
-free_srp_target:
- srp_target_free(target);
-put_host:
- scsi_host_put(shost);
-free_vport:
- kfree(vport);
- return err;
-}
-
-static int ibmvstgt_remove(struct vio_dev *dev)
-{
- struct srp_target *target = dev_get_drvdata(&dev->dev);
- struct Scsi_Host *shost = target->shost;
- struct vio_port *vport = target->ldata;
-
- crq_queue_destroy(target);
- srp_remove_host(shost);
- scsi_remove_host(shost);
- scsi_tgt_free_queue(shost);
- srp_target_free(target);
- kfree(vport);
- scsi_host_put(shost);
- return 0;
-}
-
-static struct vio_device_id ibmvstgt_device_table[] __devinitdata = {
- {"v-scsi-host", "IBM,v-scsi-host"},
- {"",""}
-};
-
-MODULE_DEVICE_TABLE(vio, ibmvstgt_device_table);
-
-static struct vio_driver ibmvstgt_driver = {
- .id_table = ibmvstgt_device_table,
- .probe = ibmvstgt_probe,
- .remove = ibmvstgt_remove,
- .driver = {
- .name = "ibmvscsis",
- .owner = THIS_MODULE,
- }
-};
-
-static int get_system_info(void)
-{
- struct device_node *rootdn;
- const char *id, *model, *name;
- const unsigned int *num;
-
- rootdn = of_find_node_by_path("/");
- if (!rootdn)
- return -ENOENT;
-
- model = of_get_property(rootdn, "model", NULL);
- id = of_get_property(rootdn, "system-id", NULL);
- if (model && id)
- snprintf(system_id, sizeof(system_id), "%s-%s", model, id);
-
- name = of_get_property(rootdn, "ibm,partition-name", NULL);
- if (name)
- strncpy(partition_name, name, sizeof(partition_name));
-
- num = of_get_property(rootdn, "ibm,partition-no", NULL);
- if (num)
- partition_number = *num;
-
- of_node_put(rootdn);
- return 0;
-}
-
-static struct srp_function_template ibmvstgt_transport_functions = {
- .tsk_mgmt_response = ibmvstgt_tsk_mgmt_response,
- .it_nexus_response = ibmvstgt_it_nexus_response,
-};
-
-static int __init ibmvstgt_init(void)
-{
- int err = -ENOMEM;
-
- printk("IBM eServer i/pSeries Virtual SCSI Target Driver\n");
-
- ibmvstgt_transport_template =
- srp_attach_transport(&ibmvstgt_transport_functions);
- if (!ibmvstgt_transport_template)
- return err;
-
- vtgtd = create_workqueue("ibmvtgtd");
- if (!vtgtd)
- goto release_transport;
-
- err = get_system_info();
- if (err)
- goto destroy_wq;
-
- err = vio_register_driver(&ibmvstgt_driver);
- if (err)
- goto destroy_wq;
-
- return 0;
-destroy_wq:
- destroy_workqueue(vtgtd);
-release_transport:
- srp_release_transport(ibmvstgt_transport_template);
- return err;
-}
-
-static void __exit ibmvstgt_exit(void)
-{
- printk("Unregister IBM virtual SCSI driver\n");
-
- destroy_workqueue(vtgtd);
- vio_unregister_driver(&ibmvstgt_driver);
- srp_release_transport(ibmvstgt_transport_template);
-}
-
-MODULE_DESCRIPTION("IBM Virtual SCSI Target");
-MODULE_AUTHOR("Santiago Leon");
-MODULE_LICENSE("GPL");
-
-module_init(ibmvstgt_init);
-module_exit(ibmvstgt_exit);
diff --git a/drivers/scsi/ibmvscsi/iseries_vscsi.c b/drivers/scsi/ibmvscsi/iseries_vscsi.c
deleted file mode 100644
index f4776451a754..000000000000
--- a/drivers/scsi/ibmvscsi/iseries_vscsi.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/* ------------------------------------------------------------
- * iSeries_vscsi.c
- * (C) Copyright IBM Corporation 1994, 2003
- * Authors: Colin DeVilbiss (devilbis@us.ibm.com)
- * Santiago Leon (santil@us.ibm.com)
- * Dave Boutcher (sleddog@us.ibm.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
- * ------------------------------------------------------------
- * iSeries-specific functions of the SCSI host adapter for Virtual I/O devices
- *
- * This driver allows the Linux SCSI peripheral drivers to directly
- * access devices in the hosting partition, either on an iSeries
- * hypervisor system or a converged hypervisor system.
- */
-
-#include <asm/iseries/vio.h>
-#include <asm/iseries/hv_lp_event.h>
-#include <asm/iseries/hv_types.h>
-#include <asm/iseries/hv_lp_config.h>
-#include <asm/vio.h>
-#include <linux/device.h>
-#include "ibmvscsi.h"
-
-/* global variables */
-static struct ibmvscsi_host_data *single_host_data;
-
-/* ------------------------------------------------------------
- * Routines for direct interpartition interaction
- */
-struct srp_lp_event {
- struct HvLpEvent lpevt; /* 0x00-0x17 */
- u32 reserved1; /* 0x18-0x1B; unused */
- u16 version; /* 0x1C-0x1D; unused */
- u16 subtype_rc; /* 0x1E-0x1F; unused */
- struct viosrp_crq crq; /* 0x20-0x3F */
-};
-
-/**
- * standard interface for handling logical partition events.
- */
-static void iseriesvscsi_handle_event(struct HvLpEvent *lpevt)
-{
- struct srp_lp_event *evt = (struct srp_lp_event *)lpevt;
-
- if (!evt) {
- printk(KERN_ERR "ibmvscsi: received null event\n");
- return;
- }
-
- if (single_host_data == NULL) {
- printk(KERN_ERR
- "ibmvscsi: received event, no adapter present\n");
- return;
- }
-
- ibmvscsi_handle_crq(&evt->crq, single_host_data);
-}
-
-/* ------------------------------------------------------------
- * Routines for driver initialization
- */
-static int iseriesvscsi_init_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata,
- int max_requests)
-{
- int rc;
-
- single_host_data = hostdata;
- rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, max_requests);
- if (rc < 0) {
- printk("viopath_open failed with rc %d in open_event_path\n",
- rc);
- goto viopath_open_failed;
- }
-
- rc = vio_setHandler(viomajorsubtype_scsi, iseriesvscsi_handle_event);
- if (rc < 0) {
- printk("vio_setHandler failed with rc %d in open_event_path\n",
- rc);
- goto vio_setHandler_failed;
- }
- return 0;
-
- vio_setHandler_failed:
- viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests);
- viopath_open_failed:
- return -1;
-}
-
-static void iseriesvscsi_release_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata,
- int max_requests)
-{
- vio_clearHandler(viomajorsubtype_scsi);
- viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests);
-}
-
-/**
- * reset_crq_queue: - resets a crq after a failure
- * @queue: crq_queue to initialize and register
- * @hostdata: ibmvscsi_host_data of host
- *
- * no-op for iSeries
- */
-static int iseriesvscsi_reset_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata)
-{
- return 0;
-}
-
-/**
- * reenable_crq_queue: - reenables a crq after a failure
- * @queue: crq_queue to initialize and register
- * @hostdata: ibmvscsi_host_data of host
- *
- * no-op for iSeries
- */
-static int iseriesvscsi_reenable_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata)
-{
- return 0;
-}
-
-/**
- * iseriesvscsi_send_crq: - Send a CRQ
- * @hostdata: the adapter
- * @word1: the first 64 bits of the data
- * @word2: the second 64 bits of the data
- */
-static int iseriesvscsi_send_crq(struct ibmvscsi_host_data *hostdata,
- u64 word1, u64 word2)
-{
- single_host_data = hostdata;
- return HvCallEvent_signalLpEventFast(viopath_hostLp,
- HvLpEvent_Type_VirtualIo,
- viomajorsubtype_scsi,
- HvLpEvent_AckInd_NoAck,
- HvLpEvent_AckType_ImmediateAck,
- viopath_sourceinst(viopath_hostLp),
- viopath_targetinst(viopath_hostLp),
- 0,
- VIOVERSION << 16, word1, word2, 0,
- 0);
-}
-
-static int iseriesvscsi_resume(struct ibmvscsi_host_data *hostdata)
-{
- return 0;
-}
-
-struct ibmvscsi_ops iseriesvscsi_ops = {
- .init_crq_queue = iseriesvscsi_init_crq_queue,
- .release_crq_queue = iseriesvscsi_release_crq_queue,
- .reset_crq_queue = iseriesvscsi_reset_crq_queue,
- .reenable_crq_queue = iseriesvscsi_reenable_crq_queue,
- .send_crq = iseriesvscsi_send_crq,
- .resume = iseriesvscsi_resume,
-};
diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c
deleted file mode 100644
index f48ae0190d95..000000000000
--- a/drivers/scsi/ibmvscsi/rpa_vscsi.c
+++ /dev/null
@@ -1,368 +0,0 @@
-/* ------------------------------------------------------------
- * rpa_vscsi.c
- * (C) Copyright IBM Corporation 1994, 2003
- * Authors: Colin DeVilbiss (devilbis@us.ibm.com)
- * Santiago Leon (santil@us.ibm.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
- * ------------------------------------------------------------
- * RPA-specific functions of the SCSI host adapter for Virtual I/O devices
- *
- * This driver allows the Linux SCSI peripheral drivers to directly
- * access devices in the hosting partition, either on an iSeries
- * hypervisor system or a converged hypervisor system.
- */
-
-#include <asm/vio.h>
-#include <asm/prom.h>
-#include <asm/iommu.h>
-#include <asm/hvcall.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/gfp.h>
-#include <linux/interrupt.h>
-#include "ibmvscsi.h"
-
-static char partition_name[97] = "UNKNOWN";
-static unsigned int partition_number = -1;
-
-/* ------------------------------------------------------------
- * Routines for managing the command/response queue
- */
-/**
- * rpavscsi_handle_event: - Interrupt handler for crq events
- * @irq: number of irq to handle, not used
- * @dev_instance: ibmvscsi_host_data of host that received interrupt
- *
- * Disables interrupts and schedules srp_task
- * Always returns IRQ_HANDLED
- */
-static irqreturn_t rpavscsi_handle_event(int irq, void *dev_instance)
-{
- struct ibmvscsi_host_data *hostdata =
- (struct ibmvscsi_host_data *)dev_instance;
- vio_disable_interrupts(to_vio_dev(hostdata->dev));
- tasklet_schedule(&hostdata->srp_task);
- return IRQ_HANDLED;
-}
-
-/**
- * release_crq_queue: - Deallocates data and unregisters CRQ
- * @queue: crq_queue to initialize and register
- * @host_data: ibmvscsi_host_data of host
- *
- * Frees irq, deallocates a page for messages, unmaps dma, and unregisters
- * the crq with the hypervisor.
- */
-static void rpavscsi_release_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata,
- int max_requests)
-{
- long rc = 0;
- struct vio_dev *vdev = to_vio_dev(hostdata->dev);
- free_irq(vdev->irq, (void *)hostdata);
- tasklet_kill(&hostdata->srp_task);
- do {
- if (rc)
- msleep(100);
- rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
- } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
- dma_unmap_single(hostdata->dev,
- queue->msg_token,
- queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
- free_page((unsigned long)queue->msgs);
-}
-
-/**
- * crq_queue_next_crq: - Returns the next entry in message queue
- * @queue: crq_queue to use
- *
- * Returns pointer to next entry in queue, or NULL if there are no new
- * entried in the CRQ.
- */
-static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue)
-{
- struct viosrp_crq *crq;
- unsigned long flags;
-
- spin_lock_irqsave(&queue->lock, flags);
- crq = &queue->msgs[queue->cur];
- if (crq->valid & 0x80) {
- if (++queue->cur == queue->size)
- queue->cur = 0;
- } else
- crq = NULL;
- spin_unlock_irqrestore(&queue->lock, flags);
-
- return crq;
-}
-
-/**
- * rpavscsi_send_crq: - Send a CRQ
- * @hostdata: the adapter
- * @word1: the first 64 bits of the data
- * @word2: the second 64 bits of the data
- */
-static int rpavscsi_send_crq(struct ibmvscsi_host_data *hostdata,
- u64 word1, u64 word2)
-{
- struct vio_dev *vdev = to_vio_dev(hostdata->dev);
-
- return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
-}
-
-/**
- * rpavscsi_task: - Process srps asynchronously
- * @data: ibmvscsi_host_data of host
- */
-static void rpavscsi_task(void *data)
-{
- struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)data;
- struct vio_dev *vdev = to_vio_dev(hostdata->dev);
- struct viosrp_crq *crq;
- int done = 0;
-
- while (!done) {
- /* Pull all the valid messages off the CRQ */
- while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) {
- ibmvscsi_handle_crq(crq, hostdata);
- crq->valid = 0x00;
- }
-
- vio_enable_interrupts(vdev);
- if ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) {
- vio_disable_interrupts(vdev);
- ibmvscsi_handle_crq(crq, hostdata);
- crq->valid = 0x00;
- } else {
- done = 1;
- }
- }
-}
-
-static void gather_partition_info(void)
-{
- struct device_node *rootdn;
-
- const char *ppartition_name;
- const unsigned int *p_number_ptr;
-
- /* Retrieve information about this partition */
- rootdn = of_find_node_by_path("/");
- if (!rootdn) {
- return;
- }
-
- ppartition_name = of_get_property(rootdn, "ibm,partition-name", NULL);
- if (ppartition_name)
- strncpy(partition_name, ppartition_name,
- sizeof(partition_name));
- p_number_ptr = of_get_property(rootdn, "ibm,partition-no", NULL);
- if (p_number_ptr)
- partition_number = *p_number_ptr;
- of_node_put(rootdn);
-}
-
-static void set_adapter_info(struct ibmvscsi_host_data *hostdata)
-{
- memset(&hostdata->madapter_info, 0x00,
- sizeof(hostdata->madapter_info));
-
- dev_info(hostdata->dev, "SRP_VERSION: %s\n", SRP_VERSION);
- strcpy(hostdata->madapter_info.srp_version, SRP_VERSION);
-
- strncpy(hostdata->madapter_info.partition_name, partition_name,
- sizeof(hostdata->madapter_info.partition_name));
-
- hostdata->madapter_info.partition_number = partition_number;
-
- hostdata->madapter_info.mad_version = 1;
- hostdata->madapter_info.os_type = 2;
-}
-
-/**
- * reset_crq_queue: - resets a crq after a failure
- * @queue: crq_queue to initialize and register
- * @hostdata: ibmvscsi_host_data of host
- *
- */
-static int rpavscsi_reset_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata)
-{
- int rc = 0;
- struct vio_dev *vdev = to_vio_dev(hostdata->dev);
-
- /* Close the CRQ */
- do {
- if (rc)
- msleep(100);
- rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
- } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
-
- /* Clean out the queue */
- memset(queue->msgs, 0x00, PAGE_SIZE);
- queue->cur = 0;
-
- set_adapter_info(hostdata);
-
- /* And re-open it again */
- rc = plpar_hcall_norets(H_REG_CRQ,
- vdev->unit_address,
- queue->msg_token, PAGE_SIZE);
- if (rc == 2) {
- /* Adapter is good, but other end is not ready */
- dev_warn(hostdata->dev, "Partner adapter not ready\n");
- } else if (rc != 0) {
- dev_warn(hostdata->dev, "couldn't register crq--rc 0x%x\n", rc);
- }
- return rc;
-}
-
-/**
- * initialize_crq_queue: - Initializes and registers CRQ with hypervisor
- * @queue: crq_queue to initialize and register
- * @hostdata: ibmvscsi_host_data of host
- *
- * Allocates a page for messages, maps it for dma, and registers
- * the crq with the hypervisor.
- * Returns zero on success.
- */
-static int rpavscsi_init_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata,
- int max_requests)
-{
- int rc;
- int retrc;
- struct vio_dev *vdev = to_vio_dev(hostdata->dev);
-
- queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
-
- if (!queue->msgs)
- goto malloc_failed;
- queue->size = PAGE_SIZE / sizeof(*queue->msgs);
-
- queue->msg_token = dma_map_single(hostdata->dev, queue->msgs,
- queue->size * sizeof(*queue->msgs),
- DMA_BIDIRECTIONAL);
-
- if (dma_mapping_error(hostdata->dev, queue->msg_token))
- goto map_failed;
-
- gather_partition_info();
- set_adapter_info(hostdata);
-
- retrc = rc = plpar_hcall_norets(H_REG_CRQ,
- vdev->unit_address,
- queue->msg_token, PAGE_SIZE);
- if (rc == H_RESOURCE)
- /* maybe kexecing and resource is busy. try a reset */
- rc = rpavscsi_reset_crq_queue(queue,
- hostdata);
-
- if (rc == 2) {
- /* Adapter is good, but other end is not ready */
- dev_warn(hostdata->dev, "Partner adapter not ready\n");
- retrc = 0;
- } else if (rc != 0) {
- dev_warn(hostdata->dev, "Error %d opening adapter\n", rc);
- goto reg_crq_failed;
- }
-
- queue->cur = 0;
- spin_lock_init(&queue->lock);
-
- tasklet_init(&hostdata->srp_task, (void *)rpavscsi_task,
- (unsigned long)hostdata);
-
- if (request_irq(vdev->irq,
- rpavscsi_handle_event,
- 0, "ibmvscsi", (void *)hostdata) != 0) {
- dev_err(hostdata->dev, "couldn't register irq 0x%x\n",
- vdev->irq);
- goto req_irq_failed;
- }
-
- rc = vio_enable_interrupts(vdev);
- if (rc != 0) {
- dev_err(hostdata->dev, "Error %d enabling interrupts!!!\n", rc);
- goto req_irq_failed;
- }
-
- return retrc;
-
- req_irq_failed:
- tasklet_kill(&hostdata->srp_task);
- rc = 0;
- do {
- if (rc)
- msleep(100);
- rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
- } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
- reg_crq_failed:
- dma_unmap_single(hostdata->dev,
- queue->msg_token,
- queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
- map_failed:
- free_page((unsigned long)queue->msgs);
- malloc_failed:
- return -1;
-}
-
-/**
- * reenable_crq_queue: - reenables a crq after
- * @queue: crq_queue to initialize and register
- * @hostdata: ibmvscsi_host_data of host
- *
- */
-static int rpavscsi_reenable_crq_queue(struct crq_queue *queue,
- struct ibmvscsi_host_data *hostdata)
-{
- int rc = 0;
- struct vio_dev *vdev = to_vio_dev(hostdata->dev);
-
- /* Re-enable the CRQ */
- do {
- if (rc)
- msleep(100);
- rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
- } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
-
- if (rc)
- dev_err(hostdata->dev, "Error %d enabling adapter\n", rc);
- return rc;
-}
-
-/**
- * rpavscsi_resume: - resume after suspend
- * @hostdata: ibmvscsi_host_data of host
- *
- */
-static int rpavscsi_resume(struct ibmvscsi_host_data *hostdata)
-{
- vio_disable_interrupts(to_vio_dev(hostdata->dev));
- tasklet_schedule(&hostdata->srp_task);
- return 0;
-}
-
-struct ibmvscsi_ops rpavscsi_ops = {
- .init_crq_queue = rpavscsi_init_crq_queue,
- .release_crq_queue = rpavscsi_release_crq_queue,
- .reset_crq_queue = rpavscsi_reset_crq_queue,
- .reenable_crq_queue = rpavscsi_reenable_crq_queue,
- .send_crq = rpavscsi_send_crq,
- .resume = rpavscsi_resume,
-};
diff --git a/drivers/scsi/ibmvscsi/viosrp.h b/drivers/scsi/ibmvscsi/viosrp.h
deleted file mode 100644
index 2cd735d1d196..000000000000
--- a/drivers/scsi/ibmvscsi/viosrp.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/*****************************************************************************/
-/* srp.h -- SCSI RDMA Protocol definitions */
-/* */
-/* Written By: Colin Devilbis, IBM Corporation */
-/* */
-/* Copyright (C) 2003 IBM Corporation */
-/* */
-/* This program is free software; you can redistribute it and/or modify */
-/* it under the terms of the GNU General Public License as published by */
-/* the Free Software Foundation; either version 2 of the License, or */
-/* (at your option) any later version. */
-/* */
-/* This program is distributed in the hope that it will be useful, */
-/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
-/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
-/* GNU General Public License for more details. */
-/* */
-/* You should have received a copy of the GNU General Public License */
-/* along with this program; if not, write to the Free Software */
-/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* */
-/* */
-/* This file contains structures and definitions for IBM RPA (RS/6000 */
-/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */
-/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */
-/* commands between logical partitions. */
-/* */
-/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */
-/* between partitions. The definitions in this file are architected, */
-/* and cannot be changed without breaking compatibility with other versions */
-/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/
-/* between logical partitions */
-/*****************************************************************************/
-#ifndef VIOSRP_H
-#define VIOSRP_H
-#include <scsi/srp.h>
-
-#define SRP_VERSION "16.a"
-#define SRP_MAX_IU_LEN 256
-#define SRP_MAX_LOC_LEN 32
-
-union srp_iu {
- struct srp_login_req login_req;
- struct srp_login_rsp login_rsp;
- struct srp_login_rej login_rej;
- struct srp_i_logout i_logout;
- struct srp_t_logout t_logout;
- struct srp_tsk_mgmt tsk_mgmt;
- struct srp_cmd cmd;
- struct srp_rsp rsp;
- u8 reserved[SRP_MAX_IU_LEN];
-};
-
-enum viosrp_crq_formats {
- VIOSRP_SRP_FORMAT = 0x01,
- VIOSRP_MAD_FORMAT = 0x02,
- VIOSRP_OS400_FORMAT = 0x03,
- VIOSRP_AIX_FORMAT = 0x04,
- VIOSRP_LINUX_FORMAT = 0x06,
- VIOSRP_INLINE_FORMAT = 0x07
-};
-
-enum viosrp_crq_status {
- VIOSRP_OK = 0x0,
- VIOSRP_NONRECOVERABLE_ERR = 0x1,
- VIOSRP_VIOLATES_MAX_XFER = 0x2,
- VIOSRP_PARTNER_PANIC = 0x3,
- VIOSRP_DEVICE_BUSY = 0x8,
- VIOSRP_ADAPTER_FAIL = 0x10,
- VIOSRP_OK2 = 0x99,
-};
-
-struct viosrp_crq {
- u8 valid; /* used by RPA */
- u8 format; /* SCSI vs out-of-band */
- u8 reserved;
- u8 status; /* non-scsi failure? (e.g. DMA failure) */
- u16 timeout; /* in seconds */
- u16 IU_length; /* in bytes */
- u64 IU_data_ptr; /* the TCE for transferring data */
-};
-
-/* MADs are Management requests above and beyond the IUs defined in the SRP
- * standard.
- */
-enum viosrp_mad_types {
- VIOSRP_EMPTY_IU_TYPE = 0x01,
- VIOSRP_ERROR_LOG_TYPE = 0x02,
- VIOSRP_ADAPTER_INFO_TYPE = 0x03,
- VIOSRP_HOST_CONFIG_TYPE = 0x04,
- VIOSRP_CAPABILITIES_TYPE = 0x05,
- VIOSRP_ENABLE_FAST_FAIL = 0x08,
-};
-
-enum viosrp_mad_status {
- VIOSRP_MAD_SUCCESS = 0x00,
- VIOSRP_MAD_NOT_SUPPORTED = 0xF1,
- VIOSRP_MAD_FAILED = 0xF7,
-};
-
-enum viosrp_capability_type {
- MIGRATION_CAPABILITIES = 0x01,
- RESERVATION_CAPABILITIES = 0x02,
-};
-
-enum viosrp_capability_support {
- SERVER_DOES_NOT_SUPPORTS_CAP = 0x0,
- SERVER_SUPPORTS_CAP = 0x01,
- SERVER_CAP_DATA = 0x02,
-};
-
-enum viosrp_reserve_type {
- CLIENT_RESERVE_SCSI_2 = 0x01,
-};
-
-enum viosrp_capability_flag {
- CLIENT_MIGRATED = 0x01,
- CLIENT_RECONNECT = 0x02,
- CAP_LIST_SUPPORTED = 0x04,
- CAP_LIST_DATA = 0x08,
-};
-
-/*
- * Common MAD header
- */
-struct mad_common {
- u32 type;
- u16 status;
- u16 length;
- u64 tag;
-};
-
-/*
- * All SRP (and MAD) requests normally flow from the
- * client to the server. There is no way for the server to send
- * an asynchronous message back to the client. The Empty IU is used
- * to hang out a meaningless request to the server so that it can respond
- * asynchrouously with something like a SCSI AER
- */
-struct viosrp_empty_iu {
- struct mad_common common;
- u64 buffer;
- u32 port;
-};
-
-struct viosrp_error_log {
- struct mad_common common;
- u64 buffer;
-};
-
-struct viosrp_adapter_info {
- struct mad_common common;
- u64 buffer;
-};
-
-struct viosrp_host_config {
- struct mad_common common;
- u64 buffer;
-};
-
-struct viosrp_fast_fail {
- struct mad_common common;
-};
-
-struct viosrp_capabilities {
- struct mad_common common;
- u64 buffer;
-};
-
-struct mad_capability_common {
- u32 cap_type;
- u16 length;
- u16 server_support;
-};
-
-struct mad_reserve_cap {
- struct mad_capability_common common;
- u32 type;
-};
-
-struct mad_migration_cap {
- struct mad_capability_common common;
- u32 ecl;
-};
-
-struct capabilities{
- u32 flags;
- char name[SRP_MAX_LOC_LEN];
- char loc[SRP_MAX_LOC_LEN];
- struct mad_migration_cap migration;
- struct mad_reserve_cap reserve;
-};
-
-union mad_iu {
- struct viosrp_empty_iu empty_iu;
- struct viosrp_error_log error_log;
- struct viosrp_adapter_info adapter_info;
- struct viosrp_host_config host_config;
- struct viosrp_fast_fail fast_fail;
- struct viosrp_capabilities capabilities;
-};
-
-union viosrp_iu {
- union srp_iu srp;
- union mad_iu mad;
-};
-
-struct mad_adapter_info_data {
- char srp_version[8];
- char partition_name[96];
- u32 partition_number;
- u32 mad_version;
- u32 os_type;
- u32 port_max_txu[8]; /* per-port maximum transfer */
-};
-
-#endif
diff --git a/drivers/scsi/ibmvscsi_tgt/Makefile b/drivers/scsi/ibmvscsi_tgt/Makefile
new file mode 100644
index 000000000000..cc7a8256dcf8
--- /dev/null
+++ b/drivers/scsi/ibmvscsi_tgt/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsis.o
+
+ibmvscsis-y := libsrp.o ibmvscsi_tgt.o
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
new file mode 100644
index 000000000000..f259746bc804
--- /dev/null
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
@@ -0,0 +1,4070 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*******************************************************************************
+ * IBM Virtual SCSI Target Driver
+ * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp.
+ * Santiago Leon (santil@us.ibm.com) IBM Corp.
+ * Linda Xie (lxie@us.ibm.com) IBM Corp.
+ *
+ * Copyright (C) 2005-2011 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2010 Nicholas A. Bellinger <nab@kernel.org>
+ *
+ * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+ * Authors: Michael Cyr <mikecyr@linux.vnet.ibm.com>
+ *
+ ****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include <asm/hvcall.h>
+#include <asm/vio.h>
+
+#include <scsi/viosrp.h>
+
+#include "ibmvscsi_tgt.h"
+
+#define IBMVSCSIS_VERSION "v0.2"
+
+#define INITIAL_SRP_LIMIT 1024
+#define DEFAULT_MAX_SECTORS 256
+#define MAX_TXU 1024 * 1024
+
+static uint max_vdma_size = MAX_H_COPY_RDMA;
+
+static char system_id[SYS_ID_NAME_LEN] = "";
+static char partition_name[PARTITION_NAMELEN] = "UNKNOWN";
+static uint partition_number = -1;
+
+/* Adapter list and lock to control it */
+static DEFINE_SPINLOCK(ibmvscsis_dev_lock);
+static LIST_HEAD(ibmvscsis_dev_list);
+
+static long ibmvscsis_parse_command(struct scsi_info *vscsi,
+ struct viosrp_crq *crq);
+
+static void ibmvscsis_adapter_idle(struct scsi_info *vscsi);
+
+static void ibmvscsis_determine_resid(struct se_cmd *se_cmd,
+ struct srp_rsp *rsp)
+{
+ u32 residual_count = se_cmd->residual_count;
+
+ if (!residual_count)
+ return;
+
+ if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+ if (se_cmd->data_direction == DMA_TO_DEVICE) {
+ /* residual data from an underflow write */
+ rsp->flags = SRP_RSP_FLAG_DOUNDER;
+ rsp->data_out_res_cnt = cpu_to_be32(residual_count);
+ } else if (se_cmd->data_direction == DMA_FROM_DEVICE) {
+ /* residual data from an underflow read */
+ rsp->flags = SRP_RSP_FLAG_DIUNDER;
+ rsp->data_in_res_cnt = cpu_to_be32(residual_count);
+ }
+ } else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
+ if (se_cmd->data_direction == DMA_TO_DEVICE) {
+ /* residual data from an overflow write */
+ rsp->flags = SRP_RSP_FLAG_DOOVER;
+ rsp->data_out_res_cnt = cpu_to_be32(residual_count);
+ } else if (se_cmd->data_direction == DMA_FROM_DEVICE) {
+ /* residual data from an overflow read */
+ rsp->flags = SRP_RSP_FLAG_DIOVER;
+ rsp->data_in_res_cnt = cpu_to_be32(residual_count);
+ }
+ }
+}
+
+/**
+ * connection_broken() - Determine if the connection to the client is good
+ * @vscsi: Pointer to our adapter structure
+ *
+ * This function attempts to send a ping MAD to the client. If the call to
+ * queue the request returns H_CLOSED then the connection has been broken
+ * and the function returns TRUE.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt or Process environment
+ */
+static bool connection_broken(struct scsi_info *vscsi)
+{
+ struct viosrp_crq *crq;
+ u64 buffer[2] = { 0, 0 };
+ long h_return_code;
+ bool rc = false;
+
+ /* create a PING crq */
+ crq = (struct viosrp_crq *)&buffer;
+ crq->valid = VALID_CMD_RESP_EL;
+ crq->format = MESSAGE_IN_CRQ;
+ crq->status = PING;
+
+ h_return_code = h_send_crq(vscsi->dds.unit_id,
+ cpu_to_be64(buffer[MSG_HI]),
+ cpu_to_be64(buffer[MSG_LOW]));
+
+ dev_dbg(&vscsi->dev, "Connection_broken: rc %ld\n", h_return_code);
+
+ if (h_return_code == H_CLOSED)
+ rc = true;
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_unregister_command_q() - Helper Function-Unregister Command Queue
+ * @vscsi: Pointer to our adapter structure
+ *
+ * This function calls h_free_q then frees the interrupt bit etc.
+ * It must release the lock before doing so because of the time it can take
+ * for h_free_crq in PHYP
+ * NOTE: * the caller must make sure that state and or flags will prevent
+ * interrupt handler from scheduling work.
+ * * anyone calling this function may need to set the CRQ_CLOSED flag
+ * we can't do it here, because we don't have the lock
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process level
+ */
+static long ibmvscsis_unregister_command_q(struct scsi_info *vscsi)
+{
+ long qrc;
+ long rc = ADAPT_SUCCESS;
+ int ticks = 0;
+
+ do {
+ qrc = h_free_crq(vscsi->dds.unit_id);
+ switch (qrc) {
+ case H_SUCCESS:
+ spin_lock_bh(&vscsi->intr_lock);
+ vscsi->flags &= ~PREP_FOR_SUSPEND_FLAGS;
+ spin_unlock_bh(&vscsi->intr_lock);
+ break;
+
+ case H_HARDWARE:
+ case H_PARAMETER:
+ dev_err(&vscsi->dev, "unregister_command_q: error from h_free_crq %ld\n",
+ qrc);
+ rc = ERROR;
+ break;
+
+ case H_BUSY:
+ case H_LONG_BUSY_ORDER_1_MSEC:
+ /* msleep not good for small values */
+ usleep_range(1000, 2000);
+ ticks += 1;
+ break;
+ case H_LONG_BUSY_ORDER_10_MSEC:
+ usleep_range(10000, 20000);
+ ticks += 10;
+ break;
+ case H_LONG_BUSY_ORDER_100_MSEC:
+ msleep(100);
+ ticks += 100;
+ break;
+ case H_LONG_BUSY_ORDER_1_SEC:
+ ssleep(1);
+ ticks += 1000;
+ break;
+ case H_LONG_BUSY_ORDER_10_SEC:
+ ssleep(10);
+ ticks += 10000;
+ break;
+ case H_LONG_BUSY_ORDER_100_SEC:
+ ssleep(100);
+ ticks += 100000;
+ break;
+ default:
+ dev_err(&vscsi->dev, "unregister_command_q: unknown error %ld from h_free_crq\n",
+ qrc);
+ rc = ERROR;
+ break;
+ }
+
+ /*
+ * dont wait more then 300 seconds
+ * ticks are in milliseconds more or less
+ */
+ if (ticks > 300000 && qrc != H_SUCCESS) {
+ rc = ERROR;
+ dev_err(&vscsi->dev, "Excessive wait for h_free_crq\n");
+ }
+ } while (qrc != H_SUCCESS && rc == ADAPT_SUCCESS);
+
+ dev_dbg(&vscsi->dev, "Freeing CRQ: phyp rc %ld, rc %ld\n", qrc, rc);
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_delete_client_info() - Helper function to Delete Client Info
+ * @vscsi: Pointer to our adapter structure
+ * @client_closed: True if client closed its queue
+ *
+ * Deletes information specific to the client when the client goes away
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt or Process
+ */
+static void ibmvscsis_delete_client_info(struct scsi_info *vscsi,
+ bool client_closed)
+{
+ vscsi->client_cap = 0;
+
+ /*
+ * Some things we don't want to clear if we're closing the queue,
+ * because some clients don't resend the host handshake when they
+ * get a transport event.
+ */
+ if (client_closed)
+ vscsi->client_data.os_type = 0;
+}
+
+/**
+ * ibmvscsis_free_command_q() - Free Command Queue
+ * @vscsi: Pointer to our adapter structure
+ *
+ * This function calls unregister_command_q, then clears interrupts and
+ * any pending interrupt acknowledgments associated with the command q.
+ * It also clears memory if there is no error.
+ *
+ * PHYP did not meet the PAPR architecture so that we must give up the
+ * lock. This causes a timing hole regarding state change. To close the
+ * hole this routine does accounting on any change that occurred during
+ * the time the lock is not held.
+ * NOTE: must give up and then acquire the interrupt lock, the caller must
+ * make sure that state and or flags will prevent interrupt handler from
+ * scheduling work.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process level, interrupt lock is held
+ */
+static long ibmvscsis_free_command_q(struct scsi_info *vscsi)
+{
+ int bytes;
+ u32 flags_under_lock;
+ u16 state_under_lock;
+ long rc = ADAPT_SUCCESS;
+
+ if (!(vscsi->flags & CRQ_CLOSED)) {
+ vio_disable_interrupts(vscsi->dma_dev);
+
+ state_under_lock = vscsi->new_state;
+ flags_under_lock = vscsi->flags;
+ vscsi->phyp_acr_state = 0;
+ vscsi->phyp_acr_flags = 0;
+
+ spin_unlock_bh(&vscsi->intr_lock);
+ rc = ibmvscsis_unregister_command_q(vscsi);
+ spin_lock_bh(&vscsi->intr_lock);
+
+ if (state_under_lock != vscsi->new_state)
+ vscsi->phyp_acr_state = vscsi->new_state;
+
+ vscsi->phyp_acr_flags = ((~flags_under_lock) & vscsi->flags);
+
+ if (rc == ADAPT_SUCCESS) {
+ bytes = vscsi->cmd_q.size * PAGE_SIZE;
+ memset(vscsi->cmd_q.base_addr, 0, bytes);
+ vscsi->cmd_q.index = 0;
+ vscsi->flags |= CRQ_CLOSED;
+
+ ibmvscsis_delete_client_info(vscsi, false);
+ }
+
+ dev_dbg(&vscsi->dev, "free_command_q: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n",
+ vscsi->flags, vscsi->state, vscsi->phyp_acr_flags,
+ vscsi->phyp_acr_state);
+ }
+ return rc;
+}
+
+/**
+ * ibmvscsis_cmd_q_dequeue() - Get valid Command element
+ * @mask: Mask to use in case index wraps
+ * @current_index: Current index into command queue
+ * @base_addr: Pointer to start of command queue
+ *
+ * Returns a pointer to a valid command element or NULL, if the command
+ * queue is empty
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt environment, interrupt lock held
+ */
+static struct viosrp_crq *ibmvscsis_cmd_q_dequeue(uint mask,
+ uint *current_index,
+ struct viosrp_crq *base_addr)
+{
+ struct viosrp_crq *ptr;
+
+ ptr = base_addr + *current_index;
+
+ if (ptr->valid) {
+ *current_index = (*current_index + 1) & mask;
+ dma_rmb();
+ } else {
+ ptr = NULL;
+ }
+
+ return ptr;
+}
+
+/**
+ * ibmvscsis_send_init_message() - send initialize message to the client
+ * @vscsi: Pointer to our adapter structure
+ * @format: Which Init Message format to send
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt environment interrupt lock held
+ */
+static long ibmvscsis_send_init_message(struct scsi_info *vscsi, u8 format)
+{
+ struct viosrp_crq *crq;
+ u64 buffer[2] = { 0, 0 };
+ long rc;
+
+ crq = (struct viosrp_crq *)&buffer;
+ crq->valid = VALID_INIT_MSG;
+ crq->format = format;
+ rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]),
+ cpu_to_be64(buffer[MSG_LOW]));
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_check_init_msg() - Check init message valid
+ * @vscsi: Pointer to our adapter structure
+ * @format: Pointer to return format of Init Message, if any.
+ * Set to UNUSED_FORMAT if no Init Message in queue.
+ *
+ * Checks if an initialize message was queued by the initiatior
+ * after the queue was created and before the interrupt was enabled.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process level only, interrupt lock held
+ */
+static long ibmvscsis_check_init_msg(struct scsi_info *vscsi, uint *format)
+{
+ struct viosrp_crq *crq;
+ long rc = ADAPT_SUCCESS;
+
+ crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask, &vscsi->cmd_q.index,
+ vscsi->cmd_q.base_addr);
+ if (!crq) {
+ *format = (uint)UNUSED_FORMAT;
+ } else if (crq->valid == VALID_INIT_MSG && crq->format == INIT_MSG) {
+ *format = (uint)INIT_MSG;
+ crq->valid = INVALIDATE_CMD_RESP_EL;
+ dma_rmb();
+
+ /*
+ * the caller has ensured no initialize message was
+ * sent after the queue was
+ * created so there should be no other message on the queue.
+ */
+ crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask,
+ &vscsi->cmd_q.index,
+ vscsi->cmd_q.base_addr);
+ if (crq) {
+ *format = (uint)(crq->format);
+ rc = ERROR;
+ crq->valid = INVALIDATE_CMD_RESP_EL;
+ dma_rmb();
+ }
+ } else {
+ *format = (uint)(crq->format);
+ rc = ERROR;
+ crq->valid = INVALIDATE_CMD_RESP_EL;
+ dma_rmb();
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_disconnect() - Helper function to disconnect
+ * @work: Pointer to work_struct, gives access to our adapter structure
+ *
+ * An error has occurred or the driver received a Transport event,
+ * and the driver is requesting that the command queue be de-registered
+ * in a safe manner. If there is no outstanding I/O then we can stop the
+ * queue. If we are restarting the queue it will be reflected in the
+ * the state of the adapter.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process environment
+ */
+static void ibmvscsis_disconnect(struct work_struct *work)
+{
+ struct scsi_info *vscsi = container_of(work, struct scsi_info,
+ proc_work);
+ u16 new_state;
+ bool wait_idle = false;
+
+ spin_lock_bh(&vscsi->intr_lock);
+ new_state = vscsi->new_state;
+ vscsi->new_state = 0;
+
+ vscsi->flags |= DISCONNECT_SCHEDULED;
+ vscsi->flags &= ~SCHEDULE_DISCONNECT;
+
+ dev_dbg(&vscsi->dev, "disconnect: flags 0x%x, state 0x%hx\n",
+ vscsi->flags, vscsi->state);
+
+ /*
+ * check which state we are in and see if we
+ * should transition to the new state
+ */
+ switch (vscsi->state) {
+ /* Should never be called while in this state. */
+ case NO_QUEUE:
+ /*
+ * Can never transition from this state;
+ * igonore errors and logout.
+ */
+ case UNCONFIGURING:
+ break;
+
+ /* can transition from this state to UNCONFIGURING */
+ case ERR_DISCONNECT:
+ if (new_state == UNCONFIGURING)
+ vscsi->state = new_state;
+ break;
+
+ /*
+ * Can transition from this state to unconfiguring
+ * or err disconnect.
+ */
+ case ERR_DISCONNECT_RECONNECT:
+ switch (new_state) {
+ case UNCONFIGURING:
+ case ERR_DISCONNECT:
+ vscsi->state = new_state;
+ break;
+
+ case WAIT_IDLE:
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* can transition from this state to UNCONFIGURING */
+ case ERR_DISCONNECTED:
+ if (new_state == UNCONFIGURING)
+ vscsi->state = new_state;
+ break;
+
+ case WAIT_ENABLED:
+ switch (new_state) {
+ case UNCONFIGURING:
+ vscsi->state = new_state;
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ vscsi->flags &= ~(SCHEDULE_DISCONNECT |
+ DISCONNECT_SCHEDULED);
+ dma_rmb();
+ if (vscsi->flags & CFG_SLEEPING) {
+ vscsi->flags &= ~CFG_SLEEPING;
+ complete(&vscsi->unconfig);
+ }
+ break;
+
+ /* should never happen */
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ case WAIT_IDLE:
+ dev_err(&vscsi->dev, "disconnect: invalid state %d for WAIT_IDLE\n",
+ vscsi->state);
+ break;
+ }
+ break;
+
+ case WAIT_IDLE:
+ switch (new_state) {
+ case UNCONFIGURING:
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ vscsi->state = new_state;
+ vscsi->flags &= ~(SCHEDULE_DISCONNECT |
+ DISCONNECT_SCHEDULED);
+ ibmvscsis_free_command_q(vscsi);
+ break;
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ vscsi->state = new_state;
+ break;
+ }
+ break;
+
+ /*
+ * Initiator has not done a successful srp login
+ * or has done a successful srp logout ( adapter was not
+ * busy). In the first case there can be responses queued
+ * waiting for space on the initiators response queue (MAD)
+ * The second case the adapter is idle. Assume the worse case,
+ * i.e. the second case.
+ */
+ case WAIT_CONNECTION:
+ case CONNECTED:
+ case SRP_PROCESSING:
+ wait_idle = true;
+ vscsi->state = new_state;
+ break;
+
+ /* can transition from this state to UNCONFIGURING */
+ case UNDEFINED:
+ if (new_state == UNCONFIGURING)
+ vscsi->state = new_state;
+ break;
+ default:
+ break;
+ }
+
+ if (wait_idle) {
+ dev_dbg(&vscsi->dev, "disconnect start wait, active %d, sched %d\n",
+ (int)list_empty(&vscsi->active_q),
+ (int)list_empty(&vscsi->schedule_q));
+ if (!list_empty(&vscsi->active_q) ||
+ !list_empty(&vscsi->schedule_q)) {
+ vscsi->flags |= WAIT_FOR_IDLE;
+ dev_dbg(&vscsi->dev, "disconnect flags 0x%x\n",
+ vscsi->flags);
+ /*
+ * This routine is can not be called with the interrupt
+ * lock held.
+ */
+ spin_unlock_bh(&vscsi->intr_lock);
+ wait_for_completion(&vscsi->wait_idle);
+ spin_lock_bh(&vscsi->intr_lock);
+ }
+ dev_dbg(&vscsi->dev, "disconnect stop wait\n");
+
+ ibmvscsis_adapter_idle(vscsi);
+ }
+
+ spin_unlock_bh(&vscsi->intr_lock);
+}
+
+/**
+ * ibmvscsis_post_disconnect() - Schedule the disconnect
+ * @vscsi: Pointer to our adapter structure
+ * @new_state: State to move to after disconnecting
+ * @flag_bits: Flags to turn on in adapter structure
+ *
+ * If it's already been scheduled, then see if we need to "upgrade"
+ * the new state (if the one passed in is more "severe" than the
+ * previous one).
+ *
+ * PRECONDITION:
+ * interrupt lock is held
+ */
+static void ibmvscsis_post_disconnect(struct scsi_info *vscsi, uint new_state,
+ uint flag_bits)
+{
+ uint state;
+
+ /* check the validity of the new state */
+ switch (new_state) {
+ case UNCONFIGURING:
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ case WAIT_IDLE:
+ break;
+
+ default:
+ dev_err(&vscsi->dev, "post_disconnect: Invalid new state %d\n",
+ new_state);
+ return;
+ }
+
+ vscsi->flags |= flag_bits;
+
+ dev_dbg(&vscsi->dev, "post_disconnect: new_state 0x%x, flag_bits 0x%x, vscsi->flags 0x%x, state %hx\n",
+ new_state, flag_bits, vscsi->flags, vscsi->state);
+
+ if (!(vscsi->flags & (DISCONNECT_SCHEDULED | SCHEDULE_DISCONNECT))) {
+ vscsi->flags |= SCHEDULE_DISCONNECT;
+ vscsi->new_state = new_state;
+
+ INIT_WORK(&vscsi->proc_work, ibmvscsis_disconnect);
+ (void)queue_work(vscsi->work_q, &vscsi->proc_work);
+ } else {
+ if (vscsi->new_state)
+ state = vscsi->new_state;
+ else
+ state = vscsi->state;
+
+ switch (state) {
+ case NO_QUEUE:
+ case UNCONFIGURING:
+ break;
+
+ case ERR_DISCONNECTED:
+ case ERR_DISCONNECT:
+ case UNDEFINED:
+ if (new_state == UNCONFIGURING)
+ vscsi->new_state = new_state;
+ break;
+
+ case ERR_DISCONNECT_RECONNECT:
+ switch (new_state) {
+ case UNCONFIGURING:
+ case ERR_DISCONNECT:
+ vscsi->new_state = new_state;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case WAIT_ENABLED:
+ case WAIT_IDLE:
+ case WAIT_CONNECTION:
+ case CONNECTED:
+ case SRP_PROCESSING:
+ vscsi->new_state = new_state;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ dev_dbg(&vscsi->dev, "Leaving post_disconnect: flags 0x%x, new_state 0x%x\n",
+ vscsi->flags, vscsi->new_state);
+}
+
+/**
+ * ibmvscsis_handle_init_compl_msg() - Respond to an Init Complete Message
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Must be called with interrupt lock held.
+ */
+static long ibmvscsis_handle_init_compl_msg(struct scsi_info *vscsi)
+{
+ long rc = ADAPT_SUCCESS;
+
+ switch (vscsi->state) {
+ case NO_QUEUE:
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ case ERR_DISCONNECTED:
+ case UNCONFIGURING:
+ case UNDEFINED:
+ rc = ERROR;
+ break;
+
+ case WAIT_CONNECTION:
+ vscsi->state = CONNECTED;
+ break;
+
+ case WAIT_IDLE:
+ case SRP_PROCESSING:
+ case CONNECTED:
+ case WAIT_ENABLED:
+ default:
+ rc = ERROR;
+ dev_err(&vscsi->dev, "init_msg: invalid state %d to get init compl msg\n",
+ vscsi->state);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_handle_init_msg() - Respond to an Init Message
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Must be called with interrupt lock held.
+ */
+static long ibmvscsis_handle_init_msg(struct scsi_info *vscsi)
+{
+ long rc = ADAPT_SUCCESS;
+
+ switch (vscsi->state) {
+ case WAIT_CONNECTION:
+ rc = ibmvscsis_send_init_message(vscsi, INIT_COMPLETE_MSG);
+ switch (rc) {
+ case H_SUCCESS:
+ vscsi->state = CONNECTED;
+ break;
+
+ case H_PARAMETER:
+ dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
+ break;
+
+ case H_DROPPED:
+ dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
+ rc);
+ rc = ERROR;
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT, 0);
+ break;
+
+ case H_CLOSED:
+ dev_warn(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
+ rc);
+ rc = 0;
+ break;
+ }
+ break;
+
+ case UNDEFINED:
+ rc = ERROR;
+ break;
+
+ case UNCONFIGURING:
+ break;
+
+ case WAIT_ENABLED:
+ case CONNECTED:
+ case SRP_PROCESSING:
+ case WAIT_IDLE:
+ case NO_QUEUE:
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ case ERR_DISCONNECTED:
+ default:
+ rc = ERROR;
+ dev_err(&vscsi->dev, "init_msg: invalid state %d to get init msg\n",
+ vscsi->state);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_init_msg() - Respond to an init message
+ * @vscsi: Pointer to our adapter structure
+ * @crq: Pointer to CRQ element containing the Init Message
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, interrupt lock held
+ */
+static long ibmvscsis_init_msg(struct scsi_info *vscsi, struct viosrp_crq *crq)
+{
+ long rc = ADAPT_SUCCESS;
+
+ dev_dbg(&vscsi->dev, "init_msg: state 0x%hx\n", vscsi->state);
+
+ rc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO,
+ (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0,
+ 0);
+ if (rc == H_SUCCESS) {
+ vscsi->client_data.partition_number =
+ be64_to_cpu(*(u64 *)vscsi->map_buf);
+ dev_dbg(&vscsi->dev, "init_msg, part num %d\n",
+ vscsi->client_data.partition_number);
+ } else {
+ dev_dbg(&vscsi->dev, "init_msg h_vioctl rc %ld\n", rc);
+ rc = ADAPT_SUCCESS;
+ }
+
+ if (crq->format == INIT_MSG) {
+ rc = ibmvscsis_handle_init_msg(vscsi);
+ } else if (crq->format == INIT_COMPLETE_MSG) {
+ rc = ibmvscsis_handle_init_compl_msg(vscsi);
+ } else {
+ rc = ERROR;
+ dev_err(&vscsi->dev, "init_msg: invalid format %d\n",
+ (uint)crq->format);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_establish_new_q() - Establish new CRQ queue
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Must be called with interrupt lock held.
+ */
+static long ibmvscsis_establish_new_q(struct scsi_info *vscsi)
+{
+ long rc = ADAPT_SUCCESS;
+ uint format;
+
+ rc = h_vioctl(vscsi->dds.unit_id, H_ENABLE_PREPARE_FOR_SUSPEND, 30000,
+ 0, 0, 0, 0);
+ if (rc == H_SUCCESS)
+ vscsi->flags |= PREP_FOR_SUSPEND_ENABLED;
+ else if (rc != H_NOT_FOUND)
+ dev_err(&vscsi->dev, "Error from Enable Prepare for Suspend: %ld\n",
+ rc);
+
+ vscsi->flags &= PRESERVE_FLAG_FIELDS;
+ vscsi->rsp_q_timer.timer_pops = 0;
+ vscsi->debit = 0;
+ vscsi->credit = 0;
+
+ rc = vio_enable_interrupts(vscsi->dma_dev);
+ if (rc) {
+ dev_warn(&vscsi->dev, "establish_new_q: failed to enable interrupts, rc %ld\n",
+ rc);
+ return rc;
+ }
+
+ rc = ibmvscsis_check_init_msg(vscsi, &format);
+ if (rc) {
+ dev_err(&vscsi->dev, "establish_new_q: check_init_msg failed, rc %ld\n",
+ rc);
+ return rc;
+ }
+
+ if (format == UNUSED_FORMAT) {
+ rc = ibmvscsis_send_init_message(vscsi, INIT_MSG);
+ switch (rc) {
+ case H_SUCCESS:
+ case H_DROPPED:
+ case H_CLOSED:
+ rc = ADAPT_SUCCESS;
+ break;
+
+ case H_PARAMETER:
+ case H_HARDWARE:
+ break;
+
+ default:
+ vscsi->state = UNDEFINED;
+ rc = H_HARDWARE;
+ break;
+ }
+ } else if (format == INIT_MSG) {
+ rc = ibmvscsis_handle_init_msg(vscsi);
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_reset_queue() - Reset CRQ Queue
+ * @vscsi: Pointer to our adapter structure
+ *
+ * This function calls h_free_q and then calls h_reg_q and does all
+ * of the bookkeeping to get us back to where we can communicate.
+ *
+ * Actually, we don't always call h_free_crq. A problem was discovered
+ * where one partition would close and reopen his queue, which would
+ * cause his partner to get a transport event, which would cause him to
+ * close and reopen his queue, which would cause the original partition
+ * to get a transport event, etc., etc. To prevent this, we don't
+ * actually close our queue if the client initiated the reset, (i.e.
+ * either we got a transport event or we have detected that the client's
+ * queue is gone)
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process environment, called with interrupt lock held
+ */
+static void ibmvscsis_reset_queue(struct scsi_info *vscsi)
+{
+ int bytes;
+ long rc = ADAPT_SUCCESS;
+
+ dev_dbg(&vscsi->dev, "reset_queue: flags 0x%x\n", vscsi->flags);
+
+ /* don't reset, the client did it for us */
+ if (vscsi->flags & (CLIENT_FAILED | TRANS_EVENT)) {
+ vscsi->flags &= PRESERVE_FLAG_FIELDS;
+ vscsi->rsp_q_timer.timer_pops = 0;
+ vscsi->debit = 0;
+ vscsi->credit = 0;
+ vscsi->state = WAIT_CONNECTION;
+ vio_enable_interrupts(vscsi->dma_dev);
+ } else {
+ rc = ibmvscsis_free_command_q(vscsi);
+ if (rc == ADAPT_SUCCESS) {
+ vscsi->state = WAIT_CONNECTION;
+
+ bytes = vscsi->cmd_q.size * PAGE_SIZE;
+ rc = h_reg_crq(vscsi->dds.unit_id,
+ vscsi->cmd_q.crq_token, bytes);
+ if (rc == H_CLOSED || rc == H_SUCCESS) {
+ rc = ibmvscsis_establish_new_q(vscsi);
+ }
+
+ if (rc != ADAPT_SUCCESS) {
+ dev_dbg(&vscsi->dev, "reset_queue: reg_crq rc %ld\n",
+ rc);
+
+ vscsi->state = ERR_DISCONNECTED;
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ ibmvscsis_free_command_q(vscsi);
+ }
+ } else {
+ vscsi->state = ERR_DISCONNECTED;
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ }
+ }
+}
+
+/**
+ * ibmvscsis_free_cmd_resources() - Free command resources
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Command which is not longer in use
+ *
+ * Must be called with interrupt lock held.
+ */
+static void ibmvscsis_free_cmd_resources(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd)
+{
+ struct iu_entry *iue = cmd->iue;
+
+ switch (cmd->type) {
+ case TASK_MANAGEMENT:
+ case SCSI_CDB:
+ /*
+ * When the queue goes down this value is cleared, so it
+ * cannot be cleared in this general purpose function.
+ */
+ if (vscsi->debit)
+ vscsi->debit -= 1;
+ break;
+ case ADAPTER_MAD:
+ vscsi->flags &= ~PROCESSING_MAD;
+ break;
+ case UNSET_TYPE:
+ break;
+ default:
+ dev_err(&vscsi->dev, "free_cmd_resources unknown type %d\n",
+ cmd->type);
+ break;
+ }
+
+ cmd->iue = NULL;
+ list_add_tail(&cmd->list, &vscsi->free_cmd);
+ srp_iu_put(iue);
+
+ if (list_empty(&vscsi->active_q) && list_empty(&vscsi->schedule_q) &&
+ list_empty(&vscsi->waiting_rsp) && (vscsi->flags & WAIT_FOR_IDLE)) {
+ vscsi->flags &= ~WAIT_FOR_IDLE;
+ complete(&vscsi->wait_idle);
+ }
+}
+
+/**
+ * ibmvscsis_ready_for_suspend() - Helper function to call VIOCTL
+ * @vscsi: Pointer to our adapter structure
+ * @idle: Indicates whether we were called from adapter_idle. This
+ * is important to know if we need to do a disconnect, since if
+ * we're called from adapter_idle, we're still processing the
+ * current disconnect, so we can't just call post_disconnect.
+ *
+ * This function is called when the adapter is idle when phyp has sent
+ * us a Prepare for Suspend Transport Event.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process or interrupt environment called with interrupt lock held
+ */
+static long ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle)
+{
+ long rc = 0;
+ struct viosrp_crq *crq;
+
+ /* See if there is a Resume event in the queue */
+ crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
+
+ dev_dbg(&vscsi->dev, "ready_suspend: flags 0x%x, state 0x%hx crq_valid:%x\n",
+ vscsi->flags, vscsi->state, (int)crq->valid);
+
+ if (!(vscsi->flags & PREP_FOR_SUSPEND_ABORTED) && !(crq->valid)) {
+ rc = h_vioctl(vscsi->dds.unit_id, H_READY_FOR_SUSPEND, 0, 0, 0,
+ 0, 0);
+ if (rc) {
+ dev_err(&vscsi->dev, "Ready for Suspend Vioctl failed: %ld\n",
+ rc);
+ rc = 0;
+ }
+ } else if (((vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE) &&
+ (vscsi->flags & PREP_FOR_SUSPEND_ABORTED)) ||
+ ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) ||
+ (crq->format != RESUME_FROM_SUSP)))) {
+ if (idle) {
+ vscsi->state = ERR_DISCONNECT_RECONNECT;
+ ibmvscsis_reset_queue(vscsi);
+ rc = -1;
+ } else if (vscsi->state == CONNECTED) {
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT, 0);
+ }
+
+ vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
+
+ if ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) ||
+ (crq->format != RESUME_FROM_SUSP)))
+ dev_err(&vscsi->dev, "Invalid element in CRQ after Prepare for Suspend");
+ }
+
+ vscsi->flags &= ~(PREP_FOR_SUSPEND_PENDING | PREP_FOR_SUSPEND_ABORTED);
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_trans_event() - Handle a Transport Event
+ * @vscsi: Pointer to our adapter structure
+ * @crq: Pointer to CRQ entry containing the Transport Event
+ *
+ * Do the logic to close the I_T nexus. This function may not
+ * behave to specification.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, interrupt lock held
+ */
+static long ibmvscsis_trans_event(struct scsi_info *vscsi,
+ struct viosrp_crq *crq)
+{
+ long rc = ADAPT_SUCCESS;
+
+ dev_dbg(&vscsi->dev, "trans_event: format %d, flags 0x%x, state 0x%hx\n",
+ (int)crq->format, vscsi->flags, vscsi->state);
+
+ switch (crq->format) {
+ case MIGRATED:
+ case PARTNER_FAILED:
+ case PARTNER_DEREGISTER:
+ ibmvscsis_delete_client_info(vscsi, true);
+ if (crq->format == MIGRATED)
+ vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
+ switch (vscsi->state) {
+ case NO_QUEUE:
+ case ERR_DISCONNECTED:
+ case UNDEFINED:
+ break;
+
+ case UNCONFIGURING:
+ vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT);
+ break;
+
+ case WAIT_ENABLED:
+ break;
+
+ case WAIT_CONNECTION:
+ break;
+
+ case CONNECTED:
+ ibmvscsis_post_disconnect(vscsi, WAIT_IDLE,
+ (RESPONSE_Q_DOWN |
+ TRANS_EVENT));
+ break;
+
+ case SRP_PROCESSING:
+ if ((vscsi->debit > 0) ||
+ !list_empty(&vscsi->schedule_q) ||
+ !list_empty(&vscsi->waiting_rsp) ||
+ !list_empty(&vscsi->active_q)) {
+ dev_dbg(&vscsi->dev, "debit %d, sched %d, wait %d, active %d\n",
+ vscsi->debit,
+ (int)list_empty(&vscsi->schedule_q),
+ (int)list_empty(&vscsi->waiting_rsp),
+ (int)list_empty(&vscsi->active_q));
+ dev_warn(&vscsi->dev, "connection lost with outstanding work\n");
+ } else {
+ dev_dbg(&vscsi->dev, "trans_event: SRP Processing, but no outstanding work\n");
+ }
+
+ ibmvscsis_post_disconnect(vscsi, WAIT_IDLE,
+ (RESPONSE_Q_DOWN |
+ TRANS_EVENT));
+ break;
+
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ case WAIT_IDLE:
+ vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT);
+ break;
+ }
+ break;
+
+ case PREPARE_FOR_SUSPEND:
+ dev_dbg(&vscsi->dev, "Prep for Suspend, crq status = 0x%x\n",
+ (int)crq->status);
+ switch (vscsi->state) {
+ case ERR_DISCONNECTED:
+ case WAIT_CONNECTION:
+ case CONNECTED:
+ ibmvscsis_ready_for_suspend(vscsi, false);
+ break;
+ case SRP_PROCESSING:
+ vscsi->resume_state = vscsi->state;
+ vscsi->flags |= PREP_FOR_SUSPEND_PENDING;
+ if (crq->status == CRQ_ENTRY_OVERWRITTEN)
+ vscsi->flags |= PREP_FOR_SUSPEND_OVERWRITE;
+ ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0);
+ break;
+ case NO_QUEUE:
+ case UNDEFINED:
+ case UNCONFIGURING:
+ case WAIT_ENABLED:
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ case WAIT_IDLE:
+ dev_err(&vscsi->dev, "Invalid state for Prepare for Suspend Trans Event: 0x%x\n",
+ vscsi->state);
+ break;
+ }
+ break;
+
+ case RESUME_FROM_SUSP:
+ dev_dbg(&vscsi->dev, "Resume from Suspend, crq status = 0x%x\n",
+ (int)crq->status);
+ if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) {
+ vscsi->flags |= PREP_FOR_SUSPEND_ABORTED;
+ } else {
+ if ((crq->status == CRQ_ENTRY_OVERWRITTEN) ||
+ (vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE)) {
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT,
+ 0);
+ vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
+ }
+ }
+ break;
+
+ default:
+ rc = ERROR;
+ dev_err(&vscsi->dev, "trans_event: invalid format %d\n",
+ (uint)crq->format);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT,
+ RESPONSE_Q_DOWN);
+ break;
+ }
+
+ rc = vscsi->flags & SCHEDULE_DISCONNECT;
+
+ dev_dbg(&vscsi->dev, "Leaving trans_event: flags 0x%x, state 0x%hx, rc %ld\n",
+ vscsi->flags, vscsi->state, rc);
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_poll_cmd_q() - Poll Command Queue
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Called to handle command elements that may have arrived while
+ * interrupts were disabled.
+ *
+ * EXECUTION ENVIRONMENT:
+ * intr_lock must be held
+ */
+static void ibmvscsis_poll_cmd_q(struct scsi_info *vscsi)
+{
+ struct viosrp_crq *crq;
+ long rc;
+ bool ack = true;
+ volatile u8 valid;
+
+ dev_dbg(&vscsi->dev, "poll_cmd_q: flags 0x%x, state 0x%hx, q index %ud\n",
+ vscsi->flags, vscsi->state, vscsi->cmd_q.index);
+
+ rc = vscsi->flags & SCHEDULE_DISCONNECT;
+ crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
+ valid = crq->valid;
+ dma_rmb();
+
+ while (valid) {
+poll_work:
+ vscsi->cmd_q.index =
+ (vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask;
+
+ if (!rc) {
+ rc = ibmvscsis_parse_command(vscsi, crq);
+ } else {
+ if ((uint)crq->valid == VALID_TRANS_EVENT) {
+ /*
+ * must service the transport layer events even
+ * in an error state, dont break out until all
+ * the consecutive transport events have been
+ * processed
+ */
+ rc = ibmvscsis_trans_event(vscsi, crq);
+ } else if (vscsi->flags & TRANS_EVENT) {
+ /*
+ * if a tranport event has occurred leave
+ * everything but transport events on the queue
+ */
+ dev_dbg(&vscsi->dev, "poll_cmd_q, ignoring\n");
+
+ /*
+ * need to decrement the queue index so we can
+ * look at the elment again
+ */
+ if (vscsi->cmd_q.index)
+ vscsi->cmd_q.index -= 1;
+ else
+ /*
+ * index is at 0 it just wrapped.
+ * have it index last element in q
+ */
+ vscsi->cmd_q.index = vscsi->cmd_q.mask;
+ break;
+ }
+ }
+
+ crq->valid = INVALIDATE_CMD_RESP_EL;
+
+ crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
+ valid = crq->valid;
+ dma_rmb();
+ }
+
+ if (!rc) {
+ if (ack) {
+ vio_enable_interrupts(vscsi->dma_dev);
+ ack = false;
+ dev_dbg(&vscsi->dev, "poll_cmd_q, reenabling interrupts\n");
+ }
+ valid = crq->valid;
+ dma_rmb();
+ if (valid)
+ goto poll_work;
+ }
+
+ dev_dbg(&vscsi->dev, "Leaving poll_cmd_q: rc %ld\n", rc);
+}
+
+/**
+ * ibmvscsis_free_cmd_qs() - Free elements in queue
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Free all of the elements on all queues that are waiting for
+ * whatever reason.
+ *
+ * PRECONDITION:
+ * Called with interrupt lock held
+ */
+static void ibmvscsis_free_cmd_qs(struct scsi_info *vscsi)
+{
+ struct ibmvscsis_cmd *cmd, *nxt;
+
+ dev_dbg(&vscsi->dev, "free_cmd_qs: waiting_rsp empty %d, timer starter %d\n",
+ (int)list_empty(&vscsi->waiting_rsp),
+ vscsi->rsp_q_timer.started);
+
+ list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, list) {
+ list_del(&cmd->list);
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ }
+}
+
+/**
+ * ibmvscsis_get_free_cmd() - Get free command from list
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Must be called with interrupt lock held.
+ */
+static struct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi)
+{
+ struct ibmvscsis_cmd *cmd = NULL;
+ struct iu_entry *iue;
+
+ iue = srp_iu_get(&vscsi->target);
+ if (iue) {
+ cmd = list_first_entry_or_null(&vscsi->free_cmd,
+ struct ibmvscsis_cmd, list);
+ if (cmd) {
+ if (cmd->abort_cmd)
+ cmd->abort_cmd = NULL;
+ cmd->flags &= ~(DELAY_SEND);
+ list_del(&cmd->list);
+ cmd->iue = iue;
+ cmd->type = UNSET_TYPE;
+ memset(&cmd->se_cmd, 0, sizeof(cmd->se_cmd));
+ } else {
+ srp_iu_put(iue);
+ }
+ }
+
+ return cmd;
+}
+
+/**
+ * ibmvscsis_adapter_idle() - Helper function to handle idle adapter
+ * @vscsi: Pointer to our adapter structure
+ *
+ * This function is called when the adapter is idle when the driver
+ * is attempting to clear an error condition.
+ * The adapter is considered busy if any of its cmd queues
+ * are non-empty. This function can be invoked
+ * from the off level disconnect function.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process environment called with interrupt lock held
+ */
+static void ibmvscsis_adapter_idle(struct scsi_info *vscsi)
+{
+ int free_qs = false;
+ long rc = 0;
+
+ dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx\n",
+ vscsi->flags, vscsi->state);
+
+ /* Only need to free qs if we're disconnecting from client */
+ if (vscsi->state != WAIT_CONNECTION || vscsi->flags & TRANS_EVENT)
+ free_qs = true;
+
+ switch (vscsi->state) {
+ case UNCONFIGURING:
+ ibmvscsis_free_command_q(vscsi);
+ dma_rmb();
+ isync();
+ if (vscsi->flags & CFG_SLEEPING) {
+ vscsi->flags &= ~CFG_SLEEPING;
+ complete(&vscsi->unconfig);
+ }
+ break;
+ case ERR_DISCONNECT_RECONNECT:
+ ibmvscsis_reset_queue(vscsi);
+ dev_dbg(&vscsi->dev, "adapter_idle, disc_rec: flags 0x%x\n",
+ vscsi->flags);
+ break;
+
+ case ERR_DISCONNECT:
+ ibmvscsis_free_command_q(vscsi);
+ vscsi->flags &= ~(SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED);
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ if (vscsi->tport.enabled)
+ vscsi->state = ERR_DISCONNECTED;
+ else
+ vscsi->state = WAIT_ENABLED;
+ dev_dbg(&vscsi->dev, "adapter_idle, disc: flags 0x%x, state 0x%hx\n",
+ vscsi->flags, vscsi->state);
+ break;
+
+ case WAIT_IDLE:
+ vscsi->rsp_q_timer.timer_pops = 0;
+ vscsi->debit = 0;
+ vscsi->credit = 0;
+ if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) {
+ vscsi->state = vscsi->resume_state;
+ vscsi->resume_state = 0;
+ rc = ibmvscsis_ready_for_suspend(vscsi, true);
+ vscsi->flags &= ~DISCONNECT_SCHEDULED;
+ if (rc)
+ break;
+ } else if (vscsi->flags & TRANS_EVENT) {
+ vscsi->state = WAIT_CONNECTION;
+ vscsi->flags &= PRESERVE_FLAG_FIELDS;
+ } else {
+ vscsi->state = CONNECTED;
+ vscsi->flags &= ~DISCONNECT_SCHEDULED;
+ }
+
+ dev_dbg(&vscsi->dev, "adapter_idle, wait: flags 0x%x, state 0x%hx\n",
+ vscsi->flags, vscsi->state);
+ ibmvscsis_poll_cmd_q(vscsi);
+ break;
+
+ case ERR_DISCONNECTED:
+ vscsi->flags &= ~DISCONNECT_SCHEDULED;
+ dev_dbg(&vscsi->dev, "adapter_idle, disconnected: flags 0x%x, state 0x%hx\n",
+ vscsi->flags, vscsi->state);
+ break;
+
+ default:
+ dev_err(&vscsi->dev, "adapter_idle: in invalid state %d\n",
+ vscsi->state);
+ break;
+ }
+
+ if (free_qs)
+ ibmvscsis_free_cmd_qs(vscsi);
+
+ /*
+ * There is a timing window where we could lose a disconnect request.
+ * The known path to this window occurs during the DISCONNECT_RECONNECT
+ * case above: reset_queue calls free_command_q, which will release the
+ * interrupt lock. During that time, a new post_disconnect call can be
+ * made with a "more severe" state (DISCONNECT or UNCONFIGURING).
+ * Because the DISCONNECT_SCHEDULED flag is already set, post_disconnect
+ * will only set the new_state. Now free_command_q reacquires the intr
+ * lock and clears the DISCONNECT_SCHEDULED flag (using PRESERVE_FLAG_
+ * FIELDS), and the disconnect is lost. This is particularly bad when
+ * the new disconnect was for UNCONFIGURING, since the unconfigure hangs
+ * forever.
+ * Fix is that free command queue sets acr state and acr flags if there
+ * is a change under the lock
+ * note free command queue writes to this state it clears it
+ * before releasing the lock, different drivers call the free command
+ * queue different times so dont initialize above
+ */
+ if (vscsi->phyp_acr_state != 0) {
+ /*
+ * set any bits in flags that may have been cleared by
+ * a call to free command queue in switch statement
+ * or reset queue
+ */
+ vscsi->flags |= vscsi->phyp_acr_flags;
+ ibmvscsis_post_disconnect(vscsi, vscsi->phyp_acr_state, 0);
+ vscsi->phyp_acr_state = 0;
+ vscsi->phyp_acr_flags = 0;
+
+ dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n",
+ vscsi->flags, vscsi->state, vscsi->phyp_acr_flags,
+ vscsi->phyp_acr_state);
+ }
+
+ dev_dbg(&vscsi->dev, "Leaving adapter_idle: flags 0x%x, state 0x%hx, new_state 0x%x\n",
+ vscsi->flags, vscsi->state, vscsi->new_state);
+}
+
+/**
+ * ibmvscsis_copy_crq_packet() - Copy CRQ Packet
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Pointer to command element to use to process the request
+ * @crq: Pointer to CRQ entry containing the request
+ *
+ * Copy the srp information unit from the hosted
+ * partition using remote dma
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, interrupt lock held
+ */
+static long ibmvscsis_copy_crq_packet(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd,
+ struct viosrp_crq *crq)
+{
+ struct iu_entry *iue = cmd->iue;
+ long rc = 0;
+ u16 len;
+
+ len = be16_to_cpu(crq->IU_length);
+ if ((len > SRP_MAX_IU_LEN) || (len == 0)) {
+ dev_err(&vscsi->dev, "copy_crq: Invalid len %d passed", len);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ return SRP_VIOLATION;
+ }
+
+ rc = h_copy_rdma(len, vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(crq->IU_data_ptr),
+ vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma);
+
+ switch (rc) {
+ case H_SUCCESS:
+ cmd->init_time = mftb();
+ iue->remote_token = crq->IU_data_ptr;
+ iue->iu_len = len;
+ dev_dbg(&vscsi->dev, "copy_crq: ioba 0x%llx, init_time 0x%llx\n",
+ be64_to_cpu(crq->IU_data_ptr), cmd->init_time);
+ break;
+ case H_PERMISSION:
+ if (connection_broken(vscsi))
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT,
+ (RESPONSE_Q_DOWN |
+ CLIENT_FAILED));
+ else
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT, 0);
+
+ dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n",
+ rc);
+ break;
+ case H_DEST_PARM:
+ case H_SOURCE_PARM:
+ default:
+ dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_adapter_info - Service an Adapter Info MAnagement Data gram
+ * @vscsi: Pointer to our adapter structure
+ * @iue: Information Unit containing the Adapter Info MAD request
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt adapter lock is held
+ */
+static long ibmvscsis_adapter_info(struct scsi_info *vscsi,
+ struct iu_entry *iue)
+{
+ struct viosrp_adapter_info *mad = &vio_iu(iue)->mad.adapter_info;
+ struct mad_adapter_info_data *info;
+ uint flag_bits = 0;
+ dma_addr_t token;
+ long rc;
+
+ mad->common.status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
+
+ if (be16_to_cpu(mad->common.length) > sizeof(*info)) {
+ mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
+ return 0;
+ }
+
+ info = dma_alloc_coherent(&vscsi->dma_dev->dev, sizeof(*info), &token,
+ GFP_ATOMIC);
+ if (!info) {
+ dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n",
+ iue->target);
+ mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
+ return 0;
+ }
+
+ /* Get remote info */
+ rc = h_copy_rdma(be16_to_cpu(mad->common.length),
+ vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(mad->buffer),
+ vscsi->dds.window[LOCAL].liobn, token);
+
+ if (rc != H_SUCCESS) {
+ if (rc == H_PERMISSION) {
+ if (connection_broken(vscsi))
+ flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED);
+ }
+ dev_warn(&vscsi->dev, "adapter_info: h_copy_rdma from client failed, rc %ld\n",
+ rc);
+ dev_dbg(&vscsi->dev, "adapter_info: ioba 0x%llx, flags 0x%x, flag_bits 0x%x\n",
+ be64_to_cpu(mad->buffer), vscsi->flags, flag_bits);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
+ flag_bits);
+ goto free_dma;
+ }
+
+ /*
+ * Copy client info, but ignore partition number, which we
+ * already got from phyp - unless we failed to get it from
+ * phyp (e.g. if we're running on a p5 system).
+ */
+ if (vscsi->client_data.partition_number == 0)
+ vscsi->client_data.partition_number =
+ be32_to_cpu(info->partition_number);
+ strscpy(vscsi->client_data.srp_version, info->srp_version,
+ sizeof(vscsi->client_data.srp_version));
+ strscpy(vscsi->client_data.partition_name, info->partition_name,
+ sizeof(vscsi->client_data.partition_name));
+ vscsi->client_data.mad_version = be32_to_cpu(info->mad_version);
+ vscsi->client_data.os_type = be32_to_cpu(info->os_type);
+
+ /* Copy our info */
+ strscpy_pad(info->srp_version, SRP_VERSION,
+ sizeof(info->srp_version));
+ strscpy_pad(info->partition_name, vscsi->dds.partition_name,
+ sizeof(info->partition_name));
+ info->partition_number = cpu_to_be32(vscsi->dds.partition_num);
+ info->mad_version = cpu_to_be32(MAD_VERSION_1);
+ info->os_type = cpu_to_be32(LINUX);
+ memset(&info->port_max_txu[0], 0, sizeof(info->port_max_txu));
+ info->port_max_txu[0] = cpu_to_be32(MAX_TXU);
+
+ dma_wmb();
+ rc = h_copy_rdma(sizeof(*info), vscsi->dds.window[LOCAL].liobn,
+ token, vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(mad->buffer));
+ switch (rc) {
+ case H_SUCCESS:
+ break;
+
+ case H_SOURCE_PARM:
+ case H_DEST_PARM:
+ case H_PERMISSION:
+ if (connection_broken(vscsi))
+ flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED);
+ fallthrough;
+ default:
+ dev_err(&vscsi->dev, "adapter_info: h_copy_rdma to client failed, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT,
+ flag_bits);
+ break;
+ }
+
+free_dma:
+ dma_free_coherent(&vscsi->dma_dev->dev, sizeof(*info), info, token);
+ dev_dbg(&vscsi->dev, "Leaving adapter_info, rc %ld\n", rc);
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_cap_mad() - Service a Capabilities MAnagement Data gram
+ * @vscsi: Pointer to our adapter structure
+ * @iue: Information Unit containing the Capabilities MAD request
+ *
+ * NOTE: if you return an error from this routine you must be
+ * disconnecting or you will cause a hang
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt called with adapter lock held
+ */
+static int ibmvscsis_cap_mad(struct scsi_info *vscsi, struct iu_entry *iue)
+{
+ struct viosrp_capabilities *mad = &vio_iu(iue)->mad.capabilities;
+ struct capabilities *cap;
+ struct mad_capability_common *common;
+ dma_addr_t token;
+ u16 olen, len, status, min_len, cap_len;
+ u32 flag;
+ uint flag_bits = 0;
+ long rc = 0;
+
+ olen = be16_to_cpu(mad->common.length);
+ /*
+ * struct capabilities hardcodes a couple capabilities after the
+ * header, but the capabilities can actually be in any order.
+ */
+ min_len = offsetof(struct capabilities, migration);
+ if ((olen < min_len) || (olen > PAGE_SIZE)) {
+ dev_warn(&vscsi->dev, "cap_mad: invalid len %d\n", olen);
+ mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
+ return 0;
+ }
+
+ cap = dma_alloc_coherent(&vscsi->dma_dev->dev, olen, &token,
+ GFP_ATOMIC);
+ if (!cap) {
+ dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n",
+ iue->target);
+ mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
+ return 0;
+ }
+ rc = h_copy_rdma(olen, vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(mad->buffer),
+ vscsi->dds.window[LOCAL].liobn, token);
+ if (rc == H_SUCCESS) {
+ strscpy_pad(cap->name, dev_name(&vscsi->dma_dev->dev),
+ sizeof(cap->name));
+
+ len = olen - min_len;
+ status = VIOSRP_MAD_SUCCESS;
+ common = (struct mad_capability_common *)&cap->migration;
+
+ while ((len > 0) && (status == VIOSRP_MAD_SUCCESS) && !rc) {
+ dev_dbg(&vscsi->dev, "cap_mad: len left %hd, cap type %d, cap len %hd\n",
+ len, be32_to_cpu(common->cap_type),
+ be16_to_cpu(common->length));
+
+ cap_len = be16_to_cpu(common->length);
+ if (cap_len > len) {
+ dev_err(&vscsi->dev, "cap_mad: cap len mismatch with total len\n");
+ status = VIOSRP_MAD_FAILED;
+ break;
+ }
+
+ if (cap_len == 0) {
+ dev_err(&vscsi->dev, "cap_mad: cap len is 0\n");
+ status = VIOSRP_MAD_FAILED;
+ break;
+ }
+
+ switch (common->cap_type) {
+ default:
+ dev_dbg(&vscsi->dev, "cap_mad: unsupported capability\n");
+ common->server_support = 0;
+ flag = cpu_to_be32((u32)CAP_LIST_SUPPORTED);
+ cap->flags &= ~flag;
+ break;
+ }
+
+ len = len - cap_len;
+ common = (struct mad_capability_common *)
+ ((char *)common + cap_len);
+ }
+
+ mad->common.status = cpu_to_be16(status);
+
+ dma_wmb();
+ rc = h_copy_rdma(olen, vscsi->dds.window[LOCAL].liobn, token,
+ vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(mad->buffer));
+
+ if (rc != H_SUCCESS) {
+ dev_dbg(&vscsi->dev, "cap_mad: failed to copy to client, rc %ld\n",
+ rc);
+
+ if (rc == H_PERMISSION) {
+ if (connection_broken(vscsi))
+ flag_bits = (RESPONSE_Q_DOWN |
+ CLIENT_FAILED);
+ }
+
+ dev_warn(&vscsi->dev, "cap_mad: error copying data to client, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT,
+ flag_bits);
+ }
+ }
+
+ dma_free_coherent(&vscsi->dma_dev->dev, olen, cap, token);
+
+ dev_dbg(&vscsi->dev, "Leaving cap_mad, rc %ld, client_cap 0x%x\n",
+ rc, vscsi->client_cap);
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_process_mad() - Service a MAnagement Data gram
+ * @vscsi: Pointer to our adapter structure
+ * @iue: Information Unit containing the MAD request
+ *
+ * Must be called with interrupt lock held.
+ */
+static long ibmvscsis_process_mad(struct scsi_info *vscsi, struct iu_entry *iue)
+{
+ struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad;
+ struct viosrp_empty_iu *empty;
+ long rc = ADAPT_SUCCESS;
+
+ switch (be32_to_cpu(mad->type)) {
+ case VIOSRP_EMPTY_IU_TYPE:
+ empty = &vio_iu(iue)->mad.empty_iu;
+ vscsi->empty_iu_id = be64_to_cpu(empty->buffer);
+ vscsi->empty_iu_tag = be64_to_cpu(empty->common.tag);
+ mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
+ break;
+ case VIOSRP_ADAPTER_INFO_TYPE:
+ rc = ibmvscsis_adapter_info(vscsi, iue);
+ break;
+ case VIOSRP_CAPABILITIES_TYPE:
+ rc = ibmvscsis_cap_mad(vscsi, iue);
+ break;
+ case VIOSRP_ENABLE_FAST_FAIL:
+ if (vscsi->state == CONNECTED) {
+ vscsi->fast_fail = true;
+ mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
+ } else {
+ dev_warn(&vscsi->dev, "fast fail mad sent after login\n");
+ mad->status = cpu_to_be16(VIOSRP_MAD_FAILED);
+ }
+ break;
+ default:
+ mad->status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED);
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * srp_snd_msg_failed() - Handle an error when sending a response
+ * @vscsi: Pointer to our adapter structure
+ * @rc: The return code from the h_send_crq command
+ *
+ * Must be called with interrupt lock held.
+ */
+static void srp_snd_msg_failed(struct scsi_info *vscsi, long rc)
+{
+ ktime_t kt;
+
+ if (rc != H_DROPPED) {
+ ibmvscsis_free_cmd_qs(vscsi);
+
+ if (rc == H_CLOSED)
+ vscsi->flags |= CLIENT_FAILED;
+
+ /* don't flag the same problem multiple times */
+ if (!(vscsi->flags & RESPONSE_Q_DOWN)) {
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ if (!(vscsi->state & (ERR_DISCONNECT |
+ ERR_DISCONNECT_RECONNECT |
+ ERR_DISCONNECTED | UNDEFINED))) {
+ dev_err(&vscsi->dev, "snd_msg_failed: setting RESPONSE_Q_DOWN, state 0x%hx, flags 0x%x, rc %ld\n",
+ vscsi->state, vscsi->flags, rc);
+ }
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT, 0);
+ }
+ return;
+ }
+
+ /*
+ * The response queue is full.
+ * If the server is processing SRP requests, i.e.
+ * the client has successfully done an
+ * SRP_LOGIN, then it will wait forever for room in
+ * the queue. However if the system admin
+ * is attempting to unconfigure the server then one
+ * or more children will be in a state where
+ * they are being removed. So if there is even one
+ * child being removed then the driver assumes
+ * the system admin is attempting to break the
+ * connection with the client and MAX_TIMER_POPS
+ * is honored.
+ */
+ if ((vscsi->rsp_q_timer.timer_pops < MAX_TIMER_POPS) ||
+ (vscsi->state == SRP_PROCESSING)) {
+ dev_dbg(&vscsi->dev, "snd_msg_failed: response queue full, flags 0x%x, timer started %d, pops %d\n",
+ vscsi->flags, (int)vscsi->rsp_q_timer.started,
+ vscsi->rsp_q_timer.timer_pops);
+
+ /*
+ * Check if the timer is running; if it
+ * is not then start it up.
+ */
+ if (!vscsi->rsp_q_timer.started) {
+ if (vscsi->rsp_q_timer.timer_pops <
+ MAX_TIMER_POPS) {
+ kt = WAIT_NANO_SECONDS;
+ } else {
+ /*
+ * slide the timeslice if the maximum
+ * timer pops have already happened
+ */
+ kt = ktime_set(WAIT_SECONDS, 0);
+ }
+
+ vscsi->rsp_q_timer.started = true;
+ hrtimer_start(&vscsi->rsp_q_timer.timer, kt,
+ HRTIMER_MODE_REL);
+ }
+ } else {
+ /*
+ * TBD: Do we need to worry about this? Need to get
+ * remove working.
+ */
+ /*
+ * waited a long time and it appears the system admin
+ * is bring this driver down
+ */
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ ibmvscsis_free_cmd_qs(vscsi);
+ /*
+ * if the driver is already attempting to disconnect
+ * from the client and has already logged an error
+ * trace this event but don't put it in the error log
+ */
+ if (!(vscsi->state & (ERR_DISCONNECT |
+ ERR_DISCONNECT_RECONNECT |
+ ERR_DISCONNECTED | UNDEFINED))) {
+ dev_err(&vscsi->dev, "client crq full too long\n");
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT,
+ 0);
+ }
+ }
+}
+
+/**
+ * ibmvscsis_send_messages() - Send a Response
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Send a response, first checking the waiting queue. Responses are
+ * sent in order they are received. If the response cannot be sent,
+ * because the client queue is full, it stays on the waiting queue.
+ *
+ * PRECONDITION:
+ * Called with interrupt lock held
+ */
+static void ibmvscsis_send_messages(struct scsi_info *vscsi)
+{
+ struct viosrp_crq empty_crq = { };
+ struct viosrp_crq *crq = &empty_crq;
+ struct ibmvscsis_cmd *cmd, *nxt;
+ long rc = ADAPT_SUCCESS;
+ bool retry = false;
+
+ if (!(vscsi->flags & RESPONSE_Q_DOWN)) {
+ do {
+ retry = false;
+ list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp,
+ list) {
+ /*
+ * Check to make sure abort cmd gets processed
+ * prior to the abort tmr cmd
+ */
+ if (cmd->flags & DELAY_SEND)
+ continue;
+
+ if (cmd->abort_cmd) {
+ retry = true;
+ cmd->abort_cmd->flags &= ~(DELAY_SEND);
+ cmd->abort_cmd = NULL;
+ }
+
+ /*
+ * If CMD_T_ABORTED w/o CMD_T_TAS scenarios and
+ * the case where LIO issued a
+ * ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST
+ * case then we dont send a response, since it
+ * was already done.
+ */
+ if (cmd->se_cmd.transport_state & CMD_T_ABORTED &&
+ !(cmd->se_cmd.transport_state & CMD_T_TAS)) {
+ list_del(&cmd->list);
+ ibmvscsis_free_cmd_resources(vscsi,
+ cmd);
+ /*
+ * With a successfully aborted op
+ * through LIO we want to increment the
+ * the vscsi credit so that when we dont
+ * send a rsp to the original scsi abort
+ * op (h_send_crq), but the tm rsp to
+ * the abort is sent, the credit is
+ * correctly sent with the abort tm rsp.
+ * We would need 1 for the abort tm rsp
+ * and 1 credit for the aborted scsi op.
+ * Thus we need to increment here.
+ * Also we want to increment the credit
+ * here because we want to make sure
+ * cmd is actually released first
+ * otherwise the client will think it
+ * it can send a new cmd, and we could
+ * find ourselves short of cmd elements.
+ */
+ vscsi->credit += 1;
+ } else {
+ crq->valid = VALID_CMD_RESP_EL;
+ crq->format = cmd->rsp.format;
+
+ if (cmd->flags & CMD_FAST_FAIL)
+ crq->status = VIOSRP_ADAPTER_FAIL;
+
+ crq->IU_length = cpu_to_be16(cmd->rsp.len);
+
+ rc = h_send_crq(vscsi->dma_dev->unit_address,
+ be64_to_cpu(crq->high),
+ be64_to_cpu(cmd->rsp.tag));
+
+ dev_dbg(&vscsi->dev, "send_messages: cmd %p, tag 0x%llx, rc %ld\n",
+ cmd, be64_to_cpu(cmd->rsp.tag),
+ rc);
+
+ /* if all ok free up the command
+ * element resources
+ */
+ if (rc == H_SUCCESS) {
+ /* some movement has occurred */
+ vscsi->rsp_q_timer.timer_pops = 0;
+ list_del(&cmd->list);
+
+ ibmvscsis_free_cmd_resources(vscsi,
+ cmd);
+ } else {
+ srp_snd_msg_failed(vscsi, rc);
+ break;
+ }
+ }
+ }
+ } while (retry);
+
+ if (!rc) {
+ /*
+ * The timer could pop with the queue empty. If
+ * this happens, rc will always indicate a
+ * success; clear the pop count.
+ */
+ vscsi->rsp_q_timer.timer_pops = 0;
+ }
+ } else {
+ ibmvscsis_free_cmd_qs(vscsi);
+ }
+}
+
+/* Called with intr lock held */
+static void ibmvscsis_send_mad_resp(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd,
+ struct viosrp_crq *crq)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad;
+ uint flag_bits = 0;
+ long rc;
+
+ dma_wmb();
+ rc = h_copy_rdma(sizeof(struct mad_common),
+ vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma,
+ vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(crq->IU_data_ptr));
+ if (!rc) {
+ cmd->rsp.format = VIOSRP_MAD_FORMAT;
+ cmd->rsp.len = sizeof(struct mad_common);
+ cmd->rsp.tag = mad->tag;
+ list_add_tail(&cmd->list, &vscsi->waiting_rsp);
+ ibmvscsis_send_messages(vscsi);
+ } else {
+ dev_dbg(&vscsi->dev, "Error sending mad response, rc %ld\n",
+ rc);
+ if (rc == H_PERMISSION) {
+ if (connection_broken(vscsi))
+ flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED);
+ }
+ dev_err(&vscsi->dev, "mad: failed to copy to client, rc %ld\n",
+ rc);
+
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
+ flag_bits);
+ }
+}
+
+/**
+ * ibmvscsis_mad() - Service a MAnagement Data gram.
+ * @vscsi: Pointer to our adapter structure
+ * @crq: Pointer to the CRQ entry containing the MAD request
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, called with adapter lock held
+ */
+static long ibmvscsis_mad(struct scsi_info *vscsi, struct viosrp_crq *crq)
+{
+ struct iu_entry *iue;
+ struct ibmvscsis_cmd *cmd;
+ struct mad_common *mad;
+ long rc = ADAPT_SUCCESS;
+
+ switch (vscsi->state) {
+ /*
+ * We have not exchanged Init Msgs yet, so this MAD was sent
+ * before the last Transport Event; client will not be
+ * expecting a response.
+ */
+ case WAIT_CONNECTION:
+ dev_dbg(&vscsi->dev, "mad: in Wait Connection state, ignoring MAD, flags %d\n",
+ vscsi->flags);
+ return ADAPT_SUCCESS;
+
+ case SRP_PROCESSING:
+ case CONNECTED:
+ break;
+
+ /*
+ * We should never get here while we're in these states.
+ * Just log an error and get out.
+ */
+ case UNCONFIGURING:
+ case WAIT_IDLE:
+ case ERR_DISCONNECT:
+ case ERR_DISCONNECT_RECONNECT:
+ default:
+ dev_err(&vscsi->dev, "mad: invalid adapter state %d for mad\n",
+ vscsi->state);
+ return ADAPT_SUCCESS;
+ }
+
+ cmd = ibmvscsis_get_free_cmd(vscsi);
+ if (!cmd) {
+ dev_err(&vscsi->dev, "mad: failed to get cmd, debit %d\n",
+ vscsi->debit);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ return ERROR;
+ }
+ iue = cmd->iue;
+ cmd->type = ADAPTER_MAD;
+
+ rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq);
+ if (!rc) {
+ mad = (struct mad_common *)&vio_iu(iue)->mad;
+
+ dev_dbg(&vscsi->dev, "mad: type %d\n", be32_to_cpu(mad->type));
+
+ rc = ibmvscsis_process_mad(vscsi, iue);
+
+ dev_dbg(&vscsi->dev, "mad: status %hd, rc %ld\n",
+ be16_to_cpu(mad->status), rc);
+
+ if (!rc)
+ ibmvscsis_send_mad_resp(vscsi, cmd, crq);
+ } else {
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ }
+
+ dev_dbg(&vscsi->dev, "Leaving mad, rc %ld\n", rc);
+ return rc;
+}
+
+/**
+ * ibmvscsis_login_rsp() - Create/copy a login response notice to the client
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Pointer to the command for the SRP Login request
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, interrupt lock held
+ */
+static long ibmvscsis_login_rsp(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct srp_login_rsp *rsp = &vio_iu(iue)->srp.login_rsp;
+ struct format_code *fmt;
+ uint flag_bits = 0;
+ long rc = ADAPT_SUCCESS;
+
+ memset(rsp, 0, sizeof(struct srp_login_rsp));
+
+ rsp->opcode = SRP_LOGIN_RSP;
+ rsp->req_lim_delta = cpu_to_be32(vscsi->request_limit);
+ rsp->tag = cmd->rsp.tag;
+ rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN);
+ rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN);
+ fmt = (struct format_code *)&rsp->buf_fmt;
+ fmt->buffers = SUPPORTED_FORMATS;
+ vscsi->credit = 0;
+
+ cmd->rsp.len = sizeof(struct srp_login_rsp);
+
+ dma_wmb();
+ rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn,
+ iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(iue->remote_token));
+
+ switch (rc) {
+ case H_SUCCESS:
+ break;
+
+ case H_PERMISSION:
+ if (connection_broken(vscsi))
+ flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED;
+ dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
+ flag_bits);
+ break;
+ case H_SOURCE_PARM:
+ case H_DEST_PARM:
+ default:
+ dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_srp_login_rej() - Create/copy a login rejection notice to client
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Pointer to the command for the SRP Login request
+ * @reason: The reason the SRP Login is being rejected, per SRP protocol
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, interrupt lock held
+ */
+static long ibmvscsis_srp_login_rej(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd, u32 reason)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct srp_login_rej *rej = &vio_iu(iue)->srp.login_rej;
+ struct format_code *fmt;
+ uint flag_bits = 0;
+ long rc = ADAPT_SUCCESS;
+
+ memset(rej, 0, sizeof(*rej));
+
+ rej->opcode = SRP_LOGIN_REJ;
+ rej->reason = cpu_to_be32(reason);
+ rej->tag = cmd->rsp.tag;
+ fmt = (struct format_code *)&rej->buf_fmt;
+ fmt->buffers = SUPPORTED_FORMATS;
+
+ cmd->rsp.len = sizeof(*rej);
+
+ dma_wmb();
+ rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn,
+ iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(iue->remote_token));
+
+ switch (rc) {
+ case H_SUCCESS:
+ break;
+ case H_PERMISSION:
+ if (connection_broken(vscsi))
+ flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED;
+ dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
+ flag_bits);
+ break;
+ case H_SOURCE_PARM:
+ case H_DEST_PARM:
+ default:
+ dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+
+ return rc;
+}
+
+static int ibmvscsis_make_nexus(struct ibmvscsis_tport *tport)
+{
+ char *name = tport->tport_name;
+ struct ibmvscsis_nexus *nexus;
+ struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport);
+ int rc;
+
+ if (tport->ibmv_nexus) {
+ dev_dbg(&vscsi->dev, "tport->ibmv_nexus already exists\n");
+ return 0;
+ }
+
+ nexus = kzalloc(sizeof(*nexus), GFP_KERNEL);
+ if (!nexus) {
+ dev_err(&vscsi->dev, "Unable to allocate struct ibmvscsis_nexus\n");
+ return -ENOMEM;
+ }
+
+ nexus->se_sess = target_setup_session(&tport->se_tpg, 0, 0,
+ TARGET_PROT_NORMAL, name, nexus,
+ NULL);
+ if (IS_ERR(nexus->se_sess)) {
+ rc = PTR_ERR(nexus->se_sess);
+ goto transport_init_fail;
+ }
+
+ tport->ibmv_nexus = nexus;
+
+ return 0;
+
+transport_init_fail:
+ kfree(nexus);
+ return rc;
+}
+
+static int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport)
+{
+ struct se_session *se_sess;
+ struct ibmvscsis_nexus *nexus;
+
+ nexus = tport->ibmv_nexus;
+ if (!nexus)
+ return -ENODEV;
+
+ se_sess = nexus->se_sess;
+ if (!se_sess)
+ return -ENODEV;
+
+ /*
+ * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port
+ */
+ target_remove_session(se_sess);
+ tport->ibmv_nexus = NULL;
+ kfree(nexus);
+
+ return 0;
+}
+
+/**
+ * ibmvscsis_srp_login() - Process an SRP Login Request
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Command element to use to process the SRP Login request
+ * @crq: Pointer to CRQ entry containing the SRP Login request
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, called with interrupt lock held
+ */
+static long ibmvscsis_srp_login(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd,
+ struct viosrp_crq *crq)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct srp_login_req *req = &vio_iu(iue)->srp.login_req;
+ struct port_id {
+ __be64 id_extension;
+ __be64 io_guid;
+ } *iport, *tport;
+ struct format_code *fmt;
+ u32 reason = 0x0;
+ long rc = ADAPT_SUCCESS;
+
+ iport = (struct port_id *)req->initiator_port_id;
+ tport = (struct port_id *)req->target_port_id;
+ fmt = (struct format_code *)&req->req_buf_fmt;
+ if (be32_to_cpu(req->req_it_iu_len) > SRP_MAX_IU_LEN)
+ reason = SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE;
+ else if (be32_to_cpu(req->req_it_iu_len) < 64)
+ reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL;
+ else if ((be64_to_cpu(iport->id_extension) > (MAX_NUM_PORTS - 1)) ||
+ (be64_to_cpu(tport->id_extension) > (MAX_NUM_PORTS - 1)))
+ reason = SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL;
+ else if (req->req_flags & SRP_MULTICHAN_MULTI)
+ reason = SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED;
+ else if (fmt->buffers & (~SUPPORTED_FORMATS))
+ reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT;
+ else if ((fmt->buffers & SUPPORTED_FORMATS) == 0)
+ reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT;
+
+ if (vscsi->state == SRP_PROCESSING)
+ reason = SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED;
+
+ rc = ibmvscsis_make_nexus(&vscsi->tport);
+ if (rc)
+ reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL;
+
+ cmd->rsp.format = VIOSRP_SRP_FORMAT;
+ cmd->rsp.tag = req->tag;
+
+ dev_dbg(&vscsi->dev, "srp_login: reason 0x%x\n", reason);
+
+ if (reason)
+ rc = ibmvscsis_srp_login_rej(vscsi, cmd, reason);
+ else
+ rc = ibmvscsis_login_rsp(vscsi, cmd);
+
+ if (!rc) {
+ if (!reason)
+ vscsi->state = SRP_PROCESSING;
+
+ list_add_tail(&cmd->list, &vscsi->waiting_rsp);
+ ibmvscsis_send_messages(vscsi);
+ } else {
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ }
+
+ dev_dbg(&vscsi->dev, "Leaving srp_login, rc %ld\n", rc);
+ return rc;
+}
+
+/**
+ * ibmvscsis_srp_i_logout() - Helper Function to close I_T Nexus
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Command element to use to process the Implicit Logout request
+ * @crq: Pointer to CRQ entry containing the Implicit Logout request
+ *
+ * Do the logic to close the I_T nexus. This function may not
+ * behave to specification.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, interrupt lock held
+ */
+static long ibmvscsis_srp_i_logout(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd,
+ struct viosrp_crq *crq)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct srp_i_logout *log_out = &vio_iu(iue)->srp.i_logout;
+
+ if ((vscsi->debit > 0) || !list_empty(&vscsi->schedule_q) ||
+ !list_empty(&vscsi->waiting_rsp)) {
+ dev_err(&vscsi->dev, "i_logout: outstanding work\n");
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
+ } else {
+ cmd->rsp.format = SRP_FORMAT;
+ cmd->rsp.tag = log_out->tag;
+ cmd->rsp.len = sizeof(struct mad_common);
+ list_add_tail(&cmd->list, &vscsi->waiting_rsp);
+ ibmvscsis_send_messages(vscsi);
+
+ ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0);
+ }
+
+ return ADAPT_SUCCESS;
+}
+
+/* Called with intr lock held */
+static void ibmvscsis_srp_cmd(struct scsi_info *vscsi, struct viosrp_crq *crq)
+{
+ struct ibmvscsis_cmd *cmd;
+ struct iu_entry *iue;
+ struct srp_cmd *srp;
+ struct srp_tsk_mgmt *tsk;
+ long rc;
+
+ if (vscsi->request_limit - vscsi->debit <= 0) {
+ /* Client has exceeded request limit */
+ dev_err(&vscsi->dev, "Client exceeded the request limit (%d), debit %d\n",
+ vscsi->request_limit, vscsi->debit);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ return;
+ }
+
+ cmd = ibmvscsis_get_free_cmd(vscsi);
+ if (!cmd) {
+ dev_err(&vscsi->dev, "srp_cmd failed to get cmd, debit %d\n",
+ vscsi->debit);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ return;
+ }
+ iue = cmd->iue;
+ srp = &vio_iu(iue)->srp.cmd;
+
+ rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq);
+ if (rc) {
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ return;
+ }
+
+ if (vscsi->state == SRP_PROCESSING) {
+ switch (srp->opcode) {
+ case SRP_LOGIN_REQ:
+ rc = ibmvscsis_srp_login(vscsi, cmd, crq);
+ break;
+
+ case SRP_TSK_MGMT:
+ tsk = &vio_iu(iue)->srp.tsk_mgmt;
+ dev_dbg(&vscsi->dev, "tsk_mgmt tag: %llu (0x%llx)\n",
+ tsk->tag, tsk->tag);
+ cmd->rsp.tag = tsk->tag;
+ vscsi->debit += 1;
+ cmd->type = TASK_MANAGEMENT;
+ list_add_tail(&cmd->list, &vscsi->schedule_q);
+ queue_work(vscsi->work_q, &cmd->work);
+ break;
+
+ case SRP_CMD:
+ dev_dbg(&vscsi->dev, "srp_cmd tag: %llu (0x%llx)\n",
+ srp->tag, srp->tag);
+ cmd->rsp.tag = srp->tag;
+ vscsi->debit += 1;
+ cmd->type = SCSI_CDB;
+ /*
+ * We want to keep track of work waiting for
+ * the workqueue.
+ */
+ list_add_tail(&cmd->list, &vscsi->schedule_q);
+ queue_work(vscsi->work_q, &cmd->work);
+ break;
+
+ case SRP_I_LOGOUT:
+ rc = ibmvscsis_srp_i_logout(vscsi, cmd, crq);
+ break;
+
+ case SRP_CRED_RSP:
+ case SRP_AER_RSP:
+ default:
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ dev_err(&vscsi->dev, "invalid srp cmd, opcode %d\n",
+ (uint)srp->opcode);
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+ } else if (srp->opcode == SRP_LOGIN_REQ && vscsi->state == CONNECTED) {
+ rc = ibmvscsis_srp_login(vscsi, cmd, crq);
+ } else {
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ dev_err(&vscsi->dev, "Invalid state %d to handle srp cmd\n",
+ vscsi->state);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ }
+}
+
+/**
+ * ibmvscsis_ping_response() - Respond to a ping request
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Let the client know that the server is alive and waiting on
+ * its native I/O stack.
+ * If any type of error occurs from the call to queue a ping
+ * response then the client is either not accepting or receiving
+ * interrupts. Disconnect with an error.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, interrupt lock held
+ */
+static long ibmvscsis_ping_response(struct scsi_info *vscsi)
+{
+ struct viosrp_crq *crq;
+ u64 buffer[2] = { 0, 0 };
+ long rc;
+
+ crq = (struct viosrp_crq *)&buffer;
+ crq->valid = VALID_CMD_RESP_EL;
+ crq->format = (u8)MESSAGE_IN_CRQ;
+ crq->status = PING_RESPONSE;
+
+ rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]),
+ cpu_to_be64(buffer[MSG_LOW]));
+
+ switch (rc) {
+ case H_SUCCESS:
+ break;
+ case H_CLOSED:
+ vscsi->flags |= CLIENT_FAILED;
+ fallthrough;
+ case H_DROPPED:
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ fallthrough;
+ case H_REMOTE_PARM:
+ dev_err(&vscsi->dev, "ping_response: h_send_crq failed, rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ default:
+ dev_err(&vscsi->dev, "ping_response: h_send_crq returned unknown rc %ld\n",
+ rc);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_parse_command() - Parse an element taken from the cmd rsp queue.
+ * @vscsi: Pointer to our adapter structure
+ * @crq: Pointer to CRQ element containing the SRP request
+ *
+ * This function will return success if the command queue element is valid
+ * and the srp iu or MAD request it pointed to was also valid. That does
+ * not mean that an error was not returned to the client.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Interrupt, intr lock held
+ */
+static long ibmvscsis_parse_command(struct scsi_info *vscsi,
+ struct viosrp_crq *crq)
+{
+ long rc = ADAPT_SUCCESS;
+
+ switch (crq->valid) {
+ case VALID_CMD_RESP_EL:
+ switch (crq->format) {
+ case OS400_FORMAT:
+ case AIX_FORMAT:
+ case LINUX_FORMAT:
+ case MAD_FORMAT:
+ if (vscsi->flags & PROCESSING_MAD) {
+ rc = ERROR;
+ dev_err(&vscsi->dev, "parse_command: already processing mad\n");
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT,
+ 0);
+ } else {
+ vscsi->flags |= PROCESSING_MAD;
+ rc = ibmvscsis_mad(vscsi, crq);
+ }
+ break;
+
+ case SRP_FORMAT:
+ ibmvscsis_srp_cmd(vscsi, crq);
+ break;
+
+ case MESSAGE_IN_CRQ:
+ if (crq->status == PING)
+ ibmvscsis_ping_response(vscsi);
+ break;
+
+ default:
+ dev_err(&vscsi->dev, "parse_command: invalid format %d\n",
+ (uint)crq->format);
+ ibmvscsis_post_disconnect(vscsi,
+ ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+ break;
+
+ case VALID_TRANS_EVENT:
+ rc = ibmvscsis_trans_event(vscsi, crq);
+ break;
+
+ case VALID_INIT_MSG:
+ rc = ibmvscsis_init_msg(vscsi, crq);
+ break;
+
+ default:
+ dev_err(&vscsi->dev, "parse_command: invalid valid field %d\n",
+ (uint)crq->valid);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ break;
+ }
+
+ /*
+ * Return only what the interrupt handler cares
+ * about. Most errors we keep right on trucking.
+ */
+ rc = vscsi->flags & SCHEDULE_DISCONNECT;
+
+ return rc;
+}
+
+static int read_dma_window(struct scsi_info *vscsi)
+{
+ struct vio_dev *vdev = vscsi->dma_dev;
+ const __be32 *dma_window;
+ const __be32 *prop;
+
+ /* TODO Using of_parse_dma_window would be better, but it doesn't give
+ * a way to read multiple windows without already knowing the size of
+ * a window or the number of windows.
+ */
+ dma_window = (const __be32 *)vio_get_attribute(vdev,
+ "ibm,my-dma-window",
+ NULL);
+ if (!dma_window) {
+ dev_err(&vscsi->dev, "Couldn't find ibm,my-dma-window property\n");
+ return -1;
+ }
+
+ vscsi->dds.window[LOCAL].liobn = be32_to_cpu(*dma_window);
+ dma_window++;
+
+ prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells",
+ NULL);
+ if (!prop) {
+ dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-address-cells property\n");
+ dma_window++;
+ } else {
+ dma_window += be32_to_cpu(*prop);
+ }
+
+ prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells",
+ NULL);
+ if (!prop) {
+ dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-size-cells property\n");
+ dma_window++;
+ } else {
+ dma_window += be32_to_cpu(*prop);
+ }
+
+ /* dma_window should point to the second window now */
+ vscsi->dds.window[REMOTE].liobn = be32_to_cpu(*dma_window);
+
+ return 0;
+}
+
+static struct ibmvscsis_tport *ibmvscsis_lookup_port(const char *name)
+{
+ struct ibmvscsis_tport *tport = NULL;
+ struct vio_dev *vdev;
+ struct scsi_info *vscsi;
+
+ spin_lock_bh(&ibmvscsis_dev_lock);
+ list_for_each_entry(vscsi, &ibmvscsis_dev_list, list) {
+ vdev = vscsi->dma_dev;
+ if (!strcmp(dev_name(&vdev->dev), name)) {
+ tport = &vscsi->tport;
+ break;
+ }
+ }
+ spin_unlock_bh(&ibmvscsis_dev_lock);
+
+ return tport;
+}
+
+/**
+ * ibmvscsis_parse_cmd() - Parse SRP Command
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Pointer to command element with SRP command
+ *
+ * Parse the srp command; if it is valid then submit it to tcm.
+ * Note: The return code does not reflect the status of the SCSI CDB.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process level
+ */
+static void ibmvscsis_parse_cmd(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf;
+ struct ibmvscsis_nexus *nexus;
+ u64 data_len = 0;
+ enum dma_data_direction dir;
+ int attr = 0;
+
+ nexus = vscsi->tport.ibmv_nexus;
+ /*
+ * additional length in bytes. Note that the SRP spec says that
+ * additional length is in 4-byte words, but technically the
+ * additional length field is only the upper 6 bits of the byte.
+ * The lower 2 bits are reserved. If the lower 2 bits are 0 (as
+ * all reserved fields should be), then interpreting the byte as
+ * an int will yield the length in bytes.
+ */
+ if (srp->add_cdb_len & 0x03) {
+ dev_err(&vscsi->dev, "parse_cmd: reserved bits set in IU\n");
+ spin_lock_bh(&vscsi->intr_lock);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ spin_unlock_bh(&vscsi->intr_lock);
+ return;
+ }
+
+ if (srp_get_desc_table(srp, &dir, &data_len)) {
+ dev_err(&vscsi->dev, "0x%llx: parsing SRP descriptor table failed.\n",
+ srp->tag);
+ goto fail;
+ }
+
+ cmd->rsp.sol_not = srp->sol_not;
+
+ switch (srp->task_attr) {
+ case SRP_SIMPLE_TASK:
+ attr = TCM_SIMPLE_TAG;
+ break;
+ case SRP_ORDERED_TASK:
+ attr = TCM_ORDERED_TAG;
+ break;
+ case SRP_HEAD_TASK:
+ attr = TCM_HEAD_TAG;
+ break;
+ case SRP_ACA_TASK:
+ attr = TCM_ACA_TAG;
+ break;
+ default:
+ dev_err(&vscsi->dev, "Invalid task attribute %d\n",
+ srp->task_attr);
+ goto fail;
+ }
+
+ cmd->se_cmd.tag = be64_to_cpu(srp->tag);
+
+ spin_lock_bh(&vscsi->intr_lock);
+ list_add_tail(&cmd->list, &vscsi->active_q);
+ spin_unlock_bh(&vscsi->intr_lock);
+
+ srp->lun.scsi_lun[0] &= 0x3f;
+
+ target_submit_cmd(&cmd->se_cmd, nexus->se_sess, srp->cdb,
+ cmd->sense_buf, scsilun_to_int(&srp->lun),
+ data_len, attr, dir, 0);
+ return;
+
+fail:
+ spin_lock_bh(&vscsi->intr_lock);
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
+ spin_unlock_bh(&vscsi->intr_lock);
+}
+
+/**
+ * ibmvscsis_parse_task() - Parse SRP Task Management Request
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Pointer to command element with SRP task management request
+ *
+ * Parse the srp task management request; if it is valid then submit it to tcm.
+ * Note: The return code does not reflect the status of the task management
+ * request.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Processor level
+ */
+static void ibmvscsis_parse_task(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt;
+ int tcm_type;
+ u64 tag_to_abort = 0;
+ int rc = 0;
+ struct ibmvscsis_nexus *nexus;
+
+ nexus = vscsi->tport.ibmv_nexus;
+
+ cmd->rsp.sol_not = srp_tsk->sol_not;
+
+ switch (srp_tsk->tsk_mgmt_func) {
+ case SRP_TSK_ABORT_TASK:
+ tcm_type = TMR_ABORT_TASK;
+ tag_to_abort = be64_to_cpu(srp_tsk->task_tag);
+ break;
+ case SRP_TSK_ABORT_TASK_SET:
+ tcm_type = TMR_ABORT_TASK_SET;
+ break;
+ case SRP_TSK_CLEAR_TASK_SET:
+ tcm_type = TMR_CLEAR_TASK_SET;
+ break;
+ case SRP_TSK_LUN_RESET:
+ tcm_type = TMR_LUN_RESET;
+ break;
+ case SRP_TSK_CLEAR_ACA:
+ tcm_type = TMR_CLEAR_ACA;
+ break;
+ default:
+ dev_err(&vscsi->dev, "unknown task mgmt func %d\n",
+ srp_tsk->tsk_mgmt_func);
+ cmd->se_cmd.se_tmr_req->response =
+ TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
+ rc = -1;
+ break;
+ }
+
+ if (!rc) {
+ cmd->se_cmd.tag = be64_to_cpu(srp_tsk->tag);
+
+ spin_lock_bh(&vscsi->intr_lock);
+ list_add_tail(&cmd->list, &vscsi->active_q);
+ spin_unlock_bh(&vscsi->intr_lock);
+
+ srp_tsk->lun.scsi_lun[0] &= 0x3f;
+
+ dev_dbg(&vscsi->dev, "calling submit_tmr, func %d\n",
+ srp_tsk->tsk_mgmt_func);
+ rc = target_submit_tmr(&cmd->se_cmd, nexus->se_sess, NULL,
+ scsilun_to_int(&srp_tsk->lun), srp_tsk,
+ tcm_type, GFP_KERNEL, tag_to_abort, 0);
+ if (rc) {
+ dev_err(&vscsi->dev, "target_submit_tmr failed, rc %d\n",
+ rc);
+ spin_lock_bh(&vscsi->intr_lock);
+ list_del(&cmd->list);
+ spin_unlock_bh(&vscsi->intr_lock);
+ cmd->se_cmd.se_tmr_req->response =
+ TMR_FUNCTION_REJECTED;
+ }
+ }
+
+ if (rc)
+ transport_send_check_condition_and_sense(&cmd->se_cmd, 0, 0);
+}
+
+static void ibmvscsis_scheduler(struct work_struct *work)
+{
+ struct ibmvscsis_cmd *cmd = container_of(work, struct ibmvscsis_cmd,
+ work);
+ struct scsi_info *vscsi = cmd->adapter;
+
+ spin_lock_bh(&vscsi->intr_lock);
+
+ /* Remove from schedule_q */
+ list_del(&cmd->list);
+
+ /* Don't submit cmd if we're disconnecting */
+ if (vscsi->flags & (SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED)) {
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+
+ /* ibmvscsis_disconnect might be waiting for us */
+ if (list_empty(&vscsi->active_q) &&
+ list_empty(&vscsi->schedule_q) &&
+ (vscsi->flags & WAIT_FOR_IDLE)) {
+ vscsi->flags &= ~WAIT_FOR_IDLE;
+ complete(&vscsi->wait_idle);
+ }
+
+ spin_unlock_bh(&vscsi->intr_lock);
+ return;
+ }
+
+ spin_unlock_bh(&vscsi->intr_lock);
+
+ switch (cmd->type) {
+ case SCSI_CDB:
+ ibmvscsis_parse_cmd(vscsi, cmd);
+ break;
+ case TASK_MANAGEMENT:
+ ibmvscsis_parse_task(vscsi, cmd);
+ break;
+ default:
+ dev_err(&vscsi->dev, "scheduler, invalid cmd type %d\n",
+ cmd->type);
+ spin_lock_bh(&vscsi->intr_lock);
+ ibmvscsis_free_cmd_resources(vscsi, cmd);
+ spin_unlock_bh(&vscsi->intr_lock);
+ break;
+ }
+}
+
+static int ibmvscsis_alloc_cmds(struct scsi_info *vscsi, int num)
+{
+ struct ibmvscsis_cmd *cmd;
+ int i;
+
+ INIT_LIST_HEAD(&vscsi->free_cmd);
+ vscsi->cmd_pool = kcalloc(num, sizeof(struct ibmvscsis_cmd),
+ GFP_KERNEL);
+ if (!vscsi->cmd_pool)
+ return -ENOMEM;
+
+ for (i = 0, cmd = (struct ibmvscsis_cmd *)vscsi->cmd_pool; i < num;
+ i++, cmd++) {
+ cmd->abort_cmd = NULL;
+ cmd->adapter = vscsi;
+ INIT_WORK(&cmd->work, ibmvscsis_scheduler);
+ list_add_tail(&cmd->list, &vscsi->free_cmd);
+ }
+
+ return 0;
+}
+
+static void ibmvscsis_free_cmds(struct scsi_info *vscsi)
+{
+ kfree(vscsi->cmd_pool);
+ vscsi->cmd_pool = NULL;
+ INIT_LIST_HEAD(&vscsi->free_cmd);
+}
+
+/**
+ * ibmvscsis_service_wait_q() - Service Waiting Queue
+ * @timer: Pointer to timer which has expired
+ *
+ * This routine is called when the timer pops to service the waiting
+ * queue. Elements on the queue have completed, their responses have been
+ * copied to the client, but the client's response queue was full so
+ * the queue message could not be sent. The routine grabs the proper locks
+ * and calls send messages.
+ *
+ * EXECUTION ENVIRONMENT:
+ * called at interrupt level
+ */
+static enum hrtimer_restart ibmvscsis_service_wait_q(struct hrtimer *timer)
+{
+ struct timer_cb *p_timer = container_of(timer, struct timer_cb, timer);
+ struct scsi_info *vscsi = container_of(p_timer, struct scsi_info,
+ rsp_q_timer);
+
+ spin_lock_bh(&vscsi->intr_lock);
+ p_timer->timer_pops += 1;
+ p_timer->started = false;
+ ibmvscsis_send_messages(vscsi);
+ spin_unlock_bh(&vscsi->intr_lock);
+
+ return HRTIMER_NORESTART;
+}
+
+static long ibmvscsis_alloctimer(struct scsi_info *vscsi)
+{
+ struct timer_cb *p_timer;
+
+ p_timer = &vscsi->rsp_q_timer;
+ hrtimer_setup(&p_timer->timer, ibmvscsis_service_wait_q, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ p_timer->started = false;
+ p_timer->timer_pops = 0;
+
+ return ADAPT_SUCCESS;
+}
+
+static void ibmvscsis_freetimer(struct scsi_info *vscsi)
+{
+ struct timer_cb *p_timer;
+
+ p_timer = &vscsi->rsp_q_timer;
+
+ (void)hrtimer_cancel(&p_timer->timer);
+
+ p_timer->started = false;
+ p_timer->timer_pops = 0;
+}
+
+static irqreturn_t ibmvscsis_interrupt(int dummy, void *data)
+{
+ struct scsi_info *vscsi = data;
+
+ vio_disable_interrupts(vscsi->dma_dev);
+ tasklet_schedule(&vscsi->work_task);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ibmvscsis_enable_change_state() - Set new state based on enabled status
+ * @vscsi: Pointer to our adapter structure
+ *
+ * This function determines our new state now that we are enabled. This
+ * may involve sending an Init Complete message to the client.
+ *
+ * Must be called with interrupt lock held.
+ */
+static long ibmvscsis_enable_change_state(struct scsi_info *vscsi)
+{
+ int bytes;
+ long rc = ADAPT_SUCCESS;
+
+ bytes = vscsi->cmd_q.size * PAGE_SIZE;
+ rc = h_reg_crq(vscsi->dds.unit_id, vscsi->cmd_q.crq_token, bytes);
+ if (rc == H_CLOSED || rc == H_SUCCESS) {
+ vscsi->state = WAIT_CONNECTION;
+ rc = ibmvscsis_establish_new_q(vscsi);
+ }
+
+ if (rc != ADAPT_SUCCESS) {
+ vscsi->state = ERR_DISCONNECTED;
+ vscsi->flags |= RESPONSE_Q_DOWN;
+ }
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_create_command_q() - Create Command Queue
+ * @vscsi: Pointer to our adapter structure
+ * @num_cmds: Currently unused. In the future, may be used to determine
+ * the size of the CRQ.
+ *
+ * Allocates memory for command queue maps remote memory into an ioba
+ * initializes the command response queue
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process level only
+ */
+static long ibmvscsis_create_command_q(struct scsi_info *vscsi, int num_cmds)
+{
+ int pages;
+ struct vio_dev *vdev = vscsi->dma_dev;
+
+ /* We might support multiple pages in the future, but just 1 for now */
+ pages = 1;
+
+ vscsi->cmd_q.size = pages;
+
+ vscsi->cmd_q.base_addr =
+ (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
+ if (!vscsi->cmd_q.base_addr)
+ return -ENOMEM;
+
+ vscsi->cmd_q.mask = ((uint)pages * CRQ_PER_PAGE) - 1;
+
+ vscsi->cmd_q.crq_token = dma_map_single(&vdev->dev,
+ vscsi->cmd_q.base_addr,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&vdev->dev, vscsi->cmd_q.crq_token)) {
+ free_page((unsigned long)vscsi->cmd_q.base_addr);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * ibmvscsis_destroy_command_q - Destroy Command Queue
+ * @vscsi: Pointer to our adapter structure
+ *
+ * Releases memory for command queue and unmaps mapped remote memory.
+ *
+ * EXECUTION ENVIRONMENT:
+ * Process level only
+ */
+static void ibmvscsis_destroy_command_q(struct scsi_info *vscsi)
+{
+ dma_unmap_single(&vscsi->dma_dev->dev, vscsi->cmd_q.crq_token,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)vscsi->cmd_q.base_addr);
+ vscsi->cmd_q.base_addr = NULL;
+ vscsi->state = NO_QUEUE;
+}
+
+static u8 ibmvscsis_fast_fail(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf;
+ struct scsi_sense_hdr sshdr;
+ u8 rc = se_cmd->scsi_status;
+
+ if (vscsi->fast_fail && (READ_CMD(srp->cdb) || WRITE_CMD(srp->cdb)))
+ if (scsi_normalize_sense(se_cmd->sense_buffer,
+ se_cmd->scsi_sense_length, &sshdr))
+ if (sshdr.sense_key == HARDWARE_ERROR &&
+ (se_cmd->residual_count == 0 ||
+ se_cmd->residual_count == se_cmd->data_length)) {
+ rc = NO_SENSE;
+ cmd->flags |= CMD_FAST_FAIL;
+ }
+
+ return rc;
+}
+
+/**
+ * srp_build_response() - Build an SRP response buffer
+ * @vscsi: Pointer to our adapter structure
+ * @cmd: Pointer to command for which to send the response
+ * @len_p: Where to return the length of the IU response sent. This
+ * is needed to construct the CRQ response.
+ *
+ * Build the SRP response buffer and copy it to the client's memory space.
+ */
+static long srp_build_response(struct scsi_info *vscsi,
+ struct ibmvscsis_cmd *cmd, uint *len_p)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct srp_rsp *rsp;
+ uint len;
+ u32 rsp_code;
+ char *data;
+ u32 *tsk_status;
+ long rc = ADAPT_SUCCESS;
+
+ spin_lock_bh(&vscsi->intr_lock);
+
+ rsp = &vio_iu(iue)->srp.rsp;
+ len = sizeof(*rsp);
+ memset(rsp, 0, len);
+ data = rsp->data;
+
+ rsp->opcode = SRP_RSP;
+
+ rsp->req_lim_delta = cpu_to_be32(1 + vscsi->credit);
+ rsp->tag = cmd->rsp.tag;
+ rsp->flags = 0;
+
+ if (cmd->type == SCSI_CDB) {
+ rsp->status = ibmvscsis_fast_fail(vscsi, cmd);
+ if (rsp->status) {
+ dev_dbg(&vscsi->dev, "build_resp: cmd %p, scsi status %d\n",
+ cmd, (int)rsp->status);
+ ibmvscsis_determine_resid(se_cmd, rsp);
+ if (se_cmd->scsi_sense_length && se_cmd->sense_buffer) {
+ rsp->sense_data_len =
+ cpu_to_be32(se_cmd->scsi_sense_length);
+ rsp->flags |= SRP_RSP_FLAG_SNSVALID;
+ len += se_cmd->scsi_sense_length;
+ memcpy(data, se_cmd->sense_buffer,
+ se_cmd->scsi_sense_length);
+ }
+ rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
+ UCSOLNT_RESP_SHIFT;
+ } else if (cmd->flags & CMD_FAST_FAIL) {
+ dev_dbg(&vscsi->dev, "build_resp: cmd %p, fast fail\n",
+ cmd);
+ rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
+ UCSOLNT_RESP_SHIFT;
+ } else {
+ rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >>
+ SCSOLNT_RESP_SHIFT;
+ }
+ } else {
+ /* this is task management */
+ rsp->status = 0;
+ rsp->resp_data_len = cpu_to_be32(4);
+ rsp->flags |= SRP_RSP_FLAG_RSPVALID;
+
+ switch (se_cmd->se_tmr_req->response) {
+ case TMR_FUNCTION_COMPLETE:
+ case TMR_TASK_DOES_NOT_EXIST:
+ rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_COMPLETE;
+ rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >>
+ SCSOLNT_RESP_SHIFT;
+ break;
+ case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
+ case TMR_LUN_DOES_NOT_EXIST:
+ rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_NOT_SUPPORTED;
+ rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
+ UCSOLNT_RESP_SHIFT;
+ break;
+ case TMR_FUNCTION_FAILED:
+ case TMR_FUNCTION_REJECTED:
+ default:
+ rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_FAILED;
+ rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
+ UCSOLNT_RESP_SHIFT;
+ break;
+ }
+
+ tsk_status = (u32 *)data;
+ *tsk_status = cpu_to_be32(rsp_code);
+ data = (char *)(tsk_status + 1);
+ len += 4;
+ }
+
+ dma_wmb();
+ rc = h_copy_rdma(len, vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma,
+ vscsi->dds.window[REMOTE].liobn,
+ be64_to_cpu(iue->remote_token));
+
+ switch (rc) {
+ case H_SUCCESS:
+ vscsi->credit = 0;
+ *len_p = len;
+ break;
+ case H_PERMISSION:
+ if (connection_broken(vscsi))
+ vscsi->flags |= RESPONSE_Q_DOWN | CLIENT_FAILED;
+
+ dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld, flags 0x%x, state 0x%hx\n",
+ rc, vscsi->flags, vscsi->state);
+ break;
+ case H_SOURCE_PARM:
+ case H_DEST_PARM:
+ default:
+ dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld\n",
+ rc);
+ break;
+ }
+
+ spin_unlock_bh(&vscsi->intr_lock);
+
+ return rc;
+}
+
+static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg,
+ int nsg, struct srp_direct_buf *md, int nmd,
+ enum dma_data_direction dir, unsigned int bytes)
+{
+ struct iu_entry *iue = cmd->iue;
+ struct srp_target *target = iue->target;
+ struct scsi_info *vscsi = target->ldata;
+ struct scatterlist *sgp;
+ dma_addr_t client_ioba, server_ioba;
+ ulong buf_len;
+ ulong client_len, server_len;
+ int md_idx;
+ long tx_len;
+ long rc = 0;
+
+ if (bytes == 0)
+ return 0;
+
+ sgp = sg;
+ client_len = 0;
+ server_len = 0;
+ md_idx = 0;
+ tx_len = bytes;
+
+ do {
+ if (client_len == 0) {
+ if (md_idx >= nmd) {
+ dev_err(&vscsi->dev, "rdma: ran out of client memory descriptors\n");
+ rc = -EIO;
+ break;
+ }
+ client_ioba = be64_to_cpu(md[md_idx].va);
+ client_len = be32_to_cpu(md[md_idx].len);
+ }
+ if (server_len == 0) {
+ if (!sgp) {
+ dev_err(&vscsi->dev, "rdma: ran out of scatter/gather list\n");
+ rc = -EIO;
+ break;
+ }
+ server_ioba = sg_dma_address(sgp);
+ server_len = sg_dma_len(sgp);
+ }
+
+ buf_len = tx_len;
+
+ if (buf_len > client_len)
+ buf_len = client_len;
+
+ if (buf_len > server_len)
+ buf_len = server_len;
+
+ if (buf_len > max_vdma_size)
+ buf_len = max_vdma_size;
+
+ if (dir == DMA_TO_DEVICE) {
+ /* read from client */
+ rc = h_copy_rdma(buf_len,
+ vscsi->dds.window[REMOTE].liobn,
+ client_ioba,
+ vscsi->dds.window[LOCAL].liobn,
+ server_ioba);
+ } else {
+ /* The h_copy_rdma will cause phyp, running in another
+ * partition, to read memory, so we need to make sure
+ * the data has been written out, hence these syncs.
+ */
+ /* ensure that everything is in memory */
+ isync();
+ /* ensure that memory has been made visible */
+ dma_wmb();
+ rc = h_copy_rdma(buf_len,
+ vscsi->dds.window[LOCAL].liobn,
+ server_ioba,
+ vscsi->dds.window[REMOTE].liobn,
+ client_ioba);
+ }
+ switch (rc) {
+ case H_SUCCESS:
+ break;
+ case H_PERMISSION:
+ case H_SOURCE_PARM:
+ case H_DEST_PARM:
+ if (connection_broken(vscsi)) {
+ spin_lock_bh(&vscsi->intr_lock);
+ vscsi->flags |=
+ (RESPONSE_Q_DOWN | CLIENT_FAILED);
+ spin_unlock_bh(&vscsi->intr_lock);
+ }
+ dev_err(&vscsi->dev, "rdma: h_copy_rdma failed, rc %ld\n",
+ rc);
+ break;
+
+ default:
+ dev_err(&vscsi->dev, "rdma: unknown error %ld from h_copy_rdma\n",
+ rc);
+ break;
+ }
+
+ if (!rc) {
+ tx_len -= buf_len;
+ if (tx_len) {
+ client_len -= buf_len;
+ if (client_len == 0)
+ md_idx++;
+ else
+ client_ioba += buf_len;
+
+ server_len -= buf_len;
+ if (server_len == 0)
+ sgp = sg_next(sgp);
+ else
+ server_ioba += buf_len;
+ } else {
+ break;
+ }
+ }
+ } while (!rc);
+
+ return rc;
+}
+
+/**
+ * ibmvscsis_handle_crq() - Handle CRQ
+ * @data: Pointer to our adapter structure
+ *
+ * Read the command elements from the command queue and copy the payloads
+ * associated with the command elements to local memory and execute the
+ * SRP requests.
+ *
+ * Note: this is an edge triggered interrupt. It can not be shared.
+ */
+static void ibmvscsis_handle_crq(unsigned long data)
+{
+ struct scsi_info *vscsi = (struct scsi_info *)data;
+ struct viosrp_crq *crq;
+ long rc;
+ bool ack = true;
+ volatile u8 valid;
+
+ spin_lock_bh(&vscsi->intr_lock);
+
+ dev_dbg(&vscsi->dev, "got interrupt\n");
+
+ /*
+ * if we are in a path where we are waiting for all pending commands
+ * to complete because we received a transport event and anything in
+ * the command queue is for a new connection, do nothing
+ */
+ if (TARGET_STOP(vscsi)) {
+ vio_enable_interrupts(vscsi->dma_dev);
+
+ dev_dbg(&vscsi->dev, "handle_crq, don't process: flags 0x%x, state 0x%hx\n",
+ vscsi->flags, vscsi->state);
+ spin_unlock_bh(&vscsi->intr_lock);
+ return;
+ }
+
+ rc = vscsi->flags & SCHEDULE_DISCONNECT;
+ crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
+ valid = crq->valid;
+ dma_rmb();
+
+ while (valid) {
+ /*
+ * These are edege triggered interrupts. After dropping out of
+ * the while loop, the code must check for work since an
+ * interrupt could be lost, and an elment be left on the queue,
+ * hence the label.
+ */
+cmd_work:
+ vscsi->cmd_q.index =
+ (vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask;
+
+ if (!rc) {
+ rc = ibmvscsis_parse_command(vscsi, crq);
+ } else {
+ if ((uint)crq->valid == VALID_TRANS_EVENT) {
+ /*
+ * must service the transport layer events even
+ * in an error state, dont break out until all
+ * the consecutive transport events have been
+ * processed
+ */
+ rc = ibmvscsis_trans_event(vscsi, crq);
+ } else if (vscsi->flags & TRANS_EVENT) {
+ /*
+ * if a transport event has occurred leave
+ * everything but transport events on the queue
+ *
+ * need to decrement the queue index so we can
+ * look at the element again
+ */
+ if (vscsi->cmd_q.index)
+ vscsi->cmd_q.index -= 1;
+ else
+ /*
+ * index is at 0 it just wrapped.
+ * have it index last element in q
+ */
+ vscsi->cmd_q.index = vscsi->cmd_q.mask;
+ break;
+ }
+ }
+
+ crq->valid = INVALIDATE_CMD_RESP_EL;
+
+ crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
+ valid = crq->valid;
+ dma_rmb();
+ }
+
+ if (!rc) {
+ if (ack) {
+ vio_enable_interrupts(vscsi->dma_dev);
+ ack = false;
+ dev_dbg(&vscsi->dev, "handle_crq, reenabling interrupts\n");
+ }
+ valid = crq->valid;
+ dma_rmb();
+ if (valid)
+ goto cmd_work;
+ } else {
+ dev_dbg(&vscsi->dev, "handle_crq, error: flags 0x%x, state 0x%hx, crq index 0x%x\n",
+ vscsi->flags, vscsi->state, vscsi->cmd_q.index);
+ }
+
+ dev_dbg(&vscsi->dev, "Leaving handle_crq: schedule_q empty %d, flags 0x%x, state 0x%hx\n",
+ (int)list_empty(&vscsi->schedule_q), vscsi->flags,
+ vscsi->state);
+
+ spin_unlock_bh(&vscsi->intr_lock);
+}
+
+static int ibmvscsis_probe(struct vio_dev *vdev,
+ const struct vio_device_id *id)
+{
+ struct scsi_info *vscsi;
+ int rc = 0;
+ long hrc = 0;
+
+ vscsi = kzalloc(sizeof(*vscsi), GFP_KERNEL);
+ if (!vscsi) {
+ rc = -ENOMEM;
+ dev_err(&vdev->dev, "probe: allocation of adapter failed\n");
+ return rc;
+ }
+
+ vscsi->dma_dev = vdev;
+ vscsi->dev = vdev->dev;
+ INIT_LIST_HEAD(&vscsi->schedule_q);
+ INIT_LIST_HEAD(&vscsi->waiting_rsp);
+ INIT_LIST_HEAD(&vscsi->active_q);
+
+ snprintf(vscsi->tport.tport_name, IBMVSCSIS_NAMELEN, "%s",
+ dev_name(&vdev->dev));
+
+ dev_dbg(&vscsi->dev, "probe tport_name: %s\n", vscsi->tport.tport_name);
+
+ rc = read_dma_window(vscsi);
+ if (rc)
+ goto free_adapter;
+ dev_dbg(&vscsi->dev, "Probe: liobn 0x%x, riobn 0x%x\n",
+ vscsi->dds.window[LOCAL].liobn,
+ vscsi->dds.window[REMOTE].liobn);
+
+ snprintf(vscsi->eye, sizeof(vscsi->eye), "VSCSI %s", vdev->name);
+
+ vscsi->dds.unit_id = vdev->unit_address;
+ strscpy(vscsi->dds.partition_name, partition_name,
+ sizeof(vscsi->dds.partition_name));
+ vscsi->dds.partition_num = partition_number;
+
+ spin_lock_bh(&ibmvscsis_dev_lock);
+ list_add_tail(&vscsi->list, &ibmvscsis_dev_list);
+ spin_unlock_bh(&ibmvscsis_dev_lock);
+
+ /*
+ * TBD: How do we determine # of cmds to request? Do we know how
+ * many "children" we have?
+ */
+ vscsi->request_limit = INITIAL_SRP_LIMIT;
+ rc = srp_target_alloc(&vscsi->target, &vdev->dev, vscsi->request_limit,
+ SRP_MAX_IU_LEN);
+ if (rc)
+ goto rem_list;
+
+ vscsi->target.ldata = vscsi;
+
+ rc = ibmvscsis_alloc_cmds(vscsi, vscsi->request_limit);
+ if (rc) {
+ dev_err(&vscsi->dev, "alloc_cmds failed, rc %d, num %d\n",
+ rc, vscsi->request_limit);
+ goto free_target;
+ }
+
+ /*
+ * Note: the lock is used in freeing timers, so must initialize
+ * first so that ordering in case of error is correct.
+ */
+ spin_lock_init(&vscsi->intr_lock);
+
+ rc = ibmvscsis_alloctimer(vscsi);
+ if (rc) {
+ dev_err(&vscsi->dev, "probe: alloctimer failed, rc %d\n", rc);
+ goto free_cmds;
+ }
+
+ rc = ibmvscsis_create_command_q(vscsi, 256);
+ if (rc) {
+ dev_err(&vscsi->dev, "probe: create_command_q failed, rc %d\n",
+ rc);
+ goto free_timer;
+ }
+
+ vscsi->map_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!vscsi->map_buf) {
+ rc = -ENOMEM;
+ dev_err(&vscsi->dev, "probe: allocating cmd buffer failed\n");
+ goto destroy_queue;
+ }
+
+ vscsi->map_ioba = dma_map_single(&vdev->dev, vscsi->map_buf, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&vdev->dev, vscsi->map_ioba)) {
+ rc = -ENOMEM;
+ dev_err(&vscsi->dev, "probe: error mapping command buffer\n");
+ goto free_buf;
+ }
+
+ hrc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO,
+ (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0,
+ 0);
+ if (hrc == H_SUCCESS)
+ vscsi->client_data.partition_number =
+ be64_to_cpu(*(u64 *)vscsi->map_buf);
+ /*
+ * We expect the VIOCTL to fail if we're configured as "any
+ * client can connect" and the client isn't activated yet.
+ * We'll make the call again when he sends an init msg.
+ */
+ dev_dbg(&vscsi->dev, "probe hrc %ld, client partition num %d\n",
+ hrc, vscsi->client_data.partition_number);
+
+ tasklet_init(&vscsi->work_task, ibmvscsis_handle_crq,
+ (unsigned long)vscsi);
+
+ init_completion(&vscsi->wait_idle);
+ init_completion(&vscsi->unconfig);
+
+ vscsi->work_q = alloc_workqueue("ibmvscsis%s",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 1,
+ dev_name(&vdev->dev));
+ if (!vscsi->work_q) {
+ rc = -ENOMEM;
+ dev_err(&vscsi->dev, "create_workqueue failed\n");
+ goto unmap_buf;
+ }
+
+ rc = request_irq(vdev->irq, ibmvscsis_interrupt, 0, "ibmvscsis", vscsi);
+ if (rc) {
+ rc = -EPERM;
+ dev_err(&vscsi->dev, "probe: request_irq failed, rc %d\n", rc);
+ goto destroy_WQ;
+ }
+
+ vscsi->state = WAIT_ENABLED;
+
+ dev_set_drvdata(&vdev->dev, vscsi);
+
+ return 0;
+
+destroy_WQ:
+ destroy_workqueue(vscsi->work_q);
+unmap_buf:
+ dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+free_buf:
+ kfree(vscsi->map_buf);
+destroy_queue:
+ tasklet_kill(&vscsi->work_task);
+ ibmvscsis_unregister_command_q(vscsi);
+ ibmvscsis_destroy_command_q(vscsi);
+free_timer:
+ ibmvscsis_freetimer(vscsi);
+free_cmds:
+ ibmvscsis_free_cmds(vscsi);
+free_target:
+ srp_target_free(&vscsi->target);
+rem_list:
+ spin_lock_bh(&ibmvscsis_dev_lock);
+ list_del(&vscsi->list);
+ spin_unlock_bh(&ibmvscsis_dev_lock);
+free_adapter:
+ kfree(vscsi);
+
+ return rc;
+}
+
+static void ibmvscsis_remove(struct vio_dev *vdev)
+{
+ struct scsi_info *vscsi = dev_get_drvdata(&vdev->dev);
+
+ dev_dbg(&vscsi->dev, "remove (%s)\n", dev_name(&vscsi->dma_dev->dev));
+
+ spin_lock_bh(&vscsi->intr_lock);
+ ibmvscsis_post_disconnect(vscsi, UNCONFIGURING, 0);
+ vscsi->flags |= CFG_SLEEPING;
+ spin_unlock_bh(&vscsi->intr_lock);
+ wait_for_completion(&vscsi->unconfig);
+
+ vio_disable_interrupts(vdev);
+ free_irq(vdev->irq, vscsi);
+ destroy_workqueue(vscsi->work_q);
+ dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ kfree(vscsi->map_buf);
+ tasklet_kill(&vscsi->work_task);
+ ibmvscsis_destroy_command_q(vscsi);
+ ibmvscsis_freetimer(vscsi);
+ ibmvscsis_free_cmds(vscsi);
+ srp_target_free(&vscsi->target);
+ spin_lock_bh(&ibmvscsis_dev_lock);
+ list_del(&vscsi->list);
+ spin_unlock_bh(&ibmvscsis_dev_lock);
+ kfree(vscsi);
+}
+
+static ssize_t partition_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%x\n", partition_number);
+}
+
+static ssize_t unit_address_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_info *vscsi = container_of(dev, struct scsi_info, dev);
+
+ return sysfs_emit(buf, "%x\n", vscsi->dma_dev->unit_address);
+}
+
+static int ibmvscsis_get_system_info(void)
+{
+ struct device_node *rootdn, *vdevdn;
+ const char *id, *model, *name;
+ const uint *num;
+
+ rootdn = of_find_node_by_path("/");
+ if (!rootdn)
+ return -ENOENT;
+
+ model = of_get_property(rootdn, "model", NULL);
+ id = of_get_property(rootdn, "system-id", NULL);
+ if (model && id)
+ snprintf(system_id, sizeof(system_id), "%s-%s", model, id);
+
+ name = of_get_property(rootdn, "ibm,partition-name", NULL);
+ if (name)
+ strscpy(partition_name, name, sizeof(partition_name));
+
+ num = of_get_property(rootdn, "ibm,partition-no", NULL);
+ if (num)
+ partition_number = of_read_number(num, 1);
+
+ of_node_put(rootdn);
+
+ vdevdn = of_find_node_by_path("/vdevice");
+ if (vdevdn) {
+ const uint *mvds;
+
+ mvds = of_get_property(vdevdn, "ibm,max-virtual-dma-size",
+ NULL);
+ if (mvds)
+ max_vdma_size = *mvds;
+ of_node_put(vdevdn);
+ }
+
+ return 0;
+}
+
+static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct ibmvscsis_tport *tport =
+ container_of(se_tpg, struct ibmvscsis_tport, se_tpg);
+
+ return tport->tport_name;
+}
+
+static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg)
+{
+ struct ibmvscsis_tport *tport =
+ container_of(se_tpg, struct ibmvscsis_tport, se_tpg);
+
+ return tport->tport_tpgt;
+}
+
+static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int ibmvscsis_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int ibmvscsis_check_stop_free(struct se_cmd *se_cmd)
+{
+ return target_put_sess_cmd(se_cmd);
+}
+
+static void ibmvscsis_release_cmd(struct se_cmd *se_cmd)
+{
+ struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
+ se_cmd);
+ struct scsi_info *vscsi = cmd->adapter;
+
+ spin_lock_bh(&vscsi->intr_lock);
+ /* Remove from active_q */
+ list_move_tail(&cmd->list, &vscsi->waiting_rsp);
+ ibmvscsis_send_messages(vscsi);
+ spin_unlock_bh(&vscsi->intr_lock);
+}
+
+static int ibmvscsis_write_pending(struct se_cmd *se_cmd)
+{
+ struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
+ se_cmd);
+ struct scsi_info *vscsi = cmd->adapter;
+ struct iu_entry *iue = cmd->iue;
+ int rc;
+
+ /*
+ * If CLIENT_FAILED OR RESPONSE_Q_DOWN, then just return success
+ * since LIO can't do anything about it, and we dont want to
+ * attempt an srp_transfer_data.
+ */
+ if ((vscsi->flags & (CLIENT_FAILED | RESPONSE_Q_DOWN))) {
+ dev_err(&vscsi->dev, "write_pending failed since: %d\n",
+ vscsi->flags);
+ return -EIO;
+
+ }
+
+ rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma,
+ 1, 1);
+ if (rc) {
+ dev_err(&vscsi->dev, "srp_transfer_data() failed: %d\n", rc);
+ return -EIO;
+ }
+ /*
+ * We now tell TCM to add this WRITE CDB directly into the TCM storage
+ * object execution queue.
+ */
+ target_execute_cmd(se_cmd);
+ return 0;
+}
+
+static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
+ se_cmd);
+ struct iu_entry *iue = cmd->iue;
+ struct scsi_info *vscsi = cmd->adapter;
+ uint len = 0;
+ int rc;
+
+ rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1,
+ 1);
+ if (rc) {
+ dev_err(&vscsi->dev, "srp_transfer_data failed: %d\n", rc);
+ se_cmd->scsi_sense_length = 18;
+ memset(se_cmd->sense_buffer, 0, se_cmd->scsi_sense_length);
+ /* Logical Unit Communication Time-out asc/ascq = 0x0801 */
+ scsi_build_sense_buffer(0, se_cmd->sense_buffer, MEDIUM_ERROR,
+ 0x08, 0x01);
+ }
+
+ srp_build_response(vscsi, cmd, &len);
+ cmd->rsp.format = SRP_FORMAT;
+ cmd->rsp.len = len;
+
+ return 0;
+}
+
+static int ibmvscsis_queue_status(struct se_cmd *se_cmd)
+{
+ struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
+ se_cmd);
+ struct scsi_info *vscsi = cmd->adapter;
+ uint len;
+
+ dev_dbg(&vscsi->dev, "queue_status %p\n", se_cmd);
+
+ srp_build_response(vscsi, cmd, &len);
+ cmd->rsp.format = SRP_FORMAT;
+ cmd->rsp.len = len;
+
+ return 0;
+}
+
+static void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
+ se_cmd);
+ struct scsi_info *vscsi = cmd->adapter;
+ struct ibmvscsis_cmd *cmd_itr;
+ struct iu_entry *iue = iue = cmd->iue;
+ struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt;
+ u64 tag_to_abort = be64_to_cpu(srp_tsk->task_tag);
+ uint len;
+
+ dev_dbg(&vscsi->dev, "queue_tm_rsp %p, status %d\n",
+ se_cmd, (int)se_cmd->se_tmr_req->response);
+
+ if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK &&
+ cmd->se_cmd.se_tmr_req->response == TMR_TASK_DOES_NOT_EXIST) {
+ spin_lock_bh(&vscsi->intr_lock);
+ list_for_each_entry(cmd_itr, &vscsi->active_q, list) {
+ if (tag_to_abort == cmd_itr->se_cmd.tag) {
+ cmd_itr->abort_cmd = cmd;
+ cmd->flags |= DELAY_SEND;
+ break;
+ }
+ }
+ spin_unlock_bh(&vscsi->intr_lock);
+ }
+
+ srp_build_response(vscsi, cmd, &len);
+ cmd->rsp.format = SRP_FORMAT;
+ cmd->rsp.len = len;
+}
+
+static void ibmvscsis_aborted_task(struct se_cmd *se_cmd)
+{
+ struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
+ se_cmd);
+ struct scsi_info *vscsi = cmd->adapter;
+
+ dev_dbg(&vscsi->dev, "ibmvscsis_aborted_task %p task_tag: %llu\n",
+ se_cmd, se_cmd->tag);
+}
+
+static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct ibmvscsis_tport *tport;
+ struct scsi_info *vscsi;
+
+ tport = ibmvscsis_lookup_port(name);
+ if (tport) {
+ vscsi = container_of(tport, struct scsi_info, tport);
+ tport->tport_proto_id = SCSI_PROTOCOL_SRP;
+ dev_dbg(&vscsi->dev, "make_tport(%s), pointer:%p, tport_id:%x\n",
+ name, tport, tport->tport_proto_id);
+ return &tport->tport_wwn;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+static void ibmvscsis_drop_tport(struct se_wwn *wwn)
+{
+ struct ibmvscsis_tport *tport = container_of(wwn,
+ struct ibmvscsis_tport,
+ tport_wwn);
+ struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport);
+
+ dev_dbg(&vscsi->dev, "drop_tport(%s)\n",
+ config_item_name(&tport->tport_wwn.wwn_group.cg_item));
+}
+
+static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn,
+ const char *name)
+{
+ struct ibmvscsis_tport *tport =
+ container_of(wwn, struct ibmvscsis_tport, tport_wwn);
+ u16 tpgt;
+ int rc;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ rc = kstrtou16(name + 5, 0, &tpgt);
+ if (rc)
+ return ERR_PTR(rc);
+ tport->tport_tpgt = tpgt;
+
+ tport->releasing = false;
+
+ rc = core_tpg_register(&tport->tport_wwn, &tport->se_tpg,
+ tport->tport_proto_id);
+ if (rc)
+ return ERR_PTR(rc);
+
+ return &tport->se_tpg;
+}
+
+static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct ibmvscsis_tport *tport = container_of(se_tpg,
+ struct ibmvscsis_tport,
+ se_tpg);
+
+ tport->releasing = true;
+ tport->enabled = false;
+
+ /*
+ * Release the virtual I_T Nexus for this ibmvscsis TPG
+ */
+ ibmvscsis_drop_nexus(tport);
+ /*
+ * Deregister the se_tpg from TCM..
+ */
+ core_tpg_deregister(se_tpg);
+}
+
+static ssize_t ibmvscsis_wwn_version_show(struct config_item *item,
+ char *page)
+{
+ return scnprintf(page, PAGE_SIZE, "%s\n", IBMVSCSIS_VERSION);
+}
+CONFIGFS_ATTR_RO(ibmvscsis_wwn_, version);
+
+static struct configfs_attribute *ibmvscsis_wwn_attrs[] = {
+ &ibmvscsis_wwn_attr_version,
+ NULL,
+};
+
+
+static int ibmvscsis_enable_tpg(struct se_portal_group *se_tpg, bool enable)
+{
+ struct ibmvscsis_tport *tport = container_of(se_tpg,
+ struct ibmvscsis_tport,
+ se_tpg);
+ struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport);
+ long lrc;
+
+ if (enable) {
+ spin_lock_bh(&vscsi->intr_lock);
+ tport->enabled = true;
+ lrc = ibmvscsis_enable_change_state(vscsi);
+ if (lrc)
+ dev_err(&vscsi->dev, "enable_change_state failed, rc %ld state %d\n",
+ lrc, vscsi->state);
+ spin_unlock_bh(&vscsi->intr_lock);
+ } else {
+ spin_lock_bh(&vscsi->intr_lock);
+ tport->enabled = false;
+ /* This simulates the server going down */
+ ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
+ spin_unlock_bh(&vscsi->intr_lock);
+ }
+
+ return 0;
+}
+
+static const struct target_core_fabric_ops ibmvscsis_ops = {
+ .module = THIS_MODULE,
+ .fabric_name = "ibmvscsis",
+ .max_data_sg_nents = MAX_TXU / PAGE_SIZE,
+ .tpg_get_wwn = ibmvscsis_get_fabric_wwn,
+ .tpg_get_tag = ibmvscsis_get_tag,
+ .tpg_get_default_depth = ibmvscsis_get_default_depth,
+ .tpg_check_demo_mode = ibmvscsis_check_true,
+ .tpg_check_demo_mode_cache = ibmvscsis_check_true,
+ .check_stop_free = ibmvscsis_check_stop_free,
+ .release_cmd = ibmvscsis_release_cmd,
+ .write_pending = ibmvscsis_write_pending,
+ .queue_data_in = ibmvscsis_queue_data_in,
+ .queue_status = ibmvscsis_queue_status,
+ .queue_tm_rsp = ibmvscsis_queue_tm_rsp,
+ .aborted_task = ibmvscsis_aborted_task,
+ /*
+ * Setup function pointers for logic in target_core_fabric_configfs.c
+ */
+ .fabric_make_wwn = ibmvscsis_make_tport,
+ .fabric_drop_wwn = ibmvscsis_drop_tport,
+ .fabric_make_tpg = ibmvscsis_make_tpg,
+ .fabric_enable_tpg = ibmvscsis_enable_tpg,
+ .fabric_drop_tpg = ibmvscsis_drop_tpg,
+
+ .tfc_wwn_attrs = ibmvscsis_wwn_attrs,
+
+ .default_submit_type = TARGET_DIRECT_SUBMIT,
+ .direct_submit_supp = 1,
+};
+
+static void ibmvscsis_dev_release(struct device *dev) {};
+
+static DEVICE_STRING_ATTR_RO(system_id, S_IRUGO, system_id);
+
+static struct device_attribute dev_attr_partition_number =
+ __ATTR(partition_number, S_IRUGO, partition_number_show, NULL);
+
+static struct device_attribute dev_attr_unit_address =
+ __ATTR(unit_address, S_IRUGO, unit_address_show, NULL);
+
+static struct attribute *ibmvscsis_dev_attrs[] = {
+ &dev_attr_system_id.attr.attr,
+ &dev_attr_partition_number.attr,
+ &dev_attr_unit_address.attr,
+};
+ATTRIBUTE_GROUPS(ibmvscsis_dev);
+
+static struct class ibmvscsis_class = {
+ .name = "ibmvscsis",
+ .dev_release = ibmvscsis_dev_release,
+ .dev_groups = ibmvscsis_dev_groups,
+};
+
+static const struct vio_device_id ibmvscsis_device_table[] = {
+ { "v-scsi-host", "IBM,v-scsi-host" },
+ { "", "" }
+};
+MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table);
+
+static struct vio_driver ibmvscsis_driver = {
+ .name = "ibmvscsis",
+ .id_table = ibmvscsis_device_table,
+ .probe = ibmvscsis_probe,
+ .remove = ibmvscsis_remove,
+};
+
+/*
+ * ibmvscsis_init() - Kernel Module initialization
+ *
+ * Note: vio_register_driver() registers callback functions, and at least one
+ * of those callback functions calls TCM - Linux IO Target Subsystem, thus
+ * the SCSI Target template must be registered before vio_register_driver()
+ * is called.
+ */
+static int __init ibmvscsis_init(void)
+{
+ int rc = 0;
+
+ rc = ibmvscsis_get_system_info();
+ if (rc) {
+ pr_err("rc %d from get_system_info\n", rc);
+ goto out;
+ }
+
+ rc = class_register(&ibmvscsis_class);
+ if (rc) {
+ pr_err("failed class register\n");
+ goto out;
+ }
+
+ rc = target_register_template(&ibmvscsis_ops);
+ if (rc) {
+ pr_err("rc %d from target_register_template\n", rc);
+ goto unregister_class;
+ }
+
+ rc = vio_register_driver(&ibmvscsis_driver);
+ if (rc) {
+ pr_err("rc %d from vio_register_driver\n", rc);
+ goto unregister_target;
+ }
+
+ return 0;
+
+unregister_target:
+ target_unregister_template(&ibmvscsis_ops);
+unregister_class:
+ class_unregister(&ibmvscsis_class);
+out:
+ return rc;
+}
+
+static void __exit ibmvscsis_exit(void)
+{
+ pr_info("Unregister IBM virtual SCSI host driver\n");
+ vio_unregister_driver(&ibmvscsis_driver);
+ target_unregister_template(&ibmvscsis_ops);
+ class_unregister(&ibmvscsis_class);
+}
+
+MODULE_DESCRIPTION("IBMVSCSIS fabric driver");
+MODULE_AUTHOR("Bryant G. Ly and Michael Cyr");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(IBMVSCSIS_VERSION);
+module_init(ibmvscsis_init);
+module_exit(ibmvscsis_exit);
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
new file mode 100644
index 000000000000..7ae074e5d7a1
--- /dev/null
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
@@ -0,0 +1,362 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*******************************************************************************
+ * IBM Virtual SCSI Target Driver
+ * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp.
+ * Santiago Leon (santil@us.ibm.com) IBM Corp.
+ * Linda Xie (lxie@us.ibm.com) IBM Corp.
+ *
+ * Copyright (C) 2005-2011 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2010 Nicholas A. Bellinger <nab@kernel.org>
+ * Copyright (C) 2016 Bryant G. Ly <bryantly@linux.vnet.ibm.com> IBM Corp.
+ *
+ * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+ * Authors: Michael Cyr <mikecyr@linux.vnet.ibm.com>
+ *
+ ****************************************************************************/
+
+#ifndef __H_IBMVSCSI_TGT
+#define __H_IBMVSCSI_TGT
+
+#include <linux/interrupt.h>
+#include "libsrp.h"
+
+#define SYS_ID_NAME_LEN 64
+#define PARTITION_NAMELEN 96
+#define IBMVSCSIS_NAMELEN 32
+
+#define MSG_HI 0
+#define MSG_LOW 1
+
+#define MAX_CMD_Q_PAGES 4
+#define CRQ_PER_PAGE (PAGE_SIZE / sizeof(struct viosrp_crq))
+/* in terms of number of elements */
+#define DEFAULT_CMD_Q_SIZE CRQ_PER_PAGE
+#define MAX_CMD_Q_SIZE (DEFAULT_CMD_Q_SIZE * MAX_CMD_Q_PAGES)
+
+#define SRP_VIOLATION 0x102 /* general error code */
+
+/*
+ * SRP buffer formats defined as of 16.a supported by this driver.
+ */
+#define SUPPORTED_FORMATS ((SRP_DATA_DESC_DIRECT << 1) | \
+ (SRP_DATA_DESC_INDIRECT << 1))
+
+#define SCSI_LUN_ADDR_METHOD_FLAT 1
+
+struct dma_window {
+ u32 liobn; /* Unique per vdevice */
+ u64 tce_base; /* Physical location of the TCE table */
+ u64 tce_size; /* Size of the TCE table in bytes */
+};
+
+struct target_dds {
+ u64 unit_id; /* 64 bit will force alignment */
+#define NUM_DMA_WINDOWS 2
+#define LOCAL 0
+#define REMOTE 1
+ struct dma_window window[NUM_DMA_WINDOWS];
+
+ /* root node property "ibm,partition-no" */
+ uint partition_num;
+ char partition_name[PARTITION_NAMELEN];
+};
+
+#define MAX_NUM_PORTS 1
+#define MAX_H_COPY_RDMA (128 * 1024)
+
+#define MAX_EYE 64
+
+/* Return codes */
+#define ADAPT_SUCCESS 0L
+/* choose error codes that do not conflict with PHYP */
+#define ERROR -40L
+
+struct format_code {
+ u8 reserved;
+ u8 buffers;
+};
+
+struct client_info {
+#define SRP_VERSION "16.a"
+ char srp_version[8];
+ /* root node property ibm,partition-name */
+ char partition_name[PARTITION_NAMELEN];
+ /* root node property ibm,partition-no */
+ u32 partition_number;
+ /* initially 1 */
+ u32 mad_version;
+ u32 os_type;
+};
+
+/*
+ * Changing this constant changes the number of seconds to wait before
+ * considering the client will never service its queue again.
+ */
+#define SECONDS_TO_CONSIDER_FAILED 30
+/*
+ * These constants set the polling period used to determine if the client
+ * has freed at least one element in the response queue.
+ */
+#define WAIT_SECONDS 1
+#define WAIT_NANO_SECONDS 5000
+#define MAX_TIMER_POPS ((1000000 / WAIT_NANO_SECONDS) * \
+ SECONDS_TO_CONSIDER_FAILED)
+/*
+ * general purpose timer control block
+ * which can be used for multiple functions
+ */
+struct timer_cb {
+ struct hrtimer timer;
+ /*
+ * how long has it been since the client
+ * serviced the queue. The variable is incrmented
+ * in the service_wait_q routine and cleared
+ * in send messages
+ */
+ int timer_pops;
+ /* the timer is started */
+ bool started;
+};
+
+struct cmd_queue {
+ /* kva */
+ struct viosrp_crq *base_addr;
+ dma_addr_t crq_token;
+ /* used to maintain index */
+ uint mask;
+ /* current element */
+ uint index;
+ int size;
+};
+
+#define SCSOLNT_RESP_SHIFT 1
+#define UCSOLNT_RESP_SHIFT 2
+
+#define SCSOLNT BIT(SCSOLNT_RESP_SHIFT)
+#define UCSOLNT BIT(UCSOLNT_RESP_SHIFT)
+
+enum cmd_type {
+ SCSI_CDB = 0x01,
+ TASK_MANAGEMENT = 0x02,
+ /* MAD or addressed to port 0 */
+ ADAPTER_MAD = 0x04,
+ UNSET_TYPE = 0x08,
+};
+
+struct iu_rsp {
+ u8 format;
+ u8 sol_not;
+ u16 len;
+ /* tag is just to help client identify cmd, so don't translate be/le */
+ u64 tag;
+};
+
+struct ibmvscsis_cmd {
+ struct list_head list;
+ /* Used for TCM Core operations */
+ struct se_cmd se_cmd;
+ struct iu_entry *iue;
+ struct iu_rsp rsp;
+ struct work_struct work;
+ struct scsi_info *adapter;
+ struct ibmvscsis_cmd *abort_cmd;
+ /* Sense buffer that will be mapped into outgoing status */
+ unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+ u64 init_time;
+#define CMD_FAST_FAIL BIT(0)
+#define DELAY_SEND BIT(1)
+ u32 flags;
+ char type;
+};
+
+struct ibmvscsis_nexus {
+ struct se_session *se_sess;
+};
+
+struct ibmvscsis_tport {
+ /* SCSI protocol the tport is providing */
+ u8 tport_proto_id;
+ /* ASCII formatted WWPN for SRP Target port */
+ char tport_name[IBMVSCSIS_NAMELEN];
+ /* Returned by ibmvscsis_make_tport() */
+ struct se_wwn tport_wwn;
+ /* Returned by ibmvscsis_make_tpg() */
+ struct se_portal_group se_tpg;
+ /* ibmvscsis port target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Pointer to TCM session for I_T Nexus */
+ struct ibmvscsis_nexus *ibmv_nexus;
+ bool enabled;
+ bool releasing;
+};
+
+struct scsi_info {
+ struct list_head list;
+ char eye[MAX_EYE];
+
+ /* commands waiting for space on repsonse queue */
+ struct list_head waiting_rsp;
+#define NO_QUEUE 0x00
+#define WAIT_ENABLED 0X01
+#define WAIT_CONNECTION 0x04
+ /* have established a connection */
+#define CONNECTED 0x08
+ /* at least one port is processing SRP IU */
+#define SRP_PROCESSING 0x10
+ /* remove request received */
+#define UNCONFIGURING 0x20
+ /* disconnect by letting adapter go idle, no error */
+#define WAIT_IDLE 0x40
+ /* disconnecting to clear an error */
+#define ERR_DISCONNECT 0x80
+ /* disconnect to clear error state, then come back up */
+#define ERR_DISCONNECT_RECONNECT 0x100
+ /* disconnected after clearing an error */
+#define ERR_DISCONNECTED 0x200
+ /* A series of errors caused unexpected errors */
+#define UNDEFINED 0x400
+ u16 state;
+ int fast_fail;
+ struct target_dds dds;
+ char *cmd_pool;
+ /* list of free commands */
+ struct list_head free_cmd;
+ /* command elements ready for scheduler */
+ struct list_head schedule_q;
+ /* commands sent to TCM */
+ struct list_head active_q;
+ caddr_t *map_buf;
+ /* ioba of map buffer */
+ dma_addr_t map_ioba;
+ /* allowable number of outstanding SRP requests */
+ int request_limit;
+ /* extra credit */
+ int credit;
+ /* outstanding transactions against credit limit */
+ int debit;
+
+ /* allow only one outstanding mad request */
+#define PROCESSING_MAD 0x00002
+ /* Waiting to go idle */
+#define WAIT_FOR_IDLE 0x00004
+ /* H_REG_CRQ called */
+#define CRQ_CLOSED 0x00010
+ /* detected that client has failed */
+#define CLIENT_FAILED 0x00040
+ /* detected that transport event occurred */
+#define TRANS_EVENT 0x00080
+ /* don't attempt to send anything to the client */
+#define RESPONSE_Q_DOWN 0x00100
+ /* request made to schedule disconnect handler */
+#define SCHEDULE_DISCONNECT 0x00400
+ /* disconnect handler is scheduled */
+#define DISCONNECT_SCHEDULED 0x00800
+ /* remove function is sleeping */
+#define CFG_SLEEPING 0x01000
+ /* Register for Prepare for Suspend Transport Events */
+#define PREP_FOR_SUSPEND_ENABLED 0x02000
+ /* Prepare for Suspend event sent */
+#define PREP_FOR_SUSPEND_PENDING 0x04000
+ /* Resume from Suspend event sent */
+#define PREP_FOR_SUSPEND_ABORTED 0x08000
+ /* Prepare for Suspend event overwrote another CRQ entry */
+#define PREP_FOR_SUSPEND_OVERWRITE 0x10000
+ u32 flags;
+ /* adapter lock */
+ spinlock_t intr_lock;
+ /* information needed to manage command queue */
+ struct cmd_queue cmd_q;
+ /* used in hcall to copy response back into srp buffer */
+ u64 empty_iu_id;
+ /* used in crq, to tag what iu the response is for */
+ u64 empty_iu_tag;
+ uint new_state;
+ uint resume_state;
+ /* control block for the response queue timer */
+ struct timer_cb rsp_q_timer;
+ /* keep last client to enable proper accounting */
+ struct client_info client_data;
+ /* what can this client do */
+ u32 client_cap;
+ /*
+ * The following two fields capture state and flag changes that
+ * can occur when the lock is given up. In the orginal design,
+ * the lock was held during calls into phyp;
+ * however, phyp did not meet PAPR architecture. This is
+ * a work around.
+ */
+ u16 phyp_acr_state;
+ u32 phyp_acr_flags;
+
+ struct workqueue_struct *work_q;
+ struct completion wait_idle;
+ struct completion unconfig;
+ struct device dev;
+ struct vio_dev *dma_dev;
+ struct srp_target target;
+ struct ibmvscsis_tport tport;
+ struct tasklet_struct work_task;
+ struct work_struct proc_work;
+};
+
+/*
+ * Provide a constant that allows software to detect the adapter is
+ * disconnecting from the client from one of several states.
+ */
+#define IS_DISCONNECTING (UNCONFIGURING | ERR_DISCONNECT_RECONNECT | \
+ ERR_DISCONNECT)
+
+/*
+ * Provide a constant that can be used with interrupt handling that
+ * essentially lets the interrupt handler know that all requests should
+ * be thrown out,
+ */
+#define DONT_PROCESS_STATE (IS_DISCONNECTING | UNDEFINED | \
+ ERR_DISCONNECTED | WAIT_IDLE)
+
+/*
+ * If any of these flag bits are set then do not allow the interrupt
+ * handler to schedule the off level handler.
+ */
+#define BLOCK (DISCONNECT_SCHEDULED)
+
+/* State and transition events that stop the interrupt handler */
+#define TARGET_STOP(VSCSI) (long)(((VSCSI)->state & DONT_PROCESS_STATE) | \
+ ((VSCSI)->flags & BLOCK))
+
+#define PREP_FOR_SUSPEND_FLAGS (PREP_FOR_SUSPEND_ENABLED | \
+ PREP_FOR_SUSPEND_PENDING | \
+ PREP_FOR_SUSPEND_ABORTED | \
+ PREP_FOR_SUSPEND_OVERWRITE)
+
+/* flag bit that are not reset during disconnect */
+#define PRESERVE_FLAG_FIELDS (PREP_FOR_SUSPEND_FLAGS)
+
+#define vio_iu(IUE) ((union viosrp_iu *)((IUE)->sbuf->buf))
+
+#define READ_CMD(cdb) (((cdb)[0] & 0x1F) == 8)
+#define WRITE_CMD(cdb) (((cdb)[0] & 0x1F) == 0xA)
+
+#ifndef H_GET_PARTNER_INFO
+#define H_GET_PARTNER_INFO 0x0000000000000008LL
+#endif
+#ifndef H_ENABLE_PREPARE_FOR_SUSPEND
+#define H_ENABLE_PREPARE_FOR_SUSPEND 0x000000000000001DLL
+#endif
+#ifndef H_READY_FOR_SUSPEND
+#define H_READY_FOR_SUSPEND 0x000000000000001ELL
+#endif
+
+
+#define h_copy_rdma(l, sa, sb, da, db) \
+ plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db)
+#define h_vioctl(u, o, a, u1, u2, u3, u4) \
+ plpar_hcall_norets(H_VIOCTL, u, o, a, u1, u2)
+#define h_reg_crq(ua, tok, sz) \
+ plpar_hcall_norets(H_REG_CRQ, ua, tok, sz)
+#define h_free_crq(ua) \
+ plpar_hcall_norets(H_FREE_CRQ, ua)
+#define h_send_crq(ua, d1, d2) \
+ plpar_hcall_norets(H_SEND_CRQ, ua, d1, d2)
+
+#endif
diff --git a/drivers/scsi/ibmvscsi_tgt/libsrp.c b/drivers/scsi/ibmvscsi_tgt/libsrp.c
new file mode 100644
index 000000000000..0ecad398ed3d
--- /dev/null
+++ b/drivers/scsi/ibmvscsi_tgt/libsrp.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*******************************************************************************
+ * SCSI RDMA Protocol lib functions
+ *
+ * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2016 Bryant G. Ly <bryantly@linux.vnet.ibm.com> IBM Corp.
+ *
+ ***********************************************************************/
+
+#define pr_fmt(fmt) "libsrp: " fmt
+
+#include <linux/printk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/kfifo.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <scsi/srp.h>
+#include <target/target_core_base.h>
+#include "libsrp.h"
+#include "ibmvscsi_tgt.h"
+
+static int srp_iu_pool_alloc(struct srp_queue *q, size_t max,
+ struct srp_buf **ring)
+{
+ struct iu_entry *iue;
+ int i;
+
+ q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL);
+ if (!q->pool)
+ return -ENOMEM;
+ q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL);
+ if (!q->items)
+ goto free_pool;
+
+ spin_lock_init(&q->lock);
+ kfifo_init(&q->queue, (void *)q->pool, max * sizeof(void *));
+
+ for (i = 0, iue = q->items; i < max; i++) {
+ kfifo_in(&q->queue, (void *)&iue, sizeof(void *));
+ iue->sbuf = ring[i];
+ iue++;
+ }
+ return 0;
+
+free_pool:
+ kfree(q->pool);
+ return -ENOMEM;
+}
+
+static void srp_iu_pool_free(struct srp_queue *q)
+{
+ kfree(q->items);
+ kfree(q->pool);
+}
+
+static struct srp_buf **srp_ring_alloc(struct device *dev,
+ size_t max, size_t size)
+{
+ struct srp_buf **ring;
+ int i;
+
+ ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL);
+ if (!ring)
+ return NULL;
+
+ for (i = 0; i < max; i++) {
+ ring[i] = kzalloc(sizeof(*ring[i]), GFP_KERNEL);
+ if (!ring[i])
+ goto out;
+ ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma,
+ GFP_KERNEL);
+ if (!ring[i]->buf)
+ goto out;
+ }
+ return ring;
+
+out:
+ for (i = 0; i < max && ring[i]; i++) {
+ if (ring[i]->buf) {
+ dma_free_coherent(dev, size, ring[i]->buf,
+ ring[i]->dma);
+ }
+ kfree(ring[i]);
+ }
+ kfree(ring);
+
+ return NULL;
+}
+
+static void srp_ring_free(struct device *dev, struct srp_buf **ring,
+ size_t max, size_t size)
+{
+ int i;
+
+ for (i = 0; i < max; i++) {
+ dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma);
+ kfree(ring[i]);
+ }
+ kfree(ring);
+}
+
+int srp_target_alloc(struct srp_target *target, struct device *dev,
+ size_t nr, size_t iu_size)
+{
+ int err;
+
+ spin_lock_init(&target->lock);
+
+ target->dev = dev;
+
+ target->srp_iu_size = iu_size;
+ target->rx_ring_size = nr;
+ target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size);
+ if (!target->rx_ring)
+ return -ENOMEM;
+ err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring);
+ if (err)
+ goto free_ring;
+
+ dev_set_drvdata(target->dev, target);
+ return 0;
+
+free_ring:
+ srp_ring_free(target->dev, target->rx_ring, nr, iu_size);
+ return -ENOMEM;
+}
+
+void srp_target_free(struct srp_target *target)
+{
+ dev_set_drvdata(target->dev, NULL);
+ srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size,
+ target->srp_iu_size);
+ srp_iu_pool_free(&target->iu_queue);
+}
+
+struct iu_entry *srp_iu_get(struct srp_target *target)
+{
+ struct iu_entry *iue = NULL;
+
+ if (kfifo_out_locked(&target->iu_queue.queue, (void *)&iue,
+ sizeof(void *),
+ &target->iu_queue.lock) != sizeof(void *)) {
+ WARN_ONCE(1, "unexpected fifo state");
+ return NULL;
+ }
+ if (!iue)
+ return iue;
+ iue->target = target;
+ iue->flags = 0;
+ return iue;
+}
+
+void srp_iu_put(struct iu_entry *iue)
+{
+ kfifo_in_locked(&iue->target->iu_queue.queue, (void *)&iue,
+ sizeof(void *), &iue->target->iu_queue.lock);
+}
+
+static int srp_direct_data(struct ibmvscsis_cmd *cmd, struct srp_direct_buf *md,
+ enum dma_data_direction dir, srp_rdma_t rdma_io,
+ int dma_map, int ext_desc)
+{
+ struct iu_entry *iue = NULL;
+ struct scatterlist *sg = NULL;
+ int err, nsg = 0, len;
+
+ if (dma_map) {
+ iue = cmd->iue;
+ sg = cmd->se_cmd.t_data_sg;
+ nsg = dma_map_sg(iue->target->dev, sg, cmd->se_cmd.t_data_nents,
+ DMA_BIDIRECTIONAL);
+ if (!nsg) {
+ pr_err("fail to map %p %d\n", iue,
+ cmd->se_cmd.t_data_nents);
+ return 0;
+ }
+ len = min(cmd->se_cmd.data_length, be32_to_cpu(md->len));
+ } else {
+ len = be32_to_cpu(md->len);
+ }
+
+ err = rdma_io(cmd, sg, nsg, md, 1, dir, len);
+
+ if (dma_map)
+ dma_unmap_sg(iue->target->dev, sg, cmd->se_cmd.t_data_nents,
+ DMA_BIDIRECTIONAL);
+
+ return err;
+}
+
+static int srp_indirect_data(struct ibmvscsis_cmd *cmd, struct srp_cmd *srp_cmd,
+ struct srp_indirect_buf *id,
+ enum dma_data_direction dir, srp_rdma_t rdma_io,
+ int dma_map, int ext_desc)
+{
+ struct iu_entry *iue = NULL;
+ struct srp_direct_buf *md = NULL;
+ struct scatterlist dummy, *sg = NULL;
+ dma_addr_t token = 0;
+ int err = 0;
+ int nmd, nsg = 0, len;
+
+ if (dma_map || ext_desc) {
+ iue = cmd->iue;
+ sg = cmd->se_cmd.t_data_sg;
+ }
+
+ nmd = be32_to_cpu(id->table_desc.len) / sizeof(struct srp_direct_buf);
+
+ if ((dir == DMA_FROM_DEVICE && nmd == srp_cmd->data_in_desc_cnt) ||
+ (dir == DMA_TO_DEVICE && nmd == srp_cmd->data_out_desc_cnt)) {
+ md = &id->desc_list[0];
+ goto rdma;
+ }
+
+ if (ext_desc && dma_map) {
+ md = dma_alloc_coherent(iue->target->dev,
+ be32_to_cpu(id->table_desc.len),
+ &token, GFP_KERNEL);
+ if (!md) {
+ pr_err("Can't get dma memory %u\n",
+ be32_to_cpu(id->table_desc.len));
+ return -ENOMEM;
+ }
+
+ sg_init_one(&dummy, md, be32_to_cpu(id->table_desc.len));
+ sg_dma_address(&dummy) = token;
+ sg_dma_len(&dummy) = be32_to_cpu(id->table_desc.len);
+ err = rdma_io(cmd, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE,
+ be32_to_cpu(id->table_desc.len));
+ if (err) {
+ pr_err("Error copying indirect table %d\n", err);
+ goto free_mem;
+ }
+ } else {
+ pr_err("This command uses external indirect buffer\n");
+ return -EINVAL;
+ }
+
+rdma:
+ if (dma_map) {
+ nsg = dma_map_sg(iue->target->dev, sg, cmd->se_cmd.t_data_nents,
+ DMA_BIDIRECTIONAL);
+ if (!nsg) {
+ pr_err("fail to map %p %d\n", iue,
+ cmd->se_cmd.t_data_nents);
+ err = -EIO;
+ goto free_mem;
+ }
+ len = min(cmd->se_cmd.data_length, be32_to_cpu(id->len));
+ } else {
+ len = be32_to_cpu(id->len);
+ }
+
+ err = rdma_io(cmd, sg, nsg, md, nmd, dir, len);
+
+ if (dma_map)
+ dma_unmap_sg(iue->target->dev, sg, cmd->se_cmd.t_data_nents,
+ DMA_BIDIRECTIONAL);
+
+free_mem:
+ if (token && dma_map) {
+ dma_free_coherent(iue->target->dev,
+ be32_to_cpu(id->table_desc.len), md, token);
+ }
+ return err;
+}
+
+static int data_out_desc_size(struct srp_cmd *cmd)
+{
+ int size = 0;
+ u8 fmt = cmd->buf_fmt >> 4;
+
+ switch (fmt) {
+ case SRP_NO_DATA_DESC:
+ break;
+ case SRP_DATA_DESC_DIRECT:
+ size = sizeof(struct srp_direct_buf);
+ break;
+ case SRP_DATA_DESC_INDIRECT:
+ size = sizeof(struct srp_indirect_buf) +
+ sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt;
+ break;
+ default:
+ pr_err("client error. Invalid data_out_format %x\n", fmt);
+ break;
+ }
+ return size;
+}
+
+/*
+ * TODO: this can be called multiple times for a single command if it
+ * has very long data.
+ */
+int srp_transfer_data(struct ibmvscsis_cmd *cmd, struct srp_cmd *srp_cmd,
+ srp_rdma_t rdma_io, int dma_map, int ext_desc)
+{
+ struct srp_direct_buf *md;
+ struct srp_indirect_buf *id;
+ enum dma_data_direction dir;
+ int offset, err = 0;
+ u8 format;
+
+ if (!cmd->se_cmd.t_data_nents)
+ return 0;
+
+ offset = srp_cmd->add_cdb_len & ~3;
+
+ dir = srp_cmd_direction(srp_cmd);
+ if (dir == DMA_FROM_DEVICE)
+ offset += data_out_desc_size(srp_cmd);
+
+ if (dir == DMA_TO_DEVICE)
+ format = srp_cmd->buf_fmt >> 4;
+ else
+ format = srp_cmd->buf_fmt & ((1U << 4) - 1);
+
+ switch (format) {
+ case SRP_NO_DATA_DESC:
+ break;
+ case SRP_DATA_DESC_DIRECT:
+ md = (struct srp_direct_buf *)(srp_cmd->add_data + offset);
+ err = srp_direct_data(cmd, md, dir, rdma_io, dma_map, ext_desc);
+ break;
+ case SRP_DATA_DESC_INDIRECT:
+ id = (struct srp_indirect_buf *)(srp_cmd->add_data + offset);
+ err = srp_indirect_data(cmd, srp_cmd, id, dir, rdma_io, dma_map,
+ ext_desc);
+ break;
+ default:
+ pr_err("Unknown format %d %x\n", dir, format);
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+u64 srp_data_length(struct srp_cmd *cmd, enum dma_data_direction dir)
+{
+ struct srp_direct_buf *md;
+ struct srp_indirect_buf *id;
+ u64 len = 0;
+ uint offset = cmd->add_cdb_len & ~3;
+ u8 fmt;
+
+ if (dir == DMA_TO_DEVICE) {
+ fmt = cmd->buf_fmt >> 4;
+ } else {
+ fmt = cmd->buf_fmt & ((1U << 4) - 1);
+ offset += data_out_desc_size(cmd);
+ }
+
+ switch (fmt) {
+ case SRP_NO_DATA_DESC:
+ break;
+ case SRP_DATA_DESC_DIRECT:
+ md = (struct srp_direct_buf *)(cmd->add_data + offset);
+ len = be32_to_cpu(md->len);
+ break;
+ case SRP_DATA_DESC_INDIRECT:
+ id = (struct srp_indirect_buf *)(cmd->add_data + offset);
+ len = be32_to_cpu(id->len);
+ break;
+ default:
+ pr_err("invalid data format %x\n", fmt);
+ break;
+ }
+ return len;
+}
+
+int srp_get_desc_table(struct srp_cmd *srp_cmd, enum dma_data_direction *dir,
+ u64 *data_len)
+{
+ struct srp_indirect_buf *idb;
+ struct srp_direct_buf *db;
+ uint add_cdb_offset;
+ int rc;
+
+ /*
+ * The pointer computations below will only be compiled correctly
+ * if srp_cmd::add_data is declared as s8*, u8*, s8[] or u8[], so check
+ * whether srp_cmd::add_data has been declared as a byte pointer.
+ */
+ BUILD_BUG_ON(!__same_type(srp_cmd->add_data[0], (s8)0)
+ && !__same_type(srp_cmd->add_data[0], (u8)0));
+
+ BUG_ON(!dir);
+ BUG_ON(!data_len);
+
+ rc = 0;
+ *data_len = 0;
+
+ *dir = DMA_NONE;
+
+ if (srp_cmd->buf_fmt & 0xf)
+ *dir = DMA_FROM_DEVICE;
+ else if (srp_cmd->buf_fmt >> 4)
+ *dir = DMA_TO_DEVICE;
+
+ add_cdb_offset = srp_cmd->add_cdb_len & ~3;
+ if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_DIRECT) ||
+ ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_DIRECT)) {
+ db = (struct srp_direct_buf *)(srp_cmd->add_data
+ + add_cdb_offset);
+ *data_len = be32_to_cpu(db->len);
+ } else if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_INDIRECT) ||
+ ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_INDIRECT)) {
+ idb = (struct srp_indirect_buf *)(srp_cmd->add_data
+ + add_cdb_offset);
+
+ *data_len = be32_to_cpu(idb->len);
+ }
+ return rc;
+}
+
+MODULE_DESCRIPTION("SCSI RDMA Protocol lib functions");
+MODULE_AUTHOR("FUJITA Tomonori");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/ibmvscsi_tgt/libsrp.h b/drivers/scsi/ibmvscsi_tgt/libsrp.h
new file mode 100644
index 000000000000..832606ae2908
--- /dev/null
+++ b/drivers/scsi/ibmvscsi_tgt/libsrp.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBSRP_H__
+#define __LIBSRP_H__
+
+#include <linux/list.h>
+#include <linux/kfifo.h>
+#include <scsi/srp.h>
+
+enum srp_valid {
+ INVALIDATE_CMD_RESP_EL = 0,
+ VALID_CMD_RESP_EL = 0x80,
+ VALID_INIT_MSG = 0xC0,
+ VALID_TRANS_EVENT = 0xFF
+};
+
+enum srp_format {
+ SRP_FORMAT = 1,
+ MAD_FORMAT = 2,
+ OS400_FORMAT = 3,
+ AIX_FORMAT = 4,
+ LINUX_FORMAT = 5,
+ MESSAGE_IN_CRQ = 6
+};
+
+enum srp_init_msg {
+ INIT_MSG = 1,
+ INIT_COMPLETE_MSG = 2
+};
+
+enum srp_trans_event {
+ UNUSED_FORMAT = 0,
+ PARTNER_FAILED = 1,
+ PARTNER_DEREGISTER = 2,
+ MIGRATED = 6,
+ PREPARE_FOR_SUSPEND = 9,
+ RESUME_FROM_SUSP = 0xA
+};
+
+enum srp_status {
+ CRQ_ENTRY_OVERWRITTEN = 0x20,
+ HEADER_DESCRIPTOR = 0xF1,
+ PING = 0xF5,
+ PING_RESPONSE = 0xF6
+};
+
+enum srp_mad_version {
+ MAD_VERSION_1 = 1
+};
+
+enum srp_os_type {
+ OS400 = 1,
+ LINUX = 2,
+ AIX = 3,
+ OFW = 4
+};
+
+enum srp_task_attributes {
+ SRP_SIMPLE_TASK = 0,
+ SRP_HEAD_TASK = 1,
+ SRP_ORDERED_TASK = 2,
+ SRP_ACA_TASK = 4
+};
+
+enum {
+ SRP_TASK_MANAGEMENT_FUNCTION_COMPLETE = 0,
+ SRP_REQUEST_FIELDS_INVALID = 2,
+ SRP_TASK_MANAGEMENT_FUNCTION_NOT_SUPPORTED = 4,
+ SRP_TASK_MANAGEMENT_FUNCTION_FAILED = 5
+};
+
+struct srp_buf {
+ dma_addr_t dma;
+ void *buf;
+};
+
+struct srp_queue {
+ void *pool;
+ void *items;
+ struct kfifo queue;
+ spinlock_t lock;
+};
+
+struct srp_target {
+ struct device *dev;
+
+ spinlock_t lock;
+ struct list_head cmd_queue;
+
+ size_t srp_iu_size;
+ struct srp_queue iu_queue;
+ size_t rx_ring_size;
+ struct srp_buf **rx_ring;
+
+ void *ldata;
+};
+
+struct iu_entry {
+ struct srp_target *target;
+
+ struct list_head ilist;
+ dma_addr_t remote_token;
+ unsigned long flags;
+
+ struct srp_buf *sbuf;
+ u16 iu_len;
+};
+
+struct ibmvscsis_cmd;
+
+typedef int (srp_rdma_t)(struct ibmvscsis_cmd *, struct scatterlist *, int,
+ struct srp_direct_buf *, int,
+ enum dma_data_direction, unsigned int);
+int srp_target_alloc(struct srp_target *, struct device *, size_t, size_t);
+void srp_target_free(struct srp_target *);
+struct iu_entry *srp_iu_get(struct srp_target *);
+void srp_iu_put(struct iu_entry *);
+int srp_transfer_data(struct ibmvscsis_cmd *, struct srp_cmd *,
+ srp_rdma_t, int, int);
+u64 srp_data_length(struct srp_cmd *cmd, enum dma_data_direction dir);
+int srp_get_desc_table(struct srp_cmd *srp_cmd, enum dma_data_direction *dir,
+ u64 *data_len);
+static inline int srp_cmd_direction(struct srp_cmd *cmd)
+{
+ return (cmd->buf_fmt >> 4) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+}
+
+#endif
diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c
index 26cd9d1d7571..5c602c057798 100644
--- a/drivers/scsi/imm.c
+++ b/drivers/scsi/imm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* imm.c -- low level driver for the IOMEGA MatchMaker
* parallel port SCSI host adapter.
*
@@ -43,16 +44,22 @@ typedef struct {
unsigned dp:1; /* Data phase present */
unsigned rd:1; /* Read data in data phase */
unsigned wanted:1; /* Parport sharing busy flag */
+ unsigned int dev_no; /* Device number */
wait_queue_head_t *waiting;
struct Scsi_Host *host;
struct list_head list;
} imm_struct;
static void imm_reset_pulse(unsigned int base);
-static int device_check(imm_struct *dev);
+static int device_check(imm_struct *dev, bool autodetect);
#include "imm.h"
+static unsigned int mode = IMM_AUTODETECT;
+module_param(mode, uint, 0644);
+MODULE_PARM_DESC(mode, "Transfer mode (0 = Autodetect, 1 = SPP 4-bit, "
+ "2 = SPP 8-bit, 3 = EPP 8-bit, 4 = EPP 16-bit, 5 = EPP 32-bit");
+
static inline imm_struct *imm_dev(struct Scsi_Host *host)
{
return *(imm_struct **)&host->hostdata;
@@ -64,7 +71,7 @@ static void got_it(imm_struct *dev)
{
dev->base = dev->dev->port->base;
if (dev->cur_cmd)
- dev->cur_cmd->SCp.phase = 1;
+ imm_scsi_pointer(dev->cur_cmd)->phase = 1;
else
wake_up(dev->waiting);
}
@@ -76,9 +83,10 @@ static void imm_wakeup(void *ref)
spin_lock_irqsave(&arbitration_lock, flags);
if (dev->wanted) {
- parport_claim(dev->dev);
- got_it(dev);
- dev->wanted = 0;
+ if (parport_claim(dev->dev) == 0) {
+ got_it(dev);
+ dev->wanted = 0;
+ }
}
spin_unlock_irqrestore(&arbitration_lock, flags);
}
@@ -121,45 +129,26 @@ static inline void imm_pb_release(imm_struct *dev)
* testing...
* Also gives a method to use a script to obtain optimum timings (TODO)
*/
-static inline int imm_proc_write(imm_struct *dev, char *buffer, int length)
+static int imm_write_info(struct Scsi_Host *host, char *buffer, int length)
{
- unsigned long x;
+ imm_struct *dev = imm_dev(host);
if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {
- x = simple_strtoul(buffer + 5, NULL, 0);
- dev->mode = x;
+ dev->mode = simple_strtoul(buffer + 5, NULL, 0);
return length;
}
printk("imm /proc: invalid variable\n");
- return (-EINVAL);
+ return -EINVAL;
}
-static int imm_proc_info(struct Scsi_Host *host, char *buffer, char **start,
- off_t offset, int length, int inout)
+static int imm_show_info(struct seq_file *m, struct Scsi_Host *host)
{
imm_struct *dev = imm_dev(host);
- int len = 0;
-
- if (inout)
- return imm_proc_write(dev, buffer, length);
-
- len += sprintf(buffer + len, "Version : %s\n", IMM_VERSION);
- len +=
- sprintf(buffer + len, "Parport : %s\n",
- dev->dev->port->name);
- len +=
- sprintf(buffer + len, "Mode : %s\n",
- IMM_MODE_STRING[dev->mode]);
-
- /* Request for beyond end of buffer */
- if (offset > len)
- return 0;
- *start = buffer + offset;
- len -= offset;
- if (len > length)
- len = length;
- return len;
+ seq_printf(m, "Version : %s\n", IMM_VERSION);
+ seq_printf(m, "Parport : %s\n", dev->dev->port->name);
+ seq_printf(m, "Mode : %s\n", IMM_MODE_STRING[dev->mode]);
+ return 0;
}
#if IMM_DEBUG > 0
@@ -382,13 +371,10 @@ static int imm_out(imm_struct *dev, char *buffer, int len)
case IMM_EPP_8:
epp_reset(ppb);
w_ctr(ppb, 0x4);
-#ifdef CONFIG_SCSI_IZIP_EPP16
- if (!(((long) buffer | len) & 0x01))
- outsw(ppb + 4, buffer, len >> 1);
-#else
- if (!(((long) buffer | len) & 0x03))
+ if (dev->mode == IMM_EPP_32 && !(((long) buffer | len) & 0x03))
outsl(ppb + 4, buffer, len >> 2);
-#endif
+ else if (dev->mode == IMM_EPP_16 && !(((long) buffer | len) & 0x01))
+ outsw(ppb + 4, buffer, len >> 1);
else
outsb(ppb + 4, buffer, len);
w_ctr(ppb, 0xc);
@@ -442,13 +428,10 @@ static int imm_in(imm_struct *dev, char *buffer, int len)
case IMM_EPP_8:
epp_reset(ppb);
w_ctr(ppb, 0x24);
-#ifdef CONFIG_SCSI_IZIP_EPP16
- if (!(((long) buffer | len) & 0x01))
- insw(ppb + 4, buffer, len >> 1);
-#else
- if (!(((long) buffer | len) & 0x03))
- insl(ppb + 4, buffer, len >> 2);
-#endif
+ if (dev->mode == IMM_EPP_32 && !(((long) buffer | len) & 0x03))
+ insw(ppb + 4, buffer, len >> 2);
+ else if (dev->mode == IMM_EPP_16 && !(((long) buffer | len) & 0x01))
+ insl(ppb + 4, buffer, len >> 1);
else
insb(ppb + 4, buffer, len);
w_ctr(ppb, 0x2c);
@@ -605,13 +588,28 @@ static int imm_select(imm_struct *dev, int target)
static int imm_init(imm_struct *dev)
{
+ bool autodetect = dev->mode == IMM_AUTODETECT;
+
+ if (autodetect) {
+ int modes = dev->dev->port->modes;
+
+ /* Mode detection works up the chain of speed
+ * This avoids a nasty if-then-else-if-... tree
+ */
+ dev->mode = IMM_NIBBLE;
+
+ if (modes & PARPORT_MODE_TRISTATE)
+ dev->mode = IMM_PS2;
+ }
+
if (imm_connect(dev, 0) != 1)
return -EIO;
imm_reset_pulse(dev->base);
mdelay(1); /* Delay to allow devices to settle */
imm_disconnect(dev);
mdelay(1); /* Another delay to allow devices to settle */
- return device_check(dev);
+
+ return device_check(dev, autodetect);
}
static inline int imm_send_command(struct scsi_cmnd *cmd)
@@ -634,13 +632,14 @@ static inline int imm_send_command(struct scsi_cmnd *cmd)
* The driver appears to remain stable if we speed up the parallel port
* i/o in this function, but not elsewhere.
*/
-static int imm_completion(struct scsi_cmnd *cmd)
+static int imm_completion(struct scsi_cmnd *const cmd)
{
/* Return codes:
* -1 Error
* 0 Told to schedule
* 1 Finished data transfer
*/
+ struct scsi_pointer *scsi_pointer = imm_scsi_pointer(cmd);
imm_struct *dev = imm_dev(cmd->device->host);
unsigned short ppb = dev->base;
unsigned long start_jiffies = jiffies;
@@ -676,44 +675,43 @@ static int imm_completion(struct scsi_cmnd *cmd)
* a) Drive status is screwy (!ready && !present)
* b) Drive is requesting/sending more data than expected
*/
- if (((r & 0x88) != 0x88) || (cmd->SCp.this_residual <= 0)) {
+ if ((r & 0x88) != 0x88 || scsi_pointer->this_residual <= 0) {
imm_fail(dev, DID_ERROR);
return -1; /* ERROR_RETURN */
}
/* determine if we should use burst I/O */
if (dev->rd == 0) {
- fast = (bulk
- && (cmd->SCp.this_residual >=
- IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 2;
- status = imm_out(dev, cmd->SCp.ptr, fast);
+ fast = bulk && scsi_pointer->this_residual >=
+ IMM_BURST_SIZE ? IMM_BURST_SIZE : 2;
+ status = imm_out(dev, scsi_pointer->ptr, fast);
} else {
- fast = (bulk
- && (cmd->SCp.this_residual >=
- IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 1;
- status = imm_in(dev, cmd->SCp.ptr, fast);
+ fast = bulk && scsi_pointer->this_residual >=
+ IMM_BURST_SIZE ? IMM_BURST_SIZE : 1;
+ status = imm_in(dev, scsi_pointer->ptr, fast);
}
- cmd->SCp.ptr += fast;
- cmd->SCp.this_residual -= fast;
+ scsi_pointer->ptr += fast;
+ scsi_pointer->this_residual -= fast;
if (!status) {
imm_fail(dev, DID_BUS_BUSY);
return -1; /* ERROR_RETURN */
}
- if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+ if (scsi_pointer->buffer && !scsi_pointer->this_residual) {
/* if scatter/gather, advance to the next segment */
- if (cmd->SCp.buffers_residual--) {
- cmd->SCp.buffer++;
- cmd->SCp.this_residual =
- cmd->SCp.buffer->length;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
+ if (scsi_pointer->buffers_residual--) {
+ scsi_pointer->buffer =
+ sg_next(scsi_pointer->buffer);
+ scsi_pointer->this_residual =
+ scsi_pointer->buffer->length;
+ scsi_pointer->ptr = sg_virt(scsi_pointer->buffer);
/*
* Make sure that we transfer even number of bytes
* otherwise it makes imm_byte_out() messy.
*/
- if (cmd->SCp.this_residual & 0x01)
- cmd->SCp.this_residual++;
+ if (scsi_pointer->this_residual & 0x01)
+ scsi_pointer->this_residual++;
}
}
/* Now check to see if the drive is ready to comunicate */
@@ -778,20 +776,21 @@ static void imm_interrupt(struct work_struct *work)
}
#endif
- if (cmd->SCp.phase > 1)
+ if (imm_scsi_pointer(cmd)->phase > 1)
imm_disconnect(dev);
imm_pb_dismiss(dev);
spin_lock_irqsave(host->host_lock, flags);
dev->cur_cmd = NULL;
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
spin_unlock_irqrestore(host->host_lock, flags);
return;
}
-static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
+static int imm_engine(imm_struct *dev, struct scsi_cmnd *const cmd)
{
+ struct scsi_pointer *scsi_pointer = imm_scsi_pointer(cmd);
unsigned short ppb = dev->base;
unsigned char l = 0, h = 0;
int retv, x;
@@ -802,7 +801,7 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
if (dev->failed)
return 0;
- switch (cmd->SCp.phase) {
+ switch (scsi_pointer->phase) {
case 0: /* Phase 0 - Waiting for parport */
if (time_after(jiffies, dev->jstart + HZ)) {
/*
@@ -813,46 +812,47 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
return 0;
}
return 1; /* wait until imm_wakeup claims parport */
- /* Phase 1 - Connected */
- case 1:
+
+ case 1: /* Phase 1 - Connected */
imm_connect(dev, CONNECT_EPP_MAYBE);
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
- /* Phase 2 - We are now talking to the scsi bus */
- case 2:
+ case 2: /* Phase 2 - We are now talking to the scsi bus */
if (!imm_select(dev, scmd_id(cmd))) {
imm_fail(dev, DID_NO_CONNECT);
return 0;
}
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
- /* Phase 3 - Ready to accept a command */
- case 3:
+ case 3: /* Phase 3 - Ready to accept a command */
w_ctr(ppb, 0x0c);
if (!(r_str(ppb) & 0x80))
return 1;
if (!imm_send_command(cmd))
return 0;
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
- /* Phase 4 - Setup scatter/gather buffers */
- case 4:
+ case 4: /* Phase 4 - Setup scatter/gather buffers */
if (scsi_bufflen(cmd)) {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
+ scsi_pointer->buffer = scsi_sglist(cmd);
+ scsi_pointer->this_residual = scsi_pointer->buffer->length;
+ scsi_pointer->ptr = sg_virt(scsi_pointer->buffer);
} else {
- cmd->SCp.buffer = NULL;
- cmd->SCp.this_residual = 0;
- cmd->SCp.ptr = NULL;
+ scsi_pointer->buffer = NULL;
+ scsi_pointer->this_residual = 0;
+ scsi_pointer->ptr = NULL;
}
- cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
- cmd->SCp.phase++;
- if (cmd->SCp.this_residual & 0x01)
- cmd->SCp.this_residual++;
- /* Phase 5 - Pre-Data transfer stage */
- case 5:
+ scsi_pointer->buffers_residual = scsi_sg_count(cmd) - 1;
+ scsi_pointer->phase++;
+ if (scsi_pointer->this_residual & 0x01)
+ scsi_pointer->this_residual++;
+ fallthrough;
+
+ case 5: /* Phase 5 - Pre-Data transfer stage */
/* Spin lock for BUSY */
w_ctr(ppb, 0x0c);
if (!(r_str(ppb) & 0x80))
@@ -866,10 +866,10 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
if ((dev->dp) && (dev->rd))
if (imm_negotiate(dev))
return 0;
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
- /* Phase 6 - Data transfer stage */
- case 6:
+ case 6: /* Phase 6 - Data transfer stage */
/* Spin lock for BUSY */
w_ctr(ppb, 0x0c);
if (!(r_str(ppb) & 0x80))
@@ -882,10 +882,10 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
if (retv == 0)
return 1;
}
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
- /* Phase 7 - Post data transfer stage */
- case 7:
+ case 7: /* Phase 7 - Post data transfer stage */
if ((dev->dp) && (dev->rd)) {
if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) {
w_ctr(ppb, 0x4);
@@ -894,10 +894,10 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
w_ctr(ppb, 0x4);
}
}
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
- /* Phase 8 - Read status/message */
- case 8:
+ case 8: /* Phase 8 - Read status/message */
/* Check for data overrun */
if (imm_wait(dev) != (unsigned char) 0xb8) {
imm_fail(dev, DID_ERROR);
@@ -909,7 +909,7 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
/* Check for optional message byte */
if (imm_wait(dev) == (unsigned char) 0xb8)
imm_in(dev, &h, 1);
- cmd->result = (DID_OK << 16) + (l & STATUS_MASK);
+ cmd->result = (DID_OK << 16) | (l & STATUS_MASK);
}
if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) {
w_ctr(ppb, 0x4);
@@ -918,7 +918,6 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
w_ctr(ppb, 0x4);
}
return 0; /* Finished */
- break;
default:
printk("imm: Invalid scsi phase\n");
@@ -926,8 +925,7 @@ static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
return 0;
}
-static int imm_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
+static int imm_queuecommand_lck(struct scsi_cmnd *cmd)
{
imm_struct *dev = imm_dev(cmd->device->host);
@@ -938,9 +936,8 @@ static int imm_queuecommand_lck(struct scsi_cmnd *cmd,
dev->failed = 0;
dev->jstart = jiffies;
dev->cur_cmd = cmd;
- cmd->scsi_done = done;
cmd->result = DID_ERROR << 16; /* default return code */
- cmd->SCp.phase = 0; /* bus free */
+ imm_scsi_pointer(cmd)->phase = 0; /* bus free */
schedule_delayed_work(&dev->imm_tq, 0);
@@ -957,7 +954,7 @@ static DEF_SCSI_QCMD(imm_queuecommand)
* be done in sd.c. Even if it gets fixed there, this will still
* work.
*/
-static int imm_biosparam(struct scsi_device *sdev, struct block_device *dev,
+static int imm_biosparam(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int ip[])
{
ip[0] = 0x40;
@@ -979,15 +976,13 @@ static int imm_abort(struct scsi_cmnd *cmd)
* have tied the SCSI_MESSAGE line high in the interface
*/
- switch (cmd->SCp.phase) {
+ switch (imm_scsi_pointer(cmd)->phase) {
case 0: /* Do not have access to parport */
case 1: /* Have not connected to interface */
dev->cur_cmd = NULL; /* Forget the problem */
return SUCCESS;
- break;
default: /* SCSI command sent, can not abort */
return FAILED;
- break;
}
}
@@ -1007,7 +1002,7 @@ static int imm_reset(struct scsi_cmnd *cmd)
{
imm_struct *dev = imm_dev(cmd->device->host);
- if (cmd->SCp.phase)
+ if (imm_scsi_pointer(cmd)->phase)
imm_disconnect(dev);
dev->cur_cmd = NULL; /* Forget the problem */
@@ -1019,7 +1014,7 @@ static int imm_reset(struct scsi_cmnd *cmd)
return SUCCESS;
}
-static int device_check(imm_struct *dev)
+static int device_check(imm_struct *dev, bool autodetect)
{
/* This routine looks for a device and then attempts to use EPP
to send a command. If all goes as planned then EPP is available. */
@@ -1031,8 +1026,8 @@ static int device_check(imm_struct *dev)
old_mode = dev->mode;
for (loop = 0; loop < 8; loop++) {
/* Attempt to use EPP for Test Unit Ready */
- if ((ppb & 0x0007) == 0x0000)
- dev->mode = IMM_EPP_32;
+ if (autodetect && (ppb & 0x0007) == 0x0000)
+ dev->mode = IMM_EPP_8;
second_pass:
imm_connect(dev, CONNECT_EPP_MAYBE);
@@ -1057,7 +1052,7 @@ static int device_check(imm_struct *dev)
udelay(1000);
imm_disconnect(dev);
udelay(1000);
- if (dev->mode == IMM_EPP_32) {
+ if (dev->mode != old_mode) {
dev->mode = old_mode;
goto second_pass;
}
@@ -1082,7 +1077,7 @@ static int device_check(imm_struct *dev)
udelay(1000);
imm_disconnect(dev);
udelay(1000);
- if (dev->mode == IMM_EPP_32) {
+ if (dev->mode != old_mode) {
dev->mode = old_mode;
goto second_pass;
}
@@ -1105,32 +1100,20 @@ static int device_check(imm_struct *dev)
return -ENODEV;
}
-/*
- * imm cannot deal with highmem, so this causes all IO pages for this host
- * to reside in low memory (hence mapped)
- */
-static int imm_adjust_queue(struct scsi_device *device)
-{
- blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH);
- return 0;
-}
-
-static struct scsi_host_template imm_template = {
+static const struct scsi_host_template imm_template = {
.module = THIS_MODULE,
.proc_name = "imm",
- .proc_info = imm_proc_info,
+ .show_info = imm_show_info,
+ .write_info = imm_write_info,
.name = "Iomega VPI2 (imm) interface",
.queuecommand = imm_queuecommand,
.eh_abort_handler = imm_abort,
- .eh_bus_reset_handler = imm_reset,
.eh_host_reset_handler = imm_reset,
.bios_param = imm_biosparam,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
.can_queue = 1,
- .slave_alloc = imm_adjust_queue,
+ .cmd_size = sizeof(struct scsi_pointer),
};
/***************************************************************************
@@ -1139,15 +1122,39 @@ static struct scsi_host_template imm_template = {
static LIST_HEAD(imm_hosts);
+/*
+ * Finds the first available device number that can be alloted to the
+ * new imm device and returns the address of the previous node so that
+ * we can add to the tail and have a list in the ascending order.
+ */
+
+static inline imm_struct *find_parent(void)
+{
+ imm_struct *dev, *par = NULL;
+ unsigned int cnt = 0;
+
+ if (list_empty(&imm_hosts))
+ return NULL;
+
+ list_for_each_entry(dev, &imm_hosts, list) {
+ if (dev->dev_no != cnt)
+ return par;
+ cnt++;
+ par = dev;
+ }
+
+ return par;
+}
+
static int __imm_attach(struct parport *pb)
{
struct Scsi_Host *host;
- imm_struct *dev;
+ imm_struct *dev, *temp;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waiting);
DEFINE_WAIT(wait);
int ports;
- int modes, ppb;
int err = -ENOMEM;
+ struct pardev_cb imm_cb;
init_waitqueue_head(&waiting);
@@ -1157,12 +1164,18 @@ static int __imm_attach(struct parport *pb)
dev->base = -1;
- dev->mode = IMM_AUTODETECT;
+ dev->mode = mode < IMM_UNKNOWN ? mode : IMM_AUTODETECT;
INIT_LIST_HEAD(&dev->list);
- dev->dev = parport_register_device(pb, "imm", NULL, imm_wakeup,
- NULL, 0, dev);
+ temp = find_parent();
+ if (temp)
+ dev->dev_no = temp->dev_no + 1;
+ memset(&imm_cb, 0, sizeof(imm_cb));
+ imm_cb.private = dev;
+ imm_cb.wakeup = imm_wakeup;
+
+ dev->dev = parport_register_dev_model(pb, "imm", &imm_cb, dev->dev_no);
if (!dev->dev)
goto out;
@@ -1186,18 +1199,9 @@ static int __imm_attach(struct parport *pb)
}
dev->waiting = NULL;
finish_wait(&waiting, &wait);
- ppb = dev->base = dev->dev->port->base;
+ dev->base = dev->dev->port->base;
dev->base_hi = dev->dev->port->base_hi;
- w_ctr(ppb, 0x0c);
- modes = dev->dev->port->modes;
-
- /* Mode detection works up the chain of speed
- * This avoids a nasty if-then-else-if-... tree
- */
- dev->mode = IMM_NIBBLE;
-
- if (modes & PARPORT_MODE_TRISTATE)
- dev->mode = IMM_PS2;
+ w_ctr(dev->base, 0x0c);
/* Done configuration */
@@ -1226,7 +1230,10 @@ static int __imm_attach(struct parport *pb)
host->unique_id = pb->number;
*(imm_struct **)&host->hostdata = dev;
dev->host = host;
- list_add_tail(&dev->list, &imm_hosts);
+ if (!temp)
+ list_add_tail(&dev->list, &imm_hosts);
+ else
+ list_add_tail(&dev->list, &temp->list);
err = scsi_add_host(host, NULL);
if (err)
goto out2;
@@ -1264,23 +1271,11 @@ static void imm_detach(struct parport *pb)
}
static struct parport_driver imm_driver = {
- .name = "imm",
- .attach = imm_attach,
- .detach = imm_detach,
+ .name = "imm",
+ .match_port = imm_attach,
+ .detach = imm_detach,
};
+module_parport_driver(imm_driver);
-static int __init imm_driver_init(void)
-{
- printk("imm: Version %s\n", IMM_VERSION);
- return parport_register_driver(&imm_driver);
-}
-
-static void __exit imm_driver_exit(void)
-{
- parport_unregister_driver(&imm_driver);
-}
-
-module_init(imm_driver_init);
-module_exit(imm_driver_exit);
-
+MODULE_DESCRIPTION("IOMEGA MatchMaker parallel port SCSI host adapter driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/imm.h b/drivers/scsi/imm.h
index 8f6f32fc61ff..398fa5b15181 100644
--- a/drivers/scsi/imm.h
+++ b/drivers/scsi/imm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Driver for the Iomega MatchMaker parallel port SCSI HBA embedded in
* the Iomega ZIP Plus drive
@@ -99,11 +100,7 @@ static char *IMM_MODE_STRING[] =
[IMM_PS2] = "PS/2",
[IMM_EPP_8] = "EPP 8 bit",
[IMM_EPP_16] = "EPP 16 bit",
-#ifdef CONFIG_SCSI_IZIP_EPP16
- [IMM_EPP_32] = "EPP 16 bit",
-#else
[IMM_EPP_32] = "EPP 32 bit",
-#endif
[IMM_UNKNOWN] = "Unknown",
};
@@ -138,6 +135,11 @@ static char *IMM_MODE_STRING[] =
#define w_ctr(x,y) outb(y, (x)+2)
#endif
+static inline struct scsi_pointer *imm_scsi_pointer(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
static int imm_engine(imm_struct *, struct scsi_cmnd *);
#endif /* _IMM_H */
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
deleted file mode 100644
index 112f1bec7756..000000000000
--- a/drivers/scsi/in2000.c
+++ /dev/null
@@ -1,2337 +0,0 @@
-/*
- * in2000.c - Linux device driver for the
- * Always IN2000 ISA SCSI card.
- *
- * Copyright (c) 1996 John Shifflett, GeoLog Consulting
- * john@geolog.com
- * jshiffle@netcom.com
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * For the avoidance of doubt the "preferred form" of this code is one which
- * is in an open non patent encumbered format. Where cryptographic key signing
- * forms part of the process of creating an executable the information
- * including keys needed to generate an equivalently functional executable
- * are deemed to be part of the source code.
- *
- * Drew Eckhardt's excellent 'Generic NCR5380' sources provided
- * much of the inspiration and some of the code for this driver.
- * The Linux IN2000 driver distributed in the Linux kernels through
- * version 1.2.13 was an extremely valuable reference on the arcane
- * (and still mysterious) workings of the IN2000's fifo. It also
- * is where I lifted in2000_biosparam(), the gist of the card
- * detection scheme, and other bits of code. Many thanks to the
- * talented and courageous people who wrote, contributed to, and
- * maintained that driver (including Brad McLean, Shaun Savage,
- * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey,
- * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric
- * Youngdale). I should also mention the driver written by
- * Hamish Macdonald for the (GASP!) Amiga A2091 card, included
- * in the Linux-m68k distribution; it gave me a good initial
- * understanding of the proper way to run a WD33c93 chip, and I
- * ended up stealing lots of code from it.
- *
- * _This_ driver is (I feel) an improvement over the old one in
- * several respects:
- * - All problems relating to the data size of a SCSI request are
- * gone (as far as I know). The old driver couldn't handle
- * swapping to partitions because that involved 4k blocks, nor
- * could it deal with the st.c tape driver unmodified, because
- * that usually involved 4k - 32k blocks. The old driver never
- * quite got away from a morbid dependence on 2k block sizes -
- * which of course is the size of the card's fifo.
- *
- * - Target Disconnection/Reconnection is now supported. Any
- * system with more than one device active on the SCSI bus
- * will benefit from this. The driver defaults to what I'm
- * calling 'adaptive disconnect' - meaning that each command
- * is evaluated individually as to whether or not it should
- * be run with the option to disconnect/reselect (if the
- * device chooses), or as a "SCSI-bus-hog".
- *
- * - Synchronous data transfers are now supported. Because there
- * are a few devices (and many improperly terminated systems)
- * that choke when doing sync, the default is sync DISABLED
- * for all devices. This faster protocol can (and should!)
- * be enabled on selected devices via the command-line.
- *
- * - Runtime operating parameters can now be specified through
- * either the LILO or the 'insmod' command line. For LILO do:
- * "in2000=blah,blah,blah"
- * and with insmod go like:
- * "insmod /usr/src/linux/modules/in2000.o setup_strings=blah,blah"
- * The defaults should be good for most people. See the comment
- * for 'setup_strings' below for more details.
- *
- * - The old driver relied exclusively on what the Western Digital
- * docs call "Combination Level 2 Commands", which are a great
- * idea in that the CPU is relieved of a lot of interrupt
- * overhead. However, by accepting a certain (user-settable)
- * amount of additional interrupts, this driver achieves
- * better control over the SCSI bus, and data transfers are
- * almost as fast while being much easier to define, track,
- * and debug.
- *
- * - You can force detection of a card whose BIOS has been disabled.
- *
- * - Multiple IN2000 cards might almost be supported. I've tried to
- * keep it in mind, but have no way to test...
- *
- *
- * TODO:
- * tagged queuing. multiple cards.
- *
- *
- * NOTE:
- * When using this or any other SCSI driver as a module, you'll
- * find that with the stock kernel, at most _two_ SCSI hard
- * drives will be linked into the device list (ie, usable).
- * If your IN2000 card has more than 2 disks on its bus, you
- * might want to change the define of 'SD_EXTRA_DEVS' in the
- * 'hosts.h' file from 2 to whatever is appropriate. It took
- * me a while to track down this surprisingly obscure and
- * undocumented little "feature".
- *
- *
- * People with bug reports, wish-lists, complaints, comments,
- * or improvements are asked to pah-leeez email me (John Shifflett)
- * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get
- * this thing into as good a shape as possible, and I'm positive
- * there are lots of lurking bugs and "Stupid Places".
- *
- * Updated for Linux 2.5 by Alan Cox <alan@lxorguk.ukuu.org.uk>
- * - Using new_eh handler
- * - Hopefully got all the locking right again
- * See "FIXME" notes for items that could do with more work
- */
-
-#include <linux/module.h>
-#include <linux/blkdev.h>
-#include <linux/interrupt.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/ioport.h>
-#include <linux/stat.h>
-
-#include <asm/io.h>
-#include <asm/system.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-#define IN2000_VERSION "1.33-2.5"
-#define IN2000_DATE "2002/11/03"
-
-#include "in2000.h"
-
-
-/*
- * 'setup_strings' is a single string used to pass operating parameters and
- * settings from the kernel/module command-line to the driver. 'setup_args[]'
- * is an array of strings that define the compile-time default values for
- * these settings. If Linux boots with a LILO or insmod command-line, those
- * settings are combined with 'setup_args[]'. Note that LILO command-lines
- * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix.
- * The driver recognizes the following keywords (lower case required) and
- * arguments:
- *
- * - ioport:addr -Where addr is IO address of a (usually ROM-less) card.
- * - noreset -No optional args. Prevents SCSI bus reset at boot time.
- * - nosync:x -x is a bitmask where the 1st 7 bits correspond with
- * the 7 possible SCSI devices (bit 0 for device #0, etc).
- * Set a bit to PREVENT sync negotiation on that device.
- * The driver default is sync DISABLED on all devices.
- * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer
- * period. Default is 500; acceptable values are 250 - 1000.
- * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them.
- * x = 1 does 'adaptive' disconnects, which is the default
- * and generally the best choice.
- * - debug:x -If 'DEBUGGING_ON' is defined, x is a bitmask that causes
- * various types of debug output to printed - see the DB_xxx
- * defines in in2000.h
- * - proc:x -If 'PROC_INTERFACE' is defined, x is a bitmask that
- * determines how the /proc interface works and what it
- * does - see the PR_xxx defines in in2000.h
- *
- * Syntax Notes:
- * - Numeric arguments can be decimal or the '0x' form of hex notation. There
- * _must_ be a colon between a keyword and its numeric argument, with no
- * spaces.
- * - Keywords are separated by commas, no spaces, in the standard kernel
- * command-line manner.
- * - A keyword in the 'nth' comma-separated command-line member will overwrite
- * the 'nth' element of setup_args[]. A blank command-line member (in
- * other words, a comma with no preceding keyword) will _not_ overwrite
- * the corresponding setup_args[] element.
- *
- * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'):
- * - in2000=ioport:0x220,noreset
- * - in2000=period:250,disconnect:2,nosync:0x03
- * - in2000=debug:0x1e
- * - in2000=proc:3
- */
-
-/* Normally, no defaults are specified... */
-static char *setup_args[] = { "", "", "", "", "", "", "", "", "" };
-
-/* filled in by 'insmod' */
-static char *setup_strings;
-
-module_param(setup_strings, charp, 0);
-
-static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
-{
- write1_io(reg_num, IO_WD_ADDR);
- return read1_io(IO_WD_DATA);
-}
-
-
-#define READ_AUX_STAT() read1_io(IO_WD_ASR)
-
-
-static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
-{
- write1_io(reg_num, IO_WD_ADDR);
- write1_io(value, IO_WD_DATA);
-}
-
-
-static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
-{
-/* while (READ_AUX_STAT() & ASR_CIP)
- printk("|");*/
- write1_io(WD_COMMAND, IO_WD_ADDR);
- write1_io(cmd, IO_WD_DATA);
-}
-
-
-static uchar read_1_byte(struct IN2000_hostdata *hostdata)
-{
- uchar asr, x = 0;
-
- write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
- write_3393_cmd(hostdata, WD_CMD_TRANS_INFO | 0x80);
- do {
- asr = READ_AUX_STAT();
- if (asr & ASR_DBR)
- x = read_3393(hostdata, WD_DATA);
- } while (!(asr & ASR_INT));
- return x;
-}
-
-
-static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
-{
- write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR);
- write1_io((value >> 16), IO_WD_DATA);
- write1_io((value >> 8), IO_WD_DATA);
- write1_io(value, IO_WD_DATA);
-}
-
-
-static unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
-{
- unsigned long value;
-
- write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR);
- value = read1_io(IO_WD_DATA) << 16;
- value |= read1_io(IO_WD_DATA) << 8;
- value |= read1_io(IO_WD_DATA);
- return value;
-}
-
-
-/* The 33c93 needs to be told which direction a command transfers its
- * data; we use this function to figure it out. Returns true if there
- * will be a DATA_OUT phase with this command, false otherwise.
- * (Thanks to Joerg Dorchain for the research and suggestion.)
- */
-static int is_dir_out(Scsi_Cmnd * cmd)
-{
- switch (cmd->cmnd[0]) {
- case WRITE_6:
- case WRITE_10:
- case WRITE_12:
- case WRITE_LONG:
- case WRITE_SAME:
- case WRITE_BUFFER:
- case WRITE_VERIFY:
- case WRITE_VERIFY_12:
- case COMPARE:
- case COPY:
- case COPY_VERIFY:
- case SEARCH_EQUAL:
- case SEARCH_HIGH:
- case SEARCH_LOW:
- case SEARCH_EQUAL_12:
- case SEARCH_HIGH_12:
- case SEARCH_LOW_12:
- case FORMAT_UNIT:
- case REASSIGN_BLOCKS:
- case RESERVE:
- case MODE_SELECT:
- case MODE_SELECT_10:
- case LOG_SELECT:
- case SEND_DIAGNOSTIC:
- case CHANGE_DEFINITION:
- case UPDATE_BLOCK:
- case SET_WINDOW:
- case MEDIUM_SCAN:
- case SEND_VOLUME_TAG:
- case 0xea:
- return 1;
- default:
- return 0;
- }
-}
-
-
-
-static struct sx_period sx_table[] = {
- {1, 0x20},
- {252, 0x20},
- {376, 0x30},
- {500, 0x40},
- {624, 0x50},
- {752, 0x60},
- {876, 0x70},
- {1000, 0x00},
- {0, 0}
-};
-
-static int round_period(unsigned int period)
-{
- int x;
-
- for (x = 1; sx_table[x].period_ns; x++) {
- if ((period <= sx_table[x - 0].period_ns) && (period > sx_table[x - 1].period_ns)) {
- return x;
- }
- }
- return 7;
-}
-
-static uchar calc_sync_xfer(unsigned int period, unsigned int offset)
-{
- uchar result;
-
- period *= 4; /* convert SDTR code to ns */
- result = sx_table[round_period(period)].reg_value;
- result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF;
- return result;
-}
-
-
-
-static void in2000_execute(struct Scsi_Host *instance);
-
-static int in2000_queuecommand_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-{
- struct Scsi_Host *instance;
- struct IN2000_hostdata *hostdata;
- Scsi_Cmnd *tmp;
-
- instance = cmd->device->host;
- hostdata = (struct IN2000_hostdata *) instance->hostdata;
-
- DB(DB_QUEUE_COMMAND, scmd_printk(KERN_DEBUG, cmd, "Q-%02x(", cmd->cmnd[0]))
-
-/* Set up a few fields in the Scsi_Cmnd structure for our own use:
- * - host_scribble is the pointer to the next cmd in the input queue
- * - scsi_done points to the routine we call when a cmd is finished
- * - result is what you'd expect
- */
- cmd->host_scribble = NULL;
- cmd->scsi_done = done;
- cmd->result = 0;
-
-/* We use the Scsi_Pointer structure that's included with each command
- * as a scratchpad (as it's intended to be used!). The handy thing about
- * the SCp.xxx fields is that they're always associated with a given
- * cmd, and are preserved across disconnect-reselect. This means we
- * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
- * if we keep all the critical pointers and counters in SCp:
- * - SCp.ptr is the pointer into the RAM buffer
- * - SCp.this_residual is the size of that buffer
- * - SCp.buffer points to the current scatter-gather buffer
- * - SCp.buffers_residual tells us how many S.G. buffers there are
- * - SCp.have_data_in helps keep track of >2048 byte transfers
- * - SCp.sent_command is not used
- * - SCp.phase records this command's SRCID_ER bit setting
- */
-
- if (scsi_bufflen(cmd)) {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- } else {
- cmd->SCp.buffer = NULL;
- cmd->SCp.buffers_residual = 0;
- cmd->SCp.ptr = NULL;
- cmd->SCp.this_residual = 0;
- }
- cmd->SCp.have_data_in = 0;
-
-/* We don't set SCp.phase here - that's done in in2000_execute() */
-
-/* WD docs state that at the conclusion of a "LEVEL2" command, the
- * status byte can be retrieved from the LUN register. Apparently,
- * this is the case only for *uninterrupted* LEVEL2 commands! If
- * there are any unexpected phases entered, even if they are 100%
- * legal (different devices may choose to do things differently),
- * the LEVEL2 command sequence is exited. This often occurs prior
- * to receiving the status byte, in which case the driver does a
- * status phase interrupt and gets the status byte on its own.
- * While such a command can then be "resumed" (ie restarted to
- * finish up as a LEVEL2 command), the LUN register will NOT be
- * a valid status byte at the command's conclusion, and we must
- * use the byte obtained during the earlier interrupt. Here, we
- * preset SCp.Status to an illegal value (0xff) so that when
- * this command finally completes, we can tell where the actual
- * status byte is stored.
- */
-
- cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
-
-/* We need to disable interrupts before messing with the input
- * queue and calling in2000_execute().
- */
-
- /*
- * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE
- * commands are added to the head of the queue so that the desired
- * sense data is not lost before REQUEST_SENSE executes.
- */
-
- if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
- cmd->host_scribble = (uchar *) hostdata->input_Q;
- hostdata->input_Q = cmd;
- } else { /* find the end of the queue */
- for (tmp = (Scsi_Cmnd *) hostdata->input_Q; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble);
- tmp->host_scribble = (uchar *) cmd;
- }
-
-/* We know that there's at least one command in 'input_Q' now.
- * Go see if any of them are runnable!
- */
-
- in2000_execute(cmd->device->host);
-
- DB(DB_QUEUE_COMMAND, printk(")Q "))
- return 0;
-}
-
-static DEF_SCSI_QCMD(in2000_queuecommand)
-
-
-
-/*
- * This routine attempts to start a scsi command. If the host_card is
- * already connected, we give up immediately. Otherwise, look through
- * the input_Q, using the first command we find that's intended
- * for a currently non-busy target/lun.
- * Note that this function is always called with interrupts already
- * disabled (either from in2000_queuecommand() or in2000_intr()).
- */
-static void in2000_execute(struct Scsi_Host *instance)
-{
- struct IN2000_hostdata *hostdata;
- Scsi_Cmnd *cmd, *prev;
- int i;
- unsigned short *sp;
- unsigned short f;
- unsigned short flushbuf[16];
-
-
- hostdata = (struct IN2000_hostdata *) instance->hostdata;
-
- DB(DB_EXECUTE, printk("EX("))
-
- if (hostdata->selecting || hostdata->connected) {
-
- DB(DB_EXECUTE, printk(")EX-0 "))
-
- return;
- }
-
- /*
- * Search through the input_Q for a command destined
- * for an idle target/lun.
- */
-
- cmd = (Scsi_Cmnd *) hostdata->input_Q;
- prev = NULL;
- while (cmd) {
- if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)))
- break;
- prev = cmd;
- cmd = (Scsi_Cmnd *) cmd->host_scribble;
- }
-
- /* quit if queue empty or all possible targets are busy */
-
- if (!cmd) {
-
- DB(DB_EXECUTE, printk(")EX-1 "))
-
- return;
- }
-
- /* remove command from queue */
-
- if (prev)
- prev->host_scribble = cmd->host_scribble;
- else
- hostdata->input_Q = (Scsi_Cmnd *) cmd->host_scribble;
-
-#ifdef PROC_STATISTICS
- hostdata->cmd_cnt[cmd->device->id]++;
-#endif
-
-/*
- * Start the selection process
- */
-
- if (is_dir_out(cmd))
- write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id);
- else
- write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
-
-/* Now we need to figure out whether or not this command is a good
- * candidate for disconnect/reselect. We guess to the best of our
- * ability, based on a set of hierarchical rules. When several
- * devices are operating simultaneously, disconnects are usually
- * an advantage. In a single device system, or if only 1 device
- * is being accessed, transfers usually go faster if disconnects
- * are not allowed:
- *
- * + Commands should NEVER disconnect if hostdata->disconnect =
- * DIS_NEVER (this holds for tape drives also), and ALWAYS
- * disconnect if hostdata->disconnect = DIS_ALWAYS.
- * + Tape drive commands should always be allowed to disconnect.
- * + Disconnect should be allowed if disconnected_Q isn't empty.
- * + Commands should NOT disconnect if input_Q is empty.
- * + Disconnect should be allowed if there are commands in input_Q
- * for a different target/lun. In this case, the other commands
- * should be made disconnect-able, if not already.
- *
- * I know, I know - this code would flunk me out of any
- * "C Programming 101" class ever offered. But it's easy
- * to change around and experiment with for now.
- */
-
- cmd->SCp.phase = 0; /* assume no disconnect */
- if (hostdata->disconnect == DIS_NEVER)
- goto no;
- if (hostdata->disconnect == DIS_ALWAYS)
- goto yes;
- if (cmd->device->type == 1) /* tape drive? */
- goto yes;
- if (hostdata->disconnected_Q) /* other commands disconnected? */
- goto yes;
- if (!(hostdata->input_Q)) /* input_Q empty? */
- goto no;
- for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble) {
- if ((prev->device->id != cmd->device->id) || (prev->device->lun != cmd->device->lun)) {
- for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble)
- prev->SCp.phase = 1;
- goto yes;
- }
- }
- goto no;
-
- yes:
- cmd->SCp.phase = 1;
-
-#ifdef PROC_STATISTICS
- hostdata->disc_allowed_cnt[cmd->device->id]++;
-#endif
-
- no:
- write_3393(hostdata, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0));
-
- write_3393(hostdata, WD_TARGET_LUN, cmd->device->lun);
- write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]);
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
-
- if ((hostdata->level2 <= L2_NONE) || (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) {
-
- /*
- * Do a 'Select-With-ATN' command. This will end with
- * one of the following interrupts:
- * CSR_RESEL_AM: failure - can try again later.
- * CSR_TIMEOUT: failure - give up.
- * CSR_SELECT: success - proceed.
- */
-
- hostdata->selecting = cmd;
-
-/* Every target has its own synchronous transfer setting, kept in
- * the sync_xfer array, and a corresponding status byte in sync_stat[].
- * Each target's sync_stat[] entry is initialized to SS_UNSET, and its
- * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET
- * means that the parameters are undetermined as yet, and that we
- * need to send an SDTR message to this device after selection is
- * complete. We set SS_FIRST to tell the interrupt routine to do so,
- * unless we don't want to even _try_ synchronous transfers: In this
- * case we set SS_SET to make the defaults final.
- */
- if (hostdata->sync_stat[cmd->device->id] == SS_UNSET) {
- if (hostdata->sync_off & (1 << cmd->device->id))
- hostdata->sync_stat[cmd->device->id] = SS_SET;
- else
- hostdata->sync_stat[cmd->device->id] = SS_FIRST;
- }
- hostdata->state = S_SELECTING;
- write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN);
- }
-
- else {
-
- /*
- * Do a 'Select-With-ATN-Xfer' command. This will end with
- * one of the following interrupts:
- * CSR_RESEL_AM: failure - can try again later.
- * CSR_TIMEOUT: failure - give up.
- * anything else: success - proceed.
- */
-
- hostdata->connected = cmd;
- write_3393(hostdata, WD_COMMAND_PHASE, 0);
-
- /* copy command_descriptor_block into WD chip
- * (take advantage of auto-incrementing)
- */
-
- write1_io(WD_CDB_1, IO_WD_ADDR);
- for (i = 0; i < cmd->cmd_len; i++)
- write1_io(cmd->cmnd[i], IO_WD_DATA);
-
- /* The wd33c93 only knows about Group 0, 1, and 5 commands when
- * it's doing a 'select-and-transfer'. To be safe, we write the
- * size of the CDB into the OWN_ID register for every case. This
- * way there won't be problems with vendor-unique, audio, etc.
- */
-
- write_3393(hostdata, WD_OWN_ID, cmd->cmd_len);
-
- /* When doing a non-disconnect command, we can save ourselves a DATA
- * phase interrupt later by setting everything up now. With writes we
- * need to pre-fill the fifo; if there's room for the 32 flush bytes,
- * put them in there too - that'll avoid a fifo interrupt. Reads are
- * somewhat simpler.
- * KLUDGE NOTE: It seems that you can't completely fill the fifo here:
- * This results in the IO_FIFO_COUNT register rolling over to zero,
- * and apparently the gate array logic sees this as empty, not full,
- * so the 3393 chip is never signalled to start reading from the
- * fifo. Or maybe it's seen as a permanent fifo interrupt condition.
- * Regardless, we fix this by temporarily pretending that the fifo
- * is 16 bytes smaller. (I see now that the old driver has a comment
- * about "don't fill completely" in an analogous place - must be the
- * same deal.) This results in CDROM, swap partitions, and tape drives
- * needing an extra interrupt per write command - I think we can live
- * with that!
- */
-
- if (!(cmd->SCp.phase)) {
- write_3393_count(hostdata, cmd->SCp.this_residual);
- write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
- write1_io(0, IO_FIFO_WRITE); /* clear fifo counter, write mode */
-
- if (is_dir_out(cmd)) {
- hostdata->fifo = FI_FIFO_WRITING;
- if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16))
- i = IN2000_FIFO_SIZE - 16;
- cmd->SCp.have_data_in = i; /* this much data in fifo */
- i >>= 1; /* Gulp. Assuming modulo 2. */
- sp = (unsigned short *) cmd->SCp.ptr;
- f = hostdata->io_base + IO_FIFO;
-
-#ifdef FAST_WRITE_IO
-
- FAST_WRITE2_IO();
-#else
- while (i--)
- write2_io(*sp++, IO_FIFO);
-
-#endif
-
- /* Is there room for the flush bytes? */
-
- if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) {
- sp = flushbuf;
- i = 16;
-
-#ifdef FAST_WRITE_IO
-
- FAST_WRITE2_IO();
-#else
- while (i--)
- write2_io(0, IO_FIFO);
-
-#endif
-
- }
- }
-
- else {
- write1_io(0, IO_FIFO_READ); /* put fifo in read mode */
- hostdata->fifo = FI_FIFO_READING;
- cmd->SCp.have_data_in = 0; /* nothing transferred yet */
- }
-
- } else {
- write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */
- }
- hostdata->state = S_RUNNING_LEVEL2;
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
- }
-
- /*
- * Since the SCSI bus can handle only 1 connection at a time,
- * we get out of here now. If the selection fails, or when
- * the command disconnects, we'll come back to this routine
- * to search the input_Q again...
- */
-
- DB(DB_EXECUTE, printk("%s)EX-2 ", (cmd->SCp.phase) ? "d:" : ""))
-
-}
-
-
-
-static void transfer_pio(uchar * buf, int cnt, int data_in_dir, struct IN2000_hostdata *hostdata)
-{
- uchar asr;
-
- DB(DB_TRANSFER, printk("(%p,%d,%s)", buf, cnt, data_in_dir ? "in" : "out"))
-
- write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
- write_3393_count(hostdata, cnt);
- write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);
- if (data_in_dir) {
- do {
- asr = READ_AUX_STAT();
- if (asr & ASR_DBR)
- *buf++ = read_3393(hostdata, WD_DATA);
- } while (!(asr & ASR_INT));
- } else {
- do {
- asr = READ_AUX_STAT();
- if (asr & ASR_DBR)
- write_3393(hostdata, WD_DATA, *buf++);
- } while (!(asr & ASR_INT));
- }
-
- /* Note: we are returning with the interrupt UN-cleared.
- * Since (presumably) an entire I/O operation has
- * completed, the bus phase is probably different, and
- * the interrupt routine will discover this when it
- * responds to the uncleared int.
- */
-
-}
-
-
-
-static void transfer_bytes(Scsi_Cmnd * cmd, int data_in_dir)
-{
- struct IN2000_hostdata *hostdata;
- unsigned short *sp;
- unsigned short f;
- int i;
-
- hostdata = (struct IN2000_hostdata *) cmd->device->host->hostdata;
-
-/* Normally, you'd expect 'this_residual' to be non-zero here.
- * In a series of scatter-gather transfers, however, this
- * routine will usually be called with 'this_residual' equal
- * to 0 and 'buffers_residual' non-zero. This means that a
- * previous transfer completed, clearing 'this_residual', and
- * now we need to setup the next scatter-gather buffer as the
- * source or destination for THIS transfer.
- */
- if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
- ++cmd->SCp.buffer;
- --cmd->SCp.buffers_residual;
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- }
-
-/* Set up hardware registers */
-
- write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]);
- write_3393_count(hostdata, cmd->SCp.this_residual);
- write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
- write1_io(0, IO_FIFO_WRITE); /* zero counter, assume write */
-
-/* Reading is easy. Just issue the command and return - we'll
- * get an interrupt later when we have actual data to worry about.
- */
-
- if (data_in_dir) {
- write1_io(0, IO_FIFO_READ);
- if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
- write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
- hostdata->state = S_RUNNING_LEVEL2;
- } else
- write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);
- hostdata->fifo = FI_FIFO_READING;
- cmd->SCp.have_data_in = 0;
- return;
- }
-
-/* Writing is more involved - we'll start the WD chip and write as
- * much data to the fifo as we can right now. Later interrupts will
- * write any bytes that don't make it at this stage.
- */
-
- if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
- write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
- hostdata->state = S_RUNNING_LEVEL2;
- } else
- write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);
- hostdata->fifo = FI_FIFO_WRITING;
- sp = (unsigned short *) cmd->SCp.ptr;
-
- if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE)
- i = IN2000_FIFO_SIZE;
- cmd->SCp.have_data_in = i;
- i >>= 1; /* Gulp. We assume this_residual is modulo 2 */
- f = hostdata->io_base + IO_FIFO;
-
-#ifdef FAST_WRITE_IO
-
- FAST_WRITE2_IO();
-#else
- while (i--)
- write2_io(*sp++, IO_FIFO);
-
-#endif
-
-}
-
-
-/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this
- * function in order to work in an SMP environment. (I'd be surprised
- * if the driver is ever used by anyone on a real multi-CPU motherboard,
- * but it _does_ need to be able to compile and run in an SMP kernel.)
- */
-
-static irqreturn_t in2000_intr(int irqnum, void *dev_id)
-{
- struct Scsi_Host *instance = dev_id;
- struct IN2000_hostdata *hostdata;
- Scsi_Cmnd *patch, *cmd;
- uchar asr, sr, phs, id, lun, *ucp, msg;
- int i, j;
- unsigned long length;
- unsigned short *sp;
- unsigned short f;
- unsigned long flags;
-
- hostdata = (struct IN2000_hostdata *) instance->hostdata;
-
-/* Get the spin_lock and disable further ints, for SMP */
-
- spin_lock_irqsave(instance->host_lock, flags);
-
-#ifdef PROC_STATISTICS
- hostdata->int_cnt++;
-#endif
-
-/* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the
- * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined
- * with a big logic array, so it's a little different than what you might
- * expect). As far as I know, there's no reason that BOTH can't be active
- * at the same time, but there's a problem: while we can read the 3393
- * to tell if _it_ wants an interrupt, I don't know of a way to ask the
- * fifo the same question. The best we can do is check the 3393 and if
- * it _isn't_ the source of the interrupt, then we can be pretty sure
- * that the fifo is the culprit.
- * UPDATE: I have it on good authority (Bill Earnest) that bit 0 of the
- * IO_FIFO_COUNT register mirrors the fifo interrupt state. I
- * assume that bit clear means interrupt active. As it turns
- * out, the driver really doesn't need to check for this after
- * all, so my remarks above about a 'problem' can safely be
- * ignored. The way the logic is set up, there's no advantage
- * (that I can see) to worrying about it.
- *
- * It seems that the fifo interrupt signal is negated when we extract
- * bytes during read or write bytes during write.
- * - fifo will interrupt when data is moving from it to the 3393, and
- * there are 31 (or less?) bytes left to go. This is sort of short-
- * sighted: what if you don't WANT to do more? In any case, our
- * response is to push more into the fifo - either actual data or
- * dummy bytes if need be. Note that we apparently have to write at
- * least 32 additional bytes to the fifo after an interrupt in order
- * to get it to release the ones it was holding on to - writing fewer
- * than 32 will result in another fifo int.
- * UPDATE: Again, info from Bill Earnest makes this more understandable:
- * 32 bytes = two counts of the fifo counter register. He tells
- * me that the fifo interrupt is a non-latching signal derived
- * from a straightforward boolean interpretation of the 7
- * highest bits of the fifo counter and the fifo-read/fifo-write
- * state. Who'd a thought?
- */
-
- write1_io(0, IO_LED_ON);
- asr = READ_AUX_STAT();
- if (!(asr & ASR_INT)) { /* no WD33c93 interrupt? */
-
-/* Ok. This is definitely a FIFO-only interrupt.
- *
- * If FI_FIFO_READING is set, there are up to 2048 bytes waiting to be read,
- * maybe more to come from the SCSI bus. Read as many as we can out of the
- * fifo and into memory at the location of SCp.ptr[SCp.have_data_in], and
- * update have_data_in afterwards.
- *
- * If we have FI_FIFO_WRITING, the FIFO has almost run out of bytes to move
- * into the WD3393 chip (I think the interrupt happens when there are 31
- * bytes left, but it may be fewer...). The 3393 is still waiting, so we
- * shove some more into the fifo, which gets things moving again. If the
- * original SCSI command specified more than 2048 bytes, there may still
- * be some of that data left: fine - use it (from SCp.ptr[SCp.have_data_in]).
- * Don't forget to update have_data_in. If we've already written out the
- * entire buffer, feed 32 dummy bytes to the fifo - they're needed to
- * push out the remaining real data.
- * (Big thanks to Bill Earnest for getting me out of the mud in here.)
- */
-
- cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */
- CHECK_NULL(cmd, "fifo_int")
-
- if (hostdata->fifo == FI_FIFO_READING) {
-
- DB(DB_FIFO, printk("{R:%02x} ", read1_io(IO_FIFO_COUNT)))
-
- sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
- i = read1_io(IO_FIFO_COUNT) & 0xfe;
- i <<= 2; /* # of words waiting in the fifo */
- f = hostdata->io_base + IO_FIFO;
-
-#ifdef FAST_READ_IO
-
- FAST_READ2_IO();
-#else
- while (i--)
- *sp++ = read2_io(IO_FIFO);
-
-#endif
-
- i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
- i <<= 1;
- cmd->SCp.have_data_in += i;
- }
-
- else if (hostdata->fifo == FI_FIFO_WRITING) {
-
- DB(DB_FIFO, printk("{W:%02x} ", read1_io(IO_FIFO_COUNT)))
-
-/* If all bytes have been written to the fifo, flush out the stragglers.
- * Note that while writing 16 dummy words seems arbitrary, we don't
- * have another choice that I can see. What we really want is to read
- * the 3393 transfer count register (that would tell us how many bytes
- * needed flushing), but the TRANSFER_INFO command hasn't completed
- * yet (not enough bytes!) and that register won't be accessible. So,
- * we use 16 words - a number obtained through trial and error.
- * UPDATE: Bill says this is exactly what Always does, so there.
- * More thanks due him for help in this section.
- */
- if (cmd->SCp.this_residual == cmd->SCp.have_data_in) {
- i = 16;
- while (i--) /* write 32 dummy bytes */
- write2_io(0, IO_FIFO);
- }
-
-/* If there are still bytes left in the SCSI buffer, write as many as we
- * can out to the fifo.
- */
-
- else {
- sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
- i = cmd->SCp.this_residual - cmd->SCp.have_data_in; /* bytes yet to go */
- j = read1_io(IO_FIFO_COUNT) & 0xfe;
- j <<= 2; /* how many words the fifo has room for */
- if ((j << 1) > i)
- j = (i >> 1);
- while (j--)
- write2_io(*sp++, IO_FIFO);
-
- i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
- i <<= 1;
- cmd->SCp.have_data_in += i;
- }
- }
-
- else {
- printk("*** Spurious FIFO interrupt ***");
- }
-
- write1_io(0, IO_LED_OFF);
-
-/* release the SMP spin_lock and restore irq state */
- spin_unlock_irqrestore(instance->host_lock, flags);
- return IRQ_HANDLED;
- }
-
-/* This interrupt was triggered by the WD33c93 chip. The fifo interrupt
- * may also be asserted, but we don't bother to check it: we get more
- * detailed info from FIFO_READING and FIFO_WRITING (see below).
- */
-
- cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */
- sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear the interrupt */
- phs = read_3393(hostdata, WD_COMMAND_PHASE);
-
- if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) {
- printk("\nNR:wd-intr-1\n");
- write1_io(0, IO_LED_OFF);
-
-/* release the SMP spin_lock and restore irq state */
- spin_unlock_irqrestore(instance->host_lock, flags);
- return IRQ_HANDLED;
- }
-
- DB(DB_INTR, printk("{%02x:%02x-", asr, sr))
-
-/* After starting a FIFO-based transfer, the next _WD3393_ interrupt is
- * guaranteed to be in response to the completion of the transfer.
- * If we were reading, there's probably data in the fifo that needs
- * to be copied into RAM - do that here. Also, we have to update
- * 'this_residual' and 'ptr' based on the contents of the
- * TRANSFER_COUNT register, in case the device decided to do an
- * intermediate disconnect (a device may do this if it has to
- * do a seek, or just to be nice and let other devices have
- * some bus time during long transfers).
- * After doing whatever is necessary with the fifo, we go on and
- * service the WD3393 interrupt normally.
- */
- if (hostdata->fifo == FI_FIFO_READING) {
-
-/* buffer index = start-of-buffer + #-of-bytes-already-read */
-
- sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
-
-/* bytes remaining in fifo = (total-wanted - #-not-got) - #-already-read */
-
- i = (cmd->SCp.this_residual - read_3393_count(hostdata)) - cmd->SCp.have_data_in;
- i >>= 1; /* Gulp. We assume this will always be modulo 2 */
- f = hostdata->io_base + IO_FIFO;
-
-#ifdef FAST_READ_IO
-
- FAST_READ2_IO();
-#else
- while (i--)
- *sp++ = read2_io(IO_FIFO);
-
-#endif
-
- hostdata->fifo = FI_FIFO_UNUSED;
- length = cmd->SCp.this_residual;
- cmd->SCp.this_residual = read_3393_count(hostdata);
- cmd->SCp.ptr += (length - cmd->SCp.this_residual);
-
- DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual))
-
- }
-
- else if (hostdata->fifo == FI_FIFO_WRITING) {
- hostdata->fifo = FI_FIFO_UNUSED;
- length = cmd->SCp.this_residual;
- cmd->SCp.this_residual = read_3393_count(hostdata);
- cmd->SCp.ptr += (length - cmd->SCp.this_residual);
-
- DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual))
-
- }
-
-/* Respond to the specific WD3393 interrupt - there are quite a few! */
-
- switch (sr) {
-
- case CSR_TIMEOUT:
- DB(DB_INTR, printk("TIMEOUT"))
-
- if (hostdata->state == S_RUNNING_LEVEL2)
- hostdata->connected = NULL;
- else {
- cmd = (Scsi_Cmnd *) hostdata->selecting; /* get a valid cmd */
- CHECK_NULL(cmd, "csr_timeout")
- hostdata->selecting = NULL;
- }
-
- cmd->result = DID_NO_CONNECT << 16;
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- hostdata->state = S_UNCONNECTED;
- cmd->scsi_done(cmd);
-
-/* We are not connected to a target - check to see if there
- * are commands waiting to be executed.
- */
-
- in2000_execute(instance);
- break;
-
-
-/* Note: this interrupt should not occur in a LEVEL2 command */
-
- case CSR_SELECT:
- DB(DB_INTR, printk("SELECT"))
- hostdata->connected = cmd = (Scsi_Cmnd *) hostdata->selecting;
- CHECK_NULL(cmd, "csr_select")
- hostdata->selecting = NULL;
-
- /* construct an IDENTIFY message with correct disconnect bit */
-
- hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->device->lun);
- if (cmd->SCp.phase)
- hostdata->outgoing_msg[0] |= 0x40;
-
- if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) {
-#ifdef SYNC_DEBUG
- printk(" sending SDTR ");
-#endif
-
- hostdata->sync_stat[cmd->device->id] = SS_WAITING;
-
- /* tack on a 2nd message to ask about synchronous transfers */
-
- hostdata->outgoing_msg[1] = EXTENDED_MESSAGE;
- hostdata->outgoing_msg[2] = 3;
- hostdata->outgoing_msg[3] = EXTENDED_SDTR;
- hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4;
- hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF;
- hostdata->outgoing_len = 6;
- } else
- hostdata->outgoing_len = 1;
-
- hostdata->state = S_CONNECTED;
- break;
-
-
- case CSR_XFER_DONE | PHS_DATA_IN:
- case CSR_UNEXP | PHS_DATA_IN:
- case CSR_SRV_REQ | PHS_DATA_IN:
- DB(DB_INTR, printk("IN-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual))
- transfer_bytes(cmd, DATA_IN_DIR);
- if (hostdata->state != S_RUNNING_LEVEL2)
- hostdata->state = S_CONNECTED;
- break;
-
-
- case CSR_XFER_DONE | PHS_DATA_OUT:
- case CSR_UNEXP | PHS_DATA_OUT:
- case CSR_SRV_REQ | PHS_DATA_OUT:
- DB(DB_INTR, printk("OUT-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual))
- transfer_bytes(cmd, DATA_OUT_DIR);
- if (hostdata->state != S_RUNNING_LEVEL2)
- hostdata->state = S_CONNECTED;
- break;
-
-
-/* Note: this interrupt should not occur in a LEVEL2 command */
-
- case CSR_XFER_DONE | PHS_COMMAND:
- case CSR_UNEXP | PHS_COMMAND:
- case CSR_SRV_REQ | PHS_COMMAND:
- DB(DB_INTR, printk("CMND-%02x", cmd->cmnd[0]))
- transfer_pio(cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata);
- hostdata->state = S_CONNECTED;
- break;
-
-
- case CSR_XFER_DONE | PHS_STATUS:
- case CSR_UNEXP | PHS_STATUS:
- case CSR_SRV_REQ | PHS_STATUS:
- DB(DB_INTR, printk("STATUS="))
-
- cmd->SCp.Status = read_1_byte(hostdata);
- DB(DB_INTR, printk("%02x", cmd->SCp.Status))
- if (hostdata->level2 >= L2_BASIC) {
- sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */
- hostdata->state = S_RUNNING_LEVEL2;
- write_3393(hostdata, WD_COMMAND_PHASE, 0x50);
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
- } else {
- hostdata->state = S_CONNECTED;
- }
- break;
-
-
- case CSR_XFER_DONE | PHS_MESS_IN:
- case CSR_UNEXP | PHS_MESS_IN:
- case CSR_SRV_REQ | PHS_MESS_IN:
- DB(DB_INTR, printk("MSG_IN="))
-
- msg = read_1_byte(hostdata);
- sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */
-
- hostdata->incoming_msg[hostdata->incoming_ptr] = msg;
- if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE)
- msg = EXTENDED_MESSAGE;
- else
- hostdata->incoming_ptr = 0;
-
- cmd->SCp.Message = msg;
- switch (msg) {
-
- case COMMAND_COMPLETE:
- DB(DB_INTR, printk("CCMP"))
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_PRE_CMP_DISC;
- break;
-
- case SAVE_POINTERS:
- DB(DB_INTR, printk("SDP"))
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- break;
-
- case RESTORE_POINTERS:
- DB(DB_INTR, printk("RDP"))
- if (hostdata->level2 >= L2_BASIC) {
- write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
- hostdata->state = S_RUNNING_LEVEL2;
- } else {
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- }
- break;
-
- case DISCONNECT:
- DB(DB_INTR, printk("DIS"))
- cmd->device->disconnect = 1;
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_PRE_TMP_DISC;
- break;
-
- case MESSAGE_REJECT:
- DB(DB_INTR, printk("REJ"))
-#ifdef SYNC_DEBUG
- printk("-REJ-");
-#endif
- if (hostdata->sync_stat[cmd->device->id] == SS_WAITING)
- hostdata->sync_stat[cmd->device->id] = SS_SET;
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- break;
-
- case EXTENDED_MESSAGE:
- DB(DB_INTR, printk("EXT"))
-
- ucp = hostdata->incoming_msg;
-
-#ifdef SYNC_DEBUG
- printk("%02x", ucp[hostdata->incoming_ptr]);
-#endif
- /* Is this the last byte of the extended message? */
-
- if ((hostdata->incoming_ptr >= 2) && (hostdata->incoming_ptr == (ucp[1] + 1))) {
-
- switch (ucp[2]) { /* what's the EXTENDED code? */
- case EXTENDED_SDTR:
- id = calc_sync_xfer(ucp[3], ucp[4]);
- if (hostdata->sync_stat[cmd->device->id] != SS_WAITING) {
-
-/* A device has sent an unsolicited SDTR message; rather than go
- * through the effort of decoding it and then figuring out what
- * our reply should be, we're just gonna say that we have a
- * synchronous fifo depth of 0. This will result in asynchronous
- * transfers - not ideal but so much easier.
- * Actually, this is OK because it assures us that if we don't
- * specifically ask for sync transfers, we won't do any.
- */
-
- write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
- hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
- hostdata->outgoing_msg[1] = 3;
- hostdata->outgoing_msg[2] = EXTENDED_SDTR;
- hostdata->outgoing_msg[3] = hostdata->default_sx_per / 4;
- hostdata->outgoing_msg[4] = 0;
- hostdata->outgoing_len = 5;
- hostdata->sync_xfer[cmd->device->id] = calc_sync_xfer(hostdata->default_sx_per / 4, 0);
- } else {
- hostdata->sync_xfer[cmd->device->id] = id;
- }
-#ifdef SYNC_DEBUG
- printk("sync_xfer=%02x", hostdata->sync_xfer[cmd->device->id]);
-#endif
- hostdata->sync_stat[cmd->device->id] = SS_SET;
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- break;
- case EXTENDED_WDTR:
- write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
- printk("sending WDTR ");
- hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
- hostdata->outgoing_msg[1] = 2;
- hostdata->outgoing_msg[2] = EXTENDED_WDTR;
- hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */
- hostdata->outgoing_len = 4;
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- break;
- default:
- write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
- printk("Rejecting Unknown Extended Message(%02x). ", ucp[2]);
- hostdata->outgoing_msg[0] = MESSAGE_REJECT;
- hostdata->outgoing_len = 1;
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- break;
- }
- hostdata->incoming_ptr = 0;
- }
-
- /* We need to read more MESS_IN bytes for the extended message */
-
- else {
- hostdata->incoming_ptr++;
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- }
- break;
-
- default:
- printk("Rejecting Unknown Message(%02x) ", msg);
- write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
- hostdata->outgoing_msg[0] = MESSAGE_REJECT;
- hostdata->outgoing_len = 1;
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- hostdata->state = S_CONNECTED;
- }
- break;
-
-
-/* Note: this interrupt will occur only after a LEVEL2 command */
-
- case CSR_SEL_XFER_DONE:
-
-/* Make sure that reselection is enabled at this point - it may
- * have been turned off for the command that just completed.
- */
-
- write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
- if (phs == 0x60) {
- DB(DB_INTR, printk("SX-DONE"))
- cmd->SCp.Message = COMMAND_COMPLETE;
- lun = read_3393(hostdata, WD_TARGET_LUN);
- DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun))
- hostdata->connected = NULL;
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- hostdata->state = S_UNCONNECTED;
- if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
- cmd->SCp.Status = lun;
- if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
- else
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- cmd->scsi_done(cmd);
-
-/* We are no longer connected to a target - check to see if
- * there are commands waiting to be executed.
- */
-
- in2000_execute(instance);
- } else {
- printk("%02x:%02x:%02x: Unknown SEL_XFER_DONE phase!!---", asr, sr, phs);
- }
- break;
-
-
-/* Note: this interrupt will occur only after a LEVEL2 command */
-
- case CSR_SDP:
- DB(DB_INTR, printk("SDP"))
- hostdata->state = S_RUNNING_LEVEL2;
- write_3393(hostdata, WD_COMMAND_PHASE, 0x41);
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
- break;
-
-
- case CSR_XFER_DONE | PHS_MESS_OUT:
- case CSR_UNEXP | PHS_MESS_OUT:
- case CSR_SRV_REQ | PHS_MESS_OUT:
- DB(DB_INTR, printk("MSG_OUT="))
-
-/* To get here, we've probably requested MESSAGE_OUT and have
- * already put the correct bytes in outgoing_msg[] and filled
- * in outgoing_len. We simply send them out to the SCSI bus.
- * Sometimes we get MESSAGE_OUT phase when we're not expecting
- * it - like when our SDTR message is rejected by a target. Some
- * targets send the REJECT before receiving all of the extended
- * message, and then seem to go back to MESSAGE_OUT for a byte
- * or two. Not sure why, or if I'm doing something wrong to
- * cause this to happen. Regardless, it seems that sending
- * NOP messages in these situations results in no harm and
- * makes everyone happy.
- */
- if (hostdata->outgoing_len == 0) {
- hostdata->outgoing_len = 1;
- hostdata->outgoing_msg[0] = NOP;
- }
- transfer_pio(hostdata->outgoing_msg, hostdata->outgoing_len, DATA_OUT_DIR, hostdata);
- DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0]))
- hostdata->outgoing_len = 0;
- hostdata->state = S_CONNECTED;
- break;
-
-
- case CSR_UNEXP_DISC:
-
-/* I think I've seen this after a request-sense that was in response
- * to an error condition, but not sure. We certainly need to do
- * something when we get this interrupt - the question is 'what?'.
- * Let's think positively, and assume some command has finished
- * in a legal manner (like a command that provokes a request-sense),
- * so we treat it as a normal command-complete-disconnect.
- */
-
-
-/* Make sure that reselection is enabled at this point - it may
- * have been turned off for the command that just completed.
- */
-
- write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
- if (cmd == NULL) {
- printk(" - Already disconnected! ");
- hostdata->state = S_UNCONNECTED;
-
-/* release the SMP spin_lock and restore irq state */
- spin_unlock_irqrestore(instance->host_lock, flags);
- return IRQ_HANDLED;
- }
- DB(DB_INTR, printk("UNEXP_DISC"))
- hostdata->connected = NULL;
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- hostdata->state = S_UNCONNECTED;
- if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
- else
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- cmd->scsi_done(cmd);
-
-/* We are no longer connected to a target - check to see if
- * there are commands waiting to be executed.
- */
-
- in2000_execute(instance);
- break;
-
-
- case CSR_DISC:
-
-/* Make sure that reselection is enabled at this point - it may
- * have been turned off for the command that just completed.
- */
-
- write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
- DB(DB_INTR, printk("DISC"))
- if (cmd == NULL) {
- printk(" - Already disconnected! ");
- hostdata->state = S_UNCONNECTED;
- }
- switch (hostdata->state) {
- case S_PRE_CMP_DISC:
- hostdata->connected = NULL;
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- hostdata->state = S_UNCONNECTED;
- DB(DB_INTR, printk(":%d", cmd->SCp.Status))
- if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
- else
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- cmd->scsi_done(cmd);
- break;
- case S_PRE_TMP_DISC:
- case S_RUNNING_LEVEL2:
- cmd->host_scribble = (uchar *) hostdata->disconnected_Q;
- hostdata->disconnected_Q = cmd;
- hostdata->connected = NULL;
- hostdata->state = S_UNCONNECTED;
-
-#ifdef PROC_STATISTICS
- hostdata->disc_done_cnt[cmd->device->id]++;
-#endif
-
- break;
- default:
- printk("*** Unexpected DISCONNECT interrupt! ***");
- hostdata->state = S_UNCONNECTED;
- }
-
-/* We are no longer connected to a target - check to see if
- * there are commands waiting to be executed.
- */
-
- in2000_execute(instance);
- break;
-
-
- case CSR_RESEL_AM:
- DB(DB_INTR, printk("RESEL"))
-
- /* First we have to make sure this reselection didn't */
- /* happen during Arbitration/Selection of some other device. */
- /* If yes, put losing command back on top of input_Q. */
- if (hostdata->level2 <= L2_NONE) {
-
- if (hostdata->selecting) {
- cmd = (Scsi_Cmnd *) hostdata->selecting;
- hostdata->selecting = NULL;
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- cmd->host_scribble = (uchar *) hostdata->input_Q;
- hostdata->input_Q = cmd;
- }
- }
-
- else {
-
- if (cmd) {
- if (phs == 0x00) {
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- cmd->host_scribble = (uchar *) hostdata->input_Q;
- hostdata->input_Q = cmd;
- } else {
- printk("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---", asr, sr, phs);
- while (1)
- printk("\r");
- }
- }
-
- }
-
- /* OK - find out which device reselected us. */
-
- id = read_3393(hostdata, WD_SOURCE_ID);
- id &= SRCID_MASK;
-
- /* and extract the lun from the ID message. (Note that we don't
- * bother to check for a valid message here - I guess this is
- * not the right way to go, but....)
- */
-
- lun = read_3393(hostdata, WD_DATA);
- if (hostdata->level2 < L2_RESELECT)
- write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
- lun &= 7;
-
- /* Now we look for the command that's reconnecting. */
-
- cmd = (Scsi_Cmnd *) hostdata->disconnected_Q;
- patch = NULL;
- while (cmd) {
- if (id == cmd->device->id && lun == cmd->device->lun)
- break;
- patch = cmd;
- cmd = (Scsi_Cmnd *) cmd->host_scribble;
- }
-
- /* Hmm. Couldn't find a valid command.... What to do? */
-
- if (!cmd) {
- printk("---TROUBLE: target %d.%d not in disconnect queue---", id, lun);
- break;
- }
-
- /* Ok, found the command - now start it up again. */
-
- if (patch)
- patch->host_scribble = cmd->host_scribble;
- else
- hostdata->disconnected_Q = (Scsi_Cmnd *) cmd->host_scribble;
- hostdata->connected = cmd;
-
- /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'
- * because these things are preserved over a disconnect.
- * But we DO need to fix the DPD bit so it's correct for this command.
- */
-
- if (is_dir_out(cmd))
- write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id);
- else
- write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
- if (hostdata->level2 >= L2_RESELECT) {
- write_3393_count(hostdata, 0); /* we want a DATA_PHASE interrupt */
- write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
- write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
- hostdata->state = S_RUNNING_LEVEL2;
- } else
- hostdata->state = S_CONNECTED;
-
- break;
-
- default:
- printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs);
- }
-
- write1_io(0, IO_LED_OFF);
-
- DB(DB_INTR, printk("} "))
-
-/* release the SMP spin_lock and restore irq state */
- spin_unlock_irqrestore(instance->host_lock, flags);
- return IRQ_HANDLED;
-}
-
-
-
-#define RESET_CARD 0
-#define RESET_CARD_AND_BUS 1
-#define B_FLAG 0x80
-
-/*
- * Caller must hold instance lock!
- */
-
-static int reset_hardware(struct Scsi_Host *instance, int type)
-{
- struct IN2000_hostdata *hostdata;
- int qt, x;
-
- hostdata = (struct IN2000_hostdata *) instance->hostdata;
-
- write1_io(0, IO_LED_ON);
- if (type == RESET_CARD_AND_BUS) {
- write1_io(0, IO_CARD_RESET);
- x = read1_io(IO_HARDWARE);
- }
- x = read_3393(hostdata, WD_SCSI_STATUS); /* clear any WD intrpt */
- write_3393(hostdata, WD_OWN_ID, instance->this_id | OWNID_EAF | OWNID_RAF | OWNID_FS_8);
- write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
- write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, calc_sync_xfer(hostdata->default_sx_per / 4, DEFAULT_SX_OFF));
-
- write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */
- write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */
- write_3393(hostdata, WD_COMMAND, WD_CMD_RESET);
- /* FIXME: timeout ?? */
- while (!(READ_AUX_STAT() & ASR_INT))
- cpu_relax(); /* wait for RESET to complete */
-
- x = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */
-
- write_3393(hostdata, WD_QUEUE_TAG, 0xa5); /* any random number */
- qt = read_3393(hostdata, WD_QUEUE_TAG);
- if (qt == 0xa5) {
- x |= B_FLAG;
- write_3393(hostdata, WD_QUEUE_TAG, 0);
- }
- write_3393(hostdata, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
- write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
- write1_io(0, IO_LED_OFF);
- return x;
-}
-
-
-
-static int in2000_bus_reset(Scsi_Cmnd * cmd)
-{
- struct Scsi_Host *instance;
- struct IN2000_hostdata *hostdata;
- int x;
- unsigned long flags;
-
- instance = cmd->device->host;
- hostdata = (struct IN2000_hostdata *) instance->hostdata;
-
- printk(KERN_WARNING "scsi%d: Reset. ", instance->host_no);
-
- spin_lock_irqsave(instance->host_lock, flags);
-
- /* do scsi-reset here */
- reset_hardware(instance, RESET_CARD_AND_BUS);
- for (x = 0; x < 8; x++) {
- hostdata->busy[x] = 0;
- hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
- hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */
- }
- hostdata->input_Q = NULL;
- hostdata->selecting = NULL;
- hostdata->connected = NULL;
- hostdata->disconnected_Q = NULL;
- hostdata->state = S_UNCONNECTED;
- hostdata->fifo = FI_FIFO_UNUSED;
- hostdata->incoming_ptr = 0;
- hostdata->outgoing_len = 0;
-
- cmd->result = DID_RESET << 16;
-
- spin_unlock_irqrestore(instance->host_lock, flags);
- return SUCCESS;
-}
-
-static int __in2000_abort(Scsi_Cmnd * cmd)
-{
- struct Scsi_Host *instance;
- struct IN2000_hostdata *hostdata;
- Scsi_Cmnd *tmp, *prev;
- uchar sr, asr;
- unsigned long timeout;
-
- instance = cmd->device->host;
- hostdata = (struct IN2000_hostdata *) instance->hostdata;
-
- printk(KERN_DEBUG "scsi%d: Abort-", instance->host_no);
- printk("(asr=%02x,count=%ld,resid=%d,buf_resid=%d,have_data=%d,FC=%02x)- ", READ_AUX_STAT(), read_3393_count(hostdata), cmd->SCp.this_residual, cmd->SCp.buffers_residual, cmd->SCp.have_data_in, read1_io(IO_FIFO_COUNT));
-
-/*
- * Case 1 : If the command hasn't been issued yet, we simply remove it
- * from the inout_Q.
- */
-
- tmp = (Scsi_Cmnd *) hostdata->input_Q;
- prev = NULL;
- while (tmp) {
- if (tmp == cmd) {
- if (prev)
- prev->host_scribble = cmd->host_scribble;
- cmd->host_scribble = NULL;
- cmd->result = DID_ABORT << 16;
- printk(KERN_WARNING "scsi%d: Abort - removing command from input_Q. ", instance->host_no);
- cmd->scsi_done(cmd);
- return SUCCESS;
- }
- prev = tmp;
- tmp = (Scsi_Cmnd *) tmp->host_scribble;
- }
-
-/*
- * Case 2 : If the command is connected, we're going to fail the abort
- * and let the high level SCSI driver retry at a later time or
- * issue a reset.
- *
- * Timeouts, and therefore aborted commands, will be highly unlikely
- * and handling them cleanly in this situation would make the common
- * case of noresets less efficient, and would pollute our code. So,
- * we fail.
- */
-
- if (hostdata->connected == cmd) {
-
- printk(KERN_WARNING "scsi%d: Aborting connected command - ", instance->host_no);
-
- printk("sending wd33c93 ABORT command - ");
- write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
- write_3393_cmd(hostdata, WD_CMD_ABORT);
-
-/* Now we have to attempt to flush out the FIFO... */
-
- printk("flushing fifo - ");
- timeout = 1000000;
- do {
- asr = READ_AUX_STAT();
- if (asr & ASR_DBR)
- read_3393(hostdata, WD_DATA);
- } while (!(asr & ASR_INT) && timeout-- > 0);
- sr = read_3393(hostdata, WD_SCSI_STATUS);
- printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ", asr, sr, read_3393_count(hostdata), timeout);
-
- /*
- * Abort command processed.
- * Still connected.
- * We must disconnect.
- */
-
- printk("sending wd33c93 DISCONNECT command - ");
- write_3393_cmd(hostdata, WD_CMD_DISCONNECT);
-
- timeout = 1000000;
- asr = READ_AUX_STAT();
- while ((asr & ASR_CIP) && timeout-- > 0)
- asr = READ_AUX_STAT();
- sr = read_3393(hostdata, WD_SCSI_STATUS);
- printk("asr=%02x, sr=%02x.", asr, sr);
-
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
- hostdata->connected = NULL;
- hostdata->state = S_UNCONNECTED;
- cmd->result = DID_ABORT << 16;
- cmd->scsi_done(cmd);
-
- in2000_execute(instance);
-
- return SUCCESS;
- }
-
-/*
- * Case 3: If the command is currently disconnected from the bus,
- * we're not going to expend much effort here: Let's just return
- * an ABORT_SNOOZE and hope for the best...
- */
-
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_Q; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble)
- if (cmd == tmp) {
- printk(KERN_DEBUG "scsi%d: unable to abort disconnected command.\n", instance->host_no);
- return FAILED;
- }
-
-/*
- * Case 4 : If we reached this point, the command was not found in any of
- * the queues.
- *
- * We probably reached this point because of an unlikely race condition
- * between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case something really
- * broke.
- */
-
- in2000_execute(instance);
-
- printk("scsi%d: warning : SCSI command probably completed successfully" " before abortion. ", instance->host_no);
- return SUCCESS;
-}
-
-static int in2000_abort(Scsi_Cmnd * cmd)
-{
- int rc;
-
- spin_lock_irq(cmd->device->host->host_lock);
- rc = __in2000_abort(cmd);
- spin_unlock_irq(cmd->device->host->host_lock);
-
- return rc;
-}
-
-
-#define MAX_IN2000_HOSTS 3
-#define MAX_SETUP_ARGS ARRAY_SIZE(setup_args)
-#define SETUP_BUFFER_SIZE 200
-static char setup_buffer[SETUP_BUFFER_SIZE];
-static char setup_used[MAX_SETUP_ARGS];
-static int done_setup = 0;
-
-static void __init in2000_setup(char *str, int *ints)
-{
- int i;
- char *p1, *p2;
-
- strlcpy(setup_buffer, str, SETUP_BUFFER_SIZE);
- p1 = setup_buffer;
- i = 0;
- while (*p1 && (i < MAX_SETUP_ARGS)) {
- p2 = strchr(p1, ',');
- if (p2) {
- *p2 = '\0';
- if (p1 != p2)
- setup_args[i] = p1;
- p1 = p2 + 1;
- i++;
- } else {
- setup_args[i] = p1;
- break;
- }
- }
- for (i = 0; i < MAX_SETUP_ARGS; i++)
- setup_used[i] = 0;
- done_setup = 1;
-}
-
-
-/* check_setup_args() returns index if key found, 0 if not
- */
-
-static int __init check_setup_args(char *key, int *val, char *buf)
-{
- int x;
- char *cp;
-
- for (x = 0; x < MAX_SETUP_ARGS; x++) {
- if (setup_used[x])
- continue;
- if (!strncmp(setup_args[x], key, strlen(key)))
- break;
- }
- if (x == MAX_SETUP_ARGS)
- return 0;
- setup_used[x] = 1;
- cp = setup_args[x] + strlen(key);
- *val = -1;
- if (*cp != ':')
- return ++x;
- cp++;
- if ((*cp >= '0') && (*cp <= '9')) {
- *val = simple_strtoul(cp, NULL, 0);
- }
- return ++x;
-}
-
-
-
-/* The "correct" (ie portable) way to access memory-mapped hardware
- * such as the IN2000 EPROM and dip switch is through the use of
- * special macros declared in 'asm/io.h'. We use readb() and readl()
- * when reading from the card's BIOS area in in2000_detect().
- */
-static u32 bios_tab[] in2000__INITDATA = {
- 0xc8000,
- 0xd0000,
- 0xd8000,
- 0
-};
-
-static unsigned short base_tab[] in2000__INITDATA = {
- 0x220,
- 0x200,
- 0x110,
- 0x100,
-};
-
-static int int_tab[] in2000__INITDATA = {
- 15,
- 14,
- 11,
- 10
-};
-
-static int probe_bios(u32 addr, u32 *s1, uchar *switches)
-{
- void __iomem *p = ioremap(addr, 0x34);
- if (!p)
- return 0;
- *s1 = readl(p + 0x10);
- if (*s1 == 0x41564f4e || readl(p + 0x30) == 0x61776c41) {
- /* Read the switch image that's mapped into EPROM space */
- *switches = ~readb(p + 0x20);
- iounmap(p);
- return 1;
- }
- iounmap(p);
- return 0;
-}
-
-static int __init in2000_detect(struct scsi_host_template * tpnt)
-{
- struct Scsi_Host *instance;
- struct IN2000_hostdata *hostdata;
- int detect_count;
- int bios;
- int x;
- unsigned short base;
- uchar switches;
- uchar hrev;
- unsigned long flags;
- int val;
- char buf[32];
-
-/* Thanks to help from Bill Earnest, probing for IN2000 cards is a
- * pretty straightforward and fool-proof operation. There are 3
- * possible locations for the IN2000 EPROM in memory space - if we
- * find a BIOS signature, we can read the dip switch settings from
- * the byte at BIOS+32 (shadowed in by logic on the card). From 2
- * of the switch bits we get the card's address in IO space. There's
- * an image of the dip switch there, also, so we have a way to back-
- * check that this really is an IN2000 card. Very nifty. Use the
- * 'ioport:xx' command-line parameter if your BIOS EPROM is absent
- * or disabled.
- */
-
- if (!done_setup && setup_strings)
- in2000_setup(setup_strings, NULL);
-
- detect_count = 0;
- for (bios = 0; bios_tab[bios]; bios++) {
- u32 s1 = 0;
- if (check_setup_args("ioport", &val, buf)) {
- base = val;
- switches = ~inb(base + IO_SWITCHES) & 0xff;
- printk("Forcing IN2000 detection at IOport 0x%x ", base);
- bios = 2;
- }
-/*
- * There have been a couple of BIOS versions with different layouts
- * for the obvious ID strings. We look for the 2 most common ones and
- * hope that they cover all the cases...
- */
- else if (probe_bios(bios_tab[bios], &s1, &switches)) {
- printk("Found IN2000 BIOS at 0x%x ", (unsigned int) bios_tab[bios]);
-
-/* Find out where the IO space is */
-
- x = switches & (SW_ADDR0 | SW_ADDR1);
- base = base_tab[x];
-
-/* Check for the IN2000 signature in IO space. */
-
- x = ~inb(base + IO_SWITCHES) & 0xff;
- if (x != switches) {
- printk("Bad IO signature: %02x vs %02x.\n", x, switches);
- continue;
- }
- } else
- continue;
-
-/* OK. We have a base address for the IO ports - run a few safety checks */
-
- if (!(switches & SW_BIT7)) { /* I _think_ all cards do this */
- printk("There is no IN-2000 SCSI card at IOport 0x%03x!\n", base);
- continue;
- }
-
-/* Let's assume any hardware version will work, although the driver
- * has only been tested on 0x21, 0x22, 0x25, 0x26, and 0x27. We'll
- * print out the rev number for reference later, but accept them all.
- */
-
- hrev = inb(base + IO_HARDWARE);
-
- /* Bit 2 tells us if interrupts are disabled */
- if (switches & SW_DISINT) {
- printk("The IN-2000 SCSI card at IOport 0x%03x ", base);
- printk("is not configured for interrupt operation!\n");
- printk("This driver requires an interrupt: cancelling detection.\n");
- continue;
- }
-
-/* Ok. We accept that there's an IN2000 at ioaddr 'base'. Now
- * initialize it.
- */
-
- tpnt->proc_name = "in2000";
- instance = scsi_register(tpnt, sizeof(struct IN2000_hostdata));
- if (instance == NULL)
- continue;
- detect_count++;
- hostdata = (struct IN2000_hostdata *) instance->hostdata;
- instance->io_port = hostdata->io_base = base;
- hostdata->dip_switch = switches;
- hostdata->hrev = hrev;
-
- write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */
- write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */
- write1_io(0, IO_INTR_MASK); /* allow all ints */
- x = int_tab[(switches & (SW_INT0 | SW_INT1)) >> SW_INT_SHIFT];
- if (request_irq(x, in2000_intr, IRQF_DISABLED, "in2000", instance)) {
- printk("in2000_detect: Unable to allocate IRQ.\n");
- detect_count--;
- continue;
- }
- instance->irq = x;
- instance->n_io_port = 13;
- request_region(base, 13, "in2000"); /* lock in this IO space for our use */
-
- for (x = 0; x < 8; x++) {
- hostdata->busy[x] = 0;
- hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
- hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */
-#ifdef PROC_STATISTICS
- hostdata->cmd_cnt[x] = 0;
- hostdata->disc_allowed_cnt[x] = 0;
- hostdata->disc_done_cnt[x] = 0;
-#endif
- }
- hostdata->input_Q = NULL;
- hostdata->selecting = NULL;
- hostdata->connected = NULL;
- hostdata->disconnected_Q = NULL;
- hostdata->state = S_UNCONNECTED;
- hostdata->fifo = FI_FIFO_UNUSED;
- hostdata->level2 = L2_BASIC;
- hostdata->disconnect = DIS_ADAPTIVE;
- hostdata->args = DEBUG_DEFAULTS;
- hostdata->incoming_ptr = 0;
- hostdata->outgoing_len = 0;
- hostdata->default_sx_per = DEFAULT_SX_PER;
-
-/* Older BIOS's had a 'sync on/off' switch - use its setting */
-
- if (s1 == 0x41564f4e && (switches & SW_SYNC_DOS5))
- hostdata->sync_off = 0x00; /* sync defaults to on */
- else
- hostdata->sync_off = 0xff; /* sync defaults to off */
-
-#ifdef PROC_INTERFACE
- hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | PR_CONNECTED | PR_INPUTQ | PR_DISCQ | PR_STOP;
-#ifdef PROC_STATISTICS
- hostdata->int_cnt = 0;
-#endif
-#endif
-
- if (check_setup_args("nosync", &val, buf))
- hostdata->sync_off = val;
-
- if (check_setup_args("period", &val, buf))
- hostdata->default_sx_per = sx_table[round_period((unsigned int) val)].period_ns;
-
- if (check_setup_args("disconnect", &val, buf)) {
- if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
- hostdata->disconnect = val;
- else
- hostdata->disconnect = DIS_ADAPTIVE;
- }
-
- if (check_setup_args("noreset", &val, buf))
- hostdata->args ^= A_NO_SCSI_RESET;
-
- if (check_setup_args("level2", &val, buf))
- hostdata->level2 = val;
-
- if (check_setup_args("debug", &val, buf))
- hostdata->args = (val & DB_MASK);
-
-#ifdef PROC_INTERFACE
- if (check_setup_args("proc", &val, buf))
- hostdata->proc = val;
-#endif
-
-
- /* FIXME: not strictly needed I think but the called code expects
- to be locked */
- spin_lock_irqsave(instance->host_lock, flags);
- x = reset_hardware(instance, (hostdata->args & A_NO_SCSI_RESET) ? RESET_CARD : RESET_CARD_AND_BUS);
- spin_unlock_irqrestore(instance->host_lock, flags);
-
- hostdata->microcode = read_3393(hostdata, WD_CDB_1);
- if (x & 0x01) {
- if (x & B_FLAG)
- hostdata->chip = C_WD33C93B;
- else
- hostdata->chip = C_WD33C93A;
- } else
- hostdata->chip = C_WD33C93;
-
- printk("dip_switch=%02x irq=%d ioport=%02x floppy=%s sync/DOS5=%s ", (switches & 0x7f), instance->irq, hostdata->io_base, (switches & SW_FLOPPY) ? "Yes" : "No", (switches & SW_SYNC_DOS5) ? "Yes" : "No");
- printk("hardware_ver=%02x chip=%s microcode=%02x\n", hrev, (hostdata->chip == C_WD33C93) ? "WD33c93" : (hostdata->chip == C_WD33C93A) ? "WD33c93A" : (hostdata->chip == C_WD33C93B) ? "WD33c93B" : "unknown", hostdata->microcode);
-#ifdef DEBUGGING_ON
- printk("setup_args = ");
- for (x = 0; x < MAX_SETUP_ARGS; x++)
- printk("%s,", setup_args[x]);
- printk("\n");
-#endif
- if (hostdata->sync_off == 0xff)
- printk("Sync-transfer DISABLED on all devices: ENABLE from command-line\n");
- printk("IN2000 driver version %s - %s\n", IN2000_VERSION, IN2000_DATE);
- }
-
- return detect_count;
-}
-
-static int in2000_release(struct Scsi_Host *shost)
-{
- if (shost->irq)
- free_irq(shost->irq, shost);
- if (shost->io_port && shost->n_io_port)
- release_region(shost->io_port, shost->n_io_port);
- return 0;
-}
-
-/* NOTE: I lifted this function straight out of the old driver,
- * and have not tested it. Presumably it does what it's
- * supposed to do...
- */
-
-static int in2000_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *iinfo)
-{
- int size;
-
- size = capacity;
- iinfo[0] = 64;
- iinfo[1] = 32;
- iinfo[2] = size >> 11;
-
-/* This should approximate the large drive handling that the DOS ASPI manager
- uses. Drives very near the boundaries may not be handled correctly (i.e.
- near 2.0 Gb and 4.0 Gb) */
-
- if (iinfo[2] > 1024) {
- iinfo[0] = 64;
- iinfo[1] = 63;
- iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]);
- }
- if (iinfo[2] > 1024) {
- iinfo[0] = 128;
- iinfo[1] = 63;
- iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]);
- }
- if (iinfo[2] > 1024) {
- iinfo[0] = 255;
- iinfo[1] = 63;
- iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]);
- }
- return 0;
-}
-
-
-static int in2000_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off, int len, int in)
-{
-
-#ifdef PROC_INTERFACE
-
- char *bp;
- char tbuf[128];
- unsigned long flags;
- struct IN2000_hostdata *hd;
- Scsi_Cmnd *cmd;
- int x, i;
- static int stop = 0;
-
- hd = (struct IN2000_hostdata *) instance->hostdata;
-
-/* If 'in' is TRUE we need to _read_ the proc file. We accept the following
- * keywords (same format as command-line, but only ONE per read):
- * debug
- * disconnect
- * period
- * resync
- * proc
- */
-
- if (in) {
- buf[len] = '\0';
- bp = buf;
- if (!strncmp(bp, "debug:", 6)) {
- bp += 6;
- hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK;
- } else if (!strncmp(bp, "disconnect:", 11)) {
- bp += 11;
- x = simple_strtoul(bp, NULL, 0);
- if (x < DIS_NEVER || x > DIS_ALWAYS)
- x = DIS_ADAPTIVE;
- hd->disconnect = x;
- } else if (!strncmp(bp, "period:", 7)) {
- bp += 7;
- x = simple_strtoul(bp, NULL, 0);
- hd->default_sx_per = sx_table[round_period((unsigned int) x)].period_ns;
- } else if (!strncmp(bp, "resync:", 7)) {
- bp += 7;
- x = simple_strtoul(bp, NULL, 0);
- for (i = 0; i < 7; i++)
- if (x & (1 << i))
- hd->sync_stat[i] = SS_UNSET;
- } else if (!strncmp(bp, "proc:", 5)) {
- bp += 5;
- hd->proc = simple_strtoul(bp, NULL, 0);
- } else if (!strncmp(bp, "level2:", 7)) {
- bp += 7;
- hd->level2 = simple_strtoul(bp, NULL, 0);
- }
- return len;
- }
-
- spin_lock_irqsave(instance->host_lock, flags);
- bp = buf;
- *bp = '\0';
- if (hd->proc & PR_VERSION) {
- sprintf(tbuf, "\nVersion %s - %s.", IN2000_VERSION, IN2000_DATE);
- strcat(bp, tbuf);
- }
- if (hd->proc & PR_INFO) {
- sprintf(tbuf, "\ndip_switch=%02x: irq=%d io=%02x floppy=%s sync/DOS5=%s", (hd->dip_switch & 0x7f), instance->irq, hd->io_base, (hd->dip_switch & 0x40) ? "Yes" : "No", (hd->dip_switch & 0x20) ? "Yes" : "No");
- strcat(bp, tbuf);
- strcat(bp, "\nsync_xfer[] = ");
- for (x = 0; x < 7; x++) {
- sprintf(tbuf, "\t%02x", hd->sync_xfer[x]);
- strcat(bp, tbuf);
- }
- strcat(bp, "\nsync_stat[] = ");
- for (x = 0; x < 7; x++) {
- sprintf(tbuf, "\t%02x", hd->sync_stat[x]);
- strcat(bp, tbuf);
- }
- }
-#ifdef PROC_STATISTICS
- if (hd->proc & PR_STATISTICS) {
- strcat(bp, "\ncommands issued: ");
- for (x = 0; x < 7; x++) {
- sprintf(tbuf, "\t%ld", hd->cmd_cnt[x]);
- strcat(bp, tbuf);
- }
- strcat(bp, "\ndisconnects allowed:");
- for (x = 0; x < 7; x++) {
- sprintf(tbuf, "\t%ld", hd->disc_allowed_cnt[x]);
- strcat(bp, tbuf);
- }
- strcat(bp, "\ndisconnects done: ");
- for (x = 0; x < 7; x++) {
- sprintf(tbuf, "\t%ld", hd->disc_done_cnt[x]);
- strcat(bp, tbuf);
- }
- sprintf(tbuf, "\ninterrupts: \t%ld", hd->int_cnt);
- strcat(bp, tbuf);
- }
-#endif
- if (hd->proc & PR_CONNECTED) {
- strcat(bp, "\nconnected: ");
- if (hd->connected) {
- cmd = (Scsi_Cmnd *) hd->connected;
- sprintf(tbuf, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
- strcat(bp, tbuf);
- }
- }
- if (hd->proc & PR_INPUTQ) {
- strcat(bp, "\ninput_Q: ");
- cmd = (Scsi_Cmnd *) hd->input_Q;
- while (cmd) {
- sprintf(tbuf, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
- strcat(bp, tbuf);
- cmd = (Scsi_Cmnd *) cmd->host_scribble;
- }
- }
- if (hd->proc & PR_DISCQ) {
- strcat(bp, "\ndisconnected_Q:");
- cmd = (Scsi_Cmnd *) hd->disconnected_Q;
- while (cmd) {
- sprintf(tbuf, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
- strcat(bp, tbuf);
- cmd = (Scsi_Cmnd *) cmd->host_scribble;
- }
- }
- if (hd->proc & PR_TEST) {
- ; /* insert your own custom function here */
- }
- strcat(bp, "\n");
- spin_unlock_irqrestore(instance->host_lock, flags);
- *start = buf;
- if (stop) {
- stop = 0;
- return 0; /* return 0 to signal end-of-file */
- }
- if (off > 0x40000) /* ALWAYS stop after 256k bytes have been read */
- stop = 1;
- if (hd->proc & PR_STOP) /* stop every other time */
- stop = 1;
- return strlen(bp);
-
-#else /* PROC_INTERFACE */
-
- return 0;
-
-#endif /* PROC_INTERFACE */
-
-}
-
-MODULE_LICENSE("GPL");
-
-
-static struct scsi_host_template driver_template = {
- .proc_name = "in2000",
- .proc_info = in2000_proc_info,
- .name = "Always IN2000",
- .detect = in2000_detect,
- .release = in2000_release,
- .queuecommand = in2000_queuecommand,
- .eh_abort_handler = in2000_abort,
- .eh_bus_reset_handler = in2000_bus_reset,
- .bios_param = in2000_biosparam,
- .can_queue = IN2000_CAN_Q,
- .this_id = IN2000_HOST_ID,
- .sg_tablesize = IN2000_SG,
- .cmd_per_lun = IN2000_CPL,
- .use_clustering = DISABLE_CLUSTERING,
-};
-#include "scsi_module.c"
diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h
deleted file mode 100644
index 5821e1fbce08..000000000000
--- a/drivers/scsi/in2000.h
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * in2000.h - Linux device driver definitions for the
- * Always IN2000 ISA SCSI card.
- *
- * IMPORTANT: This file is for version 1.33 - 26/Aug/1998
- *
- * Copyright (c) 1996 John Shifflett, GeoLog Consulting
- * john@geolog.com
- * jshiffle@netcom.com
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef IN2000_H
-#define IN2000_H
-
-#include <asm/io.h>
-
-#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */
-#ifdef PROC_INTERFACE
-#define PROC_STATISTICS /* add code for keeping various real time stats */
-#endif
-
-#define SYNC_DEBUG /* extra info on sync negotiation printed */
-#define DEBUGGING_ON /* enable command-line debugging bitmask */
-#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */
-
-#ifdef __i386__
-#define FAST_READ_IO /* No problems with these on my machine */
-#define FAST_WRITE_IO
-#endif
-
-#ifdef DEBUGGING_ON
-#define DB(f,a) if (hostdata->args & (f)) a;
-#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
-#else
-#define DB(f,a)
-#define CHECK_NULL(p,s)
-#endif
-
-#define uchar unsigned char
-
-#define read1_io(a) (inb(hostdata->io_base+(a)))
-#define read2_io(a) (inw(hostdata->io_base+(a)))
-#define write1_io(b,a) (outb((b),hostdata->io_base+(a)))
-#define write2_io(w,a) (outw((w),hostdata->io_base+(a)))
-
-#ifdef __i386__
-/* These inline assembly defines are derived from a patch
- * sent to me by Bill Earnest. He's done a lot of very
- * valuable thinking, testing, and coding during his effort
- * to squeeze more speed out of this driver. I really think
- * that we are doing IO at close to the maximum now with
- * the fifo. (And yes, insw uses 'edi' while outsw uses
- * 'esi'. Thanks Bill!)
- */
-
-#define FAST_READ2_IO() \
-({ \
-int __dummy_1,__dummy_2; \
- __asm__ __volatile__ ("\n \
- cld \n \
- orl %%ecx, %%ecx \n \
- jz 1f \n \
- rep \n \
- insw (%%dx),%%es:(%%edi) \n \
-1: " \
- : "=D" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2) /* output */ \
- : "2" (f), "0" (sp), "1" (i) /* input */ \
- ); /* trashed */ \
-})
-
-#define FAST_WRITE2_IO() \
-({ \
-int __dummy_1,__dummy_2; \
- __asm__ __volatile__ ("\n \
- cld \n \
- orl %%ecx, %%ecx \n \
- jz 1f \n \
- rep \n \
- outsw %%ds:(%%esi),(%%dx) \n \
-1: " \
- : "=S" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2)/* output */ \
- : "2" (f), "0" (sp), "1" (i) /* input */ \
- ); /* trashed */ \
-})
-#endif
-
-/* IN2000 io_port offsets */
-#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */
-#define ASR_INT 0x80
-#define ASR_LCI 0x40
-#define ASR_BSY 0x20
-#define ASR_CIP 0x10
-#define ASR_PE 0x02
-#define ASR_DBR 0x01
-#define IO_WD_ADDR 0x00 /* W - 3393 address reg */
-#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */
-#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */
-#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */
-#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */
-#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */
-#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */
-#define IO_FIFO_READ 0x07 /* W - start fifo read */
-#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */
-#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */
-#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */
-#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */
-#define SW_DISINT 0x04 /* bit 2 true if ints disabled */
-#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */
-#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */
-#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */
-#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */
-#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */
-#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */
-#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */
-#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */
-#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */
-#define IMASK_WD 0x01 /* WD33c93 interrupt mask */
-#define IMASK_FIFO 0x02 /* FIFO interrupt mask */
-
-/* wd register names */
-#define WD_OWN_ID 0x00
-#define WD_CONTROL 0x01
-#define WD_TIMEOUT_PERIOD 0x02
-#define WD_CDB_1 0x03
-#define WD_CDB_2 0x04
-#define WD_CDB_3 0x05
-#define WD_CDB_4 0x06
-#define WD_CDB_5 0x07
-#define WD_CDB_6 0x08
-#define WD_CDB_7 0x09
-#define WD_CDB_8 0x0a
-#define WD_CDB_9 0x0b
-#define WD_CDB_10 0x0c
-#define WD_CDB_11 0x0d
-#define WD_CDB_12 0x0e
-#define WD_TARGET_LUN 0x0f
-#define WD_COMMAND_PHASE 0x10
-#define WD_SYNCHRONOUS_TRANSFER 0x11
-#define WD_TRANSFER_COUNT_MSB 0x12
-#define WD_TRANSFER_COUNT 0x13
-#define WD_TRANSFER_COUNT_LSB 0x14
-#define WD_DESTINATION_ID 0x15
-#define WD_SOURCE_ID 0x16
-#define WD_SCSI_STATUS 0x17
-#define WD_COMMAND 0x18
-#define WD_DATA 0x19
-#define WD_QUEUE_TAG 0x1a
-#define WD_AUXILIARY_STATUS 0x1f
-
-/* WD commands */
-#define WD_CMD_RESET 0x00
-#define WD_CMD_ABORT 0x01
-#define WD_CMD_ASSERT_ATN 0x02
-#define WD_CMD_NEGATE_ACK 0x03
-#define WD_CMD_DISCONNECT 0x04
-#define WD_CMD_RESELECT 0x05
-#define WD_CMD_SEL_ATN 0x06
-#define WD_CMD_SEL 0x07
-#define WD_CMD_SEL_ATN_XFER 0x08
-#define WD_CMD_SEL_XFER 0x09
-#define WD_CMD_RESEL_RECEIVE 0x0a
-#define WD_CMD_RESEL_SEND 0x0b
-#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
-#define WD_CMD_TRANS_ADDR 0x18
-#define WD_CMD_TRANS_INFO 0x20
-#define WD_CMD_TRANSFER_PAD 0x21
-#define WD_CMD_SBT_MODE 0x80
-
-/* SCSI Bus Phases */
-#define PHS_DATA_OUT 0x00
-#define PHS_DATA_IN 0x01
-#define PHS_COMMAND 0x02
-#define PHS_STATUS 0x03
-#define PHS_MESS_OUT 0x06
-#define PHS_MESS_IN 0x07
-
-/* Command Status Register definitions */
-
- /* reset state interrupts */
-#define CSR_RESET 0x00
-#define CSR_RESET_AF 0x01
-
- /* successful completion interrupts */
-#define CSR_RESELECT 0x10
-#define CSR_SELECT 0x11
-#define CSR_SEL_XFER_DONE 0x16
-#define CSR_XFER_DONE 0x18
-
- /* paused or aborted interrupts */
-#define CSR_MSGIN 0x20
-#define CSR_SDP 0x21
-#define CSR_SEL_ABORT 0x22
-#define CSR_RESEL_ABORT 0x25
-#define CSR_RESEL_ABORT_AM 0x27
-#define CSR_ABORT 0x28
-
- /* terminated interrupts */
-#define CSR_INVALID 0x40
-#define CSR_UNEXP_DISC 0x41
-#define CSR_TIMEOUT 0x42
-#define CSR_PARITY 0x43
-#define CSR_PARITY_ATN 0x44
-#define CSR_BAD_STATUS 0x45
-#define CSR_UNEXP 0x48
-
- /* service required interrupts */
-#define CSR_RESEL 0x80
-#define CSR_RESEL_AM 0x81
-#define CSR_DISC 0x85
-#define CSR_SRV_REQ 0x88
-
- /* Own ID/CDB Size register */
-#define OWNID_EAF 0x08
-#define OWNID_EHP 0x10
-#define OWNID_RAF 0x20
-#define OWNID_FS_8 0x00
-#define OWNID_FS_12 0x40
-#define OWNID_FS_16 0x80
-
- /* Control register */
-#define CTRL_HSP 0x01
-#define CTRL_HA 0x02
-#define CTRL_IDI 0x04
-#define CTRL_EDI 0x08
-#define CTRL_HHP 0x10
-#define CTRL_POLLED 0x00
-#define CTRL_BURST 0x20
-#define CTRL_BUS 0x40
-#define CTRL_DMA 0x80
-
- /* Timeout Period register */
-#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */
-
- /* Synchronous Transfer Register */
-#define STR_FSS 0x80
-
- /* Destination ID register */
-#define DSTID_DPD 0x40
-#define DATA_OUT_DIR 0
-#define DATA_IN_DIR 1
-#define DSTID_SCC 0x80
-
- /* Source ID register */
-#define SRCID_MASK 0x07
-#define SRCID_SIV 0x08
-#define SRCID_DSP 0x20
-#define SRCID_ES 0x40
-#define SRCID_ER 0x80
-
-
-
-#define ILLEGAL_STATUS_BYTE 0xff
-
-
-#define DEFAULT_SX_PER 500 /* (ns) fairly safe */
-#define DEFAULT_SX_OFF 0 /* aka async */
-
-#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */
-#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */
-
-struct sx_period {
- unsigned int period_ns;
- uchar reg_value;
- };
-
-
-struct IN2000_hostdata {
- struct Scsi_Host *next;
- uchar chip; /* what kind of wd33c93 chip? */
- uchar microcode; /* microcode rev if 'B' */
- unsigned short io_base; /* IO port base */
- unsigned int dip_switch; /* dip switch settings */
- unsigned int hrev; /* hardware revision of card */
- volatile uchar busy[8]; /* index = target, bit = lun */
- volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */
- volatile Scsi_Cmnd *selecting; /* trying to select this command */
- volatile Scsi_Cmnd *connected; /* currently connected command */
- volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */
- uchar state; /* what we are currently doing */
- uchar fifo; /* what the FIFO is up to */
- uchar level2; /* extent to which Level-2 commands are used */
- uchar disconnect; /* disconnect/reselect policy */
- unsigned int args; /* set from command-line argument */
- uchar incoming_msg[8]; /* filled during message_in phase */
- int incoming_ptr; /* mainly used with EXTENDED messages */
- uchar outgoing_msg[8]; /* send this during next message_out */
- int outgoing_len; /* length of outgoing message */
- unsigned int default_sx_per; /* default transfer period for SCSI bus */
- uchar sync_xfer[8]; /* sync_xfer reg settings per target */
- uchar sync_stat[8]; /* status of sync negotiation per target */
- uchar sync_off; /* bit mask: don't use sync with these targets */
-#ifdef PROC_INTERFACE
- uchar proc; /* bit mask: what's in proc output */
-#ifdef PROC_STATISTICS
- unsigned long cmd_cnt[8]; /* # of commands issued per target */
- unsigned long int_cnt; /* # of interrupts serviced */
- unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */
- unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/
-#endif
-#endif
- };
-
-
-/* defines for hostdata->chip */
-
-#define C_WD33C93 0
-#define C_WD33C93A 1
-#define C_WD33C93B 2
-#define C_UNKNOWN_CHIP 100
-
-/* defines for hostdata->state */
-
-#define S_UNCONNECTED 0
-#define S_SELECTING 1
-#define S_RUNNING_LEVEL2 2
-#define S_CONNECTED 3
-#define S_PRE_TMP_DISC 4
-#define S_PRE_CMP_DISC 5
-
-/* defines for hostdata->fifo */
-
-#define FI_FIFO_UNUSED 0
-#define FI_FIFO_READING 1
-#define FI_FIFO_WRITING 2
-
-/* defines for hostdata->level2 */
-/* NOTE: only the first 3 are trustworthy at this point -
- * having trouble when more than 1 device is reading/writing
- * at the same time...
- */
-
-#define L2_NONE 0 /* no combination commands - we get lots of ints */
-#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */
-#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */
-#define L2_DATA 3 /* resume after DATA_IN/OUT ints */
-#define L2_MOST 4 /* resume after anything except a RESELECT int */
-#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */
-#define L2_ALL 6 /* always resume */
-
-/* defines for hostdata->disconnect */
-
-#define DIS_NEVER 0
-#define DIS_ADAPTIVE 1
-#define DIS_ALWAYS 2
-
-/* defines for hostdata->args */
-
-#define DB_TEST 1<<0
-#define DB_FIFO 1<<1
-#define DB_QUEUE_COMMAND 1<<2
-#define DB_EXECUTE 1<<3
-#define DB_INTR 1<<4
-#define DB_TRANSFER 1<<5
-#define DB_MASK 0x3f
-
-#define A_NO_SCSI_RESET 1<<15
-
-
-/* defines for hostdata->sync_xfer[] */
-
-#define SS_UNSET 0
-#define SS_FIRST 1
-#define SS_WAITING 2
-#define SS_SET 3
-
-/* defines for hostdata->proc */
-
-#define PR_VERSION 1<<0
-#define PR_INFO 1<<1
-#define PR_STATISTICS 1<<2
-#define PR_CONNECTED 1<<3
-#define PR_INPUTQ 1<<4
-#define PR_DISCQ 1<<5
-#define PR_TEST 1<<6
-#define PR_STOP 1<<7
-
-
-# include <linux/init.h>
-# include <linux/spinlock.h>
-# define in2000__INITFUNC(function) __initfunc(function)
-# define in2000__INIT __init
-# define in2000__INITDATA __initdata
-# define CLISPIN_LOCK(host,flags) spin_lock_irqsave(host->host_lock, flags)
-# define CLISPIN_UNLOCK(host,flags) spin_unlock_irqrestore(host->host_lock, \
- flags)
-
-static int in2000_detect(struct scsi_host_template *) in2000__INIT;
-static int in2000_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
-static int in2000_abort(Scsi_Cmnd *);
-static void in2000_setup(char *, int *) in2000__INIT;
-static int in2000_biosparam(struct scsi_device *, struct block_device *,
- sector_t, int *);
-static int in2000_bus_reset(Scsi_Cmnd *);
-
-
-#define IN2000_CAN_Q 16
-#define IN2000_SG SG_ALL
-#define IN2000_CPL 2
-#define IN2000_HOST_ID 7
-
-#endif /* IN2000_H */
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index dd741bcd6ccd..ed34ad92c807 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/**************************************************************************
* Initio 9100 device driver for Linux.
*
@@ -6,21 +7,6 @@
* Copyright (c) 2004 Christoph Hellwig <hch@lst.de>
* Copyright (c) 2007 Red Hat
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
*************************************************************************
*
* DESCRIPTION:
@@ -110,11 +96,6 @@
#define i91u_MAXQUEUE 2
#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a"
-#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */
-#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */
-#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */
-#define I920_DEVICE_ID 0x0002 /* Initio's other product ID */
-
#ifdef DEBUG_i91u
static unsigned int i91u_debug = DEBUG_DEFAULT;
#endif
@@ -127,17 +108,6 @@ static int setup_debug = 0;
static void i91uSCBPost(u8 * pHcb, u8 * pScb);
-/* PCI Devices supported by this driver */
-static struct pci_device_id i91u_pci_devices[] = {
- { PCI_VENDOR_ID_INIT, I950_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_INIT, I940_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_INIT, I935_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_INIT, I920_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_DOMEX, I920_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { }
-};
-MODULE_DEVICE_TABLE(pci, i91u_pci_devices);
-
#define DEBUG_INTERRUPT 0
#define DEBUG_QUEUE 0
#define DEBUG_STATE 0
@@ -401,7 +371,6 @@ static u16 initio_se2_rd(unsigned long base, u8 addr)
*/
static void initio_se2_wr(unsigned long base, u8 addr, u16 val)
{
- u8 rb;
u8 instr;
int i;
@@ -430,7 +399,7 @@ static void initio_se2_wr(unsigned long base, u8 addr, u16 val)
udelay(30);
outb(SE2CS, base + TUL_NVRAM); /* -CLK */
udelay(30);
- if ((rb = inb(base + TUL_NVRAM)) & SE2DI)
+ if (inb(base + TUL_NVRAM) & SE2DI)
break; /* write complete */
}
outb(0, base + TUL_NVRAM); /* -CS */
@@ -576,7 +545,6 @@ static int initio_reset_scsi(struct initio_host * host, int seconds)
/**
* initio_init - set up an InitIO host adapter
* @host: InitIO host adapter
- * @num_scbs: Number of SCBS
* @bios_addr: BIOS address
*
* Set up the host adapter and devices according to the configuration
@@ -896,17 +864,16 @@ static void initio_unlink_busy_scb(struct initio_host * host, struct scsi_ctrl_b
struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun)
{
- struct scsi_ctrl_blk *tmp, *prev;
+ struct scsi_ctrl_blk *tmp;
u16 scbp_tarlun;
- prev = tmp = host->first_busy;
+ tmp = host->first_busy;
while (tmp != NULL) {
scbp_tarlun = (tmp->lun << 8) | (tmp->target);
if (scbp_tarlun == tarlun) { /* Unlink this SCB */
break;
}
- prev = tmp;
tmp = tmp->next;
}
#if DEBUG_QUEUE
@@ -1198,7 +1165,7 @@ static void tulip_scsi(struct initio_host * host)
return;
}
if (host->jsint & (TSS_FUNC_COMP | TSS_BUS_SERV)) { /* func complete or Bus service */
- if ((scb = host->active) != NULL)
+ if (host->active)
initio_next_state(host);
return;
}
@@ -1345,15 +1312,15 @@ static int initio_state_1(struct initio_host * host)
}
if ((active_tc->flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {
active_tc->flags |= TCF_WDTR_DONE;
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
+ outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
outb(2, host->addr + TUL_SFifo); /* Extended msg length */
- outb(3, host->addr + TUL_SFifo); /* Sync request */
+ outb(EXTENDED_SDTR, host->addr + TUL_SFifo); /* Sync request */
outb(1, host->addr + TUL_SFifo); /* Start from 16 bits */
} else if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {
active_tc->flags |= TCF_SYNC_DONE;
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
+ outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
outb(3, host->addr + TUL_SFifo); /* extended msg length */
- outb(1, host->addr + TUL_SFifo); /* sync request */
+ outb(EXTENDED_SDTR, host->addr + TUL_SFifo); /* sync request */
outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo);
outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */
}
@@ -1439,16 +1406,16 @@ static int initio_state_3(struct initio_host * host)
case MSG_OUT: /* Message out phase */
if (active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) {
- outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */
+ outb(NOP, host->addr + TUL_SFifo); /* msg nop */
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
if (wait_tulip(host) == -1)
return -1;
} else {
active_tc->flags |= TCF_SYNC_DONE;
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
+ outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
outb(3, host->addr + TUL_SFifo); /* ext. msg len */
- outb(1, host->addr + TUL_SFifo); /* sync request */
+ outb(EXTENDED_SDTR, host->addr + TUL_SFifo); /* sync request */
outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo);
outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
@@ -1509,7 +1476,7 @@ static int initio_state_4(struct initio_host * host)
return -1;
return 6;
} else {
- outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */
+ outb(NOP, host->addr + TUL_SFifo); /* msg nop */
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
if (wait_tulip(host) == -1)
return -1;
@@ -1646,7 +1613,7 @@ static int initio_state_6(struct initio_host * host)
break;
case MSG_OUT: /* Message out phase */
- outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */
+ outb(NOP, host->addr + TUL_SFifo); /* msg nop */
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
if (wait_tulip(host) == -1)
return -1;
@@ -1670,7 +1637,7 @@ static int initio_state_6(struct initio_host * host)
*
*/
-int initio_state_7(struct initio_host * host)
+static int initio_state_7(struct initio_host * host)
{
int cnt, i;
@@ -1819,9 +1786,9 @@ int initio_status_msg(struct initio_host * host)
if (host->phase == MSG_OUT) {
if (host->jsstatus0 & TSS_PAR_ERROR)
- outb(MSG_PARITY, host->addr + TUL_SFifo);
+ outb(MSG_PARITY_ERROR, host->addr + TUL_SFifo);
else
- outb(MSG_NOP, host->addr + TUL_SFifo);
+ outb(NOP, host->addr + TUL_SFifo);
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return wait_tulip(host);
}
@@ -1832,7 +1799,7 @@ int initio_status_msg(struct initio_host * host)
return -1;
if (host->phase != MSG_OUT)
return initio_bad_seq(host);
- outb(MSG_PARITY, host->addr + TUL_SFifo);
+ outb(MSG_PARITY_ERROR, host->addr + TUL_SFifo);
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return wait_tulip(host);
}
@@ -1845,7 +1812,8 @@ int initio_status_msg(struct initio_host * host)
return initio_wait_done_disc(host);
}
- if (msg == MSG_LINK_COMP || msg == MSG_LINK_FLAG) {
+ if (msg == LINKED_CMD_COMPLETE ||
+ msg == LINKED_FLG_CMD_COMPLETE) {
if ((scb->tastat & 0x18) == 0x10)
return initio_msgin_accept(host);
}
@@ -1917,7 +1885,7 @@ static int int_initio_scsi_rst(struct initio_host * host)
}
/**
- * int_initio_scsi_resel - Reselection occurred
+ * int_initio_resel - Reselection occurred
* @host: InitIO host adapter
*
* A SCSI reselection event has been signalled and the interrupt
@@ -1960,7 +1928,8 @@ int int_initio_resel(struct initio_host * host)
return -1;
msg = inb(host->addr + TUL_SFifo); /* Read Tag Message */
- if (msg < MSG_STAG || msg > MSG_OTAG) /* Is simple Tag */
+ if (msg < SIMPLE_QUEUE_TAG || msg > ORDERED_QUEUE_TAG)
+ /* Is simple Tag */
goto no_tag;
if (initio_msgin_accept(host) == -1)
@@ -2040,7 +2009,7 @@ static int initio_msgout_abort_targ(struct initio_host * host)
if (host->phase != MSG_OUT)
return initio_bad_seq(host);
- outb(MSG_ABORT, host->addr + TUL_SFifo);
+ outb(ABORT_TASK_SET, host->addr + TUL_SFifo);
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return initio_wait_disc(host);
@@ -2063,7 +2032,7 @@ static int initio_msgout_abort_tag(struct initio_host * host)
if (host->phase != MSG_OUT)
return initio_bad_seq(host);
- outb(MSG_ABORT_TAG, host->addr + TUL_SFifo);
+ outb(ABORT_TASK, host->addr + TUL_SFifo);
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return initio_wait_disc(host);
@@ -2089,15 +2058,15 @@ static int initio_msgin(struct initio_host * host)
return -1;
switch (inb(host->addr + TUL_SFifo)) {
- case MSG_DISC: /* Disconnect msg */
+ case DISCONNECT: /* Disconnect msg */
outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
return initio_wait_disc(host);
- case MSG_SDP:
- case MSG_RESTORE:
- case MSG_NOP:
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ case NOP:
initio_msgin_accept(host);
break;
- case MSG_REJ: /* Clear ATN first */
+ case MESSAGE_REJECT: /* Clear ATN first */
outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)),
host->addr + TUL_SSignal);
active_tc = host->active_tc;
@@ -2106,13 +2075,13 @@ static int initio_msgin(struct initio_host * host)
host->addr + TUL_SSignal);
initio_msgin_accept(host);
break;
- case MSG_EXTEND: /* extended msg */
+ case EXTENDED_MESSAGE: /* extended msg */
initio_msgin_extend(host);
break;
- case MSG_IGNOREWIDE:
+ case IGNORE_WIDE_RESIDUE:
initio_msgin_accept(host);
break;
- case MSG_COMP:
+ case COMMAND_COMPLETE:
outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
return initio_wait_done_disc(host);
@@ -2134,7 +2103,7 @@ static int initio_msgout_reject(struct initio_host * host)
return -1;
if (host->phase == MSG_OUT) {
- outb(MSG_REJ, host->addr + TUL_SFifo); /* Msg reject */
+ outb(MESSAGE_REJECT, host->addr + TUL_SFifo); /* Msg reject */
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return wait_tulip(host);
}
@@ -2143,7 +2112,7 @@ static int initio_msgout_reject(struct initio_host * host)
static int initio_msgout_ide(struct initio_host * host)
{
- outb(MSG_IDE, host->addr + TUL_SFifo); /* Initiator Detected Error */
+ outb(INITIATOR_ERROR, host->addr + TUL_SFifo); /* Initiator Detected Error */
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return wait_tulip(host);
}
@@ -2197,9 +2166,9 @@ static int initio_msgin_extend(struct initio_host * host)
initio_sync_done(host);
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
+ outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
outb(3, host->addr + TUL_SFifo);
- outb(1, host->addr + TUL_SFifo);
+ outb(EXTENDED_SDTR, host->addr + TUL_SFifo);
outb(host->msg[2], host->addr + TUL_SFifo);
outb(host->msg[3], host->addr + TUL_SFifo);
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
@@ -2229,9 +2198,9 @@ static int initio_msgin_extend(struct initio_host * host)
if (initio_msgin_accept(host) != MSG_OUT)
return host->phase;
/* WDTR msg out */
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
+ outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
outb(2, host->addr + TUL_SFifo);
- outb(3, host->addr + TUL_SFifo);
+ outb(EXTENDED_WDTR, host->addr + TUL_SFifo);
outb(host->msg[2], host->addr + TUL_SFifo);
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return wait_tulip(host);
@@ -2421,7 +2390,7 @@ int initio_bus_device_reset(struct initio_host * host)
}
tmp = tmp->next;
}
- outb(MSG_DEVRST, host->addr + TUL_SFifo);
+ outb(TARGET_RESET, host->addr + TUL_SFifo);
outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
return initio_wait_disc(host);
@@ -2583,7 +2552,7 @@ static void initio_build_scb(struct initio_host * host, struct scsi_ctrl_blk * c
SENSE_SIZE, DMA_FROM_DEVICE);
cblk->senseptr = (u32)dma_addr;
cblk->senselen = SENSE_SIZE;
- cmnd->SCp.ptr = (char *)(unsigned long)dma_addr;
+ initio_priv(cmnd)->sense_dma_addr = dma_addr;
cblk->cdblen = cmnd->cmd_len;
/* Clear the returned status */
@@ -2607,7 +2576,7 @@ static void initio_build_scb(struct initio_host * host, struct scsi_ctrl_blk * c
sizeof(struct sg_entry) * TOTAL_SG_ENTRY,
DMA_BIDIRECTIONAL);
cblk->bufptr = (u32)dma_addr;
- cmnd->SCp.dma_handle = dma_addr;
+ initio_priv(cmnd)->sglist_dma_addr = dma_addr;
cblk->sglen = nseg;
@@ -2630,23 +2599,18 @@ static void initio_build_scb(struct initio_host * host, struct scsi_ctrl_blk * c
}
/**
- * i91u_queuecommand - Queue a new command if possible
+ * i91u_queuecommand_lck - Queue a new command if possible
* @cmd: SCSI command block from the mid layer
- * @done: Completion handler
*
* Attempts to queue a new command with the host adapter. Will return
* zero if successful or indicate a host busy condition if not (which
* will cause the mid layer to call us again later with the command)
*/
-
-static int i91u_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
+static int i91u_queuecommand_lck(struct scsi_cmnd *cmd)
{
struct initio_host *host = (struct initio_host *) cmd->device->host->hostdata;
struct scsi_ctrl_blk *cmnd;
- cmd->scsi_done = done;
-
cmnd = initio_alloc_scb(host);
if (!cmnd)
return SCSI_MLQUEUE_HOST_BUSY;
@@ -2679,9 +2643,9 @@ static int i91u_bus_reset(struct scsi_cmnd * cmnd)
}
/**
- * i91u_biospararm - return the "logical geometry
+ * i91u_biosparam - return the "logical geometry
* @sdev: SCSI device
- * @dev; Matching block device
+ * @unused: Matching gendisk
* @capacity: Sector size of drive
* @info_array: Return space for BIOS geometry
*
@@ -2691,7 +2655,7 @@ static int i91u_bus_reset(struct scsi_cmnd * cmnd)
* FIXME: limited to 2^32 sector devices.
*/
-static int i91u_biosparam(struct scsi_device *sdev, struct block_device *dev,
+static int i91u_biosparam(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int *info_array)
{
struct initio_host *host; /* Point to Host adapter control block */
@@ -2739,16 +2703,17 @@ static int i91u_biosparam(struct scsi_device *sdev, struct block_device *dev,
static void i91u_unmap_scb(struct pci_dev *pci_dev, struct scsi_cmnd *cmnd)
{
/* auto sense buffer */
- if (cmnd->SCp.ptr) {
+ if (initio_priv(cmnd)->sense_dma_addr) {
dma_unmap_single(&pci_dev->dev,
- (dma_addr_t)((unsigned long)cmnd->SCp.ptr),
+ initio_priv(cmnd)->sense_dma_addr,
SENSE_SIZE, DMA_FROM_DEVICE);
- cmnd->SCp.ptr = NULL;
+ initio_priv(cmnd)->sense_dma_addr = 0;
}
/* request buffer */
if (scsi_sg_count(cmnd)) {
- dma_unmap_single(&pci_dev->dev, cmnd->SCp.dma_handle,
+ dma_unmap_single(&pci_dev->dev,
+ initio_priv(cmnd)->sglist_dma_addr,
sizeof(struct sg_entry) * TOTAL_SG_ENTRY,
DMA_BIDIRECTIONAL);
@@ -2756,10 +2721,8 @@ static void i91u_unmap_scb(struct pci_dev *pci_dev, struct scsi_cmnd *cmnd)
}
}
-/**
+/*
* i91uSCBPost - SCSI callback
- * @host: Pointer to host adapter control block.
- * @cmnd: Pointer to SCSI control block.
*
* This is callback routine be called when tulip finish one
* SCSI command.
@@ -2820,11 +2783,11 @@ static void i91uSCBPost(u8 * host_mem, u8 * cblk_mem)
cmnd->result = cblk->tastat | (cblk->hastat << 16);
i91u_unmap_scb(host->pci_dev, cmnd);
- cmnd->scsi_done(cmnd); /* Notify system DONE */
+ scsi_done(cmnd); /* Notify system DONE */
initio_release_scb(host, cblk); /* Release SCB for current channel */
}
-static struct scsi_host_template initio_template = {
+static const struct scsi_host_template initio_template = {
.proc_name = "INI9100U",
.name = "Initio INI-9X00U/UW SCSI device driver",
.queuecommand = i91u_queuecommand,
@@ -2833,8 +2796,7 @@ static struct scsi_host_template initio_template = {
.can_queue = MAX_TARGETS * i91u_MAXQUEUE,
.this_id = 1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
+ .cmd_size = sizeof(struct initio_cmd_priv),
};
static int initio_probe_one(struct pci_dev *pdev,
@@ -2857,7 +2819,7 @@ static int initio_probe_one(struct pci_dev *pdev,
reg = 0;
bios_seg = (bios_seg << 8) + ((u16) ((reg & 0xFF00) >> 8));
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
printk(KERN_WARNING "i91u: Could not set 32 bit DMA mask\n");
error = -ENODEV;
goto out_disable_device;
@@ -2886,7 +2848,8 @@ static int initio_probe_one(struct pci_dev *pdev,
for (; num_scb >= MAX_TARGETS + 3; num_scb--) {
i = num_scb * sizeof(struct scsi_ctrl_blk);
- if ((scb = kzalloc(i, GFP_DMA)) != NULL)
+ scb = kzalloc(i, GFP_KERNEL);
+ if (scb)
break;
}
@@ -2931,7 +2894,7 @@ static int initio_probe_one(struct pci_dev *pdev,
shost->base = host->addr;
shost->sg_tablesize = TOTAL_SG_ENTRY;
- error = request_irq(pdev->irq, i91u_intr, IRQF_DISABLED|IRQF_SHARED, "i91u", shost);
+ error = request_irq(pdev->irq, i91u_intr, IRQF_SHARED, "i91u", shost);
if (error < 0) {
printk(KERN_WARNING "initio: Unable to request IRQ %d\n", pdev->irq);
goto out_free_scbs;
@@ -2978,7 +2941,7 @@ static void initio_remove_one(struct pci_dev *pdev)
MODULE_LICENSE("GPL");
-static struct pci_device_id initio_pci_tbl[] = {
+static const struct pci_device_id initio_pci_tbl[] = {
{PCI_VENDOR_ID_INIT, 0x9500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_INIT, 0x9400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_INIT, 0x9401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -2992,22 +2955,10 @@ static struct pci_driver initio_pci_driver = {
.name = "initio",
.id_table = initio_pci_tbl,
.probe = initio_probe_one,
- .remove = __devexit_p(initio_remove_one),
+ .remove = initio_remove_one,
};
-
-static int __init initio_init_driver(void)
-{
- return pci_register_driver(&initio_pci_driver);
-}
-
-static void __exit initio_exit_driver(void)
-{
- pci_unregister_driver(&initio_pci_driver);
-}
+module_pci_driver(initio_pci_driver);
MODULE_DESCRIPTION("Initio INI-9X00U/UW SCSI device driver");
MODULE_AUTHOR("Initio Corporation");
MODULE_LICENSE("GPL");
-
-module_init(initio_init_driver);
-module_exit(initio_exit_driver);
diff --git a/drivers/scsi/initio.h b/drivers/scsi/initio.h
index 219b901bdc25..7c9741552654 100644
--- a/drivers/scsi/initio.h
+++ b/drivers/scsi/initio.h
@@ -433,31 +433,6 @@ struct scsi_ctrl_blk {
#define TARGET_BUSY 0x08
#define INI_QUEUE_FULL 0x28
-/* SCSI MESSAGE */
-#define MSG_COMP 0x00
-#define MSG_EXTEND 0x01
-#define MSG_SDP 0x02
-#define MSG_RESTORE 0x03
-#define MSG_DISC 0x04
-#define MSG_IDE 0x05
-#define MSG_ABORT 0x06
-#define MSG_REJ 0x07
-#define MSG_NOP 0x08
-#define MSG_PARITY 0x09
-#define MSG_LINK_COMP 0x0A
-#define MSG_LINK_FLAG 0x0B
-#define MSG_DEVRST 0x0C
-#define MSG_ABORT_TAG 0x0D
-
-/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
-#define MSG_STAG 0x20
-#define MSG_HTAG 0x21
-#define MSG_OTAG 0x22
-
-#define MSG_IGNOREWIDE 0x23
-
-#define MSG_IDENT 0x80
-
/***********************************************************************
Target Device Control Structure
**********************************************************************/
@@ -665,3 +640,12 @@ typedef struct _NVRAM {
#define SCSI_RESET_HOST_RESET 0x200
#define SCSI_RESET_ACTION 0xff
+struct initio_cmd_priv {
+ dma_addr_t sense_dma_addr;
+ dma_addr_t sglist_dma_addr;
+};
+
+static inline struct initio_cmd_priv *initio_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 888086c4e709..95123689e9d1 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ipr.c -- driver for IBM Power Linux RAID adapters
*
* Written By: Brian King <brking@us.ibm.com>, IBM Corporation
*
* Copyright (C) 2003, 2004 IBM Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -72,7 +58,6 @@
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/libata.h>
#include <linux/hdreg.h>
#include <linux/reboot.h>
#include <linux/stringify.h>
@@ -92,19 +77,23 @@
static LIST_HEAD(ipr_ioa_head);
static unsigned int ipr_log_level = IPR_DEFAULT_LOG_LEVEL;
static unsigned int ipr_max_speed = 1;
-static int ipr_testmode = 0;
static unsigned int ipr_fastfail = 0;
static unsigned int ipr_transop_timeout = 0;
static unsigned int ipr_debug = 0;
static unsigned int ipr_max_devs = IPR_DEFAULT_SIS64_DEVS;
static unsigned int ipr_dual_ioa_raid = 1;
+static unsigned int ipr_number_of_msix = 16;
+static unsigned int ipr_fast_reboot;
static DEFINE_SPINLOCK(ipr_driver_lock);
/* This table describes the differences between DMA controller chips */
static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
{ /* Gemstone, Citrine, Obsidian, and Obsidian-E */
.mailbox = 0x0042C,
+ .max_cmds = 100,
.cache_line_size = 0x20,
+ .clear_isr = 1,
+ .iopoll_weight = 0,
{
.set_interrupt_mask_reg = 0x0022C,
.clr_interrupt_mask_reg = 0x00230,
@@ -126,7 +115,10 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
},
{ /* Snipe and Scamp */
.mailbox = 0x0052C,
+ .max_cmds = 100,
.cache_line_size = 0x20,
+ .clear_isr = 1,
+ .iopoll_weight = 0,
{
.set_interrupt_mask_reg = 0x00288,
.clr_interrupt_mask_reg = 0x0028C,
@@ -148,7 +140,10 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
},
{ /* CRoC */
.mailbox = 0x00044,
+ .max_cmds = 1000,
.cache_line_size = 0x20,
+ .clear_isr = 0,
+ .iopoll_weight = 64,
{
.set_interrupt_mask_reg = 0x00010,
.clr_interrupt_mask_reg = 0x00018,
@@ -175,18 +170,19 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
};
static const struct ipr_chip_t ipr_chip[] = {
- { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
- { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, IPR_USE_MSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
- { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
+ { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, true, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, false, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, true, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE, true, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_RATTLESNAKE, true, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
};
-static int ipr_max_bus_speeds [] = {
+static int ipr_max_bus_speeds[] = {
IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE
};
@@ -196,8 +192,6 @@ module_param_named(max_speed, ipr_max_speed, uint, 0);
MODULE_PARM_DESC(max_speed, "Maximum bus speed (0-2). Default: 1=U160. Speeds: 0=80 MB/s, 1=U160, 2=U320");
module_param_named(log_level, ipr_log_level, uint, 0);
MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver");
-module_param_named(testmode, ipr_testmode, int, 0);
-MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations");
module_param_named(fastfail, ipr_fastfail, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(fastfail, "Reduce timeouts and retries");
module_param_named(transop_timeout, ipr_transop_timeout, int, 0);
@@ -209,6 +203,10 @@ MODULE_PARM_DESC(dual_ioa_raid, "Enable dual adapter RAID support. Set to 1 to e
module_param_named(max_devs, ipr_max_devs, int, 0);
MODULE_PARM_DESC(max_devs, "Specify the maximum number of physical devices. "
"[Default=" __stringify(IPR_DEFAULT_SIS64_DEVS) "]");
+module_param_named(number_of_msix, ipr_number_of_msix, int, 0);
+MODULE_PARM_DESC(number_of_msix, "Specify the number of MSIX interrupts to use on capable adapters (1 - 16). (default:16)");
+module_param_named(fast_reboot, ipr_fast_reboot, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(fast_reboot, "Skip adapter shutdown during reboot. Set to 1 to enable. (default: 0)");
MODULE_LICENSE("GPL");
MODULE_VERSION(IPR_DRIVER_VERSION);
@@ -269,12 +267,22 @@ struct ipr_error_table_t ipr_error_table[] = {
"FFF6: Failure prediction threshold exceeded"},
{0x015D9200, 0, IPR_DEFAULT_LOG_LEVEL,
"8009: Impending cache battery pack failure"},
+ {0x02040100, 0, 0,
+ "Logical Unit in process of becoming ready"},
+ {0x02040200, 0, 0,
+ "Initializing command required"},
{0x02040400, 0, 0,
"34FF: Disk device format in progress"},
+ {0x02040C00, 0, 0,
+ "Logical unit not accessible, target port in unavailable state"},
{0x02048000, 0, IPR_DEFAULT_LOG_LEVEL,
"9070: IOA requested reset"},
{0x023F0000, 0, 0,
"Synchronization required"},
+ {0x02408500, 0, 0,
+ "IOA microcode download required"},
+ {0x02408600, 0, 0,
+ "Device bus connection is prohibited by host"},
{0x024E0000, 0, 0,
"No ready, IOA shutdown"},
{0x025A0000, 0, 0,
@@ -373,6 +381,8 @@ struct ipr_error_table_t ipr_error_table[] = {
"4030: Incorrect multipath connection"},
{0x04679000, 0, IPR_DEFAULT_LOG_LEVEL,
"4110: Unsupported enclosure function"},
+ {0x04679800, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4120: SAS cable VPD cannot be read"},
{0x046E0000, 0, IPR_DEFAULT_LOG_LEVEL,
"FFF4: Command to logical unit failed"},
{0x05240000, 1, 0,
@@ -395,10 +405,20 @@ struct ipr_error_table_t ipr_error_table[] = {
"Illegal request, command sequence error"},
{0x052C8000, 1, 0,
"Illegal request, dual adapter support not enabled"},
+ {0x052C8100, 1, 0,
+ "Illegal request, another cable connector was physically disabled"},
+ {0x054E8000, 1, 0,
+ "Illegal request, inconsistent group id/group count"},
{0x06040500, 0, IPR_DEFAULT_LOG_LEVEL,
"9031: Array protection temporarily suspended, protection resuming"},
{0x06040600, 0, IPR_DEFAULT_LOG_LEVEL,
"9040: Array protection temporarily suspended, protection resuming"},
+ {0x060B0100, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4080: IOA exceeded maximum operating temperature"},
+ {0x060B8000, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4085: Service required"},
+ {0x060B8100, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4086: SAS Adapter Hardware Configuration Error"},
{0x06288000, 0, IPR_DEFAULT_LOG_LEVEL,
"3140: Device bus not ready to ready transition"},
{0x06290000, 0, IPR_DEFAULT_LOG_LEVEL,
@@ -411,6 +431,8 @@ struct ipr_error_table_t ipr_error_table[] = {
"FFFB: SCSI bus was reset by another initiator"},
{0x063F0300, 0, IPR_DEFAULT_LOG_LEVEL,
"3029: A device replacement has occurred"},
+ {0x063F8300, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4102: Device bus fabric performance degradation"},
{0x064C8000, 0, IPR_DEFAULT_LOG_LEVEL,
"9051: IOA cache data exists for a missing or failed device"},
{0x064C8100, 0, IPR_DEFAULT_LOG_LEVEL,
@@ -433,6 +455,14 @@ struct ipr_error_table_t ipr_error_table[] = {
"9076: Configuration error, missing remote IOA"},
{0x06679100, 0, IPR_DEFAULT_LOG_LEVEL,
"4050: Enclosure does not support a required multipath function"},
+ {0x06679800, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4121: Configuration error, required cable is missing"},
+ {0x06679900, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4122: Cable is not plugged into the correct location on remote IOA"},
+ {0x06679A00, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4123: Configuration error, invalid cable vital product data"},
+ {0x06679B00, 0, IPR_DEFAULT_LOG_LEVEL,
+ "4124: Configuration error, both cable ends are plugged into the same IOA"},
{0x06690000, 0, IPR_DEFAULT_LOG_LEVEL,
"4070: Logically bad block written on device"},
{0x06690200, 0, IPR_DEFAULT_LOG_LEVEL,
@@ -447,12 +477,16 @@ struct ipr_error_table_t ipr_error_table[] = {
"9072: Link not operational transition"},
{0x066B8200, 0, IPR_DEFAULT_LOG_LEVEL,
"9032: Array exposed but still protected"},
- {0x066B8300, 0, IPR_DEFAULT_LOG_LEVEL + 1,
+ {0x066B8300, 0, IPR_DEBUG_LOG_LEVEL,
"70DD: Device forced failed by disrupt device command"},
{0x066B9100, 0, IPR_DEFAULT_LOG_LEVEL,
"4061: Multipath redundancy level got better"},
{0x066B9200, 0, IPR_DEFAULT_LOG_LEVEL,
"4060: Multipath redundancy level got worse"},
+ {0x06808100, 0, IPR_DEBUG_LOG_LEVEL,
+ "9083: Device raw mode enabled"},
+ {0x06808200, 0, IPR_DEBUG_LOG_LEVEL,
+ "9084: Device raw mode disabled"},
{0x07270000, 0, 0,
"Failure due to other device"},
{0x07278000, 0, IPR_DEFAULT_LOG_LEVEL,
@@ -495,10 +529,18 @@ struct ipr_error_table_t ipr_error_table[] = {
"9062: One or more disks are missing from an array"},
{0x07279900, 0, IPR_DEFAULT_LOG_LEVEL,
"9063: Maximum number of functional arrays has been exceeded"},
+ {0x07279A00, 0, 0,
+ "Data protect, other volume set problem"},
{0x0B260000, 0, 0,
"Aborted command, invalid descriptor"},
+ {0x0B3F9000, 0, 0,
+ "Target operating conditions have changed, dual adapter takeover"},
+ {0x0B530200, 0, 0,
+ "Aborted command, medium removal prevented"},
{0x0B5A0000, 0, 0,
- "Command terminated by host"}
+ "Command terminated by host"},
+ {0x0B5B8000, 0, 0,
+ "Aborted command, command terminated by host"}
};
static const struct ipr_ses_table_entry ipr_ses_table[] = {
@@ -542,24 +584,40 @@ static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd,
{
struct ipr_trace_entry *trace_entry;
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ unsigned int trace_index;
- trace_entry = &ioa_cfg->trace[ioa_cfg->trace_index++];
+ trace_index = atomic_add_return(1, &ioa_cfg->trace_index) & IPR_TRACE_INDEX_MASK;
+ trace_entry = &ioa_cfg->trace[trace_index];
trace_entry->time = jiffies;
trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0];
trace_entry->type = type;
- if (ipr_cmd->ioa_cfg->sis64)
- trace_entry->ata_op_code = ipr_cmd->i.ata_ioadl.regs.command;
- else
- trace_entry->ata_op_code = ipr_cmd->ioarcb.u.add_data.u.regs.command;
trace_entry->cmd_index = ipr_cmd->cmd_index & 0xff;
trace_entry->res_handle = ipr_cmd->ioarcb.res_handle;
trace_entry->u.add_data = add_data;
+ wmb();
}
#else
-#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0)
+#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while (0)
#endif
/**
+ * ipr_lock_and_done - Acquire lock and complete command
+ * @ipr_cmd: ipr command struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_lock_and_done(struct ipr_cmnd *ipr_cmd)
+{
+ unsigned long lock_flags;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ ipr_cmd->done(ipr_cmd);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+}
+
+/**
* ipr_reinit_ipr_cmnd - Re-initialize an IPR Cmnd block for reuse
* @ipr_cmd: ipr command struct
*
@@ -570,10 +628,12 @@ static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
{
struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
struct ipr_ioasa *ioasa = &ipr_cmd->s.ioasa;
- struct ipr_ioasa64 *ioasa64 = &ipr_cmd->s.ioasa64;
dma_addr_t dma_addr = ipr_cmd->dma_addr;
+ int hrrq_id;
+ hrrq_id = ioarcb->cmd_pkt.hrrq_id;
memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt));
+ ioarcb->cmd_pkt.hrrq_id = hrrq_id;
ioarcb->data_transfer_length = 0;
ioarcb->read_data_transfer_length = 0;
ioarcb->ioadl_len = 0;
@@ -582,18 +642,15 @@ static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
if (ipr_cmd->ioa_cfg->sis64) {
ioarcb->u.sis64_addr_data.data_ioadl_addr =
cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ioadl64));
- ioasa64->u.gata.status = 0;
} else {
ioarcb->write_ioadl_addr =
cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, i.ioadl));
ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr;
- ioasa->u.gata.status = 0;
}
ioasa->hdr.ioasc = 0;
ioasa->hdr.residual_data_len = 0;
ipr_cmd->scsi_cmd = NULL;
- ipr_cmd->qc = NULL;
ipr_cmd->sense_buffer[0] = 0;
ipr_cmd->dma_use_sg = 0;
}
@@ -601,38 +658,61 @@ static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
/**
* ipr_init_ipr_cmnd - Initialize an IPR Cmnd block
* @ipr_cmd: ipr command struct
+ * @fast_done: fast done function call-back
*
* Return value:
* none
**/
-static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
+static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd,
+ void (*fast_done) (struct ipr_cmnd *))
{
ipr_reinit_ipr_cmnd(ipr_cmd);
ipr_cmd->u.scratch = 0;
ipr_cmd->sibling = NULL;
- init_timer(&ipr_cmd->timer);
+ ipr_cmd->eh_comp = NULL;
+ ipr_cmd->fast_done = fast_done;
+ timer_setup(&ipr_cmd->timer, NULL, 0);
}
/**
- * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block
- * @ioa_cfg: ioa config struct
+ * __ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block
+ * @hrrq: hrr queue
*
* Return value:
* pointer to ipr command struct
**/
static
-struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg)
+struct ipr_cmnd *__ipr_get_free_ipr_cmnd(struct ipr_hrr_queue *hrrq)
{
- struct ipr_cmnd *ipr_cmd;
+ struct ipr_cmnd *ipr_cmd = NULL;
+
+ if (likely(!list_empty(&hrrq->hrrq_free_q))) {
+ ipr_cmd = list_entry(hrrq->hrrq_free_q.next,
+ struct ipr_cmnd, queue);
+ list_del(&ipr_cmd->queue);
+ }
- ipr_cmd = list_entry(ioa_cfg->free_q.next, struct ipr_cmnd, queue);
- list_del(&ipr_cmd->queue);
- ipr_init_ipr_cmnd(ipr_cmd);
return ipr_cmd;
}
/**
+ * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block and initialize it
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * pointer to ipr command struct
+ **/
+static
+struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct ipr_cmnd *ipr_cmd =
+ __ipr_get_free_ipr_cmnd(&ioa_cfg->hrrq[IPR_INIT_HRRQ]);
+ ipr_init_ipr_cmnd(ipr_cmd, ipr_lock_and_done);
+ return ipr_cmd;
+}
+
+/**
* ipr_mask_and_clear_interrupts - Mask all and clear specified interrupts
* @ioa_cfg: ioa config struct
* @clr_ints: interrupts to clear
@@ -646,10 +726,14 @@ struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg)
static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg,
u32 clr_ints)
{
- volatile u32 int_reg;
+ int i;
/* Stop new interrupts */
- ioa_cfg->allow_interrupts = 0;
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].allow_interrupts = 0;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
/* Set interrupt mask to stop all new interrupts */
if (ioa_cfg->sis64)
@@ -661,7 +745,7 @@ static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg,
if (ioa_cfg->sis64)
writel(~0, ioa_cfg->regs.clr_interrupt_reg);
writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg32);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
+ readl(ioa_cfg->regs.sense_interrupt_reg);
}
/**
@@ -674,12 +758,14 @@ static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg,
static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
{
int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
+ int rc;
if (pcix_cmd_reg == 0)
return 0;
- if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
- &ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
+ rc = pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
+ &ioa_cfg->saved_pcix_cmd_reg);
+ if (rc != PCIBIOS_SUCCESSFUL) {
dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n");
return -EIO;
}
@@ -698,10 +784,12 @@ static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
{
int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
+ int rc;
if (pcix_cmd_reg) {
- if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
- ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
+ rc = pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
+ ioa_cfg->saved_pcix_cmd_reg);
+ if (rc != PCIBIOS_SUCCESSFUL) {
dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n");
return -EIO;
}
@@ -710,26 +798,28 @@ static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
return 0;
}
+
/**
- * ipr_sata_eh_done - done function for aborted SATA commands
+ * __ipr_scsi_eh_done - mid-layer done function for aborted ops
* @ipr_cmd: ipr command struct
*
- * This function is invoked for ops generated to SATA
- * devices which are being aborted.
+ * This function is invoked by the interrupt handler for
+ * ops generated by the SCSI mid-layer which are being aborted.
*
* Return value:
* none
**/
-static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
+static void __ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
{
- struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
- struct ata_queued_cmd *qc = ipr_cmd->qc;
- struct ipr_sata_port *sata_port = qc->ap->private_data;
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
- qc->err_mask |= AC_ERR_OTHER;
- sata_port->ioasa.status |= ATA_BUSY;
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- ata_qc_complete(qc);
+ scsi_cmd->result |= (DID_ERROR << 16);
+
+ scsi_dma_unmap(ipr_cmd->scsi_cmd);
+ scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -744,14 +834,12 @@ static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
**/
static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
{
- struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
- struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
-
- scsi_cmd->result |= (DID_ERROR << 16);
+ unsigned long hrrq_flags;
+ struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
- scsi_dma_unmap(ipr_cmd->scsi_cmd);
- scsi_cmd->scsi_done(scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
+ __ipr_scsi_eh_done(ipr_cmd);
+ spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
}
/**
@@ -766,24 +854,30 @@ static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg)
{
struct ipr_cmnd *ipr_cmd, *temp;
+ struct ipr_hrr_queue *hrrq;
ENTER;
- list_for_each_entry_safe(ipr_cmd, temp, &ioa_cfg->pending_q, queue) {
- list_del(&ipr_cmd->queue);
+ for_each_hrrq(hrrq, ioa_cfg) {
+ spin_lock(&hrrq->_lock);
+ list_for_each_entry_safe(ipr_cmd,
+ temp, &hrrq->hrrq_pending_q, queue) {
+ list_del(&ipr_cmd->queue);
- ipr_cmd->s.ioasa.hdr.ioasc = cpu_to_be32(IPR_IOASC_IOA_WAS_RESET);
- ipr_cmd->s.ioasa.hdr.ilid = cpu_to_be32(IPR_DRIVER_ILID);
+ ipr_cmd->s.ioasa.hdr.ioasc =
+ cpu_to_be32(IPR_IOASC_IOA_WAS_RESET);
+ ipr_cmd->s.ioasa.hdr.ilid =
+ cpu_to_be32(IPR_DRIVER_ILID);
- if (ipr_cmd->scsi_cmd)
- ipr_cmd->done = ipr_scsi_eh_done;
- else if (ipr_cmd->qc)
- ipr_cmd->done = ipr_sata_eh_done;
+ if (ipr_cmd->scsi_cmd)
+ ipr_cmd->done = __ipr_scsi_eh_done;
- ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET);
- del_timer(&ipr_cmd->timer);
- ipr_cmd->done(ipr_cmd);
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH,
+ IPR_IOASC_IOA_WAS_RESET);
+ timer_delete(&ipr_cmd->timer);
+ ipr_cmd->done(ipr_cmd);
+ }
+ spin_unlock(&hrrq->_lock);
}
-
LEAVE;
}
@@ -831,24 +925,19 @@ static void ipr_send_command(struct ipr_cmnd *ipr_cmd)
**/
static void ipr_do_req(struct ipr_cmnd *ipr_cmd,
void (*done) (struct ipr_cmnd *),
- void (*timeout_func) (struct ipr_cmnd *), u32 timeout)
+ void (*timeout_func) (struct timer_list *), u32 timeout)
{
- struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
-
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
ipr_cmd->done = done;
- ipr_cmd->timer.data = (unsigned long) ipr_cmd;
ipr_cmd->timer.expires = jiffies + timeout;
- ipr_cmd->timer.function = (void (*)(unsigned long))timeout_func;
+ ipr_cmd->timer.function = timeout_func;
add_timer(&ipr_cmd->timer);
ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0);
- mb();
-
ipr_send_command(ipr_cmd);
}
@@ -925,7 +1014,7 @@ static void ipr_init_ioadl(struct ipr_cmnd *ipr_cmd, dma_addr_t dma_addr,
* none
**/
static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd,
- void (*timeout_func) (struct ipr_cmnd *ipr_cmd),
+ void (*timeout_func) (struct timer_list *),
u32 timeout)
{
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
@@ -938,6 +1027,19 @@ static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd,
spin_lock_irq(ioa_cfg->host->host_lock);
}
+static int ipr_get_hrrq_index(struct ipr_ioa_cfg *ioa_cfg)
+{
+ unsigned int hrrq;
+
+ if (ioa_cfg->hrrq_num == 1)
+ hrrq = 0;
+ else {
+ hrrq = atomic_add_return(1, &ioa_cfg->hrrq_index);
+ hrrq = (hrrq % (ioa_cfg->hrrq_num - 1)) + 1;
+ }
+ return hrrq;
+}
+
/**
* ipr_send_hcam - Send an HCAM to the adapter.
* @ioa_cfg: ioa config struct
@@ -957,9 +1059,9 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
struct ipr_cmnd *ipr_cmd;
struct ipr_ioarcb *ioarcb;
- if (ioa_cfg->allow_cmds) {
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) {
ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_pending_q);
ipr_cmd->u.hostrcb = hostrcb;
@@ -982,8 +1084,6 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR);
- mb();
-
ipr_send_command(ipr_cmd);
} else {
list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
@@ -991,31 +1091,6 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
}
/**
- * ipr_update_ata_class - Update the ata class in the resource entry
- * @res: resource entry struct
- * @proto: cfgte device bus protocol value
- *
- * Return value:
- * none
- **/
-static void ipr_update_ata_class(struct ipr_resource_entry *res, unsigned int proto)
-{
- switch(proto) {
- case IPR_PROTO_SATA:
- case IPR_PROTO_SAS_STP:
- res->ata_class = ATA_DEV_ATA;
- break;
- case IPR_PROTO_SATA_ATAPI:
- case IPR_PROTO_SAS_STP_ATAPI:
- res->ata_class = ATA_DEV_ATAPI;
- break;
- default:
- res->ata_class = ATA_DEV_UNKNOWN;
- break;
- };
-}
-
-/**
* ipr_init_res_entry - Initialize a resource entry struct.
* @res: resource entry struct
* @cfgtew: config table entry wrapper struct
@@ -1027,7 +1102,6 @@ static void ipr_init_res_entry(struct ipr_resource_entry *res,
struct ipr_config_table_entry_wrapper *cfgtew)
{
int found = 0;
- unsigned int proto;
struct ipr_ioa_cfg *ioa_cfg = res->ioa_cfg;
struct ipr_resource_entry *gscsi_res = NULL;
@@ -1036,12 +1110,12 @@ static void ipr_init_res_entry(struct ipr_resource_entry *res,
res->add_to_ml = 0;
res->del_from_ml = 0;
res->resetting_device = 0;
+ res->reset_occurred = 0;
res->sdev = NULL;
- res->sata_port = NULL;
if (ioa_cfg->sis64) {
- proto = cfgtew->u.cfgte64->proto;
- res->res_flags = cfgtew->u.cfgte64->res_flags;
+ res->flags = be16_to_cpu(cfgtew->u.cfgte64->flags);
+ res->res_flags = be16_to_cpu(cfgtew->u.cfgte64->res_flags);
res->qmodel = IPR_QUEUEING_MODEL64(res);
res->type = cfgtew->u.cfgte64->res_type;
@@ -1085,7 +1159,6 @@ static void ipr_init_res_entry(struct ipr_resource_entry *res,
set_bit(res->target, ioa_cfg->target_ids);
}
} else {
- proto = cfgtew->u.cfgte->proto;
res->qmodel = IPR_QUEUEING_MODEL(res);
res->flags = cfgtew->u.cfgte->flags;
if (res->flags & IPR_IS_IOA_RESOURCE)
@@ -1098,8 +1171,6 @@ static void ipr_init_res_entry(struct ipr_resource_entry *res,
res->lun = cfgtew->u.cfgte->res_addr.lun;
res->lun_wwn = get_unaligned_be64(cfgtew->u.cfgte->lun_wwn);
}
-
- ipr_update_ata_class(res, proto);
}
/**
@@ -1131,23 +1202,45 @@ static int ipr_is_same_device(struct ipr_resource_entry *res,
}
/**
- * ipr_format_res_path - Format the resource path for printing.
+ * __ipr_format_res_path - Format the resource path for printing.
* @res_path: resource path
- * @buf: buffer
+ * @buffer: buffer
+ * @len: length of buffer provided
*
* Return value:
* pointer to buffer
**/
-static char *ipr_format_res_path(u8 *res_path, char *buffer, int len)
+static char *__ipr_format_res_path(u8 *res_path, char *buffer, int len)
{
int i;
char *p = buffer;
*p = '\0';
- p += snprintf(p, buffer + len - p, "%02X", res_path[0]);
- for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++)
- p += snprintf(p, buffer + len - p, "-%02X", res_path[i]);
+ p += scnprintf(p, buffer + len - p, "%02X", res_path[0]);
+ for (i = 1; res_path[i] != 0xff && i < IPR_RES_PATH_BYTES; i++)
+ p += scnprintf(p, buffer + len - p, "-%02X", res_path[i]);
+
+ return buffer;
+}
+/**
+ * ipr_format_res_path - Format the resource path for printing.
+ * @ioa_cfg: ioa config struct
+ * @res_path: resource path
+ * @buffer: buffer
+ * @len: length of buffer provided
+ *
+ * Return value:
+ * pointer to buffer
+ **/
+static char *ipr_format_res_path(struct ipr_ioa_cfg *ioa_cfg,
+ u8 *res_path, char *buffer, int len)
+{
+ char *p = buffer;
+
+ *p = '\0';
+ p += scnprintf(p, buffer + len - p, "%d/", ioa_cfg->host->host_no);
+ __ipr_format_res_path(res_path, p, len - (p - buffer));
return buffer;
}
@@ -1163,19 +1256,17 @@ static void ipr_update_res_entry(struct ipr_resource_entry *res,
struct ipr_config_table_entry_wrapper *cfgtew)
{
char buffer[IPR_MAX_RES_PATH_LENGTH];
- unsigned int proto;
int new_path = 0;
if (res->ioa_cfg->sis64) {
- res->flags = cfgtew->u.cfgte64->flags;
- res->res_flags = cfgtew->u.cfgte64->res_flags;
+ res->flags = be16_to_cpu(cfgtew->u.cfgte64->flags);
+ res->res_flags = be16_to_cpu(cfgtew->u.cfgte64->res_flags);
res->type = cfgtew->u.cfgte64->res_type;
memcpy(&res->std_inq_data, &cfgtew->u.cfgte64->std_inq_data,
sizeof(struct ipr_std_inq_data));
res->qmodel = IPR_QUEUEING_MODEL64(res);
- proto = cfgtew->u.cfgte64->proto;
res->res_handle = cfgtew->u.cfgte64->res_handle;
res->dev_id = cfgtew->u.cfgte64->dev_id;
@@ -1191,8 +1282,8 @@ static void ipr_update_res_entry(struct ipr_resource_entry *res,
if (res->sdev && new_path)
sdev_printk(KERN_INFO, res->sdev, "Resource path: %s\n",
- ipr_format_res_path(res->res_path, buffer,
- sizeof(buffer)));
+ ipr_format_res_path(res->ioa_cfg,
+ res->res_path, buffer, sizeof(buffer)));
} else {
res->flags = cfgtew->u.cfgte->flags;
if (res->flags & IPR_IS_IOA_RESOURCE)
@@ -1204,18 +1295,14 @@ static void ipr_update_res_entry(struct ipr_resource_entry *res,
sizeof(struct ipr_std_inq_data));
res->qmodel = IPR_QUEUEING_MODEL(res);
- proto = cfgtew->u.cfgte->proto;
res->res_handle = cfgtew->u.cfgte->res_handle;
}
-
- ipr_update_ata_class(res, proto);
}
/**
* ipr_clear_res_target - Clear the bit in the bit map representing the target
* for the resource.
* @res: resource entry struct
- * @cfgtew: config table entry wrapper struct
*
* Return value:
* none
@@ -1296,16 +1383,14 @@ static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg,
if (res->sdev) {
res->del_from_ml = 1;
res->res_handle = IPR_INVALID_RES_HANDLE;
- if (ioa_cfg->allow_ml_add_del)
- schedule_work(&ioa_cfg->work_q);
+ schedule_work(&ioa_cfg->work_q);
} else {
ipr_clear_res_target(res);
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
}
} else if (!res->sdev || res->del_from_ml) {
res->add_to_ml = 1;
- if (ioa_cfg->allow_ml_add_del)
- schedule_work(&ioa_cfg->work_q);
+ schedule_work(&ioa_cfg->work_q);
}
ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
@@ -1327,11 +1412,12 @@ static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd)
struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
- list_del(&hostrcb->queue);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_del_init(&hostrcb->queue);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
if (ioasc) {
- if (ioasc != IPR_IOASC_IOA_WAS_RESET)
+ if (ioasc != IPR_IOASC_IOA_WAS_RESET &&
+ ioasc != IPR_IOASC_ABORTED_CMD_TERM_BY_HOST)
dev_err(&ioa_cfg->pdev->dev,
"Host RCB failed with IOASC: 0x%08X\n", ioasc);
@@ -1342,23 +1428,22 @@ static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd)
}
/**
- * strip_and_pad_whitespace - Strip and pad trailing whitespace.
- * @i: index into buffer
- * @buf: string to modify
+ * strip_whitespace - Strip and pad trailing whitespace.
+ * @i: size of buffer
+ * @buf: string to modify
*
- * This function will strip all trailing whitespace, pad the end
- * of the string with a single space, and NULL terminate the string.
+ * This function will strip all trailing whitespace and
+ * NUL terminate the string.
*
- * Return value:
- * new length of string
**/
-static int strip_and_pad_whitespace(int i, char *buf)
+static void strip_whitespace(int i, char *buf)
{
+ if (i < 1)
+ return;
+ i--;
while (i && buf[i] == ' ')
i--;
- buf[i+1] = ' ';
- buf[i+2] = '\0';
- return i + 2;
+ buf[i+1] = '\0';
}
/**
@@ -1373,19 +1458,21 @@ static int strip_and_pad_whitespace(int i, char *buf)
static void ipr_log_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb,
struct ipr_vpd *vpd)
{
- char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN + 3];
- int i = 0;
+ char vendor_id[IPR_VENDOR_ID_LEN + 1];
+ char product_id[IPR_PROD_ID_LEN + 1];
+ char sn[IPR_SERIAL_NUM_LEN + 1];
- memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN);
- i = strip_and_pad_whitespace(IPR_VENDOR_ID_LEN - 1, buffer);
+ memcpy(vendor_id, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN);
+ strip_whitespace(IPR_VENDOR_ID_LEN, vendor_id);
- memcpy(&buffer[i], vpd->vpids.product_id, IPR_PROD_ID_LEN);
- i = strip_and_pad_whitespace(i + IPR_PROD_ID_LEN - 1, buffer);
+ memcpy(product_id, vpd->vpids.product_id, IPR_PROD_ID_LEN);
+ strip_whitespace(IPR_PROD_ID_LEN, product_id);
- memcpy(&buffer[i], vpd->sn, IPR_SERIAL_NUM_LEN);
- buffer[IPR_SERIAL_NUM_LEN + i] = '\0';
+ memcpy(sn, vpd->sn, IPR_SERIAL_NUM_LEN);
+ strip_whitespace(IPR_SERIAL_NUM_LEN, sn);
- ipr_hcam_err(hostrcb, "%s VPID/SN: %s\n", prefix, buffer);
+ ipr_hcam_err(hostrcb, "%s VPID/SN: %s %s %s\n", prefix,
+ vendor_id, product_id, sn);
}
/**
@@ -1578,8 +1665,8 @@ static void ipr_log_sis64_config_error(struct ipr_ioa_cfg *ioa_cfg,
ipr_err_separator;
ipr_err("Device %d : %s", i + 1,
- ipr_format_res_path(dev_entry->res_path, buffer,
- sizeof(buffer)));
+ __ipr_format_res_path(dev_entry->res_path,
+ buffer, sizeof(buffer)));
ipr_log_ext_vpd(&dev_entry->vpd);
ipr_err("-----New Device Information-----\n");
@@ -1755,7 +1842,7 @@ static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg,
* Return value:
* none
**/
-static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len)
+static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, __be32 *data, int len)
{
int i;
@@ -1925,14 +2012,16 @@ static void ipr_log64_fabric_path(struct ipr_hostrcb *hostrcb,
ipr_hcam_err(hostrcb, "%s %s: Resource Path=%s\n",
path_active_desc[i].desc, path_state_desc[j].desc,
- ipr_format_res_path(fabric->res_path, buffer,
- sizeof(buffer)));
+ ipr_format_res_path(hostrcb->ioa_cfg,
+ fabric->res_path,
+ buffer, sizeof(buffer)));
return;
}
}
ipr_err("Path state=%02X Resource Path=%s\n", path_state,
- ipr_format_res_path(fabric->res_path, buffer, sizeof(buffer)));
+ ipr_format_res_path(hostrcb->ioa_cfg, fabric->res_path,
+ buffer, sizeof(buffer)));
}
static const struct {
@@ -2073,18 +2162,20 @@ static void ipr_log64_path_elem(struct ipr_hostrcb *hostrcb,
ipr_hcam_err(hostrcb, "%s %s: Resource Path=%s, Link rate=%s, WWN=%08X%08X\n",
path_status_desc[j].desc, path_type_desc[i].desc,
- ipr_format_res_path(cfg->res_path, buffer,
- sizeof(buffer)),
- link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
- be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ ipr_format_res_path(hostrcb->ioa_cfg,
+ cfg->res_path, buffer, sizeof(buffer)),
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]),
+ be32_to_cpu(cfg->wwid[1]));
return;
}
}
ipr_hcam_err(hostrcb, "Path element=%02X: Resource Path=%s, Link rate=%s "
"WWN=%08X%08X\n", cfg->type_status,
- ipr_format_res_path(cfg->res_path, buffer, sizeof(buffer)),
- link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
- be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ ipr_format_res_path(hostrcb->ioa_cfg,
+ cfg->res_path, buffer, sizeof(buffer)),
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
}
/**
@@ -2121,7 +2212,7 @@ static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg,
((unsigned long)fabric + be16_to_cpu(fabric->length));
}
- ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len);
+ ipr_log_hex_data(ioa_cfg, (__be32 *)fabric, add_len);
}
/**
@@ -2147,7 +2238,8 @@ static void ipr_log_sis64_array_error(struct ipr_ioa_cfg *ioa_cfg,
ipr_err("RAID %s Array Configuration: %s\n",
error->protection_level,
- ipr_format_res_path(error->last_res_path, buffer, sizeof(buffer)));
+ ipr_format_res_path(ioa_cfg, error->last_res_path,
+ buffer, sizeof(buffer)));
ipr_err_separator;
@@ -2168,11 +2260,12 @@ static void ipr_log_sis64_array_error(struct ipr_ioa_cfg *ioa_cfg,
ipr_err("Array Member %d:\n", i);
ipr_log_ext_vpd(&array_entry->vpd);
ipr_err("Current Location: %s\n",
- ipr_format_res_path(array_entry->res_path, buffer,
- sizeof(buffer)));
+ ipr_format_res_path(ioa_cfg, array_entry->res_path,
+ buffer, sizeof(buffer)));
ipr_err("Expected Location: %s\n",
- ipr_format_res_path(array_entry->expected_res_path,
- buffer, sizeof(buffer)));
+ ipr_format_res_path(ioa_cfg,
+ array_entry->expected_res_path,
+ buffer, sizeof(buffer)));
ipr_err_separator;
}
@@ -2213,10 +2306,32 @@ static void ipr_log_sis64_fabric_error(struct ipr_ioa_cfg *ioa_cfg,
((unsigned long)fabric + be16_to_cpu(fabric->length));
}
- ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len);
+ ipr_log_hex_data(ioa_cfg, (__be32 *)fabric, add_len);
}
/**
+ * ipr_log_sis64_service_required_error - Log a sis64 service required error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_sis64_service_required_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ struct ipr_hostrcb_type_41_error *error;
+
+ error = &hostrcb->hcam.u.error64.u.type_41_error;
+
+ error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
+ ipr_err("Primary Failure Reason: %s\n", error->failure_reason);
+ ipr_log_hex_data(ioa_cfg, error->data,
+ be32_to_cpu(hostrcb->hcam.length) -
+ (offsetof(struct ipr_hostrcb_error, u) +
+ offsetof(struct ipr_hostrcb_type_41_error, data)));
+}
+/**
* ipr_log_generic_error - Log an adapter error.
* @ioa_cfg: ioa config struct
* @hostrcb: hostrcb struct
@@ -2232,6 +2347,42 @@ static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg,
}
/**
+ * ipr_log_sis64_device_error - Log a cache error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_sis64_device_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ struct ipr_hostrcb_type_21_error *error;
+ char buffer[IPR_MAX_RES_PATH_LENGTH];
+
+ error = &hostrcb->hcam.u.error64.u.type_21_error;
+
+ ipr_err("-----Failing Device Information-----\n");
+ ipr_err("World Wide Unique ID: %08X%08X%08X%08X\n",
+ be32_to_cpu(error->wwn[0]), be32_to_cpu(error->wwn[1]),
+ be32_to_cpu(error->wwn[2]), be32_to_cpu(error->wwn[3]));
+ ipr_err("Device Resource Path: %s\n",
+ __ipr_format_res_path(error->res_path,
+ buffer, sizeof(buffer)));
+ error->primary_problem_desc[sizeof(error->primary_problem_desc) - 1] = '\0';
+ error->second_problem_desc[sizeof(error->second_problem_desc) - 1] = '\0';
+ ipr_err("Primary Problem Description: %s\n", error->primary_problem_desc);
+ ipr_err("Secondary Problem Description: %s\n", error->second_problem_desc);
+ ipr_err("SCSI Sense Data:\n");
+ ipr_log_hex_data(ioa_cfg, error->sense_data, sizeof(error->sense_data));
+ ipr_err("SCSI Command Descriptor Block: \n");
+ ipr_log_hex_data(ioa_cfg, error->cdb, sizeof(error->cdb));
+
+ ipr_err("Additional IOA Data:\n");
+ ipr_log_hex_data(ioa_cfg, error->ioa_data, be32_to_cpu(error->length_of_error));
+}
+
+/**
* ipr_get_error - Find the specfied IOASC in the ipr_error_table.
* @ioasc: IOASC
*
@@ -2268,6 +2419,7 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
{
u32 ioasc;
int error_index;
+ struct ipr_hostrcb_type_21_error *error;
if (hostrcb->hcam.notify_type != IPR_HOST_RCB_NOTIF_TYPE_ERROR_LOG_ENTRY)
return;
@@ -2292,6 +2444,15 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
if (!ipr_error_table[error_index].log_hcam)
return;
+ if (ioasc == IPR_IOASC_HW_CMD_FAILED &&
+ hostrcb->hcam.overlay_id == IPR_HOST_RCB_OVERLAY_ID_21) {
+ error = &hostrcb->hcam.u.error64.u.type_21_error;
+
+ if (((be32_to_cpu(error->sense_data[0]) & 0x0000ff00) >> 8) == ILLEGAL_REQUEST &&
+ ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL)
+ return;
+ }
+
ipr_hcam_err(hostrcb, "%s\n", ipr_error_table[error_index].error);
/* Set indication we have logged an error */
@@ -2332,6 +2493,9 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
case IPR_HOST_RCB_OVERLAY_ID_20:
ipr_log_fabric_error(ioa_cfg, hostrcb);
break;
+ case IPR_HOST_RCB_OVERLAY_ID_21:
+ ipr_log_sis64_device_error(ioa_cfg, hostrcb);
+ break;
case IPR_HOST_RCB_OVERLAY_ID_23:
ipr_log_sis64_config_error(ioa_cfg, hostrcb);
break;
@@ -2342,6 +2506,9 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
case IPR_HOST_RCB_OVERLAY_ID_30:
ipr_log_sis64_fabric_error(ioa_cfg, hostrcb);
break;
+ case IPR_HOST_RCB_OVERLAY_ID_41:
+ ipr_log_sis64_service_required_error(ioa_cfg, hostrcb);
+ break;
case IPR_HOST_RCB_OVERLAY_ID_1:
case IPR_HOST_RCB_OVERLAY_ID_DEFAULT:
default:
@@ -2350,6 +2517,23 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
}
}
+static struct ipr_hostrcb *ipr_get_free_hostrcb(struct ipr_ioa_cfg *ioa)
+{
+ struct ipr_hostrcb *hostrcb;
+
+ hostrcb = list_first_entry_or_null(&ioa->hostrcb_free_q,
+ struct ipr_hostrcb, queue);
+
+ if (unlikely(!hostrcb)) {
+ dev_info(&ioa->pdev->dev, "Reclaiming async error buffers.");
+ hostrcb = list_first_entry_or_null(&ioa->hostrcb_report_q,
+ struct ipr_hostrcb, queue);
+ }
+
+ list_del_init(&hostrcb->queue);
+ return hostrcb;
+}
+
/**
* ipr_process_error - Op done function for an adapter error log.
* @ipr_cmd: ipr command struct
@@ -2373,24 +2557,29 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd)
else
fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc);
- list_del(&hostrcb->queue);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_del_init(&hostrcb->queue);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
if (!ioasc) {
ipr_handle_log_data(ioa_cfg, hostrcb);
if (fd_ioasc == IPR_IOASC_NR_IOA_RESET_REQUIRED)
ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_ABBREV);
- } else if (ioasc != IPR_IOASC_IOA_WAS_RESET) {
+ } else if (ioasc != IPR_IOASC_IOA_WAS_RESET &&
+ ioasc != IPR_IOASC_ABORTED_CMD_TERM_BY_HOST) {
dev_err(&ioa_cfg->pdev->dev,
"Host RCB failed with IOASC: 0x%08X\n", ioasc);
}
+ list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_report_q);
+ schedule_work(&ioa_cfg->work_q);
+ hostrcb = ipr_get_free_hostrcb(ioa_cfg);
+
ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb);
}
/**
* ipr_timeout - An internally generated op has timed out.
- * @ipr_cmd: ipr command struct
+ * @t: Timer context used to fetch ipr command struct
*
* This function blocks host requests and initiates an
* adapter reset.
@@ -2398,8 +2587,9 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd)
* Return value:
* none
**/
-static void ipr_timeout(struct ipr_cmnd *ipr_cmd)
+static void ipr_timeout(struct timer_list *t)
{
+ struct ipr_cmnd *ipr_cmd = timer_container_of(ipr_cmd, t, timer);
unsigned long lock_flags = 0;
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
@@ -2422,7 +2612,7 @@ static void ipr_timeout(struct ipr_cmnd *ipr_cmd)
/**
* ipr_oper_timeout - Adapter timed out transitioning to operational
- * @ipr_cmd: ipr command struct
+ * @t: Timer context used to fetch ipr command struct
*
* This function blocks host requests and initiates an
* adapter reset.
@@ -2430,8 +2620,9 @@ static void ipr_timeout(struct ipr_cmnd *ipr_cmd)
* Return value:
* none
**/
-static void ipr_oper_timeout(struct ipr_cmnd *ipr_cmd)
+static void ipr_oper_timeout(struct timer_list *t)
{
+ struct ipr_cmnd *ipr_cmd = timer_container_of(ipr_cmd, t, timer);
unsigned long lock_flags = 0;
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
@@ -2456,36 +2647,6 @@ static void ipr_oper_timeout(struct ipr_cmnd *ipr_cmd)
}
/**
- * ipr_reset_reload - Reset/Reload the IOA
- * @ioa_cfg: ioa config struct
- * @shutdown_type: shutdown type
- *
- * This function resets the adapter and re-initializes it.
- * This function assumes that all new host commands have been stopped.
- * Return value:
- * SUCCESS / FAILED
- **/
-static int ipr_reset_reload(struct ipr_ioa_cfg *ioa_cfg,
- enum ipr_shutdown_type shutdown_type)
-{
- if (!ioa_cfg->in_reset_reload)
- ipr_initiate_ioa_reset(ioa_cfg, shutdown_type);
-
- spin_unlock_irq(ioa_cfg->host->host_lock);
- wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
- spin_lock_irq(ioa_cfg->host->host_lock);
-
- /* If we got hit with a host reset while we were already resetting
- the adapter for some reason, and the reset failed. */
- if (ioa_cfg->ioa_is_dead) {
- ipr_trace;
- return FAILED;
- }
-
- return SUCCESS;
-}
-
-/**
* ipr_find_ses_entry - Find matching SES in SES table
* @res: resource entry struct of SES
*
@@ -2901,7 +3062,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- if (ioa_cfg->sdt_state != GET_DUMP) {
+ if (ioa_cfg->sdt_state != READ_DUMP) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return;
}
@@ -3041,7 +3202,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
}
#else
-#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while(0)
+#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while (0)
#endif
/**
@@ -3053,7 +3214,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
**/
static void ipr_release_dump(struct kref *kref)
{
- struct ipr_dump *dump = container_of(kref,struct ipr_dump,kref);
+ struct ipr_dump *dump = container_of(kref, struct ipr_dump, kref);
struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg;
unsigned long lock_flags = 0;
int i;
@@ -3072,53 +3233,23 @@ static void ipr_release_dump(struct kref *kref)
LEAVE;
}
-/**
- * ipr_worker_thread - Worker thread
- * @work: ioa config struct
- *
- * Called at task level from a work thread. This function takes care
- * of adding and removing device from the mid-layer as configuration
- * changes are detected by the adapter.
- *
- * Return value:
- * nothing
- **/
-static void ipr_worker_thread(struct work_struct *work)
+static void ipr_add_remove_thread(struct work_struct *work)
{
unsigned long lock_flags;
struct ipr_resource_entry *res;
struct scsi_device *sdev;
- struct ipr_dump *dump;
struct ipr_ioa_cfg *ioa_cfg =
- container_of(work, struct ipr_ioa_cfg, work_q);
+ container_of(work, struct ipr_ioa_cfg, scsi_add_work_q);
u8 bus, target, lun;
int did_work;
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- if (ioa_cfg->sdt_state == GET_DUMP) {
- dump = ioa_cfg->dump;
- if (!dump) {
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return;
- }
- kref_get(&dump->kref);
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- ipr_get_ioa_dump(ioa_cfg, dump);
- kref_put(&dump->kref, ipr_release_dump);
-
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- if (ioa_cfg->sdt_state == DUMP_OBTAINED)
- ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return;
- }
-
restart:
do {
did_work = 0;
- if (!ioa_cfg->allow_cmds || !ioa_cfg->allow_ml_add_del) {
+ if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return;
}
@@ -3140,7 +3271,7 @@ restart:
break;
}
}
- } while(did_work);
+ } while (did_work);
list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if (res->add_to_ml) {
@@ -3155,11 +3286,72 @@ restart:
}
}
+ ioa_cfg->scan_done = 1;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
kobject_uevent(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE);
LEAVE;
}
+/**
+ * ipr_worker_thread - Worker thread
+ * @work: ioa config struct
+ *
+ * Called at task level from a work thread. This function takes care
+ * of adding and removing device from the mid-layer as configuration
+ * changes are detected by the adapter.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_worker_thread(struct work_struct *work)
+{
+ unsigned long lock_flags;
+ struct ipr_dump *dump;
+ struct ipr_ioa_cfg *ioa_cfg =
+ container_of(work, struct ipr_ioa_cfg, work_q);
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ if (ioa_cfg->sdt_state == READ_DUMP) {
+ dump = ioa_cfg->dump;
+ if (!dump) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+ kref_get(&dump->kref);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ ipr_get_ioa_dump(ioa_cfg, dump);
+ kref_put(&dump->kref, ipr_release_dump);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (ioa_cfg->sdt_state == DUMP_OBTAINED && !ioa_cfg->dump_timeout)
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+ if (ioa_cfg->scsi_unblock) {
+ ioa_cfg->scsi_unblock = 0;
+ ioa_cfg->scsi_blocked = 0;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ scsi_unblock_requests(ioa_cfg->host);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (ioa_cfg->scsi_blocked)
+ scsi_block_requests(ioa_cfg->host);
+ }
+
+ if (!ioa_cfg->scan_enabled) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+ schedule_work(&ioa_cfg->scsi_add_work_q);
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ LEAVE;
+}
+
#ifdef CONFIG_SCSI_IPR_TRACE
/**
* ipr_read_trace - Dump the adapter trace
@@ -3174,10 +3366,10 @@ restart:
* number of bytes printed to buffer
**/
static ssize_t ipr_read_trace(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct Scsi_Host *shost = class_to_shost(dev);
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
unsigned long lock_flags = 0;
@@ -3191,7 +3383,7 @@ static ssize_t ipr_read_trace(struct file *filp, struct kobject *kobj,
return ret;
}
-static struct bin_attribute ipr_trace_attr = {
+static const struct bin_attribute ipr_trace_attr = {
.attr = {
.name = "trace",
.mode = S_IRUGO,
@@ -3204,6 +3396,7 @@ static struct bin_attribute ipr_trace_attr = {
/**
* ipr_show_fw_version - Show the firmware version
* @dev: class device struct
+ * @attr: device attribute (unused)
* @buf: buffer
*
* Return value:
@@ -3238,6 +3431,7 @@ static struct device_attribute ipr_fw_version_attr = {
/**
* ipr_show_log_level - Show the adapter's error logging level
* @dev: class device struct
+ * @attr: device attribute (unused)
* @buf: buffer
*
* Return value:
@@ -3260,13 +3454,15 @@ static ssize_t ipr_show_log_level(struct device *dev,
/**
* ipr_store_log_level - Change the adapter's error logging level
* @dev: class device struct
+ * @attr: device attribute (unused)
* @buf: buffer
+ * @count: buffer size
*
* Return value:
* number of bytes printed to buffer
**/
static ssize_t ipr_store_log_level(struct device *dev,
- struct device_attribute *attr,
+ struct device_attribute *attr,
const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(dev);
@@ -3291,6 +3487,7 @@ static struct device_attribute ipr_log_level_attr = {
/**
* ipr_store_diagnostics - IOA Diagnostics interface
* @dev: device struct
+ * @attr: device attribute (unused)
* @buf: buffer
* @count: buffer size
*
@@ -3313,7 +3510,7 @@ static ssize_t ipr_store_diagnostics(struct device *dev,
return -EACCES;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3351,7 +3548,8 @@ static struct device_attribute ipr_diagnostics_attr = {
/**
* ipr_show_adapter_state - Show the adapter's state
- * @class_dev: device struct
+ * @dev: device struct
+ * @attr: device attribute (unused)
* @buf: buffer
*
* Return value:
@@ -3366,7 +3564,7 @@ static ssize_t ipr_show_adapter_state(struct device *dev,
int len;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- if (ioa_cfg->ioa_is_dead)
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
len = snprintf(buf, PAGE_SIZE, "offline\n");
else
len = snprintf(buf, PAGE_SIZE, "online\n");
@@ -3377,6 +3575,7 @@ static ssize_t ipr_show_adapter_state(struct device *dev,
/**
* ipr_store_adapter_state - Change adapter state
* @dev: device struct
+ * @attr: device attribute (unused)
* @buf: buffer
* @count: buffer size
*
@@ -3392,14 +3591,20 @@ static ssize_t ipr_store_adapter_state(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
unsigned long lock_flags;
- int result = count;
+ int result = count, i;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- if (ioa_cfg->ioa_is_dead && !strncmp(buf, "online", 6)) {
- ioa_cfg->ioa_is_dead = 0;
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead &&
+ !strncmp(buf, "online", 6)) {
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].ioa_is_dead = 0;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
+ wmb();
ioa_cfg->reset_retries = 0;
ioa_cfg->in_ioa_bringdown = 0;
ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
@@ -3422,6 +3627,7 @@ static struct device_attribute ipr_ioa_state_attr = {
/**
* ipr_store_reset_adapter - Reset the adapter
* @dev: device struct
+ * @attr: device attribute (unused)
* @buf: buffer
* @count: buffer size
*
@@ -3459,6 +3665,95 @@ static struct device_attribute ipr_ioa_reset_attr = {
.store = ipr_store_reset_adapter
};
+static int ipr_iopoll(struct irq_poll *iop, int budget);
+ /**
+ * ipr_show_iopoll_weight - Show ipr polling mode
+ * @dev: class device struct
+ * @attr: device attribute (unused)
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_show_iopoll_weight(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ unsigned long lock_flags = 0;
+ int len;
+
+ spin_lock_irqsave(shost->host_lock, lock_flags);
+ len = snprintf(buf, PAGE_SIZE, "%d\n", ioa_cfg->iopoll_weight);
+ spin_unlock_irqrestore(shost->host_lock, lock_flags);
+
+ return len;
+}
+
+/**
+ * ipr_store_iopoll_weight - Change the adapter's polling mode
+ * @dev: class device struct
+ * @attr: device attribute (unused)
+ * @buf: buffer
+ * @count: buffer size
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_store_iopoll_weight(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ unsigned long user_iopoll_weight;
+ unsigned long lock_flags = 0;
+ int i;
+
+ if (!ioa_cfg->sis64) {
+ dev_info(&ioa_cfg->pdev->dev, "irq_poll not supported on this adapter\n");
+ return -EINVAL;
+ }
+ if (kstrtoul(buf, 10, &user_iopoll_weight))
+ return -EINVAL;
+
+ if (user_iopoll_weight > 256) {
+ dev_info(&ioa_cfg->pdev->dev, "Invalid irq_poll weight. It must be less than 256\n");
+ return -EINVAL;
+ }
+
+ if (user_iopoll_weight == ioa_cfg->iopoll_weight) {
+ dev_info(&ioa_cfg->pdev->dev, "Current irq_poll weight has the same weight\n");
+ return strlen(buf);
+ }
+
+ if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
+ for (i = 1; i < ioa_cfg->hrrq_num; i++)
+ irq_poll_disable(&ioa_cfg->hrrq[i].iopoll);
+ }
+
+ spin_lock_irqsave(shost->host_lock, lock_flags);
+ ioa_cfg->iopoll_weight = user_iopoll_weight;
+ if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
+ for (i = 1; i < ioa_cfg->hrrq_num; i++) {
+ irq_poll_init(&ioa_cfg->hrrq[i].iopoll,
+ ioa_cfg->iopoll_weight, ipr_iopoll);
+ }
+ }
+ spin_unlock_irqrestore(shost->host_lock, lock_flags);
+
+ return strlen(buf);
+}
+
+static struct device_attribute ipr_iopoll_weight_attr = {
+ .attr = {
+ .name = "iopoll_weight",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ipr_show_iopoll_weight,
+ .store = ipr_store_iopoll_weight
+};
+
/**
* ipr_alloc_ucode_buffer - Allocates a microcode download buffer
* @buf_len: buffer length
@@ -3471,10 +3766,8 @@ static struct device_attribute ipr_ioa_reset_attr = {
**/
static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
{
- int sg_size, order, bsize_elem, num_elem, i, j;
+ int sg_size, order;
struct ipr_sglist *sglist;
- struct scatterlist *scatterlist;
- struct page *page;
/* Get the minimum size per scatter/gather element */
sg_size = buf_len / (IPR_MAX_SGLIST - 1);
@@ -3482,45 +3775,18 @@ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
/* Get the actual size per element */
order = get_order(sg_size);
- /* Determine the actual number of bytes per element */
- bsize_elem = PAGE_SIZE * (1 << order);
-
- /* Determine the actual number of sg entries needed */
- if (buf_len % bsize_elem)
- num_elem = (buf_len / bsize_elem) + 1;
- else
- num_elem = buf_len / bsize_elem;
-
/* Allocate a scatter/gather list for the DMA */
- sglist = kzalloc(sizeof(struct ipr_sglist) +
- (sizeof(struct scatterlist) * (num_elem - 1)),
- GFP_KERNEL);
-
+ sglist = kzalloc(sizeof(struct ipr_sglist), GFP_KERNEL);
if (sglist == NULL) {
ipr_trace;
return NULL;
}
-
- scatterlist = sglist->scatterlist;
- sg_init_table(scatterlist, num_elem);
-
sglist->order = order;
- sglist->num_sg = num_elem;
-
- /* Allocate a bunch of sg elements */
- for (i = 0; i < num_elem; i++) {
- page = alloc_pages(GFP_KERNEL, order);
- if (!page) {
- ipr_trace;
-
- /* Free up what we already allocated */
- for (j = i - 1; j >= 0; j--)
- __free_pages(sg_page(&scatterlist[j]), order);
- kfree(sglist);
- return NULL;
- }
-
- sg_set_page(&scatterlist[i], page, 0, 0);
+ sglist->scatterlist = sgl_alloc_order(buf_len, order, false, GFP_KERNEL,
+ &sglist->num_sg);
+ if (!sglist->scatterlist) {
+ kfree(sglist);
+ return NULL;
}
return sglist;
@@ -3528,7 +3794,7 @@ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
/**
* ipr_free_ucode_buffer - Frees a microcode download buffer
- * @p_dnld: scatter/gather list pointer
+ * @sglist: scatter/gather list pointer
*
* Free a DMA'able ucode download buffer previously allocated with
* ipr_alloc_ucode_buffer
@@ -3538,11 +3804,7 @@ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
**/
static void ipr_free_ucode_buffer(struct ipr_sglist *sglist)
{
- int i;
-
- for (i = 0; i < sglist->num_sg; i++)
- __free_pages(sg_page(&sglist->scatterlist[i]), sglist->order);
-
+ sgl_free_order(sglist->scatterlist, sglist->order);
kfree(sglist);
}
@@ -3562,22 +3824,20 @@ static int ipr_copy_ucode_buffer(struct ipr_sglist *sglist,
u8 *buffer, u32 len)
{
int bsize_elem, i, result = 0;
- struct scatterlist *scatterlist;
- void *kaddr;
+ struct scatterlist *sg;
/* Determine the actual number of bytes per element */
bsize_elem = PAGE_SIZE * (1 << sglist->order);
- scatterlist = sglist->scatterlist;
+ sg = sglist->scatterlist;
- for (i = 0; i < (len / bsize_elem); i++, buffer += bsize_elem) {
- struct page *page = sg_page(&scatterlist[i]);
+ for (i = 0; i < (len / bsize_elem); i++, sg = sg_next(sg),
+ buffer += bsize_elem) {
+ struct page *page = sg_page(sg);
- kaddr = kmap(page);
- memcpy(kaddr, buffer, bsize_elem);
- kunmap(page);
+ memcpy_to_page(page, 0, buffer, bsize_elem);
- scatterlist[i].length = bsize_elem;
+ sg->length = bsize_elem;
if (result != 0) {
ipr_trace;
@@ -3586,13 +3846,11 @@ static int ipr_copy_ucode_buffer(struct ipr_sglist *sglist,
}
if (len % bsize_elem) {
- struct page *page = sg_page(&scatterlist[i]);
+ struct page *page = sg_page(sg);
- kaddr = kmap(page);
- memcpy(kaddr, buffer, len % bsize_elem);
- kunmap(page);
+ memcpy_to_page(page, 0, buffer, len % bsize_elem);
- scatterlist[i].length = len % bsize_elem;
+ sg->length = len % bsize_elem;
}
sglist->buffer_len = len;
@@ -3613,6 +3871,7 @@ static void ipr_build_ucode_ioadl64(struct ipr_cmnd *ipr_cmd,
struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64;
struct scatterlist *scatterlist = sglist->scatterlist;
+ struct scatterlist *sg;
int i;
ipr_cmd->dma_use_sg = sglist->num_dma_sg;
@@ -3621,10 +3880,10 @@ static void ipr_build_ucode_ioadl64(struct ipr_cmnd *ipr_cmd,
ioarcb->ioadl_len =
cpu_to_be32(sizeof(struct ipr_ioadl64_desc) * ipr_cmd->dma_use_sg);
- for (i = 0; i < ipr_cmd->dma_use_sg; i++) {
+ for_each_sg(scatterlist, sg, ipr_cmd->dma_use_sg, i) {
ioadl64[i].flags = cpu_to_be32(IPR_IOADL_FLAGS_WRITE);
- ioadl64[i].data_len = cpu_to_be32(sg_dma_len(&scatterlist[i]));
- ioadl64[i].address = cpu_to_be64(sg_dma_address(&scatterlist[i]));
+ ioadl64[i].data_len = cpu_to_be32(sg_dma_len(sg));
+ ioadl64[i].address = cpu_to_be64(sg_dma_address(sg));
}
ioadl64[i-1].flags |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
@@ -3644,6 +3903,7 @@ static void ipr_build_ucode_ioadl(struct ipr_cmnd *ipr_cmd,
struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl;
struct scatterlist *scatterlist = sglist->scatterlist;
+ struct scatterlist *sg;
int i;
ipr_cmd->dma_use_sg = sglist->num_dma_sg;
@@ -3653,11 +3913,11 @@ static void ipr_build_ucode_ioadl(struct ipr_cmnd *ipr_cmd,
ioarcb->ioadl_len =
cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
- for (i = 0; i < ipr_cmd->dma_use_sg; i++) {
+ for_each_sg(scatterlist, sg, ipr_cmd->dma_use_sg, i) {
ioadl[i].flags_and_data_len =
- cpu_to_be32(IPR_IOADL_FLAGS_WRITE | sg_dma_len(&scatterlist[i]));
+ cpu_to_be32(IPR_IOADL_FLAGS_WRITE | sg_dma_len(sg));
ioadl[i].address =
- cpu_to_be32(sg_dma_address(&scatterlist[i]));
+ cpu_to_be32(sg_dma_address(sg));
}
ioadl[i-1].flags_and_data_len |=
@@ -3680,7 +3940,7 @@ static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg,
unsigned long lock_flags;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3693,8 +3953,9 @@ static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg,
return -EIO;
}
- sglist->num_dma_sg = pci_map_sg(ioa_cfg->pdev, sglist->scatterlist,
- sglist->num_sg, DMA_TO_DEVICE);
+ sglist->num_dma_sg = dma_map_sg(&ioa_cfg->pdev->dev,
+ sglist->scatterlist, sglist->num_sg,
+ DMA_TO_DEVICE);
if (!sglist->num_dma_sg) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
@@ -3716,7 +3977,8 @@ static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg,
/**
* ipr_store_update_fw - Update the firmware on the adapter
- * @class_dev: device struct
+ * @dev: device struct
+ * @attr: device attribute (unused)
* @buf: buffer
* @count: buffer size
*
@@ -3736,29 +3998,25 @@ static ssize_t ipr_store_update_fw(struct device *dev,
struct ipr_sglist *sglist;
char fname[100];
char *src;
- int len, result, dnld_size;
+ char *endline;
+ int result, dnld_size;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- len = snprintf(fname, 99, "%s", buf);
- fname[len-1] = '\0';
+ snprintf(fname, sizeof(fname), "%s", buf);
+
+ endline = strchr(fname, '\n');
+ if (endline)
+ *endline = '\0';
- if(request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
+ if (request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname);
return -EIO;
}
image_hdr = (struct ipr_ucode_image_header *)fw_entry->data;
- if (be32_to_cpu(image_hdr->header_length) > fw_entry->size ||
- (ioa_cfg->vpd_cbs->page3_data.card_type &&
- ioa_cfg->vpd_cbs->page3_data.card_type != image_hdr->card_type)) {
- dev_err(&ioa_cfg->pdev->dev, "Invalid microcode buffer\n");
- release_firmware(fw_entry);
- return -EINVAL;
- }
-
src = (u8 *)image_hdr + be32_to_cpu(image_hdr->header_length);
dnld_size = fw_entry->size - be32_to_cpu(image_hdr->header_length);
sglist = ipr_alloc_ucode_buffer(dnld_size);
@@ -3777,6 +4035,8 @@ static ssize_t ipr_store_update_fw(struct device *dev,
goto out;
}
+ ipr_info("Updating microcode, please be patient. This may take up to 30 minutes.\n");
+
result = ipr_update_ioa_ucode(ioa_cfg, sglist);
if (!result)
@@ -3798,6 +4058,7 @@ static struct device_attribute ipr_update_fw_attr = {
/**
* ipr_show_fw_type - Show the adapter's firmware type.
* @dev: class device struct
+ * @attr: device attribute (unused)
* @buf: buffer
*
* Return value:
@@ -3825,17 +4086,78 @@ static struct device_attribute ipr_ioa_fw_type_attr = {
.show = ipr_show_fw_type
};
-static struct device_attribute *ipr_ioa_attrs[] = {
- &ipr_fw_version_attr,
- &ipr_log_level_attr,
- &ipr_diagnostics_attr,
- &ipr_ioa_state_attr,
- &ipr_ioa_reset_attr,
- &ipr_update_fw_attr,
- &ipr_ioa_fw_type_attr,
+static ssize_t ipr_read_async_err_log(struct file *filep, struct kobject *kobj,
+ const struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct device *cdev = kobj_to_dev(kobj);
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ struct ipr_hostrcb *hostrcb;
+ unsigned long lock_flags = 0;
+ int ret;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q,
+ struct ipr_hostrcb, queue);
+ if (!hostrcb) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return 0;
+ }
+ ret = memory_read_from_buffer(buf, count, &off, &hostrcb->hcam,
+ sizeof(hostrcb->hcam));
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return ret;
+}
+
+static ssize_t ipr_next_async_err_log(struct file *filep, struct kobject *kobj,
+ const struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct device *cdev = kobj_to_dev(kobj);
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ struct ipr_hostrcb *hostrcb;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q,
+ struct ipr_hostrcb, queue);
+ if (!hostrcb) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return count;
+ }
+
+ /* Reclaim hostrcb before exit */
+ list_move_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return count;
+}
+
+static const struct bin_attribute ipr_ioa_async_err_log = {
+ .attr = {
+ .name = "async_err_log",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = 0,
+ .read = ipr_read_async_err_log,
+ .write = ipr_next_async_err_log
+};
+
+static struct attribute *ipr_ioa_attrs[] = {
+ &ipr_fw_version_attr.attr,
+ &ipr_log_level_attr.attr,
+ &ipr_diagnostics_attr.attr,
+ &ipr_ioa_state_attr.attr,
+ &ipr_ioa_reset_attr.attr,
+ &ipr_update_fw_attr.attr,
+ &ipr_ioa_fw_type_attr.attr,
+ &ipr_iopoll_weight_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(ipr_ioa);
+
#ifdef CONFIG_SCSI_IPR_DUMP
/**
* ipr_read_dump - Dump the adapter
@@ -3850,10 +4172,10 @@ static struct device_attribute *ipr_ioa_attrs[] = {
* number of bytes printed to buffer
**/
static ssize_t ipr_read_dump(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *cdev = container_of(kobj, struct device, kobj);
+ struct device *cdev = kobj_to_dev(kobj);
struct Scsi_Host *shost = class_to_shost(cdev);
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
struct ipr_dump *dump;
@@ -3959,9 +4281,11 @@ static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg)
}
if (ioa_cfg->sis64)
- ioa_data = vmalloc(IPR_FMT3_MAX_NUM_DUMP_PAGES * sizeof(__be32 *));
+ ioa_data = vmalloc_array(IPR_FMT3_MAX_NUM_DUMP_PAGES,
+ sizeof(__be32 *));
else
- ioa_data = vmalloc(IPR_FMT2_MAX_NUM_DUMP_PAGES * sizeof(__be32 *));
+ ioa_data = vmalloc_array(IPR_FMT2_MAX_NUM_DUMP_PAGES,
+ sizeof(__be32 *));
if (!ioa_data) {
ipr_err("Dump memory allocation failed\n");
@@ -3985,7 +4309,7 @@ static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg)
ioa_cfg->dump = dump;
ioa_cfg->sdt_state = WAIT_FOR_DUMP;
- if (ioa_cfg->ioa_is_dead && !ioa_cfg->dump_taken) {
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead && !ioa_cfg->dump_taken) {
ioa_cfg->dump_taken = 1;
schedule_work(&ioa_cfg->work_q);
}
@@ -4037,10 +4361,10 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg)
* number of bytes printed to buffer
**/
static ssize_t ipr_write_dump(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *cdev = container_of(kobj, struct device, kobj);
+ struct device *cdev = kobj_to_dev(kobj);
struct Scsi_Host *shost = class_to_shost(cdev);
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
int rc;
@@ -4061,7 +4385,7 @@ static ssize_t ipr_write_dump(struct file *filp, struct kobject *kobj,
return count;
}
-static struct bin_attribute ipr_dump_attr = {
+static const struct bin_attribute ipr_dump_attr = {
.attr = {
.name = "dump",
.mode = S_IRUSR | S_IWUSR,
@@ -4078,71 +4402,17 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; };
* ipr_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
- * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
+static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
- struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
- struct ipr_resource_entry *res;
- unsigned long lock_flags = 0;
-
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- res = (struct ipr_resource_entry *)sdev->hostdata;
-
- if (res && ipr_is_gata(res) && qdepth > IPR_MAX_CMD_PER_ATA_LUN)
- qdepth = IPR_MAX_CMD_PER_ATA_LUN;
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
-
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ scsi_change_queue_depth(sdev, qdepth);
return sdev->queue_depth;
}
/**
- * ipr_change_queue_type - Change the device's queue type
- * @dsev: scsi device struct
- * @tag_type: type of tags to use
- *
- * Return value:
- * actual queue type set
- **/
-static int ipr_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
- struct ipr_resource_entry *res;
- unsigned long lock_flags = 0;
-
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- res = (struct ipr_resource_entry *)sdev->hostdata;
-
- if (res) {
- if (ipr_is_gscsi(res) && sdev->tagged_supported) {
- /*
- * We don't bother quiescing the device here since the
- * adapter firmware does it for us.
- */
- scsi_set_tag_type(sdev, tag_type);
-
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
- } else
- tag_type = 0;
-
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return tag_type;
-}
-
-/**
* ipr_show_adapter_handle - Show the adapter's resource handle for this device
* @dev: device struct
* @attr: device attribute structure
@@ -4198,8 +4468,8 @@ static ssize_t ipr_show_resource_path(struct device *dev, struct device_attribut
res = (struct ipr_resource_entry *)sdev->hostdata;
if (res && ioa_cfg->sis64)
len = snprintf(buf, PAGE_SIZE, "%s\n",
- ipr_format_res_path(res->res_path, buffer,
- sizeof(buffer)));
+ __ipr_format_res_path(res->res_path, buffer,
+ sizeof(buffer)));
else if (res)
len = snprintf(buf, PAGE_SIZE, "%d:%d:%d:%d\n", ioa_cfg->host->host_no,
res->bus, res->target, res->lun);
@@ -4236,7 +4506,7 @@ static ssize_t ipr_show_device_id(struct device *dev, struct device_attribute *a
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
res = (struct ipr_resource_entry *)sdev->hostdata;
if (res && ioa_cfg->sis64)
- len = snprintf(buf, PAGE_SIZE, "0x%llx\n", res->dev_id);
+ len = snprintf(buf, PAGE_SIZE, "0x%llx\n", be64_to_cpu(res->dev_id));
else if (res)
len = snprintf(buf, PAGE_SIZE, "0x%llx\n", res->lun_wwn);
@@ -4287,20 +4557,97 @@ static struct device_attribute ipr_resource_type_attr = {
.show = ipr_show_resource_type
};
-static struct device_attribute *ipr_dev_attrs[] = {
- &ipr_adapter_handle_attr,
- &ipr_resource_path_attr,
- &ipr_device_id_attr,
- &ipr_resource_type_attr,
+/**
+ * ipr_show_raw_mode - Show the adapter's raw mode
+ * @dev: class device struct
+ * @attr: device attribute (unused)
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_show_raw_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags = 0;
+ ssize_t len;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = (struct ipr_resource_entry *)sdev->hostdata;
+ if (res)
+ len = snprintf(buf, PAGE_SIZE, "%d\n", res->raw_mode);
+ else
+ len = -ENXIO;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return len;
+}
+
+/**
+ * ipr_store_raw_mode - Change the adapter's raw mode
+ * @dev: class device struct
+ * @attr: device attribute (unused)
+ * @buf: buffer
+ * @count: buffer size
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_store_raw_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags = 0;
+ ssize_t len;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = (struct ipr_resource_entry *)sdev->hostdata;
+ if (res) {
+ if (ipr_is_af_dasd_device(res)) {
+ res->raw_mode = simple_strtoul(buf, NULL, 10);
+ len = strlen(buf);
+ if (res->sdev)
+ sdev_printk(KERN_INFO, res->sdev, "raw mode is %s\n",
+ res->raw_mode ? "enabled" : "disabled");
+ } else
+ len = -EINVAL;
+ } else
+ len = -ENXIO;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return len;
+}
+
+static struct device_attribute ipr_raw_mode_attr = {
+ .attr = {
+ .name = "raw_mode",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ipr_show_raw_mode,
+ .store = ipr_store_raw_mode
+};
+
+static struct attribute *ipr_dev_attrs[] = {
+ &ipr_adapter_handle_attr.attr,
+ &ipr_resource_path_attr.attr,
+ &ipr_device_id_attr.attr,
+ &ipr_resource_type_attr.attr,
+ &ipr_raw_mode_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(ipr_dev);
+
/**
* ipr_biosparam - Return the HSC mapping
- * @sdev: scsi device struct
- * @block_device: block device pointer
+ * @sdev: scsi device struct
+ * @unused: gendisk pointer
* @capacity: capacity of the device
- * @parm: Array containing returned HSC values.
+ * @parm: Array containing returned HSC values.
*
* This function generates the HSC parms that fdisk uses.
* We want to make sure we return something that places partitions
@@ -4310,7 +4657,7 @@ static struct device_attribute *ipr_dev_attrs[] = {
* 0 on success
**/
static int ipr_biosparam(struct scsi_device *sdev,
- struct block_device *block_device,
+ struct gendisk *unused,
sector_t capacity, int *parm)
{
int heads, sectors;
@@ -4345,8 +4692,7 @@ static struct ipr_resource_entry *ipr_find_starget(struct scsi_target *starget)
list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if ((res->bus == starget->channel) &&
- (res->target == starget->id) &&
- (res->lun == 0)) {
+ (res->target == starget->id)) {
return res;
}
}
@@ -4354,84 +4700,25 @@ static struct ipr_resource_entry *ipr_find_starget(struct scsi_target *starget)
return NULL;
}
-static struct ata_port_info sata_port_info;
-
-/**
- * ipr_target_alloc - Prepare for commands to a SCSI target
- * @starget: scsi target struct
- *
- * If the device is a SATA device, this function allocates an
- * ATA port with libata, else it does nothing.
- *
- * Return value:
- * 0 on success / non-0 on failure
- **/
-static int ipr_target_alloc(struct scsi_target *starget)
-{
- struct Scsi_Host *shost = dev_to_shost(&starget->dev);
- struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
- struct ipr_sata_port *sata_port;
- struct ata_port *ap;
- struct ipr_resource_entry *res;
- unsigned long lock_flags;
-
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- res = ipr_find_starget(starget);
- starget->hostdata = NULL;
-
- if (res && ipr_is_gata(res)) {
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- sata_port = kzalloc(sizeof(*sata_port), GFP_KERNEL);
- if (!sata_port)
- return -ENOMEM;
-
- ap = ata_sas_port_alloc(&ioa_cfg->ata_host, &sata_port_info, shost);
- if (ap) {
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- sata_port->ioa_cfg = ioa_cfg;
- sata_port->ap = ap;
- sata_port->res = res;
-
- res->sata_port = sata_port;
- ap->private_data = sata_port;
- starget->hostdata = sata_port;
- } else {
- kfree(sata_port);
- return -ENOMEM;
- }
- }
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
-
- return 0;
-}
-
/**
* ipr_target_destroy - Destroy a SCSI target
* @starget: scsi target struct
*
- * If the device was a SATA device, this function frees the libata
- * ATA port, else it does nothing.
- *
**/
static void ipr_target_destroy(struct scsi_target *starget)
{
- struct ipr_sata_port *sata_port = starget->hostdata;
struct Scsi_Host *shost = dev_to_shost(&starget->dev);
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
if (ioa_cfg->sis64) {
- if (starget->channel == IPR_ARRAY_VIRTUAL_BUS)
- clear_bit(starget->id, ioa_cfg->array_ids);
- else if (starget->channel == IPR_VSET_VIRTUAL_BUS)
- clear_bit(starget->id, ioa_cfg->vset_ids);
- else if (starget->channel == 0)
- clear_bit(starget->id, ioa_cfg->target_ids);
- }
-
- if (sata_port) {
- starget->hostdata = NULL;
- ata_sas_port_destroy(sata_port->ap);
- kfree(sata_port);
+ if (!ipr_find_starget(starget)) {
+ if (starget->channel == IPR_ARRAY_VIRTUAL_BUS)
+ clear_bit(starget->id, ioa_cfg->array_ids);
+ else if (starget->channel == IPR_VSET_VIRTUAL_BUS)
+ clear_bit(starget->id, ioa_cfg->vset_ids);
+ else if (starget->channel == 0)
+ clear_bit(starget->id, ioa_cfg->target_ids);
+ }
}
}
@@ -4458,13 +4745,13 @@ static struct ipr_resource_entry *ipr_find_sdev(struct scsi_device *sdev)
}
/**
- * ipr_slave_destroy - Unconfigure a SCSI device
+ * ipr_sdev_destroy - Unconfigure a SCSI device
* @sdev: scsi device struct
*
* Return value:
* nothing
**/
-static void ipr_slave_destroy(struct scsi_device *sdev)
+static void ipr_sdev_destroy(struct scsi_device *sdev)
{
struct ipr_resource_entry *res;
struct ipr_ioa_cfg *ioa_cfg;
@@ -4475,29 +4762,27 @@ static void ipr_slave_destroy(struct scsi_device *sdev)
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
res = (struct ipr_resource_entry *) sdev->hostdata;
if (res) {
- if (res->sata_port)
- res->sata_port->ap->link.device[0].class = ATA_DEV_NONE;
sdev->hostdata = NULL;
res->sdev = NULL;
- res->sata_port = NULL;
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
}
/**
- * ipr_slave_configure - Configure a SCSI device
+ * ipr_sdev_configure - Configure a SCSI device
* @sdev: scsi device struct
+ * @lim: queue limits
*
* This function configures the specified scsi device.
*
* Return value:
* 0 on success
**/
-static int ipr_slave_configure(struct scsi_device *sdev)
+static int ipr_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
struct ipr_resource_entry *res;
- struct ata_port *ap = NULL;
unsigned long lock_flags = 0;
char buffer[IPR_MAX_RES_PATH_LENGTH];
@@ -4511,23 +4796,18 @@ static int ipr_slave_configure(struct scsi_device *sdev)
sdev->no_uld_attach = 1;
}
if (ipr_is_vset_device(res)) {
+ sdev->scsi_level = SCSI_SPC_3;
+ sdev->no_report_opcodes = 1;
blk_queue_rq_timeout(sdev->request_queue,
IPR_VSET_RW_TIMEOUT);
- blk_queue_max_hw_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS);
+ lim->max_hw_sectors = IPR_VSET_MAX_SECTORS;
}
- if (ipr_is_gata(res) && res->sata_port)
- ap = res->sata_port->ap;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- if (ap) {
- scsi_adjust_queue_depth(sdev, 0, IPR_MAX_CMD_PER_ATA_LUN);
- ata_sas_slave_configure(sdev, ap);
- } else
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
if (ioa_cfg->sis64)
sdev_printk(KERN_INFO, sdev, "Resource path: %s\n",
- ipr_format_res_path(res->res_path, buffer,
- sizeof(buffer)));
+ ipr_format_res_path(ioa_cfg,
+ res->res_path, buffer, sizeof(buffer)));
return 0;
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
@@ -4535,34 +4815,7 @@ static int ipr_slave_configure(struct scsi_device *sdev)
}
/**
- * ipr_ata_slave_alloc - Prepare for commands to a SATA device
- * @sdev: scsi device struct
- *
- * This function initializes an ATA port so that future commands
- * sent through queuecommand will work.
- *
- * Return value:
- * 0 on success
- **/
-static int ipr_ata_slave_alloc(struct scsi_device *sdev)
-{
- struct ipr_sata_port *sata_port = NULL;
- int rc = -ENXIO;
-
- ENTER;
- if (sdev->sdev_target)
- sata_port = sdev->sdev_target->hostdata;
- if (sata_port)
- rc = ata_sas_port_init(sata_port->ap);
- if (rc)
- ipr_slave_destroy(sdev);
-
- LEAVE;
- return rc;
-}
-
-/**
- * ipr_slave_alloc - Prepare for commands to a device.
+ * ipr_sdev_init - Prepare for commands to a device.
* @sdev: scsi device struct
*
* This function saves a pointer to the resource entry
@@ -4573,7 +4826,7 @@ static int ipr_ata_slave_alloc(struct scsi_device *sdev)
* Return value:
* 0 on success / -ENXIO if device does not exist
**/
-static int ipr_slave_alloc(struct scsi_device *sdev)
+static int ipr_sdev_init(struct scsi_device *sdev)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
struct ipr_resource_entry *res;
@@ -4594,8 +4847,10 @@ static int ipr_slave_alloc(struct scsi_device *sdev)
res->needs_sync_complete = 1;
rc = 0;
if (ipr_is_gata(res)) {
+ sdev_printk(KERN_ERR, sdev, "SATA devices are no longer "
+ "supported by this driver. Skipping device.\n");
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return ipr_ata_slave_alloc(sdev);
+ return -ENXIO;
}
}
@@ -4605,40 +4860,140 @@ static int ipr_slave_alloc(struct scsi_device *sdev)
}
/**
- * ipr_eh_host_reset - Reset the host adapter
- * @scsi_cmd: scsi command struct
+ * ipr_match_lun - Match function for specified LUN
+ * @ipr_cmd: ipr command struct
+ * @device: device to match (sdev)
*
- * Return value:
- * SUCCESS / FAILED
+ * Returns:
+ * 1 if command matches sdev / 0 if command does not match sdev
**/
-static int __ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
+static int ipr_match_lun(struct ipr_cmnd *ipr_cmd, void *device)
{
- struct ipr_ioa_cfg *ioa_cfg;
- int rc;
+ if (ipr_cmd->scsi_cmd && ipr_cmd->scsi_cmd->device == device)
+ return 1;
+ return 0;
+}
- ENTER;
- ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
+/**
+ * ipr_cmnd_is_free - Check if a command is free or not
+ * @ipr_cmd: ipr command struct
+ *
+ * Returns:
+ * true / false
+ **/
+static bool ipr_cmnd_is_free(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_cmnd *loop_cmd;
- dev_err(&ioa_cfg->pdev->dev,
- "Adapter being reset as a result of error recovery.\n");
+ list_for_each_entry(loop_cmd, &ipr_cmd->hrrq->hrrq_free_q, queue) {
+ if (loop_cmd == ipr_cmd)
+ return true;
+ }
- if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
- ioa_cfg->sdt_state = GET_DUMP;
+ return false;
+}
- rc = ipr_reset_reload(ioa_cfg, IPR_SHUTDOWN_ABBREV);
+/**
+ * ipr_wait_for_ops - Wait for matching commands to complete
+ * @ioa_cfg: ioa config struct
+ * @device: device to match (sdev)
+ * @match: match function to use
+ *
+ * Returns:
+ * SUCCESS / FAILED
+ **/
+static int ipr_wait_for_ops(struct ipr_ioa_cfg *ioa_cfg, void *device,
+ int (*match)(struct ipr_cmnd *, void *))
+{
+ struct ipr_cmnd *ipr_cmd;
+ int wait, i;
+ unsigned long flags;
+ struct ipr_hrr_queue *hrrq;
+ signed long timeout = IPR_ABORT_TASK_TIMEOUT;
+ DECLARE_COMPLETION_ONSTACK(comp);
+
+ ENTER;
+ do {
+ wait = 0;
+
+ for_each_hrrq(hrrq, ioa_cfg) {
+ spin_lock_irqsave(hrrq->lock, flags);
+ for (i = hrrq->min_cmd_id; i <= hrrq->max_cmd_id; i++) {
+ ipr_cmd = ioa_cfg->ipr_cmnd_list[i];
+ if (!ipr_cmnd_is_free(ipr_cmd)) {
+ if (match(ipr_cmd, device)) {
+ ipr_cmd->eh_comp = &comp;
+ wait++;
+ }
+ }
+ }
+ spin_unlock_irqrestore(hrrq->lock, flags);
+ }
+
+ if (wait) {
+ timeout = wait_for_completion_timeout(&comp, timeout);
+
+ if (!timeout) {
+ wait = 0;
+
+ for_each_hrrq(hrrq, ioa_cfg) {
+ spin_lock_irqsave(hrrq->lock, flags);
+ for (i = hrrq->min_cmd_id; i <= hrrq->max_cmd_id; i++) {
+ ipr_cmd = ioa_cfg->ipr_cmnd_list[i];
+ if (!ipr_cmnd_is_free(ipr_cmd)) {
+ if (match(ipr_cmd, device)) {
+ ipr_cmd->eh_comp = NULL;
+ wait++;
+ }
+ }
+ }
+ spin_unlock_irqrestore(hrrq->lock, flags);
+ }
+
+ if (wait)
+ dev_err(&ioa_cfg->pdev->dev, "Timed out waiting for aborted commands\n");
+ LEAVE;
+ return wait ? FAILED : SUCCESS;
+ }
+ }
+ } while (wait);
LEAVE;
- return rc;
+ return SUCCESS;
}
-static int ipr_eh_host_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_host_reset(struct scsi_cmnd *cmd)
{
- int rc;
+ struct ipr_ioa_cfg *ioa_cfg;
+ unsigned long lock_flags = 0;
+ int rc = SUCCESS;
- spin_lock_irq(cmd->device->host->host_lock);
- rc = __ipr_eh_host_reset(cmd);
- spin_unlock_irq(cmd->device->host->host_lock);
+ ENTER;
+ ioa_cfg = (struct ipr_ioa_cfg *) cmd->device->host->hostdata;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ if (!ioa_cfg->in_reset_reload && !ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead) {
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_ABBREV);
+ dev_err(&ioa_cfg->pdev->dev,
+ "Adapter being reset as a result of error recovery.\n");
+
+ if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
+ ioa_cfg->sdt_state = GET_DUMP;
+ }
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ /* If we got hit with a host reset while we were already resetting
+ the adapter for some reason, and the reset failed. */
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead) {
+ ipr_trace;
+ rc = FAILED;
+ }
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ LEAVE;
return rc;
}
@@ -4650,8 +5005,7 @@ static int ipr_eh_host_reset(struct scsi_cmnd * cmd)
* This function issues a device reset to the affected device.
* If the device is a SCSI device, a LUN reset will be sent
* to the device first. If that does not work, a target reset
- * will be sent. If the device is a SATA device, a PHY reset will
- * be sent.
+ * will be sent.
*
* Return value:
* 0 on success / non-zero on failure
@@ -4662,7 +5016,6 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
struct ipr_cmnd *ipr_cmd;
struct ipr_ioarcb *ioarcb;
struct ipr_cmd_pkt *cmd_pkt;
- struct ipr_ioarcb_ata_regs *regs;
u32 ioasc;
ENTER;
@@ -4670,77 +5023,23 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
ioarcb = &ipr_cmd->ioarcb;
cmd_pkt = &ioarcb->cmd_pkt;
- if (ipr_cmd->ioa_cfg->sis64) {
- regs = &ipr_cmd->i.ata_ioadl.regs;
+ if (ipr_cmd->ioa_cfg->sis64)
ioarcb->add_cmd_parms_offset = cpu_to_be16(sizeof(*ioarcb));
- } else
- regs = &ioarcb->u.add_data.u.regs;
ioarcb->res_handle = res->res_handle;
cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
cmd_pkt->cdb[0] = IPR_RESET_DEVICE;
- if (ipr_is_gata(res)) {
- cmd_pkt->cdb[2] = IPR_ATA_PHY_RESET;
- ioarcb->add_cmd_parms_len = cpu_to_be16(sizeof(regs->flags));
- regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION;
- }
ipr_send_blocking_cmd(ipr_cmd, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- if (ipr_is_gata(res) && res->sata_port && ioasc != IPR_IOASC_IOA_WAS_RESET) {
- if (ipr_cmd->ioa_cfg->sis64)
- memcpy(&res->sata_port->ioasa, &ipr_cmd->s.ioasa64.u.gata,
- sizeof(struct ipr_ioasa_gata));
- else
- memcpy(&res->sata_port->ioasa, &ipr_cmd->s.ioasa.u.gata,
- sizeof(struct ipr_ioasa_gata));
- }
-
- LEAVE;
- return (IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0);
-}
-
-/**
- * ipr_sata_reset - Reset the SATA port
- * @link: SATA link to reset
- * @classes: class of the attached device
- *
- * This function issues a SATA phy reset to the affected ATA link.
- *
- * Return value:
- * 0 on success / non-zero on failure
- **/
-static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
- unsigned long deadline)
-{
- struct ipr_sata_port *sata_port = link->ap->private_data;
- struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
- struct ipr_resource_entry *res;
- unsigned long lock_flags = 0;
- int rc = -ENXIO;
-
- ENTER;
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- }
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
- res = sata_port->res;
- if (res) {
- rc = ipr_device_reset(ioa_cfg, res);
- *classes = res->ata_class;
- }
-
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
LEAVE;
- return rc;
+ return IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0;
}
/**
- * ipr_eh_dev_reset - Reset the device
+ * __ipr_eh_dev_reset - Reset the device
* @scsi_cmd: scsi command struct
*
* This function issues a device reset to the affected device.
@@ -4750,21 +5049,16 @@ static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
* Return value:
* SUCCESS / FAILED
**/
-static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
+static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
{
- struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
struct ipr_resource_entry *res;
- struct ata_port *ap;
int rc = 0;
ENTER;
ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
res = scsi_cmd->device->hostdata;
- if (!res)
- return FAILED;
-
/*
* If we are currently going through reset/reload, return failed. This will force the
* mid-layer to call ipr_eh_host_reset, which will then go to sleep and wait for the
@@ -4772,53 +5066,39 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
*/
if (ioa_cfg->in_reset_reload)
return FAILED;
- if (ioa_cfg->ioa_is_dead)
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
return FAILED;
- list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
- if (ipr_cmd->ioarcb.res_handle == res->res_handle) {
- if (ipr_cmd->scsi_cmd)
- ipr_cmd->done = ipr_scsi_eh_done;
- if (ipr_cmd->qc)
- ipr_cmd->done = ipr_sata_eh_done;
- if (ipr_cmd->qc && !(ipr_cmd->qc->flags & ATA_QCFLAG_FAILED)) {
- ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT;
- ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED;
- }
- }
- }
-
res->resetting_device = 1;
scmd_printk(KERN_ERR, scsi_cmd, "Resetting device\n");
- if (ipr_is_gata(res) && res->sata_port) {
- ap = res->sata_port->ap;
- spin_unlock_irq(scsi_cmd->device->host->host_lock);
- ata_std_error_handler(ap);
- spin_lock_irq(scsi_cmd->device->host->host_lock);
-
- list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
- if (ipr_cmd->ioarcb.res_handle == res->res_handle) {
- rc = -EIO;
- break;
- }
- }
- } else
- rc = ipr_device_reset(ioa_cfg, res);
+ rc = ipr_device_reset(ioa_cfg, res);
res->resetting_device = 0;
+ res->reset_occurred = 1;
LEAVE;
- return (rc ? FAILED : SUCCESS);
+ return rc ? FAILED : SUCCESS;
}
-static int ipr_eh_dev_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_dev_reset(struct scsi_cmnd *cmd)
{
int rc;
+ struct ipr_ioa_cfg *ioa_cfg;
+ struct ipr_resource_entry *res;
+
+ ioa_cfg = (struct ipr_ioa_cfg *) cmd->device->host->hostdata;
+ res = cmd->device->hostdata;
+
+ if (!res)
+ return FAILED;
spin_lock_irq(cmd->device->host->host_lock);
rc = __ipr_eh_dev_reset(cmd);
spin_unlock_irq(cmd->device->host->host_lock);
+ if (rc == SUCCESS)
+ rc = ipr_wait_for_ops(ioa_cfg, cmd->device, ipr_match_lun);
+
return rc;
}
@@ -4854,13 +5134,13 @@ static void ipr_bus_reset_done(struct ipr_cmnd *ipr_cmd)
else
ipr_cmd->sibling->done(ipr_cmd->sibling);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
LEAVE;
}
/**
* ipr_abort_timeout - An abort task has timed out
- * @ipr_cmd: ipr command struct
+ * @t: Timer context used to fetch ipr command struct
*
* This function handles when an abort task times out. If this
* happens we issue a bus reset since we have resources tied
@@ -4869,8 +5149,9 @@ static void ipr_bus_reset_done(struct ipr_cmnd *ipr_cmd)
* Return value:
* none
**/
-static void ipr_abort_timeout(struct ipr_cmnd *ipr_cmd)
+static void ipr_abort_timeout(struct timer_list *t)
{
+ struct ipr_cmnd *ipr_cmd = timer_container_of(ipr_cmd, t, timer);
struct ipr_cmnd *reset_cmd;
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct ipr_cmd_pkt *cmd_pkt;
@@ -4907,14 +5188,15 @@ static void ipr_abort_timeout(struct ipr_cmnd *ipr_cmd)
* Return value:
* SUCCESS / FAILED
**/
-static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
+static int ipr_cancel_op(struct scsi_cmnd *scsi_cmd)
{
struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
struct ipr_resource_entry *res;
struct ipr_cmd_pkt *cmd_pkt;
u32 ioasc;
- int op_found = 0;
+ int i, op_found = 0;
+ struct ipr_hrr_queue *hrrq;
ENTER;
ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata;
@@ -4924,17 +5206,33 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
* This will force the mid-layer to call ipr_eh_host_reset,
* which will then go to sleep and wait for the reset to complete
*/
- if (ioa_cfg->in_reset_reload || ioa_cfg->ioa_is_dead)
+ if (ioa_cfg->in_reset_reload ||
+ ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
return FAILED;
- if (!res || !ipr_is_gscsi(res))
+ if (!res)
return FAILED;
- list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
- if (ipr_cmd->scsi_cmd == scsi_cmd) {
- ipr_cmd->done = ipr_scsi_eh_done;
- op_found = 1;
- break;
+ /*
+ * If we are aborting a timed out op, chances are that the timeout was caused
+ * by a still not detected EEH error. In such cases, reading a register will
+ * trigger the EEH recovery infrastructure.
+ */
+ readl(ioa_cfg->regs.sense_interrupt_reg);
+
+ if (!ipr_is_gscsi(res))
+ return FAILED;
+
+ for_each_hrrq(hrrq, ioa_cfg) {
+ spin_lock(&hrrq->_lock);
+ for (i = hrrq->min_cmd_id; i <= hrrq->max_cmd_id; i++) {
+ if (ioa_cfg->ipr_cmnd_list[i]->scsi_cmd == scsi_cmd) {
+ if (!ipr_cmnd_is_free(ioa_cfg->ipr_cmnd_list[i])) {
+ op_found = 1;
+ break;
+ }
+ }
}
+ spin_unlock(&hrrq->_lock);
}
if (!op_found)
@@ -4961,32 +5259,60 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
ipr_trace;
}
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
if (!ipr_is_naca_model(res))
res->needs_sync_complete = 1;
LEAVE;
- return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS);
+ return IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
}
/**
- * ipr_eh_abort - Abort a single op
+ * ipr_scan_finished - Report whether scan is done
+ * @shost: scsi host struct
+ * @elapsed_time: elapsed time
+ *
+ * Return value:
+ * 0 if scan in progress / 1 if scan is complete
+ **/
+static int ipr_scan_finished(struct Scsi_Host *shost, unsigned long elapsed_time)
+{
+ unsigned long lock_flags;
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
+ int rc = 0;
+
+ spin_lock_irqsave(shost->host_lock, lock_flags);
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead || ioa_cfg->scan_done)
+ rc = 1;
+ if ((elapsed_time/HZ) > (ioa_cfg->transop_timeout * 2))
+ rc = 1;
+ spin_unlock_irqrestore(shost->host_lock, lock_flags);
+ return rc;
+}
+
+/**
+ * ipr_eh_abort - Reset the host adapter
* @scsi_cmd: scsi command struct
*
* Return value:
* SUCCESS / FAILED
**/
-static int ipr_eh_abort(struct scsi_cmnd * scsi_cmd)
+static int ipr_eh_abort(struct scsi_cmnd *scsi_cmd)
{
unsigned long flags;
int rc;
+ struct ipr_ioa_cfg *ioa_cfg;
ENTER;
+ ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
+
spin_lock_irqsave(scsi_cmd->device->host->host_lock, flags);
rc = ipr_cancel_op(scsi_cmd);
spin_unlock_irqrestore(scsi_cmd->device->host->host_lock, flags);
+ if (rc == SUCCESS)
+ rc = ipr_wait_for_ops(ioa_cfg, scsi_cmd->device, ipr_match_lun);
LEAVE;
return rc;
}
@@ -5021,7 +5347,7 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
writel(IPR_PCII_IPL_STAGE_CHANGE, ioa_cfg->regs.clr_interrupt_reg);
int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
list_del(&ioa_cfg->reset_cmd->queue);
- del_timer(&ioa_cfg->reset_cmd->timer);
+ timer_delete(&ioa_cfg->reset_cmd->timer);
ipr_reset_ioa_job(ioa_cfg->reset_cmd);
return IRQ_HANDLED;
}
@@ -5033,24 +5359,26 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) {
/* Mask the interrupt */
writel(IPR_PCII_IOA_TRANS_TO_OPER, ioa_cfg->regs.set_interrupt_mask_reg);
-
- /* Clear the interrupt */
- writel(IPR_PCII_IOA_TRANS_TO_OPER, ioa_cfg->regs.clr_interrupt_reg);
int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
list_del(&ioa_cfg->reset_cmd->queue);
- del_timer(&ioa_cfg->reset_cmd->timer);
+ timer_delete(&ioa_cfg->reset_cmd->timer);
ipr_reset_ioa_job(ioa_cfg->reset_cmd);
} else if ((int_reg & IPR_PCII_HRRQ_UPDATED) == int_reg) {
- if (ipr_debug && printk_ratelimit())
- dev_err(&ioa_cfg->pdev->dev,
- "Spurious interrupt detected. 0x%08X\n", int_reg);
- writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
- return IRQ_NONE;
+ if (ioa_cfg->clear_isr) {
+ if (ipr_debug && printk_ratelimit())
+ dev_err(&ioa_cfg->pdev->dev,
+ "Spurious interrupt detected. 0x%08X\n", int_reg);
+ writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
+ return IRQ_NONE;
+ }
} else {
if (int_reg & IPR_PCII_IOA_UNIT_CHECKED)
ioa_cfg->ioa_unit_checked = 1;
+ else if (int_reg & IPR_PCII_NO_HOST_RRQ)
+ dev_err(&ioa_cfg->pdev->dev,
+ "No Host RRQ. 0x%08X\n", int_reg);
else
dev_err(&ioa_cfg->pdev->dev,
"Permanent IOA failure. 0x%08X\n", int_reg);
@@ -5069,14 +5397,15 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
* ipr_isr_eh - Interrupt service routine error handler
* @ioa_cfg: ioa config struct
* @msg: message to log
+ * @number: various meanings depending on the caller/message
*
* Return value:
* none
**/
-static void ipr_isr_eh(struct ipr_ioa_cfg *ioa_cfg, char *msg)
+static void ipr_isr_eh(struct ipr_ioa_cfg *ioa_cfg, char *msg, u16 number)
{
ioa_cfg->errors_logged++;
- dev_err(&ioa_cfg->pdev->dev, "%s\n", msg);
+ dev_err(&ioa_cfg->pdev->dev, "%s %d\n", msg, number);
if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
ioa_cfg->sdt_state = GET_DUMP;
@@ -5084,6 +5413,81 @@ static void ipr_isr_eh(struct ipr_ioa_cfg *ioa_cfg, char *msg)
ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
}
+static int ipr_process_hrrq(struct ipr_hrr_queue *hrr_queue, int budget,
+ struct list_head *doneq)
+{
+ u32 ioasc;
+ u16 cmd_index;
+ struct ipr_cmnd *ipr_cmd;
+ struct ipr_ioa_cfg *ioa_cfg = hrr_queue->ioa_cfg;
+ int num_hrrq = 0;
+
+ /* If interrupts are disabled, ignore the interrupt */
+ if (!hrr_queue->allow_interrupts)
+ return 0;
+
+ while ((be32_to_cpu(*hrr_queue->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
+ hrr_queue->toggle_bit) {
+
+ cmd_index = (be32_to_cpu(*hrr_queue->hrrq_curr) &
+ IPR_HRRQ_REQ_RESP_HANDLE_MASK) >>
+ IPR_HRRQ_REQ_RESP_HANDLE_SHIFT;
+
+ if (unlikely(cmd_index > hrr_queue->max_cmd_id ||
+ cmd_index < hrr_queue->min_cmd_id)) {
+ ipr_isr_eh(ioa_cfg,
+ "Invalid response handle from IOA: ",
+ cmd_index);
+ break;
+ }
+
+ ipr_cmd = ioa_cfg->ipr_cmnd_list[cmd_index];
+ ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
+
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, ioasc);
+
+ list_move_tail(&ipr_cmd->queue, doneq);
+
+ if (hrr_queue->hrrq_curr < hrr_queue->hrrq_end) {
+ hrr_queue->hrrq_curr++;
+ } else {
+ hrr_queue->hrrq_curr = hrr_queue->hrrq_start;
+ hrr_queue->toggle_bit ^= 1u;
+ }
+ num_hrrq++;
+ if (budget > 0 && num_hrrq >= budget)
+ break;
+ }
+
+ return num_hrrq;
+}
+
+static int ipr_iopoll(struct irq_poll *iop, int budget)
+{
+ struct ipr_hrr_queue *hrrq;
+ struct ipr_cmnd *ipr_cmd, *temp;
+ unsigned long hrrq_flags;
+ int completed_ops;
+ LIST_HEAD(doneq);
+
+ hrrq = container_of(iop, struct ipr_hrr_queue, iopoll);
+
+ spin_lock_irqsave(hrrq->lock, hrrq_flags);
+ completed_ops = ipr_process_hrrq(hrrq, budget, &doneq);
+
+ if (completed_ops < budget)
+ irq_poll_complete(iop);
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+
+ list_for_each_entry_safe(ipr_cmd, temp, &doneq, queue) {
+ list_del(&ipr_cmd->queue);
+ timer_delete(&ipr_cmd->timer);
+ ipr_cmd->fast_done(ipr_cmd);
+ }
+
+ return completed_ops;
+}
+
/**
* ipr_isr - Interrupt service routine
* @irq: irq number
@@ -5094,76 +5498,48 @@ static void ipr_isr_eh(struct ipr_ioa_cfg *ioa_cfg, char *msg)
**/
static irqreturn_t ipr_isr(int irq, void *devp)
{
- struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp;
- unsigned long lock_flags = 0;
+ struct ipr_hrr_queue *hrrq = (struct ipr_hrr_queue *)devp;
+ struct ipr_ioa_cfg *ioa_cfg = hrrq->ioa_cfg;
+ unsigned long hrrq_flags = 0;
u32 int_reg = 0;
- u32 ioasc;
- u16 cmd_index;
int num_hrrq = 0;
int irq_none = 0;
- struct ipr_cmnd *ipr_cmd;
+ struct ipr_cmnd *ipr_cmd, *temp;
irqreturn_t rc = IRQ_NONE;
+ LIST_HEAD(doneq);
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
-
+ spin_lock_irqsave(hrrq->lock, hrrq_flags);
/* If interrupts are disabled, ignore the interrupt */
- if (!ioa_cfg->allow_interrupts) {
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ if (!hrrq->allow_interrupts) {
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
return IRQ_NONE;
}
while (1) {
- ipr_cmd = NULL;
-
- while ((be32_to_cpu(*ioa_cfg->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
- ioa_cfg->toggle_bit) {
-
- cmd_index = (be32_to_cpu(*ioa_cfg->hrrq_curr) &
- IPR_HRRQ_REQ_RESP_HANDLE_MASK) >> IPR_HRRQ_REQ_RESP_HANDLE_SHIFT;
-
- if (unlikely(cmd_index >= IPR_NUM_CMD_BLKS)) {
- ipr_isr_eh(ioa_cfg, "Invalid response handle from IOA");
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return IRQ_HANDLED;
- }
-
- ipr_cmd = ioa_cfg->ipr_cmnd_list[cmd_index];
-
- ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
-
- ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, ioasc);
+ if (ipr_process_hrrq(hrrq, -1, &doneq)) {
+ rc = IRQ_HANDLED;
- list_del(&ipr_cmd->queue);
- del_timer(&ipr_cmd->timer);
- ipr_cmd->done(ipr_cmd);
-
- rc = IRQ_HANDLED;
-
- if (ioa_cfg->hrrq_curr < ioa_cfg->hrrq_end) {
- ioa_cfg->hrrq_curr++;
- } else {
- ioa_cfg->hrrq_curr = ioa_cfg->hrrq_start;
- ioa_cfg->toggle_bit ^= 1u;
- }
- }
+ if (!ioa_cfg->clear_isr)
+ break;
- if (ipr_cmd != NULL) {
/* Clear the PCI interrupt */
num_hrrq = 0;
do {
- writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
+ writel(IPR_PCII_HRRQ_UPDATED,
+ ioa_cfg->regs.clr_interrupt_reg32);
int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
} while (int_reg & IPR_PCII_HRRQ_UPDATED &&
- num_hrrq++ < IPR_MAX_HRRQ_RETRIES);
+ num_hrrq++ < IPR_MAX_HRRQ_RETRIES);
} else if (rc == IRQ_NONE && irq_none == 0) {
int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
irq_none++;
} else if (num_hrrq == IPR_MAX_HRRQ_RETRIES &&
int_reg & IPR_PCII_HRRQ_UPDATED) {
- ipr_isr_eh(ioa_cfg, "Error clearing HRRQ");
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return IRQ_HANDLED;
+ ipr_isr_eh(ioa_cfg,
+ "Error clearing HRRQ: ", num_hrrq);
+ rc = IRQ_HANDLED;
+ break;
} else
break;
}
@@ -5171,7 +5547,62 @@ static irqreturn_t ipr_isr(int irq, void *devp)
if (unlikely(rc == IRQ_NONE))
rc = ipr_handle_other_interrupt(ioa_cfg, int_reg);
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ list_for_each_entry_safe(ipr_cmd, temp, &doneq, queue) {
+ list_del(&ipr_cmd->queue);
+ timer_delete(&ipr_cmd->timer);
+ ipr_cmd->fast_done(ipr_cmd);
+ }
+ return rc;
+}
+
+/**
+ * ipr_isr_mhrrq - Interrupt service routine
+ * @irq: irq number
+ * @devp: pointer to ioa config struct
+ *
+ * Return value:
+ * IRQ_NONE / IRQ_HANDLED
+ **/
+static irqreturn_t ipr_isr_mhrrq(int irq, void *devp)
+{
+ struct ipr_hrr_queue *hrrq = (struct ipr_hrr_queue *)devp;
+ struct ipr_ioa_cfg *ioa_cfg = hrrq->ioa_cfg;
+ unsigned long hrrq_flags = 0;
+ struct ipr_cmnd *ipr_cmd, *temp;
+ irqreturn_t rc = IRQ_NONE;
+ LIST_HEAD(doneq);
+
+ spin_lock_irqsave(hrrq->lock, hrrq_flags);
+
+ /* If interrupts are disabled, ignore the interrupt */
+ if (!hrrq->allow_interrupts) {
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ return IRQ_NONE;
+ }
+
+ if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
+ if ((be32_to_cpu(*hrrq->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
+ hrrq->toggle_bit) {
+ irq_poll_sched(&hrrq->iopoll);
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ return IRQ_HANDLED;
+ }
+ } else {
+ if ((be32_to_cpu(*hrrq->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
+ hrrq->toggle_bit)
+
+ if (ipr_process_hrrq(hrrq, -1, &doneq))
+ rc = IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+
+ list_for_each_entry_safe(ipr_cmd, temp, &doneq, queue) {
+ list_del(&ipr_cmd->queue);
+ timer_delete(&ipr_cmd->timer);
+ ipr_cmd->fast_done(ipr_cmd);
+ }
return rc;
}
@@ -5201,7 +5632,7 @@ static int ipr_build_ioadl64(struct ipr_ioa_cfg *ioa_cfg,
nseg = scsi_dma_map(scsi_cmd);
if (nseg < 0) {
if (printk_ratelimit())
- dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
+ dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n");
return -1;
}
@@ -5252,7 +5683,7 @@ static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg,
nseg = scsi_dma_map(scsi_cmd);
if (nseg < 0) {
- dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
+ dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n");
return -1;
}
@@ -5289,36 +5720,7 @@ static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg,
}
/**
- * ipr_get_task_attributes - Translate SPI Q-Tag to task attributes
- * @scsi_cmd: scsi command struct
- *
- * Return value:
- * task attributes
- **/
-static u8 ipr_get_task_attributes(struct scsi_cmnd *scsi_cmd)
-{
- u8 tag[2];
- u8 rc = IPR_FLAGS_LO_UNTAGGED_TASK;
-
- if (scsi_populate_tag_msg(scsi_cmd, tag)) {
- switch (tag[0]) {
- case MSG_SIMPLE_TAG:
- rc = IPR_FLAGS_LO_SIMPLE_TASK;
- break;
- case MSG_HEAD_TAG:
- rc = IPR_FLAGS_LO_HEAD_OF_Q_TASK;
- break;
- case MSG_ORDERED_TAG:
- rc = IPR_FLAGS_LO_ORDERED_TASK;
- break;
- };
- }
-
- return rc;
-}
-
-/**
- * ipr_erp_done - Process completion of ERP for a device
+ * __ipr_erp_done - Process completion of ERP for a device
* @ipr_cmd: ipr command struct
*
* This function copies the sense buffer into the scsi_cmd
@@ -5327,11 +5729,10 @@ static u8 ipr_get_task_attributes(struct scsi_cmnd *scsi_cmd)
* Return value:
* nothing
**/
-static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
+static void __ipr_erp_done(struct ipr_cmnd *ipr_cmd)
{
struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
- struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
@@ -5349,8 +5750,30 @@ static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
res->in_erp = 0;
}
scsi_dma_unmap(ipr_cmd->scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
+}
+
+/**
+ * ipr_erp_done - Process completion of ERP for a device
+ * @ipr_cmd: ipr command struct
+ *
+ * This function copies the sense buffer into the scsi_cmd
+ * struct and pushes the scsi_done function.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
+ unsigned long hrrq_flags;
+
+ spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
+ __ipr_erp_done(ipr_cmd);
+ spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
}
/**
@@ -5385,7 +5808,7 @@ static void ipr_reinit_ipr_cmnd_for_erp(struct ipr_cmnd *ipr_cmd)
}
/**
- * ipr_erp_request_sense - Send request sense to a device
+ * __ipr_erp_request_sense - Send request sense to a device
* @ipr_cmd: ipr command struct
*
* This function sends a request sense to a device as a result
@@ -5394,13 +5817,13 @@ static void ipr_reinit_ipr_cmnd_for_erp(struct ipr_cmnd *ipr_cmd)
* Return value:
* nothing
**/
-static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
+static void __ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
{
struct ipr_cmd_pkt *cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
- ipr_erp_done(ipr_cmd);
+ __ipr_erp_done(ipr_cmd);
return;
}
@@ -5421,6 +5844,26 @@ static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
}
/**
+ * ipr_erp_request_sense - Send request sense to a device
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sends a request sense to a device as a result
+ * of a check condition.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_hrr_queue *hrrq = ipr_cmd->hrrq;
+ unsigned long hrrq_flags;
+
+ spin_lock_irqsave(&hrrq->_lock, hrrq_flags);
+ __ipr_erp_request_sense(ipr_cmd);
+ spin_unlock_irqrestore(&hrrq->_lock, hrrq_flags);
+}
+
+/**
* ipr_erp_cancel_all - Send cancel all to a device
* @ipr_cmd: ipr command struct
*
@@ -5442,8 +5885,8 @@ static void ipr_erp_cancel_all(struct ipr_cmnd *ipr_cmd)
ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
- if (!scsi_get_tag_type(scsi_cmd->device)) {
- ipr_erp_request_sense(ipr_cmd);
+ if (!scsi_cmd->device->simple_tags) {
+ __ipr_erp_request_sense(ipr_cmd);
return;
}
@@ -5525,8 +5968,7 @@ static void ipr_dump_ioasa(struct ipr_ioa_cfg *ioa_cfg,
/**
* ipr_gen_sense - Generate SCSI sense data from an IOASA
- * @ioasa: IOASA
- * @sense_buf: sense data buffer
+ * @ipr_cmd: ipr command struct
*
* Return value:
* none
@@ -5663,7 +6105,7 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
u32 masked_ioasc = ioasc & IPR_IOASC_IOASC_MASK;
if (!res) {
- ipr_scsi_eh_done(ipr_cmd);
+ __ipr_scsi_eh_done(ipr_cmd);
return;
}
@@ -5695,7 +6137,12 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
break;
case IPR_IOASC_MED_DO_NOT_REALLOC: /* prevent retries */
case IPR_IOASA_IR_DUAL_IOA_DISABLED:
- scsi_cmd->result |= (DID_PASSTHROUGH << 16);
+ /*
+ * exception: do not set DID_PASSTHROUGH on CHECK CONDITION
+ * so SCSI mid-layer and upper layers handle it accordingly.
+ */
+ if (scsi_cmd->result != SAM_STAT_CHECK_CONDITION)
+ scsi_cmd->result |= (DID_PASSTHROUGH << 16);
break;
case IPR_IOASC_BUS_WAS_RESET:
case IPR_IOASC_BUS_WAS_RESET_BY_OTHER:
@@ -5724,6 +6171,13 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
break;
case IPR_IOASC_NR_INIT_CMD_REQUIRED:
break;
+ case IPR_IOASC_IR_NON_OPTIMIZED:
+ if (res->raw_mode) {
+ res->raw_mode = 0;
+ scsi_cmd->result |= (DID_IMM_RETRY << 16);
+ } else
+ scsi_cmd->result |= (DID_ERROR << 16);
+ break;
default:
if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
scsi_cmd->result |= (DID_ERROR << 16);
@@ -5733,8 +6187,10 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
}
scsi_dma_unmap(ipr_cmd->scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -5752,21 +6208,32 @@ static void ipr_scsi_done(struct ipr_cmnd *ipr_cmd)
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
+ unsigned long lock_flags;
scsi_set_resid(scsi_cmd, be32_to_cpu(ipr_cmd->s.ioasa.hdr.residual_data_len));
if (likely(IPR_IOASC_SENSE_KEY(ioasc) == 0)) {
- scsi_dma_unmap(ipr_cmd->scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- scsi_cmd->scsi_done(scsi_cmd);
- } else
+ scsi_dma_unmap(scsi_cmd);
+
+ spin_lock_irqsave(ipr_cmd->hrrq->lock, lock_flags);
+ scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
+ spin_unlock_irqrestore(ipr_cmd->hrrq->lock, lock_flags);
+ } else {
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ spin_lock(&ipr_cmd->hrrq->_lock);
ipr_erp_start(ioa_cfg, ipr_cmd);
+ spin_unlock(&ipr_cmd->hrrq->_lock);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ }
}
/**
* ipr_queuecommand - Queue a mid-layer request
+ * @shost: scsi host struct
* @scsi_cmd: scsi command struct
- * @done: done function
*
* This function queues a request generated by the mid-layer.
*
@@ -5775,123 +6242,140 @@ static void ipr_scsi_done(struct ipr_cmnd *ipr_cmd)
* SCSI_MLQUEUE_DEVICE_BUSY if device is busy
* SCSI_MLQUEUE_HOST_BUSY if host is busy
**/
-static int ipr_queuecommand_lck(struct scsi_cmnd *scsi_cmd,
- void (*done) (struct scsi_cmnd *))
+static int ipr_queuecommand(struct Scsi_Host *shost,
+ struct scsi_cmnd *scsi_cmd)
{
struct ipr_ioa_cfg *ioa_cfg;
struct ipr_resource_entry *res;
struct ipr_ioarcb *ioarcb;
struct ipr_cmnd *ipr_cmd;
- int rc = 0;
+ unsigned long hrrq_flags;
+ int rc;
+ struct ipr_hrr_queue *hrrq;
+ int hrrq_id;
+
+ ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
- scsi_cmd->scsi_done = done;
- ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata;
- res = scsi_cmd->device->hostdata;
scsi_cmd->result = (DID_OK << 16);
+ res = scsi_cmd->device->hostdata;
+ hrrq_id = ipr_get_hrrq_index(ioa_cfg);
+ hrrq = &ioa_cfg->hrrq[hrrq_id];
+
+ spin_lock_irqsave(hrrq->lock, hrrq_flags);
/*
* We are currently blocking all devices due to a host reset
* We have told the host to stop giving us new requests, but
* ERP ops don't count. FIXME
*/
- if (unlikely(!ioa_cfg->allow_cmds && !ioa_cfg->ioa_is_dead))
+ if (unlikely(!hrrq->allow_cmds && !hrrq->ioa_is_dead && !hrrq->removing_ioa)) {
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
return SCSI_MLQUEUE_HOST_BUSY;
+ }
/*
* FIXME - Create scsi_set_host_offline interface
* and the ioa_is_dead check can be removed
*/
- if (unlikely(ioa_cfg->ioa_is_dead || !res)) {
- memset(scsi_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
- scsi_cmd->result = (DID_NO_CONNECT << 16);
- scsi_cmd->scsi_done(scsi_cmd);
- return 0;
+ if (unlikely(hrrq->ioa_is_dead || hrrq->removing_ioa || !res)) {
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ goto err_nodev;
}
- if (ipr_is_gata(res) && res->sata_port)
- return ata_sas_queuecmd(scsi_cmd, res->sata_port->ap);
+ ipr_cmd = __ipr_get_free_ipr_cmnd(hrrq);
+ if (ipr_cmd == NULL) {
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
- ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ ipr_init_ipr_cmnd(ipr_cmd, ipr_scsi_done);
ioarcb = &ipr_cmd->ioarcb;
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
memcpy(ioarcb->cmd_pkt.cdb, scsi_cmd->cmnd, scsi_cmd->cmd_len);
ipr_cmd->scsi_cmd = scsi_cmd;
- ioarcb->res_handle = res->res_handle;
- ipr_cmd->done = ipr_scsi_done;
- ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_RES_PHYS_LOC(res));
+ ipr_cmd->done = ipr_scsi_eh_done;
- if (ipr_is_gscsi(res) || ipr_is_vset_device(res)) {
+ if (ipr_is_gscsi(res)) {
if (scsi_cmd->underflow == 0)
ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
- if (res->needs_sync_complete) {
- ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_SYNC_COMPLETE;
- res->needs_sync_complete = 0;
+ if (res->reset_occurred) {
+ res->reset_occurred = 0;
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST;
}
+ }
+ if (ipr_is_gscsi(res) || ipr_is_vset_device(res)) {
ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC;
- if (ipr_is_gscsi(res))
- ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST;
+
ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR;
- ioarcb->cmd_pkt.flags_lo |= ipr_get_task_attributes(scsi_cmd);
+ if (scsi_cmd->flags & SCMD_TAGGED)
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_SIMPLE_TASK;
+ else
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_UNTAGGED_TASK;
}
if (scsi_cmd->cmnd[0] >= 0xC0 &&
- (!ipr_is_gscsi(res) || scsi_cmd->cmnd[0] == IPR_QUERY_RSRC_STATE))
+ (!ipr_is_gscsi(res) || scsi_cmd->cmnd[0] == IPR_QUERY_RSRC_STATE)) {
ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
-
- if (likely(rc == 0)) {
- if (ioa_cfg->sis64)
- rc = ipr_build_ioadl64(ioa_cfg, ipr_cmd);
- else
- rc = ipr_build_ioadl(ioa_cfg, ipr_cmd);
}
+ if (res->raw_mode && ipr_is_af_dasd_device(res)) {
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_PIPE;
- if (likely(rc == 0)) {
- mb();
- ipr_send_command(ipr_cmd);
- } else {
- list_move_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- return SCSI_MLQUEUE_HOST_BUSY;
+ if (scsi_cmd->underflow == 0)
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
}
- return 0;
-}
-
-static DEF_SCSI_QCMD(ipr_queuecommand)
+ if (ioa_cfg->sis64)
+ rc = ipr_build_ioadl64(ioa_cfg, ipr_cmd);
+ else
+ rc = ipr_build_ioadl(ioa_cfg, ipr_cmd);
+
+ spin_lock_irqsave(hrrq->lock, hrrq_flags);
+ if (unlikely(rc || (!hrrq->allow_cmds && !hrrq->ioa_is_dead))) {
+ list_add_tail(&ipr_cmd->queue, &hrrq->hrrq_free_q);
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ if (!rc)
+ scsi_dma_unmap(scsi_cmd);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
-/**
- * ipr_ioctl - IOCTL handler
- * @sdev: scsi device struct
- * @cmd: IOCTL cmd
- * @arg: IOCTL arg
- *
- * Return value:
- * 0 on success / other on failure
- **/
-static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
-{
- struct ipr_resource_entry *res;
+ if (unlikely(hrrq->ioa_is_dead)) {
+ list_add_tail(&ipr_cmd->queue, &hrrq->hrrq_free_q);
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ scsi_dma_unmap(scsi_cmd);
+ goto err_nodev;
+ }
- res = (struct ipr_resource_entry *)sdev->hostdata;
- if (res && ipr_is_gata(res)) {
- if (cmd == HDIO_GET_IDENTITY)
- return -ENOTTY;
- return ata_sas_scsi_ioctl(res->sata_port->ap, sdev, cmd, arg);
+ ioarcb->res_handle = res->res_handle;
+ if (res->needs_sync_complete) {
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_SYNC_COMPLETE;
+ res->needs_sync_complete = 0;
}
+ list_add_tail(&ipr_cmd->queue, &hrrq->hrrq_pending_q);
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_RES_PHYS_LOC(res));
+ ipr_send_command(ipr_cmd);
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ return 0;
- return -EINVAL;
+err_nodev:
+ spin_lock_irqsave(hrrq->lock, hrrq_flags);
+ memset(scsi_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ scsi_cmd->result = (DID_NO_CONNECT << 16);
+ scsi_done(scsi_cmd);
+ spin_unlock_irqrestore(hrrq->lock, hrrq_flags);
+ return 0;
}
/**
- * ipr_info - Get information about the card/driver
- * @scsi_host: scsi host struct
+ * ipr_ioa_info - Get information about the card/driver
+ * @host: scsi host struct
*
* Return value:
* pointer to buffer with description string
**/
-static const char * ipr_ioa_info(struct Scsi_Host *host)
+static const char *ipr_ioa_info(struct Scsi_Host *host)
{
static char buffer[512];
struct ipr_ioa_cfg *ioa_cfg;
@@ -5906,428 +6390,32 @@ static const char * ipr_ioa_info(struct Scsi_Host *host)
return buffer;
}
-static struct scsi_host_template driver_template = {
+static const struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = "IPR",
.info = ipr_ioa_info,
- .ioctl = ipr_ioctl,
.queuecommand = ipr_queuecommand,
.eh_abort_handler = ipr_eh_abort,
.eh_device_reset_handler = ipr_eh_dev_reset,
.eh_host_reset_handler = ipr_eh_host_reset,
- .slave_alloc = ipr_slave_alloc,
- .slave_configure = ipr_slave_configure,
- .slave_destroy = ipr_slave_destroy,
- .target_alloc = ipr_target_alloc,
+ .sdev_init = ipr_sdev_init,
+ .sdev_configure = ipr_sdev_configure,
+ .sdev_destroy = ipr_sdev_destroy,
+ .scan_finished = ipr_scan_finished,
.target_destroy = ipr_target_destroy,
.change_queue_depth = ipr_change_queue_depth,
- .change_queue_type = ipr_change_queue_type,
.bios_param = ipr_biosparam,
.can_queue = IPR_MAX_COMMANDS,
.this_id = -1,
.sg_tablesize = IPR_MAX_SGLIST,
.max_sectors = IPR_IOA_MAX_SECTORS,
.cmd_per_lun = IPR_MAX_CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = ipr_ioa_attrs,
- .sdev_attrs = ipr_dev_attrs,
- .proc_name = IPR_NAME
-};
-
-/**
- * ipr_ata_phy_reset - libata phy_reset handler
- * @ap: ata port to reset
- *
- **/
-static void ipr_ata_phy_reset(struct ata_port *ap)
-{
- unsigned long flags;
- struct ipr_sata_port *sata_port = ap->private_data;
- struct ipr_resource_entry *res = sata_port->res;
- struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
- int rc;
-
- ENTER;
- spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- while(ioa_cfg->in_reset_reload) {
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
- wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
- spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- }
-
- if (!ioa_cfg->allow_cmds)
- goto out_unlock;
-
- rc = ipr_device_reset(ioa_cfg, res);
-
- if (rc) {
- ap->link.device[0].class = ATA_DEV_NONE;
- goto out_unlock;
- }
-
- ap->link.device[0].class = res->ata_class;
- if (ap->link.device[0].class == ATA_DEV_UNKNOWN)
- ap->link.device[0].class = ATA_DEV_NONE;
-
-out_unlock:
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
- LEAVE;
-}
-
-/**
- * ipr_ata_post_internal - Cleanup after an internal command
- * @qc: ATA queued command
- *
- * Return value:
- * none
- **/
-static void ipr_ata_post_internal(struct ata_queued_cmd *qc)
-{
- struct ipr_sata_port *sata_port = qc->ap->private_data;
- struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
- struct ipr_cmnd *ipr_cmd;
- unsigned long flags;
-
- spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- while(ioa_cfg->in_reset_reload) {
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
- wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
- spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- }
-
- list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
- if (ipr_cmd->qc == qc) {
- ipr_device_reset(ioa_cfg, sata_port->res);
- break;
- }
- }
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
-}
-
-/**
- * ipr_copy_sata_tf - Copy a SATA taskfile to an IOA data structure
- * @regs: destination
- * @tf: source ATA taskfile
- *
- * Return value:
- * none
- **/
-static void ipr_copy_sata_tf(struct ipr_ioarcb_ata_regs *regs,
- struct ata_taskfile *tf)
-{
- regs->feature = tf->feature;
- regs->nsect = tf->nsect;
- regs->lbal = tf->lbal;
- regs->lbam = tf->lbam;
- regs->lbah = tf->lbah;
- regs->device = tf->device;
- regs->command = tf->command;
- regs->hob_feature = tf->hob_feature;
- regs->hob_nsect = tf->hob_nsect;
- regs->hob_lbal = tf->hob_lbal;
- regs->hob_lbam = tf->hob_lbam;
- regs->hob_lbah = tf->hob_lbah;
- regs->ctl = tf->ctl;
-}
-
-/**
- * ipr_sata_done - done function for SATA commands
- * @ipr_cmd: ipr command struct
- *
- * This function is invoked by the interrupt handler for
- * ops generated by the SCSI mid-layer to SATA devices
- *
- * Return value:
- * none
- **/
-static void ipr_sata_done(struct ipr_cmnd *ipr_cmd)
-{
- struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
- struct ata_queued_cmd *qc = ipr_cmd->qc;
- struct ipr_sata_port *sata_port = qc->ap->private_data;
- struct ipr_resource_entry *res = sata_port->res;
- u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
-
- if (ipr_cmd->ioa_cfg->sis64)
- memcpy(&sata_port->ioasa, &ipr_cmd->s.ioasa64.u.gata,
- sizeof(struct ipr_ioasa_gata));
- else
- memcpy(&sata_port->ioasa, &ipr_cmd->s.ioasa.u.gata,
- sizeof(struct ipr_ioasa_gata));
- ipr_dump_ioasa(ioa_cfg, ipr_cmd, res);
-
- if (be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc_specific) & IPR_ATA_DEVICE_WAS_RESET)
- scsi_report_device_reset(ioa_cfg->host, res->bus, res->target);
-
- if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
- qc->err_mask |= __ac_err_mask(sata_port->ioasa.status);
- else
- qc->err_mask |= ac_err_mask(sata_port->ioasa.status);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- ata_qc_complete(qc);
-}
-
-/**
- * ipr_build_ata_ioadl64 - Build an ATA scatter/gather list
- * @ipr_cmd: ipr command struct
- * @qc: ATA queued command
- *
- **/
-static void ipr_build_ata_ioadl64(struct ipr_cmnd *ipr_cmd,
- struct ata_queued_cmd *qc)
-{
- u32 ioadl_flags = 0;
- struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
- struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64;
- struct ipr_ioadl64_desc *last_ioadl64 = NULL;
- int len = qc->nbytes;
- struct scatterlist *sg;
- unsigned int si;
- dma_addr_t dma_addr = ipr_cmd->dma_addr;
-
- if (len == 0)
- return;
-
- if (qc->dma_dir == DMA_TO_DEVICE) {
- ioadl_flags = IPR_IOADL_FLAGS_WRITE;
- ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
- } else if (qc->dma_dir == DMA_FROM_DEVICE)
- ioadl_flags = IPR_IOADL_FLAGS_READ;
-
- ioarcb->data_transfer_length = cpu_to_be32(len);
- ioarcb->ioadl_len =
- cpu_to_be32(sizeof(struct ipr_ioadl64_desc) * ipr_cmd->dma_use_sg);
- ioarcb->u.sis64_addr_data.data_ioadl_addr =
- cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ata_ioadl));
-
- for_each_sg(qc->sg, sg, qc->n_elem, si) {
- ioadl64->flags = cpu_to_be32(ioadl_flags);
- ioadl64->data_len = cpu_to_be32(sg_dma_len(sg));
- ioadl64->address = cpu_to_be64(sg_dma_address(sg));
-
- last_ioadl64 = ioadl64;
- ioadl64++;
- }
-
- if (likely(last_ioadl64))
- last_ioadl64->flags |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
-}
-
-/**
- * ipr_build_ata_ioadl - Build an ATA scatter/gather list
- * @ipr_cmd: ipr command struct
- * @qc: ATA queued command
- *
- **/
-static void ipr_build_ata_ioadl(struct ipr_cmnd *ipr_cmd,
- struct ata_queued_cmd *qc)
-{
- u32 ioadl_flags = 0;
- struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
- struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl;
- struct ipr_ioadl_desc *last_ioadl = NULL;
- int len = qc->nbytes;
- struct scatterlist *sg;
- unsigned int si;
-
- if (len == 0)
- return;
-
- if (qc->dma_dir == DMA_TO_DEVICE) {
- ioadl_flags = IPR_IOADL_FLAGS_WRITE;
- ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
- ioarcb->data_transfer_length = cpu_to_be32(len);
- ioarcb->ioadl_len =
- cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
- } else if (qc->dma_dir == DMA_FROM_DEVICE) {
- ioadl_flags = IPR_IOADL_FLAGS_READ;
- ioarcb->read_data_transfer_length = cpu_to_be32(len);
- ioarcb->read_ioadl_len =
- cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
- }
-
- for_each_sg(qc->sg, sg, qc->n_elem, si) {
- ioadl->flags_and_data_len = cpu_to_be32(ioadl_flags | sg_dma_len(sg));
- ioadl->address = cpu_to_be32(sg_dma_address(sg));
-
- last_ioadl = ioadl;
- ioadl++;
- }
-
- if (likely(last_ioadl))
- last_ioadl->flags_and_data_len |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
-}
-
-/**
- * ipr_qc_issue - Issue a SATA qc to a device
- * @qc: queued command
- *
- * Return value:
- * 0 if success
- **/
-static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- struct ipr_sata_port *sata_port = ap->private_data;
- struct ipr_resource_entry *res = sata_port->res;
- struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
- struct ipr_cmnd *ipr_cmd;
- struct ipr_ioarcb *ioarcb;
- struct ipr_ioarcb_ata_regs *regs;
-
- if (unlikely(!ioa_cfg->allow_cmds || ioa_cfg->ioa_is_dead))
- return AC_ERR_SYSTEM;
-
- ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
- ioarcb = &ipr_cmd->ioarcb;
-
- if (ioa_cfg->sis64) {
- regs = &ipr_cmd->i.ata_ioadl.regs;
- ioarcb->add_cmd_parms_offset = cpu_to_be16(sizeof(*ioarcb));
- } else
- regs = &ioarcb->u.add_data.u.regs;
-
- memset(regs, 0, sizeof(*regs));
- ioarcb->add_cmd_parms_len = cpu_to_be16(sizeof(*regs));
-
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
- ipr_cmd->qc = qc;
- ipr_cmd->done = ipr_sata_done;
- ipr_cmd->ioarcb.res_handle = res->res_handle;
- ioarcb->cmd_pkt.request_type = IPR_RQTYPE_ATA_PASSTHRU;
- ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC;
- ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
- ipr_cmd->dma_use_sg = qc->n_elem;
-
- if (ioa_cfg->sis64)
- ipr_build_ata_ioadl64(ipr_cmd, qc);
- else
- ipr_build_ata_ioadl(ipr_cmd, qc);
-
- regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION;
- ipr_copy_sata_tf(regs, &qc->tf);
- memcpy(ioarcb->cmd_pkt.cdb, qc->cdb, IPR_MAX_CDB_LEN);
- ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_RES_PHYS_LOC(res));
-
- switch (qc->tf.protocol) {
- case ATA_PROT_NODATA:
- case ATA_PROT_PIO:
- break;
-
- case ATA_PROT_DMA:
- regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA;
- break;
-
- case ATAPI_PROT_PIO:
- case ATAPI_PROT_NODATA:
- regs->flags |= IPR_ATA_FLAG_PACKET_CMD;
- break;
-
- case ATAPI_PROT_DMA:
- regs->flags |= IPR_ATA_FLAG_PACKET_CMD;
- regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA;
- break;
-
- default:
- WARN_ON(1);
- return AC_ERR_INVALID;
- }
-
- mb();
-
- ipr_send_command(ipr_cmd);
-
- return 0;
-}
-
-/**
- * ipr_qc_fill_rtf - Read result TF
- * @qc: ATA queued command
- *
- * Return value:
- * true
- **/
-static bool ipr_qc_fill_rtf(struct ata_queued_cmd *qc)
-{
- struct ipr_sata_port *sata_port = qc->ap->private_data;
- struct ipr_ioasa_gata *g = &sata_port->ioasa;
- struct ata_taskfile *tf = &qc->result_tf;
-
- tf->feature = g->error;
- tf->nsect = g->nsect;
- tf->lbal = g->lbal;
- tf->lbam = g->lbam;
- tf->lbah = g->lbah;
- tf->device = g->device;
- tf->command = g->status;
- tf->hob_nsect = g->hob_nsect;
- tf->hob_lbal = g->hob_lbal;
- tf->hob_lbam = g->hob_lbam;
- tf->hob_lbah = g->hob_lbah;
- tf->ctl = g->alt_status;
-
- return true;
-}
-
-static struct ata_port_operations ipr_sata_ops = {
- .phy_reset = ipr_ata_phy_reset,
- .hardreset = ipr_sata_reset,
- .post_internal_cmd = ipr_ata_post_internal,
- .qc_prep = ata_noop_qc_prep,
- .qc_issue = ipr_qc_issue,
- .qc_fill_rtf = ipr_qc_fill_rtf,
- .port_start = ata_sas_port_start,
- .port_stop = ata_sas_port_stop
-};
-
-static struct ata_port_info sata_port_info = {
- .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA,
- .pio_mask = ATA_PIO4_ONLY,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &ipr_sata_ops
-};
-
-#ifdef CONFIG_PPC_PSERIES
-static const u16 ipr_blocked_processors[] = {
- PV_NORTHSTAR,
- PV_PULSAR,
- PV_POWER4,
- PV_ICESTAR,
- PV_SSTAR,
- PV_POWER4p,
- PV_630,
- PV_630p
+ .shost_groups = ipr_ioa_groups,
+ .sdev_groups = ipr_dev_groups,
+ .proc_name = IPR_NAME,
};
/**
- * ipr_invalid_adapter - Determine if this adapter is supported on this hardware
- * @ioa_cfg: ioa cfg struct
- *
- * Adapters that use Gemstone revision < 3.1 do not work reliably on
- * certain pSeries hardware. This function determines if the given
- * adapter is in one of these confgurations or not.
- *
- * Return value:
- * 1 if adapter is not supported / 0 if adapter is supported
- **/
-static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg)
-{
- int i;
-
- if ((ioa_cfg->type == 0x5702) && (ioa_cfg->pdev->revision < 4)) {
- for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++){
- if (__is_processor(ipr_blocked_processors[i]))
- return 1;
- }
- }
- return 0;
-}
-#else
-#define ipr_invalid_adapter(ioa_cfg) 0
-#endif
-
-/**
* ipr_ioa_bringdown_done - IOA bring down completion.
* @ipr_cmd: ipr command struct
*
@@ -6340,16 +6428,26 @@ static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg)
static int ipr_ioa_bringdown_done(struct ipr_cmnd *ipr_cmd)
{
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ int i;
ENTER;
+ if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) {
+ ipr_trace;
+ ioa_cfg->scsi_unblock = 1;
+ schedule_work(&ioa_cfg->work_q);
+ }
+
ioa_cfg->in_reset_reload = 0;
ioa_cfg->reset_retries = 0;
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- wake_up_all(&ioa_cfg->reset_wait_q);
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].ioa_is_dead = 1;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
+ wmb();
- spin_unlock_irq(ioa_cfg->host->host_lock);
- scsi_unblock_requests(ioa_cfg->host);
- spin_lock_irq(ioa_cfg->host->host_lock);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
+ wake_up_all(&ioa_cfg->reset_wait_q);
LEAVE;
return IPR_RC_JOB_RETURN;
@@ -6370,45 +6468,48 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd)
{
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct ipr_resource_entry *res;
- struct ipr_hostrcb *hostrcb, *temp;
- int i = 0;
+ int j;
ENTER;
ioa_cfg->in_reset_reload = 0;
- ioa_cfg->allow_cmds = 1;
+ for (j = 0; j < ioa_cfg->hrrq_num; j++) {
+ spin_lock(&ioa_cfg->hrrq[j]._lock);
+ ioa_cfg->hrrq[j].allow_cmds = 1;
+ spin_unlock(&ioa_cfg->hrrq[j]._lock);
+ }
+ wmb();
ioa_cfg->reset_cmd = NULL;
ioa_cfg->doorbell |= IPR_RUNTIME_RESET;
list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
- if (ioa_cfg->allow_ml_add_del && (res->add_to_ml || res->del_from_ml)) {
+ if (res->add_to_ml || res->del_from_ml) {
ipr_trace;
break;
}
}
schedule_work(&ioa_cfg->work_q);
- list_for_each_entry_safe(hostrcb, temp, &ioa_cfg->hostrcb_free_q, queue) {
- list_del(&hostrcb->queue);
- if (i++ < IPR_NUM_LOG_HCAMS)
- ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb);
+ for (j = 0; j < IPR_NUM_HCAMS; j++) {
+ list_del_init(&ioa_cfg->hostrcb[j]->queue);
+ if (j < IPR_NUM_LOG_HCAMS)
+ ipr_send_hcam(ioa_cfg,
+ IPR_HCAM_CDB_OP_CODE_LOG_DATA,
+ ioa_cfg->hostrcb[j]);
else
- ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
+ ipr_send_hcam(ioa_cfg,
+ IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE,
+ ioa_cfg->hostrcb[j]);
}
scsi_report_bus_reset(ioa_cfg->host, IPR_VSET_BUS);
dev_info(&ioa_cfg->pdev->dev, "IOA initialized.\n");
ioa_cfg->reset_retries = 0;
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
wake_up_all(&ioa_cfg->reset_wait_q);
- spin_unlock(ioa_cfg->host->host_lock);
- scsi_unblock_requests(ioa_cfg->host);
- spin_lock(ioa_cfg->host->host_lock);
-
- if (!ioa_cfg->allow_cmds)
- scsi_block_requests(ioa_cfg->host);
-
+ ioa_cfg->scsi_unblock = 1;
+ schedule_work(&ioa_cfg->work_q);
LEAVE;
return IPR_RC_JOB_RETURN;
}
@@ -6477,9 +6578,11 @@ static int ipr_set_supported_devs(struct ipr_cmnd *ipr_cmd)
if (!ioa_cfg->sis64)
ipr_cmd->job_step = ipr_set_supported_devs;
+ LEAVE;
return IPR_RC_JOB_RETURN;
}
+ LEAVE;
return IPR_RC_JOB_CONTINUE;
}
@@ -6594,7 +6697,7 @@ static void ipr_scsi_bus_speed_limit(struct ipr_ioa_cfg *ioa_cfg)
* none
**/
static void ipr_modify_ioafp_mode_page_28(struct ipr_ioa_cfg *ioa_cfg,
- struct ipr_mode_pages *mode_pages)
+ struct ipr_mode_pages *mode_pages)
{
int i, entry_length;
struct ipr_dev_bus_entry *bus;
@@ -6695,7 +6798,7 @@ static int ipr_ioafp_mode_select_page28(struct ipr_cmnd *ipr_cmd)
/**
* ipr_build_mode_sense - Builds a mode sense command
* @ipr_cmd: ipr command struct
- * @res: resource entry struct
+ * @res_handle: resource entry struct
* @parm: Byte 2 of mode sense command
* @dma_addr: DMA address of mode sense buffer
* @xfer_len: Size of DMA buffer
@@ -6737,7 +6840,7 @@ static int ipr_reset_cmd_failed(struct ipr_cmnd *ipr_cmd)
ipr_cmd->ioarcb.cmd_pkt.cdb[0], ioasc);
ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
return IPR_RC_JOB_RETURN;
}
@@ -7018,9 +7121,71 @@ static int ipr_ioafp_query_ioa_cfg(struct ipr_cmnd *ipr_cmd)
return IPR_RC_JOB_RETURN;
}
+static int ipr_ioa_service_action_failed(struct ipr_cmnd *ipr_cmd)
+{
+ u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
+
+ if (ioasc == IPR_IOASC_IR_INVALID_REQ_TYPE_OR_PKT)
+ return IPR_RC_JOB_CONTINUE;
+
+ return ipr_reset_cmd_failed(ipr_cmd);
+}
+
+static void ipr_build_ioa_service_action(struct ipr_cmnd *ipr_cmd,
+ __be32 res_handle, u8 sa_code)
+{
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+
+ ioarcb->res_handle = res_handle;
+ ioarcb->cmd_pkt.cdb[0] = IPR_IOA_SERVICE_ACTION;
+ ioarcb->cmd_pkt.cdb[1] = sa_code;
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+}
+
+/**
+ * ipr_ioafp_set_caching_parameters - Issue Set Cache parameters service
+ * action
+ * @ipr_cmd: ipr command struct
+ *
+ * Return value:
+ * none
+ **/
+static int ipr_ioafp_set_caching_parameters(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_inquiry_pageC4 *pageC4 = &ioa_cfg->vpd_cbs->pageC4_data;
+
+ ENTER;
+
+ ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg;
+
+ if (pageC4->cache_cap[0] & IPR_CAP_SYNC_CACHE) {
+ ipr_build_ioa_service_action(ipr_cmd,
+ cpu_to_be32(IPR_IOA_RES_HANDLE),
+ IPR_IOA_SA_CHANGE_CACHE_PARAMS);
+
+ ioarcb->cmd_pkt.cdb[2] = 0x40;
+
+ ipr_cmd->job_step_failed = ipr_ioa_service_action_failed;
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
+ IPR_SET_SUP_DEVICE_TIMEOUT);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+ }
+
+ LEAVE;
+ return IPR_RC_JOB_CONTINUE;
+}
+
/**
* ipr_ioafp_inquiry - Send an Inquiry to the adapter.
* @ipr_cmd: ipr command struct
+ * @flags: flags to send
+ * @page: page to inquire
+ * @dma_addr: DMA address
+ * @xfer_len: transfer data length
*
* This utility function sends an inquiry to the adapter.
*
@@ -7069,6 +7234,39 @@ static int ipr_inquiry_page_supported(struct ipr_inquiry_page0 *page0, u8 page)
}
/**
+ * ipr_ioafp_pageC4_inquiry - Send a Page 0xC4 Inquiry to the adapter.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sends a Page 0xC4 inquiry to the adapter
+ * to retrieve software VPD information.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_pageC4_inquiry(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_inquiry_page0 *page0 = &ioa_cfg->vpd_cbs->page0_data;
+ struct ipr_inquiry_pageC4 *pageC4 = &ioa_cfg->vpd_cbs->pageC4_data;
+
+ ENTER;
+ ipr_cmd->job_step = ipr_ioafp_set_caching_parameters;
+ memset(pageC4, 0, sizeof(*pageC4));
+
+ if (ipr_inquiry_page_supported(page0, 0xC4)) {
+ ipr_ioafp_inquiry(ipr_cmd, 1, 0xC4,
+ (ioa_cfg->vpd_cbs_dma
+ + offsetof(struct ipr_misc_cbs,
+ pageC4_data)),
+ sizeof(struct ipr_inquiry_pageC4));
+ return IPR_RC_JOB_RETURN;
+ }
+
+ LEAVE;
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
* ipr_ioafp_cap_inquiry - Send a Page 0xD0 Inquiry to the adapter.
* @ipr_cmd: ipr command struct
*
@@ -7085,7 +7283,7 @@ static int ipr_ioafp_cap_inquiry(struct ipr_cmnd *ipr_cmd)
struct ipr_inquiry_cap *cap = &ioa_cfg->vpd_cbs->cap;
ENTER;
- ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg;
+ ipr_cmd->job_step = ipr_ioafp_pageC4_inquiry;
memset(cap, 0, sizeof(*cap));
if (ipr_inquiry_page_supported(page0, 0xD0)) {
@@ -7195,51 +7393,77 @@ static int ipr_ioafp_identify_hrrq(struct ipr_cmnd *ipr_cmd)
{
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_hrr_queue *hrrq;
ENTER;
- dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n");
+ ipr_cmd->job_step = ipr_ioafp_std_inquiry;
+ if (ioa_cfg->identify_hrrq_index == 0)
+ dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n");
- ioarcb->cmd_pkt.cdb[0] = IPR_ID_HOST_RR_Q;
- ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+ if (ioa_cfg->identify_hrrq_index < ioa_cfg->hrrq_num) {
+ hrrq = &ioa_cfg->hrrq[ioa_cfg->identify_hrrq_index];
- ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
- if (ioa_cfg->sis64)
- ioarcb->cmd_pkt.cdb[1] = 0x1;
- ioarcb->cmd_pkt.cdb[2] =
- ((u64) ioa_cfg->host_rrq_dma >> 24) & 0xff;
- ioarcb->cmd_pkt.cdb[3] =
- ((u64) ioa_cfg->host_rrq_dma >> 16) & 0xff;
- ioarcb->cmd_pkt.cdb[4] =
- ((u64) ioa_cfg->host_rrq_dma >> 8) & 0xff;
- ioarcb->cmd_pkt.cdb[5] =
- ((u64) ioa_cfg->host_rrq_dma) & 0xff;
- ioarcb->cmd_pkt.cdb[7] =
- ((sizeof(u32) * IPR_NUM_CMD_BLKS) >> 8) & 0xff;
- ioarcb->cmd_pkt.cdb[8] =
- (sizeof(u32) * IPR_NUM_CMD_BLKS) & 0xff;
+ ioarcb->cmd_pkt.cdb[0] = IPR_ID_HOST_RR_Q;
+ ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
- if (ioa_cfg->sis64) {
- ioarcb->cmd_pkt.cdb[10] =
- ((u64) ioa_cfg->host_rrq_dma >> 56) & 0xff;
- ioarcb->cmd_pkt.cdb[11] =
- ((u64) ioa_cfg->host_rrq_dma >> 48) & 0xff;
- ioarcb->cmd_pkt.cdb[12] =
- ((u64) ioa_cfg->host_rrq_dma >> 40) & 0xff;
- ioarcb->cmd_pkt.cdb[13] =
- ((u64) ioa_cfg->host_rrq_dma >> 32) & 0xff;
- }
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+ if (ioa_cfg->sis64)
+ ioarcb->cmd_pkt.cdb[1] = 0x1;
- ipr_cmd->job_step = ipr_ioafp_std_inquiry;
+ if (ioa_cfg->nvectors == 1)
+ ioarcb->cmd_pkt.cdb[1] &= ~IPR_ID_HRRQ_SELE_ENABLE;
+ else
+ ioarcb->cmd_pkt.cdb[1] |= IPR_ID_HRRQ_SELE_ENABLE;
+
+ ioarcb->cmd_pkt.cdb[2] =
+ ((u64) hrrq->host_rrq_dma >> 24) & 0xff;
+ ioarcb->cmd_pkt.cdb[3] =
+ ((u64) hrrq->host_rrq_dma >> 16) & 0xff;
+ ioarcb->cmd_pkt.cdb[4] =
+ ((u64) hrrq->host_rrq_dma >> 8) & 0xff;
+ ioarcb->cmd_pkt.cdb[5] =
+ ((u64) hrrq->host_rrq_dma) & 0xff;
+ ioarcb->cmd_pkt.cdb[7] =
+ ((sizeof(u32) * hrrq->size) >> 8) & 0xff;
+ ioarcb->cmd_pkt.cdb[8] =
+ (sizeof(u32) * hrrq->size) & 0xff;
+
+ if (ioarcb->cmd_pkt.cdb[1] & IPR_ID_HRRQ_SELE_ENABLE)
+ ioarcb->cmd_pkt.cdb[9] =
+ ioa_cfg->identify_hrrq_index;
- ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+ if (ioa_cfg->sis64) {
+ ioarcb->cmd_pkt.cdb[10] =
+ ((u64) hrrq->host_rrq_dma >> 56) & 0xff;
+ ioarcb->cmd_pkt.cdb[11] =
+ ((u64) hrrq->host_rrq_dma >> 48) & 0xff;
+ ioarcb->cmd_pkt.cdb[12] =
+ ((u64) hrrq->host_rrq_dma >> 40) & 0xff;
+ ioarcb->cmd_pkt.cdb[13] =
+ ((u64) hrrq->host_rrq_dma >> 32) & 0xff;
+ }
+
+ if (ioarcb->cmd_pkt.cdb[1] & IPR_ID_HRRQ_SELE_ENABLE)
+ ioarcb->cmd_pkt.cdb[14] =
+ ioa_cfg->identify_hrrq_index;
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
+ IPR_INTERNAL_TIMEOUT);
+
+ if (++ioa_cfg->identify_hrrq_index < ioa_cfg->hrrq_num)
+ ipr_cmd->job_step = ipr_ioafp_identify_hrrq;
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+ }
LEAVE;
- return IPR_RC_JOB_RETURN;
+ return IPR_RC_JOB_CONTINUE;
}
/**
* ipr_reset_timer_done - Adapter reset timer function
- * @ipr_cmd: ipr command struct
+ * @t: Timer context used to fetch ipr command struct
*
* Description: This function is used in adapter reset processing
* for timing events. If the reset_cmd pointer in the IOA
@@ -7250,8 +7474,9 @@ static int ipr_ioafp_identify_hrrq(struct ipr_cmnd *ipr_cmd)
* Return value:
* none
**/
-static void ipr_reset_timer_done(struct ipr_cmnd *ipr_cmd)
+static void ipr_reset_timer_done(struct timer_list *t)
{
+ struct ipr_cmnd *ipr_cmd = timer_container_of(ipr_cmd, t, timer);
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
unsigned long lock_flags = 0;
@@ -7282,12 +7507,13 @@ static void ipr_reset_timer_done(struct ipr_cmnd *ipr_cmd)
static void ipr_reset_start_timer(struct ipr_cmnd *ipr_cmd,
unsigned long timeout)
{
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->ioa_cfg->pending_q);
+
+ ENTER;
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
ipr_cmd->done = ipr_reset_ioa_job;
- ipr_cmd->timer.data = (unsigned long) ipr_cmd;
ipr_cmd->timer.expires = jiffies + timeout;
- ipr_cmd->timer.function = (void (*)(unsigned long))ipr_reset_timer_done;
+ ipr_cmd->timer.function = ipr_reset_timer_done;
add_timer(&ipr_cmd->timer);
}
@@ -7300,13 +7526,26 @@ static void ipr_reset_start_timer(struct ipr_cmnd *ipr_cmd,
**/
static void ipr_init_ioa_mem(struct ipr_ioa_cfg *ioa_cfg)
{
- memset(ioa_cfg->host_rrq, 0, sizeof(u32) * IPR_NUM_CMD_BLKS);
+ struct ipr_hrr_queue *hrrq;
- /* Initialize Host RRQ pointers */
- ioa_cfg->hrrq_start = ioa_cfg->host_rrq;
- ioa_cfg->hrrq_end = &ioa_cfg->host_rrq[IPR_NUM_CMD_BLKS - 1];
- ioa_cfg->hrrq_curr = ioa_cfg->hrrq_start;
- ioa_cfg->toggle_bit = 1;
+ for_each_hrrq(hrrq, ioa_cfg) {
+ spin_lock(&hrrq->_lock);
+ memset(hrrq->host_rrq, 0, sizeof(u32) * hrrq->size);
+
+ /* Initialize Host RRQ pointers */
+ hrrq->hrrq_start = hrrq->host_rrq;
+ hrrq->hrrq_end = &hrrq->host_rrq[hrrq->size - 1];
+ hrrq->hrrq_curr = hrrq->hrrq_start;
+ hrrq->toggle_bit = 1;
+ spin_unlock(&hrrq->_lock);
+ }
+ wmb();
+
+ ioa_cfg->identify_hrrq_index = 0;
+ if (ioa_cfg->hrrq_num == 1)
+ atomic_set(&ioa_cfg->hrrq_index, 0);
+ else
+ atomic_set(&ioa_cfg->hrrq_index, 1);
/* Zero out config table */
memset(ioa_cfg->u.cfg_table, 0, ioa_cfg->cfg_table_size);
@@ -7358,12 +7597,12 @@ static int ipr_reset_next_stage(struct ipr_cmnd *ipr_cmd)
}
}
- ipr_cmd->timer.data = (unsigned long) ipr_cmd;
ipr_cmd->timer.expires = jiffies + stage_time * HZ;
- ipr_cmd->timer.function = (void (*)(unsigned long))ipr_oper_timeout;
+ ipr_cmd->timer.function = ipr_oper_timeout;
ipr_cmd->done = ipr_reset_ioa_job;
add_timer(&ipr_cmd->timer);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
return IPR_RC_JOB_RETURN;
}
@@ -7383,12 +7622,17 @@ static int ipr_reset_enable_ioa(struct ipr_cmnd *ipr_cmd)
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
volatile u32 int_reg;
volatile u64 maskval;
+ int i;
ENTER;
ipr_cmd->job_step = ipr_ioafp_identify_hrrq;
ipr_init_ioa_mem(ioa_cfg);
- ioa_cfg->allow_interrupts = 1;
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].allow_interrupts = 1;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
if (ioa_cfg->sis64) {
/* Set the adapter to the correct endian mode. */
writel(IPR_ENDIAN_SWAP_KEY, ioa_cfg->regs.endian_swap_reg);
@@ -7423,12 +7667,11 @@ static int ipr_reset_enable_ioa(struct ipr_cmnd *ipr_cmd)
return IPR_RC_JOB_CONTINUE;
}
- ipr_cmd->timer.data = (unsigned long) ipr_cmd;
ipr_cmd->timer.expires = jiffies + (ioa_cfg->transop_timeout * HZ);
- ipr_cmd->timer.function = (void (*)(unsigned long))ipr_oper_timeout;
+ ipr_cmd->timer.function = ipr_oper_timeout;
ipr_cmd->done = ipr_reset_ioa_job;
add_timer(&ipr_cmd->timer);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
LEAVE;
return IPR_RC_JOB_RETURN;
@@ -7449,8 +7692,11 @@ static int ipr_reset_wait_for_dump(struct ipr_cmnd *ipr_cmd)
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
if (ioa_cfg->sdt_state == GET_DUMP)
+ ioa_cfg->sdt_state = WAIT_FOR_DUMP;
+ else if (ioa_cfg->sdt_state == READ_DUMP)
ioa_cfg->sdt_state = ABORT_DUMP;
+ ioa_cfg->dump_timeout = 1;
ipr_cmd->job_step = ipr_reset_alert;
return IPR_RC_JOB_CONTINUE;
@@ -7518,7 +7764,7 @@ static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg)
hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next,
struct ipr_hostrcb, queue);
- list_del(&hostrcb->queue);
+ list_del_init(&hostrcb->queue);
memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam));
rc = ipr_get_ldump_data_section(ioa_cfg,
@@ -7561,6 +7807,42 @@ static int ipr_reset_get_unit_check_job(struct ipr_cmnd *ipr_cmd)
return IPR_RC_JOB_RETURN;
}
+static int ipr_dump_mailbox_wait(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ ENTER;
+
+ if (ioa_cfg->sdt_state != GET_DUMP)
+ return IPR_RC_JOB_RETURN;
+
+ if (!ioa_cfg->sis64 || !ipr_cmd->u.time_left ||
+ (readl(ioa_cfg->regs.sense_interrupt_reg) &
+ IPR_PCII_MAILBOX_STABLE)) {
+
+ if (!ipr_cmd->u.time_left)
+ dev_err(&ioa_cfg->pdev->dev,
+ "Timed out waiting for Mailbox register.\n");
+
+ ioa_cfg->sdt_state = READ_DUMP;
+ ioa_cfg->dump_timeout = 0;
+ if (ioa_cfg->sis64)
+ ipr_reset_start_timer(ipr_cmd, IPR_SIS64_DUMP_TIMEOUT);
+ else
+ ipr_reset_start_timer(ipr_cmd, IPR_SIS32_DUMP_TIMEOUT);
+ ipr_cmd->job_step = ipr_reset_wait_for_dump;
+ schedule_work(&ioa_cfg->work_q);
+
+ } else {
+ ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
+ ipr_reset_start_timer(ipr_cmd,
+ IPR_CHECK_FOR_RESET_TIMEOUT);
+ }
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
/**
* ipr_reset_restore_cfg_space - Restore PCI config space.
* @ipr_cmd: ipr command struct
@@ -7575,10 +7857,8 @@ static int ipr_reset_get_unit_check_job(struct ipr_cmnd *ipr_cmd)
static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
{
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
- u32 int_reg;
ENTER;
- ioa_cfg->pdev->state_saved = true;
pci_restore_state(ioa_cfg->pdev);
if (ipr_set_pcix_cmd_reg(ioa_cfg)) {
@@ -7591,7 +7871,7 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
if (ioa_cfg->sis64) {
/* Set the adapter to the correct endian mode. */
writel(IPR_ENDIAN_SWAP_KEY, ioa_cfg->regs.endian_swap_reg);
- int_reg = readl(ioa_cfg->regs.endian_swap_reg);
+ readl(ioa_cfg->regs.endian_swap_reg);
}
if (ioa_cfg->ioa_unit_checked) {
@@ -7610,18 +7890,11 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
if (ioa_cfg->in_ioa_bringdown) {
ipr_cmd->job_step = ipr_ioa_bringdown_done;
+ } else if (ioa_cfg->sdt_state == GET_DUMP) {
+ ipr_cmd->job_step = ipr_dump_mailbox_wait;
+ ipr_cmd->u.time_left = IPR_WAIT_FOR_MAILBOX;
} else {
ipr_cmd->job_step = ipr_reset_enable_ioa;
-
- if (GET_DUMP == ioa_cfg->sdt_state) {
- if (ioa_cfg->sis64)
- ipr_reset_start_timer(ipr_cmd, IPR_SIS64_DUMP_TIMEOUT);
- else
- ipr_reset_start_timer(ipr_cmd, IPR_SIS32_DUMP_TIMEOUT);
- ipr_cmd->job_step = ipr_reset_wait_for_dump;
- schedule_work(&ioa_cfg->work_q);
- return IPR_RC_JOB_RETURN;
- }
}
LEAVE;
@@ -7639,8 +7912,12 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
**/
static int ipr_reset_bist_done(struct ipr_cmnd *ipr_cmd)
{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
ENTER;
- pci_unblock_user_cfg_access(ipr_cmd->ioa_cfg->pdev);
+ if (ioa_cfg->cfg_locked)
+ pci_cfg_access_unlock(ioa_cfg->pdev);
+ ioa_cfg->cfg_locked = 0;
ipr_cmd->job_step = ipr_reset_restore_cfg_space;
LEAVE;
return IPR_RC_JOB_CONTINUE;
@@ -7661,8 +7938,6 @@ static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd)
int rc = PCIBIOS_SUCCESSFUL;
ENTER;
- pci_block_user_cfg_access(ioa_cfg->pdev);
-
if (ioa_cfg->ipr_chip->bist_method == IPR_MMIO)
writel(IPR_UPROCI_SIS64_START_BIST,
ioa_cfg->regs.set_uproc_interrupt_reg32);
@@ -7674,7 +7949,9 @@ static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd)
ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT);
rc = IPR_RC_JOB_RETURN;
} else {
- pci_unblock_user_cfg_access(ipr_cmd->ioa_cfg->pdev);
+ if (ioa_cfg->cfg_locked)
+ pci_cfg_access_unlock(ipr_cmd->ioa_cfg->pdev);
+ ioa_cfg->cfg_locked = 0;
ipr_cmd->s.ioasa.hdr.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR);
rc = IPR_RC_JOB_CONTINUE;
}
@@ -7695,7 +7972,6 @@ static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd)
static int ipr_reset_slot_reset_done(struct ipr_cmnd *ipr_cmd)
{
ENTER;
- pci_set_pcie_reset_state(ipr_cmd->ioa_cfg->pdev, pcie_deassert_reset);
ipr_cmd->job_step = ipr_reset_bist_done;
ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT);
LEAVE;
@@ -7703,6 +7979,32 @@ static int ipr_reset_slot_reset_done(struct ipr_cmnd *ipr_cmd)
}
/**
+ * ipr_reset_reset_work - Pulse a PCIe fundamental reset
+ * @work: work struct
+ *
+ * Description: This pulses warm reset to a slot.
+ *
+ **/
+static void ipr_reset_reset_work(struct work_struct *work)
+{
+ struct ipr_cmnd *ipr_cmd = container_of(work, struct ipr_cmnd, work);
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct pci_dev *pdev = ioa_cfg->pdev;
+ unsigned long lock_flags = 0;
+
+ ENTER;
+ pci_set_pcie_reset_state(pdev, pcie_warm_reset);
+ msleep(jiffies_to_msecs(IPR_PCI_RESET_TIMEOUT));
+ pci_set_pcie_reset_state(pdev, pcie_deassert_reset);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (ioa_cfg->reset_cmd == ipr_cmd)
+ ipr_reset_ioa_job(ipr_cmd);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ LEAVE;
+}
+
+/**
* ipr_reset_slot_reset - Reset the PCI slot of the adapter.
* @ipr_cmd: ipr command struct
*
@@ -7714,18 +8016,66 @@ static int ipr_reset_slot_reset_done(struct ipr_cmnd *ipr_cmd)
static int ipr_reset_slot_reset(struct ipr_cmnd *ipr_cmd)
{
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
- struct pci_dev *pdev = ioa_cfg->pdev;
ENTER;
- pci_block_user_cfg_access(pdev);
- pci_set_pcie_reset_state(pdev, pcie_warm_reset);
+ INIT_WORK(&ipr_cmd->work, ipr_reset_reset_work);
+ queue_work(ioa_cfg->reset_work_q, &ipr_cmd->work);
ipr_cmd->job_step = ipr_reset_slot_reset_done;
- ipr_reset_start_timer(ipr_cmd, IPR_PCI_RESET_TIMEOUT);
LEAVE;
return IPR_RC_JOB_RETURN;
}
/**
+ * ipr_reset_block_config_access_wait - Wait for permission to block config access
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This attempts to block config access to the IOA.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_block_config_access_wait(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ int rc = IPR_RC_JOB_CONTINUE;
+
+ if (pci_cfg_access_trylock(ioa_cfg->pdev)) {
+ ioa_cfg->cfg_locked = 1;
+ ipr_cmd->job_step = ioa_cfg->reset;
+ } else {
+ if (ipr_cmd->u.time_left) {
+ rc = IPR_RC_JOB_RETURN;
+ ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
+ ipr_reset_start_timer(ipr_cmd,
+ IPR_CHECK_FOR_RESET_TIMEOUT);
+ } else {
+ ipr_cmd->job_step = ioa_cfg->reset;
+ dev_err(&ioa_cfg->pdev->dev,
+ "Timed out waiting to lock config access. Resetting anyway.\n");
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * ipr_reset_block_config_access - Block config access to the IOA
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This attempts to block config access to the IOA
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE
+ **/
+static int ipr_reset_block_config_access(struct ipr_cmnd *ipr_cmd)
+{
+ ipr_cmd->ioa_cfg->cfg_locked = 0;
+ ipr_cmd->job_step = ipr_reset_block_config_access_wait;
+ ipr_cmd->u.time_left = IPR_WAIT_FOR_RESET_TIMEOUT;
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
* ipr_reset_allowed - Query whether or not IOA can be reset
* @ioa_cfg: ioa config struct
*
@@ -7764,7 +8114,7 @@ static int ipr_reset_wait_to_start_bist(struct ipr_cmnd *ipr_cmd)
ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT);
} else {
- ipr_cmd->job_step = ioa_cfg->reset;
+ ipr_cmd->job_step = ipr_reset_block_config_access;
rc = IPR_RC_JOB_CONTINUE;
}
@@ -7797,7 +8147,7 @@ static int ipr_reset_alert(struct ipr_cmnd *ipr_cmd)
writel(IPR_UPROCI_RESET_ALERT, ioa_cfg->regs.set_uproc_interrupt_reg32);
ipr_cmd->job_step = ipr_reset_wait_to_start_bist;
} else {
- ipr_cmd->job_step = ioa_cfg->reset;
+ ipr_cmd->job_step = ipr_reset_block_config_access;
}
ipr_cmd->u.time_left = IPR_WAIT_FOR_RESET_TIMEOUT;
@@ -7808,6 +8158,122 @@ static int ipr_reset_alert(struct ipr_cmnd *ipr_cmd)
}
/**
+ * ipr_reset_quiesce_done - Complete IOA disconnect
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: Freeze the adapter to complete quiesce processing
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE
+ **/
+static int ipr_reset_quiesce_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ ENTER;
+ ipr_cmd->job_step = ipr_ioa_bringdown_done;
+ ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
+ LEAVE;
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
+ * ipr_reset_cancel_hcam_done - Check for outstanding commands
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: Ensure nothing is outstanding to the IOA and
+ * proceed with IOA disconnect. Otherwise reset the IOA.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN / IPR_RC_JOB_CONTINUE
+ **/
+static int ipr_reset_cancel_hcam_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_cmnd *loop_cmd;
+ struct ipr_hrr_queue *hrrq;
+ int rc = IPR_RC_JOB_CONTINUE;
+ int count = 0;
+
+ ENTER;
+ ipr_cmd->job_step = ipr_reset_quiesce_done;
+
+ for_each_hrrq(hrrq, ioa_cfg) {
+ spin_lock(&hrrq->_lock);
+ list_for_each_entry(loop_cmd, &hrrq->hrrq_pending_q, queue) {
+ count++;
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
+ rc = IPR_RC_JOB_RETURN;
+ break;
+ }
+ spin_unlock(&hrrq->_lock);
+
+ if (count)
+ break;
+ }
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ipr_reset_cancel_hcam - Cancel outstanding HCAMs
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: Cancel any oustanding HCAMs to the IOA.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_cancel_hcam(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ int rc = IPR_RC_JOB_CONTINUE;
+ struct ipr_cmd_pkt *cmd_pkt;
+ struct ipr_cmnd *hcam_cmd;
+ struct ipr_hrr_queue *hrrq = &ioa_cfg->hrrq[IPR_INIT_HRRQ];
+
+ ENTER;
+ ipr_cmd->job_step = ipr_reset_cancel_hcam_done;
+
+ if (!hrrq->ioa_is_dead) {
+ if (!list_empty(&ioa_cfg->hostrcb_pending_q)) {
+ list_for_each_entry(hcam_cmd, &hrrq->hrrq_pending_q, queue) {
+ if (hcam_cmd->ioarcb.cmd_pkt.cdb[0] != IPR_HOST_CONTROLLED_ASYNC)
+ continue;
+
+ ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+ ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+ cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
+ cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
+ cmd_pkt->cdb[0] = IPR_CANCEL_REQUEST;
+ cmd_pkt->cdb[1] = IPR_CANCEL_64BIT_IOARCB;
+ cmd_pkt->cdb[10] = ((u64) hcam_cmd->dma_addr >> 56) & 0xff;
+ cmd_pkt->cdb[11] = ((u64) hcam_cmd->dma_addr >> 48) & 0xff;
+ cmd_pkt->cdb[12] = ((u64) hcam_cmd->dma_addr >> 40) & 0xff;
+ cmd_pkt->cdb[13] = ((u64) hcam_cmd->dma_addr >> 32) & 0xff;
+ cmd_pkt->cdb[2] = ((u64) hcam_cmd->dma_addr >> 24) & 0xff;
+ cmd_pkt->cdb[3] = ((u64) hcam_cmd->dma_addr >> 16) & 0xff;
+ cmd_pkt->cdb[4] = ((u64) hcam_cmd->dma_addr >> 8) & 0xff;
+ cmd_pkt->cdb[5] = ((u64) hcam_cmd->dma_addr) & 0xff;
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
+ IPR_CANCEL_TIMEOUT);
+
+ rc = IPR_RC_JOB_RETURN;
+ ipr_cmd->job_step = ipr_reset_cancel_hcam;
+ break;
+ }
+ }
+ } else
+ ipr_cmd->job_step = ipr_reset_alert;
+
+ LEAVE;
+ return rc;
+}
+
+/**
* ipr_reset_ucode_download_done - Microcode download completion
* @ipr_cmd: ipr command struct
*
@@ -7821,7 +8287,7 @@ static int ipr_reset_ucode_download_done(struct ipr_cmnd *ipr_cmd)
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct ipr_sglist *sglist = ioa_cfg->ucode_sglist;
- pci_unmap_sg(ioa_cfg->pdev, sglist->scatterlist,
+ dma_unmap_sg(&ioa_cfg->pdev->dev, sglist->scatterlist,
sglist->num_sg, DMA_TO_DEVICE);
ipr_cmd->job_step = ipr_reset_alert;
@@ -7889,7 +8355,10 @@ static int ipr_reset_shutdown_ioa(struct ipr_cmnd *ipr_cmd)
int rc = IPR_RC_JOB_CONTINUE;
ENTER;
- if (shutdown_type != IPR_SHUTDOWN_NONE && !ioa_cfg->ioa_is_dead) {
+ if (shutdown_type == IPR_SHUTDOWN_QUIESCE)
+ ipr_cmd->job_step = ipr_reset_cancel_hcam;
+ else if (shutdown_type != IPR_SHUTDOWN_NONE &&
+ !ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead) {
ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
ipr_cmd->ioarcb.cmd_pkt.cdb[0] = IPR_IOA_SHUTDOWN;
@@ -7937,7 +8406,8 @@ static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd)
* We are doing nested adapter resets and this is
* not the current reset job.
*/
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_add_tail(&ipr_cmd->queue,
+ &ipr_cmd->hrrq->hrrq_free_q);
return;
}
@@ -7950,7 +8420,7 @@ static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd)
ipr_reinit_ipr_cmnd(ipr_cmd);
ipr_cmd->job_step_failed = ipr_reset_cmd_failed;
rc = ipr_cmd->job_step(ipr_cmd);
- } while(rc == IPR_RC_JOB_CONTINUE);
+ } while (rc == IPR_RC_JOB_CONTINUE);
}
/**
@@ -7972,10 +8442,20 @@ static void _ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
enum ipr_shutdown_type shutdown_type)
{
struct ipr_cmnd *ipr_cmd;
+ int i;
ioa_cfg->in_reset_reload = 1;
- ioa_cfg->allow_cmds = 0;
- scsi_block_requests(ioa_cfg->host);
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].allow_cmds = 0;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
+ wmb();
+ if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) {
+ ioa_cfg->scsi_unblock = 0;
+ ioa_cfg->scsi_blocked = 1;
+ scsi_block_requests(ioa_cfg->host);
+ }
ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
ioa_cfg->reset_cmd = ipr_cmd;
@@ -8000,18 +8480,29 @@ static void _ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
enum ipr_shutdown_type shutdown_type)
{
- if (ioa_cfg->ioa_is_dead)
+ int i;
+
+ if (ioa_cfg->hrrq[IPR_INIT_HRRQ].ioa_is_dead)
return;
- if (ioa_cfg->in_reset_reload && ioa_cfg->sdt_state == GET_DUMP)
- ioa_cfg->sdt_state = ABORT_DUMP;
+ if (ioa_cfg->in_reset_reload) {
+ if (ioa_cfg->sdt_state == GET_DUMP)
+ ioa_cfg->sdt_state = WAIT_FOR_DUMP;
+ else if (ioa_cfg->sdt_state == READ_DUMP)
+ ioa_cfg->sdt_state = ABORT_DUMP;
+ }
if (ioa_cfg->reset_retries++ >= IPR_NUM_RESET_RELOAD_RETRIES) {
dev_err(&ioa_cfg->pdev->dev,
"IOA taken offline - error recovery failed\n");
ioa_cfg->reset_retries = 0;
- ioa_cfg->ioa_is_dead = 1;
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].ioa_is_dead = 1;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
+ wmb();
if (ioa_cfg->in_ioa_bringdown) {
ioa_cfg->reset_cmd = NULL;
@@ -8019,9 +8510,10 @@ static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
ipr_fail_all_ops(ioa_cfg);
wake_up_all(&ioa_cfg->reset_wait_q);
- spin_unlock_irq(ioa_cfg->host->host_lock);
- scsi_unblock_requests(ioa_cfg->host);
- spin_lock_irq(ioa_cfg->host->host_lock);
+ if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) {
+ ioa_cfg->scsi_unblock = 1;
+ schedule_work(&ioa_cfg->work_q);
+ }
return;
} else {
ioa_cfg->in_ioa_bringdown = 1;
@@ -8043,14 +8535,41 @@ static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
*/
static int ipr_reset_freeze(struct ipr_cmnd *ipr_cmd)
{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ int i;
+
/* Disallow new interrupts, avoid loop */
- ipr_cmd->ioa_cfg->allow_interrupts = 0;
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->ioa_cfg->pending_q);
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].allow_interrupts = 0;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
+ wmb();
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q);
ipr_cmd->done = ipr_reset_ioa_job;
return IPR_RC_JOB_RETURN;
}
/**
+ * ipr_pci_mmio_enabled - Called when MMIO has been re-enabled
+ * @pdev: PCI device struct
+ *
+ * Description: This routine is called to tell us that the MMIO
+ * access to the IOA has been restored
+ */
+static pci_ers_result_t ipr_pci_mmio_enabled(struct pci_dev *pdev)
+{
+ unsigned long flags = 0;
+ struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ if (!ioa_cfg->probe_done)
+ pci_save_state(pdev);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
* ipr_pci_frozen - Called when slot has experienced a PCI bus error.
* @pdev: PCI device struct
*
@@ -8064,7 +8583,8 @@ static void ipr_pci_frozen(struct pci_dev *pdev)
struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_freeze, IPR_SHUTDOWN_NONE);
+ if (ioa_cfg->probe_done)
+ _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_freeze, IPR_SHUTDOWN_NONE);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
}
@@ -8082,11 +8602,14 @@ static pci_ers_result_t ipr_pci_slot_reset(struct pci_dev *pdev)
struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- if (ioa_cfg->needs_warm_reset)
- ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
- else
- _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_restore_cfg_space,
- IPR_SHUTDOWN_NONE);
+ if (ioa_cfg->probe_done) {
+ if (ioa_cfg->needs_warm_reset)
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ else
+ _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_restore_cfg_space,
+ IPR_SHUTDOWN_NONE);
+ } else
+ wake_up_all(&ioa_cfg->eeh_wait_q);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
return PCI_ERS_RESULT_RECOVERED;
}
@@ -8102,14 +8625,23 @@ static void ipr_pci_perm_failure(struct pci_dev *pdev)
{
unsigned long flags = 0;
struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+ int i;
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- if (ioa_cfg->sdt_state == WAIT_FOR_DUMP)
- ioa_cfg->sdt_state = ABORT_DUMP;
- ioa_cfg->reset_retries = IPR_NUM_RESET_RELOAD_RETRIES;
- ioa_cfg->in_ioa_bringdown = 1;
- ioa_cfg->allow_cmds = 0;
- ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ if (ioa_cfg->probe_done) {
+ if (ioa_cfg->sdt_state == WAIT_FOR_DUMP)
+ ioa_cfg->sdt_state = ABORT_DUMP;
+ ioa_cfg->reset_retries = IPR_NUM_RESET_RELOAD_RETRIES - 1;
+ ioa_cfg->in_ioa_bringdown = 1;
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].allow_cmds = 0;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
+ wmb();
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ } else
+ wake_up_all(&ioa_cfg->eeh_wait_q);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
}
@@ -8129,11 +8661,10 @@ static pci_ers_result_t ipr_pci_error_detected(struct pci_dev *pdev,
switch (state) {
case pci_channel_io_frozen:
ipr_pci_frozen(pdev);
- return PCI_ERS_RESULT_NEED_RESET;
+ return PCI_ERS_RESULT_CAN_RECOVER;
case pci_channel_io_perm_failure:
ipr_pci_perm_failure(pdev);
return PCI_ERS_RESULT_DISCONNECT;
- break;
default:
break;
}
@@ -8144,46 +8675,29 @@ static pci_ers_result_t ipr_pci_error_detected(struct pci_dev *pdev,
* ipr_probe_ioa_part2 - Initializes IOAs found in ipr_probe_ioa(..)
* @ioa_cfg: ioa cfg struct
*
- * Description: This is the second phase of adapter intialization
+ * Description: This is the second phase of adapter initialization
* This function takes care of initilizing the adapter to the point
* where it can accept new commands.
-
* Return value:
- * 0 on success / -EIO on failure
+ * none
**/
-static int __devinit ipr_probe_ioa_part2(struct ipr_ioa_cfg *ioa_cfg)
+static void ipr_probe_ioa_part2(struct ipr_ioa_cfg *ioa_cfg)
{
- int rc = 0;
unsigned long host_lock_flags = 0;
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
dev_dbg(&ioa_cfg->pdev->dev, "ioa_cfg adx: 0x%p\n", ioa_cfg);
+ ioa_cfg->probe_done = 1;
if (ioa_cfg->needs_hard_reset) {
ioa_cfg->needs_hard_reset = 0;
ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
} else
_ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_enable_ioa,
IPR_SHUTDOWN_NONE);
-
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
- wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
- spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
-
- if (ioa_cfg->ioa_is_dead) {
- rc = -EIO;
- } else if (ipr_invalid_adapter(ioa_cfg)) {
- if (!ipr_testmode)
- rc = -EIO;
-
- dev_err(&ioa_cfg->pdev->dev,
- "Adapter not supported in this hardware configuration.\n");
- }
-
spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
LEAVE;
- return rc;
}
/**
@@ -8197,18 +8711,23 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
{
int i;
- for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
- if (ioa_cfg->ipr_cmnd_list[i])
- pci_pool_free(ioa_cfg->ipr_cmd_pool,
- ioa_cfg->ipr_cmnd_list[i],
- ioa_cfg->ipr_cmnd_list_dma[i]);
+ if (ioa_cfg->ipr_cmnd_list) {
+ for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
+ if (ioa_cfg->ipr_cmnd_list[i])
+ dma_pool_free(ioa_cfg->ipr_cmd_pool,
+ ioa_cfg->ipr_cmnd_list[i],
+ ioa_cfg->ipr_cmnd_list_dma[i]);
- ioa_cfg->ipr_cmnd_list[i] = NULL;
+ ioa_cfg->ipr_cmnd_list[i] = NULL;
+ }
}
- if (ioa_cfg->ipr_cmd_pool)
- pci_pool_destroy (ioa_cfg->ipr_cmd_pool);
+ dma_pool_destroy(ioa_cfg->ipr_cmd_pool);
+ kfree(ioa_cfg->ipr_cmnd_list);
+ kfree(ioa_cfg->ipr_cmnd_list_dma);
+ ioa_cfg->ipr_cmnd_list = NULL;
+ ioa_cfg->ipr_cmnd_list_dma = NULL;
ioa_cfg->ipr_cmd_pool = NULL;
}
@@ -8224,20 +8743,24 @@ static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg)
int i;
kfree(ioa_cfg->res_entries);
- pci_free_consistent(ioa_cfg->pdev, sizeof(struct ipr_misc_cbs),
- ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
+ dma_free_coherent(&ioa_cfg->pdev->dev, sizeof(struct ipr_misc_cbs),
+ ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
ipr_free_cmd_blks(ioa_cfg);
- pci_free_consistent(ioa_cfg->pdev, sizeof(u32) * IPR_NUM_CMD_BLKS,
- ioa_cfg->host_rrq, ioa_cfg->host_rrq_dma);
- pci_free_consistent(ioa_cfg->pdev, ioa_cfg->cfg_table_size,
- ioa_cfg->u.cfg_table,
- ioa_cfg->cfg_table_dma);
- for (i = 0; i < IPR_NUM_HCAMS; i++) {
- pci_free_consistent(ioa_cfg->pdev,
- sizeof(struct ipr_hostrcb),
- ioa_cfg->hostrcb[i],
- ioa_cfg->hostrcb_dma[i]);
+ for (i = 0; i < ioa_cfg->hrrq_num; i++)
+ dma_free_coherent(&ioa_cfg->pdev->dev,
+ sizeof(u32) * ioa_cfg->hrrq[i].size,
+ ioa_cfg->hrrq[i].host_rrq,
+ ioa_cfg->hrrq[i].host_rrq_dma);
+
+ dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size,
+ ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma);
+
+ for (i = 0; i < IPR_MAX_HCAMS; i++) {
+ dma_free_coherent(&ioa_cfg->pdev->dev,
+ sizeof(struct ipr_hostrcb),
+ ioa_cfg->hostrcb[i],
+ ioa_cfg->hostrcb_dma[i]);
}
ipr_free_dump(ioa_cfg);
@@ -8245,8 +8768,28 @@ static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg)
}
/**
+ * ipr_free_irqs - Free all allocated IRQs for the adapter.
+ * @ioa_cfg: ipr cfg struct
+ *
+ * This function frees all allocated IRQs for the
+ * specified adapter.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_free_irqs(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct pci_dev *pdev = ioa_cfg->pdev;
+ int i;
+
+ for (i = 0; i < ioa_cfg->nvectors; i++)
+ free_irq(pci_irq_vector(pdev, i), &ioa_cfg->hrrq[i]);
+ pci_free_irq_vectors(pdev);
+}
+
+/**
* ipr_free_all_resources - Free all allocated resources for an adapter.
- * @ipr_cmd: ipr command struct
+ * @ioa_cfg: ioa config struct
*
* This function frees all allocated resources for the
* specified adapter.
@@ -8259,8 +8802,9 @@ static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg)
struct pci_dev *pdev = ioa_cfg->pdev;
ENTER;
- free_irq(pdev->irq, ioa_cfg);
- pci_disable_msi(pdev);
+ ipr_free_irqs(ioa_cfg);
+ if (ioa_cfg->reset_work_q)
+ destroy_workqueue(ioa_cfg->reset_work_q);
iounmap(ioa_cfg->hdw_dma_regs);
pci_release_regions(pdev);
ipr_free_mem(ioa_cfg);
@@ -8276,28 +8820,71 @@ static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg)
* Return value:
* 0 on success / -ENOMEM on allocation failure
**/
-static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
+static int ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
{
struct ipr_cmnd *ipr_cmd;
struct ipr_ioarcb *ioarcb;
dma_addr_t dma_addr;
- int i;
+ int i, entries_each_hrrq, hrrq_id = 0;
- ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev,
- sizeof(struct ipr_cmnd), 16, 0);
+ ioa_cfg->ipr_cmd_pool = dma_pool_create(IPR_NAME, &ioa_cfg->pdev->dev,
+ sizeof(struct ipr_cmnd), 512, 0);
if (!ioa_cfg->ipr_cmd_pool)
return -ENOMEM;
+ ioa_cfg->ipr_cmnd_list = kcalloc(IPR_NUM_CMD_BLKS, sizeof(struct ipr_cmnd *), GFP_KERNEL);
+ ioa_cfg->ipr_cmnd_list_dma = kcalloc(IPR_NUM_CMD_BLKS, sizeof(dma_addr_t), GFP_KERNEL);
+
+ if (!ioa_cfg->ipr_cmnd_list || !ioa_cfg->ipr_cmnd_list_dma) {
+ ipr_free_cmd_blks(ioa_cfg);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ if (ioa_cfg->hrrq_num > 1) {
+ if (i == 0) {
+ entries_each_hrrq = IPR_NUM_INTERNAL_CMD_BLKS;
+ ioa_cfg->hrrq[i].min_cmd_id = 0;
+ ioa_cfg->hrrq[i].max_cmd_id =
+ (entries_each_hrrq - 1);
+ } else {
+ entries_each_hrrq =
+ IPR_NUM_BASE_CMD_BLKS/
+ (ioa_cfg->hrrq_num - 1);
+ ioa_cfg->hrrq[i].min_cmd_id =
+ IPR_NUM_INTERNAL_CMD_BLKS +
+ (i - 1) * entries_each_hrrq;
+ ioa_cfg->hrrq[i].max_cmd_id =
+ (IPR_NUM_INTERNAL_CMD_BLKS +
+ i * entries_each_hrrq - 1);
+ }
+ } else {
+ entries_each_hrrq = IPR_NUM_CMD_BLKS;
+ ioa_cfg->hrrq[i].min_cmd_id = 0;
+ ioa_cfg->hrrq[i].max_cmd_id = (entries_each_hrrq - 1);
+ }
+ ioa_cfg->hrrq[i].size = entries_each_hrrq;
+ }
+
+ BUG_ON(ioa_cfg->hrrq_num == 0);
+
+ i = IPR_NUM_CMD_BLKS -
+ ioa_cfg->hrrq[ioa_cfg->hrrq_num - 1].max_cmd_id - 1;
+ if (i > 0) {
+ ioa_cfg->hrrq[ioa_cfg->hrrq_num - 1].size += i;
+ ioa_cfg->hrrq[ioa_cfg->hrrq_num - 1].max_cmd_id += i;
+ }
+
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
- ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
+ ipr_cmd = dma_pool_zalloc(ioa_cfg->ipr_cmd_pool,
+ GFP_KERNEL, &dma_addr);
if (!ipr_cmd) {
ipr_free_cmd_blks(ioa_cfg);
return -ENOMEM;
}
- memset(ipr_cmd, 0, sizeof(*ipr_cmd));
ioa_cfg->ipr_cmnd_list[i] = ipr_cmd;
ioa_cfg->ipr_cmnd_list_dma[i] = dma_addr;
@@ -8327,7 +8914,11 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
ipr_cmd->sense_buffer_dma = dma_addr +
offsetof(struct ipr_cmnd, sense_buffer);
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ ipr_cmd->ioarcb.cmd_pkt.hrrq_id = hrrq_id;
+ ipr_cmd->hrrq = &ioa_cfg->hrrq[hrrq_id];
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
+ if (i >= ioa_cfg->hrrq[hrrq_id].max_cmd_id)
+ hrrq_id++;
}
return 0;
@@ -8340,35 +8931,28 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
* Return value:
* 0 on success / non-zero for error
**/
-static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg)
+static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg)
{
struct pci_dev *pdev = ioa_cfg->pdev;
int i, rc = -ENOMEM;
ENTER;
- ioa_cfg->res_entries = kzalloc(sizeof(struct ipr_resource_entry) *
- ioa_cfg->max_devs_supported, GFP_KERNEL);
+ ioa_cfg->res_entries = kcalloc(ioa_cfg->max_devs_supported,
+ sizeof(struct ipr_resource_entry),
+ GFP_KERNEL);
if (!ioa_cfg->res_entries)
goto out;
- if (ioa_cfg->sis64) {
- ioa_cfg->target_ids = kzalloc(sizeof(unsigned long) *
- BITS_TO_LONGS(ioa_cfg->max_devs_supported), GFP_KERNEL);
- ioa_cfg->array_ids = kzalloc(sizeof(unsigned long) *
- BITS_TO_LONGS(ioa_cfg->max_devs_supported), GFP_KERNEL);
- ioa_cfg->vset_ids = kzalloc(sizeof(unsigned long) *
- BITS_TO_LONGS(ioa_cfg->max_devs_supported), GFP_KERNEL);
- }
-
for (i = 0; i < ioa_cfg->max_devs_supported; i++) {
list_add_tail(&ioa_cfg->res_entries[i].queue, &ioa_cfg->free_res_q);
ioa_cfg->res_entries[i].ioa_cfg = ioa_cfg;
}
- ioa_cfg->vpd_cbs = pci_alloc_consistent(ioa_cfg->pdev,
- sizeof(struct ipr_misc_cbs),
- &ioa_cfg->vpd_cbs_dma);
+ ioa_cfg->vpd_cbs = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct ipr_misc_cbs),
+ &ioa_cfg->vpd_cbs_dma,
+ GFP_KERNEL);
if (!ioa_cfg->vpd_cbs)
goto out_free_res_entries;
@@ -8376,24 +8960,36 @@ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg)
if (ipr_alloc_cmd_blks(ioa_cfg))
goto out_free_vpd_cbs;
- ioa_cfg->host_rrq = pci_alloc_consistent(ioa_cfg->pdev,
- sizeof(u32) * IPR_NUM_CMD_BLKS,
- &ioa_cfg->host_rrq_dma);
-
- if (!ioa_cfg->host_rrq)
- goto out_ipr_free_cmd_blocks;
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ ioa_cfg->hrrq[i].host_rrq = dma_alloc_coherent(&pdev->dev,
+ sizeof(u32) * ioa_cfg->hrrq[i].size,
+ &ioa_cfg->hrrq[i].host_rrq_dma,
+ GFP_KERNEL);
+
+ if (!ioa_cfg->hrrq[i].host_rrq) {
+ while (--i >= 0)
+ dma_free_coherent(&pdev->dev,
+ sizeof(u32) * ioa_cfg->hrrq[i].size,
+ ioa_cfg->hrrq[i].host_rrq,
+ ioa_cfg->hrrq[i].host_rrq_dma);
+ goto out_ipr_free_cmd_blocks;
+ }
+ ioa_cfg->hrrq[i].ioa_cfg = ioa_cfg;
+ }
- ioa_cfg->u.cfg_table = pci_alloc_consistent(ioa_cfg->pdev,
- ioa_cfg->cfg_table_size,
- &ioa_cfg->cfg_table_dma);
+ ioa_cfg->u.cfg_table = dma_alloc_coherent(&pdev->dev,
+ ioa_cfg->cfg_table_size,
+ &ioa_cfg->cfg_table_dma,
+ GFP_KERNEL);
if (!ioa_cfg->u.cfg_table)
goto out_free_host_rrq;
- for (i = 0; i < IPR_NUM_HCAMS; i++) {
- ioa_cfg->hostrcb[i] = pci_alloc_consistent(ioa_cfg->pdev,
- sizeof(struct ipr_hostrcb),
- &ioa_cfg->hostrcb_dma[i]);
+ for (i = 0; i < IPR_MAX_HCAMS; i++) {
+ ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct ipr_hostrcb),
+ &ioa_cfg->hostrcb_dma[i],
+ GFP_KERNEL);
if (!ioa_cfg->hostrcb[i])
goto out_free_hostrcb_dma;
@@ -8404,8 +9000,9 @@ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg)
list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q);
}
- ioa_cfg->trace = kzalloc(sizeof(struct ipr_trace_entry) *
- IPR_NUM_TRACE_ENTRIES, GFP_KERNEL);
+ ioa_cfg->trace = kcalloc(IPR_NUM_TRACE_ENTRIES,
+ sizeof(struct ipr_trace_entry),
+ GFP_KERNEL);
if (!ioa_cfg->trace)
goto out_free_hostrcb_dma;
@@ -8417,21 +9014,24 @@ out:
out_free_hostrcb_dma:
while (i-- > 0) {
- pci_free_consistent(pdev, sizeof(struct ipr_hostrcb),
- ioa_cfg->hostrcb[i],
- ioa_cfg->hostrcb_dma[i]);
+ dma_free_coherent(&pdev->dev, sizeof(struct ipr_hostrcb),
+ ioa_cfg->hostrcb[i],
+ ioa_cfg->hostrcb_dma[i]);
}
- pci_free_consistent(pdev, ioa_cfg->cfg_table_size,
- ioa_cfg->u.cfg_table,
- ioa_cfg->cfg_table_dma);
+ dma_free_coherent(&pdev->dev, ioa_cfg->cfg_table_size,
+ ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma);
out_free_host_rrq:
- pci_free_consistent(pdev, sizeof(u32) * IPR_NUM_CMD_BLKS,
- ioa_cfg->host_rrq, ioa_cfg->host_rrq_dma);
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ dma_free_coherent(&pdev->dev,
+ sizeof(u32) * ioa_cfg->hrrq[i].size,
+ ioa_cfg->hrrq[i].host_rrq,
+ ioa_cfg->hrrq[i].host_rrq_dma);
+ }
out_ipr_free_cmd_blocks:
ipr_free_cmd_blks(ioa_cfg);
out_free_vpd_cbs:
- pci_free_consistent(pdev, sizeof(struct ipr_misc_cbs),
- ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
+ dma_free_coherent(&pdev->dev, sizeof(struct ipr_misc_cbs),
+ ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
out_free_res_entries:
kfree(ioa_cfg->res_entries);
goto out;
@@ -8444,7 +9044,7 @@ out_free_res_entries:
* Return value:
* none
**/
-static void __devinit ipr_initialize_bus_attr(struct ipr_ioa_cfg *ioa_cfg)
+static void ipr_initialize_bus_attr(struct ipr_ioa_cfg *ioa_cfg)
{
int i;
@@ -8460,6 +9060,48 @@ static void __devinit ipr_initialize_bus_attr(struct ipr_ioa_cfg *ioa_cfg)
}
/**
+ * ipr_init_regs - Initialize IOA registers
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_init_regs(struct ipr_ioa_cfg *ioa_cfg)
+{
+ const struct ipr_interrupt_offsets *p;
+ struct ipr_interrupts *t;
+ void __iomem *base;
+
+ p = &ioa_cfg->chip_cfg->regs;
+ t = &ioa_cfg->regs;
+ base = ioa_cfg->hdw_dma_regs;
+
+ t->set_interrupt_mask_reg = base + p->set_interrupt_mask_reg;
+ t->clr_interrupt_mask_reg = base + p->clr_interrupt_mask_reg;
+ t->clr_interrupt_mask_reg32 = base + p->clr_interrupt_mask_reg32;
+ t->sense_interrupt_mask_reg = base + p->sense_interrupt_mask_reg;
+ t->sense_interrupt_mask_reg32 = base + p->sense_interrupt_mask_reg32;
+ t->clr_interrupt_reg = base + p->clr_interrupt_reg;
+ t->clr_interrupt_reg32 = base + p->clr_interrupt_reg32;
+ t->sense_interrupt_reg = base + p->sense_interrupt_reg;
+ t->sense_interrupt_reg32 = base + p->sense_interrupt_reg32;
+ t->ioarrin_reg = base + p->ioarrin_reg;
+ t->sense_uproc_interrupt_reg = base + p->sense_uproc_interrupt_reg;
+ t->sense_uproc_interrupt_reg32 = base + p->sense_uproc_interrupt_reg32;
+ t->set_uproc_interrupt_reg = base + p->set_uproc_interrupt_reg;
+ t->set_uproc_interrupt_reg32 = base + p->set_uproc_interrupt_reg32;
+ t->clr_uproc_interrupt_reg = base + p->clr_uproc_interrupt_reg;
+ t->clr_uproc_interrupt_reg32 = base + p->clr_uproc_interrupt_reg32;
+
+ if (ioa_cfg->sis64) {
+ t->init_feedback_reg = base + p->init_feedback_reg;
+ t->dump_addr_reg = base + p->dump_addr_reg;
+ t->dump_data_reg = base + p->dump_data_reg;
+ t->endian_swap_reg = base + p->endian_swap_reg;
+ }
+}
+
+/**
* ipr_init_ioa_cfg - Initialize IOA config struct
* @ioa_cfg: ioa config struct
* @host: scsi host struct
@@ -8468,12 +9110,10 @@ static void __devinit ipr_initialize_bus_attr(struct ipr_ioa_cfg *ioa_cfg)
* Return value:
* none
**/
-static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
- struct Scsi_Host *host, struct pci_dev *pdev)
+static void ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
+ struct Scsi_Host *host, struct pci_dev *pdev)
{
- const struct ipr_interrupt_offsets *p;
- struct ipr_interrupts *t;
- void __iomem *base;
+ int i;
ioa_cfg->host = host;
ioa_cfg->pdev = pdev;
@@ -8481,69 +9121,59 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
ioa_cfg->doorbell = IPR_DOORBELL;
sprintf(ioa_cfg->eye_catcher, IPR_EYECATCHER);
sprintf(ioa_cfg->trace_start, IPR_TRACE_START_LABEL);
- sprintf(ioa_cfg->ipr_free_label, IPR_FREEQ_LABEL);
- sprintf(ioa_cfg->ipr_pending_label, IPR_PENDQ_LABEL);
sprintf(ioa_cfg->cfg_table_start, IPR_CFG_TBL_START);
sprintf(ioa_cfg->resource_table_label, IPR_RES_TABLE_LABEL);
sprintf(ioa_cfg->ipr_hcam_label, IPR_HCAM_LABEL);
sprintf(ioa_cfg->ipr_cmd_label, IPR_CMD_LABEL);
- INIT_LIST_HEAD(&ioa_cfg->free_q);
- INIT_LIST_HEAD(&ioa_cfg->pending_q);
INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q);
INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q);
+ INIT_LIST_HEAD(&ioa_cfg->hostrcb_report_q);
INIT_LIST_HEAD(&ioa_cfg->free_res_q);
INIT_LIST_HEAD(&ioa_cfg->used_res_q);
INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread);
+ INIT_WORK(&ioa_cfg->scsi_add_work_q, ipr_add_remove_thread);
init_waitqueue_head(&ioa_cfg->reset_wait_q);
init_waitqueue_head(&ioa_cfg->msi_wait_q);
+ init_waitqueue_head(&ioa_cfg->eeh_wait_q);
ioa_cfg->sdt_state = INACTIVE;
ipr_initialize_bus_attr(ioa_cfg);
ioa_cfg->max_devs_supported = ipr_max_devs;
if (ioa_cfg->sis64) {
+ host->max_channel = IPR_MAX_SIS64_BUSES;
host->max_id = IPR_MAX_SIS64_TARGETS_PER_BUS;
host->max_lun = IPR_MAX_SIS64_LUNS_PER_TARGET;
if (ipr_max_devs > IPR_MAX_SIS64_DEVS)
ioa_cfg->max_devs_supported = IPR_MAX_SIS64_DEVS;
+ ioa_cfg->cfg_table_size = (sizeof(struct ipr_config_table_hdr64)
+ + ((sizeof(struct ipr_config_table_entry64)
+ * ioa_cfg->max_devs_supported)));
} else {
+ host->max_channel = IPR_VSET_BUS;
host->max_id = IPR_MAX_NUM_TARGETS_PER_BUS;
host->max_lun = IPR_MAX_NUM_LUNS_PER_TARGET;
if (ipr_max_devs > IPR_MAX_PHYSICAL_DEVS)
ioa_cfg->max_devs_supported = IPR_MAX_PHYSICAL_DEVS;
+ ioa_cfg->cfg_table_size = (sizeof(struct ipr_config_table_hdr)
+ + ((sizeof(struct ipr_config_table_entry)
+ * ioa_cfg->max_devs_supported)));
}
- host->max_channel = IPR_MAX_BUS_TO_SCAN;
+
host->unique_id = host->host_no;
host->max_cmd_len = IPR_MAX_CDB_LEN;
+ host->can_queue = ioa_cfg->max_cmds;
pci_set_drvdata(pdev, ioa_cfg);
- p = &ioa_cfg->chip_cfg->regs;
- t = &ioa_cfg->regs;
- base = ioa_cfg->hdw_dma_regs;
-
- t->set_interrupt_mask_reg = base + p->set_interrupt_mask_reg;
- t->clr_interrupt_mask_reg = base + p->clr_interrupt_mask_reg;
- t->clr_interrupt_mask_reg32 = base + p->clr_interrupt_mask_reg32;
- t->sense_interrupt_mask_reg = base + p->sense_interrupt_mask_reg;
- t->sense_interrupt_mask_reg32 = base + p->sense_interrupt_mask_reg32;
- t->clr_interrupt_reg = base + p->clr_interrupt_reg;
- t->clr_interrupt_reg32 = base + p->clr_interrupt_reg32;
- t->sense_interrupt_reg = base + p->sense_interrupt_reg;
- t->sense_interrupt_reg32 = base + p->sense_interrupt_reg32;
- t->ioarrin_reg = base + p->ioarrin_reg;
- t->sense_uproc_interrupt_reg = base + p->sense_uproc_interrupt_reg;
- t->sense_uproc_interrupt_reg32 = base + p->sense_uproc_interrupt_reg32;
- t->set_uproc_interrupt_reg = base + p->set_uproc_interrupt_reg;
- t->set_uproc_interrupt_reg32 = base + p->set_uproc_interrupt_reg32;
- t->clr_uproc_interrupt_reg = base + p->clr_uproc_interrupt_reg;
- t->clr_uproc_interrupt_reg32 = base + p->clr_uproc_interrupt_reg32;
-
- if (ioa_cfg->sis64) {
- t->init_feedback_reg = base + p->init_feedback_reg;
- t->dump_addr_reg = base + p->dump_addr_reg;
- t->dump_data_reg = base + p->dump_data_reg;
- t->endian_swap_reg = base + p->endian_swap_reg;
+ for (i = 0; i < ARRAY_SIZE(ioa_cfg->hrrq); i++) {
+ INIT_LIST_HEAD(&ioa_cfg->hrrq[i].hrrq_free_q);
+ INIT_LIST_HEAD(&ioa_cfg->hrrq[i].hrrq_pending_q);
+ spin_lock_init(&ioa_cfg->hrrq[i]._lock);
+ if (i == 0)
+ ioa_cfg->hrrq[i].lock = ioa_cfg->host->host_lock;
+ else
+ ioa_cfg->hrrq[i].lock = &ioa_cfg->hrrq[i]._lock;
}
}
@@ -8554,7 +9184,7 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
* Return value:
* ptr to chip information on success / NULL on failure
**/
-static const struct ipr_chip_t * __devinit
+static const struct ipr_chip_t *
ipr_get_chip_info(const struct pci_device_id *dev_id)
{
int i;
@@ -8567,8 +9197,62 @@ ipr_get_chip_info(const struct pci_device_id *dev_id)
}
/**
+ * ipr_wait_for_pci_err_recovery - Wait for any PCI error recovery to complete
+ * during probe time
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * None
+ **/
+static void ipr_wait_for_pci_err_recovery(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct pci_dev *pdev = ioa_cfg->pdev;
+
+ if (pci_channel_offline(pdev)) {
+ wait_event_timeout(ioa_cfg->eeh_wait_q,
+ !pci_channel_offline(pdev),
+ IPR_PCI_ERROR_RECOVERY_TIMEOUT);
+ pci_restore_state(pdev);
+ }
+}
+
+static void name_msi_vectors(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int vec_idx, n = sizeof(ioa_cfg->vectors_info[0].desc) - 1;
+
+ for (vec_idx = 0; vec_idx < ioa_cfg->nvectors; vec_idx++) {
+ snprintf(ioa_cfg->vectors_info[vec_idx].desc, n,
+ "host%d-%d", ioa_cfg->host->host_no, vec_idx);
+ ioa_cfg->vectors_info[vec_idx].
+ desc[strlen(ioa_cfg->vectors_info[vec_idx].desc)] = 0;
+ }
+}
+
+static int ipr_request_other_msi_irqs(struct ipr_ioa_cfg *ioa_cfg,
+ struct pci_dev *pdev)
+{
+ int i, rc;
+
+ for (i = 1; i < ioa_cfg->nvectors; i++) {
+ rc = request_irq(pci_irq_vector(pdev, i),
+ ipr_isr_mhrrq,
+ 0,
+ ioa_cfg->vectors_info[i].desc,
+ &ioa_cfg->hrrq[i]);
+ if (rc) {
+ while (--i > 0)
+ free_irq(pci_irq_vector(pdev, i),
+ &ioa_cfg->hrrq[i]);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+/**
* ipr_test_intr - Handle the interrupt generated in ipr_test_msi().
- * @pdev: PCI device struct
+ * @devp: PCI device struct
+ * @irq: IRQ number
*
* Description: Simply set the msi_received flag to 1 indicating that
* Message Signaled Interrupts are supported.
@@ -8576,39 +9260,38 @@ ipr_get_chip_info(const struct pci_device_id *dev_id)
* Return value:
* 0 on success / non-zero on failure
**/
-static irqreturn_t __devinit ipr_test_intr(int irq, void *devp)
+static irqreturn_t ipr_test_intr(int irq, void *devp)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp;
unsigned long lock_flags = 0;
- irqreturn_t rc = IRQ_HANDLED;
+ dev_info(&ioa_cfg->pdev->dev, "Received IRQ : %d\n", irq);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
ioa_cfg->msi_received = 1;
wake_up(&ioa_cfg->msi_wait_q);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return rc;
+ return IRQ_HANDLED;
}
/**
* ipr_test_msi - Test for Message Signaled Interrupt (MSI) support.
+ * @ioa_cfg: ioa config struct
* @pdev: PCI device struct
*
- * Description: The return value from pci_enable_msi() can not always be
- * trusted. This routine sets up and initiates a test interrupt to determine
+ * Description: This routine sets up and initiates a test interrupt to determine
* if the interrupt is received via the ipr_test_intr() service routine.
* If the tests fails, the driver will fall back to LSI.
*
* Return value:
* 0 on success / non-zero on failure
**/
-static int __devinit ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg,
- struct pci_dev *pdev)
+static int ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg, struct pci_dev *pdev)
{
int rc;
- volatile u32 int_reg;
unsigned long lock_flags = 0;
+ int irq = pci_irq_vector(pdev, 0);
ENTER;
@@ -8617,22 +9300,22 @@ static int __devinit ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg,
ioa_cfg->msi_received = 0;
ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_mask_reg32);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
+ readl(ioa_cfg->regs.sense_interrupt_mask_reg);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- rc = request_irq(pdev->irq, ipr_test_intr, 0, IPR_NAME, ioa_cfg);
+ rc = request_irq(irq, ipr_test_intr, 0, IPR_NAME, ioa_cfg);
if (rc) {
- dev_err(&pdev->dev, "Can not assign irq %d\n", pdev->irq);
+ dev_err(&pdev->dev, "Can not assign irq %d\n", irq);
return rc;
} else if (ipr_debug)
- dev_info(&pdev->dev, "IRQ assigned: %d\n", pdev->irq);
+ dev_info(&pdev->dev, "IRQ assigned: %d\n", irq);
writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.sense_interrupt_reg32);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
+ readl(ioa_cfg->regs.sense_interrupt_reg);
wait_event_timeout(ioa_cfg->msi_wait_q, ioa_cfg->msi_received, HZ);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
- spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
if (!ioa_cfg->msi_received) {
/* MSI test failed */
dev_info(&pdev->dev, "MSI test failed. Falling back to LSI.\n");
@@ -8642,23 +9325,22 @@ static int __devinit ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg,
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- free_irq(pdev->irq, ioa_cfg);
+ free_irq(irq, ioa_cfg);
LEAVE;
return rc;
}
-/**
- * ipr_probe_ioa - Allocates memory and does first stage of initialization
+ /* ipr_probe_ioa - Allocates memory and does first stage of initialization
* @pdev: PCI device struct
* @dev_id: PCI device id struct
*
* Return value:
* 0 on success / non-zero on failure
**/
-static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
- const struct pci_device_id *dev_id)
+static int ipr_probe_ioa(struct pci_dev *pdev,
+ const struct pci_device_id *dev_id)
{
struct ipr_ioa_cfg *ioa_cfg;
struct Scsi_Host *host;
@@ -8666,28 +9348,22 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
void __iomem *ipr_regs;
int rc = PCIBIOS_SUCCESSFUL;
volatile u32 mask, uproc, interrupts;
+ unsigned long lock_flags, driver_lock_flags;
+ unsigned int irq_flag;
ENTER;
- if ((rc = pci_enable_device(pdev))) {
- dev_err(&pdev->dev, "Cannot enable adapter\n");
- goto out;
- }
-
dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq);
-
host = scsi_host_alloc(&driver_template, sizeof(*ioa_cfg));
if (!host) {
dev_err(&pdev->dev, "call to scsi_host_alloc failed!\n");
rc = -ENOMEM;
- goto out_disable;
+ goto out;
}
ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata;
memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg));
- ata_host_init(&ioa_cfg->ata_host, &pdev->dev,
- sata_port_info.flags, &ipr_sata_ops);
ioa_cfg->ipr_chip = ipr_get_chip_info(dev_id);
@@ -8700,6 +9376,8 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
/* set SIS 32 or SIS 64 */
ioa_cfg->sis64 = ioa_cfg->ipr_chip->sis_type == IPR_SIS64 ? 1 : 0;
ioa_cfg->chip_cfg = ioa_cfg->ipr_chip->cfg;
+ ioa_cfg->clear_isr = ioa_cfg->chip_cfg->clear_isr;
+ ioa_cfg->max_cmds = ioa_cfg->chip_cfg->max_cmds;
if (ipr_transop_timeout)
ioa_cfg->transop_timeout = ipr_transop_timeout;
@@ -8710,6 +9388,8 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
ioa_cfg->revid = pdev->revision;
+ ipr_init_ioa_cfg(ioa_cfg, host, pdev);
+
ipr_regs_pci = pci_resource_start(pdev, 0);
rc = pci_request_regions(pdev, IPR_NAME);
@@ -8719,35 +9399,48 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
goto out_scsi_host_put;
}
+ rc = pci_enable_device(pdev);
+
+ if (rc || pci_channel_offline(pdev)) {
+ if (pci_channel_offline(pdev)) {
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
+ rc = pci_enable_device(pdev);
+ }
+
+ if (rc) {
+ dev_err(&pdev->dev, "Cannot enable adapter\n");
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
+ goto out_release_regions;
+ }
+ }
+
ipr_regs = pci_ioremap_bar(pdev, 0);
if (!ipr_regs) {
dev_err(&pdev->dev,
"Couldn't map memory range of registers\n");
rc = -ENOMEM;
- goto out_release_regions;
+ goto out_disable;
}
ioa_cfg->hdw_dma_regs = ipr_regs;
ioa_cfg->hdw_dma_regs_pci = ipr_regs_pci;
ioa_cfg->ioa_mailbox = ioa_cfg->chip_cfg->mailbox + ipr_regs;
- ipr_init_ioa_cfg(ioa_cfg, host, pdev);
-
- pci_set_master(pdev);
+ ipr_init_regs(ioa_cfg);
if (ioa_cfg->sis64) {
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc < 0) {
- dev_dbg(&pdev->dev, "Failed to set 64 bit PCI DMA mask\n");
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ dev_dbg(&pdev->dev, "Failed to set 64 bit DMA mask\n");
+ rc = dma_set_mask_and_coherent(&pdev->dev,
+ DMA_BIT_MASK(32));
}
-
} else
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (rc < 0) {
- dev_err(&pdev->dev, "Failed to set PCI DMA mask\n");
+ dev_err(&pdev->dev, "Failed to set DMA mask\n");
goto cleanup_nomem;
}
@@ -8756,51 +9449,89 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
if (rc != PCIBIOS_SUCCESSFUL) {
dev_err(&pdev->dev, "Write of cache line size failed\n");
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
rc = -EIO;
goto cleanup_nomem;
}
- /* Enable MSI style interrupts if they are supported. */
- if (ioa_cfg->ipr_chip->intr_type == IPR_USE_MSI && !pci_enable_msi(pdev)) {
- rc = ipr_test_msi(ioa_cfg, pdev);
- if (rc == -EOPNOTSUPP)
- pci_disable_msi(pdev);
- else if (rc)
- goto out_msi_disable;
- else
- dev_info(&pdev->dev, "MSI enabled with IRQ: %d\n", pdev->irq);
- } else if (ipr_debug)
- dev_info(&pdev->dev, "Cannot enable MSI.\n");
+ /* Issue MMIO read to ensure card is not in EEH */
+ interrupts = readl(ioa_cfg->regs.sense_interrupt_reg);
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
- /* Save away PCI config space for use following IOA reset */
- rc = pci_save_state(pdev);
+ if (ipr_number_of_msix > IPR_MAX_MSIX_VECTORS) {
+ dev_err(&pdev->dev, "The max number of MSIX is %d\n",
+ IPR_MAX_MSIX_VECTORS);
+ ipr_number_of_msix = IPR_MAX_MSIX_VECTORS;
+ }
- if (rc != PCIBIOS_SUCCESSFUL) {
- dev_err(&pdev->dev, "Failed to save PCI config space\n");
- rc = -EIO;
+ irq_flag = PCI_IRQ_INTX;
+ if (ioa_cfg->ipr_chip->has_msi)
+ irq_flag |= PCI_IRQ_MSI | PCI_IRQ_MSIX;
+ rc = pci_alloc_irq_vectors(pdev, 1, ipr_number_of_msix, irq_flag);
+ if (rc < 0) {
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
goto cleanup_nomem;
}
+ ioa_cfg->nvectors = rc;
+
+ if (!pdev->msi_enabled && !pdev->msix_enabled)
+ ioa_cfg->clear_isr = 1;
+
+ pci_set_master(pdev);
+
+ if (pci_channel_offline(pdev)) {
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
+ pci_set_master(pdev);
+ if (pci_channel_offline(pdev)) {
+ rc = -EIO;
+ goto out_msi_disable;
+ }
+ }
+
+ if (pdev->msi_enabled || pdev->msix_enabled) {
+ rc = ipr_test_msi(ioa_cfg, pdev);
+ switch (rc) {
+ case 0:
+ dev_info(&pdev->dev,
+ "Request for %d MSI%ss succeeded.", ioa_cfg->nvectors,
+ pdev->msix_enabled ? "-X" : "");
+ break;
+ case -EOPNOTSUPP:
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
+ pci_free_irq_vectors(pdev);
+
+ ioa_cfg->nvectors = 1;
+ ioa_cfg->clear_isr = 1;
+ break;
+ default:
+ goto out_msi_disable;
+ }
+ }
+
+ ioa_cfg->hrrq_num = min3(ioa_cfg->nvectors,
+ (unsigned int)num_online_cpus(),
+ (unsigned int)IPR_MAX_HRRQ_NUM);
if ((rc = ipr_save_pcix_cmd_reg(ioa_cfg)))
- goto cleanup_nomem;
+ goto out_msi_disable;
if ((rc = ipr_set_pcix_cmd_reg(ioa_cfg)))
- goto cleanup_nomem;
-
- if (ioa_cfg->sis64)
- ioa_cfg->cfg_table_size = (sizeof(struct ipr_config_table_hdr64)
- + ((sizeof(struct ipr_config_table_entry64)
- * ioa_cfg->max_devs_supported)));
- else
- ioa_cfg->cfg_table_size = (sizeof(struct ipr_config_table_hdr)
- + ((sizeof(struct ipr_config_table_entry)
- * ioa_cfg->max_devs_supported)));
+ goto out_msi_disable;
rc = ipr_alloc_mem(ioa_cfg);
if (rc < 0) {
dev_err(&pdev->dev,
"Couldn't allocate enough memory for device driver!\n");
- goto cleanup_nomem;
+ goto out_msi_disable;
+ }
+
+ /* Save away PCI config space for use following IOA reset */
+ rc = pci_save_state(pdev);
+
+ if (rc != PCIBIOS_SUCCESSFUL) {
+ dev_err(&pdev->dev, "Failed to save PCI config space\n");
+ rc = -EIO;
+ goto cleanup_nolog;
}
/*
@@ -8812,16 +9543,27 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
uproc = readl(ioa_cfg->regs.sense_uproc_interrupt_reg32);
if ((mask & IPR_PCII_HRRQ_UPDATED) == 0 || (uproc & IPR_UPROCI_RESET_ALERT))
ioa_cfg->needs_hard_reset = 1;
- if (interrupts & IPR_PCII_ERROR_INTERRUPTS)
+ if ((interrupts & IPR_PCII_ERROR_INTERRUPTS) || reset_devices)
ioa_cfg->needs_hard_reset = 1;
if (interrupts & IPR_PCII_IOA_UNIT_CHECKED)
ioa_cfg->ioa_unit_checked = 1;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
- rc = request_irq(pdev->irq, ipr_isr,
- ioa_cfg->msi_received ? 0 : IRQF_SHARED,
- IPR_NAME, ioa_cfg);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ if (pdev->msi_enabled || pdev->msix_enabled) {
+ name_msi_vectors(ioa_cfg);
+ rc = request_irq(pci_irq_vector(pdev, 0), ipr_isr, 0,
+ ioa_cfg->vectors_info[0].desc,
+ &ioa_cfg->hrrq[0]);
+ if (!rc)
+ rc = ipr_request_other_msi_irqs(ioa_cfg, pdev);
+ } else {
+ rc = request_irq(pdev->irq, ipr_isr,
+ IRQF_SHARED,
+ IPR_NAME, &ioa_cfg->hrrq[0]);
+ }
if (rc) {
dev_err(&pdev->dev, "Couldn't register IRQ %d! rc=%d\n",
pdev->irq, rc);
@@ -8832,52 +9574,45 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
(dev_id->device == PCI_DEVICE_ID_IBM_OBSIDIAN_E && !ioa_cfg->revid)) {
ioa_cfg->needs_warm_reset = 1;
ioa_cfg->reset = ipr_reset_slot_reset;
+
+ ioa_cfg->reset_work_q = alloc_ordered_workqueue("ipr_reset_%d",
+ WQ_MEM_RECLAIM, host->host_no);
+
+ if (!ioa_cfg->reset_work_q) {
+ dev_err(&pdev->dev, "Couldn't register reset workqueue\n");
+ rc = -ENOMEM;
+ goto out_free_irq;
+ }
} else
ioa_cfg->reset = ipr_reset_start_bist;
- spin_lock(&ipr_driver_lock);
+ spin_lock_irqsave(&ipr_driver_lock, driver_lock_flags);
list_add_tail(&ioa_cfg->queue, &ipr_ioa_head);
- spin_unlock(&ipr_driver_lock);
+ spin_unlock_irqrestore(&ipr_driver_lock, driver_lock_flags);
LEAVE;
out:
return rc;
+out_free_irq:
+ ipr_free_irqs(ioa_cfg);
cleanup_nolog:
ipr_free_mem(ioa_cfg);
+out_msi_disable:
+ ipr_wait_for_pci_err_recovery(ioa_cfg);
+ pci_free_irq_vectors(pdev);
cleanup_nomem:
iounmap(ipr_regs);
-out_msi_disable:
- pci_disable_msi(pdev);
+out_disable:
+ pci_disable_device(pdev);
out_release_regions:
pci_release_regions(pdev);
out_scsi_host_put:
scsi_host_put(host);
-out_disable:
- pci_disable_device(pdev);
goto out;
}
/**
- * ipr_scan_vsets - Scans for VSET devices
- * @ioa_cfg: ioa config struct
- *
- * Description: Since the VSET resources do not follow SAM in that we can have
- * sparse LUNs with no LUN 0, we have to scan for these ourselves.
- *
- * Return value:
- * none
- **/
-static void ipr_scan_vsets(struct ipr_ioa_cfg *ioa_cfg)
-{
- int target, lun;
-
- for (target = 0; target < IPR_MAX_NUM_TARGETS_PER_BUS; target++)
- for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++ )
- scsi_add_device(ioa_cfg->host, IPR_VSET_BUS, target, lun);
-}
-
-/**
* ipr_initiate_ioa_bringdown - Bring down an adapter
* @ioa_cfg: ioa config struct
* @shutdown_type: shutdown type
@@ -8916,25 +9651,36 @@ static void __ipr_remove(struct pci_dev *pdev)
{
unsigned long host_lock_flags = 0;
struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+ int i;
+ unsigned long driver_lock_flags;
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
}
+ for (i = 0; i < ioa_cfg->hrrq_num; i++) {
+ spin_lock(&ioa_cfg->hrrq[i]._lock);
+ ioa_cfg->hrrq[i].removing_ioa = 1;
+ spin_unlock(&ioa_cfg->hrrq[i]._lock);
+ }
+ wmb();
ipr_initiate_ioa_bringdown(ioa_cfg, IPR_SHUTDOWN_NORMAL);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
- flush_work_sync(&ioa_cfg->work_q);
+ flush_work(&ioa_cfg->work_q);
+ if (ioa_cfg->reset_work_q)
+ flush_workqueue(ioa_cfg->reset_work_q);
+ INIT_LIST_HEAD(&ioa_cfg->used_res_q);
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
- spin_lock(&ipr_driver_lock);
+ spin_lock_irqsave(&ipr_driver_lock, driver_lock_flags);
list_del(&ioa_cfg->queue);
- spin_unlock(&ipr_driver_lock);
+ spin_unlock_irqrestore(&ipr_driver_lock, driver_lock_flags);
if (ioa_cfg->sdt_state == ABORT_DUMP)
ioa_cfg->sdt_state = WAIT_FOR_DUMP;
@@ -8954,7 +9700,7 @@ static void __ipr_remove(struct pci_dev *pdev)
* Return value:
* none
**/
-static void __devexit ipr_remove(struct pci_dev *pdev)
+static void ipr_remove(struct pci_dev *pdev)
{
struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
@@ -8964,6 +9710,8 @@ static void __devexit ipr_remove(struct pci_dev *pdev)
&ipr_trace_attr);
ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_dump_attr);
+ sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj,
+ &ipr_ioa_async_err_log);
scsi_remove_host(ioa_cfg->host);
__ipr_remove(pdev);
@@ -8973,15 +9721,17 @@ static void __devexit ipr_remove(struct pci_dev *pdev)
/**
* ipr_probe - Adapter hot plug add entry point
+ * @pdev: pci device struct
+ * @dev_id: pci device ID
*
* Return value:
* 0 on success / non-zero on failure
**/
-static int __devinit ipr_probe(struct pci_dev *pdev,
- const struct pci_device_id *dev_id)
+static int ipr_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
{
struct ipr_ioa_cfg *ioa_cfg;
- int rc;
+ unsigned long flags;
+ int rc, i;
rc = ipr_probe_ioa(pdev, dev_id);
@@ -8989,24 +9739,32 @@ static int __devinit ipr_probe(struct pci_dev *pdev,
return rc;
ioa_cfg = pci_get_drvdata(pdev);
- rc = ipr_probe_ioa_part2(ioa_cfg);
+ ipr_probe_ioa_part2(ioa_cfg);
+
+ rc = scsi_add_host(ioa_cfg->host, &pdev->dev);
if (rc) {
__ipr_remove(pdev);
return rc;
}
- rc = scsi_add_host(ioa_cfg->host, &pdev->dev);
+ rc = ipr_create_trace_file(&ioa_cfg->host->shost_dev.kobj,
+ &ipr_trace_attr);
if (rc) {
+ scsi_remove_host(ioa_cfg->host);
__ipr_remove(pdev);
return rc;
}
- rc = ipr_create_trace_file(&ioa_cfg->host->shost_dev.kobj,
- &ipr_trace_attr);
+ rc = sysfs_create_bin_file(&ioa_cfg->host->shost_dev.kobj,
+ &ipr_ioa_async_err_log);
if (rc) {
+ ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj,
+ &ipr_dump_attr);
+ ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj,
+ &ipr_trace_attr);
scsi_remove_host(ioa_cfg->host);
__ipr_remove(pdev);
return rc;
@@ -9016,19 +9774,30 @@ static int __devinit ipr_probe(struct pci_dev *pdev,
&ipr_dump_attr);
if (rc) {
+ sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj,
+ &ipr_ioa_async_err_log);
ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_trace_attr);
scsi_remove_host(ioa_cfg->host);
__ipr_remove(pdev);
return rc;
}
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ ioa_cfg->scan_enabled = 1;
+ schedule_work(&ioa_cfg->work_q);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+
+ ioa_cfg->iopoll_weight = ioa_cfg->chip_cfg->iopoll_weight;
+
+ if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
+ for (i = 1; i < ioa_cfg->hrrq_num; i++) {
+ irq_poll_init(&ioa_cfg->hrrq[i].iopoll,
+ ioa_cfg->iopoll_weight, ipr_iopoll);
+ }
+ }
scsi_scan_host(ioa_cfg->host);
- ipr_scan_vsets(ioa_cfg);
- scsi_add_device(ioa_cfg->host, IPR_IOA_BUS, IPR_IOA_TARGET, IPR_IOA_LUN);
- ioa_cfg->allow_ml_add_del = 1;
- ioa_cfg->host->max_channel = IPR_VSET_BUS;
- schedule_work(&ioa_cfg->work_q);
+
return 0;
}
@@ -9046,20 +9815,35 @@ static void ipr_shutdown(struct pci_dev *pdev)
{
struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
unsigned long lock_flags = 0;
+ enum ipr_shutdown_type shutdown_type = IPR_SHUTDOWN_NORMAL;
+ int i;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) {
+ ioa_cfg->iopoll_weight = 0;
+ for (i = 1; i < ioa_cfg->hrrq_num; i++)
+ irq_poll_disable(&ioa_cfg->hrrq[i].iopoll);
+ }
+
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
}
- ipr_initiate_ioa_bringdown(ioa_cfg, IPR_SHUTDOWN_NORMAL);
+ if (ipr_fast_reboot && system_state == SYSTEM_RESTART && ioa_cfg->sis64)
+ shutdown_type = IPR_SHUTDOWN_QUIESCE;
+
+ ipr_initiate_ioa_bringdown(ioa_cfg, shutdown_type);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ if (ipr_fast_reboot && system_state == SYSTEM_RESTART && ioa_cfg->sis64) {
+ ipr_free_irqs(ioa_cfg);
+ pci_disable_device(ioa_cfg->pdev);
+ }
}
-static struct pci_device_id ipr_pci_table[] __devinitdata = {
+static const struct pci_device_id ipr_pci_table[] = {
{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_5702, 0, 0, 0 },
{ PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
@@ -9120,23 +9904,62 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = {
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B2, 0, 0, 0 },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C0, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C3, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C4, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B4, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B1, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C6, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
- PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575D, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C8, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57CE, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D5, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D6, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D7, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D8, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57D9, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57DA, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EB, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EC, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57ED, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EE, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57EF, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57F0, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2CCA, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2CD2, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2CCD, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_RATTLESNAKE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_580A, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_RATTLESNAKE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_580B, 0, 0, 0 },
{ }
};
MODULE_DEVICE_TABLE(pci, ipr_pci_table);
-static struct pci_error_handlers ipr_err_handler = {
+static const struct pci_error_handlers ipr_err_handler = {
.error_detected = ipr_pci_error_detected,
+ .mmio_enabled = ipr_pci_mmio_enabled,
.slot_reset = ipr_pci_slot_reset,
};
@@ -9144,26 +9967,28 @@ static struct pci_driver ipr_driver = {
.name = IPR_NAME,
.id_table = ipr_pci_table,
.probe = ipr_probe,
- .remove = __devexit_p(ipr_remove),
+ .remove = ipr_remove,
.shutdown = ipr_shutdown,
.err_handler = &ipr_err_handler,
};
/**
* ipr_halt_done - Shutdown prepare completion
+ * @ipr_cmd: ipr command struct
*
* Return value:
* none
**/
static void ipr_halt_done(struct ipr_cmnd *ipr_cmd)
{
- struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
-
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
* ipr_halt - Issue shutdown prepare to all adapters
+ * @nb: Notifier block
+ * @event: Notifier event
+ * @buf: Notifier data (unused)
*
* Return value:
* NOTIFY_OK on success / NOTIFY_DONE on failure
@@ -9172,16 +9997,17 @@ static int ipr_halt(struct notifier_block *nb, ulong event, void *buf)
{
struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
- unsigned long flags = 0;
+ unsigned long flags = 0, driver_lock_flags;
if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
return NOTIFY_DONE;
- spin_lock(&ipr_driver_lock);
+ spin_lock_irqsave(&ipr_driver_lock, driver_lock_flags);
list_for_each_entry(ioa_cfg, &ipr_ioa_head, queue) {
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- if (!ioa_cfg->allow_cmds) {
+ if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds ||
+ (ipr_fast_reboot && event == SYS_RESTART && ioa_cfg->sis64)) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
continue;
}
@@ -9195,7 +10021,7 @@ static int ipr_halt(struct notifier_block *nb, ulong event, void *buf)
ipr_do_req(ipr_cmd, ipr_halt_done, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
}
- spin_unlock(&ipr_driver_lock);
+ spin_unlock_irqrestore(&ipr_driver_lock, driver_lock_flags);
return NOTIFY_OK;
}
@@ -9212,11 +10038,19 @@ static struct notifier_block ipr_notifier = {
**/
static int __init ipr_init(void)
{
+ int rc;
+
ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n",
IPR_DRIVER_VERSION, IPR_DRIVER_DATE);
register_reboot_notifier(&ipr_notifier);
- return pci_register_driver(&ipr_driver);
+ rc = pci_register_driver(&ipr_driver);
+ if (rc) {
+ unregister_reboot_notifier(&ipr_notifier);
+ return rc;
+ }
+
+ return 0;
}
/**
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index f93f8637c5a1..fde7145835de 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ipr.h -- driver for IBM Power Linux RAID adapters
*
@@ -5,20 +6,6 @@
*
* Copyright (C) 2003, 2004 IBM Corporation
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* Alan Cox <alan@lxorguk.ukuu.org.uk> - Removed several careless u32/dma_addr_t errors
* that broke 64bit platforms.
*/
@@ -26,20 +13,20 @@
#ifndef _IPR_H
#define _IPR_H
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/types.h>
#include <linux/completion.h>
-#include <linux/libata.h>
#include <linux/list.h>
#include <linux/kref.h>
+#include <linux/irq_poll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
/*
* Literals
*/
-#define IPR_DRIVER_VERSION "2.5.2"
-#define IPR_DRIVER_DATE "(April 27, 2011)"
+#define IPR_DRIVER_VERSION "2.6.4"
+#define IPR_DRIVER_DATE "(March 14, 2017)"
/*
* IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
@@ -47,18 +34,18 @@
* This can be adjusted at runtime through sysfs device attributes.
*/
#define IPR_MAX_CMD_PER_LUN 6
-#define IPR_MAX_CMD_PER_ATA_LUN 1
/*
* IPR_NUM_BASE_CMD_BLKS: This defines the maximum number of
* ops the mid-layer can send to the adapter.
*/
-#define IPR_NUM_BASE_CMD_BLKS 100
+#define IPR_NUM_BASE_CMD_BLKS (ioa_cfg->max_cmds)
#define PCI_DEVICE_ID_IBM_OBSIDIAN_E 0x0339
#define PCI_DEVICE_ID_IBM_CROC_FPGA_E2 0x033D
-#define PCI_DEVICE_ID_IBM_CROC_ASIC_E2 0x034A
+#define PCI_DEVICE_ID_IBM_CROCODILE 0x034A
+#define PCI_DEVICE_ID_IBM_RATTLESNAKE 0x04DA
#define IPR_SUBS_DEV_ID_2780 0x0264
#define IPR_SUBS_DEV_ID_5702 0x0266
@@ -82,6 +69,8 @@
#define IPR_SUBS_DEV_ID_57B4 0x033B
#define IPR_SUBS_DEV_ID_57B2 0x035F
+#define IPR_SUBS_DEV_ID_57C0 0x0352
+#define IPR_SUBS_DEV_ID_57C3 0x0353
#define IPR_SUBS_DEV_ID_57C4 0x0354
#define IPR_SUBS_DEV_ID_57C6 0x0357
#define IPR_SUBS_DEV_ID_57CC 0x035C
@@ -91,8 +80,25 @@
#define IPR_SUBS_DEV_ID_57B1 0x0355
#define IPR_SUBS_DEV_ID_574D 0x0356
-#define IPR_SUBS_DEV_ID_575D 0x035D
-
+#define IPR_SUBS_DEV_ID_57C8 0x035D
+
+#define IPR_SUBS_DEV_ID_57D5 0x03FB
+#define IPR_SUBS_DEV_ID_57D6 0x03FC
+#define IPR_SUBS_DEV_ID_57D7 0x03FF
+#define IPR_SUBS_DEV_ID_57D8 0x03FE
+#define IPR_SUBS_DEV_ID_57D9 0x046D
+#define IPR_SUBS_DEV_ID_57DA 0x04CA
+#define IPR_SUBS_DEV_ID_57EB 0x0474
+#define IPR_SUBS_DEV_ID_57EC 0x0475
+#define IPR_SUBS_DEV_ID_57ED 0x0499
+#define IPR_SUBS_DEV_ID_57EE 0x049A
+#define IPR_SUBS_DEV_ID_57EF 0x049B
+#define IPR_SUBS_DEV_ID_57F0 0x049C
+#define IPR_SUBS_DEV_ID_2CCA 0x04C7
+#define IPR_SUBS_DEV_ID_2CD2 0x04C8
+#define IPR_SUBS_DEV_ID_2CCD 0x04C9
+#define IPR_SUBS_DEV_ID_580A 0x04FC
+#define IPR_SUBS_DEV_ID_580B 0x04FB
#define IPR_NAME "ipr"
/*
@@ -112,6 +118,7 @@
#define IPR_IOASC_HW_DEV_BUS_STATUS 0x04448500
#define IPR_IOASC_IOASC_MASK 0xFFFFFF00
#define IPR_IOASC_SCSI_STATUS_MASK 0x000000FF
+#define IPR_IOASC_HW_CMD_FAILED 0x046E0000
#define IPR_IOASC_IR_INVALID_REQ_TYPE_OR_PKT 0x05240000
#define IPR_IOASC_IR_RESOURCE_HANDLE 0x05250000
#define IPR_IOASC_IR_NO_CMDS_TO_2ND_IOA 0x05258100
@@ -119,6 +126,7 @@
#define IPR_IOASC_BUS_WAS_RESET 0x06290000
#define IPR_IOASC_BUS_WAS_RESET_BY_OTHER 0x06298000
#define IPR_IOASC_ABORTED_CMD_TERM_BY_HOST 0x0B5A0000
+#define IPR_IOASC_IR_NON_OPTIMIZED 0x05258200
#define IPR_FIRST_DRIVER_IOASC 0x10000000
#define IPR_IOASC_IOA_WAS_RESET 0x10000001
@@ -131,20 +139,20 @@
#define IPR_DEFAULT_MAX_ERROR_DUMP 984
#define IPR_NUM_LOG_HCAMS 2
#define IPR_NUM_CFG_CHG_HCAMS 2
+#define IPR_NUM_HCAM_QUEUE 12
#define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS)
+#define IPR_MAX_HCAMS (IPR_NUM_HCAMS + IPR_NUM_HCAM_QUEUE)
#define IPR_MAX_SIS64_TARGETS_PER_BUS 1024
#define IPR_MAX_SIS64_LUNS_PER_TARGET 0xffffffff
#define IPR_MAX_NUM_TARGETS_PER_BUS 256
#define IPR_MAX_NUM_LUNS_PER_TARGET 256
-#define IPR_MAX_NUM_VSET_LUNS_PER_TARGET 8
#define IPR_VSET_BUS 0xff
#define IPR_IOA_BUS 0xff
#define IPR_IOA_TARGET 0xff
#define IPR_IOA_LUN 0xff
#define IPR_MAX_NUM_BUSES 16
-#define IPR_MAX_BUS_TO_SCAN IPR_MAX_NUM_BUSES
#define IPR_NUM_RESET_RELOAD_RETRIES 3
@@ -152,7 +160,7 @@
#define IPR_NUM_INTERNAL_CMD_BLKS (IPR_NUM_HCAMS + \
((IPR_NUM_RESET_RELOAD_RETRIES + 1) * 2) + 4)
-#define IPR_MAX_COMMANDS IPR_NUM_BASE_CMD_BLKS
+#define IPR_MAX_COMMANDS 100
#define IPR_NUM_CMD_BLKS (IPR_NUM_BASE_CMD_BLKS + \
IPR_NUM_INTERNAL_CMD_BLKS)
@@ -179,13 +187,14 @@
/*
* Adapter Commands
*/
+#define IPR_CANCEL_REQUEST 0xC0
+#define IPR_CANCEL_64BIT_IOARCB 0x01
#define IPR_QUERY_RSRC_STATE 0xC2
#define IPR_RESET_DEVICE 0xC3
#define IPR_RESET_TYPE_SELECT 0x80
#define IPR_LUN_RESET 0x40
#define IPR_TARGET_RESET 0x20
#define IPR_BUS_RESET 0x10
-#define IPR_ATA_PHY_RESET 0x80
#define IPR_ID_HOST_RR_Q 0xC4
#define IPR_QUERY_IOA_CONFIG 0xC5
#define IPR_CANCEL_ALL_REQUESTS 0xCE
@@ -196,6 +205,10 @@
#define IPR_SET_ALL_SUPPORTED_DEVICES 0x80
#define IPR_IOA_SHUTDOWN 0xF7
#define IPR_WR_BUF_DOWNLOAD_AND_SAVE 0x05
+#define IPR_IOA_SERVICE_ACTION 0xD2
+
+/* IOA Service Actions */
+#define IPR_IOA_SA_CHANGE_CACHE_PARAMS 0x14
/*
* Timeouts
@@ -205,10 +218,11 @@
#define IPR_ABBREV_SHUTDOWN_TIMEOUT (10 * HZ)
#define IPR_DUAL_IOA_ABBR_SHUTDOWN_TO (2 * 60 * HZ)
#define IPR_DEVICE_RESET_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
+#define IPR_CANCEL_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
#define IPR_CANCEL_ALL_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
#define IPR_ABORT_TASK_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
#define IPR_INTERNAL_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
-#define IPR_WRITE_BUFFER_TIMEOUT (10 * 60 * HZ)
+#define IPR_WRITE_BUFFER_TIMEOUT (30 * 60 * HZ)
#define IPR_SET_SUP_DEVICE_TIMEOUT (2 * 60 * HZ)
#define IPR_REQUEST_SENSE_TIMEOUT (10 * HZ)
#define IPR_OPERATIONAL_TIMEOUT (5 * 60)
@@ -216,6 +230,7 @@
#define IPR_WAIT_FOR_RESET_TIMEOUT (2 * HZ)
#define IPR_CHECK_FOR_RESET_TIMEOUT (HZ / 10)
#define IPR_WAIT_FOR_BIST_TIMEOUT (2 * HZ)
+#define IPR_PCI_ERROR_RECOVERY_TIMEOUT (120 * HZ)
#define IPR_PCI_RESET_TIMEOUT (HZ / 2)
#define IPR_SIS32_DUMP_TIMEOUT (15 * HZ)
#define IPR_SIS64_DUMP_TIMEOUT (40 * HZ)
@@ -250,13 +265,16 @@
#define IPR_RUNTIME_RESET 0x40000000
#define IPR_IPL_INIT_MIN_STAGE_TIME 5
-#define IPR_IPL_INIT_DEFAULT_STAGE_TIME 15
+#define IPR_IPL_INIT_DEFAULT_STAGE_TIME 30
#define IPR_IPL_INIT_STAGE_UNKNOWN 0x0
#define IPR_IPL_INIT_STAGE_TRANSOP 0xB0000000
#define IPR_IPL_INIT_STAGE_MASK 0xff000000
#define IPR_IPL_INIT_STAGE_TIME_MASK 0x0000ffff
#define IPR_PCII_IPL_STAGE_CHANGE (0x80000000 >> 0)
+#define IPR_PCII_MAILBOX_STABLE (0x80000000 >> 4)
+#define IPR_WAIT_FOR_MAILBOX (2 * HZ)
+
#define IPR_PCII_IOA_TRANS_TO_OPER (0x80000000 >> 0)
#define IPR_PCII_IOARCB_XFER_FAILED (0x80000000 >> 3)
#define IPR_PCII_IOA_UNIT_CHECKED (0x80000000 >> 4)
@@ -287,7 +305,7 @@ IPR_PCII_NO_HOST_RRQ | IPR_PCII_IOARRIN_LOST | IPR_PCII_MMIO_ERROR)
* Dump literals
*/
#define IPR_FMT2_MAX_IOA_DUMP_SIZE (4 * 1024 * 1024)
-#define IPR_FMT3_MAX_IOA_DUMP_SIZE (32 * 1024 * 1024)
+#define IPR_FMT3_MAX_IOA_DUMP_SIZE (80 * 1024 * 1024)
#define IPR_FMT2_NUM_SDT_ENTRIES 511
#define IPR_FMT3_NUM_SDT_ENTRIES 0xFFF
#define IPR_FMT2_MAX_NUM_DUMP_PAGES ((IPR_FMT2_MAX_IOA_DUMP_SIZE / PAGE_SIZE) + 1)
@@ -297,6 +315,9 @@ IPR_PCII_NO_HOST_RRQ | IPR_PCII_IOARRIN_LOST | IPR_PCII_MMIO_ERROR)
* Misc literals
*/
#define IPR_NUM_IOADL_ENTRIES IPR_MAX_SGLIST
+#define IPR_MAX_MSIX_VECTORS 0x10
+#define IPR_MAX_HRRQ_NUM 0x10
+#define IPR_INIT_HRRQ 0x0
/*
* Adapter interface types
@@ -403,7 +424,8 @@ struct ipr_config_table_entry64 {
__be64 dev_id;
__be64 lun;
__be64 lun_wwn[2];
-#define IPR_MAX_RES_PATH_LENGTH 24
+#define IPR_MAX_RES_PATH_LENGTH 48
+#define IPR_RES_PATH_BYTES 8
__be64 res_path;
struct ipr_std_inq_data std_inq_data;
u8 reserved2[4];
@@ -427,12 +449,12 @@ struct ipr_config_table_hdr64 {
struct ipr_config_table {
struct ipr_config_table_hdr hdr;
- struct ipr_config_table_entry dev[0];
+ struct ipr_config_table_entry dev[];
}__attribute__((packed, aligned (4)));
struct ipr_config_table64 {
struct ipr_config_table_hdr64 hdr64;
- struct ipr_config_table_entry64 dev[0];
+ struct ipr_config_table_entry64 dev[];
}__attribute__((packed, aligned (8)));
struct ipr_config_table_entry_wrapper {
@@ -458,14 +480,45 @@ struct ipr_supported_device {
u8 reserved2[16];
}__attribute__((packed, aligned (4)));
+struct ipr_hrr_queue {
+ struct ipr_ioa_cfg *ioa_cfg;
+ __be32 *host_rrq;
+ dma_addr_t host_rrq_dma;
+#define IPR_HRRQ_REQ_RESP_HANDLE_MASK 0xfffffffc
+#define IPR_HRRQ_RESP_BIT_SET 0x00000002
+#define IPR_HRRQ_TOGGLE_BIT 0x00000001
+#define IPR_HRRQ_REQ_RESP_HANDLE_SHIFT 2
+#define IPR_ID_HRRQ_SELE_ENABLE 0x02
+ volatile __be32 *hrrq_start;
+ volatile __be32 *hrrq_end;
+ volatile __be32 *hrrq_curr;
+
+ struct list_head hrrq_free_q;
+ struct list_head hrrq_pending_q;
+ spinlock_t _lock;
+ spinlock_t *lock;
+
+ volatile u32 toggle_bit;
+ u32 size;
+ u32 min_cmd_id;
+ u32 max_cmd_id;
+ u8 allow_interrupts:1;
+ u8 ioa_is_dead:1;
+ u8 allow_cmds:1;
+ u8 removing_ioa:1;
+
+ struct irq_poll iopoll;
+};
+
/* Command packet structure */
struct ipr_cmd_pkt {
- __be16 reserved; /* Reserved by IOA */
+ u8 reserved; /* Reserved by IOA */
+ u8 hrrq_id;
u8 request_type;
#define IPR_RQTYPE_SCSICDB 0x00
#define IPR_RQTYPE_IOACMD 0x01
#define IPR_RQTYPE_HCAM 0x02
-#define IPR_RQTYPE_ATA_PASSTHRU 0x04
+#define IPR_RQTYPE_PIPE 0x05
u8 reserved2;
@@ -489,30 +542,6 @@ struct ipr_cmd_pkt {
__be16 timeout;
}__attribute__ ((packed, aligned(4)));
-struct ipr_ioarcb_ata_regs { /* 22 bytes */
- u8 flags;
-#define IPR_ATA_FLAG_PACKET_CMD 0x80
-#define IPR_ATA_FLAG_XFER_TYPE_DMA 0x40
-#define IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION 0x20
- u8 reserved[3];
-
- __be16 data;
- u8 feature;
- u8 nsect;
- u8 lbal;
- u8 lbam;
- u8 lbah;
- u8 device;
- u8 command;
- u8 reserved2[3];
- u8 hob_feature;
- u8 hob_nsect;
- u8 hob_lbal;
- u8 hob_lbam;
- u8 hob_lbah;
- u8 ctl;
-}__attribute__ ((packed, aligned(4)));
-
struct ipr_ioadl_desc {
__be32 flags_and_data_len;
#define IPR_IOADL_FLAGS_MASK 0xff000000
@@ -534,15 +563,8 @@ struct ipr_ioadl64_desc {
__be64 address;
}__attribute__((packed, aligned (16)));
-struct ipr_ata64_ioadl {
- struct ipr_ioarcb_ata_regs regs;
- u16 reserved[5];
- struct ipr_ioadl64_desc ioadl64[IPR_NUM_IOADL_ENTRIES];
-}__attribute__((packed, aligned (16)));
-
struct ipr_ioarcb_add_data {
union {
- struct ipr_ioarcb_ata_regs regs;
struct ipr_ioadl_desc ioadl[5];
__be32 add_cmd_parms[10];
} u;
@@ -608,21 +630,6 @@ struct ipr_ioasa_gpdd {
__be32 ioa_data[2];
}__attribute__((packed, aligned (4)));
-struct ipr_ioasa_gata {
- u8 error;
- u8 nsect; /* Interrupt reason */
- u8 lbal;
- u8 lbam;
- u8 lbah;
- u8 device;
- u8 status;
- u8 alt_status; /* ATA CTL */
- u8 hob_nsect;
- u8 hob_lbal;
- u8 hob_lbam;
- u8 hob_lbah;
-}__attribute__((packed, aligned (4)));
-
struct ipr_auto_sense {
__be16 auto_sense_len;
__be16 ioa_data_len;
@@ -656,7 +663,6 @@ struct ipr_ioasa_hdr {
__be32 ioasc_specific; /* status code specific field */
#define IPR_ADDITIONAL_STATUS_FMT 0x80000000
#define IPR_AUTOSENSE_VALID 0x40000000
-#define IPR_ATA_DEVICE_WAS_RESET 0x20000000
#define IPR_IOASC_SPECIFIC_MASK 0x00ffffff
#define IPR_FIELD_POINTER_VALID (0x80000000 >> 8)
#define IPR_FIELD_POINTER_MASK 0x0000ffff
@@ -670,7 +676,6 @@ struct ipr_ioasa {
struct ipr_ioasa_vset vset;
struct ipr_ioasa_af_dasd dasd;
struct ipr_ioasa_gpdd gpdd;
- struct ipr_ioasa_gata gata;
} u;
struct ipr_auto_sense auto_sense;
@@ -684,7 +689,6 @@ struct ipr_ioasa64 {
struct ipr_ioasa_vset vset;
struct ipr_ioasa_af_dasd dasd;
struct ipr_ioasa_gpdd gpdd;
- struct ipr_ioasa_gata gata;
} u;
struct ipr_auto_sense auto_sense;
@@ -736,7 +740,7 @@ struct ipr_mode_page28 {
struct ipr_mode_page_hdr hdr;
u8 num_entries;
u8 entry_length;
- struct ipr_dev_bus_entry bus[0];
+ struct ipr_dev_bus_entry bus[];
}__attribute__((packed));
struct ipr_mode_page24 {
@@ -789,6 +793,16 @@ struct ipr_inquiry_page0 {
u8 page[IPR_INQUIRY_PAGE0_ENTRIES];
}__attribute__((packed));
+struct ipr_inquiry_pageC4 {
+ u8 peri_qual_dev_type;
+ u8 page_code;
+ u8 reserved1;
+ u8 len;
+ u8 cache_cap[4];
+#define IPR_CAP_SYNC_CACHE 0x08
+ u8 reserved2[20];
+} __packed;
+
struct ipr_hostrcb_device_data_entry {
struct ipr_vpd vpd;
struct ipr_res_addr dev_res_addr;
@@ -849,6 +863,18 @@ struct ipr_hostrcb_type_01_error {
__be32 ioa_data[236];
}__attribute__((packed, aligned (4)));
+struct ipr_hostrcb_type_21_error {
+ __be32 wwn[4];
+ u8 res_path[8];
+ u8 primary_problem_desc[32];
+ u8 second_problem_desc[32];
+ __be32 sense_data[8];
+ __be32 cdb[4];
+ __be32 residual_trans_length;
+ __be32 length_of_error;
+ __be32 ioa_data[236];
+}__attribute__((packed, aligned (4)));
+
struct ipr_hostrcb_type_02_error {
struct ipr_vpd ioa_vpd;
struct ipr_vpd cfc_vpd;
@@ -936,13 +962,13 @@ struct ipr_hostrcb_type_24_error {
struct ipr_hostrcb_type_07_error {
u8 failure_reason[64];
struct ipr_vpd vpd;
- u32 data[222];
+ __be32 data[222];
}__attribute__((packed, aligned (4)));
struct ipr_hostrcb_type_17_error {
u8 failure_reason[64];
struct ipr_ext_vpd vpd;
- u32 data[476];
+ __be32 data[476];
}__attribute__((packed, aligned (4)));
struct ipr_hostrcb_config_element {
@@ -1004,7 +1030,7 @@ struct ipr_hostrcb_fabric_desc {
#define IPR_PATH_FAILED 0x03
__be16 num_entries;
- struct ipr_hostrcb_config_element elem[1];
+ struct ipr_hostrcb_config_element elem[];
}__attribute__((packed, aligned (4)));
struct ipr_hostrcb64_fabric_desc {
@@ -1018,9 +1044,13 @@ struct ipr_hostrcb64_fabric_desc {
u8 res_path[8];
u8 reserved3[6];
__be16 num_entries;
- struct ipr_hostrcb64_config_element elem[1];
+ struct ipr_hostrcb64_config_element elem[];
}__attribute__((packed, aligned (8)));
+#define for_each_hrrq(hrrq, ioa_cfg) \
+ for (hrrq = (ioa_cfg)->hrrq; \
+ hrrq < ((ioa_cfg)->hrrq + (ioa_cfg)->hrrq_num); hrrq++)
+
#define for_each_fabric_cfg(fabric, cfg) \
for (cfg = (fabric)->elem; \
cfg < ((fabric)->elem + be16_to_cpu((fabric)->num_entries)); \
@@ -1040,6 +1070,11 @@ struct ipr_hostrcb_type_30_error {
struct ipr_hostrcb64_fabric_desc desc[1];
}__attribute__((packed, aligned (4)));
+struct ipr_hostrcb_type_41_error {
+ u8 failure_reason[64];
+ __be32 data[200];
+}__attribute__((packed, aligned (4)));
+
struct ipr_hostrcb_error {
__be32 fd_ioasc;
struct ipr_res_addr fd_res_addr;
@@ -1074,9 +1109,11 @@ struct ipr_hostrcb64_error {
struct ipr_hostrcb_type_ff_error type_ff_error;
struct ipr_hostrcb_type_12_error type_12_error;
struct ipr_hostrcb_type_17_error type_17_error;
+ struct ipr_hostrcb_type_21_error type_21_error;
struct ipr_hostrcb_type_23_error type_23_error;
struct ipr_hostrcb_type_24_error type_24_error;
struct ipr_hostrcb_type_30_error type_30_error;
+ struct ipr_hostrcb_type_41_error type_41_error;
} u;
}__attribute__((packed, aligned (8)));
@@ -1117,10 +1154,12 @@ struct ipr_hcam {
#define IPR_HOST_RCB_OVERLAY_ID_16 0x16
#define IPR_HOST_RCB_OVERLAY_ID_17 0x17
#define IPR_HOST_RCB_OVERLAY_ID_20 0x20
+#define IPR_HOST_RCB_OVERLAY_ID_21 0x21
#define IPR_HOST_RCB_OVERLAY_ID_23 0x23
#define IPR_HOST_RCB_OVERLAY_ID_24 0x24
#define IPR_HOST_RCB_OVERLAY_ID_26 0x26
#define IPR_HOST_RCB_OVERLAY_ID_30 0x30
+#define IPR_HOST_RCB_OVERLAY_ID_41 0x41
#define IPR_HOST_RCB_OVERLAY_ID_DEFAULT 0xFF
u8 reserved1[3];
@@ -1187,19 +1226,14 @@ struct ipr_bus_attributes {
u32 max_xfer_rate;
};
-struct ipr_sata_port {
- struct ipr_ioa_cfg *ioa_cfg;
- struct ata_port *ap;
- struct ipr_resource_entry *res;
- struct ipr_ioasa_gata ioasa;
-};
-
struct ipr_resource_entry {
u8 needs_sync_complete:1;
u8 in_erp:1;
u8 add_to_ml:1;
u8 del_from_ml:1;
u8 resetting_device:1;
+ u8 reset_occurred:1;
+ u8 raw_mode:1;
u32 bus; /* AKA channel */
u32 target; /* AKA id */
@@ -1207,29 +1241,28 @@ struct ipr_resource_entry {
#define IPR_ARRAY_VIRTUAL_BUS 0x1
#define IPR_VSET_VIRTUAL_BUS 0x2
#define IPR_IOAFP_VIRTUAL_BUS 0x3
+#define IPR_MAX_SIS64_BUSES 0x4
#define IPR_GET_RES_PHYS_LOC(res) \
(((res)->bus << 24) | ((res)->target << 8) | (res)->lun)
u8 ata_class;
-
- u8 flags;
- __be16 res_flags;
-
u8 type;
+ u16 flags;
+ u16 res_flags;
+
u8 qmodel;
struct ipr_std_inq_data std_inq_data;
__be32 res_handle;
__be64 dev_id;
- __be64 lun_wwn;
+ u64 lun_wwn;
struct scsi_lun dev_lun;
u8 res_path[8];
struct ipr_ioa_cfg *ioa_cfg;
struct scsi_device *sdev;
- struct ipr_sata_port *sata_port;
struct list_head queue;
}; /* struct ipr_resource_entry */
@@ -1243,6 +1276,7 @@ struct ipr_misc_cbs {
struct ipr_inquiry_page0 page0_data;
struct ipr_inquiry_page3 page3_data;
struct ipr_inquiry_cap cap;
+ struct ipr_inquiry_pageC4 pageC4_data;
struct ipr_mode_pages mode_pages;
struct ipr_supported_device supp_dev;
};
@@ -1304,16 +1338,17 @@ struct ipr_interrupts {
struct ipr_chip_cfg_t {
u32 mailbox;
+ u16 max_cmds;
u8 cache_line_size;
+ u8 clear_isr;
+ u32 iopoll_weight;
struct ipr_interrupt_offsets regs;
};
struct ipr_chip_t {
u16 vendor;
u16 device;
- u16 intr_type;
-#define IPR_USE_LSI 0x00
-#define IPR_USE_MSI 0x01
+ bool has_msi;
u16 sis_type;
#define IPR_SIS32 0x00
#define IPR_SIS64 0x01
@@ -1327,7 +1362,8 @@ enum ipr_shutdown_type {
IPR_SHUTDOWN_NORMAL = 0x00,
IPR_SHUTDOWN_PREPARE_FOR_NORMAL = 0x40,
IPR_SHUTDOWN_ABBREV = 0x80,
- IPR_SHUTDOWN_NONE = 0x100
+ IPR_SHUTDOWN_NONE = 0x100,
+ IPR_SHUTDOWN_QUIESCE = 0x101,
};
struct ipr_trace_entry {
@@ -1353,13 +1389,14 @@ struct ipr_sglist {
u32 num_sg;
u32 num_dma_sg;
u32 buffer_len;
- struct scatterlist scatterlist[1];
+ struct scatterlist *scatterlist;
};
enum ipr_sdt_state {
INACTIVE,
WAIT_FOR_DUMP,
GET_DUMP,
+ READ_DUMP,
ABORT_DUMP,
DUMP_OBTAINED
};
@@ -1371,56 +1408,48 @@ struct ipr_ioa_cfg {
struct list_head queue;
- u8 allow_interrupts:1;
u8 in_reset_reload:1;
u8 in_ioa_bringdown:1;
u8 ioa_unit_checked:1;
- u8 ioa_is_dead:1;
u8 dump_taken:1;
- u8 allow_cmds:1;
- u8 allow_ml_add_del:1;
+ u8 scan_enabled:1;
+ u8 scan_done:1;
u8 needs_hard_reset:1;
u8 dual_raid:1;
u8 needs_warm_reset:1;
u8 msi_received:1;
u8 sis64:1;
+ u8 dump_timeout:1;
+ u8 cfg_locked:1;
+ u8 clear_isr:1;
+ u8 probe_done:1;
+ u8 scsi_unblock:1;
+ u8 scsi_blocked:1;
u8 revid;
/*
* Bitmaps for SIS64 generated target values
*/
- unsigned long *target_ids;
- unsigned long *array_ids;
- unsigned long *vset_ids;
+ unsigned long target_ids[BITS_TO_LONGS(IPR_MAX_SIS64_DEVS)];
+ unsigned long array_ids[BITS_TO_LONGS(IPR_MAX_SIS64_DEVS)];
+ unsigned long vset_ids[BITS_TO_LONGS(IPR_MAX_SIS64_DEVS)];
u16 type; /* CCIN of the card */
u8 log_level;
#define IPR_MAX_LOG_LEVEL 4
#define IPR_DEFAULT_LOG_LEVEL 2
+#define IPR_DEBUG_LOG_LEVEL 3
#define IPR_NUM_TRACE_INDEX_BITS 8
#define IPR_NUM_TRACE_ENTRIES (1 << IPR_NUM_TRACE_INDEX_BITS)
+#define IPR_TRACE_INDEX_MASK (IPR_NUM_TRACE_ENTRIES - 1)
#define IPR_TRACE_SIZE (sizeof(struct ipr_trace_entry) * IPR_NUM_TRACE_ENTRIES)
char trace_start[8];
#define IPR_TRACE_START_LABEL "trace"
struct ipr_trace_entry *trace;
- u32 trace_index:IPR_NUM_TRACE_INDEX_BITS;
-
- /*
- * Queue for free command blocks
- */
- char ipr_free_label[8];
-#define IPR_FREEQ_LABEL "free-q"
- struct list_head free_q;
-
- /*
- * Queue for command blocks outstanding to the adapter
- */
- char ipr_pending_label[8];
-#define IPR_PENDQ_LABEL "pend-q"
- struct list_head pending_q;
+ atomic_t trace_index;
char cfg_table_start[8];
#define IPR_CFG_TBL_START "cfg"
@@ -1440,21 +1469,16 @@ struct ipr_ioa_cfg {
char ipr_hcam_label[8];
#define IPR_HCAM_LABEL "hcams"
- struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS];
- dma_addr_t hostrcb_dma[IPR_NUM_HCAMS];
+ struct ipr_hostrcb *hostrcb[IPR_MAX_HCAMS];
+ dma_addr_t hostrcb_dma[IPR_MAX_HCAMS];
struct list_head hostrcb_free_q;
struct list_head hostrcb_pending_q;
+ struct list_head hostrcb_report_q;
- __be32 *host_rrq;
- dma_addr_t host_rrq_dma;
-#define IPR_HRRQ_REQ_RESP_HANDLE_MASK 0xfffffffc
-#define IPR_HRRQ_RESP_BIT_SET 0x00000002
-#define IPR_HRRQ_TOGGLE_BIT 0x00000001
-#define IPR_HRRQ_REQ_RESP_HANDLE_SHIFT 2
- volatile __be32 *hrrq_start;
- volatile __be32 *hrrq_end;
- volatile __be32 *hrrq_curr;
- volatile u32 toggle_bit;
+ struct ipr_hrr_queue hrrq[IPR_MAX_HRRQ_NUM];
+ u32 hrrq_num;
+ atomic_t hrrq_index;
+ u16 identify_hrrq_index;
struct ipr_bus_attributes bus_attr[IPR_MAX_NUM_BUSES];
@@ -1479,9 +1503,12 @@ struct ipr_ioa_cfg {
u8 saved_mode_page_len;
struct work_struct work_q;
+ struct work_struct scsi_add_work_q;
+ struct workqueue_struct *reset_work_q;
wait_queue_head_t reset_wait_q;
wait_queue_head_t msi_wait_q;
+ wait_queue_head_t eeh_wait_q;
struct ipr_dump *dump;
enum ipr_sdt_state sdt_state;
@@ -1489,16 +1516,25 @@ struct ipr_ioa_cfg {
struct ipr_misc_cbs *vpd_cbs;
dma_addr_t vpd_cbs_dma;
- struct pci_pool *ipr_cmd_pool;
+ struct dma_pool *ipr_cmd_pool;
struct ipr_cmnd *reset_cmd;
int (*reset) (struct ipr_cmnd *);
- struct ata_host ata_host;
char ipr_cmd_label[8];
#define IPR_CMD_LABEL "ipr_cmd"
- struct ipr_cmnd *ipr_cmnd_list[IPR_NUM_CMD_BLKS];
- dma_addr_t ipr_cmnd_list_dma[IPR_NUM_CMD_BLKS];
+ u32 max_cmds;
+ struct ipr_cmnd **ipr_cmnd_list;
+ dma_addr_t *ipr_cmnd_list_dma;
+
+ unsigned int nvectors;
+
+ struct {
+ char desc[22];
+ } vectors_info[IPR_MAX_MSIX_VECTORS];
+
+ u32 iopoll_weight;
+
}; /* struct ipr_ioa_cfg */
struct ipr_cmnd {
@@ -1506,7 +1542,6 @@ struct ipr_cmnd {
union {
struct ipr_ioadl_desc ioadl[IPR_NUM_IOADL_ENTRIES];
struct ipr_ioadl64_desc ioadl64[IPR_NUM_IOADL_ENTRIES];
- struct ipr_ata64_ioadl ata_ioadl;
} i;
union {
struct ipr_ioasa ioasa;
@@ -1514,9 +1549,10 @@ struct ipr_cmnd {
} s;
struct list_head queue;
struct scsi_cmnd *scsi_cmd;
- struct ata_queued_cmd *qc;
struct completion completion;
struct timer_list timer;
+ struct work_struct work;
+ void (*fast_done) (struct ipr_cmnd *);
void (*done) (struct ipr_cmnd *);
int (*job_step) (struct ipr_cmnd *);
int (*job_step_failed) (struct ipr_cmnd *);
@@ -1535,6 +1571,8 @@ struct ipr_cmnd {
struct scsi_device *sdev;
} u;
+ struct completion *eh_comp;
+ struct ipr_hrr_queue *hrrq;
struct ipr_ioa_cfg *ioa_cfg;
};
@@ -1583,7 +1621,7 @@ struct ipr_dump_entry_header {
struct ipr_dump_location_entry {
struct ipr_dump_entry_header hdr;
u8 location[20];
-}__attribute__((packed));
+}__attribute__((packed, aligned (4)));
struct ipr_dump_trace_entry {
struct ipr_dump_entry_header hdr;
@@ -1607,7 +1645,7 @@ struct ipr_driver_dump {
struct ipr_dump_location_entry location_entry;
struct ipr_dump_ioa_type_entry ioa_type_entry;
struct ipr_dump_trace_entry trace_entry;
-}__attribute__((packed));
+}__attribute__((packed, aligned (4)));
struct ipr_ioa_dump {
struct ipr_dump_entry_header hdr;
@@ -1708,7 +1746,8 @@ struct ipr_ucode_image_header {
if (ipr_is_device(hostrcb)) { \
if ((hostrcb)->ioa_cfg->sis64) { \
printk(KERN_ERR IPR_NAME ": %s: " fmt, \
- ipr_format_res_path(hostrcb->hcam.u.error64.fd_res_path, \
+ ipr_format_res_path(hostrcb->ioa_cfg, \
+ hostrcb->hcam.u.error64.fd_res_path, \
hostrcb->rp_buffer, \
sizeof(hostrcb->rp_buffer)), \
__VA_ARGS__); \
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index 218f71a8726e..3393a288fd23 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -180,9 +180,13 @@
#include <linux/types.h>
#include <linux/dma-mapping.h>
-#include <scsi/sg.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/sg.h>
#include "ips.h"
@@ -206,13 +210,9 @@ module_param(ips, charp, 0);
#define IPS_VERSION_HIGH IPS_VER_MAJOR_STRING "." IPS_VER_MINOR_STRING
#define IPS_VERSION_LOW "." IPS_VER_BUILD_STRING " "
-#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
-#warning "This driver has only been tested on the x86/ia64/x86_64 platforms"
-#endif
-
#define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \
- PCI_DMA_BIDIRECTIONAL : \
+ DMA_BIDIRECTIONAL : \
scb->scsi_cmd->sc_data_direction)
#ifdef IPS_DEBUG
@@ -228,8 +228,6 @@ module_param(ips, charp, 0);
/*
* Function prototypes
*/
-static int ips_detect(struct scsi_host_template *);
-static int ips_release(struct Scsi_Host *);
static int ips_eh_abort(struct scsi_cmnd *);
static int ips_eh_reset(struct scsi_cmnd *);
static int ips_queue(struct Scsi_Host *, struct scsi_cmnd *);
@@ -297,7 +295,7 @@ static void ips_freescb(ips_ha_t *, ips_scb_t *);
static void ips_setup_funclist(ips_ha_t *);
static void ips_statinit(ips_ha_t *);
static void ips_statinit_memio(ips_ha_t *);
-static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
+static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time64_t);
static void ips_ffdc_reset(ips_ha_t *, int);
static void ips_ffdc_time(ips_ha_t *);
static uint32_t ips_statupd_copperhead(ips_ha_t *);
@@ -305,13 +303,13 @@ static uint32_t ips_statupd_copperhead_memio(ips_ha_t *);
static uint32_t ips_statupd_morpheus(ips_ha_t *);
static ips_scb_t *ips_getscb(ips_ha_t *);
static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *);
-static void ips_putq_wait_tail(ips_wait_queue_t *, struct scsi_cmnd *);
+static void ips_putq_wait_tail(ips_wait_queue_entry_t *, struct scsi_cmnd *);
static void ips_putq_copp_tail(ips_copp_queue_t *,
ips_copp_wait_item_t *);
static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *);
static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *);
-static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *);
-static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *,
+static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_entry_t *);
+static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_entry_t *,
struct scsi_cmnd *);
static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *,
ips_copp_wait_item_t *);
@@ -326,10 +324,9 @@ static void ips_scmd_buf_write(struct scsi_cmnd * scmd, void *data,
static void ips_scmd_buf_read(struct scsi_cmnd * scmd, void *data,
unsigned int count);
-static int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
-static int ips_host_info(ips_ha_t *, char *, off_t, int);
-static void copy_mem_info(IPS_INFOSTR *, char *, int);
-static int copy_info(IPS_INFOSTR *, char *, ...);
+static int ips_write_info(struct Scsi_Host *, char *, int);
+static int ips_show_info(struct seq_file *, struct Scsi_Host *);
+static int ips_host_info(ips_ha_t *, struct seq_file *);
static int ips_abort_init(ips_ha_t * ha, int index);
static int ips_init_phase2(int index);
@@ -360,20 +357,19 @@ static dma_addr_t ips_flashbusaddr;
static long ips_FlashDataInUse; /* CD Boot - Flash Data In Use Flag */
static uint32_t MaxLiteCmds = 32; /* Max Active Cmds for a Lite Adapter */
static struct scsi_host_template ips_driver_template = {
- .detect = ips_detect,
- .release = ips_release,
.info = ips_info,
.queuecommand = ips_queue,
.eh_abort_handler = ips_eh_abort,
.eh_host_reset_handler = ips_eh_reset,
.proc_name = "ips",
- .proc_info = ips_proc_info,
- .slave_configure = ips_slave_configure,
+ .show_info = ips_show_info,
+ .write_info = ips_write_info,
+ .sdev_configure = ips_sdev_configure,
.bios_param = ips_biosparam,
.this_id = -1,
.sg_tablesize = IPS_MAX_SG,
.cmd_per_lun = 3,
- .use_clustering = ENABLE_CLUSTERING,
+ .no_write_same = 1,
};
@@ -389,14 +385,14 @@ MODULE_DEVICE_TABLE( pci, ips_pci_table );
static char ips_hot_plug_name[] = "ips";
-static int __devinit ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent);
-static void __devexit ips_remove_device(struct pci_dev *pci_dev);
+static int ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent);
+static void ips_remove_device(struct pci_dev *pci_dev);
static struct pci_driver ips_pci_driver = {
.name = ips_hot_plug_name,
.id_table = ips_pci_table,
.probe = ips_insert_device,
- .remove = __devexit_p(ips_remove_device),
+ .remove = ips_remove_device,
};
@@ -506,7 +502,7 @@ ips_setup(char *ips_str)
int i;
char *key;
char *value;
- IPS_OPTION options[] = {
+ static const IPS_OPTION options[] = {
{"noi2o", &ips_force_i2o, 0},
{"nommap", &ips_force_memio, 0},
{"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE},
@@ -527,7 +523,7 @@ ips_setup(char *ips_str)
* Update the variables
*/
for (i = 0; i < ARRAY_SIZE(options); i++) {
- if (strnicmp
+ if (strncasecmp
(key, options[i].option_name,
strlen(options[i].option_name)) == 0) {
if (value)
@@ -646,8 +642,7 @@ ips_setup_funclist(ips_ha_t * ha)
/* Remove a driver */
/* */
/****************************************************************************/
-static int
-ips_release(struct Scsi_Host *sh)
+static void ips_release(struct Scsi_Host *sh)
{
ips_scb_t *scb;
ips_ha_t *ha;
@@ -663,13 +658,12 @@ ips_release(struct Scsi_Host *sh)
printk(KERN_WARNING
"(%s) release, invalid Scsi_Host pointer.\n", ips_name);
BUG();
- return (FALSE);
}
ha = IPS_HA(sh);
if (!ha)
- return (FALSE);
+ return;
/* flush the cache on the controller */
scb = &ha->scbs[ha->max_cmds - 1];
@@ -707,8 +701,6 @@ ips_release(struct Scsi_Host *sh)
scsi_host_put(sh);
ips_released_controllers++;
-
- return (FALSE);
}
/****************************************************************************/
@@ -843,7 +835,6 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
int i;
ips_ha_t *ha;
ips_scb_t *scb;
- ips_copp_wait_item_t *item;
METHOD_TRACE("ips_eh_reset", 1);
@@ -868,23 +859,6 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
if (!ha->active)
return (FAILED);
- /* See if the command is on the copp queue */
- item = ha->copp_waitlist.head;
- while ((item) && (item->scsi_cmd != SC))
- item = item->next;
-
- if (item) {
- /* Found it */
- ips_removeq_copp(&ha->copp_waitlist, item);
- return (SUCCESS);
- }
-
- /* See if the command is on the wait queue */
- if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
- /* command not sent yet */
- return (SUCCESS);
- }
-
/* An explanation for the casual observer: */
/* Part of the function of a RAID controller is automatic error */
/* detection and recovery. As such, the only problem that physically */
@@ -944,7 +918,7 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
ips_freescb(ha, scb);
}
@@ -954,10 +928,10 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
scsi_cmd->result = DID_ERROR;
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
}
- ha->active = FALSE;
+ ha->active = false;
return (FAILED);
}
@@ -973,7 +947,7 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
ips_freescb(ha, scb);
}
@@ -983,19 +957,16 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
scsi_cmd->result = DID_ERROR << 16;
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
}
- ha->active = FALSE;
+ ha->active = false;
return (FAILED);
}
/* FFDC */
if (le32_to_cpu(ha->subsys->param[3]) & 0x300000) {
- struct timeval tv;
-
- do_gettimeofday(&tv);
- ha->last_ffdc = tv.tv_sec;
+ ha->last_ffdc = ktime_get_real_seconds();
ha->reset_count++;
ips_ffdc_reset(ha, IPS_INTR_IORL);
}
@@ -1005,7 +976,7 @@ static int __ips_eh_reset(struct scsi_cmnd *SC)
while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
scb->scsi_cmd->result = DID_RESET << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
ips_freescb(ha, scb);
}
@@ -1046,8 +1017,9 @@ static int ips_eh_reset(struct scsi_cmnd *SC)
/* Linux obtains io_request_lock before calling this function */
/* */
/****************************************************************************/
-static int ips_queue_lck(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *))
+static int ips_queue_lck(struct scsi_cmnd *SC)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
ips_ha_t *ha;
ips_passthru_t *pt;
@@ -1056,10 +1028,10 @@ static int ips_queue_lck(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *)
ha = (ips_ha_t *) SC->device->host->hostdata;
if (!ha)
- return (1);
+ goto out_error;
if (!ha->active)
- return (DID_ERROR);
+ goto out_error;
if (ips_is_passthru(SC)) {
if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) {
@@ -1075,8 +1047,6 @@ static int ips_queue_lck(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *)
return (0);
}
- SC->scsi_done = done;
-
DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
ips_name,
ha->host_num,
@@ -1110,7 +1080,7 @@ static int ips_queue_lck(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *)
ha->ioctl_reset = 1; /* This reset request is from an IOCTL */
__ips_eh_reset(SC);
SC->result = DID_OK << 16;
- SC->scsi_done(SC);
+ scsi_done(SC);
return (0);
}
@@ -1135,6 +1105,11 @@ static int ips_queue_lck(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *)
ips_next(ha, IPS_INTR_IORL);
return (0);
+out_error:
+ SC->result = DID_ERROR << 16;
+ done(SC);
+
+ return (0);
}
static DEF_SCSI_QCMD(ips_queue)
@@ -1148,7 +1123,7 @@ static DEF_SCSI_QCMD(ips_queue)
/* Set bios geometry for the controller */
/* */
/****************************************************************************/
-static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+static int ips_biosparam(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int geom[])
{
ips_ha_t *ha = (ips_ha_t *) sdev->host->hostdata;
@@ -1191,7 +1166,7 @@ static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
/****************************************************************************/
/* */
-/* Routine Name: ips_slave_configure */
+/* Routine Name: ips_sdev_configure */
/* */
/* Routine Description: */
/* */
@@ -1199,7 +1174,7 @@ static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
/* */
/****************************************************************************/
static int
-ips_slave_configure(struct scsi_device * SDptr)
+ips_sdev_configure(struct scsi_device *SDptr, struct queue_limits *lim)
{
ips_ha_t *ha;
int min;
@@ -1209,7 +1184,7 @@ ips_slave_configure(struct scsi_device * SDptr)
min = ha->max_cmds / 2;
if (ha->enq->ucLogDriveCount <= 2)
min = ha->max_cmds - 1;
- scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, min);
+ scsi_change_queue_depth(SDptr, min);
}
SDptr->skip_ms_page_8 = 1;
@@ -1298,7 +1273,7 @@ ips_intr_copperhead(ips_ha_t * ha)
return 0;
}
- while (TRUE) {
+ while (true) {
sp = &ha->sp;
intrstatus = (*ha->func.isintr) (ha);
@@ -1362,7 +1337,7 @@ ips_intr_morpheus(ips_ha_t * ha)
return 0;
}
- while (TRUE) {
+ while (true) {
sp = &ha->sp;
intrstatus = (*ha->func.isintr) (ha);
@@ -1433,25 +1408,12 @@ ips_info(struct Scsi_Host *SH)
return (bp);
}
-/****************************************************************************/
-/* */
-/* Routine Name: ips_proc_info */
-/* */
-/* Routine Description: */
-/* */
-/* The passthru interface for the driver */
-/* */
-/****************************************************************************/
static int
-ips_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
- int length, int func)
+ips_write_info(struct Scsi_Host *host, char *buffer, int length)
{
int i;
- int ret;
ips_ha_t *ha = NULL;
- METHOD_TRACE("ips_proc_info", 1);
-
/* Find our host structure */
for (i = 0; i < ips_next_controller; i++) {
if (ips_sh[i]) {
@@ -1465,18 +1427,29 @@ ips_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
if (!ha)
return (-EINVAL);
- if (func) {
- /* write */
- return (0);
- } else {
- /* read */
- if (start)
- *start = buffer;
+ return 0;
+}
- ret = ips_host_info(ha, buffer, offset, length);
+static int
+ips_show_info(struct seq_file *m, struct Scsi_Host *host)
+{
+ int i;
+ ips_ha_t *ha = NULL;
- return (ret);
+ /* Find our host structure */
+ for (i = 0; i < ips_next_controller; i++) {
+ if (ips_sh[i]) {
+ if (ips_sh[i] == host) {
+ ha = (ips_ha_t *) ips_sh[i]->hostdata;
+ break;
+ }
+ }
}
+
+ if (!ha)
+ return (-EINVAL);
+
+ return ips_host_info(ha, m);
}
/*--------------------------------------------------------------------------*/
@@ -1508,17 +1481,16 @@ static int ips_is_passthru(struct scsi_cmnd *SC)
struct scatterlist *sg = scsi_sglist(SC);
char *buffer;
- /* kmap_atomic() ensures addressability of the user buffer.*/
/* local_irq_save() protects the KM_IRQ0 address slot. */
local_irq_save(flags);
- buffer = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset;
- if (buffer && buffer[0] == 'C' && buffer[1] == 'O' &&
- buffer[2] == 'P' && buffer[3] == 'P') {
- kunmap_atomic(buffer - sg->offset, KM_IRQ0);
+ buffer = kmap_local_page(sg_page(sg)) + sg->offset;
+ if (buffer && buffer[0] == 'C' && buffer[1] == 'O' &&
+ buffer[2] == 'P' && buffer[3] == 'P') {
+ kunmap_local(buffer);
local_irq_restore(flags);
return 1;
}
- kunmap_atomic(buffer - sg->offset, KM_IRQ0);
+ kunmap_local(buffer);
local_irq_restore(flags);
}
return 0;
@@ -1541,11 +1513,12 @@ ips_alloc_passthru_buffer(ips_ha_t * ha, int length)
if (ha->ioctl_data && length <= ha->ioctl_len)
return 0;
/* there is no buffer or it's not big enough, allocate a new one */
- bigger_buf = pci_alloc_consistent(ha->pcidev, length, &dma_busaddr);
+ bigger_buf = dma_alloc_coherent(&ha->pcidev->dev, length, &dma_busaddr,
+ GFP_KERNEL);
if (bigger_buf) {
/* free the old memory */
- pci_free_consistent(ha->pcidev, ha->ioctl_len, ha->ioctl_data,
- ha->ioctl_busaddr);
+ dma_free_coherent(&ha->pcidev->dev, ha->ioctl_len,
+ ha->ioctl_data, ha->ioctl_busaddr);
/* use the new memory */
ha->ioctl_data = (char *) bigger_buf;
ha->ioctl_len = length;
@@ -1690,9 +1663,8 @@ ips_flash_copperhead(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
} else if (!ha->flash_data) {
datasize = pt->CoppCP.cmd.flashfw.total_packets *
pt->CoppCP.cmd.flashfw.count;
- ha->flash_data = pci_alloc_consistent(ha->pcidev,
- datasize,
- &ha->flash_busaddr);
+ ha->flash_data = dma_alloc_coherent(&ha->pcidev->dev,
+ datasize, &ha->flash_busaddr, GFP_KERNEL);
if (!ha->flash_data){
printk(KERN_WARNING "Unable to allocate a flash buffer\n");
return IPS_FAILURE;
@@ -1813,13 +1785,13 @@ ips_fill_scb_sg_single(ips_ha_t * ha, dma_addr_t busaddr,
}
if (IPS_USE_ENH_SGLIST(ha)) {
scb->sg_list.enh_list[indx].address_lo =
- cpu_to_le32(pci_dma_lo32(busaddr));
+ cpu_to_le32(lower_32_bits(busaddr));
scb->sg_list.enh_list[indx].address_hi =
- cpu_to_le32(pci_dma_hi32(busaddr));
+ cpu_to_le32(upper_32_bits(busaddr));
scb->sg_list.enh_list[indx].length = cpu_to_le32(e_len);
} else {
scb->sg_list.std_list[indx].address =
- cpu_to_le32(pci_dma_lo32(busaddr));
+ cpu_to_le32(lower_32_bits(busaddr));
scb->sg_list.std_list[indx].length = cpu_to_le32(e_len);
}
@@ -1870,7 +1842,7 @@ ips_flash_firmware(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
scb->data_len = ha->flash_datasize;
scb->data_busaddr =
- pci_map_single(ha->pcidev, ha->flash_data, scb->data_len,
+ dma_map_single(&ha->pcidev->dev, ha->flash_data, scb->data_len,
IPS_DMA_DIR(scb));
scb->flags |= IPS_SCB_MAP_SINGLE;
scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
@@ -1892,8 +1864,8 @@ ips_free_flash_copperhead(ips_ha_t * ha)
if (ha->flash_data == ips_FlashData)
test_and_clear_bit(0, &ips_FlashDataInUse);
else if (ha->flash_data)
- pci_free_consistent(ha->pcidev, ha->flash_len, ha->flash_data,
- ha->flash_busaddr);
+ dma_free_coherent(&ha->pcidev->dev, ha->flash_len,
+ ha->flash_data, ha->flash_busaddr);
ha->flash_data = NULL;
}
@@ -2035,183 +2007,112 @@ ips_cleanup_passthru(ips_ha_t * ha, ips_scb_t * scb)
/* */
/****************************************************************************/
static int
-ips_host_info(ips_ha_t * ha, char *ptr, off_t offset, int len)
+ips_host_info(ips_ha_t *ha, struct seq_file *m)
{
- IPS_INFOSTR info;
-
METHOD_TRACE("ips_host_info", 1);
- info.buffer = ptr;
- info.length = len;
- info.offset = offset;
- info.pos = 0;
- info.localpos = 0;
-
- copy_info(&info, "\nIBM ServeRAID General Information:\n\n");
+ seq_puts(m, "\nIBM ServeRAID General Information:\n\n");
if ((le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) &&
(le16_to_cpu(ha->nvram->adapter_type) != 0))
- copy_info(&info, "\tController Type : %s\n",
+ seq_printf(m, "\tController Type : %s\n",
ips_adapter_name[ha->ad_type - 1]);
else
- copy_info(&info,
- "\tController Type : Unknown\n");
+ seq_puts(m, "\tController Type : Unknown\n");
if (ha->io_addr)
- copy_info(&info,
- "\tIO region : 0x%lx (%d bytes)\n",
+ seq_printf(m,
+ "\tIO region : 0x%x (%d bytes)\n",
ha->io_addr, ha->io_len);
if (ha->mem_addr) {
- copy_info(&info,
- "\tMemory region : 0x%lx (%d bytes)\n",
+ seq_printf(m,
+ "\tMemory region : 0x%x (%d bytes)\n",
ha->mem_addr, ha->mem_len);
- copy_info(&info,
+ seq_printf(m,
"\tShared memory address : 0x%lx\n",
- ha->mem_ptr);
+ (unsigned long)ha->mem_ptr);
}
- copy_info(&info, "\tIRQ number : %d\n", ha->pcidev->irq);
+ seq_printf(m, "\tIRQ number : %d\n", ha->pcidev->irq);
/* For the Next 3 lines Check for Binary 0 at the end and don't include it if it's there. */
/* That keeps everything happy for "text" operations on the proc file. */
if (le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) {
if (ha->nvram->bios_low[3] == 0) {
- copy_info(&info,
- "\tBIOS Version : %c%c%c%c%c%c%c\n",
- ha->nvram->bios_high[0], ha->nvram->bios_high[1],
- ha->nvram->bios_high[2], ha->nvram->bios_high[3],
- ha->nvram->bios_low[0], ha->nvram->bios_low[1],
- ha->nvram->bios_low[2]);
+ seq_printf(m,
+ "\tBIOS Version : %c%c%c%c%c%c%c\n",
+ ha->nvram->bios_high[0], ha->nvram->bios_high[1],
+ ha->nvram->bios_high[2], ha->nvram->bios_high[3],
+ ha->nvram->bios_low[0], ha->nvram->bios_low[1],
+ ha->nvram->bios_low[2]);
} else {
- copy_info(&info,
- "\tBIOS Version : %c%c%c%c%c%c%c%c\n",
- ha->nvram->bios_high[0], ha->nvram->bios_high[1],
- ha->nvram->bios_high[2], ha->nvram->bios_high[3],
- ha->nvram->bios_low[0], ha->nvram->bios_low[1],
- ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
+ seq_printf(m,
+ "\tBIOS Version : %c%c%c%c%c%c%c%c\n",
+ ha->nvram->bios_high[0], ha->nvram->bios_high[1],
+ ha->nvram->bios_high[2], ha->nvram->bios_high[3],
+ ha->nvram->bios_low[0], ha->nvram->bios_low[1],
+ ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
}
}
if (ha->enq->CodeBlkVersion[7] == 0) {
- copy_info(&info,
- "\tFirmware Version : %c%c%c%c%c%c%c\n",
- ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
- ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
- ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
- ha->enq->CodeBlkVersion[6]);
+ seq_printf(m,
+ "\tFirmware Version : %c%c%c%c%c%c%c\n",
+ ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
+ ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
+ ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
+ ha->enq->CodeBlkVersion[6]);
} else {
- copy_info(&info,
- "\tFirmware Version : %c%c%c%c%c%c%c%c\n",
- ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
- ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
- ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
- ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]);
+ seq_printf(m,
+ "\tFirmware Version : %c%c%c%c%c%c%c%c\n",
+ ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
+ ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
+ ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
+ ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]);
}
if (ha->enq->BootBlkVersion[7] == 0) {
- copy_info(&info,
- "\tBoot Block Version : %c%c%c%c%c%c%c\n",
- ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
- ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
- ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
- ha->enq->BootBlkVersion[6]);
+ seq_printf(m,
+ "\tBoot Block Version : %c%c%c%c%c%c%c\n",
+ ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
+ ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
+ ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
+ ha->enq->BootBlkVersion[6]);
} else {
- copy_info(&info,
- "\tBoot Block Version : %c%c%c%c%c%c%c%c\n",
- ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
- ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
- ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
- ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]);
+ seq_printf(m,
+ "\tBoot Block Version : %c%c%c%c%c%c%c%c\n",
+ ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
+ ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
+ ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
+ ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]);
}
- copy_info(&info, "\tDriver Version : %s%s\n",
+ seq_printf(m, "\tDriver Version : %s%s\n",
IPS_VERSION_HIGH, IPS_VERSION_LOW);
- copy_info(&info, "\tDriver Build : %d\n",
+ seq_printf(m, "\tDriver Build : %d\n",
IPS_BUILD_IDENT);
- copy_info(&info, "\tMax Physical Devices : %d\n",
+ seq_printf(m, "\tMax Physical Devices : %d\n",
ha->enq->ucMaxPhysicalDevices);
- copy_info(&info, "\tMax Active Commands : %d\n",
+ seq_printf(m, "\tMax Active Commands : %d\n",
ha->max_cmds);
- copy_info(&info, "\tCurrent Queued Commands : %d\n",
+ seq_printf(m, "\tCurrent Queued Commands : %d\n",
ha->scb_waitlist.count);
- copy_info(&info, "\tCurrent Active Commands : %d\n",
+ seq_printf(m, "\tCurrent Active Commands : %d\n",
ha->scb_activelist.count - ha->num_ioctl);
- copy_info(&info, "\tCurrent Queued PT Commands : %d\n",
+ seq_printf(m, "\tCurrent Queued PT Commands : %d\n",
ha->copp_waitlist.count);
- copy_info(&info, "\tCurrent Active PT Commands : %d\n",
+ seq_printf(m, "\tCurrent Active PT Commands : %d\n",
ha->num_ioctl);
- copy_info(&info, "\n");
-
- return (info.localpos);
-}
-
-/****************************************************************************/
-/* */
-/* Routine Name: copy_mem_info */
-/* */
-/* Routine Description: */
-/* */
-/* Copy data into an IPS_INFOSTR structure */
-/* */
-/****************************************************************************/
-static void
-copy_mem_info(IPS_INFOSTR * info, char *data, int len)
-{
- METHOD_TRACE("copy_mem_info", 1);
-
- if (info->pos + len < info->offset) {
- info->pos += len;
- return;
- }
-
- if (info->pos < info->offset) {
- data += (info->offset - info->pos);
- len -= (info->offset - info->pos);
- info->pos += (info->offset - info->pos);
- }
-
- if (info->localpos + len > info->length)
- len = info->length - info->localpos;
-
- if (len > 0) {
- memcpy(info->buffer + info->localpos, data, len);
- info->pos += len;
- info->localpos += len;
- }
-}
-
-/****************************************************************************/
-/* */
-/* Routine Name: copy_info */
-/* */
-/* Routine Description: */
-/* */
-/* printf style wrapper for an info structure */
-/* */
-/****************************************************************************/
-static int
-copy_info(IPS_INFOSTR * info, char *fmt, ...)
-{
- va_list args;
- char buf[128];
- int len;
-
- METHOD_TRACE("copy_info", 1);
+ seq_putc(m, '\n');
- va_start(args, fmt);
- len = vsprintf(buf, fmt, args);
- va_end(args);
-
- copy_mem_info(info, buf, len);
-
- return (len);
+ return 0;
}
/****************************************************************************/
@@ -2317,16 +2218,13 @@ ips_get_bios_version(ips_ha_t * ha, int intr)
uint8_t minor;
uint8_t subminor;
uint8_t *buffer;
- char hexDigits[] =
- { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
- 'D', 'E', 'F' };
METHOD_TRACE("ips_get_bios_version", 1);
major = 0;
minor = 0;
- strncpy(ha->bios_version, " ?", 8);
+ memcpy(ha->bios_version, " ?", 8);
if (ha->pcidev->device == IPS_DEVICEID_COPPERHEAD) {
if (IPS_USE_MEMIO(ha)) {
@@ -2450,13 +2348,13 @@ ips_get_bios_version(ips_ha_t * ha, int intr)
}
}
- ha->bios_version[0] = hexDigits[(major & 0xF0) >> 4];
+ ha->bios_version[0] = hex_asc_upper_hi(major);
ha->bios_version[1] = '.';
- ha->bios_version[2] = hexDigits[major & 0x0F];
- ha->bios_version[3] = hexDigits[subminor];
+ ha->bios_version[2] = hex_asc_upper_lo(major);
+ ha->bios_version[3] = hex_asc_upper_lo(subminor);
ha->bios_version[4] = '.';
- ha->bios_version[5] = hexDigits[(minor & 0xF0) >> 4];
- ha->bios_version[6] = hexDigits[minor & 0x0F];
+ ha->bios_version[5] = hex_asc_upper_hi(minor);
+ ha->bios_version[6] = hex_asc_upper_lo(minor);
ha->bios_version[7] = 0;
}
@@ -2475,7 +2373,6 @@ static int
ips_hainit(ips_ha_t * ha)
{
int i;
- struct timeval tv;
METHOD_TRACE("ips_hainit", 1);
@@ -2490,8 +2387,7 @@ ips_hainit(ips_ha_t * ha)
/* Send FFDC */
ha->reset_count = 1;
- do_gettimeofday(&tv);
- ha->last_ffdc = tv.tv_sec;
+ ha->last_ffdc = ktime_get_real_seconds();
ips_ffdc_reset(ha, IPS_INTR_IORL);
if (!ips_read_config(ha, IPS_INTR_IORL)) {
@@ -2631,12 +2527,9 @@ ips_next(ips_ha_t * ha, int intr)
if ((ha->subsys->param[3] & 0x300000)
&& (ha->scb_activelist.count == 0)) {
- struct timeval tv;
-
- do_gettimeofday(&tv);
-
- if (tv.tv_sec - ha->last_ffdc > IPS_SECS_8HOURS) {
- ha->last_ffdc = tv.tv_sec;
+ time64_t now = ktime_get_real_seconds();
+ if (now - ha->last_ffdc > IPS_SECS_8HOURS) {
+ ha->last_ffdc = now;
ips_ffdc_time(ha);
}
}
@@ -2666,7 +2559,7 @@ ips_next(ips_ha_t * ha, int intr)
case IPS_FAILURE:
if (scb->scsi_cmd) {
scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
}
ips_freescb(ha, scb);
@@ -2674,7 +2567,7 @@ ips_next(ips_ha_t * ha, int intr)
case IPS_SUCCESS_IMM:
if (scb->scsi_cmd) {
scb->scsi_cmd->result = DID_OK << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
}
ips_freescb(ha, scb);
@@ -2799,7 +2692,7 @@ ips_next(ips_ha_t * ha, int intr)
case IPS_FAILURE:
if (scb->scsi_cmd) {
scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
}
if (scb->bus)
@@ -2810,7 +2703,7 @@ ips_next(ips_ha_t * ha, int intr)
break;
case IPS_SUCCESS_IMM:
if (scb->scsi_cmd)
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
if (scb->bus)
ha->dcdb_active[scb->bus - 1] &=
@@ -2950,7 +2843,7 @@ ips_removeq_scb(ips_scb_queue_t * queue, ips_scb_t * item)
/* ASSUMED to be called from within the HA lock */
/* */
/****************************************************************************/
-static void ips_putq_wait_tail(ips_wait_queue_t *queue, struct scsi_cmnd *item)
+static void ips_putq_wait_tail(ips_wait_queue_entry_t *queue, struct scsi_cmnd *item)
{
METHOD_TRACE("ips_putq_wait_tail", 1);
@@ -2981,7 +2874,7 @@ static void ips_putq_wait_tail(ips_wait_queue_t *queue, struct scsi_cmnd *item)
/* ASSUMED to be called from within the HA lock */
/* */
/****************************************************************************/
-static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *queue)
+static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_entry_t *queue)
{
struct scsi_cmnd *item;
@@ -3015,7 +2908,7 @@ static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *queue)
/* ASSUMED to be called from within the HA lock */
/* */
/****************************************************************************/
-static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *queue,
+static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_entry_t *queue,
struct scsi_cmnd *item)
{
struct scsi_cmnd *p;
@@ -3178,8 +3071,8 @@ ipsintr_blocking(ips_ha_t * ha, ips_scb_t * scb)
METHOD_TRACE("ipsintr_blocking", 2);
ips_freescb(ha, scb);
- if ((ha->waitflag == TRUE) && (ha->cmd_in_progress == scb->cdb[0])) {
- ha->waitflag = FALSE;
+ if (ha->waitflag && ha->cmd_in_progress == scb->cdb[0]) {
+ ha->waitflag = false;
return;
}
@@ -3293,7 +3186,7 @@ ips_done(ips_ha_t * ha, ips_scb_t * scb)
case IPS_FAILURE:
if (scb->scsi_cmd) {
scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
}
ips_freescb(ha, scb);
@@ -3301,7 +3194,7 @@ ips_done(ips_ha_t * ha, ips_scb_t * scb)
case IPS_SUCCESS_IMM:
if (scb->scsi_cmd) {
scb->scsi_cmd->result = DID_ERROR << 16;
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
}
ips_freescb(ha, scb);
@@ -3318,7 +3211,7 @@ ips_done(ips_ha_t * ha, ips_scb_t * scb)
ha->dcdb_active[scb->bus - 1] &= ~(1 << scb->target_id);
}
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ scsi_done(scb->scsi_cmd);
ips_freescb(ha, scb);
}
@@ -3431,13 +3324,15 @@ ips_map_status(ips_ha_t * ha, ips_scb_t * scb, ips_stat_t * sp)
IPS_CMD_EXTENDED_DCDB_SG)) {
tapeDCDB =
(IPS_DCDB_TABLE_TAPE *) & scb->dcdb;
- memcpy(scb->scsi_cmd->sense_buffer,
+ memcpy_and_pad(scb->scsi_cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
tapeDCDB->sense_info,
- SCSI_SENSE_BUFFERSIZE);
+ sizeof(tapeDCDB->sense_info), 0);
} else {
- memcpy(scb->scsi_cmd->sense_buffer,
+ memcpy_and_pad(scb->scsi_cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
scb->dcdb.sense_info,
- SCSI_SENSE_BUFFERSIZE);
+ sizeof(scb->dcdb.sense_info), 0);
}
device_error = 2; /* check condition */
}
@@ -3477,7 +3372,7 @@ ips_send_wait(ips_ha_t * ha, ips_scb_t * scb, int timeout, int intr)
METHOD_TRACE("ips_send_wait", 1);
if (intr != IPS_FFDC) { /* Won't be Waiting if this is a Time Stamp */
- ha->waitflag = TRUE;
+ ha->waitflag = true;
ha->cmd_in_progress = scb->cdb[0];
}
scb->callback = ipsintr_blocking;
@@ -3554,10 +3449,8 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
if (scb->bus > 0) {
/* Controller commands can't be issued */
/* to real devices -- fail them */
- if ((ha->waitflag == TRUE) &&
- (ha->cmd_in_progress == scb->cdb[0])) {
- ha->waitflag = FALSE;
- }
+ if (ha->waitflag && ha->cmd_in_progress == scb->cdb[0])
+ ha->waitflag = false;
return (1);
}
@@ -3576,6 +3469,7 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
case START_STOP:
scb->scsi_cmd->result = DID_OK << 16;
+ break;
case TEST_UNIT_READY:
case INQUIRY:
@@ -3606,11 +3500,11 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
inquiry.Flags[1] =
IPS_SCSI_INQ_WBus16 |
IPS_SCSI_INQ_Sync;
- strncpy(inquiry.VendorId, "IBM ",
+ memcpy(inquiry.VendorId, "IBM ",
8);
- strncpy(inquiry.ProductId,
+ memcpy(inquiry.ProductId,
"SERVERAID ", 16);
- strncpy(inquiry.ProductRevisionLevel,
+ memcpy(inquiry.ProductRevisionLevel,
"1.00", 4);
ips_scmd_buf_write(scb->scsi_cmd,
@@ -3737,8 +3631,8 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
break;
- case RESERVE:
- case RELEASE:
+ case RESERVE_6:
+ case RELEASE_6:
scb->scsi_cmd->result = DID_OK << 16;
break;
@@ -3819,7 +3713,7 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
scb->cmd.dcdb.segment_4G = 0;
scb->cmd.dcdb.enhanced_sg = 0;
- TimeOut = scb->scsi_cmd->request->timeout;
+ TimeOut = scsi_cmd_to_rq(scb->scsi_cmd)->timeout;
if (ha->subsys->param[4] & 0x00100000) { /* If NEW Tape DCDB is Supported */
if (!scb->sg_len) {
@@ -4005,8 +3899,8 @@ ips_chkstatus(ips_ha_t * ha, IPS_STATUS * pstatus)
case WRITE_6:
case READ_10:
case WRITE_10:
- case RESERVE:
- case RELEASE:
+ case RESERVE_6:
+ case RELEASE_6:
break;
case MODE_SENSE:
@@ -4127,9 +4021,9 @@ ips_inquiry(ips_ha_t * ha, ips_scb_t * scb)
inquiry.Flags[0] = IPS_SCSI_INQ_Address16;
inquiry.Flags[1] =
IPS_SCSI_INQ_WBus16 | IPS_SCSI_INQ_Sync | IPS_SCSI_INQ_CmdQue;
- strncpy(inquiry.VendorId, "IBM ", 8);
- strncpy(inquiry.ProductId, "SERVERAID ", 16);
- strncpy(inquiry.ProductRevisionLevel, "1.00", 4);
+ memcpy(inquiry.VendorId, "IBM ", 8);
+ memcpy(inquiry.ProductId, "SERVERAID ", 16);
+ memcpy(inquiry.ProductRevisionLevel, "1.00", 4);
ips_scmd_buf_write(scb->scsi_cmd, &inquiry, sizeof (inquiry));
@@ -4303,7 +4197,7 @@ ips_free(ips_ha_t * ha)
if (ha) {
if (ha->enq) {
- pci_free_consistent(ha->pcidev, sizeof(IPS_ENQ),
+ dma_free_coherent(&ha->pcidev->dev, sizeof(IPS_ENQ),
ha->enq, ha->enq_busaddr);
ha->enq = NULL;
}
@@ -4312,7 +4206,7 @@ ips_free(ips_ha_t * ha)
ha->conf = NULL;
if (ha->adapt) {
- pci_free_consistent(ha->pcidev,
+ dma_free_coherent(&ha->pcidev->dev,
sizeof (IPS_ADAPTER) +
sizeof (IPS_IO_CMD), ha->adapt,
ha->adapt->hw_status_start);
@@ -4320,7 +4214,7 @@ ips_free(ips_ha_t * ha)
}
if (ha->logical_drive_info) {
- pci_free_consistent(ha->pcidev,
+ dma_free_coherent(&ha->pcidev->dev,
sizeof (IPS_LD_INFO),
ha->logical_drive_info,
ha->logical_drive_info_dma_addr);
@@ -4334,7 +4228,7 @@ ips_free(ips_ha_t * ha)
ha->subsys = NULL;
if (ha->ioctl_data) {
- pci_free_consistent(ha->pcidev, ha->ioctl_len,
+ dma_free_coherent(&ha->pcidev->dev, ha->ioctl_len,
ha->ioctl_data, ha->ioctl_busaddr);
ha->ioctl_data = NULL;
ha->ioctl_datasize = 0;
@@ -4367,11 +4261,11 @@ static int
ips_deallocatescbs(ips_ha_t * ha, int cmds)
{
if (ha->scbs) {
- pci_free_consistent(ha->pcidev,
+ dma_free_coherent(&ha->pcidev->dev,
IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * cmds,
ha->scbs->sg_list.list,
ha->scbs->sg_busaddr);
- pci_free_consistent(ha->pcidev, sizeof (ips_scb_t) * cmds,
+ dma_free_coherent(&ha->pcidev->dev, sizeof (ips_scb_t) * cmds,
ha->scbs, ha->scbs->scb_busaddr);
ha->scbs = NULL;
} /* end if */
@@ -4398,17 +4292,16 @@ ips_allocatescbs(ips_ha_t * ha)
METHOD_TRACE("ips_allocatescbs", 1);
/* Allocate memory for the SCBs */
- ha->scbs =
- pci_alloc_consistent(ha->pcidev, ha->max_cmds * sizeof (ips_scb_t),
- &command_dma);
+ ha->scbs = dma_alloc_coherent(&ha->pcidev->dev,
+ ha->max_cmds * sizeof (ips_scb_t),
+ &command_dma, GFP_KERNEL);
if (ha->scbs == NULL)
return 0;
- ips_sg.list =
- pci_alloc_consistent(ha->pcidev,
- IPS_SGLIST_SIZE(ha) * IPS_MAX_SG *
- ha->max_cmds, &sg_dma);
+ ips_sg.list = dma_alloc_coherent(&ha->pcidev->dev,
+ IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * ha->max_cmds,
+ &sg_dma, GFP_KERNEL);
if (ips_sg.list == NULL) {
- pci_free_consistent(ha->pcidev,
+ dma_free_coherent(&ha->pcidev->dev,
ha->max_cmds * sizeof (ips_scb_t), ha->scbs,
command_dma);
return 0;
@@ -4494,7 +4387,7 @@ ips_init_scb(ips_ha_t * ha, ips_scb_t * scb)
/* */
/* Initialize a CCB to default values */
/* */
-/* ASSUMED to be callled from within a lock */
+/* ASSUMED to be called from within a lock */
/* */
/****************************************************************************/
static ips_scb_t *
@@ -4537,8 +4430,8 @@ ips_freescb(ips_ha_t * ha, ips_scb_t * scb)
if (scb->flags & IPS_SCB_MAP_SG)
scsi_dma_unmap(scb->scsi_cmd);
else if (scb->flags & IPS_SCB_MAP_SINGLE)
- pci_unmap_single(ha->pcidev, scb->data_busaddr, scb->data_len,
- IPS_DMA_DIR(scb));
+ dma_unmap_single(&ha->pcidev->dev, scb->data_busaddr,
+ scb->data_len, IPS_DMA_DIR(scb));
/* check to make sure this is not our "special" scb */
if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
@@ -4650,7 +4543,8 @@ ips_flush_and_reset(ips_ha_t *ha)
dma_addr_t command_dma;
/* Create a usuable SCB */
- scb = pci_alloc_consistent(ha->pcidev, sizeof(ips_scb_t), &command_dma);
+ scb = dma_alloc_coherent(&ha->pcidev->dev, sizeof(ips_scb_t),
+ &command_dma, GFP_KERNEL);
if (scb) {
memset(scb, 0, sizeof(ips_scb_t));
ips_init_scb(ha, scb);
@@ -4685,7 +4579,7 @@ ips_flush_and_reset(ips_ha_t *ha)
/* Now RESET and INIT the adapter */
(*ha->func.reset) (ha);
- pci_free_consistent(ha->pcidev, sizeof(ips_scb_t), scb, command_dma);
+ dma_free_coherent(&ha->pcidev->dev, sizeof(ips_scb_t), scb, command_dma);
return;
}
@@ -4704,7 +4598,7 @@ ips_poll_for_flush_complete(ips_ha_t * ha)
{
IPS_STATUS cstatus;
- while (TRUE) {
+ while (true) {
cstatus.value = (*ha->func.statupd) (ha);
if (cstatus.value == 0xffffffff) /* If No Interrupt to process */
@@ -4788,7 +4682,6 @@ ips_init_copperhead(ips_ha_t * ha)
uint8_t Isr;
uint8_t Cbsp;
uint8_t PostByte[IPS_MAX_POST_BYTES];
- uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
int i, j;
METHOD_TRACE("ips_init_copperhead", 1);
@@ -4833,7 +4726,7 @@ ips_init_copperhead(ips_ha_t * ha)
/* error occurred */
return (0);
- ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+ inb(ha->io_addr + IPS_REG_ISPR);
outb(Isr, ha->io_addr + IPS_REG_HISR);
}
@@ -4882,7 +4775,6 @@ ips_init_copperhead_memio(ips_ha_t * ha)
uint8_t Isr = 0;
uint8_t Cbsp;
uint8_t PostByte[IPS_MAX_POST_BYTES];
- uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
int i, j;
METHOD_TRACE("ips_init_copperhead_memio", 1);
@@ -4927,7 +4819,7 @@ ips_init_copperhead_memio(ips_ha_t * ha)
/* error occurred */
return (0);
- ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+ readb(ha->mem_ptr + IPS_REG_ISPR);
writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
}
@@ -5629,26 +5521,26 @@ ips_wait(ips_ha_t * ha, int time, int intr)
METHOD_TRACE("ips_wait", 1);
ret = IPS_FAILURE;
- done = FALSE;
+ done = false;
time *= IPS_ONE_SEC; /* convert seconds */
while ((time > 0) && (!done)) {
if (intr == IPS_INTR_ON) {
- if (ha->waitflag == FALSE) {
+ if (!ha->waitflag) {
ret = IPS_SUCCESS;
- done = TRUE;
+ done = true;
break;
}
} else if (intr == IPS_INTR_IORL) {
- if (ha->waitflag == FALSE) {
+ if (!ha->waitflag) {
/*
* controller generated an interrupt to
* acknowledge completion of the command
* and ips_intr() has serviced the interrupt.
*/
ret = IPS_SUCCESS;
- done = TRUE;
+ done = true;
break;
}
@@ -5683,7 +5575,7 @@ ips_write_driver_status(ips_ha_t * ha, int intr)
{
METHOD_TRACE("ips_write_driver_status", 1);
- if (!ips_readwrite_page5(ha, FALSE, intr)) {
+ if (!ips_readwrite_page5(ha, false, intr)) {
IPS_PRINTK(KERN_WARNING, ha->pcidev,
"unable to read NVRAM page 5.\n");
@@ -5713,15 +5605,15 @@ ips_write_driver_status(ips_ha_t * ha, int intr)
/* change values (as needed) */
ha->nvram->operating_system = IPS_OS_LINUX;
ha->nvram->adapter_type = ha->ad_type;
- strncpy((char *) ha->nvram->driver_high, IPS_VERSION_HIGH, 4);
- strncpy((char *) ha->nvram->driver_low, IPS_VERSION_LOW, 4);
- strncpy((char *) ha->nvram->bios_high, ha->bios_version, 4);
- strncpy((char *) ha->nvram->bios_low, ha->bios_version + 4, 4);
+ memcpy((char *) ha->nvram->driver_high, IPS_VERSION_HIGH, 4);
+ memcpy((char *) ha->nvram->driver_low, IPS_VERSION_LOW, 4);
+ memcpy((char *) ha->nvram->bios_high, ha->bios_version, 4);
+ memcpy((char *) ha->nvram->bios_low, ha->bios_version + 4, 4);
ha->nvram->versioning = 0; /* Indicate the Driver Does Not Support Versioning */
/* now update the page */
- if (!ips_readwrite_page5(ha, TRUE, intr)) {
+ if (!ips_readwrite_page5(ha, true, intr)) {
IPS_PRINTK(KERN_WARNING, ha->pcidev,
"unable to write NVRAM page 5.\n");
@@ -6071,59 +5963,21 @@ ips_ffdc_time(ips_ha_t * ha)
/* */
/****************************************************************************/
static void
-ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time_t current_time)
+ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time64_t current_time)
{
- long days;
- long rem;
- int i;
- int year;
- int yleap;
- int year_lengths[2] = { IPS_DAYS_NORMAL_YEAR, IPS_DAYS_LEAP_YEAR };
- int month_lengths[12][2] = { {31, 31},
- {28, 29},
- {31, 31},
- {30, 30},
- {31, 31},
- {30, 30},
- {31, 31},
- {31, 31},
- {30, 30},
- {31, 31},
- {30, 30},
- {31, 31}
- };
+ struct tm tm;
METHOD_TRACE("ips_fix_ffdc_time", 1);
- days = current_time / IPS_SECS_DAY;
- rem = current_time % IPS_SECS_DAY;
-
- scb->cmd.ffdc.hour = (rem / IPS_SECS_HOUR);
- rem = rem % IPS_SECS_HOUR;
- scb->cmd.ffdc.minute = (rem / IPS_SECS_MIN);
- scb->cmd.ffdc.second = (rem % IPS_SECS_MIN);
-
- year = IPS_EPOCH_YEAR;
- while (days < 0 || days >= year_lengths[yleap = IPS_IS_LEAP_YEAR(year)]) {
- int newy;
+ time64_to_tm(current_time, 0, &tm);
- newy = year + (days / IPS_DAYS_NORMAL_YEAR);
- if (days < 0)
- --newy;
- days -= (newy - year) * IPS_DAYS_NORMAL_YEAR +
- IPS_NUM_LEAP_YEARS_THROUGH(newy - 1) -
- IPS_NUM_LEAP_YEARS_THROUGH(year - 1);
- year = newy;
- }
-
- scb->cmd.ffdc.yearH = year / 100;
- scb->cmd.ffdc.yearL = year % 100;
-
- for (i = 0; days >= month_lengths[i][yleap]; ++i)
- days -= month_lengths[i][yleap];
-
- scb->cmd.ffdc.month = i + 1;
- scb->cmd.ffdc.day = days + 1;
+ scb->cmd.ffdc.hour = tm.tm_hour;
+ scb->cmd.ffdc.minute = tm.tm_min;
+ scb->cmd.ffdc.second = tm.tm_sec;
+ scb->cmd.ffdc.yearH = (tm.tm_year + 1900) / 100;
+ scb->cmd.ffdc.yearL = tm.tm_year % 100;
+ scb->cmd.ffdc.month = tm.tm_mon + 1;
+ scb->cmd.ffdc.day = tm.tm_mday;
}
/****************************************************************************
@@ -6806,7 +6660,6 @@ ips_register_scsi(int index)
sh->sg_tablesize = sh->hostt->sg_tablesize;
sh->can_queue = sh->hostt->can_queue;
sh->cmd_per_lun = sh->hostt->cmd_per_lun;
- sh->use_clustering = sh->hostt->use_clustering;
sh->max_sectors = 128;
sh->max_id = ha->ntargets;
@@ -6837,7 +6690,7 @@ err_out_sh:
/* Routine Description: */
/* Remove one Adapter ( Hot Plugging ) */
/*---------------------------------------------------------------------------*/
-static void __devexit
+static void
ips_remove_device(struct pci_dev *pci_dev)
{
struct Scsi_Host *sh = pci_get_drvdata(pci_dev);
@@ -6860,6 +6713,11 @@ ips_remove_device(struct pci_dev *pci_dev)
static int __init
ips_module_init(void)
{
+#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
+ printk(KERN_ERR "ips: This driver has only been tested on the x86/ia64/x86_64 platforms\n");
+ add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+#endif
+
if (pci_register_driver(&ips_pci_driver) < 0)
return -ENODEV;
ips_driver_template.module = THIS_MODULE;
@@ -6898,7 +6756,7 @@ module_exit(ips_module_exit);
/* Return Value: */
/* 0 if Successful, else non-zero */
/*---------------------------------------------------------------------------*/
-static int __devinit
+static int
ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent)
{
int index = -1;
@@ -6960,8 +6818,6 @@ ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
uint32_t mem_addr;
uint32_t io_len;
uint32_t mem_len;
- uint8_t bus;
- uint8_t func;
int j;
int index;
dma_addr_t dma_address;
@@ -6981,10 +6837,6 @@ ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
if (index >= IPS_MAX_ADAPTERS)
return -1;
- /* stuff that we get in dev */
- bus = pci_dev->bus->number;
- func = pci_dev->devfn;
-
/* Init MEM/IO addresses to 0 */
mem_addr = 0;
io_addr = 0;
@@ -7049,30 +6901,31 @@ ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
* it! Also, don't use 64bit addressing if dma addresses
* are guaranteed to be < 4G.
*/
- if (IPS_ENABLE_DMA64 && IPS_HAS_ENH_SGLIST(ha) &&
- !pci_set_dma_mask(ha->pcidev, DMA_BIT_MASK(64))) {
+ if (sizeof(dma_addr_t) > 4 && IPS_HAS_ENH_SGLIST(ha) &&
+ !dma_set_mask(&ha->pcidev->dev, DMA_BIT_MASK(64))) {
(ha)->flags |= IPS_HA_ENH_SG;
} else {
- if (pci_set_dma_mask(ha->pcidev, DMA_BIT_MASK(32)) != 0) {
+ if (dma_set_mask(&ha->pcidev->dev, DMA_BIT_MASK(32)) != 0) {
printk(KERN_WARNING "Unable to set DMA Mask\n");
return ips_abort_init(ha, index);
}
}
if(ips_cd_boot && !ips_FlashData){
- ips_FlashData = pci_alloc_consistent(pci_dev, PAGE_SIZE << 7,
- &ips_flashbusaddr);
+ ips_FlashData = dma_alloc_coherent(&pci_dev->dev,
+ PAGE_SIZE << 7, &ips_flashbusaddr, GFP_KERNEL);
}
- ha->enq = pci_alloc_consistent(pci_dev, sizeof (IPS_ENQ),
- &ha->enq_busaddr);
+ ha->enq = dma_alloc_coherent(&pci_dev->dev, sizeof (IPS_ENQ),
+ &ha->enq_busaddr, GFP_KERNEL);
if (!ha->enq) {
IPS_PRINTK(KERN_WARNING, pci_dev,
"Unable to allocate host inquiry structure\n");
return ips_abort_init(ha, index);
}
- ha->adapt = pci_alloc_consistent(pci_dev, sizeof (IPS_ADAPTER) +
- sizeof (IPS_IO_CMD), &dma_address);
+ ha->adapt = dma_alloc_coherent(&pci_dev->dev,
+ sizeof (IPS_ADAPTER) + sizeof (IPS_IO_CMD),
+ &dma_address, GFP_KERNEL);
if (!ha->adapt) {
IPS_PRINTK(KERN_WARNING, pci_dev,
"Unable to allocate host adapt & dummy structures\n");
@@ -7083,7 +6936,8 @@ ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
- ha->logical_drive_info = pci_alloc_consistent(pci_dev, sizeof (IPS_LD_INFO), &dma_address);
+ ha->logical_drive_info = dma_alloc_coherent(&pci_dev->dev,
+ sizeof (IPS_LD_INFO), &dma_address, GFP_KERNEL);
if (!ha->logical_drive_info) {
IPS_PRINTK(KERN_WARNING, pci_dev,
"Unable to allocate logical drive info structure\n");
@@ -7121,8 +6975,8 @@ ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
if (ips_ioctlsize < PAGE_SIZE)
ips_ioctlsize = PAGE_SIZE;
- ha->ioctl_data = pci_alloc_consistent(pci_dev, ips_ioctlsize,
- &ha->ioctl_busaddr);
+ ha->ioctl_data = dma_alloc_coherent(&pci_dev->dev, ips_ioctlsize,
+ &ha->ioctl_busaddr, GFP_KERNEL);
ha->ioctl_len = ips_ioctlsize;
if (!ha->ioctl_data) {
IPS_PRINTK(KERN_WARNING, pci_dev,
@@ -7225,23 +7079,3 @@ ips_init_phase2(int index)
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IBM ServeRAID Adapter Driver " IPS_VER_STRING);
MODULE_VERSION(IPS_VER_STRING);
-
-
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 2
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -2
- * c-argdecl-indent: 2
- * c-label-offset: -2
- * c-continued-statement-offset: 2
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h
index f2df0593332b..30a4d4a580e9 100644
--- a/drivers/scsi/ips.h
+++ b/drivers/scsi/ips.h
@@ -51,7 +51,7 @@
#define _IPS_H_
#include <linux/nmi.h>
- #include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
/*
@@ -96,15 +96,6 @@
#define __iomem
#endif
- #define pci_dma_hi32(a) ((a >> 16) >> 16)
- #define pci_dma_lo32(a) (a & 0xffffffff)
-
- #if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G)
- #define IPS_ENABLE_DMA64 (1)
- #else
- #define IPS_ENABLE_DMA64 (0)
- #endif
-
/*
* Adapter address map equates
*/
@@ -402,24 +393,15 @@
#define IPS_BIOS_HEADER 0xC0
/* time oriented stuff */
- #define IPS_IS_LEAP_YEAR(y) (((y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0))) ? 1 : 0)
- #define IPS_NUM_LEAP_YEARS_THROUGH(y) ((y) / 4 - (y) / 100 + (y) / 400)
-
- #define IPS_SECS_MIN 60
- #define IPS_SECS_HOUR 3600
#define IPS_SECS_8HOURS 28800
- #define IPS_SECS_DAY 86400
- #define IPS_DAYS_NORMAL_YEAR 365
- #define IPS_DAYS_LEAP_YEAR 366
- #define IPS_EPOCH_YEAR 1970
/*
* Scsi_Host Template
*/
- static int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
- static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ static int ips_biosparam(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int geom[]);
- static int ips_slave_configure(struct scsi_device *SDptr);
+ static int ips_sdev_configure(struct scsi_device *SDptr,
+ struct queue_limits *lim);
/*
* Raid Command Formats
@@ -959,14 +941,6 @@ typedef union {
IPS_ENH_SG_LIST *enh_list;
} IPS_SG_LIST;
-typedef struct _IPS_INFOSTR {
- char *buffer;
- int length;
- int offset;
- int pos;
- int localpos;
-} IPS_INFOSTR;
-
typedef struct {
char *option_name;
int *option_flag;
@@ -998,7 +972,7 @@ typedef struct ips_wait_queue {
struct scsi_cmnd *head;
struct scsi_cmnd *tail;
int count;
-} ips_wait_queue_t;
+} ips_wait_queue_entry_t;
typedef struct ips_copp_wait_item {
struct scsi_cmnd *scsi_cmd;
@@ -1044,7 +1018,7 @@ typedef struct ips_ha {
ips_stat_t sp; /* Status packer pointer */
struct ips_scb *scbs; /* Array of all CCBS */
struct ips_scb *scb_freelist; /* SCB free list */
- ips_wait_queue_t scb_waitlist; /* Pending SCB list */
+ ips_wait_queue_entry_t scb_waitlist; /* Pending SCB list */
ips_copp_queue_t copp_waitlist; /* Pending PT list */
ips_scb_queue_t scb_activelist; /* Active SCB list */
IPS_IO_CMD *dummy; /* dummy command */
@@ -1063,7 +1037,7 @@ typedef struct ips_ha {
uint8_t active;
int ioctl_reset; /* IOCTL Requested Reset Flag */
uint16_t reset_count; /* number of resets */
- time_t last_ffdc; /* last time we sent ffdc info*/
+ time64_t last_ffdc; /* last time we sent ffdc info*/
uint8_t slot_num; /* PCI Slot Number */
int ioctl_len; /* size of ioctl buffer */
dma_addr_t ioctl_busaddr; /* dma address of ioctl buffer*/
@@ -1238,23 +1212,3 @@ typedef struct {
IPS_COMPAT_TAMPA, \
IPS_COMPAT_KEYWEST \
}
-
-
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 2
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -2
- * c-argdecl-indent: 2
- * c-label-offset: -2
- * c-continued-statement-offset: 2
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/isci/Makefile b/drivers/scsi/isci/Makefile
index 3359e10e0d8f..da6f04cae272 100644
--- a/drivers/scsi/isci/Makefile
+++ b/drivers/scsi/isci/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SCSI_ISCI) += isci.o
isci-objs := init.o phy.o request.o \
remote_device.o port.o \
diff --git a/drivers/scsi/isci/firmware/Makefile b/drivers/scsi/isci/firmware/Makefile
deleted file mode 100644
index 5f54461cabc5..000000000000
--- a/drivers/scsi/isci/firmware/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-# Makefile for create_fw
-#
-CC=gcc
-CFLAGS=-c -Wall -O2 -g
-LDFLAGS=
-SOURCES=create_fw.c
-OBJECTS=$(SOURCES:.cpp=.o)
-EXECUTABLE=create_fw
-
-all: $(SOURCES) $(EXECUTABLE)
-
-$(EXECUTABLE): $(OBJECTS)
- $(CC) $(LDFLAGS) $(OBJECTS) -o $@
-
-.c.o:
- $(CC) $(CFLAGS) $< -O $@
-
-clean:
- rm -f *.o $(EXECUTABLE)
diff --git a/drivers/scsi/isci/firmware/README b/drivers/scsi/isci/firmware/README
deleted file mode 100644
index 8056d2bd233b..000000000000
--- a/drivers/scsi/isci/firmware/README
+++ /dev/null
@@ -1,36 +0,0 @@
-This defines the temporary binary blow we are to pass to the SCU
-driver to emulate the binary firmware that we will eventually be
-able to access via NVRAM on the SCU controller.
-
-The current size of the binary blob is expected to be 149 bytes or larger
-
-Header Types:
-0x1: Phy Masks
-0x2: Phy Gens
-0x3: SAS Addrs
-0xff: End of Data
-
-ID string - u8[12]: "#SCU MAGIC#\0"
-Version - u8: 1
-SubVersion - u8: 0
-
-Header Type - u8: 0x1
-Size - u8: 8
-Phy Mask - u32[8]
-
-Header Type - u8: 0x2
-Size - u8: 8
-Phy Gen - u32[8]
-
-Header Type - u8: 0x3
-Size - u8: 8
-Sas Addr - u64[8]
-
-Header Type - u8: 0xf
-
-
-==============================================================================
-
-Place isci_firmware.bin in /lib/firmware
-Be sure to recreate the initramfs image to include the firmware.
-
diff --git a/drivers/scsi/isci/firmware/create_fw.c b/drivers/scsi/isci/firmware/create_fw.c
deleted file mode 100644
index c7a2887a7e95..000000000000
--- a/drivers/scsi/isci/firmware/create_fw.c
+++ /dev/null
@@ -1,99 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <asm/types.h>
-#include <strings.h>
-#include <stdint.h>
-
-#include "create_fw.h"
-#include "../probe_roms.h"
-
-int write_blob(struct isci_orom *isci_orom)
-{
- FILE *fd;
- int err;
- size_t count;
-
- fd = fopen(blob_name, "w+");
- if (!fd) {
- perror("Open file for write failed");
- fclose(fd);
- return -EIO;
- }
-
- count = fwrite(isci_orom, sizeof(struct isci_orom), 1, fd);
- if (count != 1) {
- perror("Write data failed");
- fclose(fd);
- return -EIO;
- }
-
- fclose(fd);
-
- return 0;
-}
-
-void set_binary_values(struct isci_orom *isci_orom)
-{
- int ctrl_idx, phy_idx, port_idx;
-
- /* setting OROM signature */
- strncpy(isci_orom->hdr.signature, sig, strlen(sig));
- isci_orom->hdr.version = version;
- isci_orom->hdr.total_block_length = sizeof(struct isci_orom);
- isci_orom->hdr.hdr_length = sizeof(struct sci_bios_oem_param_block_hdr);
- isci_orom->hdr.num_elements = num_elements;
-
- for (ctrl_idx = 0; ctrl_idx < 2; ctrl_idx++) {
- isci_orom->ctrl[ctrl_idx].controller.mode_type = mode_type;
- isci_orom->ctrl[ctrl_idx].controller.max_concurrent_dev_spin_up =
- max_num_concurrent_dev_spin_up;
- isci_orom->ctrl[ctrl_idx].controller.do_enable_ssc =
- enable_ssc;
-
- for (port_idx = 0; port_idx < 4; port_idx++)
- isci_orom->ctrl[ctrl_idx].ports[port_idx].phy_mask =
- phy_mask[ctrl_idx][port_idx];
-
- for (phy_idx = 0; phy_idx < 4; phy_idx++) {
- isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.high =
- (__u32)(sas_addr[ctrl_idx][phy_idx] >> 32);
- isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.low =
- (__u32)(sas_addr[ctrl_idx][phy_idx]);
-
- isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control0 =
- afe_tx_amp_control0;
- isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control1 =
- afe_tx_amp_control1;
- isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control2 =
- afe_tx_amp_control2;
- isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control3 =
- afe_tx_amp_control3;
- }
- }
-}
-
-int main(void)
-{
- int err;
- struct isci_orom *isci_orom;
-
- isci_orom = malloc(sizeof(struct isci_orom));
- memset(isci_orom, 0, sizeof(struct isci_orom));
-
- set_binary_values(isci_orom);
-
- err = write_blob(isci_orom);
- if (err < 0) {
- free(isci_orom);
- return err;
- }
-
- free(isci_orom);
- return 0;
-}
diff --git a/drivers/scsi/isci/firmware/create_fw.h b/drivers/scsi/isci/firmware/create_fw.h
deleted file mode 100644
index 5f298828d22e..000000000000
--- a/drivers/scsi/isci/firmware/create_fw.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef _CREATE_FW_H_
-#define _CREATE_FW_H_
-#include "../probe_roms.h"
-
-
-/* we are configuring for 2 SCUs */
-static const int num_elements = 2;
-
-/*
- * For all defined arrays:
- * elements 0-3 are for SCU0, ports 0-3
- * elements 4-7 are for SCU1, ports 0-3
- *
- * valid configurations for one SCU are:
- * P0 P1 P2 P3
- * ----------------
- * 0xF,0x0,0x0,0x0 # 1 x4 port
- * 0x3,0x0,0x4,0x8 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are each x1
- * # ports
- * 0x1,0x2,0xC,0x0 # Phys 0 and 1 are each x1 ports, phy 2 and phy 3 are a x2
- * # port
- * 0x3,0x0,0xC,0x0 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are a x2 port
- * 0x1,0x2,0x4,0x8 # Each phy is a x1 port (this is the default configuration)
- *
- * if there is a port/phy on which you do not wish to override the default
- * values, use the value assigned to UNINIT_PARAM (255).
- */
-
-/* discovery mode type (port auto config mode by default ) */
-
-/*
- * if there is a port/phy on which you do not wish to override the default
- * values, use the value "0000000000000000". SAS address of zero's is
- * considered invalid and will not be used.
- */
-#ifdef MPC
-static const int mode_type = SCIC_PORT_MANUAL_CONFIGURATION_MODE;
-static const __u8 phy_mask[2][4] = { {1, 2, 4, 8},
- {1, 2, 4, 8} };
-static const unsigned long long sas_addr[2][4] = { { 0x5FCFFFFFF0000001ULL,
- 0x5FCFFFFFF0000002ULL,
- 0x5FCFFFFFF0000003ULL,
- 0x5FCFFFFFF0000004ULL },
- { 0x5FCFFFFFF0000005ULL,
- 0x5FCFFFFFF0000006ULL,
- 0x5FCFFFFFF0000007ULL,
- 0x5FCFFFFFF0000008ULL } };
-#else /* APC (default) */
-static const int mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE;
-static const __u8 phy_mask[2][4];
-static const unsigned long long sas_addr[2][4] = { { 0x5FCFFFFF00000001ULL,
- 0x5FCFFFFF00000001ULL,
- 0x5FCFFFFF00000001ULL,
- 0x5FCFFFFF00000001ULL },
- { 0x5FCFFFFF00000002ULL,
- 0x5FCFFFFF00000002ULL,
- 0x5FCFFFFF00000002ULL,
- 0x5FCFFFFF00000002ULL } };
-#endif
-
-/* Maximum number of concurrent device spin up */
-static const int max_num_concurrent_dev_spin_up = 1;
-
-/* enable of ssc operation */
-static const int enable_ssc;
-
-/* AFE_TX_AMP_CONTROL */
-static const unsigned int afe_tx_amp_control0 = 0x000bdd08;
-static const unsigned int afe_tx_amp_control1 = 0x000ffc00;
-static const unsigned int afe_tx_amp_control2 = 0x000b7c09;
-static const unsigned int afe_tx_amp_control3 = 0x000afc6e;
-
-static const char blob_name[] = "isci_firmware.bin";
-static const char sig[] = "ISCUOEMB";
-static const unsigned char version = 0x10;
-
-#endif
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 26072f1e9852..6d2f4c831df7 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -58,7 +58,6 @@
#include "host.h"
#include "isci.h"
#include "port.h"
-#include "host.h"
#include "probe_roms.h"
#include "remote_device.h"
#include "request.h"
@@ -90,16 +89,14 @@
#define SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT 100
-/**
- *
- *
+/*
* The number of milliseconds to wait while a given phy is consuming power
* before allowing another set of phys to consume power. Ultimately, this will
* be specified by OEM parameter.
*/
#define SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL 500
-/**
+/*
* NORMALIZE_PUT_POINTER() -
*
* This macro will normalize the completion queue put pointer so its value can
@@ -109,7 +106,7 @@
((x) & SMU_COMPLETION_QUEUE_PUT_POINTER_MASK)
-/**
+/*
* NORMALIZE_EVENT_POINTER() -
*
* This macro will normalize the completion queue event entry so its value can
@@ -121,7 +118,7 @@
>> SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_SHIFT \
)
-/**
+/*
* NORMALIZE_GET_POINTER() -
*
* This macro will normalize the completion queue get pointer so its value can
@@ -130,7 +127,7 @@
#define NORMALIZE_GET_POINTER(x) \
((x) & SMU_COMPLETION_QUEUE_GET_POINTER_MASK)
-/**
+/*
* NORMALIZE_GET_POINTER_CYCLE_BIT() -
*
* This macro will normalize the completion queue cycle pointer so it matches
@@ -139,7 +136,7 @@
#define NORMALIZE_GET_POINTER_CYCLE_BIT(x) \
((SMU_CQGR_CYCLE_BIT & (x)) << (31 - SMU_COMPLETION_QUEUE_GET_CYCLE_BIT_SHIFT))
-/**
+/*
* COMPLETION_QUEUE_CYCLE_BIT() -
*
* This macro will return the cycle bit of the completion queue entry
@@ -193,22 +190,27 @@ static bool sci_controller_completion_queue_has_entries(struct isci_host *ihost)
static bool sci_controller_isr(struct isci_host *ihost)
{
- if (sci_controller_completion_queue_has_entries(ihost)) {
+ if (sci_controller_completion_queue_has_entries(ihost))
return true;
- } else {
- /*
- * we have a spurious interrupt it could be that we have already
- * emptied the completion queue from a previous interrupt */
- writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
- /*
- * There is a race in the hardware that could cause us not to be notified
- * of an interrupt completion if we do not take this step. We will mask
- * then unmask the interrupts so if there is another interrupt pending
- * the clearing of the interrupt source we get the next interrupt message. */
+ /* we have a spurious interrupt it could be that we have already
+ * emptied the completion queue from a previous interrupt
+ * FIXME: really!?
+ */
+ writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status);
+
+ /* There is a race in the hardware that could cause us not to be
+ * notified of an interrupt completion if we do not take this
+ * step. We will mask then unmask the interrupts so if there is
+ * another interrupt pending the clearing of the interrupt
+ * source we get the next interrupt message.
+ */
+ spin_lock(&ihost->scic_lock);
+ if (test_bit(IHOST_IRQ_ENABLED, &ihost->flags)) {
writel(0xFF000000, &ihost->smu_registers->interrupt_mask);
writel(0, &ihost->smu_registers->interrupt_mask);
}
+ spin_unlock(&ihost->scic_lock);
return false;
}
@@ -411,7 +413,7 @@ static void sci_controller_event_completion(struct isci_host *ihost, u32 ent)
dev_warn(&ihost->pdev->dev,
"%s: SCIC Controller 0x%p received "
"event 0x%x for io request object "
- "that doesnt exist.\n",
+ "that doesn't exist.\n",
__func__,
ihost,
ent);
@@ -426,7 +428,7 @@ static void sci_controller_event_completion(struct isci_host *ihost, u32 ent)
dev_warn(&ihost->pdev->dev,
"%s: SCIC Controller 0x%p received "
"event 0x%x for remote device object "
- "that doesnt exist.\n",
+ "that doesn't exist.\n",
__func__,
ihost,
ent);
@@ -460,7 +462,7 @@ static void sci_controller_event_completion(struct isci_host *ihost, u32 ent)
} else
dev_err(&ihost->pdev->dev,
"%s: SCIC Controller 0x%p received event 0x%x "
- "for remote device object 0x%0x that doesnt "
+ "for remote device object 0x%0x that doesn't "
"exist.\n",
__func__,
ihost,
@@ -488,7 +490,7 @@ static void sci_controller_process_completions(struct isci_host *ihost)
u32 event_cycle;
dev_dbg(&ihost->pdev->dev,
- "%s: completion queue begining get:0x%08x\n",
+ "%s: completion queue beginning get:0x%08x\n",
__func__,
ihost->completion_queue_get);
@@ -531,6 +533,9 @@ static void sci_controller_process_completions(struct isci_host *ihost)
break;
case SCU_COMPLETION_TYPE_EVENT:
+ sci_controller_event_completion(ihost, ent);
+ break;
+
case SCU_COMPLETION_TYPE_NOTIFY: {
event_cycle ^= ((event_get+1) & SCU_MAX_EVENTS) <<
(SMU_COMPLETION_QUEUE_GET_EVENT_CYCLE_BIT_SHIFT - SCU_MAX_EVENTS_SHIFT);
@@ -630,7 +635,7 @@ irqreturn_t isci_error_isr(int vec, void *data)
/**
* isci_host_start_complete() - This function is called by the core library,
* through the ISCI Module, to indicate controller start status.
- * @isci_host: This parameter specifies the ISCI host object
+ * @ihost: This parameter specifies the ISCI host object
* @completion_status: This parameter specifies the completion status from the
* core library.
*
@@ -640,29 +645,21 @@ static void isci_host_start_complete(struct isci_host *ihost, enum sci_status co
if (completion_status != SCI_SUCCESS)
dev_info(&ihost->pdev->dev,
"controller start timed out, continuing...\n");
- isci_host_change_state(ihost, isci_ready);
clear_bit(IHOST_START_PENDING, &ihost->flags);
wake_up(&ihost->eventq);
}
int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
- struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha;
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct isci_host *ihost = ha->lldd_ha;
if (test_bit(IHOST_START_PENDING, &ihost->flags))
return 0;
- /* todo: use sas_flush_discovery once it is upstream */
- scsi_flush_work(shost);
-
- scsi_flush_work(shost);
-
- dev_dbg(&ihost->pdev->dev,
- "%s: ihost->status = %d, time = %ld\n",
- __func__, isci_host_get_state(ihost), time);
+ sas_drain_work(ha);
return 1;
-
}
/**
@@ -671,7 +668,7 @@ int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time)
* use any timeout value, but this method provides the suggested minimum
* start timeout value. The returned value is based upon empirical
* information determined as a result of interoperability testing.
- * @controller: the handle to the controller object for which to return the
+ * @ihost: the handle to the controller object for which to return the
* suggested start timeout.
*
* This method returns the number of milliseconds for the suggested start
@@ -704,14 +701,15 @@ static u32 sci_controller_get_suggested_start_timeout(struct isci_host *ihost)
static void sci_controller_enable_interrupts(struct isci_host *ihost)
{
- BUG_ON(ihost->smu_registers == NULL);
+ set_bit(IHOST_IRQ_ENABLED, &ihost->flags);
writel(0, &ihost->smu_registers->interrupt_mask);
}
void sci_controller_disable_interrupts(struct isci_host *ihost)
{
- BUG_ON(ihost->smu_registers == NULL);
+ clear_bit(IHOST_IRQ_ENABLED, &ihost->flags);
writel(0xffffffff, &ihost->smu_registers->interrupt_mask);
+ readl(&ihost->smu_registers->interrupt_mask); /* flush */
}
static void sci_controller_enable_port_task_scheduler(struct isci_host *ihost)
@@ -822,7 +820,7 @@ static void sci_controller_initialize_unsolicited_frame_queue(struct isci_host *
&ihost->scu_registers->sdma.unsolicited_frame_put_pointer);
}
-static void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status)
+void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status)
{
if (ihost->sm.current_state_id == SCIC_STARTING) {
/*
@@ -849,6 +847,7 @@ static bool is_phy_starting(struct isci_phy *iphy)
case SCI_PHY_SUB_AWAIT_SATA_POWER:
case SCI_PHY_SUB_AWAIT_SATA_PHY_EN:
case SCI_PHY_SUB_AWAIT_SATA_SPEED_EN:
+ case SCI_PHY_SUB_AWAIT_OSSP_EN:
case SCI_PHY_SUB_AWAIT_SIG_FIS_UF:
case SCI_PHY_SUB_FINAL:
return true;
@@ -857,9 +856,42 @@ static bool is_phy_starting(struct isci_phy *iphy)
}
}
+bool is_controller_start_complete(struct isci_host *ihost)
+{
+ int i;
+
+ for (i = 0; i < SCI_MAX_PHYS; i++) {
+ struct isci_phy *iphy = &ihost->phys[i];
+ u32 state = iphy->sm.current_state_id;
+
+ /* in apc mode we need to check every phy, in
+ * mpc mode we only need to check phys that have
+ * been configured into a port
+ */
+ if (is_port_config_apc(ihost))
+ /* pass */;
+ else if (!phy_get_non_dummy_port(iphy))
+ continue;
+
+ /* The controller start operation is complete iff:
+ * - all links have been given an opportunity to start
+ * - have no indication of a connected device
+ * - have an indication of a connected device and it has
+ * finished the link training process.
+ */
+ if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) ||
+ (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) ||
+ (iphy->is_in_link_training == true && is_phy_starting(iphy)) ||
+ (ihost->port_agent.phy_ready_mask != ihost->port_agent.phy_configured_mask))
+ return false;
+ }
+
+ return true;
+}
+
/**
* sci_controller_start_next_phy - start phy
- * @scic: controller
+ * @ihost: controller
*
* If all the phys have been started, then attempt to transition the
* controller to the READY state and inform the user
@@ -877,35 +909,7 @@ static enum sci_status sci_controller_start_next_phy(struct isci_host *ihost)
return status;
if (ihost->next_phy_to_start >= SCI_MAX_PHYS) {
- bool is_controller_start_complete = true;
- u32 state;
- u8 index;
-
- for (index = 0; index < SCI_MAX_PHYS; index++) {
- iphy = &ihost->phys[index];
- state = iphy->sm.current_state_id;
-
- if (!phy_get_non_dummy_port(iphy))
- continue;
-
- /* The controller start operation is complete iff:
- * - all links have been given an opportunity to start
- * - have no indication of a connected device
- * - have an indication of a connected device and it has
- * finished the link training process.
- */
- if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) ||
- (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) ||
- (iphy->is_in_link_training == true && is_phy_starting(iphy))) {
- is_controller_start_complete = false;
- break;
- }
- }
-
- /*
- * The controller has successfully finished the start process.
- * Inform the SCI Core user and transition to the READY state. */
- if (is_controller_start_complete == true) {
+ if (is_controller_start_complete(ihost)) {
sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
sci_del_timer(&ihost->phy_timer);
ihost->phy_startup_timer_pending = false;
@@ -952,9 +956,9 @@ static enum sci_status sci_controller_start_next_phy(struct isci_host *ihost)
return status;
}
-static void phy_startup_timeout(unsigned long data)
+static void phy_startup_timeout(struct timer_list *t)
{
- struct sci_timer *tmr = (struct sci_timer *)data;
+ struct sci_timer *tmr = timer_container_of(tmr, t, timer);
struct isci_host *ihost = container_of(tmr, typeof(*ihost), phy_timer);
unsigned long flags;
enum sci_status status;
@@ -986,9 +990,8 @@ static enum sci_status sci_controller_start(struct isci_host *ihost,
u16 index;
if (ihost->sm.current_state_id != SCIC_INITIALIZED) {
- dev_warn(&ihost->pdev->dev,
- "SCIC Controller start operation requested in "
- "invalid state\n");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
@@ -1039,7 +1042,7 @@ static enum sci_status sci_controller_start(struct isci_host *ihost,
return SCI_SUCCESS;
}
-void isci_host_scan_start(struct Scsi_Host *shost)
+void isci_host_start(struct Scsi_Host *shost)
{
struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha;
unsigned long tmo = sci_controller_get_suggested_start_timeout(ihost);
@@ -1052,9 +1055,8 @@ void isci_host_scan_start(struct Scsi_Host *shost)
spin_unlock_irq(&ihost->scic_lock);
}
-static void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status)
+static void isci_host_stop_complete(struct isci_host *ihost)
{
- isci_host_change_state(ihost, isci_stopped);
sci_controller_disable_interrupts(ihost);
clear_bit(IHOST_STOP_PENDING, &ihost->flags);
wake_up(&ihost->eventq);
@@ -1073,6 +1075,34 @@ static void sci_controller_completion_handler(struct isci_host *ihost)
writel(0, &ihost->smu_registers->interrupt_mask);
}
+void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task)
+{
+ if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags) &&
+ !(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (test_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags)) {
+ /* Normal notification (task_done) */
+ dev_dbg(&ihost->pdev->dev,
+ "%s: Normal - ireq/task = %p/%p\n",
+ __func__, ireq, task);
+ task->lldd_task = NULL;
+ task->task_done(task);
+ } else {
+ dev_dbg(&ihost->pdev->dev,
+ "%s: Error - ireq/task = %p/%p\n",
+ __func__, ireq, task);
+ if (sas_protocol_ata(task->task_proto))
+ task->lldd_task = NULL;
+ sas_task_abort(task);
+ }
+ } else
+ task->lldd_task = NULL;
+
+ if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
+ wake_up_all(&ihost->eventq);
+
+ if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags))
+ isci_free_tag(ihost, ireq->io_tag);
+}
/**
* isci_host_completion_routine() - This function is the delayed service
* routine that calls the sci core library's completion handler. It's
@@ -1081,106 +1111,28 @@ static void sci_controller_completion_handler(struct isci_host *ihost)
* @data: This parameter specifies the ISCI host object
*
*/
-static void isci_host_completion_routine(unsigned long data)
+void isci_host_completion_routine(unsigned long data)
{
struct isci_host *ihost = (struct isci_host *)data;
- struct list_head completed_request_list;
- struct list_head errored_request_list;
- struct list_head *current_position;
- struct list_head *next_position;
- struct isci_request *request;
- struct isci_request *next_request;
- struct sas_task *task;
-
- INIT_LIST_HEAD(&completed_request_list);
- INIT_LIST_HEAD(&errored_request_list);
+ u16 active;
spin_lock_irq(&ihost->scic_lock);
-
sci_controller_completion_handler(ihost);
-
- /* Take the lists of completed I/Os from the host. */
-
- list_splice_init(&ihost->requests_to_complete,
- &completed_request_list);
-
- /* Take the list of errored I/Os from the host. */
- list_splice_init(&ihost->requests_to_errorback,
- &errored_request_list);
-
spin_unlock_irq(&ihost->scic_lock);
- /* Process any completions in the lists. */
- list_for_each_safe(current_position, next_position,
- &completed_request_list) {
-
- request = list_entry(current_position, struct isci_request,
- completed_node);
- task = isci_request_access_task(request);
-
- /* Normal notification (task_done) */
- dev_dbg(&ihost->pdev->dev,
- "%s: Normal - request/task = %p/%p\n",
- __func__,
- request,
- task);
-
- /* Return the task to libsas */
- if (task != NULL) {
-
- task->lldd_task = NULL;
- if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
-
- /* If the task is already in the abort path,
- * the task_done callback cannot be called.
- */
- task->task_done(task);
- }
- }
-
- spin_lock_irq(&ihost->scic_lock);
- isci_free_tag(ihost, request->io_tag);
- spin_unlock_irq(&ihost->scic_lock);
- }
- list_for_each_entry_safe(request, next_request, &errored_request_list,
- completed_node) {
-
- task = isci_request_access_task(request);
-
- /* Use sas_task_abort */
- dev_warn(&ihost->pdev->dev,
- "%s: Error - request/task = %p/%p\n",
- __func__,
- request,
- task);
-
- if (task != NULL) {
-
- /* Put the task into the abort path if it's not there
- * already.
- */
- if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED))
- sas_task_abort(task);
-
- } else {
- /* This is a case where the request has completed with a
- * status such that it needed further target servicing,
- * but the sas_task reference has already been removed
- * from the request. Since it was errored, it was not
- * being aborted, so there is nothing to do except free
- * it.
- */
-
- spin_lock_irq(&ihost->scic_lock);
- /* Remove the request from the remote device's list
- * of pending requests.
- */
- list_del_init(&request->dev_node);
- isci_free_tag(ihost, request->io_tag);
- spin_unlock_irq(&ihost->scic_lock);
- }
- }
+ /*
+ * we subtract SCI_MAX_PORTS to account for the number of dummy TCs
+ * issued for hardware issue workaround
+ */
+ active = isci_tci_active(ihost) - SCI_MAX_PORTS;
+ /*
+ * the coalesence timeout doubles at each encoding step, so
+ * update it based on the ilog2 value of the outstanding requests
+ */
+ writel(SMU_ICC_GEN_VAL(NUMBER, active) |
+ SMU_ICC_GEN_VAL(TIMER, ISCI_COALESCE_BASE + ilog2(active)),
+ &ihost->smu_registers->interrupt_coalesce_control);
}
/**
@@ -1191,7 +1143,7 @@ static void isci_host_completion_routine(unsigned long data)
* controller has been quiesced. This method will ensure that all IO
* requests are quiesced, phys are stopped, and all additional operation by
* the hardware is halted.
- * @controller: the handle to the controller object to stop.
+ * @ihost: the handle to the controller object to stop.
* @timeout: This parameter specifies the number of milliseconds in which the
* stop operation should complete.
*
@@ -1204,9 +1156,8 @@ static void isci_host_completion_routine(unsigned long data)
static enum sci_status sci_controller_stop(struct isci_host *ihost, u32 timeout)
{
if (ihost->sm.current_state_id != SCIC_READY) {
- dev_warn(&ihost->pdev->dev,
- "SCIC Controller stop operation requested in "
- "invalid state\n");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
@@ -1221,7 +1172,7 @@ static enum sci_status sci_controller_stop(struct isci_host *ihost, u32 timeout)
* considered destructive. In other words, all current operations are wiped
* out. No IO completions for outstanding devices occur. Outstanding IO
* requests are not aborted or completed at the actual remote device.
- * @controller: the handle to the controller object to reset.
+ * @ihost: the handle to the controller object to reset.
*
* Indicate if the controller reset method succeeded or failed in some way.
* SCI_SUCCESS if the reset operation successfully started. SCI_FATAL_ERROR if
@@ -1232,7 +1183,7 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost)
switch (ihost->sm.current_state_id) {
case SCIC_RESET:
case SCIC_READY:
- case SCIC_STOPPED:
+ case SCIC_STOPPING:
case SCIC_FAILED:
/*
* The reset operation is not a graceful cleanup, just
@@ -1241,28 +1192,58 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost)
sci_change_state(&ihost->sm, SCIC_RESETTING);
return SCI_SUCCESS;
default:
- dev_warn(&ihost->pdev->dev,
- "SCIC Controller reset operation requested in "
- "invalid state\n");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
}
-void isci_host_deinit(struct isci_host *ihost)
+static enum sci_status sci_controller_stop_phys(struct isci_host *ihost)
{
- int i;
+ u32 index;
+ enum sci_status status;
+ enum sci_status phy_status;
- isci_host_change_state(ihost, isci_stopping);
- for (i = 0; i < SCI_MAX_PORTS; i++) {
- struct isci_port *iport = &ihost->ports[i];
- struct isci_remote_device *idev, *d;
+ status = SCI_SUCCESS;
+
+ for (index = 0; index < SCI_MAX_PHYS; index++) {
+ phy_status = sci_phy_stop(&ihost->phys[index]);
+
+ if (phy_status != SCI_SUCCESS &&
+ phy_status != SCI_FAILURE_INVALID_STATE) {
+ status = SCI_FAILURE;
- list_for_each_entry_safe(idev, d, &iport->remote_dev_list, node) {
- if (test_bit(IDEV_ALLOCATED, &idev->flags))
- isci_remote_device_stop(ihost, idev);
+ dev_warn(&ihost->pdev->dev,
+ "%s: Controller stop operation failed to stop "
+ "phy %d because of status %d.\n",
+ __func__,
+ ihost->phys[index].phy_index, phy_status);
}
}
+ return status;
+}
+
+
+/**
+ * isci_host_deinit - shutdown frame reception and dma
+ * @ihost: host to take down
+ *
+ * This is called in either the driver shutdown or the suspend path. In
+ * the shutdown case libsas went through port teardown and normal device
+ * removal (i.e. physical links stayed up to service scsi_device removal
+ * commands). In the suspend case we disable the hardware without
+ * notifying libsas of the link down events since we want libsas to
+ * remember the domain across the suspend/resume cycle
+ */
+void isci_host_deinit(struct isci_host *ihost)
+{
+ int i;
+
+ /* disable output data selects */
+ for (i = 0; i < isci_gpio_count(ihost); i++)
+ writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
+
set_bit(IHOST_STOP_PENDING, &ihost->flags);
spin_lock_irq(&ihost->scic_lock);
@@ -1270,27 +1251,42 @@ void isci_host_deinit(struct isci_host *ihost)
spin_unlock_irq(&ihost->scic_lock);
wait_for_stop(ihost);
+
+ /* phy stop is after controller stop to allow port and device to
+ * go idle before shutting down the phys, but the expectation is
+ * that i/o has been shut off well before we reach this
+ * function.
+ */
+ sci_controller_stop_phys(ihost);
+
+ /* disable sgpio: where the above wait should give time for the
+ * enclosure to sample the gpios going inactive
+ */
+ writel(0, &ihost->scu_registers->peg0.sgpio.interface_control);
+
+ spin_lock_irq(&ihost->scic_lock);
sci_controller_reset(ihost);
+ spin_unlock_irq(&ihost->scic_lock);
/* Cancel any/all outstanding port timers */
for (i = 0; i < ihost->logical_port_entries; i++) {
struct isci_port *iport = &ihost->ports[i];
- del_timer_sync(&iport->timer.timer);
+ timer_delete_sync(&iport->timer.timer);
}
/* Cancel any/all outstanding phy timers */
for (i = 0; i < SCI_MAX_PHYS; i++) {
struct isci_phy *iphy = &ihost->phys[i];
- del_timer_sync(&iphy->sata_timer.timer);
+ timer_delete_sync(&iphy->sata_timer.timer);
}
- del_timer_sync(&ihost->port_agent.timer.timer);
+ timer_delete_sync(&ihost->port_agent.timer.timer);
- del_timer_sync(&ihost->power_control.timer.timer);
+ timer_delete_sync(&ihost->power_control.timer.timer);
- del_timer_sync(&ihost->timer.timer);
+ timer_delete_sync(&ihost->timer.timer);
- del_timer_sync(&ihost->phy_timer.timer);
+ timer_delete_sync(&ihost->phy_timer.timer);
}
static void __iomem *scu_base(struct isci_host *isci_host)
@@ -1309,29 +1305,6 @@ static void __iomem *smu_base(struct isci_host *isci_host)
return pcim_iomap_table(pdev)[SCI_SMU_BAR * 2] + SCI_SMU_BAR_SIZE * id;
}
-static void isci_user_parameters_get(struct sci_user_parameters *u)
-{
- int i;
-
- for (i = 0; i < SCI_MAX_PHYS; i++) {
- struct sci_phy_user_params *u_phy = &u->phys[i];
-
- u_phy->max_speed_generation = phy_gen;
-
- /* we are not exporting these for now */
- u_phy->align_insertion_frequency = 0x7f;
- u_phy->in_connection_align_insertion_frequency = 0xff;
- u_phy->notify_enable_spin_up_insertion_frequency = 0x33;
- }
-
- u->stp_inactivity_timeout = stp_inactive_to;
- u->ssp_inactivity_timeout = ssp_inactive_to;
- u->stp_max_occupancy_timeout = stp_max_occ_to;
- u->ssp_max_occupancy_timeout = ssp_max_occ_to;
- u->no_outbound_task_timeout = no_outbound_task_to;
- u->max_number_concurrent_device_spin_up = max_concurr_spinup;
-}
-
static void sci_controller_initial_state_enter(struct sci_base_state_machine *sm)
{
struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
@@ -1356,7 +1329,7 @@ static inline void sci_controller_starting_state_exit(struct sci_base_state_mach
/**
* sci_controller_set_interrupt_coalescence() - This method allows the user to
* configure the interrupt coalescence.
- * @controller: This parameter represents the handle to the controller object
+ * @ihost: This parameter represents the handle to the controller object
* for which its interrupt coalesce register is overridden.
* @coalesce_number: Used to control the number of entries in the Completion
* Queue before an interrupt is generated. If the number of entries exceed
@@ -1469,9 +1442,18 @@ sci_controller_set_interrupt_coalescence(struct isci_host *ihost,
static void sci_controller_ready_state_enter(struct sci_base_state_machine *sm)
{
struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
+ u32 val;
+
+ /* enable clock gating for power control of the scu unit */
+ val = readl(&ihost->smu_registers->clock_gating_control);
+ val &= ~(SMU_CGUCR_GEN_BIT(REGCLK_ENABLE) |
+ SMU_CGUCR_GEN_BIT(TXCLK_ENABLE) |
+ SMU_CGUCR_GEN_BIT(XCLK_ENABLE));
+ val |= SMU_CGUCR_GEN_BIT(IDLE_ENABLE);
+ writel(val, &ihost->smu_registers->clock_gating_control);
/* set the default interrupt coalescence number and timeout value. */
- sci_controller_set_interrupt_coalescence(ihost, 0x10, 250);
+ sci_controller_set_interrupt_coalescence(ihost, 0, 0);
}
static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm)
@@ -1482,32 +1464,6 @@ static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm)
sci_controller_set_interrupt_coalescence(ihost, 0, 0);
}
-static enum sci_status sci_controller_stop_phys(struct isci_host *ihost)
-{
- u32 index;
- enum sci_status status;
- enum sci_status phy_status;
-
- status = SCI_SUCCESS;
-
- for (index = 0; index < SCI_MAX_PHYS; index++) {
- phy_status = sci_phy_stop(&ihost->phys[index]);
-
- if (phy_status != SCI_SUCCESS &&
- phy_status != SCI_FAILURE_INVALID_STATE) {
- status = SCI_FAILURE;
-
- dev_warn(&ihost->pdev->dev,
- "%s: Controller stop operation failed to stop "
- "phy %d because of status %d.\n",
- __func__,
- ihost->phys[index].phy_index, phy_status);
- }
- }
-
- return status;
-}
-
static enum sci_status sci_controller_stop_ports(struct isci_host *ihost)
{
u32 index;
@@ -1567,10 +1523,11 @@ static void sci_controller_stopping_state_enter(struct sci_base_state_machine *s
{
struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
- /* Stop all of the components for this controller */
- sci_controller_stop_phys(ihost);
- sci_controller_stop_ports(ihost);
sci_controller_stop_devices(ihost);
+ sci_controller_stop_ports(ihost);
+
+ if (!sci_controller_has_remote_devices_stopping(ihost))
+ isci_host_stop_complete(ihost);
}
static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm)
@@ -1596,6 +1553,9 @@ static void sci_controller_reset_hardware(struct isci_host *ihost)
/* The write to the UFQGP clears the UFQPR */
writel(0, &ihost->scu_registers->sdma.unsolicited_frame_get_pointer);
+
+ /* clear all interrupts */
+ writel(~SMU_INTERRUPT_STATUS_RESERVED_MASK, &ihost->smu_registers->interrupt_status);
}
static void sci_controller_resetting_state_enter(struct sci_base_state_machine *sm)
@@ -1627,58 +1587,12 @@ static const struct sci_base_state sci_controller_state_table[] = {
.enter_state = sci_controller_stopping_state_enter,
.exit_state = sci_controller_stopping_state_exit,
},
- [SCIC_STOPPED] = {},
[SCIC_FAILED] = {}
};
-static void sci_controller_set_default_config_parameters(struct isci_host *ihost)
-{
- /* these defaults are overridden by the platform / firmware */
- u16 index;
-
- /* Default to APC mode. */
- ihost->oem_parameters.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE;
-
- /* Default to APC mode. */
- ihost->oem_parameters.controller.max_concurrent_dev_spin_up = 1;
-
- /* Default to no SSC operation. */
- ihost->oem_parameters.controller.do_enable_ssc = false;
-
- /* Initialize all of the port parameter information to narrow ports. */
- for (index = 0; index < SCI_MAX_PORTS; index++) {
- ihost->oem_parameters.ports[index].phy_mask = 0;
- }
-
- /* Initialize all of the phy parameter information. */
- for (index = 0; index < SCI_MAX_PHYS; index++) {
- /* Default to 6G (i.e. Gen 3) for now. */
- ihost->user_parameters.phys[index].max_speed_generation = 3;
-
- /* the frequencies cannot be 0 */
- ihost->user_parameters.phys[index].align_insertion_frequency = 0x7f;
- ihost->user_parameters.phys[index].in_connection_align_insertion_frequency = 0xff;
- ihost->user_parameters.phys[index].notify_enable_spin_up_insertion_frequency = 0x33;
-
- /*
- * Previous Vitesse based expanders had a arbitration issue that
- * is worked around by having the upper 32-bits of SAS address
- * with a value greater then the Vitesse company identifier.
- * Hence, usage of 0x5FCFFFFF. */
- ihost->oem_parameters.phys[index].sas_address.low = 0x1 + ihost->id;
- ihost->oem_parameters.phys[index].sas_address.high = 0x5FCFFFFF;
- }
-
- ihost->user_parameters.stp_inactivity_timeout = 5;
- ihost->user_parameters.ssp_inactivity_timeout = 5;
- ihost->user_parameters.stp_max_occupancy_timeout = 5;
- ihost->user_parameters.ssp_max_occupancy_timeout = 20;
- ihost->user_parameters.no_outbound_task_timeout = 20;
-}
-
-static void controller_timeout(unsigned long data)
+static void controller_timeout(struct timer_list *t)
{
- struct sci_timer *tmr = (struct sci_timer *)data;
+ struct sci_timer *tmr = timer_container_of(tmr, t, timer);
struct isci_host *ihost = container_of(tmr, typeof(*ihost), timer);
struct sci_base_state_machine *sm = &ihost->sm;
unsigned long flags;
@@ -1692,7 +1606,7 @@ static void controller_timeout(unsigned long data)
sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT);
else if (sm->current_state_id == SCIC_STOPPING) {
sci_change_state(sm, SCIC_FAILED);
- isci_host_stop_complete(ihost, SCI_FAILURE_TIMEOUT);
+ isci_host_stop_complete(ihost);
} else /* / @todo Now what do we want to do in this case? */
dev_err(&ihost->pdev->dev,
"%s: Controller timer fired when controller was not "
@@ -1732,13 +1646,10 @@ static enum sci_status sci_controller_construct(struct isci_host *ihost,
sci_init_timer(&ihost->timer, controller_timeout);
- /* Initialize the User and OEM parameters to default values. */
- sci_controller_set_default_config_parameters(ihost);
-
return sci_controller_reset(ihost);
}
-int sci_oem_parameters_validate(struct sci_oem_params *oem)
+int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version)
{
int i;
@@ -1766,32 +1677,67 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem)
} else
return -EINVAL;
- if (oem->controller.max_concurrent_dev_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT)
+ if (oem->controller.max_concurr_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT ||
+ oem->controller.max_concurr_spin_up < 1)
return -EINVAL;
- return 0;
-}
-
-static enum sci_status sci_oem_parameters_set(struct isci_host *ihost)
-{
- u32 state = ihost->sm.current_state_id;
+ if (oem->controller.do_enable_ssc) {
+ if (version < ISCI_ROM_VER_1_1 && oem->controller.do_enable_ssc != 1)
+ return -EINVAL;
- if (state == SCIC_RESET ||
- state == SCIC_INITIALIZING ||
- state == SCIC_INITIALIZED) {
+ if (version >= ISCI_ROM_VER_1_1) {
+ u8 test = oem->controller.ssc_sata_tx_spread_level;
- if (sci_oem_parameters_validate(&ihost->oem_parameters))
- return SCI_FAILURE_INVALID_PARAMETER_VALUE;
+ switch (test) {
+ case 0:
+ case 2:
+ case 3:
+ case 6:
+ case 7:
+ break;
+ default:
+ return -EINVAL;
+ }
- return SCI_SUCCESS;
+ test = oem->controller.ssc_sas_tx_spread_level;
+ if (oem->controller.ssc_sas_tx_type == 0) {
+ switch (test) {
+ case 0:
+ case 2:
+ case 3:
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (oem->controller.ssc_sas_tx_type == 1) {
+ switch (test) {
+ case 0:
+ case 3:
+ case 6:
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ }
}
- return SCI_FAILURE_INVALID_STATE;
+ return 0;
+}
+
+static u8 max_spin_up(struct isci_host *ihost)
+{
+ if (ihost->user_parameters.max_concurr_spinup)
+ return min_t(u8, ihost->user_parameters.max_concurr_spinup,
+ MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT);
+ else
+ return min_t(u8, ihost->oem_parameters.controller.max_concurr_spin_up,
+ MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT);
}
-static void power_control_timeout(unsigned long data)
+static void power_control_timeout(struct timer_list *t)
{
- struct sci_timer *tmr = (struct sci_timer *)data;
+ struct sci_timer *tmr = timer_container_of(tmr, t, timer);
struct isci_host *ihost = container_of(tmr, typeof(*ihost), power_control.timer);
struct isci_phy *iphy;
unsigned long flags;
@@ -1818,14 +1764,38 @@ static void power_control_timeout(unsigned long data)
if (iphy == NULL)
continue;
- if (ihost->power_control.phys_granted_power >=
- ihost->oem_parameters.controller.max_concurrent_dev_spin_up)
+ if (ihost->power_control.phys_granted_power >= max_spin_up(ihost))
break;
ihost->power_control.requesters[i] = NULL;
ihost->power_control.phys_waiting--;
ihost->power_control.phys_granted_power++;
sci_phy_consume_power_handler(iphy);
+
+ if (iphy->protocol == SAS_PROTOCOL_SSP) {
+ u8 j;
+
+ for (j = 0; j < SCI_MAX_PHYS; j++) {
+ struct isci_phy *requester = ihost->power_control.requesters[j];
+
+ /*
+ * Search the power_control queue to see if there are other phys
+ * attached to the same remote device. If found, take all of
+ * them out of await_sas_power state.
+ */
+ if (requester != NULL && requester != iphy) {
+ u8 other = memcmp(requester->frame_rcvd.iaf.sas_addr,
+ iphy->frame_rcvd.iaf.sas_addr,
+ sizeof(requester->frame_rcvd.iaf.sas_addr));
+
+ if (other == 0) {
+ ihost->power_control.requesters[j] = NULL;
+ ihost->power_control.phys_waiting--;
+ sci_phy_consume_power_handler(requester);
+ }
+ }
+ }
+ }
}
/*
@@ -1844,8 +1814,7 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost,
{
BUG_ON(iphy == NULL);
- if (ihost->power_control.phys_granted_power <
- ihost->oem_parameters.controller.max_concurrent_dev_spin_up) {
+ if (ihost->power_control.phys_granted_power < max_spin_up(ihost)) {
ihost->power_control.phys_granted_power++;
sci_phy_consume_power_handler(iphy);
@@ -1861,9 +1830,34 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost,
ihost->power_control.timer_started = true;
} else {
- /* Add the phy in the waiting list */
- ihost->power_control.requesters[iphy->phy_index] = iphy;
- ihost->power_control.phys_waiting++;
+ /*
+ * There are phys, attached to the same sas address as this phy, are
+ * already in READY state, this phy don't need wait.
+ */
+ u8 i;
+ struct isci_phy *current_phy;
+
+ for (i = 0; i < SCI_MAX_PHYS; i++) {
+ u8 other;
+ current_phy = &ihost->phys[i];
+
+ other = memcmp(current_phy->frame_rcvd.iaf.sas_addr,
+ iphy->frame_rcvd.iaf.sas_addr,
+ sizeof(current_phy->frame_rcvd.iaf.sas_addr));
+
+ if (current_phy->sm.current_state_id == SCI_PHY_READY &&
+ current_phy->protocol == SAS_PROTOCOL_SSP &&
+ other == 0) {
+ sci_phy_consume_power_handler(iphy);
+ break;
+ }
+ }
+
+ if (i == SCI_MAX_PHYS) {
+ /* Add the phy in the waiting list */
+ ihost->power_control.requesters[iphy->phy_index] = iphy;
+ ihost->power_control.phys_waiting++;
+ }
}
}
@@ -1878,162 +1872,250 @@ void sci_controller_power_control_queue_remove(struct isci_host *ihost,
ihost->power_control.requesters[iphy->phy_index] = NULL;
}
+static int is_long_cable(int phy, unsigned char selection_byte)
+{
+ return !!(selection_byte & (1 << phy));
+}
+
+static int is_medium_cable(int phy, unsigned char selection_byte)
+{
+ return !!(selection_byte & (1 << (phy + 4)));
+}
+
+static enum cable_selections decode_selection_byte(
+ int phy,
+ unsigned char selection_byte)
+{
+ return ((selection_byte & (1 << phy)) ? 1 : 0)
+ + (selection_byte & (1 << (phy + 4)) ? 2 : 0);
+}
+
+static unsigned char *to_cable_select(struct isci_host *ihost)
+{
+ if (is_cable_select_overridden())
+ return ((unsigned char *)&cable_selection_override)
+ + ihost->id;
+ else
+ return &ihost->oem_parameters.controller.cable_selection_mask;
+}
+
+enum cable_selections decode_cable_selection(struct isci_host *ihost, int phy)
+{
+ return decode_selection_byte(phy, *to_cable_select(ihost));
+}
+
+char *lookup_cable_names(enum cable_selections selection)
+{
+ static char *cable_names[] = {
+ [short_cable] = "short",
+ [long_cable] = "long",
+ [medium_cable] = "medium",
+ [undefined_cable] = "<undefined, assumed long>" /* bit 0==1 */
+ };
+ return (selection <= undefined_cable) ? cable_names[selection]
+ : cable_names[undefined_cable];
+}
+
#define AFE_REGISTER_WRITE_DELAY 10
-/* Initialize the AFE for this phy index. We need to read the AFE setup from
- * the OEM parameters
- */
static void sci_controller_afe_initialization(struct isci_host *ihost)
{
+ struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe;
const struct sci_oem_params *oem = &ihost->oem_parameters;
struct pci_dev *pdev = ihost->pdev;
u32 afe_status;
u32 phy_id;
+ unsigned char cable_selection_mask = *to_cable_select(ihost);
/* Clear DFX Status registers */
- writel(0x0081000f, &ihost->scu_registers->afe.afe_dfx_master_control0);
+ writel(0x0081000f, &afe->afe_dfx_master_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
- if (is_b0(pdev)) {
+ if (is_b0(pdev) || is_c0(pdev) || is_c1(pdev)) {
/* PM Rx Equalization Save, PM SPhy Rx Acknowledgement
- * Timer, PM Stagger Timer */
- writel(0x0007BFFF, &ihost->scu_registers->afe.afe_pmsn_master_control2);
+ * Timer, PM Stagger Timer
+ */
+ writel(0x0007FFFF, &afe->afe_pmsn_master_control2);
udelay(AFE_REGISTER_WRITE_DELAY);
}
/* Configure bias currents to normal */
if (is_a2(pdev))
- writel(0x00005A00, &ihost->scu_registers->afe.afe_bias_control);
+ writel(0x00005A00, &afe->afe_bias_control);
else if (is_b0(pdev) || is_c0(pdev))
- writel(0x00005F00, &ihost->scu_registers->afe.afe_bias_control);
+ writel(0x00005F00, &afe->afe_bias_control);
+ else if (is_c1(pdev))
+ writel(0x00005500, &afe->afe_bias_control);
udelay(AFE_REGISTER_WRITE_DELAY);
/* Enable PLL */
- if (is_b0(pdev) || is_c0(pdev))
- writel(0x80040A08, &ihost->scu_registers->afe.afe_pll_control0);
- else
- writel(0x80040908, &ihost->scu_registers->afe.afe_pll_control0);
+ if (is_a2(pdev))
+ writel(0x80040908, &afe->afe_pll_control0);
+ else if (is_b0(pdev) || is_c0(pdev))
+ writel(0x80040A08, &afe->afe_pll_control0);
+ else if (is_c1(pdev)) {
+ writel(0x80000B08, &afe->afe_pll_control0);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+ writel(0x00000B08, &afe->afe_pll_control0);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+ writel(0x80000B08, &afe->afe_pll_control0);
+ }
udelay(AFE_REGISTER_WRITE_DELAY);
/* Wait for the PLL to lock */
do {
- afe_status = readl(&ihost->scu_registers->afe.afe_common_block_status);
+ afe_status = readl(&afe->afe_common_block_status);
udelay(AFE_REGISTER_WRITE_DELAY);
} while ((afe_status & 0x00001000) == 0);
if (is_a2(pdev)) {
- /* Shorten SAS SNW lock time (RxLock timer value from 76 us to 50 us) */
- writel(0x7bcc96ad, &ihost->scu_registers->afe.afe_pmsn_master_control0);
+ /* Shorten SAS SNW lock time (RxLock timer value from 76
+ * us to 50 us)
+ */
+ writel(0x7bcc96ad, &afe->afe_pmsn_master_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
}
for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) {
+ struct scu_afe_transceiver __iomem *xcvr = &afe->scu_afe_xcvr[phy_id];
const struct sci_phy_oem_params *oem_phy = &oem->phys[phy_id];
+ int cable_length_long =
+ is_long_cable(phy_id, cable_selection_mask);
+ int cable_length_medium =
+ is_medium_cable(phy_id, cable_selection_mask);
- if (is_b0(pdev)) {
- /* Configure transmitter SSC parameters */
- writel(0x00030000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_ssc_control);
+ if (is_a2(pdev)) {
+ /* All defaults, except the Receive Word
+ * Alignament/Comma Detect Enable....(0xe800)
+ */
+ writel(0x00004512, &xcvr->afe_xcvr_control0);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+
+ writel(0x0050100F, &xcvr->afe_xcvr_control1);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+ } else if (is_b0(pdev)) {
+ /* Configure transmitter SSC parameters */
+ writel(0x00030000, &xcvr->afe_tx_ssc_control);
udelay(AFE_REGISTER_WRITE_DELAY);
} else if (is_c0(pdev)) {
- /* Configure transmitter SSC parameters */
- writel(0x0003000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_ssc_control);
+ /* Configure transmitter SSC parameters */
+ writel(0x00010202, &xcvr->afe_tx_ssc_control);
udelay(AFE_REGISTER_WRITE_DELAY);
- /*
- * All defaults, except the Receive Word Alignament/Comma Detect
- * Enable....(0xe800) */
- writel(0x00004500, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0);
+ /* All defaults, except the Receive Word
+ * Alignament/Comma Detect Enable....(0xe800)
+ */
+ writel(0x00014500, &xcvr->afe_xcvr_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
- } else {
- /*
- * All defaults, except the Receive Word Alignament/Comma Detect
- * Enable....(0xe800) */
- writel(0x00004512, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0);
+ } else if (is_c1(pdev)) {
+ /* Configure transmitter SSC parameters */
+ writel(0x00010202, &xcvr->afe_tx_ssc_control);
udelay(AFE_REGISTER_WRITE_DELAY);
- writel(0x0050100F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control1);
+ /* All defaults, except the Receive Word
+ * Alignament/Comma Detect Enable....(0xe800)
+ */
+ writel(0x0001C500, &xcvr->afe_xcvr_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
}
- /*
- * Power up TX and RX out from power down (PWRDNTX and PWRDNRX)
- * & increase TX int & ext bias 20%....(0xe85c) */
+ /* Power up TX and RX out from power down (PWRDNTX and
+ * PWRDNRX) & increase TX int & ext bias 20%....(0xe85c)
+ */
if (is_a2(pdev))
- writel(0x000003F0, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
+ writel(0x000003F0, &xcvr->afe_channel_control);
else if (is_b0(pdev)) {
- /* Power down TX and RX (PWRDNTX and PWRDNRX) */
- writel(0x000003D7, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
+ writel(0x000003D7, &xcvr->afe_channel_control);
udelay(AFE_REGISTER_WRITE_DELAY);
- /*
- * Power up TX and RX out from power down (PWRDNTX and PWRDNRX)
- * & increase TX int & ext bias 20%....(0xe85c) */
- writel(0x000003D4, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
- } else {
- writel(0x000001E7, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
+ writel(0x000003D4, &xcvr->afe_channel_control);
+ } else if (is_c0(pdev)) {
+ writel(0x000001E7, &xcvr->afe_channel_control);
udelay(AFE_REGISTER_WRITE_DELAY);
- /*
- * Power up TX and RX out from power down (PWRDNTX and PWRDNRX)
- * & increase TX int & ext bias 20%....(0xe85c) */
- writel(0x000001E4, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control);
+ writel(0x000001E4, &xcvr->afe_channel_control);
+ } else if (is_c1(pdev)) {
+ writel(cable_length_long ? 0x000002F7 : 0x000001F7,
+ &xcvr->afe_channel_control);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+
+ writel(cable_length_long ? 0x000002F4 : 0x000001F4,
+ &xcvr->afe_channel_control);
}
udelay(AFE_REGISTER_WRITE_DELAY);
if (is_a2(pdev)) {
/* Enable TX equalization (0xe824) */
- writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control);
+ writel(0x00040000, &xcvr->afe_tx_control);
udelay(AFE_REGISTER_WRITE_DELAY);
}
- /*
- * RDPI=0x0(RX Power On), RXOOBDETPDNC=0x0, TPD=0x0(TX Power On),
- * RDD=0x0(RX Detect Enabled) ....(0xe800) */
- writel(0x00004100, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0);
+ if (is_a2(pdev) || is_b0(pdev))
+ /* RDPI=0x0(RX Power On), RXOOBDETPDNC=0x0,
+ * TPD=0x0(TX Power On), RDD=0x0(RX Detect
+ * Enabled) ....(0xe800)
+ */
+ writel(0x00004100, &xcvr->afe_xcvr_control0);
+ else if (is_c0(pdev))
+ writel(0x00014100, &xcvr->afe_xcvr_control0);
+ else if (is_c1(pdev))
+ writel(0x0001C100, &xcvr->afe_xcvr_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
/* Leave DFE/FFE on */
if (is_a2(pdev))
- writel(0x3F11103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0);
+ writel(0x3F11103F, &xcvr->afe_rx_ssc_control0);
else if (is_b0(pdev)) {
- writel(0x3F11103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0);
+ writel(0x3F11103F, &xcvr->afe_rx_ssc_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
/* Enable TX equalization (0xe824) */
- writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control);
- } else {
- writel(0x0140DF0F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control1);
+ writel(0x00040000, &xcvr->afe_tx_control);
+ } else if (is_c0(pdev)) {
+ writel(0x01400C0F, &xcvr->afe_rx_ssc_control1);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+
+ writel(0x3F6F103F, &xcvr->afe_rx_ssc_control0);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+
+ /* Enable TX equalization (0xe824) */
+ writel(0x00040000, &xcvr->afe_tx_control);
+ } else if (is_c1(pdev)) {
+ writel(cable_length_long ? 0x01500C0C :
+ cable_length_medium ? 0x01400C0D : 0x02400C0D,
+ &xcvr->afe_xcvr_control1);
+ udelay(AFE_REGISTER_WRITE_DELAY);
+
+ writel(0x000003E0, &xcvr->afe_dfx_rx_control1);
udelay(AFE_REGISTER_WRITE_DELAY);
- writel(0x3F6F103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0);
+ writel(cable_length_long ? 0x33091C1F :
+ cable_length_medium ? 0x3315181F : 0x2B17161F,
+ &xcvr->afe_rx_ssc_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
/* Enable TX equalization (0xe824) */
- writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control);
+ writel(0x00040000, &xcvr->afe_tx_control);
}
udelay(AFE_REGISTER_WRITE_DELAY);
- writel(oem_phy->afe_tx_amp_control0,
- &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control0);
+ writel(oem_phy->afe_tx_amp_control0, &xcvr->afe_tx_amp_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
- writel(oem_phy->afe_tx_amp_control1,
- &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control1);
+ writel(oem_phy->afe_tx_amp_control1, &xcvr->afe_tx_amp_control1);
udelay(AFE_REGISTER_WRITE_DELAY);
- writel(oem_phy->afe_tx_amp_control2,
- &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control2);
+ writel(oem_phy->afe_tx_amp_control2, &xcvr->afe_tx_amp_control2);
udelay(AFE_REGISTER_WRITE_DELAY);
- writel(oem_phy->afe_tx_amp_control3,
- &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control3);
+ writel(oem_phy->afe_tx_amp_control3, &xcvr->afe_tx_amp_control3);
udelay(AFE_REGISTER_WRITE_DELAY);
}
/* Transfer control to the PEs */
- writel(0x00010f00, &ihost->scu_registers->afe.afe_dfx_master_control0);
+ writel(0x00010f00, &afe->afe_dfx_master_control0);
udelay(AFE_REGISTER_WRITE_DELAY);
}
@@ -2055,9 +2137,8 @@ static enum sci_status sci_controller_initialize(struct isci_host *ihost)
unsigned long i, state, val;
if (ihost->sm.current_state_id != SCIC_RESET) {
- dev_warn(&ihost->pdev->dev,
- "SCIC Controller initialize operation requested "
- "in invalid state\n");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
@@ -2160,96 +2241,76 @@ static enum sci_status sci_controller_initialize(struct isci_host *ihost)
return result;
}
-static enum sci_status sci_user_parameters_set(struct isci_host *ihost,
- struct sci_user_parameters *sci_parms)
-{
- u32 state = ihost->sm.current_state_id;
-
- if (state == SCIC_RESET ||
- state == SCIC_INITIALIZING ||
- state == SCIC_INITIALIZED) {
- u16 index;
-
- /*
- * Validate the user parameters. If they are not legal, then
- * return a failure.
- */
- for (index = 0; index < SCI_MAX_PHYS; index++) {
- struct sci_phy_user_params *user_phy;
-
- user_phy = &sci_parms->phys[index];
-
- if (!((user_phy->max_speed_generation <=
- SCIC_SDS_PARM_MAX_SPEED) &&
- (user_phy->max_speed_generation >
- SCIC_SDS_PARM_NO_SPEED)))
- return SCI_FAILURE_INVALID_PARAMETER_VALUE;
-
- if (user_phy->in_connection_align_insertion_frequency <
- 3)
- return SCI_FAILURE_INVALID_PARAMETER_VALUE;
-
- if ((user_phy->in_connection_align_insertion_frequency <
- 3) ||
- (user_phy->align_insertion_frequency == 0) ||
- (user_phy->
- notify_enable_spin_up_insertion_frequency ==
- 0))
- return SCI_FAILURE_INVALID_PARAMETER_VALUE;
- }
-
- if ((sci_parms->stp_inactivity_timeout == 0) ||
- (sci_parms->ssp_inactivity_timeout == 0) ||
- (sci_parms->stp_max_occupancy_timeout == 0) ||
- (sci_parms->ssp_max_occupancy_timeout == 0) ||
- (sci_parms->no_outbound_task_timeout == 0))
- return SCI_FAILURE_INVALID_PARAMETER_VALUE;
-
- memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms));
-
- return SCI_SUCCESS;
- }
-
- return SCI_FAILURE_INVALID_STATE;
-}
-
-static int sci_controller_mem_init(struct isci_host *ihost)
+static int sci_controller_dma_alloc(struct isci_host *ihost)
{
struct device *dev = &ihost->pdev->dev;
- dma_addr_t dma;
size_t size;
- int err;
+ int i;
+
+ /* detect re-initialization */
+ if (ihost->completion_queue)
+ return 0;
size = SCU_MAX_COMPLETION_QUEUE_ENTRIES * sizeof(u32);
- ihost->completion_queue = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL);
+ ihost->completion_queue = dmam_alloc_coherent(dev, size, &ihost->cq_dma,
+ GFP_KERNEL);
if (!ihost->completion_queue)
return -ENOMEM;
- writel(lower_32_bits(dma), &ihost->smu_registers->completion_queue_lower);
- writel(upper_32_bits(dma), &ihost->smu_registers->completion_queue_upper);
-
size = ihost->remote_node_entries * sizeof(union scu_remote_node_context);
- ihost->remote_node_context_table = dmam_alloc_coherent(dev, size, &dma,
+ ihost->remote_node_context_table = dmam_alloc_coherent(dev, size, &ihost->rnc_dma,
GFP_KERNEL);
+
if (!ihost->remote_node_context_table)
return -ENOMEM;
- writel(lower_32_bits(dma), &ihost->smu_registers->remote_node_context_lower);
- writel(upper_32_bits(dma), &ihost->smu_registers->remote_node_context_upper);
-
size = ihost->task_context_entries * sizeof(struct scu_task_context),
- ihost->task_context_table = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL);
+ ihost->task_context_table = dmam_alloc_coherent(dev, size, &ihost->tc_dma,
+ GFP_KERNEL);
if (!ihost->task_context_table)
return -ENOMEM;
- ihost->task_context_dma = dma;
- writel(lower_32_bits(dma), &ihost->smu_registers->host_task_table_lower);
- writel(upper_32_bits(dma), &ihost->smu_registers->host_task_table_upper);
+ size = SCI_UFI_TOTAL_SIZE;
+ ihost->ufi_buf = dmam_alloc_coherent(dev, size, &ihost->ufi_dma, GFP_KERNEL);
+ if (!ihost->ufi_buf)
+ return -ENOMEM;
+
+ for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) {
+ struct isci_request *ireq;
+ dma_addr_t dma;
+
+ ireq = dmam_alloc_coherent(dev, sizeof(*ireq), &dma, GFP_KERNEL);
+ if (!ireq)
+ return -ENOMEM;
+
+ ireq->tc = &ihost->task_context_table[i];
+ ireq->owning_controller = ihost;
+ ireq->request_daddr = dma;
+ ireq->isci_host = ihost;
+ ihost->reqs[i] = ireq;
+ }
+
+ return 0;
+}
+
+static int sci_controller_mem_init(struct isci_host *ihost)
+{
+ int err = sci_controller_dma_alloc(ihost);
- err = sci_unsolicited_frame_control_construct(ihost);
if (err)
return err;
+ writel(lower_32_bits(ihost->cq_dma), &ihost->smu_registers->completion_queue_lower);
+ writel(upper_32_bits(ihost->cq_dma), &ihost->smu_registers->completion_queue_upper);
+
+ writel(lower_32_bits(ihost->rnc_dma), &ihost->smu_registers->remote_node_context_lower);
+ writel(upper_32_bits(ihost->rnc_dma), &ihost->smu_registers->remote_node_context_upper);
+
+ writel(lower_32_bits(ihost->tc_dma), &ihost->smu_registers->host_task_table_lower);
+ writel(upper_32_bits(ihost->tc_dma), &ihost->smu_registers->host_task_table_upper);
+
+ sci_unsolicited_frame_control_construct(ihost);
+
/*
* Inform the silicon as to the location of the UF headers and
* address table.
@@ -2267,22 +2328,22 @@ static int sci_controller_mem_init(struct isci_host *ihost)
return 0;
}
+/**
+ * isci_host_init - (re-)initialize hardware and internal (private) state
+ * @ihost: host to init
+ *
+ * Any public facing objects (like asd_sas_port, and asd_sas_phys), or
+ * one-time initialization objects like locks and waitqueues, are
+ * not touched (they are initialized in isci_host_alloc)
+ */
int isci_host_init(struct isci_host *ihost)
{
- int err = 0, i;
+ int i, err;
enum sci_status status;
- struct sci_user_parameters sci_user_params;
- struct isci_pci_info *pci_info = to_pci_info(ihost->pdev);
-
- spin_lock_init(&ihost->state_lock);
- spin_lock_init(&ihost->scic_lock);
- init_waitqueue_head(&ihost->eventq);
-
- isci_host_change_state(ihost, isci_starting);
-
- status = sci_controller_construct(ihost, scu_base(ihost),
- smu_base(ihost));
+ spin_lock_irq(&ihost->scic_lock);
+ status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost));
+ spin_unlock_irq(&ihost->scic_lock);
if (status != SCI_SUCCESS) {
dev_err(&ihost->pdev->dev,
"%s: sci_controller_construct failed - status = %x\n",
@@ -2291,48 +2352,6 @@ int isci_host_init(struct isci_host *ihost)
return -ENODEV;
}
- ihost->sas_ha.dev = &ihost->pdev->dev;
- ihost->sas_ha.lldd_ha = ihost;
-
- /*
- * grab initial values stored in the controller object for OEM and USER
- * parameters
- */
- isci_user_parameters_get(&sci_user_params);
- status = sci_user_parameters_set(ihost, &sci_user_params);
- if (status != SCI_SUCCESS) {
- dev_warn(&ihost->pdev->dev,
- "%s: sci_user_parameters_set failed\n",
- __func__);
- return -ENODEV;
- }
-
- /* grab any OEM parameters specified in orom */
- if (pci_info->orom) {
- status = isci_parse_oem_parameters(&ihost->oem_parameters,
- pci_info->orom,
- ihost->id);
- if (status != SCI_SUCCESS) {
- dev_warn(&ihost->pdev->dev,
- "parsing firmware oem parameters failed\n");
- return -EINVAL;
- }
- }
-
- status = sci_oem_parameters_set(ihost);
- if (status != SCI_SUCCESS) {
- dev_warn(&ihost->pdev->dev,
- "%s: sci_oem_parameters_set failed\n",
- __func__);
- return -ENODEV;
- }
-
- tasklet_init(&ihost->completion_tasklet,
- isci_host_completion_routine, (unsigned long)ihost);
-
- INIT_LIST_HEAD(&ihost->requests_to_complete);
- INIT_LIST_HEAD(&ihost->requests_to_errorback);
-
spin_lock_irq(&ihost->scic_lock);
status = sci_controller_initialize(ihost);
spin_unlock_irq(&ihost->scic_lock);
@@ -2348,36 +2367,11 @@ int isci_host_init(struct isci_host *ihost)
if (err)
return err;
- for (i = 0; i < SCI_MAX_PORTS; i++)
- isci_port_init(&ihost->ports[i], ihost, i);
-
- for (i = 0; i < SCI_MAX_PHYS; i++)
- isci_phy_init(&ihost->phys[i], ihost, i);
-
- for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
- struct isci_remote_device *idev = &ihost->devices[i];
-
- INIT_LIST_HEAD(&idev->reqs_in_process);
- INIT_LIST_HEAD(&idev->node);
- }
-
- for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) {
- struct isci_request *ireq;
- dma_addr_t dma;
-
- ireq = dmam_alloc_coherent(&ihost->pdev->dev,
- sizeof(struct isci_request), &dma,
- GFP_KERNEL);
- if (!ireq)
- return -ENOMEM;
-
- ireq->tc = &ihost->task_context_table[i];
- ireq->owning_controller = ihost;
- spin_lock_init(&ireq->state_lock);
- ireq->request_daddr = dma;
- ireq->isci_host = ihost;
- ihost->reqs[i] = ireq;
- }
+ /* enable sgpio */
+ writel(1, &ihost->scu_registers->peg0.sgpio.interface_control);
+ for (i = 0; i < isci_gpio_count(ihost); i++)
+ writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
+ writel(0, &ihost->scu_registers->peg0.sgpio.vendor_specific_code);
return 0;
}
@@ -2424,7 +2418,7 @@ void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport,
}
}
-static bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost)
+bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost)
{
u32 index;
@@ -2450,7 +2444,7 @@ void sci_controller_remote_device_stopped(struct isci_host *ihost,
}
if (!sci_controller_has_remote_devices_stopping(ihost))
- sci_change_state(&ihost->sm, SCIC_STOPPED);
+ isci_host_stop_complete(ihost);
}
void sci_controller_post_request(struct isci_host *ihost, u32 request)
@@ -2483,12 +2477,13 @@ struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag)
}
/**
+ * sci_controller_allocate_remote_node_context()
* This method allocates remote node index and the reserves the remote node
* context space for use. This method can fail if there are no more remote
* node index available.
- * @scic: This is the controller object which contains the set of
+ * @ihost: This is the controller object which contains the set of
* free remote node ids
- * @sci_dev: This is the device object which is requesting the a remote node
+ * @idev: This is the device object which is requesting the a remote node
* id
* @node_id: This is the remote node id that is assinged to the device if one
* is available
@@ -2612,7 +2607,8 @@ enum sci_status sci_controller_start_io(struct isci_host *ihost,
enum sci_status status;
if (ihost->sm.current_state_id != SCIC_READY) {
- dev_warn(&ihost->pdev->dev, "invalid state to start I/O");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
@@ -2636,22 +2632,26 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost,
enum sci_status status;
if (ihost->sm.current_state_id != SCIC_READY) {
- dev_warn(&ihost->pdev->dev,
- "invalid state to terminate request\n");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
-
status = sci_io_request_terminate(ireq);
- if (status != SCI_SUCCESS)
- return status;
- /*
- * Utilize the original post context command and or in the POST_TC_ABORT
- * request sub-type.
- */
- sci_controller_post_request(ihost,
- ireq->post_context | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT);
- return SCI_SUCCESS;
+ dev_dbg(&ihost->pdev->dev, "%s: status=%d; ireq=%p; flags=%lx\n",
+ __func__, status, ireq, ireq->flags);
+
+ if ((status == SCI_SUCCESS) &&
+ !test_bit(IREQ_PENDING_ABORT, &ireq->flags) &&
+ !test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) {
+ /* Utilize the original post context command and or in the
+ * POST_TC_ABORT request sub-type.
+ */
+ sci_controller_post_request(
+ ihost, ireq->post_context |
+ SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT);
+ }
+ return status;
}
/**
@@ -2670,7 +2670,6 @@ enum sci_status sci_controller_complete_io(struct isci_host *ihost,
struct isci_request *ireq)
{
enum sci_status status;
- u16 index;
switch (ihost->sm.current_state_id) {
case SCIC_STOPPING:
@@ -2681,11 +2680,11 @@ enum sci_status sci_controller_complete_io(struct isci_host *ihost,
if (status != SCI_SUCCESS)
return status;
- index = ISCI_TAG_TCI(ireq->io_tag);
clear_bit(IREQ_ACTIVE, &ireq->flags);
return SCI_SUCCESS;
default:
- dev_warn(&ihost->pdev->dev, "invalid state to complete I/O");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
@@ -2696,7 +2695,8 @@ enum sci_status sci_controller_continue_io(struct isci_request *ireq)
struct isci_host *ihost = ireq->owning_controller;
if (ihost->sm.current_state_id != SCIC_READY) {
- dev_warn(&ihost->pdev->dev, "invalid state to continue I/O");
+ dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
+ __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE;
}
@@ -2708,15 +2708,15 @@ enum sci_status sci_controller_continue_io(struct isci_request *ireq)
/**
* sci_controller_start_task() - This method is called by the SCIC user to
* send/start a framework task management request.
- * @controller: the handle to the controller object for which to start the task
+ * @ihost: the handle to the controller object for which to start the task
* management request.
- * @remote_device: the handle to the remote device object for which to start
+ * @idev: the handle to the remote device object for which to start
* the task management request.
- * @task_request: the handle to the task request object to start.
+ * @ireq: the handle to the task request object to start.
*/
-enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
- struct isci_remote_device *idev,
- struct isci_request *ireq)
+enum sci_status sci_controller_start_task(struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ struct isci_request *ireq)
{
enum sci_status status;
@@ -2725,7 +2725,7 @@ enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
"%s: SCIC Controller starting task from invalid "
"state\n",
__func__);
- return SCI_TASK_FAILURE_INVALID_STATE;
+ return SCI_FAILURE_INVALID_STATE;
}
status = sci_remote_device_start_task(ihost, idev, ireq);
@@ -2749,3 +2749,56 @@ enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
return status;
}
+
+static int sci_write_gpio_tx_gp(struct isci_host *ihost, u8 reg_index, u8 reg_count, u8 *write_data)
+{
+ int d;
+
+ /* no support for TX_GP_CFG */
+ if (reg_index == 0)
+ return -EINVAL;
+
+ for (d = 0; d < isci_gpio_count(ihost); d++) {
+ u32 val = 0x444; /* all ODx.n clear */
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ int bit;
+
+ bit = try_test_sas_gpio_gp_bit(to_sas_gpio_od(d, i),
+ write_data, reg_index,
+ reg_count);
+ if (bit < 0)
+ break;
+
+ /* if od is set, clear the 'invert' bit */
+ val &= ~(bit << ((i << 2) + 2));
+ }
+
+ if (i < 3)
+ break;
+ writel(val, &ihost->scu_registers->peg0.sgpio.output_data_select[d]);
+ }
+
+ /* unless reg_index is > 1, we should always be able to write at
+ * least one register
+ */
+ return d > 0;
+}
+
+int isci_gpio_write(struct sas_ha_struct *sas_ha, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data)
+{
+ struct isci_host *ihost = sas_ha->lldd_ha;
+ int written;
+
+ switch (reg_type) {
+ case SAS_GPIO_REG_TX_GP:
+ written = sci_write_gpio_tx_gp(ihost, reg_index, reg_count, write_data);
+ break;
+ default:
+ written = -EINVAL;
+ }
+
+ return written;
+}
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 062101a39f79..52388374cf31 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -55,6 +55,7 @@
#ifndef _SCI_HOST_H_
#define _SCI_HOST_H_
+#include <scsi/sas_ata.h>
#include "remote_device.h"
#include "phy.h"
#include "isci.h"
@@ -108,6 +109,8 @@ struct sci_port_configuration_agent;
typedef void (*port_config_fn)(struct isci_host *,
struct sci_port_configuration_agent *,
struct isci_port *, struct isci_phy *);
+bool is_port_config_apc(struct isci_host *ihost);
+bool is_controller_start_complete(struct isci_host *ihost);
struct sci_port_configuration_agent {
u16 phy_configured_mask;
@@ -157,13 +160,17 @@ struct isci_host {
struct sci_power_control power_control;
u8 io_request_sequence[SCI_MAX_IO_REQUESTS];
struct scu_task_context *task_context_table;
- dma_addr_t task_context_dma;
+ dma_addr_t tc_dma;
union scu_remote_node_context *remote_node_context_table;
+ dma_addr_t rnc_dma;
u32 *completion_queue;
+ dma_addr_t cq_dma;
u32 completion_queue_get;
u32 logical_port_entries;
u32 remote_node_entries;
u32 task_context_entries;
+ void *ufi_buf;
+ dma_addr_t ufi_dma;
struct sci_unsolicited_frame_control uf_control;
/* phy startup */
@@ -187,19 +194,16 @@ struct isci_host {
int id; /* unique within a given pci device */
struct isci_phy phys[SCI_MAX_PHYS];
struct isci_port ports[SCI_MAX_PORTS + 1]; /* includes dummy port */
+ struct asd_sas_port sas_ports[SCI_MAX_PORTS];
struct sas_ha_struct sas_ha;
- spinlock_t state_lock;
struct pci_dev *pdev;
- enum isci_status status;
#define IHOST_START_PENDING 0
#define IHOST_STOP_PENDING 1
+ #define IHOST_IRQ_ENABLED 2
unsigned long flags;
wait_queue_head_t eventq;
- struct Scsi_Host *shost;
struct tasklet_struct completion_tasklet;
- struct list_head requests_to_complete;
- struct list_head requests_to_errorback;
spinlock_t scic_lock;
struct isci_request *reqs[SCI_MAX_IO_REQUESTS];
struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES];
@@ -273,13 +277,6 @@ enum sci_controller_states {
SCIC_STOPPING,
/**
- * This state indicates that the controller has successfully been stopped.
- * In this state no new IO operations are permitted.
- * This state is entered from the STOPPING state.
- */
- SCIC_STOPPED,
-
- /**
* This state indicates that the controller could not successfully be
* initialized. In this state no new IO operations are permitted.
* This state is entered from the INITIALIZING state.
@@ -298,7 +295,6 @@ enum sci_controller_states {
#define SCI_MAX_MSIX_INT (SCI_NUM_MSI_X_INT*SCI_MAX_CONTROLLERS)
struct isci_pci_info {
- struct msix_entry msix_entries[SCI_MAX_MSIX_INT];
struct isci_host *hosts[SCI_MAX_CONTROLLERS];
struct isci_orom *orom;
};
@@ -308,31 +304,14 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
return pci_get_drvdata(pdev);
}
-#define for_each_isci_host(id, ihost, pdev) \
- for (id = 0, ihost = to_pci_info(pdev)->hosts[id]; \
- id < ARRAY_SIZE(to_pci_info(pdev)->hosts) && ihost; \
- ihost = to_pci_info(pdev)->hosts[++id])
-
-static inline enum isci_status isci_host_get_state(struct isci_host *isci_host)
+static inline struct Scsi_Host *to_shost(struct isci_host *ihost)
{
- return isci_host->status;
+ return ihost->sas_ha.shost;
}
-static inline void isci_host_change_state(struct isci_host *isci_host,
- enum isci_status status)
-{
- unsigned long flags;
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_host = %p, state = 0x%x",
- __func__,
- isci_host,
- status);
- spin_lock_irqsave(&isci_host->state_lock, flags);
- isci_host->status = status;
- spin_unlock_irqrestore(&isci_host->state_lock, flags);
-
-}
+#define for_each_isci_host(id, ihost, pdev) \
+ for (id = 0; id < SCI_MAX_CONTROLLERS && \
+ (ihost = to_pci_info(pdev)->hosts[id]); id++)
static inline void wait_for_start(struct isci_host *ihost)
{
@@ -359,6 +338,11 @@ static inline struct isci_host *dev_to_ihost(struct domain_device *dev)
return dev->port->ha->lldd_ha;
}
+static inline struct isci_host *idev_to_ihost(struct isci_remote_device *idev)
+{
+ return dev_to_ihost(idev->domain_dev);
+}
+
/* we always use protocol engine group zero */
#define ISCI_PEG 0
@@ -369,13 +353,15 @@ static inline struct isci_host *dev_to_ihost(struct domain_device *dev)
#define ISCI_TAG_SEQ(tag) (((tag) >> 12) & (SCI_MAX_SEQ-1))
#define ISCI_TAG_TCI(tag) ((tag) & (SCI_MAX_IO_REQUESTS-1))
+/* interrupt coalescing baseline: 9 == 3 to 5us interrupt delay per command */
+#define ISCI_COALESCE_BASE 9
+
/* expander attached sata devices require 3 rnc slots */
static inline int sci_remote_device_node_count(struct isci_remote_device *idev)
{
struct domain_device *dev = idev->domain_dev;
- if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) &&
- !idev->is_direct_attached)
+ if (dev_is_sata(dev) && dev->parent)
return SCU_STP_REMOTE_NODE_COUNT;
return SCU_SSP_REMOTE_NODE_COUNT;
}
@@ -390,24 +376,6 @@ static inline int sci_remote_device_node_count(struct isci_remote_device *idev)
#define sci_controller_clear_invalid_phy(controller, phy) \
((controller)->invalid_phy_mask &= ~(1 << (phy)->phy_index))
-static inline struct device *sciphy_to_dev(struct isci_phy *iphy)
-{
-
- if (!iphy || !iphy->isci_port || !iphy->isci_port->isci_host)
- return NULL;
-
- return &iphy->isci_port->isci_host->pdev->dev;
-}
-
-static inline struct device *sciport_to_dev(struct isci_port *iport)
-{
-
- if (!iport || !iport->isci_host)
- return NULL;
-
- return &iport->isci_host->pdev->dev;
-}
-
static inline struct device *scirdev_to_dev(struct isci_remote_device *idev)
{
if (!idev || !idev->isci_port || !idev->isci_port->isci_host)
@@ -432,11 +400,48 @@ static inline bool is_b0(struct pci_dev *pdev)
static inline bool is_c0(struct pci_dev *pdev)
{
- if (pdev->revision >= 5)
+ if (pdev->revision == 5)
+ return true;
+ return false;
+}
+
+static inline bool is_c1(struct pci_dev *pdev)
+{
+ if (pdev->revision >= 6)
return true;
return false;
}
+enum cable_selections {
+ short_cable = 0,
+ long_cable = 1,
+ medium_cable = 2,
+ undefined_cable = 3
+};
+
+#define CABLE_OVERRIDE_DISABLED (0x10000)
+
+static inline int is_cable_select_overridden(void)
+{
+ return cable_selection_override < CABLE_OVERRIDE_DISABLED;
+}
+
+enum cable_selections decode_cable_selection(struct isci_host *ihost, int phy);
+void validate_cable_selections(struct isci_host *ihost);
+char *lookup_cable_names(enum cable_selections);
+
+/* set hw control for 'activity', even though active enclosures seem to drive
+ * the activity led on their own. Skip setting FSENG control on 'status' due
+ * to unexpected operation and 'error' due to not being a supported automatic
+ * FSENG output
+ */
+#define SGPIO_HW_CONTROL 0x00000443
+
+static inline int isci_gpio_count(struct isci_host *ihost)
+{
+ return ARRAY_SIZE(ihost->scu_registers->peg0.sgpio.output_data_select);
+}
+
void sci_controller_post_request(struct isci_host *ihost,
u32 request);
void sci_controller_release_frame(struct isci_host *ihost,
@@ -452,73 +457,39 @@ void sci_controller_free_remote_node_context(
struct isci_remote_device *idev,
u16 node_id);
-struct isci_request *sci_request_by_tag(struct isci_host *ihost,
- u16 io_tag);
-
-void sci_controller_power_control_queue_insert(
- struct isci_host *ihost,
- struct isci_phy *iphy);
-
-void sci_controller_power_control_queue_remove(
- struct isci_host *ihost,
- struct isci_phy *iphy);
-
-void sci_controller_link_up(
- struct isci_host *ihost,
- struct isci_port *iport,
- struct isci_phy *iphy);
-
-void sci_controller_link_down(
- struct isci_host *ihost,
- struct isci_port *iport,
- struct isci_phy *iphy);
-
-void sci_controller_remote_device_stopped(
- struct isci_host *ihost,
- struct isci_remote_device *idev);
-
-void sci_controller_copy_task_context(
- struct isci_host *ihost,
- struct isci_request *ireq);
-
-void sci_controller_register_setup(struct isci_host *ihost);
+struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag);
+void sci_controller_power_control_queue_insert(struct isci_host *ihost,
+ struct isci_phy *iphy);
+void sci_controller_power_control_queue_remove(struct isci_host *ihost,
+ struct isci_phy *iphy);
+void sci_controller_link_up(struct isci_host *ihost, struct isci_port *iport,
+ struct isci_phy *iphy);
+void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport,
+ struct isci_phy *iphy);
+void sci_controller_remote_device_stopped(struct isci_host *ihost,
+ struct isci_remote_device *idev);
enum sci_status sci_controller_continue_io(struct isci_request *ireq);
int isci_host_scan_finished(struct Scsi_Host *, unsigned long);
-void isci_host_scan_start(struct Scsi_Host *);
+void isci_host_start(struct Scsi_Host *);
u16 isci_alloc_tag(struct isci_host *ihost);
enum sci_status isci_free_tag(struct isci_host *ihost, u16 io_tag);
void isci_tci_free(struct isci_host *ihost, u16 tci);
+void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task);
int isci_host_init(struct isci_host *);
-
-void isci_host_init_controller_names(
- struct isci_host *isci_host,
- unsigned int controller_idx);
-
-void isci_host_deinit(
- struct isci_host *);
-
-void isci_host_port_link_up(
- struct isci_host *,
- struct isci_port *,
- struct isci_phy *);
-int isci_host_dev_found(struct domain_device *);
-
-void isci_host_remote_device_start_complete(
- struct isci_host *,
- struct isci_remote_device *,
- enum sci_status);
-
-void sci_controller_disable_interrupts(
- struct isci_host *ihost);
+void isci_host_completion_routine(unsigned long data);
+void isci_host_deinit(struct isci_host *);
+void sci_controller_disable_interrupts(struct isci_host *ihost);
+bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost);
+void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status);
enum sci_status sci_controller_start_io(
struct isci_host *ihost,
struct isci_remote_device *idev,
struct isci_request *ireq);
-enum sci_task_status sci_controller_start_task(
+enum sci_status sci_controller_start_task(
struct isci_host *ihost,
struct isci_remote_device *idev,
struct isci_request *ireq);
@@ -539,4 +510,7 @@ void sci_port_configuration_agent_construct(
enum sci_status sci_port_configuration_agent_initialize(
struct isci_host *ihost,
struct sci_port_configuration_agent *port_agent);
+
+int isci_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data);
#endif
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index 61e0d09e2b57..acf0c2038d20 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -59,13 +59,19 @@
#include <linux/firmware.h>
#include <linux/efi.h>
#include <asm/string.h>
+#include <scsi/scsi_host.h>
+#include "host.h"
#include "isci.h"
#include "task.h"
#include "probe_roms.h"
+#define DRV_VERSION "1.2.0"
+
+MODULE_VERSION(DRV_VERSION);
+
static struct scsi_transport_template *isci_transport_template;
-static DEFINE_PCI_DEVICE_TABLE(isci_id_table) = {
+static const struct pci_device_id isci_id_table[] = {
{ PCI_VDEVICE(INTEL, 0x1D61),},
{ PCI_VDEVICE(INTEL, 0x1D63),},
{ PCI_VDEVICE(INTEL, 0x1D65),},
@@ -85,59 +91,75 @@ MODULE_DEVICE_TABLE(pci, isci_id_table);
/* linux isci specific settings */
-unsigned char no_outbound_task_to = 20;
+static unsigned char no_outbound_task_to = 2;
module_param(no_outbound_task_to, byte, 0);
MODULE_PARM_DESC(no_outbound_task_to, "No Outbound Task Timeout (1us incr)");
-u16 ssp_max_occ_to = 20;
+static u16 ssp_max_occ_to = 20;
module_param(ssp_max_occ_to, ushort, 0);
MODULE_PARM_DESC(ssp_max_occ_to, "SSP Max occupancy timeout (100us incr)");
-u16 stp_max_occ_to = 5;
+static u16 stp_max_occ_to = 5;
module_param(stp_max_occ_to, ushort, 0);
MODULE_PARM_DESC(stp_max_occ_to, "STP Max occupancy timeout (100us incr)");
-u16 ssp_inactive_to = 5;
+static u16 ssp_inactive_to = 5;
module_param(ssp_inactive_to, ushort, 0);
MODULE_PARM_DESC(ssp_inactive_to, "SSP inactivity timeout (100us incr)");
-u16 stp_inactive_to = 5;
+static u16 stp_inactive_to = 5;
module_param(stp_inactive_to, ushort, 0);
MODULE_PARM_DESC(stp_inactive_to, "STP inactivity timeout (100us incr)");
-unsigned char phy_gen = 3;
+static unsigned char phy_gen = SCIC_SDS_PARM_GEN2_SPEED;
module_param(phy_gen, byte, 0);
MODULE_PARM_DESC(phy_gen, "PHY generation (1: 1.5Gbps 2: 3.0Gbps 3: 6.0Gbps)");
-unsigned char max_concurr_spinup = 1;
+static unsigned char max_concurr_spinup;
module_param(max_concurr_spinup, byte, 0);
MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup");
-static struct scsi_host_template isci_sht = {
+uint cable_selection_override = CABLE_OVERRIDE_DISABLED;
+module_param(cable_selection_override, uint, 0);
+
+MODULE_PARM_DESC(cable_selection_override,
+ "This field indicates length of the SAS/SATA cable between "
+ "host and device. If any bits > 15 are set (default) "
+ "indicates \"use platform defaults\"");
+
+static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ struct isci_host *ihost = container_of(sas_ha, typeof(*ihost), sas_ha);
+
+ return sysfs_emit(buf, "%d\n", ihost->id);
+}
+
+static DEVICE_ATTR(isci_id, S_IRUGO, isci_show_id, NULL);
+
+static struct attribute *isci_host_attrs[] = {
+ &dev_attr_isci_id.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(isci_host);
+
+static const struct attribute_group *isci_sdev_groups[] = {
+ &sas_ata_sdev_attr_group,
+ NULL
+};
- .module = THIS_MODULE,
- .name = DRV_NAME,
- .proc_name = DRV_NAME,
- .queuecommand = sas_queuecommand,
- .target_alloc = sas_target_alloc,
- .slave_configure = sas_slave_configure,
- .slave_destroy = sas_slave_destroy,
+static const struct scsi_host_template isci_sht = {
+ LIBSAS_SHT_BASE
.scan_finished = isci_host_scan_finished,
- .scan_start = isci_host_scan_start,
- .change_queue_depth = sas_change_queue_depth,
- .change_queue_type = sas_change_queue_type,
- .bios_param = sas_bios_param,
+ .scan_start = isci_host_start,
.can_queue = ISCI_CAN_QUEUE_VAL,
- .cmd_per_lun = 1,
- .this_id = -1,
.sg_tablesize = SG_ALL,
- .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
- .use_clustering = ENABLE_CLUSTERING,
- .eh_device_reset_handler = sas_eh_device_reset_handler,
- .eh_bus_reset_handler = isci_bus_reset_handler,
- .slave_alloc = sas_slave_alloc,
- .target_destroy = sas_target_destroy,
- .ioctl = sas_ioctl,
+ .eh_abort_handler = sas_eh_abort_handler,
+ .shost_groups = isci_host_groups,
+ .sdev_groups = isci_sdev_groups,
+ .track_queue_depth = 1,
};
static struct sas_domain_function_template isci_transport_ops = {
@@ -154,18 +176,23 @@ static struct sas_domain_function_template isci_transport_ops = {
/* Task Management Functions. Must be called from process context. */
.lldd_abort_task = isci_task_abort_task,
.lldd_abort_task_set = isci_task_abort_task_set,
- .lldd_clear_aca = isci_task_clear_aca,
.lldd_clear_task_set = isci_task_clear_task_set,
.lldd_I_T_nexus_reset = isci_task_I_T_nexus_reset,
.lldd_lu_reset = isci_task_lu_reset,
.lldd_query_task = isci_task_query_task,
+ /* ata recovery called from ata-eh */
+ .lldd_ata_check_ready = isci_ata_check_ready,
+
/* Port and Adapter management */
.lldd_clear_nexus_port = isci_task_clear_nexus_port,
.lldd_clear_nexus_ha = isci_task_clear_nexus_ha,
/* Phy management */
.lldd_control_phy = isci_phy_control,
+
+ /* GPIO support */
+ .lldd_write_gpio = isci_gpio_write,
};
@@ -182,7 +209,7 @@ static struct sas_domain_function_template isci_transport_ops = {
* @isci_host: This parameter specifies the lldd specific wrapper for the
* libsas sas_ha struct.
*
- * This method returns an error code indicating sucess or failure. The user
+ * This method returns an error code indicating success or failure. The user
* should check for possible memory allocation error return otherwise, a zero
* indicates success.
*/
@@ -193,56 +220,35 @@ static int isci_register_sas_ha(struct isci_host *isci_host)
struct asd_sas_phy **sas_phys;
struct asd_sas_port **sas_ports;
- sas_phys = devm_kzalloc(&isci_host->pdev->dev,
- SCI_MAX_PHYS * sizeof(void *),
+ sas_phys = devm_kcalloc(&isci_host->pdev->dev,
+ SCI_MAX_PHYS, sizeof(void *),
GFP_KERNEL);
if (!sas_phys)
return -ENOMEM;
- sas_ports = devm_kzalloc(&isci_host->pdev->dev,
- SCI_MAX_PORTS * sizeof(void *),
+ sas_ports = devm_kcalloc(&isci_host->pdev->dev,
+ SCI_MAX_PORTS, sizeof(void *),
GFP_KERNEL);
if (!sas_ports)
return -ENOMEM;
- /*----------------- Libsas Initialization Stuff----------------------
- * Set various fields in the sas_ha struct:
- */
-
sas_ha->sas_ha_name = DRV_NAME;
- sas_ha->lldd_module = THIS_MODULE;
sas_ha->sas_addr = &isci_host->phys[0].sas_addr[0];
- /* set the array of phy and port structs. */
for (i = 0; i < SCI_MAX_PHYS; i++) {
sas_phys[i] = &isci_host->phys[i].sas_phy;
- sas_ports[i] = &isci_host->ports[i].sas_port;
+ sas_ports[i] = &isci_host->sas_ports[i];
}
sas_ha->sas_phy = sas_phys;
sas_ha->sas_port = sas_ports;
sas_ha->num_phys = SCI_MAX_PHYS;
- sas_ha->lldd_queue_size = ISCI_CAN_QUEUE_VAL;
- sas_ha->lldd_max_execute_num = 1;
sas_ha->strict_wide_ports = 1;
- sas_register_ha(sas_ha);
-
- return 0;
+ return sas_register_ha(sas_ha);
}
-static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev);
- struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
- struct isci_host *ihost = container_of(sas_ha, typeof(*ihost), sas_ha);
-
- return snprintf(buf, PAGE_SIZE, "%d\n", ihost->id);
-}
-
-static DEVICE_ATTR(isci_id, S_IRUGO, isci_show_id, NULL);
-
static void isci_unregister(struct isci_host *isci_host)
{
struct Scsi_Host *shost;
@@ -250,17 +256,14 @@ static void isci_unregister(struct isci_host *isci_host)
if (!isci_host)
return;
- shost = isci_host->shost;
- device_remove_file(&shost->shost_dev, &dev_attr_isci_id);
-
+ shost = to_shost(isci_host);
sas_unregister_ha(&isci_host->sas_ha);
- sas_remove_host(isci_host->shost);
- scsi_remove_host(isci_host->shost);
- scsi_host_put(isci_host->shost);
+ sas_remove_host(shost);
+ scsi_host_put(shost);
}
-static int __devinit isci_pci_init(struct pci_dev *pdev)
+static int isci_pci_init(struct pci_dev *pdev)
{
int err, bar_num, bar_mask = 0;
void __iomem * const *iomap;
@@ -286,21 +289,10 @@ static int __devinit isci_pci_init(struct pci_dev *pdev)
pci_set_master(pdev);
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
- if (err) {
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (err)
- return err;
- }
-
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
- if (err) {
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (err)
- return err;
- }
-
- return 0;
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err)
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ return err;
}
static int num_controllers(struct pci_dev *pdev)
@@ -331,16 +323,12 @@ static int isci_setup_interrupts(struct pci_dev *pdev)
*/
num_msix = num_controllers(pdev) * SCI_NUM_MSI_X_INT;
- for (i = 0; i < num_msix; i++)
- pci_info->msix_entries[i].entry = i;
-
- err = pci_enable_msix(pdev, pci_info->msix_entries, num_msix);
- if (err)
+ err = pci_alloc_irq_vectors(pdev, num_msix, num_msix, PCI_IRQ_MSIX);
+ if (err < 0)
goto intx;
for (i = 0; i < num_msix; i++) {
int id = i / SCI_NUM_MSI_X_INT;
- struct msix_entry *msix = &pci_info->msix_entries[i];
irq_handler_t isr;
ihost = pci_info->hosts[id];
@@ -350,8 +338,8 @@ static int isci_setup_interrupts(struct pci_dev *pdev)
else
isr = isci_msix_isr;
- err = devm_request_irq(&pdev->dev, msix->vector, isr, 0,
- DRV_NAME"-msix", ihost);
+ err = devm_request_irq(&pdev->dev, pci_irq_vector(pdev, i),
+ isr, 0, DRV_NAME"-msix", ihost);
if (!err)
continue;
@@ -359,70 +347,238 @@ static int isci_setup_interrupts(struct pci_dev *pdev)
while (i--) {
id = i / SCI_NUM_MSI_X_INT;
ihost = pci_info->hosts[id];
- msix = &pci_info->msix_entries[i];
- devm_free_irq(&pdev->dev, msix->vector, ihost);
+ devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i),
+ ihost);
}
- pci_disable_msix(pdev);
+ pci_free_irq_vectors(pdev);
goto intx;
}
return 0;
intx:
for_each_isci_host(i, ihost, pdev) {
- err = devm_request_irq(&pdev->dev, pdev->irq, isci_intx_isr,
- IRQF_SHARED, DRV_NAME"-intx", ihost);
+ err = devm_request_irq(&pdev->dev, pci_irq_vector(pdev, 0),
+ isci_intx_isr, IRQF_SHARED, DRV_NAME"-intx",
+ ihost);
if (err)
break;
}
return err;
}
+static void isci_user_parameters_get(struct sci_user_parameters *u)
+{
+ int i;
+
+ for (i = 0; i < SCI_MAX_PHYS; i++) {
+ struct sci_phy_user_params *u_phy = &u->phys[i];
+
+ u_phy->max_speed_generation = phy_gen;
+
+ /* we are not exporting these for now */
+ u_phy->align_insertion_frequency = 0x7f;
+ u_phy->in_connection_align_insertion_frequency = 0xff;
+ u_phy->notify_enable_spin_up_insertion_frequency = 0x33;
+ }
+
+ u->stp_inactivity_timeout = stp_inactive_to;
+ u->ssp_inactivity_timeout = ssp_inactive_to;
+ u->stp_max_occupancy_timeout = stp_max_occ_to;
+ u->ssp_max_occupancy_timeout = ssp_max_occ_to;
+ u->no_outbound_task_timeout = no_outbound_task_to;
+ u->max_concurr_spinup = max_concurr_spinup;
+}
+
+static enum sci_status sci_user_parameters_set(struct isci_host *ihost,
+ struct sci_user_parameters *sci_parms)
+{
+ u16 index;
+
+ /*
+ * Validate the user parameters. If they are not legal, then
+ * return a failure.
+ */
+ for (index = 0; index < SCI_MAX_PHYS; index++) {
+ struct sci_phy_user_params *u;
+
+ u = &sci_parms->phys[index];
+
+ if (!((u->max_speed_generation <= SCIC_SDS_PARM_MAX_SPEED) &&
+ (u->max_speed_generation > SCIC_SDS_PARM_NO_SPEED)))
+ return SCI_FAILURE_INVALID_PARAMETER_VALUE;
+
+ if ((u->in_connection_align_insertion_frequency < 3) ||
+ (u->align_insertion_frequency == 0) ||
+ (u->notify_enable_spin_up_insertion_frequency == 0))
+ return SCI_FAILURE_INVALID_PARAMETER_VALUE;
+ }
+
+ if ((sci_parms->stp_inactivity_timeout == 0) ||
+ (sci_parms->ssp_inactivity_timeout == 0) ||
+ (sci_parms->stp_max_occupancy_timeout == 0) ||
+ (sci_parms->ssp_max_occupancy_timeout == 0) ||
+ (sci_parms->no_outbound_task_timeout == 0))
+ return SCI_FAILURE_INVALID_PARAMETER_VALUE;
+
+ memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms));
+
+ return SCI_SUCCESS;
+}
+
+static void sci_oem_defaults(struct isci_host *ihost)
+{
+ /* these defaults are overridden by the platform / firmware */
+ struct sci_user_parameters *user = &ihost->user_parameters;
+ struct sci_oem_params *oem = &ihost->oem_parameters;
+ int i;
+
+ /* Default to APC mode. */
+ oem->controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE;
+
+ /* Default to APC mode. */
+ oem->controller.max_concurr_spin_up = 1;
+
+ /* Default to no SSC operation. */
+ oem->controller.do_enable_ssc = false;
+
+ /* Default to short cables on all phys. */
+ oem->controller.cable_selection_mask = 0;
+
+ /* Initialize all of the port parameter information to narrow ports. */
+ for (i = 0; i < SCI_MAX_PORTS; i++)
+ oem->ports[i].phy_mask = 0;
+
+ /* Initialize all of the phy parameter information. */
+ for (i = 0; i < SCI_MAX_PHYS; i++) {
+ /* Default to 3G (i.e. Gen 2). */
+ user->phys[i].max_speed_generation = SCIC_SDS_PARM_GEN2_SPEED;
+
+ /* the frequencies cannot be 0 */
+ user->phys[i].align_insertion_frequency = 0x7f;
+ user->phys[i].in_connection_align_insertion_frequency = 0xff;
+ user->phys[i].notify_enable_spin_up_insertion_frequency = 0x33;
+
+ /* Previous Vitesse based expanders had a arbitration issue that
+ * is worked around by having the upper 32-bits of SAS address
+ * with a value greater then the Vitesse company identifier.
+ * Hence, usage of 0x5FCFFFFF.
+ */
+ oem->phys[i].sas_address.low = 0x1 + ihost->id;
+ oem->phys[i].sas_address.high = 0x5FCFFFFF;
+ }
+
+ user->stp_inactivity_timeout = 5;
+ user->ssp_inactivity_timeout = 5;
+ user->stp_max_occupancy_timeout = 5;
+ user->ssp_max_occupancy_timeout = 20;
+ user->no_outbound_task_timeout = 2;
+}
+
static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
{
- struct isci_host *isci_host;
+ struct isci_orom *orom = to_pci_info(pdev)->orom;
+ struct sci_user_parameters sci_user_params;
+ u8 oem_version = ISCI_ROM_VER_1_0;
+ struct isci_host *ihost;
struct Scsi_Host *shost;
- int err;
+ int err, i;
- isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host), GFP_KERNEL);
- if (!isci_host)
+ ihost = devm_kzalloc(&pdev->dev, sizeof(*ihost), GFP_KERNEL);
+ if (!ihost)
+ return NULL;
+
+ ihost->pdev = pdev;
+ ihost->id = id;
+ spin_lock_init(&ihost->scic_lock);
+ init_waitqueue_head(&ihost->eventq);
+ ihost->sas_ha.dev = &ihost->pdev->dev;
+ ihost->sas_ha.lldd_ha = ihost;
+ tasklet_init(&ihost->completion_tasklet,
+ isci_host_completion_routine, (unsigned long)ihost);
+
+ /* validate module parameters */
+ /* TODO: kill struct sci_user_parameters and reference directly */
+ sci_oem_defaults(ihost);
+ isci_user_parameters_get(&sci_user_params);
+ if (sci_user_parameters_set(ihost, &sci_user_params)) {
+ dev_warn(&pdev->dev,
+ "%s: sci_user_parameters_set failed\n", __func__);
return NULL;
+ }
+
+ /* sanity check platform (or 'firmware') oem parameters */
+ if (orom) {
+ if (id < 0 || id >= SCI_MAX_CONTROLLERS || id > orom->hdr.num_elements) {
+ dev_warn(&pdev->dev, "parsing firmware oem parameters failed\n");
+ return NULL;
+ }
+ ihost->oem_parameters = orom->ctrl[id];
+ oem_version = orom->hdr.version;
+ }
+
+ /* validate oem parameters (platform, firmware, or built-in defaults) */
+ if (sci_oem_parameters_validate(&ihost->oem_parameters, oem_version)) {
+ dev_warn(&pdev->dev, "oem parameter validation failed\n");
+ return NULL;
+ }
+
+ for (i = 0; i < SCI_MAX_PORTS; i++) {
+ struct isci_port *iport = &ihost->ports[i];
- isci_host->pdev = pdev;
- isci_host->id = id;
+ INIT_LIST_HEAD(&iport->remote_dev_list);
+ iport->isci_host = ihost;
+ }
+
+ for (i = 0; i < SCI_MAX_PHYS; i++)
+ isci_phy_init(&ihost->phys[i], ihost, i);
+
+ for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
+ struct isci_remote_device *idev = &ihost->devices[i];
+
+ INIT_LIST_HEAD(&idev->node);
+ }
shost = scsi_host_alloc(&isci_sht, sizeof(void *));
if (!shost)
return NULL;
- isci_host->shost = shost;
- err = isci_host_init(isci_host);
+ dev_info(&pdev->dev, "%sSCU controller %d: phy 3-0 cables: "
+ "{%s, %s, %s, %s}\n",
+ (is_cable_select_overridden() ? "* " : ""), ihost->id,
+ lookup_cable_names(decode_cable_selection(ihost, 3)),
+ lookup_cable_names(decode_cable_selection(ihost, 2)),
+ lookup_cable_names(decode_cable_selection(ihost, 1)),
+ lookup_cable_names(decode_cable_selection(ihost, 0)));
+
+ err = isci_host_init(ihost);
if (err)
goto err_shost;
- SHOST_TO_SAS_HA(shost) = &isci_host->sas_ha;
- isci_host->sas_ha.core.shost = shost;
+ SHOST_TO_SAS_HA(shost) = &ihost->sas_ha;
+ ihost->sas_ha.shost = shost;
shost->transportt = isci_transport_template;
shost->max_id = ~0;
shost->max_lun = ~0;
shost->max_cmd_len = MAX_COMMAND_SIZE;
+ /* turn on DIF support */
+ scsi_host_set_prot(shost,
+ SHOST_DIF_TYPE1_PROTECTION |
+ SHOST_DIF_TYPE2_PROTECTION |
+ SHOST_DIF_TYPE3_PROTECTION);
+ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+
err = scsi_add_host(shost, &pdev->dev);
if (err)
goto err_shost;
- err = isci_register_sas_ha(isci_host);
+ err = isci_register_sas_ha(ihost);
if (err)
goto err_shost_remove;
- err = device_create_file(&shost->shost_dev, &dev_attr_isci_id);
- if (err)
- goto err_unregister_ha;
-
- return isci_host;
+ return ihost;
- err_unregister_ha:
- sas_unregister_ha(&(isci_host->sas_ha));
err_shost_remove:
scsi_remove_host(shost);
err_shost:
@@ -431,7 +587,7 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
return NULL;
}
-static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct isci_pci_info *pci_info;
int err, i;
@@ -448,17 +604,17 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic
return -ENOMEM;
pci_set_drvdata(pdev, pci_info);
- if (efi_enabled)
+ if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
orom = isci_get_efi_var(pdev);
if (!orom)
orom = isci_request_oprom(pdev);
- for (i = 0; orom && i < ARRAY_SIZE(orom->ctrl); i++) {
- if (sci_oem_parameters_validate(&orom->ctrl[i])) {
+ for (i = 0; orom && i < num_controllers(pdev); i++) {
+ if (sci_oem_parameters_validate(&orom->ctrl[i],
+ orom->hdr.version)) {
dev_warn(&pdev->dev,
"[%d]: invalid oem parameters detected, falling back to firmware\n", i);
- devm_kfree(&pdev->dev, orom);
orom = NULL;
break;
}
@@ -507,7 +663,7 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic
goto err_host_alloc;
for_each_isci_host(i, isci_host, pdev)
- scsi_scan_host(isci_host->shost);
+ scsi_scan_host(to_shost(isci_host));
return 0;
@@ -517,30 +673,69 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic
return err;
}
-static void __devexit isci_pci_remove(struct pci_dev *pdev)
+static void isci_pci_remove(struct pci_dev *pdev)
{
struct isci_host *ihost;
int i;
for_each_isci_host(i, ihost, pdev) {
+ wait_for_start(ihost);
isci_unregister(ihost);
isci_host_deinit(ihost);
- sci_controller_disable_interrupts(ihost);
}
}
+#ifdef CONFIG_PM_SLEEP
+static int isci_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct isci_host *ihost;
+ int i;
+
+ for_each_isci_host(i, ihost, pdev) {
+ sas_suspend_ha(&ihost->sas_ha);
+ isci_host_deinit(ihost);
+ }
+
+ return 0;
+}
+
+static int isci_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct isci_host *ihost;
+ int i;
+
+ for_each_isci_host(i, ihost, pdev) {
+ sas_prep_resume_ha(&ihost->sas_ha);
+
+ isci_host_init(ihost);
+ isci_host_start(ihost->sas_ha.shost);
+ wait_for_start(ihost);
+
+ sas_resume_ha(&ihost->sas_ha);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(isci_pm_ops, isci_suspend, isci_resume);
+
static struct pci_driver isci_pci_driver = {
.name = DRV_NAME,
.id_table = isci_id_table,
.probe = isci_pci_probe,
- .remove = __devexit_p(isci_pci_remove),
+ .remove = isci_pci_remove,
+ .driver.pm = &isci_pm_ops,
};
static __init int isci_init(void)
{
int err;
- pr_info("%s: Intel(R) C600 SAS Controller Driver\n", DRV_NAME);
+ pr_info("%s: Intel(R) C600 SAS Controller Driver - version %s\n",
+ DRV_NAME, DRV_VERSION);
isci_transport_template = sas_domain_attach_transport(&isci_transport_ops);
if (!isci_transport_template)
@@ -559,6 +754,7 @@ static __exit void isci_exit(void)
sas_release_transport(isci_transport_template);
}
+MODULE_DESCRIPTION("Intel(R) C600 Series Chipset SAS Controller driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_FIRMWARE(ISCI_FW_NAME);
module_init(isci_init);
diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h
index d1de63312e7f..d827e49c1d55 100644
--- a/drivers/scsi/isci/isci.h
+++ b/drivers/scsi/isci/isci.h
@@ -97,7 +97,7 @@
#define SCU_MAX_COMPLETION_QUEUE_SHIFT (ilog2(SCU_MAX_COMPLETION_QUEUE_ENTRIES))
#define SCU_ABSOLUTE_MAX_UNSOLICITED_FRAMES (4096)
-#define SCU_UNSOLICITED_FRAME_BUFFER_SIZE (1024)
+#define SCU_UNSOLICITED_FRAME_BUFFER_SIZE (1024U)
#define SCU_INVALID_FRAME_INDEX (0xFFFF)
#define SCU_IO_REQUEST_MAX_SGE_SIZE (0x00FFFFFF)
@@ -473,13 +473,7 @@ static inline void sci_swab32_cpy(void *_dest, void *_src, ssize_t word_cnt)
dest[word_cnt] = swab32(src[word_cnt]);
}
-extern unsigned char no_outbound_task_to;
-extern u16 ssp_max_occ_to;
-extern u16 stp_max_occ_to;
-extern u16 ssp_inactive_to;
-extern u16 stp_inactive_to;
-extern unsigned char phy_gen;
-extern unsigned char max_concurr_spinup;
+extern uint cable_selection_override;
irqreturn_t isci_msix_isr(int vec, void *data);
irqreturn_t isci_intx_isr(int vec, void *data);
@@ -487,9 +481,9 @@ irqreturn_t isci_error_isr(int vec, void *data);
/*
* Each timer is associated with a cancellation flag that is set when
- * del_timer() is called and checked in the timer callback function. This
- * is needed since del_timer_sync() cannot be called with sci_lock held.
- * For deinit however, del_timer_sync() is used without holding the lock.
+ * timer_delete() is called and checked in the timer callback function. This
+ * is needed since timer_delete_sync() cannot be called with sci_lock held.
+ * For deinit however, timer_delete_sync() is used without holding the lock.
*/
struct sci_timer {
struct timer_list timer;
@@ -497,24 +491,22 @@ struct sci_timer {
};
static inline
-void sci_init_timer(struct sci_timer *tmr, void (*fn)(unsigned long))
+void sci_init_timer(struct sci_timer *tmr, void (*fn)(struct timer_list *t))
{
- tmr->timer.function = fn;
- tmr->timer.data = (unsigned long) tmr;
- tmr->cancel = 0;
- init_timer(&tmr->timer);
+ tmr->cancel = false;
+ timer_setup(&tmr->timer, fn, 0);
}
static inline void sci_mod_timer(struct sci_timer *tmr, unsigned long msec)
{
- tmr->cancel = 0;
+ tmr->cancel = false;
mod_timer(&tmr->timer, jiffies + msecs_to_jiffies(msec));
}
static inline void sci_del_timer(struct sci_timer *tmr)
{
- tmr->cancel = 1;
- del_timer(&tmr->timer);
+ tmr->cancel = true;
+ timer_delete(&tmr->timer);
}
struct sci_base_state_machine {
diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c
index 79313a7a2356..88237ec8b15f 100644
--- a/drivers/scsi/isci/phy.c
+++ b/drivers/scsi/isci/phy.c
@@ -59,6 +59,16 @@
#include "scu_event_codes.h"
#include "probe_roms.h"
+#undef C
+#define C(a) (#a)
+static const char *phy_state_name(enum sci_phy_states state)
+{
+ static const char * const strings[] = PHY_STATES;
+
+ return strings[state];
+}
+#undef C
+
/* Maximum arbitration wait time in micro-seconds */
#define SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME (700)
@@ -67,6 +77,19 @@ enum sas_linkrate sci_phy_linkrate(struct isci_phy *iphy)
return iphy->max_negotiated_speed;
}
+static struct isci_host *phy_to_host(struct isci_phy *iphy)
+{
+ struct isci_phy *table = iphy - iphy->phy_index;
+ struct isci_host *ihost = container_of(table, typeof(*ihost), phys[0]);
+
+ return ihost;
+}
+
+static struct device *sciphy_to_dev(struct isci_phy *iphy)
+{
+ return &phy_to_host(iphy)->pdev->dev;
+}
+
static enum sci_status
sci_phy_transport_layer_initialization(struct isci_phy *iphy,
struct scu_transport_layer_registers __iomem *reg)
@@ -91,21 +114,23 @@ sci_phy_transport_layer_initialization(struct isci_phy *iphy,
static enum sci_status
sci_phy_link_layer_initialization(struct isci_phy *iphy,
- struct scu_link_layer_registers __iomem *reg)
+ struct scu_link_layer_registers __iomem *llr)
{
struct isci_host *ihost = iphy->owning_port->owning_controller;
+ struct sci_phy_user_params *phy_user;
+ struct sci_phy_oem_params *phy_oem;
int phy_idx = iphy->phy_index;
- struct sci_phy_user_params *phy_user = &ihost->user_parameters.phys[phy_idx];
- struct sci_phy_oem_params *phy_oem =
- &ihost->oem_parameters.phys[phy_idx];
- u32 phy_configuration;
struct sci_phy_cap phy_cap;
+ u32 phy_configuration;
u32 parity_check = 0;
u32 parity_count = 0;
u32 llctl, link_rate;
u32 clksm_value = 0;
+ u32 sp_timeouts = 0;
- iphy->link_layer_registers = reg;
+ phy_user = &ihost->user_parameters.phys[phy_idx];
+ phy_oem = &ihost->oem_parameters.phys[phy_idx];
+ iphy->link_layer_registers = llr;
/* Set our IDENTIFY frame data */
#define SCI_END_DEVICE 0x01
@@ -115,32 +140,26 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy,
SCU_SAS_TIID_GEN_BIT(STP_INITIATOR) |
SCU_SAS_TIID_GEN_BIT(DA_SATA_HOST) |
SCU_SAS_TIID_GEN_VAL(DEVICE_TYPE, SCI_END_DEVICE),
- &iphy->link_layer_registers->transmit_identification);
+ &llr->transmit_identification);
/* Write the device SAS Address */
- writel(0xFEDCBA98,
- &iphy->link_layer_registers->sas_device_name_high);
- writel(phy_idx, &iphy->link_layer_registers->sas_device_name_low);
+ writel(0xFEDCBA98, &llr->sas_device_name_high);
+ writel(phy_idx, &llr->sas_device_name_low);
/* Write the source SAS Address */
- writel(phy_oem->sas_address.high,
- &iphy->link_layer_registers->source_sas_address_high);
- writel(phy_oem->sas_address.low,
- &iphy->link_layer_registers->source_sas_address_low);
+ writel(phy_oem->sas_address.high, &llr->source_sas_address_high);
+ writel(phy_oem->sas_address.low, &llr->source_sas_address_low);
/* Clear and Set the PHY Identifier */
- writel(0, &iphy->link_layer_registers->identify_frame_phy_id);
- writel(SCU_SAS_TIPID_GEN_VALUE(ID, phy_idx),
- &iphy->link_layer_registers->identify_frame_phy_id);
+ writel(0, &llr->identify_frame_phy_id);
+ writel(SCU_SAS_TIPID_GEN_VALUE(ID, phy_idx), &llr->identify_frame_phy_id);
/* Change the initial state of the phy configuration register */
- phy_configuration =
- readl(&iphy->link_layer_registers->phy_configuration);
+ phy_configuration = readl(&llr->phy_configuration);
/* Hold OOB state machine in reset */
phy_configuration |= SCU_SAS_PCFG_GEN_BIT(OOB_RESET);
- writel(phy_configuration,
- &iphy->link_layer_registers->phy_configuration);
+ writel(phy_configuration, &llr->phy_configuration);
/* Configure the SNW capabilities */
phy_cap.all = 0;
@@ -148,15 +167,64 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy,
phy_cap.gen3_no_ssc = 1;
phy_cap.gen2_no_ssc = 1;
phy_cap.gen1_no_ssc = 1;
- if (ihost->oem_parameters.controller.do_enable_ssc == true) {
+ if (ihost->oem_parameters.controller.do_enable_ssc) {
+ struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe;
+ struct scu_afe_transceiver __iomem *xcvr = &afe->scu_afe_xcvr[phy_idx];
+ struct isci_pci_info *pci_info = to_pci_info(ihost->pdev);
+ bool en_sas = false;
+ bool en_sata = false;
+ u32 sas_type = 0;
+ u32 sata_spread = 0x2;
+ u32 sas_spread = 0x2;
+
phy_cap.gen3_ssc = 1;
phy_cap.gen2_ssc = 1;
phy_cap.gen1_ssc = 1;
+
+ if (pci_info->orom->hdr.version < ISCI_ROM_VER_1_1)
+ en_sas = en_sata = true;
+ else {
+ sata_spread = ihost->oem_parameters.controller.ssc_sata_tx_spread_level;
+ sas_spread = ihost->oem_parameters.controller.ssc_sas_tx_spread_level;
+
+ if (sata_spread)
+ en_sata = true;
+
+ if (sas_spread) {
+ en_sas = true;
+ sas_type = ihost->oem_parameters.controller.ssc_sas_tx_type;
+ }
+
+ }
+
+ if (en_sas) {
+ u32 reg;
+
+ reg = readl(&xcvr->afe_xcvr_control0);
+ reg |= (0x00100000 | (sas_type << 19));
+ writel(reg, &xcvr->afe_xcvr_control0);
+
+ reg = readl(&xcvr->afe_tx_ssc_control);
+ reg |= sas_spread << 8;
+ writel(reg, &xcvr->afe_tx_ssc_control);
+ }
+
+ if (en_sata) {
+ u32 reg;
+
+ reg = readl(&xcvr->afe_tx_ssc_control);
+ reg |= sata_spread;
+ writel(reg, &xcvr->afe_tx_ssc_control);
+
+ reg = readl(&llr->stp_control);
+ reg |= 1 << 12;
+ writel(reg, &llr->stp_control);
+ }
}
- /*
- * The SAS specification indicates that the phy_capabilities that
- * are transmitted shall have an even parity. Calculate the parity. */
+ /* The SAS specification indicates that the phy_capabilities that
+ * are transmitted shall have an even parity. Calculate the parity.
+ */
parity_check = phy_cap.all;
while (parity_check != 0) {
if (parity_check & 0x1)
@@ -164,20 +232,20 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy,
parity_check >>= 1;
}
- /*
- * If parity indicates there are an odd number of bits set, then
- * set the parity bit to 1 in the phy capabilities. */
+ /* If parity indicates there are an odd number of bits set, then
+ * set the parity bit to 1 in the phy capabilities.
+ */
if ((parity_count % 2) != 0)
phy_cap.parity = 1;
- writel(phy_cap.all, &iphy->link_layer_registers->phy_capabilities);
+ writel(phy_cap.all, &llr->phy_capabilities);
/* Set the enable spinup period but disable the ability to send
* notify enable spinup
*/
writel(SCU_ENSPINUP_GEN_VAL(COUNT,
phy_user->notify_enable_spin_up_insertion_frequency),
- &iphy->link_layer_registers->notify_enable_spinup_control);
+ &llr->notify_enable_spinup_control);
/* Write the ALIGN Insertion Ferequency for connected phy and
* inpendent of connected state
@@ -188,11 +256,13 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy,
clksm_value |= SCU_ALIGN_INSERTION_FREQUENCY_GEN_VAL(GENERAL,
phy_user->align_insertion_frequency);
- writel(clksm_value, &iphy->link_layer_registers->clock_skew_management);
+ writel(clksm_value, &llr->clock_skew_management);
- /* @todo Provide a way to write this register correctly */
- writel(0x02108421,
- &iphy->link_layer_registers->afe_lookup_table_control);
+ if (is_c0(ihost->pdev) || is_c1(ihost->pdev)) {
+ writel(0x04210400, &llr->afe_lookup_table_control);
+ writel(0x020A7C05, &llr->sas_primitive_timeout);
+ } else
+ writel(0x02108421, &llr->afe_lookup_table_control);
llctl = SCU_SAS_LLCTL_GEN_VAL(NO_OUTBOUND_TASK_TIMEOUT,
(u8)ihost->user_parameters.no_outbound_task_timeout);
@@ -209,20 +279,35 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy,
break;
}
llctl |= SCU_SAS_LLCTL_GEN_VAL(MAX_LINK_RATE, link_rate);
- writel(llctl, &iphy->link_layer_registers->link_layer_control);
+ writel(llctl, &llr->link_layer_control);
+
+ sp_timeouts = readl(&llr->sas_phy_timeouts);
+
+ /* Clear the default 0x36 (54us) RATE_CHANGE timeout value. */
+ sp_timeouts &= ~SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0xFF);
+
+ /* Set RATE_CHANGE timeout value to 0x3B (59us). This ensures SCU can
+ * lock with 3Gb drive when SCU max rate is set to 1.5Gb.
+ */
+ sp_timeouts |= SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0x3B);
+
+ writel(sp_timeouts, &llr->sas_phy_timeouts);
if (is_a2(ihost->pdev)) {
- /* Program the max ARB time for the PHY to 700us so we inter-operate with
- * the PMC expander which shuts down PHYs if the expander PHY generates too
- * many breaks. This time value will guarantee that the initiator PHY will
- * generate the break.
+ /* Program the max ARB time for the PHY to 700us so we
+ * inter-operate with the PMC expander which shuts down
+ * PHYs if the expander PHY generates too many breaks.
+ * This time value will guarantee that the initiator PHY
+ * will generate the break.
*/
writel(SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME,
- &iphy->link_layer_registers->maximum_arbitration_wait_timer_timeout);
+ &llr->maximum_arbitration_wait_timer_timeout);
}
- /* Disable link layer hang detection, rely on the OS timeout for I/O timeouts. */
- writel(0, &iphy->link_layer_registers->link_layer_hang_detection_timeout);
+ /* Disable link layer hang detection, rely on the OS timeout for
+ * I/O timeouts.
+ */
+ writel(0, &llr->link_layer_hang_detection_timeout);
/* We can exit the initial state to the stopped state */
sci_change_state(&iphy->sm, SCI_PHY_STOPPED);
@@ -230,9 +315,9 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy,
return SCI_SUCCESS;
}
-static void phy_sata_timeout(unsigned long data)
+static void phy_sata_timeout(struct timer_list *t)
{
- struct sci_timer *tmr = (struct sci_timer *)data;
+ struct sci_timer *tmr = timer_container_of(tmr, t, timer);
struct isci_phy *iphy = container_of(tmr, typeof(*iphy), sata_timer);
struct isci_host *ihost = iphy->owning_port->owning_controller;
unsigned long flags;
@@ -254,10 +339,11 @@ done:
}
/**
- * This method returns the port currently containing this phy. If the phy is
- * currently contained by the dummy port, then the phy is considered to not
- * be part of a port.
- * @sci_phy: This parameter specifies the phy for which to retrieve the
+ * phy_get_non_dummy_port() - This method returns the port currently containing
+ * this phy. If the phy is currently contained by the dummy port, then the phy
+ * is considered to not be part of a port.
+ *
+ * @iphy: This parameter specifies the phy for which to retrieve the
* containing port.
*
* This method returns a handle to a port that contains the supplied phy.
@@ -275,12 +361,8 @@ struct isci_port *phy_get_non_dummy_port(struct isci_phy *iphy)
return iphy->owning_port;
}
-/**
- * This method will assign a port to the phy object.
- * @out]: iphy This parameter specifies the phy for which to assign a port
- * object.
- *
- *
+/*
+ * sci_phy_set_port() - This method will assign a port to the phy object.
*/
void sci_phy_set_port(
struct isci_phy *iphy,
@@ -313,11 +395,11 @@ enum sci_status sci_phy_initialize(struct isci_phy *iphy,
}
/**
- * This method assigns the direct attached device ID for this phy.
+ * sci_phy_setup_transport() - This method assigns the direct attached device ID for this phy.
*
- * @iphy The phy for which the direct attached device id is to
+ * @iphy: The phy for which the direct attached device id is to
* be assigned.
- * @device_id The direct attached device ID to assign to the phy.
+ * @device_id: The direct attached device ID to assign to the phy.
* This will either be the RNi for the device or an invalid RNi if there
* is no current device assigned to the phy.
*/
@@ -384,8 +466,8 @@ enum sci_status sci_phy_start(struct isci_phy *iphy)
enum sci_phy_states state = iphy->sm.current_state_id;
if (state != SCI_PHY_STOPPED) {
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -410,8 +492,8 @@ enum sci_status sci_phy_stop(struct isci_phy *iphy)
case SCI_PHY_READY:
break;
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -424,8 +506,8 @@ enum sci_status sci_phy_reset(struct isci_phy *iphy)
enum sci_phy_states state = iphy->sm.current_state_id;
if (state != SCI_PHY_READY) {
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -474,8 +556,8 @@ enum sci_status sci_phy_consume_power_handler(struct isci_phy *iphy)
return SCI_SUCCESS;
}
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -495,7 +577,7 @@ static void sci_phy_start_sas_link_training(struct isci_phy *iphy)
sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SAS_SPEED_EN);
- iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SAS;
+ iphy->protocol = SAS_PROTOCOL_SSP;
}
static void sci_phy_start_sata_link_training(struct isci_phy *iphy)
@@ -506,13 +588,13 @@ static void sci_phy_start_sata_link_training(struct isci_phy *iphy)
*/
sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SATA_POWER);
- iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA;
+ iphy->protocol = SAS_PROTOCOL_SATA;
}
/**
* sci_phy_complete_link_training - perform processing common to
* all protocols upon completion of link training.
- * @sci_phy: This parameter specifies the phy object for which link training
+ * @iphy: This parameter specifies the phy object for which link training
* has completed.
* @max_link_rate: This parameter specifies the maximum link rate to be
* associated with this phy.
@@ -529,6 +611,73 @@ static void sci_phy_complete_link_training(struct isci_phy *iphy,
sci_change_state(&iphy->sm, next_state);
}
+static const char *phy_event_name(u32 event_code)
+{
+ switch (scu_get_event_code(event_code)) {
+ case SCU_EVENT_PORT_SELECTOR_DETECTED:
+ return "port selector";
+ case SCU_EVENT_SENT_PORT_SELECTION:
+ return "port selection";
+ case SCU_EVENT_HARD_RESET_TRANSMITTED:
+ return "tx hard reset";
+ case SCU_EVENT_HARD_RESET_RECEIVED:
+ return "rx hard reset";
+ case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT:
+ return "identify timeout";
+ case SCU_EVENT_LINK_FAILURE:
+ return "link fail";
+ case SCU_EVENT_SATA_SPINUP_HOLD:
+ return "sata spinup hold";
+ case SCU_EVENT_SAS_15_SSC:
+ case SCU_EVENT_SAS_15:
+ return "sas 1.5";
+ case SCU_EVENT_SAS_30_SSC:
+ case SCU_EVENT_SAS_30:
+ return "sas 3.0";
+ case SCU_EVENT_SAS_60_SSC:
+ case SCU_EVENT_SAS_60:
+ return "sas 6.0";
+ case SCU_EVENT_SATA_15_SSC:
+ case SCU_EVENT_SATA_15:
+ return "sata 1.5";
+ case SCU_EVENT_SATA_30_SSC:
+ case SCU_EVENT_SATA_30:
+ return "sata 3.0";
+ case SCU_EVENT_SATA_60_SSC:
+ case SCU_EVENT_SATA_60:
+ return "sata 6.0";
+ case SCU_EVENT_SAS_PHY_DETECTED:
+ return "sas detect";
+ case SCU_EVENT_SATA_PHY_DETECTED:
+ return "sata detect";
+ default:
+ return "unknown";
+ }
+}
+
+#define phy_event_dbg(iphy, state, code) \
+ dev_dbg(sciphy_to_dev(iphy), "phy-%d:%d: %s event: %s (%x)\n", \
+ phy_to_host(iphy)->id, iphy->phy_index, \
+ phy_state_name(state), phy_event_name(code), code)
+
+#define phy_event_warn(iphy, state, code) \
+ dev_warn(sciphy_to_dev(iphy), "phy-%d:%d: %s event: %s (%x)\n", \
+ phy_to_host(iphy)->id, iphy->phy_index, \
+ phy_state_name(state), phy_event_name(code), code)
+
+
+static void scu_link_layer_set_txcomsas_timeout(struct isci_phy *iphy, u32 timeout)
+{
+ u32 val;
+
+ /* Extend timeout */
+ val = readl(&iphy->link_layer_registers->transmit_comsas_signal);
+ val &= ~SCU_SAS_LLTXCOMSAS_GEN_VAL(NEGTIME, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_MASK);
+ val |= SCU_SAS_LLTXCOMSAS_GEN_VAL(NEGTIME, timeout);
+
+ writel(val, &iphy->link_layer_registers->transmit_comsas_signal);
+}
+
enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
{
enum sci_phy_states state = iphy->sm.current_state_id;
@@ -544,12 +693,15 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_phy_start_sata_link_training(iphy);
iphy->is_in_link_training = true;
break;
+ case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT:
+ /* Extend timeout value */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED);
+
+ /* Start the oob/sn state machine over again */
+ sci_change_state(&iphy->sm, SCI_PHY_STARTING);
+ break;
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__,
- event_code);
+ phy_event_dbg(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
@@ -582,17 +734,22 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_phy_start_sata_link_training(iphy);
break;
case SCU_EVENT_LINK_FAILURE:
+ /* Change the timeout value to default */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+
/* Link failure change state back to the starting state */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
- default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
+ case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT:
+ /* Extend the timeout value */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED);
+ /* Start the oob/sn state machine over again */
+ sci_change_state(&iphy->sm, SCI_PHY_STARTING);
+ break;
+ default:
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
- break;
}
return SCI_SUCCESS;
case SCI_PHY_SUB_AWAIT_IAF_UF:
@@ -609,37 +766,44 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_phy_start_sata_link_training(iphy);
break;
case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT:
+ /* Extend the timeout value */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED);
+
+ /* Start the oob/sn state machine over again */
+ sci_change_state(&iphy->sm, SCI_PHY_STARTING);
+ break;
case SCU_EVENT_LINK_FAILURE:
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+ fallthrough;
case SCU_EVENT_HARD_RESET_RECEIVED:
/* Start the oob/sn state machine over again */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
case SCI_PHY_SUB_AWAIT_SAS_POWER:
switch (scu_get_event_code(event_code)) {
case SCU_EVENT_LINK_FAILURE:
+ /* Change the timeout value to default */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+
/* Link failure change state back to the starting state */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received unexpected "
- "event_code %x\n",
- __func__,
- event_code);
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
case SCI_PHY_SUB_AWAIT_SATA_POWER:
switch (scu_get_event_code(event_code)) {
case SCU_EVENT_LINK_FAILURE:
+ /* Change the timeout value to default */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+
/* Link failure change state back to the starting state */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
@@ -657,17 +821,16 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
case SCI_PHY_SUB_AWAIT_SATA_PHY_EN:
switch (scu_get_event_code(event_code)) {
case SCU_EVENT_LINK_FAILURE:
+ /* Change the timeout value to default */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+
/* Link failure change state back to the starting state */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
@@ -677,7 +840,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
*/
break;
case SCU_EVENT_SATA_PHY_DETECTED:
- iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA;
+ iphy->protocol = SAS_PROTOCOL_SATA;
/* We have received the SATA PHY notification change state */
sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SATA_SPEED_EN);
@@ -689,13 +852,8 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_phy_start_sas_link_training(iphy);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__,
- event_code);
-
- return SCI_FAILURE;;
+ phy_event_warn(iphy, state, event_code);
+ return SCI_FAILURE;
}
return SCI_SUCCESS;
case SCI_PHY_SUB_AWAIT_SATA_SPEED_EN:
@@ -721,6 +879,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
SCI_PHY_SUB_AWAIT_SIG_FIS_UF);
break;
case SCU_EVENT_LINK_FAILURE:
+ /* Change the timeout value to default */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+
/* Link failure change state back to the starting state */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
@@ -731,11 +892,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_phy_start_sas_link_training(iphy);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
@@ -748,38 +905,43 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
break;
case SCU_EVENT_LINK_FAILURE:
+ /* Change the timeout value to default */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+
/* Link failure change state back to the starting state */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__,
- event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
case SCI_PHY_READY:
switch (scu_get_event_code(event_code)) {
case SCU_EVENT_LINK_FAILURE:
+ /* Set default timeout */
+ scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT);
+
/* Link failure change state back to the starting state */
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
case SCU_EVENT_BROADCAST_CHANGE:
+ case SCU_EVENT_BROADCAST_SES:
+ case SCU_EVENT_BROADCAST_RESERVED0:
+ case SCU_EVENT_BROADCAST_RESERVED1:
+ case SCU_EVENT_BROADCAST_EXPANDER:
+ case SCU_EVENT_BROADCAST_AEN:
/* Broadcast change received. Notify the port. */
if (phy_get_non_dummy_port(iphy) != NULL)
sci_port_broadcast_change_received(iphy->owning_port, iphy);
else
iphy->bcn_received_while_port_unassigned = true;
break;
+ case SCU_EVENT_BROADCAST_RESERVED3:
+ case SCU_EVENT_BROADCAST_RESERVED4:
default:
- dev_warn(sciphy_to_dev(iphy),
- "%sP SCIC PHY 0x%p ready state machine received "
- "unexpected event_code %x\n",
- __func__, iphy, event_code);
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE_INVALID_STATE;
}
return SCI_SUCCESS;
@@ -790,18 +952,13 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: SCIC PHY 0x%p resetting state machine received "
- "unexpected event_code %x\n",
- __func__, iphy, event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE_INVALID_STATE;
- break;
}
return SCI_SUCCESS;
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -894,8 +1051,8 @@ enum sci_status sci_phy_frame_handler(struct isci_phy *iphy, u32 frame_index)
return result;
}
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1007,8 +1164,8 @@ static void sci_phy_starting_final_substate_enter(struct sci_base_state_machine
}
/**
- *
- * @sci_phy: This is the struct isci_phy object to stop.
+ * scu_link_layer_stop_protocol_engine()
+ * @iphy: This is the struct isci_phy object to stop.
*
* This method will stop the struct isci_phy object. This does not reset the
* protocol engine it just suspends it and places it in a state where it will
@@ -1036,28 +1193,31 @@ static void scu_link_layer_stop_protocol_engine(
writel(enable_spinup_value, &iphy->link_layer_registers->notify_enable_spinup_control);
}
-/**
- *
- *
- * This method will start the OOB/SN state machine for this struct isci_phy object.
- */
-static void scu_link_layer_start_oob(
- struct isci_phy *iphy)
+static void scu_link_layer_start_oob(struct isci_phy *iphy)
{
- u32 scu_sas_pcfg_value;
-
- scu_sas_pcfg_value =
- readl(&iphy->link_layer_registers->phy_configuration);
- scu_sas_pcfg_value |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE);
- scu_sas_pcfg_value &=
- ~(SCU_SAS_PCFG_GEN_BIT(OOB_RESET) |
- SCU_SAS_PCFG_GEN_BIT(HARD_RESET));
- writel(scu_sas_pcfg_value,
- &iphy->link_layer_registers->phy_configuration);
+ struct scu_link_layer_registers __iomem *ll = iphy->link_layer_registers;
+ u32 val;
+
+ /** Reset OOB sequence - start */
+ val = readl(&ll->phy_configuration);
+ val &= ~(SCU_SAS_PCFG_GEN_BIT(OOB_RESET) |
+ SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE) |
+ SCU_SAS_PCFG_GEN_BIT(HARD_RESET));
+ writel(val, &ll->phy_configuration);
+ readl(&ll->phy_configuration); /* flush */
+ /** Reset OOB sequence - end */
+
+ /** Start OOB sequence - start */
+ val = readl(&ll->phy_configuration);
+ val |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE);
+ writel(val, &ll->phy_configuration);
+ readl(&ll->phy_configuration); /* flush */
+ /** Start OOB sequence - end */
}
/**
- *
+ * scu_link_layer_tx_hard_reset()
+ * @iphy: This is the struct isci_phy object to stop.
*
* This method will transmit a hard reset request on the specified phy. The SCU
* hardware requires that we reset the OOB state machine and set the hard reset
@@ -1074,6 +1234,7 @@ static void scu_link_layer_tx_hard_reset(
* to the starting state. */
phy_configuration_value =
readl(&iphy->link_layer_registers->phy_configuration);
+ phy_configuration_value &= ~(SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE));
phy_configuration_value |=
(SCU_SAS_PCFG_GEN_BIT(HARD_RESET) |
SCU_SAS_PCFG_GEN_BIT(OOB_RESET));
@@ -1115,7 +1276,7 @@ static void sci_phy_starting_state_enter(struct sci_base_state_machine *sm)
scu_link_layer_start_oob(iphy);
/* We don't know what kind of phy we are going to be just yet */
- iphy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN;
+ iphy->protocol = SAS_PROTOCOL_NONE;
iphy->bcn_received_while_port_unassigned = false;
if (iphy->sm.previous_state_id == SCI_PHY_READY)
@@ -1150,7 +1311,7 @@ static void sci_phy_resetting_state_enter(struct sci_base_state_machine *sm)
*/
sci_port_deactivate_phy(iphy->owning_port, iphy, false);
- if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) {
+ if (iphy->protocol == SAS_PROTOCOL_SSP) {
scu_link_layer_tx_hard_reset(iphy);
} else {
/* The SCU does not need to have a discrete reset state so
@@ -1216,7 +1377,7 @@ void sci_phy_construct(struct isci_phy *iphy,
iphy->owning_port = iport;
iphy->phy_index = phy_index;
iphy->bcn_received_while_port_unassigned = false;
- iphy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN;
+ iphy->protocol = SAS_PROTOCOL_NONE;
iphy->link_layer_registers = NULL;
iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN;
@@ -1236,7 +1397,6 @@ void isci_phy_init(struct isci_phy *iphy, struct isci_host *ihost, int index)
sas_addr = cpu_to_be64(sci_sas_addr);
memcpy(iphy->sas_addr, &sas_addr, sizeof(sas_addr));
- iphy->isci_port = NULL;
iphy->sas_phy.enabled = 0;
iphy->sas_phy.id = index;
iphy->sas_phy.sas_addr = &iphy->sas_addr[0];
@@ -1244,10 +1404,8 @@ void isci_phy_init(struct isci_phy *iphy, struct isci_host *ihost, int index)
iphy->sas_phy.ha = &ihost->sas_ha;
iphy->sas_phy.lldd_phy = iphy;
iphy->sas_phy.enabled = 1;
- iphy->sas_phy.class = SAS;
iphy->sas_phy.iproto = SAS_PROTOCOL_ALL;
iphy->sas_phy.tproto = 0;
- iphy->sas_phy.type = PHY_TYPE_PHYSICAL;
iphy->sas_phy.role = PHY_ROLE_INITIATOR;
iphy->sas_phy.oob_mode = OOB_NOT_CONNECTED;
iphy->sas_phy.linkrate = SAS_LINK_RATE_UNKNOWN;
@@ -1258,7 +1416,7 @@ void isci_phy_init(struct isci_phy *iphy, struct isci_host *ihost, int index)
/**
* isci_phy_control() - This function is one of the SAS Domain Template
* functions. This is a phy management function.
- * @phy: This parameter specifies the sphy being controlled.
+ * @sas_phy: This parameter specifies the sphy being controlled.
* @func: This parameter specifies the phy control function being invoked.
* @buf: This parameter is specific to the phy function being invoked.
*
@@ -1270,36 +1428,48 @@ int isci_phy_control(struct asd_sas_phy *sas_phy,
{
int ret = 0;
struct isci_phy *iphy = sas_phy->lldd_phy;
- struct isci_port *iport = iphy->isci_port;
+ struct asd_sas_port *port = sas_phy->port;
struct isci_host *ihost = sas_phy->ha->lldd_ha;
unsigned long flags;
dev_dbg(&ihost->pdev->dev,
"%s: phy %p; func %d; buf %p; isci phy %p, port %p\n",
- __func__, sas_phy, func, buf, iphy, iport);
+ __func__, sas_phy, func, buf, iphy, port);
switch (func) {
case PHY_FUNC_DISABLE:
spin_lock_irqsave(&ihost->scic_lock, flags);
+ scu_link_layer_start_oob(iphy);
sci_phy_stop(iphy);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
break;
case PHY_FUNC_LINK_RESET:
spin_lock_irqsave(&ihost->scic_lock, flags);
+ scu_link_layer_start_oob(iphy);
sci_phy_stop(iphy);
sci_phy_start(iphy);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
break;
case PHY_FUNC_HARD_RESET:
- if (!iport)
+ if (!port)
return -ENODEV;
- /* Perform the port reset. */
- ret = isci_port_perform_hard_reset(ihost, iport, iphy);
+ ret = isci_port_perform_hard_reset(ihost, port->lldd_port, iphy);
break;
+ case PHY_FUNC_GET_EVENTS: {
+ struct scu_link_layer_registers __iomem *r;
+ struct sas_phy *phy = sas_phy->phy;
+
+ r = iphy->link_layer_registers;
+ phy->running_disparity_error_count = readl(&r->running_disparity_error_count);
+ phy->loss_of_dword_sync_count = readl(&r->loss_of_sync_error_count);
+ phy->phy_reset_problem_count = readl(&r->phy_reset_problem_count);
+ phy->invalid_dword_count = readl(&r->invalid_dword_counter);
+ break;
+ }
default:
dev_dbg(&ihost->pdev->dev,
diff --git a/drivers/scsi/isci/phy.h b/drivers/scsi/isci/phy.h
index 67699c8e321c..5aaf95b14b2e 100644
--- a/drivers/scsi/isci/phy.h
+++ b/drivers/scsi/isci/phy.h
@@ -76,13 +76,6 @@
*/
#define SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT 250
-enum sci_phy_protocol {
- SCIC_SDS_PHY_PROTOCOL_UNKNOWN,
- SCIC_SDS_PHY_PROTOCOL_SAS,
- SCIC_SDS_PHY_PROTOCOL_SATA,
- SCIC_SDS_MAX_PHY_PROTOCOLS
-};
-
/**
* isci_phy - hba local phy infrastructure
* @sm:
@@ -95,7 +88,7 @@ struct isci_phy {
struct sci_base_state_machine sm;
struct isci_port *owning_port;
enum sas_linkrate max_negotiated_speed;
- enum sci_phy_protocol protocol;
+ enum sas_protocol protocol;
u8 phy_index;
bool bcn_received_while_port_unassigned;
bool is_in_link_training;
@@ -103,7 +96,6 @@ struct isci_phy {
struct scu_transport_layer_registers __iomem *transport_layer_registers;
struct scu_link_layer_registers __iomem *link_layer_registers;
struct asd_sas_phy sas_phy;
- struct isci_port *isci_port;
u8 sas_addr[SAS_ADDR_SIZE];
union {
struct sas_identify_frame iaf;
@@ -344,101 +336,65 @@ enum sci_phy_counter_id {
SCIC_PHY_COUNTER_SN_DWORD_SYNC_ERROR
};
-enum sci_phy_states {
- /**
- * Simply the initial state for the base domain state machine.
- */
- SCI_PHY_INITIAL,
-
- /**
- * This state indicates that the phy has successfully been stopped.
- * In this state no new IO operations are permitted on this phy.
- * This state is entered from the INITIAL state.
- * This state is entered from the STARTING state.
- * This state is entered from the READY state.
- * This state is entered from the RESETTING state.
- */
- SCI_PHY_STOPPED,
-
- /**
- * This state indicates that the phy is in the process of becomming
- * ready. In this state no new IO operations are permitted on this phy.
- * This state is entered from the STOPPED state.
- * This state is entered from the READY state.
- * This state is entered from the RESETTING state.
- */
- SCI_PHY_STARTING,
-
- /**
- * Initial state
- */
- SCI_PHY_SUB_INITIAL,
-
- /**
- * Wait state for the hardware OSSP event type notification
- */
- SCI_PHY_SUB_AWAIT_OSSP_EN,
-
- /**
- * Wait state for the PHY speed notification
- */
- SCI_PHY_SUB_AWAIT_SAS_SPEED_EN,
-
- /**
- * Wait state for the IAF Unsolicited frame notification
- */
- SCI_PHY_SUB_AWAIT_IAF_UF,
-
- /**
- * Wait state for the request to consume power
- */
- SCI_PHY_SUB_AWAIT_SAS_POWER,
-
- /**
- * Wait state for request to consume power
- */
- SCI_PHY_SUB_AWAIT_SATA_POWER,
-
- /**
- * Wait state for the SATA PHY notification
- */
- SCI_PHY_SUB_AWAIT_SATA_PHY_EN,
-
- /**
- * Wait for the SATA PHY speed notification
- */
- SCI_PHY_SUB_AWAIT_SATA_SPEED_EN,
-
- /**
- * Wait state for the SIGNATURE FIS unsolicited frame notification
- */
- SCI_PHY_SUB_AWAIT_SIG_FIS_UF,
-
- /**
- * Exit state for this state machine
- */
- SCI_PHY_SUB_FINAL,
-
- /**
- * This state indicates the the phy is now ready. Thus, the user
- * is able to perform IO operations utilizing this phy as long as it
- * is currently part of a valid port.
- * This state is entered from the STARTING state.
- */
- SCI_PHY_READY,
-
- /**
- * This state indicates that the phy is in the process of being reset.
- * In this state no new IO operations are permitted on this phy.
- * This state is entered from the READY state.
- */
- SCI_PHY_RESETTING,
-
- /**
- * Simply the final state for the base phy state machine.
- */
- SCI_PHY_FINAL,
-};
+/**
+ * enum sci_phy_states - phy state machine states
+ * @SCI_PHY_INITIAL: Simply the initial state for the base domain state
+ * machine.
+ * @SCI_PHY_STOPPED: phy has successfully been stopped. In this state
+ * no new IO operations are permitted on this phy.
+ * @SCI_PHY_STARTING: the phy is in the process of becomming ready. In
+ * this state no new IO operations are permitted on
+ * this phy.
+ * @SCI_PHY_SUB_INITIAL: Initial state
+ * @SCI_PHY_SUB_AWAIT_OSSP_EN: Wait state for the hardware OSSP event
+ * type notification
+ * @SCI_PHY_SUB_AWAIT_SAS_SPEED_EN: Wait state for the PHY speed
+ * notification
+ * @SCI_PHY_SUB_AWAIT_IAF_UF: Wait state for the IAF Unsolicited frame
+ * notification
+ * @SCI_PHY_SUB_AWAIT_SAS_POWER: Wait state for the request to consume
+ * power
+ * @SCI_PHY_SUB_AWAIT_SATA_POWER: Wait state for request to consume
+ * power
+ * @SCI_PHY_SUB_AWAIT_SATA_PHY_EN: Wait state for the SATA PHY
+ * notification
+ * @SCI_PHY_SUB_AWAIT_SATA_SPEED_EN: Wait for the SATA PHY speed
+ * notification
+ * @SCI_PHY_SUB_AWAIT_SIG_FIS_UF: Wait state for the SIGNATURE FIS
+ * unsolicited frame notification
+ * @SCI_PHY_SUB_FINAL: Exit state for this state machine
+ * @SCI_PHY_READY: phy is now ready. Thus, the user is able to perform
+ * IO operations utilizing this phy as long as it is
+ * currently part of a valid port. This state is
+ * entered from the STARTING state.
+ * @SCI_PHY_RESETTING: phy is in the process of being reset. In this
+ * state no new IO operations are permitted on this
+ * phy. This state is entered from the READY state.
+ * @SCI_PHY_FINAL: Simply the final state for the base phy state
+ * machine.
+ */
+#define PHY_STATES {\
+ C(PHY_INITIAL),\
+ C(PHY_STOPPED),\
+ C(PHY_STARTING),\
+ C(PHY_SUB_INITIAL),\
+ C(PHY_SUB_AWAIT_OSSP_EN),\
+ C(PHY_SUB_AWAIT_SAS_SPEED_EN),\
+ C(PHY_SUB_AWAIT_IAF_UF),\
+ C(PHY_SUB_AWAIT_SAS_POWER),\
+ C(PHY_SUB_AWAIT_SATA_POWER),\
+ C(PHY_SUB_AWAIT_SATA_PHY_EN),\
+ C(PHY_SUB_AWAIT_SATA_SPEED_EN),\
+ C(PHY_SUB_AWAIT_SIG_FIS_UF),\
+ C(PHY_SUB_FINAL),\
+ C(PHY_READY),\
+ C(PHY_RESETTING),\
+ C(PHY_FINAL),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_phy_states PHY_STATES;
+#undef C
void sci_phy_construct(
struct isci_phy *iphy,
@@ -491,7 +447,6 @@ void sci_phy_get_attached_sas_address(
struct isci_phy *iphy,
struct sci_sas_address *sas_address);
-struct sci_phy_proto;
void sci_phy_get_protocols(
struct isci_phy *iphy,
struct sci_phy_proto *protocols);
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 8f6f9b77e41a..10bd2aac2cb4 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -60,18 +60,29 @@
#define SCIC_SDS_PORT_HARD_RESET_TIMEOUT (1000)
#define SCU_DUMMY_INDEX (0xFFFF)
-static void isci_port_change_state(struct isci_port *iport, enum isci_status status)
+#undef C
+#define C(a) (#a)
+static const char *port_state_name(enum sci_port_states state)
{
- unsigned long flags;
+ static const char * const strings[] = PORT_STATES;
+
+ return strings[state];
+}
+#undef C
- dev_dbg(&iport->isci_host->pdev->dev,
- "%s: iport = %p, state = 0x%x\n",
- __func__, iport, status);
+static struct device *sciport_to_dev(struct isci_port *iport)
+{
+ int i = iport->physical_port_index;
+ struct isci_port *table;
+ struct isci_host *ihost;
- /* XXX pointless lock */
- spin_lock_irqsave(&iport->state_lock, flags);
- iport->status = status;
- spin_unlock_irqrestore(&iport->state_lock, flags);
+ if (i == SCIC_SDS_DUMMY_PORT)
+ i = SCI_MAX_PORTS+1;
+
+ table = iport - i;
+ ihost = container_of(table, typeof(*ihost), ports[0]);
+
+ return &ihost->pdev->dev;
}
static void sci_port_get_protocols(struct isci_port *iport, struct sci_phy_proto *proto)
@@ -104,9 +115,9 @@ static u32 sci_port_get_phys(struct isci_port *iport)
/**
* sci_port_get_properties() - This method simply returns the properties
* regarding the port, such as: physical index, protocols, sas address, etc.
- * @port: this parameter specifies the port for which to retrieve the physical
+ * @iport: this parameter specifies the port for which to retrieve the physical
* index.
- * @properties: This parameter specifies the properties structure into which to
+ * @prop: This parameter specifies the properties structure into which to
* copy the requested information.
*
* Indicate if the user specified a valid port. SCI_SUCCESS This value is
@@ -114,7 +125,7 @@ static u32 sci_port_get_phys(struct isci_port *iport)
* value is returned if the specified port is not valid. When this value is
* returned, no data is copied to the properties output parameter.
*/
-static enum sci_status sci_port_get_properties(struct isci_port *iport,
+enum sci_status sci_port_get_properties(struct isci_port *iport,
struct sci_port_properties *prop)
{
if (!iport || iport->logical_port_index == SCIC_SDS_DUMMY_PORT)
@@ -145,48 +156,16 @@ static void sci_port_bcn_enable(struct isci_port *iport)
}
}
-/* called under sci_lock to stabilize phy:port associations */
-void isci_port_bcn_enable(struct isci_host *ihost, struct isci_port *iport)
-{
- int i;
-
- clear_bit(IPORT_BCN_BLOCKED, &iport->flags);
- wake_up(&ihost->eventq);
-
- if (!test_and_clear_bit(IPORT_BCN_PENDING, &iport->flags))
- return;
-
- for (i = 0; i < ARRAY_SIZE(iport->phy_table); i++) {
- struct isci_phy *iphy = iport->phy_table[i];
-
- if (!iphy)
- continue;
-
- ihost->sas_ha.notify_port_event(&iphy->sas_phy,
- PORTE_BROADCAST_RCVD);
- break;
- }
-}
-
static void isci_port_bc_change_received(struct isci_host *ihost,
struct isci_port *iport,
struct isci_phy *iphy)
{
- if (iport && test_bit(IPORT_BCN_BLOCKED, &iport->flags)) {
- dev_dbg(&ihost->pdev->dev,
- "%s: disabled BCN; isci_phy = %p, sas_phy = %p\n",
- __func__, iphy, &iphy->sas_phy);
- set_bit(IPORT_BCN_PENDING, &iport->flags);
- atomic_inc(&iport->event);
- wake_up(&ihost->eventq);
- } else {
- dev_dbg(&ihost->pdev->dev,
- "%s: isci_phy = %p, sas_phy = %p\n",
- __func__, iphy, &iphy->sas_phy);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_phy = %p, sas_phy = %p\n",
+ __func__, iphy, &iphy->sas_phy);
- ihost->sas_ha.notify_port_event(&iphy->sas_phy,
- PORTE_BROADCAST_RCVD);
- }
+ sas_notify_port_event(&iphy->sas_phy,
+ PORTE_BROADCAST_RCVD, GFP_ATOMIC);
sci_port_bcn_enable(iport);
}
@@ -198,21 +177,15 @@ static void isci_port_link_up(struct isci_host *isci_host,
struct sci_port_properties properties;
unsigned long success = true;
- BUG_ON(iphy->isci_port != NULL);
-
- iphy->isci_port = iport;
-
dev_dbg(&isci_host->pdev->dev,
"%s: isci_port = %p\n",
__func__, iport);
spin_lock_irqsave(&iphy->sas_phy.frame_rcvd_lock, flags);
- isci_port_change_state(iphy->isci_port, isci_starting);
-
sci_port_get_properties(iport, &properties);
- if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) {
+ if (iphy->protocol == SAS_PROTOCOL_SATA) {
u64 attached_sas_address;
iphy->sas_phy.oob_mode = SATA_OOB_MODE;
@@ -232,7 +205,7 @@ static void isci_port_link_up(struct isci_host *isci_host,
memcpy(&iphy->sas_phy.attached_sas_addr,
&attached_sas_address, sizeof(attached_sas_address));
- } else if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) {
+ } else if (iphy->protocol == SAS_PROTOCOL_SSP) {
iphy->sas_phy.oob_mode = SAS_OOB_MODE;
iphy->sas_phy.frame_rcvd_size = sizeof(struct sas_identify_frame);
@@ -240,7 +213,7 @@ static void isci_port_link_up(struct isci_host *isci_host,
memcpy(iphy->sas_phy.attached_sas_addr,
iphy->frame_rcvd.iaf.sas_addr, SAS_ADDR_SIZE);
} else {
- dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__);
+ dev_err(&isci_host->pdev->dev, "%s: unknown target\n", __func__);
success = false;
}
@@ -251,8 +224,8 @@ static void isci_port_link_up(struct isci_host *isci_host,
/* Notify libsas that we have an address frame, if indeed
* we've found an SSP, SMP, or STP target */
if (success)
- isci_host->sas_ha.notify_port_event(&iphy->sas_phy,
- PORTE_BYTES_DMAED);
+ sas_notify_port_event(&iphy->sas_phy,
+ PORTE_BYTES_DMAED, GFP_ATOMIC);
}
@@ -260,8 +233,8 @@ static void isci_port_link_up(struct isci_host *isci_host,
* isci_port_link_down() - This function is called by the sci core when a link
* becomes inactive.
* @isci_host: This parameter specifies the isci host object.
- * @phy: This parameter specifies the isci phy with the active link.
- * @port: This parameter specifies the isci port with the active link.
+ * @isci_phy: This parameter specifies the isci phy with the active link.
+ * @isci_port: This parameter specifies the isci port with the active link.
*
*/
static void isci_port_link_down(struct isci_host *isci_host,
@@ -278,14 +251,11 @@ static void isci_port_link_down(struct isci_host *isci_host,
/* check to see if this is the last phy on this port. */
if (isci_phy->sas_phy.port &&
isci_phy->sas_phy.port->num_phys == 1) {
- atomic_inc(&isci_port->event);
- isci_port_bcn_enable(isci_host, isci_port);
-
/* change the state for all devices on this port. The
- * next task sent to this device will be returned as
- * SAS_TASK_UNDELIVERED, and the scsi mid layer will
- * remove the target
- */
+ * next task sent to this device will be returned as
+ * SAS_TASK_UNDELIVERED, and the scsi mid layer will
+ * remove the target
+ */
list_for_each_entry(isci_device,
&isci_port->remote_dev_list,
node) {
@@ -295,65 +265,50 @@ static void isci_port_link_down(struct isci_host *isci_host,
set_bit(IDEV_GONE, &isci_device->flags);
}
}
- isci_port_change_state(isci_port, isci_stopping);
}
/* Notify libsas of the borken link, this will trigger calls to our
* isci_port_deformed and isci_dev_gone functions.
*/
sas_phy_disconnected(&isci_phy->sas_phy);
- isci_host->sas_ha.notify_phy_event(&isci_phy->sas_phy,
- PHYE_LOSS_OF_SIGNAL);
-
- isci_phy->isci_port = NULL;
+ sas_notify_phy_event(&isci_phy->sas_phy,
+ PHYE_LOSS_OF_SIGNAL, GFP_ATOMIC);
dev_dbg(&isci_host->pdev->dev,
"%s: isci_port = %p - Done\n", __func__, isci_port);
}
-
-/**
- * isci_port_ready() - This function is called by the sci core when a link
- * becomes ready.
- * @isci_host: This parameter specifies the isci host object.
- * @port: This parameter specifies the sci port with the active link.
- *
- */
-static void isci_port_ready(struct isci_host *isci_host, struct isci_port *isci_port)
+static bool is_port_ready_state(enum sci_port_states state)
{
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_port = %p\n", __func__, isci_port);
-
- complete_all(&isci_port->start_complete);
- isci_port_change_state(isci_port, isci_ready);
- return;
+ switch (state) {
+ case SCI_PORT_READY:
+ case SCI_PORT_SUB_WAITING:
+ case SCI_PORT_SUB_OPERATIONAL:
+ case SCI_PORT_SUB_CONFIGURING:
+ return true;
+ default:
+ return false;
+ }
}
-/**
- * isci_port_not_ready() - This function is called by the sci core when a link
- * is not ready. All remote devices on this link will be removed if they are
- * in the stopping state.
- * @isci_host: This parameter specifies the isci host object.
- * @port: This parameter specifies the sci port with the active link.
- *
- */
-static void isci_port_not_ready(struct isci_host *isci_host, struct isci_port *isci_port)
+/* flag dummy rnc hanling when exiting a ready state */
+static void port_state_machine_change(struct isci_port *iport,
+ enum sci_port_states state)
{
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_port = %p\n", __func__, isci_port);
-}
+ struct sci_base_state_machine *sm = &iport->sm;
+ enum sci_port_states old_state = sm->current_state_id;
-static void isci_port_stop_complete(struct isci_host *ihost,
- struct isci_port *iport,
- enum sci_status completion_status)
-{
- dev_dbg(&ihost->pdev->dev, "Port stop complete\n");
+ if (is_port_ready_state(old_state) && !is_port_ready_state(state))
+ iport->ready_exit = true;
+
+ sci_change_state(sm, state);
+ iport->ready_exit = false;
}
/**
* isci_port_hard_reset_complete() - This function is called by the sci core
* when the hard reset complete notification has been received.
- * @port: This parameter specifies the sci port with the active link.
+ * @isci_port: This parameter specifies the sci port with the active link.
* @completion_status: This parameter specifies the core status for the reset
* process.
*
@@ -361,14 +316,37 @@ static void isci_port_stop_complete(struct isci_host *ihost,
static void isci_port_hard_reset_complete(struct isci_port *isci_port,
enum sci_status completion_status)
{
- dev_dbg(&isci_port->isci_host->pdev->dev,
+ struct isci_host *ihost = isci_port->owning_controller;
+
+ dev_dbg(&ihost->pdev->dev,
"%s: isci_port = %p, completion_status=%x\n",
__func__, isci_port, completion_status);
/* Save the status of the hard reset from the port. */
isci_port->hard_reset_status = completion_status;
- complete_all(&isci_port->hard_reset_complete);
+ if (completion_status != SCI_SUCCESS) {
+
+ /* The reset failed. The port state is now SCI_PORT_FAILED. */
+ if (isci_port->active_phy_mask == 0) {
+ int phy_idx = isci_port->last_active_phy;
+ struct isci_phy *iphy = &ihost->phys[phy_idx];
+
+ /* Generate the link down now to the host, since it
+ * was intercepted by the hard reset state machine when
+ * it really happened.
+ */
+ isci_port_link_down(ihost, iphy, isci_port);
+ }
+ /* Advance the port state so that link state changes will be
+ * noticed.
+ */
+ port_state_machine_change(isci_port, SCI_PORT_SUB_WAITING);
+
+ }
+ clear_bit(IPORT_RESET_PENDING, &isci_port->state);
+ wake_up(&ihost->eventq);
+
}
/* This method will return a true value if the specified phy can be assigned to
@@ -417,9 +395,10 @@ bool sci_port_is_valid_phy_assignment(struct isci_port *iport, u32 phy_index)
}
/**
- *
- * @sci_port: This is the port object for which to determine if the phy mask
+ * sci_port_is_phy_mask_valid()
+ * @iport: This is the port object for which to determine if the phy mask
* can be supported.
+ * @phy_mask: Phy mask belonging to this port
*
* This method will return a true value if the port's phy mask can be supported
* by the SCU. The following is a list of valid PHY mask configurations for
@@ -540,7 +519,7 @@ void sci_port_get_attached_sas_address(struct isci_port *iport, struct sci_sas_a
*/
iphy = sci_port_get_a_connected_phy(iport);
if (iphy) {
- if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA) {
+ if (iphy->protocol != SAS_PROTOCOL_SATA) {
sci_phy_get_attached_sas_address(iphy, sas);
} else {
sci_phy_get_sas_address(iphy, sas);
@@ -555,7 +534,7 @@ void sci_port_get_attached_sas_address(struct isci_port *iport, struct sci_sas_a
/**
* sci_port_construct_dummy_rnc() - create dummy rnc for si workaround
*
- * @sci_port: logical port on which we need to create the remote node context
+ * @iport: logical port on which we need to create the remote node context
* @rni: remote node index for this remote node context.
*
* This routine will construct a dummy remote node context data structure
@@ -635,19 +614,26 @@ void sci_port_setup_transports(struct isci_port *iport, u32 device_id)
}
}
-static void sci_port_activate_phy(struct isci_port *iport, struct isci_phy *iphy,
- bool do_notify_user)
+static void sci_port_resume_phy(struct isci_port *iport, struct isci_phy *iphy)
+{
+ sci_phy_resume(iphy);
+ iport->enabled_phy_mask |= 1 << iphy->phy_index;
+}
+
+static void sci_port_activate_phy(struct isci_port *iport,
+ struct isci_phy *iphy,
+ u8 flags)
{
struct isci_host *ihost = iport->owning_controller;
- if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA)
+ if (iphy->protocol != SAS_PROTOCOL_SATA && (flags & PF_RESUME))
sci_phy_resume(iphy);
iport->active_phy_mask |= 1 << iphy->phy_index;
sci_controller_clear_invalid_phy(ihost, iphy);
- if (do_notify_user == true)
+ if (flags & PF_NOTIFY)
isci_port_link_up(ihost, iport, iphy);
}
@@ -657,12 +643,19 @@ void sci_port_deactivate_phy(struct isci_port *iport, struct isci_phy *iphy,
struct isci_host *ihost = iport->owning_controller;
iport->active_phy_mask &= ~(1 << iphy->phy_index);
+ iport->enabled_phy_mask &= ~(1 << iphy->phy_index);
+ if (!iport->active_phy_mask)
+ iport->last_active_phy = iphy->phy_index;
iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN;
- /* Re-assign the phy back to the LP as if it were a narrow port */
- writel(iphy->phy_index,
- &iport->port_pe_configuration_register[iphy->phy_index]);
+ /* Re-assign the phy back to the LP as if it were a narrow port for APC
+ * mode. For MPC mode, the phy will remain in the port.
+ */
+ if (iport->owning_controller->oem_parameters.controller.mode_type ==
+ SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE)
+ writel(iphy->phy_index,
+ &iport->port_pe_configuration_register[iphy->phy_index]);
if (do_notify_user == true)
isci_port_link_down(ihost, iphy, iport);
@@ -683,49 +676,20 @@ static void sci_port_invalid_link_up(struct isci_port *iport, struct isci_phy *i
}
}
-static bool is_port_ready_state(enum sci_port_states state)
-{
- switch (state) {
- case SCI_PORT_READY:
- case SCI_PORT_SUB_WAITING:
- case SCI_PORT_SUB_OPERATIONAL:
- case SCI_PORT_SUB_CONFIGURING:
- return true;
- default:
- return false;
- }
-}
-
-/* flag dummy rnc hanling when exiting a ready state */
-static void port_state_machine_change(struct isci_port *iport,
- enum sci_port_states state)
-{
- struct sci_base_state_machine *sm = &iport->sm;
- enum sci_port_states old_state = sm->current_state_id;
-
- if (is_port_ready_state(old_state) && !is_port_ready_state(state))
- iport->ready_exit = true;
-
- sci_change_state(sm, state);
- iport->ready_exit = false;
-}
-
/**
* sci_port_general_link_up_handler - phy can be assigned to port?
- * @sci_port: sci_port object for which has a phy that has gone link up.
- * @sci_phy: This is the struct isci_phy object that has gone link up.
- * @do_notify_user: This parameter specifies whether to inform the user (via
- * sci_port_link_up()) as to the fact that a new phy as become ready.
+ * @iport: sci_port object for which has a phy that has gone link up.
+ * @iphy: This is the struct isci_phy object that has gone link up.
+ * @flags: PF_RESUME, PF_NOTIFY to sci_port_activate_phy
*
- * Determine if this phy can be assigned to this
- * port . If the phy is not a valid PHY for
- * this port then the function will notify the user. A PHY can only be
- * part of a port if it's attached SAS ADDRESS is the same as all other PHYs in
- * the same port. none
+ * Determine if this phy can be assigned to this port . If the phy is
+ * not a valid PHY for this port then the function will notify the user.
+ * A PHY can only be part of a port if it's attached SAS ADDRESS is the
+ * same as all other PHYs in the same port.
*/
static void sci_port_general_link_up_handler(struct isci_port *iport,
- struct isci_phy *iphy,
- bool do_notify_user)
+ struct isci_phy *iphy,
+ u8 flags)
{
struct sci_sas_address port_sas_address;
struct sci_sas_address phy_sas_address;
@@ -743,7 +707,7 @@ static void sci_port_general_link_up_handler(struct isci_port *iport,
iport->active_phy_mask == 0) {
struct sci_base_state_machine *sm = &iport->sm;
- sci_port_activate_phy(iport, iphy, do_notify_user);
+ sci_port_activate_phy(iport, iphy, flags);
if (sm->current_state_id == SCI_PORT_RESETTING)
port_state_machine_change(iport, SCI_PORT_READY);
} else
@@ -753,10 +717,11 @@ static void sci_port_general_link_up_handler(struct isci_port *iport,
/**
+ * sci_port_is_wide()
* This method returns false if the port only has a single phy object assigned.
* If there are no phys or more than one phy then the method will return
* true.
- * @sci_port: The port for which the wide port condition is to be checked.
+ * @iport: The port for which the wide port condition is to be checked.
*
* bool true Is returned if this is a wide ported port. false Is returned if
* this is a narrow port.
@@ -776,12 +741,13 @@ static bool sci_port_is_wide(struct isci_port *iport)
}
/**
+ * sci_port_link_detected()
* This method is called by the PHY object when the link is detected. if the
* port wants the PHY to continue on to the link up state then the port
* layer must return true. If the port object returns false the phy object
* must halt its attempt to go link up.
- * @sci_port: The port associated with the phy object.
- * @sci_phy: The phy object that is trying to go link up.
+ * @iport: The port associated with the phy object.
+ * @iphy: The phy object that is trying to go link up.
*
* true if the phy object can continue to the link up condition. true Is
* returned if this phy can continue to the ready state. false Is returned if
@@ -789,24 +755,27 @@ static bool sci_port_is_wide(struct isci_port *iport)
* wide ports and direct attached phys. Since there are no wide ported SATA
* devices this could become an invalid port configuration.
*/
-bool sci_port_link_detected(
- struct isci_port *iport,
- struct isci_phy *iphy)
+bool sci_port_link_detected(struct isci_port *iport, struct isci_phy *iphy)
{
if ((iport->logical_port_index != SCIC_SDS_DUMMY_PORT) &&
- (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) &&
- sci_port_is_wide(iport)) {
- sci_port_invalid_link_up(iport, iphy);
-
- return false;
+ (iphy->protocol == SAS_PROTOCOL_SATA)) {
+ if (sci_port_is_wide(iport)) {
+ sci_port_invalid_link_up(iport, iphy);
+ return false;
+ } else {
+ struct isci_host *ihost = iport->owning_controller;
+ struct isci_port *dst_port = &(ihost->ports[iphy->phy_index]);
+ writel(iphy->phy_index,
+ &dst_port->port_pe_configuration_register[iphy->phy_index]);
+ }
}
return true;
}
-static void port_timeout(unsigned long data)
+static void port_timeout(struct timer_list *t)
{
- struct sci_timer *tmr = (struct sci_timer *)data;
+ struct sci_timer *tmr = timer_container_of(tmr, t, timer);
struct isci_port *iport = container_of(tmr, typeof(*iport), timer);
struct isci_host *ihost = iport->owning_controller;
unsigned long flags;
@@ -829,14 +798,13 @@ static void port_timeout(unsigned long data)
* case stay in the stopped state.
*/
dev_err(sciport_to_dev(iport),
- "%s: SCIC Port 0x%p failed to stop before tiemout.\n",
+ "%s: SCIC Port 0x%p failed to stop before timeout.\n",
__func__,
iport);
} else if (current_state == SCI_PORT_STOPPING) {
- /* if the port is still stopping then the stop has not completed */
- isci_port_stop_complete(iport->owning_controller,
- iport,
- SCI_FAILURE_TIMEOUT);
+ dev_dbg(sciport_to_dev(iport),
+ "%s: port%d: stop complete timeout\n",
+ __func__, iport->physical_port_index);
} else {
/* The port is in the ready state and we have a timer
* reporting a timeout this should not happen.
@@ -852,10 +820,8 @@ done:
/* --------------------------------------------------------------------------- */
-/**
+/*
* This function updates the hardwares VIIT entry for this port.
- *
- *
*/
static void sci_port_update_viit_entry(struct isci_port *iport)
{
@@ -909,7 +875,7 @@ static void sci_port_suspend_port_task_scheduler(struct isci_port *iport)
/**
* sci_port_post_dummy_request() - post dummy/workaround request
- * @sci_port: port to post task
+ * @iport: port to post task
*
* Prevent the hardware scheduler from posting new requests to the front
* of the scheduler queue causing a starvation problem for currently
@@ -934,10 +900,11 @@ static void sci_port_post_dummy_request(struct isci_port *iport)
}
/**
- * This routine will abort the dummy request. This will alow the hardware to
+ * sci_port_abort_dummy_request()
+ * This routine will abort the dummy request. This will allow the hardware to
* power down parts of the silicon to save power.
*
- * @sci_port: The port on which the task must be aborted.
+ * @iport: The port on which the task must be aborted.
*
*/
static void sci_port_abort_dummy_request(struct isci_port *iport)
@@ -958,8 +925,8 @@ static void sci_port_abort_dummy_request(struct isci_port *iport)
}
/**
- *
- * @sci_port: This is the struct isci_port object to resume.
+ * sci_port_resume_port_task_scheduler()
+ * @iport: This is the struct isci_port object to resume.
*
* This method will resume the port task scheduler for this port object. none
*/
@@ -988,26 +955,34 @@ static void sci_port_ready_substate_waiting_enter(struct sci_base_state_machine
}
}
+static void scic_sds_port_ready_substate_waiting_exit(
+ struct sci_base_state_machine *sm)
+{
+ struct isci_port *iport = container_of(sm, typeof(*iport), sm);
+ sci_port_resume_port_task_scheduler(iport);
+}
+
static void sci_port_ready_substate_operational_enter(struct sci_base_state_machine *sm)
{
u32 index;
struct isci_port *iport = container_of(sm, typeof(*iport), sm);
struct isci_host *ihost = iport->owning_controller;
- isci_port_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d ready\n",
+ __func__, iport->physical_port_index);
for (index = 0; index < SCI_MAX_PHYS; index++) {
if (iport->phy_table[index]) {
writel(iport->physical_port_index,
&iport->port_pe_configuration_register[
iport->phy_table[index]->phy_index]);
+ if (((iport->active_phy_mask^iport->enabled_phy_mask) & (1 << index)) != 0)
+ sci_port_resume_phy(iport, iport->phy_table[index]);
}
}
sci_port_update_viit_entry(iport);
- sci_port_resume_port_task_scheduler(iport);
-
/*
* Post the dummy task for the port so the hardware can schedule
* io correctly
@@ -1041,8 +1016,8 @@ static void sci_port_invalidate_dummy_remote_node(struct isci_port *iport)
}
/**
- *
- * @object: This is the object which is cast to a struct isci_port object.
+ * sci_port_ready_substate_operational_exit()
+ * @sm: This is the object which is cast to a struct isci_port object.
*
* This method will perform the actions required by the struct isci_port on
* exiting the SCI_PORT_SUB_OPERATIONAL. This function reports
@@ -1060,7 +1035,8 @@ static void sci_port_ready_substate_operational_exit(struct sci_base_state_machi
*/
sci_port_abort_dummy_request(iport);
- isci_port_not_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d !ready\n",
+ __func__, iport->physical_port_index);
if (iport->ready_exit)
sci_port_invalidate_dummy_remote_node(iport);
@@ -1072,22 +1048,12 @@ static void sci_port_ready_substate_configuring_enter(struct sci_base_state_mach
struct isci_host *ihost = iport->owning_controller;
if (iport->active_phy_mask == 0) {
- isci_port_not_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d !ready\n",
+ __func__, iport->physical_port_index);
- port_state_machine_change(iport,
- SCI_PORT_SUB_WAITING);
- } else if (iport->started_request_count == 0)
- port_state_machine_change(iport,
- SCI_PORT_SUB_OPERATIONAL);
-}
-
-static void sci_port_ready_substate_configuring_exit(struct sci_base_state_machine *sm)
-{
- struct isci_port *iport = container_of(sm, typeof(*iport), sm);
-
- sci_port_suspend_port_task_scheduler(iport);
- if (iport->ready_exit)
- sci_port_invalidate_dummy_remote_node(iport);
+ port_state_machine_change(iport, SCI_PORT_SUB_WAITING);
+ } else
+ port_state_machine_change(iport, SCI_PORT_SUB_OPERATIONAL);
}
enum sci_status sci_port_start(struct isci_port *iport)
@@ -1099,8 +1065,8 @@ enum sci_status sci_port_start(struct isci_port *iport)
state = iport->sm.current_state_id;
if (state != SCI_PORT_STOPPED) {
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1174,8 +1140,8 @@ enum sci_status sci_port_stop(struct isci_port *iport)
SCI_PORT_STOPPING);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1189,8 +1155,8 @@ static enum sci_status sci_port_hard_reset(struct isci_port *iport, u32 timeout)
state = iport->sm.current_state_id;
if (state != SCI_PORT_SUB_OPERATIONAL) {
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1222,9 +1188,9 @@ static enum sci_status sci_port_hard_reset(struct isci_port *iport, u32 timeout)
}
/**
- * sci_port_add_phy() -
- * @sci_port: This parameter specifies the port in which the phy will be added.
- * @sci_phy: This parameter is the phy which is to be added to the port.
+ * sci_port_add_phy()
+ * @iport: This parameter specifies the port in which the phy will be added.
+ * @iphy: This parameter is the phy which is to be added to the port.
*
* This method will add a PHY to the selected port. This method returns an
* enum sci_status. SCI_SUCCESS the phy has been added to the port. Any other
@@ -1236,6 +1202,8 @@ enum sci_status sci_port_add_phy(struct isci_port *iport,
enum sci_status status;
enum sci_port_states state;
+ sci_port_bcn_enable(iport);
+
state = iport->sm.current_state_id;
switch (state) {
case SCI_PORT_STOPPED: {
@@ -1265,7 +1233,7 @@ enum sci_status sci_port_add_phy(struct isci_port *iport,
if (status != SCI_SUCCESS)
return status;
- sci_port_general_link_up_handler(iport, iphy, true);
+ sci_port_general_link_up_handler(iport, iphy, PF_NOTIFY|PF_RESUME);
iport->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING;
port_state_machine_change(iport, SCI_PORT_SUB_CONFIGURING);
@@ -1275,7 +1243,7 @@ enum sci_status sci_port_add_phy(struct isci_port *iport,
if (status != SCI_SUCCESS)
return status;
- sci_port_general_link_up_handler(iport, iphy, true);
+ sci_port_general_link_up_handler(iport, iphy, PF_NOTIFY);
/* Re-enter the configuring state since this may be the last phy in
* the port.
@@ -1284,16 +1252,16 @@ enum sci_status sci_port_add_phy(struct isci_port *iport,
SCI_PORT_SUB_CONFIGURING);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
/**
- * sci_port_remove_phy() -
- * @sci_port: This parameter specifies the port in which the phy will be added.
- * @sci_phy: This parameter is the phy which is to be added to the port.
+ * sci_port_remove_phy()
+ * @iport: This parameter specifies the port in which the phy will be added.
+ * @iphy: This parameter is the phy which is to be added to the port.
*
* This method will remove the PHY from the selected PORT. This method returns
* an enum sci_status. SCI_SUCCESS the phy has been removed from the port. Any
@@ -1334,8 +1302,8 @@ enum sci_status sci_port_remove_phy(struct isci_port *iport,
SCI_PORT_SUB_CONFIGURING);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1351,13 +1319,13 @@ enum sci_status sci_port_link_up(struct isci_port *iport,
/* Since this is the first phy going link up for the port we
* can just enable it and continue
*/
- sci_port_activate_phy(iport, iphy, true);
+ sci_port_activate_phy(iport, iphy, PF_NOTIFY|PF_RESUME);
port_state_machine_change(iport,
SCI_PORT_SUB_OPERATIONAL);
return SCI_SUCCESS;
case SCI_PORT_SUB_OPERATIONAL:
- sci_port_general_link_up_handler(iport, iphy, true);
+ sci_port_general_link_up_handler(iport, iphy, PF_NOTIFY|PF_RESUME);
return SCI_SUCCESS;
case SCI_PORT_RESETTING:
/* TODO We should make sure that the phy that has gone
@@ -1374,11 +1342,11 @@ enum sci_status sci_port_link_up(struct isci_port *iport,
/* In the resetting state we don't notify the user regarding
* link up and link down notifications.
*/
- sci_port_general_link_up_handler(iport, iphy, false);
+ sci_port_general_link_up_handler(iport, iphy, PF_RESUME);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1407,8 +1375,8 @@ enum sci_status sci_port_link_down(struct isci_port *iport,
sci_port_deactivate_phy(iport, iphy, false);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1427,8 +1395,8 @@ enum sci_status sci_port_start_io(struct isci_port *iport,
iport->started_request_count++;
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1442,8 +1410,8 @@ enum sci_status sci_port_complete_io(struct isci_port *iport,
state = iport->sm.current_state_id;
switch (state) {
case SCI_PORT_STOPPED:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_PORT_STOPPING:
sci_port_decrement_request_count(iport);
@@ -1549,7 +1517,8 @@ static void sci_port_ready_state_enter(struct sci_base_state_machine *sm)
if (prev_state == SCI_PORT_RESETTING)
isci_port_hard_reset_complete(iport, SCI_SUCCESS);
else
- isci_port_not_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d !ready\n",
+ __func__, iport->physical_port_index);
/* Post and suspend the dummy remote node context for this port. */
sci_port_post_dummy_remote_node(iport);
@@ -1582,6 +1551,29 @@ static void sci_port_failed_state_enter(struct sci_base_state_machine *sm)
isci_port_hard_reset_complete(iport, SCI_FAILURE_TIMEOUT);
}
+void sci_port_set_hang_detection_timeout(struct isci_port *iport, u32 timeout)
+{
+ int phy_index;
+ u32 phy_mask = iport->active_phy_mask;
+
+ if (timeout)
+ ++iport->hang_detect_users;
+ else if (iport->hang_detect_users > 1)
+ --iport->hang_detect_users;
+ else
+ iport->hang_detect_users = 0;
+
+ if (timeout || (iport->hang_detect_users == 0)) {
+ for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
+ if ((phy_mask >> phy_index) & 1) {
+ writel(timeout,
+ &iport->phy_table[phy_index]
+ ->link_layer_registers
+ ->link_layer_hang_detection_timeout);
+ }
+ }
+ }
+}
/* --------------------------------------------------------------------------- */
static const struct sci_base_state sci_port_state_table[] = {
@@ -1597,14 +1589,14 @@ static const struct sci_base_state sci_port_state_table[] = {
},
[SCI_PORT_SUB_WAITING] = {
.enter_state = sci_port_ready_substate_waiting_enter,
+ .exit_state = scic_sds_port_ready_substate_waiting_exit,
},
[SCI_PORT_SUB_OPERATIONAL] = {
.enter_state = sci_port_ready_substate_operational_enter,
.exit_state = sci_port_ready_substate_operational_exit
},
[SCI_PORT_SUB_CONFIGURING] = {
- .enter_state = sci_port_ready_substate_configuring_enter,
- .exit_state = sci_port_ready_substate_configuring_exit
+ .enter_state = sci_port_ready_substate_configuring_enter
},
[SCI_PORT_RESETTING] = {
.exit_state = sci_port_resetting_state_exit
@@ -1622,12 +1614,15 @@ void sci_port_construct(struct isci_port *iport, u8 index,
iport->logical_port_index = SCIC_SDS_DUMMY_PORT;
iport->physical_port_index = index;
iport->active_phy_mask = 0;
- iport->ready_exit = false;
+ iport->enabled_phy_mask = 0;
+ iport->last_active_phy = 0;
+ iport->ready_exit = false;
iport->owning_controller = ihost;
iport->started_request_count = 0;
iport->assigned_device_count = 0;
+ iport->hang_detect_users = 0;
iport->reserved_rni = SCU_DUMMY_INDEX;
iport->reserved_tag = SCI_CONTROLLER_INVALID_IO_TAG;
@@ -1640,29 +1635,6 @@ void sci_port_construct(struct isci_port *iport, u8 index,
iport->phy_table[index] = NULL;
}
-void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index)
-{
- INIT_LIST_HEAD(&iport->remote_dev_list);
- INIT_LIST_HEAD(&iport->domain_dev_list);
- spin_lock_init(&iport->state_lock);
- init_completion(&iport->start_complete);
- iport->isci_host = ihost;
- isci_port_change_state(iport, isci_freed);
- atomic_set(&iport->event, 0);
-}
-
-/**
- * isci_port_get_state() - This function gets the status of the port object.
- * @isci_port: This parameter points to the isci_port object
- *
- * status of the object as a isci_status enum.
- */
-enum isci_status isci_port_get_state(
- struct isci_port *isci_port)
-{
- return isci_port->status;
-}
-
void sci_port_broadcast_change_received(struct isci_port *iport, struct isci_phy *iphy)
{
struct isci_host *ihost = iport->owning_controller;
@@ -1671,19 +1643,23 @@ void sci_port_broadcast_change_received(struct isci_port *iport, struct isci_phy
isci_port_bc_change_received(ihost, iport, iphy);
}
+static void wait_port_reset(struct isci_host *ihost, struct isci_port *iport)
+{
+ wait_event(ihost->eventq, !test_bit(IPORT_RESET_PENDING, &iport->state));
+}
+
int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport,
struct isci_phy *iphy)
{
unsigned long flags;
enum sci_status status;
- int idx, ret = TMF_RESP_FUNC_COMPLETE;
+ int ret = TMF_RESP_FUNC_COMPLETE;
dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n",
__func__, iport);
- init_completion(&iport->hard_reset_complete);
-
spin_lock_irqsave(&ihost->scic_lock, flags);
+ set_bit(IPORT_RESET_PENDING, &iport->state);
#define ISCI_PORT_RESET_TIMEOUT SCIC_SDS_SIGNATURE_FIS_TIMEOUT
status = sci_port_hard_reset(iport, ISCI_PORT_RESET_TIMEOUT);
@@ -1691,15 +1667,22 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
spin_unlock_irqrestore(&ihost->scic_lock, flags);
if (status == SCI_SUCCESS) {
- wait_for_completion(&iport->hard_reset_complete);
+ wait_port_reset(ihost, iport);
dev_dbg(&ihost->pdev->dev,
"%s: iport = %p; hard reset completion\n",
__func__, iport);
- if (iport->hard_reset_status != SCI_SUCCESS)
+ if (iport->hard_reset_status != SCI_SUCCESS) {
ret = TMF_RESP_FUNC_FAILED;
+
+ dev_err(&ihost->pdev->dev,
+ "%s: iport = %p; hard reset failed (0x%x)\n",
+ __func__, iport, iport->hard_reset_status);
+ }
} else {
+ clear_bit(IPORT_RESET_PENDING, &iport->state);
+ wake_up(&ihost->eventq);
ret = TMF_RESP_FUNC_FAILED;
dev_err(&ihost->pdev->dev,
@@ -1708,50 +1691,83 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
__func__, iport, status);
}
+ return ret;
+}
- /* If the hard reset for the port has failed, consider this
- * the same as link failures on all phys in the port.
- */
- if (ret != TMF_RESP_FUNC_COMPLETE) {
+int isci_ata_check_ready(struct domain_device *dev)
+{
+ struct isci_port *iport = dev->port->lldd_port;
+ struct isci_host *ihost = dev_to_ihost(dev);
+ struct isci_remote_device *idev;
+ unsigned long flags;
+ int rc = 0;
- dev_err(&ihost->pdev->dev,
- "%s: iport = %p; hard reset failed "
- "(0x%x) - driving explicit link fail for all phys\n",
- __func__, iport, iport->hard_reset_status);
-
- /* Down all phys in the port. */
- spin_lock_irqsave(&ihost->scic_lock, flags);
- for (idx = 0; idx < SCI_MAX_PHYS; ++idx) {
- struct isci_phy *iphy = iport->phy_table[idx];
-
- if (!iphy)
- continue;
- sci_phy_stop(iphy);
- sci_phy_start(iphy);
- }
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
- }
- return ret;
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ idev = isci_lookup_device(dev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ if (!idev)
+ goto out;
+
+ if (test_bit(IPORT_RESET_PENDING, &iport->state))
+ goto out;
+
+ rc = !!iport->active_phy_mask;
+ out:
+ isci_put_device(idev);
+
+ return rc;
}
-/**
- * isci_port_deformed() - This function is called by libsas when a port becomes
- * inactive.
- * @phy: This parameter specifies the libsas phy with the inactive port.
- *
- */
void isci_port_deformed(struct asd_sas_phy *phy)
{
- pr_debug("%s: sas_phy = %p\n", __func__, phy);
+ struct isci_host *ihost = phy->ha->lldd_ha;
+ struct isci_port *iport = phy->port->lldd_port;
+ unsigned long flags;
+ int i;
+
+ /* we got a port notification on a port that was subsequently
+ * torn down and libsas is just now catching up
+ */
+ if (!iport)
+ return;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ for (i = 0; i < SCI_MAX_PHYS; i++) {
+ if (iport->active_phy_mask & 1 << i)
+ break;
+ }
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ if (i >= SCI_MAX_PHYS)
+ dev_dbg(&ihost->pdev->dev, "%s: port: %ld\n",
+ __func__, (long) (iport - &ihost->ports[0]));
}
-/**
- * isci_port_formed() - This function is called by libsas when a port becomes
- * active.
- * @phy: This parameter specifies the libsas phy with the active port.
- *
- */
void isci_port_formed(struct asd_sas_phy *phy)
{
- pr_debug("%s: sas_phy = %p, sas_port = %p\n", __func__, phy, phy->port);
+ struct isci_host *ihost = phy->ha->lldd_ha;
+ struct isci_phy *iphy = to_iphy(phy);
+ struct asd_sas_port *port = phy->port;
+ struct isci_port *iport = NULL;
+ unsigned long flags;
+ int i;
+
+ /* initial ports are formed as the driver is still initializing,
+ * wait for that process to complete
+ */
+ wait_for_start(ihost);
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ for (i = 0; i < SCI_MAX_PORTS; i++) {
+ iport = &ihost->ports[i];
+ if (iport->active_phy_mask & 1 << iphy->phy_index)
+ break;
+ }
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ if (i >= SCI_MAX_PORTS)
+ iport = NULL;
+
+ port->lldd_port = iport;
}
diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h
index b50ecd4e8f9c..861e8f72811b 100644
--- a/drivers/scsi/isci/port.h
+++ b/drivers/scsi/isci/port.h
@@ -63,6 +63,9 @@
#define SCIC_SDS_DUMMY_PORT 0xFF
+#define PF_NOTIFY (1 << 0)
+#define PF_RESUME (1 << 1)
+
struct isci_phy;
struct isci_host;
@@ -77,13 +80,14 @@ enum isci_status {
/**
* struct isci_port - isci direct attached sas port object
- * @event: counts bcns and port stop events (for bcn filtering)
* @ready_exit: several states constitute 'ready'. When exiting ready we
* need to take extra port-teardown actions that are
* skipped when exiting to another 'ready' state.
* @logical_port_index: software port index
* @physical_port_index: hardware port index
* @active_phy_mask: identifies phy members
+ * @enabled_phy_mask: phy mask for the port
+ * that are already part of the port
* @reserved_tag:
* @reserved_rni: reserver for port task scheduler workaround
* @started_request_count: reference count for outstanding commands
@@ -91,28 +95,23 @@ enum isci_status {
* @timer: timeout start/stop operations
*/
struct isci_port {
- enum isci_status status;
- #define IPORT_BCN_BLOCKED 0
- #define IPORT_BCN_PENDING 1
- unsigned long flags;
- atomic_t event;
struct isci_host *isci_host;
- struct asd_sas_port sas_port;
struct list_head remote_dev_list;
- spinlock_t state_lock;
- struct list_head domain_dev_list;
- struct completion start_complete;
- struct completion hard_reset_complete;
+ #define IPORT_RESET_PENDING 0
+ unsigned long state;
enum sci_status hard_reset_status;
struct sci_base_state_machine sm;
bool ready_exit;
u8 logical_port_index;
u8 physical_port_index;
u8 active_phy_mask;
+ u8 enabled_phy_mask;
+ u8 last_active_phy;
u16 reserved_rni;
u16 reserved_tag;
u32 started_request_count;
u32 assigned_device_count;
+ u32 hang_detect_users;
u32 not_ready_reason;
struct isci_phy *phy_table[SCI_MAX_PHYS];
struct isci_host *owning_controller;
@@ -145,70 +144,47 @@ struct sci_port_properties {
};
/**
- * enum sci_port_states - This enumeration depicts all the states for the
- * common port state machine.
- *
- *
+ * enum sci_port_states - port state machine states
+ * @SCI_PORT_STOPPED: port has successfully been stopped. In this state
+ * no new IO operations are permitted. This state is
+ * entered from the STOPPING state.
+ * @SCI_PORT_STOPPING: port is in the process of stopping. In this
+ * state no new IO operations are permitted, but
+ * existing IO operations are allowed to complete.
+ * This state is entered from the READY state.
+ * @SCI_PORT_READY: port is now ready. Thus, the user is able to
+ * perform IO operations on this port. This state is
+ * entered from the STARTING state.
+ * @SCI_PORT_SUB_WAITING: port is started and ready but has no active
+ * phys.
+ * @SCI_PORT_SUB_OPERATIONAL: port is started and ready and there is at
+ * least one phy operational.
+ * @SCI_PORT_SUB_CONFIGURING: port is started and there was an
+ * add/remove phy event. This state is only
+ * used in Automatic Port Configuration Mode
+ * (APC)
+ * @SCI_PORT_RESETTING: port is in the process of performing a hard
+ * reset. Thus, the user is unable to perform IO
+ * operations on this port. This state is entered
+ * from the READY state.
+ * @SCI_PORT_FAILED: port has failed a reset request. This state is
+ * entered when a port reset request times out. This
+ * state is entered from the RESETTING state.
*/
-enum sci_port_states {
- /**
- * This state indicates that the port has successfully been stopped.
- * In this state no new IO operations are permitted.
- * This state is entered from the STOPPING state.
- */
- SCI_PORT_STOPPED,
-
- /**
- * This state indicates that the port is in the process of stopping.
- * In this state no new IO operations are permitted, but existing IO
- * operations are allowed to complete.
- * This state is entered from the READY state.
- */
- SCI_PORT_STOPPING,
-
- /**
- * This state indicates the port is now ready. Thus, the user is
- * able to perform IO operations on this port.
- * This state is entered from the STARTING state.
- */
- SCI_PORT_READY,
-
- /**
- * The substate where the port is started and ready but has no
- * active phys.
- */
- SCI_PORT_SUB_WAITING,
-
- /**
- * The substate where the port is started and ready and there is
- * at least one phy operational.
- */
- SCI_PORT_SUB_OPERATIONAL,
-
- /**
- * The substate where the port is started and there was an
- * add/remove phy event. This state is only used in Automatic
- * Port Configuration Mode (APC)
- */
- SCI_PORT_SUB_CONFIGURING,
-
- /**
- * This state indicates the port is in the process of performing a hard
- * reset. Thus, the user is unable to perform IO operations on this
- * port.
- * This state is entered from the READY state.
- */
- SCI_PORT_RESETTING,
-
- /**
- * This state indicates the port has failed a reset request. This state
- * is entered when a port reset request times out.
- * This state is entered from the RESETTING state.
- */
- SCI_PORT_FAILED,
-
-
-};
+#define PORT_STATES {\
+ C(PORT_STOPPED),\
+ C(PORT_STOPPING),\
+ C(PORT_READY),\
+ C(PORT_SUB_WAITING),\
+ C(PORT_SUB_OPERATIONAL),\
+ C(PORT_SUB_CONFIGURING),\
+ C(PORT_RESETTING),\
+ C(PORT_FAILED),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_port_states PORT_STATES;
+#undef C
static inline void sci_port_decrement_request_count(struct isci_port *iport)
{
@@ -254,6 +230,10 @@ bool sci_port_link_detected(
struct isci_port *iport,
struct isci_phy *iphy);
+enum sci_status sci_port_get_properties(
+ struct isci_port *iport,
+ struct sci_port_properties *prop);
+
enum sci_status sci_port_link_up(struct isci_port *iport,
struct isci_phy *iphy);
enum sci_status sci_port_link_down(struct isci_port *iport,
@@ -290,17 +270,14 @@ void sci_port_get_attached_sas_address(
struct isci_port *iport,
struct sci_sas_address *sas_address);
-enum isci_status isci_port_get_state(
- struct isci_port *isci_port);
+void sci_port_set_hang_detection_timeout(
+ struct isci_port *isci_port,
+ u32 timeout);
void isci_port_formed(struct asd_sas_phy *);
void isci_port_deformed(struct asd_sas_phy *);
-void isci_port_init(
- struct isci_port *port,
- struct isci_host *host,
- int index);
-
int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport,
struct isci_phy *iphy);
+int isci_ata_check_ready(struct domain_device *dev);
#endif /* !defined(_ISCI_PORT_H_) */
diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c
index 486b113c634a..3b4820defe63 100644
--- a/drivers/scsi/isci/port_config.c
+++ b/drivers/scsi/isci/port_config.c
@@ -57,7 +57,7 @@
#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10)
#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10)
-#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (100)
+#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (1000)
enum SCIC_SDS_APC_ACTIVITY {
SCIC_SDS_APC_SKIP_PHY,
@@ -73,7 +73,7 @@ enum SCIC_SDS_APC_ACTIVITY {
* ****************************************************************************** */
/**
- *
+ * sci_sas_address_compare()
* @address_one: A SAS Address to be compared.
* @address_two: A SAS Address to be compared.
*
@@ -102,9 +102,9 @@ static s32 sci_sas_address_compare(
}
/**
- *
- * @controller: The controller object used for the port search.
- * @phy: The phy object to match.
+ * sci_port_configuration_agent_find_port()
+ * @ihost: The controller object used for the port search.
+ * @iphy: The phy object to match.
*
* This routine will find a matching port for the phy. This means that the
* port and phy both have the same broadcast sas address and same received sas
@@ -145,9 +145,9 @@ static struct isci_port *sci_port_configuration_agent_find_port(
}
/**
- *
- * @controller: This is the controller object that contains the port agent
- * @port_agent: This is the port configruation agent for the controller.
+ * sci_port_configuration_agent_validate_ports()
+ * @ihost: This is the controller object that contains the port agent
+ * @port_agent: This is the port configuration agent for the controller.
*
* This routine will validate the port configuration is correct for the SCU
* hardware. The SCU hardware allows for port configurations as follows. LP0
@@ -291,7 +291,7 @@ sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
* Note: We have not moved the current phy_index so we will actually
* compare the startting phy with itself.
* This is expected and required to add the phy to the port. */
- while (phy_index < SCI_MAX_PHYS) {
+ for (; phy_index < SCI_MAX_PHYS; phy_index++) {
if ((phy_mask & (1 << phy_index)) == 0)
continue;
sci_phy_get_sas_address(&ihost->phys[phy_index],
@@ -313,16 +313,15 @@ sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
assigned_phy_mask |= (1 << phy_index);
}
- phy_index++;
}
return sci_port_configuration_agent_validate_ports(ihost, port_agent);
}
-static void mpc_agent_timeout(unsigned long data)
+static void mpc_agent_timeout(struct timer_list *t)
{
u8 index;
- struct sci_timer *tmr = (struct sci_timer *)data;
+ struct sci_timer *tmr = timer_container_of(tmr, t, timer);
struct sci_port_configuration_agent *port_agent;
struct isci_host *ihost;
unsigned long flags;
@@ -374,15 +373,16 @@ static void sci_mpc_agent_link_up(struct isci_host *ihost,
}
/**
- *
- * @controller: This is the controller object that receives the link down
+ * sci_mpc_agent_link_down()
+ * @ihost: This is the controller object that receives the link down
* notification.
- * @port: This is the port object associated with the phy. If the is no
+ * @port_agent: This is the port configuration agent for the controller.
+ * @iport: This is the port object associated with the phy. If the is no
* associated port this is an NULL. The port is an invalid
* handle only if the phy was never port of this port. This happens when
* the phy is not broadcasting the same SAS address as the other phys in the
* assigned port.
- * @phy: This is the phy object which has gone link down.
+ * @iphy: This is the phy object which has gone link down.
*
* This function handles the manual port configuration link down notifications.
* Since all ports and phys are associated at initialization time we just turn
@@ -466,6 +466,19 @@ sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
return sci_port_configuration_agent_validate_ports(ihost, port_agent);
}
+/*
+ * This routine will restart the automatic port configuration timeout
+ * timer for the next time period. This could be caused by either a link
+ * down event or a link up event where we can not yet tell to which a phy
+ * belongs.
+ */
+static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent,
+ u32 timeout)
+{
+ port_agent->timer_pending = true;
+ sci_mod_timer(&port_agent->timer, timeout);
+}
+
static void sci_apc_agent_configure_ports(struct isci_host *ihost,
struct sci_port_configuration_agent *port_agent,
struct isci_phy *iphy,
@@ -565,17 +578,8 @@ static void sci_apc_agent_configure_ports(struct isci_host *ihost,
break;
case SCIC_SDS_APC_START_TIMER:
- /*
- * This can occur for either a link down event, or a link
- * up event where we cannot yet tell the port to which a
- * phy belongs.
- */
- if (port_agent->timer_pending)
- sci_del_timer(&port_agent->timer);
-
- port_agent->timer_pending = true;
- sci_mod_timer(&port_agent->timer,
- SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
+ sci_apc_agent_start_timer(port_agent,
+ SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
break;
case SCIC_SDS_APC_SKIP_PHY:
@@ -587,11 +591,12 @@ static void sci_apc_agent_configure_ports(struct isci_host *ihost,
/**
* sci_apc_agent_link_up - handle apc link up events
- * @scic: This is the controller object that receives the link up
+ * @ihost: This is the controller object that receives the link up
* notification.
- * @sci_port: This is the port object associated with the phy. If the is no
+ * @port_agent: This is the port configuration agent for the controller.
+ * @iport: This is the port object associated with the phy. If the is no
* associated port this is an NULL.
- * @sci_phy: This is the phy object which has gone link up.
+ * @iphy: This is the phy object which has gone link up.
*
* This method handles the automatic port configuration for link up
* notifications. Is it possible to get a link down notification from a phy
@@ -607,25 +612,20 @@ static void sci_apc_agent_link_up(struct isci_host *ihost,
if (!iport) {
/* the phy is not the part of this port */
port_agent->phy_ready_mask |= 1 << phy_index;
- sci_apc_agent_configure_ports(ihost, port_agent, iphy, true);
+ sci_apc_agent_start_timer(port_agent,
+ SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
} else {
/* the phy is already the part of the port */
- u32 port_state = iport->sm.current_state_id;
-
- /* if the PORT'S state is resetting then the link up is from
- * port hard reset in this case, we need to tell the port
- * that link up is recieved
- */
- BUG_ON(port_state != SCI_PORT_RESETTING);
port_agent->phy_ready_mask |= 1 << phy_index;
sci_port_link_up(iport, iphy);
}
}
/**
- *
- * @controller: This is the controller object that receives the link down
+ * sci_apc_agent_link_down()
+ * @ihost: This is the controller object that receives the link down
* notification.
+ * @port_agent: This is the port configuration agent for the controller.
* @iport: This is the port object associated with the phy. If the is no
* associated port this is an NULL.
* @iphy: This is the phy object which has gone link down.
@@ -656,10 +656,10 @@ static void sci_apc_agent_link_down(
}
/* configure the phys into ports when the timer fires */
-static void apc_agent_timeout(unsigned long data)
+static void apc_agent_timeout(struct timer_list *t)
{
u32 index;
- struct sci_timer *tmr = (struct sci_timer *)data;
+ struct sci_timer *tmr = timer_container_of(tmr, t, timer);
struct sci_port_configuration_agent *port_agent;
struct isci_host *ihost;
unsigned long flags;
@@ -678,7 +678,7 @@ static void apc_agent_timeout(unsigned long data)
configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
if (!configure_phy_mask)
- return;
+ goto done;
for (index = 0; index < SCI_MAX_PHYS; index++) {
if ((configure_phy_mask & (1 << index)) == 0)
@@ -688,6 +688,9 @@ static void apc_agent_timeout(unsigned long data)
&ihost->phys[index], false);
}
+ if (is_controller_start_complete(ihost))
+ sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
+
done:
spin_unlock_irqrestore(&ihost->scic_lock, flags);
}
@@ -697,9 +700,7 @@ done:
* Public port configuration agent routines
* ****************************************************************************** */
-/**
- *
- *
+/*
* This method will construct the port configuration agent for operation. This
* call is universal for both manual port configuration and automatic port
* configuration modes.
@@ -723,6 +724,11 @@ void sci_port_configuration_agent_construct(
}
}
+bool is_port_config_apc(struct isci_host *ihost)
+{
+ return ihost->port_agent.link_up_handler == sci_apc_agent_link_up;
+}
+
enum sci_status sci_port_configuration_agent_initialize(
struct isci_host *ihost,
struct sci_port_configuration_agent *port_agent)
diff --git a/drivers/scsi/isci/probe_roms.c b/drivers/scsi/isci/probe_roms.c
index b5f4341de243..a2bbe46f8ccb 100644
--- a/drivers/scsi/isci/probe_roms.c
+++ b/drivers/scsi/isci/probe_roms.c
@@ -54,6 +54,7 @@ struct isci_orom *isci_request_oprom(struct pci_dev *pdev)
len = pci_biosrom_size(pdev);
rom = devm_kzalloc(&pdev->dev, sizeof(*rom), GFP_KERNEL);
if (!rom) {
+ pci_unmap_biosrom(oprom);
dev_warn(&pdev->dev,
"Unable to allocate memory for orom\n");
return NULL;
@@ -104,7 +105,6 @@ struct isci_orom *isci_request_oprom(struct pci_dev *pdev)
if (i >= len) {
dev_err(&pdev->dev, "oprom parse error\n");
- devm_kfree(&pdev->dev, rom);
rom = NULL;
}
pci_unmap_biosrom(oprom);
@@ -112,18 +112,6 @@ struct isci_orom *isci_request_oprom(struct pci_dev *pdev)
return rom;
}
-enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem,
- struct isci_orom *orom, int scu_index)
-{
- /* check for valid inputs */
- if (scu_index < 0 || scu_index >= SCI_MAX_CONTROLLERS ||
- scu_index > orom->hdr.num_elements || !oem)
- return -EINVAL;
-
- *oem = orom->ctrl[scu_index];
- return 0;
-}
-
struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw)
{
struct isci_orom *orom = NULL, *data;
@@ -147,7 +135,7 @@ struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmw
memcpy(orom, fw->data, fw->size);
- if (is_c0(pdev))
+ if (is_c0(pdev) || is_c1(pdev))
goto out;
/*
diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h
index dc007e692f4e..e08b578241f8 100644
--- a/drivers/scsi/isci/probe_roms.h
+++ b/drivers/scsi/isci/probe_roms.h
@@ -112,7 +112,7 @@ struct sci_user_parameters {
* This field specifies the maximum number of direct attached devices
* that can have power supplied to them simultaneously.
*/
- u8 max_number_concurrent_device_spin_up;
+ u8 max_concurr_spinup;
/**
* This field specifies the number of seconds to allow a phy to consume
@@ -152,12 +152,10 @@ struct sci_user_parameters {
#define MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT 4
struct sci_oem_params;
-int sci_oem_parameters_validate(struct sci_oem_params *oem);
+int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version);
struct isci_orom;
struct isci_orom *isci_request_oprom(struct pci_dev *pdev);
-enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem,
- struct isci_orom *orom, int scu_index);
struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw);
struct isci_orom *isci_get_efi_var(struct pci_dev *pdev);
@@ -191,6 +189,11 @@ struct isci_oem_hdr {
0x1a, 0x04, 0xc6)
#define ISCI_EFI_VAR_NAME "RstScuO"
+#define ISCI_ROM_VER_1_0 0x10
+#define ISCI_ROM_VER_1_1 0x11
+#define ISCI_ROM_VER_1_3 0x13
+#define ISCI_ROM_VER_LATEST ISCI_ROM_VER_1_3
+
/* Allowed PORT configuration modes APC Automatic PORT configuration mode is
* defined by the OEM configuration parameters providing no PHY_MASK parameters
* for any PORT. i.e. There are no phys assigned to any of the ports at start.
@@ -219,9 +222,87 @@ struct sci_bios_oem_param_block_hdr {
struct sci_oem_params {
struct {
uint8_t mode_type;
- uint8_t max_concurrent_dev_spin_up;
- uint8_t do_enable_ssc;
- uint8_t reserved;
+ uint8_t max_concurr_spin_up;
+ /*
+ * This bitfield indicates the OEM's desired default Tx
+ * Spread Spectrum Clocking (SSC) settings for SATA and SAS.
+ * NOTE: Default SSC Modulation Frequency is 31.5KHz.
+ */
+ union {
+ struct {
+ /*
+ * NOTE: Max spread for SATA is +0 / -5000 PPM.
+ * Down-spreading SSC (only method allowed for SATA):
+ * SATA SSC Tx Disabled = 0x0
+ * SATA SSC Tx at +0 / -1419 PPM Spread = 0x2
+ * SATA SSC Tx at +0 / -2129 PPM Spread = 0x3
+ * SATA SSC Tx at +0 / -4257 PPM Spread = 0x6
+ * SATA SSC Tx at +0 / -4967 PPM Spread = 0x7
+ */
+ uint8_t ssc_sata_tx_spread_level:4;
+ /*
+ * SAS SSC Tx Disabled = 0x0
+ *
+ * NOTE: Max spread for SAS down-spreading +0 /
+ * -2300 PPM
+ * Down-spreading SSC:
+ * SAS SSC Tx at +0 / -1419 PPM Spread = 0x2
+ * SAS SSC Tx at +0 / -2129 PPM Spread = 0x3
+ *
+ * NOTE: Max spread for SAS center-spreading +2300 /
+ * -2300 PPM
+ * Center-spreading SSC:
+ * SAS SSC Tx at +1064 / -1064 PPM Spread = 0x3
+ * SAS SSC Tx at +2129 / -2129 PPM Spread = 0x6
+ */
+ uint8_t ssc_sas_tx_spread_level:3;
+ /*
+ * NOTE: Refer to the SSC section of the SAS 2.x
+ * Specification for proper setting of this field.
+ * For standard SAS Initiator SAS PHY operation it
+ * should be 0 for Down-spreading.
+ * SAS SSC Tx spread type:
+ * Down-spreading SSC = 0
+ * Center-spreading SSC = 1
+ */
+ uint8_t ssc_sas_tx_type:1;
+ };
+ uint8_t do_enable_ssc;
+ };
+ /*
+ * This field indicates length of the SAS/SATA cable between
+ * host and device.
+ * This field is used make relationship between analog
+ * parameters of the phy in the silicon and length of the cable.
+ * Supported cable attenuation levels:
+ * "short"- up to 3m, "medium"-3m to 6m, and "long"- more than
+ * 6m.
+ *
+ * This is bit mask field:
+ *
+ * BIT: (MSB) 7 6 5 4
+ * ASSIGNMENT: <phy3><phy2><phy1><phy0> - Medium cable
+ * length assignment
+ * BIT: 3 2 1 0 (LSB)
+ * ASSIGNMENT: <phy3><phy2><phy1><phy0> - Long cable length
+ * assignment
+ *
+ * BITS 7-4 are set when the cable length is assigned to medium
+ * BITS 3-0 are set when the cable length is assigned to long
+ *
+ * The BIT positions are clear when the cable length is
+ * assigned to short.
+ *
+ * Setting the bits for both long and medium cable length is
+ * undefined.
+ *
+ * A value of 0x84 would assign
+ * phy3 - medium
+ * phy2 - long
+ * phy1 - short
+ * phy0 - short
+ */
+ uint8_t cable_selection_mask;
} controller;
struct {
diff --git a/drivers/scsi/isci/registers.h b/drivers/scsi/isci/registers.h
index 9b266c7428e8..63468cfe3e4a 100644
--- a/drivers/scsi/isci/registers.h
+++ b/drivers/scsi/isci/registers.h
@@ -370,6 +370,27 @@ struct scu_iit_entry {
>> SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_SHIFT \
)
+/* ***************************************************************************** */
+#define SMU_CLOCK_GATING_CONTROL_IDLE_ENABLE_SHIFT (0)
+#define SMU_CLOCK_GATING_CONTROL_IDLE_ENABLE_MASK (0x00000001)
+#define SMU_CLOCK_GATING_CONTROL_XCLK_ENABLE_SHIFT (1)
+#define SMU_CLOCK_GATING_CONTROL_XCLK_ENABLE_MASK (0x00000002)
+#define SMU_CLOCK_GATING_CONTROL_TXCLK_ENABLE_SHIFT (2)
+#define SMU_CLOCK_GATING_CONTROL_TXCLK_ENABLE_MASK (0x00000004)
+#define SMU_CLOCK_GATING_CONTROL_REGCLK_ENABLE_SHIFT (3)
+#define SMU_CLOCK_GATING_CONTROL_REGCLK_ENABLE_MASK (0x00000008)
+#define SMU_CLOCK_GATING_CONTROL_IDLE_TIMEOUT_SHIFT (16)
+#define SMU_CLOCK_GATING_CONTROL_IDLE_TIMEOUT_MASK (0x000F0000)
+#define SMU_CLOCK_GATING_CONTROL_FORCE_IDLE_SHIFT (31)
+#define SMU_CLOCK_GATING_CONTROL_FORCE_IDLE_MASK (0x80000000)
+#define SMU_CLOCK_GATING_CONTROL_RESERVED_MASK (0x7FF0FFF0)
+
+#define SMU_CGUCR_GEN_VAL(name, value) \
+ SCU_GEN_VALUE(SMU_CLOCK_GATING_CONTROL_##name, value)
+
+#define SMU_CGUCR_GEN_BIT(name) \
+ SCU_GEN_BIT(SMU_CLOCK_GATING_CONTROL_##name)
+
/* -------------------------------------------------------------------------- */
#define SMU_CONTROL_STATUS_TASK_CONTEXT_RANGE_ENABLE_SHIFT (0)
@@ -631,7 +652,7 @@ struct scu_iit_entry {
/*
- * TODO: Where is the SAS_LNKTOV regsiter?
+ * TODO: Where is the SAS_LNKTOV register?
* TODO: Where is the SAS_PHYTOV register? */
#define SCU_SAS_TRANSMIT_IDENTIFICATION_SMP_TARGET_SHIFT (1)
@@ -875,122 +896,6 @@ struct scu_iit_entry {
#define SCU_PTSxSR_GEN_BIT(name) \
SCU_GEN_BIT(SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_ ## name)
-
-/*
- * *****************************************************************************
- * * SGPIO Register shift and mask values
- * ***************************************************************************** */
-#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_SHIFT (0)
-#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_MASK (0x00000001)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_SHIFT (1)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_MASK (0x00000002)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_SHIFT (2)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_MASK (0x00000004)
-#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_SHIFT (15)
-#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_MASK (0x00008000)
-#define SCU_SGPIO_CONTROL_SGPIO_RESERVED_MASK (0xFFFF7FF8)
-
-#define SCU_SGICRx_GEN_BIT(name) \
- SCU_GEN_BIT(SCU_SGPIO_CONTROL_SGPIO_ ## name)
-
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_SHIFT (0)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_MASK (0x0000000F)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_SHIFT (4)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_MASK (0x000000F0)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_SHIFT (8)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_MASK (0x00000F00)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_SHIFT (12)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_MASK (0x0000F000)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_RESERVED_MASK (0xFFFF0000)
-
-#define SCU_SGPBRx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_ ## name, value)
-
-#define SCU_SGPIO_START_DRIVE_LOWER_R0_SHIFT (0)
-#define SCU_SGPIO_START_DRIVE_LOWER_R0_MASK (0x00000003)
-#define SCU_SGPIO_START_DRIVE_LOWER_R1_SHIFT (4)
-#define SCU_SGPIO_START_DRIVE_LOWER_R1_MASK (0x00000030)
-#define SCU_SGPIO_START_DRIVE_LOWER_R2_SHIFT (8)
-#define SCU_SGPIO_START_DRIVE_LOWER_R2_MASK (0x00000300)
-#define SCU_SGPIO_START_DRIVE_LOWER_R3_SHIFT (12)
-#define SCU_SGPIO_START_DRIVE_LOWER_R3_MASK (0x00003000)
-#define SCU_SGPIO_START_DRIVE_LOWER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSDLRx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value)
-
-#define SCU_SGPIO_START_DRIVE_UPPER_R0_SHIFT (0)
-#define SCU_SGPIO_START_DRIVE_UPPER_R0_MASK (0x00000003)
-#define SCU_SGPIO_START_DRIVE_UPPER_R1_SHIFT (4)
-#define SCU_SGPIO_START_DRIVE_UPPER_R1_MASK (0x00000030)
-#define SCU_SGPIO_START_DRIVE_UPPER_R2_SHIFT (8)
-#define SCU_SGPIO_START_DRIVE_UPPER_R2_MASK (0x00000300)
-#define SCU_SGPIO_START_DRIVE_UPPER_R3_SHIFT (12)
-#define SCU_SGPIO_START_DRIVE_UPPER_R3_MASK (0x00003000)
-#define SCU_SGPIO_START_DRIVE_UPPER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSDURx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value)
-
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_SHIFT (0)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_MASK (0x00000003)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_SHIFT (4)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_MASK (0x00000030)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_SHIFT (8)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_MASK (0x00000300)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_SHIFT (12)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_MASK (0x00003000)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSIDLRx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value)
-
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_SHIFT (0)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_MASK (0x00000003)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_SHIFT (4)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_MASK (0x00000030)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_SHIFT (8)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_MASK (0x00000300)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_SHIFT (12)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_MASK (0x00003000)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSIDURx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value)
-
-#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_SHIFT (0)
-#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_MASK (0x0000000F)
-#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_RESERVED_MASK (0xFFFFFFF0)
-
-#define SCU_SGVSCR_GEN_VAL(value) \
- SCU_GEN_VALUE(SCU_SGPIO_VENDOR_SPECIFIC_CODE ## name, value)
-
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_SHIFT (0)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_MASK (0x00000003)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_SHIFT (2)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_MASK (0x00000004)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_SHIFT (3)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_MASK (0x00000008)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_SHIFT (4)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_MASK (0x00000030)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_SHIFT (6)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_MASK (0x00000040)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_SHIFT (7)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_MASK (0x00000080)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_SHIFT (8)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_MASK (0x00000300)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_SHIFT (10)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_MASK (0x00000400)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_SHIFT (11)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_MASK (0x00000800)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_RESERVED_MASK (0xFFFFF000)
-
-#define SCU_SGODSR_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name, value)
-
-#define SCU_SGODSR_GEN_BIT(name) \
- SCU_GEN_BIT(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name)
-
/*
* *****************************************************************************
* * SMU Registers
@@ -1108,8 +1013,10 @@ struct smu_registers {
u32 mmr_address_window;
/* 0x00A4 SMDW */
u32 mmr_data_window;
- u32 reserved_A8;
- u32 reserved_AC;
+/* 0x00A8 CGUCR */
+ u32 clock_gating_control;
+/* 0x00AC CGUPC */
+ u32 clock_gating_performance;
/* A whole bunch of reserved space */
u32 reserved_Bx[4];
u32 reserved_Cx[4];
@@ -1299,6 +1206,18 @@ struct scu_transport_layer_registers {
#define SCU_AFE_XCVRCR_OFFSET 0x00DC
#define SCU_AFE_LUTCR_OFFSET 0x00E0
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_ALIGN_DETECTION_SHIFT (0UL)
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_ALIGN_DETECTION_MASK (0x000000FFUL)
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_HOT_PLUG_SHIFT (8UL)
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_HOT_PLUG_MASK (0x0000FF00UL)
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_COMSAS_DETECTION_SHIFT (16UL)
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_COMSAS_DETECTION_MASK (0x00FF0000UL)
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_RATE_CHANGE_SHIFT (24UL)
+#define SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_RATE_CHANGE_MASK (0xFF000000UL)
+
+#define SCU_SAS_PHYTOV_GEN_VAL(name, value) \
+ SCU_GEN_VALUE(SCU_SAS_PHY_TIMER_TIMEOUT_VALUES_##name, value)
+
#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_SHIFT (0)
#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_MASK (0x00000003)
#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN1 (0)
@@ -1320,6 +1239,14 @@ struct scu_transport_layer_registers {
#define SCU_SAS_LLCTL_GEN_BIT(name) \
SCU_GEN_BIT(SCU_SAS_LINK_LAYER_CONTROL_ ## name)
+#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT (0xF0)
+#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED (0x1FF)
+#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_SHIFT (0)
+#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_MASK (0x3FF)
+
+#define SCU_SAS_LLTXCOMSAS_GEN_VAL(name, value) \
+ SCU_GEN_VALUE(SCU_SAS_LINK_LAYER_TXCOMSAS_ ## name, value)
+
/* #define SCU_FRXHECR_DCNT_OFFSET 0x00B0 */
#define SCU_PSZGCR_OFFSET 0x00E4
@@ -1517,10 +1444,12 @@ struct scu_sgpio_registers {
u32 serial_input_upper;
/* 0x0018 SGPIO_SGVSCR */
u32 vendor_specific_code;
+/* 0x001C Reserved */
+ u32 reserved_001c;
/* 0x0020 SGPIO_SGODSR */
- u32 ouput_data_select[8];
+ u32 output_data_select[8];
/* Remainder of memory space 256 bytes */
- u32 reserved_1444_14ff[0x31];
+ u32 reserved_1444_14ff[0x30];
};
@@ -1898,7 +1827,7 @@ struct scu_peg_registers {
};
/**
- * struct scu_registers - SCU regsiters including both PEG registers if we turn
+ * struct scu_registers - SCU registers including both PEG registers if we turn
* on that compile option. All of these registers are in the memory mapped
* space returned from BAR1.
*
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index b6e6368c2665..4c7462965ea1 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -53,6 +53,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <scsi/sas.h>
+#include <linux/bitops.h>
#include "isci.h"
#include "port.h"
#include "remote_device.h"
@@ -61,46 +62,21 @@
#include "scu_event_codes.h"
#include "task.h"
-/**
- * isci_remote_device_not_ready() - This function is called by the ihost when
- * the remote device is not ready. We mark the isci device as ready (not
- * "ready_for_io") and signal the waiting proccess.
- * @isci_host: This parameter specifies the isci host object.
- * @isci_device: This parameter specifies the remote device
- *
- * sci_lock is held on entrance to this function.
- */
-static void isci_remote_device_not_ready(struct isci_host *ihost,
- struct isci_remote_device *idev, u32 reason)
+#undef C
+#define C(a) (#a)
+const char *dev_state_name(enum sci_remote_device_states state)
{
- struct isci_request *ireq;
-
- dev_dbg(&ihost->pdev->dev,
- "%s: isci_device = %p\n", __func__, idev);
+ static const char * const strings[] = REMOTE_DEV_STATES;
- switch (reason) {
- case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED:
- set_bit(IDEV_GONE, &idev->flags);
- break;
- case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
- set_bit(IDEV_IO_NCQERROR, &idev->flags);
-
- /* Kill all outstanding requests for the device. */
- list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) {
-
- dev_dbg(&ihost->pdev->dev,
- "%s: isci_device = %p request = %p\n",
- __func__, idev, ireq);
+ return strings[state];
+}
+#undef C
- sci_controller_terminate_request(ihost,
- idev,
- ireq);
- }
- /* Fall through into the default case... */
- default:
- clear_bit(IDEV_IO_READY, &idev->flags);
- break;
- }
+enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
+ enum sci_remote_node_suspension_reasons reason)
+{
+ return sci_remote_node_context_suspend(&idev->rnc, reason,
+ SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT);
}
/**
@@ -122,18 +98,29 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote
wake_up(&ihost->eventq);
}
-/* called once the remote node context is ready to be freed.
- * The remote device can now report that its stop operation is complete. none
- */
-static void rnc_destruct_done(void *_dev)
+static enum sci_status sci_remote_device_terminate_req(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ int check_abort,
+ struct isci_request *ireq)
{
- struct isci_remote_device *idev = _dev;
+ if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
+ (ireq->target_device != idev) ||
+ (check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags)))
+ return SCI_SUCCESS;
- BUG_ON(idev->started_request_count != 0);
- sci_change_state(&idev->sm, SCI_DEV_STOPPED);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: idev=%p; flags=%lx; req=%p; req target=%p\n",
+ __func__, idev, idev->flags, ireq, ireq->target_device);
+
+ set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags);
+
+ return sci_controller_terminate_request(ihost, idev, ireq);
}
-static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev)
+static enum sci_status sci_remote_device_terminate_reqs_checkabort(
+ struct isci_remote_device *idev,
+ int chk)
{
struct isci_host *ihost = idev->owning_port->owning_controller;
enum sci_status status = SCI_SUCCESS;
@@ -143,18 +130,211 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d
struct isci_request *ireq = ihost->reqs[i];
enum sci_status s;
- if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
- ireq->target_device != idev)
- continue;
-
- s = sci_controller_terminate_request(ihost, idev, ireq);
+ s = sci_remote_device_terminate_req(ihost, idev, chk, ireq);
if (s != SCI_SUCCESS)
status = s;
}
+ return status;
+}
+
+static bool isci_compare_suspendcount(
+ struct isci_remote_device *idev,
+ u32 localcount)
+{
+ smp_rmb();
+ /* Check for a change in the suspend count, or the RNC
+ * being destroyed.
+ */
+ return (localcount != idev->rnc.suspend_count)
+ || sci_remote_node_context_is_being_destroyed(&idev->rnc);
+}
+
+static bool isci_check_reqterm(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ struct isci_request *ireq,
+ u32 localcount)
+{
+ unsigned long flags;
+ bool res;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ res = isci_compare_suspendcount(idev, localcount)
+ && !test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ return res;
+}
+
+static bool isci_check_devempty(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ u32 localcount)
+{
+ unsigned long flags;
+ bool res;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ res = isci_compare_suspendcount(idev, localcount)
+ && idev->started_request_count == 0;
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ return res;
+}
+
+enum sci_status isci_remote_device_terminate_requests(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ struct isci_request *ireq)
+{
+ enum sci_status status = SCI_SUCCESS;
+ unsigned long flags;
+ u32 rnc_suspend_count;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+
+ if (isci_get_device(idev) == NULL) {
+ dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n",
+ __func__, idev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ status = SCI_FAILURE;
+ } else {
+ /* If already suspended, don't wait for another suspension. */
+ smp_rmb();
+ rnc_suspend_count
+ = sci_remote_node_context_is_suspended(&idev->rnc)
+ ? 0 : idev->rnc.suspend_count;
+
+ dev_dbg(&ihost->pdev->dev,
+ "%s: idev=%p, ireq=%p; started_request_count=%d, "
+ "rnc_suspend_count=%d, rnc.suspend_count=%d"
+ "about to wait\n",
+ __func__, idev, ireq, idev->started_request_count,
+ rnc_suspend_count, idev->rnc.suspend_count);
+
+ #define MAX_SUSPEND_MSECS 10000
+ if (ireq) {
+ /* Terminate a specific TC. */
+ set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags);
+ sci_remote_device_terminate_req(ihost, idev, 0, ireq);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (!wait_event_timeout(ihost->eventq,
+ isci_check_reqterm(ihost, idev, ireq,
+ rnc_suspend_count),
+ msecs_to_jiffies(MAX_SUSPEND_MSECS))) {
+
+ dev_warn(&ihost->pdev->dev, "%s host%d timeout single\n",
+ __func__, ihost->id);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: ******* Timeout waiting for "
+ "suspend; idev=%p, current state %s; "
+ "started_request_count=%d, flags=%lx\n\t"
+ "rnc_suspend_count=%d, rnc.suspend_count=%d "
+ "RNC: current state %s, current "
+ "suspend_type %x dest state %d;\n"
+ "ireq=%p, ireq->flags = %lx\n",
+ __func__, idev,
+ dev_state_name(idev->sm.current_state_id),
+ idev->started_request_count, idev->flags,
+ rnc_suspend_count, idev->rnc.suspend_count,
+ rnc_state_name(idev->rnc.sm.current_state_id),
+ idev->rnc.suspend_type,
+ idev->rnc.destination_state,
+ ireq, ireq->flags);
+ }
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags);
+ if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
+ isci_free_tag(ihost, ireq->io_tag);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ } else {
+ /* Terminate all TCs. */
+ sci_remote_device_terminate_requests(idev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (!wait_event_timeout(ihost->eventq,
+ isci_check_devempty(ihost, idev,
+ rnc_suspend_count),
+ msecs_to_jiffies(MAX_SUSPEND_MSECS))) {
+
+ dev_warn(&ihost->pdev->dev, "%s host%d timeout all\n",
+ __func__, ihost->id);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: ******* Timeout waiting for "
+ "suspend; idev=%p, current state %s; "
+ "started_request_count=%d, flags=%lx\n\t"
+ "rnc_suspend_count=%d, "
+ "RNC: current state %s, "
+ "rnc.suspend_count=%d, current "
+ "suspend_type %x dest state %d\n",
+ __func__, idev,
+ dev_state_name(idev->sm.current_state_id),
+ idev->started_request_count, idev->flags,
+ rnc_suspend_count,
+ rnc_state_name(idev->rnc.sm.current_state_id),
+ idev->rnc.suspend_count,
+ idev->rnc.suspend_type,
+ idev->rnc.destination_state);
+ }
+ }
+ dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n",
+ __func__, idev);
+ isci_put_device(idev);
+ }
return status;
}
+/**
+* isci_remote_device_not_ready() - This function is called by the ihost when
+* the remote device is not ready. We mark the isci device as ready (not
+* "ready_for_io") and signal the waiting proccess.
+* @ihost: This parameter specifies the isci host object.
+* @idev: This parameter specifies the remote device
+* @reason: Reason to switch on
+*
+* sci_lock is held on entrance to this function.
+*/
+static void isci_remote_device_not_ready(struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ u32 reason)
+{
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p; reason = %d\n", __func__, idev, reason);
+
+ switch (reason) {
+ case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
+ set_bit(IDEV_IO_NCQERROR, &idev->flags);
+
+ /* Suspend the remote device so the I/O can be terminated. */
+ sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL);
+
+ /* Kill all outstanding requests for the device. */
+ sci_remote_device_terminate_requests(idev);
+
+ fallthrough; /* into the default case */
+ default:
+ clear_bit(IDEV_IO_READY, &idev->flags);
+ break;
+ }
+}
+
+/* called once the remote node context is ready to be freed.
+ * The remote device can now report that its stop operation is complete. none
+ */
+static void rnc_destruct_done(void *_dev)
+{
+ struct isci_remote_device *idev = _dev;
+
+ BUG_ON(idev->started_request_count != 0);
+ sci_change_state(&idev->sm, SCI_DEV_STOPPED);
+}
+
+enum sci_status sci_remote_device_terminate_requests(
+ struct isci_remote_device *idev)
+{
+ return sci_remote_device_terminate_reqs_checkabort(idev, 0);
+}
+
enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
u32 timeout)
{
@@ -166,8 +346,8 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
case SCI_DEV_FAILED:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_DEV_STOPPED:
return SCI_SUCCESS;
@@ -190,13 +370,16 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
case SCI_SMP_DEV_IDLE:
case SCI_SMP_DEV_CMD:
sci_change_state(sm, SCI_DEV_STOPPING);
- if (idev->started_request_count == 0) {
+ if (idev->started_request_count == 0)
sci_remote_node_context_destruct(&idev->rnc,
- rnc_destruct_done, idev);
- return SCI_SUCCESS;
- } else
- return sci_remote_device_terminate_requests(idev);
- break;
+ rnc_destruct_done,
+ idev);
+ else {
+ sci_remote_device_suspend(
+ idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
+ sci_remote_device_terminate_requests(idev);
+ }
+ return SCI_SUCCESS;
case SCI_DEV_STOPPING:
/* All requests should have been terminated, but if there is an
* attempt to stop a device already in the stopping state, then
@@ -209,67 +392,6 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
}
}
-enum sci_status sci_remote_device_reset(struct isci_remote_device *idev)
-{
- struct sci_base_state_machine *sm = &idev->sm;
- enum sci_remote_device_states state = sm->current_state_id;
-
- switch (state) {
- case SCI_DEV_INITIAL:
- case SCI_DEV_STOPPED:
- case SCI_DEV_STARTING:
- case SCI_SMP_DEV_IDLE:
- case SCI_SMP_DEV_CMD:
- case SCI_DEV_STOPPING:
- case SCI_DEV_FAILED:
- case SCI_DEV_RESETTING:
- case SCI_DEV_FINAL:
- default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
- return SCI_FAILURE_INVALID_STATE;
- case SCI_DEV_READY:
- case SCI_STP_DEV_IDLE:
- case SCI_STP_DEV_CMD:
- case SCI_STP_DEV_NCQ:
- case SCI_STP_DEV_NCQ_ERROR:
- case SCI_STP_DEV_AWAIT_RESET:
- sci_change_state(sm, SCI_DEV_RESETTING);
- return SCI_SUCCESS;
- }
-}
-
-enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev)
-{
- struct sci_base_state_machine *sm = &idev->sm;
- enum sci_remote_device_states state = sm->current_state_id;
-
- if (state != SCI_DEV_RESETTING) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
- return SCI_FAILURE_INVALID_STATE;
- }
-
- sci_change_state(sm, SCI_DEV_READY);
- return SCI_SUCCESS;
-}
-
-enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
- u32 suspend_type)
-{
- struct sci_base_state_machine *sm = &idev->sm;
- enum sci_remote_device_states state = sm->current_state_id;
-
- if (state != SCI_STP_DEV_CMD) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
- return SCI_FAILURE_INVALID_STATE;
- }
-
- return sci_remote_node_context_suspend(&idev->rnc,
- suspend_type, NULL, NULL);
-}
-
enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,
u32 frame_index)
{
@@ -286,8 +408,8 @@ enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,
case SCI_SMP_DEV_IDLE:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
/* Return the frame back to the controller */
sci_controller_release_frame(ihost, frame_index);
return SCI_FAILURE_INVALID_STATE;
@@ -386,12 +508,24 @@ static bool is_remote_device_ready(struct isci_remote_device *idev)
}
}
+/*
+ * called once the remote node context has transisitioned to a ready
+ * state (after suspending RX and/or TX due to early D2H fis)
+ */
+static void atapi_remote_device_resume_done(void *_dev)
+{
+ struct isci_remote_device *idev = _dev;
+ struct isci_request *ireq = idev->working_request;
+
+ sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
+}
+
enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
u32 event_code)
{
+ enum sci_status status;
struct sci_base_state_machine *sm = &idev->sm;
enum sci_remote_device_states state = sm->current_state_id;
- enum sci_status status;
switch (scu_get_event_type(event_code)) {
case SCU_EVENT_TYPE_RNC_OPS_MISC:
@@ -404,9 +538,7 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
status = SCI_SUCCESS;
/* Suspend the associated RNC */
- sci_remote_node_context_suspend(&idev->rnc,
- SCI_SOFTWARE_SUSPENSION,
- NULL, NULL);
+ sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL);
dev_dbg(scirdev_to_dev(idev),
"%s: device: %p event code: %x: %s\n",
@@ -417,7 +549,7 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
break;
}
- /* Else, fall through and treat as unhandled... */
+ fallthrough; /* and treat as unhandled */
default:
dev_dbg(scirdev_to_dev(idev),
"%s: device: %p event code: %x: %s\n",
@@ -432,6 +564,20 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
if (status != SCI_SUCCESS)
return status;
+ /* Decode device-specific states that may require an RNC resume during
+ * normal operation. When the abort path is active, these resumes are
+ * managed when the abort path exits.
+ */
+ if (state == SCI_STP_DEV_ATAPI_ERROR) {
+ /* For ATAPI error state resume the RNC right away. */
+ if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
+ scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) {
+ return sci_remote_node_context_resume(&idev->rnc,
+ atapi_remote_device_resume_done,
+ idev);
+ }
+ }
+
if (state == SCI_STP_DEV_IDLE) {
/* We pick up suspension events to handle specifically to this
@@ -479,8 +625,8 @@ enum sci_status sci_remote_device_start_io(struct isci_host *ihost,
case SCI_DEV_RESETTING:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_DEV_READY:
/* attempt to start an io request for this device object. The remote
@@ -614,8 +760,8 @@ enum sci_status sci_remote_device_complete_io(struct isci_host *ihost,
case SCI_DEV_FAILED:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_DEV_READY:
case SCI_STP_DEV_AWAIT_RESET:
@@ -625,6 +771,7 @@ enum sci_status sci_remote_device_complete_io(struct isci_host *ihost,
case SCI_STP_DEV_CMD:
case SCI_STP_DEV_NCQ:
case SCI_STP_DEV_NCQ_ERROR:
+ case SCI_STP_DEV_ATAPI_ERROR:
status = common_complete_io(iport, idev, ireq);
if (status != SCI_SUCCESS)
break;
@@ -697,8 +844,8 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
case SCI_DEV_RESETTING:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_STP_DEV_IDLE:
case SCI_STP_DEV_CMD:
@@ -709,10 +856,6 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
if (status != SCI_SUCCESS)
return status;
- status = sci_remote_node_context_start_task(&idev->rnc, ireq);
- if (status != SCI_SUCCESS)
- goto out;
-
status = sci_request_start(ireq);
if (status != SCI_SUCCESS)
goto out;
@@ -731,11 +874,11 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
* the correct action when the remote node context is suspended
* and later resumed.
*/
- sci_remote_node_context_suspend(&idev->rnc,
- SCI_SOFTWARE_SUSPENSION, NULL, NULL);
- sci_remote_node_context_resume(&idev->rnc,
- sci_remote_device_continue_request,
- idev);
+ sci_remote_device_suspend(idev,
+ SCI_SW_SUSPEND_LINKHANG_DETECT);
+
+ status = sci_remote_node_context_start_task(&idev->rnc, ireq,
+ sci_remote_device_continue_request, idev);
out:
sci_remote_device_start_request(idev, ireq, status);
@@ -749,7 +892,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
if (status != SCI_SUCCESS)
return status;
- status = sci_remote_node_context_start_task(&idev->rnc, ireq);
+ /* Resume the RNC as needed: */
+ status = sci_remote_node_context_start_task(&idev->rnc, ireq,
+ NULL, NULL);
if (status != SCI_SUCCESS)
break;
@@ -811,7 +956,7 @@ static void sci_remote_device_initial_state_enter(struct sci_base_state_machine
/**
* sci_remote_device_destruct() - free remote node context and destruct
- * @remote_device: This parameter specifies the remote device to be destructed.
+ * @idev: This parameter specifies the remote device to be destructed.
*
* Remote device objects are a limited resource. As such, they must be
* protected. Thus calls to construct and destruct are mutually exclusive and
@@ -829,8 +974,8 @@ static enum sci_status sci_remote_device_destruct(struct isci_remote_device *ide
struct isci_host *ihost;
if (state != SCI_DEV_STOPPED) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -858,7 +1003,7 @@ static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_
* here should go through isci_remote_device_nuke_requests.
* If we hit this condition, we will need a way to complete
* io requests in process */
- BUG_ON(!list_empty(&idev->reqs_in_process));
+ BUG_ON(idev->started_request_count > 0);
sci_remote_device_destruct(idev);
list_del_init(&idev->node);
@@ -896,9 +1041,9 @@ static void sci_remote_device_ready_state_enter(struct sci_base_state_machine *s
struct isci_host *ihost = idev->owning_port->owning_controller;
struct domain_device *dev = idev->domain_dev;
- if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_SATA)) {
+ if (dev->dev_type == SAS_SATA_DEV || (dev->tproto & SAS_PROTOCOL_SATA)) {
sci_change_state(&idev->sm, SCI_STP_DEV_IDLE);
- } else if (dev_is_expander(dev)) {
+ } else if (dev_is_expander(dev->dev_type)) {
sci_change_state(&idev->sm, SCI_SMP_DEV_IDLE);
} else
isci_remote_device_ready(ihost, idev);
@@ -909,7 +1054,7 @@ static void sci_remote_device_ready_state_exit(struct sci_base_state_machine *sm
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
struct domain_device *dev = idev->domain_dev;
- if (dev->dev_type == SAS_END_DEV) {
+ if (dev->dev_type == SAS_END_DEVICE) {
struct isci_host *ihost = idev->owning_port->owning_controller;
isci_remote_device_not_ready(ihost, idev,
@@ -920,14 +1065,21 @@ static void sci_remote_device_ready_state_exit(struct sci_base_state_machine *sm
static void sci_remote_device_resetting_state_enter(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
+ struct isci_host *ihost = idev->owning_port->owning_controller;
- sci_remote_node_context_suspend(
- &idev->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p\n", __func__, idev);
+
+ sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
}
static void sci_remote_device_resetting_state_exit(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
+ struct isci_host *ihost = idev->owning_port->owning_controller;
+
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p\n", __func__, idev);
sci_remote_node_context_resume(&idev->rnc, NULL, NULL);
}
@@ -1020,6 +1172,7 @@ static const struct sci_base_state sci_remote_device_state_table[] = {
[SCI_STP_DEV_NCQ_ERROR] = {
.enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter,
},
+ [SCI_STP_DEV_ATAPI_ERROR] = { },
[SCI_STP_DEV_AWAIT_RESET] = { },
[SCI_SMP_DEV_IDLE] = {
.enter_state = sci_smp_remote_device_ready_idle_substate_enter,
@@ -1039,8 +1192,8 @@ static const struct sci_base_state sci_remote_device_state_table[] = {
/**
* sci_remote_device_construct() - common construction
- * @sci_port: SAS/SATA port through which this device is accessed.
- * @sci_dev: remote device to construct
+ * @iport: SAS/SATA port through which this device is accessed.
+ * @idev: remote device to construct
*
* This routine just performs benign initialization and does not
* allocate the remote_node_context which is left to
@@ -1059,7 +1212,7 @@ static void sci_remote_device_construct(struct isci_port *iport,
SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX);
}
-/**
+/*
* sci_remote_device_da_construct() - construct direct attached device.
*
* The information (e.g. IAF, Signature FIS, etc.) necessary to build
@@ -1077,37 +1230,27 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport,
struct isci_remote_device *idev)
{
enum sci_status status;
- struct domain_device *dev = idev->domain_dev;
+ struct sci_port_properties properties;
sci_remote_device_construct(iport, idev);
- /*
- * This information is request to determine how many remote node context
- * entries will be needed to store the remote node.
- */
- idev->is_direct_attached = true;
+ sci_port_get_properties(iport, &properties);
+ /* Get accurate port width from port's phy mask for a DA device. */
+ idev->device_port_width = hweight32(properties.phy_mask);
+
status = sci_controller_allocate_remote_node_context(iport->owning_controller,
- idev,
- &idev->rnc.remote_node_index);
+ idev,
+ &idev->rnc.remote_node_index);
if (status != SCI_SUCCESS)
return status;
- if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV ||
- (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev))
- /* pass */;
- else
- return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
-
idev->connection_rate = sci_port_get_max_allowed_speed(iport);
- /* / @todo Should I assign the port width by reading all of the phys on the port? */
- idev->device_port_width = 1;
-
return SCI_SUCCESS;
}
-/**
+/*
* sci_remote_device_ea_construct() - construct expander attached device
*
* Remote node context(s) is/are a global resource allocated by this
@@ -1133,19 +1276,13 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,
if (status != SCI_SUCCESS)
return status;
- if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV ||
- (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev))
- /* pass */;
- else
- return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
-
- /*
- * For SAS-2 the physical link rate is actually a logical link
+ /* For SAS-2 the physical link rate is actually a logical link
* rate that incorporates multiplexing. The SCU doesn't
* incorporate multiplexing and for the purposes of the
* connection the logical link rate is that same as the
* physical. Furthermore, the SAS-2 and SAS-1.1 fields overlay
- * one another, so this code works for both situations. */
+ * one another, so this code works for both situations.
+ */
idev->connection_rate = min_t(u16, sci_port_get_max_allowed_speed(iport),
dev->linkrate);
@@ -1155,11 +1292,110 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,
return SCI_SUCCESS;
}
+enum sci_status sci_remote_device_resume(
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p)
+{
+ enum sci_status status;
+
+ status = sci_remote_node_context_resume(&idev->rnc, cb_fn, cb_p);
+ if (status != SCI_SUCCESS)
+ dev_dbg(scirdev_to_dev(idev), "%s: failed to resume: %d\n",
+ __func__, status);
+ return status;
+}
+
+static void isci_remote_device_resume_from_abort_complete(void *cbparam)
+{
+ struct isci_remote_device *idev = cbparam;
+ struct isci_host *ihost = idev->owning_port->owning_controller;
+ scics_sds_remote_node_context_callback abort_resume_cb =
+ idev->abort_resume_cb;
+
+ dev_dbg(scirdev_to_dev(idev), "%s: passing-along resume: %p\n",
+ __func__, abort_resume_cb);
+
+ if (abort_resume_cb != NULL) {
+ idev->abort_resume_cb = NULL;
+ abort_resume_cb(idev->abort_resume_cbparam);
+ }
+ clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+ wake_up(&ihost->eventq);
+}
+
+static bool isci_remote_device_test_resume_done(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev)
+{
+ unsigned long flags;
+ bool done;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ done = !test_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags)
+ || test_bit(IDEV_STOP_PENDING, &idev->flags)
+ || sci_remote_node_context_is_being_destroyed(&idev->rnc);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ return done;
+}
+
+static void isci_remote_device_wait_for_resume_from_abort(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev)
+{
+ dev_dbg(&ihost->pdev->dev, "%s: starting resume wait: %p\n",
+ __func__, idev);
+
+ #define MAX_RESUME_MSECS 10000
+ if (!wait_event_timeout(ihost->eventq,
+ isci_remote_device_test_resume_done(ihost, idev),
+ msecs_to_jiffies(MAX_RESUME_MSECS))) {
+
+ dev_warn(&ihost->pdev->dev, "%s: #### Timeout waiting for "
+ "resume: %p\n", __func__, idev);
+ }
+ clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+
+ dev_dbg(&ihost->pdev->dev, "%s: resume wait done: %p\n",
+ __func__, idev);
+}
+
+enum sci_status isci_remote_device_resume_from_abort(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev)
+{
+ unsigned long flags;
+ enum sci_status status = SCI_SUCCESS;
+ int destroyed;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ /* Preserve any current resume callbacks, for instance from other
+ * resumptions.
+ */
+ idev->abort_resume_cb = idev->rnc.user_callback;
+ idev->abort_resume_cbparam = idev->rnc.user_cookie;
+ set_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+ clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags);
+ destroyed = sci_remote_node_context_is_being_destroyed(&idev->rnc);
+ if (!destroyed)
+ status = sci_remote_device_resume(
+ idev, isci_remote_device_resume_from_abort_complete,
+ idev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (!destroyed && (status == SCI_SUCCESS))
+ isci_remote_device_wait_for_resume_from_abort(ihost, idev);
+ else
+ clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+
+ return status;
+}
+
/**
* sci_remote_device_start() - This method will start the supplied remote
* device. This method enables normal IO requests to flow through to the
* remote device.
- * @remote_device: This parameter specifies the device to be started.
+ * @idev: This parameter specifies the device to be started.
* @timeout: This parameter specifies the number of milliseconds in which the
* start operation should complete.
*
@@ -1169,21 +1405,20 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,
* the device when there have been no phys added to it.
*/
static enum sci_status sci_remote_device_start(struct isci_remote_device *idev,
- u32 timeout)
+ u32 timeout)
{
struct sci_base_state_machine *sm = &idev->sm;
enum sci_remote_device_states state = sm->current_state_id;
enum sci_status status;
if (state != SCI_DEV_STOPPED) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
- status = sci_remote_node_context_resume(&idev->rnc,
- remote_device_resume_done,
- idev);
+ status = sci_remote_device_resume(idev, remote_device_resume_done,
+ idev);
if (status != SCI_SUCCESS)
return status;
@@ -1199,7 +1434,7 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport,
struct domain_device *dev = idev->domain_dev;
enum sci_status status;
- if (dev->parent && dev_is_expander(dev->parent))
+ if (dev_parent_is_expander(dev))
status = sci_remote_device_ea_construct(iport, idev);
else
status = sci_remote_device_da_construct(iport, idev);
@@ -1221,25 +1456,12 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport,
return status;
}
-void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev)
-{
- DECLARE_COMPLETION_ONSTACK(aborted_task_completion);
-
- dev_dbg(&ihost->pdev->dev,
- "%s: idev = %p\n", __func__, idev);
-
- /* Cleanup all requests pending for this device. */
- isci_terminate_pending_requests(ihost, idev);
-
- dev_dbg(&ihost->pdev->dev,
- "%s: idev = %p, done\n", __func__, idev);
-}
-
/**
+ * isci_remote_device_alloc()
* This function builds the isci_remote_device when a libsas dev_found message
* is received.
- * @isci_host: This parameter specifies the isci host object.
- * @port: This parameter specifies the isci_port conected to this device.
+ * @ihost: This parameter specifies the isci host object.
+ * @iport: This parameter specifies the isci_port connected to this device.
*
* pointer to new isci_remote_device.
*/
@@ -1259,10 +1481,6 @@ isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport)
dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__);
return NULL;
}
-
- if (WARN_ONCE(!list_empty(&idev->reqs_in_process), "found requests in process\n"))
- return NULL;
-
if (WARN_ONCE(!list_empty(&idev->node), "found non-idle remote device\n"))
return NULL;
@@ -1280,8 +1498,7 @@ void isci_remote_device_release(struct kref *kref)
clear_bit(IDEV_STOP_PENDING, &idev->flags);
clear_bit(IDEV_IO_READY, &idev->flags);
clear_bit(IDEV_GONE, &idev->flags);
- clear_bit(IDEV_EH, &idev->flags);
- smp_mb__before_clear_bit();
+ smp_mb__before_atomic();
clear_bit(IDEV_ALLOCATED, &idev->flags);
wake_up(&ihost->eventq);
}
@@ -1289,8 +1506,8 @@ void isci_remote_device_release(struct kref *kref)
/**
* isci_remote_device_stop() - This function is called internally to stop the
* remote device.
- * @isci_host: This parameter specifies the isci host object.
- * @isci_device: This parameter specifies the remote device.
+ * @ihost: This parameter specifies the isci host object.
+ * @idev: This parameter specifies the remote device.
*
* The status of the ihost request to stop.
*/
@@ -1305,14 +1522,8 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem
spin_lock_irqsave(&ihost->scic_lock, flags);
idev->domain_dev->lldd_dev = NULL; /* disable new lookups */
set_bit(IDEV_GONE, &idev->flags);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- /* Kill all outstanding requests. */
- isci_remote_device_nuke_requests(ihost, idev);
set_bit(IDEV_STOP_PENDING, &idev->flags);
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
status = sci_remote_device_stop(idev, 50);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
@@ -1322,14 +1533,16 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem
else
wait_for_device_stop(ihost, idev);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p, waiting done.\n", __func__, idev);
+
return status;
}
/**
* isci_remote_device_gone() - This function is called by libsas when a domain
* device is removed.
- * @domain_device: This parameter specifies the libsas domain device.
- *
+ * @dev: This parameter specifies the libsas domain device.
*/
void isci_remote_device_gone(struct domain_device *dev)
{
@@ -1349,38 +1562,21 @@ void isci_remote_device_gone(struct domain_device *dev)
* device is discovered. A remote device object is created and started. the
* function then sleeps until the sci core device started message is
* received.
- * @domain_device: This parameter specifies the libsas domain device.
+ * @dev: This parameter specifies the libsas domain device.
*
* status, zero indicates success.
*/
-int isci_remote_device_found(struct domain_device *domain_dev)
+int isci_remote_device_found(struct domain_device *dev)
{
- struct isci_host *isci_host = dev_to_ihost(domain_dev);
- struct isci_port *isci_port;
- struct isci_phy *isci_phy;
- struct asd_sas_port *sas_port;
- struct asd_sas_phy *sas_phy;
+ struct isci_host *isci_host = dev_to_ihost(dev);
+ struct isci_port *isci_port = dev->port->lldd_port;
struct isci_remote_device *isci_device;
enum sci_status status;
dev_dbg(&isci_host->pdev->dev,
- "%s: domain_device = %p\n", __func__, domain_dev);
-
- wait_for_start(isci_host);
-
- sas_port = domain_dev->port;
- sas_phy = list_first_entry(&sas_port->phy_list, struct asd_sas_phy,
- port_phy_el);
- isci_phy = to_iphy(sas_phy);
- isci_port = isci_phy->isci_port;
-
- /* we are being called for a device on this port,
- * so it has to come up eventually
- */
- wait_for_completion(&isci_port->start_complete);
+ "%s: domain_device = %p\n", __func__, dev);
- if ((isci_stopping == isci_port_get_state(isci_port)) ||
- (isci_stopped == isci_port_get_state(isci_port)))
+ if (!isci_port)
return -ENODEV;
isci_device = isci_remote_device_alloc(isci_host, isci_port);
@@ -1391,7 +1587,7 @@ int isci_remote_device_found(struct domain_device *domain_dev)
INIT_LIST_HEAD(&isci_device->node);
spin_lock_irq(&isci_host->scic_lock);
- isci_device->domain_dev = domain_dev;
+ isci_device->domain_dev = dev;
isci_device->isci_port = isci_port;
list_add_tail(&isci_device->node, &isci_port->remote_dev_list);
@@ -1404,7 +1600,7 @@ int isci_remote_device_found(struct domain_device *domain_dev)
if (status == SCI_SUCCESS) {
/* device came up, advertise it to the world */
- domain_dev->lldd_dev = isci_device;
+ dev->lldd_dev = isci_device;
} else
isci_put_device(isci_device);
spin_unlock_irq(&isci_host->scic_lock);
@@ -1414,88 +1610,59 @@ int isci_remote_device_found(struct domain_device *domain_dev)
return status == SCI_SUCCESS ? 0 : -ENODEV;
}
-/**
- * isci_device_is_reset_pending() - This function will check if there is any
- * pending reset condition on the device.
- * @request: This parameter is the isci_device object.
- *
- * true if there is a reset pending for the device.
- */
-bool isci_device_is_reset_pending(
- struct isci_host *isci_host,
- struct isci_remote_device *isci_device)
+
+enum sci_status isci_remote_device_suspend_terminate(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ struct isci_request *ireq)
{
- struct isci_request *isci_request;
- struct isci_request *tmp_req;
- bool reset_is_pending = false;
unsigned long flags;
+ enum sci_status status;
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p\n", __func__, isci_device);
-
- spin_lock_irqsave(&isci_host->scic_lock, flags);
-
- /* Check for reset on all pending requests. */
- list_for_each_entry_safe(isci_request, tmp_req,
- &isci_device->reqs_in_process, dev_node) {
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p request = %p\n",
- __func__, isci_device, isci_request);
-
- if (isci_request->ttype == io_task) {
- struct sas_task *task = isci_request_access_task(
- isci_request);
-
- spin_lock(&task->task_state_lock);
- if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
- reset_is_pending = true;
- spin_unlock(&task->task_state_lock);
- }
- }
-
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ /* Put the device into suspension. */
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ set_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags);
+ sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p reset_is_pending = %d\n",
- __func__, isci_device, reset_is_pending);
+ /* Terminate and wait for the completions. */
+ status = isci_remote_device_terminate_requests(ihost, idev, ireq);
+ if (status != SCI_SUCCESS)
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_remote_device_terminate_requests(%p) "
+ "returned %d!\n",
+ __func__, idev, status);
- return reset_is_pending;
+ /* NOTE: RNC resumption is left to the caller! */
+ return status;
}
-/**
- * isci_device_clear_reset_pending() - This function will clear if any pending
- * reset condition flags on the device.
- * @request: This parameter is the isci_device object.
- *
- * true if there is a reset pending for the device.
- */
-void isci_device_clear_reset_pending(struct isci_host *ihost, struct isci_remote_device *idev)
+int isci_remote_device_is_safe_to_abort(
+ struct isci_remote_device *idev)
{
- struct isci_request *isci_request;
- struct isci_request *tmp_req;
- unsigned long flags = 0;
-
- dev_dbg(&ihost->pdev->dev, "%s: idev=%p, ihost=%p\n",
- __func__, idev, ihost);
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
-
- /* Clear reset pending on all pending requests. */
- list_for_each_entry_safe(isci_request, tmp_req,
- &idev->reqs_in_process, dev_node) {
- dev_dbg(&ihost->pdev->dev, "%s: idev = %p request = %p\n",
- __func__, idev, isci_request);
-
- if (isci_request->ttype == io_task) {
+ return sci_remote_node_context_is_safe_to_abort(&idev->rnc);
+}
- unsigned long flags2;
- struct sas_task *task = isci_request_access_task(
- isci_request);
+enum sci_status sci_remote_device_abort_requests_pending_abort(
+ struct isci_remote_device *idev)
+{
+ return sci_remote_device_terminate_reqs_checkabort(idev, 1);
+}
- spin_lock_irqsave(&task->task_state_lock, flags2);
- task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
- spin_unlock_irqrestore(&task->task_state_lock, flags2);
- }
+void isci_dev_set_hang_detection_timeout(
+ struct isci_remote_device *idev,
+ u32 timeout)
+{
+ if (dev_is_sata(idev->domain_dev)) {
+ if (timeout) {
+ if (test_and_set_bit(IDEV_RNC_LLHANG_ENABLED,
+ &idev->flags))
+ return; /* Already enabled. */
+ } else if (!test_and_clear_bit(IDEV_RNC_LLHANG_ENABLED,
+ &idev->flags))
+ return; /* Not enabled. */
+
+ sci_port_set_hang_detection_timeout(idev->owning_port,
+ timeout);
}
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
}
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
index 57ccfc3d6ad3..c1fdf45751cd 100644
--- a/drivers/scsi/isci/remote_device.h
+++ b/drivers/scsi/isci/remote_device.h
@@ -82,31 +82,41 @@ struct isci_remote_device {
#define IDEV_START_PENDING 0
#define IDEV_STOP_PENDING 1
#define IDEV_ALLOCATED 2
- #define IDEV_EH 3
- #define IDEV_GONE 4
- #define IDEV_IO_READY 5
- #define IDEV_IO_NCQERROR 6
+ #define IDEV_GONE 3
+ #define IDEV_IO_READY 4
+ #define IDEV_IO_NCQERROR 5
+ #define IDEV_RNC_LLHANG_ENABLED 6
+ #define IDEV_ABORT_PATH_ACTIVE 7
+ #define IDEV_ABORT_PATH_RESUME_PENDING 8
unsigned long flags;
struct kref kref;
struct isci_port *isci_port;
struct domain_device *domain_dev;
struct list_head node;
- struct list_head reqs_in_process;
struct sci_base_state_machine sm;
u32 device_port_width;
enum sas_linkrate connection_rate;
- bool is_direct_attached;
struct isci_port *owning_port;
struct sci_remote_node_context rnc;
/* XXX unify with device reference counting and delete */
u32 started_request_count;
struct isci_request *working_request;
u32 not_ready_reason;
+ scics_sds_remote_node_context_callback abort_resume_cb;
+ void *abort_resume_cbparam;
};
#define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000
/* device reference routines must be called under sci_lock */
+static inline struct isci_remote_device *isci_get_device(
+ struct isci_remote_device *idev)
+{
+ if (idev)
+ kref_get(&idev->kref);
+ return idev;
+}
+
static inline struct isci_remote_device *isci_lookup_device(struct domain_device *dev)
{
struct isci_remote_device *idev = dev->lldd_dev;
@@ -132,10 +142,7 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost,
struct isci_remote_device *idev);
void isci_remote_device_gone(struct domain_device *domain_dev);
int isci_remote_device_found(struct domain_device *domain_dev);
-bool isci_device_is_reset_pending(struct isci_host *ihost,
- struct isci_remote_device *idev);
-void isci_device_clear_reset_pending(struct isci_host *ihost,
- struct isci_remote_device *idev);
+
/**
* sci_remote_device_stop() - This method will stop both transmission and
* reception of link activity for the supplied remote device. This method
@@ -153,143 +160,103 @@ enum sci_status sci_remote_device_stop(
u32 timeout);
/**
- * sci_remote_device_reset() - This method will reset the device making it
- * ready for operation. This method must be called anytime the device is
- * reset either through a SMP phy control or a port hard reset request.
- * @remote_device: This parameter specifies the device to be reset.
- *
- * This method does not actually cause the device hardware to be reset. This
- * method resets the software object so that it will be operational after a
- * device hardware reset completes. An indication of whether the device reset
- * was accepted. SCI_SUCCESS This value is returned if the device reset is
- * started.
- */
-enum sci_status sci_remote_device_reset(
- struct isci_remote_device *idev);
-
-/**
- * sci_remote_device_reset_complete() - This method informs the device object
- * that the reset operation is complete and the device can resume operation
- * again.
- * @remote_device: This parameter specifies the device which is to be informed
- * of the reset complete operation.
- *
- * An indication that the device is resuming operation. SCI_SUCCESS the device
- * is resuming operation.
- */
-enum sci_status sci_remote_device_reset_complete(
- struct isci_remote_device *idev);
-
-/**
* enum sci_remote_device_states - This enumeration depicts all the states
* for the common remote device state machine.
+ * @SCI_DEV_INITIAL: Simply the initial state for the base remote device
+ * state machine.
+ *
+ * @SCI_DEV_STOPPED: This state indicates that the remote device has
+ * successfully been stopped. In this state no new IO operations are
+ * permitted. This state is entered from the INITIAL state. This state
+ * is entered from the STOPPING state.
+ *
+ * @SCI_DEV_STARTING: This state indicates the the remote device is in
+ * the process of becoming ready (i.e. starting). In this state no new
+ * IO operations are permitted. This state is entered from the STOPPED
+ * state.
+ *
+ * @SCI_DEV_READY: This state indicates the remote device is now ready.
+ * Thus, the user is able to perform IO operations on the remote device.
+ * This state is entered from the STARTING state.
+ *
+ * @SCI_STP_DEV_IDLE: This is the idle substate for the stp remote
+ * device. When there are no active IO for the device it is is in this
+ * state.
+ *
+ * @SCI_STP_DEV_CMD: This is the command state for the STP remote
+ * device. This state is entered when the device is processing a
+ * non-NCQ command. The device object will fail any new start IO
+ * requests until this command is complete.
+ *
+ * @SCI_STP_DEV_NCQ: This is the NCQ state for the STP remote device.
+ * This state is entered when the device is processing an NCQ reuqest.
+ * It will remain in this state so long as there is one or more NCQ
+ * requests being processed.
*
+ * @SCI_STP_DEV_NCQ_ERROR: This is the NCQ error state for the STP
+ * remote device. This state is entered when an SDB error FIS is
+ * received by the device object while in the NCQ state. The device
+ * object will only accept a READ LOG command while in this state.
*
+ * @SCI_STP_DEV_ATAPI_ERROR: This is the ATAPI error state for the STP
+ * ATAPI remote device. This state is entered when ATAPI device sends
+ * error status FIS without data while the device object is in CMD
+ * state. A suspension event is expected in this state. The device
+ * object will resume right away.
+ *
+ * @SCI_STP_DEV_AWAIT_RESET: This is the READY substate indicates the
+ * device is waiting for the RESET task coming to be recovered from
+ * certain hardware specific error.
+ *
+ * @SCI_SMP_DEV_IDLE: This is the ready operational substate for the
+ * remote device. This is the normal operational state for a remote
+ * device.
+ *
+ * @SCI_SMP_DEV_CMD: This is the suspended state for the remote device.
+ * This is the state that the device is placed in when a RNC suspend is
+ * received by the SCU hardware.
+ *
+ * @SCI_DEV_STOPPING: This state indicates that the remote device is in
+ * the process of stopping. In this state no new IO operations are
+ * permitted, but existing IO operations are allowed to complete. This
+ * state is entered from the READY state. This state is entered from
+ * the FAILED state.
+ *
+ * @SCI_DEV_FAILED: This state indicates that the remote device has
+ * failed. In this state no new IO operations are permitted. This
+ * state is entered from the INITIALIZING state. This state is entered
+ * from the READY state.
+ *
+ * @SCI_DEV_RESETTING: This state indicates the device is being reset.
+ * In this state no new IO operations are permitted. This state is
+ * entered from the READY state.
+ *
+ * @SCI_DEV_FINAL: Simply the final state for the base remote device
+ * state machine.
*/
-enum sci_remote_device_states {
- /**
- * Simply the initial state for the base remote device state machine.
- */
- SCI_DEV_INITIAL,
-
- /**
- * This state indicates that the remote device has successfully been
- * stopped. In this state no new IO operations are permitted.
- * This state is entered from the INITIAL state.
- * This state is entered from the STOPPING state.
- */
- SCI_DEV_STOPPED,
-
- /**
- * This state indicates the the remote device is in the process of
- * becoming ready (i.e. starting). In this state no new IO operations
- * are permitted.
- * This state is entered from the STOPPED state.
- */
- SCI_DEV_STARTING,
-
- /**
- * This state indicates the remote device is now ready. Thus, the user
- * is able to perform IO operations on the remote device.
- * This state is entered from the STARTING state.
- */
- SCI_DEV_READY,
-
- /**
- * This is the idle substate for the stp remote device. When there are no
- * active IO for the device it is is in this state.
- */
- SCI_STP_DEV_IDLE,
-
- /**
- * This is the command state for for the STP remote device. This state is
- * entered when the device is processing a non-NCQ command. The device object
- * will fail any new start IO requests until this command is complete.
- */
- SCI_STP_DEV_CMD,
-
- /**
- * This is the NCQ state for the STP remote device. This state is entered
- * when the device is processing an NCQ reuqest. It will remain in this state
- * so long as there is one or more NCQ requests being processed.
- */
- SCI_STP_DEV_NCQ,
-
- /**
- * This is the NCQ error state for the STP remote device. This state is
- * entered when an SDB error FIS is received by the device object while in the
- * NCQ state. The device object will only accept a READ LOG command while in
- * this state.
- */
- SCI_STP_DEV_NCQ_ERROR,
-
- /**
- * This is the READY substate indicates the device is waiting for the RESET task
- * coming to be recovered from certain hardware specific error.
- */
- SCI_STP_DEV_AWAIT_RESET,
-
- /**
- * This is the ready operational substate for the remote device. This is the
- * normal operational state for a remote device.
- */
- SCI_SMP_DEV_IDLE,
-
- /**
- * This is the suspended state for the remote device. This is the state that
- * the device is placed in when a RNC suspend is received by the SCU hardware.
- */
- SCI_SMP_DEV_CMD,
-
- /**
- * This state indicates that the remote device is in the process of
- * stopping. In this state no new IO operations are permitted, but
- * existing IO operations are allowed to complete.
- * This state is entered from the READY state.
- * This state is entered from the FAILED state.
- */
- SCI_DEV_STOPPING,
-
- /**
- * This state indicates that the remote device has failed.
- * In this state no new IO operations are permitted.
- * This state is entered from the INITIALIZING state.
- * This state is entered from the READY state.
- */
- SCI_DEV_FAILED,
-
- /**
- * This state indicates the device is being reset.
- * In this state no new IO operations are permitted.
- * This state is entered from the READY state.
- */
- SCI_DEV_RESETTING,
-
- /**
- * Simply the final state for the base remote device state machine.
- */
- SCI_DEV_FINAL,
-};
+#define REMOTE_DEV_STATES {\
+ C(DEV_INITIAL),\
+ C(DEV_STOPPED),\
+ C(DEV_STARTING),\
+ C(DEV_READY),\
+ C(STP_DEV_IDLE),\
+ C(STP_DEV_CMD),\
+ C(STP_DEV_NCQ),\
+ C(STP_DEV_NCQ_ERROR),\
+ C(STP_DEV_ATAPI_ERROR),\
+ C(STP_DEV_AWAIT_RESET),\
+ C(SMP_DEV_IDLE),\
+ C(SMP_DEV_CMD),\
+ C(DEV_STOPPING),\
+ C(DEV_FAILED),\
+ C(DEV_RESETTING),\
+ C(DEV_FINAL),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_remote_device_states REMOTE_DEV_STATES;
+#undef C
+const char *dev_state_name(enum sci_remote_device_states state);
static inline struct isci_remote_device *rnc_to_dev(struct sci_remote_node_context *rnc)
{
@@ -300,11 +267,6 @@ static inline struct isci_remote_device *rnc_to_dev(struct sci_remote_node_conte
return idev;
}
-static inline bool dev_is_expander(struct domain_device *dev)
-{
- return dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV;
-}
-
static inline void sci_remote_device_decrement_request_count(struct isci_remote_device *idev)
{
/* XXX delete this voodoo when converting to the top-level device
@@ -318,6 +280,8 @@ static inline void sci_remote_device_decrement_request_count(struct isci_remote_
idev->started_request_count--;
}
+void isci_dev_set_hang_detection_timeout(struct isci_remote_device *idev, u32 timeout);
+
enum sci_status sci_remote_device_frame_handler(
struct isci_remote_device *idev,
u32 frame_index);
@@ -341,12 +305,46 @@ enum sci_status sci_remote_device_complete_io(
struct isci_remote_device *idev,
struct isci_request *ireq);
-enum sci_status sci_remote_device_suspend(
- struct isci_remote_device *idev,
- u32 suspend_type);
-
void sci_remote_device_post_request(
struct isci_remote_device *idev,
u32 request);
+enum sci_status sci_remote_device_terminate_requests(
+ struct isci_remote_device *idev);
+
+int isci_remote_device_is_safe_to_abort(
+ struct isci_remote_device *idev);
+
+enum sci_status
+sci_remote_device_abort_requests_pending_abort(
+ struct isci_remote_device *idev);
+
+enum sci_status isci_remote_device_suspend(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev);
+
+enum sci_status sci_remote_device_resume(
+ struct isci_remote_device *idev,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p);
+
+enum sci_status isci_remote_device_resume_from_abort(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev);
+
+enum sci_status isci_remote_device_reset(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev);
+
+enum sci_status isci_remote_device_suspend_terminate(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ struct isci_request *ireq);
+
+enum sci_status isci_remote_device_terminate_requests(
+ struct isci_host *ihost,
+ struct isci_remote_device *idev,
+ struct isci_request *ireq);
+enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
+ enum sci_remote_node_suspension_reasons reason);
#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */
diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c
index 748e8339d1ec..77ba0291134e 100644
--- a/drivers/scsi/isci/remote_node_context.c
+++ b/drivers/scsi/isci/remote_node_context.c
@@ -52,7 +52,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
+#include <scsi/sas_ata.h>
#include "host.h"
#include "isci.h"
#include "remote_device.h"
@@ -60,21 +60,21 @@
#include "scu_event_codes.h"
#include "scu_task_context.h"
+#undef C
+#define C(a) (#a)
+const char *rnc_state_name(enum scis_sds_remote_node_context_states state)
+{
+ static const char * const strings[] = RNC_STATES;
-/**
- *
- * @sci_rnc: The RNC for which the is posted request is being made.
- *
- * This method will return true if the RNC is not in the initial state. In all
- * other states the RNC is considered active and this will return true. The
- * destroy request of the state machine drives the RNC back to the initial
- * state. If the state machine changes then this routine will also have to be
- * changed. bool true if the state machine is not in the initial state false if
- * the state machine is in the initial state
- */
+ if (state >= ARRAY_SIZE(strings))
+ return "UNKNOWN";
+
+ return strings[state];
+}
+#undef C
/**
- *
+ * sci_remote_node_context_is_ready()
* @sci_rnc: The state of the remote node context object to check.
*
* This method will return true if the remote node context is in a READY state
@@ -93,6 +93,15 @@ bool sci_remote_node_context_is_ready(
return false;
}
+bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc)
+{
+ u32 current_state = sci_rnc->sm.current_state_id;
+
+ if (current_state == SCI_RNC_TX_RX_SUSPENDED)
+ return true;
+ return false;
+}
+
static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id)
{
if (id < ihost->remote_node_entries &&
@@ -134,7 +143,7 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
rnc->ssp.arbitration_wait_time = 0;
- if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
+ if (dev_is_sata(dev)) {
rnc->ssp.connection_occupancy_timeout =
ihost->user_parameters.stp_max_occupancy_timeout;
rnc->ssp.connection_inactivity_timeout =
@@ -154,13 +163,7 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
rnc->ssp.oaf_source_zone_group = 0;
rnc->ssp.oaf_more_compatibility_features = 0;
}
-
-/**
- *
- * @sci_rnc:
- * @callback:
- * @callback_parameter:
- *
+/*
* This method will setup the remote node context object so it will transition
* to its ready state. If the remote node context is already setup to
* transition to its final state then this function does nothing. none
@@ -168,28 +171,33 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
static void sci_remote_node_context_setup_to_resume(
struct sci_remote_node_context *sci_rnc,
scics_sds_remote_node_context_callback callback,
- void *callback_parameter)
+ void *callback_parameter,
+ enum sci_remote_node_context_destination_state dest_param)
{
- if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) {
- sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY;
- sci_rnc->user_callback = callback;
- sci_rnc->user_cookie = callback_parameter;
+ if (sci_rnc->destination_state != RNC_DEST_FINAL) {
+ sci_rnc->destination_state = dest_param;
+ if (callback != NULL) {
+ sci_rnc->user_callback = callback;
+ sci_rnc->user_cookie = callback_parameter;
+ }
}
}
-static void sci_remote_node_context_setup_to_destory(
+static void sci_remote_node_context_setup_to_destroy(
struct sci_remote_node_context *sci_rnc,
scics_sds_remote_node_context_callback callback,
void *callback_parameter)
{
- sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL;
+ struct isci_host *ihost = idev_to_ihost(rnc_to_dev(sci_rnc));
+
+ sci_rnc->destination_state = RNC_DEST_FINAL;
sci_rnc->user_callback = callback;
sci_rnc->user_cookie = callback_parameter;
+
+ wake_up(&ihost->eventq);
}
-/**
- *
- *
+/*
* This method just calls the user callback function and then resets the
* callback.
*/
@@ -206,9 +214,19 @@ static void sci_remote_node_context_notify_user(
static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc)
{
- if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
+ switch (rnc->destination_state) {
+ case RNC_DEST_READY:
+ case RNC_DEST_SUSPENDED_RESUME:
+ rnc->destination_state = RNC_DEST_READY;
+ fallthrough;
+ case RNC_DEST_FINAL:
sci_remote_node_context_resume(rnc, rnc->user_callback,
- rnc->user_cookie);
+ rnc->user_cookie);
+ break;
+ default:
+ rnc->destination_state = RNC_DEST_UNSPECIFIED;
+ break;
+ }
}
static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc)
@@ -222,13 +240,12 @@ static void sci_remote_node_context_validate_context_buffer(struct sci_remote_no
rnc_buffer->ssp.is_valid = true;
- if (!idev->is_direct_attached &&
- (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))) {
+ if (dev_is_sata(dev) && dev->parent) {
sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_96);
} else {
sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_32);
- if (idev->is_direct_attached)
+ if (!dev->parent)
sci_port_setup_transports(idev->owning_port,
sci_rnc->remote_node_index);
}
@@ -251,13 +268,18 @@ static void sci_remote_node_context_invalidate_context_buffer(struct sci_remote_
static void sci_remote_node_context_initial_state_enter(struct sci_base_state_machine *sm)
{
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
+ struct isci_remote_device *idev = rnc_to_dev(rnc);
+ struct isci_host *ihost = idev->owning_port->owning_controller;
/* Check to see if we have gotten back to the initial state because
* someone requested to destroy the remote node context object.
*/
if (sm->previous_state_id == SCI_RNC_INVALIDATING) {
- rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+ rnc->destination_state = RNC_DEST_UNSPECIFIED;
sci_remote_node_context_notify_user(rnc);
+
+ smp_wmb();
+ wake_up(&ihost->eventq);
}
}
@@ -272,6 +294,8 @@ static void sci_remote_node_context_invalidating_state_enter(struct sci_base_sta
{
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
+ /* Terminate all outstanding requests. */
+ sci_remote_device_terminate_requests(rnc_to_dev(rnc));
sci_remote_node_context_invalidate_context_buffer(rnc);
}
@@ -290,10 +314,8 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m
* resume because of a target reset we also need to update
* the STPTLDARNI register with the RNi of the device
*/
- if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) &&
- idev->is_direct_attached)
- sci_port_setup_transports(idev->owning_port,
- rnc->remote_node_index);
+ if (dev_is_sata(dev) && !dev->parent)
+ sci_port_setup_transports(idev->owning_port, rnc->remote_node_index);
sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME);
}
@@ -301,10 +323,22 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m
static void sci_remote_node_context_ready_state_enter(struct sci_base_state_machine *sm)
{
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
+ enum sci_remote_node_context_destination_state dest_select;
+ int tell_user = 1;
+
+ dest_select = rnc->destination_state;
+ rnc->destination_state = RNC_DEST_UNSPECIFIED;
- rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+ if ((dest_select == RNC_DEST_SUSPENDED) ||
+ (dest_select == RNC_DEST_SUSPENDED_RESUME)) {
+ sci_remote_node_context_suspend(
+ rnc, rnc->suspend_reason,
+ SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT);
- if (rnc->user_callback)
+ if (dest_select == RNC_DEST_SUSPENDED_RESUME)
+ tell_user = 0; /* Wait until ready again. */
+ }
+ if (tell_user)
sci_remote_node_context_notify_user(rnc);
}
@@ -318,10 +352,34 @@ static void sci_remote_node_context_tx_suspended_state_enter(struct sci_base_sta
static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm)
{
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
+ struct isci_remote_device *idev = rnc_to_dev(rnc);
+ struct isci_host *ihost = idev->owning_port->owning_controller;
+ u32 new_count = rnc->suspend_count + 1;
+ if (new_count == 0)
+ rnc->suspend_count = 1;
+ else
+ rnc->suspend_count = new_count;
+ smp_wmb();
+
+ /* Terminate outstanding requests pending abort. */
+ sci_remote_device_abort_requests_pending_abort(idev);
+
+ wake_up(&ihost->eventq);
sci_remote_node_context_continue_state_transitions(rnc);
}
+static void sci_remote_node_context_await_suspend_state_exit(
+ struct sci_base_state_machine *sm)
+{
+ struct sci_remote_node_context *rnc
+ = container_of(sm, typeof(*rnc), sm);
+ struct isci_remote_device *idev = rnc_to_dev(rnc);
+
+ if (dev_is_sata(idev->domain_dev))
+ isci_dev_set_hang_detection_timeout(idev, 0);
+}
+
static const struct sci_base_state sci_remote_node_context_state_table[] = {
[SCI_RNC_INITIAL] = {
.enter_state = sci_remote_node_context_initial_state_enter,
@@ -344,7 +402,9 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = {
[SCI_RNC_TX_RX_SUSPENDED] = {
.enter_state = sci_remote_node_context_tx_rx_suspended_state_enter,
},
- [SCI_RNC_AWAIT_SUSPENSION] = { },
+ [SCI_RNC_AWAIT_SUSPENSION] = {
+ .exit_state = sci_remote_node_context_await_suspend_state_exit,
+ },
};
void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
@@ -353,7 +413,7 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
memset(rnc, 0, sizeof(struct sci_remote_node_context));
rnc->remote_node_index = remote_node_index;
- rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+ rnc->destination_state = RNC_DEST_UNSPECIFIED;
sci_init_sm(&rnc->sm, sci_remote_node_context_state_table, SCI_RNC_INITIAL);
}
@@ -362,6 +422,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
u32 event_code)
{
enum scis_sds_remote_node_context_states state;
+ u32 next_state;
state = sci_rnc->sm.current_state_id;
switch (state) {
@@ -376,20 +437,20 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
break;
case SCI_RNC_INVALIDATING:
if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) {
- if (sci_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL)
- state = SCI_RNC_INITIAL;
+ if (sci_rnc->destination_state == RNC_DEST_FINAL)
+ next_state = SCI_RNC_INITIAL;
else
- state = SCI_RNC_POSTING;
- sci_change_state(&sci_rnc->sm, state);
+ next_state = SCI_RNC_POSTING;
+ sci_change_state(&sci_rnc->sm, next_state);
} else {
switch (scu_get_event_type(event_code)) {
case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
/* We really dont care if the hardware is going to suspend
* the device since it's being invalidated anyway */
- dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+ dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: SCIC Remote Node Context 0x%p was "
- "suspeneded by hardware while being "
+ "suspended by hardware while being "
"invalidated.\n", __func__, sci_rnc);
break;
default:
@@ -406,9 +467,9 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
/* We really dont care if the hardware is going to suspend
* the device since it's being resumed anyway */
- dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+ dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: SCIC Remote Node Context 0x%p was "
- "suspeneded by hardware while being resumed.\n",
+ "suspended by hardware while being resumed.\n",
__func__, sci_rnc);
break;
default:
@@ -420,11 +481,11 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
switch (scu_get_event_type(event_code)) {
case SCU_EVENT_TL_RNC_SUSPEND_TX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED);
- sci_rnc->suspension_code = scu_get_event_specifier(event_code);
+ sci_rnc->suspend_type = scu_get_event_type(event_code);
break;
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);
- sci_rnc->suspension_code = scu_get_event_specifier(event_code);
+ sci_rnc->suspend_type = scu_get_event_type(event_code);
break;
default:
goto out;
@@ -433,27 +494,29 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
case SCI_RNC_AWAIT_SUSPENSION:
switch (scu_get_event_type(event_code)) {
case SCU_EVENT_TL_RNC_SUSPEND_TX:
- sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED);
- sci_rnc->suspension_code = scu_get_event_specifier(event_code);
+ next_state = SCI_RNC_TX_SUSPENDED;
break;
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
- sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);
- sci_rnc->suspension_code = scu_get_event_specifier(event_code);
+ next_state = SCI_RNC_TX_RX_SUSPENDED;
break;
default:
goto out;
}
+ if (sci_rnc->suspend_type == scu_get_event_type(event_code))
+ sci_change_state(&sci_rnc->sm, next_state);
break;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: invalid state %d\n", __func__, state);
+ "%s: invalid state: %s\n", __func__,
+ rnc_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
return SCI_SUCCESS;
out:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: code: %#x state: %d\n", __func__, event_code, state);
+ "%s: code: %#x state: %s\n", __func__, event_code,
+ rnc_state_name(state));
return SCI_FAILURE;
}
@@ -467,20 +530,23 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
state = sci_rnc->sm.current_state_id;
switch (state) {
case SCI_RNC_INVALIDATING:
- sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
+ sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
return SCI_SUCCESS;
case SCI_RNC_POSTING:
case SCI_RNC_RESUMING:
case SCI_RNC_READY:
case SCI_RNC_TX_SUSPENDED:
case SCI_RNC_TX_RX_SUSPENDED:
- case SCI_RNC_AWAIT_SUSPENSION:
- sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
+ sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
return SCI_SUCCESS;
+ case SCI_RNC_AWAIT_SUSPENSION:
+ sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
+ return SCI_SUCCESS;
case SCI_RNC_INITIAL:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: invalid state %d\n", __func__, state);
+ "%s: invalid state: %s\n", __func__,
+ rnc_state_name(state));
/* We have decided that the destruct request on the remote node context
* can not fail since it is either in the initial/destroyed state or is
* can be destroyed.
@@ -488,35 +554,101 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
return SCI_SUCCESS;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: invalid state %d\n", __func__, state);
+ "%s: invalid state %s\n", __func__,
+ rnc_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
-enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc,
- u32 suspend_type,
- scics_sds_remote_node_context_callback cb_fn,
- void *cb_p)
+enum sci_status sci_remote_node_context_suspend(
+ struct sci_remote_node_context *sci_rnc,
+ enum sci_remote_node_suspension_reasons suspend_reason,
+ u32 suspend_type)
{
- enum scis_sds_remote_node_context_states state;
+ enum scis_sds_remote_node_context_states state
+ = sci_rnc->sm.current_state_id;
+ struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
+ enum sci_status status = SCI_FAILURE_INVALID_STATE;
+ enum sci_remote_node_context_destination_state dest_param =
+ RNC_DEST_UNSPECIFIED;
+
+ dev_dbg(scirdev_to_dev(idev),
+ "%s: current state %s, current suspend_type %x dest state %d,"
+ " arg suspend_reason %d, arg suspend_type %x",
+ __func__, rnc_state_name(state), sci_rnc->suspend_type,
+ sci_rnc->destination_state, suspend_reason,
+ suspend_type);
+
+ /* Disable automatic state continuations if explicitly suspending. */
+ if ((suspend_reason == SCI_HW_SUSPEND) ||
+ (sci_rnc->destination_state == RNC_DEST_FINAL))
+ dest_param = sci_rnc->destination_state;
- state = sci_rnc->sm.current_state_id;
- if (state != SCI_RNC_READY) {
+ switch (state) {
+ case SCI_RNC_READY:
+ break;
+ case SCI_RNC_INVALIDATING:
+ if (sci_rnc->destination_state == RNC_DEST_FINAL) {
+ dev_warn(scirdev_to_dev(idev),
+ "%s: already destroying %p\n",
+ __func__, sci_rnc);
+ return SCI_FAILURE_INVALID_STATE;
+ }
+ fallthrough; /* and handle like SCI_RNC_POSTING */
+ case SCI_RNC_RESUMING:
+ fallthrough; /* and handle like SCI_RNC_POSTING */
+ case SCI_RNC_POSTING:
+ /* Set the destination state to AWAIT - this signals the
+ * entry into the SCI_RNC_READY state that a suspension
+ * needs to be done immediately.
+ */
+ if (sci_rnc->destination_state != RNC_DEST_FINAL)
+ sci_rnc->destination_state = RNC_DEST_SUSPENDED;
+ sci_rnc->suspend_type = suspend_type;
+ sci_rnc->suspend_reason = suspend_reason;
+ return SCI_SUCCESS;
+
+ case SCI_RNC_TX_SUSPENDED:
+ if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX)
+ status = SCI_SUCCESS;
+ break;
+ case SCI_RNC_TX_RX_SUSPENDED:
+ if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX)
+ status = SCI_SUCCESS;
+ break;
+ case SCI_RNC_AWAIT_SUSPENSION:
+ if ((sci_rnc->suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX)
+ || (suspend_type == sci_rnc->suspend_type))
+ return SCI_SUCCESS;
+ break;
+ default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: invalid state %d\n", __func__, state);
+ "%s: invalid state %s\n", __func__,
+ rnc_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
+ sci_rnc->destination_state = dest_param;
+ sci_rnc->suspend_type = suspend_type;
+ sci_rnc->suspend_reason = suspend_reason;
+
+ if (status == SCI_SUCCESS) { /* Already in the destination state? */
+ struct isci_host *ihost = idev->owning_port->owning_controller;
+
+ wake_up_all(&ihost->eventq); /* Let observers look. */
+ return SCI_SUCCESS;
+ }
+ if ((suspend_reason == SCI_SW_SUSPEND_NORMAL) ||
+ (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)) {
- sci_rnc->user_callback = cb_fn;
- sci_rnc->user_cookie = cb_p;
- sci_rnc->suspension_code = suspend_type;
+ if (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)
+ isci_dev_set_hang_detection_timeout(idev, 0x00000001);
- if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
- sci_remote_device_post_request(rnc_to_dev(sci_rnc),
- SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
+ sci_remote_device_post_request(
+ idev, SCI_SOFTWARE_SUSPEND_CMD);
}
+ if (state != SCI_RNC_AWAIT_SUSPENSION)
+ sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION);
- sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION);
return SCI_SUCCESS;
}
@@ -525,56 +657,86 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s
void *cb_p)
{
enum scis_sds_remote_node_context_states state;
+ struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
state = sci_rnc->sm.current_state_id;
+ dev_dbg(scirdev_to_dev(idev),
+ "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d; "
+ "dev resume path %s\n",
+ __func__, rnc_state_name(state), cb_fn, cb_p,
+ sci_rnc->destination_state,
+ test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)
+ ? "<abort active>" : "<normal>");
+
switch (state) {
case SCI_RNC_INITIAL:
if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX)
return SCI_FAILURE_INVALID_STATE;
- sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
- sci_remote_node_context_construct_buffer(sci_rnc);
- sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING);
+ sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p,
+ RNC_DEST_READY);
+ if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) {
+ sci_remote_node_context_construct_buffer(sci_rnc);
+ sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING);
+ }
return SCI_SUCCESS;
+
case SCI_RNC_POSTING:
case SCI_RNC_INVALIDATING:
case SCI_RNC_RESUMING:
- if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
- return SCI_FAILURE_INVALID_STATE;
-
- sci_rnc->user_callback = cb_fn;
- sci_rnc->user_cookie = cb_p;
+ /* We are still waiting to post when a resume was
+ * requested.
+ */
+ switch (sci_rnc->destination_state) {
+ case RNC_DEST_SUSPENDED:
+ case RNC_DEST_SUSPENDED_RESUME:
+ /* Previously waiting to suspend after posting.
+ * Now continue onto resumption.
+ */
+ sci_remote_node_context_setup_to_resume(
+ sci_rnc, cb_fn, cb_p,
+ RNC_DEST_SUSPENDED_RESUME);
+ break;
+ default:
+ sci_remote_node_context_setup_to_resume(
+ sci_rnc, cb_fn, cb_p,
+ RNC_DEST_READY);
+ break;
+ }
return SCI_SUCCESS;
- case SCI_RNC_TX_SUSPENDED: {
- struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
- struct domain_device *dev = idev->domain_dev;
-
- sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
-
- /* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */
- if (dev->dev_type == SAS_END_DEV || dev_is_expander(dev))
- sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
- else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
- if (idev->is_direct_attached) {
- /* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */
- sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
- } else {
- sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
+
+ case SCI_RNC_TX_SUSPENDED:
+ case SCI_RNC_TX_RX_SUSPENDED:
+ {
+ struct domain_device *dev = idev->domain_dev;
+ /* If this is an expander attached SATA device we must
+ * invalidate and repost the RNC since this is the only
+ * way to clear the TCi to NCQ tag mapping table for
+ * the RNi. All other device types we can just resume.
+ */
+ sci_remote_node_context_setup_to_resume(
+ sci_rnc, cb_fn, cb_p, RNC_DEST_READY);
+
+ if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) {
+ if ((dev_is_sata(dev) && dev->parent) ||
+ (sci_rnc->destination_state == RNC_DEST_FINAL))
+ sci_change_state(&sci_rnc->sm,
+ SCI_RNC_INVALIDATING);
+ else
+ sci_change_state(&sci_rnc->sm,
+ SCI_RNC_RESUMING);
}
- } else
- return SCI_FAILURE;
+ }
return SCI_SUCCESS;
- }
- case SCI_RNC_TX_RX_SUSPENDED:
- sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
- sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
- return SCI_FAILURE_INVALID_STATE;
+
case SCI_RNC_AWAIT_SUSPENSION:
- sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
+ sci_remote_node_context_setup_to_resume(
+ sci_rnc, cb_fn, cb_p, RNC_DEST_SUSPENDED_RESUME);
return SCI_SUCCESS;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: invalid state %d\n", __func__, state);
+ "%s: invalid state %s\n", __func__,
+ rnc_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -593,35 +755,51 @@ enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context
case SCI_RNC_TX_RX_SUSPENDED:
case SCI_RNC_AWAIT_SUSPENSION:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: invalid state %d\n", __func__, state);
+ "%s: invalid state %s\n", __func__,
+ rnc_state_name(state));
return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED;
default:
- break;
+ dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+ "%s: invalid state %s\n", __func__,
+ rnc_state_name(state));
+ return SCI_FAILURE_INVALID_STATE;
}
- dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
- "%s: requested to start IO while still resuming, %d\n",
- __func__, state);
- return SCI_FAILURE_INVALID_STATE;
}
-enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc,
- struct isci_request *ireq)
+enum sci_status sci_remote_node_context_start_task(
+ struct sci_remote_node_context *sci_rnc,
+ struct isci_request *ireq,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p)
+{
+ enum sci_status status = sci_remote_node_context_resume(sci_rnc,
+ cb_fn, cb_p);
+ if (status != SCI_SUCCESS)
+ dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+ "%s: resume failed: %d\n", __func__, status);
+ return status;
+}
+
+int sci_remote_node_context_is_safe_to_abort(
+ struct sci_remote_node_context *sci_rnc)
{
enum scis_sds_remote_node_context_states state;
state = sci_rnc->sm.current_state_id;
switch (state) {
+ case SCI_RNC_INVALIDATING:
+ case SCI_RNC_TX_RX_SUSPENDED:
+ return 1;
+ case SCI_RNC_POSTING:
case SCI_RNC_RESUMING:
case SCI_RNC_READY:
- case SCI_RNC_AWAIT_SUSPENSION:
- return SCI_SUCCESS;
case SCI_RNC_TX_SUSPENDED:
- case SCI_RNC_TX_RX_SUSPENDED:
- sci_remote_node_context_resume(sci_rnc, NULL, NULL);
- return SCI_SUCCESS;
+ case SCI_RNC_AWAIT_SUSPENSION:
+ case SCI_RNC_INITIAL:
+ return 0;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %d\n", __func__, state);
- return SCI_FAILURE_INVALID_STATE;
+ return 0;
}
}
diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h
index 41580ad12520..c7ee81d01125 100644
--- a/drivers/scsi/isci/remote_node_context.h
+++ b/drivers/scsi/isci/remote_node_context.h
@@ -75,8 +75,13 @@
*/
#define SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX 0x0FFF
-#define SCU_HARDWARE_SUSPENSION (0)
-#define SCI_SOFTWARE_SUSPENSION (1)
+enum sci_remote_node_suspension_reasons {
+ SCI_HW_SUSPEND,
+ SCI_SW_SUSPEND_NORMAL,
+ SCI_SW_SUSPEND_LINKHANG_DETECT
+};
+#define SCI_SOFTWARE_SUSPEND_CMD SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX
+#define SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT SCU_EVENT_TL_RNC_SUSPEND_TX_RX
struct isci_request;
struct isci_remote_device;
@@ -85,61 +90,50 @@ struct sci_remote_node_context;
typedef void (*scics_sds_remote_node_context_callback)(void *);
/**
- * This is the enumeration of the remote node context states.
+ * enum sci_remote_node_context_states
+ * @SCI_RNC_INITIAL initial state for a remote node context. On a resume
+ * request the remote node context will transition to the posting state.
+ *
+ * @SCI_RNC_POSTING: transition state that posts the RNi to the hardware. Once
+ * the RNC is posted the remote node context will be made ready.
+ *
+ * @SCI_RNC_INVALIDATING: transition state that will post an RNC invalidate to
+ * the hardware. Once the invalidate is complete the remote node context will
+ * transition to the posting state.
+ *
+ * @SCI_RNC_RESUMING: transition state that will post an RNC resume to the
+ * hardare. Once the event notification of resume complete is received the
+ * remote node context will transition to the ready state.
+ *
+ * @SCI_RNC_READY: state that the remote node context must be in to accept io
+ * request operations.
+ *
+ * @SCI_RNC_TX_SUSPENDED: state that the remote node context transitions to when
+ * it gets a TX suspend notification from the hardware.
+ *
+ * @SCI_RNC_TX_RX_SUSPENDED: state that the remote node context transitions to
+ * when it gets a TX RX suspend notification from the hardware.
+ *
+ * @SCI_RNC_AWAIT_SUSPENSION: wait state for the remote node context that waits
+ * for a suspend notification from the hardware. This state is entered when
+ * either there is a request to supend the remote node context or when there is
+ * a TC completion where the remote node will be suspended by the hardware.
*/
-enum scis_sds_remote_node_context_states {
- /**
- * This state is the initial state for a remote node context. On a resume
- * request the remote node context will transition to the posting state.
- */
- SCI_RNC_INITIAL,
-
- /**
- * This is a transition state that posts the RNi to the hardware. Once the RNC
- * is posted the remote node context will be made ready.
- */
- SCI_RNC_POSTING,
-
- /**
- * This is a transition state that will post an RNC invalidate to the
- * hardware. Once the invalidate is complete the remote node context will
- * transition to the posting state.
- */
- SCI_RNC_INVALIDATING,
-
- /**
- * This is a transition state that will post an RNC resume to the hardare.
- * Once the event notification of resume complete is received the remote node
- * context will transition to the ready state.
- */
- SCI_RNC_RESUMING,
-
- /**
- * This is the state that the remote node context must be in to accept io
- * request operations.
- */
- SCI_RNC_READY,
-
- /**
- * This is the state that the remote node context transitions to when it gets
- * a TX suspend notification from the hardware.
- */
- SCI_RNC_TX_SUSPENDED,
-
- /**
- * This is the state that the remote node context transitions to when it gets
- * a TX RX suspend notification from the hardware.
- */
- SCI_RNC_TX_RX_SUSPENDED,
-
- /**
- * This state is a wait state for the remote node context that waits for a
- * suspend notification from the hardware. This state is entered when either
- * there is a request to supend the remote node context or when there is a TC
- * completion where the remote node will be suspended by the hardware.
- */
- SCI_RNC_AWAIT_SUSPENSION
-};
+#define RNC_STATES {\
+ C(RNC_INITIAL),\
+ C(RNC_POSTING),\
+ C(RNC_INVALIDATING),\
+ C(RNC_RESUMING),\
+ C(RNC_READY),\
+ C(RNC_TX_SUSPENDED),\
+ C(RNC_TX_RX_SUSPENDED),\
+ C(RNC_AWAIT_SUSPENSION),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum scis_sds_remote_node_context_states RNC_STATES;
+#undef C
+const char *rnc_state_name(enum scis_sds_remote_node_context_states state);
/**
*
@@ -148,9 +142,13 @@ enum scis_sds_remote_node_context_states {
* node context.
*/
enum sci_remote_node_context_destination_state {
- SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED,
- SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY,
- SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL
+ RNC_DEST_UNSPECIFIED,
+ RNC_DEST_READY,
+ RNC_DEST_FINAL,
+ RNC_DEST_SUSPENDED, /* Set when suspend during post/invalidate */
+ RNC_DEST_SUSPENDED_RESUME /* Set when a resume was done during posting
+ * or invalidating and already suspending.
+ */
};
/**
@@ -167,10 +165,12 @@ struct sci_remote_node_context {
u16 remote_node_index;
/**
- * This field is the recored suspension code or the reason for the remote node
+ * This field is the recored suspension type of the remote node
* context suspension.
*/
- u32 suspension_code;
+ u32 suspend_type;
+ enum sci_remote_node_suspension_reasons suspend_reason;
+ u32 suspend_count;
/**
* This field is true if the remote node context is resuming from its current
@@ -204,21 +204,33 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
bool sci_remote_node_context_is_ready(
struct sci_remote_node_context *sci_rnc);
+bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc);
+
enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc,
u32 event_code);
enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context *sci_rnc,
scics_sds_remote_node_context_callback callback,
void *callback_parameter);
enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc,
- u32 suspend_type,
- scics_sds_remote_node_context_callback cb_fn,
- void *cb_p);
+ enum sci_remote_node_suspension_reasons reason,
+ u32 suspension_code);
enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *sci_rnc,
scics_sds_remote_node_context_callback cb_fn,
void *cb_p);
enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc,
- struct isci_request *ireq);
+ struct isci_request *ireq,
+ scics_sds_remote_node_context_callback cb_fn,
+ void *cb_p);
enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc,
struct isci_request *ireq);
+int sci_remote_node_context_is_safe_to_abort(
+ struct sci_remote_node_context *sci_rnc);
+static inline bool sci_remote_node_context_is_being_destroyed(
+ struct sci_remote_node_context *sci_rnc)
+{
+ return (sci_rnc->destination_state == RNC_DEST_FINAL)
+ || ((sci_rnc->sm.current_state_id == SCI_RNC_INITIAL)
+ && (sci_rnc->destination_state == RNC_DEST_UNSPECIFIED));
+}
#endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */
diff --git a/drivers/scsi/isci/remote_node_table.c b/drivers/scsi/isci/remote_node_table.c
index 301b3141945e..1bcaf528c1c9 100644
--- a/drivers/scsi/isci/remote_node_table.c
+++ b/drivers/scsi/isci/remote_node_table.c
@@ -53,17 +53,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/**
+/*
* This file contains the implementation of the SCIC_SDS_REMOTE_NODE_TABLE
* public, protected, and private methods.
- *
- *
*/
#include "remote_node_table.h"
#include "remote_node_context.h"
/**
- *
+ * sci_remote_node_table_get_group_index()
* @remote_node_table: This is the remote node index table from which the
* selection will be made.
* @group_table_index: This is the index to the group table from which to
@@ -98,10 +96,10 @@ static u32 sci_remote_node_table_get_group_index(
}
/**
- *
- * @out]: remote_node_table This the remote node table in which to clear the
+ * sci_remote_node_table_clear_group_index()
+ * @remote_node_table: This the remote node table in which to clear the
* selector.
- * @set_index: This is the remote node selector in which the change will be
+ * @group_table_index: This is the remote node selector in which the change will be
* made.
* @group_index: This is the bit index in the table to be modified.
*
@@ -128,8 +126,8 @@ static void sci_remote_node_table_clear_group_index(
}
/**
- *
- * @out]: remote_node_table This the remote node table in which to set the
+ * sci_remote_node_table_set_group_index()
+ * @remote_node_table: This the remote node table in which to set the
* selector.
* @group_table_index: This is the remote node selector in which the change
* will be made.
@@ -158,8 +156,8 @@ static void sci_remote_node_table_set_group_index(
}
/**
- *
- * @out]: remote_node_table This is the remote node table in which to modify
+ * sci_remote_node_table_set_node_index()
+ * @remote_node_table: This is the remote node table in which to modify
* the remote node availability.
* @remote_node_index: This is the remote node index that is being returned to
* the table.
@@ -191,8 +189,8 @@ static void sci_remote_node_table_set_node_index(
}
/**
- *
- * @out]: remote_node_table This is the remote node table from which to clear
+ * sci_remote_node_table_clear_node_index()
+ * @remote_node_table: This is the remote node table from which to clear
* the available remote node bit.
* @remote_node_index: This is the remote node index which is to be cleared
* from the table.
@@ -224,8 +222,8 @@ static void sci_remote_node_table_clear_node_index(
}
/**
- *
- * @out]: remote_node_table The remote node table from which the slot will be
+ * sci_remote_node_table_clear_group()
+ * @remote_node_table: The remote node table from which the slot will be
* cleared.
* @group_index: The index for the slot that is to be cleared.
*
@@ -252,9 +250,8 @@ static void sci_remote_node_table_clear_group(
remote_node_table->available_remote_nodes[dword_location] = dword_value;
}
-/**
- *
- * @remote_node_table:
+/*
+ * sci_remote_node_table_set_group()
*
* THis method sets an entire remote node group in the remote node table.
*/
@@ -280,7 +277,7 @@ static void sci_remote_node_table_set_group(
}
/**
- *
+ * sci_remote_node_table_get_group_value()
* @remote_node_table: This is the remote node table that for which the group
* value is to be returned.
* @group_index: This is the group index to use to find the group value.
@@ -307,8 +304,8 @@ static u8 sci_remote_node_table_get_group_value(
}
/**
- *
- * @out]: remote_node_table The remote that which is to be initialized.
+ * sci_remote_node_table_initialize()
+ * @remote_node_table: The remote that which is to be initialized.
* @remote_node_entries: The number of entries to put in the table.
*
* This method will initialize the remote node table for use. none
@@ -365,10 +362,10 @@ void sci_remote_node_table_initialize(
}
/**
- *
- * @out]: remote_node_table The remote node table from which to allocate a
+ * sci_remote_node_table_allocate_single_remote_node()
+ * @remote_node_table: The remote node table from which to allocate a
* remote node.
- * @table_index: The group index that is to be used for the search.
+ * @group_table_index: The group index that is to be used for the search.
*
* This method will allocate a single RNi from the remote node table. The
* table index will determine from which remote node group table to search.
@@ -425,10 +422,10 @@ static u16 sci_remote_node_table_allocate_single_remote_node(
}
/**
- *
+ * sci_remote_node_table_allocate_triple_remote_node()
* @remote_node_table: This is the remote node table from which to allocate the
* remote node entries.
- * @group_table_index: THis is the group table index which must equal two (2)
+ * @group_table_index: This is the group table index which must equal two (2)
* for this operation.
*
* This method will allocate three consecutive remote node context entries. If
@@ -462,7 +459,7 @@ static u16 sci_remote_node_table_allocate_triple_remote_node(
}
/**
- *
+ * sci_remote_node_table_allocate_remote_node()
* @remote_node_table: This is the remote node table from which the remote node
* allocation is to take place.
* @remote_node_count: This is ther remote node count which is one of
@@ -505,9 +502,10 @@ u16 sci_remote_node_table_allocate_remote_node(
}
/**
- *
- * @remote_node_table:
- *
+ * sci_remote_node_table_release_single_remote_node()
+ * @remote_node_table: This is the remote node table from which the remote node
+ * release is to take place.
+ * @remote_node_index: This is the remote node index that is being released.
* This method will free a single remote node index back to the remote node
* table. This routine will update the remote node groups
*/
@@ -550,9 +548,10 @@ static void sci_remote_node_table_release_single_remote_node(
}
/**
- *
+ * sci_remote_node_table_release_triple_remote_node()
* @remote_node_table: This is the remote node table to which the remote node
* index is to be freed.
+ * @remote_node_index: This is the remote node index that is being released.
*
* This method will release a group of three consecutive remote nodes back to
* the free remote nodes.
@@ -573,11 +572,12 @@ static void sci_remote_node_table_release_triple_remote_node(
}
/**
- *
+ * sci_remote_node_table_release_remote_node_index()
* @remote_node_table: The remote node table to which the remote node index is
* to be freed.
* @remote_node_count: This is the count of consecutive remote nodes that are
* to be freed.
+ * @remote_node_index: This is the remote node index that is being released.
*
* This method will release the remote node index back into the remote node
* table free pool.
diff --git a/drivers/scsi/isci/remote_node_table.h b/drivers/scsi/isci/remote_node_table.h
index 721ab982d2ac..0ddfdda2b248 100644
--- a/drivers/scsi/isci/remote_node_table.h
+++ b/drivers/scsi/isci/remote_node_table.h
@@ -61,7 +61,7 @@
/**
*
*
- * Remote node sets are sets of remote node index in the remtoe node table The
+ * Remote node sets are sets of remote node index in the remote node table. The
* SCU hardware requires that STP remote node entries take three consecutive
* remote node index so the table is arranged in sets of three. The bits are
* used as 0111 0111 to make a byte and the bits define the set of three remote
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index a46e07ac789f..bb89a2e33eb4 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -53,6 +53,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <scsi/scsi_cmnd.h>
#include "isci.h"
#include "task.h"
#include "request.h"
@@ -60,6 +61,16 @@
#include "scu_event_codes.h"
#include "sas.h"
+#undef C
+#define C(a) (#a)
+const char *req_state_name(enum sci_base_request_states state)
+{
+ static const char * const strings[] = REQUEST_STATES;
+
+ return strings[state];
+}
+#undef C
+
static struct scu_sgl_element_pair *to_sgl_element_pair(struct isci_request *ireq,
int idx)
{
@@ -81,11 +92,11 @@ static dma_addr_t to_sgl_element_pair_dma(struct isci_host *ihost,
if (idx == 0) {
offset = (void *) &ireq->tc->sgl_pair_ab -
(void *) &ihost->task_context_table[0];
- return ihost->task_context_dma + offset;
+ return ihost->tc_dma + offset;
} else if (idx == 1) {
offset = (void *) &ireq->tc->sgl_pair_cd -
(void *) &ihost->task_context_table[0];
- return ihost->task_context_dma + offset;
+ return ihost->tc_dma + offset;
}
return sci_io_request_get_dma_addr(ireq, &ireq->sg_table[idx - 2]);
@@ -169,12 +180,12 @@ static void sci_io_request_build_ssp_command_iu(struct isci_request *ireq)
cmd_iu->_r_a = 0;
cmd_iu->_r_b = 0;
cmd_iu->en_fburst = 0; /* unsupported */
- cmd_iu->task_prio = task->ssp_task.task_prio;
+ cmd_iu->task_prio = 0;
cmd_iu->task_attr = task->ssp_task.task_attr;
cmd_iu->_r_c = 0;
- sci_swab32_cpy(&cmd_iu->cdb, task->ssp_task.cdb,
- sizeof(task->ssp_task.cdb) / sizeof(u32));
+ sci_swab32_cpy(&cmd_iu->cdb, task->ssp_task.cmd->cmnd,
+ (task->ssp_task.cmd->cmd_len+3) / sizeof(u32));
}
static void sci_task_request_build_ssp_task_iu(struct isci_request *ireq)
@@ -191,18 +202,15 @@ static void sci_task_request_build_ssp_task_iu(struct isci_request *ireq)
task_iu->task_func = isci_tmf->tmf_code;
task_iu->task_tag =
- (ireq->ttype == tmf_task) ?
+ (test_bit(IREQ_TMF, &ireq->flags)) ?
isci_tmf->io_tag :
SCI_CONTROLLER_INVALID_IO_TAG;
}
-/**
+/*
* This method is will fill in the SCU Task Context for any type of SSP request.
- * @sci_req:
- * @task_context:
- *
*/
-static void scu_ssp_reqeust_construct_task_context(
+static void scu_ssp_request_construct_task_context(
struct isci_request *ireq,
struct scu_task_context *task_context)
{
@@ -213,7 +221,7 @@ static void scu_ssp_reqeust_construct_task_context(
idev = ireq->target_device;
iport = idev->owning_port;
- /* Fill in the TC with the its required data */
+ /* Fill in the TC with its required data */
task_context->abort = 0;
task_context->priority = 0;
task_context->initiator_request = 1;
@@ -264,18 +272,155 @@ static void scu_ssp_reqeust_construct_task_context(
task_context->response_iu_lower = lower_32_bits(dma_addr);
}
-/**
+static u8 scu_bg_blk_size(struct scsi_device *sdp)
+{
+ switch (sdp->sector_size) {
+ case 512:
+ return 0;
+ case 1024:
+ return 1;
+ case 4096:
+ return 3;
+ default:
+ return 0xff;
+ }
+}
+
+static u32 scu_dif_bytes(u32 len, u32 sector_size)
+{
+ return (len >> ilog2(sector_size)) * 8;
+}
+
+static void scu_ssp_ireq_dif_insert(struct isci_request *ireq, u8 type, u8 op)
+{
+ struct scu_task_context *tc = ireq->tc;
+ struct scsi_cmnd *scmd = ireq->ttype_ptr.io_task_ptr->uldd_task;
+ u8 blk_sz = scu_bg_blk_size(scmd->device);
+
+ tc->block_guard_enable = 1;
+ tc->blk_prot_en = 1;
+ tc->blk_sz = blk_sz;
+ /* DIF write insert */
+ tc->blk_prot_func = 0x2;
+
+ tc->transfer_length_bytes += scu_dif_bytes(tc->transfer_length_bytes,
+ scmd->device->sector_size);
+
+ /* always init to 0, used by hw */
+ tc->interm_crc_val = 0;
+
+ tc->init_crc_seed = 0;
+ tc->app_tag_verify = 0;
+ tc->app_tag_gen = 0;
+ tc->ref_tag_seed_verify = 0;
+
+ /* always init to same as bg_blk_sz */
+ tc->UD_bytes_immed_val = scmd->device->sector_size;
+
+ tc->reserved_DC_0 = 0;
+
+ /* always init to 8 */
+ tc->DIF_bytes_immed_val = 8;
+
+ tc->reserved_DC_1 = 0;
+ tc->bgc_blk_sz = scmd->device->sector_size;
+ tc->reserved_E0_0 = 0;
+ tc->app_tag_gen_mask = 0;
+
+ /** setup block guard control **/
+ tc->bgctl = 0;
+
+ /* DIF write insert */
+ tc->bgctl_f.op = 0x2;
+
+ tc->app_tag_verify_mask = 0;
+
+ /* must init to 0 for hw */
+ tc->blk_guard_err = 0;
+
+ tc->reserved_E8_0 = 0;
+
+ if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2))
+ tc->ref_tag_seed_gen = scsi_prot_ref_tag(scmd);
+ else if (type & SCSI_PROT_DIF_TYPE3)
+ tc->ref_tag_seed_gen = 0;
+}
+
+static void scu_ssp_ireq_dif_strip(struct isci_request *ireq, u8 type, u8 op)
+{
+ struct scu_task_context *tc = ireq->tc;
+ struct scsi_cmnd *scmd = ireq->ttype_ptr.io_task_ptr->uldd_task;
+ u8 blk_sz = scu_bg_blk_size(scmd->device);
+
+ tc->block_guard_enable = 1;
+ tc->blk_prot_en = 1;
+ tc->blk_sz = blk_sz;
+ /* DIF read strip */
+ tc->blk_prot_func = 0x1;
+
+ tc->transfer_length_bytes += scu_dif_bytes(tc->transfer_length_bytes,
+ scmd->device->sector_size);
+
+ /* always init to 0, used by hw */
+ tc->interm_crc_val = 0;
+
+ tc->init_crc_seed = 0;
+ tc->app_tag_verify = 0;
+ tc->app_tag_gen = 0;
+
+ if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2))
+ tc->ref_tag_seed_verify = scsi_prot_ref_tag(scmd);
+ else if (type & SCSI_PROT_DIF_TYPE3)
+ tc->ref_tag_seed_verify = 0;
+
+ /* always init to same as bg_blk_sz */
+ tc->UD_bytes_immed_val = scmd->device->sector_size;
+
+ tc->reserved_DC_0 = 0;
+
+ /* always init to 8 */
+ tc->DIF_bytes_immed_val = 8;
+
+ tc->reserved_DC_1 = 0;
+ tc->bgc_blk_sz = scmd->device->sector_size;
+ tc->reserved_E0_0 = 0;
+ tc->app_tag_gen_mask = 0;
+
+ /** setup block guard control **/
+ tc->bgctl = 0;
+
+ /* DIF read strip */
+ tc->bgctl_f.crc_verify = 1;
+ tc->bgctl_f.op = 0x1;
+ if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2)) {
+ tc->bgctl_f.ref_tag_chk = 1;
+ tc->bgctl_f.app_f_detect = 1;
+ } else if (type & SCSI_PROT_DIF_TYPE3)
+ tc->bgctl_f.app_ref_f_detect = 1;
+
+ tc->app_tag_verify_mask = 0;
+
+ /* must init to 0 for hw */
+ tc->blk_guard_err = 0;
+
+ tc->reserved_E8_0 = 0;
+ tc->ref_tag_seed_gen = 0;
+}
+
+/*
* This method is will fill in the SCU Task Context for a SSP IO request.
- * @sci_req:
- *
*/
static void scu_ssp_io_request_construct_task_context(struct isci_request *ireq,
enum dma_data_direction dir,
u32 len)
{
struct scu_task_context *task_context = ireq->tc;
+ struct sas_task *sas_task = ireq->ttype_ptr.io_task_ptr;
+ struct scsi_cmnd *scmd = sas_task->uldd_task;
+ u8 prot_type = scsi_get_prot_type(scmd);
+ u8 prot_op = scsi_get_prot_op(scmd);
- scu_ssp_reqeust_construct_task_context(ireq, task_context);
+ scu_ssp_request_construct_task_context(ireq, task_context);
task_context->ssp_command_iu_length =
sizeof(struct ssp_cmd_iu) / sizeof(u32);
@@ -296,26 +441,32 @@ static void scu_ssp_io_request_construct_task_context(struct isci_request *ireq,
if (task_context->transfer_length_bytes > 0)
sci_request_build_sgl(ireq);
+
+ if (prot_type != SCSI_PROT_DIF_TYPE0) {
+ if (prot_op == SCSI_PROT_READ_STRIP)
+ scu_ssp_ireq_dif_strip(ireq, prot_type, prot_op);
+ else if (prot_op == SCSI_PROT_WRITE_INSERT)
+ scu_ssp_ireq_dif_insert(ireq, prot_type, prot_op);
+ }
}
/**
- * This method will fill in the SCU Task Context for a SSP Task request. The
- * following important settings are utilized: -# priority ==
- * SCU_TASK_PRIORITY_HIGH. This ensures that the task request is issued
- * ahead of other task destined for the same Remote Node. -# task_type ==
- * SCU_TASK_TYPE_IOREAD. This simply indicates that a normal request type
- * (i.e. non-raw frame) is being utilized to perform task management. -#
- * control_frame == 1. This ensures that the proper endianess is set so
- * that the bytes are transmitted in the right order for a task frame.
- * @sci_req: This parameter specifies the task request object being
- * constructed.
- *
+ * scu_ssp_task_request_construct_task_context() - This method will fill in
+ * the SCU Task Context for a SSP Task request. The following important
+ * settings are utilized: -# priority == SCU_TASK_PRIORITY_HIGH. This
+ * ensures that the task request is issued ahead of other task destined
+ * for the same Remote Node. -# task_type == SCU_TASK_TYPE_IOREAD. This
+ * simply indicates that a normal request type (i.e. non-raw frame) is
+ * being utilized to perform task management. -#control_frame == 1. This
+ * ensures that the proper endianness is set so that the bytes are
+ * transmitted in the right order for a task frame.
+ * @ireq: This parameter specifies the task request object being constructed.
*/
static void scu_ssp_task_request_construct_task_context(struct isci_request *ireq)
{
struct scu_task_context *task_context = ireq->tc;
- scu_ssp_reqeust_construct_task_context(ireq, task_context);
+ scu_ssp_request_construct_task_context(ireq, task_context);
task_context->control_frame = 1;
task_context->priority = SCU_TASK_PRIORITY_HIGH;
@@ -327,9 +478,10 @@ static void scu_ssp_task_request_construct_task_context(struct isci_request *ire
}
/**
+ * scu_sata_request_construct_task_context()
* This method is will fill in the SCU Task Context for any type of SATA
* request. This is called from the various SATA constructors.
- * @sci_req: The general IO request object which is to be used in
+ * @ireq: The general IO request object which is to be used in
* constructing the SCU task context.
* @task_context: The buffer pointer for the SCU task context which is being
* constructed.
@@ -338,7 +490,7 @@ static void scu_ssp_task_request_construct_task_context(struct isci_request *ire
* the command buffer is complete. none Revisit task context construction to
* determine what is common for SSP/SMP/STP task context structures.
*/
-static void scu_sata_reqeust_construct_task_context(
+static void scu_sata_request_construct_task_context(
struct isci_request *ireq,
struct scu_task_context *task_context)
{
@@ -349,7 +501,7 @@ static void scu_sata_reqeust_construct_task_context(
idev = ireq->target_device;
iport = idev->owning_port;
- /* Fill in the TC with the its required data */
+ /* Fill in the TC with its required data */
task_context->abort = 0;
task_context->priority = SCU_TASK_PRIORITY_NORMAL;
task_context->initiator_request = 1;
@@ -405,7 +557,7 @@ static void scu_stp_raw_request_construct_task_context(struct isci_request *ireq
{
struct scu_task_context *task_context = ireq->tc;
- scu_sata_reqeust_construct_task_context(ireq, task_context);
+ scu_sata_request_construct_task_context(ireq, task_context);
task_context->control_frame = 0;
task_context->priority = SCU_TASK_PRIORITY_NORMAL;
@@ -436,9 +588,9 @@ static enum sci_status sci_stp_pio_request_construct(struct isci_request *ireq,
return SCI_SUCCESS;
}
-/**
- *
- * @sci_req: This parameter specifies the request to be constructed as an
+/*
+ * sci_stp_optimized_request_construct()
+ * @ireq: This parameter specifies the request to be constructed as an
* optimized request.
* @optimized_task_type: This parameter specifies whether the request is to be
* an UDMA request or a NCQ request. - A value of 0 indicates UDMA. - A
@@ -456,7 +608,7 @@ static void sci_stp_optimized_request_construct(struct isci_request *ireq,
struct scu_task_context *task_context = ireq->tc;
/* Build the STP task context structure */
- scu_sata_reqeust_construct_task_context(ireq, task_context);
+ scu_sata_request_construct_task_context(ireq, task_context);
/* Copy over the SGL elements */
sci_request_build_sgl(ireq);
@@ -481,7 +633,29 @@ static void sci_stp_optimized_request_construct(struct isci_request *ireq,
}
}
+static void sci_atapi_construct(struct isci_request *ireq)
+{
+ struct host_to_dev_fis *h2d_fis = &ireq->stp.cmd;
+ struct sas_task *task;
+
+ /* To simplify the implementation we take advantage of the
+ * silicon's partial acceleration of atapi protocol (dma data
+ * transfers), so we promote all commands to dma protocol. This
+ * breaks compatibility with ATA_HORKAGE_ATAPI_MOD16_DMA drives.
+ */
+ h2d_fis->features |= ATAPI_PKT_DMA;
+
+ scu_stp_raw_request_construct_task_context(ireq);
+
+ task = isci_request_access_task(ireq);
+ if (task->data_dir == DMA_NONE)
+ task->total_xfer_len = 0;
+ /* clear the response so we can detect arrivial of an
+ * unsolicited h2d fis
+ */
+ ireq->stp.rsp.fis_type = 0;
+}
static enum sci_status
sci_io_request_construct_sata(struct isci_request *ireq,
@@ -491,23 +665,18 @@ sci_io_request_construct_sata(struct isci_request *ireq,
{
enum sci_status status = SCI_SUCCESS;
struct sas_task *task = isci_request_access_task(ireq);
+ struct domain_device *dev = ireq->target_device->domain_dev;
/* check for management protocols */
- if (ireq->ttype == tmf_task) {
+ if (test_bit(IREQ_TMF, &ireq->flags)) {
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
- if (tmf->tmf_code == isci_tmf_sata_srst_high ||
- tmf->tmf_code == isci_tmf_sata_srst_low) {
- scu_stp_raw_request_construct_task_context(ireq);
- return SCI_SUCCESS;
- } else {
- dev_err(&ireq->owning_controller->pdev->dev,
- "%s: Request 0x%p received un-handled SAT "
- "management protocol 0x%x.\n",
- __func__, ireq, tmf->tmf_code);
+ dev_err(&ireq->owning_controller->pdev->dev,
+ "%s: Request 0x%p received un-handled SAT "
+ "management protocol 0x%x.\n",
+ __func__, ireq, tmf->tmf_code);
- return SCI_FAILURE;
- }
+ return SCI_FAILURE;
}
if (!sas_protocol_ata(task->task_proto)) {
@@ -519,6 +688,13 @@ sci_io_request_construct_sata(struct isci_request *ireq,
}
+ /* ATAPI */
+ if (dev->sata_dev.class == ATA_DEV_ATAPI &&
+ task->ata_task.fis.command == ATA_CMD_PACKET) {
+ sci_atapi_construct(ireq);
+ return SCI_SUCCESS;
+ }
+
/* non data */
if (task->data_dir == DMA_NONE) {
scu_stp_raw_request_construct_task_context(ireq);
@@ -549,7 +725,7 @@ static enum sci_status sci_io_request_construct_basic_ssp(struct isci_request *i
{
struct sas_task *task = isci_request_access_task(ireq);
- ireq->protocol = SCIC_SSP_PROTOCOL;
+ ireq->protocol = SAS_PROTOCOL_SSP;
scu_ssp_io_request_construct_task_context(ireq,
task->data_dir,
@@ -562,8 +738,7 @@ static enum sci_status sci_io_request_construct_basic_ssp(struct isci_request *i
return SCI_SUCCESS;
}
-enum sci_status sci_task_request_construct_ssp(
- struct isci_request *ireq)
+void sci_task_request_construct_ssp(struct isci_request *ireq)
{
/* Construct the SSP Task SCU Task Context */
scu_ssp_task_request_construct_task_context(ireq);
@@ -572,8 +747,6 @@ enum sci_status sci_task_request_construct_ssp(
sci_task_request_build_ssp_task_iu(ireq);
sci_change_state(&ireq->sm, SCI_REQ_CONSTRUCTED);
-
- return SCI_SUCCESS;
}
static enum sci_status sci_io_request_construct_basic_sata(struct isci_request *ireq)
@@ -582,7 +755,7 @@ static enum sci_status sci_io_request_construct_basic_sata(struct isci_request *
bool copy = false;
struct sas_task *task = isci_request_access_task(ireq);
- ireq->protocol = SCIC_STP_PROTOCOL;
+ ireq->protocol = SAS_PROTOCOL_STP;
copy = (task->data_dir == DMA_NONE) ? false : true;
@@ -597,39 +770,11 @@ static enum sci_status sci_io_request_construct_basic_sata(struct isci_request *
return status;
}
-enum sci_status sci_task_request_construct_sata(struct isci_request *ireq)
-{
- enum sci_status status = SCI_SUCCESS;
-
- /* check for management protocols */
- if (ireq->ttype == tmf_task) {
- struct isci_tmf *tmf = isci_request_access_tmf(ireq);
-
- if (tmf->tmf_code == isci_tmf_sata_srst_high ||
- tmf->tmf_code == isci_tmf_sata_srst_low) {
- scu_stp_raw_request_construct_task_context(ireq);
- } else {
- dev_err(&ireq->owning_controller->pdev->dev,
- "%s: Request 0x%p received un-handled SAT "
- "Protocol 0x%x.\n",
- __func__, ireq, tmf->tmf_code);
-
- return SCI_FAILURE;
- }
- }
-
- if (status != SCI_SUCCESS)
- return status;
- sci_change_state(&ireq->sm, SCI_REQ_CONSTRUCTED);
-
- return status;
-}
-
+#define SCU_TASK_CONTEXT_SRAM 0x200000
/**
* sci_req_tx_bytes - bytes transferred when reply underruns request
- * @sci_req: request that was terminated early
+ * @ireq: request that was terminated early
*/
-#define SCU_TASK_CONTEXT_SRAM 0x200000
static u32 sci_req_tx_bytes(struct isci_request *ireq)
{
struct isci_host *ihost = ireq->owning_controller;
@@ -710,6 +855,8 @@ sci_io_request_terminate(struct isci_request *ireq)
switch (state) {
case SCI_REQ_CONSTRUCTED:
+ /* Set to make sure no HW terminate posting is done: */
+ set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags);
ireq->scu_status = SCU_TASK_DONE_TASK_ABORT;
ireq->sci_status = SCI_FAILURE_IO_TERMINATED;
sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
@@ -726,25 +873,34 @@ sci_io_request_terminate(struct isci_request *ireq)
case SCI_REQ_STP_PIO_WAIT_FRAME:
case SCI_REQ_STP_PIO_DATA_IN:
case SCI_REQ_STP_PIO_DATA_OUT:
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED:
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG:
- case SCI_REQ_STP_SOFT_RESET_WAIT_D2H:
- sci_change_state(&ireq->sm, SCI_REQ_ABORTING);
- return SCI_SUCCESS;
+ case SCI_REQ_ATAPI_WAIT_H2D:
+ case SCI_REQ_ATAPI_WAIT_PIO_SETUP:
+ case SCI_REQ_ATAPI_WAIT_D2H:
+ case SCI_REQ_ATAPI_WAIT_TC_COMP:
+ /* Fall through and change state to ABORTING... */
case SCI_REQ_TASK_WAIT_TC_RESP:
+ /* The task frame was already confirmed to have been
+ * sent by the SCU HW. Since the state machine is
+ * now only waiting for the task response itself,
+ * abort the request and complete it immediately
+ * and don't wait for the task response.
+ */
sci_change_state(&ireq->sm, SCI_REQ_ABORTING);
- sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
- return SCI_SUCCESS;
+ fallthrough; /* and handle like ABORTING */
case SCI_REQ_ABORTING:
- sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
+ if (!isci_remote_device_is_safe_to_abort(ireq->target_device))
+ set_bit(IREQ_PENDING_ABORT, &ireq->flags);
+ else
+ clear_bit(IREQ_PENDING_ABORT, &ireq->flags);
+ /* If the request is only waiting on the remote device
+ * suspension, return SUCCESS so the caller will wait too.
+ */
return SCI_SUCCESS;
case SCI_REQ_COMPLETED:
default:
dev_warn(&ireq->owning_controller->pdev->dev,
"%s: SCIC IO Request requested to abort while in wrong "
- "state %d\n",
- __func__,
- ireq->sm.current_state_id);
+ "state %d\n", __func__, ireq->sm.current_state_id);
break;
}
@@ -758,7 +914,8 @@ enum sci_status sci_request_complete(struct isci_request *ireq)
state = ireq->sm.current_state_id;
if (WARN_ONCE(state != SCI_REQ_COMPLETED,
- "isci: request completion from wrong state (%d)\n", state))
+ "isci: request completion from wrong state (%s)\n",
+ req_state_name(state)))
return SCI_FAILURE_INVALID_STATE;
if (ireq->saved_rx_frame_index != SCU_INVALID_FRAME_INDEX)
@@ -779,8 +936,8 @@ enum sci_status sci_io_request_event_handler(struct isci_request *ireq,
state = ireq->sm.current_state_id;
if (state != SCI_REQ_STP_PIO_DATA_IN) {
- dev_warn(&ihost->pdev->dev, "%s: (%x) in wrong state %d\n",
- __func__, event_code, state);
+ dev_warn(&ihost->pdev->dev, "%s: (%x) in wrong state %s\n",
+ __func__, event_code, req_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -887,7 +1044,8 @@ request_started_state_tc_event(struct isci_request *ireq,
resp_iu = &ireq->ssp.rsp;
datapres = resp_iu->datapres;
- if (datapres == 1 || datapres == 2) {
+ if (datapres == SAS_DATAPRES_RESPONSE_DATA ||
+ datapres == SAS_DATAPRES_SENSE_DATA) {
ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID;
} else {
@@ -907,7 +1065,7 @@ request_started_state_tc_event(struct isci_request *ireq,
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_SDBFIS):
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_REG_ERR):
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SDB_ERR):
- if (ireq->protocol == SCIC_STP_PROTOCOL) {
+ if (ireq->protocol == SAS_PROTOCOL_STP) {
ireq->scu_status = SCU_GET_COMPLETION_TL_STATUS(completion_code) >>
SCU_COMPLETION_TL_STATUS_SHIFT;
ireq->sci_status = SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED;
@@ -1186,8 +1344,8 @@ static enum sci_status sci_stp_request_pio_data_out_transmit_data(struct isci_re
{
struct isci_stp_request *stp_req = &ireq->stp.req;
struct scu_sgl_element_pair *sgl_pair;
+ enum sci_status status = SCI_SUCCESS;
struct scu_sgl_element *sgl;
- enum sci_status status;
u32 offset;
u32 len = 0;
@@ -1231,17 +1389,17 @@ static enum sci_status sci_stp_request_pio_data_out_transmit_data(struct isci_re
}
/**
+ * sci_stp_request_pio_data_in_copy_data_buffer()
+ * @stp_req: The request that is used for the SGL processing.
+ * @data_buf: The buffer of data to be copied.
+ * @len: The length of the data transfer.
*
- * @stp_request: The request that is used for the SGL processing.
- * @data_buffer: The buffer of data to be copied.
- * @length: The length of the data transfer.
- *
- * Copy the data from the buffer for the length specified to the IO reqeust SGL
+ * Copy the data from the buffer for the length specified to the IO request SGL
* specified data region. enum sci_status
*/
static enum sci_status
sci_stp_request_pio_data_in_copy_data_buffer(struct isci_stp_request *stp_req,
- u8 *data_buf, u32 len)
+ u8 *data_buf, u32 len)
{
struct isci_request *ireq;
u8 *src_addr;
@@ -1262,9 +1420,9 @@ sci_stp_request_pio_data_in_copy_data_buffer(struct isci_stp_request *stp_req,
struct page *page = sg_page(sg);
copy_len = min_t(int, total_len, sg_dma_len(sg));
- kaddr = kmap_atomic(page, KM_IRQ0);
+ kaddr = kmap_atomic(page);
memcpy(kaddr + sg->offset, src_addr, copy_len);
- kunmap_atomic(kaddr, KM_IRQ0);
+ kunmap_atomic(kaddr);
total_len -= copy_len;
src_addr += copy_len;
sg = sg_next(sg);
@@ -1278,8 +1436,8 @@ sci_stp_request_pio_data_in_copy_data_buffer(struct isci_stp_request *stp_req,
}
/**
- *
- * @sci_req: The PIO DATA IN request that is to receive the data.
+ * sci_stp_request_pio_data_in_copy_data()
+ * @stp_req: The PIO DATA IN request that is to receive the data.
* @data_buffer: The buffer to copy from.
*
* Copy the data buffer to the io request data region. enum sci_status
@@ -1315,8 +1473,6 @@ static enum sci_status
stp_request_pio_await_h2d_completion_tc_event(struct isci_request *ireq,
u32 completion_code)
{
- enum sci_status status = SCI_SUCCESS;
-
switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) {
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD):
ireq->scu_status = SCU_TASK_DONE_GOOD;
@@ -1335,7 +1491,7 @@ stp_request_pio_await_h2d_completion_tc_event(struct isci_request *ireq,
break;
}
- return status;
+ return SCI_SUCCESS;
}
static enum sci_status
@@ -1415,6 +1571,128 @@ static enum sci_status sci_stp_request_udma_general_frame_handler(struct isci_re
return status;
}
+static enum sci_status process_unsolicited_fis(struct isci_request *ireq,
+ u32 frame_index)
+{
+ struct isci_host *ihost = ireq->owning_controller;
+ enum sci_status status;
+ struct dev_to_host_fis *frame_header;
+ u32 *frame_buffer;
+
+ status = sci_unsolicited_frame_control_get_header(&ihost->uf_control,
+ frame_index,
+ (void **)&frame_header);
+
+ if (status != SCI_SUCCESS)
+ return status;
+
+ if (frame_header->fis_type != FIS_REGD2H) {
+ dev_err(&ireq->isci_host->pdev->dev,
+ "%s ERROR: invalid fis type 0x%X\n",
+ __func__, frame_header->fis_type);
+ return SCI_FAILURE;
+ }
+
+ sci_unsolicited_frame_control_get_buffer(&ihost->uf_control,
+ frame_index,
+ (void **)&frame_buffer);
+
+ sci_controller_copy_sata_response(&ireq->stp.rsp,
+ (u32 *)frame_header,
+ frame_buffer);
+
+ /* Frame has been decoded return it to the controller */
+ sci_controller_release_frame(ihost, frame_index);
+
+ return status;
+}
+
+static enum sci_status atapi_d2h_reg_frame_handler(struct isci_request *ireq,
+ u32 frame_index)
+{
+ struct sas_task *task = isci_request_access_task(ireq);
+ enum sci_status status;
+
+ status = process_unsolicited_fis(ireq, frame_index);
+
+ if (status == SCI_SUCCESS) {
+ if (ireq->stp.rsp.status & ATA_ERR)
+ status = SCI_FAILURE_IO_RESPONSE_VALID;
+ } else {
+ status = SCI_FAILURE_IO_RESPONSE_VALID;
+ }
+
+ if (status != SCI_SUCCESS) {
+ ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
+ ireq->sci_status = status;
+ } else {
+ ireq->scu_status = SCU_TASK_DONE_GOOD;
+ ireq->sci_status = SCI_SUCCESS;
+ }
+
+ /* the d2h ufi is the end of non-data commands */
+ if (task->data_dir == DMA_NONE)
+ sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
+
+ return status;
+}
+
+static void scu_atapi_reconstruct_raw_frame_task_context(struct isci_request *ireq)
+{
+ struct ata_device *dev = sas_to_ata_dev(ireq->target_device->domain_dev);
+ void *atapi_cdb = ireq->ttype_ptr.io_task_ptr->ata_task.atapi_packet;
+ struct scu_task_context *task_context = ireq->tc;
+
+ /* fill in the SCU Task Context for a DATA fis containing CDB in Raw Frame
+ * type. The TC for previous Packet fis was already there, we only need to
+ * change the H2D fis content.
+ */
+ memset(&ireq->stp.cmd, 0, sizeof(struct host_to_dev_fis));
+ memcpy(((u8 *)&ireq->stp.cmd + sizeof(u32)), atapi_cdb, ATAPI_CDB_LEN);
+ memset(&(task_context->type.stp), 0, sizeof(struct stp_task_context));
+ task_context->type.stp.fis_type = FIS_DATA;
+ task_context->transfer_length_bytes = dev->cdb_len;
+}
+
+static void scu_atapi_construct_task_context(struct isci_request *ireq)
+{
+ struct ata_device *dev = sas_to_ata_dev(ireq->target_device->domain_dev);
+ struct sas_task *task = isci_request_access_task(ireq);
+ struct scu_task_context *task_context = ireq->tc;
+ int cdb_len = dev->cdb_len;
+
+ /* reference: SSTL 1.13.4.2
+ * task_type, sata_direction
+ */
+ if (task->data_dir == DMA_TO_DEVICE) {
+ task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_OUT;
+ task_context->sata_direction = 0;
+ } else {
+ /* todo: for NO_DATA command, we need to send out raw frame. */
+ task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_IN;
+ task_context->sata_direction = 1;
+ }
+
+ memset(&task_context->type.stp, 0, sizeof(task_context->type.stp));
+ task_context->type.stp.fis_type = FIS_DATA;
+
+ memset(&ireq->stp.cmd, 0, sizeof(ireq->stp.cmd));
+ memcpy(&ireq->stp.cmd.lbal, task->ata_task.atapi_packet, cdb_len);
+ task_context->ssp_command_iu_length = cdb_len / sizeof(u32);
+
+ /* task phase is set to TX_CMD */
+ task_context->task_phase = 0x1;
+
+ /* retry counter */
+ task_context->stp_retry_count = 0;
+
+ /* data transfer size. */
+ task_context->transfer_length_bytes = task->total_xfer_len;
+
+ /* setup sgl */
+ sci_request_build_sgl(ireq);
+}
+
enum sci_status
sci_io_request_frame_handler(struct isci_request *ireq,
u32 frame_index)
@@ -1450,8 +1728,8 @@ sci_io_request_frame_handler(struct isci_request *ireq,
resp_iu = &ireq->ssp.rsp;
- if (resp_iu->datapres == 0x01 ||
- resp_iu->datapres == 0x02) {
+ if (resp_iu->datapres == SAS_DATAPRES_RESPONSE_DATA ||
+ resp_iu->datapres == SAS_DATAPRES_SENSE_DATA) {
ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR;
} else {
@@ -1482,29 +1760,30 @@ sci_io_request_frame_handler(struct isci_request *ireq,
return SCI_SUCCESS;
case SCI_REQ_SMP_WAIT_RESP: {
- struct smp_resp *rsp_hdr = &ireq->smp.rsp;
- void *frame_header;
+ struct sas_task *task = isci_request_access_task(ireq);
+ struct scatterlist *sg = &task->smp_task.smp_resp;
+ void *frame_header, *kaddr;
+ u8 *rsp;
sci_unsolicited_frame_control_get_header(&ihost->uf_control,
- frame_index,
- &frame_header);
-
- /* byte swap the header. */
- word_cnt = SMP_RESP_HDR_SZ / sizeof(u32);
- sci_swab32_cpy(rsp_hdr, frame_header, word_cnt);
+ frame_index,
+ &frame_header);
+ kaddr = kmap_atomic(sg_page(sg));
+ rsp = kaddr + sg->offset;
+ sci_swab32_cpy(rsp, frame_header, 1);
- if (rsp_hdr->frame_type == SMP_RESPONSE) {
+ if (rsp[0] == SMP_RESPONSE) {
void *smp_resp;
sci_unsolicited_frame_control_get_buffer(&ihost->uf_control,
- frame_index,
- &smp_resp);
+ frame_index,
+ &smp_resp);
- word_cnt = (sizeof(struct smp_resp) - SMP_RESP_HDR_SZ) /
- sizeof(u32);
-
- sci_swab32_cpy(((u8 *) rsp_hdr) + SMP_RESP_HDR_SZ,
- smp_resp, word_cnt);
+ word_cnt = (sg->length/4)-1;
+ if (word_cnt > 0)
+ word_cnt = min_t(unsigned int, word_cnt,
+ SCU_UNSOLICITED_FRAME_BUFFER_SIZE/4);
+ sci_swab32_cpy(rsp + 4, smp_resp, word_cnt);
ireq->scu_status = SCU_TASK_DONE_GOOD;
ireq->sci_status = SCI_SUCCESS;
@@ -1520,12 +1799,13 @@ sci_io_request_frame_handler(struct isci_request *ireq,
__func__,
ireq,
frame_index,
- rsp_hdr->frame_type);
+ rsp[0]);
ireq->scu_status = SCU_TASK_DONE_SMP_FRM_TYPE_ERR;
ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR;
sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
}
+ kunmap_atomic(kaddr);
sci_controller_release_frame(ihost, frame_index);
@@ -1683,7 +1963,7 @@ sci_io_request_frame_handler(struct isci_request *ireq,
frame_index,
(void **)&frame_buffer);
- sci_controller_copy_sata_response(&ireq->stp.req,
+ sci_controller_copy_sata_response(&ireq->stp.rsp,
frame_header,
frame_buffer);
@@ -1772,59 +2052,24 @@ sci_io_request_frame_handler(struct isci_request *ireq,
return status;
}
- case SCI_REQ_STP_SOFT_RESET_WAIT_D2H: {
- struct dev_to_host_fis *frame_header;
- u32 *frame_buffer;
-
- status = sci_unsolicited_frame_control_get_header(&ihost->uf_control,
- frame_index,
- (void **)&frame_header);
- if (status != SCI_SUCCESS) {
- dev_err(&ihost->pdev->dev,
- "%s: SCIC IO Request 0x%p could not get frame "
- "header for frame index %d, status %x\n",
- __func__,
- stp_req,
- frame_index,
- status);
- return status;
- }
-
- switch (frame_header->fis_type) {
- case FIS_REGD2H:
- sci_unsolicited_frame_control_get_buffer(&ihost->uf_control,
- frame_index,
- (void **)&frame_buffer);
-
- sci_controller_copy_sata_response(&ireq->stp.rsp,
- frame_header,
- frame_buffer);
-
- /* The command has completed with error */
- ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
- ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID;
- break;
-
- default:
- dev_warn(&ihost->pdev->dev,
- "%s: IO Request:0x%p Frame Id:%d protocol "
- "violation occurred\n",
- __func__,
- stp_req,
- frame_index);
-
- ireq->scu_status = SCU_TASK_DONE_UNEXP_FIS;
- ireq->sci_status = SCI_FAILURE_PROTOCOL_VIOLATION;
- break;
- }
-
- sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
+ case SCI_REQ_ATAPI_WAIT_PIO_SETUP: {
+ struct sas_task *task = isci_request_access_task(ireq);
- /* Frame has been decoded return it to the controller */
sci_controller_release_frame(ihost, frame_index);
+ ireq->target_device->working_request = ireq;
+ if (task->data_dir == DMA_NONE) {
+ sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_TC_COMP);
+ scu_atapi_reconstruct_raw_frame_task_context(ireq);
+ } else {
+ sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_D2H);
+ scu_atapi_construct_task_context(ireq);
+ }
- return status;
+ sci_controller_continue_io(ireq);
+ return SCI_SUCCESS;
}
+ case SCI_REQ_ATAPI_WAIT_D2H:
+ return atapi_d2h_reg_frame_handler(ireq, frame_index);
case SCI_REQ_ABORTING:
/*
* TODO: Is it even possible to get an unsolicited frame in the
@@ -1849,8 +2094,6 @@ sci_io_request_frame_handler(struct isci_request *ireq,
static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq,
u32 completion_code)
{
- enum sci_status status = SCI_SUCCESS;
-
switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) {
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD):
ireq->scu_status = SCU_TASK_DONE_GOOD;
@@ -1865,7 +2108,7 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq
*/
if (ireq->stp.rsp.fis_type == FIS_REGD2H) {
sci_remote_device_suspend(ireq->target_device,
- SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code)));
+ SCI_SW_SUSPEND_NORMAL);
ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID;
@@ -1886,14 +2129,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq
/* TODO We can retry the command for SCU_TASK_DONE_CMD_LL_R_ERR
* - this comes only for B0
*/
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_INV_FIS_LEN):
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_MAX_PLD_ERR):
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_R_ERR):
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CMD_LL_R_ERR):
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CRC_ERR):
- sci_remote_device_suspend(ireq->target_device,
- SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code)));
- /* Fall through to the default case */
default:
/* All other completion status cause the IO to be complete. */
ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code);
@@ -1902,28 +2137,26 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq
break;
}
- return status;
+ return SCI_SUCCESS;
}
-static enum sci_status
-stp_request_soft_reset_await_h2d_asserted_tc_event(struct isci_request *ireq,
- u32 completion_code)
+static enum sci_status atapi_raw_completion(struct isci_request *ireq, u32 completion_code,
+ enum sci_base_request_states next)
{
switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) {
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD):
ireq->scu_status = SCU_TASK_DONE_GOOD;
ireq->sci_status = SCI_SUCCESS;
- sci_change_state(&ireq->sm, SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG);
+ sci_change_state(&ireq->sm, next);
break;
-
default:
- /*
- * All other completion status cause the IO to be complete.
+ /* All other completion status cause the IO to be complete.
* If a NAK was received, then it is up to the user to retry
* the request.
*/
ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code);
ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR;
+
sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
break;
}
@@ -1931,40 +2164,231 @@ stp_request_soft_reset_await_h2d_asserted_tc_event(struct isci_request *ireq,
return SCI_SUCCESS;
}
-static enum sci_status
-stp_request_soft_reset_await_h2d_diagnostic_tc_event(struct isci_request *ireq,
- u32 completion_code)
+static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ireq,
+ u32 completion_code)
{
+ struct isci_remote_device *idev = ireq->target_device;
+ struct dev_to_host_fis *d2h = &ireq->stp.rsp;
+ enum sci_status status = SCI_SUCCESS;
+
switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) {
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD):
+ case (SCU_TASK_DONE_GOOD << SCU_COMPLETION_TL_STATUS_SHIFT):
+ sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
+ break;
+
+ case (SCU_TASK_DONE_UNEXP_FIS << SCU_COMPLETION_TL_STATUS_SHIFT): {
+ u16 len = sci_req_tx_bytes(ireq);
+
+ /* likely non-error data underrun, workaround missing
+ * d2h frame from the controller
+ */
+ if (d2h->fis_type != FIS_REGD2H) {
+ d2h->fis_type = FIS_REGD2H;
+ d2h->flags = (1 << 6);
+ d2h->status = 0x50;
+ d2h->error = 0;
+ d2h->lbal = 0;
+ d2h->byte_count_low = len & 0xff;
+ d2h->byte_count_high = len >> 8;
+ d2h->device = 0xa0;
+ d2h->lbal_exp = 0;
+ d2h->lbam_exp = 0;
+ d2h->lbah_exp = 0;
+ d2h->_r_a = 0;
+ d2h->sector_count = 0x3;
+ d2h->sector_count_exp = 0;
+ d2h->_r_b = 0;
+ d2h->_r_c = 0;
+ d2h->_r_d = 0;
+ }
+
+ ireq->scu_status = SCU_TASK_DONE_GOOD;
+ ireq->sci_status = SCI_SUCCESS_IO_DONE_EARLY;
+ status = ireq->sci_status;
+
+ /* the hw will have suspended the rnc, so complete the
+ * request upon pending resume
+ */
+ sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR);
+ break;
+ }
+ case (SCU_TASK_DONE_EXCESS_DATA << SCU_COMPLETION_TL_STATUS_SHIFT):
+ /* In this case, there is no UF coming after.
+ * compelte the IO now.
+ */
ireq->scu_status = SCU_TASK_DONE_GOOD;
ireq->sci_status = SCI_SUCCESS;
- sci_change_state(&ireq->sm, SCI_REQ_STP_SOFT_RESET_WAIT_D2H);
+ sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
break;
default:
- /* All other completion status cause the IO to be complete. If
- * a NAK was received, then it is up to the user to retry the
- * request.
- */
- ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code);
- ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR;
- sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
+ if (d2h->fis_type == FIS_REGD2H) {
+ /* UF received change the device state to ATAPI_ERROR */
+ status = ireq->sci_status;
+ sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR);
+ } else {
+ /* If receiving any non-success TC status, no UF
+ * received yet, then an UF for the status fis
+ * is coming after (XXX: suspect this is
+ * actually a protocol error or a bug like the
+ * DONE_UNEXP_FIS case)
+ */
+ ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
+ ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID;
+
+ sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_D2H);
+ }
break;
}
- return SCI_SUCCESS;
+ return status;
+}
+
+static int sci_request_smp_completion_status_is_tx_suspend(
+ unsigned int completion_status)
+{
+ switch (completion_status) {
+ case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
+ case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
+ case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
+ return 1;
+ }
+ return 0;
+}
+
+static int sci_request_smp_completion_status_is_tx_rx_suspend(
+ unsigned int completion_status)
+{
+ return 0; /* There are no Tx/Rx SMP suspend conditions. */
+}
+
+static int sci_request_ssp_completion_status_is_tx_suspend(
+ unsigned int completion_status)
+{
+ switch (completion_status) {
+ case SCU_TASK_DONE_TX_RAW_CMD_ERR:
+ case SCU_TASK_DONE_LF_ERR:
+ case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
+ case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
+ case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
+ case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY:
+ case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED:
+ case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED:
+ return 1;
+ }
+ return 0;
+}
+
+static int sci_request_ssp_completion_status_is_tx_rx_suspend(
+ unsigned int completion_status)
+{
+ return 0; /* There are no Tx/Rx SSP suspend conditions. */
+}
+
+static int sci_request_stpsata_completion_status_is_tx_suspend(
+ unsigned int completion_status)
+{
+ switch (completion_status) {
+ case SCU_TASK_DONE_TX_RAW_CMD_ERR:
+ case SCU_TASK_DONE_LL_R_ERR:
+ case SCU_TASK_DONE_LL_PERR:
+ case SCU_TASK_DONE_REG_ERR:
+ case SCU_TASK_DONE_SDB_ERR:
+ case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
+ case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
+ case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
+ case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
+ case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY:
+ case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED:
+ case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED:
+ return 1;
+ }
+ return 0;
+}
+
+
+static int sci_request_stpsata_completion_status_is_tx_rx_suspend(
+ unsigned int completion_status)
+{
+ switch (completion_status) {
+ case SCU_TASK_DONE_LF_ERR:
+ case SCU_TASK_DONE_LL_SY_TERM:
+ case SCU_TASK_DONE_LL_LF_TERM:
+ case SCU_TASK_DONE_BREAK_RCVD:
+ case SCU_TASK_DONE_INV_FIS_LEN:
+ case SCU_TASK_DONE_UNEXP_FIS:
+ case SCU_TASK_DONE_UNEXP_SDBFIS:
+ case SCU_TASK_DONE_MAX_PLD_ERR:
+ return 1;
+ }
+ return 0;
+}
+
+static void sci_request_handle_suspending_completions(
+ struct isci_request *ireq,
+ u32 completion_code)
+{
+ int is_tx = 0;
+ int is_tx_rx = 0;
+
+ switch (ireq->protocol) {
+ case SAS_PROTOCOL_SMP:
+ is_tx = sci_request_smp_completion_status_is_tx_suspend(
+ completion_code);
+ is_tx_rx = sci_request_smp_completion_status_is_tx_rx_suspend(
+ completion_code);
+ break;
+ case SAS_PROTOCOL_SSP:
+ is_tx = sci_request_ssp_completion_status_is_tx_suspend(
+ completion_code);
+ is_tx_rx = sci_request_ssp_completion_status_is_tx_rx_suspend(
+ completion_code);
+ break;
+ case SAS_PROTOCOL_STP:
+ is_tx = sci_request_stpsata_completion_status_is_tx_suspend(
+ completion_code);
+ is_tx_rx =
+ sci_request_stpsata_completion_status_is_tx_rx_suspend(
+ completion_code);
+ break;
+ default:
+ dev_warn(&ireq->isci_host->pdev->dev,
+ "%s: request %p has no valid protocol\n",
+ __func__, ireq);
+ break;
+ }
+ if (is_tx || is_tx_rx) {
+ BUG_ON(is_tx && is_tx_rx);
+
+ sci_remote_node_context_suspend(
+ &ireq->target_device->rnc,
+ SCI_HW_SUSPEND,
+ (is_tx_rx) ? SCU_EVENT_TL_RNC_SUSPEND_TX_RX
+ : SCU_EVENT_TL_RNC_SUSPEND_TX);
+ }
}
enum sci_status
sci_io_request_tc_completion(struct isci_request *ireq,
- u32 completion_code)
+ u32 completion_code)
{
enum sci_base_request_states state;
struct isci_host *ihost = ireq->owning_controller;
state = ireq->sm.current_state_id;
+ /* Decode those completions that signal upcoming suspension events. */
+ sci_request_handle_suspending_completions(
+ ireq, SCU_GET_COMPLETION_TL_STATUS(completion_code));
+
switch (state) {
case SCI_REQ_STARTED:
return request_started_state_tc_event(ireq, completion_code);
@@ -1995,25 +2419,24 @@ sci_io_request_tc_completion(struct isci_request *ireq,
case SCI_REQ_STP_PIO_DATA_OUT:
return pio_data_out_tx_done_tc_event(ireq, completion_code);
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED:
- return stp_request_soft_reset_await_h2d_asserted_tc_event(ireq,
- completion_code);
-
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG:
- return stp_request_soft_reset_await_h2d_diagnostic_tc_event(ireq,
- completion_code);
-
case SCI_REQ_ABORTING:
return request_aborting_state_tc_event(ireq,
completion_code);
+ case SCI_REQ_ATAPI_WAIT_H2D:
+ return atapi_raw_completion(ireq, completion_code,
+ SCI_REQ_ATAPI_WAIT_PIO_SETUP);
+
+ case SCI_REQ_ATAPI_WAIT_TC_COMP:
+ return atapi_raw_completion(ireq, completion_code,
+ SCI_REQ_ATAPI_WAIT_D2H);
+
+ case SCI_REQ_ATAPI_WAIT_D2H:
+ return atapi_data_tc_completion_handler(ireq, completion_code);
+
default:
- dev_warn(&ihost->pdev->dev,
- "%s: SCIC IO Request given task completion "
- "notification %x while in wrong state %d\n",
- __func__,
- completion_code,
- state);
+ dev_warn(&ihost->pdev->dev, "%s: %x in wrong state %s\n",
+ __func__, completion_code, req_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -2022,7 +2445,7 @@ sci_io_request_tc_completion(struct isci_request *ireq,
* isci_request_process_response_iu() - This function sets the status and
* response iu, in the task struct, from the request object for the upper
* layer driver.
- * @sas_task: This parameter is the task struct from the upper layer driver.
+ * @task: This parameter is the task struct from the upper layer driver.
* @resp_iu: This parameter points to the response iu of the completed request.
* @dev: This parameter specifies the linux device struct.
*
@@ -2037,7 +2460,7 @@ static void isci_request_process_response_iu(
"%s: resp_iu = %p "
"resp_iu->status = 0x%x,\nresp_iu->datapres = %d "
"resp_iu->response_data_len = %x, "
- "resp_iu->sense_data_len = %x\nrepsonse data: ",
+ "resp_iu->sense_data_len = %x\nresponse data: ",
__func__,
resp_iu,
resp_iu->status,
@@ -2055,11 +2478,9 @@ static void isci_request_process_response_iu(
* isci_request_set_open_reject_status() - This function prepares the I/O
* completion for OPEN_REJECT conditions.
* @request: This parameter is the completed isci_request object.
+ * @task: This parameter is the task struct from the upper layer driver.
* @response_ptr: This parameter specifies the service response for the I/O.
* @status_ptr: This parameter specifies the exec status for the I/O.
- * @complete_to_host_ptr: This parameter specifies the action to be taken by
- * the LLDD with respect to completing this request or forcing an abort
- * condition on the I/O.
* @open_rej_reason: This parameter specifies the encoded reason for the
* abandon-class reject.
*
@@ -2070,26 +2491,23 @@ static void isci_request_set_open_reject_status(
struct sas_task *task,
enum service_response *response_ptr,
enum exec_status *status_ptr,
- enum isci_completion_selection *complete_to_host_ptr,
enum sas_open_rej_reason open_rej_reason)
{
/* Task in the target is done. */
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
*response_ptr = SAS_TASK_UNDELIVERED;
*status_ptr = SAS_OPEN_REJECT;
- *complete_to_host_ptr = isci_perform_normal_io_completion;
task->task_status.open_rej_reason = open_rej_reason;
}
/**
* isci_request_handle_controller_specific_errors() - This function decodes
* controller-specific I/O completion error conditions.
+ * @idev: Remote device
* @request: This parameter is the completed isci_request object.
+ * @task: This parameter is the task struct from the upper layer driver.
* @response_ptr: This parameter specifies the service response for the I/O.
* @status_ptr: This parameter specifies the exec status for the I/O.
- * @complete_to_host_ptr: This parameter specifies the action to be taken by
- * the LLDD with respect to completing this request or forcing an abort
- * condition on the I/O.
*
* none.
*/
@@ -2098,8 +2516,7 @@ static void isci_request_handle_controller_specific_errors(
struct isci_request *request,
struct sas_task *task,
enum service_response *response_ptr,
- enum exec_status *status_ptr,
- enum isci_completion_selection *complete_to_host_ptr)
+ enum exec_status *status_ptr)
{
unsigned int cstatus;
@@ -2140,9 +2557,6 @@ static void isci_request_handle_controller_specific_errors(
*status_ptr = SAS_ABORTED_TASK;
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
-
- *complete_to_host_ptr =
- isci_perform_normal_io_completion;
} else {
/* Task in the target is not done. */
*response_ptr = SAS_TASK_UNDELIVERED;
@@ -2150,12 +2564,9 @@ static void isci_request_handle_controller_specific_errors(
if (!idev)
*status_ptr = SAS_DEVICE_UNKNOWN;
else
- *status_ptr = SAM_STAT_TASK_ABORTED;
+ *status_ptr = SAS_SAM_STAT_TASK_ABORTED;
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
-
- *complete_to_host_ptr =
- isci_perform_error_io_completion;
}
break;
@@ -2184,8 +2595,6 @@ static void isci_request_handle_controller_specific_errors(
*status_ptr = SAS_ABORTED_TASK;
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
-
- *complete_to_host_ptr = isci_perform_normal_io_completion;
break;
@@ -2196,7 +2605,7 @@ static void isci_request_handle_controller_specific_errors(
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_WRONG_DEST);
+ SAS_OREJ_WRONG_DEST);
break;
case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
@@ -2206,56 +2615,56 @@ static void isci_request_handle_controller_specific_errors(
*/
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_RESV_AB0);
+ SAS_OREJ_RESV_AB0);
break;
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_RESV_AB1);
+ SAS_OREJ_RESV_AB1);
break;
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_RESV_AB2);
+ SAS_OREJ_RESV_AB2);
break;
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_RESV_AB3);
+ SAS_OREJ_RESV_AB3);
break;
case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_BAD_DEST);
+ SAS_OREJ_BAD_DEST);
break;
case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY:
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_STP_NORES);
+ SAS_OREJ_STP_NORES);
break;
case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED:
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_EPROTO);
+ SAS_OREJ_EPROTO);
break;
case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED:
isci_request_set_open_reject_status(
request, task, response_ptr, status_ptr,
- complete_to_host_ptr, SAS_OREJ_CONN_RATE);
+ SAS_OREJ_CONN_RATE);
break;
case SCU_TASK_DONE_LL_R_ERR:
@@ -2285,139 +2694,30 @@ static void isci_request_handle_controller_specific_errors(
default:
/* Task in the target is not done. */
*response_ptr = SAS_TASK_UNDELIVERED;
- *status_ptr = SAM_STAT_TASK_ABORTED;
+ *status_ptr = SAS_SAM_STAT_TASK_ABORTED;
- if (task->task_proto == SAS_PROTOCOL_SMP) {
+ if (task->task_proto == SAS_PROTOCOL_SMP)
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
-
- *complete_to_host_ptr = isci_perform_normal_io_completion;
- } else {
+ else
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
-
- *complete_to_host_ptr = isci_perform_error_io_completion;
- }
- break;
- }
-}
-
-/**
- * isci_task_save_for_upper_layer_completion() - This function saves the
- * request for later completion to the upper layer driver.
- * @host: This parameter is a pointer to the host on which the the request
- * should be queued (either as an error or success).
- * @request: This parameter is the completed request.
- * @response: This parameter is the response code for the completed task.
- * @status: This parameter is the status code for the completed task.
- *
- * none.
- */
-static void isci_task_save_for_upper_layer_completion(
- struct isci_host *host,
- struct isci_request *request,
- enum service_response response,
- enum exec_status status,
- enum isci_completion_selection task_notification_selection)
-{
- struct sas_task *task = isci_request_access_task(request);
-
- task_notification_selection
- = isci_task_set_completion_status(task, response, status,
- task_notification_selection);
-
- /* Tasks aborted specifically by a call to the lldd_abort_task
- * function should not be completed to the host in the regular path.
- */
- switch (task_notification_selection) {
-
- case isci_perform_normal_io_completion:
-
- /* Normal notification (task_done) */
- dev_dbg(&host->pdev->dev,
- "%s: Normal - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
- /* Add to the completed list. */
- list_add(&request->completed_node,
- &host->requests_to_complete);
-
- /* Take the request off the device's pending request list. */
- list_del_init(&request->dev_node);
- break;
-
- case isci_perform_aborted_io_completion:
- /* No notification to libsas because this request is
- * already in the abort path.
- */
- dev_dbg(&host->pdev->dev,
- "%s: Aborted - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
-
- /* Wake up whatever process was waiting for this
- * request to complete.
- */
- WARN_ON(request->io_request_completion == NULL);
-
- if (request->io_request_completion != NULL) {
-
- /* Signal whoever is waiting that this
- * request is complete.
- */
- complete(request->io_request_completion);
- }
- break;
-
- case isci_perform_error_io_completion:
- /* Use sas_task_abort */
- dev_dbg(&host->pdev->dev,
- "%s: Error - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
- /* Add to the aborted list. */
- list_add(&request->completed_node,
- &host->requests_to_errorback);
- break;
-
- default:
- dev_dbg(&host->pdev->dev,
- "%s: Unknown - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
-
- /* Add to the error to libsas list. */
- list_add(&request->completed_node,
- &host->requests_to_errorback);
break;
}
}
-static void isci_request_process_stp_response(struct sas_task *task,
- void *response_buffer)
+static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_fis *fis)
{
- struct dev_to_host_fis *d2h_reg_fis = response_buffer;
struct task_status_struct *ts = &task->task_status;
struct ata_task_resp *resp = (void *)&ts->buf[0];
- resp->frame_len = le16_to_cpu(*(__le16 *)(response_buffer + 6));
- memcpy(&resp->ending_fis[0], response_buffer + 16, 24);
+ resp->frame_len = sizeof(*fis);
+ memcpy(resp->ending_fis, fis, sizeof(*fis));
ts->buf_valid_size = sizeof(*resp);
- /**
- * If the device fault bit is set in the status register, then
- * set the sense data and return.
- */
- if (d2h_reg_fis->status & ATA_DF)
+ /* If an error is flagged let libata decode the fis */
+ if (ac_err_mask(fis->status))
ts->stat = SAS_PROTO_RESPONSE;
else
- ts->stat = SAM_STAT_GOOD;
+ ts->stat = SAS_SAM_STAT_GOOD;
ts->resp = SAS_TASK_COMPLETE;
}
@@ -2428,313 +2728,168 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
{
struct sas_task *task = isci_request_access_task(request);
struct ssp_response_iu *resp_iu;
- void *resp_buf;
unsigned long task_flags;
- struct isci_remote_device *idev = isci_lookup_device(task->dev);
- enum service_response response = SAS_TASK_UNDELIVERED;
- enum exec_status status = SAS_ABORTED_TASK;
- enum isci_request_status request_status;
- enum isci_completion_selection complete_to_host
- = isci_perform_normal_io_completion;
+ struct isci_remote_device *idev = request->target_device;
+ enum service_response response = SAS_TASK_UNDELIVERED;
+ enum exec_status status = SAS_ABORTED_TASK;
dev_dbg(&ihost->pdev->dev,
- "%s: request = %p, task = %p,\n"
+ "%s: request = %p, task = %p, "
"task->data_dir = %d completion_status = 0x%x\n",
- __func__,
- request,
- task,
- task->data_dir,
- completion_status);
+ __func__, request, task, task->data_dir, completion_status);
- spin_lock(&request->state_lock);
- request_status = request->status;
+ /* The request is done from an SCU HW perspective. */
- /* Decode the request status. Note that if the request has been
- * aborted by a task management function, we don't care
- * what the status is.
- */
- switch (request_status) {
-
- case aborted:
- /* "aborted" indicates that the request was aborted by a task
- * management function, since once a task management request is
- * perfomed by the device, the request only completes because
- * of the subsequent driver terminate.
- *
- * Aborted also means an external thread is explicitly managing
- * this request, so that we do not complete it up the stack.
- *
- * The target is still there (since the TMF was successful).
- */
- set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- response = SAS_TASK_COMPLETE;
+ /* This is an active request being completed from the core. */
+ switch (completion_status) {
- /* See if the device has been/is being stopped. Note
- * that we ignore the quiesce state, since we are
- * concerned about the actual device state.
- */
- if (!idev)
- status = SAS_DEVICE_UNKNOWN;
- else
- status = SAS_ABORTED_TASK;
+ case SCI_IO_FAILURE_RESPONSE_VALID:
+ dev_dbg(&ihost->pdev->dev,
+ "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n",
+ __func__, request, task);
+
+ if (sas_protocol_ata(task->task_proto)) {
+ isci_process_stp_response(task, &request->stp.rsp);
+ } else if (SAS_PROTOCOL_SSP == task->task_proto) {
- complete_to_host = isci_perform_aborted_io_completion;
- /* This was an aborted request. */
+ /* crack the iu response buffer. */
+ resp_iu = &request->ssp.rsp;
+ isci_request_process_response_iu(task, resp_iu,
+ &ihost->pdev->dev);
- spin_unlock(&request->state_lock);
+ } else if (SAS_PROTOCOL_SMP == task->task_proto) {
+
+ dev_err(&ihost->pdev->dev,
+ "%s: SCI_IO_FAILURE_RESPONSE_VALID: "
+ "SAS_PROTOCOL_SMP protocol\n",
+ __func__);
+
+ } else
+ dev_err(&ihost->pdev->dev,
+ "%s: unknown protocol\n", __func__);
+
+ /* use the task status set in the task struct by the
+ * isci_request_process_response_iu call.
+ */
+ set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
+ response = task->task_status.resp;
+ status = task->task_status.stat;
break;
- case aborting:
- /* aborting means that the task management function tried and
- * failed to abort the request. We need to note the request
- * as SAS_TASK_UNDELIVERED, so that the scsi mid layer marks the
- * target as down.
- *
- * Aborting also means an external thread is explicitly managing
- * this request, so that we do not complete it up the stack.
- */
+ case SCI_IO_SUCCESS:
+ case SCI_IO_SUCCESS_IO_DONE_EARLY:
+
+ response = SAS_TASK_COMPLETE;
+ status = SAS_SAM_STAT_GOOD;
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- response = SAS_TASK_UNDELIVERED;
- if (!idev)
- /* The device has been /is being stopped. Note that
- * we ignore the quiesce state, since we are
- * concerned about the actual device state.
- */
- status = SAS_DEVICE_UNKNOWN;
- else
- status = SAS_PHY_DOWN;
+ if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) {
- complete_to_host = isci_perform_aborted_io_completion;
+ /* This was an SSP / STP / SATA transfer.
+ * There is a possibility that less data than
+ * the maximum was transferred.
+ */
+ u32 transferred_length = sci_req_tx_bytes(request);
- /* This was an aborted request. */
+ task->task_status.residual
+ = task->total_xfer_len - transferred_length;
- spin_unlock(&request->state_lock);
+ /* If there were residual bytes, call this an
+ * underrun.
+ */
+ if (task->task_status.residual != 0)
+ status = SAS_DATA_UNDERRUN;
+
+ dev_dbg(&ihost->pdev->dev,
+ "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n",
+ __func__, status);
+
+ } else
+ dev_dbg(&ihost->pdev->dev, "%s: SCI_IO_SUCCESS\n",
+ __func__);
break;
- case terminating:
+ case SCI_IO_FAILURE_TERMINATED:
- /* This was an terminated request. This happens when
- * the I/O is being terminated because of an action on
- * the device (reset, tear down, etc.), and the I/O needs
- * to be completed up the stack.
- */
+ dev_dbg(&ihost->pdev->dev,
+ "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n",
+ __func__, request, task);
+
+ /* The request was terminated explicitly. */
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
response = SAS_TASK_UNDELIVERED;
/* See if the device has been/is being stopped. Note
- * that we ignore the quiesce state, since we are
- * concerned about the actual device state.
- */
+ * that we ignore the quiesce state, since we are
+ * concerned about the actual device state.
+ */
if (!idev)
status = SAS_DEVICE_UNKNOWN;
else
status = SAS_ABORTED_TASK;
-
- complete_to_host = isci_perform_aborted_io_completion;
-
- /* This was a terminated request. */
-
- spin_unlock(&request->state_lock);
- break;
-
- case dead:
- /* This was a terminated request that timed-out during the
- * termination process. There is no task to complete to
- * libsas.
- */
- complete_to_host = isci_perform_normal_io_completion;
- spin_unlock(&request->state_lock);
break;
- default:
-
- /* The request is done from an SCU HW perspective. */
- request->status = completed;
-
- spin_unlock(&request->state_lock);
-
- /* This is an active request being completed from the core. */
- switch (completion_status) {
+ case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR:
- case SCI_IO_FAILURE_RESPONSE_VALID:
- dev_dbg(&ihost->pdev->dev,
- "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n",
- __func__,
- request,
- task);
-
- if (sas_protocol_ata(task->task_proto)) {
- resp_buf = &request->stp.rsp;
- isci_request_process_stp_response(task,
- resp_buf);
- } else if (SAS_PROTOCOL_SSP == task->task_proto) {
-
- /* crack the iu response buffer. */
- resp_iu = &request->ssp.rsp;
- isci_request_process_response_iu(task, resp_iu,
- &ihost->pdev->dev);
-
- } else if (SAS_PROTOCOL_SMP == task->task_proto) {
-
- dev_err(&ihost->pdev->dev,
- "%s: SCI_IO_FAILURE_RESPONSE_VALID: "
- "SAS_PROTOCOL_SMP protocol\n",
- __func__);
-
- } else
- dev_err(&ihost->pdev->dev,
- "%s: unknown protocol\n", __func__);
-
- /* use the task status set in the task struct by the
- * isci_request_process_response_iu call.
- */
- set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- response = task->task_status.resp;
- status = task->task_status.stat;
- break;
+ isci_request_handle_controller_specific_errors(idev, request,
+ task, &response,
+ &status);
+ break;
- case SCI_IO_SUCCESS:
- case SCI_IO_SUCCESS_IO_DONE_EARLY:
+ case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED:
+ /* This is a special case, in that the I/O completion
+ * is telling us that the device needs a reset.
+ * In order for the device reset condition to be
+ * noticed, the I/O has to be handled in the error
+ * handler. Set the reset flag and cause the
+ * SCSI error thread to be scheduled.
+ */
+ spin_lock_irqsave(&task->task_state_lock, task_flags);
+ task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
+ spin_unlock_irqrestore(&task->task_state_lock, task_flags);
- response = SAS_TASK_COMPLETE;
- status = SAM_STAT_GOOD;
- set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
+ /* Fail the I/O. */
+ response = SAS_TASK_UNDELIVERED;
+ status = SAS_SAM_STAT_TASK_ABORTED;
- if (task->task_proto == SAS_PROTOCOL_SMP) {
- void *rsp = &request->smp.rsp;
+ clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
+ break;
- dev_dbg(&ihost->pdev->dev,
- "%s: SMP protocol completion\n",
- __func__);
-
- sg_copy_from_buffer(
- &task->smp_task.smp_resp, 1,
- rsp, sizeof(struct smp_resp));
- } else if (completion_status
- == SCI_IO_SUCCESS_IO_DONE_EARLY) {
-
- /* This was an SSP / STP / SATA transfer.
- * There is a possibility that less data than
- * the maximum was transferred.
- */
- u32 transferred_length = sci_req_tx_bytes(request);
+ case SCI_FAILURE_RETRY_REQUIRED:
- task->task_status.residual
- = task->total_xfer_len - transferred_length;
+ /* Fail the I/O so it can be retried. */
+ response = SAS_TASK_UNDELIVERED;
+ if (!idev)
+ status = SAS_DEVICE_UNKNOWN;
+ else
+ status = SAS_ABORTED_TASK;
- /* If there were residual bytes, call this an
- * underrun.
- */
- if (task->task_status.residual != 0)
- status = SAS_DATA_UNDERRUN;
+ set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
+ break;
- dev_dbg(&ihost->pdev->dev,
- "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n",
- __func__,
- status);
- } else
- dev_dbg(&ihost->pdev->dev,
- "%s: SCI_IO_SUCCESS\n",
- __func__);
+ default:
+ /* Catch any otherwise unhandled error codes here. */
+ dev_dbg(&ihost->pdev->dev,
+ "%s: invalid completion code: 0x%x - "
+ "isci_request = %p\n",
+ __func__, completion_status, request);
- break;
+ response = SAS_TASK_UNDELIVERED;
- case SCI_IO_FAILURE_TERMINATED:
- dev_dbg(&ihost->pdev->dev,
- "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n",
- __func__,
- request,
- task);
+ /* See if the device has been/is being stopped. Note
+ * that we ignore the quiesce state, since we are
+ * concerned about the actual device state.
+ */
+ if (!idev)
+ status = SAS_DEVICE_UNKNOWN;
+ else
+ status = SAS_ABORTED_TASK;
- /* The request was terminated explicitly. No handling
- * is needed in the SCSI error handler path.
- */
+ if (SAS_PROTOCOL_SMP == task->task_proto)
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- response = SAS_TASK_UNDELIVERED;
-
- /* See if the device has been/is being stopped. Note
- * that we ignore the quiesce state, since we are
- * concerned about the actual device state.
- */
- if (!idev)
- status = SAS_DEVICE_UNKNOWN;
- else
- status = SAS_ABORTED_TASK;
-
- complete_to_host = isci_perform_normal_io_completion;
- break;
-
- case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR:
-
- isci_request_handle_controller_specific_errors(
- idev, request, task, &response, &status,
- &complete_to_host);
-
- break;
-
- case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED:
- /* This is a special case, in that the I/O completion
- * is telling us that the device needs a reset.
- * In order for the device reset condition to be
- * noticed, the I/O has to be handled in the error
- * handler. Set the reset flag and cause the
- * SCSI error thread to be scheduled.
- */
- spin_lock_irqsave(&task->task_state_lock, task_flags);
- task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
- spin_unlock_irqrestore(&task->task_state_lock, task_flags);
-
- /* Fail the I/O. */
- response = SAS_TASK_UNDELIVERED;
- status = SAM_STAT_TASK_ABORTED;
-
- complete_to_host = isci_perform_error_io_completion;
+ else
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- break;
-
- case SCI_FAILURE_RETRY_REQUIRED:
-
- /* Fail the I/O so it can be retried. */
- response = SAS_TASK_UNDELIVERED;
- if (!idev)
- status = SAS_DEVICE_UNKNOWN;
- else
- status = SAS_ABORTED_TASK;
-
- complete_to_host = isci_perform_normal_io_completion;
- set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- break;
-
-
- default:
- /* Catch any otherwise unhandled error codes here. */
- dev_dbg(&ihost->pdev->dev,
- "%s: invalid completion code: 0x%x - "
- "isci_request = %p\n",
- __func__, completion_status, request);
-
- response = SAS_TASK_UNDELIVERED;
-
- /* See if the device has been/is being stopped. Note
- * that we ignore the quiesce state, since we are
- * concerned about the actual device state.
- */
- if (!idev)
- status = SAS_DEVICE_UNKNOWN;
- else
- status = SAS_ABORTED_TASK;
-
- if (SAS_PROTOCOL_SMP == task->task_proto) {
- set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- complete_to_host = isci_perform_normal_io_completion;
- } else {
- clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
- complete_to_host = isci_perform_error_io_completion;
- }
- break;
- }
break;
}
@@ -2749,7 +2904,7 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
task->total_xfer_len, task->data_dir);
else /* unmap the sgl dma addresses */
dma_unmap_sg(&ihost->pdev->dev, task->scatter,
- request->num_sg_entries, task->data_dir);
+ task->num_scatter, task->data_dir);
break;
case SAS_PROTOCOL_SMP: {
struct scatterlist *sg = &task->smp_task.smp_req;
@@ -2759,67 +2914,76 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
dma_unmap_sg(&ihost->pdev->dev, sg, 1, DMA_TO_DEVICE);
/* need to swab it back in case the command buffer is re-used */
- kaddr = kmap_atomic(sg_page(sg), KM_IRQ0);
+ kaddr = kmap_atomic(sg_page(sg));
smp_req = kaddr + sg->offset;
sci_swab32_cpy(smp_req, smp_req, sg->length / sizeof(u32));
- kunmap_atomic(kaddr, KM_IRQ0);
+ kunmap_atomic(kaddr);
break;
}
default:
break;
}
- /* Put the completed request on the correct list */
- isci_task_save_for_upper_layer_completion(ihost, request, response,
- status, complete_to_host
- );
+ spin_lock_irqsave(&task->task_state_lock, task_flags);
+
+ task->task_status.resp = response;
+ task->task_status.stat = status;
+
+ if (test_bit(IREQ_COMPLETE_IN_TARGET, &request->flags)) {
+ /* Normal notification (task_done) */
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ }
+ spin_unlock_irqrestore(&task->task_state_lock, task_flags);
/* complete the io request to the core. */
sci_controller_complete_io(ihost, request->target_device, request);
- isci_put_device(idev);
/* set terminated handle so it cannot be completed or
* terminated again, and to cause any calls into abort
* task to recognize the already completed case.
*/
set_bit(IREQ_TERMINATED, &request->flags);
+
+ ireq_done(ihost, request, task);
}
static void sci_request_started_state_enter(struct sci_base_state_machine *sm)
{
struct isci_request *ireq = container_of(sm, typeof(*ireq), sm);
struct domain_device *dev = ireq->target_device->domain_dev;
+ enum sci_base_request_states state;
struct sas_task *task;
/* XXX as hch said always creating an internal sas_task for tmf
* requests would simplify the driver
*/
- task = ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL;
+ task = (test_bit(IREQ_TMF, &ireq->flags)) ? NULL : isci_request_access_task(ireq);
/* all unaccelerated request types (non ssp or ncq) handled with
* substates
*/
- if (!task && dev->dev_type == SAS_END_DEV) {
- sci_change_state(sm, SCI_REQ_TASK_WAIT_TC_COMP);
- } else if (!task &&
- (isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_high ||
- isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_low)) {
- sci_change_state(sm, SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED);
+ if (!task && dev->dev_type == SAS_END_DEVICE) {
+ state = SCI_REQ_TASK_WAIT_TC_COMP;
} else if (task && task->task_proto == SAS_PROTOCOL_SMP) {
- sci_change_state(sm, SCI_REQ_SMP_WAIT_RESP);
+ state = SCI_REQ_SMP_WAIT_RESP;
} else if (task && sas_protocol_ata(task->task_proto) &&
!task->ata_task.use_ncq) {
- u32 state;
-
- if (task->data_dir == DMA_NONE)
+ if (dev->sata_dev.class == ATA_DEV_ATAPI &&
+ task->ata_task.fis.command == ATA_CMD_PACKET) {
+ state = SCI_REQ_ATAPI_WAIT_H2D;
+ } else if (task->data_dir == DMA_NONE) {
state = SCI_REQ_STP_NON_DATA_WAIT_H2D;
- else if (task->ata_task.dma_xfer)
+ } else if (task->ata_task.dma_xfer) {
state = SCI_REQ_STP_UDMA_WAIT_TC_COMP;
- else /* PIO */
+ } else /* PIO */ {
state = SCI_REQ_STP_PIO_WAIT_H2D;
-
- sci_change_state(sm, state);
+ }
+ } else {
+ /* SSP or NCQ are fully accelerated, no substates */
+ return;
}
+ sci_change_state(sm, state);
}
static void sci_request_completed_state_enter(struct sci_base_state_machine *sm)
@@ -2857,31 +3021,6 @@ static void sci_stp_request_started_pio_await_h2d_completion_enter(struct sci_ba
ireq->target_device->working_request = ireq;
}
-static void sci_stp_request_started_soft_reset_await_h2d_asserted_completion_enter(struct sci_base_state_machine *sm)
-{
- struct isci_request *ireq = container_of(sm, typeof(*ireq), sm);
-
- ireq->target_device->working_request = ireq;
-}
-
-static void sci_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter(struct sci_base_state_machine *sm)
-{
- struct isci_request *ireq = container_of(sm, typeof(*ireq), sm);
- struct scu_task_context *tc = ireq->tc;
- struct host_to_dev_fis *h2d_fis;
- enum sci_status status;
-
- /* Clear the SRST bit */
- h2d_fis = &ireq->stp.cmd;
- h2d_fis->control = 0;
-
- /* Clear the TC control bit */
- tc->control_frame = 0;
-
- status = sci_controller_continue_io(ireq);
- WARN_ONCE(status != SCI_SUCCESS, "isci: continue io failure\n");
-}
-
static const struct sci_base_state sci_request_state_table[] = {
[SCI_REQ_INIT] = { },
[SCI_REQ_CONSTRUCTED] = { },
@@ -2900,17 +3039,14 @@ static const struct sci_base_state sci_request_state_table[] = {
[SCI_REQ_STP_PIO_DATA_OUT] = { },
[SCI_REQ_STP_UDMA_WAIT_TC_COMP] = { },
[SCI_REQ_STP_UDMA_WAIT_D2H] = { },
- [SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED] = {
- .enter_state = sci_stp_request_started_soft_reset_await_h2d_asserted_completion_enter,
- },
- [SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG] = {
- .enter_state = sci_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter,
- },
- [SCI_REQ_STP_SOFT_RESET_WAIT_D2H] = { },
[SCI_REQ_TASK_WAIT_TC_COMP] = { },
[SCI_REQ_TASK_WAIT_TC_RESP] = { },
[SCI_REQ_SMP_WAIT_RESP] = { },
[SCI_REQ_SMP_WAIT_TC_COMP] = { },
+ [SCI_REQ_ATAPI_WAIT_H2D] = { },
+ [SCI_REQ_ATAPI_WAIT_PIO_SETUP] = { },
+ [SCI_REQ_ATAPI_WAIT_D2H] = { },
+ [SCI_REQ_ATAPI_WAIT_TC_COMP] = { },
[SCI_REQ_COMPLETED] = {
.enter_state = sci_request_completed_state_enter,
},
@@ -2928,7 +3064,7 @@ sci_general_request_construct(struct isci_host *ihost,
sci_init_sm(&ireq->sm, sci_request_state_table, SCI_REQ_INIT);
ireq->target_device = idev;
- ireq->protocol = SCIC_NO_PROTOCOL;
+ ireq->protocol = SAS_PROTOCOL_NONE;
ireq->saved_rx_frame_index = SCU_INVALID_FRAME_INDEX;
ireq->sci_status = SCI_SUCCESS;
@@ -2950,11 +3086,11 @@ sci_io_request_construct(struct isci_host *ihost,
if (idev->rnc.remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX)
return SCI_FAILURE_INVALID_REMOTE_DEVICE;
- if (dev->dev_type == SAS_END_DEV)
+ if (dev->dev_type == SAS_END_DEVICE)
/* pass */;
- else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))
+ else if (dev_is_sata(dev))
memset(&ireq->stp.cmd, 0, sizeof(ireq->stp.cmd));
- else if (dev_is_expander(dev))
+ else if (dev_is_expander(dev->dev_type))
/* pass */;
else
return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
@@ -2974,10 +3110,15 @@ enum sci_status sci_task_request_construct(struct isci_host *ihost,
/* Build the common part of the request */
sci_general_request_construct(ihost, idev, ireq);
- if (dev->dev_type == SAS_END_DEV ||
- dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
+ if (dev->dev_type == SAS_END_DEVICE || dev_is_sata(dev)) {
set_bit(IREQ_TMF, &ireq->flags);
memset(ireq->tc, 0, sizeof(struct scu_task_context));
+
+ /* Set the protocol indicator. */
+ if (dev_is_sata(dev))
+ ireq->protocol = SAS_PROTOCOL_STP;
+ else
+ ireq->protocol = SAS_PROTOCOL_SSP;
} else
status = SCI_FAILURE_UNSUPPORTED_PROTOCOL;
@@ -3017,7 +3158,10 @@ static enum sci_status isci_request_stp_request_construct(struct isci_request *i
status = sci_io_request_construct_basic_sata(ireq);
if (qc && (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
- qc->tf.command == ATA_CMD_FPDMA_READ)) {
+ qc->tf.command == ATA_CMD_FPDMA_READ ||
+ qc->tf.command == ATA_CMD_FPDMA_RECV ||
+ qc->tf.command == ATA_CMD_FPDMA_SEND ||
+ qc->tf.command == ATA_CMD_NCQ_NON_DATA)) {
fis->sector_count = qc->tag << 3;
ireq->tc->type.stp.ncq_tag = qc->tag;
}
@@ -3039,7 +3183,7 @@ sci_io_request_construct_smp(struct device *dev,
u8 req_len;
u32 cmd;
- kaddr = kmap_atomic(sg_page(sg), KM_IRQ0);
+ kaddr = kmap_atomic(sg_page(sg));
smp_req = kaddr + sg->offset;
/*
* Look at the SMP requests' header fields; for certain SAS 1.x SMP
@@ -3065,12 +3209,12 @@ sci_io_request_construct_smp(struct device *dev,
req_len = smp_req->req_len;
sci_swab32_cpy(smp_req, smp_req, sg->length / sizeof(u32));
cmd = *(u32 *) smp_req;
- kunmap_atomic(kaddr, KM_IRQ0);
+ kunmap_atomic(kaddr);
if (!dma_map_sg(dev, sg, 1, DMA_TO_DEVICE))
return SCI_FAILURE;
- ireq->protocol = SCIC_SMP_PROTOCOL;
+ ireq->protocol = SAS_PROTOCOL_SMP;
/* byte swap the smp request. */
@@ -3080,7 +3224,7 @@ sci_io_request_construct_smp(struct device *dev,
iport = idev->owning_port;
/*
- * Fill in the TC with the its required data
+ * Fill in the TC with its required data
* 00h
*/
task_context->priority = 0;
@@ -3177,7 +3321,7 @@ static enum sci_status isci_smp_request_build(struct isci_request *ireq)
* @ihost: This parameter specifies the ISCI host object
* @request: This parameter points to the isci_request object allocated in the
* request construct function.
- * @sci_device: This parameter is the handle for the sci core's remote device
+ * @idev: This parameter is the handle for the sci core's remote device
* object that is the destination for this request.
*
* SCI_SUCCESS on successfull completion, or specific failure code.
@@ -3243,7 +3387,7 @@ static enum sci_status isci_io_request_build(struct isci_host *ihost,
return SCI_FAILURE;
}
- return SCI_SUCCESS;
+ return status;
}
static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 tag)
@@ -3255,22 +3399,19 @@ static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 t
ireq->io_request_completion = NULL;
ireq->flags = 0;
ireq->num_sg_entries = 0;
- INIT_LIST_HEAD(&ireq->completed_node);
- INIT_LIST_HEAD(&ireq->dev_node);
- isci_request_change_state(ireq, allocated);
return ireq;
}
-static struct isci_request *isci_io_request_from_tag(struct isci_host *ihost,
- struct sas_task *task,
- u16 tag)
+struct isci_request *isci_io_request_from_tag(struct isci_host *ihost,
+ struct sas_task *task,
+ u16 tag)
{
struct isci_request *ireq;
ireq = isci_request_from_tag(ihost, tag);
ireq->ttype_ptr.io_task_ptr = task;
- ireq->ttype = io_task;
+ clear_bit(IREQ_TMF, &ireq->flags);
task->lldd_task = ireq;
return ireq;
@@ -3284,22 +3425,18 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost,
ireq = isci_request_from_tag(ihost, tag);
ireq->ttype_ptr.tmf_task_ptr = isci_tmf;
- ireq->ttype = tmf_task;
+ set_bit(IREQ_TMF, &ireq->flags);
return ireq;
}
int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev,
- struct sas_task *task, u16 tag)
+ struct sas_task *task, struct isci_request *ireq)
{
- enum sci_status status = SCI_FAILURE_UNSUPPORTED_PROTOCOL;
- struct isci_request *ireq;
+ enum sci_status status;
unsigned long flags;
int ret = 0;
- /* do common allocation and init of request object. */
- ireq = isci_io_request_from_tag(ihost, task, tag);
-
status = isci_io_request_build(ihost, ireq, idev);
if (status != SCI_SUCCESS) {
dev_dbg(&ihost->pdev->dev,
@@ -3341,26 +3478,15 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide
spin_unlock_irqrestore(&ihost->scic_lock, flags);
return status;
}
-
/* Either I/O started OK, or the core has signaled that
* the device needs a target reset.
- *
- * In either case, hold onto the I/O for later.
- *
- * Update it's status and add it to the list in the
- * remote device object.
*/
- list_add(&ireq->dev_node, &idev->reqs_in_process);
-
- if (status == SCI_SUCCESS) {
- isci_request_change_state(ireq, started);
- } else {
+ if (status != SCI_SUCCESS) {
/* The request did not really start in the
* hardware, so clear the request handle
* here so no terminations will be done.
*/
set_bit(IREQ_TERMINATED, &ireq->flags);
- isci_request_change_state(ireq, completed);
}
spin_unlock_irqrestore(&ihost->scic_lock, flags);
@@ -3377,8 +3503,7 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide
/* Cause this task to be scheduled in the SCSI error
* handler thread.
*/
- isci_execpath_callback(ihost, task,
- sas_task_abort);
+ sas_task_abort(task);
/* Change the status, since we are holding
* the I/O until it is managed by the SCSI
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h
index 7a1d5a9778eb..79ddfffbf73c 100644
--- a/drivers/scsi/isci/request.h
+++ b/drivers/scsi/isci/request.h
@@ -61,42 +61,12 @@
#include "scu_task_context.h"
/**
- * struct isci_request_status - This enum defines the possible states of an I/O
- * request.
- *
- *
- */
-enum isci_request_status {
- unallocated = 0x00,
- allocated = 0x01,
- started = 0x02,
- completed = 0x03,
- aborting = 0x04,
- aborted = 0x05,
- terminating = 0x06,
- dead = 0x07
-};
-
-enum task_type {
- io_task = 0,
- tmf_task = 1
-};
-
-enum sci_request_protocol {
- SCIC_NO_PROTOCOL,
- SCIC_SMP_PROTOCOL,
- SCIC_SSP_PROTOCOL,
- SCIC_STP_PROTOCOL
-}; /* XXX remove me, use sas_task.{dev|task_proto} instead */;
-
-/**
* isci_stp_request - extra request infrastructure to handle pio/atapi protocol
* @pio_len - number of bytes requested at PIO setup
* @status - pio setup ending status value to tell us if we need
* to wait for another fis or if the transfer is complete. Upon
* receipt of a d2h fis this will be the status field of that fis.
* @sgl - track pio transfer progress as we iterate through the sgl
- * @device_cdb_len - atapi device advertises it's transfer constraints at setup
*/
struct isci_stp_request {
u32 pio_len;
@@ -107,28 +77,24 @@ struct isci_stp_request {
u8 set;
u32 offset;
} sgl;
- u32 device_cdb_len;
};
struct isci_request {
- enum isci_request_status status;
#define IREQ_COMPLETE_IN_TARGET 0
#define IREQ_TERMINATED 1
#define IREQ_TMF 2
#define IREQ_ACTIVE 3
+ #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */
+ #define IREQ_TC_ABORT_POSTED 5
+ #define IREQ_ABORT_PATH_ACTIVE 6
+ #define IREQ_NO_AUTO_FREE_TAG 7 /* Set when being explicitly managed */
unsigned long flags;
/* XXX kill ttype and ttype_ptr, allocate full sas_task */
- enum task_type ttype;
union ttype_ptr_union {
struct sas_task *io_task_ptr; /* When ttype==io_task */
struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */
} ttype_ptr;
struct isci_host *isci_host;
- /* For use in the requests_to_{complete|abort} lists: */
- struct list_head completed_node;
- /* For use in the reqs_in_process list: */
- struct list_head dev_node;
- spinlock_t state_lock;
dma_addr_t request_daddr;
dma_addr_t zero_scatter_daddr;
unsigned int num_sg_entries;
@@ -148,7 +114,7 @@ struct isci_request {
struct isci_host *owning_controller;
struct isci_remote_device *target_device;
u16 io_tag;
- enum sci_request_protocol protocol;
+ enum sas_protocol protocol;
u32 scu_status; /* hardware result */
u32 sci_status; /* upper layer disposition */
u32 post_context;
@@ -174,9 +140,6 @@ struct isci_request {
};
} ssp;
struct {
- struct smp_resp rsp;
- } smp;
- struct {
struct isci_stp_request req;
struct host_to_dev_fis cmd;
struct dev_to_host_fis rsp;
@@ -193,112 +156,103 @@ static inline struct isci_request *to_ireq(struct isci_stp_request *stp_req)
}
/**
- * enum sci_base_request_states - This enumeration depicts all the states for
- * the common request state machine.
+ * enum sci_base_request_states - request state machine states
+ *
+ * @SCI_REQ_INIT: Simply the initial state for the base request state machine.
+ *
+ * @SCI_REQ_CONSTRUCTED: This state indicates that the request has been
+ * constructed. This state is entered from the INITIAL state.
+ *
+ * @SCI_REQ_STARTED: This state indicates that the request has been started.
+ * This state is entered from the CONSTRUCTED state.
+ *
+ * @SCI_REQ_STP_UDMA_WAIT_TC_COMP:
+ * @SCI_REQ_STP_UDMA_WAIT_D2H:
+ * @SCI_REQ_STP_NON_DATA_WAIT_H2D:
+ * @SCI_REQ_STP_NON_DATA_WAIT_D2H:
+ *
+ * @SCI_REQ_STP_PIO_WAIT_H2D: While in this state the IO request object is
+ * waiting for the TC completion notification for the H2D Register FIS
+ *
+ * @SCI_REQ_STP_PIO_WAIT_FRAME: While in this state the IO request object is
+ * waiting for either a PIO Setup FIS or a D2H register FIS. The type of frame
+ * received is based on the result of the prior frame and line conditions.
+ *
+ * @SCI_REQ_STP_PIO_DATA_IN: While in this state the IO request object is
+ * waiting for a DATA frame from the device.
+ *
+ * @SCI_REQ_STP_PIO_DATA_OUT: While in this state the IO request object is
+ * waiting to transmit the next data frame to the device.
+ *
+ * @SCI_REQ_ATAPI_WAIT_H2D: While in this state the IO request object is
+ * waiting for the TC completion notification for the H2D Register FIS
+ *
+ * @SCI_REQ_ATAPI_WAIT_PIO_SETUP: While in this state the IO request object is
+ * waiting for either a PIO Setup.
+ *
+ * @SCI_REQ_ATAPI_WAIT_D2H: The non-data IO transit to this state in this state
+ * after receiving TC completion. While in this state IO request object is
+ * waiting for D2H status frame as UF.
+ *
+ * @SCI_REQ_ATAPI_WAIT_TC_COMP: When transmitting raw frames hardware reports
+ * task context completion after every frame submission, so in the
+ * non-accelerated case we need to expect the completion for the "cdb" frame.
+ *
+ * @SCI_REQ_TASK_WAIT_TC_COMP: The AWAIT_TC_COMPLETION sub-state indicates that
+ * the started raw task management request is waiting for the transmission of
+ * the initial frame (i.e. command, task, etc.).
+ *
+ * @SCI_REQ_TASK_WAIT_TC_RESP: This sub-state indicates that the started task
+ * management request is waiting for the reception of an unsolicited frame
+ * (i.e. response IU).
*
+ * @SCI_REQ_SMP_WAIT_RESP: This sub-state indicates that the started task
+ * management request is waiting for the reception of an unsolicited frame
+ * (i.e. response IU).
*
+ * @SCI_REQ_SMP_WAIT_TC_COMP: The AWAIT_TC_COMPLETION sub-state indicates that
+ * the started SMP request is waiting for the transmission of the initial frame
+ * (i.e. command, task, etc.).
+ *
+ * @SCI_REQ_COMPLETED: This state indicates that the request has completed.
+ * This state is entered from the STARTED state. This state is entered from the
+ * ABORTING state.
+ *
+ * @SCI_REQ_ABORTING: This state indicates that the request is in the process
+ * of being terminated/aborted. This state is entered from the CONSTRUCTED
+ * state. This state is entered from the STARTED state.
+ *
+ * @SCI_REQ_FINAL: Simply the final state for the base request state machine.
*/
-enum sci_base_request_states {
- /*
- * Simply the initial state for the base request state machine.
- */
- SCI_REQ_INIT,
-
- /*
- * This state indicates that the request has been constructed.
- * This state is entered from the INITIAL state.
- */
- SCI_REQ_CONSTRUCTED,
-
- /*
- * This state indicates that the request has been started. This state
- * is entered from the CONSTRUCTED state.
- */
- SCI_REQ_STARTED,
-
- SCI_REQ_STP_UDMA_WAIT_TC_COMP,
- SCI_REQ_STP_UDMA_WAIT_D2H,
-
- SCI_REQ_STP_NON_DATA_WAIT_H2D,
- SCI_REQ_STP_NON_DATA_WAIT_D2H,
-
- SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED,
- SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG,
- SCI_REQ_STP_SOFT_RESET_WAIT_D2H,
-
- /*
- * While in this state the IO request object is waiting for the TC
- * completion notification for the H2D Register FIS
- */
- SCI_REQ_STP_PIO_WAIT_H2D,
-
- /*
- * While in this state the IO request object is waiting for either a
- * PIO Setup FIS or a D2H register FIS. The type of frame received is
- * based on the result of the prior frame and line conditions.
- */
- SCI_REQ_STP_PIO_WAIT_FRAME,
-
- /*
- * While in this state the IO request object is waiting for a DATA
- * frame from the device.
- */
- SCI_REQ_STP_PIO_DATA_IN,
-
- /*
- * While in this state the IO request object is waiting to transmit
- * the next data frame to the device.
- */
- SCI_REQ_STP_PIO_DATA_OUT,
-
- /*
- * The AWAIT_TC_COMPLETION sub-state indicates that the started raw
- * task management request is waiting for the transmission of the
- * initial frame (i.e. command, task, etc.).
- */
- SCI_REQ_TASK_WAIT_TC_COMP,
-
- /*
- * This sub-state indicates that the started task management request
- * is waiting for the reception of an unsolicited frame
- * (i.e. response IU).
- */
- SCI_REQ_TASK_WAIT_TC_RESP,
-
- /*
- * This sub-state indicates that the started task management request
- * is waiting for the reception of an unsolicited frame
- * (i.e. response IU).
- */
- SCI_REQ_SMP_WAIT_RESP,
-
- /*
- * The AWAIT_TC_COMPLETION sub-state indicates that the started SMP
- * request is waiting for the transmission of the initial frame
- * (i.e. command, task, etc.).
- */
- SCI_REQ_SMP_WAIT_TC_COMP,
-
- /*
- * This state indicates that the request has completed.
- * This state is entered from the STARTED state. This state is entered
- * from the ABORTING state.
- */
- SCI_REQ_COMPLETED,
-
- /*
- * This state indicates that the request is in the process of being
- * terminated/aborted.
- * This state is entered from the CONSTRUCTED state.
- * This state is entered from the STARTED state.
- */
- SCI_REQ_ABORTING,
-
- /*
- * Simply the final state for the base request state machine.
- */
- SCI_REQ_FINAL,
-};
+#define REQUEST_STATES {\
+ C(REQ_INIT),\
+ C(REQ_CONSTRUCTED),\
+ C(REQ_STARTED),\
+ C(REQ_STP_UDMA_WAIT_TC_COMP),\
+ C(REQ_STP_UDMA_WAIT_D2H),\
+ C(REQ_STP_NON_DATA_WAIT_H2D),\
+ C(REQ_STP_NON_DATA_WAIT_D2H),\
+ C(REQ_STP_PIO_WAIT_H2D),\
+ C(REQ_STP_PIO_WAIT_FRAME),\
+ C(REQ_STP_PIO_DATA_IN),\
+ C(REQ_STP_PIO_DATA_OUT),\
+ C(REQ_ATAPI_WAIT_H2D),\
+ C(REQ_ATAPI_WAIT_PIO_SETUP),\
+ C(REQ_ATAPI_WAIT_D2H),\
+ C(REQ_ATAPI_WAIT_TC_COMP),\
+ C(REQ_TASK_WAIT_TC_COMP),\
+ C(REQ_TASK_WAIT_TC_RESP),\
+ C(REQ_SMP_WAIT_RESP),\
+ C(REQ_SMP_WAIT_TC_COMP),\
+ C(REQ_COMPLETED),\
+ C(REQ_ABORTING),\
+ C(REQ_FINAL),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_base_request_states REQUEST_STATES;
+#undef C
+const char *req_state_name(enum sci_base_request_states state);
enum sci_status sci_request_start(struct isci_request *ireq);
enum sci_status sci_io_request_terminate(struct isci_request *ireq);
@@ -329,92 +283,6 @@ sci_io_request_get_dma_addr(struct isci_request *ireq, void *virt_addr)
return ireq->request_daddr + (requested_addr - base_addr);
}
-/**
- * isci_request_change_state() - This function sets the status of the request
- * object.
- * @request: This parameter points to the isci_request object
- * @status: This Parameter is the new status of the object
- *
- */
-static inline enum isci_request_status
-isci_request_change_state(struct isci_request *isci_request,
- enum isci_request_status status)
-{
- enum isci_request_status old_state;
- unsigned long flags;
-
- dev_dbg(&isci_request->isci_host->pdev->dev,
- "%s: isci_request = %p, state = 0x%x\n",
- __func__,
- isci_request,
- status);
-
- BUG_ON(isci_request == NULL);
-
- spin_lock_irqsave(&isci_request->state_lock, flags);
- old_state = isci_request->status;
- isci_request->status = status;
- spin_unlock_irqrestore(&isci_request->state_lock, flags);
-
- return old_state;
-}
-
-/**
- * isci_request_change_started_to_newstate() - This function sets the status of
- * the request object.
- * @request: This parameter points to the isci_request object
- * @status: This Parameter is the new status of the object
- *
- * state previous to any change.
- */
-static inline enum isci_request_status
-isci_request_change_started_to_newstate(struct isci_request *isci_request,
- struct completion *completion_ptr,
- enum isci_request_status newstate)
-{
- enum isci_request_status old_state;
- unsigned long flags;
-
- spin_lock_irqsave(&isci_request->state_lock, flags);
-
- old_state = isci_request->status;
-
- if (old_state == started || old_state == aborting) {
- BUG_ON(isci_request->io_request_completion != NULL);
-
- isci_request->io_request_completion = completion_ptr;
- isci_request->status = newstate;
- }
-
- spin_unlock_irqrestore(&isci_request->state_lock, flags);
-
- dev_dbg(&isci_request->isci_host->pdev->dev,
- "%s: isci_request = %p, old_state = 0x%x\n",
- __func__,
- isci_request,
- old_state);
-
- return old_state;
-}
-
-/**
- * isci_request_change_started_to_aborted() - This function sets the status of
- * the request object.
- * @request: This parameter points to the isci_request object
- * @completion_ptr: This parameter is saved as the kernel completion structure
- * signalled when the old request completes.
- *
- * state previous to any change.
- */
-static inline enum isci_request_status
-isci_request_change_started_to_aborted(struct isci_request *isci_request,
- struct completion *completion_ptr)
-{
- return isci_request_change_started_to_newstate(isci_request,
- completion_ptr,
- aborted);
-}
-
#define isci_request_access_task(req) ((req)->ttype_ptr.io_task_ptr)
#define isci_request_access_tmf(req) ((req)->ttype_ptr.tmf_task_ptr)
@@ -423,18 +291,16 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost,
struct isci_tmf *isci_tmf,
u16 tag);
int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev,
- struct sas_task *task, u16 tag);
-void isci_terminate_pending_requests(struct isci_host *ihost,
- struct isci_remote_device *idev);
+ struct sas_task *task, struct isci_request *ireq);
+struct isci_request *isci_io_request_from_tag(struct isci_host *ihost,
+ struct sas_task *task,
+ u16 tag);
enum sci_status
sci_task_request_construct(struct isci_host *ihost,
struct isci_remote_device *idev,
u16 io_tag,
struct isci_request *ireq);
-enum sci_status
-sci_task_request_construct_ssp(struct isci_request *ireq);
-enum sci_status
-sci_task_request_construct_sata(struct isci_request *ireq);
+void sci_task_request_construct_ssp(struct isci_request *ireq);
void sci_smp_request_copy_response(struct isci_request *ireq);
static inline int isci_task_is_ncq_recovery(struct sas_task *task)
@@ -444,5 +310,4 @@ static inline int isci_task_is_ncq_recovery(struct sas_task *task)
task->ata_task.fis.lbal == ATA_LOG_SATA_NCQ);
}
-
#endif /* !defined(_ISCI_REQUEST_H_) */
diff --git a/drivers/scsi/isci/sas.h b/drivers/scsi/isci/sas.h
index 462b15174d3f..15d8f3631ab7 100644
--- a/drivers/scsi/isci/sas.h
+++ b/drivers/scsi/isci/sas.h
@@ -201,11 +201,9 @@ struct smp_req {
u8 func; /* byte 1 */
u8 alloc_resp_len; /* byte 2 */
u8 req_len; /* byte 3 */
- u8 req_data[0];
+ u8 req_data[];
} __packed;
-#define SMP_RESP_HDR_SZ 4
-
/*
* struct sci_sas_address - This structure depicts how a SAS address is
* represented by SCI.
diff --git a/drivers/scsi/isci/scu_completion_codes.h b/drivers/scsi/isci/scu_completion_codes.h
index c8b329c695f9..071cb74a211c 100644
--- a/drivers/scsi/isci/scu_completion_codes.h
+++ b/drivers/scsi/isci/scu_completion_codes.h
@@ -224,6 +224,7 @@
* 32-bit value like we want, each immediate value must be cast to a u32.
*/
#define SCU_TASK_DONE_GOOD ((u32)0x00)
+#define SCU_TASK_DONE_TX_RAW_CMD_ERR ((u32)0x08)
#define SCU_TASK_DONE_CRC_ERR ((u32)0x14)
#define SCU_TASK_DONE_CHECK_RESPONSE ((u32)0x14)
#define SCU_TASK_DONE_GEN_RESPONSE ((u32)0x15)
@@ -237,6 +238,7 @@
#define SCU_TASK_DONE_LL_LF_TERM ((u32)0x1A)
#define SCU_TASK_DONE_DATA_LEN_ERR ((u32)0x1A)
#define SCU_TASK_DONE_LL_CL_TERM ((u32)0x1B)
+#define SCU_TASK_DONE_BREAK_RCVD ((u32)0x1B)
#define SCU_TASK_DONE_LL_ABORT_ERR ((u32)0x1B)
#define SCU_TASK_DONE_SEQ_INV_TYPE ((u32)0x1C)
#define SCU_TASK_DONE_UNEXP_XR ((u32)0x1C)
diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h
index 7df87d923285..582d22d54689 100644
--- a/drivers/scsi/isci/scu_task_context.h
+++ b/drivers/scsi/isci/scu_task_context.h
@@ -845,7 +845,7 @@ struct scu_task_context {
/**
* This field is used by the SCU TL to determine when to take a snapshot when
- * tranmitting read data frames.
+ * transmitting read data frames.
* - 0x00 The entire IO
* - 0x01 32k
* - 0x02 64k
@@ -866,9 +866,9 @@ struct scu_task_context {
struct transport_snapshot snapshot; /* read only set to 0 */
/* OFFSET 0x5C */
- u32 block_protection_enable:1;
- u32 block_size:2;
- u32 block_protection_function:2;
+ u32 blk_prot_en:1;
+ u32 blk_sz:2;
+ u32 blk_prot_func:2;
u32 reserved_5C_0:9;
u32 active_sgl_element:2; /* read only set to 0 */
u32 sgl_exhausted:1; /* read only set to 0 */
@@ -896,33 +896,56 @@ struct scu_task_context {
u32 reserved_C4_CC[3];
/* OFFSET 0xD0 */
- u32 intermediate_crc_value:16;
- u32 initial_crc_seed:16;
+ u32 interm_crc_val:16;
+ u32 init_crc_seed:16;
/* OFFSET 0xD4 */
- u32 application_tag_for_verify:16;
- u32 application_tag_for_generate:16;
+ u32 app_tag_verify:16;
+ u32 app_tag_gen:16;
/* OFFSET 0xD8 */
- u32 reference_tag_seed_for_verify_function;
+ u32 ref_tag_seed_verify;
/* OFFSET 0xDC */
- u32 reserved_DC;
+ u32 UD_bytes_immed_val:13;
+ u32 reserved_DC_0:3;
+ u32 DIF_bytes_immed_val:4;
+ u32 reserved_DC_1:12;
/* OFFSET 0xE0 */
- u32 reserved_E0_0:16;
- u32 application_tag_mask_for_generate:16;
+ u32 bgc_blk_sz:13;
+ u32 reserved_E0_0:3;
+ u32 app_tag_gen_mask:16;
/* OFFSET 0xE4 */
- u32 block_protection_control:16;
- u32 application_tag_mask_for_verify:16;
+ union {
+ u16 bgctl;
+ struct {
+ u16 crc_verify:1;
+ u16 app_tag_chk:1;
+ u16 ref_tag_chk:1;
+ u16 op:2;
+ u16 legacy:1;
+ u16 invert_crc_seed:1;
+ u16 ref_tag_gen:1;
+ u16 fixed_ref_tag:1;
+ u16 invert_crc:1;
+ u16 app_ref_f_detect:1;
+ u16 uninit_dif_check_err:1;
+ u16 uninit_dif_bypass:1;
+ u16 app_f_detect:1;
+ u16 reserved_0:2;
+ } bgctl_f;
+ };
+
+ u16 app_tag_verify_mask;
/* OFFSET 0xE8 */
- u32 block_protection_error:8;
+ u32 blk_guard_err:8;
u32 reserved_E8_0:24;
/* OFFSET 0xEC */
- u32 reference_tag_seed_for_verify;
+ u32 ref_tag_seed_gen;
/* OFFSET 0xF0 */
u32 intermediate_crc_valid_snapshot:16;
@@ -937,6 +960,6 @@ struct scu_task_context {
/* OFFSET 0xFC */
u32 reference_tag_seed_for_generate_function_snapshot;
-};
+} __packed;
#endif /* _SCU_TASK_CONTEXT_H_ */
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index d6bcdd013dc9..3a25b1a2c52d 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -78,56 +78,24 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task,
enum exec_status status)
{
- enum isci_completion_selection disposition;
-
- disposition = isci_perform_normal_io_completion;
- disposition = isci_task_set_completion_status(task, response, status,
- disposition);
-
- /* Tasks aborted specifically by a call to the lldd_abort_task
- * function should not be completed to the host in the regular path.
- */
- switch (disposition) {
- case isci_perform_normal_io_completion:
- /* Normal notification (task_done) */
- dev_dbg(&ihost->pdev->dev,
- "%s: Normal - task = %p, response=%d, "
- "status=%d\n",
- __func__, task, response, status);
-
- task->lldd_task = NULL;
+ unsigned long flags;
- isci_execpath_callback(ihost, task, task->task_done);
- break;
+ /* Normal notification (task_done) */
+ dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n",
+ __func__, task, response, status);
- case isci_perform_aborted_io_completion:
- /*
- * No notification because this request is already in the
- * abort path.
- */
- dev_dbg(&ihost->pdev->dev,
- "%s: Aborted - task = %p, response=%d, "
- "status=%d\n",
- __func__, task, response, status);
- break;
+ spin_lock_irqsave(&task->task_state_lock, flags);
- case isci_perform_error_io_completion:
- /* Use sas_task_abort */
- dev_dbg(&ihost->pdev->dev,
- "%s: Error - task = %p, response=%d, "
- "status=%d\n",
- __func__, task, response, status);
+ task->task_status.resp = response;
+ task->task_status.stat = status;
- isci_execpath_callback(ihost, task, sas_task_abort);
- break;
+ /* Normal notification (task_done) */
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ task->lldd_task = NULL;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
- default:
- dev_dbg(&ihost->pdev->dev,
- "%s: isci task notification default case!",
- __func__);
- sas_task_abort(task);
- break;
- }
+ task->task_done(task);
}
#define for_each_sas_task(num, task) \
@@ -148,134 +116,97 @@ static inline int isci_device_io_ready(struct isci_remote_device *idev,
* functions. This function is called by libsas to send a task down to
* hardware.
* @task: This parameter specifies the SAS task to send.
- * @num: This parameter specifies the number of tasks to queue.
* @gfp_flags: This parameter specifies the context of this call.
*
* status, zero indicates success.
*/
-int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
+int isci_task_execute_task(struct sas_task *task, gfp_t gfp_flags)
{
struct isci_host *ihost = dev_to_ihost(task->dev);
struct isci_remote_device *idev;
unsigned long flags;
+ enum sci_status status = SCI_FAILURE;
bool io_ready;
u16 tag;
- dev_dbg(&ihost->pdev->dev, "%s: num=%d\n", __func__, num);
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ idev = isci_lookup_device(task->dev);
+ io_ready = isci_device_io_ready(idev, task);
+ tag = isci_alloc_tag(ihost);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- for_each_sas_task(num, task) {
- enum sci_status status = SCI_FAILURE;
+ dev_dbg(&ihost->pdev->dev,
+ "task: %p, dev: %p idev: %p:%#lx cmd = %p\n",
+ task, task->dev, idev, idev ? idev->flags : 0,
+ task->uldd_task);
- spin_lock_irqsave(&ihost->scic_lock, flags);
- idev = isci_lookup_device(task->dev);
- io_ready = isci_device_io_ready(idev, task);
- tag = isci_alloc_tag(ihost);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (!idev) {
+ isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED,
+ SAS_DEVICE_UNKNOWN);
+ } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) {
+ /* Indicate QUEUE_FULL so that the scsi midlayer
+ * retries.
+ */
+ isci_task_refuse(ihost, task, SAS_TASK_COMPLETE,
+ SAS_QUEUE_FULL);
+ } else {
+ /* There is a device and it's ready for I/O. */
+ spin_lock_irqsave(&task->task_state_lock, flags);
- dev_dbg(&ihost->pdev->dev,
- "task: %p, num: %d dev: %p idev: %p:%#lx cmd = %p\n",
- task, num, task->dev, idev, idev ? idev->flags : 0,
- task->uldd_task);
-
- if (!idev) {
- isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED,
- SAS_DEVICE_UNKNOWN);
- } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) {
- /* Indicate QUEUE_FULL so that the scsi midlayer
- * retries.
- */
- isci_task_refuse(ihost, task, SAS_TASK_COMPLETE,
- SAS_QUEUE_FULL);
+ if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+ /* The I/O was aborted. */
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ isci_task_refuse(ihost, task,
+ SAS_TASK_UNDELIVERED,
+ SAS_SAM_STAT_TASK_ABORTED);
} else {
- /* There is a device and it's ready for I/O. */
- spin_lock_irqsave(&task->task_state_lock, flags);
-
- if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
- /* The I/O was aborted. */
- spin_unlock_irqrestore(&task->task_state_lock,
- flags);
-
- isci_task_refuse(ihost, task,
- SAS_TASK_UNDELIVERED,
- SAM_STAT_TASK_ABORTED);
- } else {
- task->task_state_flags |= SAS_TASK_AT_INITIATOR;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- /* build and send the request. */
- status = isci_request_execute(ihost, idev, task, tag);
-
- if (status != SCI_SUCCESS) {
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- /* Did not really start this command. */
- task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- /* Indicate QUEUE_FULL so that the scsi
- * midlayer retries. if the request
- * failed for remote device reasons,
- * it gets returned as
- * SAS_TASK_UNDELIVERED next time
- * through.
- */
+ struct isci_request *ireq;
+
+ /* do common allocation and init of request object. */
+ ireq = isci_io_request_from_tag(ihost, task, tag);
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ /* build and send the request. */
+ /* do common allocation and init of request object. */
+ status = isci_request_execute(ihost, idev, task, ireq);
+
+ if (status != SCI_SUCCESS) {
+ if (test_bit(IDEV_GONE, &idev->flags)) {
+ /* Indicate that the device
+ * is gone.
+ */
isci_task_refuse(ihost, task,
- SAS_TASK_COMPLETE,
- SAS_QUEUE_FULL);
+ SAS_TASK_UNDELIVERED,
+ SAS_DEVICE_UNKNOWN);
+ } else {
+ /* Indicate QUEUE_FULL so that
+ * the scsi midlayer retries.
+ * If the request failed for
+ * remote device reasons, it
+ * gets returned as
+ * SAS_TASK_UNDELIVERED next
+ * time through.
+ */
+ isci_task_refuse(ihost, task,
+ SAS_TASK_COMPLETE,
+ SAS_QUEUE_FULL);
}
}
}
- if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) {
- spin_lock_irqsave(&ihost->scic_lock, flags);
- /* command never hit the device, so just free
- * the tci and skip the sequence increment
- */
- isci_tci_free(ihost, ISCI_TAG_TCI(tag));
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
- }
- isci_put_device(idev);
}
- return 0;
-}
-
-static enum sci_status isci_sata_management_task_request_build(struct isci_request *ireq)
-{
- struct isci_tmf *isci_tmf;
- enum sci_status status;
- if (tmf_task != ireq->ttype)
- return SCI_FAILURE;
-
- isci_tmf = isci_request_access_tmf(ireq);
-
- switch (isci_tmf->tmf_code) {
-
- case isci_tmf_sata_srst_high:
- case isci_tmf_sata_srst_low: {
- struct host_to_dev_fis *fis = &ireq->stp.cmd;
-
- memset(fis, 0, sizeof(*fis));
-
- fis->fis_type = 0x27;
- fis->flags &= ~0x80;
- fis->flags &= 0xF0;
- if (isci_tmf->tmf_code == isci_tmf_sata_srst_high)
- fis->control |= ATA_SRST;
- else
- fis->control &= ~ATA_SRST;
- break;
- }
- /* other management commnd go here... */
- default:
- return SCI_FAILURE;
+ if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) {
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ /* command never hit the device, so just free
+ * the tci and skip the sequence increment
+ */
+ isci_tci_free(ihost, ISCI_TAG_TCI(tag));
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
}
- /* core builds the protocol specific request
- * based on the h2d fis.
- */
- status = sci_task_request_construct_sata(ireq);
-
- return status;
+ isci_put_device(idev);
+ return 0;
}
static struct isci_request *isci_task_request_build(struct isci_host *ihost,
@@ -310,20 +241,11 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost,
}
/* XXX convert to get this from task->tproto like other drivers */
- if (dev->dev_type == SAS_END_DEV) {
+ if (dev->dev_type == SAS_END_DEVICE) {
isci_tmf->proto = SAS_PROTOCOL_SSP;
- status = sci_task_request_construct_ssp(ireq);
- if (status != SCI_SUCCESS)
- return NULL;
+ sci_task_request_construct_ssp(ireq);
}
- if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
- isci_tmf->proto = SAS_PROTOCOL_SATA;
- status = isci_sata_management_task_request_build(ireq);
-
- if (status != SCI_SUCCESS)
- return NULL;
- }
return ireq;
}
@@ -332,7 +254,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
struct isci_tmf *tmf, unsigned long timeout_ms)
{
DECLARE_COMPLETION_ONSTACK(completion);
- enum sci_task_status status = SCI_TASK_FAILURE;
+ enum sci_status status = SCI_FAILURE;
struct isci_request *ireq;
int ret = TMF_RESP_FUNC_FAILED;
unsigned long flags;
@@ -364,6 +286,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
/* Assign the pointer to the TMF's completion kernel wait structure. */
tmf->complete = &completion;
+ tmf->status = SCI_FAILURE_TIMEOUT;
ireq = isci_task_request_build(ihost, idev, tag, tmf);
if (!ireq)
@@ -374,7 +297,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
/* start the TMF io. */
status = sci_controller_start_task(ihost, idev, ireq);
- if (status != SCI_TASK_SUCCESS) {
+ if (status != SCI_SUCCESS) {
dev_dbg(&ihost->pdev->dev,
"%s: start_io failed - status = 0x%x, request = %p\n",
__func__,
@@ -383,37 +306,23 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
spin_unlock_irqrestore(&ihost->scic_lock, flags);
goto err_tci;
}
-
- if (tmf->cb_state_func != NULL)
- tmf->cb_state_func(isci_tmf_started, tmf, tmf->cb_data);
-
- isci_request_change_state(ireq, started);
-
- /* add the request to the remote device request list. */
- list_add(&ireq->dev_node, &idev->reqs_in_process);
-
spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ /* The RNC must be unsuspended before the TMF can get a response. */
+ isci_remote_device_resume_from_abort(ihost, idev);
+
/* Wait for the TMF to complete, or a timeout. */
timeleft = wait_for_completion_timeout(&completion,
msecs_to_jiffies(timeout_ms));
if (timeleft == 0) {
- spin_lock_irqsave(&ihost->scic_lock, flags);
-
- if (tmf->cb_state_func != NULL)
- tmf->cb_state_func(isci_tmf_timed_out, tmf, tmf->cb_data);
-
- sci_controller_terminate_request(ihost,
- idev,
- ireq);
-
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- wait_for_completion(tmf->complete);
+ /* The TMF did not complete - this could be because
+ * of an unplug. Terminate the TMF request now.
+ */
+ isci_remote_device_suspend_terminate(ihost, idev, ireq);
}
- isci_print_tmf(tmf);
+ isci_print_tmf(ihost, tmf);
if (tmf->status == SCI_SUCCESS)
ret = TMF_RESP_FUNC_COMPLETE;
@@ -442,366 +351,21 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
}
static void isci_task_build_tmf(struct isci_tmf *tmf,
- enum isci_tmf_function_codes code,
- void (*tmf_sent_cb)(enum isci_tmf_cb_state,
- struct isci_tmf *,
- void *),
- void *cb_data)
+ enum isci_tmf_function_codes code)
{
memset(tmf, 0, sizeof(*tmf));
-
- tmf->tmf_code = code;
- tmf->cb_state_func = tmf_sent_cb;
- tmf->cb_data = cb_data;
+ tmf->tmf_code = code;
}
static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf,
enum isci_tmf_function_codes code,
- void (*tmf_sent_cb)(enum isci_tmf_cb_state,
- struct isci_tmf *,
- void *),
struct isci_request *old_request)
{
- isci_task_build_tmf(tmf, code, tmf_sent_cb, old_request);
+ isci_task_build_tmf(tmf, code);
tmf->io_tag = old_request->io_tag;
}
-/**
- * isci_task_validate_request_to_abort() - This function checks the given I/O
- * against the "started" state. If the request is still "started", it's
- * state is changed to aborted. NOTE: isci_host->scic_lock MUST BE HELD
- * BEFORE CALLING THIS FUNCTION.
- * @isci_request: This parameter specifies the request object to control.
- * @isci_host: This parameter specifies the ISCI host object
- * @isci_device: This is the device to which the request is pending.
- * @aborted_io_completion: This is a completion structure that will be added to
- * the request in case it is changed to aborting; this completion is
- * triggered when the request is fully completed.
- *
- * Either "started" on successful change of the task status to "aborted", or
- * "unallocated" if the task cannot be controlled.
- */
-static enum isci_request_status isci_task_validate_request_to_abort(
- struct isci_request *isci_request,
- struct isci_host *isci_host,
- struct isci_remote_device *isci_device,
- struct completion *aborted_io_completion)
-{
- enum isci_request_status old_state = unallocated;
-
- /* Only abort the task if it's in the
- * device's request_in_process list
- */
- if (isci_request && !list_empty(&isci_request->dev_node)) {
- old_state = isci_request_change_started_to_aborted(
- isci_request, aborted_io_completion);
-
- }
-
- return old_state;
-}
-
-/**
-* isci_request_cleanup_completed_loiterer() - This function will take care of
-* the final cleanup on any request which has been explicitly terminated.
-* @isci_host: This parameter specifies the ISCI host object
-* @isci_device: This is the device to which the request is pending.
-* @isci_request: This parameter specifies the terminated request object.
-* @task: This parameter is the libsas I/O request.
-*/
-static void isci_request_cleanup_completed_loiterer(
- struct isci_host *isci_host,
- struct isci_remote_device *isci_device,
- struct isci_request *isci_request,
- struct sas_task *task)
-{
- unsigned long flags;
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device=%p, request=%p, task=%p\n",
- __func__, isci_device, isci_request, task);
-
- if (task != NULL) {
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- task->lldd_task = NULL;
-
- task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
-
- isci_set_task_doneflags(task);
-
- /* If this task is not in the abort path, call task_done. */
- if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- task->task_done(task);
- } else
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- }
-
- if (isci_request != NULL) {
- spin_lock_irqsave(&isci_host->scic_lock, flags);
- list_del_init(&isci_request->dev_node);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
- }
-}
-
-/**
- * isci_terminate_request_core() - This function will terminate the given
- * request, and wait for it to complete. This function must only be called
- * from a thread that can wait. Note that the request is terminated and
- * completed (back to the host, if started there).
- * @ihost: This SCU.
- * @idev: The target.
- * @isci_request: The I/O request to be terminated.
- *
- */
-static void isci_terminate_request_core(struct isci_host *ihost,
- struct isci_remote_device *idev,
- struct isci_request *isci_request)
-{
- enum sci_status status = SCI_SUCCESS;
- bool was_terminated = false;
- bool needs_cleanup_handling = false;
- enum isci_request_status request_status;
- unsigned long flags;
- unsigned long termination_completed = 1;
- struct completion *io_request_completion;
- struct sas_task *task;
-
- dev_dbg(&ihost->pdev->dev,
- "%s: device = %p; request = %p\n",
- __func__, idev, isci_request);
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
-
- io_request_completion = isci_request->io_request_completion;
-
- task = (isci_request->ttype == io_task)
- ? isci_request_access_task(isci_request)
- : NULL;
-
- /* Note that we are not going to control
- * the target to abort the request.
- */
- set_bit(IREQ_COMPLETE_IN_TARGET, &isci_request->flags);
-
- /* Make sure the request wasn't just sitting around signalling
- * device condition (if the request handle is NULL, then the
- * request completed but needed additional handling here).
- */
- if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) {
- was_terminated = true;
- needs_cleanup_handling = true;
- status = sci_controller_terminate_request(ihost,
- idev,
- isci_request);
- }
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- /*
- * The only time the request to terminate will
- * fail is when the io request is completed and
- * being aborted.
- */
- if (status != SCI_SUCCESS) {
- dev_dbg(&ihost->pdev->dev,
- "%s: sci_controller_terminate_request"
- " returned = 0x%x\n",
- __func__, status);
-
- isci_request->io_request_completion = NULL;
-
- } else {
- if (was_terminated) {
- dev_dbg(&ihost->pdev->dev,
- "%s: before completion wait (%p/%p)\n",
- __func__, isci_request, io_request_completion);
-
- /* Wait here for the request to complete. */
- #define TERMINATION_TIMEOUT_MSEC 500
- termination_completed
- = wait_for_completion_timeout(
- io_request_completion,
- msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC));
-
- if (!termination_completed) {
-
- /* The request to terminate has timed out. */
- spin_lock_irqsave(&ihost->scic_lock,
- flags);
-
- /* Check for state changes. */
- if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) {
-
- /* The best we can do is to have the
- * request die a silent death if it
- * ever really completes.
- *
- * Set the request state to "dead",
- * and clear the task pointer so that
- * an actual completion event callback
- * doesn't do anything.
- */
- isci_request->status = dead;
- isci_request->io_request_completion
- = NULL;
-
- if (isci_request->ttype == io_task) {
-
- /* Break links with the
- * sas_task.
- */
- isci_request->ttype_ptr.io_task_ptr
- = NULL;
- }
- } else
- termination_completed = 1;
-
- spin_unlock_irqrestore(&ihost->scic_lock,
- flags);
-
- if (!termination_completed) {
-
- dev_dbg(&ihost->pdev->dev,
- "%s: *** Timeout waiting for "
- "termination(%p/%p)\n",
- __func__, io_request_completion,
- isci_request);
-
- /* The request can no longer be referenced
- * safely since it may go away if the
- * termination every really does complete.
- */
- isci_request = NULL;
- }
- }
- if (termination_completed)
- dev_dbg(&ihost->pdev->dev,
- "%s: after completion wait (%p/%p)\n",
- __func__, isci_request, io_request_completion);
- }
-
- if (termination_completed) {
-
- isci_request->io_request_completion = NULL;
-
- /* Peek at the status of the request. This will tell
- * us if there was special handling on the request such that it
- * needs to be detached and freed here.
- */
- spin_lock_irqsave(&isci_request->state_lock, flags);
- request_status = isci_request->status;
-
- if ((isci_request->ttype == io_task) /* TMFs are in their own thread */
- && ((request_status == aborted)
- || (request_status == aborting)
- || (request_status == terminating)
- || (request_status == completed)
- || (request_status == dead)
- )
- ) {
-
- /* The completion routine won't free a request in
- * the aborted/aborting/etc. states, so we do
- * it here.
- */
- needs_cleanup_handling = true;
- }
- spin_unlock_irqrestore(&isci_request->state_lock, flags);
-
- }
- if (needs_cleanup_handling)
- isci_request_cleanup_completed_loiterer(
- ihost, idev, isci_request, task);
- }
-}
-
-/**
- * isci_terminate_pending_requests() - This function will change the all of the
- * requests on the given device's state to "aborting", will terminate the
- * requests, and wait for them to complete. This function must only be
- * called from a thread that can wait. Note that the requests are all
- * terminated and completed (back to the host, if started there).
- * @isci_host: This parameter specifies SCU.
- * @idev: This parameter specifies the target.
- *
- */
-void isci_terminate_pending_requests(struct isci_host *ihost,
- struct isci_remote_device *idev)
-{
- struct completion request_completion;
- enum isci_request_status old_state;
- unsigned long flags;
- LIST_HEAD(list);
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
- list_splice_init(&idev->reqs_in_process, &list);
-
- /* assumes that isci_terminate_request_core deletes from the list */
- while (!list_empty(&list)) {
- struct isci_request *ireq = list_entry(list.next, typeof(*ireq), dev_node);
-
- /* Change state to "terminating" if it is currently
- * "started".
- */
- old_state = isci_request_change_started_to_newstate(ireq,
- &request_completion,
- terminating);
- switch (old_state) {
- case started:
- case completed:
- case aborting:
- break;
- default:
- /* termination in progress, or otherwise dispositioned.
- * We know the request was on 'list' so should be safe
- * to move it back to reqs_in_process
- */
- list_move(&ireq->dev_node, &idev->reqs_in_process);
- ireq = NULL;
- break;
- }
-
- if (!ireq)
- continue;
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- init_completion(&request_completion);
-
- dev_dbg(&ihost->pdev->dev,
- "%s: idev=%p request=%p; task=%p old_state=%d\n",
- __func__, idev, ireq,
- ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL,
- old_state);
-
- /* If the old_state is started:
- * This request was not already being aborted. If it had been,
- * then the aborting I/O (ie. the TMF request) would not be in
- * the aborting state, and thus would be terminated here. Note
- * that since the TMF completion's call to the kernel function
- * "complete()" does not happen until the pending I/O request
- * terminate fully completes, we do not have to implement a
- * special wait here for already aborting requests - the
- * termination of the TMF request will force the request
- * to finish it's already started terminate.
- *
- * If old_state == completed:
- * This request completed from the SCU hardware perspective
- * and now just needs cleaning up in terms of freeing the
- * request and potentially calling up to libsas.
- *
- * If old_state == aborting:
- * This request has already gone through a TMF timeout, but may
- * not have been terminated; needs cleaning up at least.
- */
- isci_terminate_request_core(ihost, idev, ireq);
- spin_lock_irqsave(&ihost->scic_lock, flags);
- }
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-}
-
-/**
+/*
* isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain
* Template functions.
* @lun: This parameter specifies the lun to be reset.
@@ -824,7 +388,7 @@ static int isci_task_send_lu_reset_sas(
* value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or
* was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED").
*/
- isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset, NULL, NULL);
+ isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset);
#define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS);
@@ -841,85 +405,46 @@ static int isci_task_send_lu_reset_sas(
return ret;
}
-static int isci_task_send_lu_reset_sata(struct isci_host *ihost,
- struct isci_remote_device *idev, u8 *lun)
+int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
{
- int ret = TMF_RESP_FUNC_FAILED;
- struct isci_tmf tmf;
-
- /* Send the soft reset to the target */
- #define ISCI_SRST_TIMEOUT_MS 25000 /* 25 second timeout. */
- isci_task_build_tmf(&tmf, isci_tmf_sata_srst_high, NULL, NULL);
-
- ret = isci_task_execute_tmf(ihost, idev, &tmf, ISCI_SRST_TIMEOUT_MS);
-
- if (ret != TMF_RESP_FUNC_COMPLETE) {
- dev_dbg(&ihost->pdev->dev,
- "%s: Assert SRST failed (%p) = %x",
- __func__, idev, ret);
-
- /* Return the failure so that the LUN reset is escalated
- * to a target reset.
- */
- }
- return ret;
-}
-
-/**
- * isci_task_lu_reset() - This function is one of the SAS Domain Template
- * functions. This is one of the Task Management functoins called by libsas,
- * to reset the given lun. Note the assumption that while this call is
- * executing, no I/O will be sent by the host to the device.
- * @lun: This parameter specifies the lun to be reset.
- *
- * status, zero indicates success.
- */
-int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun)
-{
- struct isci_host *isci_host = dev_to_ihost(domain_device);
- struct isci_remote_device *isci_device;
+ struct isci_host *ihost = dev_to_ihost(dev);
+ struct isci_remote_device *idev;
unsigned long flags;
- int ret;
+ int ret = TMF_RESP_FUNC_COMPLETE;
- spin_lock_irqsave(&isci_host->scic_lock, flags);
- isci_device = isci_lookup_device(domain_device);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ idev = isci_get_device(dev->lldd_dev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
+ dev_dbg(&ihost->pdev->dev,
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
- __func__, domain_device, isci_host, isci_device);
+ __func__, dev, ihost, idev);
- if (isci_device)
- set_bit(IDEV_EH, &isci_device->flags);
+ if (!idev) {
+ /* If the device is gone, escalate to I_T_Nexus_Reset. */
+ dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__);
- /* If there is a device reset pending on any request in the
- * device's list, fail this LUN reset request in order to
- * escalate to the device reset.
- */
- if (!isci_device ||
- isci_device_is_reset_pending(isci_host, isci_device)) {
- dev_dbg(&isci_host->pdev->dev,
- "%s: No dev (%p), or "
- "RESET PENDING: domain_device=%p\n",
- __func__, isci_device, domain_device);
ret = TMF_RESP_FUNC_FAILED;
goto out;
}
- /* Send the task management part of the reset. */
- if (sas_protocol_ata(domain_device->tproto)) {
- ret = isci_task_send_lu_reset_sata(isci_host, isci_device, lun);
- } else
- ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun);
-
- /* If the LUN reset worked, all the I/O can now be terminated. */
- if (ret == TMF_RESP_FUNC_COMPLETE)
- /* Terminate all I/O now. */
- isci_terminate_pending_requests(isci_host,
- isci_device);
-
+ /* Suspend the RNC, kill all TCs */
+ if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
+ != SCI_SUCCESS) {
+ /* The suspend/terminate only fails if isci_get_device fails */
+ ret = TMF_RESP_FUNC_FAILED;
+ goto out;
+ }
+ /* All pending I/Os have been terminated and cleaned up. */
+ if (!test_bit(IDEV_GONE, &idev->flags)) {
+ if (dev_is_sata(dev))
+ sas_ata_schedule_reset(dev);
+ else
+ /* Send the task management part of the reset. */
+ ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
+ }
out:
- isci_put_device(isci_device);
+ isci_put_device(idev);
return ret;
}
@@ -940,63 +465,6 @@ int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
/* Task Management Functions. Must be called from process context. */
/**
- * isci_abort_task_process_cb() - This is a helper function for the abort task
- * TMF command. It manages the request state with respect to the successful
- * transmission / completion of the abort task request.
- * @cb_state: This parameter specifies when this function was called - after
- * the TMF request has been started and after it has timed-out.
- * @tmf: This parameter specifies the TMF in progress.
- *
- *
- */
-static void isci_abort_task_process_cb(
- enum isci_tmf_cb_state cb_state,
- struct isci_tmf *tmf,
- void *cb_data)
-{
- struct isci_request *old_request;
-
- old_request = (struct isci_request *)cb_data;
-
- dev_dbg(&old_request->isci_host->pdev->dev,
- "%s: tmf=%p, old_request=%p\n",
- __func__, tmf, old_request);
-
- switch (cb_state) {
-
- case isci_tmf_started:
- /* The TMF has been started. Nothing to do here, since the
- * request state was already set to "aborted" by the abort
- * task function.
- */
- if ((old_request->status != aborted)
- && (old_request->status != completed))
- dev_dbg(&old_request->isci_host->pdev->dev,
- "%s: Bad request status (%d): tmf=%p, old_request=%p\n",
- __func__, old_request->status, tmf, old_request);
- break;
-
- case isci_tmf_timed_out:
-
- /* Set the task's state to "aborting", since the abort task
- * function thread set it to "aborted" (above) in anticipation
- * of the task management request working correctly. Since the
- * timeout has now fired, the TMF request failed. We set the
- * state such that the request completion will indicate the
- * device is no longer present.
- */
- isci_request_change_state(old_request, aborting);
- break;
-
- default:
- dev_dbg(&old_request->isci_host->pdev->dev,
- "%s: Bad cb_state (%d): tmf=%p, old_request=%p\n",
- __func__, cb_state, tmf, old_request);
- break;
- }
-}
-
-/**
* isci_task_abort_task() - This function is one of the SAS Domain Template
* functions. This function is called by libsas to abort a specified task.
* @task: This parameter specifies the SAS task to abort.
@@ -1005,195 +473,130 @@ static void isci_abort_task_process_cb(
*/
int isci_task_abort_task(struct sas_task *task)
{
- struct isci_host *isci_host = dev_to_ihost(task->dev);
+ struct isci_host *ihost = dev_to_ihost(task->dev);
DECLARE_COMPLETION_ONSTACK(aborted_io_completion);
struct isci_request *old_request = NULL;
- enum isci_request_status old_state;
- struct isci_remote_device *isci_device = NULL;
+ struct isci_remote_device *idev = NULL;
struct isci_tmf tmf;
int ret = TMF_RESP_FUNC_FAILED;
unsigned long flags;
- bool any_dev_reset = false;
+ int target_done_already = 0;
/* Get the isci_request reference from the task. Note that
* this check does not depend on the pending request list
* in the device, because tasks driving resets may land here
* after completion in the core.
*/
- spin_lock_irqsave(&isci_host->scic_lock, flags);
+ spin_lock_irqsave(&ihost->scic_lock, flags);
spin_lock(&task->task_state_lock);
old_request = task->lldd_task;
/* If task is already done, the request isn't valid */
if (!(task->task_state_flags & SAS_TASK_STATE_DONE) &&
- (task->task_state_flags & SAS_TASK_AT_INITIATOR) &&
- old_request)
- isci_device = isci_lookup_device(task->dev);
-
- spin_unlock(&task->task_state_lock);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: task = %p\n", __func__, task);
-
- if (!isci_device || !old_request)
- goto out;
-
- set_bit(IDEV_EH, &isci_device->flags);
-
- /* This version of the driver will fail abort requests for
- * SATA/STP. Failing the abort request this way will cause the
- * SCSI error handler thread to escalate to LUN reset
- */
- if (sas_protocol_ata(task->task_proto)) {
- dev_dbg(&isci_host->pdev->dev,
- " task %p is for a STP/SATA device;"
- " returning TMF_RESP_FUNC_FAILED\n"
- " to cause a LUN reset...\n", task);
- goto out;
+ old_request) {
+ idev = isci_get_device(task->dev->lldd_dev);
+ target_done_already = test_bit(IREQ_COMPLETE_IN_TARGET,
+ &old_request->flags);
}
+ spin_unlock(&task->task_state_lock);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
- "%s: old_request == %p\n", __func__, old_request);
-
- any_dev_reset = isci_device_is_reset_pending(isci_host, isci_device);
-
- spin_lock_irqsave(&task->task_state_lock, flags);
-
- any_dev_reset = any_dev_reset || (task->task_state_flags & SAS_TASK_NEED_DEV_RESET);
-
- /* If the extraction of the request reference from the task
- * failed, then the request has been completed (or if there is a
- * pending reset then this abort request function must be failed
- * in order to escalate to the target reset).
+ dev_warn(&ihost->pdev->dev,
+ "%s: dev = %p (%s%s), task = %p, old_request == %p\n",
+ __func__, idev,
+ (dev_is_sata(task->dev) ? "STP/SATA"
+ : ((dev_is_expander(task->dev->dev_type))
+ ? "SMP"
+ : "SSP")),
+ ((idev) ? ((test_bit(IDEV_GONE, &idev->flags))
+ ? " IDEV_GONE"
+ : "")
+ : " <NULL>"),
+ task, old_request);
+
+ /* Device reset conditions signalled in task_state_flags are the
+ * responsbility of libsas to observe at the start of the error
+ * handler thread.
*/
- if ((old_request == NULL) || any_dev_reset) {
-
- /* If the device reset task flag is set, fail the task
- * management request. Otherwise, the original request
- * has completed.
- */
- if (any_dev_reset) {
-
- /* Turn off the task's DONE to make sure this
- * task is escalated to a target reset.
- */
- task->task_state_flags &= ~SAS_TASK_STATE_DONE;
-
- /* Make the reset happen as soon as possible. */
- task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- /* Fail the task management request in order to
- * escalate to the target reset.
- */
- ret = TMF_RESP_FUNC_FAILED;
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: Failing task abort in order to "
- "escalate to target reset because\n"
- "SAS_TASK_NEED_DEV_RESET is set for "
- "task %p on dev %p\n",
- __func__, task, isci_device);
-
-
- } else {
- /* The request has already completed and there
- * is nothing to do here other than to set the task
- * done bit, and indicate that the task abort function
- * was sucessful.
- */
- isci_set_task_doneflags(task);
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ if (!idev || !old_request) {
+ /* The request has already completed and there
+ * is nothing to do here other than to set the task
+ * done bit, and indicate that the task abort function
+ * was successful.
+ */
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
- ret = TMF_RESP_FUNC_COMPLETE;
+ ret = TMF_RESP_FUNC_COMPLETE;
- dev_dbg(&isci_host->pdev->dev,
- "%s: abort task not needed for %p\n",
- __func__, task);
- }
+ dev_warn(&ihost->pdev->dev,
+ "%s: abort task not needed for %p\n",
+ __func__, task);
goto out;
- } else {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
}
-
- spin_lock_irqsave(&isci_host->scic_lock, flags);
-
- /* Check the request status and change to "aborted" if currently
- * "starting"; if true then set the I/O kernel completion
- * struct that will be triggered when the request completes.
- */
- old_state = isci_task_validate_request_to_abort(
- old_request, isci_host, isci_device,
- &aborted_io_completion);
- if ((old_state != started) &&
- (old_state != completed) &&
- (old_state != aborting)) {
-
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
-
- /* The request was already being handled by someone else (because
- * they got to set the state away from started).
- */
- dev_dbg(&isci_host->pdev->dev,
- "%s: device = %p; old_request %p already being aborted\n",
- __func__,
- isci_device, old_request);
- ret = TMF_RESP_FUNC_COMPLETE;
+ /* Suspend the RNC, kill the TC */
+ if (isci_remote_device_suspend_terminate(ihost, idev, old_request)
+ != SCI_SUCCESS) {
+ dev_warn(&ihost->pdev->dev,
+ "%s: isci_remote_device_reset_terminate(dev=%p, "
+ "req=%p, task=%p) failed\n",
+ __func__, idev, old_request, task);
+ ret = TMF_RESP_FUNC_FAILED;
goto out;
}
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+
if (task->task_proto == SAS_PROTOCOL_SMP ||
- test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
+ sas_protocol_ata(task->task_proto) ||
+ target_done_already ||
+ test_bit(IDEV_GONE, &idev->flags)) {
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
- "%s: SMP request (%d)"
- " or complete_in_target (%d), thus no TMF\n",
- __func__, (task->task_proto == SAS_PROTOCOL_SMP),
- test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags));
+ /* No task to send, so explicitly resume the device here */
+ isci_remote_device_resume_from_abort(ihost, idev);
- /* Set the state on the task. */
- isci_task_all_done(task);
+ dev_warn(&ihost->pdev->dev,
+ "%s: %s request"
+ " or complete_in_target (%d), "
+ "or IDEV_GONE (%d), thus no TMF\n",
+ __func__,
+ ((task->task_proto == SAS_PROTOCOL_SMP)
+ ? "SMP"
+ : (sas_protocol_ata(task->task_proto)
+ ? "SATA/STP"
+ : "<other>")
+ ),
+ test_bit(IREQ_COMPLETE_IN_TARGET,
+ &old_request->flags),
+ test_bit(IDEV_GONE, &idev->flags));
- ret = TMF_RESP_FUNC_COMPLETE;
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
- /* Stopping and SMP devices are not sent a TMF, and are not
- * reset, but the outstanding I/O request is terminated below.
- */
+ ret = TMF_RESP_FUNC_COMPLETE;
} else {
- /* Fill in the tmf stucture */
+ /* Fill in the tmf structure */
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
- isci_abort_task_process_cb,
old_request);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */
- ret = isci_task_execute_tmf(isci_host, isci_device, &tmf,
+ /* Send the task management request. */
+ #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
+ ret = isci_task_execute_tmf(ihost, idev, &tmf,
ISCI_ABORT_TASK_TIMEOUT_MS);
-
- if (ret != TMF_RESP_FUNC_COMPLETE)
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_task_send_tmf failed\n",
- __func__);
}
- if (ret == TMF_RESP_FUNC_COMPLETE) {
- set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags);
-
- /* Clean up the request on our side, and wait for the aborted
- * I/O to complete.
- */
- isci_terminate_request_core(isci_host, isci_device, old_request);
- }
-
- /* Make sure we do not leave a reference to aborted_io_completion */
- old_request->io_request_completion = NULL;
- out:
- isci_put_device(isci_device);
+out:
+ dev_warn(&ihost->pdev->dev,
+ "%s: Done; dev = %p, task = %p , old_request == %p\n",
+ __func__, idev, task, old_request);
+ isci_put_device(idev);
return ret;
}
@@ -1216,24 +619,6 @@ int isci_task_abort_task_set(
/**
- * isci_task_clear_aca() - This function is one of the SAS Domain Template
- * functions. This is one of the Task Management functoins called by libsas.
- * @d_device: This parameter specifies the domain device associated with this
- * request.
- * @lun: This parameter specifies the lun associated with this request.
- *
- * status, zero indicates success.
- */
-int isci_task_clear_aca(
- struct domain_device *d_device,
- u8 *lun)
-{
- return TMF_RESP_FUNC_FAILED;
-}
-
-
-
-/**
* isci_task_clear_task_set() - This function is one of the SAS Domain Template
* functions. This is one of the Task Management functoins called by libsas.
* @d_device: This parameter specifies the domain device associated with this
@@ -1258,7 +643,6 @@ int isci_task_clear_task_set(
* returned, libsas turns this into a LUN reset; when FUNC_FAILED is
* returned, libsas will turn this into a target reset
* @task: This parameter specifies the sas task being queried.
- * @lun: This parameter specifies the lun associated with this request.
*
* status, zero indicates success.
*/
@@ -1288,345 +672,84 @@ isci_task_request_complete(struct isci_host *ihost,
enum sci_task_status completion_status)
{
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
- struct completion *tmf_complete;
+ struct completion *tmf_complete = NULL;
dev_dbg(&ihost->pdev->dev,
"%s: request = %p, status=%d\n",
__func__, ireq, completion_status);
- isci_request_change_state(ireq, completed);
-
- tmf->status = completion_status;
set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);
- if (tmf->proto == SAS_PROTOCOL_SSP) {
- memcpy(&tmf->resp.resp_iu,
- &ireq->ssp.rsp,
- SSP_RESP_IU_MAX_SIZE);
- } else if (tmf->proto == SAS_PROTOCOL_SATA) {
- memcpy(&tmf->resp.d2h_fis,
- &ireq->stp.rsp,
- sizeof(struct dev_to_host_fis));
+ if (tmf) {
+ tmf->status = completion_status;
+
+ if (tmf->proto == SAS_PROTOCOL_SSP) {
+ memcpy(tmf->resp.rsp_buf,
+ ireq->ssp.rsp_buf,
+ SSP_RESP_IU_MAX_SIZE);
+ } else if (tmf->proto == SAS_PROTOCOL_SATA) {
+ memcpy(&tmf->resp.d2h_fis,
+ &ireq->stp.rsp,
+ sizeof(struct dev_to_host_fis));
+ }
+ /* PRINT_TMF( ((struct isci_tmf *)request->task)); */
+ tmf_complete = tmf->complete;
}
-
- /* PRINT_TMF( ((struct isci_tmf *)request->task)); */
- tmf_complete = tmf->complete;
-
sci_controller_complete_io(ihost, ireq->target_device, ireq);
/* set the 'terminated' flag handle to make sure it cannot be terminated
* or completed again.
*/
set_bit(IREQ_TERMINATED, &ireq->flags);
- isci_request_change_state(ireq, unallocated);
- list_del_init(&ireq->dev_node);
-
- /* The task management part completes last. */
- complete(tmf_complete);
-}
-
-static void isci_smp_task_timedout(unsigned long _task)
-{
- struct sas_task *task = (void *) _task;
- unsigned long flags;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- complete(&task->completion);
-}
-
-static void isci_smp_task_done(struct sas_task *task)
-{
- if (!del_timer(&task->timer))
- return;
- complete(&task->completion);
-}
-
-static struct sas_task *isci_alloc_task(void)
-{
- struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL);
-
- if (task) {
- INIT_LIST_HEAD(&task->list);
- spin_lock_init(&task->task_state_lock);
- task->task_state_flags = SAS_TASK_STATE_PENDING;
- init_timer(&task->timer);
- init_completion(&task->completion);
- }
-
- return task;
-}
+ if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
+ wake_up_all(&ihost->eventq);
-static void isci_free_task(struct isci_host *ihost, struct sas_task *task)
-{
- if (task) {
- BUG_ON(!list_empty(&task->list));
- kfree(task);
- }
-}
-
-static int isci_smp_execute_task(struct isci_host *ihost,
- struct domain_device *dev, void *req,
- int req_size, void *resp, int resp_size)
-{
- int res, retry;
- struct sas_task *task = NULL;
-
- for (retry = 0; retry < 3; retry++) {
- task = isci_alloc_task();
- if (!task)
- return -ENOMEM;
-
- task->dev = dev;
- task->task_proto = dev->tproto;
- sg_init_one(&task->smp_task.smp_req, req, req_size);
- sg_init_one(&task->smp_task.smp_resp, resp, resp_size);
-
- task->task_done = isci_smp_task_done;
-
- task->timer.data = (unsigned long) task;
- task->timer.function = isci_smp_task_timedout;
- task->timer.expires = jiffies + 10*HZ;
- add_timer(&task->timer);
-
- res = isci_task_execute_task(task, 1, GFP_KERNEL);
-
- if (res) {
- del_timer(&task->timer);
- dev_dbg(&ihost->pdev->dev,
- "%s: executing SMP task failed:%d\n",
- __func__, res);
- goto ex_err;
- }
-
- wait_for_completion(&task->completion);
- res = -ECOMM;
- if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- dev_dbg(&ihost->pdev->dev,
- "%s: smp task timed out or aborted\n",
- __func__);
- isci_task_abort_task(task);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- dev_dbg(&ihost->pdev->dev,
- "%s: SMP task aborted and not done\n",
- __func__);
- goto ex_err;
- }
- }
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAM_STAT_GOOD) {
- res = 0;
- break;
- }
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_UNDERRUN) {
- /* no error, but return the number of bytes of
- * underrun */
- res = task->task_status.residual;
- break;
- }
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_OVERRUN) {
- res = -EMSGSIZE;
- break;
- } else {
- dev_dbg(&ihost->pdev->dev,
- "%s: task to dev %016llx response: 0x%x "
- "status 0x%x\n", __func__,
- SAS_ADDR(dev->sas_addr),
- task->task_status.resp,
- task->task_status.stat);
- isci_free_task(ihost, task);
- task = NULL;
- }
- }
-ex_err:
- BUG_ON(retry == 3 && task != NULL);
- isci_free_task(ihost, task);
- return res;
-}
-
-#define DISCOVER_REQ_SIZE 16
-#define DISCOVER_RESP_SIZE 56
-
-int isci_smp_get_phy_attached_dev_type(struct isci_host *ihost,
- struct domain_device *dev,
- int phy_id, int *adt)
-{
- struct smp_resp *disc_resp;
- u8 *disc_req;
- int res;
-
- disc_resp = kzalloc(DISCOVER_RESP_SIZE, GFP_KERNEL);
- if (!disc_resp)
- return -ENOMEM;
-
- disc_req = kzalloc(DISCOVER_REQ_SIZE, GFP_KERNEL);
- if (disc_req) {
- disc_req[0] = SMP_REQUEST;
- disc_req[1] = SMP_DISCOVER;
- disc_req[9] = phy_id;
- } else {
- kfree(disc_resp);
- return -ENOMEM;
- }
- res = isci_smp_execute_task(ihost, dev, disc_req, DISCOVER_REQ_SIZE,
- disc_resp, DISCOVER_RESP_SIZE);
- if (!res) {
- if (disc_resp->result != SMP_RESP_FUNC_ACC)
- res = disc_resp->result;
- else
- *adt = disc_resp->disc.attached_dev_type;
- }
- kfree(disc_req);
- kfree(disc_resp);
-
- return res;
-}
-
-static void isci_wait_for_smp_phy_reset(struct isci_remote_device *idev, int phy_num)
-{
- struct domain_device *dev = idev->domain_dev;
- struct isci_port *iport = idev->isci_port;
- struct isci_host *ihost = iport->isci_host;
- int res, iteration = 0, attached_device_type;
- #define STP_WAIT_MSECS 25000
- unsigned long tmo = msecs_to_jiffies(STP_WAIT_MSECS);
- unsigned long deadline = jiffies + tmo;
- enum {
- SMP_PHYWAIT_PHYDOWN,
- SMP_PHYWAIT_PHYUP,
- SMP_PHYWAIT_DONE
- } phy_state = SMP_PHYWAIT_PHYDOWN;
-
- /* While there is time, wait for the phy to go away and come back */
- while (time_is_after_jiffies(deadline) && phy_state != SMP_PHYWAIT_DONE) {
- int event = atomic_read(&iport->event);
-
- ++iteration;
-
- tmo = wait_event_timeout(ihost->eventq,
- event != atomic_read(&iport->event) ||
- !test_bit(IPORT_BCN_BLOCKED, &iport->flags),
- tmo);
- /* link down, stop polling */
- if (!test_bit(IPORT_BCN_BLOCKED, &iport->flags))
- break;
-
- dev_dbg(&ihost->pdev->dev,
- "%s: iport %p, iteration %d,"
- " phase %d: time_remaining %lu, bcns = %d\n",
- __func__, iport, iteration, phy_state,
- tmo, test_bit(IPORT_BCN_PENDING, &iport->flags));
-
- res = isci_smp_get_phy_attached_dev_type(ihost, dev, phy_num,
- &attached_device_type);
- tmo = deadline - jiffies;
-
- if (res) {
- dev_dbg(&ihost->pdev->dev,
- "%s: iteration %d, phase %d:"
- " SMP error=%d, time_remaining=%lu\n",
- __func__, iteration, phy_state, res, tmo);
- break;
- }
- dev_dbg(&ihost->pdev->dev,
- "%s: iport %p, iteration %d,"
- " phase %d: time_remaining %lu, bcns = %d, "
- "attdevtype = %x\n",
- __func__, iport, iteration, phy_state,
- tmo, test_bit(IPORT_BCN_PENDING, &iport->flags),
- attached_device_type);
-
- switch (phy_state) {
- case SMP_PHYWAIT_PHYDOWN:
- /* Has the device gone away? */
- if (!attached_device_type)
- phy_state = SMP_PHYWAIT_PHYUP;
-
- break;
-
- case SMP_PHYWAIT_PHYUP:
- /* Has the device come back? */
- if (attached_device_type)
- phy_state = SMP_PHYWAIT_DONE;
- break;
-
- case SMP_PHYWAIT_DONE:
- break;
- }
+ if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags))
+ isci_free_tag(ihost, ireq->io_tag);
- }
- dev_dbg(&ihost->pdev->dev, "%s: done\n", __func__);
+ /* The task management part completes last. */
+ if (tmf_complete)
+ complete(tmf_complete);
}
static int isci_reset_device(struct isci_host *ihost,
+ struct domain_device *dev,
struct isci_remote_device *idev)
{
- struct sas_phy *phy = sas_find_local_phy(idev->domain_dev);
- struct isci_port *iport = idev->isci_port;
- enum sci_status status;
- unsigned long flags;
- int rc;
+ int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1;
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ struct isci_port *iport = dev->port->lldd_port;
dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev);
- spin_lock_irqsave(&ihost->scic_lock, flags);
- status = sci_remote_device_reset(idev);
- if (status != SCI_SUCCESS) {
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- dev_dbg(&ihost->pdev->dev,
- "%s: sci_remote_device_reset(%p) returned %d!\n",
- __func__, idev, status);
-
- return TMF_RESP_FUNC_FAILED;
- }
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- /* Make sure all pending requests are able to be fully terminated. */
- isci_device_clear_reset_pending(ihost, idev);
-
- /* If this is a device on an expander, disable BCN processing. */
- if (!scsi_is_sas_phy_local(phy))
- set_bit(IPORT_BCN_BLOCKED, &iport->flags);
-
- rc = sas_phy_reset(phy, true);
-
- /* Terminate in-progress I/O now. */
- isci_remote_device_nuke_requests(ihost, idev);
-
- /* Since all pending TCs have been cleaned, resume the RNC. */
- spin_lock_irqsave(&ihost->scic_lock, flags);
- status = sci_remote_device_reset_complete(idev);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- /* If this is a device on an expander, bring the phy back up. */
- if (!scsi_is_sas_phy_local(phy)) {
- /* A phy reset will cause the device to go away then reappear.
- * Since libsas will take action on incoming BCNs (eg. remove
- * a device going through an SMP phy-control driven reset),
- * we need to wait until the phy comes back up before letting
- * discovery proceed in libsas.
- */
- isci_wait_for_smp_phy_reset(idev, phy->number);
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
- isci_port_bcn_enable(ihost, idev->isci_port);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ /* Suspend the RNC, terminate all outstanding TCs. */
+ if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
+ != SCI_SUCCESS) {
+ rc = TMF_RESP_FUNC_FAILED;
+ goto out;
}
+ /* Note that since the termination for outstanding requests succeeded,
+ * this function will return success. This is because the resets will
+ * only fail if the device has been removed (ie. hotplug), and the
+ * primary duty of this function is to cleanup tasks, so that is the
+ * relevant status.
+ */
+ if (!test_bit(IDEV_GONE, &idev->flags)) {
+ if (scsi_is_sas_phy_local(phy)) {
+ struct isci_phy *iphy = &ihost->phys[phy->number];
- if (status != SCI_SUCCESS) {
- dev_dbg(&ihost->pdev->dev,
- "%s: sci_remote_device_reset_complete(%p) "
- "returned %d!\n", __func__, idev, status);
+ reset_stat = isci_port_perform_hard_reset(ihost, iport,
+ iphy);
+ } else
+ reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));
}
+ /* Explicitly resume the RNC here, since there was no task sent. */
+ isci_remote_device_resume_from_abort(ihost, idev);
- dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev);
-
+ dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n",
+ __func__, idev, reset_stat);
+ out:
+ sas_put_local_phy(phy);
return rc;
}
@@ -1638,38 +761,18 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev)
int ret;
spin_lock_irqsave(&ihost->scic_lock, flags);
- idev = isci_lookup_device(dev);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
- if (!idev || !test_bit(IDEV_EH, &idev->flags)) {
- ret = TMF_RESP_FUNC_COMPLETE;
- goto out;
- }
-
- ret = isci_reset_device(ihost, idev);
- out:
- isci_put_device(idev);
- return ret;
-}
-
-int isci_bus_reset_handler(struct scsi_cmnd *cmd)
-{
- struct domain_device *dev = sdev_to_domain_dev(cmd->device);
- struct isci_host *ihost = dev_to_ihost(dev);
- struct isci_remote_device *idev;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
- idev = isci_lookup_device(dev);
+ idev = isci_get_device(dev->lldd_dev);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
if (!idev) {
- ret = TMF_RESP_FUNC_COMPLETE;
+ /* XXX: need to cleanup any ireqs targeting this
+ * domain_device
+ */
+ ret = -ENODEV;
goto out;
}
- ret = isci_reset_device(ihost, idev);
+ ret = isci_reset_device(ihost, dev, idev);
out:
isci_put_device(idev);
return ret;
diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h
index 4a7fa90287ef..d05d09c1263d 100644
--- a/drivers/scsi/isci/task.h
+++ b/drivers/scsi/isci/task.h
@@ -58,20 +58,9 @@
#include <scsi/sas_ata.h>
#include "host.h"
-struct isci_request;
-
-/**
- * enum isci_tmf_cb_state - This enum defines the possible states in which the
- * TMF callback function is invoked during the TMF execution process.
- *
- *
- */
-enum isci_tmf_cb_state {
+#define ISCI_TERMINATION_TIMEOUT_MSEC 500
- isci_tmf_init_state = 0,
- isci_tmf_started,
- isci_tmf_timed_out
-};
+struct isci_request;
/**
* enum isci_tmf_function_codes - This enum defines the possible preparations
@@ -84,9 +73,8 @@ enum isci_tmf_function_codes {
isci_tmf_func_none = 0,
isci_tmf_ssp_task_abort = TMF_ABORT_TASK,
isci_tmf_ssp_lun_reset = TMF_LU_RESET,
- isci_tmf_sata_srst_high = TMF_LU_RESET + 0x100, /* Non SCSI */
- isci_tmf_sata_srst_low = TMF_LU_RESET + 0x101 /* Non SCSI */
};
+
/**
* struct isci_tmf - This class represents the task management object which
* acts as an interface to libsas for processing task management requests
@@ -97,31 +85,23 @@ struct isci_tmf {
struct completion *complete;
enum sas_protocol proto;
- union {
- struct ssp_response_iu resp_iu;
- struct dev_to_host_fis d2h_fis;
- u8 rsp_buf[SSP_RESP_IU_MAX_SIZE];
- } resp;
unsigned char lun[8];
u16 io_tag;
- struct isci_remote_device *device;
enum isci_tmf_function_codes tmf_code;
int status;
- /* The optional callback function allows the user process to
- * track the TMF transmit / timeout conditions.
- */
- void (*cb_state_func)(
- enum isci_tmf_cb_state,
- struct isci_tmf *, void *);
- void *cb_data;
-
+ /* Must be last --ends in a flexible-array member. */
+ union {
+ struct ssp_response_iu resp_iu;
+ struct dev_to_host_fis d2h_fis;
+ u8 rsp_buf[SSP_RESP_IU_MAX_SIZE];
+ } resp;
};
-static inline void isci_print_tmf(struct isci_tmf *tmf)
+static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf)
{
if (SAS_PROTOCOL_SATA == tmf->proto)
- dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev,
+ dev_dbg(&ihost->pdev->dev,
"%s: status = %x\n"
"tmf->resp.d2h_fis.status = %x\n"
"tmf->resp.d2h_fis.error = %x\n",
@@ -130,7 +110,7 @@ static inline void isci_print_tmf(struct isci_tmf *tmf)
tmf->resp.d2h_fis.status,
tmf->resp.d2h_fis.error);
else
- dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev,
+ dev_dbg(&ihost->pdev->dev,
"%s: status = %x\n"
"tmf->resp.resp_iu.data_present = %x\n"
"tmf->resp.resp_iu.status = %x\n"
@@ -153,7 +133,6 @@ static inline void isci_print_tmf(struct isci_tmf *tmf)
int isci_task_execute_task(
struct sas_task *task,
- int num,
gfp_t gfp_flags);
int isci_task_abort_task(
@@ -163,10 +142,6 @@ int isci_task_abort_task_set(
struct domain_device *d_device,
u8 *lun);
-int isci_task_clear_aca(
- struct domain_device *d_device,
- u8 *lun);
-
int isci_task_clear_task_set(
struct domain_device *d_device,
u8 *lun);
@@ -205,163 +180,4 @@ void *isci_task_ssp_request_get_response_data_address(
u32 isci_task_ssp_request_get_response_data_length(
struct isci_request *request);
-int isci_queuecommand(
- struct scsi_cmnd *scsi_cmd,
- void (*donefunc)(struct scsi_cmnd *));
-
-int isci_bus_reset_handler(struct scsi_cmnd *cmd);
-
-/**
- * enum isci_completion_selection - This enum defines the possible actions to
- * take with respect to a given request's notification back to libsas.
- *
- *
- */
-enum isci_completion_selection {
-
- isci_perform_normal_io_completion, /* Normal notify (task_done) */
- isci_perform_aborted_io_completion, /* No notification. */
- isci_perform_error_io_completion /* Use sas_task_abort */
-};
-
-static inline void isci_set_task_doneflags(
- struct sas_task *task)
-{
- /* Since no futher action will be taken on this task,
- * make sure to mark it complete from the lldd perspective.
- */
- task->task_state_flags |= SAS_TASK_STATE_DONE;
- task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
- task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
-}
-/**
- * isci_task_all_done() - This function clears the task bits to indicate the
- * LLDD is done with the task.
- *
- *
- */
-static inline void isci_task_all_done(
- struct sas_task *task)
-{
- unsigned long flags;
-
- /* Since no futher action will be taken on this task,
- * make sure to mark it complete from the lldd perspective.
- */
- spin_lock_irqsave(&task->task_state_lock, flags);
- isci_set_task_doneflags(task);
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-}
-
-/**
- * isci_task_set_completion_status() - This function sets the completion status
- * for the request.
- * @task: This parameter is the completed request.
- * @response: This parameter is the response code for the completed task.
- * @status: This parameter is the status code for the completed task.
- *
-* @return The new notification mode for the request.
-*/
-static inline enum isci_completion_selection
-isci_task_set_completion_status(
- struct sas_task *task,
- enum service_response response,
- enum exec_status status,
- enum isci_completion_selection task_notification_selection)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
-
- /* If a device reset is being indicated, make sure the I/O
- * is in the error path.
- */
- if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) {
- /* Fail the I/O to make sure it goes into the error path. */
- response = SAS_TASK_UNDELIVERED;
- status = SAM_STAT_TASK_ABORTED;
-
- task_notification_selection = isci_perform_error_io_completion;
- }
- task->task_status.resp = response;
- task->task_status.stat = status;
-
- switch (task_notification_selection) {
-
- case isci_perform_error_io_completion:
-
- if (task->task_proto == SAS_PROTOCOL_SMP) {
- /* There is no error escalation in the SMP case.
- * Convert to a normal completion to avoid the
- * timeout in the discovery path and to let the
- * next action take place quickly.
- */
- task_notification_selection
- = isci_perform_normal_io_completion;
-
- /* Fall through to the normal case... */
- } else {
- /* Use sas_task_abort */
- /* Leave SAS_TASK_STATE_DONE clear
- * Leave SAS_TASK_AT_INITIATOR set.
- */
- break;
- }
-
- case isci_perform_aborted_io_completion:
- /* This path can occur with task-managed requests as well as
- * requests terminated because of LUN or device resets.
- */
- /* Fall through to the normal case... */
- case isci_perform_normal_io_completion:
- /* Normal notification (task_done) */
- isci_set_task_doneflags(task);
- break;
- default:
- WARN_ONCE(1, "unknown task_notification_selection: %d\n",
- task_notification_selection);
- break;
- }
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- return task_notification_selection;
-
-}
-/**
-* isci_execpath_callback() - This function is called from the task
-* execute path when the task needs to callback libsas about the submit-time
-* task failure. The callback occurs either through the task's done function
-* or through sas_task_abort. In the case of regular non-discovery SATA/STP I/O
-* requests, libsas takes the host lock before calling execute task. Therefore
-* in this situation the host lock must be managed before calling the func.
-*
-* @ihost: This parameter is the controller to which the I/O request was sent.
-* @task: This parameter is the I/O request.
-* @func: This parameter is the function to call in the correct context.
-* @status: This parameter is the status code for the completed task.
-*
-*/
-static inline void isci_execpath_callback(struct isci_host *ihost,
- struct sas_task *task,
- void (*func)(struct sas_task *))
-{
- struct domain_device *dev = task->dev;
-
- if (dev_is_sata(dev) && task->uldd_task) {
- unsigned long flags;
-
- /* Since we are still in the submit path, and since
- * libsas takes the host lock on behalf of SATA
- * devices before I/O starts (in the non-discovery case),
- * we need to unlock before we can call the callback function.
- */
- raw_local_irq_save(flags);
- spin_unlock(dev->sata_dev.ap->lock);
- func(task);
- spin_lock(dev->sata_dev.ap->lock);
- raw_local_irq_restore(flags);
- } else
- func(task);
-}
#endif /* !defined(_SCI_TASK_H_) */
diff --git a/drivers/scsi/isci/unsolicited_frame_control.c b/drivers/scsi/isci/unsolicited_frame_control.c
index e9e1e2abacb9..04a6d0d59a22 100644
--- a/drivers/scsi/isci/unsolicited_frame_control.c
+++ b/drivers/scsi/isci/unsolicited_frame_control.c
@@ -57,31 +57,19 @@
#include "unsolicited_frame_control.h"
#include "registers.h"
-int sci_unsolicited_frame_control_construct(struct isci_host *ihost)
+void sci_unsolicited_frame_control_construct(struct isci_host *ihost)
{
struct sci_unsolicited_frame_control *uf_control = &ihost->uf_control;
struct sci_unsolicited_frame *uf;
- u32 buf_len, header_len, i;
- dma_addr_t dma;
- size_t size;
- void *virt;
-
- /*
- * Prepare all of the memory sizes for the UF headers, UF address
- * table, and UF buffers themselves.
- */
- buf_len = SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
- header_len = SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header);
- size = buf_len + header_len + SCU_MAX_UNSOLICITED_FRAMES * sizeof(dma_addr_t);
+ dma_addr_t dma = ihost->ufi_dma;
+ void *virt = ihost->ufi_buf;
+ int i;
/*
* The Unsolicited Frame buffers are set at the start of the UF
* memory descriptor entry. The headers and address table will be
* placed after the buffers.
*/
- virt = dmam_alloc_coherent(&ihost->pdev->dev, size, &dma, GFP_KERNEL);
- if (!virt)
- return -ENOMEM;
/*
* Program the location of the UF header table into the SCU.
@@ -93,8 +81,8 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost)
* headers, since we program the UF address table pointers to
* NULL.
*/
- uf_control->headers.physical_address = dma + buf_len;
- uf_control->headers.array = virt + buf_len;
+ uf_control->headers.physical_address = dma + SCI_UFI_BUF_SIZE;
+ uf_control->headers.array = virt + SCI_UFI_BUF_SIZE;
/*
* Program the location of the UF address table into the SCU.
@@ -103,8 +91,8 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost)
* byte boundary already due to above programming headers being on a
* 64-bit boundary and headers are on a 64-bytes in size.
*/
- uf_control->address_table.physical_address = dma + buf_len + header_len;
- uf_control->address_table.array = virt + buf_len + header_len;
+ uf_control->address_table.physical_address = dma + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE;
+ uf_control->address_table.array = virt + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE;
uf_control->get = 0;
/*
@@ -135,8 +123,6 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost)
virt += SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
dma += SCU_UNSOLICITED_FRAME_BUFFER_SIZE;
}
-
- return 0;
}
enum sci_status sci_unsolicited_frame_control_get_header(struct sci_unsolicited_frame_control *uf_control,
diff --git a/drivers/scsi/isci/unsolicited_frame_control.h b/drivers/scsi/isci/unsolicited_frame_control.h
index 31cb9506f52d..1bc551ec611f 100644
--- a/drivers/scsi/isci/unsolicited_frame_control.h
+++ b/drivers/scsi/isci/unsolicited_frame_control.h
@@ -214,7 +214,7 @@ struct sci_uf_address_table_array {
* starting address of the UF address table.
* 64-bit pointers are required by the hardware.
*/
- dma_addr_t *array;
+ u64 *array;
/**
* This field specifies the physical address location for the UF
@@ -257,9 +257,13 @@ struct sci_unsolicited_frame_control {
};
+#define SCI_UFI_BUF_SIZE (SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE)
+#define SCI_UFI_HDR_SIZE (SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header))
+#define SCI_UFI_TOTAL_SIZE (SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE + SCU_MAX_UNSOLICITED_FRAMES * sizeof(u64))
+
struct isci_host;
-int sci_unsolicited_frame_control_construct(struct isci_host *ihost);
+void sci_unsolicited_frame_control_construct(struct isci_host *ihost);
enum sci_status sci_unsolicited_frame_control_get_header(
struct sci_unsolicited_frame_control *uf_control,
diff --git a/drivers/scsi/iscsi_boot_sysfs.c b/drivers/scsi/iscsi_boot_sysfs.c
index df6bff7366cf..a64abe38db2d 100644
--- a/drivers/scsi/iscsi_boot_sysfs.c
+++ b/drivers/scsi/iscsi_boot_sysfs.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Export the iSCSI boot info to userland via sysfs.
*
* Copyright (C) 2010 Red Hat, Inc. All rights reserved.
* Copyright (C) 2010 Mike Christie
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License v2.0 as published by
- * the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -64,7 +56,8 @@ static void iscsi_boot_kobj_release(struct kobject *kobj)
struct iscsi_boot_kobj *boot_kobj =
container_of(kobj, struct iscsi_boot_kobj, kobj);
- kfree(boot_kobj->data);
+ if (boot_kobj->release)
+ boot_kobj->release(boot_kobj->data);
kfree(boot_kobj);
}
@@ -111,7 +104,7 @@ static struct attribute *target_attrs[] = {
NULL
};
-static mode_t iscsi_boot_tgt_attr_is_visible(struct kobject *kobj,
+static umode_t iscsi_boot_tgt_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int i)
{
struct iscsi_boot_kobj *boot_kobj =
@@ -165,6 +158,7 @@ static struct attribute_group iscsi_boot_target_attr_group = {
iscsi_boot_rd_attr(eth_index, index, ISCSI_BOOT_ETH_INDEX);
iscsi_boot_rd_attr(eth_flags, flags, ISCSI_BOOT_ETH_FLAGS);
iscsi_boot_rd_attr(eth_ip, ip-addr, ISCSI_BOOT_ETH_IP_ADDR);
+iscsi_boot_rd_attr(eth_prefix, prefix-len, ISCSI_BOOT_ETH_PREFIX_LEN);
iscsi_boot_rd_attr(eth_subnet, subnet-mask, ISCSI_BOOT_ETH_SUBNET_MASK);
iscsi_boot_rd_attr(eth_origin, origin, ISCSI_BOOT_ETH_ORIGIN);
iscsi_boot_rd_attr(eth_gateway, gateway, ISCSI_BOOT_ETH_GATEWAY);
@@ -180,6 +174,7 @@ static struct attribute *ethernet_attrs[] = {
&iscsi_boot_attr_eth_index.attr,
&iscsi_boot_attr_eth_flags.attr,
&iscsi_boot_attr_eth_ip.attr,
+ &iscsi_boot_attr_eth_prefix.attr,
&iscsi_boot_attr_eth_subnet.attr,
&iscsi_boot_attr_eth_origin.attr,
&iscsi_boot_attr_eth_gateway.attr,
@@ -192,7 +187,7 @@ static struct attribute *ethernet_attrs[] = {
NULL
};
-static mode_t iscsi_boot_eth_attr_is_visible(struct kobject *kobj,
+static umode_t iscsi_boot_eth_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int i)
{
struct iscsi_boot_kobj *boot_kobj =
@@ -207,6 +202,9 @@ static mode_t iscsi_boot_eth_attr_is_visible(struct kobject *kobj,
else if (attr == &iscsi_boot_attr_eth_ip.attr)
return boot_kobj->is_visible(boot_kobj->data,
ISCSI_BOOT_ETH_IP_ADDR);
+ else if (attr == &iscsi_boot_attr_eth_prefix.attr)
+ return boot_kobj->is_visible(boot_kobj->data,
+ ISCSI_BOOT_ETH_PREFIX_LEN);
else if (attr == &iscsi_boot_attr_eth_subnet.attr)
return boot_kobj->is_visible(boot_kobj->data,
ISCSI_BOOT_ETH_SUBNET_MASK);
@@ -264,7 +262,7 @@ static struct attribute *initiator_attrs[] = {
NULL
};
-static mode_t iscsi_boot_ini_attr_is_visible(struct kobject *kobj,
+static umode_t iscsi_boot_ini_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int i)
{
struct iscsi_boot_kobj *boot_kobj =
@@ -300,12 +298,49 @@ static struct attribute_group iscsi_boot_initiator_attr_group = {
.is_visible = iscsi_boot_ini_attr_is_visible,
};
+/* iBFT ACPI Table attributes */
+iscsi_boot_rd_attr(acpitbl_signature, signature, ISCSI_BOOT_ACPITBL_SIGNATURE);
+iscsi_boot_rd_attr(acpitbl_oem_id, oem_id, ISCSI_BOOT_ACPITBL_OEM_ID);
+iscsi_boot_rd_attr(acpitbl_oem_table_id, oem_table_id,
+ ISCSI_BOOT_ACPITBL_OEM_TABLE_ID);
+
+static struct attribute *acpitbl_attrs[] = {
+ &iscsi_boot_attr_acpitbl_signature.attr,
+ &iscsi_boot_attr_acpitbl_oem_id.attr,
+ &iscsi_boot_attr_acpitbl_oem_table_id.attr,
+ NULL
+};
+
+static umode_t iscsi_boot_acpitbl_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int i)
+{
+ struct iscsi_boot_kobj *boot_kobj =
+ container_of(kobj, struct iscsi_boot_kobj, kobj);
+
+ if (attr == &iscsi_boot_attr_acpitbl_signature.attr)
+ return boot_kobj->is_visible(boot_kobj->data,
+ ISCSI_BOOT_ACPITBL_SIGNATURE);
+ if (attr == &iscsi_boot_attr_acpitbl_oem_id.attr)
+ return boot_kobj->is_visible(boot_kobj->data,
+ ISCSI_BOOT_ACPITBL_OEM_ID);
+ if (attr == &iscsi_boot_attr_acpitbl_oem_table_id.attr)
+ return boot_kobj->is_visible(boot_kobj->data,
+ ISCSI_BOOT_ACPITBL_OEM_TABLE_ID);
+ return 0;
+}
+
+static struct attribute_group iscsi_boot_acpitbl_attr_group = {
+ .attrs = acpitbl_attrs,
+ .is_visible = iscsi_boot_acpitbl_attr_is_visible,
+};
+
static struct iscsi_boot_kobj *
iscsi_boot_create_kobj(struct iscsi_boot_kset *boot_kset,
struct attribute_group *attr_group,
const char *name, int index, void *data,
ssize_t (*show) (void *data, int type, char *buf),
- mode_t (*is_visible) (void *data, int type))
+ umode_t (*is_visible) (void *data, int type),
+ void (*release) (void *data))
{
struct iscsi_boot_kobj *boot_kobj;
@@ -317,12 +352,13 @@ iscsi_boot_create_kobj(struct iscsi_boot_kset *boot_kset,
boot_kobj->kobj.kset = boot_kset->kset;
if (kobject_init_and_add(&boot_kobj->kobj, &iscsi_boot_ktype,
NULL, name, index)) {
- kfree(boot_kobj);
+ kobject_put(&boot_kobj->kobj);
return NULL;
}
boot_kobj->data = data;
boot_kobj->show = show;
boot_kobj->is_visible = is_visible;
+ boot_kobj->release = release;
if (sysfs_create_group(&boot_kobj->kobj, attr_group)) {
/*
@@ -331,7 +367,7 @@ iscsi_boot_create_kobj(struct iscsi_boot_kset *boot_kset,
* the boot kobj was not setup and the normal release
* path is not being run.
*/
- boot_kobj->data = NULL;
+ boot_kobj->release = NULL;
kobject_put(&boot_kobj->kobj);
return NULL;
}
@@ -357,6 +393,7 @@ static void iscsi_boot_remove_kobj(struct iscsi_boot_kobj *boot_kobj)
* @data: driver specific data for target
* @show: attr show function
* @is_visible: attr visibility function
+ * @release: release function
*
* Note: The boot sysfs lib will free the data passed in for the caller
* when all refs to the target kobject have been released.
@@ -365,10 +402,12 @@ struct iscsi_boot_kobj *
iscsi_boot_create_target(struct iscsi_boot_kset *boot_kset, int index,
void *data,
ssize_t (*show) (void *data, int type, char *buf),
- mode_t (*is_visible) (void *data, int type))
+ umode_t (*is_visible) (void *data, int type),
+ void (*release) (void *data))
{
return iscsi_boot_create_kobj(boot_kset, &iscsi_boot_target_attr_group,
- "target%d", index, data, show, is_visible);
+ "target%d", index, data, show, is_visible,
+ release);
}
EXPORT_SYMBOL_GPL(iscsi_boot_create_target);
@@ -379,6 +418,7 @@ EXPORT_SYMBOL_GPL(iscsi_boot_create_target);
* @data: driver specific data
* @show: attr show function
* @is_visible: attr visibility function
+ * @release: release function
*
* Note: The boot sysfs lib will free the data passed in for the caller
* when all refs to the initiator kobject have been released.
@@ -387,12 +427,13 @@ struct iscsi_boot_kobj *
iscsi_boot_create_initiator(struct iscsi_boot_kset *boot_kset, int index,
void *data,
ssize_t (*show) (void *data, int type, char *buf),
- mode_t (*is_visible) (void *data, int type))
+ umode_t (*is_visible) (void *data, int type),
+ void (*release) (void *data))
{
return iscsi_boot_create_kobj(boot_kset,
&iscsi_boot_initiator_attr_group,
"initiator", index, data, show,
- is_visible);
+ is_visible, release);
}
EXPORT_SYMBOL_GPL(iscsi_boot_create_initiator);
@@ -403,6 +444,7 @@ EXPORT_SYMBOL_GPL(iscsi_boot_create_initiator);
* @data: driver specific data
* @show: attr show function
* @is_visible: attr visibility function
+ * @release: release function
*
* Note: The boot sysfs lib will free the data passed in for the caller
* when all refs to the ethernet kobject have been released.
@@ -411,16 +453,43 @@ struct iscsi_boot_kobj *
iscsi_boot_create_ethernet(struct iscsi_boot_kset *boot_kset, int index,
void *data,
ssize_t (*show) (void *data, int type, char *buf),
- mode_t (*is_visible) (void *data, int type))
+ umode_t (*is_visible) (void *data, int type),
+ void (*release) (void *data))
{
return iscsi_boot_create_kobj(boot_kset,
&iscsi_boot_ethernet_attr_group,
"ethernet%d", index, data, show,
- is_visible);
+ is_visible, release);
}
EXPORT_SYMBOL_GPL(iscsi_boot_create_ethernet);
/**
+ * iscsi_boot_create_acpitbl() - create boot acpi table sysfs dir
+ * @boot_kset: boot kset
+ * @index: not used
+ * @data: driver specific data
+ * @show: attr show function
+ * @is_visible: attr visibility function
+ * @release: release function
+ *
+ * Note: The boot sysfs lib will free the data passed in for the caller
+ * when all refs to the acpitbl kobject have been released.
+ */
+struct iscsi_boot_kobj *
+iscsi_boot_create_acpitbl(struct iscsi_boot_kset *boot_kset, int index,
+ void *data,
+ ssize_t (*show)(void *data, int type, char *buf),
+ umode_t (*is_visible)(void *data, int type),
+ void (*release)(void *data))
+{
+ return iscsi_boot_create_kobj(boot_kset,
+ &iscsi_boot_acpitbl_attr_group,
+ "acpi_header", index, data, show,
+ is_visible, release);
+}
+EXPORT_SYMBOL_GPL(iscsi_boot_create_acpitbl);
+
+/**
* iscsi_boot_create_kset() - creates root sysfs tree
* @set_name: name of root dir
*/
@@ -472,10 +541,14 @@ void iscsi_boot_destroy_kset(struct iscsi_boot_kset *boot_kset)
{
struct iscsi_boot_kobj *boot_kobj, *tmp_kobj;
+ if (!boot_kset)
+ return;
+
list_for_each_entry_safe(boot_kobj, tmp_kobj,
&boot_kset->kobj_list, list)
iscsi_boot_remove_kobj(boot_kobj);
kset_unregister(boot_kset->kset);
+ kfree(boot_kset);
}
EXPORT_SYMBOL_GPL(iscsi_boot_destroy_kset);
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 3df985305f69..7b4fe0e6afb2 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* iSCSI Initiator over TCP/IP Data-Path
*
@@ -7,16 +8,6 @@
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
* maintained by open-iscsi@googlegroups.com
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* See the file COPYING included with this distribution for more details.
*
* Credits:
@@ -29,18 +20,22 @@
#include <linux/types.h>
#include <linux/inet.h>
#include <linux/slab.h>
+#include <linux/sched/mm.h>
#include <linux/file.h>
#include <linux/blkdev.h>
-#include <linux/crypto.h>
#include <linux/delay.h>
#include <linux/kfifo.h>
#include <linux/scatterlist.h>
+#include <linux/module.h>
+#include <linux/backing-dev.h>
#include <net/tcp.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi.h>
#include <scsi/scsi_transport_iscsi.h>
+#include <trace/events/iscsi.h>
+#include <trace/events/sock.h>
#include "iscsi_tcp.h"
@@ -51,12 +46,16 @@ MODULE_DESCRIPTION("iSCSI/TCP data-path");
MODULE_LICENSE("GPL");
static struct scsi_transport_template *iscsi_sw_tcp_scsi_transport;
-static struct scsi_host_template iscsi_sw_tcp_sht;
+static const struct scsi_host_template iscsi_sw_tcp_sht;
static struct iscsi_transport iscsi_sw_tcp_transport;
-static unsigned int iscsi_max_lun = 512;
+static unsigned int iscsi_max_lun = ~0;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
+static bool iscsi_recv_from_iscsi_q;
+module_param_named(recv_from_iscsi_q, iscsi_recv_from_iscsi_q, bool, 0644);
+MODULE_PARM_DESC(recv_from_iscsi_q, "Set to true to read iSCSI data/headers from the iscsi_q workqueue. The default is false which will perform reads from the network softirq context.");
+
static int iscsi_sw_tcp_dbg;
module_param_named(debug_iscsi_tcp, iscsi_sw_tcp_dbg, int,
S_IRUGO | S_IWUSR);
@@ -69,6 +68,9 @@ MODULE_PARM_DESC(debug_iscsi_tcp, "Turn on debugging for iscsi_tcp module "
iscsi_conn_printk(KERN_INFO, _conn, \
"%s " dbg_fmt, \
__func__, ##arg); \
+ iscsi_dbg_trace(trace_iscsi_dbg_sw_tcp, \
+ &(_conn)->cls_conn->dev, \
+ "%s " dbg_fmt, __func__, ##arg);\
} while (0);
@@ -107,12 +109,15 @@ static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
* If the socket is in CLOSE or CLOSE_WAIT we should
* not close the connection if there is still some
* data pending.
+ *
+ * Must be called with sk_callback_lock.
*/
static inline int iscsi_sw_sk_state_check(struct sock *sk)
{
- struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data;
+ struct iscsi_conn *conn = sk->sk_user_data;
if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) &&
+ (conn->session->state != ISCSI_STATE_LOGGING_OUT) &&
!atomic_read(&sk->sk_rmem_alloc)) {
ISCSI_SW_TCP_DBG(conn, "TCP_CLOSE|TCP_CLOSE_WAIT\n");
iscsi_conn_failure(conn, ISCSI_ERR_TCP_CONN_CLOSE);
@@ -121,14 +126,13 @@ static inline int iscsi_sw_sk_state_check(struct sock *sk)
return 0;
}
-static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
+static void iscsi_sw_tcp_recv_data(struct iscsi_conn *conn)
{
- struct iscsi_conn *conn = sk->sk_user_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct sock *sk = tcp_sw_conn->sock->sk;
read_descriptor_t rd_desc;
- read_lock(&sk->sk_callback_lock);
-
/*
* Use rd_desc to pass 'conn' to iscsi_tcp_recv.
* We set count to 1 because we want the network layer to
@@ -137,15 +141,51 @@ static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
*/
rd_desc.arg.data = conn;
rd_desc.count = 1;
- tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv);
- iscsi_sw_sk_state_check(sk);
-
- read_unlock(&sk->sk_callback_lock);
+ tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv);
/* If we had to (atomically) map a highmem page,
* unmap it now. */
iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
+
+ iscsi_sw_sk_state_check(sk);
+}
+
+static void iscsi_sw_tcp_recv_data_work(struct work_struct *work)
+{
+ struct iscsi_conn *conn = container_of(work, struct iscsi_conn,
+ recvwork);
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct sock *sk = tcp_sw_conn->sock->sk;
+
+ lock_sock(sk);
+ iscsi_sw_tcp_recv_data(conn);
+ release_sock(sk);
+}
+
+static void iscsi_sw_tcp_data_ready(struct sock *sk)
+{
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
+ struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_conn *conn;
+
+ trace_sk_data_ready(sk);
+
+ read_lock_bh(&sk->sk_callback_lock);
+ conn = sk->sk_user_data;
+ if (!conn) {
+ read_unlock_bh(&sk->sk_callback_lock);
+ return;
+ }
+ tcp_conn = conn->dd_data;
+ tcp_sw_conn = tcp_conn->dd_data;
+
+ if (tcp_sw_conn->queue_recv)
+ iscsi_conn_queue_recv(conn);
+ else
+ iscsi_sw_tcp_recv_data(conn);
+ read_unlock_bh(&sk->sk_callback_lock);
}
static void iscsi_sw_tcp_state_change(struct sock *sk)
@@ -153,13 +193,14 @@ static void iscsi_sw_tcp_state_change(struct sock *sk)
struct iscsi_tcp_conn *tcp_conn;
struct iscsi_sw_tcp_conn *tcp_sw_conn;
struct iscsi_conn *conn;
- struct iscsi_session *session;
void (*old_state_change)(struct sock *);
- read_lock(&sk->sk_callback_lock);
-
- conn = (struct iscsi_conn*)sk->sk_user_data;
- session = conn->session;
+ read_lock_bh(&sk->sk_callback_lock);
+ conn = sk->sk_user_data;
+ if (!conn) {
+ read_unlock_bh(&sk->sk_callback_lock);
+ return;
+ }
iscsi_sw_sk_state_check(sk);
@@ -167,24 +208,38 @@ static void iscsi_sw_tcp_state_change(struct sock *sk)
tcp_sw_conn = tcp_conn->dd_data;
old_state_change = tcp_sw_conn->old_state_change;
- read_unlock(&sk->sk_callback_lock);
+ read_unlock_bh(&sk->sk_callback_lock);
old_state_change(sk);
}
/**
- * iscsi_write_space - Called when more output buffer space is available
+ * iscsi_sw_tcp_write_space - Called when more output buffer space is available
* @sk: socket space is available for
**/
static void iscsi_sw_tcp_write_space(struct sock *sk)
{
- struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct iscsi_conn *conn;
+ struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
+ void (*old_write_space)(struct sock *);
+
+ read_lock_bh(&sk->sk_callback_lock);
+ conn = sk->sk_user_data;
+ if (!conn) {
+ read_unlock_bh(&sk->sk_callback_lock);
+ return;
+ }
+
+ tcp_conn = conn->dd_data;
+ tcp_sw_conn = tcp_conn->dd_data;
+ old_write_space = tcp_sw_conn->old_write_space;
+ read_unlock_bh(&sk->sk_callback_lock);
+
+ old_write_space(sk);
- tcp_sw_conn->old_write_space(sk);
ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n");
- iscsi_conn_queue_work(conn);
+ iscsi_conn_queue_xmit(conn);
}
static void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn)
@@ -218,7 +273,7 @@ iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn)
sk->sk_data_ready = tcp_sw_conn->old_data_ready;
sk->sk_state_change = tcp_sw_conn->old_state_change;
sk->sk_write_space = tcp_sw_conn->old_write_space;
- sk->sk_no_check = 0;
+ sk->sk_no_check_tx = 0;
write_unlock_bh(&sk->sk_callback_lock);
}
@@ -245,32 +300,32 @@ static int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
while (!iscsi_tcp_segment_done(tcp_conn, segment, 0, r)) {
struct scatterlist *sg;
+ struct msghdr msg = {};
+ struct bio_vec bv;
unsigned int offset, copy;
- int flags = 0;
r = 0;
offset = segment->copied;
copy = segment->size - offset;
if (segment->total_copied + segment->size < segment->total_size)
- flags |= MSG_MORE;
+ msg.msg_flags |= MSG_MORE;
+
+ if (tcp_sw_conn->queue_recv)
+ msg.msg_flags |= MSG_DONTWAIT;
- /* Use sendpage if we can; else fall back to sendmsg */
if (!segment->data) {
+ if (!tcp_conn->iscsi_conn->datadgst_en)
+ msg.msg_flags |= MSG_SPLICE_PAGES;
sg = segment->sg;
offset += segment->sg_offset + sg->offset;
- r = tcp_sw_conn->sendpage(sk, sg_page(sg), offset,
- copy, flags);
+ bvec_set_page(&bv, sg_page(sg), copy, offset);
} else {
- struct msghdr msg = { .msg_flags = flags };
- struct kvec iov = {
- .iov_base = segment->data + offset,
- .iov_len = copy
- };
-
- r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
+ bvec_set_virt(&bv, segment->data + offset, copy);
}
+ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bv, 1, copy);
+ r = sock_sendmsg(sk, &msg);
if (r < 0) {
iscsi_tcp_segment_unmap(segment);
return r;
@@ -282,6 +337,7 @@ static int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
/**
* iscsi_sw_tcp_xmit - TCP transmit
+ * @conn: iscsi connection
**/
static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn)
{
@@ -331,7 +387,8 @@ error:
}
/**
- * iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit
+ * iscsi_sw_tcp_xmit_qlen - return the number of bytes queued for xmit
+ * @conn: iscsi connection
*/
static inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn)
{
@@ -345,17 +402,32 @@ static inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn)
static int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task)
{
struct iscsi_conn *conn = task->conn;
- int rc;
+ unsigned int noreclaim_flag;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ int rc = 0;
+
+ if (!tcp_sw_conn->sock) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "Transport not bound to socket!\n");
+ return -EINVAL;
+ }
+
+ noreclaim_flag = memalloc_noreclaim_save();
while (iscsi_sw_tcp_xmit_qlen(conn)) {
rc = iscsi_sw_tcp_xmit(conn);
- if (rc == 0)
- return -EAGAIN;
+ if (rc == 0) {
+ rc = -EAGAIN;
+ break;
+ }
if (rc < 0)
- return rc;
+ break;
+ rc = 0;
}
- return 0;
+ memalloc_noreclaim_restore(noreclaim_flag);
+ return rc;
}
/*
@@ -395,8 +467,7 @@ static void iscsi_sw_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr,
* sufficient room.
*/
if (conn->hdrdgst_en) {
- iscsi_tcp_dgst_header(&tcp_sw_conn->tx_hash, hdr, hdrlen,
- hdr + hdrlen);
+ iscsi_tcp_dgst_header(hdr, hdrlen, hdr + hdrlen);
hdrlen += ISCSI_DIGEST_SIZE;
}
@@ -421,7 +492,7 @@ iscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- struct hash_desc *tx_hash = NULL;
+ u32 *tx_crcp = NULL;
unsigned int hdr_spec_len;
ISCSI_SW_TCP_DBG(conn, "offset=%d, datalen=%d %s\n", offset, len,
@@ -434,11 +505,10 @@ iscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
if (conn->datadgst_en)
- tx_hash = &tcp_sw_conn->tx_hash;
+ tx_crcp = &tcp_sw_conn->tx_crc;
return iscsi_segment_seek_sg(&tcp_sw_conn->out.data_segment,
- sg, count, offset, len,
- NULL, tx_hash);
+ sg, count, offset, len, NULL, tx_crcp);
}
static void
@@ -447,7 +517,7 @@ iscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data,
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- struct hash_desc *tx_hash = NULL;
+ u32 *tx_crcp = NULL;
unsigned int hdr_spec_len;
ISCSI_SW_TCP_DBG(conn, "datalen=%zd %s\n", len, conn->datadgst_en ?
@@ -459,10 +529,10 @@ iscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data,
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
if (conn->datadgst_en)
- tx_hash = &tcp_sw_conn->tx_hash;
+ tx_crcp = &tcp_sw_conn->tx_crc;
iscsi_segment_init_linear(&tcp_sw_conn->out.data_segment,
- data, len, NULL, tx_hash);
+ data, len, NULL, tx_crcp);
}
static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task,
@@ -479,7 +549,7 @@ static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task,
if (!task->sc)
iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count);
else {
- struct scsi_data_buffer *sdb = scsi_out(task->sc);
+ struct scsi_data_buffer *sdb = &task->sc->sdb;
err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl,
sdb->table.nents, offset,
@@ -518,67 +588,52 @@ iscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session,
conn = cls_conn->dd_data;
tcp_conn = conn->dd_data;
tcp_sw_conn = tcp_conn->dd_data;
+ INIT_WORK(&conn->recvwork, iscsi_sw_tcp_recv_data_work);
+ tcp_sw_conn->queue_recv = iscsi_recv_from_iscsi_q;
- tcp_sw_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
- CRYPTO_ALG_ASYNC);
- tcp_sw_conn->tx_hash.flags = 0;
- if (IS_ERR(tcp_sw_conn->tx_hash.tfm))
- goto free_conn;
-
- tcp_sw_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
- CRYPTO_ALG_ASYNC);
- tcp_sw_conn->rx_hash.flags = 0;
- if (IS_ERR(tcp_sw_conn->rx_hash.tfm))
- goto free_tx_tfm;
- tcp_conn->rx_hash = &tcp_sw_conn->rx_hash;
+ mutex_init(&tcp_sw_conn->sock_lock);
+ tcp_conn->rx_crcp = &tcp_sw_conn->rx_crc;
return cls_conn;
-
-free_tx_tfm:
- crypto_free_hash(tcp_sw_conn->tx_hash.tfm);
-free_conn:
- iscsi_conn_printk(KERN_ERR, conn,
- "Could not create connection due to crc32c "
- "loading error. Make sure the crc32c "
- "module is built as a module or into the "
- "kernel\n");
- iscsi_tcp_conn_teardown(cls_conn);
- return NULL;
}
static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn)
{
- struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct socket *sock = tcp_sw_conn->sock;
+ /*
+ * The iscsi transport class will make sure we are not called in
+ * parallel with start, stop, bind and destroys. However, this can be
+ * called twice if userspace does a stop then a destroy.
+ */
if (!sock)
return;
+ /*
+ * Make sure we start socket shutdown now in case userspace is up
+ * but delayed in releasing the socket.
+ */
+ kernel_sock_shutdown(sock, SHUT_RDWR);
+
sock_hold(sock->sk);
iscsi_sw_tcp_conn_restore_callbacks(conn);
sock_put(sock->sk);
- spin_lock_bh(&session->lock);
+ iscsi_suspend_rx(conn);
+
+ mutex_lock(&tcp_sw_conn->sock_lock);
tcp_sw_conn->sock = NULL;
- spin_unlock_bh(&session->lock);
+ mutex_unlock(&tcp_sw_conn->sock_lock);
sockfd_put(sock);
}
static void iscsi_sw_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
iscsi_sw_tcp_release_conn(conn);
-
- if (tcp_sw_conn->tx_hash.tfm)
- crypto_free_hash(tcp_sw_conn->tx_hash.tfm);
- if (tcp_sw_conn->rx_hash.tfm)
- crypto_free_hash(tcp_sw_conn->rx_hash.tfm);
-
iscsi_tcp_conn_teardown(cls_conn);
}
@@ -592,20 +647,17 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
/* userspace may have goofed up and not bound us */
if (!sock)
return;
- /*
- * Make sure our recv side is stopped.
- * Older tools called conn stop before ep_disconnect
- * so IO could still be coming in.
- */
- write_lock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
- write_unlock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
sock->sk->sk_err = EIO;
wake_up_interruptible(sk_sleep(sock->sk));
- iscsi_conn_stop(cls_conn, flag);
+ /* stop xmit side */
+ iscsi_suspend_tx(conn);
+
+ /* stop recv side and release socket */
iscsi_sw_tcp_release_conn(conn);
+
+ iscsi_conn_stop(cls_conn, flag);
}
static int
@@ -613,7 +665,6 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
int is_leading)
{
- struct iscsi_session *session = cls_session->dd_data;
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
@@ -629,23 +680,29 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
return -EEXIST;
}
+ err = -EINVAL;
+ if (!sk_is_tcp(sock->sk))
+ goto free_socket;
+
err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
if (err)
goto free_socket;
- spin_lock_bh(&session->lock);
+ mutex_lock(&tcp_sw_conn->sock_lock);
/* bind iSCSI connection and socket */
tcp_sw_conn->sock = sock;
- spin_unlock_bh(&session->lock);
+ mutex_unlock(&tcp_sw_conn->sock_lock);
/* setup Socket parameters */
sk = sock->sk;
- sk->sk_reuse = 1;
+ sk->sk_reuse = SK_CAN_REUSE;
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
+ sk->sk_use_task_frag = false;
+ sk_set_memalloc(sk);
+ sock_no_linger(sk);
iscsi_sw_tcp_conn_set_callbacks(conn);
- tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage;
/*
* set receive state machine into initial state
*/
@@ -662,31 +719,24 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn,
int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- int value;
switch(param) {
case ISCSI_PARAM_HDRDGST_EN:
iscsi_set_param(cls_conn, param, buf, buflen);
break;
case ISCSI_PARAM_DATADGST_EN:
+ mutex_lock(&tcp_sw_conn->sock_lock);
+ if (!tcp_sw_conn->sock) {
+ mutex_unlock(&tcp_sw_conn->sock_lock);
+ return -ENOTCONN;
+ }
iscsi_set_param(cls_conn, param, buf, buflen);
- tcp_sw_conn->sendpage = conn->datadgst_en ?
- sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage;
+ mutex_unlock(&tcp_sw_conn->sock_lock);
break;
case ISCSI_PARAM_MAX_R2T:
- sscanf(buf, "%d", &value);
- if (value <= 0 || !is_power_of_2(value))
- return -EINVAL;
- if (session->max_r2t == value)
- break;
- iscsi_tcp_r2tpool_free(session);
- iscsi_set_param(cls_conn, param, buf, buflen);
- if (iscsi_tcp_r2tpool_alloc(session))
- return -ENOMEM;
- break;
+ return iscsi_tcp_set_max_r2t(conn, buf);
default:
return iscsi_set_param(cls_conn, param, buf, buflen);
}
@@ -698,23 +748,48 @@ static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
+ struct iscsi_tcp_conn *tcp_conn;
struct sockaddr_in6 addr;
- int rc, len;
+ struct socket *sock;
+ int rc;
switch(param) {
case ISCSI_PARAM_CONN_PORT:
case ISCSI_PARAM_CONN_ADDRESS:
- spin_lock_bh(&conn->session->lock);
- if (!tcp_sw_conn || !tcp_sw_conn->sock) {
- spin_unlock_bh(&conn->session->lock);
+ case ISCSI_PARAM_LOCAL_PORT:
+ spin_lock_bh(&conn->session->frwd_lock);
+ if (!conn->session->leadconn) {
+ spin_unlock_bh(&conn->session->frwd_lock);
return -ENOTCONN;
}
- rc = kernel_getpeername(tcp_sw_conn->sock,
- (struct sockaddr *)&addr, &len);
- spin_unlock_bh(&conn->session->lock);
- if (rc)
+ /*
+ * The conn has been setup and bound, so just grab a ref
+ * incase a destroy runs while we are in the net layer.
+ */
+ iscsi_get_conn(conn->cls_conn);
+ spin_unlock_bh(&conn->session->frwd_lock);
+
+ tcp_conn = conn->dd_data;
+ tcp_sw_conn = tcp_conn->dd_data;
+
+ mutex_lock(&tcp_sw_conn->sock_lock);
+ sock = tcp_sw_conn->sock;
+ if (!sock) {
+ rc = -ENOTCONN;
+ goto sock_unlock;
+ }
+
+ if (param == ISCSI_PARAM_LOCAL_PORT)
+ rc = kernel_getsockname(sock,
+ (struct sockaddr *)&addr);
+ else
+ rc = kernel_getpeername(sock,
+ (struct sockaddr *)&addr);
+sock_unlock:
+ mutex_unlock(&tcp_sw_conn->sock_lock);
+ iscsi_put_conn(conn->cls_conn);
+ if (rc < 0)
return rc;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
@@ -730,37 +805,49 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost);
- struct iscsi_session *session = tcp_sw_host->session;
+ struct iscsi_session *session;
struct iscsi_conn *conn;
struct iscsi_tcp_conn *tcp_conn;
struct iscsi_sw_tcp_conn *tcp_sw_conn;
struct sockaddr_in6 addr;
- int rc, len;
+ struct socket *sock;
+ int rc;
switch (param) {
case ISCSI_HOST_PARAM_IPADDRESS:
- spin_lock_bh(&session->lock);
+ session = tcp_sw_host->session;
+ if (!session)
+ return -ENOTCONN;
+
+ spin_lock_bh(&session->frwd_lock);
conn = session->leadconn;
if (!conn) {
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
return -ENOTCONN;
}
tcp_conn = conn->dd_data;
-
tcp_sw_conn = tcp_conn->dd_data;
- if (!tcp_sw_conn->sock) {
- spin_unlock_bh(&session->lock);
- return -ENOTCONN;
- }
-
- rc = kernel_getsockname(tcp_sw_conn->sock,
- (struct sockaddr *)&addr, &len);
- spin_unlock_bh(&session->lock);
- if (rc)
+ /*
+ * The conn has been setup and bound, so just grab a ref
+ * incase a destroy runs while we are in the net layer.
+ */
+ iscsi_get_conn(conn->cls_conn);
+ spin_unlock_bh(&session->frwd_lock);
+
+ mutex_lock(&tcp_sw_conn->sock_lock);
+ sock = tcp_sw_conn->sock;
+ if (!sock)
+ rc = -ENOTCONN;
+ else
+ rc = kernel_getsockname(sock, (struct sockaddr *)&addr);
+ mutex_unlock(&tcp_sw_conn->sock_lock);
+ iscsi_put_conn(conn->cls_conn);
+ if (rc < 0)
return rc;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
- &addr, param, buf);
+ &addr,
+ (enum iscsi_param)param, buf);
default:
return iscsi_host_get_param(shost, param, buf);
}
@@ -795,6 +882,7 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
struct iscsi_session *session;
struct iscsi_sw_tcp_host *tcp_sw_host;
struct Scsi_Host *shost;
+ int rc;
if (ep) {
printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep);
@@ -811,6 +899,12 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
shost->max_id = 0;
shost->max_channel = 0;
shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
+ shost->dma_alignment = 0;
+
+ rc = iscsi_host_get_max_scsi_cmds(shost, cmds_max);
+ if (rc < 0)
+ goto free_host;
+ shost->can_queue = rc;
if (iscsi_host_add(shost, NULL))
goto free_host;
@@ -823,18 +917,19 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
if (!cls_session)
goto remove_host;
session = cls_session->dd_data;
- tcp_sw_host = iscsi_host_priv(shost);
- tcp_sw_host->session = session;
- shost->can_queue = session->scsi_cmds_max;
if (iscsi_tcp_r2tpool_alloc(session))
goto remove_session;
+
+ /* We are now fully setup so expose the session to sysfs. */
+ tcp_sw_host = iscsi_host_priv(shost);
+ tcp_sw_host->session = session;
return cls_session;
remove_session:
iscsi_session_teardown(cls_session);
remove_host:
- iscsi_host_remove(shost);
+ iscsi_host_remove(shost, false);
free_host:
iscsi_host_free(shost);
return NULL;
@@ -843,45 +938,112 @@ free_host:
static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct iscsi_session *session = cls_session->dd_data;
+
+ if (WARN_ON_ONCE(session->leadconn))
+ return;
+
+ iscsi_session_remove(cls_session);
+ /*
+ * Our get_host_param needs to access the session, so remove the
+ * host from sysfs before freeing the session to make sure userspace
+ * is no longer accessing the callout.
+ */
+ iscsi_host_remove(shost, false);
iscsi_tcp_r2tpool_free(cls_session->dd_data);
- iscsi_session_teardown(cls_session);
- iscsi_host_remove(shost);
+ iscsi_session_free(cls_session);
iscsi_host_free(shost);
}
-static int iscsi_sw_tcp_slave_alloc(struct scsi_device *sdev)
+static umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param)
{
- set_bit(QUEUE_FLAG_BIDI, &sdev->request_queue->queue_flags);
+ switch (param_type) {
+ case ISCSI_HOST_PARAM:
+ switch (param) {
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ case ISCSI_HOST_PARAM_INITIATOR_NAME:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case ISCSI_PARAM:
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ case ISCSI_PARAM_HDRDGST_EN:
+ case ISCSI_PARAM_DATADGST_EN:
+ case ISCSI_PARAM_CONN_ADDRESS:
+ case ISCSI_PARAM_CONN_PORT:
+ case ISCSI_PARAM_LOCAL_PORT:
+ case ISCSI_PARAM_EXP_STATSN:
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ case ISCSI_PARAM_PING_TMO:
+ case ISCSI_PARAM_RECV_TMO:
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ case ISCSI_PARAM_MAX_R2T:
+ case ISCSI_PARAM_IMM_DATA_EN:
+ case ISCSI_PARAM_FIRST_BURST:
+ case ISCSI_PARAM_MAX_BURST:
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ case ISCSI_PARAM_ERL:
+ case ISCSI_PARAM_TARGET_NAME:
+ case ISCSI_PARAM_TPGT:
+ case ISCSI_PARAM_USERNAME:
+ case ISCSI_PARAM_PASSWORD:
+ case ISCSI_PARAM_USERNAME_IN:
+ case ISCSI_PARAM_PASSWORD_IN:
+ case ISCSI_PARAM_FAST_ABORT:
+ case ISCSI_PARAM_ABORT_TMO:
+ case ISCSI_PARAM_LU_RESET_TMO:
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ case ISCSI_PARAM_IFACE_NAME:
+ case ISCSI_PARAM_INITIATOR_NAME:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ }
+
return 0;
}
-static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev)
+static int iscsi_sw_tcp_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
- blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
- blk_queue_dma_alignment(sdev->request_queue, 0);
+ struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(sdev->host);
+ struct iscsi_session *session = tcp_sw_host->session;
+ struct iscsi_conn *conn = session->leadconn;
+
+ if (conn->datadgst_en)
+ lim->features |= BLK_FEAT_STABLE_WRITES;
return 0;
}
-static struct scsi_host_template iscsi_sw_tcp_sht = {
+static const struct scsi_host_template iscsi_sw_tcp_sht = {
.module = THIS_MODULE,
.name = "iSCSI Initiator over TCP/IP",
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
- .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
+ .change_queue_depth = scsi_change_queue_depth,
+ .can_queue = ISCSI_TOTAL_CMDS_MAX,
.sg_tablesize = 4096,
.max_sectors = 0xFFFF,
.cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .eh_timed_out = iscsi_eh_cmd_timed_out,
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler= iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_recover_target,
- .use_clustering = DISABLE_CLUSTERING,
- .slave_alloc = iscsi_sw_tcp_slave_alloc,
- .slave_configure = iscsi_sw_tcp_slave_configure,
- .target_alloc = iscsi_target_alloc,
+ .dma_boundary = PAGE_SIZE - 1,
+ .sdev_configure = iscsi_sw_tcp_sdev_configure,
.proc_name = "iscsi_tcp",
.this_id = -1,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct iscsi_cmd),
};
static struct iscsi_transport iscsi_sw_tcp_transport = {
@@ -889,33 +1051,6 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
.name = "tcp",
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
| CAP_DATADGST,
- .param_mask = ISCSI_MAX_RECV_DLENGTH |
- ISCSI_MAX_XMIT_DLENGTH |
- ISCSI_HDRDGST_EN |
- ISCSI_DATADGST_EN |
- ISCSI_INITIAL_R2T_EN |
- ISCSI_MAX_R2T |
- ISCSI_IMM_DATA_EN |
- ISCSI_FIRST_BURST |
- ISCSI_MAX_BURST |
- ISCSI_PDU_INORDER_EN |
- ISCSI_DATASEQ_INORDER_EN |
- ISCSI_ERL |
- ISCSI_CONN_PORT |
- ISCSI_CONN_ADDRESS |
- ISCSI_EXP_STATSN |
- ISCSI_PERSISTENT_PORT |
- ISCSI_PERSISTENT_ADDRESS |
- ISCSI_TARGET_NAME | ISCSI_TPGT |
- ISCSI_USERNAME | ISCSI_PASSWORD |
- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO |
- ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
- ISCSI_HOST_INITIATOR_NAME |
- ISCSI_HOST_NETDEV_NAME,
/* session management */
.create_session = iscsi_sw_tcp_session_create,
.destroy_session = iscsi_sw_tcp_session_destroy,
@@ -923,6 +1058,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
.create_conn = iscsi_sw_tcp_conn_create,
.bind_conn = iscsi_sw_tcp_conn_bind,
.destroy_conn = iscsi_sw_tcp_conn_destroy,
+ .attr_is_visible = iscsi_sw_tcp_attr_is_visible,
.set_param = iscsi_sw_tcp_conn_set_param,
.get_conn_param = iscsi_sw_tcp_conn_get_param,
.get_session_param = iscsi_session_get_param,
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
index 666fe09378fa..c3e5d9fa6add 100644
--- a/drivers/scsi/iscsi_tcp.h
+++ b/drivers/scsi/iscsi_tcp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* iSCSI Initiator TCP Transport
* Copyright (C) 2004 Dmitry Yusupov
@@ -6,16 +7,6 @@
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
* maintained by open-iscsi@googlegroups.com
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* See the file COPYING included with this distribution for more details.
*/
@@ -37,22 +28,25 @@ struct iscsi_sw_tcp_send {
struct iscsi_sw_tcp_conn {
struct socket *sock;
+ /* Taken when accessing the sock from the netlink/sysfs interface */
+ struct mutex sock_lock;
+
+ struct work_struct recvwork;
+ bool queue_recv;
struct iscsi_sw_tcp_send out;
/* old values for socket callbacks */
- void (*old_data_ready)(struct sock *, int);
+ void (*old_data_ready)(struct sock *);
void (*old_state_change)(struct sock *);
void (*old_write_space)(struct sock *);
/* data and header digests */
- struct hash_desc tx_hash; /* CRC32C (Tx) */
- struct hash_desc rx_hash; /* CRC32C (Rx) */
+ u32 tx_crc; /* CRC32C (Tx) */
+ u32 rx_crc; /* CRC32C (Rx) */
/* MIB custom statistics */
uint32_t sendpage_failures_cnt;
uint32_t discontiguous_hdr_cnt;
-
- ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
};
struct iscsi_sw_tcp_host {
diff --git a/drivers/scsi/jazz_esp.c b/drivers/scsi/jazz_esp.c
index 08e26d4e3731..35137f5cfb3a 100644
--- a/drivers/scsi/jazz_esp.c
+++ b/drivers/scsi/jazz_esp.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* jazz_esp.c: ESP front-end for MIPS JAZZ systems.
*
- * Copyright (C) 2007 Thomas Bogendrfer (tsbogend@alpha.frankende)
+ * Copyright (C) 2007 Thomas Bogendörfer (tsbogend@alpha.frankende)
*/
#include <linux/kernel.h>
@@ -38,30 +39,6 @@ static u8 jazz_esp_read8(struct esp *esp, unsigned long reg)
return *(volatile u8 *)(esp->regs + reg);
}
-static dma_addr_t jazz_esp_map_single(struct esp *esp, void *buf,
- size_t sz, int dir)
-{
- return dma_map_single(esp->dev, buf, sz, dir);
-}
-
-static int jazz_esp_map_sg(struct esp *esp, struct scatterlist *sg,
- int num_sg, int dir)
-{
- return dma_map_sg(esp->dev, sg, num_sg, dir);
-}
-
-static void jazz_esp_unmap_single(struct esp *esp, dma_addr_t addr,
- size_t sz, int dir)
-{
- dma_unmap_single(esp->dev, addr, sz, dir);
-}
-
-static void jazz_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
- int num_sg, int dir)
-{
- dma_unmap_sg(esp->dev, sg, num_sg, dir);
-}
-
static int jazz_esp_irq_pending(struct esp *esp)
{
if (jazz_esp_read8(esp, ESP_STATUS) & ESP_STAT_INTR)
@@ -117,10 +94,6 @@ static int jazz_esp_dma_error(struct esp *esp)
static const struct esp_driver_ops jazz_esp_ops = {
.esp_write8 = jazz_esp_write8,
.esp_read8 = jazz_esp_read8,
- .map_single = jazz_esp_map_single,
- .map_sg = jazz_esp_map_sg,
- .unmap_single = jazz_esp_unmap_single,
- .unmap_sg = jazz_esp_unmap_sg,
.irq_pending = jazz_esp_irq_pending,
.reset_dma = jazz_esp_reset_dma,
.dma_drain = jazz_esp_dma_drain,
@@ -129,9 +102,9 @@ static const struct esp_driver_ops jazz_esp_ops = {
.dma_error = jazz_esp_dma_error,
};
-static int __devinit esp_jazz_probe(struct platform_device *dev)
+static int esp_jazz_probe(struct platform_device *dev)
{
- struct scsi_host_template *tpnt = &scsi_esp_template;
+ const struct scsi_host_template *tpnt = &scsi_esp_template;
struct Scsi_Host *host;
struct esp *esp;
struct resource *res;
@@ -147,7 +120,7 @@ static int __devinit esp_jazz_probe(struct platform_device *dev)
esp = shost_priv(host);
esp->host = host;
- esp->dev = dev;
+ esp->dev = &dev->dev;
esp->ops = &jazz_esp_ops;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
@@ -170,7 +143,9 @@ static int __devinit esp_jazz_probe(struct platform_device *dev)
if (!esp->command_block)
goto fail_unmap_regs;
- host->irq = platform_get_irq(dev, 0);
+ host->irq = err = platform_get_irq(dev, 0);
+ if (err < 0)
+ goto fail_unmap_command_block;
err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp);
if (err < 0)
goto fail_unmap_command_block;
@@ -182,7 +157,7 @@ static int __devinit esp_jazz_probe(struct platform_device *dev)
dev_set_drvdata(&dev->dev, esp);
- err = scsi_esp_register(esp, &dev->dev);
+ err = scsi_esp_register(esp);
if (err)
goto fail_free_irq;
@@ -201,7 +176,7 @@ fail:
return err;
}
-static int __devexit esp_jazz_remove(struct platform_device *dev)
+static void esp_jazz_remove(struct platform_device *dev)
{
struct esp *esp = dev_get_drvdata(&dev->dev);
unsigned int irq = esp->host->irq;
@@ -214,8 +189,6 @@ static int __devexit esp_jazz_remove(struct platform_device *dev)
esp->command_block_dma);
scsi_host_put(esp->host);
-
- return 0;
}
/* work with hotplug and coldplug */
@@ -223,27 +196,14 @@ MODULE_ALIAS("platform:jazz_esp");
static struct platform_driver esp_jazz_driver = {
.probe = esp_jazz_probe,
- .remove = __devexit_p(esp_jazz_remove),
+ .remove = esp_jazz_remove,
.driver = {
.name = "jazz_esp",
- .owner = THIS_MODULE,
},
};
-
-static int __init jazz_esp_init(void)
-{
- return platform_driver_register(&esp_jazz_driver);
-}
-
-static void __exit jazz_esp_exit(void)
-{
- platform_driver_unregister(&esp_jazz_driver);
-}
+module_platform_driver(esp_jazz_driver);
MODULE_DESCRIPTION("JAZZ ESP SCSI driver");
-MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)");
+MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(jazz_esp_init);
-module_exit(jazz_esp_exit);
diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c
index 23880f8fe7e4..86fe19e0468d 100644
--- a/drivers/scsi/lasi700.c
+++ b/drivers/scsi/lasi700.c
@@ -1,23 +1,10 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
+// SPDX-License-Identifier: GPL-2.0-or-later
/* PARISC LASI driver for the 53c700 chip
*
* Copyright (C) 2001 by James.Bottomley@HansenPartnership.com
**-----------------------------------------------------------------------------
**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
*/
@@ -43,7 +30,6 @@
#include <linux/slab.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/parisc-device.h>
@@ -81,7 +67,7 @@ MODULE_LICENSE("GPL");
#define LASI710_CLOCK 40
#define LASI_SCSI_CORE_OFFSET 0x100
-static struct parisc_device_id lasi700_ids[] = {
+static const struct parisc_device_id lasi700_ids[] __initconst = {
LASI700_ID_TABLE,
LASI710_ID_TABLE,
{ 0 }
@@ -110,7 +96,7 @@ lasi700_probe(struct parisc_device *dev)
hostdata->dev = &dev->dev;
dma_set_mask(&dev->dev, DMA_BIT_MASK(32));
- hostdata->base = ioremap_nocache(base, 0x100);
+ hostdata->base = ioremap(base, 0x100);
hostdata->differential = 0;
if (dev->id.sversion == LASI_700_SVERSION) {
@@ -148,7 +134,7 @@ lasi700_probe(struct parisc_device *dev)
return -ENODEV;
}
-static int __exit
+static void __exit
lasi700_driver_remove(struct parisc_device *dev)
{
struct Scsi_Host *host = dev_get_drvdata(&dev->dev);
@@ -160,15 +146,13 @@ lasi700_driver_remove(struct parisc_device *dev)
free_irq(host->irq, host);
iounmap(hostdata->base);
kfree(hostdata);
-
- return 0;
}
-static struct parisc_driver lasi700_driver = {
+static struct parisc_driver lasi700_driver __refdata = {
.name = "lasi_scsi",
.id_table = lasi700_ids,
.probe = lasi700_probe,
- .remove = __devexit_p(lasi700_driver_remove),
+ .remove = __exit_p(lasi700_driver_remove),
};
static int __init
diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile
index 4bb23ac86a5c..65396f86c307 100644
--- a/drivers/scsi/libfc/Makefile
+++ b/drivers/scsi/libfc/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
# $Id: Makefile
obj-$(CONFIG_LIBFC) += libfc.o
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index b9cb8140b398..60d621ad0024 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -35,7 +23,10 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/err.h>
-#include <asm/unaligned.h>
+#include <linux/export.h>
+#include <linux/list.h>
+
+#include <linux/unaligned.h>
#include <scsi/fc/fc_gs.h>
@@ -56,30 +47,25 @@ static void fc_disc_restart(struct fc_disc *);
/**
* fc_disc_stop_rports() - Delete all the remote ports associated with the lport
* @disc: The discovery job to stop remote ports on
- *
- * Locking Note: This function expects that the lport mutex is locked before
- * calling it.
*/
-void fc_disc_stop_rports(struct fc_disc *disc)
+static void fc_disc_stop_rports(struct fc_disc *disc)
{
- struct fc_lport *lport;
struct fc_rport_priv *rdata;
- lport = fc_disc_lport(disc);
+ lockdep_assert_held(&disc->disc_mutex);
- mutex_lock(&disc->disc_mutex);
- list_for_each_entry_rcu(rdata, &disc->rports, peers)
- lport->tt.rport_logoff(rdata);
- mutex_unlock(&disc->disc_mutex);
+ list_for_each_entry(rdata, &disc->rports, peers) {
+ if (kref_get_unless_zero(&rdata->kref)) {
+ fc_rport_logoff(rdata);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
+ }
}
/**
* fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
* @disc: The discovery object to which the RSCN applies
* @fp: The RSCN frame
- *
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
*/
static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
{
@@ -89,11 +75,12 @@ static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
struct fc_seq_els_data rjt_data;
unsigned int len;
int redisc = 0;
- enum fc_els_rscn_ev_qual ev_qual;
enum fc_els_rscn_addr_fmt fmt;
LIST_HEAD(disc_ports);
struct fc_disc_port *dp, *next;
+ lockdep_assert_held(&disc->disc_mutex);
+
lport = fc_disc_lport(disc);
FC_DISC_DBG(disc, "Received an RSCN event\n");
@@ -119,8 +106,6 @@ static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
goto reject;
for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
- ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
- ev_qual &= ELS_RSCN_EV_QUAL_MASK;
fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
fmt &= ELS_RSCN_ADDR_FMT_MASK;
/*
@@ -149,7 +134,7 @@ static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
break;
}
}
- lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
+ fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
/*
* If not doing a complete rediscovery, do GPN_ID on
@@ -177,7 +162,7 @@ reject:
FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
rjt_data.reason = ELS_RJT_LOGIC;
rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
fc_frame_free(fp);
}
@@ -213,12 +198,11 @@ static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
/**
* fc_disc_restart() - Restart discovery
* @disc: The discovery object to be restarted
- *
- * Locking Note: This function expects that the disc mutex
- * is already locked.
*/
static void fc_disc_restart(struct fc_disc *disc)
{
+ lockdep_assert_held(&disc->disc_mutex);
+
if (!disc->disc_callback)
return;
@@ -264,16 +248,13 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
* fc_disc_done() - Discovery has been completed
* @disc: The discovery context
* @event: The discovery completion status
- *
- * Locking Note: This function expects that the disc mutex is locked before
- * it is called. The discovery callback is then made with the lock released,
- * and the lock is re-taken before returning from this function
*/
static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
{
struct fc_lport *lport = fc_disc_lport(disc);
struct fc_rport_priv *rdata;
+ lockdep_assert_held(&disc->disc_mutex);
FC_DISC_DBG(disc, "Discovery complete\n");
disc->pending = 0;
@@ -287,16 +268,21 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
* discovery, reverify or log them in. Otherwise, log them out.
* Skip ports which were never discovered. These are the dNS port
* and ports which were created by PLOGI.
+ *
+ * We don't need to use the _rcu variant here as the rport list
+ * is protected by the disc mutex which is already held on entry.
*/
- list_for_each_entry_rcu(rdata, &disc->rports, peers) {
- if (!rdata->disc_id)
+ list_for_each_entry(rdata, &disc->rports, peers) {
+ if (!kref_get_unless_zero(&rdata->kref))
continue;
- if (rdata->disc_id == disc->disc_id)
- lport->tt.rport_login(rdata);
- else
- lport->tt.rport_logoff(rdata);
+ if (rdata->disc_id) {
+ if (rdata->disc_id == disc->disc_id)
+ fc_rport_login(rdata);
+ else
+ fc_rport_logoff(rdata);
+ }
+ kref_put(&rdata->kref, fc_rport_destroy);
}
-
mutex_unlock(&disc->disc_mutex);
disc->disc_callback(lport, event);
mutex_lock(&disc->disc_mutex);
@@ -312,8 +298,8 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
struct fc_lport *lport = fc_disc_lport(disc);
unsigned long delay = 0;
- FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n",
- PTR_ERR(fp), disc->retry_count,
+ FC_DISC_DBG(disc, "Error %d, retries %d/%d\n",
+ PTR_ERR_OR_ZERO(fp), disc->retry_count,
FC_DISC_RETRY_LIMIT);
if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
@@ -336,21 +322,27 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
schedule_delayed_work(&disc->disc_work, delay);
} else
fc_disc_done(disc, DISC_EV_FAILED);
+ } else if (PTR_ERR(fp) == -FC_EX_CLOSED) {
+ /*
+ * if discovery fails due to lport reset, clear
+ * pending flag so that subsequent discovery can
+ * continue
+ */
+ disc->pending = 0;
}
}
/**
* fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
- * @lport: The discovery context
- *
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
+ * @disc: The discovery context
*/
static void fc_disc_gpn_ft_req(struct fc_disc *disc)
{
struct fc_frame *fp;
struct fc_lport *lport = fc_disc_lport(disc);
+ lockdep_assert_held(&disc->disc_mutex);
+
WARN_ON(!fc_lport_test_ready(lport));
disc->pending = 1;
@@ -375,7 +367,7 @@ err:
/**
* fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
- * @lport: The local port the GPN_FT was received on
+ * @disc: The discovery context
* @buf: The GPN_FT response buffer
* @len: The size of response buffer
*
@@ -438,7 +430,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
if (ids.port_id != lport->port_id &&
ids.port_name != lport->wwpn) {
- rdata = lport->tt.rport_create(lport, ids.port_id);
+ rdata = fc_rport_create(lport, ids.port_id);
if (rdata) {
rdata->ids.port_name = ids.port_name;
rdata->disc_id = disc->disc_id;
@@ -493,7 +485,7 @@ static void fc_disc_timeout(struct work_struct *work)
* fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
* @sp: The sequence that the GPN_FT response was received on
* @fp: The GPN_FT response frame
- * @lp_arg: The discovery context
+ * @disc_arg: The discovery context
*
* Locking Note: This function is called without disc mutex held, and
* should do all its processing with the mutex held
@@ -555,7 +547,7 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
event = DISC_EV_FAILED;
}
if (error)
- fc_disc_error(disc, fp);
+ fc_disc_error(disc, ERR_PTR(error));
else if (event != DISC_EV_NONE)
fc_disc_done(disc, event);
fc_frame_free(fp);
@@ -584,11 +576,14 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
lport = rdata->local_port;
disc = &lport->disc;
- mutex_lock(&disc->disc_mutex);
if (PTR_ERR(fp) == -FC_EX_CLOSED)
goto out;
- if (IS_ERR(fp))
- goto redisc;
+ if (IS_ERR(fp)) {
+ mutex_lock(&disc->disc_mutex);
+ fc_disc_restart(disc);
+ mutex_unlock(&disc->disc_mutex);
+ goto out;
+ }
cp = fc_frame_payload_get(fp, sizeof(*cp));
if (!cp)
@@ -599,37 +594,43 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
goto redisc;
pn = (struct fc_ns_gid_pn *)(cp + 1);
port_name = get_unaligned_be64(&pn->fn_wwpn);
+ mutex_lock(&rdata->rp_mutex);
if (rdata->ids.port_name == -1)
rdata->ids.port_name = port_name;
else if (rdata->ids.port_name != port_name) {
FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
"Port-id %6.6x wwpn %16.16llx\n",
rdata->ids.port_id, port_name);
- lport->tt.rport_logoff(rdata);
-
- new_rdata = lport->tt.rport_create(lport,
- rdata->ids.port_id);
+ mutex_unlock(&rdata->rp_mutex);
+ fc_rport_logoff(rdata);
+ mutex_lock(&lport->disc.disc_mutex);
+ new_rdata = fc_rport_create(lport, rdata->ids.port_id);
+ mutex_unlock(&lport->disc.disc_mutex);
if (new_rdata) {
new_rdata->disc_id = disc->disc_id;
- lport->tt.rport_login(new_rdata);
+ fc_rport_login(new_rdata);
}
- goto out;
+ goto free_fp;
}
rdata->disc_id = disc->disc_id;
- lport->tt.rport_login(rdata);
+ mutex_unlock(&rdata->rp_mutex);
+ fc_rport_login(rdata);
} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
cp->ct_reason, cp->ct_explan);
- lport->tt.rport_logoff(rdata);
+ fc_rport_logoff(rdata);
} else {
FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
ntohs(cp->ct_cmd));
redisc:
+ mutex_lock(&disc->disc_mutex);
fc_disc_restart(disc);
+ mutex_unlock(&disc->disc_mutex);
}
+free_fp:
+ fc_frame_free(fp);
out:
- mutex_unlock(&disc->disc_mutex);
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
@@ -637,8 +638,6 @@ out:
* @lport: The local port to initiate discovery on
* @rdata: remote port private data
*
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
* On failure, an error code is returned.
*/
static int fc_disc_gpn_id_req(struct fc_lport *lport,
@@ -646,6 +645,7 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
{
struct fc_frame *fp;
+ lockdep_assert_held(&lport->disc.disc_mutex);
fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
sizeof(struct fc_ns_fid));
if (!fp)
@@ -662,15 +662,14 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
* fc_disc_single() - Discover the directory information for a single target
* @lport: The local port the remote port is associated with
* @dp: The port to rediscover
- *
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
*/
static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
{
struct fc_rport_priv *rdata;
- rdata = lport->tt.rport_create(lport, dp->port_id);
+ lockdep_assert_held(&lport->disc.disc_mutex);
+
+ rdata = fc_rport_create(lport, dp->port_id);
if (!rdata)
return -ENOMEM;
rdata->disc_id = 0;
@@ -681,13 +680,15 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
* fc_disc_stop() - Stop discovery for a given lport
* @lport: The local port that discovery should stop on
*/
-void fc_disc_stop(struct fc_lport *lport)
+static void fc_disc_stop(struct fc_lport *lport)
{
struct fc_disc *disc = &lport->disc;
if (disc->pending)
cancel_delayed_work_sync(&disc->disc_work);
+ mutex_lock(&disc->disc_mutex);
fc_disc_stop_rports(disc);
+ mutex_unlock(&disc->disc_mutex);
}
/**
@@ -697,17 +698,18 @@ void fc_disc_stop(struct fc_lport *lport)
* This function will block until discovery has been
* completely stopped and all rports have been deleted.
*/
-void fc_disc_stop_final(struct fc_lport *lport)
+static void fc_disc_stop_final(struct fc_lport *lport)
{
fc_disc_stop(lport);
- lport->tt.rport_flush_queue();
+ fc_rport_flush_queue();
}
/**
- * fc_disc_init() - Initialize the discovery layer for a local port
- * @lport: The local port that needs the discovery layer to be initialized
+ * fc_disc_config() - Configure the discovery layer for a local port
+ * @lport: The local port that needs the discovery layer to be configured
+ * @priv: Private data structre for users of the discovery layer
*/
-int fc_disc_init(struct fc_lport *lport)
+void fc_disc_config(struct fc_lport *lport, void *priv)
{
struct fc_disc *disc;
@@ -724,12 +726,21 @@ int fc_disc_init(struct fc_lport *lport)
lport->tt.disc_recv_req = fc_disc_recv_req;
disc = &lport->disc;
+
+ disc->priv = priv;
+}
+EXPORT_SYMBOL(fc_disc_config);
+
+/**
+ * fc_disc_init() - Initialize the discovery layer for a local port
+ * @lport: The local port that needs the discovery layer to be initialized
+ */
+void fc_disc_init(struct fc_lport *lport)
+{
+ struct fc_disc *disc = &lport->disc;
+
INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
mutex_init(&disc->disc_mutex);
INIT_LIST_HEAD(&disc->rports);
-
- disc->priv = lport;
-
- return 0;
}
EXPORT_SYMBOL(fc_disc_init);
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index 9b25969e2ad0..4fa18a317f77 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2008 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -21,12 +9,14 @@
* Provide interface to send ELS/CT FC frames
*/
-#include <asm/unaligned.h>
+#include <linux/export.h>
+#include <linux/unaligned.h>
#include <scsi/fc/fc_gs.h>
#include <scsi/fc/fc_ns.h>
#include <scsi/fc/fc_els.h>
#include <scsi/libfc.h>
-#include <scsi/fc_encode.h>
+#include "fc_encode.h"
+#include "fc_libfc.h"
/**
* fc_elsct_send() - Send an ELS or CT frame
@@ -54,8 +44,7 @@ struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type);
else {
/* CT requests */
- rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type);
- did = FC_FID_DIR_SERV;
+ rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type, &did);
}
if (rc) {
@@ -66,7 +55,7 @@ struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
fc_fill_fc_hdr(fp, r_ctl, did, lport->port_id, fh_type,
FC_FCTL_REQ, 0);
- return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
+ return fc_exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
}
EXPORT_SYMBOL(fc_elsct_send);
diff --git a/drivers/scsi/libfc/fc_encode.h b/drivers/scsi/libfc/fc_encode.h
new file mode 100644
index 000000000000..e046091a549a
--- /dev/null
+++ b/drivers/scsi/libfc/fc_encode.h
@@ -0,0 +1,953 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_ENCODE_H_
+#define _FC_ENCODE_H_
+#include <linux/unaligned.h>
+#include <linux/utsname.h>
+#include <scsi/fc/fc_ms.h>
+
+/*
+ * F_CTL values for simple requests and responses.
+ */
+#define FC_FCTL_REQ (FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT)
+#define FC_FCTL_RESP (FC_FC_EX_CTX | FC_FC_LAST_SEQ | \
+ FC_FC_END_SEQ | FC_FC_SEQ_INIT)
+
+struct fc_ns_rft {
+ struct fc_ns_fid fid; /* port ID object */
+ struct fc_ns_fts fts; /* FC4-types object */
+};
+
+struct fc_ct_req {
+ struct fc_ct_hdr hdr;
+ union {
+ struct fc_ns_gid_ft gid;
+ struct fc_ns_rn_id rn;
+ struct fc_ns_rft rft;
+ struct fc_ns_rff_id rff;
+ struct fc_ns_fid fid;
+ struct fc_ns_rsnn snn;
+ struct fc_ns_rspn spn;
+ struct fc_fdmi_rhba rhba;
+ struct fc_fdmi_rpa rpa;
+ struct fc_fdmi_dprt dprt;
+ struct fc_fdmi_dhba dhba;
+ } payload;
+};
+
+/**
+ * fc_adisc_fill() - Fill in adisc request frame
+ * @lport: local port.
+ * @fp: fc frame where payload will be placed.
+ */
+static inline void fc_adisc_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_adisc *adisc;
+
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ memset(adisc, 0, sizeof(*adisc));
+ adisc->adisc_cmd = ELS_ADISC;
+ put_unaligned_be64(lport->wwpn, &adisc->adisc_wwpn);
+ put_unaligned_be64(lport->wwnn, &adisc->adisc_wwnn);
+ hton24(adisc->adisc_port_id, lport->port_id);
+}
+
+/**
+ * fc_ct_hdr_fill- fills ct header and reset ct payload
+ * returns pointer to ct request.
+ */
+static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp,
+ unsigned int op, size_t req_size,
+ enum fc_ct_fs_type fs_type,
+ u8 subtype)
+{
+ struct fc_ct_req *ct;
+ size_t ct_plen;
+
+ ct_plen = sizeof(struct fc_ct_hdr) + req_size;
+ ct = fc_frame_payload_get(fp, ct_plen);
+ memset(ct, 0, ct_plen);
+ ct->hdr.ct_rev = FC_CT_REV;
+ ct->hdr.ct_fs_type = fs_type;
+ ct->hdr.ct_fs_subtype = subtype;
+ ct->hdr.ct_cmd = htons((u16) op);
+ return ct;
+}
+
+/**
+ * fc_ct_ns_fill() - Fill in a name service request frame
+ * @lport: local port.
+ * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries.
+ * @fp: frame to contain payload.
+ * @op: CT opcode.
+ * @r_ctl: pointer to FC header R_CTL.
+ * @fh_type: pointer to FC-4 type.
+ */
+static inline int fc_ct_ns_fill(struct fc_lport *lport,
+ u32 fc_id, struct fc_frame *fp,
+ unsigned int op, enum fc_rctl *r_ctl,
+ enum fc_fh_type *fh_type)
+{
+ struct fc_ct_req *ct;
+ size_t len;
+
+ switch (op) {
+ case FC_NS_GPN_FT:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_gid_ft),
+ FC_FST_DIR, FC_NS_SUBTYPE);
+ ct->payload.gid.fn_fc4_type = FC_TYPE_FCP;
+ break;
+
+ case FC_NS_GPN_ID:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_fid),
+ FC_FST_DIR, FC_NS_SUBTYPE);
+ ct->payload.gid.fn_fc4_type = FC_TYPE_FCP;
+ hton24(ct->payload.fid.fp_fid, fc_id);
+ break;
+
+ case FC_NS_RFT_ID:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft),
+ FC_FST_DIR, FC_NS_SUBTYPE);
+ hton24(ct->payload.rft.fid.fp_fid, lport->port_id);
+ ct->payload.rft.fts = lport->fcts;
+ break;
+
+ case FC_NS_RFF_ID:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rff_id),
+ FC_FST_DIR, FC_NS_SUBTYPE);
+ hton24(ct->payload.rff.fr_fid.fp_fid, lport->port_id);
+ ct->payload.rff.fr_type = FC_TYPE_FCP;
+ if (lport->service_params & FCP_SPPF_INIT_FCN)
+ ct->payload.rff.fr_feat = FCP_FEAT_INIT;
+ if (lport->service_params & FCP_SPPF_TARG_FCN)
+ ct->payload.rff.fr_feat |= FCP_FEAT_TARG;
+ break;
+
+ case FC_NS_RNN_ID:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id),
+ FC_FST_DIR, FC_NS_SUBTYPE);
+ hton24(ct->payload.rn.fr_fid.fp_fid, lport->port_id);
+ put_unaligned_be64(lport->wwnn, &ct->payload.rn.fr_wwn);
+ break;
+
+ case FC_NS_RSPN_ID:
+ len = strnlen(fc_host_symbolic_name(lport->host),
+ FC_SYMBOLIC_NAME_SIZE);
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rspn) + len,
+ FC_FST_DIR, FC_NS_SUBTYPE);
+ hton24(ct->payload.spn.fr_fid.fp_fid, lport->port_id);
+ memcpy(ct->payload.spn.fr_name,
+ fc_host_symbolic_name(lport->host), len);
+ ct->payload.spn.fr_name_len = len;
+ break;
+
+ case FC_NS_RSNN_NN:
+ len = strnlen(fc_host_symbolic_name(lport->host),
+ FC_SYMBOLIC_NAME_SIZE);
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rsnn) + len,
+ FC_FST_DIR, FC_NS_SUBTYPE);
+ put_unaligned_be64(lport->wwnn, &ct->payload.snn.fr_wwn);
+ memcpy(ct->payload.snn.fr_name,
+ fc_host_symbolic_name(lport->host), len);
+ ct->payload.snn.fr_name_len = len;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ *r_ctl = FC_RCTL_DD_UNSOL_CTL;
+ *fh_type = FC_TYPE_CT;
+ return 0;
+}
+
+static inline void fc_ct_ms_fill_attr(struct fc_fdmi_attr_entry *entry,
+ const char *in, size_t len)
+{
+ int copied;
+
+ copied = strscpy(entry->value, in, len);
+ if (copied > 0 && copied + 1 < len)
+ memset(entry->value + copied + 1, 0, len - copied - 1);
+}
+
+/**
+ * fc_ct_ms_fill() - Fill in a mgmt service request frame
+ * @lport: local port.
+ * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries.
+ * @fp: frame to contain payload.
+ * @op: CT opcode.
+ * @r_ctl: pointer to FC header R_CTL.
+ * @fh_type: pointer to FC-4 type.
+ */
+static inline int fc_ct_ms_fill(struct fc_lport *lport,
+ u32 fc_id, struct fc_frame *fp,
+ unsigned int op, enum fc_rctl *r_ctl,
+ enum fc_fh_type *fh_type)
+{
+ struct fc_ct_req *ct;
+ size_t len;
+ struct fc_fdmi_attr_entry *entry;
+ struct fs_fdmi_attrs *hba_attrs;
+ int numattrs = 0;
+ struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host);
+
+ switch (op) {
+ case FC_FDMI_RHBA:
+ numattrs = 11;
+ len = sizeof(struct fc_fdmi_rhba);
+ len -= sizeof(struct fc_fdmi_attr_entry);
+ len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+ len += FC_FDMI_HBA_ATTR_NODENAME_LEN;
+ len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN;
+ len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN;
+ len += FC_FDMI_HBA_ATTR_MODEL_LEN;
+ len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN;
+ len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN;
+
+ if (fc_host->fdmi_version == FDMI_V2) {
+ numattrs += 7;
+ len += FC_FDMI_HBA_ATTR_NODESYMBLNAME_LEN;
+ len += FC_FDMI_HBA_ATTR_VENDORSPECIFICINFO_LEN;
+ len += FC_FDMI_HBA_ATTR_NUMBEROFPORTS_LEN;
+ len += FC_FDMI_HBA_ATTR_FABRICNAME_LEN;
+ len += FC_FDMI_HBA_ATTR_BIOSVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_BIOSSTATE_LEN;
+ len += FC_FDMI_HBA_ATTR_VENDORIDENTIFIER_LEN;
+ }
+
+ ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT,
+ FC_FDMI_SUBTYPE);
+
+ /* HBA Identifier */
+ put_unaligned_be64(lport->wwpn, &ct->payload.rhba.hbaid.id);
+ /* Number of Ports - always 1 */
+ put_unaligned_be32(1, &ct->payload.rhba.port.numport);
+ /* Port Name */
+ put_unaligned_be64(lport->wwpn,
+ &ct->payload.rhba.port.port[0].portname);
+
+ /* HBA Attributes */
+ put_unaligned_be32(numattrs,
+ &ct->payload.rhba.hba_attrs.numattrs);
+ hba_attrs = &ct->payload.rhba.hba_attrs;
+ entry = (struct fc_fdmi_attr_entry *)hba_attrs->attr;
+ /* NodeName*/
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_NODENAME_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_NODENAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be64(lport->wwnn,
+ (__be64 *)&entry->value);
+
+ /* Manufacturer */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_NODENAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_MANUFACTURER,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_manufacturer(lport->host),
+ FC_FDMI_HBA_ATTR_MANUFACTURER_LEN);
+
+ /* SerialNumber */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_MANUFACTURER_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_SERIALNUMBER,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_serial_number(lport->host),
+ FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN);
+
+ /* Model */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_MODEL_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_MODEL,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_model(lport->host),
+ FC_FDMI_HBA_ATTR_MODEL_LEN);
+
+ /* Model Description */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_MODEL_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_MODELDESCRIPTION,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_model_description(lport->host),
+ FC_FDMI_HBA_ATTR_MODELDESCR_LEN);
+
+ /* Hardware Version */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_MODELDESCR_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_HARDWAREVERSION,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_hardware_version(lport->host),
+ FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN);
+
+ /* Driver Version */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_DRIVERVERSION,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_driver_version(lport->host),
+ FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN);
+
+ /* OptionROM Version */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_OPTIONROMVERSION,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ "unknown",
+ FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN);
+
+ /* Firmware Version */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_FIRMWAREVERSION,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_firmware_version(lport->host),
+ FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN);
+
+ /* OS Name and Version */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_OSNAMEVERSION,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ snprintf((char *)&entry->value,
+ FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN,
+ "%.62s v%.62s",
+ init_utsname()->sysname,
+ init_utsname()->release);
+
+ /* Max CT payload */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_MAXCTPAYLOAD,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_max_ct_payload(lport->host),
+ &entry->value);
+
+ if (fc_host->fdmi_version == FDMI_V2) {
+ /* Node symbolic name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_NODESYMBLNAME_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_NODESYMBLNAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_symbolic_name(lport->host),
+ FC_FDMI_HBA_ATTR_NODESYMBLNAME_LEN);
+
+ /* Vendor specific info */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_NODESYMBLNAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_VENDORSPECIFICINFO_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_VENDORSPECIFICINFO,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(0,
+ &entry->value);
+
+ /* Number of ports */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_VENDORSPECIFICINFO_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_NUMBEROFPORTS_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_NUMBEROFPORTS,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_num_ports(lport->host),
+ &entry->value);
+
+ /* Fabric name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_NUMBEROFPORTS_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_FABRICNAME_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_FABRICNAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be64(fc_host_fabric_name(lport->host),
+ &entry->value);
+
+ /* BIOS version */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_FABRICNAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_BIOSVERSION_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_BIOSVERSION,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_bootbios_version(lport->host),
+ FC_FDMI_HBA_ATTR_BIOSVERSION_LEN);
+
+ /* BIOS state */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_BIOSVERSION_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_BIOSSTATE_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_BIOSSTATE,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_bootbios_state(lport->host),
+ &entry->value);
+
+ /* Vendor identifier */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_HBA_ATTR_BIOSSTATE_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_HBA_ATTR_VENDORIDENTIFIER_LEN;
+ put_unaligned_be16(FC_FDMI_HBA_ATTR_VENDORIDENTIFIER,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_vendor_identifier(lport->host),
+ FC_FDMI_HBA_ATTR_VENDORIDENTIFIER_LEN);
+ }
+
+ break;
+ case FC_FDMI_RPA:
+ numattrs = 6;
+ len = sizeof(struct fc_fdmi_rpa);
+ len -= sizeof(struct fc_fdmi_attr_entry);
+ len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+ len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN;
+ len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN;
+ len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN;
+ len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN;
+ len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN;
+ len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN;
+
+
+ if (fc_host->fdmi_version == FDMI_V2) {
+ numattrs += 10;
+
+ len += FC_FDMI_PORT_ATTR_NODENAME_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTNAME_LEN;
+ len += FC_FDMI_PORT_ATTR_SYMBOLICNAME_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTTYPE_LEN;
+ len += FC_FDMI_PORT_ATTR_SUPPORTEDCLASSSRVC_LEN;
+ len += FC_FDMI_PORT_ATTR_FABRICNAME_LEN;
+ len += FC_FDMI_PORT_ATTR_CURRENTFC4TYPE_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTSTATE_LEN;
+ len += FC_FDMI_PORT_ATTR_DISCOVEREDPORTS_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTID_LEN;
+
+ }
+
+ ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT,
+ FC_FDMI_SUBTYPE);
+
+ /* Port Name */
+ put_unaligned_be64(lport->wwpn,
+ &ct->payload.rpa.port.portname);
+
+ /* Port Attributes */
+ put_unaligned_be32(numattrs,
+ &ct->payload.rpa.hba_attrs.numattrs);
+
+ hba_attrs = &ct->payload.rpa.hba_attrs;
+ entry = (struct fc_fdmi_attr_entry *)hba_attrs->attr;
+
+ /* FC4 types */
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_FC4TYPES,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ memcpy(&entry->value, fc_host_supported_fc4s(lport->host),
+ FC_FDMI_PORT_ATTR_FC4TYPES_LEN);
+
+ /* Supported Speed */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_FC4TYPES_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_SUPPORTEDSPEED,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+
+ put_unaligned_be32(fc_host_supported_speeds(lport->host),
+ &entry->value);
+
+ /* Current Port Speed */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_CURRENTPORTSPEED,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(lport->link_speed,
+ &entry->value);
+
+ /* Max Frame Size */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_MAXFRAMESIZE,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_maxframe_size(lport->host),
+ &entry->value);
+
+ /* OS Device Name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_OSDEVICENAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ /* Use the sysfs device name */
+ fc_ct_ms_fill_attr(entry,
+ dev_name(&lport->host->shost_gendev),
+ strnlen(dev_name(&lport->host->shost_gendev),
+ FC_FDMI_PORT_ATTR_HOSTNAME_LEN));
+
+ /* Host Name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_HOSTNAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ if (strlen(fc_host_system_hostname(lport->host)))
+ fc_ct_ms_fill_attr(entry,
+ fc_host_system_hostname(lport->host),
+ strnlen(fc_host_system_hostname(lport->host),
+ FC_FDMI_PORT_ATTR_HOSTNAME_LEN));
+ else
+ fc_ct_ms_fill_attr(entry,
+ init_utsname()->nodename,
+ FC_FDMI_PORT_ATTR_HOSTNAME_LEN);
+
+
+ if (fc_host->fdmi_version == FDMI_V2) {
+
+ /* Node name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_HOSTNAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_NODENAME_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_NODENAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be64(fc_host_node_name(lport->host),
+ &entry->value);
+
+ /* Port name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_NODENAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTNAME_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_PORTNAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be64(lport->wwpn,
+ &entry->value);
+
+ /* Port symbolic name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_PORTNAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_SYMBOLICNAME_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_SYMBOLICNAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ fc_ct_ms_fill_attr(entry,
+ fc_host_symbolic_name(lport->host),
+ FC_FDMI_PORT_ATTR_SYMBOLICNAME_LEN);
+
+ /* Port type */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_SYMBOLICNAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTTYPE_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_PORTTYPE,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_port_type(lport->host),
+ &entry->value);
+
+ /* Supported class of service */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_PORTTYPE_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_SUPPORTEDCLASSSRVC_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_SUPPORTEDCLASSSRVC,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_supported_classes(lport->host),
+ &entry->value);
+
+ /* Port Fabric name */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_SUPPORTEDCLASSSRVC_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_FABRICNAME_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_FABRICNAME,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be64(fc_host_fabric_name(lport->host),
+ &entry->value);
+
+ /* Port active FC-4 */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_FABRICNAME_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_CURRENTFC4TYPE_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_CURRENTFC4TYPE,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ memcpy(&entry->value, fc_host_active_fc4s(lport->host),
+ FC_FDMI_PORT_ATTR_CURRENTFC4TYPE_LEN);
+
+ /* Port state */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_CURRENTFC4TYPE_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTSTATE_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_PORTSTATE,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_port_state(lport->host),
+ &entry->value);
+
+ /* Discovered ports */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_PORTSTATE_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_DISCOVEREDPORTS_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_DISCOVEREDPORTS,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_num_discovered_ports(lport->host),
+ &entry->value);
+
+ /* Port ID */
+ entry = (struct fc_fdmi_attr_entry *)((char *)entry->value +
+ FC_FDMI_PORT_ATTR_DISCOVEREDPORTS_LEN);
+ len = FC_FDMI_ATTR_ENTRY_HEADER_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTID_LEN;
+ put_unaligned_be16(FC_FDMI_PORT_ATTR_PORTID,
+ &entry->type);
+ put_unaligned_be16(len, &entry->len);
+ put_unaligned_be32(fc_host_port_id(lport->host),
+ &entry->value);
+ }
+
+ break;
+ case FC_FDMI_DPRT:
+ len = sizeof(struct fc_fdmi_dprt);
+ ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT,
+ FC_FDMI_SUBTYPE);
+ /* Port Name */
+ put_unaligned_be64(lport->wwpn,
+ &ct->payload.dprt.port.portname);
+ break;
+ case FC_FDMI_DHBA:
+ len = sizeof(struct fc_fdmi_dhba);
+ ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT,
+ FC_FDMI_SUBTYPE);
+ /* HBA Identifier */
+ put_unaligned_be64(lport->wwpn, &ct->payload.dhba.hbaid.id);
+ break;
+ default:
+ return -EINVAL;
+ }
+ *r_ctl = FC_RCTL_DD_UNSOL_CTL;
+ *fh_type = FC_TYPE_CT;
+ return 0;
+}
+
+/**
+ * fc_ct_fill() - Fill in a common transport service request frame
+ * @lport: local port.
+ * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries.
+ * @fp: frame to contain payload.
+ * @op: CT opcode.
+ * @r_ctl: pointer to FC header R_CTL.
+ * @fh_type: pointer to FC-4 type.
+ */
+static inline int fc_ct_fill(struct fc_lport *lport,
+ u32 fc_id, struct fc_frame *fp,
+ unsigned int op, enum fc_rctl *r_ctl,
+ enum fc_fh_type *fh_type, u32 *did)
+{
+ int rc = -EINVAL;
+
+ switch (fc_id) {
+ case FC_FID_MGMT_SERV:
+ rc = fc_ct_ms_fill(lport, fc_id, fp, op, r_ctl, fh_type);
+ *did = FC_FID_MGMT_SERV;
+ break;
+ case FC_FID_DIR_SERV:
+ default:
+ rc = fc_ct_ns_fill(lport, fc_id, fp, op, r_ctl, fh_type);
+ *did = FC_FID_DIR_SERV;
+ break;
+ }
+
+ return rc;
+}
+/**
+ * fc_plogi_fill - Fill in plogi request frame
+ */
+static inline void fc_plogi_fill(struct fc_lport *lport, struct fc_frame *fp,
+ unsigned int op)
+{
+ struct fc_els_flogi *plogi;
+ struct fc_els_csp *csp;
+ struct fc_els_cssp *cp;
+
+ plogi = fc_frame_payload_get(fp, sizeof(*plogi));
+ memset(plogi, 0, sizeof(*plogi));
+ plogi->fl_cmd = (u8) op;
+ put_unaligned_be64(lport->wwpn, &plogi->fl_wwpn);
+ put_unaligned_be64(lport->wwnn, &plogi->fl_wwnn);
+
+ csp = &plogi->fl_csp;
+ csp->sp_hi_ver = 0x20;
+ csp->sp_lo_ver = 0x20;
+ csp->sp_bb_cred = htons(10); /* this gets set by gateway */
+ csp->sp_bb_data = htons((u16) lport->mfs);
+ cp = &plogi->fl_cssp[3 - 1]; /* class 3 parameters */
+ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+ csp->sp_features = htons(FC_SP_FT_CIRO);
+ csp->sp_tot_seq = htons(255); /* seq. we accept */
+ csp->sp_rel_off = htons(0x1f);
+ csp->sp_e_d_tov = htonl(lport->e_d_tov);
+
+ cp->cp_rdfs = htons((u16) lport->mfs);
+ cp->cp_con_seq = htons(255);
+ cp->cp_open_seq = 1;
+}
+
+/**
+ * fc_flogi_fill - Fill in a flogi request frame.
+ */
+static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_csp *sp;
+ struct fc_els_cssp *cp;
+ struct fc_els_flogi *flogi;
+
+ flogi = fc_frame_payload_get(fp, sizeof(*flogi));
+ memset(flogi, 0, sizeof(*flogi));
+ flogi->fl_cmd = (u8) ELS_FLOGI;
+ put_unaligned_be64(lport->wwpn, &flogi->fl_wwpn);
+ put_unaligned_be64(lport->wwnn, &flogi->fl_wwnn);
+ sp = &flogi->fl_csp;
+ sp->sp_hi_ver = 0x20;
+ sp->sp_lo_ver = 0x20;
+ sp->sp_bb_cred = htons(10); /* this gets set by gateway */
+ sp->sp_bb_data = htons((u16) lport->mfs);
+ cp = &flogi->fl_cssp[3 - 1]; /* class 3 parameters */
+ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+ if (lport->does_npiv)
+ sp->sp_features = htons(FC_SP_FT_NPIV);
+}
+
+/**
+ * fc_fdisc_fill - Fill in a fdisc request frame.
+ */
+static inline void fc_fdisc_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_csp *sp;
+ struct fc_els_cssp *cp;
+ struct fc_els_flogi *fdisc;
+
+ fdisc = fc_frame_payload_get(fp, sizeof(*fdisc));
+ memset(fdisc, 0, sizeof(*fdisc));
+ fdisc->fl_cmd = (u8) ELS_FDISC;
+ put_unaligned_be64(lport->wwpn, &fdisc->fl_wwpn);
+ put_unaligned_be64(lport->wwnn, &fdisc->fl_wwnn);
+ sp = &fdisc->fl_csp;
+ sp->sp_hi_ver = 0x20;
+ sp->sp_lo_ver = 0x20;
+ sp->sp_bb_cred = htons(10); /* this gets set by gateway */
+ sp->sp_bb_data = htons((u16) lport->mfs);
+ cp = &fdisc->fl_cssp[3 - 1]; /* class 3 parameters */
+ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+}
+
+/**
+ * fc_logo_fill - Fill in a logo request frame.
+ */
+static inline void fc_logo_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_logo *logo;
+
+ logo = fc_frame_payload_get(fp, sizeof(*logo));
+ memset(logo, 0, sizeof(*logo));
+ logo->fl_cmd = ELS_LOGO;
+ hton24(logo->fl_n_port_id, lport->port_id);
+ logo->fl_n_port_wwn = htonll(lport->wwpn);
+}
+
+/**
+ * fc_rtv_fill - Fill in RTV (read timeout value) request frame.
+ */
+static inline void fc_rtv_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_rtv *rtv;
+
+ rtv = fc_frame_payload_get(fp, sizeof(*rtv));
+ memset(rtv, 0, sizeof(*rtv));
+ rtv->rtv_cmd = ELS_RTV;
+}
+
+/**
+ * fc_rec_fill - Fill in rec request frame
+ */
+static inline void fc_rec_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_rec *rec;
+ struct fc_exch *ep = fc_seq_exch(fr_seq(fp));
+
+ rec = fc_frame_payload_get(fp, sizeof(*rec));
+ memset(rec, 0, sizeof(*rec));
+ rec->rec_cmd = ELS_REC;
+ hton24(rec->rec_s_id, lport->port_id);
+ rec->rec_ox_id = htons(ep->oxid);
+ rec->rec_rx_id = htons(ep->rxid);
+}
+
+/**
+ * fc_prli_fill - Fill in prli request frame
+ */
+static inline void fc_prli_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+
+ pp = fc_frame_payload_get(fp, sizeof(*pp));
+ memset(pp, 0, sizeof(*pp));
+ pp->prli.prli_cmd = ELS_PRLI;
+ pp->prli.prli_spp_len = sizeof(struct fc_els_spp);
+ pp->prli.prli_len = htons(sizeof(*pp));
+ pp->spp.spp_type = FC_TYPE_FCP;
+ pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
+ pp->spp.spp_params = htonl(lport->service_params);
+}
+
+/**
+ * fc_scr_fill - Fill in a scr request frame.
+ */
+static inline void fc_scr_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_scr *scr;
+
+ scr = fc_frame_payload_get(fp, sizeof(*scr));
+ memset(scr, 0, sizeof(*scr));
+ scr->scr_cmd = ELS_SCR;
+ scr->scr_reg_func = ELS_SCRF_FULL;
+}
+
+/**
+ * fc_els_fill - Fill in an ELS request frame
+ */
+static inline int fc_els_fill(struct fc_lport *lport,
+ u32 did,
+ struct fc_frame *fp, unsigned int op,
+ enum fc_rctl *r_ctl, enum fc_fh_type *fh_type)
+{
+ switch (op) {
+ case ELS_ADISC:
+ fc_adisc_fill(lport, fp);
+ break;
+
+ case ELS_PLOGI:
+ fc_plogi_fill(lport, fp, ELS_PLOGI);
+ break;
+
+ case ELS_FLOGI:
+ fc_flogi_fill(lport, fp);
+ break;
+
+ case ELS_FDISC:
+ fc_fdisc_fill(lport, fp);
+ break;
+
+ case ELS_LOGO:
+ fc_logo_fill(lport, fp);
+ break;
+
+ case ELS_RTV:
+ fc_rtv_fill(lport, fp);
+ break;
+
+ case ELS_REC:
+ fc_rec_fill(lport, fp);
+ break;
+
+ case ELS_PRLI:
+ fc_prli_fill(lport, fp);
+ break;
+
+ case ELS_SCR:
+ fc_scr_fill(lport, fp);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ *r_ctl = FC_RCTL_ELS_REQ;
+ *fh_type = FC_TYPE_ELS;
+ return 0;
+}
+#endif /* _FC_ENCODE_H_ */
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 3b8a6451ea28..f84a7e6ae379 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2007 Intel Corporation. All rights reserved.
* Copyright(c) 2008 Red Hat, Inc. All rights reserved.
* Copyright(c) 2008 Mike Christie
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -26,11 +14,12 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/log2.h>
#include <scsi/fc/fc_fc2.h>
#include <scsi/libfc.h>
-#include <scsi/fc_encode.h>
#include "fc_libfc.h"
@@ -59,22 +48,22 @@ static struct workqueue_struct *fc_exch_workqueue;
* @total_exches: Total allocated exchanges
* @lock: Exch pool lock
* @ex_list: List of exchanges
+ * @left: Cache of free slot in exch array
+ * @right: Cache of free slot in exch array
*
* This structure manages per cpu exchanges in array of exchange pointers.
* This array is allocated followed by struct fc_exch_pool memory for
* assigned range of exchanges to per cpu pool.
*/
struct fc_exch_pool {
+ spinlock_t lock;
+ struct list_head ex_list;
u16 next_index;
u16 total_exches;
- /* two cache of free slot in exch array */
u16 left;
u16 right;
-
- spinlock_t lock;
- struct list_head ex_list;
-};
+} ____cacheline_aligned_in_smp;
/**
* struct fc_exch_mgr - The Exchange Manager (EM).
@@ -85,25 +74,22 @@ struct fc_exch_pool {
* @ep_pool: Reserved exchange pointers
* @pool_max_index: Max exch array index in exch pool
* @pool: Per cpu exch pool
+ * @lport: Local exchange port
* @stats: Statistics structure
*
* This structure is the center for creating exchanges and sequences.
* It manages the allocation of exchange IDs.
*/
struct fc_exch_mgr {
+ struct fc_exch_pool __percpu *pool;
+ mempool_t *ep_pool;
+ struct fc_lport *lport;
enum fc_class class;
struct kref kref;
u16 min_xid;
u16 max_xid;
- mempool_t *ep_pool;
u16 pool_max_index;
- struct fc_exch_pool *pool;
- /*
- * currently exchange mgr stats are updated but not used.
- * either stats can be expose via sysfs or remove them
- * all together if not used XXX
- */
struct {
atomic_t no_free_exch;
atomic_t no_free_exch_xid;
@@ -124,7 +110,7 @@ struct fc_exch_mgr {
* for each anchor to determine if that EM should be used. The last
* anchor in the list will always match to handle any exchanges not
* handled by other EMs. The non-default EMs would be added to the
- * anchor list by HW that provides FCoE offloads.
+ * anchor list by HW that provides offloads.
*/
struct fc_exch_mgr_anchor {
struct list_head ema_list;
@@ -285,7 +271,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
if (f_ctl & FC_FC_END_SEQ) {
fr_eof(fp) = FC_EOF_T;
- if (fc_sof_needs_ack(ep->class))
+ if (fc_sof_needs_ack((enum fc_sof)ep->class))
fr_eof(fp) = FC_EOF_N;
/*
* From F_CTL.
@@ -308,10 +294,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
fr_eof(fp) = FC_EOF_N;
}
- /*
- * Initialize remainig fh fields
- * from fc_fill_fc_hdr
- */
+ /* Initialize remaining fh fields from fc_fill_fc_hdr */
fh->fh_ox_id = htons(ep->oxid);
fh->fh_rx_id = htons(ep->rxid);
fh->fh_seq_id = ep->seq.id;
@@ -339,8 +322,59 @@ static void fc_exch_release(struct fc_exch *ep)
}
/**
+ * fc_exch_timer_cancel() - cancel exch timer
+ * @ep: The exchange whose timer to be canceled
+ */
+static inline void fc_exch_timer_cancel(struct fc_exch *ep)
+{
+ if (cancel_delayed_work(&ep->timeout_work)) {
+ FC_EXCH_DBG(ep, "Exchange timer canceled\n");
+ atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
+ }
+}
+
+/**
+ * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the
+ * the exchange lock held
+ * @ep: The exchange whose timer will start
+ * @timer_msec: The timeout period
+ *
+ * Used for upper level protocols to time out the exchange.
+ * The timer is cancelled when it fires or when the exchange completes.
+ */
+static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
+ unsigned int timer_msec)
+{
+ if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
+ return;
+
+ FC_EXCH_DBG(ep, "Exchange timer armed : %d msecs\n", timer_msec);
+
+ fc_exch_hold(ep); /* hold for timer */
+ if (!queue_delayed_work(fc_exch_workqueue, &ep->timeout_work,
+ msecs_to_jiffies(timer_msec))) {
+ FC_EXCH_DBG(ep, "Exchange already queued\n");
+ fc_exch_release(ep);
+ }
+}
+
+/**
+ * fc_exch_timer_set() - Lock the exchange and set the timer
+ * @ep: The exchange whose timer will start
+ * @timer_msec: The timeout period
+ */
+static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
+{
+ spin_lock_bh(&ep->ex_lock);
+ fc_exch_timer_set_locked(ep, timer_msec);
+ spin_unlock_bh(&ep->ex_lock);
+}
+
+/**
* fc_exch_done_locked() - Complete an exchange with the exchange lock held
* @ep: The exchange that is complete
+ *
+ * Note: May sleep if invoked from outside a response handler.
*/
static int fc_exch_done_locked(struct fc_exch *ep)
{
@@ -352,20 +386,20 @@ static int fc_exch_done_locked(struct fc_exch *ep)
* ep, and in that case we only clear the resp and set it as
* complete, so it can be reused by the timer to send the rrq.
*/
- ep->resp = NULL;
if (ep->state & FC_EX_DONE)
return rc;
ep->esb_stat |= ESB_ST_COMPLETE;
if (!(ep->esb_stat & ESB_ST_REC_QUAL)) {
ep->state |= FC_EX_DONE;
- if (cancel_delayed_work(&ep->timeout_work))
- atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
+ fc_exch_timer_cancel(ep);
rc = 0;
}
return rc;
}
+static struct fc_exch fc_quarantine_exch;
+
/**
* fc_exch_ptr_get() - Return an exchange from an exchange pool
* @pool: Exchange Pool to get an exchange from
@@ -410,69 +444,39 @@ static void fc_exch_delete(struct fc_exch *ep)
/* update cache of free slot */
index = (ep->xid - ep->em->min_xid) >> fc_cpu_order;
- if (pool->left == FC_XID_UNKNOWN)
- pool->left = index;
- else if (pool->right == FC_XID_UNKNOWN)
- pool->right = index;
- else
- pool->next_index = index;
-
- fc_exch_ptr_set(pool, index, NULL);
+ if (!(ep->state & FC_EX_QUARANTINE)) {
+ if (pool->left == FC_XID_UNKNOWN)
+ pool->left = index;
+ else if (pool->right == FC_XID_UNKNOWN)
+ pool->right = index;
+ else
+ pool->next_index = index;
+ fc_exch_ptr_set(pool, index, NULL);
+ } else {
+ fc_exch_ptr_set(pool, index, &fc_quarantine_exch);
+ }
list_del(&ep->ex_list);
spin_unlock_bh(&pool->lock);
fc_exch_release(ep); /* drop hold for exch in mp */
}
-/**
- * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the
- * the exchange lock held
- * @ep: The exchange whose timer will start
- * @timer_msec: The timeout period
- *
- * Used for upper level protocols to time out the exchange.
- * The timer is cancelled when it fires or when the exchange completes.
- */
-static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
- unsigned int timer_msec)
-{
- if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
- return;
-
- FC_EXCH_DBG(ep, "Exchange timer armed\n");
-
- if (queue_delayed_work(fc_exch_workqueue, &ep->timeout_work,
- msecs_to_jiffies(timer_msec)))
- fc_exch_hold(ep); /* hold for timer */
-}
-
-/**
- * fc_exch_timer_set() - Lock the exchange and set the timer
- * @ep: The exchange whose timer will start
- * @timer_msec: The timeout period
- */
-static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
-{
- spin_lock_bh(&ep->ex_lock);
- fc_exch_timer_set_locked(ep, timer_msec);
- spin_unlock_bh(&ep->ex_lock);
-}
-
-/**
- * fc_seq_send() - Send a frame using existing sequence/exchange pair
- * @lport: The local port that the exchange will be sent on
- * @sp: The sequence to be sent
- * @fp: The frame to be sent on the exchange
- */
-static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
- struct fc_frame *fp)
+static int fc_seq_send_locked(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
{
struct fc_exch *ep;
struct fc_frame_header *fh = fc_frame_header_get(fp);
- int error;
+ int error = -ENXIO;
u32 f_ctl;
+ u8 fh_type = fh->fh_type;
ep = fc_seq_exch(sp);
- WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
+
+ if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL)) {
+ fc_frame_free(fp);
+ goto out;
+ }
+
+ WARN_ON(!(ep->esb_stat & ESB_ST_SEQ_INIT));
f_ctl = ntoh24(fh->fh_f_ctl);
fc_exch_setup_hdr(ep, fp, f_ctl);
@@ -494,18 +498,41 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
*/
error = lport->tt.frame_send(lport, fp);
+ if (fh_type == FC_TYPE_BLS)
+ goto out;
+
/*
* Update the exchange and sequence flags,
* assuming all frames for the sequence have been sent.
* We can only be called to send once for each sequence.
*/
- spin_lock_bh(&ep->ex_lock);
ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */
if (f_ctl & FC_FC_SEQ_INIT)
ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+out:
+ return error;
+}
+
+/**
+ * fc_seq_send() - Send a frame using existing sequence/exchange pair
+ * @lport: The local port that the exchange will be sent on
+ * @sp: The sequence to be sent
+ * @fp: The frame to be sent on the exchange
+ *
+ * Note: The frame will be freed either by a direct call to fc_frame_free(fp)
+ * or indirectly by calling libfc_function_template.frame_send().
+ */
+int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_exch *ep;
+ int error;
+ ep = fc_seq_exch(sp);
+ spin_lock_bh(&ep->ex_lock);
+ error = fc_seq_send_locked(lport, sp, fp);
spin_unlock_bh(&ep->ex_lock);
return error;
}
+EXPORT_SYMBOL(fc_seq_send);
/**
* fc_seq_alloc() - Allocate a sequence for a given exchange
@@ -547,7 +574,7 @@ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
* for a given sequence/exchange pair
* @sp: The sequence/exchange to get a new exchange for
*/
-static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
+struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
{
struct fc_exch *ep = fc_seq_exch(sp);
@@ -557,44 +584,64 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
return sp;
}
+EXPORT_SYMBOL(fc_seq_start_next);
/*
* Set the response handler for the exchange associated with a sequence.
+ *
+ * Note: May sleep if invoked from outside a response handler.
*/
-static void fc_seq_set_resp(struct fc_seq *sp,
- void (*resp)(struct fc_seq *, struct fc_frame *,
- void *),
- void *arg)
+void fc_seq_set_resp(struct fc_seq *sp,
+ void (*resp)(struct fc_seq *, struct fc_frame *, void *),
+ void *arg)
{
struct fc_exch *ep = fc_seq_exch(sp);
+ DEFINE_WAIT(wait);
spin_lock_bh(&ep->ex_lock);
+ while (ep->resp_active && ep->resp_task != current) {
+ prepare_to_wait(&ep->resp_wq, &wait, TASK_UNINTERRUPTIBLE);
+ spin_unlock_bh(&ep->ex_lock);
+
+ schedule();
+
+ spin_lock_bh(&ep->ex_lock);
+ }
+ finish_wait(&ep->resp_wq, &wait);
ep->resp = resp;
ep->arg = arg;
spin_unlock_bh(&ep->ex_lock);
}
+EXPORT_SYMBOL(fc_seq_set_resp);
/**
- * fc_seq_exch_abort() - Abort an exchange and sequence
- * @req_sp: The sequence to be aborted
+ * fc_exch_abort_locked() - Abort an exchange
+ * @ep: The exchange to be aborted
* @timer_msec: The period of time to wait before aborting
*
- * Generally called because of a timeout or an abort from the upper layer.
+ * Abort an exchange and sequence. Generally called because of a
+ * exchange timeout or an abort from the upper layer.
+ *
+ * A timer_msec can be specified for abort timeout, if non-zero
+ * timer_msec value is specified then exchange resp handler
+ * will be called with timeout error if no response to abort.
+ *
+ * Locking notes: Called with exch lock held
+ *
+ * Return value: 0 on success else error code
*/
-static int fc_seq_exch_abort(const struct fc_seq *req_sp,
- unsigned int timer_msec)
+static int fc_exch_abort_locked(struct fc_exch *ep,
+ unsigned int timer_msec)
{
struct fc_seq *sp;
- struct fc_exch *ep;
struct fc_frame *fp;
int error;
- ep = fc_seq_exch(req_sp);
-
- spin_lock_bh(&ep->ex_lock);
+ FC_EXCH_DBG(ep, "exch: abort, time %d msecs\n", timer_msec);
if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL) ||
ep->state & (FC_EX_DONE | FC_EX_RST_CLEANUP)) {
- spin_unlock_bh(&ep->ex_lock);
+ FC_EXCH_DBG(ep, "exch: already completed esb %x state %x\n",
+ ep->esb_stat, ep->state);
return -ENXIO;
}
@@ -602,37 +649,115 @@ static int fc_seq_exch_abort(const struct fc_seq *req_sp,
* Send the abort on a new sequence if possible.
*/
sp = fc_seq_start_next_locked(&ep->seq);
- if (!sp) {
- spin_unlock_bh(&ep->ex_lock);
+ if (!sp)
return -ENOMEM;
- }
- ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL;
if (timer_msec)
fc_exch_timer_set_locked(ep, timer_msec);
- spin_unlock_bh(&ep->ex_lock);
- /*
- * If not logged into the fabric, don't send ABTS but leave
- * sequence active until next timeout.
- */
- if (!ep->sid)
- return 0;
+ if (ep->sid) {
+ /*
+ * Send an abort for the sequence that timed out.
+ */
+ fp = fc_frame_alloc(ep->lp, 0);
+ if (fp) {
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+ fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid,
+ FC_TYPE_BLS, FC_FC_END_SEQ |
+ FC_FC_SEQ_INIT, 0);
+ error = fc_seq_send_locked(ep->lp, sp, fp);
+ } else {
+ error = -ENOBUFS;
+ }
+ } else {
+ /*
+ * If not logged into the fabric, don't send ABTS but leave
+ * sequence active until next timeout.
+ */
+ error = 0;
+ }
+ ep->esb_stat |= ESB_ST_ABNORMAL;
+ return error;
+}
- /*
- * Send an abort for the sequence that timed out.
- */
- fp = fc_frame_alloc(ep->lp, 0);
- if (fp) {
- fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid,
- FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- error = fc_seq_send(ep->lp, sp, fp);
- } else
- error = -ENOBUFS;
+/**
+ * fc_seq_exch_abort() - Abort an exchange and sequence
+ * @req_sp: The sequence to be aborted
+ * @timer_msec: The period of time to wait before aborting
+ *
+ * Generally called because of a timeout or an abort from the upper layer.
+ *
+ * Return value: 0 on success else error code
+ */
+int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
+{
+ struct fc_exch *ep;
+ int error;
+
+ ep = fc_seq_exch(req_sp);
+ spin_lock_bh(&ep->ex_lock);
+ error = fc_exch_abort_locked(ep, timer_msec);
+ spin_unlock_bh(&ep->ex_lock);
return error;
}
/**
+ * fc_invoke_resp() - invoke ep->resp()
+ * @ep: The exchange to be operated on
+ * @fp: The frame pointer to pass through to ->resp()
+ * @sp: The sequence pointer to pass through to ->resp()
+ *
+ * Notes:
+ * It is assumed that after initialization finished (this means the
+ * first unlock of ex_lock after fc_exch_alloc()) ep->resp and ep->arg are
+ * modified only via fc_seq_set_resp(). This guarantees that none of these
+ * two variables changes if ep->resp_active > 0.
+ *
+ * If an fc_seq_set_resp() call is busy modifying ep->resp and ep->arg when
+ * this function is invoked, the first spin_lock_bh() call in this function
+ * will wait until fc_seq_set_resp() has finished modifying these variables.
+ *
+ * Since fc_exch_done() invokes fc_seq_set_resp() it is guaranteed that that
+ * ep->resp() won't be invoked after fc_exch_done() has returned.
+ *
+ * The response handler itself may invoke fc_exch_done(), which will clear the
+ * ep->resp pointer.
+ *
+ * Return value:
+ * Returns true if and only if ep->resp has been invoked.
+ */
+static bool fc_invoke_resp(struct fc_exch *ep, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+ void *arg;
+ bool res = false;
+
+ spin_lock_bh(&ep->ex_lock);
+ ep->resp_active++;
+ if (ep->resp_task != current)
+ ep->resp_task = !ep->resp_task ? current : NULL;
+ resp = ep->resp;
+ arg = ep->arg;
+ spin_unlock_bh(&ep->ex_lock);
+
+ if (resp) {
+ resp(sp, fp, arg);
+ res = true;
+ }
+
+ spin_lock_bh(&ep->ex_lock);
+ if (--ep->resp_active == 0)
+ ep->resp_task = NULL;
+ spin_unlock_bh(&ep->ex_lock);
+
+ if (ep->resp_active == 0)
+ wake_up(&ep->resp_wq);
+
+ return res;
+}
+
+/**
* fc_exch_timeout() - Handle exchange timer expiration
* @work: The work_struct identifying the exchange that timed out
*/
@@ -641,12 +766,10 @@ static void fc_exch_timeout(struct work_struct *work)
struct fc_exch *ep = container_of(work, struct fc_exch,
timeout_work.work);
struct fc_seq *sp = &ep->seq;
- void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
- void *arg;
u32 e_stat;
int rc = 1;
- FC_EXCH_DBG(ep, "Exchange timed out\n");
+ FC_EXCH_DBG(ep, "Exchange timed out state %x\n", ep->state);
spin_lock_bh(&ep->ex_lock);
if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
@@ -660,16 +783,13 @@ static void fc_exch_timeout(struct work_struct *work)
fc_exch_rrq(ep);
goto done;
} else {
- resp = ep->resp;
- arg = ep->arg;
- ep->resp = NULL;
if (e_stat & ESB_ST_ABNORMAL)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
fc_exch_delete(ep);
- if (resp)
- resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
+ fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_TIMEOUT));
+ fc_seq_set_resp(sp, NULL, ep->arg);
fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
goto done;
}
@@ -705,21 +825,24 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
}
memset(ep, 0, sizeof(*ep));
- cpu = get_cpu();
+ cpu = raw_smp_processor_id();
pool = per_cpu_ptr(mp->pool, cpu);
spin_lock_bh(&pool->lock);
- put_cpu();
/* peek cache of free slot */
if (pool->left != FC_XID_UNKNOWN) {
- index = pool->left;
- pool->left = FC_XID_UNKNOWN;
- goto hit;
+ if (!WARN_ON(fc_exch_ptr_get(pool, pool->left))) {
+ index = pool->left;
+ pool->left = FC_XID_UNKNOWN;
+ goto hit;
+ }
}
if (pool->right != FC_XID_UNKNOWN) {
- index = pool->right;
- pool->right = FC_XID_UNKNOWN;
- goto hit;
+ if (!WARN_ON(fc_exch_ptr_get(pool, pool->right))) {
+ index = pool->right;
+ pool->right = FC_XID_UNKNOWN;
+ goto hit;
+ }
}
index = pool->next_index;
@@ -756,6 +879,8 @@ hit:
ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */
ep->rxid = FC_XID_UNKNOWN;
ep->class = mp->class;
+ ep->resp_active = 0;
+ init_waitqueue_head(&ep->resp_wq);
INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout);
out:
return ep;
@@ -777,14 +902,19 @@ err:
* EM is selected when a NULL match function pointer is encountered
* or when a call to a match function returns true.
*/
-static inline struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
- struct fc_frame *fp)
+static struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
+ struct fc_frame *fp)
{
struct fc_exch_mgr_anchor *ema;
+ struct fc_exch *ep;
- list_for_each_entry(ema, &lport->ema_list, ema_list)
- if (!ema->match || ema->match(fp))
- return fc_exch_em_alloc(lport, ema->mp);
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
+ if (!ema->match || ema->match(fp)) {
+ ep = fc_exch_em_alloc(lport, ema->mp);
+ if (ep)
+ return ep;
+ }
+ }
return NULL;
}
@@ -795,16 +925,31 @@ static inline struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
*/
static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
{
+ struct fc_lport *lport = mp->lport;
struct fc_exch_pool *pool;
struct fc_exch *ep = NULL;
+ u16 cpu = xid & fc_cpu_mask;
+
+ if (xid == FC_XID_UNKNOWN)
+ return NULL;
+
+ if (cpu >= nr_cpu_ids || !cpu_possible(cpu)) {
+ pr_err("host%u: lport %6.6x: xid %d invalid CPU %d\n:",
+ lport->host->host_no, lport->port_id, xid, cpu);
+ return NULL;
+ }
if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) {
- pool = per_cpu_ptr(mp->pool, xid & fc_cpu_mask);
+ pool = per_cpu_ptr(mp->pool, cpu);
spin_lock_bh(&pool->lock);
ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order);
+ if (ep == &fc_quarantine_exch) {
+ FC_LPORT_DBG(lport, "xid %x quarantined\n", xid);
+ ep = NULL;
+ }
if (ep) {
- fc_exch_hold(ep);
WARN_ON(ep->xid != xid);
+ fc_exch_hold(ep);
}
spin_unlock_bh(&pool->lock);
}
@@ -816,8 +961,10 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
* fc_exch_done() - Indicate that an exchange/sequence tuple is complete and
* the memory allocated for the related objects may be freed.
* @sp: The sequence that has completed
+ *
+ * Note: May sleep if invoked from outside a response handler.
*/
-static void fc_exch_done(struct fc_seq *sp)
+void fc_exch_done(struct fc_seq *sp)
{
struct fc_exch *ep = fc_seq_exch(sp);
int rc;
@@ -825,9 +972,12 @@ static void fc_exch_done(struct fc_seq *sp)
spin_lock_bh(&ep->ex_lock);
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
+
+ fc_seq_set_resp(sp, NULL, ep->arg);
if (!rc)
fc_exch_delete(ep);
}
+EXPORT_SYMBOL(fc_exch_done);
/**
* fc_exch_resp() - Allocate a new exchange for a response frame
@@ -953,6 +1103,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
}
}
+ spin_lock_bh(&ep->ex_lock);
/*
* At this point, we have the exchange held.
* Find or create the sequence.
@@ -965,14 +1116,37 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
sp = &ep->seq;
if (sp->id != fh->fh_seq_id) {
atomic_inc(&mp->stats.seq_not_found);
- reject = FC_RJT_SEQ_ID; /* sequence/exch should exist */
- goto rel;
+ if (f_ctl & FC_FC_END_SEQ) {
+ /*
+ * Update sequence_id based on incoming last
+ * frame of sequence exchange. This is needed
+ * for FC target where DDP has been used
+ * on target where, stack is indicated only
+ * about last frame's (payload _header) header.
+ * Whereas "seq_id" which is part of
+ * frame_header is allocated by initiator
+ * which is totally different from "seq_id"
+ * allocated when XFER_RDY was sent by target.
+ * To avoid false -ve which results into not
+ * sending RSP, hence write request on other
+ * end never finishes.
+ */
+ sp->ssb_stat |= SSB_ST_RESP;
+ sp->id = fh->fh_seq_id;
+ } else {
+ spin_unlock_bh(&ep->ex_lock);
+
+ /* sequence/exch should exist */
+ reject = FC_RJT_SEQ_ID;
+ goto rel;
+ }
}
}
WARN_ON(ep != fc_seq_exch(sp));
if (f_ctl & FC_FC_SEQ_INIT)
ep->esb_stat |= ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
fr_seq(fp) = sp;
out:
@@ -1050,8 +1224,8 @@ static void fc_exch_set_addr(struct fc_exch *ep,
*
* The received frame is not freed.
*/
-static void fc_seq_els_rsp_send(struct fc_frame *fp, enum fc_els_cmd els_cmd,
- struct fc_seq_els_data *els_data)
+void fc_seq_els_rsp_send(struct fc_frame *fp, enum fc_els_cmd els_cmd,
+ struct fc_seq_els_data *els_data)
{
switch (els_cmd) {
case ELS_LS_RJT:
@@ -1070,6 +1244,7 @@ static void fc_seq_els_rsp_send(struct fc_frame *fp, enum fc_els_cmd els_cmd,
FC_LPORT_DBG(fr_dev(fp), "Invalid ELS CMD:%x\n", els_cmd);
}
}
+EXPORT_SYMBOL_GPL(fc_seq_els_rsp_send);
/**
* fc_seq_send_last() - Send a sequence that is the last in the exchange
@@ -1087,7 +1262,7 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
f_ctl |= ep->f_ctl;
fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0);
- fc_seq_send(ep->lp, sp, fp);
+ fc_seq_send_locked(ep->lp, sp, fp);
}
/**
@@ -1111,8 +1286,10 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
*/
if (fc_sof_needs_ack(fr_sof(rx_fp))) {
fp = fc_frame_alloc(lport, 0);
- if (!fp)
+ if (!fp) {
+ FC_EXCH_DBG(ep, "Drop ACK request, out of memory\n");
return;
+ }
fh = fc_frame_header_get(fp);
fh->fh_r_ctl = FC_RCTL_ACK_1;
@@ -1165,13 +1342,18 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
struct fc_frame_header *rx_fh;
struct fc_frame_header *fh;
struct fc_ba_rjt *rp;
+ struct fc_seq *sp;
struct fc_lport *lport;
unsigned int f_ctl;
lport = fr_dev(rx_fp);
+ sp = fr_seq(rx_fp);
fp = fc_frame_alloc(lport, sizeof(*rp));
- if (!fp)
+ if (!fp) {
+ FC_EXCH_DBG(fc_seq_exch(sp),
+ "Drop BA_RJT request, out of memory\n");
return;
+ }
fh = fc_frame_header_get(fp);
rx_fh = fc_frame_header_get(rx_fp);
@@ -1235,21 +1417,26 @@ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
if (!ep)
goto reject;
+
+ FC_EXCH_DBG(ep, "exch: ABTS received\n");
+ fp = fc_frame_alloc(ep->lp, sizeof(*ap));
+ if (!fp) {
+ FC_EXCH_DBG(ep, "Drop ABTS request, out of memory\n");
+ goto free;
+ }
+
spin_lock_bh(&ep->ex_lock);
if (ep->esb_stat & ESB_ST_COMPLETE) {
spin_unlock_bh(&ep->ex_lock);
+ FC_EXCH_DBG(ep, "exch: ABTS rejected, exchange complete\n");
+ fc_frame_free(fp);
goto reject;
}
- if (!(ep->esb_stat & ESB_ST_REC_QUAL))
+ if (!(ep->esb_stat & ESB_ST_REC_QUAL)) {
+ ep->esb_stat |= ESB_ST_REC_QUAL;
fc_exch_hold(ep); /* hold for REC_QUAL */
- ep->esb_stat |= ESB_ST_ABNORMAL | ESB_ST_REC_QUAL;
- fc_exch_timer_set_locked(ep, ep->r_a_tov);
-
- fp = fc_frame_alloc(ep->lp, sizeof(*ap));
- if (!fp) {
- spin_unlock_bh(&ep->ex_lock);
- goto free;
}
+ fc_exch_timer_set_locked(ep, ep->r_a_tov);
fh = fc_frame_header_get(fp);
ap = fc_frame_payload_get(fp, sizeof(*ap));
memset(ap, 0, sizeof(*ap));
@@ -1262,15 +1449,17 @@ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
ap->ba_low_seq_cnt = htons(sp->cnt);
}
sp = fc_seq_start_next_locked(sp);
- spin_unlock_bh(&ep->ex_lock);
fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS);
+ ep->esb_stat |= ESB_ST_ABNORMAL;
+ spin_unlock_bh(&ep->ex_lock);
+
+free:
fc_frame_free(rx_fp);
return;
reject:
fc_exch_send_ba_rjt(rx_fp, FC_BA_RJT_UNABLE, FC_BA_RJT_INV_XID);
-free:
- fc_frame_free(rx_fp);
+ goto free;
}
/**
@@ -1282,7 +1471,7 @@ free:
* A reference will be held on the exchange/sequence for the caller, which
* must call fc_seq_release().
*/
-static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
+struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_exch_mgr_anchor *ema;
@@ -1296,15 +1485,17 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
break;
return fr_seq(fp);
}
+EXPORT_SYMBOL(fc_seq_assign);
/**
* fc_seq_release() - Release the hold
* @sp: The sequence.
*/
-static void fc_seq_release(struct fc_seq *sp)
+void fc_seq_release(struct fc_seq *sp)
{
fc_exch_release(fc_seq_exch(sp));
}
+EXPORT_SYMBOL(fc_seq_release);
/**
* fc_exch_recv_req() - Handler for an incoming request
@@ -1340,7 +1531,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
* The upper-level protocol may request one later, if needed.
*/
if (fh->fh_rx_id == htons(FC_XID_UNKNOWN))
- return lport->tt.lport_recv(lport, fp);
+ return fc_lport_recv(lport, fp);
reject = fc_seq_lookup_recip(lport, mp, fp);
if (reject == FC_RJT_NONE) {
@@ -1360,10 +1551,8 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
* If new exch resp handler is valid then call that
* first.
*/
- if (ep->resp)
- ep->resp(sp, fp, ep->arg);
- else
- lport->tt.lport_recv(lport, fp);
+ if (!fc_invoke_resp(ep, sp, fp))
+ fc_lport_recv(lport, fp);
fc_exch_release(ep); /* release from lookup */
} else {
FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n",
@@ -1386,8 +1575,6 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
struct fc_exch *ep;
enum fc_sof sof;
u32 f_ctl;
- void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
- void *ex_resp_arg;
int rc;
ep = fc_exch_find(mp, ntohs(fh->fh_ox_id));
@@ -1415,31 +1602,33 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
if (fc_sof_is_init(sof)) {
sp->ssb_stat |= SSB_ST_RESP;
sp->id = fh->fh_seq_id;
- } else if (sp->id != fh->fh_seq_id) {
- atomic_inc(&mp->stats.seq_not_found);
- goto rel;
}
f_ctl = ntoh24(fh->fh_f_ctl);
fr_seq(fp) = sp;
+
+ spin_lock_bh(&ep->ex_lock);
if (f_ctl & FC_FC_SEQ_INIT)
ep->esb_stat |= ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
if (fc_sof_needs_ack(sof))
fc_seq_send_ack(sp, fp);
- resp = ep->resp;
- ex_resp_arg = ep->arg;
if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T &&
(f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
(FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
spin_lock_bh(&ep->ex_lock);
- resp = ep->resp;
rc = fc_exch_done_locked(ep);
WARN_ON(fc_seq_exch(sp) != ep);
spin_unlock_bh(&ep->ex_lock);
- if (!rc)
+ if (!rc) {
fc_exch_delete(ep);
+ } else {
+ FC_EXCH_DBG(ep, "ep is completed already,"
+ "hence skip calling the resp\n");
+ goto skip_resp;
+ }
}
/*
@@ -1455,10 +1644,10 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
* If new exch resp handler is valid then call that
* first.
*/
- if (resp)
- resp(sp, fp, ex_resp_arg);
- else
+ if (!fc_invoke_resp(ep, sp, fp))
fc_frame_free(fp);
+
+skip_resp:
fc_exch_release(ep);
return;
rel:
@@ -1497,8 +1686,6 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
*/
static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
{
- void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
- void *ex_resp_arg;
struct fc_frame_header *fh;
struct fc_ba_acc *ap;
struct fc_seq *sp;
@@ -1510,8 +1697,11 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
FC_EXCH_DBG(ep, "exch: BLS rctl %x - %s\n", fh->fh_r_ctl,
fc_exch_rctl_name(fh->fh_r_ctl));
- if (cancel_delayed_work_sync(&ep->timeout_work))
+ if (cancel_delayed_work_sync(&ep->timeout_work)) {
+ FC_EXCH_DBG(ep, "Exchange timer canceled due to ABTS response\n");
fc_exch_release(ep); /* release from pending timer hold */
+ return;
+ }
spin_lock_bh(&ep->ex_lock);
switch (fh->fh_r_ctl) {
@@ -1541,9 +1731,6 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
break;
}
- resp = ep->resp;
- ex_resp_arg = ep->arg;
-
/* do we need to do some other checks here. Can we reuse more of
* fc_exch_recv_seq_resp
*/
@@ -1555,17 +1742,15 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
+
+ fc_exch_hold(ep);
if (!rc)
fc_exch_delete(ep);
-
- if (resp)
- resp(sp, fp, ex_resp_arg);
- else
+ if (!fc_invoke_resp(ep, sp, fp))
fc_frame_free(fp);
-
if (has_rec)
fc_exch_timer_set(ep, ep->r_a_tov);
-
+ fc_exch_release(ep);
}
/**
@@ -1603,9 +1788,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
case FC_RCTL_ACK_0:
break;
default:
- FC_EXCH_DBG(ep, "BLS rctl %x - %s received",
- fh->fh_r_ctl,
- fc_exch_rctl_name(fh->fh_r_ctl));
+ if (ep)
+ FC_EXCH_DBG(ep, "BLS rctl %x - %s received\n",
+ fh->fh_r_ctl,
+ fc_exch_rctl_name(fh->fh_r_ctl));
break;
}
fc_frame_free(fp);
@@ -1619,7 +1805,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
fc_frame_free(fp);
break;
case FC_RCTL_BA_ABTS:
- fc_exch_recv_abts(ep, fp);
+ if (ep)
+ fc_exch_recv_abts(ep, fp);
+ else
+ fc_frame_free(fp);
break;
default: /* ignore junk */
fc_frame_free(fp);
@@ -1642,11 +1831,16 @@ static void fc_seq_ls_acc(struct fc_frame *rx_fp)
struct fc_lport *lport;
struct fc_els_ls_acc *acc;
struct fc_frame *fp;
+ struct fc_seq *sp;
lport = fr_dev(rx_fp);
+ sp = fr_seq(rx_fp);
fp = fc_frame_alloc(lport, sizeof(*acc));
- if (!fp)
+ if (!fp) {
+ FC_EXCH_DBG(fc_seq_exch(sp),
+ "exch: drop LS_ACC, out of memory\n");
return;
+ }
acc = fc_frame_payload_get(fp, sizeof(*acc));
memset(acc, 0, sizeof(*acc));
acc->la_cmd = ELS_LS_ACC;
@@ -1669,11 +1863,16 @@ static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,
struct fc_lport *lport;
struct fc_els_ls_rjt *rjt;
struct fc_frame *fp;
+ struct fc_seq *sp;
lport = fr_dev(rx_fp);
+ sp = fr_seq(rx_fp);
fp = fc_frame_alloc(lport, sizeof(*rjt));
- if (!fp)
+ if (!fp) {
+ FC_EXCH_DBG(fc_seq_exch(sp),
+ "exch: drop LS_ACC, out of memory\n");
return;
+ }
rjt = fc_frame_payload_get(fp, sizeof(*rjt));
memset(rjt, 0, sizeof(*rjt));
rjt->er_cmd = ELS_LS_RJT;
@@ -1686,32 +1885,38 @@ static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,
/**
* fc_exch_reset() - Reset an exchange
* @ep: The exchange to be reset
+ *
+ * Note: May sleep if invoked from outside a response handler.
*/
static void fc_exch_reset(struct fc_exch *ep)
{
struct fc_seq *sp;
- void (*resp)(struct fc_seq *, struct fc_frame *, void *);
- void *arg;
int rc = 1;
spin_lock_bh(&ep->ex_lock);
ep->state |= FC_EX_RST_CLEANUP;
- if (cancel_delayed_work(&ep->timeout_work))
- atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
- resp = ep->resp;
- ep->resp = NULL;
+ fc_exch_timer_cancel(ep);
if (ep->esb_stat & ESB_ST_REC_QUAL)
atomic_dec(&ep->ex_refcnt); /* drop hold for rec_qual */
ep->esb_stat &= ~ESB_ST_REC_QUAL;
- arg = ep->arg;
sp = &ep->seq;
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
- if (!rc)
+
+ fc_exch_hold(ep);
+
+ if (!rc) {
fc_exch_delete(ep);
+ } else {
+ FC_EXCH_DBG(ep, "ep is completed already,"
+ "hence skip calling the resp\n");
+ goto skip_resp;
+ }
- if (resp)
- resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
+ fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_CLOSED));
+skip_resp:
+ fc_seq_set_resp(sp, NULL, ep->arg);
+ fc_exch_release(ep);
}
/**
@@ -1754,6 +1959,9 @@ restart:
goto restart;
}
}
+ pool->next_index = 0;
+ pool->left = FC_XID_UNKNOWN;
+ pool->right = FC_XID_UNKNOWN;
spin_unlock_bh(&pool->lock);
}
@@ -1815,8 +2023,7 @@ static void fc_exch_els_rec(struct fc_frame *rfp)
enum fc_els_rjt_reason reason = ELS_RJT_LOGIC;
enum fc_els_rjt_explan explan;
u32 sid;
- u16 rxid;
- u16 oxid;
+ u16 xid, rxid, oxid;
lport = fr_dev(rfp);
rp = fc_frame_payload_get(rfp, sizeof(*rp));
@@ -1827,18 +2034,35 @@ static void fc_exch_els_rec(struct fc_frame *rfp)
rxid = ntohs(rp->rec_rx_id);
oxid = ntohs(rp->rec_ox_id);
- ep = fc_exch_lookup(lport,
- sid == fc_host_port_id(lport->host) ? oxid : rxid);
explan = ELS_EXPL_OXID_RXID;
- if (!ep)
+ if (sid == fc_host_port_id(lport->host))
+ xid = oxid;
+ else
+ xid = rxid;
+ if (xid == FC_XID_UNKNOWN) {
+ FC_LPORT_DBG(lport,
+ "REC request from %x: invalid rxid %x oxid %x\n",
+ sid, rxid, oxid);
+ goto reject;
+ }
+ ep = fc_exch_lookup(lport, xid);
+ if (!ep) {
+ FC_LPORT_DBG(lport,
+ "REC request from %x: rxid %x oxid %x not found\n",
+ sid, rxid, oxid);
goto reject;
+ }
+ FC_EXCH_DBG(ep, "REC request from %x: rxid %x oxid %x\n",
+ sid, rxid, oxid);
if (ep->oid != sid || oxid != ep->oxid)
goto rel;
if (rxid != FC_XID_UNKNOWN && rxid != ep->rxid)
goto rel;
fp = fc_frame_alloc(lport, sizeof(*acc));
- if (!fp)
+ if (!fp) {
+ FC_EXCH_DBG(ep, "Drop REC request, out of memory\n");
goto out;
+ }
acc = fc_frame_payload_get(fp, sizeof(*acc));
memset(acc, 0, sizeof(*acc));
@@ -1894,13 +2118,13 @@ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
switch (op) {
case ELS_LS_RJT:
- FC_EXCH_DBG(aborted_ep, "LS_RJT for RRQ");
- /* fall through */
+ FC_EXCH_DBG(aborted_ep, "LS_RJT for RRQ\n");
+ fallthrough;
case ELS_LS_ACC:
goto cleanup;
default:
- FC_EXCH_DBG(aborted_ep, "unexpected response op %x "
- "for RRQ", op);
+ FC_EXCH_DBG(aborted_ep, "unexpected response op %x for RRQ\n",
+ op);
return;
}
@@ -1920,6 +2144,24 @@ cleanup:
* @arg: The argument to be passed to the response handler
* @timer_msec: The timeout period for the exchange
*
+ * The exchange response handler is set in this routine to resp()
+ * function pointer. It can be called in two scenarios: if a timeout
+ * occurs or if a response frame is received for the exchange. The
+ * fc_frame pointer in response handler will also indicate timeout
+ * as error using IS_ERR related macros.
+ *
+ * The exchange destructor handler is also set in this routine.
+ * The destructor handler is invoked by EM layer when exchange
+ * is about to free, this can be used by caller to free its
+ * resources along with exchange free.
+ *
+ * The arg is passed back to resp and destructor handler.
+ *
+ * The timeout value (in msec) for an exchange is set if non zero
+ * timer_msec argument is specified. The timer is canceled when
+ * it fires or when the exchange is done. The exchange timeout handler
+ * is registered by EM layer.
+ *
* The frame pointer with some of the header's fields must be
* filled before calling this routine, those fields are:
*
@@ -1930,18 +2172,18 @@ cleanup:
* - frame control
* - parameter or relative offset
*/
-static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
- struct fc_frame *fp,
- void (*resp)(struct fc_seq *,
- struct fc_frame *fp,
- void *arg),
- void (*destructor)(struct fc_seq *,
- void *),
- void *arg, u32 timer_msec)
+struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
+ struct fc_frame *fp,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg),
+ void (*destructor)(struct fc_seq *, void *),
+ void *arg, u32 timer_msec)
{
struct fc_exch *ep;
struct fc_seq *sp = NULL;
struct fc_frame_header *fh;
+ struct fc_fcp_pkt *fsp = NULL;
int rc = 1;
ep = fc_exch_alloc(lport, fp);
@@ -1955,7 +2197,7 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
ep->resp = resp;
ep->destructor = destructor;
ep->arg = arg;
- ep->r_a_tov = FC_DEF_R_A_TOV;
+ ep->r_a_tov = lport->r_a_tov;
ep->lp = lport;
sp = &ep->seq;
@@ -1964,8 +2206,10 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
fc_exch_setup_hdr(ep, fp, ep->f_ctl);
sp->cnt++;
- if (ep->xid <= lport->lro_xid && fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD)
+ if (ep->xid <= lport->lro_xid && fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) {
+ fsp = fr_fsp(fp);
fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+ }
if (unlikely(lport->tt.frame_send(lport, fp)))
goto err;
@@ -1979,13 +2223,15 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
spin_unlock_bh(&ep->ex_lock);
return sp;
err:
- fc_fcp_ddp_done(fr_fsp(fp));
+ if (fsp)
+ fc_fcp_ddp_done(fsp);
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
fc_exch_delete(ep);
return NULL;
}
+EXPORT_SYMBOL(fc_exch_seq_send);
/**
* fc_exch_rrq() - Send an ELS RRQ (Reinstate Recovery Qualifier) command
@@ -2027,6 +2273,7 @@ static void fc_exch_rrq(struct fc_exch *ep)
return;
retry:
+ FC_EXCH_DBG(ep, "exch: RRQ send failed\n");
spin_lock_bh(&ep->ex_lock);
if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE)) {
spin_unlock_bh(&ep->ex_lock);
@@ -2069,6 +2316,8 @@ static void fc_exch_els_rrq(struct fc_frame *fp)
if (!ep)
goto reject;
spin_lock_bh(&ep->ex_lock);
+ FC_EXCH_DBG(ep, "RRQ request from %x: xid %x rxid %x oxid %x\n",
+ sid, xid, ntohs(rp->rrq_rx_id), ntohs(rp->rrq_ox_id));
if (ep->oxid != ntohs(rp->rrq_ox_id))
goto unlock_reject;
if (ep->rxid != ntohs(rp->rrq_rx_id) &&
@@ -2085,10 +2334,8 @@ static void fc_exch_els_rrq(struct fc_frame *fp)
ep->esb_stat &= ~ESB_ST_REC_QUAL;
atomic_dec(&ep->ex_refcnt); /* drop hold for rec qual */
}
- if (ep->esb_stat & ESB_ST_COMPLETE) {
- if (cancel_delayed_work(&ep->timeout_work))
- atomic_dec(&ep->ex_refcnt); /* drop timer hold */
- }
+ if (ep->esb_stat & ESB_ST_COMPLETE)
+ fc_exch_timer_cancel(ep);
spin_unlock_bh(&ep->ex_lock);
@@ -2108,6 +2355,31 @@ out:
}
/**
+ * fc_exch_update_stats() - update exches stats to lport
+ * @lport: The local port to update exchange manager stats
+ */
+void fc_exch_update_stats(struct fc_lport *lport)
+{
+ struct fc_host_statistics *st;
+ struct fc_exch_mgr_anchor *ema;
+ struct fc_exch_mgr *mp;
+
+ st = &lport->host_stats;
+
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
+ mp = ema->mp;
+ st->fc_no_free_exch += atomic_read(&mp->stats.no_free_exch);
+ st->fc_no_free_exch_xid +=
+ atomic_read(&mp->stats.no_free_exch_xid);
+ st->fc_xid_not_found += atomic_read(&mp->stats.xid_not_found);
+ st->fc_xid_busy += atomic_read(&mp->stats.xid_busy);
+ st->fc_seq_not_found += atomic_read(&mp->stats.seq_not_found);
+ st->fc_non_bls_resp += atomic_read(&mp->stats.non_bls_resp);
+ }
+}
+EXPORT_SYMBOL(fc_exch_update_stats);
+
+/**
* fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs
* @lport: The local port to add the exchange manager to
* @mp: The exchange manager to be added to the local port
@@ -2213,9 +2485,21 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
return NULL;
mp->class = class;
+ mp->lport = lport;
/* adjust em exch xid range for offload */
mp->min_xid = min_xid;
- mp->max_xid = max_xid;
+
+ /* reduce range so per cpu pool fits into PCPU_MIN_UNIT_SIZE pool */
+ pool_exch_range = (PCPU_MIN_UNIT_SIZE - sizeof(*pool)) /
+ sizeof(struct fc_exch *);
+ if ((max_xid - min_xid + 1) / (fc_cpu_mask + 1) > pool_exch_range) {
+ mp->max_xid = pool_exch_range * (fc_cpu_mask + 1) +
+ min_xid - 1;
+ } else {
+ mp->max_xid = max_xid;
+ pool_exch_range = (mp->max_xid - mp->min_xid + 1) /
+ (fc_cpu_mask + 1);
+ }
mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
if (!mp->ep_pool)
@@ -2226,7 +2510,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
* divided across all cpus. The exch pointers array memory is
* allocated for exch range per pool.
*/
- pool_exch_range = (mp->max_xid - mp->min_xid + 1) / (fc_cpu_mask + 1);
mp->pool_max_index = pool_exch_range - 1;
/*
@@ -2238,6 +2521,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
goto free_mempool;
for_each_possible_cpu(cpu) {
pool = per_cpu_ptr(mp->pool, cpu);
+ pool->next_index = 0;
pool->left = FC_XID_UNKNOWN;
pool->right = FC_XID_UNKNOWN;
spin_lock_init(&pool->lock);
@@ -2323,7 +2607,7 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
/* lport lock ? */
if (!lport || lport->state == LPORT_ST_DISABLED) {
- FC_LPORT_DBG(lport, "Receiving frames for an lport that "
+ FC_LIBFC_DBG("Receiving frames for an lport that "
"has not been initialized correctly\n");
fc_frame_free(fp);
return;
@@ -2349,7 +2633,7 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
case FC_EOF_T:
if (f_ctl & FC_FC_END_SEQ)
skb_trim(fp_skb(fp), fr_len(fp) - FC_FC_FILL(f_ctl));
- /* fall through */
+ fallthrough;
case FC_EOF_N:
if (fh->fh_type == FC_TYPE_BLS)
fc_exch_recv_bls(ema->mp, fp);
@@ -2375,36 +2659,9 @@ EXPORT_SYMBOL(fc_exch_recv);
*/
int fc_exch_init(struct fc_lport *lport)
{
- if (!lport->tt.seq_start_next)
- lport->tt.seq_start_next = fc_seq_start_next;
-
- if (!lport->tt.seq_set_resp)
- lport->tt.seq_set_resp = fc_seq_set_resp;
-
- if (!lport->tt.exch_seq_send)
- lport->tt.exch_seq_send = fc_exch_seq_send;
-
- if (!lport->tt.seq_send)
- lport->tt.seq_send = fc_seq_send;
-
- if (!lport->tt.seq_els_rsp_send)
- lport->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
-
- if (!lport->tt.exch_done)
- lport->tt.exch_done = fc_exch_done;
-
if (!lport->tt.exch_mgr_reset)
lport->tt.exch_mgr_reset = fc_exch_mgr_reset;
- if (!lport->tt.seq_exch_abort)
- lport->tt.seq_exch_abort = fc_seq_exch_abort;
-
- if (!lport->tt.seq_assign)
- lport->tt.seq_assign = fc_seq_assign;
-
- if (!lport->tt.seq_release)
- lport->tt.seq_release = fc_seq_release;
-
return 0;
}
EXPORT_SYMBOL(fc_exch_init);
@@ -2433,18 +2690,17 @@ int fc_setup_exch_mgr(void)
* cpu on which exchange originated by simple bitwise
* AND operation between fc_cpu_mask and exchange id.
*/
- fc_cpu_mask = 1;
- fc_cpu_order = 0;
- while (fc_cpu_mask < nr_cpu_ids) {
- fc_cpu_mask <<= 1;
- fc_cpu_order++;
- }
- fc_cpu_mask--;
+ fc_cpu_order = ilog2(roundup_pow_of_two(nr_cpu_ids));
+ fc_cpu_mask = (1 << fc_cpu_order) - 1;
- fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");
+ fc_exch_workqueue = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM,
+ "fc_exch_workqueue");
if (!fc_exch_workqueue)
- return -ENOMEM;
+ goto err;
return 0;
+err:
+ kmem_cache_destroy(fc_em_cachep);
+ return -ENOMEM;
}
/**
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 9cd2149519ac..31d08c115521 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2007 Intel Corporation. All rights reserved.
* Copyright(c) 2008 Red Hat, Inc. All rights reserved.
* Copyright(c) 2008 Mike Christie
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -38,8 +26,8 @@
#include <scsi/fc/fc_fc2.h>
#include <scsi/libfc.h>
-#include <scsi/fc_encode.h>
+#include "fc_encode.h"
#include "fc_libfc.h"
static struct kmem_cache *scsi_pkt_cachep;
@@ -57,14 +45,10 @@ static struct kmem_cache *scsi_pkt_cachep;
#define FC_SRB_READ (1 << 1)
#define FC_SRB_WRITE (1 << 0)
-/*
- * The SCp.ptr should be tested and set under the scsi_pkt_queue lock
- */
-#define CMD_SP(Cmnd) ((struct fc_fcp_pkt *)(Cmnd)->SCp.ptr)
-#define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in)
-#define CMD_COMPL_STATUS(Cmnd) ((Cmnd)->SCp.this_residual)
-#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
-#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual)
+static struct libfc_cmd_priv *libfc_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
/**
* struct fc_fcp_internal - FCP layer internal data
@@ -97,7 +81,7 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_recovery(struct fc_fcp_pkt *, u8 code);
-static void fc_fcp_timeout(unsigned long);
+static void fc_fcp_timeout(struct timer_list *);
static void fc_fcp_rec(struct fc_fcp_pkt *);
static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *);
@@ -122,6 +106,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
#define FC_HRD_ERROR 9
#define FC_CRC_ERROR 10
#define FC_TIMED_OUT 11
+#define FC_TRANS_RESET 12
/*
* Error recovery timeout values.
@@ -153,10 +138,12 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)
memset(fsp, 0, sizeof(*fsp));
fsp->lp = lport;
fsp->xfer_ddp = FC_XID_UNKNOWN;
- atomic_set(&fsp->ref_cnt, 1);
- init_timer(&fsp->timer);
+ refcount_set(&fsp->ref_cnt, 1);
+ timer_setup(&fsp->timer, NULL, 0);
INIT_LIST_HEAD(&fsp->list);
spin_lock_init(&fsp->scsi_pkt_lock);
+ } else {
+ this_cpu_inc(lport->stats->FcpPktAllocFails);
}
return fsp;
}
@@ -170,7 +157,7 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)
*/
static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
{
- if (atomic_dec_and_test(&fsp->ref_cnt)) {
+ if (refcount_dec_and_test(&fsp->ref_cnt)) {
struct fc_fcp_internal *si = fc_get_scsi_internal(fsp->lp);
mempool_free(fsp, si->scsi_pkt_pool);
@@ -183,15 +170,15 @@ static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
*/
static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp)
{
- atomic_inc(&fsp->ref_cnt);
+ refcount_inc(&fsp->ref_cnt);
}
/**
- * fc_fcp_pkt_destory() - Release hold on a fcp_pkt
+ * fc_fcp_pkt_destroy() - Release hold on a fcp_pkt
* @seq: The sequence that the FCP packet is on (required by destructor API)
* @fsp: The FCP packet to be released
*
- * This routine is called by a destructor callback in the exch_seq_send()
+ * This routine is called by a destructor callback in the fc_exch_seq_send()
* routine of the libfc Transport Template. The 'struct fc_seq' is a required
* argument even though it is not used by this routine.
*
@@ -249,8 +236,21 @@ static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
*/
static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
{
- if (!(fsp->state & FC_SRB_COMPL))
+ if (!(fsp->state & FC_SRB_COMPL)) {
mod_timer(&fsp->timer, jiffies + delay);
+ fsp->timer_delay = delay;
+ }
+}
+
+static void fc_fcp_abort_done(struct fc_fcp_pkt *fsp)
+{
+ fsp->state |= FC_SRB_ABORTED;
+ fsp->state &= ~FC_SRB_ABORT_PENDING;
+
+ if (fsp->wait_for_comp)
+ complete(&fsp->tm_done);
+ else
+ fc_fcp_complete_locked(fsp);
}
/**
@@ -260,32 +260,51 @@ static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
*/
static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
{
+ int rc;
+
if (!fsp->seq_ptr)
return -EINVAL;
+ if (fsp->state & FC_SRB_ABORT_PENDING) {
+ FC_FCP_DBG(fsp, "abort already pending\n");
+ return -EBUSY;
+ }
+
+ this_cpu_inc(fsp->lp->stats->FcpPktAborts);
+
fsp->state |= FC_SRB_ABORT_PENDING;
- return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);
+ rc = fc_seq_exch_abort(fsp->seq_ptr, 0);
+ /*
+ * fc_seq_exch_abort() might return -ENXIO if
+ * the sequence is already completed
+ */
+ if (rc == -ENXIO) {
+ fc_fcp_abort_done(fsp);
+ rc = 0;
+ }
+ return rc;
}
/**
* fc_fcp_retry_cmd() - Retry a fcp_pkt
* @fsp: The FCP packet to be retried
+ * @status_code: The FCP status code to set
*
* Sets the status code to be FC_ERROR and then calls
* fc_fcp_complete_locked() which in turn calls fc_io_compl().
* fc_io_compl() will notify the SCSI-ml that the I/O is done.
* The SCSI-ml will retry the command.
*/
-static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
+static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp, int status_code)
{
if (fsp->seq_ptr) {
- fsp->lp->tt.exch_done(fsp->seq_ptr);
+ fc_exch_done(fsp->seq_ptr);
fsp->seq_ptr = NULL;
}
fsp->state &= ~FC_SRB_ABORT_PENDING;
fsp->io_status = 0;
- fsp->status_code = FC_ERROR;
+ fsp->status_code = status_code;
fc_fcp_complete_locked(fsp);
}
@@ -375,11 +394,12 @@ unlock:
* can_queue. Eventually we will hit the point where we run
* on all reserved structs.
*/
-static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
+static bool fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
{
struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
unsigned long flags;
int can_queue;
+ bool changed = false;
spin_lock_irqsave(lport->host->host_lock, flags);
@@ -395,11 +415,11 @@ static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
if (!can_queue)
can_queue = 1;
lport->host->can_queue = can_queue;
- shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n"
- "Reducing can_queue to %d.\n", can_queue);
+ changed = true;
unlock:
spin_unlock_irqrestore(lport->host->host_lock, flags);
+ return changed;
}
/*
@@ -419,12 +439,32 @@ static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,
if (likely(fp))
return fp;
+ this_cpu_inc(lport->stats->FcpFrameAllocFails);
/* error case */
fc_fcp_can_queue_ramp_down(lport);
+ shost_printk(KERN_ERR, lport->host,
+ "libfc: Could not allocate frame, "
+ "reducing can_queue to %d.\n", lport->host->can_queue);
return NULL;
}
/**
+ * get_fsp_rec_tov() - Helper function to get REC_TOV
+ * @fsp: the FCP packet
+ *
+ * Returns rec tov in jiffies as rpriv->e_d_tov + 1 second
+ */
+static inline unsigned int get_fsp_rec_tov(struct fc_fcp_pkt *fsp)
+{
+ struct fc_rport_libfc_priv *rpriv = fsp->rport->dd_data;
+ unsigned int e_d_tov = FC_DEF_E_D_TOV;
+
+ if (rpriv && rpriv->e_d_tov > e_d_tov)
+ e_d_tov = rpriv->e_d_tov;
+ return msecs_to_jiffies(e_d_tov) + HZ;
+}
+
+/**
* fc_fcp_recv_data() - Handler for receiving SCSI-FCP data from a target
* @fsp: The FCP packet the data is on
* @fp: The data frame
@@ -433,7 +473,6 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
struct scsi_cmnd *sc = fsp->cmd;
struct fc_lport *lport = fsp->lp;
- struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
size_t start_offset;
size_t offset;
@@ -464,7 +503,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
host_bcode = FC_ERROR;
goto err;
}
- if (offset + len > fsp->data_len) {
+ if (size_add(offset, len) > fsp->data_len) {
/* this should never happen */
if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
fc_frame_crc_check(fp))
@@ -484,25 +523,23 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
- &offset, KM_SOFTIRQ0, NULL);
+ &offset, NULL);
} else {
crc = crc32(~0, (u8 *) fh, sizeof(*fh));
copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
- &offset, KM_SOFTIRQ0, &crc);
+ &offset, &crc);
buf = fc_frame_payload_get(fp, 0);
if (len % 4)
crc = crc32(crc, buf + len, 4 - (len % 4));
if (~crc != le32_to_cpu(fr_crc(fp))) {
crc_err:
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
- stats->ErrorFrames++;
+ this_cpu_inc(lport->stats->ErrorFrames);
/* per cpu count, not total count, but OK for limit */
- if (stats->InvalidCRCCount++ < 5)
+ if (this_cpu_inc_return(lport->stats->InvalidCRCCount) < FC_MAX_ERROR_CNT)
printk(KERN_WARNING "libfc: CRC error on data "
"frame for port (%6.6x)\n",
lport->port_id);
- put_cpu();
/*
* Assume the frame is total garbage.
* We may have copied it over the good part
@@ -527,8 +564,10 @@ crc_err:
* and completes the transfer, call the completion handler.
*/
if (unlikely(fsp->state & FC_SRB_RCV_STATUS) &&
- fsp->xfer_len == fsp->data_len - fsp->scsi_resid)
+ fsp->xfer_len == fsp->data_len - fsp->scsi_resid) {
+ FC_FCP_DBG( fsp, "complete out-of-order sequence\n" );
fc_fcp_complete_locked(fsp);
+ }
return;
err:
fc_fcp_recovery(fsp, host_bcode);
@@ -537,7 +576,7 @@ err:
/**
* fc_fcp_send_data() - Send SCSI data to a target
* @fsp: The FCP packet the data is on
- * @sp: The sequence the data is to be sent on
+ * @seq: The sequence the data is to be sent on
* @offset: The starting offset for this data request
* @seq_blen: The burst length for this data request
*
@@ -600,7 +639,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
remaining = seq_blen;
fh_parm_offset = frame_offset = offset;
tlen = 0;
- seq = lport->tt.seq_start_next(seq);
+ seq = fc_seq_start_next(seq);
f_ctl = FC_FC_REL_OFF;
WARN_ON(!seq);
@@ -649,10 +688,10 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
* The scatterlist item may be bigger than PAGE_SIZE,
* but we must not cross pages inside the kmap.
*/
- page_addr = kmap_atomic(page, KM_SOFTIRQ0);
+ page_addr = kmap_atomic(page);
memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),
sg_bytes);
- kunmap_atomic(page_addr, KM_SOFTIRQ0);
+ kunmap_atomic(page_addr);
data += sg_bytes;
}
offset += sg_bytes;
@@ -678,7 +717,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
/*
* send fragment using for a sequence.
*/
- error = lport->tt.seq_send(lport, seq, fp);
+ error = fc_seq_send(lport, seq, fp);
if (error) {
WARN_ON(1); /* send error should be rare */
return error;
@@ -690,7 +729,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
}
/**
- * fc_fcp_abts_resp() - Send an ABTS response
+ * fc_fcp_abts_resp() - Receive an ABTS response
* @fsp: The FCP packet that is being aborted
* @fp: The response frame
*/
@@ -708,7 +747,7 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
brp = fc_frame_payload_get(fp, sizeof(*brp));
if (brp && brp->br_reason == FC_BA_RJT_LOG_ERR)
break;
- /* fall thru */
+ fallthrough;
default:
/*
* we will let the command timeout
@@ -718,19 +757,12 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
ba_done = 0;
}
- if (ba_done) {
- fsp->state |= FC_SRB_ABORTED;
- fsp->state &= ~FC_SRB_ABORT_PENDING;
-
- if (fsp->wait_for_comp)
- complete(&fsp->tm_done);
- else
- fc_fcp_complete_locked(fsp);
- }
+ if (ba_done)
+ fc_fcp_abort_done(fsp);
}
/**
- * fc_fcp_recv() - Reveive an FCP frame
+ * fc_fcp_recv() - Receive an FCP frame
* @seq: The sequence the frame is on
* @fp: The received frame
* @arg: The related FCP packet
@@ -755,19 +787,23 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
fh = fc_frame_header_get(fp);
r_ctl = fh->fh_r_ctl;
- if (lport->state != LPORT_ST_READY)
+ if (lport->state != LPORT_ST_READY) {
+ FC_FCP_DBG(fsp, "lport state %d, ignoring r_ctl %x\n",
+ lport->state, r_ctl);
goto out;
+ }
if (fc_fcp_lock_pkt(fsp))
goto out;
- fsp->last_pkt_time = jiffies;
if (fh->fh_type == FC_TYPE_BLS) {
fc_fcp_abts_resp(fsp, fp);
goto unlock;
}
- if (fsp->state & (FC_SRB_ABORTED | FC_SRB_ABORT_PENDING))
+ if (fsp->state & (FC_SRB_ABORTED | FC_SRB_ABORT_PENDING)) {
+ FC_FCP_DBG(fsp, "command aborted, ignoring r_ctl %x\n", r_ctl);
goto unlock;
+ }
if (r_ctl == FC_RCTL_DD_DATA_DESC) {
/*
@@ -843,7 +879,8 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
fc_rp_info = (struct fcp_resp_rsp_info *)(rp_ex + 1);
if (flags & FCP_RSP_LEN_VAL) {
respl = ntohl(rp_ex->fr_rsp_len);
- if (respl != sizeof(*fc_rp_info))
+ if ((respl != FCP_RESP_RSP_INFO_LEN4) &&
+ (respl != FCP_RESP_RSP_INFO_LEN8))
goto len_err;
if (fsp->wait_for_comp) {
/* Abuse cdb_status for rsp code */
@@ -893,14 +930,24 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
/*
* Check for missing or extra data frames.
*/
- if (unlikely(fsp->xfer_len != expected_len)) {
+ if (unlikely(fsp->cdb_status == SAM_STAT_GOOD &&
+ fsp->xfer_len != expected_len)) {
if (fsp->xfer_len < expected_len) {
/*
* Some data may be queued locally,
* Wait a at least one jiffy to see if it is delivered.
* If this expires without data, we may do SRR.
*/
- fc_fcp_timer_set(fsp, 2);
+ if (fsp->lp->qfull) {
+ FC_FCP_DBG(fsp, "tgt %6.6x queue busy retry\n",
+ fsp->rport->port_id);
+ return;
+ }
+ FC_FCP_DBG(fsp, "tgt %6.6x xfer len %zx data underrun "
+ "len %x, data len %x\n",
+ fsp->rport->port_id,
+ fsp->xfer_len, expected_len, fsp->data_len);
+ fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
return;
}
fsp->status_code = FC_DATA_OVRRUN;
@@ -946,11 +993,13 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
* Test for transport underrun, independent of response
* underrun status.
*/
- if (fsp->xfer_len < fsp->data_len && !fsp->io_status &&
+ if (fsp->cdb_status == SAM_STAT_GOOD &&
+ fsp->xfer_len < fsp->data_len && !fsp->io_status &&
(!(fsp->scsi_comp_flags & FCP_RESID_UNDER) ||
fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) {
+ FC_FCP_DBG(fsp, "data underrun, xfer %zx data %x\n",
+ fsp->xfer_len, fsp->data_len);
fsp->status_code = FC_DATA_UNDRUN;
- fsp->io_status = 0;
}
}
@@ -961,7 +1010,7 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
struct fc_frame *conf_frame;
struct fc_seq *csp;
- csp = lport->tt.seq_start_next(seq);
+ csp = fc_seq_start_next(seq);
conf_frame = fc_fcp_frame_alloc(fsp->lp, 0);
if (conf_frame) {
f_ctl = FC_FC_SEQ_INIT;
@@ -970,10 +1019,10 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL,
ep->did, ep->sid,
FC_TYPE_FCP, f_ctl, 0);
- lport->tt.seq_send(lport, csp, conf_frame);
+ fc_seq_send(lport, csp, conf_frame);
}
}
- lport->tt.exch_done(seq);
+ fc_exch_done(seq);
}
/*
* Some resets driven by SCSI are not I/Os and do not have
@@ -991,10 +1040,8 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
*/
static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error)
{
- struct fc_lport *lport = fsp->lp;
-
if (fsp->seq_ptr) {
- lport->tt.exch_done(fsp->seq_ptr);
+ fc_exch_done(fsp->seq_ptr);
fsp->seq_ptr = NULL;
}
fsp->status_code = error;
@@ -1030,11 +1077,26 @@ restart:
fc_fcp_pkt_hold(fsp);
spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
- if (!fc_fcp_lock_pkt(fsp)) {
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ if (!(fsp->state & FC_SRB_COMPL)) {
+ fsp->state |= FC_SRB_COMPL;
+ /*
+ * TODO: dropping scsi_pkt_lock and then reacquiring
+ * again around fc_fcp_cleanup_cmd() is required,
+ * since fc_fcp_cleanup_cmd() calls into
+ * fc_seq_set_resp() and that func preempts cpu using
+ * schedule. May be schedule and related code should be
+ * removed instead of unlocking here to avoid scheduling
+ * while atomic bug.
+ */
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+
fc_fcp_cleanup_cmd(fsp, error);
+
+ spin_lock_bh(&fsp->scsi_pkt_lock);
fc_io_compl(fsp);
- fc_fcp_unlock_pkt(fsp);
}
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
fc_fcp_pkt_release(fsp);
spin_lock_irqsave(&si->scsi_queue_lock, flags);
@@ -1070,12 +1132,11 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
unsigned long flags;
int rc;
- fsp->cmd->SCp.ptr = (char *)fsp;
+ libfc_priv(fsp->cmd)->fsp = fsp;
fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
fsp->cdb_cmd.fc_flags = fsp->req_flags & ~FCP_CFL_LEN_MASK;
- int_to_scsilun(fsp->cmd->device->lun,
- (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+ int_to_scsilun(fsp->cmd->device->lun, &fsp->cdb_cmd.fc_lun);
memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
spin_lock_irqsave(&si->scsi_queue_lock, flags);
@@ -1084,6 +1145,7 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv);
if (unlikely(rc)) {
spin_lock_irqsave(&si->scsi_queue_lock, flags);
+ libfc_priv(fsp->cmd)->fsp = NULL;
list_del(&fsp->list);
spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
}
@@ -1092,19 +1154,6 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
}
/**
- * get_fsp_rec_tov() - Helper function to get REC_TOV
- * @fsp: the FCP packet
- *
- * Returns rec tov in jiffies as rpriv->e_d_tov + 1 second
- */
-static inline unsigned int get_fsp_rec_tov(struct fc_fcp_pkt *fsp)
-{
- struct fc_rport_libfc_priv *rpriv = fsp->rport->dd_data;
-
- return msecs_to_jiffies(rpriv->e_d_tov) + HZ;
-}
-
-/**
* fc_fcp_cmd_send() - Send a FCP command
* @lport: The local port to send the command on
* @fsp: The FCP packet the command is on
@@ -1141,17 +1190,15 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
rpriv->local_port->port_id, FC_TYPE_FCP,
FC_FCTL_REQ, 0);
- seq = lport->tt.exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy,
- fsp, 0);
+ seq = fc_exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
if (!seq) {
rc = -1;
goto unlock;
}
- fsp->last_pkt_time = jiffies;
fsp->seq_ptr = seq;
fc_fcp_pkt_hold(fsp); /* hold for fc_fcp_pkt_destroy */
- setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp);
+ fsp->timer.function = fc_fcp_timeout;
if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)
fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
@@ -1173,7 +1220,7 @@ static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
return;
if (error == -FC_EX_CLOSED) {
- fc_fcp_retry_cmd(fsp);
+ fc_fcp_retry_cmd(fsp, FC_ERROR);
goto unlock;
}
@@ -1199,8 +1246,16 @@ static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
int rc = FAILED;
unsigned long ticks_left;
- if (fc_fcp_send_abort(fsp))
+ FC_FCP_DBG(fsp, "pkt abort state %x\n", fsp->state);
+ if (fc_fcp_send_abort(fsp)) {
+ FC_FCP_DBG(fsp, "failed to send abort\n");
return FAILED;
+ }
+
+ if (fsp->state & FC_SRB_ABORTED) {
+ FC_FCP_DBG(fsp, "target abort cmd completed\n");
+ return SUCCESS;
+ }
init_completion(&fsp->tm_done);
fsp->wait_for_comp = 1;
@@ -1224,11 +1279,11 @@ static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
/**
* fc_lun_reset_send() - Send LUN reset command
- * @data: The FCP packet that identifies the LUN to be reset
+ * @t: Timer context used to fetch the FSP packet
*/
-static void fc_lun_reset_send(unsigned long data)
+static void fc_lun_reset_send(struct timer_list *t)
{
- struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
+ struct fc_fcp_pkt *fsp = timer_container_of(fsp, t, timer);
struct fc_lport *lport = fsp->lp;
if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) {
@@ -1236,7 +1291,7 @@ static void fc_lun_reset_send(unsigned long data)
return;
if (fc_fcp_lock_pkt(fsp))
return;
- setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
+ fsp->timer.function = fc_lun_reset_send;
fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
fc_fcp_unlock_pkt(fsp);
}
@@ -1257,12 +1312,12 @@ static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
fsp->cdb_cmd.fc_tm_flags = FCP_TMF_LUN_RESET;
- int_to_scsilun(lun, (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+ int_to_scsilun(lun, &fsp->cdb_cmd.fc_lun);
fsp->wait_for_comp = 1;
init_completion(&fsp->tm_done);
- fc_lun_reset_send((unsigned long)fsp);
+ fc_lun_reset_send(&fsp->timer);
/*
* wait for completion of reset
@@ -1274,11 +1329,11 @@ static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
fsp->state |= FC_SRB_COMPL;
spin_unlock_bh(&fsp->scsi_pkt_lock);
- del_timer_sync(&fsp->timer);
+ timer_delete_sync(&fsp->timer);
spin_lock_bh(&fsp->scsi_pkt_lock);
if (fsp->seq_ptr) {
- lport->tt.exch_done(fsp->seq_ptr);
+ fc_exch_done(fsp->seq_ptr);
fsp->seq_ptr = NULL;
}
fsp->wait_for_comp = 0;
@@ -1332,7 +1387,7 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
if (fh->fh_type != FC_TYPE_BLS)
fc_fcp_resp(fsp, fp);
fsp->seq_ptr = NULL;
- fsp->lp->tt.exch_done(seq);
+ fc_exch_done(seq);
out_unlock:
fc_fcp_unlock_pkt(fsp);
out:
@@ -1350,7 +1405,7 @@ static void fc_fcp_cleanup(struct fc_lport *lport)
/**
* fc_fcp_timeout() - Handler for fcp_pkt timeouts
- * @data: The FCP packet that has timed out
+ * @t: Timer context used to fetch the FSP packet
*
* If REC is supported then just issue it and return. The REC exchange will
* complete or time out and recovery can continue at that point. Otherwise,
@@ -1359,9 +1414,9 @@ static void fc_fcp_cleanup(struct fc_lport *lport)
* received we see if data was received recently. If it has been then we
* continue waiting, otherwise, we abort the command.
*/
-static void fc_fcp_timeout(unsigned long data)
+static void fc_fcp_timeout(struct timer_list *t)
{
- struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
+ struct fc_fcp_pkt *fsp = timer_container_of(fsp, t, timer);
struct fc_rport *rport = fsp->rport;
struct fc_rport_libfc_priv *rpriv = rport->dd_data;
@@ -1371,6 +1426,15 @@ static void fc_fcp_timeout(unsigned long data)
if (fsp->cdb_cmd.fc_tm_flags)
goto unlock;
+ if (fsp->lp->qfull) {
+ FC_FCP_DBG(fsp, "fcp timeout, resetting timer delay %d\n",
+ fsp->timer_delay);
+ fsp->timer.function = fc_fcp_timeout;
+ fc_fcp_timer_set(fsp, fsp->timer_delay);
+ goto unlock;
+ }
+ FC_FCP_DBG(fsp, "fcp timeout, delay %d flags %x state %x\n",
+ fsp->timer_delay, rpriv->flags, fsp->state);
fsp->state |= FC_SRB_FCP_PROCESSING_TMO;
if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)
@@ -1463,11 +1527,11 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
rjt = fc_frame_payload_get(fp, sizeof(*rjt));
switch (rjt->er_reason) {
default:
- FC_FCP_DBG(fsp, "device %x unexpected REC reject "
- "reason %d expl %d\n",
+ FC_FCP_DBG(fsp,
+ "device %x invalid REC reject %d/%d\n",
fsp->rport->port_id, rjt->er_reason,
rjt->er_explan);
- /* fall through */
+ fallthrough;
case ELS_RJT_UNSUP:
FC_FCP_DBG(fsp, "device does not support REC\n");
rpriv = fsp->rport->dd_data;
@@ -1480,18 +1544,23 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
break;
case ELS_RJT_LOGIC:
case ELS_RJT_UNAB:
+ FC_FCP_DBG(fsp, "device %x REC reject %d/%d\n",
+ fsp->rport->port_id, rjt->er_reason,
+ rjt->er_explan);
/*
- * If no data transfer, the command frame got dropped
- * so we just retry. If data was transferred, we
- * lost the response but the target has no record,
- * so we abort and retry.
+ * If response got lost or is stuck in the
+ * queue somewhere we have no idea if and when
+ * the response will be received. So quarantine
+ * the xid and retry the command.
*/
- if (rjt->er_explan == ELS_EXPL_OXID_RXID &&
- fsp->xfer_len == 0) {
- fc_fcp_retry_cmd(fsp);
+ if (rjt->er_explan == ELS_EXPL_OXID_RXID) {
+ struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
+ ep->state |= FC_EX_QUARANTINE;
+ fsp->state |= FC_SRB_ABORTED;
+ fc_fcp_retry_cmd(fsp, FC_TRANS_RESET);
break;
}
- fc_fcp_recovery(fsp, FC_ERROR);
+ fc_fcp_recovery(fsp, FC_TRANS_RESET);
break;
}
} else if (opcode == ELS_LS_ACC) {
@@ -1585,27 +1654,29 @@ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
switch (error) {
case -FC_EX_CLOSED:
- fc_fcp_retry_cmd(fsp);
+ FC_FCP_DBG(fsp, "REC %p fid %6.6x exchange closed\n",
+ fsp, fsp->rport->port_id);
+ fc_fcp_retry_cmd(fsp, FC_ERROR);
break;
default:
FC_FCP_DBG(fsp, "REC %p fid %6.6x error unexpected error %d\n",
fsp, fsp->rport->port_id, error);
fsp->status_code = FC_CMD_PLOGO;
- /* fall through */
+ fallthrough;
case -FC_EX_TIMEOUT:
/*
* Assume REC or LS_ACC was lost.
* The exchange manager will have aborted REC, so retry.
*/
- FC_FCP_DBG(fsp, "REC fid %6.6x error error %d retry %d/%d\n",
- fsp->rport->port_id, error, fsp->recov_retry,
+ FC_FCP_DBG(fsp, "REC %p fid %6.6x exchange timeout retry %d/%d\n",
+ fsp, fsp->rport->port_id, fsp->recov_retry,
FC_MAX_RECOV_RETRY);
if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
fc_fcp_rec(fsp);
else
- fc_fcp_recovery(fsp, FC_ERROR);
+ fc_fcp_recovery(fsp, FC_TIMED_OUT);
break;
}
fc_fcp_unlock_pkt(fsp);
@@ -1616,23 +1687,27 @@ out:
/**
* fc_fcp_recovery() - Handler for fcp_pkt recovery
* @fsp: The FCP pkt that needs to be aborted
+ * @code: The FCP status code to set
*/
static void fc_fcp_recovery(struct fc_fcp_pkt *fsp, u8 code)
{
+ FC_FCP_DBG(fsp, "start recovery code %x\n", code);
fsp->status_code = code;
fsp->cdb_status = 0;
fsp->io_status = 0;
- /*
- * if this fails then we let the scsi command timer fire and
- * scsi-ml escalate.
- */
- fc_fcp_send_abort(fsp);
+ if (!fsp->cmd)
+ /*
+ * Only abort non-scsi commands; otherwise let the
+ * scsi command timer fire and scsi-ml escalate.
+ */
+ fc_fcp_send_abort(fsp);
}
/**
* fc_fcp_srr() - Send a SRR request (Sequence Retransmission Request)
* @fsp: The FCP packet the SRR is to be sent on
* @r_ctl: The R_CTL field for the SRR request
+ * @offset: The SRR relative offset
* This is called after receiving status but insufficient data, or
* when expecting status but the request has timed out.
*/
@@ -1645,12 +1720,9 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
struct fc_seq *seq;
struct fcp_srr *srr;
struct fc_frame *fp;
- u8 cdb_op;
- unsigned int rec_tov;
rport = fsp->rport;
rpriv = rport->dd_data;
- cdb_op = fsp->cdb_cmd.fc_cdb[0];
if (!(rpriv->flags & FC_RP_FLAGS_RETRY) ||
rpriv->rp_state != RPORT_ST_READY)
@@ -1671,10 +1743,9 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
rpriv->local_port->port_id, FC_TYPE_FCP,
FC_FCTL_REQ, 0);
- rec_tov = get_fsp_rec_tov(fsp);
- seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp,
- fc_fcp_pkt_destroy,
- fsp, jiffies_to_msecs(rec_tov));
+ seq = fc_exch_seq_send(lport, fp, fc_fcp_srr_resp,
+ fc_fcp_pkt_destroy,
+ fsp, get_fsp_rec_tov(fsp));
if (!seq)
goto retry;
@@ -1685,7 +1756,7 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
fc_fcp_pkt_hold(fsp); /* hold for outstanding SRR */
return;
retry:
- fc_fcp_retry_cmd(fsp);
+ fc_fcp_retry_cmd(fsp, FC_TRANS_RESET);
}
/**
@@ -1709,9 +1780,9 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
fh = fc_frame_header_get(fp);
/*
- * BUG? fc_fcp_srr_error calls exch_done which would release
+ * BUG? fc_fcp_srr_error calls fc_exch_done which would release
* the ep. But if fc_fcp_srr_error had got -FC_EX_TIMEOUT,
- * then fc_exch_timeout would be sending an abort. The exch_done
+ * then fc_exch_timeout would be sending an abort. The fc_exch_done
* call by fc_fcp_srr_error would prevent fc_exch.c from seeing
* an abort response though.
*/
@@ -1732,7 +1803,7 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
}
fc_fcp_unlock_pkt(fsp);
out:
- fsp->lp->tt.exch_done(seq);
+ fc_exch_done(seq);
fc_frame_free(fp);
}
@@ -1747,20 +1818,22 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
goto out;
switch (PTR_ERR(fp)) {
case -FC_EX_TIMEOUT:
+ FC_FCP_DBG(fsp, "SRR timeout, retries %d\n", fsp->recov_retry);
if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
fc_fcp_rec(fsp);
else
fc_fcp_recovery(fsp, FC_TIMED_OUT);
break;
case -FC_EX_CLOSED: /* e.g., link failure */
- /* fall through */
+ FC_FCP_DBG(fsp, "SRR error, exchange closed\n");
+ fallthrough;
default:
- fc_fcp_retry_cmd(fsp);
+ fc_fcp_retry_cmd(fsp, FC_ERROR);
break;
}
fc_fcp_unlock_pkt(fsp);
out:
- fsp->lp->tt.exch_done(fsp->recov_seq);
+ fc_exch_done(fsp->recov_seq);
}
/**
@@ -1777,7 +1850,7 @@ static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
/**
* fc_queuecommand() - The queuecommand function of the SCSI template
* @shost: The Scsi_Host that the command was issued to
- * @cmd: The scsi_cmnd to be executed
+ * @sc_cmd: The scsi_cmnd to be executed
*
* This is the i/o strategy routine, called by the SCSI layer.
*/
@@ -1786,15 +1859,13 @@ int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
struct fc_lport *lport = shost_priv(shost);
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
struct fc_fcp_pkt *fsp;
- struct fc_rport_libfc_priv *rpriv;
int rval;
int rc = 0;
- struct fcoe_dev_stats *stats;
rval = fc_remote_port_chkready(rport);
if (rval) {
sc_cmd->result = rval;
- sc_cmd->scsi_done(sc_cmd);
+ scsi_done(sc_cmd);
return 0;
}
@@ -1804,15 +1875,18 @@ int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
* online
*/
sc_cmd->result = DID_IMM_RETRY << 16;
- sc_cmd->scsi_done(sc_cmd);
+ scsi_done(sc_cmd);
goto out;
}
- rpriv = rport->dd_data;
-
if (!fc_fcp_lport_queue_ready(lport)) {
- if (lport->qfull)
- fc_fcp_can_queue_ramp_down(lport);
+ if (lport->qfull) {
+ if (fc_fcp_can_queue_ramp_down(lport))
+ shost_printk(KERN_ERR, lport->host,
+ "libfc: queue full, "
+ "reducing can_queue to %d.\n",
+ lport->host->can_queue);
+ }
rc = SCSI_MLQUEUE_HOST_BUSY;
goto out;
}
@@ -1838,23 +1912,18 @@ int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
/*
* setup the data direction
*/
- stats = per_cpu_ptr(lport->dev_stats, get_cpu());
if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
fsp->req_flags = FC_SRB_READ;
- stats->InputRequests++;
- stats->InputBytes += fsp->data_len;
+ this_cpu_inc(lport->stats->InputRequests);
+ this_cpu_add(lport->stats->InputBytes, fsp->data_len);
} else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
fsp->req_flags = FC_SRB_WRITE;
- stats->OutputRequests++;
- stats->OutputBytes += fsp->data_len;
+ this_cpu_inc(lport->stats->OutputRequests);
+ this_cpu_add(lport->stats->OutputBytes, fsp->data_len);
} else {
fsp->req_flags = 0;
- stats->ControlRequests++;
+ this_cpu_inc(lport->stats->ControlRequests);
}
- put_cpu();
-
- init_timer(&fsp->timer);
- fsp->timer.data = (unsigned long)fsp;
/*
* send it to the lower layer
@@ -1892,7 +1961,7 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
fsp->state |= FC_SRB_COMPL;
if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) {
spin_unlock_bh(&fsp->scsi_pkt_lock);
- del_timer_sync(&fsp->timer);
+ timer_delete_sync(&fsp->timer);
spin_lock_bh(&fsp->scsi_pkt_lock);
}
@@ -1907,7 +1976,7 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
fc_fcp_can_queue_ramp_up(lport);
sc_cmd = fsp->cmd;
- CMD_SCSI_STATUS(sc_cmd) = fsp->cdb_status;
+ libfc_priv(sc_cmd)->status = fsp->cdb_status;
switch (fsp->status_code) {
case FC_COMPLETE:
if (fsp->cdb_status == 0) {
@@ -1916,7 +1985,7 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
*/
sc_cmd->result = DID_OK << 16;
if (fsp->scsi_resid)
- CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+ libfc_priv(sc_cmd)->resid_len = fsp->scsi_resid;
} else {
/*
* transport level I/O was ok but scsi
@@ -1949,7 +2018,7 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
*/
FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml "
"due to FC_DATA_UNDRUN (scsi)\n");
- CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+ libfc_priv(sc_cmd)->resid_len = fsp->scsi_resid;
sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
}
break;
@@ -1962,15 +2031,26 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
break;
case FC_CMD_ABORTED:
- FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml "
- "due to FC_CMD_ABORTED\n");
- sc_cmd->result = (DID_ERROR << 16) | fsp->io_status;
+ if (host_byte(sc_cmd->result) == DID_TIME_OUT)
+ FC_FCP_DBG(fsp, "Returning DID_TIME_OUT to scsi-ml "
+ "due to FC_CMD_ABORTED\n");
+ else {
+ FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml "
+ "due to FC_CMD_ABORTED\n");
+ set_host_byte(sc_cmd, DID_ERROR);
+ }
+ sc_cmd->result |= fsp->io_status;
break;
case FC_CMD_RESET:
FC_FCP_DBG(fsp, "Returning DID_RESET to scsi-ml "
"due to FC_CMD_RESET\n");
sc_cmd->result = (DID_RESET << 16);
break;
+ case FC_TRANS_RESET:
+ FC_FCP_DBG(fsp, "Returning DID_SOFT_ERROR to scsi-ml "
+ "due to FC_TRANS_RESET\n");
+ sc_cmd->result = (DID_SOFT_ERROR << 16);
+ break;
case FC_HRD_ERROR:
FC_FCP_DBG(fsp, "Returning DID_NO_CONNECT to scsi-ml "
"due to FC_HRD_ERROR\n");
@@ -1982,9 +2062,9 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
sc_cmd->result = (DID_PARITY << 16);
break;
case FC_TIMED_OUT:
- FC_FCP_DBG(fsp, "Returning DID_BUS_BUSY to scsi-ml "
+ FC_FCP_DBG(fsp, "Returning DID_TIME_OUT to scsi-ml "
"due to FC_TIMED_OUT\n");
- sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
+ sc_cmd->result = (DID_TIME_OUT << 16);
break;
default:
FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml "
@@ -1998,9 +2078,9 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
spin_lock_irqsave(&si->scsi_queue_lock, flags);
list_del(&fsp->list);
- sc_cmd->SCp.ptr = NULL;
+ libfc_priv(sc_cmd)->fsp = NULL;
spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
- sc_cmd->scsi_done(sc_cmd);
+ scsi_done(sc_cmd);
/* release ref from initial allocation in queue command */
fc_fcp_pkt_release(fsp);
@@ -2020,6 +2100,11 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
struct fc_fcp_internal *si;
int rc = FAILED;
unsigned long flags;
+ int rval;
+
+ rval = fc_block_scsi_eh(sc_cmd);
+ if (rval)
+ return rval;
lport = shost_priv(sc_cmd->device->host);
if (lport->state != LPORT_ST_READY)
@@ -2029,13 +2114,13 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
si = fc_get_scsi_internal(lport);
spin_lock_irqsave(&si->scsi_queue_lock, flags);
- fsp = CMD_SP(sc_cmd);
+ fsp = libfc_priv(sc_cmd)->fsp;
if (!fsp) {
/* command completed while scsi eh was setting up */
spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
return SUCCESS;
}
- /* grab a ref so the fsp and sc_cmd cannot be relased from under us */
+ /* grab a ref so the fsp and sc_cmd cannot be released from under us */
fc_fcp_pkt_hold(fsp);
spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
@@ -2069,9 +2154,9 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
int rc = FAILED;
int rval;
- rval = fc_remote_port_chkready(rport);
+ rval = fc_block_scsi_eh(sc_cmd);
if (rval)
- goto out;
+ return rval;
lport = shost_priv(sc_cmd->device->host);
@@ -2117,7 +2202,7 @@ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
FC_SCSI_DBG(lport, "Resetting host\n");
- lport->tt.lport_reset(lport);
+ fc_lport_reset(lport);
wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;
while (!fc_fcp_lport_queue_ready(lport) && time_before(jiffies,
wait_tmo))
@@ -2137,76 +2222,26 @@ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
EXPORT_SYMBOL(fc_eh_host_reset);
/**
- * fc_slave_alloc() - Configure the queue depth of a Scsi_Host
+ * fc_sdev_init() - Configure the queue depth of a Scsi_Host
* @sdev: The SCSI device that identifies the SCSI host
*
* Configures queue depth based on host's cmd_per_len. If not set
* then we use the libfc default.
*/
-int fc_slave_alloc(struct scsi_device *sdev)
+int fc_sdev_init(struct scsi_device *sdev)
{
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, FC_FCP_DFLT_QUEUE_DEPTH);
- else
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev),
- FC_FCP_DFLT_QUEUE_DEPTH);
-
+ scsi_change_queue_depth(sdev, FC_FCP_DFLT_QUEUE_DEPTH);
return 0;
}
-EXPORT_SYMBOL(fc_slave_alloc);
-
-/**
- * fc_change_queue_depth() - Change a device's queue depth
- * @sdev: The SCSI device whose queue depth is to change
- * @qdepth: The new queue depth
- * @reason: The resason for the change
- */
-int fc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, qdepth);
- break;
- case SCSI_QDEPTH_RAMP_UP:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- break;
- default:
- return -EOPNOTSUPP;
- }
- return sdev->queue_depth;
-}
-EXPORT_SYMBOL(fc_change_queue_depth);
-
-/**
- * fc_change_queue_type() - Change a device's queue type
- * @sdev: The SCSI device whose queue depth is to change
- * @tag_type: Identifier for queue type
- */
-int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
-}
-EXPORT_SYMBOL(fc_change_queue_type);
+EXPORT_SYMBOL(fc_sdev_init);
/**
- * fc_fcp_destory() - Tear down the FCP layer for a given local port
+ * fc_fcp_destroy() - Tear down the FCP layer for a given local port
* @lport: The local port that no longer needs the FCP layer
*/
void fc_fcp_destroy(struct fc_lport *lport)
@@ -2241,8 +2276,7 @@ int fc_setup_fcp(void)
void fc_destroy_fcp(void)
{
- if (scsi_pkt_cachep)
- kmem_cache_destroy(scsi_pkt_cachep);
+ kmem_cache_destroy(scsi_pkt_cachep);
}
/**
diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c
index 981329a17c48..f3aefb2deca9 100644
--- a/drivers/scsi/libfc/fc_frame.c
+++ b/drivers/scsi/libfc/fc_frame.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2007 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -49,7 +37,7 @@ u32 fc_frame_crc_check(struct fc_frame *fp)
EXPORT_SYMBOL(fc_frame_crc_check);
/*
- * Allocate a frame intended to be sent via fcoe_xmit.
+ * Allocate a frame intended to be sent.
* Get an sk_buff for the frame and set the length.
*/
struct fc_frame *_fc_frame_alloc(size_t len)
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index b7735129f1f3..0e6a1355d020 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2009 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -21,10 +9,11 @@
#include <linux/types.h>
#include <linux/scatterlist.h>
#include <linux/crc32.h>
+#include <linux/module.h>
#include <scsi/libfc.h>
-#include <scsi/fc_encode.h>
+#include "fc_encode.h"
#include "fc_libfc.h"
MODULE_AUTHOR("Open-FCoE.org");
@@ -104,14 +93,13 @@ module_exit(libfc_exit);
* @sg: pointer to the pointer of the SG list.
* @nents: pointer to the remaining number of entries in the SG list.
* @offset: pointer to the current offset in the SG list.
- * @km_type: dedicated page table slot type for kmap_atomic.
* @crc: pointer to the 32-bit crc value.
* If crc is NULL, CRC is not calculated.
*/
u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
struct scatterlist *sg,
u32 *nents, size_t *offset,
- enum km_type km_type, u32 *crc)
+ u32 *crc)
{
size_t remaining = len;
u32 copy_len = 0;
@@ -141,12 +129,11 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
off = *offset + sg->offset;
sg_bytes = min(sg_bytes,
(size_t)(PAGE_SIZE - (off & ~PAGE_MASK)));
- page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
- km_type);
+ page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT));
if (crc)
*crc = crc32(*crc, buf, sg_bytes);
memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes);
- kunmap_atomic(page_addr, km_type);
+ kunmap_atomic(page_addr);
buf += sg_bytes;
*offset += sg_bytes;
remaining -= sg_bytes;
@@ -179,7 +166,7 @@ void fc_fill_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
fill = -fr_len(fp) & 3;
if (fill) {
/* TODO, this may be a problem with fragmented skb */
- memset(skb_put(fp_skb(fp), fill), 0, fill);
+ skb_put_zero(fp_skb(fp), fill);
f_ctl |= fill;
}
fr_eof(fp) = FC_EOF_T;
@@ -227,7 +214,7 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
sp = fr_seq(in_fp);
if (sp)
- fr_seq(fp) = fr_dev(in_fp)->tt.seq_start_next(sp);
+ fr_seq(fp) = fc_seq_start_next(sp);
fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset);
}
EXPORT_SYMBOL(fc_fill_reply_hdr);
@@ -297,9 +284,9 @@ void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
BUG_ON(type >= FC_FC4_PROV_SIZE);
mutex_lock(&fc_prov_mutex);
if (prov->recv)
- rcu_assign_pointer(fc_passive_prov[type], NULL);
+ RCU_INIT_POINTER(fc_passive_prov[type], NULL);
else
- rcu_assign_pointer(fc_active_prov[type], NULL);
+ RCU_INIT_POINTER(fc_active_prov[type], NULL);
mutex_unlock(&fc_prov_mutex);
synchronize_rcu();
}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index c7d071289af5..685e3bdd033a 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2009 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -41,25 +29,25 @@ extern unsigned int fc_debug_logging;
#define FC_LIBFC_DBG(fmt, args...) \
FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \
- printk(KERN_INFO "libfc: " fmt, ##args))
+ pr_info("libfc: " fmt, ##args))
#define FC_LPORT_DBG(lport, fmt, args...) \
FC_CHECK_LOGGING(FC_LPORT_LOGGING, \
- printk(KERN_INFO "host%u: lport %6.6x: " fmt, \
- (lport)->host->host_no, \
- (lport)->port_id, ##args))
+ pr_info("host%u: lport %6.6x: " fmt, \
+ (lport)->host->host_no, \
+ (lport)->port_id, ##args))
-#define FC_DISC_DBG(disc, fmt, args...) \
- FC_CHECK_LOGGING(FC_DISC_LOGGING, \
- printk(KERN_INFO "host%u: disc: " fmt, \
- fc_disc_lport(disc)->host->host_no, \
- ##args))
+#define FC_DISC_DBG(disc, fmt, args...) \
+ FC_CHECK_LOGGING(FC_DISC_LOGGING, \
+ pr_info("host%u: disc: " fmt, \
+ fc_disc_lport(disc)->host->host_no, \
+ ##args))
#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \
FC_CHECK_LOGGING(FC_RPORT_LOGGING, \
- printk(KERN_INFO "host%u: rport %6.6x: " fmt, \
- (lport)->host->host_no, \
- (port_id), ##args))
+ pr_info("host%u: rport %6.6x: " fmt, \
+ (lport)->host->host_no, \
+ (port_id), ##args))
#define FC_RPORT_DBG(rdata, fmt, args...) \
FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args)
@@ -70,13 +58,13 @@ extern unsigned int fc_debug_logging;
if ((pkt)->seq_ptr) { \
struct fc_exch *_ep = NULL; \
_ep = fc_seq_exch((pkt)->seq_ptr); \
- printk(KERN_INFO "host%u: fcp: %6.6x: " \
+ pr_info("host%u: fcp: %6.6x: " \
"xid %04x-%04x: " fmt, \
(pkt)->lp->host->host_no, \
(pkt)->rport->port_id, \
(_ep)->oxid, (_ep)->rxid, ##args); \
} else { \
- printk(KERN_INFO "host%u: fcp: %6.6x: " fmt, \
+ pr_info("host%u: fcp: %6.6x: " fmt, \
(pkt)->lp->host->host_no, \
(pkt)->rport->port_id, ##args); \
} \
@@ -84,14 +72,14 @@ extern unsigned int fc_debug_logging;
#define FC_EXCH_DBG(exch, fmt, args...) \
FC_CHECK_LOGGING(FC_EXCH_LOGGING, \
- printk(KERN_INFO "host%u: xid %4x: " fmt, \
- (exch)->lp->host->host_no, \
- exch->xid, ##args))
+ pr_info("host%u: xid %4x: " fmt, \
+ (exch)->lp->host->host_no, \
+ exch->xid, ##args))
#define FC_SCSI_DBG(lport, fmt, args...) \
FC_CHECK_LOGGING(FC_SCSI_LOGGING, \
- printk(KERN_INFO "host%u: scsi: " fmt, \
- (lport)->host->host_no, ##args))
+ pr_info("host%u: scsi: " fmt, \
+ (lport)->host->host_no, ##args))
/*
* FC-4 Providers.
@@ -134,6 +122,6 @@ extern void fc_fc4_conf_lport_params(struct fc_lport *, enum fc_fh_type);
u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
struct scatterlist *sg,
u32 *nents, size_t *offset,
- enum km_type km_type, u32 *crc);
+ u32 *crc);
#endif /* _FC_LIBFC_H_ */
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 389ab80aef0a..310fa5add5f0 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2007 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -88,22 +76,27 @@
*/
#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <scsi/fc/fc_gs.h>
#include <scsi/libfc.h>
-#include <scsi/fc_encode.h>
#include <linux/scatterlist.h>
+#include "fc_encode.h"
#include "fc_libfc.h"
/* Fabric IDs to use for point-to-point mode, chosen on whims. */
#define FC_LOCAL_PTP_FID_LO 0x010101
#define FC_LOCAL_PTP_FID_HI 0x010102
-#define DNS_DELAY 3 /* Discovery delay after RSCN (in seconds)*/
+#define DNS_DELAY 3 /* Discovery delay after RSCN (in seconds)*/
+#define MAX_CT_PAYLOAD 2048
+#define DISCOVERED_PORTS 4
+#define NUMBER_OF_PORTS 1
static void fc_lport_error(struct fc_lport *, struct fc_frame *);
@@ -114,6 +107,8 @@ static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state);
static void fc_lport_enter_scr(struct fc_lport *);
static void fc_lport_enter_ready(struct fc_lport *);
static void fc_lport_enter_logo(struct fc_lport *);
+static void fc_lport_enter_fdmi(struct fc_lport *lport);
+static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state);
static const char *fc_lport_state_names[] = {
[LPORT_ST_DISABLED] = "disabled",
@@ -124,6 +119,11 @@ static const char *fc_lport_state_names[] = {
[LPORT_ST_RSPN_ID] = "RSPN_ID",
[LPORT_ST_RFT_ID] = "RFT_ID",
[LPORT_ST_RFF_ID] = "RFF_ID",
+ [LPORT_ST_FDMI] = "FDMI",
+ [LPORT_ST_RHBA] = "RHBA",
+ [LPORT_ST_RPA] = "RPA",
+ [LPORT_ST_DHBA] = "DHBA",
+ [LPORT_ST_DPRT] = "DPRT",
[LPORT_ST_SCR] = "SCR",
[LPORT_ST_READY] = "Ready",
[LPORT_ST_LOGO] = "LOGO",
@@ -140,7 +140,7 @@ static const char *fc_lport_state_names[] = {
* @offset: The offset into the response data
*/
struct fc_bsg_info {
- struct fc_bsg_job *job;
+ struct bsg_job *job;
struct fc_lport *lport;
u16 rsp_code;
struct scatterlist *sg;
@@ -181,20 +181,26 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
if (lport->state == LPORT_ST_DNS) {
lport->dns_rdata = rdata;
fc_lport_enter_ns(lport, LPORT_ST_RNN_ID);
+ } else if (lport->state == LPORT_ST_FDMI) {
+ lport->ms_rdata = rdata;
+ fc_lport_enter_ms(lport, LPORT_ST_DHBA);
} else {
FC_LPORT_DBG(lport, "Received an READY event "
"on port (%6.6x) for the directory "
"server, but the lport is not "
- "in the DNS state, it's in the "
+ "in the DNS or FDMI state, it's in the "
"%d state", rdata->ids.port_id,
lport->state);
- lport->tt.rport_logoff(rdata);
+ fc_rport_logoff(rdata);
}
break;
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
- lport->dns_rdata = NULL;
+ if (rdata->ids.port_id == FC_FID_DIR_SERV)
+ lport->dns_rdata = NULL;
+ else if (rdata->ids.port_id == FC_FID_MGMT_SERV)
+ lport->ms_rdata = NULL;
break;
case RPORT_EV_NONE:
break;
@@ -227,18 +233,26 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
u32 remote_fid, u64 remote_wwpn,
u64 remote_wwnn)
{
- mutex_lock(&lport->disc.disc_mutex);
+ lockdep_assert_held(&lport->lp_mutex);
+
if (lport->ptp_rdata) {
- lport->tt.rport_logoff(lport->ptp_rdata);
- kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
+ fc_rport_logoff(lport->ptp_rdata);
+ kref_put(&lport->ptp_rdata->kref, fc_rport_destroy);
+ }
+ mutex_lock(&lport->disc.disc_mutex);
+ lport->ptp_rdata = fc_rport_create(lport, remote_fid);
+ if (!lport->ptp_rdata) {
+ printk(KERN_WARNING "libfc: Failed to setup lport 0x%x\n",
+ lport->port_id);
+ mutex_unlock(&lport->disc.disc_mutex);
+ return;
}
- lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
kref_get(&lport->ptp_rdata->kref);
lport->ptp_rdata->ids.port_name = remote_wwpn;
lport->ptp_rdata->ids.node_name = remote_wwnn;
mutex_unlock(&lport->disc.disc_mutex);
- lport->tt.rport_login(lport->ptp_rdata);
+ fc_rport_login(lport->ptp_rdata);
fc_lport_enter_ready(lport);
}
@@ -284,47 +298,51 @@ EXPORT_SYMBOL(fc_get_host_speed);
*/
struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
{
- struct fc_host_statistics *fcoe_stats;
+ struct fc_host_statistics *fc_stats;
struct fc_lport *lport = shost_priv(shost);
- struct timespec v0, v1;
unsigned int cpu;
u64 fcp_in_bytes = 0;
u64 fcp_out_bytes = 0;
- fcoe_stats = &lport->host_stats;
- memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
+ fc_stats = &lport->host_stats;
+ memset(fc_stats, 0, sizeof(struct fc_host_statistics));
- jiffies_to_timespec(jiffies, &v0);
- jiffies_to_timespec(lport->boot_time, &v1);
- fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
+ fc_stats->seconds_since_last_reset = (jiffies - lport->boot_time) / HZ;
for_each_possible_cpu(cpu) {
- struct fcoe_dev_stats *stats;
-
- stats = per_cpu_ptr(lport->dev_stats, cpu);
-
- fcoe_stats->tx_frames += stats->TxFrames;
- fcoe_stats->tx_words += stats->TxWords;
- fcoe_stats->rx_frames += stats->RxFrames;
- fcoe_stats->rx_words += stats->RxWords;
- fcoe_stats->error_frames += stats->ErrorFrames;
- fcoe_stats->invalid_crc_count += stats->InvalidCRCCount;
- fcoe_stats->fcp_input_requests += stats->InputRequests;
- fcoe_stats->fcp_output_requests += stats->OutputRequests;
- fcoe_stats->fcp_control_requests += stats->ControlRequests;
- fcp_in_bytes += stats->InputBytes;
- fcp_out_bytes += stats->OutputBytes;
- fcoe_stats->link_failure_count += stats->LinkFailureCount;
+ struct fc_stats *stats;
+
+ stats = per_cpu_ptr(lport->stats, cpu);
+
+ fc_stats->tx_frames += READ_ONCE(stats->TxFrames);
+ fc_stats->tx_words += READ_ONCE(stats->TxWords);
+ fc_stats->rx_frames += READ_ONCE(stats->RxFrames);
+ fc_stats->rx_words += READ_ONCE(stats->RxWords);
+ fc_stats->error_frames += READ_ONCE(stats->ErrorFrames);
+ fc_stats->invalid_crc_count += READ_ONCE(stats->InvalidCRCCount);
+ fc_stats->fcp_input_requests += READ_ONCE(stats->InputRequests);
+ fc_stats->fcp_output_requests += READ_ONCE(stats->OutputRequests);
+ fc_stats->fcp_control_requests += READ_ONCE(stats->ControlRequests);
+ fcp_in_bytes += READ_ONCE(stats->InputBytes);
+ fcp_out_bytes += READ_ONCE(stats->OutputBytes);
+ fc_stats->fcp_packet_alloc_failures += READ_ONCE(stats->FcpPktAllocFails);
+ fc_stats->fcp_packet_aborts += READ_ONCE(stats->FcpPktAborts);
+ fc_stats->fcp_frame_alloc_failures += READ_ONCE(stats->FcpFrameAllocFails);
+ fc_stats->link_failure_count += READ_ONCE(stats->LinkFailureCount);
}
- fcoe_stats->fcp_input_megabytes = div_u64(fcp_in_bytes, 1000000);
- fcoe_stats->fcp_output_megabytes = div_u64(fcp_out_bytes, 1000000);
- fcoe_stats->lip_count = -1;
- fcoe_stats->nos_count = -1;
- fcoe_stats->loss_of_sync_count = -1;
- fcoe_stats->loss_of_signal_count = -1;
- fcoe_stats->prim_seq_protocol_err_count = -1;
- fcoe_stats->dumped_frames = -1;
- return fcoe_stats;
+ fc_stats->fcp_input_megabytes = div_u64(fcp_in_bytes, 1000000);
+ fc_stats->fcp_output_megabytes = div_u64(fcp_out_bytes, 1000000);
+ fc_stats->lip_count = -1;
+ fc_stats->nos_count = -1;
+ fc_stats->loss_of_sync_count = -1;
+ fc_stats->loss_of_signal_count = -1;
+ fc_stats->prim_seq_protocol_err_count = -1;
+ fc_stats->dumped_frames = -1;
+
+ /* update exches stats */
+ fc_exch_update_stats(lport);
+
+ return fc_stats;
}
EXPORT_SYMBOL(fc_get_host_stats);
@@ -381,26 +399,22 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
* fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report.
* @lport: Fibre Channel local port receiving the RLIR
* @fp: The RLIR request frame
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this function.
*/
static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp)
{
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n",
fc_lport_state(lport));
- lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
+ fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
fc_frame_free(fp);
}
/**
* fc_lport_recv_echo_req() - Handle received ECHO request
* @lport: The local port receiving the ECHO
- * @fp: ECHO request frame
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this function.
+ * @in_fp: ECHO request frame
*/
static void fc_lport_recv_echo_req(struct fc_lport *lport,
struct fc_frame *in_fp)
@@ -410,6 +424,8 @@ static void fc_lport_recv_echo_req(struct fc_lport *lport,
void *pp;
void *dp;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n",
fc_lport_state(lport));
@@ -433,10 +449,7 @@ static void fc_lport_recv_echo_req(struct fc_lport *lport,
/**
* fc_lport_recv_rnid_req() - Handle received Request Node ID data request
* @lport: The local port receiving the RNID
- * @fp: The RNID request frame
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this function.
+ * @in_fp: The RNID request frame
*/
static void fc_lport_recv_rnid_req(struct fc_lport *lport,
struct fc_frame *in_fp)
@@ -452,6 +465,8 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport,
u8 fmt;
size_t len;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Received RNID request while in state %s\n",
fc_lport_state(lport));
@@ -459,7 +474,7 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport,
if (!req) {
rjt_data.reason = ELS_RJT_LOGIC;
rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
} else {
fmt = req->rnid_fmt;
len = sizeof(*rp);
@@ -493,13 +508,12 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport,
* fc_lport_recv_logo_req() - Handle received fabric LOGO request
* @lport: The local port receiving the LOGO
* @fp: The LOGO request frame
- *
- * Locking Note: The lport lock is exected to be held before calling
- * this function.
*/
static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
{
- lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
+ lockdep_assert_held(&lport->lp_mutex);
+
+ fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
fc_lport_enter_reset(lport);
fc_frame_free(fp);
}
@@ -531,11 +545,11 @@ EXPORT_SYMBOL(fc_fabric_login);
/**
* __fc_linkup() - Handler for transport linkup events
* @lport: The lport whose link is up
- *
- * Locking: must be called with the lp_mutex held
*/
void __fc_linkup(struct fc_lport *lport)
{
+ lockdep_assert_held(&lport->lp_mutex);
+
if (!lport->link_up) {
lport->link_up = 1;
@@ -562,11 +576,11 @@ EXPORT_SYMBOL(fc_linkup);
/**
* __fc_linkdown() - Handler for transport linkdown events
* @lport: The lport whose link is down
- *
- * Locking: must be called with the lp_mutex held
*/
void __fc_linkdown(struct fc_lport *lport)
{
+ lockdep_assert_held(&lport->lp_mutex);
+
if (lport->link_up) {
lport->link_up = 0;
fc_lport_enter_reset(lport);
@@ -601,9 +615,9 @@ int fc_fabric_logoff(struct fc_lport *lport)
lport->tt.disc_stop_final(lport);
mutex_lock(&lport->lp_mutex);
if (lport->dns_rdata)
- lport->tt.rport_logoff(lport->dns_rdata);
+ fc_rport_logoff(lport->dns_rdata);
mutex_unlock(&lport->lp_mutex);
- lport->tt.rport_flush_queue();
+ fc_rport_flush_queue();
mutex_lock(&lport->lp_mutex);
fc_lport_enter_logo(lport);
mutex_unlock(&lport->lp_mutex);
@@ -633,6 +647,7 @@ int fc_lport_destroy(struct fc_lport *lport)
lport->tt.fcp_abort_io(lport);
lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
+ cancel_delayed_work_sync(&lport->retry_work);
fc_fc4_del_lport(lport);
return 0;
}
@@ -675,7 +690,8 @@ EXPORT_SYMBOL(fc_set_mfs);
* @lport: The local port receiving the event
* @event: The discovery event
*/
-void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
+static void fc_lport_disc_callback(struct fc_lport *lport,
+ enum fc_disc_event event)
{
switch (event) {
case DISC_EV_SUCCESS:
@@ -696,14 +712,13 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
}
/**
- * fc_rport_enter_ready() - Enter the ready state and start discovery
+ * fc_lport_enter_ready() - Enter the ready state and start discovery
* @lport: The local port that is ready
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
static void fc_lport_enter_ready(struct fc_lport *lport)
{
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered READY from state %s\n",
fc_lport_state(lport));
@@ -721,13 +736,12 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
* @lport: The local port which will have its Port ID set.
* @port_id: The new port ID.
* @fp: The frame containing the incoming request, or NULL.
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this function.
*/
static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
struct fc_frame *fp)
{
+ lockdep_assert_held(&lport->lp_mutex);
+
if (port_id)
printk(KERN_INFO "host%d: Assigned Port ID %6.6x\n",
lport->host->host_no, port_id);
@@ -742,7 +756,7 @@ static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
}
/**
- * fc_lport_set_port_id() - set the local port Port ID for point-to-multipoint
+ * fc_lport_set_local_id() - set the local port Port ID for point-to-multipoint
* @lport: The local port which will have its Port ID set.
* @port_id: The new port ID.
*
@@ -777,9 +791,6 @@ EXPORT_SYMBOL(fc_lport_set_local_id);
* A received FLOGI request indicates a point-to-point connection.
* Accept it with the common service parameters indicating our N port.
* Set up to do a PLOGI if we have the higher-number WWPN.
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this function.
*/
static void fc_lport_recv_flogi_req(struct fc_lport *lport,
struct fc_frame *rx_fp)
@@ -792,6 +803,8 @@ static void fc_lport_recv_flogi_req(struct fc_lport *lport,
u32 remote_fid;
u32 local_fid;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n",
fc_lport_state(lport));
@@ -863,8 +876,6 @@ out:
static void fc_lport_recv_els_req(struct fc_lport *lport,
struct fc_frame *fp)
{
- void (*recv)(struct fc_lport *, struct fc_frame *);
-
mutex_lock(&lport->lp_mutex);
/*
@@ -878,31 +889,35 @@ static void fc_lport_recv_els_req(struct fc_lport *lport,
/*
* Check opcode.
*/
- recv = lport->tt.rport_recv_req;
switch (fc_frame_payload_op(fp)) {
case ELS_FLOGI:
if (!lport->point_to_multipoint)
- recv = fc_lport_recv_flogi_req;
+ fc_lport_recv_flogi_req(lport, fp);
+ else
+ fc_rport_recv_req(lport, fp);
break;
case ELS_LOGO:
if (fc_frame_sid(fp) == FC_FID_FLOGI)
- recv = fc_lport_recv_logo_req;
+ fc_lport_recv_logo_req(lport, fp);
+ else
+ fc_rport_recv_req(lport, fp);
break;
case ELS_RSCN:
- recv = lport->tt.disc_recv_req;
+ lport->tt.disc_recv_req(lport, fp);
break;
case ELS_ECHO:
- recv = fc_lport_recv_echo_req;
+ fc_lport_recv_echo_req(lport, fp);
break;
case ELS_RLIR:
- recv = fc_lport_recv_rlir_req;
+ fc_lport_recv_rlir_req(lport, fp);
break;
case ELS_RNID:
- recv = fc_lport_recv_rnid_req;
+ fc_lport_recv_rnid_req(lport, fp);
+ break;
+ default:
+ fc_rport_recv_req(lport, fp);
break;
}
-
- recv(lport, fp);
}
mutex_unlock(&lport->lp_mutex);
}
@@ -920,15 +935,14 @@ struct fc4_prov fc_lport_els_prov = {
};
/**
- * fc_lport_recv_req() - The generic lport request handler
+ * fc_lport_recv() - The generic lport request handler
* @lport: The lport that received the request
* @fp: The frame the request is in
*
* Locking Note: This function should not be called with the lport
* lock held because it may grab the lock.
*/
-static void fc_lport_recv_req(struct fc_lport *lport,
- struct fc_frame *fp)
+void fc_lport_recv(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
struct fc_seq *sp = fr_seq(fp);
@@ -956,8 +970,10 @@ drop:
rcu_read_unlock();
FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
fc_frame_free(fp);
- lport->tt.exch_done(sp);
+ if (sp)
+ fc_exch_done(sp);
}
+EXPORT_SYMBOL(fc_lport_recv);
/**
* fc_lport_reset() - Reset a local port
@@ -979,18 +995,19 @@ EXPORT_SYMBOL(fc_lport_reset);
/**
* fc_lport_reset_locked() - Reset the local port w/ the lport lock held
* @lport: The local port to be reset
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
static void fc_lport_reset_locked(struct fc_lport *lport)
{
- if (lport->dns_rdata)
- lport->tt.rport_logoff(lport->dns_rdata);
+ lockdep_assert_held(&lport->lp_mutex);
+
+ if (lport->dns_rdata) {
+ fc_rport_logoff(lport->dns_rdata);
+ lport->dns_rdata = NULL;
+ }
if (lport->ptp_rdata) {
- lport->tt.rport_logoff(lport->ptp_rdata);
- kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
+ fc_rport_logoff(lport->ptp_rdata);
+ kref_put(&lport->ptp_rdata->kref, fc_rport_destroy);
lport->ptp_rdata = NULL;
}
@@ -1006,12 +1023,11 @@ static void fc_lport_reset_locked(struct fc_lport *lport)
/**
* fc_lport_enter_reset() - Reset the local port
* @lport: The local port to be reset
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
static void fc_lport_enter_reset(struct fc_lport *lport)
{
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
fc_lport_state(lport));
@@ -1025,6 +1041,8 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
}
fc_lport_state_enter(lport, LPORT_ST_RESET);
+ fc_host_post_event(lport->host, fc_get_event_number(),
+ FCH_EVT_LIPRESET, 0);
fc_vports_linkchange(lport);
fc_lport_reset_locked(lport);
if (lport->link_up)
@@ -1034,12 +1052,11 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
/**
* fc_lport_enter_disabled() - Disable the local port
* @lport: The local port to be reset
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
static void fc_lport_enter_disabled(struct fc_lport *lport)
{
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered disabled state from %s state\n",
fc_lport_state(lport));
@@ -1061,7 +1078,7 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
{
unsigned long delay = 0;
FC_LPORT_DBG(lport, "Error %ld in state %s, retries %d\n",
- PTR_ERR(fp), fc_lport_state(lport),
+ IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_lport_state(lport),
lport->retry_count);
if (PTR_ERR(fp) == -FC_EX_CLOSED)
@@ -1143,7 +1160,10 @@ static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp,
fc_lport_enter_ns(lport, LPORT_ST_RFF_ID);
break;
case LPORT_ST_RFF_ID:
- fc_lport_enter_scr(lport);
+ if (lport->fdmi_enabled)
+ fc_lport_enter_fdmi(lport);
+ else
+ fc_lport_enter_scr(lport);
break;
default:
/* should have already been caught by state checks */
@@ -1158,6 +1178,91 @@ err:
}
/**
+ * fc_lport_ms_resp() - Handle response to a management server
+ * exchange
+ * @sp: current sequence in exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
+ * and then unlock the lport.
+ */
+static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+ struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host);
+ FC_LPORT_DBG(lport, "Received a ms %s\n", fc_els_resp_type(fp));
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) {
+ FC_LPORT_DBG(lport, "Received a management server response, "
+ "but in state %s\n", fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+
+ if (fh && ct && fh->fh_type == FC_TYPE_CT &&
+ ct->ct_fs_type == FC_FST_MGMT &&
+ ct->ct_fs_subtype == FC_FDMI_SUBTYPE) {
+ FC_LPORT_DBG(lport, "Received a management server response, "
+ "reason=%d explain=%d\n",
+ ct->ct_reason,
+ ct->ct_explan);
+
+ switch (lport->state) {
+ case LPORT_ST_RHBA:
+ if ((ntohs(ct->ct_cmd) == FC_FS_RJT) && fc_host->fdmi_version == FDMI_V2) {
+ FC_LPORT_DBG(lport, "Error for FDMI-V2, fall back to FDMI-V1\n");
+ fc_host->fdmi_version = FDMI_V1;
+
+ fc_lport_enter_ms(lport, LPORT_ST_RHBA);
+
+ } else if (ntohs(ct->ct_cmd) == FC_FS_ACC)
+ fc_lport_enter_ms(lport, LPORT_ST_RPA);
+ else /* Error Skip RPA */
+ fc_lport_enter_scr(lport);
+ break;
+ case LPORT_ST_RPA:
+ fc_lport_enter_scr(lport);
+ break;
+ case LPORT_ST_DPRT:
+ fc_lport_enter_ms(lport, LPORT_ST_RHBA);
+ break;
+ case LPORT_ST_DHBA:
+ fc_lport_enter_ms(lport, LPORT_ST_DPRT);
+ break;
+ default:
+ /* should have already been caught by state checks */
+ break;
+ }
+ } else {
+ /* Invalid Frame? */
+ fc_lport_error(lport, fp);
+ }
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
* fc_lport_scr_resp() - Handle response to State Change Register (SCR) request
* @sp: current sequence in SCR exchange
* @fp: response frame
@@ -1208,14 +1313,13 @@ err:
/**
* fc_lport_enter_scr() - Send a SCR (State Change Register) request
* @lport: The local port to register for state changes
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
static void fc_lport_enter_scr(struct fc_lport *lport)
{
struct fc_frame *fp;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered SCR state from %s state\n",
fc_lport_state(lport));
@@ -1236,9 +1340,7 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
/**
* fc_lport_enter_ns() - register some object with the name server
* @lport: Fibre Channel local port to register
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
+ * @state: Local port state
*/
static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
{
@@ -1247,6 +1349,8 @@ static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
int size = sizeof(struct fc_ct_hdr);
size_t len;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
fc_lport_state_names[state],
fc_lport_state(lport));
@@ -1304,29 +1408,174 @@ static struct fc_rport_operations fc_lport_rport_ops = {
};
/**
- * fc_rport_enter_dns() - Create a fc_rport for the name server
+ * fc_lport_enter_dns() - Create a fc_rport for the name server
* @lport: The local port requesting a remote port for the name server
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
static void fc_lport_enter_dns(struct fc_lport *lport)
{
struct fc_rport_priv *rdata;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered DNS state from %s state\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_DNS);
mutex_lock(&lport->disc.disc_mutex);
- rdata = lport->tt.rport_create(lport, FC_FID_DIR_SERV);
+ rdata = fc_rport_create(lport, FC_FID_DIR_SERV);
mutex_unlock(&lport->disc.disc_mutex);
if (!rdata)
goto err;
rdata->ops = &fc_lport_rport_ops;
- lport->tt.rport_login(rdata);
+ fc_rport_login(rdata);
+ return;
+
+err:
+ fc_lport_error(lport, NULL);
+}
+
+/**
+ * fc_lport_enter_ms() - management server commands
+ * @lport: Fibre Channel local port to register
+ * @state: Local port state
+ */
+static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state)
+{
+ struct fc_frame *fp;
+ enum fc_fdmi_req cmd;
+ int size = sizeof(struct fc_ct_hdr);
+ size_t len;
+ int numattrs;
+ struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host);
+ lockdep_assert_held(&lport->lp_mutex);
+
+ FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
+ fc_lport_state_names[state],
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, state);
+
+ switch (state) {
+ case LPORT_ST_RHBA:
+ cmd = FC_FDMI_RHBA;
+ /* Number of HBA Attributes */
+ numattrs = 11;
+ len = sizeof(struct fc_fdmi_rhba);
+ len -= sizeof(struct fc_fdmi_attr_entry);
+
+ len += FC_FDMI_HBA_ATTR_NODENAME_LEN;
+ len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN;
+ len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN;
+ len += FC_FDMI_HBA_ATTR_MODEL_LEN;
+ len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN;
+ len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN;
+
+
+ if (fc_host->fdmi_version == FDMI_V2) {
+ numattrs += 7;
+ len += FC_FDMI_HBA_ATTR_NODESYMBLNAME_LEN;
+ len += FC_FDMI_HBA_ATTR_VENDORSPECIFICINFO_LEN;
+ len += FC_FDMI_HBA_ATTR_NUMBEROFPORTS_LEN;
+ len += FC_FDMI_HBA_ATTR_FABRICNAME_LEN;
+ len += FC_FDMI_HBA_ATTR_BIOSVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_BIOSSTATE_LEN;
+ len += FC_FDMI_HBA_ATTR_VENDORIDENTIFIER_LEN;
+ }
+
+ len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+
+ size += len;
+ break;
+ case LPORT_ST_RPA:
+ cmd = FC_FDMI_RPA;
+ /* Number of Port Attributes */
+ numattrs = 6;
+ len = sizeof(struct fc_fdmi_rpa);
+ len -= sizeof(struct fc_fdmi_attr_entry);
+ len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN;
+ len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN;
+ len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN;
+ len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN;
+ len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN;
+ len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN;
+
+ if (fc_host->fdmi_version == FDMI_V2) {
+ numattrs += 10;
+ len += FC_FDMI_PORT_ATTR_NODENAME_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTNAME_LEN;
+ len += FC_FDMI_PORT_ATTR_SYMBOLICNAME_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTTYPE_LEN;
+ len += FC_FDMI_PORT_ATTR_SUPPORTEDCLASSSRVC_LEN;
+ len += FC_FDMI_PORT_ATTR_FABRICNAME_LEN;
+ len += FC_FDMI_PORT_ATTR_CURRENTFC4TYPE_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTSTATE_LEN;
+ len += FC_FDMI_PORT_ATTR_DISCOVEREDPORTS_LEN;
+ len += FC_FDMI_PORT_ATTR_PORTID_LEN;
+ }
+
+ len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+
+ size += len;
+ break;
+ case LPORT_ST_DPRT:
+ cmd = FC_FDMI_DPRT;
+ len = sizeof(struct fc_fdmi_dprt);
+ size += len;
+ break;
+ case LPORT_ST_DHBA:
+ cmd = FC_FDMI_DHBA;
+ len = sizeof(struct fc_fdmi_dhba);
+ size += len;
+ break;
+ default:
+ fc_lport_error(lport, NULL);
+ return;
+ }
+
+ FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n",
+ cmd, (int)len, size);
+ fp = fc_frame_alloc(lport, size);
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, FC_FID_MGMT_SERV, fp, cmd,
+ fc_lport_ms_resp,
+ lport, 3 * lport->r_a_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_lport_enter_fdmi() - Create a fc_rport for the management server
+ * @lport: The local port requesting a remote port for the management server
+ */
+static void fc_lport_enter_fdmi(struct fc_lport *lport)
+{
+ struct fc_rport_priv *rdata;
+
+ lockdep_assert_held(&lport->lp_mutex);
+
+ FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n",
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_FDMI);
+
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = fc_rport_create(lport, FC_FID_MGMT_SERV);
+ mutex_unlock(&lport->disc.disc_mutex);
+ if (!rdata)
+ goto err;
+
+ rdata->ops = &fc_lport_rport_ops;
+ fc_rport_login(rdata);
return;
err:
@@ -1342,15 +1591,14 @@ static void fc_lport_timeout(struct work_struct *work)
struct fc_lport *lport =
container_of(work, struct fc_lport,
retry_work.work);
+ struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host);
mutex_lock(&lport->lp_mutex);
switch (lport->state) {
case LPORT_ST_DISABLED:
- WARN_ON(1);
break;
case LPORT_ST_READY:
- WARN_ON(1);
break;
case LPORT_ST_RESET:
break;
@@ -1367,6 +1615,23 @@ static void fc_lport_timeout(struct work_struct *work)
case LPORT_ST_RFF_ID:
fc_lport_enter_ns(lport, lport->state);
break;
+ case LPORT_ST_FDMI:
+ fc_lport_enter_fdmi(lport);
+ break;
+ case LPORT_ST_RHBA:
+ if (fc_host->fdmi_version == FDMI_V2) {
+ FC_LPORT_DBG(lport, "timeout for FDMI-V2 RHBA,fall back to FDMI-V1\n");
+ fc_host->fdmi_version = FDMI_V1;
+ fc_lport_enter_ms(lport, LPORT_ST_RHBA);
+ break;
+ }
+ fallthrough;
+ case LPORT_ST_RPA:
+ case LPORT_ST_DHBA:
+ case LPORT_ST_DPRT:
+ FC_LPORT_DBG(lport, "Skipping lport state %s to SCR\n",
+ fc_lport_state(lport));
+ fallthrough;
case LPORT_ST_SCR:
fc_lport_enter_scr(lport);
break;
@@ -1428,17 +1693,16 @@ err:
EXPORT_SYMBOL(fc_lport_logo_resp);
/**
- * fc_rport_enter_logo() - Logout of the fabric
+ * fc_lport_enter_logo() - Logout of the fabric
* @lport: The local port to be logged out
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
static void fc_lport_enter_logo(struct fc_lport *lport)
{
struct fc_frame *fp;
struct fc_els_logo *logo;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered LOGO state from %s state\n",
fc_lport_state(lport));
@@ -1471,6 +1735,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
void *lp_arg)
{
struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
struct fc_els_flogi *flp;
u32 did;
u16 csp_flags;
@@ -1498,49 +1763,68 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
goto err;
}
+ fh = fc_frame_header_get(fp);
did = fc_frame_did(fp);
- if (fc_frame_payload_op(fp) == ELS_LS_ACC && did) {
- flp = fc_frame_payload_get(fp, sizeof(*flp));
- if (flp) {
- mfs = ntohs(flp->fl_csp.sp_bb_data) &
- FC_SP_BB_DATA_MASK;
- if (mfs >= FC_SP_MIN_MAX_PAYLOAD &&
- mfs < lport->mfs)
- lport->mfs = mfs;
- csp_flags = ntohs(flp->fl_csp.sp_features);
- r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
- e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
- if (csp_flags & FC_SP_FT_EDTR)
- e_d_tov /= 1000000;
-
- lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC);
-
- if ((csp_flags & FC_SP_FT_FPORT) == 0) {
- if (e_d_tov > lport->e_d_tov)
- lport->e_d_tov = e_d_tov;
- lport->r_a_tov = 2 * e_d_tov;
- fc_lport_set_port_id(lport, did, fp);
- printk(KERN_INFO "host%d: libfc: "
- "Port (%6.6x) entered "
- "point-to-point mode\n",
- lport->host->host_no, did);
- fc_lport_ptp_setup(lport, fc_frame_sid(fp),
- get_unaligned_be64(
- &flp->fl_wwpn),
- get_unaligned_be64(
- &flp->fl_wwnn));
- } else {
- lport->e_d_tov = e_d_tov;
- lport->r_a_tov = r_a_tov;
- fc_host_fabric_name(lport->host) =
- get_unaligned_be64(&flp->fl_wwnn);
- fc_lport_set_port_id(lport, did, fp);
- fc_lport_enter_dns(lport);
- }
- }
- } else {
- FC_LPORT_DBG(lport, "FLOGI RJT or bad response\n");
+ if (fh->fh_r_ctl != FC_RCTL_ELS_REP || did == 0 ||
+ fc_frame_payload_op(fp) != ELS_LS_ACC) {
+ FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n");
+ fc_lport_error(lport, fp);
+ goto out;
+ }
+
+ flp = fc_frame_payload_get(fp, sizeof(*flp));
+ if (!flp) {
+ FC_LPORT_DBG(lport, "FLOGI bad response\n");
+ fc_lport_error(lport, fp);
+ goto out;
+ }
+
+ mfs = ntohs(flp->fl_csp.sp_bb_data) &
+ FC_SP_BB_DATA_MASK;
+
+ if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) {
+ FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, "
+ "lport->mfs:%u\n", mfs, lport->mfs);
fc_lport_error(lport, fp);
+ goto out;
+ }
+
+ if (mfs <= lport->mfs) {
+ lport->mfs = mfs;
+ fc_host_maxframe_size(lport->host) = mfs;
+ }
+
+ csp_flags = ntohs(flp->fl_csp.sp_features);
+ r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
+ e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
+ if (csp_flags & FC_SP_FT_EDTR)
+ e_d_tov /= 1000000;
+
+ lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC);
+
+ if ((csp_flags & FC_SP_FT_FPORT) == 0) {
+ if (e_d_tov > lport->e_d_tov)
+ lport->e_d_tov = e_d_tov;
+ lport->r_a_tov = 2 * lport->e_d_tov;
+ fc_lport_set_port_id(lport, did, fp);
+ printk(KERN_INFO "host%d: libfc: "
+ "Port (%6.6x) entered "
+ "point-to-point mode\n",
+ lport->host->host_no, did);
+ fc_lport_ptp_setup(lport, fc_frame_sid(fp),
+ get_unaligned_be64(
+ &flp->fl_wwpn),
+ get_unaligned_be64(
+ &flp->fl_wwnn));
+ } else {
+ if (e_d_tov > lport->e_d_tov)
+ lport->e_d_tov = e_d_tov;
+ if (r_a_tov > lport->r_a_tov)
+ lport->r_a_tov = r_a_tov;
+ fc_host_fabric_name(lport->host) =
+ get_unaligned_be64(&flp->fl_wwnn);
+ fc_lport_set_port_id(lport, did, fp);
+ fc_lport_enter_dns(lport);
}
out:
@@ -1551,16 +1835,15 @@ err:
EXPORT_SYMBOL(fc_lport_flogi_resp);
/**
- * fc_rport_enter_flogi() - Send a FLOGI request to the fabric manager
+ * fc_lport_enter_flogi() - Send a FLOGI request to the fabric manager
* @lport: Fibre Channel local port to be logged in to the fabric
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
-void fc_lport_enter_flogi(struct fc_lport *lport)
+static void fc_lport_enter_flogi(struct fc_lport *lport)
{
struct fc_frame *fp;
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_LPORT_DBG(lport, "Entered FLOGI state from %s state\n",
fc_lport_state(lport));
@@ -1609,11 +1892,12 @@ EXPORT_SYMBOL(fc_lport_config);
*/
int fc_lport_init(struct fc_lport *lport)
{
- if (!lport->tt.lport_recv)
- lport->tt.lport_recv = fc_lport_recv_req;
+ struct fc_host_attrs *fc_host;
- if (!lport->tt.lport_reset)
- lport->tt.lport_reset = fc_lport_reset;
+ fc_host = shost_to_fc_host(lport->host);
+
+ /* Set FDMI version to FDMI-2 specification*/
+ fc_host->fdmi_version = FDMI_V2;
fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
fc_host_node_name(lport->host) = lport->wwnn;
@@ -1623,6 +1907,7 @@ int fc_lport_init(struct fc_lport *lport)
sizeof(fc_host_supported_fc4s(lport->host)));
fc_host_supported_fc4s(lport->host)[2] = 1;
fc_host_supported_fc4s(lport->host)[7] = 1;
+ fc_host_num_discovered_ports(lport->host) = 4;
/* This value is also unchanging */
memset(fc_host_active_fc4s(lport->host), 0,
@@ -1635,8 +1920,27 @@ int fc_lport_init(struct fc_lport *lport)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+ if (lport->link_supported_speeds & FC_PORTSPEED_40GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_40GBIT;
+ if (lport->link_supported_speeds & FC_PORTSPEED_100GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_100GBIT;
+ if (lport->link_supported_speeds & FC_PORTSPEED_25GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_25GBIT;
+ if (lport->link_supported_speeds & FC_PORTSPEED_50GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_50GBIT;
+ if (lport->link_supported_speeds & FC_PORTSPEED_100GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_100GBIT;
+
fc_fc4_add_lport(lport);
+ fc_host_num_discovered_ports(lport->host) = DISCOVERED_PORTS;
+ fc_host_port_state(lport->host) = FC_PORTSTATE_ONLINE;
+ fc_host_max_ct_payload(lport->host) = MAX_CT_PAYLOAD;
+ fc_host_num_ports(lport->host) = NUMBER_OF_PORTS;
+ fc_host_bootbios_state(lport->host) = 0X00000000;
+ snprintf(fc_host_bootbios_version(lport->host),
+ FC_SYMBOLIC_NAME_SIZE, "%s", "Unknown");
+
return 0;
}
EXPORT_SYMBOL(fc_lport_init);
@@ -1651,18 +1955,19 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
void *info_arg)
{
struct fc_bsg_info *info = info_arg;
- struct fc_bsg_job *job = info->job;
+ struct bsg_job *job = info->job;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct fc_lport *lport = info->lport;
struct fc_frame_header *fh;
size_t len;
void *buf;
if (IS_ERR(fp)) {
- job->reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ?
+ bsg_reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ?
-ECONNABORTED : -ETIMEDOUT;
job->reply_len = sizeof(uint32_t);
- job->state_flags |= FC_RQST_STATE_DONE;
- job->job_done(job);
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
kfree(info);
return;
}
@@ -1679,25 +1984,25 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
(unsigned short)fc_frame_payload_op(fp);
/* Save the reply status of the job */
- job->reply->reply_data.ctels_reply.status =
+ bsg_reply->reply_data.ctels_reply.status =
(cmd == info->rsp_code) ?
FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT;
}
- job->reply->reply_payload_rcv_len +=
+ bsg_reply->reply_payload_rcv_len +=
fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents,
- &info->offset, KM_BIO_SRC_IRQ, NULL);
+ &info->offset, NULL);
if (fr_eof(fp) == FC_EOF_T &&
(ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
(FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
- if (job->reply->reply_payload_rcv_len >
+ if (bsg_reply->reply_payload_rcv_len >
job->reply_payload.payload_len)
- job->reply->reply_payload_rcv_len =
+ bsg_reply->reply_payload_rcv_len =
job->reply_payload.payload_len;
- job->reply->result = 0;
- job->state_flags |= FC_RQST_STATE_DONE;
- job->job_done(job);
+ bsg_reply->result = 0;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
kfree(info);
}
fc_frame_free(fp);
@@ -1709,11 +2014,9 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
* @job: The BSG Passthrough job
* @lport: The local port sending the request
* @did: The destination port id
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
+ * @tov: The timeout period (in ms)
*/
-static int fc_lport_els_request(struct fc_bsg_job *job,
+static int fc_lport_els_request(struct bsg_job *job,
struct fc_lport *lport,
u32 did, u32 tov)
{
@@ -1723,6 +2026,8 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
char *pp;
int len;
+ lockdep_assert_held(&lport->lp_mutex);
+
fp = fc_frame_alloc(lport, job->request_payload.payload_len);
if (!fp)
return -ENOMEM;
@@ -1756,8 +2061,8 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
info->nents = job->reply_payload.sg_cnt;
info->sg = job->reply_payload.sg_list;
- if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
- NULL, info, tov)) {
+ if (!fc_exch_seq_send(lport, fp, fc_lport_bsg_resp,
+ NULL, info, tov)) {
kfree(info);
return -ECOMM;
}
@@ -1770,11 +2075,8 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
* @lport: The local port sending the request
* @did: The destination FC-ID
* @tov: The timeout period to wait for the response
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
*/
-static int fc_lport_ct_request(struct fc_bsg_job *job,
+static int fc_lport_ct_request(struct bsg_job *job,
struct fc_lport *lport, u32 did, u32 tov)
{
struct fc_bsg_info *info;
@@ -1783,6 +2085,8 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,
struct fc_ct_req *ct;
size_t len;
+ lockdep_assert_held(&lport->lp_mutex);
+
fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
job->request_payload.payload_len);
if (!fp)
@@ -1817,8 +2121,8 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,
info->nents = job->reply_payload.sg_cnt;
info->sg = job->reply_payload.sg_list;
- if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
- NULL, info, tov)) {
+ if (!fc_exch_seq_send(lport, fp, fc_lport_bsg_resp,
+ NULL, info, tov)) {
kfree(info);
return -ECOMM;
}
@@ -1830,25 +2134,24 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,
* FC Passthrough requests
* @job: The BSG passthrough job
*/
-int fc_lport_bsg_request(struct fc_bsg_job *job)
+int fc_lport_bsg_request(struct bsg_job *job)
{
- struct request *rsp = job->req->next_rq;
- struct Scsi_Host *shost = job->shost;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
struct fc_lport *lport = shost_priv(shost);
struct fc_rport *rport;
struct fc_rport_priv *rdata;
int rc = -EINVAL;
- u32 did;
+ u32 did, tov;
- job->reply->reply_payload_rcv_len = 0;
- if (rsp)
- rsp->resid_len = job->reply_payload.payload_len;
+ bsg_reply->reply_payload_rcv_len = 0;
mutex_lock(&lport->lp_mutex);
- switch (job->request->msgcode) {
+ switch (bsg_request->msgcode) {
case FC_BSG_RPT_ELS:
- rport = job->rport;
+ rport = fc_bsg_to_rport(job);
if (!rport)
break;
@@ -1858,7 +2161,7 @@ int fc_lport_bsg_request(struct fc_bsg_job *job)
break;
case FC_BSG_RPT_CT:
- rport = job->rport;
+ rport = fc_bsg_to_rport(job);
if (!rport)
break;
@@ -1868,20 +2171,25 @@ int fc_lport_bsg_request(struct fc_bsg_job *job)
break;
case FC_BSG_HST_CT:
- did = ntoh24(job->request->rqst_data.h_ct.port_id);
- if (did == FC_FID_DIR_SERV)
+ did = ntoh24(bsg_request->rqst_data.h_ct.port_id);
+ if (did == FC_FID_DIR_SERV) {
rdata = lport->dns_rdata;
- else
- rdata = lport->tt.rport_lookup(lport, did);
-
- if (!rdata)
- break;
+ if (!rdata)
+ break;
+ tov = rdata->e_d_tov;
+ } else {
+ rdata = fc_rport_lookup(lport, did);
+ if (!rdata)
+ break;
+ tov = rdata->e_d_tov;
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
- rc = fc_lport_ct_request(job, lport, did, rdata->e_d_tov);
+ rc = fc_lport_ct_request(job, lport, did, tov);
break;
case FC_BSG_HST_ELS_NOLOGIN:
- did = ntoh24(job->request->rqst_data.h_els.port_id);
+ did = ntoh24(bsg_request->rqst_data.h_els.port_id);
rc = fc_lport_els_request(job, lport, did, lport->e_d_tov);
break;
}
diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
index f33b897e4784..c045898b84df 100644
--- a/drivers/scsi/libfc/fc_npiv.c
+++ b/drivers/scsi/libfc/fc_npiv.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2009 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -22,9 +10,10 @@
*/
#include <scsi/libfc.h>
+#include <linux/export.h>
/**
- * fc_vport_create() - Create a new NPIV vport instance
+ * libfc_vport_create() - Create a new NPIV vport instance
* @vport: fc_vport structure from scsi_transport_fc
* @privsize: driver private data size to allocate along with the Scsi_Host
*/
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 49e1ccca09d5..c25979d96808 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Maintained at www.Open-FCoE.org
*/
@@ -44,6 +32,19 @@
* path this potential over-use of the mutex is acceptable.
*/
+/*
+ * RPORT REFERENCE COUNTING
+ *
+ * A rport reference should be taken when:
+ * - an rport is allocated
+ * - a workqueue item is scheduled
+ * - an ELS request is send
+ * The reference should be dropped when:
+ * - the workqueue function has finished
+ * - the ELS response is handled
+ * - an rport is removed
+ */
+
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
@@ -51,11 +52,14 @@
#include <linux/rcupdate.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
-#include <asm/unaligned.h>
+#include <linux/export.h>
+#include <linux/rculist.h>
+
+#include <linux/unaligned.h>
#include <scsi/libfc.h>
-#include <scsi/fc_encode.h>
+#include "fc_encode.h"
#include "fc_libfc.h"
static struct workqueue_struct *rport_event_queue;
@@ -73,8 +77,8 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_frame *);
static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_frame *);
static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_frame *);
static void fc_rport_timeout(struct work_struct *);
-static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *);
-static void fc_rport_error_retry(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_error(struct fc_rport_priv *, int);
+static void fc_rport_error_retry(struct fc_rport_priv *, int);
static void fc_rport_work(struct work_struct *);
static const char *fc_rport_state_names[] = {
@@ -94,38 +98,49 @@ static const char *fc_rport_state_names[] = {
* @lport: The local port to lookup the remote port on
* @port_id: The remote port ID to look up
*
- * The caller must hold either disc_mutex or rcu_read_lock().
+ * The reference count of the fc_rport_priv structure is
+ * increased by one.
*/
-static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
- u32 port_id)
+struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
+ u32 port_id)
{
- struct fc_rport_priv *rdata;
-
- list_for_each_entry_rcu(rdata, &lport->disc.rports, peers)
- if (rdata->ids.port_id == port_id)
- return rdata;
- return NULL;
+ struct fc_rport_priv *rdata = NULL, *tmp_rdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tmp_rdata, &lport->disc.rports, peers)
+ if (tmp_rdata->ids.port_id == port_id &&
+ kref_get_unless_zero(&tmp_rdata->kref)) {
+ rdata = tmp_rdata;
+ break;
+ }
+ rcu_read_unlock();
+ return rdata;
}
+EXPORT_SYMBOL(fc_rport_lookup);
/**
* fc_rport_create() - Create a new remote port
* @lport: The local port this remote port will be associated with
- * @ids: The identifiers for the new remote port
+ * @port_id: The identifiers for the new remote port
*
* The remote port will start in the INIT state.
- *
- * Locking note: must be called with the disc_mutex held.
*/
-static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
- u32 port_id)
+struct fc_rport_priv *fc_rport_create(struct fc_lport *lport, u32 port_id)
{
struct fc_rport_priv *rdata;
+ size_t rport_priv_size = sizeof(*rdata);
+
+ lockdep_assert_held(&lport->disc.disc_mutex);
- rdata = lport->tt.rport_lookup(lport, port_id);
- if (rdata)
+ rdata = fc_rport_lookup(lport, port_id);
+ if (rdata) {
+ kref_put(&rdata->kref, fc_rport_destroy);
return rdata;
+ }
- rdata = kzalloc(sizeof(*rdata) + lport->rport_priv_size, GFP_KERNEL);
+ if (lport->rport_priv_size > 0)
+ rport_priv_size = lport->rport_priv_size;
+ rdata = kzalloc(rport_priv_size, GFP_KERNEL);
if (!rdata)
return NULL;
@@ -151,30 +166,20 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
}
return rdata;
}
-
-/**
- * fc_rport_free_rcu() - Free a remote port
- * @rcu: The rcu_head structure inside the remote port
- */
-static void fc_rport_free_rcu(struct rcu_head *rcu)
-{
- struct fc_rport_priv *rdata;
-
- rdata = container_of(rcu, struct fc_rport_priv, rcu);
- kfree(rdata);
-}
+EXPORT_SYMBOL(fc_rport_create);
/**
* fc_rport_destroy() - Free a remote port after last reference is released
* @kref: The remote port's kref
*/
-static void fc_rport_destroy(struct kref *kref)
+void fc_rport_destroy(struct kref *kref)
{
struct fc_rport_priv *rdata;
rdata = container_of(kref, struct fc_rport_priv, kref);
- call_rcu(&rdata->rcu, fc_rport_free_rcu);
+ kfree_rcu(rdata, rcu);
}
+EXPORT_SYMBOL(fc_rport_destroy);
/**
* fc_rport_state() - Return a string identifying the remote port's state
@@ -233,12 +238,12 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
* fc_rport_state_enter() - Change the state of a remote port
* @rdata: The remote port whose state should change
* @new: The new state
- *
- * Locking Note: Called with the rport lock held
*/
static void fc_rport_state_enter(struct fc_rport_priv *rdata,
enum fc_rport_state new)
{
+ lockdep_assert_held(&rdata->rp_mutex);
+
if (rdata->rp_state != new)
rdata->retries = 0;
rdata->rp_state = new;
@@ -247,6 +252,8 @@ static void fc_rport_state_enter(struct fc_rport_priv *rdata,
/**
* fc_rport_work() - Handler for remote port events in the rport_event_queue
* @work: Handle to the remote port being dequeued
+ *
+ * Reference counting: drops kref on return
*/
static void fc_rport_work(struct work_struct *work)
{
@@ -277,12 +284,14 @@ static void fc_rport_work(struct work_struct *work)
kref_get(&rdata->kref);
mutex_unlock(&rdata->rp_mutex);
- if (!rport)
+ if (!rport) {
+ FC_RPORT_DBG(rdata, "No rport!\n");
rport = fc_remote_port_add(lport->host, 0, &ids);
+ }
if (!rport) {
FC_RPORT_DBG(rdata, "Failed to add the rport\n");
- lport->tt.rport_logoff(rdata);
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ fc_rport_logoff(rdata);
+ kref_put(&rdata->kref, fc_rport_destroy);
return;
}
mutex_lock(&rdata->rp_mutex);
@@ -308,7 +317,7 @@ static void fc_rport_work(struct work_struct *work)
FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
rdata->lld_event_callback(lport, rdata, event);
}
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ kref_put(&rdata->kref, fc_rport_destroy);
break;
case RPORT_EV_FAILED:
@@ -334,7 +343,8 @@ static void fc_rport_work(struct work_struct *work)
FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
rdata->lld_event_callback(lport, rdata, event);
}
- cancel_delayed_work_sync(&rdata->retry_work);
+ if (cancel_delayed_work_sync(&rdata->retry_work))
+ kref_put(&rdata->kref, fc_rport_destroy);
/*
* Reset any outstanding exchanges before freeing rport.
@@ -351,13 +361,12 @@ static void fc_rport_work(struct work_struct *work)
fc_remote_port_delete(rport);
}
- mutex_lock(&lport->disc.disc_mutex);
mutex_lock(&rdata->rp_mutex);
if (rdata->rp_state == RPORT_ST_DELETE) {
if (port_id == FC_FID_DIR_SERV) {
rdata->event = RPORT_EV_NONE;
mutex_unlock(&rdata->rp_mutex);
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ kref_put(&rdata->kref, fc_rport_destroy);
} else if ((rdata->flags & FC_RP_STARTED) &&
rdata->major_retries <
lport->max_rport_retry_count) {
@@ -367,33 +376,45 @@ static void fc_rport_work(struct work_struct *work)
fc_rport_enter_flogi(rdata);
mutex_unlock(&rdata->rp_mutex);
} else {
+ mutex_unlock(&rdata->rp_mutex);
FC_RPORT_DBG(rdata, "work delete\n");
+ mutex_lock(&lport->disc.disc_mutex);
list_del_rcu(&rdata->peers);
- mutex_unlock(&rdata->rp_mutex);
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ mutex_unlock(&lport->disc.disc_mutex);
+ kref_put(&rdata->kref, fc_rport_destroy);
}
} else {
/*
* Re-open for events. Reissue READY event if ready.
*/
rdata->event = RPORT_EV_NONE;
- if (rdata->rp_state == RPORT_ST_READY)
+ if (rdata->rp_state == RPORT_ST_READY) {
+ FC_RPORT_DBG(rdata, "work reopen\n");
fc_rport_enter_ready(rdata);
+ }
mutex_unlock(&rdata->rp_mutex);
}
- mutex_unlock(&lport->disc.disc_mutex);
break;
default:
mutex_unlock(&rdata->rp_mutex);
break;
}
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
* fc_rport_login() - Start the remote port login state machine
* @rdata: The remote port to be logged in to
*
+ * Initiates the RP state machine. It is called from the LP module.
+ * This function will issue the following commands to the N_Port
+ * identified by the FC ID provided.
+ *
+ * - PLOGI
+ * - PRLI
+ * - RTV
+ *
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
@@ -406,6 +427,12 @@ int fc_rport_login(struct fc_rport_priv *rdata)
{
mutex_lock(&rdata->rp_mutex);
+ if (rdata->flags & FC_RP_STARTED) {
+ FC_RPORT_DBG(rdata, "port already started\n");
+ mutex_unlock(&rdata->rp_mutex);
+ return 0;
+ }
+
rdata->flags |= FC_RP_STARTED;
switch (rdata->rp_state) {
case RPORT_ST_READY:
@@ -415,33 +442,40 @@ int fc_rport_login(struct fc_rport_priv *rdata)
case RPORT_ST_DELETE:
FC_RPORT_DBG(rdata, "Restart deleted port\n");
break;
- default:
+ case RPORT_ST_INIT:
FC_RPORT_DBG(rdata, "Login to port\n");
fc_rport_enter_flogi(rdata);
break;
+ default:
+ FC_RPORT_DBG(rdata, "Login in progress, state %s\n",
+ fc_rport_state(rdata));
+ break;
}
mutex_unlock(&rdata->rp_mutex);
return 0;
}
+EXPORT_SYMBOL(fc_rport_login);
/**
* fc_rport_enter_delete() - Schedule a remote port to be deleted
* @rdata: The remote port to be deleted
* @event: The event to report as the reason for deletion
*
- * Locking Note: Called with the rport lock held.
- *
* Allow state change into DELETE only once.
*
* Call queue_work only if there's no event already pending.
* Set the new event so that the old pending event will not occur.
* Since we have the mutex, even if fc_rport_work() is already started,
* it'll see the new event.
+ *
+ * Reference counting: does not modify kref
*/
static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
enum fc_rport_event event)
{
+ lockdep_assert_held(&rdata->rp_mutex);
+
if (rdata->rp_state == RPORT_ST_DELETE)
return;
@@ -449,8 +483,12 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
fc_rport_state_enter(rdata, RPORT_ST_DELETE);
- if (rdata->event == RPORT_EV_NONE)
- queue_work(rport_event_queue, &rdata->event_work);
+ if (rdata->event == RPORT_EV_NONE) {
+ kref_get(&rdata->kref);
+ if (!queue_work(rport_event_queue, &rdata->event_work))
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
+
rdata->event = event;
}
@@ -464,6 +502,9 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
*/
int fc_rport_logoff(struct fc_rport_priv *rdata)
{
+ struct fc_lport *lport = rdata->local_port;
+ u32 port_id = rdata->ids.port_id;
+
mutex_lock(&rdata->rp_mutex);
FC_RPORT_DBG(rdata, "Remove port\n");
@@ -473,6 +514,15 @@ int fc_rport_logoff(struct fc_rport_priv *rdata)
FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
goto out;
}
+ /*
+ * FC-LS states:
+ * To explicitly Logout, the initiating Nx_Port shall terminate
+ * other open Sequences that it initiated with the destination
+ * Nx_Port prior to performing Logout.
+ */
+ lport->tt.exch_mgr_reset(lport, 0, port_id);
+ lport->tt.exch_mgr_reset(lport, port_id, 0);
+
fc_rport_enter_logo(rdata);
/*
@@ -484,22 +534,27 @@ out:
mutex_unlock(&rdata->rp_mutex);
return 0;
}
+EXPORT_SYMBOL(fc_rport_logoff);
/**
* fc_rport_enter_ready() - Transition to the RPORT_ST_READY state
* @rdata: The remote port that is ready
*
- * Locking Note: The rport lock is expected to be held before calling
- * this routine.
+ * Reference counting: schedules workqueue, does not modify kref
*/
static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
{
+ lockdep_assert_held(&rdata->rp_mutex);
+
fc_rport_state_enter(rdata, RPORT_ST_READY);
FC_RPORT_DBG(rdata, "Port is Ready\n");
- if (rdata->event == RPORT_EV_NONE)
- queue_work(rport_event_queue, &rdata->event_work);
+ kref_get(&rdata->kref);
+ if (rdata->event == RPORT_EV_NONE &&
+ !queue_work(rport_event_queue, &rdata->event_work))
+ kref_put(&rdata->kref, fc_rport_destroy);
+
rdata->event = RPORT_EV_READY;
}
@@ -510,6 +565,8 @@ static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
+ *
+ * Reference counting: Drops kref on return.
*/
static void fc_rport_timeout(struct work_struct *work)
{
@@ -517,6 +574,7 @@ static void fc_rport_timeout(struct work_struct *work)
container_of(work, struct fc_rport_priv, retry_work.work);
mutex_lock(&rdata->rp_mutex);
+ FC_RPORT_DBG(rdata, "Port timeout, state %s\n", fc_rport_state(rdata));
switch (rdata->rp_state) {
case RPORT_ST_FLOGI:
@@ -542,32 +600,43 @@ static void fc_rport_timeout(struct work_struct *work)
}
mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
* fc_rport_error() - Error handler, called once retries have been exhausted
* @rdata: The remote port the error is happened on
- * @fp: The error code encapsulated in a frame pointer
+ * @err: The error code
*
- * Locking Note: The rport lock is expected to be held before
- * calling this routine
+ * Reference counting: does not modify kref
*/
-static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
+static void fc_rport_error(struct fc_rport_priv *rdata, int err)
{
- FC_RPORT_DBG(rdata, "Error %ld in state %s, retries %d\n",
- IS_ERR(fp) ? -PTR_ERR(fp) : 0,
- fc_rport_state(rdata), rdata->retries);
+ struct fc_lport *lport = rdata->local_port;
+
+ lockdep_assert_held(&rdata->rp_mutex);
+
+ FC_RPORT_DBG(rdata, "Error %d in state %s, retries %d\n",
+ -err, fc_rport_state(rdata), rdata->retries);
switch (rdata->rp_state) {
case RPORT_ST_FLOGI:
- case RPORT_ST_PLOGI:
rdata->flags &= ~FC_RP_STARTED;
fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
break;
+ case RPORT_ST_PLOGI:
+ if (lport->point_to_multipoint) {
+ rdata->flags &= ~FC_RP_STARTED;
+ fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
+ } else
+ fc_rport_enter_logo(rdata);
+ break;
case RPORT_ST_RTV:
fc_rport_enter_ready(rdata);
break;
case RPORT_ST_PRLI:
+ fc_rport_enter_plogi(rdata);
+ break;
case RPORT_ST_ADISC:
fc_rport_enter_logo(rdata);
break;
@@ -582,36 +651,38 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
/**
* fc_rport_error_retry() - Handler for remote port state retries
* @rdata: The remote port whose state is to be retried
- * @fp: The error code encapsulated in a frame pointer
+ * @err: The error code
*
* If the error was an exchange timeout retry immediately,
* otherwise wait for E_D_TOV.
*
- * Locking Note: The rport lock is expected to be held before
- * calling this routine
+ * Reference counting: increments kref when scheduling retry_work
*/
-static void fc_rport_error_retry(struct fc_rport_priv *rdata,
- struct fc_frame *fp)
+static void fc_rport_error_retry(struct fc_rport_priv *rdata, int err)
{
- unsigned long delay = FC_DEF_E_D_TOV;
+ unsigned long delay = msecs_to_jiffies(rdata->e_d_tov);
+
+ lockdep_assert_held(&rdata->rp_mutex);
/* make sure this isn't an FC_EX_CLOSED error, never retry those */
- if (PTR_ERR(fp) == -FC_EX_CLOSED)
+ if (err == -FC_EX_CLOSED)
goto out;
if (rdata->retries < rdata->local_port->max_rport_retry_count) {
- FC_RPORT_DBG(rdata, "Error %ld in state %s, retrying\n",
- PTR_ERR(fp), fc_rport_state(rdata));
+ FC_RPORT_DBG(rdata, "Error %d in state %s, retrying\n",
+ err, fc_rport_state(rdata));
rdata->retries++;
/* no additional delay on exchange timeouts */
- if (PTR_ERR(fp) == -FC_EX_TIMEOUT)
+ if (err == -FC_EX_TIMEOUT)
delay = 0;
- schedule_delayed_work(&rdata->retry_work, delay);
+ kref_get(&rdata->kref);
+ if (!schedule_delayed_work(&rdata->retry_work, delay))
+ kref_put(&rdata->kref, fc_rport_destroy);
return;
}
out:
- fc_rport_error(rdata, fp);
+ fc_rport_error(rdata, err);
}
/**
@@ -664,15 +735,18 @@ static int fc_rport_login_complete(struct fc_rport_priv *rdata,
* @fp: The FLOGI response frame
* @rp_arg: The remote port that received the FLOGI response
*/
-void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
{
struct fc_rport_priv *rdata = rp_arg;
struct fc_lport *lport = rdata->local_port;
struct fc_els_flogi *flogi;
unsigned int r_a_tov;
+ u8 opcode;
+ int err = 0;
- FC_RPORT_DBG(rdata, "Received a FLOGI %s\n", fc_els_resp_type(fp));
+ FC_RPORT_DBG(rdata, "Received a FLOGI %s\n",
+ IS_ERR(fp) ? "error" : fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED))
goto put;
@@ -688,18 +762,34 @@ void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
}
if (IS_ERR(fp)) {
- fc_rport_error(rdata, fp);
+ fc_rport_error(rdata, PTR_ERR(fp));
goto err;
}
-
- if (fc_frame_payload_op(fp) != ELS_LS_ACC)
+ opcode = fc_frame_payload_op(fp);
+ if (opcode == ELS_LS_RJT) {
+ struct fc_els_ls_rjt *rjt;
+
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ FC_RPORT_DBG(rdata, "FLOGI ELS rejected, reason %x expl %x\n",
+ rjt->er_reason, rjt->er_explan);
+ err = -FC_EX_ELS_RJT;
+ goto bad;
+ } else if (opcode != ELS_LS_ACC) {
+ FC_RPORT_DBG(rdata, "FLOGI ELS invalid opcode %x\n", opcode);
+ err = -FC_EX_ELS_RJT;
goto bad;
- if (fc_rport_login_complete(rdata, fp))
+ }
+ if (fc_rport_login_complete(rdata, fp)) {
+ FC_RPORT_DBG(rdata, "FLOGI failed, no login\n");
+ err = -FC_EX_INV_LOGIN;
goto bad;
+ }
flogi = fc_frame_payload_get(fp, sizeof(*flogi));
- if (!flogi)
+ if (!flogi) {
+ err = -FC_EX_ALLOC_ERR;
goto bad;
+ }
r_a_tov = ntohl(flogi->fl_csp.sp_r_a_tov);
if (r_a_tov > rdata->r_a_tov)
rdata->r_a_tov = r_a_tov;
@@ -713,11 +803,11 @@ out:
err:
mutex_unlock(&rdata->rp_mutex);
put:
- kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+ kref_put(&rdata->kref, fc_rport_destroy);
return;
bad:
FC_RPORT_DBG(rdata, "Bad FLOGI response\n");
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, err);
goto out;
}
@@ -725,14 +815,15 @@ bad:
* fc_rport_enter_flogi() - Send a FLOGI request to the remote port for p-mp
* @rdata: The remote port to send a FLOGI to
*
- * Locking Note: The rport lock is expected to be held before calling
- * this routine.
+ * Reference counting: increments kref when sending ELS
*/
static void fc_rport_enter_flogi(struct fc_rport_priv *rdata)
{
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
+ lockdep_assert_held(&rdata->rp_mutex);
+
if (!lport->point_to_multipoint)
return fc_rport_enter_plogi(rdata);
@@ -743,25 +834,27 @@ static void fc_rport_enter_flogi(struct fc_rport_priv *rdata)
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
if (!fp)
- return fc_rport_error_retry(rdata, fp);
+ return fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
+ kref_get(&rdata->kref);
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_FLOGI,
fc_rport_flogi_resp, rdata,
- 2 * lport->r_a_tov))
- fc_rport_error_retry(rdata, NULL);
- else
- kref_get(&rdata->kref);
+ 2 * lport->r_a_tov)) {
+ fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
}
/**
* fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode
* @lport: The local port that received the PLOGI request
* @rx_fp: The PLOGI request frame
+ *
+ * Reference counting: drops kref on return
*/
static void fc_rport_recv_flogi_req(struct fc_lport *lport,
struct fc_frame *rx_fp)
{
- struct fc_disc *disc;
struct fc_els_flogi *flp;
struct fc_rport_priv *rdata;
struct fc_frame *fp = rx_fp;
@@ -772,9 +865,6 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n");
- disc = &lport->disc;
- mutex_lock(&disc->disc_mutex);
-
if (!lport->point_to_multipoint) {
rjt_data.reason = ELS_RJT_UNSUP;
rjt_data.explan = ELS_EXPL_NONE;
@@ -788,7 +878,7 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
goto reject;
}
- rdata = lport->tt.rport_lookup(lport, sid);
+ rdata = fc_rport_lookup(lport, sid);
if (!rdata) {
rjt_data.reason = ELS_RJT_FIP;
rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
@@ -801,11 +891,24 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
switch (rdata->rp_state) {
case RPORT_ST_INIT:
+ /*
+ * If received the FLOGI request on RPORT which is INIT state
+ * (means not transition to FLOGI either fc_rport timeout
+ * function didn;t trigger or this end hasn;t received
+ * beacon yet from other end. In that case only, allow RPORT
+ * state machine to continue, otherwise fall through which
+ * causes the code to send reject response.
+ * NOTE; Not checking for FIP->state such as VNMP_UP or
+ * VNMP_CLAIM because if FIP state is not one of those,
+ * RPORT wouldn;t have created and 'rport_lookup' would have
+ * failed anyway in that case.
+ */
+ break;
case RPORT_ST_DELETE:
mutex_unlock(&rdata->rp_mutex);
rjt_data.reason = ELS_RJT_FIP;
rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
- goto reject;
+ goto reject_put;
case RPORT_ST_FLOGI:
case RPORT_ST_PLOGI_WAIT:
case RPORT_ST_PLOGI:
@@ -822,13 +925,13 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
mutex_unlock(&rdata->rp_mutex);
rjt_data.reason = ELS_RJT_BUSY;
rjt_data.explan = ELS_EXPL_NONE;
- goto reject;
+ goto reject_put;
}
if (fc_rport_login_complete(rdata, fp)) {
mutex_unlock(&rdata->rp_mutex);
rjt_data.reason = ELS_RJT_LOGIC;
rjt_data.explan = ELS_EXPL_NONE;
- goto reject;
+ goto reject_put;
}
fp = fc_frame_alloc(lport, sizeof(*flp));
@@ -842,19 +945,27 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
lport->tt.frame_send(lport, fp);
- if (rdata->ids.port_name < lport->wwpn)
- fc_rport_enter_plogi(rdata);
- else
- fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+ /*
+ * Do not proceed with the state machine if our
+ * FLOGI has crossed with an FLOGI from the
+ * remote port; wait for the FLOGI response instead.
+ */
+ if (rdata->rp_state != RPORT_ST_FLOGI) {
+ if (rdata->ids.port_name < lport->wwpn)
+ fc_rport_enter_plogi(rdata);
+ else
+ fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+ }
out:
mutex_unlock(&rdata->rp_mutex);
- mutex_unlock(&disc->disc_mutex);
+ kref_put(&rdata->kref, fc_rport_destroy);
fc_frame_free(rx_fp);
return;
+reject_put:
+ kref_put(&rdata->kref, fc_rport_destroy);
reject:
- mutex_unlock(&disc->disc_mutex);
- lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
fc_frame_free(rx_fp);
}
@@ -878,10 +989,13 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
u16 cssp_seq;
u8 op;
- mutex_lock(&rdata->rp_mutex);
-
FC_RPORT_DBG(rdata, "Received a PLOGI %s\n", fc_els_resp_type(fp));
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ goto put;
+
+ mutex_lock(&rdata->rp_mutex);
+
if (rdata->rp_state != RPORT_ST_PLOGI) {
FC_RPORT_DBG(rdata, "Received a PLOGI response, but in state "
"%s\n", fc_rport_state(rdata));
@@ -891,7 +1005,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, PTR_ERR(fp));
goto err;
}
@@ -913,28 +1027,58 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
rdata->max_seq = csp_seq;
rdata->maxframe_size = fc_plogi_get_maxframe(plp, lport->mfs);
fc_rport_enter_prli(rdata);
- } else
- fc_rport_error_retry(rdata, fp);
-
+ } else {
+ struct fc_els_ls_rjt *rjt;
+
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ if (!rjt)
+ FC_RPORT_DBG(rdata, "PLOGI bad response\n");
+ else
+ FC_RPORT_DBG(rdata, "PLOGI ELS rejected, reason %x expl %x\n",
+ rjt->er_reason, rjt->er_explan);
+ fc_rport_error_retry(rdata, -FC_EX_ELS_RJT);
+ }
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+put:
+ kref_put(&rdata->kref, fc_rport_destroy);
+}
+
+static bool
+fc_rport_compatible_roles(struct fc_lport *lport, struct fc_rport_priv *rdata)
+{
+ if (rdata->ids.roles == FC_PORT_ROLE_UNKNOWN)
+ return true;
+ if ((rdata->ids.roles & FC_PORT_ROLE_FCP_TARGET) &&
+ (lport->service_params & FCP_SPPF_INIT_FCN))
+ return true;
+ if ((rdata->ids.roles & FC_PORT_ROLE_FCP_INITIATOR) &&
+ (lport->service_params & FCP_SPPF_TARG_FCN))
+ return true;
+ return false;
}
/**
* fc_rport_enter_plogi() - Send Port Login (PLOGI) request
* @rdata: The remote port to send a PLOGI to
*
- * Locking Note: The rport lock is expected to be held before calling
- * this routine.
+ * Reference counting: increments kref when sending ELS
*/
static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
{
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
+ lockdep_assert_held(&rdata->rp_mutex);
+
+ if (!fc_rport_compatible_roles(lport, rdata)) {
+ FC_RPORT_DBG(rdata, "PLOGI suppressed for incompatible role\n");
+ fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+ return;
+ }
+
FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n",
fc_rport_state(rdata));
@@ -944,17 +1088,18 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
if (!fp) {
FC_RPORT_DBG(rdata, "%s frame alloc failed\n", __func__);
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
return;
}
rdata->e_d_tov = lport->e_d_tov;
+ kref_get(&rdata->kref);
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
fc_rport_plogi_resp, rdata,
- 2 * lport->r_a_tov))
- fc_rport_error_retry(rdata, NULL);
- else
- kref_get(&rdata->kref);
+ 2 * lport->r_a_tov)) {
+ fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
}
/**
@@ -976,16 +1121,20 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_els_spp spp;
} *pp;
struct fc_els_spp temp_spp;
+ struct fc_els_ls_rjt *rjt;
struct fc4_prov *prov;
u32 roles = FC_RPORT_ROLE_UNKNOWN;
u32 fcp_parm = 0;
u8 op;
- u8 resp_code = 0;
-
- mutex_lock(&rdata->rp_mutex);
+ enum fc_els_spp_resp resp_code;
FC_RPORT_DBG(rdata, "Received a PRLI %s\n", fc_els_resp_type(fp));
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ goto put;
+
+ mutex_lock(&rdata->rp_mutex);
+
if (rdata->rp_state != RPORT_ST_PRLI) {
FC_RPORT_DBG(rdata, "Received a PRLI response, but in state "
"%s\n", fc_rport_state(rdata));
@@ -995,7 +1144,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, PTR_ERR(fp));
goto err;
}
@@ -1005,22 +1154,27 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
- if (!pp)
+ if (!pp) {
+ fc_rport_error_retry(rdata, -FC_EX_SEQ_ERR);
goto out;
+ }
resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK);
- FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x\n",
- pp->spp.spp_flags);
+ FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x spp_type 0x%x\n",
+ pp->spp.spp_flags, pp->spp.spp_type);
+
rdata->spp_type = pp->spp.spp_type;
if (resp_code != FC_SPP_RESP_ACK) {
if (resp_code == FC_SPP_RESP_CONF)
- fc_rport_error(rdata, fp);
+ fc_rport_error(rdata, -FC_EX_SEQ_ERR);
else
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, -FC_EX_SEQ_ERR);
goto out;
}
- if (pp->prli.prli_spp_len < sizeof(pp->spp))
+ if (pp->prli.prli_spp_len < sizeof(pp->spp)) {
+ fc_rport_error_retry(rdata, -FC_EX_SEQ_ERR);
goto out;
+ }
fcp_parm = ntohl(pp->spp.spp_params);
if (fcp_parm & FCP_SPPF_RETRY)
@@ -1028,13 +1182,27 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
if (fcp_parm & FCP_SPPF_CONF_COMPL)
rdata->flags |= FC_RP_FLAGS_CONF_REQ;
- prov = fc_passive_prov[FC_TYPE_FCP];
- if (prov) {
- memset(&temp_spp, 0, sizeof(temp_spp));
- prov->prli(rdata, pp->prli.prli_spp_len,
- &pp->spp, &temp_spp);
+ /*
+ * Call prli provider if we should act as a target
+ */
+ if (rdata->spp_type < FC_FC4_PROV_SIZE) {
+ prov = fc_passive_prov[rdata->spp_type];
+ if (prov) {
+ memset(&temp_spp, 0, sizeof(temp_spp));
+ prov->prli(rdata, pp->prli.prli_spp_len,
+ &pp->spp, &temp_spp);
+ }
+ }
+ /*
+ * Check if the image pair could be established
+ */
+ if (rdata->spp_type != FC_TYPE_FCP ||
+ !(pp->spp.spp_flags & FC_SPP_EST_IMG_PAIR)) {
+ /*
+ * Nope; we can't use this port as a target.
+ */
+ fcp_parm &= ~FCP_SPPF_TARG_FCN;
}
-
rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
@@ -1045,23 +1213,34 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
fc_rport_enter_rtv(rdata);
} else {
- FC_RPORT_DBG(rdata, "Bad ELS response for PRLI command\n");
- fc_rport_error_retry(rdata, fp);
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ if (!rjt)
+ FC_RPORT_DBG(rdata, "PRLI bad response\n");
+ else {
+ FC_RPORT_DBG(rdata, "PRLI ELS rejected, reason %x expl %x\n",
+ rjt->er_reason, rjt->er_explan);
+ if (rjt->er_reason == ELS_RJT_UNAB &&
+ rjt->er_explan == ELS_EXPL_PLOGI_REQD) {
+ fc_rport_enter_plogi(rdata);
+ goto out;
+ }
+ }
+ fc_rport_error_retry(rdata, FC_EX_ELS_RJT);
}
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+put:
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
* fc_rport_enter_prli() - Send Process Login (PRLI) request
* @rdata: The remote port to send the PRLI request to
*
- * Locking Note: The rport lock is expected to be held before calling
- * this routine.
+ * Reference counting: increments kref when sending ELS
*/
static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
{
@@ -1073,6 +1252,8 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
struct fc_frame *fp;
struct fc4_prov *prov;
+ lockdep_assert_held(&rdata->rp_mutex);
+
/*
* If the rport is one of the well known addresses
* we skip PRLI and RTV and go straight to READY.
@@ -1082,6 +1263,15 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
return;
}
+ /*
+ * And if the local port does not support the initiator function
+ * there's no need to send a PRLI, either.
+ */
+ if (!(lport->service_params & FCP_SPPF_INIT_FCN)) {
+ fc_rport_enter_ready(rdata);
+ return;
+ }
+
FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n",
fc_rport_state(rdata));
@@ -1089,7 +1279,7 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
fp = fc_frame_alloc(lport, sizeof(*pp));
if (!fp) {
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
return;
}
@@ -1105,15 +1295,16 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
fc_host_port_id(lport->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- if (!lport->tt.exch_seq_send(lport, fp, fc_rport_prli_resp,
- NULL, rdata, 2 * lport->r_a_tov))
- fc_rport_error_retry(rdata, NULL);
- else
- kref_get(&rdata->kref);
+ kref_get(&rdata->kref);
+ if (!fc_exch_seq_send(lport, fp, fc_rport_prli_resp,
+ NULL, rdata, 2 * lport->r_a_tov)) {
+ fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
}
/**
- * fc_rport_els_rtv_resp() - Handler for Request Timeout Value (RTV) responses
+ * fc_rport_rtv_resp() - Handler for Request Timeout Value (RTV) responses
* @sp: The sequence the RTV was on
* @fp: The RTV response frame
* @rdata_arg: The remote port that sent the RTV response
@@ -1130,10 +1321,13 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_rport_priv *rdata = rdata_arg;
u8 op;
- mutex_lock(&rdata->rp_mutex);
-
FC_RPORT_DBG(rdata, "Received a RTV %s\n", fc_els_resp_type(fp));
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ goto put;
+
+ mutex_lock(&rdata->rp_mutex);
+
if (rdata->rp_state != RPORT_ST_RTV) {
FC_RPORT_DBG(rdata, "Received a RTV response, but in state "
"%s\n", fc_rport_state(rdata));
@@ -1143,7 +1337,7 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
}
if (IS_ERR(fp)) {
- fc_rport_error(rdata, fp);
+ fc_rport_error(rdata, PTR_ERR(fp));
goto err;
}
@@ -1159,13 +1353,15 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
tov = ntohl(rtv->rtv_r_a_tov);
if (tov == 0)
tov = 1;
- rdata->r_a_tov = tov;
+ if (tov > rdata->r_a_tov)
+ rdata->r_a_tov = tov;
tov = ntohl(rtv->rtv_e_d_tov);
if (toq & FC_ELS_RTV_EDRES)
tov /= 1000000;
if (tov == 0)
tov = 1;
- rdata->e_d_tov = tov;
+ if (tov > rdata->e_d_tov)
+ rdata->e_d_tov = tov;
}
}
@@ -1175,21 +1371,23 @@ out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+put:
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
* fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request
* @rdata: The remote port to send the RTV request to
*
- * Locking Note: The rport lock is expected to be held before calling
- * this routine.
+ * Reference counting: increments kref when sending ELS
*/
static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
{
struct fc_frame *fp;
struct fc_lport *lport = rdata->local_port;
+ lockdep_assert_held(&rdata->rp_mutex);
+
FC_RPORT_DBG(rdata, "Port entered RTV state from %s state\n",
fc_rport_state(rdata));
@@ -1197,60 +1395,101 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
if (!fp) {
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
return;
}
+ kref_get(&rdata->kref);
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
fc_rport_rtv_resp, rdata,
- 2 * lport->r_a_tov))
- fc_rport_error_retry(rdata, NULL);
- else
- kref_get(&rdata->kref);
+ 2 * lport->r_a_tov)) {
+ fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
+}
+
+/**
+ * fc_rport_recv_rtv_req() - Handler for Read Timeout Value (RTV) requests
+ * @rdata: The remote port that sent the RTV request
+ * @in_fp: The RTV request frame
+ */
+static void fc_rport_recv_rtv_req(struct fc_rport_priv *rdata,
+ struct fc_frame *in_fp)
+{
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+ struct fc_els_rtv_acc *rtv;
+ struct fc_seq_els_data rjt_data;
+
+ lockdep_assert_held(&rdata->rp_mutex);
+ lockdep_assert_held(&lport->lp_mutex);
+
+ FC_RPORT_DBG(rdata, "Received RTV request\n");
+
+ fp = fc_frame_alloc(lport, sizeof(*rtv));
+ if (!fp) {
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
+ fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
+ goto drop;
+ }
+ rtv = fc_frame_payload_get(fp, sizeof(*rtv));
+ rtv->rtv_cmd = ELS_LS_ACC;
+ rtv->rtv_r_a_tov = htonl(lport->r_a_tov);
+ rtv->rtv_e_d_tov = htonl(lport->e_d_tov);
+ rtv->rtv_toq = 0;
+ fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0);
+ lport->tt.frame_send(lport, fp);
+drop:
+ fc_frame_free(in_fp);
}
/**
* fc_rport_logo_resp() - Handler for logout (LOGO) responses
* @sp: The sequence the LOGO was on
* @fp: The LOGO response frame
- * @lport_arg: The local port
+ * @rdata_arg: The remote port
*/
static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *lport_arg)
+ void *rdata_arg)
{
- struct fc_lport *lport = lport_arg;
+ struct fc_rport_priv *rdata = rdata_arg;
+ struct fc_lport *lport = rdata->local_port;
FC_RPORT_ID_DBG(lport, fc_seq_exch(sp)->did,
"Received a LOGO %s\n", fc_els_resp_type(fp));
- if (IS_ERR(fp))
- return;
- fc_frame_free(fp);
+ if (!IS_ERR(fp))
+ fc_frame_free(fp);
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
* fc_rport_enter_logo() - Send a logout (LOGO) request
* @rdata: The remote port to send the LOGO request to
*
- * Locking Note: The rport lock is expected to be held before calling
- * this routine.
+ * Reference counting: increments kref when sending ELS
*/
static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
{
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
+ lockdep_assert_held(&rdata->rp_mutex);
+
FC_RPORT_DBG(rdata, "Port sending LOGO from %s state\n",
fc_rport_state(rdata));
fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
if (!fp)
return;
- (void)lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
- fc_rport_logo_resp, lport, 0);
+ kref_get(&rdata->kref);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
+ fc_rport_logo_resp, rdata, 0))
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
- * fc_rport_els_adisc_resp() - Handler for Address Discovery (ADISC) responses
+ * fc_rport_adisc_resp() - Handler for Address Discovery (ADISC) responses
* @sp: The sequence the ADISC response was on
* @fp: The ADISC response frame
* @rdata_arg: The remote port that sent the ADISC response
@@ -1266,10 +1505,13 @@ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_els_adisc *adisc;
u8 op;
- mutex_lock(&rdata->rp_mutex);
-
FC_RPORT_DBG(rdata, "Received a ADISC response\n");
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ goto put;
+
+ mutex_lock(&rdata->rp_mutex);
+
if (rdata->rp_state != RPORT_ST_ADISC) {
FC_RPORT_DBG(rdata, "Received a ADISC resp but in state %s\n",
fc_rport_state(rdata));
@@ -1279,7 +1521,7 @@ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
}
if (IS_ERR(fp)) {
- fc_rport_error(rdata, fp);
+ fc_rport_error(rdata, PTR_ERR(fp));
goto err;
}
@@ -1304,21 +1546,23 @@ out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+put:
+ kref_put(&rdata->kref, fc_rport_destroy);
}
/**
* fc_rport_enter_adisc() - Send Address Discover (ADISC) request
* @rdata: The remote port to send the ADISC request to
*
- * Locking Note: The rport lock is expected to be held before calling
- * this routine.
+ * Reference counting: increments kref when sending ELS
*/
static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
{
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
+ lockdep_assert_held(&rdata->rp_mutex);
+
FC_RPORT_DBG(rdata, "sending ADISC from %s state\n",
fc_rport_state(rdata));
@@ -1326,23 +1570,22 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
fp = fc_frame_alloc(lport, sizeof(struct fc_els_adisc));
if (!fp) {
- fc_rport_error_retry(rdata, fp);
+ fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
return;
}
+ kref_get(&rdata->kref);
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
fc_rport_adisc_resp, rdata,
- 2 * lport->r_a_tov))
- fc_rport_error_retry(rdata, NULL);
- else
- kref_get(&rdata->kref);
+ 2 * lport->r_a_tov)) {
+ fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
}
/**
* fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests
* @rdata: The remote port that sent the ADISC request
* @in_fp: The ADISC request frame
- *
- * Locking Note: Called with the lport and rport locks held.
*/
static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
struct fc_frame *in_fp)
@@ -1352,13 +1595,16 @@ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
struct fc_els_adisc *adisc;
struct fc_seq_els_data rjt_data;
+ lockdep_assert_held(&rdata->rp_mutex);
+ lockdep_assert_held(&lport->lp_mutex);
+
FC_RPORT_DBG(rdata, "Received ADISC request\n");
adisc = fc_frame_payload_get(in_fp, sizeof(*adisc));
if (!adisc) {
rjt_data.reason = ELS_RJT_PROT;
rjt_data.explan = ELS_EXPL_INV_LEN;
- lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
goto drop;
}
@@ -1378,9 +1624,6 @@ drop:
* fc_rport_recv_rls_req() - Handle received Read Link Status request
* @rdata: The remote port that sent the RLS request
* @rx_fp: The PRLI request frame
- *
- * Locking Note: The rport lock is expected to be held before calling
- * this function.
*/
static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
struct fc_frame *rx_fp)
@@ -1394,6 +1637,8 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
struct fc_seq_els_data rjt_data;
struct fc_host_statistics *hst;
+ lockdep_assert_held(&rdata->rp_mutex);
+
FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
fc_rport_state(rdata));
@@ -1434,7 +1679,7 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
goto out;
out_rjt:
- lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
out:
fc_frame_free(rx_fp);
}
@@ -1447,21 +1692,24 @@ out:
* Handle incoming ELS requests that require port login.
* The ELS opcode has already been validated by the caller.
*
- * Locking Note: Called with the lport lock held.
+ * Reference counting: does not modify kref
*/
static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_rport_priv *rdata;
struct fc_seq_els_data els_data;
- mutex_lock(&lport->disc.disc_mutex);
- rdata = lport->tt.rport_lookup(lport, fc_frame_sid(fp));
+ lockdep_assert_held(&lport->lp_mutex);
+
+ rdata = fc_rport_lookup(lport, fc_frame_sid(fp));
if (!rdata) {
- mutex_unlock(&lport->disc.disc_mutex);
+ FC_RPORT_ID_DBG(lport, fc_frame_sid(fp),
+ "Received ELS 0x%02x from non-logged-in port\n",
+ fc_frame_payload_op(fp));
goto reject;
}
+
mutex_lock(&rdata->rp_mutex);
- mutex_unlock(&lport->disc.disc_mutex);
switch (rdata->rp_state) {
case RPORT_ST_PRLI:
@@ -1469,8 +1717,22 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
case RPORT_ST_READY:
case RPORT_ST_ADISC:
break;
+ case RPORT_ST_PLOGI:
+ if (fc_frame_payload_op(fp) == ELS_PRLI) {
+ FC_RPORT_DBG(rdata, "Reject ELS PRLI "
+ "while in state %s\n",
+ fc_rport_state(rdata));
+ mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ goto busy;
+ }
+ fallthrough;
default:
+ FC_RPORT_DBG(rdata,
+ "Reject ELS 0x%02x while in state %s\n",
+ fc_frame_payload_op(fp), fc_rport_state(rdata));
mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, fc_rport_destroy);
goto reject;
}
@@ -1485,29 +1747,41 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
fc_rport_recv_adisc_req(rdata, fp);
break;
case ELS_RRQ:
- lport->tt.seq_els_rsp_send(fp, ELS_RRQ, NULL);
+ fc_seq_els_rsp_send(fp, ELS_RRQ, NULL);
fc_frame_free(fp);
break;
case ELS_REC:
- lport->tt.seq_els_rsp_send(fp, ELS_REC, NULL);
+ fc_seq_els_rsp_send(fp, ELS_REC, NULL);
fc_frame_free(fp);
break;
case ELS_RLS:
fc_rport_recv_rls_req(rdata, fp);
break;
+ case ELS_RTV:
+ fc_rport_recv_rtv_req(rdata, fp);
+ break;
default:
fc_frame_free(fp); /* can't happen */
break;
}
mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, fc_rport_destroy);
return;
reject:
els_data.reason = ELS_RJT_UNAB;
els_data.explan = ELS_EXPL_PLOGI_REQD;
- lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
+ fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
+ fc_frame_free(fp);
+ return;
+
+busy:
+ els_data.reason = ELS_RJT_BUSY;
+ els_data.explan = ELS_EXPL_NONE;
+ fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
fc_frame_free(fp);
+ return;
}
/**
@@ -1515,12 +1789,14 @@ reject:
* @lport: The local port that received the request
* @fp: The request frame
*
- * Locking Note: Called with the lport lock held.
+ * Reference counting: does not modify kref
*/
void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_seq_els_data els_data;
+ lockdep_assert_held(&lport->lp_mutex);
+
/*
* Handle FLOGI, PLOGI and LOGO requests separately, since they
* don't require prior login.
@@ -1543,23 +1819,25 @@ void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
case ELS_RRQ:
case ELS_REC:
case ELS_RLS:
+ case ELS_RTV:
fc_rport_recv_els_req(lport, fp);
break;
default:
els_data.reason = ELS_RJT_UNSUP;
els_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
+ fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
fc_frame_free(fp);
break;
}
}
+EXPORT_SYMBOL(fc_rport_recv_req);
/**
* fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests
* @lport: The local port that received the PLOGI request
* @rx_fp: The PLOGI request frame
*
- * Locking Note: The rport lock is held before calling this function.
+ * Reference counting: increments kref on return
*/
static void fc_rport_recv_plogi_req(struct fc_lport *lport,
struct fc_frame *rx_fp)
@@ -1571,6 +1849,8 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
struct fc_seq_els_data rjt_data;
u32 sid;
+ lockdep_assert_held(&lport->lp_mutex);
+
sid = fc_frame_sid(fp);
FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
@@ -1585,7 +1865,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
disc = &lport->disc;
mutex_lock(&disc->disc_mutex);
- rdata = lport->tt.rport_create(lport, sid);
+ rdata = fc_rport_create(lport, sid);
if (!rdata) {
mutex_unlock(&disc->disc_mutex);
rjt_data.reason = ELS_RJT_UNAB;
@@ -1643,6 +1923,13 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
rjt_data.explan = ELS_EXPL_NONE;
goto reject;
}
+ if (!fc_rport_compatible_roles(lport, rdata)) {
+ FC_RPORT_DBG(rdata, "Received PLOGI for incompatible role\n");
+ mutex_unlock(&rdata->rp_mutex);
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ goto reject;
+ }
/*
* Get session payload size from incoming PLOGI.
@@ -1666,7 +1953,7 @@ out:
return;
reject:
- lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
fc_frame_free(fp);
}
@@ -1674,9 +1961,6 @@ reject:
* fc_rport_recv_prli_req() - Handler for process login (PRLI) requests
* @rdata: The remote port that sent the PRLI request
* @rx_fp: The PRLI request frame
- *
- * Locking Note: The rport lock is exected to be held before calling
- * this function.
*/
static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
struct fc_frame *rx_fp)
@@ -1692,10 +1976,11 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
unsigned int len;
unsigned int plen;
enum fc_els_spp_resp resp;
- enum fc_els_spp_resp passive;
struct fc_seq_els_data rjt_data;
struct fc4_prov *prov;
+ lockdep_assert_held(&rdata->rp_mutex);
+
FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
fc_rport_state(rdata));
@@ -1742,15 +2027,21 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
resp = 0;
if (rspp->spp_type < FC_FC4_PROV_SIZE) {
+ enum fc_els_spp_resp active = 0, passive = 0;
+
prov = fc_active_prov[rspp->spp_type];
if (prov)
- resp = prov->prli(rdata, plen, rspp, spp);
+ active = prov->prli(rdata, plen, rspp, spp);
prov = fc_passive_prov[rspp->spp_type];
- if (prov) {
+ if (prov)
passive = prov->prli(rdata, plen, rspp, spp);
- if (!resp || passive == FC_SPP_RESP_ACK)
- resp = passive;
- }
+ if (!active || passive == FC_SPP_RESP_ACK)
+ resp = passive;
+ else
+ resp = active;
+ FC_RPORT_DBG(rdata, "PRLI rspp type %x "
+ "active %x passive %x\n",
+ rspp->spp_type, active, passive);
}
if (!resp) {
if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
@@ -1771,20 +2062,13 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
lport->tt.frame_send(lport, fp);
- switch (rdata->rp_state) {
- case RPORT_ST_PRLI:
- fc_rport_enter_ready(rdata);
- break;
- default:
- break;
- }
goto drop;
reject_len:
rjt_data.reason = ELS_RJT_PROT;
rjt_data.explan = ELS_EXPL_INV_LEN;
reject:
- lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
drop:
fc_frame_free(rx_fp);
}
@@ -1793,9 +2077,6 @@ drop:
* fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests
* @rdata: The remote port that sent the PRLO request
* @rx_fp: The PRLO request frame
- *
- * Locking Note: The rport lock is exected to be held before calling
- * this function.
*/
static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
struct fc_frame *rx_fp)
@@ -1812,6 +2093,8 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
unsigned int plen;
struct fc_seq_els_data rjt_data;
+ lockdep_assert_held(&rdata->rp_mutex);
+
FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
fc_rport_state(rdata));
@@ -1845,7 +2128,7 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
spp->spp_type_ext = rspp->spp_type_ext;
spp->spp_flags = FC_SPP_RESP_ACK;
- fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+ fc_rport_enter_prli(rdata);
fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
lport->tt.frame_send(lport, fp);
@@ -1855,7 +2138,7 @@ reject_len:
rjt_data.reason = ELS_RJT_PROT;
rjt_data.explan = ELS_EXPL_INV_LEN;
reject:
- lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
+ fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
drop:
fc_frame_free(rx_fp);
}
@@ -1865,72 +2148,42 @@ drop:
* @lport: The local port that received the LOGO request
* @fp: The LOGO request frame
*
- * Locking Note: The rport lock is exected to be held before calling
- * this function.
+ * Reference counting: drops kref on return
*/
static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_rport_priv *rdata;
u32 sid;
- lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
+ lockdep_assert_held(&lport->lp_mutex);
+
+ fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
sid = fc_frame_sid(fp);
- mutex_lock(&lport->disc.disc_mutex);
- rdata = lport->tt.rport_lookup(lport, sid);
+ rdata = fc_rport_lookup(lport, sid);
if (rdata) {
mutex_lock(&rdata->rp_mutex);
FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
fc_rport_state(rdata));
- fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+ fc_rport_enter_delete(rdata, RPORT_EV_STOP);
mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, fc_rport_destroy);
} else
FC_RPORT_ID_DBG(lport, sid,
"Received LOGO from non-logged-in port\n");
- mutex_unlock(&lport->disc.disc_mutex);
fc_frame_free(fp);
}
/**
* fc_rport_flush_queue() - Flush the rport_event_queue
*/
-static void fc_rport_flush_queue(void)
+void fc_rport_flush_queue(void)
{
flush_workqueue(rport_event_queue);
}
-
-/**
- * fc_rport_init() - Initialize the remote port layer for a local port
- * @lport: The local port to initialize the remote port layer for
- */
-int fc_rport_init(struct fc_lport *lport)
-{
- if (!lport->tt.rport_lookup)
- lport->tt.rport_lookup = fc_rport_lookup;
-
- if (!lport->tt.rport_create)
- lport->tt.rport_create = fc_rport_create;
-
- if (!lport->tt.rport_login)
- lport->tt.rport_login = fc_rport_login;
-
- if (!lport->tt.rport_logoff)
- lport->tt.rport_logoff = fc_rport_logoff;
-
- if (!lport->tt.rport_recv_req)
- lport->tt.rport_recv_req = fc_rport_recv_req;
-
- if (!lport->tt.rport_flush_queue)
- lport->tt.rport_flush_queue = fc_rport_flush_queue;
-
- if (!lport->tt.rport_destroy)
- lport->tt.rport_destroy = fc_rport_destroy;
-
- return 0;
-}
-EXPORT_SYMBOL(fc_rport_init);
+EXPORT_SYMBOL(fc_rport_flush_queue);
/**
* fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
@@ -1959,7 +2212,7 @@ static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
rdata->flags |= FC_RP_FLAGS_RETRY;
rdata->supported_classes = FC_COS_CLASS3;
- if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR))
+ if (!(lport->service_params & FCP_SPPF_INIT_FCN))
return 0;
spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
@@ -2010,7 +2263,8 @@ struct fc4_prov fc_rport_t0_prov = {
*/
int fc_setup_rport(void)
{
- rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
+ rport_event_queue =
+ alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, "fc_rport_eq");
if (!rport_event_queue)
return -ENOMEM;
return 0;
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 0c550d5b9133..c9f410c50978 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* iSCSI lib functions
*
@@ -6,27 +7,15 @@
* Copyright (C) 2004 - 2005 Dmitry Yusupov
* Copyright (C) 2004 - 2005 Alex Aizman
* maintained by open-iscsi@googlegroups.com
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/types.h>
#include <linux/kfifo.h>
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/sched/signal.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
#include <net/tcp.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -38,6 +27,7 @@
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/libiscsi.h>
+#include <trace/events/iscsi.h>
static int iscsi_dbg_lib_conn;
module_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int,
@@ -66,6 +56,9 @@ MODULE_PARM_DESC(debug_libiscsi_eh,
iscsi_conn_printk(KERN_INFO, _conn, \
"%s " dbg_fmt, \
__func__, ##arg); \
+ iscsi_dbg_trace(trace_iscsi_dbg_conn, \
+ &(_conn)->cls_conn->dev, \
+ "%s " dbg_fmt, __func__, ##arg);\
} while (0);
#define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \
@@ -74,6 +67,9 @@ MODULE_PARM_DESC(debug_libiscsi_eh,
iscsi_session_printk(KERN_INFO, _session, \
"%s " dbg_fmt, \
__func__, ##arg); \
+ iscsi_dbg_trace(trace_iscsi_dbg_session, \
+ &(_session)->cls_session->dev, \
+ "%s " dbg_fmt, __func__, ##arg); \
} while (0);
#define ISCSI_DBG_EH(_session, dbg_fmt, arg...) \
@@ -82,33 +78,32 @@ MODULE_PARM_DESC(debug_libiscsi_eh,
iscsi_session_printk(KERN_INFO, _session, \
"%s " dbg_fmt, \
__func__, ##arg); \
+ iscsi_dbg_trace(trace_iscsi_dbg_eh, \
+ &(_session)->cls_session->dev, \
+ "%s " dbg_fmt, __func__, ##arg); \
} while (0);
-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
-#define SNA32_CHECK 2147483648UL
+#define ISCSI_CMD_COMPL_WAIT 5
-static int iscsi_sna_lt(u32 n1, u32 n2)
+inline void iscsi_conn_queue_xmit(struct iscsi_conn *conn)
{
- return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
-}
+ struct Scsi_Host *shost = conn->session->host;
+ struct iscsi_host *ihost = shost_priv(shost);
-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
-static int iscsi_sna_lte(u32 n1, u32 n2)
-{
- return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
+ if (ihost->workq)
+ queue_work(ihost->workq, &conn->xmitwork);
}
+EXPORT_SYMBOL_GPL(iscsi_conn_queue_xmit);
-inline void iscsi_conn_queue_work(struct iscsi_conn *conn)
+inline void iscsi_conn_queue_recv(struct iscsi_conn *conn)
{
struct Scsi_Host *shost = conn->session->host;
struct iscsi_host *ihost = shost_priv(shost);
- if (ihost->workq)
- queue_work(ihost->workq, &conn->xmitwork);
+ if (ihost->workq && !test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags))
+ queue_work(ihost->workq, &conn->recvwork);
}
-EXPORT_SYMBOL_GPL(iscsi_conn_queue_work);
+EXPORT_SYMBOL_GPL(iscsi_conn_queue_recv);
static void __iscsi_update_cmdsn(struct iscsi_session *session,
uint32_t exp_cmdsn, uint32_t max_cmdsn)
@@ -125,16 +120,8 @@ static void __iscsi_update_cmdsn(struct iscsi_session *session,
session->exp_cmdsn = exp_cmdsn;
if (max_cmdsn != session->max_cmdsn &&
- !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) {
+ !iscsi_sna_lt(max_cmdsn, session->max_cmdsn))
session->max_cmdsn = max_cmdsn;
- /*
- * if the window closed with IO queued, then kick the
- * xmit thread
- */
- if (!list_empty(&session->leadconn->cmdqueue) ||
- !list_empty(&session->leadconn->mgmtqueue))
- iscsi_conn_queue_work(session->leadconn);
- }
}
void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
@@ -169,7 +156,7 @@ void iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t
hdr->datasn = cpu_to_be32(r2t->datasn);
r2t->datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, task->lun, sizeof(hdr->lun));
+ hdr->lun = task->lun;
hdr->itt = task->hdr_itt;
hdr->exp_statsn = r2t->exp_statsn;
hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent);
@@ -240,32 +227,6 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_task *task)
return 0;
}
-static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
-{
- struct scsi_cmnd *sc = task->sc;
- struct iscsi_rlength_ahdr *rlen_ahdr;
- int rc;
-
- rlen_ahdr = iscsi_next_hdr(task);
- rc = iscsi_add_hdr(task, sizeof(*rlen_ahdr));
- if (rc)
- return rc;
-
- rlen_ahdr->ahslength =
- cpu_to_be16(sizeof(rlen_ahdr->read_length) +
- sizeof(rlen_ahdr->reserved));
- rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
- rlen_ahdr->reserved = 0;
- rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
-
- ISCSI_DBG_SESSION(task->conn->session,
- "bidi-in rlen_ahdr->read_length(%d) "
- "rlen_ahdr->ahslength(%d)\n",
- be32_to_cpu(rlen_ahdr->read_length),
- be16_to_cpu(rlen_ahdr->ahslength));
- return 0;
-}
-
/**
* iscsi_check_tmf_restrictions - check if a task is affected by TMF
* @task: iscsi task
@@ -281,11 +242,11 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
*/
static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
{
- struct iscsi_conn *conn = task->conn;
- struct iscsi_tm *tmf = &conn->tmhdr;
- unsigned int hdr_lun;
+ struct iscsi_session *session = task->conn->session;
+ struct iscsi_tm *tmf = &session->tmhdr;
+ u64 hdr_lun;
- if (conn->tmf_state == TMF_INITIAL)
+ if (session->tmf_state == TMF_INITIAL)
return 0;
if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC)
@@ -296,33 +257,28 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
/*
* Allow PDUs for unrelated LUNs
*/
- hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun);
+ hdr_lun = scsilun_to_int(&tmf->lun);
if (hdr_lun != task->sc->device->lun)
return 0;
- /* fall through */
+ fallthrough;
case ISCSI_TM_FUNC_TARGET_WARM_RESET:
/*
* Fail all SCSI cmd PDUs
*/
if (opcode != ISCSI_OP_SCSI_DATA_OUT) {
- iscsi_conn_printk(KERN_INFO, conn,
- "task [op %x/%x itt "
- "0x%x/0x%x] "
- "rejected.\n",
- task->hdr->opcode, opcode,
- task->itt, task->hdr_itt);
+ iscsi_session_printk(KERN_INFO, session,
+ "task [op %x itt 0x%x/0x%x] rejected.\n",
+ opcode, task->itt, task->hdr_itt);
return -EACCES;
}
/*
* And also all data-out PDUs in response to R2T
* if fast_abort is set.
*/
- if (conn->session->fast_abort) {
- iscsi_conn_printk(KERN_INFO, conn,
- "task [op %x/%x itt "
- "0x%x/0x%x] fast abort.\n",
- task->hdr->opcode, opcode,
- task->itt, task->hdr_itt);
+ if (session->fast_abort) {
+ iscsi_session_printk(KERN_INFO, session,
+ "task [op %x itt 0x%x/0x%x] fast abort.\n",
+ opcode, task->itt, task->hdr_itt);
return -EACCES;
}
break;
@@ -335,7 +291,7 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
*/
if (opcode == ISCSI_OP_SCSI_DATA_OUT &&
task->hdr_itt == tmf->rtt) {
- ISCSI_DBG_SESSION(conn->session,
+ ISCSI_DBG_SESSION(session,
"Preventing task %x/%x from sending "
"data-out due to abort task in "
"progress\n", task->itt,
@@ -360,8 +316,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
struct iscsi_conn *conn = task->conn;
struct iscsi_session *session = conn->session;
struct scsi_cmnd *sc = task->sc;
- struct iscsi_cmd *hdr;
- unsigned hdrlength, cmd_len;
+ struct iscsi_scsi_req *hdr;
+ unsigned hdrlength, cmd_len, transfer_length;
itt_t itt;
int rc;
@@ -374,7 +330,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
if (rc)
return rc;
}
- hdr = (struct iscsi_cmd *) task->hdr;
+ hdr = (struct iscsi_scsi_req *)task->hdr;
itt = hdr->itt;
memset(hdr, 0, sizeof(*hdr));
@@ -389,8 +345,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
return rc;
hdr->opcode = ISCSI_OP_SCSI_CMD;
hdr->flags = ISCSI_ATTR_SIMPLE;
- int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
- memcpy(task->lun, hdr->lun, sizeof(task->lun));
+ int_to_scsilun(sc->device->lun, &hdr->lun);
+ task->lun = hdr->lun;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
cmd_len = sc->cmd_len;
if (cmd_len < ISCSI_CDB_SIZE)
@@ -404,17 +360,14 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
memcpy(hdr->cdb, sc->cmnd, cmd_len);
task->imm_count = 0;
- if (scsi_bidi_cmnd(sc)) {
- hdr->flags |= ISCSI_FLAG_CMD_READ;
- rc = iscsi_prep_bidi_ahs(task);
- if (rc)
- return rc;
- }
+ if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
+ task->protected = true;
+
+ transfer_length = scsi_transfer_length(sc);
+ hdr->data_length = cpu_to_be32(transfer_length);
if (sc->sc_data_direction == DMA_TO_DEVICE) {
- unsigned out_len = scsi_out(sc)->length;
struct iscsi_r2t_info *r2t = &task->unsol_r2t;
- hdr->data_length = cpu_to_be32(out_len);
hdr->flags |= ISCSI_FLAG_CMD_WRITE;
/*
* Write counters:
@@ -433,18 +386,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
memset(r2t, 0, sizeof(*r2t));
if (session->imm_data_en) {
- if (out_len >= session->first_burst)
+ if (transfer_length >= session->first_burst)
task->imm_count = min(session->first_burst,
conn->max_xmit_dlength);
else
- task->imm_count = min(out_len,
- conn->max_xmit_dlength);
+ task->imm_count = min(transfer_length,
+ conn->max_xmit_dlength);
hton24(hdr->dlength, task->imm_count);
} else
zero_data(hdr->dlength);
if (!session->initial_r2t_en) {
- r2t->data_length = min(session->first_burst, out_len) -
+ r2t->data_length = min(session->first_burst,
+ transfer_length) -
task->imm_count;
r2t->data_offset = task->imm_count;
r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
@@ -457,7 +411,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
} else {
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
zero_data(hdr->dlength);
- hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
if (sc->sc_data_direction == DMA_FROM_DEVICE)
hdr->flags |= ISCSI_FLAG_CMD_READ;
@@ -481,12 +434,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
conn->scsicmd_pdus_cnt++;
ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
- "itt 0x%x len %d bidi_len %d cmdsn %d win %d]\n",
- scsi_bidi_cmnd(sc) ? "bidirectional" :
+ "itt 0x%x len %d cmdsn %d win %d]\n",
sc->sc_data_direction == DMA_TO_DEVICE ?
"write" : "read", conn->id, sc, sc->cmnd[0],
- task->itt, scsi_bufflen(sc),
- scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+ task->itt, transfer_length,
session->cmdsn,
session->max_cmdsn - session->exp_cmdsn + 1);
return 0;
@@ -496,7 +447,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
* iscsi_free_task - free a task
* @task: iscsi cmd task
*
- * Must be called with session lock.
+ * Must be called with session back_lock.
* This function returns the scsi command to scsi-ml or cleans
* up mgmt tasks then returns the task to the pool.
*/
@@ -522,27 +473,32 @@ static void iscsi_free_task(struct iscsi_task *task)
kfifo_in(&session->cmdpool.queue, (void*)&task, sizeof(void*));
if (sc) {
- task->sc = NULL;
/* SCSI eh reuses commands to verify us */
- sc->SCp.ptr = NULL;
+ iscsi_cmd(sc)->task = NULL;
/*
* queue command may call this to free the task, so
* it will decide how to return sc to scsi-ml.
*/
if (oldstate != ISCSI_TASK_REQUEUE_SCSIQ)
- sc->scsi_done(sc);
+ scsi_done(sc);
}
}
-void __iscsi_get_task(struct iscsi_task *task)
+bool iscsi_get_task(struct iscsi_task *task)
{
- atomic_inc(&task->refcount);
+ return refcount_inc_not_zero(&task->refcount);
}
-EXPORT_SYMBOL_GPL(__iscsi_get_task);
+EXPORT_SYMBOL_GPL(iscsi_get_task);
+/**
+ * __iscsi_put_task - drop the refcount on a task
+ * @task: iscsi_task to drop the refcount on
+ *
+ * The back_lock must be held when calling in case it frees the task.
+ */
void __iscsi_put_task(struct iscsi_task *task)
{
- if (atomic_dec_and_test(&task->refcount))
+ if (refcount_dec_and_test(&task->refcount))
iscsi_free_task(task);
}
EXPORT_SYMBOL_GPL(__iscsi_put_task);
@@ -551,9 +507,11 @@ void iscsi_put_task(struct iscsi_task *task)
{
struct iscsi_session *session = task->conn->session;
- spin_lock_bh(&session->lock);
- __iscsi_put_task(task);
- spin_unlock_bh(&session->lock);
+ if (refcount_dec_and_test(&task->refcount)) {
+ spin_lock_bh(&session->back_lock);
+ iscsi_free_task(task);
+ spin_unlock_bh(&session->back_lock);
+ }
}
EXPORT_SYMBOL_GPL(iscsi_put_task);
@@ -562,7 +520,7 @@ EXPORT_SYMBOL_GPL(iscsi_put_task);
* @task: iscsi cmd task
* @state: state to complete task with
*
- * Must be called with session lock.
+ * Must be called with session back_lock.
*/
static void iscsi_complete_task(struct iscsi_task *task, int state)
{
@@ -579,14 +537,8 @@ static void iscsi_complete_task(struct iscsi_task *task, int state)
WARN_ON_ONCE(task->state == ISCSI_TASK_FREE);
task->state = state;
- if (!list_empty(&task->running))
- list_del_init(&task->running);
-
- if (conn->task == task)
- conn->task = NULL;
-
- if (conn->ping_task == task)
- conn->ping_task = NULL;
+ if (READ_ONCE(conn->ping_task) == task)
+ WRITE_ONCE(conn->ping_task, NULL);
/* release get from queueing */
__iscsi_put_task(task);
@@ -601,7 +553,7 @@ static void iscsi_complete_task(struct iscsi_task *task, int state)
* This is used when drivers do not need or cannot perform
* lower level pdu processing.
*
- * Called with session lock
+ * Called with session back_lock
*/
void iscsi_complete_scsi_task(struct iscsi_task *task,
uint32_t exp_cmdsn, uint32_t max_cmdsn)
@@ -616,25 +568,57 @@ void iscsi_complete_scsi_task(struct iscsi_task *task,
}
EXPORT_SYMBOL_GPL(iscsi_complete_scsi_task);
+/*
+ * Must be called with back and frwd lock
+ */
+static bool cleanup_queued_task(struct iscsi_task *task)
+{
+ struct iscsi_conn *conn = task->conn;
+ bool early_complete = false;
+
+ /*
+ * We might have raced where we handled a R2T early and got a response
+ * but have not yet taken the task off the requeue list, then a TMF or
+ * recovery happened and so we can still see it here.
+ */
+ if (task->state == ISCSI_TASK_COMPLETED)
+ early_complete = true;
+
+ if (!list_empty(&task->running)) {
+ list_del_init(&task->running);
+ /*
+ * If it's on a list but still running this could be cleanup
+ * from a TMF or session recovery.
+ */
+ if (task->state == ISCSI_TASK_RUNNING ||
+ task->state == ISCSI_TASK_COMPLETED)
+ __iscsi_put_task(task);
+ }
+
+ if (conn->session->running_aborted_task == task) {
+ conn->session->running_aborted_task = NULL;
+ __iscsi_put_task(task);
+ }
+
+ if (conn->task == task) {
+ conn->task = NULL;
+ __iscsi_put_task(task);
+ }
+
+ return early_complete;
+}
/*
- * session lock must be held and if not called for a task that is
- * still pending or from the xmit thread, then xmit thread must
- * be suspended.
+ * session back and frwd lock must be held and if not called for a task that
+ * is still pending or from the xmit thread, then xmit thread must be suspended
*/
-static void fail_scsi_task(struct iscsi_task *task, int err)
+static void __fail_scsi_task(struct iscsi_task *task, int err)
{
struct iscsi_conn *conn = task->conn;
struct scsi_cmnd *sc;
int state;
- /*
- * if a command completes and we get a successful tmf response
- * we will hit this because the scsi eh abort code does not take
- * a ref to the task.
- */
- sc = task->sc;
- if (!sc)
+ if (cleanup_queued_task(task))
return;
if (task->state == ISCSI_TASK_PENDING) {
@@ -650,17 +634,21 @@ static void fail_scsi_task(struct iscsi_task *task, int err)
else
state = ISCSI_TASK_ABRT_TMF;
+ sc = task->sc;
sc->result = err << 16;
- if (!scsi_bidi_cmnd(sc))
- scsi_set_resid(sc, scsi_bufflen(sc));
- else {
- scsi_out(sc)->resid = scsi_out(sc)->length;
- scsi_in(sc)->resid = scsi_in(sc)->length;
- }
-
+ scsi_set_resid(sc, scsi_bufflen(sc));
iscsi_complete_task(task, state);
}
+static void fail_scsi_task(struct iscsi_task *task, int err)
+{
+ struct iscsi_session *session = task->conn->session;
+
+ spin_lock_bh(&session->back_lock);
+ __fail_scsi_task(task, err);
+ spin_unlock_bh(&session->back_lock);
+}
+
static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
struct iscsi_task *task)
{
@@ -707,17 +695,24 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
return 0;
}
+/**
+ * iscsi_alloc_mgmt_task - allocate and setup a mgmt task.
+ * @conn: iscsi conn that the task will be sent on.
+ * @hdr: iscsi pdu that will be sent.
+ * @data: buffer for data segment if needed.
+ * @data_size: length of data in bytes.
+ */
static struct iscsi_task *
-__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+iscsi_alloc_mgmt_task(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, uint32_t data_size)
{
struct iscsi_session *session = conn->session;
- struct iscsi_host *ihost = shost_priv(session->host);
uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK;
struct iscsi_task *task;
itt_t itt;
- if (session->state == ISCSI_STATE_TERMINATE)
+ if (session->state == ISCSI_STATE_TERMINATE ||
+ !test_bit(ISCSI_CONN_FLAG_BOUND, &conn->flags))
return NULL;
if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) {
@@ -733,11 +728,21 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
return NULL;
}
+ if (data_size > ISCSI_DEF_MAX_RECV_SEG_LEN) {
+ iscsi_conn_printk(KERN_ERR, conn, "Invalid buffer len of %u for login task. Max len is %u\n", data_size, ISCSI_DEF_MAX_RECV_SEG_LEN);
+ return NULL;
+ }
+
task = conn->login_task;
} else {
if (session->state != ISCSI_STATE_LOGGED_IN)
return NULL;
+ if (data_size != 0) {
+ iscsi_conn_printk(KERN_ERR, conn, "Can not send data buffer of len %u for op 0x%x\n", data_size, opcode);
+ return NULL;
+ }
+
BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
@@ -750,7 +755,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
* released by the lld when it has transmitted the task for
* pdus we do not expect a response for.
*/
- atomic_set(&task->refcount, 1);
+ refcount_set(&task->refcount, 1);
task->conn = conn;
task->sc = NULL;
INIT_LIST_HEAD(&task->running);
@@ -782,22 +787,57 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
task->conn->session->age);
}
+ return task;
+
+free_task:
+ iscsi_put_task(task);
+ return NULL;
+}
+
+/**
+ * iscsi_send_mgmt_task - Send task created with iscsi_alloc_mgmt_task.
+ * @task: iscsi task to send.
+ *
+ * On failure this returns a non-zero error code, and the driver must free
+ * the task with iscsi_put_task;
+ */
+static int iscsi_send_mgmt_task(struct iscsi_task *task)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_host *ihost = shost_priv(conn->session->host);
+ int rc = 0;
+
if (!ihost->workq) {
- if (iscsi_prep_mgmt_task(conn, task))
- goto free_task;
+ rc = iscsi_prep_mgmt_task(conn, task);
+ if (rc)
+ return rc;
- if (session->tt->xmit_task(task))
- goto free_task;
+ rc = session->tt->xmit_task(task);
+ if (rc)
+ return rc;
} else {
list_add_tail(&task->running, &conn->mgmtqueue);
- iscsi_conn_queue_work(conn);
+ iscsi_conn_queue_xmit(conn);
}
- return task;
+ return 0;
+}
-free_task:
- __iscsi_put_task(task);
- return NULL;
+static int __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size)
+{
+ struct iscsi_task *task;
+ int rc;
+
+ task = iscsi_alloc_mgmt_task(conn, hdr, data, data_size);
+ if (!task)
+ return -ENOMEM;
+
+ rc = iscsi_send_mgmt_task(task);
+ if (rc)
+ iscsi_put_task(task);
+ return rc;
}
int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
@@ -807,16 +847,16 @@ int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
struct iscsi_session *session = conn->session;
int err = 0;
- spin_lock_bh(&session->lock);
- if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
+ spin_lock_bh(&session->frwd_lock);
+ if (__iscsi_conn_send_pdu(conn, hdr, data, data_size))
err = -EPERM;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
return err;
}
EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
/**
- * iscsi_cmd_rsp - SCSI Command Response processing
+ * iscsi_scsi_cmd_rsp - SCSI Command Response processing
* @conn: iscsi connection
* @hdr: iscsi header
* @task: scsi command task
@@ -824,13 +864,13 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
* @datalen: len of buffer
*
* iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and
- * then completes the command and task.
+ * then completes the command and task. called under back_lock
**/
static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
struct iscsi_task *task, char *data,
int datalen)
{
- struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr;
+ struct iscsi_scsi_rsp *rhdr = (struct iscsi_scsi_rsp *)hdr;
struct iscsi_session *session = conn->session;
struct scsi_cmnd *sc = task->sc;
@@ -839,6 +879,27 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
sc->result = (DID_OK << 16) | rhdr->cmd_status;
+ if (task->protected) {
+ sector_t sector;
+ u8 ascq;
+
+ /**
+ * Transports that didn't implement check_protection
+ * callback but still published T10-PI support to scsi-mid
+ * deserve this BUG_ON.
+ **/
+ BUG_ON(!session->tt->check_protection);
+
+ ascq = session->tt->check_protection(task, &sector);
+ if (ascq) {
+ scsi_build_sense(sc, 1, ILLEGAL_REQUEST, 0x10, ascq);
+ scsi_set_sense_information(sc->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ sector);
+ goto out;
+ }
+ }
+
if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) {
sc->result = DID_ERROR << 16;
goto out;
@@ -869,14 +930,7 @@ invalid_datalen:
if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
- int res_count = be32_to_cpu(rhdr->bi_residual_count);
-
- if (scsi_bidi_cmnd(sc) && res_count > 0 &&
- (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
- res_count <= scsi_in(sc)->length))
- scsi_in(sc)->resid = res_count;
- else
- sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+ sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
}
if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
@@ -903,6 +957,9 @@ out:
* @conn: iscsi connection
* @hdr: iscsi pdu
* @task: scsi command task
+ *
+ * iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received
+ * then completes the command and task. called under back_lock
**/
static void
iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
@@ -923,8 +980,8 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (res_count > 0 &&
(rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
- res_count <= scsi_in(sc)->length))
- scsi_in(sc)->resid = res_count;
+ res_count <= sc->sdb.length))
+ scsi_set_resid(sc, res_count);
else
sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
}
@@ -939,58 +996,83 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
{
struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr;
+ struct iscsi_session *session = conn->session;
conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
conn->tmfrsp_pdus_cnt++;
- if (conn->tmf_state != TMF_QUEUED)
+ if (session->tmf_state != TMF_QUEUED)
return;
if (tmf->response == ISCSI_TMF_RSP_COMPLETE)
- conn->tmf_state = TMF_SUCCESS;
+ session->tmf_state = TMF_SUCCESS;
else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)
- conn->tmf_state = TMF_NOT_FOUND;
+ session->tmf_state = TMF_NOT_FOUND;
else
- conn->tmf_state = TMF_FAILED;
- wake_up(&conn->ehwait);
+ session->tmf_state = TMF_FAILED;
+ wake_up(&session->ehwait);
}
-static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
+static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
{
struct iscsi_nopout hdr;
struct iscsi_task *task;
- if (!rhdr && conn->ping_task)
- return;
+ if (!rhdr) {
+ if (READ_ONCE(conn->ping_task))
+ return -EINVAL;
+ }
memset(&hdr, 0, sizeof(struct iscsi_nopout));
hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
hdr.flags = ISCSI_FLAG_CMD_FINAL;
if (rhdr) {
- memcpy(hdr.lun, rhdr->lun, 8);
+ hdr.lun = rhdr->lun;
hdr.ttt = rhdr->ttt;
hdr.itt = RESERVED_ITT;
} else
hdr.ttt = RESERVED_ITT;
- task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+ task = iscsi_alloc_mgmt_task(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
if (!task)
+ return -ENOMEM;
+
+ if (!rhdr)
+ WRITE_ONCE(conn->ping_task, task);
+
+ if (iscsi_send_mgmt_task(task)) {
+ if (!rhdr)
+ WRITE_ONCE(conn->ping_task, NULL);
+ iscsi_put_task(task);
+
iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
- else if (!rhdr) {
+ return -EIO;
+ } else if (!rhdr) {
/* only track our nops */
- conn->ping_task = task;
conn->last_ping = jiffies;
}
+
+ return 0;
}
+/**
+ * iscsi_nop_out_rsp - SCSI NOP Response processing
+ * @task: scsi command task
+ * @nop: the nop structure
+ * @data: where to put the data
+ * @datalen: length of data
+ *
+ * iscsi_nop_out_rsp handles nop response from use or
+ * from user space. called under back_lock
+ **/
static int iscsi_nop_out_rsp(struct iscsi_task *task,
struct iscsi_nopin *nop, char *data, int datalen)
{
struct iscsi_conn *conn = task->conn;
int rc = 0;
- if (conn->ping_task != task) {
+ if (READ_ONCE(conn->ping_task) != task) {
/*
* If this is not in response to one of our
* nops then it must be from userspace.
@@ -1029,13 +1111,13 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
iscsi_conn_printk(KERN_ERR, conn,
"pdu (op 0x%x itt 0x%x) rejected "
"due to DataDigest error.\n",
- rejected_pdu.itt, opcode);
+ opcode, rejected_pdu.itt);
break;
case ISCSI_REASON_IMM_CMD_REJECT:
iscsi_conn_printk(KERN_ERR, conn,
"pdu (op 0x%x itt 0x%x) rejected. Too many "
"immediate commands.\n",
- rejected_pdu.itt, opcode);
+ opcode, rejected_pdu.itt);
/*
* We only send one TMF at a time so if the target could not
* handle it, then it should get fixed (RFC mandates that
@@ -1047,14 +1129,19 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (opcode != ISCSI_OP_NOOP_OUT)
return 0;
- if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
/*
* nop-out in response to target's nop-out rejected.
* Just resend.
*/
+ /* In RX path we are under back lock */
+ spin_unlock(&conn->session->back_lock);
+ spin_lock(&conn->session->frwd_lock);
iscsi_send_nopout(conn,
(struct iscsi_nopin*)&rejected_pdu);
- else {
+ spin_unlock(&conn->session->frwd_lock);
+ spin_lock(&conn->session->back_lock);
+ } else {
struct iscsi_task *task;
/*
* Our nop as ping got dropped. We know the target
@@ -1075,8 +1162,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
default:
iscsi_conn_printk(KERN_ERR, conn,
"pdu (op 0x%x itt 0x%x) rejected. Reason "
- "code 0x%x\n", rejected_pdu.itt,
- rejected_pdu.opcode, reject->reason);
+ "code 0x%x\n", rejected_pdu.opcode,
+ rejected_pdu.itt, reject->reason);
break;
}
return rc;
@@ -1090,7 +1177,7 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
* This should be used for mgmt tasks like login and nops, or if
* the LDD's itt space does not include the session age.
*
- * The session lock must be held.
+ * The session back_lock must be held.
*/
struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
{
@@ -1119,7 +1206,7 @@ EXPORT_SYMBOL_GPL(iscsi_itt_to_task);
* @datalen: len of data buffer
*
* Completes pdu processing by freeing any resources allocated at
- * queuecommand or send generic. session lock must be held and verify
+ * queuecommand or send generic. session back_lock must be held and verify
* itt must have been called.
*/
int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
@@ -1156,7 +1243,12 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
break;
+ /* In RX path we are under back lock */
+ spin_unlock(&session->back_lock);
+ spin_lock(&session->frwd_lock);
iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr);
+ spin_unlock(&session->frwd_lock);
+ spin_lock(&session->back_lock);
break;
case ISCSI_OP_REJECT:
rc = iscsi_handle_reject(conn, hdr, data, datalen);
@@ -1263,9 +1355,9 @@ int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
{
int rc;
- spin_lock(&conn->session->lock);
+ spin_lock(&conn->session->back_lock);
rc = __iscsi_complete_pdu(conn, hdr, data, datalen);
- spin_unlock(&conn->session->lock);
+ spin_unlock(&conn->session->back_lock);
return rc;
}
EXPORT_SYMBOL_GPL(iscsi_complete_pdu);
@@ -1309,7 +1401,7 @@ EXPORT_SYMBOL_GPL(iscsi_verify_itt);
*
* This should be used for cmd tasks.
*
- * The session lock must be held.
+ * The session back_lock must be held.
*/
struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt)
{
@@ -1322,10 +1414,10 @@ struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt)
if (!task || !task->sc)
return NULL;
- if (task->sc->SCp.phase != conn->session->age) {
+ if (iscsi_cmd(task->sc)->age != conn->session->age) {
iscsi_session_printk(KERN_ERR, conn->session,
"task's session age %d, expected %d\n",
- task->sc->SCp.phase, conn->session->age);
+ iscsi_cmd(task->sc)->age, conn->session->age);
return NULL;
}
@@ -1337,19 +1429,16 @@ void iscsi_session_failure(struct iscsi_session *session,
enum iscsi_err err)
{
struct iscsi_conn *conn;
- struct device *dev;
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
conn = session->leadconn;
if (session->state == ISCSI_STATE_TERMINATE || !conn) {
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
return;
}
- dev = get_device(&conn->cls_conn->dev);
- spin_unlock_bh(&session->lock);
- if (!dev)
- return;
+ iscsi_get_conn(conn->cls_conn);
+ spin_unlock_bh(&session->frwd_lock);
/*
* if the host is being removed bypass the connection
* recovery initialization because we are going to kill
@@ -1359,27 +1448,36 @@ void iscsi_session_failure(struct iscsi_session *session,
iscsi_conn_error_event(conn->cls_conn, err);
else
iscsi_conn_failure(conn, err);
- put_device(dev);
+ iscsi_put_conn(conn->cls_conn);
}
EXPORT_SYMBOL_GPL(iscsi_session_failure);
-void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+static bool iscsi_set_conn_failed(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
- spin_lock_bh(&session->lock);
- if (session->state == ISCSI_STATE_FAILED) {
- spin_unlock_bh(&session->lock);
- return;
- }
+ if (session->state == ISCSI_STATE_FAILED)
+ return false;
if (conn->stop_stage == 0)
session->state = ISCSI_STATE_FAILED;
- spin_unlock_bh(&session->lock);
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
- iscsi_conn_error_event(conn->cls_conn, err);
+ set_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags);
+ set_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags);
+ return true;
+}
+
+void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+{
+ struct iscsi_session *session = conn->session;
+ bool needs_evt;
+
+ spin_lock_bh(&session->frwd_lock);
+ needs_evt = iscsi_set_conn_failed(conn);
+ spin_unlock_bh(&session->frwd_lock);
+
+ if (needs_evt)
+ iscsi_conn_error_event(conn->cls_conn, err);
}
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
@@ -1400,24 +1498,63 @@ static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
return 0;
}
-static int iscsi_xmit_task(struct iscsi_conn *conn)
+static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task,
+ bool was_requeue)
{
- struct iscsi_task *task = conn->task;
int rc;
- if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx))
+ if (!conn->task) {
+ /*
+ * Take a ref so we can access it after xmit_task().
+ *
+ * This should never fail because the failure paths will have
+ * stopped the xmit thread.
+ */
+ if (!iscsi_get_task(task)) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+ } else {
+ /* Already have a ref from when we failed to send it last call */
+ conn->task = NULL;
+ }
+
+ /*
+ * If this was a requeue for a R2T we have an extra ref on the task in
+ * case a bad target sends a cmd rsp before we have handled the task.
+ */
+ if (was_requeue)
+ iscsi_put_task(task);
+
+ /*
+ * Do this after dropping the extra ref because if this was a requeue
+ * it's removed from that list and cleanup_queued_task would miss it.
+ */
+ if (test_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags)) {
+ /*
+ * Save the task and ref in case we weren't cleaning up this
+ * task and get woken up again.
+ */
+ conn->task = task;
return -ENODATA;
+ }
- __iscsi_get_task(task);
- spin_unlock_bh(&conn->session->lock);
+ spin_unlock_bh(&conn->session->frwd_lock);
rc = conn->session->tt->xmit_task(task);
- spin_lock_bh(&conn->session->lock);
+ spin_lock_bh(&conn->session->frwd_lock);
if (!rc) {
/* done with this task */
task->last_xfer = jiffies;
- conn->task = NULL;
+ } else {
+ /*
+ * get an extra ref that is released next time we access it
+ * as conn->task above.
+ */
+ iscsi_get_task(task);
+ conn->task = task;
}
- __iscsi_put_task(task);
+
+ iscsi_put_task(task);
return rc;
}
@@ -1425,9 +1562,7 @@ static int iscsi_xmit_task(struct iscsi_conn *conn)
* iscsi_requeue_task - requeue task to run from session workqueue
* @task: task to requeue
*
- * LLDs that need to run a task from the session workqueue should call
- * this. The session lock must be held. This should only be called
- * by software drivers.
+ * Callers must have taken a ref to the task that is going to be requeued.
*/
void iscsi_requeue_task(struct iscsi_task *task)
{
@@ -1437,9 +1572,18 @@ void iscsi_requeue_task(struct iscsi_task *task)
* this may be on the requeue list already if the xmit_task callout
* is handling the r2ts while we are adding new ones
*/
- if (list_empty(&task->running))
+ spin_lock_bh(&conn->session->frwd_lock);
+ if (list_empty(&task->running)) {
list_add_tail(&task->running, &conn->requeue);
- iscsi_conn_queue_work(conn);
+ } else {
+ /*
+ * Don't need the extra ref since it's already requeued and
+ * has a ref.
+ */
+ iscsi_put_task(task);
+ }
+ iscsi_conn_queue_xmit(conn);
+ spin_unlock_bh(&conn->session->frwd_lock);
}
EXPORT_SYMBOL_GPL(iscsi_requeue_task);
@@ -1457,15 +1601,15 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
struct iscsi_task *task;
int rc = 0;
- spin_lock_bh(&conn->session->lock);
- if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) {
+ spin_lock_bh(&conn->session->frwd_lock);
+ if (test_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags)) {
ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n");
- spin_unlock_bh(&conn->session->lock);
+ spin_unlock_bh(&conn->session->frwd_lock);
return -ENODATA;
}
if (conn->task) {
- rc = iscsi_xmit_task(conn);
+ rc = iscsi_xmit_task(conn, conn->task, false);
if (rc)
goto done;
}
@@ -1477,40 +1621,61 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
*/
check_mgmt:
while (!list_empty(&conn->mgmtqueue)) {
- conn->task = list_entry(conn->mgmtqueue.next,
- struct iscsi_task, running);
- list_del_init(&conn->task->running);
- if (iscsi_prep_mgmt_task(conn, conn->task)) {
- __iscsi_put_task(conn->task);
- conn->task = NULL;
+ task = list_entry(conn->mgmtqueue.next, struct iscsi_task,
+ running);
+ list_del_init(&task->running);
+ if (iscsi_prep_mgmt_task(conn, task)) {
+ /* regular RX path uses back_lock */
+ spin_lock_bh(&conn->session->back_lock);
+ __iscsi_put_task(task);
+ spin_unlock_bh(&conn->session->back_lock);
continue;
}
- rc = iscsi_xmit_task(conn);
+ rc = iscsi_xmit_task(conn, task, false);
+ if (rc)
+ goto done;
+ }
+
+check_requeue:
+ while (!list_empty(&conn->requeue)) {
+ /*
+ * we always do fastlogout - conn stop code will clean up.
+ */
+ if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
+ break;
+
+ task = list_entry(conn->requeue.next, struct iscsi_task,
+ running);
+
+ if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT))
+ break;
+
+ list_del_init(&task->running);
+ rc = iscsi_xmit_task(conn, task, true);
if (rc)
goto done;
+ if (!list_empty(&conn->mgmtqueue))
+ goto check_mgmt;
}
/* process pending command queue */
while (!list_empty(&conn->cmdqueue)) {
- conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task,
- running);
- list_del_init(&conn->task->running);
+ task = list_entry(conn->cmdqueue.next, struct iscsi_task,
+ running);
+ list_del_init(&task->running);
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
- fail_scsi_task(conn->task, DID_IMM_RETRY);
+ fail_scsi_task(task, DID_IMM_RETRY);
continue;
}
- rc = iscsi_prep_scsi_cmd_pdu(conn->task);
+ rc = iscsi_prep_scsi_cmd_pdu(task);
if (rc) {
- if (rc == -ENOMEM || rc == -EACCES) {
- list_add_tail(&conn->task->running,
- &conn->cmdqueue);
- conn->task = NULL;
- goto done;
- } else
- fail_scsi_task(conn->task, DID_ABORT);
+ if (rc == -ENOMEM || rc == -EACCES)
+ fail_scsi_task(task, DID_IMM_RETRY);
+ else
+ fail_scsi_task(task, DID_ABORT);
continue;
}
- rc = iscsi_xmit_task(conn);
+ rc = iscsi_xmit_task(conn, task, false);
if (rc)
goto done;
/*
@@ -1520,34 +1685,15 @@ check_mgmt:
*/
if (!list_empty(&conn->mgmtqueue))
goto check_mgmt;
+ if (!list_empty(&conn->requeue))
+ goto check_requeue;
}
- while (!list_empty(&conn->requeue)) {
- /*
- * we always do fastlogout - conn stop code will clean up.
- */
- if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
- break;
-
- task = list_entry(conn->requeue.next, struct iscsi_task,
- running);
- if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT))
- break;
-
- conn->task = task;
- list_del_init(&conn->task->running);
- conn->task->state = ISCSI_TASK_RUNNING;
- rc = iscsi_xmit_task(conn);
- if (rc)
- goto done;
- if (!list_empty(&conn->mgmtqueue))
- goto check_mgmt;
- }
- spin_unlock_bh(&conn->session->lock);
+ spin_unlock_bh(&conn->session->frwd_lock);
return -ENODATA;
done:
- spin_unlock_bh(&conn->session->lock);
+ spin_unlock_bh(&conn->session->frwd_lock);
return rc;
}
@@ -1573,16 +1719,17 @@ static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn,
(void *) &task, sizeof(void *)))
return NULL;
- sc->SCp.phase = conn->session->age;
- sc->SCp.ptr = (char *) task;
+ iscsi_cmd(sc)->age = conn->session->age;
+ iscsi_cmd(sc)->task = task;
- atomic_set(&task->refcount, 1);
+ refcount_set(&task->refcount, 1);
task->state = ISCSI_TASK_PENDING;
task->conn = conn;
task->sc = sc;
task->have_checked_conn = false;
task->last_timeout = jiffies;
task->last_xfer = jiffies;
+ task->protected = false;
INIT_LIST_HEAD(&task->running);
return task;
}
@@ -1610,13 +1757,13 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
struct iscsi_task *task = NULL;
sc->result = 0;
- sc->SCp.ptr = NULL;
+ iscsi_cmd(sc)->task = NULL;
ihost = shost_priv(host);
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
reason = iscsi_session_chkready(cls_session);
if (reason) {
@@ -1633,6 +1780,16 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
*/
switch (session->state) {
case ISCSI_STATE_FAILED:
+ /*
+ * cmds should fail during shutdown, if the session
+ * state is bad, allowing completion to happen
+ */
+ if (unlikely(system_state != SYSTEM_RUNNING)) {
+ reason = FAILURE_SESSION_FAILED;
+ sc->result = DID_NO_CONNECT << 16;
+ break;
+ }
+ fallthrough;
case ISCSI_STATE_IN_RECOVERY:
reason = FAILURE_SESSION_IN_RECOVERY;
sc->result = DID_IMM_RETRY << 16;
@@ -1663,9 +1820,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
goto fault;
}
- if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) {
+ if (test_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags)) {
reason = FAILURE_SESSION_IN_RECOVERY;
- sc->result = DID_REQUEUE;
+ sc->result = DID_REQUEUE << 16;
goto fault;
}
@@ -1698,57 +1855,37 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)
}
} else {
list_add_tail(&task->running, &conn->cmdqueue);
- iscsi_conn_queue_work(conn);
+ iscsi_conn_queue_xmit(conn);
}
session->queued_cmdsn++;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
return 0;
prepd_reject:
+ spin_lock_bh(&session->back_lock);
iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ);
+ spin_unlock_bh(&session->back_lock);
reject:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",
sc->cmnd[0], reason);
return SCSI_MLQUEUE_TARGET_BUSY;
prepd_fault:
+ spin_lock_bh(&session->back_lock);
iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ);
+ spin_unlock_bh(&session->back_lock);
fault:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",
sc->cmnd[0], reason);
- if (!scsi_bidi_cmnd(sc))
- scsi_set_resid(sc, scsi_bufflen(sc));
- else {
- scsi_out(sc)->resid = scsi_out(sc)->length;
- scsi_in(sc)->resid = scsi_in(sc)->length;
- }
- sc->scsi_done(sc);
+ scsi_set_resid(sc, scsi_bufflen(sc));
+ scsi_done(sc);
return 0;
}
EXPORT_SYMBOL_GPL(iscsi_queuecommand);
-int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
-{
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, depth);
- break;
- case SCSI_QDEPTH_RAMP_UP:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- default:
- return -EOPNOTSUPP;
- }
- return sdev->queue_depth;
-}
-EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
-
int iscsi_target_alloc(struct scsi_target *starget)
{
struct iscsi_cls_session *cls_session = starget_to_session(starget);
@@ -1759,45 +1896,41 @@ int iscsi_target_alloc(struct scsi_target *starget)
}
EXPORT_SYMBOL_GPL(iscsi_target_alloc);
-static void iscsi_tmf_timedout(unsigned long data)
+static void iscsi_tmf_timedout(struct timer_list *t)
{
- struct iscsi_conn *conn = (struct iscsi_conn *)data;
- struct iscsi_session *session = conn->session;
+ struct iscsi_session *session = timer_container_of(session, t,
+ tmf_timer);
- spin_lock(&session->lock);
- if (conn->tmf_state == TMF_QUEUED) {
- conn->tmf_state = TMF_TIMEDOUT;
+ spin_lock(&session->frwd_lock);
+ if (session->tmf_state == TMF_QUEUED) {
+ session->tmf_state = TMF_TIMEDOUT;
ISCSI_DBG_EH(session, "tmf timedout\n");
/* unblock eh_abort() */
- wake_up(&conn->ehwait);
+ wake_up(&session->ehwait);
}
- spin_unlock(&session->lock);
+ spin_unlock(&session->frwd_lock);
}
static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
struct iscsi_tm *hdr, int age,
int timeout)
+ __must_hold(&session->frwd_lock)
{
struct iscsi_session *session = conn->session;
- struct iscsi_task *task;
- task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
- NULL, 0);
- if (!task) {
- spin_unlock_bh(&session->lock);
+ if (__iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0)) {
+ spin_unlock_bh(&session->frwd_lock);
iscsi_conn_printk(KERN_ERR, conn, "Could not send TMF.\n");
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
return -EPERM;
}
conn->tmfcmd_pdus_cnt++;
- conn->tmf_timer.expires = timeout * HZ + jiffies;
- conn->tmf_timer.function = iscsi_tmf_timedout;
- conn->tmf_timer.data = (unsigned long)conn;
- add_timer(&conn->tmf_timer);
+ session->tmf_timer.expires = timeout * HZ + jiffies;
+ add_timer(&session->tmf_timer);
ISCSI_DBG_EH(session, "tmf set timeout\n");
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
/*
@@ -1808,15 +1941,15 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
* 3) session is terminated or restarted or userspace has
* given up on recovery
*/
- wait_event_interruptible(conn->ehwait, age != session->age ||
+ wait_event_interruptible(session->ehwait, age != session->age ||
session->state != ISCSI_STATE_LOGGED_IN ||
- conn->tmf_state != TMF_QUEUED);
+ session->tmf_state != TMF_QUEUED);
if (signal_pending(current))
flush_signals(current);
- del_timer_sync(&conn->tmf_timer);
+ timer_delete_sync(&session->tmf_timer);
mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
/* if the session drops it will clean up the task */
if (age != session->age ||
session->state != ISCSI_STATE_LOGGED_IN)
@@ -1825,35 +1958,50 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
}
/*
- * Fail commands. session lock held and recv side suspended and xmit
- * thread flushed
+ * Fail commands. session frwd lock held and xmit thread flushed.
*/
-static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun,
- int error)
+static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error)
{
+ struct iscsi_session *session = conn->session;
struct iscsi_task *task;
int i;
- for (i = 0; i < conn->session->cmds_max; i++) {
- task = conn->session->cmds[i];
+restart_cmd_loop:
+ spin_lock_bh(&session->back_lock);
+ for (i = 0; i < session->cmds_max; i++) {
+ task = session->cmds[i];
if (!task->sc || task->state == ISCSI_TASK_FREE)
continue;
if (lun != -1 && lun != task->sc->device->lun)
continue;
+ /*
+ * The cmd is completing but if this is called from an eh
+ * callout path then when we return scsi-ml owns the cmd. Wait
+ * for the completion path to finish freeing the cmd.
+ */
+ if (!iscsi_get_task(task)) {
+ spin_unlock_bh(&session->back_lock);
+ spin_unlock_bh(&session->frwd_lock);
+ udelay(ISCSI_CMD_COMPL_WAIT);
+ spin_lock_bh(&session->frwd_lock);
+ goto restart_cmd_loop;
+ }
- ISCSI_DBG_SESSION(conn->session,
+ ISCSI_DBG_SESSION(session,
"failing sc %p itt 0x%x state %d\n",
task->sc, task->itt, task->state);
- fail_scsi_task(task, error);
+ __fail_scsi_task(task, error);
+ __iscsi_put_task(task);
}
+ spin_unlock_bh(&session->back_lock);
}
/**
* iscsi_suspend_queue - suspend iscsi_queuecommand
* @conn: iscsi conn to stop queueing IO on
*
- * This grabs the session lock to make sure no one is in
+ * This grabs the session frwd_lock to make sure no one is in
* xmit_task/queuecommand, and then sets suspend to prevent
* new commands from being queued. This only needs to be called
* by offload drivers that need to sync a path like ep disconnect
@@ -1862,15 +2010,15 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun,
*/
void iscsi_suspend_queue(struct iscsi_conn *conn)
{
- spin_lock_bh(&conn->session->lock);
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
- spin_unlock_bh(&conn->session->lock);
+ spin_lock_bh(&conn->session->frwd_lock);
+ set_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags);
+ spin_unlock_bh(&conn->session->frwd_lock);
}
EXPORT_SYMBOL_GPL(iscsi_suspend_queue);
/**
* iscsi_suspend_tx - suspend iscsi_data_xmit
- * @conn: iscsi conn tp stop processing IO on.
+ * @conn: iscsi conn to stop processing IO on.
*
* This function sets the suspend bit to prevent iscsi_data_xmit
* from sending new IO, and if work is queued on the xmit thread
@@ -1881,17 +2029,32 @@ void iscsi_suspend_tx(struct iscsi_conn *conn)
struct Scsi_Host *shost = conn->session->host;
struct iscsi_host *ihost = shost_priv(shost);
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ set_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags);
if (ihost->workq)
- flush_workqueue(ihost->workq);
+ flush_work(&conn->xmitwork);
}
EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
static void iscsi_start_tx(struct iscsi_conn *conn)
{
- clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
- iscsi_conn_queue_work(conn);
+ clear_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags);
+ iscsi_conn_queue_xmit(conn);
+}
+
+/**
+ * iscsi_suspend_rx - Prevent recvwork from running again.
+ * @conn: iscsi conn to stop.
+ */
+void iscsi_suspend_rx(struct iscsi_conn *conn)
+{
+ struct Scsi_Host *shost = conn->session->host;
+ struct iscsi_host *ihost = shost_priv(shost);
+
+ set_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags);
+ if (ihost->workq)
+ flush_work(&conn->recvwork);
}
+EXPORT_SYMBOL_GPL(iscsi_suspend_rx);
/*
* We want to make sure a ping is in flight. It has timed out.
@@ -1901,7 +2064,7 @@ static void iscsi_start_tx(struct iscsi_conn *conn)
*/
static int iscsi_has_ping_timed_out(struct iscsi_conn *conn)
{
- if (conn->ping_task &&
+ if (READ_ONCE(conn->ping_task) &&
time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
(conn->ping_timeout * HZ), jiffies))
return 1;
@@ -1909,9 +2072,9 @@ static int iscsi_has_ping_timed_out(struct iscsi_conn *conn)
return 0;
}
-static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
+enum scsi_timeout_action iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
{
- enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED;
+ enum scsi_timeout_action rc = SCSI_EH_NOT_HANDLED;
struct iscsi_task *task = NULL, *running_task;
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
@@ -1923,30 +2086,56 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc);
- spin_lock(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
+ spin_lock(&session->back_lock);
+ task = iscsi_cmd(sc)->task;
+ if (!task) {
+ /*
+ * Raced with completion. Blk layer has taken ownership
+ * so let timeout code complete it now.
+ */
+ rc = SCSI_EH_NOT_HANDLED;
+ spin_unlock(&session->back_lock);
+ goto done;
+ }
+ if (!iscsi_get_task(task)) {
+ /*
+ * Racing with the completion path right now, so give it more
+ * time so that path can complete it like normal.
+ */
+ rc = SCSI_EH_RESET_TIMER;
+ task = NULL;
+ spin_unlock(&session->back_lock);
+ goto done;
+ }
+ spin_unlock(&session->back_lock);
+
if (session->state != ISCSI_STATE_LOGGED_IN) {
/*
+ * During shutdown, if session is prematurely disconnected,
+ * recovery won't happen and there will be hung cmds. Not
+ * handling cmds would trigger EH, also bad in this case.
+ * Instead, handle cmd, allow completion to happen and let
+ * upper layer to deal with the result.
+ */
+ if (unlikely(system_state != SYSTEM_RUNNING)) {
+ sc->result = DID_NO_CONNECT << 16;
+ ISCSI_DBG_EH(session, "sc on shutdown, handled\n");
+ rc = SCSI_EH_NOT_HANDLED;
+ goto done;
+ }
+ /*
* We are probably in the middle of iscsi recovery so let
* that complete and handle the error.
*/
- rc = BLK_EH_RESET_TIMER;
+ rc = SCSI_EH_RESET_TIMER;
goto done;
}
conn = session->leadconn;
if (!conn) {
/* In the middle of shuting down */
- rc = BLK_EH_RESET_TIMER;
- goto done;
- }
-
- task = (struct iscsi_task *)sc->SCp.ptr;
- if (!task) {
- /*
- * Raced with completion. Just reset timer, and let it
- * complete normally
- */
- rc = BLK_EH_RESET_TIMER;
+ rc = SCSI_EH_RESET_TIMER;
goto done;
}
@@ -1963,7 +2152,7 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
"Last data xfer at %lu. Last timeout was at "
"%lu\n.", task->last_xfer, task->last_timeout);
task->have_checked_conn = false;
- rc = BLK_EH_RESET_TIMER;
+ rc = SCSI_EH_RESET_TIMER;
goto done;
}
@@ -1974,10 +2163,11 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
* and can let the iscsi eh handle it
*/
if (iscsi_has_ping_timed_out(conn)) {
- rc = BLK_EH_RESET_TIMER;
+ rc = SCSI_EH_RESET_TIMER;
goto done;
}
+ spin_lock(&session->back_lock);
for (i = 0; i < conn->session->cmds_max; i++) {
running_task = conn->session->cmds[i];
if (!running_task->sc || running_task == task ||
@@ -2010,10 +2200,12 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
"last xfer %lu/%lu. Last check %lu.\n",
task->last_xfer, running_task->last_xfer,
task->last_timeout);
- rc = BLK_EH_RESET_TIMER;
+ spin_unlock(&session->back_lock);
+ rc = SCSI_EH_RESET_TIMER;
goto done;
}
}
+ spin_unlock(&session->back_lock);
/* Assumes nop timeout is shorter than scsi cmd timeout */
if (task->have_checked_conn)
@@ -2023,33 +2215,37 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
* Checking the transport already or nop from a cmd timeout still
* running
*/
- if (conn->ping_task) {
+ if (READ_ONCE(conn->ping_task)) {
task->have_checked_conn = true;
- rc = BLK_EH_RESET_TIMER;
+ rc = SCSI_EH_RESET_TIMER;
goto done;
}
/* Make sure there is a transport check done */
iscsi_send_nopout(conn, NULL);
task->have_checked_conn = true;
- rc = BLK_EH_RESET_TIMER;
+ rc = SCSI_EH_RESET_TIMER;
done:
- if (task)
+ spin_unlock_bh(&session->frwd_lock);
+
+ if (task) {
task->last_timeout = jiffies;
- spin_unlock(&session->lock);
- ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ?
- "timer reset" : "nh");
+ iscsi_put_task(task);
+ }
+ ISCSI_DBG_EH(session, "return %s\n", rc == SCSI_EH_RESET_TIMER ?
+ "timer reset" : "shutdown or nh");
return rc;
}
+EXPORT_SYMBOL_GPL(iscsi_eh_cmd_timed_out);
-static void iscsi_check_transport_timeouts(unsigned long data)
+static void iscsi_check_transport_timeouts(struct timer_list *t)
{
- struct iscsi_conn *conn = (struct iscsi_conn *)data;
+ struct iscsi_conn *conn = timer_container_of(conn, t, transport_timer);
struct iscsi_session *session = conn->session;
unsigned long recv_timeout, next_timeout = 0, last_recv;
- spin_lock(&session->lock);
+ spin_lock(&session->frwd_lock);
if (session->state != ISCSI_STATE_LOGGED_IN)
goto done;
@@ -2066,25 +2262,74 @@ static void iscsi_check_transport_timeouts(unsigned long data)
"last ping %lu, now %lu\n",
conn->ping_timeout, conn->recv_timeout,
last_recv, conn->last_ping, jiffies);
- spin_unlock(&session->lock);
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ spin_unlock(&session->frwd_lock);
+ iscsi_conn_failure(conn, ISCSI_ERR_NOP_TIMEDOUT);
return;
}
if (time_before_eq(last_recv + recv_timeout, jiffies)) {
/* send a ping to try to provoke some traffic */
ISCSI_DBG_CONN(conn, "Sending nopout as ping\n");
- iscsi_send_nopout(conn, NULL);
- next_timeout = conn->last_ping + (conn->ping_timeout * HZ);
+ if (iscsi_send_nopout(conn, NULL))
+ next_timeout = jiffies + (1 * HZ);
+ else
+ next_timeout = conn->last_ping + (conn->ping_timeout * HZ);
} else
next_timeout = last_recv + recv_timeout;
ISCSI_DBG_CONN(conn, "Setting next tmo %lu\n", next_timeout);
mod_timer(&conn->transport_timer, next_timeout);
done:
- spin_unlock(&session->lock);
+ spin_unlock(&session->frwd_lock);
}
+/**
+ * iscsi_conn_unbind - prevent queueing to conn.
+ * @cls_conn: iscsi conn ep is bound to.
+ * @is_active: is the conn in use for boot or is this for EH/termination
+ *
+ * This must be called by drivers implementing the ep_disconnect callout.
+ * It disables queueing to the connection from libiscsi in preparation for
+ * an ep_disconnect call.
+ */
+void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active)
+{
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+
+ if (!cls_conn)
+ return;
+
+ conn = cls_conn->dd_data;
+ session = conn->session;
+ /*
+ * Wait for iscsi_eh calls to exit. We don't wait for the tmf to
+ * complete or timeout. The caller just wants to know what's running
+ * is everything that needs to be cleaned up, and no cmds will be
+ * queued.
+ */
+ mutex_lock(&session->eh_mutex);
+
+ iscsi_suspend_queue(conn);
+ iscsi_suspend_tx(conn);
+
+ spin_lock_bh(&session->frwd_lock);
+ clear_bit(ISCSI_CONN_FLAG_BOUND, &conn->flags);
+
+ if (!is_active) {
+ /*
+ * if logout timed out before userspace could even send a PDU
+ * the state might still be in ISCSI_STATE_LOGGED_IN and
+ * allowing new cmds and TMFs.
+ */
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+ iscsi_set_conn_failed(conn);
+ }
+ spin_unlock_bh(&session->frwd_lock);
+ mutex_unlock(&session->eh_mutex);
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_unbind);
+
static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
struct iscsi_tm *hdr)
{
@@ -2092,7 +2337,7 @@ static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- memcpy(hdr->lun, task->lun, sizeof(hdr->lun));
+ hdr->lun = task->lun;
hdr->rtt = task->hdr_itt;
hdr->refcmdsn = task->cmdsn;
}
@@ -2104,23 +2349,24 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
struct iscsi_conn *conn;
struct iscsi_task *task;
struct iscsi_tm *hdr;
- int rc, age;
+ int age;
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
ISCSI_DBG_EH(session, "aborting sc %p\n", sc);
+completion_check:
mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
/*
* if session was ISCSI_STATE_IN_RECOVERY then we may not have
* got the command.
*/
- if (!sc->SCp.ptr) {
+ if (!iscsi_cmd(sc)->task) {
ISCSI_DBG_EH(session, "sc never reached iscsi layer or "
"it completed.\n");
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
return SUCCESS;
}
@@ -2130,49 +2376,61 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
* then let the host reset code handle this
*/
if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN ||
- sc->SCp.phase != session->age) {
- spin_unlock_bh(&session->lock);
+ iscsi_cmd(sc)->age != session->age) {
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
ISCSI_DBG_EH(session, "failing abort due to dropped "
"session.\n");
return FAILED;
}
- conn = session->leadconn;
- conn->eh_abort_cnt++;
- age = session->age;
+ spin_lock(&session->back_lock);
+ task = iscsi_cmd(sc)->task;
+ if (!task || !task->sc) {
+ /* task completed before time out */
+ ISCSI_DBG_EH(session, "sc completed while abort in progress\n");
- task = (struct iscsi_task *)sc->SCp.ptr;
- ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n",
- sc, task->itt);
+ spin_unlock(&session->back_lock);
+ spin_unlock_bh(&session->frwd_lock);
+ mutex_unlock(&session->eh_mutex);
+ return SUCCESS;
+ }
- /* task completed before time out */
- if (!task->sc) {
- ISCSI_DBG_EH(session, "sc completed while abort in progress\n");
- goto success;
+ if (!iscsi_get_task(task)) {
+ spin_unlock(&session->back_lock);
+ spin_unlock_bh(&session->frwd_lock);
+ mutex_unlock(&session->eh_mutex);
+ /* We are just about to call iscsi_free_task so wait for it. */
+ udelay(ISCSI_CMD_COMPL_WAIT);
+ goto completion_check;
}
+ ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt);
+ conn = session->leadconn;
+ iscsi_get_conn(conn->cls_conn);
+ conn->eh_abort_cnt++;
+ age = session->age;
+ spin_unlock(&session->back_lock);
+
if (task->state == ISCSI_TASK_PENDING) {
fail_scsi_task(task, DID_ABORT);
goto success;
}
/* only have one tmf outstanding at a time */
- if (conn->tmf_state != TMF_INITIAL)
+ if (session->tmf_state != TMF_INITIAL)
goto failed;
- conn->tmf_state = TMF_QUEUED;
+ session->tmf_state = TMF_QUEUED;
- hdr = &conn->tmhdr;
+ hdr = &session->tmhdr;
iscsi_prep_abort_task_pdu(task, hdr);
- if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
- rc = FAILED;
+ if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout))
goto failed;
- }
- switch (conn->tmf_state) {
+ switch (session->tmf_state) {
case TMF_SUCCESS:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
/*
* stop tx side incase the target had sent a abort rsp but
* the initiator was still writing out data.
@@ -2183,45 +2441,57 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
* good and have never sent us a successful tmf response
* then sent more data for the cmd.
*/
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
fail_scsi_task(task, DID_ABORT);
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
memset(hdr, 0, sizeof(*hdr));
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
iscsi_start_tx(conn);
goto success_unlocked;
case TMF_TIMEDOUT:
- spin_unlock_bh(&session->lock);
+ session->running_aborted_task = task;
+ spin_unlock_bh(&session->frwd_lock);
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
goto failed_unlocked;
case TMF_NOT_FOUND:
- if (!sc->SCp.ptr) {
- conn->tmf_state = TMF_INITIAL;
+ if (iscsi_task_is_completed(task)) {
+ session->tmf_state = TMF_INITIAL;
memset(hdr, 0, sizeof(*hdr));
/* task completed before tmf abort response */
ISCSI_DBG_EH(session, "sc completed while abort in "
"progress\n");
goto success;
}
- /* fall through */
+ fallthrough;
default:
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
goto failed;
}
success:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
success_unlocked:
ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n",
sc, task->itt);
+ iscsi_put_task(task);
+ iscsi_put_conn(conn->cls_conn);
mutex_unlock(&session->eh_mutex);
return SUCCESS;
failed:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
failed_unlocked:
ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc,
task ? task->itt : 0);
+ /*
+ * The driver might be accessing the task so hold the ref. The conn
+ * stop cleanup will drop the ref after ep_disconnect so we know the
+ * driver's no longer touching the task.
+ */
+ if (!session->running_aborted_task)
+ iscsi_put_task(task);
+
+ iscsi_put_conn(conn->cls_conn);
mutex_unlock(&session->eh_mutex);
return FAILED;
}
@@ -2233,7 +2503,7 @@ static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
+ int_to_scsilun(sc->device->lun, &hdr->lun);
hdr->rtt = RESERVED_ITT;
}
@@ -2248,10 +2518,11 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
- ISCSI_DBG_EH(session, "LU Reset [sc %p lun %u]\n", sc, sc->device->lun);
+ ISCSI_DBG_EH(session, "LU Reset [sc %p lun %llu]\n", sc,
+ sc->device->lun);
mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
/*
* Just check if we are not logged in. We cannot check for
* the phase because the reset could come from a ioctl.
@@ -2261,11 +2532,11 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
conn = session->leadconn;
/* only have one tmf outstanding at a time */
- if (conn->tmf_state != TMF_INITIAL)
+ if (session->tmf_state != TMF_INITIAL)
goto unlock;
- conn->tmf_state = TMF_QUEUED;
+ session->tmf_state = TMF_QUEUED;
- hdr = &conn->tmhdr;
+ hdr = &session->tmhdr;
iscsi_prep_lun_reset_pdu(sc, hdr);
if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
@@ -2274,34 +2545,34 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
goto unlock;
}
- switch (conn->tmf_state) {
+ switch (session->tmf_state) {
case TMF_SUCCESS:
break;
case TMF_TIMEDOUT:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
goto done;
default:
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
goto unlock;
}
rc = SUCCESS;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
iscsi_suspend_tx(conn);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
memset(hdr, 0, sizeof(*hdr));
fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
- conn->tmf_state = TMF_INITIAL;
- spin_unlock_bh(&session->lock);
+ session->tmf_state = TMF_INITIAL;
+ spin_unlock_bh(&session->frwd_lock);
iscsi_start_tx(conn);
goto done;
unlock:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
done:
ISCSI_DBG_EH(session, "dev reset result = %s\n",
rc == SUCCESS ? "SUCCESS" : "FAILED");
@@ -2314,13 +2585,12 @@ void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
{
struct iscsi_session *session = cls_session->dd_data;
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
if (session->state != ISCSI_STATE_LOGGED_IN) {
session->state = ISCSI_STATE_RECOVERY_FAILED;
- if (session->leadconn)
- wake_up(&session->leadconn->ehwait);
+ wake_up(&session->ehwait);
}
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
}
EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
@@ -2339,31 +2609,31 @@ int iscsi_eh_session_reset(struct scsi_cmnd *sc)
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
- conn = session->leadconn;
mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
if (session->state == ISCSI_STATE_TERMINATE) {
failed:
ISCSI_DBG_EH(session,
"failing session reset: Could not log back into "
- "%s, %s [age %d]\n", session->targetname,
- conn->persistent_address, session->age);
- spin_unlock_bh(&session->lock);
+ "%s [age %d]\n", session->targetname,
+ session->age);
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
return FAILED;
}
- spin_unlock_bh(&session->lock);
+ conn = session->leadconn;
+ iscsi_get_conn(conn->cls_conn);
+
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
- /*
- * we drop the lock here but the leadconn cannot be destoyed while
- * we are in the scsi eh
- */
+
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
+ iscsi_put_conn(conn->cls_conn);
ISCSI_DBG_EH(session, "wait for relogin\n");
- wait_event_interruptible(conn->ehwait,
+ wait_event_interruptible(session->ehwait,
session->state == ISCSI_STATE_TERMINATE ||
session->state == ISCSI_STATE_LOGGED_IN ||
session->state == ISCSI_STATE_RECOVERY_FAILED);
@@ -2371,14 +2641,14 @@ failed:
flush_signals(current);
mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
if (session->state == ISCSI_STATE_LOGGED_IN) {
ISCSI_DBG_EH(session,
"session reset succeeded for %s,%s\n",
session->targetname, conn->persistent_address);
} else
goto failed;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
return SUCCESS;
}
@@ -2399,7 +2669,7 @@ static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
*
* This will attempt to send a warm target reset.
*/
-int iscsi_eh_target_reset(struct scsi_cmnd *sc)
+static int iscsi_eh_target_reset(struct scsi_cmnd *sc)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
@@ -2414,7 +2684,7 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc)
session->targetname);
mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
/*
* Just check if we are not logged in. We cannot check for
* the phase because the reset could come from a ioctl.
@@ -2424,11 +2694,11 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc)
conn = session->leadconn;
/* only have one tmf outstanding at a time */
- if (conn->tmf_state != TMF_INITIAL)
+ if (session->tmf_state != TMF_INITIAL)
goto unlock;
- conn->tmf_state = TMF_QUEUED;
+ session->tmf_state = TMF_QUEUED;
- hdr = &conn->tmhdr;
+ hdr = &session->tmhdr;
iscsi_prep_tgt_reset_pdu(sc, hdr);
if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
@@ -2437,41 +2707,40 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc)
goto unlock;
}
- switch (conn->tmf_state) {
+ switch (session->tmf_state) {
case TMF_SUCCESS:
break;
case TMF_TIMEDOUT:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
goto done;
default:
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
goto unlock;
}
rc = SUCCESS;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
iscsi_suspend_tx(conn);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
memset(hdr, 0, sizeof(*hdr));
fail_scsi_tasks(conn, -1, DID_ERROR);
- conn->tmf_state = TMF_INITIAL;
- spin_unlock_bh(&session->lock);
+ session->tmf_state = TMF_INITIAL;
+ spin_unlock_bh(&session->frwd_lock);
iscsi_start_tx(conn);
goto done;
unlock:
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
done:
ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname,
rc == SUCCESS ? "SUCCESS" : "FAILED");
mutex_unlock(&session->eh_mutex);
return rc;
}
-EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
/**
* iscsi_eh_recover_target - reset target and possibly the session
@@ -2510,7 +2779,7 @@ iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size)
* the array. */
if (items)
num_arrays++;
- q->pool = kzalloc(num_arrays * max * sizeof(void*), GFP_KERNEL);
+ q->pool = kvcalloc(num_arrays * max, sizeof(void *), GFP_KERNEL);
if (q->pool == NULL)
return -ENOMEM;
@@ -2544,10 +2813,60 @@ void iscsi_pool_free(struct iscsi_pool *q)
for (i = 0; i < q->max; i++)
kfree(q->pool[i]);
- kfree(q->pool);
+ kvfree(q->pool);
}
EXPORT_SYMBOL_GPL(iscsi_pool_free);
+int iscsi_host_get_max_scsi_cmds(struct Scsi_Host *shost,
+ uint16_t requested_cmds_max)
+{
+ int scsi_cmds, total_cmds = requested_cmds_max;
+
+check:
+ if (!total_cmds)
+ total_cmds = ISCSI_DEF_XMIT_CMDS_MAX;
+ /*
+ * The iscsi layer needs some tasks for nop handling and tmfs,
+ * so the cmds_max must at least be greater than ISCSI_MGMT_CMDS_MAX
+ * + 1 command for scsi IO.
+ */
+ if (total_cmds < ISCSI_TOTAL_CMDS_MIN) {
+ printk(KERN_ERR "iscsi: invalid max cmds of %d. Must be a power of two that is at least %d.\n",
+ total_cmds, ISCSI_TOTAL_CMDS_MIN);
+ return -EINVAL;
+ }
+
+ if (total_cmds > ISCSI_TOTAL_CMDS_MAX) {
+ printk(KERN_INFO "iscsi: invalid max cmds of %d. Must be a power of 2 less than or equal to %d. Using %d.\n",
+ requested_cmds_max, ISCSI_TOTAL_CMDS_MAX,
+ ISCSI_TOTAL_CMDS_MAX);
+ total_cmds = ISCSI_TOTAL_CMDS_MAX;
+ }
+
+ if (!is_power_of_2(total_cmds)) {
+ total_cmds = rounddown_pow_of_two(total_cmds);
+ if (total_cmds < ISCSI_TOTAL_CMDS_MIN) {
+ printk(KERN_ERR "iscsi: invalid max cmds of %d. Must be a power of 2 greater than %d.\n", requested_cmds_max, ISCSI_TOTAL_CMDS_MIN);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "iscsi: invalid max cmds %d. Must be a power of 2. Rounding max cmds down to %d.\n",
+ requested_cmds_max, total_cmds);
+ }
+
+ scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX;
+ if (shost->can_queue && scsi_cmds > shost->can_queue) {
+ total_cmds = shost->can_queue;
+
+ printk(KERN_INFO "iscsi: requested max cmds %u is higher than driver limit. Using driver limit %u\n",
+ requested_cmds_max, shost->can_queue);
+ goto check;
+ }
+
+ return scsi_cmds;
+}
+EXPORT_SYMBOL_GPL(iscsi_host_get_max_scsi_cmds);
+
/**
* iscsi_host_add - add host to system
* @shost: scsi host
@@ -2564,8 +2883,6 @@ int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev)
if (!shost->cmd_per_lun)
shost->cmd_per_lun = ISCSI_DEF_CMD_PER_LUN;
- if (!shost->transportt->eh_timed_out)
- shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
return scsi_add_host(shost, pdev);
}
EXPORT_SYMBOL_GPL(iscsi_host_add);
@@ -2579,7 +2896,7 @@ EXPORT_SYMBOL_GPL(iscsi_host_add);
* This should be called by partial offload and software iscsi drivers.
* To access the driver specific memory use the iscsi_host_priv() macro.
*/
-struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
+struct Scsi_Host *iscsi_host_alloc(const struct scsi_host_template *sht,
int dd_data_size, bool xmit_can_sleep)
{
struct Scsi_Host *shost;
@@ -2591,9 +2908,9 @@ struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
ihost = shost_priv(shost);
if (xmit_can_sleep) {
- snprintf(ihost->workq_name, sizeof(ihost->workq_name),
- "iscsi_q_%d", shost->host_no);
- ihost->workq = create_singlethread_workqueue(ihost->workq_name);
+ ihost->workq = alloc_workqueue("iscsi_q_%d",
+ WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND,
+ 1, shost->host_no);
if (!ihost->workq)
goto free_host;
}
@@ -2618,11 +2935,12 @@ static void iscsi_notify_host_removed(struct iscsi_cls_session *cls_session)
/**
* iscsi_host_remove - remove host and sessions
* @shost: scsi host
+ * @is_shutdown: true if called from a driver shutdown callout
*
* If there are any sessions left, this will initiate the removal and wait
* for the completion.
*/
-void iscsi_host_remove(struct Scsi_Host *shost)
+void iscsi_host_remove(struct Scsi_Host *shost, bool is_shutdown)
{
struct iscsi_host *ihost = shost_priv(shost);
unsigned long flags;
@@ -2631,15 +2949,17 @@ void iscsi_host_remove(struct Scsi_Host *shost)
ihost->state = ISCSI_HOST_REMOVED;
spin_unlock_irqrestore(&ihost->lock, flags);
- iscsi_host_for_each_session(shost, iscsi_notify_host_removed);
+ if (!is_shutdown)
+ iscsi_host_for_each_session(shost, iscsi_notify_host_removed);
+ else
+ iscsi_host_for_each_session(shost, iscsi_force_destroy_session);
+
wait_event_interruptible(ihost->session_removal_wq,
ihost->num_sessions == 0);
if (signal_pending(current))
flush_signals(current);
scsi_remove_host(shost);
- if (ihost->workq)
- destroy_workqueue(ihost->workq);
}
EXPORT_SYMBOL_GPL(iscsi_host_remove);
@@ -2647,6 +2967,9 @@ void iscsi_host_free(struct Scsi_Host *shost)
{
struct iscsi_host *ihost = shost_priv(shost);
+ if (ihost->workq)
+ destroy_workqueue(ihost->workq);
+
kfree(ihost->netdev);
kfree(ihost->hwaddress);
kfree(ihost->initiatorname);
@@ -2680,8 +3003,10 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost)
* @iscsit: iscsi transport template
* @shost: scsi host
* @cmds_max: session can queue
+ * @dd_size: private driver data size, added to session allocation size
* @cmd_task_size: LLD task private data size
* @initial_cmdsn: initial CmdSN
+ * @id: target ID to add to this session
*
* This can be used by software iscsi_transports that allocate
* a session per scsi host.
@@ -2698,7 +3023,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
struct iscsi_host *ihost = shost_priv(shost);
struct iscsi_session *session;
struct iscsi_cls_session *cls_session;
- int cmd_i, scsi_cmds, total_cmds = cmds_max;
+ int cmd_i, scsi_cmds;
unsigned long flags;
spin_lock_irqsave(&ihost->lock, flags);
@@ -2709,37 +3034,9 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
ihost->num_sessions++;
spin_unlock_irqrestore(&ihost->lock, flags);
- if (!total_cmds)
- total_cmds = ISCSI_DEF_XMIT_CMDS_MAX;
- /*
- * The iscsi layer needs some tasks for nop handling and tmfs,
- * so the cmds_max must at least be greater than ISCSI_MGMT_CMDS_MAX
- * + 1 command for scsi IO.
- */
- if (total_cmds < ISCSI_TOTAL_CMDS_MIN) {
- printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
- "must be a power of two that is at least %d.\n",
- total_cmds, ISCSI_TOTAL_CMDS_MIN);
+ scsi_cmds = iscsi_host_get_max_scsi_cmds(shost, cmds_max);
+ if (scsi_cmds < 0)
goto dec_session_count;
- }
-
- if (total_cmds > ISCSI_TOTAL_CMDS_MAX) {
- printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
- "must be a power of 2 less than or equal to %d.\n",
- cmds_max, ISCSI_TOTAL_CMDS_MAX);
- total_cmds = ISCSI_TOTAL_CMDS_MAX;
- }
-
- if (!is_power_of_2(total_cmds)) {
- printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
- "must be a power of 2.\n", total_cmds);
- total_cmds = rounddown_pow_of_two(total_cmds);
- if (total_cmds < ISCSI_TOTAL_CMDS_MIN)
- return NULL;
- printk(KERN_INFO "iscsi: Rounding can_queue to %d.\n",
- total_cmds);
- }
- scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX;
cls_session = iscsi_alloc_session(shost, iscsit,
sizeof(struct iscsi_session) +
@@ -2755,15 +3052,21 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
session->lu_reset_timeout = 15;
session->abort_timeout = 10;
session->scsi_cmds_max = scsi_cmds;
- session->cmds_max = total_cmds;
+ session->cmds_max = scsi_cmds + ISCSI_MGMT_CMDS_MAX;
session->queued_cmdsn = session->cmdsn = initial_cmdsn;
session->exp_cmdsn = initial_cmdsn + 1;
session->max_cmdsn = initial_cmdsn + 1;
session->max_r2t = 1;
session->tt = iscsit;
session->dd_data = cls_session->dd_data + sizeof(*session);
+
+ session->tmf_state = TMF_INITIAL;
+ timer_setup(&session->tmf_timer, iscsi_tmf_timedout, 0);
mutex_init(&session->eh_mutex);
- spin_lock_init(&session->lock);
+ init_waitqueue_head(&session->ehwait);
+
+ spin_lock_init(&session->frwd_lock);
+ spin_lock_init(&session->back_lock);
/* initialize SCSI PDU commands pool */
if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
@@ -2802,33 +3105,62 @@ dec_session_count:
}
EXPORT_SYMBOL_GPL(iscsi_session_setup);
+/*
+ * issi_session_remove - Remove session from iSCSI class.
+ */
+void iscsi_session_remove(struct iscsi_cls_session *cls_session)
+{
+ struct iscsi_session *session = cls_session->dd_data;
+ struct Scsi_Host *shost = session->host;
+
+ iscsi_remove_session(cls_session);
+ /*
+ * host removal only has to wait for its children to be removed from
+ * sysfs, and iscsi_tcp needs to do iscsi_host_remove before freeing
+ * the session, so drop the session count here.
+ */
+ iscsi_host_dec_session_cnt(shost);
+}
+EXPORT_SYMBOL_GPL(iscsi_session_remove);
+
/**
- * iscsi_session_teardown - destroy session, host, and cls_session
+ * iscsi_session_free - Free iscsi session and it's resources
* @cls_session: iscsi session
- *
- * The driver must have called iscsi_remove_session before
- * calling this.
*/
-void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+void iscsi_session_free(struct iscsi_cls_session *cls_session)
{
struct iscsi_session *session = cls_session->dd_data;
struct module *owner = cls_session->transport->owner;
- struct Scsi_Host *shost = session->host;
iscsi_pool_free(&session->cmdpool);
-
kfree(session->password);
kfree(session->password_in);
kfree(session->username);
kfree(session->username_in);
kfree(session->targetname);
+ kfree(session->targetalias);
kfree(session->initiatorname);
+ kfree(session->boot_root);
+ kfree(session->boot_nic);
+ kfree(session->boot_target);
kfree(session->ifacename);
+ kfree(session->portal_type);
+ kfree(session->discovery_parent_type);
- iscsi_destroy_session(cls_session);
- iscsi_host_dec_session_cnt(shost);
+ iscsi_free_session(cls_session);
module_put(owner);
}
+EXPORT_SYMBOL_GPL(iscsi_session_free);
+
+/**
+ * iscsi_session_teardown - destroy session and cls_session
+ * @cls_session: iscsi session
+ */
+void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+{
+ iscsi_session_remove(cls_session);
+ iscsi_session_free(cls_session);
+}
EXPORT_SYMBOL_GPL(iscsi_session_teardown);
/**
@@ -2845,25 +3177,23 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
struct iscsi_conn *conn;
struct iscsi_cls_conn *cls_conn;
char *data;
+ int err;
- cls_conn = iscsi_create_conn(cls_session, sizeof(*conn) + dd_size,
+ cls_conn = iscsi_alloc_conn(cls_session, sizeof(*conn) + dd_size,
conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
- memset(conn, 0, sizeof(*conn) + dd_size);
- conn->dd_data = cls_conn->dd_data + sizeof(*conn);
+ if (dd_size)
+ conn->dd_data = cls_conn->dd_data + sizeof(*conn);
conn->session = session;
conn->cls_conn = cls_conn;
conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
conn->id = conn_idx;
conn->exp_statsn = 0;
- conn->tmf_state = TMF_INITIAL;
- init_timer(&conn->transport_timer);
- conn->transport_timer.data = (unsigned long)conn;
- conn->transport_timer.function = iscsi_check_transport_timeouts;
+ timer_setup(&conn->transport_timer, iscsi_check_transport_timeouts, 0);
INIT_LIST_HEAD(&conn->mgmtqueue);
INIT_LIST_HEAD(&conn->cmdqueue);
@@ -2871,14 +3201,14 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
/* allocate login_task used for the login/text sequences */
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
if (!kfifo_out(&session->cmdpool.queue,
(void*)&conn->login_task,
sizeof(void*))) {
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
goto login_task_alloc_fail;
}
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
data = (char *) __get_free_pages(GFP_KERNEL,
get_order(ISCSI_DEF_MAX_RECV_SEG_LEN));
@@ -2886,23 +3216,28 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
goto login_task_data_alloc_fail;
conn->login_task->data = conn->data = data;
- init_timer(&conn->tmf_timer);
- init_waitqueue_head(&conn->ehwait);
+ err = iscsi_add_conn(cls_conn);
+ if (err)
+ goto login_task_add_dev_fail;
return cls_conn;
+login_task_add_dev_fail:
+ free_pages((unsigned long) conn->data,
+ get_order(ISCSI_DEF_MAX_RECV_SEG_LEN));
+
login_task_data_alloc_fail:
kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task,
sizeof(void*));
login_task_alloc_fail:
- iscsi_destroy_conn(cls_conn);
+ iscsi_put_conn(cls_conn);
return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_conn_setup);
/**
* iscsi_conn_teardown - teardown iscsi connection
- * cls_conn: iscsi class connection
+ * @cls_conn: iscsi class connection
*
* TODO: we may need to make this into a two step process
* like scsi-mls remove + put host
@@ -2911,57 +3246,42 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
- unsigned long flags;
- del_timer_sync(&conn->transport_timer);
+ iscsi_remove_conn(cls_conn);
- spin_lock_bh(&session->lock);
+ timer_delete_sync(&conn->transport_timer);
+
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->frwd_lock);
conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
if (session->leadconn == conn) {
/*
* leading connection? then give up on recovery.
*/
session->state = ISCSI_STATE_TERMINATE;
- wake_up(&conn->ehwait);
- }
- spin_unlock_bh(&session->lock);
-
- /*
- * Block until all in-progress commands for this connection
- * time out or fail.
- */
- for (;;) {
- spin_lock_irqsave(session->host->host_lock, flags);
- if (!session->host->host_busy) { /* OK for ERL == 0 */
- spin_unlock_irqrestore(session->host->host_lock, flags);
- break;
- }
- spin_unlock_irqrestore(session->host->host_lock, flags);
- msleep_interruptible(500);
- iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): "
- "host_busy %d host_failed %d\n",
- session->host->host_busy,
- session->host->host_failed);
- /*
- * force eh_abort() to unblock
- */
- wake_up(&conn->ehwait);
+ wake_up(&session->ehwait);
}
+ spin_unlock_bh(&session->frwd_lock);
/* flush queued up work because we free the connection below */
iscsi_suspend_tx(conn);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
free_pages((unsigned long) conn->data,
get_order(ISCSI_DEF_MAX_RECV_SEG_LEN));
kfree(conn->persistent_address);
+ kfree(conn->local_ipaddr);
+ /* regular RX path uses back_lock */
+ spin_lock_bh(&session->back_lock);
kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task,
sizeof(void*));
+ spin_unlock_bh(&session->back_lock);
if (session->leadconn == conn)
session->leadconn = NULL;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
+ mutex_unlock(&session->eh_mutex);
- iscsi_destroy_conn(cls_conn);
+ iscsi_put_conn(cls_conn);
}
EXPORT_SYMBOL_GPL(iscsi_conn_teardown);
@@ -2996,7 +3316,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
conn->ping_timeout = 5;
}
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
conn->c_stage = ISCSI_CONN_STARTED;
session->state = ISCSI_STATE_LOGGED_IN;
session->queued_cmdsn = session->cmdsn;
@@ -3014,7 +3334,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
* commands after successful recovery
*/
conn->stop_stage = 0;
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
session->age++;
if (session->age == 16)
session->age = 0;
@@ -3025,10 +3345,10 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
default:
break;
}
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
iscsi_unblock_session(session->cls_session);
- wake_up(&conn->ehwait);
+ wake_up(&session->ehwait);
return 0;
}
EXPORT_SYMBOL_GPL(iscsi_conn_start);
@@ -3050,23 +3370,31 @@ fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn)
ISCSI_DBG_SESSION(conn->session,
"failing mgmt itt 0x%x state %d\n",
task->itt, task->state);
+
+ spin_lock_bh(&session->back_lock);
+ if (cleanup_queued_task(task)) {
+ spin_unlock_bh(&session->back_lock);
+ continue;
+ }
+
state = ISCSI_TASK_ABRT_SESS_RECOV;
if (task->state == ISCSI_TASK_PENDING)
state = ISCSI_TASK_COMPLETED;
iscsi_complete_task(task, state);
-
+ spin_unlock_bh(&session->back_lock);
}
}
-static void iscsi_start_session_recovery(struct iscsi_session *session,
- struct iscsi_conn *conn, int flag)
+void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
int old_stop_stage;
mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
if (conn->stop_stage == STOP_CONN_TERM) {
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
return;
}
@@ -3083,14 +3411,14 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
old_stop_stage = conn->stop_stage;
conn->stop_stage = flag;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
- del_timer_sync(&conn->transport_timer);
+ timer_delete_sync(&conn->transport_timer);
iscsi_suspend_tx(conn);
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
conn->c_stage = ISCSI_CONN_STOPPED;
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&session->frwd_lock);
/*
* for connection level recovery we should not calculate
@@ -3111,29 +3439,13 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
/*
* flush queues.
*/
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED);
fail_mgmt_tasks(session, conn);
- memset(&conn->tmhdr, 0, sizeof(conn->tmhdr));
- spin_unlock_bh(&session->lock);
+ memset(&session->tmhdr, 0, sizeof(session->tmhdr));
+ spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
}
-
-void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
-{
- struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
-
- switch (flag) {
- case STOP_CONN_RECOVER:
- case STOP_CONN_TERM:
- iscsi_start_session_recovery(session, conn, flag);
- break;
- default:
- iscsi_conn_printk(KERN_ERR, conn,
- "invalid stop flag %d\n", flag);
- }
-}
EXPORT_SYMBOL_GPL(iscsi_conn_stop);
int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
@@ -3142,21 +3454,30 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_session *session = cls_session->dd_data;
struct iscsi_conn *conn = cls_conn->dd_data;
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&session->frwd_lock);
if (is_leading)
session->leadconn = conn;
- spin_unlock_bh(&session->lock);
+
+ set_bit(ISCSI_CONN_FLAG_BOUND, &conn->flags);
+ spin_unlock_bh(&session->frwd_lock);
/*
+ * The target could have reduced it's window size between logins, so
+ * we have to reset max/exp cmdsn so we can see the new values.
+ */
+ spin_lock_bh(&session->back_lock);
+ session->max_cmdsn = session->exp_cmdsn = session->cmdsn + 1;
+ spin_unlock_bh(&session->back_lock);
+ /*
* Unblock xmitworker(), Login Phase will pass through.
*/
- clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
- clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ clear_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags);
+ clear_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags);
return 0;
}
EXPORT_SYMBOL_GPL(iscsi_conn_bind);
-static int iscsi_switch_str_param(char **param, char *new_val_buf)
+int iscsi_switch_str_param(char **param, char *new_val_buf)
{
char *new_val;
@@ -3173,13 +3494,14 @@ static int iscsi_switch_str_param(char **param, char *new_val_buf)
*param = new_val;
return 0;
}
+EXPORT_SYMBOL_GPL(iscsi_switch_str_param);
int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
- uint32_t value;
+ int val;
switch(param) {
case ISCSI_PARAM_FAST_ABORT:
@@ -3216,7 +3538,7 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
sscanf(buf, "%d", &session->initial_r2t_en);
break;
case ISCSI_PARAM_MAX_R2T:
- sscanf(buf, "%d", &session->max_r2t);
+ sscanf(buf, "%hu", &session->max_r2t);
break;
case ISCSI_PARAM_IMM_DATA_EN:
sscanf(buf, "%d", &session->imm_data_en);
@@ -3236,14 +3558,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
case ISCSI_PARAM_ERL:
sscanf(buf, "%d", &session->erl);
break;
- case ISCSI_PARAM_IFMARKER_EN:
- sscanf(buf, "%d", &value);
- BUG_ON(value);
- break;
- case ISCSI_PARAM_OFMARKER_EN:
- sscanf(buf, "%d", &value);
- BUG_ON(value);
- break;
case ISCSI_PARAM_EXP_STATSN:
sscanf(buf, "%u", &conn->exp_statsn);
break;
@@ -3257,6 +3571,8 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
return iscsi_switch_str_param(&session->password_in, buf);
case ISCSI_PARAM_TARGET_NAME:
return iscsi_switch_str_param(&session->targetname, buf);
+ case ISCSI_PARAM_TARGET_ALIAS:
+ return iscsi_switch_str_param(&session->targetalias, buf);
case ISCSI_PARAM_TPGT:
sscanf(buf, "%d", &session->tpgt);
break;
@@ -3269,6 +3585,23 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
return iscsi_switch_str_param(&session->ifacename, buf);
case ISCSI_PARAM_INITIATOR_NAME:
return iscsi_switch_str_param(&session->initiatorname, buf);
+ case ISCSI_PARAM_BOOT_ROOT:
+ return iscsi_switch_str_param(&session->boot_root, buf);
+ case ISCSI_PARAM_BOOT_NIC:
+ return iscsi_switch_str_param(&session->boot_nic, buf);
+ case ISCSI_PARAM_BOOT_TARGET:
+ return iscsi_switch_str_param(&session->boot_target, buf);
+ case ISCSI_PARAM_PORTAL_TYPE:
+ return iscsi_switch_str_param(&session->portal_type, buf);
+ case ISCSI_PARAM_DISCOVERY_PARENT_TYPE:
+ return iscsi_switch_str_param(&session->discovery_parent_type,
+ buf);
+ case ISCSI_PARAM_DISCOVERY_SESS:
+ sscanf(buf, "%d", &val);
+ session->discovery_sess = !!val;
+ break;
+ case ISCSI_PARAM_LOCAL_IPADDR:
+ return iscsi_switch_str_param(&conn->local_ipaddr, buf);
default:
return -ENOSYS;
}
@@ -3285,64 +3618,125 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
switch(param) {
case ISCSI_PARAM_FAST_ABORT:
- len = sprintf(buf, "%d\n", session->fast_abort);
+ len = sysfs_emit(buf, "%d\n", session->fast_abort);
break;
case ISCSI_PARAM_ABORT_TMO:
- len = sprintf(buf, "%d\n", session->abort_timeout);
+ len = sysfs_emit(buf, "%d\n", session->abort_timeout);
break;
case ISCSI_PARAM_LU_RESET_TMO:
- len = sprintf(buf, "%d\n", session->lu_reset_timeout);
+ len = sysfs_emit(buf, "%d\n", session->lu_reset_timeout);
break;
case ISCSI_PARAM_TGT_RESET_TMO:
- len = sprintf(buf, "%d\n", session->tgt_reset_timeout);
+ len = sysfs_emit(buf, "%d\n", session->tgt_reset_timeout);
break;
case ISCSI_PARAM_INITIAL_R2T_EN:
- len = sprintf(buf, "%d\n", session->initial_r2t_en);
+ len = sysfs_emit(buf, "%d\n", session->initial_r2t_en);
break;
case ISCSI_PARAM_MAX_R2T:
- len = sprintf(buf, "%hu\n", session->max_r2t);
+ len = sysfs_emit(buf, "%hu\n", session->max_r2t);
break;
case ISCSI_PARAM_IMM_DATA_EN:
- len = sprintf(buf, "%d\n", session->imm_data_en);
+ len = sysfs_emit(buf, "%d\n", session->imm_data_en);
break;
case ISCSI_PARAM_FIRST_BURST:
- len = sprintf(buf, "%u\n", session->first_burst);
+ len = sysfs_emit(buf, "%u\n", session->first_burst);
break;
case ISCSI_PARAM_MAX_BURST:
- len = sprintf(buf, "%u\n", session->max_burst);
+ len = sysfs_emit(buf, "%u\n", session->max_burst);
break;
case ISCSI_PARAM_PDU_INORDER_EN:
- len = sprintf(buf, "%d\n", session->pdu_inorder_en);
+ len = sysfs_emit(buf, "%d\n", session->pdu_inorder_en);
break;
case ISCSI_PARAM_DATASEQ_INORDER_EN:
- len = sprintf(buf, "%d\n", session->dataseq_inorder_en);
+ len = sysfs_emit(buf, "%d\n", session->dataseq_inorder_en);
+ break;
+ case ISCSI_PARAM_DEF_TASKMGMT_TMO:
+ len = sysfs_emit(buf, "%d\n", session->def_taskmgmt_tmo);
break;
case ISCSI_PARAM_ERL:
- len = sprintf(buf, "%d\n", session->erl);
+ len = sysfs_emit(buf, "%d\n", session->erl);
break;
case ISCSI_PARAM_TARGET_NAME:
- len = sprintf(buf, "%s\n", session->targetname);
+ len = sysfs_emit(buf, "%s\n", session->targetname);
+ break;
+ case ISCSI_PARAM_TARGET_ALIAS:
+ len = sysfs_emit(buf, "%s\n", session->targetalias);
break;
case ISCSI_PARAM_TPGT:
- len = sprintf(buf, "%d\n", session->tpgt);
+ len = sysfs_emit(buf, "%d\n", session->tpgt);
break;
case ISCSI_PARAM_USERNAME:
- len = sprintf(buf, "%s\n", session->username);
+ len = sysfs_emit(buf, "%s\n", session->username);
break;
case ISCSI_PARAM_USERNAME_IN:
- len = sprintf(buf, "%s\n", session->username_in);
+ len = sysfs_emit(buf, "%s\n", session->username_in);
break;
case ISCSI_PARAM_PASSWORD:
- len = sprintf(buf, "%s\n", session->password);
+ len = sysfs_emit(buf, "%s\n", session->password);
break;
case ISCSI_PARAM_PASSWORD_IN:
- len = sprintf(buf, "%s\n", session->password_in);
+ len = sysfs_emit(buf, "%s\n", session->password_in);
break;
case ISCSI_PARAM_IFACE_NAME:
- len = sprintf(buf, "%s\n", session->ifacename);
+ len = sysfs_emit(buf, "%s\n", session->ifacename);
break;
case ISCSI_PARAM_INITIATOR_NAME:
- len = sprintf(buf, "%s\n", session->initiatorname);
+ len = sysfs_emit(buf, "%s\n", session->initiatorname);
+ break;
+ case ISCSI_PARAM_BOOT_ROOT:
+ len = sysfs_emit(buf, "%s\n", session->boot_root);
+ break;
+ case ISCSI_PARAM_BOOT_NIC:
+ len = sysfs_emit(buf, "%s\n", session->boot_nic);
+ break;
+ case ISCSI_PARAM_BOOT_TARGET:
+ len = sysfs_emit(buf, "%s\n", session->boot_target);
+ break;
+ case ISCSI_PARAM_AUTO_SND_TGT_DISABLE:
+ len = sysfs_emit(buf, "%u\n", session->auto_snd_tgt_disable);
+ break;
+ case ISCSI_PARAM_DISCOVERY_SESS:
+ len = sysfs_emit(buf, "%u\n", session->discovery_sess);
+ break;
+ case ISCSI_PARAM_PORTAL_TYPE:
+ len = sysfs_emit(buf, "%s\n", session->portal_type);
+ break;
+ case ISCSI_PARAM_CHAP_AUTH_EN:
+ len = sysfs_emit(buf, "%u\n", session->chap_auth_en);
+ break;
+ case ISCSI_PARAM_DISCOVERY_LOGOUT_EN:
+ len = sysfs_emit(buf, "%u\n", session->discovery_logout_en);
+ break;
+ case ISCSI_PARAM_BIDI_CHAP_EN:
+ len = sysfs_emit(buf, "%u\n", session->bidi_chap_en);
+ break;
+ case ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL:
+ len = sysfs_emit(buf, "%u\n", session->discovery_auth_optional);
+ break;
+ case ISCSI_PARAM_DEF_TIME2WAIT:
+ len = sysfs_emit(buf, "%d\n", session->time2wait);
+ break;
+ case ISCSI_PARAM_DEF_TIME2RETAIN:
+ len = sysfs_emit(buf, "%d\n", session->time2retain);
+ break;
+ case ISCSI_PARAM_TSID:
+ len = sysfs_emit(buf, "%u\n", session->tsid);
+ break;
+ case ISCSI_PARAM_ISID:
+ len = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n",
+ session->isid[0], session->isid[1],
+ session->isid[2], session->isid[3],
+ session->isid[4], session->isid[5]);
+ break;
+ case ISCSI_PARAM_DISCOVERY_PARENT_IDX:
+ len = sysfs_emit(buf, "%u\n", session->discovery_parent_idx);
+ break;
+ case ISCSI_PARAM_DISCOVERY_PARENT_TYPE:
+ if (session->discovery_parent_type)
+ len = sysfs_emit(buf, "%s\n",
+ session->discovery_parent_type);
+ else
+ len = sysfs_emit(buf, "\n");
break;
default:
return -ENOSYS;
@@ -3374,15 +3768,16 @@ int iscsi_conn_get_addr_param(struct sockaddr_storage *addr,
case ISCSI_PARAM_CONN_ADDRESS:
case ISCSI_HOST_PARAM_IPADDRESS:
if (sin)
- len = sprintf(buf, "%pI4\n", &sin->sin_addr.s_addr);
+ len = sysfs_emit(buf, "%pI4\n", &sin->sin_addr.s_addr);
else
- len = sprintf(buf, "%pI6\n", &sin6->sin6_addr);
+ len = sysfs_emit(buf, "%pI6\n", &sin6->sin6_addr);
break;
case ISCSI_PARAM_CONN_PORT:
+ case ISCSI_PARAM_LOCAL_PORT:
if (sin)
- len = sprintf(buf, "%hu\n", be16_to_cpu(sin->sin_port));
+ len = sysfs_emit(buf, "%hu\n", be16_to_cpu(sin->sin_port));
else
- len = sprintf(buf, "%hu\n",
+ len = sysfs_emit(buf, "%hu\n",
be16_to_cpu(sin6->sin6_port));
break;
default:
@@ -3401,37 +3796,88 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
switch(param) {
case ISCSI_PARAM_PING_TMO:
- len = sprintf(buf, "%u\n", conn->ping_timeout);
+ len = sysfs_emit(buf, "%u\n", conn->ping_timeout);
break;
case ISCSI_PARAM_RECV_TMO:
- len = sprintf(buf, "%u\n", conn->recv_timeout);
+ len = sysfs_emit(buf, "%u\n", conn->recv_timeout);
break;
case ISCSI_PARAM_MAX_RECV_DLENGTH:
- len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+ len = sysfs_emit(buf, "%u\n", conn->max_recv_dlength);
break;
case ISCSI_PARAM_MAX_XMIT_DLENGTH:
- len = sprintf(buf, "%u\n", conn->max_xmit_dlength);
+ len = sysfs_emit(buf, "%u\n", conn->max_xmit_dlength);
break;
case ISCSI_PARAM_HDRDGST_EN:
- len = sprintf(buf, "%d\n", conn->hdrdgst_en);
+ len = sysfs_emit(buf, "%d\n", conn->hdrdgst_en);
break;
case ISCSI_PARAM_DATADGST_EN:
- len = sprintf(buf, "%d\n", conn->datadgst_en);
+ len = sysfs_emit(buf, "%d\n", conn->datadgst_en);
break;
case ISCSI_PARAM_IFMARKER_EN:
- len = sprintf(buf, "%d\n", conn->ifmarker_en);
+ len = sysfs_emit(buf, "%d\n", conn->ifmarker_en);
break;
case ISCSI_PARAM_OFMARKER_EN:
- len = sprintf(buf, "%d\n", conn->ofmarker_en);
+ len = sysfs_emit(buf, "%d\n", conn->ofmarker_en);
break;
case ISCSI_PARAM_EXP_STATSN:
- len = sprintf(buf, "%u\n", conn->exp_statsn);
+ len = sysfs_emit(buf, "%u\n", conn->exp_statsn);
break;
case ISCSI_PARAM_PERSISTENT_PORT:
- len = sprintf(buf, "%d\n", conn->persistent_port);
+ len = sysfs_emit(buf, "%d\n", conn->persistent_port);
break;
case ISCSI_PARAM_PERSISTENT_ADDRESS:
- len = sprintf(buf, "%s\n", conn->persistent_address);
+ len = sysfs_emit(buf, "%s\n", conn->persistent_address);
+ break;
+ case ISCSI_PARAM_STATSN:
+ len = sysfs_emit(buf, "%u\n", conn->statsn);
+ break;
+ case ISCSI_PARAM_MAX_SEGMENT_SIZE:
+ len = sysfs_emit(buf, "%u\n", conn->max_segment_size);
+ break;
+ case ISCSI_PARAM_KEEPALIVE_TMO:
+ len = sysfs_emit(buf, "%u\n", conn->keepalive_tmo);
+ break;
+ case ISCSI_PARAM_LOCAL_PORT:
+ len = sysfs_emit(buf, "%u\n", conn->local_port);
+ break;
+ case ISCSI_PARAM_TCP_TIMESTAMP_STAT:
+ len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_stat);
+ break;
+ case ISCSI_PARAM_TCP_NAGLE_DISABLE:
+ len = sysfs_emit(buf, "%u\n", conn->tcp_nagle_disable);
+ break;
+ case ISCSI_PARAM_TCP_WSF_DISABLE:
+ len = sysfs_emit(buf, "%u\n", conn->tcp_wsf_disable);
+ break;
+ case ISCSI_PARAM_TCP_TIMER_SCALE:
+ len = sysfs_emit(buf, "%u\n", conn->tcp_timer_scale);
+ break;
+ case ISCSI_PARAM_TCP_TIMESTAMP_EN:
+ len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_en);
+ break;
+ case ISCSI_PARAM_IP_FRAGMENT_DISABLE:
+ len = sysfs_emit(buf, "%u\n", conn->fragment_disable);
+ break;
+ case ISCSI_PARAM_IPV4_TOS:
+ len = sysfs_emit(buf, "%u\n", conn->ipv4_tos);
+ break;
+ case ISCSI_PARAM_IPV6_TC:
+ len = sysfs_emit(buf, "%u\n", conn->ipv6_traffic_class);
+ break;
+ case ISCSI_PARAM_IPV6_FLOW_LABEL:
+ len = sysfs_emit(buf, "%u\n", conn->ipv6_flow_label);
+ break;
+ case ISCSI_PARAM_IS_FW_ASSIGNED_IPV6:
+ len = sysfs_emit(buf, "%u\n", conn->is_fw_assigned_ipv6);
+ break;
+ case ISCSI_PARAM_TCP_XMIT_WSF:
+ len = sysfs_emit(buf, "%u\n", conn->tcp_xmit_wsf);
+ break;
+ case ISCSI_PARAM_TCP_RECV_WSF:
+ len = sysfs_emit(buf, "%u\n", conn->tcp_recv_wsf);
+ break;
+ case ISCSI_PARAM_LOCAL_IPADDR:
+ len = sysfs_emit(buf, "%s\n", conn->local_ipaddr);
break;
default:
return -ENOSYS;
@@ -3449,13 +3895,13 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
switch (param) {
case ISCSI_HOST_PARAM_NETDEV_NAME:
- len = sprintf(buf, "%s\n", ihost->netdev);
+ len = sysfs_emit(buf, "%s\n", ihost->netdev);
break;
case ISCSI_HOST_PARAM_HWADDRESS:
- len = sprintf(buf, "%s\n", ihost->hwaddress);
+ len = sysfs_emit(buf, "%s\n", ihost->hwaddress);
break;
case ISCSI_HOST_PARAM_INITIATOR_NAME:
- len = sprintf(buf, "%s\n", ihost->initiatorname);
+ len = sysfs_emit(buf, "%s\n", ihost->initiatorname);
break;
default:
return -ENOSYS;
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index e98ae33f1295..e90805ba868f 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* iSCSI over TCP/IP Data-Path lib
*
@@ -7,18 +8,6 @@
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
* maintained by open-iscsi@googlegroups.com
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * See the file COPYING included with this distribution for more details.
- *
* Credits:
* Christoph Hellwig
* FUJITA Tomonori
@@ -26,22 +15,24 @@
* Zhenyu Wang
*/
+#include <linux/crc32c.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/inet.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/blkdev.h>
-#include <linux/crypto.h>
#include <linux/delay.h>
#include <linux/kfifo.h>
#include <linux/scatterlist.h>
+#include <linux/module.h>
#include <net/tcp.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi.h>
#include <scsi/scsi_transport_iscsi.h>
+#include <trace/events/iscsi.h>
#include "iscsi_tcp.h"
@@ -64,6 +55,9 @@ MODULE_PARM_DESC(debug_libiscsi_tcp, "Turn on debugging for libiscsi_tcp "
iscsi_conn_printk(KERN_INFO, _conn, \
"%s " dbg_fmt, \
__func__, ##arg); \
+ iscsi_dbg_trace(trace_iscsi_dbg_tcp, \
+ &(_conn)->cls_conn->dev, \
+ "%s " dbg_fmt, __func__, ##arg);\
} while (0);
static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
@@ -124,17 +118,22 @@ static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
BUG_ON(sg->length == 0);
/*
+ * We always map for the recv path.
+ *
* If the page count is greater than one it is ok to send
* to the network layer's zero copy send path. If not we
- * have to go the slow sendmsg path. We always map for the
- * recv path.
+ * have to go the slow sendmsg path.
+ *
+ * Same goes for slab pages: skb_can_coalesce() allows
+ * coalescing neighboring slab objects into a single frag which
+ * triggers one of hardened usercopy checks.
*/
- if (page_count(sg_page(sg)) >= 1 && !recv)
+ if (!recv && sendpage_ok(sg_page(sg)))
return;
if (recv) {
segment->atomic_mapped = true;
- segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
+ segment->sg_mapped = kmap_atomic(sg_page(sg));
} else {
segment->atomic_mapped = false;
/* the xmit path can sleep with the page mapped so use kmap */
@@ -148,7 +147,7 @@ void iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
{
if (segment->sg_mapped) {
if (segment->atomic_mapped)
- kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
+ kunmap_atomic(segment->sg_mapped);
else
kunmap(sg_page(segment->sg));
segment->sg_mapped = NULL;
@@ -169,7 +168,7 @@ iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
segment->size = ISCSI_DIGEST_SIZE;
segment->copied = 0;
segment->sg = NULL;
- segment->hash = NULL;
+ segment->crcp = NULL;
}
/**
@@ -192,28 +191,27 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
struct iscsi_segment *segment, int recv,
unsigned copied)
{
- struct scatterlist sg;
unsigned int pad;
ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copied %u %u size %u %s\n",
segment->copied, copied, segment->size,
recv ? "recv" : "xmit");
- if (segment->hash && copied) {
- /*
- * If a segment is kmapd we must unmap it before sending
- * to the crypto layer since that will try to kmap it again.
- */
- iscsi_tcp_segment_unmap(segment);
-
- if (!segment->data) {
- sg_init_table(&sg, 1);
- sg_set_page(&sg, sg_page(segment->sg), copied,
- segment->copied + segment->sg_offset +
- segment->sg->offset);
- } else
- sg_init_one(&sg, segment->data + segment->copied,
- copied);
- crypto_hash_update(segment->hash, &sg, copied);
+ if (segment->crcp && copied) {
+ if (segment->data) {
+ *segment->crcp = crc32c(*segment->crcp,
+ segment->data + segment->copied,
+ copied);
+ } else {
+ const void *data;
+
+ data = kmap_local_page(sg_page(segment->sg));
+ *segment->crcp = crc32c(*segment->crcp,
+ data + segment->copied +
+ segment->sg_offset +
+ segment->sg->offset,
+ copied);
+ kunmap_local(data);
+ }
}
segment->copied += copied;
@@ -258,8 +256,8 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
* Set us up for transferring the data digest. hdr digest
* is completely handled in hdr done function.
*/
- if (segment->hash) {
- crypto_hash_final(segment->hash, segment->digest);
+ if (segment->crcp) {
+ put_unaligned_le32(~*segment->crcp, segment->digest);
iscsi_tcp_segment_splice_digest(segment,
recv ? segment->recv_digest : segment->digest);
return 0;
@@ -280,8 +278,7 @@ EXPORT_SYMBOL_GPL(iscsi_tcp_segment_done);
* given buffer, and returns the number of bytes
* consumed, which can actually be less than @len.
*
- * If hash digest is enabled, the function will update the
- * hash while copying.
+ * If CRC is enabled, the function will update the CRC while copying.
* Combining these two operations doesn't buy us a lot (yet),
* but in the future we could implement combined copy+crc,
* just way we do for network layer checksums.
@@ -309,13 +306,10 @@ iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
}
inline void
-iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
+iscsi_tcp_dgst_header(const void *hdr, size_t hdrlen,
unsigned char digest[ISCSI_DIGEST_SIZE])
{
- struct scatterlist sg;
-
- sg_init_one(&sg, hdr, hdrlen);
- crypto_hash_digest(hash, &sg, hdrlen, digest);
+ put_unaligned_le32(~crc32c(~0, hdr, hdrlen), digest);
}
EXPORT_SYMBOL_GPL(iscsi_tcp_dgst_header);
@@ -340,24 +334,23 @@ iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
*/
static inline void
__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+ iscsi_segment_done_fn_t *done, u32 *crcp)
{
memset(segment, 0, sizeof(*segment));
segment->total_size = size;
segment->done = done;
- if (hash) {
- segment->hash = hash;
- crypto_hash_init(hash);
+ if (crcp) {
+ segment->crcp = crcp;
+ *crcp = ~0;
}
}
inline void
iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
- size_t size, iscsi_segment_done_fn_t *done,
- struct hash_desc *hash)
+ size_t size, iscsi_segment_done_fn_t *done, u32 *crcp)
{
- __iscsi_segment_init(segment, size, done, hash);
+ __iscsi_segment_init(segment, size, done, crcp);
segment->data = data;
segment->size = size;
}
@@ -367,12 +360,12 @@ inline int
iscsi_segment_seek_sg(struct iscsi_segment *segment,
struct scatterlist *sg_list, unsigned int sg_count,
unsigned int offset, size_t size,
- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+ iscsi_segment_done_fn_t *done, u32 *crcp)
{
struct scatterlist *sg;
unsigned int i;
- __iscsi_segment_init(segment, size, done, hash);
+ __iscsi_segment_init(segment, size, done, crcp);
for_each_sg(sg_list, sg, sg_count, i) {
if (offset < sg->length) {
iscsi_tcp_segment_init_sg(segment, sg, offset);
@@ -389,7 +382,7 @@ EXPORT_SYMBOL_GPL(iscsi_segment_seek_sg);
* iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
* @tcp_conn: iscsi connection to prep for
*
- * This function always passes NULL for the hash argument, because when this
+ * This function always passes NULL for the crcp argument, because when this
* function is called we do not yet know the final size of the header and want
* to delay the digest processing until we know that.
*/
@@ -430,22 +423,22 @@ static void
iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
{
struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- struct hash_desc *rx_hash = NULL;
+ u32 *rx_crcp = NULL;
if (conn->datadgst_en &&
!(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
- rx_hash = tcp_conn->rx_hash;
+ rx_crcp = tcp_conn->rx_crcp;
iscsi_segment_init_linear(&tcp_conn->in.segment,
conn->data, tcp_conn->in.datalen,
- iscsi_tcp_data_recv_done, rx_hash);
+ iscsi_tcp_data_recv_done, rx_crcp);
}
/**
* iscsi_tcp_cleanup_task - free tcp_task resources
* @task: iscsi task
*
- * must be called with session lock
+ * must be called with session back_lock
*/
void iscsi_tcp_cleanup_task(struct iscsi_task *task)
{
@@ -456,6 +449,7 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task)
if (!task->sc)
return;
+ spin_lock_bh(&tcp_task->queue2pool);
/* flush task's r2t queues */
while (kfifo_out(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t,
@@ -469,6 +463,7 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task)
sizeof(void*));
tcp_task->r2t = NULL;
}
+ spin_unlock_bh(&tcp_task->queue2pool);
}
EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task);
@@ -483,7 +478,7 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
int datasn = be32_to_cpu(rhdr->datasn);
- unsigned total_in_length = scsi_in(task->sc)->length;
+ unsigned total_in_length = task->sc->sdb.length;
/*
* lib iscsi will update this in the completion handling if there
@@ -518,75 +513,115 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
/**
* iscsi_tcp_r2t_rsp - iSCSI R2T Response processing
* @conn: iscsi connection
- * @task: scsi command task
+ * @hdr: PDU header
*/
-static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
+static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
{
struct iscsi_session *session = conn->session;
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
+ struct iscsi_tcp_task *tcp_task;
+ struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_r2t_rsp *rhdr;
struct iscsi_r2t_info *r2t;
- int r2tsn = be32_to_cpu(rhdr->r2tsn);
+ struct iscsi_task *task;
+ u32 data_length;
+ u32 data_offset;
+ int r2tsn;
int rc;
+ spin_lock(&session->back_lock);
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task) {
+ spin_unlock(&session->back_lock);
+ return ISCSI_ERR_BAD_ITT;
+ } else if (task->sc->sc_data_direction != DMA_TO_DEVICE) {
+ spin_unlock(&session->back_lock);
+ return ISCSI_ERR_PROTO;
+ }
+ /*
+ * A bad target might complete the cmd before we have handled R2Ts
+ * so get a ref to the task that will be dropped in the xmit path.
+ */
+ if (task->state != ISCSI_TASK_RUNNING) {
+ spin_unlock(&session->back_lock);
+ /* Let the path that got the early rsp complete it */
+ return 0;
+ }
+ task->last_xfer = jiffies;
+ if (!iscsi_get_task(task)) {
+ spin_unlock(&session->back_lock);
+ /* Let the path that got the early rsp complete it */
+ return 0;
+ }
+
+ tcp_conn = conn->dd_data;
+ rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
+ /* fill-in new R2T associated with the task */
+ iscsi_update_cmdsn(session, (struct iscsi_nopin *)rhdr);
+ spin_unlock(&session->back_lock);
+
if (tcp_conn->in.datalen) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2t with datalen %d\n",
tcp_conn->in.datalen);
- return ISCSI_ERR_DATALEN;
+ rc = ISCSI_ERR_DATALEN;
+ goto put_task;
}
+ tcp_task = task->dd_data;
+ r2tsn = be32_to_cpu(rhdr->r2tsn);
if (tcp_task->exp_datasn != r2tsn){
ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
tcp_task->exp_datasn, r2tsn);
- return ISCSI_ERR_R2TSN;
+ rc = ISCSI_ERR_R2TSN;
+ goto put_task;
}
- /* fill-in new R2T associated with the task */
- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
-
- if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+ if (session->state != ISCSI_STATE_LOGGED_IN) {
iscsi_conn_printk(KERN_INFO, conn,
"dropping R2T itt %d in recovery.\n",
task->itt);
- return 0;
+ rc = 0;
+ goto put_task;
}
- rc = kfifo_out(&tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
- if (!rc) {
- iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. "
- "Target has sent more R2Ts than it "
- "negotiated for or driver has has leaked.\n");
- return ISCSI_ERR_PROTO;
- }
-
- r2t->exp_statsn = rhdr->statsn;
- r2t->data_length = be32_to_cpu(rhdr->data_length);
- if (r2t->data_length == 0) {
+ data_length = be32_to_cpu(rhdr->data_length);
+ if (data_length == 0) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with zero data len\n");
- kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- return ISCSI_ERR_DATALEN;
+ rc = ISCSI_ERR_DATALEN;
+ goto put_task;
}
- if (r2t->data_length > session->max_burst)
+ if (data_length > session->max_burst)
ISCSI_DBG_TCP(conn, "invalid R2T with data len %u and max "
"burst %u. Attempting to execute request.\n",
- r2t->data_length, session->max_burst);
+ data_length, session->max_burst);
- r2t->data_offset = be32_to_cpu(rhdr->data_offset);
- if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
+ data_offset = be32_to_cpu(rhdr->data_offset);
+ if (data_offset + data_length > task->sc->sdb.length) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with data len %u at offset %u "
- "and total length %d\n", r2t->data_length,
- r2t->data_offset, scsi_out(task->sc)->length);
- kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- return ISCSI_ERR_DATALEN;
+ "and total length %d\n", data_length,
+ data_offset, task->sc->sdb.length);
+ rc = ISCSI_ERR_DATALEN;
+ goto put_task;
+ }
+
+ spin_lock(&tcp_task->pool2queue);
+ rc = kfifo_out(&tcp_task->r2tpool.queue, (void *)&r2t, sizeof(void *));
+ if (!rc) {
+ iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. "
+ "Target has sent more R2Ts than it "
+ "negotiated for or driver has leaked.\n");
+ spin_unlock(&tcp_task->pool2queue);
+ rc = ISCSI_ERR_PROTO;
+ goto put_task;
}
+ r2t->exp_statsn = rhdr->statsn;
+ r2t->data_length = data_length;
+ r2t->data_offset = data_offset;
+
r2t->ttt = rhdr->ttt; /* no flip */
r2t->datasn = 0;
r2t->sent = 0;
@@ -594,9 +629,14 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
tcp_task->exp_datasn = r2tsn + 1;
kfifo_in(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
conn->r2t_pdus_cnt++;
+ spin_unlock(&tcp_task->pool2queue);
iscsi_requeue_task(task);
return 0;
+
+put_task:
+ iscsi_put_task(task);
+ return rc;
}
/*
@@ -666,24 +706,24 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
switch(opcode) {
case ISCSI_OP_SCSI_DATA_IN:
- spin_lock(&conn->session->lock);
+ spin_lock(&conn->session->back_lock);
task = iscsi_itt_to_ctask(conn, hdr->itt);
if (!task)
rc = ISCSI_ERR_BAD_ITT;
else
rc = iscsi_tcp_data_in(conn, task);
if (rc) {
- spin_unlock(&conn->session->lock);
+ spin_unlock(&conn->session->back_lock);
break;
}
if (tcp_conn->in.datalen) {
struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct hash_desc *rx_hash = NULL;
- struct scsi_data_buffer *sdb = scsi_in(task->sc);
+ u32 *rx_crcp = NULL;
+ struct scsi_data_buffer *sdb = &task->sc->sdb;
/*
- * Setup copy of Data-In into the Scsi_Cmnd
+ * Setup copy of Data-In into the struct scsi_cmnd
* Scatterlist case:
* We set up the iscsi_segment to point to the next
* scatterlist entry to copy to. As we go along,
@@ -692,7 +732,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
*/
if (conn->datadgst_en &&
!(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
- rx_hash = tcp_conn->rx_hash;
+ rx_crcp = tcp_conn->rx_crcp;
ISCSI_DBG_TCP(conn, "iscsi_tcp_begin_data_in( "
"offset=%d, datalen=%d)\n",
@@ -705,12 +745,12 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
tcp_task->data_offset,
tcp_conn->in.datalen,
iscsi_tcp_process_data_in,
- rx_hash);
- spin_unlock(&conn->session->lock);
+ rx_crcp);
+ spin_unlock(&conn->session->back_lock);
return rc;
}
rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
- spin_unlock(&conn->session->lock);
+ spin_unlock(&conn->session->back_lock);
break;
case ISCSI_OP_SCSI_CMD_RSP:
if (tcp_conn->in.datalen) {
@@ -720,18 +760,11 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
break;
case ISCSI_OP_R2T:
- spin_lock(&conn->session->lock);
- task = iscsi_itt_to_ctask(conn, hdr->itt);
- if (!task)
- rc = ISCSI_ERR_BAD_ITT;
- else if (ahslen)
+ if (ahslen) {
rc = ISCSI_ERR_AHSLEN;
- else if (task->sc->sc_data_direction == DMA_TO_DEVICE) {
- task->last_xfer = jiffies;
- rc = iscsi_tcp_r2t_rsp(conn, task);
- } else
- rc = ISCSI_ERR_PROTO;
- spin_unlock(&conn->session->lock);
+ break;
+ }
+ rc = iscsi_tcp_r2t_rsp(conn, hdr);
break;
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
@@ -760,7 +793,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
iscsi_tcp_data_recv_prep(tcp_conn);
return 0;
}
- /* fall through */
+ fallthrough;
case ISCSI_OP_LOGOUT_RSP:
case ISCSI_OP_NOOP_IN:
case ISCSI_OP_SCSI_TMFUNC_RSP:
@@ -784,6 +817,8 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
/**
* iscsi_tcp_hdr_recv_done - process PDU header
+ * @tcp_conn: iSCSI TCP connection
+ * @segment: the buffer segment being processed
*
* This is the callback invoked when the PDU header has
* been received. If the header is followed by additional
@@ -832,7 +867,7 @@ iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
return 0;
}
- iscsi_tcp_dgst_header(tcp_conn->rx_hash, hdr,
+ iscsi_tcp_dgst_header(hdr,
segment->total_copied - ISCSI_DIGEST_SIZE,
segment->digest);
@@ -862,9 +897,10 @@ EXPORT_SYMBOL_GPL(iscsi_tcp_recv_segment_is_hdr);
* @conn: iscsi connection
* @skb: network buffer with header and/or data segment
* @offset: offset in skb
- * @offload: bool indicating if transfer was offloaded
+ * @offloaded: bool indicating if transfer was offloaded
+ * @status: iscsi TCP status result
*
- * Will return status of transfer in status. And will return
+ * Will return status of transfer in @status. And will return
* number of bytes copied.
*/
int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
@@ -884,7 +920,7 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
*/
conn->last_recv = jiffies;
- if (unlikely(conn->suspend_rx)) {
+ if (unlikely(test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags))) {
ISCSI_DBG_TCP(conn, "Rx suspended!\n");
*status = ISCSI_TCP_SUSPENDED;
return 0;
@@ -905,7 +941,6 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
ISCSI_DBG_TCP(conn, "no more data avail. Consumed %d\n",
consumed);
*status = ISCSI_TCP_SKB_DONE;
- skb_abort_seq_read(&seq);
goto skb_done;
}
BUG_ON(segment->copied >= segment->size);
@@ -942,9 +977,7 @@ EXPORT_SYMBOL_GPL(iscsi_tcp_recv_skb);
/**
* iscsi_tcp_task_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
- * @conn: iscsi connection
* @task: scsi command task
- * @sc: scsi command
*/
int iscsi_tcp_task_init(struct iscsi_task *task)
{
@@ -980,14 +1013,13 @@ EXPORT_SYMBOL_GPL(iscsi_tcp_task_init);
static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
{
- struct iscsi_session *session = task->conn->session;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_r2t_info *r2t = NULL;
if (iscsi_task_has_unsol_data(task))
r2t = &task->unsol_r2t;
else {
- spin_lock_bh(&session->lock);
+ spin_lock_bh(&tcp_task->queue2pool);
if (tcp_task->r2t) {
r2t = tcp_task->r2t;
/* Continue with this R2T? */
@@ -1009,7 +1041,7 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
else
r2t = tcp_task->r2t;
}
- spin_unlock_bh(&session->lock);
+ spin_unlock_bh(&tcp_task->queue2pool);
}
return r2t;
@@ -1084,7 +1116,8 @@ iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size,
struct iscsi_cls_conn *cls_conn;
struct iscsi_tcp_conn *tcp_conn;
- cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
+ cls_conn = iscsi_conn_setup(cls_session,
+ sizeof(*tcp_conn) + dd_data_size, conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
@@ -1096,22 +1129,13 @@ iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size,
tcp_conn = conn->dd_data;
tcp_conn->iscsi_conn = conn;
-
- tcp_conn->dd_data = kzalloc(dd_data_size, GFP_KERNEL);
- if (!tcp_conn->dd_data) {
- iscsi_conn_teardown(cls_conn);
- return NULL;
- }
+ tcp_conn->dd_data = conn->dd_data + sizeof(*tcp_conn);
return cls_conn;
}
EXPORT_SYMBOL_GPL(iscsi_tcp_conn_setup);
void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn)
{
- struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-
- kfree(tcp_conn->dd_data);
iscsi_conn_teardown(cls_conn);
}
EXPORT_SYMBOL_GPL(iscsi_tcp_conn_teardown);
@@ -1147,6 +1171,8 @@ int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session)
iscsi_pool_free(&tcp_task->r2tpool);
goto r2t_alloc_fail;
}
+ spin_lock_init(&tcp_task->pool2queue);
+ spin_lock_init(&tcp_task->queue2pool);
}
return 0;
@@ -1177,6 +1203,24 @@ void iscsi_tcp_r2tpool_free(struct iscsi_session *session)
}
EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free);
+int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf)
+{
+ struct iscsi_session *session = conn->session;
+ unsigned short r2ts = 0;
+
+ sscanf(buf, "%hu", &r2ts);
+ if (session->max_r2t == r2ts)
+ return 0;
+
+ if (!r2ts || !is_power_of_2(r2ts))
+ return -EINVAL;
+
+ session->max_r2t = r2ts;
+ iscsi_tcp_r2tpool_free(session);
+ return iscsi_tcp_r2tpool_alloc(session);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t);
+
void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
struct iscsi_stats *stats)
{
diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig
index 9dafe64e7c7a..c640535d1ac0 100644
--- a/drivers/scsi/libsas/Kconfig
+++ b/drivers/scsi/libsas/Kconfig
@@ -1,26 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Kernel configuration file for the SAS Class
#
# Copyright (C) 2005 Adaptec, Inc. All rights reserved.
# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
#
-# This file is licensed under GPLv2.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; version 2 of the
-# License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
-#
config SCSI_SAS_LIBSAS
tristate "SAS Domain Transport Attributes"
@@ -34,6 +18,7 @@ config SCSI_SAS_ATA
bool "ATA support for libsas (requires libata)"
depends on SCSI_SAS_LIBSAS
depends on ATA = y || ATA = SCSI_SAS_LIBSAS
+ select SATA_HOST
help
Builds in ATA support into libsas. Will necessitate
the loading of libata along with libsas.
diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile
index 2e70140f70c3..9dc32736cf21 100644
--- a/drivers/scsi/libsas/Makefile
+++ b/drivers/scsi/libsas/Makefile
@@ -1,35 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Kernel Makefile for the libsas helpers
#
# Copyright (C) 2005 Adaptec, Inc. All rights reserved.
# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
#
-# This file is licensed under GPLv2.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; version 2 of the
-# License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o
libsas-y += sas_init.o \
sas_phy.o \
sas_port.o \
sas_event.o \
- sas_dump.o \
sas_discover.o \
sas_expander.o \
sas_scsi_host.o \
sas_task.o
libsas-$(CONFIG_SCSI_SAS_ATA) += sas_ata.o
libsas-$(CONFIG_SCSI_SAS_HOST_SMP) += sas_host_smp.o
+
+ccflags-y := -DDEBUG -I$(srctree)/drivers/scsi
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index db9238f2ecb8..bcecb4911da9 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -1,28 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for SATA devices on Serial Attached SCSI (SAS) controllers
*
* Copyright (C) 2006 IBM Corporation
*
* Written by: Darrick J. Wong <djwong@us.ibm.com>, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/async.h>
+#include <linux/export.h>
#include <scsi/sas_ata.h>
#include "sas_internal.h"
@@ -32,8 +20,8 @@
#include <scsi/scsi.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
-#include "../scsi_sas_internal.h"
-#include "../scsi_transport_api.h"
+#include "scsi_sas_internal.h"
+#include "scsi_transport_api.h"
#include <scsi/scsi_eh.h>
static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)
@@ -47,368 +35,590 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)
/* ts->resp == SAS_TASK_COMPLETE */
/* task delivered, what happened afterwards? */
switch (ts->stat) {
- case SAS_DEV_NO_RESPONSE:
- return AC_ERR_TIMEOUT;
-
- case SAS_INTERRUPTED:
- case SAS_PHY_DOWN:
- case SAS_NAK_R_ERR:
- return AC_ERR_ATA_BUS;
-
-
- case SAS_DATA_UNDERRUN:
- /*
- * Some programs that use the taskfile interface
- * (smartctl in particular) can cause underrun
- * problems. Ignore these errors, perhaps at our
- * peril.
- */
- return 0;
-
- case SAS_DATA_OVERRUN:
- case SAS_QUEUE_FULL:
- case SAS_DEVICE_UNKNOWN:
- case SAS_SG_ERR:
- return AC_ERR_INVALID;
-
- case SAS_OPEN_TO:
- case SAS_OPEN_REJECT:
- SAS_DPRINTK("%s: Saw error %d. What to do?\n",
- __func__, ts->stat);
- return AC_ERR_OTHER;
-
- case SAM_STAT_CHECK_CONDITION:
- case SAS_ABORTED_TASK:
- return AC_ERR_DEV;
-
- case SAS_PROTO_RESPONSE:
- /* This means the ending_fis has the error
- * value; return 0 here to collect it */
- return 0;
- default:
- return 0;
+ case SAS_DEV_NO_RESPONSE:
+ return AC_ERR_TIMEOUT;
+ case SAS_INTERRUPTED:
+ case SAS_PHY_DOWN:
+ case SAS_NAK_R_ERR:
+ return AC_ERR_ATA_BUS;
+ case SAS_DATA_UNDERRUN:
+ /*
+ * Some programs that use the taskfile interface
+ * (smartctl in particular) can cause underrun
+ * problems. Ignore these errors, perhaps at our
+ * peril.
+ */
+ return 0;
+ case SAS_DATA_OVERRUN:
+ case SAS_QUEUE_FULL:
+ case SAS_DEVICE_UNKNOWN:
+ case SAS_OPEN_TO:
+ case SAS_OPEN_REJECT:
+ pr_warn("%s: Saw error %d. What to do?\n",
+ __func__, ts->stat);
+ return AC_ERR_OTHER;
+ case SAM_STAT_CHECK_CONDITION:
+ case SAS_ABORTED_TASK:
+ return AC_ERR_DEV;
+ case SAS_PROTO_RESPONSE:
+ /* This means the ending_fis has the error
+ * value; return 0 here to collect it
+ */
+ return 0;
+ default:
+ return 0;
}
}
static void sas_ata_task_done(struct sas_task *task)
{
struct ata_queued_cmd *qc = task->uldd_task;
- struct domain_device *dev;
+ struct domain_device *dev = task->dev;
struct task_status_struct *stat = &task->task_status;
struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf;
- struct sas_ha_struct *sas_ha;
+ struct sas_ha_struct *sas_ha = dev->port->ha;
enum ata_completion_errors ac;
unsigned long flags;
struct ata_link *link;
+ struct ata_port *ap;
+
+ spin_lock_irqsave(&dev->done_lock, flags);
+ if (test_bit(SAS_HA_FROZEN, &sas_ha->state))
+ task = NULL;
+ else if (qc && qc->scsicmd)
+ ASSIGN_SAS_TASK(qc->scsicmd, NULL);
+ spin_unlock_irqrestore(&dev->done_lock, flags);
+
+ /* check if libsas-eh got to the task before us */
+ if (unlikely(!task))
+ return;
if (!qc)
goto qc_already_gone;
- dev = qc->ap->private_data;
- sas_ha = dev->port->ha;
- link = &dev->sata_dev.ap->link;
+ ap = qc->ap;
+ link = &ap->link;
- spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
- if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD ||
- ((stat->stat == SAM_STAT_CHECK_CONDITION &&
- dev->sata_dev.command_set == ATAPI_COMMAND_SET))) {
- ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf);
+ spin_lock_irqsave(ap->lock, flags);
+ /* check if we lost the race with libata/sas_ata_post_internal() */
+ if (unlikely(ata_port_is_frozen(ap))) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ if (qc->scsicmd)
+ goto qc_already_gone;
+ else {
+ /* if eh is not involved and the port is frozen then the
+ * ata internal abort process has taken responsibility
+ * for this sas_task
+ */
+ return;
+ }
+ }
+
+ if (stat->stat == SAS_PROTO_RESPONSE ||
+ stat->stat == SAS_SAM_STAT_GOOD ||
+ (stat->stat == SAS_SAM_STAT_CHECK_CONDITION &&
+ dev->sata_dev.class == ATA_DEV_ATAPI)) {
+ memcpy(dev->sata_dev.fis, resp->ending_fis, ATA_RESP_FIS_SIZE);
if (!link->sactive) {
- qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command);
+ qc->err_mask |= ac_err_mask(dev->sata_dev.fis[2]);
} else {
- link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.tf.command);
+ link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]);
if (unlikely(link->eh_info.err_mask))
- qc->flags |= ATA_QCFLAG_FAILED;
+ qc->flags |= ATA_QCFLAG_EH;
}
-
- dev->sata_dev.sstatus = resp->sstatus;
- dev->sata_dev.serror = resp->serror;
- dev->sata_dev.scontrol = resp->scontrol;
} else {
ac = sas_to_ata_err(stat);
if (ac) {
- SAS_DPRINTK("%s: SAS error %x\n", __func__,
- stat->stat);
+ pr_warn("%s: SAS error 0x%x\n", __func__, stat->stat);
/* We saw a SAS error. Send a vague error. */
if (!link->sactive) {
qc->err_mask = ac;
} else {
link->eh_info.err_mask |= AC_ERR_DEV;
- qc->flags |= ATA_QCFLAG_FAILED;
+ qc->flags |= ATA_QCFLAG_EH;
}
- dev->sata_dev.tf.feature = 0x04; /* status err */
- dev->sata_dev.tf.command = ATA_ERR;
+ dev->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */
+ dev->sata_dev.fis[3] = ATA_ABORTED; /* tf error */
}
}
qc->lldd_task = NULL;
- if (qc->scsicmd)
- ASSIGN_SAS_TASK(qc->scsicmd, NULL);
ata_qc_complete(qc);
- spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags);
-
- /*
- * If the sas_task has an ata qc, a scsi_cmnd and the aborted
- * flag is set, then we must have come in via the libsas EH
- * functions. When we exit this function, we need to put the
- * scsi_cmnd on the list of finished errors. The ata_qc_complete
- * call cleans up the libata side of things but we're protected
- * from the scsi_cmnd going away because the scsi_cmnd is owned
- * by the EH, making libata's call to scsi_done a NOP.
- */
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (qc->scsicmd && task->task_state_flags & SAS_TASK_STATE_ABORTED)
- scsi_eh_finish_cmd(qc->scsicmd, &sas_ha->eh_done_q);
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ spin_unlock_irqrestore(ap->lock, flags);
qc_already_gone:
- list_del_init(&task->list);
sas_free_task(task);
}
static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
+ __must_hold(ap->lock)
{
- int res;
struct sas_task *task;
- struct domain_device *dev = qc->ap->private_data;
+ struct scatterlist *sg;
+ int ret = AC_ERR_SYSTEM;
+ unsigned int si, xfer = 0;
+ struct ata_port *ap = qc->ap;
+ struct domain_device *dev = ap->private_data;
struct sas_ha_struct *sas_ha = dev->port->ha;
- struct Scsi_Host *host = sas_ha->core.shost;
+ struct Scsi_Host *host = sas_ha->shost;
struct sas_internal *i = to_sas_internal(host->transportt);
- struct scatterlist *sg;
- unsigned int xfer = 0;
- unsigned int si;
+
+ /* TODO: we should try to remove that unlock */
+ spin_unlock(ap->lock);
/* If the device fell off, no sense in issuing commands */
- if (dev->gone)
- return AC_ERR_SYSTEM;
+ if (test_bit(SAS_DEV_GONE, &dev->state))
+ goto out;
task = sas_alloc_task(GFP_ATOMIC);
if (!task)
- return AC_ERR_SYSTEM;
+ goto out;
task->dev = dev;
task->task_proto = SAS_PROTOCOL_STP;
task->task_done = sas_ata_task_done;
- if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
- qc->tf.command == ATA_CMD_FPDMA_READ) {
- /* Need to zero out the tag libata assigned us */
+ /* For NCQ commands, zero out the tag libata assigned us */
+ if (ata_is_ncq(qc->tf.protocol))
qc->tf.nsect = 0;
- }
- ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis);
+ ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, (u8 *)&task->ata_task.fis);
task->uldd_task = qc;
if (ata_is_atapi(qc->tf.protocol)) {
memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len);
task->total_xfer_len = qc->nbytes;
task->num_scatter = qc->n_elem;
+ task->data_dir = qc->dma_dir;
+ } else if (!ata_is_data(qc->tf.protocol)) {
+ task->data_dir = DMA_NONE;
} else {
for_each_sg(qc->sg, sg, qc->n_elem, si)
- xfer += sg->length;
+ xfer += sg_dma_len(sg);
task->total_xfer_len = xfer;
task->num_scatter = si;
+ task->data_dir = qc->dma_dir;
}
-
- task->data_dir = qc->dma_dir;
task->scatter = qc->sg;
- task->ata_task.retry_count = 1;
- task->task_state_flags = SAS_TASK_STATE_PENDING;
qc->lldd_task = task;
- switch (qc->tf.protocol) {
- case ATA_PROT_NCQ:
- task->ata_task.use_ncq = 1;
- /* fall through */
- case ATAPI_PROT_DMA:
- case ATA_PROT_DMA:
- task->ata_task.dma_xfer = 1;
- break;
- }
+ task->ata_task.use_ncq = ata_is_ncq(qc->tf.protocol);
+ task->ata_task.dma_xfer = ata_is_dma(qc->tf.protocol);
+
+ if (qc->flags & ATA_QCFLAG_RESULT_TF)
+ task->ata_task.return_fis_on_success = 1;
if (qc->scsicmd)
ASSIGN_SAS_TASK(qc->scsicmd, task);
- if (sas_ha->lldd_max_execute_num < 2)
- res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);
- else
- res = sas_queue_up(task);
-
- /* Examine */
- if (res) {
- SAS_DPRINTK("lldd_execute_task returned: %d\n", res);
+ ret = i->dft->lldd_execute_task(task, GFP_ATOMIC);
+ if (ret) {
+ pr_debug("lldd_execute_task returned: %d\n", ret);
if (qc->scsicmd)
ASSIGN_SAS_TASK(qc->scsicmd, NULL);
sas_free_task(task);
- return AC_ERR_SYSTEM;
+ qc->lldd_task = NULL;
+ ret = AC_ERR_SYSTEM;
}
- return 0;
+ out:
+ spin_lock(ap->lock);
+ return ret;
}
-static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
+static void sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
{
struct domain_device *dev = qc->ap->private_data;
- memcpy(&qc->result_tf, &dev->sata_dev.tf, sizeof(qc->result_tf));
- return true;
+ ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf);
}
-static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
+static struct sas_internal *dev_to_sas_internal(struct domain_device *dev)
+{
+ return to_sas_internal(dev->port->ha->shost->transportt);
+}
+
+static int sas_get_ata_command_set(struct domain_device *dev)
+{
+ struct ata_taskfile tf;
+
+ if (dev->dev_type == SAS_SATA_PENDING)
+ return ATA_DEV_UNKNOWN;
+
+ ata_tf_from_fis(dev->frame_rcvd, &tf);
+
+ return ata_dev_classify(&tf);
+}
+
+static int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy)
+{
+ if (phy->attached_tproto & SAS_PROTOCOL_STP)
+ dev->tproto = phy->attached_tproto;
+ if (phy->attached_sata_dev)
+ dev->tproto |= SAS_SATA_DEV;
+
+ if (phy->attached_dev_type == SAS_SATA_PENDING)
+ dev->dev_type = SAS_SATA_PENDING;
+ else {
+ int res;
+
+ dev->dev_type = SAS_SATA_DEV;
+ res = sas_get_report_phy_sata(dev->parent, phy->phy_id,
+ &dev->sata_dev.rps_resp);
+ if (res) {
+ pr_debug("report phy sata to %016llx:%02d returned 0x%x\n",
+ SAS_ADDR(dev->parent->sas_addr),
+ phy->phy_id, res);
+ return res;
+ }
+ memcpy(dev->frame_rcvd, &dev->sata_dev.rps_resp.rps.fis,
+ sizeof(struct dev_to_host_fis));
+ dev->sata_dev.class = sas_get_ata_command_set(dev);
+ }
+ return 0;
+}
+
+static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy)
+{
+ int res;
+
+ /* we weren't pending, so successfully end the reset sequence now */
+ if (dev->dev_type != SAS_SATA_PENDING)
+ return 1;
+
+ /* hmmm, if this succeeds do we need to repost the domain_device to the
+ * lldd so it can pick up new parameters?
+ */
+ res = sas_get_ata_info(dev, phy);
+ if (res)
+ return 0; /* retry */
+ else
+ return 1;
+}
+
+int smp_ata_check_ready_type(struct ata_link *link)
+{
+ struct domain_device *dev = link->ap->private_data;
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ struct domain_device *ex_dev = dev->parent;
+ enum sas_device_type type = SAS_PHY_UNUSED;
+ u8 sas_addr[SAS_ADDR_SIZE];
+ int res;
+
+ res = sas_get_phy_attached_dev(ex_dev, phy->number, sas_addr, &type);
+ sas_put_local_phy(phy);
+ if (res)
+ return res;
+
+ switch (type) {
+ case SAS_SATA_PENDING:
+ return 0;
+ case SAS_END_DEVICE:
+ return 1;
+ default:
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL_GPL(smp_ata_check_ready_type);
+
+static int smp_ata_check_ready(struct ata_link *link)
{
+ int res;
struct ata_port *ap = link->ap;
struct domain_device *dev = ap->private_data;
- struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
- int res = TMF_RESP_FUNC_FAILED;
- int ret = 0;
+ struct domain_device *ex_dev = dev->parent;
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy->number];
+
+ res = sas_ex_phy_discover(ex_dev, phy->number);
+ sas_put_local_phy(phy);
- if (i->dft->lldd_I_T_nexus_reset)
- res = i->dft->lldd_I_T_nexus_reset(dev);
+ /* break the wait early if the expander is unreachable,
+ * otherwise keep polling
+ */
+ if (res == -ECOMM)
+ return res;
+ if (res != SMP_RESP_FUNC_ACC)
+ return 0;
- if (res != TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__);
- ret = -EAGAIN;
+ switch (ex_phy->attached_dev_type) {
+ case SAS_SATA_PENDING:
+ return 0;
+ case SAS_END_DEVICE:
+ if (ex_phy->attached_sata_dev)
+ return sas_ata_clear_pending(dev, ex_phy);
+ fallthrough;
+ default:
+ return -ENODEV;
}
+}
- switch (dev->sata_dev.command_set) {
- case ATA_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATA device.\n", __func__);
- *class = ATA_DEV_ATA;
- break;
- case ATAPI_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATAPI device.\n", __func__);
- *class = ATA_DEV_ATAPI;
- break;
- default:
- SAS_DPRINTK("%s: Unknown SATA command set: %d.\n",
- __func__,
- dev->sata_dev.command_set);
- *class = ATA_DEV_UNKNOWN;
- break;
+static int local_ata_check_ready(struct ata_link *link)
+{
+ struct ata_port *ap = link->ap;
+ struct domain_device *dev = ap->private_data;
+ struct sas_internal *i = dev_to_sas_internal(dev);
+
+ if (i->dft->lldd_ata_check_ready)
+ return i->dft->lldd_ata_check_ready(dev);
+ else {
+ /* lldd's that don't implement 'ready' checking get the
+ * old default behavior of not coordinating reset
+ * recovery with libata
+ */
+ return 1;
}
+}
+
+static int sas_ata_printk(const char *level, const struct domain_device *ddev,
+ const char *fmt, ...)
+{
+ struct ata_port *ap = ddev->sata_dev.ap;
+ struct device *dev = &ddev->rphy->dev;
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ r = printk("%s" SAS_FMT "ata%u: %s: %pV",
+ level, ap->print_id, dev_name(dev), &vaf);
+
+ va_end(args);
+
+ return r;
+}
+
+static int sas_ata_wait_after_reset(struct domain_device *dev, unsigned long deadline)
+{
+ struct sata_device *sata_dev = &dev->sata_dev;
+ int (*check_ready)(struct ata_link *link);
+ struct ata_port *ap = sata_dev->ap;
+ struct ata_link *link = &ap->link;
+ struct sas_phy *phy;
+ int ret;
+
+ phy = sas_get_local_phy(dev);
+ if (scsi_is_sas_phy_local(phy))
+ check_ready = local_ata_check_ready;
+ else
+ check_ready = smp_ata_check_ready;
+ sas_put_local_phy(phy);
+
+ ret = ata_wait_after_reset(link, deadline, check_ready);
+ if (ret && ret != -EAGAIN)
+ sas_ata_printk(KERN_ERR, dev, "reset failed (errno=%d)\n", ret);
- ap->cbl = ATA_CBL_SATA;
return ret;
}
-static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
+static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
{
struct ata_port *ap = link->ap;
struct domain_device *dev = ap->private_data;
- struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
- int res = TMF_RESP_FUNC_FAILED;
- int ret = 0;
+ struct sas_internal *i = dev_to_sas_internal(dev);
+ int ret;
- if (i->dft->lldd_ata_soft_reset)
- res = i->dft->lldd_ata_soft_reset(dev);
+ ret = i->dft->lldd_I_T_nexus_reset(dev);
+ if (ret == -ENODEV)
+ return ret;
- if (res != TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("%s: Unable to soft reset\n", __func__);
- ret = -EAGAIN;
- }
+ if (ret != TMF_RESP_FUNC_COMPLETE)
+ sas_ata_printk(KERN_DEBUG, dev, "Unable to reset ata device?\n");
- switch (dev->sata_dev.command_set) {
- case ATA_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATA device.\n", __func__);
- *class = ATA_DEV_ATA;
- break;
- case ATAPI_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATAPI device.\n", __func__);
- *class = ATA_DEV_ATAPI;
- break;
- default:
- SAS_DPRINTK("%s: Unknown SATA command set: %d.\n",
- __func__, dev->sata_dev.command_set);
- *class = ATA_DEV_UNKNOWN;
- break;
- }
+ ret = sas_ata_wait_after_reset(dev, deadline);
+
+ *class = dev->sata_dev.class;
ap->cbl = ATA_CBL_SATA;
return ret;
}
+/*
+ * notify the lldd to forget the sas_task for this internal ata command
+ * that bypasses scsi-eh
+ */
+static void sas_ata_internal_abort(struct sas_task *task)
+{
+ struct sas_internal *si = dev_to_sas_internal(task->dev);
+ unsigned long flags;
+ int res;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_ABORTED ||
+ task->task_state_flags & SAS_TASK_STATE_DONE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ pr_debug("%s: Task %p already finished.\n", __func__, task);
+ goto out;
+ }
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ res = si->dft->lldd_abort_task(task);
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_DONE ||
+ res == TMF_RESP_FUNC_COMPLETE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ goto out;
+ }
+
+ /* XXX we are not prepared to deal with ->lldd_abort_task()
+ * failures. TODO: lldds need to unconditionally forget about
+ * aborted ata tasks, otherwise we (likely) leak the sas task
+ * here
+ */
+ pr_warn("%s: Task %p leaked.\n", __func__, task);
+
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
+ task->task_state_flags &= ~SAS_TASK_STATE_ABORTED;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ return;
+ out:
+ sas_free_task(task);
+}
+
static void sas_ata_post_internal(struct ata_queued_cmd *qc)
{
- if (qc->flags & ATA_QCFLAG_FAILED)
+ if (qc->flags & ATA_QCFLAG_EH)
qc->err_mask |= AC_ERR_OTHER;
if (qc->err_mask) {
/*
- * Find the sas_task and kill it. By this point,
- * libata has decided to kill the qc, so we needn't
- * bother with sas_ata_task_done. But we still
- * ought to abort the task.
+ * Find the sas_task and kill it. By this point, libata
+ * has decided to kill the qc and has frozen the port.
+ * In this state sas_ata_task_done() will no longer free
+ * the sas_task, so we need to notify the lldd (via
+ * ->lldd_abort_task) that the task is dead and free it
+ * ourselves.
*/
struct sas_task *task = qc->lldd_task;
- unsigned long flags;
qc->lldd_task = NULL;
- if (task) {
- /* Should this be a AT(API) device reset? */
- spin_lock_irqsave(&task->task_state_lock, flags);
- task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- task->uldd_task = NULL;
- __sas_task_abort(task);
- }
+ if (!task)
+ return;
+ task->uldd_task = NULL;
+ sas_ata_internal_abort(task);
}
}
+
+static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev)
+{
+ struct domain_device *dev = ap->private_data;
+ struct sas_internal *i = dev_to_sas_internal(dev);
+
+ if (i->dft->lldd_ata_set_dmamode)
+ i->dft->lldd_ata_set_dmamode(dev);
+}
+
+static void sas_ata_sched_eh(struct ata_port *ap)
+{
+ struct domain_device *dev = ap->private_data;
+ struct sas_ha_struct *ha = dev->port->ha;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->lock, flags);
+ if (!test_and_set_bit(SAS_DEV_EH_PENDING, &dev->state))
+ ha->eh_active++;
+ ata_std_sched_eh(ap);
+ spin_unlock_irqrestore(&ha->lock, flags);
+}
+
+void sas_ata_end_eh(struct ata_port *ap)
+{
+ struct domain_device *dev = ap->private_data;
+ struct sas_ha_struct *ha = dev->port->ha;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->lock, flags);
+ if (test_and_clear_bit(SAS_DEV_EH_PENDING, &dev->state))
+ ha->eh_active--;
+ spin_unlock_irqrestore(&ha->lock, flags);
+}
+
+static int sas_ata_prereset(struct ata_link *link, unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ struct domain_device *dev = ap->private_data;
+ struct sas_phy *local_phy = sas_get_local_phy(dev);
+ int res = 0;
+
+ if (!local_phy->enabled || test_bit(SAS_DEV_GONE, &dev->state))
+ res = -ENOENT;
+ sas_put_local_phy(local_phy);
+
+ return res;
+}
+
static struct ata_port_operations sas_sata_ops = {
- .prereset = ata_std_prereset,
- .softreset = sas_ata_soft_reset,
- .hardreset = sas_ata_hard_reset,
- .postreset = ata_std_postreset,
+ .reset.prereset = sas_ata_prereset,
+ .reset.hardreset = sas_ata_hard_reset,
.error_handler = ata_std_error_handler,
.post_internal_cmd = sas_ata_post_internal,
.qc_defer = ata_std_qc_defer,
- .qc_prep = ata_noop_qc_prep,
.qc_issue = sas_ata_qc_issue,
.qc_fill_rtf = sas_ata_qc_fill_rtf,
- .port_start = ata_sas_port_start,
- .port_stop = ata_sas_port_stop,
-};
-
-static struct ata_port_info sata_port_info = {
- .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &sas_sata_ops
+ .set_dmamode = sas_ata_set_dmamode,
+ .sched_eh = sas_ata_sched_eh,
+ .end_eh = sas_ata_end_eh,
};
-int sas_ata_init_host_and_port(struct domain_device *found_dev,
- struct scsi_target *starget)
+int sas_ata_init(struct domain_device *found_dev)
{
- struct Scsi_Host *shost = dev_to_shost(&starget->dev);
- struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct sas_ha_struct *ha = found_dev->port->ha;
+ struct Scsi_Host *shost = ha->shost;
+ struct ata_host *ata_host;
struct ata_port *ap;
+ int rc;
- ata_host_init(&found_dev->sata_dev.ata_host,
- ha->dev,
- sata_port_info.flags,
- &sas_sata_ops);
- ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host,
- &sata_port_info,
- shost);
+ ata_host = kzalloc(sizeof(*ata_host), GFP_KERNEL);
+ if (!ata_host) {
+ pr_err("ata host alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ ata_host_init(ata_host, ha->dev, &sas_sata_ops);
+
+ ap = ata_port_alloc(ata_host);
if (!ap) {
- SAS_DPRINTK("ata_sas_port_alloc failed.\n");
- return -ENODEV;
+ pr_err("ata_port_alloc failed.\n");
+ rc = -ENODEV;
+ goto free_host;
}
+ ap->port_no = 0;
+ ap->pio_mask = ATA_PIO4;
+ ap->mwdma_mask = ATA_MWDMA2;
+ ap->udma_mask = ATA_UDMA6;
+ ap->flags |= ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ |
+ ATA_FLAG_SAS_HOST | ATA_FLAG_FPDMA_AUX;
+ ap->ops = &sas_sata_ops;
ap->private_data = found_dev;
ap->cbl = ATA_CBL_SATA;
ap->scsi_host = shost;
+
+ rc = ata_tport_add(ata_host->dev, ap);
+ if (rc)
+ goto free_port;
+
+ found_dev->sata_dev.ata_host = ata_host;
found_dev->sata_dev.ap = ap;
return 0;
+
+free_port:
+ ata_port_free(ap);
+free_host:
+ ata_host_put(ata_host);
+ return rc;
}
void sas_ata_task_abort(struct sas_task *task)
@@ -418,442 +628,265 @@ void sas_ata_task_abort(struct sas_task *task)
/* Bounce SCSI-initiated commands to the SCSI EH */
if (qc->scsicmd) {
- struct request_queue *q = qc->scsicmd->device->request_queue;
- unsigned long flags;
-
- spin_lock_irqsave(q->queue_lock, flags);
- blk_abort_request(qc->scsicmd->request);
- spin_unlock_irqrestore(q->queue_lock, flags);
- scsi_schedule_eh(qc->scsicmd->device->host);
+ blk_abort_request(scsi_cmd_to_rq(qc->scsicmd));
return;
}
/* Internal command, fake a timeout and complete. */
qc->flags &= ~ATA_QCFLAG_ACTIVE;
- qc->flags |= ATA_QCFLAG_FAILED;
+ qc->flags |= ATA_QCFLAG_EH;
qc->err_mask |= AC_ERR_TIMEOUT;
waiting = qc->private_data;
complete(waiting);
}
-static void sas_task_timedout(unsigned long _task)
+void sas_probe_sata(struct asd_sas_port *port)
{
- struct sas_task *task = (void *) _task;
- unsigned long flags;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ struct domain_device *dev, *n;
- complete(&task->completion);
-}
+ mutex_lock(&port->ha->disco_mutex);
+ list_for_each_entry(dev, &port->disco_list, disco_list_node) {
+ if (!dev_is_sata(dev))
+ continue;
-static void sas_disc_task_done(struct sas_task *task)
-{
- if (!del_timer(&task->timer))
- return;
- complete(&task->completion);
-}
+ ata_port_probe(dev->sata_dev.ap);
+ }
+ mutex_unlock(&port->ha->disco_mutex);
-#define SAS_DEV_TIMEOUT 10
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
+ if (!dev_is_sata(dev))
+ continue;
-/**
- * sas_execute_task -- Basic task processing for discovery
- * @task: the task to be executed
- * @buffer: pointer to buffer to do I/O
- * @size: size of @buffer
- * @dma_dir: DMA direction. DMA_xxx
- */
-static int sas_execute_task(struct sas_task *task, void *buffer, int size,
- enum dma_data_direction dma_dir)
-{
- int res = 0;
- struct scatterlist *scatter = NULL;
- struct task_status_struct *ts = &task->task_status;
- int num_scatter = 0;
- int retries = 0;
- struct sas_internal *i =
- to_sas_internal(task->dev->port->ha->core.shost->transportt);
-
- if (dma_dir != DMA_NONE) {
- scatter = kzalloc(sizeof(*scatter), GFP_KERNEL);
- if (!scatter)
- goto out;
-
- sg_init_one(scatter, buffer, size);
- num_scatter = 1;
- }
+ sas_ata_wait_eh(dev);
- task->task_proto = task->dev->tproto;
- task->scatter = scatter;
- task->num_scatter = num_scatter;
- task->total_xfer_len = size;
- task->data_dir = dma_dir;
- task->task_done = sas_disc_task_done;
- if (dma_dir != DMA_NONE &&
- sas_protocol_ata(task->task_proto)) {
- task->num_scatter = dma_map_sg(task->dev->port->ha->dev,
- task->scatter,
- task->num_scatter,
- task->data_dir);
+ /* if libata could not bring the link up, don't surface
+ * the device
+ */
+ if (!ata_dev_enabled(sas_to_ata_dev(dev)))
+ sas_fail_probe(dev, __func__, -ENODEV);
}
- for (retries = 0; retries < 5; retries++) {
- task->task_state_flags = SAS_TASK_STATE_PENDING;
- init_completion(&task->completion);
-
- task->timer.data = (unsigned long) task;
- task->timer.function = sas_task_timedout;
- task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ;
- add_timer(&task->timer);
+}
- res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL);
- if (res) {
- del_timer(&task->timer);
- SAS_DPRINTK("executing SAS discovery task failed:%d\n",
- res);
- goto ex_err;
- }
- wait_for_completion(&task->completion);
- res = -ECOMM;
- if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
- int res2;
- SAS_DPRINTK("task aborted, flags:0x%x\n",
- task->task_state_flags);
- res2 = i->dft->lldd_abort_task(task);
- SAS_DPRINTK("came back from abort task\n");
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- if (res2 == TMF_RESP_FUNC_COMPLETE)
- continue; /* Retry the task */
- else
- goto ex_err;
- }
- }
- if (task->task_status.stat == SAM_STAT_BUSY ||
- task->task_status.stat == SAM_STAT_TASK_SET_FULL ||
- task->task_status.stat == SAS_QUEUE_FULL) {
- SAS_DPRINTK("task: q busy, sleeping...\n");
- schedule_timeout_interruptible(HZ);
- } else if (task->task_status.stat == SAM_STAT_CHECK_CONDITION) {
- struct scsi_sense_hdr shdr;
-
- if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size,
- &shdr)) {
- SAS_DPRINTK("couldn't normalize sense\n");
- continue;
- }
- if ((shdr.sense_key == 6 && shdr.asc == 0x29) ||
- (shdr.sense_key == 2 && shdr.asc == 4 &&
- shdr.ascq == 1)) {
- SAS_DPRINTK("device %016llx LUN: %016llx "
- "powering up or not ready yet, "
- "sleeping...\n",
- SAS_ADDR(task->dev->sas_addr),
- SAS_ADDR(task->ssp_task.LUN));
-
- schedule_timeout_interruptible(5*HZ);
- } else if (shdr.sense_key == 1) {
- res = 0;
- break;
- } else if (shdr.sense_key == 5) {
- break;
- } else {
- SAS_DPRINTK("dev %016llx LUN: %016llx "
- "sense key:0x%x ASC:0x%x ASCQ:0x%x"
- "\n",
- SAS_ADDR(task->dev->sas_addr),
- SAS_ADDR(task->ssp_task.LUN),
- shdr.sense_key,
- shdr.asc, shdr.ascq);
- }
- } else if (task->task_status.resp != SAS_TASK_COMPLETE ||
- task->task_status.stat != SAM_STAT_GOOD) {
- SAS_DPRINTK("task finished with resp:0x%x, "
- "stat:0x%x\n",
- task->task_status.resp,
- task->task_status.stat);
- goto ex_err;
- } else {
- res = 0;
- break;
+int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy,
+ struct domain_device *child, int phy_id)
+{
+ struct sas_rphy *rphy;
+ int ret;
+
+ if (child->linkrate > parent->min_linkrate) {
+ struct sas_phy *cphy = child->phy;
+ enum sas_linkrate min_prate = cphy->minimum_linkrate,
+ parent_min_lrate = parent->min_linkrate,
+ min_linkrate = (min_prate > parent_min_lrate) ?
+ parent_min_lrate : 0;
+ struct sas_phy_linkrates rates = {
+ .maximum_linkrate = parent->min_linkrate,
+ .minimum_linkrate = min_linkrate,
+ };
+
+ pr_notice("ex %016llx phy%02d SATA device linkrate > min pathway connection rate, attempting to lower device linkrate\n",
+ SAS_ADDR(child->sas_addr), phy_id);
+ ret = sas_smp_phy_control(parent, phy_id,
+ PHY_FUNC_LINK_RESET, &rates);
+ if (ret) {
+ pr_err("ex %016llx phy%02d SATA device could not set linkrate (%d)\n",
+ SAS_ADDR(child->sas_addr), phy_id, ret);
+ return ret;
}
+ pr_notice("ex %016llx phy%02d SATA device set linkrate successfully\n",
+ SAS_ADDR(child->sas_addr), phy_id);
+ child->linkrate = child->min_linkrate;
}
-ex_err:
- if (dma_dir != DMA_NONE) {
- if (sas_protocol_ata(task->task_proto))
- dma_unmap_sg(task->dev->port->ha->dev,
- task->scatter, task->num_scatter,
- task->data_dir);
- kfree(scatter);
- }
-out:
- return res;
-}
+ ret = sas_get_ata_info(child, phy);
+ if (ret)
+ return ret;
-/* ---------- SATA ---------- */
+ sas_init_dev(child);
+ ret = sas_ata_init(child);
+ if (ret)
+ return ret;
-static void sas_get_ata_command_set(struct domain_device *dev)
-{
- struct dev_to_host_fis *fis =
- (struct dev_to_host_fis *) dev->frame_rcvd;
+ rphy = sas_end_device_alloc(phy->port);
+ if (!rphy)
+ return -ENOMEM;
- if ((fis->sector_count == 1 && /* ATA */
- fis->lbal == 1 &&
- fis->lbam == 0 &&
- fis->lbah == 0 &&
- fis->device == 0)
- ||
- (fis->sector_count == 0 && /* CE-ATA (mATA) */
- fis->lbal == 0 &&
- fis->lbam == 0xCE &&
- fis->lbah == 0xAA &&
- (fis->device & ~0x10) == 0))
+ rphy->identify.phy_identifier = phy_id;
+ child->rphy = rphy;
+ get_device(&rphy->dev);
- dev->sata_dev.command_set = ATA_COMMAND_SET;
+ list_add_tail(&child->disco_list_node, &parent->port->disco_list);
- else if ((fis->interrupt_reason == 1 && /* ATAPI */
- fis->lbal == 1 &&
- fis->byte_count_low == 0x14 &&
- fis->byte_count_high == 0xEB &&
- (fis->device & ~0x10) == 0))
+ ret = sas_discover_sata(child);
+ if (ret) {
+ pr_notice("sas_discover_sata() for device %16llx at %016llx:%02d returned 0x%x\n",
+ SAS_ADDR(child->sas_addr),
+ SAS_ADDR(parent->sas_addr), phy_id, ret);
+ sas_rphy_free(child->rphy);
+ list_del(&child->disco_list_node);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func)
+{
+ struct domain_device *dev, *n;
- dev->sata_dev.command_set = ATAPI_COMMAND_SET;
+ list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) {
+ if (!dev_is_sata(dev))
+ continue;
- else if ((fis->sector_count == 1 && /* SEMB */
- fis->lbal == 1 &&
- fis->lbam == 0x3C &&
- fis->lbah == 0xC3 &&
- fis->device == 0)
- ||
- (fis->interrupt_reason == 1 && /* SATA PM */
- fis->lbal == 1 &&
- fis->byte_count_low == 0x69 &&
- fis->byte_count_high == 0x96 &&
- (fis->device & ~0x10) == 0))
+ sas_ata_wait_eh(dev);
- /* Treat it as a superset? */
- dev->sata_dev.command_set = ATAPI_COMMAND_SET;
+ /* if libata failed to power manage the device, tear it down */
+ if (ata_dev_disabled(sas_to_ata_dev(dev)))
+ sas_fail_probe(dev, func, -ENODEV);
+ }
}
-/**
- * sas_issue_ata_cmd -- Basic SATA command processing for discovery
- * @dev: the device to send the command to
- * @command: the command register
- * @features: the features register
- * @buffer: pointer to buffer to do I/O
- * @size: size of @buffer
- * @dma_dir: DMA direction. DMA_xxx
- */
-static int sas_issue_ata_cmd(struct domain_device *dev, u8 command,
- u8 features, void *buffer, int size,
- enum dma_data_direction dma_dir)
+void sas_suspend_sata(struct asd_sas_port *port)
{
- int res = 0;
- struct sas_task *task;
- struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *)
- &dev->frame_rcvd[0];
+ struct domain_device *dev;
- res = -ENOMEM;
- task = sas_alloc_task(GFP_KERNEL);
- if (!task)
- goto out;
+ mutex_lock(&port->ha->disco_mutex);
+ list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+ struct sata_device *sata;
- task->dev = dev;
+ if (!dev_is_sata(dev))
+ continue;
- task->ata_task.fis.fis_type = 0x27;
- task->ata_task.fis.command = command;
- task->ata_task.fis.features = features;
- task->ata_task.fis.device = d2h_fis->device;
- task->ata_task.retry_count = 1;
+ sata = &dev->sata_dev;
+ if (sata->ap->pm_mesg.event == PM_EVENT_SUSPEND)
+ continue;
- res = sas_execute_task(task, buffer, size, dma_dir);
+ ata_sas_port_suspend(sata->ap);
+ }
+ mutex_unlock(&port->ha->disco_mutex);
- sas_free_task(task);
-out:
- return res;
+ sas_ata_flush_pm_eh(port, __func__);
}
-#define ATA_IDENTIFY_DEV 0xEC
-#define ATA_IDENTIFY_PACKET_DEV 0xA1
-#define ATA_SET_FEATURES 0xEF
-#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07
-
-/**
- * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV)
- * @dev: STP/SATA device of interest (ATA/ATAPI)
- *
- * The LLDD has already been notified of this device, so that we can
- * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY
- * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its
- * performance for this device.
- */
-static int sas_discover_sata_dev(struct domain_device *dev)
+void sas_resume_sata(struct asd_sas_port *port)
{
- int res;
- __le16 *identify_x;
- u8 command;
-
- identify_x = kzalloc(512, GFP_KERNEL);
- if (!identify_x)
- return -ENOMEM;
+ struct domain_device *dev;
- if (dev->sata_dev.command_set == ATA_COMMAND_SET) {
- dev->sata_dev.identify_device = identify_x;
- command = ATA_IDENTIFY_DEV;
- } else {
- dev->sata_dev.identify_packet_device = identify_x;
- command = ATA_IDENTIFY_PACKET_DEV;
- }
+ mutex_lock(&port->ha->disco_mutex);
+ list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+ struct sata_device *sata;
- res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
- DMA_FROM_DEVICE);
- if (res)
- goto out_err;
-
- /* lives on the media? */
- if (le16_to_cpu(identify_x[0]) & 4) {
- /* incomplete response */
- SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to "
- "dev %llx\n", SAS_ADDR(dev->sas_addr));
- if (!(identify_x[83] & cpu_to_le16(1<<6)))
- goto cont1;
- res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES,
- ATA_FEATURE_PUP_STBY_SPIN_UP,
- NULL, 0, DMA_NONE);
- if (res)
- goto cont1;
-
- schedule_timeout_interruptible(5*HZ); /* More time? */
- res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
- DMA_FROM_DEVICE);
- if (res)
- goto out_err;
- }
-cont1:
- /* XXX Hint: register this SATA device with SATL.
- When this returns, dev->sata_dev->lu is alive and
- present.
- sas_satl_register_dev(dev);
- */
+ if (!dev_is_sata(dev))
+ continue;
- sas_fill_in_rphy(dev, dev->rphy);
+ sata = &dev->sata_dev;
+ if (sata->ap->pm_mesg.event == PM_EVENT_ON)
+ continue;
- return 0;
-out_err:
- dev->sata_dev.identify_packet_device = NULL;
- dev->sata_dev.identify_device = NULL;
- kfree(identify_x);
- return res;
-}
+ ata_sas_port_resume(sata->ap);
+ }
+ mutex_unlock(&port->ha->disco_mutex);
-static int sas_discover_sata_pm(struct domain_device *dev)
-{
- return -ENODEV;
+ sas_ata_flush_pm_eh(port, __func__);
}
/**
- * sas_discover_sata -- discover an STP/SATA domain device
+ * sas_discover_sata - discover an STP/SATA domain device
* @dev: pointer to struct domain_device of interest
*
- * First we notify the LLDD of this device, so we can send frames to
- * it. Then depending on the type of device we call the appropriate
- * discover functions. Once device discover is done, we notify the
- * LLDD so that it can fine-tune its parameters for the device, by
- * removing it and then adding it. That is, the second time around,
- * the driver would have certain fields, that it is looking at, set.
- * Finally we initialize the kobj so that the device can be added to
- * the system at registration time. Devices directly attached to a HA
- * port, have no parents. All other devices do, and should have their
- * "parent" pointer set appropriately before calling this function.
+ * Devices directly attached to a HA port, have no parents. All other
+ * devices do, and should have their "parent" pointer set appropriately
+ * before calling this function.
*/
int sas_discover_sata(struct domain_device *dev)
{
- int res;
+ if (dev->dev_type == SAS_SATA_PM)
+ return -ENODEV;
- sas_get_ata_command_set(dev);
+ dev->sata_dev.class = sas_get_ata_command_set(dev);
+ sas_fill_in_rphy(dev, dev->rphy);
- res = sas_notify_lldd_dev_found(dev);
- if (res)
- return res;
+ return sas_notify_lldd_dev_found(dev);
+}
- switch (dev->dev_type) {
- case SATA_DEV:
- res = sas_discover_sata_dev(dev);
- break;
- case SATA_PM:
- res = sas_discover_sata_pm(dev);
- break;
- default:
- break;
- }
- sas_notify_lldd_dev_gone(dev);
- if (!res) {
- sas_notify_lldd_dev_found(dev);
- res = sas_rphy_add(dev->rphy);
- }
+static void async_sas_ata_eh(void *data, async_cookie_t cookie)
+{
+ struct domain_device *dev = data;
+ struct ata_port *ap = dev->sata_dev.ap;
+ struct sas_ha_struct *ha = dev->port->ha;
- return res;
+ sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n");
+ ata_scsi_port_error_handler(ha->shost, ap);
+ sas_put_device(dev);
}
void sas_ata_strategy_handler(struct Scsi_Host *shost)
{
- struct scsi_device *sdev;
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ ASYNC_DOMAIN_EXCLUSIVE(async);
+ int i;
+
+ /* it's ok to defer revalidation events during ata eh, these
+ * disks are in one of three states:
+ * 1/ present for initial domain discovery, and these
+ * resets will cause bcn flutters
+ * 2/ hot removed, we'll discover that after eh fails
+ * 3/ hot added after initial discovery, lost the race, and need
+ * to catch the next train.
+ */
+ sas_disable_revalidation(sas_ha);
- shost_for_each_device(sdev, shost) {
- struct domain_device *ddev = sdev_to_domain_dev(sdev);
- struct ata_port *ap = ddev->sata_dev.ap;
+ spin_lock_irq(&sas_ha->phy_port_lock);
+ for (i = 0; i < sas_ha->num_phys; i++) {
+ struct asd_sas_port *port = sas_ha->sas_port[i];
+ struct domain_device *dev;
- if (!dev_is_sata(ddev))
- continue;
+ spin_lock(&port->dev_list_lock);
+ list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+ if (!dev_is_sata(dev))
+ continue;
- ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler");
- ata_scsi_port_error_handler(shost, ap);
- }
-}
+ /* hold a reference over eh since we may be
+ * racing with final remove once all commands
+ * are completed
+ */
+ kref_get(&dev->kref);
-int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task,
- enum blk_eh_timer_return *rtn)
-{
- struct domain_device *ddev = cmd_to_domain_dev(cmd);
+ async_schedule_domain(async_sas_ata_eh, dev, &async);
+ }
+ spin_unlock(&port->dev_list_lock);
+ }
+ spin_unlock_irq(&sas_ha->phy_port_lock);
- if (!dev_is_sata(ddev) || task)
- return 0;
+ async_synchronize_full_domain(&async);
- /* we're a sata device with no task, so this must be a libata
- * eh timeout. Ideally should hook into libata timeout
- * handling, but there's no point, it just wants to activate
- * the eh thread */
- *rtn = BLK_EH_NOT_HANDLED;
- return 1;
+ sas_enable_revalidation(sas_ha);
}
-int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
- struct list_head *done_q)
+void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q)
{
- int rtn = 0;
struct scsi_cmnd *cmd, *n;
- struct ata_port *ap;
+ struct domain_device *eh_dev;
do {
LIST_HEAD(sata_q);
-
- ap = NULL;
+ eh_dev = NULL;
list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
struct domain_device *ddev = cmd_to_domain_dev(cmd);
if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd))
continue;
- if (ap && ap != ddev->sata_dev.ap)
+ if (eh_dev && eh_dev != ddev)
continue;
- ap = ddev->sata_dev.ap;
- rtn = 1;
+ eh_dev = ddev;
list_move(&cmd->eh_entry, &sata_q);
}
if (!list_empty(&sata_q)) {
- ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata cmd error handler\n");
+ struct ata_port *ap = eh_dev->sata_dev.ap;
+
+ sas_ata_printk(KERN_DEBUG, eh_dev, "cmd error handler\n");
ata_scsi_cmd_error_handler(shost, ap, &sata_q);
/*
* ata's error handler may leave the cmd on the list
@@ -869,7 +902,140 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
while (!list_empty(&sata_q))
list_del_init(sata_q.next);
}
- } while (ap);
+ } while (eh_dev);
+}
+
+void sas_ata_schedule_reset(struct domain_device *dev)
+{
+ struct ata_eh_info *ehi;
+ struct ata_port *ap;
+ unsigned long flags;
+
+ if (!dev_is_sata(dev))
+ return;
- return rtn;
+ ap = dev->sata_dev.ap;
+ ehi = &ap->link.eh_info;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ehi->err_mask |= AC_ERR_TIMEOUT;
+ ehi->action |= ATA_EH_RESET;
+ ata_port_schedule_eh(ap);
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sas_ata_schedule_reset);
+
+void sas_ata_wait_eh(struct domain_device *dev)
+{
+ ata_port_wait_eh(dev->sata_dev.ap);
}
+
+void sas_ata_device_link_abort(struct domain_device *device, bool force_reset)
+{
+ struct ata_port *ap = device->sata_dev.ap;
+ struct ata_link *link = &ap->link;
+ unsigned long flags;
+
+ spin_lock_irqsave(ap->lock, flags);
+ device->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */
+ device->sata_dev.fis[3] = ATA_ABORTED; /* tf error */
+
+ link->eh_info.err_mask |= AC_ERR_DEV;
+ if (force_reset)
+ link->eh_info.action |= ATA_EH_RESET;
+ ata_link_abort(link);
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sas_ata_device_link_abort);
+
+int sas_execute_ata_cmd(struct domain_device *device, u8 *fis, int force_phy_id)
+{
+ struct sas_tmf_task tmf_task = {};
+ return sas_execute_tmf(device, fis, sizeof(struct host_to_dev_fis),
+ force_phy_id, &tmf_task);
+}
+EXPORT_SYMBOL_GPL(sas_execute_ata_cmd);
+
+static ssize_t sas_ncq_prio_supported_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(device);
+ struct domain_device *ddev = sdev_to_domain_dev(sdev);
+ bool supported;
+ int rc;
+
+ rc = ata_ncq_prio_supported(ddev->sata_dev.ap, sdev, &supported);
+ if (rc)
+ return rc;
+
+ return sysfs_emit(buf, "%d\n", supported);
+}
+
+static struct device_attribute dev_attr_sas_ncq_prio_supported =
+ __ATTR(ncq_prio_supported, S_IRUGO, sas_ncq_prio_supported_show, NULL);
+
+static ssize_t sas_ncq_prio_enable_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(device);
+ struct domain_device *ddev = sdev_to_domain_dev(sdev);
+ bool enabled;
+ int rc;
+
+ rc = ata_ncq_prio_enabled(ddev->sata_dev.ap, sdev, &enabled);
+ if (rc)
+ return rc;
+
+ return sysfs_emit(buf, "%d\n", enabled);
+}
+
+static ssize_t sas_ncq_prio_enable_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct scsi_device *sdev = to_scsi_device(device);
+ struct domain_device *ddev = sdev_to_domain_dev(sdev);
+ bool enable;
+ int rc;
+
+ rc = kstrtobool(buf, &enable);
+ if (rc)
+ return rc;
+
+ rc = ata_ncq_prio_enable(ddev->sata_dev.ap, sdev, enable);
+ if (rc)
+ return rc;
+
+ return len;
+}
+
+static struct device_attribute dev_attr_sas_ncq_prio_enable =
+ __ATTR(ncq_prio_enable, S_IRUGO | S_IWUSR,
+ sas_ncq_prio_enable_show, sas_ncq_prio_enable_store);
+
+static struct attribute *sas_ata_sdev_attrs[] = {
+ &dev_attr_sas_ncq_prio_supported.attr,
+ &dev_attr_sas_ncq_prio_enable.attr,
+ NULL
+};
+
+static umode_t sas_ata_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int i)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct domain_device *ddev = sdev_to_domain_dev(sdev);
+
+ if (!dev_is_sata(ddev))
+ return 0;
+
+ return attr->mode;
+}
+
+const struct attribute_group sas_ata_sdev_attr_group = {
+ .attrs = sas_ata_sdev_attrs,
+ .is_visible = sas_ata_attr_is_visible,
+};
+EXPORT_SYMBOL_GPL(sas_ata_sdev_attr_group);
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index f5831930df9b..b07062db50b2 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -1,25 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Serial Attached SCSI (SAS) Discover process
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/scatterlist.h>
@@ -30,35 +14,31 @@
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
-#include "../scsi_sas_internal.h"
+#include <scsi/sas_ata.h>
+#include "scsi_sas_internal.h"
/* ---------- Basic task processing for discovery purposes ---------- */
void sas_init_dev(struct domain_device *dev)
{
- INIT_LIST_HEAD(&dev->siblings);
- INIT_LIST_HEAD(&dev->dev_list_node);
- switch (dev->dev_type) {
- case SAS_END_DEV:
- break;
- case EDGE_DEV:
- case FANOUT_DEV:
- INIT_LIST_HEAD(&dev->ex_dev.children);
- break;
- case SATA_DEV:
- case SATA_PM:
- case SATA_PM_PORT:
- INIT_LIST_HEAD(&dev->sata_dev.children);
- break;
- default:
- break;
- }
+ switch (dev->dev_type) {
+ case SAS_END_DEVICE:
+ INIT_LIST_HEAD(&dev->ssp_dev.eh_list_node);
+ break;
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ INIT_LIST_HEAD(&dev->ex_dev.children);
+ mutex_init(&dev->ex_dev.cmd_mutex);
+ break;
+ default:
+ break;
+ }
}
/* ---------- Domain device discovery ---------- */
/**
- * sas_get_port_device -- Discover devices which caused port creation
+ * sas_get_port_device - Discover devices which caused port creation
* @port: pointer to struct sas_port of interest
*
* Devices directly attached to a HA port, have no parent. This is
@@ -68,19 +48,19 @@ void sas_init_dev(struct domain_device *dev)
*/
static int sas_get_port_device(struct asd_sas_port *port)
{
- unsigned long flags;
struct asd_sas_phy *phy;
struct sas_rphy *rphy;
struct domain_device *dev;
+ int rc = -ENODEV;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = sas_alloc_device();
if (!dev)
return -ENOMEM;
- spin_lock_irqsave(&port->phy_list_lock, flags);
+ spin_lock_irq(&port->phy_list_lock);
if (list_empty(&port->phy_list)) {
- spin_unlock_irqrestore(&port->phy_list_lock, flags);
- kfree(dev);
+ spin_unlock_irq(&port->phy_list_lock);
+ sas_put_device(dev);
return -ENODEV;
}
phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el);
@@ -88,57 +68,73 @@ static int sas_get_port_device(struct asd_sas_port *port)
memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd),
(size_t)phy->frame_rcvd_size));
spin_unlock(&phy->frame_rcvd_lock);
- spin_unlock_irqrestore(&port->phy_list_lock, flags);
+ spin_unlock_irq(&port->phy_list_lock);
if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) {
struct dev_to_host_fis *fis =
(struct dev_to_host_fis *) dev->frame_rcvd;
if (fis->interrupt_reason == 1 && fis->lbal == 1 &&
- fis->byte_count_low==0x69 && fis->byte_count_high == 0x96
+ fis->byte_count_low == 0x69 && fis->byte_count_high == 0x96
&& (fis->device & ~0x10) == 0)
- dev->dev_type = SATA_PM;
+ dev->dev_type = SAS_SATA_PM;
else
- dev->dev_type = SATA_DEV;
+ dev->dev_type = SAS_SATA_DEV;
dev->tproto = SAS_PROTOCOL_SATA;
- } else {
+ } else if (port->oob_mode == SAS_OOB_MODE) {
struct sas_identify_frame *id =
(struct sas_identify_frame *) dev->frame_rcvd;
dev->dev_type = id->dev_type;
dev->iproto = id->initiator_bits;
dev->tproto = id->target_bits;
+ } else {
+ /* If the oob mode is OOB_NOT_CONNECTED, the port is
+ * disconnected due to race with PHY down. We cannot
+ * continue to discover this port
+ */
+ sas_put_device(dev);
+ pr_warn("Port %016llx is disconnected when discovering\n",
+ SAS_ADDR(port->attached_sas_addr));
+ return -ENODEV;
}
sas_init_dev(dev);
+ dev->port = port;
switch (dev->dev_type) {
- case SAS_END_DEV:
- case SATA_DEV:
+ case SAS_SATA_DEV:
+ rc = sas_ata_init(dev);
+ if (rc) {
+ rphy = NULL;
+ break;
+ }
+ fallthrough;
+ case SAS_END_DEVICE:
rphy = sas_end_device_alloc(port->port);
break;
- case EDGE_DEV:
+ case SAS_EDGE_EXPANDER_DEVICE:
rphy = sas_expander_alloc(port->port,
SAS_EDGE_EXPANDER_DEVICE);
break;
- case FANOUT_DEV:
+ case SAS_FANOUT_EXPANDER_DEVICE:
rphy = sas_expander_alloc(port->port,
SAS_FANOUT_EXPANDER_DEVICE);
break;
default:
- printk("ERROR: Unidentified device type %d\n", dev->dev_type);
+ pr_warn("ERROR: Unidentified device type %d\n", dev->dev_type);
rphy = NULL;
break;
}
if (!rphy) {
- kfree(dev);
- return -ENODEV;
+ sas_put_device(dev);
+ return rc;
}
+
rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;
memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);
sas_fill_in_rphy(dev, rphy);
sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
port->port_dev = dev;
- dev->port = port;
dev->linkrate = port->linkrate;
dev->min_linkrate = port->linkrate;
dev->max_linkrate = port->linkrate;
@@ -147,11 +143,23 @@ static int sas_get_port_device(struct asd_sas_port *port)
memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE);
memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE);
port->disc.max_level = 0;
+ sas_device_set_phy(dev, port->port);
dev->rphy = rphy;
- spin_lock_irq(&port->dev_list_lock);
- list_add_tail(&dev->dev_list_node, &port->dev_list);
- spin_unlock_irq(&port->dev_list_lock);
+ get_device(&dev->rphy->dev);
+
+ if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEVICE)
+ list_add_tail(&dev->disco_list_node, &port->disco_list);
+ else {
+ spin_lock_irq(&port->dev_list_lock);
+ list_add_tail(&dev->dev_list_node, &port->dev_list);
+ spin_unlock_irq(&port->dev_list_lock);
+ }
+
+ spin_lock_irq(&port->phy_list_lock);
+ list_for_each_entry(phy, &port->phy_list, port_phy_el)
+ sas_phy_set_target(phy, dev);
+ spin_unlock_irq(&port->phy_list_lock);
return 0;
}
@@ -162,104 +170,284 @@ int sas_notify_lldd_dev_found(struct domain_device *dev)
{
int res = 0;
struct sas_ha_struct *sas_ha = dev->port->ha;
- struct Scsi_Host *shost = sas_ha->core.shost;
+ struct Scsi_Host *shost = sas_ha->shost;
struct sas_internal *i = to_sas_internal(shost->transportt);
- if (i->dft->lldd_dev_found) {
- res = i->dft->lldd_dev_found(dev);
- if (res) {
- printk("sas: driver on pcidev %s cannot handle "
- "device %llx, error:%d\n",
- dev_name(sas_ha->dev),
- SAS_ADDR(dev->sas_addr), res);
- }
+ if (!i->dft->lldd_dev_found)
+ return 0;
+
+ res = i->dft->lldd_dev_found(dev);
+ if (res) {
+ pr_warn("driver on host %s cannot handle device %016llx, error:%d\n",
+ dev_name(sas_ha->dev),
+ SAS_ADDR(dev->sas_addr), res);
+ return res;
}
- return res;
+ set_bit(SAS_DEV_FOUND, &dev->state);
+ kref_get(&dev->kref);
+ return 0;
}
void sas_notify_lldd_dev_gone(struct domain_device *dev)
{
struct sas_ha_struct *sas_ha = dev->port->ha;
- struct Scsi_Host *shost = sas_ha->core.shost;
+ struct Scsi_Host *shost = sas_ha->shost;
struct sas_internal *i = to_sas_internal(shost->transportt);
- if (i->dft->lldd_dev_gone)
+ if (!i->dft->lldd_dev_gone)
+ return;
+
+ if (test_and_clear_bit(SAS_DEV_FOUND, &dev->state)) {
i->dft->lldd_dev_gone(dev);
+ sas_put_device(dev);
+ }
}
-/* ---------- Common/dispatchers ---------- */
+static void sas_probe_devices(struct asd_sas_port *port)
+{
+ struct domain_device *dev, *n;
+
+ /* devices must be domain members before link recovery and probe */
+ list_for_each_entry(dev, &port->disco_list, disco_list_node) {
+ spin_lock_irq(&port->dev_list_lock);
+ list_add_tail(&dev->dev_list_node, &port->dev_list);
+ spin_unlock_irq(&port->dev_list_lock);
+ }
+ sas_probe_sata(port);
+
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
+ int err;
+
+ err = sas_rphy_add(dev->rphy);
+ if (err)
+ sas_fail_probe(dev, __func__, err);
+ else
+ list_del_init(&dev->disco_list_node);
+ }
+}
+
+static void sas_suspend_devices(struct work_struct *work)
+{
+ struct asd_sas_phy *phy;
+ struct domain_device *dev;
+ struct sas_discovery_event *ev = to_sas_discovery_event(work);
+ struct asd_sas_port *port = ev->port;
+ struct Scsi_Host *shost = port->ha->shost;
+ struct sas_internal *si = to_sas_internal(shost->transportt);
+
+ clear_bit(DISCE_SUSPEND, &port->disc.pending);
+
+ sas_suspend_sata(port);
+
+ /* lldd is free to forget the domain_device across the
+ * suspension, we force the issue here to keep the reference
+ * counts aligned
+ */
+ list_for_each_entry(dev, &port->dev_list, dev_list_node)
+ sas_notify_lldd_dev_gone(dev);
+
+ /* we are suspending, so we know events are disabled and
+ * phy_list is not being mutated
+ */
+ list_for_each_entry(phy, &port->phy_list, port_phy_el) {
+ if (si->dft->lldd_port_deformed)
+ si->dft->lldd_port_deformed(phy);
+ phy->suspended = 1;
+ port->suspended = 1;
+ }
+}
+
+static void sas_resume_devices(struct work_struct *work)
+{
+ struct sas_discovery_event *ev = to_sas_discovery_event(work);
+ struct asd_sas_port *port = ev->port;
+
+ clear_bit(DISCE_RESUME, &port->disc.pending);
+
+ sas_resume_sata(port);
+}
/**
- * sas_discover_end_dev -- discover an end device (SSP, etc)
- * @end: pointer to domain device of interest
+ * sas_discover_end_dev - discover an end device (SSP, etc)
+ * @dev: pointer to domain device of interest
*
* See comment in sas_discover_sata().
*/
-int sas_discover_end_dev(struct domain_device *dev)
+static int sas_discover_end_dev(struct domain_device *dev)
{
- int res;
+ return sas_notify_lldd_dev_found(dev);
+}
+
+/* ---------- Device registration and unregistration ---------- */
- res = sas_notify_lldd_dev_found(dev);
- if (res)
- goto out_err2;
+void sas_free_device(struct kref *kref)
+{
+ struct domain_device *dev = container_of(kref, typeof(*dev), kref);
- res = sas_rphy_add(dev->rphy);
- if (res)
- goto out_err;
+ put_device(&dev->rphy->dev);
+ dev->rphy = NULL;
- return 0;
+ if (dev->parent)
+ sas_put_device(dev->parent);
-out_err:
- sas_notify_lldd_dev_gone(dev);
-out_err2:
- return res;
-}
+ sas_port_put_phy(dev->phy);
+ dev->phy = NULL;
-/* ---------- Device registration and unregistration ---------- */
+ /* remove the phys and ports, everything else should be gone */
+ if (dev_is_expander(dev->dev_type))
+ kfree(dev->ex_dev.ex_phy);
-static inline void sas_unregister_common_dev(struct domain_device *dev)
+ if (dev_is_sata(dev) && dev->sata_dev.ap) {
+ ata_tport_delete(dev->sata_dev.ap);
+ ata_port_free(dev->sata_dev.ap);
+ ata_host_put(dev->sata_dev.ata_host);
+ dev->sata_dev.ata_host = NULL;
+ dev->sata_dev.ap = NULL;
+ }
+
+ kfree(dev);
+}
+
+static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev)
{
+ struct sas_ha_struct *ha = port->ha;
+
sas_notify_lldd_dev_gone(dev);
if (!dev->parent)
dev->port->port_dev = NULL;
else
list_del_init(&dev->siblings);
+
+ spin_lock_irq(&port->dev_list_lock);
list_del_init(&dev->dev_list_node);
+ if (dev_is_sata(dev))
+ sas_ata_end_eh(dev->sata_dev.ap);
+ spin_unlock_irq(&port->dev_list_lock);
+
+ spin_lock_irq(&ha->lock);
+ if (dev->dev_type == SAS_END_DEVICE &&
+ !list_empty(&dev->ssp_dev.eh_list_node)) {
+ list_del_init(&dev->ssp_dev.eh_list_node);
+ ha->eh_active--;
+ }
+ spin_unlock_irq(&ha->lock);
+
+ sas_put_device(dev);
}
-void sas_unregister_dev(struct domain_device *dev)
+void sas_destruct_devices(struct asd_sas_port *port)
{
- if (dev->rphy) {
+ struct domain_device *dev, *n;
+
+ list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) {
+ list_del_init(&dev->disco_list_node);
+
sas_remove_children(&dev->rphy->dev);
sas_rphy_delete(dev->rphy);
- dev->rphy = NULL;
+ sas_unregister_common_dev(port, dev);
}
- if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) {
- /* remove the phys and ports, everything else should be gone */
- kfree(dev->ex_dev.ex_phy);
- dev->ex_dev.ex_phy = NULL;
+}
+
+static void sas_destruct_ports(struct asd_sas_port *port)
+{
+ struct sas_port *sas_port, *p;
+
+ list_for_each_entry_safe(sas_port, p, &port->sas_port_del_list, del_list) {
+ list_del_init(&sas_port->del_list);
+ sas_port_delete(sas_port);
+ }
+}
+
+static bool sas_abort_cmd(struct request *req, void *data)
+{
+ struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
+ struct domain_device *dev = data;
+
+ if (dev == cmd_to_domain_dev(cmd))
+ blk_abort_request(req);
+ return true;
+}
+
+static void sas_abort_device_scsi_cmds(struct domain_device *dev)
+{
+ struct sas_ha_struct *sas_ha = dev->port->ha;
+ struct Scsi_Host *shost = sas_ha->shost;
+
+ if (dev_is_expander(dev->dev_type))
+ return;
+
+ /*
+ * For removed device with active IOs, the user space applications have
+ * to spend very long time waiting for the timeout. This is not
+ * necessary because a removed device will not return the IOs.
+ * Abort the inflight IOs here so that EH can be quickly kicked in.
+ */
+ blk_mq_tagset_busy_iter(&shost->tag_set, sas_abort_cmd, dev);
+}
+
+void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
+{
+ if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&
+ !list_empty(&dev->disco_list_node)) {
+ /* this rphy never saw sas_rphy_add */
+ list_del_init(&dev->disco_list_node);
+ sas_rphy_free(dev->rphy);
+ sas_unregister_common_dev(port, dev);
+ return;
+ }
+
+ if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
+ if (test_bit(SAS_DEV_GONE, &dev->state))
+ sas_abort_device_scsi_cmds(dev);
+ sas_rphy_unlink(dev->rphy);
+ list_move_tail(&dev->disco_list_node, &port->destroy_list);
}
- sas_unregister_common_dev(dev);
}
-void sas_unregister_domain_devices(struct asd_sas_port *port)
+void sas_unregister_domain_devices(struct asd_sas_port *port, bool gone)
{
struct domain_device *dev, *n;
- list_for_each_entry_safe_reverse(dev,n,&port->dev_list,dev_list_node)
- sas_unregister_dev(dev);
+ list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) {
+ if (gone)
+ set_bit(SAS_DEV_GONE, &dev->state);
+ sas_unregister_dev(port, dev);
+ }
+
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node)
+ sas_unregister_dev(port, dev);
port->port->rphy = NULL;
}
+void sas_device_set_phy(struct domain_device *dev, struct sas_port *port)
+{
+ struct sas_ha_struct *ha;
+ struct sas_phy *new_phy;
+
+ if (!dev)
+ return;
+
+ ha = dev->port->ha;
+ new_phy = sas_port_get_phy(port);
+
+ /* pin and record last seen phy */
+ spin_lock_irq(&ha->phy_port_lock);
+ if (new_phy) {
+ sas_port_put_phy(dev->phy);
+ dev->phy = new_phy;
+ }
+ spin_unlock_irq(&ha->phy_port_lock);
+}
+
/* ---------- Discovery and Revalidation ---------- */
/**
- * sas_discover_domain -- discover the domain
- * @port: port to the domain of interest
+ * sas_discover_domain - discover the domain
+ * @work: work structure embedded in port domain device.
*
* NOTE: this process _must_ quit (return) as soon as any connection
* errors are encountered. Connection recovery is done elsewhere.
@@ -270,12 +458,10 @@ static void sas_discover_domain(struct work_struct *work)
{
struct domain_device *dev;
int error = 0;
- struct sas_discovery_event *ev =
- container_of(work, struct sas_discovery_event, work);
+ struct sas_discovery_event *ev = to_sas_discovery_event(work);
struct asd_sas_port *port = ev->port;
- sas_begin_event(DISCE_DISCOVER_DOMAIN, &port->disc.disc_event_lock,
- &port->disc.pending);
+ clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);
if (port->port_dev)
return;
@@ -285,87 +471,119 @@ static void sas_discover_domain(struct work_struct *work)
return;
dev = port->port_dev;
- SAS_DPRINTK("DOING DISCOVERY on port %d, pid:%d\n", port->id,
- task_pid_nr(current));
+ pr_debug("DOING DISCOVERY on port %d, pid:%d\n", port->id,
+ task_pid_nr(current));
switch (dev->dev_type) {
- case SAS_END_DEV:
+ case SAS_END_DEVICE:
error = sas_discover_end_dev(dev);
break;
- case EDGE_DEV:
- case FANOUT_DEV:
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
error = sas_discover_root_expander(dev);
break;
- case SATA_DEV:
- case SATA_PM:
-#ifdef CONFIG_SCSI_SAS_ATA
+ case SAS_SATA_DEV:
+ case SAS_SATA_PM:
error = sas_discover_sata(dev);
break;
-#else
- SAS_DPRINTK("ATA device seen but CONFIG_SCSI_SAS_ATA=N so cannot attach\n");
- /* Fall through */
-#endif
default:
error = -ENXIO;
- SAS_DPRINTK("unhandled device %d\n", dev->dev_type);
+ pr_err("unhandled device %d\n", dev->dev_type);
break;
}
if (error) {
sas_rphy_free(dev->rphy);
- dev->rphy = NULL;
-
+ list_del_init(&dev->disco_list_node);
spin_lock_irq(&port->dev_list_lock);
list_del_init(&dev->dev_list_node);
spin_unlock_irq(&port->dev_list_lock);
- kfree(dev); /* not kobject_register-ed yet */
+ sas_put_device(dev);
port->port_dev = NULL;
}
- SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id,
- task_pid_nr(current), error);
+ sas_probe_devices(port);
+
+ pr_debug("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id,
+ task_pid_nr(current), error);
}
static void sas_revalidate_domain(struct work_struct *work)
{
int res = 0;
- struct sas_discovery_event *ev =
- container_of(work, struct sas_discovery_event, work);
+ struct sas_discovery_event *ev = to_sas_discovery_event(work);
struct asd_sas_port *port = ev->port;
+ struct sas_ha_struct *ha = port->ha;
+ struct domain_device *ddev = port->port_dev;
+
+ /* prevent revalidation from finding sata links in recovery */
+ mutex_lock(&ha->disco_mutex);
+ if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) {
+ pr_debug("REVALIDATION DEFERRED on port %d, pid:%d\n",
+ port->id, task_pid_nr(current));
+ goto out;
+ }
- sas_begin_event(DISCE_REVALIDATE_DOMAIN, &port->disc.disc_event_lock,
- &port->disc.pending);
+ clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending);
- SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
- task_pid_nr(current));
- if (port->port_dev)
- res = sas_ex_revalidate_domain(port->port_dev);
+ pr_debug("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
+ task_pid_nr(current));
+
+ if (ddev && dev_is_expander(ddev->dev_type))
+ res = sas_ex_revalidate_domain(ddev);
+
+ pr_debug("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
+ port->id, task_pid_nr(current), res);
+ out:
+ mutex_unlock(&ha->disco_mutex);
- SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
- port->id, task_pid_nr(current), res);
+ sas_destruct_devices(port);
+ sas_destruct_ports(port);
+ sas_probe_devices(port);
}
/* ---------- Events ---------- */
-int sas_discover_event(struct asd_sas_port *port, enum discover_event ev)
+static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw)
+{
+ /* chained work is not subject to SA_HA_DRAINING or
+ * SAS_HA_REGISTERED, because it is either submitted in the
+ * workqueue, or known to be submitted from a context that is
+ * not racing against draining
+ */
+ queue_work(ha->disco_q, &sw->work);
+}
+
+static void sas_chain_event(int event, unsigned long *pending,
+ struct sas_work *sw,
+ struct sas_ha_struct *ha)
+{
+ if (!test_and_set_bit(event, pending)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->lock, flags);
+ sas_chain_work(ha, sw);
+ spin_unlock_irqrestore(&ha->lock, flags);
+ }
+}
+
+void sas_discover_event(struct asd_sas_port *port, enum discover_event ev)
{
struct sas_discovery *disc;
if (!port)
- return 0;
+ return;
disc = &port->disc;
BUG_ON(ev >= DISC_NUM_EVENTS);
- sas_queue_event(ev, &disc->disc_event_lock, &disc->pending,
- &disc->disc_work[ev].work, port->ha);
-
- return 0;
+ sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha);
}
/**
- * sas_init_disc -- initialize the discovery struct in the port
+ * sas_init_disc - initialize the discovery struct in the port
+ * @disc: port discovery structure
* @port: pointer to struct port
*
* Called when the ports are being initialized.
@@ -377,12 +595,13 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
+ [DISCE_SUSPEND] = sas_suspend_devices,
+ [DISCE_RESUME] = sas_resume_devices,
};
- spin_lock_init(&disc->disc_event_lock);
disc->pending = 0;
for (i = 0; i < DISC_NUM_EVENTS; i++) {
- INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
+ INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
disc->disc_work[i].port = port;
}
}
diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c
deleted file mode 100644
index fc460933575c..000000000000
--- a/drivers/scsi/libsas/sas_dump.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Serial Attached SCSI (SAS) Dump/Debugging routines
- *
- * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
- * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include "sas_dump.h"
-
-static const char *sas_hae_str[] = {
- [0] = "HAE_RESET",
-};
-
-static const char *sas_porte_str[] = {
- [0] = "PORTE_BYTES_DMAED",
- [1] = "PORTE_BROADCAST_RCVD",
- [2] = "PORTE_LINK_RESET_ERR",
- [3] = "PORTE_TIMER_EVENT",
- [4] = "PORTE_HARD_RESET",
-};
-
-static const char *sas_phye_str[] = {
- [0] = "PHYE_LOSS_OF_SIGNAL",
- [1] = "PHYE_OOB_DONE",
- [2] = "PHYE_OOB_ERROR",
- [3] = "PHYE_SPINUP_HOLD",
-};
-
-void sas_dprint_porte(int phyid, enum port_event pe)
-{
- SAS_DPRINTK("phy%d: port event: %s\n", phyid, sas_porte_str[pe]);
-}
-void sas_dprint_phye(int phyid, enum phy_event pe)
-{
- SAS_DPRINTK("phy%d: phy event: %s\n", phyid, sas_phye_str[pe]);
-}
-
-void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he)
-{
- SAS_DPRINTK("ha %s: %s event\n", dev_name(sas_ha->dev),
- sas_hae_str[he]);
-}
-
-void sas_dump_port(struct asd_sas_port *port)
-{
- SAS_DPRINTK("port%d: class:0x%x\n", port->id, port->class);
- SAS_DPRINTK("port%d: sas_addr:%llx\n", port->id,
- SAS_ADDR(port->sas_addr));
- SAS_DPRINTK("port%d: attached_sas_addr:%llx\n", port->id,
- SAS_ADDR(port->attached_sas_addr));
- SAS_DPRINTK("port%d: iproto:0x%x\n", port->id, port->iproto);
- SAS_DPRINTK("port%d: tproto:0x%x\n", port->id, port->tproto);
- SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode);
- SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys);
-}
diff --git a/drivers/scsi/libsas/sas_dump.h b/drivers/scsi/libsas/sas_dump.h
deleted file mode 100644
index 800e4c69093f..000000000000
--- a/drivers/scsi/libsas/sas_dump.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Serial Attached SCSI (SAS) Dump/Debugging routines header file
- *
- * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
- * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include "sas_internal.h"
-
-void sas_dprint_porte(int phyid, enum port_event pe);
-void sas_dprint_phye(int phyid, enum phy_event pe);
-void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he);
-void sas_dump_port(struct asd_sas_port *port);
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c
index 9db30fb5caf2..f3a17191a4fe 100644
--- a/drivers/scsi/libsas/sas_event.c
+++ b/drivers/scsi/libsas/sas_event.c
@@ -1,77 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Serial Attached SCSI (SAS) Event processing
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
+#include <linux/export.h>
#include <scsi/scsi_host.h>
#include "sas_internal.h"
-#include "sas_dump.h"
-static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event)
+bool sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
+{
+ if (!test_bit(SAS_HA_REGISTERED, &ha->state))
+ return false;
+
+ if (test_bit(SAS_HA_DRAINING, &ha->state)) {
+ /* add it to the defer list, if not already pending */
+ if (list_empty(&sw->drain_node))
+ list_add_tail(&sw->drain_node, &ha->defer_q);
+ return true;
+ }
+
+ return queue_work(ha->event_q, &sw->work);
+}
+
+static bool sas_queue_event(int event, struct sas_work *work,
+ struct sas_ha_struct *ha)
+{
+ unsigned long flags;
+ bool rc;
+
+ spin_lock_irqsave(&ha->lock, flags);
+ rc = sas_queue_work(ha, work);
+ spin_unlock_irqrestore(&ha->lock, flags);
+
+ return rc;
+}
+
+void sas_queue_deferred_work(struct sas_ha_struct *ha)
+{
+ struct sas_work *sw, *_sw;
+
+ spin_lock_irq(&ha->lock);
+ list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
+ list_del_init(&sw->drain_node);
+
+ if (!sas_queue_work(ha, sw)) {
+ pm_runtime_put(ha->dev);
+ sas_free_event(to_asd_sas_event(&sw->work));
+ }
+ }
+ spin_unlock_irq(&ha->lock);
+}
+
+void __sas_drain_work(struct sas_ha_struct *ha)
+{
+ set_bit(SAS_HA_DRAINING, &ha->state);
+ /* flush submitters */
+ spin_lock_irq(&ha->lock);
+ spin_unlock_irq(&ha->lock);
+
+ drain_workqueue(ha->event_q);
+ drain_workqueue(ha->disco_q);
+
+ clear_bit(SAS_HA_DRAINING, &ha->state);
+ sas_queue_deferred_work(ha);
+}
+
+int sas_drain_work(struct sas_ha_struct *ha)
+{
+ int err;
+
+ err = mutex_lock_interruptible(&ha->drain_mutex);
+ if (err)
+ return err;
+ if (test_bit(SAS_HA_REGISTERED, &ha->state))
+ __sas_drain_work(ha);
+ mutex_unlock(&ha->drain_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sas_drain_work);
+
+void sas_disable_revalidation(struct sas_ha_struct *ha)
{
- BUG_ON(event >= HA_NUM_EVENTS);
+ mutex_lock(&ha->disco_mutex);
+ set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
+ mutex_unlock(&ha->disco_mutex);
+}
+
+void sas_enable_revalidation(struct sas_ha_struct *ha)
+{
+ int i;
+
+ mutex_lock(&ha->disco_mutex);
+ clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
+ for (i = 0; i < ha->num_phys; i++) {
+ struct asd_sas_port *port = ha->sas_port[i];
+ const int ev = DISCE_REVALIDATE_DOMAIN;
+ struct sas_discovery *d = &port->disc;
+ struct asd_sas_phy *sas_phy;
- sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending,
- &sas_ha->ha_events[event].work, sas_ha);
+ if (!test_and_clear_bit(ev, &d->pending))
+ continue;
+
+ spin_lock(&port->phy_list_lock);
+ if (list_empty(&port->phy_list)) {
+ spin_unlock(&port->phy_list_lock);
+ continue;
+ }
+
+ sas_phy = container_of(port->phy_list.next, struct asd_sas_phy,
+ port_phy_el);
+ spin_unlock(&port->phy_list_lock);
+ sas_notify_port_event(sas_phy,
+ PORTE_BROADCAST_RCVD, GFP_KERNEL);
+ }
+ mutex_unlock(&ha->disco_mutex);
}
-static void notify_port_event(struct asd_sas_phy *phy, enum port_event event)
+
+static void sas_port_event_worker(struct work_struct *work)
{
+ struct asd_sas_event *ev = to_asd_sas_event(work);
+ struct asd_sas_phy *phy = ev->phy;
struct sas_ha_struct *ha = phy->ha;
- BUG_ON(event >= PORT_NUM_EVENTS);
+ sas_port_event_fns[ev->event](work);
+ pm_runtime_put(ha->dev);
+ sas_free_event(ev);
+}
+
+static void sas_phy_event_worker(struct work_struct *work)
+{
+ struct asd_sas_event *ev = to_asd_sas_event(work);
+ struct asd_sas_phy *phy = ev->phy;
+ struct sas_ha_struct *ha = phy->ha;
- sas_queue_event(event, &ha->event_lock, &phy->port_events_pending,
- &phy->port_events[event].work, ha);
+ sas_phy_event_fns[ev->event](work);
+ pm_runtime_put(ha->dev);
+ sas_free_event(ev);
}
-static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
+/* defer works of new phys during suspend */
+static bool sas_defer_event(struct asd_sas_phy *phy, struct asd_sas_event *ev)
{
struct sas_ha_struct *ha = phy->ha;
+ unsigned long flags;
+ bool deferred = false;
- BUG_ON(event >= PHY_NUM_EVENTS);
+ spin_lock_irqsave(&ha->lock, flags);
+ if (test_bit(SAS_HA_RESUMING, &ha->state) && !phy->suspended) {
+ struct sas_work *sw = &ev->work;
- sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending,
- &phy->phy_events[event].work, ha);
+ list_add_tail(&sw->drain_node, &ha->defer_q);
+ deferred = true;
+ }
+ spin_unlock_irqrestore(&ha->lock, flags);
+ return deferred;
}
-int sas_init_events(struct sas_ha_struct *sas_ha)
+void sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,
+ gfp_t gfp_flags)
{
- static const work_func_t sas_ha_event_fns[HA_NUM_EVENTS] = {
- [HAE_RESET] = sas_hae_reset,
- };
+ struct sas_ha_struct *ha = phy->ha;
+ struct asd_sas_event *ev;
- int i;
+ BUG_ON(event >= PORT_NUM_EVENTS);
+
+ ev = sas_alloc_event(phy, gfp_flags);
+ if (!ev)
+ return;
+
+ /* Call pm_runtime_put() with pairs in sas_port_event_worker() */
+ pm_runtime_get_noresume(ha->dev);
- spin_lock_init(&sas_ha->event_lock);
+ INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event);
- for (i = 0; i < HA_NUM_EVENTS; i++) {
- INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]);
- sas_ha->ha_events[i].ha = sas_ha;
+ if (sas_defer_event(phy, ev))
+ return;
+
+ if (!sas_queue_event(event, &ev->work, ha)) {
+ pm_runtime_put(ha->dev);
+ sas_free_event(ev);
}
+}
+EXPORT_SYMBOL_GPL(sas_notify_port_event);
+
+void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
+ gfp_t gfp_flags)
+{
+ struct sas_ha_struct *ha = phy->ha;
+ struct asd_sas_event *ev;
+
+ BUG_ON(event >= PHY_NUM_EVENTS);
- sas_ha->notify_ha_event = notify_ha_event;
- sas_ha->notify_port_event = notify_port_event;
- sas_ha->notify_phy_event = notify_phy_event;
+ ev = sas_alloc_event(phy, gfp_flags);
+ if (!ev)
+ return;
- return 0;
+ /* Call pm_runtime_put() with pairs in sas_phy_event_worker() */
+ pm_runtime_get_noresume(ha->dev);
+
+ INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event);
+
+ if (sas_defer_event(phy, ev))
+ return;
+
+ if (!sas_queue_event(event, &ev->work, ha)) {
+ pm_runtime_put(ha->dev);
+ sas_free_event(ev);
+ }
}
+EXPORT_SYMBOL_GPL(sas_notify_phy_event);
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 874e29d9533f..d953225f6cc2 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Serial Attached SCSI (SAS) Expander discovery and configuration
*
@@ -5,32 +6,19 @@
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
*
* This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/scatterlist.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
+#include <linux/unaligned.h>
#include "sas_internal.h"
+#include <scsi/sas_ata.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
-#include "../scsi_sas_internal.h"
+#include "scsi_sas_internal.h"
static int sas_discover_expander(struct domain_device *dev);
static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr);
@@ -38,111 +26,138 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id,
u8 *sas_addr, int include);
static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr);
-/* ---------- SMP task management ---------- */
-
-static void smp_task_timedout(unsigned long _task)
+static void sas_port_add_ex_phy(struct sas_port *port, struct ex_phy *ex_phy)
{
- struct sas_task *task = (void *) _task;
- unsigned long flags;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- complete(&task->completion);
+ sas_port_add_phy(port, ex_phy->phy);
+ ex_phy->port = port;
+ ex_phy->phy_state = PHY_DEVICE_DISCOVERED;
}
-static void smp_task_done(struct sas_task *task)
+static void sas_ex_add_parent_port(struct domain_device *dev, int phy_id)
{
- if (!del_timer(&task->timer))
- return;
- complete(&task->completion);
+ struct expander_device *ex = &dev->ex_dev;
+ struct ex_phy *ex_phy = &ex->ex_phy[phy_id];
+
+ if (!ex->parent_port) {
+ ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id);
+ /* FIXME: error handling */
+ BUG_ON(!ex->parent_port);
+ BUG_ON(sas_port_add(ex->parent_port));
+ sas_port_mark_backlink(ex->parent_port);
+ }
+ sas_port_add_ex_phy(ex->parent_port, ex_phy);
}
+/* ---------- SMP task management ---------- */
+
/* Give it some long enough timeout. In seconds. */
#define SMP_TIMEOUT 10
-static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
- void *resp, int resp_size)
+static int smp_execute_task_sg(struct domain_device *dev,
+ struct scatterlist *req, struct scatterlist *resp)
{
int res, retry;
struct sas_task *task = NULL;
struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
+ to_sas_internal(dev->port->ha->shost->transportt);
+ struct sas_ha_struct *ha = dev->port->ha;
+ pm_runtime_get_sync(ha->dev);
+ mutex_lock(&dev->ex_dev.cmd_mutex);
for (retry = 0; retry < 3; retry++) {
- task = sas_alloc_task(GFP_KERNEL);
- if (!task)
- return -ENOMEM;
+ if (test_bit(SAS_DEV_GONE, &dev->state)) {
+ res = -ECOMM;
+ break;
+ }
+ task = sas_alloc_slow_task(GFP_KERNEL);
+ if (!task) {
+ res = -ENOMEM;
+ break;
+ }
task->dev = dev;
task->task_proto = dev->tproto;
- sg_init_one(&task->smp_task.smp_req, req, req_size);
- sg_init_one(&task->smp_task.smp_resp, resp, resp_size);
+ task->smp_task.smp_req = *req;
+ task->smp_task.smp_resp = *resp;
- task->task_done = smp_task_done;
+ task->task_done = sas_task_internal_done;
- task->timer.data = (unsigned long) task;
- task->timer.function = smp_task_timedout;
- task->timer.expires = jiffies + SMP_TIMEOUT*HZ;
- add_timer(&task->timer);
+ task->slow_task->timer.function = sas_task_internal_timedout;
+ task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ;
+ add_timer(&task->slow_task->timer);
- res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL);
+ res = i->dft->lldd_execute_task(task, GFP_KERNEL);
if (res) {
- del_timer(&task->timer);
- SAS_DPRINTK("executing SMP task failed:%d\n", res);
- goto ex_err;
+ timer_delete_sync(&task->slow_task->timer);
+ pr_notice("executing SMP task failed:%d\n", res);
+ break;
}
- wait_for_completion(&task->completion);
+ wait_for_completion(&task->slow_task->completion);
res = -ECOMM;
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- SAS_DPRINTK("smp task timed out or aborted\n");
+ pr_notice("smp task timed out or aborted\n");
i->dft->lldd_abort_task(task);
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- SAS_DPRINTK("SMP task aborted and not done\n");
- goto ex_err;
+ pr_notice("SMP task aborted and not done\n");
+ break;
}
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAM_STAT_GOOD) {
+ task->task_status.stat == SAS_SAM_STAT_GOOD) {
res = 0;
break;
- } if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_UNDERRUN) {
+ }
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_UNDERRUN) {
/* no error, but return the number of bytes of
* underrun */
res = task->task_status.residual;
break;
- } if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_OVERRUN) {
+ }
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_OVERRUN) {
res = -EMSGSIZE;
break;
- } else {
- SAS_DPRINTK("%s: task to dev %016llx response: 0x%x "
- "status 0x%x\n", __func__,
- SAS_ADDR(dev->sas_addr),
- task->task_status.resp,
- task->task_status.stat);
+ }
+ if (task->task_status.resp == SAS_TASK_UNDELIVERED &&
+ task->task_status.stat == SAS_DEVICE_UNKNOWN)
+ break;
+ else {
+ pr_notice("%s: task to dev %016llx response: 0x%x status 0x%x\n",
+ __func__,
+ SAS_ADDR(dev->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat);
sas_free_task(task);
task = NULL;
}
}
-ex_err:
+ mutex_unlock(&dev->ex_dev.cmd_mutex);
+ pm_runtime_put_sync(ha->dev);
+
BUG_ON(retry == 3 && task != NULL);
- if (task != NULL) {
- sas_free_task(task);
- }
+ sas_free_task(task);
return res;
}
+static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
+ void *resp, int resp_size)
+{
+ struct scatterlist req_sg;
+ struct scatterlist resp_sg;
+
+ sg_init_one(&req_sg, req, req_size);
+ sg_init_one(&resp_sg, resp, resp_size);
+ return smp_execute_task_sg(dev, &req_sg, &resp_sg);
+}
+
/* ---------- Allocations ---------- */
static inline void *alloc_smp_req(int size)
{
- u8 *p = kzalloc(size, GFP_KERNEL);
+ u8 *p = kzalloc(ALIGN(size, ARCH_DMA_MINALIGN), GFP_KERNEL);
if (p)
p[0] = SMP_REQUEST;
return p;
@@ -153,26 +168,59 @@ static inline void *alloc_smp_resp(int size)
return kzalloc(size, GFP_KERNEL);
}
-/* ---------- Expander configuration ---------- */
+static char sas_route_char(struct domain_device *dev, struct ex_phy *phy)
+{
+ switch (phy->routing_attr) {
+ case TABLE_ROUTING:
+ if (dev->ex_dev.t2t_supp)
+ return 'U';
+ else
+ return 'T';
+ case DIRECT_ROUTING:
+ return 'D';
+ case SUBTRACTIVE_ROUTING:
+ return 'S';
+ default:
+ return '?';
+ }
+}
+
+static enum sas_device_type to_dev_type(struct discover_resp *dr)
+{
+ /* This is detecting a failure to transmit initial dev to host
+ * FIS as described in section J.5 of sas-2 r16
+ */
+ if (dr->attached_dev_type == SAS_PHY_UNUSED && dr->attached_sata_dev &&
+ dr->linkrate >= SAS_LINK_RATE_1_5_GBPS)
+ return SAS_SATA_PENDING;
+ else
+ return dr->attached_dev_type;
+}
static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
- void *disc_resp)
+ struct smp_disc_resp *disc_resp)
{
+ enum sas_device_type dev_type;
+ enum sas_linkrate linkrate;
+ u8 sas_addr[SAS_ADDR_SIZE];
+ struct discover_resp *dr = &disc_resp->disc;
+ struct sas_ha_struct *ha = dev->port->ha;
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
- struct smp_resp *resp = disc_resp;
- struct discover_resp *dr = &resp->disc;
struct sas_rphy *rphy = dev->rphy;
- int rediscover = (phy->phy != NULL);
+ bool new_phy = !phy->phy;
+ char *type;
- if (!rediscover) {
+ if (new_phy) {
+ if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)))
+ return;
phy->phy = sas_phy_alloc(&rphy->dev, phy_id);
/* FIXME: error_handling */
BUG_ON(!phy->phy);
}
- switch (resp->result) {
+ switch (disc_resp->result) {
case SMP_RESP_PHY_VACANT:
phy->phy_state = PHY_VACANT;
break;
@@ -184,96 +232,177 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
break;
}
+ /* check if anything important changed to squelch debug */
+ dev_type = phy->attached_dev_type;
+ linkrate = phy->linkrate;
+ memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
+
+ /* Handle vacant phy - rest of dr data is not valid so skip it */
+ if (phy->phy_state == PHY_VACANT) {
+ memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
+ phy->attached_dev_type = SAS_PHY_UNUSED;
+ if (!test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) {
+ phy->phy_id = phy_id;
+ goto skip;
+ } else
+ goto out;
+ }
+
+ phy->attached_dev_type = to_dev_type(dr);
+ if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))
+ goto out;
phy->phy_id = phy_id;
- phy->attached_dev_type = dr->attached_dev_type;
phy->linkrate = dr->linkrate;
phy->attached_sata_host = dr->attached_sata_host;
phy->attached_sata_dev = dr->attached_sata_dev;
phy->attached_sata_ps = dr->attached_sata_ps;
phy->attached_iproto = dr->iproto << 1;
phy->attached_tproto = dr->tproto << 1;
- memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE);
+ /* help some expanders that fail to zero sas_address in the 'no
+ * device' case
+ */
+ if (phy->attached_dev_type == SAS_PHY_UNUSED)
+ memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
+ else
+ memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE);
phy->attached_phy_id = dr->attached_phy_id;
phy->phy_change_count = dr->change_count;
phy->routing_attr = dr->routing_attr;
phy->virtual = dr->virtual;
phy->last_da_index = -1;
+ phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr);
+ phy->phy->identify.device_type = dr->attached_dev_type;
phy->phy->identify.initiator_port_protocols = phy->attached_iproto;
phy->phy->identify.target_port_protocols = phy->attached_tproto;
+ if (!phy->attached_tproto && dr->attached_sata_dev)
+ phy->phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
phy->phy->identify.phy_identifier = phy_id;
phy->phy->minimum_linkrate_hw = dr->hmin_linkrate;
phy->phy->maximum_linkrate_hw = dr->hmax_linkrate;
phy->phy->minimum_linkrate = dr->pmin_linkrate;
phy->phy->maximum_linkrate = dr->pmax_linkrate;
phy->phy->negotiated_linkrate = phy->linkrate;
+ phy->phy->enabled = (phy->linkrate != SAS_PHY_DISABLED);
- if (!rediscover)
+ skip:
+ if (new_phy)
if (sas_phy_add(phy->phy)) {
sas_phy_free(phy->phy);
return;
}
- SAS_DPRINTK("ex %016llx phy%02d:%c attached: %016llx\n",
- SAS_ADDR(dev->sas_addr), phy->phy_id,
- phy->routing_attr == TABLE_ROUTING ? 'T' :
- phy->routing_attr == DIRECT_ROUTING ? 'D' :
- phy->routing_attr == SUBTRACTIVE_ROUTING ? 'S' : '?',
- SAS_ADDR(phy->attached_sas_addr));
+ out:
+ switch (phy->attached_dev_type) {
+ case SAS_SATA_PENDING:
+ type = "stp pending";
+ break;
+ case SAS_PHY_UNUSED:
+ type = "no device";
+ break;
+ case SAS_END_DEVICE:
+ if (phy->attached_iproto) {
+ if (phy->attached_tproto)
+ type = "host+target";
+ else
+ type = "host";
+ } else {
+ if (dr->attached_sata_dev)
+ type = "stp";
+ else
+ type = "ssp";
+ }
+ break;
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ type = "smp";
+ break;
+ default:
+ type = "unknown";
+ }
+
+ /* this routine is polled by libata error recovery so filter
+ * unimportant messages
+ */
+ if (new_phy || phy->attached_dev_type != dev_type ||
+ phy->linkrate != linkrate ||
+ SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr))
+ /* pass */;
+ else
+ return;
+
+ /* if the attached device type changed and ata_eh is active,
+ * make sure we run revalidation when eh completes (see:
+ * sas_enable_revalidation)
+ */
+ if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))
+ set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending);
+
+ pr_debug("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n",
+ test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "",
+ SAS_ADDR(dev->sas_addr), phy->phy_id,
+ sas_route_char(dev, phy), phy->linkrate,
+ SAS_ADDR(phy->attached_sas_addr), type);
+}
+
+/* check if we have an existing attached ata device on this expander phy */
+struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id)
+{
+ struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id];
+ struct domain_device *dev;
+ struct sas_rphy *rphy;
+
+ if (!ex_phy->port)
+ return NULL;
+
+ rphy = ex_phy->port->rphy;
+ if (!rphy)
+ return NULL;
+
+ dev = sas_find_dev_by_rphy(rphy);
+
+ if (dev && dev_is_sata(dev))
+ return dev;
- return;
+ return NULL;
}
#define DISCOVER_REQ_SIZE 16
-#define DISCOVER_RESP_SIZE 56
+#define DISCOVER_RESP_SIZE sizeof(struct smp_disc_resp)
static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
- u8 *disc_resp, int single)
+ struct smp_disc_resp *disc_resp,
+ int single)
{
- int i, res;
+ struct discover_resp *dr = &disc_resp->disc;
+ int res;
disc_req[9] = single;
- for (i = 1 ; i < 3; i++) {
- struct discover_resp *dr;
- res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
- disc_resp, DISCOVER_RESP_SIZE);
- if (res)
- return res;
- /* This is detecting a failure to transmit initial
- * dev to host FIS as described in section G.5 of
- * sas-2 r 04b */
- dr = &((struct smp_resp *)disc_resp)->disc;
- if (memcmp(dev->sas_addr, dr->attached_sas_addr,
- SAS_ADDR_SIZE) == 0) {
- sas_printk("Found loopback topology, just ignore it!\n");
- return 0;
- }
- if (!(dr->attached_dev_type == 0 &&
- dr->attached_sata_dev))
- break;
- /* In order to generate the dev to host FIS, we
- * send a link reset to the expander port */
- sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL);
- /* Wait for the reset to trigger the negotiation */
- msleep(500);
+ res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
+ disc_resp, DISCOVER_RESP_SIZE);
+ if (res)
+ return res;
+ if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) {
+ pr_notice("Found loopback topology, just ignore it!\n");
+ return 0;
}
sas_set_ex_phy(dev, single, disc_resp);
return 0;
}
-static int sas_ex_phy_discover(struct domain_device *dev, int single)
+int sas_ex_phy_discover(struct domain_device *dev, int single)
{
struct expander_device *ex = &dev->ex_dev;
int res = 0;
u8 *disc_req;
- u8 *disc_resp;
+ struct smp_disc_resp *disc_resp;
disc_req = alloc_smp_req(DISCOVER_REQ_SIZE);
if (!disc_req)
return -ENOMEM;
- disc_resp = alloc_smp_req(DISCOVER_RESP_SIZE);
+ disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE);
if (!disc_resp) {
kfree(disc_req);
return -ENOMEM;
@@ -302,9 +431,9 @@ out_err:
static int sas_expander_discover(struct domain_device *dev)
{
struct expander_device *ex = &dev->ex_dev;
- int res = -ENOMEM;
+ int res;
- ex->ex_phy = kzalloc(sizeof(*ex->ex_phy)*ex->num_phys, GFP_KERNEL);
+ ex->ex_phy = kcalloc(ex->num_phys, sizeof(*ex->ex_phy), GFP_KERNEL);
if (!ex->ex_phy)
return -ENOMEM;
@@ -321,26 +450,14 @@ static int sas_expander_discover(struct domain_device *dev)
#define MAX_EXPANDER_PHYS 128
-static void ex_assign_report_general(struct domain_device *dev,
- struct smp_resp *resp)
-{
- struct report_general_resp *rg = &resp->rg;
-
- dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count);
- dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes);
- dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS);
- dev->ex_dev.conf_route_table = rg->conf_route_table;
- dev->ex_dev.configuring = rg->configuring;
- memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8);
-}
-
#define RG_REQ_SIZE 8
-#define RG_RESP_SIZE 32
+#define RG_RESP_SIZE sizeof(struct smp_rg_resp)
static int sas_ex_general(struct domain_device *dev)
{
u8 *rg_req;
- struct smp_resp *rg_resp;
+ struct smp_rg_resp *rg_resp;
+ struct report_general_resp *rg;
int res;
int i;
@@ -361,21 +478,29 @@ static int sas_ex_general(struct domain_device *dev)
RG_RESP_SIZE);
if (res) {
- SAS_DPRINTK("RG to ex %016llx failed:0x%x\n",
- SAS_ADDR(dev->sas_addr), res);
+ pr_notice("RG to ex %016llx failed:0x%x\n",
+ SAS_ADDR(dev->sas_addr), res);
goto out;
} else if (rg_resp->result != SMP_RESP_FUNC_ACC) {
- SAS_DPRINTK("RG:ex %016llx returned SMP result:0x%x\n",
- SAS_ADDR(dev->sas_addr), rg_resp->result);
+ pr_debug("RG:ex %016llx returned SMP result:0x%x\n",
+ SAS_ADDR(dev->sas_addr), rg_resp->result);
res = rg_resp->result;
goto out;
}
- ex_assign_report_general(dev, rg_resp);
+ rg = &rg_resp->rg;
+ dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count);
+ dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes);
+ dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS);
+ dev->ex_dev.t2t_supp = rg->t2t_supp;
+ dev->ex_dev.conf_route_table = rg->conf_route_table;
+ dev->ex_dev.configuring = rg->configuring;
+ memcpy(dev->ex_dev.enclosure_logical_id,
+ rg->enclosure_logical_id, 8);
if (dev->ex_dev.configuring) {
- SAS_DPRINTK("RG: ex %llx self-configuring...\n",
- SAS_ADDR(dev->sas_addr));
+ pr_debug("RG: ex %016llx self-configuring...\n",
+ SAS_ADDR(dev->sas_addr));
schedule_timeout_interruptible(5*HZ);
} else
break;
@@ -427,14 +552,14 @@ static int sas_ex_manuf_info(struct domain_device *dev)
mi_req[1] = SMP_REPORT_MANUF_INFO;
- res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp,MI_RESP_SIZE);
+ res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp, MI_RESP_SIZE);
if (res) {
- SAS_DPRINTK("MI: ex %016llx failed:0x%x\n",
- SAS_ADDR(dev->sas_addr), res);
+ pr_notice("MI: ex %016llx failed:0x%x\n",
+ SAS_ADDR(dev->sas_addr), res);
goto out;
} else if (mi_resp[2] != SMP_RESP_FUNC_ACC) {
- SAS_DPRINTK("MI ex %016llx returned SMP result:0x%x\n",
- SAS_ADDR(dev->sas_addr), mi_resp[2]);
+ pr_debug("MI ex %016llx returned SMP result:0x%x\n",
+ SAS_ADDR(dev->sas_addr), mi_resp[2]);
goto out;
}
@@ -468,14 +593,21 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
pc_req[1] = SMP_PHY_CONTROL;
pc_req[9] = phy_id;
- pc_req[10]= phy_func;
+ pc_req[10] = phy_func;
if (rates) {
pc_req[32] = rates->minimum_linkrate << 4;
pc_req[33] = rates->maximum_linkrate << 4;
}
- res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE);
-
+ res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp, PC_RESP_SIZE);
+ if (res) {
+ pr_err("ex %016llx phy%02d PHY control failed: %d\n",
+ SAS_ADDR(dev->sas_addr), phy_id, res);
+ } else if (pc_resp[2] != SMP_RESP_FUNC_ACC) {
+ pr_err("ex %016llx phy%02d PHY control failed: function result 0x%x\n",
+ SAS_ADDR(dev->sas_addr), phy_id, pc_resp[2]);
+ res = pc_resp[2];
+ }
kfree(pc_resp);
kfree(pc_req);
return res;
@@ -545,17 +677,18 @@ int sas_smp_get_phy_events(struct sas_phy *phy)
req[9] = phy->number;
res = smp_execute_task(dev, req, RPEL_REQ_SIZE,
- resp, RPEL_RESP_SIZE);
+ resp, RPEL_RESP_SIZE);
- if (!res)
+ if (res)
goto out;
- phy->invalid_dword_count = scsi_to_u32(&resp[12]);
- phy->running_disparity_error_count = scsi_to_u32(&resp[16]);
- phy->loss_of_dword_sync_count = scsi_to_u32(&resp[20]);
- phy->phy_reset_problem_count = scsi_to_u32(&resp[24]);
+ phy->invalid_dword_count = get_unaligned_be32(&resp[12]);
+ phy->running_disparity_error_count = get_unaligned_be32(&resp[16]);
+ phy->loss_of_dword_sync_count = get_unaligned_be32(&resp[20]);
+ phy->phy_reset_problem_count = get_unaligned_be32(&resp[24]);
out:
+ kfree(req);
kfree(resp);
return res;
@@ -564,11 +697,10 @@ int sas_smp_get_phy_events(struct sas_phy *phy)
#ifdef CONFIG_SCSI_SAS_ATA
#define RPS_REQ_SIZE 16
-#define RPS_RESP_SIZE 60
+#define RPS_RESP_SIZE sizeof(struct smp_rps_resp)
-static int sas_get_report_phy_sata(struct domain_device *dev,
- int phy_id,
- struct smp_resp *rps_resp)
+int sas_get_report_phy_sata(struct domain_device *dev, int phy_id,
+ struct smp_rps_resp *rps_resp)
{
int res;
u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE);
@@ -581,7 +713,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev,
rps_req[9] = phy_id;
res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE,
- rps_resp, RPS_RESP_SIZE);
+ rps_resp, RPS_RESP_SIZE);
/* 0x34 is the FIS type for the D2H fis. There's a potential
* standards cockup here. sas-2 explicitly specifies the FIS
@@ -627,9 +759,7 @@ static void sas_ex_get_linkrate(struct domain_device *parent,
phy->phy_state == PHY_NOT_PRESENT)
continue;
- if (SAS_ADDR(phy->attached_sas_addr) ==
- SAS_ADDR(child->sas_addr)) {
-
+ if (sas_phy_match_dev_addr(child, phy)) {
child->min_linkrate = min(parent->min_linkrate,
phy->linkrate);
child->max_linkrate = max(parent->max_linkrate,
@@ -642,22 +772,56 @@ static void sas_ex_get_linkrate(struct domain_device *parent,
child->pathways = min(child->pathways, parent->pathways);
}
+static int sas_ex_add_dev(struct domain_device *parent, struct ex_phy *phy,
+ struct domain_device *child, int phy_id)
+{
+ struct sas_rphy *rphy;
+ int res;
+
+ child->dev_type = SAS_END_DEVICE;
+ rphy = sas_end_device_alloc(phy->port);
+ if (!rphy)
+ return -ENOMEM;
+
+ child->tproto = phy->attached_tproto;
+ sas_init_dev(child);
+
+ child->rphy = rphy;
+ get_device(&rphy->dev);
+ rphy->identify.phy_identifier = phy_id;
+ sas_fill_in_rphy(child, rphy);
+
+ list_add_tail(&child->disco_list_node, &parent->port->disco_list);
+
+ res = sas_notify_lldd_dev_found(child);
+ if (res) {
+ pr_notice("notify lldd for device %016llx at %016llx:%02d returned 0x%x\n",
+ SAS_ADDR(child->sas_addr),
+ SAS_ADDR(parent->sas_addr), phy_id, res);
+ sas_rphy_free(child->rphy);
+ list_del(&child->disco_list_node);
+ return res;
+ }
+
+ return 0;
+}
+
static struct domain_device *sas_ex_discover_end_dev(
struct domain_device *parent, int phy_id)
{
struct expander_device *parent_ex = &parent->ex_dev;
struct ex_phy *phy = &parent_ex->ex_phy[phy_id];
struct domain_device *child = NULL;
- struct sas_rphy *rphy;
int res;
if (phy->attached_sata_host || phy->attached_sata_ps)
return NULL;
- child = kzalloc(sizeof(*child), GFP_KERNEL);
+ child = sas_alloc_device();
if (!child)
return NULL;
+ kref_get(&parent->kref);
child->parent = parent;
child->port = parent->port;
child->iproto = phy->attached_iproto;
@@ -673,95 +837,35 @@ static struct domain_device *sas_ex_discover_end_dev(
}
}
sas_ex_get_linkrate(parent, child, phy);
+ sas_device_set_phy(child, phy->port);
-#ifdef CONFIG_SCSI_SAS_ATA
if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) {
- child->dev_type = SATA_DEV;
- if (phy->attached_tproto & SAS_PROTOCOL_STP)
- child->tproto = phy->attached_tproto;
- if (phy->attached_sata_dev)
- child->tproto |= SATA_DEV;
- res = sas_get_report_phy_sata(parent, phy_id,
- &child->sata_dev.rps_resp);
- if (res) {
- SAS_DPRINTK("report phy sata to %016llx:0x%x returned "
- "0x%x\n", SAS_ADDR(parent->sas_addr),
- phy_id, res);
- goto out_free;
- }
- memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis,
- sizeof(struct dev_to_host_fis));
-
- rphy = sas_end_device_alloc(phy->port);
- if (unlikely(!rphy))
- goto out_free;
-
- sas_init_dev(child);
-
- child->rphy = rphy;
-
- spin_lock_irq(&parent->port->dev_list_lock);
- list_add_tail(&child->dev_list_node, &parent->port->dev_list);
- spin_unlock_irq(&parent->port->dev_list_lock);
-
- res = sas_discover_sata(child);
- if (res) {
- SAS_DPRINTK("sas_discover_sata() for device %16llx at "
- "%016llx:0x%x returned 0x%x\n",
- SAS_ADDR(child->sas_addr),
- SAS_ADDR(parent->sas_addr), phy_id, res);
- goto out_list_del;
- }
- } else
-#endif
- if (phy->attached_tproto & SAS_PROTOCOL_SSP) {
- child->dev_type = SAS_END_DEV;
- rphy = sas_end_device_alloc(phy->port);
- /* FIXME: error handling */
- if (unlikely(!rphy))
- goto out_free;
- child->tproto = phy->attached_tproto;
- sas_init_dev(child);
-
- child->rphy = rphy;
- sas_fill_in_rphy(child, rphy);
-
- spin_lock_irq(&parent->port->dev_list_lock);
- list_add_tail(&child->dev_list_node, &parent->port->dev_list);
- spin_unlock_irq(&parent->port->dev_list_lock);
-
- res = sas_discover_end_dev(child);
- if (res) {
- SAS_DPRINTK("sas_discover_end_dev() for device %16llx "
- "at %016llx:0x%x returned 0x%x\n",
- SAS_ADDR(child->sas_addr),
- SAS_ADDR(parent->sas_addr), phy_id, res);
- goto out_list_del;
- }
+ res = sas_ata_add_dev(parent, phy, child, phy_id);
+ } else if (phy->attached_tproto & SAS_PROTOCOL_SSP) {
+ res = sas_ex_add_dev(parent, phy, child, phy_id);
} else {
- SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n",
- phy->attached_tproto, SAS_ADDR(parent->sas_addr),
- phy_id);
- goto out_free;
+ pr_notice("target proto 0x%x at %016llx:0x%x not handled\n",
+ phy->attached_tproto, SAS_ADDR(parent->sas_addr),
+ phy_id);
+ res = -ENODEV;
}
+ if (res)
+ goto out_free;
+
list_add_tail(&child->siblings, &parent_ex->children);
return child;
- out_list_del:
- sas_rphy_free(child->rphy);
- child->rphy = NULL;
- list_del(&child->dev_list_node);
out_free:
sas_port_delete(phy->port);
out_err:
phy->port = NULL;
- kfree(child);
+ sas_put_device(child);
return NULL;
}
/* See if this phy is part of a wide port */
-static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
+static bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
{
struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id];
int i;
@@ -774,14 +878,12 @@ static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr,
SAS_ADDR_SIZE) && ephy->port) {
- sas_port_add_phy(ephy->port, phy->phy);
- phy->port = ephy->port;
- phy->phy_state = PHY_DEVICE_DISCOVERED;
- return 0;
+ sas_port_add_ex_phy(ephy->port, phy);
+ return true;
}
}
- return -ENODEV;
+ return false;
}
static struct domain_device *sas_ex_discover_expander(
@@ -796,14 +898,13 @@ static struct domain_device *sas_ex_discover_expander(
int res;
if (phy->routing_attr == DIRECT_ROUTING) {
- SAS_DPRINTK("ex %016llx:0x%x:D <--> ex %016llx:0x%x is not "
- "allowed\n",
- SAS_ADDR(parent->sas_addr), phy_id,
- SAS_ADDR(phy->attached_sas_addr),
- phy->attached_phy_id);
+ pr_warn("ex %016llx:%02d:D <--> ex %016llx:0x%x is not allowed\n",
+ SAS_ADDR(parent->sas_addr), phy_id,
+ SAS_ADDR(phy->attached_sas_addr),
+ phy->attached_phy_id);
return NULL;
}
- child = kzalloc(sizeof(*child), GFP_KERNEL);
+ child = sas_alloc_device();
if (!child)
return NULL;
@@ -813,11 +914,11 @@ static struct domain_device *sas_ex_discover_expander(
switch (phy->attached_dev_type) {
- case EDGE_DEV:
+ case SAS_EDGE_EXPANDER_DEVICE:
rphy = sas_expander_alloc(phy->port,
SAS_EDGE_EXPANDER_DEVICE);
break;
- case FANOUT_DEV:
+ case SAS_FANOUT_EXPANDER_DEVICE:
rphy = sas_expander_alloc(phy->port,
SAS_FANOUT_EXPANDER_DEVICE);
break;
@@ -827,8 +928,10 @@ static struct domain_device *sas_ex_discover_expander(
}
port = parent->port;
child->rphy = rphy;
+ get_device(&rphy->dev);
edev = rphy_to_expander_device(rphy);
child->dev_type = phy->attached_dev_type;
+ kref_get(&parent->kref);
child->parent = parent;
child->port = port;
child->iproto = phy->attached_iproto;
@@ -849,7 +952,13 @@ static struct domain_device *sas_ex_discover_expander(
res = sas_discover_expander(child);
if (res) {
- kfree(child);
+ sas_rphy_delete(rphy);
+ spin_lock_irq(&parent->port->dev_list_lock);
+ list_del(&child->dev_list_node);
+ spin_unlock_irq(&parent->port->dev_list_lock);
+ sas_put_device(child);
+ sas_port_delete(phy->port);
+ phy->port = NULL;
return NULL;
}
list_add_tail(&child->siblings, &parent->ex_dev.children);
@@ -872,14 +981,12 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
}
/* Parent and domain coherency */
- if (!dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) ==
- SAS_ADDR(dev->port->sas_addr))) {
- sas_add_parent_port(dev, phy_id);
+ if (!dev->parent && sas_phy_match_port_addr(dev->port, ex_phy)) {
+ sas_ex_add_parent_port(dev, phy_id);
return 0;
}
- if (dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) ==
- SAS_ADDR(dev->parent->sas_addr))) {
- sas_add_parent_port(dev, phy_id);
+ if (dev->parent && sas_phy_match_dev_addr(dev->parent, ex_phy)) {
+ sas_ex_add_parent_port(dev, phy_id);
if (ex_phy->routing_attr == TABLE_ROUTING)
sas_configure_phy(dev, phy_id, dev->port->sas_addr, 1);
return 0;
@@ -888,7 +995,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
if (sas_dev_present_in_domain(dev->port, ex_phy->attached_sas_addr))
sas_ex_disable_port(dev, ex_phy->attached_sas_addr);
- if (ex_phy->attached_dev_type == NO_DEVICE) {
+ if (ex_phy->attached_dev_type == SAS_PHY_UNUSED) {
if (ex_phy->routing_attr == DIRECT_ROUTING) {
memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
sas_configure_routing(dev, ex_phy->attached_sas_addr);
@@ -897,80 +1004,59 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
} else if (ex_phy->linkrate == SAS_LINK_RATE_UNKNOWN)
return 0;
- if (ex_phy->attached_dev_type != SAS_END_DEV &&
- ex_phy->attached_dev_type != FANOUT_DEV &&
- ex_phy->attached_dev_type != EDGE_DEV) {
- SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx "
- "phy 0x%x\n", ex_phy->attached_dev_type,
- SAS_ADDR(dev->sas_addr),
- phy_id);
+ if (ex_phy->attached_dev_type != SAS_END_DEVICE &&
+ ex_phy->attached_dev_type != SAS_FANOUT_EXPANDER_DEVICE &&
+ ex_phy->attached_dev_type != SAS_EDGE_EXPANDER_DEVICE &&
+ ex_phy->attached_dev_type != SAS_SATA_PENDING) {
+ pr_warn("unknown device type(0x%x) attached to ex %016llx phy%02d\n",
+ ex_phy->attached_dev_type,
+ SAS_ADDR(dev->sas_addr),
+ phy_id);
return 0;
}
res = sas_configure_routing(dev, ex_phy->attached_sas_addr);
if (res) {
- SAS_DPRINTK("configure routing for dev %016llx "
- "reported 0x%x. Forgotten\n",
- SAS_ADDR(ex_phy->attached_sas_addr), res);
+ pr_notice("configure routing for dev %016llx reported 0x%x. Forgotten\n",
+ SAS_ADDR(ex_phy->attached_sas_addr), res);
sas_disable_routing(dev, ex_phy->attached_sas_addr);
return res;
}
- res = sas_ex_join_wide_port(dev, phy_id);
- if (!res) {
- SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",
- phy_id, SAS_ADDR(ex_phy->attached_sas_addr));
+ if (sas_ex_join_wide_port(dev, phy_id)) {
+ pr_debug("Attaching ex phy%02d to wide port %016llx\n",
+ phy_id, SAS_ADDR(ex_phy->attached_sas_addr));
return res;
}
switch (ex_phy->attached_dev_type) {
- case SAS_END_DEV:
+ case SAS_END_DEVICE:
+ case SAS_SATA_PENDING:
child = sas_ex_discover_end_dev(dev, phy_id);
break;
- case FANOUT_DEV:
+ case SAS_FANOUT_EXPANDER_DEVICE:
if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) {
- SAS_DPRINTK("second fanout expander %016llx phy 0x%x "
- "attached to ex %016llx phy 0x%x\n",
- SAS_ADDR(ex_phy->attached_sas_addr),
- ex_phy->attached_phy_id,
- SAS_ADDR(dev->sas_addr),
- phy_id);
+ pr_debug("second fanout expander %016llx phy%02d attached to ex %016llx phy%02d\n",
+ SAS_ADDR(ex_phy->attached_sas_addr),
+ ex_phy->attached_phy_id,
+ SAS_ADDR(dev->sas_addr),
+ phy_id);
sas_ex_disable_phy(dev, phy_id);
- break;
+ return res;
} else
memcpy(dev->port->disc.fanout_sas_addr,
ex_phy->attached_sas_addr, SAS_ADDR_SIZE);
- /* fallthrough */
- case EDGE_DEV:
+ fallthrough;
+ case SAS_EDGE_EXPANDER_DEVICE:
child = sas_ex_discover_expander(dev, phy_id);
break;
default:
break;
}
- if (child) {
- int i;
-
- for (i = 0; i < ex->num_phys; i++) {
- if (ex->ex_phy[i].phy_state == PHY_VACANT ||
- ex->ex_phy[i].phy_state == PHY_NOT_PRESENT)
- continue;
- /*
- * Due to races, the phy might not get added to the
- * wide port, so we add the phy to the wide port here.
- */
- if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) ==
- SAS_ADDR(child->sas_addr)) {
- ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED;
- res = sas_ex_join_wide_port(dev, i);
- if (!res)
- SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",
- i, SAS_ADDR(ex->ex_phy[i].attached_sas_addr));
-
- }
- }
- }
-
+ if (!child)
+ pr_notice("ex %016llx phy%02d failed to discover\n",
+ SAS_ADDR(dev->sas_addr), phy_id);
return res;
}
@@ -986,11 +1072,10 @@ static int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr)
phy->phy_state == PHY_NOT_PRESENT)
continue;
- if ((phy->attached_dev_type == EDGE_DEV ||
- phy->attached_dev_type == FANOUT_DEV) &&
+ if (dev_is_expander(phy->attached_dev_type) &&
phy->routing_attr == SUBTRACTIVE_ROUTING) {
- memcpy(sub_addr, phy->attached_sas_addr,SAS_ADDR_SIZE);
+ memcpy(sub_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
return 1;
}
@@ -1002,28 +1087,25 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev)
{
struct expander_device *ex = &dev->ex_dev;
struct domain_device *child;
- u8 sub_addr[8] = {0, };
+ u8 sub_addr[SAS_ADDR_SIZE] = {0, };
list_for_each_entry(child, &ex->children, siblings) {
- if (child->dev_type != EDGE_DEV &&
- child->dev_type != FANOUT_DEV)
+ if (!dev_is_expander(child->dev_type))
continue;
if (sub_addr[0] == 0) {
sas_find_sub_addr(child, sub_addr);
continue;
} else {
- u8 s2[8];
+ u8 s2[SAS_ADDR_SIZE];
if (sas_find_sub_addr(child, s2) &&
(SAS_ADDR(sub_addr) != SAS_ADDR(s2))) {
- SAS_DPRINTK("ex %016llx->%016llx-?->%016llx "
- "diverges from subtractive "
- "boundary %016llx\n",
- SAS_ADDR(dev->sas_addr),
- SAS_ADDR(child->sas_addr),
- SAS_ADDR(s2),
- SAS_ADDR(sub_addr));
+ pr_notice("ex %016llx->%016llx-?->%016llx diverges from subtractive boundary %016llx\n",
+ SAS_ADDR(dev->sas_addr),
+ SAS_ADDR(child->sas_addr),
+ SAS_ADDR(s2),
+ SAS_ADDR(sub_addr));
sas_ex_disable_port(child, s2);
}
@@ -1032,9 +1114,9 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev)
return 0;
}
/**
- * sas_ex_discover_devices -- discover devices attached to this expander
- * dev: pointer to the expander domain device
- * single: if you want to do a single phy, else set to -1;
+ * sas_ex_discover_devices - discover devices attached to this expander
+ * @dev: pointer to the expander domain device
+ * @single: if you want to do a single phy, else set to -1;
*
* Configure this expander for use with its devices and register the
* devices of this expander.
@@ -1083,7 +1165,7 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev)
int i;
u8 *sub_sas_addr = NULL;
- if (dev->dev_type != EDGE_DEV)
+ if (dev->dev_type != SAS_EDGE_EXPANDER_DEVICE)
return 0;
for (i = 0; i < ex->num_phys; i++) {
@@ -1093,8 +1175,7 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev)
phy->phy_state == PHY_NOT_PRESENT)
continue;
- if ((phy->attached_dev_type == FANOUT_DEV ||
- phy->attached_dev_type == EDGE_DEV) &&
+ if (dev_is_expander(phy->attached_dev_type) &&
phy->routing_attr == SUBTRACTIVE_ROUTING) {
if (!sub_sas_addr)
@@ -1102,12 +1183,10 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev)
else if (SAS_ADDR(sub_sas_addr) !=
SAS_ADDR(phy->attached_sas_addr)) {
- SAS_DPRINTK("ex %016llx phy 0x%x "
- "diverges(%016llx) on subtractive "
- "boundary(%016llx). Disabled\n",
- SAS_ADDR(dev->sas_addr), i,
- SAS_ADDR(phy->attached_sas_addr),
- SAS_ADDR(sub_sas_addr));
+ pr_notice("ex %016llx phy%02d diverges(%016llx) on subtractive boundary(%016llx). Disabled\n",
+ SAS_ADDR(dev->sas_addr), i,
+ SAS_ADDR(phy->attached_sas_addr),
+ SAS_ADDR(sub_sas_addr));
sas_ex_disable_phy(dev, i);
}
}
@@ -1119,134 +1198,144 @@ static void sas_print_parent_topology_bug(struct domain_device *child,
struct ex_phy *parent_phy,
struct ex_phy *child_phy)
{
- static const char ra_char[] = {
- [DIRECT_ROUTING] = 'D',
- [SUBTRACTIVE_ROUTING] = 'S',
- [TABLE_ROUTING] = 'T',
- };
static const char *ex_type[] = {
- [EDGE_DEV] = "edge",
- [FANOUT_DEV] = "fanout",
+ [SAS_EDGE_EXPANDER_DEVICE] = "edge",
+ [SAS_FANOUT_EXPANDER_DEVICE] = "fanout",
};
struct domain_device *parent = child->parent;
- sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx phy 0x%x "
- "has %c:%c routing link!\n",
+ pr_notice("%s ex %016llx phy%02d <--> %s ex %016llx phy%02d has %c:%c routing link!\n",
+ ex_type[parent->dev_type],
+ SAS_ADDR(parent->sas_addr),
+ parent_phy->phy_id,
- ex_type[parent->dev_type],
- SAS_ADDR(parent->sas_addr),
- parent_phy->phy_id,
+ ex_type[child->dev_type],
+ SAS_ADDR(child->sas_addr),
+ child_phy->phy_id,
- ex_type[child->dev_type],
- SAS_ADDR(child->sas_addr),
- child_phy->phy_id,
+ sas_route_char(parent, parent_phy),
+ sas_route_char(child, child_phy));
+}
+
+static bool sas_eeds_valid(struct domain_device *parent,
+ struct domain_device *child)
+{
+ struct sas_discovery *disc = &parent->port->disc;
- ra_char[parent_phy->routing_attr],
- ra_char[child_phy->routing_attr]);
+ return (SAS_ADDR(disc->eeds_a) == SAS_ADDR(parent->sas_addr) ||
+ SAS_ADDR(disc->eeds_a) == SAS_ADDR(child->sas_addr)) &&
+ (SAS_ADDR(disc->eeds_b) == SAS_ADDR(parent->sas_addr) ||
+ SAS_ADDR(disc->eeds_b) == SAS_ADDR(child->sas_addr));
}
static int sas_check_eeds(struct domain_device *child,
- struct ex_phy *parent_phy,
- struct ex_phy *child_phy)
+ struct ex_phy *parent_phy,
+ struct ex_phy *child_phy)
{
int res = 0;
struct domain_device *parent = child->parent;
+ struct sas_discovery *disc = &parent->port->disc;
- if (SAS_ADDR(parent->port->disc.fanout_sas_addr) != 0) {
+ if (SAS_ADDR(disc->fanout_sas_addr) != 0) {
res = -ENODEV;
- SAS_DPRINTK("edge ex %016llx phy S:0x%x <--> edge ex %016llx "
- "phy S:0x%x, while there is a fanout ex %016llx\n",
- SAS_ADDR(parent->sas_addr),
- parent_phy->phy_id,
- SAS_ADDR(child->sas_addr),
- child_phy->phy_id,
- SAS_ADDR(parent->port->disc.fanout_sas_addr));
- } else if (SAS_ADDR(parent->port->disc.eeds_a) == 0) {
- memcpy(parent->port->disc.eeds_a, parent->sas_addr,
- SAS_ADDR_SIZE);
- memcpy(parent->port->disc.eeds_b, child->sas_addr,
- SAS_ADDR_SIZE);
- } else if (((SAS_ADDR(parent->port->disc.eeds_a) ==
- SAS_ADDR(parent->sas_addr)) ||
- (SAS_ADDR(parent->port->disc.eeds_a) ==
- SAS_ADDR(child->sas_addr)))
- &&
- ((SAS_ADDR(parent->port->disc.eeds_b) ==
- SAS_ADDR(parent->sas_addr)) ||
- (SAS_ADDR(parent->port->disc.eeds_b) ==
- SAS_ADDR(child->sas_addr))))
- ;
- else {
+ pr_warn("edge ex %016llx phy S:%02d <--> edge ex %016llx phy S:%02d, while there is a fanout ex %016llx\n",
+ SAS_ADDR(parent->sas_addr),
+ parent_phy->phy_id,
+ SAS_ADDR(child->sas_addr),
+ child_phy->phy_id,
+ SAS_ADDR(disc->fanout_sas_addr));
+ } else if (SAS_ADDR(disc->eeds_a) == 0) {
+ memcpy(disc->eeds_a, parent->sas_addr, SAS_ADDR_SIZE);
+ memcpy(disc->eeds_b, child->sas_addr, SAS_ADDR_SIZE);
+ } else if (!sas_eeds_valid(parent, child)) {
res = -ENODEV;
- SAS_DPRINTK("edge ex %016llx phy 0x%x <--> edge ex %016llx "
- "phy 0x%x link forms a third EEDS!\n",
- SAS_ADDR(parent->sas_addr),
- parent_phy->phy_id,
- SAS_ADDR(child->sas_addr),
- child_phy->phy_id);
+ pr_warn("edge ex %016llx phy%02d <--> edge ex %016llx phy%02d link forms a third EEDS!\n",
+ SAS_ADDR(parent->sas_addr),
+ parent_phy->phy_id,
+ SAS_ADDR(child->sas_addr),
+ child_phy->phy_id);
}
return res;
}
-/* Here we spill over 80 columns. It is intentional.
- */
-static int sas_check_parent_topology(struct domain_device *child)
+static int sas_check_edge_expander_topo(struct domain_device *child,
+ struct ex_phy *parent_phy)
+{
+ struct expander_device *child_ex = &child->ex_dev;
+ struct expander_device *parent_ex = &child->parent->ex_dev;
+ struct ex_phy *child_phy;
+
+ child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id];
+
+ if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
+ if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING ||
+ child_phy->routing_attr != TABLE_ROUTING)
+ goto error;
+ } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) {
+ if (child_phy->routing_attr == SUBTRACTIVE_ROUTING)
+ return sas_check_eeds(child, parent_phy, child_phy);
+ else if (child_phy->routing_attr != TABLE_ROUTING)
+ goto error;
+ } else if (parent_phy->routing_attr == TABLE_ROUTING) {
+ if (child_phy->routing_attr != SUBTRACTIVE_ROUTING &&
+ (child_phy->routing_attr != TABLE_ROUTING ||
+ !child_ex->t2t_supp || !parent_ex->t2t_supp))
+ goto error;
+ }
+
+ return 0;
+error:
+ sas_print_parent_topology_bug(child, parent_phy, child_phy);
+ return -ENODEV;
+}
+
+static int sas_check_fanout_expander_topo(struct domain_device *child,
+ struct ex_phy *parent_phy)
{
struct expander_device *child_ex = &child->ex_dev;
+ struct ex_phy *child_phy;
+
+ child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id];
+
+ if (parent_phy->routing_attr == TABLE_ROUTING &&
+ child_phy->routing_attr == SUBTRACTIVE_ROUTING)
+ return 0;
+
+ sas_print_parent_topology_bug(child, parent_phy, child_phy);
+
+ return -ENODEV;
+}
+
+static int sas_check_parent_topology(struct domain_device *child)
+{
struct expander_device *parent_ex;
int i;
int res = 0;
- if (!child->parent)
- return 0;
-
- if (child->parent->dev_type != EDGE_DEV &&
- child->parent->dev_type != FANOUT_DEV)
+ if (!dev_parent_is_expander(child))
return 0;
parent_ex = &child->parent->ex_dev;
for (i = 0; i < parent_ex->num_phys; i++) {
struct ex_phy *parent_phy = &parent_ex->ex_phy[i];
- struct ex_phy *child_phy;
if (parent_phy->phy_state == PHY_VACANT ||
parent_phy->phy_state == PHY_NOT_PRESENT)
continue;
- if (SAS_ADDR(parent_phy->attached_sas_addr) != SAS_ADDR(child->sas_addr))
+ if (!sas_phy_match_dev_addr(child, parent_phy))
continue;
- child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id];
-
switch (child->parent->dev_type) {
- case EDGE_DEV:
- if (child->dev_type == FANOUT_DEV) {
- if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING ||
- child_phy->routing_attr != TABLE_ROUTING) {
- sas_print_parent_topology_bug(child, parent_phy, child_phy);
- res = -ENODEV;
- }
- } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) {
- if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) {
- res = sas_check_eeds(child, parent_phy, child_phy);
- } else if (child_phy->routing_attr != TABLE_ROUTING) {
- sas_print_parent_topology_bug(child, parent_phy, child_phy);
- res = -ENODEV;
- }
- } else if (parent_phy->routing_attr == TABLE_ROUTING &&
- child_phy->routing_attr != SUBTRACTIVE_ROUTING) {
- sas_print_parent_topology_bug(child, parent_phy, child_phy);
+ case SAS_EDGE_EXPANDER_DEVICE:
+ if (sas_check_edge_expander_topo(child, parent_phy))
res = -ENODEV;
- }
break;
- case FANOUT_DEV:
- if (parent_phy->routing_attr != TABLE_ROUTING ||
- child_phy->routing_attr != SUBTRACTIVE_ROUTING) {
- sas_print_parent_topology_bug(child, parent_phy, child_phy);
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ if (sas_check_fanout_expander_topo(child, parent_phy))
res = -ENODEV;
- }
break;
default:
break;
@@ -1292,14 +1381,13 @@ static int sas_configure_present(struct domain_device *dev, int phy_id,
goto out;
res = rri_resp[2];
if (res == SMP_RESP_NO_INDEX) {
- SAS_DPRINTK("overflow of indexes: dev %016llx "
- "phy 0x%x index 0x%x\n",
- SAS_ADDR(dev->sas_addr), phy_id, i);
+ pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n",
+ SAS_ADDR(dev->sas_addr), phy_id, i);
goto out;
} else if (res != SMP_RESP_FUNC_ACC) {
- SAS_DPRINTK("%s: dev %016llx phy 0x%x index 0x%x "
- "result 0x%x\n", __func__,
- SAS_ADDR(dev->sas_addr), phy_id, i, res);
+ pr_notice("%s: dev %016llx phy%02d index 0x%x result 0x%x\n",
+ __func__, SAS_ADDR(dev->sas_addr), phy_id,
+ i, res);
goto out;
}
if (SAS_ADDR(sas_addr) != 0) {
@@ -1363,9 +1451,8 @@ static int sas_configure_set(struct domain_device *dev, int phy_id,
goto out;
res = cri_resp[2];
if (res == SMP_RESP_NO_INDEX) {
- SAS_DPRINTK("overflow of indexes: dev %016llx phy 0x%x "
- "index 0x%x\n",
- SAS_ADDR(dev->sas_addr), phy_id, index);
+ pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n",
+ SAS_ADDR(dev->sas_addr), phy_id, index);
}
out:
kfree(cri_req);
@@ -1384,16 +1471,18 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id,
if (res)
return res;
if (include ^ present)
- return sas_configure_set(dev, phy_id, sas_addr, index,include);
+ return sas_configure_set(dev, phy_id, sas_addr, index,
+ include);
return res;
}
/**
- * sas_configure_parent -- configure routing table of parent
- * parent: parent expander
- * child: child expander
- * sas_addr: SAS port identifier of device directly attached to child
+ * sas_configure_parent - configure routing table of parent
+ * @parent: parent expander
+ * @child: child expander
+ * @sas_addr: SAS port identifier of device directly attached to child
+ * @include: whether or not to include @child in the expander routing table
*/
static int sas_configure_parent(struct domain_device *parent,
struct domain_device *child,
@@ -1411,8 +1500,8 @@ static int sas_configure_parent(struct domain_device *parent,
}
if (ex_parent->conf_route_table == 0) {
- SAS_DPRINTK("ex %016llx has self-configuring routing table\n",
- SAS_ADDR(parent->sas_addr));
+ pr_debug("ex %016llx has self-configuring routing table\n",
+ SAS_ADDR(parent->sas_addr));
return 0;
}
@@ -1420,8 +1509,7 @@ static int sas_configure_parent(struct domain_device *parent,
struct ex_phy *phy = &ex_parent->ex_phy[i];
if ((phy->routing_attr == TABLE_ROUTING) &&
- (SAS_ADDR(phy->attached_sas_addr) ==
- SAS_ADDR(child->sas_addr))) {
+ sas_phy_match_dev_addr(child, phy)) {
res = sas_configure_phy(parent, i, sas_addr, include);
if (res)
return res;
@@ -1432,9 +1520,9 @@ static int sas_configure_parent(struct domain_device *parent,
}
/**
- * sas_configure_routing -- configure routing
- * dev: expander device
- * sas_addr: port identifier of device directly attached to the expander device
+ * sas_configure_routing - configure routing
+ * @dev: expander device
+ * @sas_addr: port identifier of device directly attached to the expander device
*/
static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr)
{
@@ -1451,8 +1539,8 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr)
}
/**
- * sas_discover_expander -- expander discovery
- * @ex: pointer to expander domain device
+ * sas_discover_expander - expander discovery
+ * @dev: pointer to expander domain device
*
* See comment in sas_discover_sata().
*/
@@ -1473,8 +1561,8 @@ static int sas_discover_expander(struct domain_device *dev)
res = sas_expander_discover(dev);
if (res) {
- SAS_DPRINTK("expander %016llx discovery failed(0x%x)\n",
- SAS_ADDR(dev->sas_addr), res);
+ pr_warn("expander %016llx discovery failed(0x%x)\n",
+ SAS_ADDR(dev->sas_addr), res);
goto out_err;
}
@@ -1494,8 +1582,7 @@ static int sas_ex_level_discovery(struct asd_sas_port *port, const int level)
struct domain_device *dev;
list_for_each_entry(dev, &port->dev_list, dev_list_node) {
- if (dev->dev_type == EDGE_DEV ||
- dev->dev_type == FANOUT_DEV) {
+ if (dev_is_expander(dev->dev_type)) {
struct sas_expander_device *ex =
rphy_to_expander_device(dev->rphy);
@@ -1550,8 +1637,18 @@ out_err:
/* ---------- Domain revalidation ---------- */
+static void sas_get_sas_addr_and_dev_type(struct smp_disc_resp *disc_resp,
+ u8 *sas_addr,
+ enum sas_device_type *type)
+{
+ memcpy(sas_addr, disc_resp->disc.attached_sas_addr, SAS_ADDR_SIZE);
+ *type = to_dev_type(&disc_resp->disc);
+ if (*type == SAS_PHY_UNUSED)
+ memset(sas_addr, 0, SAS_ADDR_SIZE);
+}
+
static int sas_get_phy_discover(struct domain_device *dev,
- int phy_id, struct smp_resp *disc_resp)
+ int phy_id, struct smp_disc_resp *disc_resp)
{
int res;
u8 *disc_req;
@@ -1567,10 +1664,8 @@ static int sas_get_phy_discover(struct domain_device *dev,
disc_resp, DISCOVER_RESP_SIZE);
if (res)
goto out;
- else if (disc_resp->result != SMP_RESP_FUNC_ACC) {
+ if (disc_resp->result != SMP_RESP_FUNC_ACC)
res = disc_resp->result;
- goto out;
- }
out:
kfree(disc_req);
return res;
@@ -1580,7 +1675,7 @@ static int sas_get_phy_change_count(struct domain_device *dev,
int phy_id, int *pcc)
{
int res;
- struct smp_resp *disc_resp;
+ struct smp_disc_resp *disc_resp;
disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE);
if (!disc_resp)
@@ -1594,24 +1689,19 @@ static int sas_get_phy_change_count(struct domain_device *dev,
return res;
}
-static int sas_get_phy_attached_sas_addr(struct domain_device *dev,
- int phy_id, u8 *attached_sas_addr)
+int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id,
+ u8 *sas_addr, enum sas_device_type *type)
{
int res;
- struct smp_resp *disc_resp;
- struct discover_resp *dr;
+ struct smp_disc_resp *disc_resp;
disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE);
if (!disc_resp)
return -ENOMEM;
- dr = &disc_resp->disc;
res = sas_get_phy_discover(dev, phy_id, disc_resp);
- if (!res) {
- memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8);
- if (dr->attached_dev_type == 0)
- memset(attached_sas_addr, 0, 8);
- }
+ if (res == 0)
+ sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, type);
kfree(disc_resp);
return res;
}
@@ -1627,9 +1717,17 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
int phy_change_count = 0;
res = sas_get_phy_change_count(dev, i, &phy_change_count);
- if (res)
- goto out;
- else if (phy_change_count != ex->ex_phy[i].phy_change_count) {
+ switch (res) {
+ case SMP_RESP_PHY_VACANT:
+ case SMP_RESP_NO_PHY:
+ continue;
+ case SMP_RESP_FUNC_ACC:
+ break;
+ default:
+ return res;
+ }
+
+ if (phy_change_count != ex->ex_phy[i].phy_change_count) {
if (update)
ex->ex_phy[i].phy_change_count =
phy_change_count;
@@ -1637,15 +1735,14 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
return 0;
}
}
-out:
- return res;
+ return 0;
}
static int sas_get_ex_change_count(struct domain_device *dev, int *ecc)
{
int res;
u8 *rg_req;
- struct smp_resp *rg_resp;
+ struct smp_rg_resp *rg_resp;
rg_req = alloc_smp_req(RG_REQ_SIZE);
if (!rg_req)
@@ -1679,7 +1776,7 @@ out:
* @dev:domain device to be detect.
* @src_dev: the device which originated BROADCAST(CHANGE).
*
- * Add self-configuration expander suport. Suppose two expander cascading,
+ * Add self-configuration expander support. Suppose two expander cascading,
* when the first level expander is self-configuring, hotplug the disks in
* second level expander, BROADCAST(CHANGE) will not only be originated
* in the second level expander, but also be originated in the first level
@@ -1710,15 +1807,17 @@ static int sas_find_bcast_dev(struct domain_device *dev,
if (phy_id != -1) {
*src_dev = dev;
ex->ex_change_count = ex_change_count;
- SAS_DPRINTK("Expander phy change count has changed\n");
+ pr_info("ex %016llx phy%02d change count has changed\n",
+ SAS_ADDR(dev->sas_addr), phy_id);
return res;
} else
- SAS_DPRINTK("Expander phys DID NOT change\n");
+ pr_info("ex %016llx phys DID NOT change\n",
+ SAS_ADDR(dev->sas_addr));
}
list_for_each_entry(ch, &ex->children, siblings) {
- if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV) {
+ if (dev_is_expander(ch->dev_type)) {
res = sas_find_bcast_dev(ch, src_dev);
- if (src_dev)
+ if (*src_dev)
return res;
}
}
@@ -1726,20 +1825,19 @@ out:
return res;
}
-static void sas_unregister_ex_tree(struct domain_device *dev)
+static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_device *dev)
{
struct expander_device *ex = &dev->ex_dev;
struct domain_device *child, *n;
list_for_each_entry_safe(child, n, &ex->children, siblings) {
- child->gone = 1;
- if (child->dev_type == EDGE_DEV ||
- child->dev_type == FANOUT_DEV)
- sas_unregister_ex_tree(child);
+ set_bit(SAS_DEV_GONE, &child->state);
+ if (dev_is_expander(child->dev_type))
+ sas_unregister_ex_tree(port, child);
else
- sas_unregister_dev(child);
+ sas_unregister_dev(port, child);
}
- sas_unregister_dev(dev);
+ sas_unregister_dev(port, dev);
}
static void sas_unregister_devs_sas_addr(struct domain_device *parent,
@@ -1747,29 +1845,34 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent,
{
struct expander_device *ex_dev = &parent->ex_dev;
struct ex_phy *phy = &ex_dev->ex_phy[phy_id];
- struct domain_device *child, *n;
+ struct domain_device *child, *n, *found = NULL;
if (last) {
list_for_each_entry_safe(child, n,
&ex_dev->children, siblings) {
- if (SAS_ADDR(child->sas_addr) ==
- SAS_ADDR(phy->attached_sas_addr)) {
- child->gone = 1;
- if (child->dev_type == EDGE_DEV ||
- child->dev_type == FANOUT_DEV)
- sas_unregister_ex_tree(child);
+ if (sas_phy_match_dev_addr(child, phy)) {
+ set_bit(SAS_DEV_GONE, &child->state);
+ if (dev_is_expander(child->dev_type))
+ sas_unregister_ex_tree(parent->port, child);
else
- sas_unregister_dev(child);
+ sas_unregister_dev(parent->port, child);
+ found = child;
break;
}
}
- parent->gone = 1;
sas_disable_routing(parent, phy->attached_sas_addr);
}
memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
- sas_port_delete_phy(phy->port, phy->phy);
- if (phy->port->num_phys == 0)
- sas_port_delete(phy->port);
- phy->port = NULL;
+ if (phy->port) {
+ sas_port_delete_phy(phy->port, phy->phy);
+ sas_device_set_phy(found, phy->port);
+ if (phy->port->num_phys == 0) {
+ list_add_tail(&phy->port->del_list,
+ &parent->port->sas_port_del_list);
+ if (ex_dev->parent_port == phy->port)
+ ex_dev->parent_port = NULL;
+ }
+ phy->port = NULL;
+ }
}
static int sas_discover_bfs_by_root_level(struct domain_device *root,
@@ -1780,8 +1883,7 @@ static int sas_discover_bfs_by_root_level(struct domain_device *root,
int res = 0;
list_for_each_entry(child, &ex_root->children, siblings) {
- if (child->dev_type == EDGE_DEV ||
- child->dev_type == FANOUT_DEV) {
+ if (dev_is_expander(child->dev_type)) {
struct sas_expander_device *ex =
rphy_to_expander_device(child->rphy);
@@ -1817,77 +1919,121 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
{
struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id];
struct domain_device *child;
- bool found = false;
- int res, i;
+ int res;
- SAS_DPRINTK("ex %016llx phy%d new device attached\n",
- SAS_ADDR(dev->sas_addr), phy_id);
+ pr_debug("ex %016llx phy%02d new device attached\n",
+ SAS_ADDR(dev->sas_addr), phy_id);
res = sas_ex_phy_discover(dev, phy_id);
if (res)
- goto out;
- /* to support the wide port inserted */
- for (i = 0; i < dev->ex_dev.num_phys; i++) {
- struct ex_phy *ex_phy_temp = &dev->ex_dev.ex_phy[i];
- if (i == phy_id)
- continue;
- if (SAS_ADDR(ex_phy_temp->attached_sas_addr) ==
- SAS_ADDR(ex_phy->attached_sas_addr)) {
- found = true;
- break;
- }
- }
- if (found) {
- sas_ex_join_wide_port(dev, phy_id);
+ return res;
+
+ if (sas_ex_join_wide_port(dev, phy_id))
return 0;
- }
+
res = sas_ex_discover_devices(dev, phy_id);
- if (!res)
- goto out;
+ if (res)
+ return res;
list_for_each_entry(child, &dev->ex_dev.children, siblings) {
- if (SAS_ADDR(child->sas_addr) ==
- SAS_ADDR(ex_phy->attached_sas_addr)) {
- if (child->dev_type == EDGE_DEV ||
- child->dev_type == FANOUT_DEV)
+ if (sas_phy_match_dev_addr(child, ex_phy)) {
+ if (dev_is_expander(child->dev_type))
res = sas_discover_bfs_by_root(child);
break;
}
}
-out:
return res;
}
-static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
+static bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old)
+{
+ if (old == new)
+ return true;
+
+ /* treat device directed resets as flutter, if we went
+ * SAS_END_DEVICE to SAS_SATA_PENDING the link needs recovery
+ */
+ if ((old == SAS_SATA_PENDING && new == SAS_END_DEVICE) ||
+ (old == SAS_END_DEVICE && new == SAS_SATA_PENDING))
+ return true;
+
+ return false;
+}
+
+static int sas_rediscover_dev(struct domain_device *dev, int phy_id,
+ bool last, int sibling)
{
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
- u8 attached_sas_addr[8];
+ enum sas_device_type type = SAS_PHY_UNUSED;
+ struct smp_disc_resp *disc_resp;
+ u8 sas_addr[SAS_ADDR_SIZE];
+ char msg[80] = "";
int res;
- res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr);
+ if (!last)
+ sprintf(msg, ", part of a wide port with phy%02d", sibling);
+
+ pr_debug("ex %016llx rediscovering phy%02d%s\n",
+ SAS_ADDR(dev->sas_addr), phy_id, msg);
+
+ memset(sas_addr, 0, SAS_ADDR_SIZE);
+ disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE);
+ if (!disc_resp)
+ return -ENOMEM;
+
+ res = sas_get_phy_discover(dev, phy_id, disc_resp);
switch (res) {
case SMP_RESP_NO_PHY:
phy->phy_state = PHY_NOT_PRESENT;
sas_unregister_devs_sas_addr(dev, phy_id, last);
- goto out; break;
+ goto out_free_resp;
case SMP_RESP_PHY_VACANT:
phy->phy_state = PHY_VACANT;
sas_unregister_devs_sas_addr(dev, phy_id, last);
- goto out; break;
+ goto out_free_resp;
case SMP_RESP_FUNC_ACC:
break;
+ case -ECOMM:
+ break;
+ default:
+ goto out_free_resp;
}
- if (SAS_ADDR(attached_sas_addr) == 0) {
+ if (res == 0)
+ sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, &type);
+
+ if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) {
phy->phy_state = PHY_EMPTY;
sas_unregister_devs_sas_addr(dev, phy_id, last);
- } else if (SAS_ADDR(attached_sas_addr) ==
- SAS_ADDR(phy->attached_sas_addr)) {
- SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
- SAS_ADDR(dev->sas_addr), phy_id);
+ /*
+ * Even though the PHY is empty, for convenience we update
+ * the PHY info, like negotiated linkrate.
+ */
+ if (res == 0)
+ sas_set_ex_phy(dev, phy_id, disc_resp);
+ goto out_free_resp;
+ } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) &&
+ dev_type_flutter(type, phy->attached_dev_type)) {
+ struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id);
+ char *action = "";
+
sas_ex_phy_discover(dev, phy_id);
- } else
- res = sas_discover_new(dev, phy_id);
-out:
+
+ if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING)
+ action = ", needs recovery";
+ pr_debug("ex %016llx phy%02d broadcast flutter%s\n",
+ SAS_ADDR(dev->sas_addr), phy_id, action);
+ goto out_free_resp;
+ }
+
+ /* we always have to delete the old device when we went here */
+ pr_info("ex %016llx phy%02d replace %016llx\n",
+ SAS_ADDR(dev->sas_addr), phy_id,
+ SAS_ADDR(phy->attached_sas_addr));
+ sas_unregister_devs_sas_addr(dev, phy_id, last);
+
+ res = sas_discover_new(dev, phy_id);
+out_free_resp:
+ kfree(disc_resp);
return res;
}
@@ -1913,8 +2059,8 @@ static int sas_rediscover(struct domain_device *dev, const int phy_id)
int i;
bool last = true; /* is this the last phy of the port */
- SAS_DPRINTK("ex %016llx phy%d originated BROADCAST(CHANGE)\n",
- SAS_ADDR(dev->sas_addr), phy_id);
+ pr_debug("ex %016llx phy%02d originated BROADCAST(CHANGE)\n",
+ SAS_ADDR(dev->sas_addr), phy_id);
if (SAS_ADDR(changed_phy->attached_sas_addr) != 0) {
for (i = 0; i < ex->num_phys; i++) {
@@ -1922,23 +2068,20 @@ static int sas_rediscover(struct domain_device *dev, const int phy_id)
if (i == phy_id)
continue;
- if (SAS_ADDR(phy->attached_sas_addr) ==
- SAS_ADDR(changed_phy->attached_sas_addr)) {
- SAS_DPRINTK("phy%d part of wide port with "
- "phy%d\n", phy_id, i);
+ if (sas_phy_addr_match(phy, changed_phy)) {
last = false;
break;
}
}
- res = sas_rediscover_dev(dev, phy_id, last);
+ res = sas_rediscover_dev(dev, phy_id, last, i);
} else
res = sas_discover_new(dev, phy_id);
return res;
}
/**
- * sas_revalidate_domain -- revalidate the domain
- * @port: port to the domain of interest
+ * sas_ex_revalidate_domain - revalidate the domain
+ * @port_dev: port domain device.
*
* NOTE: this process _must_ quit (return) as soon as any connection
* errors are encountered. Connection recovery is done elsewhere.
@@ -1951,9 +2094,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
struct domain_device *dev = NULL;
res = sas_find_bcast_dev(port_dev, &dev);
- if (res)
- goto out;
- if (dev) {
+ if (res == 0 && dev) {
struct expander_device *ex = &dev->ex_dev;
int i = 0, phy_id;
@@ -1966,61 +2107,69 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
i = phy_id + 1;
} while (i < ex->num_phys);
}
-out:
return res;
}
-int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
- struct request *req)
+int sas_find_attached_phy_id(struct expander_device *ex_dev,
+ struct domain_device *dev)
{
- struct domain_device *dev;
- int ret, type;
- struct request *rsp = req->next_rq;
+ struct ex_phy *phy;
+ int phy_id;
- if (!rsp) {
- printk("%s: space for a smp response is missing\n",
- __func__);
- return -EINVAL;
+ for (phy_id = 0; phy_id < ex_dev->num_phys; phy_id++) {
+ phy = &ex_dev->ex_phy[phy_id];
+ if (sas_phy_match_dev_addr(dev, phy))
+ return phy_id;
}
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(sas_find_attached_phy_id);
+
+void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
+ struct sas_rphy *rphy)
+{
+ struct domain_device *dev;
+ unsigned int rcvlen = 0;
+ int ret = -EINVAL;
+
/* no rphy means no smp target support (ie aic94xx host) */
if (!rphy)
- return sas_smp_host_handler(shost, req, rsp);
-
- type = rphy->identify.device_type;
+ return sas_smp_host_handler(job, shost);
- if (type != SAS_EDGE_EXPANDER_DEVICE &&
- type != SAS_FANOUT_EXPANDER_DEVICE) {
- printk("%s: can we send a smp request to a device?\n",
+ switch (rphy->identify.device_type) {
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ break;
+ default:
+ pr_err("%s: can we send a smp request to a device?\n",
__func__);
- return -EINVAL;
+ goto out;
}
dev = sas_find_dev_by_rphy(rphy);
if (!dev) {
- printk("%s: fail to find a domain_device?\n", __func__);
- return -EINVAL;
+ pr_err("%s: fail to find a domain_device?\n", __func__);
+ goto out;
}
/* do we need to support multiple segments? */
- if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
- printk("%s: multiple segments req %u %u, rsp %u %u\n",
- __func__, req->bio->bi_vcnt, blk_rq_bytes(req),
- rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
- return -EINVAL;
- }
-
- ret = smp_execute_task(dev, bio_data(req->bio), blk_rq_bytes(req),
- bio_data(rsp->bio), blk_rq_bytes(rsp));
- if (ret > 0) {
- /* positive number is the untransferred residual */
- rsp->resid_len = ret;
- req->resid_len = 0;
+ if (job->request_payload.sg_cnt > 1 ||
+ job->reply_payload.sg_cnt > 1) {
+ pr_info("%s: multiple segments req %u, rsp %u\n",
+ __func__, job->request_payload.payload_len,
+ job->reply_payload.payload_len);
+ goto out;
+ }
+
+ ret = smp_execute_task_sg(dev, job->request_payload.sg_list,
+ job->reply_payload.sg_list);
+ if (ret >= 0) {
+ /* bsg_job_done() requires the length received */
+ rcvlen = job->reply_payload.payload_len - ret;
ret = 0;
- } else if (ret == 0) {
- rsp->resid_len = 0;
- req->resid_len = 0;
}
- return ret;
+out:
+ bsg_job_done(job, ret, rcvlen);
}
diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c
index 04ad8dd1a74c..2ecb8535634c 100644
--- a/drivers/scsi/libsas/sas_host_smp.c
+++ b/drivers/scsi/libsas/sas_host_smp.c
@@ -1,22 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Serial Attached SCSI (SAS) Expander discovery and configuration
*
* Copyright (C) 2007 James E.J. Bottomley
* <James.Bottomley@HansenPartnership.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 only.
*/
#include <linux/scatterlist.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
+#include <linux/export.h>
#include "sas_internal.h"
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
-#include "../scsi_sas_internal.h"
+#include "scsi_sas_internal.h"
static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data,
u8 phy_id)
@@ -51,6 +49,91 @@ static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data,
resp_data[15] = rphy->identify.target_port_protocols;
}
+/**
+ * to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od'
+ * @od: od bit to find
+ * @data: incoming bitstream (from frame)
+ * @index: requested data register index (from frame)
+ * @count: total number of registers in the bitstream (from frame)
+ * @bit: bit position of 'od' in the returned byte
+ *
+ * returns NULL if 'od' is not in 'data'
+ *
+ * From SFF-8485 v0.7:
+ * "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0)
+ * and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1).
+ *
+ * In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2)
+ * and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)."
+ *
+ * The general-purpose (raw-bitstream) RX registers have the same layout
+ * although 'od' is renamed 'id' for 'input data'.
+ *
+ * SFF-8489 defines the behavior of the LEDs in response to the 'od' values.
+ */
+static u8 *to_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count, u8 *bit)
+{
+ unsigned int reg;
+ u8 byte;
+
+ /* gp registers start at index 1 */
+ if (index == 0)
+ return NULL;
+
+ index--; /* make index 0-based */
+ if (od < index * 32)
+ return NULL;
+
+ od -= index * 32;
+ reg = od >> 5;
+
+ if (reg >= count)
+ return NULL;
+
+ od &= (1 << 5) - 1;
+ byte = 3 - (od >> 3);
+ *bit = od & ((1 << 3) - 1);
+
+ return &data[reg * 4 + byte];
+}
+
+int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count)
+{
+ u8 *byte;
+ u8 bit;
+
+ byte = to_sas_gpio_gp_bit(od, data, index, count, &bit);
+ if (!byte)
+ return -1;
+
+ return (*byte >> bit) & 1;
+}
+EXPORT_SYMBOL(try_test_sas_gpio_gp_bit);
+
+static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data,
+ u8 reg_type, u8 reg_index, u8 reg_count,
+ u8 *req_data)
+{
+ struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt);
+ int written;
+
+ if (i->dft->lldd_write_gpio == NULL) {
+ resp_data[2] = SMP_RESP_FUNC_UNK;
+ return 0;
+ }
+
+ written = i->dft->lldd_write_gpio(sas_ha, reg_type, reg_index,
+ reg_count, req_data);
+
+ if (written < 0) {
+ resp_data[2] = SMP_RESP_FUNC_FAILED;
+ written = 0;
+ } else
+ resp_data[2] = SMP_RESP_FUNC_ACC;
+
+ return written;
+}
+
static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data,
u8 phy_id)
{
@@ -99,13 +182,16 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
enum sas_linkrate max, u8 *resp_data)
{
struct sas_internal *i =
- to_sas_internal(sas_ha->core.shost->transportt);
+ to_sas_internal(sas_ha->shost->transportt);
struct sas_phy_linkrates rates;
+ struct asd_sas_phy *asd_phy;
if (phy_id >= sas_ha->num_phys) {
resp_data[2] = SMP_RESP_NO_PHY;
return;
}
+
+ asd_phy = sas_ha->sas_phy[phy_id];
switch (phy_op) {
case PHY_FUNC_NOP:
case PHY_FUNC_LINK_RESET:
@@ -124,53 +210,48 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
rates.minimum_linkrate = min;
rates.maximum_linkrate = max;
- if (i->dft->lldd_control_phy(sas_ha->sas_phy[phy_id], phy_op, &rates))
+ /* filter reset requests through libata eh */
+ if (phy_op == PHY_FUNC_LINK_RESET && sas_try_ata_reset(asd_phy) == 0) {
+ resp_data[2] = SMP_RESP_FUNC_ACC;
+ return;
+ }
+
+ if (i->dft->lldd_control_phy(asd_phy, phy_op, &rates))
resp_data[2] = SMP_RESP_FUNC_FAILED;
else
resp_data[2] = SMP_RESP_FUNC_ACC;
}
-int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
- struct request *rsp)
+void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost)
{
- u8 *req_data = NULL, *resp_data = NULL, *buf;
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ u8 *req_data, *resp_data;
+ unsigned int reslen = 0;
int error = -EINVAL;
/* eight is the minimum size for request and response frames */
- if (blk_rq_bytes(req) < 8 || blk_rq_bytes(rsp) < 8)
+ if (job->request_payload.payload_len < 8 ||
+ job->reply_payload.payload_len < 8)
goto out;
- if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE ||
- bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) {
- shost_printk(KERN_ERR, shost,
- "SMP request/response frame crosses page boundary");
+ error = -ENOMEM;
+ req_data = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
+ if (!req_data)
goto out;
- }
-
- req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL);
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt, req_data,
+ job->request_payload.payload_len);
/* make sure frame can always be built ... we copy
* back only the requested length */
- resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL);
-
- if (!req_data || !resp_data) {
- error = -ENOMEM;
- goto out;
- }
-
- local_irq_disable();
- buf = kmap_atomic(bio_page(req->bio), KM_USER0) + bio_offset(req->bio);
- memcpy(req_data, buf, blk_rq_bytes(req));
- kunmap_atomic(buf - bio_offset(req->bio), KM_USER0);
- local_irq_enable();
+ resp_data = kzalloc(max(job->reply_payload.payload_len, 128U),
+ GFP_KERNEL);
+ if (!resp_data)
+ goto out_free_req;
+ error = -EINVAL;
if (req_data[0] != SMP_REQUEST)
- goto out;
-
- /* always succeeds ... even if we can't process the request
- * the result is in the response frame */
- error = 0;
+ goto out_free_resp;
/* set up default don't know response */
resp_data[0] = SMP_RESPONSE;
@@ -179,20 +260,18 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
switch (req_data[1]) {
case SMP_REPORT_GENERAL:
- req->resid_len -= 8;
- rsp->resid_len -= 32;
resp_data[2] = SMP_RESP_FUNC_ACC;
resp_data[9] = sas_ha->num_phys;
+ reslen = 32;
break;
case SMP_REPORT_MANUF_INFO:
- req->resid_len -= 8;
- rsp->resid_len -= 64;
resp_data[2] = SMP_RESP_FUNC_ACC;
memcpy(resp_data + 12, shost->hostt->name,
SAS_EXPANDER_VENDOR_ID_LEN);
memcpy(resp_data + 20, "libsas virt phy",
SAS_EXPANDER_PRODUCT_ID_LEN);
+ reslen = 64;
break;
case SMP_READ_GPIO_REG:
@@ -200,14 +279,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_DISCOVER:
- req->resid_len -= 16;
- if ((int)req->resid_len < 0) {
- req->resid_len = 0;
- error = -EINVAL;
- goto out;
- }
- rsp->resid_len -= 56;
+ if (job->request_payload.payload_len < 16)
+ goto out_free_resp;
sas_host_smp_discover(sas_ha, resp_data, req_data[9]);
+ reslen = 56;
break;
case SMP_REPORT_PHY_ERR_LOG:
@@ -216,39 +291,44 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_REPORT_PHY_SATA:
- req->resid_len -= 16;
- if ((int)req->resid_len < 0) {
- req->resid_len = 0;
- error = -EINVAL;
- goto out;
- }
- rsp->resid_len -= 60;
+ if (job->request_payload.payload_len < 16)
+ goto out_free_resp;
sas_report_phy_sata(sas_ha, resp_data, req_data[9]);
+ reslen = 60;
break;
case SMP_REPORT_ROUTE_INFO:
/* Can't implement; hosts have no routes */
break;
- case SMP_WRITE_GPIO_REG:
- /* FIXME: need GPIO support in the transport class */
+ case SMP_WRITE_GPIO_REG: {
+ /* SFF-8485 v0.7 */
+ const int base_frame_size = 11;
+ int to_write = req_data[4];
+
+ if (job->request_payload.payload_len <
+ base_frame_size + to_write * 4) {
+ resp_data[2] = SMP_RESP_INV_FRM_LEN;
+ break;
+ }
+
+ to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2],
+ req_data[3], to_write, &req_data[8]);
+ reslen = 8;
break;
+ }
case SMP_CONF_ROUTE_INFO:
/* Can't implement; hosts have no routes */
break;
case SMP_PHY_CONTROL:
- req->resid_len -= 44;
- if ((int)req->resid_len < 0) {
- req->resid_len = 0;
- error = -EINVAL;
- goto out;
- }
- rsp->resid_len -= 8;
+ if (job->request_payload.payload_len < 44)
+ goto out_free_resp;
sas_phy_control(sas_ha, req_data[9], req_data[10],
req_data[32] >> 4, req_data[33] >> 4,
resp_data);
+ reslen = 8;
break;
case SMP_PHY_TEST_FUNCTION:
@@ -260,15 +340,15 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
}
- local_irq_disable();
- buf = kmap_atomic(bio_page(rsp->bio), KM_USER0) + bio_offset(rsp->bio);
- memcpy(buf, resp_data, blk_rq_bytes(rsp));
- flush_kernel_dcache_page(bio_page(rsp->bio));
- kunmap_atomic(buf - bio_offset(rsp->bio), KM_USER0);
- local_irq_enable();
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, resp_data,
+ job->reply_payload.payload_len);
- out:
- kfree(req_data);
+ error = 0;
+out_free_resp:
kfree(resp_data);
- return error;
+out_free_req:
+ kfree(req_data);
+out:
+ bsg_job_done(job, error, reslen);
}
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index 2dc55343f671..8566bb1208a0 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -1,26 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Serial Attached SCSI (SAS) Transport Layer initialization
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
*/
#include <linux/module.h>
@@ -28,6 +11,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/spinlock.h>
+#include <scsi/sas_ata.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport.h>
@@ -35,166 +19,280 @@
#include "sas_internal.h"
-#include "../scsi_sas_internal.h"
+#include "scsi_sas_internal.h"
-struct kmem_cache *sas_task_cache;
+static struct kmem_cache *sas_task_cache;
+static struct kmem_cache *sas_event_cache;
-/*------------ SAS addr hash -----------*/
-void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
+struct sas_task *sas_alloc_task(gfp_t flags)
{
- const u32 poly = 0x00DB2777;
- u32 r = 0;
- int i;
+ struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags);
- for (i = 0; i < 8; i++) {
- int b;
- for (b = 7; b >= 0; b--) {
- r <<= 1;
- if ((1 << b) & sas_addr[i]) {
- if (!(r & 0x01000000))
- r ^= poly;
- } else if (r & 0x01000000)
- r ^= poly;
- }
- }
+ if (task) {
+ spin_lock_init(&task->task_state_lock);
+ task->task_state_flags = SAS_TASK_STATE_PENDING;
+ }
- hashed[0] = (r >> 16) & 0xFF;
- hashed[1] = (r >> 8) & 0xFF ;
- hashed[2] = r & 0xFF;
+ return task;
}
+struct sas_task *sas_alloc_slow_task(gfp_t flags)
+{
+ struct sas_task *task = sas_alloc_task(flags);
+ struct sas_task_slow *slow = kmalloc(sizeof(*slow), flags);
+
+ if (!task || !slow) {
+ if (task)
+ kmem_cache_free(sas_task_cache, task);
+ kfree(slow);
+ return NULL;
+ }
+
+ task->slow_task = slow;
+ slow->task = task;
+ timer_setup(&slow->timer, NULL, 0);
+ init_completion(&slow->completion);
-/* ---------- HA events ---------- */
+ return task;
+}
-void sas_hae_reset(struct work_struct *work)
+void sas_free_task(struct sas_task *task)
{
- struct sas_ha_event *ev =
- container_of(work, struct sas_ha_event, work);
- struct sas_ha_struct *ha = ev->ha;
+ if (task) {
+ kfree(task->slow_task);
+ kmem_cache_free(sas_task_cache, task);
+ }
+}
- sas_begin_event(HAE_RESET, &ha->event_lock,
- &ha->pending);
+/*------------ SAS addr hash -----------*/
+void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
+{
+ const u32 poly = 0x00DB2777;
+ u32 r = 0;
+ int i;
+
+ for (i = 0; i < SAS_ADDR_SIZE; i++) {
+ int b;
+
+ for (b = (SAS_ADDR_SIZE - 1); b >= 0; b--) {
+ r <<= 1;
+ if ((1 << b) & sas_addr[i]) {
+ if (!(r & 0x01000000))
+ r ^= poly;
+ } else if (r & 0x01000000) {
+ r ^= poly;
+ }
+ }
+ }
+
+ hashed[0] = (r >> 16) & 0xFF;
+ hashed[1] = (r >> 8) & 0xFF;
+ hashed[2] = r & 0xFF;
}
int sas_register_ha(struct sas_ha_struct *sas_ha)
{
+ char name[64];
int error = 0;
+ mutex_init(&sas_ha->disco_mutex);
spin_lock_init(&sas_ha->phy_port_lock);
sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr);
- if (sas_ha->lldd_queue_size == 0)
- sas_ha->lldd_queue_size = 1;
- else if (sas_ha->lldd_queue_size == -1)
- sas_ha->lldd_queue_size = 128; /* Sanity */
+ set_bit(SAS_HA_REGISTERED, &sas_ha->state);
+ spin_lock_init(&sas_ha->lock);
+ mutex_init(&sas_ha->drain_mutex);
+ init_waitqueue_head(&sas_ha->eh_wait_q);
+ INIT_LIST_HEAD(&sas_ha->defer_q);
+ INIT_LIST_HEAD(&sas_ha->eh_dev_q);
- sas_ha->state = SAS_HA_REGISTERED;
- spin_lock_init(&sas_ha->state_lock);
+ sas_ha->event_thres = SAS_PHY_SHUTDOWN_THRES;
error = sas_register_phys(sas_ha);
if (error) {
- printk(KERN_NOTICE "couldn't register sas phys:%d\n", error);
+ pr_notice("couldn't register sas phys:%d\n", error);
return error;
}
error = sas_register_ports(sas_ha);
if (error) {
- printk(KERN_NOTICE "couldn't register sas ports:%d\n", error);
+ pr_notice("couldn't register sas ports:%d\n", error);
goto Undo_phys;
}
- error = sas_init_events(sas_ha);
- if (error) {
- printk(KERN_NOTICE "couldn't start event thread:%d\n", error);
+ error = -ENOMEM;
+ snprintf(name, sizeof(name), "%s_event_q", dev_name(sas_ha->dev));
+ sas_ha->event_q = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name);
+ if (!sas_ha->event_q)
goto Undo_ports;
- }
- if (sas_ha->lldd_max_execute_num > 1) {
- error = sas_init_queue(sas_ha);
- if (error) {
- printk(KERN_NOTICE "couldn't start queue thread:%d, "
- "running in direct mode\n", error);
- sas_ha->lldd_max_execute_num = 1;
- }
- }
+ snprintf(name, sizeof(name), "%s_disco_q", dev_name(sas_ha->dev));
+ sas_ha->disco_q = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name);
+ if (!sas_ha->disco_q)
+ goto Undo_event_q;
INIT_LIST_HEAD(&sas_ha->eh_done_q);
+ INIT_LIST_HEAD(&sas_ha->eh_ata_q);
return 0;
+Undo_event_q:
+ destroy_workqueue(sas_ha->event_q);
Undo_ports:
sas_unregister_ports(sas_ha);
Undo_phys:
return error;
}
+EXPORT_SYMBOL_GPL(sas_register_ha);
-int sas_unregister_ha(struct sas_ha_struct *sas_ha)
+static void sas_disable_events(struct sas_ha_struct *sas_ha)
{
- unsigned long flags;
-
- /* Set the state to unregistered to avoid further
- * events to be queued */
- spin_lock_irqsave(&sas_ha->state_lock, flags);
- sas_ha->state = SAS_HA_UNREGISTERED;
- spin_unlock_irqrestore(&sas_ha->state_lock, flags);
- scsi_flush_work(sas_ha->core.shost);
+ /* Set the state to unregistered to avoid further unchained
+ * events to be queued, and flush any in-progress drainers
+ */
+ mutex_lock(&sas_ha->drain_mutex);
+ spin_lock_irq(&sas_ha->lock);
+ clear_bit(SAS_HA_REGISTERED, &sas_ha->state);
+ spin_unlock_irq(&sas_ha->lock);
+ __sas_drain_work(sas_ha);
+ mutex_unlock(&sas_ha->drain_mutex);
+}
+int sas_unregister_ha(struct sas_ha_struct *sas_ha)
+{
+ sas_disable_events(sas_ha);
sas_unregister_ports(sas_ha);
- if (sas_ha->lldd_max_execute_num > 1) {
- sas_shutdown_queue(sas_ha);
- sas_ha->lldd_max_execute_num = 1;
- }
+ /* flush unregistration work */
+ mutex_lock(&sas_ha->drain_mutex);
+ __sas_drain_work(sas_ha);
+ mutex_unlock(&sas_ha->drain_mutex);
+
+ destroy_workqueue(sas_ha->disco_q);
+ destroy_workqueue(sas_ha->event_q);
return 0;
}
+EXPORT_SYMBOL_GPL(sas_unregister_ha);
static int sas_get_linkerrors(struct sas_phy *phy)
{
- if (scsi_is_sas_phy_local(phy))
- /* FIXME: we have no local phy stats
- * gathering at this time */
- return -EINVAL;
+ if (scsi_is_sas_phy_local(phy)) {
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+ struct sas_internal *i =
+ to_sas_internal(sas_ha->shost->transportt);
+
+ return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL);
+ }
return sas_smp_get_phy_events(phy);
}
+int sas_try_ata_reset(struct asd_sas_phy *asd_phy)
+{
+ struct domain_device *dev = NULL;
+
+ /* try to route user requested link resets through libata */
+ if (asd_phy->port)
+ dev = asd_phy->port->port_dev;
+
+ /* validate that dev has been probed */
+ if (dev)
+ dev = sas_find_dev_by_rphy(dev->rphy);
+
+ if (dev && dev_is_sata(dev)) {
+ sas_ata_schedule_reset(dev);
+ sas_ata_wait_eh(dev);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+/*
+ * transport_sas_phy_reset - reset a phy and permit libata to manage the link
+ *
+ * phy reset request via sysfs in host workqueue context so we know we
+ * can block on eh and safely traverse the domain_device topology
+ */
+static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ enum phy_func reset_type;
+
+ if (hard_reset)
+ reset_type = PHY_FUNC_HARD_RESET;
+ else
+ reset_type = PHY_FUNC_LINK_RESET;
+
+ if (scsi_is_sas_phy_local(phy)) {
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+ struct sas_internal *i =
+ to_sas_internal(sas_ha->shost->transportt);
+
+ if (!hard_reset && sas_try_ata_reset(asd_phy) == 0)
+ return 0;
+ return i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
+ } else {
+ struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
+ struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
+ struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number);
+
+ if (ata_dev && !hard_reset) {
+ sas_ata_schedule_reset(ata_dev);
+ sas_ata_wait_eh(ata_dev);
+ return 0;
+ } else
+ return sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
+ }
+}
+
int sas_phy_enable(struct sas_phy *phy, int enable)
{
int ret;
- enum phy_func command;
+ enum phy_func cmd;
if (enable)
- command = PHY_FUNC_LINK_RESET;
+ cmd = PHY_FUNC_LINK_RESET;
else
- command = PHY_FUNC_DISABLE;
+ cmd = PHY_FUNC_DISABLE;
if (scsi_is_sas_phy_local(phy)) {
struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
struct sas_internal *i =
- to_sas_internal(sas_ha->core.shost->transportt);
+ to_sas_internal(sas_ha->shost->transportt);
- if (!enable) {
- sas_phy_disconnected(asd_phy);
- sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL);
- }
- ret = i->dft->lldd_control_phy(asd_phy, command, NULL);
+ if (enable)
+ ret = transport_sas_phy_reset(phy, 0);
+ else
+ ret = i->dft->lldd_control_phy(asd_phy, cmd, NULL);
} else {
struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
- ret = sas_smp_phy_control(ddev, phy->number, command, NULL);
+
+ if (enable)
+ ret = transport_sas_phy_reset(phy, 0);
+ else
+ ret = sas_smp_phy_control(ddev, phy->number, cmd, NULL);
}
return ret;
}
+EXPORT_SYMBOL_GPL(sas_phy_enable);
int sas_phy_reset(struct sas_phy *phy, int hard_reset)
{
int ret;
enum phy_func reset_type;
+ if (!phy->enabled)
+ return -ENODEV;
+
if (hard_reset)
reset_type = PHY_FUNC_HARD_RESET;
else
@@ -205,7 +303,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
struct sas_internal *i =
- to_sas_internal(sas_ha->core.shost->transportt);
+ to_sas_internal(sas_ha->shost->transportt);
ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
} else {
@@ -215,9 +313,10 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
}
return ret;
}
+EXPORT_SYMBOL_GPL(sas_phy_reset);
-int sas_set_phy_speed(struct sas_phy *phy,
- struct sas_phy_linkrates *rates)
+static int sas_set_phy_speed(struct sas_phy *phy,
+ struct sas_phy_linkrates *rates)
{
int ret;
@@ -240,7 +339,7 @@ int sas_set_phy_speed(struct sas_phy *phy,
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
struct sas_internal *i =
- to_sas_internal(sas_ha->core.shost->transportt);
+ to_sas_internal(sas_ha->shost->transportt);
ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE,
rates);
@@ -255,14 +354,269 @@ int sas_set_phy_speed(struct sas_phy *phy,
return ret;
}
+void sas_prep_resume_ha(struct sas_ha_struct *ha)
+{
+ int i;
+
+ set_bit(SAS_HA_REGISTERED, &ha->state);
+ set_bit(SAS_HA_RESUMING, &ha->state);
+
+ /* clear out any stale link events/data from the suspension path */
+ for (i = 0; i < ha->num_phys; i++) {
+ struct asd_sas_phy *phy = ha->sas_phy[i];
+
+ memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
+ phy->frame_rcvd_size = 0;
+ }
+}
+EXPORT_SYMBOL(sas_prep_resume_ha);
+
+static int phys_suspended(struct sas_ha_struct *ha)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ha->num_phys; i++) {
+ struct asd_sas_phy *phy = ha->sas_phy[i];
+
+ if (phy->suspended)
+ rc++;
+ }
+
+ return rc;
+}
+
+static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
+{
+ int i;
+
+ for (i = 0; i < ha->num_phys; i++) {
+ struct asd_sas_port *port = ha->sas_port[i];
+ struct domain_device *dev = port->port_dev;
+
+ if (dev && dev_is_expander(dev->dev_type)) {
+ struct asd_sas_phy *first_phy;
+
+ spin_lock(&port->phy_list_lock);
+ first_phy = list_first_entry_or_null(
+ &port->phy_list, struct asd_sas_phy,
+ port_phy_el);
+ spin_unlock(&port->phy_list_lock);
+
+ if (first_phy)
+ sas_notify_port_event(first_phy,
+ PORTE_BROADCAST_RCVD, GFP_KERNEL);
+ }
+ }
+}
+
+static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+{
+ const unsigned long tmo = msecs_to_jiffies(25000);
+ int i;
+
+ /* deform ports on phys that did not resume
+ * at this point we may be racing the phy coming back (as posted
+ * by the lldd). So we post the event and once we are in the
+ * libsas context check that the phy remains suspended before
+ * tearing it down.
+ */
+ i = phys_suspended(ha);
+ if (i)
+ dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
+ i, i > 1 ? "s" : "");
+ wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
+ for (i = 0; i < ha->num_phys; i++) {
+ struct asd_sas_phy *phy = ha->sas_phy[i];
+
+ if (phy->suspended) {
+ dev_warn(&phy->phy->dev, "resume timeout\n");
+ sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT,
+ GFP_KERNEL);
+ }
+ }
+
+ /* all phys are back up or timed out, turn on i/o so we can
+ * flush out disks that did not return
+ */
+ scsi_unblock_requests(ha->shost);
+ if (drain)
+ sas_drain_work(ha);
+ clear_bit(SAS_HA_RESUMING, &ha->state);
+
+ sas_queue_deferred_work(ha);
+ /* send event PORTE_BROADCAST_RCVD to identify some new inserted
+ * disks for expander
+ */
+ sas_resume_insert_broadcast_ha(ha);
+}
+
+void sas_resume_ha(struct sas_ha_struct *ha)
+{
+ _sas_resume_ha(ha, true);
+}
+EXPORT_SYMBOL(sas_resume_ha);
+
+/* A no-sync variant, which does not call sas_drain_ha(). */
+void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
+{
+ _sas_resume_ha(ha, false);
+}
+EXPORT_SYMBOL(sas_resume_ha_no_sync);
+
+void sas_suspend_ha(struct sas_ha_struct *ha)
+{
+ int i;
+
+ sas_disable_events(ha);
+ scsi_block_requests(ha->shost);
+ for (i = 0; i < ha->num_phys; i++) {
+ struct asd_sas_port *port = ha->sas_port[i];
+
+ sas_discover_event(port, DISCE_SUSPEND);
+ }
+
+ /* flush suspend events while unregistered */
+ mutex_lock(&ha->drain_mutex);
+ __sas_drain_work(ha);
+ mutex_unlock(&ha->drain_mutex);
+}
+EXPORT_SYMBOL(sas_suspend_ha);
+
+static void sas_phy_release(struct sas_phy *phy)
+{
+ kfree(phy->hostdata);
+ phy->hostdata = NULL;
+}
+
+static void phy_reset_work(struct work_struct *work)
+{
+ struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work);
+
+ d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset);
+}
+
+static void phy_enable_work(struct work_struct *work)
+{
+ struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work);
+
+ d->enable_result = sas_phy_enable(d->phy, d->enable);
+}
+
+static int sas_phy_setup(struct sas_phy *phy)
+{
+ struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL);
+
+ if (!d)
+ return -ENOMEM;
+
+ mutex_init(&d->event_lock);
+ INIT_SAS_WORK(&d->reset_work, phy_reset_work);
+ INIT_SAS_WORK(&d->enable_work, phy_enable_work);
+ d->phy = phy;
+ phy->hostdata = d;
+
+ return 0;
+}
+
+static int queue_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct sas_phy_data *d = phy->hostdata;
+ int rc;
+
+ if (!d)
+ return -ENOMEM;
+
+ pm_runtime_get_sync(ha->dev);
+ /* libsas workqueue coordinates ata-eh reset with discovery */
+ mutex_lock(&d->event_lock);
+ d->reset_result = 0;
+ d->hard_reset = hard_reset;
+
+ spin_lock_irq(&ha->lock);
+ sas_queue_work(ha, &d->reset_work);
+ spin_unlock_irq(&ha->lock);
+
+ rc = sas_drain_work(ha);
+ if (rc == 0)
+ rc = d->reset_result;
+ mutex_unlock(&d->event_lock);
+ pm_runtime_put_sync(ha->dev);
+
+ return rc;
+}
+
+static int queue_phy_enable(struct sas_phy *phy, int enable)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct sas_phy_data *d = phy->hostdata;
+ int rc;
+
+ if (!d)
+ return -ENOMEM;
+
+ pm_runtime_get_sync(ha->dev);
+ /* libsas workqueue coordinates ata-eh reset with discovery */
+ mutex_lock(&d->event_lock);
+ d->enable_result = 0;
+ d->enable = enable;
+
+ spin_lock_irq(&ha->lock);
+ sas_queue_work(ha, &d->enable_work);
+ spin_unlock_irq(&ha->lock);
+
+ rc = sas_drain_work(ha);
+ if (rc == 0)
+ rc = d->enable_result;
+ mutex_unlock(&d->event_lock);
+ pm_runtime_put_sync(ha->dev);
+
+ return rc;
+}
+
static struct sas_function_template sft = {
- .phy_enable = sas_phy_enable,
- .phy_reset = sas_phy_reset,
+ .phy_enable = queue_phy_enable,
+ .phy_reset = queue_phy_reset,
+ .phy_setup = sas_phy_setup,
+ .phy_release = sas_phy_release,
.set_phy_speed = sas_set_phy_speed,
.get_linkerrors = sas_get_linkerrors,
.smp_handler = sas_smp_handler,
};
+static inline ssize_t phy_event_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", sha->event_thres);
+}
+
+static inline ssize_t phy_event_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ sha->event_thres = simple_strtol(buf, NULL, 10);
+
+ /* threshold cannot be set too small */
+ if (sha->event_thres < 32)
+ sha->event_thres = 32;
+
+ return count;
+}
+
+DEVICE_ATTR(phy_event_threshold,
+ S_IRUGO|S_IWUSR,
+ phy_event_threshold_show,
+ phy_event_threshold_store);
+EXPORT_SYMBOL_GPL(dev_attr_phy_event_threshold);
+
struct scsi_transport_template *
sas_domain_attach_transport(struct sas_domain_function_template *dft)
{
@@ -275,35 +629,77 @@ sas_domain_attach_transport(struct sas_domain_function_template *dft)
i = to_sas_internal(stt);
i->dft = dft;
stt->create_work_queue = 1;
- stt->eh_timed_out = sas_scsi_timed_out;
stt->eh_strategy_handler = sas_scsi_recover_host;
return stt;
}
EXPORT_SYMBOL_GPL(sas_domain_attach_transport);
+struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy,
+ gfp_t gfp_flags)
+{
+ struct asd_sas_event *event;
+ struct sas_ha_struct *sas_ha = phy->ha;
+ struct sas_internal *i =
+ to_sas_internal(sas_ha->shost->transportt);
+
+ event = kmem_cache_zalloc(sas_event_cache, gfp_flags);
+ if (!event)
+ return NULL;
+
+ atomic_inc(&phy->event_nr);
+
+ if (atomic_read(&phy->event_nr) > phy->ha->event_thres) {
+ if (i->dft->lldd_control_phy) {
+ if (cmpxchg(&phy->in_shutdown, 0, 1) == 0) {
+ pr_notice("The phy%d bursting events, shut it down.\n",
+ phy->id);
+ sas_notify_phy_event(phy, PHYE_SHUTDOWN,
+ gfp_flags);
+ }
+ } else {
+ /* Do not support PHY control, stop allocating events */
+ WARN_ONCE(1, "PHY control not supported.\n");
+ kmem_cache_free(sas_event_cache, event);
+ atomic_dec(&phy->event_nr);
+ event = NULL;
+ }
+ }
-void sas_domain_release_transport(struct scsi_transport_template *stt)
+ return event;
+}
+
+void sas_free_event(struct asd_sas_event *event)
{
- sas_release_transport(stt);
+ struct asd_sas_phy *phy = event->phy;
+
+ kmem_cache_free(sas_event_cache, event);
+ atomic_dec(&phy->event_nr);
}
-EXPORT_SYMBOL_GPL(sas_domain_release_transport);
/* ---------- SAS Class register/unregister ---------- */
static int __init sas_class_init(void)
{
- sas_task_cache = kmem_cache_create("sas_task", sizeof(struct sas_task),
- 0, SLAB_HWCACHE_ALIGN, NULL);
+ sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN);
if (!sas_task_cache)
- return -ENOMEM;
+ goto out;
+
+ sas_event_cache = KMEM_CACHE(asd_sas_event, SLAB_HWCACHE_ALIGN);
+ if (!sas_event_cache)
+ goto free_task_kmem;
return 0;
+free_task_kmem:
+ kmem_cache_destroy(sas_task_cache);
+out:
+ return -ENOMEM;
}
static void __exit sas_class_exit(void)
{
kmem_cache_destroy(sas_task_cache);
+ kmem_cache_destroy(sas_event_cache);
}
MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
@@ -313,5 +709,3 @@ MODULE_LICENSE("GPL v2");
module_init(sas_class_init);
module_exit(sas_class_exit);
-EXPORT_SYMBOL_GPL(sas_register_ha);
-EXPORT_SYMBOL_GPL(sas_unregister_ha);
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 14e21b5fb8ba..6706f2be8d27 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -1,26 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Serial Attached SCSI (SAS) class internal header file
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
*/
#ifndef _SAS_INTERNAL_H_
@@ -30,96 +13,153 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_sas.h>
#include <scsi/libsas.h>
+#include <scsi/sas_ata.h>
+#include <linux/pm_runtime.h>
-#define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+
+#define SAS_FMT "sas: "
-#define SAS_DPRINTK(fmt, ...) printk(KERN_DEBUG "sas: " fmt, ## __VA_ARGS__)
+#define pr_fmt(fmt) SAS_FMT fmt
#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble)
#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0)
-void sas_scsi_recover_host(struct Scsi_Host *shost);
+struct sas_phy_data {
+ /* let reset be performed in sas_queue_work() context */
+ struct sas_phy *phy;
+ struct mutex event_lock;
+ int hard_reset;
+ int reset_result;
+ struct sas_work reset_work;
+ int enable;
+ int enable_result;
+ struct sas_work enable_work;
+};
+
+void sas_hash_addr(u8 *hashed, const u8 *sas_addr);
+
+int sas_discover_root_expander(struct domain_device *dev);
-int sas_show_class(enum sas_class class, char *buf);
-int sas_show_proto(enum sas_protocol proto, char *buf);
-int sas_show_linkrate(enum sas_linkrate linkrate, char *buf);
-int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf);
+int sas_ex_revalidate_domain(struct domain_device *dev);
+void sas_unregister_domain_devices(struct asd_sas_port *port, bool gone);
+void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port);
+void sas_discover_event(struct asd_sas_port *port, enum discover_event ev);
+
+void sas_init_dev(struct domain_device *dev);
+void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev);
+
+void sas_scsi_recover_host(struct Scsi_Host *shost);
int sas_register_phys(struct sas_ha_struct *sas_ha);
-void sas_unregister_phys(struct sas_ha_struct *sas_ha);
+
+struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, gfp_t gfp_flags);
+void sas_free_event(struct asd_sas_event *event);
+
+struct sas_task *sas_alloc_task(gfp_t flags);
+struct sas_task *sas_alloc_slow_task(gfp_t flags);
+void sas_free_task(struct sas_task *task);
int sas_register_ports(struct sas_ha_struct *sas_ha);
void sas_unregister_ports(struct sas_ha_struct *sas_ha);
-enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);
+void sas_disable_revalidation(struct sas_ha_struct *ha);
+void sas_enable_revalidation(struct sas_ha_struct *ha);
+void sas_queue_deferred_work(struct sas_ha_struct *ha);
+void __sas_drain_work(struct sas_ha_struct *ha);
-int sas_init_queue(struct sas_ha_struct *sas_ha);
-int sas_init_events(struct sas_ha_struct *sas_ha);
-void sas_shutdown_queue(struct sas_ha_struct *sas_ha);
-
-void sas_deform_port(struct asd_sas_phy *phy, int gone);
+void sas_deform_port(struct asd_sas_phy *phy, bool gone);
void sas_porte_bytes_dmaed(struct work_struct *work);
void sas_porte_broadcast_rcvd(struct work_struct *work);
void sas_porte_link_reset_err(struct work_struct *work);
void sas_porte_timer_event(struct work_struct *work);
void sas_porte_hard_reset(struct work_struct *work);
+bool sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw);
int sas_notify_lldd_dev_found(struct domain_device *);
void sas_notify_lldd_dev_gone(struct domain_device *);
+void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
+ struct sas_rphy *rphy);
int sas_smp_phy_control(struct domain_device *dev, int phy_id,
enum phy_func phy_func, struct sas_phy_linkrates *);
int sas_smp_get_phy_events(struct sas_phy *phy);
+void sas_device_set_phy(struct domain_device *dev, struct sas_port *port);
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
+struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id);
+int sas_ex_phy_discover(struct domain_device *dev, int single);
+int sas_get_report_phy_sata(struct domain_device *dev, int phy_id,
+ struct smp_rps_resp *rps_resp);
+int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id,
+ u8 *sas_addr, enum sas_device_type *type);
+int sas_try_ata_reset(struct asd_sas_phy *phy);
+
+void sas_free_device(struct kref *kref);
+void sas_destruct_devices(struct asd_sas_port *port);
+
+extern const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS];
+extern const work_func_t sas_port_event_fns[PORT_NUM_EVENTS];
-void sas_hae_reset(struct work_struct *work);
+void sas_task_internal_done(struct sas_task *task);
+void sas_task_internal_timedout(struct timer_list *t);
+int sas_execute_tmf(struct domain_device *device, void *parameter,
+ int para_len, int force_phy_id,
+ struct sas_tmf_task *tmf);
#ifdef CONFIG_SCSI_SAS_HOST_SMP
-extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
- struct request *rsp);
+extern void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost);
#else
-static inline int sas_smp_host_handler(struct Scsi_Host *shost,
- struct request *req,
- struct request *rsp)
+static inline void sas_smp_host_handler(struct bsg_job *job,
+ struct Scsi_Host *shost)
{
shost_printk(KERN_ERR, shost,
"Cannot send SMP to a sas host (not enabled in CONFIG)\n");
- return -EINVAL;
+ bsg_job_done(job, -EINVAL, 0);
}
#endif
-static inline void sas_queue_event(int event, spinlock_t *lock,
- unsigned long *pending,
- struct work_struct *work,
- struct sas_ha_struct *sas_ha)
+static inline bool sas_phy_match_dev_addr(struct domain_device *dev,
+ struct ex_phy *phy)
{
- unsigned long flags;
+ return SAS_ADDR(dev->sas_addr) == SAS_ADDR(phy->attached_sas_addr);
+}
- spin_lock_irqsave(lock, flags);
- if (test_bit(event, pending)) {
- spin_unlock_irqrestore(lock, flags);
- return;
- }
- __set_bit(event, pending);
- spin_unlock_irqrestore(lock, flags);
+static inline bool sas_phy_match_port_addr(struct asd_sas_port *port,
+ struct ex_phy *phy)
+{
+ return SAS_ADDR(port->sas_addr) == SAS_ADDR(phy->attached_sas_addr);
+}
- spin_lock_irqsave(&sas_ha->state_lock, flags);
- if (sas_ha->state != SAS_HA_UNREGISTERED) {
- scsi_queue_work(sas_ha->core.shost, work);
- }
- spin_unlock_irqrestore(&sas_ha->state_lock, flags);
+static inline bool sas_phy_addr_match(struct ex_phy *p1, struct ex_phy *p2)
+{
+ return SAS_ADDR(p1->attached_sas_addr) == SAS_ADDR(p2->attached_sas_addr);
}
-static inline void sas_begin_event(int event, spinlock_t *lock,
- unsigned long *pending)
+static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err)
{
- unsigned long flags;
+ pr_warn("%s: for %s device %016llx returned %d\n",
+ func, dev->parent ? "exp-attached" :
+ "direct-attached",
+ SAS_ADDR(dev->sas_addr), err);
+
+ /*
+ * If the device probe failed, the expander phy attached address
+ * needs to be reset so that the phy will not be treated as flutter
+ * in the next revalidation
+ */
+ if (dev->parent && !dev_is_expander(dev->dev_type)) {
+ struct sas_phy *phy = dev->phy;
+ struct domain_device *parent = dev->parent;
+ struct ex_phy *ex_phy = &parent->ex_dev.ex_phy[phy->number];
- spin_lock_irqsave(lock, flags);
- __clear_bit(event, pending);
- spin_unlock_irqrestore(lock, flags);
+ memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
+ }
+
+ sas_unregister_dev(dev->port, dev);
}
static inline void sas_fill_in_rphy(struct domain_device *dev,
@@ -129,15 +169,16 @@ static inline void sas_fill_in_rphy(struct domain_device *dev,
rphy->identify.initiator_port_protocols = dev->iproto;
rphy->identify.target_port_protocols = dev->tproto;
switch (dev->dev_type) {
- case SATA_DEV:
+ case SAS_SATA_DEV:
/* FIXME: need sata device type */
- case SAS_END_DEV:
+ case SAS_END_DEVICE:
+ case SAS_SATA_PENDING:
rphy->identify.device_type = SAS_END_DEVICE;
break;
- case EDGE_DEV:
+ case SAS_EDGE_EXPANDER_DEVICE:
rphy->identify.device_type = SAS_EDGE_EXPANDER_DEVICE;
break;
- case FANOUT_DEV:
+ case SAS_FANOUT_EXPANDER_DEVICE:
rphy->identify.device_type = SAS_FANOUT_EXPANDER_DEVICE;
break;
default:
@@ -146,19 +187,113 @@ static inline void sas_fill_in_rphy(struct domain_device *dev,
}
}
-static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)
+static inline void sas_phy_set_target(struct asd_sas_phy *p, struct domain_device *dev)
{
- struct expander_device *ex = &dev->ex_dev;
- struct ex_phy *ex_phy = &ex->ex_phy[phy_id];
+ struct sas_phy *phy = p->phy;
- if (!ex->parent_port) {
- ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id);
- /* FIXME: error handling */
- BUG_ON(!ex->parent_port);
- BUG_ON(sas_port_add(ex->parent_port));
- sas_port_mark_backlink(ex->parent_port);
+ if (dev) {
+ if (dev_is_sata(dev))
+ phy->identify.device_type = SAS_END_DEVICE;
+ else
+ phy->identify.device_type = dev->dev_type;
+ phy->identify.target_port_protocols = dev->tproto;
+ } else {
+ phy->identify.device_type = SAS_PHY_UNUSED;
+ phy->identify.target_port_protocols = 0;
}
- sas_port_add_phy(ex->parent_port, ex_phy->phy);
}
+static inline struct domain_device *sas_alloc_device(void)
+{
+ struct domain_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+
+ if (dev) {
+ INIT_LIST_HEAD(&dev->siblings);
+ INIT_LIST_HEAD(&dev->dev_list_node);
+ INIT_LIST_HEAD(&dev->disco_list_node);
+ kref_init(&dev->kref);
+ spin_lock_init(&dev->done_lock);
+ }
+ return dev;
+}
+
+static inline void sas_put_device(struct domain_device *dev)
+{
+ kref_put(&dev->kref, sas_free_device);
+}
+
+#ifdef CONFIG_SCSI_SAS_ATA
+
+int sas_ata_init(struct domain_device *dev);
+void sas_ata_task_abort(struct sas_task *task);
+int sas_discover_sata(struct domain_device *dev);
+int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy,
+ struct domain_device *child, int phy_id);
+void sas_ata_strategy_handler(struct Scsi_Host *shost);
+void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q);
+void sas_ata_end_eh(struct ata_port *ap);
+void sas_ata_wait_eh(struct domain_device *dev);
+void sas_probe_sata(struct asd_sas_port *port);
+void sas_suspend_sata(struct asd_sas_port *port);
+void sas_resume_sata(struct asd_sas_port *port);
+
+#else
+
+static inline int sas_ata_init(struct domain_device *dev)
+{
+ return 0;
+}
+
+static inline void sas_ata_task_abort(struct sas_task *task)
+{
+}
+
+static inline void sas_ata_strategy_handler(struct Scsi_Host *shost)
+{
+}
+
+static inline void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q)
+{
+}
+
+static inline void sas_ata_end_eh(struct ata_port *ap)
+{
+}
+
+static inline void sas_ata_wait_eh(struct domain_device *dev)
+{
+}
+
+static inline void sas_probe_sata(struct asd_sas_port *port)
+{
+}
+
+static inline void sas_suspend_sata(struct asd_sas_port *port)
+{
+}
+
+static inline void sas_resume_sata(struct asd_sas_port *port)
+{
+}
+
+static inline void sas_ata_disabled_notice(void)
+{
+ pr_notice_once("ATA device seen but CONFIG_SCSI_SAS_ATA=N\n");
+}
+
+static inline int sas_discover_sata(struct domain_device *dev)
+{
+ sas_ata_disabled_notice();
+ return -ENXIO;
+}
+
+static inline int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy,
+ struct domain_device *child, int phy_id)
+{
+ sas_ata_disabled_notice();
+ return -ENODEV;
+}
+
+#endif
+
#endif /* _SAS_INTERNAL_H_ */
diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c
index e0f5018e9071..635835c28ecd 100644
--- a/drivers/scsi/libsas/sas_phy.c
+++ b/drivers/scsi/libsas/sas_phy.c
@@ -1,72 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Serial Attached SCSI (SAS) Phy class
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include "sas_internal.h"
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
-#include "../scsi_sas_internal.h"
+#include "scsi_sas_internal.h"
/* ---------- Phy events ---------- */
static void sas_phye_loss_of_signal(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock,
- &phy->phy_events_pending);
phy->error = 0;
- sas_deform_port(phy, 1);
+ sas_deform_port(phy, true);
}
static void sas_phye_oob_done(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock,
- &phy->phy_events_pending);
phy->error = 0;
}
static void sas_phye_oob_error(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
struct sas_ha_struct *sas_ha = phy->ha;
struct asd_sas_port *port = phy->port;
struct sas_internal *i =
- to_sas_internal(sas_ha->core.shost->transportt);
-
- sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock,
- &phy->phy_events_pending);
+ to_sas_internal(sas_ha->shost->transportt);
- sas_deform_port(phy, 1);
+ sas_deform_port(phy, true);
if (!port && phy->enabled && i->dft->lldd_control_phy) {
phy->error++;
@@ -88,59 +62,68 @@ static void sas_phye_oob_error(struct work_struct *work)
static void sas_phye_spinup_hold(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
struct sas_ha_struct *sas_ha = phy->ha;
struct sas_internal *i =
- to_sas_internal(sas_ha->core.shost->transportt);
-
- sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock,
- &phy->phy_events_pending);
+ to_sas_internal(sas_ha->shost->transportt);
phy->error = 0;
i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
}
+static void sas_phye_resume_timeout(struct work_struct *work)
+{
+ struct asd_sas_event *ev = to_asd_sas_event(work);
+ struct asd_sas_phy *phy = ev->phy;
+
+ /* phew, lldd got the phy back in the nick of time */
+ if (!phy->suspended) {
+ dev_info(&phy->phy->dev, "resume timeout cancelled\n");
+ return;
+ }
+
+ phy->error = 0;
+ phy->suspended = 0;
+ sas_deform_port(phy, true);
+}
+
+
+static void sas_phye_shutdown(struct work_struct *work)
+{
+ struct asd_sas_event *ev = to_asd_sas_event(work);
+ struct asd_sas_phy *phy = ev->phy;
+ struct sas_ha_struct *sas_ha = phy->ha;
+ struct sas_internal *i =
+ to_sas_internal(sas_ha->shost->transportt);
+
+ if (phy->enabled) {
+ int ret;
+
+ phy->error = 0;
+ phy->enabled = 0;
+ ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
+ if (ret)
+ pr_notice("lldd disable phy%d returned %d\n", phy->id,
+ ret);
+ } else
+ pr_notice("phy%d is not enabled, cannot shutdown\n", phy->id);
+ phy->in_shutdown = 0;
+}
+
/* ---------- Phy class registration ---------- */
int sas_register_phys(struct sas_ha_struct *sas_ha)
{
int i;
- static const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
- [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
- [PHYE_OOB_DONE] = sas_phye_oob_done,
- [PHYE_OOB_ERROR] = sas_phye_oob_error,
- [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
- };
-
- static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
- [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
- [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
- [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
- [PORTE_TIMER_EVENT] = sas_porte_timer_event,
- [PORTE_HARD_RESET] = sas_porte_hard_reset,
- };
-
/* Now register the phys. */
for (i = 0; i < sas_ha->num_phys; i++) {
- int k;
struct asd_sas_phy *phy = sas_ha->sas_phy[i];
phy->error = 0;
+ atomic_set(&phy->event_nr, 0);
INIT_LIST_HEAD(&phy->port_phy_el);
- for (k = 0; k < PORT_NUM_EVENTS; k++) {
- INIT_WORK(&phy->port_events[k].work,
- sas_port_event_fns[k]);
- phy->port_events[k].phy = phy;
- }
-
- for (k = 0; k < PHY_NUM_EVENTS; k++) {
- INIT_WORK(&phy->phy_events[k].work,
- sas_phy_event_fns[k]);
- phy->phy_events[k].phy = phy;
- }
phy->port = NULL;
phy->ha = sas_ha;
@@ -148,8 +131,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
spin_lock_init(&phy->sas_prim_lock);
phy->frame_rcvd_size = 0;
- phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev,
- i);
+ phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i);
if (!phy->phy)
return -ENOMEM;
@@ -169,3 +151,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
return 0;
}
+
+const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
+ [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
+ [PHYE_OOB_DONE] = sas_phye_oob_done,
+ [PHYE_OOB_ERROR] = sas_phye_oob_error,
+ [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
+ [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout,
+ [PHYE_SHUTDOWN] = sas_phye_shutdown,
+};
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index 42fd1f25b664..de7556070048 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -1,32 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Serial Attached SCSI (SAS) Port class
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include "sas_internal.h"
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
-#include "../scsi_sas_internal.h"
+#include "scsi_sas_internal.h"
static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy)
{
@@ -39,8 +23,79 @@ static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy
return true;
}
+static void sas_resume_port(struct asd_sas_phy *phy)
+{
+ struct domain_device *dev, *n;
+ struct asd_sas_port *port = phy->port;
+ struct sas_ha_struct *sas_ha = phy->ha;
+ struct sas_internal *si = to_sas_internal(sas_ha->shost->transportt);
+
+ if (si->dft->lldd_port_formed)
+ si->dft->lldd_port_formed(phy);
+
+ if (port->suspended)
+ port->suspended = 0;
+ else {
+ /* we only need to handle "link returned" actions once */
+ return;
+ }
+
+ /* if the port came back:
+ * 1/ presume every device came back
+ * 2/ force the next revalidation to check all expander phys
+ */
+ list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) {
+ int i, rc;
+
+ rc = sas_notify_lldd_dev_found(dev);
+ if (rc) {
+ sas_unregister_dev(port, dev);
+ sas_destruct_devices(port);
+ continue;
+ }
+
+ if (dev_is_expander(dev->dev_type)) {
+ dev->ex_dev.ex_change_count = -1;
+ for (i = 0; i < dev->ex_dev.num_phys; i++) {
+ struct ex_phy *phy = &dev->ex_dev.ex_phy[i];
+
+ phy->phy_change_count = -1;
+ }
+ }
+ }
+
+ sas_discover_event(port, DISCE_RESUME);
+}
+
+static void sas_form_port_add_phy(struct asd_sas_port *port,
+ struct asd_sas_phy *phy, bool wideport)
+{
+ list_add_tail(&phy->port_phy_el, &port->phy_list);
+ sas_phy_set_target(phy, port->port_dev);
+ phy->port = port;
+ port->num_phys++;
+ port->phy_mask |= (1U << phy->id);
+
+ if (wideport)
+ pr_debug("phy%d matched wide port%d\n", phy->id,
+ port->id);
+ else
+ memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE);
+
+ if (*(u64 *)port->attached_sas_addr == 0) {
+ memcpy(port->attached_sas_addr, phy->attached_sas_addr,
+ SAS_ADDR_SIZE);
+ port->iproto = phy->iproto;
+ port->tproto = phy->tproto;
+ port->oob_mode = phy->oob_mode;
+ port->linkrate = phy->linkrate;
+ } else {
+ port->linkrate = max(port->linkrate, phy->linkrate);
+ }
+}
+
/**
- * sas_form_port -- add this phy to a port
+ * sas_form_port - add this phy to a port
* @phy: the phy of interest
*
* This function adds this phy to an existing port, thus creating a wide
@@ -51,17 +106,25 @@ static void sas_form_port(struct asd_sas_phy *phy)
int i;
struct sas_ha_struct *sas_ha = phy->ha;
struct asd_sas_port *port = phy->port;
+ struct domain_device *port_dev = NULL;
struct sas_internal *si =
- to_sas_internal(sas_ha->core.shost->transportt);
+ to_sas_internal(sas_ha->shost->transportt);
unsigned long flags;
if (port) {
if (!phy_is_wideport_member(port, phy))
- sas_deform_port(phy, 0);
- else {
- SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n",
- __func__, phy->id, phy->port->id,
- phy->port->num_phys);
+ sas_deform_port(phy, false);
+ else if (phy->suspended) {
+ phy->suspended = 0;
+ sas_resume_port(phy);
+
+ /* phy came back, try to cancel the timeout */
+ wake_up(&sas_ha->eh_wait_q);
+ return;
+ } else {
+ pr_info("%s: phy%d belongs to port%d already(%d)!\n",
+ __func__, phy->id, phy->port->id,
+ phy->port->num_phys);
return;
}
}
@@ -74,8 +137,9 @@ static void sas_form_port(struct asd_sas_phy *phy)
if (*(u64 *) port->sas_addr &&
phy_is_wideport_member(port, phy) && port->num_phys > 0) {
/* wide port */
- SAS_DPRINTK("phy%d matched wide port%d\n", phy->id,
- port->id);
+ port_dev = port->port_dev;
+ sas_form_port_add_phy(port, phy, true);
+ spin_unlock(&port->phy_list_lock);
break;
}
spin_unlock(&port->phy_list_lock);
@@ -86,42 +150,22 @@ static void sas_form_port(struct asd_sas_phy *phy)
port = sas_ha->sas_port[i];
spin_lock(&port->phy_list_lock);
if (*(u64 *)port->sas_addr == 0
- && port->num_phys == 0) {
- memcpy(port->sas_addr, phy->sas_addr,
- SAS_ADDR_SIZE);
+ && port->num_phys == 0) {
+ port_dev = port->port_dev;
+ sas_form_port_add_phy(port, phy, false);
+ spin_unlock(&port->phy_list_lock);
break;
}
spin_unlock(&port->phy_list_lock);
}
- }
- if (i >= sas_ha->num_phys) {
- printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n",
- __func__);
- spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
- return;
+ if (i >= sas_ha->num_phys) {
+ pr_err("%s: couldn't find a free port, bug?\n",
+ __func__);
+ spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
+ return;
+ }
}
-
- /* add the phy to the port */
- list_add_tail(&phy->port_phy_el, &port->phy_list);
- phy->port = port;
- port->num_phys++;
- port->phy_mask |= (1U << phy->id);
-
- if (!port->phy)
- port->phy = phy->phy;
-
- if (*(u64 *)port->attached_sas_addr == 0) {
- port->class = phy->class;
- memcpy(port->attached_sas_addr, phy->attached_sas_addr,
- SAS_ADDR_SIZE);
- port->iproto = phy->iproto;
- port->tproto = phy->tproto;
- port->oob_mode = phy->oob_mode;
- port->linkrate = phy->linkrate;
- } else
- port->linkrate = max(port->linkrate, phy->linkrate);
- spin_unlock(&port->phy_list_lock);
spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
if (!port->port) {
@@ -131,34 +175,43 @@ static void sas_form_port(struct asd_sas_phy *phy)
}
sas_port_add_phy(port->port, phy->phy);
- SAS_DPRINTK("%s added to %s, phy_mask:0x%x (%16llx)\n",
- dev_name(&phy->phy->dev), dev_name(&port->port->dev),
- port->phy_mask,
- SAS_ADDR(port->attached_sas_addr));
+ pr_debug("%s added to %s, phy_mask:0x%x (%016llx)\n",
+ dev_name(&phy->phy->dev), dev_name(&port->port->dev),
+ port->phy_mask,
+ SAS_ADDR(port->attached_sas_addr));
- if (port->port_dev)
- port->port_dev->pathways = port->num_phys;
+ if (port_dev)
+ port_dev->pathways = port->num_phys;
/* Tell the LLDD about this port formation. */
if (si->dft->lldd_port_formed)
si->dft->lldd_port_formed(phy);
sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
+ /* Only insert a revalidate event after initial discovery */
+ if (port_dev && dev_is_expander(port_dev->dev_type)) {
+ struct expander_device *ex_dev = &port_dev->ex_dev;
+
+ ex_dev->ex_change_count = -1;
+ sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);
+ }
+ flush_workqueue(sas_ha->disco_q);
}
/**
- * sas_deform_port -- remove this phy from the port it belongs to
+ * sas_deform_port - remove this phy from the port it belongs to
* @phy: the phy of interest
+ * @gone: whether or not the PHY is gone
*
* This is called when the physical link to the other phy has been
* lost (on this phy), in Event thread context. We cannot delay here.
*/
-void sas_deform_port(struct asd_sas_phy *phy, int gone)
+void sas_deform_port(struct asd_sas_phy *phy, bool gone)
{
struct sas_ha_struct *sas_ha = phy->ha;
struct asd_sas_port *port = phy->port;
struct sas_internal *si =
- to_sas_internal(sas_ha->core.shost->transportt);
+ to_sas_internal(sas_ha->shost->transportt);
struct domain_device *dev;
unsigned long flags;
@@ -170,13 +223,14 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
dev->pathways--;
if (port->num_phys == 1) {
- if (dev && gone)
- dev->gone = 1;
- sas_unregister_domain_devices(port);
+ sas_unregister_domain_devices(port, gone);
+ sas_destruct_devices(port);
sas_port_delete(port->port);
port->port = NULL;
- } else
+ } else {
sas_port_delete_phy(port->port, phy->phy);
+ sas_device_set_phy(dev, port->port);
+ }
if (si->dft->lldd_port_deformed)
si->dft->lldd_port_deformed(phy);
@@ -185,6 +239,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
spin_lock(&port->phy_list_lock);
list_del_init(&phy->port_phy_el);
+ sas_phy_set_target(phy, NULL);
phy->port = NULL;
port->num_phys--;
port->phy_mask &= ~(1U << phy->id);
@@ -193,7 +248,6 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
INIT_LIST_HEAD(&port->phy_list);
memset(port->sas_addr, 0, SAS_ADDR_SIZE);
memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);
- port->class = 0;
port->iproto = 0;
port->tproto = 0;
port->oob_mode = 0;
@@ -202,6 +256,15 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
spin_unlock(&port->phy_list_lock);
spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
+ /* Only insert revalidate event if the port still has members */
+ if (port->port && dev && dev_is_expander(dev->dev_type)) {
+ struct expander_device *ex_dev = &dev->ex_dev;
+
+ ex_dev->ex_change_count = -1;
+ sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);
+ }
+ flush_workqueue(sas_ha->disco_q);
+
return;
}
@@ -209,69 +272,52 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
void sas_porte_bytes_dmaed(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock,
- &phy->port_events_pending);
-
sas_form_port(phy);
}
void sas_porte_broadcast_rcvd(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
unsigned long flags;
u32 prim;
- sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock,
- &phy->port_events_pending);
-
spin_lock_irqsave(&phy->sas_prim_lock, flags);
prim = phy->sas_prim;
spin_unlock_irqrestore(&phy->sas_prim_lock, flags);
- SAS_DPRINTK("broadcast received: %d\n", prim);
+ pr_debug("broadcast received: %d\n", prim);
sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
+
+ if (phy->port)
+ flush_workqueue(phy->port->ha->disco_q);
}
void sas_porte_link_reset_err(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock,
- &phy->port_events_pending);
-
- sas_deform_port(phy, 1);
+ sas_deform_port(phy, true);
}
void sas_porte_timer_event(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock,
- &phy->port_events_pending);
-
- sas_deform_port(phy, 1);
+ sas_deform_port(phy, true);
}
void sas_porte_hard_reset(struct work_struct *work)
{
- struct asd_sas_event *ev =
- container_of(work, struct asd_sas_event, work);
+ struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock,
- &phy->port_events_pending);
-
- sas_deform_port(phy, 1);
+ sas_deform_port(phy, true);
}
/* ---------- SAS port registration ---------- */
@@ -282,6 +328,9 @@ static void sas_init_port(struct asd_sas_port *port,
memset(port, 0, sizeof(*port));
port->id = i;
INIT_LIST_HEAD(&port->dev_list);
+ INIT_LIST_HEAD(&port->disco_list);
+ INIT_LIST_HEAD(&port->destroy_list);
+ INIT_LIST_HEAD(&port->sas_port_del_list);
spin_lock_init(&port->phy_list_lock);
INIT_LIST_HEAD(&port->phy_list);
port->ha = sas_ha;
@@ -309,6 +358,13 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha)
for (i = 0; i < sas_ha->num_phys; i++)
if (sas_ha->sas_phy[i]->port)
- sas_deform_port(sas_ha->sas_phy[i], 0);
-
+ sas_deform_port(sas_ha->sas_phy[i], false);
}
+
+const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
+ [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
+ [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
+ [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
+ [PORTE_TIMER_EVENT] = sas_porte_timer_event,
+ [PORTE_HARD_RESET] = sas_porte_hard_reset,
+};
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index eeba76cdf774..ffa5b49aaf08 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -1,31 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Serial Attached SCSI (SAS) class SCSI Host glue.
*
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
*/
#include <linux/kthread.h>
#include <linux/firmware.h>
+#include <linux/export.h>
#include <linux/ctype.h>
+#include <linux/kernel.h>
#include "sas_internal.h"
@@ -37,9 +22,9 @@
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
#include <scsi/sas_ata.h>
-#include "../scsi_sas_internal.h"
-#include "../scsi_transport_api.h"
-#include "../scsi_priv.h"
+#include "scsi_sas_internal.h"
+#include "scsi_transport_api.h"
+#include "scsi_priv.h"
#include <linux/err.h>
#include <linux/blkdev.h>
@@ -48,26 +33,12 @@
#include <linux/scatterlist.h>
#include <linux/libata.h>
-/* ---------- SCSI Host glue ---------- */
-
-static void sas_scsi_task_done(struct sas_task *task)
+/* record final status and free the task */
+static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task)
{
struct task_status_struct *ts = &task->task_status;
- struct scsi_cmnd *sc = task->uldd_task;
- int hs = 0, stat = 0;
-
- if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- /* Aborted tasks will be completed by the error handler */
- SAS_DPRINTK("task done but aborted\n");
- return;
- }
-
- if (unlikely(!sc)) {
- SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n");
- list_del_init(&task->list);
- sas_free_task(task);
- return;
- }
+ enum scsi_host_status hs = DID_OK;
+ enum exec_status stat = SAS_SAM_STAT_GOOD;
if (ts->resp == SAS_TASK_UNDELIVERED) {
/* transport error */
@@ -96,9 +67,6 @@ static void sas_scsi_task_done(struct sas_task *task)
case SAS_DEVICE_UNKNOWN:
hs = DID_BAD_TARGET;
break;
- case SAS_SG_ERR:
- hs = DID_PARITY;
- break;
case SAS_OPEN_REJECT:
if (ts->open_rej_reason == SAS_OREJ_RSVD_RETRY)
hs = DID_SOFT_ERROR; /* retry */
@@ -106,28 +74,56 @@ static void sas_scsi_task_done(struct sas_task *task)
hs = DID_ERROR;
break;
case SAS_PROTO_RESPONSE:
- SAS_DPRINTK("LLDD:%s sent SAS_PROTO_RESP for an SSP "
- "task; please report this\n",
- task->dev->port->ha->sas_ha_name);
+ pr_notice("LLDD:%s sent SAS_PROTO_RESP for an SSP task; please report this\n",
+ task->dev->port->ha->sas_ha_name);
break;
case SAS_ABORTED_TASK:
hs = DID_ABORT;
break;
- case SAM_STAT_CHECK_CONDITION:
+ case SAS_SAM_STAT_CHECK_CONDITION:
memcpy(sc->sense_buffer, ts->buf,
min(SCSI_SENSE_BUFFERSIZE, ts->buf_valid_size));
- stat = SAM_STAT_CHECK_CONDITION;
+ stat = SAS_SAM_STAT_CHECK_CONDITION;
break;
default:
stat = ts->stat;
break;
}
}
- ASSIGN_SAS_TASK(sc, NULL);
+
sc->result = (hs << 16) | stat;
- list_del_init(&task->list);
+ ASSIGN_SAS_TASK(sc, NULL);
sas_free_task(task);
- sc->scsi_done(sc);
+}
+
+static void sas_scsi_task_done(struct sas_task *task)
+{
+ struct scsi_cmnd *sc = task->uldd_task;
+ struct domain_device *dev = task->dev;
+ struct sas_ha_struct *ha = dev->port->ha;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->done_lock, flags);
+ if (test_bit(SAS_HA_FROZEN, &ha->state))
+ task = NULL;
+ else
+ ASSIGN_SAS_TASK(sc, NULL);
+ spin_unlock_irqrestore(&dev->done_lock, flags);
+
+ if (unlikely(!task)) {
+ /* task will be completed by the error handler */
+ pr_debug("task done but aborted\n");
+ return;
+ }
+
+ if (unlikely(!sc)) {
+ pr_debug("task_done called with non existing SCSI cmnd!\n");
+ sas_free_task(task);
+ return;
+ }
+
+ sas_end_task(sc, task);
+ scsi_done(sc);
}
static struct sas_task *sas_create_task(struct scsi_cmnd *cmd,
@@ -146,11 +142,10 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd,
task->dev = dev;
task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */
- task->ssp_task.retry_count = 1;
int_to_scsilun(cmd->device->lun, &lun);
memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8);
task->ssp_task.task_attr = TASK_ATTR_SIMPLE;
- memcpy(task->ssp_task.cdb, cmd->cmnd, 16);
+ task->ssp_task.cmd = cmd;
task->scatter = scsi_sglist(cmd);
task->num_scatter = scsi_sg_count(cmd);
@@ -162,116 +157,73 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd,
return task;
}
-int sas_queue_up(struct sas_task *task)
+int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
{
- struct sas_ha_struct *sas_ha = task->dev->port->ha;
- struct scsi_core *core = &sas_ha->core;
- unsigned long flags;
- LIST_HEAD(list);
-
- spin_lock_irqsave(&core->task_queue_lock, flags);
- if (sas_ha->lldd_queue_size < core->task_queue_size + 1) {
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- return -SAS_QUEUE_FULL;
- }
- list_add_tail(&task->list, &core->task_queue);
- core->task_queue_size += 1;
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- wake_up_process(core->queue_thread);
-
- return 0;
-}
-
-/**
- * sas_queuecommand -- Enqueue a command for processing
- * @parameters: See SCSI Core documentation
- *
- * Note: XXX: Remove the host unlock/lock pair when SCSI Core can
- * call us without holding an IRQ spinlock...
- */
-static int sas_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*scsi_done)(struct scsi_cmnd *))
- __releases(host->host_lock)
- __acquires(dev->sata_dev.ap->lock)
- __releases(dev->sata_dev.ap->lock)
- __acquires(host->host_lock)
-{
- int res = 0;
- struct domain_device *dev = cmd_to_domain_dev(cmd);
- struct Scsi_Host *host = cmd->device->host;
struct sas_internal *i = to_sas_internal(host->transportt);
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_task *task;
+ int res = 0;
- spin_unlock_irq(host->host_lock);
-
- {
- struct sas_ha_struct *sas_ha = dev->port->ha;
- struct sas_task *task;
-
- /* If the device fell off, no sense in issuing commands */
- if (dev->gone) {
- cmd->result = DID_BAD_TARGET << 16;
- scsi_done(cmd);
- goto out;
- }
-
- if (dev_is_sata(dev)) {
- unsigned long flags;
+ /* If the device fell off, no sense in issuing commands */
+ if (test_bit(SAS_DEV_GONE, &dev->state)) {
+ cmd->result = DID_BAD_TARGET << 16;
+ goto out_done;
+ }
- spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
- res = ata_sas_queuecmd(cmd, dev->sata_dev.ap);
- spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags);
- goto out;
- }
+ if (dev_is_sata(dev)) {
+ spin_lock_irq(dev->sata_dev.ap->lock);
+ res = ata_sas_queuecmd(cmd, dev->sata_dev.ap);
+ spin_unlock_irq(dev->sata_dev.ap->lock);
+ return res;
+ }
- res = -ENOMEM;
- task = sas_create_task(cmd, dev, GFP_ATOMIC);
- if (!task)
- goto out;
+ task = sas_create_task(cmd, dev, GFP_ATOMIC);
+ if (!task)
+ return SCSI_MLQUEUE_HOST_BUSY;
- cmd->scsi_done = scsi_done;
- /* Queue up, Direct Mode or Task Collector Mode. */
- if (sas_ha->lldd_max_execute_num < 2)
- res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);
- else
- res = sas_queue_up(task);
+ res = i->dft->lldd_execute_task(task, GFP_ATOMIC);
+ if (res)
+ goto out_free_task;
+ return 0;
- /* Examine */
- if (res) {
- SAS_DPRINTK("lldd_execute_task returned: %d\n", res);
- ASSIGN_SAS_TASK(cmd, NULL);
- sas_free_task(task);
- if (res == -SAS_QUEUE_FULL) {
- cmd->result = DID_SOFT_ERROR << 16; /* retry */
- res = 0;
- scsi_done(cmd);
- }
- goto out;
- }
- }
-out:
- spin_lock_irq(host->host_lock);
- return res;
+out_free_task:
+ pr_debug("lldd_execute_task returned: %d\n", res);
+ ASSIGN_SAS_TASK(cmd, NULL);
+ sas_free_task(task);
+ if (res == -SAS_QUEUE_FULL)
+ cmd->result = DID_SOFT_ERROR << 16; /* retry */
+ else
+ cmd->result = DID_ERROR << 16;
+out_done:
+ scsi_done(cmd);
+ return 0;
}
-
-DEF_SCSI_QCMD(sas_queuecommand)
+EXPORT_SYMBOL_GPL(sas_queuecommand);
static void sas_eh_finish_cmd(struct scsi_cmnd *cmd)
{
- struct sas_task *task = TO_SAS_TASK(cmd);
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host);
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_task *task = TO_SAS_TASK(cmd);
+
+ /* At this point, we only get called following an actual abort
+ * of the task, so we should be guaranteed not to be racing with
+ * any completions from the LLD. Task is freed after this.
+ */
+ sas_end_task(cmd, task);
+
+ if (dev_is_sata(dev)) {
+ /* defer commands to libata so that libata EH can
+ * handle ata qcs correctly
+ */
+ list_move_tail(&cmd->eh_entry, &sas_ha->eh_ata_q);
+ return;
+ }
- /* remove the aborted task flag to allow the task to be
- * completed now. At this point, we only get called following
- * an actual abort of the task, so we should be guaranteed not
- * to be racing with any completions from the LLD (hence we
- * don't need the task state lock to clear the flag) */
- task->task_state_flags &= ~SAS_TASK_STATE_ABORTED;
- /* Now call task_done. However, task will be free'd after
- * this */
- task->task_done(task);
/* now finish the command and move it on to the error
* handler done list, this also takes it off the
- * error handler pending list */
+ * error handler pending list.
+ */
scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q);
}
@@ -323,70 +275,50 @@ enum task_disposition {
static enum task_disposition sas_scsi_find_task(struct sas_task *task)
{
- struct sas_ha_struct *ha = task->dev->port->ha;
unsigned long flags;
int i, res;
struct sas_internal *si =
- to_sas_internal(task->dev->port->ha->core.shost->transportt);
-
- if (ha->lldd_max_execute_num > 1) {
- struct scsi_core *core = &ha->core;
- struct sas_task *t, *n;
-
- spin_lock_irqsave(&core->task_queue_lock, flags);
- list_for_each_entry_safe(t, n, &core->task_queue, list) {
- if (task == t) {
- list_del_init(&t->list);
- spin_unlock_irqrestore(&core->task_queue_lock,
- flags);
- SAS_DPRINTK("%s: task 0x%p aborted from "
- "task_queue\n",
- __func__, task);
- return TASK_IS_ABORTED;
- }
- }
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- }
+ to_sas_internal(task->dev->port->ha->shost->transportt);
for (i = 0; i < 5; i++) {
- SAS_DPRINTK("%s: aborting task 0x%p\n", __func__, task);
+ pr_notice("%s: aborting task 0x%p\n", __func__, task);
res = si->dft->lldd_abort_task(task);
spin_lock_irqsave(&task->task_state_lock, flags);
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
spin_unlock_irqrestore(&task->task_state_lock, flags);
- SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
- task);
+ pr_debug("%s: task 0x%p is done\n", __func__, task);
return TASK_IS_DONE;
}
spin_unlock_irqrestore(&task->task_state_lock, flags);
if (res == TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("%s: task 0x%p is aborted\n",
- __func__, task);
+ pr_notice("%s: task 0x%p is aborted\n",
+ __func__, task);
return TASK_IS_ABORTED;
} else if (si->dft->lldd_query_task) {
- SAS_DPRINTK("%s: querying task 0x%p\n",
- __func__, task);
+ pr_notice("%s: querying task 0x%p\n", __func__, task);
res = si->dft->lldd_query_task(task);
switch (res) {
case TMF_RESP_FUNC_SUCC:
- SAS_DPRINTK("%s: task 0x%p at LU\n",
- __func__, task);
+ pr_notice("%s: task 0x%p at LU\n", __func__,
+ task);
return TASK_IS_AT_LU;
case TMF_RESP_FUNC_COMPLETE:
- SAS_DPRINTK("%s: task 0x%p not at LU\n",
- __func__, task);
+ pr_notice("%s: task 0x%p not at LU\n",
+ __func__, task);
return TASK_IS_NOT_AT_LU;
case TMF_RESP_FUNC_FAILED:
- SAS_DPRINTK("%s: task 0x%p failed to abort\n",
- __func__, task);
- return TASK_ABORT_FAILED;
- }
-
+ pr_notice("%s: task 0x%p failed to abort\n",
+ __func__, task);
+ return TASK_ABORT_FAILED;
+ default:
+ pr_notice("%s: task 0x%p result code %d not handled\n",
+ __func__, task, res);
+ }
}
}
- return res;
+ return TASK_ABORT_FAILED;
}
static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd)
@@ -394,13 +326,13 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd)
int res = TMF_RESP_FUNC_FAILED;
struct scsi_lun lun;
struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
+ to_sas_internal(dev->port->ha->shost->transportt);
int_to_scsilun(cmd->device->lun, &lun);
- SAS_DPRINTK("eh: device %llx LUN %x has the task\n",
- SAS_ADDR(dev->sas_addr),
- cmd->device->lun);
+ pr_notice("eh: device %016llx LUN 0x%llx has the task\n",
+ SAS_ADDR(dev->sas_addr),
+ cmd->device->lun);
if (i->dft->lldd_abort_task_set)
res = i->dft->lldd_abort_task_set(dev, lun.scsi_lun);
@@ -422,10 +354,10 @@ static int sas_recover_I_T(struct domain_device *dev)
{
int res = TMF_RESP_FUNC_FAILED;
struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
+ to_sas_internal(dev->port->ha->shost->transportt);
- SAS_DPRINTK("I_T nexus reset for dev %016llx\n",
- SAS_ADDR(dev->sas_addr));
+ pr_notice("I_T nexus reset for dev %016llx\n",
+ SAS_ADDR(dev->sas_addr));
if (i->dft->lldd_I_T_nexus_reset)
res = i->dft->lldd_I_T_nexus_reset(dev);
@@ -433,39 +365,105 @@ static int sas_recover_I_T(struct domain_device *dev)
return res;
}
-/* Find the sas_phy that's attached to this device */
-struct sas_phy *sas_find_local_phy(struct domain_device *dev)
+/* take a reference on the last known good phy for this device */
+struct sas_phy *sas_get_local_phy(struct domain_device *dev)
{
- struct domain_device *pdev = dev->parent;
- struct ex_phy *exphy = NULL;
- int i;
+ struct sas_ha_struct *ha = dev->port->ha;
+ struct sas_phy *phy;
+ unsigned long flags;
- /* Directly attached device */
- if (!pdev)
- return dev->port->phy;
+ /* a published domain device always has a valid phy, it may be
+ * stale, but it is never NULL
+ */
+ BUG_ON(!dev->phy);
- /* Otherwise look in the expander */
- for (i = 0; i < pdev->ex_dev.num_phys; i++)
- if (!memcmp(dev->sas_addr,
- pdev->ex_dev.ex_phy[i].attached_sas_addr,
- SAS_ADDR_SIZE)) {
- exphy = &pdev->ex_dev.ex_phy[i];
- break;
+ spin_lock_irqsave(&ha->phy_port_lock, flags);
+ phy = dev->phy;
+ get_device(&phy->dev);
+ spin_unlock_irqrestore(&ha->phy_port_lock, flags);
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(sas_get_local_phy);
+
+static int sas_queue_reset(struct domain_device *dev, int reset_type, u64 lun)
+{
+ struct sas_ha_struct *ha = dev->port->ha;
+ int scheduled = 0, tries = 100;
+
+ /* ata: promote lun reset to bus reset */
+ if (dev_is_sata(dev)) {
+ sas_ata_schedule_reset(dev);
+ return SUCCESS;
+ }
+
+ while (!scheduled && tries--) {
+ spin_lock_irq(&ha->lock);
+ if (!test_bit(SAS_DEV_EH_PENDING, &dev->state) &&
+ !test_bit(reset_type, &dev->state)) {
+ scheduled = 1;
+ ha->eh_active++;
+ list_add_tail(&dev->ssp_dev.eh_list_node, &ha->eh_dev_q);
+ set_bit(SAS_DEV_EH_PENDING, &dev->state);
+ set_bit(reset_type, &dev->state);
+ int_to_scsilun(lun, &dev->ssp_dev.reset_lun);
+ scsi_schedule_eh(ha->shost);
}
+ spin_unlock_irq(&ha->lock);
+
+ if (scheduled)
+ return SUCCESS;
+ }
+
+ pr_warn("%s reset of %s failed\n",
+ reset_type == SAS_DEV_LU_RESET ? "LUN" : "Bus",
+ dev_name(&dev->rphy->dev));
+
+ return FAILED;
+}
+
+int sas_eh_abort_handler(struct scsi_cmnd *cmd)
+{
+ int res = TMF_RESP_FUNC_FAILED;
+ struct sas_task *task = TO_SAS_TASK(cmd);
+ struct Scsi_Host *host = cmd->device->host;
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_internal *i = to_sas_internal(host->transportt);
+ unsigned long flags;
+
+ if (!i->dft->lldd_abort_task)
+ return FAILED;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ /* We cannot do async aborts for SATA devices */
+ if (dev_is_sata(dev) && !host->host_eh_scheduled) {
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return FAILED;
+ }
+ spin_unlock_irqrestore(host->host_lock, flags);
- BUG_ON(!exphy);
- return exphy->phy;
+ if (task)
+ res = i->dft->lldd_abort_task(task);
+ else
+ pr_notice("no task to abort\n");
+ if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE)
+ return SUCCESS;
+
+ return FAILED;
}
-EXPORT_SYMBOL_GPL(sas_find_local_phy);
+EXPORT_SYMBOL_GPL(sas_eh_abort_handler);
/* Attempt to send a LUN reset message to a device */
int sas_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
- struct domain_device *dev = cmd_to_domain_dev(cmd);
- struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
- struct scsi_lun lun;
int res;
+ struct scsi_lun lun;
+ struct Scsi_Host *host = cmd->device->host;
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_internal *i = to_sas_internal(host->transportt);
+
+ if (current != host->ehandler)
+ return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun);
int_to_scsilun(cmd->device->lun, &lun);
@@ -478,24 +476,29 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd)
return FAILED;
}
+EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler);
-/* Attempt to send a phy (bus) reset */
-int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd)
+int sas_eh_target_reset_handler(struct scsi_cmnd *cmd)
{
- struct domain_device *dev = cmd_to_domain_dev(cmd);
- struct sas_phy *phy = sas_find_local_phy(dev);
int res;
+ struct Scsi_Host *host = cmd->device->host;
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_internal *i = to_sas_internal(host->transportt);
- res = sas_phy_reset(phy, 1);
- if (res)
- SAS_DPRINTK("Bus reset of %s failed 0x%x\n",
- kobject_name(&phy->dev.kobj),
- res);
- if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE)
+ if (current != host->ehandler)
+ return sas_queue_reset(dev, SAS_DEV_RESET, 0);
+
+ if (!i->dft->lldd_I_T_nexus_reset)
+ return FAILED;
+
+ res = i->dft->lldd_I_T_nexus_reset(dev);
+ if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE ||
+ res == -ENODEV)
return SUCCESS;
return FAILED;
}
+EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler);
/* Try to reset a device */
static int try_to_reset_cmd_device(struct scsi_cmnd *cmd)
@@ -504,22 +507,20 @@ static int try_to_reset_cmd_device(struct scsi_cmnd *cmd)
struct Scsi_Host *shost = cmd->device->host;
if (!shost->hostt->eh_device_reset_handler)
- goto try_bus_reset;
+ goto try_target_reset;
res = shost->hostt->eh_device_reset_handler(cmd);
if (res == SUCCESS)
return res;
-try_bus_reset:
- if (shost->hostt->eh_bus_reset_handler)
- return shost->hostt->eh_bus_reset_handler(cmd);
+try_target_reset:
+ if (shost->hostt->eh_target_reset_handler)
+ return shost->hostt->eh_target_reset_handler(cmd);
return FAILED;
}
-static int sas_eh_handle_sas_errors(struct Scsi_Host *shost,
- struct list_head *work_q,
- struct list_head *done_q)
+static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head *work_q)
{
struct scsi_cmnd *cmd, *n;
enum task_disposition res = TASK_IS_DONE;
@@ -527,13 +528,28 @@ static int sas_eh_handle_sas_errors(struct Scsi_Host *shost,
struct sas_internal *i = to_sas_internal(shost->transportt);
unsigned long flags;
struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ LIST_HEAD(done);
-Again:
+ /* clean out any commands that won the completion vs eh race */
list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
- struct sas_task *task = TO_SAS_TASK(cmd);
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_task *task;
+
+ spin_lock_irqsave(&dev->done_lock, flags);
+ /* by this point the lldd has either observed
+ * SAS_HA_FROZEN and is leaving the task alone, or has
+ * won the race with eh and decided to complete it
+ */
+ task = TO_SAS_TASK(cmd);
+ spin_unlock_irqrestore(&dev->done_lock, flags);
if (!task)
- continue;
+ list_move_tail(&cmd->eh_entry, &done);
+ }
+
+ Again:
+ list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
+ struct sas_task *task = TO_SAS_TASK(cmd);
list_del_init(&cmd->eh_entry);
@@ -542,50 +558,48 @@ Again:
spin_unlock_irqrestore(&task->task_state_lock, flags);
if (need_reset) {
- SAS_DPRINTK("%s: task 0x%p requests reset\n",
- __func__, task);
+ pr_notice("%s: task 0x%p requests reset\n",
+ __func__, task);
goto reset;
}
- SAS_DPRINTK("trying to find task 0x%p\n", task);
+ pr_debug("trying to find task 0x%p\n", task);
res = sas_scsi_find_task(task);
- cmd->eh_eflags = 0;
-
switch (res) {
case TASK_IS_DONE:
- SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
+ pr_notice("%s: task 0x%p is done\n", __func__,
task);
sas_eh_finish_cmd(cmd);
continue;
case TASK_IS_ABORTED:
- SAS_DPRINTK("%s: task 0x%p is aborted\n",
- __func__, task);
+ pr_notice("%s: task 0x%p is aborted\n",
+ __func__, task);
sas_eh_finish_cmd(cmd);
continue;
case TASK_IS_AT_LU:
- SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task);
+ pr_info("task 0x%p is at LU: lu recover\n", task);
reset:
tmf_resp = sas_recover_lu(task->dev, cmd);
if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("dev %016llx LU %x is "
- "recovered\n",
- SAS_ADDR(task->dev),
- cmd->device->lun);
+ pr_notice("dev %016llx LU 0x%llx is recovered\n",
+ SAS_ADDR(task->dev),
+ cmd->device->lun);
sas_eh_finish_cmd(cmd);
sas_scsi_clear_queue_lu(work_q, cmd);
goto Again;
}
- /* fallthrough */
+ fallthrough;
case TASK_IS_NOT_AT_LU:
case TASK_ABORT_FAILED:
- SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n",
- task);
+ pr_notice("task 0x%p is not at LU: I_T recover\n",
+ task);
tmf_resp = sas_recover_I_T(task->dev);
- if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
+ if (tmf_resp == TMF_RESP_FUNC_COMPLETE ||
+ tmf_resp == -ENODEV) {
struct domain_device *dev = task->dev;
- SAS_DPRINTK("I_T %016llx recovered\n",
- SAS_ADDR(task->dev->sas_addr));
+ pr_notice("I_T %016llx recovered\n",
+ SAS_ADDR(task->dev->sas_addr));
sas_eh_finish_cmd(cmd);
sas_scsi_clear_queue_I_T(work_q, dev);
goto Again;
@@ -594,12 +608,12 @@ Again:
try_to_reset_cmd_device(cmd);
if (i->dft->lldd_clear_nexus_port) {
struct asd_sas_port *port = task->dev->port;
- SAS_DPRINTK("clearing nexus for port:%d\n",
- port->id);
+ pr_debug("clearing nexus for port:%d\n",
+ port->id);
res = i->dft->lldd_clear_nexus_port(port);
if (res == TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("clear nexus port:%d "
- "succeeded\n", port->id);
+ pr_notice("clear nexus port:%d succeeded\n",
+ port->id);
sas_eh_finish_cmd(cmd);
sas_scsi_clear_queue_port(work_q,
port);
@@ -607,11 +621,10 @@ Again:
}
}
if (i->dft->lldd_clear_nexus_ha) {
- SAS_DPRINTK("clear nexus ha\n");
+ pr_debug("clear nexus ha\n");
res = i->dft->lldd_clear_nexus_ha(ha);
if (res == TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("clear nexus ha "
- "succeeded\n");
+ pr_notice("clear nexus ha succeeded\n");
sas_eh_finish_cmd(cmd);
goto clear_q;
}
@@ -620,41 +633,85 @@ Again:
* of effort could recover from errors. Quite
* possibly the HA just disappeared.
*/
- SAS_DPRINTK("error from device %llx, LUN %x "
- "couldn't be recovered in any way\n",
- SAS_ADDR(task->dev->sas_addr),
- cmd->device->lun);
+ pr_err("error from device %016llx, LUN 0x%llx couldn't be recovered in any way\n",
+ SAS_ADDR(task->dev->sas_addr),
+ cmd->device->lun);
sas_eh_finish_cmd(cmd);
goto clear_q;
}
}
- return list_empty(work_q);
-clear_q:
- SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__);
+ out:
+ list_splice_tail(&done, work_q);
+ list_splice_tail_init(&ha->eh_ata_q, work_q);
+ return;
+
+ clear_q:
+ pr_debug("--- Exit %s -- clear_q\n", __func__);
list_for_each_entry_safe(cmd, n, work_q, eh_entry)
sas_eh_finish_cmd(cmd);
+ goto out;
+}
+
+static void sas_eh_handle_resets(struct Scsi_Host *shost)
+{
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct sas_internal *i = to_sas_internal(shost->transportt);
+
+ /* handle directed resets to sas devices */
+ spin_lock_irq(&ha->lock);
+ while (!list_empty(&ha->eh_dev_q)) {
+ struct domain_device *dev;
+ struct ssp_device *ssp;
+
+ ssp = list_entry(ha->eh_dev_q.next, typeof(*ssp), eh_list_node);
+ list_del_init(&ssp->eh_list_node);
+ dev = container_of(ssp, typeof(*dev), ssp_dev);
+ kref_get(&dev->kref);
+ WARN_ONCE(dev_is_sata(dev), "ssp reset to ata device?\n");
+
+ spin_unlock_irq(&ha->lock);
+
+ if (test_and_clear_bit(SAS_DEV_LU_RESET, &dev->state))
+ i->dft->lldd_lu_reset(dev, ssp->reset_lun.scsi_lun);
- return list_empty(work_q);
+ if (test_and_clear_bit(SAS_DEV_RESET, &dev->state))
+ i->dft->lldd_I_T_nexus_reset(dev);
+
+ sas_put_device(dev);
+ spin_lock_irq(&ha->lock);
+ clear_bit(SAS_DEV_EH_PENDING, &dev->state);
+ ha->eh_active--;
+ }
+ spin_unlock_irq(&ha->lock);
}
+
void sas_scsi_recover_host(struct Scsi_Host *shost)
{
struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
- unsigned long flags;
LIST_HEAD(eh_work_q);
+ int tries = 0;
+ bool retry;
- spin_lock_irqsave(shost->host_lock, flags);
+retry:
+ tries++;
+ retry = true;
+ spin_lock_irq(shost->host_lock);
list_splice_init(&shost->eh_cmd_q, &eh_work_q);
- shost->host_eh_scheduled = 0;
- spin_unlock_irqrestore(shost->host_lock, flags);
+ spin_unlock_irq(shost->host_lock);
- SAS_DPRINTK("Enter %s\n", __func__);
+ pr_notice("Enter %s busy: %d failed: %d\n",
+ __func__, scsi_host_busy(shost), shost->host_failed);
/*
* Deal with commands that still have SAS tasks (i.e. they didn't
- * complete via the normal sas_task completion mechanism)
+ * complete via the normal sas_task completion mechanism),
+ * SAS_HA_FROZEN gives eh dominion over all sas_task completion.
*/
- if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q))
+ set_bit(SAS_HA_FROZEN, &ha->state);
+ sas_eh_handle_sas_errors(shost, &eh_work_q);
+ clear_bit(SAS_HA_FROZEN, &ha->state);
+ if (list_empty(&eh_work_q))
goto out;
/*
@@ -663,64 +720,35 @@ void sas_scsi_recover_host(struct Scsi_Host *shost)
* scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any
* command we see here has no sas_task and is thus unknown to the HA.
*/
- if (!sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q))
- if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q))
- scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);
+ sas_ata_eh(shost, &eh_work_q);
+ if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q))
+ scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);
out:
+ sas_eh_handle_resets(shost);
+
/* now link into libata eh --- if we have any ata devices */
sas_ata_strategy_handler(shost);
scsi_eh_flush_done_q(&ha->eh_done_q);
- SAS_DPRINTK("--- Exit %s\n", __func__);
- return;
-}
-
-enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
-{
- struct sas_task *task = TO_SAS_TASK(cmd);
- unsigned long flags;
- enum blk_eh_timer_return rtn;
-
- if (sas_ata_timed_out(cmd, task, &rtn))
- return rtn;
-
- if (!task) {
- cmd->request->timeout /= 2;
- SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n",
- cmd, task, (cmd->request->timeout ?
- "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED"));
- if (!cmd->request->timeout)
- return BLK_EH_NOT_HANDLED;
- return BLK_EH_RESET_TIMER;
+ /* check if any new eh work was scheduled during the last run */
+ spin_lock_irq(&ha->lock);
+ if (ha->eh_active == 0) {
+ shost->host_eh_scheduled = 0;
+ retry = false;
}
+ spin_unlock_irq(&ha->lock);
- spin_lock_irqsave(&task->task_state_lock, flags);
- BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED);
- if (task->task_state_flags & SAS_TASK_STATE_DONE) {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- SAS_DPRINTK("command 0x%p, task 0x%p, timed out: "
- "BLK_EH_HANDLED\n", cmd, task);
- return BLK_EH_HANDLED;
- }
- if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: "
- "BLK_EH_RESET_TIMER\n",
- cmd, task);
- return BLK_EH_RESET_TIMER;
- }
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ if (retry)
+ goto retry;
- SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n",
- cmd, task);
-
- return BLK_EH_NOT_HANDLED;
+ pr_notice("--- Exit %s: busy: %d failed: %d tries: %d\n",
+ __func__, scsi_host_busy(shost),
+ shost->host_failed, tries);
}
-int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+int sas_ioctl(struct scsi_device *sdev, unsigned int cmd, void __user *arg)
{
struct domain_device *dev = sdev_to_domain_dev(sdev);
@@ -729,6 +757,7 @@ int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
return -EINVAL;
}
+EXPORT_SYMBOL_GPL(sas_ioctl);
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)
{
@@ -759,113 +788,64 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)
return found_dev;
}
-static inline struct domain_device *sas_find_target(struct scsi_target *starget)
-{
- struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent);
-
- return sas_find_dev_by_rphy(rphy);
-}
-
int sas_target_alloc(struct scsi_target *starget)
{
- struct domain_device *found_dev = sas_find_target(starget);
- int res;
+ struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent);
+ struct domain_device *found_dev = sas_find_dev_by_rphy(rphy);
if (!found_dev)
return -ENODEV;
- if (dev_is_sata(found_dev)) {
- res = sas_ata_init_host_and_port(found_dev, starget);
- if (res)
- return res;
- }
-
+ kref_get(&found_dev->kref);
starget->hostdata = found_dev;
return 0;
}
+EXPORT_SYMBOL_GPL(sas_target_alloc);
-#define SAS_DEF_QD 32
-#define SAS_MAX_QD 64
+#define SAS_DEF_QD 256
-int sas_slave_configure(struct scsi_device *scsi_dev)
+int sas_sdev_configure(struct scsi_device *scsi_dev, struct queue_limits *lim)
{
struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
- struct sas_ha_struct *sas_ha;
BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE);
if (dev_is_sata(dev)) {
- ata_sas_slave_configure(scsi_dev, dev->sata_dev.ap);
+ ata_sas_sdev_configure(scsi_dev, lim, dev->sata_dev.ap);
return 0;
}
- sas_ha = dev->port->ha;
-
sas_read_port_mode_page(scsi_dev);
if (scsi_dev->tagged_supported) {
- scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG);
- scsi_activate_tcq(scsi_dev, SAS_DEF_QD);
+ scsi_change_queue_depth(scsi_dev, SAS_DEF_QD);
} else {
- SAS_DPRINTK("device %llx, LUN %x doesn't support "
- "TCQ\n", SAS_ADDR(dev->sas_addr),
- scsi_dev->lun);
- scsi_dev->tagged_supported = 0;
- scsi_set_tag_type(scsi_dev, 0);
- scsi_deactivate_tcq(scsi_dev, 1);
+ pr_notice("device %016llx, LUN 0x%llx doesn't support TCQ\n",
+ SAS_ADDR(dev->sas_addr), scsi_dev->lun);
+ scsi_change_queue_depth(scsi_dev, 1);
}
scsi_dev->allow_restart = 1;
return 0;
}
+EXPORT_SYMBOL_GPL(sas_sdev_configure);
-void sas_slave_destroy(struct scsi_device *scsi_dev)
+int sas_change_queue_depth(struct scsi_device *sdev, int depth)
{
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
+ struct domain_device *dev = sdev_to_domain_dev(sdev);
if (dev_is_sata(dev))
- dev->sata_dev.ap->link.device[0].class = ATA_DEV_NONE;
-}
-
-int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth,
- int reason)
-{
- int res = min(new_depth, SAS_MAX_QD);
-
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (scsi_dev->tagged_supported)
- scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev),
- res);
- else {
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
- sas_printk("device %llx LUN %x queue depth changed to 1\n",
- SAS_ADDR(dev->sas_addr),
- scsi_dev->lun);
- scsi_adjust_queue_depth(scsi_dev, 0, 1);
- res = 1;
- }
-
- return res;
-}
+ return ata_change_queue_depth(dev->sata_dev.ap, sdev, depth);
-int sas_change_queue_type(struct scsi_device *scsi_dev, int qt)
-{
- if (!scsi_dev->tagged_supported)
- return 0;
-
- scsi_deactivate_tcq(scsi_dev, 1);
-
- scsi_set_tag_type(scsi_dev, qt);
- scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
-
- return qt;
+ if (!sdev->tagged_supported)
+ depth = 1;
+ return scsi_change_queue_depth(sdev, depth);
}
+EXPORT_SYMBOL_GPL(sas_change_queue_depth);
int sas_bios_param(struct scsi_device *scsi_dev,
- struct block_device *bdev,
+ struct gendisk *unused,
sector_t capacity, int *hsc)
{
hsc[0] = 255;
@@ -875,160 +855,316 @@ int sas_bios_param(struct scsi_device *scsi_dev,
return 0;
}
+EXPORT_SYMBOL_GPL(sas_bios_param);
-/* ---------- Task Collector Thread implementation ---------- */
+void sas_task_internal_done(struct sas_task *task)
+{
+ timer_delete(&task->slow_task->timer);
+ complete(&task->slow_task->completion);
+}
-static void sas_queue(struct sas_ha_struct *sas_ha)
+void sas_task_internal_timedout(struct timer_list *t)
{
- struct scsi_core *core = &sas_ha->core;
+ struct sas_task_slow *slow = timer_container_of(slow, t, timer);
+ struct sas_task *task = slow->task;
+ bool is_completed = true;
unsigned long flags;
- LIST_HEAD(q);
- int can_queue;
- int res;
- struct sas_internal *i = to_sas_internal(core->shost->transportt);
- spin_lock_irqsave(&core->task_queue_lock, flags);
- while (!kthread_should_stop() &&
- !list_empty(&core->task_queue)) {
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ is_completed = false;
+ }
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
- can_queue = sas_ha->lldd_queue_size - core->task_queue_size;
- if (can_queue >= 0) {
- can_queue = core->task_queue_size;
- list_splice_init(&core->task_queue, &q);
- } else {
- struct list_head *a, *n;
+ if (!is_completed)
+ complete(&task->slow_task->completion);
+}
- can_queue = sas_ha->lldd_queue_size;
- list_for_each_safe(a, n, &core->task_queue) {
- list_move_tail(a, &q);
- if (--can_queue == 0)
- break;
- }
- can_queue = sas_ha->lldd_queue_size;
+#define TASK_TIMEOUT (20 * HZ)
+#define TASK_RETRY 3
+
+static int sas_execute_internal_abort(struct domain_device *device,
+ enum sas_internal_abort type, u16 tag,
+ unsigned int qid, void *data)
+{
+ struct sas_ha_struct *ha = device->port->ha;
+ struct sas_internal *i = to_sas_internal(ha->shost->transportt);
+ struct sas_task *task = NULL;
+ int res, retry;
+
+ for (retry = 0; retry < TASK_RETRY; retry++) {
+ task = sas_alloc_slow_task(GFP_KERNEL);
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = device;
+ task->task_proto = SAS_PROTOCOL_INTERNAL_ABORT;
+ task->task_done = sas_task_internal_done;
+ task->slow_task->timer.function = sas_task_internal_timedout;
+ task->slow_task->timer.expires = jiffies + TASK_TIMEOUT;
+ add_timer(&task->slow_task->timer);
+
+ task->abort_task.tag = tag;
+ task->abort_task.type = type;
+ task->abort_task.qid = qid;
+
+ res = i->dft->lldd_execute_task(task, GFP_KERNEL);
+ if (res) {
+ timer_delete_sync(&task->slow_task->timer);
+ pr_err("Executing internal abort failed %016llx (%d)\n",
+ SAS_ADDR(device->sas_addr), res);
+ break;
}
- core->task_queue_size -= can_queue;
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- {
- struct sas_task *task = list_entry(q.next,
- struct sas_task,
- list);
- list_del_init(&q);
- res = i->dft->lldd_execute_task(task, can_queue,
- GFP_KERNEL);
- if (unlikely(res))
- __list_add(&q, task->list.prev, &task->list);
+
+ wait_for_completion(&task->slow_task->completion);
+ res = TMF_RESP_FUNC_FAILED;
+
+ /* Even if the internal abort timed out, return direct. */
+ if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+ bool quit = true;
+
+ if (i->dft->lldd_abort_timeout)
+ quit = i->dft->lldd_abort_timeout(task, data);
+ else
+ pr_err("Internal abort: timeout %016llx\n",
+ SAS_ADDR(device->sas_addr));
+ res = -EIO;
+ if (quit)
+ break;
}
- spin_lock_irqsave(&core->task_queue_lock, flags);
- if (res) {
- list_splice_init(&q, &core->task_queue); /*at head*/
- core->task_queue_size += can_queue;
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_SAM_STAT_GOOD) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == TMF_RESP_FUNC_SUCC) {
+ res = TMF_RESP_FUNC_SUCC;
+ break;
}
+
+ pr_err("Internal abort: task to dev %016llx response: 0x%x status 0x%x\n",
+ SAS_ADDR(device->sas_addr), task->task_status.resp,
+ task->task_status.stat);
+ sas_free_task(task);
+ task = NULL;
}
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
+ BUG_ON(retry == TASK_RETRY && task != NULL);
+ sas_free_task(task);
+ return res;
}
-/**
- * sas_queue_thread -- The Task Collector thread
- * @_sas_ha: pointer to struct sas_ha
- */
-static int sas_queue_thread(void *_sas_ha)
+int sas_execute_internal_abort_single(struct domain_device *device, u16 tag,
+ unsigned int qid, void *data)
+{
+ return sas_execute_internal_abort(device, SAS_INTERNAL_ABORT_SINGLE,
+ tag, qid, data);
+}
+EXPORT_SYMBOL_GPL(sas_execute_internal_abort_single);
+
+int sas_execute_internal_abort_dev(struct domain_device *device,
+ unsigned int qid, void *data)
+{
+ return sas_execute_internal_abort(device, SAS_INTERNAL_ABORT_DEV,
+ SCSI_NO_TAG, qid, data);
+}
+EXPORT_SYMBOL_GPL(sas_execute_internal_abort_dev);
+
+int sas_execute_tmf(struct domain_device *device, void *parameter,
+ int para_len, int force_phy_id,
+ struct sas_tmf_task *tmf)
{
- struct sas_ha_struct *sas_ha = _sas_ha;
+ struct sas_task *task;
+ struct sas_internal *i =
+ to_sas_internal(device->port->ha->shost->transportt);
+ int res, retry;
+
+ for (retry = 0; retry < TASK_RETRY; retry++) {
+ task = sas_alloc_slow_task(GFP_KERNEL);
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = device;
+ task->task_proto = device->tproto;
+
+ if (dev_is_sata(device)) {
+ task->ata_task.device_control_reg_update = 1;
+ if (force_phy_id >= 0) {
+ task->ata_task.force_phy = true;
+ task->ata_task.force_phy_id = force_phy_id;
+ }
+ memcpy(&task->ata_task.fis, parameter, para_len);
+ } else {
+ memcpy(&task->ssp_task, parameter, para_len);
+ }
+
+ task->task_done = sas_task_internal_done;
+ task->tmf = tmf;
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- sas_queue(sas_ha);
- if (kthread_should_stop())
+ task->slow_task->timer.function = sas_task_internal_timedout;
+ task->slow_task->timer.expires = jiffies + TASK_TIMEOUT;
+ add_timer(&task->slow_task->timer);
+
+ res = i->dft->lldd_execute_task(task, GFP_KERNEL);
+ if (res) {
+ timer_delete_sync(&task->slow_task->timer);
+ pr_err("executing TMF task failed %016llx (%d)\n",
+ SAS_ADDR(device->sas_addr), res);
+ break;
+ }
+
+ wait_for_completion(&task->slow_task->completion);
+
+ if (i->dft->lldd_tmf_exec_complete)
+ i->dft->lldd_tmf_exec_complete(device);
+
+ res = TMF_RESP_FUNC_FAILED;
+
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ pr_err("TMF task timeout for %016llx and not done\n",
+ SAS_ADDR(device->sas_addr));
+ if (i->dft->lldd_tmf_aborted)
+ i->dft->lldd_tmf_aborted(task);
+ break;
+ }
+ pr_warn("TMF task timeout for %016llx and done\n",
+ SAS_ADDR(device->sas_addr));
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == TMF_RESP_FUNC_COMPLETE) {
+ res = TMF_RESP_FUNC_COMPLETE;
break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == TMF_RESP_FUNC_SUCC) {
+ res = TMF_RESP_FUNC_SUCC;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_UNDERRUN) {
+ /* no error, but return the number of bytes of
+ * underrun
+ */
+ pr_warn("TMF task to dev %016llx resp: 0x%x sts 0x%x underrun\n",
+ SAS_ADDR(device->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat);
+ res = task->task_status.residual;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_OVERRUN) {
+ pr_warn("TMF task blocked task error %016llx\n",
+ SAS_ADDR(device->sas_addr));
+ res = -EMSGSIZE;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_OPEN_REJECT) {
+ pr_warn("TMF task open reject failed %016llx\n",
+ SAS_ADDR(device->sas_addr));
+ res = -EIO;
+ } else {
+ pr_warn("TMF task to dev %016llx resp: 0x%x status 0x%x\n",
+ SAS_ADDR(device->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat);
+ }
+ sas_free_task(task);
+ task = NULL;
}
- return 0;
+ if (retry == TASK_RETRY)
+ pr_warn("executing TMF for %016llx failed after %d attempts!\n",
+ SAS_ADDR(device->sas_addr), TASK_RETRY);
+ sas_free_task(task);
+
+ return res;
}
-int sas_init_queue(struct sas_ha_struct *sas_ha)
+static int sas_execute_ssp_tmf(struct domain_device *device, u8 *lun,
+ struct sas_tmf_task *tmf)
{
- struct scsi_core *core = &sas_ha->core;
+ struct sas_ssp_task ssp_task;
- spin_lock_init(&core->task_queue_lock);
- core->task_queue_size = 0;
- INIT_LIST_HEAD(&core->task_queue);
+ if (!(device->tproto & SAS_PROTOCOL_SSP))
+ return TMF_RESP_FUNC_ESUPP;
- core->queue_thread = kthread_run(sas_queue_thread, sas_ha,
- "sas_queue_%d", core->shost->host_no);
- if (IS_ERR(core->queue_thread))
- return PTR_ERR(core->queue_thread);
- return 0;
+ memcpy(ssp_task.LUN, lun, 8);
+
+ return sas_execute_tmf(device, &ssp_task, sizeof(ssp_task), -1, tmf);
}
-void sas_shutdown_queue(struct sas_ha_struct *sas_ha)
+int sas_abort_task_set(struct domain_device *dev, u8 *lun)
{
- unsigned long flags;
- struct scsi_core *core = &sas_ha->core;
- struct sas_task *task, *n;
+ struct sas_tmf_task tmf_task = {
+ .tmf = TMF_ABORT_TASK_SET,
+ };
- kthread_stop(core->queue_thread);
+ return sas_execute_ssp_tmf(dev, lun, &tmf_task);
+}
+EXPORT_SYMBOL_GPL(sas_abort_task_set);
- if (!list_empty(&core->task_queue))
- SAS_DPRINTK("HA: %llx: scsi core task queue is NOT empty!?\n",
- SAS_ADDR(sas_ha->sas_addr));
+int sas_clear_task_set(struct domain_device *dev, u8 *lun)
+{
+ struct sas_tmf_task tmf_task = {
+ .tmf = TMF_CLEAR_TASK_SET,
+ };
- spin_lock_irqsave(&core->task_queue_lock, flags);
- list_for_each_entry_safe(task, n, &core->task_queue, list) {
- struct scsi_cmnd *cmd = task->uldd_task;
+ return sas_execute_ssp_tmf(dev, lun, &tmf_task);
+}
+EXPORT_SYMBOL_GPL(sas_clear_task_set);
- list_del_init(&task->list);
+int sas_lu_reset(struct domain_device *dev, u8 *lun)
+{
+ struct sas_tmf_task tmf_task = {
+ .tmf = TMF_LU_RESET,
+ };
- ASSIGN_SAS_TASK(cmd, NULL);
- sas_free_task(task);
- cmd->result = DID_ABORT << 16;
- cmd->scsi_done(cmd);
- }
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
+ return sas_execute_ssp_tmf(dev, lun, &tmf_task);
}
+EXPORT_SYMBOL_GPL(sas_lu_reset);
-/*
- * Call the LLDD task abort routine directly. This function is intended for
- * use by upper layers that need to tell the LLDD to abort a task.
- */
-int __sas_task_abort(struct sas_task *task)
+int sas_query_task(struct sas_task *task, u16 tag)
{
- struct sas_internal *si =
- to_sas_internal(task->dev->port->ha->core.shost->transportt);
- unsigned long flags;
- int res;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (task->task_state_flags & SAS_TASK_STATE_ABORTED ||
- task->task_state_flags & SAS_TASK_STATE_DONE) {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- SAS_DPRINTK("%s: Task %p already finished.\n", __func__,
- task);
- return 0;
- }
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ struct sas_tmf_task tmf_task = {
+ .tmf = TMF_QUERY_TASK,
+ .tag_of_task_to_be_managed = tag,
+ };
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ struct domain_device *dev = task->dev;
+ struct scsi_lun lun;
- if (!si->dft->lldd_abort_task)
- return -ENODEV;
+ int_to_scsilun(cmnd->device->lun, &lun);
- res = si->dft->lldd_abort_task(task);
+ return sas_execute_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+}
+EXPORT_SYMBOL_GPL(sas_query_task);
- spin_lock_irqsave(&task->task_state_lock, flags);
- if ((task->task_state_flags & SAS_TASK_STATE_DONE) ||
- (res == TMF_RESP_FUNC_COMPLETE))
- {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- task->task_done(task);
- return 0;
- }
+int sas_abort_task(struct sas_task *task, u16 tag)
+{
+ struct sas_tmf_task tmf_task = {
+ .tmf = TMF_ABORT_TASK,
+ .tag_of_task_to_be_managed = tag,
+ };
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ struct domain_device *dev = task->dev;
+ struct scsi_lun lun;
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
- task->task_state_flags &= ~SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ int_to_scsilun(cmnd->device->lun, &lun);
- return -EAGAIN;
+ return sas_execute_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
}
+EXPORT_SYMBOL_GPL(sas_abort_task);
/*
* Tell an upper layer that it needs to initiate an abort for a given task.
@@ -1040,62 +1176,43 @@ void sas_task_abort(struct sas_task *task)
/* Escape for libsas internal commands */
if (!sc) {
- if (!del_timer(&task->timer))
+ struct sas_task_slow *slow = task->slow_task;
+
+ if (!slow)
return;
- task->timer.function(task->timer.data);
+ if (!timer_delete(&slow->timer))
+ return;
+ slow->timer.function(&slow->timer);
return;
}
- if (dev_is_sata(task->dev)) {
+ if (dev_is_sata(task->dev))
sas_ata_task_abort(task);
- } else {
- struct request_queue *q = sc->device->request_queue;
- unsigned long flags;
-
- spin_lock_irqsave(q->queue_lock, flags);
- blk_abort_request(sc->request);
- spin_unlock_irqrestore(q->queue_lock, flags);
- scsi_schedule_eh(sc->device->host);
- }
+ else
+ blk_abort_request(scsi_cmd_to_rq(sc));
}
+EXPORT_SYMBOL_GPL(sas_task_abort);
-int sas_slave_alloc(struct scsi_device *scsi_dev)
+int sas_sdev_init(struct scsi_device *sdev)
{
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
-
- if (dev_is_sata(dev))
- return ata_sas_port_init(dev->sata_dev.ap);
+ if (dev_is_sata(sdev_to_domain_dev(sdev)) && sdev->lun)
+ return -ENXIO;
return 0;
}
+EXPORT_SYMBOL_GPL(sas_sdev_init);
void sas_target_destroy(struct scsi_target *starget)
{
- struct domain_device *found_dev = sas_find_target(starget);
+ struct domain_device *found_dev = starget->hostdata;
if (!found_dev)
return;
- if (dev_is_sata(found_dev))
- ata_sas_port_destroy(found_dev->sata_dev.ap);
-
- return;
-}
-
-static void sas_parse_addr(u8 *sas_addr, const char *p)
-{
- int i;
- for (i = 0; i < SAS_ADDR_SIZE; i++) {
- u8 h, l;
- if (!*p)
- break;
- h = isdigit(*p) ? *p-'0' : toupper(*p)-'A'+10;
- p++;
- l = isdigit(*p) ? *p-'0' : toupper(*p)-'A'+10;
- p++;
- sas_addr[i] = (h<<4) | l;
- }
+ starget->hostdata = NULL;
+ sas_put_device(found_dev);
}
+EXPORT_SYMBOL_GPL(sas_target_destroy);
#define SAS_STRING_ADDR_SIZE 16
@@ -1113,7 +1230,9 @@ int sas_request_addr(struct Scsi_Host *shost, u8 *addr)
goto out;
}
- sas_parse_addr(addr, fw->data);
+ res = hex2bin(addr, fw->data, strnlen(fw->data, SAS_ADDR_SIZE * 2) / 2);
+ if (res)
+ goto out;
out:
release_firmware(fw);
@@ -1121,19 +1240,3 @@ out:
}
EXPORT_SYMBOL_GPL(sas_request_addr);
-EXPORT_SYMBOL_GPL(sas_queuecommand);
-EXPORT_SYMBOL_GPL(sas_target_alloc);
-EXPORT_SYMBOL_GPL(sas_slave_configure);
-EXPORT_SYMBOL_GPL(sas_slave_destroy);
-EXPORT_SYMBOL_GPL(sas_change_queue_depth);
-EXPORT_SYMBOL_GPL(sas_change_queue_type);
-EXPORT_SYMBOL_GPL(sas_bios_param);
-EXPORT_SYMBOL_GPL(__sas_task_abort);
-EXPORT_SYMBOL_GPL(sas_task_abort);
-EXPORT_SYMBOL_GPL(sas_phy_reset);
-EXPORT_SYMBOL_GPL(sas_phy_enable);
-EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler);
-EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler);
-EXPORT_SYMBOL_GPL(sas_slave_alloc);
-EXPORT_SYMBOL_GPL(sas_target_destroy);
-EXPORT_SYMBOL_GPL(sas_ioctl);
diff --git a/drivers/scsi/libsas/sas_task.c b/drivers/scsi/libsas/sas_task.c
index b13a3346894c..e9d291007817 100644
--- a/drivers/scsi/libsas/sas_task.c
+++ b/drivers/scsi/libsas/sas_task.c
@@ -1,4 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "sas_internal.h"
+
#include <linux/kernel.h>
+#include <linux/export.h>
#include <scsi/sas.h>
#include <scsi/libsas.h>
@@ -10,27 +15,28 @@ void sas_ssp_task_response(struct device *dev, struct sas_task *task,
tstat->resp = SAS_TASK_COMPLETE;
- if (iu->datapres == 0)
+ switch (iu->datapres) {
+ case SAS_DATAPRES_NO_DATA:
tstat->stat = iu->status;
- else if (iu->datapres == 1)
+ break;
+ case SAS_DATAPRES_RESPONSE_DATA:
tstat->stat = iu->resp_data[3];
- else if (iu->datapres == 2) {
- tstat->stat = SAM_STAT_CHECK_CONDITION;
+ break;
+ case SAS_DATAPRES_SENSE_DATA:
+ tstat->stat = SAS_SAM_STAT_CHECK_CONDITION;
tstat->buf_valid_size =
min_t(int, SAS_STATUS_BUF_SIZE,
be32_to_cpu(iu->sense_data_len));
memcpy(tstat->buf, iu->sense_data, tstat->buf_valid_size);
if (iu->status != SAM_STAT_CHECK_CONDITION)
- dev_printk(KERN_WARNING, dev,
- "dev %llx sent sense data, but "
- "stat(%x) is not CHECK CONDITION\n",
- SAS_ADDR(task->dev->sas_addr),
- iu->status);
- }
- else
+ dev_warn(dev, "dev %016llx sent sense data, but stat(0x%x) is not CHECK CONDITION\n",
+ SAS_ADDR(task->dev->sas_addr), iu->status);
+ break;
+ default:
/* when datapres contains corrupt/unknown value... */
- tstat->stat = SAM_STAT_CHECK_CONDITION;
+ tstat->stat = SAS_SAM_STAT_CHECK_CONDITION;
+ }
}
EXPORT_SYMBOL_GPL(sas_ssp_task_response);
diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
deleted file mode 100644
index ff6a28ce9b69..000000000000
--- a/drivers/scsi/libsrp.c
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * SCSI RDMA Protocol lib functions
- *
- * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/kfifo.h>
-#include <linux/scatterlist.h>
-#include <linux/dma-mapping.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsi_tgt.h>
-#include <scsi/srp.h>
-#include <scsi/libsrp.h>
-
-enum srp_task_attributes {
- SRP_SIMPLE_TASK = 0,
- SRP_HEAD_TASK = 1,
- SRP_ORDERED_TASK = 2,
- SRP_ACA_TASK = 4
-};
-
-/* tmp - will replace with SCSI logging stuff */
-#define eprintk(fmt, args...) \
-do { \
- printk("%s(%d) " fmt, __func__, __LINE__, ##args); \
-} while (0)
-/* #define dprintk eprintk */
-#define dprintk(fmt, args...)
-
-static int srp_iu_pool_alloc(struct srp_queue *q, size_t max,
- struct srp_buf **ring)
-{
- int i;
- struct iu_entry *iue;
-
- q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL);
- if (!q->pool)
- return -ENOMEM;
- q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL);
- if (!q->items)
- goto free_pool;
-
- spin_lock_init(&q->lock);
- kfifo_init(&q->queue, (void *) q->pool, max * sizeof(void *));
-
- for (i = 0, iue = q->items; i < max; i++) {
- kfifo_in(&q->queue, (void *) &iue, sizeof(void *));
- iue->sbuf = ring[i];
- iue++;
- }
- return 0;
-
- kfree(q->items);
-free_pool:
- kfree(q->pool);
- return -ENOMEM;
-}
-
-static void srp_iu_pool_free(struct srp_queue *q)
-{
- kfree(q->items);
- kfree(q->pool);
-}
-
-static struct srp_buf **srp_ring_alloc(struct device *dev,
- size_t max, size_t size)
-{
- int i;
- struct srp_buf **ring;
-
- ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL);
- if (!ring)
- return NULL;
-
- for (i = 0; i < max; i++) {
- ring[i] = kzalloc(sizeof(struct srp_buf), GFP_KERNEL);
- if (!ring[i])
- goto out;
- ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma,
- GFP_KERNEL);
- if (!ring[i]->buf)
- goto out;
- }
- return ring;
-
-out:
- for (i = 0; i < max && ring[i]; i++) {
- if (ring[i]->buf)
- dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma);
- kfree(ring[i]);
- }
- kfree(ring);
-
- return NULL;
-}
-
-static void srp_ring_free(struct device *dev, struct srp_buf **ring, size_t max,
- size_t size)
-{
- int i;
-
- for (i = 0; i < max; i++) {
- dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma);
- kfree(ring[i]);
- }
- kfree(ring);
-}
-
-int srp_target_alloc(struct srp_target *target, struct device *dev,
- size_t nr, size_t iu_size)
-{
- int err;
-
- spin_lock_init(&target->lock);
- INIT_LIST_HEAD(&target->cmd_queue);
-
- target->dev = dev;
- dev_set_drvdata(target->dev, target);
-
- target->srp_iu_size = iu_size;
- target->rx_ring_size = nr;
- target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size);
- if (!target->rx_ring)
- return -ENOMEM;
- err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring);
- if (err)
- goto free_ring;
-
- return 0;
-
-free_ring:
- srp_ring_free(target->dev, target->rx_ring, nr, iu_size);
- return -ENOMEM;
-}
-EXPORT_SYMBOL_GPL(srp_target_alloc);
-
-void srp_target_free(struct srp_target *target)
-{
- srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size,
- target->srp_iu_size);
- srp_iu_pool_free(&target->iu_queue);
-}
-EXPORT_SYMBOL_GPL(srp_target_free);
-
-struct iu_entry *srp_iu_get(struct srp_target *target)
-{
- struct iu_entry *iue = NULL;
-
- if (kfifo_out_locked(&target->iu_queue.queue, (void *) &iue,
- sizeof(void *), &target->iu_queue.lock) != sizeof(void *)) {
- WARN_ONCE(1, "unexpected fifo state");
- return NULL;
- }
- if (!iue)
- return iue;
- iue->target = target;
- INIT_LIST_HEAD(&iue->ilist);
- iue->flags = 0;
- return iue;
-}
-EXPORT_SYMBOL_GPL(srp_iu_get);
-
-void srp_iu_put(struct iu_entry *iue)
-{
- kfifo_in_locked(&iue->target->iu_queue.queue, (void *) &iue,
- sizeof(void *), &iue->target->iu_queue.lock);
-}
-EXPORT_SYMBOL_GPL(srp_iu_put);
-
-static int srp_direct_data(struct scsi_cmnd *sc, struct srp_direct_buf *md,
- enum dma_data_direction dir, srp_rdma_t rdma_io,
- int dma_map, int ext_desc)
-{
- struct iu_entry *iue = NULL;
- struct scatterlist *sg = NULL;
- int err, nsg = 0, len;
-
- if (dma_map) {
- iue = (struct iu_entry *) sc->SCp.ptr;
- sg = scsi_sglist(sc);
-
- dprintk("%p %u %u %d\n", iue, scsi_bufflen(sc),
- md->len, scsi_sg_count(sc));
-
- nsg = dma_map_sg(iue->target->dev, sg, scsi_sg_count(sc),
- DMA_BIDIRECTIONAL);
- if (!nsg) {
- printk("fail to map %p %d\n", iue, scsi_sg_count(sc));
- return 0;
- }
- len = min(scsi_bufflen(sc), md->len);
- } else
- len = md->len;
-
- err = rdma_io(sc, sg, nsg, md, 1, dir, len);
-
- if (dma_map)
- dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL);
-
- return err;
-}
-
-static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd,
- struct srp_indirect_buf *id,
- enum dma_data_direction dir, srp_rdma_t rdma_io,
- int dma_map, int ext_desc)
-{
- struct iu_entry *iue = NULL;
- struct srp_direct_buf *md = NULL;
- struct scatterlist dummy, *sg = NULL;
- dma_addr_t token = 0;
- int err = 0;
- int nmd, nsg = 0, len;
-
- if (dma_map || ext_desc) {
- iue = (struct iu_entry *) sc->SCp.ptr;
- sg = scsi_sglist(sc);
-
- dprintk("%p %u %u %d %d\n",
- iue, scsi_bufflen(sc), id->len,
- cmd->data_in_desc_cnt, cmd->data_out_desc_cnt);
- }
-
- nmd = id->table_desc.len / sizeof(struct srp_direct_buf);
-
- if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) ||
- (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) {
- md = &id->desc_list[0];
- goto rdma;
- }
-
- if (ext_desc && dma_map) {
- md = dma_alloc_coherent(iue->target->dev, id->table_desc.len,
- &token, GFP_KERNEL);
- if (!md) {
- eprintk("Can't get dma memory %u\n", id->table_desc.len);
- return -ENOMEM;
- }
-
- sg_init_one(&dummy, md, id->table_desc.len);
- sg_dma_address(&dummy) = token;
- sg_dma_len(&dummy) = id->table_desc.len;
- err = rdma_io(sc, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE,
- id->table_desc.len);
- if (err) {
- eprintk("Error copying indirect table %d\n", err);
- goto free_mem;
- }
- } else {
- eprintk("This command uses external indirect buffer\n");
- return -EINVAL;
- }
-
-rdma:
- if (dma_map) {
- nsg = dma_map_sg(iue->target->dev, sg, scsi_sg_count(sc),
- DMA_BIDIRECTIONAL);
- if (!nsg) {
- eprintk("fail to map %p %d\n", iue, scsi_sg_count(sc));
- err = -EIO;
- goto free_mem;
- }
- len = min(scsi_bufflen(sc), id->len);
- } else
- len = id->len;
-
- err = rdma_io(sc, sg, nsg, md, nmd, dir, len);
-
- if (dma_map)
- dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL);
-
-free_mem:
- if (token && dma_map)
- dma_free_coherent(iue->target->dev, id->table_desc.len, md, token);
-
- return err;
-}
-
-static int data_out_desc_size(struct srp_cmd *cmd)
-{
- int size = 0;
- u8 fmt = cmd->buf_fmt >> 4;
-
- switch (fmt) {
- case SRP_NO_DATA_DESC:
- break;
- case SRP_DATA_DESC_DIRECT:
- size = sizeof(struct srp_direct_buf);
- break;
- case SRP_DATA_DESC_INDIRECT:
- size = sizeof(struct srp_indirect_buf) +
- sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt;
- break;
- default:
- eprintk("client error. Invalid data_out_format %x\n", fmt);
- break;
- }
- return size;
-}
-
-/*
- * TODO: this can be called multiple times for a single command if it
- * has very long data.
- */
-int srp_transfer_data(struct scsi_cmnd *sc, struct srp_cmd *cmd,
- srp_rdma_t rdma_io, int dma_map, int ext_desc)
-{
- struct srp_direct_buf *md;
- struct srp_indirect_buf *id;
- enum dma_data_direction dir;
- int offset, err = 0;
- u8 format;
-
- offset = cmd->add_cdb_len & ~3;
-
- dir = srp_cmd_direction(cmd);
- if (dir == DMA_FROM_DEVICE)
- offset += data_out_desc_size(cmd);
-
- if (dir == DMA_TO_DEVICE)
- format = cmd->buf_fmt >> 4;
- else
- format = cmd->buf_fmt & ((1U << 4) - 1);
-
- switch (format) {
- case SRP_NO_DATA_DESC:
- break;
- case SRP_DATA_DESC_DIRECT:
- md = (struct srp_direct_buf *)
- (cmd->add_data + offset);
- err = srp_direct_data(sc, md, dir, rdma_io, dma_map, ext_desc);
- break;
- case SRP_DATA_DESC_INDIRECT:
- id = (struct srp_indirect_buf *)
- (cmd->add_data + offset);
- err = srp_indirect_data(sc, cmd, id, dir, rdma_io, dma_map,
- ext_desc);
- break;
- default:
- eprintk("Unknown format %d %x\n", dir, format);
- err = -EINVAL;
- }
-
- return err;
-}
-EXPORT_SYMBOL_GPL(srp_transfer_data);
-
-static int vscsis_data_length(struct srp_cmd *cmd, enum dma_data_direction dir)
-{
- struct srp_direct_buf *md;
- struct srp_indirect_buf *id;
- int len = 0, offset = cmd->add_cdb_len & ~3;
- u8 fmt;
-
- if (dir == DMA_TO_DEVICE)
- fmt = cmd->buf_fmt >> 4;
- else {
- fmt = cmd->buf_fmt & ((1U << 4) - 1);
- offset += data_out_desc_size(cmd);
- }
-
- switch (fmt) {
- case SRP_NO_DATA_DESC:
- break;
- case SRP_DATA_DESC_DIRECT:
- md = (struct srp_direct_buf *) (cmd->add_data + offset);
- len = md->len;
- break;
- case SRP_DATA_DESC_INDIRECT:
- id = (struct srp_indirect_buf *) (cmd->add_data + offset);
- len = id->len;
- break;
- default:
- eprintk("invalid data format %x\n", fmt);
- break;
- }
- return len;
-}
-
-int srp_cmd_queue(struct Scsi_Host *shost, struct srp_cmd *cmd, void *info,
- u64 itn_id, u64 addr)
-{
- enum dma_data_direction dir;
- struct scsi_cmnd *sc;
- int tag, len, err;
-
- switch (cmd->task_attr) {
- case SRP_SIMPLE_TASK:
- tag = MSG_SIMPLE_TAG;
- break;
- case SRP_ORDERED_TASK:
- tag = MSG_ORDERED_TAG;
- break;
- case SRP_HEAD_TASK:
- tag = MSG_HEAD_TAG;
- break;
- default:
- eprintk("Task attribute %d not supported\n", cmd->task_attr);
- tag = MSG_ORDERED_TAG;
- }
-
- dir = srp_cmd_direction(cmd);
- len = vscsis_data_length(cmd, dir);
-
- dprintk("%p %x %lx %d %d %d %llx\n", info, cmd->cdb[0],
- cmd->lun, dir, len, tag, (unsigned long long) cmd->tag);
-
- sc = scsi_host_get_command(shost, dir, GFP_KERNEL);
- if (!sc)
- return -ENOMEM;
-
- sc->SCp.ptr = info;
- memcpy(sc->cmnd, cmd->cdb, MAX_COMMAND_SIZE);
- sc->sdb.length = len;
- sc->sdb.table.sgl = (void *) (unsigned long) addr;
- sc->tag = tag;
- err = scsi_tgt_queue_command(sc, itn_id, (struct scsi_lun *)&cmd->lun,
- cmd->tag);
- if (err)
- scsi_host_put_command(shost, sc);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(srp_cmd_queue);
-
-MODULE_DESCRIPTION("SCSI RDMA Protocol lib functions");
-MODULE_AUTHOR("FUJITA Tomonori");
-MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile
index 88928f00aa2d..bbd1faf41e80 100644
--- a/drivers/scsi/lpfc/Makefile
+++ b/drivers/scsi/lpfc/Makefile
@@ -1,9 +1,11 @@
#/*******************************************************************
# * This file is part of the Emulex Linux Device Driver for *
# * Fibre Channel Host Bus Adapters. *
-# * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+# * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+# * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+# * Copyright (C) 2004-2012 Emulex. All rights reserved. *
# * EMULEX and SLI are trademarks of Emulex. *
-# * www.emulex.com *
+# * www.broadcom.com *
# * *
# * This program is free software; you can redistribute it and/or *
# * modify it under the terms of version 2 of the GNU General *
@@ -22,8 +24,13 @@
ccflags-$(GCOV) := -fprofile-arcs -ftest-coverage
ccflags-$(GCOV) += -O0
+ifdef WARNINGS_BECOME_ERRORS
+ccflags-y += -Werror
+endif
+
obj-$(CONFIG_SCSI_LPFC) := lpfc.o
-lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o lpfc_hbadisc.o \
- lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsi.o lpfc_attr.o \
- lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o
+lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \
+ lpfc_hbadisc.o lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o \
+ lpfc_scsi.o lpfc_attr.o lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o \
+ lpfc_nvme.o lpfc_nvmet.o lpfc_vmid.o
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 8ec2c86a49d4..689793d03c20 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -20,8 +22,19 @@
*******************************************************************/
#include <scsi/scsi_host.h>
+#include <linux/hashtable.h>
+#include <linux/ktime.h>
+#include <linux/workqueue.h>
+
+#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS)
+#define CONFIG_SCSI_LPFC_DEBUG_FS
+#endif
+
struct lpfc_sli2_slim;
+#define ELX_MODEL_NAME_SIZE 80
+#define ELX_FW_NAME_SIZE 84
+
#define LPFC_PCI_DEV_LP 0x1
#define LPFC_PCI_DEV_OC 0x2
@@ -36,31 +49,32 @@ struct lpfc_sli2_slim;
the NameServer before giving up. */
#define LPFC_CMD_PER_LUN 3 /* max outstanding cmds per lun */
#define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
-#define LPFC_DEFAULT_MENLO_SG_SEG_CNT 128 /* sg element count per scsi
- cmnd for menlo needs nearly twice as for firmware
- downloads using bsg */
-#define LPFC_DEFAULT_PROT_SG_SEG_CNT 4096 /* sg protection elements count */
+
+#define LPFC_DEFAULT_XPSGL_SIZE 256
+#define LPFC_MAX_SG_TABLESIZE 0xffff
+#define LPFC_MIN_SG_SLI4_BUF_SZ 0x800 /* based on LPFC_DEFAULT_SG_SEG_CNT */
+#define LPFC_MAX_BG_SLI4_SEG_CNT_DIF 128 /* sg element count for BlockGuard */
+#define LPFC_MAX_SG_SEG_CNT_DIF 512 /* sg element count per scsi cmnd */
#define LPFC_MAX_SG_SEG_CNT 4096 /* sg element count per scsi cmnd */
+#define LPFC_MIN_SG_SEG_CNT 32 /* sg element count per scsi cmnd */
+#define LPFC_MAX_SGL_SEG_CNT 512 /* SGL element count per scsi cmnd */
+#define LPFC_MAX_BPL_SEG_CNT 4096 /* BPL element count per scsi cmnd */
+#define LPFC_MAX_NVME_SEG_CNT 256 /* max SGL element cnt per NVME cmnd */
+
#define LPFC_MAX_SGE_SIZE 0x80000000 /* Maximum data allowed in a SGE */
-#define LPFC_MAX_PROT_SG_SEG_CNT 4096 /* prot sg element count per scsi cmd*/
#define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */
#define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */
#define LPFC_VNAME_LEN 100 /* vport symbolic name length */
-#define LPFC_TGTQ_INTERVAL 40000 /* Min amount of time between tgt
- queue depth change in millisecs */
#define LPFC_TGTQ_RAMPUP_PCENT 5 /* Target queue rampup in percentage */
#define LPFC_MIN_TGT_QDEPTH 10
#define LPFC_MAX_TGT_QDEPTH 0xFFFF
-#define LPFC_MAX_BUCKET_COUNT 20 /* Maximum no. of buckets for stat data
- collection. */
/*
* Following time intervals are used of adjusting SCSI device
* queue depths when there are driver resource error or Firmware
* resource error.
*/
-#define QUEUE_RAMP_DOWN_INTERVAL (1 * HZ) /* 1 Second */
-#define QUEUE_RAMP_UP_INTERVAL (300 * HZ) /* 5 minutes */
+#define QUEUE_RAMP_DOWN_INTERVAL (secs_to_jiffies(1))
/* Number of exchanges reserved for discovery to complete */
#define LPFC_DISC_IOCB_BUFF_COUNT 20
@@ -81,18 +95,46 @@ struct lpfc_sli2_slim;
#define FC_MAX_ADPTMSG 64
#define MAX_HBAEVT 32
+#define MAX_HBAS_NO_RESET 16
/* Number of MSI-X vectors the driver uses */
#define LPFC_MSIX_VECTORS 2
/* lpfc wait event data ready flag */
-#define LPFC_DATA_READY (1<<0)
+#define LPFC_DATA_READY 0 /* bit 0 */
+
+/* queue dump line buffer size */
+#define LPFC_LBUF_SZ 128
+
+/* mailbox system shutdown options */
+#define LPFC_MBX_NO_WAIT 0
+#define LPFC_MBX_WAIT 1
+
+#define LPFC_CFG_PARAM_MAGIC_NUM 0xFEAA0005
+#define LPFC_PORT_CFG_NAME "/cfg/port.cfg"
+
+#define lpfc_rangecheck(val, min, max) \
+ ((uint)(val) >= (uint)(min) && (val) <= (max))
enum lpfc_polling_flags {
ENABLE_FCP_RING_POLLING = 0x1,
DISABLE_FCP_RING_INT = 0x2
};
+struct perf_prof {
+ uint16_t cmd_cpu[40];
+ uint16_t rsp_cpu[40];
+ uint16_t qh_cpu[40];
+ uint16_t wqidx[40];
+};
+
+/*
+ * Provide for FC4 TYPE x28 - NVME. The
+ * bit mask for FCP and NVME is 0x8 identically
+ * because they are 32 bit positions distance.
+ */
+#define LPFC_FC4_TYPE_BITMASK 0x00000100
+
/* Provide DMA memory definitions the driver uses per port instance. */
struct lpfc_dmabuf {
struct list_head list;
@@ -101,6 +143,14 @@ struct lpfc_dmabuf {
uint32_t buffer_tag; /* used for tagged queue ring */
};
+struct lpfc_nvmet_ctxbuf {
+ struct list_head list;
+ struct lpfc_async_xchg_ctx *context;
+ struct lpfc_iocbq *iocbq;
+ struct lpfc_sglq *sglq;
+ struct work_struct defer_work;
+};
+
struct lpfc_dma_pool {
struct lpfc_dmabuf *elements;
uint32_t max_count;
@@ -110,10 +160,22 @@ struct lpfc_dma_pool {
struct hbq_dmabuf {
struct lpfc_dmabuf hbuf;
struct lpfc_dmabuf dbuf;
- uint32_t size;
+ uint16_t total_size;
+ uint16_t bytes_recv;
uint32_t tag;
struct lpfc_cq_event cq_event;
unsigned long time_stamp;
+ void *context;
+};
+
+struct rqb_dmabuf {
+ struct lpfc_dmabuf hbuf;
+ struct lpfc_dmabuf dbuf;
+ uint16_t total_size;
+ uint16_t bytes_recv;
+ uint16_t idx;
+ struct lpfc_queue *hrq; /* ptr to associated Header RQ */
+ struct lpfc_queue *drq; /* ptr to associated Data RQ */
};
/* Priority bit. Set value to exceed low water mark in lpfc_mem. */
@@ -147,8 +209,7 @@ typedef struct lpfc_vpd {
} rev;
struct {
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd3 :19; /* Reserved */
- uint32_t cdss : 1; /* Configure Data Security SLI */
+ uint32_t rsvd3 :20; /* Reserved */
uint32_t rsvd2 : 3; /* Reserved */
uint32_t cbg : 1; /* Configure BlockGuard */
uint32_t cmv : 1; /* Configure Max VPIs */
@@ -170,14 +231,11 @@ typedef struct lpfc_vpd {
uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t cbg : 1; /* Configure BlockGuard */
uint32_t rsvd2 : 3; /* Reserved */
- uint32_t cdss : 1; /* Configure Data Security SLI */
- uint32_t rsvd3 :19; /* Reserved */
+ uint32_t rsvd3 :20; /* Reserved */
#endif
} sli3Feat;
} lpfc_vpd_t;
-struct lpfc_scsi_buf;
-
/*
* lpfc stat counters
@@ -204,11 +262,13 @@ struct lpfc_stats {
uint32_t elsRcvPRLI;
uint32_t elsRcvLIRR;
uint32_t elsRcvRLS;
- uint32_t elsRcvRPS;
uint32_t elsRcvRPL;
uint32_t elsRcvRRQ;
uint32_t elsRcvRTV;
uint32_t elsRcvECHO;
+ uint32_t elsRcvLCB;
+ uint32_t elsRcvRDP;
+ uint32_t elsRcvRDF;
uint32_t elsXmitFLOGI;
uint32_t elsXmitFDISC;
uint32_t elsXmitPLOGI;
@@ -216,6 +276,7 @@ struct lpfc_stats {
uint32_t elsXmitADISC;
uint32_t elsXmitLOGO;
uint32_t elsXmitSCR;
+ uint32_t elsXmitRSCN;
uint32_t elsXmitRNID;
uint32_t elsXmitFARP;
uint32_t elsXmitFARPR;
@@ -242,20 +303,72 @@ struct lpfc_stats {
uint32_t fcpLocalErr;
};
-enum sysfs_mbox_state {
- SMBOX_IDLE,
- SMBOX_WRITING,
- SMBOX_READING
+struct lpfc_hba;
+
+/* Data structure to keep withheld FLOGI_ACC information */
+struct lpfc_defer_flogi_acc {
+ bool flag;
+ u16 rx_id;
+ u16 ox_id;
+ struct lpfc_nodelist *ndlp;
};
-struct lpfc_sysfs_mbox {
- enum sysfs_mbox_state state;
- size_t offset;
- struct lpfcMboxq * mbox;
+#define LPFC_VMID_TIMER 300 /* timer interval in seconds */
+
+#define LPFC_MAX_VMID_SIZE 256
+
+union lpfc_vmid_io_tag {
+ u32 app_id; /* App Id vmid */
+ u8 cs_ctl_vmid; /* Priority tag vmid */
};
-struct lpfc_hba;
+#define JIFFIES_PER_HR (HZ * 60 * 60)
+
+struct lpfc_vmid {
+ u8 flag;
+#define LPFC_VMID_SLOT_FREE 0x0
+#define LPFC_VMID_SLOT_USED 0x1
+#define LPFC_VMID_REQ_REGISTER 0x2
+#define LPFC_VMID_REGISTERED 0x4
+#define LPFC_VMID_DE_REGISTER 0x8
+ char host_vmid[LPFC_MAX_VMID_SIZE];
+ union lpfc_vmid_io_tag un;
+ struct hlist_node hnode;
+ u64 io_rd_cnt;
+ u64 io_wr_cnt;
+ u8 vmid_len;
+ u8 delete_inactive; /* Delete if inactive flag 0 = no, 1 = yes */
+ u32 hash_index;
+ u64 __percpu *last_io_time;
+};
+
+#define lpfc_vmid_is_type_priority_tag(vport)\
+ (vport->vmid_priority_tagging ? 1 : 0)
+
+#define LPFC_VMID_HASH_SIZE 256
+#define LPFC_VMID_HASH_MASK 255
+#define LPFC_VMID_HASH_SHIFT 6
+
+struct lpfc_vmid_context {
+ struct lpfc_vmid *vmp;
+ struct lpfc_nodelist *nlp;
+ bool instantiated;
+};
+struct lpfc_vmid_priority_range {
+ u8 low;
+ u8 high;
+ u8 qos;
+};
+
+struct lpfc_vmid_priority_info {
+ u32 num_descriptors;
+ struct lpfc_vmid_priority_range *vmid_range;
+};
+
+#define QFPA_EVEN_ONLY 0x01
+#define QFPA_ODD_ONLY 0x02
+#define QFPA_EVEN_ODD 0x03
enum discovery_state {
LPFC_VPORT_UNKNOWN = 0, /* vport state is unknown */
@@ -286,6 +399,217 @@ enum hba_state {
LPFC_HBA_ERROR = -1
};
+enum lpfc_hba_flag { /* hba generic flags */
+ HBA_ERATT_HANDLED = 0, /* This flag is set when eratt handled */
+ DEFER_ERATT = 1, /* Deferred error attn in progress */
+ HBA_FCOE_MODE = 2, /* HBA function in FCoE Mode */
+ HBA_SP_QUEUE_EVT = 3, /* Slow-path qevt posted to worker thread*/
+ HBA_POST_RECEIVE_BUFFER = 4, /* Rcv buffers need to be posted */
+ HBA_PERSISTENT_TOPO = 5, /* Persistent topology support in hba */
+ ELS_XRI_ABORT_EVENT = 6, /* ELS_XRI abort event was queued */
+ ASYNC_EVENT = 7,
+ LINK_DISABLED = 8, /* Link disabled by user */
+ FCF_TS_INPROG = 9, /* FCF table scan in progress */
+ FCF_RR_INPROG = 10, /* FCF roundrobin flogi in progress */
+ HBA_FIP_SUPPORT = 11, /* FIP support in HBA */
+ HBA_DEVLOSS_TMO = 13, /* HBA in devloss timeout */
+ HBA_RRQ_ACTIVE = 14, /* process the rrq active list */
+ HBA_IOQ_FLUSH = 15, /* I/O queues being flushed */
+ HBA_RECOVERABLE_UE = 17, /* FW supports recoverable UE */
+ HBA_FORCED_LINK_SPEED = 18, /*
+ * Firmware supports Forced Link
+ * Speed capability
+ */
+ HBA_FLOGI_ISSUED = 20, /* FLOGI was issued */
+ HBA_DEFER_FLOGI = 23, /* Defer FLOGI till read_sparm cmpl */
+ HBA_SETUP = 24, /* HBA setup completed */
+ HBA_NEEDS_CFG_PORT = 25, /* SLI3: CONFIG_PORT mbox needed */
+ HBA_HBEAT_INP = 26, /* mbox HBEAT is in progress */
+ HBA_HBEAT_TMO = 27, /* HBEAT initiated after timeout */
+ HBA_FLOGI_OUTSTANDING = 28, /* FLOGI is outstanding */
+ HBA_RHBA_CMPL = 29, /* RHBA FDMI cmd is successful */
+};
+
+struct lpfc_trunk_link_state {
+ enum hba_state state;
+ uint8_t fault;
+};
+
+struct lpfc_trunk_link {
+ struct lpfc_trunk_link_state link0,
+ link1,
+ link2,
+ link3;
+ u32 phy_lnk_speed;
+};
+
+/* Format of congestion module parameters */
+struct lpfc_cgn_param {
+ uint32_t cgn_param_magic;
+ uint8_t cgn_param_version; /* version 1 */
+ uint8_t cgn_param_mode; /* 0=off 1=managed 2=monitor only */
+#define LPFC_CFG_OFF 0
+#define LPFC_CFG_MANAGED 1
+#define LPFC_CFG_MONITOR 2
+ uint8_t cgn_rsvd1;
+ uint8_t cgn_rsvd2;
+ uint8_t cgn_param_level0;
+ uint8_t cgn_param_level1;
+ uint8_t cgn_param_level2;
+ uint8_t byte11;
+ uint8_t byte12;
+ uint8_t byte13;
+ uint8_t byte14;
+ uint8_t byte15;
+};
+
+/* Max number of days of congestion data */
+#define LPFC_MAX_CGN_DAYS 10
+
+struct lpfc_cgn_ts {
+ uint8_t month;
+ uint8_t day;
+ uint8_t year;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+};
+
+/* Format of congestion buffer info
+ * This structure defines memory thats allocated and registered with
+ * the HBA firmware. When adding or removing fields from this structure
+ * the alignment must match the HBA firmware.
+ */
+
+struct lpfc_cgn_info {
+ /* Header */
+ __le16 cgn_info_size; /* is sizeof(struct lpfc_cgn_info) */
+ uint8_t cgn_info_version; /* represents format of structure */
+#define LPFC_CGN_INFO_V1 1
+#define LPFC_CGN_INFO_V2 2
+#define LPFC_CGN_INFO_V3 3
+#define LPFC_CGN_INFO_V4 4
+ uint8_t cgn_info_mode; /* 0=off 1=managed 2=monitor only */
+ uint8_t cgn_info_detect;
+ uint8_t cgn_info_action;
+ uint8_t cgn_info_level0;
+ uint8_t cgn_info_level1;
+ uint8_t cgn_info_level2;
+
+ /* Start Time */
+ struct lpfc_cgn_ts base_time;
+
+ /* minute / hours / daily indices */
+ uint8_t cgn_index_minute;
+ uint8_t cgn_index_hour;
+ uint8_t cgn_index_day;
+
+ __le16 cgn_warn_freq;
+ __le16 cgn_alarm_freq;
+ __le16 cgn_lunq;
+ uint8_t cgn_pad1[8];
+
+ /* Driver Information */
+ __le16 cgn_drvr_min[60];
+ __le32 cgn_drvr_hr[24];
+ __le32 cgn_drvr_day[LPFC_MAX_CGN_DAYS];
+
+ /* Congestion Warnings */
+ __le16 cgn_warn_min[60];
+ __le32 cgn_warn_hr[24];
+ __le32 cgn_warn_day[LPFC_MAX_CGN_DAYS];
+
+ /* Latency Information */
+ __le32 cgn_latency_min[60];
+ __le32 cgn_latency_hr[24];
+ __le32 cgn_latency_day[LPFC_MAX_CGN_DAYS];
+
+ /* Bandwidth Information */
+ __le16 cgn_bw_min[60];
+ __le16 cgn_bw_hr[24];
+ __le16 cgn_bw_day[LPFC_MAX_CGN_DAYS];
+
+ /* Congestion Alarms */
+ __le16 cgn_alarm_min[60];
+ __le32 cgn_alarm_hr[24];
+ __le32 cgn_alarm_day[LPFC_MAX_CGN_DAYS];
+
+ struct_group(cgn_stat,
+ uint8_t cgn_stat_npm; /* Notifications per minute */
+
+ /* Start Time */
+ struct lpfc_cgn_ts stat_start; /* Base time */
+ uint8_t cgn_pad2;
+
+ __le32 cgn_notification;
+ __le32 cgn_peer_notification;
+ __le32 link_integ_notification;
+ __le32 delivery_notification;
+ struct lpfc_cgn_ts stat_fpin; /* Last congestion notification FPIN */
+ struct lpfc_cgn_ts stat_peer; /* Last peer congestion FPIN */
+ struct lpfc_cgn_ts stat_lnk; /* Last link integrity FPIN */
+ struct lpfc_cgn_ts stat_delivery; /* Last delivery notification FPIN */
+ );
+
+ __le32 cgn_info_crc;
+#define LPFC_CGN_CRC32_MAGIC_NUMBER 0x1EDC6F41
+#define LPFC_CGN_CRC32_SEED 0xFFFFFFFF
+};
+
+#define LPFC_CGN_INFO_SZ (sizeof(struct lpfc_cgn_info) - \
+ sizeof(uint32_t))
+
+struct lpfc_cgn_stat {
+ atomic64_t total_bytes;
+ atomic64_t rcv_bytes;
+ atomic64_t rx_latency;
+#define LPFC_CGN_NOT_SENT 0xFFFFFFFFFFFFFFFFLL
+ atomic_t rx_io_cnt;
+};
+
+struct lpfc_cgn_acqe_stat {
+ atomic64_t alarm;
+ atomic64_t warn;
+};
+
+enum lpfc_fc_flag {
+ /* Several of these flags are HBA centric and should be moved to
+ * phba->link_flag (e.g. FC_PTP, FC_PUBLIC_LOOP)
+ */
+ FC_PT2PT, /* pt2pt with no fabric */
+ FC_PT2PT_PLOGI, /* pt2pt initiate PLOGI */
+ FC_DISC_TMO, /* Discovery timer running */
+ FC_PUBLIC_LOOP, /* Public loop */
+ FC_LBIT, /* LOGIN bit in loopinit set */
+ FC_RSCN_MODE, /* RSCN cmd rcv'ed */
+ FC_NLP_MORE, /* More node to process in node tbl */
+ FC_OFFLINE_MODE, /* Interface is offline for diag */
+ FC_FABRIC, /* We are fabric attached */
+ FC_VPORT_LOGO_RCVD, /* LOGO received on vport */
+ FC_RSCN_DISCOVERY, /* Auth all devices after RSCN */
+ FC_LOGO_RCVD_DID_CHNG, /* FDISC on phys port detect DID chng */
+ FC_PT2PT_NO_NVME, /* Don't send NVME PRLI */
+ FC_SCSI_SCAN_TMO, /* scsi scan timer running */
+ FC_ABORT_DISCOVERY, /* we want to abort discovery */
+ FC_NDISC_ACTIVE, /* NPort discovery active */
+ FC_BYPASSED_MODE, /* NPort is in bypassed mode */
+ FC_VPORT_NEEDS_REG_VPI, /* Needs to have its vpi registered */
+ FC_RSCN_DEFERRED, /* A deferred RSCN being processed */
+ FC_VPORT_NEEDS_INIT_VPI, /* Need to INIT_VPI before FDISC */
+ FC_VPORT_CVL_RCVD, /* VLink failed due to CVL */
+ FC_VFI_REGISTERED, /* VFI is registered */
+ FC_FDISC_COMPLETED, /* FDISC completed */
+ FC_DISC_DELAYED, /* Delay NPort discovery */
+};
+
+enum lpfc_load_flag {
+ FC_LOADING, /* HBA in process of loading drvr */
+ FC_UNLOADING, /* HBA in process of unloading drvr */
+ FC_ALLOW_FDMI, /* port is ready for FDMI requests */
+ FC_ALLOW_VMID, /* Allow VMID I/Os */
+ FC_DEREGISTER_ALL_APP_ID /* Deregister all VMIDs */
+};
+
struct lpfc_vport {
struct lpfc_hba *phba;
struct list_head listentry;
@@ -300,33 +624,7 @@ struct lpfc_vport {
uint8_t vpi_state;
#define LPFC_VPI_REGISTERED 0x1
- uint32_t fc_flag; /* FC flags */
-/* Several of these flags are HBA centric and should be moved to
- * phba->link_flag (e.g. FC_PTP, FC_PUBLIC_LOOP)
- */
-#define FC_PT2PT 0x1 /* pt2pt with no fabric */
-#define FC_PT2PT_PLOGI 0x2 /* pt2pt initiate PLOGI */
-#define FC_DISC_TMO 0x4 /* Discovery timer running */
-#define FC_PUBLIC_LOOP 0x8 /* Public loop */
-#define FC_LBIT 0x10 /* LOGIN bit in loopinit set */
-#define FC_RSCN_MODE 0x20 /* RSCN cmd rcv'ed */
-#define FC_NLP_MORE 0x40 /* More node to process in node tbl */
-#define FC_OFFLINE_MODE 0x80 /* Interface is offline for diag */
-#define FC_FABRIC 0x100 /* We are fabric attached */
-#define FC_VPORT_LOGO_RCVD 0x200 /* LOGO received on vport */
-#define FC_RSCN_DISCOVERY 0x400 /* Auth all devices after RSCN */
-#define FC_LOGO_RCVD_DID_CHNG 0x800 /* FDISC on phys port detect DID chng*/
-#define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */
-#define FC_ABORT_DISCOVERY 0x8000 /* we want to abort discovery */
-#define FC_NDISC_ACTIVE 0x10000 /* NPort discovery active */
-#define FC_BYPASSED_MODE 0x20000 /* NPort is in bypassed mode */
-#define FC_VPORT_NEEDS_REG_VPI 0x80000 /* Needs to have its vpi registered */
-#define FC_RSCN_DEFERRED 0x100000 /* A deferred RSCN being processed */
-#define FC_VPORT_NEEDS_INIT_VPI 0x200000 /* Need to INIT_VPI before FDISC */
-#define FC_VPORT_CVL_RCVD 0x400000 /* VLink failed due to CVL */
-#define FC_VFI_REGISTERED 0x800000 /* VFI is registered */
-#define FC_FDISC_COMPLETED 0x1000000/* FDISC completed */
-#define FC_DISC_DELAYED 0x2000000/* Delay NPort discovery */
+ unsigned long fc_flag; /* FC flags */
uint32_t ct_flags;
#define FC_CT_RFF_ID 0x1 /* RFF_ID accepted by switch */
@@ -334,18 +632,22 @@ struct lpfc_vport {
#define FC_CT_RSNN_NN 0x4 /* RSNN_NN accepted by switch */
#define FC_CT_RSPN_ID 0x8 /* RSPN_ID accepted by switch */
#define FC_CT_RFT_ID 0x10 /* RFT_ID accepted by switch */
+#define FC_CT_RPRT_DEFER 0x20 /* Defer issuing FDMI RPRT */
+#define FC_CT_RSPNI_PNI 0x40 /* RSPNI_PNI accepted by switch */
struct list_head fc_nodes;
+ spinlock_t fc_nodes_list_lock; /* spinlock for fc_nodes list */
/* Keep counters for the number of entries in each list. */
- uint16_t fc_plogi_cnt;
- uint16_t fc_adisc_cnt;
- uint16_t fc_reglogin_cnt;
- uint16_t fc_prli_cnt;
- uint16_t fc_unmap_cnt;
- uint16_t fc_map_cnt;
- uint16_t fc_npr_cnt;
- uint16_t fc_unused_cnt;
+ atomic_t fc_plogi_cnt;
+ atomic_t fc_adisc_cnt;
+ atomic_t fc_reglogin_cnt;
+ atomic_t fc_prli_cnt;
+ atomic_t fc_unmap_cnt;
+ atomic_t fc_map_cnt;
+ atomic_t fc_npr_cnt;
+ atomic_t fc_unused_cnt;
+
struct serv_parm fc_sparam; /* buffer for our service parameters */
uint32_t fc_myDID; /* fibre channel S_ID */
@@ -356,17 +658,15 @@ struct lpfc_vport {
int32_t stopped; /* HBA has not been restarted since last ERATT */
uint8_t fc_linkspeed; /* Link speed after last READ_LA */
- uint32_t num_disc_nodes; /*in addition to hba_state */
+ uint32_t num_disc_nodes; /* in addition to hba_state */
+ uint32_t gidft_inp; /* cnt of outstanding GID_FTs */
- uint32_t fc_nlp_cnt; /* outstanding NODELIST requests */
uint32_t fc_rscn_id_cnt; /* count of RSCNs payloads in list */
uint32_t fc_rscn_flush; /* flag use of fc_rscn_id_list */
struct lpfc_dmabuf *fc_rscn_id_list[FC_MAX_HOLD_RSCN];
struct lpfc_name fc_nodename; /* fc nodename */
struct lpfc_name fc_portname; /* fc portname */
- struct lpfc_work_evt disc_timeout_evt;
-
struct timer_list fc_disctmo; /* Discovery rescue timer */
uint8_t fc_ns_retry; /* retries for fabric nameserver */
uint32_t fc_prli_sent; /* cntr for outstanding PRLIs */
@@ -375,7 +675,6 @@ struct lpfc_vport {
uint32_t work_port_events; /* Timeout to be handled */
#define WORKER_DISC_TMO 0x1 /* vport: Discovery timeout */
#define WORKER_ELS_TMO 0x2 /* vport: ELS timeout */
-#define WORKER_FDMI_TMO 0x4 /* vport: FDMI timeout */
#define WORKER_DELAYED_DISC_TMO 0x8 /* vport: delayed discovery */
#define WORKER_MBOX_TMO 0x100 /* hba: MBOX timeout */
@@ -384,16 +683,14 @@ struct lpfc_vport {
#define WORKER_RAMP_DOWN_QUEUE 0x800 /* hba: Decrease Q depth */
#define WORKER_RAMP_UP_QUEUE 0x1000 /* hba: Increase Q depth */
#define WORKER_SERVICE_TXQ 0x2000 /* hba: IOCBs on the txq */
+#define WORKER_CHECK_INACTIVE_VMID 0x4000 /* hba: check inactive vmids */
+#define WORKER_CHECK_VMID_ISSUE_QFPA 0x8000 /* vport: Check if qfpa needs
+ * to be issued */
- struct timer_list fc_fdmitmo;
struct timer_list els_tmofunc;
struct timer_list delayed_disc_tmo;
- int unreg_vpi_cmpl;
-
- uint8_t load_flag;
-#define FC_LOADING 0x1 /* HBA in process of loading drvr */
-#define FC_UNLOADING 0x2 /* HBA in process of unloading drvr */
+ unsigned long load_flag;
/* Vport Config Parameters */
uint32_t cfg_scan_down;
uint32_t cfg_lun_queue_depth;
@@ -403,31 +700,65 @@ struct lpfc_vport {
uint32_t cfg_peer_port_login;
uint32_t cfg_fcp_class;
uint32_t cfg_use_adisc;
- uint32_t cfg_fdmi_on;
uint32_t cfg_discovery_threads;
uint32_t cfg_log_verbose;
+ uint32_t cfg_enable_fc4_type;
uint32_t cfg_max_luns;
uint32_t cfg_enable_da_id;
uint32_t cfg_max_scsicmpl_time;
uint32_t cfg_tgt_queue_depth;
-
+ uint32_t cfg_first_burst_size;
uint32_t dev_loss_tmo_changed;
+ /* VMID parameters */
+ u8 lpfc_vmid_host_uuid[16];
+ u32 max_vmid; /* maximum VMIDs allowed per port */
+ u32 cur_vmid_cnt; /* Current VMID count */
+#define LPFC_MIN_VMID 4
+#define LPFC_MAX_VMID 255
+ u32 vmid_inactivity_timeout; /* Time after which the VMID */
+ /* deregisters from switch */
+ u32 vmid_priority_tagging;
+#define LPFC_VMID_PRIO_TAG_DISABLE 0 /* Disable */
+#define LPFC_VMID_PRIO_TAG_SUP_TARGETS 1 /* Allow supported targets only */
+#define LPFC_VMID_PRIO_TAG_ALL_TARGETS 2 /* Allow all targets */
+ unsigned long *vmid_priority_range;
+#define LPFC_VMID_MAX_PRIORITY_RANGE 256
+#define LPFC_VMID_PRIORITY_BITMAP_SIZE 32
+ u8 vmid_flag;
+#define LPFC_VMID_IN_USE 0x1
+#define LPFC_VMID_ISSUE_QFPA 0x2
+#define LPFC_VMID_QFPA_CMPL 0x4
+#define LPFC_VMID_QOS_ENABLED 0x8
+#define LPFC_VMID_TIMER_ENBLD 0x10
+#define LPFC_VMID_TYPE_PRIO 0x20
+ struct fc_qfpa_res *qfpa_res;
struct fc_vport *fc_vport;
+ struct lpfc_vmid *vmid;
+ DECLARE_HASHTABLE(hash_table, 8);
+ rwlock_t vmid_lock;
+ struct lpfc_vmid_priority_info vmid_priority;
+
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- struct dentry *debug_disc_trc;
- struct dentry *debug_nodelist;
struct dentry *vport_debugfs_root;
struct lpfc_debugfs_trc *disc_trc;
atomic_t disc_trc_cnt;
#endif
- uint8_t stat_data_enabled;
- uint8_t stat_data_blocked;
struct list_head rcv_buffer_list;
unsigned long rcv_buffer_time_stamp;
uint32_t vport_flag;
-#define STATIC_VPORT 1
+#define STATIC_VPORT 0x1
+#define FAWWPN_PARAM_CHG 0x2
+
+ uint16_t fdmi_num_disc;
+ uint32_t fdmi_hba_mask;
+ uint32_t fdmi_port_mask;
+
+ /* There is a single nvme instance per vport. */
+ struct nvme_fc_local_port *localport;
+ uint8_t nvmei_support; /* driver supports NVME Initiator */
+ uint32_t rcv_flogi_cnt; /* How many unsol FLOGIs ACK'd. */
};
struct hbq_s {
@@ -445,10 +776,9 @@ struct hbq_s {
struct hbq_dmabuf *);
};
-#define LPFC_MAX_HBQS 4
/* this matches the position in the lpfc_hbq_defs array */
#define LPFC_ELS_HBQ 0
-#define LPFC_EXTRA_HBQ 1
+#define LPFC_MAX_HBQS 1
enum hba_temp_state {
HBA_NORMAL_TEMP,
@@ -462,12 +792,15 @@ enum intr_type_t {
MSIX,
};
+#define LPFC_CT_CTX_MAX 64
struct unsol_rcv_ct_ctx {
uint32_t ctxt_id;
uint32_t SID;
- uint32_t oxid;
- uint32_t flags;
-#define UNSOL_VALID 0x00000001
+ uint32_t valid;
+#define UNSOL_INVALID 0
+#define UNSOL_VALID 1
+ uint16_t oxid;
+ uint16_t rxid;
};
#define LPFC_USER_LINK_SPEED_AUTO 0 /* auto select (default)*/
@@ -477,15 +810,11 @@ struct unsol_rcv_ct_ctx {
#define LPFC_USER_LINK_SPEED_8G 8 /* 8 Gigabaud */
#define LPFC_USER_LINK_SPEED_10G 10 /* 10 Gigabaud */
#define LPFC_USER_LINK_SPEED_16G 16 /* 16 Gigabaud */
-#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_16G
-#define LPFC_USER_LINK_SPEED_BITMAP ((1 << LPFC_USER_LINK_SPEED_16G) | \
- (1 << LPFC_USER_LINK_SPEED_10G) | \
- (1 << LPFC_USER_LINK_SPEED_8G) | \
- (1 << LPFC_USER_LINK_SPEED_4G) | \
- (1 << LPFC_USER_LINK_SPEED_2G) | \
- (1 << LPFC_USER_LINK_SPEED_1G) | \
- (1 << LPFC_USER_LINK_SPEED_AUTO))
-#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16"
+#define LPFC_USER_LINK_SPEED_32G 32 /* 32 Gigabaud */
+#define LPFC_USER_LINK_SPEED_64G 64 /* 64 Gigabaud */
+#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_64G
+
+#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16, 32, 64"
enum nemb_type {
nemb_mse = 1,
@@ -523,44 +852,110 @@ struct lpfc_mbox_ext_buf_ctx {
struct list_head ext_dmabuf_list;
};
+struct lpfc_epd_pool {
+ /* Expedite pool */
+ struct list_head list;
+ u32 count;
+ spinlock_t lock; /* lock for expedite pool */
+};
+
+enum ras_state {
+ INACTIVE,
+ REG_INPROGRESS,
+ ACTIVE
+};
+
+struct lpfc_ras_fwlog {
+ uint8_t *fwlog_buff;
+ uint32_t fw_buffcount; /* Buffer size posted to FW */
+#define LPFC_RAS_BUFF_ENTERIES 16 /* Each entry can hold max of 64k */
+#define LPFC_RAS_MAX_ENTRY_SIZE (64 * 1024)
+#define LPFC_RAS_MIN_BUFF_POST_SIZE (256 * 1024)
+#define LPFC_RAS_MAX_BUFF_POST_SIZE (1024 * 1024)
+ uint32_t fw_loglevel; /* Log level set */
+ struct lpfc_dmabuf lwpd;
+ struct list_head fwlog_buff_list;
+
+ /* RAS support status on adapter */
+ bool ras_hwsupport; /* RAS Support available on HW or not */
+ bool ras_enabled; /* Ras Enabled for the function */
+#define LPFC_RAS_DISABLE_LOGGING 0x00
+#define LPFC_RAS_ENABLE_LOGGING 0x01
+ enum ras_state state; /* RAS logging running state */
+};
+
+#define DBG_LOG_STR_SZ 256
+#define DBG_LOG_SZ 256
+
+struct dbg_log_ent {
+ char log[DBG_LOG_STR_SZ];
+ u64 t_ns;
+};
+
+enum lpfc_irq_chann_mode {
+ /* Assign IRQs to all possible cpus that have hardware queues */
+ NORMAL_MODE,
+
+ /* Assign IRQs only to cpus on the same numa node as HBA */
+ NUMA_MODE,
+
+ /* Assign IRQs only on non-hyperthreaded CPUs. This is the
+ * same as normal_mode, but assign IRQS only on physical CPUs.
+ */
+ NHT_MODE,
+};
+
+enum lpfc_hba_bit_flags {
+ FABRIC_COMANDS_BLOCKED,
+ HBA_PCI_ERR,
+ MBX_TMO_ERR,
+};
+
struct lpfc_hba {
/* SCSI interface function jump table entries */
- int (*lpfc_new_scsi_buf)
- (struct lpfc_vport *, int);
- struct lpfc_scsi_buf * (*lpfc_get_scsi_buf)
- (struct lpfc_hba *, struct lpfc_nodelist *);
+ struct lpfc_io_buf * (*lpfc_get_scsi_buf)
+ (struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd);
int (*lpfc_scsi_prep_dma_buf)
- (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ (struct lpfc_hba *, struct lpfc_io_buf *);
void (*lpfc_scsi_unprep_dma_buf)
- (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ (struct lpfc_hba *, struct lpfc_io_buf *);
void (*lpfc_release_scsi_buf)
- (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ (struct lpfc_hba *, struct lpfc_io_buf *);
void (*lpfc_rampdown_queue_depth)
(struct lpfc_hba *);
void (*lpfc_scsi_prep_cmnd)
- (struct lpfc_vport *, struct lpfc_scsi_buf *,
+ (struct lpfc_vport *, struct lpfc_io_buf *,
struct lpfc_nodelist *);
+ int (*lpfc_scsi_prep_cmnd_buf)
+ (struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_cmd,
+ uint8_t tmo);
+ int (*lpfc_scsi_prep_task_mgmt_cmd)
+ (struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_cmd,
+ u64 lun, u8 task_mgmt_cmd);
+
/* IOCB interface function jump table entries */
int (*__lpfc_sli_issue_iocb)
(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, uint32_t);
+ int (*__lpfc_sli_issue_fcp_io)
+ (struct lpfc_hba *phba, uint32_t ring_number,
+ struct lpfc_iocbq *piocb, uint32_t flag);
void (*__lpfc_sli_release_iocbq)(struct lpfc_hba *,
struct lpfc_iocbq *);
int (*lpfc_hba_down_post)(struct lpfc_hba *phba);
-
- IOCB_t * (*lpfc_get_iocb_from_iocbq)
- (struct lpfc_iocbq *);
- void (*lpfc_scsi_cmd_iocb_cmpl)
- (struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *);
-
/* MBOX interface function jump table entries */
int (*lpfc_sli_issue_mbox)
(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
+
/* Slow-path IOCB process function jump table entries */
void (*lpfc_sli_handle_slow_ring_event)
(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
uint32_t mask);
+
/* INIT device interface function jump table entries */
int (*lpfc_sli_hbq_to_firmware)
(struct lpfc_hba *, uint32_t, struct hbq_dmabuf *);
@@ -579,9 +974,39 @@ struct lpfc_hba {
int (*lpfc_selective_reset)
(struct lpfc_hba *);
+ int (*lpfc_bg_scsi_prep_dma_buf)
+ (struct lpfc_hba *, struct lpfc_io_buf *);
+
+ /* Prep SLI WQE/IOCB jump table entries */
+ void (*__lpfc_sli_prep_els_req_rsp)(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_vport *vport,
+ struct lpfc_dmabuf *bmp,
+ u16 cmd_size, u32 did, u32 elscmd,
+ u8 tmo, u8 expect_rsp);
+ void (*__lpfc_sli_prep_gen_req)(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi,
+ u32 num_entry, u8 tmo);
+ void (*__lpfc_sli_prep_xmit_seq64)(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi,
+ u16 ox_id, u32 num_entry, u8 rctl,
+ u8 last_seq, u8 cr_cx_cmd);
+ void (*__lpfc_sli_prep_abort_xri)(struct lpfc_iocbq *cmdiocbq,
+ u16 ulp_context, u16 iotag,
+ u8 ulp_class, u16 cqid, bool ia,
+ bool wqec);
+
+ /* expedite pool */
+ struct lpfc_epd_pool epd_pool;
+
/* SLI4 specific HBA data structure */
struct lpfc_sli4_hba sli4_hba;
+ struct workqueue_struct *wq;
+ struct delayed_work eq_delay_work;
+
+#define LPFC_IDLE_STAT_DELAY 1000
+ struct delayed_work idle_stat_delay_work;
+
struct lpfc_sli sli;
uint8_t pci_dev_grp; /* lpfc PCI dev group: 0x0, 0x1, 0x2,... */
uint32_t sli_rev; /* SLI2, SLI3, or SLI4 */
@@ -597,6 +1022,7 @@ struct lpfc_hba {
uint32_t iocb_cmd_size;
uint32_t iocb_rsp_size;
+ struct lpfc_trunk_link trunk_link;
enum hba_state link_state;
uint32_t link_flag; /* link state flags */
#define LS_LOOPBACK_MODE 0x1 /* NPort is in Loopback mode */
@@ -604,23 +1030,14 @@ struct lpfc_hba {
/* INIT_LINK mailbox command */
#define LS_NPIV_FAB_SUPPORTED 0x2 /* Fabric supports NPIV */
#define LS_IGNORE_ERATT 0x4 /* intr handler should ignore ERATT */
+#define LS_MDS_LINK_DOWN 0x8 /* MDS Diagnostics Link Down */
+#define LS_MDS_LOOPBACK 0x10 /* MDS Diagnostics Link Up (Loopback) */
+#define LS_CT_VEN_RPA 0x20 /* Vendor RPA sent to switch */
+#define LS_EXTERNAL_LOOPBACK 0x40 /* External loopback plug inserted */
+
+ unsigned long hba_flag; /* hba generic flags */
- uint32_t hba_flag; /* hba generic flags */
-#define HBA_ERATT_HANDLED 0x1 /* This flag is set when eratt handled */
-#define DEFER_ERATT 0x2 /* Deferred error attention in progress */
-#define HBA_FCOE_MODE 0x4 /* HBA function in FCoE Mode */
-#define HBA_SP_QUEUE_EVT 0x8 /* Slow-path qevt posted to worker thread*/
-#define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */
-#define FCP_XRI_ABORT_EVENT 0x20
-#define ELS_XRI_ABORT_EVENT 0x40
-#define ASYNC_EVENT 0x80
-#define LINK_DISABLED 0x100 /* Link disabled by user */
-#define FCF_TS_INPROG 0x200 /* FCF table scan in progress */
-#define FCF_RR_INPROG 0x400 /* FCF roundrobin flogi in progress */
-#define HBA_FIP_SUPPORT 0x800 /* FIP support in HBA */
-#define HBA_AER_ENABLED 0x1000 /* AER enabled with HBA */
-#define HBA_DEVLOSS_TMO 0x2000 /* HBA in devloss timeout */
-#define HBA_RRQ_ACTIVE 0x4000 /* process the rrq active list */
+ struct completion *fw_dump_cmpl; /* cmpl event tracker for fw_dump */
uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/
struct lpfc_dmabuf slim2p;
@@ -633,8 +1050,6 @@ struct lpfc_hba {
struct lpfc_dmabuf hbqslimp;
- uint16_t pci_cfg_value;
-
uint8_t fc_linkspeed; /* Link speed after last READ_LA */
uint32_t fc_eventTag; /* event tag for link attention */
@@ -650,7 +1065,6 @@ struct lpfc_hba {
uint32_t fc_rttov; /* R_T_TOV timer value */
uint32_t fc_altov; /* AL_TOV timer value */
uint32_t fc_crtov; /* C_R_TOV timer value */
- uint32_t fc_citov; /* C_I_TOV timer value */
struct serv_parm fc_fabparam; /* fabric service parameters buffer */
uint8_t alpa_map[128]; /* AL_PA map from READ_LA */
@@ -658,22 +1072,39 @@ struct lpfc_hba {
uint32_t lmt;
uint32_t fc_topology; /* link topology, from LINK INIT */
+ uint32_t fc_topology_changed; /* link topology, from LINK INIT */
struct lpfc_stats fc_stat;
- struct lpfc_nodelist fc_fcpnodev; /* nodelist entry for no device */
uint32_t nport_event_cnt; /* timestamp for nlplist entry */
+ unsigned long pni; /* 64-bit Platform Name Identifier */
+
uint8_t wwnn[8];
uint8_t wwpn[8];
uint32_t RandomData[7];
+ uint8_t fcp_embed_io;
+ uint8_t nvmet_support; /* driver supports NVMET */
+#define LPFC_NVMET_MAX_PORTS 32
+ uint8_t mds_diags_support;
+ uint8_t bbcredit_support;
+ uint8_t enab_exp_wqcq_pages;
+ u8 nsler; /* Firmware supports FC-NVMe-2 SLER */
/* HBA Config Parameters */
uint32_t cfg_ack0;
+ uint32_t cfg_xri_rebalancing;
+ uint32_t cfg_xpsgl;
uint32_t cfg_enable_npiv;
uint32_t cfg_enable_rrq;
uint32_t cfg_topology;
uint32_t cfg_link_speed;
+#define LPFC_FCF_FOV 1 /* Fast fcf failover */
+#define LPFC_FCF_PRIORITY 2 /* Priority fcf failover */
+ uint32_t cfg_fcf_failover_policy;
+ uint32_t cfg_fcp_io_sched;
+ uint32_t cfg_ns_query;
+ uint32_t cfg_fcp2_no_tgt_reset;
uint32_t cfg_cr_delay;
uint32_t cfg_cr_count;
uint32_t cfg_multi_ring_support;
@@ -681,31 +1112,96 @@ struct lpfc_hba {
uint32_t cfg_multi_ring_type;
uint32_t cfg_poll;
uint32_t cfg_poll_tmo;
+ uint32_t cfg_task_mgmt_tmo;
uint32_t cfg_use_msi;
+ uint32_t cfg_auto_imax;
uint32_t cfg_fcp_imax;
- uint32_t cfg_fcp_wq_count;
- uint32_t cfg_fcp_eq_count;
+ uint32_t cfg_force_rscn;
+ uint32_t cfg_cq_poll_threshold;
+ uint32_t cfg_cq_max_proc_limit;
+ uint32_t cfg_fcp_cpu_map;
+ uint32_t cfg_fcp_mq_threshold;
+ uint32_t cfg_hdw_queue;
+ uint32_t cfg_irq_chann;
+ uint32_t cfg_suppress_rsp;
+ uint32_t cfg_nvme_oas;
+ uint32_t cfg_nvme_embed_cmd;
+ uint32_t cfg_nvmet_mrq_post;
+ uint32_t cfg_nvmet_mrq;
+ uint32_t cfg_enable_nvmet;
+ uint32_t cfg_nvme_enable_fb;
+ uint32_t cfg_nvmet_fb_size;
+ uint32_t cfg_total_seg_cnt;
uint32_t cfg_sg_seg_cnt;
- uint32_t cfg_prot_sg_seg_cnt;
+ uint32_t cfg_nvme_seg_cnt;
+ uint32_t cfg_scsi_seg_cnt;
uint32_t cfg_sg_dma_buf_size;
- uint64_t cfg_soft_wwnn;
- uint64_t cfg_soft_wwpn;
uint32_t cfg_hba_queue_depth;
uint32_t cfg_enable_hba_reset;
uint32_t cfg_enable_hba_heartbeat;
+ uint32_t cfg_fof;
+ uint32_t cfg_EnableXLane;
+ uint8_t cfg_oas_tgt_wwpn[8];
+ uint8_t cfg_oas_vpt_wwpn[8];
+ uint32_t cfg_oas_lun_state;
+#define OAS_LUN_ENABLE 1
+#define OAS_LUN_DISABLE 0
+ uint32_t cfg_oas_lun_status;
+#define OAS_LUN_STATUS_EXISTS 0x01
+ uint32_t cfg_oas_flags;
+#define OAS_FIND_ANY_VPORT 0x01
+#define OAS_FIND_ANY_TARGET 0x02
+#define OAS_LUN_VALID 0x04
+ uint32_t cfg_oas_priority;
+ uint32_t cfg_XLanePriority;
uint32_t cfg_enable_bg;
+ uint32_t cfg_prot_mask;
+ uint32_t cfg_prot_guard;
uint32_t cfg_hostmem_hgp;
uint32_t cfg_log_verbose;
- uint32_t cfg_aer_support;
+ uint32_t cfg_enable_fc4_type;
+#define LPFC_ENABLE_FCP 1
+#define LPFC_ENABLE_NVME 2
+#define LPFC_ENABLE_BOTH 3
+#if (IS_ENABLED(CONFIG_NVME_FC))
+#define LPFC_MAX_ENBL_FC4_TYPE LPFC_ENABLE_BOTH
+#define LPFC_DEF_ENBL_FC4_TYPE LPFC_ENABLE_BOTH
+#else
+#define LPFC_MAX_ENBL_FC4_TYPE LPFC_ENABLE_FCP
+#define LPFC_DEF_ENBL_FC4_TYPE LPFC_ENABLE_FCP
+#endif
uint32_t cfg_sriov_nr_virtfn;
- uint32_t cfg_iocb_cnt;
+ uint32_t cfg_request_firmware_upgrade;
uint32_t cfg_suppress_link_up;
+ uint32_t cfg_rrq_xri_bitmap_sz;
+ u32 cfg_fcp_wait_abts_rsp;
+ uint32_t cfg_delay_discovery;
+ uint32_t cfg_sli_mode;
#define LPFC_INITIALIZE_LINK 0 /* do normal init_link mbox */
#define LPFC_DELAY_INIT_LINK 1 /* layered driver hold off */
#define LPFC_DELAY_INIT_LINK_INDEFINITELY 2 /* wait, manual intervention */
- uint32_t cfg_enable_dss;
+ uint32_t cfg_fdmi_on;
+#define LPFC_FDMI_NO_SUPPORT 0 /* FDMI not supported */
+#define LPFC_FDMI_SUPPORT 1 /* FDMI supported? */
+ uint32_t cfg_enable_SmartSAN;
+ uint32_t cfg_enable_mds_diags;
+ uint32_t cfg_ras_fwlog_level;
+ uint32_t cfg_ras_fwlog_buffsize;
+ uint32_t cfg_ras_fwlog_func;
+ uint32_t cfg_enable_bbcr; /* Enable BB Credit Recovery */
+ uint32_t cfg_enable_dpp; /* Enable Direct Packet Push */
+ uint32_t cfg_enable_pbde;
+ uint32_t cfg_enable_mi;
+ struct nvmet_fc_target_port *targetport;
lpfc_vpd_t vpd; /* vital product data */
+ u32 cfg_max_vmid; /* maximum VMIDs allowed per port */
+ u32 cfg_vmid_app_header;
+#define LPFC_VMID_APP_HEADER_DISABLE 0
+#define LPFC_VMID_APP_HEADER_ENABLE 1
+ u32 cfg_vmid_priority_tagging;
+ u32 cfg_vmid_inactivity_timeout; /* Time after which the VMID */
+ /* deregisters from switch */
struct pci_dev *pcidev;
struct list_head work_list;
uint32_t work_ha; /* Host Attention Bits for WT */
@@ -716,22 +1212,29 @@ struct lpfc_hba {
wait_queue_head_t work_waitq;
struct task_struct *worker_thread;
unsigned long data_flags;
+ uint32_t border_sge_num;
uint32_t hbq_in_use; /* HBQs in use flag */
- struct list_head rb_pend_list; /* Received buffers to be processed */
uint32_t hbq_count; /* Count of configured HBQs */
struct hbq_s hbqs[LPFC_MAX_HBQS]; /* local copy of hbq indicies */
- uint32_t fcp_qidx; /* next work queue to post work to */
-
- unsigned long pci_bar0_map; /* Physical address for PCI BAR0 */
- unsigned long pci_bar1_map; /* Physical address for PCI BAR1 */
- unsigned long pci_bar2_map; /* Physical address for PCI BAR2 */
+ phys_addr_t pci_bar0_map; /* Physical address for PCI BAR0 */
+ phys_addr_t pci_bar1_map; /* Physical address for PCI BAR1 */
+ phys_addr_t pci_bar2_map; /* Physical address for PCI BAR2 */
void __iomem *slim_memmap_p; /* Kernel memory mapped address for
PCI BAR0 */
void __iomem *ctrl_regs_memmap_p;/* Kernel memory mapped address for
PCI BAR2 */
+ void __iomem *pci_bar0_memmap_p; /* Kernel memory mapped address for
+ PCI BAR0 with dual-ULP support */
+ void __iomem *pci_bar2_memmap_p; /* Kernel memory mapped address for
+ PCI BAR2 with dual-ULP support */
+ void __iomem *pci_bar4_memmap_p; /* Kernel memory mapped address for
+ PCI BAR4 with dual-ULP support */
+#define PCI_64BIT_BAR0 0
+#define PCI_64BIT_BAR2 2
+#define PCI_64BIT_BAR4 4
void __iomem *MBslimaddr; /* virtual address for mbox cmds */
void __iomem *HAregaddr; /* virtual address for host attn reg */
void __iomem *CAregaddr; /* virtual address for chip attn reg */
@@ -747,6 +1250,7 @@ struct lpfc_hba {
int brd_no; /* FC board number */
char SerialNumber[32]; /* adapter Serial Number */
char OptionROMVersion[32]; /* adapter BIOS / Fcode version */
+ char BIOSVersion[16]; /* Boot BIOS version */
char ModelDesc[256]; /* Model Description */
char ModelName[80]; /* Model Name */
char ProgramType[256]; /* Program Type */
@@ -759,54 +1263,54 @@ struct lpfc_hba {
#define VPD_PORT 0x8 /* valid vpd port data */
#define VPD_MASK 0xf /* mask for any vpd data */
- uint8_t soft_wwn_enable;
struct timer_list fcp_poll_timer;
struct timer_list eratt_poll;
+ uint32_t eratt_poll_interval;
- /*
- * stat counters
- */
- uint64_t fc4InputRequests;
- uint64_t fc4OutputRequests;
- uint64_t fc4ControlRequests;
uint64_t bg_guard_err_cnt;
uint64_t bg_apptag_err_cnt;
uint64_t bg_reftag_err_cnt;
- struct lpfc_sysfs_mbox sysfs_mbox;
-
/* fastpath list. */
- spinlock_t scsi_buf_list_lock;
- struct list_head lpfc_scsi_buf_list;
+ spinlock_t scsi_buf_list_get_lock; /* SCSI buf alloc list lock */
+ spinlock_t scsi_buf_list_put_lock; /* SCSI buf free list lock */
+ struct list_head lpfc_scsi_buf_list_get;
+ struct list_head lpfc_scsi_buf_list_put;
uint32_t total_scsi_bufs;
struct list_head lpfc_iocb_list;
uint32_t total_iocbq_bufs;
+ spinlock_t rrq_list_lock; /* lock for active_rrq_list */
struct list_head active_rrq_list;
spinlock_t hbalock;
-
- /* pci_mem_pools */
- struct pci_pool *lpfc_scsi_dma_buf_pool;
- struct pci_pool *lpfc_mbuf_pool;
- struct pci_pool *lpfc_hrb_pool; /* header receive buffer pool */
- struct pci_pool *lpfc_drb_pool; /* data receive buffer pool */
- struct pci_pool *lpfc_hbq_pool; /* SLI3 hbq buffer pool */
+ struct work_struct unblock_request_work; /* SCSI layer unblock IOs */
+
+ /* dma_mem_pools */
+ struct dma_pool *lpfc_sg_dma_buf_pool;
+ struct dma_pool *lpfc_mbuf_pool;
+ struct dma_pool *lpfc_hrb_pool; /* header receive buffer pool */
+ struct dma_pool *lpfc_drb_pool; /* data receive buffer pool */
+ struct dma_pool *lpfc_nvmet_drb_pool; /* data receive buffer pool */
+ struct dma_pool *lpfc_hbq_pool; /* SLI3 hbq buffer pool */
+ struct dma_pool *lpfc_cmd_rsp_buf_pool;
struct lpfc_dma_pool lpfc_mbuf_safety_pool;
mempool_t *mbox_mem_pool;
mempool_t *nlp_mem_pool;
mempool_t *rrq_pool;
+ mempool_t *active_rrq_pool;
struct fc_host_statistics link_stats;
+ enum lpfc_irq_chann_mode irq_chann_mode;
enum intr_type_t intr_type;
uint32_t intr_mode;
#define LPFC_INTR_ERROR 0xFFFFFFFF
- struct msix_entry msix_entries[LPFC_MSIX_VECTORS];
-
struct list_head port_list;
+ spinlock_t port_list_lock; /* lock for port_list mutations */
struct lpfc_vport *pport; /* physical lpfc_vport pointer */
uint16_t max_vpi; /* Maximum virtual nports */
-#define LPFC_MAX_VPI 0xFFFF /* Max number of VPI supported */
+#define LPFC_MAX_VPI 0xFF /* Max number VPI supported 0 - 0xff */
+#define LPFC_MAX_VPORTS 0x100 /* Max vports per port, with pport */
uint16_t max_vports; /*
* For IOV HBAs max_vpi can change
* after a reset. max_vports is max
@@ -825,30 +1329,37 @@ struct lpfc_hba {
atomic_t fabric_iocb_count;
struct timer_list fabric_block_timer;
unsigned long bit_flags;
-#define FABRIC_COMANDS_BLOCKED 0
atomic_t num_rsrc_err;
- atomic_t num_cmd_success;
unsigned long last_rsrc_error_time;
unsigned long last_ramp_down_time;
- unsigned long last_ramp_up_time;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *hba_debugfs_root;
- atomic_t debugfs_vport_count;
- struct dentry *debug_hbqinfo;
- struct dentry *debug_dumpHostSlim;
- struct dentry *debug_dumpHBASlim;
- struct dentry *debug_dumpData; /* BlockGuard BPL*/
- struct dentry *debug_dumpDif; /* BlockGuard BPL*/
- struct dentry *debug_slow_ring_trc;
+ unsigned int debugfs_vport_count;
+
+ struct lpfc_debugfs_nvmeio_trc *nvmeio_trc;
+ atomic_t nvmeio_trc_cnt;
+ uint32_t nvmeio_trc_size;
+ uint32_t nvmeio_trc_output_idx;
+
+ /* T10 DIF error injection */
+ uint32_t lpfc_injerr_wgrd_cnt;
+ uint32_t lpfc_injerr_wapp_cnt;
+ uint32_t lpfc_injerr_wref_cnt;
+ uint32_t lpfc_injerr_rgrd_cnt;
+ uint32_t lpfc_injerr_rapp_cnt;
+ uint32_t lpfc_injerr_rref_cnt;
+ uint32_t lpfc_injerr_nportid;
+ struct lpfc_name lpfc_injerr_wwpn;
+ sector_t lpfc_injerr_lba;
+#define LPFC_INJERR_LBA_OFF (sector_t)(-1)
+
struct lpfc_debugfs_trc *slow_ring_trc;
atomic_t slow_ring_trc_cnt;
/* iDiag debugfs sub-directory */
struct dentry *idiag_root;
- struct dentry *idiag_pci_cfg;
- struct dentry *idiag_que_info;
- struct dentry *idiag_que_acc;
- struct dentry *idiag_drb_acc;
+ uint8_t lpfc_idiag_last_eq;
#endif
+ uint16_t nvmeio_trc_on;
/* Used for deferred freeing of ELS data buffers */
struct list_head elsbuf;
@@ -860,50 +1371,208 @@ struct lpfc_hba {
unsigned long last_completion_time;
unsigned long skipped_hb;
struct timer_list hb_tmofunc;
- uint8_t hb_outstanding;
struct timer_list rrq_tmr;
enum hba_temp_state over_temp_state;
- /* ndlp reference management */
- spinlock_t ndlp_lock;
/*
* Following bit will be set for all buffer tags which are not
* associated with any HBQ.
*/
#define QUE_BUFTAG_BIT (1<<31)
uint32_t buffer_tag_count;
- int wait_4_mlo_maint_flg;
- wait_queue_head_t wait_4_mlo_m_q;
- /* data structure used for latency data collection */
-#define LPFC_NO_BUCKET 0
-#define LPFC_LINEAR_BUCKET 1
-#define LPFC_POWER2_BUCKET 2
- uint8_t bucket_type;
- uint32_t bucket_base;
- uint32_t bucket_step;
/* Maximum number of events that can be outstanding at any time*/
#define LPFC_MAX_EVT_COUNT 512
atomic_t fast_event_count;
uint32_t fcoe_eventtag;
uint32_t fcoe_eventtag_at_fcf_scan;
+ uint32_t fcoe_cvl_eventtag;
+ uint32_t fcoe_cvl_eventtag_attn;
struct lpfc_fcf fcf;
uint8_t fc_map[3];
uint8_t valid_vlan;
uint16_t vlan_id;
struct list_head fcf_conn_rec_list;
+ struct lpfc_defer_flogi_acc defer_flogi_acc;
+
spinlock_t ct_ev_lock; /* synchronize access to ct_ev_waiters */
struct list_head ct_ev_waiters;
- struct unsol_rcv_ct_ctx ct_ctx[64];
+ struct unsol_rcv_ct_ctx ct_ctx[LPFC_CT_CTX_MAX];
uint32_t ctx_idx;
+ struct timer_list inactive_vmid_poll;
+
+ /* RAS Support */
+ spinlock_t ras_fwlog_lock; /* do not take while holding another lock */
+ struct lpfc_ras_fwlog ras_fwlog;
- uint8_t menlo_flag; /* menlo generic flags */
-#define HBA_MENLO_SUPPORT 0x1 /* HBA supports menlo commands */
uint32_t iocb_cnt;
uint32_t iocb_max;
atomic_t sdev_cnt;
- uint8_t fips_spec_rev;
- uint8_t fips_level;
+ spinlock_t devicelock; /* lock for luns list */
+ mempool_t *device_data_mem_pool;
+ struct list_head luns;
+#define LPFC_TRANSGRESSION_HIGH_TEMPERATURE 0x0080
+#define LPFC_TRANSGRESSION_LOW_TEMPERATURE 0x0040
+#define LPFC_TRANSGRESSION_HIGH_VOLTAGE 0x0020
+#define LPFC_TRANSGRESSION_LOW_VOLTAGE 0x0010
+#define LPFC_TRANSGRESSION_HIGH_TXBIAS 0x0008
+#define LPFC_TRANSGRESSION_LOW_TXBIAS 0x0004
+#define LPFC_TRANSGRESSION_HIGH_TXPOWER 0x0002
+#define LPFC_TRANSGRESSION_LOW_TXPOWER 0x0001
+#define LPFC_TRANSGRESSION_HIGH_RXPOWER 0x8000
+#define LPFC_TRANSGRESSION_LOW_RXPOWER 0x4000
+ uint16_t sfp_alarm;
+ uint16_t sfp_warning;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint16_t hdwqstat_on;
+#define LPFC_CHECK_OFF 0
+#define LPFC_CHECK_NVME_IO 1
+#define LPFC_CHECK_NVMET_IO 2
+#define LPFC_CHECK_SCSI_IO 4
+ uint16_t ktime_on;
+ uint64_t ktime_data_samples;
+ uint64_t ktime_status_samples;
+ uint64_t ktime_last_cmd;
+ uint64_t ktime_seg1_total;
+ uint64_t ktime_seg1_min;
+ uint64_t ktime_seg1_max;
+ uint64_t ktime_seg2_total;
+ uint64_t ktime_seg2_min;
+ uint64_t ktime_seg2_max;
+ uint64_t ktime_seg3_total;
+ uint64_t ktime_seg3_min;
+ uint64_t ktime_seg3_max;
+ uint64_t ktime_seg4_total;
+ uint64_t ktime_seg4_min;
+ uint64_t ktime_seg4_max;
+ uint64_t ktime_seg5_total;
+ uint64_t ktime_seg5_min;
+ uint64_t ktime_seg5_max;
+ uint64_t ktime_seg6_total;
+ uint64_t ktime_seg6_min;
+ uint64_t ktime_seg6_max;
+ uint64_t ktime_seg7_total;
+ uint64_t ktime_seg7_min;
+ uint64_t ktime_seg7_max;
+ uint64_t ktime_seg8_total;
+ uint64_t ktime_seg8_min;
+ uint64_t ktime_seg8_max;
+ uint64_t ktime_seg9_total;
+ uint64_t ktime_seg9_min;
+ uint64_t ktime_seg9_max;
+ uint64_t ktime_seg10_total;
+ uint64_t ktime_seg10_min;
+ uint64_t ktime_seg10_max;
+#endif
+ /* CMF objects */
+ struct lpfc_cgn_stat __percpu *cmf_stat;
+ uint32_t cmf_interval_rate; /* timer interval limit in ms */
+ uint32_t cmf_timer_cnt;
+#define LPFC_CMF_INTERVAL 90
+ uint64_t cmf_link_byte_count;
+ uint64_t cmf_max_line_rate;
+ uint64_t cmf_max_bytes_per_interval;
+ uint64_t cmf_last_sync_bw;
+#define LPFC_CMF_BLK_SIZE 512
+ struct hrtimer cmf_timer;
+ struct hrtimer cmf_stats_timer; /* 1 minute stats timer */
+ atomic_t cmf_bw_wait;
+ atomic_t cmf_busy;
+ atomic_t cmf_stop_io; /* To block request and stop IO's */
+ uint32_t cmf_active_mode;
+ uint32_t cmf_info_per_interval;
+#define LPFC_MAX_CMF_INFO 32
+ struct timespec64 cmf_latency; /* Interval congestion timestamp */
+ uint32_t cmf_last_ts; /* Interval congestion time (ms) */
+ uint32_t cmf_active_info;
+
+ /* Signal / FPIN handling for Congestion Mgmt */
+ u8 cgn_reg_fpin; /* Negotiated value from RDF */
+ u8 cgn_init_reg_fpin; /* Initial value from READ_CONFIG */
+#define LPFC_CGN_FPIN_NONE 0x0
+#define LPFC_CGN_FPIN_WARN 0x1
+#define LPFC_CGN_FPIN_ALARM 0x2
+#define LPFC_CGN_FPIN_BOTH (LPFC_CGN_FPIN_WARN | LPFC_CGN_FPIN_ALARM)
+
+ u8 cgn_reg_signal; /* Negotiated value from EDC */
+ u8 cgn_init_reg_signal; /* Initial value from READ_CONFIG */
+ /* cgn_reg_signal and cgn_init_reg_signal use
+ * enum fc_edc_cg_signal_cap_types
+ */
+ u16 cgn_fpin_frequency; /* In units of msecs */
+#define LPFC_FPIN_INIT_FREQ 0xffff
+ u32 cgn_sig_freq;
+ u32 cgn_acqe_cnt;
+
+ /* RX monitor handling for CMF */
+ struct lpfc_rx_info_monitor *rx_monitor;
+ atomic_t rx_max_read_cnt; /* Maximum read bytes */
+ uint64_t rx_block_cnt;
+
+ /* Congestion parameters from flash */
+ struct lpfc_cgn_param cgn_p;
+
+ /* Statistics counter for ACQE cgn alarms and warnings */
+ struct lpfc_cgn_acqe_stat cgn_acqe_stat;
+
+ /* Congestion buffer information */
+ struct lpfc_dmabuf *cgn_i; /* Congestion Info buffer */
+ atomic_t cgn_fabric_warn_cnt; /* Total warning cgn events for info */
+ atomic_t cgn_fabric_alarm_cnt; /* Total alarm cgn events for info */
+ atomic_t cgn_sync_warn_cnt; /* Total warning events for SYNC wqe */
+ atomic_t cgn_sync_alarm_cnt; /* Total alarm events for SYNC wqe */
+ atomic_t cgn_driver_evt_cnt; /* Total driver cgn events for fmw */
+ atomic_t cgn_latency_evt_cnt;
+ atomic64_t cgn_latency_evt; /* Avg latency per minute */
+ unsigned long cgn_evt_timestamp;
+#define LPFC_CGN_TIMER_TO_MIN 60000 /* ms in a minute */
+ uint32_t cgn_evt_minute;
+#define LPFC_SEC_MIN 60UL
+#define LPFC_MIN_HOUR 60
+#define LPFC_HOUR_DAY 24
+#define LPFC_MIN_DAY (LPFC_MIN_HOUR * LPFC_HOUR_DAY)
+
+ struct hlist_node cpuhp; /* used for cpuhp per hba callback */
+ struct timer_list cpuhp_poll_timer;
+ struct list_head poll_list; /* slowpath eq polling list */
+#define LPFC_POLL_HB 1 /* slowpath heartbeat */
+
+ char os_host_name[MAXHOSTNAMELEN];
+
+ /* LD Signaling */
+ u32 degrade_activate_threshold;
+ u32 degrade_deactivate_threshold;
+ u32 fec_degrade_interval;
+
+ atomic_t dbg_log_idx;
+ atomic_t dbg_log_cnt;
+ atomic_t dbg_log_dmping;
+ struct dbg_log_ent dbg_log[DBG_LOG_SZ];
+};
+
+#define LPFC_MAX_RXMONITOR_ENTRY 800
+#define LPFC_MAX_RXMONITOR_DUMP 32
+struct rx_info_entry {
+ uint64_t cmf_bytes; /* Total no of read bytes for CMF_SYNC_WQE */
+ uint64_t total_bytes; /* Total no of read bytes requested */
+ uint64_t rcv_bytes; /* Total no of read bytes completed */
+ uint64_t avg_io_size;
+ uint64_t avg_io_latency;/* Average io latency in microseconds */
+ uint64_t max_read_cnt; /* Maximum read bytes */
+ uint64_t max_bytes_per_interval;
+ uint32_t cmf_busy;
+ uint32_t cmf_info; /* CMF_SYNC_WQE info */
+ uint32_t io_cnt;
+ uint32_t timer_utilization;
+ uint32_t timer_interval;
+};
+
+struct lpfc_rx_info_monitor {
+ struct rx_info_entry *ring; /* info organized in a circular buffer */
+ u32 head_idx, tail_idx; /* index to head/tail of ring */
+ spinlock_t lock; /* spinlock for ring */
+ u32 entries; /* storing number entries/size of ring */
};
static inline struct Scsi_Host *
@@ -974,3 +1643,192 @@ lpfc_sli_read_hs(struct lpfc_hba *phba)
return 0;
}
+
+static inline struct lpfc_sli_ring *
+lpfc_phba_elsring(struct lpfc_hba *phba)
+{
+ /* Return NULL if sli_rev has become invalid due to bad fw */
+ if (phba->sli_rev != LPFC_SLI_REV4 &&
+ phba->sli_rev != LPFC_SLI_REV3 &&
+ phba->sli_rev != LPFC_SLI_REV2)
+ return NULL;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ if (phba->sli4_hba.els_wq)
+ return phba->sli4_hba.els_wq->pring;
+ else
+ return NULL;
+ }
+ return &phba->sli.sli3_ring[LPFC_ELS_RING];
+}
+
+/**
+ * lpfc_next_online_cpu - Finds next online CPU on cpumask
+ * @mask: Pointer to phba's cpumask member.
+ * @start: starting cpu index
+ *
+ * Note: If no valid cpu found, then nr_cpu_ids is returned.
+ *
+ **/
+static __always_inline unsigned int
+lpfc_next_online_cpu(const struct cpumask *mask, unsigned int start)
+{
+ return cpumask_next_and_wrap(start, mask, cpu_online_mask);
+}
+
+/**
+ * lpfc_next_present_cpu - Finds next present CPU after n
+ * @n: the cpu prior to search
+ *
+ * Note: If no next present cpu, then fallback to first present cpu.
+ *
+ **/
+static __always_inline unsigned int lpfc_next_present_cpu(int n)
+{
+ return cpumask_next_wrap(n, cpu_present_mask);
+}
+
+/**
+ * lpfc_sli4_mod_hba_eq_delay - update EQ delay
+ * @phba: Pointer to HBA context object.
+ * @q: The Event Queue to update.
+ * @delay: The delay value (in us) to be written.
+ *
+ **/
+static inline void
+lpfc_sli4_mod_hba_eq_delay(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ u32 delay)
+{
+ struct lpfc_register reg_data;
+
+ reg_data.word0 = 0;
+ bf_set(lpfc_sliport_eqdelay_id, &reg_data, eq->queue_id);
+ bf_set(lpfc_sliport_eqdelay_delay, &reg_data, delay);
+ writel(reg_data.word0, phba->sli4_hba.u.if_type2.EQDregaddr);
+ eq->q_mode = delay;
+}
+
+
+/*
+ * Macro that declares tables and a routine to perform enum type to
+ * ascii string lookup.
+ *
+ * Defines a <key,value> table for an enum. Uses xxx_INIT defines for
+ * the enum to populate the table. Macro defines a routine (named
+ * by caller) that will search all elements of the table for the key
+ * and return the name string if found or "Unrecognized" if not found.
+ */
+#define DECLARE_ENUM2STR_LOOKUP(routine, enum_name, enum_init) \
+static struct { \
+ enum enum_name value; \
+ char *name; \
+} fc_##enum_name##_e2str_names[] = enum_init; \
+static const char *routine(enum enum_name table_key) \
+{ \
+ int i; \
+ char *name = "Unrecognized"; \
+ \
+ for (i = 0; i < ARRAY_SIZE(fc_##enum_name##_e2str_names); i++) {\
+ if (fc_##enum_name##_e2str_names[i].value == table_key) {\
+ name = fc_##enum_name##_e2str_names[i].name; \
+ break; \
+ } \
+ } \
+ return name; \
+}
+
+/**
+ * lpfc_is_vmid_enabled - returns if VMID is enabled for either switch types
+ * @phba: Pointer to HBA context object.
+ *
+ * Relationship between the enable, target support and if vmid tag is required
+ * for the particular combination
+ * ---------------------------------------------------
+ * Switch Enable Flag Target Support VMID Needed
+ * ---------------------------------------------------
+ * App Id 0 NA N
+ * App Id 1 0 N
+ * App Id 1 1 Y
+ * Pr Tag 0 NA N
+ * Pr Tag 1 0 N
+ * Pr Tag 1 1 Y
+ * Pr Tag 2 * Y
+ ---------------------------------------------------
+ *
+ **/
+static inline int lpfc_is_vmid_enabled(struct lpfc_hba *phba)
+{
+ return phba->cfg_vmid_app_header || phba->cfg_vmid_priority_tagging;
+}
+
+static inline
+u8 get_job_ulpstatus(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return bf_get(lpfc_wcqe_c_status, &iocbq->wcqe_cmpl);
+ else
+ return iocbq->iocb.ulpStatus;
+}
+
+static inline
+u32 get_job_word4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return iocbq->wcqe_cmpl.parameter;
+ else
+ return iocbq->iocb.un.ulpWord[4];
+}
+
+static inline
+u8 get_job_cmnd(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return bf_get(wqe_cmnd, &iocbq->wqe.generic.wqe_com);
+ else
+ return iocbq->iocb.ulpCommand;
+}
+
+static inline
+u16 get_job_ulpcontext(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return bf_get(wqe_ctxt_tag, &iocbq->wqe.generic.wqe_com);
+ else
+ return iocbq->iocb.ulpContext;
+}
+
+static inline
+u16 get_job_rcvoxid(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return bf_get(wqe_rcvoxid, &iocbq->wqe.generic.wqe_com);
+ else
+ return iocbq->iocb.unsli3.rcvsli3.ox_id;
+}
+
+static inline
+u32 get_job_data_placed(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return iocbq->wcqe_cmpl.total_data_placed;
+ else
+ return iocbq->iocb.un.genreq64.bdl.bdeSize;
+}
+
+static inline
+u32 get_job_abtsiotag(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return iocbq->wqe.abort_cmd.wqe_com.abort_tag;
+ else
+ return iocbq->iocb.un.acxri.abortIoTag;
+}
+
+static inline
+u32 get_job_els_rsp64_did(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return bf_get(wqe_els_did, &iocbq->wqe.els_req.wqe_dest);
+ else
+ return iocbq->iocb.un.elsreq64.remoteID;
+}
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 135a53baa735..33582d48ec09 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -23,6 +25,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/aer.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
@@ -40,17 +43,45 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
#include "lpfc_logmsg.h"
#include "lpfc_version.h"
#include "lpfc_compat.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
+#include "lpfc_attr.h"
-#define LPFC_DEF_DEVLOSS_TMO 30
-#define LPFC_MIN_DEVLOSS_TMO 1
-#define LPFC_MAX_DEVLOSS_TMO 255
+#define LPFC_DEF_DEVLOSS_TMO 30
+#define LPFC_MIN_DEVLOSS_TMO 1
+#define LPFC_MAX_DEVLOSS_TMO 255
+
+#define LPFC_MAX_INFO_TMP_LEN 100
+#define LPFC_INFO_MORE_STR "\nCould be more info...\n"
+/*
+ * Write key size should be multiple of 4. If write key is changed
+ * make sure that library write key is also changed.
+ */
+#define LPFC_REG_WRITE_KEY_SIZE 4
+#define LPFC_REG_WRITE_KEY "EMLX"
+
+const char *const trunk_errmsg[] = { /* map errcode */
+ "", /* There is no such error code at index 0*/
+ "link negotiated speed does not match existing"
+ " trunk - link was \"low\" speed",
+ "link negotiated speed does not match"
+ " existing trunk - link was \"middle\" speed",
+ "link negotiated speed does not match existing"
+ " trunk - link was \"high\" speed",
+ "Attached to non-trunking port - F_Port",
+ "Attached to non-trunking port - N_Port",
+ "FLOGI response timeout",
+ "non-FLOGI frame received",
+ "Invalid FLOGI response",
+ "Trunking initialization protocol",
+ "Trunk peer device mismatch",
+};
/**
* lpfc_jedec_to_ascii - Hex to ascii convertor according to JEDEC rules
@@ -83,6 +114,315 @@ lpfc_jedec_to_ascii(int incr, char hdw[])
return;
}
+static ssize_t
+lpfc_cmf_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_cgn_info *cp = NULL;
+ struct lpfc_cgn_stat *cgs;
+ int len = 0;
+ int cpu;
+ u64 rcv, total;
+ char tmp[LPFC_MAX_INFO_TMP_LEN] = {0};
+
+ if (phba->cgn_i)
+ cp = (struct lpfc_cgn_info *)phba->cgn_i->virt;
+
+ scnprintf(tmp, sizeof(tmp),
+ "Congestion Mgmt Info: E2Eattr %d Ver %d "
+ "CMF %d cnt %d\n",
+ phba->sli4_hba.pc_sli4_params.mi_cap,
+ cp ? cp->cgn_info_version : 0,
+ phba->sli4_hba.pc_sli4_params.cmf, phba->cmf_timer_cnt);
+
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ if (!phba->sli4_hba.pc_sli4_params.cmf)
+ goto buffer_done;
+
+ switch (phba->cgn_init_reg_signal) {
+ case EDC_CG_SIG_WARN_ONLY:
+ scnprintf(tmp, sizeof(tmp),
+ "Register: Init: Signal:WARN ");
+ break;
+ case EDC_CG_SIG_WARN_ALARM:
+ scnprintf(tmp, sizeof(tmp),
+ "Register: Init: Signal:WARN|ALARM ");
+ break;
+ default:
+ scnprintf(tmp, sizeof(tmp),
+ "Register: Init: Signal:NONE ");
+ break;
+ }
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ switch (phba->cgn_init_reg_fpin) {
+ case LPFC_CGN_FPIN_WARN:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:WARN\n");
+ break;
+ case LPFC_CGN_FPIN_ALARM:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:ALARM\n");
+ break;
+ case LPFC_CGN_FPIN_BOTH:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:WARN|ALARM\n");
+ break;
+ default:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:NONE\n");
+ break;
+ }
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ switch (phba->cgn_reg_signal) {
+ case EDC_CG_SIG_WARN_ONLY:
+ scnprintf(tmp, sizeof(tmp),
+ " Current: Signal:WARN ");
+ break;
+ case EDC_CG_SIG_WARN_ALARM:
+ scnprintf(tmp, sizeof(tmp),
+ " Current: Signal:WARN|ALARM ");
+ break;
+ default:
+ scnprintf(tmp, sizeof(tmp),
+ " Current: Signal:NONE ");
+ break;
+ }
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ switch (phba->cgn_reg_fpin) {
+ case LPFC_CGN_FPIN_WARN:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:WARN ACQEcnt:%d\n", phba->cgn_acqe_cnt);
+ break;
+ case LPFC_CGN_FPIN_ALARM:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:ALARM ACQEcnt:%d\n", phba->cgn_acqe_cnt);
+ break;
+ case LPFC_CGN_FPIN_BOTH:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:WARN|ALARM ACQEcnt:%d\n", phba->cgn_acqe_cnt);
+ break;
+ default:
+ scnprintf(tmp, sizeof(tmp),
+ "FPIN:NONE ACQEcnt:%d\n", phba->cgn_acqe_cnt);
+ break;
+ }
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ if (phba->cmf_active_mode != phba->cgn_p.cgn_param_mode) {
+ switch (phba->cmf_active_mode) {
+ case LPFC_CFG_OFF:
+ scnprintf(tmp, sizeof(tmp), "Active: Mode:Off\n");
+ break;
+ case LPFC_CFG_MANAGED:
+ scnprintf(tmp, sizeof(tmp), "Active: Mode:Managed\n");
+ break;
+ case LPFC_CFG_MONITOR:
+ scnprintf(tmp, sizeof(tmp), "Active: Mode:Monitor\n");
+ break;
+ default:
+ scnprintf(tmp, sizeof(tmp), "Active: Mode:Unknown\n");
+ }
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+ }
+
+ switch (phba->cgn_p.cgn_param_mode) {
+ case LPFC_CFG_OFF:
+ scnprintf(tmp, sizeof(tmp), "Config: Mode:Off ");
+ break;
+ case LPFC_CFG_MANAGED:
+ scnprintf(tmp, sizeof(tmp), "Config: Mode:Managed ");
+ break;
+ case LPFC_CFG_MONITOR:
+ scnprintf(tmp, sizeof(tmp), "Config: Mode:Monitor ");
+ break;
+ default:
+ scnprintf(tmp, sizeof(tmp), "Config: Mode:Unknown ");
+ }
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ total = 0;
+ rcv = 0;
+ for_each_present_cpu(cpu) {
+ cgs = per_cpu_ptr(phba->cmf_stat, cpu);
+ total += atomic64_read(&cgs->total_bytes);
+ rcv += atomic64_read(&cgs->rcv_bytes);
+ }
+
+ scnprintf(tmp, sizeof(tmp),
+ "IObusy:%d Info:%d Bytes: Rcv:x%llx Total:x%llx\n",
+ atomic_read(&phba->cmf_busy),
+ phba->cmf_active_info, rcv, total);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "Port_speed:%d Link_byte_cnt:%ld "
+ "Max_byte_per_interval:%ld\n",
+ lpfc_sli_port_speed_get(phba),
+ (unsigned long)phba->cmf_link_byte_count,
+ (unsigned long)phba->cmf_max_bytes_per_interval);
+ strlcat(buf, tmp, PAGE_SIZE);
+
+buffer_done:
+ len = strnlen(buf, PAGE_SIZE);
+
+ if (unlikely(len >= (PAGE_SIZE - 1))) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6312 Catching potential buffer "
+ "overflow > PAGE_SIZE = %lu bytes\n",
+ PAGE_SIZE);
+ strscpy(buf + PAGE_SIZE - 1 - sizeof(LPFC_INFO_MORE_STR),
+ LPFC_INFO_MORE_STR, sizeof(LPFC_INFO_MORE_STR) + 1);
+ }
+ return len;
+}
+
+static ssize_t
+lpfc_vmid_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_vmid *vmp;
+ int len = 0, i, j, k, cpu;
+ char hxstr[LPFC_MAX_VMID_SIZE * 3] = {0};
+ struct timespec64 curr_tm;
+ struct lpfc_vmid_priority_range *vr;
+ u64 *lta, rct_acc = 0, max_lta = 0;
+ struct tm tm_val;
+
+ ktime_get_ts64(&curr_tm);
+
+ len += scnprintf(buf + len, PAGE_SIZE - len, "Key 'vmid':\n");
+
+ /* if enabled continue, else return */
+ if (lpfc_is_vmid_enabled(phba)) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "lpfc VMID Page: ON\n\n");
+ } else {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "lpfc VMID Page: OFF\n\n");
+ return len;
+ }
+
+ /* if using priority tagging */
+ if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "VMID priority ranges:\n");
+ vr = vport->vmid_priority.vmid_range;
+ for (i = 0; i < vport->vmid_priority.num_descriptors; ++i) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "\t[x%x - x%x], qos: x%x\n",
+ vr->low, vr->high, vr->qos);
+ vr++;
+ }
+ }
+
+ for (i = 0; i < phba->cfg_max_vmid; i++) {
+ vmp = &vport->vmid[i];
+ max_lta = 0;
+
+ /* only if the slot is used */
+ if (!(vmp->flag & LPFC_VMID_SLOT_USED) ||
+ !(vmp->flag & LPFC_VMID_REGISTERED))
+ continue;
+
+ /* if using priority tagging */
+ if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "VEM ID: %02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ vport->lpfc_vmid_host_uuid[0],
+ vport->lpfc_vmid_host_uuid[1],
+ vport->lpfc_vmid_host_uuid[2],
+ vport->lpfc_vmid_host_uuid[3],
+ vport->lpfc_vmid_host_uuid[4],
+ vport->lpfc_vmid_host_uuid[5],
+ vport->lpfc_vmid_host_uuid[6],
+ vport->lpfc_vmid_host_uuid[7],
+ vport->lpfc_vmid_host_uuid[8],
+ vport->lpfc_vmid_host_uuid[9],
+ vport->lpfc_vmid_host_uuid[10],
+ vport->lpfc_vmid_host_uuid[11],
+ vport->lpfc_vmid_host_uuid[12],
+ vport->lpfc_vmid_host_uuid[13],
+ vport->lpfc_vmid_host_uuid[14],
+ vport->lpfc_vmid_host_uuid[15]);
+ }
+
+ /* IO stats */
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "ID00 READs:%llx WRITEs:%llx\n",
+ vmp->io_rd_cnt,
+ vmp->io_wr_cnt);
+ for (j = 0, k = 0; j < strlen(vmp->host_vmid); j++, k += 3)
+ sprintf((char *)(hxstr + k), "%2x ", vmp->host_vmid[j]);
+ /* UUIDs */
+ len += scnprintf(buf + len, PAGE_SIZE - len, "UUID:\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", hxstr);
+
+ len += scnprintf(buf + len, PAGE_SIZE - len, "String (%s)\n",
+ vmp->host_vmid);
+
+ if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "CS_CTL VMID: 0x%x\n",
+ vmp->un.cs_ctl_vmid);
+ else
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Application id: 0x%x\n",
+ vmp->un.app_id);
+
+ /* calculate the last access time */
+ for_each_possible_cpu(cpu) {
+ lta = per_cpu_ptr(vmp->last_io_time, cpu);
+ if (!lta)
+ continue;
+
+ /* if last access time is less than timeout */
+ if (time_after((unsigned long)*lta, jiffies))
+ continue;
+
+ if (*lta > max_lta)
+ max_lta = *lta;
+ }
+
+ rct_acc = jiffies_to_msecs(jiffies - max_lta) / 1000;
+ /* current time */
+ time64_to_tm(ktime_get_real_seconds(),
+ -(sys_tz.tz_minuteswest * 60) - rct_acc, &tm_val);
+
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Last Access Time :"
+ "%ld-%d-%dT%02d:%02d:%02d\n\n",
+ 1900 + tm_val.tm_year, tm_val.tm_mon + 1,
+ tm_val.tm_mday, tm_val.tm_hour,
+ tm_val.tm_min, tm_val.tm_sec);
+
+ if (len >= PAGE_SIZE)
+ return len;
+
+ memset(hxstr, 0, LPFC_MAX_VMID_SIZE * 3);
+ }
+ return len;
+}
+
/**
* lpfc_drvr_version_show - Return the Emulex driver string with version number
* @dev: class unused variable.
@@ -95,7 +435,7 @@ static ssize_t
lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
+ return scnprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
}
/**
@@ -114,10 +454,433 @@ lpfc_enable_fip_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- if (phba->hba_flag & HBA_FIP_SUPPORT)
- return snprintf(buf, PAGE_SIZE, "1\n");
+ if (test_bit(HBA_FIP_SUPPORT, &phba->hba_flag))
+ return scnprintf(buf, PAGE_SIZE, "1\n");
else
- return snprintf(buf, PAGE_SIZE, "0\n");
+ return scnprintf(buf, PAGE_SIZE, "0\n");
+}
+
+static ssize_t
+lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct nvme_fc_local_port *localport;
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_nvme_rport *rport;
+ struct lpfc_nodelist *ndlp;
+ struct nvme_fc_remote_port *nrport;
+ struct lpfc_fc4_ctrl_stat *cstat;
+ uint64_t data1, data2, data3;
+ uint64_t totin, totout, tot;
+ unsigned long iflags;
+ char *statep;
+ int i;
+ int len = 0;
+ char tmp[LPFC_MAX_INFO_TMP_LEN] = {0};
+
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
+ len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n");
+ return len;
+ }
+ if (phba->nvmet_support) {
+ if (!phba->targetport) {
+ len = scnprintf(buf, PAGE_SIZE,
+ "NVME Target: x%llx is not allocated\n",
+ wwn_to_u64(vport->fc_portname.u.wwn));
+ return len;
+ }
+ /* Port state is only one of two values for now. */
+ if (phba->targetport->port_id)
+ statep = "REGISTERED";
+ else
+ statep = "INIT";
+ scnprintf(tmp, sizeof(tmp),
+ "NVME Target Enabled State %s\n",
+ statep);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "%s%d WWPN x%llx WWNN x%llx DID x%06x\n",
+ "NVME Target: lpfc",
+ phba->brd_no,
+ wwn_to_u64(vport->fc_portname.u.wwn),
+ wwn_to_u64(vport->fc_nodename.u.wwn),
+ phba->targetport->port_id);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ if (strlcat(buf, "\nNVME Target: Statistics\n", PAGE_SIZE)
+ >= PAGE_SIZE)
+ goto buffer_done;
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ scnprintf(tmp, sizeof(tmp),
+ "LS: Rcv %08x Drop %08x Abort %08x\n",
+ atomic_read(&tgtp->rcv_ls_req_in),
+ atomic_read(&tgtp->rcv_ls_req_drop),
+ atomic_read(&tgtp->xmt_ls_abort));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ if (atomic_read(&tgtp->rcv_ls_req_in) !=
+ atomic_read(&tgtp->rcv_ls_req_out)) {
+ scnprintf(tmp, sizeof(tmp),
+ "Rcv LS: in %08x != out %08x\n",
+ atomic_read(&tgtp->rcv_ls_req_in),
+ atomic_read(&tgtp->rcv_ls_req_out));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+ }
+
+ scnprintf(tmp, sizeof(tmp),
+ "LS: Xmt %08x Drop %08x Cmpl %08x\n",
+ atomic_read(&tgtp->xmt_ls_rsp),
+ atomic_read(&tgtp->xmt_ls_drop),
+ atomic_read(&tgtp->xmt_ls_rsp_cmpl));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "LS: RSP Abort %08x xb %08x Err %08x\n",
+ atomic_read(&tgtp->xmt_ls_rsp_aborted),
+ atomic_read(&tgtp->xmt_ls_rsp_xb_set),
+ atomic_read(&tgtp->xmt_ls_rsp_error));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "FCP: Rcv %08x Defer %08x Release %08x "
+ "Drop %08x\n",
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_defer),
+ atomic_read(&tgtp->xmt_fcp_release),
+ atomic_read(&tgtp->rcv_fcp_cmd_drop));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ if (atomic_read(&tgtp->rcv_fcp_cmd_in) !=
+ atomic_read(&tgtp->rcv_fcp_cmd_out)) {
+ scnprintf(tmp, sizeof(tmp),
+ "Rcv FCP: in %08x != out %08x\n",
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+ }
+
+ scnprintf(tmp, sizeof(tmp),
+ "FCP Rsp: RD %08x rsp %08x WR %08x rsp %08x "
+ "drop %08x\n",
+ atomic_read(&tgtp->xmt_fcp_read),
+ atomic_read(&tgtp->xmt_fcp_read_rsp),
+ atomic_read(&tgtp->xmt_fcp_write),
+ atomic_read(&tgtp->xmt_fcp_rsp),
+ atomic_read(&tgtp->xmt_fcp_drop));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "FCP Rsp Cmpl: %08x err %08x drop %08x\n",
+ atomic_read(&tgtp->xmt_fcp_rsp_cmpl),
+ atomic_read(&tgtp->xmt_fcp_rsp_error),
+ atomic_read(&tgtp->xmt_fcp_rsp_drop));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "FCP Rsp Abort: %08x xb %08x xricqe %08x\n",
+ atomic_read(&tgtp->xmt_fcp_rsp_aborted),
+ atomic_read(&tgtp->xmt_fcp_rsp_xb_set),
+ atomic_read(&tgtp->xmt_fcp_xri_abort_cqe));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "ABORT: Xmt %08x Cmpl %08x\n",
+ atomic_read(&tgtp->xmt_fcp_abort),
+ atomic_read(&tgtp->xmt_fcp_abort_cmpl));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "ABORT: Sol %08x Usol %08x Err %08x Cmpl %08x\n",
+ atomic_read(&tgtp->xmt_abort_sol),
+ atomic_read(&tgtp->xmt_abort_unsol),
+ atomic_read(&tgtp->xmt_abort_rsp),
+ atomic_read(&tgtp->xmt_abort_rsp_error));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "DELAY: ctx %08x fod %08x wqfull %08x\n",
+ atomic_read(&tgtp->defer_ctx),
+ atomic_read(&tgtp->defer_fod),
+ atomic_read(&tgtp->defer_wqfull));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ /* Calculate outstanding IOs */
+ tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
+ tot += atomic_read(&tgtp->xmt_fcp_release);
+ tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;
+
+ scnprintf(tmp, sizeof(tmp),
+ "IO_CTX: %08x WAIT: cur %08x tot %08x\n"
+ "CTX Outstanding %08llx\n\n",
+ phba->sli4_hba.nvmet_xri_cnt,
+ phba->sli4_hba.nvmet_io_wait_cnt,
+ phba->sli4_hba.nvmet_io_wait_total,
+ tot);
+ strlcat(buf, tmp, PAGE_SIZE);
+ goto buffer_done;
+ }
+
+ localport = vport->localport;
+ if (!localport) {
+ len = scnprintf(buf, PAGE_SIZE,
+ "NVME Initiator x%llx is not allocated\n",
+ wwn_to_u64(vport->fc_portname.u.wwn));
+ return len;
+ }
+ lport = (struct lpfc_nvme_lport *)localport->private;
+ if (strlcat(buf, "\nNVME Initiator Enabled\n", PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "XRI Dist lpfc%d Total %d IO %d ELS %d\n",
+ phba->brd_no,
+ phba->sli4_hba.max_cfg_param.max_xri,
+ phba->sli4_hba.io_xri_max,
+ lpfc_sli4_get_els_iocb_cnt(phba));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ /* Port state is only one of two values for now. */
+ if (localport->port_id)
+ statep = "ONLINE";
+ else
+ statep = "UNKNOWN ";
+
+ scnprintf(tmp, sizeof(tmp),
+ "%s%d WWPN x%llx WWNN x%llx DID x%06x %s\n",
+ "NVME LPORT lpfc",
+ phba->brd_no,
+ wwn_to_u64(vport->fc_portname.u.wwn),
+ wwn_to_u64(vport->fc_nodename.u.wwn),
+ localport->port_id, statep);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
+
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ nrport = NULL;
+ spin_lock(&ndlp->lock);
+ rport = lpfc_ndlp_get_nrport(ndlp);
+ if (rport)
+ nrport = rport->remoteport;
+ spin_unlock(&ndlp->lock);
+ if (!nrport)
+ continue;
+
+ /* Port state is only one of two values for now. */
+ switch (nrport->port_state) {
+ case FC_OBJSTATE_ONLINE:
+ statep = "ONLINE";
+ break;
+ case FC_OBJSTATE_UNKNOWN:
+ statep = "UNKNOWN ";
+ break;
+ default:
+ statep = "UNSUPPORTED";
+ break;
+ }
+
+ /* Tab in to show lport ownership. */
+ if (strlcat(buf, "NVME RPORT ", PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+ if (phba->brd_no >= 10) {
+ if (strlcat(buf, " ", PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+ }
+
+ scnprintf(tmp, sizeof(tmp), "WWPN x%llx ",
+ nrport->port_name);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+
+ scnprintf(tmp, sizeof(tmp), "WWNN x%llx ",
+ nrport->node_name);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+
+ scnprintf(tmp, sizeof(tmp), "DID x%06x ",
+ nrport->port_id);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+
+ /* An NVME rport can have multiple roles. */
+ if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR) {
+ if (strlcat(buf, "INITIATOR ", PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+ }
+ if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET) {
+ if (strlcat(buf, "TARGET ", PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+ }
+ if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY) {
+ if (strlcat(buf, "DISCSRVC ", PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+ }
+ if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR |
+ FC_PORT_ROLE_NVME_TARGET |
+ FC_PORT_ROLE_NVME_DISCOVERY)) {
+ scnprintf(tmp, sizeof(tmp), "UNKNOWN ROLE x%x",
+ nrport->port_role);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+ }
+
+ scnprintf(tmp, sizeof(tmp), "%s\n", statep);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto unlock_buf_done;
+ }
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
+
+ if (!lport)
+ goto buffer_done;
+
+ if (strlcat(buf, "\nNVME Statistics\n", PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "LS: Xmt %010x Cmpl %010x Abort %08x\n",
+ atomic_read(&lport->fc4NvmeLsRequests),
+ atomic_read(&lport->fc4NvmeLsCmpls),
+ atomic_read(&lport->xmt_ls_abort));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "LS XMIT: Err %08x CMPL: xb %08x Err %08x\n",
+ atomic_read(&lport->xmt_ls_err),
+ atomic_read(&lport->cmpl_ls_xb),
+ atomic_read(&lport->cmpl_ls_err));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ totin = 0;
+ totout = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
+ tot = cstat->io_cmpls;
+ totin += tot;
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
+ totout += (data1 + data2 + data3);
+ }
+ scnprintf(tmp, sizeof(tmp),
+ "Total FCP Cmpl %016llx Issue %016llx "
+ "OutIO %016llx\n",
+ totin, totout, totout - totin);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "\tabort %08x noxri %08x nondlp %08x qdepth %08x "
+ "wqerr %08x err %08x\n",
+ atomic_read(&lport->xmt_fcp_abort),
+ atomic_read(&lport->xmt_fcp_noxri),
+ atomic_read(&lport->xmt_fcp_bad_ndlp),
+ atomic_read(&lport->xmt_fcp_qdepth),
+ atomic_read(&lport->xmt_fcp_wqerr),
+ atomic_read(&lport->xmt_fcp_err));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp),
+ "FCP CMPL: xb %08x Err %08x\n",
+ atomic_read(&lport->cmpl_fcp_xb),
+ atomic_read(&lport->cmpl_fcp_err));
+ strlcat(buf, tmp, PAGE_SIZE);
+
+ /* host_lock is already unlocked. */
+ goto buffer_done;
+
+ unlock_buf_done:
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
+
+ buffer_done:
+ len = strnlen(buf, PAGE_SIZE);
+
+ if (unlikely(len >= (PAGE_SIZE - 1))) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
+ "6314 Catching potential buffer "
+ "overflow > PAGE_SIZE = %lu bytes\n",
+ PAGE_SIZE);
+ strscpy(buf + PAGE_SIZE - 1 - sizeof(LPFC_INFO_MORE_STR),
+ LPFC_INFO_MORE_STR,
+ sizeof(LPFC_INFO_MORE_STR) + 1);
+ }
+
+ return len;
+}
+
+static ssize_t
+lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct lpfc_hba *phba = vport->phba;
+ int len;
+ struct lpfc_fc4_ctrl_stat *cstat;
+ u64 data1, data2, data3;
+ u64 tot, totin, totout;
+ int i;
+ char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};
+
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
+ (phba->sli_rev != LPFC_SLI_REV4))
+ return 0;
+
+ scnprintf(buf, PAGE_SIZE, "SCSI HDWQ Statistics\n");
+
+ totin = 0;
+ totout = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
+ tot = cstat->io_cmpls;
+ totin += tot;
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
+ totout += (data1 + data2 + data3);
+
+ scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
+ "IO %016llx ", i, data1, data2, data3);
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
+ tot, ((data1 + data2 + data3) - tot));
+ if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
+ goto buffer_done;
+ }
+ scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
+ "OutIO %016llx\n", totin, totout, totout - totin);
+ strlcat(buf, tmp, PAGE_SIZE);
+
+buffer_done:
+ len = strnlen(buf, PAGE_SIZE);
+
+ return len;
}
static ssize_t
@@ -128,14 +891,15 @@ lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- if (phba->cfg_enable_bg)
+ if (phba->cfg_enable_bg) {
if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
- return snprintf(buf, PAGE_SIZE, "BlockGuard Enabled\n");
+ return scnprintf(buf, PAGE_SIZE,
+ "BlockGuard Enabled\n");
else
- return snprintf(buf, PAGE_SIZE,
+ return scnprintf(buf, PAGE_SIZE,
"BlockGuard Not Supported\n");
- else
- return snprintf(buf, PAGE_SIZE,
+ } else
+ return scnprintf(buf, PAGE_SIZE,
"BlockGuard Disabled\n");
}
@@ -147,7 +911,7 @@ lpfc_bg_guard_err_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%llu\n",
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
(unsigned long long)phba->bg_guard_err_cnt);
}
@@ -159,7 +923,7 @@ lpfc_bg_apptag_err_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%llu\n",
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
(unsigned long long)phba->bg_apptag_err_cnt);
}
@@ -171,7 +935,7 @@ lpfc_bg_reftag_err_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%llu\n",
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
(unsigned long long)phba->bg_reftag_err_cnt);
}
@@ -189,7 +953,7 @@ lpfc_info_show(struct device *dev, struct device_attribute *attr,
{
struct Scsi_Host *host = class_to_shost(dev);
- return snprintf(buf, PAGE_SIZE, "%s\n",lpfc_info(host));
+ return scnprintf(buf, PAGE_SIZE, "%s\n", lpfc_info(host));
}
/**
@@ -208,7 +972,7 @@ lpfc_serialnum_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%s\n",phba->SerialNumber);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", phba->SerialNumber);
}
/**
@@ -230,7 +994,7 @@ lpfc_temp_sensor_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%d\n",phba->temp_sensor_support);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", phba->temp_sensor_support);
}
/**
@@ -249,7 +1013,7 @@ lpfc_modeldesc_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelDesc);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", phba->ModelDesc);
}
/**
@@ -268,7 +1032,7 @@ lpfc_modelname_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelName);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", phba->ModelName);
}
/**
@@ -287,26 +1051,7 @@ lpfc_programtype_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%s\n",phba->ProgramType);
-}
-
-/**
- * lpfc_mlomgmt_show - Return the Menlo Maintenance sli flag
- * @dev: class converted to a Scsi_host structure.
- * @attr: device attribute, not used.
- * @buf: on return contains the Menlo Maintenance sli flag.
- *
- * Returns: size of formatted string.
- **/
-static ssize_t
-lpfc_mlomgmt_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- (phba->sli.sli_flag & LPFC_MENLO_MAINT));
+ return scnprintf(buf, PAGE_SIZE, "%s\n", phba->ProgramType);
}
/**
@@ -325,7 +1070,7 @@ lpfc_vportnum_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%s\n",phba->Port);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", phba->Port);
}
/**
@@ -343,10 +1088,23 @@ lpfc_fwrev_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- char fwrev[32];
+ uint32_t if_type;
+ uint8_t sli_family;
+ char fwrev[FW_REV_STR_SIZE];
+ int len;
lpfc_decode_firmware_rev(phba, fwrev, 1);
- return snprintf(buf, PAGE_SIZE, "%s, sli-%d\n", fwrev, phba->sli_rev);
+ if_type = phba->sli4_hba.pc_sli4_params.if_type;
+ sli_family = phba->sli4_hba.pc_sli4_params.sli_family;
+
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ len = scnprintf(buf, PAGE_SIZE, "%s, sli-%d\n",
+ fwrev, phba->sli_rev);
+ else
+ len = scnprintf(buf, PAGE_SIZE, "%s, sli-%d:%d:%x\n",
+ fwrev, phba->sli_rev, if_type, sli_family);
+
+ return len;
}
/**
@@ -367,7 +1125,8 @@ lpfc_hdw_show(struct device *dev, struct device_attribute *attr, char *buf)
lpfc_vpd_t *vp = &phba->vpd;
lpfc_jedec_to_ascii(vp->rev.biuRev, hdw);
- return snprintf(buf, PAGE_SIZE, "%s\n", hdw);
+ return scnprintf(buf, PAGE_SIZE, "%s %08x %08x\n", hdw,
+ vp->rev.smRev, vp->rev.smFwRev);
}
/**
@@ -385,12 +1144,18 @@ lpfc_option_rom_version_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
+ char fwrev[FW_REV_STR_SIZE];
+
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ phba->OptionROMVersion);
- return snprintf(buf, PAGE_SIZE, "%s\n", phba->OptionROMVersion);
+ lpfc_decode_firmware_rev(phba, fwrev, 1);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", fwrev);
}
/**
- * lpfc_state_show - Return the link state of the port
+ * lpfc_link_state_show - Return the link state of the port
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains text describing the state of the link.
@@ -416,21 +1181,21 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr,
case LPFC_INIT_MBX_CMDS:
case LPFC_LINK_DOWN:
case LPFC_HBA_ERROR:
- if (phba->hba_flag & LINK_DISABLED)
- len += snprintf(buf + len, PAGE_SIZE-len,
+ if (test_bit(LINK_DISABLED, &phba->hba_flag))
+ len += scnprintf(buf + len, PAGE_SIZE-len,
"Link Down - User disabled\n");
else
- len += snprintf(buf + len, PAGE_SIZE-len,
+ len += scnprintf(buf + len, PAGE_SIZE-len,
"Link Down\n");
break;
case LPFC_LINK_UP:
case LPFC_CLEAR_LA:
case LPFC_HBA_READY:
- len += snprintf(buf + len, PAGE_SIZE-len, "Link Up - ");
+ len += scnprintf(buf + len, PAGE_SIZE-len, "Link Up - ");
switch (vport->port_state) {
case LPFC_LOCAL_CFG_LINK:
- len += snprintf(buf + len, PAGE_SIZE-len,
+ len += scnprintf(buf + len, PAGE_SIZE-len,
"Configuring Link\n");
break;
case LPFC_FDISC:
@@ -440,46 +1205,140 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr,
case LPFC_NS_QRY:
case LPFC_BUILD_DISC_LIST:
case LPFC_DISC_AUTH:
- len += snprintf(buf + len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"Discovery\n");
break;
case LPFC_VPORT_READY:
- len += snprintf(buf + len, PAGE_SIZE - len, "Ready\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Ready\n");
break;
case LPFC_VPORT_FAILED:
- len += snprintf(buf + len, PAGE_SIZE - len, "Failed\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Failed\n");
break;
case LPFC_VPORT_UNKNOWN:
- len += snprintf(buf + len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"Unknown\n");
break;
}
- if (phba->sli.sli_flag & LPFC_MENLO_MAINT)
- len += snprintf(buf + len, PAGE_SIZE-len,
- " Menlo Maint Mode\n");
- else if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
- if (vport->fc_flag & FC_PUBLIC_LOOP)
- len += snprintf(buf + len, PAGE_SIZE-len,
+ if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
+ if (test_bit(FC_PUBLIC_LOOP, &vport->fc_flag))
+ len += scnprintf(buf + len, PAGE_SIZE-len,
" Public Loop\n");
else
- len += snprintf(buf + len, PAGE_SIZE-len,
+ len += scnprintf(buf + len, PAGE_SIZE-len,
" Private Loop\n");
} else {
- if (vport->fc_flag & FC_FABRIC)
- len += snprintf(buf + len, PAGE_SIZE-len,
- " Fabric\n");
- else
- len += snprintf(buf + len, PAGE_SIZE-len,
+ if (test_bit(FC_FABRIC, &vport->fc_flag)) {
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ vport->port_type == LPFC_PHYSICAL_PORT &&
+ phba->sli4_hba.fawwpn_flag &
+ LPFC_FAWWPN_FABRIC)
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len,
+ " Fabric FA-PWWN\n");
+ else
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len,
+ " Fabric\n");
+ } else {
+ len += scnprintf(buf + len, PAGE_SIZE-len,
" Point-2-Point\n");
+ }
}
}
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ ((bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_6))) {
+ struct lpfc_trunk_link link = phba->trunk_link;
+
+ if (bf_get(lpfc_conf_trunk_port0, &phba->sli4_hba))
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Trunk port 0: Link %s %s\n",
+ (link.link0.state == LPFC_LINK_UP) ?
+ "Up" : "Down. ",
+ trunk_errmsg[link.link0.fault]);
+
+ if (bf_get(lpfc_conf_trunk_port1, &phba->sli4_hba))
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Trunk port 1: Link %s %s\n",
+ (link.link1.state == LPFC_LINK_UP) ?
+ "Up" : "Down. ",
+ trunk_errmsg[link.link1.fault]);
+
+ if (bf_get(lpfc_conf_trunk_port2, &phba->sli4_hba))
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Trunk port 2: Link %s %s\n",
+ (link.link2.state == LPFC_LINK_UP) ?
+ "Up" : "Down. ",
+ trunk_errmsg[link.link2.fault]);
+
+ if (bf_get(lpfc_conf_trunk_port3, &phba->sli4_hba))
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Trunk port 3: Link %s %s\n",
+ (link.link3.state == LPFC_LINK_UP) ?
+ "Up" : "Down. ",
+ trunk_errmsg[link.link3.fault]);
+
+ }
+
return len;
}
/**
+ * lpfc_sli4_protocol_show - Return the fip mode of the HBA
+ * @dev: class unused variable.
+ * @attr: device attribute, not used.
+ * @buf: on return contains the module description text.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_sli4_protocol_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ return scnprintf(buf, PAGE_SIZE, "fc\n");
+
+ if (phba->sli4_hba.lnk_info.lnk_dv == LPFC_LNK_DAT_VAL) {
+ if (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_GE)
+ return scnprintf(buf, PAGE_SIZE, "fcoe\n");
+ if (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC)
+ return scnprintf(buf, PAGE_SIZE, "fc\n");
+ }
+ return scnprintf(buf, PAGE_SIZE, "unknown\n");
+}
+
+/**
+ * lpfc_oas_supported_show - Return whether or not Optimized Access Storage
+ * (OAS) is supported.
+ * @dev: class unused variable.
+ * @attr: device attribute, not used.
+ * @buf: on return contains the module description text.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_oas_supported_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ phba->sli4_hba.pc_sli4_params.oas_supported);
+}
+
+/**
* lpfc_link_state_store - Transition the link_state on an HBA port
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
@@ -533,8 +1392,9 @@ lpfc_num_discovered_ports_show(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- return snprintf(buf, PAGE_SIZE, "%d\n",
- vport->fc_map_cnt + vport->fc_unmap_cnt);
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ atomic_read(&vport->fc_map_cnt) +
+ atomic_read(&vport->fc_unmap_cnt));
}
/**
@@ -559,7 +1419,12 @@ lpfc_issue_lip(struct Scsi_Host *shost)
LPFC_MBOXQ_t *pmboxq;
int mbxstatus = MBXERR_ERROR;
- if ((vport->fc_flag & FC_OFFLINE_MODE) ||
+ /*
+ * If the link is offline, disabled or BLOCK_MGMT_IO
+ * it doesn't make any sense to allow issue_lip
+ */
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag) ||
+ test_bit(LINK_DISABLED, &phba->hba_flag) ||
(phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO))
return -EPERM;
@@ -572,6 +1437,9 @@ lpfc_issue_lip(struct Scsi_Host *shost)
pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK;
pmboxq->u.mb.mbxOwner = OWN_HOST;
+ if (test_bit(FC_PT2PT, &vport->fc_flag))
+ clear_bit(FC_PT2PT_NO_NVME, &vport->fc_flag);
+
mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO * 2);
if ((mbxstatus == MBX_SUCCESS) &&
@@ -599,6 +1467,27 @@ lpfc_issue_lip(struct Scsi_Host *shost)
return 0;
}
+int
+lpfc_emptyq_wait(struct lpfc_hba *phba, struct list_head *q, spinlock_t *lock)
+{
+ int cnt = 0;
+
+ spin_lock_irq(lock);
+ while (!list_empty(q)) {
+ spin_unlock_irq(lock);
+ msleep(20);
+ if (cnt++ > 250) { /* 5 secs */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0466 Outstanding IO when "
+ "bringing Adapter offline\n");
+ return 0;
+ }
+ spin_lock_irq(lock);
+ }
+ spin_unlock_irq(lock);
+ return 1;
+}
+
/**
* lpfc_do_offline - Issues a mailbox command to bring the link down
* @phba: lpfc_hba pointer.
@@ -618,10 +1507,10 @@ static int
lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
{
struct completion online_compl;
+ struct lpfc_queue *qp = NULL;
struct lpfc_sli_ring *pring;
struct lpfc_sli *psli;
int status = 0;
- int cnt = 0;
int i;
int rc;
@@ -638,23 +1527,46 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
psli = &phba->sli;
+ /*
+ * If freeing the queues have already started, don't access them.
+ * Otherwise set FREE_WAIT to indicate that queues are being used
+ * to hold the freeing process until we finish.
+ */
+ spin_lock_irq(&phba->hbalock);
+ if (!(psli->sli_flag & LPFC_QUEUE_FREE_INIT)) {
+ psli->sli_flag |= LPFC_QUEUE_FREE_WAIT;
+ } else {
+ spin_unlock_irq(&phba->hbalock);
+ goto skip_wait;
+ }
+ spin_unlock_irq(&phba->hbalock);
+
/* Wait a little for things to settle down, but not
* long enough for dev loss timeout to expire.
*/
- for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
- while (pring->txcmplq_cnt) {
- msleep(10);
- if (cnt++ > 500) { /* 5 secs */
- lpfc_printf_log(phba,
- KERN_WARNING, LOG_INIT,
- "0466 Outstanding IO when "
- "bringing Adapter offline\n");
- break;
- }
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->sli3_ring[i];
+ if (!lpfc_emptyq_wait(phba, &pring->txcmplq,
+ &phba->hbalock))
+ goto out;
+ }
+ } else {
+ list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
+ pring = qp->pring;
+ if (!pring)
+ continue;
+ if (!lpfc_emptyq_wait(phba, &pring->txcmplq,
+ &pring->ring_lock))
+ goto out;
}
}
+out:
+ spin_lock_irq(&phba->hbalock);
+ psli->sli_flag &= ~LPFC_QUEUE_FREE_WAIT;
+ spin_unlock_irq(&phba->hbalock);
+skip_wait:
init_completion(&online_compl);
rc = lpfc_workq_post_event(phba, &status, &online_compl, type);
if (rc == 0)
@@ -669,6 +1581,83 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
}
/**
+ * lpfc_reset_pci_bus - resets PCI bridge controller's secondary bus of an HBA
+ * @phba: lpfc_hba pointer.
+ *
+ * Description:
+ * Issues a PCI secondary bus reset for the phba->pcidev.
+ *
+ * Notes:
+ * First walks the bus_list to ensure only PCI devices with Emulex
+ * vendor id, device ids that support hot reset, only one occurrence
+ * of function 0, and all ports on the bus are in offline mode to ensure the
+ * hot reset only affects one valid HBA.
+ *
+ * Returns:
+ * -ENOTSUPP, cfg_enable_hba_reset must be of value 2
+ * -ENODEV, NULL ptr to pcidev
+ * -EBADSLT, detected invalid device
+ * -EBUSY, port is not in offline state
+ * 0, successful
+ */
+static int
+lpfc_reset_pci_bus(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev = phba->pcidev;
+ struct Scsi_Host *shost = NULL;
+ struct lpfc_hba *phba_other = NULL;
+ struct pci_dev *ptr = NULL;
+ int res;
+
+ if (phba->cfg_enable_hba_reset != 2)
+ return -ENOTSUPP;
+
+ if (!pdev) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "8345 pdev NULL!\n");
+ return -ENODEV;
+ }
+
+ res = lpfc_check_pci_resettable(phba);
+ if (res)
+ return res;
+
+ /* Walk the list of devices on the pci_dev's bus */
+ list_for_each_entry(ptr, &pdev->bus->devices, bus_list) {
+ /* Check port is offline */
+ shost = pci_get_drvdata(ptr);
+ if (shost) {
+ phba_other =
+ ((struct lpfc_vport *)shost->hostdata)->phba;
+ if (!test_bit(FC_OFFLINE_MODE,
+ &phba_other->pport->fc_flag)) {
+ lpfc_printf_log(phba_other, KERN_INFO, LOG_INIT,
+ "8349 WWPN = 0x%02x%02x%02x%02x"
+ "%02x%02x%02x%02x is not "
+ "offline!\n",
+ phba_other->wwpn[0],
+ phba_other->wwpn[1],
+ phba_other->wwpn[2],
+ phba_other->wwpn[3],
+ phba_other->wwpn[4],
+ phba_other->wwpn[5],
+ phba_other->wwpn[6],
+ phba_other->wwpn[7]);
+ return -EBUSY;
+ }
+ }
+ }
+
+ /* Issue PCI bus reset */
+ res = pci_reset_bus(pdev);
+ if (res) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "8350 PCI reset bus failed: %d\n", res);
+ }
+
+ return res;
+}
+
+/**
* lpfc_selective_reset - Offline then onlines the port
* @phba: lpfc_hba pointer.
*
@@ -693,12 +1682,14 @@ lpfc_selective_reset(struct lpfc_hba *phba)
int rc;
if (!phba->cfg_enable_hba_reset)
- return -EIO;
+ return -EACCES;
- status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
+ if (!test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag)) {
+ status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
- if (status != 0)
- return status;
+ if (status != 0)
+ return status;
+ }
init_completion(&online_compl);
rc = lpfc_workq_post_event(phba, &status, &online_compl,
@@ -742,9 +1733,11 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
-
int status = -EINVAL;
+ if (!phba->cfg_enable_hba_reset)
+ return -EACCES;
+
if (strncmp(buf, "selective", sizeof("selective") - 1) == 0)
status = phba->lpfc_selective_reset(phba);
@@ -755,8 +1748,68 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr,
}
/**
+ * lpfc_sli4_pdev_status_reg_wait - Wait for pdev status register for readyness
+ * @phba: lpfc_hba pointer.
+ *
+ * Description:
+ * SLI4 interface type-2 device to wait on the sliport status register for
+ * the readyness after performing a firmware reset.
+ *
+ * Returns:
+ * zero for success, -EPERM when port does not have privilege to perform the
+ * reset, -EIO when port timeout from recovering from the reset.
+ *
+ * Note:
+ * As the caller will interpret the return code by value, be careful in making
+ * change or addition to return codes.
+ **/
+int
+lpfc_sli4_pdev_status_reg_wait(struct lpfc_hba *phba)
+{
+ struct lpfc_register portstat_reg = {0};
+ int i;
+
+ msleep(100);
+ if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
+ &portstat_reg.word0))
+ return -EIO;
+
+ /* verify if privileged for the request operation */
+ if (!bf_get(lpfc_sliport_status_rn, &portstat_reg) &&
+ !bf_get(lpfc_sliport_status_err, &portstat_reg))
+ return -EPERM;
+
+ /* There is no point to wait if the port is in an unrecoverable
+ * state.
+ */
+ if (lpfc_sli4_unrecoverable_port(&portstat_reg))
+ return -EIO;
+
+ /* wait for the SLI port firmware ready after firmware reset */
+ for (i = 0; i < LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT; i++) {
+ msleep(10);
+ if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
+ &portstat_reg.word0))
+ continue;
+ if (!bf_get(lpfc_sliport_status_err, &portstat_reg))
+ continue;
+ if (!bf_get(lpfc_sliport_status_rn, &portstat_reg))
+ continue;
+ if (!bf_get(lpfc_sliport_status_rdy, &portstat_reg))
+ continue;
+ break;
+ }
+
+ if (i < LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT)
+ return 0;
+ else
+ return -EIO;
+}
+
+/**
* lpfc_sli4_pdev_reg_request - Request physical dev to perform a register acc
* @phba: lpfc_hba pointer.
+ * @opcode: The sli4 config command opcode.
*
* Description:
* Request SLI4 interface type-2 device to perform a physical register set
@@ -769,25 +1822,43 @@ static ssize_t
lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode)
{
struct completion online_compl;
+ struct pci_dev *pdev = phba->pcidev;
+ unsigned long before_fc_flag;
+ uint32_t sriov_nr_virtfn;
uint32_t reg_val;
- int status = 0;
- int rc;
+ int status = 0, rc = 0;
+ int job_posted = 1, sriov_err;
if (!phba->cfg_enable_hba_reset)
- return -EIO;
+ return -EACCES;
if ((phba->sli_rev < LPFC_SLI_REV4) ||
- (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2))
return -EPERM;
- status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
+ /* Keep state if we need to restore back */
+ before_fc_flag = phba->pport->fc_flag;
+ sriov_nr_virtfn = phba->cfg_sriov_nr_virtfn;
- if (status != 0)
- return status;
+ if (opcode == LPFC_FW_DUMP) {
+ init_completion(&online_compl);
+ phba->fw_dump_cmpl = &online_compl;
+ } else {
+ /* Disable SR-IOV virtual functions if enabled */
+ if (phba->cfg_sriov_nr_virtfn) {
+ pci_disable_sriov(pdev);
+ phba->cfg_sriov_nr_virtfn = 0;
+ }
- /* wait for the device to be quiesced before firmware reset */
- msleep(100);
+ status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
+
+ if (status != 0)
+ return status;
+
+ /* wait for the device to be quiesced before firmware reset */
+ msleep(100);
+ }
reg_val = readl(phba->sli4_hba.conf_regs_memmap_p +
LPFC_CTL_PDEV_CTL_OFFSET);
@@ -805,20 +1876,68 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode)
readl(phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET);
/* delay driver action following IF_TYPE_2 reset */
- msleep(100);
+ rc = lpfc_sli4_pdev_status_reg_wait(phba);
+
+ if (rc == -EPERM) {
+ /* no privilege for reset */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3150 No privilege to perform the requested "
+ "access: x%x\n", reg_val);
+ } else if (rc == -EIO) {
+ /* reset failed, there is nothing more we can do */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3153 Fail to perform the requested "
+ "access: x%x\n", reg_val);
+ if (phba->fw_dump_cmpl)
+ phba->fw_dump_cmpl = NULL;
+ return rc;
+ }
- init_completion(&online_compl);
- rc = lpfc_workq_post_event(phba, &status, &online_compl,
- LPFC_EVT_ONLINE);
- if (rc == 0)
- return -ENOMEM;
+ /* keep the original port state */
+ if (test_bit(FC_OFFLINE_MODE, &before_fc_flag)) {
+ if (phba->fw_dump_cmpl)
+ phba->fw_dump_cmpl = NULL;
+ goto out;
+ }
- wait_for_completion(&online_compl);
+ /* Firmware dump will trigger an HA_ERATT event, and
+ * lpfc_handle_eratt_s4 routine already handles bringing the port back
+ * online.
+ */
+ if (opcode == LPFC_FW_DUMP) {
+ wait_for_completion(phba->fw_dump_cmpl);
+ } else {
+ init_completion(&online_compl);
+ job_posted = lpfc_workq_post_event(phba, &status, &online_compl,
+ LPFC_EVT_ONLINE);
+ if (!job_posted)
+ goto out;
- if (status != 0)
- return -EIO;
+ wait_for_completion(&online_compl);
+ }
+out:
+ /* in any case, restore the virtual functions enabled as before */
+ if (sriov_nr_virtfn) {
+ /* If fw_dump was performed, first disable to clean up */
+ if (opcode == LPFC_FW_DUMP) {
+ pci_disable_sriov(pdev);
+ phba->cfg_sriov_nr_virtfn = 0;
+ }
- return 0;
+ sriov_err =
+ lpfc_sli_probe_sriov_nr_virtfn(phba, sriov_nr_virtfn);
+ if (!sriov_err)
+ phba->cfg_sriov_nr_virtfn = sriov_nr_virtfn;
+ }
+
+ /* return proper error code */
+ if (!rc) {
+ if (!job_posted)
+ rc = -ENOMEM;
+ else if (status)
+ rc = -EIO;
+ }
+ return rc;
}
/**
@@ -837,7 +1956,177 @@ lpfc_nport_evt_cnt_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt);
+}
+
+static int
+lpfc_set_trunking(struct lpfc_hba *phba, char *buff_out)
+{
+ LPFC_MBOXQ_t *mbox = NULL;
+ u32 payload_len;
+ unsigned long val = 0;
+ char *pval = NULL;
+ int rc = 0;
+
+ if (!strncmp("enable", buff_out,
+ strlen("enable"))) {
+ pval = buff_out + strlen("enable") + 1;
+ rc = kstrtoul(pval, 0, &val);
+ if (rc)
+ return rc; /* Invalid number */
+ } else if (!strncmp("disable", buff_out,
+ strlen("disable"))) {
+ val = 0;
+ } else {
+ return -EINVAL; /* Invalid command */
+ }
+
+ switch (val) {
+ case 0:
+ val = 0x0; /* Disable */
+ break;
+ case 2:
+ val = 0x1; /* Enable two port trunk */
+ break;
+ case 4:
+ val = 0x2; /* Enable four port trunk */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "0070 Set trunk mode with val %ld ", val);
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ payload_len = sizeof(struct lpfc_mbx_set_trunk_mode) -
+ sizeof(struct lpfc_sli4_cfg_mhdr);
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_FC_SET_TRUNK_MODE,
+ payload_len, LPFC_SLI4_MBX_EMBED);
+
+ bf_set(lpfc_mbx_set_trunk_mode,
+ &mbox->u.mqe.un.set_trunk_mode,
+ val);
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ if (rc)
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "0071 Set trunk mode failed with status: %d",
+ rc);
+ mempool_free(mbox, phba->mbox_mem_pool);
+
+ return 0;
+}
+
+static ssize_t
+lpfc_xcvr_data_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int rc;
+ int len = 0;
+ struct lpfc_rdp_context *rdp_context;
+ u16 temperature;
+ u16 rx_power;
+ u16 tx_bias;
+ u16 tx_power;
+ u16 vcc;
+ char chbuf[128];
+ u16 wavelength = 0;
+ struct sff_trasnceiver_codes_byte7 *trasn_code_byte7;
+
+ /* Get transceiver information */
+ rdp_context = kmalloc(sizeof(*rdp_context), GFP_KERNEL);
+ if (!rdp_context) {
+ len = scnprintf(buf, PAGE_SIZE - len,
+ "SPF info NA: alloc failure\n");
+ return len;
+ }
+
+ rc = lpfc_get_sfp_info_wait(phba, rdp_context);
+ if (rc) {
+ len = scnprintf(buf, PAGE_SIZE - len, "SFP info NA:\n");
+ goto out_free_rdp;
+ }
+
+ strscpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_NAME], 16);
+
+ len = scnprintf(buf, PAGE_SIZE - len, "VendorName:\t%s\n", chbuf);
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "VendorOUI:\t%02x-%02x-%02x\n",
+ (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI],
+ (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI + 1],
+ (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI + 2]);
+ strscpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_PN], 16);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "VendorPN:\t%s\n", chbuf);
+ strscpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_SN], 16);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "VendorSN:\t%s\n", chbuf);
+ strscpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_REV], 4);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "VendorRev:\t%s\n", chbuf);
+ strscpy(chbuf, &rdp_context->page_a0[SSF_DATE_CODE], 8);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "DateCode:\t%s\n", chbuf);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "Identifier:\t%xh\n",
+ (uint8_t)rdp_context->page_a0[SSF_IDENTIFIER]);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "ExtIdentifier:\t%xh\n",
+ (uint8_t)rdp_context->page_a0[SSF_EXT_IDENTIFIER]);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "Connector:\t%xh\n",
+ (uint8_t)rdp_context->page_a0[SSF_CONNECTOR]);
+ wavelength = (rdp_context->page_a0[SSF_WAVELENGTH_B1] << 8) |
+ rdp_context->page_a0[SSF_WAVELENGTH_B0];
+
+ len += scnprintf(buf + len, PAGE_SIZE - len, "Wavelength:\t%d nm\n",
+ wavelength);
+ trasn_code_byte7 = (struct sff_trasnceiver_codes_byte7 *)
+ &rdp_context->page_a0[SSF_TRANSCEIVER_CODE_B7];
+
+ len += scnprintf(buf + len, PAGE_SIZE - len, "Speeds: \t");
+ if (*(uint8_t *)trasn_code_byte7 == 0) {
+ len += scnprintf(buf + len, PAGE_SIZE - len, "Unknown\n");
+ } else {
+ if (trasn_code_byte7->fc_sp_100MB)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "1 ");
+ if (trasn_code_byte7->fc_sp_200mb)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "2 ");
+ if (trasn_code_byte7->fc_sp_400MB)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "4 ");
+ if (trasn_code_byte7->fc_sp_800MB)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "8 ");
+ if (trasn_code_byte7->fc_sp_1600MB)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "16 ");
+ if (trasn_code_byte7->fc_sp_3200MB)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "32 ");
+ if (trasn_code_byte7->speed_chk_ecc)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "64 ");
+ len += scnprintf(buf + len, PAGE_SIZE - len, "GB\n");
+ }
+ temperature = (rdp_context->page_a2[SFF_TEMPERATURE_B1] << 8 |
+ rdp_context->page_a2[SFF_TEMPERATURE_B0]);
+ vcc = (rdp_context->page_a2[SFF_VCC_B1] << 8 |
+ rdp_context->page_a2[SFF_VCC_B0]);
+ tx_power = (rdp_context->page_a2[SFF_TXPOWER_B1] << 8 |
+ rdp_context->page_a2[SFF_TXPOWER_B0]);
+ tx_bias = (rdp_context->page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 |
+ rdp_context->page_a2[SFF_TX_BIAS_CURRENT_B0]);
+ rx_power = (rdp_context->page_a2[SFF_RXPOWER_B1] << 8 |
+ rdp_context->page_a2[SFF_RXPOWER_B0]);
+
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Temperature:\tx%04x C\n", temperature);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "Vcc:\t\tx%04x V\n", vcc);
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "TxBiasCurrent:\tx%04x mA\n", tx_bias);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "TxPower:\tx%04x mW\n",
+ tx_power);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "RxPower:\tx%04x mW\n",
+ rx_power);
+out_free_rdp:
+ kfree(rdp_context);
+ return len;
}
/**
@@ -866,7 +2155,7 @@ lpfc_board_mode_show(struct device *dev, struct device_attribute *attr,
else
state = "online";
- return snprintf(buf, PAGE_SIZE, "%s\n", state);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", state);
}
/**
@@ -890,29 +2179,40 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
struct completion online_compl;
- int status=0;
+ char *board_mode_str = NULL;
+ int status = 0;
int rc;
- if (!phba->cfg_enable_hba_reset)
- return -EACCES;
+ if (!phba->cfg_enable_hba_reset) {
+ status = -EACCES;
+ goto board_mode_out;
+ }
+
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "3050 lpfc_board_mode set to %s\n", buf);
+
init_completion(&online_compl);
if(strncmp(buf, "online", sizeof("online") - 1) == 0) {
rc = lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_ONLINE);
- if (rc == 0)
- return -ENOMEM;
+ if (rc == 0) {
+ status = -ENOMEM;
+ goto board_mode_out;
+ }
wait_for_completion(&online_compl);
+ if (status)
+ status = -EIO;
} else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0)
if (phba->sli_rev == LPFC_SLI_REV4)
- return -EINVAL;
+ status = -EINVAL;
else
status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
else if (strncmp(buf, "error", sizeof("error") - 1) == 0)
if (phba->sli_rev == LPFC_SLI_REV4)
- return -EINVAL;
+ status = -EINVAL;
else
status = lpfc_do_offline(phba, LPFC_EVT_KILL);
else if (strncmp(buf, "dump", sizeof("dump") - 1) == 0)
@@ -921,13 +2221,29 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_RESET);
else if (strncmp(buf, "dv_reset", sizeof("dv_reset") - 1) == 0)
status = lpfc_sli4_pdev_reg_request(phba, LPFC_DV_RESET);
+ else if (strncmp(buf, "pci_bus_reset", sizeof("pci_bus_reset") - 1)
+ == 0)
+ status = lpfc_reset_pci_bus(phba);
+ else if (strncmp(buf, "heartbeat", sizeof("heartbeat") - 1) == 0)
+ lpfc_issue_hb_tmo(phba);
+ else if (strncmp(buf, "trunk", sizeof("trunk") - 1) == 0)
+ status = lpfc_set_trunking(phba, (char *)buf + sizeof("trunk"));
else
- return -EINVAL;
+ status = -EINVAL;
+board_mode_out:
if (!status)
return strlen(buf);
- else
- return -EIO;
+ else {
+ board_mode_str = strchr(buf, '\n');
+ if (board_mode_str)
+ *board_mode_str = '\0';
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "3097 Failed \"%s\", status(%d), "
+ "fc_flag(x%lx)\n",
+ buf, status, phba->pport->fc_flag);
+ return status;
+ }
}
/**
@@ -954,11 +2270,12 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
uint32_t *mrpi, uint32_t *arpi,
uint32_t *mvpi, uint32_t *avpi)
{
- struct lpfc_mbx_read_config *rd_config;
LPFC_MBOXQ_t *pmboxq;
MAILBOX_t *pmb;
int rc = 0;
- uint32_t max_vpi;
+ struct lpfc_sli4_hba *sli4_hba;
+ struct lpfc_max_cfg_param *max_cfg_param;
+ u16 rsrc_ext_cnt, rsrc_ext_size, max_vpi;
/*
* prevent udev from issuing mailbox commands until the port is
@@ -980,9 +2297,9 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
pmb = &pmboxq->u.mb;
pmb->mbxCommand = MBX_READ_CONFIG;
pmb->mbxOwner = OWN_HOST;
- pmboxq->context1 = NULL;
+ pmboxq->ctx_buf = NULL;
- if (phba->pport->fc_flag & FC_OFFLINE_MODE)
+ if (test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
rc = MBX_NOT_FINISHED;
else
rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -994,28 +2311,65 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
}
if (phba->sli_rev == LPFC_SLI_REV4) {
- rd_config = &pmboxq->u.mqe.un.rd_config;
- if (mrpi)
- *mrpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config);
- if (arpi)
- *arpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config) -
- phba->sli4_hba.max_cfg_param.rpi_used;
- if (mxri)
- *mxri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config);
- if (axri)
- *axri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config) -
- phba->sli4_hba.max_cfg_param.xri_used;
+ sli4_hba = &phba->sli4_hba;
+ max_cfg_param = &sli4_hba->max_cfg_param;
+
+ /* Normally, extents are not used */
+ if (!phba->sli4_hba.extents_in_use) {
+ if (mrpi)
+ *mrpi = max_cfg_param->max_rpi;
+ if (mxri)
+ *mxri = max_cfg_param->max_xri;
+ if (mvpi) {
+ max_vpi = max_cfg_param->max_vpi;
+
+ /* Limit the max we support */
+ if (max_vpi > LPFC_MAX_VPI)
+ max_vpi = LPFC_MAX_VPI;
+ *mvpi = max_vpi;
+ }
+ } else { /* Extents in use */
+ if (mrpi) {
+ if (lpfc_sli4_get_avail_extnt_rsrc(phba,
+ LPFC_RSC_TYPE_FCOE_RPI,
+ &rsrc_ext_cnt,
+ &rsrc_ext_size)) {
+ rc = 0;
+ goto free_pmboxq;
+ }
+
+ *mrpi = rsrc_ext_cnt * rsrc_ext_size;
+ }
- /* Account for differences with SLI-3. Get vpi count from
- * mailbox data and subtract one for max vpi value.
- */
- max_vpi = (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) > 0) ?
- (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) - 1) : 0;
+ if (mxri) {
+ if (lpfc_sli4_get_avail_extnt_rsrc(phba,
+ LPFC_RSC_TYPE_FCOE_XRI,
+ &rsrc_ext_cnt,
+ &rsrc_ext_size)) {
+ rc = 0;
+ goto free_pmboxq;
+ }
- if (mvpi)
- *mvpi = max_vpi;
- if (avpi)
- *avpi = max_vpi - phba->sli4_hba.max_cfg_param.vpi_used;
+ *mxri = rsrc_ext_cnt * rsrc_ext_size;
+ }
+
+ if (mvpi) {
+ if (lpfc_sli4_get_avail_extnt_rsrc(phba,
+ LPFC_RSC_TYPE_FCOE_VPI,
+ &rsrc_ext_cnt,
+ &rsrc_ext_size)) {
+ rc = 0;
+ goto free_pmboxq;
+ }
+
+ max_vpi = rsrc_ext_cnt * rsrc_ext_size;
+
+ /* Limit the max we support */
+ if (max_vpi > LPFC_MAX_VPI)
+ max_vpi = LPFC_MAX_VPI;
+ *mvpi = max_vpi;
+ }
+ }
} else {
if (mrpi)
*mrpi = pmb->un.varRdConfig.max_rpi;
@@ -1027,12 +2381,21 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
*axri = pmb->un.varRdConfig.avail_xri;
if (mvpi)
*mvpi = pmb->un.varRdConfig.max_vpi;
- if (avpi)
- *avpi = pmb->un.varRdConfig.avail_vpi;
+ if (avpi) {
+ /* avail_vpi is only valid if link is up and ready */
+ if (phba->link_state == LPFC_HBA_READY)
+ *avpi = pmb->un.varRdConfig.avail_vpi;
+ else
+ *avpi = pmb->un.varRdConfig.max_vpi;
+ }
}
+ /* Success */
+ rc = 1;
+
+free_pmboxq:
mempool_free(pmboxq, phba->mbox_mem_pool);
- return 1;
+ return rc;
}
/**
@@ -1059,8 +2422,8 @@ lpfc_max_rpi_show(struct device *dev, struct device_attribute *attr,
uint32_t cnt;
if (lpfc_get_hba_info(phba, NULL, NULL, &cnt, NULL, NULL, NULL))
- return snprintf(buf, PAGE_SIZE, "%d\n", cnt);
- return snprintf(buf, PAGE_SIZE, "Unknown\n");
+ return scnprintf(buf, PAGE_SIZE, "%d\n", cnt);
+ return scnprintf(buf, PAGE_SIZE, "Unknown\n");
}
/**
@@ -1084,11 +2447,20 @@ lpfc_used_rpi_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- uint32_t cnt, acnt;
+ struct lpfc_sli4_hba *sli4_hba;
+ struct lpfc_max_cfg_param *max_cfg_param;
+ u32 cnt = 0, acnt = 0;
- if (lpfc_get_hba_info(phba, NULL, NULL, &cnt, &acnt, NULL, NULL))
- return snprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
- return snprintf(buf, PAGE_SIZE, "Unknown\n");
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ sli4_hba = &phba->sli4_hba;
+ max_cfg_param = &sli4_hba->max_cfg_param;
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ max_cfg_param->rpi_used);
+ } else {
+ if (lpfc_get_hba_info(phba, NULL, NULL, &cnt, &acnt, NULL, NULL))
+ return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
+ }
+ return scnprintf(buf, PAGE_SIZE, "Unknown\n");
}
/**
@@ -1115,8 +2487,8 @@ lpfc_max_xri_show(struct device *dev, struct device_attribute *attr,
uint32_t cnt;
if (lpfc_get_hba_info(phba, &cnt, NULL, NULL, NULL, NULL, NULL))
- return snprintf(buf, PAGE_SIZE, "%d\n", cnt);
- return snprintf(buf, PAGE_SIZE, "Unknown\n");
+ return scnprintf(buf, PAGE_SIZE, "%d\n", cnt);
+ return scnprintf(buf, PAGE_SIZE, "Unknown\n");
}
/**
@@ -1140,11 +2512,20 @@ lpfc_used_xri_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- uint32_t cnt, acnt;
+ struct lpfc_sli4_hba *sli4_hba;
+ struct lpfc_max_cfg_param *max_cfg_param;
+ u32 cnt = 0, acnt = 0;
- if (lpfc_get_hba_info(phba, &cnt, &acnt, NULL, NULL, NULL, NULL))
- return snprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
- return snprintf(buf, PAGE_SIZE, "Unknown\n");
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ sli4_hba = &phba->sli4_hba;
+ max_cfg_param = &sli4_hba->max_cfg_param;
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ max_cfg_param->xri_used);
+ } else {
+ if (lpfc_get_hba_info(phba, &cnt, &acnt, NULL, NULL, NULL, NULL))
+ return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
+ }
+ return scnprintf(buf, PAGE_SIZE, "Unknown\n");
}
/**
@@ -1171,8 +2552,8 @@ lpfc_max_vpi_show(struct device *dev, struct device_attribute *attr,
uint32_t cnt;
if (lpfc_get_hba_info(phba, NULL, NULL, NULL, NULL, &cnt, NULL))
- return snprintf(buf, PAGE_SIZE, "%d\n", cnt);
- return snprintf(buf, PAGE_SIZE, "Unknown\n");
+ return scnprintf(buf, PAGE_SIZE, "%d\n", cnt);
+ return scnprintf(buf, PAGE_SIZE, "Unknown\n");
}
/**
@@ -1196,11 +2577,20 @@ lpfc_used_vpi_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- uint32_t cnt, acnt;
+ struct lpfc_sli4_hba *sli4_hba;
+ struct lpfc_max_cfg_param *max_cfg_param;
+ u32 cnt = 0, acnt = 0;
- if (lpfc_get_hba_info(phba, NULL, NULL, NULL, NULL, &cnt, &acnt))
- return snprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
- return snprintf(buf, PAGE_SIZE, "Unknown\n");
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ sli4_hba = &phba->sli4_hba;
+ max_cfg_param = &sli4_hba->max_cfg_param;
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ max_cfg_param->vpi_used);
+ } else {
+ if (lpfc_get_hba_info(phba, NULL, NULL, NULL, NULL, &cnt, &acnt))
+ return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt));
+ }
+ return scnprintf(buf, PAGE_SIZE, "Unknown\n");
}
/**
@@ -1225,10 +2615,10 @@ lpfc_npiv_info_show(struct device *dev, struct device_attribute *attr,
struct lpfc_hba *phba = vport->phba;
if (!(phba->max_vpi))
- return snprintf(buf, PAGE_SIZE, "NPIV Not Supported\n");
+ return scnprintf(buf, PAGE_SIZE, "NPIV Not Supported\n");
if (vport->port_type == LPFC_PHYSICAL_PORT)
- return snprintf(buf, PAGE_SIZE, "NPIV Physical\n");
- return snprintf(buf, PAGE_SIZE, "NPIV Virtual (VPI %d)\n", vport->vpi);
+ return scnprintf(buf, PAGE_SIZE, "NPIV Physical\n");
+ return scnprintf(buf, PAGE_SIZE, "NPIV Virtual (VPI %d)\n", vport->vpi);
}
/**
@@ -1250,7 +2640,7 @@ lpfc_poll_show(struct device *dev, struct device_attribute *attr,
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- return snprintf(buf, PAGE_SIZE, "%#x\n", phba->cfg_poll);
+ return scnprintf(buf, PAGE_SIZE, "%#x\n", phba->cfg_poll);
}
/**
@@ -1290,6 +2680,10 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
if (phba->sli_rev == LPFC_SLI_REV4)
val = 0;
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "3051 lpfc_poll changed from %d to %d\n",
+ phba->cfg_poll, val);
+
spin_lock_irq(&phba->hbalock);
old_val = phba->cfg_poll;
@@ -1316,7 +2710,7 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
(old_val & DISABLE_FCP_RING_INT))
{
spin_unlock_irq(&phba->hbalock);
- del_timer(&phba->fcp_poll_timer);
+ timer_delete(&phba->fcp_poll_timer);
spin_lock_irq(&phba->hbalock);
if (lpfc_readl(phba->HCregaddr, &creg_val)) {
spin_unlock_irq(&phba->hbalock);
@@ -1335,66 +2729,6 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
}
/**
- * lpfc_fips_level_show - Return the current FIPS level for the HBA
- * @dev: class unused variable.
- * @attr: device attribute, not used.
- * @buf: on return contains the module description text.
- *
- * Returns: size of formatted string.
- **/
-static ssize_t
-lpfc_fips_level_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
-
- return snprintf(buf, PAGE_SIZE, "%d\n", phba->fips_level);
-}
-
-/**
- * lpfc_fips_rev_show - Return the FIPS Spec revision for the HBA
- * @dev: class unused variable.
- * @attr: device attribute, not used.
- * @buf: on return contains the module description text.
- *
- * Returns: size of formatted string.
- **/
-static ssize_t
-lpfc_fips_rev_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
-
- return snprintf(buf, PAGE_SIZE, "%d\n", phba->fips_spec_rev);
-}
-
-/**
- * lpfc_dss_show - Return the current state of dss and the configured state
- * @dev: class converted to a Scsi_host structure.
- * @attr: device attribute, not used.
- * @buf: on return contains the formatted text.
- *
- * Returns: size of formatted string.
- **/
-static ssize_t
-lpfc_dss_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
-
- return snprintf(buf, PAGE_SIZE, "%s - %sOperational\n",
- (phba->cfg_enable_dss) ? "Enabled" : "Disabled",
- (phba->sli3_options & LPFC_SLI3_DSS_ENABLED) ?
- "" : "Not ");
-}
-
-/**
* lpfc_sriov_hw_max_virtfn_show - Return maximum number of virtual functions
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
@@ -1414,83 +2748,43 @@ lpfc_sriov_hw_max_virtfn_show(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- struct pci_dev *pdev = phba->pcidev;
- union lpfc_sli4_cfg_shdr *shdr;
- uint32_t shdr_status, shdr_add_status;
- LPFC_MBOXQ_t *mboxq;
- struct lpfc_mbx_get_prof_cfg *get_prof_cfg;
- struct lpfc_rsrc_desc_pcie *desc;
- uint32_t max_nr_virtfn;
- uint32_t desc_count;
- int length, rc, i;
-
- if ((phba->sli_rev < LPFC_SLI_REV4) ||
- (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
- LPFC_SLI_INTF_IF_TYPE_2))
- return -EPERM;
-
- if (!pdev->is_physfn)
- return snprintf(buf, PAGE_SIZE, "%d\n", 0);
+ uint16_t max_nr_virtfn;
- mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mboxq)
- return -ENOMEM;
-
- /* get the maximum number of virtfn support by physfn */
- length = (sizeof(struct lpfc_mbx_get_prof_cfg) -
- sizeof(struct lpfc_sli4_cfg_mhdr));
- lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
- LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG,
- length, LPFC_SLI4_MBX_EMBED);
- shdr = (union lpfc_sli4_cfg_shdr *)
- &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
- bf_set(lpfc_mbox_hdr_pf_num, &shdr->request,
- phba->sli4_hba.iov.pf_number + 1);
-
- get_prof_cfg = &mboxq->u.mqe.un.get_prof_cfg;
- bf_set(lpfc_mbx_get_prof_cfg_prof_tp, &get_prof_cfg->u.request,
- LPFC_CFG_TYPE_CURRENT_ACTIVE);
-
- rc = lpfc_sli_issue_mbox_wait(phba, mboxq,
- lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG));
-
- if (rc != MBX_TIMEOUT) {
- /* check return status */
- shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
- shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
- &shdr->response);
- if (shdr_status || shdr_add_status || rc)
- goto error_out;
-
- } else
- goto error_out;
-
- desc_count = get_prof_cfg->u.response.prof_cfg.rsrc_desc_count;
-
- for (i = 0; i < LPFC_RSRC_DESC_MAX_NUM; i++) {
- desc = (struct lpfc_rsrc_desc_pcie *)
- &get_prof_cfg->u.response.prof_cfg.desc[i];
- if (LPFC_RSRC_DESC_TYPE_PCIE ==
- bf_get(lpfc_rsrc_desc_pcie_type, desc)) {
- max_nr_virtfn = bf_get(lpfc_rsrc_desc_pcie_nr_virtfn,
- desc);
- break;
- }
- }
+ max_nr_virtfn = lpfc_sli_sriov_nr_virtfn_get(phba);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn);
+}
- if (i < LPFC_RSRC_DESC_MAX_NUM) {
- if (rc != MBX_TIMEOUT)
- mempool_free(mboxq, phba->mbox_mem_pool);
- return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn);
+/**
+ * lpfc_enable_bbcr_set: Sets an attribute value.
+ * @phba: pointer to the adapter structure.
+ * @val: integer attribute value.
+ *
+ * Description:
+ * Validates the min and max values then sets the
+ * adapter config field if in the valid range. prints error message
+ * and does not set the parameter if invalid.
+ *
+ * Returns:
+ * zero on success
+ * -EINVAL if val is invalid
+ */
+static ssize_t
+lpfc_enable_bbcr_set(struct lpfc_hba *phba, uint val)
+{
+ if (lpfc_rangecheck(val, 0, 1) && phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3068 lpfc_enable_bbcr changed from %d to "
+ "%d\n", phba->cfg_enable_bbcr, val);
+ phba->cfg_enable_bbcr = val;
+ return 0;
}
-
-error_out:
- if (rc != MBX_TIMEOUT)
- mempool_free(mboxq, phba->mbox_mem_pool);
- return -EIO;
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0451 lpfc_enable_bbcr cannot set to %d, range is 0, "
+ "1\n", val);
+ return -EINVAL;
}
-/**
+/*
* lpfc_param_show - Return a cfg attribute value in decimal
*
* Description:
@@ -1512,13 +2806,11 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
struct Scsi_Host *shost = class_to_shost(dev);\
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\
struct lpfc_hba *phba = vport->phba;\
- uint val = 0;\
- val = phba->cfg_##attr;\
- return snprintf(buf, PAGE_SIZE, "%d\n",\
+ return scnprintf(buf, PAGE_SIZE, "%d\n",\
phba->cfg_##attr);\
}
-/**
+/*
* lpfc_param_hex_show - Return a cfg attribute value in hex
*
* Description:
@@ -1542,11 +2834,11 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
struct lpfc_hba *phba = vport->phba;\
uint val = 0;\
val = phba->cfg_##attr;\
- return snprintf(buf, PAGE_SIZE, "%#x\n",\
+ return scnprintf(buf, PAGE_SIZE, "%#x\n",\
phba->cfg_##attr);\
}
-/**
+/*
* lpfc_param_init - Initializes a cfg attribute
*
* Description:
@@ -1555,7 +2847,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
* takes a default argument, a minimum and maximum argument.
*
* lpfc_##attr##_init: Initializes an attribute.
- * @phba: pointer the the adapter structure.
+ * @phba: pointer to the adapter structure.
* @val: integer attribute value.
*
* Validates the min and max values then sets the adapter config field
@@ -1569,7 +2861,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
static int \
lpfc_##attr##_init(struct lpfc_hba *phba, uint val) \
{ \
- if (val >= minval && val <= maxval) {\
+ if (lpfc_rangecheck(val, minval, maxval)) {\
phba->cfg_##attr = val;\
return 0;\
}\
@@ -1580,7 +2872,7 @@ lpfc_##attr##_init(struct lpfc_hba *phba, uint val) \
return -EINVAL;\
}
-/**
+/*
* lpfc_param_set - Set a cfg attribute value
*
* Description:
@@ -1588,7 +2880,7 @@ lpfc_##attr##_init(struct lpfc_hba *phba, uint val) \
* into a function with the name lpfc_hba_queue_depth_set
*
* lpfc_##attr##_set: Sets an attribute value.
- * @phba: pointer the the adapter structure.
+ * @phba: pointer to the adapter structure.
* @val: integer attribute value.
*
* Description:
@@ -1604,7 +2896,10 @@ lpfc_##attr##_init(struct lpfc_hba *phba, uint val) \
static int \
lpfc_##attr##_set(struct lpfc_hba *phba, uint val) \
{ \
- if (val >= minval && val <= maxval) {\
+ if (lpfc_rangecheck(val, minval, maxval)) {\
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, \
+ "3052 lpfc_" #attr " changed from %d to %d\n", \
+ phba->cfg_##attr, val); \
phba->cfg_##attr = val;\
return 0;\
}\
@@ -1614,7 +2909,7 @@ lpfc_##attr##_set(struct lpfc_hba *phba, uint val) \
return -EINVAL;\
}
-/**
+/*
* lpfc_param_store - Set a vport attribute value
*
* Description:
@@ -1654,7 +2949,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
return -EINVAL;\
}
-/**
+/*
* lpfc_vport_param_show - Return decimal formatted cfg attribute value
*
* Description:
@@ -1675,12 +2970,10 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
{ \
struct Scsi_Host *shost = class_to_shost(dev);\
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\
- uint val = 0;\
- val = vport->cfg_##attr;\
- return snprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_##attr);\
+ return scnprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_##attr);\
}
-/**
+/*
* lpfc_vport_param_hex_show - Return hex formatted attribute value
*
* Description:
@@ -1702,12 +2995,10 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
{ \
struct Scsi_Host *shost = class_to_shost(dev);\
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\
- uint val = 0;\
- val = vport->cfg_##attr;\
- return snprintf(buf, PAGE_SIZE, "%#x\n", vport->cfg_##attr);\
+ return scnprintf(buf, PAGE_SIZE, "%#x\n", vport->cfg_##attr);\
}
-/**
+/*
* lpfc_vport_param_init - Initialize a vport cfg attribute
*
* Description:
@@ -1718,7 +3009,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
* lpfc_##attr##_init: validates the min and max values then sets the
* adapter config field accordingly, or uses the default if out of range
* and prints an error message.
- * @phba: pointer the the adapter structure.
+ * @phba: pointer to the adapter structure.
* @val: integer attribute value.
*
* Returns:
@@ -1729,7 +3020,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \
static int \
lpfc_##attr##_init(struct lpfc_vport *vport, uint val) \
{ \
- if (val >= minval && val <= maxval) {\
+ if (lpfc_rangecheck(val, minval, maxval)) {\
vport->cfg_##attr = val;\
return 0;\
}\
@@ -1740,7 +3031,7 @@ lpfc_##attr##_init(struct lpfc_vport *vport, uint val) \
return -EINVAL;\
}
-/**
+/*
* lpfc_vport_param_set - Set a vport cfg attribute
*
* Description:
@@ -1750,7 +3041,7 @@ lpfc_##attr##_init(struct lpfc_vport *vport, uint val) \
* lpfc_##attr##_set: validates the min and max values then sets the
* adapter config field if in the valid range. prints error message
* and does not set the parameter if invalid.
- * @phba: pointer the the adapter structure.
+ * @phba: pointer to the adapter structure.
* @val: integer attribute value.
*
* Returns:
@@ -1761,7 +3052,12 @@ lpfc_##attr##_init(struct lpfc_vport *vport, uint val) \
static int \
lpfc_##attr##_set(struct lpfc_vport *vport, uint val) \
{ \
- if (val >= minval && val <= maxval) {\
+ if (lpfc_rangecheck(val, minval, maxval)) {\
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, \
+ "3053 lpfc_" #attr \
+ " changed from %d (x%x) to %d (x%x)\n", \
+ vport->cfg_##attr, vport->cfg_##attr, \
+ val, val); \
vport->cfg_##attr = val;\
return 0;\
}\
@@ -1771,7 +3067,7 @@ lpfc_##attr##_set(struct lpfc_vport *vport, uint val) \
return -EINVAL;\
}
-/**
+/*
* lpfc_vport_param_store - Set a vport attribute
*
* Description:
@@ -1807,94 +3103,8 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
}
-#define LPFC_ATTR(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_param_init(name, defval, minval, maxval)
-
-#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_param_show(name)\
-lpfc_param_init(name, defval, minval, maxval)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
-
-#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_param_show(name)\
-lpfc_param_init(name, defval, minval, maxval)\
-lpfc_param_set(name, defval, minval, maxval)\
-lpfc_param_store(name)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
- lpfc_##name##_show, lpfc_##name##_store)
-
-#define LPFC_ATTR_HEX_R(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_param_hex_show(name)\
-lpfc_param_init(name, defval, minval, maxval)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
-
-#define LPFC_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_param_hex_show(name)\
-lpfc_param_init(name, defval, minval, maxval)\
-lpfc_param_set(name, defval, minval, maxval)\
-lpfc_param_store(name)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
- lpfc_##name##_show, lpfc_##name##_store)
-
-#define LPFC_VPORT_ATTR(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_vport_param_init(name, defval, minval, maxval)
-
-#define LPFC_VPORT_ATTR_R(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_vport_param_show(name)\
-lpfc_vport_param_init(name, defval, minval, maxval)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
-
-#define LPFC_VPORT_ATTR_RW(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_vport_param_show(name)\
-lpfc_vport_param_init(name, defval, minval, maxval)\
-lpfc_vport_param_set(name, defval, minval, maxval)\
-lpfc_vport_param_store(name)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
- lpfc_##name##_show, lpfc_##name##_store)
-
-#define LPFC_VPORT_ATTR_HEX_R(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_vport_param_hex_show(name)\
-lpfc_vport_param_init(name, defval, minval, maxval)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
-
-#define LPFC_VPORT_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
-static uint lpfc_##name = defval;\
-module_param(lpfc_##name, uint, S_IRUGO);\
-MODULE_PARM_DESC(lpfc_##name, desc);\
-lpfc_vport_param_hex_show(name)\
-lpfc_vport_param_init(name, defval, minval, maxval)\
-lpfc_vport_param_set(name, defval, minval, maxval)\
-lpfc_vport_param_store(name)\
-static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
- lpfc_##name##_show, lpfc_##name##_store)
-
+static DEVICE_ATTR(nvme_info, 0444, lpfc_nvme_info_show, NULL);
+static DEVICE_ATTR(scsi_stat, 0444, lpfc_scsi_stat_show, NULL);
static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL);
static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL);
static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL);
@@ -1913,12 +3123,12 @@ static DEVICE_ATTR(option_rom_version, S_IRUGO,
lpfc_option_rom_version_show, NULL);
static DEVICE_ATTR(num_discovered_ports, S_IRUGO,
lpfc_num_discovered_ports_show, NULL);
-static DEVICE_ATTR(menlo_mgmt_mode, S_IRUGO, lpfc_mlomgmt_show, NULL);
static DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL);
-static DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, NULL);
-static DEVICE_ATTR(lpfc_enable_fip, S_IRUGO, lpfc_enable_fip_show, NULL);
+static DEVICE_ATTR_RO(lpfc_drvr_version);
+static DEVICE_ATTR_RO(lpfc_enable_fip);
static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR,
lpfc_board_mode_show, lpfc_board_mode_store);
+static DEVICE_ATTR_RO(lpfc_xcvr_data);
static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset);
static DEVICE_ATTR(max_vpi, S_IRUGO, lpfc_max_vpi_show, NULL);
static DEVICE_ATTR(used_vpi, S_IRUGO, lpfc_used_vpi_show, NULL);
@@ -1927,250 +3137,572 @@ static DEVICE_ATTR(used_rpi, S_IRUGO, lpfc_used_rpi_show, NULL);
static DEVICE_ATTR(max_xri, S_IRUGO, lpfc_max_xri_show, NULL);
static DEVICE_ATTR(used_xri, S_IRUGO, lpfc_used_xri_show, NULL);
static DEVICE_ATTR(npiv_info, S_IRUGO, lpfc_npiv_info_show, NULL);
-static DEVICE_ATTR(lpfc_temp_sensor, S_IRUGO, lpfc_temp_sensor_show, NULL);
-static DEVICE_ATTR(lpfc_fips_level, S_IRUGO, lpfc_fips_level_show, NULL);
-static DEVICE_ATTR(lpfc_fips_rev, S_IRUGO, lpfc_fips_rev_show, NULL);
-static DEVICE_ATTR(lpfc_dss, S_IRUGO, lpfc_dss_show, NULL);
-static DEVICE_ATTR(lpfc_sriov_hw_max_virtfn, S_IRUGO,
- lpfc_sriov_hw_max_virtfn_show, NULL);
+static DEVICE_ATTR_RO(lpfc_temp_sensor);
+static DEVICE_ATTR_RO(lpfc_sriov_hw_max_virtfn);
+static DEVICE_ATTR(protocol, S_IRUGO, lpfc_sli4_protocol_show, NULL);
+static DEVICE_ATTR(lpfc_xlane_supported, S_IRUGO, lpfc_oas_supported_show,
+ NULL);
+static DEVICE_ATTR(cmf_info, 0444, lpfc_cmf_info_show, NULL);
+static DEVICE_ATTR_RO(lpfc_vmid_info);
+
+#define WWN_SZ 8
+/**
+ * lpfc_wwn_set - Convert string to the 8 byte WWN value.
+ * @buf: WWN string.
+ * @cnt: Length of string.
+ * @wwn: Array to receive converted wwn value.
+ *
+ * Returns:
+ * -EINVAL if the buffer does not contain a valid wwn
+ * 0 success
+ **/
+static size_t
+lpfc_wwn_set(const char *buf, size_t cnt, char wwn[])
+{
+ unsigned int i, j;
+
+ /* Count may include a LF at end of string */
+ if (buf[cnt-1] == '\n')
+ cnt--;
+
+ if ((cnt < 16) || (cnt > 18) || ((cnt == 17) && (*buf++ != 'x')) ||
+ ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x'))))
+ return -EINVAL;
+
+ memset(wwn, 0, WWN_SZ);
+
+ /* Validate and store the new name */
+ for (i = 0, j = 0; i < 16; i++) {
+ if ((*buf >= 'a') && (*buf <= 'f'))
+ j = ((j << 4) | ((*buf++ - 'a') + 10));
+ else if ((*buf >= 'A') && (*buf <= 'F'))
+ j = ((j << 4) | ((*buf++ - 'A') + 10));
+ else if ((*buf >= '0') && (*buf <= '9'))
+ j = ((j << 4) | (*buf++ - '0'));
+ else
+ return -EINVAL;
+ if (i % 2) {
+ wwn[i/2] = j & 0xff;
+ j = 0;
+ }
+ }
+ return 0;
+}
-static char *lpfc_soft_wwn_key = "C99G71SL8032A";
/**
- * lpfc_soft_wwn_enable_store - Allows setting of the wwn if the key is valid
+ * lpfc_oas_tgt_show - Return wwpn of target whose luns maybe enabled for
+ * Optimized Access Storage (OAS) operations.
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: containing the string lpfc_soft_wwn_key.
- * @count: must be size of lpfc_soft_wwn_key.
+ * @buf: buffer for passing information.
*
* Returns:
- * -EINVAL if the buffer does not contain lpfc_soft_wwn_key
- * length of buf indicates success
+ * value of count
**/
static ssize_t
-lpfc_soft_wwn_enable_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+lpfc_oas_tgt_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ return scnprintf(buf, PAGE_SIZE, "0x%llx\n",
+ wwn_to_u64(phba->cfg_oas_tgt_wwpn));
+}
+
+/**
+ * lpfc_oas_tgt_store - Store wwpn of target whose luns maybe enabled for
+ * Optimized Access Storage (OAS) operations.
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: buffer for passing information.
+ * @count: Size of the data buffer.
+ *
+ * Returns:
+ * -EINVAL count is invalid, invalid wwpn byte invalid
+ * -EPERM oas is not supported by hba
+ * value of count on success
+ **/
+static ssize_t
+lpfc_oas_tgt_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
unsigned int cnt = count;
+ uint8_t wwpn[WWN_SZ];
+ int rc;
- /*
- * We're doing a simple sanity check for soft_wwpn setting.
- * We require that the user write a specific key to enable
- * the soft_wwpn attribute to be settable. Once the attribute
- * is written, the enable key resets. If further updates are
- * desired, the key must be written again to re-enable the
- * attribute.
- *
- * The "key" is not secret - it is a hardcoded string shown
- * here. The intent is to protect against the random user or
- * application that is just writing attributes.
- */
+ if (!phba->cfg_fof)
+ return -EPERM;
/* count may include a LF at end of string */
if (buf[cnt-1] == '\n')
cnt--;
- if ((cnt != strlen(lpfc_soft_wwn_key)) ||
- (strncmp(buf, lpfc_soft_wwn_key, strlen(lpfc_soft_wwn_key)) != 0))
- return -EINVAL;
+ rc = lpfc_wwn_set(buf, cnt, wwpn);
+ if (rc)
+ return rc;
- phba->soft_wwn_enable = 1;
+ memcpy(phba->cfg_oas_tgt_wwpn, wwpn, (8 * sizeof(uint8_t)));
+ memcpy(phba->sli4_hba.oas_next_tgt_wwpn, wwpn, (8 * sizeof(uint8_t)));
+ if (wwn_to_u64(wwpn) == 0)
+ phba->cfg_oas_flags |= OAS_FIND_ANY_TARGET;
+ else
+ phba->cfg_oas_flags &= ~OAS_FIND_ANY_TARGET;
+ phba->cfg_oas_flags &= ~OAS_LUN_VALID;
+ phba->sli4_hba.oas_next_lun = FIND_FIRST_OAS_LUN;
return count;
}
-static DEVICE_ATTR(lpfc_soft_wwn_enable, S_IWUSR, NULL,
- lpfc_soft_wwn_enable_store);
+static DEVICE_ATTR(lpfc_xlane_tgt, S_IRUGO | S_IWUSR,
+ lpfc_oas_tgt_show, lpfc_oas_tgt_store);
/**
- * lpfc_soft_wwpn_show - Return the cfg soft ww port name of the adapter
+ * lpfc_oas_priority_show - Return wwpn of target whose luns maybe enabled for
+ * Optimized Access Storage (OAS) operations.
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: on return contains the wwpn in hexadecimal.
+ * @buf: buffer for passing information.
*
- * Returns: size of formatted string.
+ * Returns:
+ * value of count
**/
static ssize_t
-lpfc_soft_wwpn_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+lpfc_oas_priority_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
- return snprintf(buf, PAGE_SIZE, "0x%llx\n",
- (unsigned long long)phba->cfg_soft_wwpn);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", phba->cfg_oas_priority);
}
/**
- * lpfc_soft_wwpn_store - Set the ww port name of the adapter
- * @dev class device that is converted into a Scsi_host.
+ * lpfc_oas_priority_store - Store wwpn of target whose luns maybe enabled for
+ * Optimized Access Storage (OAS) operations.
+ * @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: contains the wwpn in hexadecimal.
- * @count: number of wwpn bytes in buf
+ * @buf: buffer for passing information.
+ * @count: Size of the data buffer.
*
* Returns:
- * -EACCES hba reset not enabled, adapter over temp
- * -EINVAL soft wwn not enabled, count is invalid, invalid wwpn byte invalid
- * -EIO error taking adapter offline or online
+ * -EINVAL count is invalid, invalid wwpn byte invalid
+ * -EPERM oas is not supported by hba
* value of count on success
**/
static ssize_t
-lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+lpfc_oas_priority_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct completion online_compl;
- int stat1=0, stat2=0;
- unsigned int i, j, cnt=count;
- u8 wwpn[8];
- int rc;
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ unsigned int cnt = count;
+ unsigned long val;
+ int ret;
+
+ if (!phba->cfg_fof)
+ return -EPERM;
- if (!phba->cfg_enable_hba_reset)
- return -EACCES;
- spin_lock_irq(&phba->hbalock);
- if (phba->over_temp_state == HBA_OVER_TEMP) {
- spin_unlock_irq(&phba->hbalock);
- return -EACCES;
- }
- spin_unlock_irq(&phba->hbalock);
/* count may include a LF at end of string */
if (buf[cnt-1] == '\n')
cnt--;
- if (!phba->soft_wwn_enable || (cnt < 16) || (cnt > 18) ||
- ((cnt == 17) && (*buf++ != 'x')) ||
- ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x'))))
+ ret = kstrtoul(buf, 0, &val);
+ if (ret || (val > 0x7f))
return -EINVAL;
- phba->soft_wwn_enable = 0;
+ if (val)
+ phba->cfg_oas_priority = (uint8_t)val;
+ else
+ phba->cfg_oas_priority = phba->cfg_XLanePriority;
+ return count;
+}
+static DEVICE_ATTR(lpfc_xlane_priority, S_IRUGO | S_IWUSR,
+ lpfc_oas_priority_show, lpfc_oas_priority_store);
- memset(wwpn, 0, sizeof(wwpn));
+/**
+ * lpfc_oas_vpt_show - Return wwpn of vport whose targets maybe enabled
+ * for Optimized Access Storage (OAS) operations.
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: buffer for passing information.
+ *
+ * Returns:
+ * value of count on success
+ **/
+static ssize_t
+lpfc_oas_vpt_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
- /* Validate and store the new name */
- for (i=0, j=0; i < 16; i++) {
- int value;
+ return scnprintf(buf, PAGE_SIZE, "0x%llx\n",
+ wwn_to_u64(phba->cfg_oas_vpt_wwpn));
+}
- value = hex_to_bin(*buf++);
- if (value >= 0)
- j = (j << 4) | value;
- else
- return -EINVAL;
- if (i % 2) {
- wwpn[i/2] = j & 0xff;
- j = 0;
- }
- }
- phba->cfg_soft_wwpn = wwn_to_u64(wwpn);
- fc_host_port_name(shost) = phba->cfg_soft_wwpn;
- if (phba->cfg_soft_wwnn)
- fc_host_node_name(shost) = phba->cfg_soft_wwnn;
+/**
+ * lpfc_oas_vpt_store - Store wwpn of vport whose targets maybe enabled
+ * for Optimized Access Storage (OAS) operations.
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: buffer for passing information.
+ * @count: Size of the data buffer.
+ *
+ * Returns:
+ * -EINVAL count is invalid, invalid wwpn byte invalid
+ * -EPERM oas is not supported by hba
+ * value of count on success
+ **/
+static ssize_t
+lpfc_oas_vpt_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ unsigned int cnt = count;
+ uint8_t wwpn[WWN_SZ];
+ int rc;
- dev_printk(KERN_NOTICE, &phba->pcidev->dev,
- "lpfc%d: Reinitializing to use soft_wwpn\n", phba->brd_no);
+ if (!phba->cfg_fof)
+ return -EPERM;
- stat1 = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
- if (stat1)
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0463 lpfc_soft_wwpn attribute set failed to "
- "reinit adapter - %d\n", stat1);
- init_completion(&online_compl);
- rc = lpfc_workq_post_event(phba, &stat2, &online_compl,
- LPFC_EVT_ONLINE);
- if (rc == 0)
- return -ENOMEM;
+ /* count may include a LF at end of string */
+ if (buf[cnt-1] == '\n')
+ cnt--;
- wait_for_completion(&online_compl);
- if (stat2)
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0464 lpfc_soft_wwpn attribute set failed to "
- "reinit adapter - %d\n", stat2);
- return (stat1 || stat2) ? -EIO : count;
+ rc = lpfc_wwn_set(buf, cnt, wwpn);
+ if (rc)
+ return rc;
+
+ memcpy(phba->cfg_oas_vpt_wwpn, wwpn, (8 * sizeof(uint8_t)));
+ memcpy(phba->sli4_hba.oas_next_vpt_wwpn, wwpn, (8 * sizeof(uint8_t)));
+ if (wwn_to_u64(wwpn) == 0)
+ phba->cfg_oas_flags |= OAS_FIND_ANY_VPORT;
+ else
+ phba->cfg_oas_flags &= ~OAS_FIND_ANY_VPORT;
+ phba->cfg_oas_flags &= ~OAS_LUN_VALID;
+ if (phba->cfg_oas_priority == 0)
+ phba->cfg_oas_priority = phba->cfg_XLanePriority;
+ phba->sli4_hba.oas_next_lun = FIND_FIRST_OAS_LUN;
+ return count;
}
-static DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR,\
- lpfc_soft_wwpn_show, lpfc_soft_wwpn_store);
+static DEVICE_ATTR(lpfc_xlane_vpt, S_IRUGO | S_IWUSR,
+ lpfc_oas_vpt_show, lpfc_oas_vpt_store);
/**
- * lpfc_soft_wwnn_show - Return the cfg soft ww node name for the adapter
+ * lpfc_oas_lun_state_show - Return the current state (enabled or disabled)
+ * of whether luns will be enabled or disabled
+ * for Optimized Access Storage (OAS) operations.
* @dev: class device that is converted into a Scsi_host.
* @attr: device attribute, not used.
- * @buf: on return contains the wwnn in hexadecimal.
+ * @buf: buffer for passing information.
*
- * Returns: size of formatted string.
+ * Returns:
+ * size of formatted string.
**/
static ssize_t
-lpfc_soft_wwnn_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+lpfc_oas_lun_state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
- return snprintf(buf, PAGE_SIZE, "0x%llx\n",
- (unsigned long long)phba->cfg_soft_wwnn);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", phba->cfg_oas_lun_state);
}
/**
- * lpfc_soft_wwnn_store - sets the ww node name of the adapter
- * @cdev: class device that is converted into a Scsi_host.
- * @buf: contains the ww node name in hexadecimal.
- * @count: number of wwnn bytes in buf.
+ * lpfc_oas_lun_state_store - Store the state (enabled or disabled)
+ * of whether luns will be enabled or disabled
+ * for Optimized Access Storage (OAS) operations.
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: buffer for passing information.
+ * @count: Size of the data buffer.
*
* Returns:
- * -EINVAL soft wwn not enabled, count is invalid, invalid wwnn byte invalid
+ * -EINVAL count is invalid, invalid wwpn byte invalid
+ * -EPERM oas is not supported by hba
* value of count on success
**/
static ssize_t
-lpfc_soft_wwnn_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+lpfc_oas_lun_state_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
- unsigned int i, j, cnt=count;
- u8 wwnn[8];
+ int val = 0;
- /* count may include a LF at end of string */
- if (buf[cnt-1] == '\n')
- cnt--;
+ if (!phba->cfg_fof)
+ return -EPERM;
- if (!phba->soft_wwn_enable || (cnt < 16) || (cnt > 18) ||
- ((cnt == 17) && (*buf++ != 'x')) ||
- ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x'))))
+ if (!isdigit(buf[0]))
return -EINVAL;
- /*
- * Allow wwnn to be set many times, as long as the enable is set.
- * However, once the wwpn is set, everything locks.
- */
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
- memset(wwnn, 0, sizeof(wwnn));
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
- /* Validate and store the new name */
- for (i=0, j=0; i < 16; i++) {
- int value;
+ phba->cfg_oas_lun_state = val;
+ return strlen(buf);
+}
+static DEVICE_ATTR(lpfc_xlane_lun_state, S_IRUGO | S_IWUSR,
+ lpfc_oas_lun_state_show, lpfc_oas_lun_state_store);
- value = hex_to_bin(*buf++);
- if (value >= 0)
- j = (j << 4) | value;
- else
- return -EINVAL;
- if (i % 2) {
- wwnn[i/2] = j & 0xff;
- j = 0;
- }
+/**
+ * lpfc_oas_lun_status_show - Return the status of the Optimized Access
+ * Storage (OAS) lun returned by the
+ * lpfc_oas_lun_show function.
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: buffer for passing information.
+ *
+ * Returns:
+ * size of formatted string.
+ **/
+static ssize_t
+lpfc_oas_lun_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ if (!(phba->cfg_oas_flags & OAS_LUN_VALID))
+ return -EFAULT;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", phba->cfg_oas_lun_status);
+}
+static DEVICE_ATTR(lpfc_xlane_lun_status, S_IRUGO,
+ lpfc_oas_lun_status_show, NULL);
+
+
+/**
+ * lpfc_oas_lun_state_set - enable or disable a lun for Optimized Access Storage
+ * (OAS) operations.
+ * @phba: lpfc_hba pointer.
+ * @vpt_wwpn: wwpn of the vport associated with the returned lun
+ * @tgt_wwpn: wwpn of the target associated with the returned lun
+ * @lun: the fc lun for setting oas state.
+ * @oas_state: the oas state to be set to the lun.
+ * @pri: priority
+ *
+ * Returns:
+ * SUCCESS : 0
+ * -EPERM OAS is not enabled or not supported by this port.
+ *
+ */
+static size_t
+lpfc_oas_lun_state_set(struct lpfc_hba *phba, uint8_t vpt_wwpn[],
+ uint8_t tgt_wwpn[], uint64_t lun,
+ uint32_t oas_state, uint8_t pri)
+{
+
+ int rc = 0;
+
+ if (!phba->cfg_fof)
+ return -EPERM;
+
+ if (oas_state) {
+ if (!lpfc_enable_oas_lun(phba, (struct lpfc_name *)vpt_wwpn,
+ (struct lpfc_name *)tgt_wwpn,
+ lun, pri))
+ rc = -ENOMEM;
+ } else {
+ lpfc_disable_oas_lun(phba, (struct lpfc_name *)vpt_wwpn,
+ (struct lpfc_name *)tgt_wwpn, lun, pri);
}
- phba->cfg_soft_wwnn = wwn_to_u64(wwnn);
+ return rc;
+
+}
- dev_printk(KERN_NOTICE, &phba->pcidev->dev,
- "lpfc%d: soft_wwnn set. Value will take effect upon "
- "setting of the soft_wwpn\n", phba->brd_no);
+/**
+ * lpfc_oas_lun_get_next - get the next lun that has been enabled for Optimized
+ * Access Storage (OAS) operations.
+ * @phba: lpfc_hba pointer.
+ * @vpt_wwpn: wwpn of the vport associated with the returned lun
+ * @tgt_wwpn: wwpn of the target associated with the returned lun
+ * @lun_status: status of the lun returned lun
+ * @lun_pri: priority of the lun returned lun
+ *
+ * Returns the first or next lun enabled for OAS operations for the vport/target
+ * specified. If a lun is found, its vport wwpn, target wwpn and status is
+ * returned. If the lun is not found, NOT_OAS_ENABLED_LUN is returned.
+ *
+ * Return:
+ * lun that is OAS enabled for the vport/target
+ * NOT_OAS_ENABLED_LUN when no oas enabled lun found.
+ */
+static uint64_t
+lpfc_oas_lun_get_next(struct lpfc_hba *phba, uint8_t vpt_wwpn[],
+ uint8_t tgt_wwpn[], uint32_t *lun_status,
+ uint32_t *lun_pri)
+{
+ uint64_t found_lun;
+
+ if (unlikely(!phba) || !vpt_wwpn || !tgt_wwpn)
+ return NOT_OAS_ENABLED_LUN;
+ if (lpfc_find_next_oas_lun(phba, (struct lpfc_name *)
+ phba->sli4_hba.oas_next_vpt_wwpn,
+ (struct lpfc_name *)
+ phba->sli4_hba.oas_next_tgt_wwpn,
+ &phba->sli4_hba.oas_next_lun,
+ (struct lpfc_name *)vpt_wwpn,
+ (struct lpfc_name *)tgt_wwpn,
+ &found_lun, lun_status, lun_pri))
+ return found_lun;
+ else
+ return NOT_OAS_ENABLED_LUN;
+}
+
+/**
+ * lpfc_oas_lun_state_change - enable/disable a lun for OAS operations
+ * @phba: lpfc_hba pointer.
+ * @vpt_wwpn: vport wwpn by reference.
+ * @tgt_wwpn: target wwpn by reference.
+ * @lun: the fc lun for setting oas state.
+ * @oas_state: the oas state to be set to the oas_lun.
+ * @pri: priority
+ *
+ * This routine enables (OAS_LUN_ENABLE) or disables (OAS_LUN_DISABLE)
+ * a lun for OAS operations.
+ *
+ * Return:
+ * SUCCESS: 0
+ * -ENOMEM: failed to enable an lun for OAS operations
+ * -EPERM: OAS is not enabled
+ */
+static ssize_t
+lpfc_oas_lun_state_change(struct lpfc_hba *phba, uint8_t vpt_wwpn[],
+ uint8_t tgt_wwpn[], uint64_t lun,
+ uint32_t oas_state, uint8_t pri)
+{
+
+ int rc;
+
+ rc = lpfc_oas_lun_state_set(phba, vpt_wwpn, tgt_wwpn, lun,
+ oas_state, pri);
+ return rc;
+}
+
+/**
+ * lpfc_oas_lun_show - Return oas enabled luns from a chosen target
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: buffer for passing information.
+ *
+ * This routine returns a lun enabled for OAS each time the function
+ * is called.
+ *
+ * Returns:
+ * SUCCESS: size of formatted string.
+ * -EFAULT: target or vport wwpn was not set properly.
+ * -EPERM: oas is not enabled.
+ **/
+static ssize_t
+lpfc_oas_lun_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ uint64_t oas_lun;
+ int len = 0;
+
+ if (!phba->cfg_fof)
+ return -EPERM;
+
+ if (wwn_to_u64(phba->cfg_oas_vpt_wwpn) == 0)
+ if (!(phba->cfg_oas_flags & OAS_FIND_ANY_VPORT))
+ return -EFAULT;
+
+ if (wwn_to_u64(phba->cfg_oas_tgt_wwpn) == 0)
+ if (!(phba->cfg_oas_flags & OAS_FIND_ANY_TARGET))
+ return -EFAULT;
+
+ oas_lun = lpfc_oas_lun_get_next(phba, phba->cfg_oas_vpt_wwpn,
+ phba->cfg_oas_tgt_wwpn,
+ &phba->cfg_oas_lun_status,
+ &phba->cfg_oas_priority);
+ if (oas_lun != NOT_OAS_ENABLED_LUN)
+ phba->cfg_oas_flags |= OAS_LUN_VALID;
+
+ len += scnprintf(buf + len, PAGE_SIZE-len, "0x%llx", oas_lun);
+
+ return len;
+}
+
+/**
+ * lpfc_oas_lun_store - Sets the OAS state for lun
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: buffer for passing information.
+ * @count: size of the formatting string
+ *
+ * This function sets the OAS state for lun. Before this function is called,
+ * the vport wwpn, target wwpn, and oas state need to be set.
+ *
+ * Returns:
+ * SUCCESS: size of formatted string.
+ * -EFAULT: target or vport wwpn was not set properly.
+ * -EPERM: oas is not enabled.
+ * size of formatted string.
+ **/
+static ssize_t
+lpfc_oas_lun_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ uint64_t scsi_lun;
+ uint32_t pri;
+ ssize_t rc;
+
+ if (!phba->cfg_fof)
+ return -EPERM;
+
+ if (wwn_to_u64(phba->cfg_oas_vpt_wwpn) == 0)
+ return -EFAULT;
+
+ if (wwn_to_u64(phba->cfg_oas_tgt_wwpn) == 0)
+ return -EFAULT;
+
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+
+ if (sscanf(buf, "0x%llx", &scsi_lun) != 1)
+ return -EINVAL;
+
+ pri = phba->cfg_oas_priority;
+ if (pri == 0)
+ pri = phba->cfg_XLanePriority;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3372 Try to set vport 0x%llx target 0x%llx lun:0x%llx "
+ "priority 0x%x with oas state %d\n",
+ wwn_to_u64(phba->cfg_oas_vpt_wwpn),
+ wwn_to_u64(phba->cfg_oas_tgt_wwpn), scsi_lun,
+ pri, phba->cfg_oas_lun_state);
+
+ rc = lpfc_oas_lun_state_change(phba, phba->cfg_oas_vpt_wwpn,
+ phba->cfg_oas_tgt_wwpn, scsi_lun,
+ phba->cfg_oas_lun_state, pri);
+ if (rc)
+ return rc;
return count;
}
-static DEVICE_ATTR(lpfc_soft_wwnn, S_IRUGO | S_IWUSR,\
- lpfc_soft_wwnn_show, lpfc_soft_wwnn_store);
+static DEVICE_ATTR(lpfc_xlane_lun, S_IRUGO | S_IWUSR,
+ lpfc_oas_lun_show, lpfc_oas_lun_store);
+int lpfc_enable_nvmet_cnt;
+unsigned long long lpfc_enable_nvmet[LPFC_NVMET_MAX_PORTS] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+module_param_array(lpfc_enable_nvmet, ullong, &lpfc_enable_nvmet_cnt, 0444);
+MODULE_PARM_DESC(lpfc_enable_nvmet, "Enable HBA port(s) WWPN as a NVME Target");
static int lpfc_poll = 0;
module_param(lpfc_poll, int, S_IRUGO);
@@ -2179,29 +3711,40 @@ MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:"
" 1 - poll with interrupts enabled"
" 3 - poll and disable FCP ring interrupts");
-static DEVICE_ATTR(lpfc_poll, S_IRUGO | S_IWUSR,
- lpfc_poll_show, lpfc_poll_store);
-
-int lpfc_sli_mode = 0;
-module_param(lpfc_sli_mode, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_sli_mode, "SLI mode selector:"
- " 0 - auto (SLI-3 if supported),"
- " 2 - select SLI-2 even on SLI-3 capable HBAs,"
- " 3 - select SLI-3");
-
-int lpfc_enable_npiv = 1;
-module_param(lpfc_enable_npiv, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_enable_npiv, "Enable NPIV functionality");
-lpfc_param_show(enable_npiv);
-lpfc_param_init(enable_npiv, 1, 0, 1);
-static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO, lpfc_enable_npiv_show, NULL);
-
-int lpfc_enable_rrq;
-module_param(lpfc_enable_rrq, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_enable_rrq, "Enable RRQ functionality");
-lpfc_param_show(enable_rrq);
-lpfc_param_init(enable_rrq, 0, 0, 1);
-static DEVICE_ATTR(lpfc_enable_rrq, S_IRUGO, lpfc_enable_rrq_show, NULL);
+static DEVICE_ATTR_RW(lpfc_poll);
+
+int lpfc_no_hba_reset_cnt;
+unsigned long lpfc_no_hba_reset[MAX_HBAS_NO_RESET] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+module_param_array(lpfc_no_hba_reset, ulong, &lpfc_no_hba_reset_cnt, 0444);
+MODULE_PARM_DESC(lpfc_no_hba_reset, "WWPN of HBAs that should not be reset");
+
+LPFC_ATTR(sli_mode, 3, 3, 3,
+ "SLI mode selector: 3 - select SLI-3");
+
+LPFC_ATTR_R(enable_npiv, 1, 0, 1,
+ "Enable NPIV functionality");
+
+LPFC_ATTR_R(fcf_failover_policy, 1, 1, 2,
+ "FCF Fast failover=1 Priority failover=2");
+
+/*
+ * lpfc_fcp_wait_abts_rsp: Modifies criteria for reporting completion of
+ * aborted IO.
+ * The range is [0,1]. Default value is 0
+ * 0, IO completes after ABTS issued (default).
+ * 1, IO completes after receipt of ABTS response or timeout.
+ */
+LPFC_ATTR_R(fcp_wait_abts_rsp, 0, 0, 1, "Wait for FCP ABTS completion");
+
+/*
+# lpfc_enable_rrq: Track XRI/OXID reuse after IO failures
+# 0x0 = disabled, XRI/OXID use not tracked.
+# 0x1 = XRI/OXID reuse is timed with ratov, RRQ sent.
+# 0x2 = XRI/OXID reuse is timed with ratov, No RRQ sent.
+*/
+LPFC_ATTR_R(enable_rrq, 2, 0, 2,
+ "Enable RRQ functionality");
/*
# lpfc_suppress_link_up: Bring link up at initialization
@@ -2213,6 +3756,32 @@ static DEVICE_ATTR(lpfc_enable_rrq, S_IRUGO, lpfc_enable_rrq_show, NULL);
LPFC_ATTR_R(suppress_link_up, LPFC_INITIALIZE_LINK, LPFC_INITIALIZE_LINK,
LPFC_DELAY_INIT_LINK_INDEFINITELY,
"Suppress Link Up at initialization");
+
+static ssize_t
+lpfc_pls_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ phba->sli4_hba.pc_sli4_params.pls);
+}
+static DEVICE_ATTR(pls, 0444,
+ lpfc_pls_show, NULL);
+
+static ssize_t
+lpfc_pt_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ test_bit(HBA_PERSISTENT_TOPO,
+ &phba->hba_flag) ? 1 : 0);
+}
+static DEVICE_ATTR(pt, 0444,
+ lpfc_pt_show, NULL);
+
/*
# lpfc_cnt: Number of IOCBs allocated for ELS, CT, and ABTS
# 1 - (1024)
@@ -2227,7 +3796,7 @@ lpfc_iocb_hw_show(struct device *dev, struct device_attribute *attr, char *buf)
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_hba *phba = ((struct lpfc_vport *) shost->hostdata)->phba;
- return snprintf(buf, PAGE_SIZE, "%d\n", phba->iocb_max);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", phba->iocb_max);
}
static DEVICE_ATTR(iocb_hw, S_IRUGO,
@@ -2237,9 +3806,10 @@ lpfc_txq_hw_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_hba *phba = ((struct lpfc_vport *) shost->hostdata)->phba;
+ struct lpfc_sli_ring *pring = lpfc_phba_elsring(phba);
- return snprintf(buf, PAGE_SIZE, "%d\n",
- phba->sli.ring[LPFC_ELS_RING].txq_max);
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ pring ? pring->txq_max : 0);
}
static DEVICE_ATTR(txq_hw, S_IRUGO,
@@ -2250,23 +3820,15 @@ lpfc_txcmplq_hw_show(struct device *dev, struct device_attribute *attr,
{
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_hba *phba = ((struct lpfc_vport *) shost->hostdata)->phba;
+ struct lpfc_sli_ring *pring = lpfc_phba_elsring(phba);
- return snprintf(buf, PAGE_SIZE, "%d\n",
- phba->sli.ring[LPFC_ELS_RING].txcmplq_max);
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ pring ? pring->txcmplq_max : 0);
}
static DEVICE_ATTR(txcmplq_hw, S_IRUGO,
lpfc_txcmplq_hw_show, NULL);
-int lpfc_iocb_cnt = 2;
-module_param(lpfc_iocb_cnt, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_iocb_cnt,
- "Number of IOCBs alloc for ELS, CT, and ABTS: 1k to 5k IOCBs");
-lpfc_param_show(iocb_cnt);
-lpfc_param_init(iocb_cnt, 2, 1, 5);
-static DEVICE_ATTR(lpfc_iocb_cnt, S_IRUGO,
- lpfc_iocb_cnt_show, NULL);
-
/*
# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear
# until the timer expires. Value range is [0,255]. Default value is 30.
@@ -2293,7 +3855,7 @@ lpfc_nodev_tmo_show(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- return snprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_devloss_tmo);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_devloss_tmo);
}
/**
@@ -2318,9 +3880,9 @@ lpfc_nodev_tmo_init(struct lpfc_vport *vport, int val)
vport->cfg_nodev_tmo = vport->cfg_devloss_tmo;
if (val != LPFC_DEF_DEVLOSS_TMO)
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0407 Ignoring nodev_tmo module "
- "parameter because devloss_tmo is "
- "set.\n");
+ "0407 Ignoring lpfc_nodev_tmo module "
+ "parameter because lpfc_devloss_tmo "
+ "is set.\n");
return 0;
}
@@ -2347,15 +3909,29 @@ lpfc_nodev_tmo_init(struct lpfc_vport *vport, int val)
static void
lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost;
struct lpfc_nodelist *ndlp;
+ unsigned long iflags;
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ struct lpfc_nvme_rport *rport;
+ struct nvme_fc_remote_port *remoteport = NULL;
+#endif
- shost = lpfc_shost_from_vport(vport);
- spin_lock_irq(shost->host_lock);
- list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp)
- if (NLP_CHK_NODE_ACT(ndlp) && ndlp->rport)
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ if (ndlp->rport)
ndlp->rport->dev_loss_tmo = vport->cfg_devloss_tmo;
- spin_unlock_irq(shost->host_lock);
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ spin_lock(&ndlp->lock);
+ rport = lpfc_ndlp_get_nrport(ndlp);
+ if (rport)
+ remoteport = rport->remoteport;
+ spin_unlock(&ndlp->lock);
+ if (rport && remoteport)
+ nvme_fc_set_remoteport_devloss(remoteport,
+ vport->cfg_devloss_tmo);
+#endif
+ }
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
}
/**
@@ -2379,8 +3955,8 @@ lpfc_nodev_tmo_set(struct lpfc_vport *vport, int val)
if (vport->dev_loss_tmo_changed ||
(lpfc_devloss_tmo != LPFC_DEF_DEVLOSS_TMO)) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0401 Ignoring change to nodev_tmo "
- "because devloss_tmo is set.\n");
+ "0401 Ignoring change to lpfc_nodev_tmo "
+ "because lpfc_devloss_tmo is set.\n");
return 0;
}
if (val >= LPFC_MIN_DEVLOSS_TMO && val <= LPFC_MAX_DEVLOSS_TMO) {
@@ -2395,7 +3971,7 @@ lpfc_nodev_tmo_set(struct lpfc_vport *vport, int val)
return 0;
}
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0403 lpfc_nodev_tmo attribute cannot be set to"
+ "0403 lpfc_nodev_tmo attribute cannot be set to "
"%d, allowed range is [%d, %d]\n",
val, LPFC_MIN_DEVLOSS_TMO, LPFC_MAX_DEVLOSS_TMO);
return -EINVAL;
@@ -2403,8 +3979,7 @@ lpfc_nodev_tmo_set(struct lpfc_vport *vport, int val)
lpfc_vport_param_store(nodev_tmo)
-static DEVICE_ATTR(lpfc_nodev_tmo, S_IRUGO | S_IWUSR,
- lpfc_nodev_tmo_show, lpfc_nodev_tmo_store);
+static DEVICE_ATTR_RW(lpfc_nodev_tmo);
/*
# lpfc_devloss_tmo: If set, it will hold all I/O errors on devices that
@@ -2446,15 +4021,53 @@ lpfc_devloss_tmo_set(struct lpfc_vport *vport, int val)
}
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0404 lpfc_devloss_tmo attribute cannot be set to"
- " %d, allowed range is [%d, %d]\n",
+ "0404 lpfc_devloss_tmo attribute cannot be set to "
+ "%d, allowed range is [%d, %d]\n",
val, LPFC_MIN_DEVLOSS_TMO, LPFC_MAX_DEVLOSS_TMO);
return -EINVAL;
}
lpfc_vport_param_store(devloss_tmo)
-static DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR,
- lpfc_devloss_tmo_show, lpfc_devloss_tmo_store);
+static DEVICE_ATTR_RW(lpfc_devloss_tmo);
+
+/*
+ * lpfc_suppress_rsp: Enable suppress rsp feature is firmware supports it
+ * lpfc_suppress_rsp = 0 Disable
+ * lpfc_suppress_rsp = 1 Enable (default)
+ *
+ */
+LPFC_ATTR_R(suppress_rsp, 1, 0, 1,
+ "Enable suppress rsp feature is firmware supports it");
+
+/*
+ * lpfc_nvmet_mrq: Specify number of RQ pairs for processing NVMET cmds
+ * lpfc_nvmet_mrq = 0 driver will calcualte optimal number of RQ pairs
+ * lpfc_nvmet_mrq = 1 use a single RQ pair
+ * lpfc_nvmet_mrq >= 2 use specified RQ pairs for MRQ
+ *
+ */
+LPFC_ATTR_R(nvmet_mrq,
+ LPFC_NVMET_MRQ_AUTO, LPFC_NVMET_MRQ_AUTO, LPFC_NVMET_MRQ_MAX,
+ "Specify number of RQ pairs for processing NVMET cmds");
+
+/*
+ * lpfc_nvmet_mrq_post: Specify number of RQ buffer to initially post
+ * to each NVMET RQ. Range 64 to 2048, default is 512.
+ */
+LPFC_ATTR_R(nvmet_mrq_post,
+ LPFC_NVMET_RQE_DEF_POST, LPFC_NVMET_RQE_MIN_POST,
+ LPFC_NVMET_RQE_DEF_COUNT,
+ "Specify number of RQ buffers to initially post");
+
+/*
+ * lpfc_enable_fc4_type: Defines what FC4 types are supported.
+ * Supported Values: 1 - register just FCP
+ * 3 - register both FCP and NVME
+ * Supported values are [1,3]. Default value is 3
+ */
+LPFC_ATTR_R(enable_fc4_type, LPFC_DEF_ENBL_FC4_TYPE,
+ LPFC_ENABLE_FCP, LPFC_MAX_ENBL_FC4_TYPE,
+ "Enable FC4 Protocol support - FCP / NVME");
/*
# lpfc_log_verbose: Only turn this flag on if you are willing to risk being
@@ -2469,22 +4082,62 @@ LPFC_VPORT_ATTR_HEX_RW(log_verbose, 0x0, 0x0, 0xffffffff,
# lpfc_enable_da_id: This turns on the DA_ID CT command that deregisters
# objects that have been registered with the nameserver after login.
*/
-LPFC_VPORT_ATTR_R(enable_da_id, 0, 0, 1,
+LPFC_VPORT_ATTR_R(enable_da_id, 1, 0, 1,
"Deregister nameserver objects before LOGO");
/*
# lun_queue_depth: This parameter is used to limit the number of outstanding
-# commands per FCP LUN. Value range is [1,128]. Default value is 30.
+# commands per FCP LUN.
*/
-LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 128,
+LPFC_VPORT_ATTR_R(lun_queue_depth, 64, 1, 512,
"Max number of FCP commands we can queue to a specific LUN");
/*
# tgt_queue_depth: This parameter is used to limit the number of outstanding
# commands per target port. Value range is [10,65535]. Default value is 65535.
*/
-LPFC_VPORT_ATTR_R(tgt_queue_depth, 65535, 10, 65535,
- "Max number of FCP commands we can queue to a specific target port");
+static uint lpfc_tgt_queue_depth = LPFC_MAX_TGT_QDEPTH;
+module_param(lpfc_tgt_queue_depth, uint, 0444);
+MODULE_PARM_DESC(lpfc_tgt_queue_depth, "Set max Target queue depth");
+lpfc_vport_param_show(tgt_queue_depth);
+lpfc_vport_param_init(tgt_queue_depth, LPFC_MAX_TGT_QDEPTH,
+ LPFC_MIN_TGT_QDEPTH, LPFC_MAX_TGT_QDEPTH);
+
+/**
+ * lpfc_tgt_queue_depth_set: Sets an attribute value.
+ * @vport: lpfc vport structure pointer.
+ * @val: integer attribute value.
+ *
+ * Description: Sets the parameter to the new value.
+ *
+ * Returns:
+ * zero on success
+ * -EINVAL if val is invalid
+ */
+static int
+lpfc_tgt_queue_depth_set(struct lpfc_vport *vport, uint val)
+{
+ struct lpfc_nodelist *ndlp;
+ unsigned long iflags;
+
+ if (!lpfc_rangecheck(val, LPFC_MIN_TGT_QDEPTH, LPFC_MAX_TGT_QDEPTH))
+ return -EINVAL;
+
+ if (val == vport->cfg_tgt_queue_depth)
+ return 0;
+
+ vport->cfg_tgt_queue_depth = val;
+
+ /* Next loop thru nodelist and change cmd_qdepth */
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp)
+ ndlp->cmd_qdepth = vport->cfg_tgt_queue_depth;
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
+ return 0;
+}
+
+lpfc_vport_param_store(tgt_queue_depth);
+static DEVICE_ATTR_RW(lpfc_tgt_queue_depth);
/*
# hba_queue_depth: This parameter is used to limit the number of outstanding
@@ -2598,8 +4251,7 @@ lpfc_restrict_login_set(struct lpfc_vport *vport, int val)
return 0;
}
lpfc_vport_param_store(restrict_login);
-static DEVICE_ATTR(lpfc_restrict_login, S_IRUGO | S_IWUSR,
- lpfc_restrict_login_show, lpfc_restrict_login_store);
+static DEVICE_ATTR_RW(lpfc_restrict_login);
/*
# Some disk devices have a "select ID" or "select Target" capability.
@@ -2632,11 +4284,15 @@ LPFC_VPORT_ATTR_R(scan_down, 1, 0, 1,
# Set loop mode if you want to run as an NL_Port. Value range is [0,0x6].
# Default value is 0.
*/
+LPFC_ATTR(topology, 0, 0, 6,
+ "Select Fibre Channel topology");
/**
- * lpfc_topology_set - Set the adapters topology field
- * @phba: lpfc_hba pointer.
- * @val: topology value.
+ * lpfc_topology_store - Set the adapters topology field
+ * @dev: class device that is converted into a scsi_host.
+ * @attr:device attribute, not used.
+ * @buf: buffer for passing information.
+ * @count: size of the data buffer.
*
* Description:
* If val is in a valid range then set the adapter's topology field and
@@ -2661,6 +4317,7 @@ lpfc_topology_store(struct device *dev, struct device_attribute *attr,
const char *val_buf = buf;
int err;
uint32_t prev_val;
+ u8 sli_family, if_type;
if (!strncmp(buf, "nolip ", strlen("nolip "))) {
nolip = 1;
@@ -2674,10 +4331,40 @@ lpfc_topology_store(struct device *dev, struct device_attribute *attr,
if (val >= 0 && val <= 6) {
prev_val = phba->cfg_topology;
+ if (phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G &&
+ val == 4) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "3113 Loop mode not supported at speed %d\n",
+ val);
+ return -EINVAL;
+ }
+ /*
+ * The 'topology' is not a configurable parameter if :
+ * - persistent topology enabled
+ * - ASIC_GEN_NUM >= 0xC, with no private loop support
+ */
+ sli_family = bf_get(lpfc_sli_intf_sli_family,
+ &phba->sli4_hba.sli_intf);
+ if_type = bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf);
+ if ((test_bit(HBA_PERSISTENT_TOPO, &phba->hba_flag) ||
+ (!phba->sli4_hba.pc_sli4_params.pls &&
+ (sli_family == LPFC_SLI_INTF_FAMILY_G6 ||
+ if_type == LPFC_SLI_INTF_IF_TYPE_6))) &&
+ val == 4) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "3114 Loop mode not supported\n");
+ return -EINVAL;
+ }
phba->cfg_topology = val;
if (nolip)
return strlen(buf);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "3054 lpfc_topology changed from %d to %d\n",
+ prev_val, val);
+ if (prev_val != val && phba->sli_rev == LPFC_SLI_REV4)
+ phba->fc_topology_changed = 1;
err = lpfc_issue_lip(lpfc_shost_from_vport(phba->pport));
if (err) {
phba->cfg_topology = prev_val;
@@ -2691,13 +4378,9 @@ lpfc_topology_store(struct device *dev, struct device_attribute *attr,
phba->brd_no, val);
return -EINVAL;
}
-static int lpfc_topology = 0;
-module_param(lpfc_topology, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_topology, "Select Fibre Channel topology");
+
lpfc_param_show(topology)
-lpfc_param_init(topology, 0, 0, 6)
-static DEVICE_ATTR(lpfc_topology, S_IRUGO | S_IWUSR,
- lpfc_topology_show, lpfc_topology_store);
+static DEVICE_ATTR_RW(lpfc_topology);
/**
* lpfc_static_vport_show: Read callback function for
@@ -2727,334 +4410,7 @@ lpfc_static_vport_show(struct device *dev, struct device_attribute *attr,
/*
* Sysfs attribute to control the statistical data collection.
*/
-static DEVICE_ATTR(lpfc_static_vport, S_IRUGO,
- lpfc_static_vport_show, NULL);
-
-/**
- * lpfc_stat_data_ctrl_store - write call back for lpfc_stat_data_ctrl sysfs file
- * @dev: Pointer to class device.
- * @buf: Data buffer.
- * @count: Size of the data buffer.
- *
- * This function get called when an user write to the lpfc_stat_data_ctrl
- * sysfs file. This function parse the command written to the sysfs file
- * and take appropriate action. These commands are used for controlling
- * driver statistical data collection.
- * Following are the command this function handles.
- *
- * setbucket <bucket_type> <base> <step>
- * = Set the latency buckets.
- * destroybucket = destroy all the buckets.
- * start = start data collection
- * stop = stop data collection
- * reset = reset the collected data
- **/
-static ssize_t
-lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
-#define LPFC_MAX_DATA_CTRL_LEN 1024
- static char bucket_data[LPFC_MAX_DATA_CTRL_LEN];
- unsigned long i;
- char *str_ptr, *token;
- struct lpfc_vport **vports;
- struct Scsi_Host *v_shost;
- char *bucket_type_str, *base_str, *step_str;
- unsigned long base, step, bucket_type;
-
- if (!strncmp(buf, "setbucket", strlen("setbucket"))) {
- if (strlen(buf) > (LPFC_MAX_DATA_CTRL_LEN - 1))
- return -EINVAL;
-
- strcpy(bucket_data, buf);
- str_ptr = &bucket_data[0];
- /* Ignore this token - this is command token */
- token = strsep(&str_ptr, "\t ");
- if (!token)
- return -EINVAL;
-
- bucket_type_str = strsep(&str_ptr, "\t ");
- if (!bucket_type_str)
- return -EINVAL;
-
- if (!strncmp(bucket_type_str, "linear", strlen("linear")))
- bucket_type = LPFC_LINEAR_BUCKET;
- else if (!strncmp(bucket_type_str, "power2", strlen("power2")))
- bucket_type = LPFC_POWER2_BUCKET;
- else
- return -EINVAL;
-
- base_str = strsep(&str_ptr, "\t ");
- if (!base_str)
- return -EINVAL;
- base = simple_strtoul(base_str, NULL, 0);
-
- step_str = strsep(&str_ptr, "\t ");
- if (!step_str)
- return -EINVAL;
- step = simple_strtoul(step_str, NULL, 0);
- if (!step)
- return -EINVAL;
-
- /* Block the data collection for every vport */
- vports = lpfc_create_vport_work_array(phba);
- if (vports == NULL)
- return -ENOMEM;
-
- for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- v_shost = lpfc_shost_from_vport(vports[i]);
- spin_lock_irq(v_shost->host_lock);
- /* Block and reset data collection */
- vports[i]->stat_data_blocked = 1;
- if (vports[i]->stat_data_enabled)
- lpfc_vport_reset_stat_data(vports[i]);
- spin_unlock_irq(v_shost->host_lock);
- }
-
- /* Set the bucket attributes */
- phba->bucket_type = bucket_type;
- phba->bucket_base = base;
- phba->bucket_step = step;
-
- for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- v_shost = lpfc_shost_from_vport(vports[i]);
-
- /* Unblock data collection */
- spin_lock_irq(v_shost->host_lock);
- vports[i]->stat_data_blocked = 0;
- spin_unlock_irq(v_shost->host_lock);
- }
- lpfc_destroy_vport_work_array(phba, vports);
- return strlen(buf);
- }
-
- if (!strncmp(buf, "destroybucket", strlen("destroybucket"))) {
- vports = lpfc_create_vport_work_array(phba);
- if (vports == NULL)
- return -ENOMEM;
-
- for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- v_shost = lpfc_shost_from_vport(vports[i]);
- spin_lock_irq(shost->host_lock);
- vports[i]->stat_data_blocked = 1;
- lpfc_free_bucket(vport);
- vport->stat_data_enabled = 0;
- vports[i]->stat_data_blocked = 0;
- spin_unlock_irq(shost->host_lock);
- }
- lpfc_destroy_vport_work_array(phba, vports);
- phba->bucket_type = LPFC_NO_BUCKET;
- phba->bucket_base = 0;
- phba->bucket_step = 0;
- return strlen(buf);
- }
-
- if (!strncmp(buf, "start", strlen("start"))) {
- /* If no buckets configured return error */
- if (phba->bucket_type == LPFC_NO_BUCKET)
- return -EINVAL;
- spin_lock_irq(shost->host_lock);
- if (vport->stat_data_enabled) {
- spin_unlock_irq(shost->host_lock);
- return strlen(buf);
- }
- lpfc_alloc_bucket(vport);
- vport->stat_data_enabled = 1;
- spin_unlock_irq(shost->host_lock);
- return strlen(buf);
- }
-
- if (!strncmp(buf, "stop", strlen("stop"))) {
- spin_lock_irq(shost->host_lock);
- if (vport->stat_data_enabled == 0) {
- spin_unlock_irq(shost->host_lock);
- return strlen(buf);
- }
- lpfc_free_bucket(vport);
- vport->stat_data_enabled = 0;
- spin_unlock_irq(shost->host_lock);
- return strlen(buf);
- }
-
- if (!strncmp(buf, "reset", strlen("reset"))) {
- if ((phba->bucket_type == LPFC_NO_BUCKET)
- || !vport->stat_data_enabled)
- return strlen(buf);
- spin_lock_irq(shost->host_lock);
- vport->stat_data_blocked = 1;
- lpfc_vport_reset_stat_data(vport);
- vport->stat_data_blocked = 0;
- spin_unlock_irq(shost->host_lock);
- return strlen(buf);
- }
- return -EINVAL;
-}
-
-
-/**
- * lpfc_stat_data_ctrl_show - Read function for lpfc_stat_data_ctrl sysfs file
- * @dev: Pointer to class device object.
- * @buf: Data buffer.
- *
- * This function is the read call back function for
- * lpfc_stat_data_ctrl sysfs file. This function report the
- * current statistical data collection state.
- **/
-static ssize_t
-lpfc_stat_data_ctrl_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- int index = 0;
- int i;
- char *bucket_type;
- unsigned long bucket_value;
-
- switch (phba->bucket_type) {
- case LPFC_LINEAR_BUCKET:
- bucket_type = "linear";
- break;
- case LPFC_POWER2_BUCKET:
- bucket_type = "power2";
- break;
- default:
- bucket_type = "No Bucket";
- break;
- }
-
- sprintf(&buf[index], "Statistical Data enabled :%d, "
- "blocked :%d, Bucket type :%s, Bucket base :%d,"
- " Bucket step :%d\nLatency Ranges :",
- vport->stat_data_enabled, vport->stat_data_blocked,
- bucket_type, phba->bucket_base, phba->bucket_step);
- index = strlen(buf);
- if (phba->bucket_type != LPFC_NO_BUCKET) {
- for (i = 0; i < LPFC_MAX_BUCKET_COUNT; i++) {
- if (phba->bucket_type == LPFC_LINEAR_BUCKET)
- bucket_value = phba->bucket_base +
- phba->bucket_step * i;
- else
- bucket_value = phba->bucket_base +
- (1 << i) * phba->bucket_step;
-
- if (index + 10 > PAGE_SIZE)
- break;
- sprintf(&buf[index], "%08ld ", bucket_value);
- index = strlen(buf);
- }
- }
- sprintf(&buf[index], "\n");
- return strlen(buf);
-}
-
-/*
- * Sysfs attribute to control the statistical data collection.
- */
-static DEVICE_ATTR(lpfc_stat_data_ctrl, S_IRUGO | S_IWUSR,
- lpfc_stat_data_ctrl_show, lpfc_stat_data_ctrl_store);
-
-/*
- * lpfc_drvr_stat_data: sysfs attr to get driver statistical data.
- */
-
-/*
- * Each Bucket takes 11 characters and 1 new line + 17 bytes WWN
- * for each target.
- */
-#define STAT_DATA_SIZE_PER_TARGET(NUM_BUCKETS) ((NUM_BUCKETS) * 11 + 18)
-#define MAX_STAT_DATA_SIZE_PER_TARGET \
- STAT_DATA_SIZE_PER_TARGET(LPFC_MAX_BUCKET_COUNT)
-
-
-/**
- * sysfs_drvr_stat_data_read - Read function for lpfc_drvr_stat_data attribute
- * @filp: sysfs file
- * @kobj: Pointer to the kernel object
- * @bin_attr: Attribute object
- * @buff: Buffer pointer
- * @off: File offset
- * @count: Buffer size
- *
- * This function is the read call back function for lpfc_drvr_stat_data
- * sysfs file. This function export the statistical data to user
- * applications.
- **/
-static ssize_t
-sysfs_drvr_stat_data_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
-{
- struct device *dev = container_of(kobj, struct device,
- kobj);
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- int i = 0, index = 0;
- unsigned long nport_index;
- struct lpfc_nodelist *ndlp = NULL;
- nport_index = (unsigned long)off /
- MAX_STAT_DATA_SIZE_PER_TARGET;
-
- if (!vport->stat_data_enabled || vport->stat_data_blocked
- || (phba->bucket_type == LPFC_NO_BUCKET))
- return 0;
-
- spin_lock_irq(shost->host_lock);
- list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp) || !ndlp->lat_data)
- continue;
-
- if (nport_index > 0) {
- nport_index--;
- continue;
- }
-
- if ((index + MAX_STAT_DATA_SIZE_PER_TARGET)
- > count)
- break;
-
- if (!ndlp->lat_data)
- continue;
-
- /* Print the WWN */
- sprintf(&buf[index], "%02x%02x%02x%02x%02x%02x%02x%02x:",
- ndlp->nlp_portname.u.wwn[0],
- ndlp->nlp_portname.u.wwn[1],
- ndlp->nlp_portname.u.wwn[2],
- ndlp->nlp_portname.u.wwn[3],
- ndlp->nlp_portname.u.wwn[4],
- ndlp->nlp_portname.u.wwn[5],
- ndlp->nlp_portname.u.wwn[6],
- ndlp->nlp_portname.u.wwn[7]);
-
- index = strlen(buf);
-
- for (i = 0; i < LPFC_MAX_BUCKET_COUNT; i++) {
- sprintf(&buf[index], "%010u,",
- ndlp->lat_data[i].cmd_count);
- index = strlen(buf);
- }
- sprintf(&buf[index], "\n");
- index = strlen(buf);
- }
- spin_unlock_irq(shost->host_lock);
- return index;
-}
-
-static struct bin_attribute sysfs_drvr_stat_data_attr = {
- .attr = {
- .name = "lpfc_drvr_stat_data",
- .mode = S_IRUSR,
- },
- .size = LPFC_MAX_TARGET * MAX_STAT_DATA_SIZE_PER_TARGET,
- .read = sysfs_drvr_stat_data_read,
- .write = NULL,
-};
+static DEVICE_ATTR_RO(lpfc_static_vport);
/*
# lpfc_link_speed: Link speed selection for initializing the Fibre Channel
@@ -3062,9 +4418,11 @@ static struct bin_attribute sysfs_drvr_stat_data_attr = {
# Value range is [0,16]. Default value is 0.
*/
/**
- * lpfc_link_speed_set - Set the adapters link speed
- * @phba: lpfc_hba pointer.
- * @val: link speed value.
+ * lpfc_link_speed_store - Set the adapters link speed
+ * @dev: Pointer to class device.
+ * @attr: Unused.
+ * @buf: Data buffer.
+ * @count: Size of the data buffer.
*
* Description:
* If val is in a valid range then set the adapter's link speed field and
@@ -3089,7 +4447,12 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
int nolip = 0;
const char *val_buf = buf;
int err;
- uint32_t prev_val;
+ uint32_t prev_val, if_type;
+
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2 &&
+ test_bit(HBA_FORCED_LINK_SPEED, &phba->hba_flag))
+ return -EPERM;
if (!strncmp(buf, "nolip ", strlen("nolip "))) {
nolip = 1;
@@ -3101,20 +4464,42 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
if (sscanf(val_buf, "%i", &val) != 1)
return -EINVAL;
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "3055 lpfc_link_speed changed from %d to %d %s\n",
+ phba->cfg_link_speed, val, nolip ? "(nolip)" : "(lip)");
+
if (((val == LPFC_USER_LINK_SPEED_1G) && !(phba->lmt & LMT_1Gb)) ||
((val == LPFC_USER_LINK_SPEED_2G) && !(phba->lmt & LMT_2Gb)) ||
((val == LPFC_USER_LINK_SPEED_4G) && !(phba->lmt & LMT_4Gb)) ||
((val == LPFC_USER_LINK_SPEED_8G) && !(phba->lmt & LMT_8Gb)) ||
((val == LPFC_USER_LINK_SPEED_10G) && !(phba->lmt & LMT_10Gb)) ||
- ((val == LPFC_USER_LINK_SPEED_16G) && !(phba->lmt & LMT_16Gb))) {
+ ((val == LPFC_USER_LINK_SPEED_16G) && !(phba->lmt & LMT_16Gb)) ||
+ ((val == LPFC_USER_LINK_SPEED_32G) && !(phba->lmt & LMT_32Gb)) ||
+ ((val == LPFC_USER_LINK_SPEED_64G) && !(phba->lmt & LMT_64Gb))) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"2879 lpfc_link_speed attribute cannot be set "
"to %d. Speed is not supported by this port.\n",
val);
return -EINVAL;
}
- if ((val >= 0) && (val <= LPFC_USER_LINK_SPEED_MAX) &&
- (LPFC_USER_LINK_SPEED_BITMAP & (1 << val))) {
+ if (val >= LPFC_USER_LINK_SPEED_16G &&
+ phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3112 lpfc_link_speed attribute cannot be set "
+ "to %d. Speed is not supported in loop mode.\n",
+ val);
+ return -EINVAL;
+ }
+
+ switch (val) {
+ case LPFC_USER_LINK_SPEED_AUTO:
+ case LPFC_USER_LINK_SPEED_1G:
+ case LPFC_USER_LINK_SPEED_2G:
+ case LPFC_USER_LINK_SPEED_4G:
+ case LPFC_USER_LINK_SPEED_8G:
+ case LPFC_USER_LINK_SPEED_16G:
+ case LPFC_USER_LINK_SPEED_32G:
+ case LPFC_USER_LINK_SPEED_64G:
prev_val = phba->cfg_link_speed;
phba->cfg_link_speed = val;
if (nolip)
@@ -3124,13 +4509,18 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
if (err) {
phba->cfg_link_speed = prev_val;
return -EINVAL;
- } else
- return strlen(buf);
+ }
+ return strlen(buf);
+ default:
+ break;
}
+
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0469 lpfc_link_speed attribute cannot be set to %d, "
- "allowed values are ["LPFC_LINK_SPEED_STRING"]\n", val);
+ "0469 lpfc_link_speed attribute cannot be set to %d, "
+ "allowed values are [%s]\n",
+ val, LPFC_LINK_SPEED_STRING);
return -EINVAL;
+
}
static int lpfc_link_speed = 0;
@@ -3157,28 +4547,55 @@ lpfc_param_show(link_speed)
static int
lpfc_link_speed_init(struct lpfc_hba *phba, int val)
{
- if ((val >= 0) && (val <= LPFC_USER_LINK_SPEED_MAX) &&
- (LPFC_USER_LINK_SPEED_BITMAP & (1 << val))) {
+ if (val >= LPFC_USER_LINK_SPEED_16G && phba->cfg_topology == 4) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3111 lpfc_link_speed of %d cannot "
+ "support loop mode, setting topology to default.\n",
+ val);
+ phba->cfg_topology = 0;
+ }
+
+ switch (val) {
+ case LPFC_USER_LINK_SPEED_AUTO:
+ case LPFC_USER_LINK_SPEED_1G:
+ case LPFC_USER_LINK_SPEED_2G:
+ case LPFC_USER_LINK_SPEED_4G:
+ case LPFC_USER_LINK_SPEED_8G:
+ case LPFC_USER_LINK_SPEED_16G:
+ case LPFC_USER_LINK_SPEED_32G:
+ case LPFC_USER_LINK_SPEED_64G:
phba->cfg_link_speed = val;
return 0;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0405 lpfc_link_speed attribute cannot "
+ "be set to %d, allowed values are "
+ "["LPFC_LINK_SPEED_STRING"]\n", val);
+ phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO;
+ return -EINVAL;
}
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0405 lpfc_link_speed attribute cannot "
- "be set to %d, allowed values are "
- "["LPFC_LINK_SPEED_STRING"]\n", val);
- phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO;
- return -EINVAL;
}
-static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
- lpfc_link_speed_show, lpfc_link_speed_store);
+static DEVICE_ATTR_RW(lpfc_link_speed);
/*
# lpfc_aer_support: Support PCIe device Advanced Error Reporting (AER)
-# 0 = aer disabled or not supported
# 1 = aer supported and enabled (default)
-# Value range is [0,1]. Default value is 1.
+# PCIe error reporting is always enabled by the PCI core, so this always
+# shows 1.
+#
+# N.B. Parts of LPFC_ATTR open-coded since some of the underlying
+# infrastructure (phba->cfg_aer_support) is gone.
*/
+static uint lpfc_aer_support = 1;
+module_param(lpfc_aer_support, uint, S_IRUGO);
+MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support");
+static ssize_t
+lpfc_aer_support_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", lpfc_aer_support);
+}
/**
* lpfc_aer_support_store - Set the adapter for aer support
@@ -3189,120 +4606,30 @@ static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
* @count: unused variable.
*
* Description:
- * If the val is 1 and currently the device's AER capability was not
- * enabled, invoke the kernel's enable AER helper routine, trying to
- * enable the device's AER capability. If the helper routine enabling
- * AER returns success, update the device's cfg_aer_support flag to
- * indicate AER is supported by the device; otherwise, if the device
- * AER capability is already enabled to support AER, then do nothing.
- *
- * If the val is 0 and currently the device's AER support was enabled,
- * invoke the kernel's disable AER helper routine. After that, update
- * the device's cfg_aer_support flag to indicate AER is not supported
- * by the device; otherwise, if the device AER capability is already
- * disabled from supporting AER, then do nothing.
+ * PCIe error reporting is enabled by the PCI core, so drivers don't need
+ * to do anything. Retain this interface for backwards compatibility,
+ * but do nothing.
*
* Returns:
- * length of the buf on success if val is in range the intended mode
- * is supported.
- * -EINVAL if val out of range or intended mode is not supported.
+ * length of the buf on success
+ * -EINVAL if val out of range
**/
static ssize_t
lpfc_aer_support_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- int val = 0, rc = -EINVAL;
+ int val = 0;
if (!isdigit(buf[0]))
return -EINVAL;
if (sscanf(buf, "%i", &val) != 1)
return -EINVAL;
- switch (val) {
- case 0:
- if (phba->hba_flag & HBA_AER_ENABLED) {
- rc = pci_disable_pcie_error_reporting(phba->pcidev);
- if (!rc) {
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~HBA_AER_ENABLED;
- spin_unlock_irq(&phba->hbalock);
- phba->cfg_aer_support = 0;
- rc = strlen(buf);
- } else
- rc = -EPERM;
- } else {
- phba->cfg_aer_support = 0;
- rc = strlen(buf);
- }
- break;
- case 1:
- if (!(phba->hba_flag & HBA_AER_ENABLED)) {
- rc = pci_enable_pcie_error_reporting(phba->pcidev);
- if (!rc) {
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag |= HBA_AER_ENABLED;
- spin_unlock_irq(&phba->hbalock);
- phba->cfg_aer_support = 1;
- rc = strlen(buf);
- } else
- rc = -EPERM;
- } else {
- phba->cfg_aer_support = 1;
- rc = strlen(buf);
- }
- break;
- default:
- rc = -EINVAL;
- break;
- }
- return rc;
-}
-
-static int lpfc_aer_support = 1;
-module_param(lpfc_aer_support, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support");
-lpfc_param_show(aer_support)
-
-/**
- * lpfc_aer_support_init - Set the initial adapters aer support flag
- * @phba: lpfc_hba pointer.
- * @val: enable aer or disable aer flag.
- *
- * Description:
- * If val is in a valid range [0,1], then set the adapter's initial
- * cfg_aer_support field. It will be up to the driver's probe_one
- * routine to determine whether the device's AER support can be set
- * or not.
- *
- * Notes:
- * If the value is not in range log a kernel error message, and
- * choose the default value of setting AER support and return.
- *
- * Returns:
- * zero if val saved.
- * -EINVAL val out of range
- **/
-static int
-lpfc_aer_support_init(struct lpfc_hba *phba, int val)
-{
- if (val == 0 || val == 1) {
- phba->cfg_aer_support = val;
- return 0;
- }
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2712 lpfc_aer_support attribute value %d out "
- "of range, allowed values are 0|1, setting it "
- "to default value of 1\n", val);
- /* By default, try to enable AER on a device */
- phba->cfg_aer_support = 1;
- return -EINVAL;
+ dev_info_once(dev, "PCIe error reporting automatically enabled by the PCI core; sysfs write ignored\n");
+ return strlen(buf);
}
-static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR,
- lpfc_aer_support_show, lpfc_aer_support_store);
+static DEVICE_ATTR_RW(lpfc_aer_support);
/**
* lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device
@@ -3312,16 +4639,16 @@ static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR,
* @count: unused variable.
*
* Description:
- * If the @buf contains 1 and the device currently has the AER support
- * enabled, then invokes the kernel AER helper routine
- * pci_cleanup_aer_uncorrect_error_status to clean up the uncorrectable
+ * If the @buf contains 1, invokes the kernel AER helper routine
+ * pci_aer_clear_nonfatal_status() to clean up the uncorrectable
* error status register.
*
* Notes:
*
* Returns:
- * -EINVAL if the buf does not contain the 1 or the device is not currently
- * enabled with the AER support.
+ * -EINVAL if the buf does not contain 1
+ * -EPERM if the OS cannot clear AER error status, i.e., when platform
+ * firmware owns the AER Capability
**/
static ssize_t
lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr,
@@ -3339,8 +4666,7 @@ lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr,
if (val != 1)
return -EINVAL;
- if (phba->hba_flag & HBA_AER_ENABLED)
- rc = pci_cleanup_aer_uncorrect_error_status(phba->pcidev);
+ rc = pci_aer_clear_nonfatal_status(phba->pcidev);
if (rc == 0)
return strlen(buf);
@@ -3445,41 +4771,552 @@ lpfc_sriov_nr_virtfn_store(struct device *dev, struct device_attribute *attr,
return rc;
}
-static int lpfc_sriov_nr_virtfn = LPFC_DEF_VFN_PER_PFN;
-module_param(lpfc_sriov_nr_virtfn, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(lpfc_sriov_nr_virtfn, "Enable PCIe device SR-IOV virtual fn");
+LPFC_ATTR(sriov_nr_virtfn, LPFC_DEF_VFN_PER_PFN, 0, LPFC_MAX_VFN_PER_PFN,
+ "Enable PCIe device SR-IOV virtual fn");
+
lpfc_param_show(sriov_nr_virtfn)
+static DEVICE_ATTR_RW(lpfc_sriov_nr_virtfn);
/**
- * lpfc_sriov_nr_virtfn_init - Set the initial sr-iov virtual function enable
+ * lpfc_request_firmware_upgrade_store - Request for Linux generic firmware upgrade
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string the number of vfs to be enabled.
+ * @count: unused variable.
+ *
+ * Description:
+ *
+ * Returns:
+ * length of the buf on success if val is in range the intended mode
+ * is supported.
+ * -EINVAL if val out of range or intended mode is not supported.
+ **/
+static ssize_t
+lpfc_request_firmware_upgrade_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int val = 0, rc;
+
+ /* Sanity check on user data */
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
+ if (val != 1)
+ return -EINVAL;
+
+ rc = lpfc_sli4_request_firmware_update(phba, RUN_FW_UPGRADE);
+ if (rc)
+ rc = -EPERM;
+ else
+ rc = strlen(buf);
+ return rc;
+}
+
+static int lpfc_req_fw_upgrade;
+module_param(lpfc_req_fw_upgrade, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(lpfc_req_fw_upgrade, "Enable Linux generic firmware upgrade");
+lpfc_param_show(request_firmware_upgrade)
+
+/**
+ * lpfc_request_firmware_upgrade_init - Enable initial linux generic fw upgrade
+ * @phba: lpfc_hba pointer.
+ * @val: 0 or 1.
+ *
+ * Description:
+ * Set the initial Linux generic firmware upgrade enable or disable flag.
+ *
+ * Returns:
+ * zero if val saved.
+ * -EINVAL val out of range
+ **/
+static int
+lpfc_request_firmware_upgrade_init(struct lpfc_hba *phba, int val)
+{
+ if (val >= 0 && val <= 1) {
+ phba->cfg_request_firmware_upgrade = val;
+ return 0;
+ }
+ return -EINVAL;
+}
+static DEVICE_ATTR(lpfc_req_fw_upgrade, S_IRUGO | S_IWUSR,
+ lpfc_request_firmware_upgrade_show,
+ lpfc_request_firmware_upgrade_store);
+
+/**
+ * lpfc_force_rscn_store
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: unused string
+ * @count: unused variable.
+ *
+ * Description:
+ * Force the switch to send a RSCN to all other NPorts in our zone
+ * If we are direct connect pt2pt, build the RSCN command ourself
+ * and send to the other NPort. Not supported for private loop.
+ *
+ * Returns:
+ * 0 - on success
+ * -EIO - if command is not sent
+ **/
+static ssize_t
+lpfc_force_rscn_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ int i;
+
+ i = lpfc_issue_els_rscn(vport, 0);
+ if (i)
+ return -EIO;
+ return strlen(buf);
+}
+
+/*
+ * lpfc_force_rscn: Force an RSCN to be sent to all remote NPorts
+ * connected to the HBA.
+ *
+ * Value range is any ascii value
+ */
+static int lpfc_force_rscn;
+module_param(lpfc_force_rscn, int, 0644);
+MODULE_PARM_DESC(lpfc_force_rscn,
+ "Force an RSCN to be sent to all remote NPorts");
+lpfc_param_show(force_rscn)
+
+/**
+ * lpfc_force_rscn_init - Force an RSCN to be sent to all remote NPorts
+ * @phba: lpfc_hba pointer.
+ * @val: unused value.
+ *
+ * Returns:
+ * zero if val saved.
+ **/
+static int
+lpfc_force_rscn_init(struct lpfc_hba *phba, int val)
+{
+ return 0;
+}
+static DEVICE_ATTR_RW(lpfc_force_rscn);
+
+/**
+ * lpfc_fcp_imax_store
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: string with the number of fast-path FCP interrupts per second.
+ * @count: unused variable.
+ *
+ * Description:
+ * If val is in a valid range [636,651042], then set the adapter's
+ * maximum number of fast-path FCP interrupts per second.
+ *
+ * Returns:
+ * length of the buf on success if val is in range the intended mode
+ * is supported.
+ * -EINVAL if val out of range or intended mode is not supported.
+ **/
+static ssize_t
+lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_eq_intr_info *eqi;
+ uint32_t usdelay;
+ int val = 0, i;
+
+ /* fcp_imax is only valid for SLI4 */
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return -EINVAL;
+
+ /* Sanity check on user data */
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
+
+ /*
+ * Value range for the HBA is [5000,5000000]
+ * The value for each EQ depends on how many EQs are configured.
+ * Allow value == 0
+ */
+ if (val && (val < LPFC_MIN_IMAX || val > LPFC_MAX_IMAX))
+ return -EINVAL;
+
+ phba->cfg_auto_imax = (val) ? 0 : 1;
+ if (phba->cfg_fcp_imax && !val) {
+ queue_delayed_work(phba->wq, &phba->eq_delay_work,
+ msecs_to_jiffies(LPFC_EQ_DELAY_MSECS));
+
+ for_each_present_cpu(i) {
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, i);
+ eqi->icnt = 0;
+ }
+ }
+
+ phba->cfg_fcp_imax = (uint32_t)val;
+
+ if (phba->cfg_fcp_imax)
+ usdelay = LPFC_SEC_TO_USEC / phba->cfg_fcp_imax;
+ else
+ usdelay = 0;
+
+ for (i = 0; i < phba->cfg_irq_chann; i += LPFC_MAX_EQ_DELAY_EQID_CNT)
+ lpfc_modify_hba_eq_delay(phba, i, LPFC_MAX_EQ_DELAY_EQID_CNT,
+ usdelay);
+
+ return strlen(buf);
+}
+
+/*
+# lpfc_fcp_imax: The maximum number of fast-path FCP interrupts per second
+# for the HBA.
+#
+# Value range is [5,000 to 5,000,000]. Default value is 50,000.
+*/
+static int lpfc_fcp_imax = LPFC_DEF_IMAX;
+module_param(lpfc_fcp_imax, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(lpfc_fcp_imax,
+ "Set the maximum number of FCP interrupts per second per HBA");
+lpfc_param_show(fcp_imax)
+
+/**
+ * lpfc_fcp_imax_init - Set the initial sr-iov virtual function enable
* @phba: lpfc_hba pointer.
* @val: link speed value.
*
* Description:
- * If val is in a valid range [0,255], then set the adapter's initial
- * cfg_sriov_nr_virtfn field. If it's greater than the maximum, the maximum
- * number shall be used instead. It will be up to the driver's probe_one
- * routine to determine whether the device's SR-IOV is supported or not.
+ * If val is in a valid range [636,651042], then initialize the adapter's
+ * maximum number of fast-path FCP interrupts per second.
*
* Returns:
* zero if val saved.
* -EINVAL val out of range
**/
static int
-lpfc_sriov_nr_virtfn_init(struct lpfc_hba *phba, int val)
+lpfc_fcp_imax_init(struct lpfc_hba *phba, int val)
{
- if (val >= 0 && val <= LPFC_MAX_VFN_PER_PFN) {
- phba->cfg_sriov_nr_virtfn = val;
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ phba->cfg_fcp_imax = 0;
+ return 0;
+ }
+
+ if ((val >= LPFC_MIN_IMAX && val <= LPFC_MAX_IMAX) ||
+ (val == 0)) {
+ phba->cfg_fcp_imax = val;
return 0;
}
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3017 Enabling %d virtual functions is not "
- "allowed.\n", val);
+ "3016 lpfc_fcp_imax: %d out of range, using default\n",
+ val);
+ phba->cfg_fcp_imax = LPFC_DEF_IMAX;
+
+ return 0;
+}
+
+static DEVICE_ATTR_RW(lpfc_fcp_imax);
+
+/**
+ * lpfc_cq_max_proc_limit_store
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: string with the cq max processing limit of cqes
+ * @count: unused variable.
+ *
+ * Description:
+ * If val is in a valid range, then set value on each cq
+ *
+ * Returns:
+ * The length of the buf: if successful
+ * -ERANGE: if val is not in the valid range
+ * -EINVAL: if bad value format or intended mode is not supported.
+ **/
+static ssize_t
+lpfc_cq_max_proc_limit_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_queue *eq, *cq;
+ unsigned long val;
+ int i;
+
+ /* cq_max_proc_limit is only valid for SLI4 */
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return -EINVAL;
+
+ /* Sanity check on user data */
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val < LPFC_CQ_MIN_PROC_LIMIT || val > LPFC_CQ_MAX_PROC_LIMIT)
+ return -ERANGE;
+
+ phba->cfg_cq_max_proc_limit = (uint32_t)val;
+
+ /* set the values on the cq's */
+ for (i = 0; i < phba->cfg_irq_chann; i++) {
+ /* Get the EQ corresponding to the IRQ vector */
+ eq = phba->sli4_hba.hba_eq_hdl[i].eq;
+ if (!eq)
+ continue;
+
+ list_for_each_entry(cq, &eq->child_list, list)
+ cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit,
+ cq->entry_count);
+ }
+
+ return strlen(buf);
+}
+
+/*
+ * lpfc_cq_max_proc_limit: The maximum number CQE entries processed in an
+ * itteration of CQ processing.
+ */
+static int lpfc_cq_max_proc_limit = LPFC_CQ_DEF_MAX_PROC_LIMIT;
+module_param(lpfc_cq_max_proc_limit, int, 0644);
+MODULE_PARM_DESC(lpfc_cq_max_proc_limit,
+ "Set the maximum number CQEs processed in an iteration of "
+ "CQ processing");
+lpfc_param_show(cq_max_proc_limit)
+
+/*
+ * lpfc_cq_poll_threshold: Set the threshold of CQE completions in a
+ * single handler call which should request a polled completion rather
+ * than re-enabling interrupts.
+ */
+LPFC_ATTR_RW(cq_poll_threshold, LPFC_CQ_DEF_THRESHOLD_TO_POLL,
+ LPFC_CQ_MIN_THRESHOLD_TO_POLL,
+ LPFC_CQ_MAX_THRESHOLD_TO_POLL,
+ "CQE Processing Threshold to enable Polling");
+
+/**
+ * lpfc_cq_max_proc_limit_init - Set the initial cq max_proc_limit
+ * @phba: lpfc_hba pointer.
+ * @val: entry limit
+ *
+ * Description:
+ * If val is in a valid range, then initialize the adapter's maximum
+ * value.
+ *
+ * Returns:
+ * Always returns 0 for success, even if value not always set to
+ * requested value. If value out of range or not supported, will fall
+ * back to default.
+ **/
+static int
+lpfc_cq_max_proc_limit_init(struct lpfc_hba *phba, int val)
+{
+ phba->cfg_cq_max_proc_limit = LPFC_CQ_DEF_MAX_PROC_LIMIT;
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return 0;
+
+ if (val >= LPFC_CQ_MIN_PROC_LIMIT && val <= LPFC_CQ_MAX_PROC_LIMIT) {
+ phba->cfg_cq_max_proc_limit = val;
+ return 0;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0371 lpfc_cq_max_proc_limit: %d out of range, using "
+ "default\n",
+ phba->cfg_cq_max_proc_limit);
+
+ return 0;
+}
+
+static DEVICE_ATTR_RW(lpfc_cq_max_proc_limit);
+
+/**
+ * lpfc_fcp_cpu_map_show - Display current driver CPU affinity
+ * @dev: class converted to a Scsi_host structure.
+ * @attr: device attribute, not used.
+ * @buf: on return contains text describing the state of the link.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_vector_map_info *cpup;
+ int len = 0;
+
+ if ((phba->sli_rev != LPFC_SLI_REV4) ||
+ (phba->intr_type != MSIX))
+ return len;
+
+ switch (phba->cfg_fcp_cpu_map) {
+ case 0:
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "fcp_cpu_map: No mapping (%d)\n",
+ phba->cfg_fcp_cpu_map);
+ return len;
+ case 1:
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "fcp_cpu_map: HBA centric mapping (%d): "
+ "%d of %d CPUs online from %d possible CPUs\n",
+ phba->cfg_fcp_cpu_map, num_online_cpus(),
+ num_present_cpus(),
+ phba->sli4_hba.num_possible_cpu);
+ break;
+ }
+
+ while (phba->sli4_hba.curr_disp_cpu <
+ phba->sli4_hba.num_possible_cpu) {
+ cpup = &phba->sli4_hba.cpu_map[phba->sli4_hba.curr_disp_cpu];
+
+ if (!cpu_present(phba->sli4_hba.curr_disp_cpu))
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "CPU %02d not present\n",
+ phba->sli4_hba.curr_disp_cpu);
+ else if (cpup->eq == LPFC_VECTOR_MAP_EMPTY) {
+ if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY)
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d hdwq None "
+ "physid %d coreid %d ht %d ua %d\n",
+ phba->sli4_hba.curr_disp_cpu,
+ cpup->phys_id, cpup->core_id,
+ (cpup->flag & LPFC_CPU_MAP_HYPER),
+ (cpup->flag & LPFC_CPU_MAP_UNASSIGN));
+ else
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d EQ None hdwq %04d "
+ "physid %d coreid %d ht %d ua %d\n",
+ phba->sli4_hba.curr_disp_cpu,
+ cpup->hdwq, cpup->phys_id,
+ cpup->core_id,
+ (cpup->flag & LPFC_CPU_MAP_HYPER),
+ (cpup->flag & LPFC_CPU_MAP_UNASSIGN));
+ } else {
+ if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY)
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d hdwq None "
+ "physid %d coreid %d ht %d ua %d IRQ %d\n",
+ phba->sli4_hba.curr_disp_cpu,
+ cpup->phys_id,
+ cpup->core_id,
+ (cpup->flag & LPFC_CPU_MAP_HYPER),
+ (cpup->flag & LPFC_CPU_MAP_UNASSIGN),
+ lpfc_get_irq(cpup->eq));
+ else
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "CPU %02d EQ %04d hdwq %04d "
+ "physid %d coreid %d ht %d ua %d IRQ %d\n",
+ phba->sli4_hba.curr_disp_cpu,
+ cpup->eq, cpup->hdwq, cpup->phys_id,
+ cpup->core_id,
+ (cpup->flag & LPFC_CPU_MAP_HYPER),
+ (cpup->flag & LPFC_CPU_MAP_UNASSIGN),
+ lpfc_get_irq(cpup->eq));
+ }
+
+ phba->sli4_hba.curr_disp_cpu++;
+
+ /* display max number of CPUs keeping some margin */
+ if (phba->sli4_hba.curr_disp_cpu <
+ phba->sli4_hba.num_possible_cpu &&
+ (len >= (PAGE_SIZE - 64))) {
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len, "more...\n");
+ break;
+ }
+ }
+
+ if (phba->sli4_hba.curr_disp_cpu == phba->sli4_hba.num_possible_cpu)
+ phba->sli4_hba.curr_disp_cpu = 0;
+
+ return len;
+}
+
+/**
+ * lpfc_fcp_cpu_map_store - Change CPU affinity of driver vectors
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: one or more lpfc_polling_flags values.
+ * @count: not used.
+ *
+ * Returns:
+ * -EINVAL - Not implemented yet.
+ **/
+static ssize_t
+lpfc_fcp_cpu_map_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
return -EINVAL;
}
-static DEVICE_ATTR(lpfc_sriov_nr_virtfn, S_IRUGO | S_IWUSR,
- lpfc_sriov_nr_virtfn_show, lpfc_sriov_nr_virtfn_store);
+
+/*
+# lpfc_fcp_cpu_map: Defines how to map CPUs to IRQ vectors
+# for the HBA.
+#
+# Value range is [0 to 1]. Default value is LPFC_HBA_CPU_MAP (1).
+# 0 - Do not affinitze IRQ vectors
+# 1 - Affintize HBA vectors with respect to each HBA
+# (start with CPU0 for each HBA)
+# This also defines how Hardware Queues are mapped to specific CPUs.
+*/
+static int lpfc_fcp_cpu_map = LPFC_HBA_CPU_MAP;
+module_param(lpfc_fcp_cpu_map, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(lpfc_fcp_cpu_map,
+ "Defines how to map CPUs to IRQ vectors per HBA");
+
+/**
+ * lpfc_fcp_cpu_map_init - Set the initial sr-iov virtual function enable
+ * @phba: lpfc_hba pointer.
+ * @val: link speed value.
+ *
+ * Description:
+ * If val is in a valid range [0-2], then affinitze the adapter's
+ * MSIX vectors.
+ *
+ * Returns:
+ * zero if val saved.
+ * -EINVAL val out of range
+ **/
+static int
+lpfc_fcp_cpu_map_init(struct lpfc_hba *phba, int val)
+{
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ phba->cfg_fcp_cpu_map = 0;
+ return 0;
+ }
+
+ if (val >= LPFC_MIN_CPU_MAP && val <= LPFC_MAX_CPU_MAP) {
+ phba->cfg_fcp_cpu_map = val;
+ return 0;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3326 lpfc_fcp_cpu_map: %d out of range, using "
+ "default\n", val);
+ phba->cfg_fcp_cpu_map = LPFC_HBA_CPU_MAP;
+
+ return 0;
+}
+
+static DEVICE_ATTR_RW(lpfc_fcp_cpu_map);
/*
# lpfc_fcp_class: Determines FC class to use for the FCP protocol.
@@ -3490,12 +5327,43 @@ LPFC_VPORT_ATTR_R(fcp_class, 3, 2, 3,
/*
# lpfc_use_adisc: Use ADISC for FCP rediscovery instead of PLOGI. Value range
-# is [0,1]. Default value is 0.
+# is [0,1]. Default value is 1.
*/
-LPFC_VPORT_ATTR_RW(use_adisc, 0, 0, 1,
+LPFC_VPORT_ATTR_RW(use_adisc, 1, 0, 1,
"Use ADISC on rediscovery to authenticate FCP devices");
/*
+# lpfc_first_burst_size: First burst size to use on the NPorts
+# that support first burst.
+# Value range is [0,65536]. Default value is 0.
+*/
+LPFC_VPORT_ATTR_RW(first_burst_size, 0, 0, 65536,
+ "First burst size for Targets that support first burst");
+
+/*
+* lpfc_nvmet_fb_size: NVME Target mode supported first burst size.
+* When the driver is configured as an NVME target, this value is
+* communicated to the NVME initiator in the PRLI response. It is
+* used only when the lpfc_nvme_enable_fb and lpfc_nvmet_support
+* parameters are set and the target is sending the PRLI RSP.
+* Parameter supported on physical port only - no NPIV support.
+* Value range is [0,65536]. Default value is 0.
+*/
+LPFC_ATTR_RW(nvmet_fb_size, 0, 0, 65536,
+ "NVME Target mode first burst size in 512B increments.");
+
+/*
+ * lpfc_nvme_enable_fb: Enable NVME first burst on I and T functions.
+ * For the Initiator (I), enabling this parameter means that an NVMET
+ * PRLI response with FBA enabled and an FB_SIZE set to a nonzero value will be
+ * processed by the initiator for subsequent NVME FCP IO.
+ * Currently, this feature is not supported on the NVME target
+ * Value range is [0,1]. Default value is 0 (disabled).
+ */
+LPFC_ATTR_RW(nvme_enable_fb, 0, 0, 1,
+ "Enable First Burst feature for NVME Initiator.");
+
+/*
# lpfc_max_scsicmpl_time: Use scsi command completion time to control I/O queue
# depth. Default value is 0. When the value of this parameter is zero the
# SCSI command completion time is not used for controlling I/O queue depth. When
@@ -3503,17 +5371,15 @@ LPFC_VPORT_ATTR_RW(use_adisc, 0, 0, 1,
# to limit the I/O completion time to the parameter value.
# The value is set in milliseconds.
*/
-static int lpfc_max_scsicmpl_time;
-module_param(lpfc_max_scsicmpl_time, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_max_scsicmpl_time,
+LPFC_VPORT_ATTR(max_scsicmpl_time, 0, 0, 60000,
"Use command completion time to control queue depth");
+
lpfc_vport_param_show(max_scsicmpl_time);
-lpfc_vport_param_init(max_scsicmpl_time, 0, 0, 60000);
static int
lpfc_max_scsicmpl_time_set(struct lpfc_vport *vport, int val)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp, *next_ndlp;
+ unsigned long iflags;
if (val == vport->cfg_max_scsicmpl_time)
return 0;
@@ -3521,21 +5387,17 @@ lpfc_max_scsicmpl_time_set(struct lpfc_vport *vport, int val)
return -EINVAL;
vport->cfg_max_scsicmpl_time = val;
- spin_lock_irq(shost->host_lock);
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
continue;
ndlp->cmd_qdepth = vport->cfg_tgt_queue_depth;
}
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
return 0;
}
lpfc_vport_param_store(max_scsicmpl_time);
-static DEVICE_ATTR(lpfc_max_scsicmpl_time, S_IRUGO | S_IWUSR,
- lpfc_max_scsicmpl_time_show,
- lpfc_max_scsicmpl_time_store);
+static DEVICE_ATTR_RW(lpfc_max_scsicmpl_time);
/*
# lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value
@@ -3544,6 +5406,56 @@ static DEVICE_ATTR(lpfc_max_scsicmpl_time, S_IRUGO | S_IWUSR,
LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support");
/*
+# lpfc_xri_rebalancing: enable or disable XRI rebalancing feature
+# range is [0,1]. Default value is 1.
+*/
+LPFC_ATTR_R(xri_rebalancing, 1, 0, 1, "Enable/Disable XRI rebalancing");
+
+/*
+ * lpfc_io_sched: Determine scheduling algrithmn for issuing FCP cmds
+ * range is [0,1]. Default value is 0.
+ * For [0], FCP commands are issued to Work Queues based on upper layer
+ * hardware queue index.
+ * For [1], FCP commands are issued to a Work Queue associated with the
+ * current CPU.
+ *
+ * LPFC_FCP_SCHED_BY_HDWQ == 0
+ * LPFC_FCP_SCHED_BY_CPU == 1
+ *
+ * The driver dynamically sets this to 1 (BY_CPU) if it's able to set up cpu
+ * affinity for FCP/NVME I/Os through Work Queues associated with the current
+ * CPU. Otherwise, the default 0 (Round Robin) scheduling of FCP/NVME I/Os
+ * through WQs will be used.
+ */
+LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_BY_CPU,
+ LPFC_FCP_SCHED_BY_HDWQ,
+ LPFC_FCP_SCHED_BY_CPU,
+ "Determine scheduling algorithm for "
+ "issuing commands [0] - Hardware Queue, [1] - Current CPU");
+
+/*
+ * lpfc_ns_query: Determine algrithmn for NameServer queries after RSCN
+ * range is [0,1]. Default value is 0.
+ * For [0], GID_FT is used for NameServer queries after RSCN (default)
+ * For [1], GID_PT is used for NameServer queries after RSCN
+ *
+ */
+LPFC_ATTR_RW(ns_query, LPFC_NS_QUERY_GID_FT,
+ LPFC_NS_QUERY_GID_FT, LPFC_NS_QUERY_GID_PT,
+ "Determine algorithm NameServer queries after RSCN "
+ "[0] - GID_FT, [1] - GID_PT");
+
+/*
+# lpfc_fcp2_no_tgt_reset: Determine bus reset behavior
+# range is [0,1]. Default value is 0.
+# For [0], bus reset issues target reset to ALL devices
+# For [1], bus reset issues target reset to non-FCP2 devices
+*/
+LPFC_ATTR_RW(fcp2_no_tgt_reset, 0, 0, 1, "Determine bus reset behavior for "
+ "FCP2 devices [0] - issue tgt reset, [1] - no tgt reset");
+
+
+/*
# lpfc_cr_delay & lpfc_cr_count: Default values for I/O colaesing
# cr_delay (msec) or cr_count outstanding commands. cr_delay can take
# value [0,63]. cr_count can take value [1,255]. Default value of cr_delay
@@ -3581,13 +5493,26 @@ LPFC_ATTR_R(multi_ring_type, FC_TYPE_IP, 1,
255, "Identifies TYPE for additional ring configuration");
/*
-# lpfc_fdmi_on: controls FDMI support.
-# 0 = no FDMI support
-# 1 = support FDMI without attribute of hostname
-# 2 = support FDMI with attribute of hostname
-# Value range [0,2]. Default value is 0.
+# lpfc_enable_SmartSAN: Sets up FDMI support for SmartSAN
+# 0 = SmartSAN functionality disabled (default)
+# 1 = SmartSAN functionality enabled
+# This parameter will override the value of lpfc_fdmi_on module parameter.
+# Value range is [0,1]. Default value is 0.
*/
-LPFC_VPORT_ATTR_RW(fdmi_on, 0, 0, 2, "Enable FDMI support");
+LPFC_ATTR_R(enable_SmartSAN, 0, 0, 1, "Enable SmartSAN functionality");
+
+/*
+# lpfc_fdmi_on: Controls FDMI support.
+# 0 No FDMI support
+# 1 Traditional FDMI support (default)
+# Traditional FDMI support means the driver will assume FDMI-2 support;
+# however, if that fails, it will fallback to FDMI-1.
+# If lpfc_enable_SmartSAN is set to 1, the driver ignores lpfc_fdmi_on.
+# If lpfc_enable_SmartSAN is set 0, the driver uses the current value of
+# lpfc_fdmi_on.
+# Value range [0,1]. Default value is 1.
+*/
+LPFC_ATTR_R(fdmi_on, 1, 0, 1, "Enable FDMI support");
/*
# Specifies the maximum number of ELS cmds we can have outstanding (for
@@ -3597,11 +5522,28 @@ LPFC_VPORT_ATTR(discovery_threads, 32, 1, 64, "Maximum number of ELS commands "
"during discovery");
/*
-# lpfc_max_luns: maximum allowed LUN.
+# lpfc_max_luns: maximum allowed LUN ID. This is the highest LUN ID that
+# will be scanned by the SCSI midlayer when sequential scanning is
+# used; and is also the highest LUN ID allowed when the SCSI midlayer
+# parses REPORT_LUN responses. The lpfc driver has no LUN count or
+# LUN ID limit, but the SCSI midlayer requires this field for the uses
+# above. The lpfc driver limits the default value to 255 for two reasons.
+# As it bounds the sequential scan loop, scanning for thousands of luns
+# on a target can take minutes of wall clock time. Additionally,
+# there are FC targets, such as JBODs, that only recognize 8-bits of
+# LUN ID. When they receive a value greater than 8 bits, they chop off
+# the high order bits. In other words, they see LUN IDs 0, 256, 512,
+# and so on all as LUN ID 0. This causes the linux kernel, which sees
+# valid responses at each of the LUN IDs, to believe there are multiple
+# devices present, when in fact, there is only 1.
+# A customer that is aware of their target behaviors, and the results as
+# indicated above, is welcome to increase the lpfc_max_luns value.
+# As mentioned, this value is not used by the lpfc driver, only the
+# SCSI midlayer.
# Value range is [0,65535]. Default value is 255.
# NOTE: The SCSI layer might probe all allowed LUN on some old targets.
*/
-LPFC_VPORT_ATTR_R(max_luns, 255, 0, 65535, "Maximum allowed LUN");
+LPFC_VPORT_ULL_ATTR_R(max_luns, 255, 0, 65535, "Maximum allowed LUN ID");
/*
# lpfc_poll_tmo: .Milliseconds driver will wait between polling FCP ring.
@@ -3611,6 +5553,12 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255,
"Milliseconds driver will wait between polling FCP ring");
/*
+# lpfc_task_mgmt_tmo: Maximum time to wait for task management commands
+# to complete in seconds. Value range is [5,180], default value is 60.
+*/
+LPFC_ATTR_RW(task_mgmt_tmo, 60, 5, 180,
+ "Maximum time to wait for task management commands to complete");
+/*
# lpfc_use_msi: Use MSI (Message Signaled Interrupts) in systems that
# support this feature
# 0 = MSI disabled
@@ -3622,36 +5570,253 @@ LPFC_ATTR_R(use_msi, 2, 0, 2, "Use Message Signaled Interrupts (1) or "
"MSI-X (2), if possible");
/*
-# lpfc_fcp_imax: Set the maximum number of fast-path FCP interrupts per second
-#
-# Value range is [636,651042]. Default value is 10000.
-*/
-LPFC_ATTR_R(fcp_imax, LPFC_FP_DEF_IMAX, LPFC_MIM_IMAX, LPFC_DMULT_CONST,
- "Set the maximum number of fast-path FCP interrupts per second");
+ * lpfc_nvme_oas: Use the oas bit when sending NVME/NVMET IOs
+ *
+ * 0 = NVME OAS disabled
+ * 1 = NVME OAS enabled
+ *
+ * Value range is [0,1]. Default value is 0.
+ */
+LPFC_ATTR_RW(nvme_oas, 0, 0, 1,
+ "Use OAS bit on NVME IOs");
/*
-# lpfc_fcp_wq_count: Set the number of fast-path FCP work queues
-#
-# Value range is [1,31]. Default value is 4.
-*/
-LPFC_ATTR_R(fcp_wq_count, LPFC_FP_WQN_DEF, LPFC_FP_WQN_MIN, LPFC_FP_WQN_MAX,
- "Set the number of fast-path FCP work queues, if possible");
+ * lpfc_nvme_embed_cmd: Use the oas bit when sending NVME/NVMET IOs
+ *
+ * 0 = Put NVME Command in SGL
+ * 1 = Embed NVME Command in WQE (unless G7)
+ * 2 = Embed NVME Command in WQE (force)
+ *
+ * Value range is [0,2]. Default value is 1.
+ */
+LPFC_ATTR_RW(nvme_embed_cmd, 1, 0, 2,
+ "Embed NVME Command in WQE");
/*
-# lpfc_fcp_eq_count: Set the number of fast-path FCP event queues
-#
-# Value range is [1,7]. Default value is 1.
-*/
-LPFC_ATTR_R(fcp_eq_count, LPFC_FP_EQN_DEF, LPFC_FP_EQN_MIN, LPFC_FP_EQN_MAX,
- "Set the number of fast-path FCP event queues, if possible");
+ * lpfc_fcp_mq_threshold: Set the maximum number of Hardware Queues
+ * the driver will advertise it supports to the SCSI layer.
+ *
+ * 0 = Set nr_hw_queues by the number of CPUs or HW queues.
+ * 1,256 = Manually specify nr_hw_queue value to be advertised,
+ *
+ * Value range is [0,256]. Default value is 8.
+ */
+LPFC_ATTR_R(fcp_mq_threshold, LPFC_FCP_MQ_THRESHOLD_DEF,
+ LPFC_FCP_MQ_THRESHOLD_MIN, LPFC_FCP_MQ_THRESHOLD_MAX,
+ "Set the number of SCSI Queues advertised");
+
+/*
+ * lpfc_hdw_queue: Set the number of Hardware Queues the driver
+ * will advertise it supports to the NVME and SCSI layers. This also
+ * will map to the number of CQ/WQ pairs the driver will create.
+ *
+ * The NVME Layer will try to create this many, plus 1 administrative
+ * hardware queue. The administrative queue will always map to WQ 0
+ * A hardware IO queue maps (qidx) to a specific driver CQ/WQ.
+ *
+ * 0 = Configure the number of hdw queues to the number of active CPUs.
+ * 1,256 = Manually specify how many hdw queues to use.
+ *
+ * Value range is [0,256]. Default value is 0.
+ */
+LPFC_ATTR_R(hdw_queue,
+ LPFC_HBA_HDWQ_DEF,
+ LPFC_HBA_HDWQ_MIN, LPFC_HBA_HDWQ_MAX,
+ "Set the number of I/O Hardware Queues");
+
+#if IS_ENABLED(CONFIG_X86)
+/**
+ * lpfc_cpumask_irq_mode_init - initalizes cpumask of phba based on
+ * irq_chann_mode
+ * @phba: Pointer to HBA context object.
+ **/
+static void
+lpfc_cpumask_irq_mode_init(struct lpfc_hba *phba)
+{
+ unsigned int cpu, first_cpu, numa_node = NUMA_NO_NODE;
+ const struct cpumask *sibling_mask;
+ struct cpumask *aff_mask = &phba->sli4_hba.irq_aff_mask;
+
+ cpumask_clear(aff_mask);
+
+ if (phba->irq_chann_mode == NUMA_MODE) {
+ /* Check if we're a NUMA architecture */
+ numa_node = dev_to_node(&phba->pcidev->dev);
+ if (numa_node == NUMA_NO_NODE) {
+ phba->irq_chann_mode = NORMAL_MODE;
+ return;
+ }
+ }
+
+ for_each_possible_cpu(cpu) {
+ switch (phba->irq_chann_mode) {
+ case NUMA_MODE:
+ if (cpu_to_node(cpu) == numa_node)
+ cpumask_set_cpu(cpu, aff_mask);
+ break;
+ case NHT_MODE:
+ sibling_mask = topology_sibling_cpumask(cpu);
+ first_cpu = cpumask_first(sibling_mask);
+ if (first_cpu < nr_cpu_ids)
+ cpumask_set_cpu(first_cpu, aff_mask);
+ break;
+ default:
+ break;
+ }
+ }
+}
+#endif
+
+static void
+lpfc_assign_default_irq_chann(struct lpfc_hba *phba)
+{
+#if IS_ENABLED(CONFIG_X86)
+ switch (boot_cpu_data.x86_vendor) {
+ case X86_VENDOR_AMD:
+ /* If AMD architecture, then default is NUMA_MODE */
+ phba->irq_chann_mode = NUMA_MODE;
+ break;
+ case X86_VENDOR_INTEL:
+ /* If Intel architecture, then default is no hyperthread mode */
+ phba->irq_chann_mode = NHT_MODE;
+ break;
+ default:
+ phba->irq_chann_mode = NORMAL_MODE;
+ break;
+ }
+ lpfc_cpumask_irq_mode_init(phba);
+#else
+ phba->irq_chann_mode = NORMAL_MODE;
+#endif
+}
+
+/*
+ * lpfc_irq_chann: Set the number of IRQ vectors that are available
+ * for Hardware Queues to utilize. This also will map to the number
+ * of EQ / MSI-X vectors the driver will create. This should never be
+ * more than the number of Hardware Queues
+ *
+ * 0 = Configure number of IRQ Channels to:
+ * if AMD architecture, number of CPUs on HBA's NUMA node
+ * if Intel architecture, number of physical CPUs.
+ * otherwise, number of active CPUs.
+ * [1,256] = Manually specify how many IRQ Channels to use.
+ *
+ * Value range is [0,256]. Default value is [0].
+ */
+static uint lpfc_irq_chann = LPFC_IRQ_CHANN_DEF;
+module_param(lpfc_irq_chann, uint, 0444);
+MODULE_PARM_DESC(lpfc_irq_chann, "Set number of interrupt vectors to allocate");
+
+/* lpfc_irq_chann_init - Set the hba irq_chann initial value
+ * @phba: lpfc_hba pointer.
+ * @val: contains the initial value
+ *
+ * Description:
+ * Validates the initial value is within range and assigns it to the
+ * adapter. If not in range, an error message is posted and the
+ * default value is assigned.
+ *
+ * Returns:
+ * zero if value is in range and is set
+ * -EINVAL if value was out of range
+ **/
+static int
+lpfc_irq_chann_init(struct lpfc_hba *phba, uint32_t val)
+{
+ const struct cpumask *aff_mask;
+
+ if (phba->cfg_use_msi != 2) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "8532 use_msi = %u ignoring cfg_irq_numa\n",
+ phba->cfg_use_msi);
+ phba->irq_chann_mode = NORMAL_MODE;
+ phba->cfg_irq_chann = LPFC_IRQ_CHANN_DEF;
+ return 0;
+ }
+
+ /* Check if default setting was passed */
+ if (val == LPFC_IRQ_CHANN_DEF &&
+ phba->cfg_hdw_queue == LPFC_HBA_HDWQ_DEF &&
+ phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_assign_default_irq_chann(phba);
+
+ if (phba->irq_chann_mode != NORMAL_MODE) {
+ aff_mask = &phba->sli4_hba.irq_aff_mask;
+
+ if (cpumask_empty(aff_mask)) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "8533 Could not identify CPUS for "
+ "mode %d, ignoring\n",
+ phba->irq_chann_mode);
+ phba->irq_chann_mode = NORMAL_MODE;
+ phba->cfg_irq_chann = LPFC_IRQ_CHANN_DEF;
+ } else {
+ phba->cfg_irq_chann = cpumask_weight(aff_mask);
+
+ /* If no hyperthread mode, then set hdwq count to
+ * aff_mask weight as well
+ */
+ if (phba->irq_chann_mode == NHT_MODE)
+ phba->cfg_hdw_queue = phba->cfg_irq_chann;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "8543 lpfc_irq_chann set to %u "
+ "(mode: %d)\n", phba->cfg_irq_chann,
+ phba->irq_chann_mode);
+ }
+ } else {
+ if (val > LPFC_IRQ_CHANN_MAX) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "8545 lpfc_irq_chann attribute cannot "
+ "be set to %u, allowed range is "
+ "[%u,%u]\n",
+ val,
+ LPFC_IRQ_CHANN_MIN,
+ LPFC_IRQ_CHANN_MAX);
+ phba->cfg_irq_chann = LPFC_IRQ_CHANN_DEF;
+ return -EINVAL;
+ }
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ phba->cfg_irq_chann = val;
+ } else {
+ phba->cfg_irq_chann = 2;
+ phba->cfg_hdw_queue = 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * lpfc_irq_chann_show - Display value of irq_chann
+ * @dev: class converted to a Scsi_host structure.
+ * @attr: device attribute, not used.
+ * @buf: on return contains a string with the list sizes
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_irq_chann_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", phba->cfg_irq_chann);
+}
+
+static DEVICE_ATTR_RO(lpfc_irq_chann);
/*
# lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware.
# 0 = HBA resets disabled
# 1 = HBA resets enabled (default)
-# Value range is [0,1]. Default value is 1.
+# 2 = HBA reset via PCI bus reset enabled
+# Value range is [0,2]. Default value is 1.
*/
-LPFC_ATTR_R(enable_hba_reset, 1, 0, 1, "Enable HBA resets from the driver.");
+LPFC_ATTR_RW(enable_hba_reset, 1, 0, 2, "Enable HBA resets from the driver.");
/*
# lpfc_enable_hba_heartbeat: Disable HBA heartbeat timer..
@@ -3662,6 +5827,21 @@ LPFC_ATTR_R(enable_hba_reset, 1, 0, 1, "Enable HBA resets from the driver.");
LPFC_ATTR_R(enable_hba_heartbeat, 0, 0, 1, "Enable HBA Heartbeat.");
/*
+# lpfc_EnableXLane: Enable Express Lane Feature
+# 0x0 Express Lane Feature disabled
+# 0x1 Express Lane Feature enabled
+# Value range is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_R(EnableXLane, 0, 0, 1, "Enable Express Lane Feature.");
+
+/*
+# lpfc_XLanePriority: Define CS_CTL priority for Express Lane Feature
+# 0x0 - 0x7f = CS_CTL field in FC header (high 7 bits)
+# Value range is [0x0,0x7f]. Default value is 0
+*/
+LPFC_ATTR_RW(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature.");
+
+/*
# lpfc_enable_bg: Enable BlockGuard (Emulex's Implementation of T10-DIF)
# 0 = BlockGuard disabled (default)
# 1 = BlockGuard enabled
@@ -3670,30 +5850,41 @@ LPFC_ATTR_R(enable_hba_heartbeat, 0, 0, 1, "Enable HBA Heartbeat.");
LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
/*
-# lpfc_prot_mask: i
+# lpfc_prot_mask:
# - Bit mask of host protection capabilities used to register with the
# SCSI mid-layer
# - Only meaningful if BG is turned on (lpfc_enable_bg=1).
# - Allows you to ultimately specify which profiles to use
# - Default will result in registering capabilities for all profiles.
+# - SHOST_DIF_TYPE1_PROTECTION 1
+# HBA supports T10 DIF Type 1: HBA to Target Type 1 Protection
+# - SHOST_DIX_TYPE0_PROTECTION 8
+# HBA supports DIX Type 0: Host to HBA protection only
+# - SHOST_DIX_TYPE1_PROTECTION 16
+# HBA supports DIX Type 1: Host to HBA Type 1 protection
#
*/
-unsigned int lpfc_prot_mask = SHOST_DIF_TYPE1_PROTECTION;
-
-module_param(lpfc_prot_mask, uint, S_IRUGO);
-MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
+LPFC_ATTR(prot_mask,
+ (SHOST_DIF_TYPE1_PROTECTION |
+ SHOST_DIX_TYPE0_PROTECTION |
+ SHOST_DIX_TYPE1_PROTECTION),
+ 0,
+ (SHOST_DIF_TYPE1_PROTECTION |
+ SHOST_DIX_TYPE0_PROTECTION |
+ SHOST_DIX_TYPE1_PROTECTION),
+ "T10-DIF host protection capabilities mask");
/*
-# lpfc_prot_guard: i
+# lpfc_prot_guard:
# - Bit mask of protection guard types to register with the SCSI mid-layer
-# - Guard types are currently either 1) IP checksum 2) T10-DIF CRC
+# - Guard types are currently either 1) T10-DIF CRC 2) IP checksum
# - Allows you to ultimately specify which profiles to use
# - Default will result in registering capabilities for all guard types
#
*/
-unsigned char lpfc_prot_guard = SHOST_DIX_GUARD_IP;
-module_param(lpfc_prot_guard, byte, S_IRUGO);
-MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type");
+LPFC_ATTR(prot_guard,
+ SHOST_DIX_GUARD_IP, SHOST_DIX_GUARD_CRC, SHOST_DIX_GUARD_IP,
+ "T10-DIF host protection guard type");
/*
* Delay initial NPort discovery when Clean Address bit is cleared in
@@ -3709,136 +5900,404 @@ MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type");
* accept and FCID/Fabric name/Fabric portname is changed.
* Default value is 0.
*/
-int lpfc_delay_discovery;
-module_param(lpfc_delay_discovery, int, S_IRUGO);
-MODULE_PARM_DESC(lpfc_delay_discovery,
- "Delay NPort discovery when Clean Address bit is cleared. "
- "Allowed values: 0,1.");
+LPFC_ATTR(delay_discovery, 0, 0, 1,
+ "Delay NPort discovery when Clean Address bit is cleared.");
/*
* lpfc_sg_seg_cnt - Initial Maximum DMA Segment Count
- * This value can be set to values between 64 and 256. The default value is
- * 64, but may be increased to allow for larger Max I/O sizes. The scsi layer
- * will be allowed to request I/Os of sizes up to (MAX_SEG_COUNT * SEG_SIZE).
+ * This value can be set to values between 64 and 4096. The default value
+ * is 64, but may be increased to allow for larger Max I/O sizes. The scsi
+ * and nvme layers will allow I/O sizes up to (MAX_SEG_COUNT * SEG_SIZE).
+ * Because of the additional overhead involved in setting up T10-DIF,
+ * this parameter will be limited to 128 if BlockGuard is enabled under SLI4
+ * and will be limited to 512 if BlockGuard is enabled under SLI3.
*/
-LPFC_ATTR_R(sg_seg_cnt, LPFC_DEFAULT_SG_SEG_CNT, LPFC_DEFAULT_SG_SEG_CNT,
- LPFC_MAX_SG_SEG_CNT, "Max Scatter Gather Segment Count");
-
-LPFC_ATTR_R(prot_sg_seg_cnt, LPFC_DEFAULT_PROT_SG_SEG_CNT,
- LPFC_DEFAULT_PROT_SG_SEG_CNT, LPFC_MAX_PROT_SG_SEG_CNT,
- "Max Protection Scatter Gather Segment Count");
-
-struct device_attribute *lpfc_hba_attrs[] = {
- &dev_attr_bg_info,
- &dev_attr_bg_guard_err,
- &dev_attr_bg_apptag_err,
- &dev_attr_bg_reftag_err,
- &dev_attr_info,
- &dev_attr_serialnum,
- &dev_attr_modeldesc,
- &dev_attr_modelname,
- &dev_attr_programtype,
- &dev_attr_portnum,
- &dev_attr_fwrev,
- &dev_attr_hdw,
- &dev_attr_option_rom_version,
- &dev_attr_link_state,
- &dev_attr_num_discovered_ports,
- &dev_attr_menlo_mgmt_mode,
- &dev_attr_lpfc_drvr_version,
- &dev_attr_lpfc_enable_fip,
- &dev_attr_lpfc_temp_sensor,
- &dev_attr_lpfc_log_verbose,
- &dev_attr_lpfc_lun_queue_depth,
- &dev_attr_lpfc_tgt_queue_depth,
- &dev_attr_lpfc_hba_queue_depth,
- &dev_attr_lpfc_peer_port_login,
- &dev_attr_lpfc_nodev_tmo,
- &dev_attr_lpfc_devloss_tmo,
- &dev_attr_lpfc_fcp_class,
- &dev_attr_lpfc_use_adisc,
- &dev_attr_lpfc_ack0,
- &dev_attr_lpfc_topology,
- &dev_attr_lpfc_scan_down,
- &dev_attr_lpfc_link_speed,
- &dev_attr_lpfc_cr_delay,
- &dev_attr_lpfc_cr_count,
- &dev_attr_lpfc_multi_ring_support,
- &dev_attr_lpfc_multi_ring_rctl,
- &dev_attr_lpfc_multi_ring_type,
- &dev_attr_lpfc_fdmi_on,
- &dev_attr_lpfc_max_luns,
- &dev_attr_lpfc_enable_npiv,
- &dev_attr_lpfc_enable_rrq,
- &dev_attr_nport_evt_cnt,
- &dev_attr_board_mode,
- &dev_attr_max_vpi,
- &dev_attr_used_vpi,
- &dev_attr_max_rpi,
- &dev_attr_used_rpi,
- &dev_attr_max_xri,
- &dev_attr_used_xri,
- &dev_attr_npiv_info,
- &dev_attr_issue_reset,
- &dev_attr_lpfc_poll,
- &dev_attr_lpfc_poll_tmo,
- &dev_attr_lpfc_use_msi,
- &dev_attr_lpfc_fcp_imax,
- &dev_attr_lpfc_fcp_wq_count,
- &dev_attr_lpfc_fcp_eq_count,
- &dev_attr_lpfc_enable_bg,
- &dev_attr_lpfc_soft_wwnn,
- &dev_attr_lpfc_soft_wwpn,
- &dev_attr_lpfc_soft_wwn_enable,
- &dev_attr_lpfc_enable_hba_reset,
- &dev_attr_lpfc_enable_hba_heartbeat,
- &dev_attr_lpfc_sg_seg_cnt,
- &dev_attr_lpfc_max_scsicmpl_time,
- &dev_attr_lpfc_stat_data_ctrl,
- &dev_attr_lpfc_prot_sg_seg_cnt,
- &dev_attr_lpfc_aer_support,
- &dev_attr_lpfc_aer_state_cleanup,
- &dev_attr_lpfc_sriov_nr_virtfn,
- &dev_attr_lpfc_suppress_link_up,
- &dev_attr_lpfc_iocb_cnt,
- &dev_attr_iocb_hw,
- &dev_attr_txq_hw,
- &dev_attr_txcmplq_hw,
- &dev_attr_lpfc_fips_level,
- &dev_attr_lpfc_fips_rev,
- &dev_attr_lpfc_dss,
- &dev_attr_lpfc_sriov_hw_max_virtfn,
+static uint lpfc_sg_seg_cnt = LPFC_DEFAULT_SG_SEG_CNT;
+module_param(lpfc_sg_seg_cnt, uint, 0444);
+MODULE_PARM_DESC(lpfc_sg_seg_cnt, "Max Scatter Gather Segment Count");
+
+/**
+ * lpfc_sg_seg_cnt_show - Display the scatter/gather list sizes
+ * configured for the adapter
+ * @dev: class converted to a Scsi_host structure.
+ * @attr: device attribute, not used.
+ * @buf: on return contains a string with the list sizes
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_sg_seg_cnt_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int len;
+
+ len = scnprintf(buf, PAGE_SIZE, "SGL sz: %d total SGEs: %d\n",
+ phba->cfg_sg_dma_buf_size, phba->cfg_total_seg_cnt);
+
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Cfg: %d SCSI: %d NVME: %d\n",
+ phba->cfg_sg_seg_cnt, phba->cfg_scsi_seg_cnt,
+ phba->cfg_nvme_seg_cnt);
+ return len;
+}
+
+static DEVICE_ATTR_RO(lpfc_sg_seg_cnt);
+
+/**
+ * lpfc_sg_seg_cnt_init - Set the hba sg_seg_cnt initial value
+ * @phba: lpfc_hba pointer.
+ * @val: contains the initial value
+ *
+ * Description:
+ * Validates the initial value is within range and assigns it to the
+ * adapter. If not in range, an error message is posted and the
+ * default value is assigned.
+ *
+ * Returns:
+ * zero if value is in range and is set
+ * -EINVAL if value was out of range
+ **/
+static int
+lpfc_sg_seg_cnt_init(struct lpfc_hba *phba, int val)
+{
+ if (val >= LPFC_MIN_SG_SEG_CNT && val <= LPFC_MAX_SG_SEG_CNT) {
+ phba->cfg_sg_seg_cnt = val;
+ return 0;
+ }
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0409 lpfc_sg_seg_cnt attribute cannot be set to %d, "
+ "allowed range is [%d, %d]\n",
+ val, LPFC_MIN_SG_SEG_CNT, LPFC_MAX_SG_SEG_CNT);
+ phba->cfg_sg_seg_cnt = LPFC_DEFAULT_SG_SEG_CNT;
+ return -EINVAL;
+}
+
+/*
+ * lpfc_enable_mds_diags: Enable MDS Diagnostics
+ * 0 = MDS Diagnostics disabled (default)
+ * 1 = MDS Diagnostics enabled
+ * Value range is [0,1]. Default value is 0.
+ */
+LPFC_ATTR_RW(enable_mds_diags, 0, 0, 1, "Enable MDS Diagnostics");
+
+/*
+ * lpfc_ras_fwlog_buffsize: Firmware logging host buffer size
+ * 0 = Disable firmware logging (default)
+ * [1-4] = Multiple of 1/4th Mb of host memory for FW logging
+ * Value range [0..4]. Default value is 0
+ */
+LPFC_ATTR(ras_fwlog_buffsize, 0, 0, 4, "Host memory for FW logging");
+lpfc_param_show(ras_fwlog_buffsize);
+
+static ssize_t
+lpfc_ras_fwlog_buffsize_set(struct lpfc_hba *phba, uint val)
+{
+ int ret = 0;
+ enum ras_state state;
+
+ if (!lpfc_rangecheck(val, 0, 4))
+ return -EINVAL;
+
+ if (phba->cfg_ras_fwlog_buffsize == val)
+ return 0;
+
+ if (phba->cfg_ras_fwlog_func != PCI_FUNC(phba->pcidev->devfn))
+ return -EINVAL;
+
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ state = phba->ras_fwlog.state;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+
+ if (state == REG_INPROGRESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "6147 RAS Logging "
+ "registration is in progress\n");
+ return -EBUSY;
+ }
+
+ /* For disable logging: stop the logs and free the DMA.
+ * For ras_fwlog_buffsize size change we still need to free and
+ * reallocate the DMA in lpfc_sli4_ras_fwlog_init.
+ */
+ phba->cfg_ras_fwlog_buffsize = val;
+ if (state == ACTIVE) {
+ lpfc_ras_stop_fwlog(phba);
+ lpfc_sli4_ras_dma_free(phba);
+ }
+
+ lpfc_sli4_ras_init(phba);
+ if (phba->ras_fwlog.ras_enabled)
+ ret = lpfc_sli4_ras_fwlog_init(phba, phba->cfg_ras_fwlog_level,
+ LPFC_RAS_ENABLE_LOGGING);
+ return ret;
+}
+
+lpfc_param_store(ras_fwlog_buffsize);
+static DEVICE_ATTR_RW(lpfc_ras_fwlog_buffsize);
+
+/*
+ * lpfc_ras_fwlog_level: Firmware logging verbosity level
+ * Valid only if firmware logging is enabled
+ * 0(Least Verbosity) 4 (most verbosity)
+ * Value range is [0..4]. Default value is 0
+ */
+LPFC_ATTR_RW(ras_fwlog_level, 0, 0, 4, "Firmware Logging Level");
+
+/*
+ * lpfc_ras_fwlog_func: Firmware logging enabled on function number
+ * Default function which has RAS support : 0
+ * Value Range is [0..3].
+ * FW logging is a global action and enablement is via a specific
+ * port.
+ */
+LPFC_ATTR_RW(ras_fwlog_func, 0, 0, 3, "Firmware Logging Enabled on Function");
+
+/*
+ * lpfc_enable_bbcr: Enable BB Credit Recovery
+ * 0 = BB Credit Recovery disabled
+ * 1 = BB Credit Recovery enabled (default)
+ * Value range is [0,1]. Default value is 1.
+ */
+LPFC_BBCR_ATTR_RW(enable_bbcr, 1, 0, 1, "Enable BBC Recovery");
+
+/* Signaling module parameters */
+int lpfc_fabric_cgn_frequency = 100; /* 100 ms default */
+module_param(lpfc_fabric_cgn_frequency, int, 0444);
+MODULE_PARM_DESC(lpfc_fabric_cgn_frequency, "Congestion signaling fabric freq");
+
+unsigned char lpfc_acqe_cgn_frequency = 10; /* 10 sec default */
+module_param(lpfc_acqe_cgn_frequency, byte, 0444);
+MODULE_PARM_DESC(lpfc_acqe_cgn_frequency, "Congestion signaling ACQE freq");
+
+int lpfc_use_cgn_signal = 1; /* 0 - only use FPINs, 1 - Use signals if avail */
+module_param(lpfc_use_cgn_signal, int, 0444);
+MODULE_PARM_DESC(lpfc_use_cgn_signal, "Use Congestion signaling if available");
+
+/*
+ * lpfc_enable_dpp: Enable DPP on G7
+ * 0 = DPP on G7 disabled
+ * 1 = DPP on G7 enabled (default)
+ * Value range is [0,1]. Default value is 1.
+ */
+LPFC_ATTR_RW(enable_dpp, 1, 0, 1, "Enable Direct Packet Push");
+
+/*
+ * lpfc_enable_mi: Enable FDMI MIB
+ * 0 = disabled
+ * 1 = enabled (default)
+ * Value range is [0,1].
+ */
+LPFC_ATTR_R(enable_mi, 1, 0, 1, "Enable MI");
+
+/*
+ * lpfc_max_vmid: Maximum number of VMs to be tagged. This is valid only if
+ * either vmid_app_header or vmid_priority_tagging is enabled.
+ * 4 - 255 = vmid support enabled for 4-255 VMs
+ * Value range is [4,255].
+ */
+LPFC_ATTR_R(max_vmid, LPFC_MIN_VMID, LPFC_MIN_VMID, LPFC_MAX_VMID,
+ "Maximum number of VMs supported");
+
+/*
+ * lpfc_vmid_inactivity_timeout: Inactivity timeout duration in hours
+ * 0 = Timeout is disabled
+ * Value range is [0,24].
+ */
+LPFC_ATTR_R(vmid_inactivity_timeout, 4, 0, 24,
+ "Inactivity timeout in hours");
+
+/*
+ * lpfc_vmid_app_header: Enable App Header VMID support
+ * 0 = Support is disabled (default)
+ * 1 = Support is enabled
+ * Value range is [0,1].
+ */
+LPFC_ATTR_R(vmid_app_header, LPFC_VMID_APP_HEADER_DISABLE,
+ LPFC_VMID_APP_HEADER_DISABLE, LPFC_VMID_APP_HEADER_ENABLE,
+ "Enable App Header VMID support");
+
+/*
+ * lpfc_vmid_priority_tagging: Enable Priority Tagging VMID support
+ * 0 = Support is disabled (default)
+ * 1 = Allow supported targets only
+ * 2 = Allow all targets
+ * Value range is [0,2].
+ */
+LPFC_ATTR_R(vmid_priority_tagging, LPFC_VMID_PRIO_TAG_DISABLE,
+ LPFC_VMID_PRIO_TAG_DISABLE,
+ LPFC_VMID_PRIO_TAG_ALL_TARGETS,
+ "Enable Priority Tagging VMID support");
+
+static struct attribute *lpfc_hba_attrs[] = {
+ &dev_attr_nvme_info.attr,
+ &dev_attr_scsi_stat.attr,
+ &dev_attr_bg_info.attr,
+ &dev_attr_bg_guard_err.attr,
+ &dev_attr_bg_apptag_err.attr,
+ &dev_attr_bg_reftag_err.attr,
+ &dev_attr_info.attr,
+ &dev_attr_serialnum.attr,
+ &dev_attr_modeldesc.attr,
+ &dev_attr_modelname.attr,
+ &dev_attr_programtype.attr,
+ &dev_attr_portnum.attr,
+ &dev_attr_fwrev.attr,
+ &dev_attr_hdw.attr,
+ &dev_attr_option_rom_version.attr,
+ &dev_attr_link_state.attr,
+ &dev_attr_num_discovered_ports.attr,
+ &dev_attr_lpfc_drvr_version.attr,
+ &dev_attr_lpfc_enable_fip.attr,
+ &dev_attr_lpfc_temp_sensor.attr,
+ &dev_attr_lpfc_log_verbose.attr,
+ &dev_attr_lpfc_lun_queue_depth.attr,
+ &dev_attr_lpfc_tgt_queue_depth.attr,
+ &dev_attr_lpfc_hba_queue_depth.attr,
+ &dev_attr_lpfc_peer_port_login.attr,
+ &dev_attr_lpfc_nodev_tmo.attr,
+ &dev_attr_lpfc_devloss_tmo.attr,
+ &dev_attr_lpfc_enable_fc4_type.attr,
+ &dev_attr_lpfc_fcp_class.attr,
+ &dev_attr_lpfc_use_adisc.attr,
+ &dev_attr_lpfc_first_burst_size.attr,
+ &dev_attr_lpfc_ack0.attr,
+ &dev_attr_lpfc_xri_rebalancing.attr,
+ &dev_attr_lpfc_topology.attr,
+ &dev_attr_lpfc_scan_down.attr,
+ &dev_attr_lpfc_link_speed.attr,
+ &dev_attr_lpfc_fcp_io_sched.attr,
+ &dev_attr_lpfc_ns_query.attr,
+ &dev_attr_lpfc_fcp2_no_tgt_reset.attr,
+ &dev_attr_lpfc_cr_delay.attr,
+ &dev_attr_lpfc_cr_count.attr,
+ &dev_attr_lpfc_multi_ring_support.attr,
+ &dev_attr_lpfc_multi_ring_rctl.attr,
+ &dev_attr_lpfc_multi_ring_type.attr,
+ &dev_attr_lpfc_fdmi_on.attr,
+ &dev_attr_lpfc_enable_SmartSAN.attr,
+ &dev_attr_lpfc_max_luns.attr,
+ &dev_attr_lpfc_enable_npiv.attr,
+ &dev_attr_lpfc_fcf_failover_policy.attr,
+ &dev_attr_lpfc_enable_rrq.attr,
+ &dev_attr_lpfc_fcp_wait_abts_rsp.attr,
+ &dev_attr_nport_evt_cnt.attr,
+ &dev_attr_board_mode.attr,
+ &dev_attr_lpfc_xcvr_data.attr,
+ &dev_attr_max_vpi.attr,
+ &dev_attr_used_vpi.attr,
+ &dev_attr_max_rpi.attr,
+ &dev_attr_used_rpi.attr,
+ &dev_attr_max_xri.attr,
+ &dev_attr_used_xri.attr,
+ &dev_attr_npiv_info.attr,
+ &dev_attr_issue_reset.attr,
+ &dev_attr_lpfc_poll.attr,
+ &dev_attr_lpfc_poll_tmo.attr,
+ &dev_attr_lpfc_task_mgmt_tmo.attr,
+ &dev_attr_lpfc_use_msi.attr,
+ &dev_attr_lpfc_nvme_oas.attr,
+ &dev_attr_lpfc_nvme_embed_cmd.attr,
+ &dev_attr_lpfc_fcp_imax.attr,
+ &dev_attr_lpfc_force_rscn.attr,
+ &dev_attr_lpfc_cq_poll_threshold.attr,
+ &dev_attr_lpfc_cq_max_proc_limit.attr,
+ &dev_attr_lpfc_fcp_cpu_map.attr,
+ &dev_attr_lpfc_fcp_mq_threshold.attr,
+ &dev_attr_lpfc_hdw_queue.attr,
+ &dev_attr_lpfc_irq_chann.attr,
+ &dev_attr_lpfc_suppress_rsp.attr,
+ &dev_attr_lpfc_nvmet_mrq.attr,
+ &dev_attr_lpfc_nvmet_mrq_post.attr,
+ &dev_attr_lpfc_nvme_enable_fb.attr,
+ &dev_attr_lpfc_nvmet_fb_size.attr,
+ &dev_attr_lpfc_enable_bg.attr,
+ &dev_attr_lpfc_enable_hba_reset.attr,
+ &dev_attr_lpfc_enable_hba_heartbeat.attr,
+ &dev_attr_lpfc_EnableXLane.attr,
+ &dev_attr_lpfc_XLanePriority.attr,
+ &dev_attr_lpfc_xlane_lun.attr,
+ &dev_attr_lpfc_xlane_tgt.attr,
+ &dev_attr_lpfc_xlane_vpt.attr,
+ &dev_attr_lpfc_xlane_lun_state.attr,
+ &dev_attr_lpfc_xlane_lun_status.attr,
+ &dev_attr_lpfc_xlane_priority.attr,
+ &dev_attr_lpfc_sg_seg_cnt.attr,
+ &dev_attr_lpfc_max_scsicmpl_time.attr,
+ &dev_attr_lpfc_aer_support.attr,
+ &dev_attr_lpfc_aer_state_cleanup.attr,
+ &dev_attr_lpfc_sriov_nr_virtfn.attr,
+ &dev_attr_lpfc_req_fw_upgrade.attr,
+ &dev_attr_lpfc_suppress_link_up.attr,
+ &dev_attr_iocb_hw.attr,
+ &dev_attr_pls.attr,
+ &dev_attr_pt.attr,
+ &dev_attr_txq_hw.attr,
+ &dev_attr_txcmplq_hw.attr,
+ &dev_attr_lpfc_sriov_hw_max_virtfn.attr,
+ &dev_attr_protocol.attr,
+ &dev_attr_lpfc_xlane_supported.attr,
+ &dev_attr_lpfc_enable_mds_diags.attr,
+ &dev_attr_lpfc_ras_fwlog_buffsize.attr,
+ &dev_attr_lpfc_ras_fwlog_level.attr,
+ &dev_attr_lpfc_ras_fwlog_func.attr,
+ &dev_attr_lpfc_enable_bbcr.attr,
+ &dev_attr_lpfc_enable_dpp.attr,
+ &dev_attr_lpfc_enable_mi.attr,
+ &dev_attr_cmf_info.attr,
+ &dev_attr_lpfc_max_vmid.attr,
+ &dev_attr_lpfc_vmid_inactivity_timeout.attr,
+ &dev_attr_lpfc_vmid_app_header.attr,
+ &dev_attr_lpfc_vmid_priority_tagging.attr,
+ &dev_attr_lpfc_vmid_info.attr,
NULL,
};
-struct device_attribute *lpfc_vport_attrs[] = {
- &dev_attr_info,
- &dev_attr_link_state,
- &dev_attr_num_discovered_ports,
- &dev_attr_lpfc_drvr_version,
- &dev_attr_lpfc_log_verbose,
- &dev_attr_lpfc_lun_queue_depth,
- &dev_attr_lpfc_tgt_queue_depth,
- &dev_attr_lpfc_nodev_tmo,
- &dev_attr_lpfc_devloss_tmo,
- &dev_attr_lpfc_hba_queue_depth,
- &dev_attr_lpfc_peer_port_login,
- &dev_attr_lpfc_restrict_login,
- &dev_attr_lpfc_fcp_class,
- &dev_attr_lpfc_use_adisc,
- &dev_attr_lpfc_fdmi_on,
- &dev_attr_lpfc_max_luns,
- &dev_attr_nport_evt_cnt,
- &dev_attr_npiv_info,
- &dev_attr_lpfc_enable_da_id,
- &dev_attr_lpfc_max_scsicmpl_time,
- &dev_attr_lpfc_stat_data_ctrl,
- &dev_attr_lpfc_static_vport,
- &dev_attr_lpfc_fips_level,
- &dev_attr_lpfc_fips_rev,
+static const struct attribute_group lpfc_hba_attr_group = {
+ .attrs = lpfc_hba_attrs
+};
+
+const struct attribute_group *lpfc_hba_groups[] = {
+ &lpfc_hba_attr_group,
+ NULL
+};
+
+static struct attribute *lpfc_vport_attrs[] = {
+ &dev_attr_info.attr,
+ &dev_attr_link_state.attr,
+ &dev_attr_num_discovered_ports.attr,
+ &dev_attr_lpfc_drvr_version.attr,
+ &dev_attr_lpfc_log_verbose.attr,
+ &dev_attr_lpfc_lun_queue_depth.attr,
+ &dev_attr_lpfc_tgt_queue_depth.attr,
+ &dev_attr_lpfc_nodev_tmo.attr,
+ &dev_attr_lpfc_devloss_tmo.attr,
+ &dev_attr_lpfc_hba_queue_depth.attr,
+ &dev_attr_lpfc_peer_port_login.attr,
+ &dev_attr_lpfc_restrict_login.attr,
+ &dev_attr_lpfc_fcp_class.attr,
+ &dev_attr_lpfc_use_adisc.attr,
+ &dev_attr_lpfc_first_burst_size.attr,
+ &dev_attr_lpfc_max_luns.attr,
+ &dev_attr_nport_evt_cnt.attr,
+ &dev_attr_npiv_info.attr,
+ &dev_attr_lpfc_enable_da_id.attr,
+ &dev_attr_lpfc_max_scsicmpl_time.attr,
+ &dev_attr_lpfc_static_vport.attr,
+ &dev_attr_cmf_info.attr,
NULL,
};
+static const struct attribute_group lpfc_vport_attr_group = {
+ .attrs = lpfc_vport_attrs
+};
+
+const struct attribute_group *lpfc_vport_groups[] = {
+ &lpfc_vport_attr_group,
+ NULL
+};
+
/**
* sysfs_ctlreg_write - Write method for writing to ctlreg
* @filp: open sysfs file
@@ -3860,7 +6319,7 @@ struct device_attribute *lpfc_vport_attrs[] = {
**/
static ssize_t
sysfs_ctlreg_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
size_t buf_off;
@@ -3875,18 +6334,23 @@ sysfs_ctlreg_write(struct file *filp, struct kobject *kobj,
if ((off + count) > FF_REG_AREA_SIZE)
return -ERANGE;
- if (count == 0) return 0;
+ if (count <= LPFC_REG_WRITE_KEY_SIZE)
+ return 0;
if (off % 4 || count % 4 || (unsigned long)buf % 4)
return -EINVAL;
- if (!(vport->fc_flag & FC_OFFLINE_MODE)) {
+ /* This is to protect HBA registers from accidental writes. */
+ if (memcmp(buf, LPFC_REG_WRITE_KEY, LPFC_REG_WRITE_KEY_SIZE))
+ return -EINVAL;
+
+ if (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag))
return -EPERM;
- }
spin_lock_irq(&phba->hbalock);
- for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t))
- writel(*((uint32_t *)(buf + buf_off)),
+ for (buf_off = 0; buf_off < count - LPFC_REG_WRITE_KEY_SIZE;
+ buf_off += sizeof(uint32_t))
+ writel(*((uint32_t *)(buf + buf_off + LPFC_REG_WRITE_KEY_SIZE)),
phba->ctrl_regs_memmap_p + off + buf_off);
spin_unlock_irq(&phba->hbalock);
@@ -3914,7 +6378,7 @@ sysfs_ctlreg_write(struct file *filp, struct kobject *kobj,
**/
static ssize_t
sysfs_ctlreg_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
size_t buf_off;
@@ -3950,7 +6414,7 @@ sysfs_ctlreg_read(struct file *filp, struct kobject *kobj,
return count;
}
-static struct bin_attribute sysfs_ctlreg_attr = {
+static const struct bin_attribute sysfs_ctlreg_attr = {
.attr = {
.name = "ctlreg",
.mode = S_IRUSR | S_IWUSR,
@@ -3961,23 +6425,6 @@ static struct bin_attribute sysfs_ctlreg_attr = {
};
/**
- * sysfs_mbox_idle - frees the sysfs mailbox
- * @phba: lpfc_hba pointer
- **/
-static void
-sysfs_mbox_idle(struct lpfc_hba *phba)
-{
- phba->sysfs_mbox.state = SMBOX_IDLE;
- phba->sysfs_mbox.offset = 0;
-
- if (phba->sysfs_mbox.mbox) {
- mempool_free(phba->sysfs_mbox.mbox,
- phba->mbox_mem_pool);
- phba->sysfs_mbox.mbox = NULL;
- }
-}
-
-/**
* sysfs_mbox_write - Write method for writing information via mbox
* @filp: open sysfs file
* @kobj: kernel kobject that contains the kernel class device.
@@ -3987,71 +6434,18 @@ sysfs_mbox_idle(struct lpfc_hba *phba)
* @count: bytes to transfer.
*
* Description:
- * Accessed via /sys/class/scsi_host/hostxxx/mbox.
- * Uses the sysfs mbox to send buf contents to the adapter.
+ * Deprecated function. All mailbox access from user space is performed via the
+ * bsg interface.
*
* Returns:
- * -ERANGE off and count combo out of range
- * -EINVAL off, count or buff address invalid
- * zero if count is zero
- * -EPERM adapter is offline
- * -ENOMEM failed to allocate memory for the mail box
- * -EAGAIN offset, state or mbox is NULL
- * count number of bytes transferred
+ * -EPERM operation not permitted
**/
static ssize_t
sysfs_mbox_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct lpfcMboxq *mbox = NULL;
-
- if ((count + off) > MAILBOX_CMD_SIZE)
- return -ERANGE;
-
- if (off % 4 || count % 4 || (unsigned long)buf % 4)
- return -EINVAL;
-
- if (count == 0)
- return 0;
-
- if (off == 0) {
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox)
- return -ENOMEM;
- memset(mbox, 0, sizeof (LPFC_MBOXQ_t));
- }
-
- spin_lock_irq(&phba->hbalock);
-
- if (off == 0) {
- if (phba->sysfs_mbox.mbox)
- mempool_free(mbox, phba->mbox_mem_pool);
- else
- phba->sysfs_mbox.mbox = mbox;
- phba->sysfs_mbox.state = SMBOX_WRITING;
- } else {
- if (phba->sysfs_mbox.state != SMBOX_WRITING ||
- phba->sysfs_mbox.offset != off ||
- phba->sysfs_mbox.mbox == NULL) {
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EAGAIN;
- }
- }
-
- memcpy((uint8_t *) &phba->sysfs_mbox.mbox->u.mb + off,
- buf, count);
-
- phba->sysfs_mbox.offset = off + count;
-
- spin_unlock_irq(&phba->hbalock);
-
- return count;
+ return -EPERM;
}
/**
@@ -4064,202 +6458,21 @@ sysfs_mbox_write(struct file *filp, struct kobject *kobj,
* @count: bytes to transfer.
*
* Description:
- * Accessed via /sys/class/scsi_host/hostxxx/mbox.
- * Uses the sysfs mbox to receive data from to the adapter.
+ * Deprecated function. All mailbox access from user space is performed via the
+ * bsg interface.
*
* Returns:
- * -ERANGE off greater than mailbox command size
- * -EINVAL off, count or buff address invalid
- * zero if off and count are zero
- * -EACCES adapter over temp
- * -EPERM garbage can value to catch a multitude of errors
- * -EAGAIN management IO not permitted, state or off error
- * -ETIME mailbox timeout
- * -ENODEV mailbox error
- * count number of bytes transferred
+ * -EPERM operation not permitted
**/
static ssize_t
sysfs_mbox_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct Scsi_Host *shost = class_to_shost(dev);
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- int rc;
- MAILBOX_t *pmb;
-
- if (off > MAILBOX_CMD_SIZE)
- return -ERANGE;
-
- if ((count + off) > MAILBOX_CMD_SIZE)
- count = MAILBOX_CMD_SIZE - off;
-
- if (off % 4 || count % 4 || (unsigned long)buf % 4)
- return -EINVAL;
-
- if (off && count == 0)
- return 0;
-
- spin_lock_irq(&phba->hbalock);
-
- if (phba->over_temp_state == HBA_OVER_TEMP) {
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EACCES;
- }
-
- if (off == 0 &&
- phba->sysfs_mbox.state == SMBOX_WRITING &&
- phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) {
- pmb = &phba->sysfs_mbox.mbox->u.mb;
- switch (pmb->mbxCommand) {
- /* Offline only */
- case MBX_INIT_LINK:
- case MBX_DOWN_LINK:
- case MBX_CONFIG_LINK:
- case MBX_CONFIG_RING:
- case MBX_RESET_RING:
- case MBX_UNREG_LOGIN:
- case MBX_CLEAR_LA:
- case MBX_DUMP_CONTEXT:
- case MBX_RUN_DIAGS:
- case MBX_RESTART:
- case MBX_SET_MASK:
- case MBX_SET_DEBUG:
- if (!(vport->fc_flag & FC_OFFLINE_MODE)) {
- printk(KERN_WARNING "mbox_read:Command 0x%x "
- "is illegal in on-line state\n",
- pmb->mbxCommand);
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EPERM;
- }
- case MBX_WRITE_NV:
- case MBX_WRITE_VPARMS:
- case MBX_LOAD_SM:
- case MBX_READ_NV:
- case MBX_READ_CONFIG:
- case MBX_READ_RCONFIG:
- case MBX_READ_STATUS:
- case MBX_READ_XRI:
- case MBX_READ_REV:
- case MBX_READ_LNK_STAT:
- case MBX_DUMP_MEMORY:
- case MBX_DOWN_LOAD:
- case MBX_UPDATE_CFG:
- case MBX_KILL_BOARD:
- case MBX_LOAD_AREA:
- case MBX_LOAD_EXP_ROM:
- case MBX_BEACON:
- case MBX_DEL_LD_ENTRY:
- case MBX_SET_VARIABLE:
- case MBX_WRITE_WWN:
- case MBX_PORT_CAPABILITIES:
- case MBX_PORT_IOV_CONTROL:
- break;
- case MBX_SECURITY_MGMT:
- case MBX_AUTH_PORT:
- if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
- printk(KERN_WARNING "mbox_read:Command 0x%x "
- "is not permitted\n", pmb->mbxCommand);
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EPERM;
- }
- break;
- case MBX_READ_SPARM64:
- case MBX_READ_TOPOLOGY:
- case MBX_REG_LOGIN:
- case MBX_REG_LOGIN64:
- case MBX_CONFIG_PORT:
- case MBX_RUN_BIU_DIAG:
- printk(KERN_WARNING "mbox_read: Illegal Command 0x%x\n",
- pmb->mbxCommand);
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EPERM;
- default:
- printk(KERN_WARNING "mbox_read: Unknown Command 0x%x\n",
- pmb->mbxCommand);
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EPERM;
- }
-
- /* If HBA encountered an error attention, allow only DUMP
- * or RESTART mailbox commands until the HBA is restarted.
- */
- if (phba->pport->stopped &&
- pmb->mbxCommand != MBX_DUMP_MEMORY &&
- pmb->mbxCommand != MBX_RESTART &&
- pmb->mbxCommand != MBX_WRITE_VPARMS &&
- pmb->mbxCommand != MBX_WRITE_WWN)
- lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
- "1259 mbox: Issued mailbox cmd "
- "0x%x while in stopped state.\n",
- pmb->mbxCommand);
-
- phba->sysfs_mbox.mbox->vport = vport;
-
- /* Don't allow mailbox commands to be sent when blocked
- * or when in the middle of discovery
- */
- if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EAGAIN;
- }
-
- if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
-
- spin_unlock_irq(&phba->hbalock);
- rc = lpfc_sli_issue_mbox (phba,
- phba->sysfs_mbox.mbox,
- MBX_POLL);
- spin_lock_irq(&phba->hbalock);
-
- } else {
- spin_unlock_irq(&phba->hbalock);
- rc = lpfc_sli_issue_mbox_wait (phba,
- phba->sysfs_mbox.mbox,
- lpfc_mbox_tmo_val(phba, pmb->mbxCommand) * HZ);
- spin_lock_irq(&phba->hbalock);
- }
-
- if (rc != MBX_SUCCESS) {
- if (rc == MBX_TIMEOUT) {
- phba->sysfs_mbox.mbox = NULL;
- }
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV;
- }
- phba->sysfs_mbox.state = SMBOX_READING;
- }
- else if (phba->sysfs_mbox.offset != off ||
- phba->sysfs_mbox.state != SMBOX_READING) {
- printk(KERN_WARNING "mbox_read: Bad State\n");
- sysfs_mbox_idle(phba);
- spin_unlock_irq(&phba->hbalock);
- return -EAGAIN;
- }
-
- memcpy(buf, (uint8_t *) &pmb + off, count);
-
- phba->sysfs_mbox.offset = off + count;
-
- if (phba->sysfs_mbox.offset == MAILBOX_CMD_SIZE)
- sysfs_mbox_idle(phba);
-
- spin_unlock_irq(&phba->hbalock);
-
- return count;
+ return -EPERM;
}
-static struct bin_attribute sysfs_mbox_attr = {
+static const struct bin_attribute sysfs_mbox_attr = {
.attr = {
.name = "mbox",
.mode = S_IRUSR | S_IWUSR,
@@ -4283,17 +6496,14 @@ lpfc_alloc_sysfs_attr(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
int error;
- error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_drvr_stat_data_attr);
-
/* Virtual ports do not need ctrl_reg and mbox */
- if (error || vport->port_type == LPFC_NPIV_PORT)
- goto out;
+ if (vport->port_type == LPFC_NPIV_PORT)
+ return 0;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
&sysfs_ctlreg_attr);
if (error)
- goto out_remove_stat_attr;
+ goto out;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
&sysfs_mbox_attr);
@@ -4303,9 +6513,6 @@ lpfc_alloc_sysfs_attr(struct lpfc_vport *vport)
return 0;
out_remove_ctlreg_attr:
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
-out_remove_stat_attr:
- sysfs_remove_bin_file(&shost->shost_dev.kobj,
- &sysfs_drvr_stat_data_attr);
out:
return error;
}
@@ -4318,8 +6525,7 @@ void
lpfc_free_sysfs_attr(struct lpfc_vport *vport)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- sysfs_remove_bin_file(&shost->shost_dev.kobj,
- &sysfs_drvr_stat_data_attr);
+
/* Virtual ports do not need ctrl_reg and mbox */
if (vport->port_type == LPFC_NPIV_PORT)
return;
@@ -4327,12 +6533,24 @@ lpfc_free_sysfs_attr(struct lpfc_vport *vport)
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
}
-
/*
* Dynamic FC Host Attributes Support
*/
/**
+ * lpfc_get_host_symbolic_name - Copy symbolic name into the scsi host
+ * @shost: kernel scsi host pointer.
+ **/
+static void
+lpfc_get_host_symbolic_name(struct Scsi_Host *shost)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+
+ lpfc_vport_symbolic_node_name(vport, fc_host_symbolic_name(shost),
+ sizeof fc_host_symbolic_name(shost));
+}
+
+/**
* lpfc_get_host_port_id - Copy the vport DID into the scsi host port id
* @shost: kernel scsi host pointer.
**/
@@ -4355,26 +6573,22 @@ lpfc_get_host_port_type(struct Scsi_Host *shost)
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- spin_lock_irq(shost->host_lock);
-
if (vport->port_type == LPFC_NPIV_PORT) {
fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
} else if (lpfc_is_link_up(phba)) {
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
- if (vport->fc_flag & FC_PUBLIC_LOOP)
+ if (test_bit(FC_PUBLIC_LOOP, &vport->fc_flag))
fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
else
fc_host_port_type(shost) = FC_PORTTYPE_LPORT;
} else {
- if (vport->fc_flag & FC_FABRIC)
+ if (test_bit(FC_FABRIC, &vport->fc_flag))
fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
else
fc_host_port_type(shost) = FC_PORTTYPE_PTP;
}
} else
fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
-
- spin_unlock_irq(shost->host_lock);
}
/**
@@ -4387,9 +6601,7 @@ lpfc_get_host_port_state(struct Scsi_Host *shost)
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- spin_lock_irq(shost->host_lock);
-
- if (vport->fc_flag & FC_OFFLINE_MODE)
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag))
fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
else {
switch (phba->link_state) {
@@ -4400,8 +6612,13 @@ lpfc_get_host_port_state(struct Scsi_Host *shost)
case LPFC_LINK_UP:
case LPFC_CLEAR_LA:
case LPFC_HBA_READY:
- /* Links up, beyond this port_type reports state */
- fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ /* Links up, reports port state accordingly */
+ if (vport->port_state < LPFC_VPORT_READY)
+ fc_host_port_state(shost) =
+ FC_PORTSTATE_BYPASSED;
+ else
+ fc_host_port_state(shost) =
+ FC_PORTSTATE_ONLINE;
break;
case LPFC_HBA_ERROR:
fc_host_port_state(shost) = FC_PORTSTATE_ERROR;
@@ -4411,8 +6628,6 @@ lpfc_get_host_port_state(struct Scsi_Host *shost)
break;
}
}
-
- spin_unlock_irq(shost->host_lock);
}
/**
@@ -4425,9 +6640,8 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- spin_lock_irq(shost->host_lock);
-
- if (lpfc_is_link_up(phba)) {
+ if ((lpfc_is_link_up(phba)) &&
+ !test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
switch(phba->fc_linkspeed) {
case LPFC_LINK_SPEED_1GHZ:
fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
@@ -4447,14 +6661,49 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
case LPFC_LINK_SPEED_16GHZ:
fc_host_speed(shost) = FC_PORTSPEED_16GBIT;
break;
+ case LPFC_LINK_SPEED_32GHZ:
+ fc_host_speed(shost) = FC_PORTSPEED_32GBIT;
+ break;
+ case LPFC_LINK_SPEED_64GHZ:
+ fc_host_speed(shost) = FC_PORTSPEED_64GBIT;
+ break;
+ case LPFC_LINK_SPEED_128GHZ:
+ fc_host_speed(shost) = FC_PORTSPEED_128GBIT;
+ break;
+ case LPFC_LINK_SPEED_256GHZ:
+ fc_host_speed(shost) = FC_PORTSPEED_256GBIT;
+ break;
+ default:
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+ break;
+ }
+ } else if (lpfc_is_link_up(phba) &&
+ test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ switch (phba->fc_linkspeed) {
+ case LPFC_ASYNC_LINK_SPEED_1GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_10GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_20GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_20GBIT;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_25GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_25GBIT;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_40GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_40GBIT;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_100GBIT;
+ break;
default:
fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
break;
}
} else
fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
-
- spin_unlock_irq(shost->host_lock);
}
/**
@@ -4468,18 +6717,15 @@ lpfc_get_host_fabric_name (struct Scsi_Host *shost)
struct lpfc_hba *phba = vport->phba;
u64 node_name;
- spin_lock_irq(shost->host_lock);
-
- if ((vport->fc_flag & FC_FABRIC) ||
- ((phba->fc_topology == LPFC_TOPOLOGY_LOOP) &&
- (vport->fc_flag & FC_PUBLIC_LOOP)))
+ if (vport->port_state > LPFC_FLOGI &&
+ (test_bit(FC_FABRIC, &vport->fc_flag) ||
+ (phba->fc_topology == LPFC_TOPOLOGY_LOOP &&
+ test_bit(FC_PUBLIC_LOOP, &vport->fc_flag))))
node_name = wwn_to_u64(phba->fc_fabparam.nodeName.u.wwn);
else
/* fabric is local port if there is no F/FL_Port */
node_name = 0;
- spin_unlock_irq(shost->host_lock);
-
fc_host_fabric_name(shost) = node_name;
}
@@ -4505,7 +6751,6 @@ lpfc_get_stats(struct Scsi_Host *shost)
struct lpfc_lnk_stat * lso = &psli->lnk_stat_offsets;
LPFC_MBOXQ_t *pmboxq;
MAILBOX_t *pmb;
- unsigned long seconds;
int rc = 0;
/*
@@ -4528,42 +6773,75 @@ lpfc_get_stats(struct Scsi_Host *shost)
pmb = &pmboxq->u.mb;
pmb->mbxCommand = MBX_READ_STATUS;
pmb->mbxOwner = OWN_HOST;
- pmboxq->context1 = NULL;
+ pmboxq->ctx_buf = NULL;
pmboxq->vport = vport;
- if (vport->fc_flag & FC_OFFLINE_MODE)
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) {
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
- else
- rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
-
- if (rc != MBX_SUCCESS) {
- if (rc != MBX_TIMEOUT)
+ if (rc != MBX_SUCCESS) {
mempool_free(pmboxq, phba->mbox_mem_pool);
- return NULL;
+ return NULL;
+ }
+ } else {
+ rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+ if (rc != MBX_SUCCESS) {
+ if (rc != MBX_TIMEOUT)
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return NULL;
+ }
}
memset(hs, 0, sizeof (struct fc_host_statistics));
hs->tx_frames = pmb->un.varRdStatus.xmitFrameCnt;
- hs->tx_words = (pmb->un.varRdStatus.xmitByteCnt * 256);
hs->rx_frames = pmb->un.varRdStatus.rcvFrameCnt;
- hs->rx_words = (pmb->un.varRdStatus.rcvByteCnt * 256);
+
+ /*
+ * The MBX_READ_STATUS returns tx_k_bytes which has to be
+ * converted to words.
+ *
+ * Check if extended byte flag is set, to know when to collect upper
+ * bits of 64 bit wide statistics counter.
+ */
+ if (pmb->un.varRdStatus.xkb & RD_ST_XKB) {
+ hs->tx_words = (u64)
+ ((((u64)(pmb->un.varRdStatus.xmit_xkb &
+ RD_ST_XMIT_XKB_MASK) << 32) |
+ (u64)pmb->un.varRdStatus.xmitByteCnt) *
+ (u64)256);
+ hs->rx_words = (u64)
+ ((((u64)(pmb->un.varRdStatus.rcv_xkb &
+ RD_ST_RCV_XKB_MASK) << 32) |
+ (u64)pmb->un.varRdStatus.rcvByteCnt) *
+ (u64)256);
+ } else {
+ hs->tx_words = (uint64_t)
+ ((uint64_t)pmb->un.varRdStatus.xmitByteCnt
+ * (uint64_t)256);
+ hs->rx_words = (uint64_t)
+ ((uint64_t)pmb->un.varRdStatus.rcvByteCnt
+ * (uint64_t)256);
+ }
memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t));
pmb->mbxCommand = MBX_READ_LNK_STAT;
pmb->mbxOwner = OWN_HOST;
- pmboxq->context1 = NULL;
+ pmboxq->ctx_buf = NULL;
pmboxq->vport = vport;
- if (vport->fc_flag & FC_OFFLINE_MODE)
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) {
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
- else
- rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
-
- if (rc != MBX_SUCCESS) {
- if (rc != MBX_TIMEOUT)
+ if (rc != MBX_SUCCESS) {
mempool_free(pmboxq, phba->mbox_mem_pool);
- return NULL;
+ return NULL;
+ }
+ } else {
+ rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+ if (rc != MBX_SUCCESS) {
+ if (rc != MBX_TIMEOUT)
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return NULL;
+ }
}
hs->link_failure_count = pmb->un.varRdLnk.linkFailureCnt;
@@ -4574,6 +6852,9 @@ lpfc_get_stats(struct Scsi_Host *shost)
hs->invalid_crc_count = pmb->un.varRdLnk.crcCnt;
hs->error_frames = pmb->un.varRdLnk.crcCnt;
+ hs->cn_sig_warn = atomic64_read(&phba->cgn_acqe_stat.warn);
+ hs->cn_sig_alarm = atomic64_read(&phba->cgn_acqe_stat.alarm);
+
hs->link_failure_count -= lso->link_failure_count;
hs->loss_of_sync_count -= lso->loss_of_sync_count;
hs->loss_of_signal_count -= lso->loss_of_signal_count;
@@ -4582,7 +6863,7 @@ lpfc_get_stats(struct Scsi_Host *shost)
hs->invalid_crc_count -= lso->invalid_crc_count;
hs->error_frames -= lso->error_frames;
- if (phba->hba_flag & HBA_FCOE_MODE) {
+ if (test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
hs->lip_count = -1;
hs->nos_count = (phba->link_events >> 1);
hs->nos_count -= lso->link_events;
@@ -4598,12 +6879,7 @@ lpfc_get_stats(struct Scsi_Host *shost)
hs->dumped_frames = -1;
- seconds = get_seconds();
- if (seconds < psli->stats_start)
- hs->seconds_since_last_reset = seconds +
- ((unsigned long)-1 - psli->stats_start);
- else
- hs->seconds_since_last_reset = seconds - psli->stats_start;
+ hs->seconds_since_last_reset = ktime_get_seconds() - psli->stats_start;
mempool_free(pmboxq, phba->mbox_mem_pool);
@@ -4637,37 +6913,45 @@ lpfc_reset_stats(struct Scsi_Host *shost)
pmb->mbxCommand = MBX_READ_STATUS;
pmb->mbxOwner = OWN_HOST;
pmb->un.varWords[0] = 0x1; /* reset request */
- pmboxq->context1 = NULL;
+ pmboxq->ctx_buf = NULL;
pmboxq->vport = vport;
- if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag) ||
+ !(psli->sli_flag & LPFC_SLI_ACTIVE)) {
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
- else
- rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
-
- if (rc != MBX_SUCCESS) {
- if (rc != MBX_TIMEOUT)
+ if (rc != MBX_SUCCESS) {
mempool_free(pmboxq, phba->mbox_mem_pool);
- return;
+ return;
+ }
+ } else {
+ rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+ if (rc != MBX_SUCCESS) {
+ if (rc != MBX_TIMEOUT)
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+ }
}
memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
pmb->mbxCommand = MBX_READ_LNK_STAT;
pmb->mbxOwner = OWN_HOST;
- pmboxq->context1 = NULL;
+ pmboxq->ctx_buf = NULL;
pmboxq->vport = vport;
- if ((vport->fc_flag & FC_OFFLINE_MODE) ||
- (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag) ||
+ !(psli->sli_flag & LPFC_SLI_ACTIVE)) {
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
- else
+ if (rc != MBX_SUCCESS) {
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+ }
+ } else {
rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
-
- if (rc != MBX_SUCCESS) {
- if (rc != MBX_TIMEOUT)
- mempool_free( pmboxq, phba->mbox_mem_pool);
- return;
+ if (rc != MBX_SUCCESS) {
+ if (rc != MBX_TIMEOUT)
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+ }
}
lso->link_failure_count = pmb->un.varRdLnk.linkFailureCnt;
@@ -4677,12 +6961,18 @@ lpfc_reset_stats(struct Scsi_Host *shost)
lso->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord;
lso->invalid_crc_count = pmb->un.varRdLnk.crcCnt;
lso->error_frames = pmb->un.varRdLnk.crcCnt;
- if (phba->hba_flag & HBA_FCOE_MODE)
+ if (test_bit(HBA_FCOE_MODE, &phba->hba_flag))
lso->link_events = (phba->link_events >> 1);
else
lso->link_events = (phba->fc_eventTag >> 1);
- psli->stats_start = get_seconds();
+ atomic64_set(&phba->cgn_acqe_stat.warn, 0);
+ atomic64_set(&phba->cgn_acqe_stat.alarm, 0);
+
+ memset(&shost_to_fc_host(shost)->fpin_stats, 0,
+ sizeof(shost_to_fc_host(shost)->fpin_stats));
+
+ psli->stats_start = ktime_get_seconds();
mempool_free(pmboxq, phba->mbox_mem_pool);
@@ -4708,18 +6998,19 @@ lpfc_get_node_by_target(struct scsi_target *starget)
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_nodelist *ndlp;
+ unsigned long iflags;
- spin_lock_irq(shost->host_lock);
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
/* Search for this, mapped, target ID */
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
- if (NLP_CHK_NODE_ACT(ndlp) &&
- ndlp->nlp_state == NLP_STE_MAPPED_NODE &&
+ if (ndlp->nlp_state == NLP_STE_MAPPED_NODE &&
starget->id == ndlp->nlp_sid) {
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock,
+ iflags);
return ndlp;
}
}
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
return NULL;
}
@@ -4777,13 +7068,34 @@ lpfc_get_starget_port_name(struct scsi_target *starget)
static void
lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
{
+ struct lpfc_rport_data *rdata = rport->dd_data;
+ struct lpfc_nodelist *ndlp = rdata->pnode;
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ struct lpfc_nvme_rport *nrport = NULL;
+#endif
+
if (timeout)
rport->dev_loss_tmo = timeout;
else
rport->dev_loss_tmo = 1;
+
+ if (!ndlp) {
+ dev_info(&rport->dev, "Cannot find remote node to "
+ "set rport dev loss tmo, port_id x%x\n",
+ rport->port_id);
+ return;
+ }
+
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ nrport = lpfc_ndlp_get_nrport(ndlp);
+
+ if (nrport && nrport->remoteport)
+ nvme_fc_set_remoteport_devloss(nrport->remoteport,
+ rport->dev_loss_tmo);
+#endif
}
-/**
+/*
* lpfc_rport_show_function - Return rport target information
*
* Description:
@@ -4803,7 +7115,7 @@ lpfc_show_rport_##field (struct device *dev, \
{ \
struct fc_rport *rport = transport_class_to_rport(dev); \
struct lpfc_rport_data *rdata = rport->hostdata; \
- return snprintf(buf, sz, format_string, \
+ return scnprintf(buf, sz, format_string, \
(rdata->target) ? cast rdata->target->field : 0); \
}
@@ -4832,6 +7144,7 @@ lpfc_set_vport_symbolic_name(struct fc_vport *fc_vport)
/**
* lpfc_hba_log_verbose_init - Set hba's log verbose level
* @phba: Pointer to lpfc_hba struct.
+ * @verbose: Verbose level to set.
*
* This function is called by the lpfc_get_cfgparam() routine to set the
* module lpfc_log_verbose into the @phba cfg_log_verbose for use with
@@ -4852,6 +7165,8 @@ struct fc_function_template lpfc_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+
+ .get_host_symbolic_name = lpfc_get_host_symbolic_name,
.show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
@@ -4919,6 +7234,8 @@ struct fc_function_template lpfc_vport_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+
+ .get_host_symbolic_name = lpfc_get_host_symbolic_name,
.show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
@@ -4973,49 +7290,238 @@ struct fc_function_template lpfc_vport_transport_functions = {
};
/**
+ * lpfc_get_hba_function_mode - Used to determine the HBA function in FCoE
+ * Mode
+ * @phba: lpfc_hba pointer.
+ **/
+static void
+lpfc_get_hba_function_mode(struct lpfc_hba *phba)
+{
+ /* If the adapter supports FCoE mode */
+ switch (phba->pcidev->device) {
+ case PCI_DEVICE_ID_SKYHAWK:
+ case PCI_DEVICE_ID_SKYHAWK_VF:
+ case PCI_DEVICE_ID_LANCER_FCOE:
+ case PCI_DEVICE_ID_LANCER_FCOE_VF:
+ case PCI_DEVICE_ID_ZEPHYR_DCSP:
+ case PCI_DEVICE_ID_TIGERSHARK:
+ case PCI_DEVICE_ID_TOMCAT:
+ set_bit(HBA_FCOE_MODE, &phba->hba_flag);
+ break;
+ default:
+ /* for others, clear the flag */
+ clear_bit(HBA_FCOE_MODE, &phba->hba_flag);
+ }
+}
+
+/**
* lpfc_get_cfgparam - Used during probe_one to init the adapter structure
* @phba: lpfc_hba pointer.
**/
void
lpfc_get_cfgparam(struct lpfc_hba *phba)
{
+ lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
+ lpfc_fcp_io_sched_init(phba, lpfc_fcp_io_sched);
+ lpfc_ns_query_init(phba, lpfc_ns_query);
+ lpfc_fcp2_no_tgt_reset_init(phba, lpfc_fcp2_no_tgt_reset);
lpfc_cr_delay_init(phba, lpfc_cr_delay);
lpfc_cr_count_init(phba, lpfc_cr_count);
lpfc_multi_ring_support_init(phba, lpfc_multi_ring_support);
lpfc_multi_ring_rctl_init(phba, lpfc_multi_ring_rctl);
lpfc_multi_ring_type_init(phba, lpfc_multi_ring_type);
lpfc_ack0_init(phba, lpfc_ack0);
+ lpfc_xri_rebalancing_init(phba, lpfc_xri_rebalancing);
lpfc_topology_init(phba, lpfc_topology);
lpfc_link_speed_init(phba, lpfc_link_speed);
lpfc_poll_tmo_init(phba, lpfc_poll_tmo);
+ lpfc_task_mgmt_tmo_init(phba, lpfc_task_mgmt_tmo);
lpfc_enable_npiv_init(phba, lpfc_enable_npiv);
+ lpfc_fcf_failover_policy_init(phba, lpfc_fcf_failover_policy);
lpfc_enable_rrq_init(phba, lpfc_enable_rrq);
+ lpfc_fcp_wait_abts_rsp_init(phba, lpfc_fcp_wait_abts_rsp);
+ lpfc_fdmi_on_init(phba, lpfc_fdmi_on);
+ lpfc_enable_SmartSAN_init(phba, lpfc_enable_SmartSAN);
lpfc_use_msi_init(phba, lpfc_use_msi);
+ lpfc_nvme_oas_init(phba, lpfc_nvme_oas);
+ lpfc_nvme_embed_cmd_init(phba, lpfc_nvme_embed_cmd);
lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
- lpfc_fcp_wq_count_init(phba, lpfc_fcp_wq_count);
- lpfc_fcp_eq_count_init(phba, lpfc_fcp_eq_count);
+ lpfc_force_rscn_init(phba, lpfc_force_rscn);
+ lpfc_cq_poll_threshold_init(phba, lpfc_cq_poll_threshold);
+ lpfc_cq_max_proc_limit_init(phba, lpfc_cq_max_proc_limit);
+ lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map);
lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
+
+ lpfc_EnableXLane_init(phba, lpfc_EnableXLane);
+ /* VMID Inits */
+ lpfc_max_vmid_init(phba, lpfc_max_vmid);
+ lpfc_vmid_inactivity_timeout_init(phba, lpfc_vmid_inactivity_timeout);
+ lpfc_vmid_app_header_init(phba, lpfc_vmid_app_header);
+ lpfc_vmid_priority_tagging_init(phba, lpfc_vmid_priority_tagging);
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ phba->cfg_EnableXLane = 0;
+ lpfc_XLanePriority_init(phba, lpfc_XLanePriority);
+
+ memset(phba->cfg_oas_tgt_wwpn, 0, (8 * sizeof(uint8_t)));
+ memset(phba->cfg_oas_vpt_wwpn, 0, (8 * sizeof(uint8_t)));
+ phba->cfg_oas_lun_state = 0;
+ phba->cfg_oas_lun_status = 0;
+ phba->cfg_oas_flags = 0;
+ phba->cfg_oas_priority = 0;
lpfc_enable_bg_init(phba, lpfc_enable_bg);
+ lpfc_prot_mask_init(phba, lpfc_prot_mask);
+ lpfc_prot_guard_init(phba, lpfc_prot_guard);
if (phba->sli_rev == LPFC_SLI_REV4)
phba->cfg_poll = 0;
else
- phba->cfg_poll = lpfc_poll;
- phba->cfg_soft_wwnn = 0L;
- phba->cfg_soft_wwpn = 0L;
+ phba->cfg_poll = lpfc_poll;
+
+ /* Get the function mode */
+ lpfc_get_hba_function_mode(phba);
+
+ /* BlockGuard allowed for FC only. */
+ if (phba->cfg_enable_bg && test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0581 BlockGuard feature not supported\n");
+ /* If set, clear the BlockGuard support param */
+ phba->cfg_enable_bg = 0;
+ } else if (phba->cfg_enable_bg) {
+ phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
+ }
+
+ lpfc_suppress_rsp_init(phba, lpfc_suppress_rsp);
+
+ lpfc_enable_fc4_type_init(phba, lpfc_enable_fc4_type);
+ lpfc_nvmet_mrq_init(phba, lpfc_nvmet_mrq);
+ lpfc_nvmet_mrq_post_init(phba, lpfc_nvmet_mrq_post);
+
+ /* Initialize first burst. Target vs Initiator are different. */
+ lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb);
+ lpfc_nvmet_fb_size_init(phba, lpfc_nvmet_fb_size);
+ lpfc_fcp_mq_threshold_init(phba, lpfc_fcp_mq_threshold);
+ lpfc_hdw_queue_init(phba, lpfc_hdw_queue);
+ lpfc_irq_chann_init(phba, lpfc_irq_chann);
+ lpfc_enable_bbcr_init(phba, lpfc_enable_bbcr);
+ lpfc_enable_dpp_init(phba, lpfc_enable_dpp);
+ lpfc_enable_mi_init(phba, lpfc_enable_mi);
+
+ phba->cgn_p.cgn_param_mode = LPFC_CFG_OFF;
+ phba->cmf_active_mode = LPFC_CFG_OFF;
+ if (lpfc_fabric_cgn_frequency > EDC_CG_SIGFREQ_CNT_MAX ||
+ lpfc_fabric_cgn_frequency < EDC_CG_SIGFREQ_CNT_MIN)
+ lpfc_fabric_cgn_frequency = 100; /* 100 ms default */
+
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ /* NVME only supported on SLI4 */
+ phba->nvmet_support = 0;
+ phba->cfg_nvmet_mrq = 0;
+ phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP;
+ phba->cfg_enable_bbcr = 0;
+ phba->cfg_xri_rebalancing = 0;
+ } else {
+ /* We MUST have FCP support */
+ if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
+ phba->cfg_enable_fc4_type |= LPFC_ENABLE_FCP;
+ }
+
+ phba->cfg_auto_imax = (phba->cfg_fcp_imax) ? 0 : 1;
+
+ phba->cfg_enable_pbde = 0;
+
+ /* A value of 0 means use the number of CPUs found in the system */
+ if (phba->cfg_hdw_queue == 0)
+ phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu;
+ if (phba->cfg_irq_chann == 0)
+ phba->cfg_irq_chann = phba->sli4_hba.num_present_cpu;
+ if (phba->cfg_irq_chann > phba->cfg_hdw_queue &&
+ phba->sli_rev == LPFC_SLI_REV4)
+ phba->cfg_irq_chann = phba->cfg_hdw_queue;
+
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
- lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
- lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
- lpfc_aer_support_init(phba, lpfc_aer_support);
lpfc_sriov_nr_virtfn_init(phba, lpfc_sriov_nr_virtfn);
+ lpfc_request_firmware_upgrade_init(phba, lpfc_req_fw_upgrade);
lpfc_suppress_link_up_init(phba, lpfc_suppress_link_up);
- lpfc_iocb_cnt_init(phba, lpfc_iocb_cnt);
- phba->cfg_enable_dss = 1;
+ lpfc_delay_discovery_init(phba, lpfc_delay_discovery);
+ lpfc_sli_mode_init(phba, lpfc_sli_mode);
+ lpfc_enable_mds_diags_init(phba, lpfc_enable_mds_diags);
+ lpfc_ras_fwlog_buffsize_init(phba, lpfc_ras_fwlog_buffsize);
+ lpfc_ras_fwlog_level_init(phba, lpfc_ras_fwlog_level);
+ lpfc_ras_fwlog_func_init(phba, lpfc_ras_fwlog_func);
+
return;
}
/**
+ * lpfc_nvme_mod_param_dep - Adjust module parameter value based on
+ * dependencies between protocols and roles.
+ * @phba: lpfc_hba pointer.
+ **/
+void
+lpfc_nvme_mod_param_dep(struct lpfc_hba *phba)
+{
+ int logit = 0;
+
+ if (phba->cfg_hdw_queue > phba->sli4_hba.num_present_cpu) {
+ phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu;
+ logit = 1;
+ }
+ if (phba->cfg_irq_chann > phba->sli4_hba.num_present_cpu) {
+ phba->cfg_irq_chann = phba->sli4_hba.num_present_cpu;
+ logit = 1;
+ }
+ if (phba->cfg_irq_chann > phba->cfg_hdw_queue) {
+ phba->cfg_irq_chann = phba->cfg_hdw_queue;
+ logit = 1;
+ }
+ if (logit)
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2006 Reducing Queues - CPU limitation: "
+ "IRQ %d HDWQ %d\n",
+ phba->cfg_irq_chann,
+ phba->cfg_hdw_queue);
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME &&
+ phba->nvmet_support) {
+ phba->cfg_enable_fc4_type &= ~LPFC_ENABLE_FCP;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6013 %s x%x fb_size x%x, fb_max x%x\n",
+ "NVME Target PRLI ACC enable_fb ",
+ phba->cfg_nvme_enable_fb,
+ phba->cfg_nvmet_fb_size,
+ LPFC_NVMET_FB_SZ_MAX);
+
+ if (phba->cfg_nvme_enable_fb == 0)
+ phba->cfg_nvmet_fb_size = 0;
+ else {
+ if (phba->cfg_nvmet_fb_size > LPFC_NVMET_FB_SZ_MAX)
+ phba->cfg_nvmet_fb_size = LPFC_NVMET_FB_SZ_MAX;
+ }
+
+ if (!phba->cfg_nvmet_mrq)
+ phba->cfg_nvmet_mrq = phba->cfg_hdw_queue;
+
+ /* Adjust lpfc_nvmet_mrq to avoid running out of WQE slots */
+ if (phba->cfg_nvmet_mrq > phba->cfg_hdw_queue) {
+ phba->cfg_nvmet_mrq = phba->cfg_hdw_queue;
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC,
+ "6018 Adjust lpfc_nvmet_mrq to %d\n",
+ phba->cfg_nvmet_mrq);
+ }
+ if (phba->cfg_nvmet_mrq > LPFC_NVMET_MRQ_MAX)
+ phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_MAX;
+
+ } else {
+ /* Not NVME Target mode. Turn off Target parameters. */
+ phba->nvmet_support = 0;
+ phba->cfg_nvmet_mrq = 0;
+ phba->cfg_nvmet_fb_size = 0;
+ }
+}
+
+/**
* lpfc_get_vport_cfgparam - Used during port create, init the vport structure
* @vport: lpfc_vport pointer.
**/
@@ -5031,8 +7537,8 @@ lpfc_get_vport_cfgparam(struct lpfc_vport *vport)
lpfc_restrict_login_init(vport, lpfc_restrict_login);
lpfc_fcp_class_init(vport, lpfc_fcp_class);
lpfc_use_adisc_init(vport, lpfc_use_adisc);
+ lpfc_first_burst_size_init(vport, lpfc_first_burst_size);
lpfc_max_scsicmpl_time_init(vport, lpfc_max_scsicmpl_time);
- lpfc_fdmi_on_init(vport, lpfc_fdmi_on);
lpfc_discovery_threads_init(vport, lpfc_discovery_threads);
lpfc_max_luns_init(vport, lpfc_max_luns);
lpfc_scan_down_init(vport, lpfc_scan_down);
diff --git a/drivers/scsi/lpfc/lpfc_attr.h b/drivers/scsi/lpfc/lpfc_attr.h
new file mode 100644
index 000000000000..9659a8fff971
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_attr.h
@@ -0,0 +1,128 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.broadcom.com *
+ * Portions Copyright (C) 2004-2005 Christoph Hellwig *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *******************************************************************/
+
+#define LPFC_ATTR(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_init(name, defval, minval, maxval)
+
+#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+lpfc_param_init(name, defval, minval, maxval)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO, lpfc_##name##_show, NULL)
+
+#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+lpfc_param_init(name, defval, minval, maxval)\
+lpfc_param_set(name, defval, minval, maxval)\
+lpfc_param_store(name)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
+ lpfc_##name##_show, lpfc_##name##_store)
+
+#define LPFC_BBCR_ATTR_RW(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, 0444);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+lpfc_param_init(name, defval, minval, maxval)\
+lpfc_param_store(name)\
+static DEVICE_ATTR(lpfc_##name, 0444 | 0644,\
+ lpfc_##name##_show, lpfc_##name##_store)
+
+#define LPFC_ATTR_HEX_R(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_hex_show(name)\
+lpfc_param_init(name, defval, minval, maxval)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO, lpfc_##name##_show, NULL)
+
+#define LPFC_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_hex_show(name)\
+lpfc_param_init(name, defval, minval, maxval)\
+lpfc_param_set(name, defval, minval, maxval)\
+lpfc_param_store(name)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
+ lpfc_##name##_show, lpfc_##name##_store)
+
+#define LPFC_VPORT_ATTR(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_vport_param_init(name, defval, minval, maxval)
+
+#define LPFC_VPORT_ATTR_R(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_vport_param_show(name)\
+lpfc_vport_param_init(name, defval, minval, maxval)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO, lpfc_##name##_show, NULL)
+
+#define LPFC_VPORT_ULL_ATTR_R(name, defval, minval, maxval, desc) \
+static uint64_t lpfc_##name = defval;\
+module_param(lpfc_##name, ullong, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_vport_param_show(name)\
+lpfc_vport_param_init(name, defval, minval, maxval)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO, lpfc_##name##_show, NULL)
+
+#define LPFC_VPORT_ATTR_RW(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_vport_param_show(name)\
+lpfc_vport_param_init(name, defval, minval, maxval)\
+lpfc_vport_param_set(name, defval, minval, maxval)\
+lpfc_vport_param_store(name)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
+ lpfc_##name##_show, lpfc_##name##_store)
+
+#define LPFC_VPORT_ATTR_HEX_R(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_vport_param_hex_show(name)\
+lpfc_vport_param_init(name, defval, minval, maxval)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO, lpfc_##name##_show, NULL)
+
+#define LPFC_VPORT_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
+static uint lpfc_##name = defval;\
+module_param(lpfc_##name, uint, S_IRUGO);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_vport_param_hex_show(name)\
+lpfc_vport_param_init(name, defval, minval, maxval)\
+lpfc_vport_param_set(name, defval, minval, maxval)\
+lpfc_vport_param_store(name)\
+static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
+ lpfc_##name##_show, lpfc_##name##_store)
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index 7fb0ba4cbfa7..d61d979f9b77 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2009-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2009-2015 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -24,6 +26,8 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/list.h>
+#include <linux/bsg-lib.h>
+#include <linux/vmalloc.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
@@ -42,6 +46,7 @@
#include "lpfc.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
+#include "lpfc_debugfs.h"
#include "lpfc_vport.h"
#include "lpfc_version.h"
@@ -63,18 +68,14 @@ struct lpfc_bsg_event {
struct list_head events_to_get;
struct list_head events_to_see;
- /* job waiting for this event to finish */
- struct fc_bsg_job *set_job;
+ /* driver data associated with the job */
+ void *dd_data;
};
struct lpfc_bsg_iocb {
struct lpfc_iocbq *cmdiocbq;
- struct lpfc_iocbq *rspiocbq;
- struct lpfc_dmabuf *bmp;
+ struct lpfc_dmabuf *rmp;
struct lpfc_nodelist *ndlp;
-
- /* job waiting for this iocb to finish */
- struct fc_bsg_job *set_job;
};
struct lpfc_bsg_mbox {
@@ -85,33 +86,18 @@ struct lpfc_bsg_mbox {
uint32_t mbOffset; /* from app */
uint32_t inExtWLen; /* from app */
uint32_t outExtWLen; /* from app */
-
- /* job waiting for this mbox command to finish */
- struct fc_bsg_job *set_job;
-};
-
-#define MENLO_DID 0x0000FC0E
-
-struct lpfc_bsg_menlo {
- struct lpfc_iocbq *cmdiocbq;
- struct lpfc_iocbq *rspiocbq;
- struct lpfc_dmabuf *bmp;
-
- /* job waiting for this iocb to finish */
- struct fc_bsg_job *set_job;
};
#define TYPE_EVT 1
#define TYPE_IOCB 2
#define TYPE_MBOX 3
-#define TYPE_MENLO 4
struct bsg_job_data {
uint32_t type;
+ struct bsg_job *set_job; /* job waiting for this iocb to finish */
union {
struct lpfc_bsg_event *evt;
struct lpfc_bsg_iocb iocb;
struct lpfc_bsg_mbox mbox;
- struct lpfc_bsg_menlo menlo;
} context_un;
};
@@ -134,12 +120,165 @@ enum ELX_LOOPBACK_CMD {
#define ELX_LOOPBACK_HEADER_SZ \
(size_t)(&((struct lpfc_sli_ct_request *)NULL)->un)
+/* For non-embedded read object command */
+#define READ_OBJ_EMB0_SCHEME_0 {1, 10, 256, 128}
+#define READ_OBJ_EMB0_SCHEME_1 {11, LPFC_EMB0_MAX_RD_OBJ_HBD_CNT, 512, 192}
+static const struct lpfc_read_object_cmd_scheme {
+ u32 min_hbd_cnt;
+ u32 max_hbd_cnt;
+ u32 cmd_size;
+ u32 payload_word_offset;
+} rd_obj_scheme[2] = {READ_OBJ_EMB0_SCHEME_0, READ_OBJ_EMB0_SCHEME_1};
+
struct lpfc_dmabufext {
struct lpfc_dmabuf dma;
uint32_t size;
uint32_t flag;
};
+static void
+lpfc_free_bsg_buffers(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
+{
+ struct lpfc_dmabuf *mlast, *next_mlast;
+
+ if (mlist) {
+ list_for_each_entry_safe(mlast, next_mlast, &mlist->list,
+ list) {
+ list_del(&mlast->list);
+ lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
+ kfree(mlast);
+ }
+ lpfc_mbuf_free(phba, mlist->virt, mlist->phys);
+ kfree(mlist);
+ }
+ return;
+}
+
+static struct lpfc_dmabuf *
+lpfc_alloc_bsg_buffers(struct lpfc_hba *phba, unsigned int size,
+ int outbound_buffers, struct ulp_bde64 *bpl,
+ int *bpl_entries)
+{
+ struct lpfc_dmabuf *mlist = NULL;
+ struct lpfc_dmabuf *mp;
+ unsigned int bytes_left = size;
+
+ /* Verify we can support the size specified */
+ if (!size || (size > (*bpl_entries * LPFC_BPL_SIZE)))
+ return NULL;
+
+ /* Determine the number of dma buffers to allocate */
+ *bpl_entries = (size % LPFC_BPL_SIZE ? size/LPFC_BPL_SIZE + 1 :
+ size/LPFC_BPL_SIZE);
+
+ /* Allocate dma buffer and place in BPL passed */
+ while (bytes_left) {
+ /* Allocate dma buffer */
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!mp) {
+ if (mlist)
+ lpfc_free_bsg_buffers(phba, mlist);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&mp->list);
+ mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
+
+ if (!mp->virt) {
+ kfree(mp);
+ if (mlist)
+ lpfc_free_bsg_buffers(phba, mlist);
+ return NULL;
+ }
+
+ /* Queue it to a linked list */
+ if (!mlist)
+ mlist = mp;
+ else
+ list_add_tail(&mp->list, &mlist->list);
+
+ /* Add buffer to buffer pointer list */
+ if (outbound_buffers)
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->addrLow = le32_to_cpu(putPaddrLow(mp->phys));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(mp->phys));
+ bpl->tus.f.bdeSize = (uint16_t)
+ (bytes_left >= LPFC_BPL_SIZE ? LPFC_BPL_SIZE :
+ bytes_left);
+ bytes_left -= bpl->tus.f.bdeSize;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+ }
+ return mlist;
+}
+
+static unsigned int
+lpfc_bsg_copy_data(struct lpfc_dmabuf *dma_buffers,
+ struct bsg_buffer *bsg_buffers,
+ unsigned int bytes_to_transfer, int to_buffers)
+{
+
+ struct lpfc_dmabuf *mp;
+ unsigned int transfer_bytes, bytes_copied = 0;
+ unsigned int sg_offset, dma_offset;
+ unsigned char *dma_address, *sg_address;
+ LIST_HEAD(temp_list);
+ struct sg_mapping_iter miter;
+ unsigned long flags;
+ unsigned int sg_flags = SG_MITER_ATOMIC;
+ bool sg_valid;
+
+ list_splice_init(&dma_buffers->list, &temp_list);
+ list_add(&dma_buffers->list, &temp_list);
+ sg_offset = 0;
+ if (to_buffers)
+ sg_flags |= SG_MITER_FROM_SG;
+ else
+ sg_flags |= SG_MITER_TO_SG;
+ sg_miter_start(&miter, bsg_buffers->sg_list, bsg_buffers->sg_cnt,
+ sg_flags);
+ local_irq_save(flags);
+ sg_valid = sg_miter_next(&miter);
+ list_for_each_entry(mp, &temp_list, list) {
+ dma_offset = 0;
+ while (bytes_to_transfer && sg_valid &&
+ (dma_offset < LPFC_BPL_SIZE)) {
+ dma_address = mp->virt + dma_offset;
+ if (sg_offset) {
+ /* Continue previous partial transfer of sg */
+ sg_address = miter.addr + sg_offset;
+ transfer_bytes = miter.length - sg_offset;
+ } else {
+ sg_address = miter.addr;
+ transfer_bytes = miter.length;
+ }
+ if (bytes_to_transfer < transfer_bytes)
+ transfer_bytes = bytes_to_transfer;
+ if (transfer_bytes > (LPFC_BPL_SIZE - dma_offset))
+ transfer_bytes = LPFC_BPL_SIZE - dma_offset;
+ if (to_buffers)
+ memcpy(dma_address, sg_address, transfer_bytes);
+ else
+ memcpy(sg_address, dma_address, transfer_bytes);
+ dma_offset += transfer_bytes;
+ sg_offset += transfer_bytes;
+ bytes_to_transfer -= transfer_bytes;
+ bytes_copied += transfer_bytes;
+ if (sg_offset >= miter.length) {
+ sg_offset = 0;
+ sg_valid = sg_miter_next(&miter);
+ }
+ }
+ }
+ sg_miter_stop(&miter);
+ local_irq_restore(flags);
+ list_del_init(&dma_buffers->list);
+ list_splice(&temp_list, &dma_buffers->list);
+ return bytes_copied;
+}
+
/**
* lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler
* @phba: Pointer to HBA context object.
@@ -163,64 +302,82 @@ lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba,
struct lpfc_iocbq *rspiocbq)
{
struct bsg_job_data *dd_data;
- struct fc_bsg_job *job;
- IOCB_t *rsp;
- struct lpfc_dmabuf *bmp;
+ struct bsg_job *job;
+ struct fc_bsg_reply *bsg_reply;
+ struct lpfc_dmabuf *bmp, *cmp, *rmp;
struct lpfc_nodelist *ndlp;
struct lpfc_bsg_iocb *iocb;
unsigned long flags;
int rc = 0;
+ u32 ulp_status, ulp_word4, total_data_placed;
+
+ dd_data = cmdiocbq->context_un.dd_data;
+ /* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
- dd_data = cmdiocbq->context2;
- if (!dd_data) {
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- lpfc_sli_release_iocbq(phba, cmdiocbq);
- return;
+ job = dd_data->set_job;
+ if (job) {
+ bsg_reply = job->reply;
+ /* Prevent timeout handling from trying to abort job */
+ job->dd_data = NULL;
}
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ /* Close the timeout handler abort window */
+ spin_lock_irqsave(&phba->hbalock, flags);
+ cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
iocb = &dd_data->context_un.iocb;
- job = iocb->set_job;
- job->dd_data = NULL; /* so timeout handler does not reply */
-
- bmp = iocb->bmp;
- rsp = &rspiocbq->iocb;
- ndlp = cmdiocbq->context1;
-
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
-
- if (rsp->ulpStatus) {
- if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- switch (rsp->un.ulpWord[4] & 0xff) {
- case IOERR_SEQUENCE_TIMEOUT:
- rc = -ETIMEDOUT;
- break;
- case IOERR_INVALID_RPI:
- rc = -EFAULT;
- break;
- default:
+ ndlp = iocb->cmdiocbq->ndlp;
+ rmp = iocb->rmp;
+ cmp = cmdiocbq->cmd_dmabuf;
+ bmp = cmdiocbq->bpl_dmabuf;
+ ulp_status = get_job_ulpstatus(phba, rspiocbq);
+ ulp_word4 = get_job_word4(phba, rspiocbq);
+ total_data_placed = get_job_data_placed(phba, rspiocbq);
+
+ /* Copy the completed data or set the error status */
+
+ if (job) {
+ if (ulp_status) {
+ if (ulp_status == IOSTAT_LOCAL_REJECT) {
+ switch (ulp_word4 & IOERR_PARAM_MASK) {
+ case IOERR_SEQUENCE_TIMEOUT:
+ rc = -ETIMEDOUT;
+ break;
+ case IOERR_INVALID_RPI:
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EACCES;
+ break;
+ }
+ } else {
rc = -EACCES;
- break;
}
- } else
- rc = -EACCES;
- } else
- job->reply->reply_payload_rcv_len =
- rsp->un.genreq64.bdl.bdeSize;
+ } else {
+ bsg_reply->reply_payload_rcv_len =
+ lpfc_bsg_copy_data(rmp, &job->reply_payload,
+ total_data_placed, 0);
+ }
+ }
+ lpfc_free_bsg_buffers(phba, cmp);
+ lpfc_free_bsg_buffers(phba, rmp);
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
- lpfc_sli_release_iocbq(phba, cmdiocbq);
- lpfc_nlp_put(ndlp);
kfree(bmp);
+ lpfc_nlp_put(ndlp);
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
kfree(dd_data);
- /* make error code available to userspace */
- job->reply->result = rc;
- /* complete the job back to userspace */
- job->job_done(job);
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ /* Complete the job if the job is still active */
+
+ if (job) {
+ bsg_reply->result = rc;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ }
return;
}
@@ -229,29 +386,34 @@ lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba,
* @job: fc_bsg_job to handle
**/
static int
-lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
+lpfc_bsg_send_mgmt_cmd(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
+ struct lpfc_rport_data *rdata = fc_bsg_to_rport(job)->dd_data;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata = job->rport->dd_data;
struct lpfc_nodelist *ndlp = rdata->pnode;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct ulp_bde64 *bpl = NULL;
- uint32_t timeout;
struct lpfc_iocbq *cmdiocbq = NULL;
- IOCB_t *cmd;
- struct lpfc_dmabuf *bmp = NULL;
- int request_nseg;
- int reply_nseg;
- struct scatterlist *sgel = NULL;
- int numbde;
- dma_addr_t busaddr;
+ struct lpfc_dmabuf *bmp = NULL, *cmp = NULL, *rmp = NULL;
+ int request_nseg, reply_nseg;
+ u32 num_entry;
struct bsg_job_data *dd_data;
+ unsigned long flags;
uint32_t creg_val;
int rc = 0;
int iocb_stat;
+ u16 ulp_context;
/* in case no data is transferred */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
+
+ if (test_bit(NLP_PLOGI_SND, &ndlp->nlp_flag) ||
+ test_bit(NLP_PRLI_SND, &ndlp->nlp_flag) ||
+ test_bit(NLP_ADISC_SND, &ndlp->nlp_flag) ||
+ test_bit(NLP_LOGO_SND, &ndlp->nlp_flag) ||
+ test_bit(NLP_RNID_SND, &ndlp->nlp_flag))
+ return -ENODEV;
/* allocate our bsg tracking structure */
dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
@@ -262,131 +424,120 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
goto no_dd_data;
}
- if (!lpfc_nlp_get(ndlp)) {
- rc = -ENODEV;
- goto no_ndlp;
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ if (!cmdiocbq) {
+ rc = -ENOMEM;
+ goto free_dd;
}
bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!bmp) {
rc = -ENOMEM;
- goto free_ndlp;
- }
-
- if (ndlp->nlp_flag & NLP_ELS_SND_MASK) {
- rc = -ENODEV;
- goto free_bmp;
- }
-
- cmdiocbq = lpfc_sli_get_iocbq(phba);
- if (!cmdiocbq) {
- rc = -ENOMEM;
- goto free_bmp;
+ goto free_cmdiocbq;
}
-
- cmd = &cmdiocbq->iocb;
bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
if (!bmp->virt) {
rc = -ENOMEM;
- goto free_cmdiocbq;
+ goto free_bmp;
}
INIT_LIST_HEAD(&bmp->list);
+
bpl = (struct ulp_bde64 *) bmp->virt;
- request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
- busaddr = sg_dma_address(sgel);
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl->tus.f.bdeSize = sg_dma_len(sgel);
- bpl->tus.w = cpu_to_le32(bpl->tus.w);
- bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
- bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
- bpl++;
+ request_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64);
+ cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
+ 1, bpl, &request_nseg);
+ if (!cmp) {
+ rc = -ENOMEM;
+ goto free_bmp;
}
+ lpfc_bsg_copy_data(cmp, &job->request_payload,
+ job->request_payload.payload_len, 1);
- reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
- for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) {
- busaddr = sg_dma_address(sgel);
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
- bpl->tus.f.bdeSize = sg_dma_len(sgel);
- bpl->tus.w = cpu_to_le32(bpl->tus.w);
- bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
- bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
- bpl++;
+ bpl += request_nseg;
+ reply_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64) - request_nseg;
+ rmp = lpfc_alloc_bsg_buffers(phba, job->reply_payload.payload_len, 0,
+ bpl, &reply_nseg);
+ if (!rmp) {
+ rc = -ENOMEM;
+ goto free_cmp;
}
- cmd->un.genreq64.bdl.ulpIoTag32 = 0;
- cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
- cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
- cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- cmd->un.genreq64.bdl.bdeSize =
- (request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
- cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
- cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
- cmd->un.genreq64.w5.hcsw.Dfctl = 0;
- cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
- cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
- cmd->ulpBdeCount = 1;
- cmd->ulpLe = 1;
- cmd->ulpClass = CLASS3;
- cmd->ulpContext = ndlp->nlp_rpi;
+ num_entry = request_nseg + reply_nseg;
+
if (phba->sli_rev == LPFC_SLI_REV4)
- cmd->ulpContext = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
- cmd->ulpOwner = OWN_CHIP;
+ ulp_context = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
+ else
+ ulp_context = ndlp->nlp_rpi;
+
+ lpfc_sli_prep_gen_req(phba, cmdiocbq, bmp, ulp_context, num_entry,
+ phba->fc_ratov * 2);
+
+ cmdiocbq->num_bdes = num_entry;
cmdiocbq->vport = phba->pport;
- cmdiocbq->context3 = bmp;
- cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
- timeout = phba->fc_ratov * 2;
- cmd->ulpTimeout = timeout;
-
- cmdiocbq->iocb_cmpl = lpfc_bsg_send_mgmt_cmd_cmp;
- cmdiocbq->context1 = ndlp;
- cmdiocbq->context2 = dd_data;
+ cmdiocbq->cmd_dmabuf = cmp;
+ cmdiocbq->bpl_dmabuf = bmp;
+ cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
+
+ cmdiocbq->cmd_cmpl = lpfc_bsg_send_mgmt_cmd_cmp;
+ cmdiocbq->context_un.dd_data = dd_data;
+
dd_data->type = TYPE_IOCB;
+ dd_data->set_job = job;
dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
- dd_data->context_un.iocb.set_job = job;
- dd_data->context_un.iocb.bmp = bmp;
+ dd_data->context_un.iocb.rmp = rmp;
+ job->dd_data = dd_data;
if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
if (lpfc_readl(phba->HCregaddr, &creg_val)) {
rc = -EIO ;
- goto free_cmdiocbq;
+ goto free_rmp;
}
creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
writel(creg_val, phba->HCregaddr);
readl(phba->HCregaddr); /* flush */
}
+ cmdiocbq->ndlp = lpfc_nlp_get(ndlp);
+ if (!cmdiocbq->ndlp) {
+ rc = -ENODEV;
+ goto free_rmp;
+ }
+
iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
- if (iocb_stat == IOCB_SUCCESS)
+ if (iocb_stat == IOCB_SUCCESS) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ /* make sure the I/O had not been completed yet */
+ if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) {
+ /* open up abort window to timeout handler */
+ cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
return 0; /* done for now */
- else if (iocb_stat == IOCB_BUSY)
+ } else if (iocb_stat == IOCB_BUSY) {
rc = -EAGAIN;
- else
+ } else {
rc = -EIO;
-
+ }
/* iocb failed so cleanup */
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
-
- lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ lpfc_nlp_put(ndlp);
-free_cmdiocbq:
- lpfc_sli_release_iocbq(phba, cmdiocbq);
+free_rmp:
+ lpfc_free_bsg_buffers(phba, rmp);
+free_cmp:
+ lpfc_free_bsg_buffers(phba, cmp);
free_bmp:
+ if (bmp->virt)
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp);
-free_ndlp:
- lpfc_nlp_put(ndlp);
-no_ndlp:
+free_cmdiocbq:
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+free_dd:
kfree(dd_data);
no_dd_data:
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
job->dd_data = NULL;
return rc;
}
@@ -414,70 +565,86 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
struct lpfc_iocbq *rspiocbq)
{
struct bsg_job_data *dd_data;
- struct fc_bsg_job *job;
- IOCB_t *rsp;
+ struct bsg_job *job;
+ struct fc_bsg_reply *bsg_reply;
struct lpfc_nodelist *ndlp;
- struct lpfc_dmabuf *pbuflist = NULL;
+ struct lpfc_dmabuf *pcmd = NULL, *prsp = NULL;
struct fc_bsg_ctels_reply *els_reply;
uint8_t *rjt_data;
unsigned long flags;
+ unsigned int rsp_size;
int rc = 0;
+ u32 ulp_status, ulp_word4, total_data_placed;
+ dd_data = cmdiocbq->context_un.dd_data;
+ ndlp = dd_data->context_un.iocb.ndlp;
+ cmdiocbq->ndlp = ndlp;
+
+ /* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
- dd_data = cmdiocbq->context1;
- /* normal completion and timeout crossed paths, already done */
- if (!dd_data) {
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- return;
+ job = dd_data->set_job;
+ if (job) {
+ bsg_reply = job->reply;
+ /* Prevent timeout handling from trying to abort job */
+ job->dd_data = NULL;
}
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
- if (cmdiocbq->context2 && rspiocbq)
- memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
- &rspiocbq->iocb, sizeof(IOCB_t));
+ /* Close the timeout handler abort window */
+ spin_lock_irqsave(&phba->hbalock, flags);
+ cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
- job = dd_data->context_un.iocb.set_job;
- cmdiocbq = dd_data->context_un.iocb.cmdiocbq;
- rspiocbq = dd_data->context_un.iocb.rspiocbq;
- rsp = &rspiocbq->iocb;
- ndlp = dd_data->context_un.iocb.ndlp;
+ ulp_status = get_job_ulpstatus(phba, rspiocbq);
+ ulp_word4 = get_job_word4(phba, rspiocbq);
+ total_data_placed = get_job_data_placed(phba, rspiocbq);
+ pcmd = cmdiocbq->cmd_dmabuf;
+ prsp = (struct lpfc_dmabuf *)pcmd->list.next;
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ /* Copy the completed job data or determine the job status if job is
+ * still active
+ */
- if (job->reply->result == -EAGAIN)
- rc = -EAGAIN;
- else if (rsp->ulpStatus == IOSTAT_SUCCESS)
- job->reply->reply_payload_rcv_len =
- rsp->un.elsreq64.bdl.bdeSize;
- else if (rsp->ulpStatus == IOSTAT_LS_RJT) {
- job->reply->reply_payload_rcv_len =
- sizeof(struct fc_bsg_ctels_reply);
- /* LS_RJT data returned in word 4 */
- rjt_data = (uint8_t *)&rsp->un.ulpWord[4];
- els_reply = &job->reply->reply_data.ctels_reply;
- els_reply->status = FC_CTELS_STATUS_REJECT;
- els_reply->rjt_data.action = rjt_data[3];
- els_reply->rjt_data.reason_code = rjt_data[2];
- els_reply->rjt_data.reason_explanation = rjt_data[1];
- els_reply->rjt_data.vendor_unique = rjt_data[0];
- } else
- rc = -EIO;
+ if (job) {
+ if (ulp_status == IOSTAT_SUCCESS) {
+ rsp_size = total_data_placed;
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ prsp->virt,
+ rsp_size);
+ } else if (ulp_status == IOSTAT_LS_RJT) {
+ bsg_reply->reply_payload_rcv_len =
+ sizeof(struct fc_bsg_ctels_reply);
+ /* LS_RJT data returned in word 4 */
+ rjt_data = (uint8_t *)&ulp_word4;
+ els_reply = &bsg_reply->reply_data.ctels_reply;
+ els_reply->status = FC_CTELS_STATUS_REJECT;
+ els_reply->rjt_data.action = rjt_data[3];
+ els_reply->rjt_data.reason_code = rjt_data[2];
+ els_reply->rjt_data.reason_explanation = rjt_data[1];
+ els_reply->rjt_data.vendor_unique = rjt_data[0];
+ } else if (ulp_status == IOSTAT_LOCAL_REJECT &&
+ (ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_SEQUENCE_TIMEOUT) {
+ rc = -ETIMEDOUT;
+ } else {
+ rc = -EIO;
+ }
+ }
+
+ lpfc_els_free_iocb(phba, cmdiocbq);
- pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3;
- lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys);
- lpfc_sli_release_iocbq(phba, rspiocbq);
- lpfc_sli_release_iocbq(phba, cmdiocbq);
lpfc_nlp_put(ndlp);
kfree(dd_data);
- /* make error code available to userspace */
- job->reply->result = rc;
- job->dd_data = NULL;
- /* complete the job back to userspace */
- job->job_done(job);
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ /* Complete the job if the job is still active */
+
+ if (job) {
+ bsg_reply->result = rc;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ }
return;
}
@@ -486,34 +653,34 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
* @job: fc_bsg_job to handle
**/
static int
-lpfc_bsg_rport_els(struct fc_bsg_job *job)
+lpfc_bsg_rport_els(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata = job->rport->dd_data;
+ struct lpfc_rport_data *rdata = fc_bsg_to_rport(job)->dd_data;
struct lpfc_nodelist *ndlp = rdata->pnode;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
uint32_t elscmd;
uint32_t cmdsize;
- uint32_t rspsize;
- struct lpfc_iocbq *rspiocbq;
struct lpfc_iocbq *cmdiocbq;
- IOCB_t *rsp;
uint16_t rpi = 0;
- struct lpfc_dmabuf *pcmd;
- struct lpfc_dmabuf *prsp;
- struct lpfc_dmabuf *pbuflist = NULL;
- struct ulp_bde64 *bpl;
- int request_nseg;
- int reply_nseg;
- struct scatterlist *sgel = NULL;
- int numbde;
- dma_addr_t busaddr;
struct bsg_job_data *dd_data;
+ unsigned long flags;
uint32_t creg_val;
int rc = 0;
/* in case no data is transferred */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
+
+ /* verify the els command is not greater than the
+ * maximum ELS transfer size.
+ */
+
+ if (job->request_payload.payload_len > FCELSSIZE) {
+ rc = -EINVAL;
+ goto no_dd_data;
+ }
/* allocate our bsg tracking structure */
dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
@@ -524,84 +691,49 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
goto no_dd_data;
}
+ elscmd = bsg_request->rqst_data.r_els.els_code;
+ cmdsize = job->request_payload.payload_len;
+
if (!lpfc_nlp_get(ndlp)) {
rc = -ENODEV;
goto free_dd_data;
}
- elscmd = job->request->rqst_data.r_els.els_code;
- cmdsize = job->request_payload.payload_len;
- rspsize = job->reply_payload.payload_len;
- rspiocbq = lpfc_sli_get_iocbq(phba);
- if (!rspiocbq) {
- lpfc_nlp_put(ndlp);
- rc = -ENOMEM;
- goto free_dd_data;
- }
-
- rsp = &rspiocbq->iocb;
- rpi = ndlp->nlp_rpi;
-
+ /* We will use the allocated dma buffers by prep els iocb for command
+ * and response to ensure if the job times out and the request is freed,
+ * we won't be dma into memory that is no longer allocated to for the
+ * request.
+ */
cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
ndlp->nlp_DID, elscmd);
if (!cmdiocbq) {
rc = -EIO;
- goto free_rspiocbq;
+ goto release_ndlp;
}
- /* prep els iocb set context1 to the ndlp, context2 to the command
- * dmabuf, context3 holds the data dmabuf
- */
- pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2;
- prsp = (struct lpfc_dmabuf *) pcmd->list.next;
- lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
- kfree(pcmd);
- lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
- kfree(prsp);
- cmdiocbq->context2 = NULL;
-
- pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3;
- bpl = (struct ulp_bde64 *) pbuflist->virt;
-
- request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
- busaddr = sg_dma_address(sgel);
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl->tus.f.bdeSize = sg_dma_len(sgel);
- bpl->tus.w = cpu_to_le32(bpl->tus.w);
- bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
- bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
- bpl++;
- }
+ /* Transfer the request payload to allocated command dma buffer */
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ cmdiocbq->cmd_dmabuf->virt,
+ cmdsize);
- reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
- for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) {
- busaddr = sg_dma_address(sgel);
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
- bpl->tus.f.bdeSize = sg_dma_len(sgel);
- bpl->tus.w = cpu_to_le32(bpl->tus.w);
- bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
- bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
- bpl++;
- }
- cmdiocbq->iocb.un.elsreq64.bdl.bdeSize =
- (request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
- cmdiocbq->iocb.ulpContext = rpi;
- cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
- cmdiocbq->context1 = NULL;
- cmdiocbq->context2 = NULL;
+ rpi = ndlp->nlp_rpi;
- cmdiocbq->iocb_cmpl = lpfc_bsg_rport_els_cmp;
- cmdiocbq->context1 = dd_data;
- cmdiocbq->context2 = rspiocbq;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ bf_set(wqe_ctxt_tag, &cmdiocbq->wqe.generic.wqe_com,
+ phba->sli4_hba.rpi_ids[rpi]);
+ else
+ cmdiocbq->iocb.ulpContext = rpi;
+ cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
+ cmdiocbq->context_un.dd_data = dd_data;
+ cmdiocbq->ndlp = ndlp;
+ cmdiocbq->cmd_cmpl = lpfc_bsg_rport_els_cmp;
dd_data->type = TYPE_IOCB;
+ dd_data->set_job = job;
dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
- dd_data->context_un.iocb.rspiocbq = rspiocbq;
- dd_data->context_un.iocb.set_job = job;
- dd_data->context_un.iocb.bmp = NULL;
dd_data->context_un.iocb.ndlp = ndlp;
+ dd_data->context_un.iocb.rmp = NULL;
+ job->dd_data = dd_data;
if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
if (lpfc_readl(phba->HCregaddr, &creg_val)) {
@@ -612,34 +744,37 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
writel(creg_val, phba->HCregaddr);
readl(phba->HCregaddr); /* flush */
}
+
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
- lpfc_nlp_put(ndlp);
- if (rc == IOCB_SUCCESS)
+ if (rc == IOCB_SUCCESS) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ /* make sure the I/O had not been completed/released */
+ if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) {
+ /* open up abort window to timeout handler */
+ cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
return 0; /* done for now */
- else if (rc == IOCB_BUSY)
+ } else if (rc == IOCB_BUSY) {
rc = -EAGAIN;
- else
+ } else {
rc = -EIO;
+ }
-linkdown_err:
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
-
- lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys);
+ /* I/O issue failed. Cleanup resources. */
- lpfc_sli_release_iocbq(phba, cmdiocbq);
+linkdown_err:
+ lpfc_els_free_iocb(phba, cmdiocbq);
-free_rspiocbq:
- lpfc_sli_release_iocbq(phba, rspiocbq);
+release_ndlp:
+ lpfc_nlp_put(ndlp);
free_dd_data:
kfree(dd_data);
no_dd_data:
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
job->dd_data = NULL;
return rc;
}
@@ -675,6 +810,7 @@ lpfc_bsg_event_free(struct kref *kref)
kfree(ed);
}
+ kfree(evt->dd_data);
kfree(evt);
}
@@ -718,6 +854,7 @@ lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id)
evt->req_id = ev_req_id;
evt->reg_id = ev_reg_id;
evt->wait_time_stamp = jiffies;
+ evt->dd_data = NULL;
init_waitqueue_head(&evt->wq);
kref_init(&evt->kref);
return evt;
@@ -755,11 +892,8 @@ diag_cmd_data_free(struct lpfc_hba *phba, struct lpfc_dmabufext *mlist)
return 0;
}
-/**
+/*
* lpfc_bsg_ct_unsol_event - process an unsolicited CT command
- * @phba:
- * @pring:
- * @piocbq:
*
* This function is called when an unsolicited CT command is received. It
* forwards the event to any processes registered to receive CT events.
@@ -769,51 +903,33 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *piocbq)
{
uint32_t evt_req_id = 0;
- uint32_t cmd;
- uint32_t len;
+ u16 cmd;
struct lpfc_dmabuf *dmabuf = NULL;
struct lpfc_bsg_event *evt;
struct event_data *evt_dat = NULL;
struct lpfc_iocbq *iocbq;
+ IOCB_t *iocb = NULL;
size_t offset = 0;
struct list_head head;
struct ulp_bde64 *bde;
dma_addr_t dma_addr;
int i;
- struct lpfc_dmabuf *bdeBuf1 = piocbq->context2;
- struct lpfc_dmabuf *bdeBuf2 = piocbq->context3;
- struct lpfc_hbq_entry *hbqe;
+ struct lpfc_dmabuf *bdeBuf1 = piocbq->cmd_dmabuf;
+ struct lpfc_dmabuf *bdeBuf2 = piocbq->bpl_dmabuf;
struct lpfc_sli_ct_request *ct_req;
- struct fc_bsg_job *job = NULL;
+ struct bsg_job *job = NULL;
+ struct fc_bsg_reply *bsg_reply;
+ struct bsg_job_data *dd_data = NULL;
unsigned long flags;
int size = 0;
+ u32 bde_count = 0;
INIT_LIST_HEAD(&head);
list_add_tail(&head, &piocbq->list);
- if (piocbq->iocb.ulpBdeCount == 0 ||
- piocbq->iocb.un.cont64[0].tus.f.bdeSize == 0)
- goto error_ct_unsol_exit;
-
- if (phba->link_state == LPFC_HBA_ERROR ||
- (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)))
- goto error_ct_unsol_exit;
-
- if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
- dmabuf = bdeBuf1;
- else {
- dma_addr = getPaddr(piocbq->iocb.un.cont64[0].addrHigh,
- piocbq->iocb.un.cont64[0].addrLow);
- dmabuf = lpfc_sli_ringpostbuf_get(phba, pring, dma_addr);
- }
- if (dmabuf == NULL)
- goto error_ct_unsol_exit;
- ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt;
+ ct_req = (struct lpfc_sli_ct_request *)bdeBuf1->virt;
evt_req_id = ct_req->FsType;
- cmd = ct_req->CommandResponse.bits.CmdRsp;
- len = ct_req->CommandResponse.bits.Size;
- if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
- lpfc_sli_ringpostbuf_put(phba, pring, dmabuf);
+ cmd = be16_to_cpu(ct_req->CommandResponse.bits.CmdRsp);
spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
@@ -836,12 +952,17 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
/* take accumulated byte count from the last iocbq */
iocbq = list_entry(head.prev, typeof(*iocbq), list);
- evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ evt_dat->len = iocbq->wcqe_cmpl.total_data_placed;
+ else
+ evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len;
} else {
list_for_each_entry(iocbq, &head, list) {
- for (i = 0; i < iocbq->iocb.ulpBdeCount; i++)
+ iocb = &iocbq->iocb;
+ for (i = 0; i < iocb->ulpBdeCount;
+ i++)
evt_dat->len +=
- iocbq->iocb.un.cont64[i].tus.f.bdeSize;
+ iocb->un.cont64[i].tus.f.bdeSize;
}
}
@@ -861,22 +982,21 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
list_for_each_entry(iocbq, &head, list) {
size = 0;
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
- bdeBuf1 = iocbq->context2;
- bdeBuf2 = iocbq->context3;
+ bdeBuf1 = iocbq->cmd_dmabuf;
+ bdeBuf2 = iocbq->bpl_dmabuf;
}
- for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ bde_count = iocbq->wcqe_cmpl.word3;
+ else
+ bde_count = iocbq->iocb.ulpBdeCount;
+ for (i = 0; i < bde_count; i++) {
if (phba->sli3_options &
LPFC_SLI3_HBQ_ENABLED) {
if (i == 0) {
- hbqe = (struct lpfc_hbq_entry *)
- &iocbq->iocb.un.ulpWord[0];
- size = hbqe->bde.tus.f.bdeSize;
+ size = iocbq->wqe.gen_req.bde.tus.f.bdeSize;
dmabuf = bdeBuf1;
} else if (i == 1) {
- hbqe = (struct lpfc_hbq_entry *)
- &iocbq->iocb.unsli3.
- sli3Words[4];
- size = hbqe->bde.tus.f.bdeSize;
+ size = iocbq->unsol_rcv_len;
dmabuf = bdeBuf2;
}
if ((offset + size) > evt_dat->len)
@@ -893,7 +1013,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (!dmabuf) {
lpfc_printf_log(phba, KERN_ERR,
LOG_LIBDFC, "2616 No dmabuf "
- "found for iocbq 0x%p\n",
+ "found for iocbq x%px\n",
iocbq);
kfree(evt_dat->data);
kfree(evt_dat);
@@ -915,9 +1035,11 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
} else {
switch (cmd) {
case ELX_LOOPBACK_DATA:
- diag_cmd_data_free(phba,
- (struct lpfc_dmabufext *)
- dmabuf);
+ if (phba->sli_rev <
+ LPFC_SLI_REV4)
+ diag_cmd_data_free(phba,
+ (struct lpfc_dmabufext
+ *)dmabuf);
break;
case ELX_LOOPBACK_XRI_SETUP:
if ((phba->sli_rev ==
@@ -928,17 +1050,17 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
lpfc_in_buf_free(phba,
dmabuf);
} else {
- lpfc_post_buffer(phba,
- pring,
- 1);
+ lpfc_sli3_post_buffer(phba,
+ pring,
+ 1);
}
break;
default:
if (!(phba->sli3_options &
LPFC_SLI3_HBQ_ENABLED))
- lpfc_post_buffer(phba,
- pring,
- 1);
+ lpfc_sli3_post_buffer(phba,
+ pring,
+ 1);
break;
}
}
@@ -948,9 +1070,9 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
spin_lock_irqsave(&phba->ct_ev_lock, flags);
if (phba->sli_rev == LPFC_SLI_REV4) {
evt_dat->immed_dat = phba->ctx_idx;
- phba->ctx_idx = (phba->ctx_idx + 1) % 64;
+ phba->ctx_idx = (phba->ctx_idx + 1) % LPFC_CT_CTX_MAX;
/* Provide warning for over-run of the ct_ctx array */
- if (phba->ct_ctx[evt_dat->immed_dat].flags &
+ if (phba->ct_ctx[evt_dat->immed_dat].valid ==
UNSOL_VALID)
lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
"2717 CT context array entry "
@@ -960,13 +1082,16 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
evt_dat->immed_dat].oxid,
phba->ct_ctx[
evt_dat->immed_dat].SID);
+ phba->ct_ctx[evt_dat->immed_dat].rxid =
+ get_job_ulpcontext(phba, piocbq);
phba->ct_ctx[evt_dat->immed_dat].oxid =
- piocbq->iocb.ulpContext;
+ get_job_rcvoxid(phba, piocbq);
phba->ct_ctx[evt_dat->immed_dat].SID =
- piocbq->iocb.un.rcvels.remoteID;
- phba->ct_ctx[evt_dat->immed_dat].flags = UNSOL_VALID;
+ bf_get(wqe_els_did,
+ &piocbq->wqe.xmit_els_rsp.wqe_dest);
+ phba->ct_ctx[evt_dat->immed_dat].valid = UNSOL_VALID;
} else
- evt_dat->immed_dat = piocbq->iocb.ulpContext;
+ evt_dat->immed_dat = get_job_ulpcontext(phba, piocbq);
evt_dat->type = FC_REG_CT_EVENT;
list_add(&evt_dat->node, &evt->events_to_see);
@@ -977,18 +1102,21 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
list_move(evt->events_to_see.prev, &evt->events_to_get);
- lpfc_bsg_event_unref(evt);
- job = evt->set_job;
- evt->set_job = NULL;
+ dd_data = (struct bsg_job_data *)evt->dd_data;
+ job = dd_data->set_job;
+ dd_data->set_job = NULL;
+ lpfc_bsg_event_unref(evt);
if (job) {
- job->reply->reply_payload_rcv_len = size;
+ bsg_reply = job->reply;
+ bsg_reply->reply_payload_rcv_len = size;
/* make error code available to userspace */
- job->reply->result = 0;
+ bsg_reply->result = 0;
job->dd_data = NULL;
/* complete the job back to userspace */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- job->job_done(job);
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
spin_lock_irqsave(&phba->ct_ev_lock, flags);
}
}
@@ -997,20 +1125,63 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
error_ct_unsol_exit:
if (!list_empty(&head))
list_del(&head);
- if (evt_req_id == SLI_CT_ELX_LOOPBACK)
+ if ((phba->sli_rev < LPFC_SLI_REV4) &&
+ (evt_req_id == SLI_CT_ELX_LOOPBACK))
return 0;
return 1;
}
/**
+ * lpfc_bsg_ct_unsol_abort - handler ct abort to management plane
+ * @phba: Pointer to HBA context object.
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function handles abort to the CT command toward management plane
+ * for SLI4 port.
+ *
+ * If the pending context of a CT command to management plane present, clears
+ * such context and returns 1 for handled; otherwise, it returns 0 indicating
+ * no context exists.
+ **/
+int
+lpfc_bsg_ct_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
+{
+ struct fc_frame_header fc_hdr;
+ struct fc_frame_header *fc_hdr_ptr = &fc_hdr;
+ int ctx_idx, handled = 0;
+ uint16_t oxid, rxid;
+ uint32_t sid;
+
+ memcpy(fc_hdr_ptr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
+ sid = sli4_sid_from_fc_hdr(fc_hdr_ptr);
+ oxid = be16_to_cpu(fc_hdr_ptr->fh_ox_id);
+ rxid = be16_to_cpu(fc_hdr_ptr->fh_rx_id);
+
+ for (ctx_idx = 0; ctx_idx < LPFC_CT_CTX_MAX; ctx_idx++) {
+ if (phba->ct_ctx[ctx_idx].valid != UNSOL_VALID)
+ continue;
+ if (phba->ct_ctx[ctx_idx].rxid != rxid)
+ continue;
+ if (phba->ct_ctx[ctx_idx].oxid != oxid)
+ continue;
+ if (phba->ct_ctx[ctx_idx].SID != sid)
+ continue;
+ phba->ct_ctx[ctx_idx].valid = UNSOL_INVALID;
+ handled = 1;
+ }
+ return handled;
+}
+
+/**
* lpfc_bsg_hba_set_event - process a SET_EVENT bsg vendor command
* @job: SET_EVENT fc_bsg_job
**/
static int
-lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
+lpfc_bsg_hba_set_event(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
struct lpfc_hba *phba = vport->phba;
+ struct fc_bsg_request *bsg_request = job->request;
struct set_ct_event *event_req;
struct lpfc_bsg_event *evt;
int rc = 0;
@@ -1027,16 +1198,8 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
goto job_error;
}
- dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
- if (dd_data == NULL) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2734 Failed allocation of dd_data\n");
- rc = -ENOMEM;
- goto job_error;
- }
-
event_req = (struct set_ct_event *)
- job->request->rqst_data.h_vendor.vendor_cmd;
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
ev_mask = ((uint32_t)(unsigned long)event_req->type_mask &
FC_REG_EVENT_MASK);
spin_lock_irqsave(&phba->ct_ev_lock, flags);
@@ -1044,6 +1207,7 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
if (evt->reg_id == event_req->ev_reg_id) {
lpfc_bsg_event_ref(evt);
evt->wait_time_stamp = jiffies;
+ dd_data = (struct bsg_job_data *)evt->dd_data;
break;
}
}
@@ -1051,6 +1215,13 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
if (&evt->node == &phba->ct_ev_waiters) {
/* no event waiting struct yet - first call */
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (dd_data == NULL) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2734 Failed allocation of dd_data\n");
+ rc = -ENOMEM;
+ goto job_error;
+ }
evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id,
event_req->ev_req_id);
if (!evt) {
@@ -1060,7 +1231,10 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
rc = -ENOMEM;
goto job_error;
}
-
+ dd_data->type = TYPE_EVT;
+ dd_data->set_job = NULL;
+ dd_data->context_un.evt = evt;
+ evt->dd_data = (void *)dd_data;
spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_add(&evt->node, &phba->ct_ev_waiters);
lpfc_bsg_event_ref(evt);
@@ -1070,17 +1244,13 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
spin_lock_irqsave(&phba->ct_ev_lock, flags);
evt->waiting = 1;
- dd_data->type = TYPE_EVT;
- dd_data->context_un.evt = evt;
- evt->set_job = job; /* for unsolicited command */
+ dd_data->set_job = job; /* for unsolicited command */
job->dd_data = dd_data; /* for fc transport timeout callback*/
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
return 0; /* call job done later */
job_error:
- if (dd_data != NULL)
- kfree(dd_data);
-
+ kfree(dd_data);
job->dd_data = NULL;
return rc;
}
@@ -1090,13 +1260,15 @@ job_error:
* @job: GET_EVENT fc_bsg_job
**/
static int
-lpfc_bsg_hba_get_event(struct fc_bsg_job *job)
+lpfc_bsg_hba_get_event(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
struct lpfc_hba *phba = vport->phba;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct get_ct_event *event_req;
struct get_ct_event_reply *event_reply;
- struct lpfc_bsg_event *evt;
+ struct lpfc_bsg_event *evt, *evt_next;
struct event_data *evt_dat = NULL;
unsigned long flags;
uint32_t rc = 0;
@@ -1111,12 +1283,12 @@ lpfc_bsg_hba_get_event(struct fc_bsg_job *job)
}
event_req = (struct get_ct_event *)
- job->request->rqst_data.h_vendor.vendor_cmd;
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
event_reply = (struct get_ct_event_reply *)
- job->reply->reply_data.vendor_reply.vendor_rsp;
+ bsg_reply->reply_data.vendor_reply.vendor_rsp;
spin_lock_irqsave(&phba->ct_ev_lock, flags);
- list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
+ list_for_each_entry_safe(evt, evt_next, &phba->ct_ev_waiters, node) {
if (evt->reg_id == event_req->ev_reg_id) {
if (list_empty(&evt->events_to_get))
break;
@@ -1134,7 +1306,7 @@ lpfc_bsg_hba_get_event(struct fc_bsg_job *job)
* an error indicating that there isn't anymore
*/
if (evt_dat == NULL) {
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
rc = -ENOENT;
goto job_error;
}
@@ -1150,12 +1322,12 @@ lpfc_bsg_hba_get_event(struct fc_bsg_job *job)
event_reply->type = evt_dat->type;
event_reply->immed_data = evt_dat->immed_dat;
if (evt_dat->len > 0)
- job->reply->reply_payload_rcv_len =
+ bsg_reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->request_payload.sg_list,
job->request_payload.sg_cnt,
evt_dat->data, evt_dat->len);
else
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
if (evt_dat) {
kfree(evt_dat->data);
@@ -1166,13 +1338,14 @@ lpfc_bsg_hba_get_event(struct fc_bsg_job *job)
lpfc_bsg_event_unref(evt);
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job->dd_data = NULL;
- job->reply->result = 0;
- job->job_done(job);
+ bsg_reply->result = 0;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return 0;
job_error:
job->dd_data = NULL;
- job->reply->result = rc;
+ bsg_reply->result = rc;
return rc;
}
@@ -1199,59 +1372,76 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
struct lpfc_iocbq *rspiocbq)
{
struct bsg_job_data *dd_data;
- struct fc_bsg_job *job;
- IOCB_t *rsp;
- struct lpfc_dmabuf *bmp;
+ struct bsg_job *job;
+ struct fc_bsg_reply *bsg_reply;
+ struct lpfc_dmabuf *bmp, *cmp;
struct lpfc_nodelist *ndlp;
unsigned long flags;
int rc = 0;
+ u32 ulp_status, ulp_word4;
+ dd_data = cmdiocbq->context_un.dd_data;
+
+ /* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
- dd_data = cmdiocbq->context2;
- /* normal completion and timeout crossed paths, already done */
- if (!dd_data) {
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- return;
+ job = dd_data->set_job;
+ if (job) {
+ /* Prevent timeout handling from trying to abort job */
+ job->dd_data = NULL;
}
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ /* Close the timeout handler abort window */
+ spin_lock_irqsave(&phba->hbalock, flags);
+ cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
- job = dd_data->context_un.iocb.set_job;
- bmp = dd_data->context_un.iocb.bmp;
- rsp = &rspiocbq->iocb;
ndlp = dd_data->context_un.iocb.ndlp;
+ cmp = cmdiocbq->cmd_dmabuf;
+ bmp = cmdiocbq->bpl_dmabuf;
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ ulp_status = get_job_ulpstatus(phba, rspiocbq);
+ ulp_word4 = get_job_word4(phba, rspiocbq);
- if (rsp->ulpStatus) {
- if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- switch (rsp->un.ulpWord[4] & 0xff) {
- case IOERR_SEQUENCE_TIMEOUT:
- rc = -ETIMEDOUT;
- break;
- case IOERR_INVALID_RPI:
- rc = -EFAULT;
- break;
- default:
+ /* Copy the completed job data or set the error status */
+
+ if (job) {
+ bsg_reply = job->reply;
+ if (ulp_status) {
+ if (ulp_status == IOSTAT_LOCAL_REJECT) {
+ switch (ulp_word4 & IOERR_PARAM_MASK) {
+ case IOERR_SEQUENCE_TIMEOUT:
+ rc = -ETIMEDOUT;
+ break;
+ case IOERR_INVALID_RPI:
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EACCES;
+ break;
+ }
+ } else {
rc = -EACCES;
- break;
}
- } else
- rc = -EACCES;
- } else
- job->reply->reply_payload_rcv_len =
- rsp->un.genreq64.bdl.bdeSize;
+ } else {
+ bsg_reply->reply_payload_rcv_len = 0;
+ }
+ }
+ lpfc_free_bsg_buffers(phba, cmp);
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ kfree(bmp);
lpfc_sli_release_iocbq(phba, cmdiocbq);
lpfc_nlp_put(ndlp);
- kfree(bmp);
kfree(dd_data);
- /* make error code available to userspace */
- job->reply->result = rc;
- job->dd_data = NULL;
- /* complete the job back to userspace */
- job->job_done(job);
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ /* Complete the job if the job is still active */
+
+ if (job) {
+ bsg_reply->result = rc;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ }
return;
}
@@ -1260,19 +1450,31 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
* @phba: Pointer to HBA context object.
* @job: Pointer to the job object.
* @tag: tag index value into the ports context exchange array.
- * @bmp: Pointer to a dma buffer descriptor.
+ * @cmp: Pointer to a cmp dma buffer descriptor.
+ * @bmp: Pointer to a bmp dma buffer descriptor.
* @num_entry: Number of enties in the bde.
**/
static int
-lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
- struct lpfc_dmabuf *bmp, int num_entry)
+lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct bsg_job *job, uint32_t tag,
+ struct lpfc_dmabuf *cmp, struct lpfc_dmabuf *bmp,
+ int num_entry)
{
- IOCB_t *icmd;
struct lpfc_iocbq *ctiocb = NULL;
int rc = 0;
struct lpfc_nodelist *ndlp = NULL;
struct bsg_job_data *dd_data;
+ unsigned long flags;
uint32_t creg_val;
+ u16 ulp_context, iotag;
+
+ ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID);
+ if (!ndlp) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+ "2721 ndlp null for oxid %x SID %x\n",
+ phba->ct_ctx[tag].rxid,
+ phba->ct_ctx[tag].SID);
+ return IOCB_ERROR;
+ }
/* allocate our bsg tracking structure */
dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
@@ -1290,84 +1492,55 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
goto no_ctiocb;
}
- icmd = &ctiocb->iocb;
- icmd->un.xseq64.bdl.ulpIoTag32 = 0;
- icmd->un.xseq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
- icmd->un.xseq64.bdl.addrLow = putPaddrLow(bmp->phys);
- icmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- icmd->un.xseq64.bdl.bdeSize = (num_entry * sizeof(struct ulp_bde64));
- icmd->un.xseq64.w5.hcsw.Fctl = (LS | LA);
- icmd->un.xseq64.w5.hcsw.Dfctl = 0;
- icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_SOL_CTL;
- icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT;
-
- /* Fill in rest of iocb */
- icmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX;
- icmd->ulpBdeCount = 1;
- icmd->ulpLe = 1;
- icmd->ulpClass = CLASS3;
if (phba->sli_rev == LPFC_SLI_REV4) {
/* Do not issue unsol response if oxid not marked as valid */
- if (!(phba->ct_ctx[tag].flags & UNSOL_VALID)) {
- rc = IOCB_ERROR;
- goto issue_ct_rsp_exit;
- }
- icmd->ulpContext = phba->ct_ctx[tag].oxid;
- ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID);
- if (!ndlp) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
- "2721 ndlp null for oxid %x SID %x\n",
- icmd->ulpContext,
- phba->ct_ctx[tag].SID);
+ if (phba->ct_ctx[tag].valid != UNSOL_VALID) {
rc = IOCB_ERROR;
goto issue_ct_rsp_exit;
}
- /* Check if the ndlp is active */
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
- rc = -IOCB_ERROR;
- goto issue_ct_rsp_exit;
- }
-
- /* get a refernece count so the ndlp doesn't go away while
- * we respond
- */
- if (!lpfc_nlp_get(ndlp)) {
- rc = -IOCB_ERROR;
- goto issue_ct_rsp_exit;
- }
-
- icmd->un.ulpWord[3] = ndlp->nlp_rpi;
- if (phba->sli_rev == LPFC_SLI_REV4)
- icmd->ulpContext =
- phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
+ lpfc_sli_prep_xmit_seq64(phba, ctiocb, bmp,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi],
+ phba->ct_ctx[tag].oxid, num_entry,
+ FC_RCTL_DD_SOL_CTL, 1,
+ CMD_XMIT_SEQUENCE64_WQE);
/* The exchange is done, mark the entry as invalid */
- phba->ct_ctx[tag].flags &= ~UNSOL_VALID;
- } else
- icmd->ulpContext = (ushort) tag;
+ phba->ct_ctx[tag].valid = UNSOL_INVALID;
+ iotag = get_wqe_reqtag(ctiocb);
+ } else {
+ lpfc_sli_prep_xmit_seq64(phba, ctiocb, bmp, 0, tag, num_entry,
+ FC_RCTL_DD_SOL_CTL, 1,
+ CMD_XMIT_SEQUENCE64_CX);
+ ctiocb->num_bdes = num_entry;
+ iotag = ctiocb->iocb.ulpIoTag;
+ }
- icmd->ulpTimeout = phba->fc_ratov * 2;
+ ulp_context = get_job_ulpcontext(phba, ctiocb);
/* Xmit CT response on exchange <xid> */
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
- "2722 Xmit CT response on exchange x%x Data: x%x x%x\n",
- icmd->ulpContext, icmd->ulpIoTag, phba->link_state);
+ "2722 Xmit CT response on exchange x%x Data: x%x x%x x%x\n",
+ ulp_context, iotag, tag, phba->link_state);
- ctiocb->iocb_cmpl = NULL;
- ctiocb->iocb_flag |= LPFC_IO_LIBDFC;
+ ctiocb->cmd_flag |= LPFC_IO_LIBDFC;
ctiocb->vport = phba->pport;
- ctiocb->context3 = bmp;
+ ctiocb->context_un.dd_data = dd_data;
+ ctiocb->cmd_dmabuf = cmp;
+ ctiocb->bpl_dmabuf = bmp;
+ ctiocb->ndlp = ndlp;
+ ctiocb->cmd_cmpl = lpfc_issue_ct_rsp_cmp;
- ctiocb->iocb_cmpl = lpfc_issue_ct_rsp_cmp;
- ctiocb->context2 = dd_data;
- ctiocb->context1 = ndlp;
dd_data->type = TYPE_IOCB;
+ dd_data->set_job = job;
dd_data->context_un.iocb.cmdiocbq = ctiocb;
- dd_data->context_un.iocb.rspiocbq = NULL;
- dd_data->context_un.iocb.set_job = job;
- dd_data->context_un.iocb.bmp = bmp;
- dd_data->context_un.iocb.ndlp = ndlp;
+ dd_data->context_un.iocb.ndlp = lpfc_nlp_get(ndlp);
+ if (!dd_data->context_un.iocb.ndlp) {
+ rc = -IOCB_ERROR;
+ goto issue_ct_rsp_exit;
+ }
+ dd_data->context_un.iocb.rmp = NULL;
+ job->dd_data = dd_data;
if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
if (lpfc_readl(phba->HCregaddr, &creg_val)) {
@@ -1380,9 +1553,20 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
}
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
-
- if (rc == IOCB_SUCCESS)
+ if (rc == IOCB_SUCCESS) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ /* make sure the I/O had not been completed/released */
+ if (ctiocb->cmd_flag & LPFC_IO_LIBDFC) {
+ /* open up abort window to timeout handler */
+ ctiocb->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
return 0; /* done for now */
+ }
+
+ /* iocb failed so cleanup */
+ job->dd_data = NULL;
+ lpfc_nlp_put(ndlp);
issue_ct_rsp_exit:
lpfc_sli_release_iocbq(phba, ctiocb);
@@ -1397,25 +1581,24 @@ no_dd_data:
* @job: SEND_MGMT_RESP fc_bsg_job
**/
static int
-lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job)
+lpfc_bsg_send_mgmt_rsp(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
struct lpfc_hba *phba = vport->phba;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *)
- job->request->rqst_data.h_vendor.vendor_cmd;
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
struct ulp_bde64 *bpl;
- struct lpfc_dmabuf *bmp = NULL;
- struct scatterlist *sgel = NULL;
- int request_nseg;
- int numbde;
- dma_addr_t busaddr;
+ struct lpfc_dmabuf *bmp = NULL, *cmp = NULL;
+ int bpl_entries;
uint32_t tag = mgmt_resp->tag;
unsigned long reqbfrcnt =
(unsigned long)job->request_payload.payload_len;
int rc = 0;
/* in case no data is transferred */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
if (!reqbfrcnt || (reqbfrcnt > (80 * BUF_SZ_4K))) {
rc = -ERANGE;
@@ -1436,34 +1619,32 @@ lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job)
INIT_LIST_HEAD(&bmp->list);
bpl = (struct ulp_bde64 *) bmp->virt;
- request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
- busaddr = sg_dma_address(sgel);
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl->tus.f.bdeSize = sg_dma_len(sgel);
- bpl->tus.w = cpu_to_le32(bpl->tus.w);
- bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
- bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
- bpl++;
+ bpl_entries = (LPFC_BPL_SIZE/sizeof(struct ulp_bde64));
+ cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
+ 1, bpl, &bpl_entries);
+ if (!cmp) {
+ rc = -ENOMEM;
+ goto send_mgmt_rsp_free_bmp;
}
+ lpfc_bsg_copy_data(cmp, &job->request_payload,
+ job->request_payload.payload_len, 1);
- rc = lpfc_issue_ct_rsp(phba, job, tag, bmp, request_nseg);
+ rc = lpfc_issue_ct_rsp(phba, job, tag, cmp, bmp, bpl_entries);
if (rc == IOCB_SUCCESS)
return 0; /* done for now */
- /* TBD need to handle a timeout */
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
rc = -EACCES;
- lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+
+ lpfc_free_bsg_buffers(phba, cmp);
send_mgmt_rsp_free_bmp:
+ if (bmp->virt)
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp);
send_mgmt_rsp_exit:
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
job->dd_data = NULL;
return rc;
}
@@ -1471,17 +1652,17 @@ send_mgmt_rsp_exit:
/**
* lpfc_bsg_diag_mode_enter - process preparing into device diag loopback mode
* @phba: Pointer to HBA context object.
- * @job: LPFC_BSG_VENDOR_DIAG_MODE
*
* This function is responsible for preparing driver for diag loopback
* on device.
*/
static int
-lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba, struct fc_bsg_job *job)
+lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
struct Scsi_Host *shost;
struct lpfc_sli *psli;
+ struct lpfc_queue *qp = NULL;
struct lpfc_sli_ring *pring;
int i = 0;
@@ -1489,9 +1670,6 @@ lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba, struct fc_bsg_job *job)
if (!psli)
return -ENODEV;
- pring = &psli->ring[LPFC_FCP_RING];
- if (!pring)
- return -ENODEV;
if ((phba->link_state == LPFC_HBA_ERROR) ||
(psli->sli_flag & LPFC_BLOCK_MGMT_IO) ||
@@ -1510,10 +1688,18 @@ lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba, struct fc_bsg_job *job)
scsi_block_requests(shost);
}
- while (pring->txcmplq_cnt) {
- if (i++ > 500) /* wait up to 5 seconds */
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ pring = &psli->sli3_ring[LPFC_FCP_RING];
+ lpfc_emptyq_wait(phba, &pring->txcmplq, &phba->hbalock);
+ return 0;
+ }
+ list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
+ pring = qp->pring;
+ if (!pring || (pring->ringno != LPFC_FCP_RING))
+ continue;
+ if (!lpfc_emptyq_wait(phba, &pring->txcmplq,
+ &pring->ring_lock))
break;
- msleep(10);
}
return 0;
}
@@ -1521,7 +1707,6 @@ lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba, struct fc_bsg_job *job)
/**
* lpfc_bsg_diag_mode_exit - exit process from device diag loopback mode
* @phba: Pointer to HBA context object.
- * @job: LPFC_BSG_VENDOR_DIAG_MODE
*
* This function is responsible for driver exit processing of setting up
* diag loopback mode on device.
@@ -1561,18 +1746,20 @@ lpfc_bsg_diag_mode_exit(struct lpfc_hba *phba)
* All of this is done in-line.
*/
static int
-lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job)
+lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct diag_mode_set *loopback_mode;
uint32_t link_flags;
uint32_t timeout;
- LPFC_MBOXQ_t *pmboxq;
- int mbxstatus;
+ LPFC_MBOXQ_t *pmboxq = NULL;
+ int mbxstatus = MBX_SUCCESS;
int i = 0;
int rc = 0;
/* no data to return just the return code */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
if (job->request_len < sizeof(struct fc_bsg_request) +
sizeof(struct diag_mode_set)) {
@@ -1586,13 +1773,13 @@ lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job)
goto job_error;
}
- rc = lpfc_bsg_diag_mode_enter(phba, job);
+ rc = lpfc_bsg_diag_mode_enter(phba);
if (rc)
goto job_error;
/* bring the link to diagnostic mode */
loopback_mode = (struct diag_mode_set *)
- job->request->rqst_data.h_vendor.vendor_cmd;
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
link_flags = loopback_mode->type;
timeout = loopback_mode->timeout * 100;
@@ -1615,7 +1802,6 @@ lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job)
rc = -ETIMEDOUT;
goto loopback_mode_exit;
}
-
msleep(10);
}
@@ -1635,7 +1821,9 @@ lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job)
if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus))
rc = -ENODEV;
else {
+ spin_lock_irq(&phba->hbalock);
phba->link_flag |= LS_LOOPBACK_MODE;
+ spin_unlock_irq(&phba->hbalock);
/* wait for the link attention interrupt */
msleep(100);
@@ -1659,15 +1847,16 @@ loopback_mode_exit:
/*
* Let SLI layer release mboxq if mbox command completed after timeout.
*/
- if (mbxstatus != MBX_TIMEOUT)
+ if (pmboxq && mbxstatus != MBX_TIMEOUT)
mempool_free(pmboxq, phba->mbox_mem_pool);
job_error:
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
/* complete the job back to userspace if no error */
if (rc == 0)
- job->job_done(job);
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return rc;
}
@@ -1700,11 +1889,18 @@ lpfc_sli4_bsg_set_link_diag_state(struct lpfc_hba *phba, uint32_t diag)
rc = -ENOMEM;
goto link_diag_state_set_out;
}
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3128 Set link to diagnostic state:x%x (x%x/x%x)\n",
+ diag, phba->sli4_hba.lnk_info.lnk_tp,
+ phba->sli4_hba.lnk_info.lnk_no);
+
link_diag_state = &pmboxq->u.mqe.un.link_diag_state;
+ bf_set(lpfc_mbx_set_diag_state_diag_bit_valid, &link_diag_state->u.req,
+ LPFC_DIAG_STATE_DIAG_BIT_VALID_CHANGE);
bf_set(lpfc_mbx_set_diag_state_link_num, &link_diag_state->u.req,
- phba->sli4_hba.link_state.number);
+ phba->sli4_hba.lnk_info.lnk_no);
bf_set(lpfc_mbx_set_diag_state_link_type, &link_diag_state->u.req,
- phba->sli4_hba.link_state.type);
+ phba->sli4_hba.lnk_info.lnk_tp);
if (diag)
bf_set(lpfc_mbx_set_diag_state_diag,
&link_diag_state->u.req, 1);
@@ -1727,6 +1923,87 @@ link_diag_state_set_out:
}
/**
+ * lpfc_sli4_bsg_set_loopback_mode - set sli4 internal loopback diagnostic
+ * @phba: Pointer to HBA context object.
+ * @mode: loopback mode to set
+ * @link_no: link number for loopback mode to set
+ *
+ * This function is responsible for issuing a sli4 mailbox command for setting
+ * up loopback diagnostic for a link.
+ */
+static int
+lpfc_sli4_bsg_set_loopback_mode(struct lpfc_hba *phba, int mode,
+ uint32_t link_no)
+{
+ LPFC_MBOXQ_t *pmboxq;
+ uint32_t req_len, alloc_len;
+ struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback;
+ int mbxstatus = MBX_SUCCESS, rc = 0;
+
+ pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmboxq)
+ return -ENOMEM;
+ req_len = (sizeof(struct lpfc_mbx_set_link_diag_loopback) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK,
+ req_len, LPFC_SLI4_MBX_EMBED);
+ if (alloc_len != req_len) {
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return -ENOMEM;
+ }
+ link_diag_loopback = &pmboxq->u.mqe.un.link_diag_loopback;
+ bf_set(lpfc_mbx_set_diag_state_link_num,
+ &link_diag_loopback->u.req, link_no);
+
+ if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
+ bf_set(lpfc_mbx_set_diag_state_link_type,
+ &link_diag_loopback->u.req, LPFC_LNK_FC_TRUNKED);
+ } else {
+ bf_set(lpfc_mbx_set_diag_state_link_type,
+ &link_diag_loopback->u.req,
+ phba->sli4_hba.lnk_info.lnk_tp);
+ }
+
+ bf_set(lpfc_mbx_set_diag_lpbk_type, &link_diag_loopback->u.req,
+ mode);
+
+ mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
+ if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "3127 Failed setup loopback mode mailbox "
+ "command, rc:x%x, status:x%x\n", mbxstatus,
+ pmboxq->u.mb.mbxStatus);
+ rc = -ENODEV;
+ }
+ if (pmboxq && (mbxstatus != MBX_TIMEOUT))
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return rc;
+}
+
+/**
+ * lpfc_sli4_diag_fcport_reg_setup - setup port registrations for diagnostic
+ * @phba: Pointer to HBA context object.
+ *
+ * This function set up SLI4 FC port registrations for diagnostic run, which
+ * includes all the rpis, vfi, and also vpi.
+ */
+static int
+lpfc_sli4_diag_fcport_reg_setup(struct lpfc_hba *phba)
+{
+ if (test_bit(FC_VFI_REGISTERED, &phba->pport->fc_flag)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "3136 Port still had vfi registered: "
+ "mydid:x%x, fcfi:%d, vfi:%d, vpi:%d\n",
+ phba->pport->fc_myDID, phba->fcf.fcfi,
+ phba->sli4_hba.vfi_ids[phba->pport->vfi],
+ phba->vpi_ids[phba->pport->vpi]);
+ return -EINVAL;
+ }
+ return lpfc_issue_reg_vfi(phba->pport);
+}
+
+/**
* lpfc_sli4_bsg_diag_loopback_mode - process an sli4 bsg vendor command
* @phba: Pointer to HBA context object.
* @job: LPFC_BSG_VENDOR_DIAG_MODE
@@ -1735,16 +2012,16 @@ link_diag_state_set_out:
* loopback mode in order to perform a diagnostic loopback test.
*/
static int
-lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job)
+lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct diag_mode_set *loopback_mode;
- uint32_t link_flags, timeout, req_len, alloc_len;
- struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback;
- LPFC_MBOXQ_t *pmboxq = NULL;
- int mbxstatus, i, rc = 0;
+ uint32_t link_flags, timeout, link_no;
+ int i, rc = 0;
/* no data to return just the return code */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
if (job->request_len < sizeof(struct fc_bsg_request) +
sizeof(struct diag_mode_set)) {
@@ -1755,72 +2032,171 @@ lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job)
(int)(sizeof(struct fc_bsg_request) +
sizeof(struct diag_mode_set)));
rc = -EINVAL;
- goto job_error;
+ goto job_done;
}
- rc = lpfc_bsg_diag_mode_enter(phba, job);
- if (rc)
- goto job_error;
-
- /* bring the link to diagnostic mode */
loopback_mode = (struct diag_mode_set *)
- job->request->rqst_data.h_vendor.vendor_cmd;
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
link_flags = loopback_mode->type;
timeout = loopback_mode->timeout * 100;
- rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1);
+ if (loopback_mode->physical_link == -1)
+ link_no = phba->sli4_hba.lnk_info.lnk_no;
+ else
+ link_no = loopback_mode->physical_link;
+
+ if (link_flags == DISABLE_LOOP_BACK) {
+ rc = lpfc_sli4_bsg_set_loopback_mode(phba,
+ LPFC_DIAG_LOOPBACK_TYPE_DISABLE,
+ link_no);
+ if (!rc) {
+ /* Unset the need disable bit */
+ phba->sli4_hba.conf_trunk &= ~((1 << link_no) << 4);
+ }
+ goto job_done;
+ } else {
+ /* Check if we need to disable the loopback state */
+ if (phba->sli4_hba.conf_trunk & ((1 << link_no) << 4)) {
+ rc = -EPERM;
+ goto job_done;
+ }
+ }
+
+ rc = lpfc_bsg_diag_mode_enter(phba);
+ if (rc)
+ goto job_done;
+
+ /* indicate we are in loobpack diagnostic mode */
+ spin_lock_irq(&phba->hbalock);
+ phba->link_flag |= LS_LOOPBACK_MODE;
+ spin_unlock_irq(&phba->hbalock);
+
+ /* reset port to start frome scratch */
+ rc = lpfc_selective_reset(phba);
if (rc)
+ goto job_done;
+
+ /* bring the link to diagnostic mode */
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3129 Bring link to diagnostic state.\n");
+
+ rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "3130 Failed to bring link to diagnostic "
+ "state, rc:x%x\n", rc);
goto loopback_mode_exit;
+ }
/* wait for link down before proceeding */
i = 0;
while (phba->link_state != LPFC_LINK_DOWN) {
if (i++ > timeout) {
rc = -ETIMEDOUT;
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3131 Timeout waiting for link to "
+ "diagnostic mode, timeout:%d ms\n",
+ timeout * 10);
goto loopback_mode_exit;
}
msleep(10);
}
+
/* set up loopback mode */
- pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!pmboxq) {
- rc = -ENOMEM;
- goto loopback_mode_exit;
- }
- req_len = (sizeof(struct lpfc_mbx_set_link_diag_loopback) -
- sizeof(struct lpfc_sli4_cfg_mhdr));
- alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK,
- req_len, LPFC_SLI4_MBX_EMBED);
- if (alloc_len != req_len) {
- rc = -ENOMEM;
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3132 Set up loopback mode:x%x\n", link_flags);
+
+ switch (link_flags) {
+ case INTERNAL_LOOP_BACK:
+ if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
+ rc = lpfc_sli4_bsg_set_loopback_mode(phba,
+ LPFC_DIAG_LOOPBACK_TYPE_INTERNAL,
+ link_no);
+ } else {
+ /* Trunk is configured, but link is not in this trunk */
+ if (phba->sli4_hba.conf_trunk) {
+ rc = -ELNRNG;
+ goto loopback_mode_exit;
+ }
+
+ rc = lpfc_sli4_bsg_set_loopback_mode(phba,
+ LPFC_DIAG_LOOPBACK_TYPE_INTERNAL,
+ link_no);
+ }
+
+ if (!rc) {
+ /* Set the need disable bit */
+ phba->sli4_hba.conf_trunk |= (1 << link_no) << 4;
+ }
+
+ break;
+ case EXTERNAL_LOOP_BACK:
+ if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
+ rc = lpfc_sli4_bsg_set_loopback_mode(phba,
+ LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL_TRUNKED,
+ link_no);
+ } else {
+ /* Trunk is configured, but link is not in this trunk */
+ if (phba->sli4_hba.conf_trunk) {
+ rc = -ELNRNG;
+ goto loopback_mode_exit;
+ }
+
+ rc = lpfc_sli4_bsg_set_loopback_mode(phba,
+ LPFC_DIAG_LOOPBACK_TYPE_SERDES,
+ link_no);
+ }
+
+ if (!rc) {
+ /* Set the need disable bit */
+ phba->sli4_hba.conf_trunk |= (1 << link_no) << 4;
+ }
+
+ break;
+ default:
+ rc = -EINVAL;
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "3141 Loopback mode:x%x not supported\n",
+ link_flags);
goto loopback_mode_exit;
}
- link_diag_loopback = &pmboxq->u.mqe.un.link_diag_loopback;
- bf_set(lpfc_mbx_set_diag_state_link_num,
- &link_diag_loopback->u.req, phba->sli4_hba.link_state.number);
- bf_set(lpfc_mbx_set_diag_state_link_type,
- &link_diag_loopback->u.req, phba->sli4_hba.link_state.type);
- if (link_flags == INTERNAL_LOOP_BACK)
- bf_set(lpfc_mbx_set_diag_lpbk_type,
- &link_diag_loopback->u.req,
- LPFC_DIAG_LOOPBACK_TYPE_INTERNAL);
- else
- bf_set(lpfc_mbx_set_diag_lpbk_type,
- &link_diag_loopback->u.req,
- LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL);
- mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
- if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus))
- rc = -ENODEV;
- else {
- phba->link_flag |= LS_LOOPBACK_MODE;
+ if (!rc) {
/* wait for the link attention interrupt */
msleep(100);
i = 0;
+ while (phba->link_state < LPFC_LINK_UP) {
+ if (i++ > timeout) {
+ rc = -ETIMEDOUT;
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3137 Timeout waiting for link up "
+ "in loopback mode, timeout:%d ms\n",
+ timeout * 10);
+ break;
+ }
+ msleep(10);
+ }
+ }
+
+ /* port resource registration setup for loopback diagnostic */
+ if (!rc) {
+ /* set up a none zero myDID for loopback test */
+ phba->pport->fc_myDID = 1;
+ rc = lpfc_sli4_diag_fcport_reg_setup(phba);
+ } else
+ goto loopback_mode_exit;
+
+ if (!rc) {
+ /* wait for the port ready */
+ msleep(100);
+ i = 0;
while (phba->link_state != LPFC_HBA_READY) {
if (i++ > timeout) {
rc = -ETIMEDOUT;
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3133 Timeout waiting for port "
+ "loopback mode ready, timeout:%d ms\n",
+ timeout * 10);
break;
}
msleep(10);
@@ -1828,20 +2204,21 @@ lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job)
}
loopback_mode_exit:
+ /* clear loopback diagnostic mode */
+ if (rc) {
+ spin_lock_irq(&phba->hbalock);
+ phba->link_flag &= ~LS_LOOPBACK_MODE;
+ spin_unlock_irq(&phba->hbalock);
+ }
lpfc_bsg_diag_mode_exit(phba);
- /*
- * Let SLI layer release mboxq if mbox command completed after timeout.
- */
- if (pmboxq && (mbxstatus != MBX_TIMEOUT))
- mempool_free(pmboxq, phba->mbox_mem_pool);
-
-job_error:
+job_done:
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
/* complete the job back to userspace if no error */
if (rc == 0)
- job->job_done(job);
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return rc;
}
@@ -1853,17 +2230,17 @@ job_error:
* command from the user to proper driver action routines.
*/
static int
-lpfc_bsg_diag_loopback_mode(struct fc_bsg_job *job)
+lpfc_bsg_diag_loopback_mode(struct bsg_job *job)
{
struct Scsi_Host *shost;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
int rc;
- shost = job->shost;
+ shost = fc_bsg_to_shost(job);
if (!shost)
return -ENODEV;
- vport = (struct lpfc_vport *)job->shost->hostdata;
+ vport = shost_priv(shost);
if (!vport)
return -ENODEV;
phba = vport->phba;
@@ -1872,14 +2249,13 @@ lpfc_bsg_diag_loopback_mode(struct fc_bsg_job *job)
if (phba->sli_rev < LPFC_SLI_REV4)
rc = lpfc_sli3_bsg_diag_loopback_mode(phba, job);
- else if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ else if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
LPFC_SLI_INTF_IF_TYPE_2)
rc = lpfc_sli4_bsg_diag_loopback_mode(phba, job);
else
rc = -ENODEV;
return rc;
-
}
/**
@@ -1890,17 +2266,21 @@ lpfc_bsg_diag_loopback_mode(struct fc_bsg_job *job)
* command from the user to proper driver action routines.
*/
static int
-lpfc_sli4_bsg_diag_mode_end(struct fc_bsg_job *job)
+lpfc_sli4_bsg_diag_mode_end(struct bsg_job *job)
{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct Scsi_Host *shost;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
- int rc;
+ struct diag_mode_set *loopback_mode_end_cmd;
+ uint32_t timeout;
+ int rc, i;
- shost = job->shost;
+ shost = fc_bsg_to_shost(job);
if (!shost)
return -ENODEV;
- vport = (struct lpfc_vport *)job->shost->hostdata;
+ vport = shost_priv(shost);
if (!vport)
return -ENODEV;
phba = vport->phba;
@@ -1909,15 +2289,51 @@ lpfc_sli4_bsg_diag_mode_end(struct fc_bsg_job *job)
if (phba->sli_rev < LPFC_SLI_REV4)
return -ENODEV;
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2)
return -ENODEV;
+ /* clear loopback diagnostic mode */
+ spin_lock_irq(&phba->hbalock);
+ phba->link_flag &= ~LS_LOOPBACK_MODE;
+ spin_unlock_irq(&phba->hbalock);
+ loopback_mode_end_cmd = (struct diag_mode_set *)
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
+ timeout = loopback_mode_end_cmd->timeout * 100;
+
rc = lpfc_sli4_bsg_set_link_diag_state(phba, 0);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "3139 Failed to bring link to diagnostic "
+ "state, rc:x%x\n", rc);
+ goto loopback_mode_end_exit;
+ }
- if (!rc)
- rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
+ /* wait for link down before proceeding */
+ i = 0;
+ while (phba->link_state != LPFC_LINK_DOWN) {
+ if (i++ > timeout) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3140 Timeout waiting for link to "
+ "diagnostic mode_end, timeout:%d ms\n",
+ timeout * 10);
+ /* there is nothing much we can do here */
+ break;
+ }
+ msleep(10);
+ }
+ /* reset port resource registrations */
+ rc = lpfc_selective_reset(phba);
+ phba->pport->fc_myDID = 0;
+
+loopback_mode_end_exit:
+ /* make return code available to userspace */
+ bsg_reply->result = rc;
+ /* complete the job back to userspace if no error */
+ if (rc == 0)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return rc;
}
@@ -1929,46 +2345,41 @@ lpfc_sli4_bsg_diag_mode_end(struct fc_bsg_job *job)
* applicaiton.
*/
static int
-lpfc_sli4_bsg_link_diag_test(struct fc_bsg_job *job)
+lpfc_sli4_bsg_link_diag_test(struct bsg_job *job)
{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct Scsi_Host *shost;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
LPFC_MBOXQ_t *pmboxq;
struct sli4_link_diag *link_diag_test_cmd;
uint32_t req_len, alloc_len;
- uint32_t timeout;
struct lpfc_mbx_run_link_diag_test *run_link_diag_test;
union lpfc_sli4_cfg_shdr *shdr;
uint32_t shdr_status, shdr_add_status;
struct diag_status *diag_status_reply;
- int mbxstatus, rc = 0;
+ int mbxstatus, rc = -ENODEV, rc1 = 0;
- shost = job->shost;
- if (!shost) {
- rc = -ENODEV;
+ shost = fc_bsg_to_shost(job);
+ if (!shost)
goto job_error;
- }
- vport = (struct lpfc_vport *)job->shost->hostdata;
- if (!vport) {
- rc = -ENODEV;
+
+ vport = shost_priv(shost);
+ if (!vport)
goto job_error;
- }
+
phba = vport->phba;
- if (!phba) {
- rc = -ENODEV;
+ if (!phba)
goto job_error;
- }
- if (phba->sli_rev < LPFC_SLI_REV4) {
- rc = -ENODEV;
+
+ if (phba->sli_rev < LPFC_SLI_REV4)
goto job_error;
- }
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
- LPFC_SLI_INTF_IF_TYPE_2) {
- rc = -ENODEV;
+
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
+ LPFC_SLI_INTF_IF_TYPE_2)
goto job_error;
- }
if (job->request_len < sizeof(struct fc_bsg_request) +
sizeof(struct sli4_link_diag)) {
@@ -1982,13 +2393,12 @@ lpfc_sli4_bsg_link_diag_test(struct fc_bsg_job *job)
goto job_error;
}
- rc = lpfc_bsg_diag_mode_enter(phba, job);
+ rc = lpfc_bsg_diag_mode_enter(phba);
if (rc)
goto job_error;
link_diag_test_cmd = (struct sli4_link_diag *)
- job->request->rqst_data.h_vendor.vendor_cmd;
- timeout = link_diag_test_cmd->timeout * 100;
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1);
@@ -2010,11 +2420,12 @@ lpfc_sli4_bsg_link_diag_test(struct fc_bsg_job *job)
rc = -ENOMEM;
goto link_diag_test_exit;
}
+
run_link_diag_test = &pmboxq->u.mqe.un.link_diag_test;
bf_set(lpfc_mbx_run_diag_test_link_num, &run_link_diag_test->u.req,
- phba->sli4_hba.link_state.number);
+ phba->sli4_hba.lnk_info.lnk_no);
bf_set(lpfc_mbx_run_diag_test_link_type, &run_link_diag_test->u.req,
- phba->sli4_hba.link_state.type);
+ phba->sli4_hba.lnk_info.lnk_tp);
bf_set(lpfc_mbx_run_diag_test_test_id, &run_link_diag_test->u.req,
link_diag_test_cmd->test_id);
bf_set(lpfc_mbx_run_diag_test_loops, &run_link_diag_test->u.req,
@@ -2038,15 +2449,14 @@ lpfc_sli4_bsg_link_diag_test(struct fc_bsg_job *job)
}
diag_status_reply = (struct diag_status *)
- job->reply->reply_data.vendor_reply.vendor_rsp;
+ bsg_reply->reply_data.vendor_reply.vendor_rsp;
- if (job->reply_len <
- sizeof(struct fc_bsg_request) + sizeof(struct diag_status)) {
+ if (job->reply_len < sizeof(*bsg_reply) + sizeof(*diag_status_reply)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"3012 Received Run link diag test reply "
"below minimum size (%d): reply_len:%d\n",
- (int)(sizeof(struct fc_bsg_request) +
- sizeof(struct diag_status)),
+ (int)(sizeof(*bsg_reply) +
+ sizeof(*diag_status_reply)),
job->reply_len);
rc = -EINVAL;
goto job_error;
@@ -2057,7 +2467,7 @@ lpfc_sli4_bsg_link_diag_test(struct fc_bsg_job *job)
diag_status_reply->shdr_add_status = shdr_add_status;
link_diag_test_exit:
- rc = lpfc_sli4_bsg_set_link_diag_state(phba, 0);
+ rc1 = lpfc_sli4_bsg_set_link_diag_state(phba, 0);
if (pmboxq)
mempool_free(pmboxq, phba->mbox_mem_pool);
@@ -2066,10 +2476,13 @@ link_diag_test_exit:
job_error:
/* make error code available to userspace */
- job->reply->result = rc;
+ if (rc1 && !rc)
+ rc = rc1;
+ bsg_reply->result = rc;
/* complete the job back to userspace if no error */
if (rc == 0)
- job->job_done(job);
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return rc;
}
@@ -2091,10 +2504,22 @@ static int lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t *rpi)
if (!mbox)
return -ENOMEM;
- if (phba->sli_rev == LPFC_SLI_REV4)
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ status = lpfc_reg_rpi(phba, 0, phba->pport->fc_myDID,
+ (uint8_t *)&phba->pport->fc_sparam,
+ mbox, *rpi);
+ else {
*rpi = lpfc_sli4_alloc_rpi(phba);
- status = lpfc_reg_rpi(phba, 0, phba->pport->fc_myDID,
- (uint8_t *)&phba->pport->fc_sparam, mbox, *rpi);
+ if (*rpi == LPFC_RPI_ALLOC_ERROR) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return -EBUSY;
+ }
+ status = lpfc_reg_rpi(phba, phba->pport->vpi,
+ phba->pport->fc_myDID,
+ (uint8_t *)&phba->pport->fc_sparam,
+ mbox, *rpi);
+ }
+
if (status) {
mempool_free(mbox, phba->mbox_mem_pool);
if (phba->sli_rev == LPFC_SLI_REV4)
@@ -2102,9 +2527,9 @@ static int lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t *rpi)
return -ENOMEM;
}
- dmabuff = (struct lpfc_dmabuf *) mbox->context1;
- mbox->context1 = NULL;
- mbox->context2 = NULL;
+ dmabuff = mbox->ctx_buf;
+ mbox->ctx_buf = NULL;
+ mbox->ctx_ndlp = NULL;
status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) {
@@ -2117,7 +2542,8 @@ static int lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t *rpi)
return -ENODEV;
}
- *rpi = mbox->u.mb.un.varWords[0];
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ *rpi = mbox->u.mb.un.varWords[0];
lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys);
kfree(dmabuff);
@@ -2142,7 +2568,12 @@ static int lpfcdiag_loop_self_unreg(struct lpfc_hba *phba, uint16_t rpi)
if (mbox == NULL)
return -ENOMEM;
- lpfc_unreg_login(phba, 0, rpi, mbox);
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ lpfc_unreg_login(phba, 0, rpi, mbox);
+ else
+ lpfc_unreg_login(phba, phba->pport->vpi,
+ phba->sli4_hba.rpi_ids[rpi], mbox);
+
status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) {
@@ -2165,7 +2596,7 @@ static int lpfcdiag_loop_self_unreg(struct lpfc_hba *phba, uint16_t rpi)
*
* This function obtains the transmit and receive ids required to send
* an unsolicited ct command with a payload. A special lpfc FsType and CmdRsp
- * flags are used to the unsolicted response handler is able to process
+ * flags are used to the unsolicited response handler is able to process
* the ct command sent on the same port.
**/
static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
@@ -2173,14 +2604,14 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
{
struct lpfc_bsg_event *evt;
struct lpfc_iocbq *cmdiocbq, *rspiocbq;
- IOCB_t *cmd, *rsp;
struct lpfc_dmabuf *dmabuf;
struct ulp_bde64 *bpl = NULL;
struct lpfc_sli_ct_request *ctreq = NULL;
int ret_val = 0;
int time_left;
- int iocb_stat = 0;
+ int iocb_stat = IOCB_SUCCESS;
unsigned long flags;
+ u32 status;
*txxri = 0;
*rxxri = 0;
@@ -2224,9 +2655,6 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
goto err_get_xri_exit;
}
- cmd = &cmdiocbq->iocb;
- rsp = &rspiocbq->iocb;
-
memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ);
ctreq->RevisionId.bits.Revision = SLI_CT_REVISION;
@@ -2236,41 +2664,30 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_XRI_SETUP;
ctreq->CommandResponse.bits.Size = 0;
-
- cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(dmabuf->phys);
- cmd->un.xseq64.bdl.addrLow = putPaddrLow(dmabuf->phys);
- cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- cmd->un.xseq64.bdl.bdeSize = sizeof(*bpl);
-
- cmd->un.xseq64.w5.hcsw.Fctl = LA;
- cmd->un.xseq64.w5.hcsw.Dfctl = 0;
- cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
- cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT;
-
- cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CR;
- cmd->ulpBdeCount = 1;
- cmd->ulpLe = 1;
- cmd->ulpClass = CLASS3;
- cmd->ulpContext = rpi;
-
- cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
+ cmdiocbq->bpl_dmabuf = dmabuf;
+ cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
cmdiocbq->vport = phba->pport;
+ cmdiocbq->cmd_cmpl = NULL;
+
+ lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, dmabuf, rpi, 0, 1,
+ FC_RCTL_DD_SOL_CTL, 0, CMD_XMIT_SEQUENCE64_CR);
iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
- rspiocbq,
- (phba->fc_ratov * 2)
- + LPFC_DRVR_TIMEOUT);
- if (iocb_stat) {
+ rspiocbq, (phba->fc_ratov * 2)
+ + LPFC_DRVR_TIMEOUT);
+
+ status = get_job_ulpstatus(phba, rspiocbq);
+ if (iocb_stat != IOCB_SUCCESS || status != IOCB_SUCCESS) {
ret_val = -EIO;
goto err_get_xri_exit;
}
- *txxri = rsp->ulpContext;
+ *txxri = get_job_ulpcontext(phba, rspiocbq);
evt->waiting = 1;
evt->wait_time_stamp = jiffies;
time_left = wait_event_interruptible_timeout(
evt->wq, !list_empty(&evt->events_to_see),
- ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ);
+ secs_to_jiffies(phba->fc_ratov * 2 + LPFC_DRVR_TIMEOUT));
if (list_empty(&evt->events_to_see))
ret_val = (time_left) ? -EINTR : -ETIMEDOUT;
else {
@@ -2306,8 +2723,8 @@ err_get_xri_exit:
* lpfc_bsg_dma_page_alloc - allocate a bsg mbox page sized dma buffers
* @phba: Pointer to HBA context object
*
- * This function allocates BSG_MBOX_SIZE (4KB) page size dma buffer and.
- * retruns the pointer to the buffer.
+ * This function allocates BSG_MBOX_SIZE (4KB) page size dma buffer and
+ * returns the pointer to the buffer.
**/
static struct lpfc_dmabuf *
lpfc_bsg_dma_page_alloc(struct lpfc_hba *phba)
@@ -2330,7 +2747,6 @@ lpfc_bsg_dma_page_alloc(struct lpfc_hba *phba)
kfree(dmabuf);
return NULL;
}
- memset((uint8_t *)dmabuf->virt, 0, BSG_MBOX_SIZE);
return dmabuf;
}
@@ -2438,9 +2854,6 @@ diag_cmd_data_alloc(struct lpfc_hba *phba,
if (nocopydata) {
bpl->tus.f.bdeFlags = 0;
- pci_dma_sync_single_for_device(phba->pcidev,
- dmp->dma.phys, LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
-
} else {
memset((uint8_t *)dmp->dma.virt, 0, cnt);
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
@@ -2458,27 +2871,28 @@ diag_cmd_data_alloc(struct lpfc_hba *phba,
size -= cnt;
}
- mlist->flag = i;
- return mlist;
+ if (mlist) {
+ mlist->flag = i;
+ return mlist;
+ }
out:
diag_cmd_data_free(phba, mlist);
return NULL;
}
/**
- * lpfcdiag_loop_post_rxbufs - post the receive buffers for an unsol CT cmd
+ * lpfcdiag_sli3_loop_post_rxbufs - post the receive buffers for an unsol CT cmd
* @phba: Pointer to HBA context object
* @rxxri: Receive exchange id
* @len: Number of data bytes
*
* This function allocates and posts a data buffer of sufficient size to receive
- * an unsolicted CT command.
+ * an unsolicited CT command.
**/
-static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
- size_t len)
+static int lpfcdiag_sli3_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
+ size_t len)
{
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring;
struct lpfc_iocbq *cmdiocbq;
IOCB_t *cmd = NULL;
struct list_head head, *curr, *next;
@@ -2492,6 +2906,8 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
int iocb_stat;
int i = 0;
+ pring = lpfc_phba_elsring(phba);
+
cmdiocbq = lpfc_sli_get_iocbq(phba);
rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (rxbmp != NULL) {
@@ -2503,7 +2919,7 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
}
}
- if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer) {
+ if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer || !pring) {
ret_val = -ENOMEM;
goto err_post_rxbufs_exit;
}
@@ -2511,7 +2927,6 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
/* Queue buffers for the receive exchange */
num_bde = (uint32_t)rxbuffer->flag;
dmp = &rxbuffer->dma;
-
cmd = &cmdiocbq->iocb;
i = 0;
@@ -2541,7 +2956,7 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
cmd->un.cont64[i].addrLow = putPaddrLow(mp[i]->phys);
cmd->un.cont64[i].tus.f.bdeSize =
((struct lpfc_dmabufext *)mp[i])->size;
- cmd->ulpBdeCount = ++i;
+ cmd->ulpBdeCount = ++i;
if ((--num_bde > 0) && (i < 2))
continue;
@@ -2579,7 +2994,6 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
ret_val = -EIO;
goto err_post_rxbufs_exit;
}
-
cmd = &cmdiocbq->iocb;
i = 0;
}
@@ -2618,11 +3032,11 @@ err_post_rxbufs_exit:
* of loopback mode.
**/
static int
-lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
+lpfc_bsg_diag_loopback_run(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct lpfc_hba *phba = vport->phba;
- struct diag_mode_test *diag_mode;
struct lpfc_bsg_event *evt;
struct event_data *evdat;
struct lpfc_sli *psli = &phba->sli;
@@ -2630,26 +3044,26 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
uint32_t full_size;
size_t segment_len = 0, segment_offset = 0, current_offset = 0;
uint16_t rpi = 0;
- struct lpfc_iocbq *cmdiocbq, *rspiocbq;
- IOCB_t *cmd, *rsp;
+ struct lpfc_iocbq *cmdiocbq, *rspiocbq = NULL;
+ union lpfc_wqe128 *cmdwqe, *rspwqe;
struct lpfc_sli_ct_request *ctreq;
struct lpfc_dmabuf *txbmp;
struct ulp_bde64 *txbpl = NULL;
struct lpfc_dmabufext *txbuffer = NULL;
struct list_head head;
struct lpfc_dmabuf *curr;
- uint16_t txxri, rxxri;
+ uint16_t txxri = 0, rxxri;
uint32_t num_bde;
uint8_t *ptr = NULL, *rx_databuf = NULL;
int rc = 0;
int time_left;
- int iocb_stat;
+ int iocb_stat = IOCB_SUCCESS;
unsigned long flags;
void *dataout = NULL;
uint32_t total_mem;
/* in case no data is returned return just the return code */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
if (job->request_len <
sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_test)) {
@@ -2666,9 +3080,6 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
goto loopback_test_exit;
}
- diag_mode = (struct diag_mode_test *)
- job->request->rqst_data.h_vendor.vendor_cmd;
-
if ((phba->link_state == LPFC_HBA_ERROR) ||
(psli->sli_flag & LPFC_BLOCK_MGMT_IO) ||
(!(psli->sli_flag & LPFC_SLI_ACTIVE))) {
@@ -2720,18 +3131,19 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
if (rc)
goto loopback_test_exit;
- rc = lpfcdiag_loop_get_xri(phba, rpi, &txxri, &rxxri);
- if (rc) {
- lpfcdiag_loop_self_unreg(phba, rpi);
- goto loopback_test_exit;
- }
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ rc = lpfcdiag_loop_get_xri(phba, rpi, &txxri, &rxxri);
+ if (rc) {
+ lpfcdiag_loop_self_unreg(phba, rpi);
+ goto loopback_test_exit;
+ }
- rc = lpfcdiag_loop_post_rxbufs(phba, rxxri, full_size);
- if (rc) {
- lpfcdiag_loop_self_unreg(phba, rpi);
- goto loopback_test_exit;
+ rc = lpfcdiag_sli3_loop_post_rxbufs(phba, rxxri, full_size);
+ if (rc) {
+ lpfcdiag_loop_self_unreg(phba, rpi);
+ goto loopback_test_exit;
+ }
}
-
evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid,
SLI_CT_ELX_LOOPBACK);
if (!evt) {
@@ -2746,7 +3158,8 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
cmdiocbq = lpfc_sli_get_iocbq(phba);
- rspiocbq = lpfc_sli_get_iocbq(phba);
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ rspiocbq = lpfc_sli_get_iocbq(phba);
txbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (txbmp) {
@@ -2759,14 +3172,21 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
}
}
- if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer ||
- !txbmp->virt) {
+ if (!cmdiocbq || !txbmp || !txbpl || !txbuffer || !txbmp->virt) {
+ rc = -ENOMEM;
+ goto err_loopback_test_exit;
+ }
+ if ((phba->sli_rev < LPFC_SLI_REV4) && !rspiocbq) {
rc = -ENOMEM;
goto err_loopback_test_exit;
}
- cmd = &cmdiocbq->iocb;
- rsp = &rspiocbq->iocb;
+ cmdwqe = &cmdiocbq->wqe;
+ memset(cmdwqe, 0, sizeof(*cmdwqe));
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ rspwqe = &rspiocbq->wqe;
+ memset(rspwqe, 0, sizeof(*rspwqe));
+ }
INIT_LIST_HEAD(&head);
list_add_tail(&head, &txbuffer->dma.list);
@@ -2779,8 +3199,8 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
ctreq->RevisionId.bits.InId = 0;
ctreq->FsType = SLI_CT_ELX_LOOPBACK;
ctreq->FsSubType = 0;
- ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_DATA;
- ctreq->CommandResponse.bits.Size = size;
+ ctreq->CommandResponse.bits.CmdRsp = cpu_to_be16(ELX_LOOPBACK_DATA);
+ ctreq->CommandResponse.bits.Size = cpu_to_be16(size);
segment_offset = ELX_LOOPBACK_HEADER_SZ;
} else
segment_offset = 0;
@@ -2796,33 +3216,40 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
list_del(&head);
/* Build the XMIT_SEQUENCE iocb */
-
num_bde = (uint32_t)txbuffer->flag;
- cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(txbmp->phys);
- cmd->un.xseq64.bdl.addrLow = putPaddrLow(txbmp->phys);
- cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- cmd->un.xseq64.bdl.bdeSize = (num_bde * sizeof(struct ulp_bde64));
+ cmdiocbq->num_bdes = num_bde;
+ cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
+ cmdiocbq->cmd_flag |= LPFC_IO_LOOPBACK;
+ if (phba->cfg_vmid_app_header)
+ cmdiocbq->cmd_flag |= LPFC_IO_VMID;
- cmd->un.xseq64.w5.hcsw.Fctl = (LS | LA);
- cmd->un.xseq64.w5.hcsw.Dfctl = 0;
- cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
- cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT;
+ cmdiocbq->vport = phba->pport;
+ cmdiocbq->cmd_cmpl = NULL;
+ cmdiocbq->bpl_dmabuf = txbmp;
- cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX;
- cmd->ulpBdeCount = 1;
- cmd->ulpLe = 1;
- cmd->ulpClass = CLASS3;
- cmd->ulpContext = txxri;
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, txbmp, 0, txxri,
+ num_bde, FC_RCTL_DD_UNSOL_CTL, 1,
+ CMD_XMIT_SEQUENCE64_CX);
- cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
- cmdiocbq->vport = phba->pport;
+ } else {
+ lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, txbmp,
+ phba->sli4_hba.rpi_ids[rpi], 0xffff,
+ full_size, FC_RCTL_DD_UNSOL_CTL, 1,
+ CMD_XMIT_SEQUENCE64_WQE);
+ cmdiocbq->sli4_xritag = NO_XRI;
+ }
iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
rspiocbq, (phba->fc_ratov * 2) +
LPFC_DRVR_TIMEOUT);
-
- if ((iocb_stat != IOCB_SUCCESS) || (rsp->ulpStatus != IOCB_SUCCESS)) {
+ if (iocb_stat != IOCB_SUCCESS ||
+ (phba->sli_rev < LPFC_SLI_REV4 &&
+ (get_job_ulpstatus(phba, rspiocbq) != IOSTAT_SUCCESS))) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "3126 Failed loopback test issue iocb: "
+ "iocb_stat:x%x\n", iocb_stat);
rc = -EIO;
goto err_loopback_test_exit;
}
@@ -2830,11 +3257,14 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
evt->waiting = 1;
time_left = wait_event_interruptible_timeout(
evt->wq, !list_empty(&evt->events_to_see),
- ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ);
+ secs_to_jiffies(phba->fc_ratov * 2 + LPFC_DRVR_TIMEOUT));
evt->waiting = 0;
- if (list_empty(&evt->events_to_see))
+ if (list_empty(&evt->events_to_see)) {
rc = (time_left) ? -EINTR : -ETIMEDOUT;
- else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "3125 Not receiving unsolicited event, "
+ "rc:x%x\n", rc);
+ } else {
spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_move(evt->events_to_see.prev, &evt->events_to_get);
evdat = list_entry(evt->events_to_get.prev,
@@ -2854,11 +3284,11 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job)
rc = IOCB_SUCCESS;
/* skip over elx loopback header */
rx_databuf += ELX_LOOPBACK_HEADER_SZ;
- job->reply->reply_payload_rcv_len =
+ bsg_reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt,
rx_databuf, size);
- job->reply->reply_payload_rcv_len = size;
+ bsg_reply->reply_payload_rcv_len = size;
}
}
@@ -2870,7 +3300,7 @@ err_loopback_test_exit:
lpfc_bsg_event_unref(evt); /* delete */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- if (cmdiocbq != NULL)
+ if ((cmdiocbq != NULL) && (iocb_stat != IOCB_TIMEDOUT))
lpfc_sli_release_iocbq(phba, cmdiocbq);
if (rspiocbq != NULL)
@@ -2888,11 +3318,12 @@ err_loopback_test_exit:
loopback_test_exit:
kfree(dataout);
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
job->dd_data = NULL;
/* complete the job back to userspace if no error */
- if (rc == 0)
- job->job_done(job);
+ if (rc == IOCB_SUCCESS)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return rc;
}
@@ -2901,11 +3332,11 @@ loopback_test_exit:
* @job: GET_DFC_REV fc_bsg_job
**/
static int
-lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job)
+lpfc_bsg_get_dfc_rev(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct lpfc_hba *phba = vport->phba;
- struct get_mgmt_rev *event_req;
struct get_mgmt_rev_reply *event_reply;
int rc = 0;
@@ -2918,14 +3349,10 @@ lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job)
goto job_error;
}
- event_req = (struct get_mgmt_rev *)
- job->request->rqst_data.h_vendor.vendor_cmd;
-
event_reply = (struct get_mgmt_rev_reply *)
- job->reply->reply_data.vendor_reply.vendor_rsp;
+ bsg_reply->reply_data.vendor_reply.vendor_rsp;
- if (job->reply_len <
- sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev_reply)) {
+ if (job->reply_len < sizeof(*bsg_reply) + sizeof(*event_reply)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2741 Received GET_DFC_REV reply below "
"minimum size\n");
@@ -2936,9 +3363,10 @@ lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job)
event_reply->info.a_Major = MANAGEMENT_MAJOR_REV;
event_reply->info.a_Minor = MANAGEMENT_MINOR_REV;
job_error:
- job->reply->result = rc;
+ bsg_reply->result = rc;
if (rc == 0)
- job->job_done(job);
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return rc;
}
@@ -2950,25 +3378,20 @@ job_error:
* This is completion handler function for mailbox commands issued from
* lpfc_bsg_issue_mbox function. This function is called by the
* mailbox event handler function with no lock held. This function
- * will wake up thread waiting on the wait queue pointed by context1
+ * will wake up thread waiting on the wait queue pointed by dd_data
* of the mailbox.
**/
-void
+static void
lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
struct bsg_job_data *dd_data;
- struct fc_bsg_job *job;
+ struct fc_bsg_reply *bsg_reply;
+ struct bsg_job *job;
uint32_t size;
unsigned long flags;
uint8_t *pmb, *pmb_buf;
- spin_lock_irqsave(&phba->ct_ev_lock, flags);
- dd_data = pmboxq->context1;
- /* job already timed out? */
- if (!dd_data) {
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- return;
- }
+ dd_data = pmboxq->ctx_u.dd_data;
/*
* The outgoing buffer is readily referred from the dma buffer,
@@ -2978,32 +3401,38 @@ lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb;
memcpy(pmb_buf, pmb, sizeof(MAILBOX_t));
- job = dd_data->context_un.mbox.set_job;
+ /* Determine if job has been aborted */
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ job = dd_data->set_job;
+ if (job) {
+ /* Prevent timeout handling from trying to abort job */
+ job->dd_data = NULL;
+ }
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+ /* Copy the mailbox data to the job if it is still active */
+
if (job) {
+ bsg_reply = job->reply;
size = job->reply_payload.payload_len;
- job->reply->reply_payload_rcv_len =
+ bsg_reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt,
pmb_buf, size);
- /* need to hold the lock until we set job->dd_data to NULL
- * to hold off the timeout handler returning to the mid-layer
- * while we are still processing the job.
- */
- job->dd_data = NULL;
- dd_data->context_un.mbox.set_job = NULL;
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- } else {
- dd_data->context_un.mbox.set_job = NULL;
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
}
+ dd_data->set_job = NULL;
mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool);
lpfc_bsg_dma_page_free(phba, dd_data->context_un.mbox.dmabuffers);
kfree(dd_data);
+ /* Complete the job if the job is still active */
+
if (job) {
- job->reply->result = 0;
- job->job_done(job);
+ bsg_reply->result = 0;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
}
return;
}
@@ -3034,13 +3463,14 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
case MBX_RUN_DIAGS:
case MBX_RESTART:
case MBX_SET_MASK:
- if (!(vport->fc_flag & FC_OFFLINE_MODE)) {
+ if (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2743 Command 0x%x is illegal in on-line "
"state\n",
mb->mbxCommand);
return -EPERM;
}
+ break;
case MBX_WRITE_NV:
case MBX_WRITE_VPARMS:
case MBX_LOAD_SM:
@@ -3055,6 +3485,7 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
case MBX_DOWN_LOAD:
case MBX_UPDATE_CFG:
case MBX_KILL_BOARD:
+ case MBX_READ_TOPOLOGY:
case MBX_LOAD_AREA:
case MBX_LOAD_EXP_ROM:
case MBX_BEACON:
@@ -3074,16 +3505,8 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
"1226 mbox: set_variable 0x%x, 0x%x\n",
mb->un.varWords[0],
mb->un.varWords[1]);
- if ((mb->un.varWords[0] == SETVAR_MLOMNT)
- && (mb->un.varWords[1] == 1)) {
- phba->wait_4_mlo_maint_flg = 1;
- } else if (mb->un.varWords[0] == SETVAR_MLORST) {
- phba->link_flag &= ~LS_LOOPBACK_MODE;
- phba->fc_topology = LPFC_TOPOLOGY_PT_PT;
- }
break;
case MBX_READ_SPARM64:
- case MBX_READ_TOPOLOGY:
case MBX_REG_LOGIN:
case MBX_REG_LOGIN64:
case MBX_CONFIG_PORT:
@@ -3099,7 +3522,7 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
}
/**
- * lpfc_bsg_mbox_ext_cleanup - clean up context of multi-buffer mbox session
+ * lpfc_bsg_mbox_ext_session_reset - clean up context of multi-buffer mbox session
* @phba: Pointer to HBA context object.
*
* This is routine clean up and reset BSG handling of multi-buffer mbox
@@ -3124,6 +3547,103 @@ lpfc_bsg_mbox_ext_session_reset(struct lpfc_hba *phba)
}
/**
+ * lpfc_rd_obj_emb0_handle_job - Handles completion for non-embedded
+ * READ_OBJECT_V0 mailbox commands
+ * @phba: pointer to lpfc_hba data struct
+ * @pmb_buf: pointer to mailbox buffer
+ * @sli_cfg_mbx: pointer to SLI_CONFIG mailbox memory region
+ * @job: pointer to bsg_job struct
+ * @bsg_reply: point to bsg_reply struct
+ *
+ * Given a non-embedded READ_OBJECT_V0's HBD_CNT, this routine copies
+ * a READ_OBJECT_V0 mailbox command's read data payload into a bsg_job
+ * structure for passing back to application layer.
+ *
+ * Return codes
+ * 0 - successful
+ * -EINVAL - invalid HBD_CNT
+ * -ENODEV - pointer to bsg_job struct is NULL
+ **/
+static int
+lpfc_rd_obj_emb0_handle_job(struct lpfc_hba *phba, u8 *pmb_buf,
+ struct lpfc_sli_config_mbox *sli_cfg_mbx,
+ struct bsg_job *job,
+ struct fc_bsg_reply *bsg_reply)
+{
+ struct lpfc_dmabuf *curr_dmabuf, *next_dmabuf;
+ struct lpfc_sli_config_emb0_subsys *emb0_subsys;
+ u32 hbd_cnt;
+ u32 dma_buf_len;
+ u8 i = 0;
+ size_t extra_bytes;
+ off_t skip = 0;
+
+ if (!job) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "2496 NULL job\n");
+ return -ENODEV;
+ }
+
+ if (!bsg_reply) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "2498 NULL bsg_reply\n");
+ return -ENODEV;
+ }
+
+ emb0_subsys = &sli_cfg_mbx->un.sli_config_emb0_subsys;
+
+ hbd_cnt = bsg_bf_get(lpfc_emb0_subcmnd_rd_obj_hbd_cnt,
+ emb0_subsys);
+
+ /* Calculate where the read object's read data payload is located based
+ * on HBD count scheme.
+ */
+ if (hbd_cnt >= rd_obj_scheme[0].min_hbd_cnt &&
+ hbd_cnt <= rd_obj_scheme[0].max_hbd_cnt) {
+ skip = rd_obj_scheme[0].payload_word_offset * 4;
+ } else if (hbd_cnt >= rd_obj_scheme[1].min_hbd_cnt &&
+ hbd_cnt <= rd_obj_scheme[1].max_hbd_cnt) {
+ skip = rd_obj_scheme[1].payload_word_offset * 4;
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "2497 bad hbd_count 0x%08x\n",
+ hbd_cnt);
+ return -EINVAL;
+ }
+
+ /* Copy SLI_CONFIG command and READ_OBJECT response first */
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ pmb_buf, skip);
+
+ /* Copy data from hbds */
+ list_for_each_entry_safe(curr_dmabuf, next_dmabuf,
+ &phba->mbox_ext_buf_ctx.ext_dmabuf_list,
+ list) {
+ dma_buf_len = emb0_subsys->hbd[i].buf_len;
+
+ /* Use sg_copy_buffer to specify a skip offset */
+ extra_bytes = sg_copy_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ curr_dmabuf->virt,
+ dma_buf_len, skip, false);
+
+ bsg_reply->reply_payload_rcv_len += extra_bytes;
+
+ skip += extra_bytes;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "2499 copied hbd[%d] "
+ "0x%zx bytes\n",
+ i, extra_bytes);
+ i++;
+ }
+
+ return 0;
+}
+
+/**
* lpfc_bsg_issue_mbox_ext_handle_job - job handler for multi-buffer mbox cmpl
* @phba: Pointer to HBA context object.
* @pmboxq: Pointer to mailbox command.
@@ -3131,67 +3651,107 @@ lpfc_bsg_mbox_ext_session_reset(struct lpfc_hba *phba)
* This is routine handles BSG job for mailbox commands completions with
* multiple external buffers.
**/
-static struct fc_bsg_job *
+static struct bsg_job *
lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
struct bsg_job_data *dd_data;
- struct fc_bsg_job *job;
+ struct bsg_job *job;
+ struct fc_bsg_reply *bsg_reply = NULL;
uint8_t *pmb, *pmb_buf;
unsigned long flags;
- uint32_t size;
+ u32 size, opcode;
int rc = 0;
+ struct lpfc_dmabuf *dmabuf;
+ struct lpfc_sli_config_mbox *sli_cfg_mbx;
+ uint8_t *pmbx;
+
+ dd_data = pmboxq->ctx_u.dd_data;
+ /* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
- dd_data = pmboxq->context1;
- /* has the job already timed out? */
- if (!dd_data) {
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- job = NULL;
- goto job_done_out;
+ job = dd_data->set_job;
+ if (job) {
+ bsg_reply = job->reply;
+ /* Prevent timeout handling from trying to abort job */
+ job->dd_data = NULL;
}
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/*
* The outgoing buffer is readily referred from the dma buffer,
* just need to get header part from mailboxq structure.
*/
+
pmb = (uint8_t *)&pmboxq->u.mb;
pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb;
+ /* Copy the byte swapped response mailbox back to the user */
memcpy(pmb_buf, pmb, sizeof(MAILBOX_t));
+ /* if there is any non-embedded extended data copy that too */
+ dmabuf = phba->mbox_ext_buf_ctx.mbx_dmabuf;
+ sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
+ if (!bsg_bf_get(lpfc_mbox_hdr_emb,
+ &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) {
+ pmbx = (uint8_t *)dmabuf->virt;
+ /* byte swap the extended data following the mailbox command */
+ lpfc_sli_pcimem_bcopy(&pmbx[sizeof(MAILBOX_t)],
+ &pmbx[sizeof(MAILBOX_t)],
+ sli_cfg_mbx->un.sli_config_emb0_subsys.mse[0].buf_len);
+
+ /* Special handling for non-embedded READ_OBJECT */
+ opcode = bsg_bf_get(lpfc_emb0_subcmnd_opcode,
+ &sli_cfg_mbx->un.sli_config_emb0_subsys);
+ switch (opcode) {
+ case COMN_OPCODE_READ_OBJECT:
+ if (job) {
+ rc = lpfc_rd_obj_emb0_handle_job(phba, pmb_buf,
+ sli_cfg_mbx,
+ job,
+ bsg_reply);
+ bsg_reply->result = rc;
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Complete the job if the job is still active */
- job = dd_data->context_un.mbox.set_job;
if (job) {
size = job->reply_payload.payload_len;
- job->reply->reply_payload_rcv_len =
+ bsg_reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt,
pmb_buf, size);
+
/* result for successful */
- job->reply->result = 0;
- job->dd_data = NULL;
- /* need to hold the lock util we set job->dd_data to NULL
- * to hold off the timeout handler from midlayer to take
- * any action.
- */
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ bsg_reply->result = 0;
+done:
+
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
- "2937 SLI_CONFIG ext-buffer maibox command "
+ "2937 SLI_CONFIG ext-buffer mailbox command "
"(x%x/x%x) complete bsg job done, bsize:%d\n",
phba->mbox_ext_buf_ctx.nembType,
- phba->mbox_ext_buf_ctx.mboxType, size);
- } else
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
-
-job_done_out:
- if (!job)
+ phba->mbox_ext_buf_ctx.mboxType,
+ job->reply_payload.payload_len);
+ lpfc_idiag_mbxacc_dump_bsg_mbox(phba,
+ phba->mbox_ext_buf_ctx.nembType,
+ phba->mbox_ext_buf_ctx.mboxType,
+ dma_ebuf, sta_pos_addr,
+ phba->mbox_ext_buf_ctx.mbx_dmabuf, 0);
+ } else {
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
- "2938 SLI_CONFIG ext-buffer maibox "
+ "2938 SLI_CONFIG ext-buffer mailbox "
"command (x%x/x%x) failure, rc:x%x\n",
phba->mbox_ext_buf_ctx.nembType,
phba->mbox_ext_buf_ctx.mboxType, rc);
+ }
+
+
/* state change */
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_DONE;
kfree(dd_data);
-
return job;
}
@@ -3206,29 +3766,32 @@ job_done_out:
static void
lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
- struct fc_bsg_job *job;
+ struct bsg_job *job;
+ struct fc_bsg_reply *bsg_reply;
+
+ job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
/* handle the BSG job with mailbox command */
- if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_ABTS)
+ if (!job)
pmboxq->u.mb.mbxStatus = MBXERR_ERROR;
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
- "2939 SLI_CONFIG ext-buffer rd maibox command "
+ "2939 SLI_CONFIG ext-buffer rd mailbox command "
"complete, ctxState:x%x, mbxStatus:x%x\n",
phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus);
- job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
-
if (pmboxq->u.mb.mbxStatus || phba->mbox_ext_buf_ctx.numBuf == 1)
lpfc_bsg_mbox_ext_session_reset(phba);
/* free base driver mailbox structure memory */
mempool_free(pmboxq, phba->mbox_mem_pool);
- /* complete the bsg job if we have it */
- if (job)
- job->job_done(job);
-
+ /* if the job is still active, call job done */
+ if (job) {
+ bsg_reply = job->reply;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ }
return;
}
@@ -3243,26 +3806,30 @@ lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
static void
lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
- struct fc_bsg_job *job;
+ struct bsg_job *job;
+ struct fc_bsg_reply *bsg_reply;
+
+ job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
/* handle the BSG job with the mailbox command */
- if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_ABTS)
+ if (!job)
pmboxq->u.mb.mbxStatus = MBXERR_ERROR;
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
- "2940 SLI_CONFIG ext-buffer wr maibox command "
+ "2940 SLI_CONFIG ext-buffer wr mailbox command "
"complete, ctxState:x%x, mbxStatus:x%x\n",
phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus);
- job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
-
/* free all memory, including dma buffers */
mempool_free(pmboxq, phba->mbox_mem_pool);
lpfc_bsg_mbox_ext_session_reset(phba);
- /* complete the bsg job if we have it */
- if (job)
- job->job_done(job);
+ /* if the job is still active, call job done */
+ if (job) {
+ bsg_reply = job->reply;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ }
return;
}
@@ -3361,40 +3928,44 @@ lpfc_bsg_sli_cfg_dma_desc_setup(struct lpfc_hba *phba, enum nemb_type nemb_tp,
}
/**
- * lpfc_bsg_sli_cfg_mse_read_cmd_ext - sli_config non-embedded mailbox cmd read
+ * lpfc_bsg_sli_cfg_read_cmd_ext - sli_config non-embedded mailbox cmd read
* @phba: Pointer to HBA context object.
- * @mb: Pointer to a BSG mailbox object.
+ * @job: Pointer to the job object.
* @nemb_tp: Enumerate of non-embedded mailbox command type.
- * @dmabuff: Pointer to a DMA buffer descriptor.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
*
* This routine performs SLI_CONFIG (0x9B) read mailbox command operation with
- * non-embedded external bufffers.
+ * non-embedded external buffers.
**/
static int
-lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
+lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job,
enum nemb_type nemb_tp,
struct lpfc_dmabuf *dmabuf)
{
+ struct fc_bsg_request *bsg_request = job->request;
struct lpfc_sli_config_mbox *sli_cfg_mbx;
+ struct lpfc_sli_config_emb0_subsys *emb0_subsys;
+ struct list_head *ext_dmabuf_list;
struct dfc_mbox_req *mbox_req;
struct lpfc_dmabuf *curr_dmabuf, *next_dmabuf;
- uint32_t ext_buf_cnt, ext_buf_index;
+ u32 ext_buf_cnt, ext_buf_index, hbd_cnt;
struct lpfc_dmabuf *ext_dmabuf = NULL;
struct bsg_job_data *dd_data = NULL;
LPFC_MBOXQ_t *pmboxq = NULL;
MAILBOX_t *pmb;
- uint8_t *pmbx;
+ u8 *pmbx, opcode;
int rc, i;
mbox_req =
- (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd;
+ (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
/* pointer to the start of mailbox command */
sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
if (nemb_tp == nemb_mse) {
+ emb0_subsys = &sli_cfg_mbx->un.sli_config_emb0_subsys;
ext_buf_cnt = bsg_bf_get(lpfc_mbox_hdr_mse_cnt,
- &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr);
+ &emb0_subsys->sli_config_hdr);
if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_MSE) {
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
"2945 Handled SLI_CONFIG(mse) rd, "
@@ -3404,12 +3975,63 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
rc = -ERANGE;
goto job_error;
}
+
+ /* Special handling for non-embedded READ_OBJECT */
+ opcode = bsg_bf_get(lpfc_emb0_subcmnd_opcode, emb0_subsys);
+ switch (opcode) {
+ case COMN_OPCODE_READ_OBJECT:
+ hbd_cnt = bsg_bf_get(lpfc_emb0_subcmnd_rd_obj_hbd_cnt,
+ emb0_subsys);
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "2449 SLI_CONFIG(mse) rd non-embedded "
+ "hbd count = %d\n",
+ hbd_cnt);
+
+ ext_dmabuf_list =
+ &phba->mbox_ext_buf_ctx.ext_dmabuf_list;
+
+ /* Allocate hbds */
+ for (i = 0; i < hbd_cnt; i++) {
+ ext_dmabuf = lpfc_bsg_dma_page_alloc(phba);
+ if (!ext_dmabuf) {
+ rc = -ENOMEM;
+ goto job_error;
+ }
+ list_add_tail(&ext_dmabuf->list,
+ ext_dmabuf_list);
+ }
+
+ /* Fill out the physical memory addresses for the
+ * hbds
+ */
+ i = 0;
+ list_for_each_entry_safe(curr_dmabuf, next_dmabuf,
+ ext_dmabuf_list, list) {
+ emb0_subsys->hbd[i].pa_hi =
+ putPaddrHigh(curr_dmabuf->phys);
+ emb0_subsys->hbd[i].pa_lo =
+ putPaddrLow(curr_dmabuf->phys);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "2495 SLI_CONFIG(hbd)[%d], "
+ "bufLen:%d, addrHi:x%x, "
+ "addrLo:x%x\n", i,
+ emb0_subsys->hbd[i].buf_len,
+ emb0_subsys->hbd[i].pa_hi,
+ emb0_subsys->hbd[i].pa_lo);
+ i++;
+ }
+ break;
+ default:
+ break;
+ }
+
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2941 Handled SLI_CONFIG(mse) rd, "
"ext_buf_cnt:%d\n", ext_buf_cnt);
} else {
/* sanity check on interface type for support */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2) {
rc = -ENODEV;
goto job_error;
@@ -3430,6 +4052,10 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
"ext_buf_cnt:%d\n", ext_buf_cnt);
}
+ /* before dma descriptor setup */
+ lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox,
+ sta_pre_addr, dmabuf, ext_buf_cnt);
+
/* reject non-embedded mailbox command with none external buffer */
if (ext_buf_cnt == 0) {
rc = -EPERM;
@@ -3477,6 +4103,10 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
}
}
+ /* after dma descriptor setup */
+ lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox,
+ sta_pos_addr, dmabuf, ext_buf_cnt);
+
/* construct base driver mbox command */
pmb = &pmboxq->u.mb;
pmbx = (uint8_t *)dmabuf->virt;
@@ -3496,26 +4126,38 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
pmboxq->mbox_cmpl = lpfc_bsg_issue_read_mbox_ext_cmpl;
/* context fields to callback function */
- pmboxq->context1 = dd_data;
+ pmboxq->ctx_u.dd_data = dd_data;
dd_data->type = TYPE_MBOX;
+ dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx;
- dd_data->context_un.mbox.set_job = job;
job->dd_data = dd_data;
/* state change */
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
+ /*
+ * Non-embedded mailbox subcommand data gets byte swapped here because
+ * the lower level driver code only does the first 64 mailbox words.
+ */
+ if ((!bsg_bf_get(lpfc_mbox_hdr_emb,
+ &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) &&
+ (nemb_tp == nemb_mse))
+ lpfc_sli_pcimem_bcopy(&pmbx[sizeof(MAILBOX_t)],
+ &pmbx[sizeof(MAILBOX_t)],
+ sli_cfg_mbx->un.sli_config_emb0_subsys.
+ mse[0].buf_len);
+
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2947 Issued SLI_CONFIG ext-buffer "
- "maibox command, rc:x%x\n", rc);
- return 1;
+ "mailbox command, rc:x%x\n", rc);
+ return SLI_CONFIG_HANDLED;
}
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
"2948 Failed to issue SLI_CONFIG ext-buffer "
- "maibox command, rc:x%x\n", rc);
+ "mailbox command, rc:x%x\n", rc);
rc = -EPIPE;
job_error:
@@ -3531,17 +4173,20 @@ job_error:
/**
* lpfc_bsg_sli_cfg_write_cmd_ext - sli_config non-embedded mailbox cmd write
* @phba: Pointer to HBA context object.
- * @mb: Pointer to a BSG mailbox object.
- * @dmabuff: Pointer to a DMA buffer descriptor.
+ * @job: Pointer to the job object.
+ * @nemb_tp: Enumerate of non-embedded mailbox command type.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
*
* This routine performs SLI_CONFIG (0x9B) write mailbox command operation with
- * non-embedded external bufffers.
+ * non-embedded external buffers.
**/
static int
-lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
+lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job,
enum nemb_type nemb_tp,
struct lpfc_dmabuf *dmabuf)
{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct dfc_mbox_req *mbox_req;
struct lpfc_sli_config_mbox *sli_cfg_mbx;
uint32_t ext_buf_cnt;
@@ -3549,10 +4194,10 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
LPFC_MBOXQ_t *pmboxq = NULL;
MAILBOX_t *pmb;
uint8_t *mbx;
- int rc = 0, i;
+ int rc = SLI_CONFIG_NOT_HANDLED, i;
mbox_req =
- (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd;
+ (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
/* pointer to the start of mailbox command */
sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
@@ -3562,7 +4207,7 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
&sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr);
if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_MSE) {
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
- "2953 Handled SLI_CONFIG(mse) wr, "
+ "2953 Failed SLI_CONFIG(mse) wr, "
"ext_buf_cnt(%d) out of range(%d)\n",
ext_buf_cnt,
LPFC_MBX_SLI_CONFIG_MAX_MSE);
@@ -3573,14 +4218,14 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
"ext_buf_cnt:%d\n", ext_buf_cnt);
} else {
/* sanity check on interface type for support */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2)
return -ENODEV;
/* nemb_tp == nemb_hbd */
ext_buf_cnt = sli_cfg_mbx->un.sli_config_emb1_subsys.hbd_count;
if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_HBD) {
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
- "2954 Handled SLI_CONFIG(hbd) wr, "
+ "2954 Failed SLI_CONFIG(hbd) wr, "
"ext_buf_cnt(%d) out of range(%d)\n",
ext_buf_cnt,
LPFC_MBX_SLI_CONFIG_MAX_HBD);
@@ -3591,12 +4236,20 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
"ext_buf_cnt:%d\n", ext_buf_cnt);
}
+ /* before dma buffer descriptor setup */
+ lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox,
+ sta_pre_addr, dmabuf, ext_buf_cnt);
+
if (ext_buf_cnt == 0)
return -EPERM;
/* for the first external buffer */
lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, 0, dmabuf, dmabuf);
+ /* after dma descriptor setup */
+ lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox,
+ sta_pos_addr, dmabuf, ext_buf_cnt);
+
/* log for looking forward */
for (i = 1; i < ext_buf_cnt; i++) {
if (nemb_tp == nemb_mse)
@@ -3645,29 +4298,37 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
pmboxq->mbox_cmpl = lpfc_bsg_issue_write_mbox_ext_cmpl;
/* context fields to callback function */
- pmboxq->context1 = dd_data;
+ pmboxq->ctx_u.dd_data = dd_data;
dd_data->type = TYPE_MBOX;
+ dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)mbx;
- dd_data->context_un.mbox.set_job = job;
job->dd_data = dd_data;
/* state change */
- phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
+ phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2955 Issued SLI_CONFIG ext-buffer "
- "maibox command, rc:x%x\n", rc);
- return 1;
+ "mailbox command, rc:x%x\n", rc);
+ return SLI_CONFIG_HANDLED;
}
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
"2956 Failed to issue SLI_CONFIG ext-buffer "
- "maibox command, rc:x%x\n", rc);
+ "mailbox command, rc:x%x\n", rc);
rc = -EPIPE;
+ goto job_error;
}
+ /* wait for additional external buffers */
+
+ bsg_reply->result = 0;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ return SLI_CONFIG_HANDLED;
+
job_error:
if (pmboxq)
mempool_free(pmboxq, phba->mbox_mem_pool);
@@ -3679,15 +4340,15 @@ job_error:
/**
* lpfc_bsg_handle_sli_cfg_mbox - handle sli-cfg mailbox cmd with ext buffer
* @phba: Pointer to HBA context object.
- * @mb: Pointer to a BSG mailbox object.
- * @dmabuff: Pointer to a DMA buffer descriptor.
+ * @job: Pointer to the job object.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
*
* This routine handles SLI_CONFIG (0x9B) mailbox command with non-embedded
- * external bufffers, including both 0x9B with non-embedded MSEs and 0x9B
- * with embedded sussystem 0x1 and opcodes with external HBDs.
+ * external buffers, including both 0x9B with non-embedded MSEs and 0x9B
+ * with embedded subsystem 0x1 and opcodes with external HBDs.
**/
static int
-lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
+lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct bsg_job *job,
struct lpfc_dmabuf *dmabuf)
{
struct lpfc_sli_config_mbox *sli_cfg_mbx;
@@ -3695,7 +4356,7 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
uint32_t opcode;
int rc = SLI_CONFIG_NOT_HANDLED;
- /* state change */
+ /* state change on new multi-buffer pass-through mailbox command */
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_HOST;
sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
@@ -3709,6 +4370,7 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
if (subsys == SLI_CONFIG_SUBSYS_FCOE) {
switch (opcode) {
case FCOE_OPCODE_READ_FCF:
+ case FCOE_OPCODE_GET_DPORT_RESULTS:
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2957 Handled SLI_CONFIG "
"subsys_fcoe, opcode:x%x\n",
@@ -3717,6 +4379,8 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
nemb_mse, dmabuf);
break;
case FCOE_OPCODE_ADD_FCF:
+ case FCOE_OPCODE_SET_DPORT_MODE:
+ case LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE:
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2958 Handled SLI_CONFIG "
"subsys_fcoe, opcode:x%x\n",
@@ -3726,18 +4390,40 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
break;
default:
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
- "2959 Not handled SLI_CONFIG "
+ "2959 Reject SLI_CONFIG "
"subsys_fcoe, opcode:x%x\n",
opcode);
- rc = SLI_CONFIG_NOT_HANDLED;
+ rc = -EPERM;
+ break;
+ }
+ } else if (subsys == SLI_CONFIG_SUBSYS_COMN) {
+ switch (opcode) {
+ case COMN_OPCODE_GET_CNTL_ADDL_ATTRIBUTES:
+ case COMN_OPCODE_GET_CNTL_ATTRIBUTES:
+ case COMN_OPCODE_GET_PROFILE_CONFIG:
+ case COMN_OPCODE_SET_FEATURES:
+ case COMN_OPCODE_READ_OBJECT:
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3106 Handled SLI_CONFIG "
+ "subsys_comn, opcode:x%x\n",
+ opcode);
+ rc = lpfc_bsg_sli_cfg_read_cmd_ext(phba, job,
+ nemb_mse, dmabuf);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
+ "3107 Reject SLI_CONFIG "
+ "subsys_comn, opcode:x%x\n",
+ opcode);
+ rc = -EPERM;
break;
}
} else {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
- "2977 Handled SLI_CONFIG "
+ "2977 Reject SLI_CONFIG "
"subsys:x%d, opcode:x%x\n",
subsys, opcode);
- rc = SLI_CONFIG_NOT_HANDLED;
+ rc = -EPERM;
}
} else {
subsys = bsg_bf_get(lpfc_emb1_subcmnd_subsys,
@@ -3773,17 +4459,22 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
}
} else {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
- "2978 Handled SLI_CONFIG "
+ "2978 Not handled SLI_CONFIG "
"subsys:x%d, opcode:x%x\n",
subsys, opcode);
rc = SLI_CONFIG_NOT_HANDLED;
}
}
+
+ /* state reset on not handled new multi-buffer mailbox command */
+ if (rc != SLI_CONFIG_HANDLED)
+ phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_IDLE;
+
return rc;
}
/**
- * lpfc_bsg_mbox_ext_abort_req - request to abort mbox command with ext buffers
+ * lpfc_bsg_mbox_ext_abort - request to abort mbox command with ext buffers
* @phba: Pointer to HBA context object.
*
* This routine is for requesting to abort a pass-through mailbox command with
@@ -3802,14 +4493,15 @@ lpfc_bsg_mbox_ext_abort(struct lpfc_hba *phba)
/**
* lpfc_bsg_read_ebuf_get - get the next mailbox read external buffer
* @phba: Pointer to HBA context object.
- * @dmabuf: Pointer to a DMA buffer descriptor.
+ * @job: Pointer to the job object.
*
* This routine extracts the next mailbox read external buffer back to
* user space through BSG.
**/
static int
-lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct fc_bsg_job *job)
+lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct bsg_job *job)
{
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct lpfc_sli_config_mbox *sli_cfg_mbx;
struct lpfc_dmabuf *dmabuf;
uint8_t *pbuf;
@@ -3840,8 +4532,14 @@ lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct fc_bsg_job *job)
dmabuf = list_first_entry(&phba->mbox_ext_buf_ctx.ext_dmabuf_list,
struct lpfc_dmabuf, list);
list_del_init(&dmabuf->list);
+
+ /* after dma buffer descriptor setup */
+ lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType,
+ mbox_rd, dma_ebuf, sta_pos_addr,
+ dmabuf, index);
+
pbuf = (uint8_t *)dmabuf->virt;
- job->reply->reply_payload_rcv_len =
+ bsg_reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt,
pbuf, size);
@@ -3855,8 +4553,9 @@ lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct fc_bsg_job *job)
lpfc_bsg_mbox_ext_session_reset(phba);
}
- job->reply->result = 0;
- job->job_done(job);
+ bsg_reply->result = 0;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return SLI_CONFIG_HANDLED;
}
@@ -3864,16 +4563,17 @@ lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct fc_bsg_job *job)
/**
* lpfc_bsg_write_ebuf_set - set the next mailbox write external buffer
* @phba: Pointer to HBA context object.
+ * @job: Pointer to the job object.
* @dmabuf: Pointer to a DMA buffer descriptor.
*
* This routine sets up the next mailbox read external buffer obtained
* from user space through BSG.
**/
static int
-lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job,
+lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job,
struct lpfc_dmabuf *dmabuf)
{
- struct lpfc_sli_config_mbox *sli_cfg_mbx;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct bsg_job_data *dd_data = NULL;
LPFC_MBOXQ_t *pmboxq = NULL;
MAILBOX_t *pmb;
@@ -3887,15 +4587,6 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job,
phba->mbox_ext_buf_ctx.seqNum++;
nemb_tp = phba->mbox_ext_buf_ctx.nembType;
- sli_cfg_mbx = (struct lpfc_sli_config_mbox *)
- phba->mbox_ext_buf_ctx.mbx_dmabuf->virt;
-
- dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
- if (!dd_data) {
- rc = -ENOMEM;
- goto job_error;
- }
-
pbuf = (uint8_t *)dmabuf->virt;
size = job->request_payload.payload_len;
sg_copy_to_buffer(job->request_payload.sg_list,
@@ -3922,11 +4613,23 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job,
dmabuf);
list_add_tail(&dmabuf->list, &phba->mbox_ext_buf_ctx.ext_dmabuf_list);
+ /* after write dma buffer */
+ lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType,
+ mbox_wr, dma_ebuf, sta_pos_addr,
+ dmabuf, index);
+
if (phba->mbox_ext_buf_ctx.seqNum == phba->mbox_ext_buf_ctx.numBuf) {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2968 SLI_CONFIG ext-buffer wr all %d "
"ebuffers received\n",
phba->mbox_ext_buf_ctx.numBuf);
+
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (!dd_data) {
+ rc = -ENOMEM;
+ goto job_error;
+ }
+
/* mailbox command structure for base driver */
pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmboxq) {
@@ -3944,11 +4647,11 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job,
pmboxq->mbox_cmpl = lpfc_bsg_issue_write_mbox_ext_cmpl;
/* context fields to callback function */
- pmboxq->context1 = dd_data;
+ pmboxq->ctx_u.dd_data = dd_data;
dd_data->type = TYPE_MBOX;
+ dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)pbuf;
- dd_data->context_un.mbox.set_job = job;
job->dd_data = dd_data;
/* state change */
@@ -3958,22 +4661,25 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job,
if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2969 Issued SLI_CONFIG ext-buffer "
- "maibox command, rc:x%x\n", rc);
- return 1;
+ "mailbox command, rc:x%x\n", rc);
+ return SLI_CONFIG_HANDLED;
}
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
"2970 Failed to issue SLI_CONFIG ext-buffer "
- "maibox command, rc:x%x\n", rc);
+ "mailbox command, rc:x%x\n", rc);
rc = -EPIPE;
goto job_error;
}
- /* wait for additoinal external buffers */
- job->reply->result = 0;
- job->job_done(job);
+ /* wait for additional external buffers */
+ bsg_reply->result = 0;
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
return SLI_CONFIG_HANDLED;
job_error:
+ if (pmboxq)
+ mempool_free(pmboxq, phba->mbox_mem_pool);
lpfc_bsg_dma_page_free(phba, dmabuf);
kfree(dd_data);
@@ -3983,14 +4689,14 @@ job_error:
/**
* lpfc_bsg_handle_sli_cfg_ebuf - handle ext buffer with sli-cfg mailbox cmd
* @phba: Pointer to HBA context object.
- * @mb: Pointer to a BSG mailbox object.
- * @dmabuff: Pointer to a DMA buffer descriptor.
+ * @job: Pointer to the job object.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
*
* This routine handles the external buffer with SLI_CONFIG (0x9B) mailbox
* command with multiple non-embedded external buffers.
**/
static int
-lpfc_bsg_handle_sli_cfg_ebuf(struct lpfc_hba *phba, struct fc_bsg_job *job,
+lpfc_bsg_handle_sli_cfg_ebuf(struct lpfc_hba *phba, struct bsg_job *job,
struct lpfc_dmabuf *dmabuf)
{
int rc;
@@ -4028,25 +4734,26 @@ lpfc_bsg_handle_sli_cfg_ebuf(struct lpfc_hba *phba, struct fc_bsg_job *job,
/**
* lpfc_bsg_handle_sli_cfg_ext - handle sli-cfg mailbox with external buffer
* @phba: Pointer to HBA context object.
- * @mb: Pointer to a BSG mailbox object.
- * @dmabuff: Pointer to a DMA buffer descriptor.
+ * @job: Pointer to the job object.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
*
- * This routine checkes and handles non-embedded multi-buffer SLI_CONFIG
+ * This routine checks and handles non-embedded multi-buffer SLI_CONFIG
* (0x9B) mailbox commands and external buffers.
**/
static int
-lpfc_bsg_handle_sli_cfg_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
+lpfc_bsg_handle_sli_cfg_ext(struct lpfc_hba *phba, struct bsg_job *job,
struct lpfc_dmabuf *dmabuf)
{
+ struct fc_bsg_request *bsg_request = job->request;
struct dfc_mbox_req *mbox_req;
- int rc;
+ int rc = SLI_CONFIG_NOT_HANDLED;
mbox_req =
- (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd;
+ (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
/* mbox command with/without single external buffer */
if (mbox_req->extMboxTag == 0 && mbox_req->extSeqNum == 0)
- return SLI_CONFIG_NOT_HANDLED;
+ return rc;
/* mbox command and first external buffer */
if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_IDLE) {
@@ -4101,20 +4808,22 @@ sli_cfg_ext_error:
/**
* lpfc_bsg_issue_mbox - issues a mailbox command on behalf of an app
* @phba: Pointer to HBA context object.
- * @mb: Pointer to a mailbox object.
+ * @job: Pointer to the job object.
* @vport: Pointer to a vport object.
*
* Allocate a tracking object, mailbox command memory, get a mailbox
* from the mailbox pool, copy the caller mailbox command.
*
* If offline and the sli is active we need to poll for the command (port is
- * being reset) and com-plete the job, otherwise issue the mailbox command and
+ * being reset) and complete the job, otherwise issue the mailbox command and
* let our completion handler finish the command.
**/
-static uint32_t
-lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
+static int
+lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct bsg_job *job,
struct lpfc_vport *vport)
{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */
MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */
/* a 4k buffer to hold the mb and extended data from/to the bsg */
@@ -4126,20 +4835,17 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
uint32_t transmit_length, receive_length, mode;
struct lpfc_mbx_sli4_config *sli4_config;
struct lpfc_mbx_nembed_cmd *nembed_sge;
- struct mbox_header *header;
struct ulp_bde64 *bde;
uint8_t *ext = NULL;
int rc = 0;
uint8_t *from;
uint32_t size;
-
/* in case no data is transferred */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
/* sanity check to protect driver */
- if (job->reply_payload.payload_len > BSG_MBOX_SIZE ||
- job->request_payload.payload_len > BSG_MBOX_SIZE) {
+ if (job->request_payload.payload_len > BSG_MBOX_SIZE) {
rc = -ERANGE;
goto job_done;
}
@@ -4148,13 +4854,13 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
* Don't allow mailbox commands to be sent when blocked or when in
* the middle of discovery
*/
- if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
+ if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
rc = -EAGAIN;
goto job_done;
}
mbox_req =
- (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd;
+ (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
/* check if requested extended data lengths are valid */
if ((mbox_req->inExtWLen > BSG_MBOX_SIZE/sizeof(uint32_t)) ||
@@ -4210,6 +4916,19 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
pmb->mbxOwner = OWN_HOST;
pmboxq->vport = vport;
+ /* non-embedded SLI_CONFIG requests already parsed, check others */
+ if (unlikely(job->reply_payload.payload_len > BSG_MBOX_SIZE)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2729 Cmd x%x (x%x/x%x) request has "
+ "out-of-range reply payload length x%x\n",
+ pmb->mbxCommand,
+ lpfc_sli_config_mbox_subsys_get(phba, pmboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, pmboxq),
+ job->reply_payload.payload_len);
+ rc = -ERANGE;
+ goto job_done;
+ }
+
/* If HBA encountered an error attention, allow only DUMP
* or RESTART mailbox commands until the HBA is restarted.
*/
@@ -4225,12 +4944,9 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* extended mailbox commands will need an extended buffer */
if (mbox_req->inExtWLen || mbox_req->outExtWLen) {
- /* any data for the device? */
- if (mbox_req->inExtWLen) {
- from = pmbx;
- ext = from + sizeof(MAILBOX_t);
- }
- pmboxq->context2 = ext;
+ from = pmbx;
+ ext = from + sizeof(MAILBOX_t);
+ pmboxq->ext_buf = ext;
pmboxq->in_ext_byte_len =
mbox_req->inExtWLen * sizeof(uint32_t);
pmboxq->out_ext_byte_len =
@@ -4249,7 +4965,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
* mailbox extension size
*/
if ((transmit_length > receive_length) ||
- (transmit_length > MAILBOX_EXT_SIZE)) {
+ (transmit_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
rc = -ERANGE;
goto job_done;
}
@@ -4272,7 +4988,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* receive length cannot be greater than mailbox
* extension size
*/
- if (receive_length > MAILBOX_EXT_SIZE) {
+ if (receive_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
rc = -ERANGE;
goto job_done;
}
@@ -4285,7 +5001,12 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
+ sizeof(MAILBOX_t));
}
} else if (phba->sli_rev == LPFC_SLI_REV4) {
- if (pmb->mbxCommand == MBX_DUMP_MEMORY) {
+ /* Let type 4 (well known data) through because the data is
+ * returned in varwords[4-8]
+ * otherwise check the recieve length and fetch the buffer addr
+ */
+ if ((pmb->mbxCommand == MBX_DUMP_MEMORY) &&
+ (pmb->un.varDmp.type != DMP_WELL_KNOWN)) {
/* rebuild the command for sli4 using our own buffers
* like we do for biu diags
*/
@@ -4306,7 +5027,8 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
bde = (struct ulp_bde64 *)&pmb->un.varWords[4];
/* bde size cannot be greater than mailbox ext size */
- if (bde->tus.f.bdeSize > MAILBOX_EXT_SIZE) {
+ if (bde->tus.f.bdeSize >
+ BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
rc = -ERANGE;
goto job_done;
}
@@ -4322,8 +5044,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* rebuild the command for sli4 using our
* own buffers like we do for biu diags
*/
- header = (struct mbox_header *)
- &pmb->un.varWords[0];
nembed_sge = (struct lpfc_mbx_nembed_cmd *)
&pmb->un.varWords[0];
receive_length = nembed_sge->sge[0].length;
@@ -4332,7 +5052,8 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
* mailbox extension size
*/
if ((receive_length == 0) ||
- (receive_length > MAILBOX_EXT_SIZE)) {
+ (receive_length >
+ BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
rc = -ERANGE;
goto job_done;
}
@@ -4353,18 +5074,18 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
pmboxq->mbox_cmpl = lpfc_bsg_issue_mbox_cmpl;
/* setup context field to pass wait_queue pointer to wake function */
- pmboxq->context1 = dd_data;
+ pmboxq->ctx_u.dd_data = dd_data;
dd_data->type = TYPE_MBOX;
+ dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx;
- dd_data->context_un.mbox.set_job = job;
dd_data->context_un.mbox.ext = ext;
dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset;
dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen;
dd_data->context_un.mbox.outExtWLen = mbox_req->outExtWLen;
job->dd_data = dd_data;
- if ((vport->fc_flag & FC_OFFLINE_MODE) ||
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag) ||
(!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
if (rc != MBX_SUCCESS) {
@@ -4374,7 +5095,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* job finished, copy the data */
memcpy(pmbx, pmb, sizeof(*pmb));
- job->reply->reply_payload_rcv_len =
+ bsg_reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt,
pmbx, size);
@@ -4403,26 +5124,28 @@ job_cont:
* @job: MBOX fc_bsg_job for LPFC_BSG_VENDOR_MBOX.
**/
static int
-lpfc_bsg_mbox_cmd(struct fc_bsg_job *job)
+lpfc_bsg_mbox_cmd(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
struct lpfc_hba *phba = vport->phba;
struct dfc_mbox_req *mbox_req;
int rc = 0;
/* mix-and-match backward compatibility */
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
if (job->request_len <
sizeof(struct fc_bsg_request) + sizeof(struct dfc_mbox_req)) {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
- "2737 Mix-and-match backward compability "
+ "2737 Mix-and-match backward compatibility "
"between MBOX_REQ old size:%d and "
"new request size:%d\n",
(int)(job->request_len -
sizeof(struct fc_bsg_request)),
(int)sizeof(struct dfc_mbox_req));
mbox_req = (struct dfc_mbox_req *)
- job->request->rqst_data.h_vendor.vendor_cmd;
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
mbox_req->extMboxTag = 0;
mbox_req->extSeqNum = 0;
}
@@ -4431,310 +5154,534 @@ lpfc_bsg_mbox_cmd(struct fc_bsg_job *job)
if (rc == 0) {
/* job done */
- job->reply->result = 0;
+ bsg_reply->result = 0;
job->dd_data = NULL;
- job->job_done(job);
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
} else if (rc == 1)
/* job submitted, will complete later*/
rc = 0; /* return zero, no error */
else {
/* some error occurred */
- job->reply->result = rc;
+ bsg_reply->result = rc;
job->dd_data = NULL;
}
return rc;
}
+static int
+lpfc_forced_link_speed(struct bsg_job *job)
+{
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ struct forced_link_speed_support_reply *forced_reply;
+ int rc = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) +
+ sizeof(struct get_forced_link_speed_support)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "0048 Received FORCED_LINK_SPEED request "
+ "below minimum size\n");
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ forced_reply = (struct forced_link_speed_support_reply *)
+ bsg_reply->reply_data.vendor_reply.vendor_rsp;
+
+ if (job->reply_len < sizeof(*bsg_reply) + sizeof(*forced_reply)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "0049 Received FORCED_LINK_SPEED reply below "
+ "minimum size\n");
+ rc = -EINVAL;
+ goto job_error;
+ }
+
+ forced_reply->supported = test_bit(HBA_FORCED_LINK_SPEED,
+ &phba->hba_flag)
+ ? LPFC_FORCED_LINK_SPEED_SUPPORTED
+ : LPFC_FORCED_LINK_SPEED_NOT_SUPPORTED;
+job_error:
+ bsg_reply->result = rc;
+ if (rc == 0)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ return rc;
+}
+
/**
- * lpfc_bsg_menlo_cmd_cmp - lpfc_menlo_cmd completion handler
+ * lpfc_check_fwlog_support: Check FW log support on the adapter
* @phba: Pointer to HBA context object.
- * @cmdiocbq: Pointer to command iocb.
- * @rspiocbq: Pointer to response iocb.
*
- * This function is the completion handler for iocbs issued using
- * lpfc_menlo_cmd function. This function is called by the
- * ring event handler function without any lock held. This function
- * can be called from both worker thread context and interrupt
- * context. This function also can be called from another thread which
- * cleans up the SLI layer objects.
- * This function copies the contents of the response iocb to the
- * response iocb memory object provided by the caller of
- * lpfc_sli_issue_iocb_wait and then wakes up the thread which
- * sleeps for the iocb completion.
+ * Check if FW Logging support by the adapter
**/
-static void
-lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba,
- struct lpfc_iocbq *cmdiocbq,
- struct lpfc_iocbq *rspiocbq)
+int
+lpfc_check_fwlog_support(struct lpfc_hba *phba)
{
- struct bsg_job_data *dd_data;
- struct fc_bsg_job *job;
- IOCB_t *rsp;
- struct lpfc_dmabuf *bmp;
- struct lpfc_bsg_menlo *menlo;
- unsigned long flags;
- struct menlo_response *menlo_resp;
+ struct lpfc_ras_fwlog *ras_fwlog = NULL;
+
+ ras_fwlog = &phba->ras_fwlog;
+
+ if (!ras_fwlog->ras_hwsupport)
+ return -EACCES;
+ else if (!ras_fwlog->ras_enabled)
+ return -EPERM;
+ else
+ return 0;
+}
+
+/**
+ * lpfc_bsg_get_ras_config: Get RAS configuration settings
+ * @job: fc_bsg_job to handle
+ *
+ * Get RAS configuration values set.
+ **/
+static int
+lpfc_bsg_get_ras_config(struct bsg_job *job)
+{
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_bsg_get_ras_config_reply *ras_reply;
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
int rc = 0;
- spin_lock_irqsave(&phba->ct_ev_lock, flags);
- dd_data = cmdiocbq->context1;
- if (!dd_data) {
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- return;
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) +
+ sizeof(struct lpfc_bsg_ras_req)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "6192 FW_LOG request received "
+ "below minimum size\n");
+ rc = -EINVAL;
+ goto ras_job_error;
}
- menlo = &dd_data->context_un.menlo;
- job = menlo->set_job;
- job->dd_data = NULL; /* so timeout handler does not reply */
+ /* Check FW log status */
+ rc = lpfc_check_fwlog_support(phba);
+ if (rc)
+ goto ras_job_error;
+
+ ras_reply = (struct lpfc_bsg_get_ras_config_reply *)
+ bsg_reply->reply_data.vendor_reply.vendor_rsp;
- spin_lock(&phba->hbalock);
- cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
- if (cmdiocbq->context2 && rspiocbq)
- memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
- &rspiocbq->iocb, sizeof(IOCB_t));
- spin_unlock(&phba->hbalock);
+ /* Current logging state */
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ if (ras_fwlog->state == ACTIVE)
+ ras_reply->state = LPFC_RASLOG_STATE_RUNNING;
+ else
+ ras_reply->state = LPFC_RASLOG_STATE_STOPPED;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
- bmp = menlo->bmp;
- rspiocbq = menlo->rspiocbq;
- rsp = &rspiocbq->iocb;
+ ras_reply->log_level = phba->ras_fwlog.fw_loglevel;
+ ras_reply->log_buff_sz = phba->cfg_ras_fwlog_buffsize;
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ras_job_error:
+ /* make error code available to userspace */
+ bsg_reply->result = rc;
- /* always return the xri, this would be used in the case
- * of a menlo download to allow the data to be sent as a continuation
- * of the exchange.
- */
- menlo_resp = (struct menlo_response *)
- job->reply->reply_data.vendor_reply.vendor_rsp;
- menlo_resp->xri = rsp->ulpContext;
- if (rsp->ulpStatus) {
- if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- switch (rsp->un.ulpWord[4] & 0xff) {
- case IOERR_SEQUENCE_TIMEOUT:
- rc = -ETIMEDOUT;
- break;
- case IOERR_INVALID_RPI:
- rc = -EFAULT;
- break;
- default:
- rc = -EACCES;
- break;
- }
- } else
- rc = -EACCES;
- } else
- job->reply->reply_payload_rcv_len =
- rsp->un.genreq64.bdl.bdeSize;
+ /* complete the job back to userspace */
+ if (!rc)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ return rc;
+}
- lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
- lpfc_sli_release_iocbq(phba, rspiocbq);
- lpfc_sli_release_iocbq(phba, cmdiocbq);
- kfree(bmp);
- kfree(dd_data);
+/**
+ * lpfc_bsg_set_ras_config: Set FW logging parameters
+ * @job: fc_bsg_job to handle
+ *
+ * Set log-level parameters for FW-logging in host memory
+ **/
+static int
+lpfc_bsg_set_ras_config(struct bsg_job *job)
+{
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_bsg_set_ras_config_req *ras_req;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ uint8_t action = 0, log_level = 0;
+ int rc = 0, action_status = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) +
+ sizeof(struct lpfc_bsg_set_ras_config_req)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "6182 Received RAS_LOG request "
+ "below minimum size\n");
+ rc = -EINVAL;
+ goto ras_job_error;
+ }
+
+ /* Check FW log status */
+ rc = lpfc_check_fwlog_support(phba);
+ if (rc)
+ goto ras_job_error;
+
+ ras_req = (struct lpfc_bsg_set_ras_config_req *)
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
+ action = ras_req->action;
+ log_level = ras_req->log_level;
+
+ if (action == LPFC_RASACTION_STOP_LOGGING) {
+ /* Check if already disabled */
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ if (ras_fwlog->state != ACTIVE) {
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+ rc = -ESRCH;
+ goto ras_job_error;
+ }
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+
+ /* Disable logging */
+ lpfc_ras_stop_fwlog(phba);
+ } else {
+ /*action = LPFC_RASACTION_START_LOGGING*/
+
+ /* Even though FW-logging is active re-initialize
+ * FW-logging with new log-level. Return status
+ * "Logging already Running" to caller.
+ **/
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ if (ras_fwlog->state != INACTIVE)
+ action_status = -EINPROGRESS;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+
+ /* Enable logging */
+ rc = lpfc_sli4_ras_fwlog_init(phba, log_level,
+ LPFC_RAS_ENABLE_LOGGING);
+ if (rc) {
+ rc = -EINVAL;
+ goto ras_job_error;
+ }
+
+ /* Check if FW-logging is re-initialized */
+ if (action_status == -EINPROGRESS)
+ rc = action_status;
+ }
+ras_job_error:
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
+
/* complete the job back to userspace */
- job->job_done(job);
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- return;
+ if (!rc)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ return rc;
}
/**
- * lpfc_menlo_cmd - send an ioctl for menlo hardware
+ * lpfc_bsg_get_ras_lwpd: Get log write position data
* @job: fc_bsg_job to handle
*
- * This function issues a gen request 64 CR ioctl for all menlo cmd requests,
- * all the command completions will return the xri for the command.
- * For menlo data requests a gen request 64 CX is used to continue the exchange
- * supplied in the menlo request header xri field.
+ * Get Offset/Wrap count of the log message written
+ * in host memory
**/
static int
-lpfc_menlo_cmd(struct fc_bsg_job *job)
+lpfc_bsg_get_ras_lwpd(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct lpfc_bsg_get_ras_lwpd *ras_reply;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_iocbq *cmdiocbq, *rspiocbq;
- IOCB_t *cmd, *rsp;
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ u32 *lwpd_ptr = NULL;
int rc = 0;
- struct menlo_command *menlo_cmd;
- struct menlo_response *menlo_resp;
- struct lpfc_dmabuf *bmp = NULL;
- int request_nseg;
- int reply_nseg;
- struct scatterlist *sgel = NULL;
- int numbde;
- dma_addr_t busaddr;
- struct bsg_job_data *dd_data;
- struct ulp_bde64 *bpl = NULL;
- /* in case no data is returned return just the return code */
- job->reply->reply_payload_rcv_len = 0;
+ rc = lpfc_check_fwlog_support(phba);
+ if (rc)
+ goto ras_job_error;
if (job->request_len <
sizeof(struct fc_bsg_request) +
- sizeof(struct menlo_command)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2784 Received MENLO_CMD request below "
- "minimum size\n");
- rc = -ERANGE;
- goto no_dd_data;
+ sizeof(struct lpfc_bsg_ras_req)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "6183 Received RAS_LOG request "
+ "below minimum size\n");
+ rc = -EINVAL;
+ goto ras_job_error;
}
- if (job->reply_len <
- sizeof(struct fc_bsg_request) + sizeof(struct menlo_response)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2785 Received MENLO_CMD reply below "
- "minimum size\n");
- rc = -ERANGE;
- goto no_dd_data;
+ ras_reply = (struct lpfc_bsg_get_ras_lwpd *)
+ bsg_reply->reply_data.vendor_reply.vendor_rsp;
+
+ if (!ras_fwlog->lwpd.virt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "6193 Restart FW Logging\n");
+ rc = -EINVAL;
+ goto ras_job_error;
}
- if (!(phba->menlo_flag & HBA_MENLO_SUPPORT)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2786 Adapter does not support menlo "
- "commands\n");
- rc = -EPERM;
- goto no_dd_data;
+ /* Get lwpd offset */
+ lwpd_ptr = (uint32_t *)(ras_fwlog->lwpd.virt);
+ ras_reply->offset = be32_to_cpu(*lwpd_ptr & 0xffffffff);
+
+ /* Get wrap count */
+ ras_reply->wrap_count = be32_to_cpu(*(++lwpd_ptr) & 0xffffffff);
+
+ras_job_error:
+ /* make error code available to userspace */
+ bsg_reply->result = rc;
+
+ /* complete the job back to userspace */
+ if (!rc)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ return rc;
+}
+
+/**
+ * lpfc_bsg_get_ras_fwlog: Read FW log
+ * @job: fc_bsg_job to handle
+ *
+ * Copy the FW log into the passed buffer.
+ **/
+static int
+lpfc_bsg_get_ras_fwlog(struct bsg_job *job)
+{
+ struct Scsi_Host *shost = fc_bsg_to_shost(job);
+ struct lpfc_vport *vport = shost_priv(shost);
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ struct lpfc_bsg_get_fwlog_req *ras_req;
+ u32 rd_offset, rd_index, offset;
+ void *src, *fwlog_buff;
+ struct lpfc_ras_fwlog *ras_fwlog = NULL;
+ struct lpfc_dmabuf *dmabuf, *next;
+ int rc = 0;
+
+ ras_fwlog = &phba->ras_fwlog;
+
+ rc = lpfc_check_fwlog_support(phba);
+ if (rc)
+ goto ras_job_error;
+
+ /* Logging to be stopped before reading */
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ if (ras_fwlog->state == ACTIVE) {
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+ rc = -EINPROGRESS;
+ goto ras_job_error;
}
+ spin_unlock_irq(&phba->ras_fwlog_lock);
- menlo_cmd = (struct menlo_command *)
- job->request->rqst_data.h_vendor.vendor_cmd;
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) +
+ sizeof(struct lpfc_bsg_get_fwlog_req)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "6184 Received RAS_LOG request "
+ "below minimum size\n");
+ rc = -EINVAL;
+ goto ras_job_error;
+ }
- menlo_resp = (struct menlo_response *)
- job->reply->reply_data.vendor_reply.vendor_rsp;
+ ras_req = (struct lpfc_bsg_get_fwlog_req *)
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
+ rd_offset = ras_req->read_offset;
- /* allocate our bsg tracking structure */
- dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
- if (!dd_data) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2787 Failed allocation of dd_data\n");
+ /* Allocate memory to read fw log*/
+ fwlog_buff = vmalloc(ras_req->read_size);
+ if (!fwlog_buff) {
rc = -ENOMEM;
- goto no_dd_data;
+ goto ras_job_error;
}
- bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!bmp) {
- rc = -ENOMEM;
- goto free_dd;
+ rd_index = (rd_offset / LPFC_RAS_MAX_ENTRY_SIZE);
+ offset = (rd_offset % LPFC_RAS_MAX_ENTRY_SIZE);
+
+ list_for_each_entry_safe(dmabuf, next,
+ &ras_fwlog->fwlog_buff_list, list) {
+
+ if (dmabuf->buffer_tag < rd_index)
+ continue;
+
+ src = dmabuf->virt + offset;
+ memcpy(fwlog_buff, src, ras_req->read_size);
+ break;
}
- cmdiocbq = lpfc_sli_get_iocbq(phba);
- if (!cmdiocbq) {
- rc = -ENOMEM;
- goto free_bmp;
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ fwlog_buff, ras_req->read_size);
+
+ vfree(fwlog_buff);
+
+ras_job_error:
+ bsg_reply->result = rc;
+ if (!rc)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+
+ return rc;
+}
+
+static int
+lpfc_get_trunk_info(struct bsg_job *job)
+{
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ struct lpfc_trunk_info *event_reply;
+ int rc = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct get_trunk_info_req)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "2744 Received GET TRUNK _INFO request below "
+ "minimum size\n");
+ rc = -EINVAL;
+ goto job_error;
}
- rspiocbq = lpfc_sli_get_iocbq(phba);
- if (!rspiocbq) {
- rc = -ENOMEM;
- goto free_cmdiocbq;
+ event_reply = (struct lpfc_trunk_info *)
+ bsg_reply->reply_data.vendor_reply.vendor_rsp;
+
+ if (job->reply_len < sizeof(*bsg_reply) + sizeof(*event_reply)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2728 Received GET TRUNK _INFO reply below "
+ "minimum size\n");
+ rc = -EINVAL;
+ goto job_error;
+ }
+ if (event_reply == NULL) {
+ rc = -EINVAL;
+ goto job_error;
}
- rsp = &rspiocbq->iocb;
+ bsg_bf_set(lpfc_trunk_info_link_status, event_reply,
+ (phba->link_state >= LPFC_LINK_UP) ? 1 : 0);
- bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
- if (!bmp->virt) {
+ bsg_bf_set(lpfc_trunk_info_trunk_active0, event_reply,
+ (phba->trunk_link.link0.state == LPFC_LINK_UP) ? 1 : 0);
+
+ bsg_bf_set(lpfc_trunk_info_trunk_active1, event_reply,
+ (phba->trunk_link.link1.state == LPFC_LINK_UP) ? 1 : 0);
+
+ bsg_bf_set(lpfc_trunk_info_trunk_active2, event_reply,
+ (phba->trunk_link.link2.state == LPFC_LINK_UP) ? 1 : 0);
+
+ bsg_bf_set(lpfc_trunk_info_trunk_active3, event_reply,
+ (phba->trunk_link.link3.state == LPFC_LINK_UP) ? 1 : 0);
+
+ bsg_bf_set(lpfc_trunk_info_trunk_config0, event_reply,
+ bf_get(lpfc_conf_trunk_port0, &phba->sli4_hba));
+
+ bsg_bf_set(lpfc_trunk_info_trunk_config1, event_reply,
+ bf_get(lpfc_conf_trunk_port1, &phba->sli4_hba));
+
+ bsg_bf_set(lpfc_trunk_info_trunk_config2, event_reply,
+ bf_get(lpfc_conf_trunk_port2, &phba->sli4_hba));
+
+ bsg_bf_set(lpfc_trunk_info_trunk_config3, event_reply,
+ bf_get(lpfc_conf_trunk_port3, &phba->sli4_hba));
+
+ event_reply->port_speed = phba->sli4_hba.link_state.speed / 1000;
+ event_reply->logical_speed =
+ phba->sli4_hba.link_state.logical_speed / 1000;
+job_error:
+ bsg_reply->result = rc;
+ if (!rc)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ return rc;
+
+}
+
+static int
+lpfc_get_cgnbuf_info(struct bsg_job *job)
+{
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ struct get_cgnbuf_info_req *cgnbuf_req;
+ struct lpfc_cgn_info *cp;
+ uint8_t *cgn_buff;
+ size_t size, cinfosz;
+ int rc = 0;
+
+ if (job->request_len < sizeof(struct fc_bsg_request) +
+ sizeof(struct get_cgnbuf_info_req)) {
rc = -ENOMEM;
- goto free_rspiocbq;
+ goto job_exit;
}
- INIT_LIST_HEAD(&bmp->list);
- bpl = (struct ulp_bde64 *) bmp->virt;
- request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
- busaddr = sg_dma_address(sgel);
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl->tus.f.bdeSize = sg_dma_len(sgel);
- bpl->tus.w = cpu_to_le32(bpl->tus.w);
- bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
- bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
- bpl++;
+ if (!phba->sli4_hba.pc_sli4_params.cmf) {
+ rc = -ENOENT;
+ goto job_exit;
}
- reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
- for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) {
- busaddr = sg_dma_address(sgel);
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
- bpl->tus.f.bdeSize = sg_dma_len(sgel);
- bpl->tus.w = cpu_to_le32(bpl->tus.w);
- bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
- bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
- bpl++;
+ if (!phba->cgn_i || !phba->cgn_i->virt) {
+ rc = -ENOENT;
+ goto job_exit;
}
- cmd = &cmdiocbq->iocb;
- cmd->un.genreq64.bdl.ulpIoTag32 = 0;
- cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
- cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
- cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- cmd->un.genreq64.bdl.bdeSize =
- (request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
- cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
- cmd->un.genreq64.w5.hcsw.Dfctl = 0;
- cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CMD;
- cmd->un.genreq64.w5.hcsw.Type = MENLO_TRANSPORT_TYPE; /* 0xfe */
- cmd->ulpBdeCount = 1;
- cmd->ulpClass = CLASS3;
- cmd->ulpOwner = OWN_CHIP;
- cmd->ulpLe = 1; /* Limited Edition */
- cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
- cmdiocbq->vport = phba->pport;
- /* We want the firmware to timeout before we do */
- cmd->ulpTimeout = MENLO_TIMEOUT - 5;
- cmdiocbq->context3 = bmp;
- cmdiocbq->context2 = rspiocbq;
- cmdiocbq->iocb_cmpl = lpfc_bsg_menlo_cmd_cmp;
- cmdiocbq->context1 = dd_data;
- cmdiocbq->context2 = rspiocbq;
- if (menlo_cmd->cmd == LPFC_BSG_VENDOR_MENLO_CMD) {
- cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
- cmd->ulpPU = MENLO_PU; /* 3 */
- cmd->un.ulpWord[4] = MENLO_DID; /* 0x0000FC0E */
- cmd->ulpContext = MENLO_CONTEXT; /* 0 */
- } else {
- cmd->ulpCommand = CMD_GEN_REQUEST64_CX;
- cmd->ulpPU = 1;
- cmd->un.ulpWord[4] = 0;
- cmd->ulpContext = menlo_cmd->xri;
+ cp = phba->cgn_i->virt;
+ if (cp->cgn_info_version < LPFC_CGN_INFO_V3) {
+ rc = -EPERM;
+ goto job_exit;
}
- dd_data->type = TYPE_MENLO;
- dd_data->context_un.menlo.cmdiocbq = cmdiocbq;
- dd_data->context_un.menlo.rspiocbq = rspiocbq;
- dd_data->context_un.menlo.set_job = job;
- dd_data->context_un.menlo.bmp = bmp;
+ cgnbuf_req = (struct get_cgnbuf_info_req *)
+ bsg_request->rqst_data.h_vendor.vendor_cmd;
- rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq,
- MENLO_TIMEOUT - 5);
- if (rc == IOCB_SUCCESS)
- return 0; /* done for now */
+ /* For reset or size == 0 */
+ bsg_reply->reply_payload_rcv_len = 0;
- /* iocb failed so cleanup */
- pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
- job->request_payload.sg_cnt, DMA_TO_DEVICE);
- pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
- job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (cgnbuf_req->reset == LPFC_BSG_CGN_RESET_STAT) {
+ lpfc_init_congestion_stat(phba);
+ goto job_exit;
+ }
- lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ /* We don't want to include the CRC at the end */
+ cinfosz = sizeof(struct lpfc_cgn_info) - sizeof(uint32_t);
-free_rspiocbq:
- lpfc_sli_release_iocbq(phba, rspiocbq);
-free_cmdiocbq:
- lpfc_sli_release_iocbq(phba, cmdiocbq);
-free_bmp:
- kfree(bmp);
-free_dd:
- kfree(dd_data);
-no_dd_data:
- /* make error code available to userspace */
- job->reply->result = rc;
- job->dd_data = NULL;
+ size = cgnbuf_req->read_size;
+ if (!size)
+ goto job_exit;
+
+ if (size < cinfosz) {
+ /* Just copy back what we can */
+ cinfosz = size;
+ rc = -E2BIG;
+ }
+
+ /* Allocate memory to read congestion info */
+ cgn_buff = vmalloc(cinfosz);
+ if (!cgn_buff) {
+ rc = -ENOMEM;
+ goto job_exit;
+ }
+
+ memcpy(cgn_buff, cp, cinfosz);
+
+ bsg_reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ cgn_buff, cinfosz);
+
+ vfree(cgn_buff);
+
+job_exit:
+ bsg_reply->result = rc;
+ if (!rc)
+ bsg_job_done(job, bsg_reply->result,
+ bsg_reply->reply_payload_rcv_len);
+ else
+ lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
+ "2724 GET CGNBUF error: %d\n", rc);
return rc;
}
@@ -4743,9 +5690,11 @@ no_dd_data:
* @job: fc_bsg_job to handle
**/
static int
-lpfc_bsg_hst_vendor(struct fc_bsg_job *job)
+lpfc_bsg_hst_vendor(struct bsg_job *job)
{
- int command = job->request->rqst_data.h_vendor.vendor_cmd[0];
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
+ int command = bsg_request->rqst_data.h_vendor.vendor_cmd[0];
int rc;
switch (command) {
@@ -4776,15 +5725,32 @@ lpfc_bsg_hst_vendor(struct fc_bsg_job *job)
case LPFC_BSG_VENDOR_MBOX:
rc = lpfc_bsg_mbox_cmd(job);
break;
- case LPFC_BSG_VENDOR_MENLO_CMD:
- case LPFC_BSG_VENDOR_MENLO_DATA:
- rc = lpfc_menlo_cmd(job);
+ case LPFC_BSG_VENDOR_FORCED_LINK_SPEED:
+ rc = lpfc_forced_link_speed(job);
+ break;
+ case LPFC_BSG_VENDOR_RAS_GET_LWPD:
+ rc = lpfc_bsg_get_ras_lwpd(job);
+ break;
+ case LPFC_BSG_VENDOR_RAS_GET_FWLOG:
+ rc = lpfc_bsg_get_ras_fwlog(job);
+ break;
+ case LPFC_BSG_VENDOR_RAS_GET_CONFIG:
+ rc = lpfc_bsg_get_ras_config(job);
+ break;
+ case LPFC_BSG_VENDOR_RAS_SET_CONFIG:
+ rc = lpfc_bsg_set_ras_config(job);
+ break;
+ case LPFC_BSG_VENDOR_GET_TRUNK_INFO:
+ rc = lpfc_get_trunk_info(job);
+ break;
+ case LPFC_BSG_VENDOR_GET_CGNBUF_INFO:
+ rc = lpfc_get_cgnbuf_info(job);
break;
default:
rc = -EINVAL;
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
break;
}
@@ -4793,15 +5759,17 @@ lpfc_bsg_hst_vendor(struct fc_bsg_job *job)
/**
* lpfc_bsg_request - handle a bsg request from the FC transport
- * @job: fc_bsg_job to handle
+ * @job: bsg_job to handle
**/
int
-lpfc_bsg_request(struct fc_bsg_job *job)
+lpfc_bsg_request(struct bsg_job *job)
{
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_bsg_reply *bsg_reply = job->reply;
uint32_t msgcode;
int rc;
- msgcode = job->request->msgcode;
+ msgcode = bsg_request->msgcode;
switch (msgcode) {
case FC_BSG_HST_VENDOR:
rc = lpfc_bsg_hst_vendor(job);
@@ -4814,9 +5782,9 @@ lpfc_bsg_request(struct fc_bsg_job *job)
break;
default:
rc = -EINVAL;
- job->reply->reply_payload_rcv_len = 0;
+ bsg_reply->reply_payload_rcv_len = 0;
/* make error code available to userspace */
- job->reply->result = rc;
+ bsg_reply->result = rc;
break;
}
@@ -4825,81 +5793,89 @@ lpfc_bsg_request(struct fc_bsg_job *job)
/**
* lpfc_bsg_timeout - handle timeout of a bsg request from the FC transport
- * @job: fc_bsg_job that has timed out
+ * @job: bsg_job that has timed out
*
* This function just aborts the job's IOCB. The aborted IOCB will return to
* the waiting function which will handle passing the error back to userspace
**/
int
-lpfc_bsg_timeout(struct fc_bsg_job *job)
+lpfc_bsg_timeout(struct bsg_job *job)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb;
- struct lpfc_bsg_event *evt;
- struct lpfc_bsg_iocb *iocb;
- struct lpfc_bsg_mbox *mbox;
- struct lpfc_bsg_menlo *menlo;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring;
struct bsg_job_data *dd_data;
unsigned long flags;
+ int rc = 0;
+ LIST_HEAD(completions);
+ struct lpfc_iocbq *check_iocb, *next_iocb;
+
+ pring = lpfc_phba_elsring(phba);
+ if (unlikely(!pring))
+ return -EIO;
+
+ /* if job's driver data is NULL, the command completed or is in the
+ * the process of completing. In this case, return status to request
+ * so the timeout is retried. This avoids double completion issues
+ * and the request will be pulled off the timer queue when the
+ * command's completion handler executes. Otherwise, prevent the
+ * command's completion handler from executing the job done callback
+ * and continue processing to abort the outstanding the command.
+ */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = (struct bsg_job_data *)job->dd_data;
- /* timeout and completion crossed paths if no dd_data */
- if (!dd_data) {
+ if (dd_data) {
+ dd_data->set_job = NULL;
+ job->dd_data = NULL;
+ } else {
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- return 0;
+ return -EAGAIN;
}
switch (dd_data->type) {
case TYPE_IOCB:
- iocb = &dd_data->context_un.iocb;
- cmdiocb = iocb->cmdiocbq;
- /* hint to completion handler that the job timed out */
- job->reply->result = -EAGAIN;
+ /* Check to see if IOCB was issued to the port or not. If not,
+ * remove it from the txq queue and call cancel iocbs.
+ * Otherwise, call abort iotag
+ */
+ cmdiocb = dd_data->context_un.iocb.cmdiocbq;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- /* this will call our completion handler */
- spin_lock_irq(&phba->hbalock);
- lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
- spin_unlock_irq(&phba->hbalock);
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+ /* make sure the I/O abort window is still open */
+ if (!(cmdiocb->cmd_flag & LPFC_IO_CMD_OUTSTANDING)) {
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ return -EAGAIN;
+ }
+ list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq,
+ list) {
+ if (check_iocb == cmdiocb) {
+ list_move_tail(&check_iocb->list, &completions);
+ break;
+ }
+ }
+ if (list_empty(&completions))
+ lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb, NULL);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (!list_empty(&completions)) {
+ lpfc_sli_cancel_iocbs(phba, &completions,
+ IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
+ }
break;
+
case TYPE_EVT:
- evt = dd_data->context_un.evt;
- /* this event has no job anymore */
- evt->set_job = NULL;
- job->dd_data = NULL;
- job->reply->reply_payload_rcv_len = 0;
- /* Return -EAGAIN which is our way of signallying the
- * app to retry.
- */
- job->reply->result = -EAGAIN;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- job->job_done(job);
break;
+
case TYPE_MBOX:
- mbox = &dd_data->context_un.mbox;
- /* this mbox has no job anymore */
- mbox->set_job = NULL;
- job->dd_data = NULL;
- job->reply->reply_payload_rcv_len = 0;
- job->reply->result = -EAGAIN;
- /* the mbox completion handler can now be run */
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- job->job_done(job);
+ /* Update the ext buf ctx state if needed */
+
if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT)
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS;
- break;
- case TYPE_MENLO:
- menlo = &dd_data->context_un.menlo;
- cmdiocb = menlo->cmdiocbq;
- /* hint to completion handler that the job timed out */
- job->reply->result = -EAGAIN;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
- /* this will call our completion handler */
- spin_lock_irq(&phba->hbalock);
- lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
- spin_unlock_irq(&phba->hbalock);
break;
default:
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
@@ -4910,5 +5886,5 @@ lpfc_bsg_timeout(struct fc_bsg_job *job)
* otherwise an error message will be displayed on the console
* so always return success (zero)
*/
- return 0;
+ return rc;
}
diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h
index c8c2b47ea886..27e7a033b53d 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.h
+++ b/drivers/scsi/lpfc/lpfc_bsg.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2010 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2010-2015 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -31,10 +33,15 @@
#define LPFC_BSG_VENDOR_DIAG_RUN_LOOPBACK 5
#define LPFC_BSG_VENDOR_GET_MGMT_REV 6
#define LPFC_BSG_VENDOR_MBOX 7
-#define LPFC_BSG_VENDOR_MENLO_CMD 8
-#define LPFC_BSG_VENDOR_MENLO_DATA 9
#define LPFC_BSG_VENDOR_DIAG_MODE_END 10
#define LPFC_BSG_VENDOR_LINK_DIAG_TEST 11
+#define LPFC_BSG_VENDOR_FORCED_LINK_SPEED 14
+#define LPFC_BSG_VENDOR_RAS_GET_LWPD 16
+#define LPFC_BSG_VENDOR_RAS_GET_FWLOG 17
+#define LPFC_BSG_VENDOR_RAS_GET_CONFIG 18
+#define LPFC_BSG_VENDOR_RAS_SET_CONFIG 19
+#define LPFC_BSG_VENDOR_GET_TRUNK_INFO 20
+#define LPFC_BSG_VENDOR_GET_CGNBUF_INFO 21
struct set_ct_event {
uint32_t command;
@@ -60,6 +67,7 @@ struct send_mgmt_resp {
};
+#define DISABLE_LOOP_BACK 0x0 /* disables loop back */
#define INTERNAL_LOOP_BACK 0x1 /* adapter short cuts the loop internally */
#define EXTERNAL_LOOP_BACK 0x2 /* requires an external loopback plug */
@@ -67,6 +75,7 @@ struct diag_mode_set {
uint32_t command;
uint32_t type;
uint32_t timeout;
+ uint32_t physical_link;
};
struct sli4_link_diag {
@@ -96,7 +105,7 @@ struct get_mgmt_rev {
};
#define MANAGEMENT_MAJOR_REV 1
-#define MANAGEMENT_MINOR_REV 0
+#define MANAGEMENT_MINOR_REV 1
/* the MgmtRevInfo structure */
struct MgmtRevInfo {
@@ -120,16 +129,6 @@ struct dfc_mbox_req {
uint32_t extSeqNum;
};
-/* Used for menlo command or menlo data. The xri is only used for menlo data */
-struct menlo_command {
- uint32_t cmd;
- uint32_t xri;
-};
-
-struct menlo_response {
- uint32_t xri; /* return the xri of the iocb exchange */
-};
-
/*
* macros and data structures for handling sli-config mailbox command
* pass-through support, this header file is shared between user and
@@ -215,6 +214,10 @@ struct lpfc_sli_config_hdr {
uint32_t reserved5;
};
+#define LPFC_CSF_BOOT_DEV 0x1D
+#define LPFC_CSF_QUERY 0
+#define LPFC_CSF_SAVE 1
+
struct lpfc_sli_config_emb0_subsys {
struct lpfc_sli_config_hdr sli_config_hdr;
#define LPFC_MBX_SLI_CONFIG_MAX_MSE 19
@@ -231,6 +234,32 @@ struct lpfc_sli_config_emb0_subsys {
#define SLI_CONFIG_SUBSYS_FCOE 0x0C
#define FCOE_OPCODE_READ_FCF 0x08
#define FCOE_OPCODE_ADD_FCF 0x09
+#define FCOE_OPCODE_SET_DPORT_MODE 0x27
+#define FCOE_OPCODE_GET_DPORT_RESULTS 0x28
+ uint32_t timeout; /* comn_set_feature timeout */
+ uint32_t request_length; /* comn_set_feature request len */
+ uint32_t version; /* comn_set_feature version */
+ uint32_t word68; /* comn_set_feature feature */
+#define lpfc_emb0_subcmnd_csf_feat_SHIFT 0
+#define lpfc_emb0_subcmnd_csf_feat_MASK 0xffffffff
+#define lpfc_emb0_subcmnd_csf_feat_WORD word68
+#define lpfc_emb0_subcmnd_rd_obj_des_rd_len_SHIFT 0
+#define lpfc_emb0_subcmnd_rd_obj_des_rd_len_MASK 0x00ffffff
+#define lpfc_emb0_subcmnd_rd_obj_des_rd_len_WORD word68
+ uint32_t word69; /* comn_set_feature parameter len */
+ uint32_t word70; /* comn_set_feature parameter val0 */
+#define lpfc_emb0_subcmnd_csf_p0_SHIFT 0
+#define lpfc_emb0_subcmnd_csf_p0_MASK 0x3
+#define lpfc_emb0_subcmnd_csf_p0_WORD word70
+ uint32_t reserved71[25];
+ uint32_t word96; /* rd_obj hbd_count */
+#define lpfc_emb0_subcmnd_rd_obj_hbd_cnt_SHIFT 0
+#define lpfc_emb0_subcmnd_rd_obj_hbd_cnt_MASK 0xffffffff
+#define lpfc_emb0_subcmnd_rd_obj_hbd_cnt_WORD word96
+#define LPFC_EMB0_MAX_RD_OBJ_HBD_CNT 31
+ struct lpfc_sli_config_hbd hbd[LPFC_EMB0_MAX_RD_OBJ_HBD_CNT];
+ uint32_t word190;
+ uint32_t word191;
};
struct lpfc_sli_config_emb1_subsys {
@@ -244,10 +273,14 @@ struct lpfc_sli_config_emb1_subsys {
#define lpfc_emb1_subcmnd_subsys_WORD word6
/* Subsystem COMN (0x01) OpCodes */
#define SLI_CONFIG_SUBSYS_COMN 0x01
+#define COMN_OPCODE_GET_PROFILE_CONFIG 0xA4
#define COMN_OPCODE_READ_OBJECT 0xAB
#define COMN_OPCODE_WRITE_OBJECT 0xAC
#define COMN_OPCODE_READ_OBJECT_LIST 0xAD
#define COMN_OPCODE_DELETE_OBJECT 0xAE
+#define COMN_OPCODE_SET_FEATURES 0xBF
+#define COMN_OPCODE_GET_CNTL_ADDL_ATTRIBUTES 0x79
+#define COMN_OPCODE_GET_CNTL_ATTRIBUTES 0x20
uint32_t timeout;
uint32_t request_length;
uint32_t word9;
@@ -279,6 +312,91 @@ struct lpfc_sli_config_mbox {
} un;
};
+#define LPFC_FORCED_LINK_SPEED_NOT_SUPPORTED 0
+#define LPFC_FORCED_LINK_SPEED_SUPPORTED 1
+struct get_forced_link_speed_support {
+ uint32_t command;
+};
+struct forced_link_speed_support_reply {
+ uint8_t supported;
+};
+
+struct lpfc_bsg_ras_req {
+ uint32_t command;
+};
+
+struct lpfc_bsg_get_fwlog_req {
+ uint32_t command;
+ uint32_t read_size;
+ uint32_t read_offset;
+};
+
+struct lpfc_bsg_get_ras_lwpd {
+ uint32_t offset;
+ uint32_t wrap_count;
+};
+
+struct lpfc_bsg_set_ras_config_req {
+ uint32_t command;
+ uint8_t action;
+#define LPFC_RASACTION_STOP_LOGGING 0x00
+#define LPFC_RASACTION_START_LOGGING 0x01
+ uint8_t log_level;
+};
+
+struct lpfc_bsg_get_ras_config_reply {
+ uint8_t state;
+#define LPFC_RASLOG_STATE_STOPPED 0x00
+#define LPFC_RASLOG_STATE_RUNNING 0x01
+ uint8_t log_level;
+ uint32_t log_buff_sz;
+};
+
+struct lpfc_trunk_info {
+ uint32_t word0;
+#define lpfc_trunk_info_link_status_SHIFT 0
+#define lpfc_trunk_info_link_status_MASK 1
+#define lpfc_trunk_info_link_status_WORD word0
+#define lpfc_trunk_info_trunk_active0_SHIFT 8
+#define lpfc_trunk_info_trunk_active0_MASK 1
+#define lpfc_trunk_info_trunk_active0_WORD word0
+#define lpfc_trunk_info_trunk_active1_SHIFT 9
+#define lpfc_trunk_info_trunk_active1_MASK 1
+#define lpfc_trunk_info_trunk_active1_WORD word0
+#define lpfc_trunk_info_trunk_active2_SHIFT 10
+#define lpfc_trunk_info_trunk_active2_MASK 1
+#define lpfc_trunk_info_trunk_active2_WORD word0
+#define lpfc_trunk_info_trunk_active3_SHIFT 11
+#define lpfc_trunk_info_trunk_active3_MASK 1
+#define lpfc_trunk_info_trunk_active3_WORD word0
+#define lpfc_trunk_info_trunk_config0_SHIFT 12
+#define lpfc_trunk_info_trunk_config0_MASK 1
+#define lpfc_trunk_info_trunk_config0_WORD word0
+#define lpfc_trunk_info_trunk_config1_SHIFT 13
+#define lpfc_trunk_info_trunk_config1_MASK 1
+#define lpfc_trunk_info_trunk_config1_WORD word0
+#define lpfc_trunk_info_trunk_config2_SHIFT 14
+#define lpfc_trunk_info_trunk_config2_MASK 1
+#define lpfc_trunk_info_trunk_config2_WORD word0
+#define lpfc_trunk_info_trunk_config3_SHIFT 15
+#define lpfc_trunk_info_trunk_config3_MASK 1
+#define lpfc_trunk_info_trunk_config3_WORD word0
+ uint16_t port_speed;
+ uint16_t logical_speed;
+ uint32_t reserved3;
+};
+
+struct get_trunk_info_req {
+ uint32_t command;
+};
+
+struct get_cgnbuf_info_req {
+ uint32_t command;
+ uint32_t read_size;
+ uint32_t reset;
+#define LPFC_BSG_CGN_RESET_STAT 1
+};
+
/* driver only */
#define SLI_CONFIG_NOT_HANDLED 0
#define SLI_CONFIG_HANDLED 1
diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h
index 75e2e569dede..43cf46a3a71f 100644
--- a/drivers/scsi/lpfc/lpfc_compat.h
+++ b/drivers/scsi/lpfc/lpfc_compat.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2005 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -82,7 +84,8 @@ lpfc_memcpy_from_slim( void *dest, void __iomem *src, unsigned int bytes)
static inline void
lpfc_memcpy_to_slim( void __iomem *dest, void *src, unsigned int bytes)
{
- __iowrite32_copy(dest, src, bytes);
+ /* convert bytes in argument list to word count for copy function */
+ __iowrite32_copy(dest, src, bytes / sizeof(uint32_t));
}
static inline void
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index fc20c247f36b..efeb61b15a5b 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -21,15 +23,18 @@
typedef int (*node_filter)(struct lpfc_nodelist *, void *);
struct fc_rport;
+struct fc_frame_header;
void lpfc_down_link(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_sli_read_link_ste(struct lpfc_hba *);
void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t, uint16_t);
void lpfc_dump_wakeup_param(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_dump_static_vport(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
-int lpfc_dump_fcoe_param(struct lpfc_hba *, struct lpfcMboxq *);
+int lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_config_async(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
-
+int lpfc_mbox_rsrc_prep(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox);
+void lpfc_mbox_rsrc_cleanup(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox,
+ enum lpfc_mbox_ctx locked);
void lpfc_heart_beat(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_read_topology(struct lpfc_hba *, LPFC_MBOXQ_t *, struct lpfc_dmabuf *);
void lpfc_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
@@ -52,12 +57,11 @@ void lpfc_register_new_vport(struct lpfc_hba *, struct lpfc_vport *,
void lpfc_unreg_vpi(struct lpfc_hba *, uint16_t, LPFC_MBOXQ_t *);
void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *);
-void lpfc_supported_pages(struct lpfcMboxq *);
-void lpfc_pc_sli4_params(struct lpfcMboxq *);
-int lpfc_pc_sli4_params_get(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_sli4_mbox_rsrc_extent(struct lpfc_hba *, struct lpfcMboxq *,
uint16_t, uint16_t, bool);
int lpfc_get_sli4_parameters(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_reg_congestion_buf(struct lpfc_hba *phba);
+int lpfc_unreg_congestion_buf(struct lpfc_hba *phba);
struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t);
void lpfc_cleanup_rcv_buffers(struct lpfc_vport *);
void lpfc_rcv_seq_check_edtov(struct lpfc_vport *);
@@ -70,19 +74,48 @@ void lpfc_mbx_cmpl_read_topology(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_init_vpi_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_cancel_all_vport_retry_delay_timer(struct lpfc_hba *);
void lpfc_retry_pport_discovery(struct lpfc_hba *);
-void lpfc_release_rpi(struct lpfc_hba *, struct lpfc_vport *, uint16_t);
-
+int lpfc_init_iocb_list(struct lpfc_hba *phba, int cnt);
+void lpfc_free_iocb_list(struct lpfc_hba *phba);
+int lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq,
+ struct lpfc_queue *drq, int count, int idx);
+int lpfc_read_lds_params(struct lpfc_hba *phba);
+uint32_t lpfc_calc_cmf_latency(struct lpfc_hba *phba);
+void lpfc_cmf_signal_init(struct lpfc_hba *phba);
+void lpfc_cmf_start(struct lpfc_hba *phba);
+void lpfc_cmf_stop(struct lpfc_hba *phba);
+void lpfc_init_congestion_stat(struct lpfc_hba *phba);
+void lpfc_init_congestion_buf(struct lpfc_hba *phba);
+int lpfc_sli4_cgn_params_read(struct lpfc_hba *phba);
+uint32_t lpfc_cgn_calc_crc32(void *bufp, uint32_t sz, uint32_t seed);
+int lpfc_config_cgn_signal(struct lpfc_hba *phba);
+int lpfc_issue_cmf_sync_wqe(struct lpfc_hba *phba, u32 ms, u64 total);
+void lpfc_cgn_dump_rxmonitor(struct lpfc_hba *phba);
+void lpfc_cgn_update_stat(struct lpfc_hba *phba, uint32_t dtag);
+void lpfc_unblock_requests(struct lpfc_hba *phba);
+void lpfc_block_requests(struct lpfc_hba *phba);
+int lpfc_rx_monitor_create_ring(struct lpfc_rx_info_monitor *rx_monitor,
+ u32 entries);
+void lpfc_rx_monitor_destroy_ring(struct lpfc_rx_info_monitor *rx_monitor);
+void lpfc_rx_monitor_record(struct lpfc_rx_info_monitor *rx_monitor,
+ struct rx_info_entry *entry);
+u32 lpfc_rx_monitor_report(struct lpfc_hba *phba,
+ struct lpfc_rx_info_monitor *rx_monitor, char *buf,
+ u32 buf_len, u32 max_read_entries);
+
+void lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_fc_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb);
void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_unregister_vfi_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_enqueue_node(struct lpfc_vport *, struct lpfc_nodelist *);
void lpfc_dequeue_node(struct lpfc_vport *, struct lpfc_nodelist *);
-struct lpfc_nodelist *lpfc_enable_node(struct lpfc_vport *,
- struct lpfc_nodelist *, int);
void lpfc_nlp_set_state(struct lpfc_vport *, struct lpfc_nodelist *, int);
+void lpfc_nlp_reg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp);
+void lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp);
void lpfc_drop_node(struct lpfc_vport *, struct lpfc_nodelist *);
void lpfc_set_disctmo(struct lpfc_vport *);
int lpfc_can_disctmo(struct lpfc_vport *);
@@ -94,19 +127,24 @@ void lpfc_issue_reg_vpi(struct lpfc_hba *, struct lpfc_vport *);
int lpfc_check_sli_ndlp(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *, struct lpfc_nodelist *);
-void lpfc_nlp_init(struct lpfc_vport *, struct lpfc_nodelist *, uint32_t);
+struct lpfc_nodelist *lpfc_nlp_init(struct lpfc_vport *vport, uint32_t did);
struct lpfc_nodelist *lpfc_nlp_get(struct lpfc_nodelist *);
int lpfc_nlp_put(struct lpfc_nodelist *);
-int lpfc_nlp_not_used(struct lpfc_nodelist *ndlp);
+void lpfc_check_nlp_post_devloss(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp);
+void lpfc_ignore_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb);
struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_vport *, uint32_t);
void lpfc_disc_list_loopmap(struct lpfc_vport *);
void lpfc_disc_start(struct lpfc_vport *);
void lpfc_cleanup_discovery_resources(struct lpfc_vport *);
void lpfc_cleanup(struct lpfc_vport *);
-void lpfc_disc_timeout(unsigned long);
+void lpfc_prep_embed_io(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd);
+void lpfc_disc_timeout(struct timer_list *);
+int lpfc_unregister_fcf_prep(struct lpfc_hba *);
struct lpfc_nodelist *__lpfc_findnode_rpi(struct lpfc_vport *, uint16_t);
-
+struct lpfc_nodelist *lpfc_findnode_rpi(struct lpfc_vport *, uint16_t);
void lpfc_worker_wake_up(struct lpfc_hba *);
int lpfc_workq_post_event(struct lpfc_hba *, void *, void *, uint32_t);
int lpfc_do_work(void *);
@@ -116,7 +154,7 @@ int lpfc_disc_state_machine(struct lpfc_vport *, struct lpfc_nodelist *, void *,
void lpfc_do_scr_ns_plogi(struct lpfc_hba *, struct lpfc_vport *);
int lpfc_check_sparm(struct lpfc_vport *, struct lpfc_nodelist *,
struct serv_parm *, uint32_t, int);
-int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist *);
+void lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist *);
void lpfc_more_plogi(struct lpfc_vport *);
void lpfc_more_adisc(struct lpfc_vport *);
void lpfc_end_rscn(struct lpfc_vport *);
@@ -130,8 +168,12 @@ int lpfc_issue_els_prli(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
int lpfc_issue_els_adisc(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
int lpfc_issue_els_logo(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
int lpfc_issue_els_npiv_logo(struct lpfc_vport *, struct lpfc_nodelist *);
-int lpfc_issue_els_scr(struct lpfc_vport *, uint32_t, uint8_t);
+int lpfc_issue_els_scr(struct lpfc_vport *vport, uint8_t retry);
+int lpfc_issue_els_rscn(struct lpfc_vport *vport, uint8_t retry);
int lpfc_issue_fabric_reglogin(struct lpfc_vport *);
+int lpfc_issue_els_rdf(struct lpfc_vport *vport, uint8_t retry);
+int lpfc_issue_els_edc(struct lpfc_vport *vport, uint8_t retry);
+void lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length);
int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
int lpfc_ct_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
int lpfc_els_rsp_acc(struct lpfc_vport *, uint32_t, struct lpfc_iocbq *,
@@ -143,7 +185,7 @@ int lpfc_els_rsp_adisc_acc(struct lpfc_vport *, struct lpfc_iocbq *,
int lpfc_els_rsp_prli_acc(struct lpfc_vport *, struct lpfc_iocbq *,
struct lpfc_nodelist *);
void lpfc_cancel_retry_delay_tmo(struct lpfc_vport *, struct lpfc_nodelist *);
-void lpfc_els_retry_delay(unsigned long);
+void lpfc_els_retry_delay(struct timer_list *);
void lpfc_els_retry_delay_handler(struct lpfc_nodelist *);
void lpfc_els_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
@@ -154,40 +196,49 @@ void lpfc_els_flush_all_cmd(struct lpfc_hba *);
void lpfc_els_flush_cmd(struct lpfc_vport *);
int lpfc_els_disc_adisc(struct lpfc_vport *);
int lpfc_els_disc_plogi(struct lpfc_vport *);
-void lpfc_els_timeout(unsigned long);
+void lpfc_els_timeout(struct timer_list *);
void lpfc_els_timeout_handler(struct lpfc_vport *);
struct lpfc_iocbq *lpfc_prep_els_iocb(struct lpfc_vport *, uint8_t, uint16_t,
uint8_t, struct lpfc_nodelist *,
uint32_t, uint32_t);
+void lpfc_sli_prep_wqe(struct lpfc_hba *phba, struct lpfc_iocbq *job);
void lpfc_hb_timeout_handler(struct lpfc_hba *);
void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
-void lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
- struct lpfc_iocbq *);
+int lpfc_ct_handle_unsol_abort(struct lpfc_hba *, struct hbq_dmabuf *);
+int lpfc_issue_gidpt(struct lpfc_vport *vport);
+int lpfc_issue_gidft(struct lpfc_vport *vport);
+int lpfc_get_gidft_type(struct lpfc_vport *vport, struct lpfc_iocbq *iocbq);
int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t);
-int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int);
-void lpfc_fdmi_tmo(unsigned long);
-void lpfc_fdmi_timeout_handler(struct lpfc_vport *);
-void lpfc_delayed_disc_tmo(unsigned long);
+int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int, uint32_t);
+void lpfc_fdmi_change_check(struct lpfc_vport *vport);
+void lpfc_delayed_disc_tmo(struct timer_list *);
void lpfc_delayed_disc_timeout_handler(struct lpfc_vport *);
int lpfc_config_port_prep(struct lpfc_hba *);
void lpfc_update_vport_wwn(struct lpfc_vport *vport);
int lpfc_config_port_post(struct lpfc_hba *);
+int lpfc_sli4_refresh_params(struct lpfc_hba *phba);
int lpfc_hba_down_prep(struct lpfc_hba *);
int lpfc_hba_down_post(struct lpfc_hba *);
void lpfc_hba_init(struct lpfc_hba *, uint32_t *);
-int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int);
+int lpfc_sli3_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt);
void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int);
int lpfc_online(struct lpfc_hba *);
void lpfc_unblock_mgmt_io(struct lpfc_hba *);
-void lpfc_offline_prep(struct lpfc_hba *);
+void lpfc_offline_prep(struct lpfc_hba *, int);
void lpfc_offline(struct lpfc_hba *);
void lpfc_reset_hba(struct lpfc_hba *);
+int lpfc_emptyq_wait(struct lpfc_hba *phba, struct list_head *hd,
+ spinlock_t *slock);
int lpfc_sli_setup(struct lpfc_hba *);
-int lpfc_sli_queue_setup(struct lpfc_hba *);
+int lpfc_sli4_setup(struct lpfc_hba *phba);
+void lpfc_sli_queue_init(struct lpfc_hba *phba);
+void lpfc_sli4_queue_init(struct lpfc_hba *phba);
+struct lpfc_sli_ring *lpfc_sli4_calc_ring(struct lpfc_hba *phba,
+ struct lpfc_iocbq *iocbq);
void lpfc_handle_eratt(struct lpfc_hba *);
void lpfc_handle_latt(struct lpfc_hba *);
@@ -195,8 +246,16 @@ irqreturn_t lpfc_sli_intr_handler(int, void *);
irqreturn_t lpfc_sli_sp_intr_handler(int, void *);
irqreturn_t lpfc_sli_fp_intr_handler(int, void *);
irqreturn_t lpfc_sli4_intr_handler(int, void *);
-irqreturn_t lpfc_sli4_sp_intr_handler(int, void *);
-irqreturn_t lpfc_sli4_fp_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_hba_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_hba_intr_handler_th(int irq, void *dev_id);
+
+int lpfc_read_object(struct lpfc_hba *phba, char *s, uint32_t *datap,
+ uint32_t len);
+
+void lpfc_sli4_cleanup_poll_list(struct lpfc_hba *phba);
+void lpfc_sli4_poll_hbtimer(struct timer_list *t);
+void lpfc_sli4_start_polling(struct lpfc_queue *q);
+void lpfc_sli4_stop_polling(struct lpfc_queue *q);
void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_sli4_swap_str(struct lpfc_hba *, LPFC_MBOXQ_t *);
@@ -209,12 +268,13 @@ void __lpfc_mbox_cmpl_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbox_cmpl_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_mbox_cmd_check(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_mbox_dev_check(struct lpfc_hba *);
-int lpfc_mbox_tmo_val(struct lpfc_hba *, int);
+int lpfc_mbox_tmo_val(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_init_vfi(struct lpfcMboxq *, struct lpfc_vport *);
void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t);
void lpfc_init_vpi(struct lpfc_hba *, struct lpfcMboxq *, uint16_t);
void lpfc_unreg_vfi(struct lpfcMboxq *, struct lpfc_vport *);
void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *);
+void lpfc_reg_fcfi_mrq(struct lpfc_hba *phba, struct lpfcMboxq *mbox, int mode);
void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t);
void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *);
int lpfc_check_pending_fcoe_event(struct lpfc_hba *, uint8_t);
@@ -226,8 +286,19 @@ struct hbq_dmabuf *lpfc_els_hbq_alloc(struct lpfc_hba *);
void lpfc_els_hbq_free(struct lpfc_hba *, struct hbq_dmabuf *);
struct hbq_dmabuf *lpfc_sli4_rb_alloc(struct lpfc_hba *);
void lpfc_sli4_rb_free(struct lpfc_hba *, struct hbq_dmabuf *);
+struct rqb_dmabuf *lpfc_sli4_nvmet_alloc(struct lpfc_hba *phba);
+void lpfc_sli4_nvmet_free(struct lpfc_hba *phba, struct rqb_dmabuf *dmab);
+void lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba,
+ struct lpfc_nvmet_ctxbuf *ctxp);
+int lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
+ struct fc_frame_header *fc_hdr);
+void lpfc_nvmet_wqfull_process(struct lpfc_hba *phba, struct lpfc_queue *wq);
+void lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba);
void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *,
uint16_t);
+int lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
+ struct lpfc_rqe *hrqe, struct lpfc_rqe *drqe);
+int lpfc_free_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hq);
void lpfc_unregister_fcf(struct lpfc_hba *);
void lpfc_unregister_fcf_rescan(struct lpfc_hba *);
void lpfc_unregister_unused_fcf(struct lpfc_hba *);
@@ -235,22 +306,27 @@ int lpfc_sli4_redisc_fcf_table(struct lpfc_hba *);
void lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *);
void lpfc_sli4_fcf_dead_failthrough(struct lpfc_hba *);
uint16_t lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *);
+void lpfc_sli4_set_fcf_flogi_fail(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *, uint16_t);
void lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *, uint16_t);
+void lpfc_sli4_clear_fcf_rr_bmask(struct lpfc_hba *);
int lpfc_mem_alloc(struct lpfc_hba *, int align);
+int lpfc_nvmet_mem_alloc(struct lpfc_hba *phba);
+int lpfc_mem_alloc_active_rrq_pool_s4(struct lpfc_hba *);
void lpfc_mem_free(struct lpfc_hba *);
void lpfc_mem_free_all(struct lpfc_hba *);
void lpfc_stop_vport_timers(struct lpfc_vport *);
-void lpfc_poll_timeout(unsigned long ptr);
+void lpfc_poll_timeout(struct timer_list *t);
void lpfc_poll_start_timer(struct lpfc_hba *);
-void lpfc_poll_eratt(unsigned long);
+void lpfc_poll_eratt(struct timer_list *);
int
lpfc_sli_handle_fast_ring_event(struct lpfc_hba *,
struct lpfc_sli_ring *, uint32_t);
+struct lpfc_iocbq *__lpfc_sli_get_iocbq(struct lpfc_hba *);
struct lpfc_iocbq * lpfc_sli_get_iocbq(struct lpfc_hba *);
void lpfc_sli_release_iocbq(struct lpfc_hba *, struct lpfc_iocbq *);
uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *);
@@ -261,6 +337,7 @@ int lpfc_selective_reset(struct lpfc_hba *);
void lpfc_reset_barrier(struct lpfc_hba *);
int lpfc_sli_brdready(struct lpfc_hba *, uint32_t);
int lpfc_sli_brdkill(struct lpfc_hba *);
+int lpfc_sli_chipset_init(struct lpfc_hba *phba);
int lpfc_sli_brdreset(struct lpfc_hba *);
int lpfc_sli_brdrestart(struct lpfc_hba *);
int lpfc_sli_hba_setup(struct lpfc_hba *);
@@ -269,19 +346,48 @@ int lpfc_sli_host_down(struct lpfc_vport *);
int lpfc_sli_hba_down(struct lpfc_hba *);
int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
int lpfc_sli_handle_mb_event(struct lpfc_hba *);
-void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *);
+void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *, int);
int lpfc_sli_check_eratt(struct lpfc_hba *);
void lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
struct lpfc_sli_ring *, uint32_t);
void lpfc_sli4_handle_received_buffer(struct lpfc_hba *, struct hbq_dmabuf *);
+void lpfc_sli4_seq_abort_rsp(struct lpfc_vport *vport,
+ struct fc_frame_header *fc_hdr, bool aborted);
void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, uint32_t);
+int lpfc_sli_issue_fcp_io(struct lpfc_hba *phba, uint32_t ring_number,
+ struct lpfc_iocbq *piocb, uint32_t flag);
+int lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp,
+ struct lpfc_iocbq *pwqe);
+int lpfc_sli4_issue_abort_iotag(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocb, void *cmpl);
+void lpfc_sli_prep_els_req_rsp(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_vport *vport,
+ struct lpfc_dmabuf *bmp, u16 cmd_size, u32 did,
+ u32 elscmd, u8 tmo, u8 expect_rsp);
+void lpfc_sli_prep_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi, u32 num_entry,
+ u8 tmo);
+void lpfc_sli_prep_xmit_seq64(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi, u16 ox_id,
+ u32 num_entry, u8 rctl, u8 last_seq,
+ u8 cr_cx_cmd);
+void lpfc_sli_prep_abort_xri(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq,
+ u16 ulp_context, u16 iotag, u8 ulp_class, u16 cqid,
+ bool ia, bool wqec);
+struct lpfc_sglq *__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xri);
+struct lpfc_sglq *__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba,
+ struct lpfc_iocbq *piocbq);
void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t);
void lpfc_sli_bemem_bcopy(void *, void *, uint32_t);
void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *);
+void lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba);
void lpfc_sli_hba_iocb_abort(struct lpfc_hba *);
-void lpfc_sli_flush_fcp_rings(struct lpfc_hba *);
+void lpfc_sli_flush_io_rings(struct lpfc_hba *phba);
int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_dmabuf *);
struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *,
@@ -297,17 +403,23 @@ int lpfc_sli_hbqbuf_add_hbqs(struct lpfc_hba *, uint32_t);
void lpfc_sli_hbqbuf_free_all(struct lpfc_hba *);
int lpfc_sli_hbq_size(void);
int lpfc_sli_issue_abort_iotag(struct lpfc_hba *, struct lpfc_sli_ring *,
- struct lpfc_iocbq *);
+ struct lpfc_iocbq *, void *);
int lpfc_sli_sum_iocb(struct lpfc_vport *, uint16_t, uint64_t, lpfc_ctx_cmd);
-int lpfc_sli_abort_iocb(struct lpfc_vport *, struct lpfc_sli_ring *, uint16_t,
- uint64_t, lpfc_ctx_cmd);
+int lpfc_sli_abort_iocb(struct lpfc_vport *vport, u16 tgt_id, u64 lun_id,
+ lpfc_ctx_cmd abort_cmd);
+int
+lpfc_sli_abort_taskmgmt(struct lpfc_vport *, struct lpfc_sli_ring *,
+ uint16_t, uint64_t, lpfc_ctx_cmd);
-void lpfc_mbox_timeout(unsigned long);
+void lpfc_mbox_timeout(struct timer_list *t);
void lpfc_mbox_timeout_handler(struct lpfc_hba *);
+int lpfc_issue_hb_mbox(struct lpfc_hba *phba);
+void lpfc_issue_hb_tmo(struct lpfc_hba *phba);
struct lpfc_nodelist *lpfc_findnode_did(struct lpfc_vport *, uint32_t);
struct lpfc_nodelist *lpfc_findnode_wwpn(struct lpfc_vport *,
struct lpfc_name *);
+struct lpfc_nodelist *lpfc_findnode_mapped(struct lpfc_vport *vport);
int lpfc_sli_issue_mbox_wait(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
@@ -322,9 +434,17 @@ void lpfc_sli_free_hbq(struct lpfc_hba *, struct hbq_dmabuf *);
void *lpfc_mbuf_alloc(struct lpfc_hba *, int, dma_addr_t *);
void __lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t);
void lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t);
+void *lpfc_nvmet_buf_alloc(struct lpfc_hba *phba, int flags,
+ dma_addr_t *handle);
+void lpfc_nvmet_buf_free(struct lpfc_hba *phba, void *virtp, dma_addr_t dma);
void lpfc_in_buf_free(struct lpfc_hba *, struct lpfc_dmabuf *);
+void lpfc_rq_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp);
+void lpfc_setup_fdmi_mask(struct lpfc_vport *vport);
+int lpfc_link_reset(struct lpfc_vport *vport);
+
/* Function prototypes. */
+int lpfc_check_pci_resettable(struct lpfc_hba *phba);
const char* lpfc_info(struct Scsi_Host *);
int lpfc_scan_finished(struct Scsi_Host *, unsigned long);
@@ -338,15 +458,15 @@ void lpfc_get_cfgparam(struct lpfc_hba *);
void lpfc_get_vport_cfgparam(struct lpfc_vport *);
int lpfc_alloc_sysfs_attr(struct lpfc_vport *);
void lpfc_free_sysfs_attr(struct lpfc_vport *);
-extern struct device_attribute *lpfc_hba_attrs[];
-extern struct device_attribute *lpfc_vport_attrs[];
+bool lpfc_error_lost_link(struct lpfc_vport *vport, u32 ulp_status,
+ u32 ulp_word4);
+extern const struct attribute_group *lpfc_hba_groups[];
+extern const struct attribute_group *lpfc_vport_groups[];
extern struct scsi_host_template lpfc_template;
+extern struct scsi_host_template lpfc_template_nvme;
extern struct scsi_host_template lpfc_vport_template;
extern struct fc_function_template lpfc_transport_functions;
extern struct fc_function_template lpfc_vport_transport_functions;
-extern int lpfc_sli_mode;
-extern int lpfc_enable_npiv;
-extern int lpfc_delay_discovery;
int lpfc_vport_symbolic_node_name(struct lpfc_vport *, char *, size_t);
int lpfc_vport_symbolic_port_name(struct lpfc_vport *, char *, size_t);
@@ -363,35 +483,28 @@ void lpfc_host_attrib_init(struct Scsi_Host *);
extern void lpfc_debugfs_initialize(struct lpfc_vport *);
extern void lpfc_debugfs_terminate(struct lpfc_vport *);
extern void lpfc_debugfs_disc_trc(struct lpfc_vport *, int, char *, uint32_t,
- uint32_t, uint32_t);
+ uint32_t, uint32_t);
extern void lpfc_debugfs_slow_ring_trc(struct lpfc_hba *, char *, uint32_t,
- uint32_t, uint32_t);
+ uint32_t, uint32_t);
+extern void lpfc_debugfs_nvme_trc(struct lpfc_hba *phba, char *fmt,
+ uint16_t data1, uint16_t data2, uint32_t data3);
extern struct lpfc_hbq_init *lpfc_hbq_defs[];
/* SLI4 if_type 2 externs. */
int lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *);
int lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *);
-
-/* externs BlockGuard */
-extern char *_dump_buf_data;
-extern unsigned long _dump_buf_data_order;
-extern char *_dump_buf_dif;
-extern unsigned long _dump_buf_dif_order;
-extern spinlock_t _dump_buf_lock;
-extern int _dump_buf_done;
-extern spinlock_t pgcnt_lock;
-extern unsigned int pgcnt;
-extern unsigned int lpfc_prot_mask;
-extern unsigned char lpfc_prot_guard;
+int lpfc_sli4_get_allocated_extnts(struct lpfc_hba *, uint16_t,
+ uint16_t *, uint16_t *);
+int lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *, uint16_t,
+ uint16_t *, uint16_t *);
/* Interface exported by fabric iocb scheduler */
void lpfc_fabric_abort_nport(struct lpfc_nodelist *);
void lpfc_fabric_abort_hba(struct lpfc_hba *);
-void lpfc_fabric_block_timeout(unsigned long);
+void lpfc_fabric_block_timeout(struct timer_list *);
void lpfc_unblock_fabric_iocbs(struct lpfc_hba *);
void lpfc_rampdown_queue_depth(struct lpfc_hba *);
void lpfc_ramp_down_queue_handler(struct lpfc_hba *);
-void lpfc_ramp_up_queue_handler(struct lpfc_hba *);
void lpfc_scsi_dev_block(struct lpfc_hba *);
void
@@ -402,6 +515,9 @@ void lpfc_free_fast_evt(struct lpfc_hba *, struct lpfc_fast_path_event *);
void lpfc_create_static_vport(struct lpfc_hba *);
void lpfc_stop_hba_timers(struct lpfc_hba *);
void lpfc_stop_port(struct lpfc_hba *);
+int lpfc_update_cmf_cmd(struct lpfc_hba *phba, uint32_t sz);
+int lpfc_update_cmf_cmpl(struct lpfc_hba *phba, uint64_t val, uint32_t sz,
+ struct Scsi_Host *shost);
void __lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *);
void lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *);
void lpfc_parse_fcoe_conf(struct lpfc_hba *, uint8_t *, uint32_t);
@@ -409,16 +525,16 @@ int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
void lpfc_start_fdiscs(struct lpfc_hba *phba);
struct lpfc_vport *lpfc_find_vport_by_vpid(struct lpfc_hba *, uint16_t);
struct lpfc_sglq *__lpfc_get_active_sglq(struct lpfc_hba *, uint16_t);
-#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
#define HBA_EVENT_RSCN 5
#define HBA_EVENT_LINK_UP 2
#define HBA_EVENT_LINK_DOWN 3
/* functions to support SGIOv4/bsg interface */
-int lpfc_bsg_request(struct fc_bsg_job *);
-int lpfc_bsg_timeout(struct fc_bsg_job *);
+int lpfc_bsg_request(struct bsg_job *);
+int lpfc_bsg_timeout(struct bsg_job *);
int lpfc_bsg_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
+int lpfc_bsg_ct_unsol_abort(struct lpfc_hba *, struct hbq_dmabuf *);
void __lpfc_sli_ringtx_put(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
struct lpfc_iocbq *lpfc_sli_ringtx_get(struct lpfc_hba *,
@@ -432,10 +548,148 @@ void lpfc_handle_rrq_active(struct lpfc_hba *);
int lpfc_send_rrq(struct lpfc_hba *, struct lpfc_node_rrq *);
int lpfc_set_rrq_active(struct lpfc_hba *, struct lpfc_nodelist *,
uint16_t, uint16_t, uint16_t);
-void lpfc_cleanup_wt_rrqs(struct lpfc_hba *);
+uint16_t lpfc_sli4_xri_inrange(struct lpfc_hba *, uint16_t);
void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *);
struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t,
uint32_t);
+void lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *, enum nemb_type,
+ enum mbox_type, enum dma_type, enum sta_type,
+ struct lpfc_dmabuf *, uint32_t);
+void lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *, MAILBOX_t *);
int lpfc_wr_object(struct lpfc_hba *, struct list_head *, uint32_t, uint32_t *);
/* functions to support SR-IOV */
int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *, int);
+uint16_t lpfc_sli_sriov_nr_virtfn_get(struct lpfc_hba *);
+int lpfc_sli4_queue_create(struct lpfc_hba *);
+void lpfc_sli4_queue_destroy(struct lpfc_hba *);
+void lpfc_sli4_abts_err_handler(struct lpfc_hba *, struct lpfc_nodelist *,
+ struct sli4_wcqe_xri_aborted *);
+void lpfc_sli_abts_recover_port(struct lpfc_vport *,
+ struct lpfc_nodelist *);
+int lpfc_hba_init_link_fc_topology(struct lpfc_hba *, uint32_t, uint32_t);
+int lpfc_issue_reg_vfi(struct lpfc_vport *);
+int lpfc_issue_unreg_vfi(struct lpfc_vport *);
+int lpfc_selective_reset(struct lpfc_hba *);
+int lpfc_sli4_read_config(struct lpfc_hba *);
+void lpfc_sli4_node_rpi_restore(struct lpfc_hba *phba);
+int lpfc_sli4_els_sgl_update(struct lpfc_hba *phba);
+int lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba);
+int lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *sglist);
+int lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf);
+int lpfc_sli4_io_sgl_update(struct lpfc_hba *phba);
+int lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba,
+ struct list_head *blist, int xricnt);
+int lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc);
+void lpfc_io_free(struct lpfc_hba *phba);
+void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *);
+uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *);
+int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t);
+void lpfc_sli4_offline_eratt(struct lpfc_hba *);
+
+struct lpfc_device_data *lpfc_create_device_data(struct lpfc_hba *,
+ struct lpfc_name *,
+ struct lpfc_name *,
+ uint64_t, uint32_t, bool);
+void lpfc_delete_device_data(struct lpfc_hba *, struct lpfc_device_data*);
+struct lpfc_device_data *__lpfc_get_device_data(struct lpfc_hba *,
+ struct list_head *list,
+ struct lpfc_name *,
+ struct lpfc_name *, uint64_t);
+bool lpfc_enable_oas_lun(struct lpfc_hba *, struct lpfc_name *,
+ struct lpfc_name *, uint64_t, uint8_t);
+bool lpfc_disable_oas_lun(struct lpfc_hba *, struct lpfc_name *,
+ struct lpfc_name *, uint64_t, uint8_t);
+bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *,
+ struct lpfc_name *, uint64_t *, struct lpfc_name *,
+ struct lpfc_name *, uint64_t *,
+ uint32_t *, uint32_t *);
+int lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox);
+void lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb);
+
+/* RAS Interface */
+void lpfc_sli4_ras_init(struct lpfc_hba *phba);
+void lpfc_sli4_ras_setup(struct lpfc_hba *phba);
+int lpfc_sli4_ras_fwlog_init(struct lpfc_hba *phba, uint32_t fwlog_level,
+ uint32_t fwlog_enable);
+void lpfc_ras_stop_fwlog(struct lpfc_hba *phba);
+int lpfc_check_fwlog_support(struct lpfc_hba *phba);
+
+/* NVME interfaces. */
+void lpfc_nvme_rescan_port(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp);
+void lpfc_nvme_unregister_port(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp);
+int lpfc_nvme_register_port(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp);
+int lpfc_nvme_create_localport(struct lpfc_vport *vport);
+void lpfc_nvme_destroy_localport(struct lpfc_vport *vport);
+void lpfc_nvme_update_localport(struct lpfc_vport *vport);
+int lpfc_nvmet_create_targetport(struct lpfc_hba *phba);
+int lpfc_nvmet_update_targetport(struct lpfc_hba *phba);
+void lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba);
+int lpfc_nvme_handle_lsreq(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *axchg);
+int lpfc_nvmet_handle_lsreq(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *axchg);
+void lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba, uint32_t idx,
+ struct rqb_dmabuf *nvmebuf, uint64_t isr_ts,
+ uint8_t cqflag);
+void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba);
+void lpfc_nvmet_invalidate_host(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp);
+void lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb);
+void lpfc_create_multixri_pools(struct lpfc_hba *phba);
+void lpfc_create_destroy_pools(struct lpfc_hba *phba);
+void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid);
+void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 cnt);
+void lpfc_adjust_high_watermark(struct lpfc_hba *phba, u32 hwqid);
+void lpfc_keep_pvt_pool_above_lowwm(struct lpfc_hba *phba, u32 hwqid);
+void lpfc_adjust_pvt_pool_count(struct lpfc_hba *phba, u32 hwqid);
+#ifdef LPFC_MXP_STAT
+void lpfc_snapshot_mxp(struct lpfc_hba *, u32);
+#endif
+struct lpfc_io_buf *lpfc_get_io_buf(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp, u32 hwqid,
+ int);
+void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *ncmd,
+ struct lpfc_sli4_hdw_queue *qp);
+void lpfc_io_ktime(struct lpfc_hba *phba, struct lpfc_io_buf *ncmd);
+void lpfc_wqe_cmd_template(void);
+void lpfc_nvmet_cmd_template(void);
+void lpfc_nvme_cancel_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
+ uint32_t stat, uint32_t param);
+void lpfc_nvmels_flush_cmd(struct lpfc_hba *phba);
+extern int lpfc_enable_nvmet_cnt;
+extern unsigned long long lpfc_enable_nvmet[];
+extern int lpfc_no_hba_reset_cnt;
+extern unsigned long lpfc_no_hba_reset[];
+extern unsigned char lpfc_acqe_cgn_frequency;
+extern int lpfc_fabric_cgn_frequency;
+extern int lpfc_use_cgn_signal;
+
+extern union lpfc_wqe128 lpfc_iread_cmd_template;
+extern union lpfc_wqe128 lpfc_iwrite_cmd_template;
+extern union lpfc_wqe128 lpfc_icmnd_cmd_template;
+
+/* vmid interface */
+int lpfc_vmid_uvem(struct lpfc_vport *vport, struct lpfc_vmid *vmid, bool ins);
+uint32_t lpfc_vmid_get_cs_ctl(struct lpfc_vport *vport);
+int lpfc_vmid_cmd(struct lpfc_vport *vport,
+ int cmdcode, struct lpfc_vmid *vmid);
+int lpfc_vmid_hash_fn(const char *vmid, int len);
+struct lpfc_vmid *lpfc_get_vmid_from_hashtable(struct lpfc_vport *vport,
+ uint32_t hash, uint8_t *buf);
+int lpfc_vmid_get_appid(struct lpfc_vport *vport, char *uuid,
+ enum dma_data_direction iodir,
+ union lpfc_vmid_io_tag *tag);
+void lpfc_vmid_vport_cleanup(struct lpfc_vport *vport);
+int lpfc_issue_els_qfpa(struct lpfc_vport *vport);
+void lpfc_reinit_vmid(struct lpfc_vport *vport);
+
+void lpfc_sli_rpi_release(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp);
+
+int lpfc_get_sfp_info_wait(struct lpfc_hba *phba,
+ struct lpfc_rdp_context *rdp_context);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 779b88e1469d..d3caac394291 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2010 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -40,27 +42,42 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_version.h"
#include "lpfc_vport.h"
#include "lpfc_debugfs.h"
-/* FDMI Port Speed definitions */
-#define HBA_PORTSPEED_1GBIT 0x0001 /* 1 GBit/sec */
-#define HBA_PORTSPEED_2GBIT 0x0002 /* 2 GBit/sec */
-#define HBA_PORTSPEED_4GBIT 0x0008 /* 4 GBit/sec */
-#define HBA_PORTSPEED_10GBIT 0x0004 /* 10 GBit/sec */
-#define HBA_PORTSPEED_8GBIT 0x0010 /* 8 GBit/sec */
-#define HBA_PORTSPEED_16GBIT 0x0020 /* 16 GBit/sec */
-#define HBA_PORTSPEED_UNKNOWN 0x0800 /* Unknown */
+/* FDMI Port Speed definitions - FC-GS-7 */
+#define HBA_PORTSPEED_1GFC 0x00000001 /* 1G FC */
+#define HBA_PORTSPEED_2GFC 0x00000002 /* 2G FC */
+#define HBA_PORTSPEED_4GFC 0x00000008 /* 4G FC */
+#define HBA_PORTSPEED_10GFC 0x00000004 /* 10G FC */
+#define HBA_PORTSPEED_8GFC 0x00000010 /* 8G FC */
+#define HBA_PORTSPEED_16GFC 0x00000020 /* 16G FC */
+#define HBA_PORTSPEED_32GFC 0x00000040 /* 32G FC */
+#define HBA_PORTSPEED_20GFC 0x00000080 /* 20G FC */
+#define HBA_PORTSPEED_40GFC 0x00000100 /* 40G FC */
+#define HBA_PORTSPEED_128GFC 0x00000200 /* 128G FC */
+#define HBA_PORTSPEED_64GFC 0x00000400 /* 64G FC */
+#define HBA_PORTSPEED_256GFC 0x00000800 /* 256G FC */
+#define HBA_PORTSPEED_UNKNOWN 0x00008000 /* Unknown */
+#define HBA_PORTSPEED_10GE 0x00010000 /* 10G E */
+#define HBA_PORTSPEED_40GE 0x00020000 /* 40G E */
+#define HBA_PORTSPEED_100GE 0x00040000 /* 100G E */
+#define HBA_PORTSPEED_25GE 0x00080000 /* 25G E */
+#define HBA_PORTSPEED_50GE 0x00100000 /* 50G E */
+#define HBA_PORTSPEED_400GE 0x00200000 /* 400G E */
#define FOURBYTES 4
static char *lpfc_release_version = LPFC_DRIVER_VERSION;
+static void
+lpfc_cmpl_ct_cmd_vmid(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb);
static void
lpfc_ct_ignore_hbq_buffer(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
@@ -70,12 +87,12 @@ lpfc_ct_ignore_hbq_buffer(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
"0146 Ignoring unsolicited CT No HBQ "
"status = x%x\n",
- piocbq->iocb.ulpStatus);
+ get_job_ulpstatus(phba, piocbq));
}
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
- "0145 Ignoring unsolicted CT HBQ Size:%d "
+ "0145 Ignoring unsolicited CT HBQ Size:%d "
"status = x%x\n",
- size, piocbq->iocb.ulpStatus);
+ size, get_job_ulpstatus(phba, piocbq));
}
static void
@@ -85,115 +102,372 @@ lpfc_ct_unsol_buffer(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
lpfc_ct_ignore_hbq_buffer(phba, piocbq, mp, size);
}
+/**
+ * lpfc_ct_unsol_cmpl : Completion callback function for unsol ct commands
+ * @phba : pointer to lpfc hba data structure.
+ * @cmdiocb : pointer to lpfc command iocb data structure.
+ * @rspiocb : pointer to lpfc response iocb data structure.
+ *
+ * This routine is the callback function for issuing unsol ct reject command.
+ * The memory allocated in the reject command path is freed up here.
+ **/
+static void
+lpfc_ct_unsol_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_dmabuf *mp, *bmp;
+
+ ndlp = cmdiocb->ndlp;
+ if (ndlp)
+ lpfc_nlp_put(ndlp);
+
+ mp = cmdiocb->rsp_dmabuf;
+ bmp = cmdiocb->bpl_dmabuf;
+ if (mp) {
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ cmdiocb->rsp_dmabuf = NULL;
+ }
+
+ if (bmp) {
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ kfree(bmp);
+ cmdiocb->bpl_dmabuf = NULL;
+ }
+
+ lpfc_sli_release_iocbq(phba, cmdiocb);
+}
+
+/**
+ * lpfc_ct_reject_event - Issue reject for unhandled CT MIB commands
+ * @ndlp: pointer to a node-list data structure.
+ * @ct_req: pointer to the CT request data structure.
+ * @ulp_context: context of received UNSOL CT command
+ * @ox_id: ox_id of the UNSOL CT command
+ *
+ * This routine is invoked by the lpfc_ct_handle_mibreq routine for sending
+ * a reject response. Reject response is sent for the unhandled commands.
+ **/
+static void
+lpfc_ct_reject_event(struct lpfc_nodelist *ndlp,
+ struct lpfc_sli_ct_request *ct_req,
+ u16 ulp_context, u16 ox_id)
+{
+ struct lpfc_vport *vport = ndlp->vport;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_sli_ct_request *ct_rsp;
+ struct lpfc_iocbq *cmdiocbq = NULL;
+ struct lpfc_dmabuf *bmp = NULL;
+ struct lpfc_dmabuf *mp = NULL;
+ struct ulp_bde64 *bpl;
+ u8 rc = 0;
+ u32 tmo;
+
+ /* fill in BDEs for command */
+ mp = kmalloc(sizeof(*mp), GFP_KERNEL);
+ if (!mp) {
+ rc = 1;
+ goto ct_exit;
+ }
+
+ mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &mp->phys);
+ if (!mp->virt) {
+ rc = 2;
+ goto ct_free_mp;
+ }
+
+ /* Allocate buffer for Buffer ptr list */
+ bmp = kmalloc(sizeof(*bmp), GFP_KERNEL);
+ if (!bmp) {
+ rc = 3;
+ goto ct_free_mpvirt;
+ }
+
+ bmp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &bmp->phys);
+ if (!bmp->virt) {
+ rc = 4;
+ goto ct_free_bmp;
+ }
+
+ INIT_LIST_HEAD(&mp->list);
+ INIT_LIST_HEAD(&bmp->list);
+
+ bpl = (struct ulp_bde64 *)bmp->virt;
+ memset(bpl, 0, sizeof(struct ulp_bde64));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(mp->phys));
+ bpl->addrLow = le32_to_cpu(putPaddrLow(mp->phys));
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bpl->tus.f.bdeSize = (LPFC_CT_PREAMBLE - 4);
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ ct_rsp = (struct lpfc_sli_ct_request *)mp->virt;
+ memset(ct_rsp, 0, sizeof(struct lpfc_sli_ct_request));
+
+ ct_rsp->RevisionId.bits.Revision = SLI_CT_REVISION;
+ ct_rsp->RevisionId.bits.InId = 0;
+ ct_rsp->FsType = ct_req->FsType;
+ ct_rsp->FsSubType = ct_req->FsSubType;
+ ct_rsp->CommandResponse.bits.Size = 0;
+ ct_rsp->CommandResponse.bits.CmdRsp =
+ cpu_to_be16(SLI_CT_RESPONSE_FS_RJT);
+ ct_rsp->ReasonCode = SLI_CT_REQ_NOT_SUPPORTED;
+ ct_rsp->Explanation = SLI_CT_NO_ADDITIONAL_EXPL;
+
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ if (!cmdiocbq) {
+ rc = 5;
+ goto ct_free_bmpvirt;
+ }
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, bmp,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi],
+ ox_id, 1, FC_RCTL_DD_SOL_CTL, 1,
+ CMD_XMIT_SEQUENCE64_WQE);
+ } else {
+ lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, bmp, 0, ulp_context, 1,
+ FC_RCTL_DD_SOL_CTL, 1,
+ CMD_XMIT_SEQUENCE64_CX);
+ }
+
+ /* Save for completion so we can release these resources */
+ cmdiocbq->rsp_dmabuf = mp;
+ cmdiocbq->bpl_dmabuf = bmp;
+ cmdiocbq->cmd_cmpl = lpfc_ct_unsol_cmpl;
+ tmo = (3 * phba->fc_ratov);
+
+ cmdiocbq->retry = 0;
+ cmdiocbq->vport = vport;
+ cmdiocbq->drvrTimeout = tmo + LPFC_DRVR_TIMEOUT;
+
+ cmdiocbq->ndlp = lpfc_nlp_get(ndlp);
+ if (!cmdiocbq->ndlp)
+ goto ct_no_ndlp;
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
+ if (rc) {
+ lpfc_nlp_put(ndlp);
+ goto ct_no_ndlp;
+ }
+ return;
+
+ct_no_ndlp:
+ rc = 6;
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ct_free_bmpvirt:
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ct_free_bmp:
+ kfree(bmp);
+ct_free_mpvirt:
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ct_free_mp:
+ kfree(mp);
+ct_exit:
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "6440 Unsol CT: Rsp err %d Data: x%lx\n",
+ rc, vport->fc_flag);
+}
+
+/**
+ * lpfc_ct_handle_mibreq - Process an unsolicited CT MIB request data buffer
+ * @phba: pointer to lpfc hba data structure.
+ * @ctiocbq: pointer to lpfc CT command iocb data structure.
+ *
+ * This routine is used for processing the IOCB associated with a unsolicited
+ * CT MIB request. It first determines whether there is an existing ndlp that
+ * matches the DID from the unsolicited IOCB. If not, it will return.
+ **/
+static void
+lpfc_ct_handle_mibreq(struct lpfc_hba *phba, struct lpfc_iocbq *ctiocbq)
+{
+ struct lpfc_sli_ct_request *ct_req;
+ struct lpfc_nodelist *ndlp = NULL;
+ struct lpfc_vport *vport = ctiocbq->vport;
+ u32 ulp_status = get_job_ulpstatus(phba, ctiocbq);
+ u32 ulp_word4 = get_job_word4(phba, ctiocbq);
+ u32 did;
+ u16 mi_cmd;
+
+ did = bf_get(els_rsp64_sid, &ctiocbq->wqe.xmit_els_rsp);
+ if (ulp_status) {
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "6438 Unsol CT: status:x%x/x%x did : x%x\n",
+ ulp_status, ulp_word4, did);
+ return;
+ }
+
+ /* Ignore traffic received during vport shutdown */
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
+ return;
+
+ ndlp = lpfc_findnode_did(vport, did);
+ if (!ndlp) {
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "6439 Unsol CT: NDLP Not Found for DID : x%x",
+ did);
+ return;
+ }
+
+ ct_req = (struct lpfc_sli_ct_request *)ctiocbq->cmd_dmabuf->virt;
+
+ mi_cmd = be16_to_cpu(ct_req->CommandResponse.bits.CmdRsp);
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "6442 MI Cmd: x%x Not Supported\n", mi_cmd);
+ lpfc_ct_reject_event(ndlp, ct_req,
+ bf_get(wqe_ctxt_tag,
+ &ctiocbq->wqe.xmit_els_rsp.wqe_com),
+ bf_get(wqe_rcvoxid,
+ &ctiocbq->wqe.xmit_els_rsp.wqe_com));
+}
+
+/**
+ * lpfc_ct_unsol_event - Process an unsolicited event from a ct sli ring
+ * @phba: pointer to lpfc hba data structure.
+ * @pring: pointer to a SLI ring.
+ * @ctiocbq: pointer to lpfc ct iocb data structure.
+ *
+ * This routine is used to process an unsolicited event received from a SLI
+ * (Service Level Interface) ring. The actual processing of the data buffer
+ * associated with the unsolicited event is done by invoking appropriate routine
+ * after properly set up the iocb buffer from the SLI ring on which the
+ * unsolicited event was received.
+ **/
void
lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
- struct lpfc_iocbq *piocbq)
+ struct lpfc_iocbq *ctiocbq)
{
struct lpfc_dmabuf *mp = NULL;
- IOCB_t *icmd = &piocbq->iocb;
+ IOCB_t *icmd = &ctiocbq->iocb;
int i;
struct lpfc_iocbq *iocbq;
- dma_addr_t paddr;
+ struct lpfc_iocbq *iocb;
+ dma_addr_t dma_addr;
uint32_t size;
struct list_head head;
- struct lpfc_dmabuf *bdeBuf;
-
- if (lpfc_bsg_ct_unsol_event(phba, pring, piocbq) == 0)
- return;
+ struct lpfc_sli_ct_request *ct_req;
+ struct lpfc_dmabuf *bdeBuf1 = ctiocbq->cmd_dmabuf;
+ struct lpfc_dmabuf *bdeBuf2 = ctiocbq->bpl_dmabuf;
+ u32 status, parameter, bde_count = 0;
+ struct lpfc_wcqe_complete *wcqe_cmpl = NULL;
+
+ ctiocbq->cmd_dmabuf = NULL;
+ ctiocbq->rsp_dmabuf = NULL;
+ ctiocbq->bpl_dmabuf = NULL;
+
+ wcqe_cmpl = &ctiocbq->wcqe_cmpl;
+ status = get_job_ulpstatus(phba, ctiocbq);
+ parameter = get_job_word4(phba, ctiocbq);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ bde_count = wcqe_cmpl->word3;
+ else
+ bde_count = icmd->ulpBdeCount;
- if (unlikely(icmd->ulpStatus == IOSTAT_NEED_BUFFER)) {
+ if (unlikely(status == IOSTAT_NEED_BUFFER)) {
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
- } else if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- ((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) {
+ } else if ((status == IOSTAT_LOCAL_REJECT) &&
+ ((parameter & IOERR_PARAM_MASK) ==
+ IOERR_RCV_BUFFER_WAITING)) {
/* Not enough posted buffers; Try posting more buffers */
phba->fc_stat.NoRcvBuf++;
if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
- lpfc_post_buffer(phba, pring, 2);
+ lpfc_sli3_post_buffer(phba, pring, 2);
return;
}
- /* If there are no BDEs associated with this IOCB,
- * there is nothing to do.
+ /* If there are no BDEs associated
+ * with this IOCB, there is nothing to do.
*/
- if (icmd->ulpBdeCount == 0)
+ if (bde_count == 0)
return;
+ ctiocbq->cmd_dmabuf = bdeBuf1;
+ if (bde_count == 2)
+ ctiocbq->bpl_dmabuf = bdeBuf2;
+
+ ct_req = (struct lpfc_sli_ct_request *)ctiocbq->cmd_dmabuf->virt;
+
+ if (ct_req->FsType == SLI_CT_MANAGEMENT_SERVICE &&
+ ct_req->FsSubType == SLI_CT_MIB_Subtypes) {
+ lpfc_ct_handle_mibreq(phba, ctiocbq);
+ } else {
+ if (!lpfc_bsg_ct_unsol_event(phba, pring, ctiocbq))
+ return;
+ }
+
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
INIT_LIST_HEAD(&head);
- list_add_tail(&head, &piocbq->list);
- list_for_each_entry(iocbq, &head, list) {
- icmd = &iocbq->iocb;
- if (icmd->ulpBdeCount == 0)
+ list_add_tail(&head, &ctiocbq->list);
+ list_for_each_entry(iocb, &head, list) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ bde_count = iocb->wcqe_cmpl.word3;
+ else
+ bde_count = iocb->iocb.ulpBdeCount;
+
+ if (!bde_count)
continue;
- bdeBuf = iocbq->context2;
- iocbq->context2 = NULL;
- size = icmd->un.cont64[0].tus.f.bdeSize;
- lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf, size);
- lpfc_in_buf_free(phba, bdeBuf);
- if (icmd->ulpBdeCount == 2) {
- bdeBuf = iocbq->context3;
- iocbq->context3 = NULL;
- size = icmd->unsli3.rcvsli3.bde2.tus.f.bdeSize;
- lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf,
+ bdeBuf1 = iocb->cmd_dmabuf;
+ iocb->cmd_dmabuf = NULL;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ size = iocb->wqe.gen_req.bde.tus.f.bdeSize;
+ else
+ size = iocb->iocb.un.cont64[0].tus.f.bdeSize;
+ lpfc_ct_unsol_buffer(phba, ctiocbq, bdeBuf1, size);
+ lpfc_in_buf_free(phba, bdeBuf1);
+ if (bde_count == 2) {
+ bdeBuf2 = iocb->bpl_dmabuf;
+ iocb->bpl_dmabuf = NULL;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ size = iocb->unsol_rcv_len;
+ else
+ size = iocb->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize;
+ lpfc_ct_unsol_buffer(phba, ctiocbq, bdeBuf2,
size);
- lpfc_in_buf_free(phba, bdeBuf);
+ lpfc_in_buf_free(phba, bdeBuf2);
}
}
list_del(&head);
} else {
INIT_LIST_HEAD(&head);
- list_add_tail(&head, &piocbq->list);
+ list_add_tail(&head, &ctiocbq->list);
list_for_each_entry(iocbq, &head, list) {
icmd = &iocbq->iocb;
if (icmd->ulpBdeCount == 0)
lpfc_ct_unsol_buffer(phba, iocbq, NULL, 0);
for (i = 0; i < icmd->ulpBdeCount; i++) {
- paddr = getPaddr(icmd->un.cont64[i].addrHigh,
- icmd->un.cont64[i].addrLow);
+ dma_addr = getPaddr(icmd->un.cont64[i].addrHigh,
+ icmd->un.cont64[i].addrLow);
mp = lpfc_sli_ringpostbuf_get(phba, pring,
- paddr);
+ dma_addr);
size = icmd->un.cont64[i].tus.f.bdeSize;
lpfc_ct_unsol_buffer(phba, iocbq, mp, size);
lpfc_in_buf_free(phba, mp);
}
- lpfc_post_buffer(phba, pring, i);
+ lpfc_sli3_post_buffer(phba, pring, i);
}
list_del(&head);
}
}
/**
- * lpfc_sli4_ct_abort_unsol_event - Default handle for sli4 unsol abort
+ * lpfc_ct_handle_unsol_abort - ct upper level protocol abort handler
* @phba: Pointer to HBA context object.
- * @pring: Pointer to the driver internal I/O ring.
- * @piocbq: Pointer to the IOCBQ.
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
*
- * This function serves as the default handler for the sli4 unsolicited
- * abort event. It shall be invoked when there is no application interface
- * registered unsolicited abort handler. This handler does nothing but
- * just simply releases the dma buffer used by the unsol abort event.
+ * This function serves as the upper level protocol abort handler for CT
+ * protocol.
+ *
+ * Return 1 if abort has been handled, 0 otherwise.
**/
-void
-lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba,
- struct lpfc_sli_ring *pring,
- struct lpfc_iocbq *piocbq)
+int
+lpfc_ct_handle_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
{
- IOCB_t *icmd = &piocbq->iocb;
- struct lpfc_dmabuf *bdeBuf;
- uint32_t size;
+ int handled;
- /* Forward abort event to any process registered to receive ct event */
- if (lpfc_bsg_ct_unsol_event(phba, pring, piocbq) == 0)
- return;
+ /* CT upper level goes through BSG */
+ handled = lpfc_bsg_ct_unsol_abort(phba, dmabuf);
- /* If there is no BDE associated with IOCB, there is nothing to do */
- if (icmd->ulpBdeCount == 0)
- return;
- bdeBuf = piocbq->context2;
- piocbq->context2 = NULL;
- size = icmd->un.cont64[0].tus.f.bdeSize;
- lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf, size);
- lpfc_in_buf_free(phba, bdeBuf);
+ return handled;
}
static void
@@ -202,8 +476,8 @@ lpfc_free_ct_rsp(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
struct lpfc_dmabuf *mlast, *next_mlast;
list_for_each_entry_safe(mlast, next_mlast, &mlist->list, list) {
- lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
list_del(&mlast->list);
+ lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
kfree(mlast);
}
lpfc_mbuf_free(phba, mlist->virt, mlist->phys);
@@ -212,7 +486,7 @@ lpfc_free_ct_rsp(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
}
static struct lpfc_dmabuf *
-lpfc_alloc_ct_rsp(struct lpfc_hba *phba, int cmdcode, struct ulp_bde64 *bpl,
+lpfc_alloc_ct_rsp(struct lpfc_hba *phba, __be16 cmdcode, struct ulp_bde64 *bpl,
uint32_t size, int *entries)
{
struct lpfc_dmabuf *mlist = NULL;
@@ -233,8 +507,8 @@ lpfc_alloc_ct_rsp(struct lpfc_hba *phba, int cmdcode, struct ulp_bde64 *bpl,
INIT_LIST_HEAD(&mp->list);
- if (cmdcode == be16_to_cpu(SLI_CTNS_GID_FT) ||
- cmdcode == be16_to_cpu(SLI_CTNS_GFF_ID))
+ if (be16_to_cpu(cmdcode) == SLI_CTNS_GID_FT ||
+ be16_to_cpu(cmdcode) == SLI_CTNS_GFF_ID)
mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
else
mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys));
@@ -273,43 +547,53 @@ lpfc_ct_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *ctiocb)
{
struct lpfc_dmabuf *buf_ptr;
- if (ctiocb->context_un.ndlp) {
- lpfc_nlp_put(ctiocb->context_un.ndlp);
- ctiocb->context_un.ndlp = NULL;
- }
- if (ctiocb->context1) {
- buf_ptr = (struct lpfc_dmabuf *) ctiocb->context1;
+ /* IOCBQ job structure gets cleaned during release. Just release
+ * the dma buffers here.
+ */
+ if (ctiocb->cmd_dmabuf) {
+ buf_ptr = ctiocb->cmd_dmabuf;
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
- ctiocb->context1 = NULL;
+ ctiocb->cmd_dmabuf = NULL;
}
- if (ctiocb->context2) {
- lpfc_free_ct_rsp(phba, (struct lpfc_dmabuf *) ctiocb->context2);
- ctiocb->context2 = NULL;
+ if (ctiocb->rsp_dmabuf) {
+ lpfc_free_ct_rsp(phba, ctiocb->rsp_dmabuf);
+ ctiocb->rsp_dmabuf = NULL;
}
- if (ctiocb->context3) {
- buf_ptr = (struct lpfc_dmabuf *) ctiocb->context3;
+ if (ctiocb->bpl_dmabuf) {
+ buf_ptr = ctiocb->bpl_dmabuf;
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
- ctiocb->context1 = NULL;
+ ctiocb->bpl_dmabuf = NULL;
}
lpfc_sli_release_iocbq(phba, ctiocb);
return 0;
}
+/*
+ * lpfc_gen_req - Build and issue a GEN_REQUEST command to the SLI Layer
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @bmp: Pointer to BPL for SLI command
+ * @inp: Pointer to data buffer for response data.
+ * @outp: Pointer to data buffer that hold the CT command.
+ * @cmpl: completion routine to call when command completes
+ * @ndlp: Destination NPort nodelist entry
+ *
+ * This function as the final part for issuing a CT command.
+ */
static int
lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp,
- void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
- struct lpfc_iocbq *),
- struct lpfc_nodelist *ndlp, uint32_t usr_flg, uint32_t num_entry,
+ void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *),
+ struct lpfc_nodelist *ndlp, uint32_t event_tag, uint32_t num_entry,
uint32_t tmo, uint8_t retry)
{
struct lpfc_hba *phba = vport->phba;
- IOCB_t *icmd;
struct lpfc_iocbq *geniocb;
int rc;
+ u16 ulp_context;
/* Allocate buffer for command iocb */
geniocb = lpfc_sli_get_iocbq(phba);
@@ -317,70 +601,65 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
if (geniocb == NULL)
return 1;
- icmd = &geniocb->iocb;
- icmd->un.genreq64.bdl.ulpIoTag32 = 0;
- icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
- icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
- icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64));
+ /* Update the num_entry bde count */
+ geniocb->num_bdes = num_entry;
- if (usr_flg)
- geniocb->context3 = NULL;
- else
- geniocb->context3 = (uint8_t *) bmp;
+ geniocb->bpl_dmabuf = bmp;
/* Save for completion so we can release these resources */
- geniocb->context1 = (uint8_t *) inp;
- geniocb->context2 = (uint8_t *) outp;
- geniocb->context_un.ndlp = lpfc_nlp_get(ndlp);
+ geniocb->cmd_dmabuf = inp;
+ geniocb->rsp_dmabuf = outp;
- /* Fill in payload, bp points to frame payload */
- icmd->ulpCommand = CMD_GEN_REQUEST64_CR;
-
- /* Fill in rest of iocb */
- icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
- icmd->un.genreq64.w5.hcsw.Dfctl = 0;
- icmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
- icmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
+ geniocb->event_tag = event_tag;
if (!tmo) {
/* FC spec states we need 3 * ratov for CT requests */
tmo = (3 * phba->fc_ratov);
}
- icmd->ulpTimeout = tmo;
- icmd->ulpBdeCount = 1;
- icmd->ulpLe = 1;
- icmd->ulpClass = CLASS3;
- icmd->ulpContext = ndlp->nlp_rpi;
+
if (phba->sli_rev == LPFC_SLI_REV4)
- icmd->ulpContext = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
+ ulp_context = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
+ else
+ ulp_context = ndlp->nlp_rpi;
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
- /* For GEN_REQUEST64_CR, use the RPI */
- icmd->ulpCt_h = 0;
- icmd->ulpCt_l = 0;
- }
+ lpfc_sli_prep_gen_req(phba, geniocb, bmp, ulp_context, num_entry, tmo);
/* Issue GEN REQ IOCB for NPORT <did> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0119 Issue GEN REQ IOCB to NPORT x%x "
"Data: x%x x%x\n",
- ndlp->nlp_DID, icmd->ulpIoTag,
+ ndlp->nlp_DID, geniocb->iotag,
vport->port_state);
- geniocb->iocb_cmpl = cmpl;
- geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT;
+ geniocb->cmd_cmpl = cmpl;
+ geniocb->drvrTimeout = tmo + LPFC_DRVR_TIMEOUT;
geniocb->vport = vport;
geniocb->retry = retry;
- rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, geniocb, 0);
+ geniocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!geniocb->ndlp)
+ goto out;
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, geniocb, 0);
if (rc == IOCB_ERROR) {
- lpfc_sli_release_iocbq(phba, geniocb);
- return 1;
+ lpfc_nlp_put(ndlp);
+ goto out;
}
return 0;
+out:
+ lpfc_sli_release_iocbq(phba, geniocb);
+ return 1;
}
+/*
+ * lpfc_ct_cmd - Build and issue a CT command
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @inmp: Pointer to data buffer for response data.
+ * @bmp: Pointer to BPL for SLI command
+ * @ndlp: Destination NPort nodelist entry
+ * @cmpl: completion routine to call when command completes
+ *
+ * This function is called for issuing a CT command.
+ */
static int
lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,
struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp,
@@ -392,7 +671,7 @@ lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,
struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt;
struct lpfc_dmabuf *outmp;
int cnt = 0, status;
- int cmdcode = ((struct lpfc_sli_ct_request *) inmp->virt)->
+ __be16 cmdcode = ((struct lpfc_sli_ct_request *)inmp->virt)->
CommandResponse.bits.CmdRsp;
bpl++; /* Skip past ct request */
@@ -407,8 +686,8 @@ lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,
* lpfc_alloc_ct_rsp.
*/
cnt += 1;
- status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp, 0,
- cnt, 0, retry);
+ status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp,
+ phba->fc_eventTag, cnt, 0, retry);
if (status) {
lpfc_free_ct_rsp(phba, outmp);
return -ENOMEM;
@@ -421,29 +700,158 @@ lpfc_find_vport_by_did(struct lpfc_hba *phba, uint32_t did) {
struct lpfc_vport *vport_curr;
unsigned long flags;
- spin_lock_irqsave(&phba->hbalock, flags);
+ spin_lock_irqsave(&phba->port_list_lock, flags);
list_for_each_entry(vport_curr, &phba->port_list, listentry) {
if ((vport_curr->fc_myDID) && (vport_curr->fc_myDID == did)) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->port_list_lock, flags);
return vport_curr;
}
}
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->port_list_lock, flags);
return NULL;
}
+static void
+lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type)
+{
+ struct lpfc_nodelist *ndlp;
+
+ if ((vport->port_type != LPFC_NPIV_PORT) ||
+ !(vport->ct_flags & FC_CT_RFF_ID) || !vport->cfg_restrict_login) {
+
+ ndlp = lpfc_setup_disc_node(vport, Did);
+
+ if (ndlp) {
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
+ "Parse GID_FTrsp: did:x%x flg:x%lx x%x",
+ Did, ndlp->nlp_flag, vport->fc_flag);
+
+ /* By default, the driver expects to support FCP FC4 */
+ if (fc4_type == FC_TYPE_FCP)
+ ndlp->nlp_fc4_type |= NLP_FC4_FCP;
+
+ if (fc4_type == FC_TYPE_NVME)
+ ndlp->nlp_fc4_type |= NLP_FC4_NVME;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "0238 Process x%06x NameServer Rsp "
+ "Data: x%lx x%x x%x x%lx x%x\n", Did,
+ ndlp->nlp_flag, ndlp->nlp_fc4_type,
+ ndlp->nlp_state, vport->fc_flag,
+ vport->fc_rscn_id_cnt);
+
+ /* if ndlp needs to be discovered and prior
+ * state of ndlp hit devloss, change state to
+ * allow rediscovery.
+ */
+ if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag) &&
+ ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
+ lpfc_nlp_set_state(vport, ndlp,
+ NLP_STE_NPR_NODE);
+ }
+ } else {
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
+ "Skip1 GID_FTrsp: did:x%x flg:x%lx cnt:%d",
+ Did, vport->fc_flag, vport->fc_rscn_id_cnt);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "0239 Skip x%06x NameServer Rsp "
+ "Data: x%lx x%x x%px\n",
+ Did, vport->fc_flag,
+ vport->fc_rscn_id_cnt, ndlp);
+ }
+ } else {
+ if (!test_bit(FC_RSCN_MODE, &vport->fc_flag) ||
+ lpfc_rscn_payload_check(vport, Did)) {
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
+ "Query GID_FTrsp: did:x%x flg:x%lx cnt:%d",
+ Did, vport->fc_flag, vport->fc_rscn_id_cnt);
+
+ /*
+ * This NPortID was previously a FCP/NVMe target,
+ * Don't even bother to send GFF_ID.
+ */
+ ndlp = lpfc_findnode_did(vport, Did);
+ if (ndlp &&
+ (ndlp->nlp_type &
+ (NLP_FCP_TARGET | NLP_NVME_TARGET))) {
+ if (fc4_type == FC_TYPE_FCP)
+ ndlp->nlp_fc4_type |= NLP_FC4_FCP;
+ if (fc4_type == FC_TYPE_NVME)
+ ndlp->nlp_fc4_type |= NLP_FC4_NVME;
+ lpfc_setup_disc_node(vport, Did);
+ } else if (lpfc_ns_cmd(vport, SLI_CTNS_GFF_ID,
+ 0, Did) == 0)
+ vport->num_disc_nodes++;
+ else
+ lpfc_setup_disc_node(vport, Did);
+ } else {
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
+ "Skip2 GID_FTrsp: did:x%x flg:x%lx cnt:%d",
+ Did, vport->fc_flag, vport->fc_rscn_id_cnt);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "0245 Skip x%06x NameServer Rsp "
+ "Data: x%lx x%x\n", Did,
+ vport->fc_flag,
+ vport->fc_rscn_id_cnt);
+ }
+ }
+}
+
+static void
+lpfc_ns_rsp_audit_did(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nodelist *ndlp = NULL;
+ char *str;
+
+ if (phba->cfg_ns_query == LPFC_NS_QUERY_GID_FT)
+ str = "GID_FT";
+ else
+ str = "GID_PT";
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6430 Process %s rsp for %08x type %x %s %s\n",
+ str, Did, fc4_type,
+ (fc4_type == FC_TYPE_FCP) ? "FCP" : " ",
+ (fc4_type == FC_TYPE_NVME) ? "NVME" : " ");
+ /*
+ * To conserve rpi's, filter out addresses for other
+ * vports on the same physical HBAs.
+ */
+ if (Did != vport->fc_myDID &&
+ (!lpfc_find_vport_by_did(phba, Did) ||
+ vport->cfg_peer_port_login)) {
+ if (!phba->nvmet_support) {
+ /* FCPI/NVMEI path. Process Did */
+ lpfc_prep_node_fc4type(vport, Did, fc4_type);
+ return;
+ }
+ /* NVMET path. NVMET only cares about NVMEI nodes. */
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ if (ndlp->nlp_type != NLP_NVME_INITIATOR ||
+ ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+ continue;
+ if (ndlp->nlp_DID == Did)
+ clear_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag);
+ else
+ set_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag);
+ }
+ }
+}
+
static int
-lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
+lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type,
+ uint32_t Size)
{
- struct lpfc_hba *phba = vport->phba;
struct lpfc_sli_ct_request *Response =
(struct lpfc_sli_ct_request *) mp->virt;
- struct lpfc_nodelist *ndlp = NULL;
struct lpfc_dmabuf *mlast, *next_mp;
uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType;
uint32_t Did, CTentry;
int Cnt;
struct list_head head;
+ struct lpfc_nodelist *ndlp = NULL;
lpfc_set_disctmo(vport);
vport->num_disc_nodes = 0;
@@ -464,117 +872,34 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
Cnt -= 16; /* subtract length of CT header */
/* Loop through entire NameServer list of DIDs */
- while (Cnt >= sizeof (uint32_t)) {
+ while (Cnt >= sizeof(uint32_t)) {
/* Get next DID from NameServer List */
CTentry = *ctptr++;
Did = ((be32_to_cpu(CTentry)) & Mask_DID);
-
- ndlp = NULL;
-
- /*
- * Check for rscn processing or not
- * To conserve rpi's, filter out addresses for other
- * vports on the same physical HBAs.
- */
- if ((Did != vport->fc_myDID) &&
- ((lpfc_find_vport_by_did(phba, Did) == NULL) ||
- vport->cfg_peer_port_login)) {
- if ((vport->port_type != LPFC_NPIV_PORT) ||
- (!(vport->ct_flags & FC_CT_RFF_ID)) ||
- (!vport->cfg_restrict_login)) {
- ndlp = lpfc_setup_disc_node(vport, Did);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- lpfc_debugfs_disc_trc(vport,
- LPFC_DISC_TRC_CT,
- "Parse GID_FTrsp: "
- "did:x%x flg:x%x x%x",
- Did, ndlp->nlp_flag,
- vport->fc_flag);
-
- lpfc_printf_vlog(vport,
- KERN_INFO,
- LOG_DISCOVERY,
- "0238 Process "
- "x%x NameServer Rsp"
- "Data: x%x x%x x%x\n",
- Did, ndlp->nlp_flag,
- vport->fc_flag,
- vport->fc_rscn_id_cnt);
- } else {
- lpfc_debugfs_disc_trc(vport,
- LPFC_DISC_TRC_CT,
- "Skip1 GID_FTrsp: "
- "did:x%x flg:x%x cnt:%d",
- Did, vport->fc_flag,
- vport->fc_rscn_id_cnt);
-
- lpfc_printf_vlog(vport,
- KERN_INFO,
- LOG_DISCOVERY,
- "0239 Skip x%x "
- "NameServer Rsp Data: "
- "x%x x%x\n",
- Did, vport->fc_flag,
- vport->fc_rscn_id_cnt);
- }
-
- } else {
- if (!(vport->fc_flag & FC_RSCN_MODE) ||
- (lpfc_rscn_payload_check(vport, Did))) {
- lpfc_debugfs_disc_trc(vport,
- LPFC_DISC_TRC_CT,
- "Query GID_FTrsp: "
- "did:x%x flg:x%x cnt:%d",
- Did, vport->fc_flag,
- vport->fc_rscn_id_cnt);
-
- /* This NPortID was previously
- * a FCP target, * Don't even
- * bother to send GFF_ID.
- */
- ndlp = lpfc_findnode_did(vport,
- Did);
- if (ndlp &&
- NLP_CHK_NODE_ACT(ndlp)
- && (ndlp->nlp_type &
- NLP_FCP_TARGET))
- lpfc_setup_disc_node
- (vport, Did);
- else if (lpfc_ns_cmd(vport,
- SLI_CTNS_GFF_ID,
- 0, Did) == 0)
- vport->num_disc_nodes++;
- else
- lpfc_setup_disc_node
- (vport, Did);
- }
- else {
- lpfc_debugfs_disc_trc(vport,
- LPFC_DISC_TRC_CT,
- "Skip2 GID_FTrsp: "
- "did:x%x flg:x%x cnt:%d",
- Did, vport->fc_flag,
- vport->fc_rscn_id_cnt);
-
- lpfc_printf_vlog(vport,
- KERN_INFO,
- LOG_DISCOVERY,
- "0245 Skip x%x "
- "NameServer Rsp Data: "
- "x%x x%x\n",
- Did, vport->fc_flag,
- vport->fc_rscn_id_cnt);
- }
- }
- }
- if (CTentry & (be32_to_cpu(SLI_CT_LAST_ENTRY)))
+ lpfc_ns_rsp_audit_did(vport, Did, fc4_type);
+ if (CTentry & (cpu_to_be32(SLI_CT_LAST_ENTRY)))
goto nsout1;
- Cnt -= sizeof (uint32_t);
+
+ Cnt -= sizeof(uint32_t);
}
ctptr = NULL;
}
+ /* All GID_FT entries processed. If the driver is running in
+ * in target mode, put impacted nodes into recovery and drop
+ * the RPI to flush outstanding IO.
+ */
+ if (vport->phba->nvmet_support) {
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ if (!test_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag))
+ continue;
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RECOVERY);
+ clear_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag);
+ }
+ }
+
nsout1:
list_del(&head);
return 0;
@@ -585,31 +910,37 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- IOCB_t *irsp;
- struct lpfc_dmabuf *bmp;
struct lpfc_dmabuf *outp;
+ struct lpfc_dmabuf *inp;
struct lpfc_sli_ct_request *CTrsp;
+ struct lpfc_sli_ct_request *CTreq;
struct lpfc_nodelist *ndlp;
- int rc;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
+ int rc, type;
/* First save ndlp, before we overwrite it */
- ndlp = cmdiocb->context_un.ndlp;
+ ndlp = cmdiocb->ndlp;
/* we pass cmdiocb to state machine which needs rspiocb as well */
- cmdiocb->context_un.rsp_iocb = rspiocb;
-
- outp = (struct lpfc_dmabuf *) cmdiocb->context2;
- bmp = (struct lpfc_dmabuf *) cmdiocb->context3;
- irsp = &rspiocb->iocb;
+ cmdiocb->rsp_iocb = rspiocb;
+ inp = cmdiocb->cmd_dmabuf;
+ outp = cmdiocb->rsp_dmabuf;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
"GID_FT cmpl: status:x%x/x%x rtry:%d",
- irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_ns_retry);
+ ulp_status, ulp_word4, vport->fc_ns_retry);
+
+ /* Ignore response if link flipped after this request was made */
+ if (cmdiocb->event_tag != phba->fc_eventTag) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "9043 Event tag mismatch. Ignoring NS rsp\n");
+ goto out;
+ }
- /* Don't bother processing response if vport is being torn down. */
- if (vport->load_flag & FC_UNLOADING) {
- if (vport->fc_flag & FC_RSCN_MODE)
+ /* Skip processing response on pport if unloading */
+ if (vport == phba->pport && test_bit(FC_UNLOADING, &vport->load_flag)) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport);
goto out;
}
@@ -617,79 +948,123 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if (lpfc_els_chk_latt(vport)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0216 Link event during NS query\n");
- if (vport->fc_flag & FC_RSCN_MODE)
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
goto out;
}
- if (lpfc_error_lost_link(irsp)) {
+ if (lpfc_error_lost_link(vport, ulp_status, ulp_word4)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0226 NS query failed due to link event\n");
- if (vport->fc_flag & FC_RSCN_MODE)
+ "0226 NS query failed due to link event: "
+ "ulp_status x%x ulp_word4 x%x fc_flag x%lx "
+ "port_state x%x gidft_inp x%x\n",
+ ulp_status, ulp_word4, vport->fc_flag,
+ vport->port_state, vport->gidft_inp);
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport);
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
goto out;
}
- if (irsp->ulpStatus) {
+
+ if (test_and_clear_bit(FC_RSCN_DEFERRED, &vport->fc_flag)) {
+ /* This is a GID_FT completing so the gidft_inp counter was
+ * incremented before the GID_FT was issued to the wire.
+ */
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
+
+ /*
+ * Skip processing the NS response
+ * Re-issue the NS cmd
+ */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "0151 Process Deferred RSCN Data: x%lx x%x\n",
+ vport->fc_flag, vport->fc_rscn_id_cnt);
+ lpfc_els_handle_rscn(vport);
+
+ goto out;
+ }
+
+ if (ulp_status) {
/* Check for retry */
if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) {
- if (irsp->ulpStatus != IOSTAT_LOCAL_REJECT ||
- irsp->un.ulpWord[4] != IOERR_NO_RESOURCES)
+ if (ulp_status != IOSTAT_LOCAL_REJECT ||
+ (ulp_word4 & IOERR_PARAM_MASK) !=
+ IOERR_NO_RESOURCES)
vport->fc_ns_retry++;
+ type = lpfc_get_gidft_type(vport, cmdiocb);
+ if (type == 0)
+ goto out;
+
/* CT command is being retried */
rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT,
- vport->fc_ns_retry, 0);
+ vport->fc_ns_retry, type);
if (rc == 0)
goto out;
+ else { /* Unable to send NS cmd */
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
+ }
}
- if (vport->fc_flag & FC_RSCN_MODE)
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0257 GID_FT Query error: 0x%x 0x%x\n",
- irsp->ulpStatus, vport->fc_ns_retry);
+ ulp_status, vport->fc_ns_retry);
} else {
/* Good status, continue checking */
+ CTreq = (struct lpfc_sli_ct_request *) inp->virt;
CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_ACC)) {
+ cpu_to_be16(SLI_CT_RESPONSE_FS_ACC)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0208 NameServer Rsp Data: x%x\n",
- vport->fc_flag);
- lpfc_ns_rsp(vport, outp,
- (uint32_t) (irsp->un.genreq64.bdl.bdeSize));
- } else if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) {
+ "0208 NameServer Rsp Data: x%lx x%x "
+ "x%x x%x sz x%x\n",
+ vport->fc_flag,
+ CTreq->un.gid.Fc4Type,
+ vport->num_disc_nodes,
+ vport->gidft_inp,
+ get_job_data_placed(phba, rspiocb));
+
+ lpfc_ns_rsp(vport,
+ outp,
+ CTreq->un.gid.Fc4Type,
+ get_job_data_placed(phba, rspiocb));
+ } else if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_RJT) {
/* NameServer Rsp Error */
if ((CTrsp->ReasonCode == SLI_CT_UNABLE_TO_PERFORM_REQ)
&& (CTrsp->Explanation == SLI_CT_NO_FC4_TYPES)) {
lpfc_printf_vlog(vport, KERN_INFO,
LOG_DISCOVERY,
"0269 No NameServer Entries "
- "Data: x%x x%x x%x x%x\n",
- CTrsp->CommandResponse.bits.CmdRsp,
+ "Data: x%x x%x x%x x%lx\n",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
(uint32_t) CTrsp->ReasonCode,
(uint32_t) CTrsp->Explanation,
vport->fc_flag);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
"GID_FT no entry cmd:x%x rsn:x%x exp:x%x",
- (uint32_t)CTrsp->CommandResponse.bits.CmdRsp,
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
(uint32_t) CTrsp->ReasonCode,
(uint32_t) CTrsp->Explanation);
} else {
lpfc_printf_vlog(vport, KERN_INFO,
LOG_DISCOVERY,
"0240 NameServer Rsp Error "
- "Data: x%x x%x x%x x%x\n",
- CTrsp->CommandResponse.bits.CmdRsp,
+ "Data: x%x x%x x%x x%lx\n",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
(uint32_t) CTrsp->ReasonCode,
(uint32_t) CTrsp->Explanation,
vport->fc_flag);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
"GID_FT rsp err1 cmd:x%x rsn:x%x exp:x%x",
- (uint32_t)CTrsp->CommandResponse.bits.CmdRsp,
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
(uint32_t) CTrsp->ReasonCode,
(uint32_t) CTrsp->Explanation);
}
@@ -697,75 +1072,308 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
} else {
/* NameServer Rsp Error */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0241 NameServer Rsp Error "
- "Data: x%x x%x x%x x%x\n",
- CTrsp->CommandResponse.bits.CmdRsp,
+ "Data: x%x x%x x%x x%lx\n",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
(uint32_t) CTrsp->ReasonCode,
(uint32_t) CTrsp->Explanation,
vport->fc_flag);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
"GID_FT rsp err2 cmd:x%x rsn:x%x exp:x%x",
- (uint32_t)CTrsp->CommandResponse.bits.CmdRsp,
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
(uint32_t) CTrsp->ReasonCode,
(uint32_t) CTrsp->Explanation);
}
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
}
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "4216 GID_FT cmpl inp %d disc %d\n",
+ vport->gidft_inp, vport->num_disc_nodes);
+
/* Link up / RSCN discovery */
- if (vport->num_disc_nodes == 0) {
+ if ((vport->num_disc_nodes == 0) &&
+ (vport->gidft_inp == 0)) {
/*
* The driver has cycled through all Nports in the RSCN payload.
* Complete the handling by cleaning up and marking the
* current driver state.
*/
if (vport->port_state >= LPFC_DISC_AUTH) {
- if (vport->fc_flag & FC_RSCN_MODE) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
lpfc_els_flush_rscn(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_RSCN_MODE; /* RSCN still */
- spin_unlock_irq(shost->host_lock);
- }
- else
+ /* RSCN still */
+ set_bit(FC_RSCN_MODE, &vport->fc_flag);
+ } else {
lpfc_els_flush_rscn(vport);
+ }
}
lpfc_disc_start(vport);
}
out:
- cmdiocb->context_un.ndlp = ndlp; /* Now restore ndlp for free */
lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
return;
}
static void
+lpfc_cmpl_ct_cmd_gid_pt(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_vport *vport = cmdiocb->vport;
+ struct lpfc_dmabuf *outp;
+ struct lpfc_dmabuf *inp;
+ struct lpfc_sli_ct_request *CTrsp;
+ struct lpfc_sli_ct_request *CTreq;
+ struct lpfc_nodelist *ndlp;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
+ int rc;
+
+ /* First save ndlp, before we overwrite it */
+ ndlp = cmdiocb->ndlp;
+
+ /* we pass cmdiocb to state machine which needs rspiocb as well */
+ cmdiocb->rsp_iocb = rspiocb;
+ inp = cmdiocb->cmd_dmabuf;
+ outp = cmdiocb->rsp_dmabuf;
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
+ "GID_PT cmpl: status:x%x/x%x rtry:%d",
+ ulp_status, ulp_word4,
+ vport->fc_ns_retry);
+
+ /* Ignore response if link flipped after this request was made */
+ if (cmdiocb->event_tag != phba->fc_eventTag) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "9044 Event tag mismatch. Ignoring NS rsp\n");
+ goto out;
+ }
+
+ /* Skip processing response on pport if unloading */
+ if (vport == phba->pport && test_bit(FC_UNLOADING, &vport->load_flag)) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
+ lpfc_els_flush_rscn(vport);
+ goto out;
+ }
+
+ if (lpfc_els_chk_latt(vport)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "4108 Link event during NS query\n");
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
+ lpfc_els_flush_rscn(vport);
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ goto out;
+ }
+ if (lpfc_error_lost_link(vport, ulp_status, ulp_word4)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "4166 NS query failed due to link event: "
+ "ulp_status x%x ulp_word4 x%x fc_flag x%lx "
+ "port_state x%x gidft_inp x%x\n",
+ ulp_status, ulp_word4, vport->fc_flag,
+ vport->port_state, vport->gidft_inp);
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
+ lpfc_els_flush_rscn(vport);
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
+ goto out;
+ }
+
+ if (test_and_clear_bit(FC_RSCN_DEFERRED, &vport->fc_flag)) {
+ /* This is a GID_PT completing so the gidft_inp counter was
+ * incremented before the GID_PT was issued to the wire.
+ */
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
+
+ /*
+ * Skip processing the NS response
+ * Re-issue the NS cmd
+ */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "4167 Process Deferred RSCN Data: x%lx x%x\n",
+ vport->fc_flag, vport->fc_rscn_id_cnt);
+ lpfc_els_handle_rscn(vport);
+
+ goto out;
+ }
+
+ if (ulp_status) {
+ /* Check for retry */
+ if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) {
+ if (ulp_status != IOSTAT_LOCAL_REJECT ||
+ (ulp_word4 & IOERR_PARAM_MASK) !=
+ IOERR_NO_RESOURCES)
+ vport->fc_ns_retry++;
+
+ /* CT command is being retried */
+ rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_PT,
+ vport->fc_ns_retry, GID_PT_N_PORT);
+ if (rc == 0)
+ goto out;
+ else { /* Unable to send NS cmd */
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
+ }
+ }
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
+ lpfc_els_flush_rscn(vport);
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "4103 GID_FT Query error: 0x%x 0x%x\n",
+ ulp_status, vport->fc_ns_retry);
+ } else {
+ /* Good status, continue checking */
+ CTreq = (struct lpfc_sli_ct_request *)inp->virt;
+ CTrsp = (struct lpfc_sli_ct_request *)outp->virt;
+ if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "4105 NameServer Rsp Data: x%lx x%x "
+ "x%x x%x sz x%x\n",
+ vport->fc_flag,
+ CTreq->un.gid.Fc4Type,
+ vport->num_disc_nodes,
+ vport->gidft_inp,
+ get_job_data_placed(phba, rspiocb));
+
+ lpfc_ns_rsp(vport,
+ outp,
+ CTreq->un.gid.Fc4Type,
+ get_job_data_placed(phba, rspiocb));
+ } else if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_RJT) {
+ /* NameServer Rsp Error */
+ if ((CTrsp->ReasonCode == SLI_CT_UNABLE_TO_PERFORM_REQ)
+ && (CTrsp->Explanation == SLI_CT_NO_FC4_TYPES)) {
+ lpfc_printf_vlog(
+ vport, KERN_INFO, LOG_DISCOVERY,
+ "4106 No NameServer Entries "
+ "Data: x%x x%x x%x x%lx\n",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
+ (uint32_t)CTrsp->ReasonCode,
+ (uint32_t)CTrsp->Explanation,
+ vport->fc_flag);
+
+ lpfc_debugfs_disc_trc(
+ vport, LPFC_DISC_TRC_CT,
+ "GID_PT no entry cmd:x%x rsn:x%x exp:x%x",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
+ (uint32_t)CTrsp->ReasonCode,
+ (uint32_t)CTrsp->Explanation);
+ } else {
+ lpfc_printf_vlog(
+ vport, KERN_INFO, LOG_DISCOVERY,
+ "4107 NameServer Rsp Error "
+ "Data: x%x x%x x%x x%lx\n",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
+ (uint32_t)CTrsp->ReasonCode,
+ (uint32_t)CTrsp->Explanation,
+ vport->fc_flag);
+
+ lpfc_debugfs_disc_trc(
+ vport, LPFC_DISC_TRC_CT,
+ "GID_PT rsp err1 cmd:x%x rsn:x%x exp:x%x",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
+ (uint32_t)CTrsp->ReasonCode,
+ (uint32_t)CTrsp->Explanation);
+ }
+ } else {
+ /* NameServer Rsp Error */
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "4109 NameServer Rsp Error "
+ "Data: x%x x%x x%x x%lx\n",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
+ (uint32_t)CTrsp->ReasonCode,
+ (uint32_t)CTrsp->Explanation,
+ vport->fc_flag);
+
+ lpfc_debugfs_disc_trc(
+ vport, LPFC_DISC_TRC_CT,
+ "GID_PT rsp err2 cmd:x%x rsn:x%x exp:x%x",
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
+ (uint32_t)CTrsp->ReasonCode,
+ (uint32_t)CTrsp->Explanation);
+ }
+ if (vport->gidft_inp)
+ vport->gidft_inp--;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6450 GID_PT cmpl inp %d disc %d\n",
+ vport->gidft_inp, vport->num_disc_nodes);
+
+ /* Link up / RSCN discovery */
+ if ((vport->num_disc_nodes == 0) &&
+ (vport->gidft_inp == 0)) {
+ /*
+ * The driver has cycled through all Nports in the RSCN payload.
+ * Complete the handling by cleaning up and marking the
+ * current driver state.
+ */
+ if (vport->port_state >= LPFC_DISC_AUTH) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
+ lpfc_els_flush_rscn(vport);
+ /* RSCN still */
+ set_bit(FC_RSCN_MODE, &vport->fc_flag);
+ } else {
+ lpfc_els_flush_rscn(vport);
+ }
+ }
+
+ lpfc_disc_start(vport);
+ }
+out:
+ lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
+}
+
+static void
lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- IOCB_t *irsp = &rspiocb->iocb;
- struct lpfc_dmabuf *inp = (struct lpfc_dmabuf *) cmdiocb->context1;
- struct lpfc_dmabuf *outp = (struct lpfc_dmabuf *) cmdiocb->context2;
+ struct lpfc_dmabuf *inp = cmdiocb->cmd_dmabuf;
+ struct lpfc_dmabuf *outp = cmdiocb->rsp_dmabuf;
struct lpfc_sli_ct_request *CTrsp;
int did, rc, retry;
uint8_t fbits;
- struct lpfc_nodelist *ndlp;
+ struct lpfc_nodelist *ndlp = NULL, *free_ndlp = NULL;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
did = ((struct lpfc_sli_ct_request *) inp->virt)->un.gff.PortId;
did = be32_to_cpu(did);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
"GFF_ID cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4], did);
+ ulp_status, ulp_word4, did);
- if (irsp->ulpStatus == IOSTAT_SUCCESS) {
+ /* Ignore response if link flipped after this request was made */
+ if (cmdiocb->event_tag != phba->fc_eventTag) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "9045 Event tag mismatch. Ignoring NS rsp\n");
+ goto iocb_free;
+ }
+
+ if (ulp_status == IOSTAT_SUCCESS) {
/* Good status, continue checking */
CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
fbits = CTrsp->un.gff_acc.fbits[FCP_TYPE_FEATURE_OFFSET];
- if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_ACC)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6431 Process GFF_ID rsp for %08x "
+ "fbits %02x %s %s\n",
+ did, fbits,
+ (fbits & FC4_FEATURE_INIT) ? "Initiator" : " ",
+ (fbits & FC4_FEATURE_TARGET) ? "Target" : " ");
+
+ if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC) {
if ((fbits & FC4_FEATURE_INIT) &&
!(fbits & FC4_FEATURE_TARGET)) {
lpfc_printf_vlog(vport, KERN_INFO,
@@ -782,8 +1390,10 @@ lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/* Check for retry */
if (cmdiocb->retry < LPFC_MAX_NS_RETRY) {
retry = 1;
- if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- switch (irsp->un.ulpWord[4]) {
+ if (ulp_status == IOSTAT_LOCAL_REJECT) {
+ switch ((ulp_word4 &
+ IOERR_PARAM_MASK)) {
+
case IOERR_NO_RESOURCES:
/* We don't increment the retry
* count for this case.
@@ -807,36 +1417,43 @@ lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
cmdiocb->retry, did);
if (rc == 0) {
/* success */
+ free_ndlp = cmdiocb->ndlp;
lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(free_ndlp);
return;
}
}
}
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0267 NameServer GFF Rsp "
- "x%x Error (%d %d) Data: x%x x%x\n",
- did, irsp->ulpStatus, irsp->un.ulpWord[4],
+ "x%x Error (%d %d) Data: x%lx x%x\n",
+ did, ulp_status, ulp_word4,
vport->fc_flag, vport->fc_rscn_id_cnt);
}
/* This is a target port, unregistered port, or the GFF_ID failed */
ndlp = lpfc_setup_disc_node(vport, did);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
+ if (ndlp) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0242 Process x%x GFF "
- "NameServer Rsp Data: x%x x%x x%x\n",
+ "NameServer Rsp Data: x%lx x%lx x%x\n",
did, ndlp->nlp_flag, vport->fc_flag,
vport->fc_rscn_id_cnt);
} else {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0243 Skip x%x GFF "
- "NameServer Rsp Data: x%x x%x\n", did,
+ "NameServer Rsp Data: x%lx x%x\n", did,
vport->fc_flag, vport->fc_rscn_id_cnt);
}
out:
/* Link up / RSCN discovery */
if (vport->num_disc_nodes)
vport->num_disc_nodes--;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6451 GFF_ID cmpl inp %d disc %d\n",
+ vport->gidft_inp, vport->num_disc_nodes);
+
if (vport->num_disc_nodes == 0) {
/*
* The driver has cycled through all Nports in the RSCN payload.
@@ -844,21 +1461,120 @@ out:
* current driver state.
*/
if (vport->port_state >= LPFC_DISC_AUTH) {
- if (vport->fc_flag & FC_RSCN_MODE) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
lpfc_els_flush_rscn(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_RSCN_MODE; /* RSCN still */
- spin_unlock_irq(shost->host_lock);
- }
- else
+ /* RSCN still */
+ set_bit(FC_RSCN_MODE, &vport->fc_flag);
+ } else {
lpfc_els_flush_rscn(vport);
+ }
}
lpfc_disc_start(vport);
}
+
+iocb_free:
+ free_ndlp = cmdiocb->ndlp;
lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(free_ndlp);
return;
}
+static void
+lpfc_cmpl_ct_cmd_gft_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_vport *vport = cmdiocb->vport;
+ struct lpfc_dmabuf *inp = cmdiocb->cmd_dmabuf;
+ struct lpfc_dmabuf *outp = cmdiocb->rsp_dmabuf;
+ struct lpfc_sli_ct_request *CTrsp;
+ int did;
+ struct lpfc_nodelist *ndlp = NULL;
+ struct lpfc_nodelist *ns_ndlp = cmdiocb->ndlp;
+ uint32_t fc4_data_0, fc4_data_1;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
+
+ did = ((struct lpfc_sli_ct_request *)inp->virt)->un.gft.PortId;
+ did = be32_to_cpu(did);
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
+ "GFT_ID cmpl: status:x%x/x%x did:x%x",
+ ulp_status, ulp_word4, did);
+
+ /* Ignore response if link flipped after this request was made */
+ if ((uint32_t)cmdiocb->event_tag != phba->fc_eventTag) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "9046 Event tag mismatch. Ignoring NS rsp\n");
+ goto out;
+ }
+
+ if (ulp_status == IOSTAT_SUCCESS) {
+ /* Good status, continue checking */
+ CTrsp = (struct lpfc_sli_ct_request *)outp->virt;
+ fc4_data_0 = be32_to_cpu(CTrsp->un.gft_acc.fc4_types[0]);
+ fc4_data_1 = be32_to_cpu(CTrsp->un.gft_acc.fc4_types[1]);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6432 Process GFT_ID rsp for %08x "
+ "Data %08x %08x %s %s\n",
+ did, fc4_data_0, fc4_data_1,
+ (fc4_data_0 & LPFC_FC4_TYPE_BITMASK) ?
+ "FCP" : " ",
+ (fc4_data_1 & LPFC_FC4_TYPE_BITMASK) ?
+ "NVME" : " ");
+
+ /* Lookup the NPort_ID queried in the GFT_ID and find the
+ * driver's local node. It's an error if the driver
+ * doesn't have one.
+ */
+ ndlp = lpfc_findnode_did(vport, did);
+ if (ndlp) {
+ /* The bitmask value for FCP and NVME FCP types is
+ * the same because they are 32 bits distant from
+ * each other in word0 and word0.
+ */
+ if (fc4_data_0 & LPFC_FC4_TYPE_BITMASK)
+ ndlp->nlp_fc4_type |= NLP_FC4_FCP;
+ if (fc4_data_1 & LPFC_FC4_TYPE_BITMASK)
+ ndlp->nlp_fc4_type |= NLP_FC4_NVME;
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY | LOG_NODE,
+ "3064 Setting ndlp x%px, DID x%06x "
+ "with FC4 x%08x, Data: x%08x x%08x "
+ "%d\n",
+ ndlp, did, ndlp->nlp_fc4_type,
+ FC_TYPE_FCP, FC_TYPE_NVME,
+ ndlp->nlp_state);
+
+ if (ndlp->nlp_state == NLP_STE_REG_LOGIN_ISSUE &&
+ ndlp->nlp_fc4_type) {
+ ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
+ lpfc_nlp_set_state(vport, ndlp,
+ NLP_STE_PRLI_ISSUE);
+ lpfc_issue_els_prli(vport, ndlp, 0);
+ } else if (!ndlp->nlp_fc4_type) {
+ /* If fc4 type is still unknown, then LOGO */
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY | LOG_NODE,
+ "6443 Sending LOGO ndlp x%px, "
+ "DID x%06x with fc4_type: "
+ "x%08x, state: %d\n",
+ ndlp, did, ndlp->nlp_fc4_type,
+ ndlp->nlp_state);
+ lpfc_issue_els_logo(vport, ndlp, 0);
+ ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
+ lpfc_nlp_set_state(vport, ndlp,
+ NLP_STE_NPR_NODE);
+ }
+ }
+ } else
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_DISCOVERY,
+ "3065 GFT_ID status x%08x\n", ulp_status);
+
+out:
+ lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ns_ndlp);
+}
static void
lpfc_cmpl_ct(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
@@ -867,22 +1583,22 @@ lpfc_cmpl_ct(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_vport *vport = cmdiocb->vport;
struct lpfc_dmabuf *inp;
struct lpfc_dmabuf *outp;
- IOCB_t *irsp;
struct lpfc_sli_ct_request *CTrsp;
struct lpfc_nodelist *ndlp;
int cmdcode, rc;
uint8_t retry;
uint32_t latt;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
/* First save ndlp, before we overwrite it */
- ndlp = cmdiocb->context_un.ndlp;
+ ndlp = cmdiocb->ndlp;
/* we pass cmdiocb to state machine which needs rspiocb as well */
- cmdiocb->context_un.rsp_iocb = rspiocb;
+ cmdiocb->rsp_iocb = rspiocb;
- inp = (struct lpfc_dmabuf *) cmdiocb->context1;
- outp = (struct lpfc_dmabuf *) cmdiocb->context2;
- irsp = &rspiocb->iocb;
+ inp = cmdiocb->cmd_dmabuf;
+ outp = cmdiocb->rsp_dmabuf;
cmdcode = be16_to_cpu(((struct lpfc_sli_ct_request *) inp->virt)->
CommandResponse.bits.CmdRsp);
@@ -890,26 +1606,28 @@ lpfc_cmpl_ct(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
latt = lpfc_els_chk_latt(vport);
- /* RFT request completes status <ulpStatus> CmdRsp <CmdRsp> */
+ /* RFT request completes status <ulp_status> CmdRsp <CmdRsp> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0209 CT Request completes, latt %d, "
- "ulpStatus x%x CmdRsp x%x, Context x%x, Tag x%x\n",
- latt, irsp->ulpStatus,
- CTrsp->CommandResponse.bits.CmdRsp,
- cmdiocb->iocb.ulpContext, cmdiocb->iocb.ulpIoTag);
+ "ulp_status x%x CmdRsp x%x, Context x%x, Tag x%x\n",
+ latt, ulp_status,
+ be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp),
+ get_job_ulpcontext(phba, cmdiocb), cmdiocb->iotag);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
"CT cmd cmpl: status:x%x/x%x cmd:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4], cmdcode);
-
- if (irsp->ulpStatus) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
- "0268 NS cmd %x Error (%d %d)\n",
- cmdcode, irsp->ulpStatus, irsp->un.ulpWord[4]);
-
- if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- ((irsp->un.ulpWord[4] == IOERR_SLI_DOWN) ||
- (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED)))
+ ulp_status, ulp_word4, cmdcode);
+
+ if (ulp_status) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0268 NS cmd x%x Error (x%x x%x)\n",
+ cmdcode, ulp_status, ulp_word4);
+
+ if (ulp_status == IOSTAT_LOCAL_REJECT &&
+ (((ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_SLI_DOWN) ||
+ ((ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_SLI_ABORTED)))
goto out;
retry = cmdiocb->retry;
@@ -925,8 +1643,18 @@ lpfc_cmpl_ct(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
out:
- cmdiocb->context_un.ndlp = ndlp; /* Now restore ndlp for free */
+ /* If the caller wanted a synchronous DA_ID completion, signal the
+ * wait obj and clear flag to reset the vport.
+ */
+ if (test_bit(NLP_WAIT_FOR_DA_ID, &ndlp->save_flags)) {
+ if (ndlp->da_id_waitq)
+ wake_up(ndlp->da_id_waitq);
+ }
+
+ clear_bit(NLP_WAIT_FOR_DA_ID, &ndlp->save_flags);
+
lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
return;
}
@@ -934,17 +1662,17 @@ static void
lpfc_cmpl_ct_cmd_rft_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- IOCB_t *irsp = &rspiocb->iocb;
struct lpfc_vport *vport = cmdiocb->vport;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
- if (irsp->ulpStatus == IOSTAT_SUCCESS) {
+ if (ulp_status == IOSTAT_SUCCESS) {
struct lpfc_dmabuf *outp;
struct lpfc_sli_ct_request *CTrsp;
- outp = (struct lpfc_dmabuf *) cmdiocb->context2;
- CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
- if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_ACC))
+ outp = cmdiocb->rsp_dmabuf;
+ CTrsp = (struct lpfc_sli_ct_request *)outp->virt;
+ if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC)
vport->ct_flags |= FC_CT_RFT_ID;
}
lpfc_cmpl_ct(phba, cmdiocb, rspiocb);
@@ -955,17 +1683,17 @@ static void
lpfc_cmpl_ct_cmd_rnn_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- IOCB_t *irsp = &rspiocb->iocb;
struct lpfc_vport *vport = cmdiocb->vport;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
- if (irsp->ulpStatus == IOSTAT_SUCCESS) {
+ if (ulp_status == IOSTAT_SUCCESS) {
struct lpfc_dmabuf *outp;
struct lpfc_sli_ct_request *CTrsp;
- outp = (struct lpfc_dmabuf *) cmdiocb->context2;
+ outp = cmdiocb->rsp_dmabuf;
CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
- if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_ACC))
+ if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC)
vport->ct_flags |= FC_CT_RNN_ID;
}
lpfc_cmpl_ct(phba, cmdiocb, rspiocb);
@@ -976,17 +1704,17 @@ static void
lpfc_cmpl_ct_cmd_rspn_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- IOCB_t *irsp = &rspiocb->iocb;
struct lpfc_vport *vport = cmdiocb->vport;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
- if (irsp->ulpStatus == IOSTAT_SUCCESS) {
+ if (ulp_status == IOSTAT_SUCCESS) {
struct lpfc_dmabuf *outp;
struct lpfc_sli_ct_request *CTrsp;
- outp = (struct lpfc_dmabuf *) cmdiocb->context2;
- CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
- if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_ACC))
+ outp = cmdiocb->rsp_dmabuf;
+ CTrsp = (struct lpfc_sli_ct_request *)outp->virt;
+ if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC)
vport->ct_flags |= FC_CT_RSPN_ID;
}
lpfc_cmpl_ct(phba, cmdiocb, rspiocb);
@@ -997,17 +1725,17 @@ static void
lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- IOCB_t *irsp = &rspiocb->iocb;
struct lpfc_vport *vport = cmdiocb->vport;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
- if (irsp->ulpStatus == IOSTAT_SUCCESS) {
+ if (ulp_status == IOSTAT_SUCCESS) {
struct lpfc_dmabuf *outp;
struct lpfc_sli_ct_request *CTrsp;
- outp = (struct lpfc_dmabuf *) cmdiocb->context2;
+ outp = cmdiocb->rsp_dmabuf;
CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
- if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_ACC))
+ if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC)
vport->ct_flags |= FC_CT_RSNN_NN;
}
lpfc_cmpl_ct(phba, cmdiocb, rspiocb);
@@ -1015,6 +1743,28 @@ lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
static void
+lpfc_cmpl_ct_cmd_rspni_pni(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_vport *vport;
+ struct lpfc_dmabuf *outp;
+ struct lpfc_sli_ct_request *ctrsp;
+ u32 ulp_status;
+
+ vport = cmdiocb->vport;
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+
+ if (ulp_status == IOSTAT_SUCCESS) {
+ outp = cmdiocb->rsp_dmabuf;
+ ctrsp = (struct lpfc_sli_ct_request *)outp->virt;
+ if (be16_to_cpu(ctrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC)
+ vport->ct_flags |= FC_CT_RSPNI_PNI;
+ }
+ lpfc_cmpl_ct(phba, cmdiocb, rspiocb);
+}
+
+static void
lpfc_cmpl_ct_cmd_da_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
@@ -1030,60 +1780,117 @@ static void
lpfc_cmpl_ct_cmd_rff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- IOCB_t *irsp = &rspiocb->iocb;
struct lpfc_vport *vport = cmdiocb->vport;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
- if (irsp->ulpStatus == IOSTAT_SUCCESS) {
+ if (ulp_status == IOSTAT_SUCCESS) {
struct lpfc_dmabuf *outp;
struct lpfc_sli_ct_request *CTrsp;
- outp = (struct lpfc_dmabuf *) cmdiocb->context2;
- CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
- if (CTrsp->CommandResponse.bits.CmdRsp ==
- be16_to_cpu(SLI_CT_RESPONSE_FS_ACC))
+ outp = cmdiocb->rsp_dmabuf;
+ CTrsp = (struct lpfc_sli_ct_request *)outp->virt;
+ if (be16_to_cpu(CTrsp->CommandResponse.bits.CmdRsp) ==
+ SLI_CT_RESPONSE_FS_ACC)
vport->ct_flags |= FC_CT_RFF_ID;
}
lpfc_cmpl_ct(phba, cmdiocb, rspiocb);
return;
}
+/*
+ * Although the symbolic port name is thought to be an integer
+ * as of January 18, 2016, leave it as a string until more of
+ * the record state becomes defined.
+ */
int
lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol,
size_t size)
{
int n;
- uint8_t *wwn = vport->phba->wwpn;
- n = snprintf(symbol, size,
- "Emulex PPN-%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
- wwn[0], wwn[1], wwn[2], wwn[3],
- wwn[4], wwn[5], wwn[6], wwn[7]);
-
- if (vport->port_type == LPFC_PHYSICAL_PORT)
- return n;
-
- if (n < size)
- n += snprintf(symbol + n, size - n, " VPort-%d", vport->vpi);
-
- if (n < size &&
- strlen(vport->fc_vport->symbolic_name))
- n += snprintf(symbol + n, size - n, " VName-%s",
- vport->fc_vport->symbolic_name);
+ /*
+ * Use the lpfc board number as the Symbolic Port
+ * Name object. NPIV is not in play so this integer
+ * value is sufficient and unique per FC-ID.
+ */
+ n = scnprintf(symbol, size, "%d", vport->phba->brd_no);
return n;
}
+
int
lpfc_vport_symbolic_node_name(struct lpfc_vport *vport, char *symbol,
size_t size)
{
- char fwrev[16];
- int n;
+ char fwrev[FW_REV_STR_SIZE] = {0};
+ char tmp[MAXHOSTNAMELEN] = {0};
+
+ memset(symbol, 0, size);
+
+ scnprintf(tmp, sizeof(tmp), "Emulex %s", vport->phba->ModelName);
+ if (strlcat(symbol, tmp, size) >= size)
+ goto buffer_done;
lpfc_decode_firmware_rev(vport->phba, fwrev, 0);
+ scnprintf(tmp, sizeof(tmp), " FV%s", fwrev);
+ if (strlcat(symbol, tmp, size) >= size)
+ goto buffer_done;
- n = snprintf(symbol, size, "Emulex %s FV%s DV%s",
- vport->phba->ModelName, fwrev, lpfc_release_version);
- return n;
+ scnprintf(tmp, sizeof(tmp), " DV%s", lpfc_release_version);
+ if (strlcat(symbol, tmp, size) >= size)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), " HN:%s", vport->phba->os_host_name);
+ if (strlcat(symbol, tmp, size) >= size)
+ goto buffer_done;
+
+ /* Note :- OS name is "Linux" */
+ scnprintf(tmp, sizeof(tmp), " OS:%s", init_utsname()->sysname);
+ strlcat(symbol, tmp, size);
+
+buffer_done:
+ return strnlen(symbol, size);
+
+}
+
+static uint32_t
+lpfc_find_map_node(struct lpfc_vport *vport)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+ unsigned long iflags;
+ uint32_t cnt = 0;
+
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
+ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
+ if (ndlp->nlp_type & NLP_FABRIC)
+ continue;
+ if ((ndlp->nlp_state == NLP_STE_MAPPED_NODE) ||
+ (ndlp->nlp_state == NLP_STE_UNMAPPED_NODE))
+ cnt++;
+ }
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
+ return cnt;
+}
+
+/*
+ * This routine will return the FC4 Type associated with the CT
+ * GID_FT command.
+ */
+int
+lpfc_get_gidft_type(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb)
+{
+ struct lpfc_sli_ct_request *CtReq;
+ struct lpfc_dmabuf *mp;
+ uint32_t type;
+
+ mp = cmdiocb->cmd_dmabuf;
+ if (mp == NULL)
+ return 0;
+ CtReq = (struct lpfc_sli_ct_request *)mp->virt;
+ type = (uint32_t)CtReq->un.gid.Fc4Type;
+ if ((type != SLI_CTPT_FCP) && (type != SLI_CTPT_NVME))
+ return 0;
+ return type;
}
/*
@@ -1104,20 +1911,20 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
struct ulp_bde64 *bpl;
void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
struct lpfc_iocbq *) = NULL;
+ uint32_t *ptr;
uint32_t rsp_size = 1024;
size_t size;
int rc = 0;
ndlp = lpfc_findnode_did(vport, NameServer_DID);
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)
- || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) {
+ if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) {
rc=1;
goto ns_cmd_exit;
}
/* fill in BDEs for command */
/* Allocate buffer for command payload */
- mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!mp) {
rc=2;
goto ns_cmd_exit;
@@ -1131,7 +1938,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
}
/* Allocate buffer for Buffer ptr list */
- bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!bmp) {
rc=4;
goto ns_cmd_free_mpvirt;
@@ -1146,8 +1953,9 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
/* NameServer Req */
lpfc_printf_vlog(vport, KERN_INFO ,LOG_DISCOVERY,
- "0236 NameServer Req Data: x%x x%x x%x\n",
- cmdcode, vport->fc_flag, vport->fc_rscn_id_cnt);
+ "0236 NameServer Req Data: x%x x%lx x%x x%x\n",
+ cmdcode, vport->fc_flag, vport->fc_rscn_id_cnt,
+ context);
bpl = (struct ulp_bde64 *) bmp->virt;
memset(bpl, 0, sizeof(struct ulp_bde64));
@@ -1156,8 +1964,12 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
bpl->tus.f.bdeFlags = 0;
if (cmdcode == SLI_CTNS_GID_FT)
bpl->tus.f.bdeSize = GID_REQUEST_SZ;
+ else if (cmdcode == SLI_CTNS_GID_PT)
+ bpl->tus.f.bdeSize = GID_REQUEST_SZ;
else if (cmdcode == SLI_CTNS_GFF_ID)
bpl->tus.f.bdeSize = GFF_REQUEST_SZ;
+ else if (cmdcode == SLI_CTNS_GFT_ID)
+ bpl->tus.f.bdeSize = GFT_REQUEST_SZ;
else if (cmdcode == SLI_CTNS_RFT_ID)
bpl->tus.f.bdeSize = RFT_REQUEST_SZ;
else if (cmdcode == SLI_CTNS_RNN_ID)
@@ -1166,6 +1978,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
bpl->tus.f.bdeSize = RSPN_REQUEST_SZ;
else if (cmdcode == SLI_CTNS_RSNN_NN)
bpl->tus.f.bdeSize = RSNN_REQUEST_SZ;
+ else if (cmdcode == SLI_CTNS_RSPNI_PNI)
+ bpl->tus.f.bdeSize = RSPNI_REQUEST_SZ;
else if (cmdcode == SLI_CTNS_DA_ID)
bpl->tus.f.bdeSize = DA_ID_REQUEST_SZ;
else if (cmdcode == SLI_CTNS_RFF_ID)
@@ -1175,7 +1989,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
bpl->tus.w = le32_to_cpu(bpl->tus.w);
CtReq = (struct lpfc_sli_ct_request *) mp->virt;
- memset(CtReq, 0, sizeof (struct lpfc_sli_ct_request));
+ memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request));
CtReq->RevisionId.bits.Revision = SLI_CT_REVISION;
CtReq->RevisionId.bits.InId = 0;
CtReq->FsType = SLI_CT_DIRECTORY_SERVICE;
@@ -1184,8 +1998,9 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
switch (cmdcode) {
case SLI_CTNS_GID_FT:
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_GID_FT);
- CtReq->un.gid.Fc4Type = SLI_CTPT_FCP;
+ cpu_to_be16(SLI_CTNS_GID_FT);
+ CtReq->un.gid.Fc4Type = context;
+
if (vport->port_state < LPFC_NS_QRY)
vport->port_state = LPFC_NS_QRY;
lpfc_set_disctmo(vport);
@@ -1193,36 +2008,80 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
rsp_size = FC_MAX_NS_RSP;
break;
+ case SLI_CTNS_GID_PT:
+ CtReq->CommandResponse.bits.CmdRsp =
+ cpu_to_be16(SLI_CTNS_GID_PT);
+ CtReq->un.gid.PortType = context;
+
+ if (vport->port_state < LPFC_NS_QRY)
+ vport->port_state = LPFC_NS_QRY;
+ lpfc_set_disctmo(vport);
+ cmpl = lpfc_cmpl_ct_cmd_gid_pt;
+ rsp_size = FC_MAX_NS_RSP;
+ break;
+
case SLI_CTNS_GFF_ID:
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_GFF_ID);
+ cpu_to_be16(SLI_CTNS_GFF_ID);
CtReq->un.gff.PortId = cpu_to_be32(context);
cmpl = lpfc_cmpl_ct_cmd_gff_id;
break;
+ case SLI_CTNS_GFT_ID:
+ CtReq->CommandResponse.bits.CmdRsp =
+ cpu_to_be16(SLI_CTNS_GFT_ID);
+ CtReq->un.gft.PortId = cpu_to_be32(context);
+ cmpl = lpfc_cmpl_ct_cmd_gft_id;
+ break;
+
case SLI_CTNS_RFT_ID:
vport->ct_flags &= ~FC_CT_RFT_ID;
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_RFT_ID);
- CtReq->un.rft.PortId = cpu_to_be32(vport->fc_myDID);
- CtReq->un.rft.fcpReg = 1;
+ cpu_to_be16(SLI_CTNS_RFT_ID);
+ CtReq->un.rft.port_id = cpu_to_be32(vport->fc_myDID);
+
+ /* Register Application Services type if vmid enabled. */
+ if (phba->cfg_vmid_app_header)
+ CtReq->un.rft.app_serv_reg =
+ cpu_to_be32(RFT_APP_SERV_REG);
+
+ /* Register FC4 FCP type if enabled. */
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH ||
+ vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)
+ CtReq->un.rft.fcp_reg = cpu_to_be32(RFT_FCP_REG);
+
+ /* Register NVME type if enabled. */
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH ||
+ vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
+ CtReq->un.rft.nvme_reg = cpu_to_be32(RFT_NVME_REG);
+
+ ptr = (uint32_t *)CtReq;
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6433 Issue RFT (%s %s %s): %08x %08x %08x "
+ "%08x %08x %08x %08x %08x\n",
+ CtReq->un.rft.fcp_reg ? "FCP" : " ",
+ CtReq->un.rft.nvme_reg ? "NVME" : " ",
+ CtReq->un.rft.app_serv_reg ? "APPS" : " ",
+ *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3),
+ *(ptr + 4), *(ptr + 5),
+ *(ptr + 6), *(ptr + 7));
cmpl = lpfc_cmpl_ct_cmd_rft_id;
break;
case SLI_CTNS_RNN_ID:
vport->ct_flags &= ~FC_CT_RNN_ID;
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_RNN_ID);
+ cpu_to_be16(SLI_CTNS_RNN_ID);
CtReq->un.rnn.PortId = cpu_to_be32(vport->fc_myDID);
memcpy(CtReq->un.rnn.wwnn, &vport->fc_nodename,
- sizeof (struct lpfc_name));
+ sizeof(struct lpfc_name));
cmpl = lpfc_cmpl_ct_cmd_rnn_id;
break;
case SLI_CTNS_RSPN_ID:
vport->ct_flags &= ~FC_CT_RSPN_ID;
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_RSPN_ID);
+ cpu_to_be16(SLI_CTNS_RSPN_ID);
CtReq->un.rspn.PortId = cpu_to_be32(vport->fc_myDID);
size = sizeof(CtReq->un.rspn.symbname);
CtReq->un.rspn.len =
@@ -1233,29 +2092,73 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
case SLI_CTNS_RSNN_NN:
vport->ct_flags &= ~FC_CT_RSNN_NN;
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_RSNN_NN);
+ cpu_to_be16(SLI_CTNS_RSNN_NN);
memcpy(CtReq->un.rsnn.wwnn, &vport->fc_nodename,
- sizeof (struct lpfc_name));
+ sizeof(struct lpfc_name));
size = sizeof(CtReq->un.rsnn.symbname);
CtReq->un.rsnn.len =
lpfc_vport_symbolic_node_name(vport,
CtReq->un.rsnn.symbname, size);
cmpl = lpfc_cmpl_ct_cmd_rsnn_nn;
break;
+ case SLI_CTNS_RSPNI_PNI:
+ vport->ct_flags &= ~FC_CT_RSPNI_PNI;
+ CtReq->CommandResponse.bits.CmdRsp =
+ cpu_to_be16(SLI_CTNS_RSPNI_PNI);
+ CtReq->un.rspni.pni = cpu_to_be64(phba->pni);
+ scnprintf(CtReq->un.rspni.symbname,
+ sizeof(CtReq->un.rspni.symbname), "OS Host Name::%s",
+ phba->os_host_name);
+ CtReq->un.rspni.len = strnlen(CtReq->un.rspni.symbname,
+ sizeof(CtReq->un.rspni.symbname));
+ cmpl = lpfc_cmpl_ct_cmd_rspni_pni;
+ break;
case SLI_CTNS_DA_ID:
/* Implement DA_ID Nameserver request */
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_DA_ID);
+ cpu_to_be16(SLI_CTNS_DA_ID);
CtReq->un.da_id.port_id = cpu_to_be32(vport->fc_myDID);
cmpl = lpfc_cmpl_ct_cmd_da_id;
break;
case SLI_CTNS_RFF_ID:
vport->ct_flags &= ~FC_CT_RFF_ID;
CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_CTNS_RFF_ID);
+ cpu_to_be16(SLI_CTNS_RFF_ID);
CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID);
CtReq->un.rff.fbits = FC4_FEATURE_INIT;
- CtReq->un.rff.type_code = FC_TYPE_FCP;
+
+ /* The driver always supports FC_TYPE_FCP. However, the
+ * caller can specify NVME (type x28) as well. But only
+ * these that FC4 type is supported.
+ */
+ if (((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) &&
+ (context == FC_TYPE_NVME)) {
+ if ((vport == phba->pport) && phba->nvmet_support) {
+ CtReq->un.rff.fbits = (FC4_FEATURE_TARGET |
+ FC4_FEATURE_NVME_DISC);
+ lpfc_nvmet_update_targetport(phba);
+ } else {
+ lpfc_nvme_update_localport(vport);
+ }
+ CtReq->un.rff.type_code = context;
+
+ } else if (((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) &&
+ (context == FC_TYPE_FCP))
+ CtReq->un.rff.type_code = context;
+
+ else
+ goto ns_cmd_free_bmpvirt;
+
+ ptr = (uint32_t *)CtReq;
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6434 Issue RFF (%s): %08x %08x %08x %08x "
+ "%08x %08x %08x %08x\n",
+ (context == FC_TYPE_NVME) ? "NVME" : "FCP",
+ *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3),
+ *(ptr + 4), *(ptr + 5),
+ *(ptr + 6), *(ptr + 7));
cmpl = lpfc_cmpl_ct_cmd_rff_id;
break;
}
@@ -1271,11 +2174,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
}
rc=6;
- /* Decrement ndlp reference count to release ndlp reference held
- * for the failed command's callback function.
- */
- lpfc_nlp_put(ndlp);
-
+ns_cmd_free_bmpvirt:
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
ns_cmd_free_bmp:
kfree(bmp);
@@ -1284,454 +2183,1262 @@ ns_cmd_free_mpvirt:
ns_cmd_free_mp:
kfree(mp);
ns_cmd_exit:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
- "0266 Issue NameServer Req x%x err %d Data: x%x x%x\n",
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0266 Issue NameServer Req x%x err %d Data: x%lx "
+ "x%x\n",
cmdcode, rc, vport->fc_flag, vport->fc_rscn_id_cnt);
return 1;
}
+/**
+ * lpfc_fdmi_rprt_defer - Check for any deferred FDMI RPRT commands
+ * @phba: Pointer to HBA context object.
+ * @mask: Initial port attributes mask
+ *
+ * This function checks to see if any vports have deferred their FDMI RPRT.
+ * A vports RPRT may be deferred if it is issued before the primary ports
+ * RHBA completes.
+ */
static void
-lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
- struct lpfc_iocbq * rspiocb)
+lpfc_fdmi_rprt_defer(struct lpfc_hba *phba, uint32_t mask)
{
- struct lpfc_dmabuf *inp = cmdiocb->context1;
- struct lpfc_dmabuf *outp = cmdiocb->context2;
- struct lpfc_sli_ct_request *CTrsp = outp->virt;
- struct lpfc_sli_ct_request *CTcmd = inp->virt;
+ struct lpfc_vport **vports;
+ struct lpfc_vport *vport;
struct lpfc_nodelist *ndlp;
- uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp;
- uint16_t fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp;
+ int i;
+
+ set_bit(HBA_RHBA_CMPL, &phba->hba_flag);
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ vport = vports[i];
+ ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+ if (!ndlp)
+ continue;
+ if (vport->ct_flags & FC_CT_RPRT_DEFER) {
+ vport->ct_flags &= ~FC_CT_RPRT_DEFER;
+ vport->fdmi_port_mask = mask;
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
+ }
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
+/**
+ * lpfc_cmpl_ct_disc_fdmi - Handle a discovery FDMI completion
+ * @phba: Pointer to HBA context object.
+ * @cmdiocb: Pointer to the command IOCBQ.
+ * @rspiocb: Pointer to the response IOCBQ.
+ *
+ * This function to handle the completion of a driver initiated FDMI
+ * CT command issued during discovery.
+ */
+static void
+lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
struct lpfc_vport *vport = cmdiocb->vport;
- IOCB_t *irsp = &rspiocb->iocb;
- uint32_t latt;
+ struct lpfc_dmabuf *inp = cmdiocb->cmd_dmabuf;
+ struct lpfc_dmabuf *outp = cmdiocb->rsp_dmabuf;
+ struct lpfc_sli_ct_request *CTcmd = inp->virt;
+ struct lpfc_sli_ct_request *CTrsp = outp->virt;
+ __be16 fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp;
+ __be16 fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp;
+ struct lpfc_nodelist *ndlp, *free_ndlp = NULL;
+ uint32_t latt, cmd, err;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
latt = lpfc_els_chk_latt(vport);
-
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
"FDMI cmpl: status:x%x/x%x latt:%d",
- irsp->ulpStatus, irsp->un.ulpWord[4], latt);
+ ulp_status, ulp_word4, latt);
- if (latt || irsp->ulpStatus) {
- lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ if (latt || ulp_status) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_DISCOVERY,
"0229 FDMI cmd %04x failed, latt = %d "
- "ulpStatus: x%x, rid x%x\n",
- be16_to_cpu(fdmi_cmd), latt, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
- lpfc_ct_free_iocb(phba, cmdiocb);
- return;
+ "ulp_status: (x%x/x%x), sli_flag x%x\n",
+ be16_to_cpu(fdmi_cmd), latt, ulp_status,
+ ulp_word4, phba->sli.sli_flag);
+
+ /* Look for a retryable error */
+ if (ulp_status == IOSTAT_LOCAL_REJECT) {
+ switch ((ulp_word4 & IOERR_PARAM_MASK)) {
+ case IOERR_ABORT_IN_PROGRESS:
+ case IOERR_SEQUENCE_TIMEOUT:
+ case IOERR_ILLEGAL_FRAME:
+ case IOERR_NO_RESOURCES:
+ case IOERR_ILLEGAL_COMMAND:
+ cmdiocb->retry++;
+ if (cmdiocb->retry >= LPFC_FDMI_MAX_RETRY)
+ break;
+
+ /* Retry the same FDMI command */
+ err = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING,
+ cmdiocb, 0);
+ if (err == IOCB_ERROR)
+ break;
+ return;
+ default:
+ break;
+ }
+ }
}
+ free_ndlp = cmdiocb->ndlp;
+ lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(free_ndlp);
+
+ if (ulp_status != IOSTAT_SUCCESS)
+ return;
+
ndlp = lpfc_findnode_did(vport, FDMI_DID);
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
- goto fail_out;
+ if (!ndlp)
+ return;
- if (fdmi_rsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) {
- /* FDMI rsp failed */
- lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0220 FDMI rsp failed Data: x%x\n",
- be16_to_cpu(fdmi_cmd));
+ /* Check for a CT LS_RJT response */
+ cmd = be16_to_cpu(fdmi_cmd);
+ if (be16_to_cpu(fdmi_rsp) == SLI_CT_RESPONSE_FS_RJT) {
+ /* Log FDMI reject */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY | LOG_ELS,
+ "0220 FDMI cmd FS_RJT Data: x%x", cmd);
+
+ /* Should we fallback to FDMI-2 / FDMI-1 ? */
+ switch (cmd) {
+ case SLI_MGMT_RHBA:
+ if (vport->fdmi_hba_mask == LPFC_FDMI2_HBA_ATTR) {
+ /* Fallback to FDMI-1 for HBA attributes */
+ vport->fdmi_hba_mask = LPFC_FDMI1_HBA_ATTR;
+
+ /* If HBA attributes are FDMI1, so should
+ * port attributes be for consistency.
+ */
+ vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
+ /* Start over */
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
+ }
+ return;
+
+ case SLI_MGMT_RPRT:
+ if (vport->port_type != LPFC_PHYSICAL_PORT) {
+ ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+ if (!ndlp)
+ return;
+ }
+ if (vport->fdmi_port_mask == LPFC_FDMI2_PORT_ATTR) {
+ /* Fallback to FDMI-1 */
+ vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
+ /* Start over */
+ lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
+ return;
+ }
+ if (vport->fdmi_port_mask == LPFC_FDMI2_SMART_ATTR) {
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ /* Retry the same command */
+ lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
+ }
+ return;
+
+ case SLI_MGMT_RPA:
+ /* No retry on Vendor, RPA only done on physical port */
+ if (phba->link_flag & LS_CT_VEN_RPA) {
+ phba->link_flag &= ~LS_CT_VEN_RPA;
+ if (phba->cmf_active_mode == LPFC_CFG_OFF)
+ return;
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_DISCOVERY | LOG_ELS,
+ "6460 VEN FDMI RPA RJT\n");
+ return;
+ }
+ if (vport->fdmi_port_mask == LPFC_FDMI2_PORT_ATTR) {
+ /* Fallback to FDMI-1 */
+ vport->fdmi_hba_mask = LPFC_FDMI1_HBA_ATTR;
+ vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
+ /* Start over */
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
+ return;
+ }
+ if (vport->fdmi_port_mask == LPFC_FDMI2_SMART_ATTR) {
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ /* Retry the same command */
+ lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
+ }
+ return;
+ }
}
- switch (be16_to_cpu(fdmi_cmd)) {
+ /*
+ * On success, need to cycle thru FDMI registration for discovery
+ * DHBA -> DPRT -> RHBA -> RPA (physical port)
+ * DPRT -> RPRT (vports)
+ */
+ switch (cmd) {
case SLI_MGMT_RHBA:
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA);
- break;
+ /* Check for any RPRTs deferred till after RHBA completes */
+ lpfc_fdmi_rprt_defer(phba, vport->fdmi_port_mask);
- case SLI_MGMT_RPA:
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA, 0);
break;
case SLI_MGMT_DHBA:
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT);
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0);
break;
case SLI_MGMT_DPRT:
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RHBA);
+ if (vport->port_type == LPFC_PHYSICAL_PORT) {
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RHBA, 0);
+ } else {
+ ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+ if (!ndlp)
+ return;
+
+ /* Only issue a RPRT for the vport if the RHBA
+ * for the physical port completes successfully.
+ * We may have to defer the RPRT accordingly.
+ */
+ if (test_bit(HBA_RHBA_CMPL, &phba->hba_flag)) {
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
+ } else {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY,
+ "6078 RPRT deferred\n");
+ vport->ct_flags |= FC_CT_RPRT_DEFER;
+ }
+ }
break;
- }
+ case SLI_MGMT_RPA:
+ if (vport->port_type == LPFC_PHYSICAL_PORT &&
+ phba->sli4_hba.pc_sli4_params.mi_ver) {
+ /* mi is only for the phyical port, no vports */
+ if (phba->link_flag & LS_CT_VEN_RPA) {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY | LOG_ELS |
+ LOG_CGN_MGMT,
+ "6449 VEN RPA FDMI Success\n");
+ phba->link_flag &= ~LS_CT_VEN_RPA;
+ break;
+ }
-fail_out:
- lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_DISCOVERY | LOG_CGN_MGMT,
+ "6210 Issue Vendor MI FDMI %x\n",
+ phba->sli4_hba.pc_sli4_params.mi_ver);
+
+ /* CGN is only for the physical port, no vports */
+ if (lpfc_fdmi_cmd(vport, ndlp, cmd,
+ LPFC_FDMI_VENDOR_ATTR_mi) == 0)
+ phba->link_flag |= LS_CT_VEN_RPA;
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_DISCOVERY | LOG_ELS,
+ "6458 Send MI FDMI:%x Flag x%x\n",
+ phba->sli4_hba.pc_sli4_params.mi_ver,
+ phba->link_flag);
+ } else {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_DISCOVERY | LOG_ELS,
+ "6459 No FDMI VEN MI support - "
+ "RPA Success\n");
+ }
+ break;
+ }
return;
}
+
+/**
+ * lpfc_fdmi_change_check - Check for changed FDMI parameters
+ * @vport: pointer to a host virtual N_Port data structure.
+ *
+ * Check how many mapped NPorts we are connected to
+ * Check if our hostname changed
+ * Called from hbeat timeout routine to check if any FDMI parameters
+ * changed. If so, re-register those Attributes.
+ */
+void
+lpfc_fdmi_change_check(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nodelist *ndlp;
+ uint16_t cnt;
+
+ if (!lpfc_is_link_up(phba))
+ return;
+
+ /* Must be connected to a Fabric */
+ if (!test_bit(FC_FABRIC, &vport->fc_flag))
+ return;
+
+ ndlp = lpfc_findnode_did(vport, FDMI_DID);
+ if (!ndlp)
+ return;
+
+ /* Check if system hostname changed */
+ if (strcmp(phba->os_host_name, init_utsname()->nodename)) {
+ memset(phba->os_host_name, 0, sizeof(phba->os_host_name));
+ scnprintf(phba->os_host_name, sizeof(phba->os_host_name), "%s",
+ init_utsname()->nodename);
+ lpfc_ns_cmd(vport, SLI_CTNS_RSNN_NN, 0, 0);
+
+ /* Since this effects multiple HBA and PORT attributes, we need
+ * de-register and go thru the whole FDMI registration cycle.
+ * DHBA -> DPRT -> RHBA -> RPA (physical port)
+ * DPRT -> RPRT (vports)
+ */
+ if (vport->port_type == LPFC_PHYSICAL_PORT) {
+ /* For extra Vendor RPA */
+ phba->link_flag &= ~LS_CT_VEN_RPA;
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
+ } else {
+ ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+ if (!ndlp)
+ return;
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0);
+ }
+
+ /* Since this code path registers all the port attributes
+ * we can just return without further checking.
+ */
+ return;
+ }
+
+ if (!(vport->fdmi_port_mask & LPFC_FDMI_PORT_ATTR_num_disc))
+ return;
+
+ /* Check if the number of mapped NPorts changed */
+ cnt = lpfc_find_map_node(vport);
+ if (cnt == vport->fdmi_num_disc)
+ return;
+
+ if (vport->port_type == LPFC_PHYSICAL_PORT) {
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA,
+ LPFC_FDMI_PORT_ATTR_num_disc);
+ } else {
+ ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+ if (!ndlp)
+ return;
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT,
+ LPFC_FDMI_PORT_ATTR_num_disc);
+ }
+}
+
+static inline int
+lpfc_fdmi_set_attr_u32(void *attr, uint16_t attrtype, uint32_t attrval)
+{
+ struct lpfc_fdmi_attr_u32 *ae = attr;
+ int size = sizeof(*ae);
+
+ ae->type = cpu_to_be16(attrtype);
+ ae->len = cpu_to_be16(size);
+ ae->value_u32 = cpu_to_be32(attrval);
+
+ return size;
+}
+
+static inline int
+lpfc_fdmi_set_attr_wwn(void *attr, uint16_t attrtype, struct lpfc_name *wwn)
+{
+ struct lpfc_fdmi_attr_wwn *ae = attr;
+ int size = sizeof(*ae);
+
+ ae->type = cpu_to_be16(attrtype);
+ ae->len = cpu_to_be16(size);
+ /* WWN's assumed to be bytestreams - Big Endian presentation */
+ memcpy(ae->name, wwn,
+ min_t(size_t, sizeof(struct lpfc_name), sizeof(__be64)));
+
+ return size;
+}
+
+static inline int
+lpfc_fdmi_set_attr_fullwwn(void *attr, uint16_t attrtype,
+ struct lpfc_name *wwnn, struct lpfc_name *wwpn)
+{
+ struct lpfc_fdmi_attr_fullwwn *ae = attr;
+ u8 *nname = ae->nname;
+ u8 *pname = ae->pname;
+ int size = sizeof(*ae);
+
+ ae->type = cpu_to_be16(attrtype);
+ ae->len = cpu_to_be16(size);
+ /* WWN's assumed to be bytestreams - Big Endian presentation */
+ memcpy(nname, wwnn,
+ min_t(size_t, sizeof(struct lpfc_name), sizeof(__be64)));
+ memcpy(pname, wwpn,
+ min_t(size_t, sizeof(struct lpfc_name), sizeof(__be64)));
+
+ return size;
+}
+
+static inline int
+lpfc_fdmi_set_attr_string(void *attr, uint16_t attrtype, char *attrstring)
+{
+ struct lpfc_fdmi_attr_string *ae = attr;
+ int len, size;
+
+ /*
+ * We are trusting the caller that if a fdmi string field
+ * is capped at 64 bytes, the caller passes in a string of
+ * 64 bytes or less.
+ */
+
+ strscpy(ae->value_string, attrstring, sizeof(ae->value_string));
+ len = strnlen(ae->value_string, sizeof(ae->value_string));
+ /* round string length to a 32bit boundary */
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ /* size is Type/Len (4 bytes) plus string length */
+ size = FOURBYTES + len;
+
+ ae->type = cpu_to_be16(attrtype);
+ ae->len = cpu_to_be16(size);
+
+ return size;
+}
+
+/* Bitfields for FC4 Types that can be reported */
+#define ATTR_FC4_CT 0x00000001
+#define ATTR_FC4_FCP 0x00000002
+#define ATTR_FC4_NVME 0x00000004
+
+static inline int
+lpfc_fdmi_set_attr_fc4types(void *attr, uint16_t attrtype, uint32_t typemask)
+{
+ struct lpfc_fdmi_attr_fc4types *ae = attr;
+ int size = sizeof(*ae);
+
+ ae->type = cpu_to_be16(attrtype);
+ ae->len = cpu_to_be16(size);
+
+ if (typemask & ATTR_FC4_FCP)
+ ae->value_types[2] = 0x01; /* Type 0x8 - FCP */
+
+ if (typemask & ATTR_FC4_CT)
+ ae->value_types[7] = 0x01; /* Type 0x20 - CT */
+
+ if (typemask & ATTR_FC4_NVME)
+ ae->value_types[6] = 0x01; /* Type 0x28 - NVME */
+
+ return size;
+}
+
+/* Routines for all individual HBA attributes */
+static int
+lpfc_fdmi_hba_attr_wwnn(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_wwn(attr, RHBA_NODENAME,
+ &vport->fc_sparam.nodeName);
+}
+
+static int
+lpfc_fdmi_hba_attr_manufacturer(struct lpfc_vport *vport, void *attr)
+{
+ /* This string MUST be consistent with other FC platforms
+ * supported by Broadcom.
+ */
+ return lpfc_fdmi_set_attr_string(attr, RHBA_MANUFACTURER,
+ "Emulex Corporation");
+}
+
+static int
+lpfc_fdmi_hba_attr_sn(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_SERIAL_NUMBER,
+ phba->SerialNumber);
+}
+
+static int
+lpfc_fdmi_hba_attr_model(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_MODEL,
+ phba->ModelName);
+}
+
+static int
+lpfc_fdmi_hba_attr_description(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_MODEL_DESCRIPTION,
+ phba->ModelDesc);
+}
+
+static int
+lpfc_fdmi_hba_attr_hdw_ver(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ lpfc_vpd_t *vp = &phba->vpd;
+ char buf[16] = { 0 };
+
+ snprintf(buf, sizeof(buf), "%08x", vp->rev.biuRev);
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_HARDWARE_VERSION, buf);
+}
+
+static int
+lpfc_fdmi_hba_attr_drvr_ver(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_string(attr, RHBA_DRIVER_VERSION,
+ lpfc_release_version);
+}
+
+static int
+lpfc_fdmi_hba_attr_rom_ver(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ char buf[64] = { 0 };
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_decode_firmware_rev(phba, buf, 1);
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_OPTION_ROM_VERSION,
+ buf);
+ }
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_OPTION_ROM_VERSION,
+ phba->OptionROMVersion);
+}
+
+static int
+lpfc_fdmi_hba_attr_fmw_ver(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ char buf[64] = { 0 };
+
+ lpfc_decode_firmware_rev(phba, buf, 1);
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_FIRMWARE_VERSION, buf);
+}
+
+static int
+lpfc_fdmi_hba_attr_os_ver(struct lpfc_vport *vport, void *attr)
+{
+ char buf[256] = { 0 };
+
+ snprintf(buf, sizeof(buf), "%s %s %s",
+ init_utsname()->sysname,
+ init_utsname()->release,
+ init_utsname()->version);
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_OS_NAME_VERSION, buf);
+}
+
+static int
+lpfc_fdmi_hba_attr_ct_len(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_u32(attr, RHBA_MAX_CT_PAYLOAD_LEN,
+ LPFC_MAX_CT_SIZE);
+}
+
+static int
+lpfc_fdmi_hba_attr_symbolic_name(struct lpfc_vport *vport, void *attr)
+{
+ char buf[256] = { 0 };
+
+ lpfc_vport_symbolic_node_name(vport, buf, sizeof(buf));
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_SYM_NODENAME, buf);
+}
+
+static int
+lpfc_fdmi_hba_attr_vendor_info(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_u32(attr, RHBA_VENDOR_INFO, 0);
+}
+
+static int
+lpfc_fdmi_hba_attr_num_ports(struct lpfc_vport *vport, void *attr)
+{
+ /* Each driver instance corresponds to a single port */
+ return lpfc_fdmi_set_attr_u32(attr, RHBA_NUM_PORTS, 1);
+}
+
+static int
+lpfc_fdmi_hba_attr_fabric_wwnn(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_wwn(attr, RHBA_FABRIC_WWNN,
+ &vport->fabric_nodename);
+}
+
+static int
+lpfc_fdmi_hba_attr_bios_ver(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ return lpfc_fdmi_set_attr_string(attr, RHBA_BIOS_VERSION,
+ phba->BIOSVersion);
+}
+
+static int
+lpfc_fdmi_hba_attr_bios_state(struct lpfc_vport *vport, void *attr)
+{
+ /* Driver doesn't have access to this information */
+ return lpfc_fdmi_set_attr_u32(attr, RHBA_BIOS_STATE, 0);
+}
+
+static int
+lpfc_fdmi_hba_attr_vendor_id(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_string(attr, RHBA_VENDOR_ID, "EMULEX");
+}
+
+/*
+ * Routines for all individual PORT attributes
+ */
+
+static int
+lpfc_fdmi_port_attr_fc4type(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ u32 fc4types;
+
+ fc4types = (ATTR_FC4_CT | ATTR_FC4_FCP);
+
+ /* Check to see if Firmware supports NVME and on physical port */
+ if ((phba->sli_rev == LPFC_SLI_REV4) && (vport == phba->pport) &&
+ phba->sli4_hba.pc_sli4_params.nvme)
+ fc4types |= ATTR_FC4_NVME;
+
+ return lpfc_fdmi_set_attr_fc4types(attr, RPRT_SUPPORTED_FC4_TYPES,
+ fc4types);
+}
+
+static int
+lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ u32 speeds = 0;
+ u32 tcfg;
+ u8 i, cnt;
+
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ cnt = 0;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tcfg = phba->sli4_hba.conf_trunk;
+ for (i = 0; i < 4; i++, tcfg >>= 1)
+ if (tcfg & 1)
+ cnt++;
+ }
+
+ if (cnt > 2) { /* 4 lane trunk group */
+ if (phba->lmt & LMT_64Gb)
+ speeds |= HBA_PORTSPEED_256GFC;
+ if (phba->lmt & LMT_32Gb)
+ speeds |= HBA_PORTSPEED_128GFC;
+ if (phba->lmt & LMT_16Gb)
+ speeds |= HBA_PORTSPEED_64GFC;
+ } else if (cnt) { /* 2 lane trunk group */
+ if (phba->lmt & LMT_128Gb)
+ speeds |= HBA_PORTSPEED_256GFC;
+ if (phba->lmt & LMT_64Gb)
+ speeds |= HBA_PORTSPEED_128GFC;
+ if (phba->lmt & LMT_32Gb)
+ speeds |= HBA_PORTSPEED_64GFC;
+ if (phba->lmt & LMT_16Gb)
+ speeds |= HBA_PORTSPEED_32GFC;
+ } else {
+ if (phba->lmt & LMT_256Gb)
+ speeds |= HBA_PORTSPEED_256GFC;
+ if (phba->lmt & LMT_128Gb)
+ speeds |= HBA_PORTSPEED_128GFC;
+ if (phba->lmt & LMT_64Gb)
+ speeds |= HBA_PORTSPEED_64GFC;
+ if (phba->lmt & LMT_32Gb)
+ speeds |= HBA_PORTSPEED_32GFC;
+ if (phba->lmt & LMT_16Gb)
+ speeds |= HBA_PORTSPEED_16GFC;
+ if (phba->lmt & LMT_10Gb)
+ speeds |= HBA_PORTSPEED_10GFC;
+ if (phba->lmt & LMT_8Gb)
+ speeds |= HBA_PORTSPEED_8GFC;
+ if (phba->lmt & LMT_4Gb)
+ speeds |= HBA_PORTSPEED_4GFC;
+ if (phba->lmt & LMT_2Gb)
+ speeds |= HBA_PORTSPEED_2GFC;
+ if (phba->lmt & LMT_1Gb)
+ speeds |= HBA_PORTSPEED_1GFC;
+ }
+ } else {
+ /* FCoE links support only one speed */
+ switch (phba->fc_linkspeed) {
+ case LPFC_ASYNC_LINK_SPEED_10GBPS:
+ speeds = HBA_PORTSPEED_10GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_25GBPS:
+ speeds = HBA_PORTSPEED_25GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_40GBPS:
+ speeds = HBA_PORTSPEED_40GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100GBPS:
+ speeds = HBA_PORTSPEED_100GE;
+ break;
+ }
+ }
+
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_SUPPORTED_SPEED, speeds);
+}
+
+static int
+lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ u32 speeds = 0;
+
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ switch (phba->fc_linkspeed) {
+ case LPFC_LINK_SPEED_1GHZ:
+ speeds = HBA_PORTSPEED_1GFC;
+ break;
+ case LPFC_LINK_SPEED_2GHZ:
+ speeds = HBA_PORTSPEED_2GFC;
+ break;
+ case LPFC_LINK_SPEED_4GHZ:
+ speeds = HBA_PORTSPEED_4GFC;
+ break;
+ case LPFC_LINK_SPEED_8GHZ:
+ speeds = HBA_PORTSPEED_8GFC;
+ break;
+ case LPFC_LINK_SPEED_10GHZ:
+ speeds = HBA_PORTSPEED_10GFC;
+ break;
+ case LPFC_LINK_SPEED_16GHZ:
+ speeds = HBA_PORTSPEED_16GFC;
+ break;
+ case LPFC_LINK_SPEED_32GHZ:
+ speeds = HBA_PORTSPEED_32GFC;
+ break;
+ case LPFC_LINK_SPEED_64GHZ:
+ speeds = HBA_PORTSPEED_64GFC;
+ break;
+ case LPFC_LINK_SPEED_128GHZ:
+ speeds = HBA_PORTSPEED_128GFC;
+ break;
+ case LPFC_LINK_SPEED_256GHZ:
+ speeds = HBA_PORTSPEED_256GFC;
+ break;
+ default:
+ speeds = HBA_PORTSPEED_UNKNOWN;
+ break;
+ }
+ } else {
+ switch (phba->fc_linkspeed) {
+ case LPFC_ASYNC_LINK_SPEED_10GBPS:
+ speeds = HBA_PORTSPEED_10GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_25GBPS:
+ speeds = HBA_PORTSPEED_25GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_40GBPS:
+ speeds = HBA_PORTSPEED_40GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100GBPS:
+ speeds = HBA_PORTSPEED_100GE;
+ break;
+ default:
+ speeds = HBA_PORTSPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_PORT_SPEED, speeds);
+}
+
+static int
+lpfc_fdmi_port_attr_max_frame(struct lpfc_vport *vport, void *attr)
+{
+ struct serv_parm *hsp = (struct serv_parm *)&vport->fc_sparam;
+
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_MAX_FRAME_SIZE,
+ (((uint32_t)hsp->cmn.bbRcvSizeMsb & 0x0F) << 8) |
+ (uint32_t)hsp->cmn.bbRcvSizeLsb);
+}
+
+static int
+lpfc_fdmi_port_attr_os_devname(struct lpfc_vport *vport, void *attr)
+{
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ char buf[64] = { 0 };
+
+ snprintf(buf, sizeof(buf), "/sys/class/scsi_host/host%d",
+ shost->host_no);
+
+ return lpfc_fdmi_set_attr_string(attr, RPRT_OS_DEVICE_NAME, buf);
+}
+
+static int
+lpfc_fdmi_port_attr_host_name(struct lpfc_vport *vport, void *attr)
+{
+ char buf[64] = { 0 };
+
+ scnprintf(buf, sizeof(buf), "%s", vport->phba->os_host_name);
+
+ return lpfc_fdmi_set_attr_string(attr, RPRT_HOST_NAME, buf);
+}
+
+static int
+lpfc_fdmi_port_attr_wwnn(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_wwn(attr, RPRT_NODENAME,
+ &vport->fc_sparam.nodeName);
+}
+
+static int
+lpfc_fdmi_port_attr_wwpn(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_wwn(attr, RPRT_PORTNAME,
+ &vport->fc_sparam.portName);
+}
+
+static int
+lpfc_fdmi_port_attr_symbolic_name(struct lpfc_vport *vport, void *attr)
+{
+ char buf[256] = { 0 };
+
+ lpfc_vport_symbolic_port_name(vport, buf, sizeof(buf));
+
+ return lpfc_fdmi_set_attr_string(attr, RPRT_SYM_PORTNAME, buf);
+}
+
+static int
+lpfc_fdmi_port_attr_port_type(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_PORT_TYPE,
+ (phba->fc_topology == LPFC_TOPOLOGY_LOOP) ?
+ LPFC_FDMI_PORTTYPE_NLPORT :
+ LPFC_FDMI_PORTTYPE_NPORT);
+}
+
+static int
+lpfc_fdmi_port_attr_class(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_SUPPORTED_CLASS,
+ FC_COS_CLASS2 | FC_COS_CLASS3);
+}
+
+static int
+lpfc_fdmi_port_attr_fabric_wwpn(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_wwn(attr, RPRT_FABRICNAME,
+ &vport->fabric_portname);
+}
+
+static int
+lpfc_fdmi_port_attr_active_fc4type(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ u32 fc4types;
+
+ fc4types = (ATTR_FC4_CT | ATTR_FC4_FCP);
+
+ /* Check to see if NVME is configured or not */
+ if (vport == phba->pport &&
+ phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ fc4types |= ATTR_FC4_NVME;
+
+ return lpfc_fdmi_set_attr_fc4types(attr, RPRT_ACTIVE_FC4_TYPES,
+ fc4types);
+}
+
+static int
+lpfc_fdmi_port_attr_port_state(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_PORT_STATE,
+ LPFC_FDMI_PORTSTATE_ONLINE);
+}
+
+static int
+lpfc_fdmi_port_attr_num_disc(struct lpfc_vport *vport, void *attr)
+{
+ vport->fdmi_num_disc = lpfc_find_map_node(vport);
+
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_DISC_PORT,
+ vport->fdmi_num_disc);
+}
+
+static int
+lpfc_fdmi_port_attr_nportid(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_PORT_ID, vport->fc_myDID);
+}
+
+static int
+lpfc_fdmi_smart_attr_service(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_string(attr, RPRT_SMART_SERVICE,
+ "Smart SAN Initiator");
+}
+
+static int
+lpfc_fdmi_smart_attr_guid(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_fullwwn(attr, RPRT_SMART_GUID,
+ &vport->fc_sparam.nodeName,
+ &vport->fc_sparam.portName);
+}
+
+static int
+lpfc_fdmi_smart_attr_version(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_string(attr, RPRT_SMART_VERSION,
+ "Smart SAN Version 2.0");
+}
+
+static int
+lpfc_fdmi_smart_attr_model(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ return lpfc_fdmi_set_attr_string(attr, RPRT_SMART_MODEL,
+ phba->ModelName);
+}
+
+static int
+lpfc_fdmi_smart_attr_port_info(struct lpfc_vport *vport, void *attr)
+{
+ /* SRIOV (type 3) is not supported */
+
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_SMART_PORT_INFO,
+ (vport->vpi) ? 2 /* NPIV */ : 1 /* Physical */);
+}
+
+static int
+lpfc_fdmi_smart_attr_qos(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_SMART_QOS, 0);
+}
+
+static int
+lpfc_fdmi_smart_attr_security(struct lpfc_vport *vport, void *attr)
+{
+ return lpfc_fdmi_set_attr_u32(attr, RPRT_SMART_SECURITY, 1);
+}
+
+static int
+lpfc_fdmi_vendor_attr_mi(struct lpfc_vport *vport, void *attr)
+{
+ struct lpfc_hba *phba = vport->phba;
+ char buf[32] = { 0 };
+
+ sprintf(buf, "ELXE2EM:%04d", phba->sli4_hba.pc_sli4_params.mi_ver);
+
+ return lpfc_fdmi_set_attr_string(attr, RPRT_VENDOR_MI, buf);
+}
+
+/* RHBA attribute jump table */
+static int (*lpfc_fdmi_hba_action[])
+ (struct lpfc_vport *vport, void *attrbuf) = {
+ /* Action routine Mask bit Attribute type */
+ lpfc_fdmi_hba_attr_wwnn, /* bit0 RHBA_NODENAME */
+ lpfc_fdmi_hba_attr_manufacturer, /* bit1 RHBA_MANUFACTURER */
+ lpfc_fdmi_hba_attr_sn, /* bit2 RHBA_SERIAL_NUMBER */
+ lpfc_fdmi_hba_attr_model, /* bit3 RHBA_MODEL */
+ lpfc_fdmi_hba_attr_description, /* bit4 RHBA_MODEL_DESCRIPTION */
+ lpfc_fdmi_hba_attr_hdw_ver, /* bit5 RHBA_HARDWARE_VERSION */
+ lpfc_fdmi_hba_attr_drvr_ver, /* bit6 RHBA_DRIVER_VERSION */
+ lpfc_fdmi_hba_attr_rom_ver, /* bit7 RHBA_OPTION_ROM_VERSION */
+ lpfc_fdmi_hba_attr_fmw_ver, /* bit8 RHBA_FIRMWARE_VERSION */
+ lpfc_fdmi_hba_attr_os_ver, /* bit9 RHBA_OS_NAME_VERSION */
+ lpfc_fdmi_hba_attr_ct_len, /* bit10 RHBA_MAX_CT_PAYLOAD_LEN */
+ lpfc_fdmi_hba_attr_symbolic_name, /* bit11 RHBA_SYM_NODENAME */
+ lpfc_fdmi_hba_attr_vendor_info, /* bit12 RHBA_VENDOR_INFO */
+ lpfc_fdmi_hba_attr_num_ports, /* bit13 RHBA_NUM_PORTS */
+ lpfc_fdmi_hba_attr_fabric_wwnn, /* bit14 RHBA_FABRIC_WWNN */
+ lpfc_fdmi_hba_attr_bios_ver, /* bit15 RHBA_BIOS_VERSION */
+ lpfc_fdmi_hba_attr_bios_state, /* bit16 RHBA_BIOS_STATE */
+ lpfc_fdmi_hba_attr_vendor_id, /* bit17 RHBA_VENDOR_ID */
+};
+
+/* RPA / RPRT attribute jump table */
+static int (*lpfc_fdmi_port_action[])
+ (struct lpfc_vport *vport, void *attrbuf) = {
+ /* Action routine Mask bit Attribute type */
+ lpfc_fdmi_port_attr_fc4type, /* bit0 RPRT_SUPPORT_FC4_TYPES */
+ lpfc_fdmi_port_attr_support_speed, /* bit1 RPRT_SUPPORTED_SPEED */
+ lpfc_fdmi_port_attr_speed, /* bit2 RPRT_PORT_SPEED */
+ lpfc_fdmi_port_attr_max_frame, /* bit3 RPRT_MAX_FRAME_SIZE */
+ lpfc_fdmi_port_attr_os_devname, /* bit4 RPRT_OS_DEVICE_NAME */
+ lpfc_fdmi_port_attr_host_name, /* bit5 RPRT_HOST_NAME */
+ lpfc_fdmi_port_attr_wwnn, /* bit6 RPRT_NODENAME */
+ lpfc_fdmi_port_attr_wwpn, /* bit7 RPRT_PORTNAME */
+ lpfc_fdmi_port_attr_symbolic_name, /* bit8 RPRT_SYM_PORTNAME */
+ lpfc_fdmi_port_attr_port_type, /* bit9 RPRT_PORT_TYPE */
+ lpfc_fdmi_port_attr_class, /* bit10 RPRT_SUPPORTED_CLASS */
+ lpfc_fdmi_port_attr_fabric_wwpn, /* bit11 RPRT_FABRICNAME */
+ lpfc_fdmi_port_attr_active_fc4type, /* bit12 RPRT_ACTIVE_FC4_TYPES */
+ lpfc_fdmi_port_attr_port_state, /* bit13 RPRT_PORT_STATE */
+ lpfc_fdmi_port_attr_num_disc, /* bit14 RPRT_DISC_PORT */
+ lpfc_fdmi_port_attr_nportid, /* bit15 RPRT_PORT_ID */
+ lpfc_fdmi_smart_attr_service, /* bit16 RPRT_SMART_SERVICE */
+ lpfc_fdmi_smart_attr_guid, /* bit17 RPRT_SMART_GUID */
+ lpfc_fdmi_smart_attr_version, /* bit18 RPRT_SMART_VERSION */
+ lpfc_fdmi_smart_attr_model, /* bit19 RPRT_SMART_MODEL */
+ lpfc_fdmi_smart_attr_port_info, /* bit20 RPRT_SMART_PORT_INFO */
+ lpfc_fdmi_smart_attr_qos, /* bit21 RPRT_SMART_QOS */
+ lpfc_fdmi_smart_attr_security, /* bit22 RPRT_SMART_SECURITY */
+ lpfc_fdmi_vendor_attr_mi, /* bit23 RPRT_VENDOR_MI */
+};
+
+/**
+ * lpfc_fdmi_cmd - Build and send a FDMI cmd to the specified NPort
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @ndlp: ndlp to send FDMI cmd to (if NULL use FDMI_DID)
+ * @cmdcode: FDMI command to send
+ * @new_mask: Mask of HBA or PORT Attributes to send
+ *
+ * Builds and sends a FDMI command using the CT subsystem.
+ */
int
-lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int cmdcode)
+lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ int cmdcode, uint32_t new_mask)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_dmabuf *mp, *bmp;
+ struct lpfc_dmabuf *rq, *rsp;
struct lpfc_sli_ct_request *CtReq;
- struct ulp_bde64 *bpl;
- uint32_t size;
- REG_HBA *rh;
- PORT_ENTRY *pe;
- REG_PORT_ATTRIBUTE *pab;
- ATTRIBUTE_BLOCK *ab;
- ATTRIBUTE_ENTRY *ae;
- void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
- struct lpfc_iocbq *);
+ struct ulp_bde64_le *bde;
+ uint32_t bit_pos;
+ uint32_t size, addsz;
+ uint32_t rsp_size;
+ uint32_t mask;
+ struct lpfc_fdmi_reg_hba *rh;
+ struct lpfc_fdmi_port_entry *pe;
+ struct lpfc_fdmi_reg_portattr *pab = NULL, *base = NULL;
+ struct lpfc_fdmi_attr_block *ab = NULL;
+ int (*func)(struct lpfc_vport *vport, void *attrbuf);
+ void (*cmpl)(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb);
+
+ if (!ndlp)
+ return 0;
+ cmpl = lpfc_cmpl_ct_disc_fdmi; /* called from discovery */
/* fill in BDEs for command */
/* Allocate buffer for command payload */
- mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
- if (!mp)
+ rq = kmalloc(sizeof(*rq), GFP_KERNEL);
+ if (!rq)
goto fdmi_cmd_exit;
- mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys));
- if (!mp->virt)
- goto fdmi_cmd_free_mp;
+ rq->virt = lpfc_mbuf_alloc(phba, 0, &rq->phys);
+ if (!rq->virt)
+ goto fdmi_cmd_free_rq;
/* Allocate buffer for Buffer ptr list */
- bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
- if (!bmp)
- goto fdmi_cmd_free_mpvirt;
+ rsp = kmalloc(sizeof(*rsp), GFP_KERNEL);
+ if (!rsp)
+ goto fdmi_cmd_free_rqvirt;
- bmp->virt = lpfc_mbuf_alloc(phba, 0, &(bmp->phys));
- if (!bmp->virt)
- goto fdmi_cmd_free_bmp;
+ rsp->virt = lpfc_mbuf_alloc(phba, 0, &rsp->phys);
+ if (!rsp->virt)
+ goto fdmi_cmd_free_rsp;
- INIT_LIST_HEAD(&mp->list);
- INIT_LIST_HEAD(&bmp->list);
+ INIT_LIST_HEAD(&rq->list);
+ INIT_LIST_HEAD(&rsp->list);
+
+ /* mbuf buffers are 1K in length - aka LPFC_BPL_SIZE */
+ memset(rq->virt, 0, LPFC_BPL_SIZE);
+ rsp_size = LPFC_BPL_SIZE;
/* FDMI request */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0218 FDMI Request Data: x%x x%x x%x\n",
- vport->fc_flag, vport->port_state, cmdcode);
- CtReq = (struct lpfc_sli_ct_request *) mp->virt;
+ "0218 FDMI Request x%x mask x%x Data: x%x x%lx x%x\n",
+ cmdcode, new_mask, vport->fdmi_port_mask,
+ vport->fc_flag, vport->port_state);
- memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request));
+ CtReq = (struct lpfc_sli_ct_request *)rq->virt;
+
+ /* First populate the CT_IU preamble */
CtReq->RevisionId.bits.Revision = SLI_CT_REVISION;
CtReq->RevisionId.bits.InId = 0;
CtReq->FsType = SLI_CT_MANAGEMENT_SERVICE;
CtReq->FsSubType = SLI_CT_FDMI_Subtypes;
+
+ CtReq->CommandResponse.bits.CmdRsp = cpu_to_be16(cmdcode);
+
size = 0;
+ /* Next fill in the specific FDMI cmd information */
switch (cmdcode) {
+ case SLI_MGMT_RHAT:
case SLI_MGMT_RHBA:
- {
- lpfc_vpd_t *vp = &phba->vpd;
- uint32_t i, j, incr;
- int len;
-
- CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_MGMT_RHBA);
- CtReq->CommandResponse.bits.Size = 0;
- rh = (REG_HBA *) & CtReq->un.PortID;
- memcpy(&rh->hi.PortName, &vport->fc_sparam.portName,
- sizeof (struct lpfc_name));
+ rh = (struct lpfc_fdmi_reg_hba *)&CtReq->un;
+ /* HBA Identifier */
+ memcpy(&rh->hi.PortName, &phba->pport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ size += sizeof(struct lpfc_fdmi_hba_ident);
+
+ if (cmdcode == SLI_MGMT_RHBA) {
+ /* Registered Port List */
/* One entry (port) per adapter */
- rh->rpl.EntryCnt = be32_to_cpu(1);
- memcpy(&rh->rpl.pe, &vport->fc_sparam.portName,
- sizeof (struct lpfc_name));
-
- /* point to the HBA attribute block */
- size = 2 * sizeof (struct lpfc_name) + FOURBYTES;
- ab = (ATTRIBUTE_BLOCK *) ((uint8_t *) rh + size);
- ab->EntryCnt = 0;
-
- /* Point to the beginning of the first HBA attribute
- entry */
- /* #1 HBA attribute entry */
- size += FOURBYTES;
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(NODE_NAME);
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES
- + sizeof (struct lpfc_name));
- memcpy(&ae->un.NodeName, &vport->fc_sparam.nodeName,
- sizeof (struct lpfc_name));
- ab->EntryCnt++;
- size += FOURBYTES + sizeof (struct lpfc_name);
-
- /* #2 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(MANUFACTURER);
- strcpy(ae->un.Manufacturer, "Emulex Corporation");
- len = strlen(ae->un.Manufacturer);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #3 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(SERIAL_NUMBER);
- strcpy(ae->un.SerialNumber, phba->SerialNumber);
- len = strlen(ae->un.SerialNumber);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #4 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(MODEL);
- strcpy(ae->un.Model, phba->ModelName);
- len = strlen(ae->un.Model);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #5 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(MODEL_DESCRIPTION);
- strcpy(ae->un.ModelDescription, phba->ModelDesc);
- len = strlen(ae->un.ModelDescription);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #6 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(HARDWARE_VERSION);
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 8);
- /* Convert JEDEC ID to ascii for hardware version */
- incr = vp->rev.biuRev;
- for (i = 0; i < 8; i++) {
- j = (incr & 0xf);
- if (j <= 9)
- ae->un.HardwareVersion[7 - i] =
- (char)((uint8_t) 0x30 +
- (uint8_t) j);
- else
- ae->un.HardwareVersion[7 - i] =
- (char)((uint8_t) 0x61 +
- (uint8_t) (j - 10));
- incr = (incr >> 4);
+ rh->rpl.EntryCnt = cpu_to_be32(1);
+ memcpy(&rh->rpl.pe.PortName,
+ &phba->pport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ size += sizeof(struct lpfc_fdmi_reg_port_list);
+ }
+
+ ab = (struct lpfc_fdmi_attr_block *)((uint8_t *)rh + size);
+ ab->EntryCnt = 0;
+ size += FOURBYTES; /* add length of EntryCnt field */
+
+ bit_pos = 0;
+ if (new_mask)
+ mask = new_mask;
+ else
+ mask = vport->fdmi_hba_mask;
+
+ /* Mask will dictate what attributes to build in the request */
+ while (mask) {
+ if (mask & 0x1) {
+ func = lpfc_fdmi_hba_action[bit_pos];
+ addsz = func(vport, ((uint8_t *)rh + size));
+ if (addsz) {
+ ab->EntryCnt++;
+ size += addsz;
+ }
+ /* check if another attribute fits */
+ if ((size + FDMI_MAX_ATTRLEN) >
+ (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
+ goto hba_out;
}
- ab->EntryCnt++;
- size += FOURBYTES + 8;
-
- /* #7 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(DRIVER_VERSION);
- strcpy(ae->un.DriverVersion, lpfc_release_version);
- len = strlen(ae->un.DriverVersion);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #8 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(OPTION_ROM_VERSION);
- strcpy(ae->un.OptionROMVersion, phba->OptionROMVersion);
- len = strlen(ae->un.OptionROMVersion);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #9 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(FIRMWARE_VERSION);
- lpfc_decode_firmware_rev(phba, ae->un.FirmwareVersion,
- 1);
- len = strlen(ae->un.FirmwareVersion);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #10 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION);
- sprintf(ae->un.OsNameVersion, "%s %s %s",
- init_utsname()->sysname,
- init_utsname()->release,
- init_utsname()->version);
- len = strlen(ae->un.OsNameVersion);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-
- /* #11 HBA attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
- ae->ad.bits.AttrType = be16_to_cpu(MAX_CT_PAYLOAD_LEN);
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
- ae->un.MaxCTPayloadLen = (65 * 4096);
- ab->EntryCnt++;
- size += FOURBYTES + 4;
-
- ab->EntryCnt = be32_to_cpu(ab->EntryCnt);
- /* Total size */
- size = GID_REQUEST_SZ - 4 + size;
+ mask = mask >> 1;
+ bit_pos++;
}
+hba_out:
+ ab->EntryCnt = cpu_to_be32(ab->EntryCnt);
+ /* Total size */
+ size += GID_REQUEST_SZ - 4;
break;
+ case SLI_MGMT_RPRT:
+ if (vport->port_type != LPFC_PHYSICAL_PORT) {
+ ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+ if (!ndlp)
+ return 0;
+ }
+ fallthrough;
case SLI_MGMT_RPA:
- {
- lpfc_vpd_t *vp;
- struct serv_parm *hsp;
- int len;
-
- vp = &phba->vpd;
-
- CtReq->CommandResponse.bits.CmdRsp =
- be16_to_cpu(SLI_MGMT_RPA);
- CtReq->CommandResponse.bits.Size = 0;
- pab = (REG_PORT_ATTRIBUTE *) & CtReq->un.PortID;
- size = sizeof (struct lpfc_name) + FOURBYTES;
- memcpy((uint8_t *) & pab->PortName,
- (uint8_t *) & vport->fc_sparam.portName,
- sizeof (struct lpfc_name));
- pab->ab.EntryCnt = 0;
-
- /* #1 Port attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
- ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_FC4_TYPES);
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 32);
- ae->un.SupportFC4Types[2] = 1;
- ae->un.SupportFC4Types[7] = 1;
- pab->ab.EntryCnt++;
- size += FOURBYTES + 32;
-
- /* #2 Port attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
- ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_SPEED);
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
-
- ae->un.SupportSpeed = 0;
- if (phba->lmt & LMT_16Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_16GBIT;
- if (phba->lmt & LMT_10Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_10GBIT;
- if (phba->lmt & LMT_8Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_8GBIT;
- if (phba->lmt & LMT_4Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_4GBIT;
- if (phba->lmt & LMT_2Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_2GBIT;
- if (phba->lmt & LMT_1Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_1GBIT;
-
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
-
- /* #3 Port attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
- ae->ad.bits.AttrType = be16_to_cpu(PORT_SPEED);
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
- switch(phba->fc_linkspeed) {
- case LPFC_LINK_SPEED_1GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_1GBIT;
- break;
- case LPFC_LINK_SPEED_2GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_2GBIT;
- break;
- case LPFC_LINK_SPEED_4GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_4GBIT;
- break;
- case LPFC_LINK_SPEED_8GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_8GBIT;
- break;
- case LPFC_LINK_SPEED_10GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_10GBIT;
- break;
- case LPFC_LINK_SPEED_16GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_16GBIT;
- break;
- default:
- ae->un.PortSpeed = HBA_PORTSPEED_UNKNOWN;
- break;
- }
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
-
- /* #4 Port attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
- ae->ad.bits.AttrType = be16_to_cpu(MAX_FRAME_SIZE);
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
- hsp = (struct serv_parm *) & vport->fc_sparam;
- ae->un.MaxFrameSize =
- (((uint32_t) hsp->cmn.
- bbRcvSizeMsb) << 8) | (uint32_t) hsp->cmn.
- bbRcvSizeLsb;
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
-
- /* #5 Port attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
- ae->ad.bits.AttrType = be16_to_cpu(OS_DEVICE_NAME);
- strcpy((char *)ae->un.OsDeviceName, LPFC_DRIVER_NAME);
- len = strlen((char *)ae->un.OsDeviceName);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
- pab->ab.EntryCnt++;
- size += FOURBYTES + len;
-
- if (vport->cfg_fdmi_on == 2) {
- /* #6 Port attribute entry */
- ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab +
- size);
- ae->ad.bits.AttrType = be16_to_cpu(HOST_NAME);
- sprintf(ae->un.HostName, "%s",
- init_utsname()->nodename);
- len = strlen(ae->un.HostName);
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ae->ad.bits.AttrLen =
- be16_to_cpu(FOURBYTES + len);
- pab->ab.EntryCnt++;
- size += FOURBYTES + len;
- }
+ /* Store base ptr right after preamble */
+ base = (struct lpfc_fdmi_reg_portattr *)&CtReq->un;
+
+ if (cmdcode == SLI_MGMT_RPRT) {
+ rh = (struct lpfc_fdmi_reg_hba *)base;
+ /* HBA Identifier */
+ memcpy(&rh->hi.PortName,
+ &phba->pport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ pab = (struct lpfc_fdmi_reg_portattr *)
+ ((uint8_t *)base + sizeof(struct lpfc_name));
+ size += sizeof(struct lpfc_name);
+ } else {
+ pab = base;
+ }
+
+ memcpy((uint8_t *)&pab->PortName,
+ (uint8_t *)&vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ pab->ab.EntryCnt = 0;
+ /* add length of name and EntryCnt field */
+ size += sizeof(struct lpfc_name) + FOURBYTES;
- pab->ab.EntryCnt = be32_to_cpu(pab->ab.EntryCnt);
- /* Total size */
- size = GID_REQUEST_SZ - 4 + size;
+ bit_pos = 0;
+ if (new_mask)
+ mask = new_mask;
+ else
+ mask = vport->fdmi_port_mask;
+
+ /* Mask will dictate what attributes to build in the request */
+ while (mask) {
+ if (mask & 0x1) {
+ func = lpfc_fdmi_port_action[bit_pos];
+ addsz = func(vport, ((uint8_t *)base + size));
+ if (addsz) {
+ pab->ab.EntryCnt++;
+ size += addsz;
+ }
+ /* check if another attribute fits */
+ if ((size + FDMI_MAX_ATTRLEN) >
+ (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
+ goto port_out;
+ }
+ mask = mask >> 1;
+ bit_pos++;
}
+port_out:
+ pab->ab.EntryCnt = cpu_to_be32(pab->ab.EntryCnt);
+ size += GID_REQUEST_SZ - 4;
break;
+ case SLI_MGMT_GHAT:
+ case SLI_MGMT_GRPL:
+ rsp_size = FC_MAX_NS_RSP;
+ fallthrough;
case SLI_MGMT_DHBA:
- CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DHBA);
- CtReq->CommandResponse.bits.Size = 0;
- pe = (PORT_ENTRY *) & CtReq->un.PortID;
- memcpy((uint8_t *) & pe->PortName,
- (uint8_t *) & vport->fc_sparam.portName,
- sizeof (struct lpfc_name));
- size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name);
+ case SLI_MGMT_DHAT:
+ pe = (struct lpfc_fdmi_port_entry *)&CtReq->un;
+ memcpy((uint8_t *)&pe->PortName,
+ (uint8_t *)&vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ size = GID_REQUEST_SZ - 4 + sizeof(struct lpfc_name);
break;
+ case SLI_MGMT_GPAT:
+ case SLI_MGMT_GPAS:
+ rsp_size = FC_MAX_NS_RSP;
+ fallthrough;
case SLI_MGMT_DPRT:
- CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DPRT);
- CtReq->CommandResponse.bits.Size = 0;
- pe = (PORT_ENTRY *) & CtReq->un.PortID;
- memcpy((uint8_t *) & pe->PortName,
- (uint8_t *) & vport->fc_sparam.portName,
- sizeof (struct lpfc_name));
- size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name);
+ if (vport->port_type != LPFC_PHYSICAL_PORT) {
+ ndlp = lpfc_findnode_did(phba->pport, FDMI_DID);
+ if (!ndlp)
+ return 0;
+ }
+ fallthrough;
+ case SLI_MGMT_DPA:
+ pe = (struct lpfc_fdmi_port_entry *)&CtReq->un;
+ memcpy((uint8_t *)&pe->PortName,
+ (uint8_t *)&vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ size = GID_REQUEST_SZ - 4 + sizeof(struct lpfc_name);
break;
+ case SLI_MGMT_GRHL:
+ size = GID_REQUEST_SZ - 4;
+ break;
+ default:
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_DISCOVERY,
+ "0298 FDMI cmdcode x%x not supported\n",
+ cmdcode);
+ goto fdmi_cmd_free_rspvirt;
}
+ CtReq->CommandResponse.bits.Size = cpu_to_be16(rsp_size);
- bpl = (struct ulp_bde64 *) bmp->virt;
- bpl->addrHigh = le32_to_cpu(putPaddrHigh(mp->phys) );
- bpl->addrLow = le32_to_cpu(putPaddrLow(mp->phys) );
- bpl->tus.f.bdeFlags = 0;
- bpl->tus.f.bdeSize = size;
- bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bde = (struct ulp_bde64_le *)rsp->virt;
+ bde->addr_high = cpu_to_le32(putPaddrHigh(rq->phys));
+ bde->addr_low = cpu_to_le32(putPaddrLow(rq->phys));
+ bde->type_size = cpu_to_le32(ULP_BDE64_TYPE_BDE_64 <<
+ ULP_BDE64_TYPE_SHIFT);
+ bde->type_size |= cpu_to_le32(size);
- cmpl = lpfc_cmpl_ct_cmd_fdmi;
-
- /* The lpfc_ct_cmd/lpfc_get_req shall increment ndlp reference count
+ /*
+ * The lpfc_ct_cmd/lpfc_get_req shall increment ndlp reference count
* to hold ndlp reference for the corresponding callback function.
*/
- if (!lpfc_ct_cmd(vport, mp, bmp, ndlp, cmpl, FC_MAX_NS_RSP, 0))
+ if (!lpfc_ct_cmd(vport, rq, rsp, ndlp, cmpl, rsp_size, 0))
return 0;
- /* Decrement ndlp reference count to release ndlp reference held
- * for the failed command's callback function.
- */
- lpfc_nlp_put(ndlp);
-
- lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
-fdmi_cmd_free_bmp:
- kfree(bmp);
-fdmi_cmd_free_mpvirt:
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
-fdmi_cmd_free_mp:
- kfree(mp);
+fdmi_cmd_free_rspvirt:
+ lpfc_mbuf_free(phba, rsp->virt, rsp->phys);
+fdmi_cmd_free_rsp:
+ kfree(rsp);
+fdmi_cmd_free_rqvirt:
+ lpfc_mbuf_free(phba, rq->virt, rq->phys);
+fdmi_cmd_free_rq:
+ kfree(rq);
fdmi_cmd_exit:
/* Issue FDMI request failed */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
@@ -1742,15 +3449,16 @@ fdmi_cmd_exit:
/**
* lpfc_delayed_disc_tmo - Timeout handler for delayed discovery timer.
- * @ptr - Context object of the timer.
+ * @t: Context object of the timer.
*
* This function set the WORKER_DELAYED_DISC_TMO flag and wake up
* the worker thread.
**/
void
-lpfc_delayed_disc_tmo(unsigned long ptr)
+lpfc_delayed_disc_tmo(struct timer_list *t)
{
- struct lpfc_vport *vport = (struct lpfc_vport *)ptr;
+ struct lpfc_vport *vport = timer_container_of(vport, t,
+ delayed_disc_tmo);
struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted;
unsigned long iflag;
@@ -1776,54 +3484,13 @@ lpfc_delayed_disc_tmo(unsigned long ptr)
void
lpfc_delayed_disc_timeout_handler(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- spin_lock_irq(shost->host_lock);
- if (!(vport->fc_flag & FC_DISC_DELAYED)) {
- spin_unlock_irq(shost->host_lock);
+ if (!test_and_clear_bit(FC_DISC_DELAYED, &vport->fc_flag))
return;
- }
- vport->fc_flag &= ~FC_DISC_DELAYED;
- spin_unlock_irq(shost->host_lock);
lpfc_do_scr_ns_plogi(vport->phba, vport);
}
void
-lpfc_fdmi_tmo(unsigned long ptr)
-{
- struct lpfc_vport *vport = (struct lpfc_vport *)ptr;
- struct lpfc_hba *phba = vport->phba;
- uint32_t tmo_posted;
- unsigned long iflag;
-
- spin_lock_irqsave(&vport->work_port_lock, iflag);
- tmo_posted = vport->work_port_events & WORKER_FDMI_TMO;
- if (!tmo_posted)
- vport->work_port_events |= WORKER_FDMI_TMO;
- spin_unlock_irqrestore(&vport->work_port_lock, iflag);
-
- if (!tmo_posted)
- lpfc_worker_wake_up(phba);
- return;
-}
-
-void
-lpfc_fdmi_timeout_handler(struct lpfc_vport *vport)
-{
- struct lpfc_nodelist *ndlp;
-
- ndlp = lpfc_findnode_did(vport, FDMI_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- if (init_utsname()->nodename[0] != '\0')
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA);
- else
- mod_timer(&vport->fc_fdmitmo, jiffies + HZ * 60);
- }
- return;
-}
-
-void
lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
{
struct lpfc_sli *psli = &phba->sli;
@@ -1834,7 +3501,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
uint8_t *fwname;
if (phba->sli_rev == LPFC_SLI_REV4)
- sprintf(fwrevision, "%s", vp->rev.opFwName);
+ snprintf(fwrevision, FW_REV_STR_SIZE, "%s", vp->rev.opFwName);
else if (vp->rev.rBit) {
if (psli->sli_flag & LPFC_SLI_ACTIVE)
rev = vp->rev.sli2FwRev;
@@ -1856,6 +3523,9 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
case 2:
c = 'B';
break;
+ case 3:
+ c = 'X';
+ break;
default:
c = 0;
break;
@@ -1905,3 +3575,259 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
}
return;
}
+
+static void
+lpfc_cmpl_ct_cmd_vmid(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_vport *vport = cmdiocb->vport;
+ struct lpfc_dmabuf *inp = cmdiocb->cmd_dmabuf;
+ struct lpfc_dmabuf *outp = cmdiocb->rsp_dmabuf;
+ struct lpfc_sli_ct_request *ctcmd = inp->virt;
+ struct lpfc_sli_ct_request *ctrsp = outp->virt;
+ __be16 rsp = ctrsp->CommandResponse.bits.CmdRsp;
+ struct app_id_object *app;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
+ u32 cmd, hash, bucket;
+ struct lpfc_vmid *vmp, *cur;
+ u8 *data = outp->virt;
+ int i;
+
+ cmd = be16_to_cpu(ctcmd->CommandResponse.bits.CmdRsp);
+ if (cmd == SLI_CTAS_DALLAPP_ID)
+ lpfc_ct_free_iocb(phba, cmdiocb);
+
+ if (lpfc_els_chk_latt(vport) || get_job_ulpstatus(phba, rspiocb)) {
+ if (cmd != SLI_CTAS_DALLAPP_ID)
+ goto free_res;
+ }
+ /* Check for a CT LS_RJT response */
+ if (be16_to_cpu(rsp) == SLI_CT_RESPONSE_FS_RJT) {
+ if (cmd != SLI_CTAS_DALLAPP_ID)
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "3306 VMID FS_RJT Data: x%x x%x x%x\n",
+ cmd, ctrsp->ReasonCode,
+ ctrsp->Explanation);
+ if ((cmd != SLI_CTAS_DALLAPP_ID) ||
+ (ctrsp->ReasonCode != SLI_CT_UNABLE_TO_PERFORM_REQ) ||
+ (ctrsp->Explanation != SLI_CT_APP_ID_NOT_AVAILABLE)) {
+ /* If DALLAPP_ID failed retry later */
+ if (cmd == SLI_CTAS_DALLAPP_ID)
+ set_bit(FC_DEREGISTER_ALL_APP_ID,
+ &vport->load_flag);
+ goto free_res;
+ }
+ }
+
+ switch (cmd) {
+ case SLI_CTAS_RAPP_IDENT:
+ app = (struct app_id_object *)(RAPP_IDENT_OFFSET + data);
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "6712 RAPP_IDENT app id %d port id x%x id "
+ "len %d\n", be32_to_cpu(app->app_id),
+ be32_to_cpu(app->port_id),
+ app->obj.entity_id_len);
+
+ if (app->obj.entity_id_len == 0 || app->port_id == 0)
+ goto free_res;
+
+ hash = lpfc_vmid_hash_fn(app->obj.entity_id,
+ app->obj.entity_id_len);
+ vmp = lpfc_get_vmid_from_hashtable(vport, hash,
+ app->obj.entity_id);
+ if (vmp) {
+ write_lock(&vport->vmid_lock);
+ vmp->un.app_id = be32_to_cpu(app->app_id);
+ vmp->flag |= LPFC_VMID_REGISTERED;
+ vmp->flag &= ~LPFC_VMID_REQ_REGISTER;
+ write_unlock(&vport->vmid_lock);
+ /* Set IN USE flag */
+ vport->vmid_flag |= LPFC_VMID_IN_USE;
+ } else {
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "6901 No entry found %s hash %d\n",
+ app->obj.entity_id, hash);
+ }
+ break;
+ case SLI_CTAS_DAPP_IDENT:
+ app = (struct app_id_object *)(DAPP_IDENT_OFFSET + data);
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "6713 DAPP_IDENT app id %d port id x%x\n",
+ be32_to_cpu(app->app_id),
+ be32_to_cpu(app->port_id));
+ break;
+ case SLI_CTAS_DALLAPP_ID:
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "8856 Deregistered all app ids\n");
+ read_lock(&vport->vmid_lock);
+ for (i = 0; i < phba->cfg_max_vmid; i++) {
+ vmp = &vport->vmid[i];
+ if (vmp->flag != LPFC_VMID_SLOT_FREE)
+ memset(vmp, 0, sizeof(struct lpfc_vmid));
+ }
+ read_unlock(&vport->vmid_lock);
+ /* for all elements in the hash table */
+ if (!hash_empty(vport->hash_table))
+ hash_for_each(vport->hash_table, bucket, cur, hnode)
+ hash_del(&cur->hnode);
+ set_bit(FC_ALLOW_VMID, &vport->load_flag);
+ break;
+ default:
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "8857 Invalid command code\n");
+ }
+free_res:
+ lpfc_ct_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
+}
+
+/**
+ * lpfc_vmid_cmd - Build and send a FDMI cmd to the specified NPort
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdcode: application server command code to send
+ * @vmid: pointer to vmid info structure
+ *
+ * Builds and sends a FDMI command using the CT subsystem.
+ */
+int
+lpfc_vmid_cmd(struct lpfc_vport *vport,
+ int cmdcode, struct lpfc_vmid *vmid)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_dmabuf *mp, *bmp;
+ struct lpfc_sli_ct_request *ctreq;
+ struct ulp_bde64 *bpl;
+ u32 size;
+ u32 rsp_size;
+ u8 *data;
+ struct lpfc_vmid_rapp_ident_list *rap;
+ struct lpfc_vmid_dapp_ident_list *dap;
+ u8 retry = 0;
+ struct lpfc_nodelist *ndlp;
+
+ void (*cmpl)(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb);
+
+ ndlp = lpfc_findnode_did(vport, FDMI_DID);
+ if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+ return 0;
+
+ cmpl = lpfc_cmpl_ct_cmd_vmid;
+
+ /* fill in BDEs for command */
+ /* Allocate buffer for command payload */
+ mp = kmalloc(sizeof(*mp), GFP_KERNEL);
+ if (!mp)
+ goto vmid_free_mp_exit;
+
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+ if (!mp->virt)
+ goto vmid_free_mp_virt_exit;
+
+ /* Allocate buffer for Buffer ptr list */
+ bmp = kmalloc(sizeof(*bmp), GFP_KERNEL);
+ if (!bmp)
+ goto vmid_free_bmp_exit;
+
+ bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
+ if (!bmp->virt)
+ goto vmid_free_bmp_virt_exit;
+
+ INIT_LIST_HEAD(&mp->list);
+ INIT_LIST_HEAD(&bmp->list);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "3275 VMID Request Data: x%lx x%x x%x\n",
+ vport->fc_flag, vport->port_state, cmdcode);
+ ctreq = (struct lpfc_sli_ct_request *)mp->virt;
+ data = mp->virt;
+ /* First populate the CT_IU preamble */
+ memset(data, 0, LPFC_BPL_SIZE);
+ ctreq->RevisionId.bits.Revision = SLI_CT_REVISION;
+ ctreq->RevisionId.bits.InId = 0;
+
+ ctreq->FsType = SLI_CT_MANAGEMENT_SERVICE;
+ ctreq->FsSubType = SLI_CT_APP_SEV_Subtypes;
+
+ ctreq->CommandResponse.bits.CmdRsp = cpu_to_be16(cmdcode);
+ rsp_size = LPFC_BPL_SIZE;
+ size = 0;
+
+ switch (cmdcode) {
+ case SLI_CTAS_RAPP_IDENT:
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "1329 RAPP_IDENT for %s\n", vmid->host_vmid);
+ ctreq->un.PortID = cpu_to_be32(vport->fc_myDID);
+ rap = (struct lpfc_vmid_rapp_ident_list *)
+ (DAPP_IDENT_OFFSET + data);
+ rap->no_of_objects = cpu_to_be32(1);
+ rap->obj[0].entity_id_len = vmid->vmid_len;
+ memcpy(rap->obj[0].entity_id, vmid->host_vmid, vmid->vmid_len);
+ size = RAPP_IDENT_OFFSET +
+ struct_size(rap, obj, be32_to_cpu(rap->no_of_objects));
+ retry = 1;
+ break;
+
+ case SLI_CTAS_GALLAPPIA_ID:
+ ctreq->un.PortID = cpu_to_be32(vport->fc_myDID);
+ size = GALLAPPIA_ID_SIZE;
+ break;
+
+ case SLI_CTAS_DAPP_IDENT:
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "1469 DAPP_IDENT for %s\n", vmid->host_vmid);
+ ctreq->un.PortID = cpu_to_be32(vport->fc_myDID);
+ dap = (struct lpfc_vmid_dapp_ident_list *)
+ (DAPP_IDENT_OFFSET + data);
+ dap->no_of_objects = cpu_to_be32(1);
+ dap->obj[0].entity_id_len = vmid->vmid_len;
+ memcpy(dap->obj[0].entity_id, vmid->host_vmid, vmid->vmid_len);
+ size = DAPP_IDENT_OFFSET +
+ struct_size(dap, obj, be32_to_cpu(dap->no_of_objects));
+ write_lock(&vport->vmid_lock);
+ vmid->flag &= ~LPFC_VMID_REGISTERED;
+ write_unlock(&vport->vmid_lock);
+ retry = 1;
+ break;
+
+ case SLI_CTAS_DALLAPP_ID:
+ ctreq->un.PortID = cpu_to_be32(vport->fc_myDID);
+ size = DALLAPP_ID_SIZE;
+ break;
+
+ default:
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "7062 VMID cmdcode x%x not supported\n",
+ cmdcode);
+ goto vmid_free_all_mem;
+ }
+
+ ctreq->CommandResponse.bits.Size = cpu_to_be16(rsp_size);
+
+ bpl = (struct ulp_bde64 *)bmp->virt;
+ bpl->addrHigh = putPaddrHigh(mp->phys);
+ bpl->addrLow = putPaddrLow(mp->phys);
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.f.bdeSize = size;
+
+ /* The lpfc_ct_cmd/lpfc_get_req shall increment ndlp reference count
+ * to hold ndlp reference for the corresponding callback function.
+ */
+ if (!lpfc_ct_cmd(vport, mp, bmp, ndlp, cmpl, rsp_size, retry))
+ return 0;
+
+ vmid_free_all_mem:
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ vmid_free_bmp_virt_exit:
+ kfree(bmp);
+ vmid_free_bmp_exit:
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ vmid_free_mp_virt_exit:
+ kfree(mp);
+ vmid_free_mp_exit:
+
+ /* Issue CT request failed */
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_DISCOVERY,
+ "3276 VMID CT request failed Data: x%x\n", cmdcode);
+ return -EIO;
+}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index ffe82d169b40..92b5b2dbe847 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2007-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2007-2015 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -20,6 +22,7 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
@@ -28,11 +31,13 @@
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>
+#include <linux/vmalloc.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -40,14 +45,16 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
#include "lpfc_version.h"
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
+#include "lpfc_bsg.h"
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/*
@@ -97,6 +104,12 @@ module_param(lpfc_debugfs_max_slow_ring_trc, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_max_slow_ring_trc,
"Set debugfs slow ring trace depth");
+/* This MUST be a power of 2 */
+static int lpfc_debugfs_max_nvmeio_trc;
+module_param(lpfc_debugfs_max_nvmeio_trc, int, 0444);
+MODULE_PARM_DESC(lpfc_debugfs_max_nvmeio_trc,
+ "Set debugfs NVME IO trace depth");
+
static int lpfc_debugfs_mask_disc_trc;
module_param(lpfc_debugfs_mask_disc_trc, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_mask_disc_trc,
@@ -135,7 +148,11 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
int i, index, len, enable;
uint32_t ms;
struct lpfc_debugfs_trc *dtp;
- char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE];
+ char *buffer;
+
+ buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return 0;
enable = lpfc_debugfs_enable;
lpfc_debugfs_enable = 0;
@@ -151,7 +168,7 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
snprintf(buffer,
LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
dtp->seq_cnt, ms, dtp->fmt);
- len += snprintf(buf+len, size-len, buffer,
+ len += scnprintf(buf+len, size-len, buffer,
dtp->data1, dtp->data2, dtp->data3);
}
for (i = 0; i < index; i++) {
@@ -162,11 +179,13 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
snprintf(buffer,
LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
dtp->seq_cnt, ms, dtp->fmt);
- len += snprintf(buf+len, size-len, buffer,
+ len += scnprintf(buf+len, size-len, buffer,
dtp->data1, dtp->data2, dtp->data3);
}
lpfc_debugfs_enable = enable;
+ kfree(buffer);
+
return len;
}
@@ -195,8 +214,11 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
int i, index, len, enable;
uint32_t ms;
struct lpfc_debugfs_trc *dtp;
- char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE];
+ char *buffer;
+ buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return 0;
enable = lpfc_debugfs_enable;
lpfc_debugfs_enable = 0;
@@ -212,7 +234,7 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
snprintf(buffer,
LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
dtp->seq_cnt, ms, dtp->fmt);
- len += snprintf(buf+len, size-len, buffer,
+ len += scnprintf(buf+len, size-len, buffer,
dtp->data1, dtp->data2, dtp->data3);
}
for (i = 0; i < index; i++) {
@@ -223,11 +245,13 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
snprintf(buffer,
LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
dtp->seq_cnt, ms, dtp->fmt);
- len += snprintf(buf+len, size-len, buffer,
+ len += scnprintf(buf+len, size-len, buffer,
dtp->data1, dtp->data2, dtp->data3);
}
lpfc_debugfs_enable = enable;
+ kfree(buffer);
+
return len;
}
@@ -256,7 +280,7 @@ static int
lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
{
int len = 0;
- int cnt, i, j, found, posted, low;
+ int i, j, found, posted, low;
uint32_t phys, raw_index, getidx;
struct lpfc_hbq_init *hip;
struct hbq_s *hbqs;
@@ -266,7 +290,7 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
if (phba->sli_rev != 3)
return 0;
- cnt = LPFC_HBQINFO_SIZE;
+
spin_lock_irq(&phba->hbalock);
/* toggle between multiple hbqs, if any */
@@ -281,7 +305,7 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
i = lpfc_debugfs_last_hbq;
- len += snprintf(buf+len, size-len, "HBQ %d Info\n", i);
+ len += scnprintf(buf+len, size-len, "HBQ %d Info\n", i);
hbqs = &phba->hbqs[i];
posted = 0;
@@ -289,21 +313,21 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
posted++;
hip = lpfc_hbq_defs[i];
- len += snprintf(buf+len, size-len,
+ len += scnprintf(buf+len, size-len,
"idx:%d prof:%d rn:%d bufcnt:%d icnt:%d acnt:%d posted %d\n",
hip->hbq_index, hip->profile, hip->rn,
hip->buffer_count, hip->init_count, hip->add_count, posted);
raw_index = phba->hbq_get[i];
getidx = le32_to_cpu(raw_index);
- len += snprintf(buf+len, size-len,
- "entrys:%d bufcnt:%d Put:%d nPut:%d localGet:%d hbaGet:%d\n",
+ len += scnprintf(buf+len, size-len,
+ "entries:%d bufcnt:%d Put:%d nPut:%d localGet:%d hbaGet:%d\n",
hbqs->entry_count, hbqs->buffer_count, hbqs->hbqPutIdx,
hbqs->next_hbqPutIdx, hbqs->local_hbqGetIdx, getidx);
hbqe = (struct lpfc_hbq_entry *) phba->hbqs[i].hbq_virt;
for (j=0; j<hbqs->entry_count; j++) {
- len += snprintf(buf+len, size-len,
+ len += scnprintf(buf+len, size-len,
"%03d: %08x %04x %05x ", j,
le32_to_cpu(hbqe->bde.addrLow),
le32_to_cpu(hbqe->bde.tus.w),
@@ -315,14 +339,16 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
low = hbqs->hbqPutIdx - posted;
if (low >= 0) {
if ((j >= hbqs->hbqPutIdx) || (j < low)) {
- len += snprintf(buf+len, size-len, "Unused\n");
+ len += scnprintf(buf + len, size - len,
+ "Unused\n");
goto skipit;
}
}
else {
if ((j >= hbqs->hbqPutIdx) &&
(j < (hbqs->entry_count+low))) {
- len += snprintf(buf+len, size-len, "Unused\n");
+ len += scnprintf(buf + len, size - len,
+ "Unused\n");
goto skipit;
}
}
@@ -332,8 +358,8 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
phys = ((uint64_t)hbq_buf->dbuf.phys & 0xffffffff);
if (phys == le32_to_cpu(hbqe->bde.addrLow)) {
- len += snprintf(buf+len, size-len,
- "Buf%d: %p %06x\n", i,
+ len += scnprintf(buf+len, size-len,
+ "Buf%d: x%px %06x\n", i,
hbq_buf->dbuf.virt, hbq_buf->tag);
found = 1;
break;
@@ -341,7 +367,7 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
i++;
}
if (!found) {
- len += snprintf(buf+len, size-len, "No DMAinfo?\n");
+ len += scnprintf(buf+len, size-len, "No DMAinfo?\n");
}
skipit:
hbqe++;
@@ -352,6 +378,267 @@ skipit:
return len;
}
+static int lpfc_debugfs_last_xripool;
+
+/**
+ * lpfc_debugfs_commonxripools_data - Dump Hardware Queue info to a buffer
+ * @phba: The HBA to gather host buffer info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the Hardware Queue info from the @phba to @buf up to
+ * @size number of bytes. A header that describes the current hdwq state will be
+ * dumped to @buf first and then info on each hdwq entry will be dumped to @buf
+ * until @size bytes have been dumped or all the hdwq info has been dumped.
+ *
+ * Notes:
+ * This routine will rotate through each configured Hardware Queue each
+ * time called.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_commonxripools_data(struct lpfc_hba *phba, char *buf, int size)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ int len = 0;
+ int i, out;
+ unsigned long iflag;
+
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ if (len > (LPFC_DUMP_MULTIXRIPOOL_SIZE - 80))
+ break;
+ qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_xripool];
+
+ len += scnprintf(buf + len, size - len, "HdwQ %d Info ", i);
+ spin_lock_irqsave(&qp->abts_io_buf_list_lock, iflag);
+ spin_lock(&qp->io_buf_list_get_lock);
+ spin_lock(&qp->io_buf_list_put_lock);
+ out = qp->total_io_bufs - (qp->get_io_bufs + qp->put_io_bufs +
+ qp->abts_scsi_io_bufs + qp->abts_nvme_io_bufs);
+ len += scnprintf(buf + len, size - len,
+ "tot:%d get:%d put:%d mt:%d "
+ "ABTS scsi:%d nvme:%d Out:%d\n",
+ qp->total_io_bufs, qp->get_io_bufs, qp->put_io_bufs,
+ qp->empty_io_bufs, qp->abts_scsi_io_bufs,
+ qp->abts_nvme_io_bufs, out);
+ spin_unlock(&qp->io_buf_list_put_lock);
+ spin_unlock(&qp->io_buf_list_get_lock);
+ spin_unlock_irqrestore(&qp->abts_io_buf_list_lock, iflag);
+
+ lpfc_debugfs_last_xripool++;
+ if (lpfc_debugfs_last_xripool >= phba->cfg_hdw_queue)
+ lpfc_debugfs_last_xripool = 0;
+ }
+
+ return len;
+}
+
+/**
+ * lpfc_debugfs_multixripools_data - Display multi-XRI pools information
+ * @phba: The HBA to gather host buffer info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine displays current multi-XRI pools information including XRI
+ * count in public, private and txcmplq. It also displays current high and
+ * low watermark.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size)
+{
+ u32 i;
+ u32 hwq_count;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ u32 txcmplq_cnt;
+ char tmp[LPFC_DEBUG_OUT_LINE_SZ] = {0};
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return 0;
+
+ if (!phba->sli4_hba.hdwq)
+ return 0;
+
+ if (!phba->cfg_xri_rebalancing) {
+ i = lpfc_debugfs_commonxripools_data(phba, buf, size);
+ return i;
+ }
+
+ /*
+ * Pbl: Current number of free XRIs in public pool
+ * Pvt: Current number of free XRIs in private pool
+ * Busy: Current number of outstanding XRIs
+ * HWM: Current high watermark
+ * pvt_empty: Incremented by 1 when IO submission fails (no xri)
+ * pbl_empty: Incremented by 1 when all pbl_pool are empty during
+ * IO submission
+ */
+ scnprintf(tmp, sizeof(tmp),
+ "HWQ: Pbl Pvt Busy HWM | pvt_empty pbl_empty ");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+
+#ifdef LPFC_MXP_STAT
+ /*
+ * MAXH: Max high watermark seen so far
+ * above_lmt: Incremented by 1 if xri_owned > xri_limit during
+ * IO submission
+ * below_lmt: Incremented by 1 if xri_owned <= xri_limit during
+ * IO submission
+ * locPbl_hit: Incremented by 1 if successfully get a batch of XRI from
+ * local pbl_pool
+ * othPbl_hit: Incremented by 1 if successfully get a batch of XRI from
+ * other pbl_pool
+ */
+ scnprintf(tmp, sizeof(tmp),
+ "MAXH above_lmt below_lmt locPbl_hit othPbl_hit");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+
+ /*
+ * sPbl: snapshot of Pbl 15 sec after stat gets cleared
+ * sPvt: snapshot of Pvt 15 sec after stat gets cleared
+ * sBusy: snapshot of Busy 15 sec after stat gets cleared
+ */
+ scnprintf(tmp, sizeof(tmp),
+ " | sPbl sPvt sBusy");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+#endif
+
+ scnprintf(tmp, sizeof(tmp), "\n");
+ if (strlcat(buf, tmp, size) >= size)
+ return strnlen(buf, size);
+
+ hwq_count = phba->cfg_hdw_queue;
+ for (i = 0; i < hwq_count; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ continue;
+ pbl_pool = &multixri_pool->pbl_pool;
+ pvt_pool = &multixri_pool->pvt_pool;
+ txcmplq_cnt = qp->io_wq->pring->txcmplq_cnt;
+
+ scnprintf(tmp, sizeof(tmp),
+ "%03d: %4d %4d %4d %4d | %10d %10d ",
+ i, pbl_pool->count, pvt_pool->count,
+ txcmplq_cnt, pvt_pool->high_watermark,
+ qp->empty_io_bufs, multixri_pool->pbl_empty_count);
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+
+#ifdef LPFC_MXP_STAT
+ scnprintf(tmp, sizeof(tmp),
+ "%4d %10d %10d %10d %10d",
+ multixri_pool->stat_max_hwm,
+ multixri_pool->above_limit_count,
+ multixri_pool->below_limit_count,
+ multixri_pool->local_pbl_hit_count,
+ multixri_pool->other_pbl_hit_count);
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+
+ scnprintf(tmp, sizeof(tmp),
+ " | %4d %4d %5d",
+ multixri_pool->stat_pbl_count,
+ multixri_pool->stat_pvt_count,
+ multixri_pool->stat_busy_count);
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+#endif
+
+ scnprintf(tmp, sizeof(tmp), "\n");
+ if (strlcat(buf, tmp, size) >= size)
+ break;
+ }
+ return strnlen(buf, size);
+}
+
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+static int lpfc_debugfs_last_lock;
+
+/**
+ * lpfc_debugfs_lockstat_data - Dump Hardware Queue info to a buffer
+ * @phba: The HBA to gather host buffer info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the Hardware Queue info from the @phba to @buf up to
+ * @size number of bytes. A header that describes the current hdwq state will be
+ * dumped to @buf first and then info on each hdwq entry will be dumped to @buf
+ * until @size bytes have been dumped or all the hdwq info has been dumped.
+ *
+ * Notes:
+ * This routine will rotate through each configured Hardware Queue each
+ * time called.
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_lockstat_data(struct lpfc_hba *phba, char *buf, int size)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ int len = 0;
+ int i;
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return 0;
+
+ if (!phba->sli4_hba.hdwq)
+ return 0;
+
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ if (len > (LPFC_HDWQINFO_SIZE - 100))
+ break;
+ qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_lock];
+
+ len += scnprintf(buf + len, size - len, "HdwQ %03d Lock ", i);
+ if (phba->cfg_xri_rebalancing) {
+ len += scnprintf(buf + len, size - len,
+ "get_pvt:%d mv_pvt:%d "
+ "mv2pub:%d mv2pvt:%d "
+ "put_pvt:%d put_pub:%d wq:%d\n",
+ qp->lock_conflict.alloc_pvt_pool,
+ qp->lock_conflict.mv_from_pvt_pool,
+ qp->lock_conflict.mv_to_pub_pool,
+ qp->lock_conflict.mv_to_pvt_pool,
+ qp->lock_conflict.free_pvt_pool,
+ qp->lock_conflict.free_pub_pool,
+ qp->lock_conflict.wq_access);
+ } else {
+ len += scnprintf(buf + len, size - len,
+ "get:%d put:%d free:%d wq:%d\n",
+ qp->lock_conflict.alloc_xri_get,
+ qp->lock_conflict.alloc_xri_put,
+ qp->lock_conflict.free_xri,
+ qp->lock_conflict.wq_access);
+ }
+
+ lpfc_debugfs_last_lock++;
+ if (lpfc_debugfs_last_lock >= phba->cfg_hdw_queue)
+ lpfc_debugfs_last_lock = 0;
+ }
+
+ return len;
+}
+#endif
+
static int lpfc_debugfs_last_hba_slim_off;
/**
@@ -378,12 +665,16 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
int len = 0;
int i, off;
uint32_t *ptr;
- char buffer[1024];
+ char *buffer;
+
+ buffer = kmalloc(1024, GFP_KERNEL);
+ if (!buffer)
+ return 0;
off = 0;
spin_lock_irq(&phba->hbalock);
- len += snprintf(buf+len, size-len, "HBA SLIM\n");
+ len += scnprintf(buf+len, size-len, "HBA SLIM\n");
lpfc_memcpy_from_slim(buffer,
phba->MBslimaddr + lpfc_debugfs_last_hba_slim_off, 1024);
@@ -397,7 +688,7 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
i = 1024;
while (i > 0) {
- len += snprintf(buf+len, size-len,
+ len += scnprintf(buf+len, size-len,
"%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
*(ptr+5), *(ptr+6), *(ptr+7));
@@ -407,6 +698,8 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
}
spin_unlock_irq(&phba->hbalock);
+ kfree(buffer);
+
return len;
}
@@ -439,11 +732,11 @@ lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size)
off = 0;
spin_lock_irq(&phba->hbalock);
- len += snprintf(buf+len, size-len, "SLIM Mailbox\n");
+ len += scnprintf(buf+len, size-len, "SLIM Mailbox\n");
ptr = (uint32_t *)phba->slim2p.virt;
i = sizeof(MAILBOX_t);
while (i > 0) {
- len += snprintf(buf+len, size-len,
+ len += scnprintf(buf+len, size-len,
"%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
*(ptr+5), *(ptr+6), *(ptr+7));
@@ -452,11 +745,11 @@ lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size)
off += (8 * sizeof(uint32_t));
}
- len += snprintf(buf+len, size-len, "SLIM PCB\n");
+ len += scnprintf(buf+len, size-len, "SLIM PCB\n");
ptr = (uint32_t *)phba->pcb;
i = sizeof(PCB_t);
while (i > 0) {
- len += snprintf(buf+len, size-len,
+ len += scnprintf(buf+len, size-len,
"%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
*(ptr+5), *(ptr+6), *(ptr+7));
@@ -465,23 +758,28 @@ lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size)
off += (8 * sizeof(uint32_t));
}
- for (i = 0; i < 4; i++) {
- pgpp = &phba->port_gp[i];
- pring = &psli->ring[i];
- len += snprintf(buf+len, size-len,
- "Ring %d: CMD GetInx:%d (Max:%d Next:%d "
- "Local:%d flg:x%x) RSP PutInx:%d Max:%d\n",
- i, pgpp->cmdGetInx, pring->numCiocb,
- pring->next_cmdidx, pring->local_getidx,
- pring->flag, pgpp->rspPutInx, pring->numRiocb);
- }
-
if (phba->sli_rev <= LPFC_SLI_REV3) {
+ for (i = 0; i < 4; i++) {
+ pgpp = &phba->port_gp[i];
+ pring = &psli->sli3_ring[i];
+ len += scnprintf(buf+len, size-len,
+ "Ring %d: CMD GetInx:%d "
+ "(Max:%d Next:%d "
+ "Local:%d flg:x%x) "
+ "RSP PutInx:%d Max:%d\n",
+ i, pgpp->cmdGetInx,
+ pring->sli.sli3.numCiocb,
+ pring->sli.sli3.next_cmdidx,
+ pring->sli.sli3.local_getidx,
+ pring->flag, pgpp->rspPutInx,
+ pring->sli.sli3.numRiocb);
+ }
+
word0 = readl(phba->HAregaddr);
word1 = readl(phba->CAregaddr);
word2 = readl(phba->HSregaddr);
word3 = readl(phba->HCregaddr);
- len += snprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x "
+ len += scnprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x "
"HC:%08x\n", word0, word1, word2, word3);
}
spin_unlock_irq(&phba->hbalock);
@@ -507,17 +805,24 @@ static int
lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
{
int len = 0;
- int cnt;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ int i, iocnt, outio, cnt;
+ struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp;
- unsigned char *statep, *name;
+ unsigned char *statep;
+ unsigned long iflags;
+ struct nvme_fc_local_port *localport;
+ struct nvme_fc_remote_port *nrport = NULL;
+ struct lpfc_nvme_rport *rport;
cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE);
+ outio = 0;
- spin_lock_irq(shost->host_lock);
+ len += scnprintf(buf+len, size-len, "\nFCP Nodelist Entries ...\n");
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ iocnt = 0;
if (!cnt) {
- len += snprintf(buf+len, size-len,
+ len += scnprintf(buf+len, size-len,
"Missing Nodelist Entries\n");
break;
}
@@ -538,11 +843,16 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
case NLP_STE_PRLI_ISSUE:
statep = "PRLI ";
break;
+ case NLP_STE_LOGO_ISSUE:
+ statep = "LOGO ";
+ break;
case NLP_STE_UNMAPPED_NODE:
statep = "UNMAP ";
+ iocnt = 1;
break;
case NLP_STE_MAPPED_NODE:
statep = "MAPPED";
+ iocnt = 1;
break;
case NLP_STE_NPR_NODE:
statep = "NPR ";
@@ -550,40 +860,943 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
default:
statep = "UNKNOWN";
}
- len += snprintf(buf+len, size-len, "%s DID:x%06x ",
- statep, ndlp->nlp_DID);
- name = (unsigned char *)&ndlp->nlp_portname;
- len += snprintf(buf+len, size-len,
- "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x ",
- *name, *(name+1), *(name+2), *(name+3),
- *(name+4), *(name+5), *(name+6), *(name+7));
- name = (unsigned char *)&ndlp->nlp_nodename;
- len += snprintf(buf+len, size-len,
- "WWNN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x ",
- *name, *(name+1), *(name+2), *(name+3),
- *(name+4), *(name+5), *(name+6), *(name+7));
- len += snprintf(buf+len, size-len, "RPI:%03d flag:x%08x ",
- ndlp->nlp_rpi, ndlp->nlp_flag);
+ len += scnprintf(buf+len, size-len, "%s DID:x%06x ",
+ statep, ndlp->nlp_DID);
+ len += scnprintf(buf+len, size-len,
+ "WWPN x%016llx ",
+ wwn_to_u64(ndlp->nlp_portname.u.wwn));
+ len += scnprintf(buf+len, size-len,
+ "WWNN x%016llx ",
+ wwn_to_u64(ndlp->nlp_nodename.u.wwn));
+ len += scnprintf(buf+len, size-len, "RPI:x%04x ",
+ ndlp->nlp_rpi);
+ len += scnprintf(buf+len, size-len, "flag:x%08lx ",
+ ndlp->nlp_flag);
if (!ndlp->nlp_type)
- len += snprintf(buf+len, size-len, "UNKNOWN_TYPE ");
+ len += scnprintf(buf+len, size-len, "UNKNOWN_TYPE ");
if (ndlp->nlp_type & NLP_FC_NODE)
- len += snprintf(buf+len, size-len, "FC_NODE ");
- if (ndlp->nlp_type & NLP_FABRIC)
- len += snprintf(buf+len, size-len, "FABRIC ");
+ len += scnprintf(buf+len, size-len, "FC_NODE ");
+ if (ndlp->nlp_type & NLP_FABRIC) {
+ len += scnprintf(buf+len, size-len, "FABRIC ");
+ iocnt = 0;
+ }
if (ndlp->nlp_type & NLP_FCP_TARGET)
- len += snprintf(buf+len, size-len, "FCP_TGT sid:%d ",
+ len += scnprintf(buf+len, size-len, "FCP_TGT sid:%d ",
ndlp->nlp_sid);
if (ndlp->nlp_type & NLP_FCP_INITIATOR)
- len += snprintf(buf+len, size-len, "FCP_INITIATOR ");
- len += snprintf(buf+len, size-len, "usgmap:%x ",
- ndlp->nlp_usg_map);
- len += snprintf(buf+len, size-len, "refcnt:%x",
- atomic_read(&ndlp->kref.refcount));
- len += snprintf(buf+len, size-len, "\n");
- }
- spin_unlock_irq(shost->host_lock);
+ len += scnprintf(buf+len, size-len, "FCP_INITIATOR ");
+ if (ndlp->nlp_type & NLP_NVME_TARGET)
+ len += scnprintf(buf + len,
+ size - len, "NVME_TGT sid:%d ",
+ NLP_NO_SID);
+ if (ndlp->nlp_type & NLP_NVME_INITIATOR)
+ len += scnprintf(buf + len,
+ size - len, "NVME_INITIATOR ");
+ len += scnprintf(buf+len, size-len, "refcnt:%d",
+ kref_read(&ndlp->kref));
+ if (iocnt) {
+ i = atomic_read(&ndlp->cmd_pending);
+ len += scnprintf(buf + len, size - len,
+ " OutIO:x%x Qdepth x%x",
+ i, ndlp->cmd_qdepth);
+ outio += i;
+ }
+ len += scnprintf(buf+len, size-len, " xpt:x%x",
+ ndlp->fc4_xpt_flags);
+ if (ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING)
+ len += scnprintf(buf+len, size-len, " defer:%x",
+ ndlp->nlp_defer_did);
+ len += scnprintf(buf+len, size-len, "\n");
+ }
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
+
+ len += scnprintf(buf + len, size - len,
+ "\nOutstanding IO x%x\n", outio);
+
+ if (phba->nvmet_support && phba->targetport && (vport == phba->pport)) {
+ len += scnprintf(buf + len, size - len,
+ "\nNVME Targetport Entry ...\n");
+
+ /* Port state is only one of two values for now. */
+ if (phba->targetport->port_id)
+ statep = "REGISTERED";
+ else
+ statep = "INIT";
+ len += scnprintf(buf + len, size - len,
+ "TGT WWNN x%llx WWPN x%llx State %s\n",
+ wwn_to_u64(vport->fc_nodename.u.wwn),
+ wwn_to_u64(vport->fc_portname.u.wwn),
+ statep);
+ len += scnprintf(buf + len, size - len,
+ " Targetport DID x%06x\n",
+ phba->targetport->port_id);
+ goto out_exit;
+ }
+
+ len += scnprintf(buf + len, size - len,
+ "\nNVME Lport/Rport Entries ...\n");
+
+ localport = vport->localport;
+ if (!localport)
+ goto out_exit;
+
+ /* Port state is only one of two values for now. */
+ if (localport->port_id)
+ statep = "ONLINE";
+ else
+ statep = "UNKNOWN ";
+
+ len += scnprintf(buf + len, size - len,
+ "Lport DID x%06x PortState %s\n",
+ localport->port_id, statep);
+
+ len += scnprintf(buf + len, size - len, "\tRport List:\n");
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ /* local short-hand pointer. */
+ spin_lock(&ndlp->lock);
+ rport = lpfc_ndlp_get_nrport(ndlp);
+ if (rport)
+ nrport = rport->remoteport;
+ else
+ nrport = NULL;
+ spin_unlock(&ndlp->lock);
+ if (!nrport)
+ continue;
+
+ /* Port state is only one of two values for now. */
+ switch (nrport->port_state) {
+ case FC_OBJSTATE_ONLINE:
+ statep = "ONLINE";
+ break;
+ case FC_OBJSTATE_UNKNOWN:
+ statep = "UNKNOWN ";
+ break;
+ default:
+ statep = "UNSUPPORTED";
+ break;
+ }
+
+ /* Tab in to show lport ownership. */
+ len += scnprintf(buf + len, size - len,
+ "\t%s Port ID:x%06x ",
+ statep, nrport->port_id);
+ len += scnprintf(buf + len, size - len, "WWPN x%llx ",
+ nrport->port_name);
+ len += scnprintf(buf + len, size - len, "WWNN x%llx ",
+ nrport->node_name);
+
+ /* An NVME rport can have multiple roles. */
+ if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR)
+ len += scnprintf(buf + len, size - len,
+ "INITIATOR ");
+ if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET)
+ len += scnprintf(buf + len, size - len,
+ "TARGET ");
+ if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY)
+ len += scnprintf(buf + len, size - len,
+ "DISCSRVC ");
+ if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR |
+ FC_PORT_ROLE_NVME_TARGET |
+ FC_PORT_ROLE_NVME_DISCOVERY))
+ len += scnprintf(buf + len, size - len,
+ "UNKNOWN ROLE x%x",
+ nrport->port_role);
+ /* Terminate the string. */
+ len += scnprintf(buf + len, size - len, "\n");
+ }
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
+ out_exit:
+ return len;
+}
+
+/**
+ * lpfc_debugfs_nvmestat_data - Dump target node list to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct lpfc_async_xchg_ctx *ctxp, *next_ctxp;
+ struct nvme_fc_local_port *localport;
+ struct lpfc_fc4_ctrl_stat *cstat;
+ struct lpfc_nvme_lport *lport;
+ uint64_t data1, data2, data3;
+ uint64_t tot, totin, totout;
+ int cnt, i;
+ int len = 0;
+
+ if (phba->nvmet_support) {
+ if (!phba->targetport)
+ return len;
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ len += scnprintf(buf + len, size - len,
+ "\nNVME Targetport Statistics\n");
+
+ len += scnprintf(buf + len, size - len,
+ "LS: Rcv %08x Drop %08x Abort %08x\n",
+ atomic_read(&tgtp->rcv_ls_req_in),
+ atomic_read(&tgtp->rcv_ls_req_drop),
+ atomic_read(&tgtp->xmt_ls_abort));
+ if (atomic_read(&tgtp->rcv_ls_req_in) !=
+ atomic_read(&tgtp->rcv_ls_req_out)) {
+ len += scnprintf(buf + len, size - len,
+ "Rcv LS: in %08x != out %08x\n",
+ atomic_read(&tgtp->rcv_ls_req_in),
+ atomic_read(&tgtp->rcv_ls_req_out));
+ }
+
+ len += scnprintf(buf + len, size - len,
+ "LS: Xmt %08x Drop %08x Cmpl %08x\n",
+ atomic_read(&tgtp->xmt_ls_rsp),
+ atomic_read(&tgtp->xmt_ls_drop),
+ atomic_read(&tgtp->xmt_ls_rsp_cmpl));
+
+ len += scnprintf(buf + len, size - len,
+ "LS: RSP Abort %08x xb %08x Err %08x\n",
+ atomic_read(&tgtp->xmt_ls_rsp_aborted),
+ atomic_read(&tgtp->xmt_ls_rsp_xb_set),
+ atomic_read(&tgtp->xmt_ls_rsp_error));
+
+ len += scnprintf(buf + len, size - len,
+ "FCP: Rcv %08x Defer %08x Release %08x "
+ "Drop %08x\n",
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_defer),
+ atomic_read(&tgtp->xmt_fcp_release),
+ atomic_read(&tgtp->rcv_fcp_cmd_drop));
+
+ if (atomic_read(&tgtp->rcv_fcp_cmd_in) !=
+ atomic_read(&tgtp->rcv_fcp_cmd_out)) {
+ len += scnprintf(buf + len, size - len,
+ "Rcv FCP: in %08x != out %08x\n",
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out));
+ }
+
+ len += scnprintf(buf + len, size - len,
+ "FCP Rsp: read %08x readrsp %08x "
+ "write %08x rsp %08x\n",
+ atomic_read(&tgtp->xmt_fcp_read),
+ atomic_read(&tgtp->xmt_fcp_read_rsp),
+ atomic_read(&tgtp->xmt_fcp_write),
+ atomic_read(&tgtp->xmt_fcp_rsp));
+
+ len += scnprintf(buf + len, size - len,
+ "FCP Rsp Cmpl: %08x err %08x drop %08x\n",
+ atomic_read(&tgtp->xmt_fcp_rsp_cmpl),
+ atomic_read(&tgtp->xmt_fcp_rsp_error),
+ atomic_read(&tgtp->xmt_fcp_rsp_drop));
+
+ len += scnprintf(buf + len, size - len,
+ "FCP Rsp Abort: %08x xb %08x xricqe %08x\n",
+ atomic_read(&tgtp->xmt_fcp_rsp_aborted),
+ atomic_read(&tgtp->xmt_fcp_rsp_xb_set),
+ atomic_read(&tgtp->xmt_fcp_xri_abort_cqe));
+
+ len += scnprintf(buf + len, size - len,
+ "ABORT: Xmt %08x Cmpl %08x\n",
+ atomic_read(&tgtp->xmt_fcp_abort),
+ atomic_read(&tgtp->xmt_fcp_abort_cmpl));
+
+ len += scnprintf(buf + len, size - len,
+ "ABORT: Sol %08x Usol %08x Err %08x Cmpl %08x",
+ atomic_read(&tgtp->xmt_abort_sol),
+ atomic_read(&tgtp->xmt_abort_unsol),
+ atomic_read(&tgtp->xmt_abort_rsp),
+ atomic_read(&tgtp->xmt_abort_rsp_error));
+
+ len += scnprintf(buf + len, size - len, "\n");
+
+ cnt = 0;
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_for_each_entry_safe(ctxp, next_ctxp,
+ &phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
+ list) {
+ cnt++;
+ }
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ if (cnt) {
+ len += scnprintf(buf + len, size - len,
+ "ABORT: %d ctx entries\n", cnt);
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_for_each_entry_safe(ctxp, next_ctxp,
+ &phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
+ list) {
+ if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ))
+ break;
+ len += scnprintf(buf + len, size - len,
+ "Entry: oxid %x state %x "
+ "flag %x\n",
+ ctxp->oxid, ctxp->state,
+ ctxp->flag);
+ }
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ }
+
+ /* Calculate outstanding IOs */
+ tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
+ tot += atomic_read(&tgtp->xmt_fcp_release);
+ tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot;
+
+ len += scnprintf(buf + len, size - len,
+ "IO_CTX: %08x WAIT: cur %08x tot %08x\n"
+ "CTX Outstanding %08llx\n",
+ phba->sli4_hba.nvmet_xri_cnt,
+ phba->sli4_hba.nvmet_io_wait_cnt,
+ phba->sli4_hba.nvmet_io_wait_total,
+ tot);
+ } else {
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
+ return len;
+
+ localport = vport->localport;
+ if (!localport)
+ return len;
+ lport = (struct lpfc_nvme_lport *)localport->private;
+ if (!lport)
+ return len;
+
+ len += scnprintf(buf + len, size - len,
+ "\nNVME HDWQ Statistics\n");
+
+ len += scnprintf(buf + len, size - len,
+ "LS: Xmt %016x Cmpl %016x\n",
+ atomic_read(&lport->fc4NvmeLsRequests),
+ atomic_read(&lport->fc4NvmeLsCmpls));
+
+ totin = 0;
+ totout = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
+ tot = cstat->io_cmpls;
+ totin += tot;
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
+ totout += (data1 + data2 + data3);
+
+ /* Limit to 32, debugfs display buffer limitation */
+ if (i >= 32)
+ continue;
+
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "HDWQ (%d): Rd %016llx Wr %016llx "
+ "IO %016llx ",
+ i, data1, data2, data3);
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Cmpl %016llx OutIO %016llx\n",
+ tot, ((data1 + data2 + data3) - tot));
+ }
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Total FCP Cmpl %016llx Issue %016llx "
+ "OutIO %016llx\n",
+ totin, totout, totout - totin);
+
+ len += scnprintf(buf + len, size - len,
+ "LS Xmt Err: Abrt %08x Err %08x "
+ "Cmpl Err: xb %08x Err %08x\n",
+ atomic_read(&lport->xmt_ls_abort),
+ atomic_read(&lport->xmt_ls_err),
+ atomic_read(&lport->cmpl_ls_xb),
+ atomic_read(&lport->cmpl_ls_err));
+
+ len += scnprintf(buf + len, size - len,
+ "FCP Xmt Err: noxri %06x nondlp %06x "
+ "qdepth %06x wqerr %06x err %06x Abrt %06x\n",
+ atomic_read(&lport->xmt_fcp_noxri),
+ atomic_read(&lport->xmt_fcp_bad_ndlp),
+ atomic_read(&lport->xmt_fcp_qdepth),
+ atomic_read(&lport->xmt_fcp_wqerr),
+ atomic_read(&lport->xmt_fcp_err),
+ atomic_read(&lport->xmt_fcp_abort));
+
+ len += scnprintf(buf + len, size - len,
+ "FCP Cmpl Err: xb %08x Err %08x\n",
+ atomic_read(&lport->cmpl_fcp_xb),
+ atomic_read(&lport->cmpl_fcp_err));
+
+ }
+
+ return len;
+}
+
+/**
+ * lpfc_debugfs_scsistat_data - Dump target node list to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the SCSI statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_scsistat_data(struct lpfc_vport *vport, char *buf, int size)
+{
+ int len;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fc4_ctrl_stat *cstat;
+ u64 data1, data2, data3;
+ u64 tot, totin, totout;
+ int i;
+ char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};
+
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
+ (phba->sli_rev != LPFC_SLI_REV4))
+ return 0;
+
+ scnprintf(buf, size, "SCSI HDWQ Statistics\n");
+
+ totin = 0;
+ totout = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
+ tot = cstat->io_cmpls;
+ totin += tot;
+ data1 = cstat->input_requests;
+ data2 = cstat->output_requests;
+ data3 = cstat->control_requests;
+ totout += (data1 + data2 + data3);
+
+ scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
+ "IO %016llx ", i, data1, data2, data3);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
+ tot, ((data1 + data2 + data3) - tot));
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+ }
+ scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
+ "OutIO %016llx\n", totin, totout, totout - totin);
+ strlcat(buf, tmp, size);
+
+buffer_done:
+ len = strnlen(buf, size);
+
+ return len;
+}
+
+void
+lpfc_io_ktime(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
+{
+ uint64_t seg1, seg2, seg3, seg4;
+ uint64_t segsum;
+
+ if (!lpfc_cmd->ts_last_cmd ||
+ !lpfc_cmd->ts_cmd_start ||
+ !lpfc_cmd->ts_cmd_wqput ||
+ !lpfc_cmd->ts_isr_cmpl ||
+ !lpfc_cmd->ts_data_io)
+ return;
+
+ if (lpfc_cmd->ts_data_io < lpfc_cmd->ts_cmd_start)
+ return;
+ if (lpfc_cmd->ts_cmd_start < lpfc_cmd->ts_last_cmd)
+ return;
+ if (lpfc_cmd->ts_cmd_wqput < lpfc_cmd->ts_cmd_start)
+ return;
+ if (lpfc_cmd->ts_isr_cmpl < lpfc_cmd->ts_cmd_wqput)
+ return;
+ if (lpfc_cmd->ts_data_io < lpfc_cmd->ts_isr_cmpl)
+ return;
+ /*
+ * Segment 1 - Time from Last FCP command cmpl is handed
+ * off to NVME Layer to start of next command.
+ * Segment 2 - Time from Driver receives a IO cmd start
+ * from NVME Layer to WQ put is done on IO cmd.
+ * Segment 3 - Time from Driver WQ put is done on IO cmd
+ * to MSI-X ISR for IO cmpl.
+ * Segment 4 - Time from MSI-X ISR for IO cmpl to when
+ * cmpl is handled off to the NVME Layer.
+ */
+ seg1 = lpfc_cmd->ts_cmd_start - lpfc_cmd->ts_last_cmd;
+ if (seg1 > 5000000) /* 5 ms - for sequential IOs only */
+ seg1 = 0;
+
+ /* Calculate times relative to start of IO */
+ seg2 = (lpfc_cmd->ts_cmd_wqput - lpfc_cmd->ts_cmd_start);
+ segsum = seg2;
+ seg3 = lpfc_cmd->ts_isr_cmpl - lpfc_cmd->ts_cmd_start;
+ if (segsum > seg3)
+ return;
+ seg3 -= segsum;
+ segsum += seg3;
+
+ seg4 = lpfc_cmd->ts_data_io - lpfc_cmd->ts_cmd_start;
+ if (segsum > seg4)
+ return;
+ seg4 -= segsum;
+
+ phba->ktime_data_samples++;
+ phba->ktime_seg1_total += seg1;
+ if (seg1 < phba->ktime_seg1_min)
+ phba->ktime_seg1_min = seg1;
+ else if (seg1 > phba->ktime_seg1_max)
+ phba->ktime_seg1_max = seg1;
+ phba->ktime_seg2_total += seg2;
+ if (seg2 < phba->ktime_seg2_min)
+ phba->ktime_seg2_min = seg2;
+ else if (seg2 > phba->ktime_seg2_max)
+ phba->ktime_seg2_max = seg2;
+ phba->ktime_seg3_total += seg3;
+ if (seg3 < phba->ktime_seg3_min)
+ phba->ktime_seg3_min = seg3;
+ else if (seg3 > phba->ktime_seg3_max)
+ phba->ktime_seg3_max = seg3;
+ phba->ktime_seg4_total += seg4;
+ if (seg4 < phba->ktime_seg4_min)
+ phba->ktime_seg4_min = seg4;
+ else if (seg4 > phba->ktime_seg4_max)
+ phba->ktime_seg4_max = seg4;
+
+ lpfc_cmd->ts_last_cmd = 0;
+ lpfc_cmd->ts_cmd_start = 0;
+ lpfc_cmd->ts_cmd_wqput = 0;
+ lpfc_cmd->ts_isr_cmpl = 0;
+ lpfc_cmd->ts_data_io = 0;
+}
+
+/**
+ * lpfc_debugfs_ioktime_data - Dump target node list to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_ioktime_data(struct lpfc_vport *vport, char *buf, int size)
+{
+ struct lpfc_hba *phba = vport->phba;
+ int len = 0;
+
+ if (phba->nvmet_support == 0) {
+ /* Initiator */
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "ktime %s: Total Samples: %lld\n",
+ (phba->ktime_on ? "Enabled" : "Disabled"),
+ phba->ktime_data_samples);
+ if (phba->ktime_data_samples == 0)
+ return len;
+
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "Segment 1: Last Cmd cmpl "
+ "done -to- Start of next Cmd (in driver)\n");
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg1_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg1_min,
+ phba->ktime_seg1_max);
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "Segment 2: Driver start of Cmd "
+ "-to- Firmware WQ doorbell\n");
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg2_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg2_min,
+ phba->ktime_seg2_max);
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "Segment 3: Firmware WQ doorbell -to- "
+ "MSI-X ISR cmpl\n");
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg3_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg3_min,
+ phba->ktime_seg3_max);
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "Segment 4: MSI-X ISR cmpl -to- "
+ "Cmd cmpl done\n");
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg4_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg4_min,
+ phba->ktime_seg4_max);
+ len += scnprintf(
+ buf + len, PAGE_SIZE - len,
+ "Total IO avg time: %08lld\n",
+ div_u64(phba->ktime_seg1_total +
+ phba->ktime_seg2_total +
+ phba->ktime_seg3_total +
+ phba->ktime_seg4_total,
+ phba->ktime_data_samples));
+ return len;
+ }
+
+ /* NVME Target */
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "ktime %s: Total Samples: %lld %lld\n",
+ (phba->ktime_on ? "Enabled" : "Disabled"),
+ phba->ktime_data_samples,
+ phba->ktime_status_samples);
+ if (phba->ktime_data_samples == 0)
+ return len;
+
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 1: MSI-X ISR Rcv cmd -to- "
+ "cmd pass to NVME Layer\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg1_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg1_min,
+ phba->ktime_seg1_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 2: cmd pass to NVME Layer- "
+ "-to- Driver rcv cmd OP (action)\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg2_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg2_min,
+ phba->ktime_seg2_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 3: Driver rcv cmd OP -to- "
+ "Firmware WQ doorbell: cmd\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg3_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg3_min,
+ phba->ktime_seg3_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 4: Firmware WQ doorbell: cmd "
+ "-to- MSI-X ISR for cmd cmpl\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg4_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg4_min,
+ phba->ktime_seg4_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 5: MSI-X ISR for cmd cmpl "
+ "-to- NVME layer passed cmd done\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg5_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg5_min,
+ phba->ktime_seg5_max);
+
+ if (phba->ktime_status_samples == 0) {
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Total: cmd received by MSI-X ISR "
+ "-to- cmd completed on wire\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld "
+ "max %08lld\n",
+ div_u64(phba->ktime_seg10_total,
+ phba->ktime_data_samples),
+ phba->ktime_seg10_min,
+ phba->ktime_seg10_max);
+ return len;
+ }
+
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 6: NVME layer passed cmd done "
+ "-to- Driver rcv rsp status OP\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg6_total,
+ phba->ktime_status_samples),
+ phba->ktime_seg6_min,
+ phba->ktime_seg6_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 7: Driver rcv rsp status OP "
+ "-to- Firmware WQ doorbell: status\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg7_total,
+ phba->ktime_status_samples),
+ phba->ktime_seg7_min,
+ phba->ktime_seg7_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 8: Firmware WQ doorbell: status"
+ " -to- MSI-X ISR for status cmpl\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg8_total,
+ phba->ktime_status_samples),
+ phba->ktime_seg8_min,
+ phba->ktime_seg8_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Segment 9: MSI-X ISR for status cmpl "
+ "-to- NVME layer passed status done\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg9_total,
+ phba->ktime_status_samples),
+ phba->ktime_seg9_min,
+ phba->ktime_seg9_max);
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "Total: cmd received by MSI-X ISR -to- "
+ "cmd completed on wire\n");
+ len += scnprintf(buf + len, PAGE_SIZE-len,
+ "avg:%08lld min:%08lld max %08lld\n",
+ div_u64(phba->ktime_seg10_total,
+ phba->ktime_status_samples),
+ phba->ktime_seg10_min,
+ phba->ktime_seg10_max);
return len;
}
+
+/**
+ * lpfc_debugfs_nvmeio_trc_data - Dump NVME IO trace list to a buffer
+ * @phba: The phba to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME IO trace associated with @phba
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_nvmeio_trc_data(struct lpfc_hba *phba, char *buf, int size)
+{
+ struct lpfc_debugfs_nvmeio_trc *dtp;
+ int i, state, index, skip;
+ int len = 0;
+
+ state = phba->nvmeio_trc_on;
+
+ index = (atomic_read(&phba->nvmeio_trc_cnt) + 1) &
+ (phba->nvmeio_trc_size - 1);
+ skip = phba->nvmeio_trc_output_idx;
+
+ len += scnprintf(buf + len, size - len,
+ "%s IO Trace %s: next_idx %d skip %d size %d\n",
+ (phba->nvmet_support ? "NVME" : "NVMET"),
+ (state ? "Enabled" : "Disabled"),
+ index, skip, phba->nvmeio_trc_size);
+
+ if (!phba->nvmeio_trc || state)
+ return len;
+
+ /* trace MUST bhe off to continue */
+
+ for (i = index; i < phba->nvmeio_trc_size; i++) {
+ if (skip) {
+ skip--;
+ continue;
+ }
+ dtp = phba->nvmeio_trc + i;
+ phba->nvmeio_trc_output_idx++;
+
+ if (!dtp->fmt)
+ continue;
+
+ len += scnprintf(buf + len, size - len, dtp->fmt,
+ dtp->data1, dtp->data2, dtp->data3);
+
+ if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) {
+ phba->nvmeio_trc_output_idx = 0;
+ len += scnprintf(buf + len, size - len,
+ "Trace Complete\n");
+ goto out;
+ }
+
+ if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
+ len += scnprintf(buf + len, size - len,
+ "Trace Continue (%d of %d)\n",
+ phba->nvmeio_trc_output_idx,
+ phba->nvmeio_trc_size);
+ goto out;
+ }
+ }
+ for (i = 0; i < index; i++) {
+ if (skip) {
+ skip--;
+ continue;
+ }
+ dtp = phba->nvmeio_trc + i;
+ phba->nvmeio_trc_output_idx++;
+
+ if (!dtp->fmt)
+ continue;
+
+ len += scnprintf(buf + len, size - len, dtp->fmt,
+ dtp->data1, dtp->data2, dtp->data3);
+
+ if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) {
+ phba->nvmeio_trc_output_idx = 0;
+ len += scnprintf(buf + len, size - len,
+ "Trace Complete\n");
+ goto out;
+ }
+
+ if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
+ len += scnprintf(buf + len, size - len,
+ "Trace Continue (%d of %d)\n",
+ phba->nvmeio_trc_output_idx,
+ phba->nvmeio_trc_size);
+ goto out;
+ }
+ }
+
+ len += scnprintf(buf + len, size - len,
+ "Trace Done\n");
+out:
+ return len;
+}
+
+/**
+ * lpfc_debugfs_hdwqstat_data - Dump I/O stats to a buffer
+ * @vport: The vport to gather target node info from.
+ * @buf: The buffer to dump log into.
+ * @size: The maximum amount of data to process.
+ *
+ * Description:
+ * This routine dumps the NVME + SCSI statistics associated with @vport
+ *
+ * Return Value:
+ * This routine returns the amount of bytes that were dumped into @buf and will
+ * not exceed @size.
+ **/
+static int
+lpfc_debugfs_hdwqstat_data(struct lpfc_vport *vport, char *buf, int size)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_hdwq_stat *c_stat;
+ int i, j, len;
+ uint32_t tot_xmt;
+ uint32_t tot_rcv;
+ uint32_t tot_cmpl;
+ char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};
+
+ scnprintf(tmp, sizeof(tmp), "HDWQ Stats:\n\n");
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), "(NVME Accounting: %s) ",
+ (phba->hdwqstat_on &
+ (LPFC_CHECK_NVME_IO | LPFC_CHECK_NVMET_IO) ?
+ "Enabled" : "Disabled"));
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), "(SCSI Accounting: %s) ",
+ (phba->hdwqstat_on & LPFC_CHECK_SCSI_IO ?
+ "Enabled" : "Disabled"));
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ scnprintf(tmp, sizeof(tmp), "\n\n");
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ tot_rcv = 0;
+ tot_xmt = 0;
+ tot_cmpl = 0;
+
+ for_each_present_cpu(j) {
+ c_stat = per_cpu_ptr(phba->sli4_hba.c_stat, j);
+
+ /* Only display for this HDWQ */
+ if (i != c_stat->hdwq_no)
+ continue;
+
+ /* Only display non-zero counters */
+ if (!c_stat->xmt_io && !c_stat->cmpl_io &&
+ !c_stat->rcv_io)
+ continue;
+
+ if (!tot_xmt && !tot_cmpl && !tot_rcv) {
+ /* Print HDWQ string only the first time */
+ scnprintf(tmp, sizeof(tmp), "[HDWQ %d]:\t", i);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+ }
+
+ tot_xmt += c_stat->xmt_io;
+ tot_cmpl += c_stat->cmpl_io;
+ if (phba->nvmet_support)
+ tot_rcv += c_stat->rcv_io;
+
+ scnprintf(tmp, sizeof(tmp), "| [CPU %d]: ", j);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ if (phba->nvmet_support) {
+ scnprintf(tmp, sizeof(tmp),
+ "XMT 0x%x CMPL 0x%x RCV 0x%x |",
+ c_stat->xmt_io, c_stat->cmpl_io,
+ c_stat->rcv_io);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+ } else {
+ scnprintf(tmp, sizeof(tmp),
+ "XMT 0x%x CMPL 0x%x |",
+ c_stat->xmt_io, c_stat->cmpl_io);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+ }
+ }
+
+ /* Check if nothing to display */
+ if (!tot_xmt && !tot_cmpl && !tot_rcv)
+ continue;
+
+ scnprintf(tmp, sizeof(tmp), "\t->\t[HDWQ Total: ");
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+
+ if (phba->nvmet_support) {
+ scnprintf(tmp, sizeof(tmp),
+ "XMT 0x%x CMPL 0x%x RCV 0x%x]\n\n",
+ tot_xmt, tot_cmpl, tot_rcv);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+ } else {
+ scnprintf(tmp, sizeof(tmp),
+ "XMT 0x%x CMPL 0x%x]\n\n",
+ tot_xmt, tot_cmpl);
+ if (strlcat(buf, tmp, size) >= size)
+ goto buffer_done;
+ }
+ }
+
+buffer_done:
+ len = strnlen(buf, size);
+ return len;
+}
+
#endif
/**
@@ -668,6 +1881,40 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
return;
}
+/**
+ * lpfc_debugfs_nvme_trc - Store NVME/NVMET trace log
+ * @phba: The phba to associate this trace string with for retrieval.
+ * @fmt: Format string to be displayed when dumping the log.
+ * @data1: 1st data parameter to be applied to @fmt.
+ * @data2: 2nd data parameter to be applied to @fmt.
+ * @data3: 3rd data parameter to be applied to @fmt.
+ *
+ * Description:
+ * This routine is used by the driver code to add a debugfs log entry to the
+ * nvme trace buffer associated with @phba. @fmt, @data1, @data2, and
+ * @data3 are used like printf when displaying the log.
+ **/
+inline void
+lpfc_debugfs_nvme_trc(struct lpfc_hba *phba, char *fmt,
+ uint16_t data1, uint16_t data2, uint32_t data3)
+{
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ struct lpfc_debugfs_nvmeio_trc *dtp;
+ int index;
+
+ if (!phba->nvmeio_trc_on || !phba->nvmeio_trc)
+ return;
+
+ index = atomic_inc_return(&phba->nvmeio_trc_cnt) &
+ (phba->nvmeio_trc_size - 1);
+ dtp = phba->nvmeio_trc + index;
+ dtp->fmt = fmt;
+ dtp->data1 = data1;
+ dtp->data2 = data2;
+ dtp->data3 = data3;
+#endif
+}
+
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
* lpfc_debugfs_disc_trc_open - Open the discovery trace log
@@ -681,7 +1928,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -693,7 +1940,7 @@ lpfc_debugfs_disc_trc_open(struct inode *inode, struct file *file)
int rc = -ENOMEM;
if (!lpfc_debugfs_max_disc_trc) {
- rc = -ENOSPC;
+ rc = -ENOSPC;
goto out;
}
@@ -731,7 +1978,7 @@ out:
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -743,7 +1990,7 @@ lpfc_debugfs_slow_ring_trc_open(struct inode *inode, struct file *file)
int rc = -ENOMEM;
if (!lpfc_debugfs_max_slow_ring_trc) {
- rc = -ENOSPC;
+ rc = -ENOSPC;
goto out;
}
@@ -781,7 +2028,7 @@ out:
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -812,22 +2059,22 @@ out:
}
/**
- * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer
- * @inode: The inode pointer that contains a vport pointer.
+ * lpfc_debugfs_multixripools_open - Open the multixripool debugfs buffer
+ * @inode: The inode pointer that contains a hba pointer.
* @file: The file pointer to attach the log output.
*
* Description:
* This routine is the entry point for the debugfs open file operation. It gets
- * the vport from the i_private field in @inode, allocates the necessary buffer
- * for the log, fills the buffer from the in-memory log for this vport, and then
+ * the hba from the i_private field in @inode, allocates the necessary buffer
+ * for the log, fills the buffer from the in-memory log for this hba, and then
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
-lpfc_debugfs_dumpHBASlim_open(struct inode *inode, struct file *file)
+lpfc_debugfs_multixripools_open(struct inode *inode, struct file *file)
{
struct lpfc_hba *phba = inode->i_private;
struct lpfc_debug *debug;
@@ -838,14 +2085,16 @@ lpfc_debugfs_dumpHBASlim_open(struct inode *inode, struct file *file)
goto out;
/* Round to page boundary */
- debug->buffer = kmalloc(LPFC_DUMPHBASLIM_SIZE, GFP_KERNEL);
+ debug->buffer = kzalloc(LPFC_DUMP_MULTIXRIPOOL_SIZE, GFP_KERNEL);
if (!debug->buffer) {
kfree(debug);
goto out;
}
- debug->len = lpfc_debugfs_dumpHBASlim_data(phba, debug->buffer,
- LPFC_DUMPHBASLIM_SIZE);
+ debug->len = lpfc_debugfs_multixripools_data(
+ phba, debug->buffer, LPFC_DUMP_MULTIXRIPOOL_SIZE);
+
+ debug->i_private = inode->i_private;
file->private_data = debug;
rc = 0;
@@ -853,8 +2102,9 @@ out:
return rc;
}
+#ifdef LPFC_HDWQ_LOCK_STAT
/**
- * lpfc_debugfs_dumpHostSlim_open - Open the Dump Host SLIM debugfs buffer
+ * lpfc_debugfs_lockstat_open - Open the lockstat debugfs buffer
* @inode: The inode pointer that contains a vport pointer.
* @file: The file pointer to attach the log output.
*
@@ -865,11 +2115,11 @@ out:
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
-lpfc_debugfs_dumpHostSlim_open(struct inode *inode, struct file *file)
+lpfc_debugfs_lockstat_open(struct inode *inode, struct file *file)
{
struct lpfc_hba *phba = inode->i_private;
struct lpfc_debug *debug;
@@ -880,14 +2130,14 @@ lpfc_debugfs_dumpHostSlim_open(struct inode *inode, struct file *file)
goto out;
/* Round to page boundary */
- debug->buffer = kmalloc(LPFC_DUMPHOSTSLIM_SIZE, GFP_KERNEL);
+ debug->buffer = kmalloc(LPFC_HDWQINFO_SIZE, GFP_KERNEL);
if (!debug->buffer) {
kfree(debug);
goto out;
}
- debug->len = lpfc_debugfs_dumpHostSlim_data(phba, debug->buffer,
- LPFC_DUMPHOSTSLIM_SIZE);
+ debug->len = lpfc_debugfs_lockstat_data(phba, debug->buffer,
+ LPFC_HBQINFO_SIZE);
file->private_data = debug;
rc = 0;
@@ -895,29 +2145,183 @@ out:
return rc;
}
+static ssize_t
+lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ struct lpfc_sli4_hdw_queue *qp;
+ char mybuf[64];
+ char *pbuf;
+ int i;
+ size_t bsize;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ bsize = min(nbytes, (sizeof(mybuf) - 1));
+
+ if (copy_from_user(mybuf, buf, bsize))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ if ((strncmp(pbuf, "reset", strlen("reset")) == 0) ||
+ (strncmp(pbuf, "zero", strlen("zero")) == 0)) {
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ qp->lock_conflict.alloc_xri_get = 0;
+ qp->lock_conflict.alloc_xri_put = 0;
+ qp->lock_conflict.free_xri = 0;
+ qp->lock_conflict.wq_access = 0;
+ qp->lock_conflict.alloc_pvt_pool = 0;
+ qp->lock_conflict.mv_from_pvt_pool = 0;
+ qp->lock_conflict.mv_to_pub_pool = 0;
+ qp->lock_conflict.mv_to_pvt_pool = 0;
+ qp->lock_conflict.free_pvt_pool = 0;
+ qp->lock_conflict.free_pub_pool = 0;
+ qp->lock_conflict.wq_access = 0;
+ }
+ }
+ return bsize;
+}
+#endif
+
+static int lpfc_debugfs_ras_log_data(struct lpfc_hba *phba,
+ char *buffer, int size)
+{
+ int copied = 0;
+ struct lpfc_dmabuf *dmabuf, *next;
+
+ memset(buffer, 0, size);
+
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ if (phba->ras_fwlog.state != ACTIVE) {
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+ return -EINVAL;
+ }
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+
+ list_for_each_entry_safe(dmabuf, next,
+ &phba->ras_fwlog.fwlog_buff_list, list) {
+ /* Check if copying will go over size and a '\0' char */
+ if ((copied + LPFC_RAS_MAX_ENTRY_SIZE) >= (size - 1)) {
+ memcpy(buffer + copied, dmabuf->virt,
+ size - copied - 1);
+ copied += size - copied - 1;
+ break;
+ }
+ memcpy(buffer + copied, dmabuf->virt, LPFC_RAS_MAX_ENTRY_SIZE);
+ copied += LPFC_RAS_MAX_ENTRY_SIZE;
+ }
+ return copied;
+}
+
static int
-lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
+lpfc_debugfs_ras_log_release(struct inode *inode, struct file *file)
{
+ struct lpfc_debug *debug = file->private_data;
+
+ vfree(debug->buffer);
+ kfree(debug);
+
+ return 0;
+}
+
+/**
+ * lpfc_debugfs_ras_log_open - Open the RAS log debugfs buffer
+ * @inode: The inode pointer that contains a vport pointer.
+ * @file: The file pointer to attach the log output.
+ *
+ * Description:
+ * This routine is the entry point for the debugfs open file operation. It gets
+ * the vport from the i_private field in @inode, allocates the necessary buffer
+ * for the log, fills the buffer from the in-memory log for this vport, and then
+ * returns a pointer to that log in the private_data field in @file.
+ *
+ * Returns:
+ * This function returns zero if successful. On error it will return a negative
+ * error value.
+ **/
+static int
+lpfc_debugfs_ras_log_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_hba *phba = inode->i_private;
struct lpfc_debug *debug;
+ int size;
int rc = -ENOMEM;
- if (!_dump_buf_data)
- return -EBUSY;
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ if (phba->ras_fwlog.state != ACTIVE) {
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+ rc = -EINVAL;
+ goto out;
+ }
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+
+ if (check_mul_overflow(LPFC_RAS_MIN_BUFF_POST_SIZE,
+ phba->cfg_ras_fwlog_buffsize, &size))
+ goto out;
+
+ debug = kzalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ debug->buffer = vmalloc(size);
+ if (!debug->buffer)
+ goto free_debug;
+
+ debug->len = lpfc_debugfs_ras_log_data(phba, debug->buffer, size);
+ if (debug->len < 0) {
+ rc = -EINVAL;
+ goto free_buffer;
+ }
+ file->private_data = debug;
+
+ return 0;
+
+free_buffer:
+ vfree(debug->buffer);
+free_debug:
+ kfree(debug);
+out:
+ return rc;
+}
+
+/**
+ * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer
+ * @inode: The inode pointer that contains a vport pointer.
+ * @file: The file pointer to attach the log output.
+ *
+ * Description:
+ * This routine is the entry point for the debugfs open file operation. It gets
+ * the vport from the i_private field in @inode, allocates the necessary buffer
+ * for the log, fills the buffer from the in-memory log for this vport, and then
+ * returns a pointer to that log in the private_data field in @file.
+ *
+ * Returns:
+ * This function returns zero if successful. On error it will return a negative
+ * error value.
+ **/
+static int
+lpfc_debugfs_dumpHBASlim_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_hba *phba = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
debug = kmalloc(sizeof(*debug), GFP_KERNEL);
if (!debug)
goto out;
/* Round to page boundary */
- printk(KERN_ERR "9059 BLKGRD: %s: _dump_buf_data=0x%p\n",
- __func__, _dump_buf_data);
- debug->buffer = _dump_buf_data;
+ debug->buffer = kmalloc(LPFC_DUMPHBASLIM_SIZE, GFP_KERNEL);
if (!debug->buffer) {
kfree(debug);
goto out;
}
- debug->len = (1 << _dump_buf_data_order) << PAGE_SHIFT;
+ debug->len = lpfc_debugfs_dumpHBASlim_data(phba, debug->buffer,
+ LPFC_DUMPHBASLIM_SIZE);
file->private_data = debug;
rc = 0;
@@ -925,29 +2329,41 @@ out:
return rc;
}
+/**
+ * lpfc_debugfs_dumpHostSlim_open - Open the Dump Host SLIM debugfs buffer
+ * @inode: The inode pointer that contains a vport pointer.
+ * @file: The file pointer to attach the log output.
+ *
+ * Description:
+ * This routine is the entry point for the debugfs open file operation. It gets
+ * the vport from the i_private field in @inode, allocates the necessary buffer
+ * for the log, fills the buffer from the in-memory log for this vport, and then
+ * returns a pointer to that log in the private_data field in @file.
+ *
+ * Returns:
+ * This function returns zero if successful. On error it will return a negative
+ * error value.
+ **/
static int
-lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
+lpfc_debugfs_dumpHostSlim_open(struct inode *inode, struct file *file)
{
+ struct lpfc_hba *phba = inode->i_private;
struct lpfc_debug *debug;
int rc = -ENOMEM;
- if (!_dump_buf_dif)
- return -EBUSY;
-
debug = kmalloc(sizeof(*debug), GFP_KERNEL);
if (!debug)
goto out;
/* Round to page boundary */
- printk(KERN_ERR "9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%s\n",
- __func__, _dump_buf_dif, file->f_dentry->d_name.name);
- debug->buffer = _dump_buf_dif;
+ debug->buffer = kmalloc(LPFC_DUMPHOSTSLIM_SIZE, GFP_KERNEL);
if (!debug->buffer) {
kfree(debug);
goto out;
}
- debug->len = (1 << _dump_buf_dif_order) << PAGE_SHIFT;
+ debug->len = lpfc_debugfs_dumpHostSlim_data(phba, debug->buffer,
+ LPFC_DUMPHOSTSLIM_SIZE);
file->private_data = debug;
rc = 0;
@@ -956,28 +2372,127 @@ out:
}
static ssize_t
-lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf,
- size_t nbytes, loff_t *ppos)
+lpfc_debugfs_dif_err_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
{
- /*
- * The Data/DIF buffers only save one failing IO
- * The write op is used as a reset mechanism after an IO has
- * already been saved to the next one can be saved
- */
- spin_lock(&_dump_buf_lock);
+ struct lpfc_hba *phba = file->private_data;
+ int kind = debugfs_get_aux_num(file);
+ char cbuf[32] = {0};
+ int cnt = 0;
+
+ switch (kind) {
+ case writeGuard:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "%u\n",
+ phba->lpfc_injerr_wgrd_cnt);
+ break;
+ case writeApp:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "%u\n",
+ phba->lpfc_injerr_wapp_cnt);
+ break;
+ case writeRef:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "%u\n",
+ phba->lpfc_injerr_wref_cnt);
+ break;
+ case readGuard:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "%u\n",
+ phba->lpfc_injerr_rgrd_cnt);
+ break;
+ case readApp:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "%u\n",
+ phba->lpfc_injerr_rapp_cnt);
+ break;
+ case readRef:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "%u\n",
+ phba->lpfc_injerr_rref_cnt);
+ break;
+ case InjErrNPortID:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "0x%06x\n",
+ phba->lpfc_injerr_nportid);
+ break;
+ case InjErrWWPN:
+ cnt = scnprintf(cbuf, sizeof(cbuf), "0x%016llx\n",
+ be64_to_cpu(phba->lpfc_injerr_wwpn.u.wwn_be));
+ break;
+ case InjErrLBA:
+ if (phba->lpfc_injerr_lba == LPFC_INJERR_LBA_OFF)
+ cnt = scnprintf(cbuf, sizeof(cbuf), "off\n");
+ else
+ cnt = scnprintf(cbuf, sizeof(cbuf), "0x%llx\n",
+ (uint64_t)phba->lpfc_injerr_lba);
+ break;
+ default:
+ lpfc_log_msg(phba, KERN_WARNING, LOG_INIT,
+ "0547 Unknown debugfs error injection entry\n");
+ break;
+ }
- memset((void *)_dump_buf_data, 0,
- ((1 << PAGE_SHIFT) << _dump_buf_data_order));
- memset((void *)_dump_buf_dif, 0,
- ((1 << PAGE_SHIFT) << _dump_buf_dif_order));
+ return simple_read_from_buffer(buf, nbytes, ppos, &cbuf, cnt);
+}
- _dump_buf_done = 0;
+static ssize_t
+lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_hba *phba = file->private_data;
+ int kind = debugfs_get_aux_num(file);
+ char dstbuf[33] = {0};
+ unsigned long long tmp;
+ unsigned long size;
+
+ size = (nbytes < (sizeof(dstbuf) - 1)) ? nbytes : (sizeof(dstbuf) - 1);
+ if (copy_from_user(dstbuf, buf, size))
+ return -EFAULT;
- spin_unlock(&_dump_buf_lock);
+ if (kstrtoull(dstbuf, 0, &tmp)) {
+ if (kind != InjErrLBA || !strstr(dstbuf, "off"))
+ return -EINVAL;
+ }
+ switch (kind) {
+ case writeGuard:
+ phba->lpfc_injerr_wgrd_cnt = (uint32_t)tmp;
+ break;
+ case writeApp:
+ phba->lpfc_injerr_wapp_cnt = (uint32_t)tmp;
+ break;
+ case writeRef:
+ phba->lpfc_injerr_wref_cnt = (uint32_t)tmp;
+ break;
+ case readGuard:
+ phba->lpfc_injerr_rgrd_cnt = (uint32_t)tmp;
+ break;
+ case readApp:
+ phba->lpfc_injerr_rapp_cnt = (uint32_t)tmp;
+ break;
+ case readRef:
+ phba->lpfc_injerr_rref_cnt = (uint32_t)tmp;
+ break;
+ case InjErrLBA:
+ if (strstr(dstbuf, "off"))
+ phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ else
+ phba->lpfc_injerr_lba = (sector_t)tmp;
+ break;
+ case InjErrNPortID:
+ phba->lpfc_injerr_nportid = (uint32_t)(tmp & Mask_DID);
+ break;
+ case InjErrWWPN:
+ phba->lpfc_injerr_wwpn.u.wwn_be = cpu_to_be64(tmp);
+ break;
+ default:
+ lpfc_log_msg(phba, KERN_WARNING, LOG_INIT,
+ "0548 Unknown debugfs error injection entry\n");
+ break;
+ }
return nbytes;
}
+static int
+lpfc_debugfs_dif_err_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
/**
* lpfc_debugfs_nodelist_open - Open the nodelist debugfs file
* @inode: The inode pointer that contains a vport pointer.
@@ -990,7 +2505,7 @@ lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf,
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -1040,22 +2555,8 @@ out:
static loff_t
lpfc_debugfs_lseek(struct file *file, loff_t off, int whence)
{
- struct lpfc_debug *debug;
- loff_t pos = -1;
-
- debug = file->private_data;
-
- switch (whence) {
- case 0:
- pos = off;
- break;
- case 1:
- pos = file->f_pos + off;
- break;
- case 2:
- pos = debug->len - off;
- }
- return (pos < 0 || pos > debug->len) ? -EINVAL : (file->f_pos = pos);
+ struct lpfc_debug *debug = file->private_data;
+ return fixed_size_llseek(file, off, whence, debug->len);
}
/**
@@ -1107,15 +2608,535 @@ lpfc_debugfs_release(struct inode *inode, struct file *file)
return 0;
}
+/**
+ * lpfc_debugfs_multixripools_write - Clear multi-XRI pools statistics
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine clears multi-XRI pools statistics when buf contains "clear".
+ *
+ * Return Value:
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_debugfs_multixripools_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ char mybuf[64];
+ char *pbuf;
+ u32 i;
+ u32 hwq_count;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+
+ if (nbytes > sizeof(mybuf) - 1)
+ nbytes = sizeof(mybuf) - 1;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ if (copy_from_user(mybuf, buf, nbytes))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ if ((strncmp(pbuf, "clear", strlen("clear"))) == 0) {
+ hwq_count = phba->cfg_hdw_queue;
+ for (i = 0; i < hwq_count; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ continue;
+
+ qp->empty_io_bufs = 0;
+ multixri_pool->pbl_empty_count = 0;
+#ifdef LPFC_MXP_STAT
+ multixri_pool->above_limit_count = 0;
+ multixri_pool->below_limit_count = 0;
+ multixri_pool->stat_max_hwm = 0;
+ multixri_pool->local_pbl_hit_count = 0;
+ multixri_pool->other_pbl_hit_count = 0;
+
+ multixri_pool->stat_pbl_count = 0;
+ multixri_pool->stat_pvt_count = 0;
+ multixri_pool->stat_busy_count = 0;
+ multixri_pool->stat_snapshot_taken = 0;
+#endif
+ }
+ return strlen(pbuf);
+ }
+
+ return -EINVAL;
+}
+
static int
-lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
+lpfc_debugfs_nvmestat_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_vport *vport = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kmalloc(LPFC_NVMESTAT_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_nvmestat_data(vport, debug->buffer,
+ LPFC_NVMESTAT_SIZE);
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_nvmestat_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
{
struct lpfc_debug *debug = file->private_data;
+ struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nvmet_tgtport *tgtp;
+ char mybuf[64];
+ char *pbuf;
- debug->buffer = NULL;
- kfree(debug);
+ if (!phba->targetport)
+ return -ENXIO;
- return 0;
+ if (nbytes > sizeof(mybuf) - 1)
+ nbytes = sizeof(mybuf) - 1;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ if (copy_from_user(mybuf, buf, nbytes))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ if ((strncmp(pbuf, "reset", strlen("reset")) == 0) ||
+ (strncmp(pbuf, "zero", strlen("zero")) == 0)) {
+ atomic_set(&tgtp->rcv_ls_req_in, 0);
+ atomic_set(&tgtp->rcv_ls_req_out, 0);
+ atomic_set(&tgtp->rcv_ls_req_drop, 0);
+ atomic_set(&tgtp->xmt_ls_abort, 0);
+ atomic_set(&tgtp->xmt_ls_abort_cmpl, 0);
+ atomic_set(&tgtp->xmt_ls_rsp, 0);
+ atomic_set(&tgtp->xmt_ls_drop, 0);
+ atomic_set(&tgtp->xmt_ls_rsp_error, 0);
+ atomic_set(&tgtp->xmt_ls_rsp_cmpl, 0);
+
+ atomic_set(&tgtp->rcv_fcp_cmd_in, 0);
+ atomic_set(&tgtp->rcv_fcp_cmd_out, 0);
+ atomic_set(&tgtp->rcv_fcp_cmd_drop, 0);
+ atomic_set(&tgtp->xmt_fcp_drop, 0);
+ atomic_set(&tgtp->xmt_fcp_read_rsp, 0);
+ atomic_set(&tgtp->xmt_fcp_read, 0);
+ atomic_set(&tgtp->xmt_fcp_write, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp, 0);
+ atomic_set(&tgtp->xmt_fcp_release, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_cmpl, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_error, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_drop, 0);
+
+ atomic_set(&tgtp->xmt_fcp_abort, 0);
+ atomic_set(&tgtp->xmt_fcp_abort_cmpl, 0);
+ atomic_set(&tgtp->xmt_abort_sol, 0);
+ atomic_set(&tgtp->xmt_abort_unsol, 0);
+ atomic_set(&tgtp->xmt_abort_rsp, 0);
+ atomic_set(&tgtp->xmt_abort_rsp_error, 0);
+ }
+ return nbytes;
+}
+
+static int
+lpfc_debugfs_scsistat_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_vport *vport = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kzalloc(LPFC_SCSISTAT_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_scsistat_data(vport, debug->buffer,
+ LPFC_SCSISTAT_SIZE);
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_scsistat_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
+ struct lpfc_hba *phba = vport->phba;
+ char mybuf[6] = {0};
+ int i;
+
+ if (copy_from_user(mybuf, buf, (nbytes >= sizeof(mybuf)) ?
+ (sizeof(mybuf) - 1) : nbytes))
+ return -EFAULT;
+
+ if ((strncmp(&mybuf[0], "reset", strlen("reset")) == 0) ||
+ (strncmp(&mybuf[0], "zero", strlen("zero")) == 0)) {
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ memset(&phba->sli4_hba.hdwq[i].scsi_cstat, 0,
+ sizeof(phba->sli4_hba.hdwq[i].scsi_cstat));
+ }
+ }
+
+ return nbytes;
+}
+
+static int
+lpfc_debugfs_ioktime_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_vport *vport = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kmalloc(LPFC_IOKTIME_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_ioktime_data(vport, debug->buffer,
+ LPFC_IOKTIME_SIZE);
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_ioktime_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
+ struct lpfc_hba *phba = vport->phba;
+ char mybuf[64];
+ char *pbuf;
+
+ if (nbytes > sizeof(mybuf) - 1)
+ nbytes = sizeof(mybuf) - 1;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ if (copy_from_user(mybuf, buf, nbytes))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
+ phba->ktime_data_samples = 0;
+ phba->ktime_status_samples = 0;
+ phba->ktime_seg1_total = 0;
+ phba->ktime_seg1_max = 0;
+ phba->ktime_seg1_min = 0xffffffff;
+ phba->ktime_seg2_total = 0;
+ phba->ktime_seg2_max = 0;
+ phba->ktime_seg2_min = 0xffffffff;
+ phba->ktime_seg3_total = 0;
+ phba->ktime_seg3_max = 0;
+ phba->ktime_seg3_min = 0xffffffff;
+ phba->ktime_seg4_total = 0;
+ phba->ktime_seg4_max = 0;
+ phba->ktime_seg4_min = 0xffffffff;
+ phba->ktime_seg5_total = 0;
+ phba->ktime_seg5_max = 0;
+ phba->ktime_seg5_min = 0xffffffff;
+ phba->ktime_seg6_total = 0;
+ phba->ktime_seg6_max = 0;
+ phba->ktime_seg6_min = 0xffffffff;
+ phba->ktime_seg7_total = 0;
+ phba->ktime_seg7_max = 0;
+ phba->ktime_seg7_min = 0xffffffff;
+ phba->ktime_seg8_total = 0;
+ phba->ktime_seg8_max = 0;
+ phba->ktime_seg8_min = 0xffffffff;
+ phba->ktime_seg9_total = 0;
+ phba->ktime_seg9_max = 0;
+ phba->ktime_seg9_min = 0xffffffff;
+ phba->ktime_seg10_total = 0;
+ phba->ktime_seg10_max = 0;
+ phba->ktime_seg10_min = 0xffffffff;
+
+ phba->ktime_on = 1;
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "off",
+ sizeof("off") - 1) == 0)) {
+ phba->ktime_on = 0;
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "zero",
+ sizeof("zero") - 1) == 0)) {
+ phba->ktime_data_samples = 0;
+ phba->ktime_status_samples = 0;
+ phba->ktime_seg1_total = 0;
+ phba->ktime_seg1_max = 0;
+ phba->ktime_seg1_min = 0xffffffff;
+ phba->ktime_seg2_total = 0;
+ phba->ktime_seg2_max = 0;
+ phba->ktime_seg2_min = 0xffffffff;
+ phba->ktime_seg3_total = 0;
+ phba->ktime_seg3_max = 0;
+ phba->ktime_seg3_min = 0xffffffff;
+ phba->ktime_seg4_total = 0;
+ phba->ktime_seg4_max = 0;
+ phba->ktime_seg4_min = 0xffffffff;
+ phba->ktime_seg5_total = 0;
+ phba->ktime_seg5_max = 0;
+ phba->ktime_seg5_min = 0xffffffff;
+ phba->ktime_seg6_total = 0;
+ phba->ktime_seg6_max = 0;
+ phba->ktime_seg6_min = 0xffffffff;
+ phba->ktime_seg7_total = 0;
+ phba->ktime_seg7_max = 0;
+ phba->ktime_seg7_min = 0xffffffff;
+ phba->ktime_seg8_total = 0;
+ phba->ktime_seg8_max = 0;
+ phba->ktime_seg8_min = 0xffffffff;
+ phba->ktime_seg9_total = 0;
+ phba->ktime_seg9_max = 0;
+ phba->ktime_seg9_min = 0xffffffff;
+ phba->ktime_seg10_total = 0;
+ phba->ktime_seg10_max = 0;
+ phba->ktime_seg10_min = 0xffffffff;
+ return strlen(pbuf);
+ }
+ return -EINVAL;
+}
+
+static int
+lpfc_debugfs_nvmeio_trc_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_hba *phba = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kmalloc(LPFC_NVMEIO_TRC_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_nvmeio_trc_data(phba, debug->buffer,
+ LPFC_NVMEIO_TRC_SIZE);
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_nvmeio_trc_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ int i;
+ unsigned long sz;
+ char mybuf[64];
+ char *pbuf;
+
+ if (nbytes > sizeof(mybuf) - 1)
+ nbytes = sizeof(mybuf) - 1;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ if (copy_from_user(mybuf, buf, nbytes))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ if ((strncmp(pbuf, "off", sizeof("off") - 1) == 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0570 nvmeio_trc_off\n");
+ phba->nvmeio_trc_output_idx = 0;
+ phba->nvmeio_trc_on = 0;
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0571 nvmeio_trc_on\n");
+ phba->nvmeio_trc_output_idx = 0;
+ phba->nvmeio_trc_on = 1;
+ return strlen(pbuf);
+ }
+
+ /* We must be off to allocate the trace buffer */
+ if (phba->nvmeio_trc_on != 0)
+ return -EINVAL;
+
+ /* If not on or off, the parameter is the trace buffer size */
+ i = kstrtoul(pbuf, 0, &sz);
+ if (i)
+ return -EINVAL;
+ phba->nvmeio_trc_size = (uint32_t)sz;
+
+ /* It must be a power of 2 - round down */
+ i = 0;
+ while (sz > 1) {
+ sz = sz >> 1;
+ i++;
+ }
+ sz = (1 << i);
+ if (phba->nvmeio_trc_size != sz)
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0572 nvmeio_trc_size changed to %ld\n",
+ sz);
+ phba->nvmeio_trc_size = (uint32_t)sz;
+
+ /* If one previously exists, free it */
+ kfree(phba->nvmeio_trc);
+
+ /* Allocate new trace buffer and initialize */
+ phba->nvmeio_trc = kzalloc((sizeof(struct lpfc_debugfs_nvmeio_trc) *
+ sz), GFP_KERNEL);
+ if (!phba->nvmeio_trc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0573 Cannot create debugfs "
+ "nvmeio_trc buffer\n");
+ return -ENOMEM;
+ }
+ atomic_set(&phba->nvmeio_trc_cnt, 0);
+ phba->nvmeio_trc_on = 0;
+ phba->nvmeio_trc_output_idx = 0;
+
+ return strlen(pbuf);
+}
+
+static int
+lpfc_debugfs_hdwqstat_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_vport *vport = inode->i_private;
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundary */
+ debug->buffer = kcalloc(1, LPFC_SCSISTAT_SIZE, GFP_KERNEL);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = lpfc_debugfs_hdwqstat_data(vport, debug->buffer,
+ LPFC_SCSISTAT_SIZE);
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_hdwqstat_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_hdwq_stat *c_stat;
+ char mybuf[64];
+ char *pbuf;
+ int i;
+
+ if (nbytes > sizeof(mybuf) - 1)
+ nbytes = sizeof(mybuf) - 1;
+
+ memset(mybuf, 0, sizeof(mybuf));
+
+ if (copy_from_user(mybuf, buf, nbytes))
+ return -EFAULT;
+ pbuf = &mybuf[0];
+
+ if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
+ if (phba->nvmet_support)
+ phba->hdwqstat_on |= LPFC_CHECK_NVMET_IO;
+ else
+ phba->hdwqstat_on |= (LPFC_CHECK_NVME_IO |
+ LPFC_CHECK_SCSI_IO);
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "nvme_on", sizeof("nvme_on") - 1) == 0)) {
+ if (phba->nvmet_support)
+ phba->hdwqstat_on |= LPFC_CHECK_NVMET_IO;
+ else
+ phba->hdwqstat_on |= LPFC_CHECK_NVME_IO;
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "scsi_on", sizeof("scsi_on") - 1) == 0)) {
+ if (!phba->nvmet_support)
+ phba->hdwqstat_on |= LPFC_CHECK_SCSI_IO;
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "nvme_off", sizeof("nvme_off") - 1) == 0)) {
+ phba->hdwqstat_on &= ~(LPFC_CHECK_NVME_IO |
+ LPFC_CHECK_NVMET_IO);
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "scsi_off", sizeof("scsi_off") - 1) == 0)) {
+ phba->hdwqstat_on &= ~LPFC_CHECK_SCSI_IO;
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "off",
+ sizeof("off") - 1) == 0)) {
+ phba->hdwqstat_on = LPFC_CHECK_OFF;
+ return strlen(pbuf);
+ } else if ((strncmp(pbuf, "zero",
+ sizeof("zero") - 1) == 0)) {
+ for_each_present_cpu(i) {
+ c_stat = per_cpu_ptr(phba->sli4_hba.c_stat, i);
+ c_stat->xmt_io = 0;
+ c_stat->cmpl_io = 0;
+ c_stat->rcv_io = 0;
+ }
+ return strlen(pbuf);
+ }
+ return -EINVAL;
}
/*
@@ -1147,11 +3168,8 @@ static int lpfc_idiag_cmd_get(const char __user *buf, size_t nbytes,
{
char mybuf[64];
char *pbuf, *step_str;
- int bsize, i;
-
- /* Protect copy from user */
- if (!access_ok(VERIFY_READ, buf, nbytes))
- return -EFAULT;
+ int i;
+ size_t bsize;
memset(mybuf, 0, sizeof(mybuf));
memset(idiag_cmd, 0, sizeof(*idiag_cmd));
@@ -1326,8 +3344,8 @@ lpfc_idiag_pcicfg_read(struct file *file, char __user *buf, size_t nbytes,
return 0;
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD) {
- where = idiag.cmd.data[0];
- count = idiag.cmd.data[1];
+ where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
+ count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
} else
return 0;
@@ -1335,22 +3353,21 @@ lpfc_idiag_pcicfg_read(struct file *file, char __user *buf, size_t nbytes,
switch (count) {
case SIZE_U8: /* byte (8 bits) */
pci_read_config_byte(pdev, where, &u8val);
- len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"%03x: %02x\n", where, u8val);
break;
case SIZE_U16: /* word (16 bits) */
pci_read_config_word(pdev, where, &u16val);
- len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"%03x: %04x\n", where, u16val);
break;
case SIZE_U32: /* double word (32 bits) */
pci_read_config_dword(pdev, where, &u32val);
- len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"%03x: %08x\n", where, u32val);
break;
case LPFC_PCI_CFG_BROWSE: /* browse all */
goto pcicfg_browse;
- break;
default:
/* illegal count */
len = 0;
@@ -1365,27 +3382,35 @@ pcicfg_browse:
offset = offset_label;
/* Read PCI config space */
- len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"%03x: ", offset_label);
while (index > 0) {
pci_read_config_dword(pdev, offset, &u32val);
- len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"%08x ", u32val);
offset += sizeof(uint32_t);
+ if (offset >= LPFC_PCI_CFG_SIZE) {
+ len += scnprintf(pbuffer+len,
+ LPFC_PCI_CFG_SIZE-len, "\n");
+ break;
+ }
index -= sizeof(uint32_t);
if (!index)
- len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"\n");
else if (!(index % (8 * sizeof(uint32_t)))) {
offset_label += (8 * sizeof(uint32_t));
- len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"\n%03x: ", offset_label);
}
}
/* Set up the offset for next portion of pci cfg read */
- idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE;
- if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE)
+ if (index == 0) {
+ idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE;
+ if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE)
+ idiag.offset.last_rd = 0;
+ } else
idiag.offset.last_rd = 0;
return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
@@ -1438,8 +3463,8 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf,
if (rc != LPFC_PCI_CFG_RD_CMD_ARG)
goto error_out;
/* Read command from PCI config space, set up command fields */
- where = idiag.cmd.data[0];
- count = idiag.cmd.data[1];
+ where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
+ count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
if (count == LPFC_PCI_CFG_BROWSE) {
if (where % sizeof(uint32_t))
goto error_out;
@@ -1474,9 +3499,9 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf,
if (rc != LPFC_PCI_CFG_WR_CMD_ARG)
goto error_out;
/* Write command to PCI config space, read-modify-write */
- where = idiag.cmd.data[0];
- count = idiag.cmd.data[1];
- value = idiag.cmd.data[2];
+ where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
+ count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
+ value = idiag.cmd.data[IDIAG_PCICFG_VALUE_INDX];
/* Sanity checks */
if ((count != sizeof(uint8_t)) &&
(count != sizeof(uint16_t)) &&
@@ -1569,6 +3594,459 @@ error_out:
}
/**
+ * lpfc_idiag_baracc_read - idiag debugfs pci bar access read
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba pci bar memory mapped space
+ * according to the idiag command, and copies to user @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_baracc_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ int offset_label, offset, offset_run, len = 0, index;
+ int bar_num, acc_range, bar_size;
+ char *pbuffer;
+ void __iomem *mem_mapped_bar;
+ uint32_t if_type;
+ struct pci_dev *pdev;
+ uint32_t u32val;
+
+ pdev = phba->pcidev;
+ if (!pdev)
+ return 0;
+
+ /* This is a user read operation */
+ debug->op = LPFC_IDIAG_OP_RD;
+
+ if (!debug->buffer)
+ debug->buffer = kmalloc(LPFC_PCI_BAR_RD_BUF_SIZE, GFP_KERNEL);
+ if (!debug->buffer)
+ return 0;
+ pbuffer = debug->buffer;
+
+ if (*ppos)
+ return 0;
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
+ bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];
+ offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
+ acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
+ bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
+ } else
+ return 0;
+
+ if (acc_range == 0)
+ return 0;
+
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+ if (bar_num == IDIAG_BARACC_BAR_0)
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+ else if (bar_num == IDIAG_BARACC_BAR_1)
+ mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
+ else if (bar_num == IDIAG_BARACC_BAR_2)
+ mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
+ else
+ return 0;
+ } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (bar_num == IDIAG_BARACC_BAR_0)
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+ else
+ return 0;
+ } else
+ return 0;
+
+ /* Read single PCI bar space register */
+ if (acc_range == SINGLE_WORD) {
+ offset_run = offset;
+ u32val = readl(mem_mapped_bar + offset_run);
+ len += scnprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
+ "%05x: %08x\n", offset_run, u32val);
+ } else
+ goto baracc_browse;
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+
+baracc_browse:
+
+ /* Browse all PCI bar space registers */
+ offset_label = idiag.offset.last_rd;
+ offset_run = offset_label;
+
+ /* Read PCI bar memory mapped space */
+ len += scnprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
+ "%05x: ", offset_label);
+ index = LPFC_PCI_BAR_RD_SIZE;
+ while (index > 0) {
+ u32val = readl(mem_mapped_bar + offset_run);
+ len += scnprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
+ "%08x ", u32val);
+ offset_run += sizeof(uint32_t);
+ if (acc_range == LPFC_PCI_BAR_BROWSE) {
+ if (offset_run >= bar_size) {
+ len += scnprintf(pbuffer+len,
+ LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
+ break;
+ }
+ } else {
+ if (offset_run >= offset +
+ (acc_range * sizeof(uint32_t))) {
+ len += scnprintf(pbuffer+len,
+ LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
+ break;
+ }
+ }
+ index -= sizeof(uint32_t);
+ if (!index)
+ len += scnprintf(pbuffer+len,
+ LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
+ else if (!(index % (8 * sizeof(uint32_t)))) {
+ offset_label += (8 * sizeof(uint32_t));
+ len += scnprintf(pbuffer+len,
+ LPFC_PCI_BAR_RD_BUF_SIZE-len,
+ "\n%05x: ", offset_label);
+ }
+ }
+
+ /* Set up the offset for next portion of pci bar read */
+ if (index == 0) {
+ idiag.offset.last_rd += LPFC_PCI_BAR_RD_SIZE;
+ if (acc_range == LPFC_PCI_BAR_BROWSE) {
+ if (idiag.offset.last_rd >= bar_size)
+ idiag.offset.last_rd = 0;
+ } else {
+ if (offset_run >= offset +
+ (acc_range * sizeof(uint32_t)))
+ idiag.offset.last_rd = offset;
+ }
+ } else {
+ if (acc_range == LPFC_PCI_BAR_BROWSE)
+ idiag.offset.last_rd = 0;
+ else
+ idiag.offset.last_rd = offset;
+ }
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_baracc_write - Syntax check and set up idiag bar access commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and
+ * then perform the syntax check for PCI bar memory mapped space read or
+ * write command accordingly. In the case of PCI bar memory mapped space
+ * read command, it sets up the command in the idiag command struct for
+ * the debugfs read operation. In the case of PCI bar memorpy mapped space
+ * write operation, it executes the write operation into the PCI bar memory
+ * mapped space accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ */
+static ssize_t
+lpfc_idiag_baracc_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ uint32_t bar_num, bar_size, offset, value, acc_range;
+ struct pci_dev *pdev;
+ void __iomem *mem_mapped_bar;
+ uint32_t if_type;
+ uint32_t u32val;
+ int rc;
+
+ pdev = phba->pcidev;
+ if (!pdev)
+ return -EFAULT;
+
+ /* This is a user write operation */
+ debug->op = LPFC_IDIAG_OP_WR;
+
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+ if (rc < 0)
+ return rc;
+
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
+ bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];
+
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+ if ((bar_num != IDIAG_BARACC_BAR_0) &&
+ (bar_num != IDIAG_BARACC_BAR_1) &&
+ (bar_num != IDIAG_BARACC_BAR_2))
+ goto error_out;
+ } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (bar_num != IDIAG_BARACC_BAR_0)
+ goto error_out;
+ } else
+ goto error_out;
+
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+ if (bar_num == IDIAG_BARACC_BAR_0) {
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+ LPFC_PCI_IF0_BAR0_SIZE;
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+ } else if (bar_num == IDIAG_BARACC_BAR_1) {
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+ LPFC_PCI_IF0_BAR1_SIZE;
+ mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
+ } else if (bar_num == IDIAG_BARACC_BAR_2) {
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+ LPFC_PCI_IF0_BAR2_SIZE;
+ mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
+ } else
+ goto error_out;
+ } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (bar_num == IDIAG_BARACC_BAR_0) {
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
+ LPFC_PCI_IF2_BAR0_SIZE;
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
+ } else
+ goto error_out;
+ } else
+ goto error_out;
+
+ offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
+ if (offset % sizeof(uint32_t))
+ goto error_out;
+
+ bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
+ /* Sanity check on PCI config read command line arguments */
+ if (rc != LPFC_PCI_BAR_RD_CMD_ARG)
+ goto error_out;
+ acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
+ if (acc_range == LPFC_PCI_BAR_BROWSE) {
+ if (offset > bar_size - sizeof(uint32_t))
+ goto error_out;
+ /* Starting offset to browse */
+ idiag.offset.last_rd = offset;
+ } else if (acc_range > SINGLE_WORD) {
+ if (offset + acc_range * sizeof(uint32_t) > bar_size)
+ goto error_out;
+ /* Starting offset to browse */
+ idiag.offset.last_rd = offset;
+ } else if (acc_range != SINGLE_WORD)
+ goto error_out;
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
+ /* Sanity check on PCI bar write command line arguments */
+ if (rc != LPFC_PCI_BAR_WR_CMD_ARG)
+ goto error_out;
+ /* Write command to PCI bar space, read-modify-write */
+ acc_range = SINGLE_WORD;
+ value = idiag.cmd.data[IDIAG_BARACC_REG_VAL_INDX];
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR) {
+ writel(value, mem_mapped_bar + offset);
+ readl(mem_mapped_bar + offset);
+ }
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST) {
+ u32val = readl(mem_mapped_bar + offset);
+ u32val |= value;
+ writel(u32val, mem_mapped_bar + offset);
+ readl(mem_mapped_bar + offset);
+ }
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
+ u32val = readl(mem_mapped_bar + offset);
+ u32val &= ~value;
+ writel(u32val, mem_mapped_bar + offset);
+ readl(mem_mapped_bar + offset);
+ }
+ } else
+ /* All other opecodes are illegal for now */
+ goto error_out;
+
+ return nbytes;
+error_out:
+ memset(&idiag, 0, sizeof(idiag));
+ return -EINVAL;
+}
+
+static int
+__lpfc_idiag_print_wq(struct lpfc_queue *qp, char *wqtype,
+ char *pbuffer, int len)
+{
+ if (!qp)
+ return len;
+
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\t\t%s WQ info: ", wqtype);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "AssocCQID[%04d]: WQ-STAT[oflow:x%x posted:x%llx]\n",
+ qp->assoc_qid, qp->q_cnt_1,
+ (unsigned long long)qp->q_cnt_4);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\t\tWQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
+ "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]",
+ qp->queue_id, qp->entry_count,
+ qp->entry_size, qp->host_index,
+ qp->hba_index, qp->notify_interval);
+ len += scnprintf(pbuffer + len,
+ LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n");
+ return len;
+}
+
+static int
+lpfc_idiag_wqs_for_cq(struct lpfc_hba *phba, char *wqtype, char *pbuffer,
+ int *len, int max_cnt, int cq_id)
+{
+ struct lpfc_queue *qp;
+ int qidx;
+
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].io_wq;
+ if (qp->assoc_qid != cq_id)
+ continue;
+ *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len);
+ if (*len >= max_cnt)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+__lpfc_idiag_print_cq(struct lpfc_queue *qp, char *cqtype,
+ char *pbuffer, int len)
+{
+ if (!qp)
+ return len;
+
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\t%s CQ info: ", cqtype);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "AssocEQID[%02d]: CQ STAT[max:x%x relw:x%x "
+ "xabt:x%x wq:x%llx]\n",
+ qp->assoc_qid, qp->q_cnt_1, qp->q_cnt_2,
+ qp->q_cnt_3, (unsigned long long)qp->q_cnt_4);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\tCQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
+ "HST-IDX[%04d], NTFI[%03d], PLMT[%03d]",
+ qp->queue_id, qp->entry_count,
+ qp->entry_size, qp->host_index,
+ qp->notify_interval, qp->max_proc_limit);
+
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\n");
+
+ return len;
+}
+
+static int
+__lpfc_idiag_print_rqpair(struct lpfc_queue *qp, struct lpfc_queue *datqp,
+ char *rqtype, char *pbuffer, int len)
+{
+ if (!qp || !datqp)
+ return len;
+
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\t\t%s RQ info: ", rqtype);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "AssocCQID[%02d]: RQ-STAT[nopost:x%x nobuf:x%x "
+ "posted:x%x rcv:x%llx]\n",
+ qp->assoc_qid, qp->q_cnt_1, qp->q_cnt_2,
+ qp->q_cnt_3, (unsigned long long)qp->q_cnt_4);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\t\tHQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
+ "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]\n",
+ qp->queue_id, qp->entry_count, qp->entry_size,
+ qp->host_index, qp->hba_index, qp->notify_interval);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\t\tDQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
+ "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]\n",
+ datqp->queue_id, datqp->entry_count,
+ datqp->entry_size, datqp->host_index,
+ datqp->hba_index, datqp->notify_interval);
+ return len;
+}
+
+static int
+lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer,
+ int *len, int max_cnt, int eqidx, int eq_id)
+{
+ struct lpfc_queue *qp;
+ int rc;
+
+ qp = phba->sli4_hba.hdwq[eqidx].io_cq;
+
+ *len = __lpfc_idiag_print_cq(qp, "IO", pbuffer, *len);
+
+ /* Reset max counter */
+ qp->CQ_max_cqe = 0;
+
+ if (*len >= max_cnt)
+ return 1;
+
+ rc = lpfc_idiag_wqs_for_cq(phba, "IO", pbuffer, len,
+ max_cnt, qp->queue_id);
+ if (rc)
+ return 1;
+
+ if ((eqidx < phba->cfg_nvmet_mrq) && phba->nvmet_support) {
+ /* NVMET CQset */
+ qp = phba->sli4_hba.nvmet_cqset[eqidx];
+ *len = __lpfc_idiag_print_cq(qp, "NVMET CQset", pbuffer, *len);
+
+ /* Reset max counter */
+ qp->CQ_max_cqe = 0;
+
+ if (*len >= max_cnt)
+ return 1;
+
+ /* RQ header */
+ qp = phba->sli4_hba.nvmet_mrq_hdr[eqidx];
+ *len = __lpfc_idiag_print_rqpair(qp,
+ phba->sli4_hba.nvmet_mrq_data[eqidx],
+ "NVMET MRQ", pbuffer, *len);
+
+ if (*len >= max_cnt)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+__lpfc_idiag_print_eq(struct lpfc_queue *qp, char *eqtype,
+ char *pbuffer, int len)
+{
+ if (!qp)
+ return len;
+
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\n%s EQ info: EQ-STAT[max:x%x noE:x%x "
+ "cqe_proc:x%x eqe_proc:x%llx eqd %d]\n",
+ eqtype, qp->q_cnt_1, qp->q_cnt_2, qp->q_cnt_3,
+ (unsigned long long)qp->q_cnt_4, qp->q_mode);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "EQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
+ "HST-IDX[%04d], NTFI[%03d], PLMT[%03d], AFFIN[%03d]",
+ qp->queue_id, qp->entry_count, qp->entry_size,
+ qp->host_index, qp->notify_interval,
+ qp->max_proc_limit, qp->chann);
+ len += scnprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "\n");
+
+ return len;
+}
+
+/**
* lpfc_idiag_queinfo_read - idiag debugfs read queue information
* @file: The file pointer to read from.
* @buf: The buffer to copy the data to.
@@ -1578,6 +4056,9 @@ error_out:
* Description:
* This routine reads data from the @phba SLI4 PCI function queue information,
* and copies to user @buf.
+ * This routine only returns 1 EQs worth of information. It remembers the last
+ * EQ read and jumps to the next EQ. Thus subsequent calls to queInfo will
+ * retrieve all EQs allocated for the phba.
*
* Returns:
* This function returns the amount of data that was read (this could be less
@@ -1589,175 +4070,119 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
{
struct lpfc_debug *debug = file->private_data;
struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
- int len = 0, fcp_qidx;
char *pbuffer;
+ int max_cnt, rc, x, len = 0;
+ struct lpfc_queue *qp = NULL;
if (!debug->buffer)
debug->buffer = kmalloc(LPFC_QUE_INFO_GET_BUF_SIZE, GFP_KERNEL);
if (!debug->buffer)
return 0;
pbuffer = debug->buffer;
+ max_cnt = LPFC_QUE_INFO_GET_BUF_SIZE - 256;
if (*ppos)
return 0;
- /* Get slow-path event queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path EQ information:\n");
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tEQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
- phba->sli4_hba.sp_eq->queue_id,
- phba->sli4_hba.sp_eq->entry_count,
- phba->sli4_hba.sp_eq->entry_size,
- phba->sli4_hba.sp_eq->host_index,
- phba->sli4_hba.sp_eq->hba_index);
-
- /* Get fast-path event queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Fast-path EQ information:\n");
- for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++) {
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tEQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
- phba->sli4_hba.fp_eq[fcp_qidx]->queue_id,
- phba->sli4_hba.fp_eq[fcp_qidx]->entry_count,
- phba->sli4_hba.fp_eq[fcp_qidx]->entry_size,
- phba->sli4_hba.fp_eq[fcp_qidx]->host_index,
- phba->sli4_hba.fp_eq[fcp_qidx]->hba_index);
- }
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n");
-
- /* Get mailbox complete queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path MBX CQ information:\n");
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Associated EQID[%02d]:\n",
- phba->sli4_hba.mbx_cq->assoc_qid);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tCQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
- phba->sli4_hba.mbx_cq->queue_id,
- phba->sli4_hba.mbx_cq->entry_count,
- phba->sli4_hba.mbx_cq->entry_size,
- phba->sli4_hba.mbx_cq->host_index,
- phba->sli4_hba.mbx_cq->hba_index);
-
- /* Get slow-path complete queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path ELS CQ information:\n");
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Associated EQID[%02d]:\n",
- phba->sli4_hba.els_cq->assoc_qid);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tCQID [%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
- phba->sli4_hba.els_cq->queue_id,
- phba->sli4_hba.els_cq->entry_count,
- phba->sli4_hba.els_cq->entry_size,
- phba->sli4_hba.els_cq->host_index,
- phba->sli4_hba.els_cq->hba_index);
-
- /* Get fast-path complete queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Fast-path FCP CQ information:\n");
- fcp_qidx = 0;
- do {
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Associated EQID[%02d]:\n",
- phba->sli4_hba.fcp_cq[fcp_qidx]->assoc_qid);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tCQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
- phba->sli4_hba.fcp_cq[fcp_qidx]->queue_id,
- phba->sli4_hba.fcp_cq[fcp_qidx]->entry_count,
- phba->sli4_hba.fcp_cq[fcp_qidx]->entry_size,
- phba->sli4_hba.fcp_cq[fcp_qidx]->host_index,
- phba->sli4_hba.fcp_cq[fcp_qidx]->hba_index);
- } while (++fcp_qidx < phba->cfg_fcp_eq_count);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n");
-
- /* Get mailbox queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path MBX MQ information:\n");
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Associated CQID[%02d]:\n",
- phba->sli4_hba.mbx_wq->assoc_qid);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tWQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
- phba->sli4_hba.mbx_wq->queue_id,
- phba->sli4_hba.mbx_wq->entry_count,
- phba->sli4_hba.mbx_wq->entry_size,
- phba->sli4_hba.mbx_wq->host_index,
- phba->sli4_hba.mbx_wq->hba_index);
-
- /* Get slow-path work queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path ELS WQ information:\n");
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Associated CQID[%02d]:\n",
- phba->sli4_hba.els_wq->assoc_qid);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tWQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
- phba->sli4_hba.els_wq->queue_id,
- phba->sli4_hba.els_wq->entry_count,
- phba->sli4_hba.els_wq->entry_size,
- phba->sli4_hba.els_wq->host_index,
- phba->sli4_hba.els_wq->hba_index);
-
- /* Get fast-path work queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Fast-path FCP WQ information:\n");
- for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++) {
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Associated CQID[%02d]:\n",
- phba->sli4_hba.fcp_wq[fcp_qidx]->assoc_qid);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tWQID[%02d], "
- "QE-COUNT[%04d], WQE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
- phba->sli4_hba.fcp_wq[fcp_qidx]->queue_id,
- phba->sli4_hba.fcp_wq[fcp_qidx]->entry_count,
- phba->sli4_hba.fcp_wq[fcp_qidx]->entry_size,
- phba->sli4_hba.fcp_wq[fcp_qidx]->host_index,
- phba->sli4_hba.fcp_wq[fcp_qidx]->hba_index);
- }
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n");
-
- /* Get receive queue information */
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path RQ information:\n");
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Associated CQID[%02d]:\n",
- phba->sli4_hba.hdr_rq->assoc_qid);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tHQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
- phba->sli4_hba.hdr_rq->queue_id,
- phba->sli4_hba.hdr_rq->entry_count,
- phba->sli4_hba.hdr_rq->entry_size,
- phba->sli4_hba.hdr_rq->host_index,
- phba->sli4_hba.hdr_rq->hba_index);
- len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tDQID[%02d], "
- "QE-COUNT[%04d], QE-SIZE[%04d], "
- "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
- phba->sli4_hba.dat_rq->queue_id,
- phba->sli4_hba.dat_rq->entry_count,
- phba->sli4_hba.dat_rq->entry_size,
- phba->sli4_hba.dat_rq->host_index,
- phba->sli4_hba.dat_rq->hba_index);
+ spin_lock_irq(&phba->hbalock);
+ /* Fast-path event queue */
+ if (phba->sli4_hba.hdwq && phba->cfg_hdw_queue) {
+
+ x = phba->lpfc_idiag_last_eq;
+ phba->lpfc_idiag_last_eq++;
+ if (phba->lpfc_idiag_last_eq >= phba->cfg_hdw_queue)
+ phba->lpfc_idiag_last_eq = 0;
+
+ len += scnprintf(pbuffer + len,
+ LPFC_QUE_INFO_GET_BUF_SIZE - len,
+ "HDWQ %d out of %d HBA HDWQs\n",
+ x, phba->cfg_hdw_queue);
+
+ /* Fast-path EQ */
+ qp = phba->sli4_hba.hdwq[x].hba_eq;
+ if (!qp)
+ goto out;
+
+ len = __lpfc_idiag_print_eq(qp, "HBA", pbuffer, len);
+
+ /* Reset max counter */
+ qp->EQ_max_eqe = 0;
+
+ if (len >= max_cnt)
+ goto too_big;
+
+ /* will dump both fcp and nvme cqs/wqs for the eq */
+ rc = lpfc_idiag_cqs_for_eq(phba, pbuffer, &len,
+ max_cnt, x, qp->queue_id);
+ if (rc)
+ goto too_big;
+
+ /* Only EQ 0 has slow path CQs configured */
+ if (x)
+ goto out;
+
+ /* Slow-path mailbox CQ */
+ qp = phba->sli4_hba.mbx_cq;
+ len = __lpfc_idiag_print_cq(qp, "MBX", pbuffer, len);
+ if (len >= max_cnt)
+ goto too_big;
+
+ /* Slow-path MBOX MQ */
+ qp = phba->sli4_hba.mbx_wq;
+ len = __lpfc_idiag_print_wq(qp, "MBX", pbuffer, len);
+ if (len >= max_cnt)
+ goto too_big;
+
+ /* Slow-path ELS response CQ */
+ qp = phba->sli4_hba.els_cq;
+ len = __lpfc_idiag_print_cq(qp, "ELS", pbuffer, len);
+ /* Reset max counter */
+ if (qp)
+ qp->CQ_max_cqe = 0;
+ if (len >= max_cnt)
+ goto too_big;
+
+ /* Slow-path ELS WQ */
+ qp = phba->sli4_hba.els_wq;
+ len = __lpfc_idiag_print_wq(qp, "ELS", pbuffer, len);
+ if (len >= max_cnt)
+ goto too_big;
+
+ qp = phba->sli4_hba.hdr_rq;
+ len = __lpfc_idiag_print_rqpair(qp, phba->sli4_hba.dat_rq,
+ "ELS RQpair", pbuffer, len);
+ if (len >= max_cnt)
+ goto too_big;
+
+ /* Slow-path NVME LS response CQ */
+ qp = phba->sli4_hba.nvmels_cq;
+ len = __lpfc_idiag_print_cq(qp, "NVME LS",
+ pbuffer, len);
+ /* Reset max counter */
+ if (qp)
+ qp->CQ_max_cqe = 0;
+ if (len >= max_cnt)
+ goto too_big;
+
+ /* Slow-path NVME LS WQ */
+ qp = phba->sli4_hba.nvmels_wq;
+ len = __lpfc_idiag_print_wq(qp, "NVME LS",
+ pbuffer, len);
+ if (len >= max_cnt)
+ goto too_big;
+
+ goto out;
+ }
+
+ spin_unlock_irq(&phba->hbalock);
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+
+too_big:
+ len += scnprintf(pbuffer + len,
+ LPFC_QUE_INFO_GET_BUF_SIZE - len, "Truncated ...\n");
+out:
+ spin_unlock_irq(&phba->hbalock);
return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}
@@ -1788,6 +4213,7 @@ lpfc_idiag_que_param_check(struct lpfc_queue *q, int index, int count)
/**
* lpfc_idiag_queacc_read_qe - read a single entry from the given queue index
* @pbuffer: The pointer to buffer to copy the read data into.
+ * @len: Length of the buffer.
* @pque: The pointer to the queue to be read.
* @index: The index into the queue entry.
*
@@ -1810,22 +4236,22 @@ lpfc_idiag_queacc_read_qe(char *pbuffer, int len, struct lpfc_queue *pque,
return 0;
esize = pque->entry_size;
- len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
"QE-INDEX[%04d]:\n", index);
offset = 0;
- pentry = pque->qe[index].address;
+ pentry = lpfc_sli4_qe(pque, index);
while (esize > 0) {
- len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
"%08x ", *pentry);
pentry++;
offset += sizeof(uint32_t);
esize -= sizeof(uint32_t);
if (esize > 0 && !(offset % (4 * sizeof(uint32_t))))
- len += snprintf(pbuffer+len,
+ len += scnprintf(pbuffer+len,
LPFC_QUE_ACC_BUF_SIZE-len, "\n");
}
- len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len, "\n");
+ len += scnprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len, "\n");
return len;
}
@@ -1870,8 +4296,8 @@ lpfc_idiag_queacc_read(struct file *file, char __user *buf, size_t nbytes,
return 0;
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
- index = idiag.cmd.data[2];
- count = idiag.cmd.data[3];
+ index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
+ count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
pque = (struct lpfc_queue *)idiag.ptr_private;
} else
return 0;
@@ -1932,7 +4358,7 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
uint32_t qidx, quetp, queid, index, count, offset, value;
uint32_t *pentry;
- struct lpfc_queue *pque;
+ struct lpfc_queue *pque, *qp;
int rc;
/* This is a user write operation */
@@ -1943,12 +4369,12 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
return rc;
/* Get and sanity check on command feilds */
- quetp = idiag.cmd.data[0];
- queid = idiag.cmd.data[1];
- index = idiag.cmd.data[2];
- count = idiag.cmd.data[3];
- offset = idiag.cmd.data[4];
- value = idiag.cmd.data[5];
+ quetp = idiag.cmd.data[IDIAG_QUEACC_QUETP_INDX];
+ queid = idiag.cmd.data[IDIAG_QUEACC_QUEID_INDX];
+ index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
+ count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
+ offset = idiag.cmd.data[IDIAG_QUEACC_OFFST_INDX];
+ value = idiag.cmd.data[IDIAG_QUEACC_VALUE_INDX];
/* Sanity check on command line arguments */
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR ||
@@ -1966,34 +4392,27 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
switch (quetp) {
case LPFC_IDIAG_EQ:
- /* Slow-path event queue */
- if (phba->sli4_hba.sp_eq->queue_id == queid) {
- /* Sanity check */
- rc = lpfc_idiag_que_param_check(
- phba->sli4_hba.sp_eq, index, count);
- if (rc)
- goto error_out;
- idiag.ptr_private = phba->sli4_hba.sp_eq;
- goto pass_check;
- }
- /* Fast-path event queue */
- for (qidx = 0; qidx < phba->cfg_fcp_eq_count; qidx++) {
- if (phba->sli4_hba.fp_eq[qidx]->queue_id == queid) {
- /* Sanity check */
- rc = lpfc_idiag_que_param_check(
- phba->sli4_hba.fp_eq[qidx],
+ /* HBA event queue */
+ if (phba->sli4_hba.hdwq) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].hba_eq;
+ if (qp && qp->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(qp,
index, count);
- if (rc)
- goto error_out;
- idiag.ptr_private = phba->sli4_hba.fp_eq[qidx];
- goto pass_check;
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = qp;
+ goto pass_check;
+ }
}
}
goto error_out;
- break;
+
case LPFC_IDIAG_CQ:
/* MBX complete queue */
- if (phba->sli4_hba.mbx_cq->queue_id == queid) {
+ if (phba->sli4_hba.mbx_cq &&
+ phba->sli4_hba.mbx_cq->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
phba->sli4_hba.mbx_cq, index, count);
@@ -2003,7 +4422,8 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
goto pass_check;
}
/* ELS complete queue */
- if (phba->sli4_hba.els_cq->queue_id == queid) {
+ if (phba->sli4_hba.els_cq &&
+ phba->sli4_hba.els_cq->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
phba->sli4_hba.els_cq, index, count);
@@ -2012,26 +4432,39 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
idiag.ptr_private = phba->sli4_hba.els_cq;
goto pass_check;
}
+ /* NVME LS complete queue */
+ if (phba->sli4_hba.nvmels_cq &&
+ phba->sli4_hba.nvmels_cq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.nvmels_cq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.nvmels_cq;
+ goto pass_check;
+ }
/* FCP complete queue */
- qidx = 0;
- do {
- if (phba->sli4_hba.fcp_cq[qidx]->queue_id == queid) {
- /* Sanity check */
- rc = lpfc_idiag_que_param_check(
- phba->sli4_hba.fcp_cq[qidx],
- index, count);
- if (rc)
- goto error_out;
- idiag.ptr_private =
- phba->sli4_hba.fcp_cq[qidx];
- goto pass_check;
+ if (phba->sli4_hba.hdwq) {
+ for (qidx = 0; qidx < phba->cfg_hdw_queue;
+ qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].io_cq;
+ if (qp && qp->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ qp, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = qp;
+ goto pass_check;
+ }
}
- } while (++qidx < phba->cfg_fcp_eq_count);
+ }
goto error_out;
- break;
+
case LPFC_IDIAG_MQ:
/* MBX work queue */
- if (phba->sli4_hba.mbx_wq->queue_id == queid) {
+ if (phba->sli4_hba.mbx_wq &&
+ phba->sli4_hba.mbx_wq->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
phba->sli4_hba.mbx_wq, index, count);
@@ -2040,10 +4473,12 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
idiag.ptr_private = phba->sli4_hba.mbx_wq;
goto pass_check;
}
- break;
+ goto error_out;
+
case LPFC_IDIAG_WQ:
/* ELS work queue */
- if (phba->sli4_hba.els_wq->queue_id == queid) {
+ if (phba->sli4_hba.els_wq &&
+ phba->sli4_hba.els_wq->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
phba->sli4_hba.els_wq, index, count);
@@ -2052,25 +4487,39 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
idiag.ptr_private = phba->sli4_hba.els_wq;
goto pass_check;
}
- /* FCP work queue */
- for (qidx = 0; qidx < phba->cfg_fcp_wq_count; qidx++) {
- if (phba->sli4_hba.fcp_wq[qidx]->queue_id == queid) {
- /* Sanity check */
- rc = lpfc_idiag_que_param_check(
- phba->sli4_hba.fcp_wq[qidx],
- index, count);
- if (rc)
- goto error_out;
- idiag.ptr_private =
- phba->sli4_hba.fcp_wq[qidx];
- goto pass_check;
+ /* NVME LS work queue */
+ if (phba->sli4_hba.nvmels_wq &&
+ phba->sli4_hba.nvmels_wq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.nvmels_wq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.nvmels_wq;
+ goto pass_check;
+ }
+
+ if (phba->sli4_hba.hdwq) {
+ /* FCP/SCSI work queue */
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = phba->sli4_hba.hdwq[qidx].io_wq;
+ if (qp && qp->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ qp, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = qp;
+ goto pass_check;
+ }
}
}
goto error_out;
- break;
+
case LPFC_IDIAG_RQ:
/* HDR queue */
- if (phba->sli4_hba.hdr_rq->queue_id == queid) {
+ if (phba->sli4_hba.hdr_rq &&
+ phba->sli4_hba.hdr_rq->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
phba->sli4_hba.hdr_rq, index, count);
@@ -2080,7 +4529,8 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
goto pass_check;
}
/* DAT queue */
- if (phba->sli4_hba.dat_rq->queue_id == queid) {
+ if (phba->sli4_hba.dat_rq &&
+ phba->sli4_hba.dat_rq->queue_id == queid) {
/* Sanity check */
rc = lpfc_idiag_que_param_check(
phba->sli4_hba.dat_rq, index, count);
@@ -2090,10 +4540,8 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
goto pass_check;
}
goto error_out;
- break;
default:
goto error_out;
- break;
}
pass_check:
@@ -2110,7 +4558,7 @@ pass_check:
pque = (struct lpfc_queue *)idiag.ptr_private;
if (offset > pque->entry_size/sizeof(uint32_t) - 1)
goto error_out;
- pentry = pque->qe[index].address;
+ pentry = lpfc_sli4_qe(pque, index);
pentry += offset;
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR)
*pentry = value;
@@ -2131,7 +4579,7 @@ error_out:
* lpfc_idiag_drbacc_read_reg - idiag debugfs read a doorbell register
* @phba: The pointer to hba structure.
* @pbuffer: The pointer to the buffer to copy the data to.
- * @len: The lenght of bytes to copied.
+ * @len: The length of bytes to copied.
* @drbregid: The id to doorbell registers.
*
* Description:
@@ -2150,23 +4598,28 @@ lpfc_idiag_drbacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
return 0;
switch (drbregid) {
- case LPFC_DRB_EQCQ:
- len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
- "EQCQ-DRB-REG: 0x%08x\n",
- readl(phba->sli4_hba.EQCQDBregaddr));
+ case LPFC_DRB_EQ:
+ len += scnprintf(pbuffer + len, LPFC_DRB_ACC_BUF_SIZE-len,
+ "EQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.EQDBregaddr));
+ break;
+ case LPFC_DRB_CQ:
+ len += scnprintf(pbuffer + len, LPFC_DRB_ACC_BUF_SIZE - len,
+ "CQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.CQDBregaddr));
break;
case LPFC_DRB_MQ:
- len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
"MQ-DRB-REG: 0x%08x\n",
readl(phba->sli4_hba.MQDBregaddr));
break;
case LPFC_DRB_WQ:
- len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
"WQ-DRB-REG: 0x%08x\n",
readl(phba->sli4_hba.WQDBregaddr));
break;
case LPFC_DRB_RQ:
- len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
+ len += scnprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
"RQ-DRB-REG: 0x%08x\n",
readl(phba->sli4_hba.RQDBregaddr));
break;
@@ -2217,7 +4670,7 @@ lpfc_idiag_drbacc_read(struct file *file, char __user *buf, size_t nbytes,
return 0;
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD)
- drb_reg_id = idiag.cmd.data[0];
+ drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
else
return 0;
@@ -2256,7 +4709,7 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
{
struct lpfc_debug *debug = file->private_data;
struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
- uint32_t drb_reg_id, value, reg_val;
+ uint32_t drb_reg_id, value, reg_val = 0;
void __iomem *drb_reg;
int rc;
@@ -2268,8 +4721,8 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
return rc;
/* Sanity check on command line arguments */
- drb_reg_id = idiag.cmd.data[0];
- value = idiag.cmd.data[1];
+ drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
+ value = idiag.cmd.data[IDIAG_DRBACC_VALUE_INDX];
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR ||
idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
@@ -2292,8 +4745,11 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
switch (drb_reg_id) {
- case LPFC_DRB_EQCQ:
- drb_reg = phba->sli4_hba.EQCQDBregaddr;
+ case LPFC_DRB_EQ:
+ drb_reg = phba->sli4_hba.EQDBregaddr;
+ break;
+ case LPFC_DRB_CQ:
+ drb_reg = phba->sli4_hba.CQDBregaddr;
break;
case LPFC_DRB_MQ:
drb_reg = phba->sli4_hba.MQDBregaddr;
@@ -2329,6 +4785,827 @@ error_out:
return -EINVAL;
}
+/**
+ * lpfc_idiag_ctlacc_read_reg - idiag debugfs read a control registers
+ * @phba: The pointer to hba structure.
+ * @pbuffer: The pointer to the buffer to copy the data to.
+ * @len: The length of bytes to copied.
+ * @ctlregid: The id to doorbell registers.
+ *
+ * Description:
+ * This routine reads a control register and copies its content to the
+ * user buffer pointed to by @pbuffer.
+ *
+ * Returns:
+ * This function returns the amount of data that was copied into @pbuffer.
+ **/
+static int
+lpfc_idiag_ctlacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
+ int len, uint32_t ctlregid)
+{
+
+ if (!pbuffer)
+ return 0;
+
+ switch (ctlregid) {
+ case LPFC_CTL_PORT_SEM:
+ len += scnprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+ "Port SemReg: 0x%08x\n",
+ readl(phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_SEM_OFFSET));
+ break;
+ case LPFC_CTL_PORT_STA:
+ len += scnprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+ "Port StaReg: 0x%08x\n",
+ readl(phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_STA_OFFSET));
+ break;
+ case LPFC_CTL_PORT_CTL:
+ len += scnprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+ "Port CtlReg: 0x%08x\n",
+ readl(phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_CTL_OFFSET));
+ break;
+ case LPFC_CTL_PORT_ER1:
+ len += scnprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+ "Port Er1Reg: 0x%08x\n",
+ readl(phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER1_OFFSET));
+ break;
+ case LPFC_CTL_PORT_ER2:
+ len += scnprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+ "Port Er2Reg: 0x%08x\n",
+ readl(phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER2_OFFSET));
+ break;
+ case LPFC_CTL_PDEV_CTL:
+ len += scnprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
+ "PDev CtlReg: 0x%08x\n",
+ readl(phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PDEV_CTL_OFFSET));
+ break;
+ default:
+ break;
+ }
+ return len;
+}
+
+/**
+ * lpfc_idiag_ctlacc_read - idiag debugfs read port and device control register
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba port and device registers according
+ * to the idiag command, and copies to user @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_ctlacc_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ uint32_t ctl_reg_id, i;
+ char *pbuffer;
+ int len = 0;
+
+ /* This is a user read operation */
+ debug->op = LPFC_IDIAG_OP_RD;
+
+ if (!debug->buffer)
+ debug->buffer = kmalloc(LPFC_CTL_ACC_BUF_SIZE, GFP_KERNEL);
+ if (!debug->buffer)
+ return 0;
+ pbuffer = debug->buffer;
+
+ if (*ppos)
+ return 0;
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD)
+ ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
+ else
+ return 0;
+
+ if (ctl_reg_id == LPFC_CTL_ACC_ALL)
+ for (i = 1; i <= LPFC_CTL_MAX; i++)
+ len = lpfc_idiag_ctlacc_read_reg(phba,
+ pbuffer, len, i);
+ else
+ len = lpfc_idiag_ctlacc_read_reg(phba,
+ pbuffer, len, ctl_reg_id);
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_ctlacc_write - Syntax check and set up idiag ctlacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for port and device control register read (dump)
+ * or write (set) command accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_ctlacc_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ uint32_t ctl_reg_id, value, reg_val = 0;
+ void __iomem *ctl_reg;
+ int rc;
+
+ /* This is a user write operation */
+ debug->op = LPFC_IDIAG_OP_WR;
+
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+ if (rc < 0)
+ return rc;
+
+ /* Sanity check on command line arguments */
+ ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
+ value = idiag.cmd.data[IDIAG_CTLACC_VALUE_INDX];
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
+ if (rc != LPFC_CTL_ACC_WR_CMD_ARG)
+ goto error_out;
+ if (ctl_reg_id > LPFC_CTL_MAX)
+ goto error_out;
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD) {
+ if (rc != LPFC_CTL_ACC_RD_CMD_ARG)
+ goto error_out;
+ if ((ctl_reg_id > LPFC_CTL_MAX) &&
+ (ctl_reg_id != LPFC_CTL_ACC_ALL))
+ goto error_out;
+ } else
+ goto error_out;
+
+ /* Perform the write access operation */
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
+ switch (ctl_reg_id) {
+ case LPFC_CTL_PORT_SEM:
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_SEM_OFFSET;
+ break;
+ case LPFC_CTL_PORT_STA:
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_STA_OFFSET;
+ break;
+ case LPFC_CTL_PORT_CTL:
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_CTL_OFFSET;
+ break;
+ case LPFC_CTL_PORT_ER1:
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER1_OFFSET;
+ break;
+ case LPFC_CTL_PORT_ER2:
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER2_OFFSET;
+ break;
+ case LPFC_CTL_PDEV_CTL:
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PDEV_CTL_OFFSET;
+ break;
+ default:
+ goto error_out;
+ }
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR)
+ reg_val = value;
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST) {
+ reg_val = readl(ctl_reg);
+ reg_val |= value;
+ }
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
+ reg_val = readl(ctl_reg);
+ reg_val &= ~value;
+ }
+ writel(reg_val, ctl_reg);
+ readl(ctl_reg); /* flush */
+ }
+ return nbytes;
+
+error_out:
+ /* Clean out command structure on command error out */
+ memset(&idiag, 0, sizeof(idiag));
+ return -EINVAL;
+}
+
+/**
+ * lpfc_idiag_mbxacc_get_setup - idiag debugfs get mailbox access setup
+ * @phba: Pointer to HBA context object.
+ * @pbuffer: Pointer to data buffer.
+ *
+ * Description:
+ * This routine gets the driver mailbox access debugfs setup information.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static int
+lpfc_idiag_mbxacc_get_setup(struct lpfc_hba *phba, char *pbuffer)
+{
+ uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
+ int len = 0;
+
+ mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+ mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+ mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+ mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+ len += scnprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+ "mbx_dump_map: 0x%08x\n", mbx_dump_map);
+ len += scnprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+ "mbx_dump_cnt: %04d\n", mbx_dump_cnt);
+ len += scnprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+ "mbx_word_cnt: %04d\n", mbx_word_cnt);
+ len += scnprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
+ "mbx_mbox_cmd: 0x%02x\n", mbx_mbox_cmd);
+
+ return len;
+}
+
+/**
+ * lpfc_idiag_mbxacc_read - idiag debugfs read on mailbox access
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba driver mailbox access debugfs setup
+ * information.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_mbxacc_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ char *pbuffer;
+ int len = 0;
+
+ /* This is a user read operation */
+ debug->op = LPFC_IDIAG_OP_RD;
+
+ if (!debug->buffer)
+ debug->buffer = kmalloc(LPFC_MBX_ACC_BUF_SIZE, GFP_KERNEL);
+ if (!debug->buffer)
+ return 0;
+ pbuffer = debug->buffer;
+
+ if (*ppos)
+ return 0;
+
+ if ((idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP) &&
+ (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP))
+ return 0;
+
+ len = lpfc_idiag_mbxacc_get_setup(phba, pbuffer);
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_mbxacc_write - Syntax check and set up idiag mbxacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for driver mailbox command (dump) and sets up the
+ * necessary states in the idiag command struct accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_mbxacc_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
+ int rc;
+
+ /* This is a user write operation */
+ debug->op = LPFC_IDIAG_OP_WR;
+
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+ if (rc < 0)
+ return rc;
+
+ /* Sanity check on command line arguments */
+ mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+ mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+ mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+ mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_MBXACC_DP) {
+ if (!(mbx_dump_map & LPFC_MBX_DMP_MBX_ALL))
+ goto error_out;
+ if ((mbx_dump_map & ~LPFC_MBX_DMP_MBX_ALL) &&
+ (mbx_dump_map != LPFC_MBX_DMP_ALL))
+ goto error_out;
+ if (mbx_word_cnt > sizeof(MAILBOX_t))
+ goto error_out;
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_BSG_MBXACC_DP) {
+ if (!(mbx_dump_map & LPFC_BSG_DMP_MBX_ALL))
+ goto error_out;
+ if ((mbx_dump_map & ~LPFC_BSG_DMP_MBX_ALL) &&
+ (mbx_dump_map != LPFC_MBX_DMP_ALL))
+ goto error_out;
+ if (mbx_word_cnt > (BSG_MBOX_SIZE)/4)
+ goto error_out;
+ if (mbx_mbox_cmd != 0x9b)
+ goto error_out;
+ } else
+ goto error_out;
+
+ if (mbx_word_cnt == 0)
+ goto error_out;
+ if (rc != LPFC_MBX_DMP_ARG)
+ goto error_out;
+ if (mbx_mbox_cmd & ~0xff)
+ goto error_out;
+
+ /* condition for stop mailbox dump */
+ if (mbx_dump_cnt == 0)
+ goto reset_out;
+
+ return nbytes;
+
+reset_out:
+ /* Clean out command structure on command error out */
+ memset(&idiag, 0, sizeof(idiag));
+ return nbytes;
+
+error_out:
+ /* Clean out command structure on command error out */
+ memset(&idiag, 0, sizeof(idiag));
+ return -EINVAL;
+}
+
+/**
+ * lpfc_idiag_extacc_avail_get - get the available extents information
+ * @phba: pointer to lpfc hba data structure.
+ * @pbuffer: pointer to internal buffer.
+ * @len: length into the internal buffer data has been copied.
+ *
+ * Description:
+ * This routine is to get the available extent information.
+ *
+ * Returns:
+ * overall length of the data read into the internal buffer.
+ **/
+static int
+lpfc_idiag_extacc_avail_get(struct lpfc_hba *phba, char *pbuffer, int len)
+{
+ uint16_t ext_cnt = 0, ext_size = 0;
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\nAvailable Extents Information:\n");
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tPort Available VPI extents: ");
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VPI,
+ &ext_cnt, &ext_size);
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tPort Available VFI extents: ");
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VFI,
+ &ext_cnt, &ext_size);
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tPort Available RPI extents: ");
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_RPI,
+ &ext_cnt, &ext_size);
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tPort Available XRI extents: ");
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_XRI,
+ &ext_cnt, &ext_size);
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
+
+ return len;
+}
+
+/**
+ * lpfc_idiag_extacc_alloc_get - get the allocated extents information
+ * @phba: pointer to lpfc hba data structure.
+ * @pbuffer: pointer to internal buffer.
+ * @len: length into the internal buffer data has been copied.
+ *
+ * Description:
+ * This routine is to get the allocated extent information.
+ *
+ * Returns:
+ * overall length of the data read into the internal buffer.
+ **/
+static int
+lpfc_idiag_extacc_alloc_get(struct lpfc_hba *phba, char *pbuffer, int len)
+{
+ uint16_t ext_cnt, ext_size;
+ int rc;
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\nAllocated Extents Information:\n");
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tHost Allocated VPI extents: ");
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VPI,
+ &ext_cnt, &ext_size);
+ if (!rc)
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Port %d Extent %3d, Size %3d\n",
+ phba->brd_no, ext_cnt, ext_size);
+ else
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "N/A\n");
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tHost Allocated VFI extents: ");
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VFI,
+ &ext_cnt, &ext_size);
+ if (!rc)
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Port %d Extent %3d, Size %3d\n",
+ phba->brd_no, ext_cnt, ext_size);
+ else
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "N/A\n");
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tHost Allocated RPI extents: ");
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_RPI,
+ &ext_cnt, &ext_size);
+ if (!rc)
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Port %d Extent %3d, Size %3d\n",
+ phba->brd_no, ext_cnt, ext_size);
+ else
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "N/A\n");
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tHost Allocated XRI extents: ");
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_XRI,
+ &ext_cnt, &ext_size);
+ if (!rc)
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "Port %d Extent %3d, Size %3d\n",
+ phba->brd_no, ext_cnt, ext_size);
+ else
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "N/A\n");
+
+ return len;
+}
+
+/**
+ * lpfc_idiag_extacc_drivr_get - get driver extent information
+ * @phba: pointer to lpfc hba data structure.
+ * @pbuffer: pointer to internal buffer.
+ * @len: length into the internal buffer data has been copied.
+ *
+ * Description:
+ * This routine is to get the driver extent information.
+ *
+ * Returns:
+ * overall length of the data read into the internal buffer.
+ **/
+static int
+lpfc_idiag_extacc_drivr_get(struct lpfc_hba *phba, char *pbuffer, int len)
+{
+ struct lpfc_rsrc_blks *rsrc_blks;
+ int index;
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\nDriver Extents Information:\n");
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tVPI extents:\n");
+ index = 0;
+ list_for_each_entry(rsrc_blks, &phba->lpfc_vpi_blk_list, list) {
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
+ index, rsrc_blks->rsrc_start,
+ rsrc_blks->rsrc_size);
+ index++;
+ }
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tVFI extents:\n");
+ index = 0;
+ list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_vfi_blk_list,
+ list) {
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
+ index, rsrc_blks->rsrc_start,
+ rsrc_blks->rsrc_size);
+ index++;
+ }
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tRPI extents:\n");
+ index = 0;
+ list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_rpi_blk_list,
+ list) {
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
+ index, rsrc_blks->rsrc_start,
+ rsrc_blks->rsrc_size);
+ index++;
+ }
+
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\tXRI extents:\n");
+ index = 0;
+ list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_xri_blk_list,
+ list) {
+ len += scnprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
+ index, rsrc_blks->rsrc_start,
+ rsrc_blks->rsrc_size);
+ index++;
+ }
+
+ return len;
+}
+
+/**
+ * lpfc_idiag_extacc_write - Syntax check and set up idiag extacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for extent information access commands and sets
+ * up the necessary states in the idiag command struct accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_extacc_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ uint32_t ext_map;
+ int rc;
+
+ /* This is a user write operation */
+ debug->op = LPFC_IDIAG_OP_WR;
+
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+ if (rc < 0)
+ return rc;
+
+ ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];
+
+ if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
+ goto error_out;
+ if (rc != LPFC_EXT_ACC_CMD_ARG)
+ goto error_out;
+ if (!(ext_map & LPFC_EXT_ACC_ALL))
+ goto error_out;
+
+ return nbytes;
+error_out:
+ /* Clean out command structure on command error out */
+ memset(&idiag, 0, sizeof(idiag));
+ return -EINVAL;
+}
+
+/**
+ * lpfc_idiag_extacc_read - idiag debugfs read access to extent information
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the proper extent information according to
+ * the idiag command, and copies to user @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_extacc_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ char *pbuffer;
+ uint32_t ext_map;
+ int len = 0;
+
+ /* This is a user read operation */
+ debug->op = LPFC_IDIAG_OP_RD;
+
+ if (!debug->buffer)
+ debug->buffer = kmalloc(LPFC_EXT_ACC_BUF_SIZE, GFP_KERNEL);
+ if (!debug->buffer)
+ return 0;
+ pbuffer = debug->buffer;
+ if (*ppos)
+ return 0;
+ if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
+ return 0;
+
+ ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];
+ if (ext_map & LPFC_EXT_ACC_AVAIL)
+ len = lpfc_idiag_extacc_avail_get(phba, pbuffer, len);
+ if (ext_map & LPFC_EXT_ACC_ALLOC)
+ len = lpfc_idiag_extacc_alloc_get(phba, pbuffer, len);
+ if (ext_map & LPFC_EXT_ACC_DRIVR)
+ len = lpfc_idiag_extacc_drivr_get(phba, pbuffer, len);
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+static int
+lpfc_cgn_buffer_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ debug->buffer = vmalloc(LPFC_CGN_BUF_SIZE);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_cgn_buffer_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ char *buffer = debug->buffer;
+ uint32_t *ptr;
+ int cnt, len = 0;
+
+ if (!phba->sli4_hba.pc_sli4_params.mi_ver || !phba->cgn_i) {
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "Congestion Mgmt is not supported\n");
+ goto out;
+ }
+ ptr = (uint32_t *)phba->cgn_i->virt;
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "Congestion Buffer Header\n");
+ /* Dump the first 32 bytes */
+ cnt = 32;
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "000: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3),
+ *(ptr + 4), *(ptr + 5), *(ptr + 6), *(ptr + 7));
+ ptr += 8;
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "Congestion Buffer Data\n");
+ while (cnt < sizeof(struct lpfc_cgn_info)) {
+ if (len > (LPFC_CGN_BUF_SIZE - LPFC_DEBUG_OUT_LINE_SZ)) {
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "Truncated . . .\n");
+ goto out;
+ }
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "%03x: %08x %08x %08x %08x "
+ "%08x %08x %08x %08x\n",
+ cnt, *ptr, *(ptr + 1), *(ptr + 2),
+ *(ptr + 3), *(ptr + 4), *(ptr + 5),
+ *(ptr + 6), *(ptr + 7));
+ cnt += 32;
+ ptr += 8;
+ }
+ if (len > (LPFC_CGN_BUF_SIZE - LPFC_DEBUG_OUT_LINE_SZ)) {
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "Truncated . . .\n");
+ goto out;
+ }
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "Parameter Data\n");
+ ptr = (uint32_t *)&phba->cgn_p;
+ len += scnprintf(buffer + len, LPFC_CGN_BUF_SIZE - len,
+ "%08x %08x %08x %08x\n",
+ *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3));
+out:
+ return simple_read_from_buffer(buf, nbytes, ppos, buffer, len);
+}
+
+static int
+lpfc_cgn_buffer_release(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug = file->private_data;
+
+ vfree(debug->buffer);
+ kfree(debug);
+
+ return 0;
+}
+
+static int
+lpfc_rx_monitor_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_rx_monitor_debug *debug;
+ int rc = -ENOMEM;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ debug->buffer = vmalloc(MAX_DEBUGFS_RX_INFO_SIZE);
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->i_private = inode->i_private;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_rx_monitor_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_rx_monitor_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ char *buffer = debug->buffer;
+
+ if (!phba->rx_monitor) {
+ scnprintf(buffer, MAX_DEBUGFS_RX_INFO_SIZE,
+ "Rx Monitor Info is empty.\n");
+ } else {
+ lpfc_rx_monitor_report(phba, phba->rx_monitor, buffer,
+ MAX_DEBUGFS_RX_INFO_SIZE,
+ LPFC_MAX_RXMONITOR_ENTRY);
+ }
+
+ return simple_read_from_buffer(buf, nbytes, ppos, buffer,
+ strlen(buffer));
+}
+
+static int
+lpfc_rx_monitor_release(struct inode *inode, struct file *file)
+{
+ struct lpfc_rx_monitor_debug *debug = file->private_data;
+
+ vfree(debug->buffer);
+ kfree(debug);
+
+ return 0;
+}
+
#undef lpfc_debugfs_op_disc_trc
static const struct file_operations lpfc_debugfs_op_disc_trc = {
.owner = THIS_MODULE,
@@ -2347,6 +5624,16 @@ static const struct file_operations lpfc_debugfs_op_nodelist = {
.release = lpfc_debugfs_release,
};
+#undef lpfc_debugfs_op_multixripools
+static const struct file_operations lpfc_debugfs_op_multixripools = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_multixripools_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_multixripools_write,
+ .release = lpfc_debugfs_release,
+};
+
#undef lpfc_debugfs_op_hbqinfo
static const struct file_operations lpfc_debugfs_op_hbqinfo = {
.owner = THIS_MODULE,
@@ -2356,6 +5643,27 @@ static const struct file_operations lpfc_debugfs_op_hbqinfo = {
.release = lpfc_debugfs_release,
};
+#ifdef LPFC_HDWQ_LOCK_STAT
+#undef lpfc_debugfs_op_lockstat
+static const struct file_operations lpfc_debugfs_op_lockstat = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_lockstat_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_lockstat_write,
+ .release = lpfc_debugfs_release,
+};
+#endif
+
+#undef lpfc_debugfs_ras_log
+static const struct file_operations lpfc_debugfs_ras_log = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_ras_log_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .release = lpfc_debugfs_ras_log_release,
+};
+
#undef lpfc_debugfs_op_dumpHBASlim
static const struct file_operations lpfc_debugfs_op_dumpHBASlim = {
.owner = THIS_MODULE,
@@ -2374,24 +5682,64 @@ static const struct file_operations lpfc_debugfs_op_dumpHostSlim = {
.release = lpfc_debugfs_release,
};
-#undef lpfc_debugfs_op_dumpData
-static const struct file_operations lpfc_debugfs_op_dumpData = {
+#undef lpfc_debugfs_op_nvmestat
+static const struct file_operations lpfc_debugfs_op_nvmestat = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_nvmestat_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_nvmestat_write,
+ .release = lpfc_debugfs_release,
+};
+
+#undef lpfc_debugfs_op_scsistat
+static const struct file_operations lpfc_debugfs_op_scsistat = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_scsistat_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_scsistat_write,
+ .release = lpfc_debugfs_release,
+};
+
+#undef lpfc_debugfs_op_ioktime
+static const struct file_operations lpfc_debugfs_op_ioktime = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_ioktime_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_ioktime_write,
+ .release = lpfc_debugfs_release,
+};
+
+#undef lpfc_debugfs_op_nvmeio_trc
+static const struct file_operations lpfc_debugfs_op_nvmeio_trc = {
.owner = THIS_MODULE,
- .open = lpfc_debugfs_dumpData_open,
+ .open = lpfc_debugfs_nvmeio_trc_open,
.llseek = lpfc_debugfs_lseek,
.read = lpfc_debugfs_read,
- .write = lpfc_debugfs_dumpDataDif_write,
- .release = lpfc_debugfs_dumpDataDif_release,
+ .write = lpfc_debugfs_nvmeio_trc_write,
+ .release = lpfc_debugfs_release,
};
-#undef lpfc_debugfs_op_dumpDif
-static const struct file_operations lpfc_debugfs_op_dumpDif = {
+#undef lpfc_debugfs_op_hdwqstat
+static const struct file_operations lpfc_debugfs_op_hdwqstat = {
.owner = THIS_MODULE,
- .open = lpfc_debugfs_dumpDif_open,
+ .open = lpfc_debugfs_hdwqstat_open,
.llseek = lpfc_debugfs_lseek,
.read = lpfc_debugfs_read,
- .write = lpfc_debugfs_dumpDataDif_write,
- .release = lpfc_debugfs_dumpDataDif_release,
+ .write = lpfc_debugfs_hdwqstat_write,
+ .release = lpfc_debugfs_release,
+};
+
+#undef lpfc_debugfs_op_dif_err
+static const struct file_operations lpfc_debugfs_op_dif_err = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_dif_err_read,
+ .write = lpfc_debugfs_dif_err_write,
+ .release = lpfc_debugfs_dif_err_release,
};
#undef lpfc_debugfs_op_slow_ring_trc
@@ -2404,7 +5752,7 @@ static const struct file_operations lpfc_debugfs_op_slow_ring_trc = {
};
static struct dentry *lpfc_debugfs_root = NULL;
-static atomic_t lpfc_debugfs_hba_count;
+static unsigned int lpfc_debugfs_hba_count;
/*
* File operations for the iDiag debugfs
@@ -2419,6 +5767,16 @@ static const struct file_operations lpfc_idiag_op_pciCfg = {
.release = lpfc_idiag_cmd_release,
};
+#undef lpfc_idiag_op_barAcc
+static const struct file_operations lpfc_idiag_op_barAcc = {
+ .owner = THIS_MODULE,
+ .open = lpfc_idiag_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_idiag_baracc_read,
+ .write = lpfc_idiag_baracc_write,
+ .release = lpfc_idiag_cmd_release,
+};
+
#undef lpfc_idiag_op_queInfo
static const struct file_operations lpfc_idiag_op_queInfo = {
.owner = THIS_MODULE,
@@ -2427,7 +5785,7 @@ static const struct file_operations lpfc_idiag_op_queInfo = {
.release = lpfc_idiag_release,
};
-#undef lpfc_idiag_op_queacc
+#undef lpfc_idiag_op_queAcc
static const struct file_operations lpfc_idiag_op_queAcc = {
.owner = THIS_MODULE,
.open = lpfc_idiag_open,
@@ -2437,7 +5795,7 @@ static const struct file_operations lpfc_idiag_op_queAcc = {
.release = lpfc_idiag_cmd_release,
};
-#undef lpfc_idiag_op_drbacc
+#undef lpfc_idiag_op_drbAcc
static const struct file_operations lpfc_idiag_op_drbAcc = {
.owner = THIS_MODULE,
.open = lpfc_idiag_open,
@@ -2447,7 +5805,249 @@ static const struct file_operations lpfc_idiag_op_drbAcc = {
.release = lpfc_idiag_cmd_release,
};
+#undef lpfc_idiag_op_ctlAcc
+static const struct file_operations lpfc_idiag_op_ctlAcc = {
+ .owner = THIS_MODULE,
+ .open = lpfc_idiag_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_idiag_ctlacc_read,
+ .write = lpfc_idiag_ctlacc_write,
+ .release = lpfc_idiag_cmd_release,
+};
+
+#undef lpfc_idiag_op_mbxAcc
+static const struct file_operations lpfc_idiag_op_mbxAcc = {
+ .owner = THIS_MODULE,
+ .open = lpfc_idiag_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_idiag_mbxacc_read,
+ .write = lpfc_idiag_mbxacc_write,
+ .release = lpfc_idiag_cmd_release,
+};
+
+#undef lpfc_idiag_op_extAcc
+static const struct file_operations lpfc_idiag_op_extAcc = {
+ .owner = THIS_MODULE,
+ .open = lpfc_idiag_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_idiag_extacc_read,
+ .write = lpfc_idiag_extacc_write,
+ .release = lpfc_idiag_cmd_release,
+};
+#undef lpfc_cgn_buffer_op
+static const struct file_operations lpfc_cgn_buffer_op = {
+ .owner = THIS_MODULE,
+ .open = lpfc_cgn_buffer_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_cgn_buffer_read,
+ .release = lpfc_cgn_buffer_release,
+};
+
+#undef lpfc_rx_monitor_op
+static const struct file_operations lpfc_rx_monitor_op = {
+ .owner = THIS_MODULE,
+ .open = lpfc_rx_monitor_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_rx_monitor_read,
+ .release = lpfc_rx_monitor_release,
+};
+#endif
+
+/* lpfc_idiag_mbxacc_dump_bsg_mbox - idiag debugfs dump bsg mailbox command
+ * @phba: Pointer to HBA context object.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
+ *
+ * Description:
+ * This routine dump a bsg pass-through non-embedded mailbox command with
+ * external buffer.
+ **/
+void
+lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp,
+ enum mbox_type mbox_tp, enum dma_type dma_tp,
+ enum sta_type sta_tp,
+ struct lpfc_dmabuf *dmabuf, uint32_t ext_buf)
+{
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint32_t *mbx_mbox_cmd, *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt;
+ char line_buf[LPFC_MBX_ACC_LBUF_SZ];
+ int len = 0;
+ uint32_t do_dump = 0;
+ uint32_t *pword;
+ uint32_t i;
+
+ if (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP)
+ return;
+
+ mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+ mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+ mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+ mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+ if (!(*mbx_dump_map & LPFC_MBX_DMP_ALL) ||
+ (*mbx_dump_cnt == 0) ||
+ (*mbx_word_cnt == 0))
+ return;
+
+ if (*mbx_mbox_cmd != 0x9B)
+ return;
+
+ if ((mbox_tp == mbox_rd) && (dma_tp == dma_mbox)) {
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_MBX) {
+ do_dump |= LPFC_BSG_DMP_MBX_RD_MBX;
+ pr_err("\nRead mbox command (x%x), "
+ "nemb:0x%x, extbuf_cnt:%d:\n",
+ sta_tp, nemb_tp, ext_buf);
+ }
+ }
+ if ((mbox_tp == mbox_rd) && (dma_tp == dma_ebuf)) {
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_BUF) {
+ do_dump |= LPFC_BSG_DMP_MBX_RD_BUF;
+ pr_err("\nRead mbox buffer (x%x), "
+ "nemb:0x%x, extbuf_seq:%d:\n",
+ sta_tp, nemb_tp, ext_buf);
+ }
+ }
+ if ((mbox_tp == mbox_wr) && (dma_tp == dma_mbox)) {
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_MBX) {
+ do_dump |= LPFC_BSG_DMP_MBX_WR_MBX;
+ pr_err("\nWrite mbox command (x%x), "
+ "nemb:0x%x, extbuf_cnt:%d:\n",
+ sta_tp, nemb_tp, ext_buf);
+ }
+ }
+ if ((mbox_tp == mbox_wr) && (dma_tp == dma_ebuf)) {
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_BUF) {
+ do_dump |= LPFC_BSG_DMP_MBX_WR_BUF;
+ pr_err("\nWrite mbox buffer (x%x), "
+ "nemb:0x%x, extbuf_seq:%d:\n",
+ sta_tp, nemb_tp, ext_buf);
+ }
+ }
+
+ /* dump buffer content */
+ if (do_dump) {
+ pword = (uint32_t *)dmabuf->virt;
+ for (i = 0; i < *mbx_word_cnt; i++) {
+ if (!(i % 8)) {
+ if (i != 0)
+ pr_err("%s\n", line_buf);
+ len = 0;
+ len += scnprintf(line_buf+len,
+ LPFC_MBX_ACC_LBUF_SZ-len,
+ "%03d: ", i);
+ }
+ len += scnprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
+ "%08x ", (uint32_t)*pword);
+ pword++;
+ }
+ if ((i - 1) % 8)
+ pr_err("%s\n", line_buf);
+ (*mbx_dump_cnt)--;
+ }
+
+ /* Clean out command structure on reaching dump count */
+ if (*mbx_dump_cnt == 0)
+ memset(&idiag, 0, sizeof(idiag));
+ return;
+#endif
+}
+
+/* lpfc_idiag_mbxacc_dump_issue_mbox - idiag debugfs dump issue mailbox command
+ * @phba: Pointer to HBA context object.
+ * @dmabuf: Pointer to a DMA buffer descriptor.
+ *
+ * Description:
+ * This routine dump a pass-through non-embedded mailbox command from issue
+ * mailbox command.
+ **/
+void
+lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox)
+{
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint32_t *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt, *mbx_mbox_cmd;
+ char line_buf[LPFC_MBX_ACC_LBUF_SZ];
+ int len = 0;
+ uint32_t *pword;
+ uint8_t *pbyte;
+ uint32_t i, j;
+
+ if (idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP)
+ return;
+
+ mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
+ mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
+ mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
+ mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
+
+ if (!(*mbx_dump_map & LPFC_MBX_DMP_MBX_ALL) ||
+ (*mbx_dump_cnt == 0) ||
+ (*mbx_word_cnt == 0))
+ return;
+
+ if ((*mbx_mbox_cmd != LPFC_MBX_ALL_CMD) &&
+ (*mbx_mbox_cmd != pmbox->mbxCommand))
+ return;
+
+ /* dump buffer content */
+ if (*mbx_dump_map & LPFC_MBX_DMP_MBX_WORD) {
+ pr_err("Mailbox command:0x%x dump by word:\n",
+ pmbox->mbxCommand);
+ pword = (uint32_t *)pmbox;
+ for (i = 0; i < *mbx_word_cnt; i++) {
+ if (!(i % 8)) {
+ if (i != 0)
+ pr_err("%s\n", line_buf);
+ len = 0;
+ memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
+ len += scnprintf(line_buf+len,
+ LPFC_MBX_ACC_LBUF_SZ-len,
+ "%03d: ", i);
+ }
+ len += scnprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
+ "%08x ",
+ ((uint32_t)*pword) & 0xffffffff);
+ pword++;
+ }
+ if ((i - 1) % 8)
+ pr_err("%s\n", line_buf);
+ pr_err("\n");
+ }
+ if (*mbx_dump_map & LPFC_MBX_DMP_MBX_BYTE) {
+ pr_err("Mailbox command:0x%x dump by byte:\n",
+ pmbox->mbxCommand);
+ pbyte = (uint8_t *)pmbox;
+ for (i = 0; i < *mbx_word_cnt; i++) {
+ if (!(i % 8)) {
+ if (i != 0)
+ pr_err("%s\n", line_buf);
+ len = 0;
+ memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
+ len += scnprintf(line_buf+len,
+ LPFC_MBX_ACC_LBUF_SZ-len,
+ "%03d: ", i);
+ }
+ for (j = 0; j < 4; j++) {
+ len += scnprintf(line_buf+len,
+ LPFC_MBX_ACC_LBUF_SZ-len,
+ "%02x",
+ ((uint8_t)*pbyte) & 0xff);
+ pbyte++;
+ }
+ len += scnprintf(line_buf+len,
+ LPFC_MBX_ACC_LBUF_SZ-len, " ");
+ }
+ if ((i - 1) % 8)
+ pr_err("%s\n", line_buf);
+ pr_err("\n");
+ }
+ (*mbx_dump_cnt)--;
+
+ /* Clean out command structure on reaching dump count */
+ if (*mbx_dump_cnt == 0)
+ memset(&idiag, 0, sizeof(idiag));
+ return;
#endif
+}
/**
* lpfc_debugfs_initialize - Initialize debugfs for a vport
@@ -2466,6 +6066,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
struct lpfc_hba *phba = vport->phba;
char name[64];
uint32_t num, i;
+ bool pport_setup = false;
if (!lpfc_debugfs_enable)
return;
@@ -2473,11 +6074,11 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
/* Setup lpfc root directory */
if (!lpfc_debugfs_root) {
lpfc_debugfs_root = debugfs_create_dir("lpfc", NULL);
- atomic_set(&lpfc_debugfs_hba_count, 0);
- if (!lpfc_debugfs_root) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0408 Cannot create debugfs root\n");
- goto debug_failed;
+ lpfc_debugfs_hba_count = 0;
+ if (IS_ERR(lpfc_debugfs_root)) {
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_INIT,
+ "0527 Cannot create debugfs lpfc\n");
+ return;
}
}
if (!lpfc_debugfs_start_time)
@@ -2486,86 +6087,100 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
/* Setup funcX directory for specific HBA PCI function */
snprintf(name, sizeof(name), "fn%d", phba->brd_no);
if (!phba->hba_debugfs_root) {
+ pport_setup = true;
phba->hba_debugfs_root =
debugfs_create_dir(name, lpfc_debugfs_root);
- if (!phba->hba_debugfs_root) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0412 Cannot create debugfs hba\n");
- goto debug_failed;
+ phba->debugfs_vport_count = 0;
+ if (IS_ERR(phba->hba_debugfs_root)) {
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_INIT,
+ "0528 Cannot create debugfs %s\n", name);
+ return;
}
- atomic_inc(&lpfc_debugfs_hba_count);
- atomic_set(&phba->debugfs_vport_count, 0);
+ lpfc_debugfs_hba_count++;
+
+ /* Multi-XRI pools */
+ debugfs_create_file("multixripools", 0644,
+ phba->hba_debugfs_root, phba,
+ &lpfc_debugfs_op_multixripools);
+
+ /* Congestion Info Buffer */
+ debugfs_create_file("cgn_buffer", 0644, phba->hba_debugfs_root,
+ phba, &lpfc_cgn_buffer_op);
+
+ /* RX Monitor */
+ debugfs_create_file("rx_monitor", 0644, phba->hba_debugfs_root,
+ phba, &lpfc_rx_monitor_op);
+
+ /* RAS log */
+ debugfs_create_file("ras_log", 0644, phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_ras_log);
/* Setup hbqinfo */
- snprintf(name, sizeof(name), "hbqinfo");
- phba->debug_hbqinfo =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->hba_debugfs_root,
- phba, &lpfc_debugfs_op_hbqinfo);
- if (!phba->debug_hbqinfo) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0411 Cannot create debugfs hbqinfo\n");
- goto debug_failed;
- }
-
- /* Setup dumpHBASlim */
+ debugfs_create_file("hbqinfo", 0644, phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_hbqinfo);
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+ /* Setup lockstat */
+ debugfs_create_file("lockstat", 0644, phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_lockstat);
+#endif
if (phba->sli_rev < LPFC_SLI_REV4) {
- snprintf(name, sizeof(name), "dumpHBASlim");
- phba->debug_dumpHBASlim =
- debugfs_create_file(name,
- S_IFREG|S_IRUGO|S_IWUSR,
- phba->hba_debugfs_root,
- phba, &lpfc_debugfs_op_dumpHBASlim);
- if (!phba->debug_dumpHBASlim) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0413 Cannot create debugfs "
- "dumpHBASlim\n");
- goto debug_failed;
- }
- } else
- phba->debug_dumpHBASlim = NULL;
+ /* Setup dumpHBASlim */
+ debugfs_create_file("dumpHBASlim", 0644,
+ phba->hba_debugfs_root, phba,
+ &lpfc_debugfs_op_dumpHBASlim);
+ }
- /* Setup dumpHostSlim */
if (phba->sli_rev < LPFC_SLI_REV4) {
- snprintf(name, sizeof(name), "dumpHostSlim");
- phba->debug_dumpHostSlim =
- debugfs_create_file(name,
- S_IFREG|S_IRUGO|S_IWUSR,
- phba->hba_debugfs_root,
- phba, &lpfc_debugfs_op_dumpHostSlim);
- if (!phba->debug_dumpHostSlim) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0414 Cannot create debugfs "
- "dumpHostSlim\n");
- goto debug_failed;
- }
- } else
- phba->debug_dumpHBASlim = NULL;
-
- /* Setup dumpData */
- snprintf(name, sizeof(name), "dumpData");
- phba->debug_dumpData =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->hba_debugfs_root,
- phba, &lpfc_debugfs_op_dumpData);
- if (!phba->debug_dumpData) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0800 Cannot create debugfs dumpData\n");
- goto debug_failed;
- }
-
- /* Setup dumpDif */
- snprintf(name, sizeof(name), "dumpDif");
- phba->debug_dumpDif =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->hba_debugfs_root,
- phba, &lpfc_debugfs_op_dumpDif);
- if (!phba->debug_dumpDif) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0801 Cannot create debugfs dumpDif\n");
- goto debug_failed;
+ /* Setup dumpHostSlim */
+ debugfs_create_file("dumpHostSlim", 0644,
+ phba->hba_debugfs_root, phba,
+ &lpfc_debugfs_op_dumpHostSlim);
}
+ /* Setup DIF Error Injections */
+ debugfs_create_file_aux_num("InjErrLBA", 0644,
+ phba->hba_debugfs_root, phba,
+ InjErrLBA,
+ &lpfc_debugfs_op_dif_err);
+ phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+
+ debugfs_create_file_aux_num("InjErrNPortID", 0644,
+ phba->hba_debugfs_root, phba,
+ InjErrNPortID,
+ &lpfc_debugfs_op_dif_err);
+
+ debugfs_create_file_aux_num("InjErrWWPN", 0644,
+ phba->hba_debugfs_root, phba,
+ InjErrWWPN,
+ &lpfc_debugfs_op_dif_err);
+
+ debugfs_create_file_aux_num("writeGuardInjErr", 0644,
+ phba->hba_debugfs_root, phba,
+ writeGuard,
+ &lpfc_debugfs_op_dif_err);
+
+ debugfs_create_file_aux_num("writeAppInjErr", 0644,
+ phba->hba_debugfs_root, phba,
+ writeApp, &lpfc_debugfs_op_dif_err);
+
+ debugfs_create_file_aux_num("writeRefInjErr", 0644,
+ phba->hba_debugfs_root, phba,
+ writeRef, &lpfc_debugfs_op_dif_err);
+
+ debugfs_create_file_aux_num("readGuardInjErr", 0644,
+ phba->hba_debugfs_root, phba,
+ readGuard,
+ &lpfc_debugfs_op_dif_err);
+
+ debugfs_create_file_aux_num("readAppInjErr", 0644,
+ phba->hba_debugfs_root, phba,
+ readApp, &lpfc_debugfs_op_dif_err);
+
+ debugfs_create_file_aux_num("readRefInjErr", 0644,
+ phba->hba_debugfs_root, phba,
+ readRef, &lpfc_debugfs_op_dif_err);
+
/* Setup slow ring trace */
if (lpfc_debugfs_max_slow_ring_trc) {
num = lpfc_debugfs_max_slow_ring_trc - 1;
@@ -2578,38 +6193,69 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
i++;
}
lpfc_debugfs_max_slow_ring_trc = (1 << i);
- printk(KERN_ERR
- "lpfc_debugfs_max_disc_trc changed to "
- "%d\n", lpfc_debugfs_max_disc_trc);
+ pr_info("lpfc_debugfs_max_slow_ring_trc "
+ "changed to %d\n",
+ lpfc_debugfs_max_slow_ring_trc);
}
}
- snprintf(name, sizeof(name), "slow_ring_trace");
- phba->debug_slow_ring_trc =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->hba_debugfs_root,
- phba, &lpfc_debugfs_op_slow_ring_trc);
- if (!phba->debug_slow_ring_trc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0415 Cannot create debugfs "
- "slow_ring_trace\n");
- goto debug_failed;
- }
+ debugfs_create_file("slow_ring_trace", 0644,
+ phba->hba_debugfs_root, phba,
+ &lpfc_debugfs_op_slow_ring_trc);
if (!phba->slow_ring_trc) {
- phba->slow_ring_trc = kmalloc(
- (sizeof(struct lpfc_debugfs_trc) *
- lpfc_debugfs_max_slow_ring_trc),
+ phba->slow_ring_trc = kcalloc(
+ lpfc_debugfs_max_slow_ring_trc,
+ sizeof(struct lpfc_debugfs_trc),
GFP_KERNEL);
if (!phba->slow_ring_trc) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
"0416 Cannot create debugfs "
"slow_ring buffer\n");
- goto debug_failed;
+ goto out;
}
atomic_set(&phba->slow_ring_trc_cnt, 0);
- memset(phba->slow_ring_trc, 0,
- (sizeof(struct lpfc_debugfs_trc) *
- lpfc_debugfs_max_slow_ring_trc));
+ }
+
+ debugfs_create_file("nvmeio_trc", 0644, phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_nvmeio_trc);
+
+ atomic_set(&phba->nvmeio_trc_cnt, 0);
+ if (lpfc_debugfs_max_nvmeio_trc) {
+ num = lpfc_debugfs_max_nvmeio_trc - 1;
+ if (num & lpfc_debugfs_max_nvmeio_trc) {
+ /* Change to be a power of 2 */
+ num = lpfc_debugfs_max_nvmeio_trc;
+ i = 0;
+ while (num > 1) {
+ num = num >> 1;
+ i++;
+ }
+ lpfc_debugfs_max_nvmeio_trc = (1 << i);
+ pr_info("lpfc_debugfs_max_nvmeio_trc changed "
+ "to %d\n",
+ lpfc_debugfs_max_nvmeio_trc);
+ }
+ phba->nvmeio_trc_size = lpfc_debugfs_max_nvmeio_trc;
+
+ /* Allocate trace buffer and initialize */
+ phba->nvmeio_trc = kzalloc(
+ (sizeof(struct lpfc_debugfs_nvmeio_trc) *
+ phba->nvmeio_trc_size), GFP_KERNEL);
+
+ if (!phba->nvmeio_trc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0576 Cannot create debugfs "
+ "nvmeio_trc buffer\n");
+ goto nvmeio_off;
+ }
+ phba->nvmeio_trc_on = 1;
+ phba->nvmeio_trc_output_idx = 0;
+ } else {
+nvmeio_off:
+ phba->nvmeio_trc_size = 0;
+ phba->nvmeio_trc_on = 0;
+ phba->nvmeio_trc_output_idx = 0;
+ phba->nvmeio_trc = NULL;
}
}
@@ -2617,12 +6263,12 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
if (!vport->vport_debugfs_root) {
vport->vport_debugfs_root =
debugfs_create_dir(name, phba->hba_debugfs_root);
- if (!vport->vport_debugfs_root) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0417 Can't create debugfs\n");
- goto debug_failed;
+ if (IS_ERR(vport->vport_debugfs_root)) {
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_INIT,
+ "0529 Cannot create debugfs %s\n", name);
+ return;
}
- atomic_inc(&phba->debugfs_vport_count);
+ phba->debugfs_vport_count++;
}
if (lpfc_debugfs_max_disc_trc) {
@@ -2636,9 +6282,8 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
i++;
}
lpfc_debugfs_max_disc_trc = (1 << i);
- printk(KERN_ERR
- "lpfc_debugfs_max_disc_trc changed to %d\n",
- lpfc_debugfs_max_disc_trc);
+ pr_info("lpfc_debugfs_max_disc_trc changed to %d\n",
+ lpfc_debugfs_max_disc_trc);
}
}
@@ -2650,105 +6295,86 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
"0418 Cannot create debugfs disc trace "
"buffer\n");
- goto debug_failed;
+ goto out;
}
atomic_set(&vport->disc_trc_cnt, 0);
- snprintf(name, sizeof(name), "discovery_trace");
- vport->debug_disc_trc =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- vport->vport_debugfs_root,
- vport, &lpfc_debugfs_op_disc_trc);
- if (!vport->debug_disc_trc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0419 Cannot create debugfs "
- "discovery_trace\n");
- goto debug_failed;
- }
- snprintf(name, sizeof(name), "nodelist");
- vport->debug_nodelist =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- vport->vport_debugfs_root,
- vport, &lpfc_debugfs_op_nodelist);
- if (!vport->debug_nodelist) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0409 Can't create debugfs nodelist\n");
- goto debug_failed;
- }
+ debugfs_create_file("discovery_trace", 0644, vport->vport_debugfs_root,
+ vport, &lpfc_debugfs_op_disc_trc);
+
+ debugfs_create_file("nodelist", 0644, vport->vport_debugfs_root, vport,
+ &lpfc_debugfs_op_nodelist);
+
+ debugfs_create_file("nvmestat", 0644, vport->vport_debugfs_root, vport,
+ &lpfc_debugfs_op_nvmestat);
+
+ debugfs_create_file("scsistat", 0644, vport->vport_debugfs_root, vport,
+ &lpfc_debugfs_op_scsistat);
+
+ debugfs_create_file("ioktime", 0644, vport->vport_debugfs_root, vport,
+ &lpfc_debugfs_op_ioktime);
+
+ debugfs_create_file("hdwqstat", 0644, vport->vport_debugfs_root, vport,
+ &lpfc_debugfs_op_hdwqstat);
+
+ /*
+ * The following section is for additional directories/files for the
+ * physical port.
+ */
+
+ if (!pport_setup)
+ return;
/*
* iDiag debugfs root entry points for SLI4 device only
*/
if (phba->sli_rev < LPFC_SLI_REV4)
- goto debug_failed;
+ return;
- snprintf(name, sizeof(name), "iDiag");
if (!phba->idiag_root) {
phba->idiag_root =
- debugfs_create_dir(name, phba->hba_debugfs_root);
- if (!phba->idiag_root) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2922 Can't create idiag debugfs\n");
- goto debug_failed;
- }
+ debugfs_create_dir("iDiag", phba->hba_debugfs_root);
/* Initialize iDiag data structure */
memset(&idiag, 0, sizeof(idiag));
}
/* iDiag read PCI config space */
- snprintf(name, sizeof(name), "pciCfg");
- if (!phba->idiag_pci_cfg) {
- phba->idiag_pci_cfg =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->idiag_root, phba, &lpfc_idiag_op_pciCfg);
- if (!phba->idiag_pci_cfg) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2923 Can't create idiag debugfs\n");
- goto debug_failed;
- }
- idiag.offset.last_rd = 0;
- }
+ debugfs_create_file("pciCfg", 0644, phba->idiag_root, phba,
+ &lpfc_idiag_op_pciCfg);
+ idiag.offset.last_rd = 0;
+
+ /* iDiag PCI BAR access */
+ debugfs_create_file("barAcc", 0644, phba->idiag_root, phba,
+ &lpfc_idiag_op_barAcc);
+ idiag.offset.last_rd = 0;
/* iDiag get PCI function queue information */
- snprintf(name, sizeof(name), "queInfo");
- if (!phba->idiag_que_info) {
- phba->idiag_que_info =
- debugfs_create_file(name, S_IFREG|S_IRUGO,
- phba->idiag_root, phba, &lpfc_idiag_op_queInfo);
- if (!phba->idiag_que_info) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2924 Can't create idiag debugfs\n");
- goto debug_failed;
- }
- }
+ debugfs_create_file("queInfo", 0444, phba->idiag_root, phba,
+ &lpfc_idiag_op_queInfo);
/* iDiag access PCI function queue */
- snprintf(name, sizeof(name), "queAcc");
- if (!phba->idiag_que_acc) {
- phba->idiag_que_acc =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->idiag_root, phba, &lpfc_idiag_op_queAcc);
- if (!phba->idiag_que_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2926 Can't create idiag debugfs\n");
- goto debug_failed;
- }
- }
+ debugfs_create_file("queAcc", 0644, phba->idiag_root, phba,
+ &lpfc_idiag_op_queAcc);
/* iDiag access PCI function doorbell registers */
- snprintf(name, sizeof(name), "drbAcc");
- if (!phba->idiag_drb_acc) {
- phba->idiag_drb_acc =
- debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
- phba->idiag_root, phba, &lpfc_idiag_op_drbAcc);
- if (!phba->idiag_drb_acc) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "2927 Can't create idiag debugfs\n");
- goto debug_failed;
- }
- }
+ debugfs_create_file("drbAcc", 0644, phba->idiag_root, phba,
+ &lpfc_idiag_op_drbAcc);
+
+ /* iDiag access PCI function control registers */
+ debugfs_create_file("ctlAcc", 0644, phba->idiag_root, phba,
+ &lpfc_idiag_op_ctlAcc);
+
+ /* iDiag access mbox commands */
+ debugfs_create_file("mbxAcc", 0644, phba->idiag_root, phba,
+ &lpfc_idiag_op_mbxAcc);
-debug_failed:
+ /* iDiag extents access commands */
+ if (phba->sli4_hba.extents_in_use) {
+ debugfs_create_file("extAcc", 0644, phba->idiag_root, phba,
+ &lpfc_idiag_op_extAcc);
+ }
+out:
+ /* alloc'ed items are kfree'd in lpfc_debugfs_terminate */
return;
#endif
}
@@ -2770,98 +6396,29 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_hba *phba = vport->phba;
- if (vport->disc_trc) {
- kfree(vport->disc_trc);
- vport->disc_trc = NULL;
- }
- if (vport->debug_disc_trc) {
- debugfs_remove(vport->debug_disc_trc); /* discovery_trace */
- vport->debug_disc_trc = NULL;
- }
- if (vport->debug_nodelist) {
- debugfs_remove(vport->debug_nodelist); /* nodelist */
- vport->debug_nodelist = NULL;
- }
+ kfree(vport->disc_trc);
+ vport->disc_trc = NULL;
if (vport->vport_debugfs_root) {
debugfs_remove(vport->vport_debugfs_root); /* vportX */
vport->vport_debugfs_root = NULL;
- atomic_dec(&phba->debugfs_vport_count);
+ phba->debugfs_vport_count--;
}
- if (atomic_read(&phba->debugfs_vport_count) == 0) {
- if (phba->debug_hbqinfo) {
- debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */
- phba->debug_hbqinfo = NULL;
- }
- if (phba->debug_dumpHBASlim) {
- debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */
- phba->debug_dumpHBASlim = NULL;
- }
- if (phba->debug_dumpHostSlim) {
- debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */
- phba->debug_dumpHostSlim = NULL;
- }
- if (phba->debug_dumpData) {
- debugfs_remove(phba->debug_dumpData); /* dumpData */
- phba->debug_dumpData = NULL;
- }
+ if (!phba->debugfs_vport_count) {
+ kfree(phba->slow_ring_trc);
+ phba->slow_ring_trc = NULL;
- if (phba->debug_dumpDif) {
- debugfs_remove(phba->debug_dumpDif); /* dumpDif */
- phba->debug_dumpDif = NULL;
- }
-
- if (phba->slow_ring_trc) {
- kfree(phba->slow_ring_trc);
- phba->slow_ring_trc = NULL;
- }
- if (phba->debug_slow_ring_trc) {
- /* slow_ring_trace */
- debugfs_remove(phba->debug_slow_ring_trc);
- phba->debug_slow_ring_trc = NULL;
- }
-
- /*
- * iDiag release
- */
- if (phba->sli_rev == LPFC_SLI_REV4) {
- if (phba->idiag_drb_acc) {
- /* iDiag drbAcc */
- debugfs_remove(phba->idiag_drb_acc);
- phba->idiag_drb_acc = NULL;
- }
- if (phba->idiag_que_acc) {
- /* iDiag queAcc */
- debugfs_remove(phba->idiag_que_acc);
- phba->idiag_que_acc = NULL;
- }
- if (phba->idiag_que_info) {
- /* iDiag queInfo */
- debugfs_remove(phba->idiag_que_info);
- phba->idiag_que_info = NULL;
- }
- if (phba->idiag_pci_cfg) {
- /* iDiag pciCfg */
- debugfs_remove(phba->idiag_pci_cfg);
- phba->idiag_pci_cfg = NULL;
- }
-
- /* Finally remove the iDiag debugfs root */
- if (phba->idiag_root) {
- /* iDiag root */
- debugfs_remove(phba->idiag_root);
- phba->idiag_root = NULL;
- }
- }
+ kfree(phba->nvmeio_trc);
+ phba->nvmeio_trc = NULL;
if (phba->hba_debugfs_root) {
debugfs_remove(phba->hba_debugfs_root); /* fnX */
phba->hba_debugfs_root = NULL;
- atomic_dec(&lpfc_debugfs_hba_count);
+ lpfc_debugfs_hba_count--;
}
- if (atomic_read(&lpfc_debugfs_hba_count) == 0) {
+ if (!lpfc_debugfs_hba_count) {
debugfs_remove(lpfc_debugfs_root); /* lpfc */
lpfc_debugfs_root = NULL;
}
@@ -2869,3 +6426,49 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
#endif
return;
}
+
+/*
+ * Driver debug utility routines outside of debugfs. The debug utility
+ * routines implemented here is intended to be used in the instrumented
+ * debug driver for debugging host or port issues.
+ */
+
+/**
+ * lpfc_debug_dump_all_queues - dump all the queues with a hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This function dumps entries of all the queues asociated with the @phba.
+ **/
+void
+lpfc_debug_dump_all_queues(struct lpfc_hba *phba)
+{
+ int idx;
+
+ /*
+ * Dump Work Queues (WQs)
+ */
+ lpfc_debug_dump_wq(phba, DUMP_MBX, 0);
+ lpfc_debug_dump_wq(phba, DUMP_ELS, 0);
+ lpfc_debug_dump_wq(phba, DUMP_NVMELS, 0);
+
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
+ lpfc_debug_dump_wq(phba, DUMP_IO, idx);
+
+ lpfc_debug_dump_hdr_rq(phba);
+ lpfc_debug_dump_dat_rq(phba);
+ /*
+ * Dump Complete Queues (CQs)
+ */
+ lpfc_debug_dump_cq(phba, DUMP_MBX, 0);
+ lpfc_debug_dump_cq(phba, DUMP_ELS, 0);
+ lpfc_debug_dump_cq(phba, DUMP_NVMELS, 0);
+
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
+ lpfc_debug_dump_cq(phba, DUMP_IO, idx);
+
+ /*
+ * Dump Event Queues (EQs)
+ */
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++)
+ lpfc_debug_dump_hba_eq(phba, idx);
+}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index 6525a5e62d27..a1464f8ac331 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2007-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -36,17 +38,73 @@
/* dumpHostSlim output buffer size */
#define LPFC_DUMPHOSTSLIM_SIZE 4096
+/* dumpSLIqinfo output buffer size */
+#define LPFC_DUMPSLIQINFO_SIZE 4096
+
/* hbqinfo output buffer size */
#define LPFC_HBQINFO_SIZE 8192
+/* hdwqinfo output buffer size */
+#define LPFC_HDWQINFO_SIZE 8192
+
+/* nvmestat output buffer size */
+#define LPFC_NVMESTAT_SIZE 8192
+#define LPFC_IOKTIME_SIZE 8192
+#define LPFC_NVMEIO_TRC_SIZE 8192
+
+/* scsistat output buffer size */
+#define LPFC_SCSISTAT_SIZE 8192
+
+/* Congestion Info Buffer size */
+#define LPFC_CGN_BUF_SIZE 8192
+
+#define LPFC_DEBUG_OUT_LINE_SZ 80
+
+/*
+ * For SLI4 iDiag debugfs diagnostics tool
+ */
+
/* pciConf */
#define LPFC_PCI_CFG_BROWSE 0xffff
#define LPFC_PCI_CFG_RD_CMD_ARG 2
#define LPFC_PCI_CFG_WR_CMD_ARG 3
#define LPFC_PCI_CFG_SIZE 4096
-#define LPFC_PCI_CFG_RD_BUF_SIZE (LPFC_PCI_CFG_SIZE/2)
#define LPFC_PCI_CFG_RD_SIZE (LPFC_PCI_CFG_SIZE/4)
+#define IDIAG_PCICFG_WHERE_INDX 0
+#define IDIAG_PCICFG_COUNT_INDX 1
+#define IDIAG_PCICFG_VALUE_INDX 2
+
+/* barAcc */
+#define LPFC_PCI_BAR_BROWSE 0xffff
+#define LPFC_PCI_BAR_RD_CMD_ARG 3
+#define LPFC_PCI_BAR_WR_CMD_ARG 3
+
+#define LPFC_PCI_IF0_BAR0_SIZE (1024 * 16)
+#define LPFC_PCI_IF0_BAR1_SIZE (1024 * 128)
+#define LPFC_PCI_IF0_BAR2_SIZE (1024 * 128)
+#define LPFC_PCI_IF2_BAR0_SIZE (1024 * 32)
+
+#define LPFC_PCI_BAR_RD_BUF_SIZE 4096
+#define LPFC_PCI_BAR_RD_SIZE (LPFC_PCI_BAR_RD_BUF_SIZE/4)
+
+#define LPFC_PCI_IF0_BAR0_RD_SIZE (LPFC_PCI_IF0_BAR0_SIZE/4)
+#define LPFC_PCI_IF0_BAR1_RD_SIZE (LPFC_PCI_IF0_BAR1_SIZE/4)
+#define LPFC_PCI_IF0_BAR2_RD_SIZE (LPFC_PCI_IF0_BAR2_SIZE/4)
+#define LPFC_PCI_IF2_BAR0_RD_SIZE (LPFC_PCI_IF2_BAR0_SIZE/4)
+
+#define IDIAG_BARACC_BAR_NUM_INDX 0
+#define IDIAG_BARACC_OFF_SET_INDX 1
+#define IDIAG_BARACC_ACC_MOD_INDX 2
+#define IDIAG_BARACC_REG_VAL_INDX 2
+#define IDIAG_BARACC_BAR_SZE_INDX 3
+
+#define IDIAG_BARACC_BAR_0 0
+#define IDIAG_BARACC_BAR_1 1
+#define IDIAG_BARACC_BAR_2 2
+
+#define SINGLE_WORD 1
+
/* queue info */
#define LPFC_QUE_INFO_GET_BUF_SIZE 4096
@@ -63,23 +121,98 @@
#define LPFC_IDIAG_WQ 4
#define LPFC_IDIAG_RQ 5
-/* doorbell acc */
+#define IDIAG_QUEACC_QUETP_INDX 0
+#define IDIAG_QUEACC_QUEID_INDX 1
+#define IDIAG_QUEACC_INDEX_INDX 2
+#define IDIAG_QUEACC_COUNT_INDX 3
+#define IDIAG_QUEACC_OFFST_INDX 4
+#define IDIAG_QUEACC_VALUE_INDX 5
+
+/* doorbell register acc */
#define LPFC_DRB_ACC_ALL 0xffff
#define LPFC_DRB_ACC_RD_CMD_ARG 1
#define LPFC_DRB_ACC_WR_CMD_ARG 2
#define LPFC_DRB_ACC_BUF_SIZE 256
-#define LPFC_DRB_EQCQ 1
-#define LPFC_DRB_MQ 2
-#define LPFC_DRB_WQ 3
-#define LPFC_DRB_RQ 4
+#define LPFC_DRB_EQ 1
+#define LPFC_DRB_CQ 2
+#define LPFC_DRB_MQ 3
+#define LPFC_DRB_WQ 4
+#define LPFC_DRB_RQ 5
+
+#define LPFC_DRB_MAX 5
+
+#define IDIAG_DRBACC_REGID_INDX 0
+#define IDIAG_DRBACC_VALUE_INDX 1
+
+/* control register acc */
+#define LPFC_CTL_ACC_ALL 0xffff
+#define LPFC_CTL_ACC_RD_CMD_ARG 1
+#define LPFC_CTL_ACC_WR_CMD_ARG 2
+#define LPFC_CTL_ACC_BUF_SIZE 256
+
+#define LPFC_CTL_PORT_SEM 1
+#define LPFC_CTL_PORT_STA 2
+#define LPFC_CTL_PORT_CTL 3
+#define LPFC_CTL_PORT_ER1 4
+#define LPFC_CTL_PORT_ER2 5
+#define LPFC_CTL_PDEV_CTL 6
+
+#define LPFC_CTL_MAX 6
+
+#define IDIAG_CTLACC_REGID_INDX 0
+#define IDIAG_CTLACC_VALUE_INDX 1
+
+/* mailbox access */
+#define LPFC_MBX_DMP_ARG 4
+
+#define LPFC_MBX_ACC_BUF_SIZE 512
+#define LPFC_MBX_ACC_LBUF_SZ 128
-#define LPFC_DRB_MAX 4
+#define LPFC_MBX_DMP_MBX_WORD 0x00000001
+#define LPFC_MBX_DMP_MBX_BYTE 0x00000002
+#define LPFC_MBX_DMP_MBX_ALL (LPFC_MBX_DMP_MBX_WORD | LPFC_MBX_DMP_MBX_BYTE)
+
+#define LPFC_BSG_DMP_MBX_RD_MBX 0x00000001
+#define LPFC_BSG_DMP_MBX_RD_BUF 0x00000002
+#define LPFC_BSG_DMP_MBX_WR_MBX 0x00000004
+#define LPFC_BSG_DMP_MBX_WR_BUF 0x00000008
+#define LPFC_BSG_DMP_MBX_ALL (LPFC_BSG_DMP_MBX_RD_MBX | \
+ LPFC_BSG_DMP_MBX_RD_BUF | \
+ LPFC_BSG_DMP_MBX_WR_MBX | \
+ LPFC_BSG_DMP_MBX_WR_BUF)
+
+#define LPFC_MBX_DMP_ALL 0xffff
+#define LPFC_MBX_ALL_CMD 0xff
+
+#define IDIAG_MBXACC_MBCMD_INDX 0
+#define IDIAG_MBXACC_DPMAP_INDX 1
+#define IDIAG_MBXACC_DPCNT_INDX 2
+#define IDIAG_MBXACC_WDCNT_INDX 3
+
+/* extents access */
+#define LPFC_EXT_ACC_CMD_ARG 1
+#define LPFC_EXT_ACC_BUF_SIZE 4096
+
+#define LPFC_EXT_ACC_AVAIL 0x1
+#define LPFC_EXT_ACC_ALLOC 0x2
+#define LPFC_EXT_ACC_DRIVR 0x4
+#define LPFC_EXT_ACC_ALL (LPFC_EXT_ACC_DRIVR | \
+ LPFC_EXT_ACC_AVAIL | \
+ LPFC_EXT_ACC_ALLOC)
+
+#define IDIAG_EXTACC_EXMAP_INDX 0
#define SIZE_U8 sizeof(uint8_t)
#define SIZE_U16 sizeof(uint16_t)
#define SIZE_U32 sizeof(uint32_t)
+#define lpfc_nvmeio_data(phba, fmt, arg...) \
+ { \
+ if (phba->nvmeio_trc_on) \
+ lpfc_debugfs_nvme_trc(phba, fmt, ##arg); \
+ }
+
struct lpfc_debug {
char *i_private;
char op;
@@ -98,6 +231,13 @@ struct lpfc_debugfs_trc {
unsigned long jif;
};
+struct lpfc_debugfs_nvmeio_trc {
+ char *fmt;
+ uint16_t data1;
+ uint16_t data2;
+ uint32_t data3;
+};
+
struct lpfc_idiag_offset {
uint32_t last_rd;
};
@@ -110,6 +250,11 @@ struct lpfc_idiag_cmd {
#define LPFC_IDIAG_CMD_PCICFG_ST 0x00000003
#define LPFC_IDIAG_CMD_PCICFG_CL 0x00000004
+#define LPFC_IDIAG_CMD_BARACC_RD 0x00000008
+#define LPFC_IDIAG_CMD_BARACC_WR 0x00000009
+#define LPFC_IDIAG_CMD_BARACC_ST 0x0000000a
+#define LPFC_IDIAG_CMD_BARACC_CL 0x0000000b
+
#define LPFC_IDIAG_CMD_QUEACC_RD 0x00000011
#define LPFC_IDIAG_CMD_QUEACC_WR 0x00000012
#define LPFC_IDIAG_CMD_QUEACC_ST 0x00000013
@@ -119,6 +264,17 @@ struct lpfc_idiag_cmd {
#define LPFC_IDIAG_CMD_DRBACC_WR 0x00000022
#define LPFC_IDIAG_CMD_DRBACC_ST 0x00000023
#define LPFC_IDIAG_CMD_DRBACC_CL 0x00000024
+
+#define LPFC_IDIAG_CMD_CTLACC_RD 0x00000031
+#define LPFC_IDIAG_CMD_CTLACC_WR 0x00000032
+#define LPFC_IDIAG_CMD_CTLACC_ST 0x00000033
+#define LPFC_IDIAG_CMD_CTLACC_CL 0x00000034
+
+#define LPFC_IDIAG_CMD_MBXACC_DP 0x00000041
+#define LPFC_IDIAG_BSG_MBXACC_DP 0x00000042
+
+#define LPFC_IDIAG_CMD_EXTACC_RD 0x00000051
+
uint32_t data[LPFC_IDIAG_CMD_DATA_SIZE];
};
@@ -128,8 +284,30 @@ struct lpfc_idiag {
struct lpfc_idiag_offset offset;
void *ptr_private;
};
+
+#define MAX_DEBUGFS_RX_INFO_SIZE (128 * LPFC_MAX_RXMONITOR_ENTRY)
+struct lpfc_rx_monitor_debug {
+ char *i_private;
+ char *buffer;
+};
+
+#else
+
+#define lpfc_nvmeio_data(phba, fmt, arg...) \
+ no_printk(fmt, ##arg)
+
#endif
+/* multixripool output buffer size */
+#define LPFC_DUMP_MULTIXRIPOOL_SIZE 8192
+
+enum {
+ DUMP_IO,
+ DUMP_MBX,
+ DUMP_ELS,
+ DUMP_NVMELS,
+};
+
/* Mask for discovery_trace */
#define LPFC_DISC_TRC_ELS_CMD 0x1 /* Trace ELS commands */
#define LPFC_DISC_TRC_ELS_RSP 0x2 /* Trace ELS response */
@@ -146,3 +324,390 @@ struct lpfc_idiag {
#define LPFC_DISC_TRC_DISCOVERY 0xef /* common mask for general
* discovery */
#endif /* H_LPFC_DEBUG_FS */
+
+enum {
+ writeGuard = 1,
+ writeApp,
+ writeRef,
+ readGuard,
+ readApp,
+ readRef,
+ InjErrLBA,
+ InjErrNPortID,
+ InjErrWWPN,
+};
+
+/*
+ * Driver debug utility routines outside of debugfs. The debug utility
+ * routines implemented here is intended to be used in the instrumented
+ * debug driver for debugging host or port issues.
+ */
+
+/**
+ * lpfc_debug_dump_qe - dump an specific entry from a queue
+ * @q: Pointer to the queue descriptor.
+ * @idx: Index to the entry on the queue.
+ *
+ * This function dumps an entry indexed by @idx from a queue specified by the
+ * queue descriptor @q.
+ **/
+static void
+lpfc_debug_dump_qe(struct lpfc_queue *q, uint32_t idx)
+{
+ char line_buf[LPFC_LBUF_SZ];
+ int i, esize, qe_word_cnt, len;
+ uint32_t *pword;
+
+ /* sanity checks */
+ if (!q)
+ return;
+ if (idx >= q->entry_count)
+ return;
+
+ esize = q->entry_size;
+ qe_word_cnt = esize / sizeof(uint32_t);
+ pword = lpfc_sli4_qe(q, idx);
+
+ len = 0;
+ len += scnprintf(line_buf+len, LPFC_LBUF_SZ-len, "QE[%04d]: ", idx);
+ if (qe_word_cnt > 8)
+ printk(KERN_ERR "%s\n", line_buf);
+
+ for (i = 0; i < qe_word_cnt; i++) {
+ if (!(i % 8)) {
+ if (i != 0)
+ printk(KERN_ERR "%s\n", line_buf);
+ if (qe_word_cnt > 8) {
+ len = 0;
+ memset(line_buf, 0, LPFC_LBUF_SZ);
+ len += scnprintf(line_buf+len, LPFC_LBUF_SZ-len,
+ "%03d: ", i);
+ }
+ }
+ len += scnprintf(line_buf+len, LPFC_LBUF_SZ-len, "%08x ",
+ ((uint32_t)*pword) & 0xffffffff);
+ pword++;
+ }
+ if (qe_word_cnt <= 8 || (i - 1) % 8)
+ printk(KERN_ERR "%s\n", line_buf);
+}
+
+/**
+ * lpfc_debug_dump_q - dump all entries from an specific queue
+ * @q: Pointer to the queue descriptor.
+ *
+ * This function dumps all entries from a queue specified by the queue
+ * descriptor @q.
+ **/
+static inline void
+lpfc_debug_dump_q(struct lpfc_queue *q)
+{
+ int idx, entry_count;
+
+ /* sanity check */
+ if (!q)
+ return;
+
+ dev_printk(KERN_ERR, &(((q->phba))->pcidev)->dev,
+ "%d: [qid:%d, type:%d, subtype:%d, "
+ "qe_size:%d, qe_count:%d, "
+ "host_index:%d, port_index:%d]\n",
+ (q->phba)->brd_no,
+ q->queue_id, q->type, q->subtype,
+ q->entry_size, q->entry_count,
+ q->host_index, q->hba_index);
+ entry_count = q->entry_count;
+ for (idx = 0; idx < entry_count; idx++)
+ lpfc_debug_dump_qe(q, idx);
+ printk(KERN_ERR "\n");
+}
+
+/**
+ * lpfc_debug_dump_wq - dump all entries from the fcp or nvme work queue
+ * @phba: Pointer to HBA context object.
+ * @wqidx: Index to a FCP or NVME work queue.
+ *
+ * This function dumps all entries from a FCP or NVME work queue specified
+ * by the wqidx.
+ **/
+static inline void
+lpfc_debug_dump_wq(struct lpfc_hba *phba, int qtype, int wqidx)
+{
+ struct lpfc_queue *wq;
+ char *qtypestr;
+
+ if (qtype == DUMP_IO) {
+ wq = phba->sli4_hba.hdwq[wqidx].io_wq;
+ qtypestr = "IO";
+ } else if (qtype == DUMP_MBX) {
+ wq = phba->sli4_hba.mbx_wq;
+ qtypestr = "MBX";
+ } else if (qtype == DUMP_ELS) {
+ wq = phba->sli4_hba.els_wq;
+ qtypestr = "ELS";
+ } else if (qtype == DUMP_NVMELS) {
+ wq = phba->sli4_hba.nvmels_wq;
+ qtypestr = "NVMELS";
+ } else
+ return;
+
+ if (qtype == DUMP_IO)
+ pr_err("%s WQ: WQ[Idx:%d|Qid:%d]\n",
+ qtypestr, wqidx, wq->queue_id);
+ else
+ pr_err("%s WQ: WQ[Qid:%d]\n",
+ qtypestr, wq->queue_id);
+
+ lpfc_debug_dump_q(wq);
+}
+
+/**
+ * lpfc_debug_dump_cq - dump all entries from a fcp or nvme work queue's
+ * cmpl queue
+ * @phba: Pointer to HBA context object.
+ * @wqidx: Index to a FCP work queue.
+ *
+ * This function dumps all entries from a FCP or NVME completion queue
+ * which is associated to the work queue specified by the @wqidx.
+ **/
+static inline void
+lpfc_debug_dump_cq(struct lpfc_hba *phba, int qtype, int wqidx)
+{
+ struct lpfc_queue *wq, *cq, *eq;
+ char *qtypestr;
+ int eqidx;
+
+ /* io wq and cq are 1:1, thus same indexes */
+ eq = NULL;
+
+ if (qtype == DUMP_IO) {
+ wq = phba->sli4_hba.hdwq[wqidx].io_wq;
+ cq = phba->sli4_hba.hdwq[wqidx].io_cq;
+ qtypestr = "IO";
+ } else if (qtype == DUMP_MBX) {
+ wq = phba->sli4_hba.mbx_wq;
+ cq = phba->sli4_hba.mbx_cq;
+ qtypestr = "MBX";
+ } else if (qtype == DUMP_ELS) {
+ wq = phba->sli4_hba.els_wq;
+ cq = phba->sli4_hba.els_cq;
+ qtypestr = "ELS";
+ } else if (qtype == DUMP_NVMELS) {
+ wq = phba->sli4_hba.nvmels_wq;
+ cq = phba->sli4_hba.nvmels_cq;
+ qtypestr = "NVMELS";
+ } else
+ return;
+
+ for (eqidx = 0; eqidx < phba->cfg_hdw_queue; eqidx++) {
+ eq = phba->sli4_hba.hdwq[eqidx].hba_eq;
+ if (cq->assoc_qid == eq->queue_id)
+ break;
+ }
+ if (eqidx == phba->cfg_hdw_queue) {
+ pr_err("Couldn't find EQ for CQ. Using EQ[0]\n");
+ eqidx = 0;
+ eq = phba->sli4_hba.hdwq[0].hba_eq;
+ }
+
+ if (qtype == DUMP_IO)
+ pr_err("%s CQ: WQ[Idx:%d|Qid%d]->CQ[Idx%d|Qid%d]"
+ "->EQ[Idx:%d|Qid:%d]:\n",
+ qtypestr, wqidx, wq->queue_id, wqidx, cq->queue_id,
+ eqidx, eq->queue_id);
+ else
+ pr_err("%s CQ: WQ[Qid:%d]->CQ[Qid:%d]"
+ "->EQ[Idx:%d|Qid:%d]:\n",
+ qtypestr, wq->queue_id, cq->queue_id,
+ eqidx, eq->queue_id);
+
+ lpfc_debug_dump_q(cq);
+}
+
+/**
+ * lpfc_debug_dump_hba_eq - dump all entries from a fcp work queue's evt queue
+ * @phba: Pointer to HBA context object.
+ * @fcp_wqidx: Index to a FCP work queue.
+ *
+ * This function dumps all entries from a FCP event queue which is
+ * associated to the FCP work queue specified by the @fcp_wqidx.
+ **/
+static inline void
+lpfc_debug_dump_hba_eq(struct lpfc_hba *phba, int qidx)
+{
+ struct lpfc_queue *qp;
+
+ qp = phba->sli4_hba.hdwq[qidx].hba_eq;
+
+ pr_err("EQ[Idx:%d|Qid:%d]\n", qidx, qp->queue_id);
+
+ lpfc_debug_dump_q(qp);
+}
+
+/**
+ * lpfc_debug_dump_dat_rq - dump all entries from the receive data queue
+ * @phba: Pointer to HBA context object.
+ *
+ * This function dumps all entries from the receive data queue.
+ **/
+static inline void
+lpfc_debug_dump_dat_rq(struct lpfc_hba *phba)
+{
+ printk(KERN_ERR "DAT RQ: RQ[Qid:%d]\n",
+ phba->sli4_hba.dat_rq->queue_id);
+ lpfc_debug_dump_q(phba->sli4_hba.dat_rq);
+}
+
+/**
+ * lpfc_debug_dump_hdr_rq - dump all entries from the receive header queue
+ * @phba: Pointer to HBA context object.
+ *
+ * This function dumps all entries from the receive header queue.
+ **/
+static inline void
+lpfc_debug_dump_hdr_rq(struct lpfc_hba *phba)
+{
+ printk(KERN_ERR "HDR RQ: RQ[Qid:%d]\n",
+ phba->sli4_hba.hdr_rq->queue_id);
+ lpfc_debug_dump_q(phba->sli4_hba.hdr_rq);
+}
+
+/**
+ * lpfc_debug_dump_wq_by_id - dump all entries from a work queue by queue id
+ * @phba: Pointer to HBA context object.
+ * @qid: Work queue identifier.
+ *
+ * This function dumps all entries from a work queue identified by the queue
+ * identifier.
+ **/
+static inline void
+lpfc_debug_dump_wq_by_id(struct lpfc_hba *phba, int qid)
+{
+ int wq_idx;
+
+ for (wq_idx = 0; wq_idx < phba->cfg_hdw_queue; wq_idx++)
+ if (phba->sli4_hba.hdwq[wq_idx].io_wq->queue_id == qid)
+ break;
+ if (wq_idx < phba->cfg_hdw_queue) {
+ pr_err("IO WQ[Idx:%d|Qid:%d]\n", wq_idx, qid);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[wq_idx].io_wq);
+ return;
+ }
+
+ if (phba->sli4_hba.els_wq->queue_id == qid) {
+ pr_err("ELS WQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.els_wq);
+ return;
+ }
+
+ if (phba->sli4_hba.nvmels_wq->queue_id == qid) {
+ pr_err("NVME LS WQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.nvmels_wq);
+ }
+}
+
+/**
+ * lpfc_debug_dump_mq_by_id - dump all entries from a mbox queue by queue id
+ * @phba: Pointer to HBA context object.
+ * @qid: Mbox work queue identifier.
+ *
+ * This function dumps all entries from a mbox work queue identified by the
+ * queue identifier.
+ **/
+static inline void
+lpfc_debug_dump_mq_by_id(struct lpfc_hba *phba, int qid)
+{
+ if (phba->sli4_hba.mbx_wq->queue_id == qid) {
+ printk(KERN_ERR "MBX WQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.mbx_wq);
+ }
+}
+
+/**
+ * lpfc_debug_dump_rq_by_id - dump all entries from a receive queue by queue id
+ * @phba: Pointer to HBA context object.
+ * @qid: Receive queue identifier.
+ *
+ * This function dumps all entries from a receive queue identified by the
+ * queue identifier.
+ **/
+static inline void
+lpfc_debug_dump_rq_by_id(struct lpfc_hba *phba, int qid)
+{
+ if (phba->sli4_hba.hdr_rq->queue_id == qid) {
+ printk(KERN_ERR "HDR RQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.hdr_rq);
+ return;
+ }
+ if (phba->sli4_hba.dat_rq->queue_id == qid) {
+ printk(KERN_ERR "DAT RQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.dat_rq);
+ }
+}
+
+/**
+ * lpfc_debug_dump_cq_by_id - dump all entries from a cmpl queue by queue id
+ * @phba: Pointer to HBA context object.
+ * @qid: Complete queue identifier.
+ *
+ * This function dumps all entries from a complete queue identified by the
+ * queue identifier.
+ **/
+static inline void
+lpfc_debug_dump_cq_by_id(struct lpfc_hba *phba, int qid)
+{
+ int cq_idx;
+
+ for (cq_idx = 0; cq_idx < phba->cfg_hdw_queue; cq_idx++)
+ if (phba->sli4_hba.hdwq[cq_idx].io_cq->queue_id == qid)
+ break;
+
+ if (cq_idx < phba->cfg_hdw_queue) {
+ pr_err("IO CQ[Idx:%d|Qid:%d]\n", cq_idx, qid);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[cq_idx].io_cq);
+ return;
+ }
+
+ if (phba->sli4_hba.els_cq->queue_id == qid) {
+ pr_err("ELS CQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.els_cq);
+ return;
+ }
+
+ if (phba->sli4_hba.nvmels_cq->queue_id == qid) {
+ pr_err("NVME LS CQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.nvmels_cq);
+ return;
+ }
+
+ if (phba->sli4_hba.mbx_cq->queue_id == qid) {
+ pr_err("MBX CQ[Qid:%d]\n", qid);
+ lpfc_debug_dump_q(phba->sli4_hba.mbx_cq);
+ }
+}
+
+/**
+ * lpfc_debug_dump_eq_by_id - dump all entries from an event queue by queue id
+ * @phba: Pointer to HBA context object.
+ * @qid: Complete queue identifier.
+ *
+ * This function dumps all entries from an event queue identified by the
+ * queue identifier.
+ **/
+static inline void
+lpfc_debug_dump_eq_by_id(struct lpfc_hba *phba, int qid)
+{
+ int eq_idx;
+
+ for (eq_idx = 0; eq_idx < phba->cfg_hdw_queue; eq_idx++)
+ if (phba->sli4_hba.hdwq[eq_idx].hba_eq->queue_id == qid)
+ break;
+
+ if (eq_idx < phba->cfg_hdw_queue) {
+ printk(KERN_ERR "FCP EQ[Idx:%d|Qid:%d]\n", eq_idx, qid);
+ lpfc_debug_dump_q(phba->sli4_hba.hdwq[eq_idx].hba_eq);
+ return;
+ }
+}
+
+void lpfc_debug_dump_all_queues(struct lpfc_hba *);
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
index 1d84b63fccad..51cb8571c049 100644
--- a/drivers/scsi/lpfc/lpfc_disc.h
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2013 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -39,6 +41,7 @@ enum lpfc_work_type {
LPFC_EVT_DEV_LOSS,
LPFC_EVT_FASTPATH_MGMT_EVT,
LPFC_EVT_RESET_HBA,
+ LPFC_EVT_RECOVER_PORT
};
/* structure used to queue event to the discovery tasklet */
@@ -74,11 +77,32 @@ struct lpfc_node_rrqs {
unsigned long xri_bitmap[XRI_BITMAP_ULONGS];
};
+enum lpfc_fc4_xpt_flags {
+ NLP_XPT_REGD = 0x1,
+ SCSI_XPT_REGD = 0x2,
+ NVME_XPT_REGD = 0x4,
+ NVME_XPT_UNREG_WAIT = 0x8,
+ NLP_XPT_HAS_HH = 0x10
+};
+
+enum lpfc_nlp_save_flags { /* mask bits */
+ /* devloss occurred during recovery */
+ NLP_IN_RECOV_POST_DEV_LOSS,
+ /* wait for outstanding LOGO to cmpl */
+ NLP_WAIT_FOR_LOGO,
+ /* wait for outstanding DA_ID to finish */
+ NLP_WAIT_FOR_DA_ID
+};
+
struct lpfc_nodelist {
struct list_head nlp_listp;
+ struct serv_parm fc_sparam; /* buffer for service params */
struct lpfc_name nlp_portname;
struct lpfc_name nlp_nodename;
- uint32_t nlp_flag; /* entry flags */
+
+ spinlock_t lock; /* Node management lock */
+
+ unsigned long nlp_flag; /* entry flags */
uint32_t nlp_DID; /* FC D_ID of entry */
uint32_t nlp_last_elscmd; /* Last ELS cmd sent */
uint16_t nlp_type;
@@ -86,6 +110,18 @@ struct lpfc_nodelist {
#define NLP_FABRIC 0x4 /* entry rep a Fabric entity */
#define NLP_FCP_TARGET 0x8 /* entry is an FCP target */
#define NLP_FCP_INITIATOR 0x10 /* entry is an FCP Initiator */
+#define NLP_NVME_TARGET 0x20 /* entry is a NVME Target */
+#define NLP_NVME_INITIATOR 0x40 /* entry is a NVME Initiator */
+#define NLP_NVME_DISCOVERY 0x80 /* entry has NVME disc srvc */
+
+ uint16_t nlp_fc4_type; /* FC types node supports. */
+ /* Assigned from GID_FF, only
+ * FCP (0x8) and NVME (0x28)
+ * supported.
+ */
+#define NLP_FC4_NONE 0x0
+#define NLP_FC4_FCP 0x1 /* FC4 Type FCP (value x8)) */
+#define NLP_FC4_NVME 0x2 /* FC4 TYPE NVME (value x28) */
uint16_t nlp_rpi;
uint16_t nlp_state; /* state transition indicator */
@@ -98,27 +134,41 @@ struct lpfc_nodelist {
uint8_t nlp_retry; /* used for ELS retries */
uint8_t nlp_fcp_info; /* class info, bits 0-3 */
#define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */
-
- uint16_t nlp_usg_map; /* ndlp management usage bitmap */
-#define NLP_USG_NODE_ACT_BIT 0x1 /* Indicate ndlp is actively used */
-#define NLP_USG_IACT_REQ_BIT 0x2 /* Request to inactivate ndlp */
-#define NLP_USG_FREE_REQ_BIT 0x4 /* Request to invoke ndlp memory free */
-#define NLP_USG_FREE_ACK_BIT 0x8 /* Indicate ndlp memory free invoked */
+ u8 nlp_nvme_info; /* NVME NSLER Support */
+ uint8_t vmid_support; /* destination VMID support */
+#define NLP_NVME_NSLER 0x1 /* NVME NSLER device */
struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */
struct lpfc_hba *phba;
- struct fc_rport *rport; /* Corresponding FC transport
- port structure */
+ struct fc_rport *rport; /* scsi_transport_fc port structure */
+ struct lpfc_nvme_rport *nrport; /* nvme transport rport struct. */
struct lpfc_vport *vport;
struct lpfc_work_evt els_retry_evt;
struct lpfc_work_evt dev_loss_evt;
+ struct lpfc_work_evt recovery_evt;
struct kref kref;
atomic_t cmd_pending;
uint32_t cmd_qdepth;
unsigned long last_change_time;
- struct lpfc_node_rrqs active_rrqs;
- struct lpfc_scsicmd_bkt *lat_data; /* Latency data */
+ unsigned long *active_rrqs_xri_bitmap;
+ uint32_t fc4_prli_sent;
+
+ /* flags to keep ndlp alive until special conditions are met */
+ unsigned long save_flags;
+
+ enum lpfc_fc4_xpt_flags fc4_xpt_flags;
+
+ uint32_t nvme_fb_size; /* NVME target's supported byte cnt */
+#define NVME_FB_BIT_SHIFT 9 /* PRLI Rsp first burst in 512B units. */
+ uint32_t nlp_defer_did;
+
+ /* These wait objects are NPIV specific. These IOs must complete
+ * synchronously.
+ */
+ wait_queue_head_t *logo_waitq;
+ wait_queue_head_t *da_id_waitq;
};
+
struct lpfc_node_rrq {
struct list_head list;
uint16_t xritag;
@@ -126,59 +176,43 @@ struct lpfc_node_rrq {
uint16_t rxid;
uint32_t nlp_DID; /* FC D_ID of entry */
struct lpfc_vport *vport;
- struct lpfc_nodelist *ndlp;
unsigned long rrq_stop_time;
};
-/* Defines for nlp_flag (uint32) */
-#define NLP_IGNR_REG_CMPL 0x00000001 /* Rcvd rscn before we cmpl reg login */
-#define NLP_REG_LOGIN_SEND 0x00000002 /* sent reglogin to adapter */
-#define NLP_PLOGI_SND 0x00000020 /* sent PLOGI request for this entry */
-#define NLP_PRLI_SND 0x00000040 /* sent PRLI request for this entry */
-#define NLP_ADISC_SND 0x00000080 /* sent ADISC request for this entry */
-#define NLP_LOGO_SND 0x00000100 /* sent LOGO request for this entry */
-#define NLP_RNID_SND 0x00000400 /* sent RNID request for this entry */
-#define NLP_ELS_SND_MASK 0x000007e0 /* sent ELS request for this entry */
-#define NLP_DEFER_RM 0x00010000 /* Remove this ndlp if no longer used */
-#define NLP_DELAY_TMO 0x00020000 /* delay timeout is running for node */
-#define NLP_NPR_2B_DISC 0x00040000 /* node is included in num_disc_nodes */
-#define NLP_RCV_PLOGI 0x00080000 /* Rcv'ed PLOGI from remote system */
-#define NLP_LOGO_ACC 0x00100000 /* Process LOGO after ACC completes */
-#define NLP_TGT_NO_SCSIID 0x00200000 /* good PRLI but no binding for scsid */
-#define NLP_ACC_REGLOGIN 0x01000000 /* Issue Reg Login after successful
+#define lpfc_ndlp_check_qdepth(phba, ndlp) \
+ (ndlp->cmd_qdepth < phba->sli4_hba.max_cfg_param.max_xri)
+
+/* nlp_flag mask bits */
+enum lpfc_nlp_flag {
+ NLP_IGNR_REG_CMPL = 0, /* Rcvd rscn before we cmpl reg login */
+ NLP_REG_LOGIN_SEND = 1, /* sent reglogin to adapter */
+ NLP_SUPPRESS_RSP = 4, /* Remote NPort supports suppress rsp */
+ NLP_PLOGI_SND = 5, /* sent PLOGI request for this entry */
+ NLP_PRLI_SND = 6, /* sent PRLI request for this entry */
+ NLP_ADISC_SND = 7, /* sent ADISC request for this entry */
+ NLP_LOGO_SND = 8, /* sent LOGO request for this entry */
+ NLP_RNID_SND = 10, /* sent RNID request for this entry */
+ NLP_NVMET_RECOV = 12, /* NVMET auditing node for recovery. */
+ NLP_UNREG_INP = 15, /* UNREG_RPI cmd is in progress */
+ NLP_DROPPED = 16, /* Init ref count has been dropped */
+ NLP_DELAY_TMO = 17, /* delay timeout is running for node */
+ NLP_NPR_2B_DISC = 18, /* node is included in num_disc_nodes */
+ NLP_RCV_PLOGI = 19, /* Rcv'ed PLOGI from remote system */
+ NLP_LOGO_ACC = 20, /* Process LOGO after ACC completes */
+ NLP_TGT_NO_SCSIID = 21, /* good PRLI but no binding for scsid */
+ NLP_ISSUE_LOGO = 22, /* waiting to issue a LOGO */
+ NLP_IN_DEV_LOSS = 23, /* devloss in progress */
+ NLP_ACC_REGLOGIN = 24, /* Issue Reg Login after successful
ACC */
-#define NLP_NPR_ADISC 0x02000000 /* Issue ADISC when dq'ed from
+ NLP_NPR_ADISC = 25, /* Issue ADISC when dq'ed from
NPR list */
-#define NLP_RM_DFLT_RPI 0x04000000 /* need to remove leftover dflt RPI */
-#define NLP_NODEV_REMOVE 0x08000000 /* Defer removal till discovery ends */
-#define NLP_TARGET_REMOVE 0x10000000 /* Target remove in process */
-#define NLP_SC_REQ 0x20000000 /* Target requires authentication */
-#define NLP_RPI_REGISTERED 0x80000000 /* nlp_rpi is valid */
-
-/* ndlp usage management macros */
-#define NLP_CHK_NODE_ACT(ndlp) (((ndlp)->nlp_usg_map \
- & NLP_USG_NODE_ACT_BIT) \
- && \
- !((ndlp)->nlp_usg_map \
- & NLP_USG_FREE_ACK_BIT))
-#define NLP_SET_NODE_ACT(ndlp) ((ndlp)->nlp_usg_map \
- |= NLP_USG_NODE_ACT_BIT)
-#define NLP_INT_NODE_ACT(ndlp) ((ndlp)->nlp_usg_map \
- = NLP_USG_NODE_ACT_BIT)
-#define NLP_CLR_NODE_ACT(ndlp) ((ndlp)->nlp_usg_map \
- &= ~NLP_USG_NODE_ACT_BIT)
-#define NLP_CHK_IACT_REQ(ndlp) ((ndlp)->nlp_usg_map \
- & NLP_USG_IACT_REQ_BIT)
-#define NLP_SET_IACT_REQ(ndlp) ((ndlp)->nlp_usg_map \
- |= NLP_USG_IACT_REQ_BIT)
-#define NLP_CHK_FREE_REQ(ndlp) ((ndlp)->nlp_usg_map \
- & NLP_USG_FREE_REQ_BIT)
-#define NLP_SET_FREE_REQ(ndlp) ((ndlp)->nlp_usg_map \
- |= NLP_USG_FREE_REQ_BIT)
-#define NLP_CHK_FREE_ACK(ndlp) ((ndlp)->nlp_usg_map \
- & NLP_USG_FREE_ACK_BIT)
-#define NLP_SET_FREE_ACK(ndlp) ((ndlp)->nlp_usg_map \
- |= NLP_USG_FREE_ACK_BIT)
+ NLP_RM_DFLT_RPI = 26, /* need to remove leftover dflt RPI */
+ NLP_NODEV_REMOVE = 27, /* Defer removal till discovery ends */
+ NLP_FLOGI_DFR_ACC = 28, /* FLOGI LS_ACC was Deferred */
+ NLP_SC_REQ = 29, /* Target requires authentication */
+ NLP_FIRSTBURST = 30, /* Target supports FirstBurst */
+ NLP_RPI_REGISTERED = 31 /* nlp_rpi is valid */
+};
/* There are 4 different double linked lists nodelist entries can reside on.
* The Port Login (PLOGI) list and Address Discovery (ADISC) list are used
@@ -201,10 +235,11 @@ struct lpfc_node_rrq {
#define NLP_STE_ADISC_ISSUE 0x2 /* ADISC was sent to NL_PORT */
#define NLP_STE_REG_LOGIN_ISSUE 0x3 /* REG_LOGIN was issued for NL_PORT */
#define NLP_STE_PRLI_ISSUE 0x4 /* PRLI was sent to NL_PORT */
-#define NLP_STE_UNMAPPED_NODE 0x5 /* PRLI completed from NL_PORT */
-#define NLP_STE_MAPPED_NODE 0x6 /* Identified as a FCP Target */
-#define NLP_STE_NPR_NODE 0x7 /* NPort disappeared */
-#define NLP_STE_MAX_STATE 0x8
+#define NLP_STE_LOGO_ISSUE 0x5 /* LOGO was sent to NL_PORT */
+#define NLP_STE_UNMAPPED_NODE 0x6 /* PRLI completed from NL_PORT */
+#define NLP_STE_MAPPED_NODE 0x7 /* Identified as a FCP Target */
+#define NLP_STE_NPR_NODE 0x8 /* NPort disappeared */
+#define NLP_STE_MAX_STATE 0x9
#define NLP_STE_FREED_NODE 0xff /* node entry was freed to MEM_NLP */
/* For UNUSED_NODE state, the node has just been allocated.
@@ -262,4 +297,4 @@ struct lpfc_node_rrq {
#define NLP_EVT_DEVICE_RM 0xb /* Device not found in NS / ALPAmap */
#define NLP_EVT_DEVICE_RECOVERY 0xc /* Device existence unknown */
#define NLP_EVT_MAX_EVENT 0xd
-
+#define NLP_EVT_NOTHING_PENDING 0xff
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 32a084534f3e..02b6d31b9ad9 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -23,11 +25,14 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <uapi/scsi/fc/fc_fs.h>
+#include <uapi/scsi/fc/fc_els.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -51,9 +56,18 @@ static int lpfc_issue_els_fdisc(struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp, uint8_t retry);
static int lpfc_issue_fabric_iocb(struct lpfc_hba *phba,
struct lpfc_iocbq *iocb);
+static void lpfc_cmpl_els_edc(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb);
+static void lpfc_cmpl_els_uvem(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *);
static int lpfc_max_els_tries = 3;
+static void lpfc_init_cs_ctl_bitmap(struct lpfc_vport *vport);
+static void lpfc_vmid_set_cs_ctl_range(struct lpfc_vport *vport, u32 min, u32 max);
+static void lpfc_vmid_put_cs_ctl(struct lpfc_vport *vport, u32 ctcl_vmid);
+
/**
* lpfc_els_chk_latt - Check host link attention event for a vport
* @vport: pointer to a host virtual N_Port data structure.
@@ -79,7 +93,6 @@ static int lpfc_max_els_tries = 3;
int
lpfc_els_chk_latt(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
uint32_t ha_copy;
@@ -96,7 +109,7 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
return 0;
/* Pending Link Event during Discovery */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0237 Pending Link Event during "
"Discovery: State x%x\n",
phba->pport->port_state);
@@ -107,9 +120,7 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
* will cleanup any left over in-progress discovery
* events.
*/
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_ABORT_DISCOVERY;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_ABORT_DISCOVERY, &vport->fc_flag);
if (phba->link_state != LPFC_CLEAR_LA)
lpfc_issue_clear_la(phba, vport);
@@ -117,12 +128,21 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
return 1;
}
+static bool lpfc_is_els_acc_rsp(struct lpfc_dmabuf *buf)
+{
+ struct fc_els_ls_acc *rsp = buf->virt;
+
+ if (rsp && rsp->la_cmd == ELS_LS_ACC)
+ return true;
+ return false;
+}
+
/**
* lpfc_prep_els_iocb - Allocate and prepare a lpfc iocb data structure
* @vport: pointer to a host virtual N_Port data structure.
- * @expectRsp: flag indicating whether response is expected.
- * @cmdSize: size of the ELS command.
- * @retry: number of retries to the command IOCB when it fails.
+ * @expect_rsp: flag indicating whether response is expected.
+ * @cmd_size: size of the ELS command.
+ * @retry: number of retries to the command when it fails.
* @ndlp: pointer to a node-list data structure.
* @did: destination identifier.
* @elscmd: the ELS command code.
@@ -138,7 +158,7 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
* Buffer Descriptor Entries (BDEs), allocates buffers for both command
* payload and response payload (if expected). The reference count on the
* ndlp is incremented by 1 and the reference to the ndlp is put into
- * context1 of the IOCB data structure for this IOCB to hold the ndlp
+ * ndlp of the IOCB data structure for this IOCB to hold the ndlp
* reference for the command's callback function to access later.
*
* Return code
@@ -146,61 +166,57 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
* NULL - when els iocb data structure allocation/preparation failed
**/
struct lpfc_iocbq *
-lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
- uint16_t cmdSize, uint8_t retry,
- struct lpfc_nodelist *ndlp, uint32_t did,
- uint32_t elscmd)
+lpfc_prep_els_iocb(struct lpfc_vport *vport, u8 expect_rsp,
+ u16 cmd_size, u8 retry,
+ struct lpfc_nodelist *ndlp, u32 did,
+ u32 elscmd)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *elsiocb;
- struct lpfc_dmabuf *pcmd, *prsp, *pbuflist;
- struct ulp_bde64 *bpl;
- IOCB_t *icmd;
-
+ struct lpfc_dmabuf *pcmd, *prsp, *pbuflist, *bmp;
+ struct ulp_bde64_le *bpl;
+ u32 timeout = 0;
if (!lpfc_is_link_up(phba))
return NULL;
/* Allocate buffer for command iocb */
elsiocb = lpfc_sli_get_iocbq(phba);
-
- if (elsiocb == NULL)
+ if (!elsiocb)
return NULL;
/*
* If this command is for fabric controller and HBA running
* in FIP mode send FLOGI, FDISC and LOGO as FIP frames.
*/
- if ((did == Fabric_DID) &&
- (phba->hba_flag & HBA_FIP_SUPPORT) &&
- ((elscmd == ELS_CMD_FLOGI) ||
- (elscmd == ELS_CMD_FDISC) ||
- (elscmd == ELS_CMD_LOGO)))
+ if (did == Fabric_DID &&
+ test_bit(HBA_FIP_SUPPORT, &phba->hba_flag) &&
+ (elscmd == ELS_CMD_FLOGI ||
+ elscmd == ELS_CMD_FDISC ||
+ elscmd == ELS_CMD_LOGO))
switch (elscmd) {
case ELS_CMD_FLOGI:
- elsiocb->iocb_flag |=
- ((LPFC_ELS_ID_FLOGI << LPFC_FIP_ELS_ID_SHIFT)
- & LPFC_FIP_ELS_ID_MASK);
- break;
+ elsiocb->cmd_flag |=
+ ((LPFC_ELS_ID_FLOGI << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
case ELS_CMD_FDISC:
- elsiocb->iocb_flag |=
- ((LPFC_ELS_ID_FDISC << LPFC_FIP_ELS_ID_SHIFT)
- & LPFC_FIP_ELS_ID_MASK);
- break;
+ elsiocb->cmd_flag |=
+ ((LPFC_ELS_ID_FDISC << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
case ELS_CMD_LOGO:
- elsiocb->iocb_flag |=
- ((LPFC_ELS_ID_LOGO << LPFC_FIP_ELS_ID_SHIFT)
- & LPFC_FIP_ELS_ID_MASK);
- break;
+ elsiocb->cmd_flag |=
+ ((LPFC_ELS_ID_LOGO << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
}
else
- elsiocb->iocb_flag &= ~LPFC_FIP_ELS_ID_MASK;
-
- icmd = &elsiocb->iocb;
+ elsiocb->cmd_flag &= ~LPFC_FIP_ELS_ID_MASK;
/* fill in BDEs for command */
/* Allocate buffer for command payload */
- pcmd = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ pcmd = kmalloc(sizeof(*pcmd), GFP_KERNEL);
if (pcmd)
pcmd->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &pcmd->phys);
if (!pcmd || !pcmd->virt)
@@ -209,19 +225,20 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
INIT_LIST_HEAD(&pcmd->list);
/* Allocate buffer for response payload */
- if (expectRsp) {
- prsp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (expect_rsp) {
+ prsp = kmalloc(sizeof(*prsp), GFP_KERNEL);
if (prsp)
prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
&prsp->phys);
if (!prsp || !prsp->virt)
goto els_iocb_free_prsp_exit;
INIT_LIST_HEAD(&prsp->list);
- } else
+ } else {
prsp = NULL;
+ }
/* Allocate buffer for Buffer ptr list */
- pbuflist = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ pbuflist = kmalloc(sizeof(*pbuflist), GFP_KERNEL);
if (pbuflist)
pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
&pbuflist->phys);
@@ -230,83 +247,76 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
INIT_LIST_HEAD(&pbuflist->list);
- icmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys);
- icmd->un.elsreq64.bdl.addrLow = putPaddrLow(pbuflist->phys);
- icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- icmd->un.elsreq64.remoteID = did; /* DID */
- if (expectRsp) {
- icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));
- icmd->ulpCommand = CMD_ELS_REQUEST64_CR;
- icmd->ulpTimeout = phba->fc_ratov * 2;
- } else {
- icmd->un.elsreq64.bdl.bdeSize = sizeof(struct ulp_bde64);
- icmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX;
- }
- icmd->ulpBdeCount = 1;
- icmd->ulpLe = 1;
- icmd->ulpClass = CLASS3;
+ if (expect_rsp) {
+ switch (elscmd) {
+ case ELS_CMD_FLOGI:
+ timeout = FF_DEF_RATOV * 2;
+ break;
+ case ELS_CMD_LOGO:
+ timeout = phba->fc_ratov;
+ break;
+ default:
+ timeout = phba->fc_ratov * 2;
+ }
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
- icmd->un.elsreq64.myID = vport->fc_myDID;
-
- /* For ELS_REQUEST64_CR, use the VPI by default */
- icmd->ulpContext = phba->vpi_ids[vport->vpi];
- icmd->ulpCt_h = 0;
- /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
- if (elscmd == ELS_CMD_ECHO)
- icmd->ulpCt_l = 0; /* context = invalid RPI */
- else
- icmd->ulpCt_l = 1; /* context = VPI */
+ /* Fill SGE for the num bde count */
+ elsiocb->num_bdes = 2;
}
- bpl = (struct ulp_bde64 *) pbuflist->virt;
- bpl->addrLow = le32_to_cpu(putPaddrLow(pcmd->phys));
- bpl->addrHigh = le32_to_cpu(putPaddrHigh(pcmd->phys));
- bpl->tus.f.bdeSize = cmdSize;
- bpl->tus.f.bdeFlags = 0;
- bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ bmp = pcmd;
+ else
+ bmp = pbuflist;
- if (expectRsp) {
+ lpfc_sli_prep_els_req_rsp(phba, elsiocb, vport, bmp, cmd_size, did,
+ elscmd, timeout, expect_rsp);
+
+ bpl = (struct ulp_bde64_le *)pbuflist->virt;
+ bpl->addr_low = cpu_to_le32(putPaddrLow(pcmd->phys));
+ bpl->addr_high = cpu_to_le32(putPaddrHigh(pcmd->phys));
+ bpl->type_size = cpu_to_le32(cmd_size);
+ bpl->type_size |= cpu_to_le32(ULP_BDE64_TYPE_BDE_64);
+
+ if (expect_rsp) {
bpl++;
- bpl->addrLow = le32_to_cpu(putPaddrLow(prsp->phys));
- bpl->addrHigh = le32_to_cpu(putPaddrHigh(prsp->phys));
- bpl->tus.f.bdeSize = FCELSSIZE;
- bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl->addr_low = cpu_to_le32(putPaddrLow(prsp->phys));
+ bpl->addr_high = cpu_to_le32(putPaddrHigh(prsp->phys));
+ bpl->type_size = cpu_to_le32(FCELSSIZE);
+ bpl->type_size |= cpu_to_le32(ULP_BDE64_TYPE_BDE_64);
}
- /* prevent preparing iocb with NULL ndlp reference */
- elsiocb->context1 = lpfc_nlp_get(ndlp);
- if (!elsiocb->context1)
- goto els_iocb_free_pbuf_exit;
- elsiocb->context2 = pcmd;
- elsiocb->context3 = pbuflist;
+ elsiocb->cmd_dmabuf = pcmd;
+ elsiocb->bpl_dmabuf = pbuflist;
elsiocb->retry = retry;
elsiocb->vport = vport;
elsiocb->drvrTimeout = (phba->fc_ratov << 1) + LPFC_DRVR_TIMEOUT;
- if (prsp) {
+ if (prsp)
list_add(&prsp->list, &pcmd->list);
- }
- if (expectRsp) {
+ if (expect_rsp) {
/* Xmit ELS command <elsCmd> to remote NPORT <did> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0116 Xmit ELS command x%x to remote "
- "NPORT x%x I/O tag: x%x, port state: x%x\n",
+ "NPORT x%x I/O tag: x%x, port state:x%x "
+ "rpi x%x fc_flag:x%lx\n",
elscmd, did, elsiocb->iotag,
- vport->port_state);
+ vport->port_state, ndlp->nlp_rpi,
+ vport->fc_flag);
} else {
/* Xmit ELS response <elsCmd> to remote NPORT <did> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0117 Xmit ELS response x%x to remote "
- "NPORT x%x I/O tag: x%x, size: x%x\n",
+ "NPORT x%x I/O tag: x%x, size: x%x "
+ "port_state x%x rpi x%x fc_flag x%lx\n",
elscmd, ndlp->nlp_DID, elsiocb->iotag,
- cmdSize);
+ cmd_size, vport->port_state,
+ ndlp->nlp_rpi, vport->fc_flag);
}
+
return elsiocb;
els_iocb_free_pbuf_exit:
- if (expectRsp)
+ if (expect_rsp)
lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
kfree(pbuflist);
@@ -341,7 +351,6 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *mbox;
- struct lpfc_dmabuf *mp;
struct lpfc_nodelist *ndlp;
struct serv_parm *sp;
int rc;
@@ -349,7 +358,7 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport)
sp = &phba->fc_fabparam;
ndlp = lpfc_findnode_did(vport, Fabric_DID);
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
+ if (!ndlp) {
err = 1;
goto fail;
}
@@ -388,11 +397,15 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport)
/* increment the reference count on ndlp to hold reference
* for the callback routine.
*/
- mbox->context2 = lpfc_nlp_get(ndlp);
+ mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!mbox->ctx_ndlp) {
+ err = 6;
+ goto fail_free_mbox;
+ }
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- err = 6;
+ err = 7;
goto fail_issue_reg_login;
}
@@ -403,16 +416,13 @@ fail_issue_reg_login:
* for the failed mbox command.
*/
lpfc_nlp_put(ndlp);
- mp = (struct lpfc_dmabuf *) mbox->context1;
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
fail_free_mbox:
- mempool_free(mbox, phba->mbox_mem_pool);
-
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
fail:
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0249 Cannot issue Register Fabric login: Err %d\n", err);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0249 Cannot issue Register Fabric login: Err %d\n",
+ err);
return -ENXIO;
}
@@ -421,72 +431,123 @@ fail:
* @vport: pointer to a host virtual N_Port data structure.
*
* This routine issues a REG_VFI mailbox for the vfi, vpi, fcfi triplet for
- * the @vport. This mailbox command is necessary for FCoE only.
+ * the @vport. This mailbox command is necessary for SLI4 port only.
*
* Return code
* 0 - successfully issued REG_VFI for @vport
* A failure code otherwise.
**/
-static int
+int
lpfc_issue_reg_vfi(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
- LPFC_MBOXQ_t *mboxq;
+ LPFC_MBOXQ_t *mboxq = NULL;
struct lpfc_nodelist *ndlp;
- struct serv_parm *sp;
- struct lpfc_dmabuf *dmabuf;
+ struct lpfc_dmabuf *dmabuf = NULL;
int rc = 0;
- sp = &phba->fc_fabparam;
- ndlp = lpfc_findnode_did(vport, Fabric_DID);
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
- rc = -ENODEV;
- goto fail;
+ /* move forward in case of SLI4 FC port loopback test and pt2pt mode */
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ !(phba->link_flag & LS_LOOPBACK_MODE) &&
+ !test_bit(FC_PT2PT, &vport->fc_flag)) {
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp) {
+ rc = -ENODEV;
+ goto fail;
+ }
}
- dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!dmabuf) {
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
rc = -ENOMEM;
goto fail;
}
- dmabuf->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &dmabuf->phys);
- if (!dmabuf->virt) {
- rc = -ENOMEM;
- goto fail_free_dmabuf;
- }
- mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mboxq) {
- rc = -ENOMEM;
- goto fail_free_coherent;
+ /* Supply CSP's only if we are fabric connect or pt-to-pt connect */
+ if (test_bit(FC_FABRIC, &vport->fc_flag) ||
+ test_bit(FC_PT2PT, &vport->fc_flag)) {
+ rc = lpfc_mbox_rsrc_prep(phba, mboxq);
+ if (rc) {
+ rc = -ENOMEM;
+ goto fail_mbox;
+ }
+ dmabuf = mboxq->ctx_buf;
+ memcpy(dmabuf->virt, &phba->fc_fabparam,
+ sizeof(struct serv_parm));
}
+
vport->port_state = LPFC_FABRIC_CFG_LINK;
- memcpy(dmabuf->virt, &phba->fc_fabparam, sizeof(vport->fc_sparam));
- lpfc_reg_vfi(mboxq, vport, dmabuf->phys);
+ if (dmabuf) {
+ lpfc_reg_vfi(mboxq, vport, dmabuf->phys);
+ /* lpfc_reg_vfi memsets the mailbox. Restore the ctx_buf. */
+ mboxq->ctx_buf = dmabuf;
+ } else {
+ lpfc_reg_vfi(mboxq, vport, 0);
+ }
+
mboxq->mbox_cmpl = lpfc_mbx_cmpl_reg_vfi;
mboxq->vport = vport;
- mboxq->context1 = dmabuf;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
rc = -ENXIO;
- goto fail_free_mbox;
+ goto fail_mbox;
}
return 0;
-fail_free_mbox:
- mempool_free(mboxq, phba->mbox_mem_pool);
-fail_free_coherent:
- lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
-fail_free_dmabuf:
- kfree(dmabuf);
+fail_mbox:
+ lpfc_mbox_rsrc_cleanup(phba, mboxq, MBOX_THD_UNLOCKED);
fail:
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0289 Issue Register VFI failed: Err %d\n", rc);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0289 Issue Register VFI failed: Err %d\n", rc);
return rc;
}
/**
+ * lpfc_issue_unreg_vfi - Unregister VFI for this vport's fabric login
+ * @vport: pointer to a host virtual N_Port data structure.
+ *
+ * This routine issues a UNREG_VFI mailbox with the vfi, vpi, fcfi triplet for
+ * the @vport. This mailbox command is necessary for SLI4 port only.
+ *
+ * Return code
+ * 0 - successfully issued REG_VFI for @vport
+ * A failure code otherwise.
+ **/
+int
+lpfc_issue_unreg_vfi(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ LPFC_MBOXQ_t *mboxq;
+ int rc;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2556 UNREG_VFI mbox allocation failed"
+ "HBA state x%x\n", phba->pport->port_state);
+ return -ENOMEM;
+ }
+
+ lpfc_unreg_vfi(mboxq, vport);
+ mboxq->vport = vport;
+ mboxq->mbox_cmpl = lpfc_unregister_vfi_cmpl;
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2557 UNREG_VFI issue mbox failed rc x%x "
+ "HBA state x%x\n",
+ rc, phba->pport->port_state);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return -EIO;
+ }
+
+ clear_bit(FC_VFI_REGISTERED, &vport->fc_flag);
+ return 0;
+}
+
+/**
* lpfc_check_clean_addr_bit - Check whether assigned FCID is clean.
* @vport: pointer to a host virtual N_Port data structure.
* @sp: pointer to service parameter data structure.
@@ -508,16 +569,18 @@ static uint8_t
lpfc_check_clean_addr_bit(struct lpfc_vport *vport,
struct serv_parm *sp)
{
+ struct lpfc_hba *phba = vport->phba;
uint8_t fabric_param_changed = 0;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if ((vport->fc_prevDID != vport->fc_myDID) ||
memcmp(&vport->fabric_portname, &sp->portName,
sizeof(struct lpfc_name)) ||
memcmp(&vport->fabric_nodename, &sp->nodeName,
- sizeof(struct lpfc_name)))
+ sizeof(struct lpfc_name)) ||
+ (vport->vport_flag & FAWWPN_PARAM_CHG)) {
fabric_param_changed = 1;
-
+ vport->vport_flag &= ~FAWWPN_PARAM_CHG;
+ }
/*
* Word 1 Bit 31 in common service parameter is overloaded.
* Word 1 Bit 31 in FLOGI request is multiple NPort request
@@ -529,11 +592,8 @@ lpfc_check_clean_addr_bit(struct lpfc_vport *vport,
* - lpfc_delay_discovery module parameter is set.
*/
if (fabric_param_changed && !sp->cmn.clean_address_bit &&
- (vport->fc_prevDID || lpfc_delay_discovery)) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_DISC_DELAYED;
- spin_unlock_irq(shost->host_lock);
- }
+ (vport->fc_prevDID || phba->cfg_delay_discovery))
+ set_bit(FC_DISC_DELAYED, &vport->fc_flag);
return fabric_param_changed;
}
@@ -544,7 +604,7 @@ lpfc_check_clean_addr_bit(struct lpfc_vport *vport,
* @vport: pointer to a host virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @sp: pointer to service parameter data structure.
- * @irsp: pointer to the IOCB within the lpfc response IOCB.
+ * @ulp_word4: command response value
*
* This routine is invoked by the lpfc_cmpl_els_flogi() completion callback
* function to handle the completion of a Fabric Login (FLOGI) into a fabric
@@ -561,17 +621,14 @@ lpfc_check_clean_addr_bit(struct lpfc_vport *vport,
**/
static int
lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
- struct serv_parm *sp, IOCB_t *irsp)
+ struct serv_parm *sp, uint32_t ulp_word4)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *np;
struct lpfc_nodelist *next_np;
uint8_t fabric_param_changed;
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_FABRIC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_FABRIC, &vport->fc_flag);
phba->fc_edtov = be32_to_cpu(sp->cmn.e_d_tov);
if (sp->cmn.edtovResolution) /* E_D_TOV ticks are in nanoseconds */
@@ -580,13 +637,10 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
phba->fc_edtovResol = sp->cmn.edtovResolution;
phba->fc_ratov = (be32_to_cpu(sp->cmn.w2.r_a_tov) + 999) / 1000;
- if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_PUBLIC_LOOP;
- spin_unlock_irq(shost->host_lock);
- }
+ if (phba->fc_topology == LPFC_TOPOLOGY_LOOP)
+ set_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
- vport->fc_myDID = irsp->un.ulpWord[4] & Mask_DID;
+ vport->fc_myDID = ulp_word4 & Mask_DID;
memcpy(&ndlp->nlp_portname, &sp->portName, sizeof(struct lpfc_name));
memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof(struct lpfc_name));
ndlp->nlp_class_sup = 0;
@@ -596,12 +650,26 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_class_sup |= FC_COS_CLASS2;
if (sp->cls3.classValid)
ndlp->nlp_class_sup |= FC_COS_CLASS3;
- if (sp->cls4.classValid)
- ndlp->nlp_class_sup |= FC_COS_CLASS4;
ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) |
sp->cmn.bbRcvSizeLsb;
fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp);
+ if (fabric_param_changed) {
+ /* Reset FDMI attribute masks based on config parameter */
+ if (phba->cfg_enable_SmartSAN ||
+ (phba->cfg_fdmi_on == LPFC_FDMI_SUPPORT)) {
+ /* Setup appropriate attribute masks */
+ vport->fdmi_hba_mask = LPFC_FDMI2_HBA_ATTR;
+ if (phba->cfg_enable_SmartSAN)
+ vport->fdmi_port_mask = LPFC_FDMI2_SMART_ATTR;
+ else
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ } else {
+ vport->fdmi_hba_mask = 0;
+ vport->fdmi_port_mask = 0;
+ }
+
+ }
memcpy(&vport->fabric_portname, &sp->portName,
sizeof(struct lpfc_name));
memcpy(&vport->fabric_nodename, &sp->nodeName,
@@ -615,58 +683,66 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
"1816 FLOGI NPIV supported, "
"response data 0x%x\n",
sp->cmn.response_multiple_NPort);
+ spin_lock_irq(&phba->hbalock);
phba->link_flag |= LS_NPIV_FAB_SUPPORTED;
+ spin_unlock_irq(&phba->hbalock);
} else {
/* Because we asked f/w for NPIV it still expects us
- to call reg_vnpid atleast for the physcial host */
+ to call reg_vnpid at least for the physical host */
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_ELS | LOG_VPORT,
"1817 Fabric does not support NPIV "
"- configuring single port mode.\n");
+ spin_lock_irq(&phba->hbalock);
phba->link_flag &= ~LS_NPIV_FAB_SUPPORTED;
+ spin_unlock_irq(&phba->hbalock);
}
}
+ /*
+ * For FC we need to do some special processing because of the SLI
+ * Port's default settings of the Common Service Parameters.
+ */
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC)) {
+ /* If physical FC port changed, unreg VFI and ALL VPIs / RPIs */
+ if (fabric_param_changed)
+ lpfc_unregister_fcf_prep(phba);
+
+ /* This should just update the VFI CSPs*/
+ if (test_bit(FC_VFI_REGISTERED, &vport->fc_flag))
+ lpfc_issue_reg_vfi(vport);
+ }
+
if (fabric_param_changed &&
- !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
+ !test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag)) {
/* If our NportID changed, we need to ensure all
* remaining NPORTs get unreg_login'ed.
*/
list_for_each_entry_safe(np, next_np,
&vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(np))
- continue;
if ((np->nlp_state != NLP_STE_NPR_NODE) ||
- !(np->nlp_flag & NLP_NPR_ADISC))
+ !test_bit(NLP_NPR_ADISC, &np->nlp_flag))
continue;
- spin_lock_irq(shost->host_lock);
- np->nlp_flag &= ~NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NPR_ADISC, &np->nlp_flag);
lpfc_unreg_rpi(vport, np);
}
lpfc_cleanup_pending_mbox(vport);
- if (phba->sli_rev == LPFC_SLI_REV4)
+ if (phba->sli_rev == LPFC_SLI_REV4) {
lpfc_sli4_unreg_all_rpis(vport);
-
- if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
lpfc_mbx_unreg_vpi(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag);
}
+
/*
- * If VPI is unreged, driver need to do INIT_VPI
- * before re-registering
+ * For SLI3 and SLI4, the VPI needs to be reregistered in
+ * response to this fabric parameter change event.
*/
- if (phba->sli_rev == LPFC_SLI_REV4) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
- spin_unlock_irq(shost->host_lock);
- }
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
} else if ((phba->sli_rev == LPFC_SLI_REV4) &&
- !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
+ !test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag)) {
/*
* Driver needs to re-reg VPI in order for f/w
* to update the MAC address.
@@ -679,24 +755,29 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (phba->sli_rev < LPFC_SLI_REV4) {
lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&
- vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)
+ test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag))
lpfc_register_new_vport(phba, vport, ndlp);
else
lpfc_issue_fabric_reglogin(vport);
} else {
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
- if ((!(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) &&
- (vport->vpi_state & LPFC_VPI_REGISTERED)) {
+ if ((!test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag)) &&
+ (vport->vpi_state & LPFC_VPI_REGISTERED)) {
lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
- } else if (vport->fc_flag & FC_VFI_REGISTERED)
+ } else if (test_bit(FC_VFI_REGISTERED, &vport->fc_flag))
lpfc_issue_init_vpi(vport);
- else
+ else {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3135 Need register VFI: (x%x/%x)\n",
+ vport->fc_prevDID, vport->fc_myDID);
lpfc_issue_reg_vfi(vport);
+ }
}
return 0;
}
+
/**
* lpfc_cmpl_els_flogi_nport - Completion function for flogi to an N_Port
* @vport: pointer to a host virtual N_Port data structure.
@@ -721,51 +802,46 @@ static int
lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct serv_parm *sp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *mbox;
int rc;
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_FABRIC, &vport->fc_flag);
+ clear_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
+ set_bit(FC_PT2PT, &vport->fc_flag);
+
+ /* If we are pt2pt with another NPort, force NPIV off! */
+ phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
+
+ /* If physical FC port changed, unreg VFI and ALL VPIs / RPIs */
+ if ((phba->sli_rev == LPFC_SLI_REV4) && phba->fc_topology_changed) {
+ lpfc_unregister_fcf_prep(phba);
+ clear_bit(FC_VFI_REGISTERED, &vport->fc_flag);
+ phba->fc_topology_changed = 0;
+ }
- phba->fc_edtov = FF_DEF_EDTOV;
- phba->fc_ratov = FF_DEF_RATOV;
rc = memcmp(&vport->fc_portname, &sp->portName,
sizeof(vport->fc_portname));
+
if (rc >= 0) {
/* This side will initiate the PLOGI */
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_PT2PT_PLOGI;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_PT2PT_PLOGI, &vport->fc_flag);
/*
- * N_Port ID cannot be 0, set our to LocalID the other
- * side will be RemoteID.
+ * N_Port ID cannot be 0, set our Id to LocalID
+ * the other side will be RemoteID.
*/
/* not equal */
if (rc)
vport->fc_myDID = PT2PT_LocalID;
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox)
- goto fail;
-
- lpfc_config_link(phba, mbox);
-
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->vport = vport;
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- mempool_free(mbox, phba->mbox_mem_pool);
- goto fail;
- }
- /* Decrement ndlp reference count indicating that ndlp can be
- * safely released when other references to it are done.
+ /* If not registered with a transport, decrement ndlp reference
+ * count indicating that ndlp can be safely released when other
+ * references are removed.
*/
- lpfc_nlp_put(ndlp);
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)))
+ lpfc_nlp_put(ndlp);
ndlp = lpfc_findnode_did(vport, PT2PT_RemoteID);
if (!ndlp) {
@@ -773,15 +849,9 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* Cannot find existing Fabric ndlp, so allocate a
* new one
*/
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, PT2PT_RemoteID);
if (!ndlp)
goto fail;
- lpfc_nlp_init(vport, ndlp, PT2PT_RemoteID);
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp,
- NLP_STE_UNUSED_NODE);
- if(!ndlp)
- goto fail;
}
memcpy(&ndlp->nlp_portname, &sp->portName,
@@ -790,25 +860,33 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
sizeof(struct lpfc_name));
/* Set state will put ndlp onto node list if not already done */
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
- } else
- /* This side will wait for the PLOGI, decrement ndlp reference
- * count indicating that ndlp can be released when other
- * references to it are done.
- */
- lpfc_nlp_put(ndlp);
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
- /* If we are pt2pt with another NPort, force NPIV off! */
- phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ goto fail;
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_PT2PT;
- spin_unlock_irq(shost->host_lock);
+ lpfc_config_link(phba, mbox);
+
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_local_config_link;
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ goto fail;
+ }
+ } else {
+ /* This side will wait for the PLOGI. If not registered with
+ * a transport, decrement node reference count indicating that
+ * ndlp can be released when other references are removed.
+ */
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)))
+ lpfc_nlp_put(ndlp);
+
+ /* Start discovery - this should just do CLEAR_LA */
+ lpfc_disc_start(vport);
+ }
- /* Start discovery - this should just do CLEAR_LA */
- lpfc_disc_start(vport);
return 0;
fail:
return -ENXIO;
@@ -842,121 +920,214 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- IOCB_t *irsp = &rspiocb->iocb;
- struct lpfc_nodelist *ndlp = cmdiocb->context1;
- struct lpfc_dmabuf *pcmd = cmdiocb->context2, *prsp;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
+ IOCB_t *irsp;
+ struct lpfc_dmabuf *pcmd = cmdiocb->cmd_dmabuf, *prsp;
struct serv_parm *sp;
uint16_t fcf_index;
int rc;
+ u32 ulp_status, ulp_word4, tmo;
+ bool flogi_in_retry = false;
/* Check to see if link went down during discovery */
if (lpfc_els_chk_latt(vport)) {
/* One additional decrement on node reference count to
- * trigger the release of the node
+ * trigger the release of the node. Make sure the ndlp
+ * is marked NLP_DROPPED.
*/
- lpfc_nlp_put(ndlp);
+ if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag) &&
+ !test_bit(NLP_DROPPED, &ndlp->nlp_flag) &&
+ !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) {
+ set_bit(NLP_DROPPED, &ndlp->nlp_flag);
+ lpfc_nlp_put(ndlp);
+ }
goto out;
}
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tmo = get_wqe_tmo(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ tmo = irsp->ulpTimeout;
+ }
+
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"FLOGI cmpl: status:x%x/x%x state:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
+ ulp_status, ulp_word4,
vport->port_state);
- if (irsp->ulpStatus) {
+ if (ulp_status) {
/*
* In case of FIP mode, perform roundrobin FCF failover
* due to new FCF discovery
*/
- if ((phba->hba_flag & HBA_FIP_SUPPORT) &&
- (phba->fcf.fcf_flag & FCF_DISCOVERY) &&
- !((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) {
+ if (test_bit(HBA_FIP_SUPPORT, &phba->hba_flag) &&
+ (phba->fcf.fcf_flag & FCF_DISCOVERY)) {
+ if (phba->link_state < LPFC_LINK_UP)
+ goto stop_rr_fcf_flogi;
+ if ((phba->fcoe_cvl_eventtag_attn ==
+ phba->fcoe_cvl_eventtag) &&
+ (ulp_status == IOSTAT_LOCAL_REJECT) &&
+ ((ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_SLI_ABORTED))
+ goto stop_rr_fcf_flogi;
+ else
+ phba->fcoe_cvl_eventtag_attn =
+ phba->fcoe_cvl_eventtag;
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP | LOG_ELS,
- "2611 FLOGI failed on FCF (x%x), "
+ "2611 FLOGI FCF (x%x), "
"status:x%x/x%x, tmo:x%x, perform "
"roundrobin FCF failover\n",
phba->fcf.current_rec.fcf_indx,
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout);
+ ulp_status, ulp_word4, tmo);
+ lpfc_sli4_set_fcf_flogi_fail(phba,
+ phba->fcf.current_rec.fcf_indx);
fcf_index = lpfc_sli4_fcf_rr_next_index_get(phba);
rc = lpfc_sli4_fcf_rr_next_proc(vport, fcf_index);
if (rc)
goto out;
}
+stop_rr_fcf_flogi:
/* FLOGI failure */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "2858 FLOGI failure Status:x%x/x%x TMO:x%x\n",
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout);
+ if (!(ulp_status == IOSTAT_LOCAL_REJECT &&
+ ((ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_LOOP_OPEN_FAILURE)))
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "2858 FLOGI Status:x%x/x%x TMO"
+ ":x%x Data x%lx x%x x%lx x%x\n",
+ ulp_status, ulp_word4, tmo,
+ phba->hba_flag, phba->fcf.fcf_flag,
+ ndlp->nlp_flag, ndlp->fc4_xpt_flags);
/* Check for retry */
- if (lpfc_els_retry(phba, cmdiocb, rspiocb))
+ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+ /* Address a timing race with dev_loss. If dev_loss
+ * is active on this FPort node, put the initial ref
+ * count back to stop premature node release actions.
+ */
+ lpfc_check_nlp_post_devloss(vport, ndlp);
+ flogi_in_retry = true;
goto out;
+ }
- /* FLOGI failure */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0100 FLOGI failure Status:x%x/x%x TMO:x%x\n",
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout);
+ /* The FLOGI will not be retried. If the FPort node is not
+ * registered with the SCSI transport, remove the initial
+ * reference to trigger node release.
+ */
+ if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag) &&
+ !test_bit(NLP_DROPPED, &ndlp->nlp_flag) &&
+ !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) {
+ set_bit(NLP_DROPPED, &ndlp->nlp_flag);
+ lpfc_nlp_put(ndlp);
+ }
+
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
+ "0150 FLOGI Status:x%x/x%x "
+ "xri x%x iotag x%x TMO:x%x refcnt %d\n",
+ ulp_status, ulp_word4, cmdiocb->sli4_xritag,
+ cmdiocb->iotag, tmo, kref_read(&ndlp->kref));
+
+ /* If this is not a loop open failure, bail out */
+ if (!(ulp_status == IOSTAT_LOCAL_REJECT &&
+ ((ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_LOOP_OPEN_FAILURE))) {
+ /* Warn FLOGI status */
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "0100 FLOGI Status:x%x/x%x "
+ "TMO:x%x\n",
+ ulp_status, ulp_word4, tmo);
+ goto flogifail;
+ }
/* FLOGI failed, so there is no fabric */
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_FABRIC, &vport->fc_flag);
+ clear_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
+ clear_bit(FC_PT2PT_NO_NVME, &vport->fc_flag);
/* If private loop, then allow max outstanding els to be
* LPFC_MAX_DISC_THREADS (32). Scanning in the case of no
* alpa map would take too long otherwise.
*/
- if (phba->alpa_map[0] == 0) {
+ if (phba->alpa_map[0] == 0)
vport->cfg_discovery_threads = LPFC_MAX_DISC_THREADS;
- if ((phba->sli_rev == LPFC_SLI_REV4) &&
- (!(vport->fc_flag & FC_VFI_REGISTERED) ||
- (vport->fc_prevDID != vport->fc_myDID))) {
- if (vport->fc_flag & FC_VFI_REGISTERED)
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ (!test_bit(FC_VFI_REGISTERED, &vport->fc_flag) ||
+ (vport->fc_prevDID != vport->fc_myDID) ||
+ phba->fc_topology_changed)) {
+ if (test_bit(FC_VFI_REGISTERED, &vport->fc_flag)) {
+ if (phba->fc_topology_changed) {
+ lpfc_unregister_fcf_prep(phba);
+ clear_bit(FC_VFI_REGISTERED,
+ &vport->fc_flag);
+ phba->fc_topology_changed = 0;
+ } else {
lpfc_sli4_unreg_all_rpis(vport);
- lpfc_issue_reg_vfi(vport);
- lpfc_nlp_put(ndlp);
- goto out;
+ }
}
+
+ /* Do not register VFI if the driver aborted FLOGI */
+ if (!lpfc_error_lost_link(vport, ulp_status, ulp_word4))
+ lpfc_issue_reg_vfi(vport);
+
+ goto out;
}
goto flogifail;
}
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_VPORT_CVL_RCVD;
- vport->fc_flag &= ~FC_VPORT_LOGO_RCVD;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_VPORT_CVL_RCVD, &vport->fc_flag);
+ clear_bit(FC_VPORT_LOGO_RCVD, &vport->fc_flag);
/*
- * The FLogI succeeded. Sync the data for the CPU before
+ * The FLOGI succeeded. Sync the data for the CPU before
* accessing it.
*/
prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
-
+ if (!prsp)
+ goto out;
+ if (!lpfc_is_els_acc_rsp(prsp))
+ goto out;
sp = prsp->virt + sizeof(uint32_t);
/* FLOGI completes successfully */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0101 FLOGI completes successfully "
- "Data: x%x x%x x%x x%x\n",
- irsp->un.ulpWord[4], sp->cmn.e_d_tov,
- sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution);
+ "0101 FLOGI completes successfully, I/O tag:x%x "
+ "xri x%x Data: x%x x%x x%x x%x x%x x%lx x%x %d\n",
+ cmdiocb->iotag, cmdiocb->sli4_xritag,
+ ulp_word4, sp->cmn.e_d_tov,
+ sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution,
+ vport->port_state, vport->fc_flag,
+ sp->cmn.priority_tagging, kref_read(&ndlp->kref));
+
+ /* reinitialize the VMID datastructure before returning */
+ if (lpfc_is_vmid_enabled(phba)) {
+ lpfc_reinit_vmid(vport);
+ vport->vmid_flag = 0;
+ }
+ if (sp->cmn.priority_tagging)
+ vport->phba->pport->vmid_flag |= (LPFC_VMID_ISSUE_QFPA |
+ LPFC_VMID_TYPE_PRIO);
+ /*
+ * Address a timing race with dev_loss. If dev_loss is active on
+ * this FPort node, put the initial ref count back to stop premature
+ * node release actions.
+ */
+ lpfc_check_nlp_post_devloss(vport, ndlp);
if (vport->port_state == LPFC_FLOGI) {
/*
* If Common Service Parameters indicate Nport
* we are point to point, if Fport we are Fabric.
*/
if (sp->cmn.fPort)
- rc = lpfc_cmpl_els_flogi_fabric(vport, ndlp, sp, irsp);
- else if (!(phba->hba_flag & HBA_FCOE_MODE))
+ rc = lpfc_cmpl_els_flogi_fabric(vport, ndlp, sp,
+ ulp_word4);
+ else if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag))
rc = lpfc_cmpl_els_flogi_nport(vport, ndlp, sp);
else {
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_FIP | LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"2831 FLOGI response with cleared Fabric "
"bit fcf_index 0x%x "
"Switch Name %02x%02x%02x%02x%02x%02x%02x%02x "
@@ -979,16 +1150,19 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
phba->fcf.current_rec.fabric_name[5],
phba->fcf.current_rec.fabric_name[6],
phba->fcf.current_rec.fabric_name[7]);
+
lpfc_nlp_put(ndlp);
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
- phba->hba_flag &= ~(FCF_RR_INPROG | HBA_DEVLOSS_TMO);
spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
+ clear_bit(HBA_DEVLOSS_TMO, &phba->hba_flag);
+ phba->fcf.fcf_redisc_attempted = 0; /* reset */
goto out;
}
if (!rc) {
/* Mark the FCF discovery process done */
- if (phba->hba_flag & HBA_FIP_SUPPORT)
+ if (test_bit(HBA_FIP_SUPPORT, &phba->hba_flag))
lpfc_printf_vlog(vport, KERN_INFO, LOG_FIP |
LOG_ELS,
"2769 FLOGI to FCF (x%x) "
@@ -996,29 +1170,83 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
phba->fcf.current_rec.fcf_indx);
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
- phba->hba_flag &= ~(FCF_RR_INPROG | HBA_DEVLOSS_TMO);
spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
+ clear_bit(HBA_DEVLOSS_TMO, &phba->hba_flag);
+ phba->fcf.fcf_redisc_attempted = 0; /* reset */
goto out;
}
+ } else if (vport->port_state > LPFC_FLOGI &&
+ test_bit(FC_PT2PT, &vport->fc_flag)) {
+ /*
+ * In a p2p topology, it is possible that discovery has
+ * already progressed, and this completion can be ignored.
+ * Recheck the indicated topology.
+ */
+ if (!sp->cmn.fPort)
+ goto out;
}
flogifail:
- lpfc_nlp_put(ndlp);
+ spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
+ spin_unlock_irq(&phba->hbalock);
- if (!lpfc_error_lost_link(irsp)) {
+ if (!lpfc_error_lost_link(vport, ulp_status, ulp_word4)) {
/* FLOGI failed, so just use loop map to make discovery list */
lpfc_disc_list_loopmap(vport);
/* Start discovery */
lpfc_disc_start(vport);
- } else if (((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) ||
- ((irsp->un.ulpWord[4] != IOERR_SLI_ABORTED) &&
- (irsp->un.ulpWord[4] != IOERR_SLI_DOWN))) &&
+ } else if (((ulp_status != IOSTAT_LOCAL_REJECT) ||
+ (((ulp_word4 & IOERR_PARAM_MASK) !=
+ IOERR_SLI_ABORTED) &&
+ ((ulp_word4 & IOERR_PARAM_MASK) !=
+ IOERR_SLI_DOWN))) &&
(phba->link_state != LPFC_CLEAR_LA)) {
/* If FLOGI failed enable link interrupt. */
lpfc_issue_clear_la(phba, vport);
}
out:
+ if (!flogi_in_retry)
+ clear_bit(HBA_FLOGI_OUTSTANDING, &phba->hba_flag);
+
+ lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
+}
+
+/**
+ * lpfc_cmpl_els_link_down - Completion callback function for ELS command
+ * aborted during a link down
+ * @phba: pointer to lpfc hba data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @rspiocb: pointer to lpfc response iocb data structure.
+ *
+ */
+static void
+lpfc_cmpl_els_link_down(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ uint32_t *pcmd;
+ uint32_t cmd;
+ u32 ulp_status, ulp_word4;
+
+ pcmd = (uint32_t *)cmdiocb->cmd_dmabuf->virt;
+ cmd = *pcmd;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "6445 ELS completes after LINK_DOWN: "
+ " Status %x/%x cmd x%x flg x%x iotag x%x\n",
+ ulp_status, ulp_word4, cmd,
+ cmdiocb->cmd_flag, cmdiocb->iotag);
+
+ if (cmdiocb->cmd_flag & LPFC_IO_FABRIC) {
+ cmdiocb->cmd_flag &= ~LPFC_IO_FABRIC;
+ atomic_dec(&phba->fabric_iocb_count);
+ }
lpfc_els_free_iocb(phba, cmdiocb);
}
@@ -1035,10 +1263,9 @@ out:
* function field. The lpfc_issue_fabric_iocb routine is invoked to send
* out FLOGI ELS command with one outstanding fabric IOCB at a time.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the FLOGI ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the FLOGI ELS command.
*
* Return code
* 0 - successfully issued flogi iocb for @vport
@@ -1050,15 +1277,27 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_hba *phba = vport->phba;
struct serv_parm *sp;
- IOCB_t *icmd;
+ union lpfc_wqe128 *wqe = NULL;
+ IOCB_t *icmd = NULL;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli_ring *pring;
- uint8_t *pcmd;
+ struct lpfc_iocbq defer_flogi_acc;
+ u8 *pcmd, ct;
uint16_t cmdsize;
- uint32_t tmo;
+ uint32_t tmo, did;
int rc;
- pring = &phba->sli.ring[LPFC_ELS_RING];
+ /* It's possible for lpfc to reissue a FLOGI on an ndlp that is marked
+ * NLP_DROPPED. This happens when the FLOGI completed with the XB bit
+ * set causing lpfc to reference the ndlp until the XRI_ABORTED CQE is
+ * issued. The time window for the XRI_ABORTED CQE can be as much as
+ * 2*2*RA_TOV allowing for ndlp reuse of this type when the link is
+ * cycling quickly. When true, restore the initial reference and remove
+ * the NLP_DROPPED flag as lpfc is retrying.
+ */
+ if (test_and_clear_bit(NLP_DROPPED, &ndlp->nlp_flag)) {
+ if (!lpfc_nlp_get(ndlp))
+ return 1;
+ }
cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
@@ -1067,8 +1306,9 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (!elsiocb)
return 1;
+ wqe = &elsiocb->wqe;
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
/* For FLOGI request, remainder of payload is service parameters */
*((uint32_t *) (pcmd)) = ELS_CMD_FLOGI;
@@ -1079,33 +1319,66 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* Setup CSPs accordingly for Fabric */
sp->cmn.e_d_tov = 0;
sp->cmn.w2.r_a_tov = 0;
+ sp->cmn.virtual_fabric_support = 0;
sp->cls1.classValid = 0;
- sp->cls2.seqDelivery = 1;
- sp->cls3.seqDelivery = 1;
if (sp->cmn.fcphLow < FC_PH3)
sp->cmn.fcphLow = FC_PH3;
if (sp->cmn.fcphHigh < FC_PH3)
sp->cmn.fcphHigh = FC_PH3;
+ /* Determine if switch supports priority tagging */
+ if (phba->cfg_vmid_priority_tagging) {
+ sp->cmn.priority_tagging = 1;
+ /* lpfc_vmid_host_uuid is combination of wwpn and wwnn */
+ if (!memchr_inv(vport->lpfc_vmid_host_uuid, 0,
+ sizeof(vport->lpfc_vmid_host_uuid))) {
+ memcpy(vport->lpfc_vmid_host_uuid, phba->wwpn,
+ sizeof(phba->wwpn));
+ memcpy(&vport->lpfc_vmid_host_uuid[8], phba->wwnn,
+ sizeof(phba->wwnn));
+ }
+ }
+
if (phba->sli_rev == LPFC_SLI_REV4) {
if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
LPFC_SLI_INTF_IF_TYPE_0) {
- elsiocb->iocb.ulpCt_h = ((SLI4_CT_FCFI >> 1) & 1);
- elsiocb->iocb.ulpCt_l = (SLI4_CT_FCFI & 1);
/* FLOGI needs to be 3 for WQE FCFI */
+ ct = SLI4_CT_FCFI;
+ bf_set(wqe_ct, &wqe->els_req.wqe_com, ct);
+
/* Set the fcfi to the fcfi we registered with */
- elsiocb->iocb.ulpContext = phba->fcf.fcfi;
+ bf_set(wqe_ctxt_tag, &wqe->els_req.wqe_com,
+ phba->fcf.fcfi);
}
- } else if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
- sp->cmn.request_multiple_Nport = 1;
- /* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
- icmd->ulpCt_h = 1;
- icmd->ulpCt_l = 0;
- }
- if (phba->fc_topology != LPFC_TOPOLOGY_LOOP) {
- icmd->un.elsreq64.myID = 0;
- icmd->un.elsreq64.fl = 1;
+ /* Can't do SLI4 class2 without support sequence coalescing */
+ sp->cls2.classValid = 0;
+ sp->cls2.seqDelivery = 0;
+
+ /* Fill out Auxiliary Parameter Data */
+ if (phba->pni) {
+ sp->aux.flags =
+ AUX_PARM_DATA_VALID | AUX_PARM_PNI_VALID;
+ sp->aux.pni = cpu_to_be64(phba->pni);
+ sp->aux.npiv_cnt = cpu_to_be16(phba->max_vpi - 1);
+ }
+ } else {
+ /* Historical, setting sequential-delivery bit for SLI3 */
+ sp->cls2.seqDelivery = (sp->cls2.classValid) ? 1 : 0;
+ sp->cls3.seqDelivery = (sp->cls3.classValid) ? 1 : 0;
+ if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
+ sp->cmn.request_multiple_Nport = 1;
+ /* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
+ icmd->ulpCt_h = 1;
+ icmd->ulpCt_l = 0;
+ } else {
+ sp->cmn.request_multiple_Nport = 0;
+ }
+
+ if (phba->fc_topology != LPFC_TOPOLOGY_LOOP) {
+ icmd->un.elsreq64.myID = 0;
+ icmd->un.elsreq64.fl = 1;
+ }
}
tmo = phba->fc_ratov;
@@ -1114,17 +1387,92 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
phba->fc_ratov = tmo;
phba->fc_stat.elsXmitFLOGI++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_flogi;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_flogi;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"Issue FLOGI: opt:x%x",
phba->sli3_options, 0, 0);
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
+
+ /* Avoid race with FLOGI completion and hba_flags. */
+ set_bit(HBA_FLOGI_ISSUED, &phba->hba_flag);
+ set_bit(HBA_FLOGI_OUTSTANDING, &phba->hba_flag);
+
rc = lpfc_issue_fabric_iocb(phba, elsiocb);
if (rc == IOCB_ERROR) {
+ clear_bit(HBA_FLOGI_ISSUED, &phba->hba_flag);
+ clear_bit(HBA_FLOGI_OUTSTANDING, &phba->hba_flag);
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
+ /* Clear external loopback plug detected flag */
+ phba->link_flag &= ~LS_EXTERNAL_LOOPBACK;
+
+ /* Check for a deferred FLOGI ACC condition */
+ if (phba->defer_flogi_acc.flag) {
+ /* lookup ndlp for received FLOGI */
+ ndlp = lpfc_findnode_did(vport, 0);
+ if (!ndlp)
+ return 0;
+
+ did = vport->fc_myDID;
+ vport->fc_myDID = Fabric_DID;
+
+ memset(&defer_flogi_acc, 0, sizeof(struct lpfc_iocbq));
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ bf_set(wqe_ctxt_tag,
+ &defer_flogi_acc.wqe.xmit_els_rsp.wqe_com,
+ phba->defer_flogi_acc.rx_id);
+ bf_set(wqe_rcvoxid,
+ &defer_flogi_acc.wqe.xmit_els_rsp.wqe_com,
+ phba->defer_flogi_acc.ox_id);
+ } else {
+ icmd = &defer_flogi_acc.iocb;
+ icmd->ulpContext = phba->defer_flogi_acc.rx_id;
+ icmd->unsli3.rcvsli3.ox_id =
+ phba->defer_flogi_acc.ox_id;
+ }
+
+ /* The LS_ACC completion needs to drop the initial reference.
+ * This is a special case for Pt2Pt because both FLOGIs need
+ * to complete and lpfc defers the LS_ACC when the remote
+ * FLOGI arrives before the driver's FLOGI.
+ */
+ set_bit(NLP_FLOGI_DFR_ACC, &ndlp->nlp_flag);
+
+ /* Send deferred FLOGI ACC */
+ lpfc_els_rsp_acc(vport, ELS_CMD_FLOGI, &defer_flogi_acc,
+ ndlp, NULL);
+
+ phba->defer_flogi_acc.flag = false;
+
+ /* Decrement the held ndlp that was incremented when the
+ * deferred flogi acc flag was set.
+ */
+ if (phba->defer_flogi_acc.ndlp) {
+ lpfc_nlp_put(phba->defer_flogi_acc.ndlp);
+ phba->defer_flogi_acc.ndlp = NULL;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3354 Xmit deferred FLOGI ACC: rx_id: x%x,"
+ " ox_id: x%x, ndlp x%px hba_flag x%lx\n",
+ phba->defer_flogi_acc.rx_id,
+ phba->defer_flogi_acc.ox_id,
+ phba->defer_flogi_acc.ndlp,
+ phba->hba_flag);
+
+ vport->fc_myDID = did;
+ }
+
return 0;
}
@@ -1148,14 +1496,16 @@ lpfc_els_abort_flogi(struct lpfc_hba *phba)
struct lpfc_sli_ring *pring;
struct lpfc_iocbq *iocb, *next_iocb;
struct lpfc_nodelist *ndlp;
- IOCB_t *icmd;
+ u32 ulp_command;
/* Abort outstanding I/O on NPort <nlp_DID> */
lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
"0201 Abort outstanding I/O on NPort x%x\n",
Fabric_DID);
- pring = &phba->sli.ring[LPFC_ELS_RING];
+ pring = lpfc_phba_elsring(phba);
+ if (unlikely(!pring))
+ return -EIO;
/*
* Check the txcmplq for an iocb that matches the nport the driver is
@@ -1163,15 +1513,23 @@ lpfc_els_abort_flogi(struct lpfc_hba *phba)
*/
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
- icmd = &iocb->iocb;
- if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR &&
- icmd->un.elsreq64.bdl.ulpIoTag32) {
- ndlp = (struct lpfc_nodelist *)(iocb->context1);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
- (ndlp->nlp_DID == Fabric_DID))
- lpfc_sli_issue_abort_iotag(phba, pring, iocb);
+ ulp_command = get_job_cmnd(phba, iocb);
+ if (ulp_command == CMD_ELS_REQUEST64_CR) {
+ ndlp = iocb->ndlp;
+ if (ndlp && ndlp->nlp_DID == Fabric_DID) {
+ if (test_bit(FC_PT2PT, &phba->pport->fc_flag) &&
+ !test_bit(FC_PT2PT_PLOGI,
+ &phba->pport->fc_flag))
+ iocb->fabric_cmd_cmpl =
+ lpfc_ignore_els_cmpl;
+ lpfc_sli_issue_abort_iotag(phba, pring, iocb,
+ NULL);
+ }
}
}
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
spin_unlock_irq(&phba->hbalock);
return 0;
@@ -1196,7 +1554,6 @@ lpfc_els_abort_flogi(struct lpfc_hba *phba)
int
lpfc_initial_flogi(struct lpfc_vport *vport)
{
- struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp;
vport->port_state = LPFC_FLOGI;
@@ -1206,26 +1563,26 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
ndlp = lpfc_findnode_did(vport, Fabric_DID);
if (!ndlp) {
/* Cannot find existing Fabric ndlp, so allocate a new one */
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, Fabric_DID);
if (!ndlp)
return 0;
- lpfc_nlp_init(vport, ndlp, Fabric_DID);
/* Set the node type */
ndlp->nlp_type |= NLP_FABRIC;
+
/* Put ndlp onto node list */
lpfc_enqueue_node(vport, ndlp);
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- /* re-setup ndlp without removing from node list */
- ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
- if (!ndlp)
- return 0;
}
+ /* Reset the Fabric flag, topology change may have happened */
+ clear_bit(FC_FABRIC, &vport->fc_flag);
if (lpfc_issue_els_flogi(vport, ndlp, 0)) {
- /* This decrement of reference count to node shall kick off
- * the release of the node.
+ /* A node reference should be retained while registered with a
+ * transport or dev-loss-evt work is pending.
+ * Otherwise, decrement node reference to trigger release.
*/
- lpfc_nlp_put(ndlp);
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) &&
+ !test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag))
+ lpfc_nlp_put(ndlp);
return 0;
}
return 1;
@@ -1250,31 +1607,31 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
int
lpfc_initial_fdisc(struct lpfc_vport *vport)
{
- struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp;
/* First look for the Fabric ndlp */
ndlp = lpfc_findnode_did(vport, Fabric_DID);
if (!ndlp) {
/* Cannot find existing Fabric ndlp, so allocate a new one */
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, Fabric_DID);
if (!ndlp)
return 0;
- lpfc_nlp_init(vport, ndlp, Fabric_DID);
+
+ /* NPIV is only supported in Fabrics. */
+ ndlp->nlp_type |= NLP_FABRIC;
+
/* Put ndlp onto node list */
lpfc_enqueue_node(vport, ndlp);
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- /* re-setup ndlp without removing from node list */
- ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
- if (!ndlp)
- return 0;
}
if (lpfc_issue_els_fdisc(vport, ndlp, 0)) {
- /* decrement node reference count to trigger the release of
- * the node.
+ /* A node reference should be retained while registered with a
+ * transport or dev-loss-evt work is pending.
+ * Otherwise, decrement node reference to trigger release.
*/
- lpfc_nlp_put(ndlp);
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) &&
+ !test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag))
+ lpfc_nlp_put(ndlp);
return 0;
}
return 1;
@@ -1294,27 +1651,26 @@ lpfc_initial_fdisc(struct lpfc_vport *vport)
void
lpfc_more_plogi(struct lpfc_vport *vport)
{
- int sentplogi;
-
if (vport->num_disc_nodes)
vport->num_disc_nodes--;
/* Continue discovery with <num_disc_nodes> PLOGIs to go */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0232 Continue discovery with %d PLOGIs to go "
- "Data: x%x x%x x%x\n",
- vport->num_disc_nodes, vport->fc_plogi_cnt,
+ "Data: x%x x%lx x%x\n",
+ vport->num_disc_nodes,
+ atomic_read(&vport->fc_plogi_cnt),
vport->fc_flag, vport->port_state);
/* Check to see if there are more PLOGIs to be sent */
- if (vport->fc_flag & FC_NLP_MORE)
+ if (test_bit(FC_NLP_MORE, &vport->fc_flag))
/* go thru NPR nodes and issue any remaining ELS PLOGIs */
- sentplogi = lpfc_els_disc_plogi(vport);
+ lpfc_els_disc_plogi(vport);
return;
}
/**
- * lpfc_plogi_confirm_nport - Confirm pologi wwpn matches stored ndlp
+ * lpfc_plogi_confirm_nport - Confirm plogi wwpn matches stored ndlp
* @phba: pointer to lpfc hba data structure.
* @prsp: pointer to response IOCB payload.
* @ndlp: pointer to a node-list data structure.
@@ -1348,22 +1704,17 @@ static struct lpfc_nodelist *
lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
struct lpfc_nodelist *ndlp)
{
- struct lpfc_vport *vport = ndlp->vport;
+ struct lpfc_vport *vport = ndlp->vport;
struct lpfc_nodelist *new_ndlp;
- struct lpfc_rport_data *rdata;
- struct fc_rport *rport;
struct serv_parm *sp;
uint8_t name[sizeof(struct lpfc_name)];
- uint32_t rc, keepDID = 0;
- int put_node;
- int put_rport;
- struct lpfc_node_rrqs rrq;
-
- /* Fabric nodes can have the same WWPN so we don't bother searching
- * by WWPN. Just return the ndlp that was given to us.
- */
- if (ndlp->nlp_type & NLP_FABRIC)
- return ndlp;
+ uint32_t keepDID = 0;
+ int rc;
+ unsigned long keep_nlp_flag = 0, keep_new_nlp_flag = 0;
+ uint16_t keep_nlp_state;
+ u32 keep_nlp_fc4_type = 0;
+ struct lpfc_nvme_rport *keep_nrport = NULL;
+ unsigned long *active_rrqs_xri_bitmap = NULL;
sp = (struct serv_parm *) ((uint8_t *) prsp + sizeof(uint32_t));
memset(name, 0, sizeof(struct lpfc_name));
@@ -1373,124 +1724,207 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
*/
new_ndlp = lpfc_findnode_wwpn(vport, &sp->portName);
- if (new_ndlp == ndlp && NLP_CHK_NODE_ACT(new_ndlp))
+ /* return immediately if the WWPN matches ndlp */
+ if (new_ndlp == ndlp)
return ndlp;
- memset(&rrq.xri_bitmap, 0, sizeof(new_ndlp->active_rrqs.xri_bitmap));
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ active_rrqs_xri_bitmap = mempool_alloc(phba->active_rrq_pool,
+ GFP_KERNEL);
+ if (active_rrqs_xri_bitmap)
+ memset(active_rrqs_xri_bitmap, 0,
+ phba->cfg_rrq_xri_bitmap_sz);
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE,
+ "3178 PLOGI confirm: ndlp x%x x%lx x%x: "
+ "new_ndlp x%x x%lx x%x\n",
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_fc4_type,
+ (new_ndlp ? new_ndlp->nlp_DID : 0),
+ (new_ndlp ? new_ndlp->nlp_flag : 0),
+ (new_ndlp ? new_ndlp->nlp_fc4_type : 0));
if (!new_ndlp) {
rc = memcmp(&ndlp->nlp_portname, name,
sizeof(struct lpfc_name));
- if (!rc)
- return ndlp;
- new_ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC);
- if (!new_ndlp)
- return ndlp;
- lpfc_nlp_init(vport, new_ndlp, ndlp->nlp_DID);
- } else if (!NLP_CHK_NODE_ACT(new_ndlp)) {
- rc = memcmp(&ndlp->nlp_portname, name,
- sizeof(struct lpfc_name));
- if (!rc)
+ if (!rc) {
+ if (active_rrqs_xri_bitmap)
+ mempool_free(active_rrqs_xri_bitmap,
+ phba->active_rrq_pool);
return ndlp;
- new_ndlp = lpfc_enable_node(vport, new_ndlp,
- NLP_STE_UNUSED_NODE);
- if (!new_ndlp)
+ }
+ new_ndlp = lpfc_nlp_init(vport, ndlp->nlp_DID);
+ if (!new_ndlp) {
+ if (active_rrqs_xri_bitmap)
+ mempool_free(active_rrqs_xri_bitmap,
+ phba->active_rrq_pool);
return ndlp;
- keepDID = new_ndlp->nlp_DID;
- if (phba->sli_rev == LPFC_SLI_REV4)
- memcpy(&rrq.xri_bitmap,
- &new_ndlp->active_rrqs.xri_bitmap,
- sizeof(new_ndlp->active_rrqs.xri_bitmap));
+ }
} else {
- keepDID = new_ndlp->nlp_DID;
- if (phba->sli_rev == LPFC_SLI_REV4)
- memcpy(&rrq.xri_bitmap,
- &new_ndlp->active_rrqs.xri_bitmap,
- sizeof(new_ndlp->active_rrqs.xri_bitmap));
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ active_rrqs_xri_bitmap)
+ memcpy(active_rrqs_xri_bitmap,
+ new_ndlp->active_rrqs_xri_bitmap,
+ phba->cfg_rrq_xri_bitmap_sz);
+
+ /*
+ * Unregister from backend if not done yet. Could have been
+ * skipped due to ADISC
+ */
+ lpfc_nlp_unreg_node(vport, new_ndlp);
+ }
+
+ keepDID = new_ndlp->nlp_DID;
+
+ /* At this point in this routine, we know new_ndlp will be
+ * returned. however, any previous GID_FTs that were done
+ * would have updated nlp_fc4_type in ndlp, so we must ensure
+ * new_ndlp has the right value.
+ */
+ if (test_bit(FC_FABRIC, &vport->fc_flag)) {
+ keep_nlp_fc4_type = new_ndlp->nlp_fc4_type;
+ new_ndlp->nlp_fc4_type = ndlp->nlp_fc4_type;
}
lpfc_unreg_rpi(vport, new_ndlp);
new_ndlp->nlp_DID = ndlp->nlp_DID;
new_ndlp->nlp_prev_state = ndlp->nlp_prev_state;
if (phba->sli_rev == LPFC_SLI_REV4)
- memcpy(new_ndlp->active_rrqs.xri_bitmap,
- &ndlp->active_rrqs.xri_bitmap,
- sizeof(ndlp->active_rrqs.xri_bitmap));
+ memcpy(new_ndlp->active_rrqs_xri_bitmap,
+ ndlp->active_rrqs_xri_bitmap,
+ phba->cfg_rrq_xri_bitmap_sz);
+
+ /* Lock both ndlps */
+ spin_lock_irq(&ndlp->lock);
+ spin_lock_irq(&new_ndlp->lock);
+ keep_new_nlp_flag = new_ndlp->nlp_flag;
+ keep_nlp_flag = ndlp->nlp_flag;
+ new_ndlp->nlp_flag = ndlp->nlp_flag;
+
+ /* if new_ndlp had NLP_UNREG_INP set, keep it */
+ if (test_bit(NLP_UNREG_INP, &keep_new_nlp_flag))
+ set_bit(NLP_UNREG_INP, &new_ndlp->nlp_flag);
+ else
+ clear_bit(NLP_UNREG_INP, &new_ndlp->nlp_flag);
+
+ /* if new_ndlp had NLP_RPI_REGISTERED set, keep it */
+ if (test_bit(NLP_RPI_REGISTERED, &keep_new_nlp_flag))
+ set_bit(NLP_RPI_REGISTERED, &new_ndlp->nlp_flag);
+ else
+ clear_bit(NLP_RPI_REGISTERED, &new_ndlp->nlp_flag);
+
+ /*
+ * Retain the DROPPED flag. This will take care of the init
+ * refcount when affecting the state change
+ */
+ if (test_bit(NLP_DROPPED, &keep_new_nlp_flag))
+ set_bit(NLP_DROPPED, &new_ndlp->nlp_flag);
+ else
+ clear_bit(NLP_DROPPED, &new_ndlp->nlp_flag);
+
+ ndlp->nlp_flag = keep_new_nlp_flag;
+
+ /* if ndlp had NLP_UNREG_INP set, keep it */
+ if (test_bit(NLP_UNREG_INP, &keep_nlp_flag))
+ set_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+ else
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+
+ /* if ndlp had NLP_RPI_REGISTERED set, keep it */
+ if (test_bit(NLP_RPI_REGISTERED, &keep_nlp_flag))
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
+ else
+ clear_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
+
+ /*
+ * Retain the DROPPED flag. This will take care of the init
+ * refcount when affecting the state change
+ */
+ if (test_bit(NLP_DROPPED, &keep_nlp_flag))
+ set_bit(NLP_DROPPED, &ndlp->nlp_flag);
+ else
+ clear_bit(NLP_DROPPED, &ndlp->nlp_flag);
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC)
- new_ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_unlock_irq(&new_ndlp->lock);
+ spin_unlock_irq(&ndlp->lock);
- /* Set state will put new_ndlp on to node list if not already done */
+ /* Set nlp_states accordingly */
+ keep_nlp_state = new_ndlp->nlp_state;
lpfc_nlp_set_state(vport, new_ndlp, ndlp->nlp_state);
+ /* interchange the nvme remoteport structs */
+ keep_nrport = new_ndlp->nrport;
+ new_ndlp->nrport = ndlp->nrport;
+
/* Move this back to NPR state */
if (memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)) == 0) {
- /* The new_ndlp is replacing ndlp totally, so we need
- * to put ndlp on UNUSED list and try to free it.
+ /* The ndlp doesn't have a portname yet, but does have an
+ * NPort ID. The new_ndlp portname matches the Rport's
+ * portname. Reinstantiate the new_ndlp and reset the ndlp.
*/
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3179 PLOGI confirm NEW: %x %x\n",
+ new_ndlp->nlp_DID, keepDID);
- /* Fix up the rport accordingly */
- rport = ndlp->rport;
- if (rport) {
- rdata = rport->dd_data;
- if (rdata->pnode == ndlp) {
- lpfc_nlp_put(ndlp);
- ndlp->rport = NULL;
- rdata->pnode = lpfc_nlp_get(new_ndlp);
- new_ndlp->rport = rport;
- }
- new_ndlp->nlp_type = ndlp->nlp_type;
- }
- /* We shall actually free the ndlp with both nlp_DID and
- * nlp_portname fields equals 0 to avoid any ndlp on the
- * nodelist never to be used.
+ /* Two ndlps cannot have the same did on the nodelist.
+ * The KeepDID and keep_nlp_fc4_type need to be swapped
+ * because ndlp is inflight with no WWPN.
*/
- if (ndlp->nlp_DID == 0) {
- spin_lock_irq(&phba->ndlp_lock);
- NLP_SET_FREE_REQ(ndlp);
- spin_unlock_irq(&phba->ndlp_lock);
- }
-
- /* Two ndlps cannot have the same did on the nodelist */
ndlp->nlp_DID = keepDID;
- if (phba->sli_rev == LPFC_SLI_REV4)
- memcpy(&ndlp->active_rrqs.xri_bitmap,
- &rrq.xri_bitmap,
- sizeof(ndlp->active_rrqs.xri_bitmap));
- lpfc_drop_node(vport, ndlp);
- }
- else {
+ ndlp->nlp_fc4_type = keep_nlp_fc4_type;
+ lpfc_nlp_set_state(vport, ndlp, keep_nlp_state);
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ active_rrqs_xri_bitmap)
+ memcpy(ndlp->active_rrqs_xri_bitmap,
+ active_rrqs_xri_bitmap,
+ phba->cfg_rrq_xri_bitmap_sz);
+
+ } else {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3180 PLOGI confirm SWAP: %x %x\n",
+ new_ndlp->nlp_DID, keepDID);
+
lpfc_unreg_rpi(vport, ndlp);
- /* Two ndlps cannot have the same did */
+
+ /* The ndlp and new_ndlp both have WWPNs but are swapping
+ * NPort Ids and attributes.
+ */
ndlp->nlp_DID = keepDID;
- if (phba->sli_rev == LPFC_SLI_REV4)
- memcpy(&ndlp->active_rrqs.xri_bitmap,
- &rrq.xri_bitmap,
- sizeof(ndlp->active_rrqs.xri_bitmap));
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- /* Since we are swapping the ndlp passed in with the new one
- * and the did has already been swapped, copy over the
- * state and names.
+ ndlp->nlp_fc4_type = keep_nlp_fc4_type;
+
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ active_rrqs_xri_bitmap)
+ memcpy(ndlp->active_rrqs_xri_bitmap,
+ active_rrqs_xri_bitmap,
+ phba->cfg_rrq_xri_bitmap_sz);
+
+ /* Since we are switching over to the new_ndlp,
+ * reset the old ndlp state
*/
- memcpy(&new_ndlp->nlp_portname, &ndlp->nlp_portname,
- sizeof(struct lpfc_name));
- memcpy(&new_ndlp->nlp_nodename, &ndlp->nlp_nodename,
- sizeof(struct lpfc_name));
- new_ndlp->nlp_state = ndlp->nlp_state;
- /* Fix up the rport accordingly */
- rport = ndlp->rport;
- if (rport) {
- rdata = rport->dd_data;
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- if (put_node)
- lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
- }
+ if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
+ (ndlp->nlp_state == NLP_STE_MAPPED_NODE))
+ keep_nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_set_state(vport, ndlp, keep_nlp_state);
+ ndlp->nrport = keep_nrport;
}
+
+ /*
+ * If ndlp is not associated with any rport we can drop it here else
+ * let dev_loss_tmo_callbk trigger DEVICE_RM event
+ */
+ if (!ndlp->rport && (ndlp->nlp_state == NLP_STE_NPR_NODE))
+ lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
+
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ active_rrqs_xri_bitmap)
+ mempool_free(active_rrqs_xri_bitmap,
+ phba->active_rrq_pool);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_NODE,
+ "3173 PLOGI confirm exit: new_ndlp x%x x%lx x%x\n",
+ new_ndlp->nlp_DID, new_ndlp->nlp_flag,
+ new_ndlp->nlp_fc4_type);
+
return new_ndlp;
}
@@ -1508,21 +1942,17 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
void
lpfc_end_rscn(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- if (vport->fc_flag & FC_RSCN_MODE) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
/*
* Check to see if more RSCNs came in while we were
* processing this one.
*/
if (vport->fc_rscn_id_cnt ||
- (vport->fc_flag & FC_RSCN_DISCOVERY) != 0)
+ test_bit(FC_RSCN_DISCOVERY, &vport->fc_flag))
lpfc_els_handle_rscn(vport);
- else {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_RSCN_MODE;
- spin_unlock_irq(shost->host_lock);
- }
+ else
+ clear_bit(FC_RSCN_MODE, &vport->fc_flag);
}
}
@@ -1540,57 +1970,48 @@ lpfc_end_rscn(struct lpfc_vport *vport)
static void
lpfc_cmpl_els_rrq(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
- struct lpfc_iocbq *rspiocb)
+ struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- IOCB_t *irsp;
- struct lpfc_nodelist *ndlp;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
struct lpfc_node_rrq *rrq;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
/* we pass cmdiocb to state machine which needs rspiocb as well */
rrq = cmdiocb->context_un.rrq;
- cmdiocb->context_un.rsp_iocb = rspiocb;
+ cmdiocb->rsp_iocb = rspiocb;
- irsp = &rspiocb->iocb;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"RRQ cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->un.elsreq64.remoteID);
+ ulp_status, ulp_word4,
+ get_job_els_rsp64_did(phba, cmdiocb));
- ndlp = lpfc_findnode_did(vport, irsp->un.elsreq64.remoteID);
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) || ndlp != rrq->ndlp) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "2882 RRQ completes to NPort x%x "
- "with no ndlp. Data: x%x x%x x%x\n",
- irsp->un.elsreq64.remoteID,
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpIoTag);
- goto out;
- }
/* rrq completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "2880 RRQ completes to NPort x%x "
+ "2880 RRQ completes to DID x%x "
"Data: x%x x%x x%x x%x x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout, rrq->xritag, rrq->rxid);
+ ndlp->nlp_DID, ulp_status, ulp_word4,
+ get_wqe_tmo(cmdiocb), rrq->xritag, rrq->rxid);
- if (irsp->ulpStatus) {
+ if (ulp_status) {
/* Check for retry */
- /* RRQ failed Don't print the vport to vport rjts */
- if (irsp->ulpStatus != IOSTAT_LS_RJT ||
- (((irsp->un.ulpWord[4]) >> 16 != LSRJT_INVALID_CMD) &&
- ((irsp->un.ulpWord[4]) >> 16 != LSRJT_UNABLE_TPC)) ||
- (phba)->pport->cfg_log_verbose & LOG_ELS)
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "2881 RRQ failure DID:%06X Status:x%x/x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
- }
-out:
- if (rrq)
- lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
+ /* Warn RRQ status Don't print the vport to vport rjts */
+ if (ulp_status != IOSTAT_LS_RJT ||
+ (((ulp_word4) >> 16 != LSRJT_INVALID_CMD) &&
+ ((ulp_word4) >> 16 != LSRJT_UNABLE_TPC)) ||
+ (phba)->pport->cfg_log_verbose & LOG_ELS)
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "2881 RRQ DID:%06X Status:"
+ "x%x/x%x\n",
+ ndlp->nlp_DID, ulp_status,
+ ulp_word4);
+ }
+
+ lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
return;
}
/**
@@ -1604,7 +2025,7 @@ out:
* ndlp on the vport node list that matches the remote node ID from the
* PLOGI response IOCB. If such ndlp does not exist, the PLOGI is simply
* ignored and command IOCB released. The PLOGI response IOCB status is
- * checked for error conditons. If there is error status reported, PLOGI
+ * checked for error conditions. If there is error status reported, PLOGI
* retry shall be attempted by invoking the lpfc_els_retry() routine.
* Otherwise, the lpfc_plogi_confirm_nport() routine shall be invoked on
* the ndlp and the NLP_EVT_CMPL_PLOGI state to the Discover State Machine
@@ -1618,93 +2039,138 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
IOCB_t *irsp;
- struct lpfc_nodelist *ndlp;
+ struct lpfc_nodelist *ndlp, *free_ndlp;
struct lpfc_dmabuf *prsp;
- int disc, rc, did, type;
+ bool disc;
+ struct serv_parm *sp = NULL;
+ u32 ulp_status, ulp_word4, did, iotag;
+ bool release_node = false;
/* we pass cmdiocb to state machine which needs rspiocb as well */
- cmdiocb->context_un.rsp_iocb = rspiocb;
+ cmdiocb->rsp_iocb = rspiocb;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+ did = get_job_els_rsp64_did(phba, cmdiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ iotag = get_wqe_reqtag(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ iotag = irsp->ulpIoTag;
+ }
- irsp = &rspiocb->iocb;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"PLOGI cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->un.elsreq64.remoteID);
+ ulp_status, ulp_word4, did);
- ndlp = lpfc_findnode_did(vport, irsp->un.elsreq64.remoteID);
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ ndlp = lpfc_findnode_did(vport, did);
+ if (!ndlp) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0136 PLOGI completes to NPort x%x "
"with no ndlp. Data: x%x x%x x%x\n",
- irsp->un.elsreq64.remoteID,
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpIoTag);
- goto out;
+ did, ulp_status, ulp_word4, iotag);
+ goto out_freeiocb;
}
/* Since ndlp can be freed in the disc state machine, note if this node
* is being used during discovery.
*/
- spin_lock_irq(shost->host_lock);
- disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
- ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
- rc = 0;
+ disc = test_and_clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
/* PLOGI completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0102 PLOGI completes to NPort x%x "
- "Data: x%x x%x x%x x%x x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout, disc, vport->num_disc_nodes);
+ "0102 PLOGI completes to NPort x%06x "
+ "IoTag x%x Data: x%x x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, iotag,
+ ndlp->nlp_fc4_type,
+ ulp_status, ulp_word4,
+ disc, vport->num_disc_nodes);
+
/* Check to see if link went down during discovery */
if (lpfc_els_chk_latt(vport)) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
goto out;
}
- /* ndlp could be freed in DSM, save these values now */
- type = ndlp->nlp_type;
- did = ndlp->nlp_DID;
-
- if (irsp->ulpStatus) {
+ if (ulp_status) {
/* Check for retry */
if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
/* ELS command is being retried */
- if (disc) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
- }
+ if (disc)
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
goto out;
}
- /* PLOGI failed Don't print the vport to vport rjts */
- if (irsp->ulpStatus != IOSTAT_LS_RJT ||
- (((irsp->un.ulpWord[4]) >> 16 != LSRJT_INVALID_CMD) &&
- ((irsp->un.ulpWord[4]) >> 16 != LSRJT_UNABLE_TPC)) ||
- (phba)->pport->cfg_log_verbose & LOG_ELS)
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "2753 PLOGI failure DID:%06X Status:x%x/x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
+ /* Warn PLOGI status Don't print the vport to vport rjts */
+ if (ulp_status != IOSTAT_LS_RJT ||
+ (((ulp_word4) >> 16 != LSRJT_INVALID_CMD) &&
+ ((ulp_word4) >> 16 != LSRJT_UNABLE_TPC)) ||
+ (phba)->pport->cfg_log_verbose & LOG_ELS)
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "2753 PLOGI DID:%06X "
+ "Status:x%x/x%x\n",
+ ndlp->nlp_DID, ulp_status,
+ ulp_word4);
+
/* Do not call DSM for lpfc_els_abort'ed ELS cmds */
- if (lpfc_error_lost_link(irsp))
- rc = NLP_STE_FREED_NODE;
- else
- rc = lpfc_disc_state_machine(vport, ndlp, cmdiocb,
- NLP_EVT_CMPL_PLOGI);
+ if (!lpfc_error_lost_link(vport, ulp_status, ulp_word4))
+ lpfc_disc_state_machine(vport, ndlp, cmdiocb,
+ NLP_EVT_CMPL_PLOGI);
+
+ /* If a PLOGI collision occurred, the node needs to continue
+ * with the reglogin process.
+ */
+ spin_lock_irq(&ndlp->lock);
+ if ((test_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag) ||
+ test_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag)) &&
+ ndlp->nlp_state == NLP_STE_REG_LOGIN_ISSUE) {
+ spin_unlock_irq(&ndlp->lock);
+ goto out;
+ }
+
+ /* No PLOGI collision and the node is not registered with the
+ * scsi or nvme transport. It is no longer an active node. Just
+ * start the device remove process.
+ */
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag))
+ release_node = true;
+ }
+ spin_unlock_irq(&ndlp->lock);
+
+ if (release_node)
+ lpfc_disc_state_machine(vport, ndlp, cmdiocb,
+ NLP_EVT_DEVICE_RM);
} else {
/* Good status, call state machine */
- prsp = list_entry(((struct lpfc_dmabuf *)
- cmdiocb->context2)->list.next,
- struct lpfc_dmabuf, list);
+ prsp = list_get_first(&cmdiocb->cmd_dmabuf->list,
+ struct lpfc_dmabuf, list);
+ if (!prsp)
+ goto out;
+ if (!lpfc_is_els_acc_rsp(prsp))
+ goto out;
ndlp = lpfc_plogi_confirm_nport(phba, prsp->virt, ndlp);
- rc = lpfc_disc_state_machine(vport, ndlp, cmdiocb,
- NLP_EVT_CMPL_PLOGI);
+
+ sp = (struct serv_parm *)((u8 *)prsp->virt +
+ sizeof(u32));
+
+ ndlp->vmid_support = 0;
+ if ((phba->cfg_vmid_app_header && sp->cmn.app_hdr_support) ||
+ (phba->cfg_vmid_priority_tagging &&
+ sp->cmn.priority_tagging)) {
+ lpfc_printf_log(phba, KERN_DEBUG, LOG_ELS,
+ "4018 app_hdr_support %d tagging %d DID x%x\n",
+ sp->cmn.app_hdr_support,
+ sp->cmn.priority_tagging,
+ ndlp->nlp_DID);
+ /* if the dest port supports VMID, mark it in ndlp */
+ ndlp->vmid_support = 1;
+ }
+
+ lpfc_disc_state_machine(vport, ndlp, cmdiocb,
+ NLP_EVT_CMPL_PLOGI);
}
if (disc && vport->num_disc_nodes) {
@@ -1712,9 +2178,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_more_plogi(vport);
if (vport->num_disc_nodes == 0) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NDISC_ACTIVE;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_NDISC_ACTIVE, &vport->fc_flag);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
@@ -1722,7 +2186,16 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
out:
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
+ "PLOGI Cmpl PUT: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
+
+out_freeiocb:
+ /* Release the reference on the original I/O request. */
+ free_ndlp = cmdiocb->ndlp;
+
lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(free_ndlp);
return;
}
@@ -1735,13 +2208,12 @@ out:
* This routine issues a Port Login (PLOGI) command to a remote N_Port
* (with the @did) for a @vport. Before issuing a PLOGI to a remote N_Port,
* the ndlp with the remote N_Port DID must exist on the @vport's ndlp list.
- * This routine constructs the proper feilds of the PLOGI IOCB and invokes
+ * This routine constructs the proper fields of the PLOGI IOCB and invokes
* the lpfc_sli_issue_iocb() routine to send out PLOGI ELS command.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the PLOGI ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding
+ * the ndlp and the reference to ndlp will be stored into the ndlp field
+ * of the IOCB for the completion callback function to the PLOGI ELS command.
*
* Return code
* 0 - Successfully issued a plogi for @vport
@@ -1752,29 +2224,44 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
{
struct lpfc_hba *phba = vport->phba;
struct serv_parm *sp;
- IOCB_t *icmd;
struct lpfc_nodelist *ndlp;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int ret;
- psli = &phba->sli;
-
ndlp = lpfc_findnode_did(vport, did);
- if (ndlp && !NLP_CHK_NODE_ACT(ndlp))
- ndlp = NULL;
+ if (!ndlp)
+ return 1;
+
+ /* Defer the processing of the issue PLOGI until after the
+ * outstanding UNREG_RPI mbox command completes, unless we
+ * are going offline. This logic does not apply for Fabric DIDs
+ */
+ if ((test_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag) ||
+ test_bit(NLP_UNREG_INP, &ndlp->nlp_flag)) &&
+ ((ndlp->nlp_DID & Fabric_DID_MASK) != Fabric_DID_MASK) &&
+ !test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "4110 Issue PLOGI x%x deferred "
+ "on NPort x%x rpi x%x flg x%lx Data:"
+ " x%px\n",
+ ndlp->nlp_defer_did, ndlp->nlp_DID,
+ ndlp->nlp_rpi, ndlp->nlp_flag, ndlp);
+
+ /* We can only defer 1st PLOGI */
+ if (ndlp->nlp_defer_did == NLP_EVT_NOTHING_PENDING)
+ ndlp->nlp_defer_did = did;
+ return 0;
+ }
- /* If ndlp is not NULL, we will bump the reference count on it */
cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did,
ELS_CMD_PLOGI);
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
/* For PLOGI request, remainder of payload is service parameters */
*((uint32_t *) (pcmd)) = ELS_CMD_PLOGI;
@@ -1786,7 +2273,8 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
* If we are a N-port connected to a Fabric, fix-up paramm's so logins
* to device on remote loops work.
*/
- if ((vport->fc_flag & FC_FABRIC) && !(vport->fc_flag & FC_PUBLIC_LOOP))
+ if (test_bit(FC_FABRIC, &vport->fc_flag) &&
+ !test_bit(FC_PUBLIC_LOOP, &vport->fc_flag))
sp->cmn.altBbCredit = 1;
if (sp->cmn.fcphLow < FC_PH_4_3)
@@ -1795,18 +2283,51 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
if (sp->cmn.fcphHigh < FC_PH3)
sp->cmn.fcphHigh = FC_PH3;
+ sp->cmn.valid_vendor_ver_level = 0;
+ memset(sp->un.vendorVersion, 0, sizeof(sp->un.vendorVersion));
+ if (!test_bit(FC_PT2PT, &vport->fc_flag))
+ sp->cmn.bbRcvSizeMsb &= 0xF;
+
+ /* Check if the destination port supports VMID */
+ ndlp->vmid_support = 0;
+ if (vport->vmid_priority_tagging)
+ sp->cmn.priority_tagging = 1;
+ else if (phba->cfg_vmid_app_header &&
+ bf_get(lpfc_ftr_ashdr, &phba->sli4_hba.sli4_flags))
+ sp->cmn.app_hdr_support = 1;
+
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"Issue PLOGI: did:x%x",
did, 0, 0);
+ /* If our firmware supports this feature, convey that
+ * information to the target using the vendor specific field.
+ */
+ if (phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) {
+ sp->cmn.valid_vendor_ver_level = 1;
+ sp->un.vv.vid = cpu_to_be32(LPFC_VV_EMLX_ID);
+ sp->un.vv.flags = cpu_to_be32(LPFC_VV_SUPPRESS_RSP);
+ }
+
phba->fc_stat.elsXmitPLOGI++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi;
- ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_plogi;
- if (ret == IOCB_ERROR) {
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+ "Issue PLOGI: did:x%x refcnt %d",
+ did, kref_read(&ndlp->kref), 0);
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
+
+ ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (ret) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
return 0;
}
@@ -1828,60 +2349,127 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- IOCB_t *irsp;
- struct lpfc_sli *psli;
struct lpfc_nodelist *ndlp;
+ char *mode;
+ u32 ulp_status;
+ u32 ulp_word4;
+ bool release_node = false;
- psli = &phba->sli;
/* we pass cmdiocb to state machine which needs rspiocb as well */
- cmdiocb->context_un.rsp_iocb = rspiocb;
+ cmdiocb->rsp_iocb = rspiocb;
- irsp = &(rspiocb->iocb);
- ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_PRLI_SND;
- spin_unlock_irq(shost->host_lock);
+ ndlp = cmdiocb->ndlp;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+
+ clear_bit(NLP_PRLI_SND, &ndlp->nlp_flag);
+
+ /* Driver supports multiple FC4 types. Counters matter. */
+ spin_lock_irq(&ndlp->lock);
+ vport->fc_prli_sent--;
+ ndlp->fc4_prli_sent--;
+ spin_unlock_irq(&ndlp->lock);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"PRLI cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
+ ulp_status, ulp_word4,
ndlp->nlp_DID);
+
/* PRLI completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0103 PRLI completes to NPort x%x "
- "Data: x%x x%x x%x x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout, vport->num_disc_nodes);
+ "0103 PRLI completes to NPort x%06x "
+ "Data: x%x x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, ulp_status, ulp_word4,
+ vport->num_disc_nodes, ndlp->fc4_prli_sent,
+ ndlp->fc4_xpt_flags);
- vport->fc_prli_sent--;
/* Check to see if link went down during discovery */
if (lpfc_els_chk_latt(vport))
goto out;
- if (irsp->ulpStatus) {
+ if (ulp_status) {
/* Check for retry */
if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
/* ELS command is being retried */
goto out;
}
- /* PRLI failed */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "2754 PRLI failure DID:%06X Status:x%x/x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
- /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
- if (lpfc_error_lost_link(irsp))
- goto out;
+
+ /* If we don't send GFT_ID to Fabric, a PRLI error
+ * could be expected.
+ */
+ if (test_bit(FC_FABRIC, &vport->fc_flag) ||
+ vport->cfg_enable_fc4_type != LPFC_ENABLE_BOTH)
+ mode = KERN_WARNING;
else
+ mode = KERN_INFO;
+
+ /* Warn PRLI status */
+ lpfc_vlog_msg(vport, mode, LOG_ELS,
+ "2754 PRLI DID:%06X Status:x%x/x%x, "
+ "data: x%x x%x x%lx\n",
+ ndlp->nlp_DID, ulp_status,
+ ulp_word4, ndlp->nlp_state,
+ ndlp->fc4_prli_sent, ndlp->nlp_flag);
+
+ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+ if (!lpfc_error_lost_link(vport, ulp_status, ulp_word4))
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
NLP_EVT_CMPL_PRLI);
- } else
- /* Good status, call state machine */
+
+ /* The following condition catches an inflight transition
+ * mismatch typically caused by an RSCN. Skip any
+ * processing to allow recovery.
+ */
+ if ((ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE &&
+ ndlp->nlp_state <= NLP_STE_REG_LOGIN_ISSUE) ||
+ (ndlp->nlp_state == NLP_STE_NPR_NODE &&
+ test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag))) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+ "2784 PRLI cmpl: Allow Node recovery "
+ "DID x%06x nstate x%x nflag x%lx\n",
+ ndlp->nlp_DID, ndlp->nlp_state,
+ ndlp->nlp_flag);
+ goto out;
+ }
+
+ /*
+ * For P2P topology, retain the node so that PLOGI can be
+ * attempted on it again.
+ */
+ if (test_bit(FC_PT2PT, &vport->fc_flag))
+ goto out;
+
+ /* As long as this node is not registered with the SCSI
+ * or NVMe transport and no other PRLIs are outstanding,
+ * it is no longer an active node. Otherwise devloss
+ * handles the final cleanup.
+ */
+ spin_lock_irq(&ndlp->lock);
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) &&
+ !ndlp->fc4_prli_sent) {
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag))
+ release_node = true;
+ }
+ spin_unlock_irq(&ndlp->lock);
+
+ if (release_node)
+ lpfc_disc_state_machine(vport, ndlp, cmdiocb,
+ NLP_EVT_DEVICE_RM);
+ } else {
+ /* Good status, call state machine. However, if another
+ * PRLI is outstanding, don't call the state machine
+ * because final disposition to Mapped or Unmapped is
+ * completed there.
+ */
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
NLP_EVT_CMPL_PRLI);
+ }
+
out:
lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
return;
}
@@ -1897,10 +2485,9 @@ out:
* is put to the IOCB completion callback func field before invoking the
* routine lpfc_sli_issue_iocb() to send out PRLI command.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the PRLI ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the PRLI ELS command.
*
* Return code
* 0 - successfully issued prli iocb command for @vport
@@ -1910,65 +2497,179 @@ int
lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint8_t retry)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ int rc = 0;
struct lpfc_hba *phba = vport->phba;
PRLI *npr;
- IOCB_t *icmd;
+ struct lpfc_nvme_prli *npr_nvme;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
uint16_t cmdsize;
+ u32 local_nlp_type, elscmd;
+
+ /*
+ * If we are in RSCN mode, the FC4 types supported from a
+ * previous GFT_ID command may not be accurate. So, if we
+ * are a NVME Initiator, always look for the possibility of
+ * the remote NPort beng a NVME Target.
+ */
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ test_bit(FC_RSCN_MODE, &vport->fc_flag) &&
+ vport->nvmei_support)
+ ndlp->nlp_fc4_type |= NLP_FC4_NVME;
+ local_nlp_type = ndlp->nlp_fc4_type;
+
+ /* This routine will issue 1 or 2 PRLIs, so zero all the ndlp
+ * fields here before any of them can complete.
+ */
+ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR);
+ ndlp->nlp_type &= ~(NLP_NVME_TARGET | NLP_NVME_INITIATOR);
+ ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ clear_bit(NLP_FIRSTBURST, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ ndlp->nvme_fb_size = 0;
+
+ send_next_prli:
+ if (local_nlp_type & NLP_FC4_FCP) {
+ /* Payload is 4 + 16 = 20 x14 bytes. */
+ cmdsize = (sizeof(uint32_t) + sizeof(PRLI));
+ elscmd = ELS_CMD_PRLI;
+ } else if (local_nlp_type & NLP_FC4_NVME) {
+ /* Payload is 4 + 20 = 24 x18 bytes. */
+ cmdsize = (sizeof(uint32_t) + sizeof(struct lpfc_nvme_prli));
+ elscmd = ELS_CMD_NVMEPRLI;
+ } else {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "3083 Unknown FC_TYPE x%x ndlp x%06x\n",
+ ndlp->nlp_fc4_type, ndlp->nlp_DID);
+ return 1;
+ }
+
+ /* SLI3 ports don't support NVME. If this rport is a strict NVME
+ * FC4 type, implicitly LOGO.
+ */
+ if (phba->sli_rev == LPFC_SLI_REV3 &&
+ ndlp->nlp_fc4_type == NLP_FC4_NVME) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "3088 Rport fc4 type 0x%x not supported by SLI3 adapter\n",
+ ndlp->nlp_type);
+ lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
+ return 1;
+ }
- cmdsize = (sizeof(uint32_t) + sizeof(PRLI));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
- ndlp->nlp_DID, ELS_CMD_PRLI);
+ ndlp->nlp_DID, elscmd);
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
/* For PRLI request, remainder of payload is service parameters */
- memset(pcmd, 0, (sizeof(PRLI) + sizeof(uint32_t)));
- *((uint32_t *) (pcmd)) = ELS_CMD_PRLI;
- pcmd += sizeof(uint32_t);
+ memset(pcmd, 0, cmdsize);
- /* For PRLI, remainder of payload is PRLI parameter page */
- npr = (PRLI *) pcmd;
- /*
- * If our firmware version is 3.20 or later,
- * set the following bits for FC-TAPE support.
- */
- if (phba->vpd.rev.feaLevelHigh >= 0x02) {
- npr->ConfmComplAllowed = 1;
- npr->Retry = 1;
- npr->TaskRetryIdReq = 1;
+ if (local_nlp_type & NLP_FC4_FCP) {
+ /* Remainder of payload is FCP PRLI parameter page.
+ * Note: this data structure is defined as
+ * BE/LE in the structure definition so no
+ * byte swap call is made.
+ */
+ *((uint32_t *)(pcmd)) = ELS_CMD_PRLI;
+ pcmd += sizeof(uint32_t);
+ npr = (PRLI *)pcmd;
+
+ /*
+ * If our firmware version is 3.20 or later,
+ * set the following bits for FC-TAPE support.
+ */
+ if (phba->vpd.rev.feaLevelHigh >= 0x02) {
+ npr->ConfmComplAllowed = 1;
+ npr->Retry = 1;
+ npr->TaskRetryIdReq = 1;
+ }
+ npr->estabImagePair = 1;
+ npr->readXferRdyDis = 1;
+ if (vport->cfg_first_burst_size)
+ npr->writeXferRdyDis = 1;
+
+ /* For FCP support */
+ npr->prliType = PRLI_FCP_TYPE;
+ npr->initiatorFunc = 1;
+ elsiocb->cmd_flag |= LPFC_PRLI_FCP_REQ;
+
+ /* Remove FCP type - processed. */
+ local_nlp_type &= ~NLP_FC4_FCP;
+ } else if (local_nlp_type & NLP_FC4_NVME) {
+ /* Remainder of payload is NVME PRLI parameter page.
+ * This data structure is the newer definition that
+ * uses bf macros so a byte swap is required.
+ */
+ *((uint32_t *)(pcmd)) = ELS_CMD_NVMEPRLI;
+ pcmd += sizeof(uint32_t);
+ npr_nvme = (struct lpfc_nvme_prli *)pcmd;
+ bf_set(prli_type_code, npr_nvme, PRLI_NVME_TYPE);
+ bf_set(prli_estabImagePair, npr_nvme, 0); /* Should be 0 */
+ if (phba->nsler) {
+ bf_set(prli_nsler, npr_nvme, 1);
+ bf_set(prli_conf, npr_nvme, 1);
+ }
+
+ /* Only initiators request first burst. */
+ if ((phba->cfg_nvme_enable_fb) &&
+ !phba->nvmet_support)
+ bf_set(prli_fba, npr_nvme, 1);
+
+ if (phba->nvmet_support) {
+ bf_set(prli_tgt, npr_nvme, 1);
+ bf_set(prli_disc, npr_nvme, 1);
+ } else {
+ bf_set(prli_init, npr_nvme, 1);
+ bf_set(prli_conf, npr_nvme, 1);
+ }
+
+ npr_nvme->word1 = cpu_to_be32(npr_nvme->word1);
+ npr_nvme->word4 = cpu_to_be32(npr_nvme->word4);
+ elsiocb->cmd_flag |= LPFC_PRLI_NVME_REQ;
+
+ /* Remove NVME type - processed. */
+ local_nlp_type &= ~NLP_FC4_NVME;
}
- npr->estabImagePair = 1;
- npr->readXferRdyDis = 1;
- /* For FCP support */
- npr->prliType = PRLI_FCP_TYPE;
- npr->initiatorFunc = 1;
+ phba->fc_stat.elsXmitPRLI++;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_prli;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
- "Issue PRLI: did:x%x",
- ndlp->nlp_DID, 0, 0);
+ "Issue PRLI: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
- phba->fc_stat.elsXmitPRLI++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_prli;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_PRLI_SND;
- spin_unlock_irq(shost->host_lock);
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
- IOCB_ERROR) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_PRLI_SND;
- spin_unlock_irq(shost->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
+ /* The vport counters are used for lpfc_scan_finished, but
+ * the ndlp is used to track outstanding PRLIs for different
+ * FC4 types.
+ */
+ set_bit(NLP_PRLI_SND, &ndlp->nlp_flag);
+ spin_lock_irq(&ndlp->lock);
vport->fc_prli_sent++;
- return 0;
+ ndlp->fc4_prli_sent++;
+ spin_unlock_irq(&ndlp->lock);
+
+ /* The driver supports 2 FC4 types. Make sure
+ * a PRLI is issued for all types before exiting.
+ */
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ local_nlp_type & (NLP_FC4_FCP | NLP_FC4_NVME))
+ goto send_next_prli;
+ else
+ return 0;
}
/**
@@ -1990,7 +2691,7 @@ lpfc_rscn_disc(struct lpfc_vport *vport)
/* RSCN discovery */
/* go thru NPR nodes and issue ELS PLOGIs */
- if (vport->fc_npr_cnt)
+ if (atomic_read(&vport->fc_npr_cnt))
if (lpfc_els_disc_plogi(vport))
return;
@@ -2010,7 +2711,6 @@ lpfc_rscn_disc(struct lpfc_vport *vport)
static void
lpfc_adisc_done(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
/*
@@ -2018,8 +2718,25 @@ lpfc_adisc_done(struct lpfc_vport *vport)
* and continue discovery.
*/
if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
- !(vport->fc_flag & FC_RSCN_MODE) &&
+ !test_bit(FC_RSCN_MODE, &vport->fc_flag) &&
(phba->sli_rev < LPFC_SLI_REV4)) {
+
+ /*
+ * If link is down, clear_la and reg_vpi will be done after
+ * flogi following a link up event
+ */
+ if (!lpfc_is_link_up(phba))
+ return;
+
+ /* The ADISCs are complete. Doesn't matter if they
+ * succeeded or failed because the ADISC completion
+ * routine guarantees to call the state machine and
+ * the RPI is either unregistered (failed ADISC response)
+ * or the RPI is still valid and the node is marked
+ * mapped for a target. The exchanges should be in the
+ * correct state. This code is specific to SLI3.
+ */
+ lpfc_issue_clear_la(phba, vport);
lpfc_issue_reg_vpi(phba, vport);
return;
}
@@ -2029,17 +2746,14 @@ lpfc_adisc_done(struct lpfc_vport *vport)
*/
if (vport->port_state < LPFC_VPORT_READY) {
/* If we get here, there is nothing to ADISC */
- if (vport->port_type == LPFC_PHYSICAL_PORT)
- lpfc_issue_clear_la(phba, vport);
- if (!(vport->fc_flag & FC_ABORT_DISCOVERY)) {
+ lpfc_issue_clear_la(phba, vport);
+ if (!test_bit(FC_ABORT_DISCOVERY, &vport->fc_flag)) {
vport->num_disc_nodes = 0;
/* go thru NPR list, issue ELS PLOGIs */
- if (vport->fc_npr_cnt)
+ if (atomic_read(&vport->fc_npr_cnt))
lpfc_els_disc_plogi(vport);
if (!vport->num_disc_nodes) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NDISC_ACTIVE;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_NDISC_ACTIVE, &vport->fc_flag);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
@@ -2061,21 +2775,20 @@ lpfc_adisc_done(struct lpfc_vport *vport)
void
lpfc_more_adisc(struct lpfc_vport *vport)
{
- int sentadisc;
-
if (vport->num_disc_nodes)
vport->num_disc_nodes--;
/* Continue discovery with <num_disc_nodes> ADISCs to go */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0210 Continue discovery with %d ADISCs to go "
- "Data: x%x x%x x%x\n",
- vport->num_disc_nodes, vport->fc_adisc_cnt,
+ "Data: x%x x%lx x%x\n",
+ vport->num_disc_nodes,
+ atomic_read(&vport->fc_adisc_cnt),
vport->fc_flag, vport->port_state);
/* Check to see if there are more ADISCs to be sent */
- if (vport->fc_flag & FC_NLP_MORE) {
+ if (test_bit(FC_NLP_MORE, &vport->fc_flag)) {
lpfc_set_disctmo(vport);
/* go thru NPR nodes and issue any remaining ELS ADISCs */
- sentadisc = lpfc_els_disc_adisc(vport);
+ lpfc_els_disc_adisc(vport);
}
if (!vport->num_disc_nodes)
lpfc_adisc_done(vport);
@@ -2103,64 +2816,86 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
IOCB_t *irsp;
struct lpfc_nodelist *ndlp;
- int disc;
+ bool disc;
+ u32 ulp_status, ulp_word4, tmo, iotag;
+ bool release_node = false;
/* we pass cmdiocb to state machine which needs rspiocb as well */
- cmdiocb->context_un.rsp_iocb = rspiocb;
+ cmdiocb->rsp_iocb = rspiocb;
+
+ ndlp = cmdiocb->ndlp;
- irsp = &(rspiocb->iocb);
- ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tmo = get_wqe_tmo(cmdiocb);
+ iotag = get_wqe_reqtag(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ tmo = irsp->ulpTimeout;
+ iotag = irsp->ulpIoTag;
+ }
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"ADISC cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
+ ulp_status, ulp_word4,
ndlp->nlp_DID);
/* Since ndlp can be freed in the disc state machine, note if this node
* is being used during discovery.
*/
- spin_lock_irq(shost->host_lock);
- disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
- ndlp->nlp_flag &= ~(NLP_ADISC_SND | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
+ disc = test_and_clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ clear_bit(NLP_ADISC_SND, &ndlp->nlp_flag);
/* ADISC completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0104 ADISC completes to NPort x%x "
- "Data: x%x x%x x%x x%x x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout, disc, vport->num_disc_nodes);
+ "IoTag x%x Data: x%x x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, iotag,
+ ulp_status, ulp_word4,
+ tmo, disc, vport->num_disc_nodes);
+
/* Check to see if link went down during discovery */
if (lpfc_els_chk_latt(vport)) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
goto out;
}
- if (irsp->ulpStatus) {
+ if (ulp_status) {
/* Check for retry */
if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
/* ELS command is being retried */
if (disc) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
lpfc_set_disctmo(vport);
}
goto out;
}
- /* ADISC failed */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "2755 ADISC failure DID:%06X Status:x%x/x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
- /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
- if (!lpfc_error_lost_link(irsp))
+ /* Warn ADISC status */
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "2755 ADISC DID:%06X Status:x%x/x%x\n",
+ ndlp->nlp_DID, ulp_status,
+ ulp_word4);
+ lpfc_disc_state_machine(vport, ndlp, cmdiocb,
+ NLP_EVT_CMPL_ADISC);
+
+ /* As long as this node is not registered with the SCSI or NVMe
+ * transport, it is no longer an active node. Otherwise
+ * devloss handles the final cleanup.
+ */
+ spin_lock_irq(&ndlp->lock);
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ if (!test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag))
+ release_node = true;
+ }
+ spin_unlock_irq(&ndlp->lock);
+
+ if (release_node)
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
- NLP_EVT_CMPL_ADISC);
+ NLP_EVT_DEVICE_RM);
} else
/* Good status, call state machine */
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
@@ -2171,6 +2906,7 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_more_adisc(vport);
out:
lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
return;
}
@@ -2185,10 +2921,9 @@ out:
* and states of the ndlp, and invokes the lpfc_sli_issue_iocb() routine
* to issue the ADISC ELS command.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the ADISC ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the ADISC ELS command.
*
* Return code
* 0 - successfully issued adisc
@@ -2198,10 +2933,9 @@ int
lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint8_t retry)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ int rc = 0;
struct lpfc_hba *phba = vport->phba;
ADISC *ap;
- IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
uint16_t cmdsize;
@@ -2212,8 +2946,7 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
/* For ADISC request, remainder of payload is service parameters */
*((uint32_t *) (pcmd)) = ELS_CMD_ADISC;
@@ -2226,24 +2959,31 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
memcpy(&ap->nodeName, &vport->fc_nodename, sizeof(struct lpfc_name));
ap->DID = be32_to_cpu(vport->fc_myDID);
+ phba->fc_stat.elsXmitADISC++;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_adisc;
+ set_bit(NLP_ADISC_SND, &ndlp->nlp_flag);
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ goto err;
+ }
+
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
- "Issue ADISC: did:x%x",
- ndlp->nlp_DID, 0, 0);
+ "Issue ADISC: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
- phba->fc_stat.elsXmitADISC++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_adisc;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_ADISC_SND;
- spin_unlock_irq(shost->host_lock);
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
- IOCB_ERROR) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_ADISC_SND;
- spin_unlock_irq(shost->host_lock);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
- return 1;
+ lpfc_nlp_put(ndlp);
+ goto err;
}
+
return 0;
+
+err:
+ clear_bit(NLP_ADISC_SND, &ndlp->nlp_flag);
+ return 1;
}
/**
@@ -2255,92 +2995,128 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* This routine is the completion function for issuing the ELS Logout (LOGO)
* command. If no error status was reported from the LOGO response, the
* state machine of the associated ndlp shall be invoked for transition with
- * respect to NLP_EVT_CMPL_LOGO event. Otherwise, if error status was reported,
- * the lpfc_els_retry() routine will be invoked to retry the LOGO command.
+ * respect to NLP_EVT_CMPL_LOGO event.
**/
static void
lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
struct lpfc_vport *vport = ndlp->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
IOCB_t *irsp;
- struct lpfc_sli *psli;
- struct lpfcMboxq *mbox;
+ uint32_t skip_recovery = 0;
+ int wake_up_waiter = 0;
+ u32 ulp_status;
+ u32 ulp_word4;
+ u32 tmo, iotag;
- psli = &phba->sli;
/* we pass cmdiocb to state machine which needs rspiocb as well */
- cmdiocb->context_un.rsp_iocb = rspiocb;
+ cmdiocb->rsp_iocb = rspiocb;
- irsp = &(rspiocb->iocb);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_LOGO_SND;
- spin_unlock_irq(shost->host_lock);
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tmo = get_wqe_tmo(cmdiocb);
+ iotag = get_wqe_reqtag(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ tmo = irsp->ulpTimeout;
+ iotag = irsp->ulpIoTag;
+ }
+
+ clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag);
+ if (test_and_clear_bit(NLP_WAIT_FOR_LOGO, &ndlp->save_flags))
+ wake_up_waiter = 1;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"LOGO cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
+ ulp_status, ulp_word4,
ndlp->nlp_DID);
+
/* LOGO completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0105 LOGO completes to NPort x%x "
+ "IoTag x%x refcnt %d nflags x%lx xflags x%x "
"Data: x%x x%x x%x x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout, vport->num_disc_nodes);
- /* Check to see if link went down during discovery */
- if (lpfc_els_chk_latt(vport))
+ ndlp->nlp_DID, iotag,
+ kref_read(&ndlp->kref), ndlp->nlp_flag,
+ ndlp->fc4_xpt_flags, ulp_status, ulp_word4,
+ tmo, vport->num_disc_nodes);
+
+ if (lpfc_els_chk_latt(vport)) {
+ skip_recovery = 1;
goto out;
+ }
- if (ndlp->nlp_flag & NLP_TARGET_REMOVE) {
- /* NLP_EVT_DEVICE_RM should unregister the RPI
- * which should abort all outstanding IOs.
- */
+ /* The LOGO will not be retried on failure. A LOGO was
+ * issued to the remote rport and a ACC or RJT or no Answer are
+ * all acceptable. Note the failure and move forward with
+ * discovery. The PLOGI will retry.
+ */
+ if (ulp_status) {
+ /* Warn LOGO status */
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "2756 LOGO, No Retry DID:%06X "
+ "Status:x%x/x%x\n",
+ ndlp->nlp_DID, ulp_status,
+ ulp_word4);
+
+ /* Call NLP_EVT_DEVICE_RM if link is down or LOGO is aborted */
+ if (lpfc_error_lost_link(vport, ulp_status, ulp_word4))
+ skip_recovery = 1;
+ }
+
+ /* Call state machine. This will unregister the rpi if needed. */
+ lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO);
+
+out:
+ /* At this point, the LOGO processing is complete. NOTE: For a
+ * pt2pt topology, we are assuming the NPortID will only change
+ * on link up processing. For a LOGO / PLOGI initiated by the
+ * Initiator, we are assuming the NPortID is not going to change.
+ */
+
+ if (wake_up_waiter && ndlp->logo_waitq)
+ wake_up(ndlp->logo_waitq);
+ /*
+ * If the node is a target, the handling attempts to recover the port.
+ * For any other port type, the rpi is unregistered as an implicit
+ * LOGO.
+ */
+ if (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET) &&
+ skip_recovery == 0) {
+ lpfc_cancel_retry_delay_tmo(vport, ndlp);
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3187 LOGO completes to NPort x%x: Start "
+ "Recovery Data: x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, ulp_status,
+ ulp_word4, tmo,
+ vport->num_disc_nodes);
+
+ lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
+
+ lpfc_disc_start(vport);
+ return;
+ }
+
+ /* Cleanup path for failed REG_RPI handling. If REG_RPI fails, the
+ * driver sends a LOGO to the rport to cleanup. For fabric and
+ * initiator ports cleanup the node as long as it the node is not
+ * register with the transport.
+ */
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
NLP_EVT_DEVICE_RM);
- goto out;
}
- if (irsp->ulpStatus) {
- /* Check for retry */
- if (lpfc_els_retry(phba, cmdiocb, rspiocb))
- /* ELS command is being retried */
- goto out;
- /* LOGO failed */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "2756 LOGO failure DID:%06X Status:x%x/x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
- /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
- if (lpfc_error_lost_link(irsp))
- goto out;
- else
- lpfc_disc_state_machine(vport, ndlp, cmdiocb,
- NLP_EVT_CMPL_LOGO);
- } else
- /* Good status, call state machine.
- * This will unregister the rpi if needed.
- */
- lpfc_disc_state_machine(vport, ndlp, cmdiocb,
- NLP_EVT_CMPL_LOGO);
-out:
+ /* Driver is done with the I/O. */
lpfc_els_free_iocb(phba, cmdiocb);
- /* If we are in pt2pt mode, we could rcv new S_ID on PLOGI */
- if ((vport->fc_flag & FC_PT2PT) &&
- !(vport->fc_flag & FC_PT2PT_PLOGI)) {
- phba->pport->fc_myDID = 0;
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (mbox) {
- lpfc_config_link(phba, mbox);
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->vport = vport;
- if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) ==
- MBX_NOT_FINISHED) {
- mempool_free(mbox, phba->mbox_mem_pool);
- }
- }
- }
- return;
+ lpfc_nlp_put(ndlp);
}
/**
@@ -2354,10 +3130,11 @@ out:
* payload of the IOCB, properly sets up the @ndlp state, and invokes the
* lpfc_sli_issue_iocb() routine to send out the LOGO ELS command.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the LOGO ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the LOGO ELS command.
+ *
+ * Callers of this routine are expected to unregister the RPI first
*
* Return code
* 0 - successfully issued logo
@@ -2367,20 +3144,14 @@ int
lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint8_t retry)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
- spin_lock_irq(shost->host_lock);
- if (ndlp->nlp_flag & NLP_LOGO_SND) {
- spin_unlock_irq(shost->host_lock);
+ if (test_bit(NLP_LOGO_SND, &ndlp->nlp_flag))
return 0;
- }
- spin_unlock_irq(shost->host_lock);
cmdsize = (2 * sizeof(uint32_t)) + sizeof(struct lpfc_name);
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
@@ -2388,8 +3159,7 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_LOGO;
pcmd += sizeof(uint32_t);
@@ -2398,25 +3168,36 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
pcmd += sizeof(uint32_t);
memcpy(pcmd, &vport->fc_portname, sizeof(struct lpfc_name));
+ phba->fc_stat.elsXmitLOGO++;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_logo;
+ set_bit(NLP_LOGO_SND, &ndlp->nlp_flag);
+ clear_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag);
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ goto err;
+ }
+
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
- "Issue LOGO: did:x%x",
- ndlp->nlp_DID, 0, 0);
+ "Issue LOGO: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
- phba->fc_stat.elsXmitLOGO++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_logo;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_LOGO_SND;
- spin_unlock_irq(shost->host_lock);
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
-
if (rc == IOCB_ERROR) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_LOGO_SND;
- spin_unlock_irq(shost->host_lock);
lpfc_els_free_iocb(phba, elsiocb);
- return 1;
+ lpfc_nlp_put(ndlp);
+ goto err;
}
+
+ spin_lock_irq(&ndlp->lock);
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+ spin_unlock_irq(&ndlp->lock);
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_LOGO_ISSUE);
return 0;
+
+err:
+ clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag);
+ return 1;
}
/**
@@ -2428,10 +3209,9 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* This routine is a generic completion callback function for ELS commands.
* Specifically, it is the callback function which does not need to perform
* any command specific operations. It is currently used by the ELS command
- * issuing routines for the ELS State Change Request (SCR),
- * lpfc_issue_els_scr(), and the ELS Fibre Channel Address Resolution
- * Protocol Response (FARPR) routine, lpfc_issue_els_farpr(). Other than
- * certain debug loggings, this callback function simply invokes the
+ * issuing routines for RSCN, lpfc_issue_els_rscn, and the ELS Fibre Channel
+ * Address Resolution Protocol Response (FARPR) routine, lpfc_issue_els_farpr().
+ * Other than certain debug loggings, this callback function simply invokes the
* lpfc_els_chk_latt() routine to check whether link went down during the
* discovery process.
**/
@@ -2440,87 +3220,316 @@ lpfc_cmpl_els_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
+ struct lpfc_nodelist *free_ndlp;
IOCB_t *irsp;
+ u32 ulp_status, ulp_word4, tmo, did, iotag;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+ did = get_job_els_rsp64_did(phba, cmdiocb);
- irsp = &rspiocb->iocb;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tmo = get_wqe_tmo(cmdiocb);
+ iotag = get_wqe_reqtag(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ tmo = irsp->ulpTimeout;
+ iotag = irsp->ulpIoTag;
+ }
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
- "ELS cmd cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->un.elsreq64.remoteID);
+ "ELS cmd cmpl: status:x%x/x%x did:x%x",
+ ulp_status, ulp_word4, did);
+
/* ELS cmd tag <ulpIoTag> completes */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0106 ELS cmd tag x%x completes Data: x%x x%x x%x\n",
- irsp->ulpIoTag, irsp->ulpStatus,
- irsp->un.ulpWord[4], irsp->ulpTimeout);
+ iotag, ulp_status, ulp_word4, tmo);
+
/* Check to see if link went down during discovery */
lpfc_els_chk_latt(vport);
+
+ free_ndlp = cmdiocb->ndlp;
+
lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(free_ndlp);
+}
+
+/**
+ * lpfc_reg_fab_ctrl_node - RPI register the fabric controller node.
+ * @vport: pointer to lpfc_vport data structure.
+ * @fc_ndlp: pointer to the fabric controller (0xfffffd) node.
+ *
+ * This routine registers the rpi assigned to the fabric controller
+ * NPort_ID (0xfffffd) with the port and moves the node to UNMAPPED
+ * state triggering a registration with the SCSI transport.
+ *
+ * This routine is single out because the fabric controller node
+ * does not receive a PLOGI. This routine is consumed by the
+ * SCR and RDF ELS commands. Callers are expected to qualify
+ * with SLI4 first.
+ **/
+static int
+lpfc_reg_fab_ctrl_node(struct lpfc_vport *vport, struct lpfc_nodelist *fc_ndlp)
+{
+ int rc;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nodelist *ns_ndlp;
+ LPFC_MBOXQ_t *mbox;
+
+ if (test_bit(NLP_RPI_REGISTERED, &fc_ndlp->nlp_flag))
+ return 0;
+
+ ns_ndlp = lpfc_findnode_did(vport, NameServer_DID);
+ if (!ns_ndlp)
+ return -ENODEV;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ "0935 %s: Reg FC RPI x%x on FC DID x%x NSSte: x%x\n",
+ __func__, fc_ndlp->nlp_rpi, fc_ndlp->nlp_DID,
+ ns_ndlp->nlp_state);
+ if (ns_ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+ return -ENODEV;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+ "0936 %s: no memory for reg_login "
+ "Data: x%x x%x x%lx x%x\n", __func__,
+ fc_ndlp->nlp_DID, fc_ndlp->nlp_state,
+ fc_ndlp->nlp_flag, fc_ndlp->nlp_rpi);
+ return -ENOMEM;
+ }
+ rc = lpfc_reg_rpi(phba, vport->vpi, fc_ndlp->nlp_DID,
+ (u8 *)&ns_ndlp->fc_sparam, mbox, fc_ndlp->nlp_rpi);
+ if (rc) {
+ rc = -EACCES;
+ goto out;
+ }
+
+ set_bit(NLP_REG_LOGIN_SEND, &fc_ndlp->nlp_flag);
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_fc_reg_login;
+ mbox->ctx_ndlp = lpfc_nlp_get(fc_ndlp);
+ if (!mbox->ctx_ndlp) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ rc = -ENODEV;
+ lpfc_nlp_put(fc_ndlp);
+ goto out;
+ }
+ /* Success path. Exit. */
+ lpfc_nlp_set_state(vport, fc_ndlp,
+ NLP_STE_REG_LOGIN_ISSUE);
+ return 0;
+
+ out:
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+ "0938 %s: failed to format reg_login "
+ "Data: x%x x%x x%lx x%x\n", __func__,
+ fc_ndlp->nlp_DID, fc_ndlp->nlp_state,
+ fc_ndlp->nlp_flag, fc_ndlp->nlp_rpi);
+ return rc;
+}
+
+/**
+ * lpfc_cmpl_els_disc_cmd - Completion callback function for Discovery ELS cmd
+ * @phba: pointer to lpfc hba data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @rspiocb: pointer to lpfc response iocb data structure.
+ *
+ * This routine is a generic completion callback function for Discovery ELS cmd.
+ * Currently used by the ELS command issuing routines for the ELS State Change
+ * Request (SCR), lpfc_issue_els_scr(), Exchange Diagnostic Capabilities (EDC),
+ * lpfc_issue_els_edc() and the ELS RDF, lpfc_issue_els_rdf().
+ * These commands will be retried once only for ELS timeout errors.
+ **/
+static void
+lpfc_cmpl_els_disc_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_vport *vport = cmdiocb->vport;
+ IOCB_t *irsp;
+ struct lpfc_els_rdf_rsp *prdf;
+ struct lpfc_dmabuf *pcmd, *prsp;
+ u32 *pdata;
+ u32 cmd;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
+ u32 ulp_status, ulp_word4, tmo, did, iotag;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+ did = get_job_els_rsp64_did(phba, cmdiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tmo = get_wqe_tmo(cmdiocb);
+ iotag = get_wqe_reqtag(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ tmo = irsp->ulpTimeout;
+ iotag = irsp->ulpIoTag;
+ }
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+ "ELS cmd cmpl: status:x%x/x%x did:x%x",
+ ulp_status, ulp_word4, did);
+
+ /* ELS cmd tag <ulpIoTag> completes */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
+ "0217 ELS cmd tag x%x completes Data: x%x x%x x%x x%x\n",
+ iotag, ulp_status, ulp_word4, tmo, cmdiocb->retry);
+
+ pcmd = cmdiocb->cmd_dmabuf;
+ if (!pcmd)
+ goto out;
+
+ pdata = (u32 *)pcmd->virt;
+ if (!pdata)
+ goto out;
+ cmd = *pdata;
+
+ /* Only 1 retry for ELS Timeout only */
+ if (ulp_status == IOSTAT_LOCAL_REJECT &&
+ ((ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_SEQUENCE_TIMEOUT)) {
+ cmdiocb->retry++;
+ if (cmdiocb->retry <= 1) {
+ switch (cmd) {
+ case ELS_CMD_SCR:
+ lpfc_issue_els_scr(vport, cmdiocb->retry);
+ break;
+ case ELS_CMD_EDC:
+ lpfc_issue_els_edc(vport, cmdiocb->retry);
+ break;
+ case ELS_CMD_RDF:
+ lpfc_issue_els_rdf(vport, cmdiocb->retry);
+ break;
+ }
+ goto out;
+ }
+ phba->fc_stat.elsRetryExceeded++;
+ }
+ if (cmd == ELS_CMD_EDC) {
+ /* must be called before checking uplStatus and returning */
+ lpfc_cmpl_els_edc(phba, cmdiocb, rspiocb);
+ return;
+ }
+
+ if (ulp_status) {
+ /* ELS discovery cmd completes with error */
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS | LOG_CGN_MGMT,
+ "4203 ELS cmd x%x error: x%x x%X\n", cmd,
+ ulp_status, ulp_word4);
+
+ /* In the case where the ELS cmd completes with an error and
+ * the node does not have RPI registered, the node is
+ * outstanding and should put its initial reference.
+ */
+ if ((cmd == ELS_CMD_SCR || cmd == ELS_CMD_RDF) &&
+ !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD) &&
+ !test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag))
+ lpfc_nlp_put(ndlp);
+ goto out;
+ }
+
+ /* The RDF response doesn't have any impact on the running driver
+ * but the notification descriptors are dumped here for support.
+ */
+ if (cmd == ELS_CMD_RDF) {
+ int i;
+
+ prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
+ if (!prsp)
+ goto out;
+
+ prdf = (struct lpfc_els_rdf_rsp *)prsp->virt;
+ if (!prdf)
+ goto out;
+ if (!lpfc_is_els_acc_rsp(prsp))
+ goto out;
+
+ for (i = 0; i < ELS_RDF_REG_TAG_CNT &&
+ i < be32_to_cpu(prdf->reg_d1.reg_desc.count); i++)
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_CGN_MGMT,
+ "4677 Fabric RDF Notification Grant "
+ "Data: 0x%08x Reg: %x %x\n",
+ be32_to_cpu(
+ prdf->reg_d1.desc_tags[i]),
+ phba->cgn_reg_signal,
+ phba->cgn_reg_fpin);
+ }
+
+out:
+ /* Check to see if link went down during discovery */
+ lpfc_els_chk_latt(vport);
+ lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
return;
}
/**
* lpfc_issue_els_scr - Issue a scr to an node on a vport
* @vport: pointer to a host virtual N_Port data structure.
- * @nportid: N_Port identifier to the remote node.
- * @retry: number of retries to the command IOCB.
+ * @retry: retry counter for the command IOCB.
*
* This routine issues a State Change Request (SCR) to a fabric node
- * on a @vport. The remote node @nportid is passed into the function. It
+ * on a @vport. The remote node is Fabric Controller (0xfffffd). It
* first search the @vport node list to find the matching ndlp. If no such
* ndlp is found, a new ndlp shall be created for this (SCR) purpose. An
* IOCB is allocated, payload prepared, and the lpfc_sli_issue_iocb()
* routine is invoked to send the SCR IOCB.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the SCR ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the SCR ELS command.
*
* Return code
* 0 - Successfully issued scr command
* 1 - Failed to issue scr command
**/
int
-lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
+lpfc_issue_els_scr(struct lpfc_vport *vport, uint8_t retry)
{
+ int rc = 0;
struct lpfc_hba *phba = vport->phba;
- IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
struct lpfc_nodelist *ndlp;
+ bool node_created = false;
- psli = &phba->sli;
cmdsize = (sizeof(uint32_t) + sizeof(SCR));
- ndlp = lpfc_findnode_did(vport, nportid);
+ ndlp = lpfc_findnode_did(vport, Fabric_Cntl_DID);
if (!ndlp) {
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, Fabric_Cntl_DID);
if (!ndlp)
return 1;
- lpfc_nlp_init(vport, ndlp, nportid);
lpfc_enqueue_node(vport, ndlp);
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
- if (!ndlp)
- return 1;
+ node_created = true;
}
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
ndlp->nlp_DID, ELS_CMD_SCR);
-
- if (!elsiocb) {
- /* This will trigger the release of the node just
- * allocated
- */
- lpfc_nlp_put(ndlp);
- return 1;
+ if (!elsiocb)
+ goto out_node_created;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ rc = lpfc_reg_fab_ctrl_node(vport, ndlp);
+ if (rc) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+ "0937 %s: Failed to reg fc node, rc %d\n",
+ __func__, rc);
+ goto out_free_iocb;
+ }
}
-
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_SCR;
pcmd += sizeof(uint32_t);
@@ -2534,21 +3543,125 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
ndlp->nlp_DID, 0, 0);
phba->fc_stat.elsXmitSCR++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
- IOCB_ERROR) {
- /* The additional lpfc_nlp_put will cause the following
- * lpfc_els_free_iocb routine to trigger the rlease of
- * the node.
- */
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_disc_cmd;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp)
+ goto out_free_iocb;
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+ "Issue SCR: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR)
+ goto out_iocb_error;
+
+ return 0;
+
+out_iocb_error:
+ lpfc_nlp_put(ndlp);
+out_free_iocb:
+ lpfc_els_free_iocb(phba, elsiocb);
+out_node_created:
+ if (node_created)
lpfc_nlp_put(ndlp);
+ return 1;
+}
+
+/**
+ * lpfc_issue_els_rscn - Issue an RSCN to the Fabric Controller (Fabric)
+ * or the other nport (pt2pt).
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @retry: number of retries to the command IOCB.
+ *
+ * This routine issues a RSCN to the Fabric Controller (DID 0xFFFFFD)
+ * when connected to a fabric, or to the remote port when connected
+ * in point-to-point mode. When sent to the Fabric Controller, it will
+ * replay the RSCN to registered recipients.
+ *
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the RSCN ELS command.
+ *
+ * Return code
+ * 0 - Successfully issued RSCN command
+ * 1 - Failed to issue RSCN command
+ **/
+int
+lpfc_issue_els_rscn(struct lpfc_vport *vport, uint8_t retry)
+{
+ int rc = 0;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_nodelist *ndlp;
+ struct {
+ struct fc_els_rscn rscn;
+ struct fc_els_rscn_page portid;
+ } *event;
+ uint32_t nportid;
+ uint16_t cmdsize = sizeof(*event);
+
+ /* Not supported for private loop */
+ if (phba->fc_topology == LPFC_TOPOLOGY_LOOP &&
+ !test_bit(FC_PUBLIC_LOOP, &vport->fc_flag))
+ return 1;
+
+ if (test_bit(FC_PT2PT, &vport->fc_flag)) {
+ /* find any mapped nport - that would be the other nport */
+ ndlp = lpfc_findnode_mapped(vport);
+ if (!ndlp)
+ return 1;
+ } else {
+ nportid = FC_FID_FCTRL;
+ /* find the fabric controller node */
+ ndlp = lpfc_findnode_did(vport, nportid);
+ if (!ndlp) {
+ /* if one didn't exist, make one */
+ ndlp = lpfc_nlp_init(vport, nportid);
+ if (!ndlp)
+ return 1;
+ lpfc_enqueue_node(vport, ndlp);
+ }
+ }
+
+ elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
+ ndlp->nlp_DID, ELS_CMD_RSCN_XMT);
+
+ if (!elsiocb)
+ return 1;
+
+ event = elsiocb->cmd_dmabuf->virt;
+
+ event->rscn.rscn_cmd = ELS_RSCN;
+ event->rscn.rscn_page_len = sizeof(struct fc_els_rscn_page);
+ event->rscn.rscn_plen = cpu_to_be16(cmdsize);
+
+ nportid = vport->fc_myDID;
+ /* appears that page flags must be 0 for fabric to broadcast RSCN */
+ event->portid.rscn_page_flags = 0;
+ event->portid.rscn_fid[0] = (nportid & 0x00FF0000) >> 16;
+ event->portid.rscn_fid[1] = (nportid & 0x0000FF00) >> 8;
+ event->portid.rscn_fid[2] = nportid & 0x000000FF;
+
+ phba->fc_stat.elsXmitRSCN++;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_cmd;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
}
- /* This will cause the callback-function lpfc_cmpl_els_cmd to
- * trigger the release of node.
- */
- lpfc_nlp_put(ndlp);
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+ "Issue RSCN: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ return 1;
+ }
+
return 0;
}
@@ -2565,10 +3678,9 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
* for this (FARPR) purpose. An IOCB is allocated, payload prepared, and the
* lpfc_sli_issue_iocb() routine is invoked to send the FARPR ELS command.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the PARPR ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the FARPR ELS command.
*
* Return code
* 0 - Successfully issued farpr command
@@ -2577,10 +3689,9 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
static int
lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
{
+ int rc = 0;
struct lpfc_hba *phba = vport->phba;
- IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
FARP *fp;
uint8_t *pcmd;
uint32_t *lp;
@@ -2588,34 +3699,22 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
struct lpfc_nodelist *ondlp;
struct lpfc_nodelist *ndlp;
- psli = &phba->sli;
cmdsize = (sizeof(uint32_t) + sizeof(FARP));
ndlp = lpfc_findnode_did(vport, nportid);
if (!ndlp) {
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, nportid);
if (!ndlp)
return 1;
- lpfc_nlp_init(vport, ndlp, nportid);
lpfc_enqueue_node(vport, ndlp);
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
- if (!ndlp)
- return 1;
}
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
- ndlp->nlp_DID, ELS_CMD_RNID);
- if (!elsiocb) {
- /* This will trigger the release of the node just
- * allocated
- */
- lpfc_nlp_put(ndlp);
+ ndlp->nlp_DID, ELS_CMD_FARPR);
+ if (!elsiocb)
return 1;
- }
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_FARPR;
pcmd += sizeof(uint32_t);
@@ -2632,7 +3731,7 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
memcpy(&fp->RportName, &vport->fc_portname, sizeof(struct lpfc_name));
memcpy(&fp->RnodeName, &vport->fc_nodename, sizeof(struct lpfc_name));
ondlp = lpfc_findnode_did(vport, nportid);
- if (ondlp && NLP_CHK_NODE_ACT(ondlp)) {
+ if (ondlp) {
memcpy(&fp->OportName, &ondlp->nlp_portname,
sizeof(struct lpfc_name));
memcpy(&fp->OnodeName, &ondlp->nlp_nodename,
@@ -2644,22 +3743,649 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
ndlp->nlp_DID, 0, 0);
phba->fc_stat.elsXmitFARPR++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
- IOCB_ERROR) {
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_cmd;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
/* The additional lpfc_nlp_put will cause the following
* lpfc_els_free_iocb routine to trigger the release of
* the node.
*/
- lpfc_nlp_put(ndlp);
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
- /* This will cause the callback-function lpfc_cmpl_els_cmd to
- * trigger the release of the node.
+
+ return 0;
+}
+
+/**
+ * lpfc_issue_els_rdf - Register for diagnostic functions from the fabric.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @retry: retry counter for the command IOCB.
+ *
+ * This routine issues an ELS RDF to the Fabric Controller to register
+ * for diagnostic functions.
+ *
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the RDF ELS command.
+ *
+ * Return code
+ * 0 - Successfully issued rdf command
+ * < 0 - Failed to issue rdf command
+ * -EACCES - RDF not required for NPIV_PORT
+ * -ENODEV - No fabric controller device available
+ * -ENOMEM - No available memory
+ * -EIO - The mailbox failed to complete successfully.
+ *
+ **/
+int
+lpfc_issue_els_rdf(struct lpfc_vport *vport, uint8_t retry)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_els_rdf_req *prdf;
+ struct lpfc_nodelist *ndlp;
+ uint16_t cmdsize;
+ int rc;
+ bool node_created = false;
+ int err;
+
+ cmdsize = sizeof(*prdf);
+
+ /* RDF ELS is not required on an NPIV VN_Port. */
+ if (vport->port_type == LPFC_NPIV_PORT)
+ return -EACCES;
+
+ ndlp = lpfc_findnode_did(vport, Fabric_Cntl_DID);
+ if (!ndlp) {
+ ndlp = lpfc_nlp_init(vport, Fabric_Cntl_DID);
+ if (!ndlp)
+ return -ENODEV;
+ lpfc_enqueue_node(vport, ndlp);
+ node_created = true;
+ }
+
+ elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
+ ndlp->nlp_DID, ELS_CMD_RDF);
+ if (!elsiocb) {
+ err = -ENOMEM;
+ goto out_node_created;
+ }
+
+ /* Configure the payload for the supported FPIN events. */
+ prdf = (struct lpfc_els_rdf_req *)elsiocb->cmd_dmabuf->virt;
+ memset(prdf, 0, cmdsize);
+ prdf->rdf.fpin_cmd = ELS_RDF;
+ prdf->rdf.desc_len = cpu_to_be32(sizeof(struct lpfc_els_rdf_req) -
+ sizeof(struct fc_els_rdf_hdr));
+ prdf->reg_d1.reg_desc.desc_tag = cpu_to_be32(ELS_DTAG_FPIN_REGISTER);
+ prdf->reg_d1.reg_desc.desc_len = cpu_to_be32(
+ FC_TLV_DESC_LENGTH_FROM_SZ(prdf->reg_d1));
+ prdf->reg_d1.reg_desc.count = cpu_to_be32(ELS_RDF_REG_TAG_CNT);
+ prdf->reg_d1.desc_tags[0] = cpu_to_be32(ELS_DTAG_LNK_INTEGRITY);
+ prdf->reg_d1.desc_tags[1] = cpu_to_be32(ELS_DTAG_DELIVERY);
+ prdf->reg_d1.desc_tags[2] = cpu_to_be32(ELS_DTAG_PEER_CONGEST);
+ prdf->reg_d1.desc_tags[3] = cpu_to_be32(ELS_DTAG_CONGESTION);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
+ "6444 Xmit RDF to remote NPORT x%x Reg: %x %x\n",
+ ndlp->nlp_DID, phba->cgn_reg_signal,
+ phba->cgn_reg_fpin);
+
+ phba->cgn_fpin_frequency = LPFC_FPIN_INIT_FREQ;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_disc_cmd;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ err = -EIO;
+ goto out_free_iocb;
+ }
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+ "Issue RDF: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ err = -EIO;
+ goto out_iocb_error;
+ }
+ return 0;
+
+out_iocb_error:
+ lpfc_nlp_put(ndlp);
+out_free_iocb:
+ lpfc_els_free_iocb(phba, elsiocb);
+out_node_created:
+ if (node_created)
+ lpfc_nlp_put(ndlp);
+ return err;
+}
+
+ /**
+ * lpfc_els_rcv_rdf - Receive RDF ELS request from the fabric.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * A received RDF implies a possible change to fabric supported diagnostic
+ * functions. This routine sends LS_ACC and then has the Nx_Port issue a new
+ * RDF request to reregister for supported diagnostic functions.
+ *
+ * Return code
+ * 0 - Success
+ * -EIO - Failed to process received RDF
+ **/
+static int
+lpfc_els_rcv_rdf(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ int rc;
+
+ rc = lpfc_els_rsp_acc(vport, ELS_CMD_RDF, cmdiocb, ndlp, NULL);
+ /* Send LS_ACC */
+ if (rc) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
+ "1623 Failed to RDF_ACC from x%x for x%x Data: %d\n",
+ ndlp->nlp_DID, vport->fc_myDID, rc);
+ return -EIO;
+ }
+
+ rc = lpfc_issue_els_rdf(vport, 0);
+ /* Issue new RDF for reregistering */
+ if (rc) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
+ "2623 Failed to re register RDF for x%x Data: %d\n",
+ vport->fc_myDID, rc);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * lpfc_least_capable_settings - helper function for EDC rsp processing
+ * @phba: pointer to lpfc hba data structure.
+ * @pcgd: pointer to congestion detection descriptor in EDC rsp.
+ *
+ * This helper routine determines the least capable setting for
+ * congestion signals, signal freq, including scale, from the
+ * congestion detection descriptor in the EDC rsp. The routine
+ * sets @phba values in preparation for a set_featues mailbox.
+ **/
+static void
+lpfc_least_capable_settings(struct lpfc_hba *phba,
+ struct fc_diag_cg_sig_desc *pcgd)
+{
+ u32 rsp_sig_cap = 0, drv_sig_cap = 0;
+ u32 rsp_sig_freq_cyc = 0, rsp_sig_freq_scale = 0;
+
+ /* Get rsp signal and frequency capabilities. */
+ rsp_sig_cap = be32_to_cpu(pcgd->xmt_signal_capability);
+ rsp_sig_freq_cyc = be16_to_cpu(pcgd->xmt_signal_frequency.count);
+ rsp_sig_freq_scale = be16_to_cpu(pcgd->xmt_signal_frequency.units);
+
+ /* If the Fport does not support signals. Set FPIN only */
+ if (rsp_sig_cap == EDC_CG_SIG_NOTSUPPORTED)
+ goto out_no_support;
+
+ /* Apply the xmt scale to the xmt cycle to get the correct frequency.
+ * Adapter default is 100 millisSeconds. Convert all xmt cycle values
+ * to milliSeconds.
+ */
+ switch (rsp_sig_freq_scale) {
+ case EDC_CG_SIGFREQ_SEC:
+ rsp_sig_freq_cyc *= MSEC_PER_SEC;
+ break;
+ case EDC_CG_SIGFREQ_MSEC:
+ rsp_sig_freq_cyc = 1;
+ break;
+ default:
+ goto out_no_support;
+ }
+
+ /* Convenient shorthand. */
+ drv_sig_cap = phba->cgn_reg_signal;
+
+ /* Choose the least capable frequency. */
+ if (rsp_sig_freq_cyc > phba->cgn_sig_freq)
+ phba->cgn_sig_freq = rsp_sig_freq_cyc;
+
+ /* Should be some common signals support. Settle on least capable
+ * signal and adjust FPIN values. Initialize defaults to ease the
+ * decision.
+ */
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_WARN | LPFC_CGN_FPIN_ALARM;
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ if (rsp_sig_cap == EDC_CG_SIG_WARN_ONLY &&
+ (drv_sig_cap == EDC_CG_SIG_WARN_ONLY ||
+ drv_sig_cap == EDC_CG_SIG_WARN_ALARM)) {
+ phba->cgn_reg_signal = EDC_CG_SIG_WARN_ONLY;
+ phba->cgn_reg_fpin &= ~LPFC_CGN_FPIN_WARN;
+ }
+ if (rsp_sig_cap == EDC_CG_SIG_WARN_ALARM) {
+ if (drv_sig_cap == EDC_CG_SIG_WARN_ALARM) {
+ phba->cgn_reg_signal = EDC_CG_SIG_WARN_ALARM;
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_NONE;
+ }
+ if (drv_sig_cap == EDC_CG_SIG_WARN_ONLY) {
+ phba->cgn_reg_signal = EDC_CG_SIG_WARN_ONLY;
+ phba->cgn_reg_fpin &= ~LPFC_CGN_FPIN_WARN;
+ }
+ }
+
+ /* We are NOT recording signal frequency in congestion info buffer */
+ return;
+
+out_no_support:
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ phba->cgn_sig_freq = 0;
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_ALARM | LPFC_CGN_FPIN_WARN;
+}
+
+DECLARE_ENUM2STR_LOOKUP(lpfc_get_tlv_dtag_nm, fc_ls_tlv_dtag,
+ FC_LS_TLV_DTAG_INIT);
+
+/**
+ * lpfc_cmpl_els_edc - Completion callback function for EDC
+ * @phba: pointer to lpfc hba data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @rspiocb: pointer to lpfc response iocb data structure.
+ *
+ * This routine is the completion callback function for issuing the Exchange
+ * Diagnostic Capabilities (EDC) command. The driver issues an EDC to
+ * notify the FPort of its Congestion and Link Fault capabilities. This
+ * routine parses the FPort's response and decides on the least common
+ * values applicable to both FPort and NPort for Warnings and Alarms that
+ * are communicated via hardware signals.
+ **/
+static void
+lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ IOCB_t *irsp_iocb;
+ struct fc_els_edc_resp *edc_rsp;
+ struct fc_tlv_desc *tlv;
+ struct fc_diag_cg_sig_desc *pcgd;
+ struct fc_diag_lnkflt_desc *plnkflt;
+ struct lpfc_dmabuf *pcmd, *prsp;
+ const char *dtag_nm;
+ u32 *pdata, dtag;
+ int desc_cnt = 0, bytes_remain;
+ bool rcv_cap_desc = false;
+ struct lpfc_nodelist *ndlp;
+ u32 ulp_status, ulp_word4, tmo, did, iotag;
+
+ ndlp = cmdiocb->ndlp;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+ did = get_job_els_rsp64_did(phba, rspiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tmo = get_wqe_tmo(rspiocb);
+ iotag = get_wqe_reqtag(rspiocb);
+ } else {
+ irsp_iocb = &rspiocb->iocb;
+ tmo = irsp_iocb->ulpTimeout;
+ iotag = irsp_iocb->ulpIoTag;
+ }
+
+ lpfc_debugfs_disc_trc(phba->pport, LPFC_DISC_TRC_ELS_CMD,
+ "EDC cmpl: status:x%x/x%x did:x%x",
+ ulp_status, ulp_word4, did);
+
+ /* ELS cmd tag <ulpIoTag> completes */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
+ "4201 EDC cmd tag x%x completes Data: x%x x%x x%x\n",
+ iotag, ulp_status, ulp_word4, tmo);
+
+ pcmd = cmdiocb->cmd_dmabuf;
+ if (!pcmd)
+ goto out;
+
+ pdata = (u32 *)pcmd->virt;
+ if (!pdata)
+ goto out;
+
+ /* Need to clear signal values, send features MB and RDF with FPIN. */
+ if (ulp_status)
+ goto out;
+
+ prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
+ if (!prsp)
+ goto out;
+
+ edc_rsp = prsp->virt;
+ if (!edc_rsp)
+ goto out;
+
+ /* ELS cmd tag <ulpIoTag> completes */
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_ELS | LOG_CGN_MGMT | LOG_LDS_EVENT,
+ "4676 Fabric EDC Rsp: "
+ "0x%02x, 0x%08x\n",
+ edc_rsp->acc_hdr.la_cmd,
+ be32_to_cpu(edc_rsp->desc_list_len));
+
+ if (!lpfc_is_els_acc_rsp(prsp))
+ goto out;
+
+ /*
+ * Payload length in bytes is the response descriptor list
+ * length minus the 12 bytes of Link Service Request
+ * Information descriptor in the reply.
*/
+ bytes_remain = be32_to_cpu(edc_rsp->desc_list_len) -
+ sizeof(struct fc_els_lsri_desc);
+ if (bytes_remain <= 0)
+ goto out;
+
+ tlv = edc_rsp->desc;
+
+ /*
+ * cycle through EDC diagnostic descriptors to find the
+ * congestion signaling capability descriptor
+ */
+ while (bytes_remain) {
+ if (bytes_remain < FC_TLV_DESC_HDR_SZ) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "6461 Truncated TLV hdr on "
+ "Diagnostic descriptor[%d]\n",
+ desc_cnt);
+ goto out;
+ }
+
+ dtag = be32_to_cpu(tlv->desc_tag);
+ switch (dtag) {
+ case ELS_DTAG_LNK_FAULT_CAP:
+ if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) ||
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv) !=
+ sizeof(struct fc_diag_lnkflt_desc)) {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_ELS | LOG_CGN_MGMT | LOG_LDS_EVENT,
+ "6462 Truncated Link Fault Diagnostic "
+ "descriptor[%d]: %d vs 0x%zx 0x%zx\n",
+ desc_cnt, bytes_remain,
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv),
+ sizeof(struct fc_diag_lnkflt_desc));
+ goto out;
+ }
+ plnkflt = (struct fc_diag_lnkflt_desc *)tlv;
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_ELS | LOG_LDS_EVENT,
+ "4617 Link Fault Desc Data: 0x%08x 0x%08x "
+ "0x%08x 0x%08x 0x%08x\n",
+ be32_to_cpu(plnkflt->desc_tag),
+ be32_to_cpu(plnkflt->desc_len),
+ be32_to_cpu(
+ plnkflt->degrade_activate_threshold),
+ be32_to_cpu(
+ plnkflt->degrade_deactivate_threshold),
+ be32_to_cpu(plnkflt->fec_degrade_interval));
+ break;
+ case ELS_DTAG_CG_SIGNAL_CAP:
+ if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) ||
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv) !=
+ sizeof(struct fc_diag_cg_sig_desc)) {
+ lpfc_printf_log(
+ phba, KERN_WARNING, LOG_CGN_MGMT,
+ "6463 Truncated Cgn Signal Diagnostic "
+ "descriptor[%d]: %d vs 0x%zx 0x%zx\n",
+ desc_cnt, bytes_remain,
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv),
+ sizeof(struct fc_diag_cg_sig_desc));
+ goto out;
+ }
+
+ pcgd = (struct fc_diag_cg_sig_desc *)tlv;
+ lpfc_printf_log(
+ phba, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
+ "4616 CGN Desc Data: 0x%08x 0x%08x "
+ "0x%08x 0x%04x 0x%04x 0x%08x 0x%04x 0x%04x\n",
+ be32_to_cpu(pcgd->desc_tag),
+ be32_to_cpu(pcgd->desc_len),
+ be32_to_cpu(pcgd->xmt_signal_capability),
+ be16_to_cpu(pcgd->xmt_signal_frequency.count),
+ be16_to_cpu(pcgd->xmt_signal_frequency.units),
+ be32_to_cpu(pcgd->rcv_signal_capability),
+ be16_to_cpu(pcgd->rcv_signal_frequency.count),
+ be16_to_cpu(pcgd->rcv_signal_frequency.units));
+
+ /* Compare driver and Fport capabilities and choose
+ * least common.
+ */
+ lpfc_least_capable_settings(phba, pcgd);
+ rcv_cap_desc = true;
+ break;
+ default:
+ dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "4919 unknown Diagnostic "
+ "Descriptor[%d]: tag x%x (%s)\n",
+ desc_cnt, dtag, dtag_nm);
+ }
+
+ bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv);
+ tlv = fc_tlv_next_desc(tlv);
+ desc_cnt++;
+ }
+
+out:
+ if (!rcv_cap_desc) {
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_ALARM | LPFC_CGN_FPIN_WARN;
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ phba->cgn_sig_freq = 0;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS | LOG_CGN_MGMT,
+ "4202 EDC rsp error - sending RDF "
+ "for FPIN only.\n");
+ }
+
+ lpfc_config_cgn_signal(phba);
+
+ /* Check to see if link went down during discovery */
+ lpfc_els_chk_latt(phba->pport);
+ lpfc_debugfs_disc_trc(phba->pport, LPFC_DISC_TRC_ELS_CMD,
+ "EDC Cmpl: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
+ lpfc_els_free_iocb(phba, cmdiocb);
lpfc_nlp_put(ndlp);
+}
+
+static void
+lpfc_format_edc_lft_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+{
+ struct fc_diag_lnkflt_desc *lft = (struct fc_diag_lnkflt_desc *)tlv;
+
+ lft->desc_tag = cpu_to_be32(ELS_DTAG_LNK_FAULT_CAP);
+ lft->desc_len = cpu_to_be32(
+ FC_TLV_DESC_LENGTH_FROM_SZ(struct fc_diag_lnkflt_desc));
+
+ lft->degrade_activate_threshold =
+ cpu_to_be32(phba->degrade_activate_threshold);
+ lft->degrade_deactivate_threshold =
+ cpu_to_be32(phba->degrade_deactivate_threshold);
+ lft->fec_degrade_interval = cpu_to_be32(phba->fec_degrade_interval);
+}
+
+static void
+lpfc_format_edc_cgn_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+{
+ struct fc_diag_cg_sig_desc *cgd = (struct fc_diag_cg_sig_desc *)tlv;
+
+ /* We are assuming cgd was zero'ed before calling this routine */
+
+ /* Configure the congestion detection capability */
+ cgd->desc_tag = cpu_to_be32(ELS_DTAG_CG_SIGNAL_CAP);
+
+ /* Descriptor len doesn't include the tag or len fields. */
+ cgd->desc_len = cpu_to_be32(
+ FC_TLV_DESC_LENGTH_FROM_SZ(struct fc_diag_cg_sig_desc));
+
+ /* xmt_signal_capability already set to EDC_CG_SIG_NOTSUPPORTED.
+ * xmt_signal_frequency.count already set to 0.
+ * xmt_signal_frequency.units already set to 0.
+ */
+
+ if (phba->cmf_active_mode == LPFC_CFG_OFF) {
+ /* rcv_signal_capability already set to EDC_CG_SIG_NOTSUPPORTED.
+ * rcv_signal_frequency.count already set to 0.
+ * rcv_signal_frequency.units already set to 0.
+ */
+ phba->cgn_sig_freq = 0;
+ return;
+ }
+ switch (phba->cgn_reg_signal) {
+ case EDC_CG_SIG_WARN_ONLY:
+ cgd->rcv_signal_capability = cpu_to_be32(EDC_CG_SIG_WARN_ONLY);
+ break;
+ case EDC_CG_SIG_WARN_ALARM:
+ cgd->rcv_signal_capability = cpu_to_be32(EDC_CG_SIG_WARN_ALARM);
+ break;
+ default:
+ /* rcv_signal_capability left 0 thus no support */
+ break;
+ }
+
+ /* We start negotiation with lpfc_fabric_cgn_frequency, after
+ * the completion we settle on the higher frequency.
+ */
+ cgd->rcv_signal_frequency.count =
+ cpu_to_be16(lpfc_fabric_cgn_frequency);
+ cgd->rcv_signal_frequency.units =
+ cpu_to_be16(EDC_CG_SIGFREQ_MSEC);
+}
+
+static bool
+lpfc_link_is_lds_capable(struct lpfc_hba *phba)
+{
+ if (!(phba->lmt & LMT_64Gb))
+ return false;
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return false;
+
+ if (phba->sli4_hba.conf_trunk) {
+ if (phba->trunk_link.phy_lnk_speed == LPFC_USER_LINK_SPEED_64G)
+ return true;
+ } else if (phba->fc_linkspeed == LPFC_LINK_SPEED_64GHZ) {
+ return true;
+ }
+ return false;
+}
+
+ /**
+ * lpfc_issue_els_edc - Exchange Diagnostic Capabilities with the fabric.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @retry: retry counter for the command iocb.
+ *
+ * This routine issues an ELS EDC to the F-Port Controller to communicate
+ * this N_Port's support of hardware signals in its Congestion
+ * Capabilities Descriptor.
+ *
+ * Note: This routine does not check if one or more signals are
+ * set in the cgn_reg_signal parameter. The caller makes the
+ * decision to enforce cgn_reg_signal as nonzero or zero depending
+ * on the conditions. During Fabric requests, the driver
+ * requires cgn_reg_signals to be nonzero. But a dynamic request
+ * to set the congestion mode to OFF from Monitor or Manage
+ * would correctly issue an EDC with no signals enabled to
+ * turn off switch functionality and then update the FW.
+ *
+ * Return code
+ * 0 - Successfully issued edc command
+ * 1 - Failed to issue edc command
+ **/
+int
+lpfc_issue_els_edc(struct lpfc_vport *vport, uint8_t retry)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_iocbq *elsiocb;
+ struct fc_els_edc *edc_req;
+ struct fc_tlv_desc *tlv;
+ u16 cmdsize;
+ struct lpfc_nodelist *ndlp;
+ u8 *pcmd = NULL;
+ u32 cgn_desc_size, lft_desc_size;
+ int rc;
+
+ if (vport->port_type == LPFC_NPIV_PORT)
+ return -EACCES;
+
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+ return -ENODEV;
+
+ cgn_desc_size = (phba->cgn_init_reg_signal) ?
+ sizeof(struct fc_diag_cg_sig_desc) : 0;
+ lft_desc_size = (lpfc_link_is_lds_capable(phba)) ?
+ sizeof(struct fc_diag_lnkflt_desc) : 0;
+ cmdsize = cgn_desc_size + lft_desc_size;
+
+ /* Skip EDC if no applicable descriptors */
+ if (!cmdsize)
+ goto try_rdf;
+
+ cmdsize += sizeof(struct fc_els_edc);
+ elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
+ ndlp->nlp_DID, ELS_CMD_EDC);
+ if (!elsiocb)
+ goto try_rdf;
+
+ /* Configure the payload for the supported Diagnostics capabilities. */
+ pcmd = (u8 *)elsiocb->cmd_dmabuf->virt;
+ memset(pcmd, 0, cmdsize);
+ edc_req = (struct fc_els_edc *)pcmd;
+ edc_req->desc_len = cpu_to_be32(cgn_desc_size + lft_desc_size);
+ edc_req->edc_cmd = ELS_EDC;
+ tlv = edc_req->desc;
+
+ if (cgn_desc_size) {
+ lpfc_format_edc_cgn_desc(phba, tlv);
+ phba->cgn_sig_freq = lpfc_fabric_cgn_frequency;
+ tlv = fc_tlv_next_desc(tlv);
+ }
+
+ if (lft_desc_size)
+ lpfc_format_edc_lft_desc(phba, tlv);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
+ "4623 Xmit EDC to remote "
+ "NPORT x%x reg_sig x%x reg_fpin:x%x\n",
+ ndlp->nlp_DID, phba->cgn_reg_signal,
+ phba->cgn_reg_fpin);
+
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_disc_cmd;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return -EIO;
+ }
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+ "Issue EDC: did:x%x refcnt %d",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), 0);
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ /* The additional lpfc_nlp_put will cause the following
+ * lpfc_els_free_iocb routine to trigger the release of
+ * the node.
+ */
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ goto try_rdf;
+ }
return 0;
+try_rdf:
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_WARN | LPFC_CGN_FPIN_ALARM;
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ rc = lpfc_issue_els_rdf(vport, 0);
+ return rc;
}
/**
@@ -2677,15 +4403,11 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
void
lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_work_evt *evtp;
- if (!(nlp->nlp_flag & NLP_DELAY_TMO))
+ if (!test_and_clear_bit(NLP_DELAY_TMO, &nlp->nlp_flag))
return;
- spin_lock_irq(shost->host_lock);
- nlp->nlp_flag &= ~NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
- del_timer_sync(&nlp->nlp_delayfunc);
+ timer_delete_sync(&nlp->nlp_delayfunc);
nlp->nlp_last_elscmd = 0;
if (!list_empty(&nlp->els_retry_evt.evt_listp)) {
list_del_init(&nlp->els_retry_evt.evt_listp);
@@ -2693,10 +4415,7 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
evtp = &nlp->els_retry_evt;
lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1);
}
- if (nlp->nlp_flag & NLP_NPR_2B_DISC) {
- spin_lock_irq(shost->host_lock);
- nlp->nlp_flag &= ~NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+ if (test_and_clear_bit(NLP_NPR_2B_DISC, &nlp->nlp_flag)) {
if (vport->num_disc_nodes) {
if (vport->port_state < LPFC_VPORT_READY) {
/* Check if there are more ADISCs to be sent */
@@ -2705,9 +4424,8 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
/* Check if there are more PLOGIs to be sent */
lpfc_more_plogi(vport);
if (vport->num_disc_nodes == 0) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NDISC_ACTIVE;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_NDISC_ACTIVE,
+ &vport->fc_flag);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
@@ -2719,7 +4437,7 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
/**
* lpfc_els_retry_delay - Timer function with a ndlp delayed function timer
- * @ptr: holder for the pointer to the timer function associated data (ndlp).
+ * @t: pointer to the timer function associated data (ndlp).
*
* This routine is invoked by the ndlp delayed-function timer to check
* whether there is any pending ELS retry event(s) with the node. If not, it
@@ -2732,31 +4450,32 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
* to the event associated with the ndlp.
**/
void
-lpfc_els_retry_delay(unsigned long ptr)
+lpfc_els_retry_delay(struct timer_list *t)
{
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) ptr;
+ struct lpfc_nodelist *ndlp = timer_container_of(ndlp, t,
+ nlp_delayfunc);
struct lpfc_vport *vport = ndlp->vport;
struct lpfc_hba *phba = vport->phba;
unsigned long flags;
struct lpfc_work_evt *evtp = &ndlp->els_retry_evt;
+ /* Hold a node reference for outstanding queued work */
+ if (!lpfc_nlp_get(ndlp))
+ return;
+
spin_lock_irqsave(&phba->hbalock, flags);
if (!list_empty(&evtp->evt_listp)) {
spin_unlock_irqrestore(&phba->hbalock, flags);
+ lpfc_nlp_put(ndlp);
return;
}
- /* We need to hold the node by incrementing the reference
- * count until the queued work is done
- */
- evtp->evt_arg1 = lpfc_nlp_get(ndlp);
- if (evtp->evt_arg1) {
- evtp->evt = LPFC_EVT_ELS_RETRY;
- list_add_tail(&evtp->evt_listp, &phba->work_list);
- lpfc_worker_wake_up(phba);
- }
+ evtp->evt_arg1 = ndlp;
+ evtp->evt = LPFC_EVT_ELS_RETRY;
+ list_add_tail(&evtp->evt_listp, &phba->work_list);
spin_unlock_irqrestore(&phba->hbalock, flags);
- return;
+
+ lpfc_worker_wake_up(phba);
}
/**
@@ -2772,27 +4491,22 @@ void
lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
{
struct lpfc_vport *vport = ndlp->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- uint32_t cmd, did, retry;
+ uint32_t cmd, retry;
- spin_lock_irq(shost->host_lock);
- did = ndlp->nlp_DID;
+ spin_lock_irq(&ndlp->lock);
cmd = ndlp->nlp_last_elscmd;
ndlp->nlp_last_elscmd = 0;
+ spin_unlock_irq(&ndlp->lock);
- if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
- spin_unlock_irq(shost->host_lock);
+ if (!test_and_clear_bit(NLP_DELAY_TMO, &ndlp->nlp_flag))
return;
- }
- ndlp->nlp_flag &= ~NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
/*
* If a discovery event readded nlp_delayfunc after timer
* firing and before processing the timer, cancel the
* nlp_delayfunc.
*/
- del_timer_sync(&ndlp->nlp_delayfunc);
+ timer_delete_sync(&ndlp->nlp_delayfunc);
retry = ndlp->nlp_retry;
ndlp->nlp_retry = 0;
@@ -2813,6 +4527,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
}
break;
case ELS_CMD_PRLI:
+ case ELS_CMD_NVMEPRLI:
if (!lpfc_issue_els_prli(vport, ndlp, retry)) {
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE);
@@ -2821,11 +4536,11 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
case ELS_CMD_LOGO:
if (!lpfc_issue_els_logo(vport, ndlp, retry)) {
ndlp->nlp_prev_state = ndlp->nlp_state;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_LOGO_ISSUE);
}
break;
case ELS_CMD_FDISC:
- if (!(vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI))
+ if (!test_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag))
lpfc_issue_els_fdisc(vport, ndlp, retry);
break;
}
@@ -2833,6 +4548,62 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
}
/**
+ * lpfc_link_reset - Issue link reset
+ * @vport: pointer to a virtual N_Port data structure.
+ *
+ * This routine performs link reset by sending INIT_LINK mailbox command.
+ * For SLI-3 adapter, link attention interrupt is enabled before issuing
+ * INIT_LINK mailbox command.
+ *
+ * Return code
+ * 0 - Link reset initiated successfully
+ * 1 - Failed to initiate link reset
+ **/
+int
+lpfc_link_reset(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ LPFC_MBOXQ_t *mbox;
+ uint32_t control;
+ int rc;
+
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "2851 Attempt link reset\n");
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2852 Failed to allocate mbox memory");
+ return 1;
+ }
+
+ /* Enable Link attention interrupts */
+ if (phba->sli_rev <= LPFC_SLI_REV3) {
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag |= LPFC_PROCESS_LA;
+ control = readl(phba->HCregaddr);
+ control |= HC_LAINT_ENA;
+ writel(control, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ spin_unlock_irq(&phba->hbalock);
+ }
+
+ lpfc_init_link(phba, mbox, phba->cfg_topology,
+ phba->cfg_link_speed);
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2853 Failed to issue INIT_LINK "
+ "mbox command, rc:x%x\n", rc);
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
* lpfc_els_retry - Make retry decision on an els command iocb
* @phba: pointer to lpfc hba data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
@@ -2841,7 +4612,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
* This routine makes a retry decision on an ELS command IOCB, which has
* failed. The following ELS IOCBs use this function for retrying the command
* when previously issued command responsed with error status: FLOGI, PLOGI,
- * PRLI, ADISC, LOGO, and FDISC. Based on the ELS command type and the
+ * PRLI, ADISC and FDISC. Based on the ELS command type and the
* returned error status, it makes the decision whether a retry shall be
* issued for the command, and whether a retry shall be made immediately or
* delayed. In the former case, the corresponding ELS command issuing-function
@@ -2858,19 +4629,22 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- IOCB_t *irsp = &rspiocb->iocb;
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
- struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ union lpfc_wqe128 *irsp = &rspiocb->wqe;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
+ struct lpfc_dmabuf *pcmd = cmdiocb->cmd_dmabuf;
uint32_t *elscmd;
struct ls_rjt stat;
int retry = 0, maxretry = lpfc_max_els_tries, delay = 0;
int logerr = 0;
uint32_t cmd = 0;
uint32_t did;
+ int link_reset = 0, rc;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
+ u8 rsn_code_exp = 0;
- /* Note: context2 may be 0 for internal driver abort
+ /* Note: cmd_dmabuf may be 0 for internal driver abort
* of delays ELS command.
*/
@@ -2879,22 +4653,21 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
cmd = *elscmd++;
}
- if (ndlp && NLP_CHK_NODE_ACT(ndlp))
+ if (ndlp)
did = ndlp->nlp_DID;
else {
/* We should only hit this case for retrying PLOGI */
- did = irsp->un.elsreq64.remoteID;
+ did = get_job_els_rsp64_did(phba, rspiocb);
ndlp = lpfc_findnode_did(vport, did);
- if ((!ndlp || !NLP_CHK_NODE_ACT(ndlp))
- && (cmd != ELS_CMD_PLOGI))
- return 1;
+ if (!ndlp && (cmd != ELS_CMD_PLOGI))
+ return 0;
}
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"Retry ELS: wd7:x%x wd4:x%x did:x%x",
- *(((uint32_t *) irsp) + 7), irsp->un.ulpWord[4], ndlp->nlp_DID);
+ *(((uint32_t *)irsp) + 7), ulp_word4, did);
- switch (irsp->ulpStatus) {
+ switch (ulp_status) {
case IOSTAT_FCP_RSP_ERROR:
break;
case IOSTAT_REMOTE_STOP:
@@ -2904,28 +4677,19 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* ABTS we cannot generate and RRQ.
*/
lpfc_set_rrq_active(phba, ndlp,
- cmdiocb->sli4_xritag, 0, 0);
+ cmdiocb->sli4_lxritag, 0, 0);
}
break;
case IOSTAT_LOCAL_REJECT:
- switch ((irsp->un.ulpWord[4] & 0xff)) {
+ switch ((ulp_word4 & IOERR_PARAM_MASK)) {
case IOERR_LOOP_OPEN_FAILURE:
- if (cmd == ELS_CMD_FLOGI) {
- if (PCI_DEVICE_ID_HORNET ==
- phba->pcidev->device) {
- phba->fc_topology = LPFC_TOPOLOGY_LOOP;
- phba->pport->fc_myDID = 0;
- phba->alpa_map[0] = 0;
- phba->alpa_map[1] = 0;
- }
- }
if (cmd == ELS_CMD_PLOGI && cmdiocb->retry == 0)
delay = 1000;
retry = 1;
break;
case IOERR_ILLEGAL_COMMAND:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0124 Retry illegal cmd x%x "
"retry:x%x delay:x%x\n",
cmd, cmdiocb->retry, delay);
@@ -2949,16 +4713,56 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
retry = 1;
break;
- case IOERR_SEQUENCE_TIMEOUT:
case IOERR_INVALID_RPI:
+ if (cmd == ELS_CMD_PLOGI &&
+ did == NameServer_DID) {
+ /* Continue forever if plogi to */
+ /* the nameserver fails */
+ maxretry = 0;
+ delay = 100;
+ } else if (cmd == ELS_CMD_PRLI &&
+ ndlp->nlp_state != NLP_STE_PRLI_ISSUE) {
+ /* State-command disagreement. The PRLI was
+ * failed with an invalid rpi meaning there
+ * some unexpected state change. Don't retry.
+ */
+ maxretry = 0;
+ retry = 0;
+ break;
+ }
+ retry = 1;
+ break;
+
+ case IOERR_SEQUENCE_TIMEOUT:
+ if (cmd == ELS_CMD_PLOGI &&
+ did == NameServer_DID &&
+ (cmdiocb->retry + 1) == maxretry) {
+ /* Reset the Link */
+ link_reset = 1;
+ break;
+ }
retry = 1;
+ delay = 100;
+ break;
+ case IOERR_SLI_ABORTED:
+ /* Retry ELS PLOGI command?
+ * Possibly the rport just wasn't ready.
+ */
+ if (cmd == ELS_CMD_PLOGI) {
+ /* No retry if state change */
+ if (ndlp &&
+ ndlp->nlp_state != NLP_STE_PLOGI_ISSUE)
+ goto out_retry;
+ retry = 1;
+ maxretry = 2;
+ }
break;
}
break;
case IOSTAT_NPORT_RJT:
case IOSTAT_FABRIC_RJT:
- if (irsp->un.ulpWord[4] & RJT_UNAVAIL_TEMP) {
+ if (ulp_word4 & RJT_UNAVAIL_TEMP) {
retry = 1;
break;
}
@@ -2971,53 +4775,104 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
break;
case IOSTAT_LS_RJT:
- stat.un.lsRjtError = be32_to_cpu(irsp->un.ulpWord[4]);
+ stat.un.ls_rjt_error_be = cpu_to_be32(ulp_word4);
/* Added for Vendor specifc support
* Just keep retrying for these Rsn / Exp codes
*/
+ if (test_bit(FC_PT2PT, &vport->fc_flag) &&
+ cmd == ELS_CMD_NVMEPRLI) {
+ switch (stat.un.b.lsRjtRsnCode) {
+ case LSRJT_UNABLE_TPC:
+ case LSRJT_INVALID_CMD:
+ case LSRJT_LOGICAL_ERR:
+ case LSRJT_CMD_UNSUPPORTED:
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
+ "0168 NVME PRLI LS_RJT "
+ "reason %x port doesn't "
+ "support NVME, disabling NVME\n",
+ stat.un.b.lsRjtRsnCode);
+ retry = 0;
+ set_bit(FC_PT2PT_NO_NVME, &vport->fc_flag);
+ goto out_retry;
+ }
+ }
switch (stat.un.b.lsRjtRsnCode) {
case LSRJT_UNABLE_TPC:
- if (stat.un.b.lsRjtRsnCodeExp ==
- LSEXP_CMD_IN_PROGRESS) {
+ /* Special case for PRLI LS_RJTs. Recall that lpfc
+ * uses a single routine to issue both PRLI FC4 types.
+ * If the PRLI is rejected because that FC4 type
+ * isn't really supported, don't retry and cause
+ * multiple transport registrations. Otherwise, parse
+ * the reason code/reason code explanation and take the
+ * appropriate action.
+ */
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY | LOG_ELS | LOG_NODE,
+ "0153 ELS cmd x%x LS_RJT by x%x. "
+ "RsnCode x%x RsnCodeExp x%x\n",
+ cmd, did, stat.un.b.lsRjtRsnCode,
+ stat.un.b.lsRjtRsnCodeExp);
+
+ switch (stat.un.b.lsRjtRsnCodeExp) {
+ case LSEXP_CANT_GIVE_DATA:
+ case LSEXP_CMD_IN_PROGRESS:
if (cmd == ELS_CMD_PLOGI) {
delay = 1000;
maxretry = 48;
}
retry = 1;
break;
- }
- if (stat.un.b.lsRjtRsnCodeExp ==
- LSEXP_CANT_GIVE_DATA) {
- if (cmd == ELS_CMD_PLOGI) {
+ case LSEXP_REQ_UNSUPPORTED:
+ case LSEXP_NO_RSRC_ASSIGN:
+ /* These explanation codes get no retry. */
+ if (cmd == ELS_CMD_PRLI ||
+ cmd == ELS_CMD_NVMEPRLI)
+ break;
+ fallthrough;
+ default:
+ /* Limit the delay and retry action to a limited
+ * cmd set. There are other ELS commands where
+ * a retry is not expected.
+ */
+ if (cmd == ELS_CMD_PLOGI ||
+ cmd == ELS_CMD_PRLI ||
+ cmd == ELS_CMD_NVMEPRLI) {
delay = 1000;
- maxretry = 48;
+ maxretry = lpfc_max_els_tries + 1;
+ retry = 1;
}
- retry = 1;
- break;
- }
- if (cmd == ELS_CMD_PLOGI) {
- delay = 1000;
- maxretry = lpfc_max_els_tries + 1;
- retry = 1;
break;
}
+
if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
(cmd == ELS_CMD_FDISC) &&
(stat.un.b.lsRjtRsnCodeExp == LSEXP_OUT_OF_RESOURCE)){
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0125 FDISC Failed (x%x). "
- "Fabric out of resources\n",
- stat.un.lsRjtError);
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "0125 FDISC (x%x). "
+ "Fabric out of resources\n",
+ stat.un.lsRjtError);
lpfc_vport_set_state(vport,
FC_VPORT_NO_FABRIC_RSCS);
}
break;
case LSRJT_LOGICAL_BSY:
+ rsn_code_exp = stat.un.b.lsRjtRsnCodeExp;
if ((cmd == ELS_CMD_PLOGI) ||
- (cmd == ELS_CMD_PRLI)) {
+ (cmd == ELS_CMD_PRLI) ||
+ (cmd == ELS_CMD_NVMEPRLI)) {
delay = 1000;
maxretry = 48;
+
+ /* An authentication LS_RJT reason code
+ * explanation means some error in the
+ * security settings end-to-end. Reduce
+ * the retry count to allow lpfc to clear
+ * RSCN mode and not race with dev_loss.
+ */
+ if (cmd == ELS_CMD_PLOGI &&
+ rsn_code_exp == LSEXP_AUTH_REQ)
+ maxretry = 8;
} else if (cmd == ELS_CMD_FDISC) {
/* FDISC retry policy */
maxretry = 48;
@@ -3037,22 +4892,64 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
maxretry = 3;
delay = 1000;
retry = 1;
- break;
+ } else if (cmd == ELS_CMD_FLOGI &&
+ stat.un.b.lsRjtRsnCodeExp ==
+ LSEXP_NOTHING_MORE) {
+ vport->fc_sparam.cmn.bbRcvSizeMsb &= 0xf;
+ retry = 1;
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "0820 FLOGI (x%x). "
+ "BBCredit Not Supported\n",
+ stat.un.lsRjtError);
+ } else if (cmd == ELS_CMD_PLOGI) {
+ rsn_code_exp = stat.un.b.lsRjtRsnCodeExp;
+
+ /* An authentication LS_RJT reason code
+ * explanation means some error in the
+ * security settings end-to-end. Reduce
+ * the retry count to allow lpfc to clear
+ * RSCN mode and not race with dev_loss.
+ */
+ if (rsn_code_exp == LSEXP_AUTH_REQ) {
+ delay = 1000;
+ retry = 1;
+ maxretry = 8;
+ }
}
+ break;
+
case LSRJT_PROTOCOL_ERR:
if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
(cmd == ELS_CMD_FDISC) &&
((stat.un.b.lsRjtRsnCodeExp == LSEXP_INVALID_PNAME) ||
(stat.un.b.lsRjtRsnCodeExp == LSEXP_INVALID_NPORT_ID))
) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0122 FDISC Failed (x%x). "
- "Fabric Detected Bad WWN\n",
- stat.un.lsRjtError);
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "0122 FDISC (x%x). "
+ "Fabric Detected Bad WWN\n",
+ stat.un.lsRjtError);
lpfc_vport_set_state(vport,
FC_VPORT_FABRIC_REJ_WWN);
}
break;
+ case LSRJT_VENDOR_UNIQUE:
+ if ((stat.un.b.vendorUnique == 0x45) &&
+ (cmd == ELS_CMD_FLOGI)) {
+ goto out_retry;
+ }
+ break;
+ case LSRJT_CMD_UNSUPPORTED:
+ /* lpfc nvmet returns this type of LS_RJT when it
+ * receives an FCP PRLI because lpfc nvmet only
+ * support NVME. ELS request is terminated for FCP4
+ * on this rport.
+ */
+ if (stat.un.b.lsRjtRsnCodeExp ==
+ LSEXP_REQ_UNSUPPORTED) {
+ if (cmd == ELS_CMD_PRLI)
+ goto out_retry;
+ }
+ break;
}
break;
@@ -3064,20 +4961,43 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
break;
}
+ if (link_reset) {
+ rc = lpfc_link_reset(vport);
+ if (rc) {
+ /* Do not give up. Retry PLOGI one more time and attempt
+ * link reset if PLOGI fails again.
+ */
+ retry = 1;
+ delay = 100;
+ goto out_retry;
+ }
+ return 1;
+ }
+
if (did == FDMI_DID)
retry = 1;
- if (((cmd == ELS_CMD_FLOGI) || (cmd == ELS_CMD_FDISC)) &&
+ if ((cmd == ELS_CMD_FLOGI) &&
(phba->fc_topology != LPFC_TOPOLOGY_LOOP) &&
- !lpfc_error_lost_link(irsp)) {
+ !lpfc_error_lost_link(vport, ulp_status, ulp_word4)) {
/* FLOGI retry policy */
retry = 1;
- /* retry forever */
- maxretry = 0;
+ /* retry FLOGI forever */
+ if (phba->link_flag != LS_LOOPBACK_MODE)
+ maxretry = 0;
+ else
+ maxretry = 2;
+
if (cmdiocb->retry >= 100)
delay = 5000;
else if (cmdiocb->retry >= 32)
delay = 1000;
+ } else if ((cmd == ELS_CMD_FDISC) &&
+ !lpfc_error_lost_link(vport, ulp_status, ulp_word4)) {
+ /* retry FDISCs every second up to devloss */
+ retry = 1;
+ maxretry = vport->cfg_devloss_tmo;
+ delay = 1000;
}
cmdiocb->retry++;
@@ -3086,9 +5006,10 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
retry = 0;
}
- if ((vport->load_flag & FC_UNLOADING) != 0)
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
retry = 0;
+out_retry:
if (retry) {
if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_FDISC)) {
/* Stop retrying PLOGI and FDISC if in FCF discovery */
@@ -3109,33 +5030,33 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
cmd, did, cmdiocb->retry, delay);
if (((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) &&
- ((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) ||
- ((irsp->un.ulpWord[4] & 0xff) != IOERR_NO_RESOURCES))) {
+ ((ulp_status != IOSTAT_LOCAL_REJECT) ||
+ ((ulp_word4 & IOERR_PARAM_MASK) !=
+ IOERR_NO_RESOURCES))) {
/* Don't reset timer for no resources */
/* If discovery / RSCN timer is running, reset it */
if (timer_pending(&vport->fc_disctmo) ||
- (vport->fc_flag & FC_RSCN_MODE))
+ test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_set_disctmo(vport);
}
phba->fc_stat.elsXmitRetry++;
- if (ndlp && NLP_CHK_NODE_ACT(ndlp) && delay) {
+ if (ndlp && delay) {
phba->fc_stat.elsDelayRetry++;
ndlp->nlp_retry = cmdiocb->retry;
/* delay is specified in milliseconds */
mod_timer(&ndlp->nlp_delayfunc,
jiffies + msecs_to_jiffies(delay));
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_prev_state = ndlp->nlp_state;
- if (cmd == ELS_CMD_PRLI)
+ if ((cmd == ELS_CMD_PRLI) ||
+ (cmd == ELS_CMD_NVMEPRLI))
lpfc_nlp_set_state(vport, ndlp,
- NLP_STE_REG_LOGIN_ISSUE);
- else
+ NLP_STE_PRLI_ISSUE);
+ else if (cmd != ELS_CMD_ADISC)
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_NPR_NODE);
ndlp->nlp_last_elscmd = cmd;
@@ -3150,7 +5071,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_issue_els_fdisc(vport, ndlp, cmdiocb->retry);
return 1;
case ELS_CMD_PLOGI:
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
+ if (ndlp) {
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_PLOGI_ISSUE);
@@ -3163,31 +5084,35 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_issue_els_adisc(vport, ndlp, cmdiocb->retry);
return 1;
case ELS_CMD_PRLI:
+ case ELS_CMD_NVMEPRLI:
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE);
lpfc_issue_els_prli(vport, ndlp, cmdiocb->retry);
return 1;
case ELS_CMD_LOGO:
ndlp->nlp_prev_state = ndlp->nlp_state;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_LOGO_ISSUE);
lpfc_issue_els_logo(vport, ndlp, cmdiocb->retry);
return 1;
}
}
/* No retry ELS command <elsCmd> to remote NPORT <did> */
if (logerr) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0137 No retry ELS command x%x to remote "
- "NPORT x%x: Out of Resources: Error:x%x/%x\n",
- cmd, did, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
+ "NPORT x%x: Out of Resources: Error:x%x/%x "
+ "IoTag x%x\n",
+ cmd, did, ulp_status, ulp_word4,
+ cmdiocb->iotag);
}
else {
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0108 No retry ELS command x%x to remote "
- "NPORT x%x Retried:%d Error:x%x/%x\n",
- cmd, did, cmdiocb->retry, irsp->ulpStatus,
- irsp->un.ulpWord[4]);
+ "0108 No retry ELS command x%x to remote "
+ "NPORT x%x Retried:%d Error:x%x/%x "
+ "IoTag x%x nflags x%lx\n",
+ cmd, did, cmdiocb->retry, ulp_status,
+ ulp_word4, cmdiocb->iotag,
+ (ndlp ? ndlp->nlp_flag : 0));
}
return 0;
}
@@ -3253,10 +5178,10 @@ lpfc_els_free_bpl(struct lpfc_hba *phba, struct lpfc_dmabuf *buf_ptr)
* command IOCB data structure contains the reference to various associated
* resources, these fields must be set to NULL if the associated reference
* not present:
- * context1 - reference to ndlp
- * context2 - reference to cmd
- * context2->next - reference to rsp
- * context3 - reference to bpl
+ * cmd_dmabuf - reference to cmd.
+ * cmd_dmabuf->next - reference to rsp
+ * rsp_dmabuf - unused
+ * bpl_dmabuf - reference to bpl
*
* It first properly decrements the reference count held on ndlp for the
* IOCB completion callback function. If LPFC_DELAY_MEM_FREE flag is not
@@ -3275,37 +5200,20 @@ int
lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
{
struct lpfc_dmabuf *buf_ptr, *buf_ptr1;
- struct lpfc_nodelist *ndlp;
- ndlp = (struct lpfc_nodelist *)elsiocb->context1;
- if (ndlp) {
- if (ndlp->nlp_flag & NLP_DEFER_RM) {
- lpfc_nlp_put(ndlp);
+ /* The I/O iocb is complete. Clear the node and first dmabuf */
+ elsiocb->ndlp = NULL;
- /* If the ndlp is not being used by another discovery
- * thread, free it.
- */
- if (!lpfc_nlp_not_used(ndlp)) {
- /* If ndlp is being used by another discovery
- * thread, just clear NLP_DEFER_RM
- */
- ndlp->nlp_flag &= ~NLP_DEFER_RM;
- }
- }
- else
- lpfc_nlp_put(ndlp);
- elsiocb->context1 = NULL;
- }
- /* context2 = cmd, context2->next = rsp, context3 = bpl */
- if (elsiocb->context2) {
- if (elsiocb->iocb_flag & LPFC_DELAY_MEM_FREE) {
+ /* cmd_dmabuf = cmd, cmd_dmabuf->next = rsp, bpl_dmabuf = bpl */
+ if (elsiocb->cmd_dmabuf) {
+ if (elsiocb->cmd_flag & LPFC_DELAY_MEM_FREE) {
/* Firmware could still be in progress of DMAing
* payload, so don't free data buffer till after
* a hbeat.
*/
- elsiocb->iocb_flag &= ~LPFC_DELAY_MEM_FREE;
- buf_ptr = elsiocb->context2;
- elsiocb->context2 = NULL;
+ elsiocb->cmd_flag &= ~LPFC_DELAY_MEM_FREE;
+ buf_ptr = elsiocb->cmd_dmabuf;
+ elsiocb->cmd_dmabuf = NULL;
if (buf_ptr) {
buf_ptr1 = NULL;
spin_lock_irq(&phba->hbalock);
@@ -3324,13 +5232,13 @@ lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
spin_unlock_irq(&phba->hbalock);
}
} else {
- buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2;
+ buf_ptr1 = elsiocb->cmd_dmabuf;
lpfc_els_free_data(phba, buf_ptr1);
}
}
- if (elsiocb->context3) {
- buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3;
+ if (elsiocb->bpl_dmabuf) {
+ buf_ptr = elsiocb->bpl_dmabuf;
lpfc_els_free_bpl(phba, buf_ptr);
}
lpfc_sli_release_iocbq(phba, elsiocb);
@@ -3345,50 +5253,63 @@ lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
*
* This routine is the completion callback function to the Logout (LOGO)
* Accept (ACC) Response ELS command. This routine is invoked to indicate
- * the completion of the LOGO process. It invokes the lpfc_nlp_not_used() to
- * release the ndlp if it has the last reference remaining (reference count
- * is 1). If succeeded (meaning ndlp released), it sets the IOCB context1
- * field to NULL to inform the following lpfc_els_free_iocb() routine no
- * ndlp reference count needs to be decremented. Otherwise, the ndlp
- * reference use-count shall be decremented by the lpfc_els_free_iocb()
- * routine. Finally, the lpfc_els_free_iocb() is invoked to release the
- * IOCB data structure.
+ * the completion of the LOGO process. If the node has transitioned to NPR,
+ * this routine unregisters the RPI if it is still registered. The
+ * lpfc_els_free_iocb() is invoked to release the IOCB data structure.
**/
static void
lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
struct lpfc_vport *vport = cmdiocb->vport;
- IOCB_t *irsp;
+ u32 ulp_status, ulp_word4;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
- irsp = &rspiocb->iocb;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
"ACC LOGO cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4], ndlp->nlp_DID);
+ ulp_status, ulp_word4, ndlp->nlp_DID);
/* ACC to LOGO completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0109 ACC to LOGO completes to NPort x%x "
- "Data: x%x x%x x%x\n",
- ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+ "0109 ACC to LOGO completes to NPort x%x refcnt %d "
+ "last els x%x Data: x%lx x%x x%x\n",
+ ndlp->nlp_DID, kref_read(&ndlp->kref),
+ ndlp->nlp_last_elscmd, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
+ /* This clause allows the LOGO ACC to complete and free resources
+ * for the Fabric Domain Controller. It does deliberately skip
+ * the unreg_rpi and release rpi because some fabrics send RDP
+ * requests after logging out from the initiator.
+ */
+ if (ndlp->nlp_type & NLP_FABRIC &&
+ ((ndlp->nlp_DID & WELL_KNOWN_DID_MASK) != WELL_KNOWN_DID_MASK))
+ goto out;
+
if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
- /* NPort Recovery mode or node is just allocated */
- if (!lpfc_nlp_not_used(ndlp)) {
- /* If the ndlp is being used by another discovery
- * thread, just unregister the RPI.
- */
+ if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag))
lpfc_unreg_rpi(vport, ndlp);
- } else {
- /* Indicate the node has already released, should
- * not reference to it from within lpfc_els_free_iocb.
- */
- cmdiocb->context1 = NULL;
+
+ /* If came from PRLO, then PRLO_ACC is done.
+ * Start rediscovery now.
+ */
+ if (ndlp->nlp_last_elscmd == ELS_CMD_PRLO) {
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
+ lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
}
}
+
+ out:
+ /*
+ * The driver received a LOGO from the rport and has ACK'd it.
+ * At this point, the driver is done so release the IOCB
+ */
lpfc_els_free_iocb(phba, cmdiocb);
- return;
+ lpfc_nlp_put(ndlp);
}
/**
@@ -3400,32 +5321,37 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* RPI (Remote Port Index) mailbox command to the @phba. It simply releases
* the associated lpfc Direct Memory Access (DMA) buffer back to the pool and
* decrements the ndlp reference count held for this completion callback
- * function. After that, it invokes the lpfc_nlp_not_used() to check
- * whether there is only one reference left on the ndlp. If so, it will
- * perform one more decrement and trigger the release of the ndlp.
+ * function. After that, it invokes the lpfc_drop_node to check
+ * whether it is appropriate to release the node.
**/
void
lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
- struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
-
- pmb->context1 = NULL;
- pmb->context2 = NULL;
+ struct lpfc_nodelist *ndlp = pmb->ctx_ndlp;
+ u32 mbx_flag = pmb->mbox_flag;
+ u32 mbx_cmd = pmb->u.mb.mbxCommand;
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- lpfc_nlp_put(ndlp);
- /* This is the end of the default RPI cleanup logic for this
- * ndlp. If no other discovery threads are using this ndlp.
- * we should free all resources associated with it.
+ if (ndlp) {
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
+ "0006 rpi x%x DID:%x flg:%lx %d x%px "
+ "mbx_cmd x%x mbx_flag x%x x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
+ kref_read(&ndlp->kref), ndlp, mbx_cmd,
+ mbx_flag, pmb);
+
+ /* This ends the default/temporary RPI cleanup logic for this
+ * ndlp and the node and rpi needs to be released. Free the rpi
+ * first on an UNREG_LOGIN and then release the final
+ * references.
*/
- lpfc_nlp_not_used(ndlp);
+ clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
+ if (mbx_cmd == MBX_UNREG_LOGIN)
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+ lpfc_nlp_put(ndlp);
+ lpfc_drop_node(ndlp->vport, ndlp);
}
- return;
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
}
/**
@@ -3439,163 +5365,173 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
* nlp_flag bitmap in the ndlp data structure, if the mbox command reference
* field in the command IOCB is not NULL, the referred mailbox command will
* be send out, and then invokes the lpfc_els_free_iocb() routine to release
- * the IOCB. Under error conditions, such as when a LS_RJT is returned or a
- * link down event occurred during the discovery, the lpfc_nlp_not_used()
- * routine shall be invoked trying to release the ndlp if no other threads
- * are currently referring it.
+ * the IOCB.
**/
static void
lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
struct lpfc_vport *vport = ndlp ? ndlp->vport : NULL;
struct Scsi_Host *shost = vport ? lpfc_shost_from_vport(vport) : NULL;
IOCB_t *irsp;
- uint8_t *pcmd;
LPFC_MBOXQ_t *mbox = NULL;
- struct lpfc_dmabuf *mp = NULL;
- uint32_t ls_rjt = 0;
-
- irsp = &rspiocb->iocb;
+ u32 ulp_status, ulp_word4, tmo, did, iotag;
+ u32 cmd;
+ if (!vport) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+ "3177 null vport in ELS rsp\n");
+ goto release;
+ }
if (cmdiocb->context_un.mbox)
mbox = cmdiocb->context_un.mbox;
- /* First determine if this is a LS_RJT cmpl. Note, this callback
- * function can have cmdiocb->contest1 (ndlp) field set to NULL.
- */
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) cmdiocb->context2)->virt);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
- (*((uint32_t *) (pcmd)) == ELS_CMD_LS_RJT)) {
- /* A LS_RJT associated with Default RPI cleanup has its own
- * separate code path.
- */
- if (!(ndlp->nlp_flag & NLP_RM_DFLT_RPI))
- ls_rjt = 1;
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+ did = get_job_els_rsp64_did(phba, cmdiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ tmo = get_wqe_tmo(cmdiocb);
+ iotag = get_wqe_reqtag(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ tmo = irsp->ulpTimeout;
+ iotag = irsp->ulpIoTag;
}
/* Check to see if link went down during discovery */
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) || lpfc_els_chk_latt(vport)) {
- if (mbox) {
- mp = (struct lpfc_dmabuf *) mbox->context1;
- if (mp) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
- mempool_free(mbox, phba->mbox_mem_pool);
- }
- if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
- (ndlp->nlp_flag & NLP_RM_DFLT_RPI))
- if (lpfc_nlp_not_used(ndlp)) {
- ndlp = NULL;
- /* Indicate the node has already released,
- * should not reference to it from within
- * the routine lpfc_els_free_iocb.
- */
- cmdiocb->context1 = NULL;
- }
+ if (!ndlp || lpfc_els_chk_latt(vport)) {
+ if (mbox)
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
goto out;
}
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
"ELS rsp cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4],
- cmdiocb->iocb.un.elsreq64.remoteID);
+ ulp_status, ulp_word4, did);
/* ELS response tag <ulpIoTag> completes */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0110 ELS response tag x%x completes "
- "Data: x%x x%x x%x x%x x%x x%x x%x\n",
- cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus,
- rspiocb->iocb.un.ulpWord[4], rspiocb->iocb.ulpTimeout,
+ "0110 ELS response tag x%x completes fc_flag x%lx"
+ "Data: x%x x%x x%x x%x x%lx x%x x%x x%x %p %p\n",
+ iotag, vport->fc_flag, ulp_status, ulp_word4, tmo,
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
- ndlp->nlp_rpi);
- if (mbox) {
- if ((rspiocb->iocb.ulpStatus == 0)
- && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) {
- lpfc_unreg_rpi(vport, ndlp);
+ ndlp->nlp_rpi, kref_read(&ndlp->kref), mbox, ndlp);
+ if (mbox && !test_bit(FC_PT2PT, &vport->fc_flag)) {
+ if (ulp_status == 0 &&
+ test_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag)) {
+ if (!lpfc_unreg_rpi(vport, ndlp) &&
+ !test_bit(FC_PT2PT, &vport->fc_flag)) {
+ if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE ||
+ ndlp->nlp_state ==
+ NLP_STE_REG_LOGIN_ISSUE) {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY,
+ "0314 PLOGI recov "
+ "DID x%x "
+ "Data: x%x x%x x%lx\n",
+ ndlp->nlp_DID,
+ ndlp->nlp_state,
+ ndlp->nlp_rpi,
+ ndlp->nlp_flag);
+ goto out_free_mbox;
+ }
+ }
+
/* Increment reference count to ndlp to hold the
* reference to ndlp for the callback function.
*/
- mbox->context2 = lpfc_nlp_get(ndlp);
+ mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!mbox->ctx_ndlp)
+ goto out_free_mbox;
+
mbox->vport = vport;
- if (ndlp->nlp_flag & NLP_RM_DFLT_RPI) {
+ if (test_bit(NLP_RM_DFLT_RPI, &ndlp->nlp_flag)) {
mbox->mbox_flag |= LPFC_MBX_IMED_UNREG;
mbox->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi;
- }
- else {
+ } else {
mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_REG_LOGIN_ISSUE);
}
+
+ set_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT)
!= MBX_NOT_FINISHED)
goto out;
- else
- /* Decrement the ndlp reference count we
- * set for this failed mailbox command.
- */
- lpfc_nlp_put(ndlp);
+
+ /* Decrement the ndlp reference count we
+ * set for this failed mailbox command.
+ */
+ lpfc_nlp_put(ndlp);
+ clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
/* ELS rsp: Cannot issue reg_login for <NPortid> */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0138 ELS rsp: Cannot issue reg_login for x%x "
- "Data: x%x x%x x%x\n",
+ "Data: x%lx x%x x%x\n",
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
-
- if (lpfc_nlp_not_used(ndlp)) {
- ndlp = NULL;
- /* Indicate node has already been released,
- * should not reference to it from within
- * the routine lpfc_els_free_iocb.
- */
- cmdiocb->context1 = NULL;
- }
- } else {
- /* Do not drop node for lpfc_els_abort'ed ELS cmds */
- if (!lpfc_error_lost_link(irsp) &&
- ndlp->nlp_flag & NLP_ACC_REGLOGIN) {
- if (lpfc_nlp_not_used(ndlp)) {
- ndlp = NULL;
- /* Indicate node has already been
- * released, should not reference
- * to it from within the routine
- * lpfc_els_free_iocb.
- */
- cmdiocb->context1 = NULL;
- }
- }
- }
- mp = (struct lpfc_dmabuf *) mbox->context1;
- if (mp) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
}
- mempool_free(mbox, phba->mbox_mem_pool);
+out_free_mbox:
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ } else if (mbox && test_bit(FC_PT2PT, &vport->fc_flag) &&
+ test_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag)) {
+ lpfc_mbx_cmpl_reg_login(phba, mbox);
+ clear_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag);
}
out:
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_ACC_REGLOGIN | NLP_RM_DFLT_RPI);
- spin_unlock_irq(shost->host_lock);
+ if (ndlp && shost) {
+ if (mbox)
+ clear_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag);
+ clear_bit(NLP_RM_DFLT_RPI, &ndlp->nlp_flag);
+ }
- /* If the node is not being used by another discovery thread,
- * and we are sending a reject, we are done with it.
- * Release driver reference count here and free associated
- * resources.
- */
- if (ls_rjt)
- if (lpfc_nlp_not_used(ndlp))
- /* Indicate node has already been released,
- * should not reference to it from within
- * the routine lpfc_els_free_iocb.
- */
- cmdiocb->context1 = NULL;
+ /* An SLI4 NPIV instance wants to drop the node at this point under
+ * these conditions because it doesn't need the login.
+ */
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ vport->port_type == LPFC_NPIV_PORT &&
+ !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) {
+ if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE &&
+ ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE &&
+ ndlp->nlp_state != NLP_STE_PRLI_ISSUE) {
+ /* Drop ndlp if there is no planned or outstanding
+ * issued PRLI.
+ *
+ * In cases when the ndlp is acting as both an initiator
+ * and target function, let our issued PRLI determine
+ * the final ndlp kref drop.
+ */
+ lpfc_drop_node(vport, ndlp);
+ }
+ }
+
+ /* The driver's unsolicited deferred FLOGI ACC in Pt2Pt needs to
+ * release the initial reference because the put after the free_iocb
+ * call removes only the reference from the defer logic. This FLOGI
+ * is never registered with the SCSI transport.
+ */
+ if (test_bit(FC_PT2PT, &vport->fc_flag) &&
+ test_and_clear_bit(NLP_FLOGI_DFR_ACC, &ndlp->nlp_flag)) {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_NODE | LOG_DISCOVERY,
+ "3357 Pt2Pt Defer FLOGI ACC ndlp x%px, "
+ "nflags x%lx, fc_flag x%lx\n",
+ ndlp, ndlp->nlp_flag,
+ vport->fc_flag);
+ cmd = *((u32 *)cmdiocb->cmd_dmabuf->virt);
+ if (cmd == ELS_CMD_ACC) {
+ if (!test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag))
+ lpfc_nlp_put(ndlp);
+ }
}
+release:
+ /* Release the originating I/O reference. */
lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
return;
}
@@ -3615,10 +5551,10 @@ out:
* field of the IOCB for the completion callback function to issue the
* mailbox command to the HBA later when callback is invoked.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the corresponding response ELS IOCB command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the corresponding
+ * response ELS IOCB command.
*
* Return code
* 0 - Successfully issued acc response
@@ -3629,19 +5565,18 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
struct lpfc_iocbq *oldiocb, struct lpfc_nodelist *ndlp,
LPFC_MBOXQ_t *mbox)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
IOCB_t *icmd;
IOCB_t *oldcmd;
+ union lpfc_wqe128 *wqe;
+ union lpfc_wqe128 *oldwqe = &oldiocb->wqe;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
uint8_t *pcmd;
+ struct serv_parm *sp;
uint16_t cmdsize;
int rc;
ELS_PKT *els_pkt_ptr;
-
- psli = &phba->sli;
- oldcmd = &oldiocb->iocb;
+ struct fc_els_rdf_resp *rdf_resp;
switch (flag) {
case ELS_CMD_ACC:
@@ -3649,22 +5584,38 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry,
ndlp, ndlp->nlp_DID, ELS_CMD_ACC);
if (!elsiocb) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_LOGO_ACC;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
return 1;
}
- icmd = &elsiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
- pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* XRI / rx_id */
+ bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_ctxt_tag,
+ &oldwqe->xmit_els_rsp.wqe_com));
+
+ /* oxid */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_rcvoxid,
+ &oldwqe->xmit_els_rsp.wqe_com));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
+ pcmd = elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC: did:x%x flg:x%x",
+ "Issue ACC: did:x%x flg:x%lx",
ndlp->nlp_DID, ndlp->nlp_flag, 0);
break;
+ case ELS_CMD_FLOGI:
case ELS_CMD_PLOGI:
cmdsize = (sizeof(struct serv_parm) + sizeof(uint32_t));
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry,
@@ -3672,19 +5623,75 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
- pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* XRI / rx_id */
+ bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_ctxt_tag,
+ &oldwqe->xmit_els_rsp.wqe_com));
+
+ /* oxid */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_rcvoxid,
+ &oldwqe->xmit_els_rsp.wqe_com));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
+ pcmd = (u8 *)elsiocb->cmd_dmabuf->virt;
if (mbox)
elsiocb->context_un.mbox = mbox;
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t);
- memcpy(pcmd, &vport->fc_sparam, sizeof(struct serv_parm));
+ sp = (struct serv_parm *)pcmd;
+
+ if (flag == ELS_CMD_FLOGI) {
+ /* Copy the received service parameters back */
+ memcpy(sp, &phba->fc_fabparam,
+ sizeof(struct serv_parm));
+
+ /* Clear the F_Port bit */
+ sp->cmn.fPort = 0;
+
+ /* Mark all class service parameters as invalid */
+ sp->cls1.classValid = 0;
+ sp->cls2.classValid = 0;
+ sp->cls3.classValid = 0;
+
+ /* Copy our worldwide names */
+ memcpy(&sp->portName, &vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ memcpy(&sp->nodeName, &vport->fc_sparam.nodeName,
+ sizeof(struct lpfc_name));
+ } else {
+ memcpy(pcmd, &vport->fc_sparam,
+ sizeof(struct serv_parm));
+
+ sp->cmn.valid_vendor_ver_level = 0;
+ memset(sp->un.vendorVersion, 0,
+ sizeof(sp->un.vendorVersion));
+ if (!test_bit(FC_PT2PT, &vport->fc_flag))
+ sp->cmn.bbRcvSizeMsb &= 0xF;
+
+ /* If our firmware supports this feature, convey that
+ * info to the target using the vendor specific field.
+ */
+ if (phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) {
+ sp->cmn.valid_vendor_ver_level = 1;
+ sp->un.vv.vid = cpu_to_be32(LPFC_VV_EMLX_ID);
+ sp->un.vv.flags =
+ cpu_to_be32(LPFC_VV_SUPPRESS_RSP);
+ }
+ }
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC PLOGI: did:x%x flg:x%x",
+ "Issue ACC FLOGI/PLOGI: did:x%x flg:x%lx",
ndlp->nlp_DID, ndlp->nlp_flag, 0);
break;
case ELS_CMD_PRLO:
@@ -3694,52 +5701,117 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
- pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* XRI / rx_id */
+ bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_ctxt_tag,
+ &oldwqe->xmit_els_rsp.wqe_com));
+
+ /* oxid */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_rcvoxid,
+ &oldwqe->xmit_els_rsp.wqe_com));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
+ pcmd = (u8 *) elsiocb->cmd_dmabuf->virt;
- memcpy(pcmd, ((struct lpfc_dmabuf *) oldiocb->context2)->virt,
+ memcpy(pcmd, oldiocb->cmd_dmabuf->virt,
sizeof(uint32_t) + sizeof(PRLO));
*((uint32_t *) (pcmd)) = ELS_CMD_PRLO_ACC;
els_pkt_ptr = (ELS_PKT *) pcmd;
els_pkt_ptr->un.prlo.acceptRspCode = PRLO_REQ_EXECUTED;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC PRLO: did:x%x flg:x%x",
+ "Issue ACC PRLO: did:x%x flg:x%lx",
ndlp->nlp_DID, ndlp->nlp_flag, 0);
break;
+ case ELS_CMD_RDF:
+ cmdsize = sizeof(*rdf_resp);
+ elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry,
+ ndlp, ndlp->nlp_DID, ELS_CMD_ACC);
+ if (!elsiocb)
+ return 1;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* XRI / rx_id */
+ bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_ctxt_tag,
+ &oldwqe->xmit_els_rsp.wqe_com));
+
+ /* oxid */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ bf_get(wqe_rcvoxid,
+ &oldwqe->xmit_els_rsp.wqe_com));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
+ pcmd = (u8 *)elsiocb->cmd_dmabuf->virt;
+ rdf_resp = (struct fc_els_rdf_resp *)pcmd;
+ memset(rdf_resp, 0, sizeof(*rdf_resp));
+ rdf_resp->acc_hdr.la_cmd = ELS_LS_ACC;
+
+ /* FC-LS-5 specifies desc_list_len shall be set to 12 */
+ rdf_resp->desc_list_len = cpu_to_be32(12);
+
+ /* FC-LS-5 specifies LS REQ Information descriptor */
+ rdf_resp->lsri.desc_tag = cpu_to_be32(1);
+ rdf_resp->lsri.desc_len = cpu_to_be32(sizeof(u32));
+ rdf_resp->lsri.rqst_w0.cmd = ELS_RDF;
+ break;
default:
return 1;
}
- /* Xmit ELS ACC response tag <ulpIoTag> */
- lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0128 Xmit ELS ACC response tag x%x, XRI: x%x, "
- "DID: x%x, nlp_flag: x%x nlp_state: x%x RPI: x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
- ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
- ndlp->nlp_rpi);
- if (ndlp->nlp_flag & NLP_LOGO_ACC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_LOGO_ACC;
- spin_unlock_irq(shost->host_lock);
- elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc;
+ if (test_bit(NLP_LOGO_ACC, &ndlp->nlp_flag)) {
+ if (!test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag) &&
+ !test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag))
+ clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_logo_acc;
} else {
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
}
phba->fc_stat.elsXmitACC++;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
+
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
+ /* Xmit ELS ACC response tag <ulpIoTag> */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "0128 Xmit ELS ACC response Status: x%x, IoTag: x%x, "
+ "XRI: x%x, DID: x%x, nlp_flag: x%lx nlp_state: x%x "
+ "RPI: x%x, fc_flag x%lx refcnt %d\n",
+ rc, elsiocb->iotag, elsiocb->sli4_xritag,
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+ ndlp->nlp_rpi, vport->fc_flag, kref_read(&ndlp->kref));
return 0;
}
/**
- * lpfc_els_rsp_reject - Propare and issue a rjt response iocb command
+ * lpfc_els_rsp_reject - Prepare and issue a rjt response iocb command
* @vport: pointer to a virtual N_Port data structure.
- * @rejectError:
+ * @rejectError: reject response to issue
* @oldiocb: pointer to the original lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
* @mbox: pointer to the driver internal queue element for mailbox command.
@@ -3749,10 +5821,10 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
* context_un.mbox field of the IOCB for the completion callback function
* to issue to the HBA later.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the reject response ELS IOCB command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the reject response
+ * ELS IOCB command.
*
* Return code
* 0 - Successfully issued reject response
@@ -3763,26 +5835,35 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
struct lpfc_iocbq *oldiocb, struct lpfc_nodelist *ndlp,
LPFC_MBOXQ_t *mbox)
{
+ int rc;
struct lpfc_hba *phba = vport->phba;
IOCB_t *icmd;
IOCB_t *oldcmd;
+ union lpfc_wqe128 *wqe;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
- int rc;
- psli = &phba->sli;
cmdsize = 2 * sizeof(uint32_t);
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
ndlp->nlp_DID, ELS_CMD_LS_RJT);
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- oldcmd = &oldiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, oldiocb)); /* Xri / rx_id */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, oldiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
pcmd += sizeof(uint32_t);
@@ -3794,23 +5875,131 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
/* Xmit ELS RJT <err> response tag <ulpIoTag> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0129 Xmit ELS RJT x%x response tag x%x "
- "xri x%x, did x%x, nlp_flag x%x, nlp_state x%x, "
+ "xri x%x, did x%x, nlp_flag x%lx, nlp_state x%x, "
"rpi x%x\n",
rejectError, elsiocb->iotag,
- elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+ get_job_ulpcontext(phba, elsiocb), ndlp->nlp_DID,
ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue LS_RJT: did:x%x flg:x%x err:x%x",
+ "Issue LS_RJT: did:x%x flg:x%lx err:x%x",
ndlp->nlp_DID, ndlp->nlp_flag, rejectError);
phba->fc_stat.elsXmitLSRJT++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
+
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ return 1;
+ }
+
+ return 0;
+}
+
+ /**
+ * lpfc_issue_els_edc_rsp - Exchange Diagnostic Capabilities with the fabric.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to the original lpfc command iocb data structure.
+ * @ndlp: NPort to where rsp is directed
+ *
+ * This routine issues an EDC ACC RSP to the F-Port Controller to communicate
+ * this N_Port's support of hardware signals in its Congestion
+ * Capabilities Descriptor.
+ *
+ * Return code
+ * 0 - Successfully issued edc rsp command
+ * 1 - Failed to issue edc rsp command
+ **/
+static int
+lpfc_issue_els_edc_rsp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_els_edc_resp *edc_rsp;
+ struct fc_tlv_desc *tlv;
+ struct lpfc_iocbq *elsiocb;
+ IOCB_t *icmd, *cmd;
+ union lpfc_wqe128 *wqe;
+ u32 cgn_desc_size, lft_desc_size;
+ u16 cmdsize;
+ uint8_t *pcmd;
+ int rc;
+
+ cmdsize = sizeof(struct fc_els_edc_resp);
+ cgn_desc_size = sizeof(struct fc_diag_cg_sig_desc);
+ lft_desc_size = (lpfc_link_is_lds_capable(phba)) ?
+ sizeof(struct fc_diag_lnkflt_desc) : 0;
+ cmdsize += cgn_desc_size + lft_desc_size;
+ elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, cmdiocb->retry,
+ ndlp, ndlp->nlp_DID, ELS_CMD_ACC);
+ if (!elsiocb)
+ return 1;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, cmdiocb)); /* Xri / rx_id */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, cmdiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ cmd = &cmdiocb->iocb;
+ icmd->ulpContext = cmd->ulpContext; /* Xri / rx_id */
+ icmd->unsli3.rcvsli3.ox_id = cmd->unsli3.rcvsli3.ox_id;
+ }
+
+ pcmd = elsiocb->cmd_dmabuf->virt;
+ memset(pcmd, 0, cmdsize);
+
+ edc_rsp = (struct fc_els_edc_resp *)pcmd;
+ edc_rsp->acc_hdr.la_cmd = ELS_LS_ACC;
+ edc_rsp->desc_list_len = cpu_to_be32(sizeof(struct fc_els_lsri_desc) +
+ cgn_desc_size + lft_desc_size);
+ edc_rsp->lsri.desc_tag = cpu_to_be32(ELS_DTAG_LS_REQ_INFO);
+ edc_rsp->lsri.desc_len = cpu_to_be32(
+ FC_TLV_DESC_LENGTH_FROM_SZ(struct fc_els_lsri_desc));
+ edc_rsp->lsri.rqst_w0.cmd = ELS_EDC;
+ tlv = edc_rsp->desc;
+ lpfc_format_edc_cgn_desc(phba, tlv);
+ tlv = fc_tlv_next_desc(tlv);
+ if (lft_desc_size)
+ lpfc_format_edc_lft_desc(phba, tlv);
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
+ "Issue EDC ACC: did:x%x flg:x%lx refcnt %d",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ kref_read(&ndlp->kref));
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ phba->fc_stat.elsXmitACC++;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
+ /* Xmit ELS ACC response tag <ulpIoTag> */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "0152 Xmit EDC ACC response Status: x%x, IoTag: x%x, "
+ "XRI: x%x, DID: x%x, nlp_flag: x%lx nlp_state: x%x "
+ "RPI: x%x, fc_flag x%lx\n",
+ rc, elsiocb->iotag, elsiocb->sli4_xritag,
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+ ndlp->nlp_rpi, vport->fc_flag);
+
return 0;
}
@@ -3824,10 +6013,10 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
* Discover (ADISC) ELS command. It simply prepares the payload of the IOCB
* and invokes the lpfc_sli_issue_iocb() routine to send out the command.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the ADISC Accept response ELS IOCB command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the ADISC Accept response
+ * ELS IOCB command.
*
* Return code
* 0 - Successfully issued acc adisc response
@@ -3840,10 +6029,12 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
struct lpfc_hba *phba = vport->phba;
ADISC *ap;
IOCB_t *icmd, *oldcmd;
+ union lpfc_wqe128 *wqe;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
+ u32 ulp_context;
cmdsize = sizeof(uint32_t) + sizeof(ADISC);
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
@@ -3851,18 +6042,32 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- oldcmd = &oldiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* XRI / rx_id */
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, oldiocb));
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
+ /* oxid */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, oldiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ ulp_context = elsiocb->iocb.ulpContext;
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
/* Xmit ADISC ACC response tag <ulpIoTag> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0130 Xmit ADISC ACC response iotag x%x xri: "
- "x%x, did x%x, nlp_flag x%x, nlp_state x%x rpi x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
+ "x%x, did x%x, nlp_flag x%lx, nlp_state x%x rpi x%x\n",
+ elsiocb->iotag, ulp_context,
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t);
@@ -3874,16 +6079,24 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
ap->DID = be32_to_cpu(vport->fc_myDID);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC ADISC: did:x%x flg:x%x",
- ndlp->nlp_DID, ndlp->nlp_flag, 0);
+ "Issue ACC ADISC: did:x%x flg:x%lx refcnt %d",
+ ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref));
phba->fc_stat.elsXmitACC++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
+
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
return 0;
}
@@ -3897,10 +6110,10 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
* Login (PRLI) ELS command. It simply prepares the payload of the IOCB
* and invokes the lpfc_sli_issue_iocb() routine to send out the command.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the PRLI Accept response ELS IOCB command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the PRLI Accept response
+ * ELS IOCB command.
*
* Return code
* 0 - Successfully issued acc prli response
@@ -3912,74 +6125,170 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
{
struct lpfc_hba *phba = vport->phba;
PRLI *npr;
+ struct lpfc_nvme_prli *npr_nvme;
lpfc_vpd_t *vpd;
IOCB_t *icmd;
IOCB_t *oldcmd;
+ union lpfc_wqe128 *wqe;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
+ uint32_t prli_fc4_req, *req_payload;
+ struct lpfc_dmabuf *req_buf;
int rc;
+ u32 elsrspcmd, ulp_context;
- psli = &phba->sli;
+ /* Need the incoming PRLI payload to determine if the ACC is for an
+ * FC4 or NVME PRLI type. The PRLI type is at word 1.
+ */
+ req_buf = oldiocb->cmd_dmabuf;
+ req_payload = (((uint32_t *)req_buf->virt) + 1);
+
+ /* PRLI type payload is at byte 3 for FCP or NVME. */
+ prli_fc4_req = be32_to_cpu(*req_payload);
+ prli_fc4_req = (prli_fc4_req >> 24) & 0xff;
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "6127 PRLI_ACC: Req Type x%x, Word1 x%08x\n",
+ prli_fc4_req, *((uint32_t *)req_payload));
+
+ if (prli_fc4_req == PRLI_FCP_TYPE) {
+ cmdsize = sizeof(uint32_t) + sizeof(PRLI);
+ elsrspcmd = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK));
+ } else if (prli_fc4_req == PRLI_NVME_TYPE) {
+ cmdsize = sizeof(uint32_t) + sizeof(struct lpfc_nvme_prli);
+ elsrspcmd = (ELS_CMD_ACC | (ELS_CMD_NVMEPRLI & ~ELS_RSP_MASK));
+ } else {
+ return 1;
+ }
- cmdsize = sizeof(uint32_t) + sizeof(PRLI);
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
- ndlp->nlp_DID, (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK)));
+ ndlp->nlp_DID, elsrspcmd);
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- oldcmd = &oldiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, oldiocb)); /* Xri / rx_id */
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, oldiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ ulp_context = elsiocb->iocb.ulpContext;
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
/* Xmit PRLI ACC response tag <ulpIoTag> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0131 Xmit PRLI ACC response tag x%x xri x%x, "
- "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
+ "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x\n",
+ elsiocb->iotag, ulp_context,
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
+ memset(pcmd, 0, cmdsize);
- *((uint32_t *) (pcmd)) = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK));
+ *((uint32_t *)(pcmd)) = elsrspcmd;
pcmd += sizeof(uint32_t);
/* For PRLI, remainder of payload is PRLI parameter page */
- memset(pcmd, 0, sizeof(PRLI));
-
- npr = (PRLI *) pcmd;
vpd = &phba->vpd;
- /*
- * If the remote port is a target and our firmware version is 3.20 or
- * later, set the following bits for FC-TAPE support.
- */
- if ((ndlp->nlp_type & NLP_FCP_TARGET) &&
- (vpd->rev.feaLevelHigh >= 0x02)) {
- npr->ConfmComplAllowed = 1;
- npr->Retry = 1;
- npr->TaskRetryIdReq = 1;
- }
- npr->acceptRspCode = PRLI_REQ_EXECUTED;
- npr->estabImagePair = 1;
- npr->readXferRdyDis = 1;
- npr->ConfmComplAllowed = 1;
+ if (prli_fc4_req == PRLI_FCP_TYPE) {
+ /*
+ * If the remote port is a target and our firmware version
+ * is 3.20 or later, set the following bits for FC-TAPE
+ * support.
+ */
+ npr = (PRLI *) pcmd;
+ if ((ndlp->nlp_type & NLP_FCP_TARGET) &&
+ (vpd->rev.feaLevelHigh >= 0x02)) {
+ npr->ConfmComplAllowed = 1;
+ npr->Retry = 1;
+ npr->TaskRetryIdReq = 1;
+ }
+ npr->acceptRspCode = PRLI_REQ_EXECUTED;
+
+ /* Set image pair for complementary pairs only. */
+ if (ndlp->nlp_type & NLP_FCP_TARGET)
+ npr->estabImagePair = 1;
+ else
+ npr->estabImagePair = 0;
+ npr->readXferRdyDis = 1;
+ npr->ConfmComplAllowed = 1;
+ npr->prliType = PRLI_FCP_TYPE;
+ npr->initiatorFunc = 1;
+
+ /* Xmit PRLI ACC response tag <ulpIoTag> */
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_NODE | LOG_DISCOVERY,
+ "6014 FCP issue PRLI ACC imgpair %d "
+ "retry %d task %d\n",
+ npr->estabImagePair,
+ npr->Retry, npr->TaskRetryIdReq);
+
+ } else if (prli_fc4_req == PRLI_NVME_TYPE) {
+ /* Respond with an NVME PRLI Type */
+ npr_nvme = (struct lpfc_nvme_prli *) pcmd;
+ bf_set(prli_type_code, npr_nvme, PRLI_NVME_TYPE);
+ bf_set(prli_estabImagePair, npr_nvme, 0); /* Should be 0 */
+ bf_set(prli_acc_rsp_code, npr_nvme, PRLI_REQ_EXECUTED);
+ if (phba->nvmet_support) {
+ bf_set(prli_tgt, npr_nvme, 1);
+ bf_set(prli_disc, npr_nvme, 1);
+ if (phba->cfg_nvme_enable_fb) {
+ bf_set(prli_fba, npr_nvme, 1);
+
+ /* TBD. Target mode needs to post buffers
+ * that support the configured first burst
+ * byte size.
+ */
+ bf_set(prli_fb_sz, npr_nvme,
+ phba->cfg_nvmet_fb_size);
+ }
+ } else {
+ bf_set(prli_init, npr_nvme, 1);
+ }
- npr->prliType = PRLI_FCP_TYPE;
- npr->initiatorFunc = 1;
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6015 NVME issue PRLI ACC word1 x%08x "
+ "word4 x%08x word5 x%08x flag x%lx, "
+ "fcp_info x%x nlp_type x%x\n",
+ npr_nvme->word1, npr_nvme->word4,
+ npr_nvme->word5, ndlp->nlp_flag,
+ ndlp->nlp_fcp_info, ndlp->nlp_type);
+ npr_nvme->word1 = cpu_to_be32(npr_nvme->word1);
+ npr_nvme->word4 = cpu_to_be32(npr_nvme->word4);
+ npr_nvme->word5 = cpu_to_be32(npr_nvme->word5);
+ } else
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6128 Unknown FC_TYPE x%x x%x ndlp x%06x\n",
+ prli_fc4_req, ndlp->nlp_fc4_type,
+ ndlp->nlp_DID);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC PRLI: did:x%x flg:x%x",
- ndlp->nlp_DID, ndlp->nlp_flag, 0);
+ "Issue ACC PRLI: did:x%x flg:x%lx",
+ ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref));
phba->fc_stat.elsXmitACC++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
return 0;
}
@@ -3993,17 +6302,11 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
* This routine issues a Request Node Identification Data (RNID) Accept
* (ACC) response. It constructs the RNID ACC response command according to
* the proper @format and then calls the lpfc_sli_issue_iocb() routine to
- * issue the response. Note that this command does not need to hold the ndlp
- * reference count for the callback. So, the ndlp reference count taken by
- * the lpfc_prep_els_iocb() routine is put back and the context1 field of
- * IOCB is set to NULL to indicate to the lpfc_els_free_iocb() routine that
- * there is no ndlp reference available.
- *
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function. However, for the RNID Accept Response ELS command,
- * this is undone later by this routine after the IOCB is allocated.
+ * issue the response.
+ *
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function.
*
* Return code
* 0 - Successfully issued acc rnid response
@@ -4016,13 +6319,13 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
struct lpfc_hba *phba = vport->phba;
RNID *rn;
IOCB_t *icmd, *oldcmd;
+ union lpfc_wqe128 *wqe;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
+ u32 ulp_context;
- psli = &phba->sli;
cmdsize = sizeof(uint32_t) + sizeof(uint32_t)
+ (2 * sizeof(struct lpfc_name));
if (format)
@@ -4033,14 +6336,27 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- oldcmd = &oldiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, oldiocb)); /* Xri / rx_id */
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, oldiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ ulp_context = elsiocb->iocb.ulpContext;
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
/* Xmit RNID ACC response tag <ulpIoTag> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0132 Xmit RNID ACC response tag x%x xri x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext);
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ elsiocb->iotag, ulp_context);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t);
@@ -4069,20 +6385,24 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
}
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC RNID: did:x%x flg:x%x",
- ndlp->nlp_DID, ndlp->nlp_flag, 0);
+ "Issue ACC RNID: did:x%x flg:x%lx refcnt %d",
+ ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref));
phba->fc_stat.elsXmitACC++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
- lpfc_nlp_put(ndlp);
- elsiocb->context1 = NULL; /* Don't need ndlp for cmpl,
- * it could be freed */
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
return 0;
}
@@ -4096,7 +6416,7 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
**/
static void
lpfc_els_clear_rrq(struct lpfc_vport *vport,
- struct lpfc_iocbq *iocb, struct lpfc_nodelist *ndlp)
+ struct lpfc_iocbq *iocb, struct lpfc_nodelist *ndlp)
{
struct lpfc_hba *phba = vport->phba;
uint8_t *pcmd;
@@ -4106,7 +6426,7 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport,
struct lpfc_node_rrq *prrq;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) iocb->context2)->virt);
+ pcmd = (uint8_t *)iocb->cmd_dmabuf->virt;
pcmd += sizeof(uint32_t);
rrq = (struct RRQ *)pcmd;
rrq->rrq_exchg = be32_to_cpu(rrq->rrq_exchg);
@@ -4118,10 +6438,11 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport,
be32_to_cpu(bf_get(rrq_did, rrq)),
bf_get(rrq_oxid, rrq),
rxid,
- iocb->iotag, iocb->iocb.ulpContext);
+ get_wqe_reqtag(iocb),
+ get_job_ulpcontext(phba, iocb));
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Clear RRQ: did:x%x flg:x%x exchg:x%.08x",
+ "Clear RRQ: did:x%x flg:x%lx exchg:x%.08x",
ndlp->nlp_DID, ndlp->nlp_flag, rrq->rrq_exchg);
if (vport->fc_myDID == be32_to_cpu(bf_get(rrq_did, rrq)))
xri = bf_get(rrq_oxid, rrq);
@@ -4149,45 +6470,73 @@ lpfc_els_rsp_echo_acc(struct lpfc_vport *vport, uint8_t *data,
struct lpfc_iocbq *oldiocb, struct lpfc_nodelist *ndlp)
{
struct lpfc_hba *phba = vport->phba;
+ IOCB_t *icmd, *oldcmd;
+ union lpfc_wqe128 *wqe;
struct lpfc_iocbq *elsiocb;
- struct lpfc_sli *psli;
uint8_t *pcmd;
uint16_t cmdsize;
int rc;
+ u32 ulp_context;
- psli = &phba->sli;
- cmdsize = oldiocb->iocb.unsli3.rcvsli3.acc_len;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ cmdsize = oldiocb->wcqe_cmpl.total_data_placed;
+ else
+ cmdsize = oldiocb->iocb.unsli3.rcvsli3.acc_len;
+ /* The accumulated length can exceed the BPL_SIZE. For
+ * now, use this as the limit
+ */
+ if (cmdsize > LPFC_BPL_SIZE)
+ cmdsize = LPFC_BPL_SIZE;
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
ndlp->nlp_DID, ELS_CMD_ACC);
if (!elsiocb)
return 1;
- elsiocb->iocb.ulpContext = oldiocb->iocb.ulpContext; /* Xri */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, oldiocb)); /* Xri / rx_id */
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, oldiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ oldcmd = &oldiocb->iocb;
+ icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */
+ ulp_context = elsiocb->iocb.ulpContext;
+ icmd->unsli3.rcvsli3.ox_id =
+ oldcmd->unsli3.rcvsli3.ox_id;
+ }
+
/* Xmit ECHO ACC response tag <ulpIoTag> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"2876 Xmit ECHO ACC response tag x%x xri x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext);
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ elsiocb->iotag, ulp_context);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t);
memcpy(pcmd, data, cmdsize - sizeof(uint32_t));
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC ECHO: did:x%x flg:x%x",
- ndlp->nlp_DID, ndlp->nlp_flag, 0);
+ "Issue ACC ECHO: did:x%x flg:x%lx refcnt %d",
+ ndlp->nlp_DID, ndlp->nlp_flag, kref_read(&ndlp->kref));
phba->fc_stat.elsXmitACC++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
- lpfc_nlp_put(ndlp);
- elsiocb->context1 = NULL; /* Don't need ndlp for cmpl,
- * it could be freed */
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
+ }
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
return 1;
}
+
return 0;
}
@@ -4213,39 +6562,45 @@ lpfc_els_rsp_echo_acc(struct lpfc_vport *vport, uint8_t *data,
int
lpfc_els_disc_adisc(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp, *next_ndlp;
int sentadisc = 0;
/* go thru NPR nodes and issue any remaining ELS ADISCs */
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
+
+ if (ndlp->nlp_state != NLP_STE_NPR_NODE ||
+ !test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag))
+ continue;
+
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
+
+ if (!test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
+ /* This node was marked for ADISC but was not picked
+ * for discovery. This is possible if the node was
+ * missing in gidft response.
+ *
+ * At time of marking node for ADISC, we skipped unreg
+ * from backend
+ */
+ lpfc_nlp_unreg_node(vport, ndlp);
+ lpfc_unreg_rpi(vport, ndlp);
continue;
- if (ndlp->nlp_state == NLP_STE_NPR_NODE &&
- (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 &&
- (ndlp->nlp_flag & NLP_NPR_ADISC) != 0) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
- ndlp->nlp_prev_state = ndlp->nlp_state;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_ADISC_ISSUE);
- lpfc_issue_els_adisc(vport, ndlp, 0);
- sentadisc++;
- vport->num_disc_nodes++;
- if (vport->num_disc_nodes >=
- vport->cfg_discovery_threads) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_NLP_MORE;
- spin_unlock_irq(shost->host_lock);
- break;
- }
}
+
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_ADISC_ISSUE);
+ lpfc_issue_els_adisc(vport, ndlp, 0);
+ sentadisc++;
+ vport->num_disc_nodes++;
+ if (vport->num_disc_nodes >=
+ vport->cfg_discovery_threads) {
+ set_bit(FC_NLP_MORE, &vport->fc_flag);
+ break;
+ }
+
}
- if (sentadisc == 0) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NLP_MORE;
- spin_unlock_irq(shost->host_lock);
- }
+ if (sentadisc == 0)
+ clear_bit(FC_NLP_MORE, &vport->fc_flag);
return sentadisc;
}
@@ -4271,43 +6626,1204 @@ lpfc_els_disc_adisc(struct lpfc_vport *vport)
int
lpfc_els_disc_plogi(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp, *next_ndlp;
int sentplogi = 0;
/* go thru NPR nodes and issue any remaining ELS PLOGIs */
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
if (ndlp->nlp_state == NLP_STE_NPR_NODE &&
- (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 &&
- (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 &&
- (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) {
+ test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag) &&
+ !test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag) &&
+ !test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) {
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
sentplogi++;
vport->num_disc_nodes++;
if (vport->num_disc_nodes >=
- vport->cfg_discovery_threads) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_NLP_MORE;
- spin_unlock_irq(shost->host_lock);
+ vport->cfg_discovery_threads) {
+ set_bit(FC_NLP_MORE, &vport->fc_flag);
break;
}
}
}
- if (sentplogi) {
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6452 Discover PLOGI %d flag x%lx\n",
+ sentplogi, vport->fc_flag);
+
+ if (sentplogi)
lpfc_set_disctmo(vport);
+ else
+ clear_bit(FC_NLP_MORE, &vport->fc_flag);
+ return sentplogi;
+}
+
+static uint32_t
+lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc,
+ uint32_t word0)
+{
+
+ desc->tag = cpu_to_be32(RDP_LINK_SERVICE_DESC_TAG);
+ desc->payload.els_req = word0;
+ desc->length = cpu_to_be32(sizeof(desc->payload));
+
+ return sizeof(struct fc_rdp_link_service_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc,
+ uint8_t *page_a0, uint8_t *page_a2)
+{
+ uint16_t wavelength;
+ uint16_t temperature;
+ uint16_t rx_power;
+ uint16_t tx_bias;
+ uint16_t tx_power;
+ uint16_t vcc;
+ uint16_t flag = 0;
+ struct sff_trasnceiver_codes_byte4 *trasn_code_byte4;
+ struct sff_trasnceiver_codes_byte5 *trasn_code_byte5;
+
+ desc->tag = cpu_to_be32(RDP_SFP_DESC_TAG);
+
+ trasn_code_byte4 = (struct sff_trasnceiver_codes_byte4 *)
+ &page_a0[SSF_TRANSCEIVER_CODE_B4];
+ trasn_code_byte5 = (struct sff_trasnceiver_codes_byte5 *)
+ &page_a0[SSF_TRANSCEIVER_CODE_B5];
+
+ if ((trasn_code_byte4->fc_sw_laser) ||
+ (trasn_code_byte5->fc_sw_laser_sl) ||
+ (trasn_code_byte5->fc_sw_laser_sn)) { /* check if its short WL */
+ flag |= (SFP_FLAG_PT_SWLASER << SFP_FLAG_PT_SHIFT);
+ } else if (trasn_code_byte4->fc_lw_laser) {
+ wavelength = (page_a0[SSF_WAVELENGTH_B1] << 8) |
+ page_a0[SSF_WAVELENGTH_B0];
+ if (wavelength == SFP_WAVELENGTH_LC1310)
+ flag |= SFP_FLAG_PT_LWLASER_LC1310 << SFP_FLAG_PT_SHIFT;
+ if (wavelength == SFP_WAVELENGTH_LL1550)
+ flag |= SFP_FLAG_PT_LWLASER_LL1550 << SFP_FLAG_PT_SHIFT;
+ }
+ /* check if its SFP+ */
+ flag |= ((page_a0[SSF_IDENTIFIER] == SFF_PG0_IDENT_SFP) ?
+ SFP_FLAG_CT_SFP_PLUS : SFP_FLAG_CT_UNKNOWN)
+ << SFP_FLAG_CT_SHIFT;
+
+ /* check if its OPTICAL */
+ flag |= ((page_a0[SSF_CONNECTOR] == SFF_PG0_CONNECTOR_LC) ?
+ SFP_FLAG_IS_OPTICAL_PORT : 0)
+ << SFP_FLAG_IS_OPTICAL_SHIFT;
+
+ temperature = (page_a2[SFF_TEMPERATURE_B1] << 8 |
+ page_a2[SFF_TEMPERATURE_B0]);
+ vcc = (page_a2[SFF_VCC_B1] << 8 |
+ page_a2[SFF_VCC_B0]);
+ tx_power = (page_a2[SFF_TXPOWER_B1] << 8 |
+ page_a2[SFF_TXPOWER_B0]);
+ tx_bias = (page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 |
+ page_a2[SFF_TX_BIAS_CURRENT_B0]);
+ rx_power = (page_a2[SFF_RXPOWER_B1] << 8 |
+ page_a2[SFF_RXPOWER_B0]);
+ desc->sfp_info.temperature = cpu_to_be16(temperature);
+ desc->sfp_info.rx_power = cpu_to_be16(rx_power);
+ desc->sfp_info.tx_bias = cpu_to_be16(tx_bias);
+ desc->sfp_info.tx_power = cpu_to_be16(tx_power);
+ desc->sfp_info.vcc = cpu_to_be16(vcc);
+
+ desc->sfp_info.flags = cpu_to_be16(flag);
+ desc->length = cpu_to_be32(sizeof(desc->sfp_info));
+
+ return sizeof(struct fc_rdp_sfp_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc,
+ READ_LNK_VAR *stat)
+{
+ uint32_t type;
+
+ desc->tag = cpu_to_be32(RDP_LINK_ERROR_STATUS_DESC_TAG);
+
+ type = VN_PT_PHY_PF_PORT << VN_PT_PHY_SHIFT;
+
+ desc->info.port_type = cpu_to_be32(type);
+
+ desc->info.link_status.link_failure_cnt =
+ cpu_to_be32(stat->linkFailureCnt);
+ desc->info.link_status.loss_of_synch_cnt =
+ cpu_to_be32(stat->lossSyncCnt);
+ desc->info.link_status.loss_of_signal_cnt =
+ cpu_to_be32(stat->lossSignalCnt);
+ desc->info.link_status.primitive_seq_proto_err =
+ cpu_to_be32(stat->primSeqErrCnt);
+ desc->info.link_status.invalid_trans_word =
+ cpu_to_be32(stat->invalidXmitWord);
+ desc->info.link_status.invalid_crc_cnt = cpu_to_be32(stat->crcCnt);
+
+ desc->length = cpu_to_be32(sizeof(desc->info));
+
+ return sizeof(struct fc_rdp_link_error_status_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_bbc_desc(struct fc_rdp_bbc_desc *desc, READ_LNK_VAR *stat,
+ struct lpfc_vport *vport)
+{
+ uint32_t bbCredit;
+
+ desc->tag = cpu_to_be32(RDP_BBC_DESC_TAG);
+
+ bbCredit = vport->fc_sparam.cmn.bbCreditLsb |
+ (vport->fc_sparam.cmn.bbCreditMsb << 8);
+ desc->bbc_info.port_bbc = cpu_to_be32(bbCredit);
+ if (vport->phba->fc_topology != LPFC_TOPOLOGY_LOOP) {
+ bbCredit = vport->phba->fc_fabparam.cmn.bbCreditLsb |
+ (vport->phba->fc_fabparam.cmn.bbCreditMsb << 8);
+ desc->bbc_info.attached_port_bbc = cpu_to_be32(bbCredit);
+ } else {
+ desc->bbc_info.attached_port_bbc = 0;
}
- else {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NLP_MORE;
- spin_unlock_irq(shost->host_lock);
+
+ desc->bbc_info.rtt = 0;
+ desc->length = cpu_to_be32(sizeof(desc->bbc_info));
+
+ return sizeof(struct fc_rdp_bbc_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_oed_temp_desc(struct lpfc_hba *phba,
+ struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2)
+{
+ uint32_t flags = 0;
+
+ desc->tag = cpu_to_be32(RDP_OED_DESC_TAG);
+
+ desc->oed_info.hi_alarm = page_a2[SSF_TEMP_HIGH_ALARM];
+ desc->oed_info.lo_alarm = page_a2[SSF_TEMP_LOW_ALARM];
+ desc->oed_info.hi_warning = page_a2[SSF_TEMP_HIGH_WARNING];
+ desc->oed_info.lo_warning = page_a2[SSF_TEMP_LOW_WARNING];
+
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_HIGH_TEMPERATURE)
+ flags |= RDP_OET_HIGH_ALARM;
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_LOW_TEMPERATURE)
+ flags |= RDP_OET_LOW_ALARM;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_HIGH_TEMPERATURE)
+ flags |= RDP_OET_HIGH_WARNING;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_LOW_TEMPERATURE)
+ flags |= RDP_OET_LOW_WARNING;
+
+ flags |= ((0xf & RDP_OED_TEMPERATURE) << RDP_OED_TYPE_SHIFT);
+ desc->oed_info.function_flags = cpu_to_be32(flags);
+ desc->length = cpu_to_be32(sizeof(desc->oed_info));
+ return sizeof(struct fc_rdp_oed_sfp_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_oed_voltage_desc(struct lpfc_hba *phba,
+ struct fc_rdp_oed_sfp_desc *desc,
+ uint8_t *page_a2)
+{
+ uint32_t flags = 0;
+
+ desc->tag = cpu_to_be32(RDP_OED_DESC_TAG);
+
+ desc->oed_info.hi_alarm = page_a2[SSF_VOLTAGE_HIGH_ALARM];
+ desc->oed_info.lo_alarm = page_a2[SSF_VOLTAGE_LOW_ALARM];
+ desc->oed_info.hi_warning = page_a2[SSF_VOLTAGE_HIGH_WARNING];
+ desc->oed_info.lo_warning = page_a2[SSF_VOLTAGE_LOW_WARNING];
+
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_HIGH_VOLTAGE)
+ flags |= RDP_OET_HIGH_ALARM;
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_LOW_VOLTAGE)
+ flags |= RDP_OET_LOW_ALARM;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_HIGH_VOLTAGE)
+ flags |= RDP_OET_HIGH_WARNING;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_LOW_VOLTAGE)
+ flags |= RDP_OET_LOW_WARNING;
+
+ flags |= ((0xf & RDP_OED_VOLTAGE) << RDP_OED_TYPE_SHIFT);
+ desc->oed_info.function_flags = cpu_to_be32(flags);
+ desc->length = cpu_to_be32(sizeof(desc->oed_info));
+ return sizeof(struct fc_rdp_oed_sfp_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_oed_txbias_desc(struct lpfc_hba *phba,
+ struct fc_rdp_oed_sfp_desc *desc,
+ uint8_t *page_a2)
+{
+ uint32_t flags = 0;
+
+ desc->tag = cpu_to_be32(RDP_OED_DESC_TAG);
+
+ desc->oed_info.hi_alarm = page_a2[SSF_BIAS_HIGH_ALARM];
+ desc->oed_info.lo_alarm = page_a2[SSF_BIAS_LOW_ALARM];
+ desc->oed_info.hi_warning = page_a2[SSF_BIAS_HIGH_WARNING];
+ desc->oed_info.lo_warning = page_a2[SSF_BIAS_LOW_WARNING];
+
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_HIGH_TXBIAS)
+ flags |= RDP_OET_HIGH_ALARM;
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_LOW_TXBIAS)
+ flags |= RDP_OET_LOW_ALARM;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_HIGH_TXBIAS)
+ flags |= RDP_OET_HIGH_WARNING;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_LOW_TXBIAS)
+ flags |= RDP_OET_LOW_WARNING;
+
+ flags |= ((0xf & RDP_OED_TXBIAS) << RDP_OED_TYPE_SHIFT);
+ desc->oed_info.function_flags = cpu_to_be32(flags);
+ desc->length = cpu_to_be32(sizeof(desc->oed_info));
+ return sizeof(struct fc_rdp_oed_sfp_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_oed_txpower_desc(struct lpfc_hba *phba,
+ struct fc_rdp_oed_sfp_desc *desc,
+ uint8_t *page_a2)
+{
+ uint32_t flags = 0;
+
+ desc->tag = cpu_to_be32(RDP_OED_DESC_TAG);
+
+ desc->oed_info.hi_alarm = page_a2[SSF_TXPOWER_HIGH_ALARM];
+ desc->oed_info.lo_alarm = page_a2[SSF_TXPOWER_LOW_ALARM];
+ desc->oed_info.hi_warning = page_a2[SSF_TXPOWER_HIGH_WARNING];
+ desc->oed_info.lo_warning = page_a2[SSF_TXPOWER_LOW_WARNING];
+
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_HIGH_TXPOWER)
+ flags |= RDP_OET_HIGH_ALARM;
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_LOW_TXPOWER)
+ flags |= RDP_OET_LOW_ALARM;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_HIGH_TXPOWER)
+ flags |= RDP_OET_HIGH_WARNING;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_LOW_TXPOWER)
+ flags |= RDP_OET_LOW_WARNING;
+
+ flags |= ((0xf & RDP_OED_TXPOWER) << RDP_OED_TYPE_SHIFT);
+ desc->oed_info.function_flags = cpu_to_be32(flags);
+ desc->length = cpu_to_be32(sizeof(desc->oed_info));
+ return sizeof(struct fc_rdp_oed_sfp_desc);
+}
+
+
+static uint32_t
+lpfc_rdp_res_oed_rxpower_desc(struct lpfc_hba *phba,
+ struct fc_rdp_oed_sfp_desc *desc,
+ uint8_t *page_a2)
+{
+ uint32_t flags = 0;
+
+ desc->tag = cpu_to_be32(RDP_OED_DESC_TAG);
+
+ desc->oed_info.hi_alarm = page_a2[SSF_RXPOWER_HIGH_ALARM];
+ desc->oed_info.lo_alarm = page_a2[SSF_RXPOWER_LOW_ALARM];
+ desc->oed_info.hi_warning = page_a2[SSF_RXPOWER_HIGH_WARNING];
+ desc->oed_info.lo_warning = page_a2[SSF_RXPOWER_LOW_WARNING];
+
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_HIGH_RXPOWER)
+ flags |= RDP_OET_HIGH_ALARM;
+ if (phba->sfp_alarm & LPFC_TRANSGRESSION_LOW_RXPOWER)
+ flags |= RDP_OET_LOW_ALARM;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_HIGH_RXPOWER)
+ flags |= RDP_OET_HIGH_WARNING;
+ if (phba->sfp_warning & LPFC_TRANSGRESSION_LOW_RXPOWER)
+ flags |= RDP_OET_LOW_WARNING;
+
+ flags |= ((0xf & RDP_OED_RXPOWER) << RDP_OED_TYPE_SHIFT);
+ desc->oed_info.function_flags = cpu_to_be32(flags);
+ desc->length = cpu_to_be32(sizeof(desc->oed_info));
+ return sizeof(struct fc_rdp_oed_sfp_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_opd_desc(struct fc_rdp_opd_sfp_desc *desc,
+ uint8_t *page_a0, struct lpfc_vport *vport)
+{
+ desc->tag = cpu_to_be32(RDP_OPD_DESC_TAG);
+ memcpy(desc->opd_info.vendor_name, &page_a0[SSF_VENDOR_NAME], 16);
+ memcpy(desc->opd_info.model_number, &page_a0[SSF_VENDOR_PN], 16);
+ memcpy(desc->opd_info.serial_number, &page_a0[SSF_VENDOR_SN], 16);
+ memcpy(desc->opd_info.revision, &page_a0[SSF_VENDOR_REV], 4);
+ memcpy(desc->opd_info.date, &page_a0[SSF_DATE_CODE], 8);
+ desc->length = cpu_to_be32(sizeof(desc->opd_info));
+ return sizeof(struct fc_rdp_opd_sfp_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_fec_desc(struct fc_fec_rdp_desc *desc, READ_LNK_VAR *stat)
+{
+ if (bf_get(lpfc_read_link_stat_gec2, stat) == 0)
+ return 0;
+ desc->tag = cpu_to_be32(RDP_FEC_DESC_TAG);
+
+ desc->info.CorrectedBlocks =
+ cpu_to_be32(stat->fecCorrBlkCount);
+ desc->info.UncorrectableBlocks =
+ cpu_to_be32(stat->fecUncorrBlkCount);
+
+ desc->length = cpu_to_be32(sizeof(desc->info));
+
+ return sizeof(struct fc_fec_rdp_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
+{
+ uint16_t rdp_cap = 0;
+ uint16_t rdp_speed;
+
+ desc->tag = cpu_to_be32(RDP_PORT_SPEED_DESC_TAG);
+
+ switch (phba->fc_linkspeed) {
+ case LPFC_LINK_SPEED_1GHZ:
+ rdp_speed = RDP_PS_1GB;
+ break;
+ case LPFC_LINK_SPEED_2GHZ:
+ rdp_speed = RDP_PS_2GB;
+ break;
+ case LPFC_LINK_SPEED_4GHZ:
+ rdp_speed = RDP_PS_4GB;
+ break;
+ case LPFC_LINK_SPEED_8GHZ:
+ rdp_speed = RDP_PS_8GB;
+ break;
+ case LPFC_LINK_SPEED_10GHZ:
+ rdp_speed = RDP_PS_10GB;
+ break;
+ case LPFC_LINK_SPEED_16GHZ:
+ rdp_speed = RDP_PS_16GB;
+ break;
+ case LPFC_LINK_SPEED_32GHZ:
+ rdp_speed = RDP_PS_32GB;
+ break;
+ case LPFC_LINK_SPEED_64GHZ:
+ rdp_speed = RDP_PS_64GB;
+ break;
+ case LPFC_LINK_SPEED_128GHZ:
+ rdp_speed = RDP_PS_128GB;
+ break;
+ case LPFC_LINK_SPEED_256GHZ:
+ rdp_speed = RDP_PS_256GB;
+ break;
+ default:
+ rdp_speed = RDP_PS_UNKNOWN;
+ break;
}
- return sentplogi;
+
+ desc->info.port_speed.speed = cpu_to_be16(rdp_speed);
+
+ if (phba->lmt & LMT_256Gb)
+ rdp_cap |= RDP_PS_256GB;
+ if (phba->lmt & LMT_128Gb)
+ rdp_cap |= RDP_PS_128GB;
+ if (phba->lmt & LMT_64Gb)
+ rdp_cap |= RDP_PS_64GB;
+ if (phba->lmt & LMT_32Gb)
+ rdp_cap |= RDP_PS_32GB;
+ if (phba->lmt & LMT_16Gb)
+ rdp_cap |= RDP_PS_16GB;
+ if (phba->lmt & LMT_10Gb)
+ rdp_cap |= RDP_PS_10GB;
+ if (phba->lmt & LMT_8Gb)
+ rdp_cap |= RDP_PS_8GB;
+ if (phba->lmt & LMT_4Gb)
+ rdp_cap |= RDP_PS_4GB;
+ if (phba->lmt & LMT_2Gb)
+ rdp_cap |= RDP_PS_2GB;
+ if (phba->lmt & LMT_1Gb)
+ rdp_cap |= RDP_PS_1GB;
+
+ if (rdp_cap == 0)
+ rdp_cap = RDP_CAP_UNKNOWN;
+ if (phba->cfg_link_speed != LPFC_USER_LINK_SPEED_AUTO)
+ rdp_cap |= RDP_CAP_USER_CONFIGURED;
+
+ desc->info.port_speed.capabilities = cpu_to_be16(rdp_cap);
+ desc->length = cpu_to_be32(sizeof(desc->info));
+ return sizeof(struct fc_rdp_port_speed_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc,
+ struct lpfc_vport *vport)
+{
+
+ desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+
+ memcpy(desc->port_names.wwnn, &vport->fc_nodename,
+ sizeof(desc->port_names.wwnn));
+
+ memcpy(desc->port_names.wwpn, &vport->fc_portname,
+ sizeof(desc->port_names.wwpn));
+
+ desc->length = cpu_to_be32(sizeof(desc->port_names));
+ return sizeof(struct fc_rdp_port_name_desc);
+}
+
+static uint32_t
+lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc,
+ struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+
+ desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+ if (test_bit(FC_FABRIC, &vport->fc_flag)) {
+ memcpy(desc->port_names.wwnn, &vport->fabric_nodename,
+ sizeof(desc->port_names.wwnn));
+
+ memcpy(desc->port_names.wwpn, &vport->fabric_portname,
+ sizeof(desc->port_names.wwpn));
+ } else { /* Point to Point */
+ memcpy(desc->port_names.wwnn, &ndlp->nlp_nodename,
+ sizeof(desc->port_names.wwnn));
+
+ memcpy(desc->port_names.wwpn, &ndlp->nlp_portname,
+ sizeof(desc->port_names.wwpn));
+ }
+
+ desc->length = cpu_to_be32(sizeof(desc->port_names));
+ return sizeof(struct fc_rdp_port_name_desc);
}
+static void
+lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context,
+ int status)
+{
+ struct lpfc_nodelist *ndlp = rdp_context->ndlp;
+ struct lpfc_vport *vport = ndlp->vport;
+ struct lpfc_iocbq *elsiocb;
+ struct ulp_bde64 *bpl;
+ IOCB_t *icmd;
+ union lpfc_wqe128 *wqe;
+ uint8_t *pcmd;
+ struct ls_rjt *stat;
+ struct fc_rdp_res_frame *rdp_res;
+ uint32_t cmdsize, len;
+ uint16_t *flag_ptr;
+ int rc;
+ u32 ulp_context;
+
+ if (status != SUCCESS)
+ goto error;
+
+ /* This will change once we know the true size of the RDP payload */
+ cmdsize = sizeof(struct fc_rdp_res_frame);
+
+ elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize,
+ lpfc_max_els_tries, rdp_context->ndlp,
+ rdp_context->ndlp->nlp_DID, ELS_CMD_ACC);
+ if (!elsiocb)
+ goto free_rdp_context;
+
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* ox-id of the frame */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ rdp_context->ox_id);
+ bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
+ rdp_context->rx_id);
+ } else {
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = rdp_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2171 Xmit RDP response tag x%x xri x%x, "
+ "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x",
+ elsiocb->iotag, ulp_context,
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+ ndlp->nlp_rpi);
+ rdp_res = (struct fc_rdp_res_frame *)elsiocb->cmd_dmabuf->virt;
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
+ memset(pcmd, 0, sizeof(struct fc_rdp_res_frame));
+ *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+
+ /* Update Alarm and Warning */
+ flag_ptr = (uint16_t *)(rdp_context->page_a2 + SSF_ALARM_FLAGS);
+ phba->sfp_alarm |= *flag_ptr;
+ flag_ptr = (uint16_t *)(rdp_context->page_a2 + SSF_WARNING_FLAGS);
+ phba->sfp_warning |= *flag_ptr;
+
+ /* For RDP payload */
+ len = 8;
+ len += lpfc_rdp_res_link_service((struct fc_rdp_link_service_desc *)
+ (len + pcmd), ELS_CMD_RDP);
+
+ len += lpfc_rdp_res_sfp_desc((struct fc_rdp_sfp_desc *)(len + pcmd),
+ rdp_context->page_a0, rdp_context->page_a2);
+ len += lpfc_rdp_res_speed((struct fc_rdp_port_speed_desc *)(len + pcmd),
+ phba);
+ len += lpfc_rdp_res_link_error((struct fc_rdp_link_error_status_desc *)
+ (len + pcmd), &rdp_context->link_stat);
+ len += lpfc_rdp_res_diag_port_names((struct fc_rdp_port_name_desc *)
+ (len + pcmd), vport);
+ len += lpfc_rdp_res_attach_port_names((struct fc_rdp_port_name_desc *)
+ (len + pcmd), vport, ndlp);
+ len += lpfc_rdp_res_fec_desc((struct fc_fec_rdp_desc *)(len + pcmd),
+ &rdp_context->link_stat);
+ len += lpfc_rdp_res_bbc_desc((struct fc_rdp_bbc_desc *)(len + pcmd),
+ &rdp_context->link_stat, vport);
+ len += lpfc_rdp_res_oed_temp_desc(phba,
+ (struct fc_rdp_oed_sfp_desc *)(len + pcmd),
+ rdp_context->page_a2);
+ len += lpfc_rdp_res_oed_voltage_desc(phba,
+ (struct fc_rdp_oed_sfp_desc *)(len + pcmd),
+ rdp_context->page_a2);
+ len += lpfc_rdp_res_oed_txbias_desc(phba,
+ (struct fc_rdp_oed_sfp_desc *)(len + pcmd),
+ rdp_context->page_a2);
+ len += lpfc_rdp_res_oed_txpower_desc(phba,
+ (struct fc_rdp_oed_sfp_desc *)(len + pcmd),
+ rdp_context->page_a2);
+ len += lpfc_rdp_res_oed_rxpower_desc(phba,
+ (struct fc_rdp_oed_sfp_desc *)(len + pcmd),
+ rdp_context->page_a2);
+ len += lpfc_rdp_res_opd_desc((struct fc_rdp_opd_sfp_desc *)(len + pcmd),
+ rdp_context->page_a0, vport);
+
+ rdp_res->length = cpu_to_be32(len - 8);
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+
+ /* Now that we know the true size of the payload, update the BPL */
+ bpl = (struct ulp_bde64 *)elsiocb->bpl_dmabuf->virt;
+ bpl->tus.f.bdeSize = len;
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ phba->fc_stat.elsXmitACC++;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ goto free_rdp_context;
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ }
+
+ goto free_rdp_context;
+
+error:
+ cmdsize = 2 * sizeof(uint32_t);
+ elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, lpfc_max_els_tries,
+ ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT);
+ if (!elsiocb)
+ goto free_rdp_context;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* ox-id of the frame */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ rdp_context->ox_id);
+ bf_set(wqe_ctxt_tag,
+ &wqe->xmit_els_rsp.wqe_com,
+ rdp_context->rx_id);
+ } else {
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = rdp_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+ }
+
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
+ stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
+ stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+
+ phba->fc_stat.elsXmitLSRJT++;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ goto free_rdp_context;
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ }
+
+free_rdp_context:
+ /* This reference put is for the original unsolicited RDP. If the
+ * prep failed, there is no reference to remove.
+ */
+ lpfc_nlp_put(ndlp);
+ kfree(rdp_context);
+}
+
+static int
+lpfc_get_rdp_info(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context)
+{
+ LPFC_MBOXQ_t *mbox = NULL;
+ int rc;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS,
+ "7105 failed to allocate mailbox memory");
+ return 1;
+ }
+
+ if (lpfc_sli4_dump_page_a0(phba, mbox))
+ goto rdp_fail;
+ mbox->vport = rdp_context->ndlp->vport;
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a0;
+ mbox->ctx_u.rdp = rdp_context;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ return 1;
+ }
+
+ return 0;
+
+rdp_fail:
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+}
+
+int lpfc_get_sfp_info_wait(struct lpfc_hba *phba,
+ struct lpfc_rdp_context *rdp_context)
+{
+ LPFC_MBOXQ_t *mbox = NULL;
+ int rc;
+ struct lpfc_dmabuf *mp;
+ struct lpfc_dmabuf *mpsave;
+ void *virt;
+ MAILBOX_t *mb;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS,
+ "7205 failed to allocate mailbox memory");
+ return 1;
+ }
+
+ if (lpfc_sli4_dump_page_a0(phba, mbox))
+ goto sfp_fail;
+ mp = mbox->ctx_buf;
+ mpsave = mp;
+ virt = mp->virt;
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ mb = &mbox->u.mb;
+ mb->un.varDmp.cv = 1;
+ mb->un.varDmp.co = 1;
+ mb->un.varWords[2] = 0;
+ mb->un.varWords[3] = DMP_SFF_PAGE_A0_SIZE / 4;
+ mb->un.varWords[4] = 0;
+ mb->un.varWords[5] = 0;
+ mb->un.varWords[6] = 0;
+ mb->un.varWords[7] = 0;
+ mb->un.varWords[8] = 0;
+ mb->un.varWords[9] = 0;
+ mb->un.varWords[10] = 0;
+ mbox->in_ext_byte_len = DMP_SFF_PAGE_A0_SIZE;
+ mbox->out_ext_byte_len = DMP_SFF_PAGE_A0_SIZE;
+ mbox->mbox_offset_word = 5;
+ mbox->ext_buf = virt;
+ } else {
+ bf_set(lpfc_mbx_memory_dump_type3_length,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE);
+ mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+ mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+ }
+ mbox->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_SLI4_CONFIG_TMO);
+ if (rc == MBX_NOT_FINISHED) {
+ rc = 1;
+ goto error;
+ }
+ if (rc == MBX_TIMEOUT)
+ goto error;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ mp = mbox->ctx_buf;
+ else
+ mp = mpsave;
+
+ if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) {
+ rc = 1;
+ goto error;
+ }
+
+ lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0,
+ DMP_SFF_PAGE_A0_SIZE);
+
+ memset(mbox, 0, sizeof(*mbox));
+ memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE);
+ INIT_LIST_HEAD(&mp->list);
+
+ /* save address for completion */
+ mbox->ctx_buf = mp;
+ mbox->vport = phba->pport;
+
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+ bf_set(lpfc_mbx_memory_dump_type3_type,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+ bf_set(lpfc_mbx_memory_dump_type3_link,
+ &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+ bf_set(lpfc_mbx_memory_dump_type3_page_no,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2);
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ mb = &mbox->u.mb;
+ mb->un.varDmp.cv = 1;
+ mb->un.varDmp.co = 1;
+ mb->un.varWords[2] = 0;
+ mb->un.varWords[3] = DMP_SFF_PAGE_A2_SIZE / 4;
+ mb->un.varWords[4] = 0;
+ mb->un.varWords[5] = 0;
+ mb->un.varWords[6] = 0;
+ mb->un.varWords[7] = 0;
+ mb->un.varWords[8] = 0;
+ mb->un.varWords[9] = 0;
+ mb->un.varWords[10] = 0;
+ mbox->in_ext_byte_len = DMP_SFF_PAGE_A2_SIZE;
+ mbox->out_ext_byte_len = DMP_SFF_PAGE_A2_SIZE;
+ mbox->mbox_offset_word = 5;
+ mbox->ext_buf = virt;
+ } else {
+ bf_set(lpfc_mbx_memory_dump_type3_length,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE);
+ mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+ mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+ }
+
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_SLI4_CONFIG_TMO);
+
+ if (rc == MBX_TIMEOUT)
+ goto error;
+ if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) {
+ rc = 1;
+ goto error;
+ }
+ rc = 0;
+
+ lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2,
+ DMP_SFF_PAGE_A2_SIZE);
+
+error:
+ if (mbox->mbox_flag & LPFC_MBX_WAKE) {
+ mbox->ctx_buf = mpsave;
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ }
+
+ return rc;
+
+sfp_fail:
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+}
+
+/*
+ * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes an unsolicited RDP(Read Diagnostic Parameters)
+ * IOCB. First, the payload of the unsolicited RDP is checked.
+ * Then it will (1) send MBX_DUMP_MEMORY, Embedded DMP_LMSD sub command TYPE-3
+ * for Page A0, (2) send MBX_DUMP_MEMORY, DMP_LMSD for Page A2,
+ * (3) send MBX_READ_LNK_STAT to get link stat, (4) Call lpfc_els_rdp_cmpl
+ * gather all data and send RDP response.
+ *
+ * Return code
+ * 0 - Sent the acc response
+ * 1 - Sent the reject response.
+ */
+static int
+lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_dmabuf *pcmd;
+ uint8_t rjt_err, rjt_expl = LSEXP_NOTHING_MORE;
+ struct fc_rdp_req_frame *rdp_req;
+ struct lpfc_rdp_context *rdp_context;
+ union lpfc_wqe128 *cmd = NULL;
+ struct ls_rjt stat;
+
+ if (phba->sli_rev < LPFC_SLI_REV4 ||
+ bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
+ LPFC_SLI_INTF_IF_TYPE_2) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ rjt_expl = LSEXP_REQ_UNSUPPORTED;
+ goto error;
+ }
+
+ if (phba->sli_rev < LPFC_SLI_REV4 ||
+ test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ rjt_expl = LSEXP_REQ_UNSUPPORTED;
+ goto error;
+ }
+
+ pcmd = cmdiocb->cmd_dmabuf;
+ rdp_req = (struct fc_rdp_req_frame *) pcmd->virt;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2422 ELS RDP Request "
+ "dec len %d tag x%x port_id %d len %d\n",
+ be32_to_cpu(rdp_req->rdp_des_length),
+ be32_to_cpu(rdp_req->nport_id_desc.tag),
+ be32_to_cpu(rdp_req->nport_id_desc.nport_id),
+ be32_to_cpu(rdp_req->nport_id_desc.length));
+
+ if (sizeof(struct fc_rdp_nport_desc) !=
+ be32_to_cpu(rdp_req->rdp_des_length))
+ goto rjt_logerr;
+ if (RDP_N_PORT_DESC_TAG != be32_to_cpu(rdp_req->nport_id_desc.tag))
+ goto rjt_logerr;
+ if (RDP_NPORT_ID_SIZE !=
+ be32_to_cpu(rdp_req->nport_id_desc.length))
+ goto rjt_logerr;
+ rdp_context = kzalloc(sizeof(struct lpfc_rdp_context), GFP_KERNEL);
+ if (!rdp_context) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ goto error;
+ }
+
+ cmd = &cmdiocb->wqe;
+ rdp_context->ndlp = lpfc_nlp_get(ndlp);
+ if (!rdp_context->ndlp) {
+ kfree(rdp_context);
+ rjt_err = LSRJT_UNABLE_TPC;
+ goto error;
+ }
+ rdp_context->ox_id = bf_get(wqe_rcvoxid,
+ &cmd->xmit_els_rsp.wqe_com);
+ rdp_context->rx_id = bf_get(wqe_ctxt_tag,
+ &cmd->xmit_els_rsp.wqe_com);
+ rdp_context->cmpl = lpfc_els_rdp_cmpl;
+ if (lpfc_get_rdp_info(phba, rdp_context)) {
+ lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_ELS,
+ "2423 Unable to send mailbox");
+ kfree(rdp_context);
+ rjt_err = LSRJT_UNABLE_TPC;
+ lpfc_nlp_put(ndlp);
+ goto error;
+ }
+
+ return 0;
+
+rjt_logerr:
+ rjt_err = LSRJT_LOGICAL_ERR;
+
+error:
+ memset(&stat, 0, sizeof(stat));
+ stat.un.b.lsRjtRsnCode = rjt_err;
+ stat.un.b.lsRjtRsnCodeExp = rjt_expl;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return 1;
+}
+
+
+static void
+lpfc_els_lcb_rsp(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ MAILBOX_t *mb;
+ IOCB_t *icmd;
+ union lpfc_wqe128 *wqe;
+ uint8_t *pcmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_nodelist *ndlp;
+ struct ls_rjt *stat;
+ union lpfc_sli4_cfg_shdr *shdr;
+ struct lpfc_lcb_context *lcb_context;
+ struct fc_lcb_res_frame *lcb_res;
+ uint32_t cmdsize, shdr_status, shdr_add_status;
+ int rc;
+
+ mb = &pmb->u.mb;
+ lcb_context = pmb->ctx_u.lcb;
+ ndlp = lcb_context->ndlp;
+ memset(&pmb->ctx_u, 0, sizeof(pmb->ctx_u));
+ pmb->ctx_buf = NULL;
+
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &pmb->u.mqe.un.beacon_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX,
+ "0194 SET_BEACON_CONFIG mailbox "
+ "completed with status x%x add_status x%x,"
+ " mbx status x%x\n",
+ shdr_status, shdr_add_status, mb->mbxStatus);
+
+ if ((mb->mbxStatus != MBX_SUCCESS) || shdr_status ||
+ (shdr_add_status == ADD_STATUS_OPERATION_ALREADY_ACTIVE) ||
+ (shdr_add_status == ADD_STATUS_INVALID_REQUEST)) {
+ mempool_free(pmb, phba->mbox_mem_pool);
+ goto error;
+ }
+
+ mempool_free(pmb, phba->mbox_mem_pool);
+ cmdsize = sizeof(struct fc_lcb_res_frame);
+ elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
+ lpfc_max_els_tries, ndlp,
+ ndlp->nlp_DID, ELS_CMD_ACC);
+
+ /* Decrement the ndlp reference count from previous mbox command */
+ lpfc_nlp_put(ndlp);
+
+ if (!elsiocb)
+ goto free_lcb_context;
+
+ lcb_res = (struct fc_lcb_res_frame *)elsiocb->cmd_dmabuf->virt;
+
+ memset(lcb_res, 0, sizeof(struct fc_lcb_res_frame));
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com, lcb_context->rx_id);
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ lcb_context->ox_id);
+ } else {
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = lcb_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id;
+ }
+
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
+ *((uint32_t *)(pcmd)) = ELS_CMD_ACC;
+ lcb_res->lcb_sub_command = lcb_context->sub_command;
+ lcb_res->lcb_type = lcb_context->type;
+ lcb_res->capability = lcb_context->capability;
+ lcb_res->lcb_frequency = lcb_context->frequency;
+ lcb_res->lcb_duration = lcb_context->duration;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ phba->fc_stat.elsXmitACC++;
+
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ goto out;
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ }
+ out:
+ kfree(lcb_context);
+ return;
+
+error:
+ cmdsize = sizeof(struct fc_lcb_res_frame);
+ elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
+ lpfc_max_els_tries, ndlp,
+ ndlp->nlp_DID, ELS_CMD_LS_RJT);
+ lpfc_nlp_put(ndlp);
+ if (!elsiocb)
+ goto free_lcb_context;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com, lcb_context->rx_id);
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ lcb_context->ox_id);
+ } else {
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = lcb_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id;
+ }
+
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
+
+ *((uint32_t *)(pcmd)) = ELS_CMD_LS_RJT;
+ stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
+ stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+
+ if (shdr_add_status == ADD_STATUS_OPERATION_ALREADY_ACTIVE)
+ stat->un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS;
+
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
+ phba->fc_stat.elsXmitLSRJT++;
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ goto free_lcb_context;
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ }
+free_lcb_context:
+ kfree(lcb_context);
+}
+
+static int
+lpfc_sli4_set_beacon(struct lpfc_vport *vport,
+ struct lpfc_lcb_context *lcb_context,
+ uint32_t beacon_state)
+{
+ struct lpfc_hba *phba = vport->phba;
+ union lpfc_sli4_cfg_shdr *cfg_shdr;
+ LPFC_MBOXQ_t *mbox = NULL;
+ uint32_t len;
+ int rc;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return 1;
+
+ cfg_shdr = &mbox->u.mqe.un.sli4_config.header.cfg_shdr;
+ len = sizeof(struct lpfc_mbx_set_beacon_config) -
+ sizeof(struct lpfc_sli4_cfg_mhdr);
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_SET_BEACON_CONFIG, len,
+ LPFC_SLI4_MBX_EMBED);
+ mbox->ctx_u.lcb = lcb_context;
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_els_lcb_rsp;
+ bf_set(lpfc_mbx_set_beacon_port_num, &mbox->u.mqe.un.beacon_config,
+ phba->sli4_hba.physical_port);
+ bf_set(lpfc_mbx_set_beacon_state, &mbox->u.mqe.un.beacon_config,
+ beacon_state);
+ mbox->u.mqe.un.beacon_config.word5 = 0; /* Reserved */
+
+ /*
+ * Check bv1s bit before issuing the mailbox
+ * if bv1s == 1, LCB V1 supported
+ * else, LCB V0 supported
+ */
+
+ if (phba->sli4_hba.pc_sli4_params.bv1s) {
+ /* COMMON_SET_BEACON_CONFIG_V1 */
+ cfg_shdr->request.word9 = BEACON_VERSION_V1;
+ lcb_context->capability |= LCB_CAPABILITY_DURATION;
+ bf_set(lpfc_mbx_set_beacon_port_type,
+ &mbox->u.mqe.un.beacon_config, 0);
+ bf_set(lpfc_mbx_set_beacon_duration_v1,
+ &mbox->u.mqe.un.beacon_config,
+ be16_to_cpu(lcb_context->duration));
+ } else {
+ /* COMMON_SET_BEACON_CONFIG_V0 */
+ if (be16_to_cpu(lcb_context->duration) != 0) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+ }
+ cfg_shdr->request.word9 = BEACON_VERSION_V0;
+ lcb_context->capability &= ~(LCB_CAPABILITY_DURATION);
+ bf_set(lpfc_mbx_set_beacon_state,
+ &mbox->u.mqe.un.beacon_config, beacon_state);
+ bf_set(lpfc_mbx_set_beacon_port_type,
+ &mbox->u.mqe.un.beacon_config, 1);
+ bf_set(lpfc_mbx_set_beacon_duration,
+ &mbox->u.mqe.un.beacon_config,
+ be16_to_cpu(lcb_context->duration));
+ }
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * lpfc_els_rcv_lcb - Process an unsolicited LCB
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes an unsolicited LCB(LINK CABLE BEACON) IOCB.
+ * First, the payload of the unsolicited LCB is checked.
+ * Then based on Subcommand beacon will either turn on or off.
+ *
+ * Return code
+ * 0 - Sent the acc response
+ * 1 - Sent the reject response.
+ **/
+static int
+lpfc_els_rcv_lcb(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_dmabuf *pcmd;
+ uint8_t *lp;
+ struct fc_lcb_request_frame *beacon;
+ struct lpfc_lcb_context *lcb_context;
+ u8 state, rjt_err = 0;
+ struct ls_rjt stat;
+
+ pcmd = cmdiocb->cmd_dmabuf;
+ lp = (uint8_t *)pcmd->virt;
+ beacon = (struct fc_lcb_request_frame *)pcmd->virt;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "0192 ELS LCB Data x%x x%x x%x x%x sub x%x "
+ "type x%x frequency %x duration x%x\n",
+ lp[0], lp[1], lp[2],
+ beacon->lcb_command,
+ beacon->lcb_sub_command,
+ beacon->lcb_type,
+ beacon->lcb_frequency,
+ be16_to_cpu(beacon->lcb_duration));
+
+ if (beacon->lcb_sub_command != LPFC_LCB_ON &&
+ beacon->lcb_sub_command != LPFC_LCB_OFF) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+
+ if (phba->sli_rev < LPFC_SLI_REV4 ||
+ test_bit(HBA_FCOE_MODE, &phba->hba_flag) ||
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
+ LPFC_SLI_INTF_IF_TYPE_2)) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+
+ lcb_context = kmalloc(sizeof(*lcb_context), GFP_KERNEL);
+ if (!lcb_context) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ goto rjt;
+ }
+
+ state = (beacon->lcb_sub_command == LPFC_LCB_ON) ? 1 : 0;
+ lcb_context->sub_command = beacon->lcb_sub_command;
+ lcb_context->capability = 0;
+ lcb_context->type = beacon->lcb_type;
+ lcb_context->frequency = beacon->lcb_frequency;
+ lcb_context->duration = beacon->lcb_duration;
+ lcb_context->ox_id = get_job_rcvoxid(phba, cmdiocb);
+ lcb_context->rx_id = get_job_ulpcontext(phba, cmdiocb);
+ lcb_context->ndlp = lpfc_nlp_get(ndlp);
+ if (!lcb_context->ndlp) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ goto rjt_free;
+ }
+
+ if (lpfc_sli4_set_beacon(vport, lcb_context, state)) {
+ lpfc_printf_vlog(ndlp->vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0193 failed to send mail box");
+ lpfc_nlp_put(ndlp);
+ rjt_err = LSRJT_UNABLE_TPC;
+ goto rjt_free;
+ }
+ return 0;
+
+rjt_free:
+ kfree(lcb_context);
+rjt:
+ memset(&stat, 0, sizeof(stat));
+ stat.un.b.lsRjtRsnCode = rjt_err;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return 1;
+}
+
+
/**
* lpfc_els_flush_rscn - Clean up any rscn activities with a vport
* @vport: pointer to a host virtual N_Port data structure.
@@ -4338,9 +7854,10 @@ lpfc_els_flush_rscn(struct lpfc_vport *vport)
lpfc_in_buf_free(phba, vport->fc_rscn_id_list[i]);
vport->fc_rscn_id_list[i] = NULL;
}
+ clear_bit(FC_RSCN_MODE, &vport->fc_flag);
+ clear_bit(FC_RSCN_DISCOVERY, &vport->fc_flag);
spin_lock_irq(shost->host_lock);
vport->fc_rscn_id_cnt = 0;
- vport->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY);
spin_unlock_irq(shost->host_lock);
lpfc_can_disctmo(vport);
/* Indicate we are done walking this fc_rscn_id_list */
@@ -4375,7 +7892,7 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
return 0;
/* If we are doing a FULL RSCN rediscovery, match everything */
- if (vport->fc_flag & FC_RSCN_DISCOVERY)
+ if (test_bit(FC_RSCN_DISCOVERY, &vport->fc_flag))
return did;
spin_lock_irq(shost->host_lock);
@@ -4438,14 +7955,37 @@ return_did_out:
static int
lpfc_rscn_recovery_check(struct lpfc_vport *vport)
{
- struct lpfc_nodelist *ndlp = NULL;
+ struct lpfc_nodelist *ndlp = NULL, *n;
/* Move all affected nodes by pending RSCNs to NPR state. */
- list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp) ||
- (ndlp->nlp_state == NLP_STE_UNUSED_NODE) ||
+ list_for_each_entry_safe(ndlp, n, &vport->fc_nodes, nlp_listp) {
+ if (test_bit(FC_UNLOADING, &vport->load_flag)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "1000 %s Unloading set\n",
+ __func__);
+ return 0;
+ }
+
+ if ((ndlp->nlp_state == NLP_STE_UNUSED_NODE) ||
!lpfc_rscn_payload_check(vport, ndlp->nlp_DID))
continue;
+
+ /* NVME Target mode does not do RSCN Recovery. */
+ if (vport->phba->nvmet_support)
+ continue;
+
+ /* If we are in the process of doing discovery on this
+ * NPort, let it continue on its own.
+ */
+ switch (ndlp->nlp_state) {
+ case NLP_STE_PLOGI_ISSUE:
+ case NLP_STE_ADISC_ISSUE:
+ case NLP_STE_REG_LOGIN_ISSUE:
+ case NLP_STE_PRLI_ISSUE:
+ case NLP_STE_LOGO_ISSUE:
+ continue;
+ }
+
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
lpfc_cancel_retry_delay_tmo(vport, ndlp);
@@ -4471,14 +8011,14 @@ lpfc_send_rscn_event(struct lpfc_vport *vport,
uint32_t payload_len;
struct lpfc_rscn_event_header *rscn_event_data;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
payload_ptr = (uint32_t *) pcmd->virt;
payload_len = be32_to_cpu(*payload_ptr & ~ELS_CMD_MASK);
rscn_event_data = kmalloc(sizeof(struct lpfc_rscn_event_header) +
payload_len, GFP_KERNEL);
if (!rscn_event_data) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0147 Failed to allocate memory for RSCN event\n");
return;
}
@@ -4489,7 +8029,7 @@ lpfc_send_rscn_event(struct lpfc_vport *vport,
fc_host_post_vendor_event(shost,
fc_get_event_number(),
- sizeof(struct lpfc_els_event_header) + payload_len,
+ sizeof(struct lpfc_rscn_event_header) + payload_len,
(char *)rscn_event_data,
LPFC_NL_VENDOR_ID);
@@ -4526,21 +8066,19 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
struct lpfc_hba *phba = vport->phba;
struct lpfc_dmabuf *pcmd;
uint32_t *lp, *datap;
- IOCB_t *icmd;
uint32_t payload_len, length, nportid, *cmd;
int rscn_cnt;
int rscn_id = 0, hba_id = 0;
- int i;
+ int i, tmo;
- icmd = &cmdiocb->iocb;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
lp = (uint32_t *) pcmd->virt;
payload_len = be32_to_cpu(*lp++ & ~ELS_CMD_MASK);
payload_len -= sizeof(uint32_t); /* take off word 0 */
/* RSCN received */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0214 RSCN received Data: x%x x%x x%x x%x\n",
+ "0214 RSCN received Data: x%lx x%x x%x x%x\n",
vport->fc_flag, payload_len, *lp,
vport->fc_rscn_id_cnt);
@@ -4551,12 +8089,29 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
fc_host_post_event(shost, fc_get_event_number(),
FCH_EVT_RSCN, lp[i]);
+ /* Check if RSCN is coming from a direct-connected remote NPort */
+ if (test_bit(FC_PT2PT, &vport->fc_flag)) {
+ /* If so, just ACC it, no other action needed for now */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2024 pt2pt RSCN %08x Data: x%lx x%x\n",
+ *lp, vport->fc_flag, payload_len);
+ lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+
+ /* Check to see if we need to NVME rescan this target
+ * remoteport.
+ */
+ if (ndlp->nlp_fc4_type & NLP_FC4_NVME &&
+ ndlp->nlp_type & (NLP_NVME_TARGET | NLP_NVME_DISCOVERY))
+ lpfc_nvme_rescan_port(vport, ndlp);
+ return 0;
+ }
+
/* If we are about to begin discovery, just ACC the RSCN.
* Discovery processing will satisfy it.
*/
if (vport->port_state <= LPFC_NS_QRY) {
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RSCN ignore: did:x%x/ste:x%x flg:x%x",
+ "RCV RSCN ignore: did:x%x/ste:x%x flg:x%lx",
ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag);
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
@@ -4582,16 +8137,22 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
/* ALL NPortIDs in RSCN are on HBA */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0219 Ignore RSCN "
- "Data: x%x x%x x%x x%x\n",
+ "Data: x%lx x%x x%x x%x\n",
vport->fc_flag, payload_len,
*lp, vport->fc_rscn_id_cnt);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RSCN vport: did:x%x/ste:x%x flg:x%x",
+ "RCV RSCN vport: did:x%x/ste:x%x flg:x%lx",
ndlp->nlp_DID, vport->port_state,
ndlp->nlp_flag);
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb,
ndlp, NULL);
+ /* Restart disctmo if its already running */
+ if (test_bit(FC_DISC_TMO, &vport->fc_flag)) {
+ tmo = ((phba->fc_ratov * 3) + 3);
+ mod_timer(&vport->fc_disctmo,
+ jiffies + secs_to_jiffies(tmo));
+ }
return 0;
}
}
@@ -4599,8 +8160,8 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
spin_lock_irq(shost->host_lock);
if (vport->fc_rscn_flush) {
/* Another thread is walking fc_rscn_id_list on this vport */
- vport->fc_flag |= FC_RSCN_DISCOVERY;
spin_unlock_irq(shost->host_lock);
+ set_bit(FC_RSCN_DISCOVERY, &vport->fc_flag);
/* Send back ACC */
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
return 0;
@@ -4611,19 +8172,25 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
/* Get the array count after successfully have the token */
rscn_cnt = vport->fc_rscn_id_cnt;
/* If we are already processing an RSCN, save the received
- * RSCN payload buffer, cmdiocb->context2 to process later.
+ * RSCN payload buffer, cmdiocb->cmd_dmabuf to process later.
*/
- if (vport->fc_flag & (FC_RSCN_MODE | FC_NDISC_ACTIVE)) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag) ||
+ test_bit(FC_NDISC_ACTIVE, &vport->fc_flag)) {
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RSCN defer: did:x%x/ste:x%x flg:x%x",
+ "RCV RSCN defer: did:x%x/ste:x%x flg:x%lx",
ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_RSCN_DEFERRED;
+ set_bit(FC_RSCN_DEFERRED, &vport->fc_flag);
+
+ /* Restart disctmo if its already running */
+ if (test_bit(FC_DISC_TMO, &vport->fc_flag)) {
+ tmo = ((phba->fc_ratov * 3) + 3);
+ mod_timer(&vport->fc_disctmo,
+ jiffies + secs_to_jiffies(tmo));
+ }
if ((rscn_cnt < FC_MAX_HOLD_RSCN) &&
- !(vport->fc_flag & FC_RSCN_DISCOVERY)) {
- vport->fc_flag |= FC_RSCN_MODE;
- spin_unlock_irq(shost->host_lock);
+ !test_bit(FC_RSCN_DISCOVERY, &vport->fc_flag)) {
+ set_bit(FC_RSCN_MODE, &vport->fc_flag);
if (rscn_cnt) {
cmd = vport->fc_rscn_id_list[rscn_cnt-1]->virt;
length = be32_to_cpu(*cmd & ~ELS_CMD_MASK);
@@ -4637,24 +8204,23 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
} else {
vport->fc_rscn_id_list[rscn_cnt] = pcmd;
vport->fc_rscn_id_cnt++;
- /* If we zero, cmdiocb->context2, the calling
+ /* If we zero, cmdiocb->cmd_dmabuf, the calling
* routine will not try to free it.
*/
- cmdiocb->context2 = NULL;
+ cmdiocb->cmd_dmabuf = NULL;
}
/* Deferred RSCN */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0235 Deferred RSCN "
- "Data: x%x x%x x%x\n",
+ "Data: x%x x%lx x%x\n",
vport->fc_rscn_id_cnt, vport->fc_flag,
vport->port_state);
} else {
- vport->fc_flag |= FC_RSCN_DISCOVERY;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_RSCN_DISCOVERY, &vport->fc_flag);
/* ReDiscovery RSCN */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0234 ReDiscovery RSCN "
- "Data: x%x x%x x%x\n",
+ "Data: x%x x%lx x%x\n",
vport->fc_rscn_id_cnt, vport->fc_flag,
vport->port_state);
}
@@ -4664,26 +8230,21 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
/* send RECOVERY event for ALL nodes that match RSCN payload */
lpfc_rscn_recovery_check(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_RSCN_DEFERRED;
- spin_unlock_irq(shost->host_lock);
return 0;
}
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RSCN: did:x%x/ste:x%x flg:x%x",
+ "RCV RSCN: did:x%x/ste:x%x flg:x%lx",
ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_RSCN_MODE;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_RSCN_MODE, &vport->fc_flag);
vport->fc_rscn_id_list[vport->fc_rscn_id_cnt++] = pcmd;
/* Indicate we are done walking fc_rscn_id_list on this vport */
vport->fc_rscn_flush = 0;
/*
- * If we zero, cmdiocb->context2, the calling routine will
+ * If we zero, cmdiocb->cmd_dmabuf, the calling routine will
* not try to free it.
*/
- cmdiocb->context2 = NULL;
+ cmdiocb->cmd_dmabuf = NULL;
lpfc_set_disctmo(vport);
/* Send back ACC */
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
@@ -4712,10 +8273,10 @@ int
lpfc_els_handle_rscn(struct lpfc_vport *vport)
{
struct lpfc_nodelist *ndlp;
- struct lpfc_hba *phba = vport->phba;
+ struct lpfc_hba *phba = vport->phba;
/* Ignore RSCN if the port is being torn down. */
- if (vport->load_flag & FC_UNLOADING) {
+ if (test_bit(FC_UNLOADING, &vport->load_flag)) {
lpfc_els_flush_rscn(vport);
return 0;
}
@@ -4725,46 +8286,42 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport)
/* RSCN processed */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0215 RSCN processed Data: x%x x%x x%x x%x\n",
+ "0215 RSCN processed Data: x%lx x%x x%x x%x x%x x%x\n",
vport->fc_flag, 0, vport->fc_rscn_id_cnt,
- vport->port_state);
+ vport->port_state, vport->num_disc_nodes,
+ vport->gidft_inp);
/* To process RSCN, first compare RSCN data with NameServer */
vport->fc_ns_retry = 0;
vport->num_disc_nodes = 0;
ndlp = lpfc_findnode_did(vport, NameServer_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)
- && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
- /* Good ndlp, issue CT Request to NameServer */
- if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, 0) == 0)
- /* Wait for NameServer query cmpl before we can
- continue */
+ if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
+ /* Good ndlp, issue CT Request to NameServer. Need to
+ * know how many gidfts were issued. If none, then just
+ * flush the RSCN. Otherwise, the outstanding requests
+ * need to complete.
+ */
+ if (phba->cfg_ns_query == LPFC_NS_QUERY_GID_FT) {
+ if (lpfc_issue_gidft(vport) > 0)
+ return 1;
+ } else if (phba->cfg_ns_query == LPFC_NS_QUERY_GID_PT) {
+ if (lpfc_issue_gidpt(vport) > 0)
+ return 1;
+ } else {
return 1;
+ }
} else {
- /* If login to NameServer does not exist, issue one */
- /* Good status, issue PLOGI to NameServer */
- ndlp = lpfc_findnode_did(vport, NameServer_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp))
- /* Wait for NameServer login cmpl before we can
- continue */
- return 1;
-
+ /* Nameserver login in question. Revalidate. */
if (ndlp) {
- ndlp = lpfc_enable_node(vport, ndlp,
- NLP_STE_PLOGI_ISSUE);
- if (!ndlp) {
- lpfc_els_flush_rscn(vport);
- return 0;
- }
ndlp->nlp_prev_state = NLP_STE_UNUSED_NODE;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
} else {
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, NameServer_DID);
if (!ndlp) {
lpfc_els_flush_rscn(vport);
return 0;
}
- lpfc_nlp_init(vport, ndlp, NameServer_DID);
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
}
@@ -4811,14 +8368,18 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ struct lpfc_dmabuf *pcmd = cmdiocb->cmd_dmabuf;
uint32_t *lp = (uint32_t *) pcmd->virt;
- IOCB_t *icmd = &cmdiocb->iocb;
+ union lpfc_wqe128 *wqe = &cmdiocb->wqe;
struct serv_parm *sp;
LPFC_MBOXQ_t *mbox;
- struct ls_rjt stat;
uint32_t cmd, did;
int rc;
+ unsigned long fc_flag = 0;
+ uint32_t port_state = 0;
+
+ /* Clear external loopback plug detected flag */
+ phba->link_flag &= ~LS_EXTERNAL_LOOPBACK;
cmd = *lp++;
sp = (struct serv_parm *) lp;
@@ -4829,32 +8390,33 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
/* We should never receive a FLOGI in loop mode, ignore it */
- did = icmd->un.elsreq64.remoteID;
+ did = bf_get(wqe_els_did, &wqe->xmit_els_rsp.wqe_dest);
/* An FLOGI ELS command <elsCmd> was received from DID <did> in
Loop Mode */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0113 An FLOGI ELS command x%x was "
"received from DID x%x in Loop Mode\n",
cmd, did);
return 1;
}
- did = Fabric_DID;
+ (void) lpfc_check_sparm(vport, ndlp, sp, CLASS3, 1);
- if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 1))) {
- /* For a FLOGI we accept, then if our portname is greater
- * then the remote portname we initiate Nport login.
- */
+ /*
+ * If our portname is greater than the remote portname,
+ * then we initiate Nport login.
+ */
- rc = memcmp(&vport->fc_portname, &sp->portName,
- sizeof(struct lpfc_name));
+ rc = memcmp(&vport->fc_portname, &sp->portName,
+ sizeof(struct lpfc_name));
- if (!rc) {
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!rc) {
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ mbox = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
if (!mbox)
return 1;
-
lpfc_linkdown(phba);
lpfc_init_link(phba, mbox,
phba->cfg_topology,
@@ -4862,34 +8424,102 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
mbox->vport = vport;
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ rc = lpfc_sli_issue_mbox(phba, mbox,
+ MBX_NOWAIT);
lpfc_set_loopback_flag(phba);
- if (rc == MBX_NOT_FINISHED) {
+ if (rc == MBX_NOT_FINISHED)
mempool_free(mbox, phba->mbox_mem_pool);
- }
return 1;
- } else if (rc > 0) { /* greater than */
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_PT2PT_PLOGI;
- spin_unlock_irq(shost->host_lock);
}
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_PT2PT;
- vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
- spin_unlock_irq(shost->host_lock);
+
+ /* External loopback plug insertion detected */
+ phba->link_flag |= LS_EXTERNAL_LOOPBACK;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_LIBDFC,
+ "1119 External Loopback plug detected\n");
+
+ /* abort the flogi coming back to ourselves
+ * due to external loopback on the port.
+ */
+ lpfc_els_abort_flogi(phba);
+ return 0;
+
+ } else if (rc > 0) { /* greater than */
+ set_bit(FC_PT2PT_PLOGI, &vport->fc_flag);
+
+ /* If we have the high WWPN we can assign our own
+ * myDID; otherwise, we have to WAIT for a PLOGI
+ * from the remote NPort to find out what it
+ * will be.
+ */
+ vport->fc_myDID = PT2PT_LocalID;
} else {
- /* Reject this request because invalid parameters */
- stat.un.b.lsRjtRsvd0 = 0;
- stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
- stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
- stat.un.b.vendorUnique = 0;
- lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
- NULL);
- return 1;
+ vport->fc_myDID = PT2PT_RemoteID;
+ }
+
+ /*
+ * The vport state should go to LPFC_FLOGI only
+ * AFTER we issue a FLOGI, not receive one.
+ */
+ spin_lock_irq(shost->host_lock);
+ fc_flag = vport->fc_flag;
+ port_state = vport->port_state;
+ /* Acking an unsol FLOGI. Count 1 for link bounce
+ * work-around.
+ */
+ vport->rcv_flogi_cnt++;
+ spin_unlock_irq(shost->host_lock);
+ set_bit(FC_PT2PT, &vport->fc_flag);
+ clear_bit(FC_FABRIC, &vport->fc_flag);
+ clear_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3311 Rcv Flogi PS x%x new PS x%x "
+ "fc_flag x%lx new fc_flag x%lx, hba_flag x%lx\n",
+ port_state, vport->port_state,
+ fc_flag, vport->fc_flag, phba->hba_flag);
+
+ /*
+ * We temporarily set fc_myDID to make it look like we are
+ * a Fabric. This is done just so we end up with the right
+ * did / sid on the FLOGI ACC rsp.
+ */
+ did = vport->fc_myDID;
+ vport->fc_myDID = Fabric_DID;
+
+ memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
+
+ /* Defer ACC response until AFTER we issue a FLOGI */
+ if (!test_bit(HBA_FLOGI_ISSUED, &phba->hba_flag)) {
+ phba->defer_flogi_acc.rx_id = bf_get(wqe_ctxt_tag,
+ &wqe->xmit_els_rsp.wqe_com);
+ phba->defer_flogi_acc.ox_id = bf_get(wqe_rcvoxid,
+ &wqe->xmit_els_rsp.wqe_com);
+
+ vport->fc_myDID = did;
+ phba->defer_flogi_acc.flag = true;
+
+ /* This nlp_get is paired with nlp_puts that reset the
+ * defer_flogi_acc.flag back to false. We need to retain
+ * a kref on the ndlp until the deferred FLOGI ACC is
+ * processed or cancelled.
+ */
+ phba->defer_flogi_acc.ndlp = lpfc_nlp_get(ndlp);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3344 Deferring FLOGI ACC: rx_id: x%x,"
+ " ox_id: x%x, ndlp x%px, hba_flag x%lx\n",
+ phba->defer_flogi_acc.rx_id,
+ phba->defer_flogi_acc.ox_id,
+ phba->defer_flogi_acc.ndlp,
+ phba->hba_flag);
+ return 0;
}
/* Send back ACC */
- lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL);
+ lpfc_els_rsp_acc(vport, ELS_CMD_FLOGI, cmdiocb, ndlp, NULL);
+
+ /* Now lets put fc_myDID back to what its supposed to be */
+ vport->fc_myDID = did;
return 0;
}
@@ -4916,17 +8546,13 @@ lpfc_els_rcv_rnid(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
{
struct lpfc_dmabuf *pcmd;
uint32_t *lp;
- IOCB_t *icmd;
RNID *rn;
struct ls_rjt stat;
- uint32_t cmd, did;
- icmd = &cmdiocb->iocb;
- did = icmd->un.elsreq64.remoteID;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
lp = (uint32_t *) pcmd->virt;
- cmd = *lp++;
+ lp++;
rn = (RNID *) lp;
/* RNID received */
@@ -4964,7 +8590,7 @@ lpfc_els_rcv_echo(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
{
uint8_t *pcmd;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) cmdiocb->context2)->virt);
+ pcmd = (uint8_t *)cmdiocb->cmd_dmabuf->virt;
/* skip over first word of echo command to find echo data */
pcmd += sizeof(uint32_t);
@@ -5033,36 +8659,41 @@ lpfc_els_rcv_rrq(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
*
* This routine is the completion callback function for the MBX_READ_LNK_STAT
* mailbox command. This callback function is to actually send the Accept
- * (ACC) response to a Read Port Status (RPS) unsolicited IOCB event. It
+ * (ACC) response to a Read Link Status (RLS) unsolicited IOCB event. It
* collects the link statistics from the completion of the MBX_READ_LNK_STAT
- * mailbox command, constructs the RPS response with the link statistics
+ * mailbox command, constructs the RLS response with the link statistics
* collected, and then invokes the lpfc_sli_issue_iocb() routine to send ACC
- * response to the RPS.
+ * response to the RLS.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the RPS Accept Response ELS IOCB command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the RLS Accept Response
+ * ELS IOCB command.
*
**/
static void
lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
+ int rc = 0;
MAILBOX_t *mb;
IOCB_t *icmd;
+ union lpfc_wqe128 *wqe;
struct RLS_RSP *rls_rsp;
uint8_t *pcmd;
struct lpfc_iocbq *elsiocb;
struct lpfc_nodelist *ndlp;
- uint16_t xri;
+ uint16_t oxid;
+ uint16_t rxid;
uint32_t cmdsize;
+ u32 ulp_context;
mb = &pmb->u.mb;
- ndlp = (struct lpfc_nodelist *) pmb->context2;
- xri = (uint16_t) ((unsigned long)(pmb->context1));
- pmb->context1 = NULL;
- pmb->context2 = NULL;
+ ndlp = pmb->ctx_ndlp;
+ rxid = (uint16_t)(pmb->ctx_u.ox_rx_id & 0xffff);
+ oxid = (uint16_t)((pmb->ctx_u.ox_rx_id >> 16) & 0xffff);
+ memset(&pmb->ctx_u, 0, sizeof(pmb->ctx_u));
+ pmb->ctx_ndlp = NULL;
if (mb->mbxStatus) {
mempool_free(pmb, phba->mbox_mem_pool);
@@ -5070,7 +8701,6 @@ lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
cmdsize = sizeof(struct RLS_RSP) + sizeof(uint32_t);
- mempool_free(pmb, phba->mbox_mem_pool);
elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
lpfc_max_els_tries, ndlp,
ndlp->nlp_DID, ELS_CMD_ACC);
@@ -5078,13 +8708,24 @@ lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
/* Decrement the ndlp reference count from previous mbox command */
lpfc_nlp_put(ndlp);
- if (!elsiocb)
+ if (!elsiocb) {
+ mempool_free(pmb, phba->mbox_mem_pool);
return;
+ }
- icmd = &elsiocb->iocb;
- icmd->ulpContext = xri;
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* Xri / rx_id */
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com, rxid);
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com, oxid);
+ } else {
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = rxid;
+ icmd->unsli3.rcvsli3.ox_id = oxid;
+ }
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t); /* Skip past command */
rls_rsp = (struct RLS_RSP *)pcmd;
@@ -5095,109 +8736,27 @@ lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
rls_rsp->primSeqErrCnt = cpu_to_be32(mb->un.varRdLnk.primSeqErrCnt);
rls_rsp->invalidXmitWord = cpu_to_be32(mb->un.varRdLnk.invalidXmitWord);
rls_rsp->crcCnt = cpu_to_be32(mb->un.varRdLnk.crcCnt);
-
+ mempool_free(pmb, phba->mbox_mem_pool);
/* Xmit ELS RLS ACC response tag <ulpIoTag> */
lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_ELS,
"2874 Xmit ELS RLS ACC response tag x%x xri x%x, "
- "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
+ "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x\n",
+ elsiocb->iotag, ulp_context,
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
phba->fc_stat.elsXmitACC++;
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) == IOCB_ERROR)
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
lpfc_els_free_iocb(phba, elsiocb);
-}
-
-/**
- * lpfc_els_rsp_rps_acc - Completion callbk func for MBX_READ_LNK_STAT mbox cmd
- * @phba: pointer to lpfc hba data structure.
- * @pmb: pointer to the driver internal queue element for mailbox command.
- *
- * This routine is the completion callback function for the MBX_READ_LNK_STAT
- * mailbox command. This callback function is to actually send the Accept
- * (ACC) response to a Read Port Status (RPS) unsolicited IOCB event. It
- * collects the link statistics from the completion of the MBX_READ_LNK_STAT
- * mailbox command, constructs the RPS response with the link statistics
- * collected, and then invokes the lpfc_sli_issue_iocb() routine to send ACC
- * response to the RPS.
- *
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the RPS Accept Response ELS IOCB command.
- *
- **/
-static void
-lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
-{
- MAILBOX_t *mb;
- IOCB_t *icmd;
- RPS_RSP *rps_rsp;
- uint8_t *pcmd;
- struct lpfc_iocbq *elsiocb;
- struct lpfc_nodelist *ndlp;
- uint16_t xri, status;
- uint32_t cmdsize;
-
- mb = &pmb->u.mb;
-
- ndlp = (struct lpfc_nodelist *) pmb->context2;
- xri = (uint16_t) ((unsigned long)(pmb->context1));
- pmb->context1 = NULL;
- pmb->context2 = NULL;
-
- if (mb->mbxStatus) {
- mempool_free(pmb, phba->mbox_mem_pool);
return;
}
- cmdsize = sizeof(RPS_RSP) + sizeof(uint32_t);
- mempool_free(pmb, phba->mbox_mem_pool);
- elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
- lpfc_max_els_tries, ndlp,
- ndlp->nlp_DID, ELS_CMD_ACC);
-
- /* Decrement the ndlp reference count from previous mbox command */
- lpfc_nlp_put(ndlp);
-
- if (!elsiocb)
- return;
-
- icmd = &elsiocb->iocb;
- icmd->ulpContext = xri;
-
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
- *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
- pcmd += sizeof(uint32_t); /* Skip past command */
- rps_rsp = (RPS_RSP *)pcmd;
-
- if (phba->fc_topology != LPFC_TOPOLOGY_LOOP)
- status = 0x10;
- else
- status = 0x8;
- if (phba->pport->fc_flag & FC_FABRIC)
- status |= 0x4;
-
- rps_rsp->rsvd1 = 0;
- rps_rsp->portStatus = cpu_to_be16(status);
- rps_rsp->linkFailureCnt = cpu_to_be32(mb->un.varRdLnk.linkFailureCnt);
- rps_rsp->lossSyncCnt = cpu_to_be32(mb->un.varRdLnk.lossSyncCnt);
- rps_rsp->lossSignalCnt = cpu_to_be32(mb->un.varRdLnk.lossSignalCnt);
- rps_rsp->primSeqErrCnt = cpu_to_be32(mb->un.varRdLnk.primSeqErrCnt);
- rps_rsp->invalidXmitWord = cpu_to_be32(mb->un.varRdLnk.invalidXmitWord);
- rps_rsp->crcCnt = cpu_to_be32(mb->un.varRdLnk.crcCnt);
- /* Xmit ELS RPS ACC response tag <ulpIoTag> */
- lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_ELS,
- "0118 Xmit ELS RPS ACC response tag x%x xri x%x, "
- "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
- ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
- ndlp->nlp_rpi);
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
- phba->fc_stat.elsXmitACC++;
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) == IOCB_ERROR)
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ }
return;
}
@@ -5207,7 +8766,7 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
*
- * This routine processes Read Port Status (RPL) IOCB received as an
+ * This routine processes Read Link Status (RLS) IOCB received as an
* ELS unsolicited event. It first checks the remote port state. If the
* remote port is not in NLP_STE_UNMAPPED_NODE state or NLP_STE_MAPPED_NODE
* state, it invokes the lpfc_els_rsl_reject() routine to send the reject
@@ -5225,22 +8784,22 @@ lpfc_els_rcv_rls(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
{
struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *mbox;
- struct lpfc_dmabuf *pcmd;
struct ls_rjt stat;
+ u32 ctx = get_job_ulpcontext(phba, cmdiocb);
+ u32 ox_id = get_job_rcvoxid(phba, cmdiocb);
if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
(ndlp->nlp_state != NLP_STE_MAPPED_NODE))
- /* reject the unsolicited RPS request and done with it */
+ /* reject the unsolicited RLS request and done with it */
goto reject_out;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
-
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
if (mbox) {
lpfc_read_lnk_stat(phba, mbox);
- mbox->context1 =
- (void *)((unsigned long) cmdiocb->iocb.ulpContext);
- mbox->context2 = lpfc_nlp_get(ndlp);
+ mbox->ctx_u.ox_rx_id = ox_id << 16 | ctx;
+ mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!mbox->ctx_ndlp)
+ goto node_err;
mbox->vport = vport;
mbox->mbox_cmpl = lpfc_els_rsp_rls_acc;
if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT)
@@ -5251,6 +8810,7 @@ lpfc_els_rcv_rls(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
* command.
*/
lpfc_nlp_put(ndlp);
+node_err:
mempool_free(mbox, phba->mbox_mem_pool);
}
reject_out:
@@ -5269,17 +8829,17 @@ reject_out:
* @cmdiocb: pointer to lpfc command iocb data structure.
* @ndlp: pointer to a node-list data structure.
*
- * This routine processes Read Timout Value (RTV) IOCB received as an
+ * This routine processes Read Timeout Value (RTV) IOCB received as an
* ELS unsolicited event. It first checks the remote port state. If the
* remote port is not in NLP_STE_UNMAPPED_NODE state or NLP_STE_MAPPED_NODE
* state, it invokes the lpfc_els_rsl_reject() routine to send the reject
* response. Otherwise, it sends the Accept(ACC) response to a Read Timeout
* Value (RTV) unsolicited IOCB event.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the RPS Accept Response ELS IOCB command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the RTV Accept Response
+ * ELS IOCB command.
*
* Return codes
* 0 - Successfully processed rtv iocb (currently always return 0)
@@ -5288,17 +8848,20 @@ static int
lpfc_els_rcv_rtv(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
struct lpfc_nodelist *ndlp)
{
+ int rc = 0;
+ IOCB_t *icmd;
+ union lpfc_wqe128 *wqe;
struct lpfc_hba *phba = vport->phba;
struct ls_rjt stat;
struct RTV_RSP *rtv_rsp;
uint8_t *pcmd;
struct lpfc_iocbq *elsiocb;
uint32_t cmdsize;
-
+ u32 ulp_context;
if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
(ndlp->nlp_state != NLP_STE_MAPPED_NODE))
- /* reject the unsolicited RPS request and done with it */
+ /* reject the unsolicited RTV request and done with it */
goto reject_out;
cmdsize = sizeof(struct RTV_RSP) + sizeof(uint32_t);
@@ -5309,12 +8872,23 @@ lpfc_els_rcv_rtv(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
if (!elsiocb)
return 1;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
- *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
+ *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t); /* Skip past command */
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
/* use the command's xri in the response */
- elsiocb->iocb.ulpContext = cmdiocb->iocb.ulpContext;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, cmdiocb));
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, cmdiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = get_job_ulpcontext(phba, cmdiocb);
+ icmd->unsli3.rcvsli3.ox_id = get_job_rcvoxid(phba, cmdiocb);
+ }
rtv_rsp = (struct RTV_RSP *)pcmd;
@@ -5328,93 +8902,26 @@ lpfc_els_rcv_rtv(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
/* Xmit ELS RLS ACC response tag <ulpIoTag> */
lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_ELS,
"2875 Xmit ELS RTV ACC response tag x%x xri x%x, "
- "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x, "
+ "did x%x, nlp_flag x%lx, nlp_state x%x, rpi x%x, "
"Data: x%x x%x x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
+ elsiocb->iotag, ulp_context,
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi,
rtv_rsp->ratov, rtv_rsp->edtov, rtv_rsp->qtov);
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
phba->fc_stat.elsXmitACC++;
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) == IOCB_ERROR)
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
lpfc_els_free_iocb(phba, elsiocb);
- return 0;
-
-reject_out:
- /* issue rejection response */
- stat.un.b.lsRjtRsvd0 = 0;
- stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
- stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
- stat.un.b.vendorUnique = 0;
- lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
- return 0;
-}
-
-/* lpfc_els_rcv_rps - Process an unsolicited rps iocb
- * @vport: pointer to a host virtual N_Port data structure.
- * @cmdiocb: pointer to lpfc command iocb data structure.
- * @ndlp: pointer to a node-list data structure.
- *
- * This routine processes Read Port Status (RPS) IOCB received as an
- * ELS unsolicited event. It first checks the remote port state. If the
- * remote port is not in NLP_STE_UNMAPPED_NODE state or NLP_STE_MAPPED_NODE
- * state, it invokes the lpfc_els_rsp_reject() routine to send the reject
- * response. Otherwise, it issue the MBX_READ_LNK_STAT mailbox command
- * for reading the HBA link statistics. It is for the callback function,
- * lpfc_els_rsp_rps_acc(), set to the MBX_READ_LNK_STAT mailbox command
- * to actually sending out RPS Accept (ACC) response.
- *
- * Return codes
- * 0 - Successfully processed rps iocb (currently always return 0)
- **/
-static int
-lpfc_els_rcv_rps(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
- struct lpfc_nodelist *ndlp)
-{
- struct lpfc_hba *phba = vport->phba;
- uint32_t *lp;
- uint8_t flag;
- LPFC_MBOXQ_t *mbox;
- struct lpfc_dmabuf *pcmd;
- RPS *rps;
- struct ls_rjt stat;
-
- if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
- (ndlp->nlp_state != NLP_STE_MAPPED_NODE))
- /* reject the unsolicited RPS request and done with it */
- goto reject_out;
+ return 0;
+ }
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
- lp = (uint32_t *) pcmd->virt;
- flag = (be32_to_cpu(*lp++) & 0xf);
- rps = (RPS *) lp;
-
- if ((flag == 0) ||
- ((flag == 1) && (be32_to_cpu(rps->un.portNum) == 0)) ||
- ((flag == 2) && (memcmp(&rps->un.portName, &vport->fc_portname,
- sizeof(struct lpfc_name)) == 0))) {
-
- printk("Fix me....\n");
- dump_stack();
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
- if (mbox) {
- lpfc_read_lnk_stat(phba, mbox);
- mbox->context1 =
- (void *)((unsigned long) cmdiocb->iocb.ulpContext);
- mbox->context2 = lpfc_nlp_get(ndlp);
- mbox->vport = vport;
- mbox->mbox_cmpl = lpfc_els_rsp_rps_acc;
- if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT)
- != MBX_NOT_FINISHED)
- /* Mbox completion will send ELS Response */
- return 0;
- /* Decrement reference count used for the failed mbox
- * command.
- */
- lpfc_nlp_put(ndlp);
- mempool_free(mbox, phba->mbox_mem_pool);
- }
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
}
+ return 0;
reject_out:
/* issue rejection response */
@@ -5426,14 +8933,14 @@ reject_out:
return 0;
}
-/* lpfc_issue_els_rrq - Process an unsolicited rps iocb
+/* lpfc_issue_els_rrq - Process an unsolicited rrq iocb
* @vport: pointer to a host virtual N_Port data structure.
* @ndlp: pointer to a node-list data structure.
* @did: DID of the target.
* @rrq: Pointer to the rrq struct.
*
* Build a ELS RRQ command and send it to the target. If the issue_iocb is
- * Successful the the completion handler will clear the RRQ.
+ * successful, the completion handler will clear the RRQ.
*
* Return codes
* 0 - Successfully sent rrq els iocb.
@@ -5445,16 +8952,12 @@ lpfc_issue_els_rrq(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_hba *phba = vport->phba;
struct RRQ *els_rrq;
- IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
uint16_t cmdsize;
int ret;
-
- if (ndlp != rrq->ndlp)
- ndlp = rrq->ndlp;
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
+ if (!ndlp)
return 1;
/* If ndlp is not NULL, we will bump the reference count on it */
@@ -5464,15 +8967,14 @@ lpfc_issue_els_rrq(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
/* For RRQ request, remainder of payload is Exchange IDs */
*((uint32_t *) (pcmd)) = ELS_CMD_RRQ;
pcmd += sizeof(uint32_t);
els_rrq = (struct RRQ *) pcmd;
- bf_set(rrq_oxid, els_rrq, rrq->xritag);
+ bf_set(rrq_oxid, els_rrq, phba->sli4_hba.xri_ids[rrq->xritag]);
bf_set(rrq_rxid, els_rrq, rrq->rxid);
bf_set(rrq_did, els_rrq, vport->fc_myDID);
els_rrq->rrq = cpu_to_be32(els_rrq->rrq);
@@ -5483,14 +8985,22 @@ lpfc_issue_els_rrq(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
"Issue RRQ: did:x%x",
did, rrq->xritag, rrq->rxid);
elsiocb->context_un.rrq = rrq;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rrq;
- ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rrq;
+
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp)
+ goto io_err;
+ ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (ret == IOCB_ERROR) {
- lpfc_els_free_iocb(phba, elsiocb);
- return 1;
+ lpfc_nlp_put(ndlp);
+ goto io_err;
}
return 0;
+
+ io_err:
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 1;
}
/**
@@ -5509,7 +9019,10 @@ int
lpfc_send_rrq(struct lpfc_hba *phba, struct lpfc_node_rrq *rrq)
{
struct lpfc_nodelist *ndlp = lpfc_findnode_did(rrq->vport,
- rrq->nlp_DID);
+ rrq->nlp_DID);
+ if (!ndlp)
+ return 1;
+
if (lpfc_test_rrq_active(phba, ndlp, rrq->xritag))
return lpfc_issue_els_rrq(rrq->vport, ndlp,
rrq->nlp_DID, rrq);
@@ -5527,10 +9040,10 @@ lpfc_send_rrq(struct lpfc_hba *phba, struct lpfc_node_rrq *rrq)
* This routine issuees an Accept (ACC) Read Port List (RPL) ELS command.
* It is to be called by the lpfc_els_rcv_rpl() routine to accept the RPL.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the RPL Accept Response ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the RPL Accept Response
+ * ELS command.
*
* Return code
* 0 - Successfully issued ACC RPL ELS command
@@ -5540,11 +9053,14 @@ static int
lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
struct lpfc_iocbq *oldiocb, struct lpfc_nodelist *ndlp)
{
+ int rc = 0;
struct lpfc_hba *phba = vport->phba;
- IOCB_t *icmd, *oldcmd;
+ IOCB_t *icmd;
+ union lpfc_wqe128 *wqe;
RPL_RSP rpl_rsp;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
+ u32 ulp_context;
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
ndlp->nlp_DID, ELS_CMD_ACC);
@@ -5552,11 +9068,21 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- oldcmd = &oldiocb->iocb;
- icmd->ulpContext = oldcmd->ulpContext; /* Xri */
+ ulp_context = get_job_ulpcontext(phba, elsiocb);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ /* Xri / rx_id */
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ get_job_ulpcontext(phba, oldiocb));
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ get_job_rcvoxid(phba, oldiocb));
+ } else {
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = get_job_ulpcontext(phba, oldiocb);
+ icmd->unsli3.rcvsli3.ox_id = get_job_rcvoxid(phba, oldiocb);
+ }
- pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint16_t);
*((uint16_t *)(pcmd)) = be16_to_cpu(cmdsize);
@@ -5573,18 +9099,26 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
/* Xmit ELS RPL ACC response tag <ulpIoTag> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0120 Xmit ELS RPL ACC response tag x%x "
- "xri x%x, did x%x, nlp_flag x%x, nlp_state x%x, "
+ "xri x%x, did x%x, nlp_flag x%lx, nlp_state x%x, "
"rpi x%x\n",
- elsiocb->iotag, elsiocb->iocb.ulpContext,
+ elsiocb->iotag, ulp_context,
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
- elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_rsp;
phba->fc_stat.elsXmitACC++;
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
- IOCB_ERROR) {
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
}
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ return 1;
+ }
+
return 0;
}
@@ -5628,7 +9162,7 @@ lpfc_els_rcv_rpl(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
return 0;
}
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
lp = (uint32_t *) pcmd->virt;
rpl = (RPL *) (lp + 1);
maxsize = be32_to_cpu(rpl->maxsize);
@@ -5676,16 +9210,14 @@ lpfc_els_rcv_farp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
{
struct lpfc_dmabuf *pcmd;
uint32_t *lp;
- IOCB_t *icmd;
FARP *fp;
- uint32_t cmd, cnt, did;
+ uint32_t cnt, did;
- icmd = &cmdiocb->iocb;
- did = icmd->un.elsreq64.remoteID;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ did = get_job_els_rsp64_did(vport->phba, cmdiocb);
+ pcmd = cmdiocb->cmd_dmabuf;
lp = (uint32_t *) pcmd->virt;
- cmd = *lp++;
+ lp++;
fp = (FARP *) lp;
/* FARP-REQ received from DID <did> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
@@ -5747,17 +9279,10 @@ static int
lpfc_els_rcv_farpr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
struct lpfc_nodelist *ndlp)
{
- struct lpfc_dmabuf *pcmd;
- uint32_t *lp;
- IOCB_t *icmd;
- uint32_t cmd, did;
+ uint32_t did;
- icmd = &cmdiocb->iocb;
- did = icmd->un.elsreq64.remoteID;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
- lp = (uint32_t *) pcmd->virt;
+ did = get_job_els_rsp64_did(vport->phba, cmdiocb);
- cmd = *lp++;
/* FARP-RSP received from DID <did> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0600 FARP-RSP received from DID x%x\n", did);
@@ -5795,7 +9320,7 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
FAN *fp;
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0265 FAN received\n");
- lp = (uint32_t *)((struct lpfc_dmabuf *)cmdiocb->context2)->virt;
+ lp = (uint32_t *)cmdiocb->cmd_dmabuf->virt;
fp = (FAN *) ++lp;
/* FAN received; Fan does not have a reply sequence */
if ((vport == phba->pport) &&
@@ -5811,16 +9336,152 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
vport->fc_myDID = vport->fc_prevDID;
if (phba->sli_rev < LPFC_SLI_REV4)
lpfc_issue_fabric_reglogin(vport);
- else
+ else {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3138 Need register VFI: (x%x/%x)\n",
+ vport->fc_prevDID, vport->fc_myDID);
lpfc_issue_reg_vfi(vport);
+ }
}
}
return 0;
}
/**
+ * lpfc_els_rcv_edc - Process an unsolicited EDC iocb
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * Return code
+ * 0 - Successfully processed echo iocb (currently always return 0)
+ **/
+static int
+lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_els_edc *edc_req;
+ struct fc_tlv_desc *tlv;
+ uint8_t *payload;
+ uint32_t *ptr, dtag;
+ const char *dtag_nm;
+ int desc_cnt = 0, bytes_remain;
+ struct fc_diag_lnkflt_desc *plnkflt;
+
+ payload = cmdiocb->cmd_dmabuf->virt;
+
+ edc_req = (struct fc_els_edc *)payload;
+ bytes_remain = be32_to_cpu(edc_req->desc_len);
+
+ ptr = (uint32_t *)payload;
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_CGN_MGMT | LOG_LDS_EVENT,
+ "3319 Rcv EDC payload len %d: x%x x%x x%x\n",
+ bytes_remain, be32_to_cpu(*ptr),
+ be32_to_cpu(*(ptr + 1)), be32_to_cpu(*(ptr + 2)));
+
+ /* No signal support unless there is a congestion descriptor */
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ phba->cgn_sig_freq = 0;
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_ALARM | LPFC_CGN_FPIN_WARN;
+
+ if (bytes_remain <= 0)
+ goto out;
+
+ tlv = edc_req->desc;
+
+ /*
+ * cycle through EDC diagnostic descriptors to find the
+ * congestion signaling capability descriptor
+ */
+ while (bytes_remain) {
+ if (bytes_remain < FC_TLV_DESC_HDR_SZ) {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_ELS | LOG_CGN_MGMT | LOG_LDS_EVENT,
+ "6464 Truncated TLV hdr on "
+ "Diagnostic descriptor[%d]\n",
+ desc_cnt);
+ goto out;
+ }
+
+ dtag = be32_to_cpu(tlv->desc_tag);
+ switch (dtag) {
+ case ELS_DTAG_LNK_FAULT_CAP:
+ if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) ||
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv) !=
+ sizeof(struct fc_diag_lnkflt_desc)) {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_ELS | LOG_CGN_MGMT | LOG_LDS_EVENT,
+ "6465 Truncated Link Fault Diagnostic "
+ "descriptor[%d]: %d vs 0x%zx 0x%zx\n",
+ desc_cnt, bytes_remain,
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv),
+ sizeof(struct fc_diag_lnkflt_desc));
+ goto out;
+ }
+ plnkflt = (struct fc_diag_lnkflt_desc *)tlv;
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_ELS | LOG_LDS_EVENT,
+ "4626 Link Fault Desc Data: x%08x len x%x "
+ "da x%x dd x%x interval x%x\n",
+ be32_to_cpu(plnkflt->desc_tag),
+ be32_to_cpu(plnkflt->desc_len),
+ be32_to_cpu(
+ plnkflt->degrade_activate_threshold),
+ be32_to_cpu(
+ plnkflt->degrade_deactivate_threshold),
+ be32_to_cpu(plnkflt->fec_degrade_interval));
+ break;
+ case ELS_DTAG_CG_SIGNAL_CAP:
+ if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) ||
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv) !=
+ sizeof(struct fc_diag_cg_sig_desc)) {
+ lpfc_printf_log(
+ phba, KERN_WARNING, LOG_CGN_MGMT,
+ "6466 Truncated cgn signal Diagnostic "
+ "descriptor[%d]: %d vs 0x%zx 0x%zx\n",
+ desc_cnt, bytes_remain,
+ FC_TLV_DESC_SZ_FROM_LENGTH(tlv),
+ sizeof(struct fc_diag_cg_sig_desc));
+ goto out;
+ }
+
+ phba->cgn_reg_fpin = phba->cgn_init_reg_fpin;
+ phba->cgn_reg_signal = phba->cgn_init_reg_signal;
+
+ /* We start negotiation with lpfc_fabric_cgn_frequency.
+ * When we process the EDC, we will settle on the
+ * higher frequency.
+ */
+ phba->cgn_sig_freq = lpfc_fabric_cgn_frequency;
+
+ lpfc_least_capable_settings(
+ phba, (struct fc_diag_cg_sig_desc *)tlv);
+ break;
+ default:
+ dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_ELS | LOG_CGN_MGMT | LOG_LDS_EVENT,
+ "6467 unknown Diagnostic "
+ "Descriptor[%d]: tag x%x (%s)\n",
+ desc_cnt, dtag, dtag_nm);
+ }
+ bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv);
+ tlv = fc_tlv_next_desc(tlv);
+ desc_cnt++;
+ }
+out:
+ /* Need to send back an ACC */
+ lpfc_issue_els_edc_rsp(vport, cmdiocb, ndlp);
+
+ lpfc_config_cgn_signal(phba);
+ return 0;
+}
+
+/**
* lpfc_els_timeout - Handler funciton to the els timer
- * @ptr: holder for the timer function associated data.
+ * @t: timer context used to obtain the vport.
*
* This routine is invoked by the ELS timer after timeout. It posts the ELS
* timer timeout event by setting the WORKER_ELS_TMO bit to the work port
@@ -5829,20 +9490,20 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
* lpfc_els_timeout_handler() to work on the posted event WORKER_ELS_TMO.
**/
void
-lpfc_els_timeout(unsigned long ptr)
+lpfc_els_timeout(struct timer_list *t)
{
- struct lpfc_vport *vport = (struct lpfc_vport *) ptr;
+ struct lpfc_vport *vport = timer_container_of(vport, t, els_tmofunc);
struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted;
unsigned long iflag;
spin_lock_irqsave(&vport->work_port_lock, iflag);
tmo_posted = vport->work_port_events & WORKER_ELS_TMO;
- if (!tmo_posted)
+ if (!tmo_posted && !test_bit(FC_UNLOADING, &vport->load_flag))
vport->work_port_events |= WORKER_ELS_TMO;
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
- if (!tmo_posted)
+ if (!tmo_posted && !test_bit(FC_UNLOADING, &vport->load_flag))
lpfc_worker_wake_up(phba);
return;
}
@@ -5868,30 +9529,45 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
uint32_t els_command = 0;
uint32_t timeout;
uint32_t remote_ID = 0xffffffff;
- LIST_HEAD(txcmplq_completions);
LIST_HEAD(abort_list);
+ u32 ulp_command = 0, ulp_context = 0, did = 0, iotag = 0;
timeout = (uint32_t)(phba->fc_ratov << 1);
- pring = &phba->sli.ring[LPFC_ELS_RING];
+ pring = lpfc_phba_elsring(phba);
+ if (unlikely(!pring))
+ return;
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return;
spin_lock_irq(&phba->hbalock);
- list_splice_init(&pring->txcmplq, &txcmplq_completions);
- spin_unlock_irq(&phba->hbalock);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_lock(&pring->ring_lock);
- list_for_each_entry_safe(piocb, tmp_iocb, &txcmplq_completions, list) {
- cmd = &piocb->iocb;
+ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
+ ulp_command = get_job_cmnd(phba, piocb);
+ ulp_context = get_job_ulpcontext(phba, piocb);
+ did = get_job_els_rsp64_did(phba, piocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ iotag = get_wqe_reqtag(piocb);
+ } else {
+ cmd = &piocb->iocb;
+ iotag = cmd->ulpIoTag;
+ }
- if ((piocb->iocb_flag & LPFC_IO_LIBDFC) != 0 ||
- piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
- piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
+ if ((piocb->cmd_flag & LPFC_IO_LIBDFC) != 0 ||
+ ulp_command == CMD_ABORT_XRI_CX ||
+ ulp_command == CMD_ABORT_XRI_CN ||
+ ulp_command == CMD_CLOSE_XRI_CN)
continue;
if (piocb->vport != vport)
continue;
- pcmd = (struct lpfc_dmabuf *) piocb->context2;
+ pcmd = piocb->cmd_dmabuf;
if (pcmd)
els_command = *(uint32_t *) (pcmd->virt);
@@ -5909,33 +9585,39 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
}
remote_ID = 0xffffffff;
- if (cmd->ulpCommand != CMD_GEN_REQUEST64_CR)
- remote_ID = cmd->un.elsreq64.remoteID;
- else {
+ if (ulp_command != CMD_GEN_REQUEST64_CR) {
+ remote_ID = did;
+ } else {
struct lpfc_nodelist *ndlp;
- ndlp = __lpfc_findnode_rpi(vport, cmd->ulpContext);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp))
+ ndlp = __lpfc_findnode_rpi(vport, ulp_context);
+ if (ndlp)
remote_ID = ndlp->nlp_DID;
}
list_add_tail(&piocb->dlist, &abort_list);
}
- spin_lock_irq(&phba->hbalock);
- list_splice(&txcmplq_completions, &pring->txcmplq);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring->ring_lock);
spin_unlock_irq(&phba->hbalock);
list_for_each_entry_safe(piocb, tmp_iocb, &abort_list, dlist) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0127 ELS timeout Data: x%x x%x x%x "
"x%x\n", els_command,
- remote_ID, cmd->ulpCommand, cmd->ulpIoTag);
+ remote_ID, ulp_command, iotag);
+
spin_lock_irq(&phba->hbalock);
list_del_init(&piocb->dlist);
- lpfc_sli_issue_abort_iotag(phba, pring, piocb);
+ lpfc_sli_issue_abort_iotag(phba, pring, piocb, NULL);
spin_unlock_irq(&phba->hbalock);
}
- if (phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt)
- mod_timer(&vport->els_tmofunc, jiffies + HZ * timeout);
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
+ if (!list_empty(&pring->txcmplq))
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ mod_timer(&vport->els_tmofunc,
+ jiffies + secs_to_jiffies(timeout));
}
/**
@@ -5961,51 +9643,148 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
void
lpfc_els_flush_cmd(struct lpfc_vport *vport)
{
- LIST_HEAD(completions);
+ LIST_HEAD(abort_list);
+ LIST_HEAD(cancel_list);
struct lpfc_hba *phba = vport->phba;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring;
struct lpfc_iocbq *tmp_iocb, *piocb;
- IOCB_t *cmd = NULL;
+ u32 ulp_command;
+ unsigned long iflags = 0;
+ bool mbx_tmo_err;
lpfc_fabric_abort_vport(vport);
- spin_lock_irq(&phba->hbalock);
- list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) {
- cmd = &piocb->iocb;
+ /*
+ * For SLI3, only the hbalock is required. But SLI4 needs to coordinate
+ * with the ring insert operation. Because lpfc_sli_issue_abort_iotag
+ * ultimately grabs the ring_lock, the driver must splice the list into
+ * a working list and release the locks before calling the abort.
+ */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pring = lpfc_phba_elsring(phba);
- if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
- continue;
- }
+ /* Bail out if we've no ELS wq, like in PCI error recovery case. */
+ if (unlikely(!pring)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return;
+ }
- /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */
- if (cmd->ulpCommand == CMD_QUE_RING_BUF_CN ||
- cmd->ulpCommand == CMD_QUE_RING_BUF64_CN ||
- cmd->ulpCommand == CMD_CLOSE_XRI_CN ||
- cmd->ulpCommand == CMD_ABORT_XRI_CN)
- continue;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_lock(&pring->ring_lock);
+ mbx_tmo_err = test_bit(MBX_TMO_ERR, &phba->bit_flags);
+ /* First we need to issue aborts to outstanding cmds on txcmpl */
+ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
if (piocb->vport != vport)
continue;
- list_move_tail(&piocb->list, &completions);
- pring->txq_cnt--;
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2243 iotag = 0x%x cmd_flag = 0x%x "
+ "ulp_command = 0x%x sli_flag = 0x%x\n",
+ piocb->iotag, piocb->cmd_flag,
+ get_job_cmnd(phba, piocb),
+ phba->sli.sli_flag);
+
+ if ((phba->sli.sli_flag & LPFC_SLI_ACTIVE) && !mbx_tmo_err) {
+ if (piocb->cmd_flag & LPFC_IO_LIBDFC)
+ continue;
+ if (piocb->cmd_flag & LPFC_DRIVER_ABORTED)
+ continue;
+ }
+
+ /* On the ELS ring we can have ELS_REQUESTs, ELS_RSPs,
+ * or GEN_REQUESTs waiting for a CQE response.
+ */
+ ulp_command = get_job_cmnd(phba, piocb);
+ if (ulp_command == CMD_ELS_REQUEST64_WQE ||
+ ulp_command == CMD_XMIT_ELS_RSP64_WQE) {
+ list_add_tail(&piocb->dlist, &abort_list);
+
+ /* If the link is down when flushing ELS commands
+ * the firmware will not complete them till after
+ * the link comes back up. This may confuse
+ * discovery for the new link up, so we need to
+ * change the compl routine to just clean up the iocb
+ * and avoid any retry logic.
+ */
+ if (phba->link_state == LPFC_LINK_DOWN)
+ piocb->cmd_cmpl = lpfc_cmpl_els_link_down;
+ } else if (ulp_command == CMD_GEN_REQUEST64_CR ||
+ mbx_tmo_err)
+ list_add_tail(&piocb->dlist, &abort_list);
}
- list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
- if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring->ring_lock);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ /* Abort each txcmpl iocb on aborted list and remove the dlist links. */
+ list_for_each_entry_safe(piocb, tmp_iocb, &abort_list, dlist) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ list_del_init(&piocb->dlist);
+ if (mbx_tmo_err || !(phba->sli.sli_flag & LPFC_SLI_ACTIVE))
+ list_move_tail(&piocb->list, &cancel_list);
+ else
+ lpfc_sli_issue_abort_iotag(phba, pring, piocb, NULL);
+
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
+ if (!list_empty(&cancel_list))
+ lpfc_sli_cancel_iocbs(phba, &cancel_list, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_ABORTED);
+ else
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
+ if (!list_empty(&abort_list))
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "3387 abort list for txq not empty\n");
+ INIT_LIST_HEAD(&abort_list);
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_lock(&pring->ring_lock);
+
+ /* No need to abort the txq list,
+ * just queue them up for lpfc_sli_cancel_iocbs
+ */
+ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) {
+ ulp_command = get_job_cmnd(phba, piocb);
+
+ if (piocb->cmd_flag & LPFC_IO_LIBDFC)
+ continue;
+
+ /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */
+ if (ulp_command == CMD_QUE_RING_BUF_CN ||
+ ulp_command == CMD_QUE_RING_BUF64_CN ||
+ ulp_command == CMD_CLOSE_XRI_CN ||
+ ulp_command == CMD_ABORT_XRI_CN ||
+ ulp_command == CMD_ABORT_XRI_CX)
continue;
- }
if (piocb->vport != vport)
continue;
- lpfc_sli_issue_abort_iotag(phba, pring, piocb);
+ list_del_init(&piocb->list);
+ list_add_tail(&piocb->list, &abort_list);
}
- spin_unlock_irq(&phba->hbalock);
- /* Cancell all the IOCBs from the completions list */
- lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
- IOERR_SLI_ABORTED);
+ /* The same holds true for any FLOGI/FDISC on the fabric_iocb_list */
+ if (vport == phba->pport) {
+ list_for_each_entry_safe(piocb, tmp_iocb,
+ &phba->fabric_iocb_list, list) {
+ list_del_init(&piocb->list);
+ list_add_tail(&piocb->list, &abort_list);
+ }
+ }
+
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring->ring_lock);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &abort_list,
+ IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED);
return;
}
@@ -6030,36 +9809,12 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport)
void
lpfc_els_flush_all_cmd(struct lpfc_hba *phba)
{
- LIST_HEAD(completions);
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
- struct lpfc_iocbq *tmp_iocb, *piocb;
- IOCB_t *cmd = NULL;
-
- lpfc_fabric_abort_hba(phba);
- spin_lock_irq(&phba->hbalock);
- list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) {
- cmd = &piocb->iocb;
- if (piocb->iocb_flag & LPFC_IO_LIBDFC)
- continue;
- /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */
- if (cmd->ulpCommand == CMD_QUE_RING_BUF_CN ||
- cmd->ulpCommand == CMD_QUE_RING_BUF64_CN ||
- cmd->ulpCommand == CMD_CLOSE_XRI_CN ||
- cmd->ulpCommand == CMD_ABORT_XRI_CN)
- continue;
- list_move_tail(&piocb->list, &completions);
- pring->txq_cnt--;
- }
- list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
- if (piocb->iocb_flag & LPFC_IO_LIBDFC)
- continue;
- lpfc_sli_issue_abort_iotag(phba, pring, piocb);
- }
- spin_unlock_irq(&phba->hbalock);
+ struct lpfc_vport *vport;
- /* Cancel all the IOCBs from the completions list */
- lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
- IOERR_SLI_ABORTED);
+ spin_lock_irq(&phba->port_list_lock);
+ list_for_each_entry(vport, &phba->port_list, listentry)
+ lpfc_els_flush_cmd(vport);
+ spin_unlock_irq(&phba->port_list_lock);
return;
}
@@ -6085,22 +9840,25 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
struct ls_rjt stat;
struct lpfc_nodelist *ndlp;
uint32_t *pcmd;
+ u32 ulp_status, ulp_word4;
- ndlp = cmdiocbp->context1;
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
+ ndlp = cmdiocbp->ndlp;
+ if (!ndlp)
return;
- if (rspiocbp->iocb.ulpStatus == IOSTAT_LS_RJT) {
+ ulp_status = get_job_ulpstatus(phba, rspiocbp);
+ ulp_word4 = get_job_word4(phba, rspiocbp);
+
+ if (ulp_status == IOSTAT_LS_RJT) {
lsrjt_event.header.event_type = FC_REG_ELS_EVENT;
lsrjt_event.header.subcategory = LPFC_EVENT_LSRJT_RCV;
memcpy(lsrjt_event.header.wwpn, &ndlp->nlp_portname,
sizeof(struct lpfc_name));
memcpy(lsrjt_event.header.wwnn, &ndlp->nlp_nodename,
sizeof(struct lpfc_name));
- pcmd = (uint32_t *) (((struct lpfc_dmabuf *)
- cmdiocbp->context2)->virt);
+ pcmd = (uint32_t *)cmdiocbp->cmd_dmabuf->virt;
lsrjt_event.command = (pcmd != NULL) ? *pcmd : 0;
- stat.un.lsRjtError = be32_to_cpu(rspiocbp->iocb.un.ulpWord[4]);
+ stat.un.ls_rjt_error_be = cpu_to_be32(ulp_word4);
lsrjt_event.reason_code = stat.un.b.lsRjtRsnCode;
lsrjt_event.explanation = stat.un.b.lsRjtRsnCodeExp;
fc_host_post_vendor_event(shost,
@@ -6110,10 +9868,10 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
LPFC_NL_VENDOR_ID);
return;
}
- if ((rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY) ||
- (rspiocbp->iocb.ulpStatus == IOSTAT_FABRIC_BSY)) {
+ if (ulp_status == IOSTAT_NPORT_BSY ||
+ ulp_status == IOSTAT_FABRIC_BSY) {
fabric_event.event_type = FC_REG_FABRIC_EVENT;
- if (rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY)
+ if (ulp_status == IOSTAT_NPORT_BSY)
fabric_event.subcategory = LPFC_EVENT_PORT_BUSY;
else
fabric_event.subcategory = LPFC_EVENT_FABRIC_BUSY;
@@ -6135,7 +9893,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
* lpfc_send_els_event - Posts unsolicited els event
* @vport: Pointer to vport object.
* @ndlp: Pointer FC node object.
- * @cmd: ELS command code.
+ * @payload: ELS command code type.
*
* This function posts an event when there is an incoming
* unsolicited ELS command.
@@ -6152,7 +9910,7 @@ lpfc_send_els_event(struct lpfc_vport *vport,
if (*payload == ELS_CMD_LOGO) {
logo_data = kmalloc(sizeof(struct lpfc_logo_event), GFP_KERNEL);
if (!logo_data) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0148 Failed to allocate memory "
"for LOGO event\n");
return;
@@ -6162,7 +9920,7 @@ lpfc_send_els_event(struct lpfc_vport *vport,
els_data = kmalloc(sizeof(struct lpfc_els_event_header),
GFP_KERNEL);
if (!els_data) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0149 Failed to allocate memory "
"for ELS event\n");
return;
@@ -6211,6 +9969,406 @@ lpfc_send_els_event(struct lpfc_vport *vport,
}
+DECLARE_ENUM2STR_LOOKUP(lpfc_get_fpin_li_event_nm, fc_fpin_li_event_types,
+ FC_FPIN_LI_EVT_TYPES_INIT);
+
+DECLARE_ENUM2STR_LOOKUP(lpfc_get_fpin_deli_event_nm, fc_fpin_deli_event_types,
+ FC_FPIN_DELI_EVT_TYPES_INIT);
+
+DECLARE_ENUM2STR_LOOKUP(lpfc_get_fpin_congn_event_nm, fc_fpin_congn_event_types,
+ FC_FPIN_CONGN_EVT_TYPES_INIT);
+
+DECLARE_ENUM2STR_LOOKUP(lpfc_get_fpin_congn_severity_nm,
+ fc_fpin_congn_severity_types,
+ FC_FPIN_CONGN_SEVERITY_INIT);
+
+
+/**
+ * lpfc_display_fpin_wwpn - Display WWPNs accessible by the attached port
+ * @phba: Pointer to phba object.
+ * @wwnlist: Pointer to list of WWPNs in FPIN payload
+ * @cnt: count of WWPNs in FPIN payload
+ *
+ * This routine is called by LI and PC descriptors.
+ * Limit the number of WWPNs displayed to 6 log messages, 6 per log message
+ */
+static void
+lpfc_display_fpin_wwpn(struct lpfc_hba *phba, __be64 *wwnlist, u32 cnt)
+{
+ char buf[LPFC_FPIN_WWPN_LINE_SZ];
+ __be64 wwn;
+ u64 wwpn;
+ int i, len;
+ int line = 0;
+ int wcnt = 0;
+ bool endit = false;
+
+ len = scnprintf(buf, LPFC_FPIN_WWPN_LINE_SZ, "Accessible WWPNs:");
+ for (i = 0; i < cnt; i++) {
+ /* Are we on the last WWPN */
+ if (i == (cnt - 1))
+ endit = true;
+
+ /* Extract the next WWPN from the payload */
+ wwn = *wwnlist++;
+ wwpn = be64_to_cpu(wwn);
+ len += scnprintf(buf + len, LPFC_FPIN_WWPN_LINE_SZ - len,
+ " %016llx", wwpn);
+
+ /* Log a message if we are on the last WWPN
+ * or if we hit the max allowed per message.
+ */
+ wcnt++;
+ if (wcnt == LPFC_FPIN_WWPN_LINE_CNT || endit) {
+ buf[len] = 0;
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "4686 %s\n", buf);
+
+ /* Check if we reached the last WWPN */
+ if (endit)
+ return;
+
+ /* Limit the number of log message displayed per FPIN */
+ line++;
+ if (line == LPFC_FPIN_WWPN_NUM_LINE) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "4687 %d WWPNs Truncated\n",
+ cnt - i - 1);
+ return;
+ }
+
+ /* Start over with next log message */
+ wcnt = 0;
+ len = scnprintf(buf, LPFC_FPIN_WWPN_LINE_SZ,
+ "Additional WWPNs:");
+ }
+ }
+}
+
+/**
+ * lpfc_els_rcv_fpin_li - Process an FPIN Link Integrity Event.
+ * @phba: Pointer to phba object.
+ * @tlv: Pointer to the Link Integrity Notification Descriptor.
+ *
+ * This function processes a Link Integrity FPIN event by logging a message.
+ **/
+static void
+lpfc_els_rcv_fpin_li(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+{
+ struct fc_fn_li_desc *li = (struct fc_fn_li_desc *)tlv;
+ const char *li_evt_str;
+ u32 li_evt, cnt;
+
+ li_evt = be16_to_cpu(li->event_type);
+ li_evt_str = lpfc_get_fpin_li_event_nm(li_evt);
+ cnt = be32_to_cpu(li->pname_count);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "4680 FPIN Link Integrity %s (x%x) "
+ "Detecting PN x%016llx Attached PN x%016llx "
+ "Duration %d mSecs Count %d Port Cnt %d\n",
+ li_evt_str, li_evt,
+ be64_to_cpu(li->detecting_wwpn),
+ be64_to_cpu(li->attached_wwpn),
+ be32_to_cpu(li->event_threshold),
+ be32_to_cpu(li->event_count), cnt);
+
+ lpfc_display_fpin_wwpn(phba, (__be64 *)&li->pname_list, cnt);
+}
+
+/**
+ * lpfc_els_rcv_fpin_del - Process an FPIN Delivery Event.
+ * @phba: Pointer to hba object.
+ * @tlv: Pointer to the Delivery Notification Descriptor TLV
+ *
+ * This function processes a Delivery FPIN event by logging a message.
+ **/
+static void
+lpfc_els_rcv_fpin_del(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+{
+ struct fc_fn_deli_desc *del = (struct fc_fn_deli_desc *)tlv;
+ const char *del_rsn_str;
+ u32 del_rsn;
+ __be32 *frame;
+
+ del_rsn = be16_to_cpu(del->deli_reason_code);
+ del_rsn_str = lpfc_get_fpin_deli_event_nm(del_rsn);
+
+ /* Skip over desc_tag/desc_len header to payload */
+ frame = (__be32 *)(del + 1);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "4681 FPIN Delivery %s (x%x) "
+ "Detecting PN x%016llx Attached PN x%016llx "
+ "DiscHdr0 x%08x "
+ "DiscHdr1 x%08x DiscHdr2 x%08x DiscHdr3 x%08x "
+ "DiscHdr4 x%08x DiscHdr5 x%08x\n",
+ del_rsn_str, del_rsn,
+ be64_to_cpu(del->detecting_wwpn),
+ be64_to_cpu(del->attached_wwpn),
+ be32_to_cpu(frame[0]),
+ be32_to_cpu(frame[1]),
+ be32_to_cpu(frame[2]),
+ be32_to_cpu(frame[3]),
+ be32_to_cpu(frame[4]),
+ be32_to_cpu(frame[5]));
+}
+
+/**
+ * lpfc_els_rcv_fpin_peer_cgn - Process a FPIN Peer Congestion Event.
+ * @phba: Pointer to hba object.
+ * @tlv: Pointer to the Peer Congestion Notification Descriptor TLV
+ *
+ * This function processes a Peer Congestion FPIN event by logging a message.
+ **/
+static void
+lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+{
+ struct fc_fn_peer_congn_desc *pc = (struct fc_fn_peer_congn_desc *)tlv;
+ const char *pc_evt_str;
+ u32 pc_evt, cnt;
+
+ pc_evt = be16_to_cpu(pc->event_type);
+ pc_evt_str = lpfc_get_fpin_congn_event_nm(pc_evt);
+ cnt = be32_to_cpu(pc->pname_count);
+
+ /* Capture FPIN frequency */
+ phba->cgn_fpin_frequency = be32_to_cpu(pc->event_period);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT | LOG_ELS,
+ "4684 FPIN Peer Congestion %s (x%x) "
+ "Duration %d mSecs "
+ "Detecting PN x%016llx Attached PN x%016llx "
+ "Impacted Port Cnt %d\n",
+ pc_evt_str, pc_evt,
+ be32_to_cpu(pc->event_period),
+ be64_to_cpu(pc->detecting_wwpn),
+ be64_to_cpu(pc->attached_wwpn),
+ cnt);
+
+ lpfc_display_fpin_wwpn(phba, (__be64 *)&pc->pname_list, cnt);
+}
+
+/**
+ * lpfc_els_rcv_fpin_cgn - Process an FPIN Congestion notification
+ * @phba: Pointer to hba object.
+ * @tlv: Pointer to the Congestion Notification Descriptor TLV
+ *
+ * This function processes an FPIN Congestion Notifiction. The notification
+ * could be an Alarm or Warning. This routine feeds that data into driver's
+ * running congestion algorithm. It also processes the FPIN by
+ * logging a message. It returns 1 to indicate deliver this message
+ * to the upper layer or 0 to indicate don't deliver it.
+ **/
+static int
+lpfc_els_rcv_fpin_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+{
+ struct lpfc_cgn_info *cp;
+ struct fc_fn_congn_desc *cgn = (struct fc_fn_congn_desc *)tlv;
+ const char *cgn_evt_str;
+ u32 cgn_evt;
+ const char *cgn_sev_str;
+ u32 cgn_sev;
+ uint16_t value;
+ u32 crc;
+ bool nm_log = false;
+ int rc = 1;
+
+ cgn_evt = be16_to_cpu(cgn->event_type);
+ cgn_evt_str = lpfc_get_fpin_congn_event_nm(cgn_evt);
+ cgn_sev = cgn->severity;
+ cgn_sev_str = lpfc_get_fpin_congn_severity_nm(cgn_sev);
+
+ /* The driver only takes action on a Credit Stall or Oversubscription
+ * event type to engage the IO algorithm. The driver prints an
+ * unmaskable message only for Lost Credit and Credit Stall.
+ * TODO: Still need to have definition of host action on clear,
+ * lost credit and device specific event types.
+ */
+ switch (cgn_evt) {
+ case FPIN_CONGN_LOST_CREDIT:
+ nm_log = true;
+ break;
+ case FPIN_CONGN_CREDIT_STALL:
+ nm_log = true;
+ fallthrough;
+ case FPIN_CONGN_OVERSUBSCRIPTION:
+ if (cgn_evt == FPIN_CONGN_OVERSUBSCRIPTION)
+ nm_log = false;
+ switch (cgn_sev) {
+ case FPIN_CONGN_SEVERITY_ERROR:
+ /* Take action here for an Alarm event */
+ if (phba->cmf_active_mode != LPFC_CFG_OFF) {
+ if (phba->cgn_reg_fpin & LPFC_CGN_FPIN_ALARM) {
+ /* Track of alarm cnt for SYNC_WQE */
+ atomic_inc(&phba->cgn_sync_alarm_cnt);
+ }
+ /* Track alarm cnt for cgn_info regardless
+ * of whether CMF is configured for Signals
+ * or FPINs.
+ */
+ atomic_inc(&phba->cgn_fabric_alarm_cnt);
+ goto cleanup;
+ }
+ break;
+ case FPIN_CONGN_SEVERITY_WARNING:
+ /* Take action here for a Warning event */
+ if (phba->cmf_active_mode != LPFC_CFG_OFF) {
+ if (phba->cgn_reg_fpin & LPFC_CGN_FPIN_WARN) {
+ /* Track of warning cnt for SYNC_WQE */
+ atomic_inc(&phba->cgn_sync_warn_cnt);
+ }
+ /* Track warning cnt and freq for cgn_info
+ * regardless of whether CMF is configured for
+ * Signals or FPINs.
+ */
+ atomic_inc(&phba->cgn_fabric_warn_cnt);
+cleanup:
+ /* Save frequency in ms */
+ phba->cgn_fpin_frequency =
+ be32_to_cpu(cgn->event_period);
+ value = phba->cgn_fpin_frequency;
+ if (phba->cgn_i) {
+ cp = (struct lpfc_cgn_info *)
+ phba->cgn_i->virt;
+ cp->cgn_alarm_freq =
+ cpu_to_le16(value);
+ cp->cgn_warn_freq =
+ cpu_to_le16(value);
+ crc = lpfc_cgn_calc_crc32
+ (cp,
+ LPFC_CGN_INFO_SZ,
+ LPFC_CGN_CRC32_SEED);
+ cp->cgn_info_crc = cpu_to_le32(crc);
+ }
+
+ /* Don't deliver to upper layer since
+ * driver took action on this tlv.
+ */
+ rc = 0;
+ }
+ break;
+ }
+ break;
+ }
+
+ /* Change the log level to unmaskable for the following event types. */
+ lpfc_printf_log(phba, (nm_log ? KERN_WARNING : KERN_INFO),
+ LOG_CGN_MGMT | LOG_ELS,
+ "4683 FPIN CONGESTION %s type %s (x%x) Event "
+ "Duration %d mSecs\n",
+ cgn_sev_str, cgn_evt_str, cgn_evt,
+ be32_to_cpu(cgn->event_period));
+ return rc;
+}
+
+void
+lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_els_fpin *fpin = (struct fc_els_fpin *)p;
+ struct fc_tlv_desc *tlv, *first_tlv, *current_tlv;
+ const char *dtag_nm;
+ int desc_cnt = 0, bytes_remain, cnt;
+ u32 dtag, deliver = 0;
+ int len;
+
+ /* FPINs handled only if we are in the right discovery state */
+ if (vport->port_state < LPFC_DISC_AUTH)
+ return;
+
+ /* make sure there is the full fpin header */
+ if (fpin_length < sizeof(struct fc_els_fpin))
+ return;
+
+ /* Sanity check descriptor length. The desc_len value does not
+ * include space for the ELS command and the desc_len fields.
+ */
+ len = be32_to_cpu(fpin->desc_len);
+ if (fpin_length < len + sizeof(struct fc_els_fpin)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "4671 Bad ELS FPIN length %d: %d\n",
+ len, fpin_length);
+ return;
+ }
+
+ tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+ first_tlv = tlv;
+ bytes_remain = fpin_length - offsetof(struct fc_els_fpin, fpin_desc);
+ bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len));
+
+ /* process each descriptor separately */
+ while (bytes_remain >= FC_TLV_DESC_HDR_SZ &&
+ bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) {
+ dtag = be32_to_cpu(tlv->desc_tag);
+ switch (dtag) {
+ case ELS_DTAG_LNK_INTEGRITY:
+ lpfc_els_rcv_fpin_li(phba, tlv);
+ deliver = 1;
+ break;
+ case ELS_DTAG_DELIVERY:
+ lpfc_els_rcv_fpin_del(phba, tlv);
+ deliver = 1;
+ break;
+ case ELS_DTAG_PEER_CONGEST:
+ lpfc_els_rcv_fpin_peer_cgn(phba, tlv);
+ deliver = 1;
+ break;
+ case ELS_DTAG_CONGESTION:
+ deliver = lpfc_els_rcv_fpin_cgn(phba, tlv);
+ break;
+ default:
+ dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "4678 unknown FPIN descriptor[%d]: "
+ "tag x%x (%s)\n",
+ desc_cnt, dtag, dtag_nm);
+
+ /* If descriptor is bad, drop the rest of the data */
+ return;
+ }
+ lpfc_cgn_update_stat(phba, dtag);
+ cnt = be32_to_cpu(tlv->desc_len);
+
+ /* Sanity check descriptor length. The desc_len value does not
+ * include space for the desc_tag and the desc_len fields.
+ */
+ len -= (cnt + sizeof(struct fc_tlv_desc));
+ if (len < 0) {
+ dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "4672 Bad FPIN descriptor TLV length "
+ "%d: %d %d %s\n",
+ cnt, len, fpin_length, dtag_nm);
+ return;
+ }
+
+ current_tlv = tlv;
+ bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv);
+ tlv = fc_tlv_next_desc(tlv);
+
+ /* Format payload such that the FPIN delivered to the
+ * upper layer is a single descriptor FPIN.
+ */
+ if (desc_cnt)
+ memcpy(first_tlv, current_tlv,
+ (cnt + sizeof(struct fc_els_fpin)));
+
+ /* Adjust the length so that it only reflects a
+ * single descriptor FPIN.
+ */
+ fpin_length = cnt + sizeof(struct fc_els_fpin);
+ fpin->desc_len = cpu_to_be32(fpin_length);
+ fpin_length += sizeof(struct fc_els_fpin); /* the entire FPIN */
+
+ /* Send every descriptor individually to the upper layer */
+ if (deliver)
+ fc_host_fpin_rcv(lpfc_shost_from_vport(vport),
+ fpin_length, (char *)fpin, 0);
+ desc_cnt++;
+ }
+}
+
/**
* lpfc_els_unsol_buffer - Process an unsolicited event data buffer
* @phba: pointer to lpfc hba data structure.
@@ -6229,27 +10387,34 @@ static void
lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_vport *vport, struct lpfc_iocbq *elsiocb)
{
- struct Scsi_Host *shost;
struct lpfc_nodelist *ndlp;
struct ls_rjt stat;
- uint32_t *payload;
- uint32_t cmd, did, newnode, rjt_err = 0;
- IOCB_t *icmd = &elsiocb->iocb;
+ u32 *payload, payload_len;
+ u32 cmd = 0, did = 0, newnode, status = 0;
+ uint8_t rjt_exp, rjt_err = 0, init_link = 0;
+ struct lpfc_wcqe_complete *wcqe_cmpl = NULL;
+ LPFC_MBOXQ_t *mbox;
- if (!vport || !(elsiocb->context2))
+ if (!vport || !elsiocb->cmd_dmabuf)
goto dropit;
newnode = 0;
- payload = ((struct lpfc_dmabuf *)elsiocb->context2)->virt;
+ wcqe_cmpl = &elsiocb->wcqe_cmpl;
+ payload = elsiocb->cmd_dmabuf->virt;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ payload_len = wcqe_cmpl->total_data_placed;
+ else
+ payload_len = elsiocb->iocb.unsli3.rcvsli3.acc_len;
+ status = get_job_ulpstatus(phba, elsiocb);
cmd = *payload;
if ((phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) == 0)
- lpfc_post_buffer(phba, pring, 1);
+ lpfc_sli3_post_buffer(phba, pring, 1);
- did = icmd->un.rcvels.remoteID;
- if (icmd->ulpStatus) {
+ did = get_job_els_rsp64_did(phba, elsiocb);
+ if (status) {
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
"RCV Unsol ELS: status:x%x/x%x did:x%x",
- icmd->ulpStatus, icmd->un.ulpWord[4], did);
+ status, get_job_word4(phba, elsiocb), did);
goto dropit;
}
@@ -6258,29 +10423,18 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
goto dropit;
/* Ignore traffic received during vport shutdown. */
- if (vport->load_flag & FC_UNLOADING)
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
goto dropit;
/* If NPort discovery is delayed drop incoming ELS */
- if ((vport->fc_flag & FC_DISC_DELAYED) &&
- (cmd != ELS_CMD_PLOGI))
+ if (test_bit(FC_DISC_DELAYED, &vport->fc_flag) &&
+ cmd != ELS_CMD_PLOGI)
goto dropit;
ndlp = lpfc_findnode_did(vport, did);
if (!ndlp) {
/* Cannot find existing Fabric ndlp, so allocate a new one */
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
- if (!ndlp)
- goto dropit;
-
- lpfc_nlp_init(vport, ndlp, did);
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- newnode = 1;
- if ((did & Fabric_DID_MASK) == Fabric_DID_MASK)
- ndlp->nlp_type |= NLP_FABRIC;
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp,
- NLP_STE_UNUSED_NODE);
+ ndlp = lpfc_nlp_init(vport, did);
if (!ndlp)
goto dropit;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
@@ -6288,17 +10442,22 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if ((did & Fabric_DID_MASK) == Fabric_DID_MASK)
ndlp->nlp_type |= NLP_FABRIC;
} else if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
- /* This is similar to the new node path */
- ndlp = lpfc_nlp_get(ndlp);
- if (!ndlp)
- goto dropit;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
newnode = 1;
}
phba->fc_stat.elsRcvFrame++;
- elsiocb->context1 = lpfc_nlp_get(ndlp);
+ /*
+ * Do not process any unsolicited ELS commands
+ * if the ndlp is in DEV_LOSS
+ */
+ if (test_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag))
+ goto dropit;
+
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp)
+ goto dropit;
elsiocb->vport = vport;
if ((cmd & ELS_CMD_MASK) == ELS_CMD_RSCN) {
@@ -6307,95 +10466,148 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/* ELS command <elsCmd> received from NPORT <did> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0112 ELS command x%x received from NPORT x%x "
- "Data: x%x\n", cmd, did, vport->port_state);
+ "refcnt %d Data: x%x x%lx x%x x%x\n",
+ cmd, did, kref_read(&ndlp->kref), vport->port_state,
+ vport->fc_flag, vport->fc_myDID, vport->fc_prevDID);
+
+ /* reject till our FLOGI completes or PLOGI assigned DID via PT2PT */
+ if ((vport->port_state < LPFC_FABRIC_CFG_LINK) &&
+ (cmd != ELS_CMD_FLOGI) &&
+ !((cmd == ELS_CMD_PLOGI) && test_bit(FC_PT2PT, &vport->fc_flag))) {
+ rjt_err = LSRJT_LOGICAL_BSY;
+ rjt_exp = LSEXP_NOTHING_MORE;
+ goto lsrjt;
+ }
+
switch (cmd) {
case ELS_CMD_PLOGI:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV PLOGI: did:x%x/ste:x%x flg:x%x",
+ "RCV PLOGI: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvPLOGI++;
ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ test_bit(FC_PT2PT, &phba->pport->fc_flag)) {
+ vport->fc_prevDID = vport->fc_myDID;
+ /* Our DID needs to be updated before registering
+ * the vfi. This is done in lpfc_rcv_plogi but
+ * that is called after the reg_vfi.
+ */
+ vport->fc_myDID =
+ bf_get(els_rsp64_sid,
+ &elsiocb->wqe.xmit_els_rsp);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3312 Remote port assigned DID x%x "
+ "%x\n", vport->fc_myDID,
+ vport->fc_prevDID);
+ }
lpfc_send_els_event(vport, ndlp, payload);
/* If Nport discovery is delayed, reject PLOGIs */
- if (vport->fc_flag & FC_DISC_DELAYED) {
+ if (test_bit(FC_DISC_DELAYED, &vport->fc_flag)) {
rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_NOTHING_MORE;
break;
}
+
if (vport->port_state < LPFC_DISC_AUTH) {
- if (!(phba->pport->fc_flag & FC_PT2PT) ||
- (phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
+ if (!test_bit(FC_PT2PT, &phba->pport->fc_flag) ||
+ test_bit(FC_PT2PT_PLOGI, &phba->pport->fc_flag)) {
rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_NOTHING_MORE;
break;
}
- /* We get here, and drop thru, if we are PT2PT with
- * another NPort and the other side has initiated
- * the PLOGI before responding to our FLOGI.
- */
}
- shost = lpfc_shost_from_vport(vport);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_TARGET_REMOVE;
- spin_unlock_irq(shost->host_lock);
-
lpfc_disc_state_machine(vport, ndlp, elsiocb,
NLP_EVT_RCV_PLOGI);
break;
case ELS_CMD_FLOGI:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV FLOGI: did:x%x/ste:x%x flg:x%x",
+ "RCV FLOGI: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvFLOGI++;
+
+ /* If the driver believes fabric discovery is done and is ready,
+ * bounce the link. There is some descrepancy.
+ */
+ if (vport->port_state >= LPFC_LOCAL_CFG_LINK &&
+ test_bit(FC_PT2PT, &vport->fc_flag) &&
+ vport->rcv_flogi_cnt >= 1) {
+ rjt_err = LSRJT_LOGICAL_BSY;
+ rjt_exp = LSEXP_NOTHING_MORE;
+ init_link++;
+ goto lsrjt;
+ }
+
lpfc_els_rcv_flogi(vport, elsiocb, ndlp);
+ /* retain node if our response is deferred */
+ if (phba->defer_flogi_acc.flag)
+ break;
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_LOGO:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV LOGO: did:x%x/ste:x%x flg:x%x",
+ "RCV LOGO: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvLOGO++;
lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_NOTHING_MORE;
break;
}
lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_LOGO);
+ if (newnode)
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_PRLO:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV PRLO: did:x%x/ste:x%x flg:x%x",
+ "RCV PRLO: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvPRLO++;
lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_NOTHING_MORE;
break;
}
lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_PRLO);
break;
+ case ELS_CMD_LCB:
+ phba->fc_stat.elsRcvLCB++;
+ lpfc_els_rcv_lcb(vport, elsiocb, ndlp);
+ break;
+ case ELS_CMD_RDP:
+ phba->fc_stat.elsRcvRDP++;
+ lpfc_els_rcv_rdp(vport, elsiocb, ndlp);
+ break;
case ELS_CMD_RSCN:
phba->fc_stat.elsRcvRSCN++;
lpfc_els_rcv_rscn(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_ADISC:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV ADISC: did:x%x/ste:x%x flg:x%x",
+ "RCV ADISC: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
lpfc_send_els_event(vport, ndlp, payload);
phba->fc_stat.elsRcvADISC++;
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_NOTHING_MORE;
break;
}
lpfc_disc_state_machine(vport, ndlp, elsiocb,
@@ -6403,12 +10615,13 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
break;
case ELS_CMD_PDISC:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV PDISC: did:x%x/ste:x%x flg:x%x",
+ "RCV PDISC: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvPDISC++;
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_NOTHING_MORE;
break;
}
lpfc_disc_state_machine(vport, ndlp, elsiocb,
@@ -6416,7 +10629,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
break;
case ELS_CMD_FARPR:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV FARPR: did:x%x/ste:x%x flg:x%x",
+ "RCV FARPR: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvFARPR++;
@@ -6424,7 +10637,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
break;
case ELS_CMD_FARP:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV FARP: did:x%x/ste:x%x flg:x%x",
+ "RCV FARP: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvFARP++;
@@ -6432,102 +10645,135 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
break;
case ELS_CMD_FAN:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV FAN: did:x%x/ste:x%x flg:x%x",
+ "RCV FAN: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvFAN++;
lpfc_els_rcv_fan(vport, elsiocb, ndlp);
break;
case ELS_CMD_PRLI:
+ case ELS_CMD_NVMEPRLI:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV PRLI: did:x%x/ste:x%x flg:x%x",
+ "RCV PRLI: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvPRLI++;
- if (vport->port_state < LPFC_DISC_AUTH) {
+ if ((vport->port_state < LPFC_DISC_AUTH) &&
+ test_bit(FC_FABRIC, &vport->fc_flag)) {
rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_NOTHING_MORE;
break;
}
lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_PRLI);
break;
case ELS_CMD_LIRR:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV LIRR: did:x%x/ste:x%x flg:x%x",
+ "RCV LIRR: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvLIRR++;
lpfc_els_rcv_lirr(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_RLS:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RLS: did:x%x/ste:x%x flg:x%x",
+ "RCV RLS: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvRLS++;
lpfc_els_rcv_rls(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
- break;
- case ELS_CMD_RPS:
- lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RPS: did:x%x/ste:x%x flg:x%x",
- did, vport->port_state, ndlp->nlp_flag);
-
- phba->fc_stat.elsRcvRPS++;
- lpfc_els_rcv_rps(vport, elsiocb, ndlp);
- if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_RPL:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RPL: did:x%x/ste:x%x flg:x%x",
+ "RCV RPL: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvRPL++;
lpfc_els_rcv_rpl(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_RNID:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RNID: did:x%x/ste:x%x flg:x%x",
+ "RCV RNID: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvRNID++;
lpfc_els_rcv_rnid(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_RTV:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RTV: did:x%x/ste:x%x flg:x%x",
+ "RCV RTV: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvRTV++;
lpfc_els_rcv_rtv(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_RRQ:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV RRQ: did:x%x/ste:x%x flg:x%x",
+ "RCV RRQ: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvRRQ++;
lpfc_els_rcv_rrq(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
case ELS_CMD_ECHO:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
- "RCV ECHO: did:x%x/ste:x%x flg:x%x",
+ "RCV ECHO: did:x%x/ste:x%x flg:x%lx",
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvECHO++;
lpfc_els_rcv_echo(vport, elsiocb, ndlp);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
+ break;
+ case ELS_CMD_REC:
+ /* receive this due to exchange closed */
+ rjt_err = LSRJT_UNABLE_TPC;
+ rjt_exp = LSEXP_INVALID_OX_RX;
+ break;
+ case ELS_CMD_FPIN:
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
+ "RCV FPIN: did:x%x/ste:x%x "
+ "flg:x%lx",
+ did, vport->port_state, ndlp->nlp_flag);
+
+ lpfc_els_rcv_fpin(vport, (struct fc_els_fpin *)payload,
+ payload_len);
+
+ /* There are no replies, so no rjt codes */
+ break;
+ case ELS_CMD_EDC:
+ lpfc_els_rcv_edc(vport, elsiocb, ndlp);
+ break;
+ case ELS_CMD_RDF:
+ phba->fc_stat.elsRcvRDF++;
+ /* Accept RDF only from fabric controller */
+ if (did != Fabric_Cntl_DID) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
+ "1115 Received RDF from invalid DID "
+ "x%x\n", did);
+ rjt_err = LSRJT_PROTOCOL_ERR;
+ rjt_exp = LSEXP_NOTHING_MORE;
+ goto lsrjt;
+ }
+
+ lpfc_els_rcv_rdf(vport, elsiocb, ndlp);
break;
default:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
@@ -6536,86 +10782,66 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/* Unsupported ELS command, reject */
rjt_err = LSRJT_CMD_UNSUPPORTED;
+ rjt_exp = LSEXP_NOTHING_MORE;
/* Unknown ELS command <elsCmd> received from NPORT <did> */
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"0115 Unknown ELS command x%x "
"received from NPORT x%x\n", cmd, did);
if (newnode)
- lpfc_nlp_put(ndlp);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
break;
}
+lsrjt:
/* check if need to LS_RJT received ELS cmd */
if (rjt_err) {
memset(&stat, 0, sizeof(stat));
stat.un.b.lsRjtRsnCode = rjt_err;
- stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ stat.un.b.lsRjtRsnCodeExp = rjt_exp;
lpfc_els_rsp_reject(vport, stat.un.lsRjtError, elsiocb, ndlp,
- NULL);
+ NULL);
+ /* Remove the reference from above for new nodes. */
+ if (newnode)
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
+ }
+
+ /* Release the reference on this elsiocb, not the ndlp. */
+ lpfc_nlp_put(elsiocb->ndlp);
+ elsiocb->ndlp = NULL;
+
+ /* Special case. Driver received an unsolicited command that
+ * unsupportable given the driver's current state. Reset the
+ * link and start over.
+ */
+ if (init_link) {
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return;
+ lpfc_linkdown(phba);
+ lpfc_init_link(phba, mbox,
+ phba->cfg_topology,
+ phba->cfg_link_speed);
+ mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->vport = vport;
+ if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) ==
+ MBX_NOT_FINISHED)
+ mempool_free(mbox, phba->mbox_mem_pool);
}
- lpfc_nlp_put(elsiocb->context1);
- elsiocb->context1 = NULL;
return;
dropit:
- if (vport && !(vport->load_flag & FC_UNLOADING))
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ if (vport && !test_bit(FC_UNLOADING, &vport->load_flag))
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0111 Dropping received ELS cmd "
- "Data: x%x x%x x%x\n",
- icmd->ulpStatus, icmd->un.ulpWord[4], icmd->ulpTimeout);
- phba->fc_stat.elsRcvDrop++;
-}
+ "Data: x%x x%x x%x x%x\n",
+ cmd, status, get_job_word4(phba, elsiocb), did);
-/**
- * lpfc_find_vport_by_vpid - Find a vport on a HBA through vport identifier
- * @phba: pointer to lpfc hba data structure.
- * @vpi: host virtual N_Port identifier.
- *
- * This routine finds a vport on a HBA (referred by @phba) through a
- * @vpi. The function walks the HBA's vport list and returns the address
- * of the vport with the matching @vpi.
- *
- * Return code
- * NULL - No vport with the matching @vpi found
- * Otherwise - Address to the vport with the matching @vpi.
- **/
-struct lpfc_vport *
-lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi)
-{
- struct lpfc_vport *vport;
- unsigned long flags;
- int i;
-
- /* The physical ports are always vpi 0 - translate is unnecessary. */
- if (vpi > 0) {
- /*
- * Translate the physical vpi to the logical vpi. The
- * vport stores the logical vpi.
- */
- for (i = 0; i < phba->max_vpi; i++) {
- if (vpi == phba->vpi_ids[i])
- break;
- }
-
- if (i >= phba->max_vpi) {
- lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
- "2936 Could not find Vport mapped "
- "to vpi %d\n", vpi);
- return NULL;
- }
- }
-
- spin_lock_irqsave(&phba->hbalock, flags);
- list_for_each_entry(vport, &phba->port_list, listentry) {
- if (vport->vpi == vpi) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return vport;
- }
- }
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return NULL;
+ phba->fc_stat.elsRcvDrop++;
}
/**
@@ -6634,76 +10860,116 @@ void
lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *elsiocb)
{
- struct lpfc_vport *vport = phba->pport;
- IOCB_t *icmd = &elsiocb->iocb;
+ struct lpfc_vport *vport = elsiocb->vport;
+ u32 ulp_command, status, parameter, bde_count = 0;
+ IOCB_t *icmd;
+ struct lpfc_wcqe_complete *wcqe_cmpl = NULL;
+ struct lpfc_dmabuf *bdeBuf1 = elsiocb->cmd_dmabuf;
+ struct lpfc_dmabuf *bdeBuf2 = elsiocb->bpl_dmabuf;
dma_addr_t paddr;
- struct lpfc_dmabuf *bdeBuf1 = elsiocb->context2;
- struct lpfc_dmabuf *bdeBuf2 = elsiocb->context3;
- elsiocb->context1 = NULL;
- elsiocb->context2 = NULL;
- elsiocb->context3 = NULL;
+ elsiocb->cmd_dmabuf = NULL;
+ elsiocb->rsp_dmabuf = NULL;
+ elsiocb->bpl_dmabuf = NULL;
- if (icmd->ulpStatus == IOSTAT_NEED_BUFFER) {
+ wcqe_cmpl = &elsiocb->wcqe_cmpl;
+ ulp_command = get_job_cmnd(phba, elsiocb);
+ status = get_job_ulpstatus(phba, elsiocb);
+ parameter = get_job_word4(phba, elsiocb);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ bde_count = wcqe_cmpl->word3;
+ else
+ bde_count = elsiocb->iocb.ulpBdeCount;
+
+ if (status == IOSTAT_NEED_BUFFER) {
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
- } else if (icmd->ulpStatus == IOSTAT_LOCAL_REJECT &&
- (icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING) {
+ } else if (status == IOSTAT_LOCAL_REJECT &&
+ (parameter & IOERR_PARAM_MASK) ==
+ IOERR_RCV_BUFFER_WAITING) {
phba->fc_stat.NoRcvBuf++;
/* Not enough posted buffers; Try posting more buffers */
if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
- lpfc_post_buffer(phba, pring, 0);
+ lpfc_sli3_post_buffer(phba, pring, 0);
return;
}
- if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
- (icmd->ulpCommand == CMD_IOCB_RCV_ELS64_CX ||
- icmd->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
- if (icmd->unsli3.rcvsli3.vpi == 0xffff)
- vport = phba->pport;
- else
- vport = lpfc_find_vport_by_vpid(phba,
+ if (phba->sli_rev == LPFC_SLI_REV3) {
+ icmd = &elsiocb->iocb;
+ if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
+ (ulp_command == CMD_IOCB_RCV_ELS64_CX ||
+ ulp_command == CMD_IOCB_RCV_SEQ64_CX)) {
+ if (icmd->unsli3.rcvsli3.vpi == 0xffff)
+ vport = phba->pport;
+ else
+ vport = lpfc_find_vport_by_vpid(phba,
icmd->unsli3.rcvsli3.vpi);
+ }
}
/* If there are no BDEs associated
* with this IOCB, there is nothing to do.
*/
- if (icmd->ulpBdeCount == 0)
+ if (bde_count == 0)
return;
- /* type of ELS cmd is first 32bit word
- * in packet
- */
+ /* Account for SLI2 or SLI3 and later unsolicited buffering */
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
- elsiocb->context2 = bdeBuf1;
+ elsiocb->cmd_dmabuf = bdeBuf1;
+ if (bde_count == 2)
+ elsiocb->bpl_dmabuf = bdeBuf2;
} else {
+ icmd = &elsiocb->iocb;
paddr = getPaddr(icmd->un.cont64[0].addrHigh,
icmd->un.cont64[0].addrLow);
- elsiocb->context2 = lpfc_sli_ringpostbuf_get(phba, pring,
- paddr);
+ elsiocb->cmd_dmabuf = lpfc_sli_ringpostbuf_get(phba, pring,
+ paddr);
+ if (bde_count == 2) {
+ paddr = getPaddr(icmd->un.cont64[1].addrHigh,
+ icmd->un.cont64[1].addrLow);
+ elsiocb->bpl_dmabuf = lpfc_sli_ringpostbuf_get(phba,
+ pring,
+ paddr);
+ }
}
lpfc_els_unsol_buffer(phba, pring, vport, elsiocb);
/*
* The different unsolicited event handlers would tell us
- * if they are done with "mp" by setting context2 to NULL.
+ * if they are done with "mp" by setting cmd_dmabuf/bpl_dmabuf to NULL.
*/
- if (elsiocb->context2) {
- lpfc_in_buf_free(phba, (struct lpfc_dmabuf *)elsiocb->context2);
- elsiocb->context2 = NULL;
+ if (elsiocb->cmd_dmabuf) {
+ lpfc_in_buf_free(phba, elsiocb->cmd_dmabuf);
+ elsiocb->cmd_dmabuf = NULL;
}
- /* RCV_ELS64_CX provide for 2 BDEs - process 2nd if included */
- if ((phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) &&
- icmd->ulpBdeCount == 2) {
- elsiocb->context2 = bdeBuf2;
- lpfc_els_unsol_buffer(phba, pring, vport, elsiocb);
- /* free mp if we are done with it */
- if (elsiocb->context2) {
- lpfc_in_buf_free(phba, elsiocb->context2);
- elsiocb->context2 = NULL;
+ if (elsiocb->bpl_dmabuf) {
+ lpfc_in_buf_free(phba, elsiocb->bpl_dmabuf);
+ elsiocb->bpl_dmabuf = NULL;
+ }
+
+}
+
+static void
+lpfc_start_fdmi(struct lpfc_vport *vport)
+{
+ struct lpfc_nodelist *ndlp;
+
+ /* If this is the first time, allocate an ndlp and initialize
+ * it. Otherwise, make sure the node is enabled and then do the
+ * login.
+ */
+ ndlp = lpfc_findnode_did(vport, FDMI_DID);
+ if (!ndlp) {
+ ndlp = lpfc_nlp_init(vport, FDMI_DID);
+ if (ndlp) {
+ ndlp->nlp_type |= NLP_FABRIC;
+ } else {
+ return;
}
}
+
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
+ lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
}
/**
@@ -6722,87 +10988,52 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
void
lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
{
- struct lpfc_nodelist *ndlp, *ndlp_fdmi;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_nodelist *ndlp;
/*
* If lpfc_delay_discovery parameter is set and the clean address
* bit is cleared and fc fabric parameters chenged, delay FC NPort
* discovery.
*/
- spin_lock_irq(shost->host_lock);
- if (vport->fc_flag & FC_DISC_DELAYED) {
- spin_unlock_irq(shost->host_lock);
+ if (test_bit(FC_DISC_DELAYED, &vport->fc_flag)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "3334 Delay fc port discovery for %d secs\n",
+ phba->fc_ratov);
mod_timer(&vport->delayed_disc_tmo,
- jiffies + HZ * phba->fc_ratov);
+ jiffies + secs_to_jiffies(phba->fc_ratov));
return;
}
- spin_unlock_irq(shost->host_lock);
ndlp = lpfc_findnode_did(vport, NameServer_DID);
if (!ndlp) {
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, NameServer_DID);
if (!ndlp) {
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
lpfc_disc_start(vport);
return;
}
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0251 NameServer login: no memory\n");
return;
}
- lpfc_nlp_init(vport, ndlp, NameServer_DID);
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
- if (!ndlp) {
- if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
- lpfc_disc_start(vport);
- return;
- }
- lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0348 NameServer login: node freed\n");
- return;
- }
}
+
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
if (lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0)) {
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0252 Cannot issue NameServer login\n");
return;
}
- if (vport->cfg_fdmi_on) {
- /* If this is the first time, allocate an ndlp and initialize
- * it. Otherwise, make sure the node is enabled and then do the
- * login.
- */
- ndlp_fdmi = lpfc_findnode_did(vport, FDMI_DID);
- if (!ndlp_fdmi) {
- ndlp_fdmi = mempool_alloc(phba->nlp_mem_pool,
- GFP_KERNEL);
- if (ndlp_fdmi) {
- lpfc_nlp_init(vport, ndlp_fdmi, FDMI_DID);
- ndlp_fdmi->nlp_type |= NLP_FABRIC;
- } else
- return;
- }
- if (!NLP_CHK_NODE_ACT(ndlp_fdmi))
- ndlp_fdmi = lpfc_enable_node(vport,
- ndlp_fdmi,
- NLP_STE_NPR_NODE);
-
- if (ndlp_fdmi) {
- lpfc_nlp_set_state(vport, ndlp_fdmi,
- NLP_STE_PLOGI_ISSUE);
- lpfc_issue_els_plogi(vport, ndlp_fdmi->nlp_DID, 0);
- }
- }
+ if ((phba->cfg_enable_SmartSAN ||
+ phba->cfg_fdmi_on == LPFC_FDMI_SUPPORT) &&
+ test_bit(FC_ALLOW_FDMI, &vport->load_flag))
+ lpfc_start_fdmi(vport);
}
/**
@@ -6821,17 +11052,15 @@ static void
lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_nodelist *ndlp = pmb->ctx_ndlp;
MAILBOX_t *mb = &pmb->u.mb;
int rc;
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
if (mb->mbxStatus) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0915 Register VPI failed : Status: x%x"
" upd bit: x%x \n", mb->mbxStatus,
mb->un.varRegVpi.upd);
@@ -6845,44 +11074,45 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
case 0x9602: /* Link event since CLEAR_LA */
/* giving up on vport registration */
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_FABRIC, &vport->fc_flag);
+ clear_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
lpfc_can_disctmo(vport);
break;
/* If reg_vpi fail with invalid VPI status, re-init VPI */
case 0x20:
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
lpfc_init_vpi(phba, pmb, vport->vpi);
pmb->vport = vport;
pmb->mbox_cmpl = lpfc_init_vpi_cmpl;
rc = lpfc_sli_issue_mbox(phba, pmb,
MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_vlog(vport,
- KERN_ERR, LOG_MBOX,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"2732 Failed to issue INIT_VPI"
" mailbox command\n");
} else {
lpfc_nlp_put(ndlp);
return;
}
-
+ fallthrough;
default:
/* Try to recover from this error */
if (phba->sli_rev == LPFC_SLI_REV4)
lpfc_sli4_unreg_all_rpis(vport);
lpfc_mbx_unreg_vpi(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(shost->host_lock);
- if (vport->port_type == LPFC_PHYSICAL_PORT
- && !(vport->fc_flag & FC_LOGO_RCVD_DID_CHNG))
- lpfc_issue_init_vfi(vport);
- else
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
+ if (mb->mbxStatus == MBX_NOT_FINISHED)
+ break;
+ if ((vport->port_type == LPFC_PHYSICAL_PORT) &&
+ !test_bit(FC_LOGO_RCVD_DID_CHNG, &vport->fc_flag)) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_issue_init_vfi(vport);
+ else
+ lpfc_initial_flogi(vport);
+ } else {
lpfc_initial_fdisc(vport);
+ }
break;
}
} else {
@@ -6901,8 +11131,9 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
}
- } else
+ } else {
lpfc_do_scr_ns_plogi(phba, vport);
+ }
}
mbox_err_exit:
/* Now, we decrement the ndlp reference count held for this
@@ -6911,6 +11142,14 @@ mbox_err_exit:
lpfc_nlp_put(ndlp);
mempool_free(pmb, phba->mbox_mem_pool);
+
+ /* reinitialize the VMID datastructure before returning.
+ * this is specifically for vport
+ */
+ if (lpfc_is_vmid_enabled(phba))
+ lpfc_reinit_vmid(vport);
+ vport->vmid_flag = vport->phba->pport->vmid_flag;
+
return;
}
@@ -6927,14 +11166,18 @@ void
lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
LPFC_MBOXQ_t *mbox;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mbox) {
lpfc_reg_vpi(vport, mbox);
mbox->vport = vport;
- mbox->context2 = lpfc_nlp_get(ndlp);
+ mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!mbox->ctx_ndlp) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ goto mbox_err_exit;
+ }
+
mbox->mbox_cmpl = lpfc_cmpl_reg_new_vport;
if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT)
== MBX_NOT_FINISHED) {
@@ -6944,12 +11187,12 @@ lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport,
lpfc_nlp_put(ndlp);
mempool_free(mbox, phba->mbox_mem_pool);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0253 Register VPI: Can't send mbox\n");
goto mbox_err_exit;
}
} else {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0254 Register VPI: no memory\n");
goto mbox_err_exit;
}
@@ -6957,9 +11200,7 @@ lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport,
mbox_err_exit:
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
return;
}
@@ -7007,7 +11248,6 @@ void
lpfc_retry_pport_discovery(struct lpfc_hba *phba)
{
struct lpfc_nodelist *ndlp;
- struct Scsi_Host *shost;
/* Cancel the all vports retry delay retry timers */
lpfc_cancel_all_vport_retry_delay_timer(phba);
@@ -7017,11 +11257,8 @@ lpfc_retry_pport_discovery(struct lpfc_hba *phba)
if (!ndlp)
return;
- shost = lpfc_shost_from_vport(phba->pport);
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + secs_to_jiffies(1));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_FLOGI;
phba->pport->port_state = LPFC_FLOGI;
return;
@@ -7041,9 +11278,11 @@ lpfc_fabric_login_reqd(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
- if ((rspiocb->iocb.ulpStatus != IOSTAT_FABRIC_RJT) ||
- (rspiocb->iocb.un.ulpWord[4] != RJT_LOGIN_REQUIRED))
+ if (ulp_status != IOSTAT_FABRIC_RJT ||
+ ulp_word4 != RJT_LOGIN_REQUIRED)
return 0;
else
return 1;
@@ -7074,19 +11313,21 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
struct lpfc_vport *vport = cmdiocb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
struct lpfc_nodelist *np;
struct lpfc_nodelist *next_np;
- IOCB_t *irsp = &rspiocb->iocb;
struct lpfc_iocbq *piocb;
- struct lpfc_dmabuf *pcmd = cmdiocb->context2, *prsp;
+ struct lpfc_dmabuf *pcmd = cmdiocb->cmd_dmabuf, *prsp;
struct serv_parm *sp;
uint8_t fabric_param_changed;
+ u32 ulp_status, ulp_word4;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0123 FDISC completes. x%x/x%x prevDID: x%x\n",
- irsp->ulpStatus, irsp->un.ulpWord[4],
+ ulp_status, ulp_word4,
vport->fc_prevDID);
/* Since all FDISCs are being single threaded, we
* must reset the discovery timer for ALL vports
@@ -7098,9 +11339,9 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"FDISC cmpl: status:x%x/x%x prevdid:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_prevDID);
+ ulp_status, ulp_word4, vport->fc_prevDID);
- if (irsp->ulpStatus) {
+ if (ulp_status) {
if (lpfc_fabric_login_reqd(phba, cmdiocb, rspiocb)) {
lpfc_retry_pport_discovery(phba);
@@ -7110,23 +11351,34 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/* Check for retry */
if (lpfc_els_retry(phba, cmdiocb, rspiocb))
goto out;
- /* FDISC failed */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0126 FDISC failed. (%d/%d)\n",
- irsp->ulpStatus, irsp->un.ulpWord[4]);
+ /* Warn FDISC status */
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS,
+ "0126 FDISC cmpl status: x%x/x%x)\n",
+ ulp_status, ulp_word4);
+
+ /* drop initial reference */
+ if (!test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag))
+ lpfc_nlp_put(ndlp);
+
goto fdisc_failed;
}
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_VPORT_CVL_RCVD;
- vport->fc_flag &= ~FC_VPORT_LOGO_RCVD;
- vport->fc_flag |= FC_FABRIC;
+
+ lpfc_check_nlp_post_devloss(vport, ndlp);
+
+ clear_bit(FC_VPORT_CVL_RCVD, &vport->fc_flag);
+ clear_bit(FC_VPORT_LOGO_RCVD, &vport->fc_flag);
+ set_bit(FC_FABRIC, &vport->fc_flag);
if (vport->phba->fc_topology == LPFC_TOPOLOGY_LOOP)
- vport->fc_flag |= FC_PUBLIC_LOOP;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
- vport->fc_myDID = irsp->un.ulpWord[4] & Mask_DID;
+ vport->fc_myDID = ulp_word4 & Mask_DID;
lpfc_vport_set_state(vport, FC_VPORT_ACTIVE);
prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
+ if (!prsp)
+ goto out;
+ if (!lpfc_is_els_acc_rsp(prsp))
+ goto out;
+
sp = prsp->virt + sizeof(uint32_t);
fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp);
memcpy(&vport->fabric_portname, &sp->portName,
@@ -7134,20 +11386,17 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
memcpy(&vport->fabric_nodename, &sp->nodeName,
sizeof(struct lpfc_name));
if (fabric_param_changed &&
- !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
+ !test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag)) {
/* If our NportID changed, we need to ensure all
* remaining NPORTs get unreg_login'ed so we can
* issue unreg_vpi.
*/
list_for_each_entry_safe(np, next_np,
&vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp) ||
- (np->nlp_state != NLP_STE_NPR_NODE) ||
- !(np->nlp_flag & NLP_NPR_ADISC))
+ if ((np->nlp_state != NLP_STE_NPR_NODE) ||
+ !test_bit(NLP_NPR_ADISC, &np->nlp_flag))
continue;
- spin_lock_irq(shost->host_lock);
- np->nlp_flag &= ~NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NPR_ADISC, &np->nlp_flag);
lpfc_unreg_rpi(vport, np);
}
lpfc_cleanup_pending_mbox(vport);
@@ -7156,37 +11405,44 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_sli4_unreg_all_rpis(vport);
lpfc_mbx_unreg_vpi(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
if (phba->sli_rev == LPFC_SLI_REV4)
- vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ set_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag);
else
- vport->fc_flag |= FC_LOGO_RCVD_DID_CHNG;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_LOGO_RCVD_DID_CHNG, &vport->fc_flag);
} else if ((phba->sli_rev == LPFC_SLI_REV4) &&
- !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
+ !test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag)) {
/*
* Driver needs to re-reg VPI in order for f/w
* to update the MAC address.
*/
lpfc_register_new_vport(phba, vport, ndlp);
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
goto out;
}
- if (vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI)
+ if (test_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag))
lpfc_issue_init_vpi(vport);
- else if (vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)
+ else if (test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag))
lpfc_register_new_vport(phba, vport, ndlp);
else
lpfc_do_scr_ns_plogi(phba, vport);
+
+ /* The FDISC completed successfully. Move the fabric ndlp to
+ * UNMAPPED state and register with the transport.
+ */
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
goto out;
+
fdisc_failed:
- lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ if (vport->fc_vport &&
+ (vport->fc_vport->vport_state != FC_VPORT_NO_FABRIC_RSCS))
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
/* Cancel discovery timer */
lpfc_can_disctmo(vport);
- lpfc_nlp_put(ndlp);
out:
lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
}
/**
@@ -7200,10 +11456,9 @@ out:
* routine to issue the IOCB, which makes sure only one outstanding fabric
* IOCB will be sent off HBA at any given time.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the FDISC ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the FDISC ELS command.
*
* Return code
* 0 - Successfully issued fdisc iocb command
@@ -7215,6 +11470,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_hba *phba = vport->phba;
IOCB_t *icmd;
+ union lpfc_wqe128 *wqe = NULL;
struct lpfc_iocbq *elsiocb;
struct serv_parm *sp;
uint8_t *pcmd;
@@ -7223,35 +11479,30 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int rc;
vport->port_state = LPFC_FDISC;
+ vport->fc_myDID = 0;
cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did,
ELS_CMD_FDISC);
if (!elsiocb) {
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0255 Issue FDISC: no IOCB\n");
return 1;
}
- icmd = &elsiocb->iocb;
- icmd->un.elsreq64.myID = 0;
- icmd->un.elsreq64.fl = 1;
-
- if ((phba->sli_rev == LPFC_SLI_REV4) &&
- (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
- LPFC_SLI_INTF_IF_TYPE_0)) {
- /* FDISC needs to be 1 for WQE VPI */
- elsiocb->iocb.ulpCt_h = (SLI4_CT_VPI >> 1) & 1;
- elsiocb->iocb.ulpCt_l = SLI4_CT_VPI & 1 ;
- /* Set the ulpContext to the vpi */
- elsiocb->iocb.ulpContext = phba->vpi_ids[vport->vpi];
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wqe = &elsiocb->wqe;
+ bf_set(els_req64_sid, &wqe->els_req, 0);
+ bf_set(els_req64_sp, &wqe->els_req, 1);
} else {
- /* For FDISC, Let FDISC rsp set the NPortID for this VPI */
+ icmd = &elsiocb->iocb;
+ icmd->un.elsreq64.myID = 0;
+ icmd->un.elsreq64.fl = 1;
icmd->ulpCt_h = 1;
icmd->ulpCt_l = 0;
}
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_FDISC;
pcmd += sizeof(uint32_t); /* CSP Word 1 */
memcpy(pcmd, &vport->phba->pport->fc_sparam, sizeof(struct serv_parm));
@@ -7259,10 +11510,18 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* Setup CSPs accordingly for Fabric */
sp->cmn.e_d_tov = 0;
sp->cmn.w2.r_a_tov = 0;
+ sp->cmn.virtual_fabric_support = 0;
sp->cls1.classValid = 0;
sp->cls2.seqDelivery = 1;
sp->cls3.seqDelivery = 1;
+ /* Fill out Auxiliary Parameter Data */
+ if (phba->pni) {
+ sp->aux.flags =
+ AUX_PARM_DATA_VALID | AUX_PARM_PNI_VALID;
+ sp->aux.pni = cpu_to_be64(phba->pni);
+ }
+
pcmd += sizeof(uint32_t); /* CSP Word 2 */
pcmd += sizeof(uint32_t); /* CSP Word 3 */
pcmd += sizeof(uint32_t); /* CSP Word 4 */
@@ -7271,26 +11530,36 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
pcmd += sizeof(uint32_t); /* Node Name */
pcmd += sizeof(uint32_t); /* Node Name */
memcpy(pcmd, &vport->fc_nodename, 8);
-
+ sp->cmn.valid_vendor_ver_level = 0;
+ memset(sp->un.vendorVersion, 0, sizeof(sp->un.vendorVersion));
lpfc_set_disctmo(vport);
phba->fc_stat.elsXmitFDISC++;
- elsiocb->iocb_cmpl = lpfc_cmpl_els_fdisc;
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_fdisc;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"Issue FDISC: did:x%x",
did, 0, 0);
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp)
+ goto err_out;
+
rc = lpfc_issue_fabric_iocb(phba, elsiocb);
if (rc == IOCB_ERROR) {
- lpfc_els_free_iocb(phba, elsiocb);
- lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0256 Issue FDISC: Cannot send IOCB\n");
- return 1;
+ lpfc_nlp_put(ndlp);
+ goto err_out;
}
+
lpfc_vport_set_state(vport, FC_VPORT_INITIALIZING);
return 0;
+
+ err_out:
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0256 Issue FDISC: Cannot send IOCB\n");
+ return 1;
}
/**
@@ -7314,32 +11583,53 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_vport *vport = cmdiocb->vport;
IOCB_t *irsp;
struct lpfc_nodelist *ndlp;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ u32 ulp_status, ulp_word4, did, tmo;
- ndlp = (struct lpfc_nodelist *)cmdiocb->context1;
- irsp = &rspiocb->iocb;
- lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
- "LOGO npiv cmpl: status:x%x/x%x did:x%x",
- irsp->ulpStatus, irsp->un.ulpWord[4], irsp->un.rcvels.remoteID);
+ ndlp = cmdiocb->ndlp;
- lpfc_els_free_iocb(phba, cmdiocb);
- vport->unreg_vpi_cmpl = VPORT_ERROR;
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
- /* Trigger the release of the ndlp after logo */
- lpfc_nlp_put(ndlp);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ did = get_job_els_rsp64_did(phba, cmdiocb);
+ tmo = get_wqe_tmo(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ did = get_job_els_rsp64_did(phba, rspiocb);
+ tmo = irsp->ulpTimeout;
+ }
+
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+ "LOGO npiv cmpl: status:x%x/x%x did:x%x",
+ ulp_status, ulp_word4, did);
/* NPIV LOGO completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"2928 NPIV LOGO completes to NPort x%x "
- "Data: x%x x%x x%x x%x\n",
- ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
- irsp->ulpTimeout, vport->num_disc_nodes);
+ "Data: x%x x%x x%x x%x x%x x%lx x%x\n",
+ ndlp->nlp_DID, ulp_status, ulp_word4,
+ tmo, vport->num_disc_nodes,
+ kref_read(&ndlp->kref), ndlp->nlp_flag,
+ ndlp->fc4_xpt_flags);
- if (irsp->ulpStatus == IOSTAT_SUCCESS) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_FABRIC;
- spin_unlock_irq(shost->host_lock);
+ if (ulp_status == IOSTAT_SUCCESS) {
+ clear_bit(FC_NDISC_ACTIVE, &vport->fc_flag);
+ clear_bit(FC_FABRIC, &vport->fc_flag);
+ lpfc_can_disctmo(vport);
}
+
+ if (test_bit(NLP_WAIT_FOR_LOGO, &ndlp->save_flags)) {
+ /* Wake up lpfc_vport_delete if waiting...*/
+ if (ndlp->logo_waitq)
+ wake_up(ndlp->logo_waitq);
+ clear_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag);
+ clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag);
+ clear_bit(NLP_WAIT_FOR_LOGO, &ndlp->save_flags);
+ }
+
+ /* Safe to release resources now. */
+ lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
}
/**
@@ -7349,10 +11639,9 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
*
* This routine issues a LOGO ELS command to an @ndlp off a @vport.
*
- * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
- * will be incremented by 1 for holding the ndlp and the reference to ndlp
- * will be stored into the context1 field of the IOCB for the completion
- * callback function to the LOGO ELS command.
+ * Note that the ndlp reference count will be incremented by 1 for holding the
+ * ndlp and the reference to ndlp will be stored into the ndlp field of
+ * the IOCB for the completion callback function to the LOGO ELS command.
*
* Return codes
* 0 - Successfully issued logo off the @vport
@@ -7361,9 +11650,8 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
int
lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ int rc = 0;
struct lpfc_hba *phba = vport->phba;
- IOCB_t *icmd;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
uint16_t cmdsize;
@@ -7374,8 +11662,7 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
if (!elsiocb)
return 1;
- icmd = &elsiocb->iocb;
- pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt;
*((uint32_t *) (pcmd)) = ELS_CMD_LOGO;
pcmd += sizeof(uint32_t);
@@ -7385,27 +11672,33 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
memcpy(pcmd, &vport->fc_portname, sizeof(struct lpfc_name));
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
- "Issue LOGO npiv did:x%x flg:x%x",
+ "Issue LOGO npiv did:x%x flg:x%lx",
ndlp->nlp_DID, ndlp->nlp_flag, 0);
- elsiocb->iocb_cmpl = lpfc_cmpl_els_npiv_logo;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_LOGO_SND;
- spin_unlock_irq(shost->host_lock);
- if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
- IOCB_ERROR) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_LOGO_SND;
- spin_unlock_irq(shost->host_lock);
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_npiv_logo;
+ set_bit(NLP_LOGO_SND, &ndlp->nlp_flag);
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
lpfc_els_free_iocb(phba, elsiocb);
- return 1;
+ goto err;
+ }
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ goto err;
}
return 0;
+
+err:
+ clear_bit(NLP_LOGO_SND, &ndlp->nlp_flag);
+ return 1;
}
/**
* lpfc_fabric_block_timeout - Handler function to the fabric block timer
- * @ptr: holder for the timer function associated data.
+ * @t: timer context used to obtain the lpfc hba.
*
* This routine is invoked by the fabric iocb block timer after
* timeout. It posts the fabric iocb block timeout event by setting the
@@ -7415,9 +11708,10 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
* posted event WORKER_FABRIC_BLOCK_TMO.
**/
void
-lpfc_fabric_block_timeout(unsigned long ptr)
+lpfc_fabric_block_timeout(struct timer_list *t)
{
- struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
+ struct lpfc_hba *phba = timer_container_of(phba, t,
+ fabric_block_timer);
unsigned long iflags;
uint32_t tmo_posted;
@@ -7448,7 +11742,6 @@ lpfc_resume_fabric_iocbs(struct lpfc_hba *phba)
struct lpfc_iocbq *iocb;
unsigned long iflags;
int ret;
- IOCB_t *cmd;
repeat:
iocb = NULL;
@@ -7463,31 +11756,28 @@ repeat:
}
spin_unlock_irqrestore(&phba->hbalock, iflags);
if (iocb) {
- iocb->fabric_iocb_cmpl = iocb->iocb_cmpl;
- iocb->iocb_cmpl = lpfc_cmpl_fabric_iocb;
- iocb->iocb_flag |= LPFC_IO_FABRIC;
+ iocb->fabric_cmd_cmpl = iocb->cmd_cmpl;
+ iocb->cmd_cmpl = lpfc_cmpl_fabric_iocb;
+ iocb->cmd_flag |= LPFC_IO_FABRIC;
lpfc_debugfs_disc_trc(iocb->vport, LPFC_DISC_TRC_ELS_CMD,
- "Fabric sched1: ste:x%x",
- iocb->vport->port_state, 0, 0);
+ "Fabric sched1: ste:x%x",
+ iocb->vport->port_state, 0, 0);
ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocb, 0);
if (ret == IOCB_ERROR) {
- iocb->iocb_cmpl = iocb->fabric_iocb_cmpl;
- iocb->fabric_iocb_cmpl = NULL;
- iocb->iocb_flag &= ~LPFC_IO_FABRIC;
- cmd = &iocb->iocb;
- cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
- cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
- iocb->iocb_cmpl(phba, iocb, iocb);
+ iocb->cmd_cmpl = iocb->fabric_cmd_cmpl;
+ iocb->fabric_cmd_cmpl = NULL;
+ iocb->cmd_flag &= ~LPFC_IO_FABRIC;
+ set_job_ulpstatus(iocb, IOSTAT_LOCAL_REJECT);
+ iocb->wcqe_cmpl.parameter = IOERR_SLI_ABORTED;
+ iocb->cmd_cmpl(phba, iocb, iocb);
atomic_dec(&phba->fabric_iocb_count);
goto repeat;
}
}
-
- return;
}
/**
@@ -7525,7 +11815,8 @@ lpfc_block_fabric_iocbs(struct lpfc_hba *phba)
blocked = test_and_set_bit(FABRIC_COMANDS_BLOCKED, &phba->bit_flags);
/* Start a timer to unblock fabric iocbs after 100ms */
if (!blocked)
- mod_timer(&phba->fabric_block_timer, jiffies + HZ/10 );
+ mod_timer(&phba->fabric_block_timer,
+ jiffies + msecs_to_jiffies(100));
return;
}
@@ -7537,27 +11828,27 @@ lpfc_block_fabric_iocbs(struct lpfc_hba *phba)
* @rspiocb: pointer to lpfc response iocb data structure.
*
* This routine is the callback function that is put to the fabric iocb's
- * callback function pointer (iocb->iocb_cmpl). The original iocb's callback
- * function pointer has been stored in iocb->fabric_iocb_cmpl. This callback
+ * callback function pointer (iocb->cmd_cmpl). The original iocb's callback
+ * function pointer has been stored in iocb->fabric_cmd_cmpl. This callback
* function first restores and invokes the original iocb's callback function
* and then invokes the lpfc_resume_fabric_iocbs() routine to issue the next
* fabric bound iocb from the driver internal fabric iocb list onto the wire.
**/
static void
lpfc_cmpl_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
- struct lpfc_iocbq *rspiocb)
+ struct lpfc_iocbq *rspiocb)
{
struct ls_rjt stat;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
- if ((cmdiocb->iocb_flag & LPFC_IO_FABRIC) != LPFC_IO_FABRIC)
- BUG();
+ WARN_ON((cmdiocb->cmd_flag & LPFC_IO_FABRIC) != LPFC_IO_FABRIC);
- switch (rspiocb->iocb.ulpStatus) {
+ switch (ulp_status) {
case IOSTAT_NPORT_RJT:
case IOSTAT_FABRIC_RJT:
- if (rspiocb->iocb.un.ulpWord[4] & RJT_UNAVAIL_TEMP) {
+ if (ulp_word4 & RJT_UNAVAIL_TEMP)
lpfc_block_fabric_iocbs(phba);
- }
break;
case IOSTAT_NPORT_BSY:
@@ -7566,21 +11857,20 @@ lpfc_cmpl_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
break;
case IOSTAT_LS_RJT:
- stat.un.lsRjtError =
- be32_to_cpu(rspiocb->iocb.un.ulpWord[4]);
+ stat.un.ls_rjt_error_be =
+ cpu_to_be32(ulp_word4);
if ((stat.un.b.lsRjtRsnCode == LSRJT_UNABLE_TPC) ||
(stat.un.b.lsRjtRsnCode == LSRJT_LOGICAL_BSY))
lpfc_block_fabric_iocbs(phba);
break;
}
- if (atomic_read(&phba->fabric_iocb_count) == 0)
- BUG();
+ BUG_ON(atomic_read(&phba->fabric_iocb_count) == 0);
- cmdiocb->iocb_cmpl = cmdiocb->fabric_iocb_cmpl;
- cmdiocb->fabric_iocb_cmpl = NULL;
- cmdiocb->iocb_flag &= ~LPFC_IO_FABRIC;
- cmdiocb->iocb_cmpl(phba, cmdiocb, rspiocb);
+ cmdiocb->cmd_cmpl = cmdiocb->fabric_cmd_cmpl;
+ cmdiocb->fabric_cmd_cmpl = NULL;
+ cmdiocb->cmd_flag &= ~LPFC_IO_FABRIC;
+ cmdiocb->cmd_cmpl(phba, cmdiocb, rspiocb);
atomic_dec(&phba->fabric_iocb_count);
if (!test_bit(FABRIC_COMANDS_BLOCKED, &phba->bit_flags)) {
@@ -7620,8 +11910,7 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
int ready;
int ret;
- if (atomic_read(&phba->fabric_iocb_count) > 1)
- BUG();
+ BUG_ON(atomic_read(&phba->fabric_iocb_count) > 1);
spin_lock_irqsave(&phba->hbalock, iflags);
ready = atomic_read(&phba->fabric_iocb_count) == 0 &&
@@ -7632,20 +11921,20 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
atomic_inc(&phba->fabric_iocb_count);
spin_unlock_irqrestore(&phba->hbalock, iflags);
if (ready) {
- iocb->fabric_iocb_cmpl = iocb->iocb_cmpl;
- iocb->iocb_cmpl = lpfc_cmpl_fabric_iocb;
- iocb->iocb_flag |= LPFC_IO_FABRIC;
+ iocb->fabric_cmd_cmpl = iocb->cmd_cmpl;
+ iocb->cmd_cmpl = lpfc_cmpl_fabric_iocb;
+ iocb->cmd_flag |= LPFC_IO_FABRIC;
lpfc_debugfs_disc_trc(iocb->vport, LPFC_DISC_TRC_ELS_CMD,
- "Fabric sched2: ste:x%x",
- iocb->vport->port_state, 0, 0);
+ "Fabric sched2: ste:x%x",
+ iocb->vport->port_state, 0, 0);
ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocb, 0);
if (ret == IOCB_ERROR) {
- iocb->iocb_cmpl = iocb->fabric_iocb_cmpl;
- iocb->fabric_iocb_cmpl = NULL;
- iocb->iocb_flag &= ~LPFC_IO_FABRIC;
+ iocb->cmd_cmpl = iocb->fabric_cmd_cmpl;
+ iocb->fabric_cmd_cmpl = NULL;
+ iocb->cmd_flag &= ~LPFC_IO_FABRIC;
atomic_dec(&phba->fabric_iocb_count);
}
} else {
@@ -7665,7 +11954,7 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
* driver internal fabric IOCB list. The list contains fabric IOCBs to be
* issued to the ELS IOCB ring. This abort function walks the fabric IOCB
* list, removes each IOCB associated with the @vport off the list, set the
- * status feild to IOSTAT_LOCAL_REJECT, and invokes the callback function
+ * status field to IOSTAT_LOCAL_REJECT, and invokes the callback function
* associated with the IOCB.
**/
static void lpfc_fabric_abort_vport(struct lpfc_vport *vport)
@@ -7698,7 +11987,7 @@ static void lpfc_fabric_abort_vport(struct lpfc_vport *vport)
* driver internal fabric IOCB list. The list contains fabric IOCBs to be
* issued to the ELS IOCB ring. This abort function walks the fabric IOCB
* list, removes each IOCB associated with the @ndlp off the list, set the
- * status feild to IOSTAT_LOCAL_REJECT, and invokes the callback function
+ * status field to IOSTAT_LOCAL_REJECT, and invokes the callback function
* associated with the IOCB.
**/
void lpfc_fabric_abort_nport(struct lpfc_nodelist *ndlp)
@@ -7706,7 +11995,12 @@ void lpfc_fabric_abort_nport(struct lpfc_nodelist *ndlp)
LIST_HEAD(completions);
struct lpfc_hba *phba = ndlp->phba;
struct lpfc_iocbq *tmp_iocb, *piocb;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring;
+
+ pring = lpfc_phba_elsring(phba);
+
+ if (unlikely(!pring))
+ return;
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(piocb, tmp_iocb, &phba->fabric_iocb_list,
@@ -7730,7 +12024,7 @@ void lpfc_fabric_abort_nport(struct lpfc_nodelist *ndlp)
* This routine aborts all the IOCBs currently on the driver internal
* fabric IOCB list. The list contains fabric IOCBs to be issued to the ELS
* IOCB ring. This function takes the entire IOCB list off the fabric IOCB
- * list, removes IOCBs off the list, set the status feild to
+ * list, removes IOCBs off the list, set the status field to
* IOSTAT_LOCAL_REJECT, and invokes the callback function associated with
* the IOCB.
**/
@@ -7759,17 +12053,31 @@ lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+ struct lpfc_nodelist *ndlp = NULL;
unsigned long iflag = 0;
- spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_sgl_list_lock);
+ spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock, iflag);
list_for_each_entry_safe(sglq_entry, sglq_next,
&phba->sli4_hba.lpfc_abts_els_sgl_list, list) {
- if (sglq_entry->ndlp && sglq_entry->ndlp->vport == vport)
+ if (sglq_entry->ndlp && sglq_entry->ndlp->vport == vport) {
+ lpfc_nlp_put(sglq_entry->ndlp);
+ ndlp = sglq_entry->ndlp;
sglq_entry->ndlp = NULL;
+
+ /* If the xri on the abts_els_sgl list is for the Fport
+ * node and the vport is unloading, the xri aborted wcqe
+ * likely isn't coming back. Just release the sgl.
+ */
+ if (test_bit(FC_UNLOADING, &vport->load_flag) &&
+ ndlp->nlp_DID == Fabric_DID) {
+ list_del(&sglq_entry->list);
+ sglq_entry->state = SGL_FREED;
+ list_add_tail(&sglq_entry->list,
+ &phba->sli4_hba.lpfc_els_sgl_list);
+ }
+ }
}
- spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
- spin_unlock_irqrestore(&phba->hbalock, iflag);
+ spin_unlock_irqrestore(&phba->sli4_hba.sgl_list_lock, iflag);
return;
}
@@ -7787,14 +12095,16 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba,
{
uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri);
+ uint16_t lxri = 0;
struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
unsigned long iflag = 0;
struct lpfc_nodelist *ndlp;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring;
- spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_sgl_list_lock);
+ pring = lpfc_phba_elsring(phba);
+
+ spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock, iflag);
list_for_each_entry_safe(sglq_entry, sglq_next,
&phba->sli4_hba.lpfc_abts_els_sgl_list, list) {
if (sglq_entry->sli4_xritag == xri) {
@@ -7802,20 +12112,35 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba,
ndlp = sglq_entry->ndlp;
sglq_entry->ndlp = NULL;
list_add_tail(&sglq_entry->list,
- &phba->sli4_hba.lpfc_sgl_list);
+ &phba->sli4_hba.lpfc_els_sgl_list);
sglq_entry->state = SGL_FREED;
- spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_set_rrq_active(phba, ndlp, xri, rxid, 1);
+ spin_unlock_irqrestore(&phba->sli4_hba.sgl_list_lock,
+ iflag);
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_SLI |
+ LOG_DISCOVERY | LOG_NODE,
+ "0732 ELS XRI ABORT on Node: ndlp=x%px "
+ "xri=x%x\n",
+ ndlp, xri);
+ if (ndlp) {
+ lpfc_set_rrq_active(phba, ndlp,
+ sglq_entry->sli4_lxritag,
+ rxid, 1);
+ lpfc_nlp_put(ndlp);
+ }
/* Check if TXQ queue needs to be serviced */
- if (pring->txq_cnt)
+ if (pring && !list_empty(&pring->txq))
lpfc_worker_wake_up(phba);
return;
}
}
- spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
- sglq_entry = __lpfc_get_active_sglq(phba, xri);
+ spin_unlock_irqrestore(&phba->sli4_hba.sgl_list_lock, iflag);
+ lxri = lpfc_sli4_xri_inrange(phba, xri);
+ if (lxri == NO_XRI)
+ return;
+
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ sglq_entry = __lpfc_get_active_sglq(phba, lxri);
if (!sglq_entry || (sglq_entry->sli4_xritag != xri)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
@@ -7824,3 +12149,359 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba,
spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
}
+
+/* lpfc_sli_abts_recover_port - Recover a port that failed a BLS_ABORT req.
+ * @vport: pointer to virtual port object.
+ * @ndlp: nodelist pointer for the impacted node.
+ *
+ * The driver calls this routine in response to an SLI4 XRI ABORT CQE
+ * or an SLI3 ASYNC_STATUS_CN event from the port. For either event,
+ * the driver is required to send a LOGO to the remote node before it
+ * attempts to recover its login to the remote node.
+ */
+void
+lpfc_sli_abts_recover_port(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp)
+{
+ struct Scsi_Host *shost;
+ struct lpfc_hba *phba;
+ unsigned long flags = 0;
+
+ shost = lpfc_shost_from_vport(vport);
+ phba = vport->phba;
+ if (ndlp->nlp_state != NLP_STE_MAPPED_NODE) {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_SLI, "3093 No rport recovery needed. "
+ "rport in state 0x%x\n", ndlp->nlp_state);
+ return;
+ }
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3094 Start rport recovery on shost id 0x%x "
+ "fc_id 0x%06x vpi 0x%x rpi 0x%x state 0x%x "
+ "flag 0x%lx\n",
+ shost->host_no, ndlp->nlp_DID,
+ vport->vpi, ndlp->nlp_rpi, ndlp->nlp_state,
+ ndlp->nlp_flag);
+ /*
+ * The rport is not responding. Remove the FCP-2 flag to prevent
+ * an ADISC in the follow-up recovery code.
+ */
+ spin_lock_irqsave(&ndlp->lock, flags);
+ ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ spin_unlock_irqrestore(&ndlp->lock, flags);
+ set_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag);
+ lpfc_unreg_rpi(vport, ndlp);
+}
+
+static void lpfc_init_cs_ctl_bitmap(struct lpfc_vport *vport)
+{
+ bitmap_zero(vport->vmid_priority_range, LPFC_VMID_MAX_PRIORITY_RANGE);
+}
+
+static void
+lpfc_vmid_set_cs_ctl_range(struct lpfc_vport *vport, u32 min, u32 max)
+{
+ u32 i;
+
+ if ((min > max) || (max > LPFC_VMID_MAX_PRIORITY_RANGE))
+ return;
+
+ for (i = min; i <= max; i++)
+ set_bit(i, vport->vmid_priority_range);
+}
+
+static void lpfc_vmid_put_cs_ctl(struct lpfc_vport *vport, u32 ctcl_vmid)
+{
+ set_bit(ctcl_vmid, vport->vmid_priority_range);
+}
+
+u32 lpfc_vmid_get_cs_ctl(struct lpfc_vport *vport)
+{
+ u32 i;
+
+ i = find_first_bit(vport->vmid_priority_range,
+ LPFC_VMID_MAX_PRIORITY_RANGE);
+
+ if (i == LPFC_VMID_MAX_PRIORITY_RANGE)
+ return 0;
+
+ clear_bit(i, vport->vmid_priority_range);
+ return i;
+}
+
+#define MAX_PRIORITY_DESC 255
+
+static void
+lpfc_cmpl_els_qfpa(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_vport *vport = cmdiocb->vport;
+ struct priority_range_desc *desc;
+ struct lpfc_dmabuf *prsp = NULL;
+ struct lpfc_vmid_priority_range *vmid_range = NULL;
+ u32 *data;
+ struct lpfc_dmabuf *dmabuf = cmdiocb->cmd_dmabuf;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
+ u8 *pcmd, max_desc;
+ u32 len, i;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
+
+ prsp = list_get_first(&dmabuf->list, struct lpfc_dmabuf, list);
+ if (!prsp)
+ goto out;
+
+ pcmd = prsp->virt;
+ data = (u32 *)pcmd;
+ if (data[0] == ELS_CMD_LS_RJT) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI,
+ "3277 QFPA LS_RJT x%x x%x\n",
+ data[0], data[1]);
+ goto out;
+ }
+ if (ulp_status) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_SLI,
+ "6529 QFPA failed with status x%x x%x\n",
+ ulp_status, ulp_word4);
+ goto out;
+ }
+
+ if (!vport->qfpa_res) {
+ max_desc = FCELSSIZE / sizeof(*vport->qfpa_res);
+ vport->qfpa_res = kcalloc(max_desc, sizeof(*vport->qfpa_res),
+ GFP_KERNEL);
+ if (!vport->qfpa_res)
+ goto out;
+ }
+
+ len = *((u32 *)(pcmd + 4));
+ len = be32_to_cpu(len);
+ memcpy(vport->qfpa_res, pcmd, len + 8);
+ len = len / LPFC_PRIORITY_RANGE_DESC_SIZE;
+
+ desc = (struct priority_range_desc *)(pcmd + 8);
+ vmid_range = vport->vmid_priority.vmid_range;
+ if (!vmid_range) {
+ vmid_range = kcalloc(MAX_PRIORITY_DESC, sizeof(*vmid_range),
+ GFP_KERNEL);
+ if (!vmid_range) {
+ kfree(vport->qfpa_res);
+ goto out;
+ }
+ vport->vmid_priority.vmid_range = vmid_range;
+ }
+ vport->vmid_priority.num_descriptors = len;
+
+ for (i = 0; i < len; i++, vmid_range++, desc++) {
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_ELS,
+ "6539 vmid values low=%d, high=%d, qos=%d, "
+ "local ve id=%d\n", desc->lo_range,
+ desc->hi_range, desc->qos_priority,
+ desc->local_ve_id);
+
+ vmid_range->low = desc->lo_range << 1;
+ if (desc->local_ve_id == QFPA_ODD_ONLY)
+ vmid_range->low++;
+ if (desc->qos_priority)
+ vport->vmid_flag |= LPFC_VMID_QOS_ENABLED;
+ vmid_range->qos = desc->qos_priority;
+
+ vmid_range->high = desc->hi_range << 1;
+ if ((desc->local_ve_id == QFPA_ODD_ONLY) ||
+ (desc->local_ve_id == QFPA_EVEN_ODD))
+ vmid_range->high++;
+ }
+ lpfc_init_cs_ctl_bitmap(vport);
+ for (i = 0; i < vport->vmid_priority.num_descriptors; i++) {
+ lpfc_vmid_set_cs_ctl_range(vport,
+ vport->vmid_priority.vmid_range[i].low,
+ vport->vmid_priority.vmid_range[i].high);
+ }
+
+ vport->vmid_flag |= LPFC_VMID_QFPA_CMPL;
+ out:
+ lpfc_els_free_iocb(phba, cmdiocb);
+ lpfc_nlp_put(ndlp);
+}
+
+int lpfc_issue_els_qfpa(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_iocbq *elsiocb;
+ u8 *pcmd;
+ int ret;
+
+ ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
+ if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+ return -ENXIO;
+
+ elsiocb = lpfc_prep_els_iocb(vport, 1, LPFC_QFPA_SIZE, 2, ndlp,
+ ndlp->nlp_DID, ELS_CMD_QFPA);
+ if (!elsiocb)
+ return -ENOMEM;
+
+ pcmd = (u8 *)elsiocb->cmd_dmabuf->virt;
+
+ *((u32 *)(pcmd)) = ELS_CMD_QFPA;
+ pcmd += 4;
+
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_qfpa;
+
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(vport->phba, elsiocb);
+ return -ENXIO;
+ }
+
+ ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 2);
+ if (ret != IOCB_SUCCESS) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ return -EIO;
+ }
+ vport->vmid_flag &= ~LPFC_VMID_QOS_ENABLED;
+ return 0;
+}
+
+int
+lpfc_vmid_uvem(struct lpfc_vport *vport,
+ struct lpfc_vmid *vmid, bool instantiated)
+{
+ struct lpfc_vem_id_desc *vem_id_desc;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_iocbq *elsiocb;
+ struct instantiated_ve_desc *inst_desc;
+ struct lpfc_vmid_context *vmid_context;
+ u8 *pcmd;
+ u32 *len;
+ int ret = 0;
+
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+ return -ENXIO;
+
+ vmid_context = kmalloc(sizeof(*vmid_context), GFP_KERNEL);
+ if (!vmid_context)
+ return -ENOMEM;
+ elsiocb = lpfc_prep_els_iocb(vport, 1, LPFC_UVEM_SIZE, 2,
+ ndlp, Fabric_DID, ELS_CMD_UVEM);
+ if (!elsiocb)
+ goto out;
+
+ lpfc_printf_vlog(vport, KERN_DEBUG, LOG_ELS,
+ "3427 Host vmid %s %d\n",
+ vmid->host_vmid, instantiated);
+ vmid_context->vmp = vmid;
+ vmid_context->nlp = ndlp;
+ vmid_context->instantiated = instantiated;
+ elsiocb->vmid_tag.vmid_context = vmid_context;
+ pcmd = (u8 *)elsiocb->cmd_dmabuf->virt;
+
+ if (!memchr_inv(vport->lpfc_vmid_host_uuid, 0,
+ sizeof(vport->lpfc_vmid_host_uuid)))
+ memcpy(vport->lpfc_vmid_host_uuid, vmid->host_vmid,
+ sizeof(vport->lpfc_vmid_host_uuid));
+
+ *((u32 *)(pcmd)) = ELS_CMD_UVEM;
+ len = (u32 *)(pcmd + 4);
+ *len = cpu_to_be32(LPFC_UVEM_SIZE - 8);
+
+ vem_id_desc = (struct lpfc_vem_id_desc *)(pcmd + 8);
+ vem_id_desc->tag = be32_to_cpu(VEM_ID_DESC_TAG);
+ vem_id_desc->length = be32_to_cpu(LPFC_UVEM_VEM_ID_DESC_SIZE);
+ memcpy(vem_id_desc->vem_id, vport->lpfc_vmid_host_uuid,
+ sizeof(vem_id_desc->vem_id));
+
+ inst_desc = (struct instantiated_ve_desc *)(pcmd + 32);
+ inst_desc->tag = be32_to_cpu(INSTANTIATED_VE_DESC_TAG);
+ inst_desc->length = be32_to_cpu(LPFC_UVEM_VE_MAP_DESC_SIZE);
+ memcpy(inst_desc->global_vem_id, vmid->host_vmid,
+ sizeof(inst_desc->global_vem_id));
+
+ bf_set(lpfc_instantiated_nport_id, inst_desc, vport->fc_myDID);
+ bf_set(lpfc_instantiated_local_id, inst_desc,
+ vmid->un.cs_ctl_vmid);
+ if (instantiated) {
+ inst_desc->tag = be32_to_cpu(INSTANTIATED_VE_DESC_TAG);
+ } else {
+ inst_desc->tag = be32_to_cpu(DEINSTANTIATED_VE_DESC_TAG);
+ lpfc_vmid_put_cs_ctl(vport, vmid->un.cs_ctl_vmid);
+ }
+ inst_desc->word6 = cpu_to_be32(inst_desc->word6);
+
+ elsiocb->cmd_cmpl = lpfc_cmpl_els_uvem;
+
+ elsiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!elsiocb->ndlp) {
+ lpfc_els_free_iocb(vport->phba, elsiocb);
+ goto out;
+ }
+
+ ret = lpfc_sli_issue_iocb(vport->phba, LPFC_ELS_RING, elsiocb, 0);
+ if (ret != IOCB_SUCCESS) {
+ lpfc_els_free_iocb(vport->phba, elsiocb);
+ lpfc_nlp_put(ndlp);
+ goto out;
+ }
+
+ return 0;
+ out:
+ kfree(vmid_context);
+ return -EIO;
+}
+
+static void
+lpfc_cmpl_els_uvem(struct lpfc_hba *phba, struct lpfc_iocbq *icmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_vport *vport = icmdiocb->vport;
+ struct lpfc_dmabuf *prsp = NULL;
+ struct lpfc_vmid_context *vmid_context =
+ icmdiocb->vmid_tag.vmid_context;
+ struct lpfc_nodelist *ndlp = icmdiocb->ndlp;
+ u8 *pcmd;
+ u32 *data;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
+ struct lpfc_dmabuf *dmabuf = icmdiocb->cmd_dmabuf;
+ struct lpfc_vmid *vmid;
+
+ vmid = vmid_context->vmp;
+ if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+ ndlp = NULL;
+
+ prsp = list_get_first(&dmabuf->list, struct lpfc_dmabuf, list);
+ if (!prsp)
+ goto out;
+ pcmd = prsp->virt;
+ data = (u32 *)pcmd;
+ if (data[0] == ELS_CMD_LS_RJT) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI,
+ "4532 UVEM LS_RJT %x %x\n", data[0], data[1]);
+ goto out;
+ }
+ if (ulp_status) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI,
+ "4533 UVEM error status %x: %x\n",
+ ulp_status, ulp_word4);
+ goto out;
+ }
+ spin_lock(&phba->hbalock);
+ /* Set IN USE flag */
+ vport->vmid_flag |= LPFC_VMID_IN_USE;
+ phba->pport->vmid_flag |= LPFC_VMID_IN_USE;
+ spin_unlock(&phba->hbalock);
+
+ if (vmid_context->instantiated) {
+ write_lock(&vport->vmid_lock);
+ vmid->flag |= LPFC_VMID_REGISTERED;
+ vmid->flag &= ~LPFC_VMID_REQ_REGISTER;
+ write_unlock(&vport->vmid_lock);
+ }
+
+ out:
+ kfree(vmid_context);
+ lpfc_els_free_iocb(phba, icmdiocb);
+ lpfc_nlp_put(ndlp);
+}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 18d0dbfda2bc..bb803f32bc1b 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -25,11 +27,14 @@
#include <linux/pci.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>
+#include <linux/lockdep.h>
+#include <linux/utsname.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -37,8 +42,9 @@
#include "lpfc_disc.h"
#include "lpfc_sli.h"
#include "lpfc_sli4.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
@@ -65,36 +71,83 @@ static void lpfc_disc_timeout_handler(struct lpfc_vport *);
static void lpfc_disc_flush_list(struct lpfc_vport *vport);
static void lpfc_unregister_fcfi_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
static int lpfc_fcf_inuse(struct lpfc_hba *);
+static void lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *);
+static void lpfc_check_inactive_vmid(struct lpfc_hba *phba);
+static void lpfc_check_vmid_qfpa_issue(struct lpfc_hba *phba);
-void
-lpfc_terminate_rport_io(struct fc_rport *rport)
+static int
+lpfc_valid_xpt_node(struct lpfc_nodelist *ndlp)
+{
+ if (ndlp->nlp_fc4_type ||
+ ndlp->nlp_type & NLP_FABRIC)
+ return 1;
+ return 0;
+}
+/* The source of a terminate rport I/O is either a dev_loss_tmo
+ * event or a call to fc_remove_host. While the rport should be
+ * valid during these downcalls, the transport can call twice
+ * in a single event. This routine provides somoe protection
+ * as the NDLP isn't really free, just released to the pool.
+ */
+static int
+lpfc_rport_invalid(struct fc_rport *rport)
{
struct lpfc_rport_data *rdata;
- struct lpfc_nodelist * ndlp;
- struct lpfc_hba *phba;
+ struct lpfc_nodelist *ndlp;
+
+ if (!rport) {
+ pr_err("**** %s: NULL rport, exit.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) {
+ pr_info("**** %s: devloss_callbk_done rport x%px SID x%x\n",
+ __func__, rport, rport->scsi_target_id);
+ return -EINVAL;
+ }
rdata = rport->dd_data;
+ if (!rdata) {
+ pr_err("**** %s: NULL dd_data on rport x%px SID x%x\n",
+ __func__, rport, rport->scsi_target_id);
+ return -EINVAL;
+ }
+
ndlp = rdata->pnode;
+ if (!rdata->pnode) {
+ pr_info("**** %s: NULL ndlp on rport x%px SID x%x\n",
+ __func__, rport, rport->scsi_target_id);
+ return -EINVAL;
+ }
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
- if (rport->roles & FC_RPORT_ROLE_FCP_TARGET)
- printk(KERN_ERR "Cannot find remote node"
- " to terminate I/O Data x%x\n",
- rport->port_id);
- return;
+ if (!ndlp->vport) {
+ pr_err("**** %s: Null vport on ndlp x%px, DID x%x rport x%px "
+ "SID x%x\n", __func__, ndlp, ndlp->nlp_DID, rport,
+ rport->scsi_target_id);
+ return -EINVAL;
}
+ return 0;
+}
- phba = ndlp->phba;
+void
+lpfc_terminate_rport_io(struct fc_rport *rport)
+{
+ struct lpfc_rport_data *rdata;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_vport *vport;
- lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_RPORT,
- "rport terminate: sid:x%x did:x%x flg:x%x",
- ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);
+ if (lpfc_rport_invalid(rport))
+ return;
- if (ndlp->nlp_sid != NLP_NO_SID) {
- lpfc_sli_abort_iocb(ndlp->vport,
- &phba->sli.ring[phba->sli.fcp_ring],
- ndlp->nlp_sid, 0, LPFC_CTX_TGT);
- }
+ rdata = rport->dd_data;
+ ndlp = rdata->pnode;
+ vport = ndlp->vport;
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
+ "rport terminate: sid:x%x did:x%x flg:x%lx",
+ ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);
+
+ if (ndlp->nlp_sid != NLP_NO_SID)
+ lpfc_sli_abort_iocb(vport, ndlp->nlp_sid, 0, LPFC_CTX_TGT);
}
/*
@@ -103,63 +156,283 @@ lpfc_terminate_rport_io(struct fc_rport *rport)
void
lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
{
- struct lpfc_rport_data *rdata;
- struct lpfc_nodelist * ndlp;
+ struct lpfc_nodelist *ndlp;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
struct lpfc_work_evt *evtp;
- int put_node;
- int put_rport;
+ unsigned long iflags;
+ bool drop_initial_node_ref = false;
- rdata = rport->dd_data;
- ndlp = rdata->pnode;
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
+ ndlp = ((struct lpfc_rport_data *)rport->dd_data)->pnode;
+ if (!ndlp)
return;
vport = ndlp->vport;
phba = vport->phba;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
- "rport devlosscb: sid:x%x did:x%x flg:x%x",
+ "rport devlosscb: sid:x%x did:x%x flg:x%lx",
ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);
- /* Don't defer this if we are in the process of deleting the vport
- * or unloading the driver. The unload will cleanup the node
- * appropriately we just need to cleanup the ndlp rport info here.
- */
- if (vport->load_flag & FC_UNLOADING) {
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
+ "3181 dev_loss_callbk x%06x, rport x%px flg x%lx "
+ "load_flag x%lx refcnt %u state %d xpt x%x\n",
+ ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag,
+ vport->load_flag, kref_read(&ndlp->kref),
+ ndlp->nlp_state, ndlp->fc4_xpt_flags);
+
+ /* Don't schedule a worker thread event if the vport is going down. */
+ if (test_bit(FC_UNLOADING, &vport->load_flag) ||
+ (phba->sli_rev == LPFC_SLI_REV4 &&
+ !test_bit(HBA_SETUP, &phba->hba_flag))) {
+
+ spin_lock_irqsave(&ndlp->lock, iflags);
ndlp->rport = NULL;
- if (put_node)
+
+ /* Only 1 thread can drop the initial node reference.
+ * If not registered for NVME and NLP_DROPPED flag is
+ * clear, remove the initial reference.
+ */
+ if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD))
+ if (!test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag))
+ drop_initial_node_ref = true;
+
+ /* The scsi_transport is done with the rport so lpfc cannot
+ * call to unregister.
+ */
+ if (ndlp->fc4_xpt_flags & SCSI_XPT_REGD) {
+ ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD;
+
+ /* If NLP_XPT_REGD was cleared in lpfc_nlp_unreg_node,
+ * unregister calls were made to the scsi and nvme
+ * transports and refcnt was already decremented. Clear
+ * the NLP_XPT_REGD flag only if the NVME nrport is
+ * confirmed unregistered.
+ */
+ if (ndlp->fc4_xpt_flags & NLP_XPT_REGD) {
+ if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD))
+ ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD;
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+
+ /* Release scsi transport reference */
+ lpfc_nlp_put(ndlp);
+ } else {
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+ }
+ } else {
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+ }
+
+ if (drop_initial_node_ref)
lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
return;
}
if (ndlp->nlp_state == NLP_STE_MAPPED_NODE)
return;
+ /* Ignore callback for a mismatched (stale) rport */
+ if (ndlp->rport != rport) {
+ lpfc_vlog_msg(vport, KERN_WARNING, LOG_NODE,
+ "6788 fc rport mismatch: d_id x%06x ndlp x%px "
+ "fc rport x%px node rport x%px state x%x "
+ "refcnt %u\n",
+ ndlp->nlp_DID, ndlp, rport, ndlp->rport,
+ ndlp->nlp_state, kref_read(&ndlp->kref));
+ return;
+ }
+
+ if (rport->port_name != wwn_to_u64(ndlp->nlp_portname.u.wwn))
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6789 rport name %llx != node port name %llx",
+ rport->port_name,
+ wwn_to_u64(ndlp->nlp_portname.u.wwn));
+
evtp = &ndlp->dev_loss_evt;
- if (!list_empty(&evtp->evt_listp))
+ if (!list_empty(&evtp->evt_listp)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6790 rport name %llx dev_loss_evt pending\n",
+ rport->port_name);
return;
+ }
- spin_lock_irq(&phba->hbalock);
- /* We need to hold the node by incrementing the reference
- * count until this queued work is done
+ set_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag);
+
+ spin_lock_irqsave(&ndlp->lock, iflags);
+ /* If there is a PLOGI in progress, and we are in a
+ * NLP_NPR_2B_DISC state, don't turn off the flag.
*/
- evtp->evt_arg1 = lpfc_nlp_get(ndlp);
- if (evtp->evt_arg1) {
- evtp->evt = LPFC_EVT_DEV_LOSS;
- list_add_tail(&evtp->evt_listp, &phba->work_list);
- lpfc_worker_wake_up(phba);
+ if (ndlp->nlp_state != NLP_STE_PLOGI_ISSUE)
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+
+ /*
+ * The backend does not expect any more calls associated with this
+ * rport. Remove the association between rport and ndlp.
+ */
+ ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD;
+ ((struct lpfc_rport_data *)rport->dd_data)->pnode = NULL;
+ ndlp->rport = NULL;
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+
+ if (phba->worker_thread) {
+ /* We need to hold the node by incrementing the reference
+ * count until this queued work is done
+ */
+ evtp->evt_arg1 = lpfc_nlp_get(ndlp);
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ if (evtp->evt_arg1) {
+ evtp->evt = LPFC_EVT_DEV_LOSS;
+ list_add_tail(&evtp->evt_listp, &phba->work_list);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ lpfc_worker_wake_up(phba);
+ return;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ } else {
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
+ "3188 worker thread is stopped %s x%06x, "
+ " rport x%px flg x%lx load_flag x%lx refcnt "
+ "%d\n", __func__, ndlp->nlp_DID,
+ ndlp->rport, ndlp->nlp_flag,
+ vport->load_flag, kref_read(&ndlp->kref));
+ if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD)) {
+ /* Node is in dev loss. No further transaction. */
+ clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag);
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
+ }
}
- spin_unlock_irq(&phba->hbalock);
+}
- return;
+/**
+ * lpfc_check_inactive_vmid_one - VMID inactivity checker for a vport
+ * @vport: Pointer to vport context object.
+ *
+ * This function checks for idle VMID entries related to a particular vport. If
+ * found unused/idle, free them accordingly.
+ **/
+static void lpfc_check_inactive_vmid_one(struct lpfc_vport *vport)
+{
+ u16 keep;
+ u32 difftime = 0, r, bucket;
+ u64 *lta;
+ int cpu;
+ struct lpfc_vmid *vmp;
+
+ write_lock(&vport->vmid_lock);
+
+ if (!vport->cur_vmid_cnt)
+ goto out;
+
+ /* iterate through the table */
+ hash_for_each(vport->hash_table, bucket, vmp, hnode) {
+ keep = 0;
+ if (vmp->flag & LPFC_VMID_REGISTERED) {
+ /* check if the particular VMID is in use */
+ /* for all available per cpu variable */
+ for_each_possible_cpu(cpu) {
+ /* if last access time is less than timeout */
+ lta = per_cpu_ptr(vmp->last_io_time, cpu);
+ if (!lta)
+ continue;
+ difftime = (jiffies) - (*lta);
+ if ((vport->vmid_inactivity_timeout *
+ JIFFIES_PER_HR) > difftime) {
+ keep = 1;
+ break;
+ }
+ }
+
+ /* if none of the cpus have been used by the vm, */
+ /* remove the entry if already registered */
+ if (!keep) {
+ /* mark the entry for deregistration */
+ vmp->flag = LPFC_VMID_DE_REGISTER;
+ write_unlock(&vport->vmid_lock);
+ if (vport->vmid_priority_tagging)
+ r = lpfc_vmid_uvem(vport, vmp, false);
+ else
+ r = lpfc_vmid_cmd(vport,
+ SLI_CTAS_DAPP_IDENT,
+ vmp);
+
+ /* decrement number of active vms and mark */
+ /* entry in slot as free */
+ write_lock(&vport->vmid_lock);
+ if (!r) {
+ struct lpfc_vmid *ht = vmp;
+
+ vport->cur_vmid_cnt--;
+ ht->flag = LPFC_VMID_SLOT_FREE;
+ free_percpu(ht->last_io_time);
+ ht->last_io_time = NULL;
+ hash_del(&ht->hnode);
+ }
+ }
+ }
+ }
+ out:
+ write_unlock(&vport->vmid_lock);
+}
+
+/**
+ * lpfc_check_inactive_vmid - VMID inactivity checker
+ * @phba: Pointer to hba context object.
+ *
+ * This function is called from the worker thread to determine if an entry in
+ * the VMID table can be released since there was no I/O activity seen from that
+ * particular VM for the specified time. When this happens, the entry in the
+ * table is released and also the resources on the switch cleared.
+ **/
+
+static void lpfc_check_inactive_vmid(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport;
+ struct lpfc_vport **vports;
+ int i;
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (!vports)
+ return;
+
+ for (i = 0; i <= phba->max_vports; i++) {
+ if ((!vports[i]) && (i == 0))
+ vport = phba->pport;
+ else
+ vport = vports[i];
+ if (!vport)
+ break;
+
+ lpfc_check_inactive_vmid_one(vport);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
+/**
+ * lpfc_check_nlp_post_devloss - Check to restore ndlp refcnt after devloss
+ * @vport: Pointer to vport object.
+ * @ndlp: Pointer to remote node object.
+ *
+ * If NLP_IN_RECOV_POST_DEV_LOSS flag was set due to outstanding recovery of
+ * node during dev_loss_tmo processing, then this function restores the nlp_put
+ * kref decrement from lpfc_dev_loss_tmo_handler.
+ **/
+void
+lpfc_check_nlp_post_devloss(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp)
+{
+ if (test_and_clear_bit(NLP_IN_RECOV_POST_DEV_LOSS, &ndlp->save_flags)) {
+ clear_bit(NLP_DROPPED, &ndlp->nlp_flag);
+ lpfc_nlp_get(ndlp);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY | LOG_NODE,
+ "8438 Devloss timeout reversed on DID x%x "
+ "refcnt %d ndlp %p flag x%lx "
+ "port_state = x%x\n",
+ ndlp->nlp_DID, kref_read(&ndlp->kref), ndlp,
+ ndlp->nlp_flag, vport->port_state);
+ }
}
/**
@@ -175,55 +448,32 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
static int
lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
{
- struct lpfc_rport_data *rdata;
- struct fc_rport *rport;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
uint8_t *name;
- int put_node;
- int put_rport;
int warn_on = 0;
int fcf_inuse = 0;
+ bool recovering = false;
+ struct fc_vport *fc_vport = NULL;
+ unsigned long iflags;
- rport = ndlp->rport;
-
- if (!rport)
- return fcf_inuse;
-
- rdata = rport->dd_data;
- name = (uint8_t *) &ndlp->nlp_portname;
vport = ndlp->vport;
- phba = vport->phba;
+ name = (uint8_t *)&ndlp->nlp_portname;
+ phba = vport->phba;
if (phba->sli_rev == LPFC_SLI_REV4)
fcf_inuse = lpfc_fcf_inuse(phba);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
- "rport devlosstmo:did:x%x type:x%x id:x%x",
- ndlp->nlp_DID, ndlp->nlp_type, rport->scsi_target_id);
+ "rport devlosstmo:did:x%x type:x%x id:x%x",
+ ndlp->nlp_DID, ndlp->nlp_type, ndlp->nlp_sid);
- /* Don't defer this if we are in the process of deleting the vport
- * or unloading the driver. The unload will cleanup the node
- * appropriately we just need to cleanup the ndlp rport info here.
- */
- if (vport->load_flag & FC_UNLOADING) {
- if (ndlp->nlp_sid != NLP_NO_SID) {
- /* flush the target */
- lpfc_sli_abort_iocb(vport,
- &phba->sli.ring[phba->sli.fcp_ring],
- ndlp->nlp_sid, 0, LPFC_CTX_TGT);
- }
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- if (put_node)
- lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
- return fcf_inuse;
- }
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
+ "3182 %s x%06x, nflag x%lx xflags x%x refcnt %d\n",
+ __func__, ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->fc4_xpt_flags, kref_read(&ndlp->kref));
+ /* If the driver is recovering the rport, ignore devloss. */
if (ndlp->nlp_state == NLP_STE_MAPPED_NODE) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0284 Devloss timeout Ignored on "
@@ -232,69 +482,163 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
*name, *(name+1), *(name+2), *(name+3),
*(name+4), *(name+5), *(name+6), *(name+7),
ndlp->nlp_DID);
+
+ clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag);
return fcf_inuse;
}
+ /* Fabric nodes are done. */
if (ndlp->nlp_type & NLP_FABRIC) {
- /* We will clean up these Nodes in linkup */
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- if (put_node)
+ spin_lock_irqsave(&ndlp->lock, iflags);
+
+ /* The driver has to account for a race between any fabric
+ * node that's in recovery when dev_loss_tmo expires. When this
+ * happens, the driver has to allow node recovery.
+ */
+ switch (ndlp->nlp_DID) {
+ case Fabric_DID:
+ fc_vport = vport->fc_vport;
+ if (fc_vport) {
+ /* NPIV path. */
+ if (fc_vport->vport_state ==
+ FC_VPORT_INITIALIZING)
+ recovering = true;
+ } else {
+ /* Physical port path. */
+ if (test_bit(HBA_FLOGI_OUTSTANDING,
+ &phba->hba_flag))
+ recovering = true;
+ }
+ break;
+ case Fabric_Cntl_DID:
+ if (test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag))
+ recovering = true;
+ break;
+ case FDMI_DID:
+ fallthrough;
+ case NameServer_DID:
+ if (ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE &&
+ ndlp->nlp_state <= NLP_STE_REG_LOGIN_ISSUE)
+ recovering = true;
+ break;
+ default:
+ /* Ensure the nlp_DID at least has the correct prefix.
+ * The fabric domain controller's last three nibbles
+ * vary so we handle it in the default case.
+ */
+ if (ndlp->nlp_DID & Fabric_DID_MASK) {
+ if (ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE &&
+ ndlp->nlp_state <= NLP_STE_REG_LOGIN_ISSUE)
+ recovering = true;
+ }
+ break;
+ }
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+
+ /* Mark an NLP_IN_RECOV_POST_DEV_LOSS flag to know if reversing
+ * the following lpfc_nlp_put is necessary after fabric node is
+ * recovered.
+ */
+ clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag);
+ if (recovering) {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY | LOG_NODE,
+ "8436 Devloss timeout marked on "
+ "DID x%x refcnt %d ndlp %p "
+ "flag x%lx port_state = x%x\n",
+ ndlp->nlp_DID, kref_read(&ndlp->kref),
+ ndlp, ndlp->nlp_flag,
+ vport->port_state);
+ set_bit(NLP_IN_RECOV_POST_DEV_LOSS, &ndlp->save_flags);
+ return fcf_inuse;
+ } else if (ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
+ /* Fabric node fully recovered before this dev_loss_tmo
+ * queue work is processed. Thus, ignore the
+ * dev_loss_tmo event.
+ */
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_DISCOVERY | LOG_NODE,
+ "8437 Devloss timeout ignored on "
+ "DID x%x refcnt %d ndlp %p "
+ "flag x%lx port_state = x%x\n",
+ ndlp->nlp_DID, kref_read(&ndlp->kref),
+ ndlp, ndlp->nlp_flag,
+ vport->port_state);
+ return fcf_inuse;
+ }
+
+ if (!test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag))
lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
return fcf_inuse;
}
if (ndlp->nlp_sid != NLP_NO_SID) {
warn_on = 1;
- /* flush the target */
- lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
- ndlp->nlp_sid, 0, LPFC_CTX_TGT);
+ lpfc_sli_abort_iocb(vport, ndlp->nlp_sid, 0, LPFC_CTX_TGT);
}
if (warn_on) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0203 Devloss timeout on "
"WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
- "NPort x%06x Data: x%x x%x x%x\n",
+ "NPort x%06x Data: x%lx x%x x%x refcnt %d\n",
*name, *(name+1), *(name+2), *(name+3),
*(name+4), *(name+5), *(name+6), *(name+7),
ndlp->nlp_DID, ndlp->nlp_flag,
- ndlp->nlp_state, ndlp->nlp_rpi);
+ ndlp->nlp_state, ndlp->nlp_rpi,
+ kref_read(&ndlp->kref));
} else {
- lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_TRACE_EVENT,
"0204 Devloss timeout on "
"WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
- "NPort x%06x Data: x%x x%x x%x\n",
+ "NPort x%06x Data: x%lx x%x x%x\n",
*name, *(name+1), *(name+2), *(name+3),
*(name+4), *(name+5), *(name+6), *(name+7),
ndlp->nlp_DID, ndlp->nlp_flag,
ndlp->nlp_state, ndlp->nlp_rpi);
}
+ clear_bit(NLP_IN_DEV_LOSS, &ndlp->nlp_flag);
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- if (put_node)
- lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
-
- if (!(vport->load_flag & FC_UNLOADING) &&
- !(ndlp->nlp_flag & NLP_DELAY_TMO) &&
- !(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
- (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
- (ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE) &&
- (ndlp->nlp_state != NLP_STE_PRLI_ISSUE))
+ /* If we are devloss, but we are in the process of rediscovering the
+ * ndlp, don't issue a NLP_EVT_DEVICE_RM event.
+ */
+ if (ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE &&
+ ndlp->nlp_state <= NLP_STE_PRLI_ISSUE) {
+ return fcf_inuse;
+ }
+
+ if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD))
lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
return fcf_inuse;
}
+static void lpfc_check_vmid_qfpa_issue(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport;
+ struct lpfc_vport **vports;
+ int i;
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (!vports)
+ return;
+
+ for (i = 0; i <= phba->max_vports; i++) {
+ if ((!vports[i]) && (i == 0))
+ vport = phba->pport;
+ else
+ vport = vports[i];
+ if (!vport)
+ break;
+
+ if (vport->vmid_flag & LPFC_VMID_ISSUE_QFPA) {
+ if (!lpfc_issue_els_qfpa(vport))
+ vport->vmid_flag &= ~LPFC_VMID_ISSUE_QFPA;
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
/**
* lpfc_sli4_post_dev_loss_tmo_handler - SLI4 post devloss timeout handler
* @phba: Pointer to hba context object.
@@ -323,14 +667,15 @@ lpfc_sli4_post_dev_loss_tmo_handler(struct lpfc_hba *phba, int fcf_inuse,
if (!fcf_inuse)
return;
- if ((phba->hba_flag & HBA_FIP_SUPPORT) && !lpfc_fcf_inuse(phba)) {
+ if (test_bit(HBA_FIP_SUPPORT, &phba->hba_flag) &&
+ !lpfc_fcf_inuse(phba)) {
spin_lock_irq(&phba->hbalock);
if (phba->fcf.fcf_flag & FCF_DISCOVERY) {
- if (phba->hba_flag & HBA_DEVLOSS_TMO) {
+ if (test_and_set_bit(HBA_DEVLOSS_TMO,
+ &phba->hba_flag)) {
spin_unlock_irq(&phba->hbalock);
return;
}
- phba->hba_flag |= HBA_DEVLOSS_TMO;
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2847 Last remote node (x%x) using "
"FCF devloss tmo\n", nlp_did);
@@ -342,8 +687,9 @@ lpfc_sli4_post_dev_loss_tmo_handler(struct lpfc_hba *phba, int fcf_inuse,
"in progress\n");
return;
}
- if (!(phba->hba_flag & (FCF_TS_INPROG | FCF_RR_INPROG))) {
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(&phba->hbalock);
+ if (!test_bit(FCF_TS_INPROG, &phba->hba_flag) &&
+ !test_bit(FCF_RR_INPROG, &phba->hba_flag)) {
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2869 Devloss tmo to idle FIP engine, "
"unreg in-use FCF and rescan.\n");
@@ -351,11 +697,10 @@ lpfc_sli4_post_dev_loss_tmo_handler(struct lpfc_hba *phba, int fcf_inuse,
lpfc_unregister_fcf_rescan(phba);
return;
}
- spin_unlock_irq(&phba->hbalock);
- if (phba->hba_flag & FCF_TS_INPROG)
+ if (test_bit(FCF_TS_INPROG, &phba->hba_flag))
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2870 FCF table scan in progress\n");
- if (phba->hba_flag & FCF_RR_INPROG)
+ if (test_bit(FCF_RR_INPROG, &phba->hba_flag))
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2871 FLOGI roundrobin FCF failover "
"in progress\n");
@@ -472,11 +817,12 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
return;
}
- fc_host_post_vendor_event(shost,
- fc_get_event_number(),
- evt_data_size,
- evt_data,
- LPFC_NL_VENDOR_ID);
+ if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ evt_data_size,
+ evt_data,
+ LPFC_NL_VENDOR_ID);
lpfc_free_fast_evt(phba, fast_evt_data);
return;
@@ -490,18 +836,22 @@ lpfc_work_list_done(struct lpfc_hba *phba)
int free_evt;
int fcf_inuse;
uint32_t nlp_did;
+ bool hba_pci_err;
spin_lock_irq(&phba->hbalock);
while (!list_empty(&phba->work_list)) {
list_remove_head((&phba->work_list), evtp, typeof(*evtp),
evt_listp);
spin_unlock_irq(&phba->hbalock);
+ hba_pci_err = test_bit(HBA_PCI_ERR, &phba->bit_flags);
free_evt = 1;
switch (evtp->evt) {
case LPFC_EVT_ELS_RETRY:
ndlp = (struct lpfc_nodelist *) (evtp->evt_arg1);
- lpfc_els_retry_delay_handler(ndlp);
- free_evt = 0; /* evt is part of ndlp */
+ if (!hba_pci_err) {
+ lpfc_els_retry_delay_handler(ndlp);
+ free_evt = 0; /* evt is part of ndlp */
+ }
/* decrement the node reference count held
* for this queued work
*/
@@ -521,6 +871,17 @@ lpfc_work_list_done(struct lpfc_hba *phba)
fcf_inuse,
nlp_did);
break;
+ case LPFC_EVT_RECOVER_PORT:
+ ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);
+ if (!hba_pci_err) {
+ lpfc_sli_abts_recover_port(ndlp->vport, ndlp);
+ free_evt = 0;
+ }
+ /* decrement the node reference count held for
+ * this queued work
+ */
+ lpfc_nlp_put(ndlp);
+ break;
case LPFC_EVT_ONLINE:
if (phba->link_state < LPFC_LINK_DOWN)
*(int *) (evtp->evt_arg1) = lpfc_online(phba);
@@ -530,7 +891,7 @@ lpfc_work_list_done(struct lpfc_hba *phba)
break;
case LPFC_EVT_OFFLINE_PREP:
if (phba->link_state >= LPFC_LINK_DOWN)
- lpfc_offline_prep(phba);
+ lpfc_offline_prep(phba, LPFC_MBX_WAIT);
*(int *)(evtp->evt_arg1) = 0;
complete((struct completion *)(evtp->evt_arg2));
break;
@@ -565,7 +926,7 @@ lpfc_work_list_done(struct lpfc_hba *phba)
free_evt = 0;
break;
case LPFC_EVT_RESET_HBA:
- if (!(phba->pport->load_flag & FC_UNLOADING))
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
lpfc_reset_hba(phba);
break;
}
@@ -585,42 +946,63 @@ lpfc_work_done(struct lpfc_hba *phba)
struct lpfc_vport **vports;
struct lpfc_vport *vport;
int i;
+ bool hba_pci_err;
+ hba_pci_err = test_bit(HBA_PCI_ERR, &phba->bit_flags);
spin_lock_irq(&phba->hbalock);
ha_copy = phba->work_ha;
phba->work_ha = 0;
spin_unlock_irq(&phba->hbalock);
+ if (hba_pci_err)
+ ha_copy = 0;
/* First, try to post the next mailbox command to SLI4 device */
- if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC && !hba_pci_err)
lpfc_sli4_post_async_mbox(phba);
- if (ha_copy & HA_ERATT)
+ if (ha_copy & HA_ERATT) {
/* Handle the error attention event */
lpfc_handle_eratt(phba);
+ if (phba->fw_dump_cmpl) {
+ complete(phba->fw_dump_cmpl);
+ phba->fw_dump_cmpl = NULL;
+ }
+ }
+
if (ha_copy & HA_MBATT)
lpfc_sli_handle_mb_event(phba);
if (ha_copy & HA_LATT)
lpfc_handle_latt(phba);
+ /* Handle VMID Events */
+ if (lpfc_is_vmid_enabled(phba) && !hba_pci_err) {
+ if (phba->pport->work_port_events &
+ WORKER_CHECK_VMID_ISSUE_QFPA) {
+ lpfc_check_vmid_qfpa_issue(phba);
+ phba->pport->work_port_events &=
+ ~WORKER_CHECK_VMID_ISSUE_QFPA;
+ }
+ if (phba->pport->work_port_events &
+ WORKER_CHECK_INACTIVE_VMID) {
+ lpfc_check_inactive_vmid(phba);
+ phba->pport->work_port_events &=
+ ~WORKER_CHECK_INACTIVE_VMID;
+ }
+ }
+
/* Process SLI4 events */
if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
- if (phba->hba_flag & HBA_RRQ_ACTIVE)
+ if (test_bit(HBA_RRQ_ACTIVE, &phba->hba_flag))
lpfc_handle_rrq_active(phba);
- if (phba->hba_flag & FCP_XRI_ABORT_EVENT)
- lpfc_sli4_fcp_xri_abort_event_proc(phba);
- if (phba->hba_flag & ELS_XRI_ABORT_EVENT)
+ if (test_bit(ELS_XRI_ABORT_EVENT, &phba->hba_flag))
lpfc_sli4_els_xri_abort_event_proc(phba);
- if (phba->hba_flag & ASYNC_EVENT)
+ if (test_bit(ASYNC_EVENT, &phba->hba_flag))
lpfc_sli4_async_event_proc(phba);
- if (phba->hba_flag & HBA_POST_RECEIVE_BUFFER) {
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~HBA_POST_RECEIVE_BUFFER;
- spin_unlock_irq(&phba->hbalock);
+ if (test_and_clear_bit(HBA_POST_RECEIVE_BUFFER,
+ &phba->hba_flag))
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
- }
if (phba->fcf.fcf_flag & FCF_REDISC_EVT)
lpfc_sli4_fcf_redisc_event_proc(phba);
}
@@ -642,6 +1024,8 @@ lpfc_work_done(struct lpfc_hba *phba)
work_port_events = vport->work_port_events;
vport->work_port_events &= ~work_port_events;
spin_unlock_irq(&vport->work_port_lock);
+ if (hba_pci_err)
+ continue;
if (work_port_events & WORKER_DISC_TMO)
lpfc_disc_timeout_handler(vport);
if (work_port_events & WORKER_ELS_TMO)
@@ -652,34 +1036,37 @@ lpfc_work_done(struct lpfc_hba *phba)
lpfc_mbox_timeout_handler(phba);
if (work_port_events & WORKER_FABRIC_BLOCK_TMO)
lpfc_unblock_fabric_iocbs(phba);
- if (work_port_events & WORKER_FDMI_TMO)
- lpfc_fdmi_timeout_handler(vport);
if (work_port_events & WORKER_RAMP_DOWN_QUEUE)
lpfc_ramp_down_queue_handler(phba);
- if (work_port_events & WORKER_RAMP_UP_QUEUE)
- lpfc_ramp_up_queue_handler(phba);
if (work_port_events & WORKER_DELAYED_DISC_TMO)
lpfc_delayed_disc_timeout_handler(vport);
}
lpfc_destroy_vport_work_array(phba, vports);
- pring = &phba->sli.ring[LPFC_ELS_RING];
+ pring = lpfc_phba_elsring(phba);
status = (ha_copy & (HA_RXMASK << (4*LPFC_ELS_RING)));
status >>= (4*LPFC_ELS_RING);
- if ((status & HA_RXMASK) ||
- (pring->flag & LPFC_DEFERRED_RING_EVENT) ||
- (phba->hba_flag & HBA_SP_QUEUE_EVT)) {
+ if (pring && (status & HA_RXMASK ||
+ pring->flag & LPFC_DEFERRED_RING_EVENT ||
+ test_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag))) {
if (pring->flag & LPFC_STOP_IOCB_EVENT) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
- /* Set the lpfc data pending flag */
- set_bit(LPFC_DATA_READY, &phba->data_flags);
+ /* Preserve legacy behavior. */
+ if (!test_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag))
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
} else {
- pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
- lpfc_sli_handle_slow_ring_event(phba, pring,
- (status &
- HA_RXMASK));
+ /* Driver could have abort request completed in queue
+ * when link goes down. Allow for this transition.
+ */
+ if (phba->link_state >= LPFC_LINK_DOWN ||
+ phba->link_flag & LS_MDS_LOOPBACK) {
+ pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
+ lpfc_sli_handle_slow_ring_event(phba, pring,
+ (status &
+ HA_RXMASK));
+ }
}
- if ((phba->sli_rev == LPFC_SLI_REV4) && pring->txq_cnt)
+ if (phba->sli_rev == LPFC_SLI_REV4)
lpfc_drain_txq(phba);
/*
* Turn on Ring interrupts
@@ -712,7 +1099,8 @@ lpfc_do_work(void *p)
struct lpfc_hba *phba = p;
int rc;
- set_user_nice(current, -20);
+ set_user_nice(current, MIN_NICE);
+ current->flags |= PF_NOFREEZE;
phba->data_flags = 0;
while (!kthread_should_stop()) {
@@ -723,7 +1111,7 @@ lpfc_do_work(void *p)
|| kthread_should_stop()));
/* Signal wakeup shall terminate the worker thread */
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0433 Wakeup on signal: rc=x%x\n", rc);
break;
}
@@ -773,37 +1161,37 @@ lpfc_workq_post_event(struct lpfc_hba *phba, void *arg1, void *arg2,
void
lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp, *next_ndlp;
- int rc;
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
- if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
- continue;
if ((phba->sli3_options & LPFC_SLI3_VPORT_TEARDOWN) ||
- ((vport->port_type == LPFC_NPIV_PORT) &&
- (ndlp->nlp_DID == NameServer_DID)))
+ ((vport->port_type == LPFC_NPIV_PORT) &&
+ ((ndlp->nlp_DID == NameServer_DID) ||
+ (ndlp->nlp_DID == FDMI_DID) ||
+ (ndlp->nlp_DID == Fabric_Cntl_DID))))
lpfc_unreg_rpi(vport, ndlp);
/* Leave Fabric nodes alone on link down */
if ((phba->sli_rev < LPFC_SLI_REV4) &&
(!remove && ndlp->nlp_type & NLP_FABRIC))
continue;
- rc = lpfc_disc_state_machine(vport, ndlp, NULL,
- remove
- ? NLP_EVT_DEVICE_RM
- : NLP_EVT_DEVICE_RECOVERY);
+
+ /* Notify transport of connectivity loss to trigger cleanup. */
+ if (phba->nvmet_support &&
+ ndlp->nlp_state == NLP_STE_UNMAPPED_NODE)
+ lpfc_nvmet_invalidate_host(phba, ndlp);
+
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ remove
+ ? NLP_EVT_DEVICE_RM
+ : NLP_EVT_DEVICE_RECOVERY);
}
if (phba->sli3_options & LPFC_SLI3_VPORT_TEARDOWN) {
if (phba->sli_rev == LPFC_SLI_REV4)
lpfc_sli4_unreg_all_rpis(vport);
lpfc_mbx_unreg_vpi(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
}
}
@@ -830,9 +1218,12 @@ lpfc_port_link_failure(struct lpfc_vport *vport)
void
lpfc_linkdown_port(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_hba *phba = vport->phba;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_LINKDOWN, 0);
+ if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
+ fc_host_post_event(shost, fc_get_event_number(),
+ FCH_EVT_LINKDOWN, 0);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"Link Down: state:x%x rtry:x%x flg:x%x",
@@ -841,44 +1232,90 @@ lpfc_linkdown_port(struct lpfc_vport *vport)
lpfc_port_link_failure(vport);
/* Stop delayed Nport discovery */
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_DISC_DELAYED;
- spin_unlock_irq(shost->host_lock);
- del_timer_sync(&vport->delayed_disc_tmo);
+ clear_bit(FC_DISC_DELAYED, &vport->fc_flag);
+ timer_delete_sync(&vport->delayed_disc_tmo);
+
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ vport->port_type == LPFC_PHYSICAL_PORT &&
+ phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_CONFIG) {
+ /* Assume success on link up */
+ phba->sli4_hba.fawwpn_flag |= LPFC_FAWWPN_FABRIC;
+ }
}
int
lpfc_linkdown(struct lpfc_hba *phba)
{
struct lpfc_vport *vport = phba->pport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_vport **vports;
LPFC_MBOXQ_t *mb;
int i;
+ int offline;
if (phba->link_state == LPFC_LINK_DOWN)
return 0;
/* Block all SCSI stack I/Os */
lpfc_scsi_dev_block(phba);
+ offline = pci_channel_offline(phba->pcidev);
+
+ /* Decrement the held ndlp if there is a deferred flogi acc */
+ if (phba->defer_flogi_acc.flag) {
+ if (phba->defer_flogi_acc.ndlp) {
+ lpfc_nlp_put(phba->defer_flogi_acc.ndlp);
+ phba->defer_flogi_acc.ndlp = NULL;
+ }
+ }
+ phba->defer_flogi_acc.flag = false;
+
+ /* reinitialize initial HBA flag */
+ clear_bit(HBA_FLOGI_ISSUED, &phba->hba_flag);
+ clear_bit(HBA_RHBA_CMPL, &phba->hba_flag);
+
+ /* Clear external loopback plug detected flag */
+ phba->link_flag &= ~LS_EXTERNAL_LOOPBACK;
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_SCAN_DONE);
spin_unlock_irq(&phba->hbalock);
if (phba->link_state > LPFC_LINK_DOWN) {
phba->link_state = LPFC_LINK_DOWN;
- spin_lock_irq(shost->host_lock);
- phba->pport->fc_flag &= ~FC_LBIT;
- spin_unlock_irq(shost->host_lock);
+ if (phba->sli4_hba.conf_trunk) {
+ phba->trunk_link.link0.state = 0;
+ phba->trunk_link.link1.state = 0;
+ phba->trunk_link.link2.state = 0;
+ phba->trunk_link.link3.state = 0;
+ phba->trunk_link.phy_lnk_speed =
+ LPFC_LINK_SPEED_UNKNOWN;
+ phba->sli4_hba.link_state.logical_speed =
+ LPFC_LINK_SPEED_UNKNOWN;
+ }
+ clear_bit(FC_LBIT, &phba->pport->fc_flag);
}
vports = lpfc_create_vport_work_array(phba);
- if (vports != NULL)
+ if (vports != NULL) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
/* Issue a LINK DOWN event to all nodes */
lpfc_linkdown_port(vports[i]);
+
+ vports[i]->fc_myDID = 0;
+
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if (phba->nvmet_support)
+ lpfc_nvmet_update_targetport(phba);
+ else
+ lpfc_nvme_update_localport(vports[i]);
+ }
}
+ }
lpfc_destroy_vport_work_array(phba, vports);
- /* Clean up any firmware default rpi's */
+
+ /* Clean up any SLI3 firmware default rpi's */
+ if (phba->sli_rev > LPFC_SLI_REV3 || offline)
+ goto skip_unreg_did;
+
mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mb) {
lpfc_unreg_did(phba, 0xffff, LPFC_UNREG_ALL_DFLT_RPIS, mb);
@@ -890,9 +1327,9 @@ lpfc_linkdown(struct lpfc_hba *phba)
}
}
+ skip_unreg_did:
/* Setup myDID for link up if we are in pt2pt mode */
- if (phba->pport->fc_flag & FC_PT2PT) {
- phba->pport->fc_myDID = 0;
+ if (test_bit(FC_PT2PT, &phba->pport->fc_flag)) {
mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mb) {
lpfc_config_link(phba, mb);
@@ -903,11 +1340,12 @@ lpfc_linkdown(struct lpfc_hba *phba)
mempool_free(mb, phba->mbox_mem_pool);
}
}
+ clear_bit(FC_PT2PT, &phba->pport->fc_flag);
+ clear_bit(FC_PT2PT_PLOGI, &phba->pport->fc_flag);
spin_lock_irq(shost->host_lock);
- phba->pport->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI);
+ phba->pport->rcv_flogi_cnt = 0;
spin_unlock_irq(shost->host_lock);
}
-
return 0;
}
@@ -917,8 +1355,8 @@ lpfc_linkup_cleanup_nodes(struct lpfc_vport *vport)
struct lpfc_nodelist *ndlp;
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
+
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
continue;
if (ndlp->nlp_type & NLP_FABRIC) {
@@ -928,7 +1366,7 @@ lpfc_linkup_cleanup_nodes(struct lpfc_vport *vport)
if (ndlp->nlp_DID != Fabric_DID)
lpfc_unreg_rpi(vport, ndlp);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
+ } else if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) {
/* Fail outstanding IO now since device is
* marked for PLOGI.
*/
@@ -943,7 +1381,7 @@ lpfc_linkup_port(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- if ((vport->load_flag & FC_UNLOADING) != 0)
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
return;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
@@ -955,18 +1393,27 @@ lpfc_linkup_port(struct lpfc_vport *vport)
(vport != phba->pport))
return;
- fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_LINKUP, 0);
+ if (phba->defer_flogi_acc.flag) {
+ clear_bit(FC_ABORT_DISCOVERY, &vport->fc_flag);
+ clear_bit(FC_RSCN_MODE, &vport->fc_flag);
+ clear_bit(FC_NLP_MORE, &vport->fc_flag);
+ clear_bit(FC_RSCN_DISCOVERY, &vport->fc_flag);
+ } else {
+ clear_bit(FC_PT2PT, &vport->fc_flag);
+ clear_bit(FC_PT2PT_PLOGI, &vport->fc_flag);
+ clear_bit(FC_ABORT_DISCOVERY, &vport->fc_flag);
+ clear_bit(FC_RSCN_MODE, &vport->fc_flag);
+ clear_bit(FC_NLP_MORE, &vport->fc_flag);
+ clear_bit(FC_RSCN_DISCOVERY, &vport->fc_flag);
+ }
+ set_bit(FC_NDISC_ACTIVE, &vport->fc_flag);
spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY |
- FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY);
- vport->fc_flag |= FC_NDISC_ACTIVE;
vport->fc_ns_retry = 0;
spin_unlock_irq(shost->host_lock);
+ lpfc_setup_fdmi_mask(vport);
- if (vport->fc_flag & FC_LBIT)
- lpfc_linkup_cleanup_nodes(vport);
-
+ lpfc_linkup_cleanup_nodes(vport);
}
static int
@@ -974,22 +1421,27 @@ lpfc_linkup(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
int i;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(phba->pport);
- lpfc_cleanup_wt_rrqs(phba);
phba->link_state = LPFC_LINK_UP;
/* Unblock fabric iocbs if they are blocked */
clear_bit(FABRIC_COMANDS_BLOCKED, &phba->bit_flags);
- del_timer_sync(&phba->fabric_block_timer);
+ timer_delete_sync(&phba->fabric_block_timer);
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
lpfc_linkup_port(vports[i]);
lpfc_destroy_vport_work_array(phba, vports);
- if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
- (phba->sli_rev < LPFC_SLI_REV4))
- lpfc_issue_clear_la(phba, phba->pport);
+
+ /* Clear the pport flogi counter in case the link down was
+ * absorbed without an ACQE. No lock here - in worker thread
+ * and discovery is synchronized.
+ */
+ spin_lock_irq(shost->host_lock);
+ phba->pport->rcv_flogi_cnt = 0;
+ spin_unlock_irq(shost->host_lock);
return 0;
}
@@ -998,26 +1450,24 @@ lpfc_linkup(struct lpfc_hba *phba)
* This routine handles processing a CLEAR_LA mailbox
* command upon completion. It is setup in the LPFC_MBOXQ
* as the completion routine when the command is
- * handed off to the SLI layer.
+ * handed off to the SLI layer. SLI3 only.
*/
static void
lpfc_mbx_cmpl_clear_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_sli *psli = &phba->sli;
MAILBOX_t *mb = &pmb->u.mb;
uint32_t control;
/* Since we don't do discovery right now, turn these off here */
- psli->ring[psli->extra_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
- psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
- psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
+ psli->sli3_ring[LPFC_EXTRA_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
+ psli->sli3_ring[LPFC_FCP_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
/* Check for error */
if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) {
/* CLEAR_LA mbox error <mbxStatus> state <hba_state> */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0320 CLEAR_LA mbxStatus error x%x hba "
"state x%x\n",
mb->mbxStatus, vport->port_state);
@@ -1044,9 +1494,7 @@ out:
"0225 Device Discovery completes\n");
mempool_free(pmb, phba->mbox_mem_pool);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_ABORT_DISCOVERY;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_ABORT_DISCOVERY, &vport->fc_flag);
lpfc_can_disctmo(vport);
@@ -1063,20 +1511,28 @@ out:
return;
}
-
-static void
+void
lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
+ LPFC_MBOXQ_t *sparam_mb;
+ u16 status = pmb->u.mb.mbxStatus;
+ int rc;
- if (pmb->u.mb.mbxStatus)
+ mempool_free(pmb, phba->mbox_mem_pool);
+
+ if (status)
goto out;
- mempool_free(pmb, phba->mbox_mem_pool);
+ /* don't perform discovery for SLI4 loopback diagnostic test */
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ !test_bit(HBA_FCOE_MODE, &phba->hba_flag) &&
+ (phba->link_flag & LS_LOOPBACK_MODE))
+ return;
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP &&
- vport->fc_flag & FC_PUBLIC_LOOP &&
- !(vport->fc_flag & FC_LBIT)) {
+ test_bit(FC_PUBLIC_LOOP, &vport->fc_flag) &&
+ !test_bit(FC_LBIT, &vport->fc_flag)) {
/* Need to wait for FAN - use discovery timer
* for timeout. port_state is identically
* LPFC_LOCAL_CFG_LINK while waiting for FAN
@@ -1086,22 +1542,52 @@ lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
/* Start discovery by sending a FLOGI. port_state is identically
- * LPFC_FLOGI while waiting for FLOGI cmpl
+ * LPFC_FLOGI while waiting for FLOGI cmpl.
*/
- if (vport->port_state != LPFC_FLOGI)
- lpfc_initial_flogi(vport);
+ if (vport->port_state != LPFC_FLOGI) {
+ /* Issue MBX_READ_SPARAM to update CSPs before FLOGI if
+ * bb-credit recovery is in place.
+ */
+ if (phba->bbcredit_support && phba->cfg_enable_bbcr &&
+ !(phba->link_flag & LS_LOOPBACK_MODE)) {
+ sparam_mb = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!sparam_mb)
+ goto sparam_out;
+
+ rc = lpfc_read_sparam(phba, sparam_mb, 0);
+ if (rc) {
+ mempool_free(sparam_mb, phba->mbox_mem_pool);
+ goto sparam_out;
+ }
+ sparam_mb->vport = vport;
+ sparam_mb->mbox_cmpl = lpfc_mbx_cmpl_read_sparam;
+ rc = lpfc_sli_issue_mbox(phba, sparam_mb, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_mbox_rsrc_cleanup(phba, sparam_mb,
+ MBOX_THD_UNLOCKED);
+ goto sparam_out;
+ }
+
+ set_bit(HBA_DEFER_FLOGI, &phba->hba_flag);
+ } else {
+ lpfc_initial_flogi(vport);
+ }
+ } else {
+ if (test_bit(FC_PT2PT, &vport->fc_flag))
+ lpfc_disc_start(vport);
+ }
return;
out:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
- "0306 CONFIG_LINK mbxStatus error x%x "
- "HBA state x%x\n",
- pmb->u.mb.mbxStatus, vport->port_state);
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0306 CONFIG_LINK mbxStatus error x%x HBA state x%x\n",
+ status, vport->port_state);
+sparam_out:
lpfc_linkdown(phba);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0200 CONFIG_LINK bad hba state x%x\n",
vport->port_state);
@@ -1109,16 +1595,38 @@ out:
return;
}
+/**
+ * lpfc_sli4_clear_fcf_rr_bmask
+ * @phba: pointer to the struct lpfc_hba for this port.
+ * This fucnction resets the round robin bit mask and clears the
+ * fcf priority list. The list deletions are done while holding the
+ * hbalock. The ON_LIST flag and the FLOGI_FAILED flags are cleared
+ * from the lpfc_fcf_pri record.
+ **/
+void
+lpfc_sli4_clear_fcf_rr_bmask(struct lpfc_hba *phba)
+{
+ struct lpfc_fcf_pri *fcf_pri;
+ struct lpfc_fcf_pri *next_fcf_pri;
+ memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask));
+ spin_lock_irq(&phba->hbalock);
+ list_for_each_entry_safe(fcf_pri, next_fcf_pri,
+ &phba->fcf.fcf_pri_list, list) {
+ list_del_init(&fcf_pri->list);
+ fcf_pri->fcf_rec.flag = 0;
+ }
+ spin_unlock_irq(&phba->hbalock);
+}
static void
lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
struct lpfc_vport *vport = mboxq->vport;
if (mboxq->u.mb.mbxStatus) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
- "2017 REG_FCFI mbxStatus error x%x "
- "HBA state x%x\n",
- mboxq->u.mb.mbxStatus, vport->port_state);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2017 REG_FCFI mbxStatus error x%x "
+ "HBA state x%x\n", mboxq->u.mb.mbxStatus,
+ vport->port_state);
goto fail_out;
}
@@ -1130,26 +1638,23 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
spin_unlock_irq(&phba->hbalock);
/* If there is a pending FCoE event, restart FCF table scan. */
- if (lpfc_check_pending_fcoe_event(phba, LPFC_UNREG_FCF))
+ if (!test_bit(FCF_RR_INPROG, &phba->hba_flag) &&
+ lpfc_check_pending_fcoe_event(phba, LPFC_UNREG_FCF))
goto fail_out;
/* Mark successful completion of FCF table scan */
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag |= (FCF_SCAN_DONE | FCF_IN_USE);
- phba->hba_flag &= ~FCF_TS_INPROG;
+ spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
if (vport->port_state != LPFC_FLOGI) {
- phba->hba_flag |= FCF_RR_INPROG;
- spin_unlock_irq(&phba->hbalock);
+ set_bit(FCF_RR_INPROG, &phba->hba_flag);
lpfc_issue_init_vfi(vport);
- goto out;
}
- spin_unlock_irq(&phba->hbalock);
goto out;
fail_out:
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~FCF_RR_INPROG;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
out:
mempool_free(mboxq, phba->mbox_mem_pool);
}
@@ -1187,7 +1692,7 @@ lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record)
/**
* lpfc_sw_name_match - Check if the fcf switch name match.
- * @fab_name: pointer to fabric name.
+ * @sw_name: pointer to switch name.
* @new_fcf_record: pointer to fcf record.
*
* This routine compare the fcf record's switch name with provided
@@ -1250,8 +1755,32 @@ lpfc_vlan_id_match(uint16_t curr_vlan_id, uint16_t new_vlan_id)
}
/**
+ * __lpfc_update_fcf_record_pri - update the lpfc_fcf_pri record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: Index for the lpfc_fcf_record.
+ * @new_fcf_record: pointer to hba fcf record.
+ *
+ * This routine updates the driver FCF priority record from the new HBA FCF
+ * record. The hbalock is asserted held in the code path calling this
+ * routine.
+ **/
+static void
+__lpfc_update_fcf_record_pri(struct lpfc_hba *phba, uint16_t fcf_index,
+ struct fcf_record *new_fcf_record
+ )
+{
+ struct lpfc_fcf_pri *fcf_pri;
+
+ fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+ fcf_pri->fcf_rec.fcf_index = fcf_index;
+ /* FCF record priority */
+ fcf_pri->fcf_rec.priority = new_fcf_record->fip_priority;
+
+}
+
+/**
* lpfc_copy_fcf_record - Copy fcf information to lpfc_hba.
- * @fcf: pointer to driver fcf record.
+ * @fcf_rec: pointer to driver fcf record.
* @new_fcf_record: pointer to fcf record.
*
* This routine copies the FCF information from the FCF
@@ -1309,7 +1838,7 @@ lpfc_copy_fcf_record(struct lpfc_fcf_rec *fcf_rec,
}
/**
- * lpfc_update_fcf_record - Update driver fcf record
+ * __lpfc_update_fcf_record - Update driver fcf record
* @phba: pointer to lpfc hba data structure.
* @fcf_rec: pointer to driver fcf record.
* @new_fcf_record: pointer to hba fcf record.
@@ -1319,19 +1848,24 @@ lpfc_copy_fcf_record(struct lpfc_fcf_rec *fcf_rec,
*
* This routine updates the driver FCF record from the new HBA FCF record
* together with the address mode, vlan_id, and other informations. This
- * routine is called with the host lock held.
+ * routine is called with the hbalock held.
**/
static void
__lpfc_update_fcf_record(struct lpfc_hba *phba, struct lpfc_fcf_rec *fcf_rec,
struct fcf_record *new_fcf_record, uint32_t addr_mode,
uint16_t vlan_id, uint32_t flag)
{
+ lockdep_assert_held(&phba->hbalock);
+
/* Copy the fields from the HBA's FCF record */
lpfc_copy_fcf_record(fcf_rec, new_fcf_record);
/* Update other fields of driver FCF record */
fcf_rec->addr_mode = addr_mode;
fcf_rec->vlan_id = vlan_id;
fcf_rec->flag |= (flag | RECORD_VALID);
+ __lpfc_update_fcf_record_pri(phba,
+ bf_get(lpfc_fcf_record_fcf_index, new_fcf_record),
+ new_fcf_record);
}
/**
@@ -1350,31 +1884,31 @@ lpfc_register_fcf(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
/* If the FCF is not available do nothing. */
if (!(phba->fcf.fcf_flag & FCF_AVAILABLE)) {
- phba->hba_flag &= ~(FCF_TS_INPROG | FCF_RR_INPROG);
spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
return;
}
/* The FCF is already registered, start discovery */
if (phba->fcf.fcf_flag & FCF_REGISTERED) {
phba->fcf.fcf_flag |= (FCF_SCAN_DONE | FCF_IN_USE);
- phba->hba_flag &= ~FCF_TS_INPROG;
- if (phba->pport->port_state != LPFC_FLOGI) {
- phba->hba_flag |= FCF_RR_INPROG;
- spin_unlock_irq(&phba->hbalock);
- lpfc_issue_init_vfi(phba->pport);
+ spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
+ if (phba->pport->port_state != LPFC_FLOGI &&
+ test_bit(FC_FABRIC, &phba->pport->fc_flag)) {
+ set_bit(FCF_RR_INPROG, &phba->hba_flag);
+ lpfc_initial_flogi(phba->pport);
return;
}
- spin_unlock_irq(&phba->hbalock);
return;
}
spin_unlock_irq(&phba->hbalock);
fcf_mbxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!fcf_mbxq) {
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~(FCF_TS_INPROG | FCF_RR_INPROG);
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
return;
}
@@ -1383,9 +1917,8 @@ lpfc_register_fcf(struct lpfc_hba *phba)
fcf_mbxq->mbox_cmpl = lpfc_mbx_cmpl_reg_fcfi;
rc = lpfc_sli_issue_mbox(phba, fcf_mbxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~(FCF_TS_INPROG | FCF_RR_INPROG);
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
mempool_free(fcf_mbxq, phba->mbox_mem_pool);
}
@@ -1432,12 +1965,13 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
}
}
- /* If FCF not available return 0 */
+ /* FCF not valid/available or solicitation in progress */
if (!bf_get(lpfc_fcf_record_fcf_avail, new_fcf_record) ||
- !bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record))
+ !bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record) ||
+ bf_get(lpfc_fcf_record_fcf_sol, new_fcf_record))
return 0;
- if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
+ if (!test_bit(HBA_FIP_SUPPORT, &phba->hba_flag)) {
*boot_flag = 0;
*addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
new_fcf_record);
@@ -1632,8 +2166,9 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_DISCOVERY,
"2833 Stop FCF discovery process due to link "
"state change (x%x)\n", phba->link_state);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~(FCF_TS_INPROG | FCF_RR_INPROG);
phba->fcf.fcf_flag &= ~(FCF_REDISC_FOV | FCF_DISCOVERY);
spin_unlock_irq(&phba->hbalock);
}
@@ -1656,8 +2191,8 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
* This function makes an running random selection decision on FCF record to
* use through a sequence of @fcf_cnt eligible FCF records with equal
* probability. To perform integer manunipulation of random numbers with
- * size unit32_t, the lower 16 bits of the 32-bit random number returned
- * from random32() are taken as the random random number generated.
+ * size unit32_t, a 16-bit random number returned from get_random_u16() is
+ * taken as the random random number generated.
*
* Returns true when outcome is for the newly read FCF record should be
* chosen; otherwise, return false when outcome is for keeping the previously
@@ -1669,7 +2204,7 @@ lpfc_sli4_new_fcf_random_select(struct lpfc_hba *phba, uint32_t fcf_cnt)
uint32_t rand_num;
/* Get 16-bit uniform random number */
- rand_num = (0xFFFF & random32());
+ rand_num = get_random_u16();
/* Decision with probability 1/fcf_cnt */
if ((fcf_cnt * rand_num) < 0xFFFF)
@@ -1696,10 +2231,9 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
uint16_t *next_fcf_index)
{
void *virt_addr;
- dma_addr_t phys_addr;
struct lpfc_mbx_sge sge;
struct lpfc_mbx_read_fcf_tbl *read_fcf;
- uint32_t shdr_status, shdr_add_status;
+ uint32_t shdr_status, shdr_add_status, if_type;
union lpfc_sli4_cfg_shdr *shdr;
struct fcf_record *new_fcf_record;
@@ -1707,9 +2241,8 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
* routine only uses a single SGE.
*/
lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
- phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
if (unlikely(!mboxq->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2524 Failed to get the non-embedded SGE "
"virtual address\n");
return NULL;
@@ -1717,15 +2250,20 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
virt_addr = mboxq->sge_array->addr[0];
shdr = (union lpfc_sli4_cfg_shdr *)virt_addr;
+ lpfc_sli_pcimem_bcopy(shdr, shdr,
+ sizeof(union lpfc_sli4_cfg_shdr));
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status) {
- if (shdr_status == STATUS_FCF_TABLE_EMPTY)
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ if (shdr_status == STATUS_FCF_TABLE_EMPTY ||
+ if_type == LPFC_SLI_INTF_IF_TYPE_2)
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
"2726 READ_FCF_RECORD Indicates empty "
"FCF table.\n");
else
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2521 READ_FCF_RECORD mailbox failed "
"with status x%x add_status x%x, "
"mbx\n", shdr_status, shdr_add_status);
@@ -1768,6 +2306,7 @@ lpfc_sli4_log_fcf_record_info(struct lpfc_hba *phba,
"\tFCF_Index : x%x\n"
"\tFCF_Avail : x%x\n"
"\tFCF_Valid : x%x\n"
+ "\tFCF_SOL : x%x\n"
"\tFIP_Priority : x%x\n"
"\tMAC_Provider : x%x\n"
"\tLowest VLANID : x%x\n"
@@ -1778,6 +2317,7 @@ lpfc_sli4_log_fcf_record_info(struct lpfc_hba *phba,
bf_get(lpfc_fcf_record_fcf_index, fcf_record),
bf_get(lpfc_fcf_record_fcf_avail, fcf_record),
bf_get(lpfc_fcf_record_fcf_valid, fcf_record),
+ bf_get(lpfc_fcf_record_fcf_sol, fcf_record),
fcf_record->fip_priority,
bf_get(lpfc_fcf_record_mac_addr_prov, fcf_record),
vlan_id,
@@ -1807,7 +2347,7 @@ lpfc_sli4_log_fcf_record_info(struct lpfc_hba *phba,
}
/**
- lpfc_sli4_fcf_record_match - testing new FCF record for matching existing FCF
+ * lpfc_sli4_fcf_record_match - testing new FCF record for matching existing FCF
* @phba: pointer to lpfc hba data structure.
* @fcf_rec: pointer to an existing FCF record.
* @new_fcf_record: pointer to a new FCF record.
@@ -1834,6 +2374,8 @@ lpfc_sli4_fcf_record_match(struct lpfc_hba *phba,
return false;
if (!lpfc_fab_name_match(fcf_rec->fabric_name, new_fcf_record))
return false;
+ if (fcf_rec->priority != new_fcf_record->fip_priority)
+ return false;
return true;
}
@@ -1854,9 +2396,7 @@ int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *vport, uint16_t fcf_index)
int rc;
if (fcf_index == LPFC_FCOE_FCF_NEXT_NONE) {
- spin_lock_irq(&phba->hbalock);
- if (phba->hba_flag & HBA_DEVLOSS_TMO) {
- spin_unlock_irq(&phba->hbalock);
+ if (test_bit(HBA_DEVLOSS_TMO, &phba->hba_flag)) {
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2872 Devloss tmo with no eligible "
"FCF, unregister in-use FCF (x%x) "
@@ -1866,8 +2406,9 @@ int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *vport, uint16_t fcf_index)
goto stop_flogi_current_fcf;
}
/* Mark the end to FLOGI roundrobin failover */
- phba->hba_flag &= ~FCF_RR_INPROG;
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
/* Allow action to new fcf asynchronous event */
+ spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_SCAN_DONE);
spin_unlock_irq(&phba->hbalock);
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
@@ -1875,6 +2416,26 @@ int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *vport, uint16_t fcf_index)
"failover and change port state:x%x/x%x\n",
phba->pport->port_state, LPFC_VPORT_UNKNOWN);
phba->pport->port_state = LPFC_VPORT_UNKNOWN;
+
+ if (!phba->fcf.fcf_redisc_attempted) {
+ lpfc_unregister_fcf(phba);
+
+ rc = lpfc_sli4_redisc_fcf_table(phba);
+ if (!rc) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+ "3195 Rediscover FCF table\n");
+ phba->fcf.fcf_redisc_attempted = 1;
+ lpfc_sli4_clear_fcf_rr_bmask(phba);
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
+ "3196 Rediscover FCF table "
+ "failed. Status:x%x\n", rc);
+ }
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
+ "3197 Already rediscover FCF table "
+ "attempted. No more retry\n");
+ }
goto stop_flogi_current_fcf;
} else {
lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_ELS,
@@ -1897,6 +2458,154 @@ stop_flogi_current_fcf:
}
/**
+ * lpfc_sli4_fcf_pri_list_del
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: the index of the fcf record to delete
+ * This routine checks the on list flag of the fcf_index to be deleted.
+ * If it is one the list then it is removed from the list, and the flag
+ * is cleared. This routine grab the hbalock before removing the fcf
+ * record from the list.
+ **/
+static void lpfc_sli4_fcf_pri_list_del(struct lpfc_hba *phba,
+ uint16_t fcf_index)
+{
+ struct lpfc_fcf_pri *new_fcf_pri;
+
+ new_fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+ lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+ "3058 deleting idx x%x pri x%x flg x%x\n",
+ fcf_index, new_fcf_pri->fcf_rec.priority,
+ new_fcf_pri->fcf_rec.flag);
+ spin_lock_irq(&phba->hbalock);
+ if (new_fcf_pri->fcf_rec.flag & LPFC_FCF_ON_PRI_LIST) {
+ if (phba->fcf.current_rec.priority ==
+ new_fcf_pri->fcf_rec.priority)
+ phba->fcf.eligible_fcf_cnt--;
+ list_del_init(&new_fcf_pri->list);
+ new_fcf_pri->fcf_rec.flag &= ~LPFC_FCF_ON_PRI_LIST;
+ }
+ spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli4_set_fcf_flogi_fail
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: the index of the fcf record to update
+ * This routine acquires the hbalock and then set the LPFC_FCF_FLOGI_FAILED
+ * flag so the round robin selection for the particular priority level
+ * will try a different fcf record that does not have this bit set.
+ * If the fcf record is re-read for any reason this flag is cleared brfore
+ * adding it to the priority list.
+ **/
+void
+lpfc_sli4_set_fcf_flogi_fail(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+ struct lpfc_fcf_pri *new_fcf_pri;
+ new_fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+ spin_lock_irq(&phba->hbalock);
+ new_fcf_pri->fcf_rec.flag |= LPFC_FCF_FLOGI_FAILED;
+ spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli4_fcf_pri_list_add
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: the index of the fcf record to add
+ * @new_fcf_record: pointer to a new FCF record.
+ * This routine checks the priority of the fcf_index to be added.
+ * If it is a lower priority than the current head of the fcf_pri list
+ * then it is added to the list in the right order.
+ * If it is the same priority as the current head of the list then it
+ * is added to the head of the list and its bit in the rr_bmask is set.
+ * If the fcf_index to be added is of a higher priority than the current
+ * head of the list then the rr_bmask is cleared, its bit is set in the
+ * rr_bmask and it is added to the head of the list.
+ * returns:
+ * 0=success 1=failure
+ **/
+static int lpfc_sli4_fcf_pri_list_add(struct lpfc_hba *phba,
+ uint16_t fcf_index,
+ struct fcf_record *new_fcf_record)
+{
+ uint16_t current_fcf_pri;
+ uint16_t last_index;
+ struct lpfc_fcf_pri *fcf_pri;
+ struct lpfc_fcf_pri *next_fcf_pri;
+ struct lpfc_fcf_pri *new_fcf_pri;
+ int ret;
+
+ new_fcf_pri = &phba->fcf.fcf_pri[fcf_index];
+ lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+ "3059 adding idx x%x pri x%x flg x%x\n",
+ fcf_index, new_fcf_record->fip_priority,
+ new_fcf_pri->fcf_rec.flag);
+ spin_lock_irq(&phba->hbalock);
+ if (new_fcf_pri->fcf_rec.flag & LPFC_FCF_ON_PRI_LIST)
+ list_del_init(&new_fcf_pri->list);
+ new_fcf_pri->fcf_rec.fcf_index = fcf_index;
+ new_fcf_pri->fcf_rec.priority = new_fcf_record->fip_priority;
+ if (list_empty(&phba->fcf.fcf_pri_list)) {
+ list_add(&new_fcf_pri->list, &phba->fcf.fcf_pri_list);
+ ret = lpfc_sli4_fcf_rr_index_set(phba,
+ new_fcf_pri->fcf_rec.fcf_index);
+ goto out;
+ }
+
+ last_index = find_first_bit(phba->fcf.fcf_rr_bmask,
+ LPFC_SLI4_FCF_TBL_INDX_MAX);
+ if (last_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+ ret = 0; /* Empty rr list */
+ goto out;
+ }
+ current_fcf_pri = phba->fcf.fcf_pri[last_index].fcf_rec.priority;
+ if (new_fcf_pri->fcf_rec.priority <= current_fcf_pri) {
+ list_add(&new_fcf_pri->list, &phba->fcf.fcf_pri_list);
+ if (new_fcf_pri->fcf_rec.priority < current_fcf_pri) {
+ memset(phba->fcf.fcf_rr_bmask, 0,
+ sizeof(*phba->fcf.fcf_rr_bmask));
+ /* fcfs_at_this_priority_level = 1; */
+ phba->fcf.eligible_fcf_cnt = 1;
+ } else
+ /* fcfs_at_this_priority_level++; */
+ phba->fcf.eligible_fcf_cnt++;
+ ret = lpfc_sli4_fcf_rr_index_set(phba,
+ new_fcf_pri->fcf_rec.fcf_index);
+ goto out;
+ }
+
+ list_for_each_entry_safe(fcf_pri, next_fcf_pri,
+ &phba->fcf.fcf_pri_list, list) {
+ if (new_fcf_pri->fcf_rec.priority <=
+ fcf_pri->fcf_rec.priority) {
+ if (fcf_pri->list.prev == &phba->fcf.fcf_pri_list)
+ list_add(&new_fcf_pri->list,
+ &phba->fcf.fcf_pri_list);
+ else
+ list_add(&new_fcf_pri->list,
+ &((struct lpfc_fcf_pri *)
+ fcf_pri->list.prev)->list);
+ ret = 0;
+ goto out;
+ } else if (fcf_pri->list.next == &phba->fcf.fcf_pri_list
+ || new_fcf_pri->fcf_rec.priority <
+ next_fcf_pri->fcf_rec.priority) {
+ list_add(&new_fcf_pri->list, &fcf_pri->list);
+ ret = 0;
+ goto out;
+ }
+ if (new_fcf_pri->fcf_rec.priority > fcf_pri->fcf_rec.priority)
+ continue;
+
+ }
+ ret = 1;
+out:
+ /* we use = instead of |= to clear the FLOGI_FAILED flag. */
+ new_fcf_pri->fcf_rec.flag = LPFC_FCF_ON_PRI_LIST;
+ spin_unlock_irq(&phba->hbalock);
+ return ret;
+}
+
+/**
* lpfc_mbx_cmpl_fcf_scan_read_fcf_rec - fcf scan read_fcf mbox cmpl handler.
* @phba: pointer to lpfc hba data structure.
* @mboxq: pointer to mailbox object.
@@ -1918,8 +2627,7 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
uint32_t boot_flag, addr_mode;
uint16_t fcf_index, next_fcf_index;
struct lpfc_fcf_rec *fcf_rec = NULL;
- uint16_t vlan_id;
- uint32_t seed;
+ uint16_t vlan_id = LPFC_FCOE_NULL_VID;
bool select_new_fcf;
int rc;
@@ -1933,13 +2641,11 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
new_fcf_record = lpfc_sli4_fcf_rec_mbox_parse(phba, mboxq,
&next_fcf_index);
if (!new_fcf_record) {
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2765 Mailbox command READ_FCF_RECORD "
"failed to retrieve a FCF record.\n");
/* Let next new FCF event trigger fast failover */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~FCF_TS_INPROG;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
lpfc_sli4_mbox_cmd_free(phba, mboxq);
return;
}
@@ -1958,21 +2664,27 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
* record for roundrobin FCF failover.
*/
if (!rc) {
+ lpfc_sli4_fcf_pri_list_del(phba,
+ bf_get(lpfc_fcf_record_fcf_index,
+ new_fcf_record));
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
"2781 FCF (x%x) failed connection "
- "list check: (x%x/x%x)\n",
+ "list check: (x%x/x%x/%x)\n",
bf_get(lpfc_fcf_record_fcf_index,
new_fcf_record),
bf_get(lpfc_fcf_record_fcf_avail,
new_fcf_record),
bf_get(lpfc_fcf_record_fcf_valid,
+ new_fcf_record),
+ bf_get(lpfc_fcf_record_fcf_sol,
new_fcf_record));
if ((phba->fcf.fcf_flag & FCF_IN_USE) &&
lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec,
new_fcf_record, LPFC_FCOE_IGNORE_VID)) {
if (bf_get(lpfc_fcf_record_fcf_index, new_fcf_record) !=
phba->fcf.current_rec.fcf_indx) {
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
"2862 FCF (x%x) matches property "
"of in-use FCF (x%x)\n",
bf_get(lpfc_fcf_record_fcf_index,
@@ -2005,7 +2717,8 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
goto read_next_fcf;
} else {
fcf_index = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
- rc = lpfc_sli4_fcf_rr_index_set(phba, fcf_index);
+ rc = lpfc_sli4_fcf_pri_list_add(phba, fcf_index,
+ new_fcf_record);
if (rc)
goto read_next_fcf;
}
@@ -2018,7 +2731,8 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
*/
spin_lock_irq(&phba->hbalock);
if (phba->fcf.fcf_flag & FCF_IN_USE) {
- if (lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec,
+ if (phba->cfg_fcf_failover_policy == LPFC_FCF_FOV &&
+ lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec,
new_fcf_record, vlan_id)) {
if (bf_get(lpfc_fcf_record_fcf_index, new_fcf_record) ==
phba->fcf.current_rec.fcf_indx) {
@@ -2033,11 +2747,14 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
spin_unlock_irq(&phba->hbalock);
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2836 New FCF matches in-use "
- "FCF (x%x)\n",
- phba->fcf.current_rec.fcf_indx);
+ "FCF (x%x), port_state:x%x, "
+ "fc_flag:x%lx\n",
+ phba->fcf.current_rec.fcf_indx,
+ phba->pport->port_state,
+ phba->pport->fc_flag);
goto out;
} else
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2863 New FCF (x%x) matches "
"property of in-use FCF (x%x)\n",
bf_get(lpfc_fcf_record_fcf_index,
@@ -2146,9 +2863,6 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
phba->fcf.fcf_flag |= FCF_AVAILABLE;
/* Setup initial running random FCF selection count */
phba->fcf.eligible_fcf_cnt = 1;
- /* Seeding the random number generator for random selection */
- seed = (uint32_t)(0xFFFFFFFF & jiffies);
- srandom32(seed);
}
spin_unlock_irq(&phba->hbalock);
goto read_next_fcf;
@@ -2172,10 +2886,10 @@ read_next_fcf:
phba->fcoe_eventtag_at_fcf_scan,
bf_get(lpfc_fcf_record_fcf_index,
new_fcf_record));
- spin_lock_irq(&phba->hbalock);
- if (phba->hba_flag & HBA_DEVLOSS_TMO) {
- phba->hba_flag &= ~FCF_TS_INPROG;
- spin_unlock_irq(&phba->hbalock);
+ if (test_bit(HBA_DEVLOSS_TMO,
+ &phba->hba_flag)) {
+ clear_bit(FCF_TS_INPROG,
+ &phba->hba_flag);
/* Unregister in-use FCF and rescan */
lpfc_printf_log(phba, KERN_INFO,
LOG_FIP,
@@ -2188,8 +2902,7 @@ read_next_fcf:
/*
* Let next new FCF event trigger fast failover
*/
- phba->hba_flag &= ~FCF_TS_INPROG;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
return;
}
/*
@@ -2232,7 +2945,8 @@ read_next_fcf:
(phba->fcf.fcf_flag & FCF_REDISC_PEND))
return;
- if (phba->fcf.fcf_flag & FCF_IN_USE) {
+ if (phba->cfg_fcf_failover_policy == LPFC_FCF_FOV &&
+ phba->fcf.fcf_flag & FCF_IN_USE) {
/*
* In case the current in-use FCF record no
* longer existed during FCF discovery that
@@ -2247,7 +2961,6 @@ read_next_fcf:
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag |= FCF_REDISC_FOV;
spin_unlock_irq(&phba->hbalock);
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
lpfc_sli4_fcf_scan_read_fcf_rec(phba,
LPFC_FCOE_FCF_GET_FIRST);
return;
@@ -2288,15 +3001,15 @@ lpfc_mbx_cmpl_fcf_rr_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
uint32_t boot_flag, addr_mode;
uint16_t next_fcf_index, fcf_index;
uint16_t current_fcf_index;
- uint16_t vlan_id;
+ uint16_t vlan_id = LPFC_FCOE_NULL_VID;
int rc;
/* If link state is not up, stop the roundrobin failover process */
if (phba->link_state < LPFC_LINK_UP) {
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
- phba->hba_flag &= ~FCF_RR_INPROG;
spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_RR_INPROG, &phba->hba_flag);
goto out;
}
@@ -2306,8 +3019,11 @@ lpfc_mbx_cmpl_fcf_rr_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
if (!new_fcf_record) {
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
"2766 Mailbox command READ_FCF_RECORD "
- "failed to retrieve a FCF record.\n");
- goto error_out;
+ "failed to retrieve a FCF record. "
+ "hba_flg x%lx fcf_flg x%x\n", phba->hba_flag,
+ phba->fcf.fcf_flag);
+ lpfc_unregister_fcf_rescan(phba);
+ goto out;
}
/* Get the needed parameters from FCF record */
@@ -2390,7 +3106,7 @@ lpfc_mbx_cmpl_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
struct fcf_record *new_fcf_record;
uint32_t boot_flag, addr_mode;
uint16_t fcf_index, next_fcf_index;
- uint16_t vlan_id;
+ uint16_t vlan_id = LPFC_FCOE_NULL_VID;
int rc;
/* If link state is not up, no need to proceed */
@@ -2424,7 +3140,8 @@ lpfc_mbx_cmpl_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
/* Update the eligible FCF record index bmask */
fcf_index = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
- rc = lpfc_sli4_fcf_rr_index_set(phba, fcf_index);
+
+ rc = lpfc_sli4_fcf_pri_list_add(phba, fcf_index, new_fcf_record);
out:
lpfc_sli4_mbox_cmd_free(phba, mboxq);
@@ -2437,20 +3154,27 @@ out:
*
* This function handles completion of init vfi mailbox command.
*/
-void
+static void
lpfc_init_vfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
struct lpfc_vport *vport = mboxq->vport;
- if (mboxq->u.mb.mbxStatus && (mboxq->u.mb.mbxStatus != 0x4002)) {
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_MBOX,
- "2891 Init VFI mailbox failed 0x%x\n",
- mboxq->u.mb.mbxStatus);
+ /*
+ * VFI not supported on interface type 0, just do the flogi
+ * Also continue if the VFI is in use - just use the same one.
+ */
+ if (mboxq->u.mb.mbxStatus &&
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ LPFC_SLI_INTF_IF_TYPE_0) &&
+ mboxq->u.mb.mbxStatus != MBX_VFI_IN_USE) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2891 Init VFI mailbox failed 0x%x\n",
+ mboxq->u.mb.mbxStatus);
mempool_free(mboxq, phba->mbox_mem_pool);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
return;
}
+
lpfc_initial_flogi(vport);
mempool_free(mboxq, phba->mbox_mem_pool);
return;
@@ -2473,7 +3197,7 @@ lpfc_issue_init_vfi(struct lpfc_vport *vport)
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq) {
lpfc_printf_vlog(vport, KERN_ERR,
- LOG_MBOX, "2892 Failed to allocate "
+ LOG_TRACE_EVENT, "2892 Failed to allocate "
"init_vfi mailbox\n");
return;
}
@@ -2481,8 +3205,8 @@ lpfc_issue_init_vfi(struct lpfc_vport *vport)
mboxq->mbox_cmpl = lpfc_init_vfi_cmpl;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_MBOX, "2893 Failed to issue init_vfi mailbox\n");
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2893 Failed to issue init_vfi mailbox\n");
mempool_free(mboxq, vport->phba->mbox_mem_pool);
}
}
@@ -2499,27 +3223,23 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
struct lpfc_vport *vport = mboxq->vport;
struct lpfc_nodelist *ndlp;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if (mboxq->u.mb.mbxStatus) {
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_MBOX,
- "2609 Init VPI mailbox failed 0x%x\n",
- mboxq->u.mb.mbxStatus);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2609 Init VPI mailbox failed 0x%x\n",
+ mboxq->u.mb.mbxStatus);
mempool_free(mboxq, phba->mbox_mem_pool);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
return;
}
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_VPORT_NEEDS_INIT_VPI;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag);
/* If this port is physical port or FDISC is done, do reg_vpi */
if ((phba->pport == vport) || (vport->port_state == LPFC_FDISC)) {
ndlp = lpfc_findnode_did(vport, Fabric_DID);
if (!ndlp)
lpfc_printf_vlog(vport, KERN_ERR,
- LOG_DISCOVERY,
+ LOG_TRACE_EVENT,
"2731 Cannot find fabric "
"controller node\n");
else
@@ -2532,7 +3252,7 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_initial_fdisc(vport);
else {
lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"2606 No NPIV Fabric support\n");
}
mempool_free(mboxq, phba->mbox_mem_pool);
@@ -2550,12 +3270,23 @@ void
lpfc_issue_init_vpi(struct lpfc_vport *vport)
{
LPFC_MBOXQ_t *mboxq;
- int rc;
+ int rc, vpi;
+
+ if ((vport->port_type != LPFC_PHYSICAL_PORT) && (!vport->vpi)) {
+ vpi = lpfc_alloc_vpi(vport->phba);
+ if (!vpi) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "3303 Failed to obtain vport vpi\n");
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ return;
+ }
+ vport->vpi = vpi;
+ }
mboxq = mempool_alloc(vport->phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq) {
lpfc_printf_vlog(vport, KERN_ERR,
- LOG_MBOX, "2607 Failed to allocate "
+ LOG_TRACE_EVENT, "2607 Failed to allocate "
"init_vpi mailbox\n");
return;
}
@@ -2564,8 +3295,8 @@ lpfc_issue_init_vpi(struct lpfc_vport *vport)
mboxq->mbox_cmpl = lpfc_init_vpi_cmpl;
rc = lpfc_sli_issue_mbox(vport->phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_MBOX, "2608 Failed to issue init_vpi mailbox\n");
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2608 Failed to issue init_vpi mailbox\n");
mempool_free(mboxq, vport->phba->mbox_mem_pool);
}
}
@@ -2599,7 +3330,8 @@ lpfc_start_fdiscs(struct lpfc_hba *phba)
FC_VPORT_LINKDOWN);
continue;
}
- if (vports[i]->fc_flag & FC_VPORT_NEEDS_INIT_VPI) {
+ if (test_bit(FC_VPORT_NEEDS_INIT_VPI,
+ &vports[i]->fc_flag)) {
lpfc_issue_init_vpi(vports[i]);
continue;
}
@@ -2609,7 +3341,7 @@ lpfc_start_fdiscs(struct lpfc_hba *phba)
lpfc_vport_set_state(vports[i],
FC_VPORT_NO_FABRIC_SUPP);
lpfc_printf_vlog(vports[i], KERN_ERR,
- LOG_ELS,
+ LOG_TRACE_EVENT,
"0259 No NPIV "
"Fabric support\n");
}
@@ -2621,66 +3353,103 @@ lpfc_start_fdiscs(struct lpfc_hba *phba)
void
lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
- struct lpfc_dmabuf *dmabuf = mboxq->context1;
struct lpfc_vport *vport = mboxq->vport;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- if (mboxq->u.mb.mbxStatus) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
- "2018 REG_VFI mbxStatus error x%x "
- "HBA state x%x\n",
- mboxq->u.mb.mbxStatus, vport->port_state);
+ /*
+ * VFI not supported for interface type 0, so ignore any mailbox
+ * error (except VFI in use) and continue with the discovery.
+ */
+ if (mboxq->u.mb.mbxStatus &&
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ LPFC_SLI_INTF_IF_TYPE_0) &&
+ mboxq->u.mb.mbxStatus != MBX_VFI_IN_USE) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2018 REG_VFI mbxStatus error x%x "
+ "HBA state x%x\n",
+ mboxq->u.mb.mbxStatus, vport->port_state);
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
/* FLOGI failed, use loop map to make discovery list */
lpfc_disc_list_loopmap(vport);
/* Start discovery */
lpfc_disc_start(vport);
- goto fail_free_mem;
+ goto out_free_mem;
}
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- goto fail_free_mem;
+ goto out_free_mem;
}
+
+ /* If the VFI is already registered, there is nothing else to do
+ * Unless this was a VFI update and we are in PT2PT mode, then
+ * we should drop through to set the port state to ready.
+ */
+ if (test_bit(FC_VFI_REGISTERED, &vport->fc_flag))
+ if (!(phba->sli_rev == LPFC_SLI_REV4 &&
+ test_bit(FC_PT2PT, &vport->fc_flag)))
+ goto out_free_mem;
+
/* The VPI is implicitly registered when the VFI is registered */
+ set_bit(FC_VFI_REGISTERED, &vport->fc_flag);
+ clear_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
+ clear_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag);
spin_lock_irq(shost->host_lock);
vport->vpi_state |= LPFC_VPI_REGISTERED;
- vport->fc_flag |= FC_VFI_REGISTERED;
- vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
spin_unlock_irq(shost->host_lock);
+ /* In case SLI4 FC loopback test, we are ready */
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ (phba->link_flag & LS_LOOPBACK_MODE)) {
+ phba->link_state = LPFC_HBA_READY;
+ goto out_free_mem;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
+ "3313 cmpl reg vfi port_state:%x fc_flag:%lx "
+ "myDid:%x alpacnt:%d LinkState:%x topology:%x\n",
+ vport->port_state, vport->fc_flag, vport->fc_myDID,
+ vport->phba->alpa_map[0],
+ phba->link_state, phba->fc_topology);
+
if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
- /* For private loop just start discovery and we are done. */
- if ((phba->fc_topology == LPFC_TOPOLOGY_LOOP) &&
- (phba->alpa_map[0] == 0) &&
- !(vport->fc_flag & FC_PUBLIC_LOOP)) {
+ /*
+ * For private loop or for NPort pt2pt,
+ * just start discovery and we are done.
+ */
+ if (test_bit(FC_PT2PT, &vport->fc_flag) ||
+ (phba->fc_topology == LPFC_TOPOLOGY_LOOP &&
+ !test_bit(FC_PUBLIC_LOOP, &vport->fc_flag))) {
+
/* Use loop map to make discovery list */
lpfc_disc_list_loopmap(vport);
/* Start discovery */
- lpfc_disc_start(vport);
+ if (test_bit(FC_PT2PT, &vport->fc_flag))
+ vport->port_state = LPFC_VPORT_READY;
+ else
+ lpfc_disc_start(vport);
} else {
lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
}
}
-fail_free_mem:
- mempool_free(mboxq, phba->mbox_mem_pool);
- lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
- kfree(dmabuf);
- return;
+out_free_mem:
+ lpfc_mbox_rsrc_cleanup(phba, mboxq, MBOX_THD_UNLOCKED);
}
static void
lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
MAILBOX_t *mb = &pmb->u.mb;
- struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1;
+ struct lpfc_dmabuf *mp = pmb->ctx_buf;
struct lpfc_vport *vport = pmb->vport;
-
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct serv_parm *sp = &vport->fc_sparam;
+ uint32_t ed_tov;
/* Check for error */
if (mb->mbxStatus) {
/* READ_SPARAM mbox error <mbxStatus> state <hba_state> */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0319 READ_SPARAM mbxStatus error x%x "
"hba state x%x>\n",
mb->mbxStatus, vport->port_state);
@@ -2690,24 +3459,39 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
memcpy((uint8_t *) &vport->fc_sparam, (uint8_t *) mp->virt,
sizeof (struct serv_parm));
+
+ ed_tov = be32_to_cpu(sp->cmn.e_d_tov);
+ if (sp->cmn.edtovResolution) /* E_D_TOV ticks are in nanoseconds */
+ ed_tov = (ed_tov + 999999) / 1000000;
+
+ phba->fc_edtov = ed_tov;
+ phba->fc_ratov = (2 * ed_tov) / 1000;
+ if (phba->fc_ratov < FF_DEF_RATOV) {
+ /* RA_TOV should be atleast 10sec for initial flogi */
+ phba->fc_ratov = FF_DEF_RATOV;
+ }
+
lpfc_update_vport_wwn(vport);
+ fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
if (vport->port_type == LPFC_PHYSICAL_PORT) {
memcpy(&phba->wwnn, &vport->fc_nodename, sizeof(phba->wwnn));
memcpy(&phba->wwpn, &vport->fc_portname, sizeof(phba->wwnn));
}
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
+
+ /* Check if sending the FLOGI is being deferred to after we get
+ * up to date CSPs from MBX_READ_SPARAM.
+ */
+ if (test_bit(HBA_DEFER_FLOGI, &phba->hba_flag)) {
+ lpfc_initial_flogi(vport);
+ clear_bit(HBA_DEFER_FLOGI, &phba->hba_flag);
+ }
return;
out:
- pmb->context1 = NULL;
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
lpfc_issue_clear_la(phba, vport);
- mempool_free(pmb, phba->mbox_mem_pool);
- return;
}
static void
@@ -2716,27 +3500,43 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
struct lpfc_vport *vport = phba->pport;
LPFC_MBOXQ_t *sparam_mbox, *cfglink_mbox = NULL;
int i;
- struct lpfc_dmabuf *mp;
int rc;
struct fcf_record *fcf_record;
+ unsigned long iflags;
- spin_lock_irq(&phba->hbalock);
- switch (bf_get(lpfc_mbx_read_top_link_spd, la)) {
- case LPFC_LINK_SPEED_1GHZ:
- case LPFC_LINK_SPEED_2GHZ:
- case LPFC_LINK_SPEED_4GHZ:
- case LPFC_LINK_SPEED_8GHZ:
- case LPFC_LINK_SPEED_10GHZ:
- case LPFC_LINK_SPEED_16GHZ:
- phba->fc_linkspeed = bf_get(lpfc_mbx_read_top_link_spd, la);
- break;
- default:
- phba->fc_linkspeed = LPFC_LINK_SPEED_UNKNOWN;
- break;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ phba->fc_linkspeed = bf_get(lpfc_mbx_read_top_link_spd, la);
+
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ switch (bf_get(lpfc_mbx_read_top_link_spd, la)) {
+ case LPFC_LINK_SPEED_1GHZ:
+ case LPFC_LINK_SPEED_2GHZ:
+ case LPFC_LINK_SPEED_4GHZ:
+ case LPFC_LINK_SPEED_8GHZ:
+ case LPFC_LINK_SPEED_10GHZ:
+ case LPFC_LINK_SPEED_16GHZ:
+ case LPFC_LINK_SPEED_32GHZ:
+ case LPFC_LINK_SPEED_64GHZ:
+ case LPFC_LINK_SPEED_128GHZ:
+ case LPFC_LINK_SPEED_256GHZ:
+ break;
+ default:
+ phba->fc_linkspeed = LPFC_LINK_SPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ if (phba->fc_topology &&
+ phba->fc_topology != bf_get(lpfc_mbx_read_top_topology, la)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3314 Topology changed was 0x%x is 0x%x\n",
+ phba->fc_topology,
+ bf_get(lpfc_mbx_read_top_topology, la));
+ phba->fc_topology_changed = 1;
}
phba->fc_topology = bf_get(lpfc_mbx_read_top_topology, la);
- phba->link_flag &= ~LS_NPIV_FAB_SUPPORTED;
+ phba->link_flag &= ~(LS_NPIV_FAB_SUPPORTED | LS_CT_VEN_RPA);
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
@@ -2750,7 +3550,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
"topology\n");
/* Get Loop Map information */
if (bf_get(lpfc_mbx_read_top_il, la))
- vport->fc_flag |= FC_LBIT;
+ set_bit(FC_LBIT, &vport->fc_flag);
vport->fc_myDID = bf_get(lpfc_mbx_read_top_alpa_granted, la);
i = la->lilpBde64.tus.f.bdeSize;
@@ -2795,15 +3595,17 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
} else {
if (!(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
if (phba->max_vpi && phba->cfg_enable_npiv &&
- (phba->sli_rev == 3))
+ (phba->sli_rev >= LPFC_SLI_REV3))
phba->sli3_options |= LPFC_SLI3_NPIV_ENABLED;
}
vport->fc_myDID = phba->fc_pref_DID;
- vport->fc_flag |= FC_LBIT;
+ set_bit(FC_LBIT, &vport->fc_flag);
}
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
lpfc_linkup(phba);
+ sparam_mbox = NULL;
+
sparam_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!sparam_mbox)
goto out;
@@ -2817,14 +3619,11 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
sparam_mbox->mbox_cmpl = lpfc_mbx_cmpl_read_sparam;
rc = lpfc_sli_issue_mbox(phba, sparam_mbox, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- mp = (struct lpfc_dmabuf *) sparam_mbox->context1;
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(sparam_mbox, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, sparam_mbox, MBOX_THD_UNLOCKED);
goto out;
}
- if (!(phba->hba_flag & HBA_FCOE_MODE)) {
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
cfglink_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!cfglink_mbox)
goto out;
@@ -2844,12 +3643,12 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
* is phase 1 implementation that support FCF index 0 and driver
* defaults.
*/
- if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
+ if (!test_bit(HBA_FIP_SUPPORT, &phba->hba_flag)) {
fcf_record = kzalloc(sizeof(struct fcf_record),
GFP_KERNEL);
if (unlikely(!fcf_record)) {
lpfc_printf_log(phba, KERN_ERR,
- LOG_MBOX | LOG_SLI,
+ LOG_TRACE_EVENT,
"2554 Could not allocate memory for "
"fcf record\n");
rc = -ENODEV;
@@ -2861,7 +3660,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
rc = lpfc_sli4_add_fcf_record(phba, fcf_record);
if (unlikely(rc)) {
lpfc_printf_log(phba, KERN_ERR,
- LOG_MBOX | LOG_SLI,
+ LOG_TRACE_EVENT,
"2013 Could not manually add FCF "
"record 0, status %d\n", rc);
rc = -ENODEV;
@@ -2874,34 +3673,35 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
* The driver is expected to do FIP/FCF. Call the port
* and get the FCF Table.
*/
- spin_lock_irq(&phba->hbalock);
- if (phba->hba_flag & FCF_TS_INPROG) {
- spin_unlock_irq(&phba->hbalock);
+ if (test_bit(FCF_TS_INPROG, &phba->hba_flag))
return;
- }
/* This is the initial FCF discovery scan */
+ spin_lock_irqsave(&phba->hbalock, iflags);
phba->fcf.fcf_flag |= FCF_INIT_DISC;
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
lpfc_printf_log(phba, KERN_INFO, LOG_FIP | LOG_DISCOVERY,
"2778 Start FCF table scan at linkup\n");
rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba,
LPFC_FCOE_FCF_GET_FIRST);
if (rc) {
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflags);
phba->fcf.fcf_flag &= ~FCF_INIT_DISC;
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
goto out;
}
/* Reset FCF roundrobin bmask for new discovery */
- memset(phba->fcf.fcf_rr_bmask, 0,
- sizeof(*phba->fcf.fcf_rr_bmask));
+ lpfc_sli4_clear_fcf_rr_bmask(phba);
}
+ /* Prepare for LINK up registrations */
+ memset(phba->os_host_name, 0, sizeof(phba->os_host_name));
+ scnprintf(phba->os_host_name, sizeof(phba->os_host_name), "%s",
+ init_utsname()->nodename);
return;
out:
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
- "0263 Discovery Mailbox error: state: 0x%x : %p %p\n",
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0263 Discovery Mailbox error: state: 0x%x : x%px x%px\n",
vport->port_state, sparam_mbox, cfglink_mbox);
lpfc_issue_clear_la(phba, vport);
return;
@@ -2937,19 +3737,23 @@ lpfc_mbx_issue_link_down(struct lpfc_hba *phba)
* This routine handles processing a READ_TOPOLOGY mailbox
* command upon completion. It is setup in the LPFC_MBOXQ
* as the completion routine when the command is
- * handed off to the SLI layer.
+ * handed off to the SLI layer. SLI4 only.
*/
void
lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_mbx_read_top *la;
+ struct lpfc_sli_ring *pring;
MAILBOX_t *mb = &pmb->u.mb;
- struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
+ struct lpfc_dmabuf *mp = pmb->ctx_buf;
+ uint8_t attn_type;
/* Unblock ELS traffic */
- phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
+ pring = lpfc_phba_elsring(phba);
+ if (pring)
+ pring->flag &= ~LPFC_STOP_IOCB_EVENT;
+
/* Check for error */
if (mb->mbxStatus) {
lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
@@ -2961,35 +3765,25 @@ lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
la = (struct lpfc_mbx_read_top *) &pmb->u.mb.un.varReadTop;
+ attn_type = bf_get(lpfc_mbx_read_top_att_type, la);
memcpy(&phba->alpa_map[0], mp->virt, 128);
- spin_lock_irq(shost->host_lock);
if (bf_get(lpfc_mbx_read_top_pb, la))
- vport->fc_flag |= FC_BYPASSED_MODE;
+ set_bit(FC_BYPASSED_MODE, &vport->fc_flag);
else
- vport->fc_flag &= ~FC_BYPASSED_MODE;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_BYPASSED_MODE, &vport->fc_flag);
- if ((phba->fc_eventTag < la->eventTag) ||
- (phba->fc_eventTag == la->eventTag)) {
+ if (phba->fc_eventTag <= la->eventTag) {
phba->fc_stat.LinkMultiEvent++;
- if (bf_get(lpfc_mbx_read_top_att_type, la) == LPFC_ATT_LINK_UP)
+ if (attn_type == LPFC_ATT_LINK_UP)
if (phba->fc_eventTag != 0)
lpfc_linkdown(phba);
}
phba->fc_eventTag = la->eventTag;
- spin_lock_irq(&phba->hbalock);
- if (bf_get(lpfc_mbx_read_top_mm, la))
- phba->sli.sli_flag |= LPFC_MENLO_MAINT;
- else
- phba->sli.sli_flag &= ~LPFC_MENLO_MAINT;
- spin_unlock_irq(&phba->hbalock);
-
phba->link_events++;
- if ((bf_get(lpfc_mbx_read_top_att_type, la) == LPFC_ATT_LINK_UP) &&
- (!bf_get(lpfc_mbx_read_top_mm, la))) {
+ if (attn_type == LPFC_ATT_LINK_UP) {
phba->fc_stat.LinkUp++;
if (phba->link_flag & LS_LOOPBACK_MODE) {
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
@@ -3003,81 +3797,57 @@ lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
} else {
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
"1303 Link Up Event x%x received "
- "Data: x%x x%x x%x x%x x%x x%x %d\n",
+ "Data: x%x x%x x%x x%x x%x\n",
la->eventTag, phba->fc_eventTag,
bf_get(lpfc_mbx_read_top_alpa_granted,
la),
bf_get(lpfc_mbx_read_top_link_spd, la),
phba->alpa_map[0],
- bf_get(lpfc_mbx_read_top_mm, la),
- bf_get(lpfc_mbx_read_top_fa, la),
- phba->wait_4_mlo_maint_flg);
+ bf_get(lpfc_mbx_read_top_fa, la));
}
lpfc_mbx_process_link_up(phba, la);
- } else if (bf_get(lpfc_mbx_read_top_att_type, la) ==
- LPFC_ATT_LINK_DOWN) {
+
+ if (phba->cmf_active_mode != LPFC_CFG_OFF)
+ lpfc_cmf_signal_init(phba);
+
+ if (phba->lmt & LMT_64Gb)
+ lpfc_read_lds_params(phba);
+
+ } else if (attn_type == LPFC_ATT_LINK_DOWN ||
+ attn_type == LPFC_ATT_UNEXP_WWPN) {
phba->fc_stat.LinkDown++;
- if (phba->link_flag & LS_LOOPBACK_MODE) {
+ if (phba->link_flag & LS_LOOPBACK_MODE)
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
"1308 Link Down Event in loop back mode "
"x%x received "
- "Data: x%x x%x x%x\n",
+ "Data: x%x x%x x%lx\n",
la->eventTag, phba->fc_eventTag,
phba->pport->port_state, vport->fc_flag);
- }
- else {
+ else if (attn_type == LPFC_ATT_UNEXP_WWPN)
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
- "1305 Link Down Event x%x received "
- "Data: x%x x%x x%x x%x x%x\n",
+ "1313 Link Down Unexpected FA WWPN Event x%x "
+ "received Data: x%x x%x x%lx x%x\n",
la->eventTag, phba->fc_eventTag,
phba->pport->port_state, vport->fc_flag,
- bf_get(lpfc_mbx_read_top_mm, la),
bf_get(lpfc_mbx_read_top_fa, la));
- }
- lpfc_mbx_issue_link_down(phba);
- }
- if ((bf_get(lpfc_mbx_read_top_mm, la)) &&
- (bf_get(lpfc_mbx_read_top_att_type, la) == LPFC_ATT_LINK_UP)) {
- if (phba->link_state != LPFC_LINK_DOWN) {
- phba->fc_stat.LinkDown++;
+ else
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
- "1312 Link Down Event x%x received "
- "Data: x%x x%x x%x\n",
- la->eventTag, phba->fc_eventTag,
- phba->pport->port_state, vport->fc_flag);
- lpfc_mbx_issue_link_down(phba);
- } else
- lpfc_enable_la(phba);
-
- lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
- "1310 Menlo Maint Mode Link up Event x%x rcvd "
- "Data: x%x x%x x%x\n",
+ "1305 Link Down Event x%x received "
+ "Data: x%x x%x x%lx x%x\n",
la->eventTag, phba->fc_eventTag,
- phba->pport->port_state, vport->fc_flag);
- /*
- * The cmnd that triggered this will be waiting for this
- * signal.
- */
- /* WAKEUP for MENLO_SET_MODE or MENLO_RESET command. */
- if (phba->wait_4_mlo_maint_flg) {
- phba->wait_4_mlo_maint_flg = 0;
- wake_up_interruptible(&phba->wait_4_mlo_m_q);
- }
+ phba->pport->port_state, vport->fc_flag,
+ bf_get(lpfc_mbx_read_top_fa, la));
+ lpfc_mbx_issue_link_down(phba);
}
- if (bf_get(lpfc_mbx_read_top_fa, la)) {
- if (bf_get(lpfc_mbx_read_top_mm, la))
- lpfc_issue_clear_la(phba, vport);
+ if ((phba->sli_rev < LPFC_SLI_REV4) &&
+ bf_get(lpfc_mbx_read_top_fa, la))
lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
"1311 fa %d\n",
bf_get(lpfc_mbx_read_top_fa, la));
- }
lpfc_mbx_cmpl_read_topology_free_mbuf:
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
- return;
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
}
/*
@@ -3090,17 +3860,24 @@ void
lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
- struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_dmabuf *mp = pmb->ctx_buf;
+ struct lpfc_nodelist *ndlp = pmb->ctx_ndlp;
- pmb->context1 = NULL;
- pmb->context2 = NULL;
+ /* The driver calls the state machine with the pmb pointer
+ * but wants to make sure a stale ctx_buf isn't acted on.
+ * The ctx_buf is restored later and cleaned up.
+ */
+ pmb->ctx_buf = NULL;
+ pmb->ctx_ndlp = NULL;
- if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND)
- ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND;
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI | LOG_NODE | LOG_DISCOVERY,
+ "0002 rpi:%x DID:%x flg:%lx %d x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
+ kref_read(&ndlp->kref),
+ ndlp);
+ clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
- if (ndlp->nlp_flag & NLP_IGNR_REG_CMPL ||
+ if (test_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag) ||
ndlp->nlp_state != NLP_STE_REG_LOGIN_ISSUE) {
/* We rcvd a rscn after issuing this
* mbox reg login, we may have cycled
@@ -3110,17 +3887,22 @@ lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
* there is another reg login in
* process.
*/
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL;
- spin_unlock_irq(shost->host_lock);
- } else
- /* Good status, call state machine */
- lpfc_disc_state_machine(vport, ndlp, pmb,
- NLP_EVT_CMPL_REG_LOGIN);
+ clear_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag);
+
+ /*
+ * We cannot leave the RPI registered because
+ * if we go thru discovery again for this ndlp
+ * a subsequent REG_RPI will fail.
+ */
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
+ lpfc_unreg_rpi(vport, ndlp);
+ }
+
+ /* Call state machine */
+ lpfc_disc_state_machine(vport, ndlp, pmb, NLP_EVT_CMPL_REG_LOGIN);
+ pmb->ctx_buf = mp;
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
/* decrement the node reference count held for this callback
* function.
*/
@@ -3145,25 +3927,25 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
break;
/* If VPI is busy, reset the HBA */
case 0x9700:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"2798 Unreg_vpi failed vpi 0x%x, mb status = 0x%x\n",
vport->vpi, mb->mbxStatus);
- if (!(phba->pport->load_flag & FC_UNLOADING))
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
lpfc_workq_post_event(phba, NULL, NULL,
LPFC_EVT_RESET_HBA);
}
+
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
spin_lock_irq(shost->host_lock);
vport->vpi_state &= ~LPFC_VPI_REGISTERED;
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
spin_unlock_irq(shost->host_lock);
- vport->unreg_vpi_cmpl = VPORT_OK;
mempool_free(pmb, phba->mbox_mem_pool);
lpfc_cleanup_vports_rrqs(vport, NULL);
/*
* This shost reference might have been taken at the beginning of
* lpfc_vport_delete()
*/
- if ((vport->load_flag & FC_UNLOADING) && (vport != phba->pport))
+ if (test_bit(FC_UNLOADING, &vport->load_flag) && vport != phba->pport)
scsi_host_put(shost);
}
@@ -3183,10 +3965,9 @@ lpfc_mbx_unreg_vpi(struct lpfc_vport *vport)
mbox->mbox_cmpl = lpfc_mbx_cmpl_unreg_vpi;
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1800 Could not issue unreg_vpi\n");
mempool_free(mbox, phba->mbox_mem_pool);
- vport->unreg_vpi_cmpl = VPORT_ERROR;
return rc;
}
return 0;
@@ -3207,26 +3988,31 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
"0912 cmpl_reg_vpi, mb status = 0x%x\n",
mb->mbxStatus);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_FABRIC, &vport->fc_flag);
+ clear_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
vport->fc_myDID = 0;
+
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if (phba->nvmet_support)
+ lpfc_nvmet_update_targetport(phba);
+ else
+ lpfc_nvme_update_localport(vport);
+ }
goto out;
}
+ clear_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
spin_lock_irq(shost->host_lock);
vport->vpi_state |= LPFC_VPI_REGISTERED;
- vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
spin_unlock_irq(shost->host_lock);
vport->num_disc_nodes = 0;
/* go thru NPR list and issue ELS PLOGIs */
- if (vport->fc_npr_cnt)
+ if (atomic_read(&vport->fc_npr_cnt))
lpfc_els_disc_plogi(vport);
if (!vport->num_disc_nodes) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NDISC_ACTIVE;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_NDISC_ACTIVE, &vport->fc_flag);
lpfc_can_disctmo(vport);
}
vport->port_state = LPFC_VPORT_READY;
@@ -3250,7 +4036,7 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
LPFC_MBOXQ_t *pmb = NULL;
MAILBOX_t *mb;
struct static_vport_info *vport_info;
- int rc = 0, i;
+ int mbx_wait_rc = 0, i;
struct fc_vport_identifiers vport_id;
struct fc_vport *new_fc_vport;
struct Scsi_Host *shost;
@@ -3262,17 +4048,17 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0542 lpfc_create_static_vport failed to"
" allocate mailbox memory\n");
return;
}
-
+ memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
mb = &pmb->u.mb;
vport_info = kzalloc(sizeof(struct static_vport_info), GFP_KERNEL);
if (!vport_info) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0543 lpfc_create_static_vport failed to"
" allocate vport_info\n");
mempool_free(pmb, phba->mbox_mem_pool);
@@ -3281,24 +4067,35 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
vport_buff = (uint8_t *) vport_info;
do {
+ /* While loop iteration forces a free dma buffer from
+ * the previous loop because the mbox is reused and
+ * the dump routine is a single-use construct.
+ */
+ if (pmb->ctx_buf) {
+ mp = pmb->ctx_buf;
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ pmb->ctx_buf = NULL;
+ }
if (lpfc_dump_static_vport(phba, pmb, offset))
goto out;
pmb->vport = phba->pport;
- rc = lpfc_sli_issue_mbox_wait(phba, pmb, LPFC_MBOX_TMO);
+ mbx_wait_rc = lpfc_sli_issue_mbox_wait(phba, pmb,
+ LPFC_MBOX_TMO);
- if ((rc != MBX_SUCCESS) || mb->mbxStatus) {
+ if ((mbx_wait_rc != MBX_SUCCESS) || mb->mbxStatus) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0544 lpfc_create_static_vport failed to"
" issue dump mailbox command ret 0x%x "
"status 0x%x\n",
- rc, mb->mbxStatus);
+ mbx_wait_rc, mb->mbxStatus);
goto out;
}
if (phba->sli_rev == LPFC_SLI_REV4) {
byte_count = pmb->u.mqe.un.mb_words[5];
- mp = (struct lpfc_dmabuf *) pmb->context2;
+ mp = pmb->ctx_buf;
if (byte_count > sizeof(struct static_vport_info) -
offset)
byte_count = sizeof(struct static_vport_info)
@@ -3326,11 +4123,12 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
if ((le32_to_cpu(vport_info->signature) != VPORT_INFO_SIG) ||
((le32_to_cpu(vport_info->rev) & VPORT_INFO_REV_MASK)
!= VPORT_INFO_REV)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0545 lpfc_create_static_vport bad"
- " information header 0x%x 0x%x\n",
- le32_to_cpu(vport_info->signature),
- le32_to_cpu(vport_info->rev) & VPORT_INFO_REV_MASK);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0545 lpfc_create_static_vport bad"
+ " information header 0x%x 0x%x\n",
+ le32_to_cpu(vport_info->signature),
+ le32_to_cpu(vport_info->rev) &
+ VPORT_INFO_REV_MASK);
goto out;
}
@@ -3362,16 +4160,8 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
out:
kfree(vport_info);
- if (rc != MBX_TIMEOUT) {
- if (pmb->context2) {
- mp = (struct lpfc_dmabuf *) pmb->context2;
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
- mempool_free(pmb, phba->mbox_mem_pool);
- }
-
- return;
+ if (mbx_wait_rc != MBX_TIMEOUT)
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
}
/*
@@ -3385,21 +4175,15 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
MAILBOX_t *mb = &pmb->u.mb;
- struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
- struct lpfc_nodelist *ndlp;
+ struct lpfc_nodelist *ndlp = pmb->ctx_ndlp;
- ndlp = (struct lpfc_nodelist *) pmb->context2;
- pmb->context1 = NULL;
- pmb->context2 = NULL;
+ pmb->ctx_ndlp = NULL;
if (mb->mbxStatus) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0258 Register Fabric login error: 0x%x\n",
mb->mbxStatus);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
-
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
/* FLOGI failed, use loop map to make discovery list */
lpfc_disc_list_loopmap(vport);
@@ -3423,23 +4207,19 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
if (phba->sli_rev < LPFC_SLI_REV4)
ndlp->nlp_rpi = mb->un.varWords[0];
- ndlp->nlp_flag |= NLP_RPI_REGISTERED;
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
/* when physical port receive logo donot start
* vport discovery */
- if (!(vport->fc_flag & FC_LOGO_RCVD_DID_CHNG))
+ if (!test_and_clear_bit(FC_LOGO_RCVD_DID_CHNG, &vport->fc_flag))
lpfc_start_fdiscs(phba);
- else
- vport->fc_flag &= ~FC_LOGO_RCVD_DID_CHNG ;
lpfc_do_scr_ns_plogi(phba, vport);
}
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
/* Drop the reference count from the mbox at the end after
* all the current reference to the ndlp have been done.
@@ -3448,6 +4228,81 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
return;
}
+ /*
+ * This routine will issue a GID_FT for each FC4 Type supported
+ * by the driver. ALL GID_FTs must complete before discovery is started.
+ */
+int
+lpfc_issue_gidft(struct lpfc_vport *vport)
+{
+ /* Good status, issue CT Request to NameServer */
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) {
+ if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_FCP)) {
+ /* Cannot issue NameServer FCP Query, so finish up
+ * discovery
+ */
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "0604 %s FC TYPE %x %s\n",
+ "Failed to issue GID_FT to ",
+ FC_TYPE_FCP,
+ "Finishing discovery.");
+ return 0;
+ }
+ vport->gidft_inp++;
+ }
+
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_NVME)) {
+ /* Cannot issue NameServer NVME Query, so finish up
+ * discovery
+ */
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "0605 %s FC_TYPE %x %s %d\n",
+ "Failed to issue GID_FT to ",
+ FC_TYPE_NVME,
+ "Finishing discovery: gidftinp ",
+ vport->gidft_inp);
+ if (vport->gidft_inp == 0)
+ return 0;
+ } else
+ vport->gidft_inp++;
+ }
+ return vport->gidft_inp;
+}
+
+/**
+ * lpfc_issue_gidpt - issue a GID_PT for all N_Ports
+ * @vport: The virtual port for which this call is being executed.
+ *
+ * This routine will issue a GID_PT to get a list of all N_Ports
+ *
+ * Return value :
+ * 0 - Failure to issue a GID_PT
+ * 1 - GID_PT issued
+ **/
+int
+lpfc_issue_gidpt(struct lpfc_vport *vport)
+{
+ /* Good status, issue CT Request to NameServer */
+ if (lpfc_ns_cmd(vport, SLI_CTNS_GID_PT, 0, GID_PT_N_PORT)) {
+ /* Cannot issue NameServer FCP Query, so finish up
+ * discovery
+ */
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0606 %s Port TYPE %x %s\n",
+ "Failed to issue GID_PT to ",
+ GID_PT_N_PORT,
+ "Finishing discovery.");
+ return 0;
+ }
+ vport->gidft_inp++;
+ return 1;
+}
+
/*
* This routine handles processing a NameServer REG_LOGIN mailbox
* command upon completion. It is setup in the LPFC_MBOXQ
@@ -3458,28 +4313,34 @@ void
lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
MAILBOX_t *mb = &pmb->u.mb;
- struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
+ struct lpfc_nodelist *ndlp = pmb->ctx_ndlp;
struct lpfc_vport *vport = pmb->vport;
+ int rc;
- pmb->context1 = NULL;
- pmb->context2 = NULL;
+ pmb->ctx_ndlp = NULL;
+ vport->gidft_inp = 0;
if (mb->mbxStatus) {
-out:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0260 Register NameServer error: 0x%x\n",
mb->mbxStatus);
+
+out:
/* decrement the node reference count held for this
* callback function.
*/
lpfc_nlp_put(ndlp);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
- /* If no other thread is using the ndlp, free it */
- lpfc_nlp_not_used(ndlp);
+ /* If the node is not registered with the scsi or nvme
+ * transport, remove the fabric node. The failed reg_login
+ * is terminal and forces the removal of the last node
+ * reference.
+ */
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ lpfc_nlp_put(ndlp);
+ }
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
/*
@@ -3498,40 +4359,118 @@ out:
if (phba->sli_rev < LPFC_SLI_REV4)
ndlp->nlp_rpi = mb->un.varWords[0];
- ndlp->nlp_flag |= NLP_RPI_REGISTERED;
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY,
+ "0003 rpi:%x DID:%x flg:%lx %d x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
+ kref_read(&ndlp->kref),
+ ndlp);
if (vport->port_state < LPFC_VPORT_READY) {
/* Link up discovery requires Fabric registration. */
- lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0); /* Do this first! */
lpfc_ns_cmd(vport, SLI_CTNS_RNN_ID, 0, 0);
lpfc_ns_cmd(vport, SLI_CTNS_RSNN_NN, 0, 0);
lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
+ if (phba->pni)
+ lpfc_ns_cmd(vport, SLI_CTNS_RSPNI_PNI, 0, 0);
lpfc_ns_cmd(vport, SLI_CTNS_RFT_ID, 0, 0);
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP))
+ lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, FC_TYPE_FCP);
+
+ if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME))
+ lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0,
+ FC_TYPE_NVME);
+
/* Issue SCR just before NameServer GID_FT Query */
- lpfc_issue_els_scr(vport, SCR_DID, 0);
+ lpfc_issue_els_scr(vport, 0);
+
+ /* Link was bounced or a Fabric LOGO occurred. Start EDC
+ * with initial FW values provided the congestion mode is
+ * not off. Note that signals may or may not be supported
+ * by the adapter but FPIN is provided by default for 1
+ * or both missing signals support.
+ */
+ if (phba->cmf_active_mode != LPFC_CFG_OFF) {
+ phba->cgn_reg_fpin = phba->cgn_init_reg_fpin;
+ phba->cgn_reg_signal = phba->cgn_init_reg_signal;
+ rc = lpfc_issue_els_edc(vport, 0);
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_INIT | LOG_ELS | LOG_DISCOVERY,
+ "4220 Issue EDC status x%x Data x%x\n",
+ rc, phba->cgn_init_reg_signal);
+ } else if (phba->lmt & LMT_64Gb) {
+ /* may send link fault capability descriptor */
+ lpfc_issue_els_edc(vport, 0);
+ } else {
+ lpfc_issue_els_rdf(vport, 0);
+ }
}
vport->fc_ns_retry = 0;
- /* Good status, issue CT Request to NameServer */
- if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, 0)) {
- /* Cannot issue NameServer Query, so finish up discovery */
+ if (lpfc_issue_gidft(vport) == 0)
goto out;
- }
- /* decrement the node reference count held for this
+ /*
+ * At this point in time we may need to wait for multiple
+ * SLI_CTNS_GID_FT CT commands to complete before we start discovery.
+ *
+ * decrement the node reference count held for this
* callback function.
*/
lpfc_nlp_put(ndlp);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
-
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
return;
}
+/*
+ * This routine handles processing a Fabric Controller REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_fc_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ struct lpfc_vport *vport = pmb->vport;
+ MAILBOX_t *mb = &pmb->u.mb;
+ struct lpfc_nodelist *ndlp = pmb->ctx_ndlp;
+
+ pmb->ctx_ndlp = NULL;
+ if (mb->mbxStatus) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0933 %s: Register FC login error: 0x%x\n",
+ __func__, mb->mbxStatus);
+ goto out;
+ }
+
+ lpfc_check_nlp_post_devloss(vport, ndlp);
+
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ ndlp->nlp_rpi = mb->un.varWords[0];
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ "0934 %s: Complete FC x%x RegLogin rpi x%x ste x%x\n",
+ __func__, ndlp->nlp_DID, ndlp->nlp_rpi,
+ ndlp->nlp_state);
+
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
+ clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
+ ndlp->nlp_type |= NLP_FABRIC;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+
+ out:
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
+
+ /* Drop the reference count from the mbox at the end after
+ * all the current reference to the ndlp have been done.
+ */
+ lpfc_nlp_put(ndlp);
+}
+
static void
lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
@@ -3540,6 +4479,10 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
struct lpfc_rport_data *rdata;
struct fc_rport_identifiers rport_ids;
struct lpfc_hba *phba = vport->phba;
+ unsigned long flags;
+
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
+ return;
/* Remote port has reappeared. Re-register w/ FC transport */
rport_ids.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn);
@@ -3547,50 +4490,63 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
rport_ids.port_id = ndlp->nlp_DID;
rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
- /*
- * We leave our node pointer in rport->dd_data when we unregister a
- * FCP target port. But fc_remote_port_add zeros the space to which
- * rport->dd_data points. So, if we're reusing a previously
- * registered port, drop the reference that we took the last time we
- * registered the port.
- */
- if (ndlp->rport && ndlp->rport->dd_data &&
- ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp)
- lpfc_nlp_put(ndlp);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
- "rport add: did:x%x flg:x%x type x%x",
- ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
+ "rport add: did:x%x flg:x%lx type x%x",
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
/* Don't add the remote port if unloading. */
- if (vport->load_flag & FC_UNLOADING)
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
return;
ndlp->rport = rport = fc_remote_port_add(shost, 0, &rport_ids);
- if (!rport || !get_device(&rport->dev)) {
+ if (!rport) {
dev_printk(KERN_WARNING, &phba->pcidev->dev,
"Warning: fc_remote_port_add failed\n");
return;
}
- /* initialize static port data */
+ /* Successful port add. Complete initializing node data */
rport->maxframe_size = ndlp->nlp_maxframe;
rport->supported_classes = ndlp->nlp_class_sup;
rdata = rport->dd_data;
rdata->pnode = lpfc_nlp_get(ndlp);
+ if (!rdata->pnode) {
+ dev_warn(&phba->pcidev->dev,
+ "Warning - node ref failed. Unreg rport\n");
+ fc_remote_port_delete(rport);
+ ndlp->rport = NULL;
+ return;
+ }
+
+ spin_lock_irqsave(&ndlp->lock, flags);
+ ndlp->fc4_xpt_flags |= SCSI_XPT_REGD;
+ spin_unlock_irqrestore(&ndlp->lock, flags);
if (ndlp->nlp_type & NLP_FCP_TARGET)
- rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+ rport_ids.roles |= FC_PORT_ROLE_FCP_TARGET;
if (ndlp->nlp_type & NLP_FCP_INITIATOR)
- rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ rport_ids.roles |= FC_PORT_ROLE_FCP_INITIATOR;
+ if (ndlp->nlp_type & NLP_NVME_INITIATOR)
+ rport_ids.roles |= FC_PORT_ROLE_NVME_INITIATOR;
+ if (ndlp->nlp_type & NLP_NVME_TARGET)
+ rport_ids.roles |= FC_PORT_ROLE_NVME_TARGET;
+ if (ndlp->nlp_type & NLP_NVME_DISCOVERY)
+ rport_ids.roles |= FC_PORT_ROLE_NVME_DISCOVERY;
if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN)
fc_remote_port_rolechg(rport, rport_ids.roles);
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
+ "3183 %s rport x%px DID x%x, role x%x refcnt %d\n",
+ __func__, rport, rport->port_id, rport->roles,
+ kref_read(&ndlp->kref));
+
if ((rport->scsi_target_id != -1) &&
(rport->scsi_target_id < LPFC_MAX_TARGET)) {
ndlp->nlp_sid = rport->scsi_target_id;
}
+
return;
}
@@ -3598,112 +4554,254 @@ static void
lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp)
{
struct fc_rport *rport = ndlp->rport;
+ struct lpfc_vport *vport = ndlp->vport;
+
+ if (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
+ return;
- lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_RPORT,
- "rport delete: did:x%x flg:x%x type x%x",
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
+ "rport delete: did:x%x flg:x%lx type x%x",
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
- fc_remote_port_delete(rport);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ "3184 rport unregister x%06x, rport x%px "
+ "xptflg x%x refcnt %d\n",
+ ndlp->nlp_DID, rport, ndlp->fc4_xpt_flags,
+ kref_read(&ndlp->kref));
- return;
+ fc_remote_port_delete(rport);
+ lpfc_nlp_put(ndlp);
}
static void
lpfc_nlp_counters(struct lpfc_vport *vport, int state, int count)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- spin_lock_irq(shost->host_lock);
switch (state) {
case NLP_STE_UNUSED_NODE:
- vport->fc_unused_cnt += count;
+ atomic_add(count, &vport->fc_unused_cnt);
break;
case NLP_STE_PLOGI_ISSUE:
- vport->fc_plogi_cnt += count;
+ atomic_add(count, &vport->fc_plogi_cnt);
break;
case NLP_STE_ADISC_ISSUE:
- vport->fc_adisc_cnt += count;
+ atomic_add(count, &vport->fc_adisc_cnt);
break;
case NLP_STE_REG_LOGIN_ISSUE:
- vport->fc_reglogin_cnt += count;
+ atomic_add(count, &vport->fc_reglogin_cnt);
break;
case NLP_STE_PRLI_ISSUE:
- vport->fc_prli_cnt += count;
+ atomic_add(count, &vport->fc_prli_cnt);
break;
case NLP_STE_UNMAPPED_NODE:
- vport->fc_unmap_cnt += count;
+ atomic_add(count, &vport->fc_unmap_cnt);
break;
case NLP_STE_MAPPED_NODE:
- vport->fc_map_cnt += count;
+ atomic_add(count, &vport->fc_map_cnt);
break;
case NLP_STE_NPR_NODE:
- vport->fc_npr_cnt += count;
+ if (!atomic_read(&vport->fc_npr_cnt) && count == -1)
+ atomic_set(&vport->fc_npr_cnt, 0);
+ else
+ atomic_add(count, &vport->fc_npr_cnt);
break;
}
- spin_unlock_irq(shost->host_lock);
+}
+
+/* Register a node with backend if not already done */
+void
+lpfc_nlp_reg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+ unsigned long iflags;
+
+ lpfc_check_nlp_post_devloss(vport, ndlp);
+
+ spin_lock_irqsave(&ndlp->lock, iflags);
+ if (ndlp->fc4_xpt_flags & NLP_XPT_REGD) {
+ /* Already registered with backend, trigger rescan */
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+
+ if (ndlp->fc4_xpt_flags & NVME_XPT_REGD &&
+ ndlp->nlp_type & (NLP_NVME_TARGET | NLP_NVME_DISCOVERY)) {
+ lpfc_nvme_rescan_port(vport, ndlp);
+ }
+ return;
+ }
+
+ ndlp->fc4_xpt_flags |= NLP_XPT_REGD;
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+
+ if (lpfc_valid_xpt_node(ndlp)) {
+ vport->phba->nport_event_cnt++;
+ /*
+ * Tell the fc transport about the port, if we haven't
+ * already. If we have, and it's a scsi entity, be
+ */
+ lpfc_register_remote_port(vport, ndlp);
+ }
+
+ /* We are done if we do not have any NVME remote node */
+ if (!(ndlp->nlp_fc4_type & NLP_FC4_NVME))
+ return;
+
+ /* Notify the NVME transport of this new rport. */
+ if (vport->phba->sli_rev >= LPFC_SLI_REV4 &&
+ ndlp->nlp_fc4_type & NLP_FC4_NVME) {
+ if (vport->phba->nvmet_support == 0) {
+ /* Register this rport with the transport.
+ * Only NVME Target Rports are registered with
+ * the transport.
+ */
+ if (ndlp->nlp_type & NLP_NVME_TARGET) {
+ vport->phba->nport_event_cnt++;
+ lpfc_nvme_register_port(vport, ndlp);
+ }
+ } else {
+ /* Just take an NDLP ref count since the
+ * target does not register rports.
+ */
+ lpfc_nlp_get(ndlp);
+ }
+ }
+}
+
+/* Unregister a node with backend if not already done */
+void
+lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+ unsigned long iflags;
+
+ spin_lock_irqsave(&ndlp->lock, iflags);
+ if (!(ndlp->fc4_xpt_flags & NLP_XPT_REGD)) {
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_NODE | LOG_DISCOVERY,
+ "0999 %s Not regd: ndlp x%px rport x%px DID "
+ "x%x FLG x%lx XPT x%x\n",
+ __func__, ndlp, ndlp->rport, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->fc4_xpt_flags);
+ return;
+ }
+
+ ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD;
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
+
+ if (ndlp->rport &&
+ ndlp->fc4_xpt_flags & SCSI_XPT_REGD) {
+ vport->phba->nport_event_cnt++;
+ lpfc_unregister_remote_port(ndlp);
+ } else if (!ndlp->rport) {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_NODE | LOG_DISCOVERY,
+ "1999 %s NDLP in devloss x%px DID x%x FLG x%lx"
+ " XPT x%x refcnt %u\n",
+ __func__, ndlp, ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->fc4_xpt_flags,
+ kref_read(&ndlp->kref));
+ }
+
+ if (ndlp->fc4_xpt_flags & NVME_XPT_REGD) {
+ vport->phba->nport_event_cnt++;
+ if (vport->phba->nvmet_support == 0) {
+ lpfc_nvme_unregister_port(vport, ndlp);
+ } else {
+ /* NVMET has no upcall. */
+ lpfc_nlp_put(ndlp);
+ }
+ }
+
+}
+
+/*
+ * Adisc state change handling
+ */
+static void
+lpfc_handle_adisc_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ int new_state)
+{
+ switch (new_state) {
+ /*
+ * Any state to ADISC_ISSUE
+ * Do nothing, adisc cmpl handling will trigger state changes
+ */
+ case NLP_STE_ADISC_ISSUE:
+ break;
+
+ /*
+ * ADISC_ISSUE to mapped states
+ * Trigger a registration with backend, it will be nop if
+ * already registered
+ */
+ case NLP_STE_UNMAPPED_NODE:
+ ndlp->nlp_type |= NLP_FC_NODE;
+ fallthrough;
+ case NLP_STE_MAPPED_NODE:
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ lpfc_nlp_reg_node(vport, ndlp);
+ break;
+
+ /*
+ * ADISC_ISSUE to non-mapped states
+ * We are moving from ADISC_ISSUE to a non-mapped state because
+ * ADISC failed, we would have skipped unregistering with
+ * backend, attempt it now
+ */
+ case NLP_STE_NPR_NODE:
+ clear_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag);
+ fallthrough;
+ default:
+ lpfc_nlp_unreg_node(vport, ndlp);
+ break;
+ }
+
}
static void
lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int old_state, int new_state)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ /* Trap ADISC changes here */
+ if (new_state == NLP_STE_ADISC_ISSUE ||
+ old_state == NLP_STE_ADISC_ISSUE) {
+ lpfc_handle_adisc_state(vport, ndlp, new_state);
+ return;
+ }
if (new_state == NLP_STE_UNMAPPED_NODE) {
- ndlp->nlp_flag &= ~NLP_NODEV_REMOVE;
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
ndlp->nlp_type |= NLP_FC_NODE;
}
if (new_state == NLP_STE_MAPPED_NODE)
- ndlp->nlp_flag &= ~NLP_NODEV_REMOVE;
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
if (new_state == NLP_STE_NPR_NODE)
- ndlp->nlp_flag &= ~NLP_RCV_PLOGI;
+ clear_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag);
- /* Transport interface */
- if (ndlp->rport && (old_state == NLP_STE_MAPPED_NODE ||
- old_state == NLP_STE_UNMAPPED_NODE)) {
- vport->phba->nport_event_cnt++;
- lpfc_unregister_remote_port(ndlp);
+ /* Reg/Unreg for FCP and NVME Transport interface */
+ if ((old_state == NLP_STE_MAPPED_NODE ||
+ old_state == NLP_STE_UNMAPPED_NODE)) {
+ /* For nodes marked for ADISC, Handle unreg in ADISC cmpl
+ * if linkup. In linkdown do unreg_node
+ */
+ if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag) ||
+ !lpfc_is_link_up(vport->phba))
+ lpfc_nlp_unreg_node(vport, ndlp);
}
if (new_state == NLP_STE_MAPPED_NODE ||
- new_state == NLP_STE_UNMAPPED_NODE) {
- vport->phba->nport_event_cnt++;
- /*
- * Tell the fc transport about the port, if we haven't
- * already. If we have, and it's a scsi entity, be
- * sure to unblock any attached scsi devices
- */
- lpfc_register_remote_port(vport, ndlp);
- }
- if ((new_state == NLP_STE_MAPPED_NODE) &&
- (vport->stat_data_enabled)) {
- /*
- * A new target is discovered, if there is no buffer for
- * statistical data collection allocate buffer.
- */
- ndlp->lat_data = kcalloc(LPFC_MAX_BUCKET_COUNT,
- sizeof(struct lpfc_scsicmd_bkt),
- GFP_KERNEL);
+ new_state == NLP_STE_UNMAPPED_NODE)
+ lpfc_nlp_reg_node(vport, ndlp);
- if (!ndlp->lat_data)
- lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
- "0286 lpfc_nlp_state_cleanup failed to "
- "allocate statistical data buffer DID "
- "0x%x\n", ndlp->nlp_DID);
- }
/*
- * if we added to Mapped list, but the remote port
- * registration failed or assigned a target id outside
- * our presentable range - move the node to the
- * Unmapped List
+ * If the node just added to Mapped list was an FCP target,
+ * but the remote port registration failed or assigned a target
+ * id outside the presentable range - move the node to the
+ * Unmapped List.
*/
- if (new_state == NLP_STE_MAPPED_NODE &&
+ if ((new_state == NLP_STE_MAPPED_NODE) &&
+ (ndlp->nlp_type & NLP_FCP_TARGET) &&
(!ndlp->rport ||
ndlp->rport->scsi_target_id == -1 ||
ndlp->rport->scsi_target_id >= LPFC_MAX_TARGET)) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_TGT_NO_SCSIID;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_TGT_NO_SCSIID, &ndlp->nlp_flag);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
}
}
@@ -3717,13 +4815,14 @@ lpfc_nlp_state_name(char *buffer, size_t size, int state)
[NLP_STE_ADISC_ISSUE] = "ADISC",
[NLP_STE_REG_LOGIN_ISSUE] = "REGLOGIN",
[NLP_STE_PRLI_ISSUE] = "PRLI",
+ [NLP_STE_LOGO_ISSUE] = "LOGO",
[NLP_STE_UNMAPPED_NODE] = "UNMAPPED",
[NLP_STE_MAPPED_NODE] = "MAPPED",
[NLP_STE_NPR_NODE] = "NPR",
};
if (state < NLP_STE_MAX_STATE && states[state])
- strlcpy(buffer, states[state], size);
+ strscpy(buffer, states[state], size);
else
snprintf(buffer, size, "unknown (%d)", state);
return buffer;
@@ -3733,9 +4832,10 @@ void
lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int state)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
int old_state = ndlp->nlp_state;
+ bool node_dropped = test_bit(NLP_DROPPED, &ndlp->nlp_flag);
char name1[16], name2[16];
+ unsigned long iflags;
lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
"0904 NPort state transition x%06x, %s -> %s\n",
@@ -3747,18 +4847,24 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
"node statechg did:x%x old:%d ste:%d",
ndlp->nlp_DID, old_state, state);
+ if (node_dropped && old_state == NLP_STE_UNUSED_NODE &&
+ state != NLP_STE_UNUSED_NODE) {
+ clear_bit(NLP_DROPPED, &ndlp->nlp_flag);
+ lpfc_nlp_get(ndlp);
+ }
+
if (old_state == NLP_STE_NPR_NODE &&
state != NLP_STE_NPR_NODE)
lpfc_cancel_retry_delay_tmo(vport, ndlp);
if (old_state == NLP_STE_UNMAPPED_NODE) {
- ndlp->nlp_flag &= ~NLP_TGT_NO_SCSIID;
+ clear_bit(NLP_TGT_NO_SCSIID, &ndlp->nlp_flag);
ndlp->nlp_type &= ~NLP_FC_NODE;
}
if (list_empty(&ndlp->nlp_listp)) {
- spin_lock_irq(shost->host_lock);
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
list_add_tail(&ndlp->nlp_listp, &vport->fc_nodes);
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
} else if (old_state)
lpfc_nlp_counters(vport, old_state, -1);
@@ -3770,39 +4876,30 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void
lpfc_enqueue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ unsigned long iflags;
if (list_empty(&ndlp->nlp_listp)) {
- spin_lock_irq(shost->host_lock);
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
list_add_tail(&ndlp->nlp_listp, &vport->fc_nodes);
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
}
}
void
lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ unsigned long iflags;
lpfc_cancel_retry_delay_tmo(vport, ndlp);
if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp))
lpfc_nlp_counters(vport, ndlp->nlp_state, -1);
- spin_lock_irq(shost->host_lock);
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
list_del_init(&ndlp->nlp_listp);
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
NLP_STE_UNUSED_NODE);
}
-static void
-lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
-{
- lpfc_cancel_retry_delay_tmo(vport, ndlp);
- if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp))
- lpfc_nlp_counters(vport, ndlp->nlp_state, -1);
- lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
- NLP_STE_UNUSED_NODE);
-}
/**
* lpfc_initialize_node - Initialize all fields of node object
* @vport: Pointer to Virtual Port object.
@@ -3823,71 +4920,18 @@ lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
- init_timer(&ndlp->nlp_delayfunc);
- ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
- ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+ timer_setup(&ndlp->nlp_delayfunc, lpfc_els_retry_delay, 0);
+ INIT_LIST_HEAD(&ndlp->recovery_evt.evt_listp);
+
ndlp->nlp_DID = did;
ndlp->vport = vport;
ndlp->phba = vport->phba;
ndlp->nlp_sid = NLP_NO_SID;
+ ndlp->nlp_fc4_type = NLP_FC4_NONE;
kref_init(&ndlp->kref);
- NLP_INT_NODE_ACT(ndlp);
atomic_set(&ndlp->cmd_pending, 0);
ndlp->cmd_qdepth = vport->cfg_tgt_queue_depth;
- if (vport->phba->sli_rev == LPFC_SLI_REV4)
- ndlp->nlp_rpi = lpfc_sli4_alloc_rpi(vport->phba);
-}
-
-struct lpfc_nodelist *
-lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
- int state)
-{
- struct lpfc_hba *phba = vport->phba;
- uint32_t did;
- unsigned long flags;
-
- if (!ndlp)
- return NULL;
-
- spin_lock_irqsave(&phba->ndlp_lock, flags);
- /* The ndlp should not be in memory free mode */
- if (NLP_CHK_FREE_REQ(ndlp)) {
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE,
- "0277 lpfc_enable_node: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
- return NULL;
- }
- /* The ndlp should not already be in active mode */
- if (NLP_CHK_NODE_ACT(ndlp)) {
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE,
- "0278 lpfc_enable_node: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
- return NULL;
- }
-
- /* Keep the original DID */
- did = ndlp->nlp_DID;
-
- /* re-initialize ndlp except of ndlp linked list pointer */
- memset((((char *)ndlp) + sizeof (struct list_head)), 0,
- sizeof (struct lpfc_nodelist) - sizeof (struct list_head));
- lpfc_initialize_node(vport, ndlp, did);
-
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
-
- if (state != NLP_STE_UNUSED_NODE)
- lpfc_nlp_set_state(vport, ndlp, state);
-
- lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
- "node enable: did:x%x",
- ndlp->nlp_DID, 0, 0);
- return ndlp;
+ ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING;
}
void
@@ -3895,18 +4939,23 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
/*
* Use of lpfc_drop_node and UNUSED list: lpfc_drop_node should
- * be used if we wish to issue the "last" lpfc_nlp_put() to remove
- * the ndlp from the vport. The ndlp marked as UNUSED on the list
- * until ALL other outstanding threads have completed. We check
- * that the ndlp not already in the UNUSED state before we proceed.
+ * be used when lpfc wants to remove the "last" lpfc_nlp_put() to
+ * release the ndlp from the vport when conditions are correct.
*/
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
return;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
- if (vport->phba->sli_rev == LPFC_SLI_REV4)
+ if (vport->phba->sli_rev == LPFC_SLI_REV4) {
lpfc_cleanup_vports_rrqs(vport, ndlp);
- lpfc_nlp_put(ndlp);
- return;
+ lpfc_unreg_rpi(vport, ndlp);
+ }
+
+ /* NLP_DROPPED means another thread already removed the initial
+ * reference from lpfc_nlp_init. If set, don't drop it again and
+ * introduce an imbalance.
+ */
+ if (!test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag))
+ lpfc_nlp_put(ndlp);
}
/*
@@ -3915,7 +4964,6 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
void
lpfc_set_disctmo(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
uint32_t tmo;
@@ -3936,18 +4984,17 @@ lpfc_set_disctmo(struct lpfc_vport *vport)
tmo, vport->port_state, vport->fc_flag);
}
- mod_timer(&vport->fc_disctmo, jiffies + HZ * tmo);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_DISC_TMO;
- spin_unlock_irq(shost->host_lock);
+ mod_timer(&vport->fc_disctmo, jiffies + secs_to_jiffies(tmo));
+ set_bit(FC_DISC_TMO, &vport->fc_flag);
/* Start Discovery Timer state <hba_state> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0247 Start Discovery Timer state x%x "
"Data: x%x x%lx x%x x%x\n",
vport->port_state, tmo,
- (unsigned long)&vport->fc_disctmo, vport->fc_plogi_cnt,
- vport->fc_adisc_cnt);
+ (unsigned long)&vport->fc_disctmo,
+ atomic_read(&vport->fc_plogi_cnt),
+ atomic_read(&vport->fc_adisc_cnt));
return;
}
@@ -3958,7 +5005,6 @@ lpfc_set_disctmo(struct lpfc_vport *vport)
int
lpfc_can_disctmo(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
unsigned long iflags;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
@@ -3966,11 +5012,10 @@ lpfc_can_disctmo(struct lpfc_vport *vport)
vport->port_state, vport->fc_ns_retry, vport->fc_flag);
/* Turn off discovery timer if its running */
- if (vport->fc_flag & FC_DISC_TMO) {
- spin_lock_irqsave(shost->host_lock, iflags);
- vport->fc_flag &= ~FC_DISC_TMO;
- spin_unlock_irqrestore(shost->host_lock, iflags);
- del_timer_sync(&vport->fc_disctmo);
+ if (test_bit(FC_DISC_TMO, &vport->fc_flag) ||
+ timer_pending(&vport->fc_disctmo)) {
+ clear_bit(FC_DISC_TMO, &vport->fc_flag);
+ timer_delete_sync(&vport->fc_disctmo);
spin_lock_irqsave(&vport->work_port_lock, iflags);
vport->work_port_events &= ~WORKER_DISC_TMO;
spin_unlock_irqrestore(&vport->work_port_lock, iflags);
@@ -3979,9 +5024,10 @@ lpfc_can_disctmo(struct lpfc_vport *vport)
/* Cancel Discovery Timer state <hba_state> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0248 Cancel Discovery Timer state x%x "
- "Data: x%x x%x x%x\n",
+ "Data: x%lx x%x x%x\n",
vport->port_state, vport->fc_flag,
- vport->fc_plogi_cnt, vport->fc_adisc_cnt);
+ atomic_read(&vport->fc_plogi_cnt),
+ atomic_read(&vport->fc_adisc_cnt));
return 0;
}
@@ -3995,42 +5041,92 @@ lpfc_check_sli_ndlp(struct lpfc_hba *phba,
struct lpfc_iocbq *iocb,
struct lpfc_nodelist *ndlp)
{
- struct lpfc_sli *psli = &phba->sli;
- IOCB_t *icmd = &iocb->iocb;
- struct lpfc_vport *vport = ndlp->vport;
+ struct lpfc_vport *vport = ndlp->vport;
+ u8 ulp_command;
+ u16 ulp_context;
+ u32 remote_id;
if (iocb->vport != vport)
return 0;
+ ulp_command = get_job_cmnd(phba, iocb);
+ ulp_context = get_job_ulpcontext(phba, iocb);
+ remote_id = get_job_els_rsp64_did(phba, iocb);
+
if (pring->ringno == LPFC_ELS_RING) {
- switch (icmd->ulpCommand) {
+ switch (ulp_command) {
case CMD_GEN_REQUEST64_CR:
- if (iocb->context_un.ndlp == ndlp)
+ if (iocb->ndlp == ndlp)
return 1;
+ break;
case CMD_ELS_REQUEST64_CR:
- if (icmd->un.elsreq64.remoteID == ndlp->nlp_DID)
+ if (remote_id == ndlp->nlp_DID)
return 1;
+ fallthrough;
case CMD_XMIT_ELS_RSP64_CX:
- if (iocb->context1 == (uint8_t *) ndlp)
+ if (iocb->ndlp == ndlp)
return 1;
}
- } else if (pring->ringno == psli->extra_ring) {
-
- } else if (pring->ringno == psli->fcp_ring) {
+ } else if (pring->ringno == LPFC_FCP_RING) {
/* Skip match check if waiting to relogin to FCP target */
if ((ndlp->nlp_type & NLP_FCP_TARGET) &&
- (ndlp->nlp_flag & NLP_DELAY_TMO)) {
+ test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag))
return 0;
- }
- if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) {
- return 1;
- }
- } else if (pring->ringno == psli->next_ring) {
+ if (ulp_context == ndlp->nlp_rpi)
+ return 1;
}
return 0;
}
+static void
+__lpfc_dequeue_nport_iocbs(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp, struct lpfc_sli_ring *pring,
+ struct list_head *dequeue_list)
+{
+ struct lpfc_iocbq *iocb, *next_iocb;
+
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+ /* Check to see if iocb matches the nport */
+ if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp))
+ /* match, dequeue */
+ list_move_tail(&iocb->list, dequeue_list);
+ }
+}
+
+static void
+lpfc_sli3_dequeue_nport_iocbs(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp, struct list_head *dequeue_list)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ uint32_t i;
+
+ spin_lock_irq(&phba->hbalock);
+ for (i = 0; i < psli->num_rings; i++)
+ __lpfc_dequeue_nport_iocbs(phba, ndlp, &psli->sli3_ring[i],
+ dequeue_list);
+ spin_unlock_irq(&phba->hbalock);
+}
+
+static void
+lpfc_sli4_dequeue_nport_iocbs(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp, struct list_head *dequeue_list)
+{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_queue *qp = NULL;
+
+ spin_lock_irq(&phba->hbalock);
+ list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
+ pring = qp->pring;
+ if (!pring)
+ continue;
+ spin_lock(&pring->ring_lock);
+ __lpfc_dequeue_nport_iocbs(phba, ndlp, pring, dequeue_list);
+ spin_unlock(&pring->ring_lock);
+ }
+ spin_unlock_irq(&phba->hbalock);
+}
+
/*
* Free resources / clean up outstanding I/Os
* associated with nlp_rpi in the LPFC_NODELIST entry.
@@ -4039,10 +5135,6 @@ static int
lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
{
LIST_HEAD(completions);
- struct lpfc_sli *psli;
- struct lpfc_sli_ring *pring;
- struct lpfc_iocbq *iocb, *next_iocb;
- uint32_t i;
lpfc_fabric_abort_nport(ndlp);
@@ -4050,30 +5142,11 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
* Everything that matches on txcmplq will be returned
* by firmware with a no rpi error.
*/
- psli = &phba->sli;
- if (ndlp->nlp_flag & NLP_RPI_REGISTERED) {
- /* Now process each ring */
- for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
-
- spin_lock_irq(&phba->hbalock);
- list_for_each_entry_safe(iocb, next_iocb, &pring->txq,
- list) {
- /*
- * Check to see if iocb matches the nport we are
- * looking for
- */
- if ((lpfc_check_sli_ndlp(phba, pring, iocb,
- ndlp))) {
- /* It matches, so deque and call compl
- with an error */
- list_move_tail(&iocb->list,
- &completions);
- pring->txq_cnt--;
- }
- }
- spin_unlock_irq(&phba->hbalock);
- }
+ if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) {
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ lpfc_sli3_dequeue_nport_iocbs(phba, ndlp, &completions);
+ else
+ lpfc_sli4_dequeue_nport_iocbs(phba, ndlp, &completions);
}
/* Cancel all the IOCBs from the completions list */
@@ -4083,6 +5156,78 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
return 0;
}
+/**
+ * lpfc_nlp_logo_unreg - Unreg mailbox completion handler before LOGO
+ * @phba: Pointer to HBA context object.
+ * @pmb: Pointer to mailbox object.
+ *
+ * This function will issue an ELS LOGO command after completing
+ * the UNREG_RPI.
+ **/
+static void
+lpfc_nlp_logo_unreg(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ struct lpfc_vport *vport = pmb->vport;
+ struct lpfc_nodelist *ndlp;
+
+ ndlp = pmb->ctx_ndlp;
+ if (!ndlp)
+ return;
+ lpfc_issue_els_logo(vport, ndlp, 0);
+
+ /* Check to see if there are any deferred events to process */
+ if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag) &&
+ ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "1434 UNREG cmpl deferred logo x%x "
+ "on NPort x%x Data: x%x x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_defer_did, ndlp);
+
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+ ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING;
+ lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
+ } else {
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+ }
+
+ /* The node has an outstanding reference for the unreg. Now
+ * that the LOGO action and cleanup are finished, release
+ * resources.
+ */
+ lpfc_nlp_put(ndlp);
+ mempool_free(pmb, phba->mbox_mem_pool);
+}
+
+/*
+ * Sets the mailbox completion handler to be used for the
+ * unreg_rpi command. The handler varies based on the state of
+ * the port and what will be happening to the rpi next.
+ */
+static void
+lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp, LPFC_MBOXQ_t *mbox)
+{
+ /* Driver always gets a reference on the mailbox job
+ * in support of async jobs.
+ */
+ mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!mbox->ctx_ndlp)
+ return;
+
+ if (test_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag)) {
+ mbox->mbox_cmpl = lpfc_nlp_logo_unreg;
+ } else if (phba->sli_rev == LPFC_SLI_REV4 &&
+ !test_bit(FC_UNLOADING, &vport->load_flag) &&
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
+ LPFC_SLI_INTF_IF_TYPE_2) &&
+ (kref_read(&ndlp->kref) > 0)) {
+ mbox->mbox_cmpl = lpfc_sli4_unreg_rpi_cmpl_clr;
+ } else {
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ }
+}
+
/*
* Free rpi associated with LPFC_NODELIST entry.
* This routine is called from lpfc_freenode(), when we are removing
@@ -4097,31 +5242,107 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *mbox;
- int rc;
+ int rc, acc_plogi = 1;
uint16_t rpi;
- if (ndlp->nlp_flag & NLP_RPI_REGISTERED) {
+ if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag) ||
+ test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag)) {
+ if (test_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag))
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "3366 RPI x%x needs to be "
+ "unregistered nlp_flag x%lx "
+ "did x%x\n",
+ ndlp->nlp_rpi, ndlp->nlp_flag,
+ ndlp->nlp_DID);
+
+ /* If there is already an UNREG in progress for this ndlp,
+ * no need to queue up another one.
+ */
+ if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag)) {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "1436 unreg_rpi SKIP UNREG x%x on "
+ "NPort x%x deferred x%x flg x%lx "
+ "Data: x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_defer_did,
+ ndlp->nlp_flag, ndlp);
+ goto out;
+ }
+
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mbox) {
/* SLI4 ports require the physical rpi value. */
rpi = ndlp->nlp_rpi;
if (phba->sli_rev == LPFC_SLI_REV4)
rpi = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
+
lpfc_unreg_login(phba, vport->vpi, rpi, mbox);
mbox->vport = vport;
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ lpfc_set_unreg_login_mbx_cmpl(phba, vport, ndlp, mbox);
+ if (!mbox->ctx_ndlp) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+ }
+
+ /* Accept PLOGIs after unreg_rpi_cmpl. */
+ if (mbox->mbox_cmpl == lpfc_sli4_unreg_rpi_cmpl_clr)
+ acc_plogi = 0;
+
+ if (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag))
+ set_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "1433 unreg_rpi UNREG x%x on "
+ "NPort x%x deferred flg x%lx "
+ "Data:x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp);
+
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED)
+ if (rc == MBX_NOT_FINISHED) {
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
mempool_free(mbox, phba->mbox_mem_pool);
+ acc_plogi = 1;
+ lpfc_nlp_put(ndlp);
+ }
+ } else {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "1444 Failed to allocate mempool "
+ "unreg_rpi UNREG x%x, "
+ "DID x%x, flag x%lx, "
+ "ndlp x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp);
+
+ /* Because mempool_alloc failed, we
+ * will issue a LOGO here and keep the rpi alive if
+ * not unloading.
+ */
+ if (!test_bit(FC_UNLOADING, &vport->load_flag)) {
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+ lpfc_issue_els_logo(vport, ndlp, 0);
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+ lpfc_nlp_set_state(vport, ndlp,
+ NLP_STE_NPR_NODE);
+ }
+
+ return 1;
}
lpfc_no_rpi(phba, ndlp);
-
+out:
if (phba->sli_rev != LPFC_SLI_REV4)
ndlp->nlp_rpi = 0;
- ndlp->nlp_flag &= ~NLP_RPI_REGISTERED;
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+ clear_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
+ if (acc_plogi)
+ clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
return 1;
}
+ clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
return 0;
}
@@ -4137,27 +5358,28 @@ lpfc_unreg_hba_rpis(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
struct lpfc_nodelist *ndlp;
- struct Scsi_Host *shost;
int i;
+ unsigned long iflags;
vports = lpfc_create_vport_work_array(phba);
if (!vports) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
- "2884 Vport array allocation failed \n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2884 Vport array allocation failed \n");
return;
}
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- shost = lpfc_shost_from_vport(vports[i]);
- spin_lock_irq(shost->host_lock);
+ spin_lock_irqsave(&vports[i]->fc_nodes_list_lock, iflags);
list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) {
- if (ndlp->nlp_flag & NLP_RPI_REGISTERED) {
+ if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) {
/* The mempool_alloc might sleep */
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vports[i]->fc_nodes_list_lock,
+ iflags);
lpfc_unreg_rpi(vports[i], ndlp);
- spin_lock_irq(shost->host_lock);
+ spin_lock_irqsave(&vports[i]->fc_nodes_list_lock,
+ iflags);
}
}
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vports[i]->fc_nodes_list_lock, iflags);
}
lpfc_destroy_vport_work_array(phba, vports);
}
@@ -4180,15 +5402,16 @@ lpfc_unreg_all_rpis(struct lpfc_vport *vport)
mbox);
mbox->vport = vport;
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->context1 = NULL;
+ mbox->ctx_ndlp = NULL;
rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
if (rc != MBX_TIMEOUT)
mempool_free(mbox, phba->mbox_mem_pool);
if ((rc == MBX_TIMEOUT) || (rc == MBX_NOT_FINISHED))
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_VPORT,
- "1836 Could not issue "
- "unreg_login(all_rpis) status %d\n", rc);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "1836 Could not issue "
+ "unreg_login(all_rpis) status %d\n",
+ rc);
}
}
@@ -4199,19 +5422,23 @@ lpfc_unreg_default_rpis(struct lpfc_vport *vport)
LPFC_MBOXQ_t *mbox;
int rc;
+ /* Unreg DID is an SLI3 operation. */
+ if (phba->sli_rev > LPFC_SLI_REV3)
+ return;
+
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mbox) {
lpfc_unreg_did(phba, vport->vpi, LPFC_UNREG_ALL_DFLT_RPIS,
mbox);
mbox->vport = vport;
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->context1 = NULL;
+ mbox->ctx_ndlp = NULL;
rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
if (rc != MBX_TIMEOUT)
mempool_free(mbox, phba->mbox_mem_pool);
if ((rc == MBX_TIMEOUT) || (rc == MBX_NOT_FINISHED))
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1815 Could not issue "
"unreg_did (default rpis) status %d\n",
rc);
@@ -4225,38 +5452,25 @@ lpfc_unreg_default_rpis(struct lpfc_vport *vport)
static int
lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *mb, *nextmb;
- struct lpfc_dmabuf *mp;
/* Cleanup node for NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
"0900 Cleanup node for NPort x%x "
- "Data: x%x x%x x%x\n",
+ "Data: x%lx x%x x%x\n",
ndlp->nlp_DID, ndlp->nlp_flag,
ndlp->nlp_state, ndlp->nlp_rpi);
- if (NLP_CHK_FREE_REQ(ndlp)) {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE,
- "0280 lpfc_cleanup_node: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
- lpfc_dequeue_node(vport, ndlp);
- } else {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE,
- "0281 lpfc_cleanup_node: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
- lpfc_disable_node(vport, ndlp);
- }
+ lpfc_dequeue_node(vport, ndlp);
+
+ /* Don't need to clean up REG_LOGIN64 cmds for Default RPI cleanup */
/* cleanup any ndlp on mbox q waiting for reglogin cmpl */
if ((mb = phba->sli.mbox_active)) {
if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
- (ndlp == (struct lpfc_nodelist *) mb->context2)) {
- mb->context2 = NULL;
+ !(mb->mbox_flag & LPFC_MBX_IMED_UNREG) &&
+ (ndlp == mb->ctx_ndlp)) {
+ mb->ctx_ndlp = NULL;
mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
}
}
@@ -4265,26 +5479,23 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
/* Cleanup REG_LOGIN completions which are not yet processed */
list_for_each_entry(mb, &phba->sli.mboxq_cmpl, list) {
if ((mb->u.mb.mbxCommand != MBX_REG_LOGIN64) ||
- (ndlp != (struct lpfc_nodelist *) mb->context2))
+ (mb->mbox_flag & LPFC_MBX_IMED_UNREG) ||
+ (ndlp != mb->ctx_ndlp))
continue;
- mb->context2 = NULL;
+ mb->ctx_ndlp = NULL;
mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
}
list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
- (ndlp == (struct lpfc_nodelist *) mb->context2)) {
- mp = (struct lpfc_dmabuf *) (mb->context1);
- if (mp) {
- __lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
+ !(mb->mbox_flag & LPFC_MBX_IMED_UNREG) &&
+ (ndlp == mb->ctx_ndlp)) {
list_del(&mb->list);
- mempool_free(mb, phba->mbox_mem_pool);
- /* We shall not invoke the lpfc_nlp_put to decrement
- * the ndlp reference count as we are in the process
- * of lpfc_nlp_release.
+ lpfc_mbox_rsrc_cleanup(phba, mb, MBOX_THD_LOCKED);
+
+ /* Don't invoke lpfc_nlp_put. The driver is in
+ * lpfc_nlp_release context.
*/
}
}
@@ -4292,74 +5503,18 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
lpfc_els_abort(phba, ndlp);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = 0;
- del_timer_sync(&ndlp->nlp_delayfunc);
+ timer_delete_sync(&ndlp->nlp_delayfunc);
list_del_init(&ndlp->els_retry_evt.evt_listp);
list_del_init(&ndlp->dev_loss_evt.evt_listp);
+ list_del_init(&ndlp->recovery_evt.evt_listp);
lpfc_cleanup_vports_rrqs(vport, ndlp);
- lpfc_unreg_rpi(vport, ndlp);
-
return 0;
}
-/*
- * Check to see if we can free the nlp back to the freelist.
- * If we are in the middle of using the nlp in the discovery state
- * machine, defer the free till we reach the end of the state machine.
- */
-static void
-lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
-{
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata;
- LPFC_MBOXQ_t *mbox;
- int rc;
-
- lpfc_cancel_retry_delay_tmo(vport, ndlp);
- if ((ndlp->nlp_flag & NLP_DEFER_RM) &&
- !(ndlp->nlp_flag & NLP_REG_LOGIN_SEND) &&
- !(ndlp->nlp_flag & NLP_RPI_REGISTERED)) {
- /* For this case we need to cleanup the default rpi
- * allocated by the firmware.
- */
- if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))
- != NULL) {
- rc = lpfc_reg_rpi(phba, vport->vpi, ndlp->nlp_DID,
- (uint8_t *) &vport->fc_sparam, mbox, ndlp->nlp_rpi);
- if (rc) {
- mempool_free(mbox, phba->mbox_mem_pool);
- }
- else {
- mbox->mbox_flag |= LPFC_MBX_IMED_UNREG;
- mbox->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi;
- mbox->vport = vport;
- mbox->context2 = NULL;
- rc =lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- mempool_free(mbox, phba->mbox_mem_pool);
- }
- }
- }
- }
- lpfc_cleanup_node(vport, ndlp);
-
- /*
- * We can get here with a non-NULL ndlp->rport because when we
- * unregister a rport we don't break the rport/node linkage. So if we
- * do, make sure we don't leaving any dangling pointers behind.
- */
- if (ndlp->rport) {
- rdata = ndlp->rport->dd_data;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- }
-}
-
static int
lpfc_matchdid(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint32_t did)
@@ -4384,9 +5539,19 @@ lpfc_matchdid(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (matchdid.un.b.id == ndlpdid.un.b.id) {
if ((mydid.un.b.domain == matchdid.un.b.domain) &&
(mydid.un.b.area == matchdid.un.b.area)) {
+ /* This code is supposed to match the ID
+ * for a private loop device that is
+ * connect to fl_port. But we need to
+ * check that the port did not just go
+ * from pt2pt to fabric or we could end
+ * up matching ndlp->nlp_DID 000001 to
+ * fabric DID 0x20101
+ */
if ((ndlpdid.un.b.domain == 0) &&
(ndlpdid.un.b.area == 0)) {
- if (ndlpdid.un.b.id)
+ if (ndlpdid.un.b.id &&
+ vport->phba->fc_topology ==
+ LPFC_TOPOLOGY_LOOP)
return 1;
}
return 0;
@@ -4410,27 +5575,35 @@ static struct lpfc_nodelist *
__lpfc_findnode_did(struct lpfc_vport *vport, uint32_t did)
{
struct lpfc_nodelist *ndlp;
+ struct lpfc_nodelist *np = NULL;
uint32_t data1;
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
if (lpfc_matchdid(vport, ndlp, did)) {
- data1 = (((uint32_t) ndlp->nlp_state << 24) |
- ((uint32_t) ndlp->nlp_xri << 16) |
- ((uint32_t) ndlp->nlp_type << 8) |
- ((uint32_t) ndlp->nlp_rpi & 0xff));
- lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ data1 = (((uint32_t)ndlp->nlp_state << 24) |
+ ((uint32_t)ndlp->nlp_xri << 16) |
+ ((uint32_t)ndlp->nlp_type << 8)
+ );
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE_VERBOSE,
"0929 FIND node DID "
- "Data: x%p x%x x%x x%x\n",
+ "Data: x%px x%x x%lx x%x x%x x%px\n",
ndlp, ndlp->nlp_DID,
- ndlp->nlp_flag, data1);
- return ndlp;
+ ndlp->nlp_flag, data1, ndlp->nlp_rpi,
+ ndlp->active_rrqs_xri_bitmap);
+
+ /* Check for new or potentially stale node */
+ if (ndlp->nlp_state != NLP_STE_UNUSED_NODE)
+ return ndlp;
+ np = ndlp;
}
}
- /* FIND node did <did> NOT FOUND */
- lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
- "0932 FIND node did x%x NOT FOUND.\n", did);
- return NULL;
+ if (!np)
+ /* FIND node did <did> NOT FOUND */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ "0932 FIND node did x%x NOT FOUND.\n", did);
+
+ return np;
}
struct lpfc_nodelist *
@@ -4447,67 +5620,134 @@ lpfc_findnode_did(struct lpfc_vport *vport, uint32_t did)
}
struct lpfc_nodelist *
+lpfc_findnode_mapped(struct lpfc_vport *vport)
+{
+ struct lpfc_nodelist *ndlp;
+ uint32_t data1;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&vport->fc_nodes_list_lock, iflags);
+
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ if (ndlp->nlp_state == NLP_STE_UNMAPPED_NODE ||
+ ndlp->nlp_state == NLP_STE_MAPPED_NODE) {
+ data1 = (((uint32_t)ndlp->nlp_state << 24) |
+ ((uint32_t)ndlp->nlp_xri << 16) |
+ ((uint32_t)ndlp->nlp_type << 8) |
+ ((uint32_t)ndlp->nlp_rpi & 0xff));
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock,
+ iflags);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE_VERBOSE,
+ "2025 FIND node DID MAPPED "
+ "Data: x%px x%x x%lx x%x x%px\n",
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, data1,
+ ndlp->active_rrqs_xri_bitmap);
+ return ndlp;
+ }
+ }
+ spin_unlock_irqrestore(&vport->fc_nodes_list_lock, iflags);
+
+ /* FIND node did <did> NOT FOUND */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ "2026 FIND mapped did NOT FOUND.\n");
+ return NULL;
+}
+
+struct lpfc_nodelist *
lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp;
ndlp = lpfc_findnode_did(vport, did);
if (!ndlp) {
- if ((vport->fc_flag & FC_RSCN_MODE) != 0 &&
+ if (vport->phba->nvmet_support)
+ return NULL;
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag) &&
lpfc_rscn_payload_check(vport, did) == 0)
return NULL;
- ndlp = (struct lpfc_nodelist *)
- mempool_alloc(vport->phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, did);
if (!ndlp)
return NULL;
- lpfc_nlp_init(vport, ndlp, did);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
- return ndlp;
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_NPR_NODE);
- if (!ndlp)
- return NULL;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6453 Setup New Node 2B_DISC x%x "
+ "Data:x%lx x%x x%lx\n",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, vport->fc_flag);
+
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
return ndlp;
}
- if ((vport->fc_flag & FC_RSCN_MODE) &&
- !(vport->fc_flag & FC_NDISC_ACTIVE)) {
+ /* The NVME Target does not want to actively manage an rport.
+ * The goal is to allow the target to reset its state and clear
+ * pending IO in preparation for the initiator to recover.
+ */
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag) &&
+ !test_bit(FC_NDISC_ACTIVE, &vport->fc_flag)) {
if (lpfc_rscn_payload_check(vport, did)) {
- /* If we've already received a PLOGI from this NPort
- * we don't need to try to discover it again.
- */
- if (ndlp->nlp_flag & NLP_RCV_PLOGI)
- return NULL;
/* Since this node is marked for discovery,
* delay timeout is not needed.
*/
lpfc_cancel_retry_delay_tmo(vport, ndlp);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
- } else
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6455 Setup RSCN Node 2B_DISC x%x "
+ "Data:x%lx x%x x%lx\n",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, vport->fc_flag);
+
+ /* NVME Target mode waits until rport is known to be
+ * impacted by the RSCN before it transitions. No
+ * active management - just go to NPR provided the
+ * node had a valid login.
+ */
+ if (vport->phba->nvmet_support)
+ return ndlp;
+
+ if (ndlp->nlp_state > NLP_STE_UNUSED_NODE &&
+ ndlp->nlp_state <= NLP_STE_PRLI_ISSUE) {
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RECOVERY);
+ }
+
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ } else {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6456 Skip Setup RSCN Node x%x "
+ "Data:x%lx x%x x%lx\n",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, vport->fc_flag);
ndlp = NULL;
+ }
} else {
- /* If we've already received a PLOGI from this NPort,
- * or we are already in the process of discovery on it,
- * we don't need to try to discover it again.
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6457 Setup Active Node 2B_DISC x%x "
+ "Data:x%lx x%x x%lx\n",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_state, vport->fc_flag);
+
+ /* If the initiator received a PLOGI from this NPort or if the
+ * initiator is already in the process of discovery on it,
+ * there's no need to try to discover it again.
*/
if (ndlp->nlp_state == NLP_STE_ADISC_ISSUE ||
ndlp->nlp_state == NLP_STE_PLOGI_ISSUE ||
- ndlp->nlp_flag & NLP_RCV_PLOGI)
+ (!vport->phba->nvmet_support &&
+ test_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag)))
return NULL;
+
+ if (vport->phba->nvmet_support)
+ return ndlp;
+
+ /* Moving to NPR state clears unsolicited flags and
+ * allows for rediscovery
+ */
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
}
return ndlp;
}
@@ -4553,14 +5793,14 @@ lpfc_disc_list_loopmap(struct lpfc_vport *vport)
return;
}
+/* SLI3 only */
void
lpfc_issue_clear_la(struct lpfc_hba *phba, struct lpfc_vport *vport)
{
LPFC_MBOXQ_t *mbox;
struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *extra_ring = &psli->ring[psli->extra_ring];
- struct lpfc_sli_ring *fcp_ring = &psli->ring[psli->fcp_ring];
- struct lpfc_sli_ring *next_ring = &psli->ring[psli->next_ring];
+ struct lpfc_sli_ring *extra_ring = &psli->sli3_ring[LPFC_EXTRA_RING];
+ struct lpfc_sli_ring *fcp_ring = &psli->sli3_ring[LPFC_FCP_RING];
int rc;
/*
@@ -4584,7 +5824,6 @@ lpfc_issue_clear_la(struct lpfc_hba *phba, struct lpfc_vport *vport)
lpfc_disc_flush_list(vport);
extra_ring->flag &= ~LPFC_STOP_IOCB_EVENT;
fcp_ring->flag &= ~LPFC_STOP_IOCB_EVENT;
- next_ring->flag &= ~LPFC_STOP_IOCB_EVENT;
phba->link_state = LPFC_HBA_ERROR;
}
}
@@ -4612,14 +5851,16 @@ lpfc_issue_reg_vpi(struct lpfc_hba *phba, struct lpfc_vport *vport)
void
lpfc_disc_start(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
uint32_t num_sent;
uint32_t clear_la_pending;
- int did_changed;
- if (!lpfc_is_link_up(phba))
+ if (!lpfc_is_link_up(phba)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
+ "3315 Link is not up %x\n",
+ phba->link_state);
return;
+ }
if (phba->link_state == LPFC_CLEAR_LA)
clear_la_pending = 1;
@@ -4631,20 +5872,17 @@ lpfc_disc_start(struct lpfc_vport *vport)
lpfc_set_disctmo(vport);
- if (vport->fc_prevDID == vport->fc_myDID)
- did_changed = 0;
- else
- did_changed = 1;
-
vport->fc_prevDID = vport->fc_myDID;
vport->num_disc_nodes = 0;
/* Start Discovery state <hba_state> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0202 Start Discovery hba state x%x "
- "Data: x%x x%x x%x\n",
- vport->port_state, vport->fc_flag, vport->fc_plogi_cnt,
- vport->fc_adisc_cnt);
+ "0202 Start Discovery port state x%x "
+ "flg x%lx Data: x%x x%x x%x\n",
+ vport->port_state, vport->fc_flag,
+ atomic_read(&vport->fc_plogi_cnt),
+ atomic_read(&vport->fc_adisc_cnt),
+ atomic_read(&vport->fc_npr_cnt));
/* First do ADISCs - if any */
num_sent = lpfc_els_disc_adisc(vport);
@@ -4652,11 +5890,12 @@ lpfc_disc_start(struct lpfc_vport *vport)
if (num_sent)
return;
- /* Register the VPI for SLI3, NON-NPIV only. */
+ /* Register the VPI for SLI3, NPIV only. */
if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
- !(vport->fc_flag & FC_PT2PT) &&
- !(vport->fc_flag & FC_RSCN_MODE) &&
+ !test_bit(FC_PT2PT, &vport->fc_flag) &&
+ !test_bit(FC_RSCN_MODE, &vport->fc_flag) &&
(phba->sli_rev < LPFC_SLI_REV4)) {
+ lpfc_issue_clear_la(phba, vport);
lpfc_issue_reg_vpi(phba, vport);
return;
}
@@ -4667,19 +5906,16 @@ lpfc_disc_start(struct lpfc_vport *vport)
*/
if (vport->port_state < LPFC_VPORT_READY && !clear_la_pending) {
/* If we get here, there is nothing to ADISC */
- if (vport->port_type == LPFC_PHYSICAL_PORT)
- lpfc_issue_clear_la(phba, vport);
+ lpfc_issue_clear_la(phba, vport);
- if (!(vport->fc_flag & FC_ABORT_DISCOVERY)) {
+ if (!test_bit(FC_ABORT_DISCOVERY, &vport->fc_flag)) {
vport->num_disc_nodes = 0;
/* go thru NPR nodes and issue ELS PLOGIs */
- if (vport->fc_npr_cnt)
+ if (atomic_read(&vport->fc_npr_cnt))
lpfc_els_disc_plogi(vport);
if (!vport->num_disc_nodes) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NDISC_ACTIVE;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_NDISC_ACTIVE, &vport->fc_flag);
lpfc_can_disctmo(vport);
}
}
@@ -4691,18 +5927,17 @@ lpfc_disc_start(struct lpfc_vport *vport)
if (num_sent)
return;
- if (vport->fc_flag & FC_RSCN_MODE) {
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
/* Check to see if more RSCNs came in while we
* were processing this one.
*/
- if ((vport->fc_rscn_id_cnt == 0) &&
- (!(vport->fc_flag & FC_RSCN_DISCOVERY))) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_RSCN_MODE;
- spin_unlock_irq(shost->host_lock);
+ if (vport->fc_rscn_id_cnt == 0 &&
+ !test_bit(FC_RSCN_DISCOVERY, &vport->fc_flag)) {
+ clear_bit(FC_RSCN_MODE, &vport->fc_flag);
lpfc_can_disctmo(vport);
- } else
+ } else {
lpfc_els_handle_rscn(vport);
+ }
}
}
return;
@@ -4716,44 +5951,48 @@ static void
lpfc_free_tx(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
{
LIST_HEAD(completions);
- struct lpfc_sli *psli;
- IOCB_t *icmd;
struct lpfc_iocbq *iocb, *next_iocb;
struct lpfc_sli_ring *pring;
+ u32 ulp_command;
- psli = &phba->sli;
- pring = &psli->ring[LPFC_ELS_RING];
+ pring = lpfc_phba_elsring(phba);
+ if (unlikely(!pring))
+ return;
/* Error matching iocb on txq or txcmplq
* First check the txq.
*/
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
- if (iocb->context1 != ndlp) {
+ if (iocb->ndlp != ndlp)
continue;
- }
- icmd = &iocb->iocb;
- if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) ||
- (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) {
+
+ ulp_command = get_job_cmnd(phba, iocb);
+
+ if (ulp_command == CMD_ELS_REQUEST64_CR ||
+ ulp_command == CMD_XMIT_ELS_RSP64_CX) {
list_move_tail(&iocb->list, &completions);
- pring->txq_cnt--;
}
}
/* Next check the txcmplq */
list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
- if (iocb->context1 != ndlp) {
+ if (iocb->ndlp != ndlp)
continue;
- }
- icmd = &iocb->iocb;
- if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR ||
- icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX) {
- lpfc_sli_issue_abort_iotag(phba, pring, iocb);
+
+ ulp_command = get_job_cmnd(phba, iocb);
+
+ if (ulp_command == CMD_ELS_REQUEST64_CR ||
+ ulp_command == CMD_XMIT_ELS_RSP64_CX) {
+ lpfc_sli_issue_abort_iotag(phba, pring, iocb, NULL);
}
}
spin_unlock_irq(&phba->hbalock);
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
/* Cancel all the IOCBs from the completions list */
lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
IOERR_SLI_ABORTED);
@@ -4765,11 +6004,10 @@ lpfc_disc_flush_list(struct lpfc_vport *vport)
struct lpfc_nodelist *ndlp, *next_ndlp;
struct lpfc_hba *phba = vport->phba;
- if (vport->fc_plogi_cnt || vport->fc_adisc_cnt) {
+ if (atomic_read(&vport->fc_plogi_cnt) ||
+ atomic_read(&vport->fc_adisc_cnt)) {
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes,
nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE ||
ndlp->nlp_state == NLP_STE_ADISC_ISSUE) {
lpfc_free_tx(phba, ndlp);
@@ -4778,12 +6016,34 @@ lpfc_disc_flush_list(struct lpfc_vport *vport)
}
}
+/*
+ * lpfc_notify_xport_npr - notifies xport of node disappearance
+ * @vport: Pointer to Virtual Port object.
+ *
+ * Transitions all ndlps to NPR state. When lpfc_nlp_set_state
+ * calls lpfc_nlp_state_cleanup, the ndlp->rport is unregistered
+ * and transport notified that the node is gone.
+ * Return Code:
+ * none
+ */
+static void
+lpfc_notify_xport_npr(struct lpfc_vport *vport)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+
+ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes,
+ nlp_listp) {
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ }
+}
void
lpfc_cleanup_discovery_resources(struct lpfc_vport *vport)
{
lpfc_els_flush_rscn(vport);
lpfc_els_flush_cmd(vport);
lpfc_disc_flush_list(vport);
+ if (pci_channel_offline(vport->phba->pcidev))
+ lpfc_notify_xport_npr(vport);
}
/*****************************************************************************/
@@ -4802,9 +6062,9 @@ lpfc_cleanup_discovery_resources(struct lpfc_vport *vport)
*/
/*****************************************************************************/
void
-lpfc_disc_timeout(unsigned long ptr)
+lpfc_disc_timeout(struct timer_list *t)
{
- struct lpfc_vport *vport = (struct lpfc_vport *) ptr;
+ struct lpfc_vport *vport = timer_container_of(vport, t, fc_disctmo);
struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted;
unsigned long flags = 0;
@@ -4826,20 +6086,15 @@ lpfc_disc_timeout(unsigned long ptr)
static void
lpfc_disc_timeout_handler(struct lpfc_vport *vport)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_sli *psli = &phba->sli;
struct lpfc_nodelist *ndlp, *next_ndlp;
LPFC_MBOXQ_t *initlinkmbox;
int rc, clrlaerr = 0;
- if (!(vport->fc_flag & FC_DISC_TMO))
+ if (!test_and_clear_bit(FC_DISC_TMO, &vport->fc_flag))
return;
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_DISC_TMO;
- spin_unlock_irq(shost->host_lock);
-
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"disc timeout: state:x%x rtry:x%x flg:x%x",
vport->port_state, vport->fc_ns_retry, vport->fc_flag);
@@ -4847,24 +6102,23 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
switch (vport->port_state) {
case LPFC_LOCAL_CFG_LINK:
- /* port_state is identically LPFC_LOCAL_CFG_LINK while waiting for
- * FAN
- */
- /* FAN timeout */
+ /*
+ * port_state is identically LPFC_LOCAL_CFG_LINK while
+ * waiting for FAN timeout
+ */
lpfc_printf_vlog(vport, KERN_WARNING, LOG_DISCOVERY,
"0221 FAN timeout\n");
+
/* Start discovery by sending FLOGI, clean up old rpis */
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes,
nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
if (ndlp->nlp_state != NLP_STE_NPR_NODE)
continue;
if (ndlp->nlp_type & NLP_FABRIC) {
/* Clean up the ndlp on Fabric connections */
lpfc_drop_node(vport, ndlp);
- } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
+ } else if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) {
/* Fail outstanding IO now since device
* is marked for PLOGI.
*/
@@ -4884,7 +6138,8 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
case LPFC_FLOGI:
/* port_state is identically LPFC_FLOGI while waiting for FLOGI cmpl */
/* Initial FLOGI timeout */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0222 Initial %s timeout\n",
vport->vpi ? "FDISC" : "FLOGI");
@@ -4902,12 +6157,13 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
case LPFC_FABRIC_CFG_LINK:
/* hba_state is identically LPFC_FABRIC_CFG_LINK while waiting for
NameServer login */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0223 Timeout while waiting for "
"NameServer login\n");
/* Next look for NameServer ndlp */
ndlp = lpfc_findnode_did(vport, NameServer_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp))
+ if (ndlp)
lpfc_els_abort(phba, ndlp);
/* ReStart discovery */
@@ -4915,7 +6171,8 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
case LPFC_NS_QRY:
/* Check for wait for NameServer Rsp timeout */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0224 NameServer Query timeout "
"Data: x%x x%x\n",
vport->fc_ns_retry, LPFC_MAX_NS_RETRY);
@@ -4923,8 +6180,8 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) {
/* Try it one more time */
vport->fc_ns_retry++;
- rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT,
- vport->fc_ns_retry, 0);
+ vport->gidft_inp = 0;
+ rc = lpfc_issue_gidft(vport);
if (rc == 0)
break;
}
@@ -4948,7 +6205,8 @@ restart_disc:
/* Setup and issue mailbox INITIALIZE LINK command */
initlinkmbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!initlinkmbox) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0206 Device Discovery "
"completion error\n");
phba->link_state = LPFC_HBA_ERROR;
@@ -4970,7 +6228,8 @@ restart_disc:
case LPFC_DISC_AUTH:
/* Node Authentication timeout */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0227 Node Authentication timeout\n");
lpfc_disc_flush_list(vport);
@@ -4989,11 +6248,13 @@ restart_disc:
break;
case LPFC_VPORT_READY:
- if (vport->fc_flag & FC_RSCN_MODE) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0231 RSCN timeout Data: x%x "
- "x%x\n",
- vport->fc_ns_retry, LPFC_MAX_NS_RETRY);
+ "x%x x%x x%x\n",
+ vport->fc_ns_retry, LPFC_MAX_NS_RETRY,
+ vport->port_state, vport->gidft_inp);
/* Cleanup any outstanding ELS commands */
lpfc_els_flush_cmd(vport);
@@ -5004,7 +6265,8 @@ restart_disc:
break;
default:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0273 Unexpected discovery timeout, "
"vport State x%x\n", vport->port_state);
break;
@@ -5013,21 +6275,23 @@ restart_disc:
switch (phba->link_state) {
case LPFC_CLEAR_LA:
/* CLEAR LA timeout */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0228 CLEAR LA timeout\n");
clrlaerr = 1;
break;
case LPFC_LINK_UP:
lpfc_issue_clear_la(phba, vport);
- /* Drop thru */
+ fallthrough;
case LPFC_LINK_UNKNOWN:
case LPFC_WARM_START:
case LPFC_INIT_START:
case LPFC_INIT_MBX_CMDS:
case LPFC_LINK_DOWN:
case LPFC_HBA_ERROR:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0230 Unexpected timeout, hba link "
"state x%x\n", phba->link_state);
clrlaerr = 1;
@@ -5039,12 +6303,14 @@ restart_disc:
if (clrlaerr) {
lpfc_disc_flush_list(vport);
- psli->ring[(psli->extra_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
- psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
- psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ psli->sli3_ring[(LPFC_EXTRA_RING)].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ psli->sli3_ring[LPFC_FCP_RING].flag &=
+ ~LPFC_STOP_IOCB_EVENT;
+ }
vport->port_state = LPFC_VPORT_READY;
}
-
return;
}
@@ -5058,38 +6324,40 @@ void
lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
MAILBOX_t *mb = &pmb->u.mb;
- struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
- struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
+ struct lpfc_nodelist *ndlp = pmb->ctx_ndlp;
struct lpfc_vport *vport = pmb->vport;
- pmb->context1 = NULL;
- pmb->context2 = NULL;
+ pmb->ctx_ndlp = NULL;
if (phba->sli_rev < LPFC_SLI_REV4)
ndlp->nlp_rpi = mb->un.varWords[0];
- ndlp->nlp_flag |= NLP_RPI_REGISTERED;
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
-
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_DISCOVERY,
+ "0004 rpi:%x DID:%x flg:%lx %d x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag,
+ kref_read(&ndlp->kref),
+ ndlp);
/*
* Start issuing Fabric-Device Management Interface (FDMI) command to
- * 0xfffffa (FDMI well known port) or Delay issuing FDMI command if
- * fdmi-on=2 (supporting RPA/hostnmae)
+ * 0xfffffa (FDMI well known port).
+ * DHBA -> DPRT -> RHBA -> RPA (physical port)
+ * DPRT -> RPRT (vports)
*/
+ if (vport->port_type == LPFC_PHYSICAL_PORT) {
+ phba->link_flag &= ~LS_CT_VEN_RPA; /* For extra Vendor RPA */
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
+ } else {
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0);
+ }
- if (vport->cfg_fdmi_on == 1)
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA);
- else
- mod_timer(&vport->fc_fdmitmo, jiffies + HZ * 60);
/* decrement the node reference count held for this callback
* function.
*/
lpfc_nlp_put(ndlp);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
-
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
return;
}
@@ -5114,9 +6382,20 @@ __lpfc_find_node(struct lpfc_vport *vport, node_filter filter, void *param)
struct lpfc_nodelist *ndlp;
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
- if (filter(ndlp, param))
+ if (filter(ndlp, param)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE_VERBOSE,
+ "3185 FIND node filter %ps DID "
+ "ndlp x%px did x%x flg x%lx st x%x "
+ "xri x%x type x%x rpi x%x\n",
+ filter, ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->nlp_state,
+ ndlp->nlp_xri, ndlp->nlp_type,
+ ndlp->nlp_rpi);
return ndlp;
+ }
}
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ "3186 FIND node filter %ps NOT FOUND.\n", filter);
return NULL;
}
@@ -5146,20 +6425,123 @@ lpfc_findnode_wwpn(struct lpfc_vport *vport, struct lpfc_name *wwpn)
return ndlp;
}
-void
-lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
- uint32_t did)
+/*
+ * This routine looks up the ndlp lists for the given RPI. If the rpi
+ * is found, the routine returns the node element list pointer else
+ * return NULL.
+ */
+struct lpfc_nodelist *
+lpfc_findnode_rpi(struct lpfc_vport *vport, uint16_t rpi)
+{
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_nodelist *ndlp;
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ ndlp = __lpfc_findnode_rpi(vport, rpi);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ return ndlp;
+}
+
+/**
+ * lpfc_find_vport_by_vpid - Find a vport on a HBA through vport identifier
+ * @phba: pointer to lpfc hba data structure.
+ * @vpi: the physical host virtual N_Port identifier.
+ *
+ * This routine finds a vport on a HBA (referred by @phba) through a
+ * @vpi. The function walks the HBA's vport list and returns the address
+ * of the vport with the matching @vpi.
+ *
+ * Return code
+ * NULL - No vport with the matching @vpi found
+ * Otherwise - Address to the vport with the matching @vpi.
+ **/
+struct lpfc_vport *
+lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi)
+{
+ struct lpfc_vport *vport;
+ unsigned long flags;
+ int i = 0;
+
+ /* The physical ports are always vpi 0 - translate is unnecessary. */
+ if (vpi > 0) {
+ /*
+ * Translate the physical vpi to the logical vpi. The
+ * vport stores the logical vpi.
+ */
+ for (i = 0; i <= phba->max_vpi; i++) {
+ if (vpi == phba->vpi_ids[i])
+ break;
+ }
+
+ if (i > phba->max_vpi) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2936 Could not find Vport mapped "
+ "to vpi %d\n", vpi);
+ return NULL;
+ }
+ }
+
+ spin_lock_irqsave(&phba->port_list_lock, flags);
+ list_for_each_entry(vport, &phba->port_list, listentry) {
+ if (vport->vpi == i) {
+ spin_unlock_irqrestore(&phba->port_list_lock, flags);
+ return vport;
+ }
+ }
+ spin_unlock_irqrestore(&phba->port_list_lock, flags);
+ return NULL;
+}
+
+struct lpfc_nodelist *
+lpfc_nlp_init(struct lpfc_vport *vport, uint32_t did)
{
+ struct lpfc_nodelist *ndlp;
+ int rpi = LPFC_RPI_ALLOC_ERROR;
+
+ if (vport->phba->sli_rev == LPFC_SLI_REV4) {
+ rpi = lpfc_sli4_alloc_rpi(vport->phba);
+ if (rpi == LPFC_RPI_ALLOC_ERROR)
+ return NULL;
+ }
+
+ ndlp = mempool_alloc(vport->phba->nlp_mem_pool, GFP_KERNEL);
+ if (!ndlp) {
+ if (vport->phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_sli4_free_rpi(vport->phba, rpi);
+ return NULL;
+ }
+
memset(ndlp, 0, sizeof (struct lpfc_nodelist));
+ spin_lock_init(&ndlp->lock);
+
lpfc_initialize_node(vport, ndlp, did);
INIT_LIST_HEAD(&ndlp->nlp_listp);
+ if (vport->phba->sli_rev == LPFC_SLI_REV4) {
+ ndlp->nlp_rpi = rpi;
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_NODE | LOG_DISCOVERY,
+ "0007 Init New ndlp x%px, rpi:x%x DID:x%x "
+ "flg:x%lx refcnt:%d\n",
+ ndlp, ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag, kref_read(&ndlp->kref));
+
+ ndlp->active_rrqs_xri_bitmap =
+ mempool_alloc(vport->phba->active_rrq_pool,
+ GFP_KERNEL);
+ if (ndlp->active_rrqs_xri_bitmap)
+ memset(ndlp->active_rrqs_xri_bitmap, 0,
+ ndlp->phba->cfg_rrq_xri_bitmap_sz);
+ }
+
+
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
"node init: did:x%x",
ndlp->nlp_DID, 0, 0);
- return;
+ return ndlp;
}
/* This routine releases all resources associated with a specifc NPort's ndlp
@@ -5168,37 +6550,44 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
static void
lpfc_nlp_release(struct kref *kref)
{
- struct lpfc_hba *phba;
- unsigned long flags;
struct lpfc_nodelist *ndlp = container_of(kref, struct lpfc_nodelist,
kref);
+ struct lpfc_vport *vport = ndlp->vport;
lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
- "node release: did:x%x flg:x%x type:x%x",
+ "node release: did:x%x flg:x%lx type:x%x",
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
- lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE,
- "0279 lpfc_nlp_release: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+ "0279 %s: ndlp: x%px did %x refcnt:%d rpi:%x\n",
+ __func__, ndlp, ndlp->nlp_DID,
+ kref_read(&ndlp->kref), ndlp->nlp_rpi);
/* remove ndlp from action. */
- lpfc_nlp_remove(ndlp->vport, ndlp);
+ lpfc_cancel_retry_delay_tmo(vport, ndlp);
+ lpfc_cleanup_node(vport, ndlp);
- /* clear the ndlp active flag for all release cases */
- phba = ndlp->phba;
- spin_lock_irqsave(&phba->ndlp_lock, flags);
- NLP_CLR_NODE_ACT(ndlp);
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
- if (phba->sli_rev == LPFC_SLI_REV4)
- lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi);
+ /* All nodes are initialized with an RPI that needs to be released
+ * now. All references are gone and the node has been dequeued.
+ */
+ if (vport->phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_sli4_free_rpi(vport->phba, ndlp->nlp_rpi);
+ ndlp->nlp_rpi = LPFC_RPI_ALLOC_ERROR;
+ }
+
+ /* The node is not freed back to memory, it is released to a pool so
+ * the node fields need to be cleaned up.
+ */
+ ndlp->vport = NULL;
+ ndlp->nlp_state = NLP_STE_FREED_NODE;
+ ndlp->nlp_flag = 0;
+ ndlp->fc4_xpt_flags = 0;
/* free ndlp memory for final ndlp release */
- if (NLP_CHK_FREE_REQ(ndlp)) {
- kfree(ndlp->lat_data);
- mempool_free(ndlp, ndlp->phba->nlp_mem_pool);
- }
+ if (ndlp->phba->sli_rev == LPFC_SLI_REV4)
+ mempool_free(ndlp->active_rrqs_xri_bitmap,
+ ndlp->phba->active_rrq_pool);
+ mempool_free(ndlp, ndlp->phba->nlp_mem_pool);
}
/* This routine bumps the reference count for a ndlp structure to ensure
@@ -5208,120 +6597,50 @@ lpfc_nlp_release(struct kref *kref)
struct lpfc_nodelist *
lpfc_nlp_get(struct lpfc_nodelist *ndlp)
{
- struct lpfc_hba *phba;
unsigned long flags;
if (ndlp) {
lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
- "node get: did:x%x flg:x%x refcnt:x%x",
+ "node get: did:x%x flg:x%lx refcnt:x%x",
ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount));
+ kref_read(&ndlp->kref));
+
/* The check of ndlp usage to prevent incrementing the
* ndlp reference count that is in the process of being
* released.
*/
- phba = ndlp->phba;
- spin_lock_irqsave(&phba->ndlp_lock, flags);
- if (!NLP_CHK_NODE_ACT(ndlp) || NLP_CHK_FREE_ACK(ndlp)) {
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
+ spin_lock_irqsave(&ndlp->lock, flags);
+ if (!kref_get_unless_zero(&ndlp->kref)) {
+ spin_unlock_irqrestore(&ndlp->lock, flags);
lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE,
- "0276 lpfc_nlp_get: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
+ "0276 %s: ndlp:x%px refcnt:%d\n",
+ __func__, (void *)ndlp, kref_read(&ndlp->kref));
return NULL;
- } else
- kref_get(&ndlp->kref);
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
+ }
+ spin_unlock_irqrestore(&ndlp->lock, flags);
+ } else {
+ WARN_ONCE(!ndlp, "**** %s, get ref on NULL ndlp!", __func__);
}
+
return ndlp;
}
/* This routine decrements the reference count for a ndlp structure. If the
- * count goes to 0, this indicates the the associated nodelist should be
- * freed. Returning 1 indicates the ndlp resource has been released; on the
- * other hand, returning 0 indicates the ndlp resource has not been released
- * yet.
+ * count goes to 0, this indicates the associated nodelist should be freed.
*/
int
lpfc_nlp_put(struct lpfc_nodelist *ndlp)
{
- struct lpfc_hba *phba;
- unsigned long flags;
-
- if (!ndlp)
- return 1;
-
- lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
- "node put: did:x%x flg:x%x refcnt:x%x",
- ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount));
- phba = ndlp->phba;
- spin_lock_irqsave(&phba->ndlp_lock, flags);
- /* Check the ndlp memory free acknowledge flag to avoid the
- * possible race condition that kref_put got invoked again
- * after previous one has done ndlp memory free.
- */
- if (NLP_CHK_FREE_ACK(ndlp)) {
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
- lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE,
- "0274 lpfc_nlp_put: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
- return 1;
- }
- /* Check the ndlp inactivate log flag to avoid the possible
- * race condition that kref_put got invoked again after ndlp
- * is already in inactivating state.
- */
- if (NLP_CHK_IACT_REQ(ndlp)) {
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
- lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE,
- "0275 lpfc_nlp_put: ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- (void *)ndlp, ndlp->nlp_usg_map,
- atomic_read(&ndlp->kref.refcount));
- return 1;
+ if (ndlp) {
+ lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
+ "node put: did:x%x flg:x%lx refcnt:x%x",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ kref_read(&ndlp->kref));
+ } else {
+ WARN_ONCE(!ndlp, "**** %s, put ref on NULL ndlp!", __func__);
}
- /* For last put, mark the ndlp usage flags to make sure no
- * other kref_get and kref_put on the same ndlp shall get
- * in between the process when the final kref_put has been
- * invoked on this ndlp.
- */
- if (atomic_read(&ndlp->kref.refcount) == 1) {
- /* Indicate ndlp is put to inactive state. */
- NLP_SET_IACT_REQ(ndlp);
- /* Acknowledge ndlp memory free has been seen. */
- if (NLP_CHK_FREE_REQ(ndlp))
- NLP_SET_FREE_ACK(ndlp);
- }
- spin_unlock_irqrestore(&phba->ndlp_lock, flags);
- /* Note, the kref_put returns 1 when decrementing a reference
- * count that was 1, it invokes the release callback function,
- * but it still left the reference count as 1 (not actually
- * performs the last decrementation). Otherwise, it actually
- * decrements the reference count and returns 0.
- */
- return kref_put(&ndlp->kref, lpfc_nlp_release);
-}
-/* This routine free's the specified nodelist if it is not in use
- * by any other discovery thread. This routine returns 1 if the
- * ndlp has been freed. A return value of 0 indicates the ndlp is
- * not yet been released.
- */
-int
-lpfc_nlp_not_used(struct lpfc_nodelist *ndlp)
-{
- lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
- "node not used: did:x%x flg:x%x refcnt:x%x",
- ndlp->nlp_DID, ndlp->nlp_flag,
- atomic_read(&ndlp->kref.refcount));
- if (atomic_read(&ndlp->kref.refcount) == 1)
- if (lpfc_nlp_put(ndlp))
- return 1;
- return 0;
+ return ndlp ? kref_put(&ndlp->kref, lpfc_nlp_release) : 0;
}
/**
@@ -5340,7 +6659,7 @@ lpfc_fcf_inuse(struct lpfc_hba *phba)
struct lpfc_vport **vports;
int i, ret = 0;
struct lpfc_nodelist *ndlp;
- struct Scsi_Host *shost;
+ unsigned long iflags;
vports = lpfc_create_vport_work_array(phba);
@@ -5349,36 +6668,36 @@ lpfc_fcf_inuse(struct lpfc_hba *phba)
return 1;
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- shost = lpfc_shost_from_vport(vports[i]);
- spin_lock_irq(shost->host_lock);
/*
* IF the CVL_RCVD bit is not set then we have sent the
* flogi.
* If dev_loss fires while we are waiting we do not want to
* unreg the fcf.
*/
- if (!(vports[i]->fc_flag & FC_VPORT_CVL_RCVD)) {
- spin_unlock_irq(shost->host_lock);
+ if (!test_bit(FC_VPORT_CVL_RCVD, &vports[i]->fc_flag)) {
ret = 1;
goto out;
}
+ spin_lock_irqsave(&vports[i]->fc_nodes_list_lock, iflags);
list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) {
- if (NLP_CHK_NODE_ACT(ndlp) && ndlp->rport &&
+ if (ndlp->rport &&
(ndlp->rport->roles & FC_RPORT_ROLE_FCP_TARGET)) {
ret = 1;
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vports[i]->fc_nodes_list_lock,
+ iflags);
goto out;
- } else {
- lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
- "2624 RPI %x DID %x flg %x still "
- "logged in\n",
- ndlp->nlp_rpi, ndlp->nlp_DID,
- ndlp->nlp_flag);
- if (ndlp->nlp_flag & NLP_RPI_REGISTERED)
- ret = 1;
+ } else if (test_bit(NLP_RPI_REGISTERED,
+ &ndlp->nlp_flag)) {
+ ret = 1;
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "2624 RPI %x DID %x flag %lx "
+ "still logged in\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag);
}
}
- spin_unlock_irq(shost->host_lock);
+ spin_unlock_irqrestore(&vports[i]->fc_nodes_list_lock, iflags);
}
out:
lpfc_destroy_vport_work_array(phba, vports);
@@ -5392,21 +6711,18 @@ out:
*
* This function frees memory associated with the mailbox command.
*/
-static void
+void
lpfc_unregister_vfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
struct lpfc_vport *vport = mboxq->vport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if (mboxq->u.mb.mbxStatus) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2555 UNREG_VFI mbxStatus error x%x "
- "HBA state x%x\n",
- mboxq->u.mb.mbxStatus, vport->port_state);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2555 UNREG_VFI mbxStatus error x%x "
+ "HBA state x%x\n",
+ mboxq->u.mb.mbxStatus, vport->port_state);
}
- spin_lock_irq(shost->host_lock);
- phba->pport->fc_flag &= ~FC_VFI_REGISTERED;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_VFI_REGISTERED, &phba->pport->fc_flag);
mempool_free(mboxq, phba->mbox_mem_pool);
return;
}
@@ -5424,10 +6740,10 @@ lpfc_unregister_fcfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
struct lpfc_vport *vport = mboxq->vport;
if (mboxq->u.mb.mbxStatus) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2550 UNREG_FCFI mbxStatus error x%x "
- "HBA state x%x\n",
- mboxq->u.mb.mbxStatus, vport->port_state);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2550 UNREG_FCFI mbxStatus error x%x "
+ "HBA state x%x\n",
+ mboxq->u.mb.mbxStatus, vport->port_state);
}
mempool_free(mboxq, phba->mbox_mem_pool);
return;
@@ -5444,11 +6760,10 @@ lpfc_unregister_fcfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
int
lpfc_unregister_fcf_prep(struct lpfc_hba *phba)
{
- LPFC_MBOXQ_t *mbox;
struct lpfc_vport **vports;
struct lpfc_nodelist *ndlp;
struct Scsi_Host *shost;
- int i, rc;
+ int i = 0, rc;
/* Unregister RPIs */
if (lpfc_fcf_inuse(phba))
@@ -5471,44 +6786,32 @@ lpfc_unregister_fcf_prep(struct lpfc_hba *phba)
lpfc_mbx_unreg_vpi(vports[i]);
shost = lpfc_shost_from_vport(vports[i]);
spin_lock_irq(shost->host_lock);
- vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_NEEDS_INIT_VPI, &vports[i]->fc_flag);
}
lpfc_destroy_vport_work_array(phba, vports);
+ if (i == 0 && (!(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED))) {
+ ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
+ if (ndlp)
+ lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);
+ lpfc_cleanup_pending_mbox(phba->pport);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_sli4_unreg_all_rpis(phba->pport);
+ lpfc_mbx_unreg_vpi(phba->pport);
+ shost = lpfc_shost_from_vport(phba->pport);
+ spin_lock_irq(shost->host_lock);
+ phba->pport->vpi_state &= ~LPFC_VPI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_NEEDS_INIT_VPI, &phba->pport->fc_flag);
+ }
/* Cleanup any outstanding ELS commands */
lpfc_els_flush_all_cmd(phba);
- /* Unregister VFI */
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2556 UNREG_VFI mbox allocation failed"
- "HBA state x%x\n", phba->pport->port_state);
- return -ENOMEM;
- }
-
- lpfc_unreg_vfi(mbox, phba->pport);
- mbox->vport = phba->pport;
- mbox->mbox_cmpl = lpfc_unregister_vfi_cmpl;
-
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
- "2557 UNREG_VFI issue mbox failed rc x%x "
- "HBA state x%x\n",
- rc, phba->pport->port_state);
- mempool_free(mbox, phba->mbox_mem_pool);
- return -EIO;
- }
-
- shost = lpfc_shost_from_vport(phba->pport);
- spin_lock_irq(shost->host_lock);
- phba->pport->fc_flag &= ~FC_VFI_REGISTERED;
- spin_unlock_irq(shost->host_lock);
-
- return 0;
+ /* Unregister the physical port VFI */
+ rc = lpfc_issue_unreg_vfi(phba->pport);
+ return rc;
}
/**
@@ -5529,7 +6832,7 @@ lpfc_sli4_unregister_fcf(struct lpfc_hba *phba)
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2551 UNREG_FCFI mbox allocation failed"
"HBA state x%x\n", phba->pport->port_state);
return -ENOMEM;
@@ -5540,7 +6843,7 @@ lpfc_sli4_unregister_fcf(struct lpfc_hba *phba)
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2552 Unregister FCFI command failed rc x%x "
"HBA state x%x\n",
rc, phba->pport->port_state);
@@ -5564,7 +6867,7 @@ lpfc_unregister_fcf_rescan(struct lpfc_hba *phba)
/* Preparation for unregistering fcf */
rc = lpfc_unregister_fcf_prep(phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2748 Failed to prepare for unregistering "
"HBA's FCF record: rc=%d\n", rc);
return;
@@ -5575,15 +6878,17 @@ lpfc_unregister_fcf_rescan(struct lpfc_hba *phba)
if (rc)
return;
/* Reset HBA FCF states after successful unregister FCF */
+ spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag = 0;
+ spin_unlock_irq(&phba->hbalock);
phba->fcf.current_rec.flag = 0;
/*
* If driver is not unloading, check if there is any other
* FCF record that can be used for discovery.
*/
- if ((phba->pport->load_flag & FC_UNLOADING) ||
- (phba->link_state < LPFC_LINK_UP))
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag) ||
+ phba->link_state < LPFC_LINK_UP)
return;
/* This is considered as the initial FCF discovery scan */
@@ -5592,7 +6897,7 @@ lpfc_unregister_fcf_rescan(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
/* Reset FCF roundrobin bmask for new discovery */
- memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask));
+ lpfc_sli4_clear_fcf_rr_bmask(phba);
rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba, LPFC_FCOE_FCF_GET_FIRST);
@@ -5600,7 +6905,7 @@ lpfc_unregister_fcf_rescan(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~FCF_INIT_DISC;
spin_unlock_irq(&phba->hbalock);
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2553 lpfc_unregister_unused_fcf failed "
"to read FCF record HBA state x%x\n",
phba->pport->port_state);
@@ -5622,7 +6927,7 @@ lpfc_unregister_fcf(struct lpfc_hba *phba)
/* Preparation for unregistering fcf */
rc = lpfc_unregister_fcf_prep(phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2749 Failed to prepare for unregistering "
"HBA's FCF record: rc=%d\n", rc);
return;
@@ -5655,11 +6960,11 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
* registered, do nothing.
*/
spin_lock_irq(&phba->hbalock);
- if (!(phba->hba_flag & HBA_FCOE_MODE) ||
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag) ||
!(phba->fcf.fcf_flag & FCF_REGISTERED) ||
- !(phba->hba_flag & HBA_FIP_SUPPORT) ||
+ !test_bit(HBA_FIP_SUPPORT, &phba->hba_flag) ||
(phba->fcf.fcf_flag & FCF_DISCOVERY) ||
- (phba->pport->port_state == LPFC_FLOGI)) {
+ phba->pport->port_state == LPFC_FLOGI) {
spin_unlock_irq(&phba->hbalock);
return;
}
@@ -5709,21 +7014,49 @@ lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba,
conn_entry = kzalloc(sizeof(struct lpfc_fcf_conn_entry),
GFP_KERNEL);
if (!conn_entry) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2566 Failed to allocate connection"
- " table entry\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2566 Failed to allocate connection"
+ " table entry\n");
return;
}
memcpy(&conn_entry->conn_rec, &conn_rec[i],
sizeof(struct lpfc_fcf_conn_rec));
- conn_entry->conn_rec.vlan_tag =
- le16_to_cpu(conn_entry->conn_rec.vlan_tag) & 0xFFF;
- conn_entry->conn_rec.flags =
- le16_to_cpu(conn_entry->conn_rec.flags);
list_add_tail(&conn_entry->list,
&phba->fcf_conn_rec_list);
}
+
+ if (!list_empty(&phba->fcf_conn_rec_list)) {
+ i = 0;
+ list_for_each_entry(conn_entry, &phba->fcf_conn_rec_list,
+ list) {
+ conn_rec = &conn_entry->conn_rec;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3345 FCF connection list rec[%02d]: "
+ "flags:x%04x, vtag:x%04x, "
+ "fabric_name:x%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x, "
+ "switch_name:x%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x\n", i++,
+ conn_rec->flags, conn_rec->vlan_tag,
+ conn_rec->fabric_name[0],
+ conn_rec->fabric_name[1],
+ conn_rec->fabric_name[2],
+ conn_rec->fabric_name[3],
+ conn_rec->fabric_name[4],
+ conn_rec->fabric_name[5],
+ conn_rec->fabric_name[6],
+ conn_rec->fabric_name[7],
+ conn_rec->switch_name[0],
+ conn_rec->switch_name[1],
+ conn_rec->switch_name[2],
+ conn_rec->switch_name[3],
+ conn_rec->switch_name[4],
+ conn_rec->switch_name[5],
+ conn_rec->switch_name[6],
+ conn_rec->switch_name[7]);
+ }
+ }
}
/**
@@ -5815,7 +7148,7 @@ lpfc_parse_fcoe_conf(struct lpfc_hba *phba,
uint8_t *buff,
uint32_t size)
{
- uint32_t offset = 0, rec_length;
+ uint32_t offset = 0;
uint8_t *rec_ptr;
/*
@@ -5827,7 +7160,7 @@ lpfc_parse_fcoe_conf(struct lpfc_hba *phba,
/* Check the region signature first */
if (memcmp(buff, LPFC_REGION23_SIGNATURE, 4)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2567 Config region 23 has bad signature\n");
return;
}
@@ -5836,14 +7169,12 @@ lpfc_parse_fcoe_conf(struct lpfc_hba *phba,
/* Check the data structure version */
if (buff[offset] != LPFC_REGION23_VERSION) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2568 Config region 23 has bad version\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2568 Config region 23 has bad version\n");
return;
}
offset += 4;
- rec_length = buff[offset + 1];
-
/* Read FCoE param record */
rec_ptr = lpfc_get_rec_conf23(&buff[offset],
size - offset, FCOE_PARAM_TYPE);
@@ -5857,3 +7188,38 @@ lpfc_parse_fcoe_conf(struct lpfc_hba *phba,
lpfc_read_fcf_conn_tbl(phba, rec_ptr);
}
+
+/*
+ * lpfc_error_lost_link - IO failure from link event or FW reset check.
+ *
+ * @vport: Pointer to lpfc_vport data structure.
+ * @ulp_status: IO completion status.
+ * @ulp_word4: Reason code for the ulp_status.
+ *
+ * This function evaluates the ulp_status and ulp_word4 values
+ * for specific error values that indicate an internal link fault
+ * or fw reset event for the completing IO. Callers require this
+ * common data to decide next steps on the IO.
+ *
+ * Return:
+ * false - No link or reset error occurred.
+ * true - A link or reset error occurred.
+ */
+bool
+lpfc_error_lost_link(struct lpfc_vport *vport, u32 ulp_status, u32 ulp_word4)
+{
+ /* Mask off the extra port data to get just the reason code. */
+ u32 rsn_code = IOERR_PARAM_MASK & ulp_word4;
+
+ if (ulp_status == IOSTAT_LOCAL_REJECT &&
+ (rsn_code == IOERR_SLI_ABORTED ||
+ rsn_code == IOERR_LINK_DOWN ||
+ rsn_code == IOERR_SLI_DOWN)) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI | LOG_ELS,
+ "0408 Report link error true: <x%x:x%x>\n",
+ ulp_status, ulp_word4);
+ return true;
+ }
+
+ return false;
+}
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 9059524cf225..b2e353590ebb 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2010 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -20,7 +22,7 @@
#define FDMI_DID 0xfffffaU
#define NameServer_DID 0xfffffcU
-#define SCR_DID 0xfffffdU
+#define Fabric_Cntl_DID 0xfffffdU
#define Fabric_DID 0xfffffeU
#define Bcast_DID 0xffffffU
#define Mask_DID 0xffffffU
@@ -33,7 +35,7 @@
#define FF_DEF_EDTOV 2000 /* Default E_D_TOV (2000ms) */
#define FF_DEF_ALTOV 15 /* Default AL_TIME (15ms) */
-#define FF_DEF_RATOV 2 /* Default RA_TOV (2s) */
+#define FF_DEF_RATOV 10 /* Default RA_TOV (10s) */
#define FF_DEF_ARBTOV 1900 /* Default ARB_TOV (1900ms) */
#define LPFC_BUF_RING0 64 /* Number of buffers to post to RING
@@ -44,7 +46,6 @@
#define LPFC_FCP_RING 0 /* ring 0 for FCP initiator commands */
#define LPFC_EXTRA_RING 1 /* ring 1 for other protocols */
#define LPFC_ELS_RING 2 /* ring 2 for ELS commands */
-#define LPFC_FCP_NEXT_RING 3
#define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */
#define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */
@@ -70,6 +71,7 @@
/* vendor ID used in SCSI netlink calls */
#define LPFC_NL_VENDOR_ID (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX)
+#define FW_REV_STR_SIZE 32
/* Common Transport structures and definitions */
union CtRevisionId {
@@ -84,14 +86,28 @@ union CtRevisionId {
union CtCommandResponse {
/* Structure is in Big Endian format */
struct {
- uint32_t CmdRsp:16;
- uint32_t Size:16;
+ __be16 CmdRsp;
+ __be16 Size;
} bits;
uint32_t word;
};
-#define FC4_FEATURE_INIT 0x2
-#define FC4_FEATURE_TARGET 0x1
+/* FC4 Feature bits for RFF_ID */
+#define FC4_FEATURE_TARGET 0x1
+#define FC4_FEATURE_INIT 0x2
+#define FC4_FEATURE_NVME_DISC 0x4
+
+enum rft_word0 {
+ RFT_FCP_REG = (0x1 << 8),
+};
+
+enum rft_word1 {
+ RFT_NVME_REG = (0x1 << 8),
+};
+
+enum rft_word3 {
+ RFT_APP_SERV_REG = (0x1 << 0),
+};
struct lpfc_sli_ct_request {
/* Structure is in Big Endian format */
@@ -105,35 +121,35 @@ struct lpfc_sli_ct_request {
uint8_t ReasonCode;
uint8_t Explanation;
uint8_t VendorUnique;
+#define LPFC_CT_PREAMBLE 20 /* Size of CTReq + 4 up to here */
union {
- uint32_t PortID;
+ __be32 PortID;
struct gid {
uint8_t PortType; /* for GID_PT requests */
+#define GID_PT_N_PORT 1
uint8_t DomainScope;
uint8_t AreaScope;
uint8_t Fc4Type; /* for GID_FT requests */
} gid;
+ struct gid_ff {
+ uint8_t Flags;
+ uint8_t DomainScope;
+ uint8_t AreaScope;
+ uint8_t rsvd1;
+ uint8_t rsvd2;
+ uint8_t rsvd3;
+ uint8_t Fc4FBits;
+ uint8_t Fc4Type;
+ } gid_ff;
struct rft {
- uint32_t PortId; /* For RFT_ID requests */
+ __be32 port_id; /* For RFT_ID requests */
-#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd0:16;
- uint32_t rsvd1:7;
- uint32_t fcpReg:1; /* Type 8 */
- uint32_t rsvd2:2;
- uint32_t ipReg:1; /* Type 5 */
- uint32_t rsvd3:5;
-#else /* __LITTLE_ENDIAN_BITFIELD */
- uint32_t rsvd0:16;
- uint32_t fcpReg:1; /* Type 8 */
- uint32_t rsvd1:7;
- uint32_t rsvd3:5;
- uint32_t ipReg:1; /* Type 5 */
- uint32_t rsvd2:2;
-#endif
-
- uint32_t rsvd[7];
+ __be32 fcp_reg; /* rsvd 31:9, fcp_reg 8, rsvd 7:0 */
+ __be32 nvme_reg; /* rsvd 31:9, nvme_reg 8, rsvd 7:0 */
+ __be32 word2;
+ __be32 app_serv_reg; /* rsvd 31:1, app_serv_reg 0 */
+ __be32 word[4];
} rft;
struct rnn {
uint32_t PortId; /* For RNN_ID requests */
@@ -152,12 +168,23 @@ struct lpfc_sli_ct_request {
uint8_t len;
uint8_t symbname[255];
} rspn;
+ struct rspni { /* For RSPNI_PNI requests */
+ __be64 pni;
+ u8 len;
+ u8 symbname[255];
+ } rspni;
struct gff {
uint32_t PortId;
} gff;
struct gff_acc {
uint8_t fbits[128];
} gff_acc;
+ struct gft {
+ uint32_t PortId;
+ } gft;
+ struct gft_acc {
+ uint32_t fc4_types[8];
+ } gft_acc;
#define FCP_TYPE_FEATURE_OFFSET 7
struct rff {
uint32_t PortId;
@@ -168,11 +195,17 @@ struct lpfc_sli_ct_request {
} un;
};
+#define LPFC_MAX_CT_SIZE (60 * 4096)
+
#define SLI_CT_REVISION 1
#define GID_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
sizeof(struct gid))
+#define GIDFF_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
+ sizeof(struct gid_ff))
#define GFF_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
sizeof(struct gff))
+#define GFT_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
+ sizeof(struct gft))
#define RFT_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
sizeof(struct rft))
#define RFF_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
@@ -185,6 +218,8 @@ struct lpfc_sli_ct_request {
sizeof(struct da_id))
#define RSPN_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
sizeof(struct rspn))
+#define RSPNI_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \
+ sizeof(struct rspni))
/*
* FsType Definitions
@@ -247,6 +282,7 @@ struct lpfc_sli_ct_request {
#define SLI_CT_ACCESS_DENIED 0x10
#define SLI_CT_INVALID_PORT_ID 0x11
#define SLI_CT_DATABASE_EMPTY 0x12
+#define SLI_CT_APP_ID_NOT_AVAILABLE 0x40
/*
* Name Server Command Codes
@@ -268,6 +304,7 @@ struct lpfc_sli_ct_request {
#define SLI_CTNS_GNN_IP 0x0153
#define SLI_CTNS_GIPA_IP 0x0156
#define SLI_CTNS_GID_FT 0x0171
+#define SLI_CTNS_GID_FF 0x01F1
#define SLI_CTNS_GID_PT 0x01A1
#define SLI_CTNS_RPN_ID 0x0212
#define SLI_CTNS_RNN_ID 0x0213
@@ -279,21 +316,23 @@ struct lpfc_sli_ct_request {
#define SLI_CTNS_RIP_NN 0x0235
#define SLI_CTNS_RIPA_NN 0x0236
#define SLI_CTNS_RSNN_NN 0x0239
+#define SLI_CTNS_RSPNI_PNI 0x0240
#define SLI_CTNS_DA_ID 0x0300
/*
* Port Types
*/
-#define SLI_CTPT_N_PORT 0x01
-#define SLI_CTPT_NL_PORT 0x02
-#define SLI_CTPT_FNL_PORT 0x03
-#define SLI_CTPT_IP 0x04
-#define SLI_CTPT_FCP 0x08
-#define SLI_CTPT_NX_PORT 0x7F
-#define SLI_CTPT_F_PORT 0x81
-#define SLI_CTPT_FL_PORT 0x82
-#define SLI_CTPT_E_PORT 0x84
+#define SLI_CTPT_N_PORT 0x01
+#define SLI_CTPT_NL_PORT 0x02
+#define SLI_CTPT_FNL_PORT 0x03
+#define SLI_CTPT_IP 0x04
+#define SLI_CTPT_FCP 0x08
+#define SLI_CTPT_NVME 0x28
+#define SLI_CTPT_NX_PORT 0x7F
+#define SLI_CTPT_F_PORT 0x81
+#define SLI_CTPT_FL_PORT 0x82
+#define SLI_CTPT_E_PORT 0x84
#define SLI_CT_LAST_ENTRY 0x80000000
@@ -334,6 +373,8 @@ struct lpfc_name {
uint8_t IEEE[6]; /* FC IEEE address */
} s;
uint8_t wwn[8];
+ uint64_t name __packed __aligned(4);
+ __be64 wwn_be __packed __aligned(4);
} u;
};
@@ -341,7 +382,7 @@ struct csp {
uint8_t fcphHigh; /* FC Word 0, byte 0 */
uint8_t fcphLow;
uint8_t bbCreditMsb;
- uint8_t bbCreditlsb; /* FC Word 0, byte 3 */
+ uint8_t bbCreditLsb; /* FC Word 0, byte 3 */
/*
* Word 1 Bit 31 in common service parameter is overloaded.
@@ -349,6 +390,18 @@ struct csp {
* Word 1 Bit 31 in FLOGI response is clean address bit
*/
#define clean_address_bit request_multiple_Nport /* Word 1, bit 31 */
+/*
+ * Word 1 Bit 30 in common service parameter is overloaded.
+ * Word 1 Bit 30 in FLOGI request is Virtual Fabrics
+ * Word 1 Bit 30 in PLOGI request is random offset
+ */
+#define virtual_fabric_support randomOffset /* Word 1, bit 30 */
+/*
+ * Word 1 Bit 29 in common service parameter is overloaded.
+ * Word 1 Bit 29 in FLOGI response is multiple NPort assignment
+ * Word 1 Bit 29 in FLOGI/PLOGI request is Valid Vendor Version Level
+ */
+#define valid_vendor_ver_level response_multiple_NPort /* Word 1, bit 29 */
#ifdef __BIG_ENDIAN_BITFIELD
uint16_t request_multiple_Nport:1; /* FC Word 1, bit 31 */
uint16_t randomOffset:1; /* FC Word 1, bit 30 */
@@ -357,16 +410,16 @@ struct csp {
uint16_t altBbCredit:1; /* FC Word 1, bit 27 */
uint16_t edtovResolution:1; /* FC Word 1, bit 26 */
uint16_t multicast:1; /* FC Word 1, bit 25 */
- uint16_t broadcast:1; /* FC Word 1, bit 24 */
+ uint16_t app_hdr_support:1; /* FC Word 1, bit 24 */
- uint16_t huntgroup:1; /* FC Word 1, bit 23 */
+ uint16_t priority_tagging:1; /* FC Word 1, bit 23 */
uint16_t simplex:1; /* FC Word 1, bit 22 */
uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */
uint16_t dhd:1; /* FC Word 1, bit 18 */
uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */
uint16_t payloadlength:1; /* FC Word 1, bit 16 */
#else /* __LITTLE_ENDIAN_BITFIELD */
- uint16_t broadcast:1; /* FC Word 1, bit 24 */
+ uint16_t app_hdr_support:1; /* FC Word 1, bit 24 */
uint16_t multicast:1; /* FC Word 1, bit 25 */
uint16_t edtovResolution:1; /* FC Word 1, bit 26 */
uint16_t altBbCredit:1; /* FC Word 1, bit 27 */
@@ -380,7 +433,7 @@ struct csp {
uint16_t dhd:1; /* FC Word 1, bit 18 */
uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */
uint16_t simplex:1; /* FC Word 1, bit 22 */
- uint16_t huntgroup:1; /* FC Word 1, bit 23 */
+ uint16_t priority_tagging:1; /* FC Word 1, bit 23 */
#endif
uint8_t bbRcvSizeMsb; /* Upper nibble is reserved */
@@ -467,6 +520,21 @@ struct class_parms {
uint8_t word3Reserved2; /* Fc Word 3, bit 0: 7 */
};
+enum aux_parm_flags {
+ AUX_PARM_PNI_VALID = 0x20, /* FC Word 0, bit 29 */
+ AUX_PARM_DATA_VALID = 0x40, /* FC Word 0, bit 30 */
+};
+
+struct aux_parm {
+ u8 flags; /* FC Word 0, bit 31:24 */
+ u8 ext_feat[3]; /* FC Word 0, bit 23:0 */
+
+ __be64 pni; /* FC Word 1 and 2, platform name identifier */
+
+ __be16 rsvd; /* FC Word 3, bit 31:16 */
+ __be16 npiv_cnt; /* FC Word 3, bit 15:0 */
+} __packed;
+
struct serv_parm { /* Structure is in Big Endian format */
struct csp cmn;
struct lpfc_name portName;
@@ -474,8 +542,16 @@ struct serv_parm { /* Structure is in Big Endian format */
struct class_parms cls1;
struct class_parms cls2;
struct class_parms cls3;
- struct class_parms cls4;
- uint8_t vendorVersion[16];
+ struct aux_parm aux;
+ union {
+ uint8_t vendorVersion[16];
+ struct {
+ uint32_t vid;
+#define LPFC_VV_EMLX_ID 0x454d4c58 /* EMLX */
+ uint32_t flags;
+#define LPFC_VV_SUPPRESS_RSP 1
+ } vv;
+ } un;
};
/*
@@ -507,6 +583,29 @@ struct fc_vft_header {
#define fc_vft_hdr_hopct_WORD word1
};
+#include <uapi/scsi/fc/fc_els.h>
+
+/*
+ * Application Header
+ */
+struct fc_app_header {
+ uint32_t dst_app_id;
+ uint32_t src_app_id;
+#define LOOPBACK_SRC_APPID 0x4321
+ uint32_t word2;
+ uint32_t word3;
+};
+
+/*
+ * dfctl optional header definition
+ */
+enum lpfc_fc_dfctl {
+ LPFC_FC_NO_DEVICE_HEADER,
+ LPFC_FC_16B_DEVICE_HEADER,
+ LPFC_FC_32B_DEVICE_HEADER,
+ LPFC_FC_64B_DEVICE_HEADER,
+};
+
/*
* Extended Link Service LS_COMMAND codes (Payload Word 0)
*/
@@ -531,7 +630,11 @@ struct fc_vft_header {
#define ELS_CMD_ECHO 0x10000000
#define ELS_CMD_TEST 0x11000000
#define ELS_CMD_RRQ 0x12000000
+#define ELS_CMD_REC 0x13000000
+#define ELS_CMD_RDP 0x18000000
+#define ELS_CMD_RDF 0x19000000
#define ELS_CMD_PRLI 0x20100014
+#define ELS_CMD_NVMEPRLI 0x20140018
#define ELS_CMD_PRLO 0x21100014
#define ELS_CMD_PRLO_ACC 0x02100014
#define ELS_CMD_PDISC 0x50000000
@@ -539,13 +642,18 @@ struct fc_vft_header {
#define ELS_CMD_ADISC 0x52000000
#define ELS_CMD_FARP 0x54000000
#define ELS_CMD_FARPR 0x55000000
-#define ELS_CMD_RPS 0x56000000
#define ELS_CMD_RPL 0x57000000
#define ELS_CMD_FAN 0x60000000
#define ELS_CMD_RSCN 0x61040000
+#define ELS_CMD_RSCN_XMT 0x61040008
#define ELS_CMD_SCR 0x62000000
#define ELS_CMD_RNID 0x78000000
#define ELS_CMD_LIRR 0x7A000000
+#define ELS_CMD_LCB 0x81000000
+#define ELS_CMD_FPIN 0x16000000
+#define ELS_CMD_EDC 0x17000000
+#define ELS_CMD_QFPA 0xB0000000
+#define ELS_CMD_UVEM 0xB1000000
#else /* __LITTLE_ENDIAN_BITFIELD */
#define ELS_CMD_MASK 0xffff
#define ELS_RSP_MASK 0xff
@@ -567,7 +675,11 @@ struct fc_vft_header {
#define ELS_CMD_ECHO 0x10
#define ELS_CMD_TEST 0x11
#define ELS_CMD_RRQ 0x12
+#define ELS_CMD_REC 0x13
+#define ELS_CMD_RDP 0x18
+#define ELS_CMD_RDF 0x19
#define ELS_CMD_PRLI 0x14001020
+#define ELS_CMD_NVMEPRLI 0x18001420
#define ELS_CMD_PRLO 0x14001021
#define ELS_CMD_PRLO_ACC 0x14001002
#define ELS_CMD_PDISC 0x50
@@ -575,13 +687,18 @@ struct fc_vft_header {
#define ELS_CMD_ADISC 0x52
#define ELS_CMD_FARP 0x54
#define ELS_CMD_FARPR 0x55
-#define ELS_CMD_RPS 0x56
#define ELS_CMD_RPL 0x57
#define ELS_CMD_FAN 0x60
#define ELS_CMD_RSCN 0x0461
+#define ELS_CMD_RSCN_XMT 0x08000461
#define ELS_CMD_SCR 0x62
#define ELS_CMD_RNID 0x78
#define ELS_CMD_LIRR 0x7A
+#define ELS_CMD_LCB 0x81
+#define ELS_CMD_FPIN ELS_FPIN
+#define ELS_CMD_EDC ELS_EDC
+#define ELS_CMD_QFPA 0xB0
+#define ELS_CMD_UVEM 0xB1
#endif
/*
@@ -590,6 +707,7 @@ struct fc_vft_header {
struct ls_rjt { /* Structure is in Big Endian format */
union {
+ __be32 ls_rjt_error_be;
uint32_t lsRjtError;
struct {
uint8_t lsRjtRsvd0; /* FC Word 0, bit 24:31 */
@@ -630,6 +748,8 @@ struct ls_rjt { /* Structure is in Big Endian format */
#define LSEXP_OUT_OF_RESOURCE 0x29
#define LSEXP_CANT_GIVE_DATA 0x2A
#define LSEXP_REQ_UNSUPPORTED 0x2C
+#define LSEXP_AUTH_REQ 0x48
+#define LSEXP_NO_RSRC_ASSIGN 0x52
uint8_t vendorUnique; /* FC Word 0, bit 0: 7 */
} b;
} un;
@@ -663,6 +783,7 @@ typedef struct _PRLI { /* Structure is in Big Endian format */
uint8_t prliType; /* FC Parm Word 0, bit 24:31 */
#define PRLI_FCP_TYPE 0x08
+#define PRLI_NVME_TYPE 0x28
uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */
#ifdef __BIG_ENDIAN_BITFIELD
@@ -689,6 +810,8 @@ typedef struct _PRLI { /* Structure is in Big Endian format */
#define PRLI_PREDEF_CONFIG 0x5
#define PRLI_PARTIAL_SUCCESS 0x6
#define PRLI_INVALID_PAGE_CNT 0x7
+#define PRLI_INV_SRV_PARM 0x8
+
uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */
uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */
@@ -851,24 +974,6 @@ typedef struct _RNID { /* Structure is in Big Endian format */
} un;
} RNID;
-typedef struct _RPS { /* Structure is in Big Endian format */
- union {
- uint32_t portNum;
- struct lpfc_name portName;
- } un;
-} RPS;
-
-typedef struct _RPS_RSP { /* Structure is in Big Endian format */
- uint16_t rsvd1;
- uint16_t portStatus;
- uint32_t linkFailureCnt;
- uint32_t lossSyncCnt;
- uint32_t lossSignalCnt;
- uint32_t primSeqErrCnt;
- uint32_t invalidXmitWord;
- uint32_t crcCnt;
-} RPS_RSP;
-
struct RLS { /* Structure is in Big Endian format */
uint32_t rls;
#define rls_rsvd_SHIFT 24
@@ -998,176 +1103,633 @@ typedef struct _ELS_PKT { /* Structure is in Big Endian format */
} ELS_PKT;
/*
- * FDMI
- * HBA MAnagement Operations Command Codes
+ * Link Cable Beacon (LCB) ELS Frame
*/
-#define SLI_MGMT_GRHL 0x100 /* Get registered HBA list */
-#define SLI_MGMT_GHAT 0x101 /* Get HBA attributes */
-#define SLI_MGMT_GRPL 0x102 /* Get registered Port list */
-#define SLI_MGMT_GPAT 0x110 /* Get Port attributes */
-#define SLI_MGMT_RHBA 0x200 /* Register HBA */
-#define SLI_MGMT_RHAT 0x201 /* Register HBA attributes */
-#define SLI_MGMT_RPRT 0x210 /* Register Port */
-#define SLI_MGMT_RPA 0x211 /* Register Port attributes */
-#define SLI_MGMT_DHBA 0x300 /* De-register HBA */
-#define SLI_MGMT_DPRT 0x310 /* De-register Port */
-/*
- * Management Service Subtypes
- */
-#define SLI_CT_FDMI_Subtypes 0x10
+struct fc_lcb_request_frame {
+ uint32_t lcb_command; /* ELS command opcode (0x81) */
+ uint8_t lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */
+#define LPFC_LCB_ON 0x1
+#define LPFC_LCB_OFF 0x2
+ uint8_t reserved[2];
+ uint8_t capability; /* LCB Payload Word 1, bit 0:7 */
+ uint8_t lcb_type; /* LCB Payload Word 2, bit 24:31 */
+#define LPFC_LCB_GREEN 0x1
+#define LPFC_LCB_AMBER 0x2
+ uint8_t lcb_frequency; /* LCB Payload Word 2, bit 16:23 */
+#define LCB_CAPABILITY_DURATION 1
+#define BEACON_VERSION_V1 1
+#define BEACON_VERSION_V0 0
+ uint16_t lcb_duration; /* LCB Payload Word 2, bit 15:0 */
+};
/*
- * HBA Management Service Reject Code
+ * Link Cable Beacon (LCB) ELS Response Frame
*/
-#define REJECT_CODE 0x9 /* Unable to perform command request */
+struct fc_lcb_res_frame {
+ uint32_t lcb_ls_acc; /* Acceptance of LCB request (0x02) */
+ uint8_t lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */
+ uint8_t reserved[2];
+ uint8_t capability; /* LCB Payload Word 1, bit 0:7 */
+ uint8_t lcb_type; /* LCB Payload Word 2, bit 24:31 */
+ uint8_t lcb_frequency; /* LCB Payload Word 2, bit 16:23 */
+ uint16_t lcb_duration; /* LCB Payload Word 2, bit 15:0 */
+};
/*
- * HBA Management Service Reject Reason Code
- * Please refer to the Reason Codes above
+ * Read Diagnostic Parameters (RDP) ELS frame.
*/
+#define SFF_PG0_IDENT_SFP 0x3
-/*
- * HBA Attribute Types
- */
-#define NODE_NAME 0x1
-#define MANUFACTURER 0x2
-#define SERIAL_NUMBER 0x3
-#define MODEL 0x4
-#define MODEL_DESCRIPTION 0x5
-#define HARDWARE_VERSION 0x6
-#define DRIVER_VERSION 0x7
-#define OPTION_ROM_VERSION 0x8
-#define FIRMWARE_VERSION 0x9
-#define OS_NAME_VERSION 0xa
-#define MAX_CT_PAYLOAD_LEN 0xb
+#define SFP_FLAG_PT_OPTICAL 0x0
+#define SFP_FLAG_PT_SWLASER 0x01
+#define SFP_FLAG_PT_LWLASER_LC1310 0x02
+#define SFP_FLAG_PT_LWLASER_LL1550 0x03
+#define SFP_FLAG_PT_MASK 0x0F
+#define SFP_FLAG_PT_SHIFT 0
-/*
- * Port Attrubute Types
- */
-#define SUPPORTED_FC4_TYPES 0x1
-#define SUPPORTED_SPEED 0x2
-#define PORT_SPEED 0x3
-#define MAX_FRAME_SIZE 0x4
-#define OS_DEVICE_NAME 0x5
-#define HOST_NAME 0x6
-
-union AttributesDef {
- /* Structure is in Big Endian format */
- struct {
- uint32_t AttrType:16;
- uint32_t AttrLen:16;
- } bits;
- uint32_t word;
+#define SFP_FLAG_IS_OPTICAL_PORT 0x01
+#define SFP_FLAG_IS_OPTICAL_MASK 0x010
+#define SFP_FLAG_IS_OPTICAL_SHIFT 4
+
+#define SFP_FLAG_IS_DESC_VALID 0x01
+#define SFP_FLAG_IS_DESC_VALID_MASK 0x020
+#define SFP_FLAG_IS_DESC_VALID_SHIFT 5
+
+#define SFP_FLAG_CT_UNKNOWN 0x0
+#define SFP_FLAG_CT_SFP_PLUS 0x01
+#define SFP_FLAG_CT_MASK 0x3C
+#define SFP_FLAG_CT_SHIFT 6
+
+struct fc_rdp_port_name_info {
+ uint8_t wwnn[8];
+ uint8_t wwpn[8];
};
/*
- * HBA Attribute Entry (8 - 260 bytes)
+ * Link Error Status Block Structure (FC-FS-3) for RDP
+ * This similar to RPS ELS
*/
-typedef struct {
- union AttributesDef ad;
- union {
- uint32_t VendorSpecific;
- uint8_t Manufacturer[64];
- uint8_t SerialNumber[64];
- uint8_t Model[256];
- uint8_t ModelDescription[256];
- uint8_t HardwareVersion[256];
- uint8_t DriverVersion[256];
- uint8_t OptionROMVersion[256];
- uint8_t FirmwareVersion[256];
- struct lpfc_name NodeName;
- uint8_t SupportFC4Types[32];
- uint32_t SupportSpeed;
- uint32_t PortSpeed;
- uint32_t MaxFrameSize;
- uint8_t OsDeviceName[256];
- uint8_t OsNameVersion[256];
- uint32_t MaxCTPayloadLen;
- uint8_t HostName[256];
- } un;
-} ATTRIBUTE_ENTRY;
+struct fc_link_status {
+ uint32_t link_failure_cnt;
+ uint32_t loss_of_synch_cnt;
+ uint32_t loss_of_signal_cnt;
+ uint32_t primitive_seq_proto_err;
+ uint32_t invalid_trans_word;
+ uint32_t invalid_crc_cnt;
+
+};
+
+#define RDP_PORT_NAMES_DESC_TAG 0x00010003
+struct fc_rdp_port_name_desc {
+ uint32_t tag; /* 0001 0003h */
+ uint32_t length; /* set to size of payload struct */
+ struct fc_rdp_port_name_info port_names;
+};
+
+
+struct fc_rdp_fec_info {
+ uint32_t CorrectedBlocks;
+ uint32_t UncorrectableBlocks;
+};
+
+#define RDP_FEC_DESC_TAG 0x00010005
+struct fc_fec_rdp_desc {
+ uint32_t tag;
+ uint32_t length;
+ struct fc_rdp_fec_info info;
+};
+
+struct fc_rdp_link_error_status_payload_info {
+ struct fc_link_status link_status; /* 24 bytes */
+ uint32_t port_type; /* bits 31-30 only */
+};
+
+#define RDP_LINK_ERROR_STATUS_DESC_TAG 0x00010002
+struct fc_rdp_link_error_status_desc {
+ uint32_t tag; /* 0001 0002h */
+ uint32_t length; /* set to size of payload struct */
+ struct fc_rdp_link_error_status_payload_info info;
+};
+
+#define VN_PT_PHY_UNKNOWN 0x00
+#define VN_PT_PHY_PF_PORT 0x01
+#define VN_PT_PHY_ETH_MAC 0x10
+#define VN_PT_PHY_SHIFT 30
+
+#define RDP_PS_1GB 0x8000
+#define RDP_PS_2GB 0x4000
+#define RDP_PS_4GB 0x2000
+#define RDP_PS_10GB 0x1000
+#define RDP_PS_8GB 0x0800
+#define RDP_PS_16GB 0x0400
+#define RDP_PS_32GB 0x0200
+#define RDP_PS_64GB 0x0100
+#define RDP_PS_128GB 0x0080
+#define RDP_PS_256GB 0x0040
+
+#define RDP_CAP_USER_CONFIGURED 0x0002
+#define RDP_CAP_UNKNOWN 0x0001
+#define RDP_PS_UNKNOWN 0x0002
+#define RDP_PS_NOT_ESTABLISHED 0x0001
+
+struct fc_rdp_port_speed {
+ uint16_t capabilities;
+ uint16_t speed;
+};
+
+struct fc_rdp_port_speed_info {
+ struct fc_rdp_port_speed port_speed;
+};
+
+#define RDP_PORT_SPEED_DESC_TAG 0x00010001
+struct fc_rdp_port_speed_desc {
+ uint32_t tag; /* 00010001h */
+ uint32_t length; /* set to size of payload struct */
+ struct fc_rdp_port_speed_info info;
+};
+
+#define RDP_NPORT_ID_SIZE 4
+#define RDP_N_PORT_DESC_TAG 0x00000003
+struct fc_rdp_nport_desc {
+ uint32_t tag; /* 0000 0003h, big endian */
+ uint32_t length; /* size of RDP_N_PORT_ID struct */
+ uint32_t nport_id : 12;
+ uint32_t reserved : 8;
+};
+
+
+struct fc_rdp_link_service_info {
+ uint32_t els_req; /* Request payload word 0 value.*/
+};
+
+#define RDP_LINK_SERVICE_DESC_TAG 0x00000001
+struct fc_rdp_link_service_desc {
+ uint32_t tag; /* Descriptor tag 1 */
+ uint32_t length; /* set to size of payload struct. */
+ struct fc_rdp_link_service_info payload;
+ /* must be ELS req Word 0(0x18) */
+};
+
+struct fc_rdp_sfp_info {
+ uint16_t temperature;
+ uint16_t vcc;
+ uint16_t tx_bias;
+ uint16_t tx_power;
+ uint16_t rx_power;
+ uint16_t flags;
+};
+
+#define RDP_SFP_DESC_TAG 0x00010000
+struct fc_rdp_sfp_desc {
+ uint32_t tag;
+ uint32_t length; /* set to size of sfp_info struct */
+ struct fc_rdp_sfp_info sfp_info;
+};
+
+/* Buffer Credit Descriptor */
+struct fc_rdp_bbc_info {
+ uint32_t port_bbc; /* FC_Port buffer-to-buffer credit */
+ uint32_t attached_port_bbc;
+ uint32_t rtt; /* Round trip time */
+};
+#define RDP_BBC_DESC_TAG 0x00010006
+struct fc_rdp_bbc_desc {
+ uint32_t tag;
+ uint32_t length;
+ struct fc_rdp_bbc_info bbc_info;
+};
+
+/* Optical Element Type Transgression Flags */
+#define RDP_OET_LOW_WARNING 0x1
+#define RDP_OET_HIGH_WARNING 0x2
+#define RDP_OET_LOW_ALARM 0x4
+#define RDP_OET_HIGH_ALARM 0x8
+
+#define RDP_OED_TEMPERATURE 0x1
+#define RDP_OED_VOLTAGE 0x2
+#define RDP_OED_TXBIAS 0x3
+#define RDP_OED_TXPOWER 0x4
+#define RDP_OED_RXPOWER 0x5
+
+#define RDP_OED_TYPE_SHIFT 28
+/* Optical Element Data descriptor */
+struct fc_rdp_oed_info {
+ uint16_t hi_alarm;
+ uint16_t lo_alarm;
+ uint16_t hi_warning;
+ uint16_t lo_warning;
+ uint32_t function_flags;
+};
+#define RDP_OED_DESC_TAG 0x00010007
+struct fc_rdp_oed_sfp_desc {
+ uint32_t tag;
+ uint32_t length;
+ struct fc_rdp_oed_info oed_info;
+};
+
+/* Optical Product Data descriptor */
+struct fc_rdp_opd_sfp_info {
+ uint8_t vendor_name[16];
+ uint8_t model_number[16];
+ uint8_t serial_number[16];
+ uint8_t revision[4];
+ uint8_t date[8];
+};
+
+#define RDP_OPD_DESC_TAG 0x00010008
+struct fc_rdp_opd_sfp_desc {
+ uint32_t tag;
+ uint32_t length;
+ struct fc_rdp_opd_sfp_info opd_info;
+};
+
+struct fc_rdp_req_frame {
+ uint32_t rdp_command; /* ELS command opcode (0x18)*/
+ uint32_t rdp_des_length; /* RDP Payload Word 1 */
+ struct fc_rdp_nport_desc nport_id_desc; /* RDP Payload Word 2 - 4 */
+};
+
+
+struct fc_rdp_res_frame {
+ uint32_t reply_sequence; /* FC word0 LS_ACC or LS_RJT */
+ uint32_t length; /* FC Word 1 */
+ struct fc_rdp_link_service_desc link_service_desc; /* Word 2 -4 */
+ struct fc_rdp_sfp_desc sfp_desc; /* Word 5 -9 */
+ struct fc_rdp_port_speed_desc portspeed_desc; /* Word 10 -12 */
+ struct fc_rdp_link_error_status_desc link_error_desc; /* Word 13 -21 */
+ struct fc_rdp_port_name_desc diag_port_names_desc; /* Word 22 -27 */
+ struct fc_rdp_port_name_desc attached_port_names_desc;/* Word 28 -33 */
+ struct fc_fec_rdp_desc fec_desc; /* FC word 34-37*/
+ struct fc_rdp_bbc_desc bbc_desc; /* FC Word 38-42*/
+ struct fc_rdp_oed_sfp_desc oed_temp_desc; /* FC Word 43-47*/
+ struct fc_rdp_oed_sfp_desc oed_voltage_desc; /* FC word 48-52*/
+ struct fc_rdp_oed_sfp_desc oed_txbias_desc; /* FC word 53-57*/
+ struct fc_rdp_oed_sfp_desc oed_txpower_desc; /* FC word 58-62*/
+ struct fc_rdp_oed_sfp_desc oed_rxpower_desc; /* FC word 63-67*/
+ struct fc_rdp_opd_sfp_desc opd_desc; /* FC word 68-84*/
+};
+
+
+/* UVEM */
+
+#define LPFC_UVEM_SIZE 60
+#define LPFC_UVEM_VEM_ID_DESC_SIZE 16
+#define LPFC_UVEM_VE_MAP_DESC_SIZE 20
+
+#define VEM_ID_DESC_TAG 0x0001000A
+struct lpfc_vem_id_desc {
+ uint32_t tag;
+ uint32_t length;
+ uint8_t vem_id[16];
+};
+
+#define LPFC_QFPA_SIZE 4
+
+#define INSTANTIATED_VE_DESC_TAG 0x0001000B
+struct instantiated_ve_desc {
+ uint32_t tag;
+ uint32_t length;
+ uint8_t global_vem_id[16];
+ uint32_t word6;
+#define lpfc_instantiated_local_id_SHIFT 0
+#define lpfc_instantiated_local_id_MASK 0x000000ff
+#define lpfc_instantiated_local_id_WORD word6
+#define lpfc_instantiated_nport_id_SHIFT 8
+#define lpfc_instantiated_nport_id_MASK 0x00ffffff
+#define lpfc_instantiated_nport_id_WORD word6
+};
+
+#define DEINSTANTIATED_VE_DESC_TAG 0x0001000C
+struct deinstantiated_ve_desc {
+ uint32_t tag;
+ uint32_t length;
+ uint8_t global_vem_id[16];
+ uint32_t word6;
+#define lpfc_deinstantiated_nport_id_SHIFT 0
+#define lpfc_deinstantiated_nport_id_MASK 0x000000ff
+#define lpfc_deinstantiated_nport_id_WORD word6
+#define lpfc_deinstantiated_local_id_SHIFT 24
+#define lpfc_deinstantiated_local_id_MASK 0x00ffffff
+#define lpfc_deinstantiated_local_id_WORD word6
+};
+
+/* Query Fabric Priority Allocation Response */
+#define LPFC_PRIORITY_RANGE_DESC_SIZE 12
+
+struct priority_range_desc {
+ uint32_t tag;
+ uint32_t length;
+ uint8_t lo_range;
+ uint8_t hi_range;
+ uint8_t qos_priority;
+ uint8_t local_ve_id;
+};
+
+struct fc_qfpa_res {
+ uint32_t reply_sequence; /* LS_ACC or LS_RJT */
+ uint32_t length; /* FC Word 1 */
+ struct priority_range_desc desc[1];
+};
+
+/* Application Server command code */
+/* VMID */
+
+#define SLI_CT_APP_SEV_Subtypes 0x20 /* Application Server subtype */
+
+#define SLI_CTAS_GAPPIA_ENT 0x0100 /* Get Application Identifier */
+#define SLI_CTAS_GALLAPPIA 0x0101 /* Get All Application Identifier */
+#define SLI_CTAS_GALLAPPIA_ID 0x0102 /* Get All Application Identifier */
+ /* for Nport */
+#define SLI_CTAS_GAPPIA_IDAPP 0x0103 /* Get Application Identifier */
+ /* for Nport */
+#define SLI_CTAS_RAPP_IDENT 0x0200 /* Register Application Identifier */
+#define SLI_CTAS_DAPP_IDENT 0x0300 /* Deregister Application */
+ /* Identifier */
+#define SLI_CTAS_DALLAPP_ID 0x0301 /* Deregister All Application */
+ /* Identifier */
+
+struct entity_id_object {
+ uint8_t entity_id_len;
+ uint8_t entity_id[255]; /* VM UUID */
+};
+
+struct app_id_object {
+ __be32 port_id;
+ __be32 app_id;
+ struct entity_id_object obj;
+};
+
+struct lpfc_vmid_rapp_ident_list {
+ __be32 no_of_objects;
+ struct entity_id_object obj[];
+};
+
+struct lpfc_vmid_dapp_ident_list {
+ __be32 no_of_objects;
+ struct entity_id_object obj[];
+};
+
+#define GALLAPPIA_ID_LAST 0x80
+struct lpfc_vmid_gallapp_ident_list {
+ uint8_t control;
+ uint8_t reserved[3];
+ struct app_id_object app_id;
+};
+
+#define RAPP_IDENT_OFFSET (offsetof(struct lpfc_sli_ct_request, un) + 4)
+#define DAPP_IDENT_OFFSET (offsetof(struct lpfc_sli_ct_request, un) + 4)
+#define GALLAPPIA_ID_SIZE (offsetof(struct lpfc_sli_ct_request, un) + 4)
+#define DALLAPP_ID_SIZE (offsetof(struct lpfc_sli_ct_request, un) + 4)
+
+/******** FDMI ********/
+
+/* lpfc_sli_ct_request defines the CT_IU preamble for FDMI commands */
+#define SLI_CT_FDMI_Subtypes 0x10 /* Management Service Subtype */
+
+/* Definitions for HBA / Port attribute entries */
+
+/* Attribute Entry Structures */
+
+struct lpfc_fdmi_attr_u32 {
+ __be16 type;
+ __be16 len;
+ __be32 value_u32;
+};
+
+struct lpfc_fdmi_attr_wwn {
+ __be16 type;
+ __be16 len;
+
+ /* Keep as u8[8] instead of __be64 to avoid accidental zero padding
+ * by compiler
+ */
+ u8 name[8];
+};
+
+struct lpfc_fdmi_attr_fullwwn {
+ __be16 type;
+ __be16 len;
+
+ /* Keep as u8[8] instead of __be64 to avoid accidental zero padding
+ * by compiler
+ */
+ u8 nname[8];
+ u8 pname[8];
+};
+
+struct lpfc_fdmi_attr_fc4types {
+ __be16 type;
+ __be16 len;
+ u8 value_types[32];
+};
+
+struct lpfc_fdmi_attr_string {
+ __be16 type;
+ __be16 len;
+ char value_string[256];
+};
+
+/* Maximum FDMI attribute length is Type+Len (4 bytes) + 256 byte string */
+#define FDMI_MAX_ATTRLEN sizeof(struct lpfc_fdmi_attr_string)
/*
* HBA Attribute Block
*/
-typedef struct {
- uint32_t EntryCnt; /* Number of HBA attribute entries */
- ATTRIBUTE_ENTRY Entry; /* Variable-length array */
-} ATTRIBUTE_BLOCK;
+struct lpfc_fdmi_attr_block {
+ uint32_t EntryCnt; /* Number of HBA attribute entries */
+ /* Variable Length Attribute Entry TLV's follow */
+};
/*
* Port Entry
*/
-typedef struct {
+struct lpfc_fdmi_port_entry {
struct lpfc_name PortName;
-} PORT_ENTRY;
+};
/*
* HBA Identifier
*/
-typedef struct {
+struct lpfc_fdmi_hba_ident {
struct lpfc_name PortName;
-} HBA_IDENTIFIER;
+};
/*
* Registered Port List Format
*/
-typedef struct {
- uint32_t EntryCnt;
- PORT_ENTRY pe; /* Variable-length array */
-} REG_PORT_LIST;
+struct lpfc_fdmi_reg_port_list {
+ __be32 EntryCnt;
+ struct lpfc_fdmi_port_entry pe;
+};
/*
* Register HBA(RHBA)
*/
-typedef struct {
- HBA_IDENTIFIER hi;
- REG_PORT_LIST rpl; /* variable-length array */
-/* ATTRIBUTE_BLOCK ab; */
-} REG_HBA;
+struct lpfc_fdmi_reg_hba {
+ struct lpfc_fdmi_hba_ident hi;
+ struct lpfc_fdmi_reg_port_list rpl;
+};
+
+/******** MI MIB ********/
+#define SLI_CT_MIB_Subtypes 0x11
/*
* Register HBA Attributes (RHAT)
*/
-typedef struct {
+struct lpfc_fdmi_reg_hbaattr {
struct lpfc_name HBA_PortName;
- ATTRIBUTE_BLOCK ab;
-} REG_HBA_ATTRIBUTE;
+ struct lpfc_fdmi_attr_block ab;
+};
/*
* Register Port Attributes (RPA)
*/
-typedef struct {
+struct lpfc_fdmi_reg_portattr {
struct lpfc_name PortName;
- ATTRIBUTE_BLOCK ab;
-} REG_PORT_ATTRIBUTE;
+ struct lpfc_fdmi_attr_block ab;
+};
/*
- * Get Registered HBA List (GRHL) Accept Payload Format
+ * HBA MAnagement Operations Command Codes
*/
-typedef struct {
- uint32_t HBA__Entry_Cnt; /* Number of Registered HBA Identifiers */
- struct lpfc_name HBA_PortName; /* Variable-length array */
-} GRHL_ACC_PAYLOAD;
+#define SLI_MGMT_GRHL 0x100 /* Get registered HBA list */
+#define SLI_MGMT_GHAT 0x101 /* Get HBA attributes */
+#define SLI_MGMT_GRPL 0x102 /* Get registered Port list */
+#define SLI_MGMT_GPAT 0x110 /* Get Port attributes */
+#define SLI_MGMT_GPAS 0x120 /* Get Port Statistics */
+#define SLI_MGMT_RHBA 0x200 /* Register HBA */
+#define SLI_MGMT_RHAT 0x201 /* Register HBA attributes */
+#define SLI_MGMT_RPRT 0x210 /* Register Port */
+#define SLI_MGMT_RPA 0x211 /* Register Port attributes */
+#define SLI_MGMT_DHBA 0x300 /* De-register HBA */
+#define SLI_MGMT_DHAT 0x301 /* De-register HBA attributes */
+#define SLI_MGMT_DPRT 0x310 /* De-register Port */
+#define SLI_MGMT_DPA 0x311 /* De-register Port attributes */
+
+#define LPFC_FDMI_MAX_RETRY 3 /* Max retries for a FDMI command */
/*
- * Get Registered Port List (GRPL) Accept Payload Format
+ * HBA Attribute Types
*/
-typedef struct {
- uint32_t RPL_Entry_Cnt; /* Number of Registered Port Entries */
- PORT_ENTRY Reg_Port_Entry[1]; /* Variable-length array */
-} GRPL_ACC_PAYLOAD;
+#define RHBA_NODENAME 0x1 /* 8 byte WWNN */
+#define RHBA_MANUFACTURER 0x2 /* 4 to 64 byte ASCII string */
+#define RHBA_SERIAL_NUMBER 0x3 /* 4 to 64 byte ASCII string */
+#define RHBA_MODEL 0x4 /* 4 to 256 byte ASCII string */
+#define RHBA_MODEL_DESCRIPTION 0x5 /* 4 to 256 byte ASCII string */
+#define RHBA_HARDWARE_VERSION 0x6 /* 4 to 256 byte ASCII string */
+#define RHBA_DRIVER_VERSION 0x7 /* 4 to 256 byte ASCII string */
+#define RHBA_OPTION_ROM_VERSION 0x8 /* 4 to 256 byte ASCII string */
+#define RHBA_FIRMWARE_VERSION 0x9 /* 4 to 256 byte ASCII string */
+#define RHBA_OS_NAME_VERSION 0xa /* 4 to 256 byte ASCII string */
+#define RHBA_MAX_CT_PAYLOAD_LEN 0xb /* 32-bit unsigned int */
+#define RHBA_SYM_NODENAME 0xc /* 4 to 256 byte ASCII string */
+#define RHBA_VENDOR_INFO 0xd /* 32-bit unsigned int */
+#define RHBA_NUM_PORTS 0xe /* 32-bit unsigned int */
+#define RHBA_FABRIC_WWNN 0xf /* 8 byte WWNN */
+#define RHBA_BIOS_VERSION 0x10 /* 4 to 256 byte ASCII string */
+#define RHBA_BIOS_STATE 0x11 /* 32-bit unsigned int */
+#define RHBA_VENDOR_ID 0xe0 /* 8 byte ASCII string */
+
+/* Bit mask for all individual HBA attributes */
+#define LPFC_FDMI_HBA_ATTR_wwnn 0x00000001
+#define LPFC_FDMI_HBA_ATTR_manufacturer 0x00000002
+#define LPFC_FDMI_HBA_ATTR_sn 0x00000004
+#define LPFC_FDMI_HBA_ATTR_model 0x00000008
+#define LPFC_FDMI_HBA_ATTR_description 0x00000010
+#define LPFC_FDMI_HBA_ATTR_hdw_ver 0x00000020
+#define LPFC_FDMI_HBA_ATTR_drvr_ver 0x00000040
+#define LPFC_FDMI_HBA_ATTR_rom_ver 0x00000080
+#define LPFC_FDMI_HBA_ATTR_fmw_ver 0x00000100
+#define LPFC_FDMI_HBA_ATTR_os_ver 0x00000200
+#define LPFC_FDMI_HBA_ATTR_ct_len 0x00000400
+#define LPFC_FDMI_HBA_ATTR_symbolic_name 0x00000800
+#define LPFC_FDMI_HBA_ATTR_vendor_info 0x00001000 /* Not used */
+#define LPFC_FDMI_HBA_ATTR_num_ports 0x00002000
+#define LPFC_FDMI_HBA_ATTR_fabric_wwnn 0x00004000
+#define LPFC_FDMI_HBA_ATTR_bios_ver 0x00008000
+#define LPFC_FDMI_HBA_ATTR_bios_state 0x00010000 /* Not used */
+#define LPFC_FDMI_HBA_ATTR_vendor_id 0x00020000
+
+/* Bit mask for FDMI-1 defined HBA attributes */
+#define LPFC_FDMI1_HBA_ATTR 0x000007ff
+
+/* Bit mask for FDMI-2 defined HBA attributes */
+/* Skip vendor_info and bios_state */
+#define LPFC_FDMI2_HBA_ATTR 0x0002efff
/*
- * Get Port Attributes (GPAT) Accept Payload Format
+ * Port Attribute Types
*/
-
-typedef struct {
- ATTRIBUTE_BLOCK pab;
-} GPAT_ACC_PAYLOAD;
-
+#define RPRT_SUPPORTED_FC4_TYPES 0x1 /* 32 byte binary array */
+#define RPRT_SUPPORTED_SPEED 0x2 /* 32-bit unsigned int */
+#define RPRT_PORT_SPEED 0x3 /* 32-bit unsigned int */
+#define RPRT_MAX_FRAME_SIZE 0x4 /* 32-bit unsigned int */
+#define RPRT_OS_DEVICE_NAME 0x5 /* 4 to 256 byte ASCII string */
+#define RPRT_HOST_NAME 0x6 /* 4 to 256 byte ASCII string */
+#define RPRT_NODENAME 0x7 /* 8 byte WWNN */
+#define RPRT_PORTNAME 0x8 /* 8 byte WWPN */
+#define RPRT_SYM_PORTNAME 0x9 /* 4 to 256 byte ASCII string */
+#define RPRT_PORT_TYPE 0xa /* 32-bit unsigned int */
+#define RPRT_SUPPORTED_CLASS 0xb /* 32-bit unsigned int */
+#define RPRT_FABRICNAME 0xc /* 8 byte Fabric WWPN */
+#define RPRT_ACTIVE_FC4_TYPES 0xd /* 32 byte binary array */
+#define RPRT_PORT_STATE 0x101 /* 32-bit unsigned int */
+#define RPRT_DISC_PORT 0x102 /* 32-bit unsigned int */
+#define RPRT_PORT_ID 0x103 /* 32-bit unsigned int */
+#define RPRT_VENDOR_MI 0xf047 /* vendor ascii string */
+#define RPRT_SMART_SERVICE 0xf100 /* 4 to 256 byte ASCII string */
+#define RPRT_SMART_GUID 0xf101 /* 8 byte WWNN + 8 byte WWPN */
+#define RPRT_SMART_VERSION 0xf102 /* 4 to 256 byte ASCII string */
+#define RPRT_SMART_MODEL 0xf103 /* 4 to 256 byte ASCII string */
+#define RPRT_SMART_PORT_INFO 0xf104 /* 32-bit unsigned int */
+#define RPRT_SMART_QOS 0xf105 /* 32-bit unsigned int */
+#define RPRT_SMART_SECURITY 0xf106 /* 32-bit unsigned int */
+
+/* Bit mask for all individual PORT attributes */
+#define LPFC_FDMI_PORT_ATTR_fc4type 0x00000001
+#define LPFC_FDMI_PORT_ATTR_support_speed 0x00000002
+#define LPFC_FDMI_PORT_ATTR_speed 0x00000004
+#define LPFC_FDMI_PORT_ATTR_max_frame 0x00000008
+#define LPFC_FDMI_PORT_ATTR_os_devname 0x00000010
+#define LPFC_FDMI_PORT_ATTR_host_name 0x00000020
+#define LPFC_FDMI_PORT_ATTR_wwnn 0x00000040
+#define LPFC_FDMI_PORT_ATTR_wwpn 0x00000080
+#define LPFC_FDMI_PORT_ATTR_symbolic_name 0x00000100
+#define LPFC_FDMI_PORT_ATTR_port_type 0x00000200
+#define LPFC_FDMI_PORT_ATTR_class 0x00000400
+#define LPFC_FDMI_PORT_ATTR_fabric_wwpn 0x00000800
+#define LPFC_FDMI_PORT_ATTR_port_state 0x00001000
+#define LPFC_FDMI_PORT_ATTR_active_fc4type 0x00002000
+#define LPFC_FDMI_PORT_ATTR_num_disc 0x00004000
+#define LPFC_FDMI_PORT_ATTR_nportid 0x00008000
+#define LPFC_FDMI_SMART_ATTR_service 0x00010000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_guid 0x00020000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_version 0x00040000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_model 0x00080000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_port_info 0x00100000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_qos 0x00200000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_security 0x00400000 /* Vendor specific */
+#define LPFC_FDMI_VENDOR_ATTR_mi 0x00800000 /* Vendor specific */
+
+/* Bit mask for FDMI-1 defined PORT attributes */
+#define LPFC_FDMI1_PORT_ATTR 0x0000003f
+
+/* Bit mask for FDMI-2 defined PORT attributes */
+#define LPFC_FDMI2_PORT_ATTR 0x0000ffff
+
+/* Bit mask for Smart SAN defined PORT attributes */
+#define LPFC_FDMI2_SMART_ATTR 0x007fffff
+
+/* Defines for PORT port state attribute */
+#define LPFC_FDMI_PORTSTATE_UNKNOWN 1
+#define LPFC_FDMI_PORTSTATE_ONLINE 2
+
+/* Defines for PORT port type attribute */
+#define LPFC_FDMI_PORTTYPE_UNKNOWN 0
+#define LPFC_FDMI_PORTTYPE_NPORT 1
+#define LPFC_FDMI_PORTTYPE_NLPORT 2
/*
* Begin HBA configuration parameters.
@@ -1181,8 +1743,8 @@ typedef struct {
*/
/* Number of rings currently used and available. */
-#define MAX_CONFIGURED_RINGS 3
-#define MAX_RINGS 4
+#define MAX_SLI3_CONFIGURED_RINGS 3
+#define MAX_SLI3_RINGS 4
/* IOCB / Mailbox is owned by FireFly */
#define OWN_CHIP 1
@@ -1206,6 +1768,9 @@ typedef struct {
#define PCI_DEVICE_ID_LANCER_FC_VF 0xe208
#define PCI_DEVICE_ID_LANCER_FCOE 0xe260
#define PCI_DEVICE_ID_LANCER_FCOE_VF 0xe268
+#define PCI_DEVICE_ID_LANCER_G6_FC 0xe300
+#define PCI_DEVICE_ID_LANCER_G7_FC 0xf400
+#define PCI_DEVICE_ID_LANCER_G7P_FC 0xf500
#define PCI_DEVICE_ID_SAT_SMB 0xf011
#define PCI_DEVICE_ID_SAT_MID 0xf015
#define PCI_DEVICE_ID_RFLY 0xf095
@@ -1238,12 +1803,35 @@ typedef struct {
#define PCI_DEVICE_ID_HELIOS_SCSP 0xfd11
#define PCI_DEVICE_ID_HELIOS_DCSP 0xfd12
#define PCI_DEVICE_ID_ZEPHYR 0xfe00
-#define PCI_DEVICE_ID_HORNET 0xfe05
#define PCI_DEVICE_ID_ZEPHYR_SCSP 0xfe11
#define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12
#define PCI_VENDOR_ID_SERVERENGINE 0x19a2
#define PCI_DEVICE_ID_TIGERSHARK 0x0704
#define PCI_DEVICE_ID_TOMCAT 0x0714
+#define PCI_DEVICE_ID_SKYHAWK 0x0724
+#define PCI_DEVICE_ID_SKYHAWK_VF 0x072c
+#define PCI_VENDOR_ID_ATTO 0x117c
+#define PCI_DEVICE_ID_CLRY_16XE 0x0064
+#define PCI_DEVICE_ID_CLRY_161E 0x0063
+#define PCI_DEVICE_ID_CLRY_162E 0x0064
+#define PCI_DEVICE_ID_CLRY_164E 0x0065
+#define PCI_DEVICE_ID_CLRY_16XP 0x0094
+#define PCI_DEVICE_ID_CLRY_161P 0x00a0
+#define PCI_DEVICE_ID_CLRY_162P 0x0094
+#define PCI_DEVICE_ID_CLRY_164P 0x00a1
+#define PCI_DEVICE_ID_CLRY_32XE 0x0094
+#define PCI_DEVICE_ID_CLRY_321E 0x00a2
+#define PCI_DEVICE_ID_CLRY_322E 0x00a3
+#define PCI_DEVICE_ID_CLRY_324E 0x00ac
+#define PCI_DEVICE_ID_CLRY_32XP 0x00bb
+#define PCI_DEVICE_ID_CLRY_321P 0x00bc
+#define PCI_DEVICE_ID_CLRY_322P 0x00bd
+#define PCI_DEVICE_ID_CLRY_324P 0x00be
+#define PCI_DEVICE_ID_TLFC_2 0x0064
+#define PCI_DEVICE_ID_TLFC_2XX2 0x4064
+#define PCI_DEVICE_ID_TLFC_3 0x0094
+#define PCI_DEVICE_ID_TLFC_3162 0x40a6
+#define PCI_DEVICE_ID_TLFC_3322 0x40a7
#define JEDEC_ID_ADDRESS 0x0080001c
#define FIREFLY_JEDEC_ID 0x1ACC
@@ -1259,7 +1847,6 @@ typedef struct {
#define ZEPHYR_JEDEC_ID 0x0577
#define VIPER_JEDEC_ID 0x4838
#define SATURN_JEDEC_ID 0x1004
-#define HORNET_JDEC_ID 0x2057706D
#define JEDEC_ID_MASK 0x0FFFF000
#define JEDEC_ID_SHIFT 12
@@ -1451,6 +2038,8 @@ typedef struct { /* FireFly BIU registers */
#define MBX_UNREG_FCFI 0xA2
#define MBX_INIT_VFI 0xA3
#define MBX_INIT_VPI 0xA4
+#define MBX_ACCESS_VDATA 0xA5
+#define MBX_REG_FCFI_MRQ 0xAF
#define MBX_AUTH_PORT 0xF8
#define MBX_SECURITY_MGMT 0xF9
@@ -1586,6 +2175,11 @@ typedef struct { /* FireFly BIU registers */
#define TEMPERATURE_OFFSET 0xB0 /* Slim offset for critical temperature event */
/*
+ * return code Fail
+ */
+#define FAILURE 1
+
+/*
* Begin Structure Definitions for Mailbox Commands
*/
@@ -1655,6 +2249,7 @@ enum lpfc_protgrp_type {
#define BG_OP_IN_CSUM_OUT_CSUM 0x5
#define BG_OP_IN_CRC_OUT_CSUM 0x6
#define BG_OP_IN_CSUM_OUT_CRC 0x7
+#define BG_OP_RAW_MODE 0x8
struct lpfc_pde5 {
uint32_t word0;
@@ -1852,8 +2447,8 @@ typedef struct {
uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */
#endif
-#define FLAGS_LOCAL_LB 0x01 /* link_flags (=1) ENDEC loopback */
#define FLAGS_TOPOLOGY_MODE_LOOP_PT 0x00 /* Attempt loop then pt-pt */
+#define FLAGS_LOCAL_LB 0x01 /* link_flags (=1) ENDEC loopback */
#define FLAGS_TOPOLOGY_MODE_PT_PT 0x02 /* Attempt pt-pt only */
#define FLAGS_TOPOLOGY_MODE_LOOP 0x04 /* Attempt loop only */
#define FLAGS_TOPOLOGY_MODE_PT_LOOP 0x06 /* Attempt pt-pt then loop */
@@ -1872,6 +2467,10 @@ typedef struct {
#define LINK_SPEED_8G 0x8 /* 8 Gigabaud */
#define LINK_SPEED_10G 0x10 /* 10 Gigabaud */
#define LINK_SPEED_16G 0x11 /* 16 Gigabaud */
+#define LINK_SPEED_32G 0x14 /* 32 Gigabaud */
+#define LINK_SPEED_64G 0x17 /* 64 Gigabaud */
+#define LINK_SPEED_128G 0x1A /* 128 Gigabaud */
+#define LINK_SPEED_256G 0x1D /* 256 Gigabaud */
} INIT_LINK_VAR;
@@ -1908,15 +2507,27 @@ typedef struct {
uint32_t rttov;
uint32_t altov;
uint32_t crtov;
- uint32_t citov;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t rsvd4:19;
+ uint32_t cscn:1;
+ uint32_t bbscn:4;
+ uint32_t rsvd3:8;
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t rsvd3:8;
+ uint32_t bbscn:4;
+ uint32_t cscn:1;
+ uint32_t rsvd4:19;
+#endif
+
#ifdef __BIG_ENDIAN_BITFIELD
uint32_t rrq_enable:1;
uint32_t rrq_immed:1;
- uint32_t rsvd4:29;
+ uint32_t rsvd5:29;
uint32_t ack0_enable:1;
#else /* __LITTLE_ENDIAN_BITFIELD */
uint32_t ack0_enable:1;
- uint32_t rsvd4:29;
+ uint32_t rsvd5:29;
uint32_t rrq_immed:1;
uint32_t rrq_enable:1;
#endif
@@ -2043,6 +2654,10 @@ typedef struct {
#define LMT_8Gb 0x080
#define LMT_10Gb 0x100
#define LMT_16Gb 0x200
+#define LMT_32Gb 0x400
+#define LMT_64Gb 0x800
+#define LMT_128Gb 0x1000
+#define LMT_256Gb 0x2000
uint32_t rsvd2;
uint32_t rsvd3;
uint32_t max_xri;
@@ -2128,19 +2743,26 @@ typedef struct {
} READ_SPARM_VAR;
/* Structure for MB Command READ_STATUS (14) */
+enum read_status_word1 {
+ RD_ST_CC = 0x01,
+ RD_ST_XKB = 0x80,
+};
+
+enum read_status_word17 {
+ RD_ST_XMIT_XKB_MASK = 0x3fffff,
+};
+
+enum read_status_word18 {
+ RD_ST_RCV_XKB_MASK = 0x3fffff,
+};
typedef struct {
-#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd1:31;
- uint32_t clrCounters:1;
- uint16_t activeXriCnt;
- uint16_t activeRpiCnt;
-#else /* __LITTLE_ENDIAN_BITFIELD */
- uint32_t clrCounters:1;
- uint32_t rsvd1:31;
- uint16_t activeRpiCnt;
- uint16_t activeXriCnt;
-#endif
+ u8 clear_counters; /* rsvd 7:1, cc 0 */
+ u8 rsvd5;
+ u8 rsvd6;
+ u8 xkb; /* xkb 7, rsvd 6:0 */
+
+ u32 rsvd8;
uint32_t xmitByteCnt;
uint32_t rcvByteCnt;
@@ -2152,6 +2774,14 @@ typedef struct {
uint32_t totalRespExchanges;
uint32_t rcvPbsyCnt;
uint32_t rcvFbsyCnt;
+
+ u32 drop_frame_no_rq;
+ u32 empty_rq;
+ u32 drop_frame_no_xri;
+ u32 empty_xri;
+
+ u32 xmit_xkb; /* rsvd 31:22, xmit_xkb 21:0 */
+ u32 rcv_xkb; /* rsvd 31:22, rcv_xkb 21:0 */
} READ_STATUS_VAR;
/* Structure for MB Command READ_RPI (15) */
@@ -2292,10 +2922,38 @@ typedef struct {
/* Structure for MB Command READ_LINK_STAT (18) */
typedef struct {
- uint32_t rsvd1;
+ uint32_t word0;
+
+#define lpfc_read_link_stat_rec_SHIFT 0
+#define lpfc_read_link_stat_rec_MASK 0x1
+#define lpfc_read_link_stat_rec_WORD word0
+
+#define lpfc_read_link_stat_gec_SHIFT 1
+#define lpfc_read_link_stat_gec_MASK 0x1
+#define lpfc_read_link_stat_gec_WORD word0
+
+#define lpfc_read_link_stat_w02oftow23of_SHIFT 2
+#define lpfc_read_link_stat_w02oftow23of_MASK 0x3FFFFF
+#define lpfc_read_link_stat_w02oftow23of_WORD word0
+
+#define lpfc_read_link_stat_rsvd_SHIFT 24
+#define lpfc_read_link_stat_rsvd_MASK 0x1F
+#define lpfc_read_link_stat_rsvd_WORD word0
+
+#define lpfc_read_link_stat_gec2_SHIFT 29
+#define lpfc_read_link_stat_gec2_MASK 0x1
+#define lpfc_read_link_stat_gec2_WORD word0
+
+#define lpfc_read_link_stat_clrc_SHIFT 30
+#define lpfc_read_link_stat_clrc_MASK 0x1
+#define lpfc_read_link_stat_clrc_WORD word0
+
+#define lpfc_read_link_stat_clof_SHIFT 31
+#define lpfc_read_link_stat_clof_MASK 0x1
+#define lpfc_read_link_stat_clof_WORD word0
+
uint32_t linkFailureCnt;
uint32_t lossSyncCnt;
-
uint32_t lossSignalCnt;
uint32_t primSeqErrCnt;
uint32_t invalidXmitWord;
@@ -2303,6 +2961,19 @@ typedef struct {
uint32_t primSeqTimeout;
uint32_t elasticOverrun;
uint32_t arbTimeout;
+ uint32_t advRecBufCredit;
+ uint32_t curRecBufCredit;
+ uint32_t advTransBufCredit;
+ uint32_t curTransBufCredit;
+ uint32_t recEofCount;
+ uint32_t recEofdtiCount;
+ uint32_t recEofniCount;
+ uint32_t recSofcount;
+ uint32_t rsvd1;
+ uint32_t rsvd2;
+ uint32_t recDrpXriCount;
+ uint32_t fecCorrBlkCount;
+ uint32_t fecUncorrBlkCount;
} READ_LNK_VAR;
/* Structure for MB Command REG_LOGIN (19) */
@@ -2460,6 +3131,7 @@ struct lpfc_mbx_read_top {
#define LPFC_ATT_RESERVED 0x00 /* Reserved - attType */
#define LPFC_ATT_LINK_UP 0x01 /* Link is up */
#define LPFC_ATT_LINK_DOWN 0x02 /* Link is down */
+#define LPFC_ATT_UNEXP_WWPN 0x06 /* Link is down Unexpected WWWPN */
uint32_t word3;
#define lpfc_mbx_read_top_alpa_granted_SHIFT 24
#define lpfc_mbx_read_top_alpa_granted_MASK 0x000000FF
@@ -2475,7 +3147,6 @@ struct lpfc_mbx_read_top {
#define lpfc_mbx_read_top_topology_WORD word3
#define LPFC_TOPOLOGY_PT_PT 0x01 /* Topology is pt-pt / pt-fabric */
#define LPFC_TOPOLOGY_LOOP 0x02 /* Topology is FC-AL */
-#define LPFC_TOPOLOGY_MM 0x05 /* maint mode zephtr to menlo */
/* store the LILP AL_PA position map into */
struct ulp_bde64 lilpBde64;
#define LPFC_ALPA_MAP_SIZE 128
@@ -2524,6 +3195,10 @@ struct lpfc_mbx_read_top {
#define LPFC_LINK_SPEED_8GHZ 0x20
#define LPFC_LINK_SPEED_10GHZ 0x40
#define LPFC_LINK_SPEED_16GHZ 0x80
+#define LPFC_LINK_SPEED_32GHZ 0x90
+#define LPFC_LINK_SPEED_64GHZ 0xA0
+#define LPFC_LINK_SPEED_128GHZ 0xB0
+#define LPFC_LINK_SPEED_256GHZ 0xC0
};
/* Structure for MB Command CLEAR_LA (22) */
@@ -2561,6 +3236,8 @@ typedef struct {
#define DMP_MEM_REG 0x1
#define DMP_NV_PARAMS 0x2
+#define DMP_LMSD 0x3 /* Link Module Serial Data */
+#define DMP_WELL_KNOWN 0x4
#define DMP_REGION_VPD 0xe
#define DMP_VPD_SIZE 0x400 /* maximum amount of VPD */
@@ -2817,9 +3494,9 @@ typedef struct {
#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd1 : 19; /* Reserved */
- uint32_t cdss : 1; /* Configure Data Security SLI */
- uint32_t rsvd2 : 3; /* Reserved */
+ uint32_t rsvd1 : 20; /* Reserved */
+ uint32_t casabt : 1; /* Configure async abts status notice */
+ uint32_t rsvd2 : 2; /* Reserved */
uint32_t cbg : 1; /* Configure BlockGuard */
uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t ccrp : 1; /* Config Command Ring Polling */
@@ -2839,14 +3516,14 @@ typedef struct {
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t cbg : 1; /* Configure BlockGuard */
- uint32_t rsvd2 : 3; /* Reserved */
- uint32_t cdss : 1; /* Configure Data Security SLI */
- uint32_t rsvd1 : 19; /* Reserved */
+ uint32_t rsvd2 : 2; /* Reserved */
+ uint32_t casabt : 1; /* Configure async abts status notice */
+ uint32_t rsvd1 : 20; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd3 : 19; /* Reserved */
- uint32_t gdss : 1; /* Configure Data Security SLI */
- uint32_t rsvd4 : 3; /* Reserved */
+ uint32_t rsvd3 : 20; /* Reserved */
+ uint32_t gasabt : 1; /* Grant async abts status notice */
+ uint32_t rsvd4 : 2; /* Reserved */
uint32_t gbg : 1; /* Grant BlockGuard */
uint32_t gmv : 1; /* Grant Max VPIs */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
@@ -2866,9 +3543,9 @@ typedef struct {
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gmv : 1; /* Grant Max VPIs */
uint32_t gbg : 1; /* Grant BlockGuard */
- uint32_t rsvd4 : 3; /* Reserved */
- uint32_t gdss : 1; /* Configure Data Security SLI */
- uint32_t rsvd3 : 19; /* Reserved */
+ uint32_t rsvd4 : 2; /* Reserved */
+ uint32_t gasabt : 1; /* Grant async abts status notice */
+ uint32_t rsvd3 : 20; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
@@ -2890,15 +3567,11 @@ typedef struct {
uint32_t rsvd6; /* Reserved */
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t fips_rev : 3; /* FIPS Spec Revision */
- uint32_t fips_level : 4; /* FIPS Level */
- uint32_t sec_err : 9; /* security crypto error */
+ uint32_t rsvd7 : 16;
uint32_t max_vpi : 16; /* Max number of virt N-Ports */
#else /* __LITTLE_ENDIAN */
uint32_t max_vpi : 16; /* Max number of virt N-Ports */
- uint32_t sec_err : 9; /* security crypto error */
- uint32_t fips_level : 4; /* FIPS Level */
- uint32_t fips_rev : 3; /* FIPS Spec Revision */
+ uint32_t rsvd7 : 16;
#endif
} CONFIG_PORT_VAR;
@@ -2955,18 +3628,18 @@ typedef struct _SLI2_RDSC {
typedef struct _PCB {
#ifdef __BIG_ENDIAN_BITFIELD
uint32_t type:8;
-#define TYPE_NATIVE_SLI2 0x01;
+#define TYPE_NATIVE_SLI2 0x01
uint32_t feature:8;
-#define FEATURE_INITIAL_SLI2 0x01;
+#define FEATURE_INITIAL_SLI2 0x01
uint32_t rsvd:12;
uint32_t maxRing:4;
#else /* __LITTLE_ENDIAN_BITFIELD */
uint32_t maxRing:4;
uint32_t rsvd:12;
uint32_t feature:8;
-#define FEATURE_INITIAL_SLI2 0x01;
+#define FEATURE_INITIAL_SLI2 0x01
uint32_t type:8;
-#define TYPE_NATIVE_SLI2 0x01;
+#define TYPE_NATIVE_SLI2 0x01
#endif
uint32_t mailBoxSize;
@@ -2978,7 +3651,7 @@ typedef struct _PCB {
uint32_t pgpAddrLow;
uint32_t pgpAddrHigh;
- SLI2_RDSC rdsc[MAX_RINGS];
+ SLI2_RDSC rdsc[MAX_SLI3_RINGS];
} PCB_t;
/* NEW_FEATURE */
@@ -3088,18 +3761,18 @@ struct lpfc_pgp {
struct sli2_desc {
uint32_t unused1[16];
- struct lpfc_hgp host[MAX_RINGS];
- struct lpfc_pgp port[MAX_RINGS];
+ struct lpfc_hgp host[MAX_SLI3_RINGS];
+ struct lpfc_pgp port[MAX_SLI3_RINGS];
};
struct sli3_desc {
- struct lpfc_hgp host[MAX_RINGS];
+ struct lpfc_hgp host[MAX_SLI3_RINGS];
uint32_t reserved[8];
uint32_t hbq_put[16];
};
struct sli3_pgp {
- struct lpfc_pgp port[MAX_RINGS];
+ struct lpfc_pgp port[MAX_SLI3_RINGS];
uint32_t hbq_get[16];
};
@@ -3110,19 +3783,26 @@ union sli_var {
};
typedef struct {
+ struct_group_tagged(MAILBOX_word0, bits,
+ union {
+ struct {
#ifdef __BIG_ENDIAN_BITFIELD
- uint16_t mbxStatus;
- uint8_t mbxCommand;
- uint8_t mbxReserved:6;
- uint8_t mbxHc:1;
- uint8_t mbxOwner:1; /* Low order bit first word */
+ uint16_t mbxStatus;
+ uint8_t mbxCommand;
+ uint8_t mbxReserved:6;
+ uint8_t mbxHc:1;
+ uint8_t mbxOwner:1; /* Low order bit first word */
#else /* __LITTLE_ENDIAN_BITFIELD */
- uint8_t mbxOwner:1; /* Low order bit first word */
- uint8_t mbxHc:1;
- uint8_t mbxReserved:6;
- uint8_t mbxCommand;
- uint16_t mbxStatus;
+ uint8_t mbxOwner:1; /* Low order bit first word */
+ uint8_t mbxHc:1;
+ uint8_t mbxReserved:6;
+ uint8_t mbxCommand;
+ uint16_t mbxStatus;
#endif
+ };
+ u32 word0;
+ };
+ );
MAILVARIANTS un;
union sli_var us;
@@ -3181,7 +3861,7 @@ typedef struct {
#define IOERR_ILLEGAL_COMMAND 0x06
#define IOERR_XCHG_DROPPED 0x07
#define IOERR_ILLEGAL_FIELD 0x08
-#define IOERR_BAD_CONTINUE 0x09
+#define IOERR_RPI_SUSPENDED 0x09
#define IOERR_TOO_MANY_BUFFERS 0x0A
#define IOERR_RCV_BUFFER_WAITING 0x0B
#define IOERR_NO_CONNECTION 0x0C
@@ -3229,6 +3909,7 @@ typedef struct {
#define IOERR_SLI_DOWN 0x101 /* ulpStatus - Driver defined */
#define IOERR_SLI_BRESET 0x102
#define IOERR_SLI_ABORTED 0x103
+#define IOERR_PARAM_MASK 0x1ff
} PARM_ERR;
typedef union {
@@ -3361,6 +4042,9 @@ typedef struct {
WORD5 w5; /* Header control/status word */
} XMT_SEQ_FIELDS64;
+/* This word is remote ports D_ID for XMIT_ELS_RSP64 */
+#define xmit_els_remoteID xrsqRo
+
/* IOCB Command template for 64 bit RCV_SEQUENCE64 */
typedef struct {
struct ulp_bde64 rcvBde;
@@ -3465,16 +4149,22 @@ typedef struct {
} ASYNCSTAT_FIELDS;
#define ASYNC_TEMP_WARN 0x100
#define ASYNC_TEMP_SAFE 0x101
+#define ASYNC_STATUS_CN 0x102
/* IOCB Command template for CMD_IOCB_RCV_ELS64_CX (0xB7)
or CMD_IOCB_RCV_SEQ64_CX (0xB5) */
struct rcv_sli3 {
- uint32_t word8Rsvd;
#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t ox_id;
+ uint16_t seq_cnt;
+
uint16_t vpi;
uint16_t word9Rsvd;
#else /* __LITTLE_ENDIAN */
+ uint16_t seq_cnt;
+ uint16_t ox_id;
+
uint16_t word9Rsvd;
uint16_t vpi;
#endif
@@ -3793,24 +4483,4 @@ lpfc_is_LC_HBA(unsigned short device)
return 0;
}
-/*
- * Determine if an IOCB failed because of a link event or firmware reset.
- */
-
-static inline int
-lpfc_error_lost_link(IOCB_t *iocbp)
-{
- return (iocbp->ulpStatus == IOSTAT_LOCAL_REJECT &&
- (iocbp->un.ulpWord[4] == IOERR_SLI_ABORTED ||
- iocbp->un.ulpWord[4] == IOERR_LINK_DOWN ||
- iocbp->un.ulpWord[4] == IOERR_SLI_DOWN));
-}
-
-#define MENLO_TRANSPORT_TYPE 0xfe
-#define MENLO_CONTEXT 0
-#define MENLO_PU 3
-#define MENLO_TIMEOUT 30
-#define SETVAR_MLOMNT 0x103107
-#define SETVAR_MLORST 0x103007
-
#define BPL_ALIGN_SZ 8 /* 8 byte alignment for bpl and mbufs */
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 11e26a26b5d1..a7f7ed86d2b0 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2009 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2009-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -18,6 +20,9 @@
* included with this package. *
*******************************************************************/
+#include <uapi/scsi/fc/fc_fs.h>
+#include <uapi/scsi/fc/fc_els.h>
+
/* Macros to deal with bit fields. Each bit field must have 3 #defines
* associated with it (_SHIFT, _MASK, and _WORD).
* EG. For a bit field that is in the 7th bit of the "field4" field of a
@@ -41,6 +46,8 @@
* Or clear that bit field:
* bf_set(example_bit_field, &t1, 0);
*/
+#define bf_get_be32(name, ptr) \
+ ((be32_to_cpu((ptr)->name##_WORD) >> name##_SHIFT) & name##_MASK)
#define bf_get_le32(name, ptr) \
((le32_to_cpu((ptr)->name##_WORD) >> name##_SHIFT) & name##_MASK)
#define bf_get(name, ptr) \
@@ -53,6 +60,14 @@
((ptr)->name##_WORD = ((((value) & name##_MASK) << name##_SHIFT) | \
((ptr)->name##_WORD & ~(name##_MASK << name##_SHIFT))))
+#define get_wqe_reqtag(x) (((x)->wqe.words[9] >> 0) & 0xFFFF)
+#define get_wqe_tmo(x) (((x)->wqe.words[7] >> 24) & 0x00FF)
+
+#define get_job_ulpword(x, y) ((x)->iocb.un.ulpWord[y])
+
+#define set_job_ulpstatus(x, y) bf_set(lpfc_wcqe_c_status, &(x)->wcqe_cmpl, y)
+#define set_job_ulpword4(x, y) ((&(x)->wcqe_cmpl)->parameter = y)
+
struct dma_address {
uint32_t addr_lo;
uint32_t addr_hi;
@@ -80,6 +95,7 @@ struct lpfc_sli_intf {
#define LPFC_SLI_INTF_IF_TYPE_0 0
#define LPFC_SLI_INTF_IF_TYPE_1 1
#define LPFC_SLI_INTF_IF_TYPE_2 2
+#define LPFC_SLI_INTF_IF_TYPE_6 6
#define lpfc_sli_intf_sli_family_SHIFT 8
#define lpfc_sli_intf_sli_family_MASK 0x0000000F
#define lpfc_sli_intf_sli_family_WORD word0
@@ -87,6 +103,9 @@ struct lpfc_sli_intf {
#define LPFC_SLI_INTF_FAMILY_BE3 0x1
#define LPFC_SLI_INTF_FAMILY_LNCR_A0 0xa
#define LPFC_SLI_INTF_FAMILY_LNCR_B0 0xb
+#define LPFC_SLI_INTF_FAMILY_G6 0xc
+#define LPFC_SLI_INTF_FAMILY_G7 0xd
+#define LPFC_SLI_INTF_FAMILY_G7P 0xe
#define lpfc_sli_intf_slirev_SHIFT 4
#define lpfc_sli_intf_slirev_MASK 0x0000000F
#define lpfc_sli_intf_slirev_WORD word0
@@ -104,7 +123,9 @@ struct lpfc_sli_intf {
#define LPFC_SLI4_MB_WORD_COUNT 64
#define LPFC_MAX_MQ_PAGE 8
+#define LPFC_MAX_WQ_PAGE_V0 4
#define LPFC_MAX_WQ_PAGE 8
+#define LPFC_MAX_RQ_PAGE 8
#define LPFC_MAX_CQ_PAGE 4
#define LPFC_MAX_EQ_PAGE 8
@@ -115,6 +136,7 @@ struct lpfc_sli_intf {
/* Define SLI4 Alignment requirements. */
#define LPFC_ALIGN_16_BYTE 16
#define LPFC_ALIGN_64_BYTE 64
+#define SLI4_PAGE_SIZE 4096
/* Define SLI4 specific definitions. */
#define LPFC_MQ_CQE_BYTE_OFFSET 256
@@ -170,15 +192,8 @@ struct lpfc_sli_intf {
#define LPFC_PCI_FUNC3 3
#define LPFC_PCI_FUNC4 4
-/* SLI4 interface type-2 control register offsets */
-#define LPFC_CTL_PORT_SEM_OFFSET 0x400
-#define LPFC_CTL_PORT_STA_OFFSET 0x404
-#define LPFC_CTL_PORT_CTL_OFFSET 0x408
-#define LPFC_CTL_PORT_ER1_OFFSET 0x40C
-#define LPFC_CTL_PORT_ER2_OFFSET 0x410
+/* SLI4 interface type-2 PDEV_CTL register */
#define LPFC_CTL_PDEV_CTL_OFFSET 0x414
-
-/* Some SLI4 interface type-2 PDEV_CTL register bits */
#define LPFC_CTL_PDEV_CTL_DRST 0x00000001
#define LPFC_CTL_PDEV_CTL_FRST 0x00000002
#define LPFC_CTL_PDEV_CTL_DD 0x00000004
@@ -186,21 +201,71 @@ struct lpfc_sli_intf {
#define LPFC_CTL_PDEV_CTL_FRL_ALL 0x00
#define LPFC_CTL_PDEV_CTL_FRL_FC_FCOE 0x10
#define LPFC_CTL_PDEV_CTL_FRL_NIC 0x20
+#define LPFC_CTL_PDEV_CTL_DDL_RAS 0x1000000
#define LPFC_FW_DUMP_REQUEST (LPFC_CTL_PDEV_CTL_DD | LPFC_CTL_PDEV_CTL_FRST)
/* Active interrupt test count */
#define LPFC_ACT_INTR_CNT 4
+/* Algrithmns for scheduling FCP commands to WQs */
+#define LPFC_FCP_SCHED_BY_HDWQ 0
+#define LPFC_FCP_SCHED_BY_CPU 1
+
+/* Algrithmns for NameServer Query after RSCN */
+#define LPFC_NS_QUERY_GID_FT 0
+#define LPFC_NS_QUERY_GID_PT 1
+
/* Delay Multiplier constant */
#define LPFC_DMULT_CONST 651042
-#define LPFC_MIM_IMAX 636
-#define LPFC_FP_DEF_IMAX 10000
-#define LPFC_SP_DEF_IMAX 10000
+#define LPFC_DMULT_MAX 1023
+
+/* Configuration of Interrupts / sec for entire HBA port */
+#define LPFC_MIN_IMAX 5000
+#define LPFC_MAX_IMAX 5000000
+#define LPFC_DEF_IMAX 0
+
+#define LPFC_MAX_AUTO_EQ_DELAY 120
+#define LPFC_EQ_DELAY_STEP 15
+#define LPFC_EQD_ISR_TRIGGER 20000
+/* 1s intervals */
+#define LPFC_EQ_DELAY_MSECS 1000
+
+#define LPFC_MIN_CPU_MAP 0
+#define LPFC_MAX_CPU_MAP 1
+#define LPFC_HBA_CPU_MAP 1
/* PORT_CAPABILITIES constants. */
#define LPFC_MAX_SUPPORTED_PAGES 8
+enum ulp_bde64_word3 {
+ ULP_BDE64_SIZE_MASK = 0xffffff,
+
+ ULP_BDE64_TYPE_SHIFT = 24,
+ ULP_BDE64_TYPE_MASK = (0xff << ULP_BDE64_TYPE_SHIFT),
+
+ /* BDE (Host_resident) */
+ ULP_BDE64_TYPE_BDE_64 = (0x00 << ULP_BDE64_TYPE_SHIFT),
+ /* Immediate Data BDE */
+ ULP_BDE64_TYPE_BDE_IMMED = (0x01 << ULP_BDE64_TYPE_SHIFT),
+ /* BDE (Port-resident) */
+ ULP_BDE64_TYPE_BDE_64P = (0x02 << ULP_BDE64_TYPE_SHIFT),
+ /* Input BDE (Host-resident) */
+ ULP_BDE64_TYPE_BDE_64I = (0x08 << ULP_BDE64_TYPE_SHIFT),
+ /* Input BDE (Port-resident) */
+ ULP_BDE64_TYPE_BDE_64IP = (0x0A << ULP_BDE64_TYPE_SHIFT),
+ /* BLP (Host-resident) */
+ ULP_BDE64_TYPE_BLP_64 = (0x40 << ULP_BDE64_TYPE_SHIFT),
+ /* BLP (Port-resident) */
+ ULP_BDE64_TYPE_BLP_64P = (0x42 << ULP_BDE64_TYPE_SHIFT),
+};
+
+struct ulp_bde64_le {
+ __le32 type_size; /* type 31:24, size 23:0 */
+ __le32 addr_low;
+ __le32 addr_high;
+};
+
struct ulp_bde64 {
union ULP_BDE_TUS {
uint32_t w;
@@ -227,28 +292,30 @@ struct ulp_bde64 {
uint32_t addrHigh;
};
+/* Maximun size of immediate data that can fit into a 128 byte WQE */
+#define LPFC_MAX_BDE_IMM_SIZE 64
+
struct lpfc_sli4_flags {
uint32_t word0;
#define lpfc_idx_rsrc_rdy_SHIFT 0
#define lpfc_idx_rsrc_rdy_MASK 0x00000001
#define lpfc_idx_rsrc_rdy_WORD word0
#define LPFC_IDX_RSRC_RDY 1
-#define lpfc_xri_rsrc_rdy_SHIFT 1
-#define lpfc_xri_rsrc_rdy_MASK 0x00000001
-#define lpfc_xri_rsrc_rdy_WORD word0
-#define LPFC_XRI_RSRC_RDY 1
-#define lpfc_rpi_rsrc_rdy_SHIFT 2
+#define lpfc_rpi_rsrc_rdy_SHIFT 1
#define lpfc_rpi_rsrc_rdy_MASK 0x00000001
#define lpfc_rpi_rsrc_rdy_WORD word0
#define LPFC_RPI_RSRC_RDY 1
-#define lpfc_vpi_rsrc_rdy_SHIFT 3
+#define lpfc_vpi_rsrc_rdy_SHIFT 2
#define lpfc_vpi_rsrc_rdy_MASK 0x00000001
#define lpfc_vpi_rsrc_rdy_WORD word0
#define LPFC_VPI_RSRC_RDY 1
-#define lpfc_vfi_rsrc_rdy_SHIFT 4
+#define lpfc_vfi_rsrc_rdy_SHIFT 3
#define lpfc_vfi_rsrc_rdy_MASK 0x00000001
#define lpfc_vfi_rsrc_rdy_WORD word0
#define LPFC_VFI_RSRC_RDY 1
+#define lpfc_ftr_ashdr_SHIFT 4
+#define lpfc_ftr_ashdr_MASK 0x00000001
+#define lpfc_ftr_ashdr_WORD word0
};
struct sli4_bls_rsp {
@@ -285,7 +352,7 @@ struct sli4_bls_rsp {
struct lpfc_eqe {
uint32_t word0;
#define lpfc_eqe_resource_id_SHIFT 16
-#define lpfc_eqe_resource_id_MASK 0x000000FF
+#define lpfc_eqe_resource_id_MASK 0x0000FFFF
#define lpfc_eqe_resource_id_WORD word0
#define lpfc_eqe_minor_code_SHIFT 4
#define lpfc_eqe_minor_code_MASK 0x00000FFF
@@ -326,6 +393,7 @@ struct lpfc_cqe {
#define CQE_STATUS_CMD_REJECT 0xb
#define CQE_STATUS_FCP_TGT_LENCHECK 0xc
#define CQE_STATUS_NEED_BUFF_ENTRY 0xf
+#define CQE_STATUS_DI_ERROR 0x16
/* Status returned by hardware (valid only if status = CQE_STATUS_SUCCESS). */
#define CQE_HW_STATUS_NO_ERR 0x0
@@ -337,6 +405,14 @@ struct lpfc_cqe {
#define CQE_CODE_RELEASE_WQE 0x2
#define CQE_CODE_RECEIVE 0x4
#define CQE_CODE_XRI_ABORTED 0x5
+#define CQE_CODE_RECEIVE_V1 0x9
+#define CQE_CODE_NVME_ERSP 0xd
+
+/*
+ * Define mask value for xri_aborted and wcqe completed CQE extended status.
+ * Currently, extended status is limited to 9 bits (0x0 -> 0x103) .
+ */
+#define WCQE_PARAM_MASK 0x1FF
/* completion queue entry for wqe completions */
struct lpfc_wcqe_complete {
@@ -350,8 +426,32 @@ struct lpfc_wcqe_complete {
#define lpfc_wcqe_c_hw_status_SHIFT 0
#define lpfc_wcqe_c_hw_status_MASK 0x000000FF
#define lpfc_wcqe_c_hw_status_WORD word0
+#define lpfc_wcqe_c_ersp0_SHIFT 0
+#define lpfc_wcqe_c_ersp0_MASK 0x0000FFFF
+#define lpfc_wcqe_c_ersp0_WORD word0
uint32_t total_data_placed;
+#define lpfc_wcqe_c_cmf_cg_SHIFT 31
+#define lpfc_wcqe_c_cmf_cg_MASK 0x00000001
+#define lpfc_wcqe_c_cmf_cg_WORD total_data_placed
+#define lpfc_wcqe_c_cmf_bw_SHIFT 0
+#define lpfc_wcqe_c_cmf_bw_MASK 0x0FFFFFFF
+#define lpfc_wcqe_c_cmf_bw_WORD total_data_placed
uint32_t parameter;
+#define lpfc_wcqe_c_bg_edir_SHIFT 5
+#define lpfc_wcqe_c_bg_edir_MASK 0x00000001
+#define lpfc_wcqe_c_bg_edir_WORD parameter
+#define lpfc_wcqe_c_bg_tdpv_SHIFT 3
+#define lpfc_wcqe_c_bg_tdpv_MASK 0x00000001
+#define lpfc_wcqe_c_bg_tdpv_WORD parameter
+#define lpfc_wcqe_c_bg_re_SHIFT 2
+#define lpfc_wcqe_c_bg_re_MASK 0x00000001
+#define lpfc_wcqe_c_bg_re_WORD parameter
+#define lpfc_wcqe_c_bg_ae_SHIFT 1
+#define lpfc_wcqe_c_bg_ae_MASK 0x00000001
+#define lpfc_wcqe_c_bg_ae_WORD parameter
+#define lpfc_wcqe_c_bg_ge_SHIFT 0
+#define lpfc_wcqe_c_bg_ge_MASK 0x00000001
+#define lpfc_wcqe_c_bg_ge_WORD parameter
uint32_t word3;
#define lpfc_wcqe_c_valid_SHIFT lpfc_cqe_valid_SHIFT
#define lpfc_wcqe_c_valid_MASK lpfc_cqe_valid_MASK
@@ -363,11 +463,14 @@ struct lpfc_wcqe_complete {
#define lpfc_wcqe_c_pv_MASK 0x00000001
#define lpfc_wcqe_c_pv_WORD word3
#define lpfc_wcqe_c_priority_SHIFT 24
-#define lpfc_wcqe_c_priority_MASK 0x00000007
-#define lpfc_wcqe_c_priority_WORD word3
+#define lpfc_wcqe_c_priority_MASK 0x00000007
+#define lpfc_wcqe_c_priority_WORD word3
#define lpfc_wcqe_c_code_SHIFT lpfc_cqe_code_SHIFT
#define lpfc_wcqe_c_code_MASK lpfc_cqe_code_MASK
#define lpfc_wcqe_c_code_WORD lpfc_cqe_code_WORD
+#define lpfc_wcqe_c_sqhead_SHIFT 0
+#define lpfc_wcqe_c_sqhead_MASK 0x0000FFFF
+#define lpfc_wcqe_c_sqhead_WORD word3
};
/* completion queue entry for wqe release */
@@ -430,9 +533,9 @@ struct sli4_wcqe_xri_aborted {
/* completion queue entry structure for rqe completion */
struct lpfc_rcqe {
uint32_t word0;
-#define lpfc_rcqe_bindex_SHIFT 16
-#define lpfc_rcqe_bindex_MASK 0x0000FFF
-#define lpfc_rcqe_bindex_WORD word0
+#define lpfc_rcqe_iv_SHIFT 31
+#define lpfc_rcqe_iv_MASK 0x00000001
+#define lpfc_rcqe_iv_WORD word0
#define lpfc_rcqe_status_SHIFT 8
#define lpfc_rcqe_status_MASK 0x000000FF
#define lpfc_rcqe_status_WORD word0
@@ -440,7 +543,11 @@ struct lpfc_rcqe {
#define FC_STATUS_RQ_BUF_LEN_EXCEEDED 0x11 /* payload truncated */
#define FC_STATUS_INSUFF_BUF_NEED_BUF 0x12 /* Insufficient buffers */
#define FC_STATUS_INSUFF_BUF_FRM_DISC 0x13 /* Frame Discard */
- uint32_t reserved1;
+#define FC_STATUS_RQ_DMA_FAILURE 0x14 /* DMA failure */
+ uint32_t word1;
+#define lpfc_rcqe_fcf_id_v1_SHIFT 0
+#define lpfc_rcqe_fcf_id_v1_MASK 0x0000003F
+#define lpfc_rcqe_fcf_id_v1_WORD word1
uint32_t word2;
#define lpfc_rcqe_length_SHIFT 16
#define lpfc_rcqe_length_MASK 0x0000FFFF
@@ -451,6 +558,9 @@ struct lpfc_rcqe {
#define lpfc_rcqe_fcf_id_SHIFT 0
#define lpfc_rcqe_fcf_id_MASK 0x0000003F
#define lpfc_rcqe_fcf_id_WORD word2
+#define lpfc_rcqe_rq_id_v1_SHIFT 0
+#define lpfc_rcqe_rq_id_v1_MASK 0x0000FFFF
+#define lpfc_rcqe_rq_id_v1_WORD word2
uint32_t word3;
#define lpfc_rcqe_valid_SHIFT lpfc_cqe_valid_SHIFT
#define lpfc_rcqe_valid_MASK lpfc_cqe_valid_MASK
@@ -506,6 +616,8 @@ struct lpfc_register {
uint32_t word0;
};
+#define LPFC_PORT_SEM_UE_RECOVERABLE 0xE000
+#define LPFC_PORT_SEM_MASK 0xF000
/* The following BAR0 Registers apply to SLI4 if_type 0 UCNAs. */
#define LPFC_UERR_STATUS_HI 0x00A4
#define LPFC_UERR_STATUS_LO 0x00A0
@@ -514,8 +626,9 @@ struct lpfc_register {
/* The following BAR0 register sets are defined for if_type 0 and 2 UCNAs. */
#define LPFC_SLI_INTF 0x0058
+#define LPFC_SLI_ASIC_VER 0x009C
-#define LPFC_SLIPORT_IF2_SMPHR 0x0400
+#define LPFC_CTL_PORT_SEM_OFFSET 0x400
#define lpfc_port_smphr_perr_SHIFT 31
#define lpfc_port_smphr_perr_MASK 0x1
#define lpfc_port_smphr_perr_WORD word0
@@ -575,7 +688,7 @@ struct lpfc_register {
#define LPFC_POST_STAGE_PORT_READY 0xC000
#define LPFC_POST_STAGE_PORT_UE 0xF000
-#define LPFC_SLIPORT_STATUS 0x0404
+#define LPFC_CTL_PORT_STA_OFFSET 0x404
#define lpfc_sliport_status_err_SHIFT 31
#define lpfc_sliport_status_err_MASK 0x1
#define lpfc_sliport_status_err_WORD word0
@@ -585,15 +698,22 @@ struct lpfc_register {
#define lpfc_sliport_status_oti_SHIFT 29
#define lpfc_sliport_status_oti_MASK 0x1
#define lpfc_sliport_status_oti_WORD word0
+#define lpfc_sliport_status_dip_SHIFT 25
+#define lpfc_sliport_status_dip_MASK 0x1
+#define lpfc_sliport_status_dip_WORD word0
#define lpfc_sliport_status_rn_SHIFT 24
#define lpfc_sliport_status_rn_MASK 0x1
#define lpfc_sliport_status_rn_WORD word0
#define lpfc_sliport_status_rdy_SHIFT 23
#define lpfc_sliport_status_rdy_MASK 0x1
#define lpfc_sliport_status_rdy_WORD word0
-#define MAX_IF_TYPE_2_RESETS 1000
+#define lpfc_sliport_status_pldv_SHIFT 0
+#define lpfc_sliport_status_pldv_MASK 0x1
+#define lpfc_sliport_status_pldv_WORD word0
+#define CFG_PLD 0x3C
+#define MAX_IF_TYPE_2_RESETS 6
-#define LPFC_SLIPORT_CNTRL 0x0408
+#define LPFC_CTL_PORT_CTL_OFFSET 0x408
#define lpfc_sliport_ctrl_end_SHIFT 30
#define lpfc_sliport_ctrl_end_MASK 0x1
#define lpfc_sliport_ctrl_end_WORD word0
@@ -604,8 +724,19 @@ struct lpfc_register {
#define lpfc_sliport_ctrl_ip_WORD word0
#define LPFC_SLIPORT_INIT_PORT 1
-#define LPFC_SLIPORT_ERR_1 0x040C
-#define LPFC_SLIPORT_ERR_2 0x0410
+#define LPFC_CTL_PORT_ER1_OFFSET 0x40C
+#define LPFC_CTL_PORT_ER2_OFFSET 0x410
+
+#define LPFC_CTL_PORT_EQ_DELAY_OFFSET 0x418
+#define lpfc_sliport_eqdelay_delay_SHIFT 16
+#define lpfc_sliport_eqdelay_delay_MASK 0xffff
+#define lpfc_sliport_eqdelay_delay_WORD word0
+#define lpfc_sliport_eqdelay_id_SHIFT 0
+#define lpfc_sliport_eqdelay_id_MASK 0xfff
+#define lpfc_sliport_eqdelay_id_WORD word0
+#define LPFC_SEC_TO_USEC 1000000
+#define LPFC_SEC_TO_MSEC 1000
+#define LPFC_MSECS_TO_SECS(msecs) ((msecs) / 1000)
/* The following Registers apply to SLI4 if_type 0 UCNAs. They typically
* reside in BAR 2.
@@ -671,28 +802,60 @@ struct lpfc_register {
* register sets depending on the UCNA Port's reported if_type
* value. For UCNA ports running SLI4 and if_type 0, they reside in
* BAR4. For UCNA ports running SLI4 and if_type 2, they reside in
- * BAR0. The offsets are the same so the driver must account for
- * any base address difference.
+ * BAR0. For FC ports running SLI4 and if_type 6, they reside in
+ * BAR2. The offsets and base address are different, so the driver
+ * has to compute the register addresses accordingly
*/
-#define LPFC_RQ_DOORBELL 0x00A0
-#define lpfc_rq_doorbell_num_posted_SHIFT 16
-#define lpfc_rq_doorbell_num_posted_MASK 0x3FFF
-#define lpfc_rq_doorbell_num_posted_WORD word0
-#define LPFC_RQ_POST_BATCH 8 /* RQEs to post at one time */
-#define lpfc_rq_doorbell_id_SHIFT 0
-#define lpfc_rq_doorbell_id_MASK 0xFFFF
-#define lpfc_rq_doorbell_id_WORD word0
-
-#define LPFC_WQ_DOORBELL 0x0040
-#define lpfc_wq_doorbell_num_posted_SHIFT 24
-#define lpfc_wq_doorbell_num_posted_MASK 0x00FF
-#define lpfc_wq_doorbell_num_posted_WORD word0
-#define lpfc_wq_doorbell_index_SHIFT 16
-#define lpfc_wq_doorbell_index_MASK 0x00FF
-#define lpfc_wq_doorbell_index_WORD word0
-#define lpfc_wq_doorbell_id_SHIFT 0
-#define lpfc_wq_doorbell_id_MASK 0xFFFF
-#define lpfc_wq_doorbell_id_WORD word0
+#define LPFC_ULP0_RQ_DOORBELL 0x00A0
+#define LPFC_ULP1_RQ_DOORBELL 0x00C0
+#define LPFC_IF6_RQ_DOORBELL 0x0080
+#define lpfc_rq_db_list_fm_num_posted_SHIFT 24
+#define lpfc_rq_db_list_fm_num_posted_MASK 0x00FF
+#define lpfc_rq_db_list_fm_num_posted_WORD word0
+#define lpfc_rq_db_list_fm_index_SHIFT 16
+#define lpfc_rq_db_list_fm_index_MASK 0x00FF
+#define lpfc_rq_db_list_fm_index_WORD word0
+#define lpfc_rq_db_list_fm_id_SHIFT 0
+#define lpfc_rq_db_list_fm_id_MASK 0xFFFF
+#define lpfc_rq_db_list_fm_id_WORD word0
+#define lpfc_rq_db_ring_fm_num_posted_SHIFT 16
+#define lpfc_rq_db_ring_fm_num_posted_MASK 0x3FFF
+#define lpfc_rq_db_ring_fm_num_posted_WORD word0
+#define lpfc_rq_db_ring_fm_id_SHIFT 0
+#define lpfc_rq_db_ring_fm_id_MASK 0xFFFF
+#define lpfc_rq_db_ring_fm_id_WORD word0
+
+#define LPFC_ULP0_WQ_DOORBELL 0x0040
+#define LPFC_ULP1_WQ_DOORBELL 0x0060
+#define lpfc_wq_db_list_fm_num_posted_SHIFT 24
+#define lpfc_wq_db_list_fm_num_posted_MASK 0x00FF
+#define lpfc_wq_db_list_fm_num_posted_WORD word0
+#define lpfc_wq_db_list_fm_index_SHIFT 16
+#define lpfc_wq_db_list_fm_index_MASK 0x00FF
+#define lpfc_wq_db_list_fm_index_WORD word0
+#define lpfc_wq_db_list_fm_id_SHIFT 0
+#define lpfc_wq_db_list_fm_id_MASK 0xFFFF
+#define lpfc_wq_db_list_fm_id_WORD word0
+#define lpfc_wq_db_ring_fm_num_posted_SHIFT 16
+#define lpfc_wq_db_ring_fm_num_posted_MASK 0x3FFF
+#define lpfc_wq_db_ring_fm_num_posted_WORD word0
+#define lpfc_wq_db_ring_fm_id_SHIFT 0
+#define lpfc_wq_db_ring_fm_id_MASK 0xFFFF
+#define lpfc_wq_db_ring_fm_id_WORD word0
+
+#define LPFC_IF6_WQ_DOORBELL 0x0040
+#define lpfc_if6_wq_db_list_fm_num_posted_SHIFT 24
+#define lpfc_if6_wq_db_list_fm_num_posted_MASK 0x00FF
+#define lpfc_if6_wq_db_list_fm_num_posted_WORD word0
+#define lpfc_if6_wq_db_list_fm_dpp_SHIFT 23
+#define lpfc_if6_wq_db_list_fm_dpp_MASK 0x0001
+#define lpfc_if6_wq_db_list_fm_dpp_WORD word0
+#define lpfc_if6_wq_db_list_fm_dpp_id_SHIFT 16
+#define lpfc_if6_wq_db_list_fm_dpp_id_MASK 0x001F
+#define lpfc_if6_wq_db_list_fm_dpp_id_WORD word0
+#define lpfc_if6_wq_db_list_fm_id_SHIFT 0
+#define lpfc_if6_wq_db_list_fm_id_MASK 0xFFFF
+#define lpfc_if6_wq_db_list_fm_id_WORD word0
#define LPFC_EQCQ_DOORBELL 0x0120
#define lpfc_eqcq_doorbell_se_SHIFT 31
@@ -714,12 +877,52 @@ struct lpfc_register {
#define lpfc_eqcq_doorbell_eqci_SHIFT 9
#define lpfc_eqcq_doorbell_eqci_MASK 0x0001
#define lpfc_eqcq_doorbell_eqci_WORD word0
-#define lpfc_eqcq_doorbell_cqid_SHIFT 0
-#define lpfc_eqcq_doorbell_cqid_MASK 0x03FF
-#define lpfc_eqcq_doorbell_cqid_WORD word0
-#define lpfc_eqcq_doorbell_eqid_SHIFT 0
-#define lpfc_eqcq_doorbell_eqid_MASK 0x01FF
-#define lpfc_eqcq_doorbell_eqid_WORD word0
+#define lpfc_eqcq_doorbell_cqid_lo_SHIFT 0
+#define lpfc_eqcq_doorbell_cqid_lo_MASK 0x03FF
+#define lpfc_eqcq_doorbell_cqid_lo_WORD word0
+#define lpfc_eqcq_doorbell_cqid_hi_SHIFT 11
+#define lpfc_eqcq_doorbell_cqid_hi_MASK 0x001F
+#define lpfc_eqcq_doorbell_cqid_hi_WORD word0
+#define lpfc_eqcq_doorbell_eqid_lo_SHIFT 0
+#define lpfc_eqcq_doorbell_eqid_lo_MASK 0x01FF
+#define lpfc_eqcq_doorbell_eqid_lo_WORD word0
+#define lpfc_eqcq_doorbell_eqid_hi_SHIFT 11
+#define lpfc_eqcq_doorbell_eqid_hi_MASK 0x001F
+#define lpfc_eqcq_doorbell_eqid_hi_WORD word0
+#define LPFC_CQID_HI_FIELD_SHIFT 10
+#define LPFC_EQID_HI_FIELD_SHIFT 9
+
+#define LPFC_IF6_CQ_DOORBELL 0x00C0
+#define lpfc_if6_cq_doorbell_se_SHIFT 31
+#define lpfc_if6_cq_doorbell_se_MASK 0x0001
+#define lpfc_if6_cq_doorbell_se_WORD word0
+#define LPFC_IF6_CQ_SOLICIT_ENABLE_OFF 0
+#define LPFC_IF6_CQ_SOLICIT_ENABLE_ON 1
+#define lpfc_if6_cq_doorbell_arm_SHIFT 29
+#define lpfc_if6_cq_doorbell_arm_MASK 0x0001
+#define lpfc_if6_cq_doorbell_arm_WORD word0
+#define lpfc_if6_cq_doorbell_num_released_SHIFT 16
+#define lpfc_if6_cq_doorbell_num_released_MASK 0x1FFF
+#define lpfc_if6_cq_doorbell_num_released_WORD word0
+#define lpfc_if6_cq_doorbell_cqid_SHIFT 0
+#define lpfc_if6_cq_doorbell_cqid_MASK 0xFFFF
+#define lpfc_if6_cq_doorbell_cqid_WORD word0
+
+#define LPFC_IF6_EQ_DOORBELL 0x0120
+#define lpfc_if6_eq_doorbell_io_SHIFT 31
+#define lpfc_if6_eq_doorbell_io_MASK 0x0001
+#define lpfc_if6_eq_doorbell_io_WORD word0
+#define LPFC_IF6_EQ_INTR_OVERRIDE_OFF 0
+#define LPFC_IF6_EQ_INTR_OVERRIDE_ON 1
+#define lpfc_if6_eq_doorbell_arm_SHIFT 29
+#define lpfc_if6_eq_doorbell_arm_MASK 0x0001
+#define lpfc_if6_eq_doorbell_arm_WORD word0
+#define lpfc_if6_eq_doorbell_num_released_SHIFT 16
+#define lpfc_if6_eq_doorbell_num_released_MASK 0x1FFF
+#define lpfc_if6_eq_doorbell_num_released_WORD word0
+#define lpfc_if6_eq_doorbell_eqid_SHIFT 0
+#define lpfc_if6_eq_doorbell_eqid_MASK 0x0FFF
+#define lpfc_if6_eq_doorbell_eqid_WORD word0
#define LPFC_BMBX 0x0160
#define lpfc_bmbx_addr_SHIFT 2
@@ -733,6 +936,7 @@ struct lpfc_register {
#define lpfc_bmbx_rdy_WORD word0
#define LPFC_MQ_DOORBELL 0x0140
+#define LPFC_IF6_MQ_DOORBELL 0x0160
#define lpfc_mq_doorbell_num_posted_SHIFT 16
#define lpfc_mq_doorbell_num_posted_MASK 0x3FFF
#define lpfc_mq_doorbell_num_posted_WORD word0
@@ -784,6 +988,8 @@ union lpfc_sli4_cfg_shdr {
#define LPFC_Q_CREATE_VERSION_2 2
#define LPFC_Q_CREATE_VERSION_1 1
#define LPFC_Q_CREATE_VERSION_0 0
+#define LPFC_OPCODE_VERSION_0 0
+#define LPFC_OPCODE_VERSION_1 1
} request;
struct {
uint32_t word6;
@@ -803,6 +1009,12 @@ union lpfc_sli4_cfg_shdr {
#define lpfc_mbox_hdr_add_status_SHIFT 8
#define lpfc_mbox_hdr_add_status_MASK 0x000000FF
#define lpfc_mbox_hdr_add_status_WORD word7
+#define LPFC_ADD_STATUS_INCOMPAT_OBJ 0xA2
+#define lpfc_mbox_hdr_add_status_2_SHIFT 16
+#define lpfc_mbox_hdr_add_status_2_MASK 0x000000FF
+#define lpfc_mbox_hdr_add_status_2_WORD word7
+#define LPFC_ADD_STATUS_2_INCOMPAT_FLASH 0x01
+#define LPFC_ADD_STATUS_2_INCORRECT_ASIC 0x02
uint32_t response_length;
uint32_t actual_response_length;
} response;
@@ -825,7 +1037,9 @@ struct mbox_header {
#define LPFC_EXTENT_VERSION_DEFAULT 0
/* Subsystem Definitions */
+#define LPFC_MBOX_SUBSYSTEM_NA 0x0
#define LPFC_MBOX_SUBSYSTEM_COMMON 0x1
+#define LPFC_MBOX_SUBSYSTEM_LOWLEVEL 0xB
#define LPFC_MBOX_SUBSYSTEM_FCOE 0xC
/* Device Specific Definitions */
@@ -835,25 +1049,46 @@ struct mbox_header {
#define HOST_ENDIAN_HIGH_WORD1 0xFF7856FF
/* Common Opcodes */
-#define LPFC_MBOX_OPCODE_CQ_CREATE 0x0C
-#define LPFC_MBOX_OPCODE_EQ_CREATE 0x0D
-#define LPFC_MBOX_OPCODE_MQ_CREATE 0x15
-#define LPFC_MBOX_OPCODE_GET_CNTL_ATTRIBUTES 0x20
-#define LPFC_MBOX_OPCODE_NOP 0x21
-#define LPFC_MBOX_OPCODE_MQ_DESTROY 0x35
-#define LPFC_MBOX_OPCODE_CQ_DESTROY 0x36
-#define LPFC_MBOX_OPCODE_EQ_DESTROY 0x37
-#define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A
-#define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D
-#define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A
-#define LPFC_MBOX_OPCODE_GET_RSRC_EXTENT_INFO 0x9A
-#define LPFC_MBOX_OPCODE_GET_ALLOC_RSRC_EXTENT 0x9B
-#define LPFC_MBOX_OPCODE_ALLOC_RSRC_EXTENT 0x9C
-#define LPFC_MBOX_OPCODE_DEALLOC_RSRC_EXTENT 0x9D
-#define LPFC_MBOX_OPCODE_GET_FUNCTION_CONFIG 0xA0
-#define LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG 0xA4
-#define LPFC_MBOX_OPCODE_WRITE_OBJECT 0xAC
-#define LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS 0xB5
+#define LPFC_MBOX_OPCODE_NA 0x00
+#define LPFC_MBOX_OPCODE_CQ_CREATE 0x0C
+#define LPFC_MBOX_OPCODE_EQ_CREATE 0x0D
+#define LPFC_MBOX_OPCODE_MQ_CREATE 0x15
+#define LPFC_MBOX_OPCODE_GET_CNTL_ATTRIBUTES 0x20
+#define LPFC_MBOX_OPCODE_NOP 0x21
+#define LPFC_MBOX_OPCODE_MODIFY_EQ_DELAY 0x29
+#define LPFC_MBOX_OPCODE_MQ_DESTROY 0x35
+#define LPFC_MBOX_OPCODE_CQ_DESTROY 0x36
+#define LPFC_MBOX_OPCODE_EQ_DESTROY 0x37
+#define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A
+#define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D
+#define LPFC_MBOX_OPCODE_SET_PHYSICAL_LINK_CONFIG 0x3E
+#define LPFC_MBOX_OPCODE_SET_BOOT_CONFIG 0x43
+#define LPFC_MBOX_OPCODE_SET_BEACON_CONFIG 0x45
+#define LPFC_MBOX_OPCODE_GET_BEACON_CONFIG 0x46
+#define LPFC_MBOX_OPCODE_GET_PORT_NAME 0x4D
+#define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A
+#define LPFC_MBOX_OPCODE_GET_VPD_DATA 0x5B
+#define LPFC_MBOX_OPCODE_SET_HOST_DATA 0x5D
+#define LPFC_MBOX_OPCODE_SEND_ACTIVATION 0x73
+#define LPFC_MBOX_OPCODE_RESET_LICENSES 0x74
+#define LPFC_MBOX_OPCODE_REG_CONGESTION_BUF 0x8E
+#define LPFC_MBOX_OPCODE_GET_RSRC_EXTENT_INFO 0x9A
+#define LPFC_MBOX_OPCODE_GET_ALLOC_RSRC_EXTENT 0x9B
+#define LPFC_MBOX_OPCODE_ALLOC_RSRC_EXTENT 0x9C
+#define LPFC_MBOX_OPCODE_DEALLOC_RSRC_EXTENT 0x9D
+#define LPFC_MBOX_OPCODE_GET_FUNCTION_CONFIG 0xA0
+#define LPFC_MBOX_OPCODE_GET_PROFILE_CAPACITIES 0xA1
+#define LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG 0xA4
+#define LPFC_MBOX_OPCODE_SET_PROFILE_CONFIG 0xA5
+#define LPFC_MBOX_OPCODE_GET_PROFILE_LIST 0xA6
+#define LPFC_MBOX_OPCODE_SET_ACT_PROFILE 0xA8
+#define LPFC_MBOX_OPCODE_GET_FACTORY_PROFILE_CONFIG 0xA9
+#define LPFC_MBOX_OPCODE_READ_OBJECT 0xAB
+#define LPFC_MBOX_OPCODE_WRITE_OBJECT 0xAC
+#define LPFC_MBOX_OPCODE_READ_OBJECT_LIST 0xAD
+#define LPFC_MBOX_OPCODE_DELETE_OBJECT 0xAE
+#define LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS 0xB5
+#define LPFC_MBOX_OPCODE_SET_FEATURES 0xBF
/* FCoE Opcodes */
#define LPFC_MBOX_OPCODE_FCOE_WQ_CREATE 0x01
@@ -867,8 +1102,14 @@ struct mbox_header {
#define LPFC_MBOX_OPCODE_FCOE_DELETE_FCF 0x0A
#define LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE 0x0B
#define LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF 0x10
+#define LPFC_MBOX_OPCODE_FCOE_CQ_CREATE_SET 0x1D
+#define LPFC_MBOX_OPCODE_FCOE_SET_FCLINK_SETTINGS 0x21
#define LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE 0x22
#define LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK 0x23
+#define LPFC_MBOX_OPCODE_FCOE_FC_SET_TRUNK_MODE 0x42
+
+/* Low level Opcodes */
+#define LPFC_MBOX_OPCODE_SET_DIAG_LOG_OPTION 0x37
/* Mailbox command structures */
struct eq_context {
@@ -881,6 +1122,9 @@ struct eq_context {
#define lpfc_eq_context_valid_SHIFT 29
#define lpfc_eq_context_valid_MASK 0x00000001
#define lpfc_eq_context_valid_WORD word0
+#define lpfc_eq_context_autovalid_SHIFT 28
+#define lpfc_eq_context_autovalid_MASK 0x00000001
+#define lpfc_eq_context_autovalid_WORD word0
uint32_t word1;
#define lpfc_eq_context_count_SHIFT 26
#define lpfc_eq_context_count_MASK 0x00000003
@@ -897,6 +1141,13 @@ struct eq_context {
uint32_t reserved3;
};
+struct eq_delay_info {
+ uint32_t eq_id;
+ uint32_t phase;
+ uint32_t delay_multi;
+};
+#define LPFC_MAX_EQ_DELAY_EQID_CNT 8
+
struct sgl_page_pairs {
uint32_t sgl_pg0_addr_lo;
uint32_t sgl_pg0_addr_hi;
@@ -929,6 +1180,12 @@ struct lpfc_mbx_sge {
uint32_t length;
};
+struct lpfc_mbx_host_buf {
+ uint32_t length;
+ uint32_t pa_lo;
+ uint32_t pa_hi;
+};
+
struct lpfc_mbx_nembed_cmd {
struct lpfc_sli4_cfg_mhdr cfg_mhdr;
#define LPFC_SLI4_MBX_SGE_MAX_PAGES 19
@@ -939,6 +1196,31 @@ struct lpfc_mbx_nembed_sge_virt {
void *addr[LPFC_SLI4_MBX_SGE_MAX_PAGES];
};
+#define LPFC_MBX_OBJECT_NAME_LEN_DW 26
+struct lpfc_mbx_read_object { /* Version 0 */
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_rd_object_rlen_SHIFT 0
+#define lpfc_mbx_rd_object_rlen_MASK 0x00FFFFFF
+#define lpfc_mbx_rd_object_rlen_WORD word0
+ uint32_t rd_object_offset;
+ __le32 rd_object_name[LPFC_MBX_OBJECT_NAME_LEN_DW];
+#define LPFC_OBJ_NAME_SZ 104 /* 26 x sizeof(uint32_t) is 104. */
+ uint32_t rd_object_cnt;
+ struct lpfc_mbx_host_buf rd_object_hbuf[4];
+ } request;
+ struct {
+ uint32_t rd_object_actual_rlen;
+ uint32_t word1;
+#define lpfc_mbx_rd_object_eof_SHIFT 31
+#define lpfc_mbx_rd_object_eof_MASK 0x1
+#define lpfc_mbx_rd_object_eof_WORD word1
+ } response;
+ } u;
+};
+
struct lpfc_mbx_eq_create {
struct mbox_header header;
union {
@@ -959,6 +1241,19 @@ struct lpfc_mbx_eq_create {
} u;
};
+struct lpfc_mbx_modify_eq_delay {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t num_eq;
+ struct eq_delay_info eq[LPFC_MAX_EQ_DELAY_EQID_CNT];
+ } request;
+ struct {
+ uint32_t word0;
+ } response;
+ } u;
+};
+
struct lpfc_mbx_eq_destroy {
struct mbox_header header;
union {
@@ -979,6 +1274,45 @@ struct lpfc_mbx_nop {
uint32_t context[2];
};
+
+
+struct lpfc_mbx_set_ras_fwlog {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word4;
+#define lpfc_fwlog_enable_SHIFT 0
+#define lpfc_fwlog_enable_MASK 0x00000001
+#define lpfc_fwlog_enable_WORD word4
+#define lpfc_fwlog_loglvl_SHIFT 8
+#define lpfc_fwlog_loglvl_MASK 0x0000000F
+#define lpfc_fwlog_loglvl_WORD word4
+#define lpfc_fwlog_ra_SHIFT 15
+#define lpfc_fwlog_ra_WORD 0x00000008
+#define lpfc_fwlog_buffcnt_SHIFT 16
+#define lpfc_fwlog_buffcnt_MASK 0x000000FF
+#define lpfc_fwlog_buffcnt_WORD word4
+#define lpfc_fwlog_buffsz_SHIFT 24
+#define lpfc_fwlog_buffsz_MASK 0x000000FF
+#define lpfc_fwlog_buffsz_WORD word4
+ uint32_t word5;
+#define lpfc_fwlog_acqe_SHIFT 0
+#define lpfc_fwlog_acqe_MASK 0x0000FFFF
+#define lpfc_fwlog_acqe_WORD word5
+#define lpfc_fwlog_cqid_SHIFT 16
+#define lpfc_fwlog_cqid_MASK 0x0000FFFF
+#define lpfc_fwlog_cqid_WORD word5
+#define LPFC_MAX_FWLOG_PAGE 16
+ struct dma_address lwpd;
+ struct dma_address buff_fwlog[LPFC_MAX_FWLOG_PAGE];
+ } request;
+ struct {
+ uint32_t word0;
+ } response;
+ } u;
+};
+
+
struct cq_context {
uint32_t word0;
#define lpfc_cq_context_event_SHIFT 31
@@ -993,6 +1327,13 @@ struct cq_context {
#define LPFC_CQ_CNT_256 0x0
#define LPFC_CQ_CNT_512 0x1
#define LPFC_CQ_CNT_1024 0x2
+#define LPFC_CQ_CNT_WORD7 0x3
+#define lpfc_cq_context_cqe_sz_SHIFT 25
+#define lpfc_cq_context_cqe_sz_MASK 0x00000003
+#define lpfc_cq_context_cqe_sz_WORD word0
+#define lpfc_cq_context_autovalid_SHIFT 15
+#define lpfc_cq_context_autovalid_MASK 0x00000001
+#define lpfc_cq_context_autovalid_WORD word0
uint32_t word1;
#define lpfc_cq_eq_id_SHIFT 22 /* Version 0 Only */
#define lpfc_cq_eq_id_MASK 0x000000FF
@@ -1000,7 +1341,7 @@ struct cq_context {
#define lpfc_cq_eq_id_2_SHIFT 0 /* Version 2 Only */
#define lpfc_cq_eq_id_2_MASK 0x0000FFFF
#define lpfc_cq_eq_id_2_WORD word1
- uint32_t reserved0;
+ uint32_t lpfc_cq_context_count; /* Version 2 Only */
uint32_t reserved1;
};
@@ -1027,6 +1368,122 @@ struct lpfc_mbx_cq_create {
} u;
};
+struct lpfc_mbx_cq_create_set {
+ union lpfc_sli4_cfg_shdr cfg_shdr;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_cq_create_set_page_size_SHIFT 16 /* Version 2 Only */
+#define lpfc_mbx_cq_create_set_page_size_MASK 0x000000FF
+#define lpfc_mbx_cq_create_set_page_size_WORD word0
+#define lpfc_mbx_cq_create_set_num_pages_SHIFT 0
+#define lpfc_mbx_cq_create_set_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_num_pages_WORD word0
+ uint32_t word1;
+#define lpfc_mbx_cq_create_set_evt_SHIFT 31
+#define lpfc_mbx_cq_create_set_evt_MASK 0x00000001
+#define lpfc_mbx_cq_create_set_evt_WORD word1
+#define lpfc_mbx_cq_create_set_valid_SHIFT 29
+#define lpfc_mbx_cq_create_set_valid_MASK 0x00000001
+#define lpfc_mbx_cq_create_set_valid_WORD word1
+#define lpfc_mbx_cq_create_set_cqecnt_SHIFT 27
+#define lpfc_mbx_cq_create_set_cqecnt_MASK 0x00000003
+#define lpfc_mbx_cq_create_set_cqecnt_WORD word1
+#define lpfc_mbx_cq_create_set_cqe_size_SHIFT 25
+#define lpfc_mbx_cq_create_set_cqe_size_MASK 0x00000003
+#define lpfc_mbx_cq_create_set_cqe_size_WORD word1
+#define lpfc_mbx_cq_create_set_autovalid_SHIFT 15
+#define lpfc_mbx_cq_create_set_autovalid_MASK 0x0000001
+#define lpfc_mbx_cq_create_set_autovalid_WORD word1
+#define lpfc_mbx_cq_create_set_nodelay_SHIFT 14
+#define lpfc_mbx_cq_create_set_nodelay_MASK 0x00000001
+#define lpfc_mbx_cq_create_set_nodelay_WORD word1
+#define lpfc_mbx_cq_create_set_clswm_SHIFT 12
+#define lpfc_mbx_cq_create_set_clswm_MASK 0x00000003
+#define lpfc_mbx_cq_create_set_clswm_WORD word1
+#define lpfc_mbx_cq_create_set_cqe_cnt_hi_SHIFT 0
+#define lpfc_mbx_cq_create_set_cqe_cnt_hi_MASK 0x0000001F
+#define lpfc_mbx_cq_create_set_cqe_cnt_hi_WORD word1
+ uint32_t word2;
+#define lpfc_mbx_cq_create_set_arm_SHIFT 31
+#define lpfc_mbx_cq_create_set_arm_MASK 0x00000001
+#define lpfc_mbx_cq_create_set_arm_WORD word2
+#define lpfc_mbx_cq_create_set_cqe_cnt_lo_SHIFT 16
+#define lpfc_mbx_cq_create_set_cqe_cnt_lo_MASK 0x00007FFF
+#define lpfc_mbx_cq_create_set_cqe_cnt_lo_WORD word2
+#define lpfc_mbx_cq_create_set_num_cq_SHIFT 0
+#define lpfc_mbx_cq_create_set_num_cq_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_num_cq_WORD word2
+ uint32_t word3;
+#define lpfc_mbx_cq_create_set_eq_id1_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id1_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id1_WORD word3
+#define lpfc_mbx_cq_create_set_eq_id0_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id0_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id0_WORD word3
+ uint32_t word4;
+#define lpfc_mbx_cq_create_set_eq_id3_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id3_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id3_WORD word4
+#define lpfc_mbx_cq_create_set_eq_id2_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id2_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id2_WORD word4
+ uint32_t word5;
+#define lpfc_mbx_cq_create_set_eq_id5_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id5_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id5_WORD word5
+#define lpfc_mbx_cq_create_set_eq_id4_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id4_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id4_WORD word5
+ uint32_t word6;
+#define lpfc_mbx_cq_create_set_eq_id7_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id7_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id7_WORD word6
+#define lpfc_mbx_cq_create_set_eq_id6_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id6_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id6_WORD word6
+ uint32_t word7;
+#define lpfc_mbx_cq_create_set_eq_id9_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id9_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id9_WORD word7
+#define lpfc_mbx_cq_create_set_eq_id8_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id8_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id8_WORD word7
+ uint32_t word8;
+#define lpfc_mbx_cq_create_set_eq_id11_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id11_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id11_WORD word8
+#define lpfc_mbx_cq_create_set_eq_id10_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id10_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id10_WORD word8
+ uint32_t word9;
+#define lpfc_mbx_cq_create_set_eq_id13_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id13_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id13_WORD word9
+#define lpfc_mbx_cq_create_set_eq_id12_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id12_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id12_WORD word9
+ uint32_t word10;
+#define lpfc_mbx_cq_create_set_eq_id15_SHIFT 16
+#define lpfc_mbx_cq_create_set_eq_id15_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id15_WORD word10
+#define lpfc_mbx_cq_create_set_eq_id14_SHIFT 0
+#define lpfc_mbx_cq_create_set_eq_id14_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_eq_id14_WORD word10
+ struct dma_address page[1];
+ } request;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_cq_create_set_num_alloc_SHIFT 16
+#define lpfc_mbx_cq_create_set_num_alloc_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_num_alloc_WORD word0
+#define lpfc_mbx_cq_create_set_base_id_SHIFT 0
+#define lpfc_mbx_cq_create_set_base_id_MASK 0x0000FFFF
+#define lpfc_mbx_cq_create_set_base_id_WORD word0
+ } response;
+ } u;
+};
+
struct lpfc_mbx_cq_destroy {
struct mbox_header header;
union {
@@ -1055,12 +1512,22 @@ struct lpfc_mbx_wq_create {
struct { /* Version 0 Request */
uint32_t word0;
#define lpfc_mbx_wq_create_num_pages_SHIFT 0
-#define lpfc_mbx_wq_create_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_num_pages_MASK 0x000000FF
#define lpfc_mbx_wq_create_num_pages_WORD word0
+#define lpfc_mbx_wq_create_dua_SHIFT 8
+#define lpfc_mbx_wq_create_dua_MASK 0x00000001
+#define lpfc_mbx_wq_create_dua_WORD word0
#define lpfc_mbx_wq_create_cq_id_SHIFT 16
#define lpfc_mbx_wq_create_cq_id_MASK 0x0000FFFF
#define lpfc_mbx_wq_create_cq_id_WORD word0
- struct dma_address page[LPFC_MAX_WQ_PAGE];
+ struct dma_address page[LPFC_MAX_WQ_PAGE_V0];
+ uint32_t word9;
+#define lpfc_mbx_wq_create_bua_SHIFT 0
+#define lpfc_mbx_wq_create_bua_MASK 0x00000001
+#define lpfc_mbx_wq_create_bua_WORD word9
+#define lpfc_mbx_wq_create_ulp_num_SHIFT 8
+#define lpfc_mbx_wq_create_ulp_num_MASK 0x000000FF
+#define lpfc_mbx_wq_create_ulp_num_WORD word9
} request;
struct { /* Version 1 Request */
uint32_t word0; /* Word 0 is the same as in v0 */
@@ -1068,6 +1535,16 @@ struct lpfc_mbx_wq_create {
#define lpfc_mbx_wq_create_page_size_SHIFT 0
#define lpfc_mbx_wq_create_page_size_MASK 0x000000FF
#define lpfc_mbx_wq_create_page_size_WORD word1
+#define LPFC_WQ_PAGE_SIZE_4096 0x1
+#define lpfc_mbx_wq_create_dpp_req_SHIFT 15
+#define lpfc_mbx_wq_create_dpp_req_MASK 0x00000001
+#define lpfc_mbx_wq_create_dpp_req_WORD word1
+#define lpfc_mbx_wq_create_doe_SHIFT 14
+#define lpfc_mbx_wq_create_doe_MASK 0x00000001
+#define lpfc_mbx_wq_create_doe_WORD word1
+#define lpfc_mbx_wq_create_toe_SHIFT 13
+#define lpfc_mbx_wq_create_toe_MASK 0x00000001
+#define lpfc_mbx_wq_create_toe_WORD word1
#define lpfc_mbx_wq_create_wqe_size_SHIFT 8
#define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F
#define lpfc_mbx_wq_create_wqe_size_WORD word1
@@ -1084,7 +1561,40 @@ struct lpfc_mbx_wq_create {
#define lpfc_mbx_wq_create_q_id_SHIFT 0
#define lpfc_mbx_wq_create_q_id_MASK 0x0000FFFF
#define lpfc_mbx_wq_create_q_id_WORD word0
+ uint32_t doorbell_offset;
+ uint32_t word2;
+#define lpfc_mbx_wq_create_bar_set_SHIFT 0
+#define lpfc_mbx_wq_create_bar_set_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_bar_set_WORD word2
+#define WQ_PCI_BAR_0_AND_1 0x00
+#define WQ_PCI_BAR_2_AND_3 0x01
+#define WQ_PCI_BAR_4_AND_5 0x02
+#define lpfc_mbx_wq_create_db_format_SHIFT 16
+#define lpfc_mbx_wq_create_db_format_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_db_format_WORD word2
} response;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_wq_create_dpp_rsp_SHIFT 31
+#define lpfc_mbx_wq_create_dpp_rsp_MASK 0x00000001
+#define lpfc_mbx_wq_create_dpp_rsp_WORD word0
+#define lpfc_mbx_wq_create_v1_q_id_SHIFT 0
+#define lpfc_mbx_wq_create_v1_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_v1_q_id_WORD word0
+ uint32_t word1;
+#define lpfc_mbx_wq_create_v1_bar_set_SHIFT 0
+#define lpfc_mbx_wq_create_v1_bar_set_MASK 0x0000000F
+#define lpfc_mbx_wq_create_v1_bar_set_WORD word1
+ uint32_t doorbell_offset;
+ uint32_t word3;
+#define lpfc_mbx_wq_create_dpp_id_SHIFT 16
+#define lpfc_mbx_wq_create_dpp_id_MASK 0x0000001F
+#define lpfc_mbx_wq_create_dpp_id_WORD word3
+#define lpfc_mbx_wq_create_dpp_bar_SHIFT 0
+#define lpfc_mbx_wq_create_dpp_bar_MASK 0x0000000F
+#define lpfc_mbx_wq_create_dpp_bar_WORD word3
+ uint32_t dpp_offset;
+ } response_1;
} u;
};
@@ -1105,6 +1615,7 @@ struct lpfc_mbx_wq_destroy {
#define LPFC_HDR_BUF_SIZE 128
#define LPFC_DATA_BUF_SIZE 2048
+#define LPFC_NVMET_DATA_BUF_SIZE 128
struct rq_context {
uint32_t word0;
#define lpfc_rq_context_rqe_count_SHIFT 16 /* Version 0 Only */
@@ -1114,10 +1625,10 @@ struct rq_context {
#define LPFC_RQ_RING_SIZE_1024 10 /* 1024 entries */
#define LPFC_RQ_RING_SIZE_2048 11 /* 2048 entries */
#define LPFC_RQ_RING_SIZE_4096 12 /* 4096 entries */
-#define lpfc_rq_context_rqe_count_1_SHIFT 16 /* Version 1 Only */
+#define lpfc_rq_context_rqe_count_1_SHIFT 16 /* Version 1-2 Only */
#define lpfc_rq_context_rqe_count_1_MASK 0x0000FFFF
#define lpfc_rq_context_rqe_count_1_WORD word0
-#define lpfc_rq_context_rqe_size_SHIFT 8 /* Version 1 Only */
+#define lpfc_rq_context_rqe_size_SHIFT 8 /* Version 1-2 Only */
#define lpfc_rq_context_rqe_size_MASK 0x0000000F
#define lpfc_rq_context_rqe_size_WORD word0
#define LPFC_RQE_SIZE_8 2
@@ -1128,14 +1639,24 @@ struct rq_context {
#define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */
#define lpfc_rq_context_page_size_MASK 0x000000FF
#define lpfc_rq_context_page_size_WORD word0
- uint32_t reserved1;
+#define LPFC_RQ_PAGE_SIZE_4096 0x1
+ uint32_t word1;
+#define lpfc_rq_context_data_size_SHIFT 16 /* Version 2 Only */
+#define lpfc_rq_context_data_size_MASK 0x0000FFFF
+#define lpfc_rq_context_data_size_WORD word1
+#define lpfc_rq_context_hdr_size_SHIFT 0 /* Version 2 Only */
+#define lpfc_rq_context_hdr_size_MASK 0x0000FFFF
+#define lpfc_rq_context_hdr_size_WORD word1
uint32_t word2;
#define lpfc_rq_context_cq_id_SHIFT 16
-#define lpfc_rq_context_cq_id_MASK 0x000003FF
+#define lpfc_rq_context_cq_id_MASK 0x0000FFFF
#define lpfc_rq_context_cq_id_WORD word2
#define lpfc_rq_context_buf_size_SHIFT 0
#define lpfc_rq_context_buf_size_MASK 0x0000FFFF
#define lpfc_rq_context_buf_size_WORD word2
+#define lpfc_rq_context_base_cq_SHIFT 0 /* Version 2 Only */
+#define lpfc_rq_context_base_cq_MASK 0x0000FFFF
+#define lpfc_rq_context_base_cq_WORD word2
uint32_t buffer_size; /* Version 1 Only */
};
@@ -1147,14 +1668,86 @@ struct lpfc_mbx_rq_create {
#define lpfc_mbx_rq_create_num_pages_SHIFT 0
#define lpfc_mbx_rq_create_num_pages_MASK 0x0000FFFF
#define lpfc_mbx_rq_create_num_pages_WORD word0
+#define lpfc_mbx_rq_create_dua_SHIFT 16
+#define lpfc_mbx_rq_create_dua_MASK 0x00000001
+#define lpfc_mbx_rq_create_dua_WORD word0
+#define lpfc_mbx_rq_create_bqu_SHIFT 17
+#define lpfc_mbx_rq_create_bqu_MASK 0x00000001
+#define lpfc_mbx_rq_create_bqu_WORD word0
+#define lpfc_mbx_rq_create_ulp_num_SHIFT 24
+#define lpfc_mbx_rq_create_ulp_num_MASK 0x000000FF
+#define lpfc_mbx_rq_create_ulp_num_WORD word0
+ struct rq_context context;
+ struct dma_address page[LPFC_MAX_RQ_PAGE];
+ } request;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_rq_create_q_cnt_v2_SHIFT 16
+#define lpfc_mbx_rq_create_q_cnt_v2_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_q_cnt_v2_WORD word0
+#define lpfc_mbx_rq_create_q_id_SHIFT 0
+#define lpfc_mbx_rq_create_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_q_id_WORD word0
+ uint32_t doorbell_offset;
+ uint32_t word2;
+#define lpfc_mbx_rq_create_bar_set_SHIFT 0
+#define lpfc_mbx_rq_create_bar_set_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_bar_set_WORD word2
+#define lpfc_mbx_rq_create_db_format_SHIFT 16
+#define lpfc_mbx_rq_create_db_format_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_db_format_WORD word2
+ } response;
+ } u;
+};
+
+struct lpfc_mbx_rq_create_v2 {
+ union lpfc_sli4_cfg_shdr cfg_shdr;
+ union {
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_rq_create_num_pages_SHIFT 0
+#define lpfc_mbx_rq_create_num_pages_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_num_pages_WORD word0
+#define lpfc_mbx_rq_create_rq_cnt_SHIFT 16
+#define lpfc_mbx_rq_create_rq_cnt_MASK 0x000000FF
+#define lpfc_mbx_rq_create_rq_cnt_WORD word0
+#define lpfc_mbx_rq_create_dua_SHIFT 16
+#define lpfc_mbx_rq_create_dua_MASK 0x00000001
+#define lpfc_mbx_rq_create_dua_WORD word0
+#define lpfc_mbx_rq_create_bqu_SHIFT 17
+#define lpfc_mbx_rq_create_bqu_MASK 0x00000001
+#define lpfc_mbx_rq_create_bqu_WORD word0
+#define lpfc_mbx_rq_create_ulp_num_SHIFT 24
+#define lpfc_mbx_rq_create_ulp_num_MASK 0x000000FF
+#define lpfc_mbx_rq_create_ulp_num_WORD word0
+#define lpfc_mbx_rq_create_dim_SHIFT 29
+#define lpfc_mbx_rq_create_dim_MASK 0x00000001
+#define lpfc_mbx_rq_create_dim_WORD word0
+#define lpfc_mbx_rq_create_dfd_SHIFT 30
+#define lpfc_mbx_rq_create_dfd_MASK 0x00000001
+#define lpfc_mbx_rq_create_dfd_WORD word0
+#define lpfc_mbx_rq_create_dnb_SHIFT 31
+#define lpfc_mbx_rq_create_dnb_MASK 0x00000001
+#define lpfc_mbx_rq_create_dnb_WORD word0
struct rq_context context;
- struct dma_address page[LPFC_MAX_WQ_PAGE];
+ struct dma_address page[1];
} request;
struct {
uint32_t word0;
-#define lpfc_mbx_rq_create_q_id_SHIFT 0
-#define lpfc_mbx_rq_create_q_id_MASK 0x0000FFFF
-#define lpfc_mbx_rq_create_q_id_WORD word0
+#define lpfc_mbx_rq_create_q_cnt_v2_SHIFT 16
+#define lpfc_mbx_rq_create_q_cnt_v2_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_q_cnt_v2_WORD word0
+#define lpfc_mbx_rq_create_q_id_SHIFT 0
+#define lpfc_mbx_rq_create_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_q_id_WORD word0
+ uint32_t doorbell_offset;
+ uint32_t word2;
+#define lpfc_mbx_rq_create_bar_set_SHIFT 0
+#define lpfc_mbx_rq_create_bar_set_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_bar_set_WORD word2
+#define lpfc_mbx_rq_create_db_format_SHIFT 16
+#define lpfc_mbx_rq_create_db_format_MASK 0x0000FFFF
+#define lpfc_mbx_rq_create_db_format_WORD word2
} response;
} u;
};
@@ -1229,6 +1822,11 @@ struct lpfc_mbx_mq_create_ext {
#define lpfc_mbx_mq_create_ext_async_evt_link_SHIFT LPFC_TRAILER_CODE_LINK
#define lpfc_mbx_mq_create_ext_async_evt_link_MASK 0x00000001
#define lpfc_mbx_mq_create_ext_async_evt_link_WORD async_evt_bmap
+#define LPFC_EVT_CODE_LINK_NO_LINK 0x0
+#define LPFC_EVT_CODE_LINK_10_MBIT 0x1
+#define LPFC_EVT_CODE_LINK_100_MBIT 0x2
+#define LPFC_EVT_CODE_LINK_1_GBIT 0x3
+#define LPFC_EVT_CODE_LINK_10_GBIT 0x4
#define lpfc_mbx_mq_create_ext_async_evt_fip_SHIFT LPFC_TRAILER_CODE_FCOE
#define lpfc_mbx_mq_create_ext_async_evt_fip_MASK 0x00000001
#define lpfc_mbx_mq_create_ext_async_evt_fip_WORD async_evt_bmap
@@ -1238,6 +1836,13 @@ struct lpfc_mbx_mq_create_ext {
#define lpfc_mbx_mq_create_ext_async_evt_fc_SHIFT LPFC_TRAILER_CODE_FC
#define lpfc_mbx_mq_create_ext_async_evt_fc_MASK 0x00000001
#define lpfc_mbx_mq_create_ext_async_evt_fc_WORD async_evt_bmap
+#define LPFC_EVT_CODE_FC_NO_LINK 0x0
+#define LPFC_EVT_CODE_FC_1_GBAUD 0x1
+#define LPFC_EVT_CODE_FC_2_GBAUD 0x2
+#define LPFC_EVT_CODE_FC_4_GBAUD 0x4
+#define LPFC_EVT_CODE_FC_8_GBAUD 0x8
+#define LPFC_EVT_CODE_FC_10_GBAUD 0xA
+#define LPFC_EVT_CODE_FC_16_GBAUD 0x10
#define lpfc_mbx_mq_create_ext_async_evt_sli_SHIFT LPFC_TRAILER_CODE_SLI
#define lpfc_mbx_mq_create_ext_async_evt_sli_MASK 0x00000001
#define lpfc_mbx_mq_create_ext_async_evt_sli_WORD async_evt_bmap
@@ -1300,6 +1905,56 @@ struct lpfc_mbx_get_rsrc_extent_info {
} u;
};
+struct lpfc_mbx_query_fw_config {
+ struct mbox_header header;
+ struct {
+ uint32_t config_number;
+#define LPFC_FC_FCOE 0x00000007
+ uint32_t asic_revision;
+ uint32_t physical_port;
+ uint32_t function_mode;
+#define LPFC_FC_INI_MODE 0x00000040
+#define LPFC_FC_TGT_MODE 0x00000080
+#define LPFC_DUA_MODE 0x00000800
+ uint32_t oper_mode;
+ uint32_t rsvd9[2];
+ uint32_t wqid_base;
+ uint32_t wqid_tot;
+ uint32_t rqid_base;
+ uint32_t rqid_tot;
+ uint32_t rsvd15[19];
+ uint32_t function_capabilities;
+ uint32_t cqid_base;
+ uint32_t cqid_tot;
+ uint32_t eqid_base;
+ uint32_t eqid_tot;
+ uint32_t rsvd39[4];
+ } rsp;
+};
+
+struct lpfc_mbx_set_beacon_config {
+ struct mbox_header header;
+ uint32_t word4;
+#define lpfc_mbx_set_beacon_port_num_SHIFT 0
+#define lpfc_mbx_set_beacon_port_num_MASK 0x0000003F
+#define lpfc_mbx_set_beacon_port_num_WORD word4
+#define lpfc_mbx_set_beacon_port_type_SHIFT 6
+#define lpfc_mbx_set_beacon_port_type_MASK 0x00000003
+#define lpfc_mbx_set_beacon_port_type_WORD word4
+#define lpfc_mbx_set_beacon_state_SHIFT 8
+#define lpfc_mbx_set_beacon_state_MASK 0x000000FF
+#define lpfc_mbx_set_beacon_state_WORD word4
+#define lpfc_mbx_set_beacon_duration_SHIFT 16
+#define lpfc_mbx_set_beacon_duration_MASK 0x000000FF
+#define lpfc_mbx_set_beacon_duration_WORD word4
+
+/* COMMON_SET_BEACON_CONFIG_V1 */
+#define lpfc_mbx_set_beacon_duration_v1_SHIFT 16
+#define lpfc_mbx_set_beacon_duration_v1_MASK 0x0000FFFF
+#define lpfc_mbx_set_beacon_duration_v1_WORD word4
+ uint32_t word5; /* RESERVED */
+};
+
struct lpfc_id_range {
uint32_t word5;
#define lpfc_mbx_rsrc_id_word4_0_SHIFT 0
@@ -1318,6 +1973,11 @@ struct lpfc_mbx_set_link_diag_state {
#define lpfc_mbx_set_diag_state_diag_SHIFT 0
#define lpfc_mbx_set_diag_state_diag_MASK 0x00000001
#define lpfc_mbx_set_diag_state_diag_WORD word0
+#define lpfc_mbx_set_diag_state_diag_bit_valid_SHIFT 2
+#define lpfc_mbx_set_diag_state_diag_bit_valid_MASK 0x00000001
+#define lpfc_mbx_set_diag_state_diag_bit_valid_WORD word0
+#define LPFC_DIAG_STATE_DIAG_BIT_VALID_NO_CHANGE 0
+#define LPFC_DIAG_STATE_DIAG_BIT_VALID_CHANGE 1
#define lpfc_mbx_set_diag_state_link_num_SHIFT 16
#define lpfc_mbx_set_diag_state_link_num_MASK 0x0000003F
#define lpfc_mbx_set_diag_state_link_num_WORD word0
@@ -1336,18 +1996,19 @@ struct lpfc_mbx_set_link_diag_loopback {
union {
struct {
uint32_t word0;
-#define lpfc_mbx_set_diag_lpbk_type_SHIFT 0
-#define lpfc_mbx_set_diag_lpbk_type_MASK 0x00000001
-#define lpfc_mbx_set_diag_lpbk_type_WORD word0
-#define LPFC_DIAG_LOOPBACK_TYPE_DISABLE 0x0
-#define LPFC_DIAG_LOOPBACK_TYPE_INTERNAL 0x1
-#define LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL 0x2
-#define lpfc_mbx_set_diag_lpbk_link_num_SHIFT 16
-#define lpfc_mbx_set_diag_lpbk_link_num_MASK 0x0000003F
-#define lpfc_mbx_set_diag_lpbk_link_num_WORD word0
-#define lpfc_mbx_set_diag_lpbk_link_type_SHIFT 22
-#define lpfc_mbx_set_diag_lpbk_link_type_MASK 0x00000003
-#define lpfc_mbx_set_diag_lpbk_link_type_WORD word0
+#define lpfc_mbx_set_diag_lpbk_type_SHIFT 0
+#define lpfc_mbx_set_diag_lpbk_type_MASK 0x00000003
+#define lpfc_mbx_set_diag_lpbk_type_WORD word0
+#define LPFC_DIAG_LOOPBACK_TYPE_DISABLE 0x0
+#define LPFC_DIAG_LOOPBACK_TYPE_INTERNAL 0x1
+#define LPFC_DIAG_LOOPBACK_TYPE_SERDES 0x2
+#define LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL_TRUNKED 0x3
+#define lpfc_mbx_set_diag_lpbk_link_num_SHIFT 16
+#define lpfc_mbx_set_diag_lpbk_link_num_MASK 0x0000003F
+#define lpfc_mbx_set_diag_lpbk_link_num_WORD word0
+#define lpfc_mbx_set_diag_lpbk_link_type_SHIFT 22
+#define lpfc_mbx_set_diag_lpbk_link_type_MASK 0x00000003
+#define lpfc_mbx_set_diag_lpbk_link_type_WORD word0
} req;
struct {
uint32_t word0;
@@ -1470,16 +2131,106 @@ struct sli4_sge { /* SLI-4 */
uint32_t addr_lo;
uint32_t word2;
-#define lpfc_sli4_sge_offset_SHIFT 0 /* Offset of buffer - Not used*/
-#define lpfc_sli4_sge_offset_MASK 0x1FFFFFFF
+#define lpfc_sli4_sge_offset_SHIFT 0
+#define lpfc_sli4_sge_offset_MASK 0x07FFFFFF
#define lpfc_sli4_sge_offset_WORD word2
-#define lpfc_sli4_sge_last_SHIFT 31 /* Last SEG in the SGL sets
- this flag !! */
+#define lpfc_sli4_sge_type_SHIFT 27
+#define lpfc_sli4_sge_type_MASK 0x0000000F
+#define lpfc_sli4_sge_type_WORD word2
+#define LPFC_SGE_TYPE_DATA 0x0
+#define LPFC_SGE_TYPE_DIF 0x4
+#define LPFC_SGE_TYPE_LSP 0x5
+#define LPFC_SGE_TYPE_PEDIF 0x6
+#define LPFC_SGE_TYPE_PESEED 0x7
+#define LPFC_SGE_TYPE_DISEED 0x8
+#define LPFC_SGE_TYPE_ENC 0x9
+#define LPFC_SGE_TYPE_ATM 0xA
+#define LPFC_SGE_TYPE_SKIP 0xC
+#define lpfc_sli4_sge_last_SHIFT 31 /* Last SEG in the SGL sets it */
#define lpfc_sli4_sge_last_MASK 0x00000001
#define lpfc_sli4_sge_last_WORD word2
uint32_t sge_len;
};
+struct sli4_sge_le {
+ __le32 addr_hi;
+ __le32 addr_lo;
+
+ __le32 word2;
+ __le32 sge_len;
+};
+
+struct sli4_hybrid_sgl {
+ struct list_head list_node;
+ struct sli4_sge *dma_sgl;
+ dma_addr_t dma_phys_sgl;
+};
+
+struct fcp_cmd_rsp_buf {
+ struct list_head list_node;
+
+ /* for storing cmd/rsp dma alloc'ed virt_addr */
+ struct fcp_cmnd *fcp_cmnd;
+ struct fcp_rsp *fcp_rsp;
+
+ /* for storing this cmd/rsp's dma mapped phys addr from per CPU pool */
+ dma_addr_t fcp_cmd_rsp_dma_handle;
+};
+
+struct sli4_sge_diseed { /* SLI-4 */
+ uint32_t ref_tag;
+ uint32_t ref_tag_tran;
+
+ uint32_t word2;
+#define lpfc_sli4_sge_dif_apptran_SHIFT 0
+#define lpfc_sli4_sge_dif_apptran_MASK 0x0000FFFF
+#define lpfc_sli4_sge_dif_apptran_WORD word2
+#define lpfc_sli4_sge_dif_af_SHIFT 24
+#define lpfc_sli4_sge_dif_af_MASK 0x00000001
+#define lpfc_sli4_sge_dif_af_WORD word2
+#define lpfc_sli4_sge_dif_na_SHIFT 25
+#define lpfc_sli4_sge_dif_na_MASK 0x00000001
+#define lpfc_sli4_sge_dif_na_WORD word2
+#define lpfc_sli4_sge_dif_hi_SHIFT 26
+#define lpfc_sli4_sge_dif_hi_MASK 0x00000001
+#define lpfc_sli4_sge_dif_hi_WORD word2
+#define lpfc_sli4_sge_dif_type_SHIFT 27
+#define lpfc_sli4_sge_dif_type_MASK 0x0000000F
+#define lpfc_sli4_sge_dif_type_WORD word2
+#define lpfc_sli4_sge_dif_last_SHIFT 31 /* Last SEG in the SGL sets it */
+#define lpfc_sli4_sge_dif_last_MASK 0x00000001
+#define lpfc_sli4_sge_dif_last_WORD word2
+ uint32_t word3;
+#define lpfc_sli4_sge_dif_apptag_SHIFT 0
+#define lpfc_sli4_sge_dif_apptag_MASK 0x0000FFFF
+#define lpfc_sli4_sge_dif_apptag_WORD word3
+#define lpfc_sli4_sge_dif_bs_SHIFT 16
+#define lpfc_sli4_sge_dif_bs_MASK 0x00000007
+#define lpfc_sli4_sge_dif_bs_WORD word3
+#define lpfc_sli4_sge_dif_ai_SHIFT 19
+#define lpfc_sli4_sge_dif_ai_MASK 0x00000001
+#define lpfc_sli4_sge_dif_ai_WORD word3
+#define lpfc_sli4_sge_dif_me_SHIFT 20
+#define lpfc_sli4_sge_dif_me_MASK 0x00000001
+#define lpfc_sli4_sge_dif_me_WORD word3
+#define lpfc_sli4_sge_dif_re_SHIFT 21
+#define lpfc_sli4_sge_dif_re_MASK 0x00000001
+#define lpfc_sli4_sge_dif_re_WORD word3
+#define lpfc_sli4_sge_dif_ce_SHIFT 22
+#define lpfc_sli4_sge_dif_ce_MASK 0x00000001
+#define lpfc_sli4_sge_dif_ce_WORD word3
+#define lpfc_sli4_sge_dif_nr_SHIFT 23
+#define lpfc_sli4_sge_dif_nr_MASK 0x00000001
+#define lpfc_sli4_sge_dif_nr_WORD word3
+#define lpfc_sli4_sge_dif_oprx_SHIFT 24
+#define lpfc_sli4_sge_dif_oprx_MASK 0x0000000F
+#define lpfc_sli4_sge_dif_oprx_WORD word3
+#define lpfc_sli4_sge_dif_optx_SHIFT 28
+#define lpfc_sli4_sge_dif_optx_MASK 0x0000000F
+#define lpfc_sli4_sge_dif_optx_WORD word3
+/* optx and oprx use BG_OP_IN defines in lpfc_hw.h */
+};
+
struct fcf_record {
uint32_t max_rcv_size;
uint32_t fka_adv_period;
@@ -1549,8 +2300,14 @@ struct fcf_record {
#define lpfc_fcf_record_fc_map_2_MASK 0x000000FF
#define lpfc_fcf_record_fc_map_2_WORD word7
#define lpfc_fcf_record_fcf_valid_SHIFT 24
-#define lpfc_fcf_record_fcf_valid_MASK 0x000000FF
+#define lpfc_fcf_record_fcf_valid_MASK 0x00000001
#define lpfc_fcf_record_fcf_valid_WORD word7
+#define lpfc_fcf_record_fcf_fc_SHIFT 25
+#define lpfc_fcf_record_fcf_fc_MASK 0x00000001
+#define lpfc_fcf_record_fcf_fc_WORD word7
+#define lpfc_fcf_record_fcf_sol_SHIFT 31
+#define lpfc_fcf_record_fcf_sol_MASK 0x00000001
+#define lpfc_fcf_record_fcf_sol_WORD word7
uint32_t word8;
#define lpfc_fcf_record_fcf_index_SHIFT 0
#define lpfc_fcf_record_fcf_index_MASK 0x0000FFFF
@@ -1639,51 +2396,6 @@ struct lpfc_mbx_redisc_fcf_tbl {
#define lpfc_mbx_redisc_fcf_index_WORD word12
};
-struct lpfc_mbx_query_fw_cfg {
- struct mbox_header header;
- uint32_t config_number;
- uint32_t asic_rev;
- uint32_t phys_port;
- uint32_t function_mode;
-/* firmware Function Mode */
-#define lpfc_function_mode_toe_SHIFT 0
-#define lpfc_function_mode_toe_MASK 0x00000001
-#define lpfc_function_mode_toe_WORD function_mode
-#define lpfc_function_mode_nic_SHIFT 1
-#define lpfc_function_mode_nic_MASK 0x00000001
-#define lpfc_function_mode_nic_WORD function_mode
-#define lpfc_function_mode_rdma_SHIFT 2
-#define lpfc_function_mode_rdma_MASK 0x00000001
-#define lpfc_function_mode_rdma_WORD function_mode
-#define lpfc_function_mode_vm_SHIFT 3
-#define lpfc_function_mode_vm_MASK 0x00000001
-#define lpfc_function_mode_vm_WORD function_mode
-#define lpfc_function_mode_iscsi_i_SHIFT 4
-#define lpfc_function_mode_iscsi_i_MASK 0x00000001
-#define lpfc_function_mode_iscsi_i_WORD function_mode
-#define lpfc_function_mode_iscsi_t_SHIFT 5
-#define lpfc_function_mode_iscsi_t_MASK 0x00000001
-#define lpfc_function_mode_iscsi_t_WORD function_mode
-#define lpfc_function_mode_fcoe_i_SHIFT 6
-#define lpfc_function_mode_fcoe_i_MASK 0x00000001
-#define lpfc_function_mode_fcoe_i_WORD function_mode
-#define lpfc_function_mode_fcoe_t_SHIFT 7
-#define lpfc_function_mode_fcoe_t_MASK 0x00000001
-#define lpfc_function_mode_fcoe_t_WORD function_mode
-#define lpfc_function_mode_dal_SHIFT 8
-#define lpfc_function_mode_dal_MASK 0x00000001
-#define lpfc_function_mode_dal_WORD function_mode
-#define lpfc_function_mode_lro_SHIFT 9
-#define lpfc_function_mode_lro_MASK 0x00000001
-#define lpfc_function_mode_lro_WORD function_mode
-#define lpfc_function_mode_flex10_SHIFT 10
-#define lpfc_function_mode_flex10_MASK 0x00000001
-#define lpfc_function_mode_flex10_WORD function_mode
-#define lpfc_function_mode_ncsi_SHIFT 11
-#define lpfc_function_mode_ncsi_MASK 0x00000001
-#define lpfc_function_mode_ncsi_WORD function_mode
-};
-
/* Status field for embedded SLI_CONFIG mailbox command */
#define STATUS_SUCCESS 0x0
#define STATUS_FAILED 0x1
@@ -1711,6 +2423,16 @@ struct lpfc_mbx_query_fw_cfg {
#define STATUS_FCF_IN_USE 0x3a
#define STATUS_FCF_TABLE_EMPTY 0x43
+/*
+ * Additional status field for embedded SLI_CONFIG mailbox
+ * command.
+ */
+#define ADD_STATUS_OPERATION_ALREADY_ACTIVE 0x67
+#define ADD_STATUS_FW_NOT_SUPPORTED 0xEB
+#define ADD_STATUS_INVALID_REQUEST 0x4B
+#define ADD_STATUS_INVALID_OBJECT_NAME 0xA0
+#define ADD_STATUS_FW_DOWNLOAD_HW_DISABLED 0x58
+
struct lpfc_mbx_sli4_config {
struct mbox_header header;
};
@@ -1751,9 +2473,14 @@ struct lpfc_mbx_init_vfi {
#define lpfc_init_vfi_hop_count_MASK 0x000000FF
#define lpfc_init_vfi_hop_count_WORD word4
};
+#define MBX_VFI_IN_USE 0x9F02
+
struct lpfc_mbx_reg_vfi {
uint32_t word1;
+#define lpfc_reg_vfi_upd_SHIFT 29
+#define lpfc_reg_vfi_upd_MASK 0x00000001
+#define lpfc_reg_vfi_upd_WORD word1
#define lpfc_reg_vfi_vp_SHIFT 28
#define lpfc_reg_vfi_vp_MASK 0x00000001
#define lpfc_reg_vfi_vp_WORD word1
@@ -1772,9 +2499,15 @@ struct lpfc_mbx_reg_vfi {
uint32_t e_d_tov;
uint32_t r_a_tov;
uint32_t word10;
-#define lpfc_reg_vfi_nport_id_SHIFT 0
-#define lpfc_reg_vfi_nport_id_MASK 0x00FFFFFF
-#define lpfc_reg_vfi_nport_id_WORD word10
+#define lpfc_reg_vfi_nport_id_SHIFT 0
+#define lpfc_reg_vfi_nport_id_MASK 0x00FFFFFF
+#define lpfc_reg_vfi_nport_id_WORD word10
+#define lpfc_reg_vfi_bbcr_SHIFT 27
+#define lpfc_reg_vfi_bbcr_MASK 0x00000001
+#define lpfc_reg_vfi_bbcr_WORD word10
+#define lpfc_reg_vfi_bbscn_SHIFT 28
+#define lpfc_reg_vfi_bbscn_MASK 0x0000000F
+#define lpfc_reg_vfi_bbscn_WORD word10
};
struct lpfc_mbx_init_vpi {
@@ -1956,6 +2689,160 @@ struct lpfc_mbx_reg_fcfi {
#define lpfc_reg_fcfi_vlan_tag_WORD word8
};
+struct lpfc_mbx_reg_fcfi_mrq {
+ uint32_t word1;
+#define lpfc_reg_fcfi_mrq_info_index_SHIFT 0
+#define lpfc_reg_fcfi_mrq_info_index_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_mrq_info_index_WORD word1
+#define lpfc_reg_fcfi_mrq_fcfi_SHIFT 16
+#define lpfc_reg_fcfi_mrq_fcfi_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_mrq_fcfi_WORD word1
+ uint32_t word2;
+#define lpfc_reg_fcfi_mrq_rq_id1_SHIFT 0
+#define lpfc_reg_fcfi_mrq_rq_id1_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_mrq_rq_id1_WORD word2
+#define lpfc_reg_fcfi_mrq_rq_id0_SHIFT 16
+#define lpfc_reg_fcfi_mrq_rq_id0_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_mrq_rq_id0_WORD word2
+ uint32_t word3;
+#define lpfc_reg_fcfi_mrq_rq_id3_SHIFT 0
+#define lpfc_reg_fcfi_mrq_rq_id3_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_mrq_rq_id3_WORD word3
+#define lpfc_reg_fcfi_mrq_rq_id2_SHIFT 16
+#define lpfc_reg_fcfi_mrq_rq_id2_MASK 0x0000FFFF
+#define lpfc_reg_fcfi_mrq_rq_id2_WORD word3
+ uint32_t word4;
+#define lpfc_reg_fcfi_mrq_type_match0_SHIFT 24
+#define lpfc_reg_fcfi_mrq_type_match0_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_match0_WORD word4
+#define lpfc_reg_fcfi_mrq_type_mask0_SHIFT 16
+#define lpfc_reg_fcfi_mrq_type_mask0_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_mask0_WORD word4
+#define lpfc_reg_fcfi_mrq_rctl_match0_SHIFT 8
+#define lpfc_reg_fcfi_mrq_rctl_match0_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_match0_WORD word4
+#define lpfc_reg_fcfi_mrq_rctl_mask0_SHIFT 0
+#define lpfc_reg_fcfi_mrq_rctl_mask0_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_mask0_WORD word4
+ uint32_t word5;
+#define lpfc_reg_fcfi_mrq_type_match1_SHIFT 24
+#define lpfc_reg_fcfi_mrq_type_match1_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_match1_WORD word5
+#define lpfc_reg_fcfi_mrq_type_mask1_SHIFT 16
+#define lpfc_reg_fcfi_mrq_type_mask1_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_mask1_WORD word5
+#define lpfc_reg_fcfi_mrq_rctl_match1_SHIFT 8
+#define lpfc_reg_fcfi_mrq_rctl_match1_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_match1_WORD word5
+#define lpfc_reg_fcfi_mrq_rctl_mask1_SHIFT 0
+#define lpfc_reg_fcfi_mrq_rctl_mask1_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_mask1_WORD word5
+ uint32_t word6;
+#define lpfc_reg_fcfi_mrq_type_match2_SHIFT 24
+#define lpfc_reg_fcfi_mrq_type_match2_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_match2_WORD word6
+#define lpfc_reg_fcfi_mrq_type_mask2_SHIFT 16
+#define lpfc_reg_fcfi_mrq_type_mask2_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_mask2_WORD word6
+#define lpfc_reg_fcfi_mrq_rctl_match2_SHIFT 8
+#define lpfc_reg_fcfi_mrq_rctl_match2_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_match2_WORD word6
+#define lpfc_reg_fcfi_mrq_rctl_mask2_SHIFT 0
+#define lpfc_reg_fcfi_mrq_rctl_mask2_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_mask2_WORD word6
+ uint32_t word7;
+#define lpfc_reg_fcfi_mrq_type_match3_SHIFT 24
+#define lpfc_reg_fcfi_mrq_type_match3_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_match3_WORD word7
+#define lpfc_reg_fcfi_mrq_type_mask3_SHIFT 16
+#define lpfc_reg_fcfi_mrq_type_mask3_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_type_mask3_WORD word7
+#define lpfc_reg_fcfi_mrq_rctl_match3_SHIFT 8
+#define lpfc_reg_fcfi_mrq_rctl_match3_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_match3_WORD word7
+#define lpfc_reg_fcfi_mrq_rctl_mask3_SHIFT 0
+#define lpfc_reg_fcfi_mrq_rctl_mask3_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_rctl_mask3_WORD word7
+ uint32_t word8;
+#define lpfc_reg_fcfi_mrq_ptc7_SHIFT 31
+#define lpfc_reg_fcfi_mrq_ptc7_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc7_WORD word8
+#define lpfc_reg_fcfi_mrq_ptc6_SHIFT 30
+#define lpfc_reg_fcfi_mrq_ptc6_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc6_WORD word8
+#define lpfc_reg_fcfi_mrq_ptc5_SHIFT 29
+#define lpfc_reg_fcfi_mrq_ptc5_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc5_WORD word8
+#define lpfc_reg_fcfi_mrq_ptc4_SHIFT 28
+#define lpfc_reg_fcfi_mrq_ptc4_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc4_WORD word8
+#define lpfc_reg_fcfi_mrq_ptc3_SHIFT 27
+#define lpfc_reg_fcfi_mrq_ptc3_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc3_WORD word8
+#define lpfc_reg_fcfi_mrq_ptc2_SHIFT 26
+#define lpfc_reg_fcfi_mrq_ptc2_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc2_WORD word8
+#define lpfc_reg_fcfi_mrq_ptc1_SHIFT 25
+#define lpfc_reg_fcfi_mrq_ptc1_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc1_WORD word8
+#define lpfc_reg_fcfi_mrq_ptc0_SHIFT 24
+#define lpfc_reg_fcfi_mrq_ptc0_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_ptc0_WORD word8
+#define lpfc_reg_fcfi_mrq_pt7_SHIFT 23
+#define lpfc_reg_fcfi_mrq_pt7_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt7_WORD word8
+#define lpfc_reg_fcfi_mrq_pt6_SHIFT 22
+#define lpfc_reg_fcfi_mrq_pt6_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt6_WORD word8
+#define lpfc_reg_fcfi_mrq_pt5_SHIFT 21
+#define lpfc_reg_fcfi_mrq_pt5_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt5_WORD word8
+#define lpfc_reg_fcfi_mrq_pt4_SHIFT 20
+#define lpfc_reg_fcfi_mrq_pt4_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt4_WORD word8
+#define lpfc_reg_fcfi_mrq_pt3_SHIFT 19
+#define lpfc_reg_fcfi_mrq_pt3_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt3_WORD word8
+#define lpfc_reg_fcfi_mrq_pt2_SHIFT 18
+#define lpfc_reg_fcfi_mrq_pt2_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt2_WORD word8
+#define lpfc_reg_fcfi_mrq_pt1_SHIFT 17
+#define lpfc_reg_fcfi_mrq_pt1_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt1_WORD word8
+#define lpfc_reg_fcfi_mrq_pt0_SHIFT 16
+#define lpfc_reg_fcfi_mrq_pt0_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_pt0_WORD word8
+#define lpfc_reg_fcfi_mrq_xmv_SHIFT 15
+#define lpfc_reg_fcfi_mrq_xmv_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_xmv_WORD word8
+#define lpfc_reg_fcfi_mrq_mode_SHIFT 13
+#define lpfc_reg_fcfi_mrq_mode_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_mode_WORD word8
+#define lpfc_reg_fcfi_mrq_vv_SHIFT 12
+#define lpfc_reg_fcfi_mrq_vv_MASK 0x00000001
+#define lpfc_reg_fcfi_mrq_vv_WORD word8
+#define lpfc_reg_fcfi_mrq_vlan_tag_SHIFT 0
+#define lpfc_reg_fcfi_mrq_vlan_tag_MASK 0x00000FFF
+#define lpfc_reg_fcfi_mrq_vlan_tag_WORD word8
+ uint32_t word9;
+#define lpfc_reg_fcfi_mrq_policy_SHIFT 12
+#define lpfc_reg_fcfi_mrq_policy_MASK 0x0000000F
+#define lpfc_reg_fcfi_mrq_policy_WORD word9
+#define lpfc_reg_fcfi_mrq_filter_SHIFT 8
+#define lpfc_reg_fcfi_mrq_filter_MASK 0x0000000F
+#define lpfc_reg_fcfi_mrq_filter_WORD word9
+#define lpfc_reg_fcfi_mrq_npairs_SHIFT 0
+#define lpfc_reg_fcfi_mrq_npairs_MASK 0x000000FF
+#define lpfc_reg_fcfi_mrq_npairs_WORD word9
+ uint32_t word10;
+ uint32_t word11;
+ uint32_t word12;
+ uint32_t word13;
+ uint32_t word14;
+ uint32_t word15;
+ uint32_t word16;
+};
+
struct lpfc_mbx_unreg_fcfi {
uint32_t word1_rsv;
uint32_t word2;
@@ -1981,6 +2868,7 @@ struct lpfc_mbx_read_rev {
#define lpfc_mbx_rd_rev_vpd_MASK 0x00000001
#define lpfc_mbx_rd_rev_vpd_WORD word1
uint32_t first_hw_rev;
+#define LPFC_G7_ASIC_1 0xd
uint32_t second_hw_rev;
uint32_t word4_rsvd;
uint32_t third_hw_rev;
@@ -2018,7 +2906,39 @@ struct lpfc_mbx_read_config {
#define lpfc_mbx_rd_conf_extnts_inuse_SHIFT 31
#define lpfc_mbx_rd_conf_extnts_inuse_MASK 0x00000001
#define lpfc_mbx_rd_conf_extnts_inuse_WORD word1
+#define lpfc_mbx_rd_conf_fawwpn_SHIFT 30
+#define lpfc_mbx_rd_conf_fawwpn_MASK 0x00000001
+#define lpfc_mbx_rd_conf_fawwpn_WORD word1
+#define lpfc_mbx_rd_conf_wcs_SHIFT 28 /* warning signaling */
+#define lpfc_mbx_rd_conf_wcs_MASK 0x00000001
+#define lpfc_mbx_rd_conf_wcs_WORD word1
+#define lpfc_mbx_rd_conf_acs_SHIFT 27 /* alarm signaling */
+#define lpfc_mbx_rd_conf_acs_MASK 0x00000001
+#define lpfc_mbx_rd_conf_acs_WORD word1
uint32_t word2;
+#define lpfc_mbx_rd_conf_lnk_numb_SHIFT 0
+#define lpfc_mbx_rd_conf_lnk_numb_MASK 0x0000003F
+#define lpfc_mbx_rd_conf_lnk_numb_WORD word2
+#define lpfc_mbx_rd_conf_lnk_type_SHIFT 6
+#define lpfc_mbx_rd_conf_lnk_type_MASK 0x00000003
+#define lpfc_mbx_rd_conf_lnk_type_WORD word2
+#define LPFC_LNK_TYPE_GE 0
+#define LPFC_LNK_TYPE_FC 1
+#define lpfc_mbx_rd_conf_lnk_ldv_SHIFT 8
+#define lpfc_mbx_rd_conf_lnk_ldv_MASK 0x00000001
+#define lpfc_mbx_rd_conf_lnk_ldv_WORD word2
+#define lpfc_mbx_rd_conf_trunk_SHIFT 12
+#define lpfc_mbx_rd_conf_trunk_MASK 0x0000000F
+#define lpfc_mbx_rd_conf_trunk_WORD word2
+#define lpfc_mbx_rd_conf_pt_SHIFT 20
+#define lpfc_mbx_rd_conf_pt_MASK 0x00000003
+#define lpfc_mbx_rd_conf_pt_WORD word2
+#define lpfc_mbx_rd_conf_tf_SHIFT 22
+#define lpfc_mbx_rd_conf_tf_MASK 0x00000001
+#define lpfc_mbx_rd_conf_tf_WORD word2
+#define lpfc_mbx_rd_conf_ptv_SHIFT 23
+#define lpfc_mbx_rd_conf_ptv_MASK 0x00000001
+#define lpfc_mbx_rd_conf_ptv_WORD word2
#define lpfc_mbx_rd_conf_topology_SHIFT 24
#define lpfc_mbx_rd_conf_topology_MASK 0x000000FF
#define lpfc_mbx_rd_conf_topology_WORD word2
@@ -2032,8 +2952,20 @@ struct lpfc_mbx_read_config {
#define lpfc_mbx_rd_conf_r_a_tov_SHIFT 0
#define lpfc_mbx_rd_conf_r_a_tov_MASK 0x0000FFFF
#define lpfc_mbx_rd_conf_r_a_tov_WORD word6
+#define lpfc_mbx_rd_conf_link_speed_SHIFT 16
+#define lpfc_mbx_rd_conf_link_speed_MASK 0x0000FFFF
+#define lpfc_mbx_rd_conf_link_speed_WORD word6
uint32_t rsvd_7;
- uint32_t rsvd_8;
+ uint32_t word8;
+#define lpfc_mbx_rd_conf_bbscn_min_SHIFT 0
+#define lpfc_mbx_rd_conf_bbscn_min_MASK 0x0000000F
+#define lpfc_mbx_rd_conf_bbscn_min_WORD word8
+#define lpfc_mbx_rd_conf_bbscn_max_SHIFT 4
+#define lpfc_mbx_rd_conf_bbscn_max_MASK 0x0000000F
+#define lpfc_mbx_rd_conf_bbscn_max_WORD word8
+#define lpfc_mbx_rd_conf_bbscn_def_SHIFT 8
+#define lpfc_mbx_rd_conf_bbscn_def_MASK 0x0000000F
+#define lpfc_mbx_rd_conf_bbscn_def_WORD word8
uint32_t word9;
#define lpfc_mbx_rd_conf_lmt_SHIFT 0
#define lpfc_mbx_rd_conf_lmt_MASK 0x0000FFFF
@@ -2118,9 +3050,18 @@ struct lpfc_mbx_request_features {
#define lpfc_mbx_rq_ftr_rq_ifip_SHIFT 7
#define lpfc_mbx_rq_ftr_rq_ifip_MASK 0x00000001
#define lpfc_mbx_rq_ftr_rq_ifip_WORD word2
+#define lpfc_mbx_rq_ftr_rq_iaar_SHIFT 9
+#define lpfc_mbx_rq_ftr_rq_iaar_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_iaar_WORD word2
#define lpfc_mbx_rq_ftr_rq_perfh_SHIFT 11
#define lpfc_mbx_rq_ftr_rq_perfh_MASK 0x00000001
#define lpfc_mbx_rq_ftr_rq_perfh_WORD word2
+#define lpfc_mbx_rq_ftr_rq_mrqp_SHIFT 16
+#define lpfc_mbx_rq_ftr_rq_mrqp_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_mrqp_WORD word2
+#define lpfc_mbx_rq_ftr_rq_ashdr_SHIFT 17
+#define lpfc_mbx_rq_ftr_rq_ashdr_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rq_ashdr_WORD word2
uint32_t word3;
#define lpfc_mbx_rq_ftr_rsp_iaab_SHIFT 0
#define lpfc_mbx_rq_ftr_rsp_iaab_MASK 0x00000001
@@ -2149,179 +3090,231 @@ struct lpfc_mbx_request_features {
#define lpfc_mbx_rq_ftr_rsp_perfh_SHIFT 11
#define lpfc_mbx_rq_ftr_rsp_perfh_MASK 0x00000001
#define lpfc_mbx_rq_ftr_rsp_perfh_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_mrqp_SHIFT 16
+#define lpfc_mbx_rq_ftr_rsp_mrqp_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_mrqp_WORD word3
+#define lpfc_mbx_rq_ftr_rsp_ashdr_SHIFT 17
+#define lpfc_mbx_rq_ftr_rsp_ashdr_MASK 0x00000001
+#define lpfc_mbx_rq_ftr_rsp_ashdr_WORD word3
};
-struct lpfc_mbx_supp_pages {
- uint32_t word1;
-#define qs_SHIFT 0
-#define qs_MASK 0x00000001
-#define qs_WORD word1
-#define wr_SHIFT 1
-#define wr_MASK 0x00000001
-#define wr_WORD word1
-#define pf_SHIFT 8
-#define pf_MASK 0x000000ff
-#define pf_WORD word1
-#define cpn_SHIFT 16
-#define cpn_MASK 0x000000ff
-#define cpn_WORD word1
- uint32_t word2;
-#define list_offset_SHIFT 0
-#define list_offset_MASK 0x000000ff
-#define list_offset_WORD word2
-#define next_offset_SHIFT 8
-#define next_offset_MASK 0x000000ff
-#define next_offset_WORD word2
-#define elem_cnt_SHIFT 16
-#define elem_cnt_MASK 0x000000ff
-#define elem_cnt_WORD word2
- uint32_t word3;
-#define pn_0_SHIFT 24
-#define pn_0_MASK 0x000000ff
-#define pn_0_WORD word3
-#define pn_1_SHIFT 16
-#define pn_1_MASK 0x000000ff
-#define pn_1_WORD word3
-#define pn_2_SHIFT 8
-#define pn_2_MASK 0x000000ff
-#define pn_2_WORD word3
-#define pn_3_SHIFT 0
-#define pn_3_MASK 0x000000ff
-#define pn_3_WORD word3
- uint32_t word4;
-#define pn_4_SHIFT 24
-#define pn_4_MASK 0x000000ff
-#define pn_4_WORD word4
-#define pn_5_SHIFT 16
-#define pn_5_MASK 0x000000ff
-#define pn_5_WORD word4
-#define pn_6_SHIFT 8
-#define pn_6_MASK 0x000000ff
-#define pn_6_WORD word4
-#define pn_7_SHIFT 0
-#define pn_7_MASK 0x000000ff
-#define pn_7_WORD word4
- uint32_t rsvd[27];
-#define LPFC_SUPP_PAGES 0
-#define LPFC_BLOCK_GUARD_PROFILES 1
-#define LPFC_SLI4_PARAMETERS 2
-};
-
-struct lpfc_mbx_pc_sli4_params {
+struct lpfc_mbx_memory_dump_type3 {
uint32_t word1;
-#define qs_SHIFT 0
-#define qs_MASK 0x00000001
-#define qs_WORD word1
-#define wr_SHIFT 1
-#define wr_MASK 0x00000001
-#define wr_WORD word1
-#define pf_SHIFT 8
-#define pf_MASK 0x000000ff
-#define pf_WORD word1
-#define cpn_SHIFT 16
-#define cpn_MASK 0x000000ff
-#define cpn_WORD word1
+#define lpfc_mbx_memory_dump_type3_type_SHIFT 0
+#define lpfc_mbx_memory_dump_type3_type_MASK 0x0000000f
+#define lpfc_mbx_memory_dump_type3_type_WORD word1
+#define lpfc_mbx_memory_dump_type3_link_SHIFT 24
+#define lpfc_mbx_memory_dump_type3_link_MASK 0x000000ff
+#define lpfc_mbx_memory_dump_type3_link_WORD word1
uint32_t word2;
-#define if_type_SHIFT 0
-#define if_type_MASK 0x00000007
-#define if_type_WORD word2
-#define sli_rev_SHIFT 4
-#define sli_rev_MASK 0x0000000f
-#define sli_rev_WORD word2
-#define sli_family_SHIFT 8
-#define sli_family_MASK 0x000000ff
-#define sli_family_WORD word2
-#define featurelevel_1_SHIFT 16
-#define featurelevel_1_MASK 0x000000ff
-#define featurelevel_1_WORD word2
-#define featurelevel_2_SHIFT 24
-#define featurelevel_2_MASK 0x0000001f
-#define featurelevel_2_WORD word2
+#define lpfc_mbx_memory_dump_type3_page_no_SHIFT 0
+#define lpfc_mbx_memory_dump_type3_page_no_MASK 0x0000ffff
+#define lpfc_mbx_memory_dump_type3_page_no_WORD word2
+#define lpfc_mbx_memory_dump_type3_offset_SHIFT 16
+#define lpfc_mbx_memory_dump_type3_offset_MASK 0x0000ffff
+#define lpfc_mbx_memory_dump_type3_offset_WORD word2
uint32_t word3;
-#define fcoe_SHIFT 0
-#define fcoe_MASK 0x00000001
-#define fcoe_WORD word3
-#define fc_SHIFT 1
-#define fc_MASK 0x00000001
-#define fc_WORD word3
-#define nic_SHIFT 2
-#define nic_MASK 0x00000001
-#define nic_WORD word3
-#define iscsi_SHIFT 3
-#define iscsi_MASK 0x00000001
-#define iscsi_WORD word3
-#define rdma_SHIFT 4
-#define rdma_MASK 0x00000001
-#define rdma_WORD word3
- uint32_t sge_supp_len;
-#define SLI4_PAGE_SIZE 4096
- uint32_t word5;
-#define if_page_sz_SHIFT 0
-#define if_page_sz_MASK 0x0000ffff
-#define if_page_sz_WORD word5
-#define loopbk_scope_SHIFT 24
-#define loopbk_scope_MASK 0x0000000f
-#define loopbk_scope_WORD word5
-#define rq_db_window_SHIFT 28
-#define rq_db_window_MASK 0x0000000f
-#define rq_db_window_WORD word5
- uint32_t word6;
-#define eq_pages_SHIFT 0
-#define eq_pages_MASK 0x0000000f
-#define eq_pages_WORD word6
-#define eqe_size_SHIFT 8
-#define eqe_size_MASK 0x000000ff
-#define eqe_size_WORD word6
- uint32_t word7;
-#define cq_pages_SHIFT 0
-#define cq_pages_MASK 0x0000000f
-#define cq_pages_WORD word7
-#define cqe_size_SHIFT 8
-#define cqe_size_MASK 0x000000ff
-#define cqe_size_WORD word7
- uint32_t word8;
-#define mq_pages_SHIFT 0
-#define mq_pages_MASK 0x0000000f
-#define mq_pages_WORD word8
-#define mqe_size_SHIFT 8
-#define mqe_size_MASK 0x000000ff
-#define mqe_size_WORD word8
-#define mq_elem_cnt_SHIFT 16
-#define mq_elem_cnt_MASK 0x000000ff
-#define mq_elem_cnt_WORD word8
- uint32_t word9;
-#define wq_pages_SHIFT 0
-#define wq_pages_MASK 0x0000ffff
-#define wq_pages_WORD word9
-#define wqe_size_SHIFT 8
-#define wqe_size_MASK 0x000000ff
-#define wqe_size_WORD word9
- uint32_t word10;
-#define rq_pages_SHIFT 0
-#define rq_pages_MASK 0x0000ffff
-#define rq_pages_WORD word10
-#define rqe_size_SHIFT 8
-#define rqe_size_MASK 0x000000ff
-#define rqe_size_WORD word10
- uint32_t word11;
-#define hdr_pages_SHIFT 0
-#define hdr_pages_MASK 0x0000000f
-#define hdr_pages_WORD word11
-#define hdr_size_SHIFT 8
-#define hdr_size_MASK 0x0000000f
-#define hdr_size_WORD word11
-#define hdr_pp_align_SHIFT 16
-#define hdr_pp_align_MASK 0x0000ffff
-#define hdr_pp_align_WORD word11
- uint32_t word12;
-#define sgl_pages_SHIFT 0
-#define sgl_pages_MASK 0x0000000f
-#define sgl_pages_WORD word12
-#define sgl_pp_align_SHIFT 16
-#define sgl_pp_align_MASK 0x0000ffff
-#define sgl_pp_align_WORD word12
- uint32_t rsvd_13_63[51];
+#define lpfc_mbx_memory_dump_type3_length_SHIFT 0
+#define lpfc_mbx_memory_dump_type3_length_MASK 0x00ffffff
+#define lpfc_mbx_memory_dump_type3_length_WORD word3
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+ uint32_t return_len;
+};
+
+#define DMP_PAGE_A0 0xa0
+#define DMP_PAGE_A2 0xa2
+#define DMP_SFF_PAGE_A0_SIZE 256
+#define DMP_SFF_PAGE_A2_SIZE 256
+
+#define SFP_WAVELENGTH_LC1310 1310
+#define SFP_WAVELENGTH_LL1550 1550
+
+
+/*
+ * * SFF-8472 TABLE 3.4
+ * */
+#define SFF_PG0_CONNECTOR_UNKNOWN 0x00 /* Unknown */
+#define SFF_PG0_CONNECTOR_SC 0x01 /* SC */
+#define SFF_PG0_CONNECTOR_FC_COPPER1 0x02 /* FC style 1 copper connector */
+#define SFF_PG0_CONNECTOR_FC_COPPER2 0x03 /* FC style 2 copper connector */
+#define SFF_PG0_CONNECTOR_BNC 0x04 /* BNC / TNC */
+#define SFF_PG0_CONNECTOR__FC_COAX 0x05 /* FC coaxial headers */
+#define SFF_PG0_CONNECTOR_FIBERJACK 0x06 /* FiberJack */
+#define SFF_PG0_CONNECTOR_LC 0x07 /* LC */
+#define SFF_PG0_CONNECTOR_MT 0x08 /* MT - RJ */
+#define SFF_PG0_CONNECTOR_MU 0x09 /* MU */
+#define SFF_PG0_CONNECTOR_SF 0x0A /* SG */
+#define SFF_PG0_CONNECTOR_OPTICAL_PIGTAIL 0x0B /* Optical pigtail */
+#define SFF_PG0_CONNECTOR_OPTICAL_PARALLEL 0x0C /* MPO Parallel Optic */
+#define SFF_PG0_CONNECTOR_HSSDC_II 0x20 /* HSSDC II */
+#define SFF_PG0_CONNECTOR_COPPER_PIGTAIL 0x21 /* Copper pigtail */
+#define SFF_PG0_CONNECTOR_RJ45 0x22 /* RJ45 */
+
+/* SFF-8472 Table 3.1 Diagnostics: Data Fields Address/Page A0 */
+
+#define SSF_IDENTIFIER 0
+#define SSF_EXT_IDENTIFIER 1
+#define SSF_CONNECTOR 2
+#define SSF_TRANSCEIVER_CODE_B0 3
+#define SSF_TRANSCEIVER_CODE_B1 4
+#define SSF_TRANSCEIVER_CODE_B2 5
+#define SSF_TRANSCEIVER_CODE_B3 6
+#define SSF_TRANSCEIVER_CODE_B4 7
+#define SSF_TRANSCEIVER_CODE_B5 8
+#define SSF_TRANSCEIVER_CODE_B6 9
+#define SSF_TRANSCEIVER_CODE_B7 10
+#define SSF_ENCODING 11
+#define SSF_BR_NOMINAL 12
+#define SSF_RATE_IDENTIFIER 13
+#define SSF_LENGTH_9UM_KM 14
+#define SSF_LENGTH_9UM 15
+#define SSF_LENGTH_50UM_OM2 16
+#define SSF_LENGTH_62UM_OM1 17
+#define SFF_LENGTH_COPPER 18
+#define SSF_LENGTH_50UM_OM3 19
+#define SSF_VENDOR_NAME 20
+#define SSF_TRANSCEIVER2 36
+#define SSF_VENDOR_OUI 37
+#define SSF_VENDOR_PN 40
+#define SSF_VENDOR_REV 56
+#define SSF_WAVELENGTH_B1 60
+#define SSF_WAVELENGTH_B0 61
+#define SSF_CC_BASE 63
+#define SSF_OPTIONS_B1 64
+#define SSF_OPTIONS_B0 65
+#define SSF_BR_MAX 66
+#define SSF_BR_MIN 67
+#define SSF_VENDOR_SN 68
+#define SSF_DATE_CODE 84
+#define SSF_MONITORING_TYPEDIAGNOSTIC 92
+#define SSF_ENHANCED_OPTIONS 93
+#define SFF_8472_COMPLIANCE 94
+#define SSF_CC_EXT 95
+#define SSF_A0_VENDOR_SPECIFIC 96
+
+/* SFF-8472 Table 3.1a Diagnostics: Data Fields Address/Page A2 */
+
+#define SSF_TEMP_HIGH_ALARM 0
+#define SSF_TEMP_LOW_ALARM 2
+#define SSF_TEMP_HIGH_WARNING 4
+#define SSF_TEMP_LOW_WARNING 6
+#define SSF_VOLTAGE_HIGH_ALARM 8
+#define SSF_VOLTAGE_LOW_ALARM 10
+#define SSF_VOLTAGE_HIGH_WARNING 12
+#define SSF_VOLTAGE_LOW_WARNING 14
+#define SSF_BIAS_HIGH_ALARM 16
+#define SSF_BIAS_LOW_ALARM 18
+#define SSF_BIAS_HIGH_WARNING 20
+#define SSF_BIAS_LOW_WARNING 22
+#define SSF_TXPOWER_HIGH_ALARM 24
+#define SSF_TXPOWER_LOW_ALARM 26
+#define SSF_TXPOWER_HIGH_WARNING 28
+#define SSF_TXPOWER_LOW_WARNING 30
+#define SSF_RXPOWER_HIGH_ALARM 32
+#define SSF_RXPOWER_LOW_ALARM 34
+#define SSF_RXPOWER_HIGH_WARNING 36
+#define SSF_RXPOWER_LOW_WARNING 38
+#define SSF_EXT_CAL_CONSTANTS 56
+#define SSF_CC_DMI 95
+#define SFF_TEMPERATURE_B1 96
+#define SFF_TEMPERATURE_B0 97
+#define SFF_VCC_B1 98
+#define SFF_VCC_B0 99
+#define SFF_TX_BIAS_CURRENT_B1 100
+#define SFF_TX_BIAS_CURRENT_B0 101
+#define SFF_TXPOWER_B1 102
+#define SFF_TXPOWER_B0 103
+#define SFF_RXPOWER_B1 104
+#define SFF_RXPOWER_B0 105
+#define SSF_STATUS_CONTROL 110
+#define SSF_ALARM_FLAGS 112
+#define SSF_WARNING_FLAGS 116
+#define SSF_EXT_TATUS_CONTROL_B1 118
+#define SSF_EXT_TATUS_CONTROL_B0 119
+#define SSF_A2_VENDOR_SPECIFIC 120
+#define SSF_USER_EEPROM 128
+#define SSF_VENDOR_CONTROL 148
+
+
+/*
+ * Tranceiver codes Fibre Channel SFF-8472
+ * Table 3.5.
+ */
+
+struct sff_trasnceiver_codes_byte0 {
+ uint8_t inifiband:4;
+ uint8_t teng_ethernet:4;
+};
+
+struct sff_trasnceiver_codes_byte1 {
+ uint8_t sonet:6;
+ uint8_t escon:2;
+};
+
+struct sff_trasnceiver_codes_byte2 {
+ uint8_t soNet:8;
+};
+
+struct sff_trasnceiver_codes_byte3 {
+ uint8_t ethernet:8;
+};
+
+struct sff_trasnceiver_codes_byte4 {
+ uint8_t fc_el_lo:1;
+ uint8_t fc_lw_laser:1;
+ uint8_t fc_sw_laser:1;
+ uint8_t fc_md_distance:1;
+ uint8_t fc_lg_distance:1;
+ uint8_t fc_int_distance:1;
+ uint8_t fc_short_distance:1;
+ uint8_t fc_vld_distance:1;
+};
+
+struct sff_trasnceiver_codes_byte5 {
+ uint8_t reserved1:1;
+ uint8_t reserved2:1;
+ uint8_t fc_sfp_active:1; /* Active cable */
+ uint8_t fc_sfp_passive:1; /* Passive cable */
+ uint8_t fc_lw_laser:1; /* Longwave laser */
+ uint8_t fc_sw_laser_sl:1;
+ uint8_t fc_sw_laser_sn:1;
+ uint8_t fc_el_hi:1; /* Electrical enclosure high bit */
};
+
+struct sff_trasnceiver_codes_byte6 {
+ uint8_t fc_tm_sm:1; /* Single Mode */
+ uint8_t reserved:1;
+ uint8_t fc_tm_m6:1; /* Multimode, 62.5um (M6) */
+ uint8_t fc_tm_tv:1; /* Video Coax (TV) */
+ uint8_t fc_tm_mi:1; /* Miniature Coax (MI) */
+ uint8_t fc_tm_tp:1; /* Twisted Pair (TP) */
+ uint8_t fc_tm_tw:1; /* Twin Axial Pair */
+};
+
+struct sff_trasnceiver_codes_byte7 {
+ uint8_t fc_sp_100MB:1; /* 100 MB/sec */
+ uint8_t speed_chk_ecc:1;
+ uint8_t fc_sp_200mb:1; /* 200 MB/sec */
+ uint8_t fc_sp_3200MB:1; /* 3200 MB/sec */
+ uint8_t fc_sp_400MB:1; /* 400 MB/sec */
+ uint8_t fc_sp_1600MB:1; /* 1600 MB/sec */
+ uint8_t fc_sp_800MB:1; /* 800 MB/sec */
+ uint8_t fc_sp_1200MB:1; /* 1200 MB/sec */
+};
+
+/* User writable non-volatile memory, SFF-8472 Table 3.20 */
+struct user_eeprom {
+ uint8_t vendor_name[16];
+ uint8_t vendor_oui[3];
+ uint8_t vendor_pn[816];
+ uint8_t vendor_rev[4];
+ uint8_t vendor_sn[16];
+ uint8_t datecode[6];
+ uint8_t lot_code[2];
+ uint8_t reserved191[57];
+};
+
#define SLI4_PAGE_ALIGN(addr) (((addr)+((SLI4_PAGE_SIZE)-1)) \
&(~((SLI4_PAGE_SIZE)-1)))
@@ -2350,11 +3343,20 @@ struct lpfc_sli4_parameters {
#define cfg_sli_hint_2_MASK 0x0000001f
#define cfg_sli_hint_2_WORD word1
uint32_t word2;
+#define cfg_eqav_SHIFT 31
+#define cfg_eqav_MASK 0x00000001
+#define cfg_eqav_WORD word2
uint32_t word3;
uint32_t word4;
#define cfg_cqv_SHIFT 14
#define cfg_cqv_MASK 0x00000003
#define cfg_cqv_WORD word4
+#define cfg_cqpsize_SHIFT 16
+#define cfg_cqpsize_MASK 0x000000ff
+#define cfg_cqpsize_WORD word4
+#define cfg_cqav_SHIFT 31
+#define cfg_cqav_MASK 0x00000001
+#define cfg_cqav_WORD word4
uint32_t word5;
uint32_t word6;
#define cfg_mqv_SHIFT 14
@@ -2362,9 +3364,18 @@ struct lpfc_sli4_parameters {
#define cfg_mqv_WORD word6
uint32_t word7;
uint32_t word8;
+#define cfg_wqpcnt_SHIFT 0
+#define cfg_wqpcnt_MASK 0x0000000f
+#define cfg_wqpcnt_WORD word8
+#define cfg_wqsize_SHIFT 8
+#define cfg_wqsize_MASK 0x0000000f
+#define cfg_wqsize_WORD word8
#define cfg_wqv_SHIFT 14
#define cfg_wqv_MASK 0x00000003
#define cfg_wqv_WORD word8
+#define cfg_wqpsize_SHIFT 16
+#define cfg_wqpsize_MASK 0x000000ff
+#define cfg_wqpsize_WORD word8
uint32_t word9;
uint32_t word10;
#define cfg_rqv_SHIFT 14
@@ -2387,6 +3398,9 @@ struct lpfc_sli4_parameters {
#define cfg_phwq_SHIFT 15
#define cfg_phwq_MASK 0x00000001
#define cfg_phwq_WORD word12
+#define cfg_oas_SHIFT 25
+#define cfg_oas_MASK 0x00000001
+#define cfg_oas_WORD word12
#define cfg_loopbk_scope_SHIFT 28
#define cfg_loopbk_scope_MASK 0x0000000f
#define cfg_loopbk_scope_WORD word12
@@ -2406,6 +3420,191 @@ struct lpfc_sli4_parameters {
uint32_t word17;
uint32_t word18;
uint32_t word19;
+#define cfg_ext_embed_cb_SHIFT 0
+#define cfg_ext_embed_cb_MASK 0x00000001
+#define cfg_ext_embed_cb_WORD word19
+#define cfg_mds_diags_SHIFT 1
+#define cfg_mds_diags_MASK 0x00000001
+#define cfg_mds_diags_WORD word19
+#define cfg_nvme_SHIFT 3
+#define cfg_nvme_MASK 0x00000001
+#define cfg_nvme_WORD word19
+#define cfg_xib_SHIFT 4
+#define cfg_xib_MASK 0x00000001
+#define cfg_xib_WORD word19
+#define cfg_xpsgl_SHIFT 6
+#define cfg_xpsgl_MASK 0x00000001
+#define cfg_xpsgl_WORD word19
+#define cfg_eqdr_SHIFT 8
+#define cfg_eqdr_MASK 0x00000001
+#define cfg_eqdr_WORD word19
+#define cfg_nosr_SHIFT 9
+#define cfg_nosr_MASK 0x00000001
+#define cfg_nosr_WORD word19
+#define cfg_bv1s_SHIFT 10
+#define cfg_bv1s_MASK 0x00000001
+#define cfg_bv1s_WORD word19
+
+#define cfg_nsler_SHIFT 12
+#define cfg_nsler_MASK 0x00000001
+#define cfg_nsler_WORD word19
+#define cfg_pvl_SHIFT 13
+#define cfg_pvl_MASK 0x00000001
+#define cfg_pvl_WORD word19
+
+#define cfg_pbde_SHIFT 20
+#define cfg_pbde_MASK 0x00000001
+#define cfg_pbde_WORD word19
+
+ uint32_t word20;
+#define cfg_max_tow_xri_SHIFT 0
+#define cfg_max_tow_xri_MASK 0x0000ffff
+#define cfg_max_tow_xri_WORD word20
+
+ uint32_t word21;
+#define cfg_mi_ver_SHIFT 0
+#define cfg_mi_ver_MASK 0x0000ffff
+#define cfg_mi_ver_WORD word21
+#define cfg_cmf_SHIFT 24
+#define cfg_cmf_MASK 0x000000ff
+#define cfg_cmf_WORD word21
+
+ uint32_t mib_size;
+ uint32_t word23; /* RESERVED */
+
+ uint32_t word24;
+#define cfg_frag_field_offset_SHIFT 0
+#define cfg_frag_field_offset_MASK 0x0000ffff
+#define cfg_frag_field_offset_WORD word24
+
+#define cfg_frag_field_size_SHIFT 16
+#define cfg_frag_field_size_MASK 0x0000ffff
+#define cfg_frag_field_size_WORD word24
+
+ uint32_t word25;
+#define cfg_sgl_field_offset_SHIFT 0
+#define cfg_sgl_field_offset_MASK 0x0000ffff
+#define cfg_sgl_field_offset_WORD word25
+
+#define cfg_sgl_field_size_SHIFT 16
+#define cfg_sgl_field_size_MASK 0x0000ffff
+#define cfg_sgl_field_size_WORD word25
+
+ uint32_t word26; /* Chain SGE initial value LOW */
+ uint32_t word27; /* Chain SGE initial value HIGH */
+#define LPFC_NODELAY_MAX_IO 32
+};
+
+#define LPFC_SET_UE_RECOVERY 0x10
+#define LPFC_SET_MDS_DIAGS 0x12
+#define LPFC_SET_DUAL_DUMP 0x1e
+#define LPFC_SET_CGN_SIGNAL 0x1f
+#define LPFC_SET_ENABLE_MI 0x21
+#define LPFC_SET_LD_SIGNAL 0x23
+#define LPFC_SET_ENABLE_CMF 0x24
+struct lpfc_mbx_set_feature {
+ struct mbox_header header;
+ uint32_t feature;
+ uint32_t param_len;
+ uint32_t word6;
+#define lpfc_mbx_set_feature_UER_SHIFT 0
+#define lpfc_mbx_set_feature_UER_MASK 0x00000001
+#define lpfc_mbx_set_feature_UER_WORD word6
+#define lpfc_mbx_set_feature_mds_SHIFT 2
+#define lpfc_mbx_set_feature_mds_MASK 0x00000001
+#define lpfc_mbx_set_feature_mds_WORD word6
+#define lpfc_mbx_set_feature_mds_deep_loopbk_SHIFT 1
+#define lpfc_mbx_set_feature_mds_deep_loopbk_MASK 0x00000001
+#define lpfc_mbx_set_feature_mds_deep_loopbk_WORD word6
+#define lpfc_mbx_set_feature_CGN_warn_freq_SHIFT 0
+#define lpfc_mbx_set_feature_CGN_warn_freq_MASK 0x0000ffff
+#define lpfc_mbx_set_feature_CGN_warn_freq_WORD word6
+#define lpfc_mbx_set_feature_dd_SHIFT 0
+#define lpfc_mbx_set_feature_dd_MASK 0x00000001
+#define lpfc_mbx_set_feature_dd_WORD word6
+#define lpfc_mbx_set_feature_ddquery_SHIFT 1
+#define lpfc_mbx_set_feature_ddquery_MASK 0x00000001
+#define lpfc_mbx_set_feature_ddquery_WORD word6
+#define LPFC_DISABLE_DUAL_DUMP 0
+#define LPFC_ENABLE_DUAL_DUMP 1
+#define LPFC_QUERY_OP_DUAL_DUMP 2
+#define lpfc_mbx_set_feature_cmf_SHIFT 0
+#define lpfc_mbx_set_feature_cmf_MASK 0x00000001
+#define lpfc_mbx_set_feature_cmf_WORD word6
+#define lpfc_mbx_set_feature_lds_qry_SHIFT 0
+#define lpfc_mbx_set_feature_lds_qry_MASK 0x00000001
+#define lpfc_mbx_set_feature_lds_qry_WORD word6
+#define LPFC_QUERY_LDS_OP 1
+#define lpfc_mbx_set_feature_mi_SHIFT 0
+#define lpfc_mbx_set_feature_mi_MASK 0x0000ffff
+#define lpfc_mbx_set_feature_mi_WORD word6
+#define lpfc_mbx_set_feature_milunq_SHIFT 16
+#define lpfc_mbx_set_feature_milunq_MASK 0x0000ffff
+#define lpfc_mbx_set_feature_milunq_WORD word6
+ u32 word7;
+#define lpfc_mbx_set_feature_UERP_SHIFT 0
+#define lpfc_mbx_set_feature_UERP_MASK 0x0000ffff
+#define lpfc_mbx_set_feature_UERP_WORD word7
+#define lpfc_mbx_set_feature_UESR_SHIFT 16
+#define lpfc_mbx_set_feature_UESR_MASK 0x0000ffff
+#define lpfc_mbx_set_feature_UESR_WORD word7
+#define lpfc_mbx_set_feature_CGN_alarm_freq_SHIFT 0
+#define lpfc_mbx_set_feature_CGN_alarm_freq_MASK 0x0000ffff
+#define lpfc_mbx_set_feature_CGN_alarm_freq_WORD word7
+ u32 word8;
+#define lpfc_mbx_set_feature_CGN_acqe_freq_SHIFT 0
+#define lpfc_mbx_set_feature_CGN_acqe_freq_MASK 0x000000ff
+#define lpfc_mbx_set_feature_CGN_acqe_freq_WORD word8
+ u32 word9;
+ u32 word10;
+};
+
+
+#define LPFC_SET_HOST_OS_DRIVER_VERSION 0x2
+#define LPFC_SET_HOST_DATE_TIME 0x4
+
+struct lpfc_mbx_set_host_date_time {
+ uint32_t word6;
+#define lpfc_mbx_set_host_month_WORD word6
+#define lpfc_mbx_set_host_month_SHIFT 16
+#define lpfc_mbx_set_host_month_MASK 0xFF
+#define lpfc_mbx_set_host_day_WORD word6
+#define lpfc_mbx_set_host_day_SHIFT 8
+#define lpfc_mbx_set_host_day_MASK 0xFF
+#define lpfc_mbx_set_host_year_WORD word6
+#define lpfc_mbx_set_host_year_SHIFT 0
+#define lpfc_mbx_set_host_year_MASK 0xFF
+ uint32_t word7;
+#define lpfc_mbx_set_host_hour_WORD word7
+#define lpfc_mbx_set_host_hour_SHIFT 16
+#define lpfc_mbx_set_host_hour_MASK 0xFF
+#define lpfc_mbx_set_host_min_WORD word7
+#define lpfc_mbx_set_host_min_SHIFT 8
+#define lpfc_mbx_set_host_min_MASK 0xFF
+#define lpfc_mbx_set_host_sec_WORD word7
+#define lpfc_mbx_set_host_sec_SHIFT 0
+#define lpfc_mbx_set_host_sec_MASK 0xFF
+};
+
+struct lpfc_mbx_set_host_data {
+#define LPFC_HOST_OS_DRIVER_VERSION_SIZE 48
+ struct mbox_header header;
+ uint32_t param_id;
+ uint32_t param_len;
+ union {
+ uint8_t data[LPFC_HOST_OS_DRIVER_VERSION_SIZE];
+ struct lpfc_mbx_set_host_date_time tm;
+ } un;
+};
+
+struct lpfc_mbx_set_trunk_mode {
+ struct mbox_header header;
+ uint32_t word0;
+#define lpfc_mbx_set_trunk_mode_WORD word0
+#define lpfc_mbx_set_trunk_mode_SHIFT 0
+#define lpfc_mbx_set_trunk_mode_MASK 0xFF
+ uint32_t word1;
+ uint32_t word2;
};
struct lpfc_mbx_get_sli4_parameters {
@@ -2413,8 +3612,23 @@ struct lpfc_mbx_get_sli4_parameters {
struct lpfc_sli4_parameters sli4_parameters;
};
+struct lpfc_mbx_reg_congestion_buf {
+ struct mbox_header header;
+ uint32_t word0;
+#define lpfc_mbx_reg_cgn_buf_type_WORD word0
+#define lpfc_mbx_reg_cgn_buf_type_SHIFT 0
+#define lpfc_mbx_reg_cgn_buf_type_MASK 0xFF
+#define lpfc_mbx_reg_cgn_buf_cnt_WORD word0
+#define lpfc_mbx_reg_cgn_buf_cnt_SHIFT 16
+#define lpfc_mbx_reg_cgn_buf_cnt_MASK 0xFF
+ uint32_t word1;
+ uint32_t length;
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+};
+
struct lpfc_rscr_desc_generic {
-#define LPFC_RSRC_DESC_WSIZE 18
+#define LPFC_RSRC_DESC_WSIZE 22
uint32_t desc[LPFC_RSRC_DESC_WSIZE];
};
@@ -2424,6 +3638,9 @@ struct lpfc_rsrc_desc_pcie {
#define lpfc_rsrc_desc_pcie_type_MASK 0x000000ff
#define lpfc_rsrc_desc_pcie_type_WORD word0
#define LPFC_RSRC_DESC_TYPE_PCIE 0x40
+#define lpfc_rsrc_desc_pcie_length_SHIFT 8
+#define lpfc_rsrc_desc_pcie_length_MASK 0x000000ff
+#define lpfc_rsrc_desc_pcie_length_WORD word0
uint32_t word1;
#define lpfc_rsrc_desc_pcie_pfnum_SHIFT 0
#define lpfc_rsrc_desc_pcie_pfnum_MASK 0x000000ff
@@ -2451,6 +3668,12 @@ struct lpfc_rsrc_desc_fcfcoe {
#define lpfc_rsrc_desc_fcfcoe_type_MASK 0x000000ff
#define lpfc_rsrc_desc_fcfcoe_type_WORD word0
#define LPFC_RSRC_DESC_TYPE_FCFCOE 0x43
+#define lpfc_rsrc_desc_fcfcoe_length_SHIFT 8
+#define lpfc_rsrc_desc_fcfcoe_length_MASK 0x000000ff
+#define lpfc_rsrc_desc_fcfcoe_length_WORD word0
+#define LPFC_RSRC_DESC_TYPE_FCFCOE_V0_RSVD 0
+#define LPFC_RSRC_DESC_TYPE_FCFCOE_V0_LENGTH 72
+#define LPFC_RSRC_DESC_TYPE_FCFCOE_V1_LENGTH 88
uint32_t word1;
#define lpfc_rsrc_desc_fcfcoe_vfnum_SHIFT 0
#define lpfc_rsrc_desc_fcfcoe_vfnum_MASK 0x000000ff
@@ -2509,6 +3732,12 @@ struct lpfc_rsrc_desc_fcfcoe {
#define lpfc_rsrc_desc_fcfcoe_eq_cnt_SHIFT 16
#define lpfc_rsrc_desc_fcfcoe_eq_cnt_MASK 0x0000ffff
#define lpfc_rsrc_desc_fcfcoe_eq_cnt_WORD word13
+/* extended FC/FCoE Resource Descriptor when length = 88 bytes */
+ uint32_t bw_min;
+ uint32_t bw_max;
+ uint32_t iops_min;
+ uint32_t iops_max;
+ uint32_t reserved[4];
};
struct lpfc_func_cfg {
@@ -2552,15 +3781,130 @@ struct lpfc_mbx_get_prof_cfg {
} u;
};
+struct lpfc_controller_attribute {
+ uint32_t version_string[8];
+ uint32_t manufacturer_name[8];
+ uint32_t rsvd16;
+ uint32_t word17;
+#define lpfc_cntl_attr_flash_id_SHIFT 16
+#define lpfc_cntl_attr_flash_id_MASK 0x000000ff
+#define lpfc_cntl_attr_flash_id_WORD word17
+#define lpfc_cntl_attr_boot_enable_SHIFT 24
+#define lpfc_cntl_attr_boot_enable_MASK 0x00000001
+#define lpfc_cntl_attr_boot_enable_WORD word17
+ uint32_t rsvd18[2];
+ uint32_t ncsi_ver_str[3];
+ uint32_t rsvd23;
+ uint32_t model_number[8];
+ uint32_t description[16];
+ uint32_t serial_number[8];
+ uint32_t ipl_name[5];
+ uint32_t rsvd61[3];
+ uint32_t fw_ver_str[8];
+ uint32_t bios_ver_str[8];
+ uint32_t redboot_ver_str[8];
+ uint32_t driver_ver_str[8];
+ uint32_t flash_fw_ver_str[8];
+ uint32_t functionality;
+ uint32_t word105;
+#define lpfc_cntl_attr_asic_rev_SHIFT 16
+#define lpfc_cntl_attr_asic_rev_MASK 0x000000ff
+#define lpfc_cntl_attr_asic_rev_WORD word105
+ uint32_t rsvd106[3];
+ uint32_t word109;
+#define lpfc_cntl_attr_hba_port_cnt_SHIFT 24
+#define lpfc_cntl_attr_hba_port_cnt_MASK 0x000000ff
+#define lpfc_cntl_attr_hba_port_cnt_WORD word109
+ uint32_t rsvd110;
+ uint32_t word111;
+#define lpfc_cntl_attr_hba_status_SHIFT 8
+#define lpfc_cntl_attr_hba_status_MASK 0x000000ff
+#define lpfc_cntl_attr_hba_status_WORD word111
+#define lpfc_cntl_attr_lnk_numb_SHIFT 24
+#define lpfc_cntl_attr_lnk_numb_MASK 0x0000003f
+#define lpfc_cntl_attr_lnk_numb_WORD word111
+#define lpfc_cntl_attr_lnk_type_SHIFT 30
+#define lpfc_cntl_attr_lnk_type_MASK 0x00000003
+#define lpfc_cntl_attr_lnk_type_WORD word111
+ uint32_t rsvd112[9];
+ uint32_t word121;
+#define lpfc_cntl_attr_asic_gen_SHIFT 8
+#define lpfc_cntl_attr_asic_gen_MASK 0x000000ff
+#define lpfc_cntl_attr_asic_gen_WORD word121
+ uint32_t rsvd122[3];
+ uint32_t word125;
+#define lpfc_cntl_attr_pci_vendor_id_SHIFT 0
+#define lpfc_cntl_attr_pci_vendor_id_MASK 0x0000ffff
+#define lpfc_cntl_attr_pci_vendor_id_WORD word125
+#define lpfc_cntl_attr_pci_device_id_SHIFT 16
+#define lpfc_cntl_attr_pci_device_id_MASK 0x0000ffff
+#define lpfc_cntl_attr_pci_device_id_WORD word125
+ uint32_t word126;
+#define lpfc_cntl_attr_pci_subvdr_id_SHIFT 0
+#define lpfc_cntl_attr_pci_subvdr_id_MASK 0x0000ffff
+#define lpfc_cntl_attr_pci_subvdr_id_WORD word126
+#define lpfc_cntl_attr_pci_subsys_id_SHIFT 16
+#define lpfc_cntl_attr_pci_subsys_id_MASK 0x0000ffff
+#define lpfc_cntl_attr_pci_subsys_id_WORD word126
+ uint32_t word127;
+#define lpfc_cntl_attr_pci_bus_num_SHIFT 0
+#define lpfc_cntl_attr_pci_bus_num_MASK 0x000000ff
+#define lpfc_cntl_attr_pci_bus_num_WORD word127
+#define lpfc_cntl_attr_pci_dev_num_SHIFT 8
+#define lpfc_cntl_attr_pci_dev_num_MASK 0x000000ff
+#define lpfc_cntl_attr_pci_dev_num_WORD word127
+#define lpfc_cntl_attr_pci_fnc_num_SHIFT 16
+#define lpfc_cntl_attr_pci_fnc_num_MASK 0x000000ff
+#define lpfc_cntl_attr_pci_fnc_num_WORD word127
+ uint32_t rsvd128[7];
+};
+
+struct lpfc_mbx_get_cntl_attributes {
+ union lpfc_sli4_cfg_shdr cfg_shdr;
+ struct lpfc_controller_attribute cntl_attr;
+};
+
+struct lpfc_mbx_get_port_name {
+ struct mbox_header header;
+ union {
+ struct {
+ uint32_t word4;
+#define lpfc_mbx_get_port_name_lnk_type_SHIFT 0
+#define lpfc_mbx_get_port_name_lnk_type_MASK 0x00000003
+#define lpfc_mbx_get_port_name_lnk_type_WORD word4
+ } request;
+ struct {
+ uint32_t word4;
+#define lpfc_mbx_get_port_name_name0_SHIFT 0
+#define lpfc_mbx_get_port_name_name0_MASK 0x000000FF
+#define lpfc_mbx_get_port_name_name0_WORD word4
+#define lpfc_mbx_get_port_name_name1_SHIFT 8
+#define lpfc_mbx_get_port_name_name1_MASK 0x000000FF
+#define lpfc_mbx_get_port_name_name1_WORD word4
+#define lpfc_mbx_get_port_name_name2_SHIFT 16
+#define lpfc_mbx_get_port_name_name2_MASK 0x000000FF
+#define lpfc_mbx_get_port_name_name2_WORD word4
+#define lpfc_mbx_get_port_name_name3_SHIFT 24
+#define lpfc_mbx_get_port_name_name3_MASK 0x000000FF
+#define lpfc_mbx_get_port_name_name3_WORD word4
+#define LPFC_LINK_NUMBER_0 0
+#define LPFC_LINK_NUMBER_1 1
+#define LPFC_LINK_NUMBER_2 2
+#define LPFC_LINK_NUMBER_3 3
+ } response;
+ } u;
+};
+
/* Mailbox Completion Queue Error Messages */
-#define MB_CQE_STATUS_SUCCESS 0x0
+#define MB_CQE_STATUS_SUCCESS 0x0
#define MB_CQE_STATUS_INSUFFICIENT_PRIVILEGES 0x1
#define MB_CQE_STATUS_INVALID_PARAMETER 0x2
#define MB_CQE_STATUS_INSUFFICIENT_RESOURCES 0x3
#define MB_CEQ_STATUS_QUEUE_FLUSHING 0x4
#define MB_CQE_STATUS_DMA_FAILED 0x5
-#define LPFC_MBX_WR_CONFIG_MAX_BDE 8
+
+#define LPFC_MBX_WR_CONFIG_MAX_BDE 1
struct lpfc_mbx_wr_object {
struct mbox_header header;
union {
@@ -2569,16 +3913,31 @@ struct lpfc_mbx_wr_object {
#define lpfc_wr_object_eof_SHIFT 31
#define lpfc_wr_object_eof_MASK 0x00000001
#define lpfc_wr_object_eof_WORD word4
+#define lpfc_wr_object_eas_SHIFT 29
+#define lpfc_wr_object_eas_MASK 0x00000001
+#define lpfc_wr_object_eas_WORD word4
#define lpfc_wr_object_write_length_SHIFT 0
#define lpfc_wr_object_write_length_MASK 0x00FFFFFF
#define lpfc_wr_object_write_length_WORD word4
uint32_t write_offset;
- uint32_t object_name[26];
+ uint32_t object_name[LPFC_MBX_OBJECT_NAME_LEN_DW];
uint32_t bde_count;
struct ulp_bde64 bde[LPFC_MBX_WR_CONFIG_MAX_BDE];
} request;
struct {
uint32_t actual_write_length;
+ uint32_t word5;
+#define lpfc_wr_object_change_status_SHIFT 0
+#define lpfc_wr_object_change_status_MASK 0x000000FF
+#define lpfc_wr_object_change_status_WORD word5
+#define LPFC_CHANGE_STATUS_NO_RESET_NEEDED 0x00
+#define LPFC_CHANGE_STATUS_PHYS_DEV_RESET 0x01
+#define LPFC_CHANGE_STATUS_FW_RESET 0x02
+#define LPFC_CHANGE_STATUS_PORT_MIGRATION 0x04
+#define LPFC_CHANGE_STATUS_PCI_RESET 0x05
+#define lpfc_wr_object_csf_SHIFT 8
+#define lpfc_wr_object_csf_MASK 0x00000001
+#define lpfc_wr_object_csf_WORD word5
} response;
} u;
};
@@ -2606,13 +3965,18 @@ struct lpfc_mqe {
struct lpfc_mbx_del_fcf_tbl_entry del_fcf_entry;
struct lpfc_mbx_redisc_fcf_tbl redisc_fcf_tbl;
struct lpfc_mbx_reg_fcfi reg_fcfi;
+ struct lpfc_mbx_reg_fcfi_mrq reg_fcfi_mrq;
struct lpfc_mbx_unreg_fcfi unreg_fcfi;
struct lpfc_mbx_mq_create mq_create;
struct lpfc_mbx_mq_create_ext mq_create_ext;
+ struct lpfc_mbx_read_object read_object;
struct lpfc_mbx_eq_create eq_create;
+ struct lpfc_mbx_modify_eq_delay eq_delay;
struct lpfc_mbx_cq_create cq_create;
+ struct lpfc_mbx_cq_create_set cq_create_set;
struct lpfc_mbx_wq_create wq_create;
struct lpfc_mbx_rq_create rq_create;
+ struct lpfc_mbx_rq_create_v2 rq_create_v2;
struct lpfc_mbx_mq_destroy mq_destroy;
struct lpfc_mbx_eq_destroy eq_destroy;
struct lpfc_mbx_cq_destroy cq_destroy;
@@ -2628,17 +3992,23 @@ struct lpfc_mqe {
struct lpfc_mbx_read_config rd_config;
struct lpfc_mbx_request_features req_ftrs;
struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
- struct lpfc_mbx_query_fw_cfg query_fw_cfg;
- struct lpfc_mbx_supp_pages supp_pages;
- struct lpfc_mbx_pc_sli4_params sli4_params;
+ struct lpfc_mbx_query_fw_config query_fw_cfg;
+ struct lpfc_mbx_set_beacon_config beacon_config;
struct lpfc_mbx_get_sli4_parameters get_sli4_parameters;
+ struct lpfc_mbx_reg_congestion_buf reg_congestion_buf;
struct lpfc_mbx_set_link_diag_state link_diag_state;
struct lpfc_mbx_set_link_diag_loopback link_diag_loopback;
struct lpfc_mbx_run_link_diag_test link_diag_test;
struct lpfc_mbx_get_func_cfg get_func_cfg;
struct lpfc_mbx_get_prof_cfg get_prof_cfg;
- struct lpfc_mbx_nop nop;
struct lpfc_mbx_wr_object wr_object;
+ struct lpfc_mbx_get_port_name get_port_name;
+ struct lpfc_mbx_set_feature set_feature;
+ struct lpfc_mbx_memory_dump_type3 mem_dump_type3;
+ struct lpfc_mbx_set_host_data set_host_data;
+ struct lpfc_mbx_set_trunk_mode set_trunk_mode;
+ struct lpfc_mbx_nop nop;
+ struct lpfc_mbx_set_ras_fwlog ras_fwlog;
} un;
};
@@ -2648,8 +4018,8 @@ struct lpfc_mcqe {
#define lpfc_mcqe_status_MASK 0x0000FFFF
#define lpfc_mcqe_status_WORD word0
#define lpfc_mcqe_ext_status_SHIFT 16
-#define lpfc_mcqe_ext_status_MASK 0x0000FFFF
-#define lpfc_mcqe_ext_status_WORD word0
+#define lpfc_mcqe_ext_status_MASK 0x0000FFFF
+#define lpfc_mcqe_ext_status_WORD word0
uint32_t mcqe_tag0;
uint32_t mcqe_tag1;
uint32_t trailer;
@@ -2692,6 +4062,10 @@ struct lpfc_acqe_link {
#define LPFC_ASYNC_LINK_SPEED_100MBPS 0x2
#define LPFC_ASYNC_LINK_SPEED_1GBPS 0x3
#define LPFC_ASYNC_LINK_SPEED_10GBPS 0x4
+#define LPFC_ASYNC_LINK_SPEED_20GBPS 0x5
+#define LPFC_ASYNC_LINK_SPEED_25GBPS 0x6
+#define LPFC_ASYNC_LINK_SPEED_40GBPS 0x7
+#define LPFC_ASYNC_LINK_SPEED_100GBPS 0x8
#define lpfc_acqe_link_duplex_SHIFT 16
#define lpfc_acqe_link_duplex_MASK 0x000000FF
#define lpfc_acqe_link_duplex_WORD word0
@@ -2718,6 +4092,7 @@ struct lpfc_acqe_link {
#define LPFC_ASYNC_LINK_FAULT_NONE 0x0
#define LPFC_ASYNC_LINK_FAULT_LOCAL 0x1
#define LPFC_ASYNC_LINK_FAULT_REMOTE 0x2
+#define LPFC_ASYNC_LINK_FAULT_LR_LRR 0x3
#define lpfc_acqe_logical_link_speed_SHIFT 16
#define lpfc_acqe_logical_link_speed_MASK 0x0000FFFF
#define lpfc_acqe_logical_link_speed_WORD word1
@@ -2768,18 +4143,24 @@ struct lpfc_acqe_grp5 {
uint32_t trailer;
};
+extern const char *const trunk_errmsg[];
+
struct lpfc_acqe_fc_la {
uint32_t word0;
#define lpfc_acqe_fc_la_speed_SHIFT 24
#define lpfc_acqe_fc_la_speed_MASK 0x000000FF
#define lpfc_acqe_fc_la_speed_WORD word0
-#define LPFC_FC_LA_SPEED_UNKOWN 0x0
+#define LPFC_FC_LA_SPEED_UNKNOWN 0x0
#define LPFC_FC_LA_SPEED_1G 0x1
#define LPFC_FC_LA_SPEED_2G 0x2
#define LPFC_FC_LA_SPEED_4G 0x4
#define LPFC_FC_LA_SPEED_8G 0x8
#define LPFC_FC_LA_SPEED_10G 0xA
#define LPFC_FC_LA_SPEED_16G 0x10
+#define LPFC_FC_LA_SPEED_32G 0x20
+#define LPFC_FC_LA_SPEED_64G 0x21
+#define LPFC_FC_LA_SPEED_128G 0x22
+#define LPFC_FC_LA_SPEED_256G 0x23
#define lpfc_acqe_fc_la_topology_SHIFT 16
#define lpfc_acqe_fc_la_topology_MASK 0x000000FF
#define lpfc_acqe_fc_la_topology_WORD word0
@@ -2794,6 +4175,12 @@ struct lpfc_acqe_fc_la {
#define LPFC_FC_LA_TYPE_LINK_UP 0x1
#define LPFC_FC_LA_TYPE_LINK_DOWN 0x2
#define LPFC_FC_LA_TYPE_NO_HARD_ALPA 0x3
+#define LPFC_FC_LA_TYPE_MDS_LINK_DOWN 0x4
+#define LPFC_FC_LA_TYPE_MDS_LOOPBACK 0x5
+#define LPFC_FC_LA_TYPE_UNEXP_WWPN 0x6
+#define LPFC_FC_LA_TYPE_TRUNKING_EVENT 0x7
+#define LPFC_FC_LA_TYPE_ACTIVATE_FAIL 0x8
+#define LPFC_FC_LA_TYPE_LINK_RESET_PRTCL_EVT 0x9
#define lpfc_acqe_fc_la_port_type_SHIFT 6
#define lpfc_acqe_fc_la_port_type_MASK 0x00000003
#define lpfc_acqe_fc_la_port_type_WORD word0
@@ -2802,6 +4189,32 @@ struct lpfc_acqe_fc_la {
#define lpfc_acqe_fc_la_port_number_SHIFT 0
#define lpfc_acqe_fc_la_port_number_MASK 0x0000003F
#define lpfc_acqe_fc_la_port_number_WORD word0
+
+/* Attention Type is 0x07 (Trunking Event) word0 */
+#define lpfc_acqe_fc_la_trunk_link_status_port0_SHIFT 16
+#define lpfc_acqe_fc_la_trunk_link_status_port0_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_link_status_port0_WORD word0
+#define lpfc_acqe_fc_la_trunk_link_status_port1_SHIFT 17
+#define lpfc_acqe_fc_la_trunk_link_status_port1_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_link_status_port1_WORD word0
+#define lpfc_acqe_fc_la_trunk_link_status_port2_SHIFT 18
+#define lpfc_acqe_fc_la_trunk_link_status_port2_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_link_status_port2_WORD word0
+#define lpfc_acqe_fc_la_trunk_link_status_port3_SHIFT 19
+#define lpfc_acqe_fc_la_trunk_link_status_port3_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_link_status_port3_WORD word0
+#define lpfc_acqe_fc_la_trunk_config_port0_SHIFT 20
+#define lpfc_acqe_fc_la_trunk_config_port0_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_config_port0_WORD word0
+#define lpfc_acqe_fc_la_trunk_config_port1_SHIFT 21
+#define lpfc_acqe_fc_la_trunk_config_port1_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_config_port1_WORD word0
+#define lpfc_acqe_fc_la_trunk_config_port2_SHIFT 22
+#define lpfc_acqe_fc_la_trunk_config_port2_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_config_port2_WORD word0
+#define lpfc_acqe_fc_la_trunk_config_port3_SHIFT 23
+#define lpfc_acqe_fc_la_trunk_config_port3_MASK 0x0000001
+#define lpfc_acqe_fc_la_trunk_config_port3_WORD word0
uint32_t word1;
#define lpfc_acqe_fc_la_llink_spd_SHIFT 16
#define lpfc_acqe_fc_la_llink_spd_MASK 0x0000FFFF
@@ -2809,6 +4222,15 @@ struct lpfc_acqe_fc_la {
#define lpfc_acqe_fc_la_fault_SHIFT 0
#define lpfc_acqe_fc_la_fault_MASK 0x000000FF
#define lpfc_acqe_fc_la_fault_WORD word1
+#define lpfc_acqe_fc_la_link_status_SHIFT 8
+#define lpfc_acqe_fc_la_link_status_MASK 0x0000007F
+#define lpfc_acqe_fc_la_link_status_WORD word1
+#define lpfc_acqe_fc_la_trunk_fault_SHIFT 0
+#define lpfc_acqe_fc_la_trunk_fault_MASK 0x0000000F
+#define lpfc_acqe_fc_la_trunk_fault_WORD word1
+#define lpfc_acqe_fc_la_trunk_linkmask_SHIFT 4
+#define lpfc_acqe_fc_la_trunk_linkmask_MASK 0x000000F
+#define lpfc_acqe_fc_la_trunk_linkmask_WORD word1
#define LPFC_FC_LA_FAULT_NONE 0x0
#define LPFC_FC_LA_FAULT_LOCAL 0x1
#define LPFC_FC_LA_FAULT_REMOTE 0x2
@@ -2818,16 +4240,86 @@ struct lpfc_acqe_fc_la {
#define LPFC_FC_LA_EVENT_TYPE_SHARED_LINK 0x2
};
+struct lpfc_acqe_misconfigured_event {
+ struct {
+ uint32_t word0;
+#define lpfc_sli_misconfigured_port0_state_SHIFT 0
+#define lpfc_sli_misconfigured_port0_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port0_state_WORD word0
+#define lpfc_sli_misconfigured_port1_state_SHIFT 8
+#define lpfc_sli_misconfigured_port1_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port1_state_WORD word0
+#define lpfc_sli_misconfigured_port2_state_SHIFT 16
+#define lpfc_sli_misconfigured_port2_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port2_state_WORD word0
+#define lpfc_sli_misconfigured_port3_state_SHIFT 24
+#define lpfc_sli_misconfigured_port3_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port3_state_WORD word0
+ uint32_t word1;
+#define lpfc_sli_misconfigured_port0_op_SHIFT 0
+#define lpfc_sli_misconfigured_port0_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port0_op_WORD word1
+#define lpfc_sli_misconfigured_port0_severity_SHIFT 1
+#define lpfc_sli_misconfigured_port0_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port0_severity_WORD word1
+#define lpfc_sli_misconfigured_port1_op_SHIFT 8
+#define lpfc_sli_misconfigured_port1_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port1_op_WORD word1
+#define lpfc_sli_misconfigured_port1_severity_SHIFT 9
+#define lpfc_sli_misconfigured_port1_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port1_severity_WORD word1
+#define lpfc_sli_misconfigured_port2_op_SHIFT 16
+#define lpfc_sli_misconfigured_port2_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port2_op_WORD word1
+#define lpfc_sli_misconfigured_port2_severity_SHIFT 17
+#define lpfc_sli_misconfigured_port2_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port2_severity_WORD word1
+#define lpfc_sli_misconfigured_port3_op_SHIFT 24
+#define lpfc_sli_misconfigured_port3_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port3_op_WORD word1
+#define lpfc_sli_misconfigured_port3_severity_SHIFT 25
+#define lpfc_sli_misconfigured_port3_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port3_severity_WORD word1
+ } theEvent;
+#define LPFC_SLI_EVENT_STATUS_VALID 0x00
+#define LPFC_SLI_EVENT_STATUS_NOT_PRESENT 0x01
+#define LPFC_SLI_EVENT_STATUS_WRONG_TYPE 0x02
+#define LPFC_SLI_EVENT_STATUS_UNSUPPORTED 0x03
+#define LPFC_SLI_EVENT_STATUS_UNQUALIFIED 0x04
+#define LPFC_SLI_EVENT_STATUS_UNCERTIFIED 0x05
+};
+
+struct lpfc_acqe_cgn_signal {
+ u32 word0;
+#define lpfc_warn_acqe_SHIFT 0
+#define lpfc_warn_acqe_MASK 0x7FFFFFFF
+#define lpfc_warn_acqe_WORD word0
+#define lpfc_imm_acqe_SHIFT 31
+#define lpfc_imm_acqe_MASK 0x1
+#define lpfc_imm_acqe_WORD word0
+ u32 alarm_cnt;
+ u32 word2;
+ u32 trailer;
+};
+
struct lpfc_acqe_sli {
uint32_t event_data1;
uint32_t event_data2;
- uint32_t reserved;
+ uint32_t event_data3;
uint32_t trailer;
#define LPFC_SLI_EVENT_TYPE_PORT_ERROR 0x1
#define LPFC_SLI_EVENT_TYPE_OVER_TEMP 0x2
#define LPFC_SLI_EVENT_TYPE_NORM_TEMP 0x3
#define LPFC_SLI_EVENT_TYPE_NVLOG_POST 0x4
#define LPFC_SLI_EVENT_TYPE_DIAG_DUMP 0x5
+#define LPFC_SLI_EVENT_TYPE_MISCONFIGURED 0x9
+#define LPFC_SLI_EVENT_TYPE_REMOTE_DPORT 0xA
+#define LPFC_SLI_EVENT_TYPE_PORT_PARAMS_CHG 0xE
+#define LPFC_SLI_EVENT_TYPE_MISCONF_FAWWN 0xF
+#define LPFC_SLI_EVENT_TYPE_EEPROM_FAILURE 0x10
+#define LPFC_SLI_EVENT_TYPE_CGN_SIGNAL 0x11
+#define LPFC_SLI_EVENT_TYPE_RD_SIGNAL 0x12
+#define LPFC_SLI_EVENT_TYPE_RESET_CM_STATS 0x13
};
/*
@@ -2855,6 +4347,12 @@ struct wqe_common {
#define wqe_ctxt_tag_MASK 0x0000FFFF
#define wqe_ctxt_tag_WORD word6
uint32_t word7;
+#define wqe_dif_SHIFT 0
+#define wqe_dif_MASK 0x00000003
+#define wqe_dif_WORD word7
+#define LPFC_WQE_DIF_PASSTHRU 1
+#define LPFC_WQE_DIF_STRIP 2
+#define LPFC_WQE_DIF_INSERT 3
#define wqe_ct_SHIFT 2
#define wqe_ct_MASK 0x00000003
#define wqe_ct_WORD word7
@@ -2867,12 +4365,21 @@ struct wqe_common {
#define wqe_class_SHIFT 16
#define wqe_class_MASK 0x00000007
#define wqe_class_WORD word7
+#define wqe_ar_SHIFT 19
+#define wqe_ar_MASK 0x00000001
+#define wqe_ar_WORD word7
+#define wqe_ag_SHIFT wqe_ar_SHIFT
+#define wqe_ag_MASK wqe_ar_MASK
+#define wqe_ag_WORD wqe_ar_WORD
#define wqe_pu_SHIFT 20
#define wqe_pu_MASK 0x00000003
#define wqe_pu_WORD word7
#define wqe_erp_SHIFT 22
#define wqe_erp_MASK 0x00000001
#define wqe_erp_WORD word7
+#define wqe_conf_SHIFT wqe_erp_SHIFT
+#define wqe_conf_MASK wqe_erp_MASK
+#define wqe_conf_WORD wqe_erp_WORD
#define wqe_lnk_SHIFT 23
#define wqe_lnk_MASK 0x00000001
#define wqe_lnk_WORD word7
@@ -2890,10 +4397,27 @@ struct wqe_common {
#define wqe_rcvoxid_SHIFT 16
#define wqe_rcvoxid_MASK 0x0000FFFF
#define wqe_rcvoxid_WORD word9
+#define wqe_sof_SHIFT 24
+#define wqe_sof_MASK 0x000000FF
+#define wqe_sof_WORD word9
+#define wqe_eof_SHIFT 16
+#define wqe_eof_MASK 0x000000FF
+#define wqe_eof_WORD word9
uint32_t word10;
#define wqe_ebde_cnt_SHIFT 0
#define wqe_ebde_cnt_MASK 0x0000000f
#define wqe_ebde_cnt_WORD word10
+#define wqe_xchg_SHIFT 4
+#define wqe_xchg_MASK 0x00000001
+#define wqe_xchg_WORD word10
+#define LPFC_SCSI_XCHG 0x0
+#define LPFC_NVME_XCHG 0x1
+#define wqe_appid_SHIFT 5
+#define wqe_appid_MASK 0x00000001
+#define wqe_appid_WORD word10
+#define wqe_oas_SHIFT 6
+#define wqe_oas_MASK 0x00000001
+#define wqe_oas_WORD word10
#define wqe_lenloc_SHIFT 7
#define wqe_lenloc_MASK 0x00000003
#define wqe_lenloc_WORD word10
@@ -2910,6 +4434,7 @@ struct wqe_common {
#define wqe_iod_SHIFT 13
#define wqe_iod_MASK 0x00000001
#define wqe_iod_WORD word10
+#define LPFC_WQE_IOD_NONE 0
#define LPFC_WQE_IOD_WRITE 0
#define LPFC_WQE_IOD_READ 1
#define wqe_dbde_SHIFT 14
@@ -2931,6 +4456,9 @@ struct wqe_common {
#define wqe_xc_SHIFT 21
#define wqe_xc_MASK 0x00000001
#define wqe_xc_WORD word10
+#define wqe_sr_SHIFT 22
+#define wqe_sr_MASK 0x00000001
+#define wqe_sr_WORD word10
#define wqe_ccpe_SHIFT 23
#define wqe_ccpe_MASK 0x00000001
#define wqe_ccpe_WORD word10
@@ -2942,15 +4470,26 @@ struct wqe_common {
#define wqe_cmd_type_MASK 0x0000000f
#define wqe_cmd_type_WORD word11
#define wqe_els_id_SHIFT 4
-#define wqe_els_id_MASK 0x00000003
+#define wqe_els_id_MASK 0x00000007
#define wqe_els_id_WORD word11
-#define LPFC_ELS_ID_FLOGI 3
-#define LPFC_ELS_ID_FDISC 2
-#define LPFC_ELS_ID_LOGO 1
-#define LPFC_ELS_ID_DEFAULT 0
+#define wqe_irsp_SHIFT 4
+#define wqe_irsp_MASK 0x00000001
+#define wqe_irsp_WORD word11
+#define wqe_pbde_SHIFT 5
+#define wqe_pbde_MASK 0x00000001
+#define wqe_pbde_WORD word11
+#define wqe_sup_SHIFT 6
+#define wqe_sup_MASK 0x00000001
+#define wqe_sup_WORD word11
+#define wqe_ffrq_SHIFT 6
+#define wqe_ffrq_MASK 0x00000001
+#define wqe_ffrq_WORD word11
#define wqe_wqec_SHIFT 7
#define wqe_wqec_MASK 0x00000001
#define wqe_wqec_WORD word11
+#define wqe_irsplen_SHIFT 8
+#define wqe_irsplen_MASK 0x0000000f
+#define wqe_irsplen_WORD word11
#define wqe_cqid_SHIFT 16
#define wqe_cqid_MASK 0x0000ffff
#define wqe_cqid_WORD word11
@@ -2982,6 +4521,14 @@ struct lpfc_wqe_generic{
uint32_t payload[4];
};
+enum els_request64_wqe_word11 {
+ LPFC_ELS_ID_DEFAULT,
+ LPFC_ELS_ID_LOGO,
+ LPFC_ELS_ID_FDISC,
+ LPFC_ELS_ID_FLOGI,
+ LPFC_ELS_ID_PLOGI,
+};
+
struct els_request64_wqe {
struct ulp_bde64 bde;
uint32_t payload_len;
@@ -3008,13 +4555,20 @@ struct els_request64_wqe {
#define els_req64_hopcnt_SHIFT 24
#define els_req64_hopcnt_MASK 0x000000ff
#define els_req64_hopcnt_WORD word13
- uint32_t reserved[2];
+ uint32_t word14;
+ uint32_t max_response_payload_len;
};
struct xmit_els_rsp64_wqe {
struct ulp_bde64 bde;
uint32_t response_payload_len;
- uint32_t rsvd4;
+ uint32_t word4;
+#define els_rsp64_sid_SHIFT 0
+#define els_rsp64_sid_MASK 0x00FFFFFF
+#define els_rsp64_sid_WORD word4
+#define els_rsp64_sp_SHIFT 24
+#define els_rsp64_sp_MASK 0x00000001
+#define els_rsp64_sp_WORD word4
struct wqe_did wqe_dest;
struct wqe_common wqe_com; /* words 6-11 */
uint32_t word12;
@@ -3061,7 +4615,11 @@ struct xmit_bls_rsp64_wqe {
uint32_t rsrvd4;
struct wqe_did wqe_dest;
struct wqe_common wqe_com; /* words 6-11 */
- uint32_t rsvd_12_15[4];
+ uint32_t word12;
+#define xmit_bls_rsp64_temprpi_SHIFT 0
+#define xmit_bls_rsp64_temprpi_MASK 0x0000ffff
+#define xmit_bls_rsp64_temprpi_WORD word12
+ uint32_t rsvd_13_15[3];
};
struct wqe_rctl_dfctl {
@@ -3072,6 +4630,9 @@ struct wqe_rctl_dfctl {
#define wqe_la_SHIFT 3
#define wqe_la_MASK 0x000000001
#define wqe_la_WORD word5
+#define wqe_xo_SHIFT 6
+#define wqe_xo_MASK 0x000000001
+#define wqe_xo_WORD word5
#define wqe_ls_SHIFT 7
#define wqe_ls_MASK 0x000000001
#define wqe_ls_WORD word5
@@ -3110,7 +4671,56 @@ struct gen_req64_wqe {
uint32_t relative_offset;
struct wqe_rctl_dfctl wge_ctl; /* word 5 */
struct wqe_common wqe_com; /* words 6-11 */
- uint32_t rsvd_12_15[4];
+ uint32_t rsvd_12_14[3];
+ uint32_t max_response_payload_len;
+};
+
+/* Define NVME PRLI request to fabric. NVME is a
+ * fabric-only protocol.
+ * Updated to red-lined v1.08 on Sept 16, 2016
+ */
+struct lpfc_nvme_prli {
+ uint32_t word1;
+ /* The Response Code is defined in the FCP PRLI lpfc_hw.h */
+#define prli_acc_rsp_code_SHIFT 8
+#define prli_acc_rsp_code_MASK 0x0000000f
+#define prli_acc_rsp_code_WORD word1
+#define prli_estabImagePair_SHIFT 13
+#define prli_estabImagePair_MASK 0x00000001
+#define prli_estabImagePair_WORD word1
+#define prli_type_code_ext_SHIFT 16
+#define prli_type_code_ext_MASK 0x000000ff
+#define prli_type_code_ext_WORD word1
+#define prli_type_code_SHIFT 24
+#define prli_type_code_MASK 0x000000ff
+#define prli_type_code_WORD word1
+ uint32_t word_rsvd2;
+ uint32_t word_rsvd3;
+
+ uint32_t word4;
+#define prli_fba_SHIFT 0
+#define prli_fba_MASK 0x00000001
+#define prli_fba_WORD word4
+#define prli_disc_SHIFT 3
+#define prli_disc_MASK 0x00000001
+#define prli_disc_WORD word4
+#define prli_tgt_SHIFT 4
+#define prli_tgt_MASK 0x00000001
+#define prli_tgt_WORD word4
+#define prli_init_SHIFT 5
+#define prli_init_MASK 0x00000001
+#define prli_init_WORD word4
+#define prli_conf_SHIFT 7
+#define prli_conf_MASK 0x00000001
+#define prli_conf_WORD word4
+#define prli_nsler_SHIFT 8
+#define prli_nsler_MASK 0x00000001
+#define prli_nsler_WORD word4
+ uint32_t word5;
+#define prli_fb_sz_SHIFT 0
+#define prli_fb_sz_MASK 0x0000ffff
+#define prli_fb_sz_WORD word5
+#define LPFC_NVMET_FB_SZ_MAX 65536 /* Driver target mode only. */
};
struct create_xri_wqe {
@@ -3123,6 +4733,72 @@ struct create_xri_wqe {
#define T_REQUEST_TAG 3
#define T_XRI_TAG 1
+struct cmf_sync_wqe {
+ uint32_t rsrvd[3];
+ uint32_t word3;
+#define cmf_sync_interval_SHIFT 0
+#define cmf_sync_interval_MASK 0x00000ffff
+#define cmf_sync_interval_WORD word3
+#define cmf_sync_afpin_SHIFT 16
+#define cmf_sync_afpin_MASK 0x000000001
+#define cmf_sync_afpin_WORD word3
+#define cmf_sync_asig_SHIFT 17
+#define cmf_sync_asig_MASK 0x000000001
+#define cmf_sync_asig_WORD word3
+#define cmf_sync_op_SHIFT 20
+#define cmf_sync_op_MASK 0x00000000f
+#define cmf_sync_op_WORD word3
+#define cmf_sync_ver_SHIFT 24
+#define cmf_sync_ver_MASK 0x0000000ff
+#define cmf_sync_ver_WORD word3
+#define LPFC_CMF_SYNC_VER 1
+ uint32_t event_tag;
+ uint32_t word5;
+#define cmf_sync_wsigmax_SHIFT 0
+#define cmf_sync_wsigmax_MASK 0x00000ffff
+#define cmf_sync_wsigmax_WORD word5
+#define cmf_sync_wsigcnt_SHIFT 16
+#define cmf_sync_wsigcnt_MASK 0x00000ffff
+#define cmf_sync_wsigcnt_WORD word5
+ uint32_t word6;
+ uint32_t word7;
+#define cmf_sync_cmnd_SHIFT 8
+#define cmf_sync_cmnd_MASK 0x0000000ff
+#define cmf_sync_cmnd_WORD word7
+ uint32_t word8;
+ uint32_t word9;
+#define cmf_sync_reqtag_SHIFT 0
+#define cmf_sync_reqtag_MASK 0x00000ffff
+#define cmf_sync_reqtag_WORD word9
+#define cmf_sync_wfpinmax_SHIFT 16
+#define cmf_sync_wfpinmax_MASK 0x0000000ff
+#define cmf_sync_wfpinmax_WORD word9
+#define cmf_sync_wfpincnt_SHIFT 24
+#define cmf_sync_wfpincnt_MASK 0x0000000ff
+#define cmf_sync_wfpincnt_WORD word9
+ uint32_t word10;
+#define cmf_sync_qosd_SHIFT 9
+#define cmf_sync_qosd_MASK 0x00000001
+#define cmf_sync_qosd_WORD word10
+ uint32_t word11;
+#define cmf_sync_cmd_type_SHIFT 0
+#define cmf_sync_cmd_type_MASK 0x0000000f
+#define cmf_sync_cmd_type_WORD word11
+#define cmf_sync_wqec_SHIFT 7
+#define cmf_sync_wqec_MASK 0x00000001
+#define cmf_sync_wqec_WORD word11
+#define cmf_sync_cqid_SHIFT 16
+#define cmf_sync_cqid_MASK 0x0000ffff
+#define cmf_sync_cqid_WORD word11
+ uint32_t read_bytes;
+ uint32_t word13;
+#define cmf_sync_period_SHIFT 24
+#define cmf_sync_period_MASK 0x000000ff
+#define cmf_sync_period_WORD word13
+ uint32_t word14;
+ uint32_t word15;
+};
+
struct abort_cmd_wqe {
uint32_t rsrvd[3];
uint32_t word3;
@@ -3140,7 +4816,14 @@ struct abort_cmd_wqe {
struct fcp_iwrite64_wqe {
struct ulp_bde64 bde;
- uint32_t payload_offset_len;
+ uint32_t word3;
+#define cmd_buff_len_SHIFT 16
+#define cmd_buff_len_MASK 0x00000ffff
+#define cmd_buff_len_WORD word3
+/* Note: payload_offset_len field depends on ASIC support */
+#define payload_offset_len_SHIFT 0
+#define payload_offset_len_MASK 0x0000ffff
+#define payload_offset_len_WORD word3
uint32_t total_xfer_len;
uint32_t initial_xfer_len;
struct wqe_common wqe_com; /* words 6-11 */
@@ -3150,7 +4833,14 @@ struct fcp_iwrite64_wqe {
struct fcp_iread64_wqe {
struct ulp_bde64 bde;
- uint32_t payload_offset_len; /* word 3 */
+ uint32_t word3;
+#define cmd_buff_len_SHIFT 16
+#define cmd_buff_len_MASK 0x00000ffff
+#define cmd_buff_len_WORD word3
+/* Note: payload_offset_len field depends on ASIC support */
+#define payload_offset_len_SHIFT 0
+#define payload_offset_len_MASK 0x0000ffff
+#define payload_offset_len_WORD word3
uint32_t total_xfer_len; /* word 4 */
uint32_t rsrvd5; /* word 5 */
struct wqe_common wqe_com; /* words 6-11 */
@@ -3160,13 +4850,79 @@ struct fcp_iread64_wqe {
struct fcp_icmnd64_wqe {
struct ulp_bde64 bde; /* words 0-2 */
- uint32_t rsrvd3; /* word 3 */
+ uint32_t word3;
+#define cmd_buff_len_SHIFT 16
+#define cmd_buff_len_MASK 0x00000ffff
+#define cmd_buff_len_WORD word3
+/* Note: payload_offset_len field depends on ASIC support */
+#define payload_offset_len_SHIFT 0
+#define payload_offset_len_MASK 0x0000ffff
+#define payload_offset_len_WORD word3
uint32_t rsrvd4; /* word 4 */
uint32_t rsrvd5; /* word 5 */
struct wqe_common wqe_com; /* words 6-11 */
uint32_t rsvd_12_15[4]; /* word 12-15 */
};
+struct fcp_trsp64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t response_len;
+ uint32_t rsvd_4_5[2];
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t rsvd_12_15[4]; /* word 12-15 */
+};
+
+struct fcp_tsend64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t payload_offset_len;
+ uint32_t relative_offset;
+ uint32_t reserved;
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t fcp_data_len; /* word 12 */
+ uint32_t rsvd_13_15[3]; /* word 13-15 */
+};
+
+struct fcp_treceive64_wqe {
+ struct ulp_bde64 bde;
+ uint32_t payload_offset_len;
+ uint32_t relative_offset;
+ uint32_t reserved;
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t fcp_data_len; /* word 12 */
+ uint32_t rsvd_13_15[3]; /* word 13-15 */
+};
+#define TXRDY_PAYLOAD_LEN 12
+
+#define CMD_SEND_FRAME 0xE1
+
+struct send_frame_wqe {
+ struct ulp_bde64 bde; /* words 0-2 */
+ uint32_t frame_len; /* word 3 */
+ uint32_t fc_hdr_wd0; /* word 4 */
+ uint32_t fc_hdr_wd1; /* word 5 */
+ struct wqe_common wqe_com; /* words 6-11 */
+ uint32_t fc_hdr_wd2; /* word 12 */
+ uint32_t fc_hdr_wd3; /* word 13 */
+ uint32_t fc_hdr_wd4; /* word 14 */
+ uint32_t fc_hdr_wd5; /* word 15 */
+};
+
+#define ELS_RDF_REG_TAG_CNT 4
+struct lpfc_els_rdf_reg_desc {
+ struct fc_df_desc_fpin_reg_hdr reg_desc; /* descriptor header */
+ __be32 desc_tags[ELS_RDF_REG_TAG_CNT];
+ /* tags in reg_desc */
+};
+
+struct lpfc_els_rdf_req {
+ struct fc_els_rdf_hdr rdf; /* hdr up to descriptors */
+ struct lpfc_els_rdf_reg_desc reg_d1; /* 1st descriptor */
+};
+
+struct lpfc_els_rdf_rsp {
+ struct fc_els_rdf_resp_hdr rdf_resp; /* hdr up to descriptors */
+ struct lpfc_els_rdf_reg_desc reg_d1; /* 1st descriptor */
+};
union lpfc_wqe {
uint32_t words[16];
@@ -3175,6 +4931,7 @@ union lpfc_wqe {
struct fcp_iread64_wqe fcp_iread;
struct fcp_iwrite64_wqe fcp_iwrite;
struct abort_cmd_wqe abort_cmd;
+ struct cmf_sync_wqe cmf_sync;
struct create_xri_wqe create_xri;
struct xmit_bcast64_wqe xmit_bcast64;
struct xmit_seq64_wqe xmit_sequence;
@@ -3182,11 +4939,37 @@ union lpfc_wqe {
struct xmit_els_rsp64_wqe xmit_els_rsp;
struct els_request64_wqe els_req;
struct gen_req64_wqe gen_req;
+ struct fcp_trsp64_wqe fcp_trsp;
+ struct fcp_tsend64_wqe fcp_tsend;
+ struct fcp_treceive64_wqe fcp_treceive;
+ struct send_frame_wqe send_frame;
};
-#define LPFC_GROUP_OJECT_MAGIC_NUM 0xfeaa0001
-#define LPFC_FILE_TYPE_GROUP 0xf7
-#define LPFC_FILE_ID_GROUP 0xa2
+union lpfc_wqe128 {
+ uint32_t words[32];
+ struct lpfc_wqe_generic generic;
+ struct fcp_icmnd64_wqe fcp_icmd;
+ struct fcp_iread64_wqe fcp_iread;
+ struct fcp_iwrite64_wqe fcp_iwrite;
+ struct abort_cmd_wqe abort_cmd;
+ struct cmf_sync_wqe cmf_sync;
+ struct create_xri_wqe create_xri;
+ struct xmit_bcast64_wqe xmit_bcast64;
+ struct xmit_seq64_wqe xmit_sequence;
+ struct xmit_bls_rsp64_wqe xmit_bls_rsp;
+ struct xmit_els_rsp64_wqe xmit_els_rsp;
+ struct els_request64_wqe els_req;
+ struct gen_req64_wqe gen_req;
+ struct fcp_trsp64_wqe fcp_trsp;
+ struct fcp_tsend64_wqe fcp_tsend;
+ struct fcp_treceive64_wqe fcp_treceive;
+ struct send_frame_wqe send_frame;
+};
+
+#define MAGIC_NUMBER_G6 0xFEAA0003
+#define MAGIC_NUMBER_G7 0xFEAA0005
+#define MAGIC_NUMBER_G7P 0xFEAA0020
+
struct lpfc_grp_hdr {
uint32_t size;
uint32_t magic_number;
@@ -3198,14 +4981,72 @@ struct lpfc_grp_hdr {
#define lpfc_grp_hdr_id_MASK 0x000000FF
#define lpfc_grp_hdr_id_WORD word2
uint8_t rev_name[128];
+ uint8_t date[12];
+ uint8_t revision[32];
};
-#define FCP_COMMAND 0x0
-#define FCP_COMMAND_DATA_OUT 0x1
-#define ELS_COMMAND_NON_FIP 0xC
-#define ELS_COMMAND_FIP 0xD
-#define OTHER_COMMAND 0x8
+/* Defines for WQE command type */
+#define FCP_COMMAND 0x0
+#define NVME_READ_CMD 0x0
+#define FCP_COMMAND_DATA_OUT 0x1
+#define NVME_WRITE_CMD 0x1
+#define COMMAND_DATA_IN 0x0
+#define COMMAND_DATA_OUT 0x1
+#define FCP_COMMAND_TRECEIVE 0x2
+#define FCP_COMMAND_TRSP 0x3
+#define FCP_COMMAND_TSEND 0x7
+#define OTHER_COMMAND 0x8
+#define CMF_SYNC_COMMAND 0xA
+#define ELS_COMMAND_NON_FIP 0xC
+#define ELS_COMMAND_FIP 0xD
+
+#define LPFC_NVME_EMBED_CMD 0x0
+#define LPFC_NVME_EMBED_WRITE 0x1
+#define LPFC_NVME_EMBED_READ 0x2
+
+/* WQE Commands */
+#define CMD_ABORT_XRI_WQE 0x0F
+#define CMD_XMIT_SEQUENCE64_WQE 0x82
+#define CMD_XMIT_BCAST64_WQE 0x84
+#define CMD_ELS_REQUEST64_WQE 0x8A
+#define CMD_XMIT_ELS_RSP64_WQE 0x95
+#define CMD_XMIT_BLS_RSP64_WQE 0x97
+#define CMD_FCP_IWRITE64_WQE 0x98
+#define CMD_FCP_IREAD64_WQE 0x9A
+#define CMD_FCP_ICMND64_WQE 0x9C
+#define CMD_FCP_TSEND64_WQE 0x9F
+#define CMD_FCP_TRECEIVE64_WQE 0xA1
+#define CMD_FCP_TRSP64_WQE 0xA3
+#define CMD_GEN_REQUEST64_WQE 0xC2
+#define CMD_CMF_SYNC_WQE 0xE8
+
+#define CMD_WQE_MASK 0xff
+
#define LPFC_FW_DUMP 1
#define LPFC_FW_RESET 2
#define LPFC_DV_RESET 3
+
+/* On some kernels, enum fc_ls_tlv_dtag does not have
+ * these 2 enums defined, on other kernels it does.
+ * To get aound this we need to add these 2 defines here.
+ */
+#ifndef ELS_DTAG_LNK_FAULT_CAP
+#define ELS_DTAG_LNK_FAULT_CAP 0x0001000D
+#endif
+#ifndef ELS_DTAG_CG_SIGNAL_CAP
+#define ELS_DTAG_CG_SIGNAL_CAP 0x0001000F
+#endif
+
+/*
+ * Initializer useful for decoding FPIN string table.
+ */
+#define FC_FPIN_CONGN_SEVERITY_INIT { \
+ { FPIN_CONGN_SEVERITY_WARNING, "Warning" }, \
+ { FPIN_CONGN_SEVERITY_ERROR, "Alarm" }, \
+}
+
+/* Used for logging FPIN messages */
+#define LPFC_FPIN_WWPN_LINE_SZ 128
+#define LPFC_FPIN_WWPN_LINE_CNT 6
+#define LPFC_FPIN_WWPN_NUM_LINE 6
diff --git a/drivers/scsi/lpfc/lpfc_ids.h b/drivers/scsi/lpfc/lpfc_ids.h
new file mode 100644
index 000000000000..0b1616e93cf4
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_ids.h
@@ -0,0 +1,156 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.broadcom.com *
+ * Portions Copyright (C) 2004-2005 Christoph Hellwig *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *******************************************************************/
+
+#include <linux/pci.h>
+
+const struct pci_device_id lpfc_id_table[] = {
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FIREFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_THOR,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PEGASUS,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_CENTAUR,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_DRAGONFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SUPERFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_RFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE_SCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE_DCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS_SCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS_DCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BMID,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BSMB,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_SCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_DCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZMID,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZSMB,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_TFLY,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP101,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP10000S,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP11000S,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LPE11000S,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_MID,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_SMB,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_DCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_SCSP,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_S,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_VF,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_PF,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_S,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TOMCAT,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FALCON,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BALIUS,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FC,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FC_VF,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE_VF,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G6_FC,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G7_FC,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G7P_FC,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_VF,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_16XE,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_161E, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_16XE,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_162E, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_16XE,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_164E, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_16XP,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_161P, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_16XP,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_162P, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_16XP,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_164P, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_32XE,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_321E, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_32XE,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_322E, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_32XE,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_324E, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_32XP,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_321P, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_32XP,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_322P, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_32XP,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_CLRY_324P, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_TLFC_2,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_TLFC_2XX2, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_TLFC_3,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_TLFC_3162, },
+ {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_TLFC_3,
+ PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_TLFC_3322, },
+ { 0 }
+};
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 148b98ddbb1d..b1460b16dd91 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -24,18 +26,28 @@
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
+#include <linux/sched/clock.h>
#include <linux/ctype.h>
-#include <linux/aer.h>
#include <linux/slab.h>
#include <linux/firmware.h>
+#include <linux/miscdevice.h>
+#include <linux/percpu.h>
+#include <linux/irq.h>
+#include <linux/bitops.h>
+#include <linux/crash_dump.h>
+#include <linux/cpu.h>
+#include <linux/cpuhotplug.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -43,29 +55,32 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
#include "lpfc_version.h"
+#include "lpfc_ids.h"
-char *_dump_buf_data;
-unsigned long _dump_buf_data_order;
-char *_dump_buf_dif;
-unsigned long _dump_buf_dif_order;
-spinlock_t _dump_buf_lock;
+static enum cpuhp_state lpfc_cpuhp_state;
+/* Used when mapping IRQ vectors in a driver centric manner */
+static uint32_t lpfc_present_cpu;
+static bool lpfc_pldv_detect;
+static void __lpfc_cpuhp_remove(struct lpfc_hba *phba);
+static void lpfc_cpuhp_remove(struct lpfc_hba *phba);
+static void lpfc_cpuhp_add(struct lpfc_hba *phba);
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
static int lpfc_post_rcv_buf(struct lpfc_hba *);
-static int lpfc_sli4_queue_create(struct lpfc_hba *);
-static void lpfc_sli4_queue_destroy(struct lpfc_hba *);
+static int lpfc_sli4_queue_verify(struct lpfc_hba *);
static int lpfc_create_bootstrap_mbox(struct lpfc_hba *);
static int lpfc_setup_endian_order(struct lpfc_hba *);
-static int lpfc_sli4_read_config(struct lpfc_hba *);
static void lpfc_destroy_bootstrap_mbox(struct lpfc_hba *);
-static void lpfc_free_sgl_list(struct lpfc_hba *);
-static int lpfc_init_sgl_list(struct lpfc_hba *);
+static void lpfc_free_els_sgl_list(struct lpfc_hba *);
+static void lpfc_free_nvmet_sgl_list(struct lpfc_hba *);
+static void lpfc_init_sgl_list(struct lpfc_hba *);
static int lpfc_init_active_sgl_array(struct lpfc_hba *);
static void lpfc_free_active_sgl(struct lpfc_hba *);
static int lpfc_hba_down_post_s3(struct lpfc_hba *phba);
@@ -73,10 +88,21 @@ static int lpfc_hba_down_post_s4(struct lpfc_hba *phba);
static int lpfc_sli4_cq_event_pool_create(struct lpfc_hba *);
static void lpfc_sli4_cq_event_pool_destroy(struct lpfc_hba *);
static void lpfc_sli4_cq_event_release_all(struct lpfc_hba *);
+static void lpfc_sli4_disable_intr(struct lpfc_hba *);
+static uint32_t lpfc_sli4_enable_intr(struct lpfc_hba *, uint32_t);
+static void lpfc_sli4_oas_verify(struct lpfc_hba *phba);
+static uint16_t lpfc_find_cpu_handle(struct lpfc_hba *, uint16_t, int);
+static void lpfc_setup_bg(struct lpfc_hba *, struct Scsi_Host *);
+static int lpfc_sli4_cgn_parm_chg_evt(struct lpfc_hba *);
+static void lpfc_sli4_async_cmstat_evt(struct lpfc_hba *phba);
+static void lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba);
static struct scsi_transport_template *lpfc_transport_template = NULL;
static struct scsi_transport_template *lpfc_vport_transport_template = NULL;
static DEFINE_IDR(lpfc_hba_index);
+#define LPFC_NVMET_BUF_POST 254
+static int lpfc_vmid_res_alloc(struct lpfc_hba *phba, struct lpfc_vport *vport);
+static void lpfc_cgn_update_tstamp(struct lpfc_hba *phba, struct lpfc_cgn_ts *ts);
/**
* lpfc_config_port_prep - Perform lpfc initialization prior to config port
@@ -132,7 +158,7 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0324 Config Port initialization "
"error, mbxCmd x%x READ_NVPARM, "
"mbxStatus x%x\n",
@@ -146,13 +172,17 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
sizeof(phba->wwpn));
}
- phba->sli3_options = 0x0;
+ /*
+ * Clear all option bits except LPFC_SLI3_BG_ENABLED,
+ * which was already set in lpfc_get_cfgparam()
+ */
+ phba->sli3_options &= (uint32_t)LPFC_SLI3_BG_ENABLED;
/* Setup and issue mailbox READ REV command */
lpfc_read_rev(phba, pmb);
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0439 Adapter failed to init, mbxCmd x%x "
"READ_REV, mbxStatus x%x\n",
mb->mbxCommand, mb->mbxStatus);
@@ -167,7 +197,7 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
*/
if (mb->un.varRdRev.rr == 0) {
vp->rev.rBit = 0;
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0440 Adapter failed to init, READ_REV has "
"missing revision information.\n");
mempool_free(pmb, phba->mbox_mem_pool);
@@ -228,6 +258,7 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
*/
if (mb->un.varDmp.word_cnt == 0)
break;
+
if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)
mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;
lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
@@ -235,6 +266,7 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
mb->un.varDmp.word_cnt);
offset += mb->un.varDmp.word_cnt;
} while (mb->un.varDmp.word_cnt && offset < DMP_VPD_SIZE);
+
lpfc_parse_vpd(phba, lpfc_vpd_data, offset);
kfree(lpfc_vpd_data);
@@ -294,14 +326,13 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
prog_id_word = pmboxq->u.mb.un.varWords[7];
/* Decode the Option rom version word to a readable string */
- if (prg->dist < 4)
- dist = dist_char[prg->dist];
+ dist = dist_char[prg->dist];
if ((prg->dist == 3) && (prg->num == 0))
- sprintf(phba->OptionROMVersion, "%d.%d%d",
+ snprintf(phba->OptionROMVersion, 32, "%d.%d%d",
prg->ver, prg->rev, prg->lev);
else
- sprintf(phba->OptionROMVersion, "%d.%d%d%c%d",
+ snprintf(phba->OptionROMVersion, 32, "%d.%d%d%c%d",
prg->ver, prg->rev, prg->lev,
dist, prg->num);
mempool_free(pmboxq, phba->mbox_mem_pool);
@@ -310,7 +341,6 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
/**
* lpfc_update_vport_wwn - Updates the fc_nodename, fc_portname,
- * cfg_soft_wwnn, cfg_soft_wwpn
* @vport: pointer to lpfc vport data structure.
*
*
@@ -320,31 +350,54 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
void
lpfc_update_vport_wwn(struct lpfc_vport *vport)
{
- /* If the soft name exists then update it using the service params */
- if (vport->phba->cfg_soft_wwnn)
- u64_to_wwn(vport->phba->cfg_soft_wwnn,
- vport->fc_sparam.nodeName.u.wwn);
- if (vport->phba->cfg_soft_wwpn)
- u64_to_wwn(vport->phba->cfg_soft_wwpn,
- vport->fc_sparam.portName.u.wwn);
+ struct lpfc_hba *phba = vport->phba;
/*
* If the name is empty or there exists a soft name
* then copy the service params name, otherwise use the fc name
*/
- if (vport->fc_nodename.u.wwn[0] == 0 || vport->phba->cfg_soft_wwnn)
+ if (vport->fc_nodename.u.wwn[0] == 0)
memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName,
sizeof(struct lpfc_name));
else
memcpy(&vport->fc_sparam.nodeName, &vport->fc_nodename,
sizeof(struct lpfc_name));
- if (vport->fc_portname.u.wwn[0] == 0 || vport->phba->cfg_soft_wwpn)
+ /*
+ * If the port name has changed, then set the Param changes flag
+ * to unreg the login
+ */
+ if (vport->fc_portname.u.wwn[0] != 0 &&
+ memcmp(&vport->fc_portname, &vport->fc_sparam.portName,
+ sizeof(struct lpfc_name))) {
+ vport->vport_flag |= FAWWPN_PARAM_CHG;
+
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ vport->port_type == LPFC_PHYSICAL_PORT &&
+ phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_FABRIC) {
+ if (!(phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_CONFIG))
+ phba->sli4_hba.fawwpn_flag &=
+ ~LPFC_FAWWPN_FABRIC;
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_SLI | LOG_DISCOVERY | LOG_ELS,
+ "2701 FA-PWWN change WWPN from %llx to "
+ "%llx: vflag x%x fawwpn_flag x%x\n",
+ wwn_to_u64(vport->fc_portname.u.wwn),
+ wwn_to_u64
+ (vport->fc_sparam.portName.u.wwn),
+ vport->vport_flag,
+ phba->sli4_hba.fawwpn_flag);
+ memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ }
+ }
+
+ if (vport->fc_portname.u.wwn[0] == 0)
memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
- sizeof(struct lpfc_name));
+ sizeof(struct lpfc_name));
else
memcpy(&vport->fc_sparam.portName, &vport->fc_portname,
- sizeof(struct lpfc_name));
+ sizeof(struct lpfc_name));
}
/**
@@ -398,24 +451,25 @@ lpfc_config_port_post(struct lpfc_hba *phba)
pmb->vport = vport;
if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0448 Adapter failed init, mbxCmd x%x "
"READ_SPARM mbxStatus x%x\n",
mb->mbxCommand, mb->mbxStatus);
phba->link_state = LPFC_HBA_ERROR;
- mp = (struct lpfc_dmabuf *) pmb->context1;
- mempool_free(pmb, phba->mbox_mem_pool);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
return -EIO;
}
- mp = (struct lpfc_dmabuf *) pmb->context1;
+ mp = pmb->ctx_buf;
+ /* This dmabuf was allocated by lpfc_read_sparam. The dmabuf is no
+ * longer needed. Prevent unintended ctx_buf access as the mbox is
+ * reused.
+ */
memcpy(&vport->fc_sparam, mp->virt, sizeof (struct serv_parm));
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
- pmb->context1 = NULL;
+ pmb->ctx_buf = NULL;
lpfc_update_vport_wwn(vport);
/* Update the fc_host data structures with new wwn. */
@@ -452,7 +506,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
lpfc_read_config(phba, pmb);
pmb->vport = vport;
if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0453 Adapter failed to init, mbxCmd x%x "
"READ_CONFIG, mbxStatus x%x\n",
mb->mbxCommand, mb->mbxStatus);
@@ -465,46 +519,26 @@ lpfc_config_port_post(struct lpfc_hba *phba)
lpfc_sli_read_link_ste(phba);
/* Reset the DFT_HBA_Q_DEPTH to the max xri */
- if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1))
- phba->cfg_hba_queue_depth =
- (mb->un.varRdConfig.max_xri + 1) -
- lpfc_sli4_get_els_iocb_cnt(phba);
+ if (phba->cfg_hba_queue_depth > mb->un.varRdConfig.max_xri) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "3359 HBA queue depth changed from %d to %d\n",
+ phba->cfg_hba_queue_depth,
+ mb->un.varRdConfig.max_xri);
+ phba->cfg_hba_queue_depth = mb->un.varRdConfig.max_xri;
+ }
phba->lmt = mb->un.varRdConfig.lmt;
/* Get the default values for Model Name and Description */
lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
- if ((phba->cfg_link_speed > LPFC_USER_LINK_SPEED_16G)
- || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_1G)
- && !(phba->lmt & LMT_1Gb))
- || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_2G)
- && !(phba->lmt & LMT_2Gb))
- || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_4G)
- && !(phba->lmt & LMT_4Gb))
- || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_8G)
- && !(phba->lmt & LMT_8Gb))
- || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_10G)
- && !(phba->lmt & LMT_10Gb))
- || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G)
- && !(phba->lmt & LMT_16Gb))) {
- /* Reset link speed to auto */
- lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
- "1302 Invalid speed for this board: "
- "Reset link speed to auto: x%x\n",
- phba->cfg_link_speed);
- phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO;
- }
-
phba->link_state = LPFC_LINK_DOWN;
/* Only process IOCBs on ELS ring till hba_state is READY */
- if (psli->ring[psli->extra_ring].cmdringaddr)
- psli->ring[psli->extra_ring].flag |= LPFC_STOP_IOCB_EVENT;
- if (psli->ring[psli->fcp_ring].cmdringaddr)
- psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT;
- if (psli->ring[psli->next_ring].cmdringaddr)
- psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT;
+ if (psli->sli3_ring[LPFC_EXTRA_RING].sli.sli3.cmdringaddr)
+ psli->sli3_ring[LPFC_EXTRA_RING].flag |= LPFC_STOP_IOCB_EVENT;
+ if (psli->sli3_ring[LPFC_FCP_RING].sli.sli3.cmdringaddr)
+ psli->sli3_ring[LPFC_FCP_RING].flag |= LPFC_STOP_IOCB_EVENT;
/* Post receive buffers for desired rings */
if (phba->sli_rev != 3)
@@ -521,7 +555,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
}
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0352 Config MSI mailbox command "
"failed, mbxCmd x%x, mbxStatus x%x\n",
pmb->u.mb.mbxCommand,
@@ -533,7 +567,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
/* Initialize ERATT handling flag */
- phba->hba_flag &= ~HBA_ERATT_HANDLED;
+ clear_bit(HBA_ERATT_HANDLED, &phba->hba_flag);
/* Enable appropriate host interrupts */
if (lpfc_readl(phba->HCregaddr, &status)) {
@@ -560,53 +594,37 @@ lpfc_config_port_post(struct lpfc_hba *phba)
/* Set up ring-0 (ELS) timer */
timeout = phba->fc_ratov * 2;
- mod_timer(&vport->els_tmofunc, jiffies + HZ * timeout);
+ mod_timer(&vport->els_tmofunc,
+ jiffies + secs_to_jiffies(timeout));
/* Set up heart beat (HB) timer */
- mod_timer(&phba->hb_tmofunc, jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
- phba->hb_outstanding = 0;
+ mod_timer(&phba->hb_tmofunc,
+ jiffies + secs_to_jiffies(LPFC_HB_MBOX_INTERVAL));
+ clear_bit(HBA_HBEAT_INP, &phba->hba_flag);
+ clear_bit(HBA_HBEAT_TMO, &phba->hba_flag);
phba->last_completion_time = jiffies;
/* Set up error attention (ERATT) polling timer */
- mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
+ mod_timer(&phba->eratt_poll,
+ jiffies + secs_to_jiffies(phba->eratt_poll_interval));
- if (phba->hba_flag & LINK_DISABLED) {
- lpfc_printf_log(phba,
- KERN_ERR, LOG_INIT,
- "2598 Adapter Link is disabled.\n");
+ if (test_bit(LINK_DISABLED, &phba->hba_flag)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2598 Adapter Link is disabled.\n");
lpfc_down_link(phba, pmb);
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
- lpfc_printf_log(phba,
- KERN_ERR, LOG_INIT,
- "2599 Adapter failed to issue DOWN_LINK"
- " mbox command rc 0x%x\n", rc);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2599 Adapter failed to issue DOWN_LINK"
+ " mbox command rc 0x%x\n", rc);
mempool_free(pmb, phba->mbox_mem_pool);
return -EIO;
}
} else if (phba->cfg_suppress_link_up == LPFC_INITIALIZE_LINK) {
- lpfc_init_link(phba, pmb, phba->cfg_topology,
- phba->cfg_link_speed);
- pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- lpfc_set_loopback_flag(phba);
- rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
- if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0454 Adapter failed to init, mbxCmd x%x "
- "INIT_LINK, mbxStatus x%x\n",
- mb->mbxCommand, mb->mbxStatus);
-
- /* Clear all interrupt enable conditions */
- writel(0, phba->HCregaddr);
- readl(phba->HCregaddr); /* flush */
- /* Clear all pending interrupts */
- writel(0xffffffff, phba->HAregaddr);
- readl(phba->HAregaddr); /* flush */
- phba->link_state = LPFC_HBA_ERROR;
- if (rc != MBX_BUSY)
- mempool_free(pmb, phba->mbox_mem_pool);
- return -EIO;
- }
+ mempool_free(pmb, phba->mbox_mem_pool);
+ rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
+ if (rc)
+ return rc;
}
/* MBOX buffer will be freed in mbox compl */
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
@@ -621,9 +639,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
- lpfc_printf_log(phba,
- KERN_ERR,
- LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0456 Adapter failed to issue "
"ASYNCEVT_ENABLE mbox status x%x\n",
rc);
@@ -643,7 +659,8 @@ lpfc_config_port_post(struct lpfc_hba *phba)
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0435 Adapter failed "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0435 Adapter failed "
"to get Option ROM version status x%x\n", rc);
mempool_free(pmb, phba->mbox_mem_pool);
}
@@ -652,6 +669,58 @@ lpfc_config_port_post(struct lpfc_hba *phba)
}
/**
+ * lpfc_sli4_refresh_params - update driver copy of params.
+ * @phba: Pointer to HBA context object.
+ *
+ * This is called to refresh driver copy of dynamic fields from the
+ * common_get_sli4_parameters descriptor.
+ **/
+int
+lpfc_sli4_refresh_params(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mqe *mqe;
+ struct lpfc_sli4_parameters *mbx_sli4_parameters;
+ int length, rc;
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+
+ mqe = &mboxq->u.mqe;
+ /* Read the port's SLI4 Config Parameters */
+ length = (sizeof(struct lpfc_mbx_get_sli4_parameters) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS,
+ length, LPFC_SLI4_MBX_EMBED);
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (unlikely(rc)) {
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return rc;
+ }
+ mbx_sli4_parameters = &mqe->un.get_sli4_parameters.sli4_parameters;
+ phba->sli4_hba.pc_sli4_params.mi_cap =
+ bf_get(cfg_mi_ver, mbx_sli4_parameters);
+
+ /* Are we forcing MI off via module parameter? */
+ if (phba->cfg_enable_mi)
+ phba->sli4_hba.pc_sli4_params.mi_ver =
+ bf_get(cfg_mi_ver, mbx_sli4_parameters);
+ else
+ phba->sli4_hba.pc_sli4_params.mi_ver = 0;
+
+ phba->sli4_hba.pc_sli4_params.cmf =
+ bf_get(cfg_cmf, mbx_sli4_parameters);
+ phba->sli4_hba.pc_sli4_params.pls =
+ bf_get(cfg_pvl, mbx_sli4_parameters);
+
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return rc;
+}
+
+/**
* lpfc_hba_init_link - Initialize the FC link
* @phba: pointer to lpfc hba data structure.
* @flag: mailbox command issue mode - either MBX_POLL or MBX_NOWAIT
@@ -665,9 +734,31 @@ lpfc_config_port_post(struct lpfc_hba *phba)
* 0 - success
* Any other value - error
**/
-int
+static int
lpfc_hba_init_link(struct lpfc_hba *phba, uint32_t flag)
{
+ return lpfc_hba_init_link_fc_topology(phba, phba->cfg_topology, flag);
+}
+
+/**
+ * lpfc_hba_init_link_fc_topology - Initialize FC link with desired topology
+ * @phba: pointer to lpfc hba data structure.
+ * @fc_topology: desired fc topology.
+ * @flag: mailbox command issue mode - either MBX_POLL or MBX_NOWAIT
+ *
+ * This routine will issue the INIT_LINK mailbox command call.
+ * It is available to other drivers through the lpfc_hba data
+ * structure for use as a delayed link up mechanism with the
+ * module parameter lpfc_suppress_link_up.
+ *
+ * Return code
+ * 0 - success
+ * Any other value - error
+ **/
+int
+lpfc_hba_init_link_fc_topology(struct lpfc_hba *phba, uint32_t fc_topology,
+ uint32_t flag)
+{
struct lpfc_vport *vport = phba->pport;
LPFC_MBOXQ_t *pmb;
MAILBOX_t *mb;
@@ -681,15 +772,40 @@ lpfc_hba_init_link(struct lpfc_hba *phba, uint32_t flag)
mb = &pmb->u.mb;
pmb->vport = vport;
- lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed);
+ if ((phba->cfg_link_speed > LPFC_USER_LINK_SPEED_MAX) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_1G) &&
+ !(phba->lmt & LMT_1Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_2G) &&
+ !(phba->lmt & LMT_2Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_4G) &&
+ !(phba->lmt & LMT_4Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_8G) &&
+ !(phba->lmt & LMT_8Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_10G) &&
+ !(phba->lmt & LMT_10Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G) &&
+ !(phba->lmt & LMT_16Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_32G) &&
+ !(phba->lmt & LMT_32Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_64G) &&
+ !(phba->lmt & LMT_64Gb))) {
+ /* Reset link speed to auto */
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "1302 Invalid speed for this board:%d "
+ "Reset link speed to auto.\n",
+ phba->cfg_link_speed);
+ phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO;
+ }
+ lpfc_init_link(phba, pmb, fc_topology, phba->cfg_link_speed);
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- lpfc_set_loopback_flag(phba);
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ lpfc_set_loopback_flag(phba);
rc = lpfc_sli_issue_mbox(phba, pmb, flag);
if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0498 Adapter failed to init, mbxCmd x%x "
- "INIT_LINK, mbxStatus x%x\n",
- mb->mbxCommand, mb->mbxStatus);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0498 Adapter failed to init, mbxCmd x%x "
+ "INIT_LINK, mbxStatus x%x\n",
+ mb->mbxCommand, mb->mbxStatus);
if (phba->sli_rev <= LPFC_SLI_REV3) {
/* Clear all interrupt enable conditions */
writel(0, phba->HCregaddr);
@@ -723,7 +839,7 @@ lpfc_hba_init_link(struct lpfc_hba *phba, uint32_t flag)
* 0 - success
* Any other value - error
**/
-int
+static int
lpfc_hba_down_link(struct lpfc_hba *phba, uint32_t flag)
{
LPFC_MBOXQ_t *pmb;
@@ -735,17 +851,15 @@ lpfc_hba_down_link(struct lpfc_hba *phba, uint32_t flag)
return -ENOMEM;
}
- lpfc_printf_log(phba,
- KERN_ERR, LOG_INIT,
- "0491 Adapter Link is disabled.\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0491 Adapter Link is disabled.\n");
lpfc_down_link(phba, pmb);
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
rc = lpfc_sli_issue_mbox(phba, pmb, flag);
if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
- lpfc_printf_log(phba,
- KERN_ERR, LOG_INIT,
- "2522 Adapter failed to issue DOWN_LINK"
- " mbox command rc 0x%x\n", rc);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2522 Adapter failed to issue DOWN_LINK"
+ " mbox command rc 0x%x\n", rc);
mempool_free(pmb, phba->mbox_mem_pool);
return -EIO;
@@ -779,7 +893,7 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
readl(phba->HCregaddr); /* flush */
}
- if (phba->pport->load_flag & FC_UNLOADING)
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
lpfc_cleanup_discovery_resources(phba->pport);
else {
vports = lpfc_create_vport_work_array(phba);
@@ -793,58 +907,165 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
}
/**
- * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset
+ * lpfc_sli4_free_sp_events - Cleanup sp_queue_events to free
+ * rspiocb which got deferred
+ *
* @phba: pointer to lpfc HBA data structure.
*
- * This routine will do uninitialization after the HBA is reset when bring
- * down the SLI Layer.
+ * This routine will cleanup completed slow path events after HBA is reset
+ * when bringing down the SLI Layer.
+ *
*
* Return codes
- * 0 - success.
- * Any other value - error.
+ * void.
**/
-static int
-lpfc_hba_down_post_s3(struct lpfc_hba *phba)
+static void
+lpfc_sli4_free_sp_events(struct lpfc_hba *phba)
+{
+ struct lpfc_iocbq *rspiocbq;
+ struct hbq_dmabuf *dmabuf;
+ struct lpfc_cq_event *cq_event;
+
+ clear_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag);
+
+ while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
+ /* Get the response iocb from the head of work queue */
+ spin_lock_irq(&phba->hbalock);
+ list_remove_head(&phba->sli4_hba.sp_queue_event,
+ cq_event, struct lpfc_cq_event, list);
+ spin_unlock_irq(&phba->hbalock);
+
+ switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) {
+ case CQE_CODE_COMPL_WQE:
+ rspiocbq = container_of(cq_event, struct lpfc_iocbq,
+ cq_event);
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+ break;
+ case CQE_CODE_RECEIVE:
+ case CQE_CODE_RECEIVE_V1:
+ dmabuf = container_of(cq_event, struct hbq_dmabuf,
+ cq_event);
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ }
+ }
+}
+
+/**
+ * lpfc_hba_free_post_buf - Perform lpfc uninitialization after HBA reset
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will cleanup posted ELS buffers after the HBA is reset
+ * when bringing down the SLI Layer.
+ *
+ *
+ * Return codes
+ * void.
+ **/
+static void
+lpfc_hba_free_post_buf(struct lpfc_hba *phba)
{
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
struct lpfc_dmabuf *mp, *next_mp;
- LIST_HEAD(completions);
- int i;
+ LIST_HEAD(buflist);
+ int count;
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
lpfc_sli_hbqbuf_free_all(phba);
else {
/* Cleanup preposted buffers on the ELS ring */
- pring = &psli->ring[LPFC_ELS_RING];
- list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) {
+ pring = &psli->sli3_ring[LPFC_ELS_RING];
+ spin_lock_irq(&phba->hbalock);
+ list_splice_init(&pring->postbufq, &buflist);
+ spin_unlock_irq(&phba->hbalock);
+
+ count = 0;
+ list_for_each_entry_safe(mp, next_mp, &buflist, list) {
list_del(&mp->list);
- pring->postbufq_cnt--;
+ count++;
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
}
+
+ spin_lock_irq(&phba->hbalock);
+ pring->postbufq_cnt -= count;
+ spin_unlock_irq(&phba->hbalock);
}
+}
- spin_lock_irq(&phba->hbalock);
- for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
+/**
+ * lpfc_hba_clean_txcmplq - Perform lpfc uninitialization after HBA reset
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will cleanup the txcmplq after the HBA is reset when bringing
+ * down the SLI Layer.
+ *
+ * Return codes
+ * void
+ **/
+static void
+lpfc_hba_clean_txcmplq(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_queue *qp = NULL;
+ struct lpfc_sli_ring *pring;
+ LIST_HEAD(completions);
+ int i;
+ struct lpfc_iocbq *piocb, *next_iocb;
- /* At this point in time the HBA is either reset or DOA. Either
- * way, nothing should be on txcmplq as it will NEVER complete.
- */
- list_splice_init(&pring->txcmplq, &completions);
- pring->txcmplq_cnt = 0;
- spin_unlock_irq(&phba->hbalock);
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->sli3_ring[i];
+ spin_lock_irq(&phba->hbalock);
+ /* At this point in time the HBA is either reset or DOA
+ * Nothing should be on txcmplq as it will
+ * NEVER complete.
+ */
+ list_splice_init(&pring->txcmplq, &completions);
+ pring->txcmplq_cnt = 0;
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_sli_abort_iocb_ring(phba, pring);
+ }
/* Cancel all the IOCBs from the completions list */
- lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
- IOERR_SLI_ABORTED);
-
+ lpfc_sli_cancel_iocbs(phba, &completions,
+ IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED);
+ return;
+ }
+ list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
+ pring = qp->pring;
+ if (!pring)
+ continue;
+ spin_lock_irq(&pring->ring_lock);
+ list_for_each_entry_safe(piocb, next_iocb,
+ &pring->txcmplq, list)
+ piocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ;
+ list_splice_init(&pring->txcmplq, &completions);
+ pring->txcmplq_cnt = 0;
+ spin_unlock_irq(&pring->ring_lock);
lpfc_sli_abort_iocb_ring(phba, pring);
- spin_lock_irq(&phba->hbalock);
}
- spin_unlock_irq(&phba->hbalock);
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &completions,
+ IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED);
+}
+/**
+ * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will do uninitialization after the HBA is reset when bring
+ * down the SLI Layer.
+ *
+ * Return codes
+ * 0 - success.
+ * Any other value - error.
+ **/
+static int
+lpfc_hba_down_post_s3(struct lpfc_hba *phba)
+{
+ lpfc_hba_free_post_buf(phba);
+ lpfc_hba_clean_txcmplq(phba);
return 0;
}
@@ -862,51 +1083,81 @@ lpfc_hba_down_post_s3(struct lpfc_hba *phba)
static int
lpfc_hba_down_post_s4(struct lpfc_hba *phba)
{
- struct lpfc_scsi_buf *psb, *psb_next;
+ struct lpfc_io_buf *psb, *psb_next;
+ struct lpfc_async_xchg_ctx *ctxp, *ctxp_next;
+ struct lpfc_sli4_hdw_queue *qp;
LIST_HEAD(aborts);
- int ret;
- unsigned long iflag = 0;
+ LIST_HEAD(nvme_aborts);
+ LIST_HEAD(nvmet_aborts);
struct lpfc_sglq *sglq_entry = NULL;
+ int cnt, idx;
+
+
+ lpfc_sli_hbqbuf_free_all(phba);
+ lpfc_hba_clean_txcmplq(phba);
- ret = lpfc_hba_down_post_s3(phba);
- if (ret)
- return ret;
/* At this point in time the HBA is either reset or DOA. Either
* way, nothing should be on lpfc_abts_els_sgl_list, it needs to be
- * on the lpfc_sgl_list so that it can either be freed if the
+ * on the lpfc_els_sgl_list so that it can either be freed if the
* driver is unloading or reposted if the driver is restarting
* the port.
*/
- spin_lock_irq(&phba->hbalock); /* required for lpfc_sgl_list and */
- /* scsl_buf_list */
- /* abts_sgl_list_lock required because worker thread uses this
+
+ /* sgl_list_lock required because worker thread uses this
* list.
*/
- spin_lock(&phba->sli4_hba.abts_sgl_list_lock);
+ spin_lock_irq(&phba->sli4_hba.sgl_list_lock);
list_for_each_entry(sglq_entry,
&phba->sli4_hba.lpfc_abts_els_sgl_list, list)
sglq_entry->state = SGL_FREED;
list_splice_init(&phba->sli4_hba.lpfc_abts_els_sgl_list,
- &phba->sli4_hba.lpfc_sgl_list);
- spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
- /* abts_scsi_buf_list_lock required because worker thread uses this
+ &phba->sli4_hba.lpfc_els_sgl_list);
+
+
+ spin_unlock_irq(&phba->sli4_hba.sgl_list_lock);
+
+ /* abts_xxxx_buf_list_lock required because worker thread uses this
* list.
*/
- spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
- list_splice_init(&phba->sli4_hba.lpfc_abts_scsi_buf_list,
- &aborts);
- spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+ spin_lock_irq(&phba->hbalock);
+ cnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+
+ spin_lock(&qp->abts_io_buf_list_lock);
+ list_splice_init(&qp->lpfc_abts_io_buf_list,
+ &aborts);
+
+ list_for_each_entry_safe(psb, psb_next, &aborts, list) {
+ psb->pCmd = NULL;
+ psb->status = IOSTAT_SUCCESS;
+ cnt++;
+ }
+ spin_lock(&qp->io_buf_list_put_lock);
+ list_splice_init(&aborts, &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs += qp->abts_scsi_io_bufs;
+ qp->put_io_bufs += qp->abts_nvme_io_bufs;
+ qp->abts_scsi_io_bufs = 0;
+ qp->abts_nvme_io_bufs = 0;
+ spin_unlock(&qp->io_buf_list_put_lock);
+ spin_unlock(&qp->abts_io_buf_list_lock);
+ }
spin_unlock_irq(&phba->hbalock);
- list_for_each_entry_safe(psb, psb_next, &aborts, list) {
- psb->pCmd = NULL;
- psb->status = IOSTAT_SUCCESS;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ spin_lock_irq(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
+ &nvmet_aborts);
+ spin_unlock_irq(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_for_each_entry_safe(ctxp, ctxp_next, &nvmet_aborts, list) {
+ ctxp->flag &= ~(LPFC_NVME_XBUSY | LPFC_NVME_ABORT_OP);
+ lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
+ }
}
- spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
- list_splice(&aborts, &phba->lpfc_scsi_buf_list);
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
- return 0;
+
+ lpfc_sli4_free_sp_events(phba);
+ return cnt;
}
/**
@@ -928,7 +1179,7 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
/**
* lpfc_hb_timeout - The HBA-timer timeout handler
- * @ptr: unsigned long holds the pointer to lpfc hba data structure.
+ * @t: timer context used to obtain the pointer to lpfc hba data structure.
*
* This is the HBA-timer timeout handler registered to the lpfc driver. When
* this timer fires, a HBA timeout event shall be posted to the lpfc driver
@@ -939,13 +1190,13 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
* be cleared by the worker thread after it has taken the event bitmap out.
**/
static void
-lpfc_hb_timeout(unsigned long ptr)
+lpfc_hb_timeout(struct timer_list *t)
{
struct lpfc_hba *phba;
uint32_t tmo_posted;
unsigned long iflag;
- phba = (struct lpfc_hba *)ptr;
+ phba = timer_container_of(phba, t, hb_tmofunc);
/* Check for heart beat timeout conditions */
spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
@@ -962,7 +1213,7 @@ lpfc_hb_timeout(unsigned long ptr)
/**
* lpfc_rrq_timeout - The RRQ-timer timeout handler
- * @ptr: unsigned long holds the pointer to lpfc hba data structure.
+ * @t: timer context used to obtain the pointer to lpfc hba data structure.
*
* This is the RRQ-timer timeout handler registered to the lpfc driver. When
* this timer fires, a RRQ timeout event shall be posted to the lpfc driver
@@ -973,15 +1224,17 @@ lpfc_hb_timeout(unsigned long ptr)
* be cleared by the worker thread after it has taken the event bitmap out.
**/
static void
-lpfc_rrq_timeout(unsigned long ptr)
+lpfc_rrq_timeout(struct timer_list *t)
{
struct lpfc_hba *phba;
- unsigned long iflag;
- phba = (struct lpfc_hba *)ptr;
- spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
- phba->hba_flag |= HBA_RRQ_ACTIVE;
- spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
+ phba = timer_container_of(phba, t, rrq_tmr);
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
+ clear_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
+ return;
+ }
+
+ set_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
lpfc_worker_wake_up(phba);
}
@@ -1004,22 +1257,238 @@ lpfc_rrq_timeout(unsigned long ptr)
static void
lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
{
- unsigned long drvr_flag;
+ clear_bit(HBA_HBEAT_INP, &phba->hba_flag);
+ clear_bit(HBA_HBEAT_TMO, &phba->hba_flag);
- spin_lock_irqsave(&phba->hbalock, drvr_flag);
- phba->hb_outstanding = 0;
- spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
-
- /* Check and reset heart-beat timer is necessary */
+ /* Check and reset heart-beat timer if necessary */
mempool_free(pmboxq, phba->mbox_mem_pool);
- if (!(phba->pport->fc_flag & FC_OFFLINE_MODE) &&
- !(phba->link_state == LPFC_HBA_ERROR) &&
- !(phba->pport->load_flag & FC_UNLOADING))
+ if (!test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag) &&
+ !(phba->link_state == LPFC_HBA_ERROR) &&
+ !test_bit(FC_UNLOADING, &phba->pport->load_flag))
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
+ jiffies +
+ secs_to_jiffies(LPFC_HB_MBOX_INTERVAL));
return;
}
+/*
+ * lpfc_idle_stat_delay_work - idle_stat tracking
+ *
+ * This routine tracks per-eq idle_stat and determines polling decisions.
+ *
+ * Return codes:
+ * None
+ **/
+static void
+lpfc_idle_stat_delay_work(struct work_struct *work)
+{
+ struct lpfc_hba *phba = container_of(to_delayed_work(work),
+ struct lpfc_hba,
+ idle_stat_delay_work);
+ struct lpfc_queue *eq;
+ struct lpfc_sli4_hdw_queue *hdwq;
+ struct lpfc_idle_stat *idle_stat;
+ u32 i, idle_percent;
+ u64 wall, wall_idle, diff_wall, diff_idle, busy_time;
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return;
+
+ if (phba->link_state == LPFC_HBA_ERROR ||
+ test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag) ||
+ phba->cmf_active_mode != LPFC_CFG_OFF)
+ goto requeue;
+
+ for_each_present_cpu(i) {
+ hdwq = &phba->sli4_hba.hdwq[phba->sli4_hba.cpu_map[i].hdwq];
+ eq = hdwq->hba_eq;
+
+ /* Skip if we've already handled this eq's primary CPU */
+ if (eq->chann != i)
+ continue;
+
+ idle_stat = &phba->sli4_hba.idle_stat[i];
+
+ /* get_cpu_idle_time returns values as running counters. Thus,
+ * to know the amount for this period, the prior counter values
+ * need to be subtracted from the current counter values.
+ * From there, the idle time stat can be calculated as a
+ * percentage of 100 - the sum of the other consumption times.
+ */
+ wall_idle = get_cpu_idle_time(i, &wall, 1);
+ diff_idle = wall_idle - idle_stat->prev_idle;
+ diff_wall = wall - idle_stat->prev_wall;
+
+ if (diff_wall <= diff_idle)
+ busy_time = 0;
+ else
+ busy_time = diff_wall - diff_idle;
+
+ idle_percent = div64_u64(100 * busy_time, diff_wall);
+ idle_percent = 100 - idle_percent;
+
+ if (idle_percent < 15)
+ eq->poll_mode = LPFC_QUEUE_WORK;
+ else
+ eq->poll_mode = LPFC_THREADED_IRQ;
+
+ idle_stat->prev_idle = wall_idle;
+ idle_stat->prev_wall = wall;
+ }
+
+requeue:
+ schedule_delayed_work(&phba->idle_stat_delay_work,
+ msecs_to_jiffies(LPFC_IDLE_STAT_DELAY));
+}
+
+static void
+lpfc_hb_eq_delay_work(struct work_struct *work)
+{
+ struct lpfc_hba *phba = container_of(to_delayed_work(work),
+ struct lpfc_hba, eq_delay_work);
+ struct lpfc_eq_intr_info *eqi, *eqi_new;
+ struct lpfc_queue *eq, *eq_next;
+ unsigned char *ena_delay = NULL;
+ uint32_t usdelay;
+ int i;
+
+ if (!phba->cfg_auto_imax ||
+ test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return;
+
+ if (phba->link_state == LPFC_HBA_ERROR ||
+ test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
+ goto requeue;
+
+ ena_delay = kcalloc(phba->sli4_hba.num_possible_cpu, sizeof(*ena_delay),
+ GFP_KERNEL);
+ if (!ena_delay)
+ goto requeue;
+
+ for (i = 0; i < phba->cfg_irq_chann; i++) {
+ /* Get the EQ corresponding to the IRQ vector */
+ eq = phba->sli4_hba.hba_eq_hdl[i].eq;
+ if (!eq)
+ continue;
+ if (eq->q_mode || eq->q_flag & HBA_EQ_DELAY_CHK) {
+ eq->q_flag &= ~HBA_EQ_DELAY_CHK;
+ ena_delay[eq->last_cpu] = 1;
+ }
+ }
+
+ for_each_present_cpu(i) {
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, i);
+ if (ena_delay[i]) {
+ usdelay = (eqi->icnt >> 10) * LPFC_EQ_DELAY_STEP;
+ if (usdelay > LPFC_MAX_AUTO_EQ_DELAY)
+ usdelay = LPFC_MAX_AUTO_EQ_DELAY;
+ } else {
+ usdelay = 0;
+ }
+
+ eqi->icnt = 0;
+
+ list_for_each_entry_safe(eq, eq_next, &eqi->list, cpu_list) {
+ if (unlikely(eq->last_cpu != i)) {
+ eqi_new = per_cpu_ptr(phba->sli4_hba.eq_info,
+ eq->last_cpu);
+ list_move_tail(&eq->cpu_list, &eqi_new->list);
+ continue;
+ }
+ if (usdelay != eq->q_mode)
+ lpfc_modify_hba_eq_delay(phba, eq->hdwq, 1,
+ usdelay);
+ }
+ }
+
+ kfree(ena_delay);
+
+requeue:
+ queue_delayed_work(phba->wq, &phba->eq_delay_work,
+ msecs_to_jiffies(LPFC_EQ_DELAY_MSECS));
+}
+
+/**
+ * lpfc_hb_mxp_handler - Multi-XRI pools handler to adjust XRI distribution
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * For each heartbeat, this routine does some heuristic methods to adjust
+ * XRI distribution. The goal is to fully utilize free XRIs.
+ **/
+static void lpfc_hb_mxp_handler(struct lpfc_hba *phba)
+{
+ u32 i;
+ u32 hwq_count;
+
+ hwq_count = phba->cfg_hdw_queue;
+ for (i = 0; i < hwq_count; i++) {
+ /* Adjust XRIs in private pool */
+ lpfc_adjust_pvt_pool_count(phba, i);
+
+ /* Adjust high watermark */
+ lpfc_adjust_high_watermark(phba, i);
+
+#ifdef LPFC_MXP_STAT
+ /* Snapshot pbl, pvt and busy count */
+ lpfc_snapshot_mxp(phba, i);
+#endif
+ }
+}
+
+/**
+ * lpfc_issue_hb_mbox - Issues heart-beat mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * If a HB mbox is not already in progrees, this routine will allocate
+ * a LPFC_MBOXQ_t, populate it with a MBX_HEARTBEAT (0x31) command,
+ * and issue it. The HBA_HBEAT_INP flag means the command is in progress.
+ **/
+int
+lpfc_issue_hb_mbox(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *pmboxq;
+ int retval;
+
+ /* Is a Heartbeat mbox already in progress */
+ if (test_bit(HBA_HBEAT_INP, &phba->hba_flag))
+ return 0;
+
+ pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmboxq)
+ return -ENOMEM;
+
+ lpfc_heart_beat(phba, pmboxq);
+ pmboxq->mbox_cmpl = lpfc_hb_mbox_cmpl;
+ pmboxq->vport = phba->pport;
+ retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
+
+ if (retval != MBX_BUSY && retval != MBX_SUCCESS) {
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return -ENXIO;
+ }
+ set_bit(HBA_HBEAT_INP, &phba->hba_flag);
+
+ return 0;
+}
+
+/**
+ * lpfc_issue_hb_tmo - Signals heartbeat timer to issue mbox command
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * The heartbeat timer (every 5 sec) will fire. If the HBA_HBEAT_TMO
+ * flag is set, it will force a MBX_HEARTBEAT mbox command, regardless
+ * of the value of lpfc_enable_hba_heartbeat.
+ * If lpfc_enable_hba_heartbeat is set, the timeout routine will always
+ * try to issue a MBX_HEARTBEAT mbox command.
+ **/
+void
+lpfc_issue_hb_tmo(struct lpfc_hba *phba)
+{
+ if (phba->cfg_enable_hba_heartbeat)
+ return;
+ set_bit(HBA_HBEAT_TMO, &phba->hba_flag);
+}
+
/**
* lpfc_hb_timeout_handler - The HBA-timer timeout handler
* @phba: pointer to lpfc hba data structure.
@@ -1040,37 +1509,29 @@ void
lpfc_hb_timeout_handler(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
- LPFC_MBOXQ_t *pmboxq;
struct lpfc_dmabuf *buf_ptr;
- int retval, i;
+ int retval = 0;
+ int i, tmo;
struct lpfc_sli *psli = &phba->sli;
LIST_HEAD(completions);
+ if (phba->cfg_xri_rebalancing) {
+ /* Multi-XRI pools handler */
+ lpfc_hb_mxp_handler(phba);
+ }
+
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
lpfc_rcv_seq_check_edtov(vports[i]);
+ lpfc_fdmi_change_check(vports[i]);
+ }
lpfc_destroy_vport_work_array(phba, vports);
- if ((phba->link_state == LPFC_HBA_ERROR) ||
- (phba->pport->load_flag & FC_UNLOADING) ||
- (phba->pport->fc_flag & FC_OFFLINE_MODE))
- return;
-
- spin_lock_irq(&phba->pport->work_port_lock);
-
- if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
- jiffies)) {
- spin_unlock_irq(&phba->pport->work_port_lock);
- if (!phba->hb_outstanding)
- mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
- else
- mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
+ if (phba->link_state == LPFC_HBA_ERROR ||
+ test_bit(FC_UNLOADING, &phba->pport->load_flag) ||
+ test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
return;
- }
- spin_unlock_irq(&phba->pport->work_port_lock);
if (phba->elsbuf_cnt &&
(phba->elsbuf_cnt == phba->elsbuf_prev_cnt)) {
@@ -1091,35 +1552,43 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
/* If there is no heart beat outstanding, issue a heartbeat command */
if (phba->cfg_enable_hba_heartbeat) {
- if (!phba->hb_outstanding) {
+ /* If IOs are completing, no need to issue a MBX_HEARTBEAT */
+ spin_lock_irq(&phba->pport->work_port_lock);
+ if (time_after(phba->last_completion_time +
+ secs_to_jiffies(LPFC_HB_MBOX_INTERVAL),
+ jiffies)) {
+ spin_unlock_irq(&phba->pport->work_port_lock);
+ if (test_bit(HBA_HBEAT_INP, &phba->hba_flag))
+ tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
+ else
+ tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
+ goto out;
+ }
+ spin_unlock_irq(&phba->pport->work_port_lock);
+
+ /* Check if a MBX_HEARTBEAT is already in progress */
+ if (test_bit(HBA_HBEAT_INP, &phba->hba_flag)) {
+ /*
+ * If heart beat timeout called with HBA_HBEAT_INP set
+ * we need to give the hb mailbox cmd a chance to
+ * complete or TMO.
+ */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0459 Adapter heartbeat still outstanding: "
+ "last compl time was %d ms.\n",
+ jiffies_to_msecs(jiffies
+ - phba->last_completion_time));
+ tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
+ } else {
if ((!(psli->sli_flag & LPFC_SLI_MBOX_ACTIVE)) &&
(list_empty(&psli->mboxq))) {
- pmboxq = mempool_alloc(phba->mbox_mem_pool,
- GFP_KERNEL);
- if (!pmboxq) {
- mod_timer(&phba->hb_tmofunc,
- jiffies +
- HZ * LPFC_HB_MBOX_INTERVAL);
- return;
- }
- lpfc_heart_beat(phba, pmboxq);
- pmboxq->mbox_cmpl = lpfc_hb_mbox_cmpl;
- pmboxq->vport = phba->pport;
- retval = lpfc_sli_issue_mbox(phba, pmboxq,
- MBX_NOWAIT);
-
- if (retval != MBX_BUSY &&
- retval != MBX_SUCCESS) {
- mempool_free(pmboxq,
- phba->mbox_mem_pool);
- mod_timer(&phba->hb_tmofunc,
- jiffies +
- HZ * LPFC_HB_MBOX_INTERVAL);
- return;
+ retval = lpfc_issue_hb_mbox(phba);
+ if (retval) {
+ tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
+ goto out;
}
phba->skipped_hb = 0;
- phba->hb_outstanding = 1;
} else if (time_before_eq(phba->last_completion_time,
phba->skipped_hb)) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
@@ -1130,24 +1599,23 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
} else
phba->skipped_hb = jiffies;
- mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
- return;
- } else {
- /*
- * If heart beat timeout called with hb_outstanding set
- * we need to give the hb mailbox cmd a chance to
- * complete or TMO.
- */
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "0459 Adapter heartbeat still out"
- "standing:last compl time was %d ms.\n",
- jiffies_to_msecs(jiffies
- - phba->last_completion_time));
- mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
+ tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
+ goto out;
+ }
+ } else {
+ /* Check to see if we want to force a MBX_HEARTBEAT */
+ if (test_bit(HBA_HBEAT_TMO, &phba->hba_flag)) {
+ retval = lpfc_issue_hb_mbox(phba);
+ if (retval)
+ tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
+ else
+ tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
+ goto out;
}
+ tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
}
+out:
+ mod_timer(&phba->hb_tmofunc, jiffies + msecs_to_jiffies(tmo));
}
/**
@@ -1165,7 +1633,7 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
- lpfc_offline_prep(phba);
+ lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
lpfc_offline(phba);
lpfc_reset_barrier(phba);
@@ -1186,16 +1654,23 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
* This routine is called to bring a SLI4 HBA offline when HBA hardware error
* other than Port Error 6 has been detected.
**/
-static void
+void
lpfc_sli4_offline_eratt(struct lpfc_hba *phba)
{
- lpfc_offline_prep(phba);
+ spin_lock_irq(&phba->hbalock);
+ if (phba->link_state == LPFC_HBA_ERROR &&
+ test_bit(HBA_PCI_ERR, &phba->bit_flags)) {
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+ phba->link_state = LPFC_HBA_ERROR;
+ spin_unlock_irq(&phba->hbalock);
+
+ lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
+ lpfc_sli_flush_io_rings(phba);
lpfc_offline(phba);
- lpfc_sli4_brdreset(phba);
lpfc_hba_down_post(phba);
- lpfc_sli4_post_status_check(phba);
lpfc_unblock_mgmt_io(phba);
- phba->link_state = LPFC_HBA_ERROR;
}
/**
@@ -1211,24 +1686,21 @@ static void
lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
{
uint32_t old_host_status = phba->work_hs;
- struct lpfc_sli_ring *pring;
struct lpfc_sli *psli = &phba->sli;
/* If the pci channel is offline, ignore possible errors,
* since we cannot communicate with the pci card anyway.
*/
if (pci_channel_offline(phba->pcidev)) {
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~DEFER_ERATT;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(DEFER_ERATT, &phba->hba_flag);
return;
}
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0479 Deferred Adapter Hardware Error "
- "Data: x%x x%x x%x\n",
- phba->work_hs,
- phba->work_status[0], phba->work_status[1]);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0479 Deferred Adapter Hardware Error "
+ "Data: x%x x%x x%x\n",
+ phba->work_hs, phba->work_status[0],
+ phba->work_status[1]);
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_SLI_ACTIVE;
@@ -1240,14 +1712,13 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
* dropped by the firmware. Error iocb (I/O) on txcmplq and let the
* SCSI layer retry it after re-establishing link.
*/
- pring = &psli->ring[psli->fcp_ring];
- lpfc_sli_abort_iocb_ring(phba, pring);
+ lpfc_sli_abort_fcp_rings(phba);
/*
* There was a firmware error. Take the hba offline and then
* attempt to restart it.
*/
- lpfc_offline_prep(phba);
+ lpfc_offline_prep(phba, LPFC_MBX_WAIT);
lpfc_offline(phba);
/* Wait for the ER1 bit to clear.*/
@@ -1258,7 +1729,7 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
break;
}
/* If driver is unloading let the worker thread continue */
- if (phba->pport->load_flag & FC_UNLOADING) {
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
phba->work_hs = 0;
break;
}
@@ -1269,12 +1740,10 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
* first write to the host attention register clear the
* host status register.
*/
- if ((!phba->work_hs) && (!(phba->pport->load_flag & FC_UNLOADING)))
+ if (!phba->work_hs && !test_bit(FC_UNLOADING, &phba->pport->load_flag))
phba->work_hs = old_host_status & ~HS_FFER1;
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~DEFER_ERATT;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(DEFER_ERATT, &phba->hba_flag);
phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
}
@@ -1309,7 +1778,6 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
{
struct lpfc_vport *vport = phba->pport;
struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring;
uint32_t event_data;
unsigned long temperature;
struct temp_event temp_event_data;
@@ -1319,9 +1787,7 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
* since we cannot communicate with the pci card anyway.
*/
if (pci_channel_offline(phba->pcidev)) {
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~DEFER_ERATT;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(DEFER_ERATT, &phba->hba_flag);
return;
}
@@ -1332,7 +1798,7 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
/* Send an internal error event to mgmt application */
lpfc_board_errevt_to_mgmt(phba);
- if (phba->hba_flag & DEFER_ERATT)
+ if (test_bit(DEFER_ERATT, &phba->hba_flag))
lpfc_handle_deferred_eratt(phba);
if ((phba->work_hs & HS_FFER6) || (phba->work_hs & HS_FFER8)) {
@@ -1361,14 +1827,13 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
* Error iocb (I/O) on txcmplq and let the SCSI layer
* retry it after re-establishing link.
*/
- pring = &psli->ring[psli->fcp_ring];
- lpfc_sli_abort_iocb_ring(phba, pring);
+ lpfc_sli_abort_fcp_rings(phba);
/*
* There was a firmware error. Take the hba offline and then
* attempt to restart it.
*/
- lpfc_offline_prep(phba);
+ lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
lpfc_offline(phba);
lpfc_sli_brdrestart(phba);
if (lpfc_online(phba) == 0) { /* Initialize the HBA */
@@ -1382,7 +1847,7 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
temp_event_data.event_code = LPFC_CRIT_TEMP;
temp_event_data.data = (uint32_t)temperature;
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0406 Adapter maximum temperature exceeded "
"(%ld), taking this port offline "
"Data: x%x x%x x%x\n",
@@ -1406,7 +1871,7 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
* failure is a value other than FFER6. Do not call the offline
* twice. This is the adapter hardware error path.
*/
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0457 Adapter Hardware Error "
"Data: x%x x%x x%x\n",
phba->work_hs,
@@ -1424,6 +1889,89 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
}
/**
+ * lpfc_sli4_port_sta_fn_reset - The SLI4 function reset due to port status reg
+ * @phba: pointer to lpfc hba data structure.
+ * @mbx_action: flag for mailbox shutdown action.
+ * @en_rn_msg: send reset/port recovery message.
+ * This routine is invoked to perform an SLI4 port PCI function reset in
+ * response to port status register polling attention. It waits for port
+ * status register (ERR, RDY, RN) bits before proceeding with function reset.
+ * During this process, interrupt vectors are freed and later requested
+ * for handling possible port resource change.
+ **/
+static int
+lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action,
+ bool en_rn_msg)
+{
+ int rc;
+ uint32_t intr_mode;
+ LPFC_MBOXQ_t *mboxq;
+
+ /* Notifying the transport that the targets are going offline. */
+ lpfc_scsi_dev_block(phba);
+
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
+ LPFC_SLI_INTF_IF_TYPE_2) {
+ /*
+ * On error status condition, driver need to wait for port
+ * ready before performing reset.
+ */
+ rc = lpfc_sli4_pdev_status_reg_wait(phba);
+ if (rc)
+ return rc;
+ }
+
+ /* need reset: attempt for port recovery */
+ if (en_rn_msg)
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2887 Reset Needed: Attempting Port "
+ "Recovery...\n");
+
+ /* If we are no wait, the HBA has been reset and is not
+ * functional, thus we should clear
+ * (LPFC_SLI_ACTIVE | LPFC_SLI_MBOX_ACTIVE) flags.
+ */
+ if (mbx_action == LPFC_MBX_NO_WAIT) {
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE;
+ if (phba->sli.mbox_active) {
+ mboxq = phba->sli.mbox_active;
+ mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+ __lpfc_mbox_cmpl_put(phba, mboxq);
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = NULL;
+ }
+ spin_unlock_irq(&phba->hbalock);
+ }
+
+ lpfc_offline_prep(phba, mbx_action);
+ lpfc_sli_flush_io_rings(phba);
+ lpfc_nvmels_flush_cmd(phba);
+ lpfc_offline(phba);
+ /* release interrupt for possible resource change */
+ lpfc_sli4_disable_intr(phba);
+ rc = lpfc_sli_brdrestart(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6309 Failed to restart board\n");
+ return rc;
+ }
+ /* request and enable interrupt */
+ intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3175 Failed to enable interrupt\n");
+ return -EIO;
+ }
+ phba->intr_mode = intr_mode;
+ rc = lpfc_online(phba);
+ if (rc == 0)
+ lpfc_unblock_mgmt_io(phba);
+
+ return rc;
+}
+
+/**
* lpfc_handle_eratt_s4 - The SLI4 HBA hardware error handler
* @phba: pointer to lpfc hba data structure.
*
@@ -1437,66 +1985,178 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba)
uint32_t event_data;
struct Scsi_Host *shost;
uint32_t if_type;
- struct lpfc_register portstat_reg;
+ struct lpfc_register portstat_reg = {0};
+ uint32_t reg_err1, reg_err2;
+ uint32_t uerrlo_reg, uemasklo_reg;
+ uint32_t smphr_port_status = 0, pci_rd_rc1, pci_rd_rc2;
+ bool en_rn_msg = true;
+ struct temp_event temp_event_data;
+ struct lpfc_register portsmphr_reg;
+ int rc, i;
/* If the pci channel is offline, ignore possible errors, since
* we cannot communicate with the pci card anyway.
*/
- if (pci_channel_offline(phba->pcidev))
- return;
- /* If resets are disabled then leave the HBA alone and return */
- if (!phba->cfg_enable_hba_reset)
+ if (pci_channel_offline(phba->pcidev)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3166 pci channel is offline\n");
+ lpfc_sli_flush_io_rings(phba);
return;
+ }
- /* Send an internal error event to mgmt application */
- lpfc_board_errevt_to_mgmt(phba);
-
- /* For now, the actual action for SLI4 device handling is not
- * specified yet, just treated it as adaptor hardware failure
- */
- event_data = FC_REG_DUMP_EVENT;
- shost = lpfc_shost_from_vport(vport);
- fc_host_post_vendor_event(shost, fc_get_event_number(),
- sizeof(event_data), (char *) &event_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
-
+ memset(&portsmphr_reg, 0, sizeof(portsmphr_reg));
if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
switch (if_type) {
case LPFC_SLI_INTF_IF_TYPE_0:
- lpfc_sli4_offline_eratt(phba);
+ pci_rd_rc1 = lpfc_readl(
+ phba->sli4_hba.u.if_type0.UERRLOregaddr,
+ &uerrlo_reg);
+ pci_rd_rc2 = lpfc_readl(
+ phba->sli4_hba.u.if_type0.UEMASKLOregaddr,
+ &uemasklo_reg);
+ /* consider PCI bus read error as pci_channel_offline */
+ if (pci_rd_rc1 == -EIO && pci_rd_rc2 == -EIO)
+ return;
+ if (!test_bit(HBA_RECOVERABLE_UE, &phba->hba_flag)) {
+ lpfc_sli4_offline_eratt(phba);
+ return;
+ }
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "7623 Checking UE recoverable");
+
+ for (i = 0; i < phba->sli4_hba.ue_to_sr / 1000; i++) {
+ if (lpfc_readl(phba->sli4_hba.PSMPHRregaddr,
+ &portsmphr_reg.word0))
+ continue;
+
+ smphr_port_status = bf_get(lpfc_port_smphr_port_status,
+ &portsmphr_reg);
+ if ((smphr_port_status & LPFC_PORT_SEM_MASK) ==
+ LPFC_PORT_SEM_UE_RECOVERABLE)
+ break;
+ /*Sleep for 1Sec, before checking SEMAPHORE */
+ msleep(1000);
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "4827 smphr_port_status x%x : Waited %dSec",
+ smphr_port_status, i);
+
+ /* Recoverable UE, reset the HBA device */
+ if ((smphr_port_status & LPFC_PORT_SEM_MASK) ==
+ LPFC_PORT_SEM_UE_RECOVERABLE) {
+ for (i = 0; i < 20; i++) {
+ msleep(1000);
+ if (!lpfc_readl(phba->sli4_hba.PSMPHRregaddr,
+ &portsmphr_reg.word0) &&
+ (LPFC_POST_STAGE_PORT_READY ==
+ bf_get(lpfc_port_smphr_port_status,
+ &portsmphr_reg))) {
+ rc = lpfc_sli4_port_sta_fn_reset(phba,
+ LPFC_MBX_NO_WAIT, en_rn_msg);
+ if (rc == 0)
+ return;
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "4215 Failed to recover UE");
+ break;
+ }
+ }
+ }
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "7624 Firmware not ready: Failing UE recovery,"
+ " waited %dSec", i);
+ phba->link_state = LPFC_HBA_ERROR;
break;
- case LPFC_SLI_INTF_IF_TYPE_2:
- portstat_reg.word0 =
- readl(phba->sli4_hba.u.if_type2.STATUSregaddr);
+ case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ pci_rd_rc1 = lpfc_readl(
+ phba->sli4_hba.u.if_type2.STATUSregaddr,
+ &portstat_reg.word0);
+ /* consider PCI bus read error as pci_channel_offline */
+ if (pci_rd_rc1 == -EIO) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3151 PCI bus read access failure: x%x\n",
+ readl(phba->sli4_hba.u.if_type2.STATUSregaddr));
+ lpfc_sli4_offline_eratt(phba);
+ return;
+ }
+ reg_err1 = readl(phba->sli4_hba.u.if_type2.ERR1regaddr);
+ reg_err2 = readl(phba->sli4_hba.u.if_type2.ERR2regaddr);
if (bf_get(lpfc_sliport_status_oti, &portstat_reg)) {
- /* TODO: Register for Overtemp async events. */
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2889 Port Overtemperature event, "
- "taking port\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2889 Port Overtemperature event, "
+ "taking port offline Data: x%x x%x\n",
+ reg_err1, reg_err2);
+
+ phba->sfp_alarm |= LPFC_TRANSGRESSION_HIGH_TEMPERATURE;
+ temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
+ temp_event_data.event_code = LPFC_CRIT_TEMP;
+ temp_event_data.data = 0xFFFFFFFF;
+
+ shost = lpfc_shost_from_vport(phba->pport);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(temp_event_data),
+ (char *)&temp_event_data,
+ SCSI_NL_VID_TYPE_PCI
+ | PCI_VENDOR_ID_EMULEX);
+
spin_lock_irq(&phba->hbalock);
phba->over_temp_state = HBA_OVER_TEMP;
spin_unlock_irq(&phba->hbalock);
lpfc_sli4_offline_eratt(phba);
return;
}
- if (bf_get(lpfc_sliport_status_rn, &portstat_reg)) {
- /*
- * TODO: Attempt port recovery via a port reset.
- * When fully implemented, the driver should
- * attempt to recover the port here and return.
- * For now, log an error and take the port offline.
- */
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2887 Port Error: Attempting "
- "Port Recovery\n");
+ if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 &&
+ reg_err2 == SLIPORT_ERR2_REG_FW_RESTART) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3143 Port Down: Firmware Update "
+ "Detected\n");
+ en_rn_msg = false;
+ } else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 &&
+ reg_err2 == SLIPORT_ERR2_REG_FORCED_DUMP)
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3144 Port Down: Debug Dump\n");
+ else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 &&
+ reg_err2 == SLIPORT_ERR2_REG_FUNC_PROVISON)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3145 Port Down: Provisioning\n");
+
+ /* If resets are disabled then leave the HBA alone and return */
+ if (!phba->cfg_enable_hba_reset)
+ return;
+
+ /* Check port status register for function reset */
+ rc = lpfc_sli4_port_sta_fn_reset(phba, LPFC_MBX_NO_WAIT,
+ en_rn_msg);
+ if (rc == 0) {
+ /* don't report event on forced debug dump */
+ if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 &&
+ reg_err2 == SLIPORT_ERR2_REG_FORCED_DUMP)
+ return;
+ else
+ break;
}
+ /* fall through for not able to recover */
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3152 Unrecoverable error\n");
lpfc_sli4_offline_eratt(phba);
break;
case LPFC_SLI_INTF_IF_TYPE_1:
default:
break;
}
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "3123 Report dump event to upper layer\n");
+ /* Send an internal error event to mgmt application */
+ lpfc_board_errevt_to_mgmt(phba);
+
+ event_data = FC_REG_DUMP_EVENT;
+ shost = lpfc_shost_from_vport(vport);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(event_data), (char *) &event_data,
+ SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
}
/**
@@ -1521,7 +2181,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked from the worker thread to handle a HBA host
- * attention link event.
+ * attention link event. SLI3 only.
**/
void
lpfc_handle_latt(struct lpfc_hba *phba)
@@ -1530,7 +2190,6 @@ lpfc_handle_latt(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
LPFC_MBOXQ_t *pmb;
volatile uint32_t control;
- struct lpfc_dmabuf *mp;
int rc = 0;
pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
@@ -1539,27 +2198,21 @@ lpfc_handle_latt(struct lpfc_hba *phba)
goto lpfc_handle_latt_err_exit;
}
- mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!mp) {
+ rc = lpfc_mbox_rsrc_prep(phba, pmb);
+ if (rc) {
rc = 2;
- goto lpfc_handle_latt_free_pmb;
- }
-
- mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
- if (!mp->virt) {
- rc = 3;
- goto lpfc_handle_latt_free_mp;
+ mempool_free(pmb, phba->mbox_mem_pool);
+ goto lpfc_handle_latt_err_exit;
}
/* Cleanup any outstanding ELS commands */
lpfc_els_flush_all_cmd(phba);
-
psli->slistat.link_event++;
- lpfc_read_topology(phba, pmb, mp);
+ lpfc_read_topology(phba, pmb, pmb->ctx_buf);
pmb->mbox_cmpl = lpfc_mbx_cmpl_read_topology;
pmb->vport = vport;
/* Block ELS IOCBs until we have processed this mbox command */
- phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
+ phba->sli.sli3_ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
rc = lpfc_sli_issue_mbox (phba, pmb, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
rc = 4;
@@ -1575,12 +2228,8 @@ lpfc_handle_latt(struct lpfc_hba *phba)
return;
lpfc_handle_latt_free_mbuf:
- phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
-lpfc_handle_latt_free_mp:
- kfree(mp);
-lpfc_handle_latt_free_pmb:
- mempool_free(pmb, phba->mbox_mem_pool);
+ phba->sli.sli3_ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
lpfc_handle_latt_err_exit:
/* Enable Link attention interrupts */
spin_lock_irq(&phba->hbalock);
@@ -1597,12 +2246,107 @@ lpfc_handle_latt_err_exit:
lpfc_linkdown(phba);
phba->link_state = LPFC_HBA_ERROR;
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "0300 LATT: Cannot issue READ_LA: Data:%d\n", rc);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0300 LATT: Cannot issue READ_LA: Data:%d\n", rc);
return;
}
+static void
+lpfc_fill_vpd(struct lpfc_hba *phba, uint8_t *vpd, int length, int *pindex)
+{
+ int i, j;
+
+ while (length > 0) {
+ /* Look for Serial Number */
+ if ((vpd[*pindex] == 'S') && (vpd[*pindex + 1] == 'N')) {
+ *pindex += 2;
+ i = vpd[*pindex];
+ *pindex += 1;
+ j = 0;
+ length -= (3+i);
+ while (i--) {
+ phba->SerialNumber[j++] = vpd[(*pindex)++];
+ if (j == 31)
+ break;
+ }
+ phba->SerialNumber[j] = 0;
+ continue;
+ } else if ((vpd[*pindex] == 'V') && (vpd[*pindex + 1] == '1')) {
+ phba->vpd_flag |= VPD_MODEL_DESC;
+ *pindex += 2;
+ i = vpd[*pindex];
+ *pindex += 1;
+ j = 0;
+ length -= (3+i);
+ while (i--) {
+ phba->ModelDesc[j++] = vpd[(*pindex)++];
+ if (j == 255)
+ break;
+ }
+ phba->ModelDesc[j] = 0;
+ continue;
+ } else if ((vpd[*pindex] == 'V') && (vpd[*pindex + 1] == '2')) {
+ phba->vpd_flag |= VPD_MODEL_NAME;
+ *pindex += 2;
+ i = vpd[*pindex];
+ *pindex += 1;
+ j = 0;
+ length -= (3+i);
+ while (i--) {
+ phba->ModelName[j++] = vpd[(*pindex)++];
+ if (j == 79)
+ break;
+ }
+ phba->ModelName[j] = 0;
+ continue;
+ } else if ((vpd[*pindex] == 'V') && (vpd[*pindex + 1] == '3')) {
+ phba->vpd_flag |= VPD_PROGRAM_TYPE;
+ *pindex += 2;
+ i = vpd[*pindex];
+ *pindex += 1;
+ j = 0;
+ length -= (3+i);
+ while (i--) {
+ phba->ProgramType[j++] = vpd[(*pindex)++];
+ if (j == 255)
+ break;
+ }
+ phba->ProgramType[j] = 0;
+ continue;
+ } else if ((vpd[*pindex] == 'V') && (vpd[*pindex + 1] == '4')) {
+ phba->vpd_flag |= VPD_PORT;
+ *pindex += 2;
+ i = vpd[*pindex];
+ *pindex += 1;
+ j = 0;
+ length -= (3 + i);
+ while (i--) {
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ (phba->sli4_hba.pport_name_sta ==
+ LPFC_SLI4_PPNAME_GET)) {
+ j++;
+ (*pindex)++;
+ } else
+ phba->Port[j++] = vpd[(*pindex)++];
+ if (j == 19)
+ break;
+ }
+ if ((phba->sli_rev != LPFC_SLI_REV4) ||
+ (phba->sli4_hba.pport_name_sta ==
+ LPFC_SLI4_PPNAME_NON))
+ phba->Port[j] = 0;
+ continue;
+ } else {
+ *pindex += 2;
+ i = vpd[*pindex];
+ *pindex += 1;
+ *pindex += i;
+ length -= (3 + i);
+ }
+ }
+}
+
/**
* lpfc_parse_vpd - Parse VPD (Vital Product Data)
* @phba: pointer to lpfc hba data structure.
@@ -1622,7 +2366,7 @@ lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
{
uint8_t lenlo, lenhi;
int Length;
- int i, j;
+ int i;
int finished = 0;
int index = 0;
@@ -1655,92 +2399,10 @@ lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
Length = ((((unsigned short)lenhi) << 8) + lenlo);
if (Length > len - index)
Length = len - index;
- while (Length > 0) {
- /* Look for Serial Number */
- if ((vpd[index] == 'S') && (vpd[index+1] == 'N')) {
- index += 2;
- i = vpd[index];
- index += 1;
- j = 0;
- Length -= (3+i);
- while(i--) {
- phba->SerialNumber[j++] = vpd[index++];
- if (j == 31)
- break;
- }
- phba->SerialNumber[j] = 0;
- continue;
- }
- else if ((vpd[index] == 'V') && (vpd[index+1] == '1')) {
- phba->vpd_flag |= VPD_MODEL_DESC;
- index += 2;
- i = vpd[index];
- index += 1;
- j = 0;
- Length -= (3+i);
- while(i--) {
- phba->ModelDesc[j++] = vpd[index++];
- if (j == 255)
- break;
- }
- phba->ModelDesc[j] = 0;
- continue;
- }
- else if ((vpd[index] == 'V') && (vpd[index+1] == '2')) {
- phba->vpd_flag |= VPD_MODEL_NAME;
- index += 2;
- i = vpd[index];
- index += 1;
- j = 0;
- Length -= (3+i);
- while(i--) {
- phba->ModelName[j++] = vpd[index++];
- if (j == 79)
- break;
- }
- phba->ModelName[j] = 0;
- continue;
- }
- else if ((vpd[index] == 'V') && (vpd[index+1] == '3')) {
- phba->vpd_flag |= VPD_PROGRAM_TYPE;
- index += 2;
- i = vpd[index];
- index += 1;
- j = 0;
- Length -= (3+i);
- while(i--) {
- phba->ProgramType[j++] = vpd[index++];
- if (j == 255)
- break;
- }
- phba->ProgramType[j] = 0;
- continue;
- }
- else if ((vpd[index] == 'V') && (vpd[index+1] == '4')) {
- phba->vpd_flag |= VPD_PORT;
- index += 2;
- i = vpd[index];
- index += 1;
- j = 0;
- Length -= (3+i);
- while(i--) {
- phba->Port[j++] = vpd[index++];
- if (j == 19)
- break;
- }
- phba->Port[j] = 0;
- continue;
- }
- else {
- index += 2;
- i = vpd[index];
- index += 1;
- index += i;
- Length -= (3 + i);
- }
- }
- finished = 0;
- break;
+
+ lpfc_fill_vpd(phba, vpd, Length, &index);
+ finished = 0;
+ break;
case 0x78:
finished = 1;
break;
@@ -1754,6 +2416,90 @@ lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
}
/**
+ * lpfc_get_atto_model_desc - Retrieve ATTO HBA device model name and description
+ * @phba: pointer to lpfc hba data structure.
+ * @mdp: pointer to the data structure to hold the derived model name.
+ * @descp: pointer to the data structure to hold the derived description.
+ *
+ * This routine retrieves HBA's description based on its registered PCI device
+ * ID. The @descp passed into this function points to an array of 256 chars. It
+ * shall be returned with the model name, maximum speed, and the host bus type.
+ * The @mdp passed into this function points to an array of 80 chars. When the
+ * function returns, the @mdp will be filled with the model name.
+ **/
+static void
+lpfc_get_atto_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
+{
+ uint16_t sub_dev_id = phba->pcidev->subsystem_device;
+ char *model = "<Unknown>";
+ int tbolt = 0;
+
+ switch (sub_dev_id) {
+ case PCI_DEVICE_ID_CLRY_161E:
+ model = "161E";
+ break;
+ case PCI_DEVICE_ID_CLRY_162E:
+ model = "162E";
+ break;
+ case PCI_DEVICE_ID_CLRY_164E:
+ model = "164E";
+ break;
+ case PCI_DEVICE_ID_CLRY_161P:
+ model = "161P";
+ break;
+ case PCI_DEVICE_ID_CLRY_162P:
+ model = "162P";
+ break;
+ case PCI_DEVICE_ID_CLRY_164P:
+ model = "164P";
+ break;
+ case PCI_DEVICE_ID_CLRY_321E:
+ model = "321E";
+ break;
+ case PCI_DEVICE_ID_CLRY_322E:
+ model = "322E";
+ break;
+ case PCI_DEVICE_ID_CLRY_324E:
+ model = "324E";
+ break;
+ case PCI_DEVICE_ID_CLRY_321P:
+ model = "321P";
+ break;
+ case PCI_DEVICE_ID_CLRY_322P:
+ model = "322P";
+ break;
+ case PCI_DEVICE_ID_CLRY_324P:
+ model = "324P";
+ break;
+ case PCI_DEVICE_ID_TLFC_2XX2:
+ model = "2XX2";
+ tbolt = 1;
+ break;
+ case PCI_DEVICE_ID_TLFC_3162:
+ model = "3162";
+ tbolt = 1;
+ break;
+ case PCI_DEVICE_ID_TLFC_3322:
+ model = "3322";
+ tbolt = 1;
+ break;
+ default:
+ model = "Unknown";
+ break;
+ }
+
+ if (mdp && mdp[0] == '\0')
+ snprintf(mdp, 79, "%s", model);
+
+ if (descp && descp[0] == '\0')
+ snprintf(descp, 255,
+ "ATTO %s%s, Fibre Channel Adapter Initiator, Port %s",
+ (tbolt) ? "ThunderLink FC " : "Celerity FC-",
+ model,
+ phba->Port);
+}
+
+/**
* lpfc_get_hba_model_desc - Retrieve HBA device model name and description
* @phba: pointer to lpfc hba data structure.
* @mdp: pointer to the data structure to hold the derived model name.
@@ -1783,7 +2529,16 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
&& descp && descp[0] != '\0')
return;
- if (phba->lmt & LMT_16Gb)
+ if (phba->pcidev->vendor == PCI_VENDOR_ID_ATTO) {
+ lpfc_get_atto_model_desc(phba, mdp, descp);
+ return;
+ }
+
+ if (phba->lmt & LMT_64Gb)
+ max_speed = 64;
+ else if (phba->lmt & LMT_32Gb)
+ max_speed = 32;
+ else if (phba->lmt & LMT_16Gb)
max_speed = 16;
else if (phba->lmt & LMT_10Gb)
max_speed = 10;
@@ -1793,172 +2548,215 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
max_speed = 4;
else if (phba->lmt & LMT_2Gb)
max_speed = 2;
- else
+ else if (phba->lmt & LMT_1Gb)
max_speed = 1;
+ else
+ max_speed = 0;
vp = &phba->vpd;
switch (dev_id) {
case PCI_DEVICE_ID_FIREFLY:
- m = (typeof(m)){"LP6000", "PCI", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP6000", "PCI",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SUPERFLY:
if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
- m = (typeof(m)){"LP7000", "PCI",
- "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP7000", "PCI", ""};
else
- m = (typeof(m)){"LP7000E", "PCI",
- "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP7000E", "PCI", ""};
+ m.function = "Obsolete, Unsupported Fibre Channel Adapter";
break;
case PCI_DEVICE_ID_DRAGONFLY:
m = (typeof(m)){"LP8000", "PCI",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_CENTAUR:
if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
- m = (typeof(m)){"LP9002", "PCI",
- "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP9002", "PCI", ""};
else
- m = (typeof(m)){"LP9000", "PCI",
- "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP9000", "PCI", ""};
+ m.function = "Obsolete, Unsupported Fibre Channel Adapter";
break;
case PCI_DEVICE_ID_RFLY:
m = (typeof(m)){"LP952", "PCI",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PEGASUS:
m = (typeof(m)){"LP9802", "PCI-X",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_THOR:
m = (typeof(m)){"LP10000", "PCI-X",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_VIPER:
m = (typeof(m)){"LPX1000", "PCI-X",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PFLY:
m = (typeof(m)){"LP982", "PCI-X",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_TFLY:
m = (typeof(m)){"LP1050", "PCI-X",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS:
m = (typeof(m)){"LP11000", "PCI-X2",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS_SCSP:
m = (typeof(m)){"LP11000-SP", "PCI-X2",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS_DCSP:
m = (typeof(m)){"LP11002-SP", "PCI-X2",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE:
- m = (typeof(m)){"LPe1000", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe1000", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE_SCSP:
- m = (typeof(m)){"LPe1000-SP", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe1000-SP", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE_DCSP:
- m = (typeof(m)){"LPe1002-SP", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe1002-SP", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_BMID:
- m = (typeof(m)){"LP1150", "PCI-X2", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP1150", "PCI-X2",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_BSMB:
- m = (typeof(m)){"LP111", "PCI-X2", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP111", "PCI-X2",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR:
- m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe11000", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR_SCSP:
- m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe11000", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR_DCSP:
- m = (typeof(m)){"LP2105", "PCIe", "FCoE Adapter"};
+ m = (typeof(m)){"LP2105", "PCIe",
+ "Obsolete, Unsupported FCoE Adapter"};
GE = 1;
break;
case PCI_DEVICE_ID_ZMID:
- m = (typeof(m)){"LPe1150", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe1150", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZSMB:
- m = (typeof(m)){"LPe111", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe111", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP101:
- m = (typeof(m)){"LP101", "PCI-X", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP101", "PCI-X",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP10000S:
- m = (typeof(m)){"LP10000-S", "PCI", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP10000-S", "PCI",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP11000S:
- m = (typeof(m)){"LP11000-S", "PCI-X2", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LP11000-S", "PCI-X2",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LPE11000S:
- m = (typeof(m)){"LPe11000-S", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe11000-S", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT:
- m = (typeof(m)){"LPe12000", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe12000", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_MID:
- m = (typeof(m)){"LPe1250", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe1250", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_SMB:
- m = (typeof(m)){"LPe121", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe121", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_DCSP:
- m = (typeof(m)){"LPe12002-SP", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe12002-SP", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_SCSP:
- m = (typeof(m)){"LPe12000-SP", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe12000-SP", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_S:
- m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"};
- break;
- case PCI_DEVICE_ID_HORNET:
- m = (typeof(m)){"LP21000", "PCIe", "FCoE Adapter"};
- GE = 1;
+ m = (typeof(m)){"LPe12000-S", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PROTEUS_VF:
m = (typeof(m)){"LPev12000", "PCIe IOV",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PROTEUS_PF:
m = (typeof(m)){"LPev12000", "PCIe IOV",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PROTEUS_S:
m = (typeof(m)){"LPemv12002-S", "PCIe IOV",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_TIGERSHARK:
oneConnect = 1;
- m = (typeof(m)){"OCe10100", "PCIe", "FCoE"};
+ m = (typeof(m)){"OCe10100", "PCIe",
+ "Obsolete, Unsupported FCoE Adapter"};
break;
case PCI_DEVICE_ID_TOMCAT:
oneConnect = 1;
- m = (typeof(m)){"OCe11100", "PCIe", "FCoE"};
+ m = (typeof(m)){"OCe11100", "PCIe",
+ "Obsolete, Unsupported FCoE Adapter"};
break;
case PCI_DEVICE_ID_FALCON:
m = (typeof(m)){"LPSe12002-ML1-E", "PCIe",
- "EmulexSecure Fibre"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_BALIUS:
m = (typeof(m)){"LPVe12002", "PCIe Shared I/O",
- "Fibre Channel Adapter"};
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LANCER_FC:
+ m = (typeof(m)){"LPe16000", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
+ break;
case PCI_DEVICE_ID_LANCER_FC_VF:
- m = (typeof(m)){"LPe16000", "PCIe", "Fibre Channel Adapter"};
+ m = (typeof(m)){"LPe16000", "PCIe",
+ "Obsolete, Unsupported Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LANCER_FCOE:
+ oneConnect = 1;
+ m = (typeof(m)){"OCe15100", "PCIe",
+ "Obsolete, Unsupported FCoE Adapter"};
+ break;
case PCI_DEVICE_ID_LANCER_FCOE_VF:
oneConnect = 1;
- m = (typeof(m)){"OCe50100", "PCIe", "FCoE"};
+ m = (typeof(m)){"OCe15100", "PCIe",
+ "Obsolete, Unsupported FCoE Adapter"};
+ break;
+ case PCI_DEVICE_ID_LANCER_G6_FC:
+ m = (typeof(m)){"LPe32000", "PCIe", "Fibre Channel Adapter"};
+ break;
+ case PCI_DEVICE_ID_LANCER_G7_FC:
+ m = (typeof(m)){"LPe36000", "PCIe", "Fibre Channel Adapter"};
+ break;
+ case PCI_DEVICE_ID_LANCER_G7P_FC:
+ m = (typeof(m)){"LPe38000", "PCIe", "Fibre Channel Adapter"};
+ break;
+ case PCI_DEVICE_ID_SKYHAWK:
+ case PCI_DEVICE_ID_SKYHAWK_VF:
+ oneConnect = 1;
+ m = (typeof(m)){"OCe14000", "PCIe",
+ "Obsolete, Unsupported FCoE Adapter"};
break;
default:
m = (typeof(m)){"Unknown", "", ""};
@@ -1974,9 +2772,13 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
if (descp && descp[0] == '\0') {
if (oneConnect)
snprintf(descp, 255,
- "Emulex OneConnect %s, %s Initiator, Port %s",
+ "Emulex OneConnect %s, %s Initiator %s",
m.name, m.function,
phba->Port);
+ else if (max_speed == 0)
+ snprintf(descp, 255,
+ "Emulex %s %s %s",
+ m.name, m.bus, m.function);
else
snprintf(descp, 255,
"Emulex %s %d%s %s %s",
@@ -1986,7 +2788,7 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
}
/**
- * lpfc_post_buffer - Post IOCB(s) with DMA buffer descriptor(s) to a IOCB ring
+ * lpfc_sli3_post_buffer - Post IOCB(s) with DMA buffer descriptor(s) to a IOCB ring
* @phba: pointer to lpfc hba data structure.
* @pring: pointer to a IOCB ring.
* @cnt: the number of IOCBs to be posted to the IOCB ring.
@@ -1998,7 +2800,7 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
* The number of IOCBs NOT able to be posted to the IOCB ring.
**/
int
-lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
+lpfc_sli3_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
{
IOCB_t *icmd;
struct lpfc_iocbq *iocb;
@@ -2093,7 +2895,7 @@ lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
*
* This routine posts initial receive IOCB buffers to the ELS ring. The
* current number of initial IOCB buffers specified by LPFC_BUF_RING0 is
- * set to 64 IOCBs.
+ * set to 64 IOCBs. SLI3 only.
*
* Return codes
* 0 - success (currently always success)
@@ -2104,7 +2906,7 @@ lpfc_post_rcv_buf(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
/* Ring 0, ELS / CT buffers */
- lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0);
+ lpfc_sli3_post_buffer(phba, &psli->sli3_ring[LPFC_ELS_RING], LPFC_BUF_RING0);
/* Ring 2 - FCP no buffers needed */
return 0;
@@ -2250,44 +3052,39 @@ lpfc_cleanup(struct lpfc_vport *vport)
if (phba->link_state > LPFC_LINK_DOWN)
lpfc_port_link_failure(vport);
- list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp,
- NLP_STE_UNUSED_NODE);
- if (!ndlp)
- continue;
- spin_lock_irq(&phba->ndlp_lock);
- NLP_SET_FREE_REQ(ndlp);
- spin_unlock_irq(&phba->ndlp_lock);
- /* Trigger the release of the ndlp memory */
- lpfc_nlp_put(ndlp);
- continue;
- }
- spin_lock_irq(&phba->ndlp_lock);
- if (NLP_CHK_FREE_REQ(ndlp)) {
- /* The ndlp should not be in memory free mode already */
- spin_unlock_irq(&phba->ndlp_lock);
- continue;
- } else
- /* Indicate request for freeing ndlp memory */
- NLP_SET_FREE_REQ(ndlp);
- spin_unlock_irq(&phba->ndlp_lock);
-
- if (vport->port_type != LPFC_PHYSICAL_PORT &&
- ndlp->nlp_DID == Fabric_DID) {
- /* Just free up ndlp with Fabric_DID for vports */
- lpfc_nlp_put(ndlp);
- continue;
- }
+ /* Clean up VMID resources */
+ if (lpfc_is_vmid_enabled(phba))
+ lpfc_vmid_vport_cleanup(vport);
- if (ndlp->nlp_type & NLP_FABRIC)
+ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
+ /* Fabric Ports not in UNMAPPED state are cleaned up in the
+ * DEVICE_RM event.
+ */
+ if (ndlp->nlp_type & NLP_FABRIC &&
+ ndlp->nlp_state == NLP_STE_UNMAPPED_NODE)
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
- lpfc_disc_state_machine(vport, ndlp, NULL,
- NLP_EVT_DEVICE_RM);
-
- }
+ if (!(ndlp->fc4_xpt_flags & (NVME_XPT_REGD|SCSI_XPT_REGD)))
+ lpfc_disc_state_machine(vport, ndlp, NULL,
+ NLP_EVT_DEVICE_RM);
+ }
+
+ /* This is a special case flush to return all
+ * IOs before entering this loop. There are
+ * two points in the code where a flush is
+ * avoided if the FC_UNLOADING flag is set.
+ * one is in the multipool destroy,
+ * (this prevents a crash) and the other is
+ * in the nvme abort handler, ( also prevents
+ * a crash). Both of these exceptions are
+ * cases where the slot is still accessible.
+ * The flush here is only when the pci slot
+ * is offline.
+ */
+ if (test_bit(FC_UNLOADING, &vport->load_flag) &&
+ pci_channel_offline(phba->pcidev))
+ lpfc_sli_flush_io_rings(vport->phba);
/* At this point, ALL ndlp's should be gone
* because of the previous NLP_EVT_DEVICE_RM.
@@ -2295,18 +3092,20 @@ lpfc_cleanup(struct lpfc_vport *vport)
*/
while (!list_empty(&vport->fc_nodes)) {
if (i++ > 3000) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
"0233 Nodelist not empty\n");
list_for_each_entry_safe(ndlp, next_ndlp,
&vport->fc_nodes, nlp_listp) {
lpfc_printf_vlog(ndlp->vport, KERN_ERR,
- LOG_NODE,
- "0282 did:x%x ndlp:x%p "
- "usgmap:x%x refcnt:%d\n",
- ndlp->nlp_DID, (void *)ndlp,
- ndlp->nlp_usg_map,
- atomic_read(
- &ndlp->kref.refcount));
+ LOG_DISCOVERY,
+ "0282 did:x%x ndlp:x%px "
+ "refcnt:%d xflags x%x "
+ "nflag x%lx\n",
+ ndlp->nlp_DID, (void *)ndlp,
+ kref_read(&ndlp->kref),
+ ndlp->fc4_xpt_flags,
+ ndlp->nlp_flag);
}
break;
}
@@ -2328,9 +3127,8 @@ lpfc_cleanup(struct lpfc_vport *vport)
void
lpfc_stop_vport_timers(struct lpfc_vport *vport)
{
- del_timer_sync(&vport->els_tmofunc);
- del_timer_sync(&vport->fc_fdmitmo);
- del_timer_sync(&vport->delayed_disc_tmo);
+ timer_delete_sync(&vport->els_tmofunc);
+ timer_delete_sync(&vport->delayed_disc_tmo);
lpfc_can_disctmo(vport);
return;
}
@@ -2349,7 +3147,7 @@ __lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba)
phba->fcf.fcf_flag &= ~FCF_REDISC_PEND;
/* Now, try to stop the timer */
- del_timer(&phba->fcf.redisc_wait);
+ timer_delete(&phba->fcf.redisc_wait);
}
/**
@@ -2377,6 +3175,127 @@ lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba)
}
/**
+ * lpfc_cmf_stop - Stop CMF processing
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This is called when the link goes down or if CMF mode is turned OFF.
+ * It is also called when going offline or unloaded just before the
+ * congestion info buffer is unregistered.
+ **/
+void
+lpfc_cmf_stop(struct lpfc_hba *phba)
+{
+ int cpu;
+ struct lpfc_cgn_stat *cgs;
+
+ /* We only do something if CMF is enabled */
+ if (!phba->sli4_hba.pc_sli4_params.cmf)
+ return;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6221 Stop CMF / Cancel Timer\n");
+
+ /* Cancel the CMF timer */
+ hrtimer_cancel(&phba->cmf_stats_timer);
+ hrtimer_cancel(&phba->cmf_timer);
+
+ /* Zero CMF counters */
+ atomic_set(&phba->cmf_busy, 0);
+ for_each_present_cpu(cpu) {
+ cgs = per_cpu_ptr(phba->cmf_stat, cpu);
+ atomic64_set(&cgs->total_bytes, 0);
+ atomic64_set(&cgs->rcv_bytes, 0);
+ atomic_set(&cgs->rx_io_cnt, 0);
+ atomic64_set(&cgs->rx_latency, 0);
+ }
+ atomic_set(&phba->cmf_bw_wait, 0);
+
+ /* Resume any blocked IO - Queue unblock on workqueue */
+ queue_work(phba->wq, &phba->unblock_request_work);
+}
+
+static inline uint64_t
+lpfc_get_max_line_rate(struct lpfc_hba *phba)
+{
+ uint64_t rate = lpfc_sli_port_speed_get(phba);
+
+ return ((((unsigned long)rate) * 1024 * 1024) / 10);
+}
+
+void
+lpfc_cmf_signal_init(struct lpfc_hba *phba)
+{
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6223 Signal CMF init\n");
+
+ /* Use the new fc_linkspeed to recalculate */
+ phba->cmf_interval_rate = LPFC_CMF_INTERVAL;
+ phba->cmf_max_line_rate = lpfc_get_max_line_rate(phba);
+ phba->cmf_link_byte_count = div_u64(phba->cmf_max_line_rate *
+ phba->cmf_interval_rate, 1000);
+ phba->cmf_max_bytes_per_interval = phba->cmf_link_byte_count;
+
+ /* This is a signal to firmware to sync up CMF BW with link speed */
+ lpfc_issue_cmf_sync_wqe(phba, 0, 0);
+}
+
+/**
+ * lpfc_cmf_start - Start CMF processing
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This is called when the link comes up or if CMF mode is turned OFF
+ * to Monitor or Managed.
+ **/
+void
+lpfc_cmf_start(struct lpfc_hba *phba)
+{
+ struct lpfc_cgn_stat *cgs;
+ int cpu;
+
+ /* We only do something if CMF is enabled */
+ if (!phba->sli4_hba.pc_sli4_params.cmf ||
+ phba->cmf_active_mode == LPFC_CFG_OFF)
+ return;
+
+ /* Reinitialize congestion buffer info */
+ lpfc_init_congestion_buf(phba);
+
+ atomic_set(&phba->cgn_fabric_warn_cnt, 0);
+ atomic_set(&phba->cgn_fabric_alarm_cnt, 0);
+ atomic_set(&phba->cgn_sync_alarm_cnt, 0);
+ atomic_set(&phba->cgn_sync_warn_cnt, 0);
+
+ atomic_set(&phba->cmf_busy, 0);
+ for_each_present_cpu(cpu) {
+ cgs = per_cpu_ptr(phba->cmf_stat, cpu);
+ atomic64_set(&cgs->total_bytes, 0);
+ atomic64_set(&cgs->rcv_bytes, 0);
+ atomic_set(&cgs->rx_io_cnt, 0);
+ atomic64_set(&cgs->rx_latency, 0);
+ }
+ phba->cmf_latency.tv_sec = 0;
+ phba->cmf_latency.tv_nsec = 0;
+
+ lpfc_cmf_signal_init(phba);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6222 Start CMF / Timer\n");
+
+ phba->cmf_timer_cnt = 0;
+ hrtimer_start(&phba->cmf_timer,
+ ktime_set(0, LPFC_CMF_INTERVAL * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ hrtimer_start(&phba->cmf_stats_timer,
+ ktime_set(0, LPFC_SEC_MIN * NSEC_PER_SEC),
+ HRTIMER_MODE_REL);
+ /* Setup for latency check in IO cmpl routines */
+ ktime_get_real_ts64(&phba->cmf_latency);
+
+ atomic_set(&phba->cmf_bw_wait, 0);
+ atomic_set(&phba->cmf_stop_io, 0);
+}
+
+/**
* lpfc_stop_hba_timers - Stop all the timers associated with an HBA
* @phba: pointer to lpfc hba data structure.
*
@@ -2386,28 +3305,32 @@ lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba)
void
lpfc_stop_hba_timers(struct lpfc_hba *phba)
{
- lpfc_stop_vport_timers(phba->pport);
- del_timer_sync(&phba->sli.mbox_tmo);
- del_timer_sync(&phba->fabric_block_timer);
- del_timer_sync(&phba->eratt_poll);
- del_timer_sync(&phba->hb_tmofunc);
+ if (phba->pport)
+ lpfc_stop_vport_timers(phba->pport);
+ cancel_delayed_work_sync(&phba->eq_delay_work);
+ cancel_delayed_work_sync(&phba->idle_stat_delay_work);
+ timer_delete_sync(&phba->sli.mbox_tmo);
+ timer_delete_sync(&phba->fabric_block_timer);
+ timer_delete_sync(&phba->eratt_poll);
+ timer_delete_sync(&phba->hb_tmofunc);
if (phba->sli_rev == LPFC_SLI_REV4) {
- del_timer_sync(&phba->rrq_tmr);
- phba->hba_flag &= ~HBA_RRQ_ACTIVE;
+ timer_delete_sync(&phba->rrq_tmr);
+ clear_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
}
- phba->hb_outstanding = 0;
+ clear_bit(HBA_HBEAT_INP, &phba->hba_flag);
+ clear_bit(HBA_HBEAT_TMO, &phba->hba_flag);
switch (phba->pci_dev_grp) {
case LPFC_PCI_DEV_LP:
/* Stop any LightPulse device specific driver timers */
- del_timer_sync(&phba->fcp_poll_timer);
+ timer_delete_sync(&phba->fcp_poll_timer);
break;
case LPFC_PCI_DEV_OC:
- /* Stop any OneConnect device sepcific driver timers */
+ /* Stop any OneConnect device specific driver timers */
lpfc_sli4_stop_fcf_redisc_wait_timer(phba);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0297 Invalid device group (x%x)\n",
phba->pci_dev_grp);
break;
@@ -2418,6 +3341,7 @@ lpfc_stop_hba_timers(struct lpfc_hba *phba)
/**
* lpfc_block_mgmt_io - Mark a HBA's management interface as blocked
* @phba: pointer to lpfc hba data structure.
+ * @mbx_action: flag for mailbox no wait action.
*
* This routine marks a HBA's management interface as blocked. Once the HBA's
* management interface is marked as blocked, all the user space access to
@@ -2426,38 +3350,335 @@ lpfc_stop_hba_timers(struct lpfc_hba *phba)
* driver prepares the HBA interface for online or offline.
**/
static void
-lpfc_block_mgmt_io(struct lpfc_hba * phba)
+lpfc_block_mgmt_io(struct lpfc_hba *phba, int mbx_action)
{
unsigned long iflag;
uint8_t actcmd = MBX_HEARTBEAT;
unsigned long timeout;
-
spin_lock_irqsave(&phba->hbalock, iflag);
phba->sli.sli_flag |= LPFC_BLOCK_MGMT_IO;
- if (phba->sli.mbox_active)
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ if (mbx_action == LPFC_MBX_NO_WAIT)
+ return;
+ timeout = secs_to_jiffies(LPFC_MBOX_TMO) + jiffies;
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (phba->sli.mbox_active) {
actcmd = phba->sli.mbox_active->u.mb.mbxCommand;
+ /* Determine how long we might wait for the active mailbox
+ * command to be gracefully completed by firmware.
+ */
+ timeout = secs_to_jiffies(lpfc_mbox_tmo_val(phba,
+ phba->sli.mbox_active)) + jiffies;
+ }
spin_unlock_irqrestore(&phba->hbalock, iflag);
- /* Determine how long we might wait for the active mailbox
- * command to be gracefully completed by firmware.
- */
- timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, actcmd) * 1000) +
- jiffies;
+
/* Wait for the outstnading mailbox command to complete */
while (phba->sli.mbox_active) {
/* Check active mailbox complete status every 2ms */
msleep(2);
if (time_after(jiffies, timeout)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2813 Mgmt IO is Blocked %x "
- "- mbox cmd %x still active\n",
- phba->sli.sli_flag, actcmd);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2813 Mgmt IO is Blocked %x "
+ "- mbox cmd %x still active\n",
+ phba->sli.sli_flag, actcmd);
break;
}
}
}
/**
+ * lpfc_sli4_node_rpi_restore - Recover assigned RPIs for active nodes.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * Allocate RPIs for all active remote nodes. This is needed whenever
+ * an SLI4 adapter is reset and the driver is not unloading. Its purpose
+ * is to fixup the temporary rpi assignments.
+ **/
+void
+lpfc_sli4_node_rpi_restore(struct lpfc_hba *phba)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+ struct lpfc_vport **vports;
+ int i, rpi;
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return;
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (!vports)
+ return;
+
+ for (i = 0; i <= phba->max_vports && vports[i]; i++) {
+ if (test_bit(FC_UNLOADING, &vports[i]->load_flag))
+ continue;
+
+ list_for_each_entry_safe(ndlp, next_ndlp,
+ &vports[i]->fc_nodes,
+ nlp_listp) {
+ rpi = lpfc_sli4_alloc_rpi(phba);
+ if (rpi == LPFC_RPI_ALLOC_ERROR) {
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "0099 RPI alloc error for "
+ "ndlp x%px DID:x%06x "
+ "flg:x%lx\n",
+ ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag);
+ continue;
+ }
+ ndlp->nlp_rpi = rpi;
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "0009 Assign RPI x%x to ndlp x%px "
+ "DID:x%06x flg:x%lx\n",
+ ndlp->nlp_rpi, ndlp, ndlp->nlp_DID,
+ ndlp->nlp_flag);
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
+/**
+ * lpfc_create_expedite_pool - create expedite pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine moves a batch of XRIs from lpfc_io_buf_list_put of HWQ 0
+ * to expedite pool. Mark them as expedite.
+ **/
+static void lpfc_create_expedite_pool(struct lpfc_hba *phba)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ struct lpfc_epd_pool *epd_pool;
+ unsigned long iflag;
+
+ epd_pool = &phba->epd_pool;
+ qp = &phba->sli4_hba.hdwq[0];
+
+ spin_lock_init(&epd_pool->lock);
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+ spin_lock(&epd_pool->lock);
+ INIT_LIST_HEAD(&epd_pool->list);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_put, list) {
+ list_move_tail(&lpfc_ncmd->list, &epd_pool->list);
+ lpfc_ncmd->expedite = true;
+ qp->put_io_bufs--;
+ epd_pool->count++;
+ if (epd_pool->count >= XRI_BATCH)
+ break;
+ }
+ spin_unlock(&epd_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+}
+
+/**
+ * lpfc_destroy_expedite_pool - destroy expedite pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine returns XRIs from expedite pool to lpfc_io_buf_list_put
+ * of HWQ 0. Clear the mark.
+ **/
+static void lpfc_destroy_expedite_pool(struct lpfc_hba *phba)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ struct lpfc_epd_pool *epd_pool;
+ unsigned long iflag;
+
+ epd_pool = &phba->epd_pool;
+ qp = &phba->sli4_hba.hdwq[0];
+
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+ spin_lock(&epd_pool->lock);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &epd_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ lpfc_ncmd->flags = false;
+ qp->put_io_bufs++;
+ epd_pool->count--;
+ }
+ spin_unlock(&epd_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+}
+
+/**
+ * lpfc_create_multixri_pools - create multi-XRI pools
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine initialize public, private per HWQ. Then, move XRIs from
+ * lpfc_io_buf_list_put to public pool. High and low watermark are also
+ * Initialized.
+ **/
+void lpfc_create_multixri_pools(struct lpfc_hba *phba)
+{
+ u32 i, j;
+ u32 hwq_count;
+ u32 count_per_hwq;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1234 num_hdw_queue=%d num_present_cpu=%d common_xri_cnt=%d\n",
+ phba->cfg_hdw_queue, phba->sli4_hba.num_present_cpu,
+ phba->sli4_hba.io_xri_cnt);
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ lpfc_create_expedite_pool(phba);
+
+ hwq_count = phba->cfg_hdw_queue;
+ count_per_hwq = phba->sli4_hba.io_xri_cnt / hwq_count;
+
+ for (i = 0; i < hwq_count; i++) {
+ multixri_pool = kzalloc(sizeof(*multixri_pool), GFP_KERNEL);
+
+ if (!multixri_pool) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1238 Failed to allocate memory for "
+ "multixri_pool\n");
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ lpfc_destroy_expedite_pool(phba);
+
+ j = 0;
+ while (j < i) {
+ qp = &phba->sli4_hba.hdwq[j];
+ kfree(qp->p_multixri_pool);
+ j++;
+ }
+ phba->cfg_xri_rebalancing = 0;
+ return;
+ }
+
+ qp = &phba->sli4_hba.hdwq[i];
+ qp->p_multixri_pool = multixri_pool;
+
+ multixri_pool->xri_limit = count_per_hwq;
+ multixri_pool->rrb_next_hwqid = i;
+
+ /* Deal with public free xri pool */
+ pbl_pool = &multixri_pool->pbl_pool;
+ spin_lock_init(&pbl_pool->lock);
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+ spin_lock(&pbl_pool->lock);
+ INIT_LIST_HEAD(&pbl_pool->list);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_put, list) {
+ list_move_tail(&lpfc_ncmd->list, &pbl_pool->list);
+ qp->put_io_bufs--;
+ pbl_pool->count++;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1235 Moved %d buffers from PUT list over to pbl_pool[%d]\n",
+ pbl_pool->count, i);
+ spin_unlock(&pbl_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+
+ /* Deal with private free xri pool */
+ pvt_pool = &multixri_pool->pvt_pool;
+ pvt_pool->high_watermark = multixri_pool->xri_limit / 2;
+ pvt_pool->low_watermark = XRI_BATCH;
+ spin_lock_init(&pvt_pool->lock);
+ spin_lock_irqsave(&pvt_pool->lock, iflag);
+ INIT_LIST_HEAD(&pvt_pool->list);
+ pvt_pool->count = 0;
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+ }
+}
+
+/**
+ * lpfc_destroy_multixri_pools - destroy multi-XRI pools
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine returns XRIs from public/private to lpfc_io_buf_list_put.
+ **/
+static void lpfc_destroy_multixri_pools(struct lpfc_hba *phba)
+{
+ u32 i;
+ u32 hwq_count;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ lpfc_destroy_expedite_pool(phba);
+
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ lpfc_sli_flush_io_rings(phba);
+
+ hwq_count = phba->cfg_hdw_queue;
+
+ for (i = 0; i < hwq_count; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ continue;
+
+ qp->p_multixri_pool = NULL;
+
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag);
+
+ /* Deal with public free xri pool */
+ pbl_pool = &multixri_pool->pbl_pool;
+ spin_lock(&pbl_pool->lock);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1236 Moving %d buffers from pbl_pool[%d] TO PUT list\n",
+ pbl_pool->count, i);
+
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pbl_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ pbl_pool->count--;
+ }
+
+ INIT_LIST_HEAD(&pbl_pool->list);
+ pbl_pool->count = 0;
+
+ spin_unlock(&pbl_pool->lock);
+
+ /* Deal with private free xri pool */
+ pvt_pool = &multixri_pool->pvt_pool;
+ spin_lock(&pvt_pool->lock);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1237 Moving %d buffers from pvt_pool[%d] TO PUT list\n",
+ pvt_pool->count, i);
+
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pvt_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ pvt_pool->count--;
+ }
+
+ INIT_LIST_HEAD(&pvt_pool->list);
+ pvt_pool->count = 0;
+
+ spin_unlock(&pvt_pool->lock);
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag);
+
+ kfree(multixri_pool);
+ }
+}
+
+/**
* lpfc_online - Initialize and bring a HBA online
* @phba: pointer to lpfc hba data structure.
*
@@ -2474,31 +3695,44 @@ lpfc_online(struct lpfc_hba *phba)
{
struct lpfc_vport *vport;
struct lpfc_vport **vports;
- int i;
+ int i, error = 0;
+ bool vpis_cleared = false;
if (!phba)
return 0;
vport = phba->pport;
- if (!(vport->fc_flag & FC_OFFLINE_MODE))
+ if (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag))
return 0;
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0458 Bring Adapter online\n");
- lpfc_block_mgmt_io(phba);
-
- if (!lpfc_sli_queue_setup(phba)) {
- lpfc_unblock_mgmt_io(phba);
- return 1;
- }
+ lpfc_block_mgmt_io(phba, LPFC_MBX_WAIT);
if (phba->sli_rev == LPFC_SLI_REV4) {
if (lpfc_sli4_hba_setup(phba)) { /* Initialize SLI4 HBA */
lpfc_unblock_mgmt_io(phba);
return 1;
}
+ spin_lock_irq(&phba->hbalock);
+ if (!phba->sli4_hba.max_cfg_param.vpi_used)
+ vpis_cleared = true;
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Reestablish the local initiator port.
+ * The offline process destroyed the previous lport.
+ */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME &&
+ !phba->nvmet_support) {
+ error = lpfc_nvme_create_localport(phba->pport);
+ if (error)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6132 NVME restore reg failed "
+ "on nvmei error x%x\n", error);
+ }
} else {
+ lpfc_sli_queue_init(phba);
if (lpfc_sli_hba_setup(phba)) { /* Initialize SLI2/SLI3 HBA */
lpfc_unblock_mgmt_io(phba);
return 1;
@@ -2506,19 +3740,28 @@ lpfc_online(struct lpfc_hba *phba)
}
vports = lpfc_create_vport_work_array(phba);
- if (vports != NULL)
+ if (vports != NULL) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- struct Scsi_Host *shost;
- shost = lpfc_shost_from_vport(vports[i]);
- spin_lock_irq(shost->host_lock);
- vports[i]->fc_flag &= ~FC_OFFLINE_MODE;
+ clear_bit(FC_OFFLINE_MODE, &vports[i]->fc_flag);
if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
- vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- if (phba->sli_rev == LPFC_SLI_REV4)
- vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_NEEDS_REG_VPI,
+ &vports[i]->fc_flag);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ set_bit(FC_VPORT_NEEDS_INIT_VPI,
+ &vports[i]->fc_flag);
+ if ((vpis_cleared) &&
+ (vports[i]->port_type !=
+ LPFC_PHYSICAL_PORT))
+ vports[i]->vpi = 0;
+ }
}
- lpfc_destroy_vport_work_array(phba, vports);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+
+ if (phba->cfg_xri_rebalancing)
+ lpfc_create_multixri_pools(phba);
+
+ lpfc_cpuhp_add(phba);
lpfc_unblock_mgmt_io(phba);
return 0;
@@ -2548,64 +3791,87 @@ lpfc_unblock_mgmt_io(struct lpfc_hba * phba)
/**
* lpfc_offline_prep - Prepare a HBA to be brought offline
* @phba: pointer to lpfc hba data structure.
+ * @mbx_action: flag for mailbox shutdown action.
*
* This routine is invoked to prepare a HBA to be brought offline. It performs
* unregistration login to all the nodes on all vports and flushes the mailbox
* queue to make it ready to be brought offline.
**/
void
-lpfc_offline_prep(struct lpfc_hba * phba)
+lpfc_offline_prep(struct lpfc_hba *phba, int mbx_action)
{
struct lpfc_vport *vport = phba->pport;
struct lpfc_nodelist *ndlp, *next_ndlp;
struct lpfc_vport **vports;
struct Scsi_Host *shost;
int i;
+ int offline;
+ bool hba_pci_err;
- if (vport->fc_flag & FC_OFFLINE_MODE)
+ if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag))
return;
- lpfc_block_mgmt_io(phba);
+ lpfc_block_mgmt_io(phba, mbx_action);
lpfc_linkdown(phba);
+ offline = pci_channel_offline(phba->pcidev);
+ hba_pci_err = test_bit(HBA_PCI_ERR, &phba->bit_flags);
+
/* Issue an unreg_login to all nodes on all vports */
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- if (vports[i]->load_flag & FC_UNLOADING)
+ if (test_bit(FC_UNLOADING, &vports[i]->load_flag))
continue;
shost = lpfc_shost_from_vport(vports[i]);
spin_lock_irq(shost->host_lock);
vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
- vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- vports[i]->fc_flag &= ~FC_VFI_REGISTERED;
spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vports[i]->fc_flag);
+ clear_bit(FC_VFI_REGISTERED, &vports[i]->fc_flag);
- shost = lpfc_shost_from_vport(vports[i]);
list_for_each_entry_safe(ndlp, next_ndlp,
&vports[i]->fc_nodes,
nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
- if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
- continue;
+
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
+ if (offline || hba_pci_err) {
+ clear_bit(NLP_UNREG_INP,
+ &ndlp->nlp_flag);
+ clear_bit(NLP_RPI_REGISTERED,
+ &ndlp->nlp_flag);
+ }
+
if (ndlp->nlp_type & NLP_FABRIC) {
lpfc_disc_state_machine(vports[i], ndlp,
NULL, NLP_EVT_DEVICE_RECOVERY);
- lpfc_disc_state_machine(vports[i], ndlp,
- NULL, NLP_EVT_DEVICE_RM);
+
+ /* Don't remove the node unless the node
+ * has been unregistered with the
+ * transport, and we're not in recovery
+ * before dev_loss_tmo triggered.
+ * Otherwise, let dev_loss take care of
+ * the node.
+ */
+ if (!test_bit(NLP_IN_RECOV_POST_DEV_LOSS,
+ &ndlp->save_flags) &&
+ !(ndlp->fc4_xpt_flags &
+ (NVME_XPT_REGD | SCSI_XPT_REGD)))
+ lpfc_disc_state_machine
+ (vports[i], ndlp,
+ NULL,
+ NLP_EVT_DEVICE_RM);
}
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
- lpfc_unreg_rpi(vports[i], ndlp);
}
}
}
lpfc_destroy_vport_work_array(phba, vports);
- lpfc_sli_mbox_sys_shutdown(phba);
+ lpfc_sli_mbox_sys_shutdown(phba, mbx_action);
+
+ if (phba->wq)
+ flush_workqueue(phba->wq);
}
/**
@@ -2623,11 +3889,18 @@ lpfc_offline(struct lpfc_hba *phba)
struct lpfc_vport **vports;
int i;
- if (phba->pport->fc_flag & FC_OFFLINE_MODE)
+ if (test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
return;
/* stop port and all timers associated with this hba */
lpfc_stop_port(phba);
+
+ /* Tear down the local and target port registrations. The
+ * nvme transports need to cleanup.
+ */
+ lpfc_nvmet_destroy_targetport(phba);
+ lpfc_nvme_destroy_localport(phba->pport);
+
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
@@ -2647,10 +3920,18 @@ lpfc_offline(struct lpfc_hba *phba)
shost = lpfc_shost_from_vport(vports[i]);
spin_lock_irq(shost->host_lock);
vports[i]->work_port_events = 0;
- vports[i]->fc_flag |= FC_OFFLINE_MODE;
spin_unlock_irq(shost->host_lock);
+ set_bit(FC_OFFLINE_MODE, &vports[i]->fc_flag);
}
lpfc_destroy_vport_work_array(phba, vports);
+ /* If OFFLINE flag is clear (i.e. unloading), cpuhp removal is handled
+ * in hba_unset
+ */
+ if (test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
+ __lpfc_cpuhp_remove(phba);
+
+ if (phba->cfg_xri_rebalancing)
+ lpfc_destroy_multixri_pools(phba);
}
/**
@@ -2660,36 +3941,726 @@ lpfc_offline(struct lpfc_hba *phba)
* This routine is to free all the SCSI buffers and IOCBs from the driver
* list back to kernel. It is called from lpfc_pci_remove_one to free
* the internal resources before the device is removed from the system.
- *
- * Return codes
- * 0 - successful (for now, it always returns 0)
**/
-static int
+static void
lpfc_scsi_free(struct lpfc_hba *phba)
{
- struct lpfc_scsi_buf *sb, *sb_next;
- struct lpfc_iocbq *io, *io_next;
+ struct lpfc_io_buf *sb, *sb_next;
+
+ if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
+ return;
spin_lock_irq(&phba->hbalock);
+
/* Release all the lpfc_scsi_bufs maintained by this host. */
- spin_lock(&phba->scsi_buf_list_lock);
- list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
+
+ spin_lock(&phba->scsi_buf_list_put_lock);
+ list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list_put,
+ list) {
list_del(&sb->list);
- pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data,
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool, sb->data,
sb->dma_handle);
kfree(sb);
phba->total_scsi_bufs--;
}
- spin_unlock(&phba->scsi_buf_list_lock);
+ spin_unlock(&phba->scsi_buf_list_put_lock);
- /* Release all the lpfc_iocbq entries maintained by this host. */
- list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) {
- list_del(&io->list);
- kfree(io);
- phba->total_iocbq_bufs--;
+ spin_lock(&phba->scsi_buf_list_get_lock);
+ list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list_get,
+ list) {
+ list_del(&sb->list);
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool, sb->data,
+ sb->dma_handle);
+ kfree(sb);
+ phba->total_scsi_bufs--;
}
-
+ spin_unlock(&phba->scsi_buf_list_get_lock);
spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_io_free - Free all the IO buffers and IOCBs from driver lists
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to free all the IO buffers and IOCBs from the driver
+ * list back to kernel. It is called from lpfc_pci_remove_one to free
+ * the internal resources before the device is removed from the system.
+ **/
+void
+lpfc_io_free(struct lpfc_hba *phba)
+{
+ struct lpfc_io_buf *lpfc_ncmd, *lpfc_ncmd_next;
+ struct lpfc_sli4_hdw_queue *qp;
+ int idx;
+
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ /* Release all the lpfc_nvme_bufs maintained by this host. */
+ spin_lock(&qp->io_buf_list_put_lock);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_put,
+ list) {
+ list_del(&lpfc_ncmd->list);
+ qp->put_io_bufs--;
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ if (phba->cfg_xpsgl && !phba->nvmet_support)
+ lpfc_put_sgl_per_hdwq(phba, lpfc_ncmd);
+ lpfc_put_cmd_rsp_buf_per_hdwq(phba, lpfc_ncmd);
+ kfree(lpfc_ncmd);
+ qp->total_io_bufs--;
+ }
+ spin_unlock(&qp->io_buf_list_put_lock);
+
+ spin_lock(&qp->io_buf_list_get_lock);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &qp->lpfc_io_buf_list_get,
+ list) {
+ list_del(&lpfc_ncmd->list);
+ qp->get_io_bufs--;
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ if (phba->cfg_xpsgl && !phba->nvmet_support)
+ lpfc_put_sgl_per_hdwq(phba, lpfc_ncmd);
+ lpfc_put_cmd_rsp_buf_per_hdwq(phba, lpfc_ncmd);
+ kfree(lpfc_ncmd);
+ qp->total_io_bufs--;
+ }
+ spin_unlock(&qp->io_buf_list_get_lock);
+ }
+}
+
+/**
+ * lpfc_sli4_els_sgl_update - update ELS xri-sgl sizing and mapping
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine first calculates the sizes of the current els and allocated
+ * scsi sgl lists, and then goes through all sgls to updates the physical
+ * XRIs assigned due to port function reset. During port initialization, the
+ * current els and allocated scsi sgl lists are 0s.
+ *
+ * Return codes
+ * 0 - successful (for now, it always returns 0)
+ **/
+int
+lpfc_sli4_els_sgl_update(struct lpfc_hba *phba)
+{
+ struct lpfc_sglq *sglq_entry = NULL, *sglq_entry_next = NULL;
+ uint16_t i, lxri, xri_cnt, els_xri_cnt;
+ LIST_HEAD(els_sgl_list);
+ int rc;
+
+ /*
+ * update on pci function's els xri-sgl list
+ */
+ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+
+ if (els_xri_cnt > phba->sli4_hba.els_xri_cnt) {
+ /* els xri-sgl expanded */
+ xri_cnt = els_xri_cnt - phba->sli4_hba.els_xri_cnt;
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3157 ELS xri-sgl count increased from "
+ "%d to %d\n", phba->sli4_hba.els_xri_cnt,
+ els_xri_cnt);
+ /* allocate the additional els sgls */
+ for (i = 0; i < xri_cnt; i++) {
+ sglq_entry = kzalloc(sizeof(struct lpfc_sglq),
+ GFP_KERNEL);
+ if (sglq_entry == NULL) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "2562 Failure to allocate an "
+ "ELS sgl entry:%d\n", i);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+ sglq_entry->buff_type = GEN_BUFF_TYPE;
+ sglq_entry->virt = lpfc_mbuf_alloc(phba, 0,
+ &sglq_entry->phys);
+ if (sglq_entry->virt == NULL) {
+ kfree(sglq_entry);
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "2563 Failure to allocate an "
+ "ELS mbuf:%d\n", i);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+ sglq_entry->sgl = sglq_entry->virt;
+ memset(sglq_entry->sgl, 0, LPFC_BPL_SIZE);
+ sglq_entry->state = SGL_FREED;
+ list_add_tail(&sglq_entry->list, &els_sgl_list);
+ }
+ spin_lock_irq(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(&els_sgl_list,
+ &phba->sli4_hba.lpfc_els_sgl_list);
+ spin_unlock_irq(&phba->sli4_hba.sgl_list_lock);
+ } else if (els_xri_cnt < phba->sli4_hba.els_xri_cnt) {
+ /* els xri-sgl shrinked */
+ xri_cnt = phba->sli4_hba.els_xri_cnt - els_xri_cnt;
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3158 ELS xri-sgl count decreased from "
+ "%d to %d\n", phba->sli4_hba.els_xri_cnt,
+ els_xri_cnt);
+ spin_lock_irq(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_els_sgl_list,
+ &els_sgl_list);
+ /* release extra els sgls from list */
+ for (i = 0; i < xri_cnt; i++) {
+ list_remove_head(&els_sgl_list,
+ sglq_entry, struct lpfc_sglq, list);
+ if (sglq_entry) {
+ __lpfc_mbuf_free(phba, sglq_entry->virt,
+ sglq_entry->phys);
+ kfree(sglq_entry);
+ }
+ }
+ list_splice_init(&els_sgl_list,
+ &phba->sli4_hba.lpfc_els_sgl_list);
+ spin_unlock_irq(&phba->sli4_hba.sgl_list_lock);
+ } else
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3163 ELS xri-sgl count unchanged: %d\n",
+ els_xri_cnt);
+ phba->sli4_hba.els_xri_cnt = els_xri_cnt;
+
+ /* update xris to els sgls on the list */
+ sglq_entry = NULL;
+ sglq_entry_next = NULL;
+ list_for_each_entry_safe(sglq_entry, sglq_entry_next,
+ &phba->sli4_hba.lpfc_els_sgl_list, list) {
+ lxri = lpfc_sli4_next_xritag(phba);
+ if (lxri == NO_XRI) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "2400 Failed to allocate xri for "
+ "ELS sgl\n");
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+ sglq_entry->sli4_lxritag = lxri;
+ sglq_entry->sli4_xritag = phba->sli4_hba.xri_ids[lxri];
+ }
+ return 0;
+
+out_free_mem:
+ lpfc_free_els_sgl_list(phba);
+ return rc;
+}
+
+/**
+ * lpfc_sli4_nvmet_sgl_update - update xri-sgl sizing and mapping
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine first calculates the sizes of the current els and allocated
+ * scsi sgl lists, and then goes through all sgls to updates the physical
+ * XRIs assigned due to port function reset. During port initialization, the
+ * current els and allocated scsi sgl lists are 0s.
+ *
+ * Return codes
+ * 0 - successful (for now, it always returns 0)
+ **/
+int
+lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba)
+{
+ struct lpfc_sglq *sglq_entry = NULL, *sglq_entry_next = NULL;
+ uint16_t i, lxri, xri_cnt, els_xri_cnt;
+ uint16_t nvmet_xri_cnt;
+ LIST_HEAD(nvmet_sgl_list);
+ int rc;
+
+ /*
+ * update on pci function's nvmet xri-sgl list
+ */
+ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+
+ /* For NVMET, ALL remaining XRIs are dedicated for IO processing */
+ nvmet_xri_cnt = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt;
+ if (nvmet_xri_cnt > phba->sli4_hba.nvmet_xri_cnt) {
+ /* els xri-sgl expanded */
+ xri_cnt = nvmet_xri_cnt - phba->sli4_hba.nvmet_xri_cnt;
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "6302 NVMET xri-sgl cnt grew from %d to %d\n",
+ phba->sli4_hba.nvmet_xri_cnt, nvmet_xri_cnt);
+ /* allocate the additional nvmet sgls */
+ for (i = 0; i < xri_cnt; i++) {
+ sglq_entry = kzalloc(sizeof(struct lpfc_sglq),
+ GFP_KERNEL);
+ if (sglq_entry == NULL) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6303 Failure to allocate an "
+ "NVMET sgl entry:%d\n", i);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+ sglq_entry->buff_type = NVMET_BUFF_TYPE;
+ sglq_entry->virt = lpfc_nvmet_buf_alloc(phba, 0,
+ &sglq_entry->phys);
+ if (sglq_entry->virt == NULL) {
+ kfree(sglq_entry);
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6304 Failure to allocate an "
+ "NVMET buf:%d\n", i);
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+ sglq_entry->sgl = sglq_entry->virt;
+ memset(sglq_entry->sgl, 0,
+ phba->cfg_sg_dma_buf_size);
+ sglq_entry->state = SGL_FREED;
+ list_add_tail(&sglq_entry->list, &nvmet_sgl_list);
+ }
+ spin_lock_irq(&phba->hbalock);
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(&nvmet_sgl_list,
+ &phba->sli4_hba.lpfc_nvmet_sgl_list);
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
+ spin_unlock_irq(&phba->hbalock);
+ } else if (nvmet_xri_cnt < phba->sli4_hba.nvmet_xri_cnt) {
+ /* nvmet xri-sgl shrunk */
+ xri_cnt = phba->sli4_hba.nvmet_xri_cnt - nvmet_xri_cnt;
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "6305 NVMET xri-sgl count decreased from "
+ "%d to %d\n", phba->sli4_hba.nvmet_xri_cnt,
+ nvmet_xri_cnt);
+ spin_lock_irq(&phba->hbalock);
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_nvmet_sgl_list,
+ &nvmet_sgl_list);
+ /* release extra nvmet sgls from list */
+ for (i = 0; i < xri_cnt; i++) {
+ list_remove_head(&nvmet_sgl_list,
+ sglq_entry, struct lpfc_sglq, list);
+ if (sglq_entry) {
+ lpfc_nvmet_buf_free(phba, sglq_entry->virt,
+ sglq_entry->phys);
+ kfree(sglq_entry);
+ }
+ }
+ list_splice_init(&nvmet_sgl_list,
+ &phba->sli4_hba.lpfc_nvmet_sgl_list);
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
+ spin_unlock_irq(&phba->hbalock);
+ } else
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "6306 NVMET xri-sgl count unchanged: %d\n",
+ nvmet_xri_cnt);
+ phba->sli4_hba.nvmet_xri_cnt = nvmet_xri_cnt;
+
+ /* update xris to nvmet sgls on the list */
+ sglq_entry = NULL;
+ sglq_entry_next = NULL;
+ list_for_each_entry_safe(sglq_entry, sglq_entry_next,
+ &phba->sli4_hba.lpfc_nvmet_sgl_list, list) {
+ lxri = lpfc_sli4_next_xritag(phba);
+ if (lxri == NO_XRI) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6307 Failed to allocate xri for "
+ "NVMET sgl\n");
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+ sglq_entry->sli4_lxritag = lxri;
+ sglq_entry->sli4_xritag = phba->sli4_hba.xri_ids[lxri];
+ }
+ return 0;
+
+out_free_mem:
+ lpfc_free_nvmet_sgl_list(phba);
+ return rc;
+}
+
+int
+lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *cbuf)
+{
+ LIST_HEAD(blist);
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_cmd;
+ struct lpfc_io_buf *iobufp, *prev_iobufp;
+ int idx, cnt, xri, inserted;
+
+ cnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ spin_lock_irq(&qp->io_buf_list_get_lock);
+ spin_lock(&qp->io_buf_list_put_lock);
+
+ /* Take everything off the get and put lists */
+ list_splice_init(&qp->lpfc_io_buf_list_get, &blist);
+ list_splice(&qp->lpfc_io_buf_list_put, &blist);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_get);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put);
+ cnt += qp->get_io_bufs + qp->put_io_bufs;
+ qp->get_io_bufs = 0;
+ qp->put_io_bufs = 0;
+ qp->total_io_bufs = 0;
+ spin_unlock(&qp->io_buf_list_put_lock);
+ spin_unlock_irq(&qp->io_buf_list_get_lock);
+ }
+
+ /*
+ * Take IO buffers off blist and put on cbuf sorted by XRI.
+ * This is because POST_SGL takes a sequential range of XRIs
+ * to post to the firmware.
+ */
+ for (idx = 0; idx < cnt; idx++) {
+ list_remove_head(&blist, lpfc_cmd, struct lpfc_io_buf, list);
+ if (!lpfc_cmd)
+ return cnt;
+ if (idx == 0) {
+ list_add_tail(&lpfc_cmd->list, cbuf);
+ continue;
+ }
+ xri = lpfc_cmd->cur_iocbq.sli4_xritag;
+ inserted = 0;
+ prev_iobufp = NULL;
+ list_for_each_entry(iobufp, cbuf, list) {
+ if (xri < iobufp->cur_iocbq.sli4_xritag) {
+ if (prev_iobufp)
+ list_add(&lpfc_cmd->list,
+ &prev_iobufp->list);
+ else
+ list_add(&lpfc_cmd->list, cbuf);
+ inserted = 1;
+ break;
+ }
+ prev_iobufp = iobufp;
+ }
+ if (!inserted)
+ list_add_tail(&lpfc_cmd->list, cbuf);
+ }
+ return cnt;
+}
+
+int
+lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_cmd;
+ int idx, cnt;
+ unsigned long iflags;
+
+ qp = phba->sli4_hba.hdwq;
+ cnt = 0;
+ while (!list_empty(cbuf)) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ list_remove_head(cbuf, lpfc_cmd,
+ struct lpfc_io_buf, list);
+ if (!lpfc_cmd)
+ return cnt;
+ cnt++;
+ qp = &phba->sli4_hba.hdwq[idx];
+ lpfc_cmd->hdwq_no = idx;
+ lpfc_cmd->hdwq = qp;
+ lpfc_cmd->cur_iocbq.cmd_cmpl = NULL;
+ spin_lock_irqsave(&qp->io_buf_list_put_lock, iflags);
+ list_add_tail(&lpfc_cmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ qp->total_io_bufs++;
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock,
+ iflags);
+ }
+ }
+ return cnt;
+}
+
+/**
+ * lpfc_sli4_io_sgl_update - update xri-sgl sizing and mapping
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine first calculates the sizes of the current els and allocated
+ * scsi sgl lists, and then goes through all sgls to updates the physical
+ * XRIs assigned due to port function reset. During port initialization, the
+ * current els and allocated scsi sgl lists are 0s.
+ *
+ * Return codes
+ * 0 - successful (for now, it always returns 0)
+ **/
+int
+lpfc_sli4_io_sgl_update(struct lpfc_hba *phba)
+{
+ struct lpfc_io_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL;
+ uint16_t i, lxri, els_xri_cnt;
+ uint16_t io_xri_cnt, io_xri_max;
+ LIST_HEAD(io_sgl_list);
+ int rc, cnt;
+
+ /*
+ * update on pci function's allocated nvme xri-sgl list
+ */
+
+ /* maximum number of xris available for nvme buffers */
+ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+ io_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt;
+ phba->sli4_hba.io_xri_max = io_xri_max;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "6074 Current allocated XRI sgl count:%d, "
+ "maximum XRI count:%d els_xri_cnt:%d\n\n",
+ phba->sli4_hba.io_xri_cnt,
+ phba->sli4_hba.io_xri_max,
+ els_xri_cnt);
+
+ cnt = lpfc_io_buf_flush(phba, &io_sgl_list);
+
+ if (phba->sli4_hba.io_xri_cnt > phba->sli4_hba.io_xri_max) {
+ /* max nvme xri shrunk below the allocated nvme buffers */
+ io_xri_cnt = phba->sli4_hba.io_xri_cnt -
+ phba->sli4_hba.io_xri_max;
+ /* release the extra allocated nvme buffers */
+ for (i = 0; i < io_xri_cnt; i++) {
+ list_remove_head(&io_sgl_list, lpfc_ncmd,
+ struct lpfc_io_buf, list);
+ if (lpfc_ncmd) {
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data,
+ lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ }
+ }
+ phba->sli4_hba.io_xri_cnt -= io_xri_cnt;
+ }
+
+ /* update xris associated to remaining allocated nvme buffers */
+ lpfc_ncmd = NULL;
+ lpfc_ncmd_next = NULL;
+ phba->sli4_hba.io_xri_cnt = cnt;
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &io_sgl_list, list) {
+ lxri = lpfc_sli4_next_xritag(phba);
+ if (lxri == NO_XRI) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6075 Failed to allocate xri for "
+ "nvme buffer\n");
+ rc = -ENOMEM;
+ goto out_free_mem;
+ }
+ lpfc_ncmd->cur_iocbq.sli4_lxritag = lxri;
+ lpfc_ncmd->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
+ }
+ cnt = lpfc_io_buf_replenish(phba, &io_sgl_list);
+ return 0;
+
+out_free_mem:
+ lpfc_io_free(phba);
+ return rc;
+}
+
+/**
+ * lpfc_new_io_buf - IO buffer allocator for HBA with SLI4 IF spec
+ * @phba: Pointer to lpfc hba data structure.
+ * @num_to_alloc: The requested number of buffers to allocate.
+ *
+ * This routine allocates nvme buffers for device with SLI-4 interface spec,
+ * the nvme buffer contains all the necessary information needed to initiate
+ * an I/O. After allocating up to @num_to_allocate IO buffers and put
+ * them on a list, it post them to the port by using SGL block post.
+ *
+ * Return codes:
+ * int - number of IO buffers that were allocated and posted.
+ * 0 = failure, less than num_to_alloc is a partial failure.
+ **/
+int
+lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_iocbq *pwqeq;
+ uint16_t iotag, lxri = 0;
+ int bcnt, num_posted;
+ LIST_HEAD(prep_nblist);
+ LIST_HEAD(post_nblist);
+ LIST_HEAD(nvme_nblist);
+
+ phba->sli4_hba.io_xri_cnt = 0;
+ for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
+ lpfc_ncmd = kzalloc(sizeof(*lpfc_ncmd), GFP_KERNEL);
+ if (!lpfc_ncmd)
+ break;
+ /*
+ * Get memory from the pci pool to map the virt space to
+ * pci bus space for an I/O. The DMA buffer includes the
+ * number of SGE's necessary to support the sg_tablesize.
+ */
+ lpfc_ncmd->data = dma_pool_zalloc(phba->lpfc_sg_dma_buf_pool,
+ GFP_KERNEL,
+ &lpfc_ncmd->dma_handle);
+ if (!lpfc_ncmd->data) {
+ kfree(lpfc_ncmd);
+ break;
+ }
+
+ if (phba->cfg_xpsgl && !phba->nvmet_support) {
+ INIT_LIST_HEAD(&lpfc_ncmd->dma_sgl_xtra_list);
+ } else {
+ /*
+ * 4K Page alignment is CRITICAL to BlockGuard, double
+ * check to be sure.
+ */
+ if ((phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
+ (((unsigned long)(lpfc_ncmd->data) &
+ (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "3369 Memory alignment err: "
+ "addr=%lx\n",
+ (unsigned long)lpfc_ncmd->data);
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data,
+ lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ break;
+ }
+ }
+
+ INIT_LIST_HEAD(&lpfc_ncmd->dma_cmd_rsp_list);
+
+ lxri = lpfc_sli4_next_xritag(phba);
+ if (lxri == NO_XRI) {
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ break;
+ }
+ pwqeq = &lpfc_ncmd->cur_iocbq;
+
+ /* Allocate iotag for lpfc_ncmd->cur_iocbq. */
+ iotag = lpfc_sli_next_iotag(phba, pwqeq);
+ if (iotag == 0) {
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ lpfc_ncmd->data, lpfc_ncmd->dma_handle);
+ kfree(lpfc_ncmd);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6121 Failed to allocate IOTAG for"
+ " XRI:0x%x\n", lxri);
+ lpfc_sli4_free_xri(phba, lxri);
+ break;
+ }
+ pwqeq->sli4_lxritag = lxri;
+ pwqeq->sli4_xritag = phba->sli4_hba.xri_ids[lxri];
+
+ /* Initialize local short-hand pointers. */
+ lpfc_ncmd->dma_sgl = lpfc_ncmd->data;
+ lpfc_ncmd->dma_phys_sgl = lpfc_ncmd->dma_handle;
+ lpfc_ncmd->cur_iocbq.io_buf = lpfc_ncmd;
+ spin_lock_init(&lpfc_ncmd->buf_lock);
+
+ /* add the nvme buffer to a post list */
+ list_add_tail(&lpfc_ncmd->list, &post_nblist);
+ phba->sli4_hba.io_xri_cnt++;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
+ "6114 Allocate %d out of %d requested new NVME "
+ "buffers of size x%zu bytes\n", bcnt, num_to_alloc,
+ sizeof(*lpfc_ncmd));
+
+
+ /* post the list of nvme buffer sgls to port if available */
+ if (!list_empty(&post_nblist))
+ num_posted = lpfc_sli4_post_io_sgl_list(
+ phba, &post_nblist, bcnt);
+ else
+ num_posted = 0;
+
+ return num_posted;
+}
+
+static uint64_t
+lpfc_get_wwpn(struct lpfc_hba *phba)
+{
+ uint64_t wwn;
+ int rc;
+ LPFC_MBOXQ_t *mboxq;
+ MAILBOX_t *mb;
+
+ mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!mboxq)
+ return (uint64_t)-1;
+
+ /* First get WWN of HBA instance */
+ lpfc_read_nv(phba, mboxq);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6019 Mailbox failed , mbxCmd x%x "
+ "READ_NV, mbxStatus x%x\n",
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ bf_get(lpfc_mqe_status, &mboxq->u.mqe));
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return (uint64_t) -1;
+ }
+ mb = &mboxq->u.mb;
+ memcpy(&wwn, (char *)mb->un.varRDnvp.portname, sizeof(uint64_t));
+ /* wwn is WWPN of HBA instance */
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return be64_to_cpu(wwn);
+ else
+ return rol64(wwn, 32);
+}
+
+static unsigned short lpfc_get_sg_tablesize(struct lpfc_hba *phba)
+{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ if (phba->cfg_xpsgl && !phba->nvmet_support)
+ return LPFC_MAX_SG_TABLESIZE;
+ else
+ return phba->cfg_scsi_seg_cnt;
+ else
+ return phba->cfg_sg_seg_cnt;
+}
+
+/**
+ * lpfc_vmid_res_alloc - Allocates resources for VMID
+ * @phba: pointer to lpfc hba data structure.
+ * @vport: pointer to vport data structure
+ *
+ * This routine allocated the resources needed for the VMID.
+ *
+ * Return codes
+ * 0 on Success
+ * Non-0 on Failure
+ */
+static int
+lpfc_vmid_res_alloc(struct lpfc_hba *phba, struct lpfc_vport *vport)
+{
+ /* VMID feature is supported only on SLI4 */
+ if (phba->sli_rev == LPFC_SLI_REV3) {
+ phba->cfg_vmid_app_header = 0;
+ phba->cfg_vmid_priority_tagging = 0;
+ }
+
+ if (lpfc_is_vmid_enabled(phba)) {
+ vport->vmid =
+ kcalloc(phba->cfg_max_vmid, sizeof(struct lpfc_vmid),
+ GFP_KERNEL);
+ if (!vport->vmid)
+ return -ENOMEM;
+
+ rwlock_init(&vport->vmid_lock);
+
+ /* Set the VMID parameters for the vport */
+ vport->vmid_priority_tagging = phba->cfg_vmid_priority_tagging;
+ vport->vmid_inactivity_timeout =
+ phba->cfg_vmid_inactivity_timeout;
+ vport->max_vmid = phba->cfg_max_vmid;
+ vport->cur_vmid_cnt = 0;
+
+ vport->vmid_priority_range = bitmap_zalloc
+ (LPFC_VMID_MAX_PRIORITY_RANGE, GFP_KERNEL);
+
+ if (!vport->vmid_priority_range) {
+ kfree(vport->vmid);
+ return -ENOMEM;
+ }
+
+ hash_init(vport->hash_table);
+ }
return 0;
}
@@ -2713,35 +4684,122 @@ struct lpfc_vport *
lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
{
struct lpfc_vport *vport;
- struct Scsi_Host *shost;
+ struct Scsi_Host *shost = NULL;
+ struct scsi_host_template *template;
int error = 0;
+ int i;
+ uint64_t wwn;
+ bool use_no_reset_hba = false;
+ int rc;
+ u8 if_type;
+
+ if (lpfc_no_hba_reset_cnt) {
+ if (phba->sli_rev < LPFC_SLI_REV4 &&
+ dev == &phba->pcidev->dev) {
+ /* Reset the port first */
+ lpfc_sli_brdrestart(phba);
+ rc = lpfc_sli_chipset_init(phba);
+ if (rc)
+ return NULL;
+ }
+ wwn = lpfc_get_wwpn(phba);
+ }
- if (dev != &phba->pcidev->dev)
- shost = scsi_host_alloc(&lpfc_vport_template,
- sizeof(struct lpfc_vport));
- else
- shost = scsi_host_alloc(&lpfc_template,
- sizeof(struct lpfc_vport));
+ for (i = 0; i < lpfc_no_hba_reset_cnt; i++) {
+ if (wwn == lpfc_no_hba_reset[i]) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6020 Setting use_no_reset port=%llx\n",
+ wwn);
+ use_no_reset_hba = true;
+ break;
+ }
+ }
+
+ /* Seed template for SCSI host registration */
+ if (dev == &phba->pcidev->dev) {
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) {
+ /* Seed physical port template */
+ template = &lpfc_template;
+
+ if (use_no_reset_hba)
+ /* template is for a no reset SCSI Host */
+ template->eh_host_reset_handler = NULL;
+
+ /* Seed updated value of sg_tablesize */
+ template->sg_tablesize = lpfc_get_sg_tablesize(phba);
+ } else {
+ /* NVMET is for physical port only */
+ template = &lpfc_template_nvme;
+ }
+ } else {
+ /* Seed vport template */
+ template = &lpfc_vport_template;
+
+ /* Seed updated value of sg_tablesize */
+ template->sg_tablesize = lpfc_get_sg_tablesize(phba);
+ }
+
+ shost = scsi_host_alloc(template, sizeof(struct lpfc_vport));
if (!shost)
goto out;
vport = (struct lpfc_vport *) shost->hostdata;
vport->phba = phba;
- vport->load_flag |= FC_LOADING;
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+ set_bit(FC_LOADING, &vport->load_flag);
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
vport->fc_rscn_flush = 0;
-
+ atomic_set(&vport->fc_plogi_cnt, 0);
+ atomic_set(&vport->fc_adisc_cnt, 0);
+ atomic_set(&vport->fc_reglogin_cnt, 0);
+ atomic_set(&vport->fc_prli_cnt, 0);
+ atomic_set(&vport->fc_unmap_cnt, 0);
+ atomic_set(&vport->fc_map_cnt, 0);
+ atomic_set(&vport->fc_npr_cnt, 0);
+ atomic_set(&vport->fc_unused_cnt, 0);
lpfc_get_vport_cfgparam(vport);
+
+ /* Adjust value in vport */
+ vport->cfg_enable_fc4_type = phba->cfg_enable_fc4_type;
+
shost->unique_id = instance;
shost->max_id = LPFC_MAX_TARGET;
shost->max_lun = vport->cfg_max_luns;
shost->this_id = -1;
- shost->max_cmd_len = 16;
+
+ /* Set max_cmd_len applicable to ASIC support */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ if_type = bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf);
+ switch (if_type) {
+ case LPFC_SLI_INTF_IF_TYPE_2:
+ fallthrough;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ shost->max_cmd_len = LPFC_FCP_CDB_LEN_32;
+ break;
+ default:
+ shost->max_cmd_len = LPFC_FCP_CDB_LEN;
+ break;
+ }
+ } else {
+ shost->max_cmd_len = LPFC_FCP_CDB_LEN;
+ }
+
if (phba->sli_rev == LPFC_SLI_REV4) {
+ if (!phba->cfg_fcp_mq_threshold ||
+ phba->cfg_fcp_mq_threshold > phba->cfg_hdw_queue)
+ phba->cfg_fcp_mq_threshold = phba->cfg_hdw_queue;
+
+ shost->nr_hw_queues = min_t(int, 2 * num_possible_nodes(),
+ phba->cfg_fcp_mq_threshold);
+
shost->dma_boundary =
phba->sli4_hba.pc_sli4_params.sge_supp_len-1;
- shost->sg_tablesize = phba->cfg_sg_seg_cnt;
- }
+ } else
+ /* SLI-3 has a limited number of hardware queues (3),
+ * thus there is only one for FCP processing.
+ */
+ shost->nr_hw_queues = 1;
/*
* Set initial can_queue value since 0 is no longer supported and
@@ -2757,36 +4815,45 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
vport->port_type = LPFC_PHYSICAL_PORT;
}
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_FCP,
+ "9081 CreatePort TMPLATE type %x TBLsize %d "
+ "SEGcnt %d/%d\n",
+ vport->port_type, shost->sg_tablesize,
+ phba->cfg_scsi_seg_cnt, phba->cfg_sg_seg_cnt);
+
+ /* Allocate the resources for VMID */
+ rc = lpfc_vmid_res_alloc(phba, vport);
+
+ if (rc)
+ goto out_put_shost;
+
/* Initialize all internally managed lists. */
INIT_LIST_HEAD(&vport->fc_nodes);
+ spin_lock_init(&vport->fc_nodes_list_lock);
INIT_LIST_HEAD(&vport->rcv_buffer_list);
spin_lock_init(&vport->work_port_lock);
- init_timer(&vport->fc_disctmo);
- vport->fc_disctmo.function = lpfc_disc_timeout;
- vport->fc_disctmo.data = (unsigned long)vport;
+ timer_setup(&vport->fc_disctmo, lpfc_disc_timeout, 0);
- init_timer(&vport->fc_fdmitmo);
- vport->fc_fdmitmo.function = lpfc_fdmi_tmo;
- vport->fc_fdmitmo.data = (unsigned long)vport;
+ timer_setup(&vport->els_tmofunc, lpfc_els_timeout, 0);
- init_timer(&vport->els_tmofunc);
- vport->els_tmofunc.function = lpfc_els_timeout;
- vport->els_tmofunc.data = (unsigned long)vport;
+ timer_setup(&vport->delayed_disc_tmo, lpfc_delayed_disc_tmo, 0);
- init_timer(&vport->delayed_disc_tmo);
- vport->delayed_disc_tmo.function = lpfc_delayed_disc_tmo;
- vport->delayed_disc_tmo.data = (unsigned long)vport;
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+ lpfc_setup_bg(phba, shost);
error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev);
if (error)
- goto out_put_shost;
+ goto out_free_vmid;
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(&phba->port_list_lock);
list_add_tail(&vport->listentry, &phba->port_list);
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(&phba->port_list_lock);
return vport;
+out_free_vmid:
+ kfree(vport->vmid);
+ bitmap_free(vport->vmid_priority_range);
out_put_shost:
scsi_host_put(shost);
out:
@@ -2810,9 +4877,9 @@ destroy_port(struct lpfc_vport *vport)
fc_remove_host(shost);
scsi_remove_host(shost);
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(&phba->port_list_lock);
list_del_init(&vport->listentry);
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(&phba->port_list_lock);
lpfc_cleanup(vport);
return;
@@ -2831,14 +4898,10 @@ destroy_port(struct lpfc_vport *vport)
int
lpfc_get_instance(void)
{
- int instance = 0;
+ int ret;
- /* Assign an unused number */
- if (!idr_pre_get(&lpfc_hba_index, GFP_KERNEL))
- return -1;
- if (idr_get_new(&lpfc_hba_index, NULL, &instance))
- return -1;
- return instance;
+ ret = idr_alloc(&lpfc_hba_index, NULL, 0, 0, GFP_KERNEL);
+ return ret < 0 ? -1 : ret;
}
/**
@@ -2864,18 +4927,19 @@ int lpfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
spin_lock_irq(shost->host_lock);
- if (vport->load_flag & FC_UNLOADING) {
+ if (test_bit(FC_UNLOADING, &vport->load_flag)) {
stat = 1;
goto finished;
}
- if (time >= 30 * HZ) {
+ if (time >= secs_to_jiffies(30)) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0461 Scanning longer than 30 "
"seconds. Continuing initialization\n");
stat = 1;
goto finished;
}
- if (time >= 15 * HZ && phba->link_state <= LPFC_LINK_DOWN) {
+ if (time >= secs_to_jiffies(15) &&
+ phba->link_state <= LPFC_LINK_DOWN) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0465 Link down longer than 15 "
"seconds. Continuing initialization\n");
@@ -2887,7 +4951,8 @@ int lpfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
goto finished;
if (vport->num_disc_nodes || vport->fc_prli_sent)
goto finished;
- if (vport->fc_map_cnt == 0 && time < 2 * HZ)
+ if (!atomic_read(&vport->fc_map_cnt) &&
+ time < secs_to_jiffies(2))
goto finished;
if ((phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) != 0)
goto finished;
@@ -2899,6 +4964,41 @@ finished:
return stat;
}
+static void lpfc_host_supported_speeds_set(struct Scsi_Host *shost)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ fc_host_supported_speeds(shost) = 0;
+ /*
+ * Avoid reporting supported link speed for FCoE as it can't be
+ * controlled via FCoE.
+ */
+ if (test_bit(HBA_FCOE_MODE, &phba->hba_flag))
+ return;
+
+ if (phba->lmt & LMT_256Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_256GBIT;
+ if (phba->lmt & LMT_128Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_128GBIT;
+ if (phba->lmt & LMT_64Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_64GBIT;
+ if (phba->lmt & LMT_32Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_32GBIT;
+ if (phba->lmt & LMT_16Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_16GBIT;
+ if (phba->lmt & LMT_10Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_10GBIT;
+ if (phba->lmt & LMT_8Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_8GBIT;
+ if (phba->lmt & LMT_4Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_4GBIT;
+ if (phba->lmt & LMT_2Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_2GBIT;
+ if (phba->lmt & LMT_1Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_1GBIT;
+}
+
/**
* lpfc_host_attrib_init - Initialize SCSI host attributes on a FC port
* @shost: pointer to SCSI host data structure.
@@ -2926,17 +5026,7 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
lpfc_vport_symbolic_node_name(vport, fc_host_symbolic_name(shost),
sizeof fc_host_symbolic_name(shost));
- fc_host_supported_speeds(shost) = 0;
- if (phba->lmt & LMT_10Gb)
- fc_host_supported_speeds(shost) |= FC_PORTSPEED_10GBIT;
- if (phba->lmt & LMT_8Gb)
- fc_host_supported_speeds(shost) |= FC_PORTSPEED_8GBIT;
- if (phba->lmt & LMT_4Gb)
- fc_host_supported_speeds(shost) |= FC_PORTSPEED_4GBIT;
- if (phba->lmt & LMT_2Gb)
- fc_host_supported_speeds(shost) |= FC_PORTSPEED_2GBIT;
- if (phba->lmt & LMT_1Gb)
- fc_host_supported_speeds(shost) |= FC_PORTSPEED_1GBIT;
+ lpfc_host_supported_speeds_set(shost);
fc_host_maxframe_size(shost) =
(((uint32_t) vport->fc_sparam.cmn.bbRcvSizeMsb & 0x0F) << 8) |
@@ -2951,9 +5041,7 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
fc_host_active_fc4s(shost)[7] = 1;
fc_host_max_npiv_vports(shost) = phba->max_vpi;
- spin_lock_irq(shost->host_lock);
- vport->load_flag &= ~FC_LOADING;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_LOADING, &vport->load_flag);
}
/**
@@ -2992,7 +5080,8 @@ lpfc_stop_port_s4(struct lpfc_hba *phba)
{
/* Reset some HBA SLI4 setup states */
lpfc_stop_hba_timers(phba);
- phba->pport->work_port_events = 0;
+ if (phba->pport)
+ phba->pport->work_port_events = 0;
phba->sli4_hba.intr_enable = 0;
}
@@ -3007,6 +5096,9 @@ void
lpfc_stop_port(struct lpfc_hba *phba)
{
phba->lpfc_stop_port(phba);
+
+ if (phba->wq)
+ flush_workqueue(phba->wq);
}
/**
@@ -3032,7 +5124,7 @@ lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *phba)
/**
* lpfc_sli4_fcf_redisc_wait_tmo - FCF table rediscover wait timeout
- * @ptr: Map to lpfc_hba data structure pointer.
+ * @t: Timer context used to obtain the pointer to lpfc hba data structure.
*
* This routine is invoked when waiting for FCF table rediscover has been
* timed out. If new FCF record(s) has (have) been discovered during the
@@ -3040,10 +5132,10 @@ lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *phba)
* list, and then worker thread shall be waked up for processing from the
* worker thread context.
**/
-void
-lpfc_sli4_fcf_redisc_wait_tmo(unsigned long ptr)
+static void
+lpfc_sli4_fcf_redisc_wait_tmo(struct timer_list *t)
{
- struct lpfc_hba *phba = (struct lpfc_hba *)ptr;
+ struct lpfc_hba *phba = timer_container_of(phba, t, fcf.redisc_wait);
/* Don't send FCF rediscovery event if timer cancelled */
spin_lock_irq(&phba->hbalock);
@@ -3063,36 +5155,74 @@ lpfc_sli4_fcf_redisc_wait_tmo(unsigned long ptr)
}
/**
+ * lpfc_vmid_poll - VMID timeout detection
+ * @t: Timer context used to obtain the pointer to lpfc hba data structure.
+ *
+ * This routine is invoked when there is no I/O on by a VM for the specified
+ * amount of time. When this situation is detected, the VMID has to be
+ * deregistered from the switch and all the local resources freed. The VMID
+ * will be reassigned to the VM once the I/O begins.
+ **/
+static void
+lpfc_vmid_poll(struct timer_list *t)
+{
+ struct lpfc_hba *phba = timer_container_of(phba, t,
+ inactive_vmid_poll);
+ u32 wake_up = 0;
+
+ /* check if there is a need to issue QFPA */
+ if (phba->pport->vmid_priority_tagging) {
+ wake_up = 1;
+ phba->pport->work_port_events |= WORKER_CHECK_VMID_ISSUE_QFPA;
+ }
+
+ /* Is the vmid inactivity timer enabled */
+ if (phba->pport->vmid_inactivity_timeout ||
+ test_bit(FC_DEREGISTER_ALL_APP_ID, &phba->pport->load_flag)) {
+ wake_up = 1;
+ phba->pport->work_port_events |= WORKER_CHECK_INACTIVE_VMID;
+ }
+
+ if (wake_up)
+ lpfc_worker_wake_up(phba);
+
+ /* restart the timer for the next iteration */
+ mod_timer(&phba->inactive_vmid_poll,
+ jiffies + secs_to_jiffies(LPFC_VMID_TIMER));
+}
+
+/**
* lpfc_sli4_parse_latt_fault - Parse sli4 link-attention link fault code
* @phba: pointer to lpfc hba data structure.
* @acqe_link: pointer to the async link completion queue entry.
*
- * This routine is to parse the SLI4 link-attention link fault code and
- * translate it into the base driver's read link attention mailbox command
- * status.
- *
- * Return: Link-attention status in terms of base driver's coding.
+ * This routine is to parse the SLI4 link-attention link fault code.
**/
-static uint16_t
+static void
lpfc_sli4_parse_latt_fault(struct lpfc_hba *phba,
struct lpfc_acqe_link *acqe_link)
{
- uint16_t latt_fault;
-
- switch (bf_get(lpfc_acqe_link_fault, acqe_link)) {
- case LPFC_ASYNC_LINK_FAULT_NONE:
- case LPFC_ASYNC_LINK_FAULT_LOCAL:
- case LPFC_ASYNC_LINK_FAULT_REMOTE:
- latt_fault = 0;
+ switch (bf_get(lpfc_acqe_fc_la_att_type, acqe_link)) {
+ case LPFC_FC_LA_TYPE_LINK_DOWN:
+ case LPFC_FC_LA_TYPE_TRUNKING_EVENT:
+ case LPFC_FC_LA_TYPE_ACTIVATE_FAIL:
+ case LPFC_FC_LA_TYPE_LINK_RESET_PRTCL_EVT:
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0398 Invalid link fault code: x%x\n",
- bf_get(lpfc_acqe_link_fault, acqe_link));
- latt_fault = MBXERR_ERROR;
+ switch (bf_get(lpfc_acqe_link_fault, acqe_link)) {
+ case LPFC_ASYNC_LINK_FAULT_NONE:
+ case LPFC_ASYNC_LINK_FAULT_LOCAL:
+ case LPFC_ASYNC_LINK_FAULT_REMOTE:
+ case LPFC_ASYNC_LINK_FAULT_LR_LRR:
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0398 Unknown link fault code: x%x\n",
+ bf_get(lpfc_acqe_link_fault, acqe_link));
+ break;
+ }
break;
}
- return latt_fault;
}
/**
@@ -3124,7 +5254,7 @@ lpfc_sli4_parse_latt_type(struct lpfc_hba *phba,
att_type = LPFC_ATT_LINK_UP;
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0399 Invalid link attention type: x%x\n",
bf_get(lpfc_acqe_link_status, acqe_link));
att_type = LPFC_ATT_RESERVED;
@@ -3134,41 +5264,148 @@ lpfc_sli4_parse_latt_type(struct lpfc_hba *phba,
}
/**
- * lpfc_sli4_parse_latt_link_speed - Parse sli4 link-attention link speed
+ * lpfc_sli_port_speed_get - Get sli3 link speed code to link speed
* @phba: pointer to lpfc hba data structure.
- * @acqe_link: pointer to the async link completion queue entry.
*
- * This routine is to parse the SLI4 link-attention link speed and translate
- * it into the base driver's link-attention link speed coding.
+ * This routine is to get an SLI3 FC port's link speed in Mbps.
*
- * Return: Link-attention link speed in terms of base driver's coding.
+ * Return: link speed in terms of Mbps.
**/
-static uint8_t
-lpfc_sli4_parse_latt_link_speed(struct lpfc_hba *phba,
- struct lpfc_acqe_link *acqe_link)
+uint32_t
+lpfc_sli_port_speed_get(struct lpfc_hba *phba)
{
- uint8_t link_speed;
+ uint32_t link_speed;
- switch (bf_get(lpfc_acqe_link_speed, acqe_link)) {
- case LPFC_ASYNC_LINK_SPEED_ZERO:
- case LPFC_ASYNC_LINK_SPEED_10MBPS:
- case LPFC_ASYNC_LINK_SPEED_100MBPS:
- link_speed = LPFC_LINK_SPEED_UNKNOWN;
- break;
- case LPFC_ASYNC_LINK_SPEED_1GBPS:
- link_speed = LPFC_LINK_SPEED_1GHZ;
+ if (!lpfc_is_link_up(phba))
+ return 0;
+
+ if (phba->sli_rev <= LPFC_SLI_REV3) {
+ switch (phba->fc_linkspeed) {
+ case LPFC_LINK_SPEED_1GHZ:
+ link_speed = 1000;
+ break;
+ case LPFC_LINK_SPEED_2GHZ:
+ link_speed = 2000;
+ break;
+ case LPFC_LINK_SPEED_4GHZ:
+ link_speed = 4000;
+ break;
+ case LPFC_LINK_SPEED_8GHZ:
+ link_speed = 8000;
+ break;
+ case LPFC_LINK_SPEED_10GHZ:
+ link_speed = 10000;
+ break;
+ case LPFC_LINK_SPEED_16GHZ:
+ link_speed = 16000;
+ break;
+ default:
+ link_speed = 0;
+ }
+ } else {
+ if (phba->sli4_hba.link_state.logical_speed)
+ link_speed =
+ phba->sli4_hba.link_state.logical_speed;
+ else
+ link_speed = phba->sli4_hba.link_state.speed;
+ }
+ return link_speed;
+}
+
+/**
+ * lpfc_sli4_port_speed_parse - Parse async evt link speed code to link speed
+ * @phba: pointer to lpfc hba data structure.
+ * @evt_code: asynchronous event code.
+ * @speed_code: asynchronous event link speed code.
+ *
+ * This routine is to parse the giving SLI4 async event link speed code into
+ * value of Mbps for the link speed.
+ *
+ * Return: link speed in terms of Mbps.
+ **/
+static uint32_t
+lpfc_sli4_port_speed_parse(struct lpfc_hba *phba, uint32_t evt_code,
+ uint8_t speed_code)
+{
+ uint32_t port_speed;
+
+ switch (evt_code) {
+ case LPFC_TRAILER_CODE_LINK:
+ switch (speed_code) {
+ case LPFC_ASYNC_LINK_SPEED_ZERO:
+ port_speed = 0;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_10MBPS:
+ port_speed = 10;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100MBPS:
+ port_speed = 100;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_1GBPS:
+ port_speed = 1000;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_10GBPS:
+ port_speed = 10000;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_20GBPS:
+ port_speed = 20000;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_25GBPS:
+ port_speed = 25000;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_40GBPS:
+ port_speed = 40000;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100GBPS:
+ port_speed = 100000;
+ break;
+ default:
+ port_speed = 0;
+ }
break;
- case LPFC_ASYNC_LINK_SPEED_10GBPS:
- link_speed = LPFC_LINK_SPEED_10GHZ;
+ case LPFC_TRAILER_CODE_FC:
+ switch (speed_code) {
+ case LPFC_FC_LA_SPEED_UNKNOWN:
+ port_speed = 0;
+ break;
+ case LPFC_FC_LA_SPEED_1G:
+ port_speed = 1000;
+ break;
+ case LPFC_FC_LA_SPEED_2G:
+ port_speed = 2000;
+ break;
+ case LPFC_FC_LA_SPEED_4G:
+ port_speed = 4000;
+ break;
+ case LPFC_FC_LA_SPEED_8G:
+ port_speed = 8000;
+ break;
+ case LPFC_FC_LA_SPEED_10G:
+ port_speed = 10000;
+ break;
+ case LPFC_FC_LA_SPEED_16G:
+ port_speed = 16000;
+ break;
+ case LPFC_FC_LA_SPEED_32G:
+ port_speed = 32000;
+ break;
+ case LPFC_FC_LA_SPEED_64G:
+ port_speed = 64000;
+ break;
+ case LPFC_FC_LA_SPEED_128G:
+ port_speed = 128000;
+ break;
+ case LPFC_FC_LA_SPEED_256G:
+ port_speed = 256000;
+ break;
+ default:
+ port_speed = 0;
+ }
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0483 Invalid link-attention link speed: x%x\n",
- bf_get(lpfc_acqe_link_speed, acqe_link));
- link_speed = LPFC_LINK_SPEED_UNKNOWN;
- break;
+ port_speed = 0;
}
- return link_speed;
+ return port_speed;
}
/**
@@ -3182,7 +5419,6 @@ static void
lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
struct lpfc_acqe_link *acqe_link)
{
- struct lpfc_dmabuf *mp;
LPFC_MBOXQ_t *pmb;
MAILBOX_t *mb;
struct lpfc_mbx_read_top *la;
@@ -3195,40 +5431,36 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
phba->fcoe_eventtag = acqe_link->event_tag;
pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0395 The mboxq allocation failed\n");
return;
}
- mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!mp) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0396 The lpfc_dmabuf allocation failed\n");
+
+ rc = lpfc_mbox_rsrc_prep(phba, pmb);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0396 mailbox allocation failed\n");
goto out_free_pmb;
}
- mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
- if (!mp->virt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0397 The mbuf allocation failed\n");
- goto out_free_dmabuf;
- }
/* Cleanup any outstanding ELS commands */
lpfc_els_flush_all_cmd(phba);
/* Block ELS IOCBs until we have done process link event */
- phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
+ phba->sli4_hba.els_wq->pring->flag |= LPFC_STOP_IOCB_EVENT;
/* Update link event statistics */
phba->sli.slistat.link_event++;
/* Create lpfc_handle_latt mailbox command from link ACQE */
- lpfc_read_topology(phba, pmb, mp);
+ lpfc_read_topology(phba, pmb, pmb->ctx_buf);
pmb->mbox_cmpl = lpfc_mbx_cmpl_read_topology;
pmb->vport = phba->pport;
/* Keep the link status for extra SLI4 state machine reference */
phba->sli4_hba.link_state.speed =
- bf_get(lpfc_acqe_link_speed, acqe_link);
+ lpfc_sli4_port_speed_parse(phba, LPFC_TRAILER_CODE_LINK,
+ bf_get(lpfc_acqe_link_speed, acqe_link));
phba->sli4_hba.link_state.duplex =
bf_get(lpfc_acqe_link_duplex, acqe_link);
phba->sli4_hba.link_state.status =
@@ -3240,7 +5472,8 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
phba->sli4_hba.link_state.fault =
bf_get(lpfc_acqe_link_fault, acqe_link);
phba->sli4_hba.link_state.logical_speed =
- bf_get(lpfc_acqe_logical_link_speed, acqe_link);
+ bf_get(lpfc_acqe_logical_link_speed, acqe_link) * 10;
+
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"2900 Async FC/FCoE Link event - Speed:%dGBit "
"duplex:x%x LA Type:x%x Port Type:%d Port Number:%d "
@@ -3250,16 +5483,16 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
phba->sli4_hba.link_state.status,
phba->sli4_hba.link_state.type,
phba->sli4_hba.link_state.number,
- phba->sli4_hba.link_state.logical_speed * 10,
+ phba->sli4_hba.link_state.logical_speed,
phba->sli4_hba.link_state.fault);
/*
* For FC Mode: issue the READ_TOPOLOGY mailbox command to fetch
* topology info. Note: Optional for non FC-AL ports.
*/
- if (!(phba->hba_flag & HBA_FCOE_MODE)) {
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED)
- goto out_free_dmabuf;
+ goto out_free_pmb;
return;
}
/*
@@ -3267,18 +5500,21 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
* the READ_TOPOLOGY completion routine to continue without actually
* sending the READ_TOPOLOGY mailbox command to the port.
*/
- /* Parse and translate status field */
+ /* Initialize completion status */
mb = &pmb->u.mb;
- mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba, acqe_link);
+ mb->mbxStatus = MBX_SUCCESS;
+
+ /* Parse port fault information field */
+ lpfc_sli4_parse_latt_fault(phba, acqe_link);
/* Parse and translate link attention fields */
la = (struct lpfc_mbx_read_top *) &pmb->u.mb.un.varReadTop;
la->eventTag = acqe_link->event_tag;
bf_set(lpfc_mbx_read_top_att_type, la, att_type);
bf_set(lpfc_mbx_read_top_link_spd, la,
- lpfc_sli4_parse_latt_link_speed(phba, acqe_link));
+ (bf_get(lpfc_acqe_link_speed, acqe_link)));
- /* Fake the the following irrelvant fields */
+ /* Fake the following irrelevant fields */
bf_set(lpfc_mbx_read_top_topology, la, LPFC_TOPOLOGY_PT_PT);
bf_set(lpfc_mbx_read_top_alpa_granted, la, 0);
bf_set(lpfc_mbx_read_top_il, la, 0);
@@ -3291,13 +5527,692 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
return;
-out_free_dmabuf:
- kfree(mp);
out_free_pmb:
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
+}
+
+/**
+ * lpfc_async_link_speed_to_read_top - Parse async evt link speed code to read
+ * topology.
+ * @phba: pointer to lpfc hba data structure.
+ * @speed_code: asynchronous event link speed code.
+ *
+ * This routine is to parse the giving SLI4 async event link speed code into
+ * value of Read topology link speed.
+ *
+ * Return: link speed in terms of Read topology.
+ **/
+static uint8_t
+lpfc_async_link_speed_to_read_top(struct lpfc_hba *phba, uint8_t speed_code)
+{
+ uint8_t port_speed;
+
+ switch (speed_code) {
+ case LPFC_FC_LA_SPEED_1G:
+ port_speed = LPFC_LINK_SPEED_1GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_2G:
+ port_speed = LPFC_LINK_SPEED_2GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_4G:
+ port_speed = LPFC_LINK_SPEED_4GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_8G:
+ port_speed = LPFC_LINK_SPEED_8GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_16G:
+ port_speed = LPFC_LINK_SPEED_16GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_32G:
+ port_speed = LPFC_LINK_SPEED_32GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_64G:
+ port_speed = LPFC_LINK_SPEED_64GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_128G:
+ port_speed = LPFC_LINK_SPEED_128GHZ;
+ break;
+ case LPFC_FC_LA_SPEED_256G:
+ port_speed = LPFC_LINK_SPEED_256GHZ;
+ break;
+ default:
+ port_speed = 0;
+ break;
+ }
+
+ return port_speed;
+}
+
+void
+lpfc_cgn_dump_rxmonitor(struct lpfc_hba *phba)
+{
+ if (!phba->rx_monitor) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "4411 Rx Monitor Info is empty.\n");
+ } else {
+ lpfc_rx_monitor_report(phba, phba->rx_monitor, NULL, 0,
+ LPFC_MAX_RXMONITOR_DUMP);
+ }
}
/**
+ * lpfc_cgn_update_stat - Save data into congestion stats buffer
+ * @phba: pointer to lpfc hba data structure.
+ * @dtag: FPIN descriptor received
+ *
+ * Increment the FPIN received counter/time when it happens.
+ */
+void
+lpfc_cgn_update_stat(struct lpfc_hba *phba, uint32_t dtag)
+{
+ struct lpfc_cgn_info *cp;
+ u32 value;
+
+ /* Make sure we have a congestion info buffer */
+ if (!phba->cgn_i)
+ return;
+ cp = (struct lpfc_cgn_info *)phba->cgn_i->virt;
+
+ /* Update congestion statistics */
+ switch (dtag) {
+ case ELS_DTAG_LNK_INTEGRITY:
+ le32_add_cpu(&cp->link_integ_notification, 1);
+ lpfc_cgn_update_tstamp(phba, &cp->stat_lnk);
+ break;
+ case ELS_DTAG_DELIVERY:
+ le32_add_cpu(&cp->delivery_notification, 1);
+ lpfc_cgn_update_tstamp(phba, &cp->stat_delivery);
+ break;
+ case ELS_DTAG_PEER_CONGEST:
+ le32_add_cpu(&cp->cgn_peer_notification, 1);
+ lpfc_cgn_update_tstamp(phba, &cp->stat_peer);
+ break;
+ case ELS_DTAG_CONGESTION:
+ le32_add_cpu(&cp->cgn_notification, 1);
+ lpfc_cgn_update_tstamp(phba, &cp->stat_fpin);
+ }
+ if (phba->cgn_fpin_frequency &&
+ phba->cgn_fpin_frequency != LPFC_FPIN_INIT_FREQ) {
+ value = LPFC_CGN_TIMER_TO_MIN / phba->cgn_fpin_frequency;
+ cp->cgn_stat_npm = value;
+ }
+
+ value = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ,
+ LPFC_CGN_CRC32_SEED);
+ cp->cgn_info_crc = cpu_to_le32(value);
+}
+
+/**
+ * lpfc_cgn_update_tstamp - Update cmf timestamp
+ * @phba: pointer to lpfc hba data structure.
+ * @ts: structure to write the timestamp to.
+ */
+void
+lpfc_cgn_update_tstamp(struct lpfc_hba *phba, struct lpfc_cgn_ts *ts)
+{
+ struct timespec64 cur_time;
+ struct tm tm_val;
+
+ ktime_get_real_ts64(&cur_time);
+ time64_to_tm(cur_time.tv_sec, 0, &tm_val);
+
+ ts->month = tm_val.tm_mon + 1;
+ ts->day = tm_val.tm_mday;
+ ts->year = tm_val.tm_year - 100;
+ ts->hour = tm_val.tm_hour;
+ ts->minute = tm_val.tm_min;
+ ts->second = tm_val.tm_sec;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "2646 Updated CMF timestamp : "
+ "%u/%u/%u %u:%u:%u\n",
+ ts->day, ts->month,
+ ts->year, ts->hour,
+ ts->minute, ts->second);
+}
+
+/**
+ * lpfc_cmf_stats_timer - Save data into registered congestion buffer
+ * @timer: Timer cookie to access lpfc private data
+ *
+ * Save the congestion event data every minute.
+ * On the hour collapse all the minute data into hour data. Every day
+ * collapse all the hour data into daily data. Separate driver
+ * and fabrc congestion event counters that will be saved out
+ * to the registered congestion buffer every minute.
+ */
+static enum hrtimer_restart
+lpfc_cmf_stats_timer(struct hrtimer *timer)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_cgn_info *cp;
+ uint32_t i, index;
+ uint16_t value, mvalue;
+ uint64_t bps;
+ uint32_t mbps;
+ uint32_t dvalue, wvalue, lvalue, avalue;
+ uint64_t latsum;
+ __le16 *ptr;
+ __le32 *lptr;
+ __le16 *mptr;
+
+ phba = container_of(timer, struct lpfc_hba, cmf_stats_timer);
+ /* Make sure we have a congestion info buffer */
+ if (!phba->cgn_i)
+ return HRTIMER_NORESTART;
+ cp = (struct lpfc_cgn_info *)phba->cgn_i->virt;
+
+ phba->cgn_evt_timestamp = jiffies +
+ msecs_to_jiffies(LPFC_CGN_TIMER_TO_MIN);
+ phba->cgn_evt_minute++;
+
+ /* We should get to this point in the routine on 1 minute intervals */
+ lpfc_cgn_update_tstamp(phba, &cp->base_time);
+
+ if (phba->cgn_fpin_frequency &&
+ phba->cgn_fpin_frequency != LPFC_FPIN_INIT_FREQ) {
+ value = LPFC_CGN_TIMER_TO_MIN / phba->cgn_fpin_frequency;
+ cp->cgn_stat_npm = value;
+ }
+
+ /* Read and clear the latency counters for this minute */
+ lvalue = atomic_read(&phba->cgn_latency_evt_cnt);
+ latsum = atomic64_read(&phba->cgn_latency_evt);
+ atomic_set(&phba->cgn_latency_evt_cnt, 0);
+ atomic64_set(&phba->cgn_latency_evt, 0);
+
+ /* We need to store MB/sec bandwidth in the congestion information.
+ * block_cnt is count of 512 byte blocks for the entire minute,
+ * bps will get bytes per sec before finally converting to MB/sec.
+ */
+ bps = div_u64(phba->rx_block_cnt, LPFC_SEC_MIN) * 512;
+ phba->rx_block_cnt = 0;
+ mvalue = bps / (1024 * 1024); /* convert to MB/sec */
+
+ /* Every minute */
+ /* cgn parameters */
+ cp->cgn_info_mode = phba->cgn_p.cgn_param_mode;
+ cp->cgn_info_level0 = phba->cgn_p.cgn_param_level0;
+ cp->cgn_info_level1 = phba->cgn_p.cgn_param_level1;
+ cp->cgn_info_level2 = phba->cgn_p.cgn_param_level2;
+
+ /* Fill in default LUN qdepth */
+ value = (uint16_t)(phba->pport->cfg_lun_queue_depth);
+ cp->cgn_lunq = cpu_to_le16(value);
+
+ /* Record congestion buffer info - every minute
+ * cgn_driver_evt_cnt (Driver events)
+ * cgn_fabric_warn_cnt (Congestion Warnings)
+ * cgn_latency_evt_cnt / cgn_latency_evt (IO Latency)
+ * cgn_fabric_alarm_cnt (Congestion Alarms)
+ */
+ index = ++cp->cgn_index_minute;
+ if (cp->cgn_index_minute == LPFC_MIN_HOUR) {
+ cp->cgn_index_minute = 0;
+ index = 0;
+ }
+
+ /* Get the number of driver events in this sample and reset counter */
+ dvalue = atomic_read(&phba->cgn_driver_evt_cnt);
+ atomic_set(&phba->cgn_driver_evt_cnt, 0);
+
+ /* Get the number of warning events - FPIN and Signal for this minute */
+ wvalue = 0;
+ if ((phba->cgn_reg_fpin & LPFC_CGN_FPIN_WARN) ||
+ phba->cgn_reg_signal == EDC_CG_SIG_WARN_ONLY ||
+ phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM)
+ wvalue = atomic_read(&phba->cgn_fabric_warn_cnt);
+ atomic_set(&phba->cgn_fabric_warn_cnt, 0);
+
+ /* Get the number of alarm events - FPIN and Signal for this minute */
+ avalue = 0;
+ if ((phba->cgn_reg_fpin & LPFC_CGN_FPIN_ALARM) ||
+ phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM)
+ avalue = atomic_read(&phba->cgn_fabric_alarm_cnt);
+ atomic_set(&phba->cgn_fabric_alarm_cnt, 0);
+
+ /* Collect the driver, warning, alarm and latency counts for this
+ * minute into the driver congestion buffer.
+ */
+ ptr = &cp->cgn_drvr_min[index];
+ value = (uint16_t)dvalue;
+ *ptr = cpu_to_le16(value);
+
+ ptr = &cp->cgn_warn_min[index];
+ value = (uint16_t)wvalue;
+ *ptr = cpu_to_le16(value);
+
+ ptr = &cp->cgn_alarm_min[index];
+ value = (uint16_t)avalue;
+ *ptr = cpu_to_le16(value);
+
+ lptr = &cp->cgn_latency_min[index];
+ if (lvalue) {
+ lvalue = (uint32_t)div_u64(latsum, lvalue);
+ *lptr = cpu_to_le32(lvalue);
+ } else {
+ *lptr = 0;
+ }
+
+ /* Collect the bandwidth value into the driver's congesion buffer. */
+ mptr = &cp->cgn_bw_min[index];
+ *mptr = cpu_to_le16(mvalue);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "2418 Congestion Info - minute (%d): %d %d %d %d %d\n",
+ index, dvalue, wvalue, *lptr, mvalue, avalue);
+
+ /* Every hour */
+ if ((phba->cgn_evt_minute % LPFC_MIN_HOUR) == 0) {
+ /* Record congestion buffer info - every hour
+ * Collapse all minutes into an hour
+ */
+ index = ++cp->cgn_index_hour;
+ if (cp->cgn_index_hour == LPFC_HOUR_DAY) {
+ cp->cgn_index_hour = 0;
+ index = 0;
+ }
+
+ dvalue = 0;
+ wvalue = 0;
+ lvalue = 0;
+ avalue = 0;
+ mvalue = 0;
+ mbps = 0;
+ for (i = 0; i < LPFC_MIN_HOUR; i++) {
+ dvalue += le16_to_cpu(cp->cgn_drvr_min[i]);
+ wvalue += le16_to_cpu(cp->cgn_warn_min[i]);
+ lvalue += le32_to_cpu(cp->cgn_latency_min[i]);
+ mbps += le16_to_cpu(cp->cgn_bw_min[i]);
+ avalue += le16_to_cpu(cp->cgn_alarm_min[i]);
+ }
+ if (lvalue) /* Avg of latency averages */
+ lvalue /= LPFC_MIN_HOUR;
+ if (mbps) /* Avg of Bandwidth averages */
+ mvalue = mbps / LPFC_MIN_HOUR;
+
+ lptr = &cp->cgn_drvr_hr[index];
+ *lptr = cpu_to_le32(dvalue);
+ lptr = &cp->cgn_warn_hr[index];
+ *lptr = cpu_to_le32(wvalue);
+ lptr = &cp->cgn_latency_hr[index];
+ *lptr = cpu_to_le32(lvalue);
+ mptr = &cp->cgn_bw_hr[index];
+ *mptr = cpu_to_le16(mvalue);
+ lptr = &cp->cgn_alarm_hr[index];
+ *lptr = cpu_to_le32(avalue);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "2419 Congestion Info - hour "
+ "(%d): %d %d %d %d %d\n",
+ index, dvalue, wvalue, lvalue, mvalue, avalue);
+ }
+
+ /* Every day */
+ if ((phba->cgn_evt_minute % LPFC_MIN_DAY) == 0) {
+ /* Record congestion buffer info - every hour
+ * Collapse all hours into a day. Rotate days
+ * after LPFC_MAX_CGN_DAYS.
+ */
+ index = ++cp->cgn_index_day;
+ if (cp->cgn_index_day == LPFC_MAX_CGN_DAYS) {
+ cp->cgn_index_day = 0;
+ index = 0;
+ }
+
+ dvalue = 0;
+ wvalue = 0;
+ lvalue = 0;
+ mvalue = 0;
+ mbps = 0;
+ avalue = 0;
+ for (i = 0; i < LPFC_HOUR_DAY; i++) {
+ dvalue += le32_to_cpu(cp->cgn_drvr_hr[i]);
+ wvalue += le32_to_cpu(cp->cgn_warn_hr[i]);
+ lvalue += le32_to_cpu(cp->cgn_latency_hr[i]);
+ mbps += le16_to_cpu(cp->cgn_bw_hr[i]);
+ avalue += le32_to_cpu(cp->cgn_alarm_hr[i]);
+ }
+ if (lvalue) /* Avg of latency averages */
+ lvalue /= LPFC_HOUR_DAY;
+ if (mbps) /* Avg of Bandwidth averages */
+ mvalue = mbps / LPFC_HOUR_DAY;
+
+ lptr = &cp->cgn_drvr_day[index];
+ *lptr = cpu_to_le32(dvalue);
+ lptr = &cp->cgn_warn_day[index];
+ *lptr = cpu_to_le32(wvalue);
+ lptr = &cp->cgn_latency_day[index];
+ *lptr = cpu_to_le32(lvalue);
+ mptr = &cp->cgn_bw_day[index];
+ *mptr = cpu_to_le16(mvalue);
+ lptr = &cp->cgn_alarm_day[index];
+ *lptr = cpu_to_le32(avalue);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "2420 Congestion Info - daily (%d): "
+ "%d %d %d %d %d\n",
+ index, dvalue, wvalue, lvalue, mvalue, avalue);
+ }
+
+ /* Use the frequency found in the last rcv'ed FPIN */
+ value = phba->cgn_fpin_frequency;
+ cp->cgn_warn_freq = cpu_to_le16(value);
+ cp->cgn_alarm_freq = cpu_to_le16(value);
+
+ lvalue = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ,
+ LPFC_CGN_CRC32_SEED);
+ cp->cgn_info_crc = cpu_to_le32(lvalue);
+
+ hrtimer_forward_now(timer, ktime_set(0, LPFC_SEC_MIN * NSEC_PER_SEC));
+
+ return HRTIMER_RESTART;
+}
+
+/**
+ * lpfc_calc_cmf_latency - latency from start of rxate timer interval
+ * @phba: The Hba for which this call is being executed.
+ *
+ * The routine calculates the latency from the beginning of the CMF timer
+ * interval to the current point in time. It is called from IO completion
+ * when we exceed our Bandwidth limitation for the time interval.
+ */
+uint32_t
+lpfc_calc_cmf_latency(struct lpfc_hba *phba)
+{
+ struct timespec64 cmpl_time;
+ uint32_t msec = 0;
+
+ ktime_get_real_ts64(&cmpl_time);
+
+ /* This routine works on a ms granularity so sec and usec are
+ * converted accordingly.
+ */
+ if (cmpl_time.tv_sec == phba->cmf_latency.tv_sec) {
+ msec = (cmpl_time.tv_nsec - phba->cmf_latency.tv_nsec) /
+ NSEC_PER_MSEC;
+ } else {
+ if (cmpl_time.tv_nsec >= phba->cmf_latency.tv_nsec) {
+ msec = (cmpl_time.tv_sec -
+ phba->cmf_latency.tv_sec) * MSEC_PER_SEC;
+ msec += ((cmpl_time.tv_nsec -
+ phba->cmf_latency.tv_nsec) / NSEC_PER_MSEC);
+ } else {
+ msec = (cmpl_time.tv_sec - phba->cmf_latency.tv_sec -
+ 1) * MSEC_PER_SEC;
+ msec += (((NSEC_PER_SEC - phba->cmf_latency.tv_nsec) +
+ cmpl_time.tv_nsec) / NSEC_PER_MSEC);
+ }
+ }
+ return msec;
+}
+
+/**
+ * lpfc_cmf_timer - This is the timer function for one congestion
+ * rate interval.
+ * @timer: Pointer to the high resolution timer that expired
+ */
+static enum hrtimer_restart
+lpfc_cmf_timer(struct hrtimer *timer)
+{
+ struct lpfc_hba *phba = container_of(timer, struct lpfc_hba,
+ cmf_timer);
+ struct rx_info_entry entry;
+ uint32_t io_cnt;
+ uint32_t busy, max_read;
+ uint64_t total, rcv, lat, mbpi, extra, cnt;
+ int timer_interval = LPFC_CMF_INTERVAL;
+ uint32_t ms;
+ struct lpfc_cgn_stat *cgs;
+ int cpu;
+
+ /* Only restart the timer if congestion mgmt is on */
+ if (phba->cmf_active_mode == LPFC_CFG_OFF ||
+ !phba->cmf_latency.tv_sec) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6224 CMF timer exit: %d %lld\n",
+ phba->cmf_active_mode,
+ (uint64_t)phba->cmf_latency.tv_sec);
+ return HRTIMER_NORESTART;
+ }
+
+ /* If pport is not ready yet, just exit and wait for
+ * the next timer cycle to hit.
+ */
+ if (!phba->pport)
+ goto skip;
+
+ /* Do not block SCSI IO while in the timer routine since
+ * total_bytes will be cleared
+ */
+ atomic_set(&phba->cmf_stop_io, 1);
+
+ /* First we need to calculate the actual ms between
+ * the last timer interrupt and this one. We ask for
+ * LPFC_CMF_INTERVAL, however the actual time may
+ * vary depending on system overhead.
+ */
+ ms = lpfc_calc_cmf_latency(phba);
+
+
+ /* Immediately after we calculate the time since the last
+ * timer interrupt, set the start time for the next
+ * interrupt
+ */
+ ktime_get_real_ts64(&phba->cmf_latency);
+
+ phba->cmf_link_byte_count =
+ div_u64(phba->cmf_max_line_rate * LPFC_CMF_INTERVAL, 1000);
+
+ /* Collect all the stats from the prior timer interval */
+ total = 0;
+ io_cnt = 0;
+ lat = 0;
+ rcv = 0;
+ for_each_present_cpu(cpu) {
+ cgs = per_cpu_ptr(phba->cmf_stat, cpu);
+ total += atomic64_xchg(&cgs->total_bytes, 0);
+ io_cnt += atomic_xchg(&cgs->rx_io_cnt, 0);
+ lat += atomic64_xchg(&cgs->rx_latency, 0);
+ rcv += atomic64_xchg(&cgs->rcv_bytes, 0);
+ }
+
+ /* Before we issue another CMF_SYNC_WQE, retrieve the BW
+ * returned from the last CMF_SYNC_WQE issued, from
+ * cmf_last_sync_bw. This will be the target BW for
+ * this next timer interval.
+ */
+ if (phba->cmf_active_mode == LPFC_CFG_MANAGED &&
+ phba->link_state != LPFC_LINK_DOWN &&
+ test_bit(HBA_SETUP, &phba->hba_flag)) {
+ mbpi = phba->cmf_last_sync_bw;
+ phba->cmf_last_sync_bw = 0;
+ extra = 0;
+
+ /* Calculate any extra bytes needed to account for the
+ * timer accuracy. If we are less than LPFC_CMF_INTERVAL
+ * calculate the adjustment needed for total to reflect
+ * a full LPFC_CMF_INTERVAL.
+ */
+ if (ms && ms < LPFC_CMF_INTERVAL) {
+ cnt = div_u64(total, ms); /* bytes per ms */
+ cnt *= LPFC_CMF_INTERVAL; /* what total should be */
+ extra = cnt - total;
+ }
+ lpfc_issue_cmf_sync_wqe(phba, LPFC_CMF_INTERVAL, total + extra);
+ } else {
+ /* For Monitor mode or link down we want mbpi
+ * to be the full link speed
+ */
+ mbpi = phba->cmf_link_byte_count;
+ extra = 0;
+ }
+ phba->cmf_timer_cnt++;
+
+ if (io_cnt) {
+ /* Update congestion info buffer latency in us */
+ atomic_add(io_cnt, &phba->cgn_latency_evt_cnt);
+ atomic64_add(lat, &phba->cgn_latency_evt);
+ }
+ busy = atomic_xchg(&phba->cmf_busy, 0);
+ max_read = atomic_xchg(&phba->rx_max_read_cnt, 0);
+
+ /* Calculate MBPI for the next timer interval */
+ if (mbpi) {
+ if (mbpi > phba->cmf_link_byte_count ||
+ phba->cmf_active_mode == LPFC_CFG_MONITOR)
+ mbpi = phba->cmf_link_byte_count;
+
+ /* Change max_bytes_per_interval to what the prior
+ * CMF_SYNC_WQE cmpl indicated.
+ */
+ if (mbpi != phba->cmf_max_bytes_per_interval)
+ phba->cmf_max_bytes_per_interval = mbpi;
+ }
+
+ /* Save rxmonitor information for debug */
+ if (phba->rx_monitor) {
+ entry.total_bytes = total;
+ entry.cmf_bytes = total + extra;
+ entry.rcv_bytes = rcv;
+ entry.cmf_busy = busy;
+ entry.cmf_info = phba->cmf_active_info;
+ if (io_cnt) {
+ entry.avg_io_latency = div_u64(lat, io_cnt);
+ entry.avg_io_size = div_u64(rcv, io_cnt);
+ } else {
+ entry.avg_io_latency = 0;
+ entry.avg_io_size = 0;
+ }
+ entry.max_read_cnt = max_read;
+ entry.io_cnt = io_cnt;
+ entry.max_bytes_per_interval = mbpi;
+ if (phba->cmf_active_mode == LPFC_CFG_MANAGED)
+ entry.timer_utilization = phba->cmf_last_ts;
+ else
+ entry.timer_utilization = ms;
+ entry.timer_interval = ms;
+ phba->cmf_last_ts = 0;
+
+ lpfc_rx_monitor_record(phba->rx_monitor, &entry);
+ }
+
+ if (phba->cmf_active_mode == LPFC_CFG_MONITOR) {
+ /* If Monitor mode, check if we are oversubscribed
+ * against the full line rate.
+ */
+ if (mbpi && total > mbpi)
+ atomic_inc(&phba->cgn_driver_evt_cnt);
+ }
+ phba->rx_block_cnt += div_u64(rcv, 512); /* save 512 byte block cnt */
+
+ /* Since total_bytes has already been zero'ed, its okay to unblock
+ * after max_bytes_per_interval is setup.
+ */
+ if (atomic_xchg(&phba->cmf_bw_wait, 0))
+ queue_work(phba->wq, &phba->unblock_request_work);
+
+ /* SCSI IO is now unblocked */
+ atomic_set(&phba->cmf_stop_io, 0);
+
+skip:
+ hrtimer_forward_now(timer,
+ ktime_set(0, timer_interval * NSEC_PER_MSEC));
+ return HRTIMER_RESTART;
+}
+
+#define trunk_link_status(__idx)\
+ bf_get(lpfc_acqe_fc_la_trunk_config_port##__idx, acqe_fc) ?\
+ ((phba->trunk_link.link##__idx.state == LPFC_LINK_UP) ?\
+ "Link up" : "Link down") : "NA"
+/* Did port __idx reported an error */
+#define trunk_port_fault(__idx)\
+ bf_get(lpfc_acqe_fc_la_trunk_config_port##__idx, acqe_fc) ?\
+ (port_fault & (1 << __idx) ? "YES" : "NO") : "NA"
+
+static void
+lpfc_update_trunk_link_status(struct lpfc_hba *phba,
+ struct lpfc_acqe_fc_la *acqe_fc)
+{
+ uint8_t port_fault = bf_get(lpfc_acqe_fc_la_trunk_linkmask, acqe_fc);
+ uint8_t err = bf_get(lpfc_acqe_fc_la_trunk_fault, acqe_fc);
+ u8 cnt = 0;
+
+ phba->sli4_hba.link_state.speed =
+ lpfc_sli4_port_speed_parse(phba, LPFC_TRAILER_CODE_FC,
+ bf_get(lpfc_acqe_fc_la_speed, acqe_fc));
+
+ phba->sli4_hba.link_state.logical_speed =
+ bf_get(lpfc_acqe_fc_la_llink_spd, acqe_fc) * 10;
+ /* We got FC link speed, convert to fc_linkspeed (READ_TOPOLOGY) */
+ phba->fc_linkspeed =
+ lpfc_async_link_speed_to_read_top(
+ phba,
+ bf_get(lpfc_acqe_fc_la_speed, acqe_fc));
+
+ if (bf_get(lpfc_acqe_fc_la_trunk_config_port0, acqe_fc)) {
+ phba->trunk_link.link0.state =
+ bf_get(lpfc_acqe_fc_la_trunk_link_status_port0, acqe_fc)
+ ? LPFC_LINK_UP : LPFC_LINK_DOWN;
+ phba->trunk_link.link0.fault = port_fault & 0x1 ? err : 0;
+ cnt++;
+ }
+ if (bf_get(lpfc_acqe_fc_la_trunk_config_port1, acqe_fc)) {
+ phba->trunk_link.link1.state =
+ bf_get(lpfc_acqe_fc_la_trunk_link_status_port1, acqe_fc)
+ ? LPFC_LINK_UP : LPFC_LINK_DOWN;
+ phba->trunk_link.link1.fault = port_fault & 0x2 ? err : 0;
+ cnt++;
+ }
+ if (bf_get(lpfc_acqe_fc_la_trunk_config_port2, acqe_fc)) {
+ phba->trunk_link.link2.state =
+ bf_get(lpfc_acqe_fc_la_trunk_link_status_port2, acqe_fc)
+ ? LPFC_LINK_UP : LPFC_LINK_DOWN;
+ phba->trunk_link.link2.fault = port_fault & 0x4 ? err : 0;
+ cnt++;
+ }
+ if (bf_get(lpfc_acqe_fc_la_trunk_config_port3, acqe_fc)) {
+ phba->trunk_link.link3.state =
+ bf_get(lpfc_acqe_fc_la_trunk_link_status_port3, acqe_fc)
+ ? LPFC_LINK_UP : LPFC_LINK_DOWN;
+ phba->trunk_link.link3.fault = port_fault & 0x8 ? err : 0;
+ cnt++;
+ }
+
+ if (cnt)
+ phba->trunk_link.phy_lnk_speed =
+ phba->sli4_hba.link_state.logical_speed / (cnt * 1000);
+ else
+ phba->trunk_link.phy_lnk_speed = LPFC_LINK_SPEED_UNKNOWN;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2910 Async FC Trunking Event - Speed:%d\n"
+ "\tLogical speed:%d "
+ "port0: %s port1: %s port2: %s port3: %s\n",
+ phba->sli4_hba.link_state.speed,
+ phba->sli4_hba.link_state.logical_speed,
+ trunk_link_status(0), trunk_link_status(1),
+ trunk_link_status(2), trunk_link_status(3));
+
+ if (phba->cmf_active_mode != LPFC_CFG_OFF)
+ lpfc_cmf_signal_init(phba);
+
+ if (port_fault)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3202 trunk error:0x%x (%s) seen on port0:%s "
+ /*
+ * SLI-4: We have only 0xA error codes
+ * defined as of now. print an appropriate
+ * message in case driver needs to be updated.
+ */
+ "port1:%s port2:%s port3:%s\n", err, err > 0xA ?
+ "UNDEFINED. update driver." : trunk_errmsg[err],
+ trunk_port_fault(0), trunk_port_fault(1),
+ trunk_port_fault(2), trunk_port_fault(3));
+}
+
+
+/**
* lpfc_sli4_async_fc_evt - Process the asynchronous FC link event
* @phba: pointer to lpfc hba data structure.
* @acqe_fc: pointer to the async fc completion queue entry.
@@ -3309,20 +6224,30 @@ out_free_pmb:
static void
lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc)
{
- struct lpfc_dmabuf *mp;
LPFC_MBOXQ_t *pmb;
+ MAILBOX_t *mb;
+ struct lpfc_mbx_read_top *la;
+ char *log_level;
int rc;
if (bf_get(lpfc_trailer_type, acqe_fc) !=
LPFC_FC_LA_EVENT_TYPE_FC_LINK) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2895 Non FC link Event detected.(%d)\n",
bf_get(lpfc_trailer_type, acqe_fc));
return;
}
+
+ if (bf_get(lpfc_acqe_fc_la_att_type, acqe_fc) ==
+ LPFC_FC_LA_TYPE_TRUNKING_EVENT) {
+ lpfc_update_trunk_link_status(phba, acqe_fc);
+ return;
+ }
+
/* Keep the link status for extra SLI4 state machine reference */
phba->sli4_hba.link_state.speed =
- bf_get(lpfc_acqe_fc_la_speed, acqe_fc);
+ lpfc_sli4_port_speed_parse(phba, LPFC_TRAILER_CODE_FC,
+ bf_get(lpfc_acqe_fc_la_speed, acqe_fc));
phba->sli4_hba.link_state.duplex = LPFC_ASYNC_LINK_DUPLEX_FULL;
phba->sli4_hba.link_state.topology =
bf_get(lpfc_acqe_fc_la_topology, acqe_fc);
@@ -3334,79 +6259,400 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc)
bf_get(lpfc_acqe_fc_la_port_number, acqe_fc);
phba->sli4_hba.link_state.fault =
bf_get(lpfc_acqe_link_fault, acqe_fc);
- phba->sli4_hba.link_state.logical_speed =
- bf_get(lpfc_acqe_fc_la_llink_spd, acqe_fc);
+ phba->sli4_hba.link_state.link_status =
+ bf_get(lpfc_acqe_fc_la_link_status, acqe_fc);
+
+ /*
+ * Only select attention types need logical speed modification to what
+ * was previously set.
+ */
+ if (phba->sli4_hba.link_state.status >= LPFC_FC_LA_TYPE_LINK_UP &&
+ phba->sli4_hba.link_state.status < LPFC_FC_LA_TYPE_ACTIVATE_FAIL) {
+ if (bf_get(lpfc_acqe_fc_la_att_type, acqe_fc) ==
+ LPFC_FC_LA_TYPE_LINK_DOWN)
+ phba->sli4_hba.link_state.logical_speed = 0;
+ else if (!phba->sli4_hba.conf_trunk)
+ phba->sli4_hba.link_state.logical_speed =
+ bf_get(lpfc_acqe_fc_la_llink_spd, acqe_fc) * 10;
+ }
+
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"2896 Async FC event - Speed:%dGBaud Topology:x%x "
"LA Type:x%x Port Type:%d Port Number:%d Logical speed:"
- "%dMbps Fault:%d\n",
+ "%dMbps Fault:x%x Link Status:x%x\n",
phba->sli4_hba.link_state.speed,
phba->sli4_hba.link_state.topology,
phba->sli4_hba.link_state.status,
phba->sli4_hba.link_state.type,
phba->sli4_hba.link_state.number,
- phba->sli4_hba.link_state.logical_speed * 10,
- phba->sli4_hba.link_state.fault);
+ phba->sli4_hba.link_state.logical_speed,
+ phba->sli4_hba.link_state.fault,
+ phba->sli4_hba.link_state.link_status);
+
+ /*
+ * The following attention types are informational only, providing
+ * further details about link status. Overwrite the value of
+ * link_state.status appropriately. No further action is required.
+ */
+ if (phba->sli4_hba.link_state.status >= LPFC_FC_LA_TYPE_ACTIVATE_FAIL) {
+ switch (phba->sli4_hba.link_state.status) {
+ case LPFC_FC_LA_TYPE_ACTIVATE_FAIL:
+ log_level = KERN_WARNING;
+ phba->sli4_hba.link_state.status =
+ LPFC_FC_LA_TYPE_LINK_DOWN;
+ break;
+ case LPFC_FC_LA_TYPE_LINK_RESET_PRTCL_EVT:
+ /*
+ * During bb credit recovery establishment, receiving
+ * this attention type is normal. Link Up attention
+ * type is expected to occur before this informational
+ * attention type so keep the Link Up status.
+ */
+ log_level = KERN_INFO;
+ phba->sli4_hba.link_state.status =
+ LPFC_FC_LA_TYPE_LINK_UP;
+ break;
+ default:
+ log_level = KERN_INFO;
+ break;
+ }
+ lpfc_log_msg(phba, log_level, LOG_SLI,
+ "2992 Async FC event - Informational Link "
+ "Attention Type x%x\n",
+ bf_get(lpfc_acqe_fc_la_att_type, acqe_fc));
+ return;
+ }
+
pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2897 The mboxq allocation failed\n");
return;
}
- mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!mp) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2898 The lpfc_dmabuf allocation failed\n");
+ rc = lpfc_mbox_rsrc_prep(phba, pmb);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2898 The mboxq prep failed\n");
goto out_free_pmb;
}
- mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
- if (!mp->virt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2899 The mbuf allocation failed\n");
- goto out_free_dmabuf;
- }
/* Cleanup any outstanding ELS commands */
lpfc_els_flush_all_cmd(phba);
/* Block ELS IOCBs until we have done process link event */
- phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
+ phba->sli4_hba.els_wq->pring->flag |= LPFC_STOP_IOCB_EVENT;
/* Update link event statistics */
phba->sli.slistat.link_event++;
/* Create lpfc_handle_latt mailbox command from link ACQE */
- lpfc_read_topology(phba, pmb, mp);
+ lpfc_read_topology(phba, pmb, pmb->ctx_buf);
pmb->mbox_cmpl = lpfc_mbx_cmpl_read_topology;
pmb->vport = phba->pport;
+ if (phba->sli4_hba.link_state.status != LPFC_FC_LA_TYPE_LINK_UP) {
+ phba->link_flag &= ~(LS_MDS_LINK_DOWN | LS_MDS_LOOPBACK);
+
+ switch (phba->sli4_hba.link_state.status) {
+ case LPFC_FC_LA_TYPE_MDS_LINK_DOWN:
+ phba->link_flag |= LS_MDS_LINK_DOWN;
+ break;
+ case LPFC_FC_LA_TYPE_MDS_LOOPBACK:
+ phba->link_flag |= LS_MDS_LOOPBACK;
+ break;
+ default:
+ break;
+ }
+
+ /* Initialize completion status */
+ mb = &pmb->u.mb;
+ mb->mbxStatus = MBX_SUCCESS;
+
+ /* Parse port fault information field */
+ lpfc_sli4_parse_latt_fault(phba, (void *)acqe_fc);
+
+ /* Parse and translate link attention fields */
+ la = (struct lpfc_mbx_read_top *)&pmb->u.mb.un.varReadTop;
+ la->eventTag = acqe_fc->event_tag;
+
+ if (phba->sli4_hba.link_state.status ==
+ LPFC_FC_LA_TYPE_UNEXP_WWPN) {
+ bf_set(lpfc_mbx_read_top_att_type, la,
+ LPFC_FC_LA_TYPE_UNEXP_WWPN);
+ } else {
+ bf_set(lpfc_mbx_read_top_att_type, la,
+ LPFC_FC_LA_TYPE_LINK_DOWN);
+ }
+ /* Invoke the mailbox command callback function */
+ lpfc_mbx_cmpl_read_topology(phba, pmb);
+
+ return;
+ }
+
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED)
- goto out_free_dmabuf;
+ goto out_free_pmb;
return;
-out_free_dmabuf:
- kfree(mp);
out_free_pmb:
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
}
/**
* lpfc_sli4_async_sli_evt - Process the asynchronous SLI link event
* @phba: pointer to lpfc hba data structure.
- * @acqe_fc: pointer to the async SLI completion queue entry.
+ * @acqe_sli: pointer to the async SLI completion queue entry.
*
* This routine is to handle the SLI4 asynchronous SLI events.
**/
static void
lpfc_sli4_async_sli_evt(struct lpfc_hba *phba, struct lpfc_acqe_sli *acqe_sli)
{
+ char port_name;
+ char message[128];
+ uint8_t status;
+ uint8_t evt_type;
+ uint8_t operational = 0;
+ struct temp_event temp_event_data;
+ struct lpfc_acqe_misconfigured_event *misconfigured;
+ struct lpfc_acqe_cgn_signal *cgn_signal;
+ struct Scsi_Host *shost;
+ struct lpfc_vport **vports;
+ int rc, i, cnt;
+
+ evt_type = bf_get(lpfc_trailer_type, acqe_sli);
+
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "2901 Async SLI event - Event Data1:x%08x Event Data2:"
- "x%08x SLI Event Type:%d",
+ "2901 Async SLI event - Type:%d, Event Data: x%08x "
+ "x%08x x%08x x%08x\n", evt_type,
acqe_sli->event_data1, acqe_sli->event_data2,
- bf_get(lpfc_trailer_type, acqe_sli));
- return;
+ acqe_sli->event_data3, acqe_sli->trailer);
+
+ port_name = phba->Port[0];
+ if (port_name == 0x00)
+ port_name = '?'; /* get port name is empty */
+
+ switch (evt_type) {
+ case LPFC_SLI_EVENT_TYPE_OVER_TEMP:
+ temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
+ temp_event_data.event_code = LPFC_THRESHOLD_TEMP;
+ temp_event_data.data = (uint32_t)acqe_sli->event_data1;
+
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3190 Over Temperature:%d Celsius- Port Name %c\n",
+ acqe_sli->event_data1, port_name);
+
+ phba->sfp_warning |= LPFC_TRANSGRESSION_HIGH_TEMPERATURE;
+ shost = lpfc_shost_from_vport(phba->pport);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(temp_event_data),
+ (char *)&temp_event_data,
+ SCSI_NL_VID_TYPE_PCI
+ | PCI_VENDOR_ID_EMULEX);
+ break;
+ case LPFC_SLI_EVENT_TYPE_NORM_TEMP:
+ temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
+ temp_event_data.event_code = LPFC_NORMAL_TEMP;
+ temp_event_data.data = (uint32_t)acqe_sli->event_data1;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI | LOG_LDS_EVENT,
+ "3191 Normal Temperature:%d Celsius - Port Name %c\n",
+ acqe_sli->event_data1, port_name);
+
+ shost = lpfc_shost_from_vport(phba->pport);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(temp_event_data),
+ (char *)&temp_event_data,
+ SCSI_NL_VID_TYPE_PCI
+ | PCI_VENDOR_ID_EMULEX);
+ break;
+ case LPFC_SLI_EVENT_TYPE_MISCONFIGURED:
+ misconfigured = (struct lpfc_acqe_misconfigured_event *)
+ &acqe_sli->event_data1;
+
+ /* fetch the status for this port */
+ switch (phba->sli4_hba.lnk_info.lnk_no) {
+ case LPFC_LINK_NUMBER_0:
+ status = bf_get(lpfc_sli_misconfigured_port0_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port0_op,
+ &misconfigured->theEvent);
+ break;
+ case LPFC_LINK_NUMBER_1:
+ status = bf_get(lpfc_sli_misconfigured_port1_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port1_op,
+ &misconfigured->theEvent);
+ break;
+ case LPFC_LINK_NUMBER_2:
+ status = bf_get(lpfc_sli_misconfigured_port2_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port2_op,
+ &misconfigured->theEvent);
+ break;
+ case LPFC_LINK_NUMBER_3:
+ status = bf_get(lpfc_sli_misconfigured_port3_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port3_op,
+ &misconfigured->theEvent);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3296 "
+ "LPFC_SLI_EVENT_TYPE_MISCONFIGURED "
+ "event: Invalid link %d",
+ phba->sli4_hba.lnk_info.lnk_no);
+ return;
+ }
+
+ /* Skip if optic state unchanged */
+ if (phba->sli4_hba.lnk_info.optic_state == status)
+ return;
+
+ switch (status) {
+ case LPFC_SLI_EVENT_STATUS_VALID:
+ sprintf(message, "Physical Link is functional");
+ break;
+ case LPFC_SLI_EVENT_STATUS_NOT_PRESENT:
+ sprintf(message, "Optics faulted/incorrectly "
+ "installed/not installed - Reseat optics, "
+ "if issue not resolved, replace.");
+ break;
+ case LPFC_SLI_EVENT_STATUS_WRONG_TYPE:
+ sprintf(message,
+ "Optics of two types installed - Remove one "
+ "optic or install matching pair of optics.");
+ break;
+ case LPFC_SLI_EVENT_STATUS_UNSUPPORTED:
+ sprintf(message, "Incompatible optics - Replace with "
+ "compatible optics for card to function.");
+ break;
+ case LPFC_SLI_EVENT_STATUS_UNQUALIFIED:
+ sprintf(message, "Unqualified optics - Replace with "
+ "Avago optics for Warranty and Technical "
+ "Support - Link is%s operational",
+ (operational) ? " not" : "");
+ break;
+ case LPFC_SLI_EVENT_STATUS_UNCERTIFIED:
+ sprintf(message, "Uncertified optics - Replace with "
+ "Avago-certified optics to enable link "
+ "operation - Link is%s operational",
+ (operational) ? " not" : "");
+ break;
+ default:
+ /* firmware is reporting a status we don't know about */
+ sprintf(message, "Unknown event status x%02x", status);
+ break;
+ }
+
+ /* Issue READ_CONFIG mbox command to refresh supported speeds */
+ rc = lpfc_sli4_read_config(phba);
+ if (rc) {
+ phba->lmt = 0;
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "3194 Unable to retrieve supported "
+ "speeds, rc = 0x%x\n", rc);
+ }
+ rc = lpfc_sli4_refresh_params(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3174 Unable to update pls support, "
+ "rc x%x\n", rc);
+ }
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL;
+ i++) {
+ shost = lpfc_shost_from_vport(vports[i]);
+ lpfc_host_supported_speeds_set(shost);
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+
+ phba->sli4_hba.lnk_info.optic_state = status;
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3176 Port Name %c %s\n", port_name, message);
+ break;
+ case LPFC_SLI_EVENT_TYPE_REMOTE_DPORT:
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3192 Remote DPort Test Initiated - "
+ "Event Data1:x%08x Event Data2: x%08x\n",
+ acqe_sli->event_data1, acqe_sli->event_data2);
+ break;
+ case LPFC_SLI_EVENT_TYPE_PORT_PARAMS_CHG:
+ /* Call FW to obtain active parms */
+ lpfc_sli4_cgn_parm_chg_evt(phba);
+ break;
+ case LPFC_SLI_EVENT_TYPE_MISCONF_FAWWN:
+ /* Misconfigured WWN. Reports that the SLI Port is configured
+ * to use FA-WWN, but the attached device doesn’t support it.
+ * Event Data1 - N.A, Event Data2 - N.A
+ * This event only happens on the physical port.
+ */
+ lpfc_log_msg(phba, KERN_WARNING, LOG_SLI | LOG_DISCOVERY,
+ "2699 Misconfigured FA-PWWN - Attached device "
+ "does not support FA-PWWN\n");
+ phba->sli4_hba.fawwpn_flag &= ~LPFC_FAWWPN_FABRIC;
+ memset(phba->pport->fc_portname.u.wwn, 0,
+ sizeof(struct lpfc_name));
+ break;
+ case LPFC_SLI_EVENT_TYPE_EEPROM_FAILURE:
+ /* EEPROM failure. No driver action is required */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2518 EEPROM failure - "
+ "Event Data1: x%08x Event Data2: x%08x\n",
+ acqe_sli->event_data1, acqe_sli->event_data2);
+ break;
+ case LPFC_SLI_EVENT_TYPE_CGN_SIGNAL:
+ if (phba->cmf_active_mode == LPFC_CFG_OFF)
+ break;
+ cgn_signal = (struct lpfc_acqe_cgn_signal *)
+ &acqe_sli->event_data1;
+ phba->cgn_acqe_cnt++;
+
+ cnt = bf_get(lpfc_warn_acqe, cgn_signal);
+ atomic64_add(cnt, &phba->cgn_acqe_stat.warn);
+ atomic64_add(cgn_signal->alarm_cnt, &phba->cgn_acqe_stat.alarm);
+
+ /* no threshold for CMF, even 1 signal will trigger an event */
+
+ /* Alarm overrides warning, so check that first */
+ if (cgn_signal->alarm_cnt) {
+ if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) {
+ /* Keep track of alarm cnt for CMF_SYNC_WQE */
+ atomic_add(cgn_signal->alarm_cnt,
+ &phba->cgn_sync_alarm_cnt);
+ }
+ } else if (cnt) {
+ /* signal action needs to be taken */
+ if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ONLY ||
+ phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) {
+ /* Keep track of warning cnt for CMF_SYNC_WQE */
+ atomic_add(cnt, &phba->cgn_sync_warn_cnt);
+ }
+ }
+ break;
+ case LPFC_SLI_EVENT_TYPE_RD_SIGNAL:
+ /* May be accompanied by a temperature event */
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_SLI | LOG_LINK_EVENT | LOG_LDS_EVENT,
+ "2902 Remote Degrade Signaling: x%08x x%08x "
+ "x%08x\n",
+ acqe_sli->event_data1, acqe_sli->event_data2,
+ acqe_sli->event_data3);
+ break;
+ case LPFC_SLI_EVENT_TYPE_RESET_CM_STATS:
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "2905 Reset CM statistics\n");
+ lpfc_sli4_async_cmstat_evt(phba);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3193 Unrecognized SLI event, type: 0x%x",
+ evt_type);
+ break;
+ }
}
/**
@@ -3434,19 +6680,13 @@ lpfc_sli4_perform_vport_cvl(struct lpfc_vport *vport)
ndlp = lpfc_findnode_did(vport, Fabric_DID);
if (!ndlp) {
/* Cannot find existing Fabric ndlp, so allocate a new one */
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ ndlp = lpfc_nlp_init(vport, Fabric_DID);
if (!ndlp)
- return 0;
- lpfc_nlp_init(vport, ndlp, Fabric_DID);
+ return NULL;
/* Set the node type */
ndlp->nlp_type |= NLP_FABRIC;
/* Put ndlp onto node list */
lpfc_enqueue_node(vport, ndlp);
- } else if (!NLP_CHK_NODE_ACT(ndlp)) {
- /* re-setup ndlp without removing from node list */
- ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
- if (!ndlp)
- return 0;
}
if ((phba->pport->port_state < LPFC_FLOGI) &&
(phba->pport->port_state != LPFC_VPORT_FAILED))
@@ -3460,16 +6700,14 @@ lpfc_sli4_perform_vport_cvl(struct lpfc_vport *vport)
return NULL;
lpfc_linkdown_port(vport);
lpfc_cleanup_pending_mbox(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_CVL_RCVD;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_CVL_RCVD, &vport->fc_flag);
return ndlp;
}
/**
* lpfc_sli4_perform_all_vport_cvl - Perform clear virtual link on all vports
- * @vport: pointer to lpfc hba data structure.
+ * @phba: pointer to lpfc hba data structure.
*
* This routine is to perform Clear Virtual Link (CVL) on all vports in
* response to a FCF dead event.
@@ -3490,7 +6728,7 @@ lpfc_sli4_perform_all_vport_cvl(struct lpfc_hba *phba)
/**
* lpfc_sli4_async_fip_evt - Process the asynchronous FCoE FIP event
* @phba: pointer to lpfc hba data structure.
- * @acqe_link: pointer to the async fcoe completion queue entry.
+ * @acqe_fip: pointer to the async fcoe completion queue entry.
*
* This routine is to handle the SLI4 asynchronous fcoe event.
**/
@@ -3502,7 +6740,6 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
int rc;
struct lpfc_vport *vport;
struct lpfc_nodelist *ndlp;
- struct Scsi_Host *shost;
int active_vlink_present;
struct lpfc_vport **vports;
int i;
@@ -3513,8 +6750,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
case LPFC_FIP_EVENT_TYPE_NEW_FCF:
case LPFC_FIP_EVENT_TYPE_FCF_PARAM_MOD:
if (event_type == LPFC_FIP_EVENT_TYPE_NEW_FCF)
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP |
- LOG_DISCOVERY,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2546 New FCF event, evt_tag:x%x, "
"index:x%x\n",
acqe_fip->event_tag,
@@ -3541,13 +6777,11 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
}
/* If the FCF discovery is in progress, do nothing. */
- spin_lock_irq(&phba->hbalock);
- if (phba->hba_flag & FCF_TS_INPROG) {
- spin_unlock_irq(&phba->hbalock);
+ if (test_bit(FCF_TS_INPROG, &phba->hba_flag))
break;
- }
+ spin_lock_irq(&phba->hbalock);
/* If fast FCF failover rescan event is pending, do nothing */
- if (phba->fcf.fcf_flag & FCF_REDISC_EVT) {
+ if (phba->fcf.fcf_flag & (FCF_REDISC_EVT | FCF_REDISC_PEND)) {
spin_unlock_irq(&phba->hbalock);
break;
}
@@ -3567,28 +6801,31 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba,
LPFC_FCOE_FCF_GET_FIRST);
if (rc)
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2547 Issue FCF scan read FCF mailbox "
"command failed (x%x)\n", rc);
break;
case LPFC_FIP_EVENT_TYPE_FCF_TABLE_FULL:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2548 FCF Table full count 0x%x tag 0x%x\n",
- bf_get(lpfc_acqe_fip_fcf_count, acqe_fip),
- acqe_fip->event_tag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2548 FCF Table full count 0x%x tag 0x%x\n",
+ bf_get(lpfc_acqe_fip_fcf_count, acqe_fip),
+ acqe_fip->event_tag);
break;
case LPFC_FIP_EVENT_TYPE_FCF_DEAD:
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
- "2549 FCF (x%x) disconnected from network, "
- "tag:x%x\n", acqe_fip->index, acqe_fip->event_tag);
+ phba->fcoe_cvl_eventtag = acqe_fip->event_tag;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2549 FCF (x%x) disconnected from network, "
+ "tag:x%x\n", acqe_fip->index,
+ acqe_fip->event_tag);
/*
* If we are in the middle of FCF failover process, clear
* the corresponding FCF bit in the roundrobin bitmap.
*/
spin_lock_irq(&phba->hbalock);
- if (phba->fcf.fcf_flag & FCF_DISCOVERY) {
+ if ((phba->fcf.fcf_flag & FCF_DISCOVERY) &&
+ (phba->fcf.current_rec.fcf_indx != acqe_fip->index)) {
spin_unlock_irq(&phba->hbalock);
/* Update FLOGI FCF failover eligible FCF bmask */
lpfc_sli4_fcf_rr_index_clear(phba, acqe_fip->index);
@@ -3618,8 +6855,8 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
rc = lpfc_sli4_redisc_fcf_table(phba);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_FIP |
- LOG_DISCOVERY,
- "2772 Issue FCF rediscover mabilbox "
+ LOG_TRACE_EVENT,
+ "2772 Issue FCF rediscover mailbox "
"command failed, fail through to FCF "
"dead event\n");
spin_lock_irq(&phba->hbalock);
@@ -3632,8 +6869,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
lpfc_sli4_fcf_dead_failthrough(phba);
} else {
/* Reset FCF roundrobin bmask for new discovery */
- memset(phba->fcf.fcf_rr_bmask, 0,
- sizeof(*phba->fcf.fcf_rr_bmask));
+ lpfc_sli4_clear_fcf_rr_bmask(phba);
/*
* Handling fast FCF failover to a DEAD FCF event is
* considered equalivant to receiving CVL to all vports.
@@ -3642,12 +6878,14 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
}
break;
case LPFC_FIP_EVENT_TYPE_CVL:
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
+ phba->fcoe_cvl_eventtag = acqe_fip->event_tag;
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
"2718 Clear Virtual Link Received for VPI 0x%x"
" tag 0x%x\n", acqe_fip->index, acqe_fip->event_tag);
vport = lpfc_find_vport_by_vpid(phba,
- acqe_fip->index - phba->vpi_base);
+ acqe_fip->index);
ndlp = lpfc_sli4_perform_vport_cvl(vport);
if (!ndlp)
break;
@@ -3657,9 +6895,9 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
if (vports) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL;
i++) {
- if ((!(vports[i]->fc_flag &
- FC_VPORT_CVL_RCVD)) &&
- (vports[i]->port_state > LPFC_FDISC)) {
+ if (!test_bit(FC_VPORT_CVL_RCVD,
+ &vports[i]->fc_flag) &&
+ vports[i]->port_state > LPFC_FDISC) {
active_vlink_present = 1;
break;
}
@@ -3667,16 +6905,20 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
lpfc_destroy_vport_work_array(phba, vports);
}
- if (active_vlink_present) {
+ /*
+ * Don't re-instantiate if vport is marked for deletion.
+ * If we are here first then vport_delete is going to wait
+ * for discovery to complete.
+ */
+ if (!test_bit(FC_UNLOADING, &vport->load_flag) &&
+ active_vlink_present) {
/*
* If there are other active VLinks present,
* re-instantiate the Vlink using FDISC.
*/
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
- shost = lpfc_shost_from_vport(vport);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + secs_to_jiffies(1));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_FDISC;
vport->port_state = LPFC_FDISC;
} else {
@@ -3702,9 +6944,9 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
rc = lpfc_sli4_redisc_fcf_table(phba);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_FIP |
- LOG_DISCOVERY,
+ LOG_TRACE_EVENT,
"2774 Issue FCF rediscover "
- "mabilbox command failed, "
+ "mailbox command failed, "
"through to CVL event\n");
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~FCF_ACVL_DISC;
@@ -3719,14 +6961,13 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
* Reset FCF roundrobin bmask for new
* discovery.
*/
- memset(phba->fcf.fcf_rr_bmask, 0,
- sizeof(*phba->fcf.fcf_rr_bmask));
+ lpfc_sli4_clear_fcf_rr_bmask(phba);
}
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0288 Unknown FCoE event type 0x%x event tag "
- "0x%x\n", event_type, acqe_fip->event_tag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0288 Unknown FCoE event type 0x%x event tag "
+ "0x%x\n", event_type, acqe_fip->event_tag);
break;
}
}
@@ -3734,7 +6975,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
/**
* lpfc_sli4_async_dcbx_evt - Process the asynchronous dcbx event
* @phba: pointer to lpfc hba data structure.
- * @acqe_link: pointer to the async dcbx completion queue entry.
+ * @acqe_dcbx: pointer to the async dcbx completion queue entry.
*
* This routine is to handle the SLI4 asynchronous dcbx event.
**/
@@ -3743,7 +6984,7 @@ lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba,
struct lpfc_acqe_dcbx *acqe_dcbx)
{
phba->fc_eventTag = acqe_dcbx->event_tag;
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0290 The SLI4 DCBX asynchronous event is not "
"handled yet\n");
}
@@ -3751,7 +6992,7 @@ lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba,
/**
* lpfc_sli4_async_grp5_evt - Process the asynchronous group5 event
* @phba: pointer to lpfc hba data structure.
- * @acqe_link: pointer to the async grp5 completion queue entry.
+ * @acqe_grp5: pointer to the async grp5 completion queue entry.
*
* This routine is to handle the SLI4 asynchronous grp5 event. A grp5 event
* is an asynchronous notified of a logical link speed change. The Port
@@ -3767,11 +7008,299 @@ lpfc_sli4_async_grp5_evt(struct lpfc_hba *phba,
phba->fcoe_eventtag = acqe_grp5->event_tag;
prev_ll_spd = phba->sli4_hba.link_state.logical_speed;
phba->sli4_hba.link_state.logical_speed =
- (bf_get(lpfc_acqe_grp5_llink_spd, acqe_grp5));
+ (bf_get(lpfc_acqe_grp5_llink_spd, acqe_grp5)) * 10;
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"2789 GRP5 Async Event: Updating logical link speed "
- "from %dMbps to %dMbps\n", (prev_ll_spd * 10),
- (phba->sli4_hba.link_state.logical_speed*10));
+ "from %dMbps to %dMbps\n", prev_ll_spd,
+ phba->sli4_hba.link_state.logical_speed);
+}
+
+/**
+ * lpfc_sli4_async_cmstat_evt - Process the asynchronous cmstat event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to handle the SLI4 asynchronous cmstat event. A cmstat event
+ * is an asynchronous notification of a request to reset CM stats.
+ **/
+static void
+lpfc_sli4_async_cmstat_evt(struct lpfc_hba *phba)
+{
+ if (!phba->cgn_i)
+ return;
+ lpfc_init_congestion_stat(phba);
+}
+
+/**
+ * lpfc_cgn_params_val - Validate FW congestion parameters.
+ * @phba: pointer to lpfc hba data structure.
+ * @p_cfg_param: pointer to FW provided congestion parameters.
+ *
+ * This routine validates the congestion parameters passed
+ * by the FW to the driver via an ACQE event.
+ **/
+static void
+lpfc_cgn_params_val(struct lpfc_hba *phba, struct lpfc_cgn_param *p_cfg_param)
+{
+ spin_lock_irq(&phba->hbalock);
+
+ if (!lpfc_rangecheck(p_cfg_param->cgn_param_mode, LPFC_CFG_OFF,
+ LPFC_CFG_MONITOR)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT,
+ "6225 CMF mode param out of range: %d\n",
+ p_cfg_param->cgn_param_mode);
+ p_cfg_param->cgn_param_mode = LPFC_CFG_OFF;
+ }
+
+ spin_unlock_irq(&phba->hbalock);
+}
+
+static const char * const lpfc_cmf_mode_to_str[] = {
+ "OFF",
+ "MANAGED",
+ "MONITOR",
+};
+
+/**
+ * lpfc_cgn_params_parse - Process a FW cong parm change event
+ * @phba: pointer to lpfc hba data structure.
+ * @p_cgn_param: pointer to a data buffer with the FW cong params.
+ * @len: the size of pdata in bytes.
+ *
+ * This routine validates the congestion management buffer signature
+ * from the FW, validates the contents and makes corrections for
+ * valid, in-range values. If the signature magic is correct and
+ * after parameter validation, the contents are copied to the driver's
+ * @phba structure. If the magic is incorrect, an error message is
+ * logged.
+ **/
+static void
+lpfc_cgn_params_parse(struct lpfc_hba *phba,
+ struct lpfc_cgn_param *p_cgn_param, uint32_t len)
+{
+ struct lpfc_cgn_info *cp;
+ uint32_t crc, oldmode;
+ char acr_string[4] = {0};
+
+ /* Make sure the FW has encoded the correct magic number to
+ * validate the congestion parameter in FW memory.
+ */
+ if (p_cgn_param->cgn_param_magic == LPFC_CFG_PARAM_MAGIC_NUM) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT | LOG_INIT,
+ "4668 FW cgn parm buffer data: "
+ "magic 0x%x version %d mode %d "
+ "level0 %d level1 %d "
+ "level2 %d byte13 %d "
+ "byte14 %d byte15 %d "
+ "byte11 %d byte12 %d activeMode %d\n",
+ p_cgn_param->cgn_param_magic,
+ p_cgn_param->cgn_param_version,
+ p_cgn_param->cgn_param_mode,
+ p_cgn_param->cgn_param_level0,
+ p_cgn_param->cgn_param_level1,
+ p_cgn_param->cgn_param_level2,
+ p_cgn_param->byte13,
+ p_cgn_param->byte14,
+ p_cgn_param->byte15,
+ p_cgn_param->byte11,
+ p_cgn_param->byte12,
+ phba->cmf_active_mode);
+
+ oldmode = phba->cmf_active_mode;
+
+ /* Any parameters out of range are corrected to defaults
+ * by this routine. No need to fail.
+ */
+ lpfc_cgn_params_val(phba, p_cgn_param);
+
+ /* Parameters are verified, move them into driver storage */
+ spin_lock_irq(&phba->hbalock);
+ memcpy(&phba->cgn_p, p_cgn_param,
+ sizeof(struct lpfc_cgn_param));
+
+ /* Update parameters in congestion info buffer now */
+ if (phba->cgn_i) {
+ cp = (struct lpfc_cgn_info *)phba->cgn_i->virt;
+ cp->cgn_info_mode = phba->cgn_p.cgn_param_mode;
+ cp->cgn_info_level0 = phba->cgn_p.cgn_param_level0;
+ cp->cgn_info_level1 = phba->cgn_p.cgn_param_level1;
+ cp->cgn_info_level2 = phba->cgn_p.cgn_param_level2;
+ crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ,
+ LPFC_CGN_CRC32_SEED);
+ cp->cgn_info_crc = cpu_to_le32(crc);
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ phba->cmf_active_mode = phba->cgn_p.cgn_param_mode;
+
+ switch (oldmode) {
+ case LPFC_CFG_OFF:
+ if (phba->cgn_p.cgn_param_mode != LPFC_CFG_OFF) {
+ /* Turning CMF on */
+ lpfc_cmf_start(phba);
+
+ if (phba->link_state >= LPFC_LINK_UP) {
+ phba->cgn_reg_fpin =
+ phba->cgn_init_reg_fpin;
+ phba->cgn_reg_signal =
+ phba->cgn_init_reg_signal;
+ lpfc_issue_els_edc(phba->pport, 0);
+ }
+ }
+ break;
+ case LPFC_CFG_MANAGED:
+ switch (phba->cgn_p.cgn_param_mode) {
+ case LPFC_CFG_OFF:
+ /* Turning CMF off */
+ lpfc_cmf_stop(phba);
+ if (phba->link_state >= LPFC_LINK_UP)
+ lpfc_issue_els_edc(phba->pport, 0);
+ break;
+ case LPFC_CFG_MONITOR:
+ phba->cmf_max_bytes_per_interval =
+ phba->cmf_link_byte_count;
+
+ /* Resume blocked IO - unblock on workqueue */
+ queue_work(phba->wq,
+ &phba->unblock_request_work);
+ break;
+ }
+ break;
+ case LPFC_CFG_MONITOR:
+ switch (phba->cgn_p.cgn_param_mode) {
+ case LPFC_CFG_OFF:
+ /* Turning CMF off */
+ lpfc_cmf_stop(phba);
+ if (phba->link_state >= LPFC_LINK_UP)
+ lpfc_issue_els_edc(phba->pport, 0);
+ break;
+ case LPFC_CFG_MANAGED:
+ lpfc_cmf_signal_init(phba);
+ break;
+ }
+ break;
+ }
+ if (oldmode != LPFC_CFG_OFF ||
+ oldmode != phba->cgn_p.cgn_param_mode) {
+ if (phba->cgn_p.cgn_param_mode == LPFC_CFG_MANAGED)
+ scnprintf(acr_string, sizeof(acr_string), "%u",
+ phba->cgn_p.cgn_param_level0);
+ else
+ scnprintf(acr_string, sizeof(acr_string), "NA");
+
+ dev_info(&phba->pcidev->dev, "%d: "
+ "4663 CMF: Mode %s acr %s\n",
+ phba->brd_no,
+ lpfc_cmf_mode_to_str
+ [phba->cgn_p.cgn_param_mode],
+ acr_string);
+ }
+ } else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT | LOG_INIT,
+ "4669 FW cgn parm buf wrong magic 0x%x "
+ "version %d\n", p_cgn_param->cgn_param_magic,
+ p_cgn_param->cgn_param_version);
+ }
+}
+
+/**
+ * lpfc_sli4_cgn_params_read - Read and Validate FW congestion parameters.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine issues a read_object mailbox command to
+ * get the congestion management parameters from the FW
+ * parses it and updates the driver maintained values.
+ *
+ * Returns
+ * 0 if the object was empty
+ * -Eval if an error was encountered
+ * Count if bytes were read from object
+ **/
+int
+lpfc_sli4_cgn_params_read(struct lpfc_hba *phba)
+{
+ int ret = 0;
+ struct lpfc_cgn_param *p_cgn_param = NULL;
+ u32 *pdata = NULL;
+ u32 len = 0;
+
+ /* Find out if the FW has a new set of congestion parameters. */
+ len = sizeof(struct lpfc_cgn_param);
+ pdata = kzalloc(len, GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ ret = lpfc_read_object(phba, (char *)LPFC_PORT_CFG_NAME,
+ pdata, len);
+
+ /* 0 means no data. A negative means error. A positive means
+ * bytes were copied.
+ */
+ if (!ret) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT | LOG_INIT,
+ "4670 CGN RD OBJ returns no data\n");
+ goto rd_obj_err;
+ } else if (ret < 0) {
+ /* Some error. Just exit and return it to the caller.*/
+ goto rd_obj_err;
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT | LOG_INIT,
+ "6234 READ CGN PARAMS Successful %d\n", len);
+
+ /* Parse data pointer over len and update the phba congestion
+ * parameters with values passed back. The receive rate values
+ * may have been altered in FW, but take no action here.
+ */
+ p_cgn_param = (struct lpfc_cgn_param *)pdata;
+ lpfc_cgn_params_parse(phba, p_cgn_param, len);
+
+ rd_obj_err:
+ kfree(pdata);
+ return ret;
+}
+
+/**
+ * lpfc_sli4_cgn_parm_chg_evt - Process a FW congestion param change event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * The FW generated Async ACQE SLI event calls this routine when
+ * the event type is an SLI Internal Port Event and the Event Code
+ * indicates a change to the FW maintained congestion parameters.
+ *
+ * This routine executes a Read_Object mailbox call to obtain the
+ * current congestion parameters maintained in FW and corrects
+ * the driver's active congestion parameters.
+ *
+ * The acqe event is not passed because there is no further data
+ * required.
+ *
+ * Returns nonzero error if event processing encountered an error.
+ * Zero otherwise for success.
+ **/
+static int
+lpfc_sli4_cgn_parm_chg_evt(struct lpfc_hba *phba)
+{
+ int ret = 0;
+
+ if (!phba->sli4_hba.pc_sli4_params.cmf) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT | LOG_INIT,
+ "4664 Cgn Evt when E2E off. Drop event\n");
+ return -EACCES;
+ }
+
+ /* If the event is claiming an empty object, it's ok. A write
+ * could have cleared it. Only error is a negative return
+ * status.
+ */
+ ret = lpfc_sli4_cgn_params_read(phba);
+ if (ret < 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT | LOG_INIT,
+ "4667 Error reading Cgn Params (%d)\n",
+ ret);
+ } else if (!ret) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT | LOG_INIT,
+ "4673 CGN Event empty object.\n");
+ }
+ return ret;
}
/**
@@ -3784,18 +7313,19 @@ lpfc_sli4_async_grp5_evt(struct lpfc_hba *phba,
void lpfc_sli4_async_event_proc(struct lpfc_hba *phba)
{
struct lpfc_cq_event *cq_event;
+ unsigned long iflags;
/* First, declare the async event has been handled */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~ASYNC_EVENT;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(ASYNC_EVENT, &phba->hba_flag);
+
/* Now, handle all the async events */
+ spin_lock_irqsave(&phba->sli4_hba.asynce_list_lock, iflags);
while (!list_empty(&phba->sli4_hba.sp_asynce_work_queue)) {
- /* Get the first event from the head of the event queue */
- spin_lock_irq(&phba->hbalock);
list_remove_head(&phba->sli4_hba.sp_asynce_work_queue,
cq_event, struct lpfc_cq_event, list);
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->sli4_hba.asynce_list_lock,
+ iflags);
+
/* Process the asynchronous event */
switch (bf_get(lpfc_trailer_code, &cq_event->cqe.mcqe_cmpl)) {
case LPFC_TRAILER_CODE_LINK:
@@ -3820,15 +7350,19 @@ void lpfc_sli4_async_event_proc(struct lpfc_hba *phba)
lpfc_sli4_async_sli_evt(phba, &cq_event->cqe.acqe_sli);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "1804 Invalid asynchrous event code: "
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "1804 Invalid asynchronous event code: "
"x%x\n", bf_get(lpfc_trailer_code,
&cq_event->cqe.mcqe_cmpl));
break;
}
+
/* Free the completion event processed to the free pool */
lpfc_sli4_cq_event_release(phba, cq_event);
+ spin_lock_irqsave(&phba->sli4_hba.asynce_list_lock, iflags);
}
+ spin_unlock_irqrestore(&phba->sli4_hba.asynce_list_lock, iflags);
}
/**
@@ -3856,7 +7390,7 @@ void lpfc_sli4_fcf_redisc_event_proc(struct lpfc_hba *phba)
"2777 Start post-quiescent FCF table scan\n");
rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba, LPFC_FCOE_FCF_GET_FIRST);
if (rc)
- lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2747 Issue FCF scan read FCF mailbox "
"command failed 0x%x\n", rc);
}
@@ -3927,7 +7461,7 @@ static void lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
"0480 Enabled MSI-X interrupt mode.\n");
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0482 Illegal interrupt mode.\n");
break;
}
@@ -3949,20 +7483,17 @@ static int
lpfc_enable_pci_dev(struct lpfc_hba *phba)
{
struct pci_dev *pdev;
- int bars;
/* Obtain PCI device reference */
if (!phba->pcidev)
goto out_error;
else
pdev = phba->pcidev;
- /* Select PCI BARs */
- bars = pci_select_bars(pdev, IORESOURCE_MEM);
/* Enable PCI device */
if (pci_enable_device_mem(pdev))
goto out_error;
/* Request PCI resource for the device */
- if (pci_request_selected_regions(pdev, bars, LPFC_DRIVER_NAME))
+ if (pci_request_mem_regions(pdev, LPFC_DRIVER_NAME))
goto out_disable_device;
/* Set up device as PCI master and save state for EEH */
pci_set_master(pdev);
@@ -3970,7 +7501,7 @@ lpfc_enable_pci_dev(struct lpfc_hba *phba)
pci_save_state(pdev);
/* PCIe EEH recovery on powerpc platforms needs fundamental reset */
- if (pci_find_capability(pdev, PCI_CAP_ID_EXP))
+ if (pci_is_pcie(pdev))
pdev->needs_freset = 1;
return 0;
@@ -3978,6 +7509,8 @@ lpfc_enable_pci_dev(struct lpfc_hba *phba)
out_disable_device:
pci_disable_device(pdev);
out_error:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1401 Failed to enable pci device\n");
return -ENODEV;
}
@@ -3992,20 +7525,15 @@ static void
lpfc_disable_pci_dev(struct lpfc_hba *phba)
{
struct pci_dev *pdev;
- int bars;
/* Obtain PCI device reference */
if (!phba->pcidev)
return;
else
pdev = phba->pcidev;
- /* Select PCI BARs */
- bars = pci_select_bars(pdev, IORESOURCE_MEM);
/* Release PCI resource and disable PCI device */
- pci_release_selected_regions(pdev, bars);
+ pci_release_mem_regions(pdev);
pci_disable_device(pdev);
- /* Null out PCI private reference to driver */
- pci_set_drvdata(pdev, NULL);
return;
}
@@ -4022,16 +7550,62 @@ lpfc_disable_pci_dev(struct lpfc_hba *phba)
void
lpfc_reset_hba(struct lpfc_hba *phba)
{
+ int rc = 0;
+
/* If resets are disabled then set error state and return. */
if (!phba->cfg_enable_hba_reset) {
phba->link_state = LPFC_HBA_ERROR;
return;
}
- lpfc_offline_prep(phba);
+
+ /* If not LPFC_SLI_ACTIVE, force all IO to be flushed */
+ if (phba->sli.sli_flag & LPFC_SLI_ACTIVE) {
+ lpfc_offline_prep(phba, LPFC_MBX_WAIT);
+ } else {
+ if (test_bit(MBX_TMO_ERR, &phba->bit_flags)) {
+ /* Perform a PCI function reset to start from clean */
+ rc = lpfc_pci_function_reset(phba);
+ lpfc_els_flush_all_cmd(phba);
+ }
+ lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
+ lpfc_sli_flush_io_rings(phba);
+ }
lpfc_offline(phba);
- lpfc_sli_brdrestart(phba);
- lpfc_online(phba);
- lpfc_unblock_mgmt_io(phba);
+ clear_bit(MBX_TMO_ERR, &phba->bit_flags);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "8888 PCI function reset failed rc %x\n",
+ rc);
+ } else {
+ lpfc_sli_brdrestart(phba);
+ lpfc_online(phba);
+ lpfc_unblock_mgmt_io(phba);
+ }
+}
+
+/**
+ * lpfc_sli_sriov_nr_virtfn_get - Get the number of sr-iov virtual functions
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function enables the PCI SR-IOV virtual functions to a physical
+ * function. It invokes the PCI SR-IOV api with the @nr_vfn provided to
+ * enable the number of virtual functions to the physical function. As
+ * not all devices support SR-IOV, the return code from the pci_enable_sriov()
+ * API call does not considered as an error condition for most of the device.
+ **/
+uint16_t
+lpfc_sli_sriov_nr_virtfn_get(struct lpfc_hba *phba)
+{
+ struct pci_dev *pdev = phba->pcidev;
+ uint16_t nr_virtfn;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+ if (pos == 0)
+ return 0;
+
+ pci_read_config_word(pdev, pos + PCI_SRIOV_TOTAL_VF, &nr_virtfn);
+ return nr_virtfn;
}
/**
@@ -4049,8 +7623,17 @@ int
lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *phba, int nr_vfn)
{
struct pci_dev *pdev = phba->pcidev;
+ uint16_t max_nr_vfn;
int rc;
+ max_nr_vfn = lpfc_sli_sriov_nr_virtfn_get(phba);
+ if (nr_vfn > max_nr_vfn) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3057 Requested vfs (%d) greater than "
+ "supported vfs (%d)", nr_vfn, max_nr_vfn);
+ return -EINVAL;
+ }
+
rc = pci_enable_sriov(pdev, nr_vfn);
if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -4064,49 +7647,118 @@ lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *phba, int nr_vfn)
return rc;
}
+static void
+lpfc_unblock_requests_work(struct work_struct *work)
+{
+ struct lpfc_hba *phba = container_of(work, struct lpfc_hba,
+ unblock_request_work);
+
+ lpfc_unblock_requests(phba);
+}
+
+/**
+ * lpfc_setup_driver_resource_phase1 - Phase1 etup driver internal resources.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources before the
+ * device specific resource setup to support the HBA device it attached to.
+ *
+ * Return codes
+ * 0 - successful
+ * other values - error
+ **/
+static int
+lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+
+ /*
+ * Driver resources common to all SLI revisions
+ */
+ atomic_set(&phba->fast_event_count, 0);
+ atomic_set(&phba->dbg_log_idx, 0);
+ atomic_set(&phba->dbg_log_cnt, 0);
+ atomic_set(&phba->dbg_log_dmping, 0);
+ spin_lock_init(&phba->hbalock);
+
+ /* Initialize port_list spinlock */
+ spin_lock_init(&phba->port_list_lock);
+ INIT_LIST_HEAD(&phba->port_list);
+
+ INIT_LIST_HEAD(&phba->work_list);
+
+ /* Initialize the wait queue head for the kernel thread */
+ init_waitqueue_head(&phba->work_waitq);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1403 Protocols supported %s %s %s\n",
+ ((phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ?
+ "SCSI" : " "),
+ ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) ?
+ "NVME" : " "),
+ (phba->nvmet_support ? "NVMET" : " "));
+
+ /* ras_fwlog state */
+ spin_lock_init(&phba->ras_fwlog_lock);
+
+ /* Initialize the IO buffer list used by driver for SLI3 SCSI */
+ spin_lock_init(&phba->scsi_buf_list_get_lock);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_get);
+ spin_lock_init(&phba->scsi_buf_list_put_lock);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
+
+ /* Initialize the fabric iocb list */
+ INIT_LIST_HEAD(&phba->fabric_iocb_list);
+
+ /* Initialize list to save ELS buffers */
+ INIT_LIST_HEAD(&phba->elsbuf);
+
+ /* Initialize FCF connection rec list */
+ INIT_LIST_HEAD(&phba->fcf_conn_rec_list);
+
+ /* Initialize OAS configuration list */
+ spin_lock_init(&phba->devicelock);
+ INIT_LIST_HEAD(&phba->luns);
+
+ /* MBOX heartbeat timer */
+ timer_setup(&psli->mbox_tmo, lpfc_mbox_timeout, 0);
+ /* Fabric block timer */
+ timer_setup(&phba->fabric_block_timer, lpfc_fabric_block_timeout, 0);
+ /* EA polling mode timer */
+ timer_setup(&phba->eratt_poll, lpfc_poll_eratt, 0);
+ /* Heartbeat timer */
+ timer_setup(&phba->hb_tmofunc, lpfc_hb_timeout, 0);
+
+ INIT_DELAYED_WORK(&phba->eq_delay_work, lpfc_hb_eq_delay_work);
+
+ INIT_DELAYED_WORK(&phba->idle_stat_delay_work,
+ lpfc_idle_stat_delay_work);
+ INIT_WORK(&phba->unblock_request_work, lpfc_unblock_requests_work);
+ return 0;
+}
+
/**
- * lpfc_sli_driver_resource_setup - Setup driver internal resources for SLI3 dev.
+ * lpfc_sli_driver_resource_setup - Setup driver internal resources for SLI3 dev
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to set up the driver internal resources specific to
* support the SLI-3 HBA device it attached to.
*
* Return codes
- * 0 - successful
- * other values - error
+ * 0 - successful
+ * other values - error
**/
static int
lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
{
- struct lpfc_sli *psli;
- int rc;
+ int rc, entry_sz;
/*
* Initialize timers used by driver
*/
- /* Heartbeat timer */
- init_timer(&phba->hb_tmofunc);
- phba->hb_tmofunc.function = lpfc_hb_timeout;
- phba->hb_tmofunc.data = (unsigned long)phba;
-
- psli = &phba->sli;
- /* MBOX heartbeat timer */
- init_timer(&psli->mbox_tmo);
- psli->mbox_tmo.function = lpfc_mbox_timeout;
- psli->mbox_tmo.data = (unsigned long) phba;
/* FCP polling mode timer */
- init_timer(&phba->fcp_poll_timer);
- phba->fcp_poll_timer.function = lpfc_poll_timeout;
- phba->fcp_poll_timer.data = (unsigned long) phba;
- /* Fabric block timer */
- init_timer(&phba->fabric_block_timer);
- phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
- phba->fabric_block_timer.data = (unsigned long) phba;
- /* EA polling mode timer */
- init_timer(&phba->eratt_poll);
- phba->eratt_poll.function = lpfc_poll_eratt;
- phba->eratt_poll.data = (unsigned long) phba;
+ timer_setup(&phba->fcp_poll_timer, lpfc_poll_timeout, 0);
/* Host attention work mask setup */
phba->work_ha_mask = (HA_ERATT | HA_MBATT | HA_LATT);
@@ -4114,31 +7766,67 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
/* Get all the module params for configuring this host */
lpfc_get_cfgparam(phba);
- if (phba->pcidev->device == PCI_DEVICE_ID_HORNET) {
- phba->menlo_flag |= HBA_MENLO_SUPPORT;
- /* check for menlo minimum sg count */
- if (phba->cfg_sg_seg_cnt < LPFC_DEFAULT_MENLO_SG_SEG_CNT)
- phba->cfg_sg_seg_cnt = LPFC_DEFAULT_MENLO_SG_SEG_CNT;
- }
+ /* Set up phase-1 common device driver resources */
+
+ rc = lpfc_setup_driver_resource_phase1(phba);
+ if (rc)
+ return -ENODEV;
+
+ if (!phba->sli.sli3_ring)
+ phba->sli.sli3_ring = kcalloc(LPFC_SLI3_MAX_RING,
+ sizeof(struct lpfc_sli_ring),
+ GFP_KERNEL);
+ if (!phba->sli.sli3_ring)
+ return -ENOMEM;
/*
- * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+ * Since lpfc_sg_seg_cnt is module parameter, the sg_dma_buf_size
* used to create the sg_dma_buf_pool must be dynamically calculated.
- * 2 segments are added since the IOCB needs a command and response bde.
*/
- phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
- sizeof(struct fcp_rsp) +
- ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ entry_sz = sizeof(struct sli4_sge);
+ else
+ entry_sz = sizeof(struct ulp_bde64);
+
+ /* There are going to be 2 reserved BDEs: 1 FCP cmnd + 1 FCP rsp */
if (phba->cfg_enable_bg) {
- phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
- phba->cfg_sg_dma_buf_size +=
- phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
+ /*
+ * The scsi_buf for a T10-DIF I/O will hold the FCP cmnd,
+ * the FCP rsp, and a BDE for each. Sice we have no control
+ * over how many protection data segments the SCSI Layer
+ * will hand us (ie: there could be one for every block
+ * in the IO), we just allocate enough BDEs to accomidate
+ * our max amount and we need to limit lpfc_sg_seg_cnt to
+ * minimize the risk of running out.
+ */
+ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp) +
+ (LPFC_MAX_SG_SEG_CNT * entry_sz);
+
+ if (phba->cfg_sg_seg_cnt > LPFC_MAX_SG_SEG_CNT_DIF)
+ phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT_DIF;
+
+ /* Total BDEs in BPL for scsi_sg_list and scsi_sg_prot_list */
+ phba->cfg_total_seg_cnt = LPFC_MAX_SG_SEG_CNT;
+ } else {
+ /*
+ * The scsi_buf for a regular I/O will hold the FCP cmnd,
+ * the FCP rsp, a BDE for each, and a BDE for up to
+ * cfg_sg_seg_cnt data segments.
+ */
+ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp) +
+ ((phba->cfg_sg_seg_cnt + 2) * entry_sz);
+
+ /* Total BDEs in BPL for scsi_sg_list */
+ phba->cfg_total_seg_cnt = phba->cfg_sg_seg_cnt + 2;
}
- /* Also reinitialize the host templates with new values. */
- lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
- lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_FCP,
+ "9088 INIT sg_tablesize:%d dmabuf_size:%d total_bde:%d\n",
+ phba->cfg_sg_seg_cnt, phba->cfg_sg_dma_buf_size,
+ phba->cfg_total_seg_cnt);
phba->max_vpi = LPFC_MAX_VPI;
/* This will be set to correct value after config_port mbox */
@@ -4148,12 +7836,30 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
* Initialize the SLI Layer to run with lpfc HBAs.
*/
lpfc_sli_setup(phba);
- lpfc_sli_queue_setup(phba);
+ lpfc_sli_queue_init(phba);
/* Allocate device driver memory */
if (lpfc_mem_alloc(phba, BPL_ALIGN_SZ))
return -ENOMEM;
+ phba->lpfc_sg_dma_buf_pool =
+ dma_pool_create("lpfc_sg_dma_buf_pool",
+ &phba->pcidev->dev, phba->cfg_sg_dma_buf_size,
+ BPL_ALIGN_SZ, 0);
+
+ if (!phba->lpfc_sg_dma_buf_pool)
+ goto fail_free_mem;
+
+ phba->lpfc_cmd_rsp_buf_pool =
+ dma_pool_create("lpfc_cmd_rsp_buf_pool",
+ &phba->pcidev->dev,
+ sizeof(struct fcp_cmnd) +
+ sizeof(struct fcp_rsp),
+ BPL_ALIGN_SZ, 0);
+
+ if (!phba->lpfc_cmd_rsp_buf_pool)
+ goto fail_free_dma_buf_pool;
+
/*
* Enable sr-iov virtual functions if supported and configured
* through the module parameter.
@@ -4172,6 +7878,13 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
}
return 0;
+
+fail_free_dma_buf_pool:
+ dma_pool_destroy(phba->lpfc_sg_dma_buf_pool);
+ phba->lpfc_sg_dma_buf_pool = NULL;
+fail_free_mem:
+ lpfc_mem_free(phba);
+ return -ENOMEM;
}
/**
@@ -4204,47 +7917,51 @@ lpfc_sli_driver_resource_unset(struct lpfc_hba *phba)
static int
lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
{
- struct lpfc_sli *psli;
LPFC_MBOXQ_t *mboxq;
- int rc, i, hbq_count, buf_size, dma_buf_size, max_buf_size;
- uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0};
- struct lpfc_mqe *mqe;
- int longs, sli_family;
+ MAILBOX_t *mb;
+ int rc, i, max_buf_size;
+ int longs;
+ int extra;
+ uint64_t wwn;
+
+ phba->sli4_hba.num_present_cpu = lpfc_present_cpu;
+ phba->sli4_hba.num_possible_cpu = cpumask_last(cpu_possible_mask) + 1;
+ phba->sli4_hba.curr_disp_cpu = 0;
+
+ /* Get all the module params for configuring this host */
+ lpfc_get_cfgparam(phba);
+
+ /* Set up phase-1 common device driver resources */
+ rc = lpfc_setup_driver_resource_phase1(phba);
+ if (rc)
+ return -ENODEV;
/* Before proceed, wait for POST done and device ready */
rc = lpfc_sli4_post_status_check(phba);
if (rc)
return -ENODEV;
+ /* Allocate all driver workqueues here */
+
+ /* The lpfc_wq workqueue for deferred irq use */
+ phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM | WQ_PERCPU, 0);
+ if (!phba->wq)
+ return -ENOMEM;
+
/*
* Initialize timers used by driver
*/
- /* Heartbeat timer */
- init_timer(&phba->hb_tmofunc);
- phba->hb_tmofunc.function = lpfc_hb_timeout;
- phba->hb_tmofunc.data = (unsigned long)phba;
- init_timer(&phba->rrq_tmr);
- phba->rrq_tmr.function = lpfc_rrq_timeout;
- phba->rrq_tmr.data = (unsigned long)phba;
-
- psli = &phba->sli;
- /* MBOX heartbeat timer */
- init_timer(&psli->mbox_tmo);
- psli->mbox_tmo.function = lpfc_mbox_timeout;
- psli->mbox_tmo.data = (unsigned long) phba;
- /* Fabric block timer */
- init_timer(&phba->fabric_block_timer);
- phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
- phba->fabric_block_timer.data = (unsigned long) phba;
- /* EA polling mode timer */
- init_timer(&phba->eratt_poll);
- phba->eratt_poll.function = lpfc_poll_eratt;
- phba->eratt_poll.data = (unsigned long) phba;
+ timer_setup(&phba->rrq_tmr, lpfc_rrq_timeout, 0);
+
/* FCF rediscover timer */
- init_timer(&phba->fcf.redisc_wait);
- phba->fcf.redisc_wait.function = lpfc_sli4_fcf_redisc_wait_tmo;
- phba->fcf.redisc_wait.data = (unsigned long)phba;
+ timer_setup(&phba->fcf.redisc_wait, lpfc_sli4_fcf_redisc_wait_tmo, 0);
+
+ /* CMF congestion timer */
+ hrtimer_setup(&phba->cmf_timer, lpfc_cmf_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ /* CMF 1 minute stats collection timer */
+ hrtimer_setup(&phba->cmf_stats_timer, lpfc_cmf_stats_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
/*
* Control structure for handling external multi-buffer mailbox
@@ -4254,16 +7971,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
sizeof(struct lpfc_mbox_ext_buf_ctx));
INIT_LIST_HEAD(&phba->mbox_ext_buf_ctx.ext_dmabuf_list);
- /*
- * We need to do a READ_CONFIG mailbox command here before
- * calling lpfc_get_cfgparam. For VFs this will report the
- * MAX_XRI, MAX_VPI, MAX_RPI, MAX_IOCB, and MAX_VFI settings.
- * All of the resources allocated
- * for this Port are tied to these values.
- */
- /* Get all the module params for configuring this host */
- lpfc_get_cfgparam(phba);
phba->max_vpi = LPFC_MAX_VPI;
+
/* This will be set to correct value after the read_config mbox */
phba->max_vports = 0;
@@ -4274,57 +7983,41 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
/*
- * Since the sg_tablesize is module parameter, the sg_dma_buf_size
- * used to create the sg_dma_buf_pool must be dynamically calculated.
- * 2 segments are added since the IOCB needs a command and response bde.
- * To insure that the scsi sgl does not cross a 4k page boundary only
- * sgl sizes of must be a power of 2.
+ * For SLI4, instead of using ring 0 (LPFC_FCP_RING) for FCP commands
+ * we will associate a new ring, for each EQ/CQ/WQ tuple.
+ * The WQ create will allocate the ring.
*/
- buf_size = (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) +
- ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge)));
-
- sli_family = bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf);
- max_buf_size = LPFC_SLI4_MAX_BUF_SIZE;
- switch (sli_family) {
- case LPFC_SLI_INTF_FAMILY_BE2:
- case LPFC_SLI_INTF_FAMILY_BE3:
- /* There is a single hint for BE - 2 pages per BPL. */
- if (bf_get(lpfc_sli_intf_sli_hint1, &phba->sli4_hba.sli_intf) ==
- LPFC_SLI_INTF_SLI_HINT1_1)
- max_buf_size = LPFC_SLI4_FL1_MAX_BUF_SIZE;
- break;
- case LPFC_SLI_INTF_FAMILY_LNCR_A0:
- case LPFC_SLI_INTF_FAMILY_LNCR_B0:
- default:
- break;
- }
- for (dma_buf_size = LPFC_SLI4_MIN_BUF_SIZE;
- dma_buf_size < max_buf_size && buf_size > dma_buf_size;
- dma_buf_size = dma_buf_size << 1)
- ;
- if (dma_buf_size == max_buf_size)
- phba->cfg_sg_seg_cnt = (dma_buf_size -
- sizeof(struct fcp_cmnd) - sizeof(struct fcp_rsp) -
- (2 * sizeof(struct sli4_sge))) /
- sizeof(struct sli4_sge);
- phba->cfg_sg_dma_buf_size = dma_buf_size;
/* Initialize buffer queue management fields */
- hbq_count = lpfc_sli_hbq_count();
- for (i = 0; i < hbq_count; ++i)
- INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list);
- INIT_LIST_HEAD(&phba->rb_pend_list);
+ INIT_LIST_HEAD(&phba->hbqs[LPFC_ELS_HBQ].hbq_buffer_list);
phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_sli4_rb_alloc;
phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_sli4_rb_free;
+ /* for VMID idle timeout if VMID is enabled */
+ if (lpfc_is_vmid_enabled(phba))
+ timer_setup(&phba->inactive_vmid_poll, lpfc_vmid_poll, 0);
+
/*
* Initialize the SLI Layer to run with lpfc SLI4 HBAs.
*/
- /* Initialize the Abort scsi buffer list used by driver */
- spin_lock_init(&phba->sli4_hba.abts_scsi_buf_list_lock);
- INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_scsi_buf_list);
+ /* Initialize the Abort buffer list used by driver */
+ spin_lock_init(&phba->sli4_hba.abts_io_buf_list_lock);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_io_buf_list);
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ /* Initialize the Abort nvme buffer list used by driver */
+ spin_lock_init(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_io_wait_list);
+ spin_lock_init(&phba->sli4_hba.t_active_list_lock);
+ INIT_LIST_HEAD(&phba->sli4_hba.t_active_ctx_list);
+ }
+
/* This abort list used by worker thread */
- spin_lock_init(&phba->sli4_hba.abts_sgl_list_lock);
+ spin_lock_init(&phba->sli4_hba.sgl_list_lock);
+ spin_lock_init(&phba->sli4_hba.nvmet_io_wait_lock);
+ spin_lock_init(&phba->sli4_hba.asynce_list_lock);
+ spin_lock_init(&phba->sli4_hba.els_xri_abrt_list_lock);
/*
* Initialize driver internal slow-path work queues
@@ -4336,8 +8029,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
INIT_LIST_HEAD(&phba->sli4_hba.sp_queue_event);
/* Asynchronous event CQ Event work queue list */
INIT_LIST_HEAD(&phba->sli4_hba.sp_asynce_work_queue);
- /* Fast-path XRI aborted CQ Event work queue list */
- INIT_LIST_HEAD(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue);
/* Slow-path XRI aborted CQ Event work queue list */
INIT_LIST_HEAD(&phba->sli4_hba.sp_els_xri_aborted_work_queue);
/* Receive queue CQ Event work queue list */
@@ -4349,21 +8040,29 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_vfi_blk_list);
INIT_LIST_HEAD(&phba->lpfc_vpi_blk_list);
- /* Initialize the driver internal SLI layer lists. */
- lpfc_sli_setup(phba);
- lpfc_sli_queue_setup(phba);
+ /* Initialize mboxq lists. If the early init routines fail
+ * these lists need to be correctly initialized.
+ */
+ INIT_LIST_HEAD(&phba->sli.mboxq);
+ INIT_LIST_HEAD(&phba->sli.mboxq_cmpl);
+
+ /* initialize optic_state to 0xFF */
+ phba->sli4_hba.lnk_info.optic_state = 0xff;
/* Allocate device driver memory */
rc = lpfc_mem_alloc(phba, SGL_ALIGN_SZ);
if (rc)
- return -ENOMEM;
+ goto out_destroy_workqueue;
/* IF Type 2 ports get initialized now. */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
LPFC_SLI_INTF_IF_TYPE_2) {
rc = lpfc_pci_function_reset(phba);
- if (unlikely(rc))
- return -ENODEV;
+ if (unlikely(rc)) {
+ rc = -ENODEV;
+ goto out_free_mem;
+ }
+ phba->temp_sensor_support = 1;
}
/* Create the bootstrap mailbox command */
@@ -4381,6 +8080,21 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
if (unlikely(rc))
goto out_free_bsmbx;
+ if (phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_CONFIG) {
+ /* Right now the link is down, if FA-PWWN is configured the
+ * firmware will try FLOGI before the driver gets a link up.
+ * If it fails, the driver should get a MISCONFIGURED async
+ * event which will clear this flag. The only notification
+ * the driver gets is if it fails, if it succeeds there is no
+ * notification given. Assume success.
+ */
+ phba->sli4_hba.fawwpn_flag |= LPFC_FAWWPN_FABRIC;
+ }
+
+ rc = lpfc_mem_alloc_active_rrq_pool_s4(phba);
+ if (unlikely(rc))
+ goto out_free_bsmbx;
+
/* IF Type 0 ports get initialized now. */
if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
LPFC_SLI_INTF_IF_TYPE_0) {
@@ -4396,31 +8110,71 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_bsmbx;
}
- /* Get the Supported Pages if PORT_CAPABILITIES is supported by port. */
- lpfc_supported_pages(mboxq);
- rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
- if (!rc) {
- mqe = &mboxq->u.mqe;
- memcpy(&pn_page[0], ((uint8_t *)&mqe->un.supp_pages.word3),
- LPFC_MAX_SUPPORTED_PAGES);
- for (i = 0; i < LPFC_MAX_SUPPORTED_PAGES; i++) {
- switch (pn_page[i]) {
- case LPFC_SLI4_PARAMETERS:
- phba->sli4_hba.pc_sli4_params.supported = 1;
- break;
- default:
- break;
- }
- }
- /* Read the port's SLI4 Parameters capabilities if supported. */
- if (phba->sli4_hba.pc_sli4_params.supported)
- rc = lpfc_pc_sli4_params_get(phba, mboxq);
- if (rc) {
+ /* Check for NVMET being configured */
+ phba->nvmet_support = 0;
+ if (lpfc_enable_nvmet_cnt) {
+
+ /* First get WWN of HBA instance */
+ lpfc_read_nv(phba, mboxq);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6016 Mailbox failed , mbxCmd x%x "
+ "READ_NV, mbxStatus x%x\n",
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ bf_get(lpfc_mqe_status, &mboxq->u.mqe));
mempool_free(mboxq, phba->mbox_mem_pool);
rc = -EIO;
goto out_free_bsmbx;
}
+ mb = &mboxq->u.mb;
+ memcpy(&wwn, (char *)mb->un.varRDnvp.nodename,
+ sizeof(uint64_t));
+ wwn = cpu_to_be64(wwn);
+ phba->sli4_hba.wwnn.u.name = wwn;
+ memcpy(&wwn, (char *)mb->un.varRDnvp.portname,
+ sizeof(uint64_t));
+ /* wwn is WWPN of HBA instance */
+ wwn = cpu_to_be64(wwn);
+ phba->sli4_hba.wwpn.u.name = wwn;
+
+ /* Check to see if it matches any module parameter */
+ for (i = 0; i < lpfc_enable_nvmet_cnt; i++) {
+ if (wwn == lpfc_enable_nvmet[i]) {
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ if (lpfc_nvmet_mem_alloc(phba))
+ break;
+
+ phba->nvmet_support = 1; /* a match */
+
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6017 NVME Target %016llx\n",
+ wwn);
+#else
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6021 Can't enable NVME Target."
+ " NVME_TARGET_FC infrastructure"
+ " is not in kernel\n");
+#endif
+ /* Not supported for NVMET */
+ phba->cfg_xri_rebalancing = 0;
+ if (phba->irq_chann_mode == NHT_MODE) {
+ phba->cfg_irq_chann =
+ phba->sli4_hba.num_present_cpu;
+ phba->cfg_hdw_queue =
+ phba->sli4_hba.num_present_cpu;
+ phba->irq_chann_mode = NORMAL_MODE;
+ }
+ break;
+ }
+ }
}
+
+ lpfc_nvme_mod_param_dep(phba);
+
/*
* Get sli4 parameters that override parameters from Port capabilities.
* If this call fails, it isn't critical unless the SLI4 parameters come
@@ -4428,75 +8182,237 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
*/
rc = lpfc_get_sli4_parameters(phba, mboxq);
if (rc) {
- if (phba->sli4_hba.extents_in_use &&
- phba->sli4_hba.rpi_hdrs_in_use) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2999 Unsupported SLI4 Parameters "
- "Extents and RPI headers enabled.\n");
- goto out_free_bsmbx;
- }
+ lpfc_log_msg(phba, KERN_WARNING, LOG_INIT,
+ "2999 Could not get SLI4 parameters\n");
+ rc = -EIO;
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ goto out_free_bsmbx;
+ }
+
+ /*
+ * 1 for cmd, 1 for rsp, NVME adds an extra one
+ * for boundary conditions in its max_sgl_segment template.
+ */
+ extra = 2;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ extra++;
+
+ /*
+ * It doesn't matter what family our adapter is in, we are
+ * limited to 2 Pages, 512 SGEs, for our SGL.
+ * There are going to be 2 reserved SGEs: 1 FCP cmnd + 1 FCP rsp
+ */
+ max_buf_size = (2 * SLI4_PAGE_SIZE);
+
+ /*
+ * Since lpfc_sg_seg_cnt is module param, the sg_dma_buf_size
+ * used to create the sg_dma_buf_pool must be calculated.
+ */
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ /* Both cfg_enable_bg and cfg_external_dif code paths */
+
+ /*
+ * The scsi_buf for a T10-DIF I/O holds the FCP cmnd,
+ * the FCP rsp, and a SGE. Sice we have no control
+ * over how many protection segments the SCSI Layer
+ * will hand us (ie: there could be one for every block
+ * in the IO), just allocate enough SGEs to accomidate
+ * our max amount and we need to limit lpfc_sg_seg_cnt
+ * to minimize the risk of running out.
+ */
+ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd32) +
+ sizeof(struct fcp_rsp) + max_buf_size;
+
+ /* Total SGEs for scsi_sg_list and scsi_sg_prot_list */
+ phba->cfg_total_seg_cnt = LPFC_MAX_SGL_SEG_CNT;
+
+ /*
+ * If supporting DIF, reduce the seg count for scsi to
+ * allow room for the DIF sges.
+ */
+ if (phba->cfg_enable_bg &&
+ phba->cfg_sg_seg_cnt > LPFC_MAX_BG_SLI4_SEG_CNT_DIF)
+ phba->cfg_scsi_seg_cnt = LPFC_MAX_BG_SLI4_SEG_CNT_DIF;
+ else
+ phba->cfg_scsi_seg_cnt = phba->cfg_sg_seg_cnt;
+
+ } else {
+ /*
+ * The scsi_buf for a regular I/O holds the FCP cmnd,
+ * the FCP rsp, a SGE for each, and a SGE for up to
+ * cfg_sg_seg_cnt data segments.
+ */
+ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd32) +
+ sizeof(struct fcp_rsp) +
+ ((phba->cfg_sg_seg_cnt + extra) *
+ sizeof(struct sli4_sge));
+
+ /* Total SGEs for scsi_sg_list */
+ phba->cfg_total_seg_cnt = phba->cfg_sg_seg_cnt + extra;
+ phba->cfg_scsi_seg_cnt = phba->cfg_sg_seg_cnt;
+
+ /*
+ * NOTE: if (phba->cfg_sg_seg_cnt + extra) <= 256 we only
+ * need to post 1 page for the SGL.
+ */
+ }
+
+ if (phba->cfg_xpsgl && !phba->nvmet_support)
+ phba->cfg_sg_dma_buf_size = LPFC_DEFAULT_XPSGL_SIZE;
+ else if (phba->cfg_sg_dma_buf_size <= LPFC_MIN_SG_SLI4_BUF_SZ)
+ phba->cfg_sg_dma_buf_size = LPFC_MIN_SG_SLI4_BUF_SZ;
+ else
+ phba->cfg_sg_dma_buf_size =
+ SLI4_PAGE_ALIGN(phba->cfg_sg_dma_buf_size);
+
+ phba->border_sge_num = phba->cfg_sg_dma_buf_size /
+ sizeof(struct sli4_sge);
+
+ /* Limit to LPFC_MAX_NVME_SEG_CNT for NVME. */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ if (phba->cfg_sg_seg_cnt > LPFC_MAX_NVME_SEG_CNT) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_INIT,
+ "6300 Reducing NVME sg segment "
+ "cnt to %d\n",
+ LPFC_MAX_NVME_SEG_CNT);
+ phba->cfg_nvme_seg_cnt = LPFC_MAX_NVME_SEG_CNT;
+ } else
+ phba->cfg_nvme_seg_cnt = phba->cfg_sg_seg_cnt;
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_FCP,
+ "9087 sg_seg_cnt:%d dmabuf_size:%d "
+ "total:%d scsi:%d nvme:%d\n",
+ phba->cfg_sg_seg_cnt, phba->cfg_sg_dma_buf_size,
+ phba->cfg_total_seg_cnt, phba->cfg_scsi_seg_cnt,
+ phba->cfg_nvme_seg_cnt);
+
+ i = min(phba->cfg_sg_dma_buf_size, SLI4_PAGE_SIZE);
+
+ phba->lpfc_sg_dma_buf_pool =
+ dma_pool_create("lpfc_sg_dma_buf_pool",
+ &phba->pcidev->dev,
+ phba->cfg_sg_dma_buf_size,
+ i, 0);
+ if (!phba->lpfc_sg_dma_buf_pool) {
+ rc = -ENOMEM;
+ goto out_free_bsmbx;
}
+
+ phba->lpfc_cmd_rsp_buf_pool =
+ dma_pool_create("lpfc_cmd_rsp_buf_pool",
+ &phba->pcidev->dev,
+ sizeof(struct fcp_cmnd32) +
+ sizeof(struct fcp_rsp),
+ i, 0);
+ if (!phba->lpfc_cmd_rsp_buf_pool) {
+ rc = -ENOMEM;
+ goto out_free_sg_dma_buf;
+ }
+
mempool_free(mboxq, phba->mbox_mem_pool);
- /* Create all the SLI4 queues */
- rc = lpfc_sli4_queue_create(phba);
+
+ /* Verify OAS is supported */
+ lpfc_sli4_oas_verify(phba);
+
+ /* Verify RAS support on adapter */
+ lpfc_sli4_ras_init(phba);
+
+ /* Verify all the SLI4 queues */
+ rc = lpfc_sli4_queue_verify(phba);
if (rc)
- goto out_free_bsmbx;
+ goto out_free_cmd_rsp_buf;
/* Create driver internal CQE event pool */
rc = lpfc_sli4_cq_event_pool_create(phba);
if (rc)
- goto out_destroy_queue;
+ goto out_free_cmd_rsp_buf;
- /* Initialize and populate the iocb list per host */
- rc = lpfc_init_sgl_list(phba);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1400 Failed to initialize sgl list.\n");
- goto out_destroy_cq_event_pool;
- }
+ /* Initialize sgl lists per host */
+ lpfc_init_sgl_list(phba);
+
+ /* Allocate and initialize active sgl array */
rc = lpfc_init_active_sgl_array(phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1430 Failed to initialize sgl list.\n");
- goto out_free_sgl_list;
+ goto out_destroy_cq_event_pool;
}
rc = lpfc_sli4_init_rpi_hdrs(phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1432 Failed to initialize rpi headers.\n");
goto out_free_active_sgl;
}
/* Allocate eligible FCF bmask memory for FCF roundrobin failover */
longs = (LPFC_SLI4_FCF_TBL_INDX_MAX + BITS_PER_LONG - 1)/BITS_PER_LONG;
- phba->fcf.fcf_rr_bmask = kzalloc(longs * sizeof(unsigned long),
+ phba->fcf.fcf_rr_bmask = kcalloc(longs, sizeof(unsigned long),
GFP_KERNEL);
if (!phba->fcf.fcf_rr_bmask) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2759 Failed allocate memory for FCF round "
"robin failover bmask\n");
rc = -ENOMEM;
goto out_remove_rpi_hdrs;
}
- phba->sli4_hba.fcp_eq_hdl = kzalloc((sizeof(struct lpfc_fcp_eq_hdl) *
- phba->cfg_fcp_eq_count), GFP_KERNEL);
- if (!phba->sli4_hba.fcp_eq_hdl) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2572 Failed allocate memory for fast-path "
- "per-EQ handle array\n");
+ phba->sli4_hba.hba_eq_hdl = kcalloc(phba->cfg_irq_chann,
+ sizeof(struct lpfc_hba_eq_hdl),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.hba_eq_hdl) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2572 Failed allocate memory for "
+ "fast-path per-EQ handle array\n");
rc = -ENOMEM;
goto out_free_fcf_rr_bmask;
}
- phba->sli4_hba.msix_entries = kzalloc((sizeof(struct msix_entry) *
- phba->sli4_hba.cfg_eqn), GFP_KERNEL);
- if (!phba->sli4_hba.msix_entries) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2573 Failed allocate memory for msi-x "
- "interrupt vector entries\n");
+ phba->sli4_hba.cpu_map = kcalloc(phba->sli4_hba.num_possible_cpu,
+ sizeof(struct lpfc_vector_map_info),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.cpu_map) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3327 Failed allocate memory for msi-x "
+ "interrupt vector mapping\n");
+ rc = -ENOMEM;
+ goto out_free_hba_eq_hdl;
+ }
+
+ phba->sli4_hba.eq_info = alloc_percpu(struct lpfc_eq_intr_info);
+ if (!phba->sli4_hba.eq_info) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3321 Failed allocation for per_cpu stats\n");
+ rc = -ENOMEM;
+ goto out_free_hba_cpu_map;
+ }
+
+ phba->sli4_hba.idle_stat = kcalloc(phba->sli4_hba.num_possible_cpu,
+ sizeof(*phba->sli4_hba.idle_stat),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.idle_stat) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3390 Failed allocation for idle_stat\n");
+ rc = -ENOMEM;
+ goto out_free_hba_eq_info;
+ }
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ phba->sli4_hba.c_stat = alloc_percpu(struct lpfc_hdwq_stat);
+ if (!phba->sli4_hba.c_stat) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3332 Failed allocating per cpu hdwq stats\n");
rc = -ENOMEM;
- goto out_free_fcp_eq_hdl;
+ goto out_free_hba_idle_stat;
+ }
+#endif
+
+ phba->cmf_stat = alloc_percpu(struct lpfc_cgn_stat);
+ if (!phba->cmf_stat) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3331 Failed allocating per cpu cgn stats\n");
+ rc = -ENOMEM;
+ goto out_free_hba_hdwq_info;
}
/*
@@ -4516,26 +8432,41 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
}
}
- return rc;
+ return 0;
-out_free_fcp_eq_hdl:
- kfree(phba->sli4_hba.fcp_eq_hdl);
+out_free_hba_hdwq_info:
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ free_percpu(phba->sli4_hba.c_stat);
+out_free_hba_idle_stat:
+#endif
+ kfree(phba->sli4_hba.idle_stat);
+out_free_hba_eq_info:
+ free_percpu(phba->sli4_hba.eq_info);
+out_free_hba_cpu_map:
+ kfree(phba->sli4_hba.cpu_map);
+out_free_hba_eq_hdl:
+ kfree(phba->sli4_hba.hba_eq_hdl);
out_free_fcf_rr_bmask:
kfree(phba->fcf.fcf_rr_bmask);
out_remove_rpi_hdrs:
lpfc_sli4_remove_rpi_hdrs(phba);
out_free_active_sgl:
lpfc_free_active_sgl(phba);
-out_free_sgl_list:
- lpfc_free_sgl_list(phba);
out_destroy_cq_event_pool:
lpfc_sli4_cq_event_pool_destroy(phba);
-out_destroy_queue:
- lpfc_sli4_queue_destroy(phba);
+out_free_cmd_rsp_buf:
+ dma_pool_destroy(phba->lpfc_cmd_rsp_buf_pool);
+ phba->lpfc_cmd_rsp_buf_pool = NULL;
+out_free_sg_dma_buf:
+ dma_pool_destroy(phba->lpfc_sg_dma_buf_pool);
+ phba->lpfc_sg_dma_buf_pool = NULL;
out_free_bsmbx:
lpfc_destroy_bootstrap_mbox(phba);
out_free_mem:
lpfc_mem_free(phba);
+out_destroy_workqueue:
+ destroy_workqueue(phba->wq);
+ phba->wq = NULL;
return rc;
}
@@ -4551,11 +8482,22 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
{
struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry;
- /* Free memory allocated for msi-x interrupt vector entries */
- kfree(phba->sli4_hba.msix_entries);
+ free_percpu(phba->sli4_hba.eq_info);
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ free_percpu(phba->sli4_hba.c_stat);
+#endif
+ free_percpu(phba->cmf_stat);
+ kfree(phba->sli4_hba.idle_stat);
+
+ /* Free memory allocated for msi-x interrupt vector to CPU mapping */
+ kfree(phba->sli4_hba.cpu_map);
+ phba->sli4_hba.num_possible_cpu = 0;
+ phba->sli4_hba.num_present_cpu = 0;
+ phba->sli4_hba.curr_disp_cpu = 0;
+ cpumask_clear(&phba->sli4_hba.irq_aff_mask);
/* Free memory allocated for fast-path work queue handles */
- kfree(phba->sli4_hba.fcp_eq_hdl);
+ kfree(phba->sli4_hba.hba_eq_hdl);
/* Free the allocated rpi headers. */
lpfc_sli4_remove_rpi_hdrs(phba);
@@ -4566,13 +8508,8 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
/* Free the ELS sgl list */
lpfc_free_active_sgl(phba);
- lpfc_free_sgl_list(phba);
-
- /* Free the SCSI sgl management array */
- kfree(phba->sli4_hba.lpfc_scsi_psb_array);
-
- /* Free the SLI4 queues */
- lpfc_sli4_queue_destroy(phba);
+ lpfc_free_els_sgl_list(phba);
+ lpfc_free_nvmet_sgl_list(phba);
/* Free the completion queue EQ event pool */
lpfc_sli4_cq_event_release_all(phba);
@@ -4629,58 +8566,11 @@ lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
"1431 Invalid HBA PCI-device group: 0x%x\n",
dev_grp);
return -ENODEV;
- break;
}
return 0;
}
/**
- * lpfc_setup_driver_resource_phase1 - Phase1 etup driver internal resources.
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to set up the driver internal resources before the
- * device specific resource setup to support the HBA device it attached to.
- *
- * Return codes
- * 0 - successful
- * other values - error
- **/
-static int
-lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
-{
- /*
- * Driver resources common to all SLI revisions
- */
- atomic_set(&phba->fast_event_count, 0);
- spin_lock_init(&phba->hbalock);
-
- /* Initialize ndlp management spinlock */
- spin_lock_init(&phba->ndlp_lock);
-
- INIT_LIST_HEAD(&phba->port_list);
- INIT_LIST_HEAD(&phba->work_list);
- init_waitqueue_head(&phba->wait_4_mlo_m_q);
-
- /* Initialize the wait queue head for the kernel thread */
- init_waitqueue_head(&phba->work_waitq);
-
- /* Initialize the scsi buffer list used by driver for scsi IO */
- spin_lock_init(&phba->scsi_buf_list_lock);
- INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list);
-
- /* Initialize the fabric iocb list */
- INIT_LIST_HEAD(&phba->fabric_iocb_list);
-
- /* Initialize list to save ELS buffers */
- INIT_LIST_HEAD(&phba->elsbuf);
-
- /* Initialize FCF connection rec list */
- INIT_LIST_HEAD(&phba->fcf_conn_rec_list);
-
- return 0;
-}
-
-/**
* lpfc_setup_driver_resource_phase2 - Phase2 setup driver internal resources.
* @phba: pointer to lpfc hba data structure.
*
@@ -4718,8 +8608,14 @@ lpfc_setup_driver_resource_phase2(struct lpfc_hba *phba)
static void
lpfc_unset_driver_resource_phase2(struct lpfc_hba *phba)
{
+ if (phba->wq) {
+ destroy_workqueue(phba->wq);
+ phba->wq = NULL;
+ }
+
/* Stop kernel worker thread */
- kthread_stop(phba->worker_thread);
+ if (phba->worker_thread)
+ kthread_stop(phba->worker_thread);
}
/**
@@ -4728,7 +8624,7 @@ lpfc_unset_driver_resource_phase2(struct lpfc_hba *phba)
*
* This routine is invoked to free the driver's IOCB list and memory.
**/
-static void
+void
lpfc_free_iocb_list(struct lpfc_hba *phba)
{
struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL;
@@ -4748,6 +8644,7 @@ lpfc_free_iocb_list(struct lpfc_hba *phba)
/**
* lpfc_init_iocb_list - Allocate and initialize iocb list.
* @phba: pointer to lpfc hba data structure.
+ * @iocb_count: number of requested iocbs
*
* This routine is invoked to allocate and initizlize the driver's IOCB
* list and set up the IOCB tag array accordingly.
@@ -4756,7 +8653,7 @@ lpfc_free_iocb_list(struct lpfc_hba *phba)
* 0 - successful
* other values - error
**/
-static int
+int
lpfc_init_iocb_list(struct lpfc_hba *phba, int iocb_count)
{
struct lpfc_iocbq *iocbq_entry = NULL;
@@ -4770,7 +8667,7 @@ lpfc_init_iocb_list(struct lpfc_hba *phba, int iocb_count)
if (iocbq_entry == NULL) {
printk(KERN_ERR "%s: only allocated %d iocbs of "
"expected %d count. Unloading driver.\n",
- __func__, i, LPFC_IOCB_LIST_CNT);
+ __func__, i, iocb_count);
goto out_free_iocbq;
}
@@ -4799,29 +8696,75 @@ out_free_iocbq:
}
/**
- * lpfc_free_sgl_list - Free sgl list.
+ * lpfc_free_sgl_list - Free a given sgl list.
+ * @phba: pointer to lpfc hba data structure.
+ * @sglq_list: pointer to the head of sgl list.
+ *
+ * This routine is invoked to free a give sgl list and memory.
+ **/
+void
+lpfc_free_sgl_list(struct lpfc_hba *phba, struct list_head *sglq_list)
+{
+ struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+
+ list_for_each_entry_safe(sglq_entry, sglq_next, sglq_list, list) {
+ list_del(&sglq_entry->list);
+ lpfc_mbuf_free(phba, sglq_entry->virt, sglq_entry->phys);
+ kfree(sglq_entry);
+ }
+}
+
+/**
+ * lpfc_free_els_sgl_list - Free els sgl list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the driver's els sgl list and memory.
+ **/
+static void
+lpfc_free_els_sgl_list(struct lpfc_hba *phba)
+{
+ LIST_HEAD(sglq_list);
+
+ /* Retrieve all els sgls from driver list */
+ spin_lock_irq(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_els_sgl_list, &sglq_list);
+ spin_unlock_irq(&phba->sli4_hba.sgl_list_lock);
+
+ /* Now free the sgl list */
+ lpfc_free_sgl_list(phba, &sglq_list);
+}
+
+/**
+ * lpfc_free_nvmet_sgl_list - Free nvmet sgl list.
* @phba: pointer to lpfc hba data structure.
*
- * This routine is invoked to free the driver's sgl list and memory.
+ * This routine is invoked to free the driver's nvmet sgl list and memory.
**/
static void
-lpfc_free_sgl_list(struct lpfc_hba *phba)
+lpfc_free_nvmet_sgl_list(struct lpfc_hba *phba)
{
struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
LIST_HEAD(sglq_list);
+ /* Retrieve all nvmet sgls from driver list */
spin_lock_irq(&phba->hbalock);
- list_splice_init(&phba->sli4_hba.lpfc_sgl_list, &sglq_list);
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(&phba->sli4_hba.lpfc_nvmet_sgl_list, &sglq_list);
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
spin_unlock_irq(&phba->hbalock);
- list_for_each_entry_safe(sglq_entry, sglq_next,
- &sglq_list, list) {
+ /* Now free the sgl list */
+ list_for_each_entry_safe(sglq_entry, sglq_next, &sglq_list, list) {
list_del(&sglq_entry->list);
- lpfc_mbuf_free(phba, sglq_entry->virt, sglq_entry->phys);
+ lpfc_nvmet_buf_free(phba, sglq_entry->virt, sglq_entry->phys);
kfree(sglq_entry);
- phba->sli4_hba.total_sglq_bufs--;
}
- kfree(phba->sli4_hba.lpfc_els_sgl_array);
+
+ /* Update the nvmet_xri_cnt to reflect no current sgls.
+ * The next initialization cycle sets the count and allocates
+ * the sgls over again.
+ */
+ phba->sli4_hba.nvmet_xri_cnt = 0;
}
/**
@@ -4866,99 +8809,21 @@ lpfc_free_active_sgl(struct lpfc_hba *phba)
* This routine is invoked to allocate and initizlize the driver's sgl
* list and set up the sgl xritag tag array accordingly.
*
- * Return codes
- * 0 - successful
- * other values - error
**/
-static int
+static void
lpfc_init_sgl_list(struct lpfc_hba *phba)
{
- struct lpfc_sglq *sglq_entry = NULL;
- int i;
- int els_xri_cnt;
-
- els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "2400 ELS XRI count %d.\n",
- els_xri_cnt);
/* Initialize and populate the sglq list per host/VF. */
- INIT_LIST_HEAD(&phba->sli4_hba.lpfc_sgl_list);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_els_sgl_list);
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_els_sgl_list);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_sgl_list);
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
- /* Sanity check on XRI management */
- if (phba->sli4_hba.max_cfg_param.max_xri <= els_xri_cnt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2562 No room left for SCSI XRI allocation: "
- "max_xri=%d, els_xri=%d\n",
- phba->sli4_hba.max_cfg_param.max_xri,
- els_xri_cnt);
- return -ENOMEM;
- }
-
- /* Allocate memory for the ELS XRI management array */
- phba->sli4_hba.lpfc_els_sgl_array =
- kzalloc((sizeof(struct lpfc_sglq *) * els_xri_cnt),
- GFP_KERNEL);
-
- if (!phba->sli4_hba.lpfc_els_sgl_array) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2401 Failed to allocate memory for ELS "
- "XRI management array of size %d.\n",
- els_xri_cnt);
- return -ENOMEM;
- }
-
- /* Keep the SCSI XRI into the XRI management array */
- phba->sli4_hba.scsi_xri_max =
- phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt;
- phba->sli4_hba.scsi_xri_cnt = 0;
- phba->sli4_hba.lpfc_scsi_psb_array =
- kzalloc((sizeof(struct lpfc_scsi_buf *) *
- phba->sli4_hba.scsi_xri_max), GFP_KERNEL);
-
- if (!phba->sli4_hba.lpfc_scsi_psb_array) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2563 Failed to allocate memory for SCSI "
- "XRI management array of size %d.\n",
- phba->sli4_hba.scsi_xri_max);
- kfree(phba->sli4_hba.lpfc_els_sgl_array);
- return -ENOMEM;
- }
+ /* els xri-sgl book keeping */
+ phba->sli4_hba.els_xri_cnt = 0;
- for (i = 0; i < els_xri_cnt; i++) {
- sglq_entry = kzalloc(sizeof(struct lpfc_sglq), GFP_KERNEL);
- if (sglq_entry == NULL) {
- printk(KERN_ERR "%s: only allocated %d sgls of "
- "expected %d count. Unloading driver.\n",
- __func__, i, els_xri_cnt);
- goto out_free_mem;
- }
-
- sglq_entry->buff_type = GEN_BUFF_TYPE;
- sglq_entry->virt = lpfc_mbuf_alloc(phba, 0, &sglq_entry->phys);
- if (sglq_entry->virt == NULL) {
- kfree(sglq_entry);
- printk(KERN_ERR "%s: failed to allocate mbuf.\n"
- "Unloading driver.\n", __func__);
- goto out_free_mem;
- }
- sglq_entry->sgl = sglq_entry->virt;
- memset(sglq_entry->sgl, 0, LPFC_BPL_SIZE);
-
- /* The list order is used by later block SGL registraton */
- spin_lock_irq(&phba->hbalock);
- sglq_entry->state = SGL_FREED;
- list_add_tail(&sglq_entry->list, &phba->sli4_hba.lpfc_sgl_list);
- phba->sli4_hba.lpfc_els_sgl_array[i] = sglq_entry;
- phba->sli4_hba.total_sglq_bufs++;
- spin_unlock_irq(&phba->hbalock);
- }
- return 0;
-
-out_free_mem:
- kfree(phba->sli4_hba.lpfc_scsi_psb_array);
- lpfc_free_sgl_list(phba);
- return -ENOMEM;
+ /* nvme xri-buffer book keeping */
+ phba->sli4_hba.io_xri_cnt = 0;
}
/**
@@ -4966,17 +8831,14 @@ out_free_mem:
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to post rpi header templates to the
- * HBA consistent with the SLI-4 interface spec. This routine
+ * port for those SLI4 ports that do not support extents. This routine
* posts a PAGE_SIZE memory region to the port to hold up to
- * PAGE_SIZE modulo 64 rpi context headers.
- * No locks are held here because this is an initialization routine
- * called only from probe or lpfc_online when interrupts are not
- * enabled and the driver is reinitializing the device.
+ * PAGE_SIZE modulo 64 rpi context headers. This is an initialization routine
+ * and should be called only when interrupts are disabled.
*
* Return codes
* 0 - successful
- * -ENOMEM - No available memory
- * -EIO - The mailbox failed to complete successfully.
+ * -ERROR - otherwise.
**/
int
lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *phba)
@@ -4985,21 +8847,14 @@ lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *phba)
struct lpfc_rpi_hdr *rpi_hdr;
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_rpi_hdr_list);
- /*
- * If the SLI4 port supports extents, posting the rpi header isn't
- * required. Set the expected maximum count and let the actual value
- * get set when extents are fully allocated.
- */
- if (!phba->sli4_hba.rpi_hdrs_in_use) {
- phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.max_rpi;
+ if (!phba->sli4_hba.rpi_hdrs_in_use)
return rc;
- }
if (phba->sli4_hba.extents_in_use)
return -EIO;
rpi_hdr = lpfc_sli4_create_rpi_hdr(phba);
if (!rpi_hdr) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0391 Error during rpi post operation\n");
lpfc_sli4_remove_rpis(phba);
rc = -ENODEV;
@@ -5027,7 +8882,6 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
uint16_t rpi_limit, curr_rpi_range;
struct lpfc_dmabuf *dmabuf;
struct lpfc_rpi_hdr *rpi_hdr;
- uint32_t rpi_count;
/*
* If the SLI4 port supports extents, posting the rpi header isn't
@@ -5040,8 +8894,7 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
return NULL;
/* The limit on the logical index is just the max_rpi count. */
- rpi_limit = phba->sli4_hba.max_cfg_param.rpi_base +
- phba->sli4_hba.max_cfg_param.max_rpi - 1;
+ rpi_limit = phba->sli4_hba.max_cfg_param.max_rpi;
spin_lock_irq(&phba->hbalock);
/*
@@ -5049,22 +8902,13 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
* rpi is normalized to a zero base because the physical rpi is
* port based.
*/
- curr_rpi_range = phba->sli4_hba.next_rpi -
- phba->sli4_hba.max_cfg_param.rpi_base;
+ curr_rpi_range = phba->sli4_hba.next_rpi;
spin_unlock_irq(&phba->hbalock);
- /*
- * The port has a limited number of rpis. The increment here
- * is LPFC_RPI_HDR_COUNT - 1 to account for the starting value
- * and to allow the full max_rpi range per port.
- */
- if ((curr_rpi_range + (LPFC_RPI_HDR_COUNT - 1)) > rpi_limit)
- rpi_count = rpi_limit - curr_rpi_range;
- else
- rpi_count = LPFC_RPI_HDR_COUNT;
-
- if (!rpi_count)
+ /* Reached full RPI range */
+ if (curr_rpi_range == rpi_limit)
return NULL;
+
/*
* First allocate the protocol header region for the port. The
* port expects a 4KB DMA-mapped memory region that is 4K aligned.
@@ -5075,14 +8919,12 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
LPFC_HDR_TEMPLATE_SIZE,
- &dmabuf->phys,
- GFP_KERNEL);
+ &dmabuf->phys, GFP_KERNEL);
if (!dmabuf->virt) {
rpi_hdr = NULL;
goto err_free_dmabuf;
}
- memset(dmabuf->virt, 0, LPFC_HDR_TEMPLATE_SIZE);
if (!IS_ALIGNED(dmabuf->phys, LPFC_HDR_TEMPLATE_SIZE)) {
rpi_hdr = NULL;
goto err_free_coherent;
@@ -5100,13 +8942,9 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
/* The rpi_hdr stores the logical index only. */
rpi_hdr->start_rpi = curr_rpi_range;
+ rpi_hdr->next_rpi = phba->sli4_hba.next_rpi + LPFC_RPI_HDR_COUNT;
list_add_tail(&rpi_hdr->list, &phba->sli4_hba.lpfc_rpi_hdr_list);
- /*
- * The next_rpi stores the next logical module-64 rpi value used
- * to post physical rpis in subsequent rpi postings.
- */
- phba->sli4_hba.next_rpi += rpi_count;
spin_unlock_irq(&phba->hbalock);
return rpi_hdr;
@@ -5181,6 +9019,7 @@ lpfc_hba_alloc(struct pci_dev *pdev)
kfree(phba);
return NULL;
}
+ phba->eratt_poll_interval = LPFC_ERATT_POLL_INTERVAL;
spin_lock_init(&phba->ct_ev_lock);
INIT_LIST_HEAD(&phba->ct_ev_waiters);
@@ -5198,14 +9037,51 @@ lpfc_hba_alloc(struct pci_dev *pdev)
static void
lpfc_hba_free(struct lpfc_hba *phba)
{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ kfree(phba->sli4_hba.hdwq);
+
/* Release the driver assigned board number */
idr_remove(&lpfc_hba_index, phba->brd_no);
+ /* Free memory allocated with sli3 rings */
+ kfree(phba->sli.sli3_ring);
+ phba->sli.sli3_ring = NULL;
+
kfree(phba);
return;
}
/**
+ * lpfc_setup_fdmi_mask - Setup initial FDMI mask for HBA and Port attributes
+ * @vport: pointer to lpfc vport data structure.
+ *
+ * This routine is will setup initial FDMI attribute masks for
+ * FDMI2 or SmartSAN depending on module parameters. The driver will attempt
+ * to get these attributes first before falling back, the attribute
+ * fallback hierarchy is SmartSAN -> FDMI2 -> FMDI1
+ **/
+void
+lpfc_setup_fdmi_mask(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+
+ set_bit(FC_ALLOW_FDMI, &vport->load_flag);
+ if (phba->cfg_enable_SmartSAN ||
+ phba->cfg_fdmi_on == LPFC_FDMI_SUPPORT) {
+ /* Setup appropriate attribute masks */
+ vport->fdmi_hba_mask = LPFC_FDMI2_HBA_ATTR;
+ if (phba->cfg_enable_SmartSAN)
+ vport->fdmi_port_mask = LPFC_FDMI2_SMART_ATTR;
+ else
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "6077 Setup FDMI mask: hba x%x port x%x\n",
+ vport->fdmi_hba_mask, vport->fdmi_port_mask);
+}
+
+/**
* lpfc_create_shost - Create hba physical port with associated scsi host.
* @phba: pointer to lpfc hba data structure.
*
@@ -5235,10 +9111,25 @@ lpfc_create_shost(struct lpfc_hba *phba)
shost = lpfc_shost_from_vport(vport);
phba->pport = vport;
+
+ if (phba->nvmet_support) {
+ /* Only 1 vport (pport) will support NVME target */
+ phba->targetport = NULL;
+ phba->cfg_enable_fc4_type = LPFC_ENABLE_NVME;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_NVME_DISC,
+ "6076 NVME Target Found\n");
+ }
+
lpfc_debugfs_initialize(vport);
/* Put reference to SCSI host to driver's device private data */
pci_set_drvdata(phba->pcidev, shost);
+ lpfc_setup_fdmi_mask(vport);
+
+ /*
+ * At this point we are fully registered with PSA. In addition,
+ * any initial discovery should be completed.
+ */
return 0;
}
@@ -5271,63 +9162,45 @@ lpfc_destroy_shost(struct lpfc_hba *phba)
static void
lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
{
- int pagecnt = 10;
- if (lpfc_prot_mask && lpfc_prot_guard) {
+ uint32_t old_mask;
+ uint32_t old_guard;
+
+ if (phba->cfg_prot_mask && phba->cfg_prot_guard) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"1478 Registering BlockGuard with the "
"SCSI layer\n");
- scsi_host_set_prot(shost, lpfc_prot_mask);
- scsi_host_set_guard(shost, lpfc_prot_guard);
- }
- if (!_dump_buf_data) {
- while (pagecnt) {
- spin_lock_init(&_dump_buf_lock);
- _dump_buf_data =
- (char *) __get_free_pages(GFP_KERNEL, pagecnt);
- if (_dump_buf_data) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9043 BLKGRD: allocated %d pages for "
- "_dump_buf_data at 0x%p\n",
- (1 << pagecnt), _dump_buf_data);
- _dump_buf_data_order = pagecnt;
- memset(_dump_buf_data, 0,
- ((1 << PAGE_SHIFT) << pagecnt));
- break;
- } else
- --pagecnt;
- }
- if (!_dump_buf_data_order)
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9044 BLKGRD: ERROR unable to allocate "
- "memory for hexdump\n");
- } else
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9045 BLKGRD: already allocated _dump_buf_data=0x%p"
- "\n", _dump_buf_data);
- if (!_dump_buf_dif) {
- while (pagecnt) {
- _dump_buf_dif =
- (char *) __get_free_pages(GFP_KERNEL, pagecnt);
- if (_dump_buf_dif) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9046 BLKGRD: allocated %d pages for "
- "_dump_buf_dif at 0x%p\n",
- (1 << pagecnt), _dump_buf_dif);
- _dump_buf_dif_order = pagecnt;
- memset(_dump_buf_dif, 0,
- ((1 << PAGE_SHIFT) << pagecnt));
- break;
- } else
- --pagecnt;
- }
- if (!_dump_buf_dif_order)
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9047 BLKGRD: ERROR unable to allocate "
- "memory for hexdump\n");
- } else
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9048 BLKGRD: already allocated _dump_buf_dif=0x%p\n",
- _dump_buf_dif);
+
+ old_mask = phba->cfg_prot_mask;
+ old_guard = phba->cfg_prot_guard;
+
+ /* Only allow supported values */
+ phba->cfg_prot_mask &= (SHOST_DIF_TYPE1_PROTECTION |
+ SHOST_DIX_TYPE0_PROTECTION |
+ SHOST_DIX_TYPE1_PROTECTION);
+ phba->cfg_prot_guard &= (SHOST_DIX_GUARD_IP |
+ SHOST_DIX_GUARD_CRC);
+
+ /* DIF Type 1 protection for profiles AST1/C1 is end to end */
+ if (phba->cfg_prot_mask == SHOST_DIX_TYPE1_PROTECTION)
+ phba->cfg_prot_mask |= SHOST_DIF_TYPE1_PROTECTION;
+
+ if (phba->cfg_prot_mask && phba->cfg_prot_guard) {
+ if ((old_mask != phba->cfg_prot_mask) ||
+ (old_guard != phba->cfg_prot_guard))
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "1475 Registering BlockGuard with the "
+ "SCSI layer: mask %d guard %d\n",
+ phba->cfg_prot_mask,
+ phba->cfg_prot_guard);
+
+ scsi_host_set_prot(shost, phba->cfg_prot_mask);
+ scsi_host_set_guard(shost, phba->cfg_prot_guard);
+ } else
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "1479 Not Registering BlockGuard with the SCSI "
+ "layer, Bad protection parameters: %d %d\n",
+ old_mask, old_guard);
+ }
}
/**
@@ -5352,8 +9225,6 @@ lpfc_post_init_setup(struct lpfc_hba *phba)
*/
shost = pci_get_drvdata(phba->pcidev);
shost->can_queue = phba->cfg_hba_queue_depth - 10;
- if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
- lpfc_setup_bg(phba, shost);
lpfc_host_attrib_init(shost);
@@ -5389,26 +9260,22 @@ lpfc_post_init_setup(struct lpfc_hba *phba)
static int
lpfc_sli_pci_mem_setup(struct lpfc_hba *phba)
{
- struct pci_dev *pdev;
+ struct pci_dev *pdev = phba->pcidev;
unsigned long bar0map_len, bar2map_len;
int i, hbq_count;
void *ptr;
- int error = -ENODEV;
+ int error;
- /* Obtain PCI device reference */
- if (!phba->pcidev)
- return error;
- else
- pdev = phba->pcidev;
+ if (!pdev)
+ return -ENODEV;
/* Set the device DMA mask size */
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0
- || pci_set_consistent_dma_mask(pdev,DMA_BIT_MASK(64)) != 0) {
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0
- || pci_set_consistent_dma_mask(pdev,DMA_BIT_MASK(32)) != 0) {
- return error;
- }
- }
+ error = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (error)
+ error = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (error)
+ return error;
+ error = -ENODEV;
/* Get the bus address of Bar0 and Bar2 and the number of bytes
* required by each mapping.
@@ -5436,14 +9303,11 @@ lpfc_sli_pci_mem_setup(struct lpfc_hba *phba)
}
/* Allocate memory for SLI-2 structures */
- phba->slim2p.virt = dma_alloc_coherent(&pdev->dev,
- SLI2_SLIM_SIZE,
- &phba->slim2p.phys,
- GFP_KERNEL);
+ phba->slim2p.virt = dma_alloc_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+ &phba->slim2p.phys, GFP_KERNEL);
if (!phba->slim2p.virt)
goto out_iounmap;
- memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx);
phba->mbox_ext = (phba->slim2p.virt +
offsetof(struct lpfc_sli2_slim, mbx_ext_words));
@@ -5471,8 +9335,6 @@ lpfc_sli_pci_mem_setup(struct lpfc_hba *phba)
memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size());
- INIT_LIST_HEAD(&phba->rb_pend_list);
-
phba->MBslimaddr = phba->slim_memmap_p;
phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET;
@@ -5565,7 +9427,7 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
* other register reads as the data may not be valid. Just exit.
*/
if (port_error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1408 Port Failed POST - portsmphr=0x%x, "
"perr=x%x, sfi=x%x, nip=x%x, ipc=x%x, scr1=x%x, "
"scr2=x%x, hscratch=x%x, pstatus=x%x\n",
@@ -5614,7 +9476,8 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
readl(phba->sli4_hba.u.if_type0.UERRHIregaddr);
if ((~phba->sli4_hba.ue_mask_lo & uerrlo_reg.word0) ||
(~phba->sli4_hba.ue_mask_hi & uerrhi_reg.word0)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
"1422 Unrecoverable Error "
"Detected during POST "
"uerr_lo_reg=0x%x, "
@@ -5629,29 +9492,36 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
}
break;
case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_6:
/* Final checks. The port status should be clean. */
if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
&reg_data.word0) ||
- (bf_get(lpfc_sliport_status_err, &reg_data) &&
- !bf_get(lpfc_sliport_status_rn, &reg_data))) {
+ lpfc_sli4_unrecoverable_port(&reg_data)) {
phba->work_status[0] =
readl(phba->sli4_hba.u.if_type2.
ERR1regaddr);
phba->work_status[1] =
readl(phba->sli4_hba.u.if_type2.
ERR2regaddr);
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2888 Port Error Detected "
- "during POST: "
- "port status reg 0x%x, "
- "port_smphr reg 0x%x, "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2888 Unrecoverable port error "
+ "following POST: port status reg "
+ "0x%x, port_smphr reg 0x%x, "
"error 1=0x%x, error 2=0x%x\n",
reg_data.word0,
portsmphr_reg.word0,
phba->work_status[0],
phba->work_status[1]);
port_error = -ENODEV;
+ break;
}
+
+ if (lpfc_pldv_detect &&
+ bf_get(lpfc_sli_intf_sli_family,
+ &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_FAMILY_G6)
+ pci_write_config_byte(phba->pcidev,
+ LPFC_SLI_INTF, CFG_PLD);
break;
case LPFC_SLI_INTF_IF_TYPE_1:
default:
@@ -5686,29 +9556,62 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba, uint32_t if_type)
phba->sli4_hba.conf_regs_memmap_p + LPFC_SLI_INTF;
break;
case LPFC_SLI_INTF_IF_TYPE_2:
+ phba->sli4_hba.u.if_type2.EQDregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_EQ_DELAY_OFFSET;
phba->sli4_hba.u.if_type2.ERR1regaddr =
- phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_ERR_1;
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER1_OFFSET;
phba->sli4_hba.u.if_type2.ERR2regaddr =
- phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_ERR_2;
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER2_OFFSET;
phba->sli4_hba.u.if_type2.CTRLregaddr =
- phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_CNTRL;
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_CTL_OFFSET;
phba->sli4_hba.u.if_type2.STATUSregaddr =
- phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_STATUS;
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_STA_OFFSET;
phba->sli4_hba.SLIINTFregaddr =
phba->sli4_hba.conf_regs_memmap_p + LPFC_SLI_INTF;
phba->sli4_hba.PSMPHRregaddr =
- phba->sli4_hba.conf_regs_memmap_p + LPFC_SLIPORT_IF2_SMPHR;
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_SEM_OFFSET;
phba->sli4_hba.RQDBregaddr =
- phba->sli4_hba.conf_regs_memmap_p + LPFC_RQ_DOORBELL;
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_ULP0_RQ_DOORBELL;
phba->sli4_hba.WQDBregaddr =
- phba->sli4_hba.conf_regs_memmap_p + LPFC_WQ_DOORBELL;
- phba->sli4_hba.EQCQDBregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_ULP0_WQ_DOORBELL;
+ phba->sli4_hba.CQDBregaddr =
phba->sli4_hba.conf_regs_memmap_p + LPFC_EQCQ_DOORBELL;
+ phba->sli4_hba.EQDBregaddr = phba->sli4_hba.CQDBregaddr;
phba->sli4_hba.MQDBregaddr =
phba->sli4_hba.conf_regs_memmap_p + LPFC_MQ_DOORBELL;
phba->sli4_hba.BMBXregaddr =
phba->sli4_hba.conf_regs_memmap_p + LPFC_BMBX;
break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ phba->sli4_hba.u.if_type2.EQDregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_EQ_DELAY_OFFSET;
+ phba->sli4_hba.u.if_type2.ERR1regaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER1_OFFSET;
+ phba->sli4_hba.u.if_type2.ERR2regaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER2_OFFSET;
+ phba->sli4_hba.u.if_type2.CTRLregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_CTL_OFFSET;
+ phba->sli4_hba.u.if_type2.STATUSregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_STA_OFFSET;
+ phba->sli4_hba.PSMPHRregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_SEM_OFFSET;
+ phba->sli4_hba.BMBXregaddr =
+ phba->sli4_hba.conf_regs_memmap_p + LPFC_BMBX;
+ break;
case LPFC_SLI_INTF_IF_TYPE_1:
default:
dev_printk(KERN_ERR, &phba->pcidev->dev,
@@ -5721,21 +9624,45 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba, uint32_t if_type)
/**
* lpfc_sli4_bar1_register_memmap - Set up SLI4 BAR1 register memory map.
* @phba: pointer to lpfc hba data structure.
+ * @if_type: sli if type to operate on.
*
- * This routine is invoked to set up SLI4 BAR1 control status register (CSR)
- * memory map.
+ * This routine is invoked to set up SLI4 BAR1 register memory map.
**/
static void
-lpfc_sli4_bar1_register_memmap(struct lpfc_hba *phba)
+lpfc_sli4_bar1_register_memmap(struct lpfc_hba *phba, uint32_t if_type)
{
- phba->sli4_hba.PSMPHRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_SLIPORT_IF0_SMPHR;
- phba->sli4_hba.ISRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_HST_ISR0;
- phba->sli4_hba.IMRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_HST_IMR0;
- phba->sli4_hba.ISCRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_HST_ISCR0;
+ switch (if_type) {
+ case LPFC_SLI_INTF_IF_TYPE_0:
+ phba->sli4_hba.PSMPHRregaddr =
+ phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_SLIPORT_IF0_SMPHR;
+ phba->sli4_hba.ISRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_ISR0;
+ phba->sli4_hba.IMRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_IMR0;
+ phba->sli4_hba.ISCRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_ISCR0;
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ phba->sli4_hba.RQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_RQ_DOORBELL;
+ phba->sli4_hba.WQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_WQ_DOORBELL;
+ phba->sli4_hba.CQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_CQ_DOORBELL;
+ phba->sli4_hba.EQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_EQ_DOORBELL;
+ phba->sli4_hba.MQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_MQ_DOORBELL;
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_1:
+ default:
+ dev_err(&phba->pcidev->dev,
+ "FATAL - unsupported SLI4 interface type - %d\n",
+ if_type);
+ break;
+ }
}
/**
@@ -5755,11 +9682,15 @@ lpfc_sli4_bar2_register_memmap(struct lpfc_hba *phba, uint32_t vf)
return -ENODEV;
phba->sli4_hba.RQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
- vf * LPFC_VFR_PAGE_SIZE + LPFC_RQ_DOORBELL);
+ vf * LPFC_VFR_PAGE_SIZE +
+ LPFC_ULP0_RQ_DOORBELL);
phba->sli4_hba.WQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
- vf * LPFC_VFR_PAGE_SIZE + LPFC_WQ_DOORBELL);
- phba->sli4_hba.EQCQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
- vf * LPFC_VFR_PAGE_SIZE + LPFC_EQCQ_DOORBELL);
+ vf * LPFC_VFR_PAGE_SIZE +
+ LPFC_ULP0_WQ_DOORBELL);
+ phba->sli4_hba.CQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+ vf * LPFC_VFR_PAGE_SIZE +
+ LPFC_EQCQ_DOORBELL);
+ phba->sli4_hba.EQDBregaddr = phba->sli4_hba.CQDBregaddr;
phba->sli4_hba.MQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
vf * LPFC_VFR_PAGE_SIZE + LPFC_MQ_DOORBELL);
phba->sli4_hba.BMBXregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
@@ -5800,15 +9731,12 @@ lpfc_create_bootstrap_mbox(struct lpfc_hba *phba)
* plus an alignment restriction of 16 bytes.
*/
bmbx_size = sizeof(struct lpfc_bmbx_create) + (LPFC_ALIGN_16_BYTE - 1);
- dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
- bmbx_size,
- &dmabuf->phys,
- GFP_KERNEL);
+ dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev, bmbx_size,
+ &dmabuf->phys, GFP_KERNEL);
if (!dmabuf->virt) {
kfree(dmabuf);
return -ENOMEM;
}
- memset(dmabuf->virt, 0, bmbx_size);
/*
* Initialize the bootstrap mailbox pointers now so that the register
@@ -5868,6 +9796,83 @@ lpfc_destroy_bootstrap_mbox(struct lpfc_hba *phba)
memset(&phba->sli4_hba.bmbx, 0, sizeof(struct lpfc_bmbx));
}
+static const char * const lpfc_topo_to_str[] = {
+ "Loop then P2P",
+ "Loopback",
+ "P2P Only",
+ "Unsupported",
+ "Loop Only",
+ "Unsupported",
+ "P2P then Loop",
+};
+
+#define LINK_FLAGS_DEF 0x0
+#define LINK_FLAGS_P2P 0x1
+#define LINK_FLAGS_LOOP 0x2
+/**
+ * lpfc_map_topology - Map the topology read from READ_CONFIG
+ * @phba: pointer to lpfc hba data structure.
+ * @rd_config: pointer to read config data
+ *
+ * This routine is invoked to map the topology values as read
+ * from the read config mailbox command. If the persistent
+ * topology feature is supported, the firmware will provide the
+ * saved topology information to be used in INIT_LINK
+ **/
+static void
+lpfc_map_topology(struct lpfc_hba *phba, struct lpfc_mbx_read_config *rd_config)
+{
+ u8 ptv, tf, pt;
+
+ ptv = bf_get(lpfc_mbx_rd_conf_ptv, rd_config);
+ tf = bf_get(lpfc_mbx_rd_conf_tf, rd_config);
+ pt = bf_get(lpfc_mbx_rd_conf_pt, rd_config);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2027 Read Config Data : ptv:0x%x, tf:0x%x pt:0x%x",
+ ptv, tf, pt);
+ if (!ptv) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2019 FW does not support persistent topology "
+ "Using driver parameter defined value [%s]",
+ lpfc_topo_to_str[phba->cfg_topology]);
+ return;
+ }
+ /* FW supports persistent topology - override module parameter value */
+ set_bit(HBA_PERSISTENT_TOPO, &phba->hba_flag);
+
+ /* if ASIC_GEN_NUM >= 0xC) */
+ if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_6) ||
+ (bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_FAMILY_G6)) {
+ if (!tf)
+ phba->cfg_topology = ((pt == LINK_FLAGS_LOOP)
+ ? FLAGS_TOPOLOGY_MODE_LOOP
+ : FLAGS_TOPOLOGY_MODE_PT_PT);
+ else
+ clear_bit(HBA_PERSISTENT_TOPO, &phba->hba_flag);
+ } else { /* G5 */
+ if (tf)
+ /* If topology failover set - pt is '0' or '1' */
+ phba->cfg_topology = (pt ? FLAGS_TOPOLOGY_MODE_PT_LOOP :
+ FLAGS_TOPOLOGY_MODE_LOOP_PT);
+ else
+ phba->cfg_topology = ((pt == LINK_FLAGS_P2P)
+ ? FLAGS_TOPOLOGY_MODE_PT_PT
+ : FLAGS_TOPOLOGY_MODE_LOOP);
+ }
+ if (test_bit(HBA_PERSISTENT_TOPO, &phba->hba_flag))
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2020 Using persistent topology value [%s]",
+ lpfc_topo_to_str[phba->cfg_topology]);
+ else
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2021 Invalid topology values from FW "
+ "Using driver parameter defined value [%s]",
+ lpfc_topo_to_str[phba->cfg_topology]);
+}
+
/**
* lpfc_sli4_read_config - Get the config parameters.
* @phba: pointer to lpfc hba data structure.
@@ -5882,7 +9887,7 @@ lpfc_destroy_bootstrap_mbox(struct lpfc_hba *phba)
* -ENOMEM - No available memory
* -EIO - The mailbox failed to complete successfully.
**/
-static int
+int
lpfc_sli4_read_config(struct lpfc_hba *phba)
{
LPFC_MBOXQ_t *pmb;
@@ -5891,12 +9896,14 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
uint32_t shdr_status, shdr_add_status;
struct lpfc_mbx_get_func_cfg *get_func_cfg;
struct lpfc_rsrc_desc_fcfcoe *desc;
- uint32_t desc_count;
- int length, i, rc = 0;
+ char *pdesc_0;
+ uint16_t forced_link_speed;
+ uint32_t if_type, qmin, fawwpn;
+ int length, i, rc = 0, rc2;
pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2011 Unable to allocate memory for issuing "
"SLI_CONFIG_SPECIAL mailbox command\n");
return -ENOMEM;
@@ -5906,22 +9913,64 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2012 Mailbox failed , mbxCmd x%x "
- "READ_CONFIG, mbxStatus x%x\n",
- bf_get(lpfc_mqe_command, &pmb->u.mqe),
- bf_get(lpfc_mqe_status, &pmb->u.mqe));
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2012 Mailbox failed , mbxCmd x%x "
+ "READ_CONFIG, mbxStatus x%x\n",
+ bf_get(lpfc_mqe_command, &pmb->u.mqe),
+ bf_get(lpfc_mqe_status, &pmb->u.mqe));
rc = -EIO;
} else {
rd_config = &pmb->u.mqe.un.rd_config;
+ if (bf_get(lpfc_mbx_rd_conf_lnk_ldv, rd_config)) {
+ phba->sli4_hba.lnk_info.lnk_dv = LPFC_LNK_DAT_VAL;
+ phba->sli4_hba.lnk_info.lnk_tp =
+ bf_get(lpfc_mbx_rd_conf_lnk_type, rd_config);
+ phba->sli4_hba.lnk_info.lnk_no =
+ bf_get(lpfc_mbx_rd_conf_lnk_numb, rd_config);
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3081 lnk_type:%d, lnk_numb:%d\n",
+ phba->sli4_hba.lnk_info.lnk_tp,
+ phba->sli4_hba.lnk_info.lnk_no);
+ } else
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3082 Mailbox (x%x) returned ldv:x0\n",
+ bf_get(lpfc_mqe_command, &pmb->u.mqe));
+ if (bf_get(lpfc_mbx_rd_conf_bbscn_def, rd_config)) {
+ phba->bbcredit_support = 1;
+ phba->sli4_hba.bbscn_params.word0 = rd_config->word8;
+ }
+
+ fawwpn = bf_get(lpfc_mbx_rd_conf_fawwpn, rd_config);
+
+ if (fawwpn) {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_INIT | LOG_DISCOVERY,
+ "2702 READ_CONFIG: FA-PWWN is "
+ "configured on\n");
+ phba->sli4_hba.fawwpn_flag |= LPFC_FAWWPN_CONFIG;
+ } else {
+ /* Clear FW configured flag, preserve driver flag */
+ phba->sli4_hba.fawwpn_flag &= ~LPFC_FAWWPN_CONFIG;
+ }
+
+ phba->sli4_hba.conf_trunk =
+ bf_get(lpfc_mbx_rd_conf_trunk, rd_config);
phba->sli4_hba.extents_in_use =
bf_get(lpfc_mbx_rd_conf_extnts_inuse, rd_config);
+
phba->sli4_hba.max_cfg_param.max_xri =
bf_get(lpfc_mbx_rd_conf_xri_count, rd_config);
+ /* Reduce resource usage in kdump environment */
+ if (is_kdump_kernel() &&
+ phba->sli4_hba.max_cfg_param.max_xri > 512)
+ phba->sli4_hba.max_cfg_param.max_xri = 512;
phba->sli4_hba.max_cfg_param.xri_base =
bf_get(lpfc_mbx_rd_conf_xri_base, rd_config);
phba->sli4_hba.max_cfg_param.max_vpi =
bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config);
+ /* Limit the max we support */
+ if (phba->sli4_hba.max_cfg_param.max_vpi > LPFC_MAX_VPORTS)
+ phba->sli4_hba.max_cfg_param.max_vpi = LPFC_MAX_VPORTS;
phba->sli4_hba.max_cfg_param.vpi_base =
bf_get(lpfc_mbx_rd_conf_vpi_base, rd_config);
phba->sli4_hba.max_cfg_param.max_rpi =
@@ -5946,17 +9995,63 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->sli4_hba.next_xri = phba->sli4_hba.max_cfg_param.xri_base;
phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base;
phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base;
- phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
phba->max_vpi = (phba->sli4_hba.max_cfg_param.max_vpi > 0) ?
(phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0;
phba->max_vports = phba->max_vpi;
+
+ /* Next decide on FPIN or Signal E2E CGN support
+ * For congestion alarms and warnings valid combination are:
+ * 1. FPIN alarms / FPIN warnings
+ * 2. Signal alarms / Signal warnings
+ * 3. FPIN alarms / Signal warnings
+ * 4. Signal alarms / FPIN warnings
+ *
+ * Initialize the adapter frequency to 100 mSecs
+ */
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_BOTH;
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ phba->cgn_sig_freq = lpfc_fabric_cgn_frequency;
+
+ if (lpfc_use_cgn_signal) {
+ if (bf_get(lpfc_mbx_rd_conf_wcs, rd_config)) {
+ phba->cgn_reg_signal = EDC_CG_SIG_WARN_ONLY;
+ phba->cgn_reg_fpin &= ~LPFC_CGN_FPIN_WARN;
+ }
+ if (bf_get(lpfc_mbx_rd_conf_acs, rd_config)) {
+ /* MUST support both alarm and warning
+ * because EDC does not support alarm alone.
+ */
+ if (phba->cgn_reg_signal !=
+ EDC_CG_SIG_WARN_ONLY) {
+ /* Must support both or none */
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_BOTH;
+ phba->cgn_reg_signal =
+ EDC_CG_SIG_NOTSUPPORTED;
+ } else {
+ phba->cgn_reg_signal =
+ EDC_CG_SIG_WARN_ALARM;
+ phba->cgn_reg_fpin =
+ LPFC_CGN_FPIN_NONE;
+ }
+ }
+ }
+
+ /* Set the congestion initial signal and fpin values. */
+ phba->cgn_init_reg_fpin = phba->cgn_reg_fpin;
+ phba->cgn_init_reg_signal = phba->cgn_reg_signal;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6446 READ_CONFIG reg_sig x%x reg_fpin:x%x\n",
+ phba->cgn_reg_signal, phba->cgn_reg_fpin);
+
+ lpfc_map_topology(phba, rd_config);
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"2003 cfg params Extents? %d "
"XRI(B:%d M:%d), "
"VPI(B:%d M:%d) "
"VFI(B:%d M:%d) "
"RPI(B:%d M:%d) "
- "FCFI(Count:%d)\n",
+ "FCFI:%d EQ:%d CQ:%d WQ:%d RQ:%d lmt:x%x\n",
phba->sli4_hba.extents_in_use,
phba->sli4_hba.max_cfg_param.xri_base,
phba->sli4_hba.max_cfg_param.max_xri,
@@ -5966,21 +10061,120 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->sli4_hba.max_cfg_param.max_vfi,
phba->sli4_hba.max_cfg_param.rpi_base,
phba->sli4_hba.max_cfg_param.max_rpi,
- phba->sli4_hba.max_cfg_param.max_fcfi);
+ phba->sli4_hba.max_cfg_param.max_fcfi,
+ phba->sli4_hba.max_cfg_param.max_eq,
+ phba->sli4_hba.max_cfg_param.max_cq,
+ phba->sli4_hba.max_cfg_param.max_wq,
+ phba->sli4_hba.max_cfg_param.max_rq,
+ phba->lmt);
+
+ /*
+ * Calculate queue resources based on how
+ * many WQ/CQ/EQs are available.
+ */
+ qmin = phba->sli4_hba.max_cfg_param.max_wq;
+ if (phba->sli4_hba.max_cfg_param.max_cq < qmin)
+ qmin = phba->sli4_hba.max_cfg_param.max_cq;
+ /*
+ * Reserve 4 (ELS, NVME LS, MBOX, plus one extra) and
+ * the remainder can be used for NVME / FCP.
+ */
+ qmin -= 4;
+ if (phba->sli4_hba.max_cfg_param.max_eq < qmin)
+ qmin = phba->sli4_hba.max_cfg_param.max_eq;
+
+ /* Check to see if there is enough for default cfg */
+ if ((phba->cfg_irq_chann > qmin) ||
+ (phba->cfg_hdw_queue > qmin)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2005 Reducing Queues - "
+ "FW resource limitation: "
+ "WQ %d CQ %d EQ %d: min %d: "
+ "IRQ %d HDWQ %d\n",
+ phba->sli4_hba.max_cfg_param.max_wq,
+ phba->sli4_hba.max_cfg_param.max_cq,
+ phba->sli4_hba.max_cfg_param.max_eq,
+ qmin, phba->cfg_irq_chann,
+ phba->cfg_hdw_queue);
+
+ if (phba->cfg_irq_chann > qmin)
+ phba->cfg_irq_chann = qmin;
+ if (phba->cfg_hdw_queue > qmin)
+ phba->cfg_hdw_queue = qmin;
+ }
}
if (rc)
goto read_cfg_out;
+ /* Update link speed if forced link speed is supported */
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
+ forced_link_speed =
+ bf_get(lpfc_mbx_rd_conf_link_speed, rd_config);
+ if (forced_link_speed) {
+ set_bit(HBA_FORCED_LINK_SPEED, &phba->hba_flag);
+
+ switch (forced_link_speed) {
+ case LINK_SPEED_1G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_1G;
+ break;
+ case LINK_SPEED_2G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_2G;
+ break;
+ case LINK_SPEED_4G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_4G;
+ break;
+ case LINK_SPEED_8G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_8G;
+ break;
+ case LINK_SPEED_10G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_10G;
+ break;
+ case LINK_SPEED_16G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_16G;
+ break;
+ case LINK_SPEED_32G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_32G;
+ break;
+ case LINK_SPEED_64G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_64G;
+ break;
+ case 0xffff:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_AUTO;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "0047 Unrecognized link "
+ "speed : %d\n",
+ forced_link_speed);
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_AUTO;
+ }
+ }
+ }
+
/* Reset the DFT_HBA_Q_DEPTH to the max xri */
- if (phba->cfg_hba_queue_depth >
- (phba->sli4_hba.max_cfg_param.max_xri -
- lpfc_sli4_get_els_iocb_cnt(phba)))
- phba->cfg_hba_queue_depth =
- phba->sli4_hba.max_cfg_param.max_xri -
- lpfc_sli4_get_els_iocb_cnt(phba);
-
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ length = phba->sli4_hba.max_cfg_param.max_xri -
+ lpfc_sli4_get_els_iocb_cnt(phba);
+ if (phba->cfg_hba_queue_depth > length) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "3361 HBA queue depth changed from %d to %d\n",
+ phba->cfg_hba_queue_depth, length);
+ phba->cfg_hba_queue_depth = length;
+ }
+
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2)
goto read_cfg_out;
@@ -5991,30 +10185,35 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
LPFC_MBOX_OPCODE_GET_FUNCTION_CONFIG,
length, LPFC_SLI4_MBX_EMBED);
- rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+ rc2 = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
shdr = (union lpfc_sli4_cfg_shdr *)
&pmb->u.mqe.un.sli4_config.header.cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
- if (rc || shdr_status || shdr_add_status) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ if (rc2 || shdr_status || shdr_add_status) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"3026 Mailbox failed , mbxCmd x%x "
"GET_FUNCTION_CONFIG, mbxStatus x%x\n",
bf_get(lpfc_mqe_command, &pmb->u.mqe),
bf_get(lpfc_mqe_status, &pmb->u.mqe));
- rc = -EIO;
goto read_cfg_out;
}
/* search for fc_fcoe resrouce descriptor */
get_func_cfg = &pmb->u.mqe.un.get_func_cfg;
- desc_count = get_func_cfg->func_cfg.rsrc_desc_count;
+
+ pdesc_0 = (char *)&get_func_cfg->func_cfg.desc[0];
+ desc = (struct lpfc_rsrc_desc_fcfcoe *)pdesc_0;
+ length = bf_get(lpfc_rsrc_desc_fcfcoe_length, desc);
+ if (length == LPFC_RSRC_DESC_TYPE_FCFCOE_V0_RSVD)
+ length = LPFC_RSRC_DESC_TYPE_FCFCOE_V0_LENGTH;
+ else if (length != LPFC_RSRC_DESC_TYPE_FCFCOE_V1_LENGTH)
+ goto read_cfg_out;
for (i = 0; i < LPFC_RSRC_DESC_MAX_NUM; i++) {
- desc = (struct lpfc_rsrc_desc_fcfcoe *)
- &get_func_cfg->func_cfg.desc[i];
+ desc = (struct lpfc_rsrc_desc_fcfcoe *)(pdesc_0 + length * i);
if (LPFC_RSRC_DESC_TYPE_FCFCOE ==
- bf_get(lpfc_rsrc_desc_pcie_type, desc)) {
+ bf_get(lpfc_rsrc_desc_fcfcoe_type, desc)) {
phba->sli4_hba.iov.pf_number =
bf_get(lpfc_rsrc_desc_fcfcoe_pfnum, desc);
phba->sli4_hba.iov.vf_number =
@@ -6028,13 +10227,11 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
"3027 GET_FUNCTION_CONFIG: pf_number:%d, "
"vf_number:%d\n", phba->sli4_hba.iov.pf_number,
phba->sli4_hba.iov.vf_number);
- else {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ else
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"3028 GET_FUNCTION_CONFIG: failed to find "
- "Resrouce Descriptor:x%x\n",
+ "Resource Descriptor:x%x\n",
LPFC_RSRC_DESC_TYPE_FCFCOE);
- rc = -EIO;
- }
read_cfg_out:
mempool_free(pmb, phba->mbox_mem_pool);
@@ -6068,7 +10265,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
GFP_KERNEL);
if (!mboxq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0492 Unable to allocate memory for "
"issuing SLI_CONFIG_SPECIAL mailbox "
"command\n");
@@ -6083,7 +10280,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
memcpy(&mboxq->u.mqe, &endian_mb_data, sizeof(endian_mb_data));
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0493 SLI_CONFIG_SPECIAL mailbox "
"failed with status x%x\n",
rc);
@@ -6091,6 +10288,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
}
mempool_free(mboxq, phba->mbox_mem_pool);
break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
case LPFC_SLI_INTF_IF_TYPE_2:
case LPFC_SLI_INTF_IF_TYPE_1:
default:
@@ -6100,6 +10298,105 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
}
/**
+ * lpfc_sli4_queue_verify - Verify and update EQ counts
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to check the user settable queue counts for EQs.
+ * After this routine is called the counts will be set to valid values that
+ * adhere to the constraints of the system's interrupt vectors and the port's
+ * queue resources.
+ *
+ * Return codes
+ * 0 - successful
+ * -ENOMEM - No available memory
+ **/
+static int
+lpfc_sli4_queue_verify(struct lpfc_hba *phba)
+{
+ /*
+ * Sanity check for configured queue parameters against the run-time
+ * device parameters
+ */
+
+ if (phba->nvmet_support) {
+ if (phba->cfg_hdw_queue < phba->cfg_nvmet_mrq)
+ phba->cfg_nvmet_mrq = phba->cfg_hdw_queue;
+ if (phba->cfg_nvmet_mrq > LPFC_NVMET_MRQ_MAX)
+ phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_MAX;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2574 IO channels: hdwQ %d IRQ %d MRQ: %d\n",
+ phba->cfg_hdw_queue, phba->cfg_irq_chann,
+ phba->cfg_nvmet_mrq);
+
+ /* Get EQ depth from module parameter, fake the default for now */
+ phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B;
+ phba->sli4_hba.eq_ecount = LPFC_EQE_DEF_COUNT;
+
+ /* Get CQ depth from module parameter, fake the default for now */
+ phba->sli4_hba.cq_esize = LPFC_CQE_SIZE;
+ phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT;
+ return 0;
+}
+
+static int
+lpfc_alloc_io_wq_cq(struct lpfc_hba *phba, int idx)
+{
+ struct lpfc_queue *qdesc;
+ u32 wqesize;
+ int cpu;
+
+ cpu = lpfc_find_cpu_handle(phba, idx, LPFC_FIND_BY_HDWQ);
+ /* Create Fast Path IO CQs */
+ if (phba->enab_exp_wqcq_pages)
+ /* Increase the CQ size when WQEs contain an embedded cdb */
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE,
+ phba->sli4_hba.cq_esize,
+ LPFC_CQE_EXP_COUNT, cpu);
+
+ else
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount, cpu);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0499 Failed allocate fast-path IO CQ (%d)\n",
+ idx);
+ return 1;
+ }
+ qdesc->qe_valid = 1;
+ qdesc->hdwq = idx;
+ qdesc->chann = cpu;
+ phba->sli4_hba.hdwq[idx].io_cq = qdesc;
+
+ /* Create Fast Path IO WQs */
+ if (phba->enab_exp_wqcq_pages) {
+ /* Increase the WQ size when WQEs contain an embedded cdb */
+ wqesize = (phba->fcp_embed_io) ?
+ LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize;
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE,
+ wqesize,
+ LPFC_WQE_EXP_COUNT, cpu);
+ } else
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.wq_esize,
+ phba->sli4_hba.wq_ecount, cpu);
+
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0503 Failed allocate fast-path IO WQ (%d)\n",
+ idx);
+ return 1;
+ }
+ qdesc->hdwq = idx;
+ qdesc->chann = cpu;
+ phba->sli4_hba.hdwq[idx].io_wq = qdesc;
+ list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
+ return 0;
+}
+
+/**
* lpfc_sli4_queue_create - Create all the SLI4 queues
* @phba: pointer to lpfc hba data structure.
*
@@ -6110,292 +10407,439 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
*
* Return codes
* 0 - successful
- * -ENOMEM - No available memory
+ * -ENOMEM - No availble memory
* -EIO - The mailbox failed to complete successfully.
**/
-static int
+int
lpfc_sli4_queue_create(struct lpfc_hba *phba)
{
struct lpfc_queue *qdesc;
- int fcp_eqidx, fcp_cqidx, fcp_wqidx;
- int cfg_fcp_wq_count;
- int cfg_fcp_eq_count;
+ int idx, cpu, eqcpu;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_vector_map_info *cpup;
+ struct lpfc_vector_map_info *eqcpup;
+ struct lpfc_eq_intr_info *eqi;
+ u32 wqesize;
/*
- * Sanity check for confiugred queue parameters against the run-time
- * device parameters
+ * Create HBA Record arrays.
+ * Both NVME and FCP will share that same vectors / EQs
*/
+ phba->sli4_hba.mq_esize = LPFC_MQE_SIZE;
+ phba->sli4_hba.mq_ecount = LPFC_MQE_DEF_COUNT;
+ phba->sli4_hba.wq_esize = LPFC_WQE_SIZE;
+ phba->sli4_hba.wq_ecount = LPFC_WQE_DEF_COUNT;
+ phba->sli4_hba.rq_esize = LPFC_RQE_SIZE;
+ phba->sli4_hba.rq_ecount = LPFC_RQE_DEF_COUNT;
+ phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B;
+ phba->sli4_hba.eq_ecount = LPFC_EQE_DEF_COUNT;
+ phba->sli4_hba.cq_esize = LPFC_CQE_SIZE;
+ phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT;
- /* Sanity check on FCP fast-path WQ parameters */
- cfg_fcp_wq_count = phba->cfg_fcp_wq_count;
- if (cfg_fcp_wq_count >
- (phba->sli4_hba.max_cfg_param.max_wq - LPFC_SP_WQN_DEF)) {
- cfg_fcp_wq_count = phba->sli4_hba.max_cfg_param.max_wq -
- LPFC_SP_WQN_DEF;
- if (cfg_fcp_wq_count < LPFC_FP_WQN_MIN) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2581 Not enough WQs (%d) from "
- "the pci function for supporting "
- "FCP WQs (%d)\n",
- phba->sli4_hba.max_cfg_param.max_wq,
- phba->cfg_fcp_wq_count);
+ if (!phba->sli4_hba.hdwq) {
+ phba->sli4_hba.hdwq = kcalloc(
+ phba->cfg_hdw_queue, sizeof(struct lpfc_sli4_hdw_queue),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.hdwq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6427 Failed allocate memory for "
+ "fast-path Hardware Queue array\n");
goto out_error;
}
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2582 Not enough WQs (%d) from the pci "
- "function for supporting the requested "
- "FCP WQs (%d), the actual FCP WQs can "
- "be supported: %d\n",
- phba->sli4_hba.max_cfg_param.max_wq,
- phba->cfg_fcp_wq_count, cfg_fcp_wq_count);
- }
- /* The actual number of FCP work queues adopted */
- phba->cfg_fcp_wq_count = cfg_fcp_wq_count;
-
- /* Sanity check on FCP fast-path EQ parameters */
- cfg_fcp_eq_count = phba->cfg_fcp_eq_count;
- if (cfg_fcp_eq_count >
- (phba->sli4_hba.max_cfg_param.max_eq - LPFC_SP_EQN_DEF)) {
- cfg_fcp_eq_count = phba->sli4_hba.max_cfg_param.max_eq -
- LPFC_SP_EQN_DEF;
- if (cfg_fcp_eq_count < LPFC_FP_EQN_MIN) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2574 Not enough EQs (%d) from the "
- "pci function for supporting FCP "
- "EQs (%d)\n",
- phba->sli4_hba.max_cfg_param.max_eq,
- phba->cfg_fcp_eq_count);
+ /* Prepare hardware queues to take IO buffers */
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ spin_lock_init(&qp->io_buf_list_get_lock);
+ spin_lock_init(&qp->io_buf_list_put_lock);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_get);
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put);
+ qp->get_io_bufs = 0;
+ qp->put_io_bufs = 0;
+ qp->total_io_bufs = 0;
+ spin_lock_init(&qp->abts_io_buf_list_lock);
+ INIT_LIST_HEAD(&qp->lpfc_abts_io_buf_list);
+ qp->abts_scsi_io_bufs = 0;
+ qp->abts_nvme_io_bufs = 0;
+ INIT_LIST_HEAD(&qp->sgl_list);
+ INIT_LIST_HEAD(&qp->cmd_rsp_buf_list);
+ spin_lock_init(&qp->hdwq_lock);
+ }
+ }
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ if (phba->nvmet_support) {
+ phba->sli4_hba.nvmet_cqset = kcalloc(
+ phba->cfg_nvmet_mrq,
+ sizeof(struct lpfc_queue *),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.nvmet_cqset) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3121 Fail allocate memory for "
+ "fast-path CQ set array\n");
+ goto out_error;
+ }
+ phba->sli4_hba.nvmet_mrq_hdr = kcalloc(
+ phba->cfg_nvmet_mrq,
+ sizeof(struct lpfc_queue *),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.nvmet_mrq_hdr) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3122 Fail allocate memory for "
+ "fast-path RQ set hdr array\n");
+ goto out_error;
+ }
+ phba->sli4_hba.nvmet_mrq_data = kcalloc(
+ phba->cfg_nvmet_mrq,
+ sizeof(struct lpfc_queue *),
+ GFP_KERNEL);
+ if (!phba->sli4_hba.nvmet_mrq_data) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3124 Fail allocate memory for "
+ "fast-path RQ set data array\n");
+ goto out_error;
+ }
+ }
+ }
+
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_wq_list);
+
+ /* Create HBA Event Queues (EQs) */
+ for_each_present_cpu(cpu) {
+ /* We only want to create 1 EQ per vector, even though
+ * multiple CPUs might be using that vector. so only
+ * selects the CPUs that are LPFC_CPU_FIRST_IRQ.
+ */
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+ if (!(cpup->flag & LPFC_CPU_FIRST_IRQ))
+ continue;
+
+ /* Get a ptr to the Hardware Queue associated with this CPU */
+ qp = &phba->sli4_hba.hdwq[cpup->hdwq];
+
+ /* Allocate an EQ */
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.eq_esize,
+ phba->sli4_hba.eq_ecount, cpu);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0497 Failed allocate EQ (%d)\n",
+ cpup->hdwq);
goto out_error;
}
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2575 Not enough EQs (%d) from the pci "
- "function for supporting the requested "
- "FCP EQs (%d), the actual FCP EQs can "
- "be supported: %d\n",
- phba->sli4_hba.max_cfg_param.max_eq,
- phba->cfg_fcp_eq_count, cfg_fcp_eq_count);
+ qdesc->qe_valid = 1;
+ qdesc->hdwq = cpup->hdwq;
+ qdesc->chann = cpu; /* First CPU this EQ is affinitized to */
+ qdesc->last_cpu = qdesc->chann;
+
+ /* Save the allocated EQ in the Hardware Queue */
+ qp->hba_eq = qdesc;
+
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, qdesc->last_cpu);
+ list_add(&qdesc->cpu_list, &eqi->list);
}
- /* It does not make sense to have more EQs than WQs */
- if (cfg_fcp_eq_count > phba->cfg_fcp_wq_count) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2593 The FCP EQ count(%d) cannot be greater "
- "than the FCP WQ count(%d), limiting the "
- "FCP EQ count to %d\n", cfg_fcp_eq_count,
- phba->cfg_fcp_wq_count,
- phba->cfg_fcp_wq_count);
- cfg_fcp_eq_count = phba->cfg_fcp_wq_count;
- }
- /* The actual number of FCP event queues adopted */
- phba->cfg_fcp_eq_count = cfg_fcp_eq_count;
- /* The overall number of event queues used */
- phba->sli4_hba.cfg_eqn = phba->cfg_fcp_eq_count + LPFC_SP_EQN_DEF;
- /*
- * Create Event Queues (EQs)
+ /* Now we need to populate the other Hardware Queues, that share
+ * an IRQ vector, with the associated EQ ptr.
*/
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
- /* Get EQ depth from module parameter, fake the default for now */
- phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B;
- phba->sli4_hba.eq_ecount = LPFC_EQE_DEF_COUNT;
+ /* Check for EQ already allocated in previous loop */
+ if (cpup->flag & LPFC_CPU_FIRST_IRQ)
+ continue;
- /* Create slow path event queue */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
- phba->sli4_hba.eq_ecount);
- if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0496 Failed allocate slow-path EQ\n");
- goto out_error;
+ /* Check for multiple CPUs per hdwq */
+ qp = &phba->sli4_hba.hdwq[cpup->hdwq];
+ if (qp->hba_eq)
+ continue;
+
+ /* We need to share an EQ for this hdwq */
+ eqcpu = lpfc_find_cpu_handle(phba, cpup->eq, LPFC_FIND_BY_EQ);
+ eqcpup = &phba->sli4_hba.cpu_map[eqcpu];
+ qp->hba_eq = phba->sli4_hba.hdwq[eqcpup->hdwq].hba_eq;
}
- phba->sli4_hba.sp_eq = qdesc;
- /* Create fast-path FCP Event Queue(s) */
- phba->sli4_hba.fp_eq = kzalloc((sizeof(struct lpfc_queue *) *
- phba->cfg_fcp_eq_count), GFP_KERNEL);
- if (!phba->sli4_hba.fp_eq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2576 Failed allocate memory for fast-path "
- "EQ record array\n");
- goto out_free_sp_eq;
+ /* Allocate IO Path SLI4 CQ/WQs */
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ if (lpfc_alloc_io_wq_cq(phba, idx))
+ goto out_error;
}
- for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
- phba->sli4_hba.eq_ecount);
- if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0497 Failed allocate fast-path EQ\n");
- goto out_free_fp_eq;
+
+ if (phba->nvmet_support) {
+ for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) {
+ cpu = lpfc_find_cpu_handle(phba, idx,
+ LPFC_FIND_BY_HDWQ);
+ qdesc = lpfc_sli4_queue_alloc(phba,
+ LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount,
+ cpu);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3142 Failed allocate NVME "
+ "CQ Set (%d)\n", idx);
+ goto out_error;
+ }
+ qdesc->qe_valid = 1;
+ qdesc->hdwq = idx;
+ qdesc->chann = cpu;
+ phba->sli4_hba.nvmet_cqset[idx] = qdesc;
}
- phba->sli4_hba.fp_eq[fcp_eqidx] = qdesc;
}
/*
- * Create Complete Queues (CQs)
+ * Create Slow Path Completion Queues (CQs)
*/
- /* Get CQ depth from module parameter, fake the default for now */
- phba->sli4_hba.cq_esize = LPFC_CQE_SIZE;
- phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT;
-
+ cpu = lpfc_find_cpu_handle(phba, 0, LPFC_FIND_BY_EQ);
/* Create slow-path Mailbox Command Complete Queue */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
- phba->sli4_hba.cq_ecount);
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount, cpu);
if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0500 Failed allocate slow-path mailbox CQ\n");
- goto out_free_fp_eq;
+ goto out_error;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.mbx_cq = qdesc;
/* Create slow-path ELS Complete Queue */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
- phba->sli4_hba.cq_ecount);
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount, cpu);
if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0501 Failed allocate slow-path ELS CQ\n");
- goto out_free_mbx_cq;
+ goto out_error;
}
+ qdesc->qe_valid = 1;
+ qdesc->chann = cpu;
phba->sli4_hba.els_cq = qdesc;
- /* Create fast-path FCP Completion Queue(s), one-to-one with EQs */
- phba->sli4_hba.fcp_cq = kzalloc((sizeof(struct lpfc_queue *) *
- phba->cfg_fcp_eq_count), GFP_KERNEL);
- if (!phba->sli4_hba.fcp_cq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2577 Failed allocate memory for fast-path "
- "CQ record array\n");
- goto out_free_els_cq;
- }
- for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
- phba->sli4_hba.cq_ecount);
- if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0499 Failed allocate fast-path FCP "
- "CQ (%d)\n", fcp_cqidx);
- goto out_free_fcp_cq;
- }
- phba->sli4_hba.fcp_cq[fcp_cqidx] = qdesc;
- }
+ /*
+ * Create Slow Path Work Queues (WQs)
+ */
/* Create Mailbox Command Queue */
- phba->sli4_hba.mq_esize = LPFC_MQE_SIZE;
- phba->sli4_hba.mq_ecount = LPFC_MQE_DEF_COUNT;
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.mq_esize,
- phba->sli4_hba.mq_ecount);
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.mq_esize,
+ phba->sli4_hba.mq_ecount, cpu);
if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0505 Failed allocate slow-path MQ\n");
- goto out_free_fcp_cq;
+ goto out_error;
}
+ qdesc->chann = cpu;
phba->sli4_hba.mbx_wq = qdesc;
/*
- * Create all the Work Queues (WQs)
+ * Create ELS Work Queues
*/
- phba->sli4_hba.wq_esize = LPFC_WQE_SIZE;
- phba->sli4_hba.wq_ecount = LPFC_WQE_DEF_COUNT;
- /* Create slow-path ELS Work Queue */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
- phba->sli4_hba.wq_ecount);
+ /*
+ * Create slow-path ELS Work Queue.
+ * Increase the ELS WQ size when WQEs contain an embedded cdb
+ */
+ wqesize = (phba->fcp_embed_io) ?
+ LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize;
+
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ wqesize,
+ phba->sli4_hba.wq_ecount, cpu);
if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0504 Failed allocate slow-path ELS WQ\n");
- goto out_free_mbx_wq;
+ goto out_error;
}
+ qdesc->chann = cpu;
phba->sli4_hba.els_wq = qdesc;
+ list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
- /* Create fast-path FCP Work Queue(s) */
- phba->sli4_hba.fcp_wq = kzalloc((sizeof(struct lpfc_queue *) *
- phba->cfg_fcp_wq_count), GFP_KERNEL);
- if (!phba->sli4_hba.fcp_wq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2578 Failed allocate memory for fast-path "
- "WQ record array\n");
- goto out_free_els_wq;
- }
- for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_wq_count; fcp_wqidx++) {
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
- phba->sli4_hba.wq_ecount);
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ /* Create NVME LS Complete Queue */
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.cq_esize,
+ phba->sli4_hba.cq_ecount, cpu);
if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0503 Failed allocate fast-path FCP "
- "WQ (%d)\n", fcp_wqidx);
- goto out_free_fcp_wq;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6079 Failed allocate NVME LS CQ\n");
+ goto out_error;
}
- phba->sli4_hba.fcp_wq[fcp_wqidx] = qdesc;
+ qdesc->chann = cpu;
+ qdesc->qe_valid = 1;
+ phba->sli4_hba.nvmels_cq = qdesc;
+
+ /* Create NVME LS Work Queue */
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.wq_esize,
+ phba->sli4_hba.wq_ecount, cpu);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6080 Failed allocate NVME LS WQ\n");
+ goto out_error;
+ }
+ qdesc->chann = cpu;
+ phba->sli4_hba.nvmels_wq = qdesc;
+ list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list);
}
/*
* Create Receive Queue (RQ)
*/
- phba->sli4_hba.rq_esize = LPFC_RQE_SIZE;
- phba->sli4_hba.rq_ecount = LPFC_RQE_DEF_COUNT;
/* Create Receive Queue for header */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.rq_esize,
- phba->sli4_hba.rq_ecount);
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.rq_esize,
+ phba->sli4_hba.rq_ecount, cpu);
if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0506 Failed allocate receive HRQ\n");
- goto out_free_fcp_wq;
+ goto out_error;
}
phba->sli4_hba.hdr_rq = qdesc;
/* Create Receive Queue for data */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.rq_esize,
- phba->sli4_hba.rq_ecount);
+ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.rq_esize,
+ phba->sli4_hba.rq_ecount, cpu);
if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0507 Failed allocate receive DRQ\n");
- goto out_free_hdr_rq;
+ goto out_error;
}
phba->sli4_hba.dat_rq = qdesc;
+ if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) &&
+ phba->nvmet_support) {
+ for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) {
+ cpu = lpfc_find_cpu_handle(phba, idx,
+ LPFC_FIND_BY_HDWQ);
+ /* Create NVMET Receive Queue for header */
+ qdesc = lpfc_sli4_queue_alloc(phba,
+ LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.rq_esize,
+ LPFC_NVMET_RQE_DEF_COUNT,
+ cpu);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3146 Failed allocate "
+ "receive HRQ\n");
+ goto out_error;
+ }
+ qdesc->hdwq = idx;
+ phba->sli4_hba.nvmet_mrq_hdr[idx] = qdesc;
+
+ /* Only needed for header of RQ pair */
+ qdesc->rqbp = kzalloc_node(sizeof(*qdesc->rqbp),
+ GFP_KERNEL,
+ cpu_to_node(cpu));
+ if (qdesc->rqbp == NULL) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6131 Failed allocate "
+ "Header RQBP\n");
+ goto out_error;
+ }
+
+ /* Put list in known state in case driver load fails. */
+ INIT_LIST_HEAD(&qdesc->rqbp->rqb_buffer_list);
+
+ /* Create NVMET Receive Queue for data */
+ qdesc = lpfc_sli4_queue_alloc(phba,
+ LPFC_DEFAULT_PAGE_SIZE,
+ phba->sli4_hba.rq_esize,
+ LPFC_NVMET_RQE_DEF_COUNT,
+ cpu);
+ if (!qdesc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3156 Failed allocate "
+ "receive DRQ\n");
+ goto out_error;
+ }
+ qdesc->hdwq = idx;
+ phba->sli4_hba.nvmet_mrq_data[idx] = qdesc;
+ }
+ }
+
+ /* Clear NVME stats */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ memset(&phba->sli4_hba.hdwq[idx].nvme_cstat, 0,
+ sizeof(phba->sli4_hba.hdwq[idx].nvme_cstat));
+ }
+ }
+
+ /* Clear SCSI stats */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) {
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ memset(&phba->sli4_hba.hdwq[idx].scsi_cstat, 0,
+ sizeof(phba->sli4_hba.hdwq[idx].scsi_cstat));
+ }
+ }
+
return 0;
-out_free_hdr_rq:
- lpfc_sli4_queue_free(phba->sli4_hba.hdr_rq);
- phba->sli4_hba.hdr_rq = NULL;
-out_free_fcp_wq:
- for (--fcp_wqidx; fcp_wqidx >= 0; fcp_wqidx--) {
- lpfc_sli4_queue_free(phba->sli4_hba.fcp_wq[fcp_wqidx]);
- phba->sli4_hba.fcp_wq[fcp_wqidx] = NULL;
- }
- kfree(phba->sli4_hba.fcp_wq);
-out_free_els_wq:
- lpfc_sli4_queue_free(phba->sli4_hba.els_wq);
- phba->sli4_hba.els_wq = NULL;
-out_free_mbx_wq:
- lpfc_sli4_queue_free(phba->sli4_hba.mbx_wq);
- phba->sli4_hba.mbx_wq = NULL;
-out_free_fcp_cq:
- for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--) {
- lpfc_sli4_queue_free(phba->sli4_hba.fcp_cq[fcp_cqidx]);
- phba->sli4_hba.fcp_cq[fcp_cqidx] = NULL;
- }
- kfree(phba->sli4_hba.fcp_cq);
-out_free_els_cq:
- lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
- phba->sli4_hba.els_cq = NULL;
-out_free_mbx_cq:
- lpfc_sli4_queue_free(phba->sli4_hba.mbx_cq);
- phba->sli4_hba.mbx_cq = NULL;
-out_free_fp_eq:
- for (--fcp_eqidx; fcp_eqidx >= 0; fcp_eqidx--) {
- lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_eqidx]);
- phba->sli4_hba.fp_eq[fcp_eqidx] = NULL;
- }
- kfree(phba->sli4_hba.fp_eq);
-out_free_sp_eq:
- lpfc_sli4_queue_free(phba->sli4_hba.sp_eq);
- phba->sli4_hba.sp_eq = NULL;
out_error:
+ lpfc_sli4_queue_destroy(phba);
return -ENOMEM;
}
+static inline void
+__lpfc_sli4_release_queue(struct lpfc_queue **qp)
+{
+ if (*qp != NULL) {
+ lpfc_sli4_queue_free(*qp);
+ *qp = NULL;
+ }
+}
+
+static inline void
+lpfc_sli4_release_queues(struct lpfc_queue ***qs, int max)
+{
+ int idx;
+
+ if (*qs == NULL)
+ return;
+
+ for (idx = 0; idx < max; idx++)
+ __lpfc_sli4_release_queue(&(*qs)[idx]);
+
+ kfree(*qs);
+ *qs = NULL;
+}
+
+static inline void
+lpfc_sli4_release_hdwq(struct lpfc_hba *phba)
+{
+ struct lpfc_sli4_hdw_queue *hdwq;
+ struct lpfc_queue *eq;
+ uint32_t idx;
+
+ hdwq = phba->sli4_hba.hdwq;
+
+ /* Loop thru all Hardware Queues */
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ /* Free the CQ/WQ corresponding to the Hardware Queue */
+ lpfc_sli4_queue_free(hdwq[idx].io_cq);
+ lpfc_sli4_queue_free(hdwq[idx].io_wq);
+ hdwq[idx].hba_eq = NULL;
+ hdwq[idx].io_cq = NULL;
+ hdwq[idx].io_wq = NULL;
+ if (phba->cfg_xpsgl && !phba->nvmet_support)
+ lpfc_free_sgl_per_hdwq(phba, &hdwq[idx]);
+ lpfc_free_cmd_rsp_buf_per_hdwq(phba, &hdwq[idx]);
+ }
+ /* Loop thru all IRQ vectors */
+ for (idx = 0; idx < phba->cfg_irq_chann; idx++) {
+ /* Free the EQ corresponding to the IRQ vector */
+ eq = phba->sli4_hba.hba_eq_hdl[idx].eq;
+ lpfc_sli4_queue_free(eq);
+ phba->sli4_hba.hba_eq_hdl[idx].eq = NULL;
+ }
+}
+
/**
* lpfc_sli4_queue_destroy - Destroy all the SLI4 queues
* @phba: pointer to lpfc hba data structure.
@@ -6408,58 +10852,190 @@ out_error:
* -ENOMEM - No available memory
* -EIO - The mailbox failed to complete successfully.
**/
-static void
+void
lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
{
- int fcp_qidx;
+ /*
+ * Set FREE_INIT before beginning to free the queues.
+ * Wait until the users of queues to acknowledge to
+ * release queues by clearing FREE_WAIT.
+ */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag |= LPFC_QUEUE_FREE_INIT;
+ while (phba->sli.sli_flag & LPFC_QUEUE_FREE_WAIT) {
+ spin_unlock_irq(&phba->hbalock);
+ msleep(20);
+ spin_lock_irq(&phba->hbalock);
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ lpfc_sli4_cleanup_poll_list(phba);
+
+ /* Release HBA eqs */
+ if (phba->sli4_hba.hdwq)
+ lpfc_sli4_release_hdwq(phba);
+
+ if (phba->nvmet_support) {
+ lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_cqset,
+ phba->cfg_nvmet_mrq);
+
+ lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_mrq_hdr,
+ phba->cfg_nvmet_mrq);
+ lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_mrq_data,
+ phba->cfg_nvmet_mrq);
+ }
/* Release mailbox command work queue */
- lpfc_sli4_queue_free(phba->sli4_hba.mbx_wq);
- phba->sli4_hba.mbx_wq = NULL;
+ __lpfc_sli4_release_queue(&phba->sli4_hba.mbx_wq);
/* Release ELS work queue */
- lpfc_sli4_queue_free(phba->sli4_hba.els_wq);
- phba->sli4_hba.els_wq = NULL;
+ __lpfc_sli4_release_queue(&phba->sli4_hba.els_wq);
- /* Release FCP work queue */
- for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++)
- lpfc_sli4_queue_free(phba->sli4_hba.fcp_wq[fcp_qidx]);
- kfree(phba->sli4_hba.fcp_wq);
- phba->sli4_hba.fcp_wq = NULL;
+ /* Release ELS work queue */
+ __lpfc_sli4_release_queue(&phba->sli4_hba.nvmels_wq);
/* Release unsolicited receive queue */
- lpfc_sli4_queue_free(phba->sli4_hba.hdr_rq);
- phba->sli4_hba.hdr_rq = NULL;
- lpfc_sli4_queue_free(phba->sli4_hba.dat_rq);
- phba->sli4_hba.dat_rq = NULL;
+ __lpfc_sli4_release_queue(&phba->sli4_hba.hdr_rq);
+ __lpfc_sli4_release_queue(&phba->sli4_hba.dat_rq);
/* Release ELS complete queue */
- lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
- phba->sli4_hba.els_cq = NULL;
+ __lpfc_sli4_release_queue(&phba->sli4_hba.els_cq);
+
+ /* Release NVME LS complete queue */
+ __lpfc_sli4_release_queue(&phba->sli4_hba.nvmels_cq);
/* Release mailbox command complete queue */
- lpfc_sli4_queue_free(phba->sli4_hba.mbx_cq);
- phba->sli4_hba.mbx_cq = NULL;
-
- /* Release FCP response complete queue */
- fcp_qidx = 0;
- do
- lpfc_sli4_queue_free(phba->sli4_hba.fcp_cq[fcp_qidx]);
- while (++fcp_qidx < phba->cfg_fcp_eq_count);
- kfree(phba->sli4_hba.fcp_cq);
- phba->sli4_hba.fcp_cq = NULL;
-
- /* Release fast-path event queue */
- for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
- lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_qidx]);
- kfree(phba->sli4_hba.fp_eq);
- phba->sli4_hba.fp_eq = NULL;
-
- /* Release slow-path event queue */
- lpfc_sli4_queue_free(phba->sli4_hba.sp_eq);
- phba->sli4_hba.sp_eq = NULL;
+ __lpfc_sli4_release_queue(&phba->sli4_hba.mbx_cq);
- return;
+ /* Everything on this list has been freed */
+ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_wq_list);
+
+ /* Done with freeing the queues */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag &= ~LPFC_QUEUE_FREE_INIT;
+ spin_unlock_irq(&phba->hbalock);
+}
+
+int
+lpfc_free_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *rq)
+{
+ struct lpfc_rqb *rqbp;
+ struct lpfc_dmabuf *h_buf;
+ struct rqb_dmabuf *rqb_buffer;
+
+ rqbp = rq->rqbp;
+ while (!list_empty(&rqbp->rqb_buffer_list)) {
+ list_remove_head(&rqbp->rqb_buffer_list, h_buf,
+ struct lpfc_dmabuf, list);
+
+ rqb_buffer = container_of(h_buf, struct rqb_dmabuf, hbuf);
+ (rqbp->rqb_free_buffer)(phba, rqb_buffer);
+ rqbp->buffer_count--;
+ }
+ return 1;
+}
+
+static int
+lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ struct lpfc_queue *cq, struct lpfc_queue *wq, uint16_t *cq_map,
+ int qidx, uint32_t qtype)
+{
+ struct lpfc_sli_ring *pring;
+ int rc;
+
+ if (!eq || !cq || !wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6085 Fast-path %s (%d) not allocated\n",
+ ((eq) ? ((cq) ? "WQ" : "CQ") : "EQ"), qidx);
+ return -ENOMEM;
+ }
+
+ /* create the Cq first */
+ rc = lpfc_cq_create(phba, cq, eq,
+ (qtype == LPFC_MBOX) ? LPFC_MCQ : LPFC_WCQ, qtype);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6086 Failed setup of CQ (%d), rc = 0x%x\n",
+ qidx, (uint32_t)rc);
+ return rc;
+ }
+
+ if (qtype != LPFC_MBOX) {
+ /* Setup cq_map for fast lookup */
+ if (cq_map)
+ *cq_map = cq->queue_id;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "6087 CQ setup: cq[%d]-id=%d, parent eq[%d]-id=%d\n",
+ qidx, cq->queue_id, qidx, eq->queue_id);
+
+ /* create the wq */
+ rc = lpfc_wq_create(phba, wq, cq, qtype);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "4618 Fail setup fastpath WQ (%d), rc = 0x%x\n",
+ qidx, (uint32_t)rc);
+ /* no need to tear down cq - caller will do so */
+ return rc;
+ }
+
+ /* Bind this CQ/WQ to the NVME ring */
+ pring = wq->pring;
+ pring->sli.sli4.wqp = (void *)wq;
+ cq->pring = pring;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2593 WQ setup: wq[%d]-id=%d assoc=%d, cq[%d]-id=%d\n",
+ qidx, wq->queue_id, wq->assoc_qid, qidx, cq->queue_id);
+ } else {
+ rc = lpfc_mq_create(phba, wq, cq, LPFC_MBOX);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0539 Failed setup of slow-path MQ: "
+ "rc = 0x%x\n", rc);
+ /* no need to tear down cq - caller will do so */
+ return rc;
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2589 MBX MQ setup: wq-id=%d, parent cq-id=%d\n",
+ phba->sli4_hba.mbx_wq->queue_id,
+ phba->sli4_hba.mbx_cq->queue_id);
+ }
+
+ return 0;
+}
+
+/**
+ * lpfc_setup_cq_lookup - Setup the CQ lookup table
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine will populate the cq_lookup table by all
+ * available CQ queue_id's.
+ **/
+static void
+lpfc_setup_cq_lookup(struct lpfc_hba *phba)
+{
+ struct lpfc_queue *eq, *childq;
+ int qidx;
+
+ memset(phba->sli4_hba.cq_lookup, 0,
+ (sizeof(struct lpfc_queue *) * (phba->sli4_hba.cq_max + 1)));
+ /* Loop thru all IRQ vectors */
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
+ /* Get the EQ corresponding to the IRQ vector */
+ eq = phba->sli4_hba.hba_eq_hdl[qidx].eq;
+ if (!eq)
+ continue;
+ /* Loop through all CQs associated with that EQ */
+ list_for_each_entry(childq, &eq->child_list, list) {
+ if (childq->queue_id > phba->sli4_hba.cq_max)
+ continue;
+ if (childq->subtype == LPFC_IO)
+ phba->sli4_hba.cq_lookup[childq->queue_id] =
+ childq;
+ }
+ }
}
/**
@@ -6477,245 +11053,344 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
int
lpfc_sli4_queue_setup(struct lpfc_hba *phba)
{
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ struct lpfc_vector_map_info *cpup;
+ struct lpfc_sli4_hdw_queue *qp;
+ LPFC_MBOXQ_t *mboxq;
+ int qidx, cpu;
+ uint32_t length, usdelay;
int rc = -ENOMEM;
- int fcp_eqidx, fcp_cqidx, fcp_wqidx;
- int fcp_cq_index = 0;
- /*
- * Set up Event Queues (EQs)
- */
-
- /* Set up slow-path event queue */
- if (!phba->sli4_hba.sp_eq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0520 Slow-path EQ not allocated\n");
- goto out_error;
+ /* Check for dual-ULP support */
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3249 Unable to allocate memory for "
+ "QUERY_FW_CFG mailbox command\n");
+ return -ENOMEM;
}
- rc = lpfc_eq_create(phba, phba->sli4_hba.sp_eq,
- LPFC_SP_DEF_IMAX);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0521 Failed setup of slow-path EQ: "
- "rc = 0x%x\n", rc);
+ length = (sizeof(struct lpfc_mbx_query_fw_config) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_QUERY_FW_CFG,
+ length, LPFC_SLI4_MBX_EMBED);
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3250 QUERY_FW_CFG mailbox failed with status "
+ "x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ rc = -ENXIO;
goto out_error;
}
+
+ phba->sli4_hba.fw_func_mode =
+ mboxq->u.mqe.un.query_fw_cfg.rsp.function_mode;
+ phba->sli4_hba.physical_port =
+ mboxq->u.mqe.un.query_fw_cfg.rsp.physical_port;
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2583 Slow-path EQ setup: queue-id=%d\n",
- phba->sli4_hba.sp_eq->queue_id);
+ "3251 QUERY_FW_CFG: func_mode:x%x\n",
+ phba->sli4_hba.fw_func_mode);
- /* Set up fast-path event queue */
- for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
- if (!phba->sli4_hba.fp_eq[fcp_eqidx]) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0522 Fast-path EQ (%d) not "
- "allocated\n", fcp_eqidx);
- goto out_destroy_fp_eq;
- }
- rc = lpfc_eq_create(phba, phba->sli4_hba.fp_eq[fcp_eqidx],
- phba->cfg_fcp_imax);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0523 Failed setup of fast-path EQ "
- "(%d), rc = 0x%x\n", fcp_eqidx, rc);
- goto out_destroy_fp_eq;
- }
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2584 Fast-path EQ setup: "
- "queue[%d]-id=%d\n", fcp_eqidx,
- phba->sli4_hba.fp_eq[fcp_eqidx]->queue_id);
- }
+ mempool_free(mboxq, phba->mbox_mem_pool);
/*
- * Set up Complete Queues (CQs)
+ * Set up HBA Event Queues (EQs)
*/
+ qp = phba->sli4_hba.hdwq;
- /* Set up slow-path MBOX Complete Queue as the first CQ */
- if (!phba->sli4_hba.mbx_cq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0528 Mailbox CQ not allocated\n");
- goto out_destroy_fp_eq;
- }
- rc = lpfc_cq_create(phba, phba->sli4_hba.mbx_cq, phba->sli4_hba.sp_eq,
- LPFC_MCQ, LPFC_MBOX);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0529 Failed setup of slow-path mailbox CQ: "
- "rc = 0x%x\n", rc);
- goto out_destroy_fp_eq;
+ /* Set up HBA event queue */
+ if (!qp) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3147 Fast-path EQs not allocated\n");
+ rc = -ENOMEM;
+ goto out_error;
}
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2585 MBX CQ setup: cq-id=%d, parent eq-id=%d\n",
- phba->sli4_hba.mbx_cq->queue_id,
- phba->sli4_hba.sp_eq->queue_id);
- /* Set up slow-path ELS Complete Queue */
- if (!phba->sli4_hba.els_cq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0530 ELS CQ not allocated\n");
- goto out_destroy_mbx_cq;
- }
- rc = lpfc_cq_create(phba, phba->sli4_hba.els_cq, phba->sli4_hba.sp_eq,
- LPFC_WCQ, LPFC_ELS);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0531 Failed setup of slow-path ELS CQ: "
- "rc = 0x%x\n", rc);
- goto out_destroy_mbx_cq;
+ /* Loop thru all IRQ vectors */
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
+ /* Create HBA Event Queues (EQs) in order */
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* Look for the CPU thats using that vector with
+ * LPFC_CPU_FIRST_IRQ set.
+ */
+ if (!(cpup->flag & LPFC_CPU_FIRST_IRQ))
+ continue;
+ if (qidx != cpup->eq)
+ continue;
+
+ /* Create an EQ for that vector */
+ rc = lpfc_eq_create(phba, qp[cpup->hdwq].hba_eq,
+ phba->cfg_fcp_imax);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0523 Failed setup of fast-path"
+ " EQ (%d), rc = 0x%x\n",
+ cpup->eq, (uint32_t)rc);
+ goto out_destroy;
+ }
+
+ /* Save the EQ for that vector in the hba_eq_hdl */
+ phba->sli4_hba.hba_eq_hdl[cpup->eq].eq =
+ qp[cpup->hdwq].hba_eq;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2584 HBA EQ setup: queue[%d]-id=%d\n",
+ cpup->eq,
+ qp[cpup->hdwq].hba_eq->queue_id);
+ }
}
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2586 ELS CQ setup: cq-id=%d, parent eq-id=%d\n",
- phba->sli4_hba.els_cq->queue_id,
- phba->sli4_hba.sp_eq->queue_id);
- /* Set up fast-path FCP Response Complete Queue */
- fcp_cqidx = 0;
- do {
- if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0526 Fast-path FCP CQ (%d) not "
- "allocated\n", fcp_cqidx);
- goto out_destroy_fcp_cq;
- }
- if (phba->cfg_fcp_eq_count)
- rc = lpfc_cq_create(phba,
- phba->sli4_hba.fcp_cq[fcp_cqidx],
- phba->sli4_hba.fp_eq[fcp_cqidx],
- LPFC_WCQ, LPFC_FCP);
- else
- rc = lpfc_cq_create(phba,
- phba->sli4_hba.fcp_cq[fcp_cqidx],
- phba->sli4_hba.sp_eq,
- LPFC_WCQ, LPFC_FCP);
+ /* Loop thru all Hardware Queues */
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ cpu = lpfc_find_cpu_handle(phba, qidx, LPFC_FIND_BY_HDWQ);
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* Create the CQ/WQ corresponding to the Hardware Queue */
+ rc = lpfc_create_wq_cq(phba,
+ phba->sli4_hba.hdwq[cpup->hdwq].hba_eq,
+ qp[qidx].io_cq,
+ qp[qidx].io_wq,
+ &phba->sli4_hba.hdwq[qidx].io_cq_map,
+ qidx,
+ LPFC_IO);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0527 Failed setup of fast-path FCP "
- "CQ (%d), rc = 0x%x\n", fcp_cqidx, rc);
- goto out_destroy_fcp_cq;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0535 Failed to setup fastpath "
+ "IO WQ/CQ (%d), rc = 0x%x\n",
+ qidx, (uint32_t)rc);
+ goto out_destroy;
}
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2588 FCP CQ setup: cq[%d]-id=%d, "
- "parent %seq[%d]-id=%d\n",
- fcp_cqidx,
- phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id,
- (phba->cfg_fcp_eq_count) ? "" : "sp_",
- fcp_cqidx,
- (phba->cfg_fcp_eq_count) ?
- phba->sli4_hba.fp_eq[fcp_cqidx]->queue_id :
- phba->sli4_hba.sp_eq->queue_id);
- } while (++fcp_cqidx < phba->cfg_fcp_eq_count);
+ }
/*
- * Set up all the Work Queues (WQs)
+ * Set up Slow Path Complete Queues (CQs)
*/
- /* Set up Mailbox Command Queue */
- if (!phba->sli4_hba.mbx_wq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0538 Slow-path MQ not allocated\n");
- goto out_destroy_fcp_cq;
+ /* Set up slow-path MBOX CQ/MQ */
+
+ if (!phba->sli4_hba.mbx_cq || !phba->sli4_hba.mbx_wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0528 %s not allocated\n",
+ phba->sli4_hba.mbx_cq ?
+ "Mailbox WQ" : "Mailbox CQ");
+ rc = -ENOMEM;
+ goto out_destroy;
}
- rc = lpfc_mq_create(phba, phba->sli4_hba.mbx_wq,
- phba->sli4_hba.mbx_cq, LPFC_MBOX);
+
+ rc = lpfc_create_wq_cq(phba, qp[0].hba_eq,
+ phba->sli4_hba.mbx_cq,
+ phba->sli4_hba.mbx_wq,
+ NULL, 0, LPFC_MBOX);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0539 Failed setup of slow-path MQ: "
- "rc = 0x%x\n", rc);
- goto out_destroy_fcp_cq;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0529 Failed setup of mailbox WQ/CQ: rc = 0x%x\n",
+ (uint32_t)rc);
+ goto out_destroy;
+ }
+ if (phba->nvmet_support) {
+ if (!phba->sli4_hba.nvmet_cqset) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3165 Fast-path NVME CQ Set "
+ "array not allocated\n");
+ rc = -ENOMEM;
+ goto out_destroy;
+ }
+ if (phba->cfg_nvmet_mrq > 1) {
+ rc = lpfc_cq_create_set(phba,
+ phba->sli4_hba.nvmet_cqset,
+ qp,
+ LPFC_WCQ, LPFC_NVMET);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3164 Failed setup of NVME CQ "
+ "Set, rc = 0x%x\n",
+ (uint32_t)rc);
+ goto out_destroy;
+ }
+ } else {
+ /* Set up NVMET Receive Complete Queue */
+ rc = lpfc_cq_create(phba, phba->sli4_hba.nvmet_cqset[0],
+ qp[0].hba_eq,
+ LPFC_WCQ, LPFC_NVMET);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6089 Failed setup NVMET CQ: "
+ "rc = 0x%x\n", (uint32_t)rc);
+ goto out_destroy;
+ }
+ phba->sli4_hba.nvmet_cqset[0]->chann = 0;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "6090 NVMET CQ setup: cq-id=%d, "
+ "parent eq-id=%d\n",
+ phba->sli4_hba.nvmet_cqset[0]->queue_id,
+ qp[0].hba_eq->queue_id);
+ }
}
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2589 MBX MQ setup: wq-id=%d, parent cq-id=%d\n",
- phba->sli4_hba.mbx_wq->queue_id,
- phba->sli4_hba.mbx_cq->queue_id);
- /* Set up slow-path ELS Work Queue */
- if (!phba->sli4_hba.els_wq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0536 Slow-path ELS WQ not allocated\n");
- goto out_destroy_mbx_wq;
+ /* Set up slow-path ELS WQ/CQ */
+ if (!phba->sli4_hba.els_cq || !phba->sli4_hba.els_wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0530 ELS %s not allocated\n",
+ phba->sli4_hba.els_cq ? "WQ" : "CQ");
+ rc = -ENOMEM;
+ goto out_destroy;
}
- rc = lpfc_wq_create(phba, phba->sli4_hba.els_wq,
- phba->sli4_hba.els_cq, LPFC_ELS);
+ rc = lpfc_create_wq_cq(phba, qp[0].hba_eq,
+ phba->sli4_hba.els_cq,
+ phba->sli4_hba.els_wq,
+ NULL, 0, LPFC_ELS);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0537 Failed setup of slow-path ELS WQ: "
- "rc = 0x%x\n", rc);
- goto out_destroy_mbx_wq;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0525 Failed setup of ELS WQ/CQ: rc = 0x%x\n",
+ (uint32_t)rc);
+ goto out_destroy;
}
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"2590 ELS WQ setup: wq-id=%d, parent cq-id=%d\n",
phba->sli4_hba.els_wq->queue_id,
phba->sli4_hba.els_cq->queue_id);
- /* Set up fast-path FCP Work Queue */
- for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_wq_count; fcp_wqidx++) {
- if (!phba->sli4_hba.fcp_wq[fcp_wqidx]) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0534 Fast-path FCP WQ (%d) not "
- "allocated\n", fcp_wqidx);
- goto out_destroy_fcp_wq;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ /* Set up NVME LS Complete Queue */
+ if (!phba->sli4_hba.nvmels_cq || !phba->sli4_hba.nvmels_wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6091 LS %s not allocated\n",
+ phba->sli4_hba.nvmels_cq ? "WQ" : "CQ");
+ rc = -ENOMEM;
+ goto out_destroy;
}
- rc = lpfc_wq_create(phba, phba->sli4_hba.fcp_wq[fcp_wqidx],
- phba->sli4_hba.fcp_cq[fcp_cq_index],
- LPFC_FCP);
+ rc = lpfc_create_wq_cq(phba, qp[0].hba_eq,
+ phba->sli4_hba.nvmels_cq,
+ phba->sli4_hba.nvmels_wq,
+ NULL, 0, LPFC_NVME_LS);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0535 Failed setup of fast-path FCP "
- "WQ (%d), rc = 0x%x\n", fcp_wqidx, rc);
- goto out_destroy_fcp_wq;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0526 Failed setup of NVVME LS WQ/CQ: "
+ "rc = 0x%x\n", (uint32_t)rc);
+ goto out_destroy;
}
+
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2591 FCP WQ setup: wq[%d]-id=%d, "
- "parent cq[%d]-id=%d\n",
- fcp_wqidx,
- phba->sli4_hba.fcp_wq[fcp_wqidx]->queue_id,
- fcp_cq_index,
- phba->sli4_hba.fcp_cq[fcp_cq_index]->queue_id);
- /* Round robin FCP Work Queue's Completion Queue assignment */
- if (phba->cfg_fcp_eq_count)
- fcp_cq_index = ((fcp_cq_index + 1) %
- phba->cfg_fcp_eq_count);
+ "6096 ELS WQ setup: wq-id=%d, "
+ "parent cq-id=%d\n",
+ phba->sli4_hba.nvmels_wq->queue_id,
+ phba->sli4_hba.nvmels_cq->queue_id);
}
/*
- * Create Receive Queue (RQ)
+ * Create NVMET Receive Queue (RQ)
*/
+ if (phba->nvmet_support) {
+ if ((!phba->sli4_hba.nvmet_cqset) ||
+ (!phba->sli4_hba.nvmet_mrq_hdr) ||
+ (!phba->sli4_hba.nvmet_mrq_data)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6130 MRQ CQ Queues not "
+ "allocated\n");
+ rc = -ENOMEM;
+ goto out_destroy;
+ }
+ if (phba->cfg_nvmet_mrq > 1) {
+ rc = lpfc_mrq_create(phba,
+ phba->sli4_hba.nvmet_mrq_hdr,
+ phba->sli4_hba.nvmet_mrq_data,
+ phba->sli4_hba.nvmet_cqset,
+ LPFC_NVMET);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6098 Failed setup of NVMET "
+ "MRQ: rc = 0x%x\n",
+ (uint32_t)rc);
+ goto out_destroy;
+ }
+
+ } else {
+ rc = lpfc_rq_create(phba,
+ phba->sli4_hba.nvmet_mrq_hdr[0],
+ phba->sli4_hba.nvmet_mrq_data[0],
+ phba->sli4_hba.nvmet_cqset[0],
+ LPFC_NVMET);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6057 Failed setup of NVMET "
+ "Receive Queue: rc = 0x%x\n",
+ (uint32_t)rc);
+ goto out_destroy;
+ }
+
+ lpfc_printf_log(
+ phba, KERN_INFO, LOG_INIT,
+ "6099 NVMET RQ setup: hdr-rq-id=%d, "
+ "dat-rq-id=%d parent cq-id=%d\n",
+ phba->sli4_hba.nvmet_mrq_hdr[0]->queue_id,
+ phba->sli4_hba.nvmet_mrq_data[0]->queue_id,
+ phba->sli4_hba.nvmet_cqset[0]->queue_id);
+
+ }
+ }
+
if (!phba->sli4_hba.hdr_rq || !phba->sli4_hba.dat_rq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0540 Receive Queue not allocated\n");
- goto out_destroy_fcp_wq;
+ rc = -ENOMEM;
+ goto out_destroy;
}
+
rc = lpfc_rq_create(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
phba->sli4_hba.els_cq, LPFC_USOL);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0541 Failed setup of Receive Queue: "
- "rc = 0x%x\n", rc);
- goto out_destroy_fcp_wq;
+ "rc = 0x%x\n", (uint32_t)rc);
+ goto out_destroy;
}
+
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"2592 USL RQ setup: hdr-rq-id=%d, dat-rq-id=%d "
"parent cq-id=%d\n",
phba->sli4_hba.hdr_rq->queue_id,
phba->sli4_hba.dat_rq->queue_id,
phba->sli4_hba.els_cq->queue_id);
+
+ if (phba->cfg_fcp_imax)
+ usdelay = LPFC_SEC_TO_USEC / phba->cfg_fcp_imax;
+ else
+ usdelay = 0;
+
+ for (qidx = 0; qidx < phba->cfg_irq_chann;
+ qidx += LPFC_MAX_EQ_DELAY_EQID_CNT)
+ lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT,
+ usdelay);
+
+ if (phba->sli4_hba.cq_max) {
+ kfree(phba->sli4_hba.cq_lookup);
+ phba->sli4_hba.cq_lookup = kcalloc((phba->sli4_hba.cq_max + 1),
+ sizeof(struct lpfc_queue *), GFP_KERNEL);
+ if (!phba->sli4_hba.cq_lookup) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0549 Failed setup of CQ Lookup table: "
+ "size 0x%x\n", phba->sli4_hba.cq_max);
+ rc = -ENOMEM;
+ goto out_destroy;
+ }
+ lpfc_setup_cq_lookup(phba);
+ }
return 0;
-out_destroy_fcp_wq:
- for (--fcp_wqidx; fcp_wqidx >= 0; fcp_wqidx--)
- lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_wqidx]);
- lpfc_wq_destroy(phba, phba->sli4_hba.els_wq);
-out_destroy_mbx_wq:
- lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
-out_destroy_fcp_cq:
- for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--)
- lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_cqidx]);
- lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
-out_destroy_mbx_cq:
- lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
-out_destroy_fp_eq:
- for (--fcp_eqidx; fcp_eqidx >= 0; fcp_eqidx--)
- lpfc_eq_destroy(phba, phba->sli4_hba.fp_eq[fcp_eqidx]);
- lpfc_eq_destroy(phba, phba->sli4_hba.sp_eq);
+out_destroy:
+ lpfc_sli4_queue_unset(phba);
out_error:
return rc;
}
@@ -6735,29 +11410,77 @@ out_error:
void
lpfc_sli4_queue_unset(struct lpfc_hba *phba)
{
- int fcp_qidx;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_queue *eq;
+ int qidx;
/* Unset mailbox command work queue */
- lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
+ if (phba->sli4_hba.mbx_wq)
+ lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
+
+ /* Unset NVME LS work queue */
+ if (phba->sli4_hba.nvmels_wq)
+ lpfc_wq_destroy(phba, phba->sli4_hba.nvmels_wq);
+
/* Unset ELS work queue */
- lpfc_wq_destroy(phba, phba->sli4_hba.els_wq);
+ if (phba->sli4_hba.els_wq)
+ lpfc_wq_destroy(phba, phba->sli4_hba.els_wq);
+
/* Unset unsolicited receive queue */
- lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq);
- /* Unset FCP work queue */
- for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++)
- lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_qidx]);
+ if (phba->sli4_hba.hdr_rq)
+ lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq,
+ phba->sli4_hba.dat_rq);
+
/* Unset mailbox command complete queue */
- lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
+ if (phba->sli4_hba.mbx_cq)
+ lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
+
/* Unset ELS complete queue */
- lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
- /* Unset FCP response complete queue */
- for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
- lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_qidx]);
- /* Unset fast-path event queue */
- for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
- lpfc_eq_destroy(phba, phba->sli4_hba.fp_eq[fcp_qidx]);
- /* Unset slow-path event queue */
- lpfc_eq_destroy(phba, phba->sli4_hba.sp_eq);
+ if (phba->sli4_hba.els_cq)
+ lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
+
+ /* Unset NVME LS complete queue */
+ if (phba->sli4_hba.nvmels_cq)
+ lpfc_cq_destroy(phba, phba->sli4_hba.nvmels_cq);
+
+ if (phba->nvmet_support) {
+ /* Unset NVMET MRQ queue */
+ if (phba->sli4_hba.nvmet_mrq_hdr) {
+ for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++)
+ lpfc_rq_destroy(
+ phba,
+ phba->sli4_hba.nvmet_mrq_hdr[qidx],
+ phba->sli4_hba.nvmet_mrq_data[qidx]);
+ }
+
+ /* Unset NVMET CQ Set complete queue */
+ if (phba->sli4_hba.nvmet_cqset) {
+ for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++)
+ lpfc_cq_destroy(
+ phba, phba->sli4_hba.nvmet_cqset[qidx]);
+ }
+ }
+
+ /* Unset fast-path SLI4 queues */
+ if (phba->sli4_hba.hdwq) {
+ /* Loop thru all Hardware Queues */
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ /* Destroy the CQ/WQ corresponding to Hardware Queue */
+ qp = &phba->sli4_hba.hdwq[qidx];
+ lpfc_wq_destroy(phba, qp->io_wq);
+ lpfc_cq_destroy(phba, qp->io_cq);
+ }
+ /* Loop thru all IRQ vectors */
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
+ /* Destroy the EQ corresponding to the IRQ vector */
+ eq = phba->sli4_hba.hba_eq_hdl[qidx].eq;
+ lpfc_eq_destroy(phba, eq);
+ }
+ }
+
+ kfree(phba->sli4_hba.cq_lookup);
+ phba->sli4_hba.cq_lookup = NULL;
+ phba->sli4_hba.cq_max = 0;
}
/**
@@ -6903,26 +11626,28 @@ lpfc_sli4_cq_event_release(struct lpfc_hba *phba,
static void
lpfc_sli4_cq_event_release_all(struct lpfc_hba *phba)
{
- LIST_HEAD(cqelist);
- struct lpfc_cq_event *cqe;
+ LIST_HEAD(cq_event_list);
+ struct lpfc_cq_event *cq_event;
unsigned long iflags;
/* Retrieve all the pending WCQEs from pending WCQE lists */
- spin_lock_irqsave(&phba->hbalock, iflags);
- /* Pending FCP XRI abort events */
- list_splice_init(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue,
- &cqelist);
+
/* Pending ELS XRI abort events */
+ spin_lock_irqsave(&phba->sli4_hba.els_xri_abrt_list_lock, iflags);
list_splice_init(&phba->sli4_hba.sp_els_xri_aborted_work_queue,
- &cqelist);
+ &cq_event_list);
+ spin_unlock_irqrestore(&phba->sli4_hba.els_xri_abrt_list_lock, iflags);
+
/* Pending asynnc events */
+ spin_lock_irqsave(&phba->sli4_hba.asynce_list_lock, iflags);
list_splice_init(&phba->sli4_hba.sp_asynce_work_queue,
- &cqelist);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ &cq_event_list);
+ spin_unlock_irqrestore(&phba->sli4_hba.asynce_list_lock, iflags);
- while (!list_empty(&cqelist)) {
- list_remove_head(&cqelist, cqe, struct lpfc_cq_event, list);
- lpfc_sli4_cq_event_release(phba, cqe);
+ while (!list_empty(&cq_event_list)) {
+ list_remove_head(&cq_event_list, cq_event,
+ struct lpfc_cq_event, list);
+ lpfc_sli4_cq_event_release(phba, cq_event);
}
}
@@ -6944,9 +11669,11 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
LPFC_MBOXQ_t *mboxq;
uint32_t rc = 0, if_type;
uint32_t shdr_status, shdr_add_status;
- uint32_t rdy_chk, num_resets = 0, reset_again = 0;
+ uint32_t rdy_chk;
+ uint32_t port_reset = 0;
union lpfc_sli4_cfg_shdr *shdr;
struct lpfc_register reg_data;
+ uint16_t devid;
if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
switch (if_type) {
@@ -6954,7 +11681,7 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
GFP_KERNEL);
if (!mboxq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0494 Unable to allocate memory for "
"issuing SLI_FUNCTION_RESET mailbox "
"command\n");
@@ -6971,10 +11698,9 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
&shdr->response);
- if (rc != MBX_TIMEOUT)
- mempool_free(mboxq, phba->mbox_mem_pool);
+ mempool_free(mboxq, phba->mbox_mem_pool);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0495 SLI_FUNCTION_RESET mailbox "
"failed with status x%x add_status x%x,"
" mbx status x%x\n",
@@ -6983,161 +11709,81 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
}
break;
case LPFC_SLI_INTF_IF_TYPE_2:
- for (num_resets = 0;
- num_resets < MAX_IF_TYPE_2_RESETS;
- num_resets++) {
- reg_data.word0 = 0;
- bf_set(lpfc_sliport_ctrl_end, &reg_data,
- LPFC_SLIPORT_LITTLE_ENDIAN);
- bf_set(lpfc_sliport_ctrl_ip, &reg_data,
- LPFC_SLIPORT_INIT_PORT);
- writel(reg_data.word0, phba->sli4_hba.u.if_type2.
- CTRLregaddr);
-
- /*
- * Poll the Port Status Register and wait for RDY for
- * up to 10 seconds. If the port doesn't respond, treat
- * it as an error. If the port responds with RN, start
- * the loop again.
- */
- for (rdy_chk = 0; rdy_chk < 1000; rdy_chk++) {
- if (lpfc_readl(phba->sli4_hba.u.if_type2.
- STATUSregaddr, &reg_data.word0)) {
- rc = -ENODEV;
- break;
- }
- if (bf_get(lpfc_sliport_status_rdy, &reg_data))
- break;
- if (bf_get(lpfc_sliport_status_rn, &reg_data)) {
- reset_again++;
- break;
- }
- msleep(10);
- }
-
- /*
- * If the port responds to the init request with
- * reset needed, delay for a bit and restart the loop.
- */
- if (reset_again) {
- msleep(10);
- reset_again = 0;
- continue;
- }
-
- /* Detect any port errors. */
- if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
- &reg_data.word0)) {
+ case LPFC_SLI_INTF_IF_TYPE_6:
+wait:
+ /*
+ * Poll the Port Status Register and wait for RDY for
+ * up to 30 seconds. If the port doesn't respond, treat
+ * it as an error.
+ */
+ for (rdy_chk = 0; rdy_chk < 1500; rdy_chk++) {
+ if (lpfc_readl(phba->sli4_hba.u.if_type2.
+ STATUSregaddr, &reg_data.word0)) {
rc = -ENODEV;
- break;
+ goto out;
}
- if ((bf_get(lpfc_sliport_status_err, &reg_data)) ||
- (rdy_chk >= 1000)) {
- phba->work_status[0] = readl(
- phba->sli4_hba.u.if_type2.ERR1regaddr);
- phba->work_status[1] = readl(
- phba->sli4_hba.u.if_type2.ERR2regaddr);
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2890 Port Error Detected "
- "during Port Reset: "
- "port status reg 0x%x, "
- "error 1=0x%x, error 2=0x%x\n",
+ if (bf_get(lpfc_sliport_status_rdy, &reg_data))
+ break;
+ msleep(20);
+ }
+
+ if (!bf_get(lpfc_sliport_status_rdy, &reg_data)) {
+ phba->work_status[0] = readl(
+ phba->sli4_hba.u.if_type2.ERR1regaddr);
+ phba->work_status[1] = readl(
+ phba->sli4_hba.u.if_type2.ERR2regaddr);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2890 Port not ready, port status reg "
+ "0x%x error 1=0x%x, error 2=0x%x\n",
reg_data.word0,
phba->work_status[0],
phba->work_status[1]);
- rc = -ENODEV;
- }
+ rc = -ENODEV;
+ goto out;
+ }
+ if (bf_get(lpfc_sliport_status_pldv, &reg_data))
+ lpfc_pldv_detect = true;
+
+ if (!port_reset) {
/*
- * Terminate the outer loop provided the Port indicated
- * ready within 10 seconds.
+ * Reset the port now
*/
- if (rdy_chk < 1000)
- break;
+ reg_data.word0 = 0;
+ bf_set(lpfc_sliport_ctrl_end, &reg_data,
+ LPFC_SLIPORT_LITTLE_ENDIAN);
+ bf_set(lpfc_sliport_ctrl_ip, &reg_data,
+ LPFC_SLIPORT_INIT_PORT);
+ writel(reg_data.word0, phba->sli4_hba.u.if_type2.
+ CTRLregaddr);
+ /* flush */
+ pci_read_config_word(phba->pcidev,
+ PCI_DEVICE_ID, &devid);
+
+ port_reset = 1;
+ msleep(20);
+ goto wait;
+ } else if (bf_get(lpfc_sliport_status_rn, &reg_data)) {
+ rc = -ENODEV;
+ goto out;
}
- /* delay driver action following IF_TYPE_2 function reset */
- msleep(100);
break;
+
case LPFC_SLI_INTF_IF_TYPE_1:
default:
break;
}
+out:
/* Catch the not-ready port failure after a port reset. */
- if (num_resets >= MAX_IF_TYPE_2_RESETS)
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3317 HBA not functional: IP Reset Failed "
+ "try: echo fw_reset > board_mode\n");
rc = -ENODEV;
-
- return rc;
-}
-
-/**
- * lpfc_sli4_send_nop_mbox_cmds - Send sli-4 nop mailbox commands
- * @phba: pointer to lpfc hba data structure.
- * @cnt: number of nop mailbox commands to send.
- *
- * This routine is invoked to send a number @cnt of NOP mailbox command and
- * wait for each command to complete.
- *
- * Return: the number of NOP mailbox command completed.
- **/
-static int
-lpfc_sli4_send_nop_mbox_cmds(struct lpfc_hba *phba, uint32_t cnt)
-{
- LPFC_MBOXQ_t *mboxq;
- int length, cmdsent;
- uint32_t mbox_tmo;
- uint32_t rc = 0;
- uint32_t shdr_status, shdr_add_status;
- union lpfc_sli4_cfg_shdr *shdr;
-
- if (cnt == 0) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2518 Requested to send 0 NOP mailbox cmd\n");
- return cnt;
- }
-
- mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mboxq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2519 Unable to allocate memory for issuing "
- "NOP mailbox command\n");
- return 0;
}
- /* Set up NOP SLI4_CONFIG mailbox-ioctl command */
- length = (sizeof(struct lpfc_mbx_nop) -
- sizeof(struct lpfc_sli4_cfg_mhdr));
- lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
- LPFC_MBOX_OPCODE_NOP, length, LPFC_SLI4_MBX_EMBED);
-
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
- for (cmdsent = 0; cmdsent < cnt; cmdsent++) {
- if (!phba->sli4_hba.intr_enable)
- rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
- else
- rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
- if (rc == MBX_TIMEOUT)
- break;
- /* Check return status */
- shdr = (union lpfc_sli4_cfg_shdr *)
- &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
- shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
- shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
- &shdr->response);
- if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2520 NOP mailbox command failed "
- "status x%x add_status x%x mbx "
- "status x%x\n", shdr_status,
- shdr_add_status, rc);
- break;
- }
- }
-
- if (rc != MBX_TIMEOUT)
- mempool_free(mboxq, phba->mbox_mem_pool);
-
- return cmdsent;
+ return rc;
}
/**
@@ -7154,25 +11800,20 @@ lpfc_sli4_send_nop_mbox_cmds(struct lpfc_hba *phba, uint32_t cnt)
static int
lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
{
- struct pci_dev *pdev;
+ struct pci_dev *pdev = phba->pcidev;
unsigned long bar0map_len, bar1map_len, bar2map_len;
- int error = -ENODEV;
+ int error;
uint32_t if_type;
- /* Obtain PCI device reference */
- if (!phba->pcidev)
- return error;
- else
- pdev = phba->pcidev;
+ if (!pdev)
+ return -ENODEV;
/* Set the device DMA mask size */
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0
- || pci_set_consistent_dma_mask(pdev,DMA_BIT_MASK(64)) != 0) {
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0
- || pci_set_consistent_dma_mask(pdev,DMA_BIT_MASK(32)) != 0) {
- return error;
- }
- }
+ error = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (error)
+ error = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (error)
+ return error;
/*
* The BARs and register set definitions and offset locations are
@@ -7180,7 +11821,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
*/
if (pci_read_config_dword(pdev, LPFC_SLI_INTF,
&phba->sli4_hba.sli_intf.word0)) {
- return error;
+ return -ENODEV;
}
/* There is no SLI3 failback for SLI4 devices. */
@@ -7190,7 +11831,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
"2894 SLI_INTF reg contents invalid "
"sli_intf reg 0x%x\n",
phba->sli4_hba.sli_intf.word0);
- return error;
+ return -ENODEV;
}
if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
@@ -7200,9 +11841,9 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
* particular PCI BARs regions is dependent on the type of
* SLI4 device.
*/
- if (pci_resource_start(pdev, 0)) {
- phba->pci_bar0_map = pci_resource_start(pdev, 0);
- bar0map_len = pci_resource_len(pdev, 0);
+ if (pci_resource_start(pdev, PCI_64BIT_BAR0)) {
+ phba->pci_bar0_map = pci_resource_start(pdev, PCI_64BIT_BAR0);
+ bar0map_len = pci_resource_len(pdev, PCI_64BIT_BAR0);
/*
* Map SLI4 PCI Config Space Register base to a kernel virtual
@@ -7214,17 +11855,18 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
dev_printk(KERN_ERR, &pdev->dev,
"ioremap failed for SLI4 PCI config "
"registers.\n");
- goto out;
+ return -ENODEV;
}
+ phba->pci_bar0_memmap_p = phba->sli4_hba.conf_regs_memmap_p;
/* Set up BAR0 PCI config space register memory map */
lpfc_sli4_bar0_register_memmap(phba, if_type);
} else {
phba->pci_bar0_map = pci_resource_start(pdev, 1);
bar0map_len = pci_resource_len(pdev, 1);
- if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
dev_printk(KERN_ERR, &pdev->dev,
"FATAL - No BAR0 mapping for SLI4, if_type 2\n");
- goto out;
+ return -ENODEV;
}
phba->sli4_hba.conf_regs_memmap_p =
ioremap(phba->pci_bar0_map, bar0map_len);
@@ -7232,58 +11874,136 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
dev_printk(KERN_ERR, &pdev->dev,
"ioremap failed for SLI4 PCI config "
"registers.\n");
- goto out;
+ return -ENODEV;
}
lpfc_sli4_bar0_register_memmap(phba, if_type);
}
- if ((if_type == LPFC_SLI_INTF_IF_TYPE_0) &&
- (pci_resource_start(pdev, 2))) {
- /*
- * Map SLI4 if type 0 HBA Control Register base to a kernel
- * virtual address and setup the registers.
- */
- phba->pci_bar1_map = pci_resource_start(pdev, 2);
- bar1map_len = pci_resource_len(pdev, 2);
- phba->sli4_hba.ctrl_regs_memmap_p =
- ioremap(phba->pci_bar1_map, bar1map_len);
- if (!phba->sli4_hba.ctrl_regs_memmap_p) {
- dev_printk(KERN_ERR, &pdev->dev,
- "ioremap failed for SLI4 HBA control registers.\n");
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+ if (pci_resource_start(pdev, PCI_64BIT_BAR2)) {
+ /*
+ * Map SLI4 if type 0 HBA Control Register base to a
+ * kernel virtual address and setup the registers.
+ */
+ phba->pci_bar1_map = pci_resource_start(pdev,
+ PCI_64BIT_BAR2);
+ bar1map_len = pci_resource_len(pdev, PCI_64BIT_BAR2);
+ phba->sli4_hba.ctrl_regs_memmap_p =
+ ioremap(phba->pci_bar1_map,
+ bar1map_len);
+ if (!phba->sli4_hba.ctrl_regs_memmap_p) {
+ dev_err(&pdev->dev,
+ "ioremap failed for SLI4 HBA "
+ "control registers.\n");
+ error = -ENOMEM;
+ goto out_iounmap_conf;
+ }
+ phba->pci_bar2_memmap_p =
+ phba->sli4_hba.ctrl_regs_memmap_p;
+ lpfc_sli4_bar1_register_memmap(phba, if_type);
+ } else {
+ error = -ENOMEM;
goto out_iounmap_conf;
}
- lpfc_sli4_bar1_register_memmap(phba);
}
- if ((if_type == LPFC_SLI_INTF_IF_TYPE_0) &&
- (pci_resource_start(pdev, 4))) {
+ if ((if_type == LPFC_SLI_INTF_IF_TYPE_6) &&
+ (pci_resource_start(pdev, PCI_64BIT_BAR2))) {
/*
- * Map SLI4 if type 0 HBA Doorbell Register base to a kernel
+ * Map SLI4 if type 6 HBA Doorbell Register base to a kernel
* virtual address and setup the registers.
*/
- phba->pci_bar2_map = pci_resource_start(pdev, 4);
- bar2map_len = pci_resource_len(pdev, 4);
+ phba->pci_bar1_map = pci_resource_start(pdev, PCI_64BIT_BAR2);
+ bar1map_len = pci_resource_len(pdev, PCI_64BIT_BAR2);
phba->sli4_hba.drbl_regs_memmap_p =
- ioremap(phba->pci_bar2_map, bar2map_len);
+ ioremap(phba->pci_bar1_map, bar1map_len);
if (!phba->sli4_hba.drbl_regs_memmap_p) {
- dev_printk(KERN_ERR, &pdev->dev,
+ dev_err(&pdev->dev,
"ioremap failed for SLI4 HBA doorbell registers.\n");
+ error = -ENOMEM;
+ goto out_iounmap_conf;
+ }
+ phba->pci_bar2_memmap_p = phba->sli4_hba.drbl_regs_memmap_p;
+ lpfc_sli4_bar1_register_memmap(phba, if_type);
+ }
+
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
+ if (pci_resource_start(pdev, PCI_64BIT_BAR4)) {
+ /*
+ * Map SLI4 if type 0 HBA Doorbell Register base to
+ * a kernel virtual address and setup the registers.
+ */
+ phba->pci_bar2_map = pci_resource_start(pdev,
+ PCI_64BIT_BAR4);
+ bar2map_len = pci_resource_len(pdev, PCI_64BIT_BAR4);
+ phba->sli4_hba.drbl_regs_memmap_p =
+ ioremap(phba->pci_bar2_map,
+ bar2map_len);
+ if (!phba->sli4_hba.drbl_regs_memmap_p) {
+ dev_err(&pdev->dev,
+ "ioremap failed for SLI4 HBA"
+ " doorbell registers.\n");
+ error = -ENOMEM;
+ goto out_iounmap_ctrl;
+ }
+ phba->pci_bar4_memmap_p =
+ phba->sli4_hba.drbl_regs_memmap_p;
+ error = lpfc_sli4_bar2_register_memmap(phba, LPFC_VF0);
+ if (error)
+ goto out_iounmap_all;
+ } else {
+ error = -ENOMEM;
goto out_iounmap_ctrl;
}
- error = lpfc_sli4_bar2_register_memmap(phba, LPFC_VF0);
- if (error)
+ }
+
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_6 &&
+ pci_resource_start(pdev, PCI_64BIT_BAR4)) {
+ /*
+ * Map SLI4 if type 6 HBA DPP Register base to a kernel
+ * virtual address and setup the registers.
+ */
+ phba->pci_bar2_map = pci_resource_start(pdev, PCI_64BIT_BAR4);
+ bar2map_len = pci_resource_len(pdev, PCI_64BIT_BAR4);
+ phba->sli4_hba.dpp_regs_memmap_p =
+ ioremap(phba->pci_bar2_map, bar2map_len);
+ if (!phba->sli4_hba.dpp_regs_memmap_p) {
+ dev_err(&pdev->dev,
+ "ioremap failed for SLI4 HBA dpp registers.\n");
+ error = -ENOMEM;
goto out_iounmap_all;
+ }
+ phba->pci_bar4_memmap_p = phba->sli4_hba.dpp_regs_memmap_p;
+ }
+
+ /* Set up the EQ/CQ register handeling functions now */
+ switch (if_type) {
+ case LPFC_SLI_INTF_IF_TYPE_0:
+ case LPFC_SLI_INTF_IF_TYPE_2:
+ phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_eq_clr_intr;
+ phba->sli4_hba.sli4_write_eq_db = lpfc_sli4_write_eq_db;
+ phba->sli4_hba.sli4_write_cq_db = lpfc_sli4_write_cq_db;
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_if6_eq_clr_intr;
+ phba->sli4_hba.sli4_write_eq_db = lpfc_sli4_if6_write_eq_db;
+ phba->sli4_hba.sli4_write_cq_db = lpfc_sli4_if6_write_cq_db;
+ break;
+ default:
+ break;
}
return 0;
out_iounmap_all:
- iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+ if (phba->sli4_hba.drbl_regs_memmap_p)
+ iounmap(phba->sli4_hba.drbl_regs_memmap_p);
out_iounmap_ctrl:
- iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
+ if (phba->sli4_hba.ctrl_regs_memmap_p)
+ iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
out_iounmap_conf:
iounmap(phba->sli4_hba.conf_regs_memmap_p);
-out:
+
return error;
}
@@ -7297,22 +12017,32 @@ out:
static void
lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
{
- struct pci_dev *pdev;
-
- /* Obtain PCI device reference */
- if (!phba->pcidev)
- return;
- else
- pdev = phba->pcidev;
-
- /* Free coherent DMA memory allocated */
-
- /* Unmap I/O memory space */
- iounmap(phba->sli4_hba.drbl_regs_memmap_p);
- iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
- iounmap(phba->sli4_hba.conf_regs_memmap_p);
+ uint32_t if_type;
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
- return;
+ switch (if_type) {
+ case LPFC_SLI_INTF_IF_TYPE_0:
+ iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+ iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
+ iounmap(phba->sli4_hba.conf_regs_memmap_p);
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_2:
+ iounmap(phba->sli4_hba.conf_regs_memmap_p);
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+ iounmap(phba->sli4_hba.conf_regs_memmap_p);
+ if (phba->sli4_hba.dpp_regs_memmap_p)
+ iounmap(phba->sli4_hba.dpp_regs_memmap_p);
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_1:
+ break;
+ default:
+ dev_printk(KERN_ERR, &phba->pcidev->dev,
+ "FATAL - unsupported SLI4 interface type - %d\n",
+ if_type);
+ break;
+ }
}
/**
@@ -7320,16 +12050,7 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to enable the MSI-X interrupt vectors to device
- * with SLI-3 interface specs. The kernel function pci_enable_msix() is
- * called to enable the MSI-X vectors. Note that pci_enable_msix(), once
- * invoked, enables either all or nothing, depending on the current
- * availability of PCI vector resources. The device driver is responsible
- * for calling the individual request_irq() to register each MSI-X vector
- * with a interrupt handler, which is done in this function. Note that
- * later when device is unloading, the driver should always call free_irq()
- * on all MSI-X vectors it has done request_irq() on before calling
- * pci_disable_msix(). Failure to do so results in a BUG_ON() and a device
- * will be left with MSI-X enabled and leaks its vectors.
+ * with SLI-3 interface specs.
*
* Return codes
* 0 - successful
@@ -7338,34 +12059,25 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
static int
lpfc_sli_enable_msix(struct lpfc_hba *phba)
{
- int rc, i;
+ int rc;
LPFC_MBOXQ_t *pmb;
/* Set up MSI-X multi-message vectors */
- for (i = 0; i < LPFC_MSIX_VECTORS; i++)
- phba->msix_entries[i].entry = i;
-
- /* Configure MSI-X capability structure */
- rc = pci_enable_msix(phba->pcidev, phba->msix_entries,
- ARRAY_SIZE(phba->msix_entries));
- if (rc) {
+ rc = pci_alloc_irq_vectors(phba->pcidev,
+ LPFC_MSIX_VECTORS, LPFC_MSIX_VECTORS, PCI_IRQ_MSIX);
+ if (rc < 0) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0420 PCI enable MSI-X failed (%d)\n", rc);
- goto msi_fail_out;
+ goto vec_fail_out;
}
- for (i = 0; i < LPFC_MSIX_VECTORS; i++)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0477 MSI-X entry[%d]: vector=x%x "
- "message=%d\n", i,
- phba->msix_entries[i].vector,
- phba->msix_entries[i].entry);
+
/*
* Assign MSI-X vectors to interrupt handlers
*/
/* vector-0 is associated to slow-path handler */
- rc = request_irq(phba->msix_entries[0].vector,
- &lpfc_sli_sp_intr_handler, IRQF_SHARED,
+ rc = request_irq(pci_irq_vector(phba->pcidev, 0),
+ &lpfc_sli_sp_intr_handler, 0,
LPFC_SP_DRIVER_HANDLER_NAME, phba);
if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -7375,8 +12087,8 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba)
}
/* vector-1 is associated to fast-path handler */
- rc = request_irq(phba->msix_entries[1].vector,
- &lpfc_sli_fp_intr_handler, IRQF_SHARED,
+ rc = request_irq(pci_irq_vector(phba->pcidev, 1),
+ &lpfc_sli_fp_intr_handler, 0,
LPFC_FP_DRIVER_HANDLER_NAME, phba);
if (rc) {
@@ -7393,7 +12105,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba)
if (!pmb) {
rc = -ENOMEM;
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0474 Unable to allocate memory for issuing "
"MBOX_CONFIG_MSI command\n");
goto mem_fail_out;
@@ -7420,37 +12132,18 @@ mbx_fail_out:
mem_fail_out:
/* free the irq already requested */
- free_irq(phba->msix_entries[1].vector, phba);
+ free_irq(pci_irq_vector(phba->pcidev, 1), phba);
irq_fail_out:
/* free the irq already requested */
- free_irq(phba->msix_entries[0].vector, phba);
+ free_irq(pci_irq_vector(phba->pcidev, 0), phba);
msi_fail_out:
/* Unconfigure MSI-X capability structure */
- pci_disable_msix(phba->pcidev);
- return rc;
-}
-
-/**
- * lpfc_sli_disable_msix - Disable MSI-X interrupt mode on SLI-3 device.
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to release the MSI-X vectors and then disable the
- * MSI-X interrupt mode to device with SLI-3 interface spec.
- **/
-static void
-lpfc_sli_disable_msix(struct lpfc_hba *phba)
-{
- int i;
+ pci_free_irq_vectors(phba->pcidev);
- /* Free up MSI-X multi-message vectors */
- for (i = 0; i < LPFC_MSIX_VECTORS; i++)
- free_irq(phba->msix_entries[i].vector, phba);
- /* Disable MSI-X */
- pci_disable_msix(phba->pcidev);
-
- return;
+vec_fail_out:
+ return rc;
}
/**
@@ -7475,7 +12168,7 @@ lpfc_sli_enable_msi(struct lpfc_hba *phba)
rc = pci_enable_msi(phba->pcidev);
if (!rc)
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0462 PCI enable MSI mode success.\n");
+ "0012 PCI enable MSI mode success.\n");
else {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0471 PCI enable MSI mode failed (%d)\n", rc);
@@ -7483,7 +12176,7 @@ lpfc_sli_enable_msi(struct lpfc_hba *phba)
}
rc = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ 0, LPFC_DRIVER_NAME, phba);
if (rc) {
pci_disable_msi(phba->pcidev);
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -7493,26 +12186,9 @@ lpfc_sli_enable_msi(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_disable_msi - Disable MSI interrupt mode to SLI-3 device.
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to disable the MSI interrupt mode to device with
- * SLI-3 interface spec. The driver calls free_irq() on MSI vector it has
- * done request_irq() on before calling pci_disable_msi(). Failure to do so
- * results in a BUG_ON() and a device will be left with MSI enabled and leaks
- * its vector.
- */
-static void
-lpfc_sli_disable_msi(struct lpfc_hba *phba)
-{
- free_irq(phba->pcidev->irq, phba);
- pci_disable_msi(phba->pcidev);
- return;
-}
-
-/**
* lpfc_sli_enable_intr - Enable device interrupt to SLI-3 device.
* @phba: pointer to lpfc hba data structure.
+ * @cfg_mode: Interrupt configuration mode (INTx, MSI or MSI-X).
*
* This routine is invoked to enable device interrupt and associate driver's
* interrupt handler(s) to interrupt vector(s) to device with SLI-3 interface
@@ -7532,17 +12208,19 @@ lpfc_sli_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
uint32_t intr_mode = LPFC_INTR_ERROR;
int retval;
+ /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+ retval = lpfc_sli_config_port(phba, LPFC_SLI_REV3);
+ if (retval)
+ return intr_mode;
+ clear_bit(HBA_NEEDS_CFG_PORT, &phba->hba_flag);
+
if (cfg_mode == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- retval = lpfc_sli_config_port(phba, LPFC_SLI_REV3);
+ /* Now, try to enable MSI-X interrupt mode */
+ retval = lpfc_sli_enable_msix(phba);
if (!retval) {
- /* Now, try to enable MSI-X interrupt mode */
- retval = lpfc_sli_enable_msix(phba);
- if (!retval) {
- /* Indicate initialization to MSI-X mode */
- phba->intr_type = MSIX;
- intr_mode = 2;
- }
+ /* Indicate initialization to MSI-X mode */
+ phba->intr_type = MSIX;
+ intr_mode = 2;
}
}
@@ -7581,36 +12259,711 @@ lpfc_sli_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
static void
lpfc_sli_disable_intr(struct lpfc_hba *phba)
{
- /* Disable the currently initialized interrupt mode */
+ int nr_irqs, i;
+
if (phba->intr_type == MSIX)
- lpfc_sli_disable_msix(phba);
- else if (phba->intr_type == MSI)
- lpfc_sli_disable_msi(phba);
- else if (phba->intr_type == INTx)
- free_irq(phba->pcidev->irq, phba);
+ nr_irqs = LPFC_MSIX_VECTORS;
+ else
+ nr_irqs = 1;
+
+ for (i = 0; i < nr_irqs; i++)
+ free_irq(pci_irq_vector(phba->pcidev, i), phba);
+ pci_free_irq_vectors(phba->pcidev);
/* Reset interrupt management states */
phba->intr_type = NONE;
phba->sli.slistat.sli_intr = 0;
+}
+
+/**
+ * lpfc_find_cpu_handle - Find the CPU that corresponds to the specified Queue
+ * @phba: pointer to lpfc hba data structure.
+ * @id: EQ vector index or Hardware Queue index
+ * @match: LPFC_FIND_BY_EQ = match by EQ
+ * LPFC_FIND_BY_HDWQ = match by Hardware Queue
+ * Return the CPU that matches the selection criteria
+ */
+static uint16_t
+lpfc_find_cpu_handle(struct lpfc_hba *phba, uint16_t id, int match)
+{
+ struct lpfc_vector_map_info *cpup;
+ int cpu;
+
+ /* Loop through all CPUs */
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* If we are matching by EQ, there may be multiple CPUs using
+ * using the same vector, so select the one with
+ * LPFC_CPU_FIRST_IRQ set.
+ */
+ if ((match == LPFC_FIND_BY_EQ) &&
+ (cpup->flag & LPFC_CPU_FIRST_IRQ) &&
+ (cpup->eq == id))
+ return cpu;
+
+ /* If matching by HDWQ, select the first CPU that matches */
+ if ((match == LPFC_FIND_BY_HDWQ) && (cpup->hdwq == id))
+ return cpu;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_X86
+/**
+ * lpfc_find_hyper - Determine if the CPU map entry is hyper-threaded
+ * @phba: pointer to lpfc hba data structure.
+ * @cpu: CPU map index
+ * @phys_id: CPU package physical id
+ * @core_id: CPU core id
+ */
+static int
+lpfc_find_hyper(struct lpfc_hba *phba, int cpu,
+ uint16_t phys_id, uint16_t core_id)
+{
+ struct lpfc_vector_map_info *cpup;
+ int idx;
+
+ for_each_present_cpu(idx) {
+ cpup = &phba->sli4_hba.cpu_map[idx];
+ /* Does the cpup match the one we are looking for */
+ if ((cpup->phys_id == phys_id) &&
+ (cpup->core_id == core_id) &&
+ (cpu != idx))
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * lpfc_assign_eq_map_info - Assigns eq for vector_map structure
+ * @phba: pointer to lpfc hba data structure.
+ * @eqidx: index for eq and irq vector
+ * @flag: flags to set for vector_map structure
+ * @cpu: cpu used to index vector_map structure
+ *
+ * The routine assigns eq info into vector_map structure
+ */
+static inline void
+lpfc_assign_eq_map_info(struct lpfc_hba *phba, uint16_t eqidx, uint16_t flag,
+ unsigned int cpu)
+{
+ struct lpfc_vector_map_info *cpup = &phba->sli4_hba.cpu_map[cpu];
+ struct lpfc_hba_eq_hdl *eqhdl = lpfc_get_eq_hdl(eqidx);
+
+ cpup->eq = eqidx;
+ cpup->flag |= flag;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3336 Set Affinity: CPU %d irq %d eq %d flag x%x\n",
+ cpu, eqhdl->irq, cpup->eq, cpup->flag);
+}
+
+/**
+ * lpfc_cpu_map_array_init - Initialize cpu_map structure
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * The routine initializes the cpu_map array structure
+ */
+static void
+lpfc_cpu_map_array_init(struct lpfc_hba *phba)
+{
+ struct lpfc_vector_map_info *cpup;
+ struct lpfc_eq_intr_info *eqi;
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+ cpup->phys_id = LPFC_VECTOR_MAP_EMPTY;
+ cpup->core_id = LPFC_VECTOR_MAP_EMPTY;
+ cpup->hdwq = LPFC_VECTOR_MAP_EMPTY;
+ cpup->eq = LPFC_VECTOR_MAP_EMPTY;
+ cpup->flag = 0;
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, cpu);
+ INIT_LIST_HEAD(&eqi->list);
+ eqi->icnt = 0;
+ }
+}
+
+/**
+ * lpfc_hba_eq_hdl_array_init - Initialize hba_eq_hdl structure
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * The routine initializes the hba_eq_hdl array structure
+ */
+static void
+lpfc_hba_eq_hdl_array_init(struct lpfc_hba *phba)
+{
+ struct lpfc_hba_eq_hdl *eqhdl;
+ int i;
+
+ for (i = 0; i < phba->cfg_irq_chann; i++) {
+ eqhdl = lpfc_get_eq_hdl(i);
+ eqhdl->irq = LPFC_IRQ_EMPTY;
+ eqhdl->phba = phba;
+ }
+}
+
+/**
+ * lpfc_cpu_affinity_check - Check vector CPU affinity mappings
+ * @phba: pointer to lpfc hba data structure.
+ * @vectors: number of msix vectors allocated.
+ *
+ * The routine will figure out the CPU affinity assignment for every
+ * MSI-X vector allocated for the HBA.
+ * In addition, the CPU to IO channel mapping will be calculated
+ * and the phba->sli4_hba.cpu_map array will reflect this.
+ */
+static void
+lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
+{
+ int i, cpu, idx, next_idx, new_cpu, start_cpu, first_cpu;
+ int max_phys_id, min_phys_id;
+ int max_core_id, min_core_id;
+ struct lpfc_vector_map_info *cpup;
+ struct lpfc_vector_map_info *new_cpup;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ struct lpfc_hdwq_stat *c_stat;
+#endif
+
+ max_phys_id = 0;
+ min_phys_id = LPFC_VECTOR_MAP_EMPTY;
+ max_core_id = 0;
+ min_core_id = LPFC_VECTOR_MAP_EMPTY;
+
+ /* Update CPU map with physical id and core id of each CPU */
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+#ifdef CONFIG_X86
+ cpup->phys_id = topology_physical_package_id(cpu);
+ cpup->core_id = topology_core_id(cpu);
+ if (lpfc_find_hyper(phba, cpu, cpup->phys_id, cpup->core_id))
+ cpup->flag |= LPFC_CPU_MAP_HYPER;
+#else
+ /* No distinction between CPUs for other platforms */
+ cpup->phys_id = 0;
+ cpup->core_id = cpu;
+#endif
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3328 CPU %d physid %d coreid %d flag x%x\n",
+ cpu, cpup->phys_id, cpup->core_id, cpup->flag);
+
+ if (cpup->phys_id > max_phys_id)
+ max_phys_id = cpup->phys_id;
+ if (cpup->phys_id < min_phys_id)
+ min_phys_id = cpup->phys_id;
+
+ if (cpup->core_id > max_core_id)
+ max_core_id = cpup->core_id;
+ if (cpup->core_id < min_core_id)
+ min_core_id = cpup->core_id;
+ }
+
+ /* After looking at each irq vector assigned to this pcidev, its
+ * possible to see that not ALL CPUs have been accounted for.
+ * Next we will set any unassigned (unaffinitized) cpu map
+ * entries to a IRQ on the same phys_id.
+ */
+ first_cpu = cpumask_first(cpu_present_mask);
+ start_cpu = first_cpu;
+
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* Is this CPU entry unassigned */
+ if (cpup->eq == LPFC_VECTOR_MAP_EMPTY) {
+ /* Mark CPU as IRQ not assigned by the kernel */
+ cpup->flag |= LPFC_CPU_MAP_UNASSIGN;
+
+ /* If so, find a new_cpup that is on the SAME
+ * phys_id as cpup. start_cpu will start where we
+ * left off so all unassigned entries don't get assgined
+ * the IRQ of the first entry.
+ */
+ new_cpu = start_cpu;
+ for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ new_cpup = &phba->sli4_hba.cpu_map[new_cpu];
+ if (!(new_cpup->flag & LPFC_CPU_MAP_UNASSIGN) &&
+ (new_cpup->eq != LPFC_VECTOR_MAP_EMPTY) &&
+ (new_cpup->phys_id == cpup->phys_id))
+ goto found_same;
+ new_cpu = lpfc_next_present_cpu(new_cpu);
+ }
+ /* At this point, we leave the CPU as unassigned */
+ continue;
+found_same:
+ /* We found a matching phys_id, so copy the IRQ info */
+ cpup->eq = new_cpup->eq;
+
+ /* Bump start_cpu to the next slot to minmize the
+ * chance of having multiple unassigned CPU entries
+ * selecting the same IRQ.
+ */
+ start_cpu = lpfc_next_present_cpu(new_cpu);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3337 Set Affinity: CPU %d "
+ "eq %d from peer cpu %d same "
+ "phys_id (%d)\n",
+ cpu, cpup->eq, new_cpu,
+ cpup->phys_id);
+ }
+ }
+
+ /* Set any unassigned cpu map entries to a IRQ on any phys_id */
+ start_cpu = first_cpu;
+
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* Is this entry unassigned */
+ if (cpup->eq == LPFC_VECTOR_MAP_EMPTY) {
+ /* Mark it as IRQ not assigned by the kernel */
+ cpup->flag |= LPFC_CPU_MAP_UNASSIGN;
+
+ /* If so, find a new_cpup thats on ANY phys_id
+ * as the cpup. start_cpu will start where we
+ * left off so all unassigned entries don't get
+ * assigned the IRQ of the first entry.
+ */
+ new_cpu = start_cpu;
+ for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ new_cpup = &phba->sli4_hba.cpu_map[new_cpu];
+ if (!(new_cpup->flag & LPFC_CPU_MAP_UNASSIGN) &&
+ (new_cpup->eq != LPFC_VECTOR_MAP_EMPTY))
+ goto found_any;
+ new_cpu = lpfc_next_present_cpu(new_cpu);
+ }
+ /* We should never leave an entry unassigned */
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3339 Set Affinity: CPU %d "
+ "eq %d UNASSIGNED\n",
+ cpup->hdwq, cpup->eq);
+ continue;
+found_any:
+ /* We found an available entry, copy the IRQ info */
+ cpup->eq = new_cpup->eq;
+ /* Bump start_cpu to the next slot to minmize the
+ * chance of having multiple unassigned CPU entries
+ * selecting the same IRQ.
+ */
+ start_cpu = lpfc_next_present_cpu(new_cpu);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3338 Set Affinity: CPU %d "
+ "eq %d from peer cpu %d (%d/%d)\n",
+ cpu, cpup->eq, new_cpu,
+ new_cpup->phys_id, new_cpup->core_id);
+ }
+ }
+
+ /* Assign hdwq indices that are unique across all cpus in the map
+ * that are also FIRST_CPUs.
+ */
+ idx = 0;
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* Only FIRST IRQs get a hdwq index assignment. */
+ if (!(cpup->flag & LPFC_CPU_FIRST_IRQ))
+ continue;
+
+ /* 1 to 1, the first LPFC_CPU_FIRST_IRQ cpus to a unique hdwq */
+ cpup->hdwq = idx;
+ idx++;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3333 Set Affinity: CPU %d (phys %d core %d): "
+ "hdwq %d eq %d flg x%x\n",
+ cpu, cpup->phys_id, cpup->core_id,
+ cpup->hdwq, cpup->eq, cpup->flag);
+ }
+ /* Associate a hdwq with each cpu_map entry
+ * This will be 1 to 1 - hdwq to cpu, unless there are less
+ * hardware queues then CPUs. For that case we will just round-robin
+ * the available hardware queues as they get assigned to CPUs.
+ * The next_idx is the idx from the FIRST_CPU loop above to account
+ * for irq_chann < hdwq. The idx is used for round-robin assignments
+ * and needs to start at 0.
+ */
+ next_idx = idx;
+ start_cpu = 0;
+ idx = 0;
+ for_each_present_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* FIRST cpus are already mapped. */
+ if (cpup->flag & LPFC_CPU_FIRST_IRQ)
+ continue;
+
+ /* If the cfg_irq_chann < cfg_hdw_queue, set the hdwq
+ * of the unassigned cpus to the next idx so that all
+ * hdw queues are fully utilized.
+ */
+ if (next_idx < phba->cfg_hdw_queue) {
+ cpup->hdwq = next_idx;
+ next_idx++;
+ continue;
+ }
+
+ /* Not a First CPU and all hdw_queues are used. Reuse a
+ * Hardware Queue for another CPU, so be smart about it
+ * and pick one that has its IRQ/EQ mapped to the same phys_id
+ * (CPU package) and core_id.
+ */
+ new_cpu = start_cpu;
+ for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ new_cpup = &phba->sli4_hba.cpu_map[new_cpu];
+ if (new_cpup->hdwq != LPFC_VECTOR_MAP_EMPTY &&
+ new_cpup->phys_id == cpup->phys_id &&
+ new_cpup->core_id == cpup->core_id) {
+ goto found_hdwq;
+ }
+ new_cpu = lpfc_next_present_cpu(new_cpu);
+ }
+
+ /* If we can't match both phys_id and core_id,
+ * settle for just a phys_id match.
+ */
+ new_cpu = start_cpu;
+ for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
+ new_cpup = &phba->sli4_hba.cpu_map[new_cpu];
+ if (new_cpup->hdwq != LPFC_VECTOR_MAP_EMPTY &&
+ new_cpup->phys_id == cpup->phys_id)
+ goto found_hdwq;
+ new_cpu = lpfc_next_present_cpu(new_cpu);
+ }
+
+ /* Otherwise just round robin on cfg_hdw_queue */
+ cpup->hdwq = idx % phba->cfg_hdw_queue;
+ idx++;
+ goto logit;
+ found_hdwq:
+ /* We found an available entry, copy the IRQ info */
+ start_cpu = lpfc_next_present_cpu(new_cpu);
+ cpup->hdwq = new_cpup->hdwq;
+ logit:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3335 Set Affinity: CPU %d (phys %d core %d): "
+ "hdwq %d eq %d flg x%x\n",
+ cpu, cpup->phys_id, cpup->core_id,
+ cpup->hdwq, cpup->eq, cpup->flag);
+ }
+
+ /*
+ * Initialize the cpu_map slots for not-present cpus in case
+ * a cpu is hot-added. Perform a simple hdwq round robin assignment.
+ */
+ idx = 0;
+ for_each_possible_cpu(cpu) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ c_stat = per_cpu_ptr(phba->sli4_hba.c_stat, cpu);
+ c_stat->hdwq_no = cpup->hdwq;
+#endif
+ if (cpup->hdwq != LPFC_VECTOR_MAP_EMPTY)
+ continue;
+
+ cpup->hdwq = idx++ % phba->cfg_hdw_queue;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ c_stat->hdwq_no = cpup->hdwq;
+#endif
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3340 Set Affinity: not present "
+ "CPU %d hdwq %d\n",
+ cpu, cpup->hdwq);
+ }
+
+ /* The cpu_map array will be used later during initialization
+ * when EQ / CQ / WQs are allocated and configured.
+ */
return;
}
/**
+ * lpfc_cpuhp_get_eq
+ *
+ * @phba: pointer to lpfc hba data structure.
+ * @cpu: cpu going offline
+ * @eqlist: eq list to append to
+ */
+static int
+lpfc_cpuhp_get_eq(struct lpfc_hba *phba, unsigned int cpu,
+ struct list_head *eqlist)
+{
+ const struct cpumask *maskp;
+ struct lpfc_queue *eq;
+ struct cpumask *tmp;
+ u16 idx;
+
+ tmp = kzalloc(cpumask_size(), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ for (idx = 0; idx < phba->cfg_irq_chann; idx++) {
+ maskp = pci_irq_get_affinity(phba->pcidev, idx);
+ if (!maskp)
+ continue;
+ /*
+ * if irq is not affinitized to the cpu going
+ * then we don't need to poll the eq attached
+ * to it.
+ */
+ if (!cpumask_and(tmp, maskp, cpumask_of(cpu)))
+ continue;
+ /* get the cpus that are online and are affini-
+ * tized to this irq vector. If the count is
+ * more than 1 then cpuhp is not going to shut-
+ * down this vector. Since this cpu has not
+ * gone offline yet, we need >1.
+ */
+ cpumask_and(tmp, maskp, cpu_online_mask);
+ if (cpumask_weight(tmp) > 1)
+ continue;
+
+ /* Now that we have an irq to shutdown, get the eq
+ * mapped to this irq. Note: multiple hdwq's in
+ * the software can share an eq, but eventually
+ * only eq will be mapped to this vector
+ */
+ eq = phba->sli4_hba.hba_eq_hdl[idx].eq;
+ list_add(&eq->_poll_list, eqlist);
+ }
+ kfree(tmp);
+ return 0;
+}
+
+static void __lpfc_cpuhp_remove(struct lpfc_hba *phba)
+{
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return;
+
+ cpuhp_state_remove_instance_nocalls(lpfc_cpuhp_state,
+ &phba->cpuhp);
+ /*
+ * unregistering the instance doesn't stop the polling
+ * timer. Wait for the poll timer to retire.
+ */
+ synchronize_rcu();
+ timer_delete_sync(&phba->cpuhp_poll_timer);
+}
+
+static void lpfc_cpuhp_remove(struct lpfc_hba *phba)
+{
+ if (phba->pport &&
+ test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
+ return;
+
+ __lpfc_cpuhp_remove(phba);
+}
+
+static void lpfc_cpuhp_add(struct lpfc_hba *phba)
+{
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return;
+
+ rcu_read_lock();
+
+ if (!list_empty(&phba->poll_list))
+ mod_timer(&phba->cpuhp_poll_timer,
+ jiffies + msecs_to_jiffies(LPFC_POLL_HB));
+
+ rcu_read_unlock();
+
+ cpuhp_state_add_instance_nocalls(lpfc_cpuhp_state,
+ &phba->cpuhp);
+}
+
+static int __lpfc_cpuhp_checks(struct lpfc_hba *phba, int *retval)
+{
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
+ *retval = -EAGAIN;
+ return true;
+ }
+
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ *retval = 0;
+ return true;
+ }
+
+ /* proceed with the hotplug */
+ return false;
+}
+
+/**
+ * lpfc_irq_set_aff - set IRQ affinity
+ * @eqhdl: EQ handle
+ * @cpu: cpu to set affinity
+ *
+ **/
+static inline void
+lpfc_irq_set_aff(struct lpfc_hba_eq_hdl *eqhdl, unsigned int cpu)
+{
+ cpumask_clear(&eqhdl->aff_mask);
+ cpumask_set_cpu(cpu, &eqhdl->aff_mask);
+ irq_set_status_flags(eqhdl->irq, IRQ_NO_BALANCING);
+ irq_set_affinity(eqhdl->irq, &eqhdl->aff_mask);
+}
+
+/**
+ * lpfc_irq_clear_aff - clear IRQ affinity
+ * @eqhdl: EQ handle
+ *
+ **/
+static inline void
+lpfc_irq_clear_aff(struct lpfc_hba_eq_hdl *eqhdl)
+{
+ cpumask_clear(&eqhdl->aff_mask);
+ irq_clear_status_flags(eqhdl->irq, IRQ_NO_BALANCING);
+}
+
+/**
+ * lpfc_irq_rebalance - rebalances IRQ affinity according to cpuhp event
+ * @phba: pointer to HBA context object.
+ * @cpu: cpu going offline/online
+ * @offline: true, cpu is going offline. false, cpu is coming online.
+ *
+ * If cpu is going offline, we'll try our best effort to find the next
+ * online cpu on the phba's original_mask and migrate all offlining IRQ
+ * affinities.
+ *
+ * If cpu is coming online, reaffinitize the IRQ back to the onlining cpu.
+ *
+ * Note: Call only if NUMA or NHT mode is enabled, otherwise rely on
+ * PCI_IRQ_AFFINITY to auto-manage IRQ affinity.
+ *
+ **/
+static void
+lpfc_irq_rebalance(struct lpfc_hba *phba, unsigned int cpu, bool offline)
+{
+ struct lpfc_vector_map_info *cpup;
+ struct cpumask *aff_mask;
+ unsigned int cpu_select, cpu_next, idx;
+ const struct cpumask *orig_mask;
+
+ if (phba->irq_chann_mode == NORMAL_MODE)
+ return;
+
+ orig_mask = &phba->sli4_hba.irq_aff_mask;
+
+ if (!cpumask_test_cpu(cpu, orig_mask))
+ return;
+
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ if (!(cpup->flag & LPFC_CPU_FIRST_IRQ))
+ return;
+
+ if (offline) {
+ /* Find next online CPU on original mask */
+ cpu_next = cpumask_next_wrap(cpu, orig_mask);
+ cpu_select = lpfc_next_online_cpu(orig_mask, cpu_next);
+
+ /* Found a valid CPU */
+ if ((cpu_select < nr_cpu_ids) && (cpu_select != cpu)) {
+ /* Go through each eqhdl and ensure offlining
+ * cpu aff_mask is migrated
+ */
+ for (idx = 0; idx < phba->cfg_irq_chann; idx++) {
+ aff_mask = lpfc_get_aff_mask(idx);
+
+ /* Migrate affinity */
+ if (cpumask_test_cpu(cpu, aff_mask))
+ lpfc_irq_set_aff(lpfc_get_eq_hdl(idx),
+ cpu_select);
+ }
+ } else {
+ /* Rely on irqbalance if no online CPUs left on NUMA */
+ for (idx = 0; idx < phba->cfg_irq_chann; idx++)
+ lpfc_irq_clear_aff(lpfc_get_eq_hdl(idx));
+ }
+ } else {
+ /* Migrate affinity back to this CPU */
+ lpfc_irq_set_aff(lpfc_get_eq_hdl(cpup->eq), cpu);
+ }
+}
+
+static int lpfc_cpu_offline(unsigned int cpu, struct hlist_node *node)
+{
+ struct lpfc_hba *phba = hlist_entry_safe(node, struct lpfc_hba, cpuhp);
+ struct lpfc_queue *eq, *next;
+ LIST_HEAD(eqlist);
+ int retval;
+
+ if (!phba) {
+ WARN_ONCE(!phba, "cpu: %u. phba:NULL", raw_smp_processor_id());
+ return 0;
+ }
+
+ if (__lpfc_cpuhp_checks(phba, &retval))
+ return retval;
+
+ lpfc_irq_rebalance(phba, cpu, true);
+
+ retval = lpfc_cpuhp_get_eq(phba, cpu, &eqlist);
+ if (retval)
+ return retval;
+
+ /* start polling on these eq's */
+ list_for_each_entry_safe(eq, next, &eqlist, _poll_list) {
+ list_del_init(&eq->_poll_list);
+ lpfc_sli4_start_polling(eq);
+ }
+
+ return 0;
+}
+
+static int lpfc_cpu_online(unsigned int cpu, struct hlist_node *node)
+{
+ struct lpfc_hba *phba = hlist_entry_safe(node, struct lpfc_hba, cpuhp);
+ struct lpfc_queue *eq, *next;
+ unsigned int n;
+ int retval;
+
+ if (!phba) {
+ WARN_ONCE(!phba, "cpu: %u. phba:NULL", raw_smp_processor_id());
+ return 0;
+ }
+
+ if (__lpfc_cpuhp_checks(phba, &retval))
+ return retval;
+
+ lpfc_irq_rebalance(phba, cpu, false);
+
+ list_for_each_entry_safe(eq, next, &phba->poll_list, _poll_list) {
+ n = lpfc_find_cpu_handle(phba, eq->hdwq, LPFC_FIND_BY_HDWQ);
+ if (n == cpu)
+ lpfc_sli4_stop_polling(eq);
+ }
+
+ return 0;
+}
+
+/**
* lpfc_sli4_enable_msix - Enable MSI-X interrupt mode to SLI-4 device
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to enable the MSI-X interrupt vectors to device
- * with SLI-4 interface spec. The kernel function pci_enable_msix() is called
- * to enable the MSI-X vectors. Note that pci_enable_msix(), once invoked,
- * enables either all or nothing, depending on the current availability of
- * PCI vector resources. The device driver is responsible for calling the
- * individual request_irq() to register each MSI-X vector with a interrupt
- * handler, which is done in this function. Note that later when device is
- * unloading, the driver should always call free_irq() on all MSI-X vectors
- * it has done request_irq() on before calling pci_disable_msix(). Failure
- * to do so results in a BUG_ON() and a device will be left with MSI-X
- * enabled and leaks its vectors.
+ * with SLI-4 interface spec. It also allocates MSI-X vectors and maps them
+ * to cpus on the system.
+ *
+ * When cfg_irq_numa is enabled, the adapter will only allocate vectors for
+ * the number of cpus on the same numa node as this adapter. The vectors are
+ * allocated without requesting OS affinity mapping. A vector will be
+ * allocated and assigned to each online and offline cpu. If the cpu is
+ * online, then affinity will be set to that cpu. If the cpu is offline, then
+ * affinity will be set to the nearest peer cpu within the numa node that is
+ * online. If there are no online cpus within the numa node, affinity is not
+ * assigned and the OS may do as it pleases. Note: cpu vector affinity mapping
+ * is consistent with the way cpu online/offline is handled when cfg_irq_numa is
+ * configured.
+ *
+ * If numa mode is not enabled and there is more than 1 vector allocated, then
+ * the driver relies on the managed irq interface where the OS assigns vector to
+ * cpu affinity. The driver will then use that affinity mapping to setup its
+ * cpu mapping table.
*
* Return codes
* 0 - successful
@@ -7620,108 +12973,142 @@ static int
lpfc_sli4_enable_msix(struct lpfc_hba *phba)
{
int vectors, rc, index;
+ char *name;
+ const struct cpumask *aff_mask = NULL;
+ unsigned int cpu = 0, cpu_cnt = 0, cpu_select = nr_cpu_ids;
+ struct lpfc_vector_map_info *cpup;
+ struct lpfc_hba_eq_hdl *eqhdl;
+ const struct cpumask *maskp;
+ unsigned int flags = PCI_IRQ_MSIX;
/* Set up MSI-X multi-message vectors */
- for (index = 0; index < phba->sli4_hba.cfg_eqn; index++)
- phba->sli4_hba.msix_entries[index].entry = index;
-
- /* Configure MSI-X capability structure */
- vectors = phba->sli4_hba.cfg_eqn;
-enable_msix_vectors:
- rc = pci_enable_msix(phba->pcidev, phba->sli4_hba.msix_entries,
- vectors);
- if (rc > 1) {
- vectors = rc;
- goto enable_msix_vectors;
- } else if (rc) {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0484 PCI enable MSI-X failed (%d)\n", rc);
- goto msi_fail_out;
+ vectors = phba->cfg_irq_chann;
+
+ if (phba->irq_chann_mode != NORMAL_MODE)
+ aff_mask = &phba->sli4_hba.irq_aff_mask;
+
+ if (aff_mask) {
+ cpu_cnt = cpumask_weight(aff_mask);
+ vectors = min(phba->cfg_irq_chann, cpu_cnt);
+
+ /* cpu: iterates over aff_mask including offline or online
+ * cpu_select: iterates over online aff_mask to set affinity
+ */
+ cpu = cpumask_first(aff_mask);
+ cpu_select = lpfc_next_online_cpu(aff_mask, cpu);
+ } else {
+ flags |= PCI_IRQ_AFFINITY;
}
- /* Log MSI-X vector assignment */
- for (index = 0; index < vectors; index++)
+ rc = pci_alloc_irq_vectors(phba->pcidev, 1, vectors, flags);
+ if (rc < 0) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0489 MSI-X entry[%d]: vector=x%x "
- "message=%d\n", index,
- phba->sli4_hba.msix_entries[index].vector,
- phba->sli4_hba.msix_entries[index].entry);
- /*
- * Assign MSI-X vectors to interrupt handlers
- */
- if (vectors > 1)
- rc = request_irq(phba->sli4_hba.msix_entries[0].vector,
- &lpfc_sli4_sp_intr_handler, IRQF_SHARED,
- LPFC_SP_DRIVER_HANDLER_NAME, phba);
- else
- /* All Interrupts need to be handled by one EQ */
- rc = request_irq(phba->sli4_hba.msix_entries[0].vector,
- &lpfc_sli4_intr_handler, IRQF_SHARED,
- LPFC_DRIVER_NAME, phba);
- if (rc) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "0485 MSI-X slow-path request_irq failed "
- "(%d)\n", rc);
- goto msi_fail_out;
+ "0484 PCI enable MSI-X failed (%d)\n", rc);
+ goto vec_fail_out;
}
+ vectors = rc;
+
+ /* Assign MSI-X vectors to interrupt handlers */
+ for (index = 0; index < vectors; index++) {
+ eqhdl = lpfc_get_eq_hdl(index);
+ name = eqhdl->handler_name;
+ memset(name, 0, LPFC_SLI4_HANDLER_NAME_SZ);
+ snprintf(name, LPFC_SLI4_HANDLER_NAME_SZ,
+ LPFC_DRIVER_HANDLER_NAME"%d", index);
+
+ eqhdl->idx = index;
+ rc = pci_irq_vector(phba->pcidev, index);
+ if (rc < 0) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0489 MSI-X fast-path (%d) "
+ "pci_irq_vec failed (%d)\n", index, rc);
+ goto cfg_fail_out;
+ }
+ eqhdl->irq = rc;
- /* The rest of the vector(s) are associated to fast-path handler(s) */
- for (index = 1; index < vectors; index++) {
- phba->sli4_hba.fcp_eq_hdl[index - 1].idx = index - 1;
- phba->sli4_hba.fcp_eq_hdl[index - 1].phba = phba;
- rc = request_irq(phba->sli4_hba.msix_entries[index].vector,
- &lpfc_sli4_fp_intr_handler, IRQF_SHARED,
- LPFC_FP_DRIVER_HANDLER_NAME,
- &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+ rc = request_threaded_irq(eqhdl->irq,
+ &lpfc_sli4_hba_intr_handler,
+ &lpfc_sli4_hba_intr_handler_th,
+ 0, name, eqhdl);
if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0486 MSI-X fast-path (%d) "
"request_irq failed (%d)\n", index, rc);
goto cfg_fail_out;
}
+
+ if (aff_mask) {
+ /* If found a neighboring online cpu, set affinity */
+ if (cpu_select < nr_cpu_ids)
+ lpfc_irq_set_aff(eqhdl, cpu_select);
+
+ /* Assign EQ to cpu_map */
+ lpfc_assign_eq_map_info(phba, index,
+ LPFC_CPU_FIRST_IRQ,
+ cpu);
+
+ /* Iterate to next offline or online cpu in aff_mask */
+ cpu = cpumask_next(cpu, aff_mask);
+
+ /* Find next online cpu in aff_mask to set affinity */
+ cpu_select = lpfc_next_online_cpu(aff_mask, cpu);
+ } else if (vectors == 1) {
+ cpu = cpumask_first(cpu_present_mask);
+ lpfc_assign_eq_map_info(phba, index, LPFC_CPU_FIRST_IRQ,
+ cpu);
+ } else {
+ maskp = pci_irq_get_affinity(phba->pcidev, index);
+
+ /* Loop through all CPUs associated with vector index */
+ for_each_cpu_and(cpu, maskp, cpu_present_mask) {
+ cpup = &phba->sli4_hba.cpu_map[cpu];
+
+ /* If this is the first CPU thats assigned to
+ * this vector, set LPFC_CPU_FIRST_IRQ.
+ *
+ * With certain platforms its possible that irq
+ * vectors are affinitized to all the cpu's.
+ * This can result in each cpu_map.eq to be set
+ * to the last vector, resulting in overwrite
+ * of all the previous cpu_map.eq. Ensure that
+ * each vector receives a place in cpu_map.
+ * Later call to lpfc_cpu_affinity_check will
+ * ensure we are nicely balanced out.
+ */
+ if (cpup->eq != LPFC_VECTOR_MAP_EMPTY)
+ continue;
+ lpfc_assign_eq_map_info(phba, index,
+ LPFC_CPU_FIRST_IRQ,
+ cpu);
+ break;
+ }
+ }
+ }
+
+ if (vectors != phba->cfg_irq_chann) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3238 Reducing IO channels to match number of "
+ "MSI-X vectors, requested %d got %d\n",
+ phba->cfg_irq_chann, vectors);
+ if (phba->cfg_irq_chann > vectors)
+ phba->cfg_irq_chann = vectors;
}
- phba->sli4_hba.msix_vec_nr = vectors;
return rc;
cfg_fail_out:
/* free the irq already requested */
- for (--index; index >= 1; index--)
- free_irq(phba->sli4_hba.msix_entries[index - 1].vector,
- &phba->sli4_hba.fcp_eq_hdl[index - 1]);
-
- /* free the irq already requested */
- free_irq(phba->sli4_hba.msix_entries[0].vector, phba);
+ for (--index; index >= 0; index--) {
+ eqhdl = lpfc_get_eq_hdl(index);
+ lpfc_irq_clear_aff(eqhdl);
+ free_irq(eqhdl->irq, eqhdl);
+ }
-msi_fail_out:
/* Unconfigure MSI-X capability structure */
- pci_disable_msix(phba->pcidev);
- return rc;
-}
-
-/**
- * lpfc_sli4_disable_msix - Disable MSI-X interrupt mode to SLI-4 device
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to release the MSI-X vectors and then disable the
- * MSI-X interrupt mode to device with SLI-4 interface spec.
- **/
-static void
-lpfc_sli4_disable_msix(struct lpfc_hba *phba)
-{
- int index;
-
- /* Free up MSI-X multi-message vectors */
- free_irq(phba->sli4_hba.msix_entries[0].vector, phba);
-
- for (index = 1; index < phba->sli4_hba.msix_vec_nr; index++)
- free_irq(phba->sli4_hba.msix_entries[index].vector,
- &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+ pci_free_irq_vectors(phba->pcidev);
- /* Disable MSI-X */
- pci_disable_msix(phba->pcidev);
-
- return;
+vec_fail_out:
+ return rc;
}
/**
@@ -7729,10 +13116,10 @@ lpfc_sli4_disable_msix(struct lpfc_hba *phba)
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to enable the MSI interrupt mode to device with
- * SLI-4 interface spec. The kernel function pci_enable_msi() is called
- * to enable the MSI vector. The device driver is responsible for calling
- * the request_irq() to register MSI vector with a interrupt the handler,
- * which is done in this function.
+ * SLI-4 interface spec. The kernel function pci_alloc_irq_vectors() is
+ * called to enable the MSI vector. The device driver is responsible for
+ * calling the request_irq() to register MSI vector with a interrupt the
+ * handler, which is done in this function.
*
* Return codes
* 0 - successful
@@ -7742,55 +13129,55 @@ static int
lpfc_sli4_enable_msi(struct lpfc_hba *phba)
{
int rc, index;
+ unsigned int cpu;
+ struct lpfc_hba_eq_hdl *eqhdl;
- rc = pci_enable_msi(phba->pcidev);
- if (!rc)
+ rc = pci_alloc_irq_vectors(phba->pcidev, 1, 1,
+ PCI_IRQ_MSI | PCI_IRQ_AFFINITY);
+ if (rc > 0)
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0487 PCI enable MSI mode success.\n");
else {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0488 PCI enable MSI mode failed (%d)\n", rc);
- return rc;
+ return rc ? rc : -1;
}
rc = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ 0, LPFC_DRIVER_NAME, phba);
if (rc) {
- pci_disable_msi(phba->pcidev);
+ pci_free_irq_vectors(phba->pcidev);
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0490 MSI request_irq failed (%d)\n", rc);
return rc;
}
- for (index = 0; index < phba->cfg_fcp_eq_count; index++) {
- phba->sli4_hba.fcp_eq_hdl[index].idx = index;
- phba->sli4_hba.fcp_eq_hdl[index].phba = phba;
+ eqhdl = lpfc_get_eq_hdl(0);
+ rc = pci_irq_vector(phba->pcidev, 0);
+ if (rc < 0) {
+ free_irq(phba->pcidev->irq, phba);
+ pci_free_irq_vectors(phba->pcidev);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0496 MSI pci_irq_vec failed (%d)\n", rc);
+ return rc;
}
+ eqhdl->irq = rc;
- return 0;
-}
+ cpu = cpumask_first(cpu_present_mask);
+ lpfc_assign_eq_map_info(phba, 0, LPFC_CPU_FIRST_IRQ, cpu);
-/**
- * lpfc_sli4_disable_msi - Disable MSI interrupt mode to SLI-4 device
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to disable the MSI interrupt mode to device with
- * SLI-4 interface spec. The driver calls free_irq() on MSI vector it has
- * done request_irq() on before calling pci_disable_msi(). Failure to do so
- * results in a BUG_ON() and a device will be left with MSI enabled and leaks
- * its vector.
- **/
-static void
-lpfc_sli4_disable_msi(struct lpfc_hba *phba)
-{
- free_irq(phba->pcidev->irq, phba);
- pci_disable_msi(phba->pcidev);
- return;
+ for (index = 0; index < phba->cfg_irq_chann; index++) {
+ eqhdl = lpfc_get_eq_hdl(index);
+ eqhdl->idx = index;
+ }
+
+ return 0;
}
/**
* lpfc_sli4_enable_intr - Enable device interrupt to SLI-4 device
* @phba: pointer to lpfc hba data structure.
+ * @cfg_mode: Interrupt configuration mode (INTx, MSI or MSI-X).
*
* This routine is invoked to enable device interrupt and associate driver's
* interrupt handler(s) to interrupt vector(s) to device with SLI-4
@@ -7801,14 +13188,14 @@ lpfc_sli4_disable_msi(struct lpfc_hba *phba)
* MSI-X -> MSI -> IRQ.
*
* Return codes
- * 0 - successful
- * other values - error
+ * Interrupt mode (2, 1, 0) - successful
+ * LPFC_INTR_ERROR - error
**/
static uint32_t
lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
{
uint32_t intr_mode = LPFC_INTR_ERROR;
- int retval, index;
+ int retval, idx;
if (cfg_mode == 2) {
/* Preparation before conf_msi mbox cmd */
@@ -7839,13 +13226,30 @@ lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
retval = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
IRQF_SHARED, LPFC_DRIVER_NAME, phba);
if (!retval) {
+ struct lpfc_hba_eq_hdl *eqhdl;
+ unsigned int cpu;
+
/* Indicate initialization to INTx mode */
phba->intr_type = INTx;
intr_mode = 0;
- for (index = 0; index < phba->cfg_fcp_eq_count;
- index++) {
- phba->sli4_hba.fcp_eq_hdl[index].idx = index;
- phba->sli4_hba.fcp_eq_hdl[index].phba = phba;
+
+ eqhdl = lpfc_get_eq_hdl(0);
+ retval = pci_irq_vector(phba->pcidev, 0);
+ if (retval < 0) {
+ free_irq(phba->pcidev->irq, phba);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0502 INTR pci_irq_vec failed (%d)\n",
+ retval);
+ return LPFC_INTR_ERROR;
+ }
+ eqhdl->irq = retval;
+
+ cpu = cpumask_first(cpu_present_mask);
+ lpfc_assign_eq_map_info(phba, 0, LPFC_CPU_FIRST_IRQ,
+ cpu);
+ for (idx = 0; idx < phba->cfg_irq_chann; idx++) {
+ eqhdl = lpfc_get_eq_hdl(idx);
+ eqhdl->idx = idx;
}
}
}
@@ -7865,18 +13269,25 @@ static void
lpfc_sli4_disable_intr(struct lpfc_hba *phba)
{
/* Disable the currently initialized interrupt mode */
- if (phba->intr_type == MSIX)
- lpfc_sli4_disable_msix(phba);
- else if (phba->intr_type == MSI)
- lpfc_sli4_disable_msi(phba);
- else if (phba->intr_type == INTx)
+ if (phba->intr_type == MSIX) {
+ int index;
+ struct lpfc_hba_eq_hdl *eqhdl;
+
+ /* Free up MSI-X multi-message vectors */
+ for (index = 0; index < phba->cfg_irq_chann; index++) {
+ eqhdl = lpfc_get_eq_hdl(index);
+ lpfc_irq_clear_aff(eqhdl);
+ free_irq(eqhdl->irq, eqhdl);
+ }
+ } else {
free_irq(phba->pcidev->irq, phba);
+ }
+
+ pci_free_irq_vectors(phba->pcidev);
/* Reset interrupt management states */
phba->intr_type = NONE;
phba->sli.slistat.sli_intr = 0;
-
- return;
}
/**
@@ -7889,12 +13300,10 @@ lpfc_sli4_disable_intr(struct lpfc_hba *phba)
static void
lpfc_unset_hba(struct lpfc_hba *phba)
{
- struct lpfc_vport *vport = phba->pport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ set_bit(FC_UNLOADING, &phba->pport->load_flag);
- spin_lock_irq(shost->host_lock);
- vport->load_flag |= FC_UNLOADING;
- spin_unlock_irq(shost->host_lock);
+ kfree(phba->vpi_bmask);
+ kfree(phba->vpi_ids);
lpfc_stop_hba_timers(phba);
@@ -7910,36 +13319,6 @@ lpfc_unset_hba(struct lpfc_hba *phba)
}
/**
- * lpfc_sli4_unset_hba - Unset SLI4 hba device initialization.
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to unset the HBA device initialization steps to
- * a device with SLI-4 interface spec.
- **/
-static void
-lpfc_sli4_unset_hba(struct lpfc_hba *phba)
-{
- struct lpfc_vport *vport = phba->pport;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- spin_lock_irq(shost->host_lock);
- vport->load_flag |= FC_UNLOADING;
- spin_unlock_irq(shost->host_lock);
-
- phba->pport->work_port_events = 0;
-
- /* Stop the SLI4 device port */
- lpfc_stop_port(phba);
-
- lpfc_sli4_disable_intr(phba);
-
- /* Reset SLI4 HBA FCoE function */
- lpfc_pci_function_reset(phba);
-
- return;
-}
-
-/**
* lpfc_sli4_xri_exchange_busy_wait - Wait for device XRI exchange busy
* @phba: Pointer to HBA context object.
*
@@ -7955,19 +13334,52 @@ lpfc_sli4_unset_hba(struct lpfc_hba *phba)
static void
lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba)
{
+ struct lpfc_sli4_hdw_queue *qp;
+ int idx, ccnt;
int wait_time = 0;
- int fcp_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list);
+ int io_xri_cmpl = 1;
+ int nvmet_xri_cmpl = 1;
int els_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list);
- while (!fcp_xri_cmpl || !els_xri_cmpl) {
+ /* Driver just aborted IOs during the hba_unset process. Pause
+ * here to give the HBA time to complete the IO and get entries
+ * into the abts lists.
+ */
+ msleep(LPFC_XRI_EXCH_BUSY_WAIT_T1 * 5);
+
+ /* Wait for NVME pending IO to flush back to transport. */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ lpfc_nvme_wait_for_io_drain(phba);
+
+ ccnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ io_xri_cmpl = list_empty(&qp->lpfc_abts_io_buf_list);
+ if (!io_xri_cmpl) /* if list is NOT empty */
+ ccnt++;
+ }
+ if (ccnt)
+ io_xri_cmpl = 0;
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ nvmet_xri_cmpl =
+ list_empty(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
+ }
+
+ while (!els_xri_cmpl || !io_xri_cmpl || !nvmet_xri_cmpl) {
if (wait_time > LPFC_XRI_EXCH_BUSY_WAIT_TMO) {
- if (!fcp_xri_cmpl)
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2877 FCP XRI exchange busy "
+ if (!nvmet_xri_cmpl)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6424 NVMET XRI exchange busy "
+ "wait time: %d seconds.\n",
+ wait_time/1000);
+ if (!io_xri_cmpl)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6100 IO XRI exchange busy "
"wait time: %d seconds.\n",
wait_time/1000);
if (!els_xri_cmpl)
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2878 ELS XRI exchange busy "
"wait time: %d seconds.\n",
wait_time/1000);
@@ -7977,10 +13389,25 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba)
msleep(LPFC_XRI_EXCH_BUSY_WAIT_T1);
wait_time += LPFC_XRI_EXCH_BUSY_WAIT_T1;
}
- fcp_xri_cmpl =
- list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list);
+
+ ccnt = 0;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ io_xri_cmpl = list_empty(
+ &qp->lpfc_abts_io_buf_list);
+ if (!io_xri_cmpl) /* if list is NOT empty */
+ ccnt++;
+ }
+ if (ccnt)
+ io_xri_cmpl = 0;
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ nvmet_xri_cmpl = list_empty(
+ &phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
+ }
els_xri_cmpl =
list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list);
+
}
}
@@ -8002,7 +13429,11 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba)
struct pci_dev *pdev = phba->pcidev;
lpfc_stop_hba_timers(phba);
- phba->sli4_hba.intr_enable = 0;
+ hrtimer_cancel(&phba->cmf_stats_timer);
+ hrtimer_cancel(&phba->cmf_timer);
+
+ if (phba->pport)
+ phba->sli4_hba.intr_enable = 0;
/*
* Gracefully wait out the potential current outstanding asynchronous
@@ -8033,8 +13464,13 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba)
/* Abort all iocbs associated with the hba */
lpfc_sli_hba_iocb_abort(phba);
- /* Wait for completion of device XRI exchange busy */
- lpfc_sli4_xri_exchange_busy_wait(phba);
+ if (!pci_channel_offline(phba->pcidev))
+ /* Wait for completion of device XRI exchange busy */
+ lpfc_sli4_xri_exchange_busy_wait(phba);
+
+ /* per-phba callback de-registration for hotplug event */
+ if (phba->pport)
+ lpfc_cpuhp_remove(phba);
/* Disable PCI subsystem interrupt */
lpfc_sli4_disable_intr(phba);
@@ -8046,83 +13482,221 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba)
/* Stop kthread signal shall trigger work_done one more time */
kthread_stop(phba->worker_thread);
+ /* Disable FW logging to host memory */
+ lpfc_ras_stop_fwlog(phba);
+
+ lpfc_sli4_queue_unset(phba);
+
/* Reset SLI4 HBA FCoE function */
lpfc_pci_function_reset(phba);
+ /* release all queue allocated resources. */
+ lpfc_sli4_queue_destroy(phba);
+
+ /* Free RAS DMA memory */
+ if (phba->ras_fwlog.ras_enabled)
+ lpfc_sli4_ras_dma_free(phba);
+
/* Stop the SLI4 device port */
- phba->pport->work_port_events = 0;
+ if (phba->pport)
+ phba->pport->work_port_events = 0;
}
- /**
- * lpfc_pc_sli4_params_get - Get the SLI4_PARAMS port capabilities.
- * @phba: Pointer to HBA context object.
- * @mboxq: Pointer to the mailboxq memory for the mailbox command response.
- *
- * This function is called in the SLI4 code path to read the port's
- * sli4 capabilities.
- *
- * This function may be be called from any context that can block-wait
- * for the completion. The expectation is that this routine is called
- * typically from probe_one or from the online routine.
- **/
-int
-lpfc_pc_sli4_params_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+static uint32_t
+lpfc_cgn_crc32(uint32_t crc, u8 byte)
{
- int rc;
- struct lpfc_mqe *mqe;
- struct lpfc_pc_sli4_params *sli4_params;
- uint32_t mbox_tmo;
+ uint32_t msb = 0;
+ uint32_t bit;
- rc = 0;
- mqe = &mboxq->u.mqe;
+ for (bit = 0; bit < 8; bit++) {
+ msb = (crc >> 31) & 1;
+ crc <<= 1;
- /* Read the port's SLI4 Parameters port capabilities */
- lpfc_pc_sli4_params(mboxq);
- if (!phba->sli4_hba.intr_enable)
- rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
- else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_PORT_CAPABILITIES);
- rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+ if (msb ^ (byte & 1)) {
+ crc ^= LPFC_CGN_CRC32_MAGIC_NUMBER;
+ crc |= 1;
+ }
+ byte >>= 1;
}
+ return crc;
+}
- if (unlikely(rc))
- return 1;
+static uint32_t
+lpfc_cgn_reverse_bits(uint32_t wd)
+{
+ uint32_t result = 0;
+ uint32_t i;
- sli4_params = &phba->sli4_hba.pc_sli4_params;
- sli4_params->if_type = bf_get(if_type, &mqe->un.sli4_params);
- sli4_params->sli_rev = bf_get(sli_rev, &mqe->un.sli4_params);
- sli4_params->sli_family = bf_get(sli_family, &mqe->un.sli4_params);
- sli4_params->featurelevel_1 = bf_get(featurelevel_1,
- &mqe->un.sli4_params);
- sli4_params->featurelevel_2 = bf_get(featurelevel_2,
- &mqe->un.sli4_params);
- sli4_params->proto_types = mqe->un.sli4_params.word3;
- sli4_params->sge_supp_len = mqe->un.sli4_params.sge_supp_len;
- sli4_params->if_page_sz = bf_get(if_page_sz, &mqe->un.sli4_params);
- sli4_params->rq_db_window = bf_get(rq_db_window, &mqe->un.sli4_params);
- sli4_params->loopbk_scope = bf_get(loopbk_scope, &mqe->un.sli4_params);
- sli4_params->eq_pages_max = bf_get(eq_pages, &mqe->un.sli4_params);
- sli4_params->eqe_size = bf_get(eqe_size, &mqe->un.sli4_params);
- sli4_params->cq_pages_max = bf_get(cq_pages, &mqe->un.sli4_params);
- sli4_params->cqe_size = bf_get(cqe_size, &mqe->un.sli4_params);
- sli4_params->mq_pages_max = bf_get(mq_pages, &mqe->un.sli4_params);
- sli4_params->mqe_size = bf_get(mqe_size, &mqe->un.sli4_params);
- sli4_params->mq_elem_cnt = bf_get(mq_elem_cnt, &mqe->un.sli4_params);
- sli4_params->wq_pages_max = bf_get(wq_pages, &mqe->un.sli4_params);
- sli4_params->wqe_size = bf_get(wqe_size, &mqe->un.sli4_params);
- sli4_params->rq_pages_max = bf_get(rq_pages, &mqe->un.sli4_params);
- sli4_params->rqe_size = bf_get(rqe_size, &mqe->un.sli4_params);
- sli4_params->hdr_pages_max = bf_get(hdr_pages, &mqe->un.sli4_params);
- sli4_params->hdr_size = bf_get(hdr_size, &mqe->un.sli4_params);
- sli4_params->hdr_pp_align = bf_get(hdr_pp_align, &mqe->un.sli4_params);
- sli4_params->sgl_pages_max = bf_get(sgl_pages, &mqe->un.sli4_params);
- sli4_params->sgl_pp_align = bf_get(sgl_pp_align, &mqe->un.sli4_params);
+ for (i = 0; i < 32; i++) {
+ result <<= 1;
+ result |= (1 & (wd >> i));
+ }
+ return result;
+}
- /* Make sure that sge_supp_len can be handled by the driver */
- if (sli4_params->sge_supp_len > LPFC_MAX_SGE_SIZE)
- sli4_params->sge_supp_len = LPFC_MAX_SGE_SIZE;
+/*
+ * The routine corresponds with the algorithm the HBA firmware
+ * uses to validate the data integrity.
+ */
+uint32_t
+lpfc_cgn_calc_crc32(void *ptr, uint32_t byteLen, uint32_t crc)
+{
+ uint32_t i;
+ uint32_t result;
+ uint8_t *data = (uint8_t *)ptr;
- return rc;
+ for (i = 0; i < byteLen; ++i)
+ crc = lpfc_cgn_crc32(crc, data[i]);
+
+ result = ~lpfc_cgn_reverse_bits(crc);
+ return result;
+}
+
+void
+lpfc_init_congestion_buf(struct lpfc_hba *phba)
+{
+ struct lpfc_cgn_info *cp;
+ uint16_t size;
+ uint32_t crc;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6235 INIT Congestion Buffer %p\n", phba->cgn_i);
+
+ if (!phba->cgn_i)
+ return;
+ cp = (struct lpfc_cgn_info *)phba->cgn_i->virt;
+
+ atomic_set(&phba->cgn_fabric_warn_cnt, 0);
+ atomic_set(&phba->cgn_fabric_alarm_cnt, 0);
+ atomic_set(&phba->cgn_sync_alarm_cnt, 0);
+ atomic_set(&phba->cgn_sync_warn_cnt, 0);
+
+ atomic_set(&phba->cgn_driver_evt_cnt, 0);
+ atomic_set(&phba->cgn_latency_evt_cnt, 0);
+ atomic64_set(&phba->cgn_latency_evt, 0);
+ phba->cgn_evt_minute = 0;
+
+ memset(cp, 0xff, offsetof(struct lpfc_cgn_info, cgn_stat));
+ cp->cgn_info_size = cpu_to_le16(LPFC_CGN_INFO_SZ);
+ cp->cgn_info_version = LPFC_CGN_INFO_V4;
+
+ /* cgn parameters */
+ cp->cgn_info_mode = phba->cgn_p.cgn_param_mode;
+ cp->cgn_info_level0 = phba->cgn_p.cgn_param_level0;
+ cp->cgn_info_level1 = phba->cgn_p.cgn_param_level1;
+ cp->cgn_info_level2 = phba->cgn_p.cgn_param_level2;
+
+ lpfc_cgn_update_tstamp(phba, &cp->base_time);
+
+ /* Fill in default LUN qdepth */
+ if (phba->pport) {
+ size = (uint16_t)(phba->pport->cfg_lun_queue_depth);
+ cp->cgn_lunq = cpu_to_le16(size);
+ }
+
+ /* last used Index initialized to 0xff already */
+
+ cp->cgn_warn_freq = cpu_to_le16(LPFC_FPIN_INIT_FREQ);
+ cp->cgn_alarm_freq = cpu_to_le16(LPFC_FPIN_INIT_FREQ);
+ crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ, LPFC_CGN_CRC32_SEED);
+ cp->cgn_info_crc = cpu_to_le32(crc);
+
+ phba->cgn_evt_timestamp = jiffies +
+ msecs_to_jiffies(LPFC_CGN_TIMER_TO_MIN);
+}
+
+void
+lpfc_init_congestion_stat(struct lpfc_hba *phba)
+{
+ struct lpfc_cgn_info *cp;
+ uint32_t crc;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6236 INIT Congestion Stat %p\n", phba->cgn_i);
+
+ if (!phba->cgn_i)
+ return;
+
+ cp = (struct lpfc_cgn_info *)phba->cgn_i->virt;
+ memset(&cp->cgn_stat, 0, sizeof(cp->cgn_stat));
+
+ lpfc_cgn_update_tstamp(phba, &cp->stat_start);
+ crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ, LPFC_CGN_CRC32_SEED);
+ cp->cgn_info_crc = cpu_to_le32(crc);
+}
+
+/**
+ * __lpfc_reg_congestion_buf - register congestion info buffer with HBA
+ * @phba: Pointer to hba context object.
+ * @reg: flag to determine register or unregister.
+ */
+static int
+__lpfc_reg_congestion_buf(struct lpfc_hba *phba, int reg)
+{
+ struct lpfc_mbx_reg_congestion_buf *reg_congestion_buf;
+ union lpfc_sli4_cfg_shdr *shdr;
+ uint32_t shdr_status, shdr_add_status;
+ LPFC_MBOXQ_t *mboxq;
+ int length, rc;
+
+ if (!phba->cgn_i)
+ return -ENXIO;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2641 REG_CONGESTION_BUF mbox allocation fail: "
+ "HBA state x%x reg %d\n",
+ phba->pport->port_state, reg);
+ return -ENOMEM;
+ }
+
+ length = (sizeof(struct lpfc_mbx_reg_congestion_buf) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_REG_CONGESTION_BUF, length,
+ LPFC_SLI4_MBX_EMBED);
+ reg_congestion_buf = &mboxq->u.mqe.un.reg_congestion_buf;
+ bf_set(lpfc_mbx_reg_cgn_buf_type, reg_congestion_buf, 1);
+ if (reg > 0)
+ bf_set(lpfc_mbx_reg_cgn_buf_cnt, reg_congestion_buf, 1);
+ else
+ bf_set(lpfc_mbx_reg_cgn_buf_cnt, reg_congestion_buf, 0);
+ reg_congestion_buf->length = sizeof(struct lpfc_cgn_info);
+ reg_congestion_buf->addr_lo =
+ putPaddrLow(phba->cgn_i->phys);
+ reg_congestion_buf->addr_hi =
+ putPaddrHigh(phba->cgn_i->phys);
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &shdr->response);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2642 REG_CONGESTION_BUF mailbox "
+ "failed with status x%x add_status x%x,"
+ " mbx status x%x reg %d\n",
+ shdr_status, shdr_add_status, rc, reg);
+ return -ENXIO;
+ }
+ return 0;
+}
+
+int
+lpfc_unreg_congestion_buf(struct lpfc_hba *phba)
+{
+ lpfc_cmf_stop(phba);
+ return __lpfc_reg_congestion_buf(phba, 0);
+}
+
+int
+lpfc_reg_congestion_buf(struct lpfc_hba *phba)
+{
+ return __lpfc_reg_congestion_buf(phba, 1);
}
/**
@@ -8143,7 +13717,9 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
int rc;
struct lpfc_mqe *mqe = &mboxq->u.mqe;
struct lpfc_pc_sli4_params *sli4_params;
+ uint32_t mbox_tmo;
int length;
+ bool exp_wqcq_pages = true;
struct lpfc_sli4_parameters *mbx_sli4_parameters;
/*
@@ -8161,9 +13737,10 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
length, LPFC_SLI4_MBX_EMBED);
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
- else
- rc = lpfc_sli_issue_mbox_wait(phba, mboxq,
- lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG));
+ else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mboxq);
+ rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+ }
if (unlikely(rc))
return rc;
sli4_params = &phba->sli4_hba.pc_sli4_params;
@@ -8180,22 +13757,149 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
else
phba->sli3_options &= ~LPFC_SLI4_PHWQ_ENABLED;
sli4_params->sge_supp_len = mbx_sli4_parameters->sge_supp_len;
- sli4_params->loopbk_scope = bf_get(loopbk_scope, mbx_sli4_parameters);
+ sli4_params->loopbk_scope = bf_get(cfg_loopbk_scope,
+ mbx_sli4_parameters);
+ sli4_params->oas_supported = bf_get(cfg_oas, mbx_sli4_parameters);
sli4_params->cqv = bf_get(cfg_cqv, mbx_sli4_parameters);
sli4_params->mqv = bf_get(cfg_mqv, mbx_sli4_parameters);
sli4_params->wqv = bf_get(cfg_wqv, mbx_sli4_parameters);
sli4_params->rqv = bf_get(cfg_rqv, mbx_sli4_parameters);
+ sli4_params->eqav = bf_get(cfg_eqav, mbx_sli4_parameters);
+ sli4_params->cqav = bf_get(cfg_cqav, mbx_sli4_parameters);
+ sli4_params->wqsize = bf_get(cfg_wqsize, mbx_sli4_parameters);
+ sli4_params->bv1s = bf_get(cfg_bv1s, mbx_sli4_parameters);
+ sli4_params->pls = bf_get(cfg_pvl, mbx_sli4_parameters);
sli4_params->sgl_pages_max = bf_get(cfg_sgl_page_cnt,
mbx_sli4_parameters);
+ sli4_params->wqpcnt = bf_get(cfg_wqpcnt, mbx_sli4_parameters);
sli4_params->sgl_pp_align = bf_get(cfg_sgl_pp_align,
mbx_sli4_parameters);
phba->sli4_hba.extents_in_use = bf_get(cfg_ext, mbx_sli4_parameters);
phba->sli4_hba.rpi_hdrs_in_use = bf_get(cfg_hdrr, mbx_sli4_parameters);
+ sli4_params->mi_cap = bf_get(cfg_mi_ver, mbx_sli4_parameters);
+
+ /* Check for Extended Pre-Registered SGL support */
+ phba->cfg_xpsgl = bf_get(cfg_xpsgl, mbx_sli4_parameters);
+
+ /* Check for firmware nvme support */
+ rc = (bf_get(cfg_nvme, mbx_sli4_parameters) &&
+ bf_get(cfg_xib, mbx_sli4_parameters));
+
+ if (rc) {
+ /* Save this to indicate the Firmware supports NVME */
+ sli4_params->nvme = 1;
+
+ /* Firmware NVME support, check driver FC4 NVME support */
+ if (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_NVME,
+ "6133 Disabling NVME support: "
+ "FC4 type not supported: x%x\n",
+ phba->cfg_enable_fc4_type);
+ goto fcponly;
+ }
+ } else {
+ /* No firmware NVME support, check driver FC4 NVME support */
+ sli4_params->nvme = 0;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_NVME,
+ "6101 Disabling NVME support: Not "
+ "supported by firmware (%d %d) x%x\n",
+ bf_get(cfg_nvme, mbx_sli4_parameters),
+ bf_get(cfg_xib, mbx_sli4_parameters),
+ phba->cfg_enable_fc4_type);
+fcponly:
+ phba->nvmet_support = 0;
+ phba->cfg_nvmet_mrq = 0;
+ phba->cfg_nvme_seg_cnt = 0;
+
+ /* If no FC4 type support, move to just SCSI support */
+ if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
+ return -ENODEV;
+ phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP;
+ }
+ }
+
+ /* If the NVME FC4 type is enabled, scale the sg_seg_cnt to
+ * accommodate 512K and 1M IOs in a single nvme buf.
+ */
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)
+ phba->cfg_sg_seg_cnt = LPFC_MAX_NVME_SEG_CNT;
+
+ /* Enable embedded Payload BDE if support is indicated */
+ if (bf_get(cfg_pbde, mbx_sli4_parameters))
+ phba->cfg_enable_pbde = 1;
+ else
+ phba->cfg_enable_pbde = 0;
+
+ /*
+ * To support Suppress Response feature we must satisfy 3 conditions.
+ * lpfc_suppress_rsp module parameter must be set (default).
+ * In SLI4-Parameters Descriptor:
+ * Extended Inline Buffers (XIB) must be supported.
+ * Suppress Response IU Not Supported (SRIUNS) must NOT be supported
+ * (double negative).
+ */
+ if (phba->cfg_suppress_rsp && bf_get(cfg_xib, mbx_sli4_parameters) &&
+ !(bf_get(cfg_nosr, mbx_sli4_parameters)))
+ phba->sli.sli_flag |= LPFC_SLI_SUPPRESS_RSP;
+ else
+ phba->cfg_suppress_rsp = 0;
+
+ if (bf_get(cfg_eqdr, mbx_sli4_parameters))
+ phba->sli.sli_flag |= LPFC_SLI_USE_EQDR;
/* Make sure that sge_supp_len can be handled by the driver */
if (sli4_params->sge_supp_len > LPFC_MAX_SGE_SIZE)
sli4_params->sge_supp_len = LPFC_MAX_SGE_SIZE;
+ dma_set_max_seg_size(&phba->pcidev->dev, sli4_params->sge_supp_len);
+
+ /*
+ * Check whether the adapter supports an embedded copy of the
+ * FCP CMD IU within the WQE for FCP_Ixxx commands. In order
+ * to use this option, 128-byte WQEs must be used.
+ */
+ if (bf_get(cfg_ext_embed_cb, mbx_sli4_parameters))
+ phba->fcp_embed_io = 1;
+ else
+ phba->fcp_embed_io = 0;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_NVME,
+ "6422 XIB %d PBDE %d: FCP %d NVME %d %d %d\n",
+ bf_get(cfg_xib, mbx_sli4_parameters),
+ phba->cfg_enable_pbde,
+ phba->fcp_embed_io, sli4_params->nvme,
+ phba->cfg_nvme_embed_cmd, phba->cfg_suppress_rsp);
+
+ if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_2) &&
+ (bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_FAMILY_LNCR_A0))
+ exp_wqcq_pages = false;
+
+ if ((bf_get(cfg_cqpsize, mbx_sli4_parameters) & LPFC_CQ_16K_PAGE_SZ) &&
+ (bf_get(cfg_wqpsize, mbx_sli4_parameters) & LPFC_WQ_16K_PAGE_SZ) &&
+ exp_wqcq_pages &&
+ (sli4_params->wqsize & LPFC_WQ_SZ128_SUPPORT))
+ phba->enab_exp_wqcq_pages = 1;
+ else
+ phba->enab_exp_wqcq_pages = 0;
+ /*
+ * Check if the SLI port supports MDS Diagnostics
+ */
+ if (bf_get(cfg_mds_diags, mbx_sli4_parameters))
+ phba->mds_diags_support = 1;
+ else
+ phba->mds_diags_support = 0;
+
+ /*
+ * Check if the SLI port supports NSLER
+ */
+ if (bf_get(cfg_nsler, mbx_sli4_parameters))
+ phba->nsler = 1;
+ else
+ phba->nsler = 0;
+
return 0;
}
@@ -8216,7 +13920,7 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
* 0 - driver can claim the device
* negative value - driver can not claim the device
**/
-static int __devinit
+static int
lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct lpfc_hba *phba;
@@ -8232,11 +13936,8 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
/* Perform generic PCI device enabling operation */
error = lpfc_enable_pci_dev(phba);
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1401 Failed to enable pci device.\n");
+ if (error)
goto out_free_phba;
- }
/* Set up SLI API function jump table for PCI-device group-0 HBAs */
error = lpfc_api_table_setup(phba, LPFC_PCI_DEV_LP);
@@ -8251,14 +13952,6 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_disable_pci_dev;
}
- /* Set up phase-1 common device driver resources */
- error = lpfc_setup_driver_resource_phase1(phba);
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1403 Failed to set up driver resource.\n");
- goto out_unset_pci_mem_s3;
- }
-
/* Set up SLI-3 specific device driver resources */
error = lpfc_sli_driver_resource_setup(phba);
if (error) {
@@ -8268,6 +13961,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
}
/* Initialize and populate the iocb list per host */
+
error = lpfc_init_iocb_list(phba, LPFC_IOCB_LIST_CNT);
if (error) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -8283,6 +13977,9 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_free_iocb_list;
}
+ /* Get the default values for Model Name and Description */
+ lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
+
/* Create SCSI host to the physical port */
error = lpfc_create_shost(phba);
if (error) {
@@ -8309,14 +14006,14 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
/* Configure and enable interrupt */
intr_mode = lpfc_sli_enable_intr(phba, cfg_mode);
if (intr_mode == LPFC_INTR_ERROR) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0431 Failed to enable interrupt.\n");
error = -ENODEV;
goto out_free_sysfs_attr;
}
/* SLI-3 HBA setup */
if (lpfc_sli_hba_setup(phba)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1477 Failed to set up hba\n");
error = -ENODEV;
goto out_remove_device;
@@ -8383,7 +14080,7 @@ out_free_phba:
* removed from PCI bus, it performs all the necessary cleanup for the HBA
* device to be removed from the PCI subsystem properly.
**/
-static void __devexit
+static void
lpfc_pci_remove_one_s3(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
@@ -8391,24 +14088,26 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
struct lpfc_vport **vports;
struct lpfc_hba *phba = vport->phba;
int i;
- int bars = pci_select_bars(pdev, IORESOURCE_MEM);
- spin_lock_irq(&phba->hbalock);
- vport->load_flag |= FC_UNLOADING;
- spin_unlock_irq(&phba->hbalock);
+ set_bit(FC_UNLOADING, &vport->load_flag);
lpfc_free_sysfs_attr(vport);
/* Release all the vports against this physical port */
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for (i = 1; i <= phba->max_vports && vports[i] != NULL; i++)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ if (vports[i]->port_type == LPFC_PHYSICAL_PORT)
+ continue;
fc_vport_terminate(vports[i]->fc_vport);
+ }
lpfc_destroy_vport_work_array(phba, vports);
- /* Remove FC host and then SCSI host with the physical port */
+ /* Remove FC host with the physical port */
fc_remove_host(shost);
scsi_remove_host(shost);
+
+ /* Clean up all nodes, mailboxes and IOs. */
lpfc_cleanup(vport);
/*
@@ -8424,10 +14123,13 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
/* Final cleanup of txcmplq and reset the HBA */
lpfc_sli_brdrestart(phba);
+ kfree(phba->vpi_bmask);
+ kfree(phba->vpi_ids);
+
lpfc_stop_hba_timers(phba);
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(&phba->port_list_lock);
list_del_init(&vport->listentry);
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(&phba->port_list_lock);
lpfc_debugfs_terminate(vport);
@@ -8438,7 +14140,6 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
/* Disable interrupt */
lpfc_sli_disable_intr(phba);
- pci_set_drvdata(pdev, NULL);
scsi_host_put(shost);
/*
@@ -8446,6 +14147,8 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
* corresponding pools here.
*/
lpfc_scsi_free(phba);
+ lpfc_free_iocb_list(phba);
+
lpfc_mem_free_all(phba);
dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
@@ -8461,14 +14164,13 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
lpfc_hba_free(phba);
- pci_release_selected_regions(pdev, bars);
+ pci_release_mem_regions(pdev);
pci_disable_device(pdev);
}
/**
* lpfc_pci_suspend_one_s3 - PCI func to suspend SLI-3 device for power mgmnt
- * @pdev: pointer to PCI device
- * @msg: power management message
+ * @dev_d: pointer to device
*
* This routine is to be called from the kernel's PCI subsystem to support
* system Power Management (PM) to device with SLI-3 interface spec. When
@@ -8486,33 +14188,29 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
* 0 - driver suspended the device
* Error otherwise
**/
-static int
-lpfc_pci_suspend_one_s3(struct pci_dev *pdev, pm_message_t msg)
+static int __maybe_unused
+lpfc_pci_suspend_one_s3(struct device *dev_d)
{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct Scsi_Host *shost = dev_get_drvdata(dev_d);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0473 PCI device Power Management suspend.\n");
/* Bring down the device */
- lpfc_offline_prep(phba);
+ lpfc_offline_prep(phba, LPFC_MBX_WAIT);
lpfc_offline(phba);
kthread_stop(phba->worker_thread);
/* Disable interrupt from device */
lpfc_sli_disable_intr(phba);
- /* Save device state to PCI config space */
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
-
return 0;
}
/**
* lpfc_pci_resume_one_s3 - PCI func to resume SLI-3 device for power mgmnt
- * @pdev: pointer to PCI device
+ * @dev_d: pointer to device
*
* This routine is to be called from the kernel's PCI subsystem to support
* system Power Management (PM) to device with SLI-3 interface spec. When PM
@@ -8529,10 +14227,10 @@ lpfc_pci_suspend_one_s3(struct pci_dev *pdev, pm_message_t msg)
* 0 - driver suspended the device
* Error otherwise
**/
-static int
-lpfc_pci_resume_one_s3(struct pci_dev *pdev)
+static int __maybe_unused
+lpfc_pci_resume_one_s3(struct device *dev_d)
{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct Scsi_Host *shost = dev_get_drvdata(dev_d);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
uint32_t intr_mode;
int error;
@@ -8540,19 +14238,6 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0452 PCI device Power Management resume.\n");
- /* Restore device state from PCI config space */
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
- /*
- * As the new kernel behavior of pci_restore_state() API call clears
- * device saved_state flag, need to save the restored state again.
- */
- pci_save_state(pdev);
-
- if (pdev->is_busmaster)
- pci_set_master(pdev);
-
/* Startup the kernel thread for this host adapter. */
phba->worker_thread = kthread_run(lpfc_do_work, phba,
"lpfc_worker_%d", phba->brd_no);
@@ -8564,10 +14249,14 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
return error;
}
+ /* Init cpu_map array */
+ lpfc_cpu_map_array_init(phba);
+ /* Init hba_eq_hdl array */
+ lpfc_hba_eq_hdl_array_init(phba);
/* Configure and enable interrupt */
intr_mode = lpfc_sli_enable_intr(phba, phba->intr_mode);
if (intr_mode == LPFC_INTR_ERROR) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0430 PM resume Failed to enable interrupt\n");
return -EIO;
} else
@@ -8593,18 +14282,14 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
static void
lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba)
{
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring;
-
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2723 PCI channel I/O abort preparing for recovery\n");
/*
* There may be errored I/Os through HBA, abort all I/Os on txcmplq
* and let the SCSI mid-layer to retry them to recover.
*/
- pring = &psli->ring[psli->fcp_ring];
- lpfc_sli_abort_iocb_ring(phba, pring);
+ lpfc_sli_abort_fcp_rings(phba);
}
/**
@@ -8618,24 +14303,24 @@ lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba)
static void
lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
{
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2710 PCI channel disable preparing for reset\n");
/* Block any management I/Os to the device */
- lpfc_block_mgmt_io(phba);
+ lpfc_block_mgmt_io(phba, LPFC_MBX_WAIT);
/* Block all SCSI devices' I/Os on the host */
lpfc_scsi_dev_block(phba);
+ /* Flush all driver's outstanding SCSI I/Os as we are to reset */
+ lpfc_sli_flush_io_rings(phba);
+
/* stop all timers */
lpfc_stop_hba_timers(phba);
/* Disable interrupt and pci device */
lpfc_sli_disable_intr(phba);
pci_disable_device(phba->pcidev);
-
- /* Flush all driver's outstanding SCSI I/Os as we are to reset */
- lpfc_sli_flush_fcp_rings(phba);
}
/**
@@ -8649,16 +14334,17 @@ lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
static void
lpfc_sli_prep_dev_for_perm_failure(struct lpfc_hba *phba)
{
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2711 PCI channel permanent disable for failure\n");
/* Block all SCSI devices' I/Os on the host */
lpfc_scsi_dev_block(phba);
+ lpfc_sli4_prep_dev_for_reset(phba);
/* stop all timers */
lpfc_stop_hba_timers(phba);
/* Clean up all driver's outstanding SCSI I/Os */
- lpfc_sli_flush_fcp_rings(phba);
+ lpfc_sli_flush_io_rings(phba);
}
/**
@@ -8675,7 +14361,7 @@ lpfc_sli_prep_dev_for_perm_failure(struct lpfc_hba *phba)
* as desired.
*
* Return codes
- * PCI_ERS_RESULT_CAN_RECOVER - can be recovered with reset_link
+ * PCI_ERS_RESULT_CAN_RECOVER - can be recovered without reset
* PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
* PCI_ERS_RESULT_DISCONNECT - device could not be recovered
**/
@@ -8700,7 +14386,7 @@ lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state)
return PCI_ERS_RESULT_DISCONNECT;
default:
/* Unknown state, prepare and request slot reset */
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0472 Unknown PCI error state: x%x\n", state);
lpfc_sli_prep_dev_for_reset(phba);
return PCI_ERS_RESULT_NEED_RESET;
@@ -8742,12 +14428,6 @@ lpfc_io_slot_reset_s3(struct pci_dev *pdev)
pci_restore_state(pdev);
- /*
- * As the new kernel behavior of pci_restore_state() API call clears
- * device saved_state flag, need to save the restored state again.
- */
- pci_save_state(pdev);
-
if (pdev->is_busmaster)
pci_set_master(pdev);
@@ -8758,7 +14438,7 @@ lpfc_io_slot_reset_s3(struct pci_dev *pdev)
/* Configure and enable interrupt */
intr_mode = lpfc_sli_enable_intr(phba, phba->intr_mode);
if (intr_mode == LPFC_INTR_ERROR) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0427 Cannot re-enable interrupt after "
"slot reset.\n");
return PCI_ERS_RESULT_DISCONNECT;
@@ -8766,7 +14446,7 @@ lpfc_io_slot_reset_s3(struct pci_dev *pdev)
phba->intr_mode = intr_mode;
/* Take device offline, it will perform cleanup */
- lpfc_offline_prep(phba);
+ lpfc_offline_prep(phba, LPFC_MBX_WAIT);
lpfc_offline(phba);
lpfc_sli_brdrestart(phba);
@@ -8794,10 +14474,6 @@ lpfc_io_resume_s3(struct pci_dev *pdev)
/* Bring device online, it will be no-op for non-fatal error resume */
lpfc_online(phba);
-
- /* Clean up Advanced Error Reporting (AER) if needed */
- if (phba->hba_flag & HBA_AER_ENABLED)
- pci_cleanup_aer_uncorrect_error_status(pdev);
}
/**
@@ -8820,56 +14496,126 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba)
return 50;
else if (max_xri <= 1024)
return 100;
- else
+ else if (max_xri <= 1536)
return 150;
+ else if (max_xri <= 2048)
+ return 200;
+ else
+ return 250;
} else
return 0;
}
/**
- * lpfc_write_firmware - attempt to write a firmware image to the port
+ * lpfc_sli4_get_iocb_cnt - Calculate the # of total IOCBs to reserve
* @phba: pointer to lpfc hba data structure.
- * @fw: pointer to firmware image returned from request_firmware.
*
- * returns the number of bytes written if write is successful.
- * returns a negative error value if there were errors.
- * returns 0 if firmware matches currently active firmware on port.
+ * returns the number of ELS/CT + NVMET IOCBs to reserve
**/
int
-lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw)
+lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba)
+{
+ int max_xri = lpfc_sli4_get_els_iocb_cnt(phba);
+
+ if (phba->nvmet_support)
+ max_xri += LPFC_NVMET_BUF_POST;
+ return max_xri;
+}
+
+
+static int
+lpfc_log_write_firmware_error(struct lpfc_hba *phba, uint32_t offset,
+ uint32_t magic_number, uint32_t ftype, uint32_t fid, uint32_t fsize,
+ const struct firmware *fw)
+{
+ int rc;
+ u8 sli_family;
+
+ sli_family = bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf);
+ /* Three cases: (1) FW was not supported on the detected adapter.
+ * (2) FW update has been locked out administratively.
+ * (3) Some other error during FW update.
+ * In each case, an unmaskable message is written to the console
+ * for admin diagnosis.
+ */
+ if (offset == ADD_STATUS_FW_NOT_SUPPORTED ||
+ (sli_family == LPFC_SLI_INTF_FAMILY_G6 &&
+ magic_number != MAGIC_NUMBER_G6) ||
+ (sli_family == LPFC_SLI_INTF_FAMILY_G7 &&
+ magic_number != MAGIC_NUMBER_G7) ||
+ (sli_family == LPFC_SLI_INTF_FAMILY_G7P &&
+ magic_number != MAGIC_NUMBER_G7P)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3030 This firmware version is not supported on"
+ " this HBA model. Device:%x Magic:%x Type:%x "
+ "ID:%x Size %d %zd\n",
+ phba->pcidev->device, magic_number, ftype, fid,
+ fsize, fw->size);
+ rc = -EINVAL;
+ } else if (offset == ADD_STATUS_FW_DOWNLOAD_HW_DISABLED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3021 Firmware downloads have been prohibited "
+ "by a system configuration setting on "
+ "Device:%x Magic:%x Type:%x ID:%x Size %d "
+ "%zd\n",
+ phba->pcidev->device, magic_number, ftype, fid,
+ fsize, fw->size);
+ rc = -EACCES;
+ } else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3022 FW Download failed. Add Status x%x "
+ "Device:%x Magic:%x Type:%x ID:%x Size %d "
+ "%zd\n",
+ offset, phba->pcidev->device, magic_number,
+ ftype, fid, fsize, fw->size);
+ rc = -EIO;
+ }
+ return rc;
+}
+
+/**
+ * lpfc_write_firmware - attempt to write a firmware image to the port
+ * @fw: pointer to firmware image returned from request_firmware.
+ * @context: pointer to firmware image returned from request_firmware.
+ *
+ **/
+static void
+lpfc_write_firmware(const struct firmware *fw, void *context)
{
- char fwrev[32];
- struct lpfc_grp_hdr *image = (struct lpfc_grp_hdr *)fw->data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)context;
+ char fwrev[FW_REV_STR_SIZE];
+ struct lpfc_grp_hdr *image;
struct list_head dma_buffer_list;
int i, rc = 0;
struct lpfc_dmabuf *dmabuf, *next;
uint32_t offset = 0, temp_offset = 0;
+ uint32_t magic_number, ftype, fid, fsize;
- INIT_LIST_HEAD(&dma_buffer_list);
- if ((image->magic_number != LPFC_GROUP_OJECT_MAGIC_NUM) ||
- (bf_get(lpfc_grp_hdr_file_type, image) != LPFC_FILE_TYPE_GROUP) ||
- (bf_get(lpfc_grp_hdr_id, image) != LPFC_FILE_ID_GROUP) ||
- (image->size != fw->size)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3022 Invalid FW image found. "
- "Magic:%d Type:%x ID:%x\n",
- image->magic_number,
- bf_get(lpfc_grp_hdr_file_type, image),
- bf_get(lpfc_grp_hdr_id, image));
- return -EINVAL;
+ /* It can be null in no-wait mode, sanity check */
+ if (!fw) {
+ rc = -ENXIO;
+ goto out;
}
+ image = (struct lpfc_grp_hdr *)fw->data;
+
+ magic_number = be32_to_cpu(image->magic_number);
+ ftype = bf_get_be32(lpfc_grp_hdr_file_type, image);
+ fid = bf_get_be32(lpfc_grp_hdr_id, image);
+ fsize = be32_to_cpu(image->size);
+
+ INIT_LIST_HEAD(&dma_buffer_list);
lpfc_decode_firmware_rev(phba, fwrev, 1);
- if (strncmp(fwrev, image->rev_name, strnlen(fwrev, 16))) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3023 Updating Firmware. Current Version:%s "
- "New Version:%s\n",
- fwrev, image->rev_name);
+ if (strncmp(fwrev, image->revision, strnlen(image->revision, 16))) {
+ lpfc_log_msg(phba, KERN_NOTICE, LOG_INIT | LOG_SLI,
+ "3023 Updating Firmware, Current Version:%s "
+ "New Version:%s\n",
+ fwrev, image->revision);
for (i = 0; i < LPFC_MBX_WR_CONFIG_MAX_BDE; i++) {
dmabuf = kzalloc(sizeof(struct lpfc_dmabuf),
GFP_KERNEL);
if (!dmabuf) {
rc = -ENOMEM;
- goto out;
+ goto release_out;
}
dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
SLI4_PAGE_SIZE,
@@ -8878,43 +14624,96 @@ lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw)
if (!dmabuf->virt) {
kfree(dmabuf);
rc = -ENOMEM;
- goto out;
+ goto release_out;
}
list_add_tail(&dmabuf->list, &dma_buffer_list);
}
while (offset < fw->size) {
temp_offset = offset;
list_for_each_entry(dmabuf, &dma_buffer_list, list) {
- if (offset + SLI4_PAGE_SIZE > fw->size) {
- temp_offset += fw->size - offset;
+ if (temp_offset + SLI4_PAGE_SIZE > fw->size) {
memcpy(dmabuf->virt,
fw->data + temp_offset,
- fw->size - offset);
+ fw->size - temp_offset);
+ temp_offset = fw->size;
break;
}
- temp_offset += SLI4_PAGE_SIZE;
memcpy(dmabuf->virt, fw->data + temp_offset,
SLI4_PAGE_SIZE);
+ temp_offset += SLI4_PAGE_SIZE;
}
rc = lpfc_wr_object(phba, &dma_buffer_list,
(fw->size - offset), &offset);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3024 Firmware update failed. "
- "%d\n", rc);
- goto out;
+ rc = lpfc_log_write_firmware_error(phba, offset,
+ magic_number,
+ ftype,
+ fid,
+ fsize,
+ fw);
+ goto release_out;
}
}
rc = offset;
- }
-out:
+ } else
+ lpfc_log_msg(phba, KERN_NOTICE, LOG_INIT | LOG_SLI,
+ "3029 Skipped Firmware update, Current "
+ "Version:%s New Version:%s\n",
+ fwrev, image->revision);
+
+release_out:
list_for_each_entry_safe(dmabuf, next, &dma_buffer_list, list) {
list_del(&dmabuf->list);
dma_free_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE,
dmabuf->virt, dmabuf->phys);
kfree(dmabuf);
}
- return rc;
+ release_firmware(fw);
+out:
+ if (rc < 0)
+ lpfc_log_msg(phba, KERN_ERR, LOG_INIT | LOG_SLI,
+ "3062 Firmware update error, status %d.\n", rc);
+ else
+ lpfc_log_msg(phba, KERN_NOTICE, LOG_INIT | LOG_SLI,
+ "3024 Firmware update success: size %d.\n", rc);
+}
+
+/**
+ * lpfc_sli4_request_firmware_update - Request linux generic firmware upgrade
+ * @phba: pointer to lpfc hba data structure.
+ * @fw_upgrade: which firmware to update.
+ *
+ * This routine is called to perform Linux generic firmware upgrade on device
+ * that supports such feature.
+ **/
+int
+lpfc_sli4_request_firmware_update(struct lpfc_hba *phba, uint8_t fw_upgrade)
+{
+ char file_name[ELX_FW_NAME_SIZE] = {0};
+ int ret;
+ const struct firmware *fw;
+
+ /* Only supported on SLI4 interface type 2 for now */
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
+ LPFC_SLI_INTF_IF_TYPE_2)
+ return -EPERM;
+
+ scnprintf(file_name, sizeof(file_name), "%s.grp", phba->ModelName);
+
+ if (fw_upgrade == INT_FW_UPGRADE) {
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ file_name, &phba->pcidev->dev,
+ GFP_KERNEL, (void *)phba,
+ lpfc_write_firmware);
+ } else if (fw_upgrade == RUN_FW_UPGRADE) {
+ ret = request_firmware(&fw, file_name, &phba->pcidev->dev);
+ if (!ret)
+ lpfc_write_firmware(fw, (void *)phba);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
}
/**
@@ -8935,7 +14734,7 @@ out:
* 0 - driver can claim the device
* negative value - driver can not claim the device
**/
-static int __devinit
+static int
lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct lpfc_hba *phba;
@@ -8943,24 +14742,18 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
struct Scsi_Host *shost = NULL;
int error;
uint32_t cfg_mode, intr_mode;
- int mcnt;
- int adjusted_fcp_eq_count;
- int fcp_qidx;
- const struct firmware *fw;
- uint8_t file_name[16];
/* Allocate memory for HBA structure */
phba = lpfc_hba_alloc(pdev);
if (!phba)
return -ENOMEM;
+ INIT_LIST_HEAD(&phba->poll_list);
+
/* Perform generic PCI device enabling operation */
error = lpfc_enable_pci_dev(phba);
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1409 Failed to enable pci device.\n");
+ if (error)
goto out_free_phba;
- }
/* Set up SLI API function jump table for PCI-device group-1 HBAs */
error = lpfc_api_table_setup(phba, LPFC_PCI_DEV_OC);
@@ -8975,14 +14768,6 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_disable_pci_dev;
}
- /* Set up phase-1 common device driver resources */
- error = lpfc_setup_driver_resource_phase1(phba);
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1411 Failed to set up driver resource.\n");
- goto out_unset_pci_mem_s4;
- }
-
/* Set up SLI-4 Specific device driver resources */
error = lpfc_sli4_driver_resource_setup(phba);
if (error) {
@@ -8991,39 +14776,63 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_unset_pci_mem_s4;
}
- /* Initialize and populate the iocb list per host */
-
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2821 initialize iocb list %d.\n",
- phba->cfg_iocb_cnt*1024);
- error = lpfc_init_iocb_list(phba, phba->cfg_iocb_cnt*1024);
-
- if (error) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1413 Failed to initialize iocb list.\n");
- goto out_unset_driver_resource_s4;
- }
-
+ spin_lock_init(&phba->rrq_list_lock);
INIT_LIST_HEAD(&phba->active_rrq_list);
+ INIT_LIST_HEAD(&phba->fcf.fcf_pri_list);
/* Set up common device driver resources */
error = lpfc_setup_driver_resource_phase2(phba);
if (error) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1414 Failed to set up driver resource.\n");
- goto out_free_iocb_list;
+ goto out_unset_driver_resource_s4;
}
+ /* Get the default values for Model Name and Description */
+ lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
+
+ /* Now, trying to enable interrupt and bring up the device */
+ cfg_mode = phba->cfg_use_msi;
+
+ /* Put device to a known state before enabling interrupt */
+ phba->pport = NULL;
+ lpfc_stop_port(phba);
+
+ /* Init cpu_map array */
+ lpfc_cpu_map_array_init(phba);
+
+ /* Init hba_eq_hdl array */
+ lpfc_hba_eq_hdl_array_init(phba);
+
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_sli4_enable_intr(phba, cfg_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0426 Failed to enable interrupt.\n");
+ error = -ENODEV;
+ goto out_unset_driver_resource;
+ }
+ /* Default to single EQ for non-MSI-X */
+ if (phba->intr_type != MSIX) {
+ phba->cfg_irq_chann = 1;
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ if (phba->nvmet_support)
+ phba->cfg_nvmet_mrq = 1;
+ }
+ }
+ lpfc_cpu_affinity_check(phba, phba->cfg_irq_chann);
+
/* Create SCSI host to the physical port */
error = lpfc_create_shost(phba);
if (error) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1415 Failed to create scsi host.\n");
- goto out_unset_driver_resource;
+ goto out_disable_intr;
}
+ vport = phba->pport;
+ shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
/* Configure sysfs attributes */
- vport = phba->pport;
error = lpfc_alloc_sysfs_attr(vport);
if (error) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -9031,100 +14840,61 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_destroy_shost;
}
- shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
- /* Now, trying to enable interrupt and bring up the device */
- cfg_mode = phba->cfg_use_msi;
- while (true) {
- /* Put device to a known state before enabling interrupt */
- lpfc_stop_port(phba);
- /* Configure and enable interrupt */
- intr_mode = lpfc_sli4_enable_intr(phba, cfg_mode);
- if (intr_mode == LPFC_INTR_ERROR) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0426 Failed to enable interrupt.\n");
- error = -ENODEV;
- goto out_free_sysfs_attr;
- }
- /* Default to single EQ for non-MSI-X */
- if (phba->intr_type != MSIX)
- adjusted_fcp_eq_count = 0;
- else if (phba->sli4_hba.msix_vec_nr <
- phba->cfg_fcp_eq_count + 1)
- adjusted_fcp_eq_count = phba->sli4_hba.msix_vec_nr - 1;
- else
- adjusted_fcp_eq_count = phba->cfg_fcp_eq_count;
- /* Free unused EQs */
- for (fcp_qidx = adjusted_fcp_eq_count;
- fcp_qidx < phba->cfg_fcp_eq_count;
- fcp_qidx++) {
- lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_qidx]);
- /* do not delete the first fcp_cq */
- if (fcp_qidx)
- lpfc_sli4_queue_free(
- phba->sli4_hba.fcp_cq[fcp_qidx]);
- }
- phba->cfg_fcp_eq_count = adjusted_fcp_eq_count;
- /* Set up SLI-4 HBA */
- if (lpfc_sli4_hba_setup(phba)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1421 Failed to set up hba\n");
- error = -ENODEV;
- goto out_disable_intr;
- }
-
- /* Send NOP mbx cmds for non-INTx mode active interrupt test */
- if (intr_mode != 0)
- mcnt = lpfc_sli4_send_nop_mbox_cmds(phba,
- LPFC_ACT_INTR_CNT);
-
- /* Check active interrupts received only for MSI/MSI-X */
- if (intr_mode == 0 ||
- phba->sli.slistat.sli_intr >= LPFC_ACT_INTR_CNT) {
- /* Log the current active interrupt mode */
- phba->intr_mode = intr_mode;
- lpfc_log_intr_mode(phba, intr_mode);
- break;
- }
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0451 Configure interrupt mode (%d) "
- "failed active interrupt test.\n",
- intr_mode);
- /* Unset the previous SLI-4 HBA setup. */
- /*
- * TODO: Is this operation compatible with IF TYPE 2
- * devices? All port state is deleted and cleared.
- */
- lpfc_sli4_unset_hba(phba);
- /* Try next level of interrupt mode */
- cfg_mode = --intr_mode;
+ /* Set up SLI-4 HBA */
+ if (lpfc_sli4_hba_setup(phba)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "1421 Failed to set up hba\n");
+ error = -ENODEV;
+ goto out_free_sysfs_attr;
}
+ /* Log the current active interrupt mode */
+ phba->intr_mode = intr_mode;
+ lpfc_log_intr_mode(phba, intr_mode);
+
/* Perform post initialization setup */
lpfc_post_init_setup(phba);
- /* check for firmware upgrade or downgrade */
- snprintf(file_name, 16, "%s.grp", phba->ModelName);
- error = request_firmware(&fw, file_name, &phba->pcidev->dev);
- if (!error) {
- lpfc_write_firmware(phba, fw);
- release_firmware(fw);
+ /* NVME support in FW earlier in the driver load corrects the
+ * FC4 type making a check for nvme_support unnecessary.
+ */
+ if (phba->nvmet_support == 0) {
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ /* Create NVME binding with nvme_fc_transport. This
+ * ensures the vport is initialized. If the localport
+ * create fails, it should not unload the driver to
+ * support field issues.
+ */
+ error = lpfc_nvme_create_localport(vport);
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6004 NVME registration "
+ "failed, error x%x\n",
+ error);
+ }
+ }
}
+ /* check for firmware upgrade or downgrade */
+ if (phba->cfg_request_firmware_upgrade)
+ lpfc_sli4_request_firmware_update(phba, INT_FW_UPGRADE);
+
/* Check if there are static vports to be created. */
lpfc_create_static_vport(phba);
+ timer_setup(&phba->cpuhp_poll_timer, lpfc_sli4_poll_hbtimer, 0);
+ cpuhp_state_add_instance_nocalls(lpfc_cpuhp_state, &phba->cpuhp);
+
return 0;
-out_disable_intr:
- lpfc_sli4_disable_intr(phba);
out_free_sysfs_attr:
lpfc_free_sysfs_attr(vport);
out_destroy_shost:
lpfc_destroy_shost(phba);
+out_disable_intr:
+ lpfc_sli4_disable_intr(phba);
out_unset_driver_resource:
lpfc_unset_driver_resource_phase2(phba);
-out_free_iocb_list:
- lpfc_free_iocb_list(phba);
out_unset_driver_resource_s4:
lpfc_sli4_driver_resource_unset(phba);
out_unset_pci_mem_s4:
@@ -9147,7 +14917,7 @@ out_free_phba:
* removed from PCI bus, it performs all the necessary cleanup for the HBA
* device to be removed from the PCI subsystem properly.
**/
-static void __devexit
+static void
lpfc_pci_remove_one_s4(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
@@ -9157,26 +14927,36 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
int i;
/* Mark the device unloading flag */
- spin_lock_irq(&phba->hbalock);
- vport->load_flag |= FC_UNLOADING;
- spin_unlock_irq(&phba->hbalock);
+ set_bit(FC_UNLOADING, &vport->load_flag);
+ if (phba->cgn_i)
+ lpfc_unreg_congestion_buf(phba);
- /* Free the HBA sysfs attributes */
lpfc_free_sysfs_attr(vport);
/* Release all the vports against this physical port */
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for (i = 1; i <= phba->max_vports && vports[i] != NULL; i++)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ if (vports[i]->port_type == LPFC_PHYSICAL_PORT)
+ continue;
fc_vport_terminate(vports[i]->fc_vport);
+ }
lpfc_destroy_vport_work_array(phba, vports);
- /* Remove FC host and then SCSI host with the physical port */
+ /* Remove FC host with the physical port */
fc_remove_host(shost);
scsi_remove_host(shost);
- /* Perform cleanup on the physical port */
+ /* Perform ndlp cleanup on the physical port. The nvme and nvmet
+ * localports are destroyed after to cleanup all transport memory.
+ */
lpfc_cleanup(vport);
+ lpfc_nvmet_destroy_targetport(phba);
+ lpfc_nvme_destroy_localport(vport);
+
+ /* De-allocate multi-XRI pools */
+ if (phba->cfg_xri_rebalancing)
+ lpfc_destroy_multixri_pools(phba);
/*
* Bring down the SLI Layer. This step disables all interrupts,
@@ -9184,16 +14964,20 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
* the HBA FCoE function.
*/
lpfc_debugfs_terminate(vport);
- lpfc_sli4_hba_unset(phba);
- spin_lock_irq(&phba->hbalock);
+ lpfc_stop_hba_timers(phba);
+ spin_lock_irq(&phba->port_list_lock);
list_del_init(&vport->listentry);
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(&phba->port_list_lock);
/* Perform scsi free before driver resource_unset since scsi
* buffers are released to their corresponding pools here.
*/
- lpfc_scsi_free(phba);
+ lpfc_io_free(phba);
+ lpfc_free_iocb_list(phba);
+ lpfc_sli4_hba_unset(phba);
+
+ lpfc_unset_driver_resource_phase2(phba);
lpfc_sli4_driver_resource_unset(phba);
/* Unmap adapter Control and Doorbell registers */
@@ -9211,8 +14995,7 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
/**
* lpfc_pci_suspend_one_s4 - PCI func to suspend SLI-4 device for power mgmnt
- * @pdev: pointer to PCI device
- * @msg: power management message
+ * @dev_d: pointer to device
*
* This routine is called from the kernel's PCI subsystem to support system
* Power Management (PM) to device with SLI-4 interface spec. When PM invokes
@@ -9230,33 +15013,30 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
* 0 - driver suspended the device
* Error otherwise
**/
-static int
-lpfc_pci_suspend_one_s4(struct pci_dev *pdev, pm_message_t msg)
+static int __maybe_unused
+lpfc_pci_suspend_one_s4(struct device *dev_d)
{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct Scsi_Host *shost = dev_get_drvdata(dev_d);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"2843 PCI device Power Management suspend.\n");
/* Bring down the device */
- lpfc_offline_prep(phba);
+ lpfc_offline_prep(phba, LPFC_MBX_WAIT);
lpfc_offline(phba);
kthread_stop(phba->worker_thread);
/* Disable interrupt from device */
lpfc_sli4_disable_intr(phba);
-
- /* Save device state to PCI config space */
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
+ lpfc_sli4_queue_destroy(phba);
return 0;
}
/**
* lpfc_pci_resume_one_s4 - PCI func to resume SLI-4 device for power mgmnt
- * @pdev: pointer to PCI device
+ * @dev_d: pointer to device
*
* This routine is called from the kernel's PCI subsystem to support system
* Power Management (PM) to device with SLI-4 interface spac. When PM invokes
@@ -9273,10 +15053,10 @@ lpfc_pci_suspend_one_s4(struct pci_dev *pdev, pm_message_t msg)
* 0 - driver suspended the device
* Error otherwise
**/
-static int
-lpfc_pci_resume_one_s4(struct pci_dev *pdev)
+static int __maybe_unused
+lpfc_pci_resume_one_s4(struct device *dev_d)
{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct Scsi_Host *shost = dev_get_drvdata(dev_d);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
uint32_t intr_mode;
int error;
@@ -9284,19 +15064,6 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev)
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"0292 PCI device Power Management resume.\n");
- /* Restore device state from PCI config space */
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
- /*
- * As the new kernel behavior of pci_restore_state() API call clears
- * device saved_state flag, need to save the restored state again.
- */
- pci_save_state(pdev);
-
- if (pdev->is_busmaster)
- pci_set_master(pdev);
-
/* Startup the kernel thread for this host adapter. */
phba->worker_thread = kthread_run(lpfc_do_work, phba,
"lpfc_worker_%d", phba->brd_no);
@@ -9311,7 +15078,7 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev)
/* Configure and enable interrupt */
intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
if (intr_mode == LPFC_INTR_ERROR) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0294 PM resume Failed to enable interrupt\n");
return -EIO;
} else
@@ -9337,17 +15104,13 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev)
static void
lpfc_sli4_prep_dev_for_recover(struct lpfc_hba *phba)
{
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring;
-
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2828 PCI channel I/O abort preparing for recovery\n");
/*
* There may be errored I/Os through HBA, abort all I/Os on txcmplq
* and let the SCSI mid-layer to retry them to recover.
*/
- pring = &psli->ring[psli->fcp_ring];
- lpfc_sli_abort_iocb_ring(phba, pring);
+ lpfc_sli_abort_fcp_rings(phba);
}
/**
@@ -9361,24 +15124,29 @@ lpfc_sli4_prep_dev_for_recover(struct lpfc_hba *phba)
static void
lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba)
{
+ int offline = pci_channel_offline(phba->pcidev);
+
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2826 PCI channel disable preparing for reset\n");
+ "2826 PCI channel disable preparing for reset offline"
+ " %d\n", offline);
/* Block any management I/Os to the device */
- lpfc_block_mgmt_io(phba);
+ lpfc_block_mgmt_io(phba, LPFC_MBX_NO_WAIT);
- /* Block all SCSI devices' I/Os on the host */
- lpfc_scsi_dev_block(phba);
+
+ /* HBA_PCI_ERR was set in io_error_detect */
+ lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
+ /* Flush all driver's outstanding I/Os as we are to reset */
+ lpfc_sli_flush_io_rings(phba);
+ lpfc_offline(phba);
/* stop all timers */
lpfc_stop_hba_timers(phba);
+ lpfc_sli4_queue_destroy(phba);
/* Disable interrupt and pci device */
lpfc_sli4_disable_intr(phba);
pci_disable_device(phba->pcidev);
-
- /* Flush all driver's outstanding SCSI I/Os as we are to reset */
- lpfc_sli_flush_fcp_rings(phba);
}
/**
@@ -9392,7 +15160,7 @@ lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba)
static void
lpfc_sli4_prep_dev_for_perm_failure(struct lpfc_hba *phba)
{
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2827 PCI channel permanent disable for failure\n");
/* Block all SCSI devices' I/Os on the host */
@@ -9401,8 +15169,8 @@ lpfc_sli4_prep_dev_for_perm_failure(struct lpfc_hba *phba)
/* stop all timers */
lpfc_stop_hba_timers(phba);
- /* Clean up all driver's outstanding SCSI I/Os */
- lpfc_sli_flush_fcp_rings(phba);
+ /* Clean up all driver's outstanding I/Os */
+ lpfc_sli_flush_io_rings(phba);
}
/**
@@ -9426,6 +15194,7 @@ lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ bool hba_pci_err;
switch (state) {
case pci_channel_io_normal:
@@ -9433,16 +15202,26 @@ lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state)
lpfc_sli4_prep_dev_for_recover(phba);
return PCI_ERS_RESULT_CAN_RECOVER;
case pci_channel_io_frozen:
+ hba_pci_err = test_and_set_bit(HBA_PCI_ERR, &phba->bit_flags);
/* Fatal error, prepare for slot reset */
- lpfc_sli4_prep_dev_for_reset(phba);
+ if (!hba_pci_err)
+ lpfc_sli4_prep_dev_for_reset(phba);
+ else
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2832 Already handling PCI error "
+ "state: x%x\n", state);
return PCI_ERS_RESULT_NEED_RESET;
case pci_channel_io_perm_failure:
+ set_bit(HBA_PCI_ERR, &phba->bit_flags);
/* Permanent failure, prepare for device down */
lpfc_sli4_prep_dev_for_perm_failure(phba);
return PCI_ERS_RESULT_DISCONNECT;
default:
+ hba_pci_err = test_and_set_bit(HBA_PCI_ERR, &phba->bit_flags);
+ if (!hba_pci_err)
+ lpfc_sli4_prep_dev_for_reset(phba);
/* Unknown state, prepare and request slot reset */
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2825 Unknown PCI error state: x%x\n", state);
lpfc_sli4_prep_dev_for_reset(phba);
return PCI_ERS_RESULT_NEED_RESET;
@@ -9474,15 +15253,27 @@ lpfc_io_slot_reset_s4(struct pci_dev *pdev)
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
struct lpfc_sli *psli = &phba->sli;
uint32_t intr_mode;
+ bool hba_pci_err;
dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
if (pci_enable_device_mem(pdev)) {
printk(KERN_ERR "lpfc: Cannot re-enable "
- "PCI device after reset.\n");
+ "PCI device after reset.\n");
return PCI_ERS_RESULT_DISCONNECT;
}
pci_restore_state(pdev);
+
+ hba_pci_err = test_and_clear_bit(HBA_PCI_ERR, &phba->bit_flags);
+ if (!hba_pci_err)
+ dev_info(&pdev->dev,
+ "hba_pci_err was not set, recovering slot reset.\n");
+ /*
+ * As the new kernel behavior of pci_restore_state() API call clears
+ * device saved_state flag, need to save the restored state again.
+ */
+ pci_save_state(pdev);
+
if (pdev->is_busmaster)
pci_set_master(pdev);
@@ -9490,15 +15281,18 @@ lpfc_io_slot_reset_s4(struct pci_dev *pdev)
psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
+ /* Init cpu_map array */
+ lpfc_cpu_map_array_init(phba);
/* Configure and enable interrupt */
intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
if (intr_mode == LPFC_INTR_ERROR) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2824 Cannot re-enable interrupt after "
"slot reset.\n");
return PCI_ERS_RESULT_DISCONNECT;
} else
phba->intr_mode = intr_mode;
+ lpfc_cpu_affinity_check(phba, phba->cfg_irq_chann);
/* Log the current active interrupt mode */
lpfc_log_intr_mode(phba, phba->intr_mode);
@@ -9530,16 +15324,10 @@ lpfc_io_resume_s4(struct pci_dev *pdev)
*/
if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) {
/* Perform device reset */
- lpfc_offline_prep(phba);
- lpfc_offline(phba);
lpfc_sli_brdrestart(phba);
/* Bring the device back online */
lpfc_online(phba);
}
-
- /* Clean up Advanced Error Reporting (AER) if needed */
- if (phba->hba_flag & HBA_AER_ENABLED)
- pci_cleanup_aer_uncorrect_error_status(pdev);
}
/**
@@ -9560,7 +15348,7 @@ lpfc_io_resume_s4(struct pci_dev *pdev)
* 0 - driver can claim the device
* negative value - driver can not claim the device
**/
-static int __devinit
+static int
lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
{
int rc;
@@ -9588,7 +15376,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
* remove routine, which will perform all the necessary cleanup for the
* device to be removed from the PCI subsystem properly.
**/
-static void __devexit
+static void
lpfc_pci_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
@@ -9602,7 +15390,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
lpfc_pci_remove_one_s4(pdev);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1424 Invalid PCI device group: 0x%x\n",
phba->pci_dev_grp);
break;
@@ -9612,8 +15400,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
/**
* lpfc_pci_suspend_one - lpfc PCI func to suspend dev for power management
- * @pdev: pointer to PCI device
- * @msg: power management message
+ * @dev: pointer to device
*
* This routine is to be registered to the kernel's PCI subsystem to support
* system Power Management (PM). When PM invokes this method, it dispatches
@@ -9624,22 +15411,22 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
* 0 - driver suspended the device
* Error otherwise
**/
-static int
-lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+static int __maybe_unused
+lpfc_pci_suspend_one(struct device *dev)
{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct Scsi_Host *shost = dev_get_drvdata(dev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
int rc = -ENODEV;
switch (phba->pci_dev_grp) {
case LPFC_PCI_DEV_LP:
- rc = lpfc_pci_suspend_one_s3(pdev, msg);
+ rc = lpfc_pci_suspend_one_s3(dev);
break;
case LPFC_PCI_DEV_OC:
- rc = lpfc_pci_suspend_one_s4(pdev, msg);
+ rc = lpfc_pci_suspend_one_s4(dev);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1425 Invalid PCI device group: 0x%x\n",
phba->pci_dev_grp);
break;
@@ -9649,7 +15436,7 @@ lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
/**
* lpfc_pci_resume_one - lpfc PCI func to resume dev for power management
- * @pdev: pointer to PCI device
+ * @dev: pointer to device
*
* This routine is to be registered to the kernel's PCI subsystem to support
* system Power Management (PM). When PM invokes this method, it dispatches
@@ -9660,22 +15447,22 @@ lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
* 0 - driver suspended the device
* Error otherwise
**/
-static int
-lpfc_pci_resume_one(struct pci_dev *pdev)
+static int __maybe_unused
+lpfc_pci_resume_one(struct device *dev)
{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct Scsi_Host *shost = dev_get_drvdata(dev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
int rc = -ENODEV;
switch (phba->pci_dev_grp) {
case LPFC_PCI_DEV_LP:
- rc = lpfc_pci_resume_one_s3(pdev);
+ rc = lpfc_pci_resume_one_s3(dev);
break;
case LPFC_PCI_DEV_OC:
- rc = lpfc_pci_resume_one_s4(pdev);
+ rc = lpfc_pci_resume_one_s4(dev);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1426 Invalid PCI device group: 0x%x\n",
phba->pci_dev_grp);
break;
@@ -9705,6 +15492,10 @@ lpfc_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT;
+ if (phba->link_state == LPFC_HBA_ERROR &&
+ test_bit(HBA_IOQ_FLUSH, &phba->hba_flag))
+ return PCI_ERS_RESULT_NEED_RESET;
+
switch (phba->pci_dev_grp) {
case LPFC_PCI_DEV_LP:
rc = lpfc_io_error_detected_s3(pdev, state);
@@ -9713,7 +15504,7 @@ lpfc_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
rc = lpfc_io_error_detected_s4(pdev, state);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1427 Invalid PCI device group: 0x%x\n",
phba->pci_dev_grp);
break;
@@ -9750,7 +15541,7 @@ lpfc_io_slot_reset(struct pci_dev *pdev)
rc = lpfc_io_slot_reset_s4(pdev);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1428 Invalid PCI device group: 0x%x\n",
phba->pci_dev_grp);
break;
@@ -9782,7 +15573,7 @@ lpfc_io_resume(struct pci_dev *pdev)
lpfc_io_resume_s4(pdev);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1429 Invalid PCI device group: 0x%x\n",
phba->pci_dev_grp);
break;
@@ -9790,118 +15581,93 @@ lpfc_io_resume(struct pci_dev *pdev)
return;
}
-static struct pci_device_id lpfc_id_table[] = {
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FIREFLY,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_THOR,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PEGASUS,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_CENTAUR,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_DRAGONFLY,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SUPERFLY,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_RFLY,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PFLY,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE_SCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE_DCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS_SCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS_DCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BMID,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BSMB,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HORNET,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_SCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_DCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZMID,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZSMB,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_TFLY,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP101,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP10000S,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP11000S,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LPE11000S,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_MID,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_SMB,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_DCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_SCSP,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_S,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_VF,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_PF,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_S,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TOMCAT,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FALCON,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BALIUS,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FC,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FC_VF,
- PCI_ANY_ID, PCI_ANY_ID, },
- {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE_VF,
- PCI_ANY_ID, PCI_ANY_ID, },
- { 0 }
-};
+/**
+ * lpfc_sli4_oas_verify - Verify OAS is supported by this adapter
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine checks to see if OAS is supported for this adapter. If
+ * supported, the configure Flash Optimized Fabric flag is set. Otherwise,
+ * the enable oas flag is cleared and the pool created for OAS device data
+ * is destroyed.
+ *
+ **/
+static void
+lpfc_sli4_oas_verify(struct lpfc_hba *phba)
+{
+
+ if (!phba->cfg_EnableXLane)
+ return;
+
+ if (phba->sli4_hba.pc_sli4_params.oas_supported) {
+ phba->cfg_fof = 1;
+ } else {
+ phba->cfg_fof = 0;
+ mempool_destroy(phba->device_data_mem_pool);
+ phba->device_data_mem_pool = NULL;
+ }
+
+ return;
+}
+
+/**
+ * lpfc_sli4_ras_init - Verify RAS-FW log is supported by this adapter
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine checks to see if RAS is supported by the adapter. Check the
+ * function through which RAS support enablement is to be done.
+ **/
+void
+lpfc_sli4_ras_init(struct lpfc_hba *phba)
+{
+ /* if ASIC_GEN_NUM >= 0xC) */
+ if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_6) ||
+ (bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_FAMILY_G6)) {
+ phba->ras_fwlog.ras_hwsupport = true;
+ if (phba->cfg_ras_fwlog_func == PCI_FUNC(phba->pcidev->devfn) &&
+ phba->cfg_ras_fwlog_buffsize)
+ phba->ras_fwlog.ras_enabled = true;
+ else
+ phba->ras_fwlog.ras_enabled = false;
+ } else {
+ phba->ras_fwlog.ras_hwsupport = false;
+ }
+}
+
MODULE_DEVICE_TABLE(pci, lpfc_id_table);
-static struct pci_error_handlers lpfc_err_handler = {
+static const struct pci_error_handlers lpfc_err_handler = {
.error_detected = lpfc_io_error_detected,
.slot_reset = lpfc_io_slot_reset,
.resume = lpfc_io_resume,
};
+static SIMPLE_DEV_PM_OPS(lpfc_pci_pm_ops_one,
+ lpfc_pci_suspend_one,
+ lpfc_pci_resume_one);
+
static struct pci_driver lpfc_driver = {
.name = LPFC_DRIVER_NAME,
.id_table = lpfc_id_table,
.probe = lpfc_pci_probe_one,
- .remove = __devexit_p(lpfc_pci_remove_one),
- .suspend = lpfc_pci_suspend_one,
- .resume = lpfc_pci_resume_one,
+ .remove = lpfc_pci_remove_one,
+ .shutdown = lpfc_pci_remove_one,
+ .driver.pm = &lpfc_pci_pm_ops_one,
.err_handler = &lpfc_err_handler,
};
+static const struct file_operations lpfc_mgmt_fop = {
+ .owner = THIS_MODULE,
+};
+
+static struct miscdevice lpfc_mgmt_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "lpfcmgmt",
+ .fops = &lpfc_mgmt_fop,
+};
+
/**
* lpfc_init - lpfc module initialization routine
*
@@ -9919,33 +15685,136 @@ lpfc_init(void)
{
int error = 0;
- printk(LPFC_MODULE_DESC "\n");
- printk(LPFC_COPYRIGHT "\n");
+ pr_info(LPFC_MODULE_DESC "\n");
+ pr_info(LPFC_COPYRIGHT "\n");
- if (lpfc_enable_npiv) {
- lpfc_transport_functions.vport_create = lpfc_vport_create;
- lpfc_transport_functions.vport_delete = lpfc_vport_delete;
- }
+ error = misc_register(&lpfc_mgmt_dev);
+ if (error)
+ printk(KERN_ERR "Could not register lpfcmgmt device, "
+ "misc_register returned with status %d", error);
+
+ error = -ENOMEM;
+ lpfc_transport_functions.vport_create = lpfc_vport_create;
+ lpfc_transport_functions.vport_delete = lpfc_vport_delete;
lpfc_transport_template =
fc_attach_transport(&lpfc_transport_functions);
if (lpfc_transport_template == NULL)
- return -ENOMEM;
- if (lpfc_enable_npiv) {
- lpfc_vport_transport_template =
- fc_attach_transport(&lpfc_vport_transport_functions);
- if (lpfc_vport_transport_template == NULL) {
- fc_release_transport(lpfc_transport_template);
- return -ENOMEM;
- }
- }
- error = pci_register_driver(&lpfc_driver);
- if (error) {
+ goto unregister;
+ lpfc_vport_transport_template =
+ fc_attach_transport(&lpfc_vport_transport_functions);
+ if (lpfc_vport_transport_template == NULL) {
fc_release_transport(lpfc_transport_template);
- if (lpfc_enable_npiv)
- fc_release_transport(lpfc_vport_transport_template);
+ goto unregister;
}
+ lpfc_wqe_cmd_template();
+ lpfc_nvmet_cmd_template();
+
+ /* Initialize in case vector mapping is needed */
+ lpfc_present_cpu = num_present_cpus();
+
+ lpfc_pldv_detect = false;
+
+ error = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
+ "lpfc/sli4:online",
+ lpfc_cpu_online, lpfc_cpu_offline);
+ if (error < 0)
+ goto cpuhp_failure;
+ lpfc_cpuhp_state = error;
+
+ error = pci_register_driver(&lpfc_driver);
+ if (error)
+ goto unwind;
return error;
+
+unwind:
+ cpuhp_remove_multi_state(lpfc_cpuhp_state);
+cpuhp_failure:
+ fc_release_transport(lpfc_transport_template);
+ fc_release_transport(lpfc_vport_transport_template);
+unregister:
+ misc_deregister(&lpfc_mgmt_dev);
+
+ return error;
+}
+
+void lpfc_dmp_dbg(struct lpfc_hba *phba)
+{
+ unsigned int start_idx;
+ unsigned int dbg_cnt;
+ unsigned int temp_idx;
+ int i;
+ int j = 0;
+ unsigned long rem_nsec;
+
+ if (atomic_cmpxchg(&phba->dbg_log_dmping, 0, 1) != 0)
+ return;
+
+ start_idx = (unsigned int)atomic_read(&phba->dbg_log_idx) % DBG_LOG_SZ;
+ dbg_cnt = (unsigned int)atomic_read(&phba->dbg_log_cnt);
+ if (!dbg_cnt)
+ goto out;
+ temp_idx = start_idx;
+ if (dbg_cnt >= DBG_LOG_SZ) {
+ dbg_cnt = DBG_LOG_SZ;
+ temp_idx -= 1;
+ } else {
+ if ((start_idx + dbg_cnt) > (DBG_LOG_SZ - 1)) {
+ temp_idx = (start_idx + dbg_cnt) % DBG_LOG_SZ;
+ } else {
+ if (start_idx < dbg_cnt)
+ start_idx = DBG_LOG_SZ - (dbg_cnt - start_idx);
+ else
+ start_idx -= dbg_cnt;
+ }
+ }
+ dev_info(&phba->pcidev->dev, "start %d end %d cnt %d\n",
+ start_idx, temp_idx, dbg_cnt);
+
+ for (i = 0; i < dbg_cnt; i++) {
+ if ((start_idx + i) < DBG_LOG_SZ)
+ temp_idx = (start_idx + i) % DBG_LOG_SZ;
+ else
+ temp_idx = j++;
+ rem_nsec = do_div(phba->dbg_log[temp_idx].t_ns, NSEC_PER_SEC);
+ dev_info(&phba->pcidev->dev, "%d: [%5lu.%06lu] %s",
+ temp_idx,
+ (unsigned long)phba->dbg_log[temp_idx].t_ns,
+ rem_nsec / 1000,
+ phba->dbg_log[temp_idx].log);
+ }
+out:
+ atomic_set(&phba->dbg_log_cnt, 0);
+ atomic_set(&phba->dbg_log_dmping, 0);
+}
+
+__printf(2, 3)
+void lpfc_dbg_print(struct lpfc_hba *phba, const char *fmt, ...)
+{
+ unsigned int idx;
+ va_list args;
+ int dbg_dmping = atomic_read(&phba->dbg_log_dmping);
+ struct va_format vaf;
+
+
+ va_start(args, fmt);
+ if (unlikely(dbg_dmping)) {
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ dev_info(&phba->pcidev->dev, "%pV", &vaf);
+ va_end(args);
+ return;
+ }
+ idx = (unsigned int)atomic_fetch_add(1, &phba->dbg_log_idx) %
+ DBG_LOG_SZ;
+
+ atomic_inc(&phba->dbg_log_cnt);
+
+ vscnprintf(phba->dbg_log[idx].log,
+ sizeof(phba->dbg_log[idx].log), fmt, args);
+ va_end(args);
+
+ phba->dbg_log[idx].t_ns = local_clock();
}
/**
@@ -9958,28 +15827,17 @@ lpfc_init(void)
static void __exit
lpfc_exit(void)
{
+ misc_deregister(&lpfc_mgmt_dev);
pci_unregister_driver(&lpfc_driver);
+ cpuhp_remove_multi_state(lpfc_cpuhp_state);
fc_release_transport(lpfc_transport_template);
- if (lpfc_enable_npiv)
- fc_release_transport(lpfc_vport_transport_template);
- if (_dump_buf_data) {
- printk(KERN_ERR "9062 BLKGRD: freeing %lu pages for "
- "_dump_buf_data at 0x%p\n",
- (1L << _dump_buf_data_order), _dump_buf_data);
- free_pages((unsigned long)_dump_buf_data, _dump_buf_data_order);
- }
-
- if (_dump_buf_dif) {
- printk(KERN_ERR "9049 BLKGRD: freeing %lu pages for "
- "_dump_buf_dif at 0x%p\n",
- (1L << _dump_buf_dif_order), _dump_buf_dif);
- free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
- }
+ fc_release_transport(lpfc_vport_transport_template);
+ idr_destroy(&lpfc_hba_index);
}
module_init(lpfc_init);
module_exit(lpfc_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(LPFC_MODULE_DESC);
-MODULE_AUTHOR("Emulex Corporation - tech.support@emulex.com");
+MODULE_AUTHOR("Broadcom");
MODULE_VERSION("0:" LPFC_DRIVER_VERSION);
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
index e3b790e59156..59bd2bafc73f 100644
--- a/drivers/scsi/lpfc/lpfc_logmsg.h
+++ b/drivers/scsi/lpfc/lpfc_logmsg.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2023 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2009 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -23,7 +25,7 @@
#define LOG_MBOX 0x00000004 /* Mailbox events */
#define LOG_INIT 0x00000008 /* Initialization events */
#define LOG_LINK_EVENT 0x00000010 /* Link events */
-#define LOG_IP 0x00000020 /* IP traffic history */
+#define LOG_NODE_VERBOSE 0x00000020 /* Node verbose events */
#define LOG_FCP 0x00000040 /* FCP traffic history */
#define LOG_NODE 0x00000080 /* Node table events */
#define LOG_TEMP 0x00000100 /* Temperature sensor events */
@@ -33,16 +35,52 @@
#define LOG_FCP_ERROR 0x00001000 /* log errors, not underruns */
#define LOG_LIBDFC 0x00002000 /* Libdfc events */
#define LOG_VPORT 0x00004000 /* NPIV events */
-#define LOG_SECURITY 0x00008000 /* Security events */
+#define LOG_LDS_EVENT 0x00008000 /* Link Degrade Signaling events */
#define LOG_EVENT 0x00010000 /* CT,TEMP,DUMP, logging */
#define LOG_FIP 0x00020000 /* FIP events */
-#define LOG_ALL_MSG 0xffffffff /* LOG all messages */
+#define LOG_FCP_UNDER 0x00040000 /* FCP underruns errors */
+#define LOG_SCSI_CMD 0x00080000 /* ALL SCSI commands */
+#define LOG_NVME 0x00100000 /* NVME general events. */
+#define LOG_NVME_DISC 0x00200000 /* NVME Discovery/Connect events. */
+#define LOG_NVME_ABTS 0x00400000 /* NVME ABTS events. */
+#define LOG_NVME_IOERR 0x00800000 /* NVME IO Error events. */
+#define LOG_RSVD1 0x01000000 /* Reserved */
+#define LOG_RSVD2 0x02000000 /* Reserved */
+#define LOG_CGN_MGMT 0x04000000 /* Congestion Mgmt events */
+#define LOG_TRACE_EVENT 0x80000000 /* Dmp the DBG log on this err */
+#define LOG_ALL_MSG 0x7fffffff /* LOG all messages */
+
+void lpfc_dmp_dbg(struct lpfc_hba *phba);
+void lpfc_dbg_print(struct lpfc_hba *phba, const char *fmt, ...);
+
+/* generate message by verbose log setting or severity */
+#define lpfc_vlog_msg(vport, level, mask, fmt, arg...) \
+{ if (((mask) & (vport)->cfg_log_verbose) || (level[1] <= '5')) \
+ dev_printk(level, &((vport)->phba->pcidev)->dev, "%d:(%d):" \
+ fmt, (vport)->phba->brd_no, vport->vpi, ##arg); }
+
+#define lpfc_log_msg(phba, level, mask, fmt, arg...) \
+do { \
+ { uint32_t log_verbose = (phba)->pport ? \
+ (phba)->pport->cfg_log_verbose : \
+ (phba)->cfg_log_verbose; \
+ if (((mask) & log_verbose) || (level[1] <= '5')) \
+ dev_printk(level, &((phba)->pcidev)->dev, "%d:" \
+ fmt, phba->brd_no, ##arg); \
+ } \
+} while (0)
#define lpfc_printf_vlog(vport, level, mask, fmt, arg...) \
do { \
- { if (((mask) & (vport)->cfg_log_verbose) || (level[1] <= '3')) \
+ { if (((mask) & (vport)->cfg_log_verbose) || (level[1] <= '3')) { \
+ if ((mask) & LOG_TRACE_EVENT && !(vport)->cfg_log_verbose) \
+ lpfc_dmp_dbg((vport)->phba); \
dev_printk(level, &((vport)->phba->pcidev)->dev, "%d:(%d):" \
- fmt, (vport)->phba->brd_no, vport->vpi, ##arg); } \
+ fmt, (vport)->phba->brd_no, vport->vpi, ##arg); \
+ } else if (!(vport)->cfg_log_verbose) \
+ lpfc_dbg_print((vport)->phba, "%d:(%d):" fmt, \
+ (vport)->phba->brd_no, (vport)->vpi, ##arg); \
+ } \
} while (0)
#define lpfc_printf_log(phba, level, mask, fmt, arg...) \
@@ -50,8 +88,12 @@ do { \
{ uint32_t log_verbose = (phba)->pport ? \
(phba)->pport->cfg_log_verbose : \
(phba)->cfg_log_verbose; \
- if (((mask) & log_verbose) || (level[1] <= '3')) \
+ if (((mask) & log_verbose) || (level[1] <= '3')) { \
+ if ((mask) & LOG_TRACE_EVENT && !log_verbose) \
+ lpfc_dmp_dbg(phba); \
dev_printk(level, &((phba)->pcidev)->dev, "%d:" \
- fmt, phba->brd_no, ##arg); \
+ fmt, phba->brd_no, ##arg); \
+ } else if (!log_verbose)\
+ lpfc_dbg_print(phba, "%d:" fmt, phba->brd_no, ##arg); \
} \
} while (0)
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 556767028353..fb6dbcb86c09 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -42,6 +44,80 @@
#include "lpfc_compat.h"
/**
+ * lpfc_mbox_rsrc_prep - Prepare a mailbox with DMA buffer memory.
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to the driver internal queue element for mailbox command.
+ *
+ * A mailbox command consists of the pool memory for the command, @mbox, and
+ * one or more DMA buffers for the data transfer. This routine provides
+ * a standard framework for allocating the dma buffer and assigning to the
+ * @mbox. Callers should cleanup the mbox with a call to
+ * lpfc_mbox_rsrc_cleanup.
+ *
+ * The lpfc_mbuf_alloc routine acquires the hbalock so the caller is
+ * responsible to ensure the hbalock is released. Also note that the
+ * driver design is a single dmabuf/mbuf per mbox in the ctx_buf.
+ *
+ **/
+int
+lpfc_mbox_rsrc_prep(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ struct lpfc_dmabuf *mp;
+
+ mp = kmalloc(sizeof(*mp), GFP_KERNEL);
+ if (!mp)
+ return -ENOMEM;
+
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+ if (!mp->virt) {
+ kfree(mp);
+ return -ENOMEM;
+ }
+
+ memset(mp->virt, 0, LPFC_BPL_SIZE);
+
+ /* Initialization only. Driver does not use a list of dmabufs. */
+ INIT_LIST_HEAD(&mp->list);
+ mbox->ctx_buf = mp;
+ return 0;
+}
+
+/**
+ * lpfc_mbox_rsrc_cleanup - Free the mailbox DMA buffer and virtual memory.
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to the driver internal queue element for mailbox command.
+ * @locked: value that indicates if the hbalock is held (1) or not (0).
+ *
+ * A mailbox command consists of the pool memory for the command, @mbox, and
+ * possibly a DMA buffer for the data transfer. This routine provides
+ * a standard framework for releasing any dma buffers and freeing all
+ * memory resources in it as well as releasing the @mbox back to the @phba pool.
+ * Callers should use this routine for cleanup for all mailboxes prepped with
+ * lpfc_mbox_rsrc_prep.
+ *
+ **/
+void
+lpfc_mbox_rsrc_cleanup(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox,
+ enum lpfc_mbox_ctx locked)
+{
+ struct lpfc_dmabuf *mp;
+
+ mp = mbox->ctx_buf;
+ mbox->ctx_buf = NULL;
+
+ /* Release the generic BPL buffer memory. */
+ if (mp) {
+ if (locked == MBOX_THD_LOCKED)
+ __lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ else
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+
+ mempool_free(mbox, phba->mbox_mem_pool);
+}
+
+/**
* lpfc_dump_static_vport - Dump HBA's static vport information.
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -59,6 +135,7 @@ lpfc_dump_static_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb,
{
MAILBOX_t *mb;
struct lpfc_dmabuf *mp;
+ int rc;
mb = &pmb->u.mb;
@@ -77,22 +154,15 @@ lpfc_dump_static_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb,
return 0;
}
- /* For SLI4 HBAs driver need to allocate memory */
- mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (mp)
- mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
-
- if (!mp || !mp->virt) {
- kfree(mp);
+ rc = lpfc_mbox_rsrc_prep(phba, pmb);
+ if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2605 lpfc_dump_static_vport: memory"
- " allocation failed\n");
+ "2605 %s: memory allocation failed\n",
+ __func__);
return 1;
}
- memset(mp->virt, 0, LPFC_BPL_SIZE);
- INIT_LIST_HEAD(&mp->list);
- /* save address for completion */
- pmb->context2 = (uint8_t *) mp;
+
+ mp = pmb->ctx_buf;
mb->un.varWords[3] = putPaddrLow(mp->phys);
mb->un.varWords[4] = putPaddrHigh(mp->phys);
mb->un.varDmp.sli4_length = sizeof(struct static_vport_info);
@@ -134,10 +204,8 @@ lpfc_dump_mem(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, uint16_t offset,
uint16_t region_id)
{
MAILBOX_t *mb;
- void *ctx;
mb = &pmb->u.mb;
- ctx = pmb->context2;
/* Setup to dump VPD region */
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
@@ -149,7 +217,6 @@ lpfc_dump_mem(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, uint16_t offset,
mb->un.varDmp.word_cnt = (DMP_RSP_SIZE / sizeof (uint32_t));
mb->un.varDmp.co = 0;
mb->un.varDmp.resp_offset = 0;
- pmb->context2 = ctx;
mb->mbxOwner = OWN_HOST;
return;
}
@@ -166,11 +233,8 @@ void
lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
MAILBOX_t *mb;
- void *ctx;
mb = &pmb->u.mb;
- /* Save context so that we can restore after memset */
- ctx = pmb->context2;
/* Setup to dump VPD region */
memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
@@ -178,12 +242,12 @@ lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
mb->mbxOwner = OWN_HOST;
mb->un.varDmp.cv = 1;
mb->un.varDmp.type = DMP_NV_PARAMS;
- mb->un.varDmp.entry_index = 0;
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ mb->un.varDmp.entry_index = 0;
mb->un.varDmp.region_id = WAKE_UP_PARMS_REGION_ID;
mb->un.varDmp.word_cnt = WAKE_UP_PARMS_WORD_SIZE;
mb->un.varDmp.co = 0;
mb->un.varDmp.resp_offset = 0;
- pmb->context2 = ctx;
return;
}
@@ -288,9 +352,7 @@ lpfc_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb,
struct lpfc_dmabuf *mp)
{
MAILBOX_t *mb;
- struct lpfc_sli *psli;
- psli = &phba->sli;
mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
@@ -303,7 +365,7 @@ lpfc_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb,
/* Save address for later completion and set the owner to host so that
* the FW knows this mailbox is available for processing.
*/
- pmb->context1 = (uint8_t *)mp;
+ pmb->ctx_buf = mp;
mb->mbxOwner = OWN_HOST;
return (0);
}
@@ -361,7 +423,7 @@ lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
/* NEW_FEATURE
* SLI-2, Coalescing Response Feature.
*/
- if (phba->cfg_cr_delay) {
+ if (phba->cfg_cr_delay && (phba->sli_rev < LPFC_SLI_REV4)) {
mb->un.varCfgLnk.cr = 1;
mb->un.varCfgLnk.ci = 1;
mb->un.varCfgLnk.cr_delay = phba->cfg_cr_delay;
@@ -375,9 +437,14 @@ lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
mb->un.varCfgLnk.rttov = phba->fc_rttov;
mb->un.varCfgLnk.altov = phba->fc_altov;
mb->un.varCfgLnk.crtov = phba->fc_crtov;
- mb->un.varCfgLnk.citov = phba->fc_citov;
+ mb->un.varCfgLnk.cscn = 0;
+ if (phba->bbcredit_support && phba->cfg_enable_bbcr) {
+ mb->un.varCfgLnk.cscn = 1;
+ mb->un.varCfgLnk.bbscn = bf_get(lpfc_bbscn_def,
+ &phba->sli4_hba.bbscn_params);
+ }
- if (phba->cfg_ack0)
+ if (phba->cfg_ack0 && (phba->sli_rev < LPFC_SLI_REV4))
mb->un.varCfgLnk.ack0_enable = 1;
mb->mbxCommand = MBX_CONFIG_LINK;
@@ -423,7 +490,7 @@ lpfc_config_msi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
/*
- * SLI-3, Message Signaled Interrupt Fearure.
+ * SLI-3, Message Signaled Interrupt Feature.
*/
/* Multi-message attention configuration */
@@ -482,13 +549,11 @@ lpfc_init_link(struct lpfc_hba * phba,
LPFC_MBOXQ_t * pmb, uint32_t topology, uint32_t linkspeed)
{
lpfc_vpd_t *vpd;
- struct lpfc_sli *psli;
MAILBOX_t *mb;
mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
- psli = &phba->sli;
switch (topology) {
case FLAGS_TOPOLOGY_MODE_LOOP_PT:
mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP;
@@ -509,8 +574,18 @@ lpfc_init_link(struct lpfc_hba * phba,
break;
}
+ /* Topology handling for ASIC_GEN_NUM 0xC and later */
+ if ((phba->sli4_hba.pc_sli4_params.sli_family == LPFC_SLI_INTF_FAMILY_G6 ||
+ phba->sli4_hba.pc_sli4_params.if_type == LPFC_SLI_INTF_IF_TYPE_6) &&
+ !(phba->sli4_hba.pc_sli4_params.pls) &&
+ mb->un.varInitLnk.link_flags & FLAGS_TOPOLOGY_MODE_LOOP) {
+ mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT;
+ phba->cfg_topology = FLAGS_TOPOLOGY_MODE_PT_PT;
+ }
+
/* Enable asynchronous ABTS responses from firmware */
- mb->un.varInitLnk.link_flags |= FLAGS_IMED_ABORT;
+ if (phba->sli_rev == LPFC_SLI_REV3 && !phba->cfg_fcp_wait_abts_rsp)
+ mb->un.varInitLnk.link_flags |= FLAGS_IMED_ABORT;
/* NEW_FEATURE
* Setting up the link speed
@@ -542,6 +617,14 @@ lpfc_init_link(struct lpfc_hba * phba,
mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED;
mb->un.varInitLnk.link_speed = LINK_SPEED_16G;
break;
+ case LPFC_USER_LINK_SPEED_32G:
+ mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED;
+ mb->un.varInitLnk.link_speed = LINK_SPEED_32G;
+ break;
+ case LPFC_USER_LINK_SPEED_64G:
+ mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED;
+ mb->un.varInitLnk.link_speed = LINK_SPEED_64G;
+ break;
case LPFC_USER_LINK_SPEED_AUTO:
default:
mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO;
@@ -584,28 +667,21 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi)
{
struct lpfc_dmabuf *mp;
MAILBOX_t *mb;
- struct lpfc_sli *psli;
+ int rc;
- psli = &phba->sli;
- mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
- mb->mbxOwner = OWN_HOST;
-
/* Get a buffer to hold the HBAs Service Parameters */
-
- mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
- if (mp)
- mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
- if (!mp || !mp->virt) {
- kfree(mp);
- mb->mbxCommand = MBX_READ_SPARM64;
- /* READ_SPARAM: no buffers */
+ rc = lpfc_mbox_rsrc_prep(phba, pmb);
+ if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
"0301 READ_SPARAM: no buffers\n");
- return (1);
+ return 1;
}
- INIT_LIST_HEAD(&mp->list);
+
+ mp = pmb->ctx_buf;
+ mb = &pmb->u.mb;
+ mb->mbxOwner = OWN_HOST;
mb->mbxCommand = MBX_READ_SPARM64;
mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm);
mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys);
@@ -613,9 +689,6 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi)
if (phba->sli_rev >= LPFC_SLI_REV3)
mb->un.varRdSparm.vpi = phba->vpi_ids[vpi];
- /* save address for completion */
- pmb->context1 = mp;
-
return (0);
}
@@ -736,6 +809,7 @@ lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
MAILBOX_t *mb = &pmb->u.mb;
uint8_t *sparam;
struct lpfc_dmabuf *mp;
+ int rc;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
@@ -746,12 +820,10 @@ lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
mb->un.varRegLogin.vpi = phba->vpi_ids[vpi];
mb->un.varRegLogin.did = did;
mb->mbxOwner = OWN_HOST;
+
/* Get a buffer to hold NPorts Service Parameters */
- mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
- if (mp)
- mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
- if (!mp || !mp->virt) {
- kfree(mp);
+ rc = lpfc_mbox_rsrc_prep(phba, pmb);
+ if (rc) {
mb->mbxCommand = MBX_REG_LOGIN64;
/* REG_LOGIN: no buffers */
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
@@ -759,15 +831,13 @@ lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
"rpi x%x\n", vpi, did, rpi);
return 1;
}
- INIT_LIST_HEAD(&mp->list);
- sparam = mp->virt;
/* Copy param's into a new buffer */
+ mp = pmb->ctx_buf;
+ sparam = mp->virt;
memcpy(sparam, param, sizeof (struct serv_parm));
- /* save address for completion */
- pmb->context1 = (uint8_t *) mp;
-
+ /* Finish initializing the mailbox. */
mb->mbxCommand = MBX_REG_LOGIN64;
mb->un.varRegLogin.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm);
mb->un.varRegLogin.un.sp64.addrHigh = putPaddrHigh(mp->phys);
@@ -841,7 +911,7 @@ lpfc_sli4_unreg_all_rpis(struct lpfc_vport *vport)
mbox->u.mb.un.varUnregLogin.rsvd1 = 0x4000;
mbox->vport = vport;
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->context1 = NULL;
+ mbox->ctx_ndlp = NULL;
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED)
mempool_free(mbox, phba->mbox_mem_pool);
@@ -850,9 +920,7 @@ lpfc_sli4_unreg_all_rpis(struct lpfc_vport *vport)
/**
* lpfc_reg_vpi - Prepare a mailbox command for registering vport identifier
- * @phba: pointer to lpfc hba data structure.
- * @vpi: virtual N_Port identifier.
- * @sid: Fibre Channel S_ID (N_Port_ID assigned to a virtual N_Port).
+ * @vport: pointer to a vport object.
* @pmb: pointer to the driver internal queue element for mailbox command.
*
* The registration vport identifier mailbox command is used to activate a
@@ -874,7 +942,7 @@ lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb)
* Set the re-reg VPI bit for f/w to update the MAC address.
*/
if ((phba->sli_rev == LPFC_SLI_REV4) &&
- !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI))
+ !test_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag))
mb->un.varRegVpi.upd = 1;
mb->un.varRegVpi.vpi = phba->vpi_ids[vport->vpi];
@@ -948,46 +1016,49 @@ lpfc_config_pcb_setup(struct lpfc_hba * phba)
pcbp->maxRing = (psli->num_rings - 1);
for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
+ pring = &psli->sli3_ring[i];
- pring->sizeCiocb = phba->sli_rev == 3 ? SLI3_IOCB_CMD_SIZE:
+ pring->sli.sli3.sizeCiocb =
+ phba->sli_rev == 3 ? SLI3_IOCB_CMD_SIZE :
SLI2_IOCB_CMD_SIZE;
- pring->sizeRiocb = phba->sli_rev == 3 ? SLI3_IOCB_RSP_SIZE:
+ pring->sli.sli3.sizeRiocb =
+ phba->sli_rev == 3 ? SLI3_IOCB_RSP_SIZE :
SLI2_IOCB_RSP_SIZE;
/* A ring MUST have both cmd and rsp entries defined to be
valid */
- if ((pring->numCiocb == 0) || (pring->numRiocb == 0)) {
+ if ((pring->sli.sli3.numCiocb == 0) ||
+ (pring->sli.sli3.numRiocb == 0)) {
pcbp->rdsc[i].cmdEntries = 0;
pcbp->rdsc[i].rspEntries = 0;
pcbp->rdsc[i].cmdAddrHigh = 0;
pcbp->rdsc[i].rspAddrHigh = 0;
pcbp->rdsc[i].cmdAddrLow = 0;
pcbp->rdsc[i].rspAddrLow = 0;
- pring->cmdringaddr = NULL;
- pring->rspringaddr = NULL;
+ pring->sli.sli3.cmdringaddr = NULL;
+ pring->sli.sli3.rspringaddr = NULL;
continue;
}
/* Command ring setup for ring */
- pring->cmdringaddr = (void *)&phba->IOCBs[iocbCnt];
- pcbp->rdsc[i].cmdEntries = pring->numCiocb;
+ pring->sli.sli3.cmdringaddr = (void *)&phba->IOCBs[iocbCnt];
+ pcbp->rdsc[i].cmdEntries = pring->sli.sli3.numCiocb;
offset = (uint8_t *) &phba->IOCBs[iocbCnt] -
(uint8_t *) phba->slim2p.virt;
pdma_addr = phba->slim2p.phys + offset;
pcbp->rdsc[i].cmdAddrHigh = putPaddrHigh(pdma_addr);
pcbp->rdsc[i].cmdAddrLow = putPaddrLow(pdma_addr);
- iocbCnt += pring->numCiocb;
+ iocbCnt += pring->sli.sli3.numCiocb;
/* Response ring setup for ring */
- pring->rspringaddr = (void *) &phba->IOCBs[iocbCnt];
+ pring->sli.sli3.rspringaddr = (void *) &phba->IOCBs[iocbCnt];
- pcbp->rdsc[i].rspEntries = pring->numRiocb;
+ pcbp->rdsc[i].rspEntries = pring->sli.sli3.numRiocb;
offset = (uint8_t *)&phba->IOCBs[iocbCnt] -
(uint8_t *)phba->slim2p.virt;
pdma_addr = phba->slim2p.phys + offset;
pcbp->rdsc[i].rspAddrHigh = putPaddrHigh(pdma_addr);
pcbp->rdsc[i].rspAddrLow = putPaddrLow(pdma_addr);
- iocbCnt += pring->numRiocb;
+ iocbCnt += pring->sli.sli3.numRiocb;
}
}
@@ -1178,7 +1249,7 @@ lpfc_config_hbq(struct lpfc_hba *phba, uint32_t id,
/**
* lpfc_config_ring - Prepare a mailbox command for configuring an IOCB ring
* @phba: pointer to lpfc hba data structure.
- * @ring:
+ * @ring: ring number/index
* @pmb: pointer to the driver internal queue element for mailbox command.
*
* The configure ring mailbox command is used to configure an IOCB ring. This
@@ -1208,7 +1279,7 @@ lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
mb->un.varCfgRing.recvNotify = 1;
psli = &phba->sli;
- pring = &psli->ring[ring];
+ pring = &psli->sli3_ring[ring];
mb->un.varCfgRing.numMask = pring->num_mask;
mb->mbxCommand = MBX_CONFIG_RING;
mb->mbxOwner = OWN_HOST;
@@ -1278,8 +1349,6 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
if (phba->sli_rev == LPFC_SLI_REV3 && phba->vpd.sli3Feat.cerbm) {
if (phba->cfg_enable_bg)
mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
- if (phba->cfg_enable_dss)
- mb->un.varCfgPort.cdss = 1; /* Configure Security */
mb->un.varCfgPort.cerbm = 1; /* Request HBQs */
mb->un.varCfgPort.ccrp = 1; /* Command Ring Polling */
mb->un.varCfgPort.max_hbq = lpfc_sli_hbq_count();
@@ -1293,6 +1362,10 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
phba->sli_rev = LPFC_SLI_REV2;
mb->un.varCfgPort.sli_mode = phba->sli_rev;
+ /* If this is an SLI3 port, configure async status notification. */
+ if (phba->sli_rev == LPFC_SLI_REV3)
+ mb->un.varCfgPort.casabt = 1;
+
/* Now setup pcb */
phba->pcb->type = TYPE_NATIVE_SLI2;
phba->pcb->feature = FEATURE_INITIAL_SLI2;
@@ -1355,7 +1428,8 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
*/
if (phba->cfg_hostmem_hgp && phba->sli_rev != 3) {
- phba->host_gp = &phba->mbox->us.s2.host[0];
+ phba->host_gp = (struct lpfc_hgp __iomem *)
+ &phba->mbox->us.s2.host[0];
phba->hbq_put = NULL;
offset = (uint8_t *)&phba->mbox->us.s2.host -
(uint8_t *)phba->slim2p.virt;
@@ -1589,7 +1663,7 @@ lpfc_mbox_dev_check(struct lpfc_hba *phba)
/**
* lpfc_mbox_tmo_val - Retrieve mailbox command timeout value
* @phba: pointer to lpfc hba data structure.
- * @cmd: mailbox command code.
+ * @mboxq: pointer to the driver internal queue element for mailbox command.
*
* This routine retrieves the proper timeout value according to the mailbox
* command code.
@@ -1598,18 +1672,52 @@ lpfc_mbox_dev_check(struct lpfc_hba *phba)
* Timeout value to be used for the given mailbox command
**/
int
-lpfc_mbox_tmo_val(struct lpfc_hba *phba, int cmd)
+lpfc_mbox_tmo_val(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
- switch (cmd) {
+ MAILBOX_t *mbox = &mboxq->u.mb;
+ uint8_t subsys, opcode;
+
+ switch (mbox->mbxCommand) {
case MBX_WRITE_NV: /* 0x03 */
+ case MBX_DUMP_MEMORY: /* 0x17 */
case MBX_UPDATE_CFG: /* 0x1B */
case MBX_DOWN_LOAD: /* 0x1C */
case MBX_DEL_LD_ENTRY: /* 0x1D */
+ case MBX_WRITE_VPARMS: /* 0x32 */
case MBX_LOAD_AREA: /* 0x81 */
case MBX_WRITE_WWN: /* 0x98 */
case MBX_LOAD_EXP_ROM: /* 0x9C */
+ case MBX_ACCESS_VDATA: /* 0xA5 */
return LPFC_MBOX_TMO_FLASH_CMD;
case MBX_SLI4_CONFIG: /* 0x9b */
+ subsys = lpfc_sli_config_mbox_subsys_get(phba, mboxq);
+ opcode = lpfc_sli_config_mbox_opcode_get(phba, mboxq);
+ if (subsys == LPFC_MBOX_SUBSYSTEM_COMMON) {
+ switch (opcode) {
+ case LPFC_MBOX_OPCODE_READ_OBJECT:
+ case LPFC_MBOX_OPCODE_WRITE_OBJECT:
+ case LPFC_MBOX_OPCODE_READ_OBJECT_LIST:
+ case LPFC_MBOX_OPCODE_DELETE_OBJECT:
+ case LPFC_MBOX_OPCODE_GET_PROFILE_LIST:
+ case LPFC_MBOX_OPCODE_SET_ACT_PROFILE:
+ case LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG:
+ case LPFC_MBOX_OPCODE_SET_PROFILE_CONFIG:
+ case LPFC_MBOX_OPCODE_GET_FACTORY_PROFILE_CONFIG:
+ case LPFC_MBOX_OPCODE_GET_PROFILE_CAPACITIES:
+ case LPFC_MBOX_OPCODE_SEND_ACTIVATION:
+ case LPFC_MBOX_OPCODE_RESET_LICENSES:
+ case LPFC_MBOX_OPCODE_SET_BOOT_CONFIG:
+ case LPFC_MBOX_OPCODE_GET_VPD_DATA:
+ case LPFC_MBOX_OPCODE_SET_PHYSICAL_LINK_CONFIG:
+ return LPFC_MBOX_SLI4_CONFIG_EXTENDED_TMO;
+ }
+ }
+ if (subsys == LPFC_MBOX_SUBSYSTEM_FCOE) {
+ switch (opcode) {
+ case LPFC_MBOX_OPCODE_FCOE_SET_FCLINK_SETTINGS:
+ return LPFC_MBOX_SLI4_CONFIG_EXTENDED_TMO;
+ }
+ }
return LPFC_MBOX_SLI4_CONFIG_TMO;
}
return LPFC_MBOX_TMO;
@@ -1642,6 +1750,7 @@ lpfc_sli4_mbx_sge_set(struct lpfcMboxq *mbox, uint32_t sgentry,
* lpfc_sli4_mbx_sge_get - Get a sge entry from non-embedded mailbox command
* @mbox: pointer to lpfc mbox command.
* @sgentry: sge entry index.
+ * @sge: pointer to lpfc mailbox sge to load into.
*
* This routine gets an entry from the non-embedded mailbox command at the sge
* index location.
@@ -1664,7 +1773,9 @@ lpfc_sli4_mbx_sge_get(struct lpfcMboxq *mbox, uint32_t sgentry,
* @phba: pointer to lpfc hba data structure.
* @mbox: pointer to lpfc mbox command.
*
- * This routine frees SLI4 specific mailbox command for sending IOCTL command.
+ * This routine cleans up and releases an SLI4 mailbox command that was
+ * configured using lpfc_sli4_config. It accounts for the embedded and
+ * non-embedded config types.
**/
void
lpfc_sli4_mbox_cmd_free(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
@@ -1696,7 +1807,9 @@ lpfc_sli4_mbox_cmd_free(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
dma_free_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE,
mbox->sge_array->addr[sgentry], phyaddr);
}
- /* Free the sge address array memory */
+ /* Reinitialize the context pointers to avoid stale usage. */
+ mbox->ctx_buf = NULL;
+ memset(&mbox->ctx_u, 0, sizeof(mbox->ctx_u));
kfree(mbox->sge_array);
/* Finally, free the mailbox command itself */
mempool_free(mbox, phba->mbox_mem_pool);
@@ -1709,6 +1822,7 @@ lpfc_sli4_mbox_cmd_free(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
* @subsystem: The sli4 config sub mailbox subsystem.
* @opcode: The sli4 config sub mailbox command opcode.
* @length: Length of the sli4 config mailbox command (including sub-header).
+ * @emb: True if embedded mbox command should be setup.
*
* This routine sets up the header fields of SLI4 specific mailbox command
* for sending IOCTL command.
@@ -1769,12 +1883,12 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
* page, this is used as a priori size of SLI4_PAGE_SIZE for
* the later DMA memory free.
*/
- viraddr = dma_alloc_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE,
- &phyaddr, GFP_KERNEL);
+ viraddr = dma_alloc_coherent(&phba->pcidev->dev,
+ SLI4_PAGE_SIZE, &phyaddr,
+ GFP_KERNEL);
/* In case of malloc fails, proceed with whatever we have */
if (!viraddr)
break;
- memset(viraddr, 0, SLI4_PAGE_SIZE);
mbox->sge_array->addr[pagen] = viraddr;
/* Keep the first page for later sub-header construction */
if (pagen == 0)
@@ -1859,7 +1973,7 @@ lpfc_sli4_mbox_rsrc_extent(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
}
/* Complete the initialization for the particular Opcode. */
- opcode = lpfc_sli4_mbox_opcode_get(phba, mbox);
+ opcode = lpfc_sli_config_mbox_opcode_get(phba, mbox);
switch (opcode) {
case LPFC_MBOX_OPCODE_ALLOC_RSRC_EXTENT:
if (emb == LPFC_SLI4_MBX_EMBED)
@@ -1886,23 +2000,56 @@ lpfc_sli4_mbox_rsrc_extent(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
}
/**
- * lpfc_sli4_mbox_opcode_get - Get the opcode from a sli4 mailbox command
+ * lpfc_sli_config_mbox_subsys_get - Get subsystem from a sli_config mbox cmd
* @phba: pointer to lpfc hba data structure.
- * @mbox: pointer to lpfc mbox command.
+ * @mbox: pointer to lpfc mbox command queue entry.
*
- * This routine gets the opcode from a SLI4 specific mailbox command for
- * sending IOCTL command. If the mailbox command is not MBX_SLI4_CONFIG
- * (0x9B) or if the IOCTL sub-header is not present, opcode 0x0 shall be
+ * This routine gets the subsystem from a SLI4 specific SLI_CONFIG mailbox
+ * command. If the mailbox command is not MBX_SLI4_CONFIG (0x9B) or if the
+ * sub-header is not present, subsystem LPFC_MBOX_SUBSYSTEM_NA (0x0) shall
+ * be returned.
+ **/
+uint8_t
+lpfc_sli_config_mbox_subsys_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ struct lpfc_mbx_sli4_config *sli4_cfg;
+ union lpfc_sli4_cfg_shdr *cfg_shdr;
+
+ if (mbox->u.mb.mbxCommand != MBX_SLI4_CONFIG)
+ return LPFC_MBOX_SUBSYSTEM_NA;
+ sli4_cfg = &mbox->u.mqe.un.sli4_config;
+
+ /* For embedded mbox command, get opcode from embedded sub-header*/
+ if (bf_get(lpfc_mbox_hdr_emb, &sli4_cfg->header.cfg_mhdr)) {
+ cfg_shdr = &mbox->u.mqe.un.sli4_config.header.cfg_shdr;
+ return bf_get(lpfc_mbox_hdr_subsystem, &cfg_shdr->request);
+ }
+
+ /* For non-embedded mbox command, get opcode from first dma page */
+ if (unlikely(!mbox->sge_array))
+ return LPFC_MBOX_SUBSYSTEM_NA;
+ cfg_shdr = (union lpfc_sli4_cfg_shdr *)mbox->sge_array->addr[0];
+ return bf_get(lpfc_mbox_hdr_subsystem, &cfg_shdr->request);
+}
+
+/**
+ * lpfc_sli_config_mbox_opcode_get - Get opcode from a sli_config mbox cmd
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to lpfc mbox command queue entry.
+ *
+ * This routine gets the opcode from a SLI4 specific SLI_CONFIG mailbox
+ * command. If the mailbox command is not MBX_SLI4_CONFIG (0x9B) or if
+ * the sub-header is not present, opcode LPFC_MBOX_OPCODE_NA (0x0) be
* returned.
**/
uint8_t
-lpfc_sli4_mbox_opcode_get(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+lpfc_sli_config_mbox_opcode_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
{
struct lpfc_mbx_sli4_config *sli4_cfg;
union lpfc_sli4_cfg_shdr *cfg_shdr;
if (mbox->u.mb.mbxCommand != MBX_SLI4_CONFIG)
- return 0;
+ return LPFC_MBOX_OPCODE_NA;
sli4_cfg = &mbox->u.mqe.un.sli4_config;
/* For embedded mbox command, get opcode from embedded sub-header*/
@@ -1913,7 +2060,7 @@ lpfc_sli4_mbox_opcode_get(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
/* For non-embedded mbox command, get opcode from first dma page */
if (unlikely(!mbox->sge_array))
- return 0;
+ return LPFC_MBOX_OPCODE_NA;
cfg_shdr = (union lpfc_sli4_cfg_shdr *)mbox->sge_array->addr[0];
return bf_get(lpfc_mbox_hdr_opcode, &cfg_shdr->request);
}
@@ -1921,6 +2068,7 @@ lpfc_sli4_mbox_opcode_get(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
/**
* lpfc_sli4_mbx_read_fcf_rec - Allocate and construct read fcf mbox cmd
* @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to lpfc mbox command.
* @fcf_index: index to fcf table.
*
* This routine routine allocates and constructs non-embedded mailbox command
@@ -1935,7 +2083,6 @@ lpfc_sli4_mbx_read_fcf_rec(struct lpfc_hba *phba,
uint16_t fcf_index)
{
void *virt_addr;
- dma_addr_t phys_addr;
uint8_t *bytep;
struct lpfc_mbx_sge sge;
uint32_t alloc_len, req_len;
@@ -1964,7 +2111,6 @@ lpfc_sli4_mbx_read_fcf_rec(struct lpfc_hba *phba,
* routine only uses a single SGE.
*/
lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
- phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
virt_addr = mboxq->sge_array->addr[0];
read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
@@ -1979,6 +2125,7 @@ lpfc_sli4_mbx_read_fcf_rec(struct lpfc_hba *phba,
/**
* lpfc_request_features: Configure SLI4 REQUEST_FEATURES mailbox
+ * @phba: pointer to lpfc hba data structure.
* @mboxq: pointer to lpfc mbox command.
*
* This routine sets up the mailbox for an SLI4 REQUEST_FEATURES
@@ -2003,6 +2150,18 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
if (phba->max_vpi && phba->cfg_enable_npiv)
bf_set(lpfc_mbx_rq_ftr_rq_npiv, &mboxq->u.mqe.un.req_ftrs, 1);
+ if (phba->nvmet_support) {
+ bf_set(lpfc_mbx_rq_ftr_rq_mrqp, &mboxq->u.mqe.un.req_ftrs, 1);
+ /* iaab/iaar NOT set for now */
+ bf_set(lpfc_mbx_rq_ftr_rq_iaab, &mboxq->u.mqe.un.req_ftrs, 0);
+ bf_set(lpfc_mbx_rq_ftr_rq_iaar, &mboxq->u.mqe.un.req_ftrs, 0);
+ }
+
+ /* Enable Application Services Header for appheader VMID */
+ if (phba->cfg_vmid_app_header) {
+ bf_set(lpfc_mbx_rq_ftr_rq_ashdr, &mboxq->u.mqe.un.req_ftrs, 1);
+ bf_set(lpfc_ftr_ashdr, &phba->sli4_hba.sli4_flags, 1);
+ }
return;
}
@@ -2031,7 +2190,7 @@ lpfc_init_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport)
bf_set(lpfc_init_vfi_vp, init_vfi, 1);
bf_set(lpfc_init_vfi_vfi, init_vfi,
vport->phba->sli4_hba.vfi_ids[vport->vfi]);
- bf_set(lpfc_init_vpi_vpi, init_vfi,
+ bf_set(lpfc_init_vfi_vpi, init_vfi,
vport->phba->vpi_ids[vport->vpi]);
bf_set(lpfc_init_vfi_fcfi, init_vfi,
vport->phba->fcf.fcfi);
@@ -2052,25 +2211,68 @@ void
lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
{
struct lpfc_mbx_reg_vfi *reg_vfi;
+ struct lpfc_hba *phba = vport->phba;
+ uint8_t bbscn_fabric = 0, bbscn_max = 0, bbscn_def = 0;
memset(mbox, 0, sizeof(*mbox));
reg_vfi = &mbox->u.mqe.un.reg_vfi;
bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_VFI);
bf_set(lpfc_reg_vfi_vp, reg_vfi, 1);
bf_set(lpfc_reg_vfi_vfi, reg_vfi,
- vport->phba->sli4_hba.vfi_ids[vport->vfi]);
- bf_set(lpfc_reg_vfi_fcfi, reg_vfi, vport->phba->fcf.fcfi);
- bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->phba->vpi_ids[vport->vpi]);
+ phba->sli4_hba.vfi_ids[vport->vfi]);
+ bf_set(lpfc_reg_vfi_fcfi, reg_vfi, phba->fcf.fcfi);
+ bf_set(lpfc_reg_vfi_vpi, reg_vfi, phba->vpi_ids[vport->vpi]);
memcpy(reg_vfi->wwn, &vport->fc_portname, sizeof(struct lpfc_name));
reg_vfi->wwn[0] = cpu_to_le32(reg_vfi->wwn[0]);
reg_vfi->wwn[1] = cpu_to_le32(reg_vfi->wwn[1]);
- reg_vfi->e_d_tov = vport->phba->fc_edtov;
- reg_vfi->r_a_tov = vport->phba->fc_ratov;
- reg_vfi->bde.addrHigh = putPaddrHigh(phys);
- reg_vfi->bde.addrLow = putPaddrLow(phys);
- reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam);
- reg_vfi->bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ reg_vfi->e_d_tov = phba->fc_edtov;
+ reg_vfi->r_a_tov = phba->fc_ratov;
+ if (phys) {
+ reg_vfi->bde.addrHigh = putPaddrHigh(phys);
+ reg_vfi->bde.addrLow = putPaddrLow(phys);
+ reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam);
+ reg_vfi->bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ }
bf_set(lpfc_reg_vfi_nport_id, reg_vfi, vport->fc_myDID);
+
+ /* Only FC supports upd bit */
+ if ((phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC) &&
+ test_bit(FC_VFI_REGISTERED, &vport->fc_flag) &&
+ (!phba->fc_topology_changed))
+ bf_set(lpfc_reg_vfi_upd, reg_vfi, 1);
+
+ bf_set(lpfc_reg_vfi_bbcr, reg_vfi, 0);
+ bf_set(lpfc_reg_vfi_bbscn, reg_vfi, 0);
+ bbscn_fabric = (phba->fc_fabparam.cmn.bbRcvSizeMsb >> 4) & 0xF;
+
+ if (phba->bbcredit_support && phba->cfg_enable_bbcr &&
+ bbscn_fabric != 0) {
+ bbscn_max = bf_get(lpfc_bbscn_max,
+ &phba->sli4_hba.bbscn_params);
+ if (bbscn_fabric <= bbscn_max) {
+ bbscn_def = bf_get(lpfc_bbscn_def,
+ &phba->sli4_hba.bbscn_params);
+
+ if (bbscn_fabric > bbscn_def)
+ bf_set(lpfc_reg_vfi_bbscn, reg_vfi,
+ bbscn_fabric);
+ else
+ bf_set(lpfc_reg_vfi_bbscn, reg_vfi, bbscn_def);
+
+ bf_set(lpfc_reg_vfi_bbcr, reg_vfi, 1);
+ }
+ }
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_MBOX,
+ "3134 Register VFI, mydid:x%x, fcfi:%d, "
+ "vfi:%d, vpi:%d, fc_pname:%x%x fc_flag:x%lx "
+ "port_state:x%x topology chg:%d bbscn_fabric :%d\n",
+ vport->fc_myDID,
+ phba->fcf.fcfi,
+ phba->sli4_hba.vfi_ids[vport->vfi],
+ phba->vpi_ids[vport->vpi],
+ reg_vfi->wwn[0], reg_vfi->wwn[1], vport->fc_flag,
+ vport->port_state, phba->fc_topology_changed,
+ bbscn_fabric);
}
/**
@@ -2117,51 +2319,178 @@ lpfc_unreg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport)
}
/**
- * lpfc_dump_fcoe_param - Dump config region 23 to get FCoe parameters.
+ * lpfc_sli4_dump_cfg_rg23 - Dump sli4 port config region 23
* @phba: pointer to the hba structure containing.
* @mbox: pointer to lpfc mbox command to initialize.
*
- * This function create a SLI4 dump mailbox command to dump FCoE
- * parameters stored in region 23.
+ * This function create a SLI4 dump mailbox command to dump configure
+ * region 23.
**/
int
-lpfc_dump_fcoe_param(struct lpfc_hba *phba,
- struct lpfcMboxq *mbox)
+lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
{
struct lpfc_dmabuf *mp = NULL;
MAILBOX_t *mb;
+ int rc;
memset(mbox, 0, sizeof(*mbox));
mb = &mbox->u.mb;
- mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (mp)
- mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
-
- if (!mp || !mp->virt) {
- kfree(mp);
- /* dump_fcoe_param failed to allocate memory */
+ rc = lpfc_mbox_rsrc_prep(phba, mbox);
+ if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
- "2569 lpfc_dump_fcoe_param: memory"
- " allocation failed\n");
+ "2569 %s: memory allocation failed\n",
+ __func__);
return 1;
}
- memset(mp->virt, 0, LPFC_BPL_SIZE);
- INIT_LIST_HEAD(&mp->list);
-
- /* save address for completion */
- mbox->context1 = (uint8_t *) mp;
-
mb->mbxCommand = MBX_DUMP_MEMORY;
mb->un.varDmp.type = DMP_NV_PARAMS;
mb->un.varDmp.region_id = DMP_REGION_23;
mb->un.varDmp.sli4_length = DMP_RGN23_SIZE;
+ mp = mbox->ctx_buf;
mb->un.varWords[3] = putPaddrLow(mp->phys);
mb->un.varWords[4] = putPaddrHigh(mp->phys);
return 0;
}
+static void
+lpfc_mbx_cmpl_rdp_link_stat(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ MAILBOX_t *mb;
+ int rc = FAILURE;
+ struct lpfc_rdp_context *rdp_context = mboxq->ctx_u.rdp;
+
+ mb = &mboxq->u.mb;
+ if (mb->mbxStatus)
+ goto mbx_failed;
+
+ memcpy(&rdp_context->link_stat, &mb->un.varRdLnk, sizeof(READ_LNK_VAR));
+
+ rc = SUCCESS;
+
+mbx_failed:
+ lpfc_mbox_rsrc_cleanup(phba, mboxq, MBOX_THD_UNLOCKED);
+ rdp_context->cmpl(phba, rdp_context, rc);
+}
+
+static void
+lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ struct lpfc_dmabuf *mp = mbox->ctx_buf;
+ struct lpfc_rdp_context *rdp_context = mbox->ctx_u.rdp;
+
+ if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+ goto error_mbox_free;
+
+ lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2,
+ DMP_SFF_PAGE_A2_SIZE);
+
+ lpfc_read_lnk_stat(phba, mbox);
+ mbox->vport = rdp_context->ndlp->vport;
+
+ /* Save the dma buffer for cleanup in the final completion. */
+ mbox->ctx_buf = mp;
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_link_stat;
+ mbox->ctx_u.rdp = rdp_context;
+ if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == MBX_NOT_FINISHED)
+ goto error_mbox_free;
+
+ return;
+
+error_mbox_free:
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+void
+lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ int rc;
+ struct lpfc_dmabuf *mp = mbox->ctx_buf;
+ struct lpfc_rdp_context *rdp_context = mbox->ctx_u.rdp;
+
+ if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+ goto error;
+
+ lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0,
+ DMP_SFF_PAGE_A0_SIZE);
+
+ memset(mbox, 0, sizeof(*mbox));
+
+ memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE);
+ INIT_LIST_HEAD(&mp->list);
+
+ /* save address for completion */
+ mbox->ctx_buf = mp;
+ mbox->vport = rdp_context->ndlp->vport;
+
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+ bf_set(lpfc_mbx_memory_dump_type3_type,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+ bf_set(lpfc_mbx_memory_dump_type3_link,
+ &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+ bf_set(lpfc_mbx_memory_dump_type3_page_no,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2);
+ bf_set(lpfc_mbx_memory_dump_type3_length,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE);
+ mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+ mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a2;
+ mbox->ctx_u.rdp = rdp_context;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED)
+ goto error;
+
+ return;
+
+error:
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+
+/*
+ * lpfc_sli4_dump_page_a0 - Dump sli4 read SFP Diagnostic.
+ * @phba: pointer to the hba structure containing.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * This function create a SLI4 dump mailbox command to dump configure
+ * type 3 page 0xA0.
+ */
+int
+lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+ int rc;
+ struct lpfc_dmabuf *mp = NULL;
+
+ memset(mbox, 0, sizeof(*mbox));
+
+ rc = lpfc_mbox_rsrc_prep(phba, mbox);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+ "3569 dump type 3 page 0xA0 allocation failed\n");
+ return 1;
+ }
+
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+ bf_set(lpfc_mbx_memory_dump_type3_type,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+ bf_set(lpfc_mbx_memory_dump_type3_link,
+ &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+ bf_set(lpfc_mbx_memory_dump_type3_page_no,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A0);
+ bf_set(lpfc_mbx_memory_dump_type3_length,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE);
+
+ mp = mbox->ctx_buf;
+ mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+ mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+ return 0;
+}
+
/**
* lpfc_reg_fcfi - Initialize the REG_FCFI mailbox command
* @phba: pointer to the hba structure containing the FCF index and RQ ID.
@@ -2172,7 +2501,7 @@ lpfc_dump_fcoe_param(struct lpfc_hba *phba,
* information via a READ_FCF mailbox command. This mailbox command also is used
* to indicate where received unsolicited frames from this FCF will be sent. By
* default this routine will set up the FCF to forward all unsolicited frames
- * the the RQ ID passed in the @phba. This can be overridden by the caller for
+ * to the RQ ID passed in the @phba. This can be overridden by the caller for
* more complicated setups.
**/
void
@@ -2183,14 +2512,47 @@ lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
memset(mbox, 0, sizeof(*mbox));
reg_fcfi = &mbox->u.mqe.un.reg_fcfi;
bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_FCFI);
- bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi, phba->sli4_hba.hdr_rq->queue_id);
- bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID);
+ if (phba->nvmet_support == 0) {
+ bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi,
+ phba->sli4_hba.hdr_rq->queue_id);
+ /* Match everything - rq_id0 */
+ bf_set(lpfc_reg_fcfi_type_match0, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_type_mask0, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_rctl_match0, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_rctl_mask0, reg_fcfi, 0);
+
+ bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID);
+
+ /* addr mode is bit wise inverted value of fcf addr_mode */
+ if (test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ bf_set(lpfc_reg_fcfi_mam, reg_fcfi,
+ (~phba->fcf.addr_mode) & 0x3);
+ }
+ } else {
+ /* This is ONLY for NVMET MRQ == 1 */
+ if (phba->cfg_nvmet_mrq != 1)
+ return;
+
+ bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi,
+ phba->sli4_hba.nvmet_mrq_hdr[0]->queue_id);
+ /* Match type FCP - rq_id0 */
+ bf_set(lpfc_reg_fcfi_type_match0, reg_fcfi, FC_TYPE_FCP);
+ bf_set(lpfc_reg_fcfi_type_mask0, reg_fcfi, 0xff);
+ bf_set(lpfc_reg_fcfi_rctl_match0, reg_fcfi,
+ FC_RCTL_DD_UNSOL_CMD);
+
+ bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi,
+ phba->sli4_hba.hdr_rq->queue_id);
+ /* Match everything else - rq_id1 */
+ bf_set(lpfc_reg_fcfi_type_match1, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_type_mask1, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_rctl_match1, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_rctl_mask1, reg_fcfi, 0);
+ }
bf_set(lpfc_reg_fcfi_rq_id2, reg_fcfi, REG_FCF_INVALID_QID);
bf_set(lpfc_reg_fcfi_rq_id3, reg_fcfi, REG_FCF_INVALID_QID);
bf_set(lpfc_reg_fcfi_info_index, reg_fcfi,
phba->fcf.current_rec.fcf_indx);
- /* reg_fcf addr mode is bit wise inverted value of fcf addr_mode */
- bf_set(lpfc_reg_fcfi_mam, reg_fcfi, (~phba->fcf.addr_mode) & 0x3);
if (phba->fcf.current_rec.vlan_id != LPFC_FCOE_NULL_VID) {
bf_set(lpfc_reg_fcfi_vv, reg_fcfi, 1);
bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi,
@@ -2199,6 +2561,70 @@ lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
}
/**
+ * lpfc_reg_fcfi_mrq - Initialize the REG_FCFI_MRQ mailbox command
+ * @phba: pointer to the hba structure containing the FCF index and RQ ID.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @mode: 0 to register FCFI, 1 to register MRQs
+ *
+ * The REG_FCFI_MRQ mailbox command supports Fibre Channel Forwarders (FCFs).
+ * The SLI Host uses the command to activate an FCF after it has acquired FCF
+ * information via a READ_FCF mailbox command. This mailbox command also is used
+ * to indicate where received unsolicited frames from this FCF will be sent. By
+ * default this routine will set up the FCF to forward all unsolicited frames
+ * to the RQ ID passed in the @phba. This can be overridden by the caller for
+ * more complicated setups.
+ **/
+void
+lpfc_reg_fcfi_mrq(struct lpfc_hba *phba, struct lpfcMboxq *mbox, int mode)
+{
+ struct lpfc_mbx_reg_fcfi_mrq *reg_fcfi;
+
+ /* This is ONLY for MRQ */
+ if (phba->cfg_nvmet_mrq <= 1)
+ return;
+
+ memset(mbox, 0, sizeof(*mbox));
+ reg_fcfi = &mbox->u.mqe.un.reg_fcfi_mrq;
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_FCFI_MRQ);
+ if (mode == 0) {
+ bf_set(lpfc_reg_fcfi_mrq_info_index, reg_fcfi,
+ phba->fcf.current_rec.fcf_indx);
+ if (phba->fcf.current_rec.vlan_id != LPFC_FCOE_NULL_VID) {
+ bf_set(lpfc_reg_fcfi_mrq_vv, reg_fcfi, 1);
+ bf_set(lpfc_reg_fcfi_mrq_vlan_tag, reg_fcfi,
+ phba->fcf.current_rec.vlan_id);
+ }
+ return;
+ }
+
+ bf_set(lpfc_reg_fcfi_mrq_rq_id0, reg_fcfi,
+ phba->sli4_hba.nvmet_mrq_hdr[0]->queue_id);
+ /* Match NVME frames of type FCP (protocol NVME) - rq_id0 */
+ bf_set(lpfc_reg_fcfi_mrq_type_match0, reg_fcfi, FC_TYPE_FCP);
+ bf_set(lpfc_reg_fcfi_mrq_type_mask0, reg_fcfi, 0xff);
+ bf_set(lpfc_reg_fcfi_mrq_rctl_match0, reg_fcfi, FC_RCTL_DD_UNSOL_CMD);
+ bf_set(lpfc_reg_fcfi_mrq_rctl_mask0, reg_fcfi, 0xff);
+ bf_set(lpfc_reg_fcfi_mrq_ptc0, reg_fcfi, 1);
+ bf_set(lpfc_reg_fcfi_mrq_pt0, reg_fcfi, 1);
+
+ bf_set(lpfc_reg_fcfi_mrq_policy, reg_fcfi, 3); /* NVME connection id */
+ bf_set(lpfc_reg_fcfi_mrq_mode, reg_fcfi, 1);
+ bf_set(lpfc_reg_fcfi_mrq_filter, reg_fcfi, 1); /* rq_id0 */
+ bf_set(lpfc_reg_fcfi_mrq_npairs, reg_fcfi, phba->cfg_nvmet_mrq);
+
+ bf_set(lpfc_reg_fcfi_mrq_rq_id1, reg_fcfi,
+ phba->sli4_hba.hdr_rq->queue_id);
+ /* Match everything - rq_id1 */
+ bf_set(lpfc_reg_fcfi_mrq_type_match1, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_mrq_type_mask1, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_mrq_rctl_match1, reg_fcfi, 0);
+ bf_set(lpfc_reg_fcfi_mrq_rctl_mask1, reg_fcfi, 0);
+
+ bf_set(lpfc_reg_fcfi_mrq_rq_id2, reg_fcfi, REG_FCF_INVALID_QID);
+ bf_set(lpfc_reg_fcfi_mrq_rq_id3, reg_fcfi, REG_FCF_INVALID_QID);
+}
+
+/**
* lpfc_unreg_fcfi - Initialize the UNREG_FCFI mailbox command
* @mbox: pointer to lpfc mbox command to initialize.
* @fcfi: FCFI to be unregistered.
@@ -2237,39 +2663,3 @@ lpfc_resume_rpi(struct lpfcMboxq *mbox, struct lpfc_nodelist *ndlp)
resume_rpi->event_tag = ndlp->phba->fc_eventTag;
}
-/**
- * lpfc_supported_pages - Initialize the PORT_CAPABILITIES supported pages
- * mailbox command.
- * @mbox: pointer to lpfc mbox command to initialize.
- *
- * The PORT_CAPABILITIES supported pages mailbox command is issued to
- * retrieve the particular feature pages supported by the port.
- **/
-void
-lpfc_supported_pages(struct lpfcMboxq *mbox)
-{
- struct lpfc_mbx_supp_pages *supp_pages;
-
- memset(mbox, 0, sizeof(*mbox));
- supp_pages = &mbox->u.mqe.un.supp_pages;
- bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_PORT_CAPABILITIES);
- bf_set(cpn, supp_pages, LPFC_SUPP_PAGES);
-}
-
-/**
- * lpfc_pc_sli4_params - Initialize the PORT_CAPABILITIES SLI4 Params mbox cmd.
- * @mbox: pointer to lpfc mbox command to initialize.
- *
- * The PORT_CAPABILITIES SLI4 parameters mailbox command is issued to
- * retrieve the particular SLI4 features supported by the port.
- **/
-void
-lpfc_pc_sli4_params(struct lpfcMboxq *mbox)
-{
- struct lpfc_mbx_pc_sli4_params *sli4_params;
-
- memset(mbox, 0, sizeof(*mbox));
- sli4_params = &mbox->u.mqe.un.sli4_params;
- bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_PORT_CAPABILITIES);
- bf_set(cpn, sli4_params, LPFC_SLI4_PARAMETERS);
-}
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
index 10d5b5e41499..2697da3248b3 100644
--- a/drivers/scsi/lpfc/lpfc_mem.c
+++ b/drivers/scsi/lpfc/lpfc_mem.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2023 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2014 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -24,10 +26,10 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport_fc.h>
-
-#include <scsi/scsi.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -35,20 +37,65 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
#include "lpfc_crtn.h"
+#include "lpfc_logmsg.h"
#define LPFC_MBUF_POOL_SIZE 64 /* max elements in MBUF safety pool */
#define LPFC_MEM_POOL_SIZE 64 /* max elem in non-DMA safety pool */
+#define LPFC_DEVICE_DATA_POOL_SIZE 64 /* max elements in device data pool */
+#define LPFC_RRQ_POOL_SIZE 256 /* max elements in non-DMA pool */
+#define LPFC_MBX_POOL_SIZE 256 /* max elements in MBX non-DMA pool */
+
+/* lpfc_mbox_free_sli_mbox
+ *
+ * @phba: HBA to free memory for
+ * @mbox: mailbox command to free
+ *
+ * This routine detects the mbox type and calls the correct
+ * free routine to fully release all associated memory.
+ */
+static void
+lpfc_mem_free_sli_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ /* Detect if the caller's mbox is an SLI4_CONFIG type. If so, this
+ * mailbox type requires a different cleanup routine. Otherwise, the
+ * mailbox is just an mbuf and mem_pool release.
+ */
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ bf_get(lpfc_mqe_command, &mbox->u.mqe) == MBX_SLI4_CONFIG) {
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ } else {
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ }
+}
+int
+lpfc_mem_alloc_active_rrq_pool_s4(struct lpfc_hba *phba) {
+ size_t bytes;
+ int max_xri = phba->sli4_hba.max_cfg_param.max_xri;
+
+ if (max_xri <= 0)
+ return -ENOMEM;
+ bytes = ((BITS_PER_LONG - 1 + max_xri) / BITS_PER_LONG) *
+ sizeof(unsigned long);
+ phba->cfg_rrq_xri_bitmap_sz = bytes;
+ phba->active_rrq_pool = mempool_create_kmalloc_pool(LPFC_MEM_POOL_SIZE,
+ bytes);
+ if (!phba->active_rrq_pool)
+ return -ENOMEM;
+ else
+ return 0;
+}
/**
* lpfc_mem_alloc - create and allocate all PCI and memory pools
* @phba: HBA to allocate pools for
+ * @align: alignment requirement for blocks; must be a power of two
*
- * Description: Creates and allocates PCI pools lpfc_scsi_dma_buf_pool,
- * lpfc_mbuf_pool, lpfc_hrb_pool. Creates and allocates kmalloc-backed mempools
+ * Description: Creates and allocates PCI pools lpfc_mbuf_pool,
+ * lpfc_hrb_pool. Creates and allocates kmalloc-backed mempools
* for LPFC_MBOXQ_t and lpfc_nodelist. Also allocates the VPI bitmask.
*
* Notes: Not interrupt-safe. Must be called with no locks held. If any
@@ -64,36 +111,23 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align)
struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
int i;
- if (phba->sli_rev == LPFC_SLI_REV4)
- phba->lpfc_scsi_dma_buf_pool =
- pci_pool_create("lpfc_scsi_dma_buf_pool",
- phba->pcidev,
- phba->cfg_sg_dma_buf_size,
- phba->cfg_sg_dma_buf_size,
- 0);
- else
- phba->lpfc_scsi_dma_buf_pool =
- pci_pool_create("lpfc_scsi_dma_buf_pool",
- phba->pcidev, phba->cfg_sg_dma_buf_size,
- align, 0);
- if (!phba->lpfc_scsi_dma_buf_pool)
- goto fail;
- phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev,
+ phba->lpfc_mbuf_pool = dma_pool_create("lpfc_mbuf_pool", &phba->pcidev->dev,
LPFC_BPL_SIZE,
align, 0);
if (!phba->lpfc_mbuf_pool)
- goto fail_free_dma_buf_pool;
+ goto fail;
- pool->elements = kmalloc(sizeof(struct lpfc_dmabuf) *
- LPFC_MBUF_POOL_SIZE, GFP_KERNEL);
+ pool->elements = kmalloc_array(LPFC_MBUF_POOL_SIZE,
+ sizeof(struct lpfc_dmabuf),
+ GFP_KERNEL);
if (!pool->elements)
goto fail_free_lpfc_mbuf_pool;
pool->max_count = 0;
pool->current_count = 0;
for ( i = 0; i < LPFC_MBUF_POOL_SIZE; i++) {
- pool->elements[i].virt = pci_pool_alloc(phba->lpfc_mbuf_pool,
+ pool->elements[i].virt = dma_pool_alloc(phba->lpfc_mbuf_pool,
GFP_KERNEL, &pool->elements[i].phys);
if (!pool->elements[i].virt)
goto fail_free_mbuf_pool;
@@ -101,8 +135,8 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align)
pool->current_count++;
}
- phba->mbox_mem_pool = mempool_create_kmalloc_pool(LPFC_MEM_POOL_SIZE,
- sizeof(LPFC_MBOXQ_t));
+ phba->mbox_mem_pool = mempool_create_kmalloc_pool(LPFC_MBX_POOL_SIZE,
+ sizeof(LPFC_MBOXQ_t));
if (!phba->mbox_mem_pool)
goto fail_free_mbuf_pool;
@@ -113,34 +147,47 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align)
if (phba->sli_rev == LPFC_SLI_REV4) {
phba->rrq_pool =
- mempool_create_kmalloc_pool(LPFC_MEM_POOL_SIZE,
+ mempool_create_kmalloc_pool(LPFC_RRQ_POOL_SIZE,
sizeof(struct lpfc_node_rrq));
if (!phba->rrq_pool)
goto fail_free_nlp_mem_pool;
- phba->lpfc_hrb_pool = pci_pool_create("lpfc_hrb_pool",
- phba->pcidev,
+ phba->lpfc_hrb_pool = dma_pool_create("lpfc_hrb_pool",
+ &phba->pcidev->dev,
LPFC_HDR_BUF_SIZE, align, 0);
if (!phba->lpfc_hrb_pool)
goto fail_free_rrq_mem_pool;
- phba->lpfc_drb_pool = pci_pool_create("lpfc_drb_pool",
- phba->pcidev,
+ phba->lpfc_drb_pool = dma_pool_create("lpfc_drb_pool",
+ &phba->pcidev->dev,
LPFC_DATA_BUF_SIZE, align, 0);
if (!phba->lpfc_drb_pool)
goto fail_free_hrb_pool;
phba->lpfc_hbq_pool = NULL;
} else {
- phba->lpfc_hbq_pool = pci_pool_create("lpfc_hbq_pool",
- phba->pcidev, LPFC_BPL_SIZE, align, 0);
+ phba->lpfc_hbq_pool = dma_pool_create("lpfc_hbq_pool",
+ &phba->pcidev->dev, LPFC_BPL_SIZE, align, 0);
if (!phba->lpfc_hbq_pool)
goto fail_free_nlp_mem_pool;
phba->lpfc_hrb_pool = NULL;
phba->lpfc_drb_pool = NULL;
}
+ if (phba->cfg_EnableXLane) {
+ phba->device_data_mem_pool = mempool_create_kmalloc_pool(
+ LPFC_DEVICE_DATA_POOL_SIZE,
+ sizeof(struct lpfc_device_data));
+ if (!phba->device_data_mem_pool)
+ goto fail_free_drb_pool;
+ } else {
+ phba->device_data_mem_pool = NULL;
+ }
+
return 0;
+fail_free_drb_pool:
+ dma_pool_destroy(phba->lpfc_drb_pool);
+ phba->lpfc_drb_pool = NULL;
fail_free_hrb_pool:
- pci_pool_destroy(phba->lpfc_hrb_pool);
+ dma_pool_destroy(phba->lpfc_hrb_pool);
phba->lpfc_hrb_pool = NULL;
fail_free_rrq_mem_pool:
mempool_destroy(phba->rrq_pool);
@@ -153,19 +200,31 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align)
phba->mbox_mem_pool = NULL;
fail_free_mbuf_pool:
while (i--)
- pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
+ dma_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
pool->elements[i].phys);
kfree(pool->elements);
fail_free_lpfc_mbuf_pool:
- pci_pool_destroy(phba->lpfc_mbuf_pool);
+ dma_pool_destroy(phba->lpfc_mbuf_pool);
phba->lpfc_mbuf_pool = NULL;
- fail_free_dma_buf_pool:
- pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
- phba->lpfc_scsi_dma_buf_pool = NULL;
fail:
return -ENOMEM;
}
+int
+lpfc_nvmet_mem_alloc(struct lpfc_hba *phba)
+{
+ phba->lpfc_nvmet_drb_pool =
+ dma_pool_create("lpfc_nvmet_drb_pool",
+ &phba->pcidev->dev, LPFC_NVMET_DATA_BUF_SIZE,
+ SGL_ALIGN_SZ, 0);
+ if (!phba->lpfc_nvmet_drb_pool) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "6024 Can't enable NVME Target - no memory\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
/**
* lpfc_mem_free - Frees memory allocated by lpfc_mem_alloc
* @phba: HBA to free memory for
@@ -180,23 +239,32 @@ lpfc_mem_free(struct lpfc_hba *phba)
{
int i;
struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+ struct lpfc_device_data *device_data;
/* Free HBQ pools */
lpfc_sli_hbqbuf_free_all(phba);
- if (phba->lpfc_drb_pool)
- pci_pool_destroy(phba->lpfc_drb_pool);
+ dma_pool_destroy(phba->lpfc_nvmet_drb_pool);
+ phba->lpfc_nvmet_drb_pool = NULL;
+
+ dma_pool_destroy(phba->lpfc_drb_pool);
phba->lpfc_drb_pool = NULL;
- if (phba->lpfc_hrb_pool)
- pci_pool_destroy(phba->lpfc_hrb_pool);
+
+ dma_pool_destroy(phba->lpfc_hrb_pool);
phba->lpfc_hrb_pool = NULL;
- if (phba->lpfc_hbq_pool)
- pci_pool_destroy(phba->lpfc_hbq_pool);
+ dma_pool_destroy(phba->lpfc_hbq_pool);
phba->lpfc_hbq_pool = NULL;
+ mempool_destroy(phba->rrq_pool);
+ phba->rrq_pool = NULL;
+
/* Free NLP memory pool */
mempool_destroy(phba->nlp_mem_pool);
phba->nlp_mem_pool = NULL;
+ if (phba->sli_rev == LPFC_SLI_REV4 && phba->active_rrq_pool) {
+ mempool_destroy(phba->active_rrq_pool);
+ phba->active_rrq_pool = NULL;
+ }
/* Free mbox memory pool */
mempool_destroy(phba->mbox_mem_pool);
@@ -204,17 +272,26 @@ lpfc_mem_free(struct lpfc_hba *phba)
/* Free MBUF memory pool */
for (i = 0; i < pool->current_count; i++)
- pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
+ dma_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
pool->elements[i].phys);
kfree(pool->elements);
- pci_pool_destroy(phba->lpfc_mbuf_pool);
+ dma_pool_destroy(phba->lpfc_mbuf_pool);
phba->lpfc_mbuf_pool = NULL;
- /* Free DMA buffer memory pool */
- pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
- phba->lpfc_scsi_dma_buf_pool = NULL;
-
+ /* Free Device Data memory pool */
+ if (phba->device_data_mem_pool) {
+ /* Ensure all objects have been returned to the pool */
+ while (!list_empty(&phba->luns)) {
+ device_data = list_first_entry(&phba->luns,
+ struct lpfc_device_data,
+ listentry);
+ list_del(&device_data->listentry);
+ mempool_free(device_data, phba->device_data_mem_pool);
+ }
+ mempool_destroy(phba->device_data_mem_pool);
+ }
+ phba->device_data_mem_pool = NULL;
return;
}
@@ -223,7 +300,7 @@ lpfc_mem_free(struct lpfc_hba *phba)
* @phba: HBA to free memory for
*
* Description: Free memory from PCI and driver memory pools and also those
- * used : lpfc_scsi_dma_buf_pool, lpfc_mbuf_pool, lpfc_hrb_pool. Frees
+ * used : lpfc_sg_dma_buf_pool, lpfc_mbuf_pool, lpfc_hrb_pool. Frees
* kmalloc-backed mempools for LPFC_MBOXQ_t and lpfc_nodelist. Also frees
* the VPI bitmask.
*
@@ -234,27 +311,16 @@ lpfc_mem_free_all(struct lpfc_hba *phba)
{
struct lpfc_sli *psli = &phba->sli;
LPFC_MBOXQ_t *mbox, *next_mbox;
- struct lpfc_dmabuf *mp;
/* Free memory used in mailbox queue back to mailbox memory pool */
list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) {
- mp = (struct lpfc_dmabuf *) (mbox->context1);
- if (mp) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
list_del(&mbox->list);
- mempool_free(mbox, phba->mbox_mem_pool);
+ lpfc_mem_free_sli_mbox(phba, mbox);
}
/* Free memory used in mailbox cmpl list back to mailbox memory pool */
list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq_cmpl, list) {
- mp = (struct lpfc_dmabuf *) (mbox->context1);
- if (mp) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
list_del(&mbox->list);
- mempool_free(mbox, phba->mbox_mem_pool);
+ lpfc_mem_free_sli_mbox(phba, mbox);
}
/* Free the active mailbox command back to the mailbox memory pool */
spin_lock_irq(&phba->hbalock);
@@ -262,18 +328,36 @@ lpfc_mem_free_all(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
if (psli->mbox_active) {
mbox = psli->mbox_active;
- mp = (struct lpfc_dmabuf *) (mbox->context1);
- if (mp) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
- mempool_free(mbox, phba->mbox_mem_pool);
+ lpfc_mem_free_sli_mbox(phba, mbox);
psli->mbox_active = NULL;
}
/* Free and destroy all the allocated memory pools */
lpfc_mem_free(phba);
+ /* Free DMA buffer memory pool */
+ dma_pool_destroy(phba->lpfc_sg_dma_buf_pool);
+ phba->lpfc_sg_dma_buf_pool = NULL;
+
+ dma_pool_destroy(phba->lpfc_cmd_rsp_buf_pool);
+ phba->lpfc_cmd_rsp_buf_pool = NULL;
+
+ /* Free Congestion Data buffer */
+ if (phba->cgn_i) {
+ dma_free_coherent(&phba->pcidev->dev,
+ sizeof(struct lpfc_cgn_info),
+ phba->cgn_i->virt, phba->cgn_i->phys);
+ kfree(phba->cgn_i);
+ phba->cgn_i = NULL;
+ }
+
+ /* Free RX Monitor */
+ if (phba->rx_monitor) {
+ lpfc_rx_monitor_destroy_ring(phba->rx_monitor);
+ kfree(phba->rx_monitor);
+ phba->rx_monitor = NULL;
+ }
+
/* Free the iocb lookup array */
kfree(psli->iocbq_lookup);
psli->iocbq_lookup = NULL;
@@ -288,7 +372,7 @@ lpfc_mem_free_all(struct lpfc_hba *phba)
* @handle: used to return the DMA-mapped address of the mbuf
*
* Description: Allocates a DMA-mapped buffer from the lpfc_mbuf_pool PCI pool.
- * Allocates from generic pci_pool_alloc function first and if that fails and
+ * Allocates from generic dma_pool_alloc function first and if that fails and
* mem_flags has MEM_PRI set (the only defined flag), returns an mbuf from the
* HBA's pool.
*
@@ -306,7 +390,7 @@ lpfc_mbuf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle)
unsigned long iflags;
void *ret;
- ret = pci_pool_alloc(phba->lpfc_mbuf_pool, GFP_KERNEL, handle);
+ ret = dma_pool_alloc(phba->lpfc_mbuf_pool, GFP_KERNEL, handle);
spin_lock_irqsave(&phba->hbalock, iflags);
if (!ret && (mem_flags & MEM_PRI) && pool->current_count) {
@@ -342,7 +426,7 @@ __lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
pool->elements[pool->current_count].phys = dma;
pool->current_count++;
} else {
- pci_pool_free(phba->lpfc_mbuf_pool, virt, dma);
+ dma_pool_free(phba->lpfc_mbuf_pool, virt, dma);
}
return;
}
@@ -372,6 +456,44 @@ lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
}
/**
+ * lpfc_nvmet_buf_alloc - Allocate an nvmet_buf from the
+ * lpfc_sg_dma_buf_pool PCI pool
+ * @phba: HBA which owns the pool to allocate from
+ * @mem_flags: indicates if this is a priority (MEM_PRI) allocation
+ * @handle: used to return the DMA-mapped address of the nvmet_buf
+ *
+ * Description: Allocates a DMA-mapped buffer from the lpfc_sg_dma_buf_pool
+ * PCI pool. Allocates from generic dma_pool_alloc function.
+ *
+ * Returns:
+ * pointer to the allocated nvmet_buf on success
+ * NULL on failure
+ **/
+void *
+lpfc_nvmet_buf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle)
+{
+ void *ret;
+
+ ret = dma_pool_alloc(phba->lpfc_sg_dma_buf_pool, GFP_KERNEL, handle);
+ return ret;
+}
+
+/**
+ * lpfc_nvmet_buf_free - Free an nvmet_buf from the lpfc_sg_dma_buf_pool
+ * PCI pool
+ * @phba: HBA which owns the pool to return to
+ * @virt: nvmet_buf to free
+ * @dma: the DMA-mapped address of the lpfc_sg_dma_buf_pool to be freed
+ *
+ * Returns: None
+ **/
+void
+lpfc_nvmet_buf_free(struct lpfc_hba *phba, void *virt, dma_addr_t dma)
+{
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool, virt, dma);
+}
+
+/**
* lpfc_els_hbq_alloc - Allocate an HBQ buffer
* @phba: HBA to allocate HBQ buffer for
*
@@ -389,17 +511,17 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
{
struct hbq_dmabuf *hbqbp;
- hbqbp = kmalloc(sizeof(struct hbq_dmabuf), GFP_KERNEL);
+ hbqbp = kzalloc(sizeof(struct hbq_dmabuf), GFP_KERNEL);
if (!hbqbp)
return NULL;
- hbqbp->dbuf.virt = pci_pool_alloc(phba->lpfc_hbq_pool, GFP_KERNEL,
+ hbqbp->dbuf.virt = dma_pool_alloc(phba->lpfc_hbq_pool, GFP_KERNEL,
&hbqbp->dbuf.phys);
if (!hbqbp->dbuf.virt) {
kfree(hbqbp);
return NULL;
}
- hbqbp->size = LPFC_BPL_SIZE;
+ hbqbp->total_size = LPFC_BPL_SIZE;
return hbqbp;
}
@@ -418,7 +540,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
void
lpfc_els_hbq_free(struct lpfc_hba *phba, struct hbq_dmabuf *hbqbp)
{
- pci_pool_free(phba->lpfc_hbq_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
+ dma_pool_free(phba->lpfc_hbq_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
kfree(hbqbp);
return;
}
@@ -441,25 +563,25 @@ lpfc_sli4_rb_alloc(struct lpfc_hba *phba)
{
struct hbq_dmabuf *dma_buf;
- dma_buf = kmalloc(sizeof(struct hbq_dmabuf), GFP_KERNEL);
+ dma_buf = kzalloc(sizeof(struct hbq_dmabuf), GFP_KERNEL);
if (!dma_buf)
return NULL;
- dma_buf->hbuf.virt = pci_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
+ dma_buf->hbuf.virt = dma_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
&dma_buf->hbuf.phys);
if (!dma_buf->hbuf.virt) {
kfree(dma_buf);
return NULL;
}
- dma_buf->dbuf.virt = pci_pool_alloc(phba->lpfc_drb_pool, GFP_KERNEL,
+ dma_buf->dbuf.virt = dma_pool_alloc(phba->lpfc_drb_pool, GFP_KERNEL,
&dma_buf->dbuf.phys);
if (!dma_buf->dbuf.virt) {
- pci_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt,
+ dma_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt,
dma_buf->hbuf.phys);
kfree(dma_buf);
return NULL;
}
- dma_buf->size = LPFC_BPL_SIZE;
+ dma_buf->total_size = LPFC_DATA_BUF_SIZE;
return dma_buf;
}
@@ -478,10 +600,68 @@ lpfc_sli4_rb_alloc(struct lpfc_hba *phba)
void
lpfc_sli4_rb_free(struct lpfc_hba *phba, struct hbq_dmabuf *dmab)
{
- pci_pool_free(phba->lpfc_hrb_pool, dmab->hbuf.virt, dmab->hbuf.phys);
- pci_pool_free(phba->lpfc_drb_pool, dmab->dbuf.virt, dmab->dbuf.phys);
+ dma_pool_free(phba->lpfc_hrb_pool, dmab->hbuf.virt, dmab->hbuf.phys);
+ dma_pool_free(phba->lpfc_drb_pool, dmab->dbuf.virt, dmab->dbuf.phys);
+ kfree(dmab);
+}
+
+/**
+ * lpfc_sli4_nvmet_alloc - Allocate an SLI4 Receive buffer
+ * @phba: HBA to allocate a receive buffer for
+ *
+ * Description: Allocates a DMA-mapped receive buffer from the lpfc_hrb_pool PCI
+ * pool along a non-DMA-mapped container for it.
+ *
+ * Returns:
+ * pointer to HBQ on success
+ * NULL on failure
+ **/
+struct rqb_dmabuf *
+lpfc_sli4_nvmet_alloc(struct lpfc_hba *phba)
+{
+ struct rqb_dmabuf *dma_buf;
+
+ dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL);
+ if (!dma_buf)
+ return NULL;
+
+ dma_buf->hbuf.virt = dma_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
+ &dma_buf->hbuf.phys);
+ if (!dma_buf->hbuf.virt) {
+ kfree(dma_buf);
+ return NULL;
+ }
+ dma_buf->dbuf.virt = dma_pool_alloc(phba->lpfc_nvmet_drb_pool,
+ GFP_KERNEL, &dma_buf->dbuf.phys);
+ if (!dma_buf->dbuf.virt) {
+ dma_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt,
+ dma_buf->hbuf.phys);
+ kfree(dma_buf);
+ return NULL;
+ }
+ dma_buf->total_size = LPFC_NVMET_DATA_BUF_SIZE;
+ return dma_buf;
+}
+
+/**
+ * lpfc_sli4_nvmet_free - Frees a receive buffer
+ * @phba: HBA buffer was allocated for
+ * @dmab: DMA Buffer container returned by lpfc_sli4_rbq_alloc
+ *
+ * Description: Frees both the container and the DMA-mapped buffers returned by
+ * lpfc_sli4_nvmet_alloc.
+ *
+ * Notes: Can be called with or without locks held.
+ *
+ * Returns: None
+ **/
+void
+lpfc_sli4_nvmet_free(struct lpfc_hba *phba, struct rqb_dmabuf *dmab)
+{
+ dma_pool_free(phba->lpfc_hrb_pool, dmab->hbuf.virt, dmab->hbuf.phys);
+ dma_pool_free(phba->lpfc_nvmet_drb_pool,
+ dmab->dbuf.virt, dmab->dbuf.phys);
kfree(dmab);
- return;
}
/**
@@ -506,13 +686,13 @@ lpfc_in_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp)
return;
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
+ hbq_entry = container_of(mp, struct hbq_dmabuf, dbuf);
/* Check whether HBQ is still in use */
spin_lock_irqsave(&phba->hbalock, flags);
if (!phba->hbq_in_use) {
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}
- hbq_entry = container_of(mp, struct hbq_dmabuf, dbuf);
list_del(&hbq_entry->dbuf.list);
if (hbq_entry->tag == -1) {
(phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer)
@@ -527,3 +707,57 @@ lpfc_in_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp)
}
return;
}
+
+/**
+ * lpfc_rq_buf_free - Free a RQ DMA buffer
+ * @phba: HBA buffer is associated with
+ * @mp: Buffer to free
+ *
+ * Description: Frees the given DMA buffer in the appropriate way given by
+ * reposting it to its associated RQ so it can be reused.
+ *
+ * Notes: Takes phba->hbalock. Can be called with or without other locks held.
+ *
+ * Returns: None
+ **/
+void
+lpfc_rq_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp)
+{
+ struct lpfc_rqb *rqbp;
+ struct lpfc_rqe hrqe;
+ struct lpfc_rqe drqe;
+ struct rqb_dmabuf *rqb_entry;
+ unsigned long flags;
+ int rc;
+
+ if (!mp)
+ return;
+
+ rqb_entry = container_of(mp, struct rqb_dmabuf, hbuf);
+ rqbp = rqb_entry->hrq->rqbp;
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+ list_del(&rqb_entry->hbuf.list);
+ hrqe.address_lo = putPaddrLow(rqb_entry->hbuf.phys);
+ hrqe.address_hi = putPaddrHigh(rqb_entry->hbuf.phys);
+ drqe.address_lo = putPaddrLow(rqb_entry->dbuf.phys);
+ drqe.address_hi = putPaddrHigh(rqb_entry->dbuf.phys);
+ rc = lpfc_sli4_rq_put(rqb_entry->hrq, rqb_entry->drq, &hrqe, &drqe);
+ if (rc < 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "6409 Cannot post to HRQ %d: %x %x %x "
+ "DRQ %x %x\n",
+ rqb_entry->hrq->queue_id,
+ rqb_entry->hrq->host_index,
+ rqb_entry->hrq->hba_index,
+ rqb_entry->hrq->entry_count,
+ rqb_entry->drq->host_index,
+ rqb_entry->drq->hba_index);
+ (rqbp->rqb_free_buffer)(phba, rqb_entry);
+ } else {
+ list_add_tail(&rqb_entry->hbuf.list, &rqbp->rqb_buffer_list);
+ rqbp->buffer_count++;
+ }
+
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+}
diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h
index f2b1bbcb196f..95d60ab5ebf9 100644
--- a/drivers/scsi/lpfc/lpfc_nl.h
+++ b/drivers/scsi/lpfc/lpfc_nl.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2010 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 2ddd02f7c603..8240d59f4120 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1,9 +1,11 @@
- /*******************************************************************
+/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -28,6 +30,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -35,19 +38,33 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
#include "lpfc_debugfs.h"
+/* Called to clear RSCN discovery flags when driver is unloading. */
+static bool
+lpfc_check_unload_and_clr_rscn(unsigned long *fc_flag)
+{
+ /* If unloading, then clear the FC_RSCN_DEFERRED flag */
+ if (test_bit(FC_UNLOADING, fc_flag)) {
+ clear_bit(FC_RSCN_DEFERRED, fc_flag);
+ return false;
+ }
+ return test_bit(FC_RSCN_DEFERRED, fc_flag);
+}
+
/* Called to verify a rcv'ed ADISC was intended for us. */
static int
lpfc_check_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_name *nn, struct lpfc_name *pn)
{
+
/* Compare the ADISC rsp WWNN / WWPN matches our internal node
* table entry for that node.
*/
@@ -144,7 +161,7 @@ lpfc_check_sparm(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name));
return 1;
bad_service_param:
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0207 Device %x "
"(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x) sent "
"invalid service parameters. Ignoring device.\n",
@@ -163,12 +180,11 @@ lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_dmabuf *pcmd, *prsp;
uint32_t *lp;
void *ptr = NULL;
- IOCB_t *irsp;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
- irsp = &rspiocb->iocb;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
- /* For lpfc_els_abort, context2 could be zero'ed to delay
+ /* For lpfc_els_abort, cmd_dmabuf could be zero'ed to delay
* freeing associated memory till after ABTS completes.
*/
if (pcmd) {
@@ -179,10 +195,16 @@ lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
ptr = (void *)((uint8_t *)lp + sizeof(uint32_t));
}
} else {
- /* Force ulpStatus error since we are returning NULL ptr */
- if (!(irsp->ulpStatus)) {
- irsp->ulpStatus = IOSTAT_LOCAL_REJECT;
- irsp->un.ulpWord[4] = IOERR_SLI_ABORTED;
+ /* Force ulp_status error since we are returning NULL ptr */
+ if (!(ulp_status)) {
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ bf_set(lpfc_wcqe_c_status, &rspiocb->wcqe_cmpl,
+ IOSTAT_LOCAL_REJECT);
+ rspiocb->wcqe_cmpl.parameter = IOERR_SLI_ABORTED;
+ } else {
+ rspiocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
+ rspiocb->iocb.un.ulpWord[4] = IOERR_SLI_ABORTED;
+ }
}
ptr = NULL;
}
@@ -196,112 +218,169 @@ lpfc_check_elscmpl_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* associated with a LPFC_NODELIST entry. This
* routine effectively results in a "software abort".
*/
-int
+void
lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
{
- LIST_HEAD(completions);
- LIST_HEAD(txcmplq_completions);
LIST_HEAD(abort_list);
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
+ LIST_HEAD(drv_cmpl_list);
+ struct lpfc_sli_ring *pring;
struct lpfc_iocbq *iocb, *next_iocb;
+ int retval = 0;
+
+ pring = lpfc_phba_elsring(phba);
+
+ /* In case of error recovery path, we might have a NULL pring here */
+ if (unlikely(!pring))
+ return;
/* Abort outstanding I/O on NPort <nlp_DID> */
lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_DISCOVERY,
"2819 Abort outstanding I/O on NPort x%x "
- "Data: x%x x%x x%x\n",
+ "Data: x%lx x%x x%x\n",
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
-
+ /* Clean up all fabric IOs first.*/
lpfc_fabric_abort_nport(ndlp);
- /* First check the txq */
+ /*
+ * Lock the ELS ring txcmplq for SLI3/SLI4 and build a local list
+ * of all ELS IOs that need an ABTS. The IOs need to stay on the
+ * txcmplq so that the abort operation completes them successfully.
+ */
spin_lock_irq(&phba->hbalock);
- list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
- /* Check to see if iocb matches the nport we are looking for */
- if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) {
- /* It matches, so deque and call compl with anp error */
- list_move_tail(&iocb->list, &completions);
- pring->txq_cnt--;
- }
- }
-
- /* Next check the txcmplq */
- list_splice_init(&pring->txcmplq, &txcmplq_completions);
- spin_unlock_irq(&phba->hbalock);
-
- list_for_each_entry_safe(iocb, next_iocb, &txcmplq_completions, list) {
- /* Check to see if iocb matches the nport we are looking for */
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_lock(&pring->ring_lock);
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+ /* Add to abort_list on on NDLP match. */
if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp))
list_add_tail(&iocb->dlist, &abort_list);
}
- spin_lock_irq(&phba->hbalock);
- list_splice(&txcmplq_completions, &pring->txcmplq);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring->ring_lock);
spin_unlock_irq(&phba->hbalock);
+ /* Abort the targeted IOs and remove them from the abort list. */
list_for_each_entry_safe(iocb, next_iocb, &abort_list, dlist) {
- spin_lock_irq(&phba->hbalock);
- list_del_init(&iocb->dlist);
- lpfc_sli_issue_abort_iotag(phba, pring, iocb);
- spin_unlock_irq(&phba->hbalock);
+ spin_lock_irq(&phba->hbalock);
+ list_del_init(&iocb->dlist);
+ retval = lpfc_sli_issue_abort_iotag(phba, pring, iocb, NULL);
+ spin_unlock_irq(&phba->hbalock);
+
+ if (retval && test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
+ list_del_init(&iocb->list);
+ list_add_tail(&iocb->list, &drv_cmpl_list);
+ }
}
- /* Cancel all the IOCBs from the completions list */
- lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ lpfc_sli_cancel_iocbs(phba, &drv_cmpl_list, IOSTAT_LOCAL_REJECT,
IOERR_SLI_ABORTED);
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
+ INIT_LIST_HEAD(&abort_list);
+
+ /* Now process the txq */
+ spin_lock_irq(&phba->hbalock);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_lock(&pring->ring_lock);
+
+ list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+ /* Check to see if iocb matches the nport we are looking for */
+ if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) {
+ list_del_init(&iocb->list);
+ list_add_tail(&iocb->list, &abort_list);
+ }
+ }
+
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring->ring_lock);
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &abort_list,
+ IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED);
+
lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);
- return 0;
+}
+
+/* lpfc_defer_plogi_acc - Issue PLOGI ACC after reg_login completes
+ * @phba: pointer to lpfc hba data structure.
+ * @login_mbox: pointer to REG_RPI mailbox object
+ *
+ * The ACC for a rcv'ed PLOGI is deferred until AFTER the REG_RPI completes
+ */
+static void
+lpfc_defer_plogi_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *login_mbox)
+{
+ struct lpfc_iocbq *save_iocb;
+ struct lpfc_nodelist *ndlp;
+ MAILBOX_t *mb = &login_mbox->u.mb;
+
+ int rc;
+
+ ndlp = login_mbox->ctx_ndlp;
+ save_iocb = login_mbox->ctx_u.save_iocb;
+
+ if (mb->mbxStatus == MBX_SUCCESS) {
+ /* Now that REG_RPI completed successfully,
+ * we can now proceed with sending the PLOGI ACC.
+ */
+ if (test_bit(FC_PT2PT, &ndlp->vport->fc_flag)) {
+ rc = lpfc_els_rsp_acc(login_mbox->vport, ELS_CMD_PLOGI,
+ save_iocb, ndlp, login_mbox);
+ } else {
+ rc = lpfc_els_rsp_acc(login_mbox->vport, ELS_CMD_PLOGI,
+ save_iocb, ndlp, NULL);
+ }
+
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "4576 PLOGI ACC fails pt2pt discovery: "
+ "DID %x Data: %x\n", ndlp->nlp_DID, rc);
+ }
+ }
+
+ /* If this is a fabric topology, complete the reg_rpi and prli now.
+ * For Pt2Pt, the reg_rpi and PRLI are deferred until after the LS_ACC
+ * completes. This ensures, in Pt2Pt, that the PLOGI LS_ACC is sent
+ * before the PRLI.
+ */
+ if (!test_bit(FC_PT2PT, &ndlp->vport->fc_flag)) {
+ /* Now process the REG_RPI cmpl */
+ lpfc_mbx_cmpl_reg_login(phba, login_mbox);
+ clear_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag);
+ }
+ kfree(save_iocb);
}
static int
lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_dmabuf *pcmd;
+ uint64_t nlp_portwwn = 0;
uint32_t *lp;
+ union lpfc_wqe128 *wqe;
IOCB_t *icmd;
struct serv_parm *sp;
- LPFC_MBOXQ_t *mbox;
+ uint32_t ed_tov;
+ LPFC_MBOXQ_t *link_mbox;
+ LPFC_MBOXQ_t *login_mbox;
+ struct lpfc_iocbq *save_iocb;
struct ls_rjt stat;
+ uint32_t vid, flag;
int rc;
+ u32 remote_did;
memset(&stat, 0, sizeof (struct ls_rjt));
- if (vport->port_state <= LPFC_FDISC) {
- /* Before responding to PLOGI, check for pt2pt mode.
- * If we are pt2pt, with an outstanding FLOGI, abort
- * the FLOGI and resend it first.
- */
- if (vport->fc_flag & FC_PT2PT) {
- lpfc_els_abort_flogi(phba);
- if (!(vport->fc_flag & FC_PT2PT_PLOGI)) {
- /* If the other side is supposed to initiate
- * the PLOGI anyway, just ACC it now and
- * move on with discovery.
- */
- phba->fc_edtov = FF_DEF_EDTOV;
- phba->fc_ratov = FF_DEF_RATOV;
- /* Start discovery - this should just do
- CLEAR_LA */
- lpfc_disc_start(vport);
- } else
- lpfc_initial_flogi(vport);
- } else {
- stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY;
- stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
- lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
- ndlp, NULL);
- return 0;
- }
- }
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
lp = (uint32_t *) pcmd->virt;
sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
if (wwn_to_u64(sp->portName.u.wwn) == 0) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0140 PLOGI Reject: invalid nname\n");
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0140 PLOGI Reject: invalid pname\n");
stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
stat.un.b.lsRjtRsnCodeExp = LSEXP_INVALID_PNAME;
lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
@@ -309,14 +388,16 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
return 0;
}
if (wwn_to_u64(sp->nodeName.u.wwn) == 0) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0141 PLOGI Reject: invalid pname\n");
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0141 PLOGI Reject: invalid nname\n");
stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
stat.un.b.lsRjtRsnCodeExp = LSEXP_INVALID_NNAME;
lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
NULL);
return 0;
}
+
+ nlp_portwwn = wwn_to_u64(ndlp->nlp_portname.u.wwn);
if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 0) == 0)) {
/* Reject this request because invalid parameters */
stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
@@ -325,13 +406,19 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
NULL);
return 0;
}
- icmd = &cmdiocb->iocb;
+
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ wqe = &cmdiocb->wqe;
+ else
+ icmd = &cmdiocb->iocb;
/* PLOGI chkparm OK */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n",
+ "0114 PLOGI chkparm OK Data: x%x x%x x%lx "
+ "x%x x%x x%lx\n",
ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag,
- ndlp->nlp_rpi);
+ ndlp->nlp_rpi, vport->port_state,
+ vport->fc_flag);
if (vport->cfg_fcp_class == 2 && sp->cls2.classValid)
ndlp->nlp_fcp_info |= CLASS2;
@@ -345,65 +432,164 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_class_sup |= FC_COS_CLASS2;
if (sp->cls3.classValid)
ndlp->nlp_class_sup |= FC_COS_CLASS3;
- if (sp->cls4.classValid)
- ndlp->nlp_class_sup |= FC_COS_CLASS4;
ndlp->nlp_maxframe =
((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb;
-
- /* no need to reg_login if we are already in one of these states */
+ /* if already logged in, do implicit logout */
switch (ndlp->nlp_state) {
case NLP_STE_NPR_NODE:
- if (!(ndlp->nlp_flag & NLP_NPR_ADISC))
+ if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag))
break;
+ fallthrough;
case NLP_STE_REG_LOGIN_ISSUE:
case NLP_STE_PRLI_ISSUE:
case NLP_STE_UNMAPPED_NODE:
case NLP_STE_MAPPED_NODE:
- lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL);
- return 1;
+ /* For initiators, lpfc_plogi_confirm_nport skips fabric did.
+ * For target mode, execute implicit logo.
+ * Fabric nodes go into NPR.
+ */
+ if (!(ndlp->nlp_type & NLP_FABRIC) &&
+ !(phba->nvmet_support)) {
+ break;
+ }
+ if (nlp_portwwn != 0 &&
+ nlp_portwwn != wwn_to_u64(sp->portName.u.wwn))
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "0143 PLOGI recv'd from DID: x%x "
+ "WWPN changed: old %llx new %llx\n",
+ ndlp->nlp_DID,
+ (unsigned long long)nlp_portwwn,
+ (unsigned long long)
+ wwn_to_u64(sp->portName.u.wwn));
+
+ /* Notify transport of connectivity loss to trigger cleanup. */
+ if (phba->nvmet_support &&
+ ndlp->nlp_state == NLP_STE_UNMAPPED_NODE)
+ lpfc_nvmet_invalidate_host(phba, ndlp);
+
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+ /* rport needs to be unregistered first */
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ break;
}
+ /* Clear ndlp info, since follow up processes may have
+ * updated ndlp information
+ */
+ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR);
+ ndlp->nlp_type &= ~(NLP_NVME_TARGET | NLP_NVME_INITIATOR);
+ ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ ndlp->nlp_nvme_info &= ~NLP_NVME_NSLER;
+ clear_bit(NLP_FIRSTBURST, &ndlp->nlp_flag);
- if ((vport->fc_flag & FC_PT2PT) &&
- !(vport->fc_flag & FC_PT2PT_PLOGI)) {
+ login_mbox = NULL;
+ link_mbox = NULL;
+ save_iocb = NULL;
+
+ /* Check for Nport to NPort pt2pt protocol */
+ if (test_bit(FC_PT2PT, &vport->fc_flag) &&
+ !test_bit(FC_PT2PT_PLOGI, &vport->fc_flag)) {
/* rcv'ed PLOGI decides what our NPortId will be */
- vport->fc_myDID = icmd->un.rcvels.parmRo;
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (mbox == NULL)
- goto out;
- lpfc_config_link(phba, mbox);
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->vport = vport;
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- mempool_free(mbox, phba->mbox_mem_pool);
- goto out;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ vport->fc_myDID = bf_get(els_rsp64_sid,
+ &cmdiocb->wqe.xmit_els_rsp);
+ } else {
+ vport->fc_myDID = icmd->un.rcvels.parmRo;
+ }
+
+ /* If there is an outstanding FLOGI, abort it now.
+ * The remote NPort is not going to ACC our FLOGI
+ * if its already issuing a PLOGI for pt2pt mode.
+ * This indicates our FLOGI was dropped; however, we
+ * must have ACCed the remote NPorts FLOGI to us
+ * to make it here.
+ */
+ if (test_bit(HBA_FLOGI_OUTSTANDING, &phba->hba_flag))
+ lpfc_els_abort_flogi(phba);
+
+ ed_tov = be32_to_cpu(sp->cmn.e_d_tov);
+ if (sp->cmn.edtovResolution) {
+ /* E_D_TOV ticks are in nanoseconds */
+ ed_tov = (phba->fc_edtov + 999999) / 1000000;
+ }
+
+ /*
+ * For pt-to-pt, use the larger EDTOV
+ * RATOV = 2 * EDTOV
+ */
+ if (ed_tov > phba->fc_edtov)
+ phba->fc_edtov = ed_tov;
+ phba->fc_ratov = (2 * phba->fc_edtov) / 1000;
+
+ memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
+
+ /* Issue CONFIG_LINK for SLI3 or REG_VFI for SLI4,
+ * to account for updated TOV's / parameters
+ */
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_issue_reg_vfi(vport);
+ else {
+ link_mbox = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!link_mbox)
+ goto out;
+ lpfc_config_link(phba, link_mbox);
+ link_mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ link_mbox->vport = vport;
+
+ /* The default completion handling for CONFIG_LINK
+ * does not require the ndlp so no reference is needed.
+ */
+ link_mbox->ctx_ndlp = ndlp;
+
+ rc = lpfc_sli_issue_mbox(phba, link_mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(link_mbox, phba->mbox_mem_pool);
+ goto out;
+ }
}
lpfc_can_disctmo(vport);
}
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox)
+
+ clear_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag);
+ if ((phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) &&
+ sp->cmn.valid_vendor_ver_level) {
+ vid = be32_to_cpu(sp->un.vv.vid);
+ flag = be32_to_cpu(sp->un.vv.flags);
+ if ((vid == LPFC_VV_EMLX_ID) && (flag & LPFC_VV_SUPPRESS_RSP))
+ set_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag);
+ }
+
+ login_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!login_mbox)
goto out;
- rc = lpfc_reg_rpi(phba, vport->vpi, icmd->un.rcvels.remoteID,
- (uint8_t *) sp, mbox, ndlp->nlp_rpi);
- if (rc) {
- mempool_free(mbox, phba->mbox_mem_pool);
+ save_iocb = kzalloc(sizeof(*save_iocb), GFP_KERNEL);
+ if (!save_iocb)
goto out;
- }
- /* ACC PLOGI rsp command needs to execute first,
- * queue this mbox command to be processed later.
- */
- mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
- /*
- * mbox->context2 = lpfc_nlp_get(ndlp) deferred until mailbox
- * command issued in lpfc_cmpl_els_acc().
+ /* Save info from cmd IOCB to be used in rsp after all mbox completes */
+ memcpy((uint8_t *)save_iocb, (uint8_t *)cmdiocb,
+ sizeof(struct lpfc_iocbq));
+
+ /* Registering an existing RPI behaves differently for SLI3 vs SLI4 */
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_unreg_rpi(vport, ndlp);
+
+ /* Issue REG_LOGIN first, before ACCing the PLOGI, thus we will
+ * always be deferring the ACC.
*/
- mbox->vport = vport;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= (NLP_ACC_REGLOGIN | NLP_RCV_PLOGI);
- spin_unlock_irq(shost->host_lock);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ remote_did = bf_get(wqe_els_did, &wqe->xmit_els_rsp.wqe_dest);
+ else
+ remote_did = icmd->un.rcvels.remoteID;
+ rc = lpfc_reg_rpi(phba, vport->vpi, remote_did,
+ (uint8_t *)sp, login_mbox, ndlp->nlp_rpi);
+ if (rc)
+ goto out;
+
+ login_mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
+ login_mbox->vport = vport;
/*
* If there is an outstanding PLOGI issued, abort it before
@@ -413,8 +599,10 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* single discovery thread, this will cause a huge delay in
* discovery. Also this will cause multiple state machines
* running in parallel for this node.
+ * This only applies to a fabric environment.
*/
- if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) {
+ if ((ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) &&
+ test_bit(FC_FABRIC, &vport->fc_flag)) {
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp);
}
@@ -422,44 +610,133 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if ((vport->port_type == LPFC_NPIV_PORT &&
vport->cfg_restrict_login)) {
- /* In order to preserve RPIs, we want to cleanup
- * the default RPI the firmware created to rcv
- * this ELS request. The only way to do this is
- * to register, then unregister the RPI.
+ /* no deferred ACC */
+ kfree(save_iocb);
+
+ /* This is an NPIV SLI4 instance that does not need to register
+ * a default RPI.
*/
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_RM_DFLT_RPI;
- spin_unlock_irq(shost->host_lock);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_mbox_rsrc_cleanup(phba, login_mbox,
+ MBOX_THD_UNLOCKED);
+ login_mbox = NULL;
+ } else {
+ /* In order to preserve RPIs, we want to cleanup
+ * the default RPI the firmware created to rcv
+ * this ELS request. The only way to do this is
+ * to register, then unregister the RPI.
+ */
+ set_bit(NLP_RM_DFLT_RPI, &ndlp->nlp_flag);
+ set_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag);
+ set_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag);
+ }
+
stat.un.b.lsRjtRsnCode = LSRJT_INVALID_CMD;
stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
- lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
- ndlp, mbox);
+ rc = lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
+ ndlp, login_mbox);
+ if (rc && login_mbox)
+ lpfc_mbox_rsrc_cleanup(phba, login_mbox,
+ MBOX_THD_UNLOCKED);
return 1;
}
- lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
+
+ /* So the order here should be:
+ * SLI3 pt2pt
+ * Issue CONFIG_LINK mbox
+ * CONFIG_LINK cmpl
+ * SLI4 pt2pt
+ * Issue REG_VFI mbox
+ * REG_VFI cmpl
+ * SLI4
+ * Issue UNREG RPI mbx
+ * UNREG RPI cmpl
+ * Issue REG_RPI mbox
+ * REG RPI cmpl
+ * Issue PLOGI ACC
+ * PLOGI ACC cmpl
+ */
+ login_mbox->mbox_cmpl = lpfc_defer_plogi_acc;
+ login_mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!login_mbox->ctx_ndlp)
+ goto out;
+
+ login_mbox->ctx_u.save_iocb = save_iocb; /* For PLOGI ACC */
+
+ set_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag);
+ set_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag);
+
+ /* Start the ball rolling by issuing REG_LOGIN here */
+ rc = lpfc_sli_issue_mbox(phba, login_mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_nlp_put(ndlp);
+ goto out;
+ }
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
+
return 1;
out:
+ kfree(save_iocb);
+ if (login_mbox)
+ mempool_free(login_mbox, phba->mbox_mem_pool);
+
stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
return 0;
}
+/**
+ * lpfc_mbx_cmpl_resume_rpi - Resume RPI completion routine
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to mailbox object
+ *
+ * This routine is invoked to issue a completion to a rcv'ed
+ * ADISC or PDISC after the paused RPI has been resumed.
+ **/
+static void
+lpfc_mbx_cmpl_resume_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_vport *vport;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_nodelist *ndlp;
+ uint32_t cmd;
+
+ elsiocb = mboxq->ctx_u.save_iocb;
+ ndlp = mboxq->ctx_ndlp;
+ vport = mboxq->vport;
+ cmd = elsiocb->drvrTimeout;
+
+ if (cmd == ELS_CMD_ADISC) {
+ lpfc_els_rsp_adisc_acc(vport, elsiocb, ndlp);
+ } else {
+ lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, elsiocb,
+ ndlp, NULL);
+ }
+
+ /* This nlp_put pairs with lpfc_sli4_resume_rpi */
+ lpfc_nlp_put(ndlp);
+
+ kfree(elsiocb);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+}
+
static int
lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_iocbq *elsiocb;
struct lpfc_dmabuf *pcmd;
struct serv_parm *sp;
struct lpfc_name *pnn, *ppn;
struct ls_rjt stat;
ADISC *ap;
- IOCB_t *icmd;
uint32_t *lp;
uint32_t cmd;
+ int rc;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
lp = (uint32_t *) pcmd->virt;
cmd = *lp++;
@@ -473,14 +750,64 @@ lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ppn = (struct lpfc_name *) & sp->portName;
}
- icmd = &cmdiocb->iocb;
- if (icmd->ulpStatus == 0 && lpfc_check_adisc(vport, ndlp, pnn, ppn)) {
+ if (get_job_ulpstatus(phba, cmdiocb) == 0 &&
+ lpfc_check_adisc(vport, ndlp, pnn, ppn)) {
+
+ /*
+ * As soon as we send ACC, the remote NPort can
+ * start sending us data. Thus, for SLI4 we must
+ * resume the RPI before the ACC goes out.
+ */
+ if (vport->phba->sli_rev == LPFC_SLI_REV4) {
+ /* Don't resume an unregistered RPI - unnecessary
+ * mailbox. Just send the ACC when the RPI is not
+ * registered.
+ */
+ if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) {
+ elsiocb = kmalloc(sizeof(*elsiocb), GFP_KERNEL);
+ if (elsiocb) {
+ /* Save info from cmd IOCB used in
+ * rsp
+ */
+ memcpy(elsiocb, cmdiocb,
+ sizeof(*elsiocb));
+
+ elsiocb->drvrTimeout = cmd;
+
+ rc = lpfc_sli4_resume_rpi(ndlp,
+ lpfc_mbx_cmpl_resume_rpi,
+ elsiocb);
+ if (rc)
+ kfree(elsiocb);
+
+ goto out;
+ }
+ }
+ }
+
if (cmd == ELS_CMD_ADISC) {
lpfc_els_rsp_adisc_acc(vport, cmdiocb, ndlp);
} else {
- lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp,
- NULL);
+ lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb,
+ ndlp, NULL);
}
+out:
+ /* If we are authenticated, move to the proper state.
+ * It is possible an ADISC arrived and the remote nport
+ * is already in MAPPED or UNMAPPED state. Catch this
+ * condition and don't set the nlp_state again because
+ * it causes an unnecessary transport unregister/register.
+ *
+ * Nodes marked for ADISC will move MAPPED or UNMAPPED state
+ * after issuing ADISC
+ */
+ if (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET)) {
+ if ((ndlp->nlp_state != NLP_STE_MAPPED_NODE) &&
+ !test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag))
+ lpfc_nlp_set_state(vport, ndlp,
+ NLP_STE_MAPPED_NODE);
+ }
+
return 1;
}
/* Reject this request because invalid parameters */
@@ -491,14 +818,11 @@ lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
/* 1 sec timeout */
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + msecs_to_jiffies(1000));
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
ndlp->nlp_prev_state = ndlp->nlp_state;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
return 0;
}
@@ -506,7 +830,6 @@ static int
lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb, uint32_t els_cmd)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_vport **vports;
int i, active_vlink_present = 0 ;
@@ -515,27 +838,39 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* Only call LOGO ACC for first LOGO, this avoids sending unnecessary
* PLOGIs during LOGO storms from a device.
*/
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_LOGO_ACC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
if (els_cmd == ELS_CMD_PRLO)
lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL);
else
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+
+ /* This clause allows the initiator to ACC the LOGO back to the
+ * Fabric Domain Controller. It does deliberately skip all other
+ * steps because some fabrics send RDP requests after logging out
+ * from the initiator.
+ */
+ if (ndlp->nlp_type & NLP_FABRIC &&
+ ((ndlp->nlp_DID & WELL_KNOWN_DID_MASK) != WELL_KNOWN_DID_MASK))
+ return 0;
+
+ /* Notify transport of connectivity loss to trigger cleanup. */
+ if (phba->nvmet_support &&
+ ndlp->nlp_state == NLP_STE_UNMAPPED_NODE)
+ lpfc_nvmet_invalidate_host(phba, ndlp);
+
if (ndlp->nlp_DID == Fabric_DID) {
- if (vport->port_state <= LPFC_FDISC)
+ if (vport->port_state <= LPFC_FDISC ||
+ test_bit(FC_PT2PT, &vport->fc_flag))
goto out;
lpfc_linkdown_port(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_LOGO_RCVD;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_VPORT_LOGO_RCVD, &vport->fc_flag);
vports = lpfc_create_vport_work_array(phba);
if (vports) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL;
i++) {
- if ((!(vports[i]->fc_flag &
- FC_VPORT_LOGO_RCVD)) &&
- (vports[i]->port_state > LPFC_FDISC)) {
+ if (!test_bit(FC_VPORT_LOGO_RCVD,
+ &vports[i]->fc_flag) &&
+ vports[i]->port_state > LPFC_FDISC) {
active_vlink_present = 1;
break;
}
@@ -543,42 +878,61 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
lpfc_destroy_vport_work_array(phba, vports);
}
- if (active_vlink_present) {
+ /*
+ * Don't re-instantiate if vport is marked for deletion.
+ * If we are here first then vport_delete is going to wait
+ * for discovery to complete.
+ */
+ if (!test_bit(FC_UNLOADING, &vport->load_flag) &&
+ active_vlink_present) {
/*
* If there are other active VLinks present,
* re-instantiate the Vlink using FDISC.
*/
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + msecs_to_jiffies(1000));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_FDISC;
vport->port_state = LPFC_FDISC;
} else {
- spin_lock_irq(shost->host_lock);
- phba->pport->fc_flag &= ~FC_LOGO_RCVD_DID_CHNG;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_LOGO_RCVD_DID_CHNG, &phba->pport->fc_flag);
lpfc_retry_pport_discovery(phba);
}
- } else if ((!(ndlp->nlp_type & NLP_FABRIC) &&
- ((ndlp->nlp_type & NLP_FCP_TARGET) ||
- !(ndlp->nlp_type & NLP_FCP_INITIATOR))) ||
- (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) {
- /* Only try to re-login if this is NOT a Fabric Node */
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
-
- ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
+ } else {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NODE | LOG_ELS | LOG_DISCOVERY,
+ "3203 LOGO recover nport x%06x state x%x "
+ "ntype x%x fc_flag x%lx\n",
+ ndlp->nlp_DID, ndlp->nlp_state,
+ ndlp->nlp_type, vport->fc_flag);
+
+ /* Special cases for rports that recover post LOGO. */
+ if ((!(ndlp->nlp_type == NLP_FABRIC) &&
+ (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET) ||
+ test_bit(FC_PT2PT, &vport->fc_flag))) ||
+ (ndlp->nlp_state >= NLP_STE_ADISC_ISSUE ||
+ ndlp->nlp_state <= NLP_STE_PRLI_ISSUE)) {
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + secs_to_jiffies(1));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
+ ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NODE | LOG_ELS | LOG_DISCOVERY,
+ "3204 Start nlpdelay on DID x%06x "
+ "nflag x%lx lastels x%x ref cnt %u",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_last_elscmd,
+ kref_read(&ndlp->kref));
+ }
}
out:
+ /* Unregister from backend, could have been skipped due to ADISC */
+ lpfc_nlp_unreg_node(vport, ndlp);
+
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
/* The driver has to wait until the ACC completes before it continues
* processing the LOGO. The action will resume in
* lpfc_cmpl_els_logo_acc routine. Since part of processing includes an
@@ -587,29 +941,107 @@ out:
return 0;
}
+static uint32_t
+lpfc_rcv_prli_support_check(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp,
+ struct lpfc_iocbq *cmdiocb)
+{
+ struct ls_rjt stat;
+ uint32_t *payload;
+ uint32_t cmd;
+ PRLI *npr;
+
+ payload = cmdiocb->cmd_dmabuf->virt;
+ cmd = *payload;
+ npr = (PRLI *)((uint8_t *)payload + sizeof(uint32_t));
+
+ if (vport->phba->nvmet_support) {
+ /* Must be a NVME PRLI */
+ if (cmd == ELS_CMD_PRLI)
+ goto out;
+ } else {
+ /* Initiator mode. */
+ if (!vport->nvmei_support && (cmd == ELS_CMD_NVMEPRLI))
+ goto out;
+
+ /* NPIV ports will RJT initiator only functions */
+ if (vport->port_type == LPFC_NPIV_PORT &&
+ npr->initiatorFunc && !npr->targetFunc)
+ goto out;
+ }
+ return 1;
+out:
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_DISCOVERY,
+ "6115 Rcv PRLI (%x) check failed: ndlp rpi %d "
+ "state x%x flags x%lx port_type: x%x "
+ "npr->initfcn: x%x npr->tgtfcn: x%x\n",
+ cmd, ndlp->nlp_rpi, ndlp->nlp_state,
+ ndlp->nlp_flag, vport->port_type,
+ npr->initiatorFunc, npr->targetFunc);
+ memset(&stat, 0, sizeof(struct ls_rjt));
+ stat.un.b.lsRjtRsnCode = LSRJT_CMD_UNSUPPORTED;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_REQ_UNSUPPORTED;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
+ ndlp, NULL);
+ return 0;
+}
+
static void
lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb)
{
+ struct lpfc_hba *phba = vport->phba;
struct lpfc_dmabuf *pcmd;
uint32_t *lp;
PRLI *npr;
struct fc_rport *rport = ndlp->rport;
u32 roles;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
- lp = (uint32_t *) pcmd->virt;
- npr = (PRLI *) ((uint8_t *) lp + sizeof (uint32_t));
-
- ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR);
- ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
- if (npr->prliType == PRLI_FCP_TYPE) {
- if (npr->initiatorFunc)
- ndlp->nlp_type |= NLP_FCP_INITIATOR;
- if (npr->targetFunc)
- ndlp->nlp_type |= NLP_FCP_TARGET;
- if (npr->Retry)
+ pcmd = cmdiocb->cmd_dmabuf;
+ lp = (uint32_t *)pcmd->virt;
+ npr = (PRLI *)((uint8_t *)lp + sizeof(uint32_t));
+
+ if ((npr->prliType == PRLI_FCP_TYPE) ||
+ (npr->prliType == PRLI_NVME_TYPE)) {
+ if (npr->initiatorFunc) {
+ if (npr->prliType == PRLI_FCP_TYPE)
+ ndlp->nlp_type |= NLP_FCP_INITIATOR;
+ if (npr->prliType == PRLI_NVME_TYPE)
+ ndlp->nlp_type |= NLP_NVME_INITIATOR;
+ }
+ if (npr->targetFunc) {
+ if (npr->prliType == PRLI_FCP_TYPE)
+ ndlp->nlp_type |= NLP_FCP_TARGET;
+ if (npr->prliType == PRLI_NVME_TYPE)
+ ndlp->nlp_type |= NLP_NVME_TARGET;
+ if (npr->writeXferRdyDis)
+ set_bit(NLP_FIRSTBURST, &ndlp->nlp_flag);
+ }
+ if (npr->Retry && ndlp->nlp_type &
+ (NLP_FCP_INITIATOR | NLP_FCP_TARGET))
ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE;
+
+ if (npr->Retry && phba->nsler &&
+ ndlp->nlp_type & (NLP_NVME_INITIATOR | NLP_NVME_TARGET))
+ ndlp->nlp_nvme_info |= NLP_NVME_NSLER;
+
+
+ /* If this driver is in nvme target mode, set the ndlp's fc4
+ * type to NVME provided the PRLI response claims NVME FC4
+ * type. Target mode does not issue gft_id so doesn't get
+ * the fc4 type set until now.
+ */
+ if (phba->nvmet_support && (npr->prliType == PRLI_NVME_TYPE)) {
+ ndlp->nlp_fc4_type |= NLP_FC4_NVME;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+ }
+
+ /* Fabric Controllers send FCP PRLI as an initiator but should
+ * not get recognized as FCP type and registered with transport.
+ */
+ if (npr->prliType == PRLI_FCP_TYPE &&
+ !(ndlp->nlp_type & NLP_FABRIC))
+ ndlp->nlp_fc4_type |= NLP_FC4_FCP;
}
if (rport) {
/* We need to update the rport role values */
@@ -620,35 +1052,34 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
roles |= FC_RPORT_ROLE_FCP_TARGET;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
- "rport rolechg: role:x%x did:x%x flg:x%x",
+ "rport rolechg: role:x%x did:x%x flg:x%lx",
roles, ndlp->nlp_DID, ndlp->nlp_flag);
- fc_remote_port_rolechg(rport, roles);
+ if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME)
+ fc_remote_port_rolechg(rport, roles);
}
}
static uint32_t
lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED)) {
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+ if (!test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) {
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
return 0;
}
- if (!(vport->fc_flag & FC_PT2PT)) {
+ if (!test_bit(FC_PT2PT, &vport->fc_flag)) {
/* Check config parameter use-adisc or FCP-2 */
- if ((vport->cfg_use_adisc && (vport->fc_flag & FC_RSCN_MODE)) ||
+ if (vport->cfg_use_adisc &&
+ (test_bit(FC_RSCN_MODE, &vport->fc_flag) ||
((ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) &&
- (ndlp->nlp_type & NLP_FCP_TARGET))) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
+ (ndlp->nlp_type & NLP_FCP_TARGET)))) {
+ set_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
return 1;
}
}
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
lpfc_unreg_rpi(vport, ndlp);
return 0;
}
@@ -657,30 +1088,62 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
* lpfc_release_rpi - Release a RPI by issuing unreg_login mailbox cmd.
* @phba : Pointer to lpfc_hba structure.
* @vport: Pointer to lpfc_vport structure.
+ * @ndlp: Pointer to lpfc_nodelist structure.
* @rpi : rpi to be release.
*
* This function will send a unreg_login mailbox command to the firmware
* to release a rpi.
**/
-void
-lpfc_release_rpi(struct lpfc_hba *phba,
- struct lpfc_vport *vport,
- uint16_t rpi)
+static void
+lpfc_release_rpi(struct lpfc_hba *phba, struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp, uint16_t rpi)
{
LPFC_MBOXQ_t *pmb;
int rc;
+ /* If there is already an UNREG in progress for this ndlp,
+ * no need to queue up another one.
+ */
+ if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "1435 release_rpi SKIP UNREG x%x on "
+ "NPort x%x deferred x%x flg x%lx "
+ "Data: x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_defer_did,
+ ndlp->nlp_flag, ndlp);
+ return;
+ }
+
pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
GFP_KERNEL);
if (!pmb)
- lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
- "2796 mailbox memory allocation failed \n");
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2796 mailbox memory allocation failed \n");
else {
lpfc_unreg_login(phba, vport->vpi, rpi, pmb);
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ pmb->vport = vport;
+ pmb->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!pmb->ctx_ndlp) {
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return;
+ }
+
+ if (((ndlp->nlp_DID & Fabric_DID_MASK) != Fabric_DID_MASK) &&
+ (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag)))
+ set_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "1437 release_rpi UNREG x%x "
+ "on NPort x%x flg x%lx\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_flag);
+
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED)
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_nlp_put(ndlp);
mempool_free(pmb, phba->mbox_mem_pool);
+ }
}
}
@@ -690,21 +1153,18 @@ lpfc_disc_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_hba *phba;
LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg;
- MAILBOX_t *mb;
uint16_t rpi;
phba = vport->phba;
/* Release the RPI if reglogin completing */
- if (!(phba->pport->load_flag & FC_UNLOADING) &&
- (evt == NLP_EVT_CMPL_REG_LOGIN) &&
- (!pmb->u.mb.mbxStatus)) {
- mb = &pmb->u.mb;
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag) &&
+ evt == NLP_EVT_CMPL_REG_LOGIN && !pmb->u.mb.mbxStatus) {
rpi = pmb->u.mb.un.varWords[0];
- lpfc_release_rpi(phba, vport, rpi);
+ lpfc_release_rpi(phba, vport, ndlp, rpi);
}
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0271 Illegal State Transition: node x%x "
- "event x%x, state x%x Data: x%x x%x\n",
+ "event x%x, state x%x Data: x%x x%lx\n",
ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi,
ndlp->nlp_flag);
return ndlp->nlp_state;
@@ -719,13 +1179,12 @@ lpfc_cmpl_plogi_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* working on the same NPortID, do nothing for this thread
* to stop it.
*/
- if (!(ndlp->nlp_flag & NLP_RCV_PLOGI)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
- "0272 Illegal State Transition: node x%x "
- "event x%x, state x%x Data: x%x x%x\n",
- ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi,
- ndlp->nlp_flag);
- }
+ if (!test_bit(NLP_RCV_PLOGI, &ndlp->nlp_flag))
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0272 Illegal State Transition: node x%x "
+ "event x%x, state x%x Data: x%x x%lx\n",
+ ndlp->nlp_DID, evt, ndlp->nlp_state,
+ ndlp->nlp_rpi, ndlp->nlp_flag);
return ndlp->nlp_state;
}
@@ -757,12 +1216,9 @@ static uint32_t
lpfc_rcv_logo_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_LOGO_ACC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
return ndlp->nlp_state;
@@ -783,13 +1239,20 @@ lpfc_device_rm_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
static uint32_t
+lpfc_device_recov_unused_node(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ return ndlp->nlp_state;
+}
+
+static uint32_t
lpfc_rcv_plogi_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb = arg;
- struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ struct lpfc_dmabuf *pcmd = cmdiocb->cmd_dmabuf;
uint32_t *lp = (uint32_t *) pcmd->virt;
struct serv_parm *sp = (struct serv_parm *) (lp + 1);
struct ls_rjt stat;
@@ -813,17 +1276,13 @@ lpfc_rcv_plogi_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
NULL);
} else {
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb) &&
- (ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
- (vport->num_disc_nodes)) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+ test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag) &&
+ vport->num_disc_nodes) {
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
/* Check if there are more PLOGIs to be sent */
lpfc_more_plogi(vport);
if (vport->num_disc_nodes == 0) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_NDISC_ACTIVE;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_NDISC_ACTIVE, &vport->fc_flag);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
@@ -853,6 +1312,9 @@ lpfc_rcv_logo_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
+ /* Retrieve RPI from LOGO IOCB. RPI is used for CMD_ABORT_XRI_CN */
+ if (vport->phba->sli_rev == LPFC_SLI_REV3)
+ ndlp->nlp_rpi = cmdiocb->iocb.ulpIoTag;
/* software abort outstanding PLOGI */
lpfc_els_abort(vport->phba, ndlp);
@@ -864,7 +1326,6 @@ static uint32_t
lpfc_rcv_els_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
@@ -878,10 +1339,8 @@ lpfc_rcv_els_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
/* Put ndlp in npr state set plogi timer for 1 sec */
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + secs_to_jiffies(1));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
ndlp->nlp_prev_state = NLP_STE_PLOGI_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
@@ -896,30 +1355,35 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
uint32_t evt)
{
struct lpfc_hba *phba = vport->phba;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_iocbq *cmdiocb, *rspiocb;
- struct lpfc_dmabuf *pcmd, *prsp, *mp;
+ struct lpfc_dmabuf *pcmd, *prsp;
uint32_t *lp;
- IOCB_t *irsp;
+ uint32_t vid, flag;
struct serv_parm *sp;
+ uint32_t ed_tov;
LPFC_MBOXQ_t *mbox;
+ int rc;
+ u32 ulp_status;
+ u32 did;
cmdiocb = (struct lpfc_iocbq *) arg;
- rspiocb = cmdiocb->context_un.rsp_iocb;
+ rspiocb = cmdiocb->rsp_iocb;
- if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) {
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+
+ if (test_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag)) {
/* Recovery from PLOGI collision logic */
return ndlp->nlp_state;
}
- irsp = &rspiocb->iocb;
-
- if (irsp->ulpStatus)
+ if (ulp_status)
goto out;
- pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ pcmd = cmdiocb->cmd_dmabuf;
prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
+ if (!prsp)
+ goto out;
lp = (uint32_t *) prsp->virt;
sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
@@ -928,7 +1392,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
if ((ndlp->nlp_DID != FDMI_DID) &&
(wwn_to_u64(sp->portName.u.wwn) == 0 ||
wwn_to_u64(sp->nodeName.u.wwn) == 0)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0142 PLOGI RSP: Invalid WWN.\n");
goto out;
}
@@ -936,7 +1400,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
goto out;
/* PLOGI chkparm OK */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0121 PLOGI chkparm OK Data: x%x x%x x%x x%x\n",
+ "0121 PLOGI chkparm OK Data: x%x x%x x%lx x%x\n",
ndlp->nlp_DID, ndlp->nlp_state,
ndlp->nlp_flag, ndlp->nlp_rpi);
if (vport->cfg_fcp_class == 2 && (sp->cls2.classValid))
@@ -951,37 +1415,99 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
ndlp->nlp_class_sup |= FC_COS_CLASS2;
if (sp->cls3.classValid)
ndlp->nlp_class_sup |= FC_COS_CLASS3;
- if (sp->cls4.classValid)
- ndlp->nlp_class_sup |= FC_COS_CLASS4;
ndlp->nlp_maxframe =
((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb;
+ if (test_bit(FC_PT2PT, &vport->fc_flag) &&
+ test_bit(FC_PT2PT_PLOGI, &vport->fc_flag)) {
+ ed_tov = be32_to_cpu(sp->cmn.e_d_tov);
+ if (sp->cmn.edtovResolution) {
+ /* E_D_TOV ticks are in nanoseconds */
+ ed_tov = (phba->fc_edtov + 999999) / 1000000;
+ }
+
+ clear_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag);
+ if ((phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) &&
+ sp->cmn.valid_vendor_ver_level) {
+ vid = be32_to_cpu(sp->un.vv.vid);
+ flag = be32_to_cpu(sp->un.vv.flags);
+ if ((vid == LPFC_VV_EMLX_ID) &&
+ (flag & LPFC_VV_SUPPRESS_RSP))
+ set_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag);
+ }
+
+ /*
+ * Use the larger EDTOV
+ * RATOV = 2 * EDTOV for pt-to-pt
+ */
+ if (ed_tov > phba->fc_edtov)
+ phba->fc_edtov = ed_tov;
+ phba->fc_ratov = (2 * phba->fc_edtov) / 1000;
+
+ memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
+
+ /* Issue config_link / reg_vfi to account for updated TOV's */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_issue_reg_vfi(vport);
+ } else {
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "0133 PLOGI: no memory "
+ "for config_link "
+ "Data: x%x x%x x%lx x%x\n",
+ ndlp->nlp_DID, ndlp->nlp_state,
+ ndlp->nlp_flag, ndlp->nlp_rpi);
+ goto out;
+ }
+
+ lpfc_config_link(phba, mbox);
+
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ goto out;
+ }
+ }
+ }
+
+ lpfc_unreg_rpi(vport, ndlp);
+
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0133 PLOGI: no memory for reg_login "
- "Data: x%x x%x x%x x%x\n",
- ndlp->nlp_DID, ndlp->nlp_state,
- ndlp->nlp_flag, ndlp->nlp_rpi);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0018 PLOGI: no memory for reg_login "
+ "Data: x%x x%x x%lx x%x\n",
+ ndlp->nlp_DID, ndlp->nlp_state,
+ ndlp->nlp_flag, ndlp->nlp_rpi);
goto out;
}
- lpfc_unreg_rpi(vport, ndlp);
+ did = get_job_els_rsp64_did(phba, cmdiocb);
- if (lpfc_reg_rpi(phba, vport->vpi, irsp->un.elsreq64.remoteID,
+ if (lpfc_reg_rpi(phba, vport->vpi, did,
(uint8_t *) sp, mbox, ndlp->nlp_rpi) == 0) {
switch (ndlp->nlp_DID) {
case NameServer_DID:
mbox->mbox_cmpl = lpfc_mbx_cmpl_ns_reg_login;
+ /* Fabric Controller Node needs these parameters. */
+ memcpy(&ndlp->fc_sparam, sp, sizeof(struct serv_parm));
break;
case FDMI_DID:
mbox->mbox_cmpl = lpfc_mbx_cmpl_fdmi_reg_login;
break;
default:
- ndlp->nlp_flag |= NLP_REG_LOGIN_SEND;
+ set_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
}
- mbox->context2 = lpfc_nlp_get(ndlp);
+
+ mbox->ctx_ndlp = lpfc_nlp_get(ndlp);
+ if (!mbox->ctx_ndlp)
+ goto out;
+
mbox->vport = vport;
if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT)
!= MBX_NOT_FINISHED) {
@@ -989,28 +1515,23 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
NLP_STE_REG_LOGIN_ISSUE);
return ndlp->nlp_state;
}
- if (ndlp->nlp_flag & NLP_REG_LOGIN_SEND)
- ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND;
+ clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
/* decrement node reference count to the failed mbox
* command
*/
lpfc_nlp_put(ndlp);
- mp = (struct lpfc_dmabuf *) mbox->context1;
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(mbox, phba->mbox_mem_pool);
-
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0134 PLOGI: cannot issue reg_login "
- "Data: x%x x%x x%x x%x\n",
+ "Data: x%x x%x x%lx x%x\n",
ndlp->nlp_DID, ndlp->nlp_state,
ndlp->nlp_flag, ndlp->nlp_rpi);
} else {
mempool_free(mbox, phba->mbox_mem_pool);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0135 PLOGI: cannot format reg_login "
- "Data: x%x x%x x%x x%x\n",
+ "Data: x%x x%x x%lx x%x\n",
ndlp->nlp_DID, ndlp->nlp_state,
ndlp->nlp_flag, ndlp->nlp_rpi);
}
@@ -1019,13 +1540,17 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
out:
if (ndlp->nlp_DID == NameServer_DID) {
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0261 Cannot Register NameServer login\n");
}
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DEFER_RM;
- spin_unlock_irq(shost->host_lock);
+ /*
+ ** In case the node reference counter does not go to zero, ensure that
+ ** the stale state for the node is not processed.
+ */
+
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
return NLP_STE_FREED_NODE;
}
@@ -1047,10 +1572,10 @@ lpfc_cmpl_reglogin_plogi_issue(struct lpfc_vport *vport,
phba = vport->phba;
/* Release the RPI */
- if (!(phba->pport->load_flag & FC_UNLOADING) &&
- !mb->mbxStatus) {
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag) &&
+ !mb->mbxStatus) {
rpi = pmb->u.mb.un.varWords[0];
- lpfc_release_rpi(phba, vport, rpi);
+ lpfc_release_rpi(phba, vport, ndlp, rpi);
}
return ndlp->nlp_state;
}
@@ -1059,20 +1584,15 @@ static uint32_t
lpfc_device_rm_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NODEV_REMOVE;
- spin_unlock_irq(shost->host_lock);
+ if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
+ set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
return ndlp->nlp_state;
- } else {
- /* software abort outstanding PLOGI */
- lpfc_els_abort(vport->phba, ndlp);
-
- lpfc_drop_node(vport, ndlp);
- return NLP_STE_FREED_NODE;
}
+ /* software abort outstanding PLOGI */
+ lpfc_els_abort(vport->phba, ndlp);
+
+ lpfc_drop_node(vport, ndlp);
+ return NLP_STE_FREED_NODE;
}
static uint32_t
@@ -1081,13 +1601,10 @@ lpfc_device_recov_plogi_issue(struct lpfc_vport *vport,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- /* Don't do anything that will mess up processing of the
- * previous RSCN.
- */
- if (vport->fc_flag & FC_RSCN_DEFERRED)
+ /* Don't do anything that disrupts the RSCN unless lpfc is unloading. */
+ if (lpfc_check_unload_and_clr_rscn(&vport->fc_flag))
return ndlp->nlp_state;
/* software abort outstanding PLOGI */
@@ -1095,9 +1612,8 @@ lpfc_device_recov_plogi_issue(struct lpfc_vport *vport,
ndlp->nlp_prev_state = NLP_STE_PLOGI_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
return ndlp->nlp_state;
}
@@ -1106,7 +1622,6 @@ static uint32_t
lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb;
@@ -1116,10 +1631,7 @@ lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
cmdiocb = (struct lpfc_iocbq *) arg;
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) {
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
- spin_unlock_irq(shost->host_lock);
+ if (test_and_clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
if (vport->num_disc_nodes)
lpfc_more_adisc(vport);
}
@@ -1138,7 +1650,8 @@ lpfc_rcv_prli_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
- lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
+ if (lpfc_rcv_prli_support_check(vport, ndlp, cmdiocb))
+ lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
return ndlp->nlp_state;
}
@@ -1189,31 +1702,27 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb, *rspiocb;
- IOCB_t *irsp;
ADISC *ap;
int rc;
+ u32 ulp_status;
cmdiocb = (struct lpfc_iocbq *) arg;
- rspiocb = cmdiocb->context_un.rsp_iocb;
+ rspiocb = cmdiocb->rsp_iocb;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
ap = (ADISC *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb);
- irsp = &rspiocb->iocb;
- if ((irsp->ulpStatus) ||
+ if ((ulp_status) ||
(!lpfc_check_adisc(vport, ndlp, &ap->nodeName, &ap->portName))) {
/* 1 sec timeout */
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + msecs_to_jiffies(1000));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
- memset(&ndlp->nlp_nodename, 0, sizeof(struct lpfc_name));
- memset(&ndlp->nlp_portname, 0, sizeof(struct lpfc_name));
-
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
lpfc_unreg_rpi(vport, ndlp);
@@ -1221,7 +1730,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
}
if (phba->sli_rev == LPFC_SLI_REV4) {
- rc = lpfc_sli4_resume_rpi(ndlp);
+ rc = lpfc_sli4_resume_rpi(ndlp, NULL, NULL);
if (rc) {
/* Stay in state and retry. */
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
@@ -1229,7 +1738,13 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
}
}
- if (ndlp->nlp_type & NLP_FCP_TARGET) {
+ if (ndlp->nlp_type & NLP_FCP_TARGET)
+ ndlp->nlp_fc4_type |= NLP_FC4_FCP;
+
+ if (ndlp->nlp_type & NLP_NVME_TARGET)
+ ndlp->nlp_fc4_type |= NLP_FC4_NVME;
+
+ if (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET)) {
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE);
} else {
@@ -1244,20 +1759,15 @@ static uint32_t
lpfc_device_rm_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NODEV_REMOVE;
- spin_unlock_irq(shost->host_lock);
+ if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
+ set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
return ndlp->nlp_state;
- } else {
- /* software abort outstanding ADISC */
- lpfc_els_abort(vport->phba, ndlp);
-
- lpfc_drop_node(vport, ndlp);
- return NLP_STE_FREED_NODE;
}
+ /* software abort outstanding ADISC */
+ lpfc_els_abort(vport->phba, ndlp);
+
+ lpfc_drop_node(vport, ndlp);
+ return NLP_STE_FREED_NODE;
}
static uint32_t
@@ -1266,13 +1776,10 @@ lpfc_device_recov_adisc_issue(struct lpfc_vport *vport,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- /* Don't do anything that will mess up processing of the
- * previous RSCN.
- */
- if (vport->fc_flag & FC_RSCN_DEFERRED)
+ /* Don't do anything that disrupts the RSCN unless lpfc is unloading. */
+ if (lpfc_check_unload_and_clr_rscn(&vport->fc_flag))
return ndlp->nlp_state;
/* software abort outstanding ADISC */
@@ -1280,9 +1787,8 @@ lpfc_device_recov_adisc_issue(struct lpfc_vport *vport,
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
lpfc_disc_set_adisc(vport, ndlp);
return ndlp->nlp_state;
}
@@ -1306,8 +1812,35 @@ lpfc_rcv_prli_reglogin_issue(struct lpfc_vport *vport,
uint32_t evt)
{
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
+ struct ls_rjt stat;
- lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
+ if (!lpfc_rcv_prli_support_check(vport, ndlp, cmdiocb)) {
+ return ndlp->nlp_state;
+ }
+ if (vport->phba->nvmet_support) {
+ /* NVME Target mode. Handle and respond to the PRLI and
+ * transition to UNMAPPED provided the RPI has completed
+ * registration.
+ */
+ if (test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag)) {
+ lpfc_rcv_prli(vport, ndlp, cmdiocb);
+ lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
+ } else {
+ /* RPI registration has not completed. Reject the PRLI
+ * to prevent an illegal state transition when the
+ * rpi registration does complete.
+ */
+ memset(&stat, 0, sizeof(struct ls_rjt));
+ stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
+ ndlp, NULL);
+ return ndlp->nlp_state;
+ }
+ } else {
+ /* Initiator mode. */
+ lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
+ }
return ndlp->nlp_state;
}
@@ -1321,16 +1854,16 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
LPFC_MBOXQ_t *mb;
LPFC_MBOXQ_t *nextmb;
- struct lpfc_dmabuf *mp;
cmdiocb = (struct lpfc_iocbq *) arg;
/* cleanup any ndlp on mbox q waiting for reglogin cmpl */
if ((mb = phba->sli.mbox_active)) {
if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
- (ndlp == (struct lpfc_nodelist *) mb->context2)) {
+ (ndlp == mb->ctx_ndlp)) {
+ clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
lpfc_nlp_put(ndlp);
- mb->context2 = NULL;
+ mb->ctx_ndlp = NULL;
mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
}
}
@@ -1338,16 +1871,12 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
- (ndlp == (struct lpfc_nodelist *) mb->context2)) {
- mp = (struct lpfc_dmabuf *) (mb->context1);
- if (mp) {
- __lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
+ (ndlp == mb->ctx_ndlp)) {
+ clear_bit(NLP_REG_LOGIN_SEND, &ndlp->nlp_flag);
lpfc_nlp_put(ndlp);
list_del(&mb->list);
phba->sli.mboxq_cnt--;
- mempool_free(mb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, mb, MBOX_THD_LOCKED);
}
}
spin_unlock_irq(&phba->hbalock);
@@ -1387,15 +1916,15 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg;
MAILBOX_t *mb = &pmb->u.mb;
uint32_t did = mb->un.varWords[1];
if (mb->mbxStatus) {
/* RegLogin failed */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
- "0246 RegLogin failed Data: x%x x%x x%x x%x "
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0246 RegLogin failed Data: x%x x%x x%x x%x "
"x%x\n",
did, mb->mbxStatus, vport->port_state,
mb->un.varRegLogin.vpi,
@@ -1411,32 +1940,81 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
}
/* Put ndlp in npr state set plogi timer for 1 sec */
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- spin_unlock_irq(shost->host_lock);
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + secs_to_jiffies(1));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
lpfc_issue_els_logo(vport, ndlp, 0);
- ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
return ndlp->nlp_state;
}
/* SLI4 ports have preallocated logical rpis. */
- if (vport->phba->sli_rev < LPFC_SLI_REV4)
+ if (phba->sli_rev < LPFC_SLI_REV4)
ndlp->nlp_rpi = mb->un.varWords[0];
- ndlp->nlp_flag |= NLP_RPI_REGISTERED;
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
/* Only if we are not a fabric nport do we issue PRLI */
- if (!(ndlp->nlp_type & NLP_FABRIC)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
+ "3066 RegLogin Complete on x%x x%x x%x\n",
+ did, ndlp->nlp_type, ndlp->nlp_fc4_type);
+ if (!(ndlp->nlp_type & NLP_FABRIC) &&
+ (phba->nvmet_support == 0)) {
+ /* The driver supports FCP and NVME concurrently. If the
+ * ndlp's nlp_fc4_type is still zero, the driver doesn't
+ * know what PRLI to send yet. Figure that out now and
+ * call PRLI depending on the outcome.
+ */
+ if (test_bit(FC_PT2PT, &vport->fc_flag)) {
+ /* If we are pt2pt, there is no Fabric to determine
+ * the FC4 type of the remote nport. So if NVME
+ * is configured try it.
+ */
+ ndlp->nlp_fc4_type |= NLP_FC4_FCP;
+ if ((!test_bit(FC_PT2PT_NO_NVME, &vport->fc_flag)) &&
+ (vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH ||
+ vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) {
+ ndlp->nlp_fc4_type |= NLP_FC4_NVME;
+ /* We need to update the localport also */
+ lpfc_nvme_update_localport(vport);
+ }
+
+ } else if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
+ ndlp->nlp_fc4_type |= NLP_FC4_FCP;
+
+ } else if (ndlp->nlp_fc4_type == 0) {
+ /* If we are only configured for FCP, the driver
+ * should just issue PRLI for FCP. Otherwise issue
+ * GFT_ID to determine if remote port supports NVME.
+ */
+ if (vport->cfg_enable_fc4_type != LPFC_ENABLE_FCP) {
+ lpfc_ns_cmd(vport, SLI_CTNS_GFT_ID, 0,
+ ndlp->nlp_DID);
+ return ndlp->nlp_state;
+ }
+ ndlp->nlp_fc4_type = NLP_FC4_FCP;
+ }
+
ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE);
- lpfc_issue_els_prli(vport, ndlp, 0);
+ if (lpfc_issue_els_prli(vport, ndlp, 0)) {
+ lpfc_issue_els_logo(vport, ndlp, 0);
+ ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ }
} else {
- ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+ if (test_bit(FC_PT2PT, &vport->fc_flag) && phba->nvmet_support)
+ phba->targetport->port_id = vport->fc_myDID;
+
+ /* Only Fabric ports should transition. NVME target
+ * must complete PRLI.
+ */
+ if (ndlp->nlp_type & NLP_FABRIC) {
+ ndlp->nlp_fc4_type &= ~NLP_FC4_FCP;
+ ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+ }
}
return ndlp->nlp_state;
}
@@ -1447,17 +2025,12 @@ lpfc_device_rm_reglogin_issue(struct lpfc_vport *vport,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NODEV_REMOVE;
- spin_unlock_irq(shost->host_lock);
+ if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
+ set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
return ndlp->nlp_state;
- } else {
- lpfc_drop_node(vport, ndlp);
- return NLP_STE_FREED_NODE;
}
+ lpfc_drop_node(vport, ndlp);
+ return NLP_STE_FREED_NODE;
}
static uint32_t
@@ -1466,20 +2039,22 @@ lpfc_device_recov_reglogin_issue(struct lpfc_vport *vport,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- /* Don't do anything that will mess up processing of the
- * previous RSCN.
- */
- if (vport->fc_flag & FC_RSCN_DEFERRED)
+ /* Don't do anything that disrupts the RSCN unless lpfc is unloading. */
+ if (lpfc_check_unload_and_clr_rscn(&vport->fc_flag))
return ndlp->nlp_state;
ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_IGNR_REG_CMPL;
- ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
+
+ /* If we are a target we won't immediately transition into PRLI,
+ * so if REG_LOGIN already completed we don't need to ignore it.
+ */
+ if (!test_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag) ||
+ !vport->phba->nvmet_support)
+ set_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag);
+
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
lpfc_disc_set_adisc(vport, ndlp);
return ndlp->nlp_state;
}
@@ -1502,6 +2077,9 @@ lpfc_rcv_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
+ if (!lpfc_rcv_prli_support_check(vport, ndlp, cmdiocb))
+ return ndlp->nlp_state;
+ lpfc_rcv_prli(vport, ndlp, cmdiocb);
lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
return ndlp->nlp_state;
}
@@ -1548,58 +2126,169 @@ static uint32_t
lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_iocbq *cmdiocb, *rspiocb;
struct lpfc_hba *phba = vport->phba;
- IOCB_t *irsp;
PRLI *npr;
+ struct lpfc_nvme_prli *nvpr;
+ void *temp_ptr;
+ u32 ulp_status;
+ bool acc_imode_sps = false;
cmdiocb = (struct lpfc_iocbq *) arg;
- rspiocb = cmdiocb->context_un.rsp_iocb;
- npr = (PRLI *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb);
+ rspiocb = cmdiocb->rsp_iocb;
- irsp = &rspiocb->iocb;
- if (irsp->ulpStatus) {
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+
+ /* A solicited PRLI is either FCP or NVME. The PRLI cmd/rsp
+ * format is different so NULL the two PRLI types so that the
+ * driver correctly gets the correct context.
+ */
+ npr = NULL;
+ nvpr = NULL;
+ temp_ptr = lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb);
+ if (cmdiocb->cmd_flag & LPFC_PRLI_FCP_REQ)
+ npr = (PRLI *) temp_ptr;
+ else if (cmdiocb->cmd_flag & LPFC_PRLI_NVME_REQ)
+ nvpr = (struct lpfc_nvme_prli *) temp_ptr;
+
+ if (ulp_status) {
if ((vport->port_type == LPFC_NPIV_PORT) &&
vport->cfg_restrict_login) {
goto out;
}
- ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
- return ndlp->nlp_state;
+
+ /* Adjust the nlp_type accordingly if the PRLI failed */
+ if (npr)
+ ndlp->nlp_fc4_type &= ~NLP_FC4_FCP;
+ if (nvpr)
+ ndlp->nlp_fc4_type &= ~NLP_FC4_NVME;
+
+ /* We can't set the DSM state till BOTH PRLIs complete */
+ goto out_err;
}
- /* Check out PRLI rsp */
- ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR);
- ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
- if ((npr->acceptRspCode == PRLI_REQ_EXECUTED) &&
- (npr->prliType == PRLI_FCP_TYPE)) {
- if (npr->initiatorFunc)
- ndlp->nlp_type |= NLP_FCP_INITIATOR;
- if (npr->targetFunc)
- ndlp->nlp_type |= NLP_FCP_TARGET;
- if (npr->Retry)
- ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE;
+ if (npr && npr->prliType == PRLI_FCP_TYPE) {
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_NODE | LOG_DISCOVERY,
+ "6028 FCP NPR PRLI Cmpl Init %d Target %d "
+ "EIP %d AccCode x%x\n",
+ npr->initiatorFunc, npr->targetFunc,
+ npr->estabImagePair, npr->acceptRspCode);
+
+ if (npr->acceptRspCode == PRLI_INV_SRV_PARM) {
+ /* Strict initiators don't establish an image pair. */
+ if (npr->initiatorFunc && !npr->targetFunc &&
+ !npr->estabImagePair)
+ acc_imode_sps = true;
+ }
+
+ if (npr->acceptRspCode == PRLI_REQ_EXECUTED || acc_imode_sps) {
+ if (npr->initiatorFunc)
+ ndlp->nlp_type |= NLP_FCP_INITIATOR;
+ if (npr->targetFunc) {
+ ndlp->nlp_type |= NLP_FCP_TARGET;
+ if (npr->writeXferRdyDis)
+ set_bit(NLP_FIRSTBURST,
+ &ndlp->nlp_flag);
+ }
+ if (npr->Retry)
+ ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE;
+ }
+ } else if (nvpr &&
+ (bf_get_be32(prli_acc_rsp_code, nvpr) ==
+ PRLI_REQ_EXECUTED) &&
+ (bf_get_be32(prli_type_code, nvpr) ==
+ PRLI_NVME_TYPE)) {
+
+ /* Complete setting up the remote ndlp personality. */
+ if (bf_get_be32(prli_init, nvpr))
+ ndlp->nlp_type |= NLP_NVME_INITIATOR;
+
+ if (phba->nsler && bf_get_be32(prli_nsler, nvpr) &&
+ bf_get_be32(prli_conf, nvpr))
+
+ ndlp->nlp_nvme_info |= NLP_NVME_NSLER;
+ else
+ ndlp->nlp_nvme_info &= ~NLP_NVME_NSLER;
+
+ /* Target driver cannot solicit NVME FB. */
+ if (bf_get_be32(prli_tgt, nvpr)) {
+ /* Complete the nvme target roles. The transport
+ * needs to know if the rport is capable of
+ * discovery in addition to its role.
+ */
+ ndlp->nlp_type |= NLP_NVME_TARGET;
+ if (bf_get_be32(prli_disc, nvpr))
+ ndlp->nlp_type |= NLP_NVME_DISCOVERY;
+
+ /*
+ * If prli_fba is set, the Target supports FirstBurst.
+ * If prli_fb_sz is 0, the FirstBurst size is unlimited,
+ * otherwise it defines the actual size supported by
+ * the NVME Target.
+ */
+ if ((bf_get_be32(prli_fba, nvpr) == 1) &&
+ (phba->cfg_nvme_enable_fb) &&
+ (!phba->nvmet_support)) {
+ /* Both sides support FB. The target's first
+ * burst size is a 512 byte encoded value.
+ */
+ set_bit(NLP_FIRSTBURST, &ndlp->nlp_flag);
+ ndlp->nvme_fb_size = bf_get_be32(prli_fb_sz,
+ nvpr);
+
+ /* Expressed in units of 512 bytes */
+ if (ndlp->nvme_fb_size)
+ ndlp->nvme_fb_size <<=
+ LPFC_NVME_FB_SHIFT;
+ else
+ ndlp->nvme_fb_size = LPFC_NVME_MAX_FB;
+ }
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6029 NVME PRLI Cmpl w1 x%08x "
+ "w4 x%08x w5 x%08x flag x%lx, "
+ "fcp_info x%x nlp_type x%x\n",
+ be32_to_cpu(nvpr->word1),
+ be32_to_cpu(nvpr->word4),
+ be32_to_cpu(nvpr->word5),
+ ndlp->nlp_flag, ndlp->nlp_fcp_info,
+ ndlp->nlp_type);
}
if (!(ndlp->nlp_type & NLP_FCP_TARGET) &&
(vport->port_type == LPFC_NPIV_PORT) &&
vport->cfg_restrict_login) {
out:
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_TARGET_REMOVE;
- spin_unlock_irq(shost->host_lock);
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_ELS | LOG_DISCOVERY | LOG_NODE,
+ "6228 Sending LOGO, determined nlp_type "
+ "0x%x nlp_flag x%lx refcnt %u\n",
+ ndlp->nlp_type, ndlp->nlp_flag,
+ kref_read(&ndlp->kref));
lpfc_issue_els_logo(vport, ndlp, 0);
-
- ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
return ndlp->nlp_state;
}
- ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE;
- if (ndlp->nlp_type & NLP_FCP_TARGET)
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE);
- else
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+out_err:
+ /* The ndlp state cannot move to MAPPED or UNMAPPED before all PRLIs
+ * are complete.
+ */
+ if (ndlp->fc4_prli_sent == 0) {
+ ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE;
+ if (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET))
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE);
+ else if (ndlp->nlp_type &
+ (NLP_FCP_INITIATOR | NLP_NVME_INITIATOR))
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+ } else
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_ELS,
+ "3067 PRLI's still outstanding "
+ "on x%06x - count %d, Pend Node Mode "
+ "transition...\n",
+ ndlp->nlp_DID, ndlp->fc4_prli_sent);
+
return ndlp->nlp_state;
}
@@ -1625,20 +2314,15 @@ static uint32_t
lpfc_device_rm_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NODEV_REMOVE;
- spin_unlock_irq(shost->host_lock);
+ if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
+ set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
return ndlp->nlp_state;
- } else {
- /* software abort outstanding PLOGI */
- lpfc_els_abort(vport->phba, ndlp);
-
- lpfc_drop_node(vport, ndlp);
- return NLP_STE_FREED_NODE;
}
+ /* software abort outstanding PLOGI */
+ lpfc_els_abort(vport->phba, ndlp);
+
+ lpfc_drop_node(vport, ndlp);
+ return NLP_STE_FREED_NODE;
}
@@ -1664,13 +2348,10 @@ lpfc_device_recov_prli_issue(struct lpfc_vport *vport,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- /* Don't do anything that will mess up processing of the
- * previous RSCN.
- */
- if (vport->fc_flag & FC_RSCN_DEFERRED)
+ /* Don't do anything that disrupts the RSCN unless lpfc is unloading. */
+ if (lpfc_check_unload_and_clr_rscn(&vport->fc_flag))
return ndlp->nlp_state;
/* software abort outstanding PRLI */
@@ -1678,14 +2359,121 @@ lpfc_device_recov_prli_issue(struct lpfc_vport *vport,
ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ lpfc_disc_set_adisc(vport, ndlp);
+ return ndlp->nlp_state;
+}
+
+static uint32_t
+lpfc_rcv_plogi_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg;
+ struct ls_rjt stat;
+
+ memset(&stat, 0, sizeof(struct ls_rjt));
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return ndlp->nlp_state;
+}
+
+static uint32_t
+lpfc_rcv_prli_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg;
+ struct ls_rjt stat;
+
+ memset(&stat, 0, sizeof(struct ls_rjt));
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return ndlp->nlp_state;
+}
+
+static uint32_t
+lpfc_rcv_logo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg;
+
+ set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
+ lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+ return ndlp->nlp_state;
+}
+
+static uint32_t
+lpfc_rcv_padisc_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg;
+ struct ls_rjt stat;
+
+ memset(&stat, 0, sizeof(struct ls_rjt));
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return ndlp->nlp_state;
+}
+
+static uint32_t
+lpfc_rcv_prlo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg;
+ struct ls_rjt stat;
+
+ memset(&stat, 0, sizeof(struct ls_rjt));
+ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+ stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return ndlp->nlp_state;
+}
+
+static uint32_t
+lpfc_cmpl_logo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ ndlp->nlp_prev_state = NLP_STE_LOGO_ISSUE;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
lpfc_disc_set_adisc(vport, ndlp);
return ndlp->nlp_state;
}
static uint32_t
+lpfc_device_rm_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ /*
+ * DevLoss has timed out and is calling for Device Remove.
+ * In this case, abort the LOGO and cleanup the ndlp
+ */
+
+ lpfc_unreg_rpi(vport, ndlp);
+ /* software abort outstanding PLOGI */
+ lpfc_els_abort(vport->phba, ndlp);
+ lpfc_drop_node(vport, ndlp);
+ return NLP_STE_FREED_NODE;
+}
+
+static uint32_t
+lpfc_device_recov_logo_issue(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp,
+ void *arg, uint32_t evt)
+{
+ /*
+ * Device Recovery events have no meaning for a node with a LOGO
+ * outstanding. The LOGO has to complete first and handle the
+ * node from that point.
+ */
+ return ndlp->nlp_state;
+}
+
+static uint32_t
lpfc_rcv_plogi_unmap_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
@@ -1701,6 +2489,9 @@ lpfc_rcv_prli_unmap_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
+ if (!lpfc_rcv_prli_support_check(vport, ndlp, cmdiocb))
+ return ndlp->nlp_state;
+
lpfc_rcv_prli(vport, ndlp, cmdiocb);
lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
return ndlp->nlp_state;
@@ -1737,18 +2528,28 @@ lpfc_rcv_prlo_unmap_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
static uint32_t
+lpfc_device_rm_unmap_node(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp,
+ void *arg,
+ uint32_t evt)
+{
+ lpfc_drop_node(vport, ndlp);
+ return NLP_STE_FREED_NODE;
+}
+
+static uint32_t
lpfc_device_recov_unmap_node(struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
ndlp->nlp_prev_state = NLP_STE_UNMAPPED_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ spin_lock_irq(&ndlp->lock);
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
+ spin_unlock_irq(&ndlp->lock);
lpfc_disc_set_adisc(vport, ndlp);
return ndlp->nlp_state;
@@ -1770,6 +2571,8 @@ lpfc_rcv_prli_mapped_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
{
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
+ if (!lpfc_rcv_prli_support_check(vport, ndlp, cmdiocb))
+ return ndlp->nlp_state;
lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp);
return ndlp->nlp_state;
}
@@ -1799,15 +2602,29 @@ static uint32_t
lpfc_rcv_prlo_mapped_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
/* flush the target */
- lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
- ndlp->nlp_sid, 0, LPFC_CTX_TGT);
+ lpfc_sli_abort_iocb(vport, ndlp->nlp_sid, 0, LPFC_CTX_TGT);
+
+ /* Send PRLO_ACC */
+ set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
+ lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL);
+
+ /* Save ELS_CMD_PRLO as the last elscmd and then set to NPR.
+ * lpfc_cmpl_els_logo_acc is expected to restart discovery.
+ */
+ ndlp->nlp_last_elscmd = ELS_CMD_PRLO;
+ ndlp->nlp_prev_state = ndlp->nlp_state;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_ELS | LOG_DISCOVERY,
+ "3422 DID x%06x nflag x%lx lastels x%x ref cnt %u\n",
+ ndlp->nlp_DID, ndlp->nlp_flag,
+ ndlp->nlp_last_elscmd,
+ kref_read(&ndlp->kref));
+
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- /* Treat like rcv logo */
- lpfc_rcv_logo(vport, ndlp, cmdiocb, ELS_CMD_PRLO);
return ndlp->nlp_state;
}
@@ -1817,14 +2634,15 @@ lpfc_device_recov_mapped_node(struct lpfc_vport *vport,
void *arg,
uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ lpfc_disc_set_adisc(vport, ndlp);
ndlp->nlp_prev_state = NLP_STE_MAPPED_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
- lpfc_disc_set_adisc(vport, ndlp);
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ spin_lock_irq(&ndlp->lock);
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
+ spin_unlock_irq(&ndlp->lock);
return ndlp->nlp_state;
}
@@ -1832,20 +2650,19 @@ static uint32_t
lpfc_rcv_plogi_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
/* Ignore PLOGI if we have an outstanding LOGO */
- if (ndlp->nlp_flag & (NLP_LOGO_SND | NLP_LOGO_ACC))
+ if (test_bit(NLP_LOGO_SND, &ndlp->nlp_flag) ||
+ test_bit(NLP_LOGO_ACC, &ndlp->nlp_flag))
return ndlp->nlp_state;
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) {
lpfc_cancel_retry_delay_tmo(vport, ndlp);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
- } else if (!(ndlp->nlp_flag & NLP_NPR_2B_DISC)) {
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ } else if (!test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
/* send PLOGI immediately, move to PLOGI issue state */
- if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+ if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) {
ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
@@ -1858,7 +2675,6 @@ static uint32_t
lpfc_rcv_prli_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
struct ls_rjt stat;
@@ -1867,15 +2683,14 @@ lpfc_rcv_prli_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
- if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
- if (ndlp->nlp_flag & NLP_NPR_ADISC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
- ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
- spin_unlock_irq(shost->host_lock);
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_ADISC_ISSUE);
- lpfc_issue_els_adisc(vport, ndlp, 0);
- } else {
+ if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) {
+ /*
+ * ADISC nodes will be handled in regular discovery path after
+ * receiving response from NS.
+ *
+ * For other nodes, Send PLOGI to trigger an implicit LOGO.
+ */
+ if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) {
ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
@@ -1906,14 +2721,15 @@ lpfc_rcv_padisc_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* or discovery in progress for this node. Starting discovery
* here will affect the counting of discovery threads.
*/
- if (!(ndlp->nlp_flag & NLP_DELAY_TMO) &&
- !(ndlp->nlp_flag & NLP_NPR_2B_DISC)) {
- if (ndlp->nlp_flag & NLP_NPR_ADISC) {
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
- ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_ADISC_ISSUE);
- lpfc_issue_els_adisc(vport, ndlp, 0);
- } else {
+ if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag) &&
+ !test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
+ /*
+ * ADISC nodes will be handled in regular discovery path after
+ * receiving response from NS.
+ *
+ * For other nodes, Send PLOGI to trigger an implicit LOGO.
+ */
+ if (!test_bit(NLP_NPR_ADISC, &ndlp->nlp_flag)) {
ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
@@ -1926,26 +2742,20 @@ static uint32_t
lpfc_rcv_prlo_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_LOGO_ACC;
- spin_unlock_irq(shost->host_lock);
+ set_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
- if ((ndlp->nlp_flag & NLP_DELAY_TMO) == 0) {
- mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_DELAY_TMO;
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
+ if (!test_bit(NLP_DELAY_TMO, &ndlp->nlp_flag)) {
+ mod_timer(&ndlp->nlp_delayfunc,
+ jiffies + secs_to_jiffies(1));
+ set_bit(NLP_DELAY_TMO, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
} else {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_NPR_ADISC;
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NPR_ADISC, &ndlp->nlp_flag);
}
return ndlp->nlp_state;
}
@@ -1954,17 +2764,18 @@ static uint32_t
lpfc_cmpl_plogi_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
+ struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb, *rspiocb;
- IOCB_t *irsp;
+ u32 ulp_status;
cmdiocb = (struct lpfc_iocbq *) arg;
- rspiocb = cmdiocb->context_un.rsp_iocb;
+ rspiocb = cmdiocb->rsp_iocb;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
- irsp = &rspiocb->iocb;
- if (irsp->ulpStatus) {
- ndlp->nlp_flag |= NLP_DEFER_RM;
+ if (ulp_status)
return NLP_STE_FREED_NODE;
- }
+
return ndlp->nlp_state;
}
@@ -1972,14 +2783,16 @@ static uint32_t
lpfc_cmpl_prli_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
+ struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb, *rspiocb;
- IOCB_t *irsp;
+ u32 ulp_status;
cmdiocb = (struct lpfc_iocbq *) arg;
- rspiocb = cmdiocb->context_un.rsp_iocb;
+ rspiocb = cmdiocb->rsp_iocb;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
- irsp = &rspiocb->iocb;
- if (irsp->ulpStatus && (ndlp->nlp_flag & NLP_NODEV_REMOVE)) {
+ if (ulp_status && test_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag)) {
lpfc_drop_node(vport, ndlp);
return NLP_STE_FREED_NODE;
}
@@ -1990,11 +2803,10 @@ static uint32_t
lpfc_cmpl_logo_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ /* For the fabric port just clear the fc flags. */
if (ndlp->nlp_DID == Fabric_DID) {
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_FABRIC, &vport->fc_flag);
+ clear_bit(FC_PUBLIC_LOOP, &vport->fc_flag);
}
lpfc_unreg_rpi(vport, ndlp);
return ndlp->nlp_state;
@@ -2004,14 +2816,16 @@ static uint32_t
lpfc_cmpl_adisc_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
+ struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb, *rspiocb;
- IOCB_t *irsp;
+ u32 ulp_status;
cmdiocb = (struct lpfc_iocbq *) arg;
- rspiocb = cmdiocb->context_un.rsp_iocb;
+ rspiocb = cmdiocb->rsp_iocb;
+
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
- irsp = &rspiocb->iocb;
- if (irsp->ulpStatus && (ndlp->nlp_flag & NLP_NODEV_REMOVE)) {
+ if (ulp_status && test_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag)) {
lpfc_drop_node(vport, ndlp);
return NLP_STE_FREED_NODE;
}
@@ -2030,9 +2844,11 @@ lpfc_cmpl_reglogin_npr_node(struct lpfc_vport *vport,
/* SLI4 ports have preallocated logical rpis. */
if (vport->phba->sli_rev < LPFC_SLI_REV4)
ndlp->nlp_rpi = mb->un.varWords[0];
- ndlp->nlp_flag |= NLP_RPI_REGISTERED;
+ set_bit(NLP_RPI_REGISTERED, &ndlp->nlp_flag);
+ if (test_bit(NLP_LOGO_ACC, &ndlp->nlp_flag))
+ lpfc_unreg_rpi(vport, ndlp);
} else {
- if (ndlp->nlp_flag & NLP_NODEV_REMOVE) {
+ if (test_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag)) {
lpfc_drop_node(vport, ndlp);
return NLP_STE_FREED_NODE;
}
@@ -2044,12 +2860,8 @@ static uint32_t
lpfc_device_rm_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag |= NLP_NODEV_REMOVE;
- spin_unlock_irq(shost->host_lock);
+ if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag)) {
+ set_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
return ndlp->nlp_state;
}
lpfc_drop_node(vport, ndlp);
@@ -2060,18 +2872,16 @@ static uint32_t
lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
-
- /* Don't do anything that will mess up processing of the
- * previous RSCN.
- */
- if (vport->fc_flag & FC_RSCN_DEFERRED)
+ /* Don't do anything that disrupts the RSCN unless lpfc is unloading. */
+ if (lpfc_check_unload_and_clr_rscn(&vport->fc_flag))
return ndlp->nlp_state;
lpfc_cancel_retry_delay_tmo(vport, ndlp);
- spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
- spin_unlock_irq(shost->host_lock);
+ clear_bit(NLP_NODEV_REMOVE, &ndlp->nlp_flag);
+ clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
+ spin_lock_irq(&ndlp->lock);
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
+ spin_unlock_irq(&ndlp->lock);
return ndlp->nlp_state;
}
@@ -2147,7 +2957,7 @@ static uint32_t (*lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT])
lpfc_disc_illegal, /* CMPL_ADISC */
lpfc_disc_illegal, /* CMPL_REG_LOGIN */
lpfc_device_rm_unused_node, /* DEVICE_RM */
- lpfc_disc_illegal, /* DEVICE_RECOVERY */
+ lpfc_device_recov_unused_node, /* DEVICE_RECOVERY */
lpfc_rcv_plogi_plogi_issue, /* RCV_PLOGI PLOGI_ISSUE */
lpfc_rcv_prli_plogi_issue, /* RCV_PRLI */
@@ -2205,6 +3015,20 @@ static uint32_t (*lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT])
lpfc_device_rm_prli_issue, /* DEVICE_RM */
lpfc_device_recov_prli_issue, /* DEVICE_RECOVERY */
+ lpfc_rcv_plogi_logo_issue, /* RCV_PLOGI LOGO_ISSUE */
+ lpfc_rcv_prli_logo_issue, /* RCV_PRLI */
+ lpfc_rcv_logo_logo_issue, /* RCV_LOGO */
+ lpfc_rcv_padisc_logo_issue, /* RCV_ADISC */
+ lpfc_rcv_padisc_logo_issue, /* RCV_PDISC */
+ lpfc_rcv_prlo_logo_issue, /* RCV_PRLO */
+ lpfc_cmpl_plogi_illegal, /* CMPL_PLOGI */
+ lpfc_disc_illegal, /* CMPL_PRLI */
+ lpfc_cmpl_logo_logo_issue, /* CMPL_LOGO */
+ lpfc_disc_illegal, /* CMPL_ADISC */
+ lpfc_disc_illegal, /* CMPL_REG_LOGIN */
+ lpfc_device_rm_logo_issue, /* DEVICE_RM */
+ lpfc_device_recov_logo_issue, /* DEVICE_RECOVERY */
+
lpfc_rcv_plogi_unmap_node, /* RCV_PLOGI UNMAPPED_NODE */
lpfc_rcv_prli_unmap_node, /* RCV_PRLI */
lpfc_rcv_logo_unmap_node, /* RCV_LOGO */
@@ -2216,7 +3040,7 @@ static uint32_t (*lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT])
lpfc_disc_illegal, /* CMPL_LOGO */
lpfc_disc_illegal, /* CMPL_ADISC */
lpfc_disc_illegal, /* CMPL_REG_LOGIN */
- lpfc_disc_illegal, /* DEVICE_RM */
+ lpfc_device_rm_unmap_node, /* DEVICE_RM */
lpfc_device_recov_unmap_node, /* DEVICE_RECOVERY */
lpfc_rcv_plogi_mapped_node, /* RCV_PLOGI MAPPED_NODE */
@@ -2256,17 +3080,21 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint32_t(*func) (struct lpfc_vport *, struct lpfc_nodelist *, void *,
uint32_t);
uint32_t got_ndlp = 0;
+ uint32_t data1;
if (lpfc_nlp_get(ndlp))
got_ndlp = 1;
cur_state = ndlp->nlp_state;
+ data1 = (((uint32_t)ndlp->nlp_fc4_type << 16) |
+ ((uint32_t)ndlp->nlp_type));
/* DSM in event <evt> on NPort <nlp_DID> in state <cur_state> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0211 DSM in event x%x on NPort x%x in "
- "state %d Data: x%x\n",
- evt, ndlp->nlp_DID, cur_state, ndlp->nlp_flag);
+ "state %d rpi x%x Data: x%lx x%x\n",
+ evt, ndlp->nlp_DID, cur_state, ndlp->nlp_rpi,
+ ndlp->nlp_flag, data1);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_DSM,
"DSM in: evt:%d ste:%d did:x%x",
@@ -2277,12 +3105,16 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* DSM out state <rc> on NPort <nlp_DID> */
if (got_ndlp) {
+ data1 = (((uint32_t)ndlp->nlp_fc4_type << 16) |
+ ((uint32_t)ndlp->nlp_type));
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0212 DSM out state %d on NPort x%x Data: x%x\n",
- rc, ndlp->nlp_DID, ndlp->nlp_flag);
+ "0212 DSM out state %d on NPort x%x "
+ "rpi x%x Data: x%lx x%x\n",
+ rc, ndlp->nlp_DID, ndlp->nlp_rpi, ndlp->nlp_flag,
+ data1);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_DSM,
- "DSM out: ste:%d did:x%x flg:x%x",
+ "DSM out: ste:%d did:x%x flg:x%lx",
rc, ndlp->nlp_DID, ndlp->nlp_flag);
/* Decrement the ndlp reference count held for this function */
lpfc_nlp_put(ndlp);
diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c
new file mode 100644
index 000000000000..e6f632521cff
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_nvme.c
@@ -0,0 +1,2887 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.broadcom.com *
+ * Portions Copyright (C) 2004-2005 Christoph Hellwig *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ ********************************************************************/
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/unaligned.h>
+#include <linux/crc-t10dif.h>
+#include <net/checksum.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
+
+#include "lpfc_version.h"
+#include "lpfc_hw4.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
+#include "lpfc_nl.h"
+#include "lpfc_disc.h"
+#include "lpfc.h"
+#include "lpfc_nvme.h"
+#include "lpfc_scsi.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+#include "lpfc_vport.h"
+#include "lpfc_debugfs.h"
+
+/* NVME initiator-based functions */
+
+static struct lpfc_io_buf *
+lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ int idx, int expedite);
+
+static void
+lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_io_buf *);
+
+static struct nvme_fc_port_template lpfc_nvme_template;
+
+/**
+ * lpfc_nvme_create_queue -
+ * @pnvme_lport: Transport localport that LS is to be issued from
+ * @qidx: An cpu index used to affinitize IO queues and MSIX vectors.
+ * @qsize: Size of the queue in bytes
+ * @handle: An opaque driver handle used in follow-up calls.
+ *
+ * Driver registers this routine to preallocate and initialize any
+ * internal data structures to bind the @qidx to its internal IO queues.
+ * A hardware queue maps (qidx) to a specific driver MSI-X vector/EQ/CQ/WQ.
+ *
+ * Return value :
+ * 0 - Success
+ * -EINVAL - Unsupported input value.
+ * -ENOMEM - Could not alloc necessary memory
+ **/
+static int
+lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport,
+ unsigned int qidx, u16 qsize,
+ void **handle)
+{
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_vport *vport;
+ struct lpfc_nvme_qhandle *qhandle;
+ char *str;
+
+ if (!pnvme_lport->private)
+ return -ENOMEM;
+
+ lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+ vport = lport->vport;
+
+ if (!vport || test_bit(FC_UNLOADING, &vport->load_flag) ||
+ test_bit(HBA_IOQ_FLUSH, &vport->phba->hba_flag))
+ return -ENODEV;
+
+ qhandle = kzalloc(sizeof(struct lpfc_nvme_qhandle), GFP_KERNEL);
+ if (qhandle == NULL)
+ return -ENOMEM;
+
+ qhandle->cpu_id = raw_smp_processor_id();
+ qhandle->qidx = qidx;
+ /*
+ * NVME qidx == 0 is the admin queue, so both admin queue
+ * and first IO queue will use MSI-X vector and associated
+ * EQ/CQ/WQ at index 0. After that they are sequentially assigned.
+ */
+ if (qidx) {
+ str = "IO "; /* IO queue */
+ qhandle->index = ((qidx - 1) %
+ lpfc_nvme_template.max_hw_queues);
+ } else {
+ str = "ADM"; /* Admin queue */
+ qhandle->index = qidx;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+ "6073 Binding %s HdwQueue %d (cpu %d) to "
+ "hdw_queue %d qhandle x%px\n", str,
+ qidx, qhandle->cpu_id, qhandle->index, qhandle);
+ *handle = (void *)qhandle;
+ return 0;
+}
+
+/**
+ * lpfc_nvme_delete_queue -
+ * @pnvme_lport: Transport localport that LS is to be issued from
+ * @qidx: An cpu index used to affinitize IO queues and MSIX vectors.
+ * @handle: An opaque driver handle from lpfc_nvme_create_queue
+ *
+ * Driver registers this routine to free
+ * any internal data structures to bind the @qidx to its internal
+ * IO queues.
+ *
+ * Return value :
+ * 0 - Success
+ * TODO: What are the failure codes.
+ **/
+static void
+lpfc_nvme_delete_queue(struct nvme_fc_local_port *pnvme_lport,
+ unsigned int qidx,
+ void *handle)
+{
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_vport *vport;
+
+ if (!pnvme_lport->private)
+ return;
+
+ lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+ vport = lport->vport;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+ "6001 ENTER. lpfc_pnvme x%px, qidx x%x qhandle x%px\n",
+ lport, qidx, handle);
+ kfree(handle);
+}
+
+static void
+lpfc_nvme_localport_delete(struct nvme_fc_local_port *localport)
+{
+ struct lpfc_nvme_lport *lport = localport->private;
+
+ lpfc_printf_vlog(lport->vport, KERN_INFO, LOG_NVME,
+ "6173 localport x%px delete complete\n",
+ lport);
+
+ /* release any threads waiting for the unreg to complete */
+ if (lport->vport->localport)
+ complete(lport->lport_unreg_cmp);
+}
+
+/* lpfc_nvme_remoteport_delete
+ *
+ * @remoteport: Pointer to an nvme transport remoteport instance.
+ *
+ * This is a template downcall. NVME transport calls this function
+ * when it has completed the unregistration of a previously
+ * registered remoteport.
+ *
+ * Return value :
+ * None
+ */
+static void
+lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport)
+{
+ struct lpfc_nvme_rport *rport = remoteport->private;
+ struct lpfc_vport *vport;
+ struct lpfc_nodelist *ndlp;
+ u32 fc4_xpt_flags;
+
+ ndlp = rport->ndlp;
+ if (!ndlp) {
+ pr_err("**** %s: NULL ndlp on rport x%px remoteport x%px\n",
+ __func__, rport, remoteport);
+ goto rport_err;
+ }
+
+ vport = ndlp->vport;
+ if (!vport) {
+ pr_err("**** %s: Null vport on ndlp x%px, ste x%x rport x%px\n",
+ __func__, ndlp, ndlp->nlp_state, rport);
+ goto rport_err;
+ }
+
+ fc4_xpt_flags = NVME_XPT_REGD | SCSI_XPT_REGD;
+
+ /* Remove this rport from the lport's list - memory is owned by the
+ * transport. Remove the ndlp reference for the NVME transport before
+ * calling state machine to remove the node.
+ */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6146 remoteport delete of remoteport x%px, ndlp x%px "
+ "DID x%x xflags x%x\n",
+ remoteport, ndlp, ndlp->nlp_DID, ndlp->fc4_xpt_flags);
+ spin_lock_irq(&ndlp->lock);
+
+ /* The register rebind might have occurred before the delete
+ * downcall. Guard against this race.
+ */
+ if (ndlp->fc4_xpt_flags & NVME_XPT_UNREG_WAIT)
+ ndlp->fc4_xpt_flags &= ~(NVME_XPT_UNREG_WAIT | NVME_XPT_REGD);
+
+ spin_unlock_irq(&ndlp->lock);
+
+ /* On a devloss timeout event, one more put is executed provided the
+ * NVME and SCSI rport unregister requests are complete.
+ */
+ if (!(ndlp->fc4_xpt_flags & fc4_xpt_flags))
+ lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
+
+ rport_err:
+ return;
+}
+
+/**
+ * lpfc_nvme_handle_lsreq - Process an unsolicited NVME LS request
+ * @phba: pointer to lpfc hba data structure.
+ * @axchg: pointer to exchange context for the NVME LS request
+ *
+ * This routine is used for processing an asynchronously received NVME LS
+ * request. Any remaining validation is done and the LS is then forwarded
+ * to the nvme-fc transport via nvme_fc_rcv_ls_req().
+ *
+ * The calling sequence should be: nvme_fc_rcv_ls_req() -> (processing)
+ * -> lpfc_nvme_xmt_ls_rsp/cmp -> req->done.
+ * __lpfc_nvme_xmt_ls_rsp_cmp should free the allocated axchg.
+ *
+ * Returns 0 if LS was handled and delivered to the transport
+ * Returns 1 if LS failed to be handled and should be dropped
+ */
+int
+lpfc_nvme_handle_lsreq(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *axchg)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ struct lpfc_vport *vport;
+ struct lpfc_nvme_rport *lpfc_rport;
+ struct nvme_fc_remote_port *remoteport;
+ struct lpfc_nvme_lport *lport;
+ uint32_t *payload = axchg->payload;
+ int rc;
+
+ vport = axchg->ndlp->vport;
+ lpfc_rport = axchg->ndlp->nrport;
+ if (!lpfc_rport)
+ return -EINVAL;
+
+ remoteport = lpfc_rport->remoteport;
+ if (!vport->localport ||
+ test_bit(HBA_IOQ_FLUSH, &vport->phba->hba_flag))
+ return -EINVAL;
+
+ lport = vport->localport->private;
+ if (!lport)
+ return -EINVAL;
+
+ rc = nvme_fc_rcv_ls_req(remoteport, &axchg->ls_rsp, axchg->payload,
+ axchg->size);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6205 NVME Unsol rcv: sz %d rc %d: %08x %08x %08x "
+ "%08x %08x %08x\n",
+ axchg->size, rc,
+ *payload, *(payload+1), *(payload+2),
+ *(payload+3), *(payload+4), *(payload+5));
+
+ if (!rc)
+ return 0;
+#endif
+ return 1;
+}
+
+/**
+ * __lpfc_nvme_ls_req_cmp - Generic completion handler for a NVME
+ * LS request.
+ * @phba: Pointer to HBA context object
+ * @vport: The local port that issued the LS
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @wcqe: Pointer to driver response CQE object.
+ *
+ * This function is the generic completion handler for NVME LS requests.
+ * The function updates any states and statistics, calls the transport
+ * ls_req done() routine, then tears down the command and buffers used
+ * for the LS request.
+ **/
+void
+__lpfc_nvme_ls_req_cmp(struct lpfc_hba *phba, struct lpfc_vport *vport,
+ struct lpfc_iocbq *cmdwqe,
+ struct lpfc_wcqe_complete *wcqe)
+{
+ struct nvmefc_ls_req *pnvme_lsreq;
+ struct lpfc_dmabuf *buf_ptr;
+ struct lpfc_nodelist *ndlp;
+ int status;
+
+ pnvme_lsreq = cmdwqe->context_un.nvme_lsreq;
+ ndlp = cmdwqe->ndlp;
+ buf_ptr = cmdwqe->bpl_dmabuf;
+
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6047 NVMEx LS REQ x%px cmpl DID %x Xri: %x "
+ "status %x reason x%x cmd:x%px lsreg:x%px bmp:x%px "
+ "ndlp:x%px\n",
+ pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0,
+ cmdwqe->sli4_xritag, status,
+ (wcqe->parameter & 0xffff),
+ cmdwqe, pnvme_lsreq, cmdwqe->bpl_dmabuf,
+ ndlp);
+
+ lpfc_nvmeio_data(phba, "NVMEx LS CMPL: xri x%x stat x%x parm x%x\n",
+ cmdwqe->sli4_xritag, status, wcqe->parameter);
+
+ if (buf_ptr) {
+ lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+ kfree(buf_ptr);
+ cmdwqe->bpl_dmabuf = NULL;
+ }
+ if (pnvme_lsreq->done) {
+ if (status != CQE_STATUS_SUCCESS)
+ status = -ENXIO;
+ pnvme_lsreq->done(pnvme_lsreq, status);
+ } else {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6046 NVMEx cmpl without done call back? "
+ "Data x%px DID %x Xri: %x status %x\n",
+ pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0,
+ cmdwqe->sli4_xritag, status);
+ }
+ if (ndlp) {
+ lpfc_nlp_put(ndlp);
+ cmdwqe->ndlp = NULL;
+ }
+ lpfc_sli_release_iocbq(phba, cmdwqe);
+}
+
+static void
+lpfc_nvme_ls_req_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_vport *vport = cmdwqe->vport;
+ struct lpfc_nvme_lport *lport;
+ uint32_t status;
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+
+ if (vport->localport) {
+ lport = (struct lpfc_nvme_lport *)vport->localport->private;
+ if (lport) {
+ atomic_inc(&lport->fc4NvmeLsCmpls);
+ if (status) {
+ if (bf_get(lpfc_wcqe_c_xb, wcqe))
+ atomic_inc(&lport->cmpl_ls_xb);
+ atomic_inc(&lport->cmpl_ls_err);
+ }
+ }
+ }
+
+ __lpfc_nvme_ls_req_cmp(phba, vport, cmdwqe, wcqe);
+}
+
+static int
+lpfc_nvme_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
+ struct lpfc_dmabuf *inp,
+ struct nvmefc_ls_req *pnvme_lsreq,
+ void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *),
+ struct lpfc_nodelist *ndlp, uint32_t num_entry,
+ uint32_t tmo, uint8_t retry)
+{
+ struct lpfc_hba *phba = vport->phba;
+ union lpfc_wqe128 *wqe;
+ struct lpfc_iocbq *genwqe;
+ struct ulp_bde64 *bpl;
+ struct ulp_bde64 bde;
+ int i, rc, xmit_len, first_len;
+
+ /* Allocate buffer for command WQE */
+ genwqe = lpfc_sli_get_iocbq(phba);
+ if (genwqe == NULL)
+ return 1;
+
+ wqe = &genwqe->wqe;
+ /* Initialize only 64 bytes */
+ memset(wqe, 0, sizeof(union lpfc_wqe));
+
+ genwqe->bpl_dmabuf = bmp;
+ genwqe->cmd_flag |= LPFC_IO_NVME_LS;
+
+ /* Save for completion so we can release these resources */
+ genwqe->ndlp = lpfc_nlp_get(ndlp);
+ if (!genwqe->ndlp) {
+ dev_warn(&phba->pcidev->dev,
+ "Warning: Failed node ref, not sending LS_REQ\n");
+ lpfc_sli_release_iocbq(phba, genwqe);
+ return 1;
+ }
+
+ genwqe->context_un.nvme_lsreq = pnvme_lsreq;
+ /* Fill in payload, bp points to frame payload */
+
+ if (!tmo)
+ /* FC spec states we need 3 * ratov for CT requests */
+ tmo = (3 * phba->fc_ratov);
+
+ /* For this command calculate the xmit length of the request bde. */
+ xmit_len = 0;
+ first_len = 0;
+ bpl = (struct ulp_bde64 *)bmp->virt;
+ for (i = 0; i < num_entry; i++) {
+ bde.tus.w = bpl[i].tus.w;
+ if (bde.tus.f.bdeFlags != BUFF_TYPE_BDE_64)
+ break;
+ xmit_len += bde.tus.f.bdeSize;
+ if (i == 0)
+ first_len = xmit_len;
+ }
+
+ genwqe->num_bdes = num_entry;
+ genwqe->hba_wqidx = 0;
+
+ /* Words 0 - 2 */
+ wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ wqe->generic.bde.tus.f.bdeSize = first_len;
+ wqe->generic.bde.addrLow = bpl[0].addrLow;
+ wqe->generic.bde.addrHigh = bpl[0].addrHigh;
+
+ /* Word 3 */
+ wqe->gen_req.request_payload_len = first_len;
+
+ /* Word 4 */
+
+ /* Word 5 */
+ bf_set(wqe_dfctl, &wqe->gen_req.wge_ctl, 0);
+ bf_set(wqe_si, &wqe->gen_req.wge_ctl, 1);
+ bf_set(wqe_la, &wqe->gen_req.wge_ctl, 1);
+ bf_set(wqe_rctl, &wqe->gen_req.wge_ctl, FC_RCTL_ELS4_REQ);
+ bf_set(wqe_type, &wqe->gen_req.wge_ctl, FC_TYPE_NVME);
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->gen_req.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe->gen_req.wqe_com, genwqe->sli4_xritag);
+
+ /* Word 7 */
+ bf_set(wqe_tmo, &wqe->gen_req.wqe_com, tmo);
+ bf_set(wqe_class, &wqe->gen_req.wqe_com, CLASS3);
+ bf_set(wqe_cmnd, &wqe->gen_req.wqe_com, CMD_GEN_REQUEST64_WQE);
+ bf_set(wqe_ct, &wqe->gen_req.wqe_com, SLI4_CT_RPI);
+
+ /* Word 8 */
+ wqe->gen_req.wqe_com.abort_tag = genwqe->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->gen_req.wqe_com, genwqe->iotag);
+
+ /* Word 10 */
+ bf_set(wqe_dbde, &wqe->gen_req.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->gen_req.wqe_com, LPFC_WQE_IOD_READ);
+ bf_set(wqe_qosd, &wqe->gen_req.wqe_com, 1);
+ bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE);
+ bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0);
+
+ /* Word 11 */
+ bf_set(wqe_cqid, &wqe->gen_req.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_cmd_type, &wqe->gen_req.wqe_com, OTHER_COMMAND);
+
+
+ /* Issue GEN REQ WQE for NPORT <did> */
+ genwqe->cmd_cmpl = cmpl;
+ genwqe->drvrTimeout = tmo + LPFC_DRVR_TIMEOUT;
+ genwqe->vport = vport;
+ genwqe->retry = retry;
+
+ lpfc_nvmeio_data(phba, "NVME LS XMIT: xri x%x iotag x%x to x%06x\n",
+ genwqe->sli4_xritag, genwqe->iotag, ndlp->nlp_DID);
+
+ rc = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[0], genwqe);
+ if (rc) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6045 Issue GEN REQ WQE to NPORT x%x "
+ "Data: x%x x%x rc x%x\n",
+ ndlp->nlp_DID, genwqe->iotag,
+ vport->port_state, rc);
+ lpfc_nlp_put(ndlp);
+ lpfc_sli_release_iocbq(phba, genwqe);
+ return 1;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC | LOG_ELS,
+ "6050 Issue GEN REQ WQE to NPORT x%x "
+ "Data: oxid: x%x state: x%x wq:x%px lsreq:x%px "
+ "bmp:x%px xmit:%d 1st:%d\n",
+ ndlp->nlp_DID, genwqe->sli4_xritag,
+ vport->port_state,
+ genwqe, pnvme_lsreq, bmp, xmit_len, first_len);
+ return 0;
+}
+
+
+/**
+ * __lpfc_nvme_ls_req - Generic service routine to issue an NVME LS request
+ * @vport: The local port issuing the LS
+ * @ndlp: The remote port to send the LS to
+ * @pnvme_lsreq: Pointer to LS request structure from the transport
+ * @gen_req_cmp: Completion call-back
+ *
+ * Routine validates the ndlp, builds buffers and sends a GEN_REQUEST
+ * WQE to perform the LS operation.
+ *
+ * Return value :
+ * 0 - Success
+ * non-zero: various error codes, in form of -Exxx
+ **/
+int
+__lpfc_nvme_ls_req(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ struct nvmefc_ls_req *pnvme_lsreq,
+ void (*gen_req_cmp)(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe))
+{
+ struct lpfc_dmabuf *bmp;
+ struct ulp_bde64 *bpl;
+ int ret;
+ uint16_t ntype, nstate;
+
+ if (!ndlp) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6051 NVMEx LS REQ: Bad NDLP x%px, Failing "
+ "LS Req\n",
+ ndlp);
+ return -ENODEV;
+ }
+
+ ntype = ndlp->nlp_type;
+ nstate = ndlp->nlp_state;
+ if ((ntype & NLP_NVME_TARGET && nstate != NLP_STE_MAPPED_NODE) ||
+ (ntype & NLP_NVME_INITIATOR && nstate != NLP_STE_UNMAPPED_NODE)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6088 NVMEx LS REQ: Fail DID x%06x not "
+ "ready for IO. Type x%x, State x%x\n",
+ ndlp->nlp_DID, ntype, nstate);
+ return -ENODEV;
+ }
+ if (test_bit(HBA_IOQ_FLUSH, &vport->phba->hba_flag))
+ return -ENODEV;
+
+ if (!vport->phba->sli4_hba.nvmels_wq)
+ return -ENOMEM;
+
+ /*
+ * there are two dma buf in the request, actually there is one and
+ * the second one is just the start address + cmd size.
+ * Before calling lpfc_nvme_gen_req these buffers need to be wrapped
+ * in a lpfc_dmabuf struct. When freeing we just free the wrapper
+ * because the nvem layer owns the data bufs.
+ * We do not have to break these packets open, we don't care what is
+ * in them. And we do not have to look at the resonse data, we only
+ * care that we got a response. All of the caring is going to happen
+ * in the nvme-fc layer.
+ */
+
+ bmp = kmalloc(sizeof(*bmp), GFP_KERNEL);
+ if (!bmp) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6044 NVMEx LS REQ: Could not alloc LS buf "
+ "for DID %x\n",
+ ndlp->nlp_DID);
+ return -ENOMEM;
+ }
+
+ bmp->virt = lpfc_mbuf_alloc(vport->phba, MEM_PRI, &(bmp->phys));
+ if (!bmp->virt) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6042 NVMEx LS REQ: Could not alloc mbuf "
+ "for DID %x\n",
+ ndlp->nlp_DID);
+ kfree(bmp);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&bmp->list);
+
+ bpl = (struct ulp_bde64 *)bmp->virt;
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(pnvme_lsreq->rqstdma));
+ bpl->addrLow = le32_to_cpu(putPaddrLow(pnvme_lsreq->rqstdma));
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.f.bdeSize = pnvme_lsreq->rqstlen;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(pnvme_lsreq->rspdma));
+ bpl->addrLow = le32_to_cpu(putPaddrLow(pnvme_lsreq->rspdma));
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.f.bdeSize = pnvme_lsreq->rsplen;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6149 NVMEx LS REQ: Issue to DID 0x%06x lsreq x%px, "
+ "rqstlen:%d rsplen:%d %pad %pad\n",
+ ndlp->nlp_DID, pnvme_lsreq, pnvme_lsreq->rqstlen,
+ pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma,
+ &pnvme_lsreq->rspdma);
+
+ ret = lpfc_nvme_gen_req(vport, bmp, pnvme_lsreq->rqstaddr,
+ pnvme_lsreq, gen_req_cmp, ndlp, 2,
+ pnvme_lsreq->timeout, 0);
+ if (ret != WQE_SUCCESS) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6052 NVMEx REQ: EXIT. issue ls wqe failed "
+ "lsreq x%px Status %x DID %x\n",
+ pnvme_lsreq, ret, ndlp->nlp_DID);
+ lpfc_mbuf_free(vport->phba, bmp->virt, bmp->phys);
+ kfree(bmp);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * lpfc_nvme_ls_req - Issue an NVME Link Service request
+ * @pnvme_lport: Transport localport that LS is to be issued from.
+ * @pnvme_rport: Transport remoteport that LS is to be sent to.
+ * @pnvme_lsreq: the transport nvme_ls_req structure for the LS
+ *
+ * Driver registers this routine to handle any link service request
+ * from the nvme_fc transport to a remote nvme-aware port.
+ *
+ * Return value :
+ * 0 - Success
+ * non-zero: various error codes, in form of -Exxx
+ **/
+static int
+lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport,
+ struct nvme_fc_remote_port *pnvme_rport,
+ struct nvmefc_ls_req *pnvme_lsreq)
+{
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_nvme_rport *rport;
+ struct lpfc_vport *vport;
+ int ret;
+
+ lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+ rport = (struct lpfc_nvme_rport *)pnvme_rport->private;
+ if (unlikely(!lport) || unlikely(!rport))
+ return -EINVAL;
+
+ vport = lport->vport;
+ if (test_bit(FC_UNLOADING, &vport->load_flag) ||
+ test_bit(HBA_IOQ_FLUSH, &vport->phba->hba_flag))
+ return -ENODEV;
+
+ atomic_inc(&lport->fc4NvmeLsRequests);
+
+ ret = __lpfc_nvme_ls_req(vport, rport->ndlp, pnvme_lsreq,
+ lpfc_nvme_ls_req_cmp);
+ if (ret)
+ atomic_inc(&lport->xmt_ls_err);
+
+ return ret;
+}
+
+/**
+ * __lpfc_nvme_ls_abort - Generic service routine to abort a prior
+ * NVME LS request
+ * @vport: The local port that issued the LS
+ * @ndlp: The remote port the LS was sent to
+ * @pnvme_lsreq: Pointer to LS request structure from the transport
+ *
+ * The driver validates the ndlp, looks for the LS, and aborts the
+ * LS if found.
+ *
+ * Returns:
+ * 0 : if LS found and aborted
+ * non-zero: various error conditions in form -Exxx
+ **/
+int
+__lpfc_nvme_ls_abort(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ struct nvmefc_ls_req *pnvme_lsreq)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *wqe, *next_wqe;
+ bool foundit = false;
+
+ if (!ndlp) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6049 NVMEx LS REQ Abort: Bad NDLP x%px DID "
+ "x%06x, Failing LS Req\n",
+ ndlp, ndlp ? ndlp->nlp_DID : 0);
+ return -EINVAL;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC | LOG_NVME_ABTS,
+ "6040 NVMEx LS REQ Abort: Issue LS_ABORT for lsreq "
+ "x%px rqstlen:%d rsplen:%d %pad %pad\n",
+ pnvme_lsreq, pnvme_lsreq->rqstlen,
+ pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma,
+ &pnvme_lsreq->rspdma);
+
+ /*
+ * Lock the ELS ring txcmplq and look for the wqe that matches
+ * this ELS. If found, issue an abort on the wqe.
+ */
+ pring = phba->sli4_hba.nvmels_wq->pring;
+ spin_lock_irq(&phba->hbalock);
+ spin_lock(&pring->ring_lock);
+ list_for_each_entry_safe(wqe, next_wqe, &pring->txcmplq, list) {
+ if (wqe->context_un.nvme_lsreq == pnvme_lsreq) {
+ wqe->cmd_flag |= LPFC_DRIVER_ABORTED;
+ foundit = true;
+ break;
+ }
+ }
+ spin_unlock(&pring->ring_lock);
+
+ if (foundit)
+ lpfc_sli_issue_abort_iotag(phba, pring, wqe, NULL);
+ spin_unlock_irq(&phba->hbalock);
+
+ if (foundit)
+ return 0;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC | LOG_NVME_ABTS,
+ "6213 NVMEx LS REQ Abort: Unable to locate req x%px\n",
+ pnvme_lsreq);
+ return -EINVAL;
+}
+
+static int
+lpfc_nvme_xmt_ls_rsp(struct nvme_fc_local_port *localport,
+ struct nvme_fc_remote_port *remoteport,
+ struct nvmefc_ls_rsp *ls_rsp)
+{
+ struct lpfc_async_xchg_ctx *axchg =
+ container_of(ls_rsp, struct lpfc_async_xchg_ctx, ls_rsp);
+ struct lpfc_nvme_lport *lport;
+ int rc;
+
+ if (test_bit(FC_UNLOADING, &axchg->phba->pport->load_flag))
+ return -ENODEV;
+
+ lport = (struct lpfc_nvme_lport *)localport->private;
+
+ rc = __lpfc_nvme_xmt_ls_rsp(axchg, ls_rsp, __lpfc_nvme_xmt_ls_rsp_cmp);
+
+ if (rc) {
+ /*
+ * unless the failure is due to having already sent
+ * the response, an abort will be generated for the
+ * exchange if the rsp can't be sent.
+ */
+ if (rc != -EALREADY)
+ atomic_inc(&lport->xmt_ls_abort);
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * lpfc_nvme_ls_abort - Abort a prior NVME LS request
+ * @pnvme_lport: Transport localport that LS is to be issued from.
+ * @pnvme_rport: Transport remoteport that LS is to be sent to.
+ * @pnvme_lsreq: the transport nvme_ls_req structure for the LS
+ *
+ * Driver registers this routine to abort a NVME LS request that is
+ * in progress (from the transports perspective).
+ **/
+static void
+lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport,
+ struct nvme_fc_remote_port *pnvme_rport,
+ struct nvmefc_ls_req *pnvme_lsreq)
+{
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_vport *vport;
+ struct lpfc_nodelist *ndlp;
+ int ret;
+
+ lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+ if (unlikely(!lport))
+ return;
+ vport = lport->vport;
+
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
+ return;
+
+ ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id);
+
+ ret = __lpfc_nvme_ls_abort(vport, ndlp, pnvme_lsreq);
+ if (!ret)
+ atomic_inc(&lport->xmt_ls_abort);
+}
+
+/* Fix up the existing sgls for NVME IO. */
+static inline void
+lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_ncmd,
+ struct nvmefc_fcp_req *nCmd)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct sli4_sge *sgl;
+ union lpfc_wqe128 *wqe;
+ uint32_t *wptr, *dptr;
+
+ /*
+ * Get a local pointer to the built-in wqe and correct
+ * the cmd size to match NVME's 96 bytes and fix
+ * the dma address.
+ */
+
+ wqe = &lpfc_ncmd->cur_iocbq.wqe;
+
+ /*
+ * Adjust the FCP_CMD and FCP_RSP DMA data and sge_len to
+ * match NVME. NVME sends 96 bytes. Also, use the
+ * nvme commands command and response dma addresses
+ * rather than the virtual memory to ease the restore
+ * operation.
+ */
+ sgl = lpfc_ncmd->dma_sgl;
+ sgl->sge_len = cpu_to_le32(nCmd->cmdlen);
+ if (phba->cfg_nvme_embed_cmd) {
+ sgl->addr_hi = 0;
+ sgl->addr_lo = 0;
+
+ /* Word 0-2 - NVME CMND IU (embedded payload) */
+ wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_IMMED;
+ wqe->generic.bde.tus.f.bdeSize = 56;
+ wqe->generic.bde.addrHigh = 0;
+ wqe->generic.bde.addrLow = 64; /* Word 16 */
+
+ /* Word 10 - dbde is 0, wqes is 1 in template */
+
+ /*
+ * Embed the payload in the last half of the WQE
+ * WQE words 16-30 get the NVME CMD IU payload
+ *
+ * WQE words 16-19 get payload Words 1-4
+ * WQE words 20-21 get payload Words 6-7
+ * WQE words 22-29 get payload Words 16-23
+ */
+ wptr = &wqe->words[16]; /* WQE ptr */
+ dptr = (uint32_t *)nCmd->cmdaddr; /* payload ptr */
+ dptr++; /* Skip Word 0 in payload */
+
+ *wptr++ = *dptr++; /* Word 1 */
+ *wptr++ = *dptr++; /* Word 2 */
+ *wptr++ = *dptr++; /* Word 3 */
+ *wptr++ = *dptr++; /* Word 4 */
+ dptr++; /* Skip Word 5 in payload */
+ *wptr++ = *dptr++; /* Word 6 */
+ *wptr++ = *dptr++; /* Word 7 */
+ dptr += 8; /* Skip Words 8-15 in payload */
+ *wptr++ = *dptr++; /* Word 16 */
+ *wptr++ = *dptr++; /* Word 17 */
+ *wptr++ = *dptr++; /* Word 18 */
+ *wptr++ = *dptr++; /* Word 19 */
+ *wptr++ = *dptr++; /* Word 20 */
+ *wptr++ = *dptr++; /* Word 21 */
+ *wptr++ = *dptr++; /* Word 22 */
+ *wptr = *dptr; /* Word 23 */
+ } else {
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(nCmd->cmddma));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(nCmd->cmddma));
+
+ /* Word 0-2 - NVME CMND IU Inline BDE */
+ wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ wqe->generic.bde.tus.f.bdeSize = nCmd->cmdlen;
+ wqe->generic.bde.addrHigh = sgl->addr_hi;
+ wqe->generic.bde.addrLow = sgl->addr_lo;
+
+ /* Word 10 */
+ bf_set(wqe_dbde, &wqe->generic.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->generic.wqe_com, 0);
+ }
+
+ sgl++;
+
+ /* Setup the physical region for the FCP RSP */
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(nCmd->rspdma));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(nCmd->rspdma));
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ if (nCmd->sg_cnt)
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ else
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(nCmd->rsplen);
+}
+
+
+/*
+ * lpfc_nvme_io_cmd_cmpl - Complete an NVME-over-FCP IO
+ *
+ * Driver registers this routine as it io request handler. This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ * 0 - Success
+ * TODO: What are the failure codes.
+ **/
+static void
+lpfc_nvme_io_cmd_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
+ struct lpfc_iocbq *pwqeOut)
+{
+ struct lpfc_io_buf *lpfc_ncmd = pwqeIn->io_buf;
+ struct lpfc_wcqe_complete *wcqe = &pwqeOut->wcqe_cmpl;
+ struct lpfc_vport *vport = pwqeIn->vport;
+ struct nvmefc_fcp_req *nCmd;
+ struct nvme_fc_ersp_iu *ep;
+ struct nvme_fc_cmd_iu *cp;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_nvme_fcpreq_priv *freqpriv;
+ struct lpfc_nvme_lport *lport;
+ uint32_t code, status, idx;
+ uint16_t cid, sqhd, data;
+ uint32_t *ptr;
+ uint32_t lat;
+ bool call_done = false;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ int cpu;
+#endif
+ bool offline = false;
+
+ /* Sanity check on return of outstanding command */
+ if (!lpfc_ncmd) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6071 Null lpfc_ncmd pointer. No "
+ "release, skip completion\n");
+ return;
+ }
+
+ /* Guard against abort handler being called at same time */
+ spin_lock(&lpfc_ncmd->buf_lock);
+
+ if (!lpfc_ncmd->nvmeCmd) {
+ spin_unlock(&lpfc_ncmd->buf_lock);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6066 Missing cmpl ptrs: lpfc_ncmd x%px, "
+ "nvmeCmd x%px\n",
+ lpfc_ncmd, lpfc_ncmd->nvmeCmd);
+
+ /* Release the lpfc_ncmd regardless of the missing elements. */
+ lpfc_release_nvme_buf(phba, lpfc_ncmd);
+ return;
+ }
+ nCmd = lpfc_ncmd->nvmeCmd;
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+
+ idx = lpfc_ncmd->cur_iocbq.hba_wqidx;
+ phba->sli4_hba.hdwq[idx].nvme_cstat.io_cmpls++;
+
+ if (unlikely(status && vport->localport)) {
+ lport = (struct lpfc_nvme_lport *)vport->localport->private;
+ if (lport) {
+ if (bf_get(lpfc_wcqe_c_xb, wcqe))
+ atomic_inc(&lport->cmpl_fcp_xb);
+ atomic_inc(&lport->cmpl_fcp_err);
+ }
+ }
+
+ lpfc_nvmeio_data(phba, "NVME FCP CMPL: xri x%x stat x%x parm x%x\n",
+ lpfc_ncmd->cur_iocbq.sli4_xritag,
+ status, wcqe->parameter);
+ /*
+ * Catch race where our node has transitioned, but the
+ * transport is still transitioning.
+ */
+ ndlp = lpfc_ncmd->ndlp;
+ if (!ndlp) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6062 Ignoring NVME cmpl. No ndlp\n");
+ goto out_err;
+ }
+
+ code = bf_get(lpfc_wcqe_c_code, wcqe);
+ if (code == CQE_CODE_NVME_ERSP) {
+ /* For this type of CQE, we need to rebuild the rsp */
+ ep = (struct nvme_fc_ersp_iu *)nCmd->rspaddr;
+
+ /*
+ * Get Command Id from cmd to plug into response. This
+ * code is not needed in the next NVME Transport drop.
+ */
+ cp = (struct nvme_fc_cmd_iu *)nCmd->cmdaddr;
+ cid = cp->sqe.common.command_id;
+
+ /*
+ * RSN is in CQE word 2
+ * SQHD is in CQE Word 3 bits 15:0
+ * Cmd Specific info is in CQE Word 1
+ * and in CQE Word 0 bits 15:0
+ */
+ sqhd = bf_get(lpfc_wcqe_c_sqhead, wcqe);
+
+ /* Now lets build the NVME ERSP IU */
+ ep->iu_len = cpu_to_be16(8);
+ ep->rsn = wcqe->parameter;
+ ep->xfrd_len = cpu_to_be32(nCmd->payload_length);
+ ep->rsvd12 = 0;
+ ptr = (uint32_t *)&ep->cqe.result.u64;
+ *ptr++ = wcqe->total_data_placed;
+ data = bf_get(lpfc_wcqe_c_ersp0, wcqe);
+ *ptr = (uint32_t)data;
+ ep->cqe.sq_head = sqhd;
+ ep->cqe.sq_id = nCmd->sqid;
+ ep->cqe.command_id = cid;
+ ep->cqe.status = 0;
+
+ lpfc_ncmd->status = IOSTAT_SUCCESS;
+ lpfc_ncmd->result = 0;
+ nCmd->rcv_rsplen = LPFC_NVME_ERSP_LEN;
+ nCmd->transferred_length = nCmd->payload_length;
+ } else {
+ lpfc_ncmd->status = status;
+ lpfc_ncmd->result = (wcqe->parameter & IOERR_PARAM_MASK);
+
+ /* For NVME, the only failure path that results in an
+ * IO error is when the adapter rejects it. All other
+ * conditions are a success case and resolved by the
+ * transport.
+ * IOSTAT_FCP_RSP_ERROR means:
+ * 1. Length of data received doesn't match total
+ * transfer length in WQE
+ * 2. If the RSP payload does NOT match these cases:
+ * a. RSP length 12/24 bytes and all zeros
+ * b. NVME ERSP
+ */
+ switch (lpfc_ncmd->status) {
+ case IOSTAT_SUCCESS:
+ nCmd->transferred_length = wcqe->total_data_placed;
+ nCmd->rcv_rsplen = 0;
+ nCmd->status = 0;
+ break;
+ case IOSTAT_FCP_RSP_ERROR:
+ nCmd->transferred_length = wcqe->total_data_placed;
+ nCmd->rcv_rsplen = wcqe->parameter;
+ nCmd->status = 0;
+
+ /* Get the NVME cmd details for this unique error. */
+ cp = (struct nvme_fc_cmd_iu *)nCmd->cmdaddr;
+ ep = (struct nvme_fc_ersp_iu *)nCmd->rspaddr;
+
+ /* Check if this is really an ERSP */
+ if (nCmd->rcv_rsplen == LPFC_NVME_ERSP_LEN) {
+ lpfc_ncmd->status = IOSTAT_SUCCESS;
+ lpfc_ncmd->result = 0;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+ "6084 NVME FCP_ERR ERSP: "
+ "xri %x placed x%x opcode x%x cmd_id "
+ "x%x cqe_status x%x\n",
+ lpfc_ncmd->cur_iocbq.sli4_xritag,
+ wcqe->total_data_placed,
+ cp->sqe.common.opcode,
+ cp->sqe.common.command_id,
+ ep->cqe.status);
+ break;
+ }
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6081 NVME Completion Protocol Error: "
+ "xri %x status x%x result x%x "
+ "placed x%x opcode x%x cmd_id x%x, "
+ "cqe_status x%x\n",
+ lpfc_ncmd->cur_iocbq.sli4_xritag,
+ lpfc_ncmd->status, lpfc_ncmd->result,
+ wcqe->total_data_placed,
+ cp->sqe.common.opcode,
+ cp->sqe.common.command_id,
+ ep->cqe.status);
+ break;
+ case IOSTAT_LOCAL_REJECT:
+ /* Let fall through to set command final state. */
+ if (lpfc_ncmd->result == IOERR_ABORT_REQUESTED)
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NVME_IOERR,
+ "6032 Delay Aborted cmd x%px "
+ "nvme cmd x%px, xri x%x, "
+ "xb %d\n",
+ lpfc_ncmd, nCmd,
+ lpfc_ncmd->cur_iocbq.sli4_xritag,
+ bf_get(lpfc_wcqe_c_xb, wcqe));
+ fallthrough;
+ default:
+out_err:
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6072 NVME Completion Error: xri %x "
+ "status x%x result x%x [x%x] "
+ "placed x%x\n",
+ lpfc_ncmd->cur_iocbq.sli4_xritag,
+ lpfc_ncmd->status, lpfc_ncmd->result,
+ wcqe->parameter,
+ wcqe->total_data_placed);
+ nCmd->transferred_length = 0;
+ nCmd->rcv_rsplen = 0;
+ nCmd->status = NVME_SC_INTERNAL;
+ if (pci_channel_offline(vport->phba->pcidev) ||
+ lpfc_ncmd->result == IOERR_SLI_DOWN)
+ offline = true;
+ }
+ }
+
+ /* pick up SLI4 exhange busy condition */
+ if (bf_get(lpfc_wcqe_c_xb, wcqe) && !offline)
+ lpfc_ncmd->flags |= LPFC_SBUF_XBUSY;
+ else
+ lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY;
+
+ /* Update stats and complete the IO. There is
+ * no need for dma unprep because the nvme_transport
+ * owns the dma address.
+ */
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (lpfc_ncmd->ts_cmd_start) {
+ lpfc_ncmd->ts_isr_cmpl = pwqeIn->isr_timestamp;
+ lpfc_ncmd->ts_data_io = ktime_get_ns();
+ phba->ktime_last_cmd = lpfc_ncmd->ts_data_io;
+ lpfc_io_ktime(phba, lpfc_ncmd);
+ }
+ if (unlikely(phba->hdwqstat_on & LPFC_CHECK_NVME_IO)) {
+ cpu = raw_smp_processor_id();
+ this_cpu_inc(phba->sli4_hba.c_stat->cmpl_io);
+ if (lpfc_ncmd->cpu != cpu)
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_NVME_IOERR,
+ "6701 CPU Check cmpl: "
+ "cpu %d expect %d\n",
+ cpu, lpfc_ncmd->cpu);
+ }
+#endif
+
+ /* NVME targets need completion held off until the abort exchange
+ * completes unless the NVME Rport is getting unregistered.
+ */
+
+ if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) {
+ freqpriv = nCmd->private;
+ freqpriv->nvme_buf = NULL;
+ lpfc_ncmd->nvmeCmd = NULL;
+ call_done = true;
+ }
+ spin_unlock(&lpfc_ncmd->buf_lock);
+
+ /* Check if IO qualified for CMF */
+ if (phba->cmf_active_mode != LPFC_CFG_OFF &&
+ nCmd->io_dir == NVMEFC_FCP_READ &&
+ nCmd->payload_length) {
+ /* Used when calculating average latency */
+ lat = ktime_get_ns() - lpfc_ncmd->rx_cmd_start;
+ lpfc_update_cmf_cmpl(phba, lat, nCmd->payload_length, NULL);
+ }
+
+ if (call_done)
+ nCmd->done(nCmd);
+
+ /* Call release with XB=1 to queue the IO into the abort list. */
+ lpfc_release_nvme_buf(phba, lpfc_ncmd);
+}
+
+
+/**
+ * lpfc_nvme_prep_io_cmd - Issue an NVME-over-FCP IO
+ * @vport: pointer to a host virtual N_Port data structure
+ * @lpfc_ncmd: Pointer to lpfc scsi command
+ * @pnode: pointer to a node-list data structure
+ * @cstat: pointer to the control status structure
+ *
+ * Driver registers this routine as it io request handler. This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ * 0 - Success
+ * TODO: What are the failure codes.
+ **/
+static int
+lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_ncmd,
+ struct lpfc_nodelist *pnode,
+ struct lpfc_fc4_ctrl_stat *cstat)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd;
+ struct nvme_common_command *sqe;
+ struct lpfc_iocbq *pwqeq = &lpfc_ncmd->cur_iocbq;
+ union lpfc_wqe128 *wqe = &pwqeq->wqe;
+ uint32_t req_len;
+
+ /*
+ * There are three possibilities here - use scatter-gather segment, use
+ * the single mapping, or neither.
+ */
+ if (nCmd->sg_cnt) {
+ if (nCmd->io_dir == NVMEFC_FCP_WRITE) {
+ /* From the iwrite template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_iwrite_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
+ /* Word 4 */
+ wqe->fcp_iwrite.total_xfer_len = nCmd->payload_length;
+
+ /* Word 5 */
+ if ((phba->cfg_nvme_enable_fb) &&
+ test_bit(NLP_FIRSTBURST, &pnode->nlp_flag)) {
+ req_len = lpfc_ncmd->nvmeCmd->payload_length;
+ wqe->fcp_iwrite.initial_xfer_len = min(req_len,
+ pnode->nvme_fb_size);
+ } else {
+ wqe->fcp_iwrite.initial_xfer_len = 0;
+ }
+ cstat->output_requests++;
+ } else {
+ /* From the iread template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_iread_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
+ /* Word 4 */
+ wqe->fcp_iread.total_xfer_len = nCmd->payload_length;
+
+ /* Word 5 */
+ wqe->fcp_iread.rsrvd5 = 0;
+
+ /* For a CMF Managed port, iod must be zero'ed */
+ if (phba->cmf_active_mode == LPFC_CFG_MANAGED)
+ bf_set(wqe_iod, &wqe->fcp_iread.wqe_com,
+ LPFC_WQE_IOD_NONE);
+ cstat->input_requests++;
+ }
+ } else {
+ /* From the icmnd template, initialize words 4 - 11 */
+ memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4],
+ sizeof(uint32_t) * 8);
+ cstat->control_requests++;
+ }
+
+ if (pnode->nlp_nvme_info & NLP_NVME_NSLER) {
+ bf_set(wqe_erp, &wqe->generic.wqe_com, 1);
+ sqe = &((struct nvme_fc_cmd_iu *)
+ nCmd->cmdaddr)->sqe.common;
+ if (sqe->opcode == nvme_admin_async_event)
+ bf_set(wqe_ffrq, &wqe->generic.wqe_com, 1);
+ }
+
+ /*
+ * Finish initializing those WQE fields that are independent
+ * of the nvme_cmnd request_buffer
+ */
+
+ /* Word 3 */
+ bf_set(payload_offset_len, &wqe->fcp_icmd,
+ (nCmd->rsplen + nCmd->cmdlen));
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ phba->sli4_hba.rpi_ids[pnode->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe->generic.wqe_com, pwqeq->sli4_xritag);
+
+ /* Word 8 */
+ wqe->generic.wqe_com.abort_tag = pwqeq->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->generic.wqe_com, pwqeq->iotag);
+
+ /* Word 10 */
+ bf_set(wqe_xchg, &wqe->fcp_iwrite.wqe_com, LPFC_NVME_XCHG);
+
+ /* Words 13 14 15 are for PBDE support */
+
+ /* add the VMID tags as per switch response */
+ if (unlikely(lpfc_ncmd->cur_iocbq.cmd_flag & LPFC_IO_VMID)) {
+ if (phba->pport->vmid_priority_tagging) {
+ bf_set(wqe_ccpe, &wqe->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_ccp, &wqe->fcp_iwrite.wqe_com,
+ lpfc_ncmd->cur_iocbq.vmid_tag.cs_ctl_vmid);
+ } else {
+ bf_set(wqe_appid, &wqe->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1);
+ wqe->words[31] = lpfc_ncmd->cur_iocbq.vmid_tag.app_id;
+ }
+ }
+
+ pwqeq->vport = vport;
+ return 0;
+}
+
+
+/**
+ * lpfc_nvme_prep_io_dma - Issue an NVME-over-FCP IO
+ * @vport: pointer to a host virtual N_Port data structure
+ * @lpfc_ncmd: Pointer to lpfc scsi command
+ *
+ * Driver registers this routine as it io request handler. This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ * 0 - Success
+ * TODO: What are the failure codes.
+ **/
+static int
+lpfc_nvme_prep_io_dma(struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_ncmd)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd;
+ union lpfc_wqe128 *wqe = &lpfc_ncmd->cur_iocbq.wqe;
+ struct sli4_sge *sgl = lpfc_ncmd->dma_sgl;
+ struct sli4_hybrid_sgl *sgl_xtra = NULL;
+ struct scatterlist *data_sg;
+ struct sli4_sge *first_data_sgl;
+ struct ulp_bde64 *bde;
+ dma_addr_t physaddr = 0;
+ uint32_t dma_len = 0;
+ uint32_t dma_offset = 0;
+ int nseg, i, j;
+ bool lsp_just_set = false;
+
+ /* Fix up the command and response DMA stuff. */
+ lpfc_nvme_adj_fcp_sgls(vport, lpfc_ncmd, nCmd);
+
+ /*
+ * There are three possibilities here - use scatter-gather segment, use
+ * the single mapping, or neither.
+ */
+ if (nCmd->sg_cnt) {
+ /*
+ * Jump over the cmd and rsp SGEs. The fix routine
+ * has already adjusted for this.
+ */
+ sgl += 2;
+
+ first_data_sgl = sgl;
+ lpfc_ncmd->seg_cnt = nCmd->sg_cnt;
+ if (lpfc_ncmd->seg_cnt > lpfc_nvme_template.max_sgl_segments) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6058 Too many sg segments from "
+ "NVME Transport. Max %d, "
+ "nvmeIO sg_cnt %d\n",
+ phba->cfg_nvme_seg_cnt + 1,
+ lpfc_ncmd->seg_cnt);
+ lpfc_ncmd->seg_cnt = 0;
+ return 1;
+ }
+
+ /*
+ * The driver established a maximum scatter-gather segment count
+ * during probe that limits the number of sg elements in any
+ * single nvme command. Just run through the seg_cnt and format
+ * the sge's.
+ */
+ nseg = nCmd->sg_cnt;
+ data_sg = nCmd->first_sgl;
+
+ /* for tracking the segment boundaries */
+ j = 2;
+ for (i = 0; i < nseg; i++) {
+ if (data_sg == NULL) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6059 dptr err %d, nseg %d\n",
+ i, nseg);
+ lpfc_ncmd->seg_cnt = 0;
+ return 1;
+ }
+
+ sgl->word2 = 0;
+ if (nseg == 1) {
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_DATA);
+ } else {
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+
+ /* expand the segment */
+ if (!lsp_just_set &&
+ !((j + 1) % phba->border_sge_num) &&
+ ((nseg - 1) != i)) {
+ /* set LSP type */
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_LSP);
+
+ sgl_xtra = lpfc_get_sgl_per_hdwq(
+ phba, lpfc_ncmd);
+
+ if (unlikely(!sgl_xtra)) {
+ lpfc_ncmd->seg_cnt = 0;
+ return 1;
+ }
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(
+ sgl_xtra->dma_phys_sgl));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(
+ sgl_xtra->dma_phys_sgl));
+
+ } else {
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_DATA);
+ }
+ }
+
+ if (!(bf_get(lpfc_sli4_sge_type, sgl) &
+ LPFC_SGE_TYPE_LSP)) {
+ if ((nseg - 1) == i)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+
+ physaddr = sg_dma_address(data_sg);
+ dma_len = sg_dma_len(data_sg);
+ sgl->addr_lo = cpu_to_le32(
+ putPaddrLow(physaddr));
+ sgl->addr_hi = cpu_to_le32(
+ putPaddrHigh(physaddr));
+
+ bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(dma_len);
+
+ dma_offset += dma_len;
+ data_sg = sg_next(data_sg);
+
+ sgl++;
+
+ lsp_just_set = false;
+ } else {
+ sgl->word2 = cpu_to_le32(sgl->word2);
+
+ sgl->sge_len = cpu_to_le32(
+ phba->cfg_sg_dma_buf_size);
+
+ sgl = (struct sli4_sge *)sgl_xtra->dma_sgl;
+ i = i - 1;
+
+ lsp_just_set = true;
+ }
+
+ j++;
+ }
+
+ /* PBDE support for first data SGE only */
+ if (nseg == 1 && phba->cfg_enable_pbde) {
+ /* Words 13-15 */
+ bde = (struct ulp_bde64 *)
+ &wqe->words[13];
+ bde->addrLow = first_data_sgl->addr_lo;
+ bde->addrHigh = first_data_sgl->addr_hi;
+ bde->tus.f.bdeSize =
+ le32_to_cpu(first_data_sgl->sge_len);
+ bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bde->tus.w = cpu_to_le32(bde->tus.w);
+
+ /* Word 11 - set PBDE bit */
+ bf_set(wqe_pbde, &wqe->generic.wqe_com, 1);
+ } else {
+ memset(&wqe->words[13], 0, (sizeof(uint32_t) * 3));
+ /* Word 11 - PBDE bit disabled by default template */
+ }
+
+ } else {
+ lpfc_ncmd->seg_cnt = 0;
+
+ /* For this clause to be valid, the payload_length
+ * and sg_cnt must zero.
+ */
+ if (nCmd->payload_length != 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6063 NVME DMA Prep Err: sg_cnt %d "
+ "payload_length x%x\n",
+ nCmd->sg_cnt, nCmd->payload_length);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * lpfc_nvme_fcp_io_submit - Issue an NVME-over-FCP IO
+ * @pnvme_lport: Pointer to the driver's local port data
+ * @pnvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_queue
+ * @pnvme_fcreq: IO request from nvme fc to driver.
+ *
+ * Driver registers this routine as it io request handler. This
+ * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport.
+ *
+ * Return value :
+ * 0 - Success
+ * TODO: What are the failure codes.
+ **/
+static int
+lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
+ struct nvme_fc_remote_port *pnvme_rport,
+ void *hw_queue_handle,
+ struct nvmefc_fcp_req *pnvme_fcreq)
+{
+ int ret = 0;
+ int expedite = 0;
+ int idx, cpu;
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_fc4_ctrl_stat *cstat;
+ struct lpfc_vport *vport;
+ struct lpfc_hba *phba;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_nvme_rport *rport;
+ struct lpfc_nvme_qhandle *lpfc_queue_info;
+ struct lpfc_nvme_fcpreq_priv *freqpriv;
+ struct nvme_common_command *sqe;
+ uint64_t start = 0;
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ u8 *uuid = NULL;
+ int err;
+ enum dma_data_direction iodir;
+#endif
+
+ /* Validate pointers. LLDD fault handling with transport does
+ * have timing races.
+ */
+ lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+ if (unlikely(!lport)) {
+ ret = -EINVAL;
+ goto out_fail;
+ }
+
+ vport = lport->vport;
+
+ if (unlikely(!hw_queue_handle)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6117 Fail IO, NULL hw_queue_handle\n");
+ atomic_inc(&lport->xmt_fcp_err);
+ ret = -EBUSY;
+ goto out_fail;
+ }
+
+ phba = vport->phba;
+
+ if ((unlikely(test_bit(FC_UNLOADING, &vport->load_flag))) ||
+ test_bit(HBA_IOQ_FLUSH, &phba->hba_flag)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6124 Fail IO, Driver unload\n");
+ atomic_inc(&lport->xmt_fcp_err);
+ ret = -ENODEV;
+ goto out_fail;
+ }
+
+ freqpriv = pnvme_fcreq->private;
+ if (unlikely(!freqpriv)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6158 Fail IO, NULL request data\n");
+ atomic_inc(&lport->xmt_fcp_err);
+ ret = -EINVAL;
+ goto out_fail;
+ }
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (phba->ktime_on)
+ start = ktime_get_ns();
+#endif
+ rport = (struct lpfc_nvme_rport *)pnvme_rport->private;
+ lpfc_queue_info = (struct lpfc_nvme_qhandle *)hw_queue_handle;
+
+ /*
+ * Catch race where our node has transitioned, but the
+ * transport is still transitioning.
+ */
+ ndlp = rport->ndlp;
+ if (!ndlp) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_NVME_IOERR,
+ "6053 Busy IO, ndlp not ready: rport x%px "
+ "ndlp x%px, DID x%06x\n",
+ rport, ndlp, pnvme_rport->port_id);
+ atomic_inc(&lport->xmt_fcp_err);
+ ret = -EBUSY;
+ goto out_fail;
+ }
+
+ /* The remote node has to be a mapped target or it's an error. */
+ if ((ndlp->nlp_type & NLP_NVME_TARGET) &&
+ (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_NVME_IOERR,
+ "6036 Fail IO, DID x%06x not ready for "
+ "IO. State x%x, Type x%x Flg x%x\n",
+ pnvme_rport->port_id,
+ ndlp->nlp_state, ndlp->nlp_type,
+ ndlp->fc4_xpt_flags);
+ atomic_inc(&lport->xmt_fcp_bad_ndlp);
+ ret = -EBUSY;
+ goto out_fail;
+
+ }
+
+ /* Currently only NVME Keep alive commands should be expedited
+ * if the driver runs out of a resource. These should only be
+ * issued on the admin queue, qidx 0
+ */
+ if (!lpfc_queue_info->qidx && !pnvme_fcreq->sg_cnt) {
+ sqe = &((struct nvme_fc_cmd_iu *)
+ pnvme_fcreq->cmdaddr)->sqe.common;
+ if (sqe->opcode == nvme_admin_keep_alive)
+ expedite = 1;
+ }
+
+ /* Check if IO qualifies for CMF */
+ if (phba->cmf_active_mode != LPFC_CFG_OFF &&
+ pnvme_fcreq->io_dir == NVMEFC_FCP_READ &&
+ pnvme_fcreq->payload_length) {
+ ret = lpfc_update_cmf_cmd(phba, pnvme_fcreq->payload_length);
+ if (ret) {
+ ret = -EBUSY;
+ goto out_fail;
+ }
+ /* Get start time for IO latency */
+ start = ktime_get_ns();
+ }
+
+ /* The node is shared with FCP IO, make sure the IO pending count does
+ * not exceed the programmed depth.
+ */
+ if (lpfc_ndlp_check_qdepth(phba, ndlp)) {
+ if ((atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) &&
+ !expedite) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6174 Fail IO, ndlp qdepth exceeded: "
+ "idx %d DID %x pend %d qdepth %d\n",
+ lpfc_queue_info->index, ndlp->nlp_DID,
+ atomic_read(&ndlp->cmd_pending),
+ ndlp->cmd_qdepth);
+ atomic_inc(&lport->xmt_fcp_qdepth);
+ ret = -EBUSY;
+ goto out_fail1;
+ }
+ }
+
+ /* Lookup Hardware Queue index based on fcp_io_sched module parameter */
+ if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) {
+ idx = lpfc_queue_info->index;
+ } else {
+ cpu = raw_smp_processor_id();
+ idx = phba->sli4_hba.cpu_map[cpu].hdwq;
+ }
+
+ lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, idx, expedite);
+ if (lpfc_ncmd == NULL) {
+ atomic_inc(&lport->xmt_fcp_noxri);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6065 Fail IO, driver buffer pool is empty: "
+ "idx %d DID %x\n",
+ lpfc_queue_info->index, ndlp->nlp_DID);
+ ret = -EBUSY;
+ goto out_fail1;
+ }
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (start) {
+ lpfc_ncmd->ts_cmd_start = start;
+ lpfc_ncmd->ts_last_cmd = phba->ktime_last_cmd;
+ } else {
+ lpfc_ncmd->ts_cmd_start = 0;
+ }
+#endif
+ lpfc_ncmd->rx_cmd_start = start;
+
+ /*
+ * Store the data needed by the driver to issue, abort, and complete
+ * an IO.
+ * Do not let the IO hang out forever. There is no midlayer issuing
+ * an abort so inform the FW of the maximum IO pending time.
+ */
+ freqpriv->nvme_buf = lpfc_ncmd;
+ lpfc_ncmd->nvmeCmd = pnvme_fcreq;
+ lpfc_ncmd->ndlp = ndlp;
+ lpfc_ncmd->qidx = lpfc_queue_info->qidx;
+
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ /* check the necessary and sufficient condition to support VMID */
+ if (lpfc_is_vmid_enabled(phba) &&
+ (ndlp->vmid_support ||
+ phba->pport->vmid_priority_tagging ==
+ LPFC_VMID_PRIO_TAG_ALL_TARGETS)) {
+ /* is the I/O generated by a VM, get the associated virtual */
+ /* entity id */
+ uuid = nvme_fc_io_getuuid(pnvme_fcreq);
+
+ if (uuid) {
+ if (pnvme_fcreq->io_dir == NVMEFC_FCP_WRITE)
+ iodir = DMA_TO_DEVICE;
+ else if (pnvme_fcreq->io_dir == NVMEFC_FCP_READ)
+ iodir = DMA_FROM_DEVICE;
+ else
+ iodir = DMA_NONE;
+
+ err = lpfc_vmid_get_appid(vport, uuid, iodir,
+ (union lpfc_vmid_io_tag *)
+ &lpfc_ncmd->cur_iocbq.vmid_tag);
+ if (!err)
+ lpfc_ncmd->cur_iocbq.cmd_flag |= LPFC_IO_VMID;
+ }
+ }
+#endif
+
+ /*
+ * Issue the IO on the WQ indicated by index in the hw_queue_handle.
+ * This identfier was create in our hardware queue create callback
+ * routine. The driver now is dependent on the IO queue steering from
+ * the transport. We are trusting the upper NVME layers know which
+ * index to use and that they have affinitized a CPU to this hardware
+ * queue. A hardware queue maps to a driver MSI-X vector/EQ/CQ/WQ.
+ */
+ lpfc_ncmd->cur_iocbq.hba_wqidx = idx;
+ cstat = &phba->sli4_hba.hdwq[idx].nvme_cstat;
+
+ lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp, cstat);
+ ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd);
+ if (ret) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6175 Fail IO, Prep DMA: "
+ "idx %d DID %x\n",
+ lpfc_queue_info->index, ndlp->nlp_DID);
+ atomic_inc(&lport->xmt_fcp_err);
+ ret = -ENOMEM;
+ goto out_free_nvme_buf;
+ }
+
+ lpfc_nvmeio_data(phba, "NVME FCP XMIT: xri x%x idx %d to %06x\n",
+ lpfc_ncmd->cur_iocbq.sli4_xritag,
+ lpfc_queue_info->index, ndlp->nlp_DID);
+
+ ret = lpfc_sli4_issue_wqe(phba, lpfc_ncmd->hdwq, &lpfc_ncmd->cur_iocbq);
+ if (ret) {
+ atomic_inc(&lport->xmt_fcp_wqerr);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6113 Fail IO, Could not issue WQE err %x "
+ "sid: x%x did: x%x oxid: x%x\n",
+ ret, vport->fc_myDID, ndlp->nlp_DID,
+ lpfc_ncmd->cur_iocbq.sli4_xritag);
+ goto out_free_nvme_buf;
+ }
+
+ if (phba->cfg_xri_rebalancing)
+ lpfc_keep_pvt_pool_above_lowwm(phba, lpfc_ncmd->hdwq_no);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (lpfc_ncmd->ts_cmd_start)
+ lpfc_ncmd->ts_cmd_wqput = ktime_get_ns();
+
+ if (phba->hdwqstat_on & LPFC_CHECK_NVME_IO) {
+ cpu = raw_smp_processor_id();
+ this_cpu_inc(phba->sli4_hba.c_stat->xmt_io);
+ lpfc_ncmd->cpu = cpu;
+ if (idx != cpu)
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_NVME_IOERR,
+ "6702 CPU Check cmd: "
+ "cpu %d wq %d\n",
+ lpfc_ncmd->cpu,
+ lpfc_queue_info->index);
+ }
+#endif
+ return 0;
+
+ out_free_nvme_buf:
+ if (lpfc_ncmd->nvmeCmd->sg_cnt) {
+ if (lpfc_ncmd->nvmeCmd->io_dir == NVMEFC_FCP_WRITE)
+ cstat->output_requests--;
+ else
+ cstat->input_requests--;
+ } else
+ cstat->control_requests--;
+ lpfc_release_nvme_buf(phba, lpfc_ncmd);
+ out_fail1:
+ lpfc_update_cmf_cmpl(phba, LPFC_CGN_NOT_SENT,
+ pnvme_fcreq->payload_length, NULL);
+ out_fail:
+ return ret;
+}
+
+/**
+ * lpfc_nvme_abort_fcreq_cmpl - Complete an NVME FCP abort request.
+ * @phba: Pointer to HBA context object
+ * @cmdiocb: Pointer to command iocb object.
+ * @rspiocb: Pointer to response iocb object.
+ *
+ * This is the callback function for any NVME FCP IO that was aborted.
+ *
+ * Return value:
+ * None
+ **/
+void
+lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_wcqe_complete *abts_cmpl = &rspiocb->wcqe_cmpl;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
+ "6145 ABORT_XRI_CN completing on rpi x%x "
+ "original iotag x%x, abort cmd iotag x%x "
+ "req_tag x%x, status x%x, hwstatus x%x\n",
+ bf_get(wqe_ctxt_tag, &cmdiocb->wqe.generic.wqe_com),
+ get_job_abtsiotag(phba, cmdiocb), cmdiocb->iotag,
+ bf_get(lpfc_wcqe_c_request_tag, abts_cmpl),
+ bf_get(lpfc_wcqe_c_status, abts_cmpl),
+ bf_get(lpfc_wcqe_c_hw_status, abts_cmpl));
+ lpfc_sli_release_iocbq(phba, cmdiocb);
+}
+
+/**
+ * lpfc_nvme_fcp_abort - Issue an NVME-over-FCP ABTS
+ * @pnvme_lport: Pointer to the driver's local port data
+ * @pnvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq
+ * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_queue
+ * @pnvme_fcreq: IO request from nvme fc to driver.
+ *
+ * Driver registers this routine as its nvme request io abort handler. This
+ * routine issues an fcp Abort WQE with data from the @lpfc_nvme_fcpreq
+ * data structure to the rport indicated in @lpfc_nvme_rport. This routine
+ * is executed asynchronously - one the target is validated as "MAPPED" and
+ * ready for IO, the driver issues the abort request and returns.
+ *
+ * Return value:
+ * None
+ **/
+static void
+lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
+ struct nvme_fc_remote_port *pnvme_rport,
+ void *hw_queue_handle,
+ struct nvmefc_fcp_req *pnvme_fcreq)
+{
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_vport *vport;
+ struct lpfc_hba *phba;
+ struct lpfc_io_buf *lpfc_nbuf;
+ struct lpfc_iocbq *nvmereq_wqe;
+ struct lpfc_nvme_fcpreq_priv *freqpriv;
+ unsigned long flags;
+ int ret_val;
+
+ /* Validate pointers. LLDD fault handling with transport does
+ * have timing races.
+ */
+ lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+ if (unlikely(!lport))
+ return;
+
+ vport = lport->vport;
+
+ if (unlikely(!hw_queue_handle)) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS,
+ "6129 Fail Abort, HW Queue Handle NULL.\n");
+ return;
+ }
+
+ phba = vport->phba;
+ freqpriv = pnvme_fcreq->private;
+
+ if (unlikely(!freqpriv))
+ return;
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
+ return;
+
+ /* Announce entry to new IO submit field. */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS,
+ "6002 Abort Request to rport DID x%06x "
+ "for nvme_fc_req x%px\n",
+ pnvme_rport->port_id,
+ pnvme_fcreq);
+
+ lpfc_nbuf = freqpriv->nvme_buf;
+ if (!lpfc_nbuf) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6140 NVME IO req has no matching lpfc nvme "
+ "io buffer. Skipping abort req.\n");
+ return;
+ } else if (!lpfc_nbuf->nvmeCmd) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6141 lpfc NVME IO req has no nvme_fcreq "
+ "io buffer. Skipping abort req.\n");
+ return;
+ }
+
+ /* driver queued commands are in process of being flushed */
+ if (test_bit(HBA_IOQ_FLUSH, &phba->hba_flag)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6139 Driver in reset cleanup - flushing "
+ "NVME Req now. hba_flag x%lx\n",
+ phba->hba_flag);
+ return;
+ }
+
+ /* Guard against IO completion being called at same time */
+ spin_lock_irqsave(&lpfc_nbuf->buf_lock, flags);
+ spin_lock(&phba->hbalock);
+
+ nvmereq_wqe = &lpfc_nbuf->cur_iocbq;
+
+ /*
+ * The lpfc_nbuf and the mapped nvme_fcreq in the driver's
+ * state must match the nvme_fcreq passed by the nvme
+ * transport. If they don't match, it is likely the driver
+ * has already completed the NVME IO and the nvme transport
+ * has not seen it yet.
+ */
+ if (lpfc_nbuf->nvmeCmd != pnvme_fcreq) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6143 NVME req mismatch: "
+ "lpfc_nbuf x%px nvmeCmd x%px, "
+ "pnvme_fcreq x%px. Skipping Abort xri x%x\n",
+ lpfc_nbuf, lpfc_nbuf->nvmeCmd,
+ pnvme_fcreq, nvmereq_wqe->sli4_xritag);
+ goto out_unlock;
+ }
+
+ /* Don't abort IOs no longer on the pending queue. */
+ if (!(nvmereq_wqe->cmd_flag & LPFC_IO_ON_TXCMPLQ)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6142 NVME IO req x%px not queued - skipping "
+ "abort req xri x%x\n",
+ pnvme_fcreq, nvmereq_wqe->sli4_xritag);
+ goto out_unlock;
+ }
+
+ atomic_inc(&lport->xmt_fcp_abort);
+ lpfc_nvmeio_data(phba, "NVME FCP ABORT: xri x%x idx %d to %06x\n",
+ nvmereq_wqe->sli4_xritag,
+ nvmereq_wqe->hba_wqidx, pnvme_rport->port_id);
+
+ /* Outstanding abort is in progress */
+ if (nvmereq_wqe->cmd_flag & LPFC_DRIVER_ABORTED) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6144 Outstanding NVME I/O Abort Request "
+ "still pending on nvme_fcreq x%px, "
+ "lpfc_ncmd x%px xri x%x\n",
+ pnvme_fcreq, lpfc_nbuf,
+ nvmereq_wqe->sli4_xritag);
+ goto out_unlock;
+ }
+
+ ret_val = lpfc_sli4_issue_abort_iotag(phba, nvmereq_wqe,
+ lpfc_nvme_abort_fcreq_cmpl);
+
+ spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&lpfc_nbuf->buf_lock, flags);
+
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
+ if (ret_val != WQE_SUCCESS) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6137 Failed abts issue_wqe with status x%x "
+ "for nvme_fcreq x%px.\n",
+ ret_val, pnvme_fcreq);
+ return;
+ }
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS,
+ "6138 Transport Abort NVME Request Issued for "
+ "ox_id x%x\n",
+ nvmereq_wqe->sli4_xritag);
+ return;
+
+out_unlock:
+ spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&lpfc_nbuf->buf_lock, flags);
+ return;
+}
+
+/* Declare and initialization an instance of the FC NVME template. */
+static struct nvme_fc_port_template lpfc_nvme_template = {
+ /* initiator-based functions */
+ .localport_delete = lpfc_nvme_localport_delete,
+ .remoteport_delete = lpfc_nvme_remoteport_delete,
+ .create_queue = lpfc_nvme_create_queue,
+ .delete_queue = lpfc_nvme_delete_queue,
+ .ls_req = lpfc_nvme_ls_req,
+ .fcp_io = lpfc_nvme_fcp_io_submit,
+ .ls_abort = lpfc_nvme_ls_abort,
+ .fcp_abort = lpfc_nvme_fcp_abort,
+ .xmt_ls_rsp = lpfc_nvme_xmt_ls_rsp,
+
+ .max_hw_queues = 1,
+ .max_sgl_segments = LPFC_NVME_DEFAULT_SEGS,
+ .max_dif_sgl_segments = LPFC_NVME_DEFAULT_SEGS,
+ .dma_boundary = 0xFFFFFFFF,
+
+ /* Sizes of additional private data for data structures.
+ * No use for the last two sizes at this time.
+ */
+ .local_priv_sz = sizeof(struct lpfc_nvme_lport),
+ .remote_priv_sz = sizeof(struct lpfc_nvme_rport),
+ .lsrqst_priv_sz = 0,
+ .fcprqst_priv_sz = sizeof(struct lpfc_nvme_fcpreq_priv),
+};
+
+/*
+ * lpfc_get_nvme_buf - Get a nvme buffer from io_buf_list of the HBA
+ *
+ * This routine removes a nvme buffer from head of @hdwq io_buf_list
+ * and returns to caller.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_nvme_buf - Success
+ **/
+static struct lpfc_io_buf *
+lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ int idx, int expedite)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct sli4_sge *sgl;
+ struct lpfc_iocbq *pwqeq;
+ union lpfc_wqe128 *wqe;
+
+ lpfc_ncmd = lpfc_get_io_buf(phba, NULL, idx, expedite);
+
+ if (lpfc_ncmd) {
+ pwqeq = &(lpfc_ncmd->cur_iocbq);
+ wqe = &pwqeq->wqe;
+
+ /* Setup key fields in buffer that may have been changed
+ * if other protocols used this buffer.
+ */
+ pwqeq->cmd_flag = LPFC_IO_NVME;
+ pwqeq->cmd_cmpl = lpfc_nvme_io_cmd_cmpl;
+ lpfc_ncmd->start_time = jiffies;
+ lpfc_ncmd->flags = 0;
+
+ /* Rsp SGE will be filled in when we rcv an IO
+ * from the NVME Layer to be sent.
+ * The cmd is going to be embedded so we need a SKIP SGE.
+ */
+ sgl = lpfc_ncmd->dma_sgl;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP);
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ /* Fill in word 3 / sgl_len during cmd submission */
+
+ /* Initialize 64 bytes only */
+ memset(wqe, 0, sizeof(union lpfc_wqe));
+
+ if (lpfc_ndlp_check_qdepth(phba, ndlp)) {
+ atomic_inc(&ndlp->cmd_pending);
+ lpfc_ncmd->flags |= LPFC_SBUF_BUMP_QDEPTH;
+ }
+
+ } else {
+ qp = &phba->sli4_hba.hdwq[idx];
+ qp->empty_io_bufs++;
+ }
+
+ return lpfc_ncmd;
+}
+
+/**
+ * lpfc_release_nvme_buf: Return a nvme buffer back to hba nvme buf list.
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_ncmd: The nvme buffer which is being released.
+ *
+ * This routine releases @lpfc_ncmd nvme buffer by adding it to tail of @phba
+ * lpfc_io_buf_list list. For SLI4 XRI's are tied to the nvme buffer
+ * and cannot be reused for at least RA_TOV amount of time if it was
+ * aborted.
+ **/
+static void
+lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ unsigned long iflag = 0;
+
+ if ((lpfc_ncmd->flags & LPFC_SBUF_BUMP_QDEPTH) && lpfc_ncmd->ndlp)
+ atomic_dec(&lpfc_ncmd->ndlp->cmd_pending);
+
+ lpfc_ncmd->ndlp = NULL;
+ lpfc_ncmd->flags &= ~LPFC_SBUF_BUMP_QDEPTH;
+
+ qp = lpfc_ncmd->hdwq;
+ if (unlikely(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6310 XB release deferred for "
+ "ox_id x%x on reqtag x%x\n",
+ lpfc_ncmd->cur_iocbq.sli4_xritag,
+ lpfc_ncmd->cur_iocbq.iotag);
+
+ spin_lock_irqsave(&qp->abts_io_buf_list_lock, iflag);
+ list_add_tail(&lpfc_ncmd->list,
+ &qp->lpfc_abts_io_buf_list);
+ qp->abts_nvme_io_bufs++;
+ spin_unlock_irqrestore(&qp->abts_io_buf_list_lock, iflag);
+ } else
+ lpfc_release_io_buf(phba, (struct lpfc_io_buf *)lpfc_ncmd, qp);
+}
+
+/**
+ * lpfc_nvme_create_localport - Create/Bind an nvme localport instance.
+ * @vport: the lpfc_vport instance requesting a localport.
+ *
+ * This routine is invoked to create an nvme localport instance to bind
+ * to the nvme_fc_transport. It is called once during driver load
+ * like lpfc_create_shost after all other services are initialized.
+ * It requires a vport, vpi, and wwns at call time. Other localport
+ * parameters are modified as the driver's FCID and the Fabric WWN
+ * are established.
+ *
+ * Return codes
+ * 0 - successful
+ * -ENOMEM - no heap memory available
+ * other values - from nvme registration upcall
+ **/
+int
+lpfc_nvme_create_localport(struct lpfc_vport *vport)
+{
+ int ret = 0;
+ struct lpfc_hba *phba = vport->phba;
+ struct nvme_fc_port_info nfcp_info;
+ struct nvme_fc_local_port *localport;
+ struct lpfc_nvme_lport *lport;
+
+ /* Initialize this localport instance. The vport wwn usage ensures
+ * that NPIV is accounted for.
+ */
+ memset(&nfcp_info, 0, sizeof(struct nvme_fc_port_info));
+ nfcp_info.port_role = FC_PORT_ROLE_NVME_INITIATOR;
+ nfcp_info.node_name = wwn_to_u64(vport->fc_nodename.u.wwn);
+ nfcp_info.port_name = wwn_to_u64(vport->fc_portname.u.wwn);
+
+ /* We need to tell the transport layer + 1 because it takes page
+ * alignment into account. When space for the SGL is allocated we
+ * allocate + 3, one for cmd, one for rsp and one for this alignment
+ */
+ lpfc_nvme_template.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1;
+
+ /* Advertise how many hw queues we support based on cfg_hdw_queue,
+ * which will not exceed cpu count.
+ */
+ lpfc_nvme_template.max_hw_queues = phba->cfg_hdw_queue;
+
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return ret;
+
+ /* localport is allocated from the stack, but the registration
+ * call allocates heap memory as well as the private area.
+ */
+
+ ret = nvme_fc_register_localport(&nfcp_info, &lpfc_nvme_template,
+ &vport->phba->pcidev->dev, &localport);
+ if (!ret) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME | LOG_NVME_DISC,
+ "6005 Successfully registered local "
+ "NVME port num %d, localP x%px, private "
+ "x%px, sg_seg %d\n",
+ localport->port_num, localport,
+ localport->private,
+ lpfc_nvme_template.max_sgl_segments);
+
+ /* Private is our lport size declared in the template. */
+ lport = (struct lpfc_nvme_lport *)localport->private;
+ vport->localport = localport;
+ lport->vport = vport;
+ vport->nvmei_support = 1;
+
+ atomic_set(&lport->xmt_fcp_noxri, 0);
+ atomic_set(&lport->xmt_fcp_bad_ndlp, 0);
+ atomic_set(&lport->xmt_fcp_qdepth, 0);
+ atomic_set(&lport->xmt_fcp_err, 0);
+ atomic_set(&lport->xmt_fcp_wqerr, 0);
+ atomic_set(&lport->xmt_fcp_abort, 0);
+ atomic_set(&lport->xmt_ls_abort, 0);
+ atomic_set(&lport->xmt_ls_err, 0);
+ atomic_set(&lport->cmpl_fcp_xb, 0);
+ atomic_set(&lport->cmpl_fcp_err, 0);
+ atomic_set(&lport->cmpl_ls_xb, 0);
+ atomic_set(&lport->cmpl_ls_err, 0);
+
+ atomic_set(&lport->fc4NvmeLsRequests, 0);
+ atomic_set(&lport->fc4NvmeLsCmpls, 0);
+ }
+
+ return ret;
+}
+
+#if (IS_ENABLED(CONFIG_NVME_FC))
+/* lpfc_nvme_lport_unreg_wait - Wait for the host to complete an lport unreg.
+ *
+ * The driver has to wait for the host nvme transport to callback
+ * indicating the localport has successfully unregistered all
+ * resources. Since this is an uninterruptible wait, loop every ten
+ * seconds and print a message indicating no progress.
+ *
+ * An uninterruptible wait is used because of the risk of transport-to-
+ * driver state mismatch.
+ */
+static void
+lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport,
+ struct lpfc_nvme_lport *lport,
+ struct completion *lport_unreg_cmp)
+{
+ u32 wait_tmo;
+ int ret, i, pending = 0;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_sli4_hdw_queue *qp;
+ int abts_scsi, abts_nvme;
+ u16 nvmels_cnt;
+
+ /* Host transport has to clean up and confirm requiring an indefinite
+ * wait. Print a message if a 10 second wait expires and renew the
+ * wait. This is unexpected.
+ */
+ wait_tmo = secs_to_jiffies(LPFC_NVME_WAIT_TMO);
+ while (true) {
+ ret = wait_for_completion_timeout(lport_unreg_cmp, wait_tmo);
+ if (unlikely(!ret)) {
+ pending = 0;
+ abts_scsi = 0;
+ abts_nvme = 0;
+ nvmels_cnt = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ qp = &phba->sli4_hba.hdwq[i];
+ if (!vport->localport || !qp || !qp->io_wq)
+ return;
+
+ pring = qp->io_wq->pring;
+ if (!pring)
+ continue;
+ pending += pring->txcmplq_cnt;
+ abts_scsi += qp->abts_scsi_io_bufs;
+ abts_nvme += qp->abts_nvme_io_bufs;
+ }
+ if (phba->sli4_hba.nvmels_wq) {
+ pring = phba->sli4_hba.nvmels_wq->pring;
+ if (pring)
+ nvmels_cnt = pring->txcmplq_cnt;
+ }
+ if (!vport->localport ||
+ test_bit(HBA_PCI_ERR, &vport->phba->bit_flags) ||
+ phba->link_state == LPFC_HBA_ERROR ||
+ test_bit(FC_UNLOADING, &vport->load_flag))
+ return;
+
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6176 Lport x%px Localport x%px wait "
+ "timed out. Pending %d [%d:%d:%d]. "
+ "Renewing.\n",
+ lport, vport->localport, pending,
+ abts_scsi, abts_nvme, nvmels_cnt);
+ continue;
+ }
+ break;
+ }
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR,
+ "6177 Lport x%px Localport x%px Complete Success\n",
+ lport, vport->localport);
+}
+#endif
+
+/**
+ * lpfc_nvme_destroy_localport - Destroy lpfc_nvme bound to nvme transport.
+ * @vport: pointer to a host virtual N_Port data structure
+ *
+ * This routine is invoked to destroy all lports bound to the phba.
+ * The lport memory was allocated by the nvme fc transport and is
+ * released there. This routine ensures all rports bound to the
+ * lport have been disconnected.
+ *
+ **/
+void
+lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ struct nvme_fc_local_port *localport;
+ struct lpfc_nvme_lport *lport;
+ int ret;
+ DECLARE_COMPLETION_ONSTACK(lport_unreg_cmp);
+
+ if (vport->nvmei_support == 0)
+ return;
+
+ localport = vport->localport;
+ if (!localport)
+ return;
+ lport = (struct lpfc_nvme_lport *)localport->private;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+ "6011 Destroying NVME localport x%px\n",
+ localport);
+
+ /* lport's rport list is clear. Unregister
+ * lport and release resources.
+ */
+ lport->lport_unreg_cmp = &lport_unreg_cmp;
+ ret = nvme_fc_unregister_localport(localport);
+
+ /* Wait for completion. This either blocks
+ * indefinitely or succeeds
+ */
+ lpfc_nvme_lport_unreg_wait(vport, lport, &lport_unreg_cmp);
+ vport->localport = NULL;
+
+ /* Regardless of the unregister upcall response, clear
+ * nvmei_support. All rports are unregistered and the
+ * driver will clean up.
+ */
+ vport->nvmei_support = 0;
+ if (ret == 0) {
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_NVME_DISC,
+ "6009 Unregistered lport Success\n");
+ } else {
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_NVME_DISC,
+ "6010 Unregistered lport "
+ "Failed, status x%x\n",
+ ret);
+ }
+#endif
+}
+
+void
+lpfc_nvme_update_localport(struct lpfc_vport *vport)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ struct nvme_fc_local_port *localport;
+ struct lpfc_nvme_lport *lport;
+
+ localport = vport->localport;
+ if (!localport) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_NVME,
+ "6710 Update NVME fail. No localport\n");
+ return;
+ }
+ lport = (struct lpfc_nvme_lport *)localport->private;
+ if (!lport) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_NVME,
+ "6171 Update NVME fail. localP x%px, No lport\n",
+ localport);
+ return;
+ }
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+ "6012 Update NVME lport x%px did x%x\n",
+ localport, vport->fc_myDID);
+
+ localport->port_id = vport->fc_myDID;
+ if (localport->port_id == 0)
+ localport->port_role = FC_PORT_ROLE_NVME_DISCOVERY;
+ else
+ localport->port_role = FC_PORT_ROLE_NVME_INITIATOR;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6030 bound lport x%px to DID x%06x\n",
+ lport, localport->port_id);
+#endif
+}
+
+int
+lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ int ret = 0;
+ struct nvme_fc_local_port *localport;
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_nvme_rport *rport;
+ struct lpfc_nvme_rport *oldrport;
+ struct nvme_fc_remote_port *remote_port;
+ struct nvme_fc_port_info rpinfo;
+ struct lpfc_nodelist *prev_ndlp = NULL;
+ struct fc_rport *srport = ndlp->rport;
+
+ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NVME_DISC,
+ "6006 Register NVME PORT. DID x%06x nlptype x%x\n",
+ ndlp->nlp_DID, ndlp->nlp_type);
+
+ localport = vport->localport;
+ if (!localport)
+ return 0;
+
+ lport = (struct lpfc_nvme_lport *)localport->private;
+
+ /* NVME rports are not preserved across devloss.
+ * Just register this instance. Note, rpinfo->dev_loss_tmo
+ * is left 0 to indicate accept transport defaults. The
+ * driver communicates port role capabilities consistent
+ * with the PRLI response data.
+ */
+ memset(&rpinfo, 0, sizeof(struct nvme_fc_port_info));
+ rpinfo.port_id = ndlp->nlp_DID;
+ if (ndlp->nlp_type & NLP_NVME_TARGET)
+ rpinfo.port_role |= FC_PORT_ROLE_NVME_TARGET;
+ if (ndlp->nlp_type & NLP_NVME_INITIATOR)
+ rpinfo.port_role |= FC_PORT_ROLE_NVME_INITIATOR;
+
+ if (ndlp->nlp_type & NLP_NVME_DISCOVERY)
+ rpinfo.port_role |= FC_PORT_ROLE_NVME_DISCOVERY;
+
+ rpinfo.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn);
+ rpinfo.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn);
+ if (srport)
+ rpinfo.dev_loss_tmo = srport->dev_loss_tmo;
+ else
+ rpinfo.dev_loss_tmo = vport->cfg_devloss_tmo;
+
+ spin_lock_irq(&ndlp->lock);
+
+ /* If an oldrport exists, so does the ndlp reference. If not
+ * a new reference is needed because either the node has never
+ * been registered or it's been unregistered and getting deleted.
+ */
+ oldrport = lpfc_ndlp_get_nrport(ndlp);
+ if (oldrport) {
+ prev_ndlp = oldrport->ndlp;
+ spin_unlock_irq(&ndlp->lock);
+ } else {
+ spin_unlock_irq(&ndlp->lock);
+ if (!lpfc_nlp_get(ndlp)) {
+ dev_warn(&vport->phba->pcidev->dev,
+ "Warning - No node ref - exit register\n");
+ return 0;
+ }
+ }
+
+ ret = nvme_fc_register_remoteport(localport, &rpinfo, &remote_port);
+ if (!ret) {
+ /* If the ndlp already has an nrport, this is just
+ * a resume of the existing rport. Else this is a
+ * new rport.
+ */
+ /* Guard against an unregister/reregister
+ * race that leaves the WAIT flag set.
+ */
+ spin_lock_irq(&ndlp->lock);
+ ndlp->fc4_xpt_flags &= ~NVME_XPT_UNREG_WAIT;
+ ndlp->fc4_xpt_flags |= NVME_XPT_REGD;
+ spin_unlock_irq(&ndlp->lock);
+ rport = remote_port->private;
+ if (oldrport) {
+
+ /* Sever the ndlp<->rport association
+ * before dropping the ndlp ref from
+ * register.
+ */
+ spin_lock_irq(&ndlp->lock);
+ ndlp->nrport = NULL;
+ ndlp->fc4_xpt_flags &= ~NVME_XPT_UNREG_WAIT;
+ spin_unlock_irq(&ndlp->lock);
+ rport->ndlp = NULL;
+ rport->remoteport = NULL;
+
+ /* Reference only removed if previous NDLP is no longer
+ * active. It might be just a swap and removing the
+ * reference would cause a premature cleanup.
+ */
+ if (prev_ndlp && prev_ndlp != ndlp) {
+ if (!prev_ndlp->nrport)
+ lpfc_nlp_put(prev_ndlp);
+ }
+ }
+
+ /* Clean bind the rport to the ndlp. */
+ rport->remoteport = remote_port;
+ rport->lport = lport;
+ rport->ndlp = ndlp;
+ spin_lock_irq(&ndlp->lock);
+ ndlp->nrport = rport;
+ spin_unlock_irq(&ndlp->lock);
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_NVME_DISC | LOG_NODE,
+ "6022 Bind lport x%px to remoteport x%px "
+ "rport x%px WWNN 0x%llx, "
+ "Rport WWPN 0x%llx DID "
+ "x%06x Role x%x, ndlp %p prev_ndlp x%px\n",
+ lport, remote_port, rport,
+ rpinfo.node_name, rpinfo.port_name,
+ rpinfo.port_id, rpinfo.port_role,
+ ndlp, prev_ndlp);
+ } else {
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "6031 RemotePort Registration failed "
+ "err: %d, DID x%06x ref %u\n",
+ ret, ndlp->nlp_DID, kref_read(&ndlp->kref));
+
+ /* Only release reference if one was taken for this request */
+ if (!oldrport)
+ lpfc_nlp_put(ndlp);
+ }
+
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * lpfc_nvme_rescan_port - Check to see if we should rescan this remoteport
+ *
+ * If the ndlp represents an NVME Target, that we are logged into,
+ * ping the NVME FC Transport layer to initiate a device rescan
+ * on this remote NPort.
+ */
+void
+lpfc_nvme_rescan_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ struct lpfc_nvme_rport *nrport;
+ struct nvme_fc_remote_port *remoteport = NULL;
+
+ spin_lock_irq(&ndlp->lock);
+ nrport = lpfc_ndlp_get_nrport(ndlp);
+ if (nrport)
+ remoteport = nrport->remoteport;
+ spin_unlock_irq(&ndlp->lock);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6170 Rescan NPort DID x%06x type x%x "
+ "state x%x nrport x%px remoteport x%px\n",
+ ndlp->nlp_DID, ndlp->nlp_type, ndlp->nlp_state,
+ nrport, remoteport);
+
+ if (!nrport || !remoteport)
+ goto rescan_exit;
+
+ /* Rescan an NVME target in MAPPED state with DISCOVERY role set */
+ if (remoteport->port_role & FC_PORT_ROLE_NVME_DISCOVERY &&
+ ndlp->nlp_state == NLP_STE_MAPPED_NODE) {
+ nvme_fc_rescan_remoteport(remoteport);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6172 NVME rescanned DID x%06x "
+ "port_state x%x\n",
+ ndlp->nlp_DID, remoteport->port_state);
+ }
+ return;
+ rescan_exit:
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6169 Skip NVME Rport Rescan, NVME remoteport "
+ "unregistered\n");
+#endif
+}
+
+/* lpfc_nvme_unregister_port - unbind the DID and port_role from this rport.
+ *
+ * There is no notion of Devloss or rport recovery from the current
+ * nvme_transport perspective. Loss of an rport just means IO cannot
+ * be sent and recovery is completely up to the initator.
+ * For now, the driver just unbinds the DID and port_role so that
+ * no further IO can be issued.
+ */
+void
+lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ int ret;
+ struct nvme_fc_local_port *localport;
+ struct lpfc_nvme_lport *lport;
+ struct lpfc_nvme_rport *rport;
+ struct nvme_fc_remote_port *remoteport = NULL;
+
+ localport = vport->localport;
+
+ /* This is fundamental error. The localport is always
+ * available until driver unload. Just exit.
+ */
+ if (!localport)
+ return;
+
+ lport = (struct lpfc_nvme_lport *)localport->private;
+ if (!lport)
+ goto input_err;
+
+ spin_lock_irq(&ndlp->lock);
+ rport = lpfc_ndlp_get_nrport(ndlp);
+ if (rport)
+ remoteport = rport->remoteport;
+ spin_unlock_irq(&ndlp->lock);
+ if (!remoteport)
+ goto input_err;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ "6033 Unreg nvme remoteport x%px, portname x%llx, "
+ "port_id x%06x, portstate x%x port type x%x "
+ "refcnt %d\n",
+ remoteport, remoteport->port_name,
+ remoteport->port_id, remoteport->port_state,
+ ndlp->nlp_type, kref_read(&ndlp->kref));
+
+ /* Sanity check ndlp type. Only call for NVME ports. Don't
+ * clear any rport state until the transport calls back.
+ */
+
+ if ((ndlp->nlp_type & NLP_NVME_TARGET) ||
+ (remoteport->port_role & FC_PORT_ROLE_NVME_TARGET)) {
+ /* No concern about the role change on the nvme remoteport.
+ * The transport will update it.
+ */
+ spin_lock_irq(&ndlp->lock);
+ ndlp->fc4_xpt_flags |= NVME_XPT_UNREG_WAIT;
+ spin_unlock_irq(&ndlp->lock);
+
+ /* Don't let the host nvme transport keep sending keep-alives
+ * on this remoteport. Vport is unloading, no recovery. The
+ * return values is ignored. The upcall is a courtesy to the
+ * transport.
+ */
+ if (test_bit(FC_UNLOADING, &vport->load_flag) ||
+ unlikely(vport->phba->link_state == LPFC_HBA_ERROR))
+ (void)nvme_fc_set_remoteport_devloss(remoteport, 0);
+
+ ret = nvme_fc_unregister_remoteport(remoteport);
+
+ /* The driver no longer knows if the nrport memory is valid.
+ * because the controller teardown process has begun and
+ * is asynchronous. Break the binding in the ndlp. Also
+ * remove the register ndlp reference to setup node release.
+ */
+ ndlp->nrport = NULL;
+ lpfc_nlp_put(ndlp);
+ if (ret != 0) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6167 NVME unregister failed %d "
+ "port_state x%x\n",
+ ret, remoteport->port_state);
+
+ if (test_bit(FC_UNLOADING, &vport->load_flag)) {
+ /* Only 1 thread can drop the initial node
+ * reference. Check if another thread has set
+ * NLP_DROPPED.
+ */
+ if (!test_and_set_bit(NLP_DROPPED,
+ &ndlp->nlp_flag)) {
+ lpfc_nlp_put(ndlp);
+ return;
+ }
+ }
+ }
+ }
+ return;
+
+ input_err:
+#endif
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "6168 State error: lport x%px, rport x%px FCID x%06x\n",
+ vport->localport, ndlp->rport, ndlp->nlp_DID);
+}
+
+/**
+ * lpfc_sli4_nvme_pci_offline_aborted - Fast-path process of NVME xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @lpfc_ncmd: The nvme job structure for the request being aborted.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 fast-path
+ * NVME aborted xri. Aborted NVME IO commands are completed to the transport
+ * here.
+ **/
+void
+lpfc_sli4_nvme_pci_offline_aborted(struct lpfc_hba *phba,
+ struct lpfc_io_buf *lpfc_ncmd)
+{
+ struct nvmefc_fcp_req *nvme_cmd = NULL;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6533 %s nvme_cmd %p tag x%x abort complete and "
+ "xri released\n", __func__,
+ lpfc_ncmd->nvmeCmd,
+ lpfc_ncmd->cur_iocbq.iotag);
+
+ /* Aborted NVME commands are required to not complete
+ * before the abort exchange command fully completes.
+ * Once completed, it is available via the put list.
+ */
+ if (lpfc_ncmd->nvmeCmd) {
+ nvme_cmd = lpfc_ncmd->nvmeCmd;
+ nvme_cmd->transferred_length = 0;
+ nvme_cmd->rcv_rsplen = 0;
+ nvme_cmd->status = NVME_SC_INTERNAL;
+ nvme_cmd->done(nvme_cmd);
+ lpfc_ncmd->nvmeCmd = NULL;
+ }
+ lpfc_release_nvme_buf(phba, lpfc_ncmd);
+}
+
+/**
+ * lpfc_sli4_nvme_xri_aborted - Fast-path process of NVME xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @axri: pointer to the fcp xri abort wcqe structure.
+ * @lpfc_ncmd: The nvme job structure for the request being aborted.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 fast-path
+ * NVME aborted xri. Aborted NVME IO commands are completed to the transport
+ * here.
+ **/
+void
+lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri,
+ struct lpfc_io_buf *lpfc_ncmd)
+{
+ uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
+ struct nvmefc_fcp_req *nvme_cmd = NULL;
+ struct lpfc_nodelist *ndlp = lpfc_ncmd->ndlp;
+
+
+ if (ndlp)
+ lpfc_sli4_abts_err_handler(phba, ndlp, axri);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6311 nvme_cmd %p xri x%x tag x%x abort complete and "
+ "xri released\n",
+ lpfc_ncmd->nvmeCmd, xri,
+ lpfc_ncmd->cur_iocbq.iotag);
+
+ /* Aborted NVME commands are required to not complete
+ * before the abort exchange command fully completes.
+ * Once completed, it is available via the put list.
+ */
+ if (lpfc_ncmd->nvmeCmd) {
+ nvme_cmd = lpfc_ncmd->nvmeCmd;
+ nvme_cmd->done(nvme_cmd);
+ lpfc_ncmd->nvmeCmd = NULL;
+ }
+ lpfc_release_nvme_buf(phba, lpfc_ncmd);
+}
+
+/**
+ * lpfc_nvme_wait_for_io_drain - Wait for all NVME wqes to complete
+ * @phba: Pointer to HBA context object.
+ *
+ * This function flushes all wqes in the nvme rings and frees all resources
+ * in the txcmplq. This function does not issue abort wqes for the IO
+ * commands in txcmplq, they will just be returned with
+ * IOERR_SLI_DOWN. This function is invoked with EEH when device's PCI
+ * slot has been permanently disabled.
+ **/
+void
+lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba)
+{
+ struct lpfc_sli_ring *pring;
+ u32 i, wait_cnt = 0;
+
+ if (phba->sli_rev < LPFC_SLI_REV4 || !phba->sli4_hba.hdwq)
+ return;
+
+ /* Cycle through all IO rings and make sure all outstanding
+ * WQEs have been removed from the txcmplqs.
+ */
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ if (!phba->sli4_hba.hdwq[i].io_wq)
+ continue;
+ pring = phba->sli4_hba.hdwq[i].io_wq->pring;
+
+ if (!pring)
+ continue;
+
+ /* Retrieve everything on the txcmplq */
+ while (!list_empty(&pring->txcmplq)) {
+ msleep(LPFC_XRI_EXCH_BUSY_WAIT_T1);
+ wait_cnt++;
+
+ /* The sleep is 10mS. Every ten seconds,
+ * dump a message. Something is wrong.
+ */
+ if ((wait_cnt % 1000) == 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6178 NVME IO not empty, "
+ "cnt %d\n", wait_cnt);
+ }
+ }
+ }
+
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
+}
+
+void
+lpfc_nvme_cancel_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
+ uint32_t stat, uint32_t param)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct nvmefc_fcp_req *nCmd;
+ struct lpfc_wcqe_complete wcqe;
+ struct lpfc_wcqe_complete *wcqep = &wcqe;
+
+ lpfc_ncmd = pwqeIn->io_buf;
+ if (!lpfc_ncmd) {
+ lpfc_sli_release_iocbq(phba, pwqeIn);
+ return;
+ }
+ /* For abort iocb just return, IO iocb will do a done call */
+ if (bf_get(wqe_cmnd, &pwqeIn->wqe.gen_req.wqe_com) ==
+ CMD_ABORT_XRI_CX) {
+ lpfc_sli_release_iocbq(phba, pwqeIn);
+ return;
+ }
+
+ spin_lock(&lpfc_ncmd->buf_lock);
+ nCmd = lpfc_ncmd->nvmeCmd;
+ if (!nCmd) {
+ spin_unlock(&lpfc_ncmd->buf_lock);
+ lpfc_release_nvme_buf(phba, lpfc_ncmd);
+ return;
+ }
+ spin_unlock(&lpfc_ncmd->buf_lock);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6194 NVME Cancel xri %x\n",
+ lpfc_ncmd->cur_iocbq.sli4_xritag);
+
+ wcqep->word0 = 0;
+ bf_set(lpfc_wcqe_c_status, wcqep, stat);
+ wcqep->parameter = param;
+ wcqep->total_data_placed = 0;
+ wcqep->word3 = 0; /* xb is 0 */
+
+ /* Call release with XB=1 to queue the IO into the abort list. */
+ if (phba->sli.sli_flag & LPFC_SLI_ACTIVE)
+ bf_set(lpfc_wcqe_c_xb, wcqep, 1);
+
+ memcpy(&pwqeIn->wcqe_cmpl, wcqep, sizeof(*wcqep));
+ (pwqeIn->cmd_cmpl)(phba, pwqeIn, pwqeIn);
+#endif
+}
+
+/**
+ * lpfc_nvmels_flush_cmd - Clean up outstanding nvmels commands for a port
+ * @phba: Pointer to HBA context object.
+ *
+ **/
+void
+lpfc_nvmels_flush_cmd(struct lpfc_hba *phba)
+{
+#if (IS_ENABLED(CONFIG_NVME_FC))
+ LIST_HEAD(cancel_list);
+ struct lpfc_sli_ring *pring = NULL;
+ struct lpfc_iocbq *piocb, *tmp_iocb;
+ unsigned long iflags;
+
+ if (phba->sli4_hba.nvmels_wq)
+ pring = phba->sli4_hba.nvmels_wq->pring;
+
+ if (unlikely(!pring))
+ return;
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ spin_lock(&pring->ring_lock);
+ list_splice_init(&pring->txq, &cancel_list);
+ pring->txq_cnt = 0;
+ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
+ if (piocb->cmd_flag & LPFC_IO_NVME_LS) {
+ list_move_tail(&piocb->list, &cancel_list);
+ pring->txcmplq_cnt--;
+ piocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ;
+ }
+ }
+ spin_unlock(&pring->ring_lock);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ if (!list_empty(&cancel_list))
+ lpfc_sli_cancel_iocbs(phba, &cancel_list, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
+#endif
+}
diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h
new file mode 100644
index 000000000000..733c277948c0
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_nvme.h
@@ -0,0 +1,253 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.broadcom.com *
+ * Portions Copyright (C) 2004-2005 Christoph Hellwig *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ ********************************************************************/
+
+#include <linux/nvme.h>
+#include <linux/nvme-fc-driver.h>
+#include <linux/nvme-fc.h>
+
+#define LPFC_NVME_DEFAULT_SEGS (64 + 1) /* 256K IOs */
+
+#define LPFC_NVME_ERSP_LEN 0x20
+
+#define LPFC_NVME_WAIT_TMO 10
+#define LPFC_NVME_EXPEDITE_XRICNT 8
+#define LPFC_NVME_FB_SHIFT 9
+#define LPFC_NVME_MAX_FB (1 << 20) /* 1M */
+
+#define lpfc_ndlp_get_nrport(ndlp) \
+ ((!ndlp->nrport || (ndlp->fc4_xpt_flags & NVME_XPT_UNREG_WAIT))\
+ ? NULL : ndlp->nrport)
+
+struct lpfc_nvme_qhandle {
+ uint32_t index; /* WQ index to use */
+ uint32_t qidx; /* queue index passed to create */
+ uint32_t cpu_id; /* current cpu id at time of create */
+};
+
+/* Declare nvme-based local and remote port definitions. */
+struct lpfc_nvme_lport {
+ struct lpfc_vport *vport;
+ struct completion *lport_unreg_cmp;
+ /* Add stats counters here */
+ atomic_t fc4NvmeLsRequests;
+ atomic_t fc4NvmeLsCmpls;
+ atomic_t xmt_fcp_noxri;
+ atomic_t xmt_fcp_bad_ndlp;
+ atomic_t xmt_fcp_qdepth;
+ atomic_t xmt_fcp_wqerr;
+ atomic_t xmt_fcp_err;
+ atomic_t xmt_fcp_abort;
+ atomic_t xmt_ls_abort;
+ atomic_t xmt_ls_err;
+ atomic_t cmpl_fcp_xb;
+ atomic_t cmpl_fcp_err;
+ atomic_t cmpl_ls_xb;
+ atomic_t cmpl_ls_err;
+};
+
+struct lpfc_nvme_rport {
+ struct lpfc_nvme_lport *lport;
+ struct nvme_fc_remote_port *remoteport;
+ struct lpfc_nodelist *ndlp;
+ struct completion rport_unreg_done;
+};
+
+struct lpfc_nvme_fcpreq_priv {
+ struct lpfc_io_buf *nvme_buf;
+};
+
+/*
+ * set NVME LS request timeouts to 30s. It is larger than the 2*R_A_TOV
+ * set by the spec, which appears to have issues with some devices.
+ */
+#define LPFC_NVME_LS_TIMEOUT 30
+
+
+#define LPFC_NVMET_DEFAULT_SEGS (64 + 1) /* 256K IOs */
+#define LPFC_NVMET_RQE_MIN_POST 128
+#define LPFC_NVMET_RQE_DEF_POST 512
+#define LPFC_NVMET_RQE_DEF_COUNT 2048
+#define LPFC_NVMET_SUCCESS_LEN 12
+
+#define LPFC_NVMET_MRQ_AUTO 0
+#define LPFC_NVMET_MRQ_MAX 16
+
+#define LPFC_NVMET_WAIT_TMO (5 * MSEC_PER_SEC)
+
+/* Used for NVME Target */
+#define LPFC_NVMET_INV_HOST_ACTIVE 1
+
+struct lpfc_nvmet_tgtport {
+ struct lpfc_hba *phba;
+ struct completion *tport_unreg_cmp;
+ atomic_t state; /* tracks nvmet hosthandle invalidation */
+
+ /* Stats counters - lpfc_nvmet_unsol_ls_buffer */
+ atomic_t rcv_ls_req_in;
+ atomic_t rcv_ls_req_out;
+ atomic_t rcv_ls_req_drop;
+ atomic_t xmt_ls_abort;
+ atomic_t xmt_ls_abort_cmpl;
+
+ /* Stats counters - lpfc_nvmet_xmt_ls_rsp */
+ atomic_t xmt_ls_rsp;
+ atomic_t xmt_ls_drop;
+
+ /* Stats counters - lpfc_nvmet_xmt_ls_rsp_cmp */
+ atomic_t xmt_ls_rsp_error;
+ atomic_t xmt_ls_rsp_aborted;
+ atomic_t xmt_ls_rsp_xb_set;
+ atomic_t xmt_ls_rsp_cmpl;
+
+ /* Stats counters - lpfc_nvmet_unsol_fcp_buffer */
+ atomic_t rcv_fcp_cmd_in;
+ atomic_t rcv_fcp_cmd_out;
+ atomic_t rcv_fcp_cmd_drop;
+ atomic_t rcv_fcp_cmd_defer;
+ atomic_t xmt_fcp_release;
+
+ /* Stats counters - lpfc_nvmet_xmt_fcp_op */
+ atomic_t xmt_fcp_drop;
+ atomic_t xmt_fcp_read_rsp;
+ atomic_t xmt_fcp_read;
+ atomic_t xmt_fcp_write;
+ atomic_t xmt_fcp_rsp;
+
+ /* Stats counters - lpfc_nvmet_xmt_fcp_op_cmp */
+ atomic_t xmt_fcp_rsp_xb_set;
+ atomic_t xmt_fcp_rsp_cmpl;
+ atomic_t xmt_fcp_rsp_error;
+ atomic_t xmt_fcp_rsp_aborted;
+ atomic_t xmt_fcp_rsp_drop;
+
+ /* Stats counters - lpfc_nvmet_xmt_fcp_abort */
+ atomic_t xmt_fcp_xri_abort_cqe;
+ atomic_t xmt_fcp_abort;
+ atomic_t xmt_fcp_abort_cmpl;
+ atomic_t xmt_abort_sol;
+ atomic_t xmt_abort_unsol;
+ atomic_t xmt_abort_rsp;
+ atomic_t xmt_abort_rsp_error;
+
+ /* Stats counters - defer IO */
+ atomic_t defer_ctx;
+ atomic_t defer_fod;
+ atomic_t defer_wqfull;
+};
+
+struct lpfc_nvmet_ctx_info {
+ struct list_head nvmet_ctx_list;
+ spinlock_t nvmet_ctx_list_lock; /* lock per CPU */
+ struct lpfc_nvmet_ctx_info *nvmet_ctx_next_cpu;
+ struct lpfc_nvmet_ctx_info *nvmet_ctx_start_cpu;
+ uint16_t nvmet_ctx_list_cnt;
+ char pad[16]; /* pad to a cache-line */
+};
+
+/* This retrieves the context info associated with the specified cpu / mrq */
+#define lpfc_get_ctx_list(phba, cpu, mrq) \
+ (phba->sli4_hba.nvmet_ctx_info + ((cpu * phba->cfg_nvmet_mrq) + mrq))
+
+/* Values for state field of struct lpfc_async_xchg_ctx */
+#define LPFC_NVME_STE_LS_RCV 1
+#define LPFC_NVME_STE_LS_ABORT 2
+#define LPFC_NVME_STE_LS_RSP 3
+#define LPFC_NVME_STE_RCV 4
+#define LPFC_NVME_STE_DATA 5
+#define LPFC_NVME_STE_ABORT 6
+#define LPFC_NVME_STE_DONE 7
+#define LPFC_NVME_STE_FREE 0xff
+
+/* Values for flag field of struct lpfc_async_xchg_ctx */
+#define LPFC_NVME_IO_INP 0x1 /* IO is in progress on exchange */
+#define LPFC_NVME_ABORT_OP 0x2 /* Abort WQE issued on exchange */
+#define LPFC_NVME_XBUSY 0x4 /* XB bit set on IO cmpl */
+#define LPFC_NVME_CTX_RLS 0x8 /* ctx free requested */
+#define LPFC_NVME_ABTS_RCV 0x10 /* ABTS received on exchange */
+#define LPFC_NVME_CTX_REUSE_WQ 0x20 /* ctx reused via WQ */
+#define LPFC_NVME_DEFER_WQFULL 0x40 /* Waiting on a free WQE */
+#define LPFC_NVME_TNOTIFY 0x80 /* notify transport of abts */
+
+struct lpfc_async_xchg_ctx {
+ union {
+ struct nvmefc_tgt_fcp_req fcp_req;
+ } hdlrctx;
+ struct list_head list;
+ struct lpfc_hba *phba;
+ struct lpfc_nodelist *ndlp;
+ struct nvmefc_ls_req *ls_req;
+ struct nvmefc_ls_rsp ls_rsp;
+ struct lpfc_iocbq *wqeq;
+ struct lpfc_iocbq *abort_wqeq;
+ spinlock_t ctxlock; /* protect flag access */
+ uint32_t sid;
+ uint32_t offset;
+ uint16_t oxid;
+ uint16_t size;
+ uint16_t entry_cnt;
+ uint16_t cpu;
+ uint16_t idx;
+ uint16_t state;
+ uint16_t flag;
+ void *payload;
+ struct rqb_dmabuf *rqb_buffer;
+ struct lpfc_nvmet_ctxbuf *ctxbuf;
+ struct lpfc_sli4_hdw_queue *hdwq;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint64_t ts_isr_cmd;
+ uint64_t ts_cmd_nvme;
+ uint64_t ts_nvme_data;
+ uint64_t ts_data_wqput;
+ uint64_t ts_isr_data;
+ uint64_t ts_data_nvme;
+ uint64_t ts_nvme_status;
+ uint64_t ts_status_wqput;
+ uint64_t ts_isr_status;
+ uint64_t ts_status_nvme;
+#endif
+};
+
+
+/* routines found in lpfc_nvme.c */
+int __lpfc_nvme_ls_req(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ struct nvmefc_ls_req *pnvme_lsreq,
+ void (*gen_req_cmp)(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe));
+void __lpfc_nvme_ls_req_cmp(struct lpfc_hba *phba, struct lpfc_vport *vport,
+ struct lpfc_iocbq *cmdwqe, struct lpfc_wcqe_complete *wcqe);
+int __lpfc_nvme_ls_abort(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp, struct nvmefc_ls_req *pnvme_lsreq);
+
+/* routines found in lpfc_nvmet.c */
+int lpfc_nvme_unsol_ls_issue_abort(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp, uint32_t sid,
+ uint16_t xri);
+int __lpfc_nvme_xmt_ls_rsp(struct lpfc_async_xchg_ctx *axchg,
+ struct nvmefc_ls_rsp *ls_rsp,
+ void (*xmt_ls_rsp_cmp)(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe));
+void __lpfc_nvme_xmt_ls_rsp_cmp(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdwqe, struct lpfc_iocbq *rspwqe);
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c
new file mode 100644
index 000000000000..4cfc928bcf2d
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_nvmet.c
@@ -0,0 +1,3656 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.broadcom.com *
+ * Portions Copyright (C) 2004-2005 Christoph Hellwig *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ ********************************************************************/
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/unaligned.h>
+#include <linux/crc-t10dif.h>
+#include <net/checksum.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
+
+#include "lpfc_version.h"
+#include "lpfc_hw4.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
+#include "lpfc_nl.h"
+#include "lpfc_disc.h"
+#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+#include "lpfc_vport.h"
+#include "lpfc_debugfs.h"
+
+static struct lpfc_iocbq *lpfc_nvmet_prep_ls_wqe(struct lpfc_hba *,
+ struct lpfc_async_xchg_ctx *,
+ dma_addr_t rspbuf,
+ uint16_t rspsize);
+static struct lpfc_iocbq *lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *,
+ struct lpfc_async_xchg_ctx *);
+static int lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *,
+ struct lpfc_async_xchg_ctx *,
+ uint32_t, uint16_t);
+static int lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *,
+ struct lpfc_async_xchg_ctx *,
+ uint32_t, uint16_t);
+static void lpfc_nvmet_wqfull_flush(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_async_xchg_ctx *);
+static void lpfc_nvmet_fcp_rqst_defer_work(struct work_struct *);
+
+static void lpfc_nvmet_process_rcv_fcp_req(struct lpfc_nvmet_ctxbuf *ctx_buf);
+
+static union lpfc_wqe128 lpfc_tsend_cmd_template;
+static union lpfc_wqe128 lpfc_treceive_cmd_template;
+static union lpfc_wqe128 lpfc_trsp_cmd_template;
+
+/* Setup WQE templates for NVME IOs */
+void
+lpfc_nvmet_cmd_template(void)
+{
+ union lpfc_wqe128 *wqe;
+
+ /* TSEND template */
+ wqe = &lpfc_tsend_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - payload_offset_len is zero */
+
+ /* Word 4 - relative_offset is variable */
+
+ /* Word 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 - wqe_ar is variable */
+ bf_set(wqe_cmnd, &wqe->fcp_tsend.wqe_com, CMD_FCP_TSEND64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_tsend.wqe_com, PARM_REL_OFF);
+ bf_set(wqe_class, &wqe->fcp_tsend.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_tsend.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 1);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag, rcvoxid is variable */
+
+ /* Word 10 - wqes, xc is variable */
+ bf_set(wqe_xchg, &wqe->fcp_tsend.wqe_com, LPFC_NVME_XCHG);
+ bf_set(wqe_dbde, &wqe->fcp_tsend.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->fcp_tsend.wqe_com, LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_lenloc, &wqe->fcp_tsend.wqe_com, LPFC_WQE_LENLOC_WORD12);
+
+ /* Word 11 - sup, irsp, irsplen is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_tsend.wqe_com, FCP_COMMAND_TSEND);
+ bf_set(wqe_cqid, &wqe->fcp_tsend.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_pbde, &wqe->fcp_tsend.wqe_com, 0);
+
+ /* Word 12 - fcp_data_len is variable */
+
+ /* Word 13, 14, 15 - PBDE is zero */
+
+ /* TRECEIVE template */
+ wqe = &lpfc_treceive_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 */
+ wqe->fcp_treceive.payload_offset_len = TXRDY_PAYLOAD_LEN;
+
+ /* Word 4 - relative_offset is variable */
+
+ /* Word 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_treceive.wqe_com, CMD_FCP_TRECEIVE64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_treceive.wqe_com, PARM_REL_OFF);
+ bf_set(wqe_class, &wqe->fcp_treceive.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_treceive.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_ar, &wqe->fcp_treceive.wqe_com, 0);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag, rcvoxid is variable */
+
+ /* Word 10 - xc is variable */
+ bf_set(wqe_dbde, &wqe->fcp_treceive.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_xchg, &wqe->fcp_treceive.wqe_com, LPFC_NVME_XCHG);
+ bf_set(wqe_iod, &wqe->fcp_treceive.wqe_com, LPFC_WQE_IOD_READ);
+ bf_set(wqe_lenloc, &wqe->fcp_treceive.wqe_com, LPFC_WQE_LENLOC_WORD12);
+ bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, 1);
+
+ /* Word 11 - pbde is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_treceive.wqe_com, FCP_COMMAND_TRECEIVE);
+ bf_set(wqe_cqid, &wqe->fcp_treceive.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_sup, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_irsp, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_irsplen, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_pbde, &wqe->fcp_treceive.wqe_com, 1);
+
+ /* Word 12 - fcp_data_len is variable */
+
+ /* Word 13, 14, 15 - PBDE is variable */
+
+ /* TRSP template */
+ wqe = &lpfc_trsp_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - response_len is variable */
+
+ /* Word 4, 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_trsp.wqe_com, CMD_FCP_TRSP64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_trsp.wqe_com, PARM_UNUSED);
+ bf_set(wqe_class, &wqe->fcp_trsp.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_trsp.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_ag, &wqe->fcp_trsp.wqe_com, 1); /* wqe_ar */
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 wqes, xc is variable */
+ bf_set(wqe_dbde, &wqe->fcp_trsp.wqe_com, 1);
+ bf_set(wqe_xchg, &wqe->fcp_trsp.wqe_com, LPFC_NVME_XCHG);
+ bf_set(wqe_wqes, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_xc, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_iod, &wqe->fcp_trsp.wqe_com, LPFC_WQE_IOD_NONE);
+ bf_set(wqe_lenloc, &wqe->fcp_trsp.wqe_com, LPFC_WQE_LENLOC_WORD3);
+
+ /* Word 11 irsp, irsplen is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_trsp.wqe_com, FCP_COMMAND_TRSP);
+ bf_set(wqe_cqid, &wqe->fcp_trsp.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_sup, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_pbde, &wqe->fcp_trsp.wqe_com, 0);
+
+ /* Word 12, 13, 14, 15 - is zero */
+}
+
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+static struct lpfc_async_xchg_ctx *
+lpfc_nvmet_get_ctx_for_xri(struct lpfc_hba *phba, u16 xri)
+{
+ struct lpfc_async_xchg_ctx *ctxp;
+ unsigned long iflag;
+ bool found = false;
+
+ spin_lock_irqsave(&phba->sli4_hba.t_active_list_lock, iflag);
+ list_for_each_entry(ctxp, &phba->sli4_hba.t_active_ctx_list, list) {
+ if (ctxp->ctxbuf->sglq->sli4_xritag != xri)
+ continue;
+
+ found = true;
+ break;
+ }
+ spin_unlock_irqrestore(&phba->sli4_hba.t_active_list_lock, iflag);
+ if (found)
+ return ctxp;
+
+ return NULL;
+}
+
+static struct lpfc_async_xchg_ctx *
+lpfc_nvmet_get_ctx_for_oxid(struct lpfc_hba *phba, u16 oxid, u32 sid)
+{
+ struct lpfc_async_xchg_ctx *ctxp;
+ unsigned long iflag;
+ bool found = false;
+
+ spin_lock_irqsave(&phba->sli4_hba.t_active_list_lock, iflag);
+ list_for_each_entry(ctxp, &phba->sli4_hba.t_active_ctx_list, list) {
+ if (ctxp->oxid != oxid || ctxp->sid != sid)
+ continue;
+
+ found = true;
+ break;
+ }
+ spin_unlock_irqrestore(&phba->sli4_hba.t_active_list_lock, iflag);
+ if (found)
+ return ctxp;
+
+ return NULL;
+}
+#endif
+
+static void
+lpfc_nvmet_defer_release(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp)
+{
+ lockdep_assert_held(&ctxp->ctxlock);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6313 NVMET Defer ctx release oxid x%x flg x%x\n",
+ ctxp->oxid, ctxp->flag);
+
+ if (ctxp->flag & LPFC_NVME_CTX_RLS)
+ return;
+
+ ctxp->flag |= LPFC_NVME_CTX_RLS;
+ spin_lock(&phba->sli4_hba.t_active_list_lock);
+ list_del(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.t_active_list_lock);
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_add_tail(&ctxp->list, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+}
+
+/**
+ * __lpfc_nvme_xmt_ls_rsp_cmp - Generic completion handler for the
+ * transmission of an NVME LS response.
+ * @phba: Pointer to HBA context object.
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @rspwqe: Pointer to driver response WQE object.
+ *
+ * The function is called from SLI ring event handler with no
+ * lock held. The function frees memory resources used for the command
+ * used to send the NVME LS RSP.
+ **/
+void
+__lpfc_nvme_xmt_ls_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_async_xchg_ctx *axchg = cmdwqe->context_un.axchg;
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+ struct nvmefc_ls_rsp *ls_rsp = &axchg->ls_rsp;
+ uint32_t status, result;
+
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+ result = wcqe->parameter;
+
+ if (axchg->state != LPFC_NVME_STE_LS_RSP || axchg->entry_cnt != 2) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6410 NVMEx LS cmpl state mismatch IO x%x: "
+ "%d %d\n",
+ axchg->oxid, axchg->state, axchg->entry_cnt);
+ }
+
+ lpfc_nvmeio_data(phba, "NVMEx LS CMPL: xri x%x stat x%x result x%x\n",
+ axchg->oxid, status, result);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6038 NVMEx LS rsp cmpl: %d %d oxid x%x\n",
+ status, result, axchg->oxid);
+
+ lpfc_nlp_put(cmdwqe->ndlp);
+ cmdwqe->context_un.axchg = NULL;
+ cmdwqe->bpl_dmabuf = NULL;
+ lpfc_sli_release_iocbq(phba, cmdwqe);
+ ls_rsp->done(ls_rsp);
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6200 NVMEx LS rsp cmpl done status %d oxid x%x\n",
+ status, axchg->oxid);
+ kfree(axchg);
+}
+
+/**
+ * lpfc_nvmet_xmt_ls_rsp_cmp - Completion handler for LS Response
+ * @phba: Pointer to HBA context object.
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @rspwqe: Pointer to driver response WQE object.
+ *
+ * The function is called from SLI ring event handler with no
+ * lock held. This function is the completion handler for NVME LS commands
+ * The function updates any states and statistics, then calls the
+ * generic completion handler to free resources.
+ **/
+static void
+lpfc_nvmet_xmt_ls_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_nvmet_tgtport *tgtp;
+ uint32_t status, result;
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+
+ if (!phba->targetport)
+ goto finish;
+
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+ result = wcqe->parameter;
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ if (tgtp) {
+ if (status) {
+ atomic_inc(&tgtp->xmt_ls_rsp_error);
+ if (result == IOERR_ABORT_REQUESTED)
+ atomic_inc(&tgtp->xmt_ls_rsp_aborted);
+ if (bf_get(lpfc_wcqe_c_xb, wcqe))
+ atomic_inc(&tgtp->xmt_ls_rsp_xb_set);
+ } else {
+ atomic_inc(&tgtp->xmt_ls_rsp_cmpl);
+ }
+ }
+
+finish:
+ __lpfc_nvme_xmt_ls_rsp_cmp(phba, cmdwqe, rspwqe);
+}
+
+/**
+ * lpfc_nvmet_ctxbuf_post - Repost a NVMET RQ DMA buffer and clean up context
+ * @phba: HBA buffer is associated with
+ * @ctx_buf: ctx buffer context
+ *
+ * Description: Frees the given DMA buffer in the appropriate way given by
+ * reposting it to its associated RQ so it can be reused.
+ *
+ * Notes: Takes phba->hbalock. Can be called with or without other locks held.
+ *
+ * Returns: None
+ **/
+void
+lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_async_xchg_ctx *ctxp = ctx_buf->context;
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct fc_frame_header *fc_hdr;
+ struct rqb_dmabuf *nvmebuf;
+ struct lpfc_nvmet_ctx_info *infop;
+ uint32_t size, oxid, sid;
+ int cpu;
+ unsigned long iflag;
+
+ if (ctxp->state == LPFC_NVME_STE_FREE) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6411 NVMET free, already free IO x%x: %d %d\n",
+ ctxp->oxid, ctxp->state, ctxp->entry_cnt);
+ }
+
+ if (ctxp->rqb_buffer) {
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ nvmebuf = ctxp->rqb_buffer;
+ /* check if freed in another path whilst acquiring lock */
+ if (nvmebuf) {
+ ctxp->rqb_buffer = NULL;
+ if (ctxp->flag & LPFC_NVME_CTX_REUSE_WQ) {
+ ctxp->flag &= ~LPFC_NVME_CTX_REUSE_WQ;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ nvmebuf->hrq->rqbp->rqb_free_buffer(phba,
+ nvmebuf);
+ } else {
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ /* repost */
+ lpfc_rq_buf_free(phba, &nvmebuf->hbuf);
+ }
+ } else {
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ }
+ }
+ ctxp->state = LPFC_NVME_STE_FREE;
+
+ spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
+ if (phba->sli4_hba.nvmet_io_wait_cnt) {
+ list_remove_head(&phba->sli4_hba.lpfc_nvmet_io_wait_list,
+ nvmebuf, struct rqb_dmabuf,
+ hbuf.list);
+ phba->sli4_hba.nvmet_io_wait_cnt--;
+ spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock,
+ iflag);
+
+ fc_hdr = (struct fc_frame_header *)(nvmebuf->hbuf.virt);
+ oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ size = nvmebuf->bytes_recv;
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
+
+ ctxp = (struct lpfc_async_xchg_ctx *)ctx_buf->context;
+ ctxp->wqeq = NULL;
+ ctxp->offset = 0;
+ ctxp->phba = phba;
+ ctxp->size = size;
+ ctxp->oxid = oxid;
+ ctxp->sid = sid;
+ ctxp->state = LPFC_NVME_STE_RCV;
+ ctxp->entry_cnt = 1;
+ ctxp->flag = 0;
+ ctxp->ctxbuf = ctx_buf;
+ ctxp->rqb_buffer = (void *)nvmebuf;
+ spin_lock_init(&ctxp->ctxlock);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ /* NOTE: isr time stamp is stale when context is re-assigned*/
+ if (ctxp->ts_isr_cmd) {
+ ctxp->ts_cmd_nvme = 0;
+ ctxp->ts_nvme_data = 0;
+ ctxp->ts_data_wqput = 0;
+ ctxp->ts_isr_data = 0;
+ ctxp->ts_data_nvme = 0;
+ ctxp->ts_nvme_status = 0;
+ ctxp->ts_status_wqput = 0;
+ ctxp->ts_isr_status = 0;
+ ctxp->ts_status_nvme = 0;
+ }
+#endif
+ atomic_inc(&tgtp->rcv_fcp_cmd_in);
+
+ /* Indicate that a replacement buffer has been posted */
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ ctxp->flag |= LPFC_NVME_CTX_REUSE_WQ;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+
+ if (!queue_work(phba->wq, &ctx_buf->defer_work)) {
+ atomic_inc(&tgtp->rcv_fcp_cmd_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6181 Unable to queue deferred work "
+ "for oxid x%x. "
+ "FCP Drop IO [x%x x%x x%x]\n",
+ ctxp->oxid,
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out),
+ atomic_read(&tgtp->xmt_fcp_release));
+
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid);
+ }
+ return;
+ }
+ spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
+
+ /*
+ * Use the CPU context list, from the MRQ the IO was received on
+ * (ctxp->idx), to save context structure.
+ */
+ spin_lock_irqsave(&phba->sli4_hba.t_active_list_lock, iflag);
+ list_del_init(&ctxp->list);
+ spin_unlock_irqrestore(&phba->sli4_hba.t_active_list_lock, iflag);
+ cpu = raw_smp_processor_id();
+ infop = lpfc_get_ctx_list(phba, cpu, ctxp->idx);
+ spin_lock_irqsave(&infop->nvmet_ctx_list_lock, iflag);
+ list_add_tail(&ctx_buf->list, &infop->nvmet_ctx_list);
+ infop->nvmet_ctx_list_cnt++;
+ spin_unlock_irqrestore(&infop->nvmet_ctx_list_lock, iflag);
+#endif
+}
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+static void
+lpfc_nvmet_ktime(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp)
+{
+ uint64_t seg1, seg2, seg3, seg4, seg5;
+ uint64_t seg6, seg7, seg8, seg9, seg10;
+ uint64_t segsum;
+
+ if (!ctxp->ts_isr_cmd || !ctxp->ts_cmd_nvme ||
+ !ctxp->ts_nvme_data || !ctxp->ts_data_wqput ||
+ !ctxp->ts_isr_data || !ctxp->ts_data_nvme ||
+ !ctxp->ts_nvme_status || !ctxp->ts_status_wqput ||
+ !ctxp->ts_isr_status || !ctxp->ts_status_nvme)
+ return;
+
+ if (ctxp->ts_status_nvme < ctxp->ts_isr_cmd)
+ return;
+ if (ctxp->ts_isr_cmd > ctxp->ts_cmd_nvme)
+ return;
+ if (ctxp->ts_cmd_nvme > ctxp->ts_nvme_data)
+ return;
+ if (ctxp->ts_nvme_data > ctxp->ts_data_wqput)
+ return;
+ if (ctxp->ts_data_wqput > ctxp->ts_isr_data)
+ return;
+ if (ctxp->ts_isr_data > ctxp->ts_data_nvme)
+ return;
+ if (ctxp->ts_data_nvme > ctxp->ts_nvme_status)
+ return;
+ if (ctxp->ts_nvme_status > ctxp->ts_status_wqput)
+ return;
+ if (ctxp->ts_status_wqput > ctxp->ts_isr_status)
+ return;
+ if (ctxp->ts_isr_status > ctxp->ts_status_nvme)
+ return;
+ /*
+ * Segment 1 - Time from FCP command received by MSI-X ISR
+ * to FCP command is passed to NVME Layer.
+ * Segment 2 - Time from FCP command payload handed
+ * off to NVME Layer to Driver receives a Command op
+ * from NVME Layer.
+ * Segment 3 - Time from Driver receives a Command op
+ * from NVME Layer to Command is put on WQ.
+ * Segment 4 - Time from Driver WQ put is done
+ * to MSI-X ISR for Command cmpl.
+ * Segment 5 - Time from MSI-X ISR for Command cmpl to
+ * Command cmpl is passed to NVME Layer.
+ * Segment 6 - Time from Command cmpl is passed to NVME
+ * Layer to Driver receives a RSP op from NVME Layer.
+ * Segment 7 - Time from Driver receives a RSP op from
+ * NVME Layer to WQ put is done on TRSP FCP Status.
+ * Segment 8 - Time from Driver WQ put is done on TRSP
+ * FCP Status to MSI-X ISR for TRSP cmpl.
+ * Segment 9 - Time from MSI-X ISR for TRSP cmpl to
+ * TRSP cmpl is passed to NVME Layer.
+ * Segment 10 - Time from FCP command received by
+ * MSI-X ISR to command is completed on wire.
+ * (Segments 1 thru 8) for READDATA / WRITEDATA
+ * (Segments 1 thru 4) for READDATA_RSP
+ */
+ seg1 = ctxp->ts_cmd_nvme - ctxp->ts_isr_cmd;
+ segsum = seg1;
+
+ seg2 = ctxp->ts_nvme_data - ctxp->ts_isr_cmd;
+ if (segsum > seg2)
+ return;
+ seg2 -= segsum;
+ segsum += seg2;
+
+ seg3 = ctxp->ts_data_wqput - ctxp->ts_isr_cmd;
+ if (segsum > seg3)
+ return;
+ seg3 -= segsum;
+ segsum += seg3;
+
+ seg4 = ctxp->ts_isr_data - ctxp->ts_isr_cmd;
+ if (segsum > seg4)
+ return;
+ seg4 -= segsum;
+ segsum += seg4;
+
+ seg5 = ctxp->ts_data_nvme - ctxp->ts_isr_cmd;
+ if (segsum > seg5)
+ return;
+ seg5 -= segsum;
+ segsum += seg5;
+
+
+ /* For auto rsp commands seg6 thru seg10 will be 0 */
+ if (ctxp->ts_nvme_status > ctxp->ts_data_nvme) {
+ seg6 = ctxp->ts_nvme_status - ctxp->ts_isr_cmd;
+ if (segsum > seg6)
+ return;
+ seg6 -= segsum;
+ segsum += seg6;
+
+ seg7 = ctxp->ts_status_wqput - ctxp->ts_isr_cmd;
+ if (segsum > seg7)
+ return;
+ seg7 -= segsum;
+ segsum += seg7;
+
+ seg8 = ctxp->ts_isr_status - ctxp->ts_isr_cmd;
+ if (segsum > seg8)
+ return;
+ seg8 -= segsum;
+ segsum += seg8;
+
+ seg9 = ctxp->ts_status_nvme - ctxp->ts_isr_cmd;
+ if (segsum > seg9)
+ return;
+ seg9 -= segsum;
+ segsum += seg9;
+
+ if (ctxp->ts_isr_status < ctxp->ts_isr_cmd)
+ return;
+ seg10 = (ctxp->ts_isr_status -
+ ctxp->ts_isr_cmd);
+ } else {
+ if (ctxp->ts_isr_data < ctxp->ts_isr_cmd)
+ return;
+ seg6 = 0;
+ seg7 = 0;
+ seg8 = 0;
+ seg9 = 0;
+ seg10 = (ctxp->ts_isr_data - ctxp->ts_isr_cmd);
+ }
+
+ phba->ktime_seg1_total += seg1;
+ if (seg1 < phba->ktime_seg1_min)
+ phba->ktime_seg1_min = seg1;
+ else if (seg1 > phba->ktime_seg1_max)
+ phba->ktime_seg1_max = seg1;
+
+ phba->ktime_seg2_total += seg2;
+ if (seg2 < phba->ktime_seg2_min)
+ phba->ktime_seg2_min = seg2;
+ else if (seg2 > phba->ktime_seg2_max)
+ phba->ktime_seg2_max = seg2;
+
+ phba->ktime_seg3_total += seg3;
+ if (seg3 < phba->ktime_seg3_min)
+ phba->ktime_seg3_min = seg3;
+ else if (seg3 > phba->ktime_seg3_max)
+ phba->ktime_seg3_max = seg3;
+
+ phba->ktime_seg4_total += seg4;
+ if (seg4 < phba->ktime_seg4_min)
+ phba->ktime_seg4_min = seg4;
+ else if (seg4 > phba->ktime_seg4_max)
+ phba->ktime_seg4_max = seg4;
+
+ phba->ktime_seg5_total += seg5;
+ if (seg5 < phba->ktime_seg5_min)
+ phba->ktime_seg5_min = seg5;
+ else if (seg5 > phba->ktime_seg5_max)
+ phba->ktime_seg5_max = seg5;
+
+ phba->ktime_data_samples++;
+ if (!seg6)
+ goto out;
+
+ phba->ktime_seg6_total += seg6;
+ if (seg6 < phba->ktime_seg6_min)
+ phba->ktime_seg6_min = seg6;
+ else if (seg6 > phba->ktime_seg6_max)
+ phba->ktime_seg6_max = seg6;
+
+ phba->ktime_seg7_total += seg7;
+ if (seg7 < phba->ktime_seg7_min)
+ phba->ktime_seg7_min = seg7;
+ else if (seg7 > phba->ktime_seg7_max)
+ phba->ktime_seg7_max = seg7;
+
+ phba->ktime_seg8_total += seg8;
+ if (seg8 < phba->ktime_seg8_min)
+ phba->ktime_seg8_min = seg8;
+ else if (seg8 > phba->ktime_seg8_max)
+ phba->ktime_seg8_max = seg8;
+
+ phba->ktime_seg9_total += seg9;
+ if (seg9 < phba->ktime_seg9_min)
+ phba->ktime_seg9_min = seg9;
+ else if (seg9 > phba->ktime_seg9_max)
+ phba->ktime_seg9_max = seg9;
+out:
+ phba->ktime_seg10_total += seg10;
+ if (seg10 < phba->ktime_seg10_min)
+ phba->ktime_seg10_min = seg10;
+ else if (seg10 > phba->ktime_seg10_max)
+ phba->ktime_seg10_max = seg10;
+ phba->ktime_status_samples++;
+}
+#endif
+
+/**
+ * lpfc_nvmet_xmt_fcp_op_cmp - Completion handler for FCP Response
+ * @phba: Pointer to HBA context object.
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @rspwqe: Pointer to driver response WQE object.
+ *
+ * The function is called from SLI ring event handler with no
+ * lock held. This function is the completion handler for NVME FCP commands
+ * The function frees memory resources used for the NVME commands.
+ **/
+static void
+lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct nvmefc_tgt_fcp_req *rsp;
+ struct lpfc_async_xchg_ctx *ctxp;
+ uint32_t status, result, op, logerr;
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ int id;
+#endif
+
+ ctxp = cmdwqe->context_un.axchg;
+ ctxp->flag &= ~LPFC_NVME_IO_INP;
+
+ rsp = &ctxp->hdlrctx.fcp_req;
+ op = rsp->op;
+
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+ result = wcqe->parameter;
+
+ if (phba->targetport)
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ else
+ tgtp = NULL;
+
+ lpfc_nvmeio_data(phba, "NVMET FCP CMPL: xri x%x op x%x status x%x\n",
+ ctxp->oxid, op, status);
+
+ if (status) {
+ rsp->fcp_error = NVME_SC_DATA_XFER_ERROR;
+ rsp->transferred_length = 0;
+ if (tgtp) {
+ atomic_inc(&tgtp->xmt_fcp_rsp_error);
+ if (result == IOERR_ABORT_REQUESTED)
+ atomic_inc(&tgtp->xmt_fcp_rsp_aborted);
+ }
+
+ logerr = LOG_NVME_IOERR;
+
+ /* pick up SLI4 exhange busy condition */
+ if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
+ ctxp->flag |= LPFC_NVME_XBUSY;
+ logerr |= LOG_NVME_ABTS;
+ if (tgtp)
+ atomic_inc(&tgtp->xmt_fcp_rsp_xb_set);
+
+ } else {
+ ctxp->flag &= ~LPFC_NVME_XBUSY;
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, logerr,
+ "6315 IO Error Cmpl oxid: x%x xri: x%x %x/%x "
+ "XBUSY:x%x\n",
+ ctxp->oxid, ctxp->ctxbuf->sglq->sli4_xritag,
+ status, result, ctxp->flag);
+
+ } else {
+ rsp->fcp_error = NVME_SC_SUCCESS;
+ if (op == NVMET_FCOP_RSP)
+ rsp->transferred_length = rsp->rsplen;
+ else
+ rsp->transferred_length = rsp->transfer_length;
+ if (tgtp)
+ atomic_inc(&tgtp->xmt_fcp_rsp_cmpl);
+ }
+
+ if ((op == NVMET_FCOP_READDATA_RSP) ||
+ (op == NVMET_FCOP_RSP)) {
+ /* Sanity check */
+ ctxp->state = LPFC_NVME_STE_DONE;
+ ctxp->entry_cnt++;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (ctxp->ts_cmd_nvme) {
+ if (rsp->op == NVMET_FCOP_READDATA_RSP) {
+ ctxp->ts_isr_data =
+ cmdwqe->isr_timestamp;
+ ctxp->ts_data_nvme =
+ ktime_get_ns();
+ ctxp->ts_nvme_status =
+ ctxp->ts_data_nvme;
+ ctxp->ts_status_wqput =
+ ctxp->ts_data_nvme;
+ ctxp->ts_isr_status =
+ ctxp->ts_data_nvme;
+ ctxp->ts_status_nvme =
+ ctxp->ts_data_nvme;
+ } else {
+ ctxp->ts_isr_status =
+ cmdwqe->isr_timestamp;
+ ctxp->ts_status_nvme =
+ ktime_get_ns();
+ }
+ }
+#endif
+ rsp->done(rsp);
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (ctxp->ts_cmd_nvme)
+ lpfc_nvmet_ktime(phba, ctxp);
+#endif
+ /* lpfc_nvmet_xmt_fcp_release() will recycle the context */
+ } else {
+ ctxp->entry_cnt++;
+ memset_startat(cmdwqe, 0, cmd_flag);
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (ctxp->ts_cmd_nvme) {
+ ctxp->ts_isr_data = cmdwqe->isr_timestamp;
+ ctxp->ts_data_nvme = ktime_get_ns();
+ }
+#endif
+ rsp->done(rsp);
+ }
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (phba->hdwqstat_on & LPFC_CHECK_NVMET_IO) {
+ id = raw_smp_processor_id();
+ this_cpu_inc(phba->sli4_hba.c_stat->cmpl_io);
+ if (ctxp->cpu != id)
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6704 CPU Check cmdcmpl: "
+ "cpu %d expect %d\n",
+ id, ctxp->cpu);
+ }
+#endif
+}
+
+/**
+ * __lpfc_nvme_xmt_ls_rsp - Generic service routine to issue transmit
+ * an NVME LS rsp for a prior NVME LS request that was received.
+ * @axchg: pointer to exchange context for the NVME LS request the response
+ * is for.
+ * @ls_rsp: pointer to the transport LS RSP that is to be sent
+ * @xmt_ls_rsp_cmp: completion routine to call upon RSP transmit done
+ *
+ * This routine is used to format and send a WQE to transmit a NVME LS
+ * Response. The response is for a prior NVME LS request that was
+ * received and posted to the transport.
+ *
+ * Returns:
+ * 0 : if response successfully transmit
+ * non-zero : if response failed to transmit, of the form -Exxx.
+ **/
+int
+__lpfc_nvme_xmt_ls_rsp(struct lpfc_async_xchg_ctx *axchg,
+ struct nvmefc_ls_rsp *ls_rsp,
+ void (*xmt_ls_rsp_cmp)(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe))
+{
+ struct lpfc_hba *phba = axchg->phba;
+ struct hbq_dmabuf *nvmebuf = (struct hbq_dmabuf *)axchg->rqb_buffer;
+ struct lpfc_iocbq *nvmewqeq;
+ struct lpfc_dmabuf dmabuf;
+ struct ulp_bde64 bpl;
+ int rc;
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return -ENODEV;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6023 NVMEx LS rsp oxid x%x\n", axchg->oxid);
+
+ if (axchg->state != LPFC_NVME_STE_LS_RCV || axchg->entry_cnt != 1) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6412 NVMEx LS rsp state mismatch "
+ "oxid x%x: %d %d\n",
+ axchg->oxid, axchg->state, axchg->entry_cnt);
+ return -EALREADY;
+ }
+ axchg->state = LPFC_NVME_STE_LS_RSP;
+ axchg->entry_cnt++;
+
+ nvmewqeq = lpfc_nvmet_prep_ls_wqe(phba, axchg, ls_rsp->rspdma,
+ ls_rsp->rsplen);
+ if (nvmewqeq == NULL) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6150 NVMEx LS Drop Rsp x%x: Prep\n",
+ axchg->oxid);
+ rc = -ENOMEM;
+ goto out_free_buf;
+ }
+
+ /* Save numBdes for bpl2sgl */
+ nvmewqeq->num_bdes = 1;
+ nvmewqeq->hba_wqidx = 0;
+ nvmewqeq->bpl_dmabuf = &dmabuf;
+ dmabuf.virt = &bpl;
+ bpl.addrLow = nvmewqeq->wqe.xmit_sequence.bde.addrLow;
+ bpl.addrHigh = nvmewqeq->wqe.xmit_sequence.bde.addrHigh;
+ bpl.tus.f.bdeSize = ls_rsp->rsplen;
+ bpl.tus.f.bdeFlags = 0;
+ bpl.tus.w = le32_to_cpu(bpl.tus.w);
+ /*
+ * Note: although we're using stack space for the dmabuf, the
+ * call to lpfc_sli4_issue_wqe is synchronous, so it will not
+ * be referenced after it returns back to this routine.
+ */
+
+ nvmewqeq->cmd_cmpl = xmt_ls_rsp_cmp;
+ nvmewqeq->context_un.axchg = axchg;
+
+ lpfc_nvmeio_data(phba, "NVMEx LS RSP: xri x%x wqidx x%x len x%x\n",
+ axchg->oxid, nvmewqeq->hba_wqidx, ls_rsp->rsplen);
+
+ rc = lpfc_sli4_issue_wqe(phba, axchg->hdwq, nvmewqeq);
+
+ /* clear to be sure there's no reference */
+ nvmewqeq->bpl_dmabuf = NULL;
+
+ if (rc == WQE_SUCCESS) {
+ /*
+ * Okay to repost buffer here, but wait till cmpl
+ * before freeing ctxp and iocbq.
+ */
+ lpfc_in_buf_free(phba, &nvmebuf->dbuf);
+ return 0;
+ }
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6151 NVMEx LS RSP x%x: failed to transmit %d\n",
+ axchg->oxid, rc);
+
+ rc = -ENXIO;
+
+ lpfc_nlp_put(nvmewqeq->ndlp);
+
+out_free_buf:
+ /* Give back resources */
+ lpfc_in_buf_free(phba, &nvmebuf->dbuf);
+
+ /*
+ * As transport doesn't track completions of responses, if the rsp
+ * fails to send, the transport will effectively ignore the rsp
+ * and consider the LS done. However, the driver has an active
+ * exchange open for the LS - so be sure to abort the exchange
+ * if the response isn't sent.
+ */
+ lpfc_nvme_unsol_ls_issue_abort(phba, axchg, axchg->sid, axchg->oxid);
+ return rc;
+}
+
+/**
+ * lpfc_nvmet_xmt_ls_rsp - Transmit NVME LS response
+ * @tgtport: pointer to target port that NVME LS is to be transmit from.
+ * @ls_rsp: pointer to the transport LS RSP that is to be sent
+ *
+ * Driver registers this routine to transmit responses for received NVME
+ * LS requests.
+ *
+ * This routine is used to format and send a WQE to transmit a NVME LS
+ * Response. The ls_rsp is used to reverse-map the LS to the original
+ * NVME LS request sequence, which provides addressing information for
+ * the remote port the LS to be sent to, as well as the exchange id
+ * that is the LS is bound to.
+ *
+ * Returns:
+ * 0 : if response successfully transmit
+ * non-zero : if response failed to transmit, of the form -Exxx.
+ **/
+static int
+lpfc_nvmet_xmt_ls_rsp(struct nvmet_fc_target_port *tgtport,
+ struct nvmefc_ls_rsp *ls_rsp)
+{
+ struct lpfc_async_xchg_ctx *axchg =
+ container_of(ls_rsp, struct lpfc_async_xchg_ctx, ls_rsp);
+ struct lpfc_nvmet_tgtport *nvmep = tgtport->private;
+ int rc;
+
+ if (test_bit(FC_UNLOADING, &axchg->phba->pport->load_flag))
+ return -ENODEV;
+
+ rc = __lpfc_nvme_xmt_ls_rsp(axchg, ls_rsp, lpfc_nvmet_xmt_ls_rsp_cmp);
+
+ if (rc) {
+ atomic_inc(&nvmep->xmt_ls_drop);
+ /*
+ * unless the failure is due to having already sent
+ * the response, an abort will be generated for the
+ * exchange if the rsp can't be sent.
+ */
+ if (rc != -EALREADY)
+ atomic_inc(&nvmep->xmt_ls_abort);
+ return rc;
+ }
+
+ atomic_inc(&nvmep->xmt_ls_rsp);
+ return 0;
+}
+
+static int
+lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
+ struct nvmefc_tgt_fcp_req *rsp)
+{
+ struct lpfc_nvmet_tgtport *lpfc_nvmep = tgtport->private;
+ struct lpfc_async_xchg_ctx *ctxp =
+ container_of(rsp, struct lpfc_async_xchg_ctx, hdlrctx.fcp_req);
+ struct lpfc_hba *phba = ctxp->phba;
+ struct lpfc_queue *wq;
+ struct lpfc_iocbq *nvmewqeq;
+ struct lpfc_sli_ring *pring;
+ unsigned long iflags;
+ int rc;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ int id;
+#endif
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
+ rc = -ENODEV;
+ goto aerr;
+ }
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (ctxp->ts_cmd_nvme) {
+ if (rsp->op == NVMET_FCOP_RSP)
+ ctxp->ts_nvme_status = ktime_get_ns();
+ else
+ ctxp->ts_nvme_data = ktime_get_ns();
+ }
+
+ /* Setup the hdw queue if not already set */
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[rsp->hwqid];
+
+ if (phba->hdwqstat_on & LPFC_CHECK_NVMET_IO) {
+ id = raw_smp_processor_id();
+ this_cpu_inc(phba->sli4_hba.c_stat->xmt_io);
+ if (rsp->hwqid != id)
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6705 CPU Check OP: "
+ "cpu %d expect %d\n",
+ id, rsp->hwqid);
+ ctxp->cpu = id; /* Setup cpu for cmpl check */
+ }
+#endif
+
+ /* Sanity check */
+ if ((ctxp->flag & LPFC_NVME_ABTS_RCV) ||
+ (ctxp->state == LPFC_NVME_STE_ABORT)) {
+ atomic_inc(&lpfc_nvmep->xmt_fcp_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6102 IO oxid x%x aborted\n",
+ ctxp->oxid);
+ rc = -ENXIO;
+ goto aerr;
+ }
+
+ nvmewqeq = lpfc_nvmet_prep_fcp_wqe(phba, ctxp);
+ if (nvmewqeq == NULL) {
+ atomic_inc(&lpfc_nvmep->xmt_fcp_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6152 FCP Drop IO x%x: Prep\n",
+ ctxp->oxid);
+ rc = -ENXIO;
+ goto aerr;
+ }
+
+ nvmewqeq->cmd_cmpl = lpfc_nvmet_xmt_fcp_op_cmp;
+ nvmewqeq->context_un.axchg = ctxp;
+ nvmewqeq->cmd_flag |= LPFC_IO_NVMET;
+ ctxp->wqeq->hba_wqidx = rsp->hwqid;
+
+ lpfc_nvmeio_data(phba, "NVMET FCP CMND: xri x%x op x%x len x%x\n",
+ ctxp->oxid, rsp->op, rsp->rsplen);
+
+ ctxp->flag |= LPFC_NVME_IO_INP;
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq);
+ if (rc == WQE_SUCCESS) {
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (!ctxp->ts_cmd_nvme)
+ return 0;
+ if (rsp->op == NVMET_FCOP_RSP)
+ ctxp->ts_status_wqput = ktime_get_ns();
+ else
+ ctxp->ts_data_wqput = ktime_get_ns();
+#endif
+ return 0;
+ }
+
+ if (rc == -EBUSY) {
+ /*
+ * WQ was full, so queue nvmewqeq to be sent after
+ * WQE release CQE
+ */
+ ctxp->flag |= LPFC_NVME_DEFER_WQFULL;
+ wq = ctxp->hdwq->io_wq;
+ pring = wq->pring;
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ list_add_tail(&nvmewqeq->list, &wq->wqfull_list);
+ wq->q_flag |= HBA_NVMET_WQFULL;
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ atomic_inc(&lpfc_nvmep->defer_wqfull);
+ return 0;
+ }
+
+ /* Give back resources */
+ atomic_inc(&lpfc_nvmep->xmt_fcp_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6153 FCP Drop IO x%x: Issue: %d\n",
+ ctxp->oxid, rc);
+
+ ctxp->wqeq->hba_wqidx = 0;
+ nvmewqeq->context_un.axchg = NULL;
+ nvmewqeq->bpl_dmabuf = NULL;
+ rc = -EBUSY;
+aerr:
+ return rc;
+}
+
+static void
+lpfc_nvmet_targetport_delete(struct nvmet_fc_target_port *targetport)
+{
+ struct lpfc_nvmet_tgtport *tport = targetport->private;
+
+ /* release any threads waiting for the unreg to complete */
+ if (tport->phba->targetport)
+ complete(tport->tport_unreg_cmp);
+}
+
+static void
+lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
+ struct nvmefc_tgt_fcp_req *req)
+{
+ struct lpfc_nvmet_tgtport *lpfc_nvmep = tgtport->private;
+ struct lpfc_async_xchg_ctx *ctxp =
+ container_of(req, struct lpfc_async_xchg_ctx, hdlrctx.fcp_req);
+ struct lpfc_hba *phba = ctxp->phba;
+ struct lpfc_queue *wq;
+ unsigned long flags;
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return;
+
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[0];
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6103 NVMET Abort op: oxid x%x flg x%x ste %d\n",
+ ctxp->oxid, ctxp->flag, ctxp->state);
+
+ lpfc_nvmeio_data(phba, "NVMET FCP ABRT: xri x%x flg x%x ste x%x\n",
+ ctxp->oxid, ctxp->flag, ctxp->state);
+
+ atomic_inc(&lpfc_nvmep->xmt_fcp_abort);
+
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+
+ /* Since iaab/iaar are NOT set, we need to check
+ * if the firmware is in process of aborting IO
+ */
+ if (ctxp->flag & (LPFC_NVME_XBUSY | LPFC_NVME_ABORT_OP)) {
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ return;
+ }
+ ctxp->flag |= LPFC_NVME_ABORT_OP;
+
+ if (ctxp->flag & LPFC_NVME_DEFER_WQFULL) {
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
+ ctxp->oxid);
+ wq = ctxp->hdwq->io_wq;
+ lpfc_nvmet_wqfull_flush(phba, wq, ctxp);
+ return;
+ }
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+
+ /* A state of LPFC_NVME_STE_RCV means we have just received
+ * the NVME command and have not started processing it.
+ * (by issuing any IO WQEs on this exchange yet)
+ */
+ if (ctxp->state == LPFC_NVME_STE_RCV)
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
+ ctxp->oxid);
+ else
+ lpfc_nvmet_sol_fcp_issue_abort(phba, ctxp, ctxp->sid,
+ ctxp->oxid);
+}
+
+static void
+lpfc_nvmet_xmt_fcp_release(struct nvmet_fc_target_port *tgtport,
+ struct nvmefc_tgt_fcp_req *rsp)
+{
+ struct lpfc_nvmet_tgtport *lpfc_nvmep = tgtport->private;
+ struct lpfc_async_xchg_ctx *ctxp =
+ container_of(rsp, struct lpfc_async_xchg_ctx, hdlrctx.fcp_req);
+ struct lpfc_hba *phba = ctxp->phba;
+ unsigned long flags;
+ bool aborting = false;
+
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ if (ctxp->flag & LPFC_NVME_XBUSY)
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6027 NVMET release with XBUSY flag x%x"
+ " oxid x%x\n",
+ ctxp->flag, ctxp->oxid);
+ else if (ctxp->state != LPFC_NVME_STE_DONE &&
+ ctxp->state != LPFC_NVME_STE_ABORT)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6413 NVMET release bad state %d %d oxid x%x\n",
+ ctxp->state, ctxp->entry_cnt, ctxp->oxid);
+
+ if ((ctxp->flag & LPFC_NVME_ABORT_OP) ||
+ (ctxp->flag & LPFC_NVME_XBUSY)) {
+ aborting = true;
+ /* let the abort path do the real release */
+ lpfc_nvmet_defer_release(phba, ctxp);
+ }
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+
+ lpfc_nvmeio_data(phba, "NVMET FCP FREE: xri x%x ste %d abt %d\n", ctxp->oxid,
+ ctxp->state, aborting);
+
+ atomic_inc(&lpfc_nvmep->xmt_fcp_release);
+ ctxp->flag &= ~LPFC_NVME_TNOTIFY;
+
+ if (aborting)
+ return;
+
+ lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
+}
+
+static void
+lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport,
+ struct nvmefc_tgt_fcp_req *rsp)
+{
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct lpfc_async_xchg_ctx *ctxp =
+ container_of(rsp, struct lpfc_async_xchg_ctx, hdlrctx.fcp_req);
+ struct rqb_dmabuf *nvmebuf;
+ struct lpfc_hba *phba = ctxp->phba;
+ unsigned long iflag;
+
+
+ lpfc_nvmeio_data(phba, "NVMET DEFERRCV: xri x%x sz %d CPU %02x\n",
+ ctxp->oxid, ctxp->size, raw_smp_processor_id());
+
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ nvmebuf = ctxp->rqb_buffer;
+ if (!nvmebuf) {
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6425 Defer rcv: no buffer oxid x%x: "
+ "flg %x ste %x\n",
+ ctxp->oxid, ctxp->flag, ctxp->state);
+ return;
+ }
+ ctxp->rqb_buffer = NULL;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+
+ tgtp = phba->targetport->private;
+ if (tgtp)
+ atomic_inc(&tgtp->rcv_fcp_cmd_defer);
+
+ /* Free the nvmebuf since a new buffer already replaced it */
+ nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
+}
+
+/**
+ * lpfc_nvmet_ls_req_cmp - completion handler for a nvme ls request
+ * @phba: Pointer to HBA context object
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @rspwqe: Pointer to driver response WQE object.
+ *
+ * This function is the completion handler for NVME LS requests.
+ * The function updates any states and statistics, then calls the
+ * generic completion handler to finish completion of the request.
+ **/
+static void
+lpfc_nvmet_ls_req_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+ __lpfc_nvme_ls_req_cmp(phba, cmdwqe->vport, cmdwqe, wcqe);
+}
+
+/**
+ * lpfc_nvmet_ls_req - Issue an Link Service request
+ * @targetport: pointer to target instance registered with nvmet transport.
+ * @hosthandle: hosthandle set by the driver in a prior ls_rqst_rcv.
+ * Driver sets this value to the ndlp pointer.
+ * @pnvme_lsreq: the transport nvme_ls_req structure for the LS
+ *
+ * Driver registers this routine to handle any link service request
+ * from the nvme_fc transport to a remote nvme-aware port.
+ *
+ * Return value :
+ * 0 - Success
+ * non-zero: various error codes, in form of -Exxx
+ **/
+static int
+lpfc_nvmet_ls_req(struct nvmet_fc_target_port *targetport,
+ void *hosthandle,
+ struct nvmefc_ls_req *pnvme_lsreq)
+{
+ struct lpfc_nvmet_tgtport *lpfc_nvmet = targetport->private;
+ struct lpfc_hba *phba;
+ struct lpfc_nodelist *ndlp;
+ int ret;
+ u32 hstate;
+
+ if (!lpfc_nvmet)
+ return -EINVAL;
+
+ phba = lpfc_nvmet->phba;
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return -EINVAL;
+
+ hstate = atomic_read(&lpfc_nvmet->state);
+ if (hstate == LPFC_NVMET_INV_HOST_ACTIVE)
+ return -EACCES;
+
+ ndlp = (struct lpfc_nodelist *)hosthandle;
+
+ ret = __lpfc_nvme_ls_req(phba->pport, ndlp, pnvme_lsreq,
+ lpfc_nvmet_ls_req_cmp);
+
+ return ret;
+}
+
+/**
+ * lpfc_nvmet_ls_abort - Abort a prior NVME LS request
+ * @targetport: Transport targetport, that LS was issued from.
+ * @hosthandle: hosthandle set by the driver in a prior ls_rqst_rcv.
+ * Driver sets this value to the ndlp pointer.
+ * @pnvme_lsreq: the transport nvme_ls_req structure for LS to be aborted
+ *
+ * Driver registers this routine to abort an NVME LS request that is
+ * in progress (from the transports perspective).
+ **/
+static void
+lpfc_nvmet_ls_abort(struct nvmet_fc_target_port *targetport,
+ void *hosthandle,
+ struct nvmefc_ls_req *pnvme_lsreq)
+{
+ struct lpfc_nvmet_tgtport *lpfc_nvmet = targetport->private;
+ struct lpfc_hba *phba;
+ struct lpfc_nodelist *ndlp;
+ int ret;
+
+ phba = lpfc_nvmet->phba;
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return;
+
+ ndlp = (struct lpfc_nodelist *)hosthandle;
+
+ ret = __lpfc_nvme_ls_abort(phba->pport, ndlp, pnvme_lsreq);
+ if (!ret)
+ atomic_inc(&lpfc_nvmet->xmt_ls_abort);
+}
+
+static int
+lpfc_nvmet_host_traddr(void *hosthandle, u64 *wwnn, u64 *wwpn)
+{
+ struct lpfc_nodelist *ndlp = hosthandle;
+
+ *wwnn = wwn_to_u64(ndlp->nlp_nodename.u.wwn);
+ *wwpn = wwn_to_u64(ndlp->nlp_portname.u.wwn);
+ return 0;
+}
+
+static void
+lpfc_nvmet_host_release(void *hosthandle)
+{
+ struct lpfc_nodelist *ndlp = hosthandle;
+ struct lpfc_hba *phba = ndlp->phba;
+ struct lpfc_nvmet_tgtport *tgtp;
+
+ if (!phba->targetport || !phba->targetport->private)
+ return;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME,
+ "6202 NVMET XPT releasing hosthandle x%px "
+ "DID x%x xflags x%x refcnt %d\n",
+ hosthandle, ndlp->nlp_DID, ndlp->fc4_xpt_flags,
+ kref_read(&ndlp->kref));
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ spin_lock_irq(&ndlp->lock);
+ ndlp->fc4_xpt_flags &= ~NLP_XPT_HAS_HH;
+ spin_unlock_irq(&ndlp->lock);
+ lpfc_nlp_put(ndlp);
+ atomic_set(&tgtp->state, 0);
+}
+
+static void
+lpfc_nvmet_discovery_event(struct nvmet_fc_target_port *tgtport)
+{
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct lpfc_hba *phba;
+ uint32_t rc;
+
+ tgtp = tgtport->private;
+ phba = tgtp->phba;
+
+ rc = lpfc_issue_els_rscn(phba->pport, 0);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6420 NVMET subsystem change: Notification %s\n",
+ (rc) ? "Failed" : "Sent");
+}
+
+static struct nvmet_fc_target_template lpfc_tgttemplate = {
+ .targetport_delete = lpfc_nvmet_targetport_delete,
+ .xmt_ls_rsp = lpfc_nvmet_xmt_ls_rsp,
+ .fcp_op = lpfc_nvmet_xmt_fcp_op,
+ .fcp_abort = lpfc_nvmet_xmt_fcp_abort,
+ .fcp_req_release = lpfc_nvmet_xmt_fcp_release,
+ .defer_rcv = lpfc_nvmet_defer_rcv,
+ .discovery_event = lpfc_nvmet_discovery_event,
+ .ls_req = lpfc_nvmet_ls_req,
+ .ls_abort = lpfc_nvmet_ls_abort,
+ .host_release = lpfc_nvmet_host_release,
+ .host_traddr = lpfc_nvmet_host_traddr,
+
+ .max_hw_queues = 1,
+ .max_sgl_segments = LPFC_NVMET_DEFAULT_SEGS,
+ .max_dif_sgl_segments = LPFC_NVMET_DEFAULT_SEGS,
+ .dma_boundary = 0xFFFFFFFF,
+
+ /* optional features */
+ .target_features = 0,
+ /* sizes of additional private data for data structures */
+ .target_priv_sz = sizeof(struct lpfc_nvmet_tgtport),
+ .lsrqst_priv_sz = 0,
+};
+
+static void
+__lpfc_nvmet_clean_io_for_cpu(struct lpfc_hba *phba,
+ struct lpfc_nvmet_ctx_info *infop)
+{
+ struct lpfc_nvmet_ctxbuf *ctx_buf, *next_ctx_buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&infop->nvmet_ctx_list_lock, flags);
+ list_for_each_entry_safe(ctx_buf, next_ctx_buf,
+ &infop->nvmet_ctx_list, list) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_del_init(&ctx_buf->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+
+ spin_lock(&phba->hbalock);
+ __lpfc_clear_active_sglq(phba, ctx_buf->sglq->sli4_lxritag);
+ spin_unlock(&phba->hbalock);
+
+ ctx_buf->sglq->state = SGL_FREED;
+ ctx_buf->sglq->ndlp = NULL;
+
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ list_add_tail(&ctx_buf->sglq->list,
+ &phba->sli4_hba.lpfc_nvmet_sgl_list);
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
+
+ lpfc_sli_release_iocbq(phba, ctx_buf->iocbq);
+ kfree(ctx_buf->context);
+ }
+ spin_unlock_irqrestore(&infop->nvmet_ctx_list_lock, flags);
+}
+
+static void
+lpfc_nvmet_cleanup_io_context(struct lpfc_hba *phba)
+{
+ struct lpfc_nvmet_ctx_info *infop;
+ int i, j;
+
+ /* The first context list, MRQ 0 CPU 0 */
+ infop = phba->sli4_hba.nvmet_ctx_info;
+ if (!infop)
+ return;
+
+ /* Cycle the entire CPU context list for every MRQ */
+ for (i = 0; i < phba->cfg_nvmet_mrq; i++) {
+ for_each_present_cpu(j) {
+ infop = lpfc_get_ctx_list(phba, j, i);
+ __lpfc_nvmet_clean_io_for_cpu(phba, infop);
+ }
+ }
+ kfree(phba->sli4_hba.nvmet_ctx_info);
+ phba->sli4_hba.nvmet_ctx_info = NULL;
+}
+
+static int
+lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
+{
+ struct lpfc_nvmet_ctxbuf *ctx_buf;
+ struct lpfc_iocbq *nvmewqe;
+ union lpfc_wqe128 *wqe;
+ struct lpfc_nvmet_ctx_info *last_infop;
+ struct lpfc_nvmet_ctx_info *infop;
+ int i, j, idx, cpu;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME,
+ "6403 Allocate NVMET resources for %d XRIs\n",
+ phba->sli4_hba.nvmet_xri_cnt);
+
+ phba->sli4_hba.nvmet_ctx_info = kcalloc(
+ phba->sli4_hba.num_possible_cpu * phba->cfg_nvmet_mrq,
+ sizeof(struct lpfc_nvmet_ctx_info), GFP_KERNEL);
+ if (!phba->sli4_hba.nvmet_ctx_info) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6419 Failed allocate memory for "
+ "nvmet context lists\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Assuming X CPUs in the system, and Y MRQs, allocate some
+ * lpfc_nvmet_ctx_info structures as follows:
+ *
+ * cpu0/mrq0 cpu1/mrq0 ... cpuX/mrq0
+ * cpu0/mrq1 cpu1/mrq1 ... cpuX/mrq1
+ * ...
+ * cpuX/mrqY cpuX/mrqY ... cpuX/mrqY
+ *
+ * Each line represents a MRQ "silo" containing an entry for
+ * every CPU.
+ *
+ * MRQ X is initially assumed to be associated with CPU X, thus
+ * contexts are initially distributed across all MRQs using
+ * the MRQ index (N) as follows cpuN/mrqN. When contexts are
+ * freed, the are freed to the MRQ silo based on the CPU number
+ * of the IO completion. Thus a context that was allocated for MRQ A
+ * whose IO completed on CPU B will be freed to cpuB/mrqA.
+ */
+ for_each_possible_cpu(i) {
+ for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
+ infop = lpfc_get_ctx_list(phba, i, j);
+ INIT_LIST_HEAD(&infop->nvmet_ctx_list);
+ spin_lock_init(&infop->nvmet_ctx_list_lock);
+ infop->nvmet_ctx_list_cnt = 0;
+ }
+ }
+
+ /*
+ * Setup the next CPU context info ptr for each MRQ.
+ * MRQ 0 will cycle thru CPUs 0 - X separately from
+ * MRQ 1 cycling thru CPUs 0 - X, and so on.
+ */
+ for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
+ last_infop = lpfc_get_ctx_list(phba,
+ cpumask_first(cpu_present_mask),
+ j);
+ for (i = phba->sli4_hba.num_possible_cpu - 1; i >= 0; i--) {
+ infop = lpfc_get_ctx_list(phba, i, j);
+ infop->nvmet_ctx_next_cpu = last_infop;
+ last_infop = infop;
+ }
+ }
+
+ /* For all nvmet xris, allocate resources needed to process a
+ * received command on a per xri basis.
+ */
+ idx = 0;
+ cpu = cpumask_first(cpu_present_mask);
+ for (i = 0; i < phba->sli4_hba.nvmet_xri_cnt; i++) {
+ ctx_buf = kzalloc(sizeof(*ctx_buf), GFP_KERNEL);
+ if (!ctx_buf) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6404 Ran out of memory for NVMET\n");
+ return -ENOMEM;
+ }
+
+ ctx_buf->context = kzalloc(sizeof(*ctx_buf->context),
+ GFP_KERNEL);
+ if (!ctx_buf->context) {
+ kfree(ctx_buf);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6405 Ran out of NVMET "
+ "context memory\n");
+ return -ENOMEM;
+ }
+ ctx_buf->context->ctxbuf = ctx_buf;
+ ctx_buf->context->state = LPFC_NVME_STE_FREE;
+
+ ctx_buf->iocbq = lpfc_sli_get_iocbq(phba);
+ if (!ctx_buf->iocbq) {
+ kfree(ctx_buf->context);
+ kfree(ctx_buf);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6406 Ran out of NVMET iocb/WQEs\n");
+ return -ENOMEM;
+ }
+ ctx_buf->iocbq->cmd_flag = LPFC_IO_NVMET;
+ nvmewqe = ctx_buf->iocbq;
+ wqe = &nvmewqe->wqe;
+
+ /* Initialize WQE */
+ memset(wqe, 0, sizeof(*wqe));
+
+ ctx_buf->iocbq->cmd_dmabuf = NULL;
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ ctx_buf->sglq = __lpfc_sli_get_nvmet_sglq(phba, ctx_buf->iocbq);
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
+ if (!ctx_buf->sglq) {
+ lpfc_sli_release_iocbq(phba, ctx_buf->iocbq);
+ kfree(ctx_buf->context);
+ kfree(ctx_buf);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6407 Ran out of NVMET XRIs\n");
+ return -ENOMEM;
+ }
+ INIT_WORK(&ctx_buf->defer_work, lpfc_nvmet_fcp_rqst_defer_work);
+
+ /*
+ * Add ctx to MRQidx context list. Our initial assumption
+ * is MRQidx will be associated with CPUidx. This association
+ * can change on the fly.
+ */
+ infop = lpfc_get_ctx_list(phba, cpu, idx);
+ spin_lock(&infop->nvmet_ctx_list_lock);
+ list_add_tail(&ctx_buf->list, &infop->nvmet_ctx_list);
+ infop->nvmet_ctx_list_cnt++;
+ spin_unlock(&infop->nvmet_ctx_list_lock);
+
+ /* Spread ctx structures evenly across all MRQs */
+ idx++;
+ if (idx >= phba->cfg_nvmet_mrq) {
+ idx = 0;
+ cpu = cpumask_first(cpu_present_mask);
+ continue;
+ }
+ cpu = lpfc_next_present_cpu(cpu);
+ }
+
+ for_each_present_cpu(i) {
+ for (j = 0; j < phba->cfg_nvmet_mrq; j++) {
+ infop = lpfc_get_ctx_list(phba, i, j);
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_INIT,
+ "6408 TOTAL NVMET ctx for CPU %d "
+ "MRQ %d: cnt %d nextcpu x%px\n",
+ i, j, infop->nvmet_ctx_list_cnt,
+ infop->nvmet_ctx_next_cpu);
+ }
+ }
+ return 0;
+}
+
+int
+lpfc_nvmet_create_targetport(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport = phba->pport;
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct nvmet_fc_port_info pinfo;
+ int error;
+
+ if (phba->targetport)
+ return 0;
+
+ error = lpfc_nvmet_setup_io_context(phba);
+ if (error)
+ return error;
+
+ memset(&pinfo, 0, sizeof(struct nvmet_fc_port_info));
+ pinfo.node_name = wwn_to_u64(vport->fc_nodename.u.wwn);
+ pinfo.port_name = wwn_to_u64(vport->fc_portname.u.wwn);
+ pinfo.port_id = vport->fc_myDID;
+
+ /* We need to tell the transport layer + 1 because it takes page
+ * alignment into account. When space for the SGL is allocated we
+ * allocate + 3, one for cmd, one for rsp and one for this alignment
+ */
+ lpfc_tgttemplate.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1;
+ lpfc_tgttemplate.max_hw_queues = phba->cfg_hdw_queue;
+ lpfc_tgttemplate.target_features = NVMET_FCTGTFEAT_READDATA_RSP;
+
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ error = nvmet_fc_register_targetport(&pinfo, &lpfc_tgttemplate,
+ &phba->pcidev->dev,
+ &phba->targetport);
+#else
+ error = -ENOENT;
+#endif
+ if (error) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6025 Cannot register NVME targetport x%x: "
+ "portnm %llx nodenm %llx segs %d qs %d\n",
+ error,
+ pinfo.port_name, pinfo.node_name,
+ lpfc_tgttemplate.max_sgl_segments,
+ lpfc_tgttemplate.max_hw_queues);
+ phba->targetport = NULL;
+ phba->nvmet_support = 0;
+
+ lpfc_nvmet_cleanup_io_context(phba);
+
+ } else {
+ tgtp = (struct lpfc_nvmet_tgtport *)
+ phba->targetport->private;
+ tgtp->phba = phba;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6026 Registered NVME "
+ "targetport: x%px, private x%px "
+ "portnm %llx nodenm %llx segs %d qs %d\n",
+ phba->targetport, tgtp,
+ pinfo.port_name, pinfo.node_name,
+ lpfc_tgttemplate.max_sgl_segments,
+ lpfc_tgttemplate.max_hw_queues);
+
+ atomic_set(&tgtp->rcv_ls_req_in, 0);
+ atomic_set(&tgtp->rcv_ls_req_out, 0);
+ atomic_set(&tgtp->rcv_ls_req_drop, 0);
+ atomic_set(&tgtp->xmt_ls_abort, 0);
+ atomic_set(&tgtp->xmt_ls_abort_cmpl, 0);
+ atomic_set(&tgtp->xmt_ls_rsp, 0);
+ atomic_set(&tgtp->xmt_ls_drop, 0);
+ atomic_set(&tgtp->xmt_ls_rsp_error, 0);
+ atomic_set(&tgtp->xmt_ls_rsp_xb_set, 0);
+ atomic_set(&tgtp->xmt_ls_rsp_aborted, 0);
+ atomic_set(&tgtp->xmt_ls_rsp_cmpl, 0);
+ atomic_set(&tgtp->rcv_fcp_cmd_in, 0);
+ atomic_set(&tgtp->rcv_fcp_cmd_out, 0);
+ atomic_set(&tgtp->rcv_fcp_cmd_drop, 0);
+ atomic_set(&tgtp->xmt_fcp_drop, 0);
+ atomic_set(&tgtp->xmt_fcp_read_rsp, 0);
+ atomic_set(&tgtp->xmt_fcp_read, 0);
+ atomic_set(&tgtp->xmt_fcp_write, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp, 0);
+ atomic_set(&tgtp->xmt_fcp_release, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_cmpl, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_error, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_xb_set, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_aborted, 0);
+ atomic_set(&tgtp->xmt_fcp_rsp_drop, 0);
+ atomic_set(&tgtp->xmt_fcp_xri_abort_cqe, 0);
+ atomic_set(&tgtp->xmt_fcp_abort, 0);
+ atomic_set(&tgtp->xmt_fcp_abort_cmpl, 0);
+ atomic_set(&tgtp->xmt_abort_unsol, 0);
+ atomic_set(&tgtp->xmt_abort_sol, 0);
+ atomic_set(&tgtp->xmt_abort_rsp, 0);
+ atomic_set(&tgtp->xmt_abort_rsp_error, 0);
+ atomic_set(&tgtp->defer_ctx, 0);
+ atomic_set(&tgtp->defer_fod, 0);
+ atomic_set(&tgtp->defer_wqfull, 0);
+ }
+ return error;
+}
+
+int
+lpfc_nvmet_update_targetport(struct lpfc_hba *phba)
+{
+ struct lpfc_vport *vport = phba->pport;
+
+ if (!phba->targetport)
+ return 0;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME,
+ "6007 Update NVMET port x%px did x%x\n",
+ phba->targetport, vport->fc_myDID);
+
+ phba->targetport->port_id = vport->fc_myDID;
+ return 0;
+}
+
+/**
+ * lpfc_sli4_nvmet_xri_aborted - Fast-path process of nvmet xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @axri: pointer to the nvmet xri abort wcqe structure.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 fast-path
+ * NVMET aborted xri.
+ **/
+void
+lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
+ uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri);
+ struct lpfc_async_xchg_ctx *ctxp, *next_ctxp;
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct nvmefc_tgt_fcp_req *req = NULL;
+ struct lpfc_nodelist *ndlp;
+ unsigned long iflag = 0;
+ int rrq_empty = 0;
+ bool released = false;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6317 XB aborted xri x%x rxid x%x\n", xri, rxid);
+
+ if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
+ return;
+
+ if (phba->targetport) {
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ atomic_inc(&tgtp->xmt_fcp_xri_abort_cqe);
+ }
+
+ spin_lock_irqsave(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag);
+ list_for_each_entry_safe(ctxp, next_ctxp,
+ &phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
+ list) {
+ if (ctxp->ctxbuf->sglq->sli4_xritag != xri)
+ continue;
+
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock,
+ iflag);
+
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ /* Check if we already received a free context call
+ * and we have completed processing an abort situation.
+ */
+ if (ctxp->flag & LPFC_NVME_CTX_RLS &&
+ !(ctxp->flag & LPFC_NVME_ABORT_OP)) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_del_init(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ released = true;
+ }
+ ctxp->flag &= ~LPFC_NVME_XBUSY;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+
+ spin_lock_irqsave(&phba->rrq_list_lock, iflag);
+ rrq_empty = list_empty(&phba->active_rrq_list);
+ spin_unlock_irqrestore(&phba->rrq_list_lock, iflag);
+ ndlp = lpfc_findnode_did(phba->pport, ctxp->sid);
+ if (ndlp &&
+ (ndlp->nlp_state == NLP_STE_UNMAPPED_NODE ||
+ ndlp->nlp_state == NLP_STE_MAPPED_NODE)) {
+ lpfc_set_rrq_active(phba, ndlp,
+ ctxp->ctxbuf->sglq->sli4_lxritag,
+ rxid, 1);
+ lpfc_sli4_abts_err_handler(phba, ndlp, axri);
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6318 XB aborted oxid x%x flg x%x (%x)\n",
+ ctxp->oxid, ctxp->flag, released);
+ if (released)
+ lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
+
+ if (rrq_empty)
+ lpfc_worker_wake_up(phba);
+ return;
+ }
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag);
+ ctxp = lpfc_nvmet_get_ctx_for_xri(phba, xri);
+ if (ctxp) {
+ /*
+ * Abort already done by FW, so BA_ACC sent.
+ * However, the transport may be unaware.
+ */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6323 NVMET Rcv ABTS xri x%x ctxp state x%x "
+ "flag x%x oxid x%x rxid x%x\n",
+ xri, ctxp->state, ctxp->flag, ctxp->oxid,
+ rxid);
+
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ ctxp->flag |= LPFC_NVME_ABTS_RCV;
+ ctxp->state = LPFC_NVME_STE_ABORT;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+
+ lpfc_nvmeio_data(phba,
+ "NVMET ABTS RCV: xri x%x CPU %02x rjt %d\n",
+ xri, raw_smp_processor_id(), 0);
+
+ req = &ctxp->hdlrctx.fcp_req;
+ if (req)
+ nvmet_fc_rcv_fcp_abort(phba->targetport, req);
+ }
+#endif
+}
+
+int
+lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
+ struct fc_frame_header *fc_hdr)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_async_xchg_ctx *ctxp, *next_ctxp;
+ struct nvmefc_tgt_fcp_req *rsp;
+ uint32_t sid;
+ uint16_t oxid, xri;
+ unsigned long iflag = 0;
+
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
+ oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+
+ spin_lock_irqsave(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag);
+ list_for_each_entry_safe(ctxp, next_ctxp,
+ &phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
+ list) {
+ if (ctxp->oxid != oxid || ctxp->sid != sid)
+ continue;
+
+ xri = ctxp->ctxbuf->sglq->sli4_xritag;
+
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock,
+ iflag);
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ ctxp->flag |= LPFC_NVME_ABTS_RCV;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+
+ lpfc_nvmeio_data(phba,
+ "NVMET ABTS RCV: xri x%x CPU %02x rjt %d\n",
+ xri, raw_smp_processor_id(), 0);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6319 NVMET Rcv ABTS:acc xri x%x\n", xri);
+
+ rsp = &ctxp->hdlrctx.fcp_req;
+ nvmet_fc_rcv_fcp_abort(phba->targetport, rsp);
+
+ /* Respond with BA_ACC accordingly */
+ lpfc_sli4_seq_abort_rsp(vport, fc_hdr, 1);
+ return 0;
+ }
+ spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag);
+ /* check the wait list */
+ if (phba->sli4_hba.nvmet_io_wait_cnt) {
+ struct rqb_dmabuf *nvmebuf;
+ struct fc_frame_header *fc_hdr_tmp;
+ u32 sid_tmp;
+ u16 oxid_tmp;
+ bool found = false;
+
+ spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
+
+ /* match by oxid and s_id */
+ list_for_each_entry(nvmebuf,
+ &phba->sli4_hba.lpfc_nvmet_io_wait_list,
+ hbuf.list) {
+ fc_hdr_tmp = (struct fc_frame_header *)
+ (nvmebuf->hbuf.virt);
+ oxid_tmp = be16_to_cpu(fc_hdr_tmp->fh_ox_id);
+ sid_tmp = sli4_sid_from_fc_hdr(fc_hdr_tmp);
+ if (oxid_tmp != oxid || sid_tmp != sid)
+ continue;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6321 NVMET Rcv ABTS oxid x%x from x%x "
+ "is waiting for a ctxp\n",
+ oxid, sid);
+
+ list_del_init(&nvmebuf->hbuf.list);
+ phba->sli4_hba.nvmet_io_wait_cnt--;
+ found = true;
+ break;
+ }
+ spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock,
+ iflag);
+
+ /* free buffer since already posted a new DMA buffer to RQ */
+ if (found) {
+ nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
+ /* Respond with BA_ACC accordingly */
+ lpfc_sli4_seq_abort_rsp(vport, fc_hdr, 1);
+ return 0;
+ }
+ }
+
+ /* check active list */
+ ctxp = lpfc_nvmet_get_ctx_for_oxid(phba, oxid, sid);
+ if (ctxp) {
+ xri = ctxp->ctxbuf->sglq->sli4_xritag;
+
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ ctxp->flag |= (LPFC_NVME_ABTS_RCV | LPFC_NVME_ABORT_OP);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+
+ lpfc_nvmeio_data(phba,
+ "NVMET ABTS RCV: xri x%x CPU %02x rjt %d\n",
+ xri, raw_smp_processor_id(), 0);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6322 NVMET Rcv ABTS:acc oxid x%x xri x%x "
+ "flag x%x state x%x\n",
+ ctxp->oxid, xri, ctxp->flag, ctxp->state);
+
+ if (ctxp->flag & LPFC_NVME_TNOTIFY) {
+ /* Notify the transport */
+ nvmet_fc_rcv_fcp_abort(phba->targetport,
+ &ctxp->hdlrctx.fcp_req);
+ } else {
+ cancel_work_sync(&ctxp->ctxbuf->defer_work);
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ }
+ lpfc_nvmet_sol_fcp_issue_abort(phba, ctxp, ctxp->sid,
+ ctxp->oxid);
+
+ lpfc_sli4_seq_abort_rsp(vport, fc_hdr, 1);
+ return 0;
+ }
+
+ lpfc_nvmeio_data(phba, "NVMET ABTS RCV: oxid x%x CPU %02x rjt %d\n",
+ oxid, raw_smp_processor_id(), 1);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6320 NVMET Rcv ABTS:rjt oxid x%x\n", oxid);
+
+ /* Respond with BA_RJT accordingly */
+ lpfc_sli4_seq_abort_rsp(vport, fc_hdr, 0);
+#endif
+ return 0;
+}
+
+static void
+lpfc_nvmet_wqfull_flush(struct lpfc_hba *phba, struct lpfc_queue *wq,
+ struct lpfc_async_xchg_ctx *ctxp)
+{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *nvmewqeq;
+ struct lpfc_iocbq *next_nvmewqeq;
+ unsigned long iflags;
+ struct lpfc_wcqe_complete wcqe;
+ struct lpfc_wcqe_complete *wcqep;
+
+ pring = wq->pring;
+ wcqep = &wcqe;
+
+ /* Fake an ABORT error code back to cmpl routine */
+ memset(wcqep, 0, sizeof(struct lpfc_wcqe_complete));
+ bf_set(lpfc_wcqe_c_status, wcqep, IOSTAT_LOCAL_REJECT);
+ wcqep->parameter = IOERR_ABORT_REQUESTED;
+
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ list_for_each_entry_safe(nvmewqeq, next_nvmewqeq,
+ &wq->wqfull_list, list) {
+ if (ctxp) {
+ /* Checking for a specific IO to flush */
+ if (nvmewqeq->context_un.axchg == ctxp) {
+ list_del(&nvmewqeq->list);
+ spin_unlock_irqrestore(&pring->ring_lock,
+ iflags);
+ memcpy(&nvmewqeq->wcqe_cmpl, wcqep,
+ sizeof(*wcqep));
+ lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq,
+ nvmewqeq);
+ return;
+ }
+ continue;
+ } else {
+ /* Flush all IOs */
+ list_del(&nvmewqeq->list);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ memcpy(&nvmewqeq->wcqe_cmpl, wcqep, sizeof(*wcqep));
+ lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq, nvmewqeq);
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ }
+ }
+ if (!ctxp)
+ wq->q_flag &= ~HBA_NVMET_WQFULL;
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+}
+
+void
+lpfc_nvmet_wqfull_process(struct lpfc_hba *phba,
+ struct lpfc_queue *wq)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *nvmewqeq;
+ struct lpfc_async_xchg_ctx *ctxp;
+ unsigned long iflags;
+ int rc;
+
+ /*
+ * Some WQE slots are available, so try to re-issue anything
+ * on the WQ wqfull_list.
+ */
+ pring = wq->pring;
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ while (!list_empty(&wq->wqfull_list)) {
+ list_remove_head(&wq->wqfull_list, nvmewqeq, struct lpfc_iocbq,
+ list);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ ctxp = nvmewqeq->context_un.axchg;
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq);
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ if (rc == -EBUSY) {
+ /* WQ was full again, so put it back on the list */
+ list_add(&nvmewqeq->list, &wq->wqfull_list);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ return;
+ }
+ if (rc == WQE_SUCCESS) {
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (ctxp->ts_cmd_nvme) {
+ if (ctxp->hdlrctx.fcp_req.op == NVMET_FCOP_RSP)
+ ctxp->ts_status_wqput = ktime_get_ns();
+ else
+ ctxp->ts_data_wqput = ktime_get_ns();
+ }
+#endif
+ } else {
+ WARN_ON(rc);
+ }
+ }
+ wq->q_flag &= ~HBA_NVMET_WQFULL;
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+#endif
+}
+
+void
+lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct lpfc_queue *wq;
+ uint32_t qidx;
+ DECLARE_COMPLETION_ONSTACK(tport_unreg_cmp);
+
+ if (phba->nvmet_support == 0)
+ return;
+ if (phba->targetport) {
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ wq = phba->sli4_hba.hdwq[qidx].io_wq;
+ lpfc_nvmet_wqfull_flush(phba, wq, NULL);
+ }
+ tgtp->tport_unreg_cmp = &tport_unreg_cmp;
+ nvmet_fc_unregister_targetport(phba->targetport);
+ if (!wait_for_completion_timeout(&tport_unreg_cmp,
+ msecs_to_jiffies(LPFC_NVMET_WAIT_TMO)))
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6179 Unreg targetport x%px timeout "
+ "reached.\n", phba->targetport);
+ lpfc_nvmet_cleanup_io_context(phba);
+ }
+ phba->targetport = NULL;
+#endif
+}
+
+/**
+ * lpfc_nvmet_handle_lsreq - Process an NVME LS request
+ * @phba: pointer to lpfc hba data structure.
+ * @axchg: pointer to exchange context for the NVME LS request
+ *
+ * This routine is used for processing an asynchronously received NVME LS
+ * request. Any remaining validation is done and the LS is then forwarded
+ * to the nvmet-fc transport via nvmet_fc_rcv_ls_req().
+ *
+ * The calling sequence should be: nvmet_fc_rcv_ls_req() -> (processing)
+ * -> lpfc_nvmet_xmt_ls_rsp/cmp -> req->done.
+ * lpfc_nvme_xmt_ls_rsp_cmp should free the allocated axchg.
+ *
+ * Returns 0 if LS was handled and delivered to the transport
+ * Returns 1 if LS failed to be handled and should be dropped
+ */
+int
+lpfc_nvmet_handle_lsreq(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *axchg)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_nvmet_tgtport *tgtp = phba->targetport->private;
+ uint32_t *payload = axchg->payload;
+ int rc;
+
+ atomic_inc(&tgtp->rcv_ls_req_in);
+
+ /*
+ * Driver passes the ndlp as the hosthandle argument allowing
+ * the transport to generate LS requests for any associateions
+ * that are created.
+ */
+ rc = nvmet_fc_rcv_ls_req(phba->targetport, axchg->ndlp, &axchg->ls_rsp,
+ axchg->payload, axchg->size);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6037 NVMET Unsol rcv: sz %d rc %d: %08x %08x %08x "
+ "%08x %08x %08x\n", axchg->size, rc,
+ *payload, *(payload+1), *(payload+2),
+ *(payload+3), *(payload+4), *(payload+5));
+
+ if (!rc) {
+ atomic_inc(&tgtp->rcv_ls_req_out);
+ return 0;
+ }
+
+ atomic_inc(&tgtp->rcv_ls_req_drop);
+#endif
+ return 1;
+}
+
+static void
+lpfc_nvmet_process_rcv_fcp_req(struct lpfc_nvmet_ctxbuf *ctx_buf)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_async_xchg_ctx *ctxp = ctx_buf->context;
+ struct lpfc_hba *phba = ctxp->phba;
+ struct rqb_dmabuf *nvmebuf = ctxp->rqb_buffer;
+ struct lpfc_nvmet_tgtport *tgtp;
+ uint32_t *payload, qno;
+ uint32_t rc;
+ unsigned long iflags;
+
+ if (!nvmebuf) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6159 process_rcv_fcp_req, nvmebuf is NULL, "
+ "oxid: x%x flg: x%x state: x%x\n",
+ ctxp->oxid, ctxp->flag, ctxp->state);
+ spin_lock_irqsave(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
+ ctxp->oxid);
+ return;
+ }
+
+ if (ctxp->flag & LPFC_NVME_ABTS_RCV) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6324 IO oxid x%x aborted\n",
+ ctxp->oxid);
+ return;
+ }
+
+ payload = (uint32_t *)(nvmebuf->dbuf.virt);
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ ctxp->flag |= LPFC_NVME_TNOTIFY;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (ctxp->ts_isr_cmd)
+ ctxp->ts_cmd_nvme = ktime_get_ns();
+#endif
+ /*
+ * The calling sequence should be:
+ * nvmet_fc_rcv_fcp_req->lpfc_nvmet_xmt_fcp_op/cmp- req->done
+ * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp.
+ * When we return from nvmet_fc_rcv_fcp_req, all relevant info
+ * the NVME command / FC header is stored.
+ * A buffer has already been reposted for this IO, so just free
+ * the nvmebuf.
+ */
+ rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->hdlrctx.fcp_req,
+ payload, ctxp->size);
+ /* Process FCP command */
+ if (rc == 0) {
+ atomic_inc(&tgtp->rcv_fcp_cmd_out);
+ spin_lock_irqsave(&ctxp->ctxlock, iflags);
+ if ((ctxp->flag & LPFC_NVME_CTX_REUSE_WQ) ||
+ (nvmebuf != ctxp->rqb_buffer)) {
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ return;
+ }
+ ctxp->rqb_buffer = NULL;
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */
+ return;
+ }
+
+ /* Processing of FCP command is deferred */
+ if (rc == -EOVERFLOW) {
+ lpfc_nvmeio_data(phba, "NVMET RCV BUSY: xri x%x sz %d "
+ "from %06x\n",
+ ctxp->oxid, ctxp->size, ctxp->sid);
+ atomic_inc(&tgtp->rcv_fcp_cmd_out);
+ atomic_inc(&tgtp->defer_fod);
+ spin_lock_irqsave(&ctxp->ctxlock, iflags);
+ if (ctxp->flag & LPFC_NVME_CTX_REUSE_WQ) {
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ return;
+ }
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ /*
+ * Post a replacement DMA buffer to RQ and defer
+ * freeing rcv buffer till .defer_rcv callback
+ */
+ qno = nvmebuf->idx;
+ lpfc_post_rq_buffer(
+ phba, phba->sli4_hba.nvmet_mrq_hdr[qno],
+ phba->sli4_hba.nvmet_mrq_data[qno], 1, qno);
+ return;
+ }
+ ctxp->flag &= ~LPFC_NVME_TNOTIFY;
+ atomic_inc(&tgtp->rcv_fcp_cmd_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2582 FCP Drop IO x%x: err x%x: x%x x%x x%x\n",
+ ctxp->oxid, rc,
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out),
+ atomic_read(&tgtp->xmt_fcp_release));
+ lpfc_nvmeio_data(phba, "NVMET FCP DROP: xri x%x sz %d from %06x\n",
+ ctxp->oxid, ctxp->size, ctxp->sid);
+ spin_lock_irqsave(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflags);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid);
+#endif
+}
+
+static void
+lpfc_nvmet_fcp_rqst_defer_work(struct work_struct *work)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_nvmet_ctxbuf *ctx_buf =
+ container_of(work, struct lpfc_nvmet_ctxbuf, defer_work);
+
+ lpfc_nvmet_process_rcv_fcp_req(ctx_buf);
+#endif
+}
+
+static struct lpfc_nvmet_ctxbuf *
+lpfc_nvmet_replenish_context(struct lpfc_hba *phba,
+ struct lpfc_nvmet_ctx_info *current_infop)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_nvmet_ctxbuf *ctx_buf = NULL;
+ struct lpfc_nvmet_ctx_info *get_infop;
+ int i;
+
+ /*
+ * The current_infop for the MRQ a NVME command IU was received
+ * on is empty. Our goal is to replenish this MRQs context
+ * list from a another CPUs.
+ *
+ * First we need to pick a context list to start looking on.
+ * nvmet_ctx_start_cpu has available context the last time
+ * we needed to replenish this CPU where nvmet_ctx_next_cpu
+ * is just the next sequential CPU for this MRQ.
+ */
+ if (current_infop->nvmet_ctx_start_cpu)
+ get_infop = current_infop->nvmet_ctx_start_cpu;
+ else
+ get_infop = current_infop->nvmet_ctx_next_cpu;
+
+ for (i = 0; i < phba->sli4_hba.num_possible_cpu; i++) {
+ if (get_infop == current_infop) {
+ get_infop = get_infop->nvmet_ctx_next_cpu;
+ continue;
+ }
+ spin_lock(&get_infop->nvmet_ctx_list_lock);
+
+ /* Just take the entire context list, if there are any */
+ if (get_infop->nvmet_ctx_list_cnt) {
+ list_splice_init(&get_infop->nvmet_ctx_list,
+ &current_infop->nvmet_ctx_list);
+ current_infop->nvmet_ctx_list_cnt =
+ get_infop->nvmet_ctx_list_cnt - 1;
+ get_infop->nvmet_ctx_list_cnt = 0;
+ spin_unlock(&get_infop->nvmet_ctx_list_lock);
+
+ current_infop->nvmet_ctx_start_cpu = get_infop;
+ list_remove_head(&current_infop->nvmet_ctx_list,
+ ctx_buf, struct lpfc_nvmet_ctxbuf,
+ list);
+ return ctx_buf;
+ }
+
+ /* Otherwise, move on to the next CPU for this MRQ */
+ spin_unlock(&get_infop->nvmet_ctx_list_lock);
+ get_infop = get_infop->nvmet_ctx_next_cpu;
+ }
+
+#endif
+ /* Nothing found, all contexts for the MRQ are in-flight */
+ return NULL;
+}
+
+/**
+ * lpfc_nvmet_unsol_fcp_buffer - Process an unsolicited event data buffer
+ * @phba: pointer to lpfc hba data structure.
+ * @idx: relative index of MRQ vector
+ * @nvmebuf: pointer to lpfc nvme command HBQ data structure.
+ * @isr_timestamp: in jiffies.
+ * @cqflag: cq processing information regarding workload.
+ *
+ * This routine is used for processing the WQE associated with a unsolicited
+ * event. It first determines whether there is an existing ndlp that matches
+ * the DID from the unsolicited WQE. If not, it will create a new one with
+ * the DID from the unsolicited WQE. The ELS command from the unsolicited
+ * WQE is then used to invoke the proper routine and to set up proper state
+ * of the discovery state machine.
+ **/
+static void
+lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
+ uint32_t idx,
+ struct rqb_dmabuf *nvmebuf,
+ uint64_t isr_timestamp,
+ uint8_t cqflag)
+{
+ struct lpfc_async_xchg_ctx *ctxp;
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct fc_frame_header *fc_hdr;
+ struct lpfc_nvmet_ctxbuf *ctx_buf;
+ struct lpfc_nvmet_ctx_info *current_infop;
+ uint32_t size, oxid, sid, qno;
+ unsigned long iflag;
+ int current_cpu;
+
+ if (!IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ return;
+
+ ctx_buf = NULL;
+ if (!nvmebuf || !phba->targetport) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6157 NVMET FCP Drop IO\n");
+ if (nvmebuf)
+ lpfc_rq_buf_free(phba, &nvmebuf->hbuf);
+ return;
+ }
+
+ /*
+ * Get a pointer to the context list for this MRQ based on
+ * the CPU this MRQ IRQ is associated with. If the CPU association
+ * changes from our initial assumption, the context list could
+ * be empty, thus it would need to be replenished with the
+ * context list from another CPU for this MRQ.
+ */
+ current_cpu = raw_smp_processor_id();
+ current_infop = lpfc_get_ctx_list(phba, current_cpu, idx);
+ spin_lock_irqsave(&current_infop->nvmet_ctx_list_lock, iflag);
+ if (current_infop->nvmet_ctx_list_cnt) {
+ list_remove_head(&current_infop->nvmet_ctx_list,
+ ctx_buf, struct lpfc_nvmet_ctxbuf, list);
+ current_infop->nvmet_ctx_list_cnt--;
+ } else {
+ ctx_buf = lpfc_nvmet_replenish_context(phba, current_infop);
+ }
+ spin_unlock_irqrestore(&current_infop->nvmet_ctx_list_lock, iflag);
+
+ fc_hdr = (struct fc_frame_header *)(nvmebuf->hbuf.virt);
+ oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+ size = nvmebuf->bytes_recv;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (phba->hdwqstat_on & LPFC_CHECK_NVMET_IO) {
+ this_cpu_inc(phba->sli4_hba.c_stat->rcv_io);
+ if (idx != current_cpu)
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6703 CPU Check rcv: "
+ "cpu %d expect %d\n",
+ current_cpu, idx);
+ }
+#endif
+
+ lpfc_nvmeio_data(phba, "NVMET FCP RCV: xri x%x sz %d CPU %02x\n",
+ oxid, size, raw_smp_processor_id());
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+
+ if (!ctx_buf) {
+ /* Queue this NVME IO to process later */
+ spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
+ list_add_tail(&nvmebuf->hbuf.list,
+ &phba->sli4_hba.lpfc_nvmet_io_wait_list);
+ phba->sli4_hba.nvmet_io_wait_cnt++;
+ phba->sli4_hba.nvmet_io_wait_total++;
+ spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock,
+ iflag);
+
+ /* Post a brand new DMA buffer to RQ */
+ qno = nvmebuf->idx;
+ lpfc_post_rq_buffer(
+ phba, phba->sli4_hba.nvmet_mrq_hdr[qno],
+ phba->sli4_hba.nvmet_mrq_data[qno], 1, qno);
+
+ atomic_inc(&tgtp->defer_ctx);
+ return;
+ }
+
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
+
+ ctxp = (struct lpfc_async_xchg_ctx *)ctx_buf->context;
+ spin_lock_irqsave(&phba->sli4_hba.t_active_list_lock, iflag);
+ list_add_tail(&ctxp->list, &phba->sli4_hba.t_active_ctx_list);
+ spin_unlock_irqrestore(&phba->sli4_hba.t_active_list_lock, iflag);
+ if (ctxp->state != LPFC_NVME_STE_FREE) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6414 NVMET Context corrupt %d %d oxid x%x\n",
+ ctxp->state, ctxp->entry_cnt, ctxp->oxid);
+ }
+ ctxp->wqeq = NULL;
+ ctxp->offset = 0;
+ ctxp->phba = phba;
+ ctxp->size = size;
+ ctxp->oxid = oxid;
+ ctxp->sid = sid;
+ ctxp->idx = idx;
+ ctxp->state = LPFC_NVME_STE_RCV;
+ ctxp->entry_cnt = 1;
+ ctxp->flag = 0;
+ ctxp->ctxbuf = ctx_buf;
+ ctxp->rqb_buffer = (void *)nvmebuf;
+ ctxp->hdwq = NULL;
+ spin_lock_init(&ctxp->ctxlock);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (isr_timestamp)
+ ctxp->ts_isr_cmd = isr_timestamp;
+ ctxp->ts_cmd_nvme = 0;
+ ctxp->ts_nvme_data = 0;
+ ctxp->ts_data_wqput = 0;
+ ctxp->ts_isr_data = 0;
+ ctxp->ts_data_nvme = 0;
+ ctxp->ts_nvme_status = 0;
+ ctxp->ts_status_wqput = 0;
+ ctxp->ts_isr_status = 0;
+ ctxp->ts_status_nvme = 0;
+#endif
+
+ atomic_inc(&tgtp->rcv_fcp_cmd_in);
+ /* check for cq processing load */
+ if (!cqflag) {
+ lpfc_nvmet_process_rcv_fcp_req(ctx_buf);
+ return;
+ }
+
+ if (!queue_work(phba->wq, &ctx_buf->defer_work)) {
+ atomic_inc(&tgtp->rcv_fcp_cmd_drop);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6325 Unable to queue work for oxid x%x. "
+ "FCP Drop IO [x%x x%x x%x]\n",
+ ctxp->oxid,
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out),
+ atomic_read(&tgtp->xmt_fcp_release));
+
+ spin_lock_irqsave(&ctxp->ctxlock, iflag);
+ lpfc_nvmet_defer_release(phba, ctxp);
+ spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid);
+ }
+}
+
+/**
+ * lpfc_nvmet_unsol_fcp_event - Process an unsolicited event from an nvme nport
+ * @phba: pointer to lpfc hba data structure.
+ * @idx: relative index of MRQ vector
+ * @nvmebuf: pointer to received nvme data structure.
+ * @isr_timestamp: in jiffies.
+ * @cqflag: cq processing information regarding workload.
+ *
+ * This routine is used to process an unsolicited event received from a SLI
+ * (Service Level Interface) ring. The actual processing of the data buffer
+ * associated with the unsolicited event is done by invoking the routine
+ * lpfc_nvmet_unsol_fcp_buffer() after properly set up the buffer from the
+ * SLI RQ on which the unsolicited event was received.
+ **/
+void
+lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba,
+ uint32_t idx,
+ struct rqb_dmabuf *nvmebuf,
+ uint64_t isr_timestamp,
+ uint8_t cqflag)
+{
+ if (!nvmebuf) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3167 NVMET FCP Drop IO\n");
+ return;
+ }
+ if (phba->nvmet_support == 0) {
+ lpfc_rq_buf_free(phba, &nvmebuf->hbuf);
+ return;
+ }
+ lpfc_nvmet_unsol_fcp_buffer(phba, idx, nvmebuf, isr_timestamp, cqflag);
+}
+
+/**
+ * lpfc_nvmet_prep_ls_wqe - Allocate and prepare a lpfc wqe data structure
+ * @phba: pointer to a host N_Port data structure.
+ * @ctxp: Context info for NVME LS Request
+ * @rspbuf: DMA buffer of NVME command.
+ * @rspsize: size of the NVME command.
+ *
+ * This routine is used for allocating a lpfc-WQE data structure from
+ * the driver lpfc-WQE free-list and prepare the WQE with the parameters
+ * passed into the routine for discovery state machine to issue an Extended
+ * Link Service (NVME) commands. It is a generic lpfc-WQE allocation
+ * and preparation routine that is used by all the discovery state machine
+ * routines and the NVME command-specific fields will be later set up by
+ * the individual discovery machine routines after calling this routine
+ * allocating and preparing a generic WQE data structure. It fills in the
+ * Buffer Descriptor Entries (BDEs), allocates buffers for both command
+ * payload and response payload (if expected). The reference count on the
+ * ndlp is incremented by 1 and the reference to the ndlp is put into
+ * context1 of the WQE data structure for this WQE to hold the ndlp
+ * reference for the command's callback function to access later.
+ *
+ * Return code
+ * Pointer to the newly allocated/prepared nvme wqe data structure
+ * NULL - when nvme wqe data structure allocation/preparation failed
+ **/
+static struct lpfc_iocbq *
+lpfc_nvmet_prep_ls_wqe(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp,
+ dma_addr_t rspbuf, uint16_t rspsize)
+{
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_iocbq *nvmewqe;
+ union lpfc_wqe128 *wqe;
+
+ if (!lpfc_is_link_up(phba)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6104 NVMET prep LS wqe: link err: "
+ "NPORT x%x oxid:x%x ste %d\n",
+ ctxp->sid, ctxp->oxid, ctxp->state);
+ return NULL;
+ }
+
+ /* Allocate buffer for command wqe */
+ nvmewqe = lpfc_sli_get_iocbq(phba);
+ if (nvmewqe == NULL) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6105 NVMET prep LS wqe: No WQE: "
+ "NPORT x%x oxid x%x ste %d\n",
+ ctxp->sid, ctxp->oxid, ctxp->state);
+ return NULL;
+ }
+
+ ndlp = lpfc_findnode_did(phba->pport, ctxp->sid);
+ if (!ndlp ||
+ ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
+ (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6106 NVMET prep LS wqe: No ndlp: "
+ "NPORT x%x oxid x%x ste %d\n",
+ ctxp->sid, ctxp->oxid, ctxp->state);
+ goto nvme_wqe_free_wqeq_exit;
+ }
+ ctxp->wqeq = nvmewqe;
+
+ /* prevent preparing wqe with NULL ndlp reference */
+ nvmewqe->ndlp = lpfc_nlp_get(ndlp);
+ if (!nvmewqe->ndlp)
+ goto nvme_wqe_free_wqeq_exit;
+ nvmewqe->context_un.axchg = ctxp;
+
+ wqe = &nvmewqe->wqe;
+ memset(wqe, 0, sizeof(union lpfc_wqe));
+
+ /* Words 0 - 2 */
+ wqe->xmit_sequence.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ wqe->xmit_sequence.bde.tus.f.bdeSize = rspsize;
+ wqe->xmit_sequence.bde.addrLow = le32_to_cpu(putPaddrLow(rspbuf));
+ wqe->xmit_sequence.bde.addrHigh = le32_to_cpu(putPaddrHigh(rspbuf));
+
+ /* Word 3 */
+
+ /* Word 4 */
+
+ /* Word 5 */
+ bf_set(wqe_dfctl, &wqe->xmit_sequence.wge_ctl, 0);
+ bf_set(wqe_ls, &wqe->xmit_sequence.wge_ctl, 1);
+ bf_set(wqe_la, &wqe->xmit_sequence.wge_ctl, 0);
+ bf_set(wqe_rctl, &wqe->xmit_sequence.wge_ctl, FC_RCTL_ELS4_REP);
+ bf_set(wqe_type, &wqe->xmit_sequence.wge_ctl, FC_TYPE_NVME);
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->xmit_sequence.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe->xmit_sequence.wqe_com, nvmewqe->sli4_xritag);
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->xmit_sequence.wqe_com,
+ CMD_XMIT_SEQUENCE64_WQE);
+ bf_set(wqe_ct, &wqe->xmit_sequence.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_class, &wqe->xmit_sequence.wqe_com, CLASS3);
+ bf_set(wqe_pu, &wqe->xmit_sequence.wqe_com, 0);
+
+ /* Word 8 */
+ wqe->xmit_sequence.wqe_com.abort_tag = nvmewqe->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->xmit_sequence.wqe_com, nvmewqe->iotag);
+ /* Needs to be set by caller */
+ bf_set(wqe_rcvoxid, &wqe->xmit_sequence.wqe_com, ctxp->oxid);
+
+ /* Word 10 */
+ bf_set(wqe_dbde, &wqe->xmit_sequence.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->xmit_sequence.wqe_com, LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_lenloc, &wqe->xmit_sequence.wqe_com,
+ LPFC_WQE_LENLOC_WORD12);
+ bf_set(wqe_ebde_cnt, &wqe->xmit_sequence.wqe_com, 0);
+
+ /* Word 11 */
+ bf_set(wqe_cqid, &wqe->xmit_sequence.wqe_com,
+ LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_cmd_type, &wqe->xmit_sequence.wqe_com,
+ OTHER_COMMAND);
+
+ /* Word 12 */
+ wqe->xmit_sequence.xmit_len = rspsize;
+
+ nvmewqe->retry = 1;
+ nvmewqe->vport = phba->pport;
+ nvmewqe->drvrTimeout = (phba->fc_ratov * 3) + LPFC_DRVR_TIMEOUT;
+ nvmewqe->cmd_flag |= LPFC_IO_NVME_LS;
+
+ /* Xmit NVMET response to remote NPORT <did> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC,
+ "6039 Xmit NVMET LS response to remote "
+ "NPORT x%x iotag:x%x oxid:x%x size:x%x\n",
+ ndlp->nlp_DID, nvmewqe->iotag, ctxp->oxid,
+ rspsize);
+ return nvmewqe;
+
+nvme_wqe_free_wqeq_exit:
+ nvmewqe->context_un.axchg = NULL;
+ nvmewqe->ndlp = NULL;
+ nvmewqe->bpl_dmabuf = NULL;
+ lpfc_sli_release_iocbq(phba, nvmewqe);
+ return NULL;
+}
+
+
+static struct lpfc_iocbq *
+lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp)
+{
+ struct nvmefc_tgt_fcp_req *rsp = &ctxp->hdlrctx.fcp_req;
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct sli4_sge *sgl;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_iocbq *nvmewqe;
+ struct scatterlist *sgel;
+ union lpfc_wqe128 *wqe;
+ struct ulp_bde64 *bde;
+ dma_addr_t physaddr;
+ int i, cnt, nsegs;
+ bool use_pbde = false;
+ int xc = 1;
+
+ if (!lpfc_is_link_up(phba)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6107 NVMET prep FCP wqe: link err:"
+ "NPORT x%x oxid x%x ste %d\n",
+ ctxp->sid, ctxp->oxid, ctxp->state);
+ return NULL;
+ }
+
+ ndlp = lpfc_findnode_did(phba->pport, ctxp->sid);
+ if (!ndlp ||
+ ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
+ (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6108 NVMET prep FCP wqe: no ndlp: "
+ "NPORT x%x oxid x%x ste %d\n",
+ ctxp->sid, ctxp->oxid, ctxp->state);
+ return NULL;
+ }
+
+ if (rsp->sg_cnt > lpfc_tgttemplate.max_sgl_segments) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6109 NVMET prep FCP wqe: seg cnt err: "
+ "NPORT x%x oxid x%x ste %d cnt %d\n",
+ ctxp->sid, ctxp->oxid, ctxp->state,
+ phba->cfg_nvme_seg_cnt);
+ return NULL;
+ }
+ nsegs = rsp->sg_cnt;
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ nvmewqe = ctxp->wqeq;
+ if (nvmewqe == NULL) {
+ /* Allocate buffer for command wqe */
+ nvmewqe = ctxp->ctxbuf->iocbq;
+ if (nvmewqe == NULL) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6110 NVMET prep FCP wqe: No "
+ "WQE: NPORT x%x oxid x%x ste %d\n",
+ ctxp->sid, ctxp->oxid, ctxp->state);
+ return NULL;
+ }
+ ctxp->wqeq = nvmewqe;
+ xc = 0; /* create new XRI */
+ nvmewqe->sli4_lxritag = NO_XRI;
+ nvmewqe->sli4_xritag = NO_XRI;
+ }
+
+ /* Sanity check */
+ if (((ctxp->state == LPFC_NVME_STE_RCV) &&
+ (ctxp->entry_cnt == 1)) ||
+ (ctxp->state == LPFC_NVME_STE_DATA)) {
+ wqe = &nvmewqe->wqe;
+ } else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6111 Wrong state NVMET FCP: %d cnt %d\n",
+ ctxp->state, ctxp->entry_cnt);
+ return NULL;
+ }
+
+ sgl = (struct sli4_sge *)ctxp->ctxbuf->sglq->sgl;
+ switch (rsp->op) {
+ case NVMET_FCOP_READDATA:
+ case NVMET_FCOP_READDATA_RSP:
+ /* From the tsend template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_tsend_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
+ /* Words 0 - 2 : The first sg segment */
+ sgel = &rsp->sg[0];
+ physaddr = sg_dma_address(sgel);
+ wqe->fcp_tsend.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ wqe->fcp_tsend.bde.tus.f.bdeSize = sg_dma_len(sgel);
+ wqe->fcp_tsend.bde.addrLow = cpu_to_le32(putPaddrLow(physaddr));
+ wqe->fcp_tsend.bde.addrHigh =
+ cpu_to_le32(putPaddrHigh(physaddr));
+
+ /* Word 3 */
+ wqe->fcp_tsend.payload_offset_len = 0;
+
+ /* Word 4 */
+ wqe->fcp_tsend.relative_offset = ctxp->offset;
+
+ /* Word 5 */
+ wqe->fcp_tsend.reserved = 0;
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->fcp_tsend.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe->fcp_tsend.wqe_com,
+ nvmewqe->sli4_xritag);
+
+ /* Word 7 - set ar later */
+
+ /* Word 8 */
+ wqe->fcp_tsend.wqe_com.abort_tag = nvmewqe->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->fcp_tsend.wqe_com, nvmewqe->iotag);
+ bf_set(wqe_rcvoxid, &wqe->fcp_tsend.wqe_com, ctxp->oxid);
+
+ /* Word 10 - set wqes later, in template xc=1 */
+ if (!xc)
+ bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, 0);
+
+ /* Word 12 */
+ wqe->fcp_tsend.fcp_data_len = rsp->transfer_length;
+
+ /* Setup 2 SKIP SGEs */
+ sgl->addr_hi = 0;
+ sgl->addr_lo = 0;
+ sgl->word2 = 0;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = 0;
+ sgl++;
+ sgl->addr_hi = 0;
+ sgl->addr_lo = 0;
+ sgl->word2 = 0;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = 0;
+ sgl++;
+ if (rsp->op == NVMET_FCOP_READDATA_RSP) {
+ atomic_inc(&tgtp->xmt_fcp_read_rsp);
+
+ /* In template ar=1 wqes=0 sup=0 irsp=0 irsplen=0 */
+
+ if (rsp->rsplen == LPFC_NVMET_SUCCESS_LEN) {
+ if (test_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag))
+ bf_set(wqe_sup,
+ &wqe->fcp_tsend.wqe_com, 1);
+ } else {
+ bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 1);
+ bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 1);
+ bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com,
+ ((rsp->rsplen >> 2) - 1));
+ memcpy(&wqe->words[16], rsp->rspaddr,
+ rsp->rsplen);
+ }
+ } else {
+ atomic_inc(&tgtp->xmt_fcp_read);
+
+ /* In template ar=1 wqes=0 sup=0 irsp=0 irsplen=0 */
+ bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 0);
+ }
+ break;
+
+ case NVMET_FCOP_WRITEDATA:
+ /* From the treceive template, initialize words 3 - 11 */
+ memcpy(&wqe->words[3],
+ &lpfc_treceive_cmd_template.words[3],
+ sizeof(uint32_t) * 9);
+
+ /* Words 0 - 2 : First SGE is skipped, set invalid BDE type */
+ wqe->fcp_treceive.bde.tus.f.bdeFlags = LPFC_SGE_TYPE_SKIP;
+ wqe->fcp_treceive.bde.tus.f.bdeSize = 0;
+ wqe->fcp_treceive.bde.addrLow = 0;
+ wqe->fcp_treceive.bde.addrHigh = 0;
+
+ /* Word 4 */
+ wqe->fcp_treceive.relative_offset = ctxp->offset;
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->fcp_treceive.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe->fcp_treceive.wqe_com,
+ nvmewqe->sli4_xritag);
+
+ /* Word 7 */
+
+ /* Word 8 */
+ wqe->fcp_treceive.wqe_com.abort_tag = nvmewqe->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->fcp_treceive.wqe_com, nvmewqe->iotag);
+ bf_set(wqe_rcvoxid, &wqe->fcp_treceive.wqe_com, ctxp->oxid);
+
+ /* Word 10 - in template xc=1 */
+ if (!xc)
+ bf_set(wqe_xc, &wqe->fcp_treceive.wqe_com, 0);
+
+ /* Word 11 - check for pbde */
+ if (nsegs == 1 && phba->cfg_enable_pbde) {
+ use_pbde = true;
+ /* Word 11 - PBDE bit already preset by template */
+ } else {
+ /* Overwrite default template setting */
+ bf_set(wqe_pbde, &wqe->fcp_treceive.wqe_com, 0);
+ }
+
+ /* Word 12 */
+ wqe->fcp_tsend.fcp_data_len = rsp->transfer_length;
+
+ /* Setup 2 SKIP SGEs */
+ sgl->addr_hi = 0;
+ sgl->addr_lo = 0;
+ sgl->word2 = 0;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = 0;
+ sgl++;
+ sgl->addr_hi = 0;
+ sgl->addr_lo = 0;
+ sgl->word2 = 0;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = 0;
+ sgl++;
+ atomic_inc(&tgtp->xmt_fcp_write);
+ break;
+
+ case NVMET_FCOP_RSP:
+ /* From the treceive template, initialize words 4 - 11 */
+ memcpy(&wqe->words[4],
+ &lpfc_trsp_cmd_template.words[4],
+ sizeof(uint32_t) * 8);
+
+ /* Words 0 - 2 */
+ physaddr = rsp->rspdma;
+ wqe->fcp_trsp.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ wqe->fcp_trsp.bde.tus.f.bdeSize = rsp->rsplen;
+ wqe->fcp_trsp.bde.addrLow =
+ cpu_to_le32(putPaddrLow(physaddr));
+ wqe->fcp_trsp.bde.addrHigh =
+ cpu_to_le32(putPaddrHigh(physaddr));
+
+ /* Word 3 */
+ wqe->fcp_trsp.response_len = rsp->rsplen;
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->fcp_trsp.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe->fcp_trsp.wqe_com,
+ nvmewqe->sli4_xritag);
+
+ /* Word 7 */
+
+ /* Word 8 */
+ wqe->fcp_trsp.wqe_com.abort_tag = nvmewqe->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->fcp_trsp.wqe_com, nvmewqe->iotag);
+ bf_set(wqe_rcvoxid, &wqe->fcp_trsp.wqe_com, ctxp->oxid);
+
+ /* Word 10 */
+ if (xc)
+ bf_set(wqe_xc, &wqe->fcp_trsp.wqe_com, 1);
+
+ /* Word 11 */
+ /* In template wqes=0 irsp=0 irsplen=0 - good response */
+ if (rsp->rsplen != LPFC_NVMET_SUCCESS_LEN) {
+ /* Bad response - embed it */
+ bf_set(wqe_wqes, &wqe->fcp_trsp.wqe_com, 1);
+ bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 1);
+ bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com,
+ ((rsp->rsplen >> 2) - 1));
+ memcpy(&wqe->words[16], rsp->rspaddr, rsp->rsplen);
+ }
+
+ /* Word 12 */
+ wqe->fcp_trsp.rsvd_12_15[0] = 0;
+
+ /* Use rspbuf, NOT sg list */
+ nsegs = 0;
+ sgl->word2 = 0;
+ atomic_inc(&tgtp->xmt_fcp_rsp);
+ break;
+
+ default:
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR,
+ "6064 Unknown Rsp Op %d\n",
+ rsp->op);
+ return NULL;
+ }
+
+ nvmewqe->retry = 1;
+ nvmewqe->vport = phba->pport;
+ nvmewqe->drvrTimeout = (phba->fc_ratov * 3) + LPFC_DRVR_TIMEOUT;
+ nvmewqe->ndlp = ndlp;
+
+ for_each_sg(rsp->sg, sgel, nsegs, i) {
+ physaddr = sg_dma_address(sgel);
+ cnt = sg_dma_len(sgel);
+ sgl->addr_hi = putPaddrHigh(physaddr);
+ sgl->addr_lo = putPaddrLow(physaddr);
+ sgl->word2 = 0;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA);
+ bf_set(lpfc_sli4_sge_offset, sgl, ctxp->offset);
+ if ((i+1) == rsp->sg_cnt)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(cnt);
+ sgl++;
+ ctxp->offset += cnt;
+ }
+
+ bde = (struct ulp_bde64 *)&wqe->words[13];
+ if (use_pbde) {
+ /* decrement sgl ptr backwards once to first data sge */
+ sgl--;
+
+ /* Words 13-15 (PBDE) */
+ bde->addrLow = sgl->addr_lo;
+ bde->addrHigh = sgl->addr_hi;
+ bde->tus.f.bdeSize = le32_to_cpu(sgl->sge_len);
+ bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bde->tus.w = cpu_to_le32(bde->tus.w);
+ } else {
+ memset(bde, 0, sizeof(struct ulp_bde64));
+ }
+ ctxp->state = LPFC_NVME_STE_DATA;
+ ctxp->entry_cnt++;
+ return nvmewqe;
+}
+
+/**
+ * lpfc_nvmet_sol_fcp_abort_cmp - Completion handler for ABTS
+ * @phba: Pointer to HBA context object.
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @rspwqe: Pointer to driver response WQE object.
+ *
+ * The function is called from SLI ring event handler with no
+ * lock held. This function is the completion handler for NVME ABTS for FCP cmds
+ * The function frees memory resources used for the NVME commands.
+ **/
+static void
+lpfc_nvmet_sol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_async_xchg_ctx *ctxp;
+ struct lpfc_nvmet_tgtport *tgtp;
+ uint32_t result;
+ unsigned long flags;
+ bool released = false;
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+
+ ctxp = cmdwqe->context_un.axchg;
+ result = wcqe->parameter;
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ if (ctxp->flag & LPFC_NVME_ABORT_OP)
+ atomic_inc(&tgtp->xmt_fcp_abort_cmpl);
+
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ ctxp->state = LPFC_NVME_STE_DONE;
+
+ /* Check if we already received a free context call
+ * and we have completed processing an abort situation.
+ */
+ if ((ctxp->flag & LPFC_NVME_CTX_RLS) &&
+ !(ctxp->flag & LPFC_NVME_XBUSY)) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_del_init(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ released = true;
+ }
+ ctxp->flag &= ~LPFC_NVME_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ atomic_inc(&tgtp->xmt_abort_rsp);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6165 ABORT cmpl: oxid x%x flg x%x (%d) "
+ "WCQE: %08x %08x %08x %08x\n",
+ ctxp->oxid, ctxp->flag, released,
+ wcqe->word0, wcqe->total_data_placed,
+ result, wcqe->word3);
+
+ cmdwqe->rsp_dmabuf = NULL;
+ cmdwqe->bpl_dmabuf = NULL;
+ /*
+ * if transport has released ctx, then can reuse it. Otherwise,
+ * will be recycled by transport release call.
+ */
+ if (released)
+ lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
+
+ /* This is the iocbq for the abort, not the command */
+ lpfc_sli_release_iocbq(phba, cmdwqe);
+
+ /* Since iaab/iaar are NOT set, there is no work left.
+ * For LPFC_NVME_XBUSY, lpfc_sli4_nvmet_xri_aborted
+ * should have been called already.
+ */
+}
+
+/**
+ * lpfc_nvmet_unsol_fcp_abort_cmp - Completion handler for ABTS
+ * @phba: Pointer to HBA context object.
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @rspwqe: Pointer to driver response WQE object.
+ *
+ * The function is called from SLI ring event handler with no
+ * lock held. This function is the completion handler for NVME ABTS for FCP cmds
+ * The function frees memory resources used for the NVME commands.
+ **/
+static void
+lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_async_xchg_ctx *ctxp;
+ struct lpfc_nvmet_tgtport *tgtp;
+ unsigned long flags;
+ uint32_t result;
+ bool released = false;
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+
+ ctxp = cmdwqe->context_un.axchg;
+ result = wcqe->parameter;
+
+ if (!ctxp) {
+ /* if context is clear, related io alrady complete */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6070 ABTS cmpl: WCQE: %08x %08x %08x %08x\n",
+ wcqe->word0, wcqe->total_data_placed,
+ result, wcqe->word3);
+ return;
+ }
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ if (ctxp->flag & LPFC_NVME_ABORT_OP)
+ atomic_inc(&tgtp->xmt_fcp_abort_cmpl);
+
+ /* Sanity check */
+ if (ctxp->state != LPFC_NVME_STE_ABORT) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6112 ABTS Wrong state:%d oxid x%x\n",
+ ctxp->state, ctxp->oxid);
+ }
+
+ /* Check if we already received a free context call
+ * and we have completed processing an abort situation.
+ */
+ ctxp->state = LPFC_NVME_STE_DONE;
+ if ((ctxp->flag & LPFC_NVME_CTX_RLS) &&
+ !(ctxp->flag & LPFC_NVME_XBUSY)) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_del_init(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ released = true;
+ }
+ ctxp->flag &= ~LPFC_NVME_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ atomic_inc(&tgtp->xmt_abort_rsp);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6316 ABTS cmpl oxid x%x flg x%x (%x) "
+ "WCQE: %08x %08x %08x %08x\n",
+ ctxp->oxid, ctxp->flag, released,
+ wcqe->word0, wcqe->total_data_placed,
+ result, wcqe->word3);
+
+ cmdwqe->rsp_dmabuf = NULL;
+ cmdwqe->bpl_dmabuf = NULL;
+ /*
+ * if transport has released ctx, then can reuse it. Otherwise,
+ * will be recycled by transport release call.
+ */
+ if (released)
+ lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
+
+ /* Since iaab/iaar are NOT set, there is no work left.
+ * For LPFC_NVME_XBUSY, lpfc_sli4_nvmet_xri_aborted
+ * should have been called already.
+ */
+}
+
+/**
+ * lpfc_nvmet_xmt_ls_abort_cmp - Completion handler for ABTS
+ * @phba: Pointer to HBA context object.
+ * @cmdwqe: Pointer to driver command WQE object.
+ * @rspwqe: Pointer to driver response WQE object.
+ *
+ * The function is called from SLI ring event handler with no
+ * lock held. This function is the completion handler for NVME ABTS for LS cmds
+ * The function frees memory resources used for the NVME commands.
+ **/
+static void
+lpfc_nvmet_xmt_ls_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
+ struct lpfc_iocbq *rspwqe)
+{
+ struct lpfc_async_xchg_ctx *ctxp;
+ struct lpfc_nvmet_tgtport *tgtp;
+ uint32_t result;
+ struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl;
+
+ ctxp = cmdwqe->context_un.axchg;
+ result = wcqe->parameter;
+
+ if (phba->nvmet_support) {
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ atomic_inc(&tgtp->xmt_ls_abort_cmpl);
+ }
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6083 Abort cmpl: ctx x%px WCQE:%08x %08x %08x %08x\n",
+ ctxp, wcqe->word0, wcqe->total_data_placed,
+ result, wcqe->word3);
+
+ if (!ctxp) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6415 NVMET LS Abort No ctx: WCQE: "
+ "%08x %08x %08x %08x\n",
+ wcqe->word0, wcqe->total_data_placed,
+ result, wcqe->word3);
+
+ lpfc_sli_release_iocbq(phba, cmdwqe);
+ return;
+ }
+
+ if (ctxp->state != LPFC_NVME_STE_LS_ABORT) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6416 NVMET LS abort cmpl state mismatch: "
+ "oxid x%x: %d %d\n",
+ ctxp->oxid, ctxp->state, ctxp->entry_cnt);
+ }
+
+ cmdwqe->rsp_dmabuf = NULL;
+ cmdwqe->bpl_dmabuf = NULL;
+ lpfc_sli_release_iocbq(phba, cmdwqe);
+ kfree(ctxp);
+}
+
+static int
+lpfc_nvmet_unsol_issue_abort(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp,
+ uint32_t sid, uint16_t xri)
+{
+ struct lpfc_nvmet_tgtport *tgtp = NULL;
+ struct lpfc_iocbq *abts_wqeq;
+ union lpfc_wqe128 *wqe_abts;
+ struct lpfc_nodelist *ndlp;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6067 ABTS: sid %x xri x%x/x%x\n",
+ sid, xri, ctxp->wqeq->sli4_xritag);
+
+ if (phba->nvmet_support && phba->targetport)
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+
+ ndlp = lpfc_findnode_did(phba->pport, sid);
+ if (!ndlp ||
+ ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
+ (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) {
+ if (tgtp)
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6134 Drop ABTS - wrong NDLP state x%x.\n",
+ (ndlp) ? ndlp->nlp_state : NLP_STE_MAX_STATE);
+
+ /* No failure to an ABTS request. */
+ return 0;
+ }
+
+ abts_wqeq = ctxp->wqeq;
+ wqe_abts = &abts_wqeq->wqe;
+
+ /*
+ * Since we zero the whole WQE, we need to ensure we set the WQE fields
+ * that were initialized in lpfc_sli4_nvmet_alloc.
+ */
+ memset(wqe_abts, 0, sizeof(union lpfc_wqe));
+
+ /* Word 5 */
+ bf_set(wqe_dfctl, &wqe_abts->xmit_sequence.wge_ctl, 0);
+ bf_set(wqe_ls, &wqe_abts->xmit_sequence.wge_ctl, 1);
+ bf_set(wqe_la, &wqe_abts->xmit_sequence.wge_ctl, 0);
+ bf_set(wqe_rctl, &wqe_abts->xmit_sequence.wge_ctl, FC_RCTL_BA_ABTS);
+ bf_set(wqe_type, &wqe_abts->xmit_sequence.wge_ctl, FC_TYPE_BLS);
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe_abts->xmit_sequence.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe_abts->xmit_sequence.wqe_com,
+ abts_wqeq->sli4_xritag);
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe_abts->xmit_sequence.wqe_com,
+ CMD_XMIT_SEQUENCE64_WQE);
+ bf_set(wqe_ct, &wqe_abts->xmit_sequence.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_class, &wqe_abts->xmit_sequence.wqe_com, CLASS3);
+ bf_set(wqe_pu, &wqe_abts->xmit_sequence.wqe_com, 0);
+
+ /* Word 8 */
+ wqe_abts->xmit_sequence.wqe_com.abort_tag = abts_wqeq->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe_abts->xmit_sequence.wqe_com, abts_wqeq->iotag);
+ /* Needs to be set by caller */
+ bf_set(wqe_rcvoxid, &wqe_abts->xmit_sequence.wqe_com, xri);
+
+ /* Word 10 */
+ bf_set(wqe_iod, &wqe_abts->xmit_sequence.wqe_com, LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_lenloc, &wqe_abts->xmit_sequence.wqe_com,
+ LPFC_WQE_LENLOC_WORD12);
+ bf_set(wqe_ebde_cnt, &wqe_abts->xmit_sequence.wqe_com, 0);
+ bf_set(wqe_qosd, &wqe_abts->xmit_sequence.wqe_com, 0);
+
+ /* Word 11 */
+ bf_set(wqe_cqid, &wqe_abts->xmit_sequence.wqe_com,
+ LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_cmd_type, &wqe_abts->xmit_sequence.wqe_com,
+ OTHER_COMMAND);
+
+ abts_wqeq->vport = phba->pport;
+ abts_wqeq->ndlp = ndlp;
+ abts_wqeq->context_un.axchg = ctxp;
+ abts_wqeq->bpl_dmabuf = NULL;
+ abts_wqeq->num_bdes = 0;
+ /* hba_wqidx should already be setup from command we are aborting */
+ abts_wqeq->iocb.ulpCommand = CMD_XMIT_SEQUENCE64_CR;
+ abts_wqeq->iocb.ulpLe = 1;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6069 Issue ABTS to xri x%x reqtag x%x\n",
+ xri, abts_wqeq->iotag);
+ return 1;
+}
+
+static int
+lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp,
+ uint32_t sid, uint16_t xri)
+{
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct lpfc_iocbq *abts_wqeq;
+ struct lpfc_nodelist *ndlp;
+ unsigned long flags;
+ bool ia;
+ int rc;
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ if (!ctxp->wqeq) {
+ ctxp->wqeq = ctxp->ctxbuf->iocbq;
+ ctxp->wqeq->hba_wqidx = 0;
+ }
+
+ ndlp = lpfc_findnode_did(phba->pport, sid);
+ if (!ndlp ||
+ ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
+ (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) {
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6160 Drop ABORT - wrong NDLP state x%x.\n",
+ (ndlp) ? ndlp->nlp_state : NLP_STE_MAX_STATE);
+
+ /* No failure to an ABTS request. */
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ ctxp->flag &= ~LPFC_NVME_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ return 0;
+ }
+
+ /* Issue ABTS for this WQE based on iotag */
+ ctxp->abort_wqeq = lpfc_sli_get_iocbq(phba);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ if (!ctxp->abort_wqeq) {
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6161 ABORT failed: No wqeqs: "
+ "xri: x%x\n", ctxp->oxid);
+ /* No failure to an ABTS request. */
+ ctxp->flag &= ~LPFC_NVME_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ return 0;
+ }
+ abts_wqeq = ctxp->abort_wqeq;
+ ctxp->state = LPFC_NVME_STE_ABORT;
+ ia = (ctxp->flag & LPFC_NVME_ABTS_RCV) ? true : false;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+
+ /* Announce entry to new IO submit field. */
+ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
+ "6162 ABORT Request to rport DID x%06x "
+ "for xri x%x x%x\n",
+ ctxp->sid, ctxp->oxid, ctxp->wqeq->sli4_xritag);
+
+ /* If the hba is getting reset, this flag is set. It is
+ * cleared when the reset is complete and rings reestablished.
+ */
+ /* driver queued commands are in process of being flushed */
+ if (test_bit(HBA_IOQ_FLUSH, &phba->hba_flag)) {
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6163 Driver in reset cleanup - flushing "
+ "NVME Req now. hba_flag x%lx oxid x%x\n",
+ phba->hba_flag, ctxp->oxid);
+ lpfc_sli_release_iocbq(phba, abts_wqeq);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ ctxp->flag &= ~LPFC_NVME_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ return 0;
+ }
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+ /* Outstanding abort is in progress */
+ if (abts_wqeq->cmd_flag & LPFC_DRIVER_ABORTED) {
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6164 Outstanding NVME I/O Abort Request "
+ "still pending on oxid x%x\n",
+ ctxp->oxid);
+ lpfc_sli_release_iocbq(phba, abts_wqeq);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ ctxp->flag &= ~LPFC_NVME_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ return 0;
+ }
+
+ /* Ready - mark outstanding as aborted by driver. */
+ abts_wqeq->cmd_flag |= LPFC_DRIVER_ABORTED;
+
+ lpfc_sli_prep_abort_xri(phba, abts_wqeq, ctxp->wqeq->sli4_xritag,
+ abts_wqeq->iotag, CLASS3,
+ LPFC_WQE_CQ_ID_DEFAULT, ia, true);
+
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abts_wqeq->hba_wqidx = ctxp->wqeq->hba_wqidx;
+ abts_wqeq->cmd_cmpl = lpfc_nvmet_sol_fcp_abort_cmp;
+ abts_wqeq->cmd_flag |= LPFC_IO_NVME;
+ abts_wqeq->context_un.axchg = ctxp;
+ abts_wqeq->vport = phba->pport;
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[abts_wqeq->hba_wqidx];
+
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (rc == WQE_SUCCESS) {
+ atomic_inc(&tgtp->xmt_abort_sol);
+ return 0;
+ }
+
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ ctxp->flag &= ~LPFC_NVME_ABORT_OP;
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ lpfc_sli_release_iocbq(phba, abts_wqeq);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6166 Failed ABORT issue_wqe with status x%x "
+ "for oxid x%x.\n",
+ rc, ctxp->oxid);
+ return 1;
+}
+
+static int
+lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp,
+ uint32_t sid, uint16_t xri)
+{
+ struct lpfc_nvmet_tgtport *tgtp;
+ struct lpfc_iocbq *abts_wqeq;
+ unsigned long flags;
+ bool released = false;
+ int rc;
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ if (!ctxp->wqeq) {
+ ctxp->wqeq = ctxp->ctxbuf->iocbq;
+ ctxp->wqeq->hba_wqidx = 0;
+ }
+
+ if (ctxp->state == LPFC_NVME_STE_FREE) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6417 NVMET ABORT ctx freed %d %d oxid x%x\n",
+ ctxp->state, ctxp->entry_cnt, ctxp->oxid);
+ rc = WQE_BUSY;
+ goto aerr;
+ }
+ ctxp->state = LPFC_NVME_STE_ABORT;
+ ctxp->entry_cnt++;
+ rc = lpfc_nvmet_unsol_issue_abort(phba, ctxp, sid, xri);
+ if (rc == 0)
+ goto aerr;
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+ abts_wqeq = ctxp->wqeq;
+ abts_wqeq->cmd_cmpl = lpfc_nvmet_unsol_fcp_abort_cmp;
+ abts_wqeq->cmd_flag |= LPFC_IO_NVMET;
+ if (!ctxp->hdwq)
+ ctxp->hdwq = &phba->sli4_hba.hdwq[abts_wqeq->hba_wqidx];
+
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (rc == WQE_SUCCESS) {
+ return 0;
+ }
+
+aerr:
+ spin_lock_irqsave(&ctxp->ctxlock, flags);
+ if (ctxp->flag & LPFC_NVME_CTX_RLS) {
+ spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ list_del_init(&ctxp->list);
+ spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock);
+ released = true;
+ }
+ ctxp->flag &= ~(LPFC_NVME_ABORT_OP | LPFC_NVME_CTX_RLS);
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6135 Failed to Issue ABTS for oxid x%x. Status x%x "
+ "(%x)\n",
+ ctxp->oxid, rc, released);
+ if (released)
+ lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
+ return 1;
+}
+
+/**
+ * lpfc_nvme_unsol_ls_issue_abort - issue ABTS on an exchange received
+ * via async frame receive where the frame is not handled.
+ * @phba: pointer to adapter structure
+ * @ctxp: pointer to the asynchronously received received sequence
+ * @sid: address of the remote port to send the ABTS to
+ * @xri: oxid value to for the ABTS (other side's exchange id).
+ **/
+int
+lpfc_nvme_unsol_ls_issue_abort(struct lpfc_hba *phba,
+ struct lpfc_async_xchg_ctx *ctxp,
+ uint32_t sid, uint16_t xri)
+{
+ struct lpfc_nvmet_tgtport *tgtp = NULL;
+ struct lpfc_iocbq *abts_wqeq;
+ unsigned long flags;
+ int rc;
+
+ if ((ctxp->state == LPFC_NVME_STE_LS_RCV && ctxp->entry_cnt == 1) ||
+ (ctxp->state == LPFC_NVME_STE_LS_RSP && ctxp->entry_cnt == 2)) {
+ ctxp->state = LPFC_NVME_STE_LS_ABORT;
+ ctxp->entry_cnt++;
+ } else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6418 NVMET LS abort state mismatch "
+ "IO x%x: %d %d\n",
+ ctxp->oxid, ctxp->state, ctxp->entry_cnt);
+ ctxp->state = LPFC_NVME_STE_LS_ABORT;
+ }
+
+ if (phba->nvmet_support && phba->targetport)
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+
+ if (!ctxp->wqeq) {
+ /* Issue ABTS for this WQE based on iotag */
+ ctxp->wqeq = lpfc_sli_get_iocbq(phba);
+ if (!ctxp->wqeq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6068 Abort failed: No wqeqs: "
+ "xri: x%x\n", xri);
+ /* No failure to an ABTS request. */
+ kfree(ctxp);
+ return 0;
+ }
+ }
+ abts_wqeq = ctxp->wqeq;
+
+ if (lpfc_nvmet_unsol_issue_abort(phba, ctxp, sid, xri) == 0) {
+ rc = WQE_BUSY;
+ goto out;
+ }
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+ abts_wqeq->cmd_cmpl = lpfc_nvmet_xmt_ls_abort_cmp;
+ abts_wqeq->cmd_flag |= LPFC_IO_NVME_LS;
+ rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (rc == WQE_SUCCESS) {
+ if (tgtp)
+ atomic_inc(&tgtp->xmt_abort_unsol);
+ return 0;
+ }
+out:
+ if (tgtp)
+ atomic_inc(&tgtp->xmt_abort_rsp_error);
+ abts_wqeq->rsp_dmabuf = NULL;
+ abts_wqeq->bpl_dmabuf = NULL;
+ lpfc_sli_release_iocbq(phba, abts_wqeq);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6056 Failed to Issue ABTS. Status x%x\n", rc);
+ return 1;
+}
+
+/**
+ * lpfc_nvmet_invalidate_host
+ *
+ * @phba: pointer to the driver instance bound to an adapter port.
+ * @ndlp: pointer to an lpfc_nodelist type
+ *
+ * This routine upcalls the nvmet transport to invalidate an NVME
+ * host to which this target instance had active connections.
+ */
+void
+lpfc_nvmet_invalidate_host(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+{
+ u32 ndlp_has_hh;
+ struct lpfc_nvmet_tgtport *tgtp;
+
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_NVME | LOG_NVME_ABTS | LOG_NVME_DISC,
+ "6203 Invalidating hosthandle x%px\n",
+ ndlp);
+
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ atomic_set(&tgtp->state, LPFC_NVMET_INV_HOST_ACTIVE);
+
+ spin_lock_irq(&ndlp->lock);
+ ndlp_has_hh = ndlp->fc4_xpt_flags & NLP_XPT_HAS_HH;
+ spin_unlock_irq(&ndlp->lock);
+
+ /* Do not invalidate any nodes that do not have a hosthandle.
+ * The host_release callbk will cause a node reference
+ * count imbalance and a crash.
+ */
+ if (!ndlp_has_hh) {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_NVME | LOG_NVME_ABTS | LOG_NVME_DISC,
+ "6204 Skip invalidate on node x%px DID x%x\n",
+ ndlp, ndlp->nlp_DID);
+ return;
+ }
+
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ /* Need to get the nvmet_fc_target_port pointer here.*/
+ nvmet_fc_invalidate_host(phba->targetport, ndlp);
+#endif
+}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 3ccc97496ebf..6d9d8c196936 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -21,8 +23,13 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/export.h>
#include <linux/delay.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
+#include <linux/t10-pi.h>
+#include <linux/crc-t10dif.h>
+#include <linux/blk-cgroup.h>
+#include <net/checksum.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -38,8 +45,8 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
@@ -47,78 +54,39 @@
#define LPFC_RESET_WAIT 2
#define LPFC_ABORT_WAIT 2
-int _dump_buf_done;
-
static char *dif_op_str[] = {
- "SCSI_PROT_NORMAL",
- "SCSI_PROT_READ_INSERT",
- "SCSI_PROT_WRITE_STRIP",
- "SCSI_PROT_READ_STRIP",
- "SCSI_PROT_WRITE_INSERT",
- "SCSI_PROT_READ_PASS",
- "SCSI_PROT_WRITE_PASS",
+ "PROT_NORMAL",
+ "PROT_READ_INSERT",
+ "PROT_WRITE_STRIP",
+ "PROT_READ_STRIP",
+ "PROT_WRITE_INSERT",
+ "PROT_READ_PASS",
+ "PROT_WRITE_PASS",
};
-static void
-lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
-static void
-lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
-
-static void
-lpfc_debug_save_data(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
-{
- void *src, *dst;
- struct scatterlist *sgde = scsi_sglist(cmnd);
-
- if (!_dump_buf_data) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9050 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
- __func__);
- return;
- }
+struct scsi_dif_tuple {
+ __be16 guard_tag; /* Checksum */
+ __be16 app_tag; /* Opaque storage */
+ __be32 ref_tag; /* Target LBA or indirect LBA */
+};
- if (!sgde) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9051 BLKGRD: ERROR: data scatterlist is null\n");
- return;
- }
+static struct lpfc_rport_data *
+lpfc_rport_data_from_scsi_device(struct scsi_device *sdev)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)sdev->host->hostdata;
- dst = (void *) _dump_buf_data;
- while (sgde) {
- src = sg_virt(sgde);
- memcpy(dst, src, sgde->length);
- dst += sgde->length;
- sgde = sg_next(sgde);
- }
+ if (vport->phba->cfg_fof)
+ return ((struct lpfc_device_data *)sdev->hostdata)->rport_data;
+ else
+ return (struct lpfc_rport_data *)sdev->hostdata;
}
static void
-lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
-{
- void *src, *dst;
- struct scatterlist *sgde = scsi_prot_sglist(cmnd);
-
- if (!_dump_buf_dif) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9052 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
- __func__);
- return;
- }
-
- if (!sgde) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9053 BLKGRD: ERROR: prot scatterlist is null\n");
- return;
- }
-
- dst = _dump_buf_dif;
- while (sgde) {
- src = sg_virt(sgde);
- memcpy(dst, src, sgde->length);
- dst += sgde->length;
- sgde = sg_next(sgde);
- }
-}
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *psb);
+static void
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *psb);
+static int
+lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc);
/**
* lpfc_sli4_set_rsp_sgl_last - Set the last bit in the response sge.
@@ -130,9 +98,9 @@ lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
**/
static void
lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd)
+ struct lpfc_io_buf *lpfc_cmd)
{
- struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
if (sgl) {
sgl += 1;
sgl->word2 = le32_to_cpu(sgl->word2);
@@ -142,144 +110,6 @@ lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba,
}
/**
- * lpfc_update_stats - Update statistical data for the command completion
- * @phba: Pointer to HBA object.
- * @lpfc_cmd: lpfc scsi command object pointer.
- *
- * This function is called when there is a command completion and this
- * function updates the statistical data for the command completion.
- **/
-static void
-lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
-{
- struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
- struct lpfc_nodelist *pnode = rdata->pnode;
- struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
- unsigned long flags;
- struct Scsi_Host *shost = cmd->device->host;
- struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- unsigned long latency;
- int i;
-
- if (cmd->result)
- return;
-
- latency = jiffies_to_msecs((long)jiffies - (long)lpfc_cmd->start_time);
-
- spin_lock_irqsave(shost->host_lock, flags);
- if (!vport->stat_data_enabled ||
- vport->stat_data_blocked ||
- !pnode ||
- !pnode->lat_data ||
- (phba->bucket_type == LPFC_NO_BUCKET)) {
- spin_unlock_irqrestore(shost->host_lock, flags);
- return;
- }
-
- if (phba->bucket_type == LPFC_LINEAR_BUCKET) {
- i = (latency + phba->bucket_step - 1 - phba->bucket_base)/
- phba->bucket_step;
- /* check array subscript bounds */
- if (i < 0)
- i = 0;
- else if (i >= LPFC_MAX_BUCKET_COUNT)
- i = LPFC_MAX_BUCKET_COUNT - 1;
- } else {
- for (i = 0; i < LPFC_MAX_BUCKET_COUNT-1; i++)
- if (latency <= (phba->bucket_base +
- ((1<<i)*phba->bucket_step)))
- break;
- }
-
- pnode->lat_data[i].cmd_count++;
- spin_unlock_irqrestore(shost->host_lock, flags);
-}
-
-/**
- * lpfc_send_sdev_queuedepth_change_event - Posts a queuedepth change event
- * @phba: Pointer to HBA context object.
- * @vport: Pointer to vport object.
- * @ndlp: Pointer to FC node associated with the target.
- * @lun: Lun number of the scsi device.
- * @old_val: Old value of the queue depth.
- * @new_val: New value of the queue depth.
- *
- * This function sends an event to the mgmt application indicating
- * there is a change in the scsi device queue depth.
- **/
-static void
-lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
- struct lpfc_vport *vport,
- struct lpfc_nodelist *ndlp,
- uint32_t lun,
- uint32_t old_val,
- uint32_t new_val)
-{
- struct lpfc_fast_path_event *fast_path_evt;
- unsigned long flags;
-
- fast_path_evt = lpfc_alloc_fast_evt(phba);
- if (!fast_path_evt)
- return;
-
- fast_path_evt->un.queue_depth_evt.scsi_event.event_type =
- FC_REG_SCSI_EVENT;
- fast_path_evt->un.queue_depth_evt.scsi_event.subcategory =
- LPFC_EVENT_VARQUEDEPTH;
-
- /* Report all luns with change in queue depth */
- fast_path_evt->un.queue_depth_evt.scsi_event.lun = lun;
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwpn,
- &ndlp->nlp_portname, sizeof(struct lpfc_name));
- memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwnn,
- &ndlp->nlp_nodename, sizeof(struct lpfc_name));
- }
-
- fast_path_evt->un.queue_depth_evt.oldval = old_val;
- fast_path_evt->un.queue_depth_evt.newval = new_val;
- fast_path_evt->vport = vport;
-
- fast_path_evt->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT;
- spin_lock_irqsave(&phba->hbalock, flags);
- list_add_tail(&fast_path_evt->work_evt.evt_listp, &phba->work_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- lpfc_worker_wake_up(phba);
-
- return;
-}
-
-/**
- * lpfc_change_queue_depth - Alter scsi device queue depth
- * @sdev: Pointer the scsi device on which to change the queue depth.
- * @qdepth: New queue depth to set the sdev to.
- * @reason: The reason for the queue depth change.
- *
- * This function is called by the midlayer and the LLD to alter the queue
- * depth for a scsi device. This function sets the queue depth to the new
- * value and sends an event out to log the queue depth change.
- **/
-int
-lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata;
- unsigned long new_queue_depth, old_queue_depth;
-
- old_queue_depth = sdev->queue_depth;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- new_queue_depth = sdev->queue_depth;
- rdata = sdev->hostdata;
- if (rdata)
- lpfc_send_sdev_queuedepth_change_event(phba, vport,
- rdata->pnode, sdev->lun,
- old_queue_depth,
- new_queue_depth);
- return sdev->queue_depth;
-}
-
-/**
* lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread
* @phba: The Hba for which this call is being executed.
*
@@ -295,12 +125,14 @@ lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
{
unsigned long flags;
uint32_t evt_posted;
+ unsigned long expires;
spin_lock_irqsave(&phba->hbalock, flags);
atomic_inc(&phba->num_rsrc_err);
phba->last_rsrc_error_time = jiffies;
- if ((phba->last_ramp_down_time + QUEUE_RAMP_DOWN_INTERVAL) > jiffies) {
+ expires = phba->last_ramp_down_time + QUEUE_RAMP_DOWN_INTERVAL;
+ if (time_after(expires, jiffies)) {
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}
@@ -321,50 +153,6 @@ lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
}
/**
- * lpfc_rampup_queue_depth - Post RAMP_UP_QUEUE event for worker thread
- * @phba: The Hba for which this call is being executed.
- *
- * This routine post WORKER_RAMP_UP_QUEUE event for @phba vport. This routine
- * post at most 1 event every 5 minute after last_ramp_up_time or
- * last_rsrc_error_time. This routine wakes up worker thread of @phba
- * to process WORKER_RAM_DOWN_EVENT event.
- *
- * This routine should be called with no lock held.
- **/
-static inline void
-lpfc_rampup_queue_depth(struct lpfc_vport *vport,
- uint32_t queue_depth)
-{
- unsigned long flags;
- struct lpfc_hba *phba = vport->phba;
- uint32_t evt_posted;
- atomic_inc(&phba->num_cmd_success);
-
- if (vport->cfg_lun_queue_depth <= queue_depth)
- return;
- spin_lock_irqsave(&phba->hbalock, flags);
- if (time_before(jiffies,
- phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) ||
- time_before(jiffies,
- phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL)) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return;
- }
- phba->last_ramp_up_time = jiffies;
- spin_unlock_irqrestore(&phba->hbalock, flags);
-
- spin_lock_irqsave(&phba->pport->work_port_lock, flags);
- evt_posted = phba->pport->work_port_events & WORKER_RAMP_UP_QUEUE;
- if (!evt_posted)
- phba->pport->work_port_events |= WORKER_RAMP_UP_QUEUE;
- spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
-
- if (!evt_posted)
- lpfc_worker_wake_up(phba);
- return;
-}
-
-/**
* lpfc_ramp_down_queue_handler - WORKER_RAMP_DOWN_QUEUE event handler
* @phba: The Hba for which this call is being executed.
*
@@ -379,67 +167,34 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
struct Scsi_Host *shost;
struct scsi_device *sdev;
unsigned long new_queue_depth;
- unsigned long num_rsrc_err, num_cmd_success;
+ unsigned long num_rsrc_err;
int i;
num_rsrc_err = atomic_read(&phba->num_rsrc_err);
- num_cmd_success = atomic_read(&phba->num_cmd_success);
+
+ /*
+ * The error and success command counters are global per
+ * driver instance. If another handler has already
+ * operated on this error event, just exit.
+ */
+ if (num_rsrc_err == 0)
+ return;
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
shost = lpfc_shost_from_vport(vports[i]);
shost_for_each_device(sdev, shost) {
- new_queue_depth =
- sdev->queue_depth * num_rsrc_err /
- (num_rsrc_err + num_cmd_success);
- if (!new_queue_depth)
- new_queue_depth = sdev->queue_depth - 1;
+ if (num_rsrc_err >= sdev->queue_depth)
+ new_queue_depth = 1;
else
new_queue_depth = sdev->queue_depth -
- new_queue_depth;
- lpfc_change_queue_depth(sdev, new_queue_depth,
- SCSI_QDEPTH_DEFAULT);
+ num_rsrc_err;
+ scsi_change_queue_depth(sdev, new_queue_depth);
}
}
lpfc_destroy_vport_work_array(phba, vports);
atomic_set(&phba->num_rsrc_err, 0);
- atomic_set(&phba->num_cmd_success, 0);
-}
-
-/**
- * lpfc_ramp_up_queue_handler - WORKER_RAMP_UP_QUEUE event handler
- * @phba: The Hba for which this call is being executed.
- *
- * This routine is called to process WORKER_RAMP_UP_QUEUE event for worker
- * thread.This routine increases queue depth for all scsi device on each vport
- * associated with @phba by 1. This routine also sets @phba num_rsrc_err and
- * num_cmd_success to zero.
- **/
-void
-lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
-{
- struct lpfc_vport **vports;
- struct Scsi_Host *shost;
- struct scsi_device *sdev;
- int i;
-
- vports = lpfc_create_vport_work_array(phba);
- if (vports != NULL)
- for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
- shost = lpfc_shost_from_vport(vports[i]);
- shost_for_each_device(sdev, shost) {
- if (vports[i]->cfg_lun_queue_depth <=
- sdev->queue_depth)
- continue;
- lpfc_change_queue_depth(sdev,
- sdev->queue_depth+1,
- SCSI_QDEPTH_RAMP_UP);
- }
- }
- lpfc_destroy_vport_work_array(phba, vports);
- atomic_set(&phba->num_rsrc_err, 0);
- atomic_set(&phba->num_cmd_success, 0);
}
/**
@@ -474,7 +229,7 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
/**
* lpfc_new_scsi_buf_s3 - Scsi buffer allocator for HBA with SLI3 IF spec
* @vport: The virtual port for which this call being executed.
- * @num_to_allocate: The requested number of buffers to allocate.
+ * @num_to_alloc: The requested number of buffers to allocate.
*
* This routine allocates a scsi buffer for device with SLI-3 interface spec,
* the scsi buffer contains all the necessary information needed to initiate
@@ -491,17 +246,26 @@ static int
lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *psb;
+ struct lpfc_io_buf *psb;
struct ulp_bde64 *bpl;
IOCB_t *iocb;
dma_addr_t pdma_phys_fcp_cmd;
dma_addr_t pdma_phys_fcp_rsp;
- dma_addr_t pdma_phys_bpl;
+ dma_addr_t pdma_phys_sgl;
uint16_t iotag;
- int bcnt;
+ int bcnt, bpl_size;
+
+ bpl_size = phba->cfg_sg_dma_buf_size -
+ (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "9067 ALLOC %d scsi_bufs: %d (%d + %d + %d)\n",
+ num_to_alloc, phba->cfg_sg_dma_buf_size,
+ (int)sizeof(struct fcp_cmnd),
+ (int)sizeof(struct fcp_rsp), bpl_size);
for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
- psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+ psb = kzalloc(sizeof(struct lpfc_io_buf), GFP_KERNEL);
if (!psb)
break;
@@ -511,36 +275,34 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
* struct fcp_cmnd, struct fcp_rsp and the number of bde's
* necessary to support the sg_tablesize.
*/
- psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool,
+ psb->data = dma_pool_zalloc(phba->lpfc_sg_dma_buf_pool,
GFP_KERNEL, &psb->dma_handle);
if (!psb->data) {
kfree(psb);
break;
}
- /* Initialize virtual ptrs to dma_buf region. */
- memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
/* Allocate iotag for psb->cur_iocbq. */
iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
if (iotag == 0) {
- pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
- psb->data, psb->dma_handle);
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ psb->data, psb->dma_handle);
kfree(psb);
break;
}
- psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+ psb->cur_iocbq.cmd_flag |= LPFC_IO_FCP;
psb->fcp_cmnd = psb->data;
psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd);
- psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) +
+ psb->dma_sgl = psb->data + sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp);
/* Initialize local short-hand pointers. */
- bpl = psb->fcp_bpl;
+ bpl = (struct ulp_bde64 *)psb->dma_sgl;
pdma_phys_fcp_cmd = psb->dma_handle;
pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd);
- pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) +
+ pdma_phys_sgl = psb->dma_handle + sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp);
/*
@@ -591,16 +353,17 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
iocb->un.fcpi64.bdl.bdeSize =
(2 * sizeof(struct ulp_bde64));
iocb->un.fcpi64.bdl.addrLow =
- putPaddrLow(pdma_phys_bpl);
+ putPaddrLow(pdma_phys_sgl);
iocb->un.fcpi64.bdl.addrHigh =
- putPaddrHigh(pdma_phys_bpl);
+ putPaddrHigh(pdma_phys_sgl);
iocb->ulpBdeCount = 1;
iocb->ulpLe = 1;
}
iocb->ulpClass = CLASS3;
psb->status = IOSTAT_SUCCESS;
/* Put it back into the SCSI buffer list */
- psb->cur_iocbq.context1 = psb;
+ psb->cur_iocbq.io_buf = psb;
+ spin_lock_init(&psb->buf_lock);
lpfc_release_scsi_buf_s3(phba, psb);
}
@@ -619,367 +382,174 @@ void
lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *psb, *next_psb;
+ struct lpfc_io_buf *psb, *next_psb;
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
+ int idx;
+
+ if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
+ return;
+
+ /* may be called before queues established if hba_setup fails */
+ if (!phba->sli4_hba.hdwq)
+ return;
spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
- list_for_each_entry_safe(psb, next_psb,
- &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
- if (psb->rdata && psb->rdata->pnode
- && psb->rdata->pnode->vport == vport)
- psb->rdata = NULL;
+ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
+ qp = &phba->sli4_hba.hdwq[idx];
+
+ spin_lock(&qp->abts_io_buf_list_lock);
+ list_for_each_entry_safe(psb, next_psb,
+ &qp->lpfc_abts_io_buf_list, list) {
+ if (psb->cur_iocbq.cmd_flag & LPFC_IO_NVME)
+ continue;
+
+ if (psb->rdata && psb->rdata->pnode &&
+ psb->rdata->pnode->vport == vport)
+ psb->rdata = NULL;
+ }
+ spin_unlock(&qp->abts_io_buf_list_lock);
}
- spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
}
/**
- * lpfc_sli4_fcp_xri_aborted - Fast-path process of fcp xri abort
+ * lpfc_sli4_io_xri_aborted - Fast-path process of fcp xri abort
* @phba: pointer to lpfc hba data structure.
* @axri: pointer to the fcp xri abort wcqe structure.
+ * @idx: index into hdwq
*
* This routine is invoked by the worker thread to process a SLI4 fast-path
- * FCP aborted xri.
+ * FCP or NVME aborted xri.
**/
void
-lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
- struct sli4_wcqe_xri_aborted *axri)
+lpfc_sli4_io_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri, int idx)
{
- uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
- uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri);
- struct lpfc_scsi_buf *psb, *next_psb;
+ u16 xri = 0;
+ u16 rxid = 0;
+ struct lpfc_io_buf *psb, *next_psb;
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
struct lpfc_iocbq *iocbq;
int i;
struct lpfc_nodelist *ndlp;
int rrq_empty = 0;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring = phba->sli4_hba.els_wq->pring;
+ struct scsi_cmnd *cmd;
+ int offline = 0;
+ if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP))
+ return;
+ offline = pci_channel_offline(phba->pcidev);
+ if (!offline) {
+ xri = bf_get(lpfc_wcqe_xa_xri, axri);
+ rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri);
+ }
+ qp = &phba->sli4_hba.hdwq[idx];
spin_lock_irqsave(&phba->hbalock, iflag);
- spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+ spin_lock(&qp->abts_io_buf_list_lock);
list_for_each_entry_safe(psb, next_psb,
- &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
+ &qp->lpfc_abts_io_buf_list, list) {
+ if (offline)
+ xri = psb->cur_iocbq.sli4_xritag;
if (psb->cur_iocbq.sli4_xritag == xri) {
- list_del(&psb->list);
- psb->exch_busy = 0;
+ list_del_init(&psb->list);
+ psb->flags &= ~LPFC_SBUF_XBUSY;
psb->status = IOSTAT_SUCCESS;
- spin_unlock(
- &phba->sli4_hba.abts_scsi_buf_list_lock);
+ if (psb->cur_iocbq.cmd_flag & LPFC_IO_NVME) {
+ qp->abts_nvme_io_bufs--;
+ spin_unlock(&qp->abts_io_buf_list_lock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ if (!offline) {
+ lpfc_sli4_nvme_xri_aborted(phba, axri,
+ psb);
+ return;
+ }
+ lpfc_sli4_nvme_pci_offline_aborted(phba, psb);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ spin_lock(&qp->abts_io_buf_list_lock);
+ continue;
+ }
+ qp->abts_scsi_io_bufs--;
+ spin_unlock(&qp->abts_io_buf_list_lock);
+
if (psb->rdata && psb->rdata->pnode)
ndlp = psb->rdata->pnode;
else
ndlp = NULL;
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ spin_lock_irqsave(&phba->rrq_list_lock, iflag);
rrq_empty = list_empty(&phba->active_rrq_list);
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- if (ndlp)
- lpfc_set_rrq_active(phba, ndlp, xri, rxid, 1);
- lpfc_release_scsi_buf_s4(phba, psb);
- if (rrq_empty)
- lpfc_worker_wake_up(phba);
- return;
- }
- }
- spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
- for (i = 1; i <= phba->sli.last_iotag; i++) {
- iocbq = phba->sli.iocbq_lookup[i];
+ spin_unlock_irqrestore(&phba->rrq_list_lock, iflag);
+ if (ndlp && !offline) {
+ lpfc_set_rrq_active(phba, ndlp,
+ psb->cur_iocbq.sli4_lxritag, rxid, 1);
+ lpfc_sli4_abts_err_handler(phba, ndlp, axri);
+ }
- if (!(iocbq->iocb_flag & LPFC_IO_FCP) ||
- (iocbq->iocb_flag & LPFC_IO_LIBDFC))
- continue;
- if (iocbq->sli4_xritag != xri)
- continue;
- psb = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
- psb->exch_busy = 0;
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- if (pring->txq_cnt)
- lpfc_worker_wake_up(phba);
- return;
+ if (phba->cfg_fcp_wait_abts_rsp || offline) {
+ spin_lock_irqsave(&psb->buf_lock, iflag);
+ cmd = psb->pCmd;
+ psb->pCmd = NULL;
+ spin_unlock_irqrestore(&psb->buf_lock, iflag);
- }
- spin_unlock_irqrestore(&phba->hbalock, iflag);
-}
+ /* The sdev is not guaranteed to be valid post
+ * scsi_done upcall.
+ */
+ if (cmd)
+ scsi_done(cmd);
-/**
- * lpfc_sli4_repost_scsi_sgl_list - Repsot the Scsi buffers sgl pages as block
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine walks the list of scsi buffers that have been allocated and
- * repost them to the HBA by using SGL block post. This is needed after a
- * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine
- * is responsible for moving all scsi buffers on the lpfc_abts_scsi_sgl_list
- * to the lpfc_scsi_buf_list. If the repost fails, reject all scsi buffers.
- *
- * Returns: 0 = success, non-zero failure.
- **/
-int
-lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba)
-{
- struct lpfc_scsi_buf *psb;
- int index, status, bcnt = 0, rcnt = 0, rc = 0;
- LIST_HEAD(sblist);
-
- for (index = 0; index < phba->sli4_hba.scsi_xri_cnt; index++) {
- psb = phba->sli4_hba.lpfc_scsi_psb_array[index];
- if (psb) {
- /* Remove from SCSI buffer list */
- list_del(&psb->list);
- /* Add it to a local SCSI buffer list */
- list_add_tail(&psb->list, &sblist);
- if (++rcnt == LPFC_NEMBED_MBOX_SGL_CNT) {
- bcnt = rcnt;
- rcnt = 0;
+ /*
+ * We expect there is an abort thread waiting
+ * for command completion wake up the thread.
+ */
+ spin_lock_irqsave(&psb->buf_lock, iflag);
+ psb->cur_iocbq.cmd_flag &=
+ ~LPFC_DRIVER_ABORTED;
+ if (psb->waitq)
+ wake_up(psb->waitq);
+ spin_unlock_irqrestore(&psb->buf_lock, iflag);
}
- } else
- /* A hole present in the XRI array, need to skip */
- bcnt = rcnt;
- if (index == phba->sli4_hba.scsi_xri_cnt - 1)
- /* End of XRI array for SCSI buffer, complete */
- bcnt = rcnt;
-
- /* Continue until collect up to a nembed page worth of sgls */
- if (bcnt == 0)
- continue;
- /* Now, post the SCSI buffer list sgls as a block */
- if (!phba->sli4_hba.extents_in_use)
- status = lpfc_sli4_post_scsi_sgl_block(phba,
- &sblist,
- bcnt);
- else
- status = lpfc_sli4_post_scsi_sgl_blk_ext(phba,
- &sblist,
- bcnt);
- /* Reset SCSI buffer count for next round of posting */
- bcnt = 0;
- while (!list_empty(&sblist)) {
- list_remove_head(&sblist, psb, struct lpfc_scsi_buf,
- list);
- if (status) {
- /* Put this back on the abort scsi list */
- psb->exch_busy = 1;
- rc++;
- } else {
- psb->exch_busy = 0;
- psb->status = IOSTAT_SUCCESS;
- }
- /* Put it back into the SCSI buffer list */
lpfc_release_scsi_buf_s4(phba, psb);
+ if (rrq_empty)
+ lpfc_worker_wake_up(phba);
+ if (!offline)
+ return;
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ spin_lock(&qp->abts_io_buf_list_lock);
+ continue;
}
}
- return rc;
-}
-
-/**
- * lpfc_new_scsi_buf_s4 - Scsi buffer allocator for HBA with SLI4 IF spec
- * @vport: The virtual port for which this call being executed.
- * @num_to_allocate: The requested number of buffers to allocate.
- *
- * This routine allocates a scsi buffer for device with SLI-4 interface spec,
- * the scsi buffer contains all the necessary information needed to initiate
- * a SCSI I/O.
- *
- * Return codes:
- * int - number of scsi buffers that were allocated.
- * 0 = failure, less than num_to_alloc is a partial failure.
- **/
-static int
-lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
-{
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *psb;
- struct sli4_sge *sgl;
- IOCB_t *iocb;
- dma_addr_t pdma_phys_fcp_cmd;
- dma_addr_t pdma_phys_fcp_rsp;
- dma_addr_t pdma_phys_bpl, pdma_phys_bpl1;
- uint16_t iotag, last_xritag = NO_XRI, lxri = 0;
- int status = 0, index;
- int bcnt;
- int non_sequential_xri = 0;
- LIST_HEAD(sblist);
-
- for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
- psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
- if (!psb)
- break;
-
- /*
- * Get memory from the pci pool to map the virt space to pci bus
- * space for an I/O. The DMA buffer includes space for the
- * struct fcp_cmnd, struct fcp_rsp and the number of bde's
- * necessary to support the sg_tablesize.
- */
- psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool,
- GFP_KERNEL, &psb->dma_handle);
- if (!psb->data) {
- kfree(psb);
- break;
- }
-
- /* Initialize virtual ptrs to dma_buf region. */
- memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
-
- /* Allocate iotag for psb->cur_iocbq. */
- iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
- if (iotag == 0) {
- pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
- psb->data, psb->dma_handle);
- kfree(psb);
- break;
- }
-
- lxri = lpfc_sli4_next_xritag(phba);
- if (lxri == NO_XRI) {
- pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
- psb->data, psb->dma_handle);
- kfree(psb);
- break;
- }
- psb->cur_iocbq.sli4_lxritag = lxri;
- psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri];
- if (last_xritag != NO_XRI
- && psb->cur_iocbq.sli4_xritag != (last_xritag+1)) {
- non_sequential_xri = 1;
- } else
- list_add_tail(&psb->list, &sblist);
- last_xritag = psb->cur_iocbq.sli4_xritag;
-
- index = phba->sli4_hba.scsi_xri_cnt++;
- psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
-
- psb->fcp_bpl = psb->data;
- psb->fcp_cmnd = (psb->data + phba->cfg_sg_dma_buf_size)
- - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
- psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd +
- sizeof(struct fcp_cmnd));
+ spin_unlock(&qp->abts_io_buf_list_lock);
+ if (!offline) {
+ for (i = 1; i <= phba->sli.last_iotag; i++) {
+ iocbq = phba->sli.iocbq_lookup[i];
- /* Initialize local short-hand pointers. */
- sgl = (struct sli4_sge *)psb->fcp_bpl;
- pdma_phys_bpl = psb->dma_handle;
- pdma_phys_fcp_cmd =
- (psb->dma_handle + phba->cfg_sg_dma_buf_size)
- - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
- pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd);
-
- /*
- * The first two bdes are the FCP_CMD and FCP_RSP. The balance
- * are sg list bdes. Initialize the first two and leave the
- * rest for queuecommand.
- */
- sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
- sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
- sgl->word2 = le32_to_cpu(sgl->word2);
- bf_set(lpfc_sli4_sge_last, sgl, 0);
- sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd));
- sgl++;
-
- /* Setup the physical region for the FCP RSP */
- sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
- sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
- sgl->word2 = le32_to_cpu(sgl->word2);
- bf_set(lpfc_sli4_sge_last, sgl, 1);
- sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp));
-
- /*
- * Since the IOCB for the FCP I/O is built into this
- * lpfc_scsi_buf, initialize it with all known data now.
- */
- iocb = &psb->cur_iocbq.iocb;
- iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
- iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64;
- /* setting the BLP size to 2 * sizeof BDE may not be correct.
- * We are setting the bpl to point to out sgl. An sgl's
- * entries are 16 bytes, a bpl entries are 12 bytes.
- */
- iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
- iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd);
- iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd);
- iocb->ulpBdeCount = 1;
- iocb->ulpLe = 1;
- iocb->ulpClass = CLASS3;
- psb->cur_iocbq.context1 = psb;
- if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
- pdma_phys_bpl1 = pdma_phys_bpl + SGL_PAGE_SIZE;
- else
- pdma_phys_bpl1 = 0;
- psb->dma_phys_bpl = pdma_phys_bpl;
- phba->sli4_hba.lpfc_scsi_psb_array[index] = psb;
- if (non_sequential_xri) {
- status = lpfc_sli4_post_sgl(phba, pdma_phys_bpl,
- pdma_phys_bpl1,
- psb->cur_iocbq.sli4_xritag);
- if (status) {
- /* Put this back on the abort scsi list */
- psb->exch_busy = 1;
- } else {
- psb->exch_busy = 0;
- psb->status = IOSTAT_SUCCESS;
- }
- /* Put it back into the SCSI buffer list */
- lpfc_release_scsi_buf_s4(phba, psb);
- break;
- }
- }
- if (bcnt) {
- if (!phba->sli4_hba.extents_in_use)
- status = lpfc_sli4_post_scsi_sgl_block(phba,
- &sblist,
- bcnt);
- else
- status = lpfc_sli4_post_scsi_sgl_blk_ext(phba,
- &sblist,
- bcnt);
-
- if (status) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "3021 SCSI SGL post error %d\n",
- status);
- bcnt = 0;
- }
- /* Reset SCSI buffer count for next round of posting */
- while (!list_empty(&sblist)) {
- list_remove_head(&sblist, psb, struct lpfc_scsi_buf,
- list);
- if (status) {
- /* Put this back on the abort scsi list */
- psb->exch_busy = 1;
- } else {
- psb->exch_busy = 0;
- psb->status = IOSTAT_SUCCESS;
- }
- /* Put it back into the SCSI buffer list */
- lpfc_release_scsi_buf_s4(phba, psb);
+ if (!(iocbq->cmd_flag & LPFC_IO_FCP) ||
+ (iocbq->cmd_flag & LPFC_IO_LIBDFC))
+ continue;
+ if (iocbq->sli4_xritag != xri)
+ continue;
+ psb = container_of(iocbq, struct lpfc_io_buf, cur_iocbq);
+ psb->flags &= ~LPFC_SBUF_XBUSY;
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ if (test_bit(HBA_SETUP, &phba->hba_flag) &&
+ !list_empty(&pring->txq))
+ lpfc_worker_wake_up(phba);
+ return;
}
}
-
- return bcnt + non_sequential_xri;
-}
-
-/**
- * lpfc_new_scsi_buf - Wrapper funciton for scsi buffer allocator
- * @vport: The virtual port for which this call being executed.
- * @num_to_allocate: The requested number of buffers to allocate.
- *
- * This routine wraps the actual SCSI buffer allocator function pointer from
- * the lpfc_hba struct.
- *
- * Return codes:
- * int - number of scsi buffers that were allocated.
- * 0 = failure, less than num_to_alloc is a partial failure.
- **/
-static inline int
-lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc)
-{
- return vport->phba->lpfc_new_scsi_buf(vport, num_to_alloc);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
}
/**
* lpfc_get_scsi_buf_s3 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
* @phba: The HBA for which this call is being executed.
+ * @ndlp: pointer to a node-list data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
*
* This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
* and returns to caller.
@@ -988,64 +558,134 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc)
* NULL - Error
* Pointer to lpfc_scsi_buf - Success
**/
-static struct lpfc_scsi_buf*
-lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+static struct lpfc_io_buf *
+lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd)
{
- struct lpfc_scsi_buf * lpfc_cmd = NULL;
- struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
+ struct lpfc_io_buf *lpfc_cmd = NULL;
+ struct list_head *scsi_buf_list_get = &phba->lpfc_scsi_buf_list_get;
unsigned long iflag = 0;
- spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
- list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
- if (lpfc_cmd) {
- lpfc_cmd->seg_cnt = 0;
- lpfc_cmd->nonsg_phys = 0;
- lpfc_cmd->prot_seg_cnt = 0;
+ spin_lock_irqsave(&phba->scsi_buf_list_get_lock, iflag);
+ list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_io_buf,
+ list);
+ if (!lpfc_cmd) {
+ spin_lock(&phba->scsi_buf_list_put_lock);
+ list_splice(&phba->lpfc_scsi_buf_list_put,
+ &phba->lpfc_scsi_buf_list_get);
+ INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put);
+ list_remove_head(scsi_buf_list_get, lpfc_cmd,
+ struct lpfc_io_buf, list);
+ spin_unlock(&phba->scsi_buf_list_put_lock);
+ }
+ spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, iflag);
+
+ if (lpfc_ndlp_check_qdepth(phba, ndlp) && lpfc_cmd) {
+ atomic_inc(&ndlp->cmd_pending);
+ lpfc_cmd->flags |= LPFC_SBUF_BUMP_QDEPTH;
}
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
return lpfc_cmd;
}
/**
- * lpfc_get_scsi_buf_s4 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
+ * lpfc_get_scsi_buf_s4 - Get a scsi buffer from io_buf_list of the HBA
* @phba: The HBA for which this call is being executed.
+ * @ndlp: pointer to a node-list data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
*
- * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
+ * This routine removes a scsi buffer from head of @hdwq io_buf_list
* and returns to caller.
*
* Return codes:
* NULL - Error
* Pointer to lpfc_scsi_buf - Success
**/
-static struct lpfc_scsi_buf*
-lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+static struct lpfc_io_buf *
+lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd)
{
- struct lpfc_scsi_buf *lpfc_cmd ;
- unsigned long iflag = 0;
- int found = 0;
+ struct lpfc_io_buf *lpfc_cmd;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct sli4_sge_le *sgl;
+ dma_addr_t pdma_phys_fcp_rsp;
+ dma_addr_t pdma_phys_fcp_cmd;
+ uint32_t cpu, idx;
+ int tag;
+ struct fcp_cmd_rsp_buf *tmp = NULL;
+
+ cpu = raw_smp_processor_id();
+ if (cmnd && phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) {
+ tag = blk_mq_unique_tag(scsi_cmd_to_rq(cmnd));
+ idx = blk_mq_unique_tag_to_hwq(tag);
+ } else {
+ idx = phba->sli4_hba.cpu_map[cpu].hdwq;
+ }
- spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
- list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list,
- list) {
- if (lpfc_test_rrq_active(phba, ndlp,
- lpfc_cmd->cur_iocbq.sli4_xritag))
- continue;
- list_del(&lpfc_cmd->list);
- found = 1;
- lpfc_cmd->seg_cnt = 0;
- lpfc_cmd->nonsg_phys = 0;
- lpfc_cmd->prot_seg_cnt = 0;
- break;
+ lpfc_cmd = lpfc_get_io_buf(phba, ndlp, idx,
+ !phba->cfg_xri_rebalancing);
+ if (!lpfc_cmd) {
+ qp = &phba->sli4_hba.hdwq[idx];
+ qp->empty_io_bufs++;
+ return NULL;
}
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock,
- iflag);
- if (!found)
+
+ /* Setup key fields in buffer that may have been changed
+ * if other protocols used this buffer.
+ */
+ lpfc_cmd->cur_iocbq.cmd_flag = LPFC_IO_FCP;
+ lpfc_cmd->prot_seg_cnt = 0;
+ lpfc_cmd->seg_cnt = 0;
+ lpfc_cmd->timeout = 0;
+ lpfc_cmd->flags = 0;
+ lpfc_cmd->start_time = jiffies;
+ lpfc_cmd->waitq = NULL;
+ lpfc_cmd->cpu = cpu;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ lpfc_cmd->prot_data_type = 0;
+#endif
+ tmp = lpfc_get_cmd_rsp_buf_per_hdwq(phba, lpfc_cmd);
+ if (!tmp) {
+ lpfc_release_io_buf(phba, lpfc_cmd, lpfc_cmd->hdwq);
return NULL;
+ }
+
+ lpfc_cmd->fcp_cmnd = tmp->fcp_cmnd;
+ lpfc_cmd->fcp_rsp = tmp->fcp_rsp;
+
+ /*
+ * The first two SGEs are the FCP_CMD and FCP_RSP.
+ * The balance are sg list bdes. Initialize the
+ * first two and leave the rest for queuecommand.
+ */
+ sgl = (struct sli4_sge_le *)lpfc_cmd->dma_sgl;
+ pdma_phys_fcp_cmd = tmp->fcp_cmd_rsp_dma_handle;
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
+ bf_set_le32(lpfc_sli4_sge_last, sgl, 0);
+ if (cmnd && cmnd->cmd_len > LPFC_FCP_CDB_LEN)
+ sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd32));
else
- return lpfc_cmd;
+ sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd));
+
+ sgl++;
+
+ /* Setup the physical region for the FCP RSP */
+ pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd32);
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
+ bf_set_le32(lpfc_sli4_sge_last, sgl, 1);
+ sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp));
+
+ if (lpfc_ndlp_check_qdepth(phba, ndlp)) {
+ atomic_inc(&ndlp->cmd_pending);
+ lpfc_cmd->flags |= LPFC_SBUF_BUMP_QDEPTH;
+ }
+ return lpfc_cmd;
}
/**
* lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
* @phba: The HBA for which this call is being executed.
+ * @ndlp: pointer to a node-list data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
*
* This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
* and returns to caller.
@@ -1054,14 +694,15 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
* NULL - Error
* Pointer to lpfc_scsi_buf - Success
**/
-static struct lpfc_scsi_buf*
-lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+static struct lpfc_io_buf*
+lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
+ struct scsi_cmnd *cmnd)
{
- return phba->lpfc_get_scsi_buf(phba, ndlp);
+ return phba->lpfc_get_scsi_buf(phba, ndlp, cmnd);
}
/**
- * lpfc_release_scsi_buf - Return a scsi buffer back to hba scsi buf list
+ * lpfc_release_scsi_buf_s3 - Return a scsi buffer back to hba scsi buf list
* @phba: The Hba for which this call is being executed.
* @psb: The scsi buffer which is being released.
*
@@ -1069,14 +710,18 @@ lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
* lpfc_scsi_buf_list list.
**/
static void
-lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
unsigned long iflag = 0;
- spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
+ psb->seg_cnt = 0;
+ psb->prot_seg_cnt = 0;
+
+ spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag);
psb->pCmd = NULL;
- list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list);
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
+ psb->cur_iocbq.cmd_flag = LPFC_IO_FCP;
+ list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list_put);
+ spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag);
}
/**
@@ -1084,30 +729,30 @@ lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
* @phba: The Hba for which this call is being executed.
* @psb: The scsi buffer which is being released.
*
- * This routine releases @psb scsi buffer by adding it to tail of @phba
- * lpfc_scsi_buf_list list. For SLI4 XRI's are tied to the scsi buffer
+ * This routine releases @psb scsi buffer by adding it to tail of @hdwq
+ * io_buf_list list. For SLI4 XRI's are tied to the scsi buffer
* and cannot be reused for at least RA_TOV amount of time if it was
* aborted.
**/
static void
-lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
+ struct lpfc_sli4_hdw_queue *qp;
unsigned long iflag = 0;
- if (psb->exch_busy) {
- spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock,
- iflag);
- psb->pCmd = NULL;
- list_add_tail(&psb->list,
- &phba->sli4_hba.lpfc_abts_scsi_buf_list);
- spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock,
- iflag);
+ psb->seg_cnt = 0;
+ psb->prot_seg_cnt = 0;
+
+ qp = psb->hdwq;
+ if (psb->flags & LPFC_SBUF_XBUSY) {
+ spin_lock_irqsave(&qp->abts_io_buf_list_lock, iflag);
+ if (!phba->cfg_fcp_wait_abts_rsp)
+ psb->pCmd = NULL;
+ list_add_tail(&psb->list, &qp->lpfc_abts_io_buf_list);
+ qp->abts_scsi_io_bufs++;
+ spin_unlock_irqrestore(&qp->abts_io_buf_list_lock, iflag);
} else {
-
- spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
- psb->pCmd = NULL;
- list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list);
- spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
+ lpfc_release_io_buf(phba, (struct lpfc_io_buf *)psb, qp);
}
}
@@ -1120,20 +765,42 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
* lpfc_scsi_buf_list list.
**/
static void
-lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
+ if ((psb->flags & LPFC_SBUF_BUMP_QDEPTH) && psb->ndlp)
+ atomic_dec(&psb->ndlp->cmd_pending);
+ psb->flags &= ~LPFC_SBUF_BUMP_QDEPTH;
phba->lpfc_release_scsi_buf(phba, psb);
}
/**
+ * lpfc_fcpcmd_to_iocb - copy the fcp_cmd data into the IOCB
+ * @data: A pointer to the immediate command data portion of the IOCB.
+ * @fcp_cmnd: The FCP Command that is provided by the SCSI layer.
+ *
+ * The routine copies the entire FCP command from @fcp_cmnd to @data while
+ * byte swapping the data to big endian format for transmission on the wire.
+ **/
+static void
+lpfc_fcpcmd_to_iocb(u8 *data, struct fcp_cmnd *fcp_cmnd)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < sizeof(struct fcp_cmnd);
+ i += sizeof(uint32_t), j++) {
+ ((uint32_t *)data)[j] = cpu_to_be32(((uint32_t *)fcp_cmnd)[j]);
+ }
+}
+
+/**
* lpfc_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec
* @phba: The Hba for which this call is being executed.
* @lpfc_cmd: The scsi buffer which is going to be mapped.
*
* This routine does the pci dma mapping for scatter-gather list of scsi cmnd
* field of @lpfc_cmd for device with SLI-3 interface spec. This routine scans
- * through sg elements and format the bdea. This routine also initializes all
+ * through sg elements and format the bde. This routine also initializes all
* IOCB fields which are dependent on scsi command request buffer.
*
* Return codes:
@@ -1141,12 +808,12 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
* 0 - Success
**/
static int
-lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct scatterlist *sgel = NULL;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ struct ulp_bde64 *bpl = (struct ulp_bde64 *)lpfc_cmd->dma_sgl;
struct lpfc_iocbq *iocbq = &lpfc_cmd->cur_iocbq;
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
struct ulp_bde64 *data_bde = iocb_cmd->unsli3.fcp_ext.dbde;
@@ -1163,7 +830,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
bpl += 2;
if (scsi_sg_count(scsi_cmnd)) {
/*
- * The driver stores the segment count returned from pci_map_sg
+ * The driver stores the segment count returned from dma_map_sg
* because this a count of dma-mappings used to map the use_sg
* pages. They are not guaranteed to be the same for those
* architectures that implement an IOMMU.
@@ -1176,13 +843,15 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9064 BLKGRD: %s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d\n",
- __func__, phba->cfg_sg_seg_cnt,
- lpfc_cmd->seg_cnt);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9064 BLKGRD: %s: Too many sg segments"
+ " from dma_map_sg. Config %d, seg_cnt"
+ " %d\n", __func__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ WARN_ON_ONCE(lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt);
+ lpfc_cmd->seg_cnt = 0;
scsi_dma_unmap(scsi_cmnd);
- return 1;
+ return 2;
}
/*
@@ -1198,7 +867,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
physaddr = sg_dma_address(sgel);
if (phba->sli_rev == 3 &&
!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
- !(iocbq->iocb_flag & DSS_SECURITY_OP) &&
+ !(iocbq->cmd_flag & DSS_SECURITY_OP) &&
nseg <= LPFC_EXT_DATA_BDE_COUNT) {
data_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
data_bde->tus.f.bdeSize = sg_dma_len(sgel);
@@ -1226,7 +895,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
*/
if (phba->sli_rev == 3 &&
!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
- !(iocbq->iocb_flag & DSS_SECURITY_OP)) {
+ !(iocbq->cmd_flag & DSS_SECURITY_OP)) {
if (num_bde > LPFC_EXT_DATA_BDE_COUNT) {
/*
* The extended IOCB format can only fit 3 BDE or a BPL.
@@ -1260,139 +929,589 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* we need to set word 4 of IOCB here
*/
iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
+ lpfc_fcpcmd_to_iocb(iocb_cmd->unsli3.fcp_ext.icd, fcp_cmnd);
return 0;
}
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+
+/* Return BG_ERR_INIT if error injection is detected by Initiator */
+#define BG_ERR_INIT 0x1
+/* Return BG_ERR_TGT if error injection is detected by Target */
+#define BG_ERR_TGT 0x2
+/* Return BG_ERR_SWAP if swapping CSUM<-->CRC is required for error injection */
+#define BG_ERR_SWAP 0x10
/*
- * Given a scsi cmnd, determine the BlockGuard opcodes to be used with it
+ * Return BG_ERR_CHECK if disabling Guard/Ref/App checking is required for
+ * error injection
+ */
+#define BG_ERR_CHECK 0x20
+
+/**
+ * lpfc_bg_err_inject - Determine if we should inject an error
+ * @phba: The Hba for which this call is being executed.
+ * @sc: The SCSI command to examine
+ * @reftag: (out) BlockGuard reference tag for transmitted data
+ * @apptag: (out) BlockGuard application tag for transmitted data
+ * @new_guard: (in) Value to replace CRC with if needed
+ *
+ * Returns BG_ERR_* bit mask or 0 if request ignored
+ **/
+static int
+lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ uint32_t *reftag, uint16_t *apptag, uint32_t new_guard)
+{
+ struct scatterlist *sgpe; /* s/g prot entry */
+ struct lpfc_io_buf *lpfc_cmd = NULL;
+ struct scsi_dif_tuple *src = NULL;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_rport_data *rdata;
+ uint32_t op = scsi_get_prot_op(sc);
+ uint32_t blksize;
+ uint32_t numblks;
+ u32 lba;
+ int rc = 0;
+ int blockoff = 0;
+
+ if (op == SCSI_PROT_NORMAL)
+ return 0;
+
+ sgpe = scsi_prot_sglist(sc);
+ lba = scsi_prot_ref_tag(sc);
+
+ /* First check if we need to match the LBA */
+ if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) {
+ blksize = scsi_prot_interval(sc);
+ numblks = (scsi_bufflen(sc) + blksize - 1) / blksize;
+
+ /* Make sure we have the right LBA if one is specified */
+ if (phba->lpfc_injerr_lba < (u64)lba ||
+ (phba->lpfc_injerr_lba >= (u64)(lba + numblks)))
+ return 0;
+ if (sgpe) {
+ blockoff = phba->lpfc_injerr_lba - (u64)lba;
+ numblks = sg_dma_len(sgpe) /
+ sizeof(struct scsi_dif_tuple);
+ if (numblks < blockoff)
+ blockoff = numblks;
+ }
+ }
+
+ /* Next check if we need to match the remote NPortID or WWPN */
+ rdata = lpfc_rport_data_from_scsi_device(sc->device);
+ if (rdata && rdata->pnode) {
+ ndlp = rdata->pnode;
+
+ /* Make sure we have the right NPortID if one is specified */
+ if (phba->lpfc_injerr_nportid &&
+ (phba->lpfc_injerr_nportid != ndlp->nlp_DID))
+ return 0;
+
+ /*
+ * Make sure we have the right WWPN if one is specified.
+ * wwn[0] should be a non-zero NAA in a good WWPN.
+ */
+ if (phba->lpfc_injerr_wwpn.u.wwn[0] &&
+ (memcmp(&ndlp->nlp_portname, &phba->lpfc_injerr_wwpn,
+ sizeof(struct lpfc_name)) != 0))
+ return 0;
+ }
+
+ /* Setup a ptr to the protection data if the SCSI host provides it */
+ if (sgpe) {
+ src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+ src += blockoff;
+ lpfc_cmd = (struct lpfc_io_buf *)sc->host_scribble;
+ }
+
+ /* Should we change the Reference Tag */
+ if (reftag) {
+ if (phba->lpfc_injerr_wref_cnt) {
+ switch (op) {
+ case SCSI_PROT_WRITE_PASS:
+ if (src) {
+ /*
+ * For WRITE_PASS, force the error
+ * to be sent on the wire. It should
+ * be detected by the Target.
+ * If blockoff != 0 error will be
+ * inserted in middle of the IO.
+ */
+
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "9076 BLKGRD: Injecting reftag error: "
+ "write lba x%lx + x%x oldrefTag x%x\n",
+ (unsigned long)lba, blockoff,
+ be32_to_cpu(src->ref_tag));
+
+ /*
+ * Save the old ref_tag so we can
+ * restore it on completion.
+ */
+ if (lpfc_cmd) {
+ lpfc_cmd->prot_data_type =
+ LPFC_INJERR_REFTAG;
+ lpfc_cmd->prot_data_segment =
+ src;
+ lpfc_cmd->prot_data =
+ src->ref_tag;
+ }
+ src->ref_tag = cpu_to_be32(0xDEADBEEF);
+ phba->lpfc_injerr_wref_cnt--;
+ if (phba->lpfc_injerr_wref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
+
+ break;
+ }
+ fallthrough;
+ case SCSI_PROT_WRITE_INSERT:
+ /*
+ * For WRITE_INSERT, force the error
+ * to be sent on the wire. It should be
+ * detected by the Target.
+ */
+ /* DEADBEEF will be the reftag on the wire */
+ *reftag = 0xDEADBEEF;
+ phba->lpfc_injerr_wref_cnt--;
+ if (phba->lpfc_injerr_wref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9078 BLKGRD: Injecting reftag error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ case SCSI_PROT_WRITE_STRIP:
+ /*
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
+ */
+ *reftag = 0xDEADBEEF;
+ phba->lpfc_injerr_wref_cnt--;
+ if (phba->lpfc_injerr_wref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_INIT;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9077 BLKGRD: Injecting reftag error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ }
+ }
+ if (phba->lpfc_injerr_rref_cnt) {
+ switch (op) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_READ_PASS:
+ /*
+ * For READ_STRIP and READ_PASS, force the
+ * error on data being read off the wire. It
+ * should force an IO error to the driver.
+ */
+ *reftag = 0xDEADBEEF;
+ phba->lpfc_injerr_rref_cnt--;
+ if (phba->lpfc_injerr_rref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_INIT;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9079 BLKGRD: Injecting reftag error: "
+ "read lba x%lx\n", (unsigned long)lba);
+ break;
+ }
+ }
+ }
+
+ /* Should we change the Application Tag */
+ if (apptag) {
+ if (phba->lpfc_injerr_wapp_cnt) {
+ switch (op) {
+ case SCSI_PROT_WRITE_PASS:
+ if (src) {
+ /*
+ * For WRITE_PASS, force the error
+ * to be sent on the wire. It should
+ * be detected by the Target.
+ * If blockoff != 0 error will be
+ * inserted in middle of the IO.
+ */
+
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "9080 BLKGRD: Injecting apptag error: "
+ "write lba x%lx + x%x oldappTag x%x\n",
+ (unsigned long)lba, blockoff,
+ be16_to_cpu(src->app_tag));
+
+ /*
+ * Save the old app_tag so we can
+ * restore it on completion.
+ */
+ if (lpfc_cmd) {
+ lpfc_cmd->prot_data_type =
+ LPFC_INJERR_APPTAG;
+ lpfc_cmd->prot_data_segment =
+ src;
+ lpfc_cmd->prot_data =
+ src->app_tag;
+ }
+ src->app_tag = cpu_to_be16(0xDEAD);
+ phba->lpfc_injerr_wapp_cnt--;
+ if (phba->lpfc_injerr_wapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
+ break;
+ }
+ fallthrough;
+ case SCSI_PROT_WRITE_INSERT:
+ /*
+ * For WRITE_INSERT, force the
+ * error to be sent on the wire. It should be
+ * detected by the Target.
+ */
+ /* DEAD will be the apptag on the wire */
+ *apptag = 0xDEAD;
+ phba->lpfc_injerr_wapp_cnt--;
+ if (phba->lpfc_injerr_wapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0813 BLKGRD: Injecting apptag error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ case SCSI_PROT_WRITE_STRIP:
+ /*
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
+ */
+ *apptag = 0xDEAD;
+ phba->lpfc_injerr_wapp_cnt--;
+ if (phba->lpfc_injerr_wapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_INIT;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0812 BLKGRD: Injecting apptag error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ }
+ }
+ if (phba->lpfc_injerr_rapp_cnt) {
+ switch (op) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_READ_PASS:
+ /*
+ * For READ_STRIP and READ_PASS, force the
+ * error on data being read off the wire. It
+ * should force an IO error to the driver.
+ */
+ *apptag = 0xDEAD;
+ phba->lpfc_injerr_rapp_cnt--;
+ if (phba->lpfc_injerr_rapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_INIT;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0814 BLKGRD: Injecting apptag error: "
+ "read lba x%lx\n", (unsigned long)lba);
+ break;
+ }
+ }
+ }
+
+
+ /* Should we change the Guard Tag */
+ if (new_guard) {
+ if (phba->lpfc_injerr_wgrd_cnt) {
+ switch (op) {
+ case SCSI_PROT_WRITE_PASS:
+ rc = BG_ERR_CHECK;
+ fallthrough;
+
+ case SCSI_PROT_WRITE_INSERT:
+ /*
+ * For WRITE_INSERT, force the
+ * error to be sent on the wire. It should be
+ * detected by the Target.
+ */
+ phba->lpfc_injerr_wgrd_cnt--;
+ if (phba->lpfc_injerr_wgrd_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+
+ rc |= BG_ERR_TGT | BG_ERR_SWAP;
+ /* Signals the caller to swap CRC->CSUM */
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0817 BLKGRD: Injecting guard error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ case SCSI_PROT_WRITE_STRIP:
+ /*
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
+ */
+ phba->lpfc_injerr_wgrd_cnt--;
+ if (phba->lpfc_injerr_wgrd_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+
+ rc = BG_ERR_INIT | BG_ERR_SWAP;
+ /* Signals the caller to swap CRC->CSUM */
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0816 BLKGRD: Injecting guard error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ }
+ }
+ if (phba->lpfc_injerr_rgrd_cnt) {
+ switch (op) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_READ_PASS:
+ /*
+ * For READ_STRIP and READ_PASS, force the
+ * error on data being read off the wire. It
+ * should force an IO error to the driver.
+ */
+ phba->lpfc_injerr_rgrd_cnt--;
+ if (phba->lpfc_injerr_rgrd_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+
+ rc = BG_ERR_INIT | BG_ERR_SWAP;
+ /* Signals the caller to swap CRC->CSUM */
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0818 BLKGRD: Injecting guard error: "
+ "read lba x%lx\n", (unsigned long)lba);
+ }
+ }
+ }
+
+ return rc;
+}
+#endif
+
+/**
+ * lpfc_sc_to_bg_opcodes - Determine the BlockGuard opcodes to be used with
+ * the specified SCSI command.
+ * @phba: The Hba for which this call is being executed.
* @sc: The SCSI command to examine
- * @txopt: (out) BlockGuard operation for transmitted data
- * @rxopt: (out) BlockGuard operation for received data
+ * @txop: (out) BlockGuard operation for transmitted data
+ * @rxop: (out) BlockGuard operation for received data
*
* Returns: zero on success; non-zero if tx and/or rx op cannot be determined
*
- */
+ **/
static int
lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint8_t *txop, uint8_t *rxop)
{
- uint8_t guard_type = scsi_host_get_guard(sc->device->host);
uint8_t ret = 0;
- if (guard_type == SHOST_DIX_GUARD_IP) {
+ if (sc->prot_flags & SCSI_PROT_IP_CHECKSUM) {
switch (scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
- *txop = BG_OP_IN_CSUM_OUT_NODIF;
*rxop = BG_OP_IN_NODIF_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_NODIF;
break;
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
- *txop = BG_OP_IN_NODIF_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CRC;
break;
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
- *txop = BG_OP_IN_CSUM_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_CRC;
break;
case SCSI_PROT_NORMAL:
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9063 BLKGRD: Bad op/guard:%d/%d combination\n",
- scsi_get_prot_op(sc), guard_type);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9063 BLKGRD: Bad op/guard:%d/IP combination\n",
+ scsi_get_prot_op(sc));
ret = 1;
break;
}
- } else if (guard_type == SHOST_DIX_GUARD_CRC) {
+ } else {
switch (scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
- *txop = BG_OP_IN_NODIF_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CRC;
break;
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
- *txop = BG_OP_IN_CRC_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_CRC;
break;
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
+ *rxop = BG_OP_IN_NODIF_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_NODIF;
+ break;
+
case SCSI_PROT_NORMAL:
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9075 BLKGRD: Bad op/guard:%d/%d combination\n",
- scsi_get_prot_op(sc), guard_type);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9075 BLKGRD: Bad op/guard:%d/CRC combination\n",
+ scsi_get_prot_op(sc));
ret = 1;
break;
}
- } else {
- /* unsupported format */
- BUG();
}
return ret;
}
-struct scsi_dif_tuple {
- __be16 guard_tag; /* Checksum */
- __be16 app_tag; /* Opaque storage */
- __be32 ref_tag; /* Target LBA or indirect LBA */
-};
-
-static inline unsigned
-lpfc_cmd_blksize(struct scsi_cmnd *sc)
-{
- return sc->device->sector_size;
-}
-
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
- * lpfc_get_cmd_dif_parms - Extract DIF parameters from SCSI command
- * @sc: in: SCSI command
- * @apptagmask: out: app tag mask
- * @apptagval: out: app tag value
- * @reftag: out: ref tag (reference tag)
+ * lpfc_bg_err_opcodes - reDetermine the BlockGuard opcodes to be used with
+ * the specified SCSI command in order to force a guard tag error.
+ * @phba: The Hba for which this call is being executed.
+ * @sc: The SCSI command to examine
+ * @txop: (out) BlockGuard operation for transmitted data
+ * @rxop: (out) BlockGuard operation for received data
*
- * Description:
- * Extract DIF parameters from the command if possible. Otherwise,
- * use default parameters.
+ * Returns: zero on success; non-zero if tx and/or rx op cannot be determined
*
**/
-static inline void
-lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask,
- uint16_t *apptagval, uint32_t *reftag)
+static int
+lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ uint8_t *txop, uint8_t *rxop)
{
- struct scsi_dif_tuple *spt;
- unsigned char op = scsi_get_prot_op(sc);
- unsigned int protcnt = scsi_prot_sg_count(sc);
- static int cnt;
- if (protcnt && (op == SCSI_PROT_WRITE_STRIP ||
- op == SCSI_PROT_WRITE_PASS)) {
+ if (sc->prot_flags & SCSI_PROT_IP_CHECKSUM) {
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ *rxop = BG_OP_IN_NODIF_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_NODIF;
+ break;
+
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ *rxop = BG_OP_IN_CSUM_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CSUM;
+ break;
- cnt++;
- spt = page_address(sg_page(scsi_prot_sglist(sc))) +
- scsi_prot_sglist(sc)[0].offset;
- *apptagmask = 0;
- *apptagval = 0;
- *reftag = cpu_to_be32(spt->ref_tag);
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ *rxop = BG_OP_IN_CSUM_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_CSUM;
+ break;
+ case SCSI_PROT_NORMAL:
+ default:
+ break;
+
+ }
} else {
- /* SBC defines ref tag to be lower 32bits of LBA */
- *reftag = (uint32_t) (0xffffffff & scsi_get_lba(sc));
- *apptagmask = 0;
- *apptagval = 0;
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ *rxop = BG_OP_IN_CSUM_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CSUM;
+ break;
+
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ *rxop = BG_OP_IN_CSUM_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_CSUM;
+ break;
+
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ *rxop = BG_OP_IN_NODIF_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_NODIF;
+ break;
+
+ case SCSI_PROT_NORMAL:
+ default:
+ break;
+ }
}
+
+ return 0;
}
+#endif
-/*
- * This function sets up buffer list for protection groups of
+/**
+ * lpfc_bg_setup_bpl - Setup BlockGuard BPL with no protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datasegcnt: number of segments of data that have been dma mapped
+ *
+ * This function sets up BPL buffer list for protection groups of
* type LPFC_PG_TYPE_NO_DIF
*
* This is usually used when the HBA is instructed to generate
@@ -1411,12 +1530,11 @@ lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask,
* |more Data BDE's ... (opt)|
* +-------------------------+
*
- * @sc: pointer to scsi command we're working on
- * @bpl: pointer to buffer list for protection groups
- * @datacnt: number of segments of data that have been dma mapped
*
* Note: Data s/g buffers have been dma mapped
- */
+ *
+ * Returns the number of BDEs added to the BPL.
+ **/
static int
lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
struct ulp_bde64 *bpl, int datasegcnt)
@@ -1427,9 +1545,11 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
dma_addr_t physaddr;
int i = 0, num_bde = 0, status;
int datadir = sc->sc_data_direction;
- unsigned blksize;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint32_t rc;
+#endif
+ uint32_t checking = 1;
uint32_t reftag;
- uint16_t apptagmask, apptagval;
uint8_t txop, rxop;
status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
@@ -1437,18 +1557,26 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
goto out;
/* extract some info from the scsi command for pde*/
- blksize = lpfc_cmd_blksize(sc);
- lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+ reftag = scsi_prot_ref_tag(sc);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
+ if (rc) {
+ if (rc & BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc & BG_ERR_CHECK)
+ checking = 0;
+ }
+#endif
/* setup PDE5 with what we have */
pde5 = (struct lpfc_pde5 *) bpl;
memset(pde5, 0, sizeof(struct lpfc_pde5));
bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
- pde5->reftag = reftag;
/* Endianness conversion if necessary for PDE5 */
pde5->word0 = cpu_to_le32(pde5->word0);
- pde5->reftag = cpu_to_le32(pde5->reftag);
+ pde5->reftag = cpu_to_le32(reftag);
/* advance bpl and increment bde count */
num_bde++;
@@ -1460,13 +1588,25 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR);
bf_set(pde6_optx, pde6, txop);
bf_set(pde6_oprx, pde6, rxop);
+
+ /*
+ * We only need to check the data on READs, for WRITEs
+ * protection data is automatically generated, not checked.
+ */
if (datadir == DMA_FROM_DEVICE) {
- bf_set(pde6_ce, pde6, 1);
- bf_set(pde6_re, pde6, 1);
- bf_set(pde6_ae, pde6, 1);
+ if (sc->prot_flags & SCSI_PROT_GUARD_CHECK)
+ bf_set(pde6_ce, pde6, checking);
+ else
+ bf_set(pde6_ce, pde6, 0);
+
+ if (sc->prot_flags & SCSI_PROT_REF_CHECK)
+ bf_set(pde6_re, pde6, checking);
+ else
+ bf_set(pde6_re, pde6, 0);
}
bf_set(pde6_ai, pde6, 1);
- bf_set(pde6_apptagval, pde6, apptagval);
+ bf_set(pde6_ae, pde6, 0);
+ bf_set(pde6_apptagval, pde6, 0);
/* Endianness conversion if necessary for PDE6 */
pde6->word0 = cpu_to_le32(pde6->word0);
@@ -1496,9 +1636,16 @@ out:
return num_bde;
}
-/*
- * This function sets up buffer list for protection groups of
- * type LPFC_PG_TYPE_DIF_BUF
+/**
+ * lpfc_bg_setup_bpl_prot - Setup BlockGuard BPL with protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * This function sets up BPL buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF
*
* This is usually used when DIFs are in their own buffers,
* separate from the data. The HBA can then by instructed
@@ -1523,14 +1670,11 @@ out:
* | ... |
* +-------------------------+
*
- * @sc: pointer to scsi command we're working on
- * @bpl: pointer to buffer list for protection groups
- * @datacnt: number of segments of data that have been dma mapped
- * @protcnt: number of segment of protection data that have been dma mapped
- *
* Note: It is assumed that both data and protection s/g buffers have been
* mapped for DMA
- */
+ *
+ * Returns the number of BDEs added to the BPL.
+ **/
static int
lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
struct ulp_bde64 *bpl, int datacnt, int protcnt)
@@ -1541,7 +1685,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
struct lpfc_pde6 *pde6 = NULL;
struct lpfc_pde7 *pde7 = NULL;
dma_addr_t dataphysaddr, protphysaddr;
- unsigned short curr_data = 0, curr_prot = 0;
+ unsigned short curr_prot = 0;
unsigned int split_offset;
unsigned int protgroup_len, protgroup_offset = 0, protgroup_remainder;
unsigned int protgrp_blks, protgrp_bytes;
@@ -1550,8 +1694,11 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
int datadir = sc->sc_data_direction;
unsigned char pgdone = 0, alldone = 0;
unsigned blksize;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint32_t rc;
+#endif
+ uint32_t checking = 1;
uint32_t reftag;
- uint16_t apptagmask, apptagval;
uint8_t txop, rxop;
int num_bde = 0;
@@ -1559,8 +1706,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
sgde = scsi_sglist(sc);
if (!sgpe || !sgde) {
- lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
- "9020 Invalid s/g entry: data=0x%p prot=0x%p\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9020 Invalid s/g entry: data=x%px prot=x%px\n",
sgpe, sgde);
return 0;
}
@@ -1570,20 +1717,33 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
goto out;
/* extract some info from the scsi command */
- blksize = lpfc_cmd_blksize(sc);
- lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+ blksize = scsi_prot_interval(sc);
+ reftag = scsi_prot_ref_tag(sc);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
+ if (rc) {
+ if (rc & BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc & BG_ERR_CHECK)
+ checking = 0;
+ }
+#endif
split_offset = 0;
do {
+ /* Check to see if we ran out of space */
+ if (num_bde >= (phba->cfg_total_seg_cnt - 2))
+ return num_bde + 3;
+
/* setup PDE5 with what we have */
pde5 = (struct lpfc_pde5 *) bpl;
memset(pde5, 0, sizeof(struct lpfc_pde5));
bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);
- pde5->reftag = reftag;
/* Endianness conversion if necessary for PDE5 */
pde5->word0 = cpu_to_le32(pde5->word0);
- pde5->reftag = cpu_to_le32(pde5->reftag);
+ pde5->reftag = cpu_to_le32(reftag);
/* advance bpl and increment bde count */
num_bde++;
@@ -1595,11 +1755,20 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR);
bf_set(pde6_optx, pde6, txop);
bf_set(pde6_oprx, pde6, rxop);
- bf_set(pde6_ce, pde6, 1);
- bf_set(pde6_re, pde6, 1);
- bf_set(pde6_ae, pde6, 1);
+
+ if (sc->prot_flags & SCSI_PROT_GUARD_CHECK)
+ bf_set(pde6_ce, pde6, checking);
+ else
+ bf_set(pde6_ce, pde6, 0);
+
+ if (sc->prot_flags & SCSI_PROT_REF_CHECK)
+ bf_set(pde6_re, pde6, checking);
+ else
+ bf_set(pde6_re, pde6, 0);
+
bf_set(pde6_ai, pde6, 1);
- bf_set(pde6_apptagval, pde6, apptagval);
+ bf_set(pde6_ae, pde6, 0);
+ bf_set(pde6_apptagval, pde6, 0);
/* Endianness conversion if necessary for PDE6 */
pde6->word0 = cpu_to_le32(pde6->word0);
@@ -1621,8 +1790,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
memset(pde7, 0, sizeof(struct lpfc_pde7));
bf_set(pde7_type, pde7, LPFC_PDE7_DESCRIPTOR);
- pde7->addrHigh = le32_to_cpu(putPaddrLow(protphysaddr));
- pde7->addrLow = le32_to_cpu(putPaddrHigh(protphysaddr));
+ pde7->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr));
+ pde7->addrLow = le32_to_cpu(putPaddrLow(protphysaddr));
protgrp_blks = protgroup_len / 8;
protgrp_bytes = protgrp_blks * blksize;
@@ -1632,7 +1801,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
protgroup_remainder = 0x1000 - (pde7->addrLow & 0xfff);
protgroup_offset += protgroup_remainder;
protgrp_blks = protgroup_remainder / 8;
- protgrp_bytes = protgroup_remainder * blksize;
+ protgrp_bytes = protgrp_blks * blksize;
} else {
protgroup_offset = 0;
curr_prot++;
@@ -1644,8 +1813,12 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
pgdone = 0;
subtotal = 0; /* total bytes processed for current prot grp */
while (!pgdone) {
+ /* Check to see if we ran out of space */
+ if (num_bde >= phba->cfg_total_seg_cnt)
+ return num_bde + 1;
+
if (!sgde) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"9065 BLKGRD:%s Invalid data segment\n",
__func__);
return 0;
@@ -1679,7 +1852,6 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bpl->tus.w = le32_to_cpu(bpl->tus.w);
num_bde++;
- curr_data++;
if (split_offset)
break;
@@ -1708,24 +1880,537 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
reftag += protgrp_blks;
} else {
/* if we're here, we have a bug */
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9054 BLKGRD: bug in %s\n", __func__);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9054 BLKGRD: bug in %s\n", __func__);
}
} while (!alldone);
-
out:
return num_bde;
}
-/*
+/**
+ * lpfc_bg_setup_sgl - Setup BlockGuard SGL with no protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @sgl: pointer to buffer list for protection groups
+ * @datasegcnt: number of segments of data that have been dma mapped
+ * @lpfc_cmd: lpfc scsi command object pointer.
+ *
+ * This function sets up SGL buffer list for protection groups of
+ * type LPFC_PG_TYPE_NO_DIF
+ *
+ * This is usually used when the HBA is instructed to generate
+ * DIFs and insert them into data stream (or strip DIF from
+ * incoming data stream)
+ *
+ * The buffer list consists of just one protection group described
+ * below:
+ * +-------------------------+
+ * start of prot group --> | DI_SEED |
+ * +-------------------------+
+ * | Data SGE |
+ * +-------------------------+
+ * |more Data SGE's ... (opt)|
+ * +-------------------------+
+ *
+ *
+ * Note: Data s/g buffers have been dma mapped
+ *
+ * Returns the number of SGEs added to the SGL.
+ **/
+static uint32_t
+lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct sli4_sge *sgl, int datasegcnt,
+ struct lpfc_io_buf *lpfc_cmd)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct sli4_sge_diseed *diseed = NULL;
+ dma_addr_t physaddr;
+ int i = 0, status;
+ uint32_t reftag, num_sge = 0;
+ uint8_t txop, rxop;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint32_t rc;
+#endif
+ uint32_t checking = 1;
+ uint32_t dma_len;
+ uint32_t dma_offset = 0;
+ struct sli4_hybrid_sgl *sgl_xtra = NULL;
+ int j;
+ bool lsp_just_set = false;
+
+ status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
+ if (status)
+ goto out;
+
+ /* extract some info from the scsi command for pde*/
+ reftag = scsi_prot_ref_tag(sc);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
+ if (rc) {
+ if (rc & BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc & BG_ERR_CHECK)
+ checking = 0;
+ }
+#endif
+
+ /* setup DISEED with what we have */
+ diseed = (struct sli4_sge_diseed *) sgl;
+ memset(diseed, 0, sizeof(struct sli4_sge_diseed));
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DISEED);
+
+ /* Endianness conversion if necessary */
+ diseed->ref_tag = cpu_to_le32(reftag);
+ diseed->ref_tag_tran = diseed->ref_tag;
+
+ /*
+ * We only need to check the data on READs, for WRITEs
+ * protection data is automatically generated, not checked.
+ */
+ if (sc->sc_data_direction == DMA_FROM_DEVICE) {
+ if (sc->prot_flags & SCSI_PROT_GUARD_CHECK)
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
+ else
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, 0);
+
+ if (sc->prot_flags & SCSI_PROT_REF_CHECK)
+ bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
+ else
+ bf_set(lpfc_sli4_sge_dif_re, diseed, 0);
+ }
+
+ /* setup DISEED with the rest of the info */
+ bf_set(lpfc_sli4_sge_dif_optx, diseed, txop);
+ bf_set(lpfc_sli4_sge_dif_oprx, diseed, rxop);
+
+ bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
+ bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
+
+ /* Endianness conversion if necessary for DISEED */
+ diseed->word2 = cpu_to_le32(diseed->word2);
+ diseed->word3 = cpu_to_le32(diseed->word3);
+
+ /* advance bpl and increment sge count */
+ num_sge++;
+ sgl++;
+
+ /* assumption: caller has already run dma_map_sg on command data */
+ sgde = scsi_sglist(sc);
+ j = 3;
+ for (i = 0; i < datasegcnt; i++) {
+ /* clear it */
+ sgl->word2 = 0;
+
+ /* do we need to expand the segment */
+ if (!lsp_just_set && !((j + 1) % phba->border_sge_num) &&
+ ((datasegcnt - 1) != i)) {
+ /* set LSP type */
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_LSP);
+
+ sgl_xtra = lpfc_get_sgl_per_hdwq(phba, lpfc_cmd);
+
+ if (unlikely(!sgl_xtra)) {
+ lpfc_cmd->seg_cnt = 0;
+ return 0;
+ }
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(
+ sgl_xtra->dma_phys_sgl));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(
+ sgl_xtra->dma_phys_sgl));
+
+ } else {
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA);
+ }
+
+ if (!(bf_get(lpfc_sli4_sge_type, sgl) & LPFC_SGE_TYPE_LSP)) {
+ if ((datasegcnt - 1) == i)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ physaddr = sg_dma_address(sgde);
+ dma_len = sg_dma_len(sgde);
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
+
+ bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(dma_len);
+
+ dma_offset += dma_len;
+ sgde = sg_next(sgde);
+
+ sgl++;
+ num_sge++;
+ lsp_just_set = false;
+
+ } else {
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(phba->cfg_sg_dma_buf_size);
+
+ sgl = (struct sli4_sge *)sgl_xtra->dma_sgl;
+ i = i - 1;
+
+ lsp_just_set = true;
+ }
+
+ j++;
+
+ }
+
+out:
+ return num_sge;
+}
+
+/**
+ * lpfc_bg_setup_sgl_prot - Setup BlockGuard SGL with protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @sgl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ * @lpfc_cmd: lpfc scsi command object pointer.
+ *
+ * This function sets up SGL buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF
+ *
+ * This is usually used when DIFs are in their own buffers,
+ * separate from the data. The HBA can then by instructed
+ * to place the DIFs in the outgoing stream. For read operations,
+ * The HBA could extract the DIFs and place it in DIF buffers.
+ *
+ * The buffer list for this type consists of one or more of the
+ * protection groups described below:
+ * +-------------------------+
+ * start of first prot group --> | DISEED |
+ * +-------------------------+
+ * | DIF (Prot SGE) |
+ * +-------------------------+
+ * | Data SGE |
+ * +-------------------------+
+ * |more Data SGE's ... (opt)|
+ * +-------------------------+
+ * start of new prot group --> | DISEED |
+ * +-------------------------+
+ * | ... |
+ * +-------------------------+
+ *
+ * Note: It is assumed that both data and protection s/g buffers have been
+ * mapped for DMA
+ *
+ * Returns the number of SGEs added to the SGL.
+ **/
+static uint32_t
+lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct sli4_sge *sgl, int datacnt, int protcnt,
+ struct lpfc_io_buf *lpfc_cmd)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct scatterlist *sgpe = NULL; /* s/g prot entry */
+ struct sli4_sge_diseed *diseed = NULL;
+ dma_addr_t dataphysaddr, protphysaddr;
+ unsigned short curr_prot = 0;
+ unsigned int split_offset;
+ unsigned int protgroup_len, protgroup_offset = 0, protgroup_remainder;
+ unsigned int protgrp_blks, protgrp_bytes;
+ unsigned int remainder, subtotal;
+ int status;
+ unsigned char pgdone = 0, alldone = 0;
+ unsigned blksize;
+ uint32_t reftag;
+ uint8_t txop, rxop;
+ uint32_t dma_len;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint32_t rc;
+#endif
+ uint32_t checking = 1;
+ uint32_t dma_offset = 0, num_sge = 0;
+ int j = 2;
+ struct sli4_hybrid_sgl *sgl_xtra = NULL;
+
+ sgpe = scsi_prot_sglist(sc);
+ sgde = scsi_sglist(sc);
+
+ if (!sgpe || !sgde) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9082 Invalid s/g entry: data=x%px prot=x%px\n",
+ sgpe, sgde);
+ return 0;
+ }
+
+ status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
+ if (status)
+ goto out;
+
+ /* extract some info from the scsi command */
+ blksize = scsi_prot_interval(sc);
+ reftag = scsi_prot_ref_tag(sc);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
+ if (rc) {
+ if (rc & BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc & BG_ERR_CHECK)
+ checking = 0;
+ }
+#endif
+
+ split_offset = 0;
+ do {
+ /* Check to see if we ran out of space */
+ if ((num_sge >= (phba->cfg_total_seg_cnt - 2)) &&
+ !(phba->cfg_xpsgl))
+ return num_sge + 3;
+
+ /* DISEED and DIF have to be together */
+ if (!((j + 1) % phba->border_sge_num) ||
+ !((j + 2) % phba->border_sge_num) ||
+ !((j + 3) % phba->border_sge_num)) {
+ sgl->word2 = 0;
+
+ /* set LSP type */
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_LSP);
+
+ sgl_xtra = lpfc_get_sgl_per_hdwq(phba, lpfc_cmd);
+
+ if (unlikely(!sgl_xtra)) {
+ goto out;
+ } else {
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(
+ sgl_xtra->dma_phys_sgl));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(
+ sgl_xtra->dma_phys_sgl));
+ }
+
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(phba->cfg_sg_dma_buf_size);
+
+ sgl = (struct sli4_sge *)sgl_xtra->dma_sgl;
+ j = 0;
+ }
+
+ /* setup DISEED with what we have */
+ diseed = (struct sli4_sge_diseed *) sgl;
+ memset(diseed, 0, sizeof(struct sli4_sge_diseed));
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DISEED);
+
+ /* Endianness conversion if necessary */
+ diseed->ref_tag = cpu_to_le32(reftag);
+ diseed->ref_tag_tran = diseed->ref_tag;
+
+ if (sc->prot_flags & SCSI_PROT_GUARD_CHECK) {
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
+ } else {
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, 0);
+ /*
+ * When in this mode, the hardware will replace
+ * the guard tag from the host with a
+ * newly generated good CRC for the wire.
+ * Switch to raw mode here to avoid this
+ * behavior. What the host sends gets put on the wire.
+ */
+ if (txop == BG_OP_IN_CRC_OUT_CRC) {
+ txop = BG_OP_RAW_MODE;
+ rxop = BG_OP_RAW_MODE;
+ }
+ }
+
+
+ if (sc->prot_flags & SCSI_PROT_REF_CHECK)
+ bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
+ else
+ bf_set(lpfc_sli4_sge_dif_re, diseed, 0);
+
+ /* setup DISEED with the rest of the info */
+ bf_set(lpfc_sli4_sge_dif_optx, diseed, txop);
+ bf_set(lpfc_sli4_sge_dif_oprx, diseed, rxop);
+
+ bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
+ bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
+
+ /* Endianness conversion if necessary for DISEED */
+ diseed->word2 = cpu_to_le32(diseed->word2);
+ diseed->word3 = cpu_to_le32(diseed->word3);
+
+ /* advance sgl and increment bde count */
+ num_sge++;
+
+ sgl++;
+ j++;
+
+ /* setup the first BDE that points to protection buffer */
+ protphysaddr = sg_dma_address(sgpe) + protgroup_offset;
+ protgroup_len = sg_dma_len(sgpe) - protgroup_offset;
+
+ /* must be integer multiple of the DIF block length */
+ BUG_ON(protgroup_len % 8);
+
+ /* Now setup DIF SGE */
+ sgl->word2 = 0;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DIF);
+ sgl->addr_hi = le32_to_cpu(putPaddrHigh(protphysaddr));
+ sgl->addr_lo = le32_to_cpu(putPaddrLow(protphysaddr));
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = 0;
+
+ protgrp_blks = protgroup_len / 8;
+ protgrp_bytes = protgrp_blks * blksize;
+
+ /* check if DIF SGE is crossing the 4K boundary; if so split */
+ if ((sgl->addr_lo & 0xfff) + protgroup_len > 0x1000) {
+ protgroup_remainder = 0x1000 - (sgl->addr_lo & 0xfff);
+ protgroup_offset += protgroup_remainder;
+ protgrp_blks = protgroup_remainder / 8;
+ protgrp_bytes = protgrp_blks * blksize;
+ } else {
+ protgroup_offset = 0;
+ curr_prot++;
+ }
+
+ num_sge++;
+
+ /* setup SGE's for data blocks associated with DIF data */
+ pgdone = 0;
+ subtotal = 0; /* total bytes processed for current prot grp */
+
+ sgl++;
+ j++;
+
+ while (!pgdone) {
+ /* Check to see if we ran out of space */
+ if ((num_sge >= phba->cfg_total_seg_cnt) &&
+ !phba->cfg_xpsgl)
+ return num_sge + 1;
+
+ if (!sgde) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9086 BLKGRD:%s Invalid data segment\n",
+ __func__);
+ return 0;
+ }
+
+ if (!((j + 1) % phba->border_sge_num)) {
+ sgl->word2 = 0;
+
+ /* set LSP type */
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_LSP);
+
+ sgl_xtra = lpfc_get_sgl_per_hdwq(phba,
+ lpfc_cmd);
+
+ if (unlikely(!sgl_xtra)) {
+ goto out;
+ } else {
+ sgl->addr_lo = cpu_to_le32(
+ putPaddrLow(sgl_xtra->dma_phys_sgl));
+ sgl->addr_hi = cpu_to_le32(
+ putPaddrHigh(sgl_xtra->dma_phys_sgl));
+ }
+
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(
+ phba->cfg_sg_dma_buf_size);
+
+ sgl = (struct sli4_sge *)sgl_xtra->dma_sgl;
+ } else {
+ dataphysaddr = sg_dma_address(sgde) +
+ split_offset;
+
+ remainder = sg_dma_len(sgde) - split_offset;
+
+ if ((subtotal + remainder) <= protgrp_bytes) {
+ /* we can use this whole buffer */
+ dma_len = remainder;
+ split_offset = 0;
+
+ if ((subtotal + remainder) ==
+ protgrp_bytes)
+ pgdone = 1;
+ } else {
+ /* must split this buffer with next
+ * prot grp
+ */
+ dma_len = protgrp_bytes - subtotal;
+ split_offset += dma_len;
+ }
+
+ subtotal += dma_len;
+
+ sgl->word2 = 0;
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(
+ dataphysaddr));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(
+ dataphysaddr));
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_DATA);
+
+ sgl->sge_len = cpu_to_le32(dma_len);
+ dma_offset += dma_len;
+
+ num_sge++;
+
+ if (split_offset) {
+ sgl++;
+ j++;
+ break;
+ }
+
+ /* Move to the next s/g segment if possible */
+ sgde = sg_next(sgde);
+
+ sgl++;
+ }
+
+ j++;
+ }
+
+ if (protgroup_offset) {
+ /* update the reference tag */
+ reftag += protgrp_blks;
+ continue;
+ }
+
+ /* are we done ? */
+ if (curr_prot == protcnt) {
+ /* mark the last SGL */
+ sgl--;
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ alldone = 1;
+ } else if (curr_prot < protcnt) {
+ /* advance to next prot buffer */
+ sgpe = sg_next(sgpe);
+
+ /* update the reference tag */
+ reftag += protgrp_blks;
+ } else {
+ /* if we're here, we have a bug */
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9085 BLKGRD: bug in %s\n", __func__);
+ }
+
+ } while (!alldone);
+
+out:
+
+ return num_sge;
+}
+
+/**
+ * lpfc_prot_group_type - Get prtotection group type of SCSI command
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ *
* Given a SCSI command that supports DIF, determine composition of protection
* groups involved in setting up buffer lists
*
- * Returns:
- * for DIF (for both read and write)
- * */
+ * Returns: Protection group type (with or without DIF)
+ *
+ **/
static int
lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
{
@@ -1744,32 +2429,82 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
ret = LPFC_PG_TYPE_DIF_BUF;
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
- "9021 Unsupported protection op:%d\n", op);
+ if (phba)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9021 Unsupported protection op:%d\n",
+ op);
break;
}
-
return ret;
}
-/*
+/**
+ * lpfc_bg_scsi_adjust_dl - Adjust SCSI data length for BlockGuard
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be adjusted.
+ *
+ * Adjust the data length to account for how much data
+ * is actually on the wire.
+ *
+ * returns the adjusted data length
+ **/
+static int
+lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba,
+ struct lpfc_io_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *sc = lpfc_cmd->pCmd;
+ int fcpdl;
+
+ fcpdl = scsi_bufflen(sc);
+
+ /* Check if there is protection data on the wire */
+ if (sc->sc_data_direction == DMA_FROM_DEVICE) {
+ /* Read check for protection data */
+ if (scsi_get_prot_op(sc) == SCSI_PROT_READ_INSERT)
+ return fcpdl;
+
+ } else {
+ /* Write check for protection data */
+ if (scsi_get_prot_op(sc) == SCSI_PROT_WRITE_STRIP)
+ return fcpdl;
+ }
+
+ /*
+ * If we are in DIF Type 1 mode every data block has a 8 byte
+ * DIF (trailer) attached to it. Must ajust FCP data length
+ * to account for the protection data.
+ */
+ fcpdl += (fcpdl / scsi_prot_interval(sc)) * 8;
+
+ return fcpdl;
+}
+
+/**
+ * lpfc_bg_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be prep'ed.
+ *
* This is the protection/DIF aware version of
* lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
- * two functions eventually, but for now, it's here
- */
+ * two functions eventually, but for now, it's here.
+ * RETURNS 0 - SUCCESS,
+ * 1 - Failed DMA map, retry.
+ * 2 - Invalid scsi cmd or prot-type. Do not rety.
+ **/
static int
-lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
- struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
+ struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ struct ulp_bde64 *bpl = (struct ulp_bde64 *)lpfc_cmd->dma_sgl;
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
uint32_t num_bde = 0;
int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
int prot_group_type = 0;
- int diflen, fcpdl;
- unsigned blksize;
+ int fcpdl;
+ int ret = 1;
+ struct lpfc_vport *vport = phba->pport;
/*
* Start the lpfc command prep by bumping the bpl beyond fcp_cmnd
@@ -1778,7 +2513,7 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
bpl += 2;
if (scsi_sg_count(scsi_cmnd)) {
/*
- * The driver stores the segment count returned from pci_map_sg
+ * The driver stores the segment count returned from dma_map_sg
* because this a count of dma-mappings used to map the use_sg
* pages. They are not guaranteed to be the same for those
* architectures that implement an IOMMU.
@@ -1790,28 +2525,35 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
return 1;
lpfc_cmd->seg_cnt = datasegcnt;
+
+ /* First check if data segment count from SCSI Layer is good */
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9067 BLKGRD: %s: Too many sg segments"
- " from dma_map_sg. Config %d, seg_cnt"
- " %d\n",
- __func__, phba->cfg_sg_seg_cnt,
- lpfc_cmd->seg_cnt);
- scsi_dma_unmap(scsi_cmnd);
- return 1;
+ WARN_ON_ONCE(lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt);
+ ret = 2;
+ goto err;
}
prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
switch (prot_group_type) {
case LPFC_PG_TYPE_NO_DIF:
+
+ /* Here we need to add a PDE5 and PDE6 to the count */
+ if ((lpfc_cmd->seg_cnt + 2) > phba->cfg_total_seg_cnt) {
+ ret = 2;
+ goto err;
+ }
+
num_bde = lpfc_bg_setup_bpl(phba, scsi_cmnd, bpl,
datasegcnt);
/* we should have 2 or more entries in buffer list */
- if (num_bde < 2)
+ if (num_bde < 2) {
+ ret = 2;
goto err;
+ }
break;
- case LPFC_PG_TYPE_DIF_BUF:{
+
+ case LPFC_PG_TYPE_DIF_BUF:
/*
* This type indicates that protection buffers are
* passed to the driver, so that needs to be prepared
@@ -1826,35 +2568,36 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
}
lpfc_cmd->prot_seg_cnt = protsegcnt;
- if (lpfc_cmd->prot_seg_cnt
- > phba->cfg_prot_sg_seg_cnt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9068 BLKGRD: %s: Too many prot sg "
- "segments from dma_map_sg. Config %d,"
- "prot_seg_cnt %d\n", __func__,
- phba->cfg_prot_sg_seg_cnt,
- lpfc_cmd->prot_seg_cnt);
- dma_unmap_sg(&phba->pcidev->dev,
- scsi_prot_sglist(scsi_cmnd),
- scsi_prot_sg_count(scsi_cmnd),
- datadir);
- scsi_dma_unmap(scsi_cmnd);
- return 1;
+
+ /*
+ * There is a minimun of 4 BPLs used for every
+ * protection data segment.
+ */
+ if ((lpfc_cmd->prot_seg_cnt * 4) >
+ (phba->cfg_total_seg_cnt - 2)) {
+ ret = 2;
+ goto err;
}
num_bde = lpfc_bg_setup_bpl_prot(phba, scsi_cmnd, bpl,
datasegcnt, protsegcnt);
/* we should have 3 or more entries in buffer list */
- if (num_bde < 3)
+ if ((num_bde < 3) ||
+ (num_bde > phba->cfg_total_seg_cnt)) {
+ ret = 2;
goto err;
+ }
break;
- }
+
case LPFC_PG_TYPE_INVALID:
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ scsi_dma_unmap(scsi_cmnd);
+ lpfc_cmd->seg_cnt = 0;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"9022 Unexpected protection group %i\n",
prot_group_type);
- return 1;
+ return 2;
}
}
@@ -1869,19 +2612,8 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
iocb_cmd->ulpBdeCount = 1;
iocb_cmd->ulpLe = 1;
- fcpdl = scsi_bufflen(scsi_cmnd);
-
- if (scsi_get_prot_type(scsi_cmnd) == SCSI_PROT_DIF_TYPE1) {
- /*
- * We are in DIF Type 1 mode
- * Every data block has a 8 byte DIF (trailer)
- * attached to it. Must ajust FCP data length
- */
- blksize = lpfc_cmd_blksize(scsi_cmnd);
- diflen = (fcpdl / blksize) * 8;
- fcpdl += diflen;
- }
- fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
+ fcpdl = lpfc_bg_scsi_adjust_dl(phba, lpfc_cmd);
+ fcp_cmnd->fcpDl = cpu_to_be32(fcpdl);
/*
* Due to difference in data length between DIF/non-DIF paths,
@@ -1889,13 +2621,234 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
*/
iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
+ /*
+ * For First burst, we may need to adjust the initial transfer
+ * length for DIF
+ */
+ if (iocb_cmd->un.fcpi.fcpi_XRdy &&
+ (fcpdl < vport->cfg_first_burst_size))
+ iocb_cmd->un.fcpi.fcpi_XRdy = fcpdl;
+
return 0;
err:
- lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
- "9023 Could not setup all needed BDE's"
- "prot_group_type=%d, num_bde=%d\n",
+ if (lpfc_cmd->seg_cnt)
+ scsi_dma_unmap(scsi_cmnd);
+ if (lpfc_cmd->prot_seg_cnt)
+ dma_unmap_sg(&phba->pcidev->dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ scsi_cmnd->sc_data_direction);
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9023 Cannot setup S/G List for HBA"
+ "IO segs %d/%d BPL %d SCSI %d: %d %d\n",
+ lpfc_cmd->seg_cnt, lpfc_cmd->prot_seg_cnt,
+ phba->cfg_total_seg_cnt, phba->cfg_sg_seg_cnt,
prot_group_type, num_bde);
- return 1;
+
+ lpfc_cmd->seg_cnt = 0;
+ lpfc_cmd->prot_seg_cnt = 0;
+ return ret;
+}
+
+/*
+ * This function calcuates the T10 DIF guard tag
+ * on the specified data using a CRC algorithmn
+ * using crc_t10dif.
+ */
+static uint16_t
+lpfc_bg_crc(uint8_t *data, int count)
+{
+ uint16_t crc = 0;
+ uint16_t x;
+
+ crc = crc_t10dif(data, count);
+ x = cpu_to_be16(crc);
+ return x;
+}
+
+/*
+ * This function calcuates the T10 DIF guard tag
+ * on the specified data using a CSUM algorithmn
+ * using ip_compute_csum.
+ */
+static uint16_t
+lpfc_bg_csum(uint8_t *data, int count)
+{
+ uint16_t ret;
+
+ ret = ip_compute_csum(data, count);
+ return ret;
+}
+
+/*
+ * This function examines the protection data to try to determine
+ * what type of T10-DIF error occurred.
+ */
+static void
+lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
+{
+ struct scatterlist *sgpe; /* s/g prot entry */
+ struct scatterlist *sgde; /* s/g data entry */
+ struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
+ struct scsi_dif_tuple *src = NULL;
+ uint8_t *data_src = NULL;
+ uint16_t guard_tag;
+ uint16_t start_app_tag, app_tag;
+ uint32_t start_ref_tag, ref_tag;
+ int prot, protsegcnt;
+ int err_type, len, data_len;
+ int chk_ref, chk_app, chk_guard;
+ uint16_t sum;
+ unsigned blksize;
+
+ err_type = BGS_GUARD_ERR_MASK;
+ sum = 0;
+ guard_tag = 0;
+
+ /* First check to see if there is protection data to examine */
+ prot = scsi_get_prot_op(cmd);
+ if ((prot == SCSI_PROT_READ_STRIP) ||
+ (prot == SCSI_PROT_WRITE_INSERT) ||
+ (prot == SCSI_PROT_NORMAL))
+ goto out;
+
+ /* Currently the driver just supports ref_tag and guard_tag checking */
+ chk_ref = 1;
+ chk_app = 0;
+ chk_guard = 0;
+
+ /* Setup a ptr to the protection data provided by the SCSI host */
+ sgpe = scsi_prot_sglist(cmd);
+ protsegcnt = lpfc_cmd->prot_seg_cnt;
+
+ if (sgpe && protsegcnt) {
+
+ /*
+ * We will only try to verify guard tag if the segment
+ * data length is a multiple of the blksize.
+ */
+ sgde = scsi_sglist(cmd);
+ blksize = scsi_prot_interval(cmd);
+ data_src = (uint8_t *)sg_virt(sgde);
+ data_len = sg_dma_len(sgde);
+ if ((data_len & (blksize - 1)) == 0)
+ chk_guard = 1;
+
+ src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+ start_ref_tag = scsi_prot_ref_tag(cmd);
+ start_app_tag = src->app_tag;
+ len = sg_dma_len(sgpe);
+ while (src && protsegcnt) {
+ while (len) {
+
+ /*
+ * First check to see if a protection data
+ * check is valid
+ */
+ if ((src->ref_tag == T10_PI_REF_ESCAPE) ||
+ (src->app_tag == T10_PI_APP_ESCAPE)) {
+ start_ref_tag++;
+ goto skipit;
+ }
+
+ /* First Guard Tag checking */
+ if (chk_guard) {
+ guard_tag = src->guard_tag;
+ if (cmd->prot_flags
+ & SCSI_PROT_IP_CHECKSUM)
+ sum = lpfc_bg_csum(data_src,
+ blksize);
+ else
+ sum = lpfc_bg_crc(data_src,
+ blksize);
+ if ((guard_tag != sum)) {
+ err_type = BGS_GUARD_ERR_MASK;
+ goto out;
+ }
+ }
+
+ /* Reference Tag checking */
+ ref_tag = be32_to_cpu(src->ref_tag);
+ if (chk_ref && (ref_tag != start_ref_tag)) {
+ err_type = BGS_REFTAG_ERR_MASK;
+ goto out;
+ }
+ start_ref_tag++;
+
+ /* App Tag checking */
+ app_tag = src->app_tag;
+ if (chk_app && (app_tag != start_app_tag)) {
+ err_type = BGS_APPTAG_ERR_MASK;
+ goto out;
+ }
+skipit:
+ len -= sizeof(struct scsi_dif_tuple);
+ if (len < 0)
+ len = 0;
+ src++;
+
+ data_src += blksize;
+ data_len -= blksize;
+
+ /*
+ * Are we at the end of the Data segment?
+ * The data segment is only used for Guard
+ * tag checking.
+ */
+ if (chk_guard && (data_len == 0)) {
+ chk_guard = 0;
+ sgde = sg_next(sgde);
+ if (!sgde)
+ goto out;
+
+ data_src = (uint8_t *)sg_virt(sgde);
+ data_len = sg_dma_len(sgde);
+ if ((data_len & (blksize - 1)) == 0)
+ chk_guard = 1;
+ }
+ }
+
+ /* Goto the next Protection data segment */
+ sgpe = sg_next(sgpe);
+ if (sgpe) {
+ src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+ len = sg_dma_len(sgpe);
+ } else {
+ src = NULL;
+ }
+ protsegcnt--;
+ }
+ }
+out:
+ if (err_type == BGS_GUARD_ERR_MASK) {
+ scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x1);
+ set_host_byte(cmd, DID_ABORT);
+ phba->bg_guard_err_cnt++;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9069 BLKGRD: reftag %x grd_tag err %x != %x\n",
+ scsi_prot_ref_tag(cmd),
+ sum, guard_tag);
+
+ } else if (err_type == BGS_REFTAG_ERR_MASK) {
+ scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x3);
+ set_host_byte(cmd, DID_ABORT);
+
+ phba->bg_reftag_err_cnt++;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9066 BLKGRD: reftag %x ref_tag err %x != %x\n",
+ scsi_prot_ref_tag(cmd),
+ ref_tag, start_ref_tag);
+
+ } else if (err_type == BGS_APPTAG_ERR_MASK) {
+ scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x2);
+ set_host_byte(cmd, DID_ABORT);
+
+ phba->bg_apptag_err_cnt++;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9041 BLKGRD: reftag %x app_tag err %x != %x\n",
+ scsi_prot_ref_tag(cmd),
+ app_tag, start_app_tag);
+ }
}
/*
@@ -1911,120 +2864,168 @@ err:
* -1 - Internal error (bad profile, ...etc)
*/
static int
-lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
- struct lpfc_iocbq *pIocbOut)
+lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd,
+ struct lpfc_iocbq *pIocbOut)
{
struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
- struct sli3_bg_fields *bgf = &pIocbOut->iocb.unsli3.sli3_bg;
+ struct sli3_bg_fields *bgf;
int ret = 0;
- uint32_t bghm = bgf->bghm;
- uint32_t bgstat = bgf->bgstat;
- uint64_t failing_sector = 0;
-
- lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9069 BLKGRD: BG ERROR in cmd"
- " 0x%x lba 0x%llx blk cnt 0x%x "
- "bgstat=0x%x bghm=0x%x\n",
- cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
- blk_rq_sectors(cmd->request), bgstat, bghm);
-
- spin_lock(&_dump_buf_lock);
- if (!_dump_buf_done) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9070 BLKGRD: Saving"
- " Data for %u blocks to debugfs\n",
- (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
- lpfc_debug_save_data(phba, cmd);
+ struct lpfc_wcqe_complete *wcqe;
+ u32 status;
+ u32 bghm = 0;
+ u32 bgstat = 0;
+ u64 failing_sector = 0;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ wcqe = &pIocbOut->wcqe_cmpl;
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+
+ if (status == CQE_STATUS_DI_ERROR) {
+ /* Guard Check failed */
+ if (bf_get(lpfc_wcqe_c_bg_ge, wcqe))
+ bgstat |= BGS_GUARD_ERR_MASK;
+
+ /* AppTag Check failed */
+ if (bf_get(lpfc_wcqe_c_bg_ae, wcqe))
+ bgstat |= BGS_APPTAG_ERR_MASK;
+
+ /* RefTag Check failed */
+ if (bf_get(lpfc_wcqe_c_bg_re, wcqe))
+ bgstat |= BGS_REFTAG_ERR_MASK;
+
+ /* Check to see if there was any good data before the
+ * error
+ */
+ if (bf_get(lpfc_wcqe_c_bg_tdpv, wcqe)) {
+ bgstat |= BGS_HI_WATER_MARK_PRESENT_MASK;
+ bghm = wcqe->total_data_placed;
+ }
- /* If we have a prot sgl, save the DIF buffer */
- if (lpfc_prot_group_type(phba, cmd) ==
- LPFC_PG_TYPE_DIF_BUF) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9071 BLKGRD: "
- "Saving DIF for %u blocks to debugfs\n",
- (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
- lpfc_debug_save_dif(phba, cmd);
+ /*
+ * Set ALL the error bits to indicate we don't know what
+ * type of error it is.
+ */
+ if (!bgstat)
+ bgstat |= (BGS_REFTAG_ERR_MASK |
+ BGS_APPTAG_ERR_MASK |
+ BGS_GUARD_ERR_MASK);
}
- _dump_buf_done = 1;
+ } else {
+ bgf = &pIocbOut->iocb.unsli3.sli3_bg;
+ bghm = bgf->bghm;
+ bgstat = bgf->bgstat;
}
- spin_unlock(&_dump_buf_lock);
if (lpfc_bgs_get_invalid_prof(bgstat)) {
- cmd->result = ScsiResult(DID_ERROR, 0);
- lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9072 BLKGRD: Invalid"
- " BlockGuard profile. bgstat:0x%x\n",
- bgstat);
+ cmd->result = DID_ERROR << 16;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9072 BLKGRD: Invalid BG Profile in cmd "
+ "0x%x reftag 0x%x blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ scsi_prot_ref_tag(cmd),
+ scsi_logical_block_count(cmd), bgstat, bghm);
ret = (-1);
goto out;
}
if (lpfc_bgs_get_uninit_dif_block(bgstat)) {
- cmd->result = ScsiResult(DID_ERROR, 0);
- lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9073 BLKGRD: "
- "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
- bgstat);
+ cmd->result = DID_ERROR << 16;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9073 BLKGRD: Invalid BG PDIF Block in cmd "
+ "0x%x reftag 0x%x blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ scsi_prot_ref_tag(cmd),
+ scsi_logical_block_count(cmd), bgstat, bghm);
ret = (-1);
goto out;
}
if (lpfc_bgs_get_guard_err(bgstat)) {
ret = 1;
-
- scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
- 0x10, 0x1);
- cmd->result = DRIVER_SENSE << 24
- | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+ scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x1);
+ set_host_byte(cmd, DID_ABORT);
phba->bg_guard_err_cnt++;
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9055 BLKGRD: guard_tag error\n");
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9055 BLKGRD: Guard Tag error in cmd "
+ "0x%x reftag 0x%x blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ scsi_prot_ref_tag(cmd),
+ scsi_logical_block_count(cmd), bgstat, bghm);
}
if (lpfc_bgs_get_reftag_err(bgstat)) {
ret = 1;
-
- scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
- 0x10, 0x3);
- cmd->result = DRIVER_SENSE << 24
- | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
-
+ scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x3);
+ set_host_byte(cmd, DID_ABORT);
phba->bg_reftag_err_cnt++;
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9056 BLKGRD: ref_tag error\n");
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9056 BLKGRD: Ref Tag error in cmd "
+ "0x%x reftag 0x%x blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ scsi_prot_ref_tag(cmd),
+ scsi_logical_block_count(cmd), bgstat, bghm);
}
if (lpfc_bgs_get_apptag_err(bgstat)) {
ret = 1;
-
- scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
- 0x10, 0x2);
- cmd->result = DRIVER_SENSE << 24
- | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
-
+ scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x2);
+ set_host_byte(cmd, DID_ABORT);
phba->bg_apptag_err_cnt++;
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9061 BLKGRD: app_tag error\n");
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9061 BLKGRD: App Tag error in cmd "
+ "0x%x reftag 0x%x blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ scsi_prot_ref_tag(cmd),
+ scsi_logical_block_count(cmd), bgstat, bghm);
}
if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
/*
* setup sense data descriptor 0 per SPC-4 as an information
- * field, and put the failing LBA in it
+ * field, and put the failing LBA in it.
+ * This code assumes there was also a guard/app/ref tag error
+ * indication.
*/
- cmd->sense_buffer[8] = 0; /* Information */
- cmd->sense_buffer[9] = 0xa; /* Add. length */
- bghm /= cmd->device->sector_size;
+ cmd->sense_buffer[7] = 0xc; /* Additional sense length */
+ cmd->sense_buffer[8] = 0; /* Information descriptor type */
+ cmd->sense_buffer[9] = 0xa; /* Additional descriptor length */
+ cmd->sense_buffer[10] = 0x80; /* Validity bit */
+
+ /* bghm is a "on the wire" FC frame based count */
+ switch (scsi_get_prot_op(cmd)) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ bghm /= cmd->device->sector_size;
+ break;
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ bghm /= (cmd->device->sector_size +
+ sizeof(struct scsi_dif_tuple));
+ break;
+ }
failing_sector = scsi_get_lba(cmd);
failing_sector += bghm;
- put_unaligned_be64(failing_sector, &cmd->sense_buffer[10]);
+ /* Descriptor Information */
+ put_unaligned_be64(failing_sector, &cmd->sense_buffer[12]);
}
if (!ret) {
/* No error was reported - problem in FW? */
- cmd->result = ScsiResult(DID_ERROR, 0);
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9057 BLKGRD: no errors reported!\n");
+ lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9057 BLKGRD: Unknown error in cmd "
+ "0x%x reftag 0x%x blk cnt 0x%x "
+ "bgstat=x%x bghm=x%x\n", cmd->cmnd[0],
+ scsi_prot_ref_tag(cmd),
+ scsi_logical_block_count(cmd), bgstat, bghm);
+
+ /* Calculate what type of error it was */
+ lpfc_calc_bg_err(phba, lpfc_cmd);
}
-
out:
return ret;
}
@@ -2038,24 +3039,28 @@ out:
* field of @lpfc_cmd for device with SLI-4 interface spec.
*
* Return codes:
- * 1 - Error
+ * 2 - Error - Do not retry
+ * 1 - Error - Retry
* 0 - Success
**/
static int
-lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct scatterlist *sgel = NULL;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+ struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl;
struct sli4_sge *first_data_sgl;
- IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ struct lpfc_iocbq *pwqeq = &lpfc_cmd->cur_iocbq;
+ struct lpfc_vport *vport = phba->pport;
+ union lpfc_wqe128 *wqe = &pwqeq->wqe;
dma_addr_t physaddr;
- uint32_t num_bde = 0;
uint32_t dma_len;
uint32_t dma_offset = 0;
- int nseg;
+ int nseg, i, j;
struct ulp_bde64 *bde;
+ bool lsp_just_set = false;
+ struct sli4_hybrid_sgl *sgl_xtra = NULL;
/*
* There are three possibilities here - use scatter-gather segment, use
@@ -2065,14 +3070,14 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
*/
if (scsi_sg_count(scsi_cmnd)) {
/*
- * The driver stores the segment count returned from pci_map_sg
+ * The driver stores the segment count returned from dma_map_sg
* because this a count of dma-mappings used to map the use_sg
* pages. They are not guaranteed to be the same for those
* architectures that implement an IOMMU.
*/
nseg = scsi_dma_map(scsi_cmnd);
- if (unlikely(!nseg))
+ if (unlikely(nseg <= 0))
return 1;
sgl += 1;
/* clear the last flag in the fcp_rsp map entry */
@@ -2082,14 +3087,18 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
sgl += 1;
first_data_sgl = sgl;
lpfc_cmd->seg_cnt = nseg;
- if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9074 BLKGRD:"
- " %s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d\n",
- __func__, phba->cfg_sg_seg_cnt,
- lpfc_cmd->seg_cnt);
+ if (!phba->cfg_xpsgl &&
+ lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9074 BLKGRD:"
+ " %s: Too many sg segments from "
+ "dma_map_sg. Config %d, seg_cnt %d\n",
+ __func__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ WARN_ON_ONCE(lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt);
+ lpfc_cmd->seg_cnt = 0;
scsi_dma_unmap(scsi_cmnd);
- return 1;
+ return 2;
}
/*
@@ -2101,39 +3110,118 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* the IOCB. If it can't then the BDEs get added to a BPL as it
* does for SLI-2 mode.
*/
- scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) {
- physaddr = sg_dma_address(sgel);
- dma_len = sg_dma_len(sgel);
- sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
- sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
- sgl->word2 = le32_to_cpu(sgl->word2);
- if ((num_bde + 1) == nseg)
+
+ /* for tracking segment boundaries */
+ sgel = scsi_sglist(scsi_cmnd);
+ j = 2;
+ for (i = 0; i < nseg; i++) {
+ sgl->word2 = 0;
+ if (nseg == 1) {
bf_set(lpfc_sli4_sge_last, sgl, 1);
- else
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_DATA);
+ } else {
bf_set(lpfc_sli4_sge_last, sgl, 0);
- bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
- sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->sge_len = cpu_to_le32(dma_len);
- dma_offset += dma_len;
- sgl++;
+
+ /* do we need to expand the segment */
+ if (!lsp_just_set &&
+ !((j + 1) % phba->border_sge_num) &&
+ ((nseg - 1) != i)) {
+ /* set LSP type */
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_LSP);
+
+ sgl_xtra = lpfc_get_sgl_per_hdwq(
+ phba, lpfc_cmd);
+
+ if (unlikely(!sgl_xtra)) {
+ lpfc_cmd->seg_cnt = 0;
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(
+ sgl_xtra->dma_phys_sgl));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(
+ sgl_xtra->dma_phys_sgl));
+
+ } else {
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_DATA);
+ }
+ }
+
+ if (!(bf_get(lpfc_sli4_sge_type, sgl) &
+ LPFC_SGE_TYPE_LSP)) {
+ if ((nseg - 1) == i)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+
+ physaddr = sg_dma_address(sgel);
+ dma_len = sg_dma_len(sgel);
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(
+ physaddr));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(
+ physaddr));
+
+ bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(dma_len);
+
+ dma_offset += dma_len;
+ sgel = sg_next(sgel);
+
+ sgl++;
+ lsp_just_set = false;
+
+ } else {
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(
+ phba->cfg_sg_dma_buf_size);
+
+ sgl = (struct sli4_sge *)sgl_xtra->dma_sgl;
+ i = i - 1;
+
+ lsp_just_set = true;
+ }
+
+ j++;
}
- /* setup the performance hint (first data BDE) if enabled */
- if (phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) {
+
+ /* PBDE support for first data SGE only.
+ * For FCoE, we key off Performance Hints.
+ * For FC, we key off lpfc_enable_pbde.
+ */
+ if (nseg == 1 &&
+ ((phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) ||
+ phba->cfg_enable_pbde)) {
+ /* Words 13-15 */
bde = (struct ulp_bde64 *)
- &(iocb_cmd->unsli3.sli3Words[5]);
+ &wqe->words[13];
bde->addrLow = first_data_sgl->addr_lo;
bde->addrHigh = first_data_sgl->addr_hi;
bde->tus.f.bdeSize =
le32_to_cpu(first_data_sgl->sge_len);
bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
bde->tus.w = cpu_to_le32(bde->tus.w);
+
+ /* Word 11 - set PBDE bit */
+ bf_set(wqe_pbde, &wqe->generic.wqe_com, 1);
+ } else {
+ memset(&wqe->words[13], 0, (sizeof(uint32_t) * 3));
+ /* Word 11 - PBDE bit disabled by default template */
}
} else {
sgl += 1;
- /* clear the last flag in the fcp_rsp map entry */
+ /* set the last flag in the fcp_rsp map entry */
sgl->word2 = le32_to_cpu(sgl->word2);
bf_set(lpfc_sli4_sge_last, sgl, 1);
sgl->word2 = cpu_to_le32(sgl->word2);
+
+ if ((phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) ||
+ phba->cfg_enable_pbde) {
+ bde = (struct ulp_bde64 *)
+ &wqe->words[13];
+ memset(bde, 0, (sizeof(uint32_t) * 3));
+ }
}
/*
@@ -2142,17 +3230,275 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* explicitly reinitialized.
* all iocb memory resources are reused.
*/
- fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd));
+ if (scsi_cmnd->cmd_len > LPFC_FCP_CDB_LEN)
+ ((struct fcp_cmnd32 *)fcp_cmnd)->fcpDl =
+ cpu_to_be32(scsi_bufflen(scsi_cmnd));
+ else
+ fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd));
+ /* Set first-burst provided it was successfully negotiated */
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag) &&
+ vport->cfg_first_burst_size &&
+ scsi_cmnd->sc_data_direction == DMA_TO_DEVICE) {
+ u32 init_len, total_len;
+
+ total_len = scsi_bufflen(scsi_cmnd);
+ init_len = min(total_len, vport->cfg_first_burst_size);
+
+ /* Word 4 & 5 */
+ wqe->fcp_iwrite.initial_xfer_len = init_len;
+ wqe->fcp_iwrite.total_xfer_len = total_len;
+ } else {
+ /* Word 4 */
+ wqe->fcp_iwrite.total_xfer_len =
+ be32_to_cpu(fcp_cmnd->fcpDl);
+ }
/*
- * Due to difference in data length between DIF/non-DIF paths,
- * we need to set word 4 of IOCB here
+ * If the OAS driver feature is enabled and the lun is enabled for
+ * OAS, set the oas iocb related flags.
*/
- iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
+ if ((phba->cfg_fof) && ((struct lpfc_device_data *)
+ scsi_cmnd->device->hostdata)->oas_enabled) {
+ lpfc_cmd->cur_iocbq.cmd_flag |= (LPFC_IO_OAS | LPFC_IO_FOF);
+ lpfc_cmd->cur_iocbq.priority = ((struct lpfc_device_data *)
+ scsi_cmnd->device->hostdata)->priority;
+
+ /* Word 10 */
+ bf_set(wqe_oas, &wqe->generic.wqe_com, 1);
+ bf_set(wqe_ccpe, &wqe->generic.wqe_com, 1);
+
+ if (lpfc_cmd->cur_iocbq.priority)
+ bf_set(wqe_ccp, &wqe->generic.wqe_com,
+ (lpfc_cmd->cur_iocbq.priority << 1));
+ else
+ bf_set(wqe_ccp, &wqe->generic.wqe_com,
+ (phba->cfg_XLanePriority << 1));
+ }
+
return 0;
}
/**
+ * lpfc_bg_scsi_prep_dma_buf_s4 - DMA mapping for scsi buffer to SLI4 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This is the protection/DIF aware version of
+ * lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
+ * two functions eventually, but for now, it's here
+ * Return codes:
+ * 2 - Error - Do not retry
+ * 1 - Error - Retry
+ * 0 - Success
+ **/
+static int
+lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
+ struct lpfc_io_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->dma_sgl);
+ struct lpfc_iocbq *pwqeq = &lpfc_cmd->cur_iocbq;
+ union lpfc_wqe128 *wqe = &pwqeq->wqe;
+ uint32_t num_sge = 0;
+ int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
+ int prot_group_type = 0;
+ int fcpdl;
+ int ret = 1;
+ struct lpfc_vport *vport = phba->pport;
+
+ /*
+ * Start the lpfc command prep by bumping the sgl beyond fcp_cmnd
+ * fcp_rsp regions to the first data sge entry
+ */
+ if (scsi_sg_count(scsi_cmnd)) {
+ /*
+ * The driver stores the segment count returned from dma_map_sg
+ * because this a count of dma-mappings used to map the use_sg
+ * pages. They are not guaranteed to be the same for those
+ * architectures that implement an IOMMU.
+ */
+ datasegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_sglist(scsi_cmnd),
+ scsi_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!datasegcnt))
+ return 1;
+
+ sgl += 1;
+ /* clear the last flag in the fcp_rsp map entry */
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+
+ sgl += 1;
+ lpfc_cmd->seg_cnt = datasegcnt;
+
+ /* First check if data segment count from SCSI Layer is good */
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt &&
+ !phba->cfg_xpsgl) {
+ WARN_ON_ONCE(lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt);
+ ret = 2;
+ goto err;
+ }
+
+ prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
+
+ switch (prot_group_type) {
+ case LPFC_PG_TYPE_NO_DIF:
+ /* Here we need to add a DISEED to the count */
+ if (((lpfc_cmd->seg_cnt + 1) >
+ phba->cfg_total_seg_cnt) &&
+ !phba->cfg_xpsgl) {
+ ret = 2;
+ goto err;
+ }
+
+ num_sge = lpfc_bg_setup_sgl(phba, scsi_cmnd, sgl,
+ datasegcnt, lpfc_cmd);
+
+ /* we should have 2 or more entries in buffer list */
+ if (num_sge < 2) {
+ ret = 2;
+ goto err;
+ }
+ break;
+
+ case LPFC_PG_TYPE_DIF_BUF:
+ /*
+ * This type indicates that protection buffers are
+ * passed to the driver, so that needs to be prepared
+ * for DMA
+ */
+ protsegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!protsegcnt)) {
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ lpfc_cmd->prot_seg_cnt = protsegcnt;
+ /*
+ * There is a minimun of 3 SGEs used for every
+ * protection data segment.
+ */
+ if (((lpfc_cmd->prot_seg_cnt * 3) >
+ (phba->cfg_total_seg_cnt - 2)) &&
+ !phba->cfg_xpsgl) {
+ ret = 2;
+ goto err;
+ }
+
+ num_sge = lpfc_bg_setup_sgl_prot(phba, scsi_cmnd, sgl,
+ datasegcnt, protsegcnt, lpfc_cmd);
+
+ /* we should have 3 or more entries in buffer list */
+ if (num_sge < 3 ||
+ (num_sge > phba->cfg_total_seg_cnt &&
+ !phba->cfg_xpsgl)) {
+ ret = 2;
+ goto err;
+ }
+ break;
+
+ case LPFC_PG_TYPE_INVALID:
+ default:
+ scsi_dma_unmap(scsi_cmnd);
+ lpfc_cmd->seg_cnt = 0;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9083 Unexpected protection group %i\n",
+ prot_group_type);
+ return 2;
+ }
+ }
+
+ switch (scsi_get_prot_op(scsi_cmnd)) {
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_READ_STRIP:
+ lpfc_cmd->cur_iocbq.cmd_flag |= LPFC_IO_DIF_STRIP;
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_READ_INSERT:
+ lpfc_cmd->cur_iocbq.cmd_flag |= LPFC_IO_DIF_INSERT;
+ break;
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_READ_PASS:
+ lpfc_cmd->cur_iocbq.cmd_flag |= LPFC_IO_DIF_PASS;
+ break;
+ }
+
+ fcpdl = lpfc_bg_scsi_adjust_dl(phba, lpfc_cmd);
+ if (lpfc_cmd->pCmd->cmd_len > LPFC_FCP_CDB_LEN)
+ ((struct fcp_cmnd32 *)fcp_cmnd)->fcpDl = cpu_to_be32(fcpdl);
+ else
+ fcp_cmnd->fcpDl = cpu_to_be32(fcpdl);
+
+ /* Set first-burst provided it was successfully negotiated */
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag) &&
+ vport->cfg_first_burst_size &&
+ scsi_cmnd->sc_data_direction == DMA_TO_DEVICE) {
+ u32 init_len, total_len;
+
+ total_len = fcpdl;
+ init_len = min(total_len, vport->cfg_first_burst_size);
+
+ /* Word 4 & 5 */
+ wqe->fcp_iwrite.initial_xfer_len = init_len;
+ wqe->fcp_iwrite.total_xfer_len = total_len;
+ } else {
+ /* Word 4 */
+ wqe->fcp_iwrite.total_xfer_len = fcpdl;
+ }
+
+ /*
+ * If the OAS driver feature is enabled and the lun is enabled for
+ * OAS, set the oas iocb related flags.
+ */
+ if ((phba->cfg_fof) && ((struct lpfc_device_data *)
+ scsi_cmnd->device->hostdata)->oas_enabled) {
+ lpfc_cmd->cur_iocbq.cmd_flag |= (LPFC_IO_OAS | LPFC_IO_FOF);
+
+ /* Word 10 */
+ bf_set(wqe_oas, &wqe->generic.wqe_com, 1);
+ bf_set(wqe_ccpe, &wqe->generic.wqe_com, 1);
+ bf_set(wqe_ccp, &wqe->generic.wqe_com,
+ (phba->cfg_XLanePriority << 1));
+ }
+
+ /* Word 7. DIF Flags */
+ if (lpfc_cmd->cur_iocbq.cmd_flag & LPFC_IO_DIF_PASS)
+ bf_set(wqe_dif, &wqe->generic.wqe_com, LPFC_WQE_DIF_PASSTHRU);
+ else if (lpfc_cmd->cur_iocbq.cmd_flag & LPFC_IO_DIF_STRIP)
+ bf_set(wqe_dif, &wqe->generic.wqe_com, LPFC_WQE_DIF_STRIP);
+ else if (lpfc_cmd->cur_iocbq.cmd_flag & LPFC_IO_DIF_INSERT)
+ bf_set(wqe_dif, &wqe->generic.wqe_com, LPFC_WQE_DIF_INSERT);
+
+ lpfc_cmd->cur_iocbq.cmd_flag &= ~(LPFC_IO_DIF_PASS |
+ LPFC_IO_DIF_STRIP | LPFC_IO_DIF_INSERT);
+
+ return 0;
+err:
+ if (lpfc_cmd->seg_cnt)
+ scsi_dma_unmap(scsi_cmnd);
+ if (lpfc_cmd->prot_seg_cnt)
+ dma_unmap_sg(&phba->pcidev->dev, scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ scsi_cmnd->sc_data_direction);
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "9084 Cannot setup S/G List for HBA "
+ "IO segs %d/%d SGL %d SCSI %d: %d %d %d\n",
+ lpfc_cmd->seg_cnt, lpfc_cmd->prot_seg_cnt,
+ phba->cfg_total_seg_cnt, phba->cfg_sg_seg_cnt,
+ prot_group_type, num_sge, ret);
+
+ lpfc_cmd->seg_cnt = 0;
+ lpfc_cmd->prot_seg_cnt = 0;
+ return ret;
+}
+
+/**
* lpfc_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
* @phba: The Hba for which this call is being executed.
* @lpfc_cmd: The scsi buffer which is going to be mapped.
@@ -2165,34 +3511,72 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* 0 - Success
**/
static inline int
-lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
{
return phba->lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
}
/**
+ * lpfc_bg_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
+ * using BlockGuard.
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine wraps the actual DMA mapping function pointer from the
+ * lpfc_hba struct.
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
+static inline int
+lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
+{
+ return phba->lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
+}
+
+/**
+ * lpfc_scsi_prep_cmnd_buf - Wrapper function for IOCB/WQE mapping of scsi
+ * buffer
+ * @vport: Pointer to vport object.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ * @tmo: Timeout value for IO
+ *
+ * This routine initializes IOCB/WQE data structure from scsi command
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
+static inline int
+lpfc_scsi_prep_cmnd_buf(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd,
+ uint8_t tmo)
+{
+ return vport->phba->lpfc_scsi_prep_cmnd_buf(vport, lpfc_cmd, tmo);
+}
+
+/**
* lpfc_send_scsi_error_event - Posts an event when there is SCSI error
* @phba: Pointer to hba context object.
* @vport: Pointer to vport object.
* @lpfc_cmd: Pointer to lpfc scsi command which reported the error.
- * @rsp_iocb: Pointer to response iocb object which reported error.
+ * @fcpi_parm: FCP Initiator parameter.
*
* This function posts an event when there is a SCSI command reporting
* error from the scsi device.
**/
static void
lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
- struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_iocbq *rsp_iocb) {
+ struct lpfc_io_buf *lpfc_cmd, uint32_t fcpi_parm) {
struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
uint32_t resp_info = fcprsp->rspStatus2;
uint32_t scsi_status = fcprsp->rspStatus3;
- uint32_t fcpi_parm = rsp_iocb->iocb.un.fcpi.fcpi_parm;
struct lpfc_fast_path_event *fast_path_evt = NULL;
struct lpfc_nodelist *pnode = lpfc_cmd->rdata->pnode;
unsigned long flags;
- if (!pnode || !NLP_CHK_NODE_ACT(pnode))
+ if (!pnode)
return;
/* If there is queuefull or busy condition send a scsi event */
@@ -2274,7 +3658,7 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
* field of @lpfc_cmd for device with SLI-3 interface spec.
**/
static void
-lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *psb)
{
/*
* There are only two special cases to consider. (1) the scsi command
@@ -2291,28 +3675,166 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
}
/**
- * lpfc_handler_fcp_err - FCP response handler
+ * lpfc_unblock_requests - allow further commands to be queued.
+ * @phba: pointer to phba object
+ *
+ * For single vport, just call scsi_unblock_requests on physical port.
+ * For multiple vports, send scsi_unblock_requests for all the vports.
+ */
+void
+lpfc_unblock_requests(struct lpfc_hba *phba)
+{
+ struct lpfc_vport **vports;
+ struct Scsi_Host *shost;
+ int i;
+
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ !phba->sli4_hba.max_cfg_param.vpi_used) {
+ shost = lpfc_shost_from_vport(phba->pport);
+ scsi_unblock_requests(shost);
+ return;
+ }
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ shost = lpfc_shost_from_vport(vports[i]);
+ scsi_unblock_requests(shost);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
+/**
+ * lpfc_block_requests - prevent further commands from being queued.
+ * @phba: pointer to phba object
+ *
+ * For single vport, just call scsi_block_requests on physical port.
+ * For multiple vports, send scsi_block_requests for all the vports.
+ */
+void
+lpfc_block_requests(struct lpfc_hba *phba)
+{
+ struct lpfc_vport **vports;
+ struct Scsi_Host *shost;
+ int i;
+
+ if (atomic_read(&phba->cmf_stop_io))
+ return;
+
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ !phba->sli4_hba.max_cfg_param.vpi_used) {
+ shost = lpfc_shost_from_vport(phba->pport);
+ scsi_block_requests(shost);
+ return;
+ }
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ shost = lpfc_shost_from_vport(vports[i]);
+ scsi_block_requests(shost);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
+/**
+ * lpfc_update_cmf_cmpl - Adjust CMF counters for IO completion
+ * @phba: The HBA for which this call is being executed.
+ * @time: The latency of the IO that completed (in ns)
+ * @size: The size of the IO that completed
+ * @shost: SCSI host the IO completed on (NULL for a NVME IO)
+ *
+ * The routine adjusts the various Burst and Bandwidth counters used in
+ * Congestion management and E2E. If time is set to LPFC_CGN_NOT_SENT,
+ * that means the IO was never issued to the HBA, so this routine is
+ * just being called to cleanup the counter from a previous
+ * lpfc_update_cmf_cmd call.
+ */
+int
+lpfc_update_cmf_cmpl(struct lpfc_hba *phba,
+ uint64_t time, uint32_t size, struct Scsi_Host *shost)
+{
+ struct lpfc_cgn_stat *cgs;
+
+ if (time != LPFC_CGN_NOT_SENT) {
+ /* lat is ns coming in, save latency in us */
+ if (time < 1000)
+ time = 1;
+ else
+ time = div_u64(time + 500, 1000); /* round it */
+
+ cgs = per_cpu_ptr(phba->cmf_stat, raw_smp_processor_id());
+ atomic64_add(size, &cgs->rcv_bytes);
+ atomic64_add(time, &cgs->rx_latency);
+ atomic_inc(&cgs->rx_io_cnt);
+ }
+ return 0;
+}
+
+/**
+ * lpfc_update_cmf_cmd - Adjust CMF counters for IO submission
+ * @phba: The HBA for which this call is being executed.
+ * @size: The size of the IO that will be issued
+ *
+ * The routine adjusts the various Burst and Bandwidth counters used in
+ * Congestion management and E2E.
+ */
+int
+lpfc_update_cmf_cmd(struct lpfc_hba *phba, uint32_t size)
+{
+ uint64_t total;
+ struct lpfc_cgn_stat *cgs;
+ int cpu;
+
+ /* At this point we are either LPFC_CFG_MANAGED or LPFC_CFG_MONITOR */
+ if (phba->cmf_active_mode == LPFC_CFG_MANAGED &&
+ phba->cmf_max_bytes_per_interval) {
+ total = 0;
+ for_each_present_cpu(cpu) {
+ cgs = per_cpu_ptr(phba->cmf_stat, cpu);
+ total += atomic64_read(&cgs->total_bytes);
+ }
+ if (total >= phba->cmf_max_bytes_per_interval) {
+ if (!atomic_xchg(&phba->cmf_bw_wait, 1)) {
+ lpfc_block_requests(phba);
+ phba->cmf_last_ts =
+ lpfc_calc_cmf_latency(phba);
+ }
+ atomic_inc(&phba->cmf_busy);
+ return -EBUSY;
+ }
+ if (size > atomic_read(&phba->rx_max_read_cnt))
+ atomic_set(&phba->rx_max_read_cnt, size);
+ }
+
+ cgs = per_cpu_ptr(phba->cmf_stat, raw_smp_processor_id());
+ atomic64_add(size, &cgs->total_bytes);
+ return 0;
+}
+
+/**
+ * lpfc_handle_fcp_err - FCP response handler
* @vport: The virtual port for which this call is being executed.
- * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
- * @rsp_iocb: The response IOCB which contains FCP error.
+ * @lpfc_cmd: Pointer to lpfc_io_buf data structure.
+ * @fcpi_parm: FCP Initiator parameter.
*
* This routine is called to process response IOCB with status field
* IOSTAT_FCP_RSP_ERROR. This routine sets result field of scsi command
* based upon SCSI and FCP error.
**/
static void
-lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
- struct lpfc_iocbq *rsp_iocb)
+lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd,
+ uint32_t fcpi_parm)
{
struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
struct fcp_cmnd *fcpcmd = lpfc_cmd->fcp_cmnd;
struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
- uint32_t fcpi_parm = rsp_iocb->iocb.un.fcpi.fcpi_parm;
uint32_t resp_info = fcprsp->rspStatus2;
uint32_t scsi_status = fcprsp->rspStatus3;
uint32_t *lp;
uint32_t host_status = DID_OK;
uint32_t rsplen = 0;
+ uint32_t fcpDl;
uint32_t logit = LOG_FCP | LOG_FCP_ERROR;
@@ -2329,20 +3851,20 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
if (resp_info & RSP_LEN_VALID) {
rsplen = be32_to_cpu(fcprsp->rspRspLen);
if (rsplen != 0 && rsplen != 4 && rsplen != 8) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "2719 Invalid response length: "
- "tgt x%x lun x%x cmnd x%x rsplen x%x\n",
- cmnd->device->id,
- cmnd->device->lun, cmnd->cmnd[0],
- rsplen);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2719 Invalid response length: "
+ "tgt x%x lun x%llx cmnd x%x rsplen "
+ "x%x\n", cmnd->device->id,
+ cmnd->device->lun, cmnd->cmnd[0],
+ rsplen);
host_status = DID_ERROR;
goto out;
}
if (fcprsp->rspInfo3 != RSP_NO_FAILURE) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"2757 Protocol failure detected during "
"processing of FCP I/O op: "
- "tgt x%x lun x%x cmnd x%x rspInfo3 x%x\n",
+ "tgt x%x lun x%llx cmnd x%x rspInfo3 x%x\n",
cmnd->device->id,
cmnd->device->lun, cmnd->cmnd[0],
fcprsp->rspInfo3);
@@ -2362,8 +3884,15 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
lp = (uint32_t *)cmnd->sense_buffer;
- if (!scsi_status && (resp_info & RESID_UNDER))
- logit = LOG_FCP;
+ /* special handling for under run conditions */
+ if (!scsi_status && (resp_info & RESID_UNDER)) {
+ /* don't log under runs if fcp set... */
+ if (vport->cfg_log_verbose & LOG_FCP)
+ logit = LOG_FCP_ERROR;
+ /* unless operator says so */
+ if (vport->cfg_log_verbose & LOG_FCP_UNDER)
+ logit = LOG_FCP_UNDER;
+ }
lpfc_printf_vlog(vport, KERN_WARNING, logit,
"9024 FCP command x%x failed: x%x SNS x%x x%x "
@@ -2376,29 +3905,31 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
fcprsp->rspInfo3);
scsi_set_resid(cmnd, 0);
+ if (cmnd->cmd_len > LPFC_FCP_CDB_LEN)
+ fcpDl = be32_to_cpu(((struct fcp_cmnd32 *)fcpcmd)->fcpDl);
+ else
+ fcpDl = be32_to_cpu(fcpcmd->fcpDl);
if (resp_info & RESID_UNDER) {
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
- lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "9025 FCP Read Underrun, expected %d, "
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_UNDER,
+ "9025 FCP Underrun, expected %d, "
"residual %d Data: x%x x%x x%x\n",
- be32_to_cpu(fcpcmd->fcpDl),
+ fcpDl,
scsi_get_resid(cmnd), fcpi_parm, cmnd->cmnd[0],
cmnd->underflow);
/*
- * If there is an under run check if under run reported by
+ * If there is an under run, check if under run reported by
* storage array is same as the under run reported by HBA.
* If this is not same, there is a dropped frame.
*/
- if ((cmnd->sc_data_direction == DMA_FROM_DEVICE) &&
- fcpi_parm &&
- (scsi_get_resid(cmnd) != fcpi_parm)) {
+ if (fcpi_parm && (scsi_get_resid(cmnd) != fcpi_parm)) {
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_FCP | LOG_FCP_ERROR,
"9026 FCP Read Check Error "
"and Underrun Data: x%x x%x x%x x%x\n",
- be32_to_cpu(fcpcmd->fcpDl),
+ fcpDl,
scsi_get_resid(cmnd), fcpi_parm,
cmnd->cmnd[0]);
scsi_set_resid(cmnd, scsi_bufflen(cmnd));
@@ -2433,13 +3964,22 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
* Check SLI validation that all the transfer was actually done
* (fcpi_parm should be zero). Apply check only to reads.
*/
- } else if (fcpi_parm && (cmnd->sc_data_direction == DMA_FROM_DEVICE)) {
+ } else if (fcpi_parm) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR,
- "9029 FCP Read Check Error Data: "
+ "9029 FCP %s Check Error Data: "
"x%x x%x x%x x%x x%x\n",
- be32_to_cpu(fcpcmd->fcpDl),
- be32_to_cpu(fcprsp->rspResId),
+ ((cmnd->sc_data_direction == DMA_FROM_DEVICE) ?
+ "Read" : "Write"),
+ fcpDl, be32_to_cpu(fcprsp->rspResId),
fcpi_parm, cmnd->cmnd[0], scsi_status);
+
+ /* There is some issue with the LPe12000 that causes it
+ * to miscalculate the fcpi_parm and falsely trip this
+ * recovery logic. Detect this case and don't error when true.
+ */
+ if (fcpi_parm > fcpDl)
+ goto out;
+
switch (scsi_status) {
case SAM_STAT_GOOD:
case SAM_STAT_CHECK_CONDITION:
@@ -2455,8 +3995,338 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
out:
- cmnd->result = ScsiResult(host_status, scsi_status);
- lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb);
+ cmnd->result = host_status << 16 | scsi_status;
+ lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, fcpi_parm);
+}
+
+/**
+ * lpfc_fcp_io_cmd_wqe_cmpl - Complete a FCP IO
+ * @phba: The hba for which this call is being executed.
+ * @pwqeIn: The command WQE for the scsi cmnd.
+ * @pwqeOut: Pointer to driver response WQE object.
+ *
+ * This routine assigns scsi command result by looking into response WQE
+ * status field appropriately. This routine handles QUEUE FULL condition as
+ * well by ramping down device queue depth.
+ **/
+static void
+lpfc_fcp_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
+ struct lpfc_iocbq *pwqeOut)
+{
+ struct lpfc_io_buf *lpfc_cmd = pwqeIn->io_buf;
+ struct lpfc_wcqe_complete *wcqe = &pwqeOut->wcqe_cmpl;
+ struct lpfc_vport *vport = pwqeIn->vport;
+ struct lpfc_rport_data *rdata;
+ struct lpfc_nodelist *ndlp;
+ struct scsi_cmnd *cmd;
+ unsigned long flags;
+ struct lpfc_fast_path_event *fast_path_evt;
+ struct Scsi_Host *shost;
+ u32 logit = LOG_FCP;
+ u32 idx;
+ u32 lat;
+ u8 wait_xb_clr = 0;
+
+ /* Sanity check on return of outstanding command */
+ if (!lpfc_cmd) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "9032 Null lpfc_cmd pointer. No "
+ "release, skip completion\n");
+ return;
+ }
+
+ rdata = lpfc_cmd->rdata;
+ ndlp = rdata->pnode;
+
+ /* Sanity check on return of outstanding command */
+ cmd = lpfc_cmd->pCmd;
+ if (!cmd) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "9042 I/O completion: Not an active IO\n");
+ lpfc_release_scsi_buf(phba, lpfc_cmd);
+ return;
+ }
+ /* Guard against abort handler being called at same time */
+ spin_lock(&lpfc_cmd->buf_lock);
+ idx = lpfc_cmd->cur_iocbq.hba_wqidx;
+ if (phba->sli4_hba.hdwq)
+ phba->sli4_hba.hdwq[idx].scsi_cstat.io_cmpls++;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (unlikely(phba->hdwqstat_on & LPFC_CHECK_SCSI_IO))
+ this_cpu_inc(phba->sli4_hba.c_stat->cmpl_io);
+#endif
+ shost = cmd->device->host;
+
+ lpfc_cmd->status = bf_get(lpfc_wcqe_c_status, wcqe);
+ lpfc_cmd->result = (wcqe->parameter & IOERR_PARAM_MASK);
+
+ lpfc_cmd->flags &= ~LPFC_SBUF_XBUSY;
+ if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
+ lpfc_cmd->flags |= LPFC_SBUF_XBUSY;
+ if (phba->cfg_fcp_wait_abts_rsp)
+ wait_xb_clr = 1;
+ }
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (lpfc_cmd->prot_data_type) {
+ struct scsi_dif_tuple *src = NULL;
+
+ src = (struct scsi_dif_tuple *)lpfc_cmd->prot_data_segment;
+ /*
+ * Used to restore any changes to protection
+ * data for error injection.
+ */
+ switch (lpfc_cmd->prot_data_type) {
+ case LPFC_INJERR_REFTAG:
+ src->ref_tag =
+ lpfc_cmd->prot_data;
+ break;
+ case LPFC_INJERR_APPTAG:
+ src->app_tag =
+ (uint16_t)lpfc_cmd->prot_data;
+ break;
+ case LPFC_INJERR_GUARD:
+ src->guard_tag =
+ (uint16_t)lpfc_cmd->prot_data;
+ break;
+ default:
+ break;
+ }
+
+ lpfc_cmd->prot_data = 0;
+ lpfc_cmd->prot_data_type = 0;
+ lpfc_cmd->prot_data_segment = NULL;
+ }
+#endif
+ if (unlikely(lpfc_cmd->status)) {
+ if (lpfc_cmd->status == IOSTAT_FCP_RSP_ERROR &&
+ !lpfc_cmd->fcp_rsp->rspStatus3 &&
+ (lpfc_cmd->fcp_rsp->rspStatus2 & RESID_UNDER) &&
+ !(vport->cfg_log_verbose & LOG_FCP_UNDER))
+ logit = 0;
+ else
+ logit = LOG_FCP | LOG_FCP_UNDER;
+ lpfc_printf_vlog(vport, KERN_WARNING, logit,
+ "9034 FCP cmd x%x failed <%d/%lld> "
+ "status: x%x result: x%x "
+ "sid: x%x did: x%x oxid: x%x "
+ "Data: x%x x%x x%x\n",
+ cmd->cmnd[0],
+ cmd->device ? cmd->device->id : 0xffff,
+ cmd->device ? cmd->device->lun : 0xffff,
+ lpfc_cmd->status, lpfc_cmd->result,
+ vport->fc_myDID,
+ (ndlp) ? ndlp->nlp_DID : 0,
+ lpfc_cmd->cur_iocbq.sli4_xritag,
+ wcqe->parameter, wcqe->total_data_placed,
+ lpfc_cmd->cur_iocbq.iotag);
+ }
+
+ switch (lpfc_cmd->status) {
+ case CQE_STATUS_SUCCESS:
+ cmd->result = DID_OK << 16;
+ break;
+ case CQE_STATUS_FCP_RSP_FAILURE:
+ lpfc_handle_fcp_err(vport, lpfc_cmd,
+ pwqeIn->wqe.fcp_iread.total_xfer_len -
+ wcqe->total_data_placed);
+ break;
+ case CQE_STATUS_NPORT_BSY:
+ case CQE_STATUS_FABRIC_BSY:
+ cmd->result = DID_TRANSPORT_DISRUPTED << 16;
+ fast_path_evt = lpfc_alloc_fast_evt(phba);
+ if (!fast_path_evt)
+ break;
+ fast_path_evt->un.fabric_evt.event_type =
+ FC_REG_FABRIC_EVENT;
+ fast_path_evt->un.fabric_evt.subcategory =
+ (lpfc_cmd->status == IOSTAT_NPORT_BSY) ?
+ LPFC_EVENT_PORT_BUSY : LPFC_EVENT_FABRIC_BUSY;
+ if (ndlp) {
+ memcpy(&fast_path_evt->un.fabric_evt.wwpn,
+ &ndlp->nlp_portname,
+ sizeof(struct lpfc_name));
+ memcpy(&fast_path_evt->un.fabric_evt.wwnn,
+ &ndlp->nlp_nodename,
+ sizeof(struct lpfc_name));
+ }
+ fast_path_evt->vport = vport;
+ fast_path_evt->work_evt.evt =
+ LPFC_EVT_FASTPATH_MGMT_EVT;
+ spin_lock_irqsave(&phba->hbalock, flags);
+ list_add_tail(&fast_path_evt->work_evt.evt_listp,
+ &phba->work_list);
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ lpfc_worker_wake_up(phba);
+ lpfc_printf_vlog(vport, KERN_WARNING, logit,
+ "9035 Fabric/Node busy FCP cmd x%x failed"
+ " <%d/%lld> "
+ "status: x%x result: x%x "
+ "sid: x%x did: x%x oxid: x%x "
+ "Data: x%x x%x x%x\n",
+ cmd->cmnd[0],
+ cmd->device ? cmd->device->id : 0xffff,
+ cmd->device ? cmd->device->lun : 0xffff,
+ lpfc_cmd->status, lpfc_cmd->result,
+ vport->fc_myDID,
+ (ndlp) ? ndlp->nlp_DID : 0,
+ lpfc_cmd->cur_iocbq.sli4_xritag,
+ wcqe->parameter,
+ wcqe->total_data_placed,
+ lpfc_cmd->cur_iocbq.iocb.ulpIoTag);
+ break;
+ case CQE_STATUS_DI_ERROR:
+ if (bf_get(lpfc_wcqe_c_bg_edir, wcqe))
+ lpfc_cmd->result = IOERR_RX_DMA_FAILED;
+ else
+ lpfc_cmd->result = IOERR_TX_DMA_FAILED;
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_BG,
+ "9048 DI Error xri x%x status x%x DI ext "
+ "status x%x data placed x%x\n",
+ lpfc_cmd->cur_iocbq.sli4_xritag,
+ lpfc_cmd->status, wcqe->parameter,
+ wcqe->total_data_placed);
+ if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
+ /* BG enabled cmd. Parse BG error */
+ lpfc_parse_bg_err(phba, lpfc_cmd, pwqeOut);
+ break;
+ }
+ cmd->result = DID_ERROR << 16;
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9040 DI Error on unprotected cmd\n");
+ break;
+ case CQE_STATUS_REMOTE_STOP:
+ if (ndlp) {
+ /* This I/O was aborted by the target, we don't
+ * know the rxid and because we did not send the
+ * ABTS we cannot generate and RRQ.
+ */
+ lpfc_set_rrq_active(phba, ndlp,
+ lpfc_cmd->cur_iocbq.sli4_lxritag,
+ 0, 0);
+ }
+ fallthrough;
+ case CQE_STATUS_LOCAL_REJECT:
+ if (lpfc_cmd->result & IOERR_DRVR_MASK)
+ lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+ if (lpfc_cmd->result == IOERR_ELXSEC_KEY_UNWRAP_ERROR ||
+ lpfc_cmd->result ==
+ IOERR_ELXSEC_KEY_UNWRAP_COMPARE_ERROR ||
+ lpfc_cmd->result == IOERR_ELXSEC_CRYPTO_ERROR ||
+ lpfc_cmd->result ==
+ IOERR_ELXSEC_CRYPTO_COMPARE_ERROR) {
+ cmd->result = DID_NO_CONNECT << 16;
+ break;
+ }
+ if (lpfc_cmd->result == IOERR_INVALID_RPI ||
+ lpfc_cmd->result == IOERR_LINK_DOWN ||
+ lpfc_cmd->result == IOERR_NO_RESOURCES ||
+ lpfc_cmd->result == IOERR_ABORT_REQUESTED ||
+ lpfc_cmd->result == IOERR_RPI_SUSPENDED ||
+ lpfc_cmd->result == IOERR_SLER_CMD_RCV_FAILURE) {
+ cmd->result = DID_TRANSPORT_DISRUPTED << 16;
+ break;
+ }
+ lpfc_printf_vlog(vport, KERN_WARNING, logit,
+ "9036 Local Reject FCP cmd x%x failed"
+ " <%d/%lld> "
+ "status: x%x result: x%x "
+ "sid: x%x did: x%x oxid: x%x "
+ "Data: x%x x%x x%x\n",
+ cmd->cmnd[0],
+ cmd->device ? cmd->device->id : 0xffff,
+ cmd->device ? cmd->device->lun : 0xffff,
+ lpfc_cmd->status, lpfc_cmd->result,
+ vport->fc_myDID,
+ (ndlp) ? ndlp->nlp_DID : 0,
+ lpfc_cmd->cur_iocbq.sli4_xritag,
+ wcqe->parameter,
+ wcqe->total_data_placed,
+ lpfc_cmd->cur_iocbq.iocb.ulpIoTag);
+ fallthrough;
+ default:
+ cmd->result = DID_ERROR << 16;
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "9037 FCP Completion Error: xri %x "
+ "status x%x result x%x [x%x] "
+ "placed x%x\n",
+ lpfc_cmd->cur_iocbq.sli4_xritag,
+ lpfc_cmd->status, lpfc_cmd->result,
+ wcqe->parameter,
+ wcqe->total_data_placed);
+ }
+ if (cmd->result || lpfc_cmd->fcp_rsp->rspSnsLen) {
+ u32 *lp = (u32 *)cmd->sense_buffer;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "9039 Iodone <%d/%llu> cmd x%px, error "
+ "x%x SNS x%x x%x LBA x%llx Data: x%x x%x\n",
+ cmd->device->id, cmd->device->lun, cmd,
+ cmd->result, *lp, *(lp + 3),
+ (cmd->device->sector_size) ?
+ (u64)scsi_get_lba(cmd) : 0,
+ cmd->retries, scsi_get_resid(cmd));
+ }
+
+ if (vport->cfg_max_scsicmpl_time &&
+ time_after(jiffies, lpfc_cmd->start_time +
+ msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) {
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (ndlp) {
+ if (ndlp->cmd_qdepth >
+ atomic_read(&ndlp->cmd_pending) &&
+ (atomic_read(&ndlp->cmd_pending) >
+ LPFC_MIN_TGT_QDEPTH) &&
+ (cmd->cmnd[0] == READ_10 ||
+ cmd->cmnd[0] == WRITE_10))
+ ndlp->cmd_qdepth =
+ atomic_read(&ndlp->cmd_pending);
+
+ ndlp->last_change_time = jiffies;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ }
+ lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (lpfc_cmd->ts_cmd_start) {
+ lpfc_cmd->ts_isr_cmpl = lpfc_cmd->cur_iocbq.isr_timestamp;
+ lpfc_cmd->ts_data_io = ktime_get_ns();
+ phba->ktime_last_cmd = lpfc_cmd->ts_data_io;
+ lpfc_io_ktime(phba, lpfc_cmd);
+ }
+#endif
+ if (likely(!wait_xb_clr))
+ lpfc_cmd->pCmd = NULL;
+ spin_unlock(&lpfc_cmd->buf_lock);
+
+ /* Check if IO qualified for CMF */
+ if (phba->cmf_active_mode != LPFC_CFG_OFF &&
+ cmd->sc_data_direction == DMA_FROM_DEVICE &&
+ (scsi_sg_count(cmd))) {
+ /* Used when calculating average latency */
+ lat = ktime_get_ns() - lpfc_cmd->rx_cmd_start;
+ lpfc_update_cmf_cmpl(phba, lat, scsi_bufflen(cmd), shost);
+ }
+
+ if (wait_xb_clr)
+ goto out;
+
+ /* The sdev is not guaranteed to be valid post scsi_done upcall. */
+ scsi_done(cmd);
+
+ /*
+ * If there is an abort thread waiting for command completion
+ * wake up the thread.
+ */
+ spin_lock(&lpfc_cmd->buf_lock);
+ lpfc_cmd->cur_iocbq.cmd_flag &= ~LPFC_DRIVER_ABORTED;
+ if (lpfc_cmd->waitq)
+ wake_up(lpfc_cmd->waitq);
+ spin_unlock(&lpfc_cmd->buf_lock);
+out:
+ lpfc_release_scsi_buf(phba, lpfc_cmd);
}
/**
@@ -2473,59 +4343,117 @@ static void
lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct lpfc_iocbq *pIocbOut)
{
- struct lpfc_scsi_buf *lpfc_cmd =
- (struct lpfc_scsi_buf *) pIocbIn->context1;
+ struct lpfc_io_buf *lpfc_cmd =
+ (struct lpfc_io_buf *) pIocbIn->io_buf;
struct lpfc_vport *vport = pIocbIn->vport;
struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
struct lpfc_nodelist *pnode = rdata->pnode;
struct scsi_cmnd *cmd;
- int result;
- struct scsi_device *tmp_sdev;
- int depth;
unsigned long flags;
struct lpfc_fast_path_event *fast_path_evt;
struct Scsi_Host *shost;
- uint32_t queue_depth, scsi_id;
+ int idx;
+ uint32_t logit = LOG_FCP;
+
+ /* Guard against abort handler being called at same time */
+ spin_lock(&lpfc_cmd->buf_lock);
/* Sanity check on return of outstanding command */
- if (!(lpfc_cmd->pCmd))
- return;
cmd = lpfc_cmd->pCmd;
+ if (!cmd || !phba) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2621 IO completion: Not an active IO\n");
+ spin_unlock(&lpfc_cmd->buf_lock);
+ return;
+ }
+
+ idx = lpfc_cmd->cur_iocbq.hba_wqidx;
+ if (phba->sli4_hba.hdwq)
+ phba->sli4_hba.hdwq[idx].scsi_cstat.io_cmpls++;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (unlikely(phba->hdwqstat_on & LPFC_CHECK_SCSI_IO))
+ this_cpu_inc(phba->sli4_hba.c_stat->cmpl_io);
+#endif
shost = cmd->device->host;
- lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
+ lpfc_cmd->result = (pIocbOut->iocb.un.ulpWord[4] & IOERR_PARAM_MASK);
lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
- /* pick up SLI4 exhange busy status from HBA */
- lpfc_cmd->exch_busy = pIocbOut->iocb_flag & LPFC_EXCHANGE_BUSY;
+ /* pick up SLI4 exchange busy status from HBA */
+ lpfc_cmd->flags &= ~LPFC_SBUF_XBUSY;
+ if (pIocbOut->cmd_flag & LPFC_EXCHANGE_BUSY)
+ lpfc_cmd->flags |= LPFC_SBUF_XBUSY;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (lpfc_cmd->prot_data_type) {
+ struct scsi_dif_tuple *src = NULL;
+
+ src = (struct scsi_dif_tuple *)lpfc_cmd->prot_data_segment;
+ /*
+ * Used to restore any changes to protection
+ * data for error injection.
+ */
+ switch (lpfc_cmd->prot_data_type) {
+ case LPFC_INJERR_REFTAG:
+ src->ref_tag =
+ lpfc_cmd->prot_data;
+ break;
+ case LPFC_INJERR_APPTAG:
+ src->app_tag =
+ (uint16_t)lpfc_cmd->prot_data;
+ break;
+ case LPFC_INJERR_GUARD:
+ src->guard_tag =
+ (uint16_t)lpfc_cmd->prot_data;
+ break;
+ default:
+ break;
+ }
- if (pnode && NLP_CHK_NODE_ACT(pnode))
- atomic_dec(&pnode->cmd_pending);
+ lpfc_cmd->prot_data = 0;
+ lpfc_cmd->prot_data_type = 0;
+ lpfc_cmd->prot_data_segment = NULL;
+ }
+#endif
- if (lpfc_cmd->status) {
+ if (unlikely(lpfc_cmd->status)) {
if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
(lpfc_cmd->result & IOERR_DRVR_MASK))
lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
else if (lpfc_cmd->status >= IOSTAT_CNT)
lpfc_cmd->status = IOSTAT_DEFAULT;
-
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "9030 FCP cmd x%x failed <%d/%d> "
- "status: x%x result: x%x Data: x%x x%x\n",
- cmd->cmnd[0],
- cmd->device ? cmd->device->id : 0xffff,
- cmd->device ? cmd->device->lun : 0xffff,
- lpfc_cmd->status, lpfc_cmd->result,
- pIocbOut->iocb.ulpContext,
- lpfc_cmd->cur_iocbq.iocb.ulpIoTag);
+ if (lpfc_cmd->status == IOSTAT_FCP_RSP_ERROR &&
+ !lpfc_cmd->fcp_rsp->rspStatus3 &&
+ (lpfc_cmd->fcp_rsp->rspStatus2 & RESID_UNDER) &&
+ !(vport->cfg_log_verbose & LOG_FCP_UNDER))
+ logit = 0;
+ else
+ logit = LOG_FCP | LOG_FCP_UNDER;
+ lpfc_printf_vlog(vport, KERN_WARNING, logit,
+ "9030 FCP cmd x%x failed <%d/%lld> "
+ "status: x%x result: x%x "
+ "sid: x%x did: x%x oxid: x%x "
+ "Data: x%x x%x\n",
+ cmd->cmnd[0],
+ cmd->device ? cmd->device->id : 0xffff,
+ cmd->device ? cmd->device->lun : 0xffff,
+ lpfc_cmd->status, lpfc_cmd->result,
+ vport->fc_myDID,
+ (pnode) ? pnode->nlp_DID : 0,
+ phba->sli_rev == LPFC_SLI_REV4 ?
+ lpfc_cmd->cur_iocbq.sli4_xritag : 0xffff,
+ pIocbOut->iocb.ulpContext,
+ lpfc_cmd->cur_iocbq.iocb.ulpIoTag);
switch (lpfc_cmd->status) {
case IOSTAT_FCP_RSP_ERROR:
/* Call FCP RSP handler to determine result */
- lpfc_handle_fcp_err(vport, lpfc_cmd, pIocbOut);
+ lpfc_handle_fcp_err(vport, lpfc_cmd,
+ pIocbOut->iocb.un.fcpi.fcpi_parm);
break;
case IOSTAT_NPORT_BSY:
case IOSTAT_FABRIC_BSY:
- cmd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0);
+ cmd->result = DID_TRANSPORT_DISRUPTED << 16;
fast_path_evt = lpfc_alloc_fast_evt(phba);
if (!fast_path_evt)
break;
@@ -2534,7 +4462,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
fast_path_evt->un.fabric_evt.subcategory =
(lpfc_cmd->status == IOSTAT_NPORT_BSY) ?
LPFC_EVENT_PORT_BUSY : LPFC_EVENT_FABRIC_BUSY;
- if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if (pnode) {
memcpy(&fast_path_evt->un.fabric_evt.wwpn,
&pnode->nlp_portname,
sizeof(struct lpfc_name));
@@ -2559,14 +4487,14 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result == IOERR_ELXSEC_CRYPTO_ERROR ||
lpfc_cmd->result ==
IOERR_ELXSEC_CRYPTO_COMPARE_ERROR) {
- cmd->result = ScsiResult(DID_NO_CONNECT, 0);
+ cmd->result = DID_NO_CONNECT << 16;
break;
}
if (lpfc_cmd->result == IOERR_INVALID_RPI ||
lpfc_cmd->result == IOERR_NO_RESOURCES ||
lpfc_cmd->result == IOERR_ABORT_REQUESTED ||
lpfc_cmd->result == IOERR_SLER_CMD_RCV_FAILURE) {
- cmd->result = ScsiResult(DID_REQUEUE, 0);
+ cmd->result = DID_TRANSPORT_DISRUPTED << 16;
break;
}
if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||
@@ -2589,46 +4517,43 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
}
if ((lpfc_cmd->status == IOSTAT_REMOTE_STOP)
&& (phba->sli_rev == LPFC_SLI_REV4)
- && (pnode && NLP_CHK_NODE_ACT(pnode))) {
+ && pnode) {
/* This IO was aborted by the target, we don't
* know the rxid and because we did not send the
* ABTS we cannot generate and RRQ.
*/
lpfc_set_rrq_active(phba, pnode,
- lpfc_cmd->cur_iocbq.sli4_xritag,
- 0, 0);
+ lpfc_cmd->cur_iocbq.sli4_lxritag,
+ 0, 0);
}
- /* else: fall through */
+ fallthrough;
default:
- cmd->result = ScsiResult(DID_ERROR, 0);
+ cmd->result = DID_ERROR << 16;
break;
}
- if (!pnode || !NLP_CHK_NODE_ACT(pnode)
- || (pnode->nlp_state != NLP_STE_MAPPED_NODE))
- cmd->result = ScsiResult(DID_TRANSPORT_DISRUPTED,
- SAM_STAT_BUSY);
+ if (!pnode || (pnode->nlp_state != NLP_STE_MAPPED_NODE))
+ cmd->result = DID_TRANSPORT_DISRUPTED << 16 |
+ SAM_STAT_BUSY;
} else
- cmd->result = ScsiResult(DID_OK, 0);
+ cmd->result = DID_OK << 16;
if (cmd->result || lpfc_cmd->fcp_rsp->rspSnsLen) {
uint32_t *lp = (uint32_t *)cmd->sense_buffer;
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0710 Iodone <%d/%d> cmd %p, error "
+ "0710 Iodone <%d/%llu> cmd x%px, error "
"x%x SNS x%x x%x Data: x%x x%x\n",
cmd->device->id, cmd->device->lun, cmd,
cmd->result, *lp, *(lp + 3), cmd->retries,
scsi_get_resid(cmd));
}
- lpfc_update_stats(phba, lpfc_cmd);
- result = cmd->result;
if (vport->cfg_max_scsicmpl_time &&
time_after(jiffies, lpfc_cmd->start_time +
msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) {
spin_lock_irqsave(shost->host_lock, flags);
- if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if (pnode) {
if (pnode->cmd_qdepth >
atomic_read(&pnode->cmd_pending) &&
(atomic_read(&pnode->cmd_pending) >
@@ -2641,147 +4566,62 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
pnode->last_change_time = jiffies;
}
spin_unlock_irqrestore(shost->host_lock, flags);
- } else if (pnode && NLP_CHK_NODE_ACT(pnode)) {
- if ((pnode->cmd_qdepth < vport->cfg_tgt_queue_depth) &&
- time_after(jiffies, pnode->last_change_time +
- msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
- spin_lock_irqsave(shost->host_lock, flags);
- depth = pnode->cmd_qdepth * LPFC_TGTQ_RAMPUP_PCENT
- / 100;
- depth = depth ? depth : 1;
- pnode->cmd_qdepth += depth;
- if (pnode->cmd_qdepth > vport->cfg_tgt_queue_depth)
- pnode->cmd_qdepth = vport->cfg_tgt_queue_depth;
- pnode->last_change_time = jiffies;
- spin_unlock_irqrestore(shost->host_lock, flags);
- }
}
-
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
- /* The sdev is not guaranteed to be valid post scsi_done upcall. */
- queue_depth = cmd->device->queue_depth;
- scsi_id = cmd->device->id;
- cmd->scsi_done(cmd);
-
- if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- /*
- * If there is a thread waiting for command completion
- * wake up the thread.
- */
- spin_lock_irqsave(shost->host_lock, flags);
- lpfc_cmd->pCmd = NULL;
- if (lpfc_cmd->waitq)
- wake_up(lpfc_cmd->waitq);
- spin_unlock_irqrestore(shost->host_lock, flags);
- lpfc_release_scsi_buf(phba, lpfc_cmd);
- return;
+ lpfc_cmd->pCmd = NULL;
+ spin_unlock(&lpfc_cmd->buf_lock);
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (lpfc_cmd->ts_cmd_start) {
+ lpfc_cmd->ts_isr_cmpl = pIocbIn->isr_timestamp;
+ lpfc_cmd->ts_data_io = ktime_get_ns();
+ phba->ktime_last_cmd = lpfc_cmd->ts_data_io;
+ lpfc_io_ktime(phba, lpfc_cmd);
}
+#endif
- if (!result)
- lpfc_rampup_queue_depth(vport, queue_depth);
-
- /*
- * Check for queue full. If the lun is reporting queue full, then
- * back off the lun queue depth to prevent target overloads.
- */
- if (result == SAM_STAT_TASK_SET_FULL && pnode &&
- NLP_CHK_NODE_ACT(pnode)) {
- shost_for_each_device(tmp_sdev, shost) {
- if (tmp_sdev->id != scsi_id)
- continue;
- depth = scsi_track_queue_full(tmp_sdev,
- tmp_sdev->queue_depth-1);
- if (depth <= 0)
- continue;
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0711 detected queue full - lun queue "
- "depth adjusted to %d.\n", depth);
- lpfc_send_sdev_queuedepth_change_event(phba, vport,
- pnode,
- tmp_sdev->lun,
- depth+1, depth);
- }
- }
+ /* The sdev is not guaranteed to be valid post scsi_done upcall. */
+ scsi_done(cmd);
/*
- * If there is a thread waiting for command completion
+ * If there is an abort thread waiting for command completion
* wake up the thread.
*/
- spin_lock_irqsave(shost->host_lock, flags);
- lpfc_cmd->pCmd = NULL;
+ spin_lock(&lpfc_cmd->buf_lock);
+ lpfc_cmd->cur_iocbq.cmd_flag &= ~LPFC_DRIVER_ABORTED;
if (lpfc_cmd->waitq)
wake_up(lpfc_cmd->waitq);
- spin_unlock_irqrestore(shost->host_lock, flags);
+ spin_unlock(&lpfc_cmd->buf_lock);
lpfc_release_scsi_buf(phba, lpfc_cmd);
}
/**
- * lpfc_fcpcmd_to_iocb - copy the fcp_cmd data into the IOCB
- * @data: A pointer to the immediate command data portion of the IOCB.
- * @fcp_cmnd: The FCP Command that is provided by the SCSI layer.
+ * lpfc_scsi_prep_cmnd_buf_s3 - SLI-3 IOCB init for the IO
+ * @vport: Pointer to vport object.
+ * @lpfc_cmd: The scsi buffer which is going to be prep'ed.
+ * @tmo: timeout value for the IO
*
- * The routine copies the entire FCP command from @fcp_cmnd to @data while
- * byte swapping the data to big endian format for transmission on the wire.
- **/
-static void
-lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
-{
- int i, j;
- for (i = 0, j = 0; i < sizeof(struct fcp_cmnd);
- i += sizeof(uint32_t), j++) {
- ((uint32_t *)data)[j] = cpu_to_be32(((uint32_t *)fcp_cmnd)[j]);
- }
-}
-
-/**
- * lpfc_scsi_prep_cmnd - Wrapper func for convert scsi cmnd to FCP info unit
- * @vport: The virtual port for which this call is being executed.
- * @lpfc_cmd: The scsi command which needs to send.
- * @pnode: Pointer to lpfc_nodelist.
+ * Based on the data-direction of the command, initialize IOCB
+ * in the I/O buffer. Fill in the IOCB fields which are independent
+ * of the scsi buffer
*
- * This routine initializes fcp_cmnd and iocb data structure from scsi command
- * to transfer for device with SLI3 interface spec.
+ * RETURNS 0 - SUCCESS,
**/
-static void
-lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
- struct lpfc_nodelist *pnode)
+static int lpfc_scsi_prep_cmnd_buf_s3(struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_cmd,
+ uint8_t tmo)
{
- struct lpfc_hba *phba = vport->phba;
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ struct lpfc_iocbq *piocbq = &lpfc_cmd->cur_iocbq;
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
- IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
- struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq);
+ struct lpfc_nodelist *pnode = lpfc_cmd->ndlp;
int datadir = scsi_cmnd->sc_data_direction;
- char tag[2];
-
- if (!pnode || !NLP_CHK_NODE_ACT(pnode))
- return;
+ u32 fcpdl;
- lpfc_cmd->fcp_rsp->rspSnsLen = 0;
- /* clear task management bits */
- lpfc_cmd->fcp_cmnd->fcpCntl2 = 0;
-
- int_to_scsilun(lpfc_cmd->pCmd->device->lun,
- &lpfc_cmd->fcp_cmnd->fcp_lun);
-
- memcpy(&fcp_cmnd->fcpCdb[0], scsi_cmnd->cmnd, 16);
-
- if (scsi_populate_tag_msg(scsi_cmnd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- fcp_cmnd->fcpCntl1 = HEAD_OF_Q;
- break;
- case ORDERED_QUEUE_TAG:
- fcp_cmnd->fcpCntl1 = ORDERED_Q;
- break;
- default:
- fcp_cmnd->fcpCntl1 = SIMPLE_Q;
- break;
- }
- } else
- fcp_cmnd->fcpCntl1 = 0;
+ piocbq->iocb.un.fcpi.fcpi_XRdy = 0;
/*
* There are three possibilities here - use scatter-gather segment, use
@@ -2792,53 +4632,212 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
if (scsi_sg_count(scsi_cmnd)) {
if (datadir == DMA_TO_DEVICE) {
iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
- if (phba->sli_rev < LPFC_SLI_REV4) {
- iocb_cmd->un.fcpi.fcpi_parm = 0;
- iocb_cmd->ulpPU = 0;
- } else
- iocb_cmd->ulpPU = PARM_READ_CHECK;
+ iocb_cmd->ulpPU = PARM_READ_CHECK;
+ if (vport->cfg_first_burst_size &&
+ test_bit(NLP_FIRSTBURST, &pnode->nlp_flag)) {
+ u32 xrdy_len;
+
+ fcpdl = scsi_bufflen(scsi_cmnd);
+ xrdy_len = min(fcpdl,
+ vport->cfg_first_burst_size);
+ piocbq->iocb.un.fcpi.fcpi_XRdy = xrdy_len;
+ }
fcp_cmnd->fcpCntl3 = WRITE_DATA;
- phba->fc4OutputRequests++;
} else {
iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
iocb_cmd->ulpPU = PARM_READ_CHECK;
fcp_cmnd->fcpCntl3 = READ_DATA;
- phba->fc4InputRequests++;
}
} else {
iocb_cmd->ulpCommand = CMD_FCP_ICMND64_CR;
iocb_cmd->un.fcpi.fcpi_parm = 0;
iocb_cmd->ulpPU = 0;
fcp_cmnd->fcpCntl3 = 0;
- phba->fc4ControlRequests++;
}
- if (phba->sli_rev == 3 &&
- !(phba->sli3_options & LPFC_SLI3_BG_ENABLED))
- lpfc_fcpcmd_to_iocb(iocb_cmd->unsli3.fcp_ext.icd, fcp_cmnd);
+
/*
* Finish initializing those IOCB fields that are independent
* of the scsi_cmnd request_buffer
*/
piocbq->iocb.ulpContext = pnode->nlp_rpi;
- if (phba->sli_rev == LPFC_SLI_REV4)
- piocbq->iocb.ulpContext =
- phba->sli4_hba.rpi_ids[pnode->nlp_rpi];
if (pnode->nlp_fcp_info & NLP_FCP_2_DEVICE)
piocbq->iocb.ulpFCP2Rcvy = 1;
else
piocbq->iocb.ulpFCP2Rcvy = 0;
piocbq->iocb.ulpClass = (pnode->nlp_fcp_info & 0x0f);
- piocbq->context1 = lpfc_cmd;
- piocbq->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl;
- piocbq->iocb.ulpTimeout = lpfc_cmd->timeout;
+ piocbq->io_buf = lpfc_cmd;
+ if (!piocbq->cmd_cmpl)
+ piocbq->cmd_cmpl = lpfc_scsi_cmd_iocb_cmpl;
+ piocbq->iocb.ulpTimeout = tmo;
piocbq->vport = vport;
+ return 0;
+}
+
+/**
+ * lpfc_scsi_prep_cmnd_buf_s4 - SLI-4 WQE init for the IO
+ * @vport: Pointer to vport object.
+ * @lpfc_cmd: The scsi buffer which is going to be prep'ed.
+ * @tmo: timeout value for the IO
+ *
+ * Based on the data-direction of the command copy WQE template
+ * to I/O buffer WQE. Fill in the WQE fields which are independent
+ * of the scsi buffer
+ *
+ * RETURNS 0 - SUCCESS,
+ **/
+static int lpfc_scsi_prep_cmnd_buf_s4(struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_cmd,
+ uint8_t tmo)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct lpfc_sli4_hdw_queue *hdwq = NULL;
+ struct lpfc_iocbq *pwqeq = &lpfc_cmd->cur_iocbq;
+ struct lpfc_nodelist *pnode = lpfc_cmd->ndlp;
+ union lpfc_wqe128 *wqe = &pwqeq->wqe;
+ u16 idx = lpfc_cmd->hdwq_no;
+ int datadir = scsi_cmnd->sc_data_direction;
+
+ hdwq = &phba->sli4_hba.hdwq[idx];
+
+ /* Initialize 64 bytes only */
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /*
+ * There are three possibilities here - use scatter-gather segment, use
+ * the single mapping, or neither.
+ */
+ if (scsi_sg_count(scsi_cmnd)) {
+ if (datadir == DMA_TO_DEVICE) {
+ /* From the iwrite template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_iwrite_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
+ fcp_cmnd->fcpCntl3 = WRITE_DATA;
+ if (hdwq)
+ hdwq->scsi_cstat.output_requests++;
+ } else {
+ /* From the iread template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_iread_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
+ /* Word 7 */
+ bf_set(wqe_tmo, &wqe->fcp_iread.wqe_com, tmo);
+
+ fcp_cmnd->fcpCntl3 = READ_DATA;
+ if (hdwq)
+ hdwq->scsi_cstat.input_requests++;
+
+ /* For a CMF Managed port, iod must be zero'ed */
+ if (phba->cmf_active_mode == LPFC_CFG_MANAGED)
+ bf_set(wqe_iod, &wqe->fcp_iread.wqe_com,
+ LPFC_WQE_IOD_NONE);
+ }
+
+ /* Additional fcp cdb length field calculation.
+ * LPFC_FCP_CDB_LEN_32 - normal 16 byte cdb length,
+ * then divide by 4 for the word count.
+ * shift 2 because of the RDDATA/WRDATA.
+ */
+ if (scsi_cmnd->cmd_len > LPFC_FCP_CDB_LEN)
+ fcp_cmnd->fcpCntl3 |= 4 << 2;
+ } else {
+ /* From the icmnd template, initialize words 4 - 11 */
+ memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4],
+ sizeof(uint32_t) * 8);
+
+ /* Word 7 */
+ bf_set(wqe_tmo, &wqe->fcp_icmd.wqe_com, tmo);
+
+ fcp_cmnd->fcpCntl3 = 0;
+ if (hdwq)
+ hdwq->scsi_cstat.control_requests++;
+ }
+
+ /*
+ * Finish initializing those WQE fields that are independent
+ * of the request_buffer
+ */
+
+ /* Word 3 */
+ bf_set(payload_offset_len, &wqe->fcp_icmd,
+ sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
+ phba->sli4_hba.rpi_ids[pnode->nlp_rpi]);
+ bf_set(wqe_xri_tag, &wqe->generic.wqe_com, pwqeq->sli4_xritag);
+
+ /* Word 7*/
+ if (pnode->nlp_fcp_info & NLP_FCP_2_DEVICE)
+ bf_set(wqe_erp, &wqe->generic.wqe_com, 1);
+
+ bf_set(wqe_class, &wqe->generic.wqe_com,
+ (pnode->nlp_fcp_info & 0x0f));
+
+ /* Word 8 */
+ wqe->generic.wqe_com.abort_tag = pwqeq->iotag;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->generic.wqe_com, pwqeq->iotag);
+
+ pwqeq->vport = vport;
+ pwqeq->io_buf = lpfc_cmd;
+ pwqeq->hba_wqidx = lpfc_cmd->hdwq_no;
+ pwqeq->cmd_cmpl = lpfc_fcp_io_cmd_wqe_cmpl;
+
+ return 0;
+}
+
+/**
+ * lpfc_scsi_prep_cmnd - Wrapper func for convert scsi cmnd to FCP info unit
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine initializes fcp_cmnd and iocb data structure from scsi command
+ * to transfer for device with SLI3 interface spec.
+ **/
+static int
+lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd,
+ struct lpfc_nodelist *pnode)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ u8 *ptr;
+
+ if (!pnode)
+ return 0;
+
+ lpfc_cmd->fcp_rsp->rspSnsLen = 0;
+ /* clear task management bits */
+ lpfc_cmd->fcp_cmnd->fcpCntl2 = 0;
+
+ int_to_scsilun(lpfc_cmd->pCmd->device->lun,
+ &lpfc_cmd->fcp_cmnd->fcp_lun);
+
+ ptr = &((struct fcp_cmnd32 *)fcp_cmnd)->fcpCdb[0];
+ memcpy(ptr, scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
+ if (scsi_cmnd->cmd_len < LPFC_FCP_CDB_LEN) {
+ ptr += scsi_cmnd->cmd_len;
+ memset(ptr, 0, (LPFC_FCP_CDB_LEN - scsi_cmnd->cmd_len));
+ }
+
+ fcp_cmnd->fcpCntl1 = SIMPLE_Q;
+
+ lpfc_scsi_prep_cmnd_buf(vport, lpfc_cmd, lpfc_cmd->timeout);
+
+ return 0;
}
/**
- * lpfc_scsi_prep_task_mgmt_cmd - Convert SLI3 scsi TM cmd to FCP info unit
+ * lpfc_scsi_prep_task_mgmt_cmd_s3 - Convert SLI3 scsi TM cmd to FCP info unit
* @vport: The virtual port for which this call is being executed.
- * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lpfc_cmd: Pointer to lpfc_io_buf data structure.
* @lun: Logical unit number.
* @task_mgmt_cmd: SCSI task management command.
*
@@ -2850,10 +4849,9 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
* 1 - Success
**/
static int
-lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
- struct lpfc_scsi_buf *lpfc_cmd,
- unsigned int lun,
- uint8_t task_mgmt_cmd)
+lpfc_scsi_prep_task_mgmt_cmd_s3(struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_cmd,
+ u64 lun, u8 task_mgmt_cmd)
{
struct lpfc_iocbq *piocbq;
IOCB_t *piocb;
@@ -2861,8 +4859,7 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
struct lpfc_nodelist *ndlp = rdata->pnode;
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) ||
- ndlp->nlp_state != NLP_STE_MAPPED_NODE)
+ if (!ndlp || ndlp->nlp_state != NLP_STE_MAPPED_NODE)
return 0;
piocbq = &(lpfc_cmd->cur_iocbq);
@@ -2875,19 +4872,14 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
int_to_scsilun(lun, &fcp_cmnd->fcp_lun);
fcp_cmnd->fcpCntl2 = task_mgmt_cmd;
- if (vport->phba->sli_rev == 3 &&
- !(vport->phba->sli3_options & LPFC_SLI3_BG_ENABLED))
+ if (!(vport->phba->sli3_options & LPFC_SLI3_BG_ENABLED))
lpfc_fcpcmd_to_iocb(piocb->unsli3.fcp_ext.icd, fcp_cmnd);
piocb->ulpCommand = CMD_FCP_ICMND64_CR;
piocb->ulpContext = ndlp->nlp_rpi;
- if (vport->phba->sli_rev == LPFC_SLI_REV4) {
- piocb->ulpContext =
- vport->phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
- }
- if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) {
- piocb->ulpFCP2Rcvy = 1;
- }
+ piocb->ulpFCP2Rcvy = (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) ? 1 : 0;
piocb->ulpClass = (ndlp->nlp_fcp_info & 0x0f);
+ piocb->ulpPU = 0;
+ piocb->un.fcpi.fcpi_parm = 0;
/* ulpTimeout is only one byte */
if (lpfc_cmd->timeout > 0xff) {
@@ -2899,8 +4891,79 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
} else
piocb->ulpTimeout = lpfc_cmd->timeout;
- if (vport->phba->sli_rev == LPFC_SLI_REV4)
- lpfc_sli4_set_rsp_sgl_last(vport->phba, lpfc_cmd);
+ return 1;
+}
+
+/**
+ * lpfc_scsi_prep_task_mgmt_cmd_s4 - Convert SLI4 scsi TM cmd to FCP info unit
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_io_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd
+ * for device with SLI-4 interface spec.
+ *
+ * Return codes:
+ * 0 - Error
+ * 1 - Success
+ **/
+static int
+lpfc_scsi_prep_task_mgmt_cmd_s4(struct lpfc_vport *vport,
+ struct lpfc_io_buf *lpfc_cmd,
+ u64 lun, u8 task_mgmt_cmd)
+{
+ struct lpfc_iocbq *pwqeq = &lpfc_cmd->cur_iocbq;
+ union lpfc_wqe128 *wqe = &pwqeq->wqe;
+ struct fcp_cmnd *fcp_cmnd;
+ struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
+ struct lpfc_nodelist *ndlp = rdata->pnode;
+
+ if (!ndlp || ndlp->nlp_state != NLP_STE_MAPPED_NODE)
+ return 0;
+
+ pwqeq->vport = vport;
+ /* Initialize 64 bytes only */
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* From the icmnd template, initialize words 4 - 11 */
+ memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4],
+ sizeof(uint32_t) * 8);
+
+ fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ /* Clear out any old data in the FCP command area */
+ memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
+ int_to_scsilun(lun, &fcp_cmnd->fcp_lun);
+ fcp_cmnd->fcpCntl3 = 0;
+ fcp_cmnd->fcpCntl2 = task_mgmt_cmd;
+
+ bf_set(payload_offset_len, &wqe->fcp_icmd,
+ sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+ bf_set(cmd_buff_len, &wqe->fcp_icmd, 0);
+ bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com, /* ulpContext */
+ vport->phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_erp, &wqe->fcp_icmd.wqe_com,
+ ((ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) ? 1 : 0));
+ bf_set(wqe_class, &wqe->fcp_icmd.wqe_com,
+ (ndlp->nlp_fcp_info & 0x0f));
+
+ /* ulpTimeout is only one byte */
+ if (lpfc_cmd->timeout > 0xff) {
+ /*
+ * Do not timeout the command at the firmware level.
+ * The driver will provide the timeout mechanism.
+ */
+ bf_set(wqe_tmo, &wqe->fcp_icmd.wqe_com, 0);
+ } else {
+ bf_set(wqe_tmo, &wqe->fcp_icmd.wqe_com, lpfc_cmd->timeout);
+ }
+
+ lpfc_prep_embed_io(vport->phba, lpfc_cmd);
+ bf_set(wqe_xri_tag, &wqe->generic.wqe_com, pwqeq->sli4_xritag);
+ wqe->generic.wqe_com.abort_tag = pwqeq->iotag;
+ bf_set(wqe_reqtag, &wqe->generic.wqe_com, pwqeq->iotag);
+
+ lpfc_sli4_set_rsp_sgl_last(vport->phba, lpfc_cmd);
return 1;
}
@@ -2919,35 +4982,38 @@ lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
{
phba->lpfc_scsi_unprep_dma_buf = lpfc_scsi_unprep_dma_buf;
- phba->lpfc_scsi_prep_cmnd = lpfc_scsi_prep_cmnd;
switch (dev_grp) {
case LPFC_PCI_DEV_LP:
- phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s3;
phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s3;
+ phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s3;
phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s3;
phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s3;
+ phba->lpfc_scsi_prep_cmnd_buf = lpfc_scsi_prep_cmnd_buf_s3;
+ phba->lpfc_scsi_prep_task_mgmt_cmd =
+ lpfc_scsi_prep_task_mgmt_cmd_s3;
break;
case LPFC_PCI_DEV_OC:
- phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s4;
phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s4;
+ phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s4;
phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s4;
phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s4;
+ phba->lpfc_scsi_prep_cmnd_buf = lpfc_scsi_prep_cmnd_buf_s4;
+ phba->lpfc_scsi_prep_task_mgmt_cmd =
+ lpfc_scsi_prep_task_mgmt_cmd_s4;
break;
default:
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1418 Invalid HBA PCI-device group: 0x%x\n",
dev_grp);
return -ENODEV;
- break;
}
phba->lpfc_rampdown_queue_depth = lpfc_rampdown_queue_depth;
- phba->lpfc_scsi_cmd_iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl;
return 0;
}
/**
- * lpfc_taskmgmt_def_cmpl - IOCB completion routine for task management command
+ * lpfc_tskmgmt_def_cmpl - IOCB completion routine for task management command
* @phba: The Hba for which this call is being executed.
* @cmdiocbq: Pointer to lpfc_iocbq data structure.
* @rspiocbq: Pointer to lpfc_iocbq data structure.
@@ -2960,14 +5026,69 @@ lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocbq,
struct lpfc_iocbq *rspiocbq)
{
- struct lpfc_scsi_buf *lpfc_cmd =
- (struct lpfc_scsi_buf *) cmdiocbq->context1;
+ struct lpfc_io_buf *lpfc_cmd = cmdiocbq->io_buf;
if (lpfc_cmd)
lpfc_release_scsi_buf(phba, lpfc_cmd);
return;
}
/**
+ * lpfc_check_pci_resettable - Walks list of devices on pci_dev's bus to check
+ * if issuing a pci_bus_reset is possibly unsafe
+ * @phba: lpfc_hba pointer.
+ *
+ * Description:
+ * Walks the bus_list to ensure only PCI devices with Emulex
+ * vendor id, device ids that support hot reset, and only one occurrence
+ * of function 0.
+ *
+ * Returns:
+ * -EBADSLT, detected invalid device
+ * 0, successful
+ */
+int
+lpfc_check_pci_resettable(struct lpfc_hba *phba)
+{
+ const struct pci_dev *pdev = phba->pcidev;
+ struct pci_dev *ptr = NULL;
+ u8 counter = 0;
+
+ /* Walk the list of devices on the pci_dev's bus */
+ list_for_each_entry(ptr, &pdev->bus->devices, bus_list) {
+ /* Check for Emulex Vendor ID */
+ if (ptr->vendor != PCI_VENDOR_ID_EMULEX) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "8346 Non-Emulex vendor found: "
+ "0x%04x\n", ptr->vendor);
+ return -EBADSLT;
+ }
+
+ /* Check for valid Emulex Device ID */
+ if (phba->sli_rev != LPFC_SLI_REV4 ||
+ test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "8347 Incapable PCI reset device: "
+ "0x%04x\n", ptr->device);
+ return -EBADSLT;
+ }
+
+ /* Check for only one function 0 ID to ensure only one HBA on
+ * secondary bus
+ */
+ if (ptr->devfn == 0) {
+ if (++counter > 1) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "8348 More than one device on "
+ "secondary bus found\n");
+ return -EBADSLT;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
* lpfc_info - Info entry point of scsi_host_template data structure
* @host: The scsi host for which this call is being executed.
*
@@ -2981,39 +5102,64 @@ lpfc_info(struct Scsi_Host *host)
{
struct lpfc_vport *vport = (struct lpfc_vport *) host->hostdata;
struct lpfc_hba *phba = vport->phba;
- int len;
- static char lpfcinfobuf[384];
+ int link_speed = 0;
+ static char lpfcinfobuf[384];
+ char tmp[384] = {0};
- memset(lpfcinfobuf,0,384);
+ memset(lpfcinfobuf, 0, sizeof(lpfcinfobuf));
if (phba && phba->pcidev){
- strncpy(lpfcinfobuf, phba->ModelDesc, 256);
- len = strlen(lpfcinfobuf);
- snprintf(lpfcinfobuf + len,
- 384-len,
- " on PCI bus %02x device %02x irq %d",
- phba->pcidev->bus->number,
- phba->pcidev->devfn,
- phba->pcidev->irq);
- len = strlen(lpfcinfobuf);
+ /* Model Description */
+ scnprintf(tmp, sizeof(tmp), phba->ModelDesc);
+ if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >=
+ sizeof(lpfcinfobuf))
+ goto buffer_done;
+
+ /* PCI Info */
+ scnprintf(tmp, sizeof(tmp),
+ " on PCI bus %02x device %02x irq %d",
+ phba->pcidev->bus->number, phba->pcidev->devfn,
+ phba->pcidev->irq);
+ if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >=
+ sizeof(lpfcinfobuf))
+ goto buffer_done;
+
+ /* Port Number */
if (phba->Port[0]) {
- snprintf(lpfcinfobuf + len,
- 384-len,
- " port %s",
- phba->Port);
+ scnprintf(tmp, sizeof(tmp), " port %s", phba->Port);
+ if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >=
+ sizeof(lpfcinfobuf))
+ goto buffer_done;
}
- len = strlen(lpfcinfobuf);
- if (phba->sli4_hba.link_state.logical_speed) {
- snprintf(lpfcinfobuf + len,
- 384-len,
- " Logical Link Speed: %d Mbps",
- phba->sli4_hba.link_state.logical_speed * 10);
+
+ /* Link Speed */
+ link_speed = lpfc_sli_port_speed_get(phba);
+ if (link_speed != 0) {
+ scnprintf(tmp, sizeof(tmp),
+ " Logical Link Speed: %d Mbps", link_speed);
+ if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >=
+ sizeof(lpfcinfobuf))
+ goto buffer_done;
+ }
+
+ /* Support for BSG ioctls */
+ scnprintf(tmp, sizeof(tmp), " BSG");
+ if (strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf)) >=
+ sizeof(lpfcinfobuf))
+ goto buffer_done;
+
+ /* PCI resettable */
+ if (!lpfc_check_pci_resettable(phba)) {
+ scnprintf(tmp, sizeof(tmp), " PCI resettable");
+ strlcat(lpfcinfobuf, tmp, sizeof(lpfcinfobuf));
}
}
+
+buffer_done:
return lpfcinfobuf;
}
/**
- * lpfc_poll_rearm_time - Routine to modify fcp_poll timer of hba
+ * lpfc_poll_rearm_timer - Routine to modify fcp_poll timer of hba
* @phba: The Hba for which this call is being executed.
*
* This routine modifies fcp_poll_timer field of @phba by cfg_poll_tmo.
@@ -3024,7 +5170,7 @@ static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
unsigned long poll_tmo_expires =
(jiffies + msecs_to_jiffies(phba->cfg_poll_tmo));
- if (phba->sli.ring[LPFC_FCP_RING].txcmplq_cnt)
+ if (!list_empty(&phba->sli.sli3_ring[LPFC_FCP_RING].txcmplq))
mod_timer(&phba->fcp_poll_timer,
poll_tmo_expires);
}
@@ -3042,29 +5188,42 @@ void lpfc_poll_start_timer(struct lpfc_hba * phba)
/**
* lpfc_poll_timeout - Restart polling timer
- * @ptr: Map to lpfc_hba data structure pointer.
+ * @t: Timer construct where lpfc_hba data structure pointer is obtained.
*
* This routine restarts fcp_poll timer, when FCP ring polling is enable
* and FCP Ring interrupt is disable.
**/
-
-void lpfc_poll_timeout(unsigned long ptr)
+void lpfc_poll_timeout(struct timer_list *t)
{
- struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
+ struct lpfc_hba *phba = timer_container_of(phba, t, fcp_poll_timer);
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
lpfc_sli_handle_fast_ring_event(phba,
- &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+ &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
}
+/*
+ * lpfc_is_command_vm_io - get the UUID from blk cgroup
+ * @cmd: Pointer to scsi_cmnd data structure
+ * Returns UUID if present, otherwise NULL
+ */
+static char *lpfc_is_command_vm_io(struct scsi_cmnd *cmd)
+{
+ struct bio *bio = scsi_cmd_to_rq(cmd)->bio;
+
+ if (!IS_ENABLED(CONFIG_BLK_CGROUP_FC_APPID) || !bio)
+ return NULL;
+ return blkcg_get_fc_appid(bio);
+}
+
/**
* lpfc_queuecommand - scsi_host_template queuecommand entry point
+ * @shost: kernel scsi host pointer.
* @cmnd: Pointer to scsi_cmnd data structure.
- * @done: Pointer to done routine.
*
* Driver registers this routine to scsi midlayer to submit a @cmd to process.
* This routine prepares an IOCB from scsi command and provides to firmware.
@@ -3075,16 +5234,25 @@ void lpfc_poll_timeout(unsigned long ptr)
* SCSI_MLQUEUE_HOST_BUSY - Block all devices served by this host temporarily.
**/
static int
-lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
{
- struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata = cmnd->device->hostdata;
+ struct lpfc_iocbq *cur_iocbq = NULL;
+ struct lpfc_rport_data *rdata;
struct lpfc_nodelist *ndlp;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
- int err;
+ int err, idx;
+ u8 *uuid = NULL;
+ uint64_t start;
+
+ start = ktime_get_ns();
+ rdata = lpfc_rport_data_from_scsi_device(cmnd->device);
+
+ /* sanity check on references */
+ if (unlikely(!rdata) || unlikely(!rport))
+ goto out_fail_command;
err = fc_remote_port_chkready(rport);
if (err) {
@@ -3093,10 +5261,10 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
}
ndlp = rdata->pnode;
- if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
- scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
+ if ((scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) &&
+ (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED))) {
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"9058 BLKGRD: ERROR: rcvd protected cmd:%02x"
" op:%02x str=%s without registering for"
" BlockGuard - Rejecting command\n",
@@ -3109,137 +5277,237 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
* Catch race where our node has transitioned, but the
* transport is still transitioning.
*/
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
- cmnd->result = ScsiResult(DID_IMM_RETRY, 0);
- goto out_fail_command;
+ if (!ndlp)
+ goto out_tgt_busy1;
+
+ /* Check if IO qualifies for CMF */
+ if (phba->cmf_active_mode != LPFC_CFG_OFF &&
+ cmnd->sc_data_direction == DMA_FROM_DEVICE &&
+ (scsi_sg_count(cmnd))) {
+ /* Latency start time saved in rx_cmd_start later in routine */
+ err = lpfc_update_cmf_cmd(phba, scsi_bufflen(cmnd));
+ if (err)
+ goto out_tgt_busy1;
+ }
+
+ if (lpfc_ndlp_check_qdepth(phba, ndlp)) {
+ if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_ERROR,
+ "3377 Target Queue Full, scsi Id:%d "
+ "Qdepth:%d Pending command:%d"
+ " WWNN:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x, "
+ " WWPN:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x",
+ ndlp->nlp_sid, ndlp->cmd_qdepth,
+ atomic_read(&ndlp->cmd_pending),
+ ndlp->nlp_nodename.u.wwn[0],
+ ndlp->nlp_nodename.u.wwn[1],
+ ndlp->nlp_nodename.u.wwn[2],
+ ndlp->nlp_nodename.u.wwn[3],
+ ndlp->nlp_nodename.u.wwn[4],
+ ndlp->nlp_nodename.u.wwn[5],
+ ndlp->nlp_nodename.u.wwn[6],
+ ndlp->nlp_nodename.u.wwn[7],
+ ndlp->nlp_portname.u.wwn[0],
+ ndlp->nlp_portname.u.wwn[1],
+ ndlp->nlp_portname.u.wwn[2],
+ ndlp->nlp_portname.u.wwn[3],
+ ndlp->nlp_portname.u.wwn[4],
+ ndlp->nlp_portname.u.wwn[5],
+ ndlp->nlp_portname.u.wwn[6],
+ ndlp->nlp_portname.u.wwn[7]);
+ goto out_tgt_busy2;
+ }
}
- if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)
- goto out_tgt_busy;
- lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp);
+ lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp, cmnd);
if (lpfc_cmd == NULL) {
lpfc_rampdown_queue_depth(phba);
- lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_ERROR,
"0707 driver's buffer pool is empty, "
"IO busied\n");
goto out_host_busy;
}
+ lpfc_cmd->rx_cmd_start = start;
+ cur_iocbq = &lpfc_cmd->cur_iocbq;
/*
* Store the midlayer's command structure for the completion phase
* and complete the command initialization.
*/
lpfc_cmd->pCmd = cmnd;
lpfc_cmd->rdata = rdata;
- lpfc_cmd->timeout = 0;
- lpfc_cmd->start_time = jiffies;
+ lpfc_cmd->ndlp = ndlp;
+ cur_iocbq->cmd_cmpl = NULL;
cmnd->host_scribble = (unsigned char *)lpfc_cmd;
- cmnd->scsi_done = done;
+
+ err = lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp);
+ if (err)
+ goto out_host_busy_release_buf;
if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
if (vport->phba->cfg_enable_bg) {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
- "str=%s\n",
- cmnd->cmnd[0], scsi_get_prot_op(cmnd),
- dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x\n",
- cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
- cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
- cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
- cmnd->cmnd[9]);
- if (cmnd->cmnd[0] == READ_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9035 BLKGRD: READ @ sector %llu, "
- "count %u\n",
- (unsigned long long)scsi_get_lba(cmnd),
- blk_rq_sectors(cmnd->request));
- else if (cmnd->cmnd[0] == WRITE_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9036 BLKGRD: WRITE @ sector %llu, "
- "count %u cmd=%p\n",
- (unsigned long long)scsi_get_lba(cmnd),
- blk_rq_sectors(cmnd->request),
- cmnd);
+ lpfc_printf_vlog(vport,
+ KERN_INFO, LOG_SCSI_CMD,
+ "9033 BLKGRD: rcvd %s cmd:x%x "
+ "reftag x%x cnt %u pt %x\n",
+ dif_op_str[scsi_get_prot_op(cmnd)],
+ cmnd->cmnd[0],
+ scsi_prot_ref_tag(cmnd),
+ scsi_logical_block_count(cmnd),
+ scsi_get_prot_type(cmnd));
}
-
err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
} else {
- if (vport->phba->cfg_enable_bg) {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9038 BLKGRD: rcvd unprotected cmd:"
- "%02x op:%02x str=%s\n",
- cmnd->cmnd[0], scsi_get_prot_op(cmnd),
- dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9039 BLKGRD: CDB: %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x\n",
- cmnd->cmnd[0], cmnd->cmnd[1],
- cmnd->cmnd[2], cmnd->cmnd[3],
- cmnd->cmnd[4], cmnd->cmnd[5],
- cmnd->cmnd[6], cmnd->cmnd[7],
- cmnd->cmnd[8], cmnd->cmnd[9]);
- if (cmnd->cmnd[0] == READ_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9040 dbg: READ @ sector %llu, "
- "count %u\n",
- (unsigned long long)scsi_get_lba(cmnd),
- blk_rq_sectors(cmnd->request));
- else if (cmnd->cmnd[0] == WRITE_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9041 dbg: WRITE @ sector %llu, "
- "count %u cmd=%p\n",
- (unsigned long long)scsi_get_lba(cmnd),
- blk_rq_sectors(cmnd->request), cmnd);
- else
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9042 dbg: parser not implemented\n");
- }
err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
}
- if (err)
+ if (unlikely(err)) {
+ if (err == 2) {
+ cmnd->result = DID_ERROR << 16;
+ goto out_fail_command_release_buf;
+ }
goto out_host_busy_free_buf;
+ }
- lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp);
+ /* check the necessary and sufficient condition to support VMID */
+ if (lpfc_is_vmid_enabled(phba) &&
+ (ndlp->vmid_support ||
+ phba->pport->vmid_priority_tagging ==
+ LPFC_VMID_PRIO_TAG_ALL_TARGETS)) {
+ /* is the I/O generated by a VM, get the associated virtual */
+ /* entity id */
+ uuid = lpfc_is_command_vm_io(cmnd);
+
+ if (uuid) {
+ err = lpfc_vmid_get_appid(vport, uuid,
+ cmnd->sc_data_direction,
+ (union lpfc_vmid_io_tag *)
+ &cur_iocbq->vmid_tag);
+ if (!err)
+ cur_iocbq->cmd_flag |= LPFC_IO_VMID;
+ }
+ }
- atomic_inc(&ndlp->cmd_pending);
- err = lpfc_sli_issue_iocb(phba, LPFC_FCP_RING,
- &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (unlikely(phba->hdwqstat_on & LPFC_CHECK_SCSI_IO))
+ this_cpu_inc(phba->sli4_hba.c_stat->xmt_io);
+#endif
+ /* Issue I/O to adapter */
+ err = lpfc_sli_issue_fcp_io(phba, LPFC_FCP_RING, cur_iocbq,
+ SLI_IOCB_RET_IOCB);
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (start) {
+ lpfc_cmd->ts_cmd_start = start;
+ lpfc_cmd->ts_last_cmd = phba->ktime_last_cmd;
+ lpfc_cmd->ts_cmd_wqput = ktime_get_ns();
+ } else {
+ lpfc_cmd->ts_cmd_start = 0;
+ }
+#endif
if (err) {
- atomic_dec(&ndlp->cmd_pending);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "3376 FCP could not issue iocb err %x "
+ "FCP cmd x%x <%d/%llu> "
+ "sid: x%x did: x%x oxid: x%x "
+ "Data: x%x x%x x%x x%x\n",
+ err, cmnd->cmnd[0],
+ cmnd->device ? cmnd->device->id : 0xffff,
+ cmnd->device ? cmnd->device->lun : (u64)-1,
+ vport->fc_myDID, ndlp->nlp_DID,
+ phba->sli_rev == LPFC_SLI_REV4 ?
+ cur_iocbq->sli4_xritag : 0xffff,
+ phba->sli_rev == LPFC_SLI_REV4 ?
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi] :
+ cur_iocbq->iocb.ulpContext,
+ cur_iocbq->iotag,
+ phba->sli_rev == LPFC_SLI_REV4 ?
+ bf_get(wqe_tmo,
+ &cur_iocbq->wqe.generic.wqe_com) :
+ cur_iocbq->iocb.ulpTimeout,
+ (uint32_t)(scsi_cmd_to_rq(cmnd)->timeout / 1000));
+
goto out_host_busy_free_buf;
}
+
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- spin_unlock(shost->host_lock);
lpfc_sli_handle_fast_ring_event(phba,
- &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+ &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ);
- spin_lock(shost->host_lock);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
+ if (phba->cfg_xri_rebalancing)
+ lpfc_keep_pvt_pool_above_lowwm(phba, lpfc_cmd->hdwq_no);
+
return 0;
out_host_busy_free_buf:
+ idx = lpfc_cmd->hdwq_no;
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
+ if (phba->sli4_hba.hdwq) {
+ switch (lpfc_cmd->fcp_cmnd->fcpCntl3) {
+ case WRITE_DATA:
+ phba->sli4_hba.hdwq[idx].scsi_cstat.output_requests--;
+ break;
+ case READ_DATA:
+ phba->sli4_hba.hdwq[idx].scsi_cstat.input_requests--;
+ break;
+ default:
+ phba->sli4_hba.hdwq[idx].scsi_cstat.control_requests--;
+ }
+ }
+ out_host_busy_release_buf:
lpfc_release_scsi_buf(phba, lpfc_cmd);
out_host_busy:
+ lpfc_update_cmf_cmpl(phba, LPFC_CGN_NOT_SENT, scsi_bufflen(cmnd),
+ shost);
return SCSI_MLQUEUE_HOST_BUSY;
- out_tgt_busy:
+ out_tgt_busy2:
+ lpfc_update_cmf_cmpl(phba, LPFC_CGN_NOT_SENT, scsi_bufflen(cmnd),
+ shost);
+ out_tgt_busy1:
return SCSI_MLQUEUE_TARGET_BUSY;
+ out_fail_command_release_buf:
+ lpfc_release_scsi_buf(phba, lpfc_cmd);
+ lpfc_update_cmf_cmpl(phba, LPFC_CGN_NOT_SENT, scsi_bufflen(cmnd),
+ shost);
+
out_fail_command:
- done(cmnd);
+ scsi_done(cmnd);
return 0;
}
-static DEF_SCSI_QCMD(lpfc_queuecommand)
+/*
+ * lpfc_vmid_vport_cleanup - cleans up the resources associated with a vport
+ * @vport: The virtual port for which this call is being executed.
+ */
+void lpfc_vmid_vport_cleanup(struct lpfc_vport *vport)
+{
+ u32 bucket;
+ struct lpfc_vmid *cur;
+
+ if (vport->port_type == LPFC_PHYSICAL_PORT)
+ timer_delete_sync(&vport->phba->inactive_vmid_poll);
+
+ kfree(vport->qfpa_res);
+ kfree(vport->vmid_priority.vmid_range);
+ kfree(vport->vmid);
+
+ if (!hash_empty(vport->hash_table))
+ hash_for_each(vport->hash_table, bucket, cur, hnode)
+ hash_del(&cur->hnode);
+
+ vport->qfpa_res = NULL;
+ vport->vmid_priority.vmid_range = NULL;
+ vport->vmid = NULL;
+ vport->cur_vmid_cnt = 0;
+}
/**
* lpfc_abort_handler - scsi_host_template eh_abort_handler entry point
@@ -3255,109 +5523,163 @@ static int
lpfc_abort_handler(struct scsi_cmnd *cmnd)
{
struct Scsi_Host *shost = cmnd->device->host;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *iocb;
- struct lpfc_iocbq *abtsiocb;
- struct lpfc_scsi_buf *lpfc_cmd;
- IOCB_t *cmd, *icmd;
- int ret = SUCCESS;
+ struct lpfc_io_buf *lpfc_cmd;
+ int ret = SUCCESS, status = 0;
+ struct lpfc_sli_ring *pring_s4 = NULL;
+ struct lpfc_sli_ring *pring = NULL;
+ int ret_val;
+ unsigned long flags;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
- ret = fc_block_scsi_eh(cmnd);
- if (ret)
+ status = fc_block_rport(rport);
+ if (status != 0 && status != SUCCESS)
+ return status;
+
+ lpfc_cmd = (struct lpfc_io_buf *)cmnd->host_scribble;
+ if (!lpfc_cmd)
return ret;
- lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble;
- if (!lpfc_cmd) {
+
+ /* Guard against IO completion being called at same time */
+ spin_lock_irqsave(&lpfc_cmd->buf_lock, flags);
+
+ spin_lock(&phba->hbalock);
+ /* driver queued commands are in process of being flushed */
+ if (test_bit(HBA_IOQ_FLUSH, &phba->hba_flag)) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+ "3168 SCSI Layer abort requested I/O has been "
+ "flushed by LLD.\n");
+ ret = FAILED;
+ goto out_unlock_hba;
+ }
+
+ if (!lpfc_cmd->pCmd) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"2873 SCSI Layer I/O Abort Request IO CMPL Status "
- "x%x ID %d LUN %d\n",
- ret, cmnd->device->id, cmnd->device->lun);
- return SUCCESS;
+ "x%x ID %d LUN %llu\n",
+ SUCCESS, cmnd->device->id, cmnd->device->lun);
+ goto out_unlock_hba;
}
- /*
- * If pCmd field of the corresponding lpfc_scsi_buf structure
- * points to a different SCSI command, then the driver has
- * already completed this command, but the midlayer did not
- * see the completion before the eh fired. Just return
- * SUCCESS.
- */
iocb = &lpfc_cmd->cur_iocbq;
- if (lpfc_cmd->pCmd != cmnd)
- goto out;
-
- BUG_ON(iocb->context1 != lpfc_cmd);
-
- abtsiocb = lpfc_sli_get_iocbq(phba);
- if (abtsiocb == NULL) {
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ /* if the io_wq & pring are gone, the port was reset. */
+ if (!phba->sli4_hba.hdwq[iocb->hba_wqidx].io_wq ||
+ !phba->sli4_hba.hdwq[iocb->hba_wqidx].io_wq->pring) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+ "2877 SCSI Layer I/O Abort Request "
+ "IO CMPL Status x%x ID %d LUN %llu "
+ "HBA_SETUP %d\n", FAILED,
+ cmnd->device->id,
+ (u64)cmnd->device->lun,
+ test_bit(HBA_SETUP, &phba->hba_flag));
+ ret = FAILED;
+ goto out_unlock_hba;
+ }
+ pring_s4 = phba->sli4_hba.hdwq[iocb->hba_wqidx].io_wq->pring;
+ spin_lock(&pring_s4->ring_lock);
+ }
+ /* the command is in process of being cancelled */
+ if (!(iocb->cmd_flag & LPFC_IO_ON_TXCMPLQ)) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+ "3169 SCSI Layer abort requested I/O has been "
+ "cancelled by LLD.\n");
ret = FAILED;
- goto out;
+ goto out_unlock_ring;
}
-
/*
- * The scsi command can not be in txq and it is in flight because the
- * pCmd is still pointig at the SCSI command we have to abort. There
- * is no need to search the txcmplq. Just send an abort to the FW.
+ * If pCmd field of the corresponding lpfc_io_buf structure
+ * points to a different SCSI command, then the driver has
+ * already completed this command, but the midlayer did not
+ * see the completion before the eh fired. Just return SUCCESS.
*/
+ if (lpfc_cmd->pCmd != cmnd) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+ "3170 SCSI Layer abort requested I/O has been "
+ "completed by LLD.\n");
+ goto out_unlock_ring;
+ }
- cmd = &iocb->iocb;
- icmd = &abtsiocb->iocb;
- icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
- icmd->un.acxri.abortContextTag = cmd->ulpContext;
- if (phba->sli_rev == LPFC_SLI_REV4)
- icmd->un.acxri.abortIoTag = iocb->sli4_xritag;
- else
- icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+ WARN_ON(iocb->io_buf != lpfc_cmd);
- icmd->ulpLe = 1;
- icmd->ulpClass = cmd->ulpClass;
+ /* abort issued in recovery is still in progress */
+ if (iocb->cmd_flag & LPFC_DRIVER_ABORTED) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+ "3389 SCSI Layer I/O Abort Request is pending\n");
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring_s4->ring_lock);
+ spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&lpfc_cmd->buf_lock, flags);
+ goto wait_for_cmpl;
+ }
- /* ABTS WQE must go to the same WQ as the WQE to be aborted */
- abtsiocb->fcp_wqidx = iocb->fcp_wqidx;
- abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX;
+ lpfc_cmd->waitq = &waitq;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ spin_unlock(&pring_s4->ring_lock);
+ ret_val = lpfc_sli4_issue_abort_iotag(phba, iocb,
+ lpfc_sli_abort_fcp_cmpl);
+ } else {
+ pring = &phba->sli.sli3_ring[LPFC_FCP_RING];
+ ret_val = lpfc_sli_issue_abort_iotag(phba, pring, iocb,
+ lpfc_sli_abort_fcp_cmpl);
+ }
- if (lpfc_is_link_up(phba))
- icmd->ulpCommand = CMD_ABORT_XRI_CN;
- else
- icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
- abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
- abtsiocb->vport = vport;
- if (lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0) ==
- IOCB_ERROR) {
- lpfc_sli_release_iocbq(phba, abtsiocb);
+ if (ret_val != IOCB_SUCCESS) {
+ /* Indicate the IO is not being aborted by the driver. */
+ lpfc_cmd->waitq = NULL;
ret = FAILED;
- goto out;
+ goto out_unlock_hba;
}
+ /* no longer need the lock after this point */
+ spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&lpfc_cmd->buf_lock, flags);
+
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_sli_handle_fast_ring_event(phba,
- &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+ &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ);
- lpfc_cmd->waitq = &waitq;
- /* Wait for abort to complete */
- wait_event_timeout(waitq,
- (lpfc_cmd->pCmd != cmnd),
- (2*vport->cfg_devloss_tmo*HZ));
+wait_for_cmpl:
+ /*
+ * cmd_flag is set to LPFC_DRIVER_ABORTED before we wait
+ * for abort to complete.
+ */
+ wait_event_timeout(waitq, (lpfc_cmd->pCmd != cmnd),
+ secs_to_jiffies(2*vport->cfg_devloss_tmo));
- spin_lock_irq(shost->host_lock);
- lpfc_cmd->waitq = NULL;
- spin_unlock_irq(shost->host_lock);
+ spin_lock(&lpfc_cmd->buf_lock);
if (lpfc_cmd->pCmd == cmnd) {
ret = FAILED;
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0748 abort handler timed out waiting "
- "for abort to complete: ret %#x, ID %d, "
- "LUN %d\n",
- ret, cmnd->device->id, cmnd->device->lun);
+ "for aborting I/O (xri:x%x) to complete: "
+ "ret %#x, ID %d, LUN %llu\n",
+ iocb->sli4_xritag, ret,
+ cmnd->device->id, cmnd->device->lun);
}
- out:
+ lpfc_cmd->waitq = NULL;
+
+ spin_unlock(&lpfc_cmd->buf_lock);
+ goto out;
+
+out_unlock_ring:
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring_s4->ring_lock);
+out_unlock_hba:
+ spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&lpfc_cmd->buf_lock, flags);
+out:
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"0749 SCSI Layer I/O Abort Request Status x%x ID %d "
- "LUN %d\n", ret, cmnd->device->id,
+ "LUN %llu\n", ret, cmnd->device->id,
cmnd->device->lun);
return ret;
}
@@ -3385,10 +5707,82 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd)
}
}
+
+/**
+ * lpfc_check_fcp_rsp - check the returned fcp_rsp to see if task failed
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_io_buf data structure.
+ *
+ * This routine checks the FCP RSP INFO to see if the tsk mgmt command succeded
+ *
+ * Return code :
+ * 0x2003 - Error
+ * 0x2002 - Success
+ **/
+static int
+lpfc_check_fcp_rsp(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd)
+{
+ struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
+ uint32_t rsp_info;
+ uint32_t rsp_len;
+ uint8_t rsp_info_code;
+ int ret = FAILED;
+
+
+ if (fcprsp == NULL)
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "0703 fcp_rsp is missing\n");
+ else {
+ rsp_info = fcprsp->rspStatus2;
+ rsp_len = be32_to_cpu(fcprsp->rspRspLen);
+ rsp_info_code = fcprsp->rspInfo3;
+
+
+ lpfc_printf_vlog(vport, KERN_INFO,
+ LOG_FCP,
+ "0706 fcp_rsp valid 0x%x,"
+ " rsp len=%d code 0x%x\n",
+ rsp_info,
+ rsp_len, rsp_info_code);
+
+ /* If FCP_RSP_LEN_VALID bit is one, then the FCP_RSP_LEN
+ * field specifies the number of valid bytes of FCP_RSP_INFO.
+ * The FCP_RSP_LEN field shall be set to 0x04 or 0x08
+ */
+ if ((fcprsp->rspStatus2 & RSP_LEN_VALID) &&
+ ((rsp_len == 8) || (rsp_len == 4))) {
+ switch (rsp_info_code) {
+ case RSP_NO_FAILURE:
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "0715 Task Mgmt No Failure\n");
+ ret = SUCCESS;
+ break;
+ case RSP_TM_NOT_SUPPORTED: /* TM rejected */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "0716 Task Mgmt Target "
+ "reject\n");
+ break;
+ case RSP_TM_NOT_COMPLETED: /* TM failed */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "0717 Task Mgmt Target "
+ "failed TM\n");
+ break;
+ case RSP_TM_INVALID_LU: /* TM to invalid LU! */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "0718 Task Mgmt to invalid "
+ "LUN\n");
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+
/**
* lpfc_send_taskmgmt - Generic SCSI Task Mgmt Handler
* @vport: The virtual port for which this call is being executed.
- * @rdata: Pointer to remote port local data
+ * @rport: Pointer to remote port
* @tgt_id: Target ID of remote device.
* @lun_id: Lun number for the TMF
* @task_mgmt_cmd: type of TMF to send
@@ -3401,29 +5795,34 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd)
* 0x2002 - Success.
**/
static int
-lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
- unsigned tgt_id, unsigned int lun_id,
- uint8_t task_mgmt_cmd)
+lpfc_send_taskmgmt(struct lpfc_vport *vport, struct fc_rport *rport,
+ unsigned int tgt_id, uint64_t lun_id,
+ uint8_t task_mgmt_cmd)
{
struct lpfc_hba *phba = vport->phba;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct lpfc_iocbq *iocbq;
struct lpfc_iocbq *iocbqrsp;
- struct lpfc_nodelist *pnode = rdata->pnode;
+ struct lpfc_rport_data *rdata;
+ struct lpfc_nodelist *pnode;
int ret;
int status;
- if (!pnode || !NLP_CHK_NODE_ACT(pnode))
+ rdata = rport->dd_data;
+ if (!rdata || !rdata->pnode)
return FAILED;
+ pnode = rdata->pnode;
- lpfc_cmd = lpfc_get_scsi_buf(phba, rdata->pnode);
+ lpfc_cmd = lpfc_get_scsi_buf(phba, rdata->pnode, NULL);
if (lpfc_cmd == NULL)
return FAILED;
- lpfc_cmd->timeout = 60;
+ lpfc_cmd->timeout = phba->cfg_task_mgmt_tmo;
lpfc_cmd->rdata = rdata;
+ lpfc_cmd->pCmd = NULL;
+ lpfc_cmd->ndlp = pnode;
- status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun_id,
- task_mgmt_cmd);
+ status = phba->lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun_id,
+ task_mgmt_cmd);
if (!status) {
lpfc_release_scsi_buf(phba, lpfc_cmd);
return FAILED;
@@ -3435,38 +5834,51 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
lpfc_release_scsi_buf(phba, lpfc_cmd);
return FAILED;
}
+ iocbq->cmd_cmpl = lpfc_tskmgmt_def_cmpl;
+ iocbq->vport = vport;
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0702 Issue %s to TGT %d LUN %d "
- "rpi x%x nlp_flag x%x Data: x%x x%x\n",
+ "0702 Issue %s to TGT %d LUN %llu "
+ "rpi x%x nlp_flag x%lx Data: x%x x%x\n",
lpfc_taskmgmt_name(task_mgmt_cmd), tgt_id, lun_id,
pnode->nlp_rpi, pnode->nlp_flag, iocbq->sli4_xritag,
- iocbq->iocb_flag);
+ iocbq->cmd_flag);
status = lpfc_sli_issue_iocb_wait(phba, LPFC_FCP_RING,
iocbq, iocbqrsp, lpfc_cmd->timeout);
- if (status != IOCB_SUCCESS) {
- if (status == IOCB_TIMEDOUT) {
- iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
+ if ((status != IOCB_SUCCESS) ||
+ (get_job_ulpstatus(phba, iocbqrsp) != IOSTAT_SUCCESS)) {
+ if (status != IOCB_SUCCESS ||
+ get_job_ulpstatus(phba, iocbqrsp) != IOSTAT_FCP_RSP_ERROR)
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0727 TMF %s to TGT %d LUN %llu "
+ "failed (%d, %d) cmd_flag x%x\n",
+ lpfc_taskmgmt_name(task_mgmt_cmd),
+ tgt_id, lun_id,
+ get_job_ulpstatus(phba, iocbqrsp),
+ get_job_word4(phba, iocbqrsp),
+ iocbq->cmd_flag);
+ /* if ulpStatus != IOCB_SUCCESS, then status == IOCB_SUCCESS */
+ if (status == IOCB_SUCCESS) {
+ if (get_job_ulpstatus(phba, iocbqrsp) ==
+ IOSTAT_FCP_RSP_ERROR)
+ /* Something in the FCP_RSP was invalid.
+ * Check conditions */
+ ret = lpfc_check_fcp_rsp(vport, lpfc_cmd);
+ else
+ ret = FAILED;
+ } else if ((status == IOCB_TIMEDOUT) ||
+ (status == IOCB_ABORTED)) {
ret = TIMEOUT_ERROR;
- } else
+ } else {
ret = FAILED;
- lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0727 TMF %s to TGT %d LUN %d failed (%d, %d) "
- "iocb_flag x%x\n",
- lpfc_taskmgmt_name(task_mgmt_cmd),
- tgt_id, lun_id, iocbqrsp->iocb.ulpStatus,
- iocbqrsp->iocb.un.ulpWord[4],
- iocbq->iocb_flag);
- } else if (status == IOCB_BUSY)
- ret = FAILED;
- else
+ }
+ } else
ret = SUCCESS;
lpfc_sli_release_iocbq(phba, iocbqrsp);
- if (ret != TIMEOUT_ERROR)
+ if (status != IOCB_TIMEDOUT)
lpfc_release_scsi_buf(phba, lpfc_cmd);
return ret;
@@ -3475,7 +5887,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
/**
* lpfc_chk_tgt_mapped -
* @vport: The virtual port to check on
- * @cmnd: Pointer to scsi_cmnd data structure.
+ * @rport: Pointer to fc_rport data structure.
*
* This routine delays until the scsi target (aka rport) for the
* command exists (is present and logged in) or we declare it non-existent.
@@ -3485,36 +5897,37 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
* 0x2002 - Success
**/
static int
-lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd)
+lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct fc_rport *rport)
{
- struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *pnode;
+ struct lpfc_rport_data *rdata;
+ struct lpfc_nodelist *pnode = NULL;
unsigned long later;
+ rdata = rport->dd_data;
if (!rdata) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0797 Tgt Map rport failure: rdata x%p\n", rdata);
+ "0797 Tgt Map rport failure: rdata x%px\n", rdata);
return FAILED;
}
pnode = rdata->pnode;
+
/*
* If target is not in a MAPPED state, delay until
* target is rediscovered or devloss timeout expires.
*/
- later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
+ later = secs_to_jiffies(2 * vport->cfg_devloss_tmo) + jiffies;
while (time_after(later, jiffies)) {
- if (!pnode || !NLP_CHK_NODE_ACT(pnode))
+ if (!pnode)
return FAILED;
if (pnode->nlp_state == NLP_STE_MAPPED_NODE)
return SUCCESS;
schedule_timeout_uninterruptible(msecs_to_jiffies(500));
- rdata = cmnd->device->hostdata;
+ rdata = rport->dd_data;
if (!rdata)
return FAILED;
pnode = rdata->pnode;
}
- if (!pnode || !NLP_CHK_NODE_ACT(pnode) ||
- (pnode->nlp_state != NLP_STE_MAPPED_NODE))
+ if (!pnode || (pnode->nlp_state != NLP_STE_MAPPED_NODE))
return FAILED;
return SUCCESS;
}
@@ -3522,7 +5935,7 @@ lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd)
/**
* lpfc_reset_flush_io_context -
* @vport: The virtual port (scsi_host) for the flush context
- * @tgt_id: If aborting by Target contect - specifies the target id
+ * @tgt_id: If aborting by Target context - specifies the target id
* @lun_id: If aborting by Lun context - specifies the lun id
* @context: specifies the context level to flush at.
*
@@ -3545,15 +5958,16 @@ lpfc_reset_flush_io_context(struct lpfc_vport *vport, uint16_t tgt_id,
cnt = lpfc_sli_sum_iocb(vport, tgt_id, lun_id, context);
if (cnt)
- lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
- tgt_id, lun_id, context);
- later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
+ lpfc_sli_abort_taskmgmt(vport,
+ &phba->sli.sli3_ring[LPFC_FCP_RING],
+ tgt_id, lun_id, context);
+ later = secs_to_jiffies(2 * vport->cfg_devloss_tmo) + jiffies;
while (time_after(later, jiffies) && cnt) {
schedule_timeout_uninterruptible(msecs_to_jiffies(20));
cnt = lpfc_sli_sum_iocb(vport, tgt_id, lun_id, context);
}
if (cnt) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0724 I/O flush failure for context %s : cnt x%x\n",
((context == LPFC_CTX_LUN) ? "LUN" :
((context == LPFC_CTX_TGT) ? "TGT" :
@@ -3579,28 +5993,35 @@ static int
lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
{
struct Scsi_Host *shost = cmnd->device->host;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_rport_data *rdata = cmnd->device->hostdata;
+ struct lpfc_rport_data *rdata;
struct lpfc_nodelist *pnode;
unsigned tgt_id = cmnd->device->id;
- unsigned int lun_id = cmnd->device->lun;
+ uint64_t lun_id = cmnd->device->lun;
struct lpfc_scsi_event_header scsi_event;
int status;
+ u32 logit = LOG_FCP;
- if (!rdata) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0798 Device Reset rport failure: rdata x%p\n", rdata);
+ if (!rport)
+ return FAILED;
+
+ rdata = rport->dd_data;
+ if (!rdata || !rdata->pnode) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0798 Device Reset rdata failure: rdata x%px\n",
+ rdata);
return FAILED;
}
pnode = rdata->pnode;
- status = fc_block_scsi_eh(cmnd);
- if (status)
+ status = fc_block_rport(rport);
+ if (status != 0 && status != SUCCESS)
return status;
- status = lpfc_chk_tgt_mapped(vport, cmnd);
+ status = lpfc_chk_tgt_mapped(vport, rport);
if (status == FAILED) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0721 Device Reset rport failure: rdata x%p\n", rdata);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0721 Device Reset rport failure: rdata x%px\n", rdata);
return FAILED;
}
@@ -3613,11 +6034,13 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID);
- status = lpfc_send_taskmgmt(vport, rdata, tgt_id, lun_id,
+ status = lpfc_send_taskmgmt(vport, rport, tgt_id, lun_id,
FCP_LUN_RESET);
+ if (status != SUCCESS)
+ logit = LOG_TRACE_EVENT;
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0713 SCSI layer issued Device Reset (%d, %d) "
+ lpfc_printf_vlog(vport, KERN_ERR, logit,
+ "0713 SCSI layer issued Device Reset (%d, %llu) "
"return x%x\n", tgt_id, lun_id, status);
/*
@@ -3626,8 +6049,10 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
* So, continue on.
* We will report success if all the i/o aborts successfully.
*/
- status = lpfc_reset_flush_io_context(vport, tgt_id, lun_id,
+ if (status == SUCCESS)
+ status = lpfc_reset_flush_io_context(vport, tgt_id, lun_id,
LPFC_CTX_LUN);
+
return status;
}
@@ -3646,29 +6071,53 @@ static int
lpfc_target_reset_handler(struct scsi_cmnd *cmnd)
{
struct Scsi_Host *shost = cmnd->device->host;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_rport_data *rdata = cmnd->device->hostdata;
+ struct lpfc_rport_data *rdata;
struct lpfc_nodelist *pnode;
unsigned tgt_id = cmnd->device->id;
- unsigned int lun_id = cmnd->device->lun;
+ uint64_t lun_id = cmnd->device->lun;
struct lpfc_scsi_event_header scsi_event;
int status;
+ u32 logit = LOG_FCP;
+ u32 dev_loss_tmo = vport->cfg_devloss_tmo;
+ unsigned long flags;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
- if (!rdata) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0799 Target Reset rport failure: rdata x%p\n", rdata);
+ if (!rport)
+ return FAILED;
+
+ rdata = rport->dd_data;
+ if (!rdata || !rdata->pnode) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0799 Target Reset rdata failure: rdata x%px\n",
+ rdata);
return FAILED;
}
pnode = rdata->pnode;
- status = fc_block_scsi_eh(cmnd);
- if (status)
+ status = fc_block_rport(rport);
+ if (status != 0 && status != SUCCESS)
return status;
- status = lpfc_chk_tgt_mapped(vport, cmnd);
+ status = lpfc_chk_tgt_mapped(vport, rport);
if (status == FAILED) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0722 Target Reset rport failure: rdata x%p\n", rdata);
- return FAILED;
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0722 Target Reset rport failure: rdata x%px\n", rdata);
+ if (pnode) {
+ clear_bit(NLP_NPR_ADISC, &pnode->nlp_flag);
+ spin_lock_irqsave(&pnode->lock, flags);
+ pnode->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ spin_unlock_irqrestore(&pnode->lock, flags);
+ }
+ status = lpfc_reset_flush_io_context(vport, tgt_id, lun_id,
+ LPFC_CTX_TGT);
+ if (status != SUCCESS) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "0726 Target Reset flush status x%x\n",
+ status);
+ return status;
+ }
+ return FAST_IO_FAIL;
}
scsi_event.event_type = FC_REG_SCSI_EVENT;
@@ -3680,11 +6129,46 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID);
- status = lpfc_send_taskmgmt(vport, rdata, tgt_id, lun_id,
+ status = lpfc_send_taskmgmt(vport, rport, tgt_id, lun_id,
FCP_TARGET_RESET);
+ if (status != SUCCESS) {
+ logit = LOG_TRACE_EVENT;
+
+ /* Issue LOGO, if no LOGO is outstanding */
+ spin_lock_irqsave(&pnode->lock, flags);
+ if (!test_bit(NLP_WAIT_FOR_LOGO, &pnode->save_flags) &&
+ !pnode->logo_waitq) {
+ pnode->logo_waitq = &waitq;
+ pnode->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ spin_unlock_irqrestore(&pnode->lock, flags);
+ set_bit(NLP_ISSUE_LOGO, &pnode->nlp_flag);
+ set_bit(NLP_WAIT_FOR_LOGO, &pnode->save_flags);
+ lpfc_unreg_rpi(vport, pnode);
+ wait_event_timeout(waitq,
+ !test_bit(NLP_WAIT_FOR_LOGO,
+ &pnode->save_flags),
+ secs_to_jiffies(dev_loss_tmo));
+
+ if (test_and_clear_bit(NLP_WAIT_FOR_LOGO,
+ &pnode->save_flags))
+ lpfc_printf_vlog(vport, KERN_ERR, logit,
+ "0725 SCSI layer TGTRST "
+ "failed & LOGO TMO (%d, %llu) "
+ "return x%x\n",
+ tgt_id, lun_id, status);
+ spin_lock_irqsave(&pnode->lock, flags);
+ pnode->logo_waitq = NULL;
+ spin_unlock_irqrestore(&pnode->lock, flags);
+ status = SUCCESS;
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0723 SCSI layer issued Target Reset (%d, %d) "
+ } else {
+ spin_unlock_irqrestore(&pnode->lock, flags);
+ status = FAILED;
+ }
+ }
+
+ lpfc_printf_vlog(vport, KERN_ERR, logit,
+ "0723 SCSI layer issued Target Reset (%d, %llu) "
"return x%x\n", tgt_id, lun_id, status);
/*
@@ -3693,96 +6177,68 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd)
* So, continue on.
* We will report success if all the i/o aborts successfully.
*/
- status = lpfc_reset_flush_io_context(vport, tgt_id, lun_id,
- LPFC_CTX_TGT);
+ if (status == SUCCESS)
+ status = lpfc_reset_flush_io_context(vport, tgt_id, lun_id,
+ LPFC_CTX_TGT);
return status;
}
/**
- * lpfc_bus_reset_handler - scsi_host_template eh_bus_reset_handler entry point
+ * lpfc_host_reset_handler - scsi_host_template eh_host_reset_handler entry pt
* @cmnd: Pointer to scsi_cmnd data structure.
*
- * This routine does target reset to all targets on @cmnd->device->host.
- * This emulates Parallel SCSI Bus Reset Semantics.
+ * This routine does host reset to the adaptor port. It brings the HBA
+ * offline, performs a board restart, and then brings the board back online.
+ * The lpfc_offline calls lpfc_sli_hba_down which will abort and local
+ * reject all outstanding SCSI commands to the host and error returned
+ * back to SCSI mid-level. As this will be SCSI mid-level's last resort
+ * of error handling, it will only return error if resetting of the adapter
+ * is not successful; in all other cases, will return success.
*
* Return code :
* 0x2003 - Error
* 0x2002 - Success
**/
static int
-lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
+lpfc_host_reset_handler(struct scsi_cmnd *cmnd)
{
- struct Scsi_Host *shost = cmnd->device->host;
+ struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
- struct lpfc_nodelist *ndlp = NULL;
- struct lpfc_scsi_event_header scsi_event;
- int match;
- int ret = SUCCESS, status, i;
-
- scsi_event.event_type = FC_REG_SCSI_EVENT;
- scsi_event.subcategory = LPFC_EVENT_BUSRESET;
- scsi_event.lun = 0;
- memcpy(scsi_event.wwpn, &vport->fc_portname, sizeof(struct lpfc_name));
- memcpy(scsi_event.wwnn, &vport->fc_nodename, sizeof(struct lpfc_name));
-
- fc_host_post_vendor_event(shost, fc_get_event_number(),
- sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID);
-
- ret = fc_block_scsi_eh(cmnd);
- if (ret)
- return ret;
-
- /*
- * Since the driver manages a single bus device, reset all
- * targets known to the driver. Should any target reset
- * fail, this routine returns failure to the midlayer.
- */
- for (i = 0; i < LPFC_MAX_TARGET; i++) {
- /* Search for mapped node by target ID */
- match = 0;
- spin_lock_irq(shost->host_lock);
- list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
- if (ndlp->nlp_state == NLP_STE_MAPPED_NODE &&
- ndlp->nlp_sid == i &&
- ndlp->rport) {
- match = 1;
- break;
- }
- }
- spin_unlock_irq(shost->host_lock);
- if (!match)
- continue;
-
- status = lpfc_send_taskmgmt(vport, ndlp->rport->dd_data,
- i, 0, FCP_TARGET_RESET);
+ struct lpfc_hba *phba = vport->phba;
+ int rc, ret = SUCCESS;
- if (status != SUCCESS) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0700 Bus Reset on target %d failed\n",
- i);
- ret = FAILED;
- }
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "3172 SCSI layer issued Host Reset\n");
+
+ lpfc_offline_prep(phba, LPFC_MBX_WAIT);
+ lpfc_offline(phba);
+ rc = lpfc_sli_brdrestart(phba);
+ if (rc)
+ goto error;
+
+ /* Wait for successful restart of adapter */
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ rc = lpfc_sli_chipset_init(phba);
+ if (rc)
+ goto error;
}
- /*
- * We have to clean up i/o as : they may be orphaned by the TMFs
- * above; or if any of the TMFs failed, they may be in an
- * indeterminate state.
- * We will report success if all the i/o aborts successfully.
- */
- status = lpfc_reset_flush_io_context(vport, 0, 0, LPFC_CTX_HOST);
- if (status != SUCCESS)
- ret = FAILED;
+ rc = lpfc_online(phba);
+ if (rc)
+ goto error;
+
+ lpfc_unblock_mgmt_io(phba);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0714 SCSI layer issued Bus Reset Data: x%x\n", ret);
return ret;
+error:
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "3323 Failed host reset\n");
+ lpfc_unblock_mgmt_io(phba);
+ return FAILED;
}
/**
- * lpfc_slave_alloc - scsi_host_template slave_alloc entry point
+ * lpfc_sdev_init - scsi_host_template sdev_init entry point
* @sdev: Pointer to scsi_device.
*
* This routine populates the cmds_per_lun count + 2 scsi_bufs into this host's
@@ -3795,7 +6251,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
* 0 - Success
**/
static int
-lpfc_slave_alloc(struct scsi_device *sdev)
+lpfc_sdev_init(struct scsi_device *sdev)
{
struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
struct lpfc_hba *phba = vport->phba;
@@ -3804,13 +6260,55 @@ lpfc_slave_alloc(struct scsi_device *sdev)
uint32_t num_to_alloc = 0;
int num_allocated = 0;
uint32_t sdev_cnt;
+ struct lpfc_device_data *device_data;
+ unsigned long flags;
+ struct lpfc_name target_wwpn;
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
- sdev->hostdata = rport->dd_data;
+ if (phba->cfg_fof) {
+
+ /*
+ * Check to see if the device data structure for the lun
+ * exists. If not, create one.
+ */
+
+ u64_to_wwn(rport->port_name, target_wwpn.u.wwn);
+ spin_lock_irqsave(&phba->devicelock, flags);
+ device_data = __lpfc_get_device_data(phba,
+ &phba->luns,
+ &vport->fc_portname,
+ &target_wwpn,
+ sdev->lun);
+ if (!device_data) {
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ device_data = lpfc_create_device_data(phba,
+ &vport->fc_portname,
+ &target_wwpn,
+ sdev->lun,
+ phba->cfg_XLanePriority,
+ true);
+ if (!device_data)
+ return -ENOMEM;
+ spin_lock_irqsave(&phba->devicelock, flags);
+ list_add_tail(&device_data->listentry, &phba->luns);
+ }
+ device_data->rport_data = rport->dd_data;
+ device_data->available = true;
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ sdev->hostdata = device_data;
+ } else {
+ sdev->hostdata = rport->dd_data;
+ }
sdev_cnt = atomic_inc_return(&phba->sdev_cnt);
+ /* For SLI4, all IO buffers are pre-allocated */
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return 0;
+
+ /* This code path is now ONLY for SLI3 adapters */
+
/*
* Populate the cmds_per_lun count scsi_bufs into this host's globally
* available list of scsi buffers. Don't allocate more than the
@@ -3842,13 +6340,13 @@ lpfc_slave_alloc(struct scsi_device *sdev)
(phba->cfg_hba_queue_depth - total));
num_to_alloc = phba->cfg_hba_queue_depth - total;
}
- num_allocated = lpfc_new_scsi_buf(vport, num_to_alloc);
+ num_allocated = lpfc_new_scsi_buf_s3(vport, num_to_alloc);
if (num_to_alloc != num_allocated) {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0708 Allocation request of %d "
- "command buffers did not succeed. "
- "Allocated %d buffers.\n",
- num_to_alloc, num_allocated);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "0708 Allocation request of %d "
+ "command buffers did not succeed. "
+ "Allocated %d buffers.\n",
+ num_to_alloc, num_allocated);
}
if (num_allocated > 0)
phba->total_scsi_bufs += num_allocated;
@@ -3856,8 +6354,9 @@ lpfc_slave_alloc(struct scsi_device *sdev)
}
/**
- * lpfc_slave_configure - scsi_host_template slave_configure entry point
+ * lpfc_sdev_configure - scsi_host_template sdev_configure entry point
* @sdev: Pointer to scsi_device.
+ * @lim: Request queue limits.
*
* This routine configures following items
* - Tag command queuing support for @sdev if supported.
@@ -3867,19 +6366,16 @@ lpfc_slave_alloc(struct scsi_device *sdev)
* 0 - Success
**/
static int
-lpfc_slave_configure(struct scsi_device *sdev)
+lpfc_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
struct lpfc_hba *phba = vport->phba;
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, vport->cfg_lun_queue_depth);
- else
- scsi_deactivate_tcq(sdev, vport->cfg_lun_queue_depth);
+ scsi_change_queue_depth(sdev, vport->cfg_lun_queue_depth);
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
lpfc_sli_handle_fast_ring_event(phba,
- &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+ &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
@@ -3888,63 +6384,450 @@ lpfc_slave_configure(struct scsi_device *sdev)
}
/**
- * lpfc_slave_destroy - slave_destroy entry point of SHT data structure
+ * lpfc_sdev_destroy - sdev_destroy entry point of SHT data structure
* @sdev: Pointer to scsi_device.
*
* This routine sets @sdev hostatdata filed to null.
**/
static void
-lpfc_slave_destroy(struct scsi_device *sdev)
+lpfc_sdev_destroy(struct scsi_device *sdev)
{
struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
struct lpfc_hba *phba = vport->phba;
+ unsigned long flags;
+ struct lpfc_device_data *device_data = sdev->hostdata;
+
atomic_dec(&phba->sdev_cnt);
+ if ((phba->cfg_fof) && (device_data)) {
+ spin_lock_irqsave(&phba->devicelock, flags);
+ device_data->available = false;
+ if (!device_data->oas_enabled)
+ lpfc_delete_device_data(phba, device_data);
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ }
sdev->hostdata = NULL;
return;
}
+/**
+ * lpfc_create_device_data - creates and initializes device data structure for OAS
+ * @phba: Pointer to host bus adapter structure.
+ * @vport_wwpn: Pointer to vport's wwpn information
+ * @target_wwpn: Pointer to target's wwpn information
+ * @lun: Lun on target
+ * @pri: Priority
+ * @atomic_create: Flag to indicate if memory should be allocated using the
+ * GFP_ATOMIC flag or not.
+ *
+ * This routine creates a device data structure which will contain identifying
+ * information for the device (host wwpn, target wwpn, lun), state of OAS,
+ * whether or not the corresponding lun is available by the system,
+ * and pointer to the rport data.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_device_data - Success
+ **/
+struct lpfc_device_data*
+lpfc_create_device_data(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
+ struct lpfc_name *target_wwpn, uint64_t lun,
+ uint32_t pri, bool atomic_create)
+{
+
+ struct lpfc_device_data *lun_info;
+ gfp_t memory_flags;
+
+ if (unlikely(!phba) || !vport_wwpn || !target_wwpn ||
+ !(phba->cfg_fof))
+ return NULL;
+
+ /* Attempt to create the device data to contain lun info */
+
+ if (atomic_create)
+ memory_flags = GFP_ATOMIC;
+ else
+ memory_flags = GFP_KERNEL;
+ lun_info = mempool_alloc(phba->device_data_mem_pool, memory_flags);
+ if (!lun_info)
+ return NULL;
+ INIT_LIST_HEAD(&lun_info->listentry);
+ lun_info->rport_data = NULL;
+ memcpy(&lun_info->device_id.vport_wwpn, vport_wwpn,
+ sizeof(struct lpfc_name));
+ memcpy(&lun_info->device_id.target_wwpn, target_wwpn,
+ sizeof(struct lpfc_name));
+ lun_info->device_id.lun = lun;
+ lun_info->oas_enabled = false;
+ lun_info->priority = pri;
+ lun_info->available = false;
+ return lun_info;
+}
+
+/**
+ * lpfc_delete_device_data - frees a device data structure for OAS
+ * @phba: Pointer to host bus adapter structure.
+ * @lun_info: Pointer to device data structure to free.
+ *
+ * This routine frees the previously allocated device data structure passed.
+ *
+ **/
+void
+lpfc_delete_device_data(struct lpfc_hba *phba,
+ struct lpfc_device_data *lun_info)
+{
+
+ if (unlikely(!phba) || !lun_info ||
+ !(phba->cfg_fof))
+ return;
+
+ if (!list_empty(&lun_info->listentry))
+ list_del(&lun_info->listentry);
+ mempool_free(lun_info, phba->device_data_mem_pool);
+ return;
+}
+
+/**
+ * __lpfc_get_device_data - returns the device data for the specified lun
+ * @phba: Pointer to host bus adapter structure.
+ * @list: Point to list to search.
+ * @vport_wwpn: Pointer to vport's wwpn information
+ * @target_wwpn: Pointer to target's wwpn information
+ * @lun: Lun on target
+ *
+ * This routine searches the list passed for the specified lun's device data.
+ * This function does not hold locks, it is the responsibility of the caller
+ * to ensure the proper lock is held before calling the function.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_device_data - Success
+ **/
+struct lpfc_device_data*
+__lpfc_get_device_data(struct lpfc_hba *phba, struct list_head *list,
+ struct lpfc_name *vport_wwpn,
+ struct lpfc_name *target_wwpn, uint64_t lun)
+{
+
+ struct lpfc_device_data *lun_info;
+
+ if (unlikely(!phba) || !list || !vport_wwpn || !target_wwpn ||
+ !phba->cfg_fof)
+ return NULL;
+
+ /* Check to see if the lun is already enabled for OAS. */
+
+ list_for_each_entry(lun_info, list, listentry) {
+ if ((memcmp(&lun_info->device_id.vport_wwpn, vport_wwpn,
+ sizeof(struct lpfc_name)) == 0) &&
+ (memcmp(&lun_info->device_id.target_wwpn, target_wwpn,
+ sizeof(struct lpfc_name)) == 0) &&
+ (lun_info->device_id.lun == lun))
+ return lun_info;
+ }
+
+ return NULL;
+}
+
+/**
+ * lpfc_find_next_oas_lun - searches for the next oas lun
+ * @phba: Pointer to host bus adapter structure.
+ * @vport_wwpn: Pointer to vport's wwpn information
+ * @target_wwpn: Pointer to target's wwpn information
+ * @starting_lun: Pointer to the lun to start searching for
+ * @found_vport_wwpn: Pointer to the found lun's vport wwpn information
+ * @found_target_wwpn: Pointer to the found lun's target wwpn information
+ * @found_lun: Pointer to the found lun.
+ * @found_lun_status: Pointer to status of the found lun.
+ * @found_lun_pri: Pointer to priority of the found lun.
+ *
+ * This routine searches the luns list for the specified lun
+ * or the first lun for the vport/target. If the vport wwpn contains
+ * a zero value then a specific vport is not specified. In this case
+ * any vport which contains the lun will be considered a match. If the
+ * target wwpn contains a zero value then a specific target is not specified.
+ * In this case any target which contains the lun will be considered a
+ * match. If the lun is found, the lun, vport wwpn, target wwpn and lun status
+ * are returned. The function will also return the next lun if available.
+ * If the next lun is not found, starting_lun parameter will be set to
+ * NO_MORE_OAS_LUN.
+ *
+ * Return codes:
+ * non-0 - Error
+ * 0 - Success
+ **/
+bool
+lpfc_find_next_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
+ struct lpfc_name *target_wwpn, uint64_t *starting_lun,
+ struct lpfc_name *found_vport_wwpn,
+ struct lpfc_name *found_target_wwpn,
+ uint64_t *found_lun,
+ uint32_t *found_lun_status,
+ uint32_t *found_lun_pri)
+{
+
+ unsigned long flags;
+ struct lpfc_device_data *lun_info;
+ struct lpfc_device_id *device_id;
+ uint64_t lun;
+ bool found = false;
+
+ if (unlikely(!phba) || !vport_wwpn || !target_wwpn ||
+ !starting_lun || !found_vport_wwpn ||
+ !found_target_wwpn || !found_lun || !found_lun_status ||
+ (*starting_lun == NO_MORE_OAS_LUN) ||
+ !phba->cfg_fof)
+ return false;
+
+ lun = *starting_lun;
+ *found_lun = NO_MORE_OAS_LUN;
+ *starting_lun = NO_MORE_OAS_LUN;
+
+ /* Search for lun or the lun closet in value */
+
+ spin_lock_irqsave(&phba->devicelock, flags);
+ list_for_each_entry(lun_info, &phba->luns, listentry) {
+ if (((wwn_to_u64(vport_wwpn->u.wwn) == 0) ||
+ (memcmp(&lun_info->device_id.vport_wwpn, vport_wwpn,
+ sizeof(struct lpfc_name)) == 0)) &&
+ ((wwn_to_u64(target_wwpn->u.wwn) == 0) ||
+ (memcmp(&lun_info->device_id.target_wwpn, target_wwpn,
+ sizeof(struct lpfc_name)) == 0)) &&
+ (lun_info->oas_enabled)) {
+ device_id = &lun_info->device_id;
+ if ((!found) &&
+ ((lun == FIND_FIRST_OAS_LUN) ||
+ (device_id->lun == lun))) {
+ *found_lun = device_id->lun;
+ memcpy(found_vport_wwpn,
+ &device_id->vport_wwpn,
+ sizeof(struct lpfc_name));
+ memcpy(found_target_wwpn,
+ &device_id->target_wwpn,
+ sizeof(struct lpfc_name));
+ if (lun_info->available)
+ *found_lun_status =
+ OAS_LUN_STATUS_EXISTS;
+ else
+ *found_lun_status = 0;
+ *found_lun_pri = lun_info->priority;
+ if (phba->cfg_oas_flags & OAS_FIND_ANY_VPORT)
+ memset(vport_wwpn, 0x0,
+ sizeof(struct lpfc_name));
+ if (phba->cfg_oas_flags & OAS_FIND_ANY_TARGET)
+ memset(target_wwpn, 0x0,
+ sizeof(struct lpfc_name));
+ found = true;
+ } else if (found) {
+ *starting_lun = device_id->lun;
+ memcpy(vport_wwpn, &device_id->vport_wwpn,
+ sizeof(struct lpfc_name));
+ memcpy(target_wwpn, &device_id->target_wwpn,
+ sizeof(struct lpfc_name));
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ return found;
+}
+
+/**
+ * lpfc_enable_oas_lun - enables a lun for OAS operations
+ * @phba: Pointer to host bus adapter structure.
+ * @vport_wwpn: Pointer to vport's wwpn information
+ * @target_wwpn: Pointer to target's wwpn information
+ * @lun: Lun
+ * @pri: Priority
+ *
+ * This routine enables a lun for oas operations. The routines does so by
+ * doing the following :
+ *
+ * 1) Checks to see if the device data for the lun has been created.
+ * 2) If found, sets the OAS enabled flag if not set and returns.
+ * 3) Otherwise, creates a device data structure.
+ * 4) If successfully created, indicates the device data is for an OAS lun,
+ * indicates the lun is not available and add to the list of luns.
+ *
+ * Return codes:
+ * false - Error
+ * true - Success
+ **/
+bool
+lpfc_enable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
+ struct lpfc_name *target_wwpn, uint64_t lun, uint8_t pri)
+{
+
+ struct lpfc_device_data *lun_info;
+ unsigned long flags;
+
+ if (unlikely(!phba) || !vport_wwpn || !target_wwpn ||
+ !phba->cfg_fof)
+ return false;
+
+ spin_lock_irqsave(&phba->devicelock, flags);
+
+ /* Check to see if the device data for the lun has been created */
+ lun_info = __lpfc_get_device_data(phba, &phba->luns, vport_wwpn,
+ target_wwpn, lun);
+ if (lun_info) {
+ if (!lun_info->oas_enabled)
+ lun_info->oas_enabled = true;
+ lun_info->priority = pri;
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ return true;
+ }
+
+ /* Create an lun info structure and add to list of luns */
+ lun_info = lpfc_create_device_data(phba, vport_wwpn, target_wwpn, lun,
+ pri, true);
+ if (lun_info) {
+ lun_info->oas_enabled = true;
+ lun_info->priority = pri;
+ lun_info->available = false;
+ list_add_tail(&lun_info->listentry, &phba->luns);
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ return true;
+ }
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ return false;
+}
+
+/**
+ * lpfc_disable_oas_lun - disables a lun for OAS operations
+ * @phba: Pointer to host bus adapter structure.
+ * @vport_wwpn: Pointer to vport's wwpn information
+ * @target_wwpn: Pointer to target's wwpn information
+ * @lun: Lun
+ * @pri: Priority
+ *
+ * This routine disables a lun for oas operations. The routines does so by
+ * doing the following :
+ *
+ * 1) Checks to see if the device data for the lun is created.
+ * 2) If present, clears the flag indicating this lun is for OAS.
+ * 3) If the lun is not available by the system, the device data is
+ * freed.
+ *
+ * Return codes:
+ * false - Error
+ * true - Success
+ **/
+bool
+lpfc_disable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
+ struct lpfc_name *target_wwpn, uint64_t lun, uint8_t pri)
+{
+
+ struct lpfc_device_data *lun_info;
+ unsigned long flags;
+
+ if (unlikely(!phba) || !vport_wwpn || !target_wwpn ||
+ !phba->cfg_fof)
+ return false;
+
+ spin_lock_irqsave(&phba->devicelock, flags);
+
+ /* Check to see if the lun is available. */
+ lun_info = __lpfc_get_device_data(phba,
+ &phba->luns, vport_wwpn,
+ target_wwpn, lun);
+ if (lun_info) {
+ lun_info->oas_enabled = false;
+ lun_info->priority = pri;
+ if (!lun_info->available)
+ lpfc_delete_device_data(phba, lun_info);
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ return true;
+ }
+
+ spin_unlock_irqrestore(&phba->devicelock, flags);
+ return false;
+}
+
+static int
+lpfc_no_command(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
+{
+ return SCSI_MLQUEUE_HOST_BUSY;
+}
+
+static int
+lpfc_init_no_sdev(struct scsi_device *sdev)
+{
+ return -ENODEV;
+}
+
+static int
+lpfc_config_no_sdev(struct scsi_device *sdev, struct queue_limits *lim)
+{
+ return -ENODEV;
+}
+
+struct scsi_host_template lpfc_template_nvme = {
+ .module = THIS_MODULE,
+ .name = LPFC_DRIVER_NAME,
+ .proc_name = LPFC_DRIVER_NAME,
+ .info = lpfc_info,
+ .queuecommand = lpfc_no_command,
+ .sdev_init = lpfc_init_no_sdev,
+ .sdev_configure = lpfc_config_no_sdev,
+ .scan_finished = lpfc_scan_finished,
+ .this_id = -1,
+ .sg_tablesize = 1,
+ .cmd_per_lun = 1,
+ .shost_groups = lpfc_hba_groups,
+ .max_sectors = 0xFFFFFFFF,
+ .vendor_id = LPFC_NL_VENDOR_ID,
+ .track_queue_depth = 0,
+};
struct scsi_host_template lpfc_template = {
.module = THIS_MODULE,
.name = LPFC_DRIVER_NAME,
+ .proc_name = LPFC_DRIVER_NAME,
.info = lpfc_info,
.queuecommand = lpfc_queuecommand,
+ .eh_timed_out = fc_eh_timed_out,
+ .eh_should_retry_cmd = fc_eh_should_retry_cmd,
.eh_abort_handler = lpfc_abort_handler,
.eh_device_reset_handler = lpfc_device_reset_handler,
.eh_target_reset_handler = lpfc_target_reset_handler,
- .eh_bus_reset_handler = lpfc_bus_reset_handler,
- .slave_alloc = lpfc_slave_alloc,
- .slave_configure = lpfc_slave_configure,
- .slave_destroy = lpfc_slave_destroy,
+ .eh_host_reset_handler = lpfc_host_reset_handler,
+ .sdev_init = lpfc_sdev_init,
+ .sdev_configure = lpfc_sdev_configure,
+ .sdev_destroy = lpfc_sdev_destroy,
.scan_finished = lpfc_scan_finished,
.this_id = -1,
.sg_tablesize = LPFC_DEFAULT_SG_SEG_CNT,
.cmd_per_lun = LPFC_CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = lpfc_hba_attrs,
- .max_sectors = 0xFFFF,
+ .shost_groups = lpfc_hba_groups,
+ .max_sectors = 0xFFFFFFFF,
.vendor_id = LPFC_NL_VENDOR_ID,
- .change_queue_depth = lpfc_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
+ .track_queue_depth = 1,
};
struct scsi_host_template lpfc_vport_template = {
.module = THIS_MODULE,
.name = LPFC_DRIVER_NAME,
+ .proc_name = LPFC_DRIVER_NAME,
.info = lpfc_info,
.queuecommand = lpfc_queuecommand,
+ .eh_timed_out = fc_eh_timed_out,
+ .eh_should_retry_cmd = fc_eh_should_retry_cmd,
.eh_abort_handler = lpfc_abort_handler,
.eh_device_reset_handler = lpfc_device_reset_handler,
.eh_target_reset_handler = lpfc_target_reset_handler,
- .eh_bus_reset_handler = lpfc_bus_reset_handler,
- .slave_alloc = lpfc_slave_alloc,
- .slave_configure = lpfc_slave_configure,
- .slave_destroy = lpfc_slave_destroy,
+ .eh_bus_reset_handler = NULL,
+ .eh_host_reset_handler = NULL,
+ .sdev_init = lpfc_sdev_init,
+ .sdev_configure = lpfc_sdev_configure,
+ .sdev_destroy = lpfc_sdev_destroy,
.scan_finished = lpfc_scan_finished,
.this_id = -1,
.sg_tablesize = LPFC_DEFAULT_SG_SEG_CNT,
.cmd_per_lun = LPFC_CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = lpfc_vport_attrs,
- .max_sectors = 0xFFFF,
- .change_queue_depth = lpfc_change_queue_depth,
+ .shost_groups = lpfc_vport_groups,
+ .max_sectors = 0xFFFFFFFF,
+ .vendor_id = 0,
+ .change_queue_depth = scsi_change_queue_depth,
+ .track_queue_depth = 1,
};
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index ce645b20a6ad..a05d203e4777 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2006 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -21,6 +23,8 @@
#include <asm/byteorder.h>
struct lpfc_hba;
+#define LPFC_FCP_CDB_LEN 16
+#define LPFC_FCP_CDB_LEN_32 32
#define list_remove_head(list, entry, type, member) \
do { \
@@ -40,6 +44,21 @@ struct lpfc_rport_data {
struct lpfc_nodelist *pnode; /* Pointer to the node structure. */
};
+struct lpfc_device_id {
+ struct lpfc_name vport_wwpn;
+ struct lpfc_name target_wwpn;
+ uint64_t lun;
+};
+
+struct lpfc_device_data {
+ struct list_head listentry;
+ struct lpfc_rport_data *rport_data;
+ struct lpfc_device_id device_id;
+ uint8_t priority;
+ bool oas_enabled;
+ bool available;
+};
+
struct fcp_rsp {
uint32_t rspRsvd1; /* FC Word 0, byte 0:3 */
uint32_t rspRsvd2; /* FC Word 1, byte 0:3 */
@@ -72,6 +91,7 @@ struct fcp_rsp {
#define RSP_RO_MISMATCH_ERR 0x03
#define RSP_TM_NOT_SUPPORTED 0x04 /* Task mgmt function not supported */
#define RSP_TM_NOT_COMPLETED 0x05 /* Task mgmt function not performed */
+#define RSP_TM_INVALID_LU 0x09 /* Task mgmt function to invalid LU */
uint32_t rspInfoRsvd; /* FCP_RSP_INFO bytes 4-7 (reserved) */
@@ -80,17 +100,11 @@ struct fcp_rsp {
#define SNSCOD_BADCMD 0x20 /* sense code is byte 13 ([12]) */
};
-struct fcp_cmnd {
- struct scsi_lun fcp_lun;
-
- uint8_t fcpCntl0; /* FCP_CNTL byte 0 (reserved) */
- uint8_t fcpCntl1; /* FCP_CNTL byte 1 task codes */
#define SIMPLE_Q 0x00
#define HEAD_OF_Q 0x01
#define ORDERED_Q 0x02
#define ACA_Q 0x04
#define UNTAGGED 0x05
- uint8_t fcpCntl2; /* FCP_CTL byte 2 task management codes */
#define FCP_ABORT_TASK_SET 0x02 /* Bit 1 */
#define FCP_CLEAR_TASK_SET 0x04 /* bit 2 */
#define FCP_BUS_RESET 0x08 /* bit 3 */
@@ -98,60 +112,52 @@ struct fcp_cmnd {
#define FCP_TARGET_RESET 0x20 /* bit 5 */
#define FCP_CLEAR_ACA 0x40 /* bit 6 */
#define FCP_TERMINATE_TASK 0x80 /* bit 7 */
- uint8_t fcpCntl3;
#define WRITE_DATA 0x01 /* Bit 0 */
#define READ_DATA 0x02 /* Bit 1 */
- uint8_t fcpCdb[16]; /* SRB cdb field is copied here */
- uint32_t fcpDl; /* Total transfer length */
+struct fcp_cmnd {
+ struct scsi_lun fcp_lun;
-};
+ uint8_t fcpCntl0; /* FCP_CNTL byte 0 (reserved) */
+ uint8_t fcpCntl1; /* FCP_CNTL byte 1 task codes */
+ uint8_t fcpCntl2; /* FCP_CTL byte 2 task management codes */
+ uint8_t fcpCntl3;
-struct lpfc_scsicmd_bkt {
- uint32_t cmd_count;
-};
+ uint8_t fcpCdb[LPFC_FCP_CDB_LEN]; /* SRB cdb field is copied here */
+ __be32 fcpDl; /* Total transfer length */
-struct lpfc_scsi_buf {
- struct list_head list;
- struct scsi_cmnd *pCmd;
- struct lpfc_rport_data *rdata;
+};
+struct fcp_cmnd32 {
+ struct scsi_lun fcp_lun;
- uint32_t timeout;
+ uint8_t fcpCntl0; /* FCP_CNTL byte 0 (reserved) */
+ uint8_t fcpCntl1; /* FCP_CNTL byte 1 task codes */
+ uint8_t fcpCntl2; /* FCP_CTL byte 2 task management codes */
+ uint8_t fcpCntl3;
- uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */
- uint16_t status; /* From IOCB Word 7- ulpStatus */
- uint32_t result; /* From IOCB Word 4. */
+ uint8_t fcpCdb[LPFC_FCP_CDB_LEN_32]; /* SRB cdb field is copied here */
+ __be32 fcpDl; /* Total transfer length */
- uint32_t seg_cnt; /* Number of scatter-gather segments returned by
- * dma_map_sg. The driver needs this for calls
- * to dma_unmap_sg. */
- uint32_t prot_seg_cnt; /* seg_cnt's counterpart for protection data */
+};
- dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */
+#define LPFC_SCSI_DMA_EXT_SIZE 264
+#define LPFC_BPL_SIZE 1024
+#define MDAC_DIRECT_CMD 0x22
- /*
- * data and dma_handle are the kernel virtual and bus address of the
- * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter
- * gather bde list that supports the sg_tablesize value.
- */
- void *data;
- dma_addr_t dma_handle;
+#define FIND_FIRST_OAS_LUN 0
+#define NO_MORE_OAS_LUN -1
+#define NOT_OAS_ENABLED_LUN NO_MORE_OAS_LUN
- struct fcp_cmnd *fcp_cmnd;
- struct fcp_rsp *fcp_rsp;
- struct ulp_bde64 *fcp_bpl;
+#ifndef FC_PORTSPEED_128GBIT
+#define FC_PORTSPEED_128GBIT 0x2000
+#endif
- dma_addr_t dma_phys_bpl;
+#ifndef FC_PORTSPEED_256GBIT
+#define FC_PORTSPEED_256GBIT 0x4000
+#endif
- /* cur_iocbq has phys of the dma-able buffer.
- * Iotag is in here
- */
- struct lpfc_iocbq cur_iocbq;
- wait_queue_head_t *waitq;
- unsigned long start_time;
-};
+#define TXRDY_PAYLOAD_LEN 12
-#define LPFC_SCSI_DMA_EXT_SIZE 264
-#define LPFC_BPL_SIZE 1024
+/* For sysfs/debugfs tmp string max len */
+#define LPFC_MAX_SCSI_INFO_TMP_LEN 79
-#define MDAC_DIRECT_CMD 0x22
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 98999bbd8cbf..73d77cfab5f8 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -24,6 +26,9 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/lockdep.h>
+#include <linux/dmi.h>
+#include <linux/of.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -31,7 +36,10 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>
-#include <linux/aer.h>
+#include <linux/crash_dump.h>
+#ifdef CONFIG_X86
+#include <asm/set_memory.h>
+#endif
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -39,13 +47,15 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
+#include "lpfc_nvme.h"
#include "lpfc_crtn.h"
#include "lpfc_logmsg.h"
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
#include "lpfc_vport.h"
+#include "lpfc_version.h"
/* There are only four IOCB completion types. */
typedef enum _lpfc_iocb_type {
@@ -61,18 +71,184 @@ static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
uint32_t);
static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
uint8_t *, uint32_t *);
-static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *,
- struct lpfc_iocbq *);
+static struct lpfc_iocbq *
+lpfc_sli4_els_preprocess_rspiocbq(struct lpfc_hba *phba,
+ struct lpfc_iocbq *rspiocbq);
static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
struct hbq_dmabuf *);
-static int lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *, struct lpfc_queue *,
- struct lpfc_cqe *);
+static void lpfc_sli4_handle_mds_loopback(struct lpfc_vport *vport,
+ struct hbq_dmabuf *dmabuf);
+static bool lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba,
+ struct lpfc_queue *cq, struct lpfc_cqe *cqe);
+static int lpfc_sli4_post_sgl_list(struct lpfc_hba *, struct list_head *,
+ int);
+static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba,
+ struct lpfc_queue *eq,
+ struct lpfc_eqe *eqe,
+ enum lpfc_poll_mode poll_mode);
+static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba);
+static bool lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba);
+static struct lpfc_cqe *lpfc_sli4_cq_get(struct lpfc_queue *q);
+static void __lpfc_sli4_consume_cqe(struct lpfc_hba *phba,
+ struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe);
+static uint16_t lpfc_wqe_bpl2sgl(struct lpfc_hba *phba,
+ struct lpfc_iocbq *pwqeq,
+ struct lpfc_sglq *sglq);
+
+union lpfc_wqe128 lpfc_iread_cmd_template;
+union lpfc_wqe128 lpfc_iwrite_cmd_template;
+union lpfc_wqe128 lpfc_icmnd_cmd_template;
+
+/* Setup WQE templates for IOs */
+void lpfc_wqe_cmd_template(void)
+{
+ union lpfc_wqe128 *wqe;
-static IOCB_t *
-lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
+ /* IREAD template */
+ wqe = &lpfc_iread_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - cmd_buff_len, payload_offset_len is zero */
+
+ /* Word 4 - total_xfer_len is variable */
+
+ /* Word 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_iread.wqe_com, CMD_FCP_IREAD64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, PARM_READ_CHECK);
+ bf_set(wqe_class, &wqe->fcp_iread.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_iread.wqe_com, SLI4_CT_RPI);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 - dbde, wqes is variable */
+ bf_set(wqe_qosd, &wqe->fcp_iread.wqe_com, 0);
+ bf_set(wqe_iod, &wqe->fcp_iread.wqe_com, LPFC_WQE_IOD_READ);
+ bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com, LPFC_WQE_LENLOC_WORD4);
+ bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 0);
+ bf_set(wqe_wqes, &wqe->fcp_iread.wqe_com, 1);
+
+ /* Word 11 - pbde is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_iread.wqe_com, COMMAND_DATA_IN);
+ bf_set(wqe_cqid, &wqe->fcp_iread.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 0);
+
+ /* Word 12 - is zero */
+
+ /* Word 13, 14, 15 - PBDE is variable */
+
+ /* IWRITE template */
+ wqe = &lpfc_iwrite_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - cmd_buff_len, payload_offset_len is zero */
+
+ /* Word 4 - total_xfer_len is variable */
+
+ /* Word 5 - initial_xfer_len is variable */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_iwrite.wqe_com, CMD_FCP_IWRITE64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, PARM_READ_CHECK);
+ bf_set(wqe_class, &wqe->fcp_iwrite.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_iwrite.wqe_com, SLI4_CT_RPI);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 - dbde, wqes is variable */
+ bf_set(wqe_qosd, &wqe->fcp_iwrite.wqe_com, 0);
+ bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_LENLOC_WORD4);
+ bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 0);
+ bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1);
+
+ /* Word 11 - pbde is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_iwrite.wqe_com, COMMAND_DATA_OUT);
+ bf_set(wqe_cqid, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 0);
+
+ /* Word 12 - is zero */
+
+ /* Word 13, 14, 15 - PBDE is variable */
+
+ /* ICMND template */
+ wqe = &lpfc_icmnd_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - payload_offset_len is variable */
+
+ /* Word 4, 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_icmd.wqe_com, CMD_FCP_ICMND64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_icmd.wqe_com, 0);
+ bf_set(wqe_class, &wqe->fcp_icmd.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_icmd.wqe_com, SLI4_CT_RPI);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 - dbde, wqes is variable */
+ bf_set(wqe_qosd, &wqe->fcp_icmd.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->fcp_icmd.wqe_com, LPFC_WQE_IOD_NONE);
+ bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com, LPFC_WQE_LENLOC_NONE);
+ bf_set(wqe_dbde, &wqe->fcp_icmd.wqe_com, 0);
+ bf_set(wqe_wqes, &wqe->fcp_icmd.wqe_com, 1);
+
+ /* Word 11 */
+ bf_set(wqe_cmd_type, &wqe->fcp_icmd.wqe_com, COMMAND_DATA_IN);
+ bf_set(wqe_cqid, &wqe->fcp_icmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_pbde, &wqe->fcp_icmd.wqe_com, 0);
+
+ /* Word 12, 13, 14, 15 - is zero */
+}
+
+#if defined(CONFIG_64BIT) && defined(__LITTLE_ENDIAN)
+/**
+ * lpfc_sli4_pcimem_bcopy - SLI4 memory copy function
+ * @srcp: Source memory pointer.
+ * @destp: Destination memory pointer.
+ * @cnt: Number of words required to be copied.
+ * Must be a multiple of sizeof(uint64_t)
+ *
+ * This function is used for copying data between driver memory
+ * and the SLI WQ. This function also changes the endianness
+ * of each word if native endianness is different from SLI
+ * endianness. This function can be called with or without
+ * lock.
+ **/
+static void
+lpfc_sli4_pcimem_bcopy(void *srcp, void *destp, uint32_t cnt)
{
- return &iocbq->iocb;
+ uint64_t *src = srcp;
+ uint64_t *dest = destp;
+ int i;
+
+ for (i = 0; i < (int)cnt; i += sizeof(uint64_t))
+ *dest++ = *src++;
}
+#else
+#define lpfc_sli4_pcimem_bcopy(a, b, c) lpfc_sli_pcimem_bcopy(a, b, c)
+#endif
/**
* lpfc_sli4_wq_put - Put a Work Queue Entry on an Work Queue
@@ -86,34 +262,87 @@ lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
* -ENOMEM.
* The caller is expected to hold the hbalock when calling this routine.
**/
-static uint32_t
-lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
+static int
+lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe128 *wqe)
{
- union lpfc_wqe *temp_wqe = q->qe[q->host_index].wqe;
+ union lpfc_wqe *temp_wqe;
struct lpfc_register doorbell;
uint32_t host_index;
+ uint32_t idx;
+ uint32_t i = 0;
+ uint8_t *tmp;
+ u32 if_type;
- /* If the host has not yet processed the next entry then we are done */
- if (((q->host_index + 1) % q->entry_count) == q->hba_index)
+ /* sanity check on queue memory */
+ if (unlikely(!q))
return -ENOMEM;
+
+ temp_wqe = lpfc_sli4_qe(q, q->host_index);
+
+ /* If the host has not yet processed the next entry then we are done */
+ idx = ((q->host_index + 1) % q->entry_count);
+ if (idx == q->hba_index) {
+ q->WQ_overflow++;
+ return -EBUSY;
+ }
+ q->WQ_posted++;
/* set consumption flag every once in a while */
- if (!((q->host_index + 1) % LPFC_RELEASE_NOTIFICATION_INTERVAL))
+ if (!((q->host_index + 1) % q->notify_interval))
bf_set(wqe_wqec, &wqe->generic.wqe_com, 1);
+ else
+ bf_set(wqe_wqec, &wqe->generic.wqe_com, 0);
if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED)
bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id);
- lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
+ lpfc_sli4_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
+ if (q->dpp_enable && q->phba->cfg_enable_dpp) {
+ /* write to DPP aperture taking advatage of Combined Writes */
+ tmp = (uint8_t *)temp_wqe;
+#ifdef __raw_writeq
+ for (i = 0; i < q->entry_size; i += sizeof(uint64_t))
+ __raw_writeq(*((uint64_t *)(tmp + i)),
+ q->dpp_regaddr + i);
+#else
+ for (i = 0; i < q->entry_size; i += sizeof(uint32_t))
+ __raw_writel(*((uint32_t *)(tmp + i)),
+ q->dpp_regaddr + i);
+#endif
+ }
+ /* ensure WQE bcopy and DPP flushed before doorbell write */
+ wmb();
/* Update the host index before invoking device */
host_index = q->host_index;
- q->host_index = ((q->host_index + 1) % q->entry_count);
+
+ q->host_index = idx;
/* Ring Doorbell */
doorbell.word0 = 0;
- bf_set(lpfc_wq_doorbell_num_posted, &doorbell, 1);
- bf_set(lpfc_wq_doorbell_index, &doorbell, host_index);
- bf_set(lpfc_wq_doorbell_id, &doorbell, q->queue_id);
- writel(doorbell.word0, q->phba->sli4_hba.WQDBregaddr);
- readl(q->phba->sli4_hba.WQDBregaddr); /* Flush */
+ if (q->db_format == LPFC_DB_LIST_FORMAT) {
+ if (q->dpp_enable && q->phba->cfg_enable_dpp) {
+ bf_set(lpfc_if6_wq_db_list_fm_num_posted, &doorbell, 1);
+ bf_set(lpfc_if6_wq_db_list_fm_dpp, &doorbell, 1);
+ bf_set(lpfc_if6_wq_db_list_fm_dpp_id, &doorbell,
+ q->dpp_id);
+ bf_set(lpfc_if6_wq_db_list_fm_id, &doorbell,
+ q->queue_id);
+ } else {
+ bf_set(lpfc_wq_db_list_fm_num_posted, &doorbell, 1);
+ bf_set(lpfc_wq_db_list_fm_id, &doorbell, q->queue_id);
+
+ /* Leave bits <23:16> clear for if_type 6 dpp */
+ if_type = bf_get(lpfc_sli_intf_if_type,
+ &q->phba->sli4_hba.sli_intf);
+ if (if_type != LPFC_SLI_INTF_IF_TYPE_6)
+ bf_set(lpfc_wq_db_list_fm_index, &doorbell,
+ host_index);
+ }
+ } else if (q->db_format == LPFC_DB_RING_FORMAT) {
+ bf_set(lpfc_wq_db_ring_fm_num_posted, &doorbell, 1);
+ bf_set(lpfc_wq_db_ring_fm_id, &doorbell, q->queue_id);
+ } else {
+ return -EINVAL;
+ }
+ writel(doorbell.word0, q->db_regaddr);
return 0;
}
@@ -126,27 +355,22 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
* This routine will update the HBA index of a queue to reflect consumption of
* Work Queue Entries by the HBA. When the HBA indicates that it has consumed
* an entry the host calls this function to update the queue's internal
- * pointers. This routine returns the number of entries that were consumed by
- * the HBA.
+ * pointers.
**/
-static uint32_t
+static void
lpfc_sli4_wq_release(struct lpfc_queue *q, uint32_t index)
{
- uint32_t released = 0;
+ /* sanity check on queue memory */
+ if (unlikely(!q))
+ return;
- if (q->hba_index == index)
- return 0;
- do {
- q->hba_index = ((q->hba_index + 1) % q->entry_count);
- released++;
- } while (q->hba_index != index);
- return released;
+ q->hba_index = index;
}
/**
* lpfc_sli4_mq_put - Put a Mailbox Queue Entry on an Mailbox Queue
* @q: The Mailbox Queue to operate on.
- * @wqe: The Mailbox Queue Entry to put on the Work queue.
+ * @mqe: The Mailbox Queue Entry to put on the Work queue.
*
* This routine will copy the contents of @mqe to the next available entry on
* the @q. This function will then ring the Work Queue Doorbell to signal the
@@ -158,19 +382,22 @@ lpfc_sli4_wq_release(struct lpfc_queue *q, uint32_t index)
static uint32_t
lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe)
{
- struct lpfc_mqe *temp_mqe = q->qe[q->host_index].mqe;
+ struct lpfc_mqe *temp_mqe;
struct lpfc_register doorbell;
- uint32_t host_index;
+
+ /* sanity check on queue memory */
+ if (unlikely(!q))
+ return -ENOMEM;
+ temp_mqe = lpfc_sli4_qe(q, q->host_index);
/* If the host has not yet processed the next entry then we are done */
if (((q->host_index + 1) % q->entry_count) == q->hba_index)
return -ENOMEM;
- lpfc_sli_pcimem_bcopy(mqe, temp_mqe, q->entry_size);
+ lpfc_sli4_pcimem_bcopy(mqe, temp_mqe, q->entry_size);
/* Save off the mailbox pointer for completion */
q->phba->mbox = (MAILBOX_t *)temp_mqe;
/* Update the host index before invoking device */
- host_index = q->host_index;
q->host_index = ((q->host_index + 1) % q->entry_count);
/* Ring Doorbell */
@@ -178,7 +405,6 @@ lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe)
bf_set(lpfc_mq_doorbell_num_posted, &doorbell, 1);
bf_set(lpfc_mq_doorbell_id, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.MQDBregaddr);
- readl(q->phba->sli4_hba.MQDBregaddr); /* Flush */
return 0;
}
@@ -195,6 +421,10 @@ lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe)
static uint32_t
lpfc_sli4_mq_release(struct lpfc_queue *q)
{
+ /* sanity check on queue memory */
+ if (unlikely(!q))
+ return 0;
+
/* Clear the mailbox pointer for completion */
q->phba->mbox = NULL;
q->hba_index = ((q->hba_index + 1) % q->entry_count);
@@ -213,50 +443,84 @@ lpfc_sli4_mq_release(struct lpfc_queue *q)
static struct lpfc_eqe *
lpfc_sli4_eq_get(struct lpfc_queue *q)
{
- struct lpfc_eqe *eqe = q->qe[q->hba_index].eqe;
+ struct lpfc_eqe *eqe;
- /* If the next EQE is not valid then we are done */
- if (!bf_get_le32(lpfc_eqe_valid, eqe))
+ /* sanity check on queue memory */
+ if (unlikely(!q))
return NULL;
- /* If the host has not yet processed the next entry then we are done */
- if (((q->hba_index + 1) % q->entry_count) == q->host_index)
+ eqe = lpfc_sli4_qe(q, q->host_index);
+
+ /* If the next EQE is not valid then we are done */
+ if (bf_get_le32(lpfc_eqe_valid, eqe) != q->qe_valid)
return NULL;
- q->hba_index = ((q->hba_index + 1) % q->entry_count);
+ /*
+ * insert barrier for instruction interlock : data from the hardware
+ * must have the valid bit checked before it can be copied and acted
+ * upon. Speculative instructions were allowing a bcopy at the start
+ * of lpfc_sli4_fp_handle_wcqe(), which is called immediately
+ * after our return, to copy data before the valid bit check above
+ * was done. As such, some of the copied data was stale. The barrier
+ * ensures the check is before any data is copied.
+ */
+ mb();
return eqe;
}
/**
- * lpfc_sli4_eq_release - Indicates the host has finished processing an EQ
+ * lpfc_sli4_eq_clr_intr - Turn off interrupts from this EQ
+ * @q: The Event Queue to disable interrupts
+ *
+ **/
+void
+lpfc_sli4_eq_clr_intr(struct lpfc_queue *q)
+{
+ struct lpfc_register doorbell;
+
+ doorbell.word0 = 0;
+ bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
+ bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
+ bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
+ (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
+ bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
+}
+
+/**
+ * lpfc_sli4_if6_eq_clr_intr - Turn off interrupts from this EQ
+ * @q: The Event Queue to disable interrupts
+ *
+ **/
+void
+lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q)
+{
+ struct lpfc_register doorbell;
+
+ doorbell.word0 = 0;
+ bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
+}
+
+/**
+ * lpfc_sli4_write_eq_db - write EQ DB for eqe's consumed or arm state
+ * @phba: adapter with EQ
* @q: The Event Queue that the host has completed processing for.
+ * @count: Number of elements that have been consumed
* @arm: Indicates whether the host wants to arms this CQ.
*
- * This routine will mark all Event Queue Entries on @q, from the last
- * known completed entry to the last entry that was processed, as completed
- * by clearing the valid bit for each completion queue entry. Then it will
- * notify the HBA, by ringing the doorbell, that the EQEs have been processed.
- * The internal host index in the @q will be updated by this routine to indicate
- * that the host has finished processing the entries. The @arm parameter
- * indicates that the queue should be rearmed when ringing the doorbell.
- *
- * This function will return the number of EQEs that were popped.
+ * This routine will notify the HBA, by ringing the doorbell, that count
+ * number of EQEs have been processed. The @arm parameter indicates whether
+ * the queue should be rearmed when ringing the doorbell.
**/
-uint32_t
-lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
+void
+lpfc_sli4_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
{
- uint32_t released = 0;
- struct lpfc_eqe *temp_eqe;
struct lpfc_register doorbell;
- /* while there are valid entries */
- while (q->hba_index != q->host_index) {
- temp_eqe = q->qe[q->host_index].eqe;
- bf_set_le32(lpfc_eqe_valid, temp_eqe, 0);
- released++;
- q->host_index = ((q->host_index + 1) % q->entry_count);
- }
- if (unlikely(released == 0 && !arm))
- return 0;
+ /* sanity check on queue memory */
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
/* ring doorbell for number popped */
doorbell.word0 = 0;
@@ -264,14 +528,148 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
}
- bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count);
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
- bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id);
- writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
+ (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
+ bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
/* PCI read to flush PCI pipeline on re-arming for INTx mode */
if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
- readl(q->phba->sli4_hba.EQCQDBregaddr);
- return released;
+ readl(q->phba->sli4_hba.EQDBregaddr);
+}
+
+/**
+ * lpfc_sli4_if6_write_eq_db - write EQ DB for eqe's consumed or arm state
+ * @phba: adapter with EQ
+ * @q: The Event Queue that the host has completed processing for.
+ * @count: Number of elements that have been consumed
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will notify the HBA, by ringing the doorbell, that count
+ * number of EQEs have been processed. The @arm parameter indicates whether
+ * the queue should be rearmed when ringing the doorbell.
+ **/
+void
+lpfc_sli4_if6_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
+{
+ struct lpfc_register doorbell;
+
+ /* sanity check on queue memory */
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
+
+ /* ring doorbell for number popped */
+ doorbell.word0 = 0;
+ if (arm)
+ bf_set(lpfc_if6_eq_doorbell_arm, &doorbell, 1);
+ bf_set(lpfc_if6_eq_doorbell_num_released, &doorbell, count);
+ bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
+ /* PCI read to flush PCI pipeline on re-arming for INTx mode */
+ if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
+ readl(q->phba->sli4_hba.EQDBregaddr);
+}
+
+static void
+__lpfc_sli4_consume_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ struct lpfc_eqe *eqe)
+{
+ if (!phba->sli4_hba.pc_sli4_params.eqav)
+ bf_set_le32(lpfc_eqe_valid, eqe, 0);
+
+ eq->host_index = ((eq->host_index + 1) % eq->entry_count);
+
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.eqav && !eq->host_index)
+ eq->qe_valid = (eq->qe_valid) ? 0 : 1;
+}
+
+static void
+lpfc_sli4_eqcq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
+{
+ struct lpfc_eqe *eqe = NULL;
+ u32 eq_count = 0, cq_count = 0;
+ struct lpfc_cqe *cqe = NULL;
+ struct lpfc_queue *cq = NULL, *childq = NULL;
+ int cqid = 0;
+
+ /* walk all the EQ entries and drop on the floor */
+ eqe = lpfc_sli4_eq_get(eq);
+ while (eqe) {
+ /* Get the reference to the corresponding CQ */
+ cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
+ cq = NULL;
+
+ list_for_each_entry(childq, &eq->child_list, list) {
+ if (childq->queue_id == cqid) {
+ cq = childq;
+ break;
+ }
+ }
+ /* If CQ is valid, iterate through it and drop all the CQEs */
+ if (cq) {
+ cqe = lpfc_sli4_cq_get(cq);
+ while (cqe) {
+ __lpfc_sli4_consume_cqe(phba, cq, cqe);
+ cq_count++;
+ cqe = lpfc_sli4_cq_get(cq);
+ }
+ /* Clear and re-arm the CQ */
+ phba->sli4_hba.sli4_write_cq_db(phba, cq, cq_count,
+ LPFC_QUEUE_REARM);
+ cq_count = 0;
+ }
+ __lpfc_sli4_consume_eqe(phba, eq, eqe);
+ eq_count++;
+ eqe = lpfc_sli4_eq_get(eq);
+ }
+
+ /* Clear and re-arm the EQ */
+ phba->sli4_hba.sli4_write_eq_db(phba, eq, eq_count, LPFC_QUEUE_REARM);
+}
+
+static int
+lpfc_sli4_process_eq(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ u8 rearm, enum lpfc_poll_mode poll_mode)
+{
+ struct lpfc_eqe *eqe;
+ int count = 0, consumed = 0;
+
+ if (cmpxchg(&eq->queue_claimed, 0, 1) != 0)
+ goto rearm_and_exit;
+
+ eqe = lpfc_sli4_eq_get(eq);
+ while (eqe) {
+ lpfc_sli4_hba_handle_eqe(phba, eq, eqe, poll_mode);
+ __lpfc_sli4_consume_eqe(phba, eq, eqe);
+
+ consumed++;
+ if (!(++count % eq->max_proc_limit))
+ break;
+
+ if (!(count % eq->notify_interval)) {
+ phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed,
+ LPFC_QUEUE_NOARM);
+ consumed = 0;
+ }
+
+ eqe = lpfc_sli4_eq_get(eq);
+ }
+ eq->EQ_processed += count;
+
+ /* Track the max number of EQEs processed in 1 intr */
+ if (count > eq->EQ_max_eqe)
+ eq->EQ_max_eqe = count;
+
+ xchg(&eq->queue_claimed, 0);
+
+rearm_and_exit:
+ /* Always clear the EQ. */
+ phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed, rearm);
+
+ return count;
}
/**
@@ -288,65 +686,106 @@ lpfc_sli4_cq_get(struct lpfc_queue *q)
{
struct lpfc_cqe *cqe;
- /* If the next CQE is not valid then we are done */
- if (!bf_get_le32(lpfc_cqe_valid, q->qe[q->hba_index].cqe))
+ /* sanity check on queue memory */
+ if (unlikely(!q))
return NULL;
- /* If the host has not yet processed the next entry then we are done */
- if (((q->hba_index + 1) % q->entry_count) == q->host_index)
+ cqe = lpfc_sli4_qe(q, q->host_index);
+
+ /* If the next CQE is not valid then we are done */
+ if (bf_get_le32(lpfc_cqe_valid, cqe) != q->qe_valid)
return NULL;
- cqe = q->qe[q->hba_index].cqe;
- q->hba_index = ((q->hba_index + 1) % q->entry_count);
+ /*
+ * insert barrier for instruction interlock : data from the hardware
+ * must have the valid bit checked before it can be copied and acted
+ * upon. Given what was seen in lpfc_sli4_cq_get() of speculative
+ * instructions allowing action on content before valid bit checked,
+ * add barrier here as well. May not be needed as "content" is a
+ * single 32-bit entity here (vs multi word structure for cq's).
+ */
+ mb();
return cqe;
}
+static void
+__lpfc_sli4_consume_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
+{
+ if (!phba->sli4_hba.pc_sli4_params.cqav)
+ bf_set_le32(lpfc_cqe_valid, cqe, 0);
+
+ cq->host_index = ((cq->host_index + 1) % cq->entry_count);
+
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.cqav && !cq->host_index)
+ cq->qe_valid = (cq->qe_valid) ? 0 : 1;
+}
+
/**
- * lpfc_sli4_cq_release - Indicates the host has finished processing a CQ
+ * lpfc_sli4_write_cq_db - write cq DB for entries consumed or arm state.
+ * @phba: the adapter with the CQ
* @q: The Completion Queue that the host has completed processing for.
+ * @count: the number of elements that were consumed
* @arm: Indicates whether the host wants to arms this CQ.
*
- * This routine will mark all Completion queue entries on @q, from the last
- * known completed entry to the last entry that was processed, as completed
- * by clearing the valid bit for each completion queue entry. Then it will
- * notify the HBA, by ringing the doorbell, that the CQEs have been processed.
- * The internal host index in the @q will be updated by this routine to indicate
- * that the host has finished processing the entries. The @arm parameter
- * indicates that the queue should be rearmed when ringing the doorbell.
- *
- * This function will return the number of CQEs that were released.
+ * This routine will notify the HBA, by ringing the doorbell, that the
+ * CQEs have been processed. The @arm parameter specifies whether the
+ * queue should be rearmed when ringing the doorbell.
**/
-uint32_t
-lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
+void
+lpfc_sli4_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
{
- uint32_t released = 0;
- struct lpfc_cqe *temp_qe;
struct lpfc_register doorbell;
- /* while there are valid entries */
- while (q->hba_index != q->host_index) {
- temp_qe = q->qe[q->host_index].cqe;
- bf_set_le32(lpfc_cqe_valid, temp_qe, 0);
- released++;
- q->host_index = ((q->host_index + 1) % q->entry_count);
- }
- if (unlikely(released == 0 && !arm))
- return 0;
+ /* sanity check on queue memory */
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
/* ring doorbell for number popped */
doorbell.word0 = 0;
if (arm)
bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
- bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count);
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_COMPLETION);
- bf_set(lpfc_eqcq_doorbell_cqid, &doorbell, q->queue_id);
- writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
- return released;
+ bf_set(lpfc_eqcq_doorbell_cqid_hi, &doorbell,
+ (q->queue_id >> LPFC_CQID_HI_FIELD_SHIFT));
+ bf_set(lpfc_eqcq_doorbell_cqid_lo, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
}
/**
+ * lpfc_sli4_if6_write_cq_db - write cq DB for entries consumed or arm state.
+ * @phba: the adapter with the CQ
+ * @q: The Completion Queue that the host has completed processing for.
+ * @count: the number of elements that were consumed
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will notify the HBA, by ringing the doorbell, that the
+ * CQEs have been processed. The @arm parameter specifies whether the
+ * queue should be rearmed when ringing the doorbell.
+ **/
+void
+lpfc_sli4_if6_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm)
+{
+ struct lpfc_register doorbell;
+
+ /* sanity check on queue memory */
+ if (unlikely(!q || (count == 0 && !arm)))
+ return;
+
+ /* ring doorbell for number popped */
+ doorbell.word0 = 0;
+ if (arm)
+ bf_set(lpfc_if6_cq_doorbell_arm, &doorbell, 1);
+ bf_set(lpfc_if6_cq_doorbell_num_released, &doorbell, count);
+ bf_set(lpfc_if6_cq_doorbell_cqid, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
+}
+
+/*
* lpfc_sli4_rq_put - Put a Receive Buffer Queue Entry on a Receive Queue
- * @q: The Header Receive Queue to operate on.
- * @wqe: The Receive Queue Entry to put on the Receive queue.
*
* This routine will copy the contents of @wqe to the next available entry on
* the @q. This function will then ring the Receive Queue Doorbell to signal the
@@ -355,43 +794,62 @@ lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
* on @q then this function will return -ENOMEM.
* The caller is expected to hold the hbalock when calling this routine.
**/
-static int
+int
lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
struct lpfc_rqe *hrqe, struct lpfc_rqe *drqe)
{
- struct lpfc_rqe *temp_hrqe = hq->qe[hq->host_index].rqe;
- struct lpfc_rqe *temp_drqe = dq->qe[dq->host_index].rqe;
+ struct lpfc_rqe *temp_hrqe;
+ struct lpfc_rqe *temp_drqe;
struct lpfc_register doorbell;
- int put_index = hq->host_index;
+ int hq_put_index;
+ int dq_put_index;
+
+ /* sanity check on queue memory */
+ if (unlikely(!hq) || unlikely(!dq))
+ return -ENOMEM;
+ hq_put_index = hq->host_index;
+ dq_put_index = dq->host_index;
+ temp_hrqe = lpfc_sli4_qe(hq, hq_put_index);
+ temp_drqe = lpfc_sli4_qe(dq, dq_put_index);
if (hq->type != LPFC_HRQ || dq->type != LPFC_DRQ)
return -EINVAL;
- if (hq->host_index != dq->host_index)
+ if (hq_put_index != dq_put_index)
return -EINVAL;
/* If the host has not yet processed the next entry then we are done */
- if (((hq->host_index + 1) % hq->entry_count) == hq->hba_index)
+ if (((hq_put_index + 1) % hq->entry_count) == hq->hba_index)
return -EBUSY;
- lpfc_sli_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size);
- lpfc_sli_pcimem_bcopy(drqe, temp_drqe, dq->entry_size);
+ lpfc_sli4_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size);
+ lpfc_sli4_pcimem_bcopy(drqe, temp_drqe, dq->entry_size);
/* Update the host index to point to the next slot */
- hq->host_index = ((hq->host_index + 1) % hq->entry_count);
- dq->host_index = ((dq->host_index + 1) % dq->entry_count);
+ hq->host_index = ((hq_put_index + 1) % hq->entry_count);
+ dq->host_index = ((dq_put_index + 1) % dq->entry_count);
+ hq->RQ_buf_posted++;
/* Ring The Header Receive Queue Doorbell */
- if (!(hq->host_index % LPFC_RQ_POST_BATCH)) {
+ if (!(hq->host_index % hq->notify_interval)) {
doorbell.word0 = 0;
- bf_set(lpfc_rq_doorbell_num_posted, &doorbell,
- LPFC_RQ_POST_BATCH);
- bf_set(lpfc_rq_doorbell_id, &doorbell, hq->queue_id);
- writel(doorbell.word0, hq->phba->sli4_hba.RQDBregaddr);
+ if (hq->db_format == LPFC_DB_RING_FORMAT) {
+ bf_set(lpfc_rq_db_ring_fm_num_posted, &doorbell,
+ hq->notify_interval);
+ bf_set(lpfc_rq_db_ring_fm_id, &doorbell, hq->queue_id);
+ } else if (hq->db_format == LPFC_DB_LIST_FORMAT) {
+ bf_set(lpfc_rq_db_list_fm_num_posted, &doorbell,
+ hq->notify_interval);
+ bf_set(lpfc_rq_db_list_fm_index, &doorbell,
+ hq->host_index);
+ bf_set(lpfc_rq_db_list_fm_id, &doorbell, hq->queue_id);
+ } else {
+ return -EINVAL;
+ }
+ writel(doorbell.word0, hq->db_regaddr);
}
- return put_index;
+ return hq_put_index;
}
-/**
+/*
* lpfc_sli4_rq_release - Updates internal hba index for RQ
- * @q: The Header Receive Queue to operate on.
*
* This routine will update the HBA index of a queue to reflect consumption of
* one Receive Queue Entry by the HBA. When the HBA indicates that it has
@@ -402,6 +860,10 @@ lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
static uint32_t
lpfc_sli4_rq_release(struct lpfc_queue *hq, struct lpfc_queue *dq)
{
+ /* sanity check on queue memory */
+ if (unlikely(!hq) || unlikely(!dq))
+ return 0;
+
if ((hq->type != LPFC_HRQ) || (dq->type != LPFC_DRQ))
return 0;
hq->hba_index = ((hq->hba_index + 1) % hq->entry_count);
@@ -422,8 +884,8 @@ lpfc_sli4_rq_release(struct lpfc_queue *hq, struct lpfc_queue *dq)
static inline IOCB_t *
lpfc_cmd_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
- return (IOCB_t *) (((char *) pring->cmdringaddr) +
- pring->cmdidx * phba->iocb_cmd_size);
+ return (IOCB_t *) (((char *) pring->sli.sli3.cmdringaddr) +
+ pring->sli.sli3.cmdidx * phba->iocb_cmd_size);
}
/**
@@ -439,8 +901,8 @@ lpfc_cmd_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
static inline IOCB_t *
lpfc_resp_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
- return (IOCB_t *) (((char *) pring->rspringaddr) +
- pring->rspidx * phba->iocb_rsp_size);
+ return (IOCB_t *) (((char *) pring->sli.sli3.rspringaddr) +
+ pring->sli.sli3.rspidx * phba->iocb_rsp_size);
}
/**
@@ -452,12 +914,14 @@ lpfc_resp_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
* allocation is successful, it returns pointer to the newly
* allocated iocb object else it returns NULL.
**/
-static struct lpfc_iocbq *
+struct lpfc_iocbq *
__lpfc_sli_get_iocbq(struct lpfc_hba *phba)
{
struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
struct lpfc_iocbq * iocbq = NULL;
+ lockdep_assert_held(&phba->hbalock);
+
list_remove_head(lpfc_iocb_list, iocbq, struct lpfc_iocbq, list);
if (iocbq)
phba->iocb_cnt++;
@@ -471,14 +935,14 @@ __lpfc_sli_get_iocbq(struct lpfc_hba *phba)
* @phba: Pointer to HBA context object.
* @xritag: XRI value.
*
- * This function clears the sglq pointer from the array of acive
+ * This function clears the sglq pointer from the array of active
* sglq's. The xritag that is passed in is used to index into the
* array. Before the xritag can be used it needs to be adjusted
* by subtracting the xribase.
*
* Returns sglq ponter = success, NULL = Failure.
**/
-static struct lpfc_sglq *
+struct lpfc_sglq *
__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
{
struct lpfc_sglq *sglq;
@@ -493,7 +957,7 @@ __lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
* @phba: Pointer to HBA context object.
* @xritag: XRI value.
*
- * This function returns the sglq pointer from the array of acive
+ * This function returns the sglq pointer from the array of active
* sglq's. The xritag that is passed in is used to index into the
* array. Before the xritag can be used it needs to be adjusted
* by subtracting the xribase.
@@ -510,81 +974,6 @@ __lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
}
/**
- * __lpfc_set_rrq_active - set RRQ active bit in the ndlp's xri_bitmap.
- * @phba: Pointer to HBA context object.
- * @ndlp: nodelist pointer for this target.
- * @xritag: xri used in this exchange.
- * @rxid: Remote Exchange ID.
- * @send_rrq: Flag used to determine if we should send rrq els cmd.
- *
- * This function is called with hbalock held.
- * The active bit is set in the ndlp's active rrq xri_bitmap. Allocates an
- * rrq struct and adds it to the active_rrq_list.
- *
- * returns 0 for rrq slot for this xri
- * < 0 Were not able to get rrq mem or invalid parameter.
- **/
-static int
-__lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
- uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
-{
- struct lpfc_node_rrq *rrq;
- int empty;
- uint32_t did = 0;
-
-
- if (!ndlp)
- return -EINVAL;
-
- if (!phba->cfg_enable_rrq)
- return -EINVAL;
-
- if (phba->pport->load_flag & FC_UNLOADING) {
- phba->hba_flag &= ~HBA_RRQ_ACTIVE;
- goto out;
- }
- did = ndlp->nlp_DID;
-
- /*
- * set the active bit even if there is no mem available.
- */
- if (NLP_CHK_FREE_REQ(ndlp))
- goto out;
-
- if (ndlp->vport && (ndlp->vport->load_flag & FC_UNLOADING))
- goto out;
-
- if (test_and_set_bit(xritag, ndlp->active_rrqs.xri_bitmap))
- goto out;
-
- rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL);
- if (rrq) {
- rrq->send_rrq = send_rrq;
- rrq->xritag = phba->sli4_hba.xri_ids[xritag];
- rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1);
- rrq->ndlp = ndlp;
- rrq->nlp_DID = ndlp->nlp_DID;
- rrq->vport = ndlp->vport;
- rrq->rxid = rxid;
- empty = list_empty(&phba->active_rrq_list);
- rrq->send_rrq = send_rrq;
- list_add_tail(&rrq->list, &phba->active_rrq_list);
- if (!(phba->hba_flag & HBA_RRQ_ACTIVE)) {
- phba->hba_flag |= HBA_RRQ_ACTIVE;
- if (empty)
- lpfc_worker_wake_up(phba);
- }
- return 0;
- }
-out:
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "2921 Can't set rrq active xri:0x%x rxid:0x%x"
- " DID:0x%x Send:%d\n",
- xritag, rxid, did, send_rrq);
- return -EINVAL;
-}
-
-/**
* lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
* @phba: Pointer to HBA context object.
* @xritag: xri used in this exchange.
@@ -598,20 +987,14 @@ lpfc_clr_rrq_active(struct lpfc_hba *phba,
{
struct lpfc_nodelist *ndlp = NULL;
- if ((rrq->vport) && NLP_CHK_NODE_ACT(rrq->ndlp))
+ /* Lookup did to verify if did is still active on this vport */
+ if (rrq->vport)
ndlp = lpfc_findnode_did(rrq->vport, rrq->nlp_DID);
- /* The target DID could have been swapped (cable swap)
- * we should use the ndlp from the findnode if it is
- * available.
- */
- if ((!ndlp) && rrq->ndlp)
- ndlp = rrq->ndlp;
-
if (!ndlp)
goto out;
- if (test_and_clear_bit(xritag, ndlp->active_rrqs.xri_bitmap)) {
+ if (test_and_clear_bit(xritag, ndlp->active_rrqs_xri_bitmap)) {
rrq->send_rrq = 0;
rrq->xritag = 0;
rrq->rrq_stop_time = 0;
@@ -643,9 +1026,9 @@ lpfc_handle_rrq_active(struct lpfc_hba *phba)
unsigned long iflags;
LIST_HEAD(send_rrq);
- spin_lock_irqsave(&phba->hbalock, iflags);
- phba->hba_flag &= ~HBA_RRQ_ACTIVE;
- next_time = jiffies + HZ * (phba->fc_ratov + 1);
+ clear_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
+ next_time = jiffies + secs_to_jiffies(phba->fc_ratov + 1);
+ spin_lock_irqsave(&phba->rrq_list_lock, iflags);
list_for_each_entry_safe(rrq, nextrrq,
&phba->active_rrq_list, list) {
if (time_after(jiffies, rrq->rrq_stop_time))
@@ -653,15 +1036,16 @@ lpfc_handle_rrq_active(struct lpfc_hba *phba)
else if (time_before(rrq->rrq_stop_time, next_time))
next_time = rrq->rrq_stop_time;
}
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- if (!list_empty(&phba->active_rrq_list))
+ spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
+ if ((!list_empty(&phba->active_rrq_list)) &&
+ (!test_bit(FC_UNLOADING, &phba->pport->load_flag)))
mod_timer(&phba->rrq_tmr, next_time);
list_for_each_entry_safe(rrq, nextrrq, &send_rrq, list) {
list_del(&rrq->list);
- if (!rrq->send_rrq)
+ if (!rrq->send_rrq) {
/* this call will free the rrq */
- lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
- else if (lpfc_send_rrq(phba, rrq)) {
+ lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
+ } else if (lpfc_send_rrq(phba, rrq)) {
/* if we send the rrq then the completion handler
* will clear the bit in the xribitmap.
*/
@@ -690,16 +1074,16 @@ lpfc_get_active_rrq(struct lpfc_vport *vport, uint16_t xri, uint32_t did)
if (phba->sli_rev != LPFC_SLI_REV4)
return NULL;
- spin_lock_irqsave(&phba->hbalock, iflags);
+ spin_lock_irqsave(&phba->rrq_list_lock, iflags);
list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
if (rrq->vport == vport && rrq->xritag == xri &&
rrq->nlp_DID == did){
list_del(&rrq->list);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
return rrq;
}
}
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
return NULL;
}
@@ -727,61 +1111,32 @@ lpfc_cleanup_vports_rrqs(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
lpfc_sli4_vport_delete_els_xri_aborted(vport);
lpfc_sli4_vport_delete_fcp_xri_aborted(vport);
}
- spin_lock_irqsave(&phba->hbalock, iflags);
- list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list)
- if ((rrq->vport == vport) && (!ndlp || rrq->ndlp == ndlp))
+ spin_lock_irqsave(&phba->rrq_list_lock, iflags);
+ list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
+ if (rrq->vport != vport)
+ continue;
+
+ if (!ndlp || ndlp == lpfc_findnode_did(vport, rrq->nlp_DID))
list_move(&rrq->list, &rrq_list);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- list_for_each_entry_safe(rrq, nextrrq, &rrq_list, list) {
- list_del(&rrq->list);
- lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
}
-}
-
-/**
- * lpfc_cleanup_wt_rrqs - Remove all rrq's from the active list.
- * @phba: Pointer to HBA context object.
- *
- * Remove all rrqs from the phba->active_rrq_list and free them by
- * calling __lpfc_clr_active_rrq
- *
- **/
-void
-lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba)
-{
- struct lpfc_node_rrq *rrq;
- struct lpfc_node_rrq *nextrrq;
- unsigned long next_time;
- unsigned long iflags;
- LIST_HEAD(rrq_list);
-
- if (phba->sli_rev != LPFC_SLI_REV4)
- return;
- spin_lock_irqsave(&phba->hbalock, iflags);
- phba->hba_flag &= ~HBA_RRQ_ACTIVE;
- next_time = jiffies + HZ * (phba->fc_ratov * 2);
- list_splice_init(&phba->active_rrq_list, &rrq_list);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
list_for_each_entry_safe(rrq, nextrrq, &rrq_list, list) {
list_del(&rrq->list);
lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
}
- if (!list_empty(&phba->active_rrq_list))
- mod_timer(&phba->rrq_tmr, next_time);
}
-
/**
* lpfc_test_rrq_active - Test RRQ bit in xri_bitmap.
* @phba: Pointer to HBA context object.
* @ndlp: Targets nodelist pointer for this exchange.
- * @xritag the xri in the bitmap to test.
+ * @xritag: the xri in the bitmap to test.
*
- * This function is called with hbalock held. This function
- * returns 0 = rrq not active for this xri
- * 1 = rrq is valid for this xri.
+ * This function returns:
+ * 0 = rrq not active for this xri
+ * 1 = rrq is valid for this xri.
**/
int
lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
@@ -789,8 +1144,10 @@ lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
{
if (!ndlp)
return 0;
- if (test_bit(xritag, ndlp->active_rrqs.xri_bitmap))
- return 1;
+ if (!ndlp->active_rrqs_xri_bitmap)
+ return 0;
+ if (test_bit(xritag, ndlp->active_rrqs_xri_bitmap))
+ return 1;
else
return 0;
}
@@ -812,60 +1169,127 @@ lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
**/
int
lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
- uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
+ uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
{
- int ret;
unsigned long iflags;
+ struct lpfc_node_rrq *rrq;
+ int empty;
+
+ if (!ndlp)
+ return -EINVAL;
+
+ if (!phba->cfg_enable_rrq)
+ return -EINVAL;
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
+ clear_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
+ goto outnl;
+ }
spin_lock_irqsave(&phba->hbalock, iflags);
- ret = __lpfc_set_rrq_active(phba, ndlp, xritag, rxid, send_rrq);
+ if (ndlp->vport && test_bit(FC_UNLOADING, &ndlp->vport->load_flag))
+ goto out;
+
+ if (!ndlp->active_rrqs_xri_bitmap)
+ goto out;
+
+ if (test_and_set_bit(xritag, ndlp->active_rrqs_xri_bitmap))
+ goto out;
+
spin_unlock_irqrestore(&phba->hbalock, iflags);
- return ret;
+ rrq = mempool_alloc(phba->rrq_pool, GFP_ATOMIC);
+ if (!rrq) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3155 Unable to allocate RRQ xri:0x%x rxid:0x%x"
+ " DID:0x%x Send:%d\n",
+ xritag, rxid, ndlp->nlp_DID, send_rrq);
+ return -EINVAL;
+ }
+ if (phba->cfg_enable_rrq == 1)
+ rrq->send_rrq = send_rrq;
+ else
+ rrq->send_rrq = 0;
+ rrq->xritag = xritag;
+ rrq->rrq_stop_time = jiffies + secs_to_jiffies(phba->fc_ratov + 1);
+ rrq->nlp_DID = ndlp->nlp_DID;
+ rrq->vport = ndlp->vport;
+ rrq->rxid = rxid;
+
+ spin_lock_irqsave(&phba->rrq_list_lock, iflags);
+ empty = list_empty(&phba->active_rrq_list);
+ list_add_tail(&rrq->list, &phba->active_rrq_list);
+ spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
+ set_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
+ if (empty)
+ lpfc_worker_wake_up(phba);
+ return 0;
+out:
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+outnl:
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2921 Can't set rrq active xri:0x%x rxid:0x%x"
+ " DID:0x%x Send:%d\n",
+ xritag, rxid, ndlp->nlp_DID, send_rrq);
+ return -EINVAL;
}
/**
- * __lpfc_sli_get_sglq - Allocates an iocb object from sgl pool
+ * __lpfc_sli_get_els_sglq - Allocates an iocb object from sgl pool
* @phba: Pointer to HBA context object.
- * @piocb: Pointer to the iocbq.
+ * @piocbq: Pointer to the iocbq.
*
- * This function is called with hbalock held. This function
- * gets a new driver sglq object from the sglq list. If the
- * list is not empty then it is successful, it returns pointer to the newly
- * allocated sglq object else it returns NULL.
+ * The driver calls this function with either the nvme ls ring lock
+ * or the fc els ring lock held depending on the iocb usage. This function
+ * gets a new driver sglq object from the sglq list. If the list is not empty
+ * then it is successful, it returns pointer to the newly allocated sglq
+ * object else it returns NULL.
**/
static struct lpfc_sglq *
-__lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
+__lpfc_sli_get_els_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
{
- struct list_head *lpfc_sgl_list = &phba->sli4_hba.lpfc_sgl_list;
+ struct list_head *lpfc_els_sgl_list = &phba->sli4_hba.lpfc_els_sgl_list;
struct lpfc_sglq *sglq = NULL;
struct lpfc_sglq *start_sglq = NULL;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
struct lpfc_nodelist *ndlp;
int found = 0;
+ u8 cmnd;
- if (piocbq->iocb_flag & LPFC_IO_FCP) {
- lpfc_cmd = (struct lpfc_scsi_buf *) piocbq->context1;
+ cmnd = get_job_cmnd(phba, piocbq);
+
+ if (piocbq->cmd_flag & LPFC_IO_FCP) {
+ lpfc_cmd = piocbq->io_buf;
ndlp = lpfc_cmd->rdata->pnode;
- } else if ((piocbq->iocb.ulpCommand == CMD_GEN_REQUEST64_CR) &&
- !(piocbq->iocb_flag & LPFC_IO_LIBDFC))
- ndlp = piocbq->context_un.ndlp;
- else
- ndlp = piocbq->context1;
+ } else if ((cmnd == CMD_GEN_REQUEST64_CR) &&
+ !(piocbq->cmd_flag & LPFC_IO_LIBDFC)) {
+ ndlp = piocbq->ndlp;
+ } else if (piocbq->cmd_flag & LPFC_IO_LIBDFC) {
+ if (piocbq->cmd_flag & LPFC_IO_LOOPBACK)
+ ndlp = NULL;
+ else
+ ndlp = piocbq->ndlp;
+ } else {
+ ndlp = piocbq->ndlp;
+ }
- list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list);
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ list_remove_head(lpfc_els_sgl_list, sglq, struct lpfc_sglq, list);
start_sglq = sglq;
while (!found) {
if (!sglq)
- return NULL;
- if (lpfc_test_rrq_active(phba, ndlp, sglq->sli4_xritag)) {
+ break;
+ if (ndlp && ndlp->active_rrqs_xri_bitmap &&
+ test_bit(sglq->sli4_lxritag,
+ ndlp->active_rrqs_xri_bitmap)) {
/* This xri has an rrq outstanding for this DID.
* put it back in the list and get another xri.
*/
- list_add_tail(&sglq->list, lpfc_sgl_list);
+ list_add_tail(&sglq->list, lpfc_els_sgl_list);
sglq = NULL;
- list_remove_head(lpfc_sgl_list, sglq,
+ list_remove_head(lpfc_els_sgl_list, sglq,
struct lpfc_sglq, list);
if (sglq == start_sglq) {
+ list_add_tail(&sglq->list, lpfc_els_sgl_list);
sglq = NULL;
break;
} else
@@ -876,6 +1300,35 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
phba->sli4_hba.lpfc_sglq_active_list[sglq->sli4_lxritag] = sglq;
sglq->state = SGL_ALLOCATED;
}
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
+ return sglq;
+}
+
+/**
+ * __lpfc_sli_get_nvmet_sglq - Allocates an iocb object from sgl pool
+ * @phba: Pointer to HBA context object.
+ * @piocbq: Pointer to the iocbq.
+ *
+ * This function is called with the sgl_list lock held. This function
+ * gets a new driver sglq object from the sglq list. If the
+ * list is not empty then it is successful, it returns pointer to the newly
+ * allocated sglq object else it returns NULL.
+ **/
+struct lpfc_sglq *
+__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
+{
+ struct list_head *lpfc_nvmet_sgl_list;
+ struct lpfc_sglq *sglq = NULL;
+
+ lpfc_nvmet_sgl_list = &phba->sli4_hba.lpfc_nvmet_sgl_list;
+
+ lockdep_assert_held(&phba->sli4_hba.sgl_list_lock);
+
+ list_remove_head(lpfc_nvmet_sgl_list, sglq, struct lpfc_sglq, list);
+ if (!sglq)
+ return NULL;
+ phba->sli4_hba.lpfc_sglq_active_list[sglq->sli4_lxritag] = sglq;
+ sglq->state = SGL_ALLOCATED;
return sglq;
}
@@ -905,8 +1358,8 @@ lpfc_sli_get_iocbq(struct lpfc_hba *phba)
* @phba: Pointer to HBA context object.
* @iocbq: Pointer to driver iocb object.
*
- * This function is called with hbalock held to release driver
- * iocb object to the iocb pool. The iotag in the iocb object
+ * This function is called to release the driver iocb object
+ * to the iocb pool. The iotag in the iocb object
* does not change for each use of the iocb object. This function
* clears all other fields of the iocb object when it is freed.
* The sqlq structure that holds the xritag and phys and virtual
@@ -916,49 +1369,74 @@ lpfc_sli_get_iocbq(struct lpfc_hba *phba)
* this IO was aborted then the sglq entry it put on the
* lpfc_abts_els_sgl_list until the CQ_ABORTED_XRI is received. If the
* IO has good status or fails for any other reason then the sglq
- * entry is added to the free list (lpfc_sgl_list).
+ * entry is added to the free list (lpfc_els_sgl_list). The hbalock is
+ * asserted held in the code path calling this routine.
**/
static void
__lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{
struct lpfc_sglq *sglq;
- size_t start_clean = offsetof(struct lpfc_iocbq, iocb);
unsigned long iflag = 0;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring;
if (iocbq->sli4_xritag == NO_XRI)
sglq = NULL;
else
sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_lxritag);
+
if (sglq) {
- if ((iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) &&
- (sglq->state != SGL_XRI_ABORTED)) {
- spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
- iflag);
+ if (iocbq->cmd_flag & LPFC_IO_NVMET) {
+ spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock,
+ iflag);
+ sglq->state = SGL_FREED;
+ sglq->ndlp = NULL;
+ list_add_tail(&sglq->list,
+ &phba->sli4_hba.lpfc_nvmet_sgl_list);
+ spin_unlock_irqrestore(
+ &phba->sli4_hba.sgl_list_lock, iflag);
+ goto out;
+ }
+
+ if ((iocbq->cmd_flag & LPFC_EXCHANGE_BUSY) &&
+ (!(unlikely(pci_channel_offline(phba->pcidev)))) &&
+ sglq->state != SGL_XRI_ABORTED) {
+ spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock,
+ iflag);
+
+ /* Check if we can get a reference on ndlp */
+ if (sglq->ndlp && !lpfc_nlp_get(sglq->ndlp))
+ sglq->ndlp = NULL;
+
list_add(&sglq->list,
- &phba->sli4_hba.lpfc_abts_els_sgl_list);
+ &phba->sli4_hba.lpfc_abts_els_sgl_list);
spin_unlock_irqrestore(
- &phba->sli4_hba.abts_sgl_list_lock, iflag);
+ &phba->sli4_hba.sgl_list_lock, iflag);
} else {
+ spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock,
+ iflag);
sglq->state = SGL_FREED;
sglq->ndlp = NULL;
list_add_tail(&sglq->list,
- &phba->sli4_hba.lpfc_sgl_list);
-
+ &phba->sli4_hba.lpfc_els_sgl_list);
+ spin_unlock_irqrestore(
+ &phba->sli4_hba.sgl_list_lock, iflag);
+ pring = lpfc_phba_elsring(phba);
/* Check if TXQ queue needs to be serviced */
- if (pring->txq_cnt)
+ if (pring && (!list_empty(&pring->txq)))
lpfc_worker_wake_up(phba);
}
}
-
+out:
/*
* Clean all volatile data fields, preserve iotag and node struct.
*/
- memset((char *)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean);
+ memset_startat(iocbq, 0, wqe);
iocbq->sli4_lxritag = NO_XRI;
iocbq->sli4_xritag = NO_XRI;
+ iocbq->cmd_flag &= ~(LPFC_IO_NVME | LPFC_IO_NVMET | LPFC_IO_CMF |
+ LPFC_IO_NVME_LS);
list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
}
@@ -968,20 +1446,20 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
* @phba: Pointer to HBA context object.
* @iocbq: Pointer to driver iocb object.
*
- * This function is called with hbalock held to release driver
- * iocb object to the iocb pool. The iotag in the iocb object
- * does not change for each use of the iocb object. This function
- * clears all other fields of the iocb object when it is freed.
+ * This function is called to release the driver iocb object to the
+ * iocb pool. The iotag in the iocb object does not change for each
+ * use of the iocb object. This function clears all other fields of
+ * the iocb object when it is freed. The hbalock is asserted held in
+ * the code path calling this routine.
**/
static void
__lpfc_sli_release_iocbq_s3(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{
- size_t start_clean = offsetof(struct lpfc_iocbq, iocb);
/*
* Clean all volatile data fields, preserve iotag and node struct.
*/
- memset((char*)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean);
+ memset_startat(iocbq, 0, iocb);
iocbq->sli4_xritag = NO_XRI;
list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
}
@@ -999,6 +1477,8 @@ __lpfc_sli_release_iocbq_s3(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
static void
__lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{
+ lockdep_assert_held(&phba->hbalock);
+
phba->__lpfc_sli_release_iocbq(phba, iocbq);
phba->iocb_cnt--;
}
@@ -1044,13 +1524,23 @@ lpfc_sli_cancel_iocbs(struct lpfc_hba *phba, struct list_head *iocblist,
while (!list_empty(iocblist)) {
list_remove_head(iocblist, piocb, struct lpfc_iocbq, list);
-
- if (!piocb->iocb_cmpl)
+ if (piocb->cmd_cmpl) {
+ if (piocb->cmd_flag & LPFC_IO_NVME) {
+ lpfc_nvme_cancel_iocb(phba, piocb,
+ ulpstatus, ulpWord4);
+ } else {
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ bf_set(lpfc_wcqe_c_status,
+ &piocb->wcqe_cmpl, ulpstatus);
+ piocb->wcqe_cmpl.parameter = ulpWord4;
+ } else {
+ piocb->iocb.ulpStatus = ulpstatus;
+ piocb->iocb.un.ulpWord[4] = ulpWord4;
+ }
+ (piocb->cmd_cmpl) (phba, piocb, piocb);
+ }
+ } else {
lpfc_sli_release_iocbq(phba, piocb);
- else {
- piocb->iocb.ulpStatus = ulpstatus;
- piocb->iocb.un.ulpWord[4] = ulpWord4;
- (piocb->iocb_cmpl) (phba, piocb, piocb);
}
}
return;
@@ -1125,6 +1615,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
case DSSCMD_IWRITE64_CX:
case DSSCMD_IREAD64_CR:
case DSSCMD_IREAD64_CX:
+ case CMD_SEND_FRAME:
type = LPFC_SOL_IOCB;
break;
case CMD_ABORT_XRI_CN:
@@ -1199,7 +1690,7 @@ lpfc_sli_ring_map(struct lpfc_hba *phba)
lpfc_config_ring(phba, i, pmb);
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0446 Adapter failed to init (%d), "
"mbxCmd x%x CFG_RING, mbxStatus x%x, "
"ring %d\n",
@@ -1220,7 +1711,8 @@ lpfc_sli_ring_map(struct lpfc_hba *phba)
* @pring: Pointer to driver SLI ring object.
* @piocb: Pointer to the driver iocb object.
*
- * This function is called with hbalock held. The function adds the
+ * The driver calls this function with the hbalock held for SLI3 ports or
+ * the ring lock held for SLI4 ports. The function adds the
* new iocb to txcmplq of the given ring. This function always returns
* 0. If this function is called for ELS ring, this function checks if
* there is a vport associated with the ELS command. This function also
@@ -1230,23 +1722,24 @@ static int
lpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *piocb)
{
+ u32 ulp_command = 0;
+
+ BUG_ON(!piocb);
+ ulp_command = get_job_cmnd(phba, piocb);
+
list_add_tail(&piocb->list, &pring->txcmplq);
- piocb->iocb_flag |= LPFC_IO_ON_Q;
+ piocb->cmd_flag |= LPFC_IO_ON_TXCMPLQ;
pring->txcmplq_cnt++;
- if (pring->txcmplq_cnt > pring->txcmplq_max)
- pring->txcmplq_max = pring->txcmplq_cnt;
-
if ((unlikely(pring->ringno == LPFC_ELS_RING)) &&
- (piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
- (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN)) {
- if (!piocb->vport)
- BUG();
- else
+ (ulp_command != CMD_ABORT_XRI_WQE) &&
+ (ulp_command != CMD_ABORT_XRI_CN) &&
+ (ulp_command != CMD_CLOSE_XRI_CN)) {
+ BUG_ON(!piocb->vport);
+ if (!test_bit(FC_UNLOADING, &piocb->vport->load_flag))
mod_timer(&piocb->vport->els_tmofunc,
- jiffies + HZ * (phba->fc_ratov << 1));
+ jiffies + secs_to_jiffies(phba->fc_ratov << 1));
}
-
return 0;
}
@@ -1265,13 +1758,292 @@ lpfc_sli_ringtx_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
struct lpfc_iocbq *cmd_iocb;
+ lockdep_assert_held(&phba->hbalock);
+
list_remove_head((&pring->txq), cmd_iocb, struct lpfc_iocbq, list);
- if (cmd_iocb != NULL)
- pring->txq_cnt--;
return cmd_iocb;
}
/**
+ * lpfc_cmf_sync_cmpl - Process a CMF_SYNC_WQE cmpl
+ * @phba: Pointer to HBA context object.
+ * @cmdiocb: Pointer to driver command iocb object.
+ * @rspiocb: Pointer to driver response iocb object.
+ *
+ * This routine will inform the driver of any BW adjustments we need
+ * to make. These changes will be picked up during the next CMF
+ * timer interrupt. In addition, any BW changes will be logged
+ * with LOG_CGN_MGMT.
+ **/
+static void
+lpfc_cmf_sync_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ union lpfc_wqe128 *wqe;
+ uint32_t status, info;
+ struct lpfc_wcqe_complete *wcqe = &rspiocb->wcqe_cmpl;
+ uint64_t bw, bwdif, slop;
+ uint64_t pcent, bwpcent;
+ int asig, afpin, sigcnt, fpincnt;
+ int wsigmax, wfpinmax, cg, tdp;
+ char *s;
+
+ /* First check for error */
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+ if (status) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6211 CMF_SYNC_WQE Error "
+ "req_tag x%x status x%x hwstatus x%x "
+ "tdatap x%x parm x%x\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe),
+ bf_get(lpfc_wcqe_c_status, wcqe),
+ bf_get(lpfc_wcqe_c_hw_status, wcqe),
+ wcqe->total_data_placed,
+ wcqe->parameter);
+ goto out;
+ }
+
+ /* Gather congestion information on a successful cmpl */
+ info = wcqe->parameter;
+ phba->cmf_active_info = info;
+
+ /* See if firmware info count is valid or has changed */
+ if (info > LPFC_MAX_CMF_INFO || phba->cmf_info_per_interval == info)
+ info = 0;
+ else
+ phba->cmf_info_per_interval = info;
+
+ tdp = bf_get(lpfc_wcqe_c_cmf_bw, wcqe);
+ cg = bf_get(lpfc_wcqe_c_cmf_cg, wcqe);
+
+ /* Get BW requirement from firmware */
+ bw = (uint64_t)tdp * LPFC_CMF_BLK_SIZE;
+ if (!bw) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6212 CMF_SYNC_WQE x%x: NULL bw\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ goto out;
+ }
+
+ /* Gather information needed for logging if a BW change is required */
+ wqe = &cmdiocb->wqe;
+ asig = bf_get(cmf_sync_asig, &wqe->cmf_sync);
+ afpin = bf_get(cmf_sync_afpin, &wqe->cmf_sync);
+ fpincnt = bf_get(cmf_sync_wfpincnt, &wqe->cmf_sync);
+ sigcnt = bf_get(cmf_sync_wsigcnt, &wqe->cmf_sync);
+ if (phba->cmf_max_bytes_per_interval != bw ||
+ (asig || afpin || sigcnt || fpincnt)) {
+ /* Are we increasing or decreasing BW */
+ if (phba->cmf_max_bytes_per_interval < bw) {
+ bwdif = bw - phba->cmf_max_bytes_per_interval;
+ s = "Increase";
+ } else {
+ bwdif = phba->cmf_max_bytes_per_interval - bw;
+ s = "Decrease";
+ }
+
+ /* What is the change percentage */
+ slop = div_u64(phba->cmf_link_byte_count, 200); /*For rounding*/
+ pcent = div64_u64(bwdif * 100 + slop,
+ phba->cmf_link_byte_count);
+ bwpcent = div64_u64(bw * 100 + slop,
+ phba->cmf_link_byte_count);
+ /* Because of bytes adjustment due to shorter timer in
+ * lpfc_cmf_timer() the cmf_link_byte_count can be shorter and
+ * may seem like BW is above 100%.
+ */
+ if (bwpcent > 100)
+ bwpcent = 100;
+
+ if (phba->cmf_max_bytes_per_interval < bw &&
+ bwpcent > 95)
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6208 Congestion bandwidth "
+ "limits removed\n");
+ else if ((phba->cmf_max_bytes_per_interval > bw) &&
+ ((bwpcent + pcent) <= 100) && ((bwpcent + pcent) > 95))
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6209 Congestion bandwidth "
+ "limits in effect\n");
+
+ if (asig) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6237 BW Threshold %lld%% (%lld): "
+ "%lld%% %s: Signal Alarm: cg:%d "
+ "Info:%u\n",
+ bwpcent, bw, pcent, s, cg,
+ phba->cmf_active_info);
+ } else if (afpin) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6238 BW Threshold %lld%% (%lld): "
+ "%lld%% %s: FPIN Alarm: cg:%d "
+ "Info:%u\n",
+ bwpcent, bw, pcent, s, cg,
+ phba->cmf_active_info);
+ } else if (sigcnt) {
+ wsigmax = bf_get(cmf_sync_wsigmax, &wqe->cmf_sync);
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6239 BW Threshold %lld%% (%lld): "
+ "%lld%% %s: Signal Warning: "
+ "Cnt %d Max %d: cg:%d Info:%u\n",
+ bwpcent, bw, pcent, s, sigcnt,
+ wsigmax, cg, phba->cmf_active_info);
+ } else if (fpincnt) {
+ wfpinmax = bf_get(cmf_sync_wfpinmax, &wqe->cmf_sync);
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6240 BW Threshold %lld%% (%lld): "
+ "%lld%% %s: FPIN Warning: "
+ "Cnt %d Max %d: cg:%d Info:%u\n",
+ bwpcent, bw, pcent, s, fpincnt,
+ wfpinmax, cg, phba->cmf_active_info);
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6241 BW Threshold %lld%% (%lld): "
+ "CMF %lld%% %s: cg:%d Info:%u\n",
+ bwpcent, bw, pcent, s, cg,
+ phba->cmf_active_info);
+ }
+ } else if (info) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6246 Info Threshold %u\n", info);
+ }
+
+ /* Save BW change to be picked up during next timer interrupt */
+ phba->cmf_last_sync_bw = bw;
+out:
+ lpfc_sli_release_iocbq(phba, cmdiocb);
+}
+
+/**
+ * lpfc_issue_cmf_sync_wqe - Issue a CMF_SYNC_WQE
+ * @phba: Pointer to HBA context object.
+ * @ms: ms to set in WQE interval, 0 means use init op
+ * @total: Total rcv bytes for this interval
+ *
+ * This routine is called every CMF timer interrupt. Its purpose is
+ * to issue a CMF_SYNC_WQE to the firmware to inform it of any events
+ * that may indicate we have congestion (FPINs or Signals). Upon
+ * completion, the firmware will indicate any BW restrictions the
+ * driver may need to take.
+ **/
+int
+lpfc_issue_cmf_sync_wqe(struct lpfc_hba *phba, u32 ms, u64 total)
+{
+ union lpfc_wqe128 *wqe;
+ struct lpfc_iocbq *sync_buf;
+ unsigned long iflags;
+ u32 ret_val, cgn_sig_freq;
+ u32 atot, wtot, max;
+ u8 warn_sync_period = 0;
+
+ /* First address any alarm / warning activity */
+ atot = atomic_xchg(&phba->cgn_sync_alarm_cnt, 0);
+ wtot = atomic_xchg(&phba->cgn_sync_warn_cnt, 0);
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+
+ /* ONLY Managed mode will send the CMF_SYNC_WQE to the HBA */
+ if (phba->cmf_active_mode != LPFC_CFG_MANAGED ||
+ phba->link_state < LPFC_LINK_UP) {
+ ret_val = 0;
+ goto out_unlock;
+ }
+
+ sync_buf = __lpfc_sli_get_iocbq(phba);
+ if (!sync_buf) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT,
+ "6244 No available WQEs for CMF_SYNC_WQE\n");
+ ret_val = ENOMEM;
+ goto out_unlock;
+ }
+
+ wqe = &sync_buf->wqe;
+
+ /* WQEs are reused. Clear stale data and set key fields to zero */
+ memset(wqe, 0, sizeof(*wqe));
+
+ /* If this is the very first CMF_SYNC_WQE, issue an init operation */
+ if (!ms) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6441 CMF Init %d - CMF_SYNC_WQE\n",
+ phba->fc_eventTag);
+ bf_set(cmf_sync_op, &wqe->cmf_sync, 1); /* 1=init */
+ bf_set(cmf_sync_interval, &wqe->cmf_sync, LPFC_CMF_INTERVAL);
+ goto initpath;
+ }
+
+ bf_set(cmf_sync_op, &wqe->cmf_sync, 0); /* 0=recalc */
+ bf_set(cmf_sync_interval, &wqe->cmf_sync, ms);
+
+ /* Check for alarms / warnings */
+ if (atot) {
+ if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) {
+ /* We hit an Signal alarm condition */
+ bf_set(cmf_sync_asig, &wqe->cmf_sync, 1);
+ } else {
+ /* We hit a FPIN alarm condition */
+ bf_set(cmf_sync_afpin, &wqe->cmf_sync, 1);
+ }
+ } else if (wtot) {
+ if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ONLY ||
+ phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) {
+ cgn_sig_freq = phba->cgn_sig_freq ? phba->cgn_sig_freq :
+ lpfc_fabric_cgn_frequency;
+ /* We hit an Signal warning condition */
+ max = LPFC_SEC_TO_MSEC / cgn_sig_freq *
+ lpfc_acqe_cgn_frequency;
+ bf_set(cmf_sync_wsigmax, &wqe->cmf_sync, max);
+ bf_set(cmf_sync_wsigcnt, &wqe->cmf_sync, wtot);
+ warn_sync_period = lpfc_acqe_cgn_frequency;
+ } else {
+ /* We hit a FPIN warning condition */
+ bf_set(cmf_sync_wfpinmax, &wqe->cmf_sync, 1);
+ bf_set(cmf_sync_wfpincnt, &wqe->cmf_sync, 1);
+ if (phba->cgn_fpin_frequency != LPFC_FPIN_INIT_FREQ)
+ warn_sync_period =
+ LPFC_MSECS_TO_SECS(phba->cgn_fpin_frequency);
+ }
+ }
+
+ /* Update total read blocks during previous timer interval */
+ wqe->cmf_sync.read_bytes = (u32)(total / LPFC_CMF_BLK_SIZE);
+
+initpath:
+ bf_set(cmf_sync_ver, &wqe->cmf_sync, LPFC_CMF_SYNC_VER);
+ wqe->cmf_sync.event_tag = phba->fc_eventTag;
+ bf_set(cmf_sync_cmnd, &wqe->cmf_sync, CMD_CMF_SYNC_WQE);
+
+ /* Setup reqtag to match the wqe completion. */
+ bf_set(cmf_sync_reqtag, &wqe->cmf_sync, sync_buf->iotag);
+
+ bf_set(cmf_sync_qosd, &wqe->cmf_sync, 1);
+ bf_set(cmf_sync_period, &wqe->cmf_sync, warn_sync_period);
+
+ bf_set(cmf_sync_cmd_type, &wqe->cmf_sync, CMF_SYNC_COMMAND);
+ bf_set(cmf_sync_wqec, &wqe->cmf_sync, 1);
+ bf_set(cmf_sync_cqid, &wqe->cmf_sync, LPFC_WQE_CQ_ID_DEFAULT);
+
+ sync_buf->vport = phba->pport;
+ sync_buf->cmd_cmpl = lpfc_cmf_sync_cmpl;
+ sync_buf->cmd_dmabuf = NULL;
+ sync_buf->rsp_dmabuf = NULL;
+ sync_buf->bpl_dmabuf = NULL;
+ sync_buf->sli4_xritag = NO_XRI;
+
+ sync_buf->cmd_flag |= LPFC_IO_CMF;
+ ret_val = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[0], sync_buf);
+ if (ret_val) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "6214 Cannot issue CMF_SYNC_WQE: x%x\n",
+ ret_val);
+ __lpfc_sli_release_iocbq(phba, sync_buf);
+ }
+out_unlock:
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return ret_val;
+}
+
+/**
* lpfc_sli_next_iocb_slot - Get next iocb slot in the ring
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
@@ -1289,21 +2061,26 @@ static IOCB_t *
lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
struct lpfc_pgp *pgp = &phba->port_gp[pring->ringno];
- uint32_t max_cmd_idx = pring->numCiocb;
- if ((pring->next_cmdidx == pring->cmdidx) &&
- (++pring->next_cmdidx >= max_cmd_idx))
- pring->next_cmdidx = 0;
+ uint32_t max_cmd_idx = pring->sli.sli3.numCiocb;
- if (unlikely(pring->local_getidx == pring->next_cmdidx)) {
+ lockdep_assert_held(&phba->hbalock);
- pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+ if ((pring->sli.sli3.next_cmdidx == pring->sli.sli3.cmdidx) &&
+ (++pring->sli.sli3.next_cmdidx >= max_cmd_idx))
+ pring->sli.sli3.next_cmdidx = 0;
- if (unlikely(pring->local_getidx >= max_cmd_idx)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ if (unlikely(pring->sli.sli3.local_getidx ==
+ pring->sli.sli3.next_cmdidx)) {
+
+ pring->sli.sli3.local_getidx = le32_to_cpu(pgp->cmdGetInx);
+
+ if (unlikely(pring->sli.sli3.local_getidx >= max_cmd_idx)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0315 Ring %d issue: portCmdGet %d "
"is bigger than cmd ring %d\n",
pring->ringno,
- pring->local_getidx, max_cmd_idx);
+ pring->sli.sli3.local_getidx,
+ max_cmd_idx);
phba->link_state = LPFC_HBA_ERROR;
/*
@@ -1318,7 +2095,7 @@ lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
return NULL;
}
- if (pring->local_getidx == pring->next_cmdidx)
+ if (pring->sli.sli3.local_getidx == pring->sli.sli3.next_cmdidx)
return NULL;
}
@@ -1358,7 +2135,7 @@ lpfc_sli_next_iotag(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
- LPFC_IOCBQ_LOOKUP_INCREMENT)) {
new_len = psli->iocbq_lookup_len + LPFC_IOCBQ_LOOKUP_INCREMENT;
spin_unlock_irq(&phba->hbalock);
- new_arr = kzalloc(new_len * sizeof (struct lpfc_iocbq *),
+ new_arr = kcalloc(new_len, sizeof(struct lpfc_iocbq *),
GFP_KERNEL);
if (new_arr) {
spin_lock_irq(&phba->hbalock);
@@ -1408,11 +2185,12 @@ lpfc_sli_next_iotag(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
* @nextiocb: Pointer to driver iocb object which need to be
* posted to firmware.
*
- * This function is called with hbalock held to post a new iocb to
- * the firmware. This function copies the new iocb to ring iocb slot and
- * updates the ring pointers. It adds the new iocb to txcmplq if there is
+ * This function is called to post a new iocb to the firmware. This
+ * function copies the new iocb to ring iocb slot and updates the
+ * ring pointers. It adds the new iocb to txcmplq if there is
* a completion call back for this iocb else the function will free the
- * iocb object.
+ * iocb object. The hbalock is asserted held in the code path calling
+ * this routine.
**/
static void
lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
@@ -1421,7 +2199,7 @@ lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/*
* Set up an iotag
*/
- nextiocb->iocb.ulpIoTag = (nextiocb->iocb_cmpl) ? nextiocb->iotag : 0;
+ nextiocb->iocb.ulpIoTag = (nextiocb->cmd_cmpl) ? nextiocb->iotag : 0;
if (pring->ringno == LPFC_ELS_RING) {
@@ -1442,9 +2220,9 @@ lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/*
* If there is no completion routine to call, we can release the
* IOCB buffer back right now. For IOCBs, like QUE_RING_BUF,
- * that have no rsp ring completion, iocb_cmpl MUST be NULL.
+ * that have no rsp ring completion, cmd_cmpl MUST be NULL.
*/
- if (nextiocb->iocb_cmpl)
+ if (nextiocb->cmd_cmpl)
lpfc_sli_ringtxcmpl_put(phba, pring, nextiocb);
else
__lpfc_sli_release_iocbq(phba, nextiocb);
@@ -1453,8 +2231,8 @@ lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
* Let the HBA know what IOCB slot will be the next one the
* driver will put a command into.
*/
- pring->cmdidx = pring->next_cmdidx;
- writel(pring->cmdidx, &phba->host_gp[pring->ringno].cmdPutInx);
+ pring->sli.sli3.cmdidx = pring->sli.sli3.next_cmdidx;
+ writel(pring->sli.sli3.cmdidx, &phba->host_gp[pring->ringno].cmdPutInx);
}
/**
@@ -1527,6 +2305,8 @@ lpfc_sli_resume_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
IOCB_t *iocb;
struct lpfc_iocbq *nextiocb;
+ lockdep_assert_held(&phba->hbalock);
+
/*
* Check to see if:
* (a) there is anything on the txq to send
@@ -1534,9 +2314,10 @@ lpfc_sli_resume_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
* (c) link attention events can be processed (fcp ring only)
* (d) IOCB processing is not blocked by the outstanding mbox command.
*/
- if (pring->txq_cnt &&
- lpfc_is_link_up(phba) &&
- (pring->ringno != phba->sli.fcp_ring ||
+
+ if (lpfc_is_link_up(phba) &&
+ (!list_empty(&pring->txq)) &&
+ (pring->ringno != LPFC_FCP_RING ||
phba->sli.sli_flag & LPFC_PROCESS_LA)) {
while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) &&
@@ -1567,6 +2348,8 @@ lpfc_sli_next_hbq_slot(struct lpfc_hba *phba, uint32_t hbqno)
{
struct hbq_s *hbqp = &phba->hbqs[hbqno];
+ lockdep_assert_held(&phba->hbalock);
+
if (hbqp->next_hbqPutIdx == hbqp->hbqPutIdx &&
++hbqp->next_hbqPutIdx >= hbqp->entry_count)
hbqp->next_hbqPutIdx = 0;
@@ -1578,8 +2361,7 @@ lpfc_sli_next_hbq_slot(struct lpfc_hba *phba, uint32_t hbqno)
hbqp->local_hbqGetIdx = getidx;
if (unlikely(hbqp->local_hbqGetIdx >= hbqp->entry_count)) {
- lpfc_printf_log(phba, KERN_ERR,
- LOG_SLI | LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1802 HBQ %d: local_hbqGetIdx "
"%u is > than hbqp->entry_count %u\n",
hbqno, hbqp->local_hbqGetIdx,
@@ -1613,7 +2395,6 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
struct hbq_dmabuf *hbq_buf;
unsigned long flags;
int i, hbq_count;
- uint32_t hbqno;
hbq_count = lpfc_sli_hbq_count();
/* Return all memory used by all HBQs */
@@ -1627,24 +2408,6 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
}
phba->hbqs[i].buffer_count = 0;
}
- /* Return all HBQ buffer that are in-fly */
- list_for_each_entry_safe(dmabuf, next_dmabuf, &phba->rb_pend_list,
- list) {
- hbq_buf = container_of(dmabuf, struct hbq_dmabuf, dbuf);
- list_del(&hbq_buf->dbuf.list);
- if (hbq_buf->tag == -1) {
- (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer)
- (phba, hbq_buf);
- } else {
- hbqno = hbq_buf->tag >> 16;
- if (hbqno >= LPFC_MAX_HBQS)
- (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer)
- (phba, hbq_buf);
- else
- (phba->hbqs[hbqno].hbq_free_buffer)(phba,
- hbq_buf);
- }
- }
/* Mark the HBQs not in use */
phba->hbq_in_use = 0;
@@ -1667,6 +2430,7 @@ static int
lpfc_sli_hbq_to_firmware(struct lpfc_hba *phba, uint32_t hbqno,
struct hbq_dmabuf *hbq_buf)
{
+ lockdep_assert_held(&phba->hbalock);
return phba->lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buf);
}
@@ -1688,6 +2452,7 @@ lpfc_sli_hbq_to_firmware_s3(struct lpfc_hba *phba, uint32_t hbqno,
struct lpfc_hbq_entry *hbqe;
dma_addr_t physaddr = hbq_buf->dbuf.phys;
+ lockdep_assert_held(&phba->hbalock);
/* Get next HBQ entry slot to use */
hbqe = lpfc_sli_next_hbq_slot(phba, hbqno);
if (hbqe) {
@@ -1695,7 +2460,7 @@ lpfc_sli_hbq_to_firmware_s3(struct lpfc_hba *phba, uint32_t hbqno,
hbqe->bde.addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
hbqe->bde.addrLow = le32_to_cpu(putPaddrLow(physaddr));
- hbqe->bde.tus.f.bdeSize = hbq_buf->size;
+ hbqe->bde.tus.f.bdeSize = hbq_buf->total_size;
hbqe->bde.tus.f.bdeFlags = 0;
hbqe->bde.tus.w = le32_to_cpu(hbqe->bde.tus.w);
hbqe->buffer_tag = le32_to_cpu(hbq_buf->tag);
@@ -1727,16 +2492,23 @@ lpfc_sli_hbq_to_firmware_s4(struct lpfc_hba *phba, uint32_t hbqno,
int rc;
struct lpfc_rqe hrqe;
struct lpfc_rqe drqe;
+ struct lpfc_queue *hrq;
+ struct lpfc_queue *drq;
+ if (hbqno != LPFC_ELS_HBQ)
+ return 1;
+ hrq = phba->sli4_hba.hdr_rq;
+ drq = phba->sli4_hba.dat_rq;
+
+ lockdep_assert_held(&phba->hbalock);
hrqe.address_lo = putPaddrLow(hbq_buf->hbuf.phys);
hrqe.address_hi = putPaddrHigh(hbq_buf->hbuf.phys);
drqe.address_lo = putPaddrLow(hbq_buf->dbuf.phys);
drqe.address_hi = putPaddrHigh(hbq_buf->dbuf.phys);
- rc = lpfc_sli4_rq_put(phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
- &hrqe, &drqe);
+ rc = lpfc_sli4_rq_put(hrq, drq, &hrqe, &drqe);
if (rc < 0)
return rc;
- hbq_buf->tag = rc;
+ hbq_buf->tag = (rc | (hbqno << 16));
list_add_tail(&hbq_buf->dbuf.list, &phba->hbqs[hbqno].hbq_buffer_list);
return 0;
}
@@ -1753,22 +2525,9 @@ static struct lpfc_hbq_init lpfc_els_hbq = {
.add_count = 40,
};
-/* HBQ for the extra ring if needed */
-static struct lpfc_hbq_init lpfc_extra_hbq = {
- .rn = 1,
- .entry_count = 200,
- .mask_count = 0,
- .profile = 0,
- .ring_mask = (1 << LPFC_EXTRA_RING),
- .buffer_count = 0,
- .init_count = 0,
- .add_count = 5,
-};
-
/* Array of HBQs */
struct lpfc_hbq_init *lpfc_hbq_defs[] = {
&lpfc_els_hbq,
- &lpfc_extra_hbq,
};
/**
@@ -1864,16 +2623,14 @@ lpfc_sli_hbqbuf_init_hbqs(struct lpfc_hba *phba, uint32_t qno)
{
if (phba->sli_rev == LPFC_SLI_REV4)
return lpfc_sli_hbqbuf_fill_hbqs(phba, qno,
- lpfc_hbq_defs[qno]->entry_count);
+ lpfc_hbq_defs[qno]->entry_count);
else
return lpfc_sli_hbqbuf_fill_hbqs(phba, qno,
lpfc_hbq_defs[qno]->init_count);
}
-/**
+/*
* lpfc_sli_hbqbuf_get - Remove the first hbq off of an hbq list
- * @phba: Pointer to HBA context object.
- * @hbqno: HBQ number.
*
* This function removes the first hbq buffer on an hbq list and returns a
* pointer to that buffer. If it finds no buffers on the list it returns NULL.
@@ -1890,14 +2647,36 @@ lpfc_sli_hbqbuf_get(struct list_head *rb_list)
}
/**
+ * lpfc_sli_rqbuf_get - Remove the first dma buffer off of an RQ list
+ * @phba: Pointer to HBA context object.
+ * @hrq: HBQ number.
+ *
+ * This function removes the first RQ buffer on an RQ buffer list and returns a
+ * pointer to that buffer. If it finds no buffers on the list it returns NULL.
+ **/
+static struct rqb_dmabuf *
+lpfc_sli_rqbuf_get(struct lpfc_hba *phba, struct lpfc_queue *hrq)
+{
+ struct lpfc_dmabuf *h_buf;
+ struct lpfc_rqb *rqbp;
+
+ rqbp = hrq->rqbp;
+ list_remove_head(&rqbp->rqb_buffer_list, h_buf,
+ struct lpfc_dmabuf, list);
+ if (!h_buf)
+ return NULL;
+ rqbp->buffer_count--;
+ return container_of(h_buf, struct rqb_dmabuf, hbuf);
+}
+
+/**
* lpfc_sli_hbqbuf_find - Find the hbq buffer associated with a tag
* @phba: Pointer to HBA context object.
* @tag: Tag of the hbq buffer.
*
- * This function is called with hbalock held. This function searches
- * for the hbq buffer associated with the given tag in the hbq buffer
- * list. If it finds the hbq buffer, it returns the hbq_buffer other wise
- * it returns NULL.
+ * This function searches for the hbq buffer associated with the given tag in
+ * the hbq buffer list. If it finds the hbq buffer, it returns the hbq_buffer
+ * otherwise it returns NULL.
**/
static struct hbq_dmabuf *
lpfc_sli_hbqbuf_find(struct lpfc_hba *phba, uint32_t tag)
@@ -1919,7 +2698,7 @@ lpfc_sli_hbqbuf_find(struct lpfc_hba *phba, uint32_t tag)
}
}
spin_unlock_irq(&phba->hbalock);
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI | LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1803 Bad hbq tag. Data: x%x x%x\n",
tag, phba->hbqs[tag >> 16].buffer_count);
return NULL;
@@ -2025,6 +2804,7 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
case MBX_READ_EVENT_LOG:
case MBX_SECURITY_MGMT:
case MBX_AUTH_PORT:
+ case MBX_ACCESS_VDATA:
ret = mbxCommand;
break;
default:
@@ -2048,23 +2828,22 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
void
lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
- wait_queue_head_t *pdone_q;
unsigned long drvr_flag;
+ struct completion *pmbox_done;
/*
- * If pdone_q is empty, the driver thread gave up waiting and
+ * If pmbox_done is empty, the driver thread gave up waiting and
* continued running.
*/
pmboxq->mbox_flag |= LPFC_MBX_WAKE;
spin_lock_irqsave(&phba->hbalock, drvr_flag);
- pdone_q = (wait_queue_head_t *) pmboxq->context1;
- if (pdone_q)
- wake_up_interruptible(pdone_q);
+ pmbox_done = pmboxq->ctx_u.mbox_wait;
+ if (pmbox_done)
+ complete(pmbox_done);
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
return;
}
-
/**
* lpfc_sli_def_mbox_cmpl - Default mailbox completion handler
* @phba: Pointer to HBA context object.
@@ -2085,23 +2864,25 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
uint16_t rpi, vpi;
int rc;
- mp = (struct lpfc_dmabuf *) (pmb->context1);
-
- if (mp) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
-
/*
* If a REG_LOGIN succeeded after node is destroyed or node
* is in re-discovery driver need to cleanup the RPI.
*/
- if (!(phba->pport->load_flag & FC_UNLOADING) &&
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag) &&
pmb->u.mb.mbxCommand == MBX_REG_LOGIN64 &&
!pmb->u.mb.mbxStatus) {
+ mp = pmb->ctx_buf;
+ if (mp) {
+ pmb->ctx_buf = NULL;
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
rpi = pmb->u.mb.un.varWords[0];
vpi = pmb->u.mb.un.varRegLogin.vpi;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ vpi -= phba->sli4_hba.max_cfg_param.vpi_base;
lpfc_unreg_login(phba, vpi, rpi, pmb);
+ pmb->vport = vport;
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if (rc != MBX_NOT_FINISHED)
@@ -2109,32 +2890,141 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
}
if ((pmb->u.mb.mbxCommand == MBX_REG_VPI) &&
- !(phba->pport->load_flag & FC_UNLOADING) &&
+ !test_bit(FC_UNLOADING, &phba->pport->load_flag) &&
!pmb->u.mb.mbxStatus) {
shost = lpfc_shost_from_vport(vport);
spin_lock_irq(shost->host_lock);
vport->vpi_state |= LPFC_VPI_REGISTERED;
- vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
spin_unlock_irq(shost->host_lock);
+ clear_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
}
if (pmb->u.mb.mbxCommand == MBX_REG_LOGIN64) {
- ndlp = (struct lpfc_nodelist *)pmb->context2;
+ ndlp = pmb->ctx_ndlp;
+ lpfc_nlp_put(ndlp);
+ }
+
+ if (pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) {
+ ndlp = pmb->ctx_ndlp;
+
+ /* Check to see if there are any deferred events to process */
+ if (ndlp) {
+ lpfc_printf_vlog(
+ vport,
+ KERN_INFO, LOG_MBOX | LOG_DISCOVERY,
+ "1438 UNREG cmpl deferred mbox x%x "
+ "on NPort x%x Data: x%lx x%x x%px x%lx x%x\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag, ndlp->nlp_defer_did,
+ ndlp, vport->load_flag, kref_read(&ndlp->kref));
+
+ if (test_bit(NLP_UNREG_INP, &ndlp->nlp_flag) &&
+ ndlp->nlp_defer_did != NLP_EVT_NOTHING_PENDING) {
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+ ndlp->nlp_defer_did = NLP_EVT_NOTHING_PENDING;
+ lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
+ } else {
+ clear_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
+ }
+
+ /* The unreg_login mailbox is complete and had a
+ * reference that has to be released. The PLOGI
+ * got its own ref.
+ */
+ lpfc_nlp_put(ndlp);
+ pmb->ctx_ndlp = NULL;
+ }
+ }
+
+ /* This nlp_put pairs with lpfc_sli4_resume_rpi */
+ if (pmb->u.mb.mbxCommand == MBX_RESUME_RPI) {
+ ndlp = pmb->ctx_ndlp;
lpfc_nlp_put(ndlp);
- pmb->context2 = NULL;
}
/* Check security permission status on INIT_LINK mailbox command */
if ((pmb->u.mb.mbxCommand == MBX_INIT_LINK) &&
(pmb->u.mb.mbxStatus == MBXERR_SEC_NO_PERMISSION))
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2860 SLI authentication is required "
"for INIT_LINK but has not done yet\n");
if (bf_get(lpfc_mqe_command, &pmb->u.mqe) == MBX_SLI4_CONFIG)
lpfc_sli4_mbox_cmd_free(phba, pmb);
else
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
+}
+ /**
+ * lpfc_sli4_unreg_rpi_cmpl_clr - mailbox completion handler
+ * @phba: Pointer to HBA context object.
+ * @pmb: Pointer to mailbox object.
+ *
+ * This function is the unreg rpi mailbox completion handler. It
+ * frees the memory resources associated with the completed mailbox
+ * command. An additional reference is put on the ndlp to prevent
+ * lpfc_nlp_release from freeing the rpi bit in the bitmask before
+ * the unreg mailbox command completes, this routine puts the
+ * reference back.
+ *
+ **/
+void
+lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ struct lpfc_vport *vport = pmb->vport;
+ struct lpfc_nodelist *ndlp;
+ bool unreg_inp;
+
+ ndlp = pmb->ctx_ndlp;
+ if (pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) {
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ (bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf) >=
+ LPFC_SLI_INTF_IF_TYPE_2)) {
+ if (ndlp) {
+ lpfc_printf_vlog(
+ vport, KERN_INFO,
+ LOG_MBOX | LOG_SLI | LOG_NODE,
+ "0010 UNREG_LOGIN vpi:x%x "
+ "rpi:%x DID:%x defer x%x flg x%lx "
+ "x%px\n",
+ vport->vpi, ndlp->nlp_rpi,
+ ndlp->nlp_DID, ndlp->nlp_defer_did,
+ ndlp->nlp_flag,
+ ndlp);
+
+ /* Cleanup the nlp_flag now that the UNREG RPI
+ * has completed.
+ */
+ unreg_inp = test_and_clear_bit(NLP_UNREG_INP,
+ &ndlp->nlp_flag);
+ clear_bit(NLP_LOGO_ACC, &ndlp->nlp_flag);
+
+ /* Check to see if there are any deferred
+ * events to process
+ */
+ if (unreg_inp &&
+ ndlp->nlp_defer_did !=
+ NLP_EVT_NOTHING_PENDING) {
+ lpfc_printf_vlog(
+ vport, KERN_INFO,
+ LOG_MBOX | LOG_SLI | LOG_NODE,
+ "4111 UNREG cmpl deferred "
+ "clr x%x on "
+ "NPort x%x Data: x%x x%px\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_defer_did, ndlp);
+ ndlp->nlp_defer_did =
+ NLP_EVT_NOTHING_PENDING;
+ lpfc_issue_els_plogi(
+ vport, ndlp->nlp_DID, 0);
+ }
+
+ lpfc_nlp_put(ndlp);
+ }
+ }
+ }
+
+ mempool_free(pmb, phba->mbox_mem_pool);
}
/**
@@ -2198,12 +3088,16 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) ==
MBX_SHUTDOWN) {
/* Unknown mailbox command compl */
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"(%d):0323 Unknown Mailbox command "
- "x%x (x%x) Cmpl\n",
- pmb->vport ? pmb->vport->vpi : 0,
+ "x%x (x%x/x%x) Cmpl\n",
+ pmb->vport ? pmb->vport->vpi :
+ LPFC_VPORT_UNKNOWN,
pmbox->mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, pmb));
+ lpfc_sli_config_mbox_subsys_get(phba,
+ pmb),
+ lpfc_sli_config_mbox_opcode_get(phba,
+ pmb));
phba->link_state = LPFC_HBA_ERROR;
phba->work_hs = HS_FFER3;
lpfc_handle_eratt(phba);
@@ -2215,17 +3109,21 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
if (pmbox->mbxStatus == MBXERR_NO_RESOURCES) {
/* Mbox cmd cmpl error - RETRYing */
lpfc_printf_log(phba, KERN_INFO,
- LOG_MBOX | LOG_SLI,
- "(%d):0305 Mbox cmd cmpl "
- "error - RETRYing Data: x%x "
- "(x%x) x%x x%x x%x\n",
- pmb->vport ? pmb->vport->vpi :0,
- pmbox->mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba,
- pmb),
- pmbox->mbxStatus,
- pmbox->un.varWords[0],
- pmb->vport->port_state);
+ LOG_MBOX | LOG_SLI,
+ "(%d):0305 Mbox cmd cmpl "
+ "error - RETRYing Data: x%x "
+ "(x%x/x%x) x%x x%x x%x\n",
+ pmb->vport ? pmb->vport->vpi :
+ LPFC_VPORT_UNKNOWN,
+ pmbox->mbxCommand,
+ lpfc_sli_config_mbox_subsys_get(phba,
+ pmb),
+ lpfc_sli_config_mbox_opcode_get(phba,
+ pmb),
+ pmbox->mbxStatus,
+ pmbox->un.varWords[0],
+ pmb->vport ? pmb->vport->port_state :
+ LPFC_VPORT_UNKNOWN);
pmbox->mbxStatus = 0;
pmbox->mbxOwner = OWN_HOST;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
@@ -2236,11 +3134,13 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
/* Mailbox cmd <cmd> Cmpl <cmpl> */
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
- "(%d):0307 Mailbox cmd x%x (x%x) Cmpl x%p "
- "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x\n",
+ "(%d):0307 Mailbox cmd x%x (x%x/x%x) Cmpl %ps "
+ "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+ "x%x x%x x%x\n",
pmb->vport ? pmb->vport->vpi : 0,
pmbox->mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, pmb),
+ lpfc_sli_config_mbox_subsys_get(phba, pmb),
+ lpfc_sli_config_mbox_opcode_get(phba, pmb),
pmb->mbox_cmpl,
*((uint32_t *) pmbox),
pmbox->un.varWords[0],
@@ -2250,7 +3150,10 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
pmbox->un.varWords[4],
pmbox->un.varWords[5],
pmbox->un.varWords[6],
- pmbox->un.varWords[7]);
+ pmbox->un.varWords[7],
+ pmbox->un.varWords[8],
+ pmbox->un.varWords[9],
+ pmbox->un.varWords[10]);
if (pmb->mbox_cmpl)
pmb->mbox_cmpl(phba,pmb);
@@ -2286,6 +3189,144 @@ lpfc_sli_get_buff(struct lpfc_hba *phba,
}
/**
+ * lpfc_nvme_unsol_ls_handler - Process an unsolicited event data buffer
+ * containing a NVME LS request.
+ * @phba: pointer to lpfc hba data structure.
+ * @piocb: pointer to the iocbq struct representing the sequence starting
+ * frame.
+ *
+ * This routine initially validates the NVME LS, validates there is a login
+ * with the port that sent the LS, and then calls the appropriate nvme host
+ * or target LS request handler.
+ **/
+static void
+lpfc_nvme_unsol_ls_handler(struct lpfc_hba *phba, struct lpfc_iocbq *piocb)
+{
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_dmabuf *d_buf;
+ struct hbq_dmabuf *nvmebuf;
+ struct fc_frame_header *fc_hdr;
+ struct lpfc_async_xchg_ctx *axchg = NULL;
+ char *failwhy = NULL;
+ uint32_t oxid, sid, did, fctl, size;
+ int ret = 1;
+
+ d_buf = piocb->cmd_dmabuf;
+
+ nvmebuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+ fc_hdr = nvmebuf->hbuf.virt;
+ oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
+ did = sli4_did_from_fc_hdr(fc_hdr);
+ fctl = (fc_hdr->fh_f_ctl[0] << 16 |
+ fc_hdr->fh_f_ctl[1] << 8 |
+ fc_hdr->fh_f_ctl[2]);
+ size = bf_get(lpfc_rcqe_length, &nvmebuf->cq_event.cqe.rcqe_cmpl);
+
+ lpfc_nvmeio_data(phba, "NVME LS RCV: xri x%x sz %d from %06x\n",
+ oxid, size, sid);
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
+ failwhy = "Driver Unloading";
+ } else if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
+ failwhy = "NVME FC4 Disabled";
+ } else if (!phba->nvmet_support && !phba->pport->localport) {
+ failwhy = "No Localport";
+ } else if (phba->nvmet_support && !phba->targetport) {
+ failwhy = "No Targetport";
+ } else if (unlikely(fc_hdr->fh_r_ctl != FC_RCTL_ELS4_REQ)) {
+ failwhy = "Bad NVME LS R_CTL";
+ } else if (unlikely((fctl & 0x00FF0000) !=
+ (FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT))) {
+ failwhy = "Bad NVME LS F_CTL";
+ } else {
+ axchg = kzalloc(sizeof(*axchg), GFP_ATOMIC);
+ if (!axchg)
+ failwhy = "No CTX memory";
+ }
+
+ if (unlikely(failwhy)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6154 Drop NVME LS: SID %06X OXID x%X: %s\n",
+ sid, oxid, failwhy);
+ goto out_fail;
+ }
+
+ /* validate the source of the LS is logged in */
+ ndlp = lpfc_findnode_did(phba->pport, sid);
+ if (!ndlp ||
+ ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) &&
+ (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC,
+ "6216 NVME Unsol rcv: No ndlp: "
+ "NPort_ID x%x oxid x%x\n",
+ sid, oxid);
+ goto out_fail;
+ }
+
+ axchg->phba = phba;
+ axchg->ndlp = ndlp;
+ axchg->size = size;
+ axchg->oxid = oxid;
+ axchg->sid = sid;
+ axchg->wqeq = NULL;
+ axchg->state = LPFC_NVME_STE_LS_RCV;
+ axchg->entry_cnt = 1;
+ axchg->rqb_buffer = (void *)nvmebuf;
+ axchg->hdwq = &phba->sli4_hba.hdwq[0];
+ axchg->payload = nvmebuf->dbuf.virt;
+ INIT_LIST_HEAD(&axchg->list);
+
+ if (phba->nvmet_support) {
+ ret = lpfc_nvmet_handle_lsreq(phba, axchg);
+ spin_lock_irq(&ndlp->lock);
+ if (!ret && !(ndlp->fc4_xpt_flags & NLP_XPT_HAS_HH)) {
+ ndlp->fc4_xpt_flags |= NLP_XPT_HAS_HH;
+ spin_unlock_irq(&ndlp->lock);
+
+ /* This reference is a single occurrence to hold the
+ * node valid until the nvmet transport calls
+ * host_release.
+ */
+ if (!lpfc_nlp_get(ndlp))
+ goto out_fail;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_NODE,
+ "6206 NVMET unsol ls_req ndlp x%px "
+ "DID x%x xflags x%x refcnt %d\n",
+ ndlp, ndlp->nlp_DID,
+ ndlp->fc4_xpt_flags,
+ kref_read(&ndlp->kref));
+ } else {
+ spin_unlock_irq(&ndlp->lock);
+ }
+ } else {
+ ret = lpfc_nvme_handle_lsreq(phba, axchg);
+ }
+
+ /* if zero, LS was successfully handled. If non-zero, LS not handled */
+ if (!ret)
+ return;
+
+out_fail:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6155 Drop NVME LS from DID %06X: SID %06X OXID x%X "
+ "NVMe%s handler failed %d\n",
+ did, sid, oxid,
+ (phba->nvmet_support) ? "T" : "I", ret);
+
+ /* recycle receive buffer */
+ lpfc_in_buf_free(phba, &nvmebuf->dbuf);
+
+ /* If start of new exchange, abort it */
+ if (axchg && (fctl & FC_FC_FIRST_SEQ && !(fctl & FC_FC_EX_CTX)))
+ ret = lpfc_nvme_unsol_ls_issue_abort(phba, axchg, sid, oxid);
+
+ if (ret)
+ kfree(axchg);
+}
+
+/**
* lpfc_complete_unsol_iocb - Complete an unsolicited sequence
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
@@ -2304,6 +3345,14 @@ lpfc_complete_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
{
int i;
+ switch (fch_type) {
+ case FC_TYPE_NVME:
+ lpfc_nvme_unsol_ls_handler(phba, saveq);
+ return 1;
+ default:
+ break;
+ }
+
/* unSolicited Responses */
if (pring->prt[0].profile) {
if (pring->prt[0].lpfc_sli_rcv_unsol_event)
@@ -2325,6 +3374,56 @@ lpfc_complete_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
return 0;
}
+static void
+lpfc_sli_prep_unsol_wqe(struct lpfc_hba *phba,
+ struct lpfc_iocbq *saveq)
+{
+ IOCB_t *irsp;
+ union lpfc_wqe128 *wqe;
+ u16 i = 0;
+
+ irsp = &saveq->iocb;
+ wqe = &saveq->wqe;
+
+ /* Fill wcqe with the IOCB status fields */
+ bf_set(lpfc_wcqe_c_status, &saveq->wcqe_cmpl, irsp->ulpStatus);
+ saveq->wcqe_cmpl.word3 = irsp->ulpBdeCount;
+ saveq->wcqe_cmpl.parameter = irsp->un.ulpWord[4];
+ saveq->wcqe_cmpl.total_data_placed = irsp->unsli3.rcvsli3.acc_len;
+
+ /* Source ID */
+ bf_set(els_rsp64_sid, &wqe->xmit_els_rsp, irsp->un.rcvels.parmRo);
+
+ /* rx-id of the response frame */
+ bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com, irsp->ulpContext);
+
+ /* ox-id of the frame */
+ bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
+ irsp->unsli3.rcvsli3.ox_id);
+
+ /* DID */
+ bf_set(wqe_els_did, &wqe->xmit_els_rsp.wqe_dest,
+ irsp->un.rcvels.remoteID);
+
+ /* unsol data len */
+ for (i = 0; i < irsp->ulpBdeCount; i++) {
+ struct lpfc_hbq_entry *hbqe = NULL;
+
+ if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
+ if (i == 0) {
+ hbqe = (struct lpfc_hbq_entry *)
+ &irsp->un.ulpWord[0];
+ saveq->wqe.gen_req.bde.tus.f.bdeSize =
+ hbqe->bde.tus.f.bdeSize;
+ } else if (i == 1) {
+ hbqe = (struct lpfc_hbq_entry *)
+ &irsp->unsli3.sli3Words[4];
+ saveq->unsol_rcv_len = hbqe->bde.tus.f.bdeSize;
+ }
+ }
+ }
+}
+
/**
* lpfc_sli_process_unsol_iocb - Unsolicited iocb handler
* @phba: Pointer to HBA context object.
@@ -2345,13 +3444,13 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
{
IOCB_t * irsp;
WORD5 * w5p;
+ dma_addr_t paddr;
uint32_t Rctl, Type;
- uint32_t match;
struct lpfc_iocbq *iocbq;
struct lpfc_dmabuf *dmzbuf;
- match = 0;
- irsp = &(saveq->iocb);
+ irsp = &saveq->iocb;
+ saveq->vport = phba->pport;
if (irsp->ulpCommand == CMD_ASYNC_STATUS) {
if (pring->lpfc_sli_rcv_async_status)
@@ -2369,22 +3468,22 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
if ((irsp->ulpCommand == CMD_IOCB_RET_XRI64_CX) &&
- (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)) {
+ (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)) {
if (irsp->ulpBdeCount > 0) {
dmzbuf = lpfc_sli_get_buff(phba, pring,
- irsp->un.ulpWord[3]);
+ irsp->un.ulpWord[3]);
lpfc_in_buf_free(phba, dmzbuf);
}
if (irsp->ulpBdeCount > 1) {
dmzbuf = lpfc_sli_get_buff(phba, pring,
- irsp->unsli3.sli3Words[3]);
+ irsp->unsli3.sli3Words[3]);
lpfc_in_buf_free(phba, dmzbuf);
}
if (irsp->ulpBdeCount > 2) {
dmzbuf = lpfc_sli_get_buff(phba, pring,
- irsp->unsli3.sli3Words[7]);
+ irsp->unsli3.sli3Words[7]);
lpfc_in_buf_free(phba, dmzbuf);
}
@@ -2393,9 +3492,9 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
if (irsp->ulpBdeCount != 0) {
- saveq->context2 = lpfc_sli_get_buff(phba, pring,
+ saveq->cmd_dmabuf = lpfc_sli_get_buff(phba, pring,
irsp->un.ulpWord[3]);
- if (!saveq->context2)
+ if (!saveq->cmd_dmabuf)
lpfc_printf_log(phba,
KERN_ERR,
LOG_SLI,
@@ -2405,9 +3504,9 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
irsp->un.ulpWord[3]);
}
if (irsp->ulpBdeCount == 2) {
- saveq->context3 = lpfc_sli_get_buff(phba, pring,
+ saveq->bpl_dmabuf = lpfc_sli_get_buff(phba, pring,
irsp->unsli3.sli3Words[7]);
- if (!saveq->context3)
+ if (!saveq->bpl_dmabuf)
lpfc_printf_log(phba,
KERN_ERR,
LOG_SLI,
@@ -2417,11 +3516,12 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
irsp->unsli3.sli3Words[7]);
}
list_for_each_entry(iocbq, &saveq->list, list) {
- irsp = &(iocbq->iocb);
+ irsp = &iocbq->iocb;
if (irsp->ulpBdeCount != 0) {
- iocbq->context2 = lpfc_sli_get_buff(phba, pring,
+ iocbq->cmd_dmabuf = lpfc_sli_get_buff(phba,
+ pring,
irsp->un.ulpWord[3]);
- if (!iocbq->context2)
+ if (!iocbq->cmd_dmabuf)
lpfc_printf_log(phba,
KERN_ERR,
LOG_SLI,
@@ -2431,9 +3531,10 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
irsp->un.ulpWord[3]);
}
if (irsp->ulpBdeCount == 2) {
- iocbq->context3 = lpfc_sli_get_buff(phba, pring,
+ iocbq->bpl_dmabuf = lpfc_sli_get_buff(phba,
+ pring,
irsp->unsli3.sli3Words[7]);
- if (!iocbq->context3)
+ if (!iocbq->bpl_dmabuf)
lpfc_printf_log(phba,
KERN_ERR,
LOG_SLI,
@@ -2444,7 +3545,20 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
irsp->unsli3.sli3Words[7]);
}
}
+ } else {
+ paddr = getPaddr(irsp->un.cont64[0].addrHigh,
+ irsp->un.cont64[0].addrLow);
+ saveq->cmd_dmabuf = lpfc_sli_ringpostbuf_get(phba, pring,
+ paddr);
+ if (irsp->ulpBdeCount == 2) {
+ paddr = getPaddr(irsp->un.cont64[1].addrHigh,
+ irsp->un.cont64[1].addrLow);
+ saveq->bpl_dmabuf = lpfc_sli_ringpostbuf_get(phba,
+ pring,
+ paddr);
+ }
}
+
if (irsp->ulpBdeCount != 0 &&
(irsp->ulpCommand == CMD_IOCB_RCV_CONT64_CX ||
irsp->ulpStatus == IOSTAT_INTERMED_RSP)) {
@@ -2452,7 +3566,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/* search continue save q for same XRI */
list_for_each_entry(iocbq, &pring->iocb_continue_saveq, clist) {
- if (iocbq->iocb.ulpContext == saveq->iocb.ulpContext) {
+ if (iocbq->iocb.unsli3.rcvsli3.ox_id ==
+ saveq->iocb.unsli3.rcvsli3.ox_id) {
list_add_tail(&saveq->list, &iocbq->list);
found = 1;
break;
@@ -2461,12 +3576,14 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (!found)
list_add_tail(&saveq->clist,
&pring->iocb_continue_saveq);
+
if (saveq->iocb.ulpStatus != IOSTAT_INTERMED_RSP) {
list_del_init(&iocbq->clist);
saveq = iocbq;
- irsp = &(saveq->iocb);
- } else
+ irsp = &saveq->iocb;
+ } else {
return 0;
+ }
}
if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) ||
(irsp->ulpCommand == CMD_RCV_ELS_REQ_CX) ||
@@ -2489,6 +3606,19 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
}
+ if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
+ (irsp->ulpCommand == CMD_IOCB_RCV_ELS64_CX ||
+ irsp->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
+ if (irsp->unsli3.rcvsli3.vpi == 0xffff)
+ saveq->vport = phba->pport;
+ else
+ saveq->vport = lpfc_find_vport_by_vpid(phba,
+ irsp->unsli3.rcvsli3.vpi);
+ }
+
+ /* Prepare WQE with Unsol frame */
+ lpfc_sli_prep_unsol_wqe(phba, saveq);
+
if (!lpfc_complete_unsol_iocb(phba, pring, saveq, Rctl, Type))
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"0313 Ring %d handler: unexpected Rctl x%x "
@@ -2506,7 +3636,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*
* This function looks up the iocb_lookup table to get the command iocb
* corresponding to the given response iocb using the iotag of the
- * response iocb. This function is called with the hbalock held.
+ * response iocb. The driver calls this function with the hbalock held
+ * for SLI3 ports or the ring lock held for SLI4 ports.
* This function returns the command iocb object if it finds the command
* iocb else returns NULL.
**/
@@ -2516,25 +3647,28 @@ lpfc_sli_iocbq_lookup(struct lpfc_hba *phba,
struct lpfc_iocbq *prspiocb)
{
struct lpfc_iocbq *cmd_iocb = NULL;
- uint16_t iotag;
+ u16 iotag;
- iotag = prspiocb->iocb.ulpIoTag;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ iotag = get_wqe_reqtag(prspiocb);
+ else
+ iotag = prspiocb->iocb.ulpIoTag;
if (iotag != 0 && iotag <= phba->sli.last_iotag) {
cmd_iocb = phba->sli.iocbq_lookup[iotag];
- list_del_init(&cmd_iocb->list);
- if (cmd_iocb->iocb_flag & LPFC_IO_ON_Q) {
+ if (cmd_iocb->cmd_flag & LPFC_IO_ON_TXCMPLQ) {
+ /* remove from txcmpl queue list */
+ list_del_init(&cmd_iocb->list);
+ cmd_iocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ;
pring->txcmplq_cnt--;
- cmd_iocb->iocb_flag &= ~LPFC_IO_ON_Q;
+ return cmd_iocb;
}
- return cmd_iocb;
}
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0317 iotag x%x is out off "
- "range: max iotag x%x wd0 x%x\n",
- iotag, phba->sli.last_iotag,
- *(((uint32_t *) &prspiocb->iocb) + 7));
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0317 iotag x%x is out of "
+ "range: max iotag x%x\n",
+ iotag, phba->sli.last_iotag);
return NULL;
}
@@ -2545,8 +3679,8 @@ lpfc_sli_iocbq_lookup(struct lpfc_hba *phba,
* @iotag: IOCB tag.
*
* This function looks up the iocb_lookup table to get the command iocb
- * corresponding to the given iotag. This function is called with the
- * hbalock held.
+ * corresponding to the given iotag. The driver calls this function with
+ * the ring lock held because this function is an SLI4 port only helper.
* This function returns the command iocb object if it finds the command
* iocb else returns NULL.
**/
@@ -2554,21 +3688,24 @@ static struct lpfc_iocbq *
lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint16_t iotag)
{
- struct lpfc_iocbq *cmd_iocb;
+ struct lpfc_iocbq *cmd_iocb = NULL;
if (iotag != 0 && iotag <= phba->sli.last_iotag) {
cmd_iocb = phba->sli.iocbq_lookup[iotag];
- list_del_init(&cmd_iocb->list);
- if (cmd_iocb->iocb_flag & LPFC_IO_ON_Q) {
- cmd_iocb->iocb_flag &= ~LPFC_IO_ON_Q;
+ if (cmd_iocb->cmd_flag & LPFC_IO_ON_TXCMPLQ) {
+ /* remove from txcmpl queue list */
+ list_del_init(&cmd_iocb->list);
+ cmd_iocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ;
pring->txcmplq_cnt--;
+ return cmd_iocb;
}
- return cmd_iocb;
}
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0372 iotag x%x is out off range: max iotag (x%x)\n",
- iotag, phba->sli.last_iotag);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0372 iotag x%x lookup error: max iotag (x%x) "
+ "cmd_flag x%x\n",
+ iotag, phba->sli.last_iotag,
+ cmd_iocb ? cmd_iocb->cmd_flag : 0xffff);
return NULL;
}
@@ -2594,24 +3731,38 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *saveq)
{
struct lpfc_iocbq *cmdiocbp;
- int rc = 1;
unsigned long iflag;
+ u32 ulp_command, ulp_status, ulp_word4, ulp_context, iotag;
- /* Based on the iotag field, get the cmd IOCB from the txcmplq */
- spin_lock_irqsave(&phba->hbalock, iflag);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_lock_irqsave(&pring->ring_lock, iflag);
+ else
+ spin_lock_irqsave(&phba->hbalock, iflag);
cmdiocbp = lpfc_sli_iocbq_lookup(phba, pring, saveq);
- spin_unlock_irqrestore(&phba->hbalock, iflag);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock_irqrestore(&pring->ring_lock, iflag);
+ else
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+
+ ulp_command = get_job_cmnd(phba, saveq);
+ ulp_status = get_job_ulpstatus(phba, saveq);
+ ulp_word4 = get_job_word4(phba, saveq);
+ ulp_context = get_job_ulpcontext(phba, saveq);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ iotag = get_wqe_reqtag(saveq);
+ else
+ iotag = saveq->iocb.ulpIoTag;
if (cmdiocbp) {
- if (cmdiocbp->iocb_cmpl) {
+ ulp_command = get_job_cmnd(phba, cmdiocbp);
+ if (cmdiocbp->cmd_cmpl) {
/*
* If an ELS command failed send an event to mgmt
* application.
*/
- if (saveq->iocb.ulpStatus &&
+ if (ulp_status &&
(pring->ringno == LPFC_ELS_RING) &&
- (cmdiocbp->iocb.ulpCommand ==
- CMD_ELS_REQUEST64_CR))
+ (ulp_command == CMD_ELS_REQUEST64_CR))
lpfc_send_els_failure_event(phba,
cmdiocbp, saveq);
@@ -2621,11 +3772,11 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*/
if (pring->ringno == LPFC_ELS_RING) {
if ((phba->sli_rev < LPFC_SLI_REV4) &&
- (cmdiocbp->iocb_flag &
+ (cmdiocbp->cmd_flag &
LPFC_DRIVER_ABORTED)) {
spin_lock_irqsave(&phba->hbalock,
iflag);
- cmdiocbp->iocb_flag &=
+ cmdiocbp->cmd_flag &=
~LPFC_DRIVER_ABORTED;
spin_unlock_irqrestore(&phba->hbalock,
iflag);
@@ -2640,12 +3791,12 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*/
spin_lock_irqsave(&phba->hbalock,
iflag);
- saveq->iocb_flag |= LPFC_DELAY_MEM_FREE;
+ saveq->cmd_flag |= LPFC_DELAY_MEM_FREE;
spin_unlock_irqrestore(&phba->hbalock,
iflag);
}
if (phba->sli_rev == LPFC_SLI_REV4) {
- if (saveq->iocb_flag &
+ if (saveq->cmd_flag &
LPFC_EXCHANGE_BUSY) {
/* Set cmdiocb flag for the
* exchange busy so sgl (xri)
@@ -2655,12 +3806,12 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*/
spin_lock_irqsave(
&phba->hbalock, iflag);
- cmdiocbp->iocb_flag |=
+ cmdiocbp->cmd_flag |=
LPFC_EXCHANGE_BUSY;
spin_unlock_irqrestore(
&phba->hbalock, iflag);
}
- if (cmdiocbp->iocb_flag &
+ if (cmdiocbp->cmd_flag &
LPFC_DRIVER_ABORTED) {
/*
* Clear LPFC_DRIVER_ABORTED
@@ -2669,34 +3820,34 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*/
spin_lock_irqsave(
&phba->hbalock, iflag);
- cmdiocbp->iocb_flag &=
+ cmdiocbp->cmd_flag &=
~LPFC_DRIVER_ABORTED;
spin_unlock_irqrestore(
&phba->hbalock, iflag);
- cmdiocbp->iocb.ulpStatus =
- IOSTAT_LOCAL_REJECT;
- cmdiocbp->iocb.un.ulpWord[4] =
- IOERR_ABORT_REQUESTED;
+ set_job_ulpstatus(cmdiocbp,
+ IOSTAT_LOCAL_REJECT);
+ set_job_ulpword4(cmdiocbp,
+ IOERR_ABORT_REQUESTED);
/*
- * For SLI4, irsiocb contains
+ * For SLI4, irspiocb contains
* NO_XRI in sli_xritag, it
* shall not affect releasing
* sgl (xri) process.
*/
- saveq->iocb.ulpStatus =
- IOSTAT_LOCAL_REJECT;
- saveq->iocb.un.ulpWord[4] =
- IOERR_SLI_ABORTED;
+ set_job_ulpstatus(saveq,
+ IOSTAT_LOCAL_REJECT);
+ set_job_ulpword4(saveq,
+ IOERR_SLI_ABORTED);
spin_lock_irqsave(
&phba->hbalock, iflag);
- saveq->iocb_flag |=
+ saveq->cmd_flag |=
LPFC_DELAY_MEM_FREE;
spin_unlock_irqrestore(
&phba->hbalock, iflag);
}
}
}
- (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
+ cmdiocbp->cmd_cmpl(phba, cmdiocbp, saveq);
} else
lpfc_sli_release_iocbq(phba, cmdiocbp);
} else {
@@ -2714,16 +3865,12 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
"0322 Ring %d handler: "
"unexpected completion IoTag x%x "
"Data: x%x x%x x%x x%x\n",
- pring->ringno,
- saveq->iocb.ulpIoTag,
- saveq->iocb.ulpStatus,
- saveq->iocb.un.ulpWord[4],
- saveq->iocb.ulpCommand,
- saveq->iocb.ulpContext);
+ pring->ringno, iotag, ulp_status,
+ ulp_word4, ulp_command, ulp_context);
}
}
- return rc;
+ return 1;
}
/**
@@ -2744,11 +3891,11 @@ lpfc_sli_rsp_pointers_error(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
* Ring <ringno> handler: portRspPut <portRspPut> is bigger than
* rsp ring <portRspMax>
*/
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0312 Ring %d handler: portRspPut %d "
"is bigger than rsp ring %d\n",
pring->ringno, le32_to_cpu(pgp->rspPutInx),
- pring->numRiocb);
+ pring->sli.sli3.numRiocb);
phba->link_state = LPFC_HBA_ERROR;
@@ -2766,7 +3913,7 @@ lpfc_sli_rsp_pointers_error(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
/**
* lpfc_poll_eratt - Error attention polling timer timeout handler
- * @ptr: Pointer to address of HBA context object.
+ * @t: Context to fetch pointer to address of HBA context object from.
*
* This function is invoked by the Error Attention polling timer when the
* timer times out. It will check the SLI Error Attention register for
@@ -2774,23 +3921,54 @@ lpfc_sli_rsp_pointers_error(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
* and wake up worker thread to process it. Otherwise, it will set up the
* Error Attention polling timer for the next poll.
**/
-void lpfc_poll_eratt(unsigned long ptr)
+void lpfc_poll_eratt(struct timer_list *t)
{
struct lpfc_hba *phba;
uint32_t eratt = 0;
+ uint64_t sli_intr, cnt;
- phba = (struct lpfc_hba *)ptr;
+ phba = timer_container_of(phba, t, eratt_poll);
+
+ if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ return;
+
+ if (phba->sli_rev == LPFC_SLI_REV4 &&
+ !test_bit(HBA_SETUP, &phba->hba_flag)) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0663 HBA still initializing 0x%lx, restart "
+ "timer\n",
+ phba->hba_flag);
+ goto restart_timer;
+ }
+
+ /* Here we will also keep track of interrupts per sec of the hba */
+ sli_intr = phba->sli.slistat.sli_intr;
+
+ if (phba->sli.slistat.sli_prev_intr > sli_intr)
+ cnt = (((uint64_t)(-1) - phba->sli.slistat.sli_prev_intr) +
+ sli_intr);
+ else
+ cnt = (sli_intr - phba->sli.slistat.sli_prev_intr);
+
+ /* 64-bit integer division not supported on 32-bit x86 - use do_div */
+ do_div(cnt, phba->eratt_poll_interval);
+ phba->sli.slistat.sli_ips = cnt;
+
+ phba->sli.slistat.sli_prev_intr = sli_intr;
/* Check chip HA register for error event */
eratt = lpfc_sli_check_eratt(phba);
- if (eratt)
+ if (eratt) {
/* Tell the worker thread there is work to do */
lpfc_worker_wake_up(phba);
- else
- /* Restart the timer for next eratt poll */
- mod_timer(&phba->eratt_poll, jiffies +
- HZ * LPFC_ERATT_POLL_INTERVAL);
+ return;
+ }
+
+restart_timer:
+ /* Restart the timer for next eratt poll */
+ mod_timer(&phba->eratt_poll,
+ jiffies + secs_to_jiffies(phba->eratt_poll_interval));
return;
}
@@ -2835,7 +4013,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
* The next available response entry should never exceed the maximum
* entries. If it does, treat it as an adapter hardware error.
*/
- portRspMax = pring->numRiocb;
+ portRspMax = pring->sli.sli3.numRiocb;
portRspPut = le32_to_cpu(pgp->rspPutInx);
if (unlikely(portRspPut >= portRspMax)) {
lpfc_sli_rsp_pointers_error(phba, pring);
@@ -2849,7 +4027,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
phba->fcp_ring_in_use = 1;
rmb();
- while (pring->rspidx != portRspPut) {
+ while (pring->sli.sli3.rspidx != portRspPut) {
/*
* Fetch an entry off the ring and copy it into a local data
* structure. The copy involves a byte-swap since the
@@ -2858,8 +4036,8 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
entry = lpfc_resp_iocb(phba, pring);
phba->last_completion_time = jiffies;
- if (++pring->rspidx >= portRspMax)
- pring->rspidx = 0;
+ if (++pring->sli.sli3.rspidx >= portRspMax)
+ pring->sli.sli3.rspidx = 0;
lpfc_sli_pcimem_bcopy((uint32_t *) entry,
(uint32_t *) &rspiocbq.iocb,
@@ -2877,7 +4055,8 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
* queuedepths of the SCSI device.
*/
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
+ ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK) ==
+ IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
phba->lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
@@ -2918,12 +4097,11 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
&rspiocbq);
if (unlikely(!cmdiocbq))
break;
- if (cmdiocbq->iocb_flag & LPFC_DRIVER_ABORTED)
- cmdiocbq->iocb_flag &= ~LPFC_DRIVER_ABORTED;
- if (cmdiocbq->iocb_cmpl) {
+ if (cmdiocbq->cmd_flag & LPFC_DRIVER_ABORTED)
+ cmdiocbq->cmd_flag &= ~LPFC_DRIVER_ABORTED;
+ if (cmdiocbq->cmd_cmpl) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
+ cmdiocbq->cmd_cmpl(phba, cmdiocbq, &rspiocbq);
spin_lock_irqsave(&phba->hbalock, iflag);
}
break;
@@ -2943,7 +4121,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
phba->brd_no, adaptermsg);
} else {
/* Unknown IOCB command */
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0334 Unknown IOCB command "
"Data: x%x, x%x x%x x%x x%x\n",
type, irsp->ulpCommand,
@@ -2960,9 +4138,10 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
* been updated, sync the pgp->rspPutInx and fetch the new port
* response put pointer.
*/
- writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
+ writel(pring->sli.sli3.rspidx,
+ &phba->host_gp[pring->ringno].rspGetInx);
- if (pring->rspidx == portRspPut)
+ if (pring->sli.sli3.rspidx == portRspPut)
portRspPut = le32_to_cpu(pgp->rspPutInx);
}
@@ -2977,7 +4156,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
pring->stats.iocb_cmd_empty++;
/* Force update of the local copy of cmdGetInx */
- pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+ pring->sli.sli3.local_getidx = le32_to_cpu(pgp->cmdGetInx);
lpfc_sli_resume_iocb(phba, pring);
if ((pring->lpfc_sli_cmd_available))
@@ -3013,151 +4192,159 @@ lpfc_sli_sp_handle_rspiocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *rspiocbp)
{
struct lpfc_iocbq *saveq;
- struct lpfc_iocbq *cmdiocbp;
+ struct lpfc_iocbq *cmdiocb;
struct lpfc_iocbq *next_iocb;
- IOCB_t *irsp = NULL;
+ IOCB_t *irsp;
uint32_t free_saveq;
- uint8_t iocb_cmd_type;
+ u8 cmd_type;
lpfc_iocb_type type;
unsigned long iflag;
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocbp);
+ u32 ulp_word4 = get_job_word4(phba, rspiocbp);
+ u32 ulp_command = get_job_cmnd(phba, rspiocbp);
int rc;
spin_lock_irqsave(&phba->hbalock, iflag);
/* First add the response iocb to the countinueq list */
- list_add_tail(&rspiocbp->list, &(pring->iocb_continueq));
+ list_add_tail(&rspiocbp->list, &pring->iocb_continueq);
pring->iocb_continueq_cnt++;
- /* Now, determine whether the list is completed for processing */
- irsp = &rspiocbp->iocb;
- if (irsp->ulpLe) {
- /*
- * By default, the driver expects to free all resources
- * associated with this iocb completion.
- */
- free_saveq = 1;
- saveq = list_get_first(&pring->iocb_continueq,
- struct lpfc_iocbq, list);
- irsp = &(saveq->iocb);
- list_del_init(&pring->iocb_continueq);
- pring->iocb_continueq_cnt = 0;
+ /*
+ * By default, the driver expects to free all resources
+ * associated with this iocb completion.
+ */
+ free_saveq = 1;
+ saveq = list_get_first(&pring->iocb_continueq,
+ struct lpfc_iocbq, list);
+ list_del_init(&pring->iocb_continueq);
+ pring->iocb_continueq_cnt = 0;
- pring->stats.iocb_rsp++;
+ pring->stats.iocb_rsp++;
- /*
- * If resource errors reported from HBA, reduce
- * queuedepths of the SCSI device.
- */
- if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- phba->lpfc_rampdown_queue_depth(phba);
- spin_lock_irqsave(&phba->hbalock, iflag);
- }
+ /*
+ * If resource errors reported from HBA, reduce
+ * queuedepths of the SCSI device.
+ */
+ if (ulp_status == IOSTAT_LOCAL_REJECT &&
+ ((ulp_word4 & IOERR_PARAM_MASK) ==
+ IOERR_NO_RESOURCES)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ phba->lpfc_rampdown_queue_depth(phba);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ }
- if (irsp->ulpStatus) {
- /* Rsp ring <ringno> error: IOCB */
+ if (ulp_status) {
+ /* Rsp ring <ringno> error: IOCB */
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ irsp = &rspiocbp->iocb;
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0328 Rsp Ring %d error: "
+ "0328 Rsp Ring %d error: ulp_status x%x "
+ "IOCB Data: "
+ "x%08x x%08x x%08x x%08x "
+ "x%08x x%08x x%08x x%08x "
+ "x%08x x%08x x%08x x%08x "
+ "x%08x x%08x x%08x x%08x\n",
+ pring->ringno, ulp_status,
+ get_job_ulpword(rspiocbp, 0),
+ get_job_ulpword(rspiocbp, 1),
+ get_job_ulpword(rspiocbp, 2),
+ get_job_ulpword(rspiocbp, 3),
+ get_job_ulpword(rspiocbp, 4),
+ get_job_ulpword(rspiocbp, 5),
+ *(((uint32_t *)irsp) + 6),
+ *(((uint32_t *)irsp) + 7),
+ *(((uint32_t *)irsp) + 8),
+ *(((uint32_t *)irsp) + 9),
+ *(((uint32_t *)irsp) + 10),
+ *(((uint32_t *)irsp) + 11),
+ *(((uint32_t *)irsp) + 12),
+ *(((uint32_t *)irsp) + 13),
+ *(((uint32_t *)irsp) + 14),
+ *(((uint32_t *)irsp) + 15));
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0321 Rsp Ring %d error: "
"IOCB Data: "
- "x%x x%x x%x x%x "
- "x%x x%x x%x x%x "
- "x%x x%x x%x x%x "
"x%x x%x x%x x%x\n",
pring->ringno,
- irsp->un.ulpWord[0],
- irsp->un.ulpWord[1],
- irsp->un.ulpWord[2],
- irsp->un.ulpWord[3],
- irsp->un.ulpWord[4],
- irsp->un.ulpWord[5],
- *(((uint32_t *) irsp) + 6),
- *(((uint32_t *) irsp) + 7),
- *(((uint32_t *) irsp) + 8),
- *(((uint32_t *) irsp) + 9),
- *(((uint32_t *) irsp) + 10),
- *(((uint32_t *) irsp) + 11),
- *(((uint32_t *) irsp) + 12),
- *(((uint32_t *) irsp) + 13),
- *(((uint32_t *) irsp) + 14),
- *(((uint32_t *) irsp) + 15));
+ rspiocbp->wcqe_cmpl.word0,
+ rspiocbp->wcqe_cmpl.total_data_placed,
+ rspiocbp->wcqe_cmpl.parameter,
+ rspiocbp->wcqe_cmpl.word3);
}
+ }
- /*
- * Fetch the IOCB command type and call the correct completion
- * routine. Solicited and Unsolicited IOCBs on the ELS ring
- * get freed back to the lpfc_iocb_list by the discovery
- * kernel thread.
- */
- iocb_cmd_type = irsp->ulpCommand & CMD_IOCB_MASK;
- type = lpfc_sli_iocb_cmd_type(iocb_cmd_type);
- switch (type) {
- case LPFC_SOL_IOCB:
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- rc = lpfc_sli_process_sol_iocb(phba, pring, saveq);
- spin_lock_irqsave(&phba->hbalock, iflag);
- break;
-
- case LPFC_UNSOL_IOCB:
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- rc = lpfc_sli_process_unsol_iocb(phba, pring, saveq);
- spin_lock_irqsave(&phba->hbalock, iflag);
- if (!rc)
- free_saveq = 0;
- break;
-
- case LPFC_ABORT_IOCB:
- cmdiocbp = NULL;
- if (irsp->ulpCommand != CMD_XRI_ABORTED_CX)
- cmdiocbp = lpfc_sli_iocbq_lookup(phba, pring,
- saveq);
- if (cmdiocbp) {
- /* Call the specified completion routine */
- if (cmdiocbp->iocb_cmpl) {
- spin_unlock_irqrestore(&phba->hbalock,
- iflag);
- (cmdiocbp->iocb_cmpl)(phba, cmdiocbp,
- saveq);
- spin_lock_irqsave(&phba->hbalock,
- iflag);
- } else
- __lpfc_sli_release_iocbq(phba,
- cmdiocbp);
- }
- break;
- case LPFC_UNKNOWN_IOCB:
- if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
- char adaptermsg[LPFC_MAX_ADPTMSG];
- memset(adaptermsg, 0, LPFC_MAX_ADPTMSG);
- memcpy(&adaptermsg[0], (uint8_t *)irsp,
- MAX_MSG_DATA);
- dev_warn(&((phba->pcidev)->dev),
- "lpfc%d: %s\n",
- phba->brd_no, adaptermsg);
+ /*
+ * Fetch the iocb command type and call the correct completion
+ * routine. Solicited and Unsolicited IOCBs on the ELS ring
+ * get freed back to the lpfc_iocb_list by the discovery
+ * kernel thread.
+ */
+ cmd_type = ulp_command & CMD_IOCB_MASK;
+ type = lpfc_sli_iocb_cmd_type(cmd_type);
+ switch (type) {
+ case LPFC_SOL_IOCB:
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ rc = lpfc_sli_process_sol_iocb(phba, pring, saveq);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ break;
+ case LPFC_UNSOL_IOCB:
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ rc = lpfc_sli_process_unsol_iocb(phba, pring, saveq);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ if (!rc)
+ free_saveq = 0;
+ break;
+ case LPFC_ABORT_IOCB:
+ cmdiocb = NULL;
+ if (ulp_command != CMD_XRI_ABORTED_CX)
+ cmdiocb = lpfc_sli_iocbq_lookup(phba, pring,
+ saveq);
+ if (cmdiocb) {
+ /* Call the specified completion routine */
+ if (cmdiocb->cmd_cmpl) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ cmdiocb->cmd_cmpl(phba, cmdiocb, saveq);
+ spin_lock_irqsave(&phba->hbalock, iflag);
} else {
- /* Unknown IOCB command */
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0335 Unknown IOCB "
- "command Data: x%x "
- "x%x x%x x%x\n",
- irsp->ulpCommand,
- irsp->ulpStatus,
- irsp->ulpIoTag,
- irsp->ulpContext);
+ __lpfc_sli_release_iocbq(phba, cmdiocb);
}
- break;
}
+ break;
+ case LPFC_UNKNOWN_IOCB:
+ if (ulp_command == CMD_ADAPTER_MSG) {
+ char adaptermsg[LPFC_MAX_ADPTMSG];
+
+ memset(adaptermsg, 0, LPFC_MAX_ADPTMSG);
+ memcpy(&adaptermsg[0], (uint8_t *)&rspiocbp->wqe,
+ MAX_MSG_DATA);
+ dev_warn(&((phba->pcidev)->dev),
+ "lpfc%d: %s\n",
+ phba->brd_no, adaptermsg);
+ } else {
+ /* Unknown command */
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0335 Unknown IOCB "
+ "command Data: x%x "
+ "x%x x%x x%x\n",
+ ulp_command,
+ ulp_status,
+ get_wqe_reqtag(rspiocbp),
+ get_job_ulpcontext(phba, rspiocbp));
+ }
+ break;
+ }
- if (free_saveq) {
- list_for_each_entry_safe(rspiocbp, next_iocb,
- &saveq->list, list) {
- list_del(&rspiocbp->list);
- __lpfc_sli_release_iocbq(phba, rspiocbp);
- }
- __lpfc_sli_release_iocbq(phba, saveq);
+ if (free_saveq) {
+ list_for_each_entry_safe(rspiocbp, next_iocb,
+ &saveq->list, list) {
+ list_del_init(&rspiocbp->list);
+ __lpfc_sli_release_iocbq(phba, rspiocbp);
}
- rspiocbp = NULL;
+ __lpfc_sli_release_iocbq(phba, saveq);
}
+ rspiocbp = NULL;
spin_unlock_irqrestore(&phba->hbalock, iflag);
return rspiocbp;
}
@@ -3209,14 +4396,14 @@ lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
* The next available response entry should never exceed the maximum
* entries. If it does, treat it as an adapter hardware error.
*/
- portRspMax = pring->numRiocb;
+ portRspMax = pring->sli.sli3.numRiocb;
portRspPut = le32_to_cpu(pgp->rspPutInx);
if (portRspPut >= portRspMax) {
/*
* Ring <ringno> handler: portRspPut <portRspPut> is bigger than
* rsp ring <portRspMax>
*/
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0303 Ring %d handler: portRspPut %d "
"is bigger than rsp ring %d\n",
pring->ringno, portRspPut, portRspMax);
@@ -3231,7 +4418,7 @@ lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
}
rmb();
- while (pring->rspidx != portRspPut) {
+ while (pring->sli.sli3.rspidx != portRspPut) {
/*
* Build a completion list and call the appropriate handler.
* The process is to get the next available response iocb, get
@@ -3259,8 +4446,8 @@ lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
phba->iocb_rsp_size);
irsp = &rspiocbp->iocb;
- if (++pring->rspidx >= portRspMax)
- pring->rspidx = 0;
+ if (++pring->sli.sli3.rspidx >= portRspMax)
+ pring->sli.sli3.rspidx = 0;
if (pring->ringno == LPFC_ELS_RING) {
lpfc_debugfs_slow_ring_trc(phba,
@@ -3270,7 +4457,8 @@ lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
*(((uint32_t *) irsp) + 7));
}
- writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
+ writel(pring->sli.sli3.rspidx,
+ &phba->host_gp[pring->ringno].rspGetInx);
spin_unlock_irqrestore(&phba->hbalock, iflag);
/* Handle the response IOCB */
@@ -3282,10 +4470,10 @@ lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
* the pgp->rspPutInx in the MAILBOX_tand fetch the new port
* response put pointer.
*/
- if (pring->rspidx == portRspPut) {
+ if (pring->sli.sli3.rspidx == portRspPut) {
portRspPut = le32_to_cpu(pgp->rspPutInx);
}
- } /* while (pring->rspidx != portRspPut) */
+ } /* while (pring->sli.sli3.rspidx != portRspPut) */
if ((rspiocbp != NULL) && (mask & HA_R0RE_REQ)) {
/* At least one response entry has been freed */
@@ -3300,7 +4488,7 @@ lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
pring->stats.iocb_cmd_empty++;
/* Force update of the local copy of cmdGetInx */
- pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+ pring->sli.sli3.local_getidx = le32_to_cpu(pgp->cmdGetInx);
lpfc_sli_resume_iocb(phba, pring);
if ((pring->lpfc_sli_cmd_available))
@@ -3332,10 +4520,9 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
struct hbq_dmabuf *dmabuf;
struct lpfc_cq_event *cq_event;
unsigned long iflag;
+ int count = 0;
- spin_lock_irqsave(&phba->hbalock, iflag);
- phba->hba_flag &= ~HBA_SP_QUEUE_EVT;
- spin_unlock_irqrestore(&phba->hbalock, iflag);
+ clear_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag);
while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
/* Get the response iocb from the head of work queue */
spin_lock_irqsave(&phba->hbalock, iflag);
@@ -3348,20 +4535,27 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
irspiocbq = container_of(cq_event, struct lpfc_iocbq,
cq_event);
/* Translate ELS WCQE to response IOCBQ */
- irspiocbq = lpfc_sli4_els_wcqe_to_rspiocbq(phba,
- irspiocbq);
+ irspiocbq = lpfc_sli4_els_preprocess_rspiocbq(phba,
+ irspiocbq);
if (irspiocbq)
lpfc_sli_sp_handle_rspiocb(phba, pring,
irspiocbq);
+ count++;
break;
case CQE_CODE_RECEIVE:
+ case CQE_CODE_RECEIVE_V1:
dmabuf = container_of(cq_event, struct hbq_dmabuf,
cq_event);
lpfc_sli4_handle_received_buffer(phba, dmabuf);
+ count++;
break;
default:
break;
}
+
+ /* Limit the number of events to 64 to avoid soft lockups */
+ if (count == 64)
+ break;
}
}
@@ -3378,69 +4572,177 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
void
lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
- LIST_HEAD(completions);
+ LIST_HEAD(tx_completions);
+ LIST_HEAD(txcmplq_completions);
struct lpfc_iocbq *iocb, *next_iocb;
+ int offline;
if (pring->ringno == LPFC_ELS_RING) {
lpfc_fabric_abort_hba(phba);
}
+ offline = pci_channel_offline(phba->pcidev);
/* Error everything on txq and txcmplq
* First do the txq.
*/
- spin_lock_irq(&phba->hbalock);
- list_splice_init(&pring->txq, &completions);
- pring->txq_cnt = 0;
+ if (phba->sli_rev >= LPFC_SLI_REV4) {
+ spin_lock_irq(&pring->ring_lock);
+ list_splice_init(&pring->txq, &tx_completions);
+ pring->txq_cnt = 0;
- /* Next issue ABTS for everything on the txcmplq */
- list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list)
- lpfc_sli_issue_abort_iotag(phba, pring, iocb);
+ if (offline) {
+ list_splice_init(&pring->txcmplq,
+ &txcmplq_completions);
+ } else {
+ /* Next issue ABTS for everything on the txcmplq */
+ list_for_each_entry_safe(iocb, next_iocb,
+ &pring->txcmplq, list)
+ lpfc_sli_issue_abort_iotag(phba, pring,
+ iocb, NULL);
+ }
+ spin_unlock_irq(&pring->ring_lock);
+ } else {
+ spin_lock_irq(&phba->hbalock);
+ list_splice_init(&pring->txq, &tx_completions);
+ pring->txq_cnt = 0;
- spin_unlock_irq(&phba->hbalock);
+ if (offline) {
+ list_splice_init(&pring->txcmplq, &txcmplq_completions);
+ } else {
+ /* Next issue ABTS for everything on the txcmplq */
+ list_for_each_entry_safe(iocb, next_iocb,
+ &pring->txcmplq, list)
+ lpfc_sli_issue_abort_iotag(phba, pring,
+ iocb, NULL);
+ }
+ spin_unlock_irq(&phba->hbalock);
+ }
+ if (offline) {
+ /* Cancel all the IOCBs from the completions list */
+ lpfc_sli_cancel_iocbs(phba, &txcmplq_completions,
+ IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED);
+ } else {
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+ }
/* Cancel all the IOCBs from the completions list */
- lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
+ lpfc_sli_cancel_iocbs(phba, &tx_completions, IOSTAT_LOCAL_REJECT,
IOERR_SLI_ABORTED);
}
/**
- * lpfc_sli_flush_fcp_rings - flush all iocbs in the fcp ring
+ * lpfc_sli_abort_fcp_rings - Abort all iocbs in all FCP rings
* @phba: Pointer to HBA context object.
*
- * This function flushes all iocbs in the fcp ring and frees all the iocb
+ * This function aborts all iocbs in FCP rings and frees all the iocb
+ * objects in txq. This function issues an abort iocb for all the iocb commands
+ * in txcmplq. The iocbs in the txcmplq is not guaranteed to complete before
+ * the return of this function. The caller is not required to hold any locks.
+ **/
+void
+lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring;
+ uint32_t i;
+
+ /* Look on all the FCP Rings for the iotag */
+ if (phba->sli_rev >= LPFC_SLI_REV4) {
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].io_wq->pring;
+ lpfc_sli_abort_iocb_ring(phba, pring);
+ }
+ } else {
+ pring = &psli->sli3_ring[LPFC_FCP_RING];
+ lpfc_sli_abort_iocb_ring(phba, pring);
+ }
+}
+
+/**
+ * lpfc_sli_flush_io_rings - flush all iocbs in the IO ring
+ * @phba: Pointer to HBA context object.
+ *
+ * This function flushes all iocbs in the IO ring and frees all the iocb
* objects in txq and txcmplq. This function will not issue abort iocbs
* for all the iocb commands in txcmplq, they will just be returned with
* IOERR_SLI_DOWN. This function is invoked with EEH when device's PCI
* slot has been permanently disabled.
**/
void
-lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
+lpfc_sli_flush_io_rings(struct lpfc_hba *phba)
{
LIST_HEAD(txq);
LIST_HEAD(txcmplq);
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
+ uint32_t i;
+ struct lpfc_iocbq *piocb, *next_iocb;
- /* Currently, only one fcp ring */
- pring = &psli->ring[psli->fcp_ring];
-
- spin_lock_irq(&phba->hbalock);
- /* Retrieve everything on txq */
- list_splice_init(&pring->txq, &txq);
- pring->txq_cnt = 0;
+ /* Indicate the I/O queues are flushed */
+ set_bit(HBA_IOQ_FLUSH, &phba->hba_flag);
- /* Retrieve everything on the txcmplq */
- list_splice_init(&pring->txcmplq, &txcmplq);
- pring->txcmplq_cnt = 0;
- spin_unlock_irq(&phba->hbalock);
+ /* Look on all the FCP Rings for the iotag */
+ if (phba->sli_rev >= LPFC_SLI_REV4) {
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ if (!phba->sli4_hba.hdwq ||
+ !phba->sli4_hba.hdwq[i].io_wq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "7777 hdwq's deleted %lx "
+ "%lx %x %x\n",
+ phba->pport->load_flag,
+ phba->hba_flag,
+ phba->link_state,
+ phba->sli.sli_flag);
+ return;
+ }
+ pring = phba->sli4_hba.hdwq[i].io_wq->pring;
+
+ spin_lock_irq(&pring->ring_lock);
+ /* Retrieve everything on txq */
+ list_splice_init(&pring->txq, &txq);
+ list_for_each_entry_safe(piocb, next_iocb,
+ &pring->txcmplq, list)
+ piocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ;
+ /* Retrieve everything on the txcmplq */
+ list_splice_init(&pring->txcmplq, &txcmplq);
+ pring->txq_cnt = 0;
+ pring->txcmplq_cnt = 0;
+ spin_unlock_irq(&pring->ring_lock);
+
+ /* Flush the txq */
+ lpfc_sli_cancel_iocbs(phba, &txq,
+ IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
+ /* Flush the txcmplq */
+ lpfc_sli_cancel_iocbs(phba, &txcmplq,
+ IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
+ if (unlikely(pci_channel_offline(phba->pcidev)))
+ lpfc_sli4_io_xri_aborted(phba, NULL, 0);
+ }
+ } else {
+ pring = &psli->sli3_ring[LPFC_FCP_RING];
- /* Flush the txq */
- lpfc_sli_cancel_iocbs(phba, &txq, IOSTAT_LOCAL_REJECT,
- IOERR_SLI_DOWN);
+ spin_lock_irq(&phba->hbalock);
+ /* Retrieve everything on txq */
+ list_splice_init(&pring->txq, &txq);
+ list_for_each_entry_safe(piocb, next_iocb,
+ &pring->txcmplq, list)
+ piocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ;
+ /* Retrieve everything on the txcmplq */
+ list_splice_init(&pring->txcmplq, &txcmplq);
+ pring->txq_cnt = 0;
+ pring->txcmplq_cnt = 0;
+ spin_unlock_irq(&phba->hbalock);
- /* Flush the txcmpq */
- lpfc_sli_cancel_iocbs(phba, &txcmplq, IOSTAT_LOCAL_REJECT,
- IOERR_SLI_DOWN);
+ /* Flush the txq */
+ lpfc_sli_cancel_iocbs(phba, &txq, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
+ /* Flush the txcmpq */
+ lpfc_sli_cancel_iocbs(phba, &txcmplq, IOSTAT_LOCAL_REJECT,
+ IOERR_SLI_DOWN);
+ }
}
/**
@@ -3467,6 +4769,8 @@ lpfc_sli_brdready_s3(struct lpfc_hba *phba, uint32_t mask)
if (lpfc_readl(phba->HSregaddr, &status))
return 1;
+ set_bit(HBA_NEEDS_CFG_PORT, &phba->hba_flag);
+
/*
* Check status register every 100ms for 5 retries, then every
* 500ms for 5, then every 2.5 sec for 5, then reset board and
@@ -3498,7 +4802,7 @@ lpfc_sli_brdready_s3(struct lpfc_hba *phba, uint32_t mask)
/* Check to see if any errors occurred during init */
if ((status & HS_FFERM) || (i >= 20)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2751 Adapter failed to restart, "
"status reg x%x, FW Data: A8 x%x AC x%x\n",
status,
@@ -3544,6 +4848,7 @@ lpfc_sli_brdready_s4(struct lpfc_hba *phba, uint32_t mask)
} else
phba->sli4_hba.intr_enable = 0;
+ clear_bit(HBA_SETUP, &phba->hba_flag);
return retval;
}
@@ -3567,20 +4872,22 @@ lpfc_sli_brdready(struct lpfc_hba *phba, uint32_t mask)
* lpfc_reset_barrier - Make HBA ready for HBA reset
* @phba: Pointer to HBA context object.
*
- * This function is called before resetting an HBA. This
- * function requests HBA to quiesce DMAs before a reset.
+ * This function is called before resetting an HBA. This function is called
+ * with hbalock held and requests HBA to quiesce DMAs before a reset.
**/
void lpfc_reset_barrier(struct lpfc_hba *phba)
{
uint32_t __iomem *resp_buf;
uint32_t __iomem *mbox_buf;
- volatile uint32_t mbox;
+ volatile struct MAILBOX_word0 mbox;
uint32_t hc_copy, ha_copy, resp_data;
int i;
uint8_t hdrtype;
+ lockdep_assert_held(&phba->hbalock);
+
pci_read_config_byte(phba->pcidev, PCI_HEADER_TYPE, &hdrtype);
- if (hdrtype != 0x80 ||
+ if (hdrtype != PCI_HEADER_TYPE_MFD ||
(FC_JEDEC_ID(phba->vpd.rev.biuRev) != HELIOS_JEDEC_ID &&
FC_JEDEC_ID(phba->vpd.rev.biuRev) != THOR_JEDEC_ID))
return;
@@ -3606,13 +4913,13 @@ void lpfc_reset_barrier(struct lpfc_hba *phba)
phba->pport->stopped = 1;
}
- mbox = 0;
- ((MAILBOX_t *)&mbox)->mbxCommand = MBX_KILL_BOARD;
- ((MAILBOX_t *)&mbox)->mbxOwner = OWN_CHIP;
+ mbox.word0 = 0;
+ mbox.mbxCommand = MBX_KILL_BOARD;
+ mbox.mbxOwner = OWN_CHIP;
writel(BARRIER_TEST_PATTERN, (resp_buf + 1));
mbox_buf = phba->MBslimaddr;
- writel(mbox, mbox_buf);
+ writel(mbox.word0, mbox_buf);
for (i = 0; i < 50; i++) {
if (lpfc_readl((resp_buf + 1), &resp_data))
@@ -3633,12 +4940,12 @@ void lpfc_reset_barrier(struct lpfc_hba *phba)
goto clear_errat;
}
- ((MAILBOX_t *)&mbox)->mbxOwner = OWN_HOST;
+ mbox.mbxOwner = OWN_HOST;
resp_data = 0;
for (i = 0; i < 500; i++) {
if (lpfc_readl(resp_buf, &resp_data))
return;
- if (resp_data != mbox)
+ if (resp_data != mbox.word0)
mdelay(1);
else
break;
@@ -3718,7 +5025,7 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
if (retval != MBX_SUCCESS) {
if (retval != MBX_BUSY)
mempool_free(pmb, phba->mbox_mem_pool);
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2752 KILL_BOARD command failed retval %d\n",
retval);
spin_lock_irq(&phba->hbalock);
@@ -3746,7 +5053,7 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
return 1;
}
- del_timer_sync(&psli->mbox_tmo);
+ timer_delete_sync(&psli->mbox_tmo);
if (ha_copy & HA_ERATT) {
writel(HA_ERATT, phba->HAregaddr);
phba->pport->stopped = 1;
@@ -3787,16 +5094,22 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
/* Reset HBA */
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"0325 Reset HBA Data: x%x x%x\n",
- phba->pport->port_state, psli->sli_flag);
+ (phba->pport) ? phba->pport->port_state : 0,
+ psli->sli_flag);
/* perform board reset */
phba->fc_eventTag = 0;
phba->link_events = 0;
- phba->pport->fc_myDID = 0;
- phba->pport->fc_prevDID = 0;
+ set_bit(HBA_NEEDS_CFG_PORT, &phba->hba_flag);
+ if (phba->pport) {
+ phba->pport->fc_myDID = 0;
+ phba->pport->fc_prevDID = 0;
+ }
/* Turn off parity checking and serr during the physical reset */
- pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value);
+ if (pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value))
+ return -EIO;
+
pci_write_config_word(phba->pcidev, PCI_COMMAND,
(cfg_value &
~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
@@ -3815,12 +5128,12 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
/* Initialize relevant SLI info */
for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
+ pring = &psli->sli3_ring[i];
pring->flag = 0;
- pring->rspidx = 0;
- pring->next_cmdidx = 0;
- pring->local_getidx = 0;
- pring->cmdidx = 0;
+ pring->sli.sli3.rspidx = 0;
+ pring->sli.sli3.next_cmdidx = 0;
+ pring->sli.sli3.local_getidx = 0;
+ pring->sli.sli3.cmdidx = 0;
pring->missbufcnt = 0;
}
@@ -3836,19 +5149,20 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
* checking during resets the device. The caller is not required to hold
* any locks.
*
- * This function returns 0 always.
+ * This function returns 0 on success else returns negative error code.
**/
int
lpfc_sli4_brdreset(struct lpfc_hba *phba)
{
struct lpfc_sli *psli = &phba->sli;
uint16_t cfg_value;
- uint8_t qindx;
+ int rc = 0;
/* Reset HBA */
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "0295 Reset HBA Data: x%x x%x\n",
- phba->pport->port_state, psli->sli_flag);
+ "0295 Reset HBA Data: x%x x%x x%lx\n",
+ phba->pport->port_state, psli->sli_flag,
+ phba->hba_flag);
/* perform board reset */
phba->fc_eventTag = 0;
@@ -3859,19 +5173,6 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~(LPFC_PROCESS_LA);
phba->fcf.fcf_flag = 0;
- /* Clean up the child queue list for the CQs */
- list_del_init(&phba->sli4_hba.mbx_wq->list);
- list_del_init(&phba->sli4_hba.els_wq->list);
- list_del_init(&phba->sli4_hba.hdr_rq->list);
- list_del_init(&phba->sli4_hba.dat_rq->list);
- list_del_init(&phba->sli4_hba.mbx_cq->list);
- list_del_init(&phba->sli4_hba.els_cq->list);
- for (qindx = 0; qindx < phba->cfg_fcp_wq_count; qindx++)
- list_del_init(&phba->sli4_hba.fcp_wq[qindx]->list);
- qindx = 0;
- do
- list_del_init(&phba->sli4_hba.fcp_cq[qindx]->list);
- while (++qindx < phba->cfg_fcp_eq_count);
spin_unlock_irq(&phba->hbalock);
/* Now physically reset the device */
@@ -3879,17 +5180,22 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
"0389 Performing PCI function reset!\n");
/* Turn off parity checking and serr during the physical reset */
- pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value);
+ if (pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value)) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3205 PCI read Config failed\n");
+ return -EIO;
+ }
+
pci_write_config_word(phba->pcidev, PCI_COMMAND, (cfg_value &
~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
- /* Perform FCoE PCI function reset */
- lpfc_pci_function_reset(phba);
+ /* Perform FCoE PCI function reset before freeing queue memory */
+ rc = lpfc_pci_function_reset(phba);
/* Restore PCI cmd register */
pci_write_config_word(phba->pcidev, PCI_COMMAND, cfg_value);
- return 0;
+ return rc;
}
/**
@@ -3908,60 +5214,52 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
static int
lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
{
- MAILBOX_t *mb;
+ volatile struct MAILBOX_word0 mb;
struct lpfc_sli *psli;
- volatile uint32_t word0;
void __iomem *to_slim;
- uint32_t hba_aer_enabled;
spin_lock_irq(&phba->hbalock);
- /* Take PCIe device Advanced Error Reporting (AER) state */
- hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED;
-
psli = &phba->sli;
/* Restart HBA */
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"0337 Restart HBA Data: x%x x%x\n",
- phba->pport->port_state, psli->sli_flag);
+ (phba->pport) ? phba->pport->port_state : 0,
+ psli->sli_flag);
- word0 = 0;
- mb = (MAILBOX_t *) &word0;
- mb->mbxCommand = MBX_RESTART;
- mb->mbxHc = 1;
+ mb.word0 = 0;
+ mb.mbxCommand = MBX_RESTART;
+ mb.mbxHc = 1;
lpfc_reset_barrier(phba);
to_slim = phba->MBslimaddr;
- writel(*(uint32_t *) mb, to_slim);
+ writel(mb.word0, to_slim);
readl(to_slim); /* flush */
/* Only skip post after fc_ffinit is completed */
- if (phba->pport->port_state)
- word0 = 1; /* This is really setting up word1 */
+ if (phba->pport && phba->pport->port_state)
+ mb.word0 = 1; /* This is really setting up word1 */
else
- word0 = 0; /* This is really setting up word1 */
+ mb.word0 = 0; /* This is really setting up word1 */
to_slim = phba->MBslimaddr + sizeof (uint32_t);
- writel(*(uint32_t *) mb, to_slim);
+ writel(mb.word0, to_slim);
readl(to_slim); /* flush */
lpfc_sli_brdreset(phba);
- phba->pport->stopped = 0;
+ if (phba->pport)
+ phba->pport->stopped = 0;
phba->link_state = LPFC_INIT_START;
phba->hba_flag = 0;
spin_unlock_irq(&phba->hbalock);
memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
- psli->stats_start = get_seconds();
+ psli->stats_start = ktime_get_seconds();
/* Give the INITFF and Post time to settle. */
mdelay(100);
- /* Reset HBA AER if it was enabled, note hba_flag was reset above */
- if (hba_aer_enabled)
- pci_disable_pcie_error_reporting(phba->pcidev);
-
lpfc_hba_down_post(phba);
return 0;
@@ -3980,34 +5278,38 @@ static int
lpfc_sli_brdrestart_s4(struct lpfc_hba *phba)
{
struct lpfc_sli *psli = &phba->sli;
- uint32_t hba_aer_enabled;
+ int rc;
/* Restart HBA */
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"0296 Restart HBA Data: x%x x%x\n",
phba->pport->port_state, psli->sli_flag);
- /* Take PCIe device Advanced Error Reporting (AER) state */
- hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED;
+ clear_bit(HBA_SETUP, &phba->hba_flag);
+ lpfc_sli4_queue_unset(phba);
- lpfc_sli4_brdreset(phba);
+ rc = lpfc_sli4_brdreset(phba);
+ if (rc) {
+ phba->link_state = LPFC_HBA_ERROR;
+ goto hba_down_queue;
+ }
spin_lock_irq(&phba->hbalock);
phba->pport->stopped = 0;
phba->link_state = LPFC_INIT_START;
phba->hba_flag = 0;
+ /* Preserve FA-PWWN expectation */
+ phba->sli4_hba.fawwpn_flag &= LPFC_FAWWPN_FABRIC;
spin_unlock_irq(&phba->hbalock);
memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
- psli->stats_start = get_seconds();
-
- /* Reset HBA AER if it was enabled, note hba_flag was reset above */
- if (hba_aer_enabled)
- pci_disable_pcie_error_reporting(phba->pcidev);
+ psli->stats_start = ktime_get_seconds();
+hba_down_queue:
lpfc_hba_down_post(phba);
+ lpfc_sli4_queue_destroy(phba);
- return 0;
+ return rc;
}
/**
@@ -4033,7 +5335,7 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
* iteration, the function will restart the HBA again. The function returns
* zero if HBA successfully restarted else returns negative error code.
**/
-static int
+int
lpfc_sli_chipset_init(struct lpfc_hba *phba)
{
uint32_t status, i = 0;
@@ -4057,7 +5359,7 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba)
if (i++ >= 200) {
/* Adapter failed to init, timeout, status reg
<status> */
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0436 Adapter failed to init, "
"timeout, status reg x%x, "
"FW Data: A8 x%x AC x%x\n", status,
@@ -4072,7 +5374,7 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba)
/* ERROR: During chipset initialization */
/* Adapter failed to init, chipset, status reg
<status> */
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0437 Adapter failed to init, "
"chipset, status reg x%x, "
"FW Data: A8 x%x AC x%x\n", status,
@@ -4103,7 +5405,7 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba)
if (status & HS_FFERM) {
/* ERROR: During chipset initialization */
/* Adapter failed to init, chipset, status reg <status> */
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0438 Adapter failed to init, chipset, "
"status reg x%x, "
"FW Data: A8 x%x AC x%x\n", status,
@@ -4113,6 +5415,8 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba)
return -EIO;
}
+ set_bit(HBA_NEEDS_CFG_PORT, &phba->hba_flag);
+
/* Clear all interrupt enable conditions */
writel(0, phba->HCregaddr);
readl(phba->HCregaddr); /* flush */
@@ -4248,10 +5552,20 @@ static int
lpfc_sli4_rb_setup(struct lpfc_hba *phba)
{
phba->hbq_in_use = 1;
- phba->hbqs[0].entry_count = lpfc_hbq_defs[0]->entry_count;
+ /**
+ * Specific case when the MDS diagnostics is enabled and supported.
+ * The receive buffer count is truncated to manage the incoming
+ * traffic.
+ **/
+ if (phba->cfg_enable_mds_diags && phba->mds_diags_support)
+ phba->hbqs[LPFC_ELS_HBQ].entry_count =
+ lpfc_hbq_defs[LPFC_ELS_HBQ]->entry_count >> 1;
+ else
+ phba->hbqs[LPFC_ELS_HBQ].entry_count =
+ lpfc_hbq_defs[LPFC_ELS_HBQ]->entry_count;
phba->hbq_count = 1;
+ lpfc_sli_hbqbuf_init_hbqs(phba, LPFC_ELS_HBQ);
/* Initially populate or replenish the HBQs */
- lpfc_sli_hbqbuf_init_hbqs(phba, 0);
return 0;
}
@@ -4260,7 +5574,7 @@ lpfc_sli4_rb_setup(struct lpfc_hba *phba)
* @phba: Pointer to HBA context object.
* @sli_mode: sli mode - 2/3
*
- * This function is called by the sli intialization code path
+ * This function is called by the sli initialization code path
* to issue config_port mailbox command. This function restarts the
* HBA firmware and issues a config_port mailbox command to configure
* the SLI interface in the sli mode specified by sli_mode
@@ -4314,10 +5628,9 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options &= ~(LPFC_SLI3_NPIV_ENABLED |
LPFC_SLI3_HBQ_ENABLED |
LPFC_SLI3_CRP_ENABLED |
- LPFC_SLI3_BG_ENABLED |
LPFC_SLI3_DSS_ENABLED);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0442 Adapter failed to init, mbxCmd x%x "
"CONFIG_PORT, mbxStatus x%x Data: x%x\n",
pmb->u.mb.mbxCommand, pmb->u.mb.mbxStatus, 0);
@@ -4331,6 +5644,11 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli.sli_flag &= ~LPFC_SLI_ASYNC_MBX_BLK;
spin_unlock_irq(&phba->hbalock);
done = 1;
+
+ if ((pmb->u.mb.un.varCfgPort.casabt == 1) &&
+ (pmb->u.mb.un.varCfgPort.gasabt == 0))
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "3110 Port did not grant ASABT\n");
}
}
if (!done) {
@@ -4350,23 +5668,6 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
} else
phba->max_vpi = 0;
- phba->fips_level = 0;
- phba->fips_spec_rev = 0;
- if (pmb->u.mb.un.varCfgPort.gdss) {
- phba->sli3_options |= LPFC_SLI3_DSS_ENABLED;
- phba->fips_level = pmb->u.mb.un.varCfgPort.fips_level;
- phba->fips_spec_rev = pmb->u.mb.un.varCfgPort.fips_rev;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2850 Security Crypto Active. FIPS x%d "
- "(Spec Rev: x%d)",
- phba->fips_level, phba->fips_spec_rev);
- }
- if (pmb->u.mb.un.varCfgPort.sec_err) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2856 Config Port Security Crypto "
- "Error: x%x ",
- pmb->u.mb.un.varCfgPort.sec_err);
- }
if (pmb->u.mb.un.varCfgPort.gerbm)
phba->sli3_options |= LPFC_SLI3_HBQ_ENABLED;
if (pmb->u.mb.un.varCfgPort.gcrp)
@@ -4375,13 +5676,14 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_pgp.port;
- if (phba->cfg_enable_bg) {
- if (pmb->u.mb.un.varCfgPort.gbg)
- phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
- else
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ if (pmb->u.mb.un.varCfgPort.gbg == 0) {
+ phba->cfg_enable_bg = 0;
+ phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0443 Adapter did not grant "
"BlockGuard\n");
+ }
}
} else {
phba->hbq_get = NULL;
@@ -4395,11 +5697,11 @@ do_prep_failed:
/**
- * lpfc_sli_hba_setup - SLI intialization function
+ * lpfc_sli_hba_setup - SLI initialization function
* @phba: Pointer to HBA context object.
*
- * This function is the main SLI intialization function. This function
- * is called by the HBA intialization code, HBA reset code and HBA
+ * This function is the main SLI initialization function. This function
+ * is called by the HBA initialization code, HBA reset code and HBA
* error attention handler code. Caller is not required to hold any
* locks. This function issues config_port mailbox command to configure
* the SLI, setup iocb rings and HBQ rings. In the end the function
@@ -4411,59 +5713,17 @@ int
lpfc_sli_hba_setup(struct lpfc_hba *phba)
{
uint32_t rc;
- int mode = 3, i;
+ int i;
int longs;
- switch (lpfc_sli_mode) {
- case 2:
- if (phba->cfg_enable_npiv) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_VPORT,
- "1824 NPIV enabled: Override lpfc_sli_mode "
- "parameter (%d) to auto (0).\n",
- lpfc_sli_mode);
- break;
- }
- mode = 2;
- break;
- case 0:
- case 3:
- break;
- default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_VPORT,
- "1819 Unrecognized lpfc_sli_mode "
- "parameter: %d.\n", lpfc_sli_mode);
-
- break;
- }
-
- rc = lpfc_sli_config_port(phba, mode);
-
- if (rc && lpfc_sli_mode == 3)
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_VPORT,
- "1820 Unable to select SLI-3. "
- "Not supported by adapter.\n");
- if (rc && mode != 2)
- rc = lpfc_sli_config_port(phba, 2);
- if (rc)
- goto lpfc_sli_hba_setup_error;
-
- /* Enable PCIe device Advanced Error Reporting (AER) if configured */
- if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) {
- rc = pci_enable_pcie_error_reporting(phba->pcidev);
- if (!rc) {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2709 This device supports "
- "Advanced Error Reporting (AER)\n");
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag |= HBA_AER_ENABLED;
- spin_unlock_irq(&phba->hbalock);
- } else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2708 This device does not support "
- "Advanced Error Reporting (AER)\n");
- phba->cfg_aer_support = 0;
- }
+ /* Enable ISR already does config_port because of config_msi mbx */
+ if (test_bit(HBA_NEEDS_CFG_PORT, &phba->hba_flag)) {
+ rc = lpfc_sli_config_port(phba, LPFC_SLI_REV3);
+ if (rc)
+ return -EIO;
+ clear_bit(HBA_NEEDS_CFG_PORT, &phba->hba_flag);
}
+ phba->fcp_embed_io = 0; /* SLI4 FC support only */
if (phba->sli_rev == 3) {
phba->iocb_cmd_size = SLI3_IOCB_CMD_SIZE;
@@ -4491,16 +5751,17 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
*/
if ((phba->vpi_bmask == NULL) && (phba->vpi_ids == NULL)) {
longs = (phba->max_vpi + BITS_PER_LONG) / BITS_PER_LONG;
- phba->vpi_bmask = kzalloc(longs * sizeof(unsigned long),
+ phba->vpi_bmask = kcalloc(longs,
+ sizeof(unsigned long),
GFP_KERNEL);
if (!phba->vpi_bmask) {
rc = -ENOMEM;
goto lpfc_sli_hba_setup_error;
}
- phba->vpi_ids = kzalloc(
- (phba->max_vpi+1) * sizeof(uint16_t),
- GFP_KERNEL);
+ phba->vpi_ids = kcalloc(phba->max_vpi + 1,
+ sizeof(uint16_t),
+ GFP_KERNEL);
if (!phba->vpi_ids) {
kfree(phba->vpi_bmask);
rc = -ENOMEM;
@@ -4529,7 +5790,7 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
lpfc_sli_hba_setup_error:
phba->link_state = LPFC_HBA_ERROR;
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0445 Firmware initialization failed\n");
return rc;
}
@@ -4537,15 +5798,15 @@ lpfc_sli_hba_setup_error:
/**
* lpfc_sli4_read_fcoe_params - Read fcoe params from conf region
* @phba: Pointer to HBA context object.
- * @mboxq: mailbox pointer.
+ *
* This function issue a dump mailbox command to read config region
* 23 and parse the records in the region and populate driver
* data structure.
**/
static int
-lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
- LPFC_MBOXQ_t *mboxq)
+lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba)
{
+ LPFC_MBOXQ_t *mboxq;
struct lpfc_dmabuf *mp;
struct lpfc_mqe *mqe;
uint32_t data_length;
@@ -4557,11 +5818,17 @@ lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
- mqe = &mboxq->u.mqe;
- if (lpfc_dump_fcoe_param(phba, mboxq))
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
return -ENOMEM;
- mp = (struct lpfc_dmabuf *) mboxq->context1;
+ mqe = &mboxq->u.mqe;
+ if (lpfc_sli4_dump_cfg_rg23(phba, mboxq)) {
+ rc = -ENOMEM;
+ goto out_free_mboxq;
+ }
+
+ mp = mboxq->ctx_buf;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
@@ -4586,21 +5853,21 @@ lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
mboxq->mcqe.trailer);
if (rc) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- return -EIO;
+ rc = -EIO;
+ goto out_free_mboxq;
}
data_length = mqe->un.mb_words[5];
if (data_length > DMP_RGN23_SIZE) {
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- return -EIO;
+ rc = -EIO;
+ goto out_free_mboxq;
}
lpfc_parse_fcoe_conf(phba, mp->virt, data_length);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- return 0;
+ rc = 0;
+
+out_free_mboxq:
+ lpfc_mbox_rsrc_cleanup(phba, mboxq, MBOX_THD_UNLOCKED);
+ return rc;
}
/**
@@ -4636,15 +5903,12 @@ lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
* mailbox command.
*/
dma_size = *vpd_size;
- dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
- dma_size,
- &dmabuf->phys,
- GFP_KERNEL);
+ dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev, dma_size,
+ &dmabuf->phys, GFP_KERNEL);
if (!dmabuf->virt) {
kfree(dmabuf);
return -ENOMEM;
}
- memset(dmabuf->virt, 0, dma_size);
/*
* The SLI4 implementation of READ_REV conflicts at word1,
@@ -4684,6 +5948,201 @@ lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
}
/**
+ * lpfc_sli4_get_ctl_attr - Retrieve SLI4 device controller attributes
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine retrieves SLI4 device physical port name this PCI function
+ * is attached to.
+ *
+ * Return codes
+ * 0 - successful
+ * otherwise - failed to retrieve controller attributes
+ **/
+static int
+lpfc_sli4_get_ctl_attr(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mbx_get_cntl_attributes *mbx_cntl_attr;
+ struct lpfc_controller_attribute *cntl_attr;
+ void *virtaddr = NULL;
+ uint32_t alloclen, reqlen;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ int rc;
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+
+ /* Send COMMON_GET_CNTL_ATTRIBUTES mbox cmd */
+ reqlen = sizeof(struct lpfc_mbx_get_cntl_attributes);
+ alloclen = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_GET_CNTL_ATTRIBUTES, reqlen,
+ LPFC_SLI4_MBX_NEMBED);
+
+ if (alloclen < reqlen) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3084 Allocated DMA memory size (%d) is "
+ "less than the requested DMA memory size "
+ "(%d)\n", alloclen, reqlen);
+ rc = -ENOMEM;
+ goto out_free_mboxq;
+ }
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ virtaddr = mboxq->sge_array->addr[0];
+ mbx_cntl_attr = (struct lpfc_mbx_get_cntl_attributes *)virtaddr;
+ shdr = &mbx_cntl_attr->cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3085 Mailbox x%x (x%x/x%x) failed, "
+ "rc:x%x, status:x%x, add_status:x%x\n",
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
+ rc, shdr_status, shdr_add_status);
+ rc = -ENXIO;
+ goto out_free_mboxq;
+ }
+
+ cntl_attr = &mbx_cntl_attr->cntl_attr;
+ phba->sli4_hba.lnk_info.lnk_dv = LPFC_LNK_DAT_VAL;
+ phba->sli4_hba.lnk_info.lnk_tp =
+ bf_get(lpfc_cntl_attr_lnk_type, cntl_attr);
+ phba->sli4_hba.lnk_info.lnk_no =
+ bf_get(lpfc_cntl_attr_lnk_numb, cntl_attr);
+ phba->sli4_hba.flash_id = bf_get(lpfc_cntl_attr_flash_id, cntl_attr);
+ phba->sli4_hba.asic_rev = bf_get(lpfc_cntl_attr_asic_rev, cntl_attr);
+
+ memcpy(phba->BIOSVersion, cntl_attr->bios_ver_str,
+ sizeof(phba->BIOSVersion));
+ phba->BIOSVersion[sizeof(phba->BIOSVersion) - 1] = '\0';
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3086 lnk_type:%d, lnk_numb:%d, bios_ver:%s, "
+ "flash_id: x%02x, asic_rev: x%02x\n",
+ phba->sli4_hba.lnk_info.lnk_tp,
+ phba->sli4_hba.lnk_info.lnk_no,
+ phba->BIOSVersion, phba->sli4_hba.flash_id,
+ phba->sli4_hba.asic_rev);
+out_free_mboxq:
+ if (bf_get(lpfc_mqe_command, &mboxq->u.mqe) == MBX_SLI4_CONFIG)
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ else
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return rc;
+}
+
+/**
+ * lpfc_sli4_retrieve_pport_name - Retrieve SLI4 device physical port name
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine retrieves SLI4 device physical port name this PCI function
+ * is attached to.
+ *
+ * Return codes
+ * 0 - successful
+ * otherwise - failed to retrieve physical port name
+ **/
+static int
+lpfc_sli4_retrieve_pport_name(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mbx_get_port_name *get_port_name;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ char cport_name = 0;
+ int rc;
+
+ /* We assume nothing at this point */
+ phba->sli4_hba.lnk_info.lnk_dv = LPFC_LNK_DAT_INVAL;
+ phba->sli4_hba.pport_name_sta = LPFC_SLI4_PPNAME_NON;
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+ /* obtain link type and link number via READ_CONFIG */
+ phba->sli4_hba.lnk_info.lnk_dv = LPFC_LNK_DAT_INVAL;
+ lpfc_sli4_read_config(phba);
+
+ if (phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_CONFIG)
+ phba->sli4_hba.fawwpn_flag |= LPFC_FAWWPN_FABRIC;
+
+ if (phba->sli4_hba.lnk_info.lnk_dv == LPFC_LNK_DAT_VAL)
+ goto retrieve_ppname;
+
+ /* obtain link type and link number via COMMON_GET_CNTL_ATTRIBUTES */
+ rc = lpfc_sli4_get_ctl_attr(phba);
+ if (rc)
+ goto out_free_mboxq;
+
+retrieve_ppname:
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_GET_PORT_NAME,
+ sizeof(struct lpfc_mbx_get_port_name) -
+ sizeof(struct lpfc_sli4_cfg_mhdr),
+ LPFC_SLI4_MBX_EMBED);
+ get_port_name = &mboxq->u.mqe.un.get_port_name;
+ shdr = (union lpfc_sli4_cfg_shdr *)&get_port_name->header.cfg_shdr;
+ bf_set(lpfc_mbox_hdr_version, &shdr->request, LPFC_OPCODE_VERSION_1);
+ bf_set(lpfc_mbx_get_port_name_lnk_type, &get_port_name->u.request,
+ phba->sli4_hba.lnk_info.lnk_tp);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3087 Mailbox x%x (x%x/x%x) failed: "
+ "rc:x%x, status:x%x, add_status:x%x\n",
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
+ rc, shdr_status, shdr_add_status);
+ rc = -ENXIO;
+ goto out_free_mboxq;
+ }
+ switch (phba->sli4_hba.lnk_info.lnk_no) {
+ case LPFC_LINK_NUMBER_0:
+ cport_name = bf_get(lpfc_mbx_get_port_name_name0,
+ &get_port_name->u.response);
+ phba->sli4_hba.pport_name_sta = LPFC_SLI4_PPNAME_GET;
+ break;
+ case LPFC_LINK_NUMBER_1:
+ cport_name = bf_get(lpfc_mbx_get_port_name_name1,
+ &get_port_name->u.response);
+ phba->sli4_hba.pport_name_sta = LPFC_SLI4_PPNAME_GET;
+ break;
+ case LPFC_LINK_NUMBER_2:
+ cport_name = bf_get(lpfc_mbx_get_port_name_name2,
+ &get_port_name->u.response);
+ phba->sli4_hba.pport_name_sta = LPFC_SLI4_PPNAME_GET;
+ break;
+ case LPFC_LINK_NUMBER_3:
+ cport_name = bf_get(lpfc_mbx_get_port_name_name3,
+ &get_port_name->u.response);
+ phba->sli4_hba.pport_name_sta = LPFC_SLI4_PPNAME_GET;
+ break;
+ default:
+ break;
+ }
+
+ if (phba->sli4_hba.pport_name_sta == LPFC_SLI4_PPNAME_GET) {
+ phba->Port[0] = cport_name;
+ phba->Port[1] = '\0';
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3091 SLI get port name: %s\n", phba->Port);
+ }
+
+out_free_mboxq:
+ if (bf_get(lpfc_mqe_command, &mboxq->u.mqe) == MBX_SLI4_CONFIG)
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ else
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return rc;
+}
+
+/**
* lpfc_sli4_arm_cqeq_intr - Arm sli-4 device completion and event queues
* @phba: pointer to lpfc hba data structure.
*
@@ -4693,29 +6152,57 @@ lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
static void
lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
{
- uint8_t fcp_eqidx;
+ int qidx;
+ struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_queue *eq;
+
+ sli4_hba->sli4_write_cq_db(phba, sli4_hba->mbx_cq, 0, LPFC_QUEUE_REARM);
+ sli4_hba->sli4_write_cq_db(phba, sli4_hba->els_cq, 0, LPFC_QUEUE_REARM);
+ if (sli4_hba->nvmels_cq)
+ sli4_hba->sli4_write_cq_db(phba, sli4_hba->nvmels_cq, 0,
+ LPFC_QUEUE_REARM);
+
+ if (sli4_hba->hdwq) {
+ /* Loop thru all Hardware Queues */
+ for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) {
+ qp = &sli4_hba->hdwq[qidx];
+ /* ARM the corresponding CQ */
+ sli4_hba->sli4_write_cq_db(phba, qp->io_cq, 0,
+ LPFC_QUEUE_REARM);
+ }
- lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM);
- lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM);
- fcp_eqidx = 0;
- do
- lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx],
- LPFC_QUEUE_REARM);
- while (++fcp_eqidx < phba->cfg_fcp_eq_count);
- lpfc_sli4_eq_release(phba->sli4_hba.sp_eq, LPFC_QUEUE_REARM);
- for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++)
- lpfc_sli4_eq_release(phba->sli4_hba.fp_eq[fcp_eqidx],
- LPFC_QUEUE_REARM);
+ /* Loop thru all IRQ vectors */
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
+ eq = sli4_hba->hba_eq_hdl[qidx].eq;
+ /* ARM the corresponding EQ */
+ sli4_hba->sli4_write_eq_db(phba, eq,
+ 0, LPFC_QUEUE_REARM);
+ }
+ }
+
+ if (phba->nvmet_support) {
+ for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) {
+ sli4_hba->sli4_write_cq_db(phba,
+ sli4_hba->nvmet_cqset[qidx], 0,
+ LPFC_QUEUE_REARM);
+ }
+ }
}
/**
* lpfc_sli4_get_avail_extnt_rsrc - Get available resource extent count.
* @phba: Pointer to HBA context object.
* @type: The resource extent type.
+ * @extnt_count: buffer to hold port available extent count.
+ * @extnt_size: buffer to hold element count per extent.
*
- * This function allocates all SLI4 resource identifiers.
+ * This function calls the port and retrievs the number of available
+ * extents and their size for a particular extent type.
+ *
+ * Returns: 0 if successful. Nonzero otherwise.
**/
-static int
+int
lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type,
uint16_t *extnt_count, uint16_t *extnt_size)
{
@@ -4725,6 +6212,9 @@ lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type,
struct lpfc_mbx_get_rsrc_extent_info *rsrc_info;
LPFC_MBOXQ_t *mbox;
+ *extnt_count = 0;
+ *extnt_size = 0;
+
mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -4747,7 +6237,7 @@ lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type,
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
if (unlikely(rc)) {
@@ -4758,7 +6248,7 @@ lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type,
rsrc_info = &mbox->u.mqe.un.rsrc_extent_info;
if (bf_get(lpfc_mbox_hdr_status,
&rsrc_info->header.cfg_shdr.response)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2930 Failed to get resource extents "
"Status 0x%x Add'l Status 0x%x\n",
bf_get(lpfc_mbox_hdr_status,
@@ -4773,7 +6263,12 @@ lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type,
&rsrc_info->u.rsp);
*extnt_size = bf_get(lpfc_mbx_get_rsrc_extent_info_size,
&rsrc_info->u.rsp);
- err_exit:
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3162 Retrieved extents type-%d from port: count:%d, "
+ "size:%d\n", type, *extnt_count, *extnt_size);
+
+err_exit:
mempool_free(mbox, phba->mbox_mem_pool);
return rc;
}
@@ -4842,10 +6337,10 @@ lpfc_sli4_chk_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type)
/**
* lpfc_sli4_cfg_post_extnts -
* @phba: Pointer to HBA context object.
- * @extnt_cnt - number of available extents.
- * @type - the extent type (rpi, xri, vfi, vpi).
- * @emb - buffer to hold either MBX_EMBED or MBX_NEMBED operation.
- * @mbox - pointer to the caller's allocated mailbox structure.
+ * @extnt_cnt: number of available extents.
+ * @type: the extent type (rpi, xri, vfi, vpi).
+ * @emb: buffer to hold either MBX_EMBED or MBX_NEMBED operation.
+ * @mbox: pointer to the caller's allocated mailbox structure.
*
* This function executes the extents allocation request. It also
* takes care of the amount of memory needed to allocate or get the
@@ -4857,7 +6352,7 @@ lpfc_sli4_chk_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type)
* 0: if successful
**/
static int
-lpfc_sli4_cfg_post_extnts(struct lpfc_hba *phba, uint16_t *extnt_cnt,
+lpfc_sli4_cfg_post_extnts(struct lpfc_hba *phba, uint16_t extnt_cnt,
uint16_t type, bool *emb, LPFC_MBOXQ_t *mbox)
{
int rc = 0;
@@ -4866,7 +6361,7 @@ lpfc_sli4_cfg_post_extnts(struct lpfc_hba *phba, uint16_t *extnt_cnt,
uint32_t alloc_len, mbox_tmo;
/* Calculate the total requested length of the dma memory */
- req_len = *extnt_cnt * sizeof(uint16_t);
+ req_len = extnt_cnt * sizeof(uint16_t);
/*
* Calculate the size of an embedded mailbox. The uint32_t
@@ -4881,7 +6376,7 @@ lpfc_sli4_cfg_post_extnts(struct lpfc_hba *phba, uint16_t *extnt_cnt,
*/
*emb = LPFC_SLI4_MBX_EMBED;
if (req_len > emb_len) {
- req_len = *extnt_cnt * sizeof(uint16_t) +
+ req_len = extnt_cnt * sizeof(uint16_t) +
sizeof(union lpfc_sli4_cfg_shdr) +
sizeof(uint32_t);
*emb = LPFC_SLI4_MBX_NEMBED;
@@ -4891,20 +6386,20 @@ lpfc_sli4_cfg_post_extnts(struct lpfc_hba *phba, uint16_t *extnt_cnt,
LPFC_MBOX_OPCODE_ALLOC_RSRC_EXTENT,
req_len, *emb);
if (alloc_len < req_len) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "9000 Allocated DMA memory size (x%x) is "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2982 Allocated DMA memory size (x%x) is "
"less than the requested DMA memory "
"size (x%x)\n", alloc_len, req_len);
return -ENOMEM;
}
- rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, *extnt_cnt, type, *emb);
+ rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, extnt_cnt, type, *emb);
if (unlikely(rc))
return -EIO;
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
@@ -4947,7 +6442,7 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
return -EIO;
if ((rsrc_cnt == 0) || (rsrc_size == 0)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"3009 No available Resource Extents "
"for resource type 0x%x: Count: 0x%x, "
"Size 0x%x\n", type, rsrc_cnt,
@@ -4955,17 +6450,15 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
return -ENOMEM;
}
- lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_INIT,
- "2903 Available Resource Extents "
- "for resource type 0x%x: Count: 0x%x, "
- "Size 0x%x\n", type, rsrc_cnt,
- rsrc_size);
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_INIT | LOG_SLI,
+ "2903 Post resource extents type-0x%x: "
+ "count:%d, size %d\n", type, rsrc_cnt, rsrc_size);
mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
- rc = lpfc_sli4_cfg_post_extnts(phba, &rsrc_cnt, type, &emb, mbox);
+ rc = lpfc_sli4_cfg_post_extnts(phba, rsrc_cnt, type, &emb, mbox);
if (unlikely(rc)) {
rc = -EIO;
goto err_exit;
@@ -4998,14 +6491,14 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
length = sizeof(struct lpfc_rsrc_blks);
switch (type) {
case LPFC_RSC_TYPE_FCOE_RPI:
- phba->sli4_hba.rpi_bmask = kzalloc(longs *
+ phba->sli4_hba.rpi_bmask = kcalloc(longs,
sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.rpi_bmask)) {
rc = -ENOMEM;
goto err_exit;
}
- phba->sli4_hba.rpi_ids = kzalloc(rsrc_id_cnt *
+ phba->sli4_hba.rpi_ids = kcalloc(rsrc_id_cnt,
sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.rpi_ids)) {
@@ -5027,15 +6520,13 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
ext_blk_list = &phba->sli4_hba.lpfc_rpi_blk_list;
break;
case LPFC_RSC_TYPE_FCOE_VPI:
- phba->vpi_bmask = kzalloc(longs *
- sizeof(unsigned long),
+ phba->vpi_bmask = kcalloc(longs, sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->vpi_bmask)) {
rc = -ENOMEM;
goto err_exit;
}
- phba->vpi_ids = kzalloc(rsrc_id_cnt *
- sizeof(uint16_t),
+ phba->vpi_ids = kcalloc(rsrc_id_cnt, sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->vpi_ids)) {
kfree(phba->vpi_bmask);
@@ -5049,14 +6540,15 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
ext_blk_list = &phba->lpfc_vpi_blk_list;
break;
case LPFC_RSC_TYPE_FCOE_XRI:
- phba->sli4_hba.xri_bmask = kzalloc(longs *
+ phba->sli4_hba.xri_bmask = kcalloc(longs,
sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.xri_bmask)) {
rc = -ENOMEM;
goto err_exit;
}
- phba->sli4_hba.xri_ids = kzalloc(rsrc_id_cnt *
+ phba->sli4_hba.max_cfg_param.xri_used = 0;
+ phba->sli4_hba.xri_ids = kcalloc(rsrc_id_cnt,
sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.xri_ids)) {
@@ -5071,14 +6563,14 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
ext_blk_list = &phba->sli4_hba.lpfc_xri_blk_list;
break;
case LPFC_RSC_TYPE_FCOE_VFI:
- phba->sli4_hba.vfi_bmask = kzalloc(longs *
+ phba->sli4_hba.vfi_bmask = kcalloc(longs,
sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.vfi_bmask)) {
rc = -ENOMEM;
goto err_exit;
}
- phba->sli4_hba.vfi_ids = kzalloc(rsrc_id_cnt *
+ phba->sli4_hba.vfi_ids = kcalloc(rsrc_id_cnt,
sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.vfi_ids)) {
@@ -5126,9 +6618,10 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
rsrc_blks->rsrc_size = rsrc_size;
list_add_tail(&rsrc_blks->list, ext_blk_list);
rsrc_start = rsrc_id;
- if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0))
- phba->sli4_hba.scsi_xri_start = rsrc_start +
- lpfc_sli4_get_els_iocb_cnt(phba);
+ if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0)) {
+ phba->sli4_hba.io_xri_start = rsrc_start +
+ lpfc_sli4_get_iocb_cnt(phba);
+ }
while (rsrc_id < (rsrc_start + rsrc_size)) {
ids[j] = rsrc_id;
@@ -5144,6 +6637,8 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type)
return rc;
}
+
+
/**
* lpfc_sli4_dealloc_extent - Deallocate an SLI4 resource extent.
* @phba: Pointer to HBA context object.
@@ -5187,7 +6682,7 @@ lpfc_sli4_dealloc_extent(struct lpfc_hba *phba, uint16_t type)
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, mbox_tmo);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
if (unlikely(rc)) {
@@ -5198,7 +6693,7 @@ lpfc_sli4_dealloc_extent(struct lpfc_hba *phba, uint16_t type)
dealloc_rsrc = &mbox->u.mqe.un.dealloc_rsrc_extents;
if (bf_get(lpfc_mbox_hdr_status,
&dealloc_rsrc->header.cfg_shdr.response)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2919 Failed to release resource extents "
"for type %d - Status 0x%x Add'l Status 0x%x. "
"Resource memory not released.\n",
@@ -5222,11 +6717,11 @@ lpfc_sli4_dealloc_extent(struct lpfc_hba *phba, uint16_t type)
list_del_init(&rsrc_blk->list);
kfree(rsrc_blk);
}
+ phba->sli4_hba.max_cfg_param.vpi_used = 0;
break;
case LPFC_RSC_TYPE_FCOE_XRI:
kfree(phba->sli4_hba.xri_bmask);
kfree(phba->sli4_hba.xri_ids);
- bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0);
list_for_each_entry_safe(rsrc_blk, rsrc_blk_next,
&phba->sli4_hba.lpfc_xri_blk_list, list) {
list_del_init(&rsrc_blk->list);
@@ -5262,6 +6757,395 @@ lpfc_sli4_dealloc_extent(struct lpfc_hba *phba, uint16_t type)
return rc;
}
+static void
+lpfc_set_features(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox,
+ uint32_t feature)
+{
+ uint32_t len;
+ u32 sig_freq = 0;
+
+ len = sizeof(struct lpfc_mbx_set_feature) -
+ sizeof(struct lpfc_sli4_cfg_mhdr);
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_SET_FEATURES, len,
+ LPFC_SLI4_MBX_EMBED);
+
+ switch (feature) {
+ case LPFC_SET_UE_RECOVERY:
+ bf_set(lpfc_mbx_set_feature_UER,
+ &mbox->u.mqe.un.set_feature, 1);
+ mbox->u.mqe.un.set_feature.feature = LPFC_SET_UE_RECOVERY;
+ mbox->u.mqe.un.set_feature.param_len = 8;
+ break;
+ case LPFC_SET_MDS_DIAGS:
+ bf_set(lpfc_mbx_set_feature_mds,
+ &mbox->u.mqe.un.set_feature, 1);
+ bf_set(lpfc_mbx_set_feature_mds_deep_loopbk,
+ &mbox->u.mqe.un.set_feature, 1);
+ mbox->u.mqe.un.set_feature.feature = LPFC_SET_MDS_DIAGS;
+ mbox->u.mqe.un.set_feature.param_len = 8;
+ break;
+ case LPFC_SET_CGN_SIGNAL:
+ if (phba->cmf_active_mode == LPFC_CFG_OFF)
+ sig_freq = 0;
+ else
+ sig_freq = phba->cgn_sig_freq;
+
+ if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) {
+ bf_set(lpfc_mbx_set_feature_CGN_alarm_freq,
+ &mbox->u.mqe.un.set_feature, sig_freq);
+ bf_set(lpfc_mbx_set_feature_CGN_warn_freq,
+ &mbox->u.mqe.un.set_feature, sig_freq);
+ }
+
+ if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ONLY)
+ bf_set(lpfc_mbx_set_feature_CGN_warn_freq,
+ &mbox->u.mqe.un.set_feature, sig_freq);
+
+ if (phba->cmf_active_mode == LPFC_CFG_OFF ||
+ phba->cgn_reg_signal == EDC_CG_SIG_NOTSUPPORTED)
+ sig_freq = 0;
+ else
+ sig_freq = lpfc_acqe_cgn_frequency;
+
+ bf_set(lpfc_mbx_set_feature_CGN_acqe_freq,
+ &mbox->u.mqe.un.set_feature, sig_freq);
+
+ mbox->u.mqe.un.set_feature.feature = LPFC_SET_CGN_SIGNAL;
+ mbox->u.mqe.un.set_feature.param_len = 12;
+ break;
+ case LPFC_SET_DUAL_DUMP:
+ bf_set(lpfc_mbx_set_feature_dd,
+ &mbox->u.mqe.un.set_feature, LPFC_ENABLE_DUAL_DUMP);
+ bf_set(lpfc_mbx_set_feature_ddquery,
+ &mbox->u.mqe.un.set_feature, 0);
+ mbox->u.mqe.un.set_feature.feature = LPFC_SET_DUAL_DUMP;
+ mbox->u.mqe.un.set_feature.param_len = 4;
+ break;
+ case LPFC_SET_ENABLE_MI:
+ mbox->u.mqe.un.set_feature.feature = LPFC_SET_ENABLE_MI;
+ mbox->u.mqe.un.set_feature.param_len = 4;
+ bf_set(lpfc_mbx_set_feature_milunq, &mbox->u.mqe.un.set_feature,
+ phba->pport->cfg_lun_queue_depth);
+ bf_set(lpfc_mbx_set_feature_mi, &mbox->u.mqe.un.set_feature,
+ phba->sli4_hba.pc_sli4_params.mi_ver);
+ break;
+ case LPFC_SET_LD_SIGNAL:
+ mbox->u.mqe.un.set_feature.feature = LPFC_SET_LD_SIGNAL;
+ mbox->u.mqe.un.set_feature.param_len = 16;
+ bf_set(lpfc_mbx_set_feature_lds_qry,
+ &mbox->u.mqe.un.set_feature, LPFC_QUERY_LDS_OP);
+ break;
+ case LPFC_SET_ENABLE_CMF:
+ mbox->u.mqe.un.set_feature.feature = LPFC_SET_ENABLE_CMF;
+ mbox->u.mqe.un.set_feature.param_len = 4;
+ bf_set(lpfc_mbx_set_feature_cmf,
+ &mbox->u.mqe.un.set_feature, 1);
+ break;
+ }
+ return;
+}
+
+/**
+ * lpfc_ras_stop_fwlog: Disable FW logging by the adapter
+ * @phba: Pointer to HBA context object.
+ *
+ * Disable FW logging into host memory on the adapter. To
+ * be done before reading logs from the host memory.
+ **/
+void
+lpfc_ras_stop_fwlog(struct lpfc_hba *phba)
+{
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
+
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ ras_fwlog->state = INACTIVE;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+
+ /* Disable FW logging to host memory */
+ writel(LPFC_CTL_PDEV_CTL_DDL_RAS,
+ phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET);
+
+ /* Wait 10ms for firmware to stop using DMA buffer */
+ usleep_range(10 * 1000, 20 * 1000);
+}
+
+/**
+ * lpfc_sli4_ras_dma_free - Free memory allocated for FW logging.
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called to free memory allocated for RAS FW logging
+ * support in the driver.
+ **/
+void
+lpfc_sli4_ras_dma_free(struct lpfc_hba *phba)
+{
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
+ struct lpfc_dmabuf *dmabuf, *next;
+
+ if (!list_empty(&ras_fwlog->fwlog_buff_list)) {
+ list_for_each_entry_safe(dmabuf, next,
+ &ras_fwlog->fwlog_buff_list,
+ list) {
+ list_del(&dmabuf->list);
+ dma_free_coherent(&phba->pcidev->dev,
+ LPFC_RAS_MAX_ENTRY_SIZE,
+ dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
+ }
+ }
+
+ if (ras_fwlog->lwpd.virt) {
+ dma_free_coherent(&phba->pcidev->dev,
+ sizeof(uint32_t) * 2,
+ ras_fwlog->lwpd.virt,
+ ras_fwlog->lwpd.phys);
+ ras_fwlog->lwpd.virt = NULL;
+ }
+
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ ras_fwlog->state = INACTIVE;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+}
+
+/**
+ * lpfc_sli4_ras_dma_alloc: Allocate memory for FW support
+ * @phba: Pointer to HBA context object.
+ * @fwlog_buff_count: Count of buffers to be created.
+ *
+ * This routine DMA memory for Log Write Position Data[LPWD] and buffer
+ * to update FW log is posted to the adapter.
+ * Buffer count is calculated based on module param ras_fwlog_buffsize
+ * Size of each buffer posted to FW is 64K.
+ **/
+
+static int
+lpfc_sli4_ras_dma_alloc(struct lpfc_hba *phba,
+ uint32_t fwlog_buff_count)
+{
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
+ struct lpfc_dmabuf *dmabuf;
+ int rc = 0, i = 0;
+
+ /* Initialize List */
+ INIT_LIST_HEAD(&ras_fwlog->fwlog_buff_list);
+
+ /* Allocate memory for the LWPD */
+ ras_fwlog->lwpd.virt = dma_alloc_coherent(&phba->pcidev->dev,
+ sizeof(uint32_t) * 2,
+ &ras_fwlog->lwpd.phys,
+ GFP_KERNEL);
+ if (!ras_fwlog->lwpd.virt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6185 LWPD Memory Alloc Failed\n");
+
+ return -ENOMEM;
+ }
+
+ ras_fwlog->fw_buffcount = fwlog_buff_count;
+ for (i = 0; i < ras_fwlog->fw_buffcount; i++) {
+ dmabuf = kzalloc(sizeof(struct lpfc_dmabuf),
+ GFP_KERNEL);
+ if (!dmabuf) {
+ rc = -ENOMEM;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "6186 Memory Alloc failed FW logging");
+ goto free_mem;
+ }
+
+ dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+ LPFC_RAS_MAX_ENTRY_SIZE,
+ &dmabuf->phys, GFP_KERNEL);
+ if (!dmabuf->virt) {
+ kfree(dmabuf);
+ rc = -ENOMEM;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "6187 DMA Alloc Failed FW logging");
+ goto free_mem;
+ }
+ dmabuf->buffer_tag = i;
+ list_add_tail(&dmabuf->list, &ras_fwlog->fwlog_buff_list);
+ }
+
+free_mem:
+ if (rc)
+ lpfc_sli4_ras_dma_free(phba);
+
+ return rc;
+}
+
+/**
+ * lpfc_sli4_ras_mbox_cmpl: Completion handler for RAS MBX command
+ * @phba: pointer to lpfc hba data structure.
+ * @pmb: pointer to the driver internal queue element for mailbox command.
+ *
+ * Completion handler for driver's RAS MBX command to the device.
+ **/
+static void
+lpfc_sli4_ras_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ MAILBOX_t *mb;
+ union lpfc_sli4_cfg_shdr *shdr;
+ uint32_t shdr_status, shdr_add_status;
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
+
+ mb = &pmb->u.mb;
+
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &pmb->u.mqe.un.ras_fwlog.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+
+ if (mb->mbxStatus != MBX_SUCCESS || shdr_status) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6188 FW LOG mailbox "
+ "completed with status x%x add_status x%x,"
+ " mbx status x%x\n",
+ shdr_status, shdr_add_status, mb->mbxStatus);
+
+ ras_fwlog->ras_hwsupport = false;
+ goto disable_ras;
+ }
+
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ ras_fwlog->state = ACTIVE;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+ mempool_free(pmb, phba->mbox_mem_pool);
+
+ return;
+
+disable_ras:
+ /* Free RAS DMA memory */
+ lpfc_sli4_ras_dma_free(phba);
+ mempool_free(pmb, phba->mbox_mem_pool);
+}
+
+/**
+ * lpfc_sli4_ras_fwlog_init: Initialize memory and post RAS MBX command
+ * @phba: pointer to lpfc hba data structure.
+ * @fwlog_level: Logging verbosity level.
+ * @fwlog_enable: Enable/Disable logging.
+ *
+ * Initialize memory and post mailbox command to enable FW logging in host
+ * memory.
+ **/
+int
+lpfc_sli4_ras_fwlog_init(struct lpfc_hba *phba,
+ uint32_t fwlog_level,
+ uint32_t fwlog_enable)
+{
+ struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
+ struct lpfc_mbx_set_ras_fwlog *mbx_fwlog = NULL;
+ struct lpfc_dmabuf *dmabuf;
+ LPFC_MBOXQ_t *mbox;
+ uint32_t len = 0, fwlog_buffsize, fwlog_entry_count;
+ int rc = 0;
+
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ ras_fwlog->state = INACTIVE;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+
+ fwlog_buffsize = (LPFC_RAS_MIN_BUFF_POST_SIZE *
+ phba->cfg_ras_fwlog_buffsize);
+ fwlog_entry_count = (fwlog_buffsize/LPFC_RAS_MAX_ENTRY_SIZE);
+
+ /*
+ * If re-enabling FW logging support use earlier allocated
+ * DMA buffers while posting MBX command.
+ **/
+ if (!ras_fwlog->lwpd.virt) {
+ rc = lpfc_sli4_ras_dma_alloc(phba, fwlog_entry_count);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "6189 FW Log Memory Allocation Failed");
+ return rc;
+ }
+ }
+
+ /* Setup Mailbox command */
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6190 RAS MBX Alloc Failed");
+ rc = -ENOMEM;
+ goto mem_free;
+ }
+
+ ras_fwlog->fw_loglevel = fwlog_level;
+ len = (sizeof(struct lpfc_mbx_set_ras_fwlog) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_LOWLEVEL,
+ LPFC_MBOX_OPCODE_SET_DIAG_LOG_OPTION,
+ len, LPFC_SLI4_MBX_EMBED);
+
+ mbx_fwlog = (struct lpfc_mbx_set_ras_fwlog *)&mbox->u.mqe.un.ras_fwlog;
+ bf_set(lpfc_fwlog_enable, &mbx_fwlog->u.request,
+ fwlog_enable);
+ bf_set(lpfc_fwlog_loglvl, &mbx_fwlog->u.request,
+ ras_fwlog->fw_loglevel);
+ bf_set(lpfc_fwlog_buffcnt, &mbx_fwlog->u.request,
+ ras_fwlog->fw_buffcount);
+ bf_set(lpfc_fwlog_buffsz, &mbx_fwlog->u.request,
+ LPFC_RAS_MAX_ENTRY_SIZE/SLI4_PAGE_SIZE);
+
+ /* Update DMA buffer address */
+ list_for_each_entry(dmabuf, &ras_fwlog->fwlog_buff_list, list) {
+ memset(dmabuf->virt, 0, LPFC_RAS_MAX_ENTRY_SIZE);
+
+ mbx_fwlog->u.request.buff_fwlog[dmabuf->buffer_tag].addr_lo =
+ putPaddrLow(dmabuf->phys);
+
+ mbx_fwlog->u.request.buff_fwlog[dmabuf->buffer_tag].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ }
+
+ /* Update LPWD address */
+ mbx_fwlog->u.request.lwpd.addr_lo = putPaddrLow(ras_fwlog->lwpd.phys);
+ mbx_fwlog->u.request.lwpd.addr_hi = putPaddrHigh(ras_fwlog->lwpd.phys);
+
+ spin_lock_irq(&phba->ras_fwlog_lock);
+ ras_fwlog->state = REG_INPROGRESS;
+ spin_unlock_irq(&phba->ras_fwlog_lock);
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_sli4_ras_mbox_cmpl;
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6191 FW-Log Mailbox failed. "
+ "status %d mbxStatus : x%x", rc,
+ bf_get(lpfc_mqe_status, &mbox->u.mqe));
+ mempool_free(mbox, phba->mbox_mem_pool);
+ rc = -EIO;
+ goto mem_free;
+ } else
+ rc = 0;
+mem_free:
+ if (rc)
+ lpfc_sli4_ras_dma_free(phba);
+
+ return rc;
+}
+
+/**
+ * lpfc_sli4_ras_setup - Check if RAS supported on the adapter
+ * @phba: Pointer to HBA context object.
+ *
+ * Check if RAS is supported on the adapter and initialize it.
+ **/
+void
+lpfc_sli4_ras_setup(struct lpfc_hba *phba)
+{
+ /* Check RAS FW Log needs to be enabled or not */
+ if (lpfc_check_fwlog_support(phba))
+ return;
+
+ lpfc_sli4_ras_fwlog_init(phba, phba->cfg_ras_fwlog_level,
+ LPFC_RAS_ENABLE_LOGGING);
+}
+
/**
* lpfc_sli4_alloc_resource_identifiers - Allocate all SLI4 resource extents.
* @phba: Pointer to HBA context object.
@@ -5275,6 +7159,8 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
uint16_t count, base;
unsigned long longs;
+ if (!phba->sli4_hba.rpi_hdrs_in_use)
+ phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.max_rpi;
if (phba->sli4_hba.extents_in_use) {
/*
* The port supports resource extents. The XRI, VPI, VFI, RPI
@@ -5356,22 +7242,29 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
* need any action - just exit.
*/
if (bf_get(lpfc_idx_rsrc_rdy, &phba->sli4_hba.sli4_flags) ==
- LPFC_IDX_RSRC_RDY)
- return 0;
-
+ LPFC_IDX_RSRC_RDY) {
+ lpfc_sli4_dealloc_resource_identifiers(phba);
+ lpfc_sli4_remove_rpis(phba);
+ }
/* RPIs. */
count = phba->sli4_hba.max_cfg_param.max_rpi;
+ if (count <= 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3279 Invalid provisioning of "
+ "rpi:%d\n", count);
+ rc = -EINVAL;
+ goto err_exit;
+ }
base = phba->sli4_hba.max_cfg_param.rpi_base;
longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG;
- phba->sli4_hba.rpi_bmask = kzalloc(longs *
+ phba->sli4_hba.rpi_bmask = kcalloc(longs,
sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.rpi_bmask)) {
rc = -ENOMEM;
goto err_exit;
}
- phba->sli4_hba.rpi_ids = kzalloc(count *
- sizeof(uint16_t),
+ phba->sli4_hba.rpi_ids = kcalloc(count, sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.rpi_ids)) {
rc = -ENOMEM;
@@ -5383,17 +7276,22 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
/* VPIs. */
count = phba->sli4_hba.max_cfg_param.max_vpi;
+ if (count <= 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3280 Invalid provisioning of "
+ "vpi:%d\n", count);
+ rc = -EINVAL;
+ goto free_rpi_ids;
+ }
base = phba->sli4_hba.max_cfg_param.vpi_base;
longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG;
- phba->vpi_bmask = kzalloc(longs *
- sizeof(unsigned long),
+ phba->vpi_bmask = kcalloc(longs, sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->vpi_bmask)) {
rc = -ENOMEM;
goto free_rpi_ids;
}
- phba->vpi_ids = kzalloc(count *
- sizeof(uint16_t),
+ phba->vpi_ids = kcalloc(count, sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->vpi_ids)) {
rc = -ENOMEM;
@@ -5405,17 +7303,24 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
/* XRIs. */
count = phba->sli4_hba.max_cfg_param.max_xri;
+ if (count <= 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3281 Invalid provisioning of "
+ "xri:%d\n", count);
+ rc = -EINVAL;
+ goto free_vpi_ids;
+ }
base = phba->sli4_hba.max_cfg_param.xri_base;
longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG;
- phba->sli4_hba.xri_bmask = kzalloc(longs *
+ phba->sli4_hba.xri_bmask = kcalloc(longs,
sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.xri_bmask)) {
rc = -ENOMEM;
goto free_vpi_ids;
}
- phba->sli4_hba.xri_ids = kzalloc(count *
- sizeof(uint16_t),
+ phba->sli4_hba.max_cfg_param.xri_used = 0;
+ phba->sli4_hba.xri_ids = kcalloc(count, sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.xri_ids)) {
rc = -ENOMEM;
@@ -5427,17 +7332,23 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
/* VFIs. */
count = phba->sli4_hba.max_cfg_param.max_vfi;
+ if (count <= 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3282 Invalid provisioning of "
+ "vfi:%d\n", count);
+ rc = -EINVAL;
+ goto free_xri_ids;
+ }
base = phba->sli4_hba.max_cfg_param.vfi_base;
longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG;
- phba->sli4_hba.vfi_bmask = kzalloc(longs *
+ phba->sli4_hba.vfi_bmask = kcalloc(longs,
sizeof(unsigned long),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.vfi_bmask)) {
rc = -ENOMEM;
goto free_xri_ids;
}
- phba->sli4_hba.vfi_ids = kzalloc(count *
- sizeof(uint16_t),
+ phba->sli4_hba.vfi_ids = kcalloc(count, sizeof(uint16_t),
GFP_KERNEL);
if (unlikely(!phba->sli4_hba.vfi_ids)) {
rc = -ENOMEM;
@@ -5458,18 +7369,25 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
free_vfi_bmask:
kfree(phba->sli4_hba.vfi_bmask);
+ phba->sli4_hba.vfi_bmask = NULL;
free_xri_ids:
kfree(phba->sli4_hba.xri_ids);
+ phba->sli4_hba.xri_ids = NULL;
free_xri_bmask:
kfree(phba->sli4_hba.xri_bmask);
+ phba->sli4_hba.xri_bmask = NULL;
free_vpi_ids:
kfree(phba->vpi_ids);
+ phba->vpi_ids = NULL;
free_vpi_bmask:
kfree(phba->vpi_bmask);
+ phba->vpi_bmask = NULL;
free_rpi_ids:
kfree(phba->sli4_hba.rpi_ids);
+ phba->sli4_hba.rpi_ids = NULL;
free_rpi_bmask:
kfree(phba->sli4_hba.rpi_bmask);
+ phba->sli4_hba.rpi_bmask = NULL;
err_exit:
return rc;
}
@@ -5491,11 +7409,11 @@ lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *phba)
lpfc_sli4_dealloc_extent(phba, LPFC_RSC_TYPE_FCOE_VFI);
} else {
kfree(phba->vpi_bmask);
+ phba->sli4_hba.max_cfg_param.vpi_used = 0;
kfree(phba->vpi_ids);
bf_set(lpfc_vpi_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0);
kfree(phba->sli4_hba.xri_bmask);
kfree(phba->sli4_hba.xri_ids);
- bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0);
kfree(phba->sli4_hba.vfi_bmask);
kfree(phba->sli4_hba.vfi_ids);
bf_set(lpfc_vfi_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0);
@@ -5506,18 +7424,1107 @@ lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *phba)
}
/**
- * lpfc_sli4_hba_setup - SLI4 device intialization PCI function
+ * lpfc_sli4_get_allocated_extnts - Get the port's allocated extents.
+ * @phba: Pointer to HBA context object.
+ * @type: The resource extent type.
+ * @extnt_cnt: buffer to hold port extent count response
+ * @extnt_size: buffer to hold port extent size response.
+ *
+ * This function calls the port to read the host allocated extents
+ * for a particular type.
+ **/
+int
+lpfc_sli4_get_allocated_extnts(struct lpfc_hba *phba, uint16_t type,
+ uint16_t *extnt_cnt, uint16_t *extnt_size)
+{
+ bool emb;
+ int rc = 0;
+ uint16_t curr_blks = 0;
+ uint32_t req_len, emb_len;
+ uint32_t alloc_len, mbox_tmo;
+ struct list_head *blk_list_head;
+ struct lpfc_rsrc_blks *rsrc_blk;
+ LPFC_MBOXQ_t *mbox;
+ void *virtaddr = NULL;
+ struct lpfc_mbx_nembed_rsrc_extent *n_rsrc;
+ struct lpfc_mbx_alloc_rsrc_extents *rsrc_ext;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ switch (type) {
+ case LPFC_RSC_TYPE_FCOE_VPI:
+ blk_list_head = &phba->lpfc_vpi_blk_list;
+ break;
+ case LPFC_RSC_TYPE_FCOE_XRI:
+ blk_list_head = &phba->sli4_hba.lpfc_xri_blk_list;
+ break;
+ case LPFC_RSC_TYPE_FCOE_VFI:
+ blk_list_head = &phba->sli4_hba.lpfc_vfi_blk_list;
+ break;
+ case LPFC_RSC_TYPE_FCOE_RPI:
+ blk_list_head = &phba->sli4_hba.lpfc_rpi_blk_list;
+ break;
+ default:
+ return -EIO;
+ }
+
+ /* Count the number of extents currently allocatd for this type. */
+ list_for_each_entry(rsrc_blk, blk_list_head, list) {
+ if (curr_blks == 0) {
+ /*
+ * The GET_ALLOCATED mailbox does not return the size,
+ * just the count. The size should be just the size
+ * stored in the current allocated block and all sizes
+ * for an extent type are the same so set the return
+ * value now.
+ */
+ *extnt_size = rsrc_blk->rsrc_size;
+ }
+ curr_blks++;
+ }
+
+ /*
+ * Calculate the size of an embedded mailbox. The uint32_t
+ * accounts for extents-specific word.
+ */
+ emb_len = sizeof(MAILBOX_t) - sizeof(struct mbox_header) -
+ sizeof(uint32_t);
+
+ /*
+ * Presume the allocation and response will fit into an embedded
+ * mailbox. If not true, reconfigure to a non-embedded mailbox.
+ */
+ emb = LPFC_SLI4_MBX_EMBED;
+ req_len = emb_len;
+ if (req_len > emb_len) {
+ req_len = curr_blks * sizeof(uint16_t) +
+ sizeof(union lpfc_sli4_cfg_shdr) +
+ sizeof(uint32_t);
+ emb = LPFC_SLI4_MBX_NEMBED;
+ }
+
+ mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ memset(mbox, 0, sizeof(LPFC_MBOXQ_t));
+
+ alloc_len = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_GET_ALLOC_RSRC_EXTENT,
+ req_len, emb);
+ if (alloc_len < req_len) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2983 Allocated DMA memory size (x%x) is "
+ "less than the requested DMA memory "
+ "size (x%x)\n", alloc_len, req_len);
+ rc = -ENOMEM;
+ goto err_exit;
+ }
+ rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, curr_blks, type, emb);
+ if (unlikely(rc)) {
+ rc = -EIO;
+ goto err_exit;
+ }
+
+ if (!phba->sli4_hba.intr_enable)
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
+ rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+ }
+
+ if (unlikely(rc)) {
+ rc = -EIO;
+ goto err_exit;
+ }
+
+ /*
+ * Figure out where the response is located. Then get local pointers
+ * to the response data. The port does not guarantee to respond to
+ * all extents counts request so update the local variable with the
+ * allocated count from the port.
+ */
+ if (emb == LPFC_SLI4_MBX_EMBED) {
+ rsrc_ext = &mbox->u.mqe.un.alloc_rsrc_extents;
+ shdr = &rsrc_ext->header.cfg_shdr;
+ *extnt_cnt = bf_get(lpfc_mbx_rsrc_cnt, &rsrc_ext->u.rsp);
+ } else {
+ virtaddr = mbox->sge_array->addr[0];
+ n_rsrc = (struct lpfc_mbx_nembed_rsrc_extent *) virtaddr;
+ shdr = &n_rsrc->cfg_shdr;
+ *extnt_cnt = bf_get(lpfc_mbx_rsrc_cnt, n_rsrc);
+ }
+
+ if (bf_get(lpfc_mbox_hdr_status, &shdr->response)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2984 Failed to read allocated resources "
+ "for type %d - Status 0x%x Add'l Status 0x%x.\n",
+ type,
+ bf_get(lpfc_mbox_hdr_status, &shdr->response),
+ bf_get(lpfc_mbox_hdr_add_status, &shdr->response));
+ rc = -EIO;
+ goto err_exit;
+ }
+ err_exit:
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return rc;
+}
+
+/**
+ * lpfc_sli4_repost_sgl_list - Repost the buffers sgl pages as block
+ * @phba: pointer to lpfc hba data structure.
+ * @sgl_list: linked link of sgl buffers to post
+ * @cnt: number of linked list buffers
+ *
+ * This routine walks the list of buffers that have been allocated and
+ * repost them to the port by using SGL block post. This is needed after a
+ * pci_function_reset/warm_start or start. It attempts to construct blocks
+ * of buffer sgls which contains contiguous xris and uses the non-embedded
+ * SGL block post mailbox commands to post them to the port. For single
+ * buffer sgl with non-contiguous xri, if any, it shall use embedded SGL post
+ * mailbox command for posting.
+ *
+ * Returns: 0 = success, non-zero failure.
+ **/
+static int
+lpfc_sli4_repost_sgl_list(struct lpfc_hba *phba,
+ struct list_head *sgl_list, int cnt)
+{
+ struct lpfc_sglq *sglq_entry = NULL;
+ struct lpfc_sglq *sglq_entry_next = NULL;
+ struct lpfc_sglq *sglq_entry_first = NULL;
+ int status = 0, total_cnt;
+ int post_cnt = 0, num_posted = 0, block_cnt = 0;
+ int last_xritag = NO_XRI;
+ LIST_HEAD(prep_sgl_list);
+ LIST_HEAD(blck_sgl_list);
+ LIST_HEAD(allc_sgl_list);
+ LIST_HEAD(post_sgl_list);
+ LIST_HEAD(free_sgl_list);
+
+ spin_lock_irq(&phba->hbalock);
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(sgl_list, &allc_sgl_list);
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
+ spin_unlock_irq(&phba->hbalock);
+
+ total_cnt = cnt;
+ list_for_each_entry_safe(sglq_entry, sglq_entry_next,
+ &allc_sgl_list, list) {
+ list_del_init(&sglq_entry->list);
+ block_cnt++;
+ if ((last_xritag != NO_XRI) &&
+ (sglq_entry->sli4_xritag != last_xritag + 1)) {
+ /* a hole in xri block, form a sgl posting block */
+ list_splice_init(&prep_sgl_list, &blck_sgl_list);
+ post_cnt = block_cnt - 1;
+ /* prepare list for next posting block */
+ list_add_tail(&sglq_entry->list, &prep_sgl_list);
+ block_cnt = 1;
+ } else {
+ /* prepare list for next posting block */
+ list_add_tail(&sglq_entry->list, &prep_sgl_list);
+ /* enough sgls for non-embed sgl mbox command */
+ if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) {
+ list_splice_init(&prep_sgl_list,
+ &blck_sgl_list);
+ post_cnt = block_cnt;
+ block_cnt = 0;
+ }
+ }
+ num_posted++;
+
+ /* keep track of last sgl's xritag */
+ last_xritag = sglq_entry->sli4_xritag;
+
+ /* end of repost sgl list condition for buffers */
+ if (num_posted == total_cnt) {
+ if (post_cnt == 0) {
+ list_splice_init(&prep_sgl_list,
+ &blck_sgl_list);
+ post_cnt = block_cnt;
+ } else if (block_cnt == 1) {
+ status = lpfc_sli4_post_sgl(phba,
+ sglq_entry->phys, 0,
+ sglq_entry->sli4_xritag);
+ if (!status) {
+ /* successful, put sgl to posted list */
+ list_add_tail(&sglq_entry->list,
+ &post_sgl_list);
+ } else {
+ /* Failure, put sgl to free list */
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_SLI,
+ "3159 Failed to post "
+ "sgl, xritag:x%x\n",
+ sglq_entry->sli4_xritag);
+ list_add_tail(&sglq_entry->list,
+ &free_sgl_list);
+ total_cnt--;
+ }
+ }
+ }
+
+ /* continue until a nembed page worth of sgls */
+ if (post_cnt == 0)
+ continue;
+
+ /* post the buffer list sgls as a block */
+ status = lpfc_sli4_post_sgl_list(phba, &blck_sgl_list,
+ post_cnt);
+
+ if (!status) {
+ /* success, put sgl list to posted sgl list */
+ list_splice_init(&blck_sgl_list, &post_sgl_list);
+ } else {
+ /* Failure, put sgl list to free sgl list */
+ sglq_entry_first = list_first_entry(&blck_sgl_list,
+ struct lpfc_sglq,
+ list);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3160 Failed to post sgl-list, "
+ "xritag:x%x-x%x\n",
+ sglq_entry_first->sli4_xritag,
+ (sglq_entry_first->sli4_xritag +
+ post_cnt - 1));
+ list_splice_init(&blck_sgl_list, &free_sgl_list);
+ total_cnt -= post_cnt;
+ }
+
+ /* don't reset xirtag due to hole in xri block */
+ if (block_cnt == 0)
+ last_xritag = NO_XRI;
+
+ /* reset sgl post count for next round of posting */
+ post_cnt = 0;
+ }
+
+ /* free the sgls failed to post */
+ lpfc_free_sgl_list(phba, &free_sgl_list);
+
+ /* push sgls posted to the available list */
+ if (!list_empty(&post_sgl_list)) {
+ spin_lock_irq(&phba->hbalock);
+ spin_lock(&phba->sli4_hba.sgl_list_lock);
+ list_splice_init(&post_sgl_list, sgl_list);
+ spin_unlock(&phba->sli4_hba.sgl_list_lock);
+ spin_unlock_irq(&phba->hbalock);
+ } else {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3161 Failure to post sgl to port,status %x "
+ "blkcnt %d totalcnt %d postcnt %d\n",
+ status, block_cnt, total_cnt, post_cnt);
+ return -EIO;
+ }
+
+ /* return the number of XRIs actually posted */
+ return total_cnt;
+}
+
+/**
+ * lpfc_sli4_repost_io_sgl_list - Repost all the allocated nvme buffer sgls
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine walks the list of nvme buffers that have been allocated and
+ * repost them to the port by using SGL block post. This is needed after a
+ * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine
+ * is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list
+ * to the lpfc_io_buf_list. If the repost fails, reject all nvme buffers.
+ *
+ * Returns: 0 = success, non-zero failure.
+ **/
+static int
+lpfc_sli4_repost_io_sgl_list(struct lpfc_hba *phba)
+{
+ LIST_HEAD(post_nblist);
+ int num_posted, rc = 0;
+
+ /* get all NVME buffers need to repost to a local list */
+ lpfc_io_buf_flush(phba, &post_nblist);
+
+ /* post the list of nvme buffer sgls to port if available */
+ if (!list_empty(&post_nblist)) {
+ num_posted = lpfc_sli4_post_io_sgl_list(
+ phba, &post_nblist, phba->sli4_hba.io_xri_cnt);
+ /* failed to post any nvme buffer, return error */
+ if (num_posted == 0)
+ rc = -EIO;
+ }
+ return rc;
+}
+
+static void
+lpfc_set_host_data(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ uint32_t len;
+
+ len = sizeof(struct lpfc_mbx_set_host_data) -
+ sizeof(struct lpfc_sli4_cfg_mhdr);
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_SET_HOST_DATA, len,
+ LPFC_SLI4_MBX_EMBED);
+
+ mbox->u.mqe.un.set_host_data.param_id = LPFC_SET_HOST_OS_DRIVER_VERSION;
+ mbox->u.mqe.un.set_host_data.param_len =
+ LPFC_HOST_OS_DRIVER_VERSION_SIZE;
+ snprintf(mbox->u.mqe.un.set_host_data.un.data,
+ LPFC_HOST_OS_DRIVER_VERSION_SIZE,
+ "Linux %s v"LPFC_DRIVER_VERSION,
+ test_bit(HBA_FCOE_MODE, &phba->hba_flag) ? "FCoE" : "FC");
+}
+
+int
+lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq,
+ struct lpfc_queue *drq, int count, int idx)
+{
+ int rc, i;
+ struct lpfc_rqe hrqe;
+ struct lpfc_rqe drqe;
+ struct lpfc_rqb *rqbp;
+ unsigned long flags;
+ struct rqb_dmabuf *rqb_buffer;
+ LIST_HEAD(rqb_buf_list);
+
+ rqbp = hrq->rqbp;
+ for (i = 0; i < count; i++) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ /* IF RQ is already full, don't bother */
+ if (rqbp->buffer_count + i >= rqbp->entry_count - 1) {
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ break;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+
+ rqb_buffer = rqbp->rqb_alloc_buffer(phba);
+ if (!rqb_buffer)
+ break;
+ rqb_buffer->hrq = hrq;
+ rqb_buffer->drq = drq;
+ rqb_buffer->idx = idx;
+ list_add_tail(&rqb_buffer->hbuf.list, &rqb_buf_list);
+ }
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+ while (!list_empty(&rqb_buf_list)) {
+ list_remove_head(&rqb_buf_list, rqb_buffer, struct rqb_dmabuf,
+ hbuf.list);
+
+ hrqe.address_lo = putPaddrLow(rqb_buffer->hbuf.phys);
+ hrqe.address_hi = putPaddrHigh(rqb_buffer->hbuf.phys);
+ drqe.address_lo = putPaddrLow(rqb_buffer->dbuf.phys);
+ drqe.address_hi = putPaddrHigh(rqb_buffer->dbuf.phys);
+ rc = lpfc_sli4_rq_put(hrq, drq, &hrqe, &drqe);
+ if (rc < 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6421 Cannot post to HRQ %d: %x %x %x "
+ "DRQ %x %x\n",
+ hrq->queue_id,
+ hrq->host_index,
+ hrq->hba_index,
+ hrq->entry_count,
+ drq->host_index,
+ drq->hba_index);
+ rqbp->rqb_free_buffer(phba, rqb_buffer);
+ } else {
+ list_add_tail(&rqb_buffer->hbuf.list,
+ &rqbp->rqb_buffer_list);
+ rqbp->buffer_count++;
+ }
+ }
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ return 1;
+}
+
+static void
+lpfc_mbx_cmpl_read_lds_params(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ union lpfc_sli4_cfg_shdr *shdr;
+ u32 shdr_status, shdr_add_status;
+
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &pmb->u.mqe.un.sli4_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || pmb->u.mb.mbxStatus) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_LDS_EVENT | LOG_MBOX,
+ "4622 SET_FEATURE (x%x) mbox failed, "
+ "status x%x add_status x%x, mbx status x%x\n",
+ LPFC_SET_LD_SIGNAL, shdr_status,
+ shdr_add_status, pmb->u.mb.mbxStatus);
+ phba->degrade_activate_threshold = 0;
+ phba->degrade_deactivate_threshold = 0;
+ phba->fec_degrade_interval = 0;
+ goto out;
+ }
+
+ phba->degrade_activate_threshold = pmb->u.mqe.un.set_feature.word7;
+ phba->degrade_deactivate_threshold = pmb->u.mqe.un.set_feature.word8;
+ phba->fec_degrade_interval = pmb->u.mqe.un.set_feature.word10;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_LDS_EVENT,
+ "4624 Success: da x%x dd x%x interval x%x\n",
+ phba->degrade_activate_threshold,
+ phba->degrade_deactivate_threshold,
+ phba->fec_degrade_interval);
+out:
+ mempool_free(pmb, phba->mbox_mem_pool);
+}
+
+int
+lpfc_read_lds_params(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ int rc;
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+
+ lpfc_set_features(phba, mboxq, LPFC_SET_LD_SIGNAL);
+ mboxq->vport = phba->pport;
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_lds_params;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void
+lpfc_mbx_cmpl_cgn_set_ftrs(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ struct lpfc_vport *vport = pmb->vport;
+ union lpfc_sli4_cfg_shdr *shdr;
+ u32 shdr_status, shdr_add_status;
+ u32 sig, acqe;
+
+ /* Two outcomes. (1) Set featurs was successul and EDC negotiation
+ * is done. (2) Mailbox failed and send FPIN support only.
+ */
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &pmb->u.mqe.un.sli4_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || pmb->u.mb.mbxStatus) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_CGN_MGMT,
+ "2516 CGN SET_FEATURE mbox failed with "
+ "status x%x add_status x%x, mbx status x%x "
+ "Reset Congestion to FPINs only\n",
+ shdr_status, shdr_add_status,
+ pmb->u.mb.mbxStatus);
+ /* If there is a mbox error, move on to RDF */
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_WARN | LPFC_CGN_FPIN_ALARM;
+ goto out;
+ }
+
+ /* Zero out Congestion Signal ACQE counter */
+ phba->cgn_acqe_cnt = 0;
+
+ acqe = bf_get(lpfc_mbx_set_feature_CGN_acqe_freq,
+ &pmb->u.mqe.un.set_feature);
+ sig = bf_get(lpfc_mbx_set_feature_CGN_warn_freq,
+ &pmb->u.mqe.un.set_feature);
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "4620 SET_FEATURES Success: Freq: %ds %dms "
+ " Reg: x%x x%x\n", acqe, sig,
+ phba->cgn_reg_signal, phba->cgn_reg_fpin);
+out:
+ mempool_free(pmb, phba->mbox_mem_pool);
+
+ /* Register for FPIN events from the fabric now that the
+ * EDC common_set_features has completed.
+ */
+ lpfc_issue_els_rdf(vport, 0);
+}
+
+int
+lpfc_config_cgn_signal(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ u32 rc;
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ goto out_rdf;
+
+ lpfc_set_features(phba, mboxq, LPFC_SET_CGN_SIGNAL);
+ mboxq->vport = phba->pport;
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_cgn_set_ftrs;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "4621 SET_FEATURES: FREQ sig x%x acqe x%x: "
+ "Reg: x%x x%x\n",
+ phba->cgn_sig_freq, lpfc_acqe_cgn_frequency,
+ phba->cgn_reg_signal, phba->cgn_reg_fpin);
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED)
+ goto out;
+ return 0;
+
+out:
+ mempool_free(mboxq, phba->mbox_mem_pool);
+out_rdf:
+ /* If there is a mbox error, move on to RDF */
+ phba->cgn_reg_fpin = LPFC_CGN_FPIN_WARN | LPFC_CGN_FPIN_ALARM;
+ phba->cgn_reg_signal = EDC_CG_SIG_NOTSUPPORTED;
+ lpfc_issue_els_rdf(phba->pport, 0);
+ return -EIO;
+}
+
+/**
+ * lpfc_init_idle_stat_hb - Initialize idle_stat tracking
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine initializes the per-eq idle_stat to dynamically dictate
+ * polling decisions.
+ *
+ * Return codes:
+ * None
+ **/
+static void lpfc_init_idle_stat_hb(struct lpfc_hba *phba)
+{
+ int i;
+ struct lpfc_sli4_hdw_queue *hdwq;
+ struct lpfc_queue *eq;
+ struct lpfc_idle_stat *idle_stat;
+ u64 wall;
+
+ for_each_present_cpu(i) {
+ hdwq = &phba->sli4_hba.hdwq[phba->sli4_hba.cpu_map[i].hdwq];
+ eq = hdwq->hba_eq;
+
+ /* Skip if we've already handled this eq's primary CPU */
+ if (eq->chann != i)
+ continue;
+
+ idle_stat = &phba->sli4_hba.idle_stat[i];
+
+ idle_stat->prev_idle = get_cpu_idle_time(i, &wall, 1);
+ idle_stat->prev_wall = wall;
+
+ if (phba->nvmet_support ||
+ phba->cmf_active_mode != LPFC_CFG_OFF ||
+ phba->intr_type != MSIX)
+ eq->poll_mode = LPFC_QUEUE_WORK;
+ else
+ eq->poll_mode = LPFC_THREADED_IRQ;
+ }
+
+ if (!phba->nvmet_support && phba->intr_type == MSIX)
+ schedule_delayed_work(&phba->idle_stat_delay_work,
+ msecs_to_jiffies(LPFC_IDLE_STAT_DELAY));
+}
+
+static void lpfc_sli4_dip(struct lpfc_hba *phba)
+{
+ uint32_t if_type;
+
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_2 ||
+ if_type == LPFC_SLI_INTF_IF_TYPE_6) {
+ struct lpfc_register reg_data;
+
+ if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
+ &reg_data.word0))
+ return;
+
+ if (bf_get(lpfc_sliport_status_dip, &reg_data))
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2904 Firmware Dump Image Present"
+ " on Adapter");
+ }
+}
+
+/**
+ * lpfc_rx_monitor_create_ring - Initialize ring buffer for rx_monitor
+ * @rx_monitor: Pointer to lpfc_rx_info_monitor object
+ * @entries: Number of rx_info_entry objects to allocate in ring
+ *
+ * Return:
+ * 0 - Success
+ * ENOMEM - Failure to kmalloc
+ **/
+int lpfc_rx_monitor_create_ring(struct lpfc_rx_info_monitor *rx_monitor,
+ u32 entries)
+{
+ rx_monitor->ring = kmalloc_array(entries, sizeof(struct rx_info_entry),
+ GFP_KERNEL);
+ if (!rx_monitor->ring)
+ return -ENOMEM;
+
+ rx_monitor->head_idx = 0;
+ rx_monitor->tail_idx = 0;
+ spin_lock_init(&rx_monitor->lock);
+ rx_monitor->entries = entries;
+
+ return 0;
+}
+
+/**
+ * lpfc_rx_monitor_destroy_ring - Free ring buffer for rx_monitor
+ * @rx_monitor: Pointer to lpfc_rx_info_monitor object
+ *
+ * Called after cancellation of cmf_timer.
+ **/
+void lpfc_rx_monitor_destroy_ring(struct lpfc_rx_info_monitor *rx_monitor)
+{
+ kfree(rx_monitor->ring);
+ rx_monitor->ring = NULL;
+ rx_monitor->entries = 0;
+ rx_monitor->head_idx = 0;
+ rx_monitor->tail_idx = 0;
+}
+
+/**
+ * lpfc_rx_monitor_record - Insert an entry into rx_monitor's ring
+ * @rx_monitor: Pointer to lpfc_rx_info_monitor object
+ * @entry: Pointer to rx_info_entry
+ *
+ * Used to insert an rx_info_entry into rx_monitor's ring. Note that this is a
+ * deep copy of rx_info_entry not a shallow copy of the rx_info_entry ptr.
+ *
+ * This is called from lpfc_cmf_timer, which is in timer/softirq context.
+ *
+ * In cases of old data overflow, we do a best effort of FIFO order.
+ **/
+void lpfc_rx_monitor_record(struct lpfc_rx_info_monitor *rx_monitor,
+ struct rx_info_entry *entry)
+{
+ struct rx_info_entry *ring = rx_monitor->ring;
+ u32 *head_idx = &rx_monitor->head_idx;
+ u32 *tail_idx = &rx_monitor->tail_idx;
+ spinlock_t *ring_lock = &rx_monitor->lock;
+ u32 ring_size = rx_monitor->entries;
+
+ spin_lock(ring_lock);
+ memcpy(&ring[*tail_idx], entry, sizeof(*entry));
+ *tail_idx = (*tail_idx + 1) % ring_size;
+
+ /* Best effort of FIFO saved data */
+ if (*tail_idx == *head_idx)
+ *head_idx = (*head_idx + 1) % ring_size;
+
+ spin_unlock(ring_lock);
+}
+
+/**
+ * lpfc_rx_monitor_report - Read out rx_monitor's ring
+ * @phba: Pointer to lpfc_hba object
+ * @rx_monitor: Pointer to lpfc_rx_info_monitor object
+ * @buf: Pointer to char buffer that will contain rx monitor info data
+ * @buf_len: Length buf including null char
+ * @max_read_entries: Maximum number of entries to read out of ring
+ *
+ * Used to dump/read what's in rx_monitor's ring buffer.
+ *
+ * If buf is NULL || buf_len == 0, then it is implied that we want to log the
+ * information to kmsg instead of filling out buf.
+ *
+ * Return:
+ * Number of entries read out of the ring
+ **/
+u32 lpfc_rx_monitor_report(struct lpfc_hba *phba,
+ struct lpfc_rx_info_monitor *rx_monitor, char *buf,
+ u32 buf_len, u32 max_read_entries)
+{
+ struct rx_info_entry *ring = rx_monitor->ring;
+ struct rx_info_entry *entry;
+ u32 *head_idx = &rx_monitor->head_idx;
+ u32 *tail_idx = &rx_monitor->tail_idx;
+ spinlock_t *ring_lock = &rx_monitor->lock;
+ u32 ring_size = rx_monitor->entries;
+ u32 cnt = 0;
+ char tmp[DBG_LOG_STR_SZ] = {0};
+ bool log_to_kmsg = (!buf || !buf_len) ? true : false;
+
+ if (!log_to_kmsg) {
+ /* clear the buffer to be sure */
+ memset(buf, 0, buf_len);
+
+ scnprintf(buf, buf_len, "\t%-16s%-16s%-16s%-16s%-8s%-8s%-8s"
+ "%-8s%-8s%-8s%-16s\n",
+ "MaxBPI", "Tot_Data_CMF",
+ "Tot_Data_Cmd", "Tot_Data_Cmpl",
+ "Lat(us)", "Avg_IO", "Max_IO", "Bsy",
+ "IO_cnt", "Info", "BWutil(ms)");
+ }
+
+ /* Needs to be _irq because record is called from timer interrupt
+ * context
+ */
+ spin_lock_irq(ring_lock);
+ while (*head_idx != *tail_idx) {
+ entry = &ring[*head_idx];
+
+ /* Read out this entry's data. */
+ if (!log_to_kmsg) {
+ /* If !log_to_kmsg, then store to buf. */
+ scnprintf(tmp, sizeof(tmp),
+ "%03d:\t%-16llu%-16llu%-16llu%-16llu%-8llu"
+ "%-8llu%-8llu%-8u%-8u%-8u%u(%u)\n",
+ *head_idx, entry->max_bytes_per_interval,
+ entry->cmf_bytes, entry->total_bytes,
+ entry->rcv_bytes, entry->avg_io_latency,
+ entry->avg_io_size, entry->max_read_cnt,
+ entry->cmf_busy, entry->io_cnt,
+ entry->cmf_info, entry->timer_utilization,
+ entry->timer_interval);
+
+ /* Check for buffer overflow */
+ if ((strlen(buf) + strlen(tmp)) >= buf_len)
+ break;
+
+ /* Append entry's data to buffer */
+ strlcat(buf, tmp, buf_len);
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
+ "4410 %02u: MBPI %llu Xmit %llu "
+ "Cmpl %llu Lat %llu ASz %llu Info %02u "
+ "BWUtil %u Int %u slot %u\n",
+ cnt, entry->max_bytes_per_interval,
+ entry->total_bytes, entry->rcv_bytes,
+ entry->avg_io_latency,
+ entry->avg_io_size, entry->cmf_info,
+ entry->timer_utilization,
+ entry->timer_interval, *head_idx);
+ }
+
+ *head_idx = (*head_idx + 1) % ring_size;
+
+ /* Don't feed more than max_read_entries */
+ cnt++;
+ if (cnt >= max_read_entries)
+ break;
+ }
+ spin_unlock_irq(ring_lock);
+
+ return cnt;
+}
+
+/**
+ * lpfc_cmf_setup - Initialize idle_stat tracking
+ * @phba: Pointer to HBA context object.
+ *
+ * This is called from HBA setup during driver load or when the HBA
+ * comes online. this does all the initialization to support CMF and MI.
+ **/
+static int
+lpfc_cmf_setup(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_dmabuf *mp;
+ struct lpfc_pc_sli4_params *sli4_params;
+ int rc, cmf, mi_ver;
+
+ rc = lpfc_sli4_refresh_params(phba);
+ if (unlikely(rc))
+ return rc;
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+
+ sli4_params = &phba->sli4_hba.pc_sli4_params;
+
+ /* Always try to enable MI feature if we can */
+ if (sli4_params->mi_ver) {
+ lpfc_set_features(phba, mboxq, LPFC_SET_ENABLE_MI);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ mi_ver = bf_get(lpfc_mbx_set_feature_mi,
+ &mboxq->u.mqe.un.set_feature);
+
+ if (rc == MBX_SUCCESS) {
+ if (mi_ver) {
+ lpfc_printf_log(phba,
+ KERN_WARNING, LOG_CGN_MGMT,
+ "6215 MI is enabled\n");
+ sli4_params->mi_ver = mi_ver;
+ } else {
+ lpfc_printf_log(phba,
+ KERN_WARNING, LOG_CGN_MGMT,
+ "6338 MI is disabled\n");
+ sli4_params->mi_ver = 0;
+ }
+ } else {
+ /* mi_ver is already set from GET_SLI4_PARAMETERS */
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_CGN_MGMT | LOG_INIT,
+ "6245 Enable MI Mailbox x%x (x%x/x%x) "
+ "failed, rc:x%x mi:x%x\n",
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ lpfc_sli_config_mbox_subsys_get
+ (phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get
+ (phba, mboxq),
+ rc, sli4_params->mi_ver);
+ }
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "6217 MI is disabled\n");
+ }
+
+ /* Ensure FDMI is enabled for MI if enable_mi is set */
+ if (sli4_params->mi_ver)
+ phba->cfg_fdmi_on = LPFC_FDMI_SUPPORT;
+
+ /* Always try to enable CMF feature if we can */
+ if (sli4_params->cmf) {
+ lpfc_set_features(phba, mboxq, LPFC_SET_ENABLE_CMF);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ cmf = bf_get(lpfc_mbx_set_feature_cmf,
+ &mboxq->u.mqe.un.set_feature);
+ if (rc == MBX_SUCCESS && cmf) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "6218 CMF is enabled: mode %d\n",
+ phba->cmf_active_mode);
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_CGN_MGMT | LOG_INIT,
+ "6219 Enable CMF Mailbox x%x (x%x/x%x) "
+ "failed, rc:x%x dd:x%x\n",
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ lpfc_sli_config_mbox_subsys_get
+ (phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get
+ (phba, mboxq),
+ rc, cmf);
+ sli4_params->cmf = 0;
+ phba->cmf_active_mode = LPFC_CFG_OFF;
+ goto no_cmf;
+ }
+
+ /* Allocate Congestion Information Buffer */
+ if (!phba->cgn_i) {
+ mp = kmalloc(sizeof(*mp), GFP_KERNEL);
+ if (mp)
+ mp->virt = dma_alloc_coherent
+ (&phba->pcidev->dev,
+ sizeof(struct lpfc_cgn_info),
+ &mp->phys, GFP_KERNEL);
+ if (!mp || !mp->virt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2640 Failed to alloc memory "
+ "for Congestion Info\n");
+ kfree(mp);
+ sli4_params->cmf = 0;
+ phba->cmf_active_mode = LPFC_CFG_OFF;
+ goto no_cmf;
+ }
+ phba->cgn_i = mp;
+
+ /* initialize congestion buffer info */
+ lpfc_init_congestion_buf(phba);
+ lpfc_init_congestion_stat(phba);
+
+ /* Zero out Congestion Signal counters */
+ atomic64_set(&phba->cgn_acqe_stat.alarm, 0);
+ atomic64_set(&phba->cgn_acqe_stat.warn, 0);
+ }
+
+ rc = lpfc_sli4_cgn_params_read(phba);
+ if (rc < 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT | LOG_INIT,
+ "6242 Error reading Cgn Params (%d)\n",
+ rc);
+ /* Ensure CGN Mode is off */
+ sli4_params->cmf = 0;
+ } else if (!rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT | LOG_INIT,
+ "6243 CGN Event empty object.\n");
+ /* Ensure CGN Mode is off */
+ sli4_params->cmf = 0;
+ }
+ } else {
+no_cmf:
+ lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
+ "6220 CMF is disabled\n");
+ }
+
+ /* Only register congestion buffer with firmware if BOTH
+ * CMF and E2E are enabled.
+ */
+ if (sli4_params->cmf && sli4_params->mi_ver) {
+ rc = lpfc_reg_congestion_buf(phba);
+ if (rc) {
+ dma_free_coherent(&phba->pcidev->dev,
+ sizeof(struct lpfc_cgn_info),
+ phba->cgn_i->virt, phba->cgn_i->phys);
+ kfree(phba->cgn_i);
+ phba->cgn_i = NULL;
+ /* Ensure CGN Mode is off */
+ phba->cmf_active_mode = LPFC_CFG_OFF;
+ sli4_params->cmf = 0;
+ return 0;
+ }
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "6470 Setup MI version %d CMF %d mode %d\n",
+ sli4_params->mi_ver, sli4_params->cmf,
+ phba->cmf_active_mode);
+
+ mempool_free(mboxq, phba->mbox_mem_pool);
+
+ /* Initialize atomic counters */
+ atomic_set(&phba->cgn_fabric_warn_cnt, 0);
+ atomic_set(&phba->cgn_fabric_alarm_cnt, 0);
+ atomic_set(&phba->cgn_sync_alarm_cnt, 0);
+ atomic_set(&phba->cgn_sync_warn_cnt, 0);
+ atomic_set(&phba->cgn_driver_evt_cnt, 0);
+ atomic_set(&phba->cgn_latency_evt_cnt, 0);
+ atomic64_set(&phba->cgn_latency_evt, 0);
+
+ phba->cmf_interval_rate = LPFC_CMF_INTERVAL;
+
+ /* Allocate RX Monitor Buffer */
+ if (!phba->rx_monitor) {
+ phba->rx_monitor = kzalloc(sizeof(*phba->rx_monitor),
+ GFP_KERNEL);
+
+ if (!phba->rx_monitor) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2644 Failed to alloc memory "
+ "for RX Monitor Buffer\n");
+ return -ENOMEM;
+ }
+
+ /* Instruct the rx_monitor object to instantiate its ring */
+ if (lpfc_rx_monitor_create_ring(phba->rx_monitor,
+ LPFC_MAX_RXMONITOR_ENTRY)) {
+ kfree(phba->rx_monitor);
+ phba->rx_monitor = NULL;
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2645 Failed to alloc memory "
+ "for RX Monitor's Ring\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int
+lpfc_set_host_tm(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mboxq;
+ uint32_t len, rc;
+ struct timespec64 cur_time;
+ struct tm broken;
+ uint32_t month, day, year;
+ uint32_t hour, minute, second;
+ struct lpfc_mbx_set_host_date_time *tm;
+
+ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq)
+ return -ENOMEM;
+
+ len = sizeof(struct lpfc_mbx_set_host_data) -
+ sizeof(struct lpfc_sli4_cfg_mhdr);
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_SET_HOST_DATA, len,
+ LPFC_SLI4_MBX_EMBED);
+
+ mboxq->u.mqe.un.set_host_data.param_id = LPFC_SET_HOST_DATE_TIME;
+ mboxq->u.mqe.un.set_host_data.param_len =
+ sizeof(struct lpfc_mbx_set_host_date_time);
+ tm = &mboxq->u.mqe.un.set_host_data.un.tm;
+ ktime_get_real_ts64(&cur_time);
+ time64_to_tm(cur_time.tv_sec, 0, &broken);
+ month = broken.tm_mon + 1;
+ day = broken.tm_mday;
+ year = broken.tm_year - 100;
+ hour = broken.tm_hour;
+ minute = broken.tm_min;
+ second = broken.tm_sec;
+ bf_set(lpfc_mbx_set_host_month, tm, month);
+ bf_set(lpfc_mbx_set_host_day, tm, day);
+ bf_set(lpfc_mbx_set_host_year, tm, year);
+ bf_set(lpfc_mbx_set_host_hour, tm, hour);
+ bf_set(lpfc_mbx_set_host_min, tm, minute);
+ bf_set(lpfc_mbx_set_host_sec, tm, second);
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return rc;
+}
+
+/**
+ * lpfc_get_platform_uuid - Attempts to extract a platform uuid
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine attempts to first read SMBIOS DMI data for the System
+ * Information structure offset 08h called System UUID. Else, no platform
+ * UUID will be advertised.
+ **/
+static void
+lpfc_get_platform_uuid(struct lpfc_hba *phba)
+{
+ int rc;
+ const char *uuid;
+ char pni[17] = {0}; /* 16 characters + '\0' */
+ bool is_ff = true, is_00 = true;
+ u8 i;
+
+ /* First attempt SMBIOS DMI */
+ uuid = dmi_get_system_info(DMI_PRODUCT_UUID);
+ if (uuid) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2088 SMBIOS UUID %s\n",
+ uuid);
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2099 Could not extract UUID\n");
+ }
+
+ if (uuid && uuid_is_valid(uuid)) {
+ /* Generate PNI from UUID format.
+ *
+ * 1.) Extract lower 64 bits from UUID format.
+ * 2.) Set 3h for NAA Locally Assigned Name Identifier format.
+ *
+ * e.g. xxxxxxxx-xxxx-xxxx-yyyy-yyyyyyyyyyyy
+ *
+ * extract the yyyy-yyyyyyyyyyyy portion
+ * final PNI 3yyyyyyyyyyyyyyy
+ */
+ scnprintf(pni, sizeof(pni), "3%c%c%c%s",
+ uuid[20], uuid[21], uuid[22], &uuid[24]);
+
+ /* Sanitize the converted PNI */
+ for (i = 1; i < 16 && (is_ff || is_00); i++) {
+ if (pni[i] != '0')
+ is_00 = false;
+ if (pni[i] != 'f' && pni[i] != 'F')
+ is_ff = false;
+ }
+
+ /* Convert from char* to unsigned long */
+ rc = kstrtoul(pni, 16, &phba->pni);
+ if (!rc && !is_ff && !is_00) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2100 PNI 0x%016lx\n", phba->pni);
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2101 PNI %s generation status %d\n",
+ pni, rc);
+ phba->pni = 0;
+ }
+ }
+}
+
+/**
+ * lpfc_sli4_hba_setup - SLI4 device initialization PCI function
* @phba: Pointer to HBA context object.
*
- * This function is the main SLI4 device intialization PCI function. This
- * function is called by the HBA intialization code, HBA reset code and
+ * This function is the main SLI4 device initialization PCI function. This
+ * function is called by the HBA initialization code, HBA reset code and
* HBA error attention handler code. Caller is not required to hold any
* locks.
**/
int
lpfc_sli4_hba_setup(struct lpfc_hba *phba)
{
- int rc;
+ int rc, i, cnt, len, dd;
LPFC_MBOXQ_t *mboxq;
struct lpfc_mqe *mqe;
uint8_t *vpd;
@@ -5526,6 +8533,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
struct Scsi_Host *shost = lpfc_shost_from_vport(phba->pport);
struct lpfc_vport *vport = phba->pport;
struct lpfc_dmabuf *mp;
+ struct lpfc_rqb *rqbp;
+ u32 flg;
/* Perform a PCI function reset to start from clean */
rc = lpfc_pci_function_reset(phba);
@@ -5539,8 +8548,21 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
else {
spin_lock_irq(&phba->hbalock);
phba->sli.sli_flag |= LPFC_SLI_ACTIVE;
+ flg = phba->sli.sli_flag;
spin_unlock_irq(&phba->hbalock);
+ /* Allow a little time after setting SLI_ACTIVE for any polled
+ * MBX commands to complete via BSG.
+ */
+ for (i = 0; i < 50 && (flg & LPFC_SLI_MBOX_ACTIVE); i++) {
+ msleep(20);
+ spin_lock_irq(&phba->hbalock);
+ flg = phba->sli.sli_flag;
+ spin_unlock_irq(&phba->hbalock);
+ }
}
+ clear_bit(HBA_SETUP, &phba->hba_flag);
+
+ lpfc_sli4_dip(phba);
/*
* Allocate a single mailbox container for initializing the
@@ -5550,14 +8572,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
if (!mboxq)
return -ENOMEM;
- /*
- * Continue initialization with default values even if driver failed
- * to read FCoE param config regions
- */
- if (lpfc_sli4_read_fcoe_params(phba, mboxq))
- lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_INIT,
- "2570 Failed to read FCoE parameters\n");
-
/* Issue READ_REV to collect vpd and FW information. */
vpd_size = SLI4_PAGE_SIZE;
vpd = kzalloc(vpd_size, GFP_KERNEL);
@@ -5571,45 +8585,96 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
kfree(vpd);
goto out_free_mbox;
}
+
mqe = &mboxq->u.mqe;
phba->sli_rev = bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev);
- if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev))
- phba->hba_flag |= HBA_FCOE_MODE;
- else
- phba->hba_flag &= ~HBA_FCOE_MODE;
+ if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev)) {
+ set_bit(HBA_FCOE_MODE, &phba->hba_flag);
+ phba->fcp_embed_io = 0; /* SLI4 FC support only */
+ } else {
+ clear_bit(HBA_FCOE_MODE, &phba->hba_flag);
+ }
+
+ /* Obtain platform UUID, only for SLI4 FC adapters */
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag))
+ lpfc_get_platform_uuid(phba);
if (bf_get(lpfc_mbx_rd_rev_cee_ver, &mqe->un.read_rev) ==
LPFC_DCBX_CEE_MODE)
- phba->hba_flag |= HBA_FIP_SUPPORT;
+ set_bit(HBA_FIP_SUPPORT, &phba->hba_flag);
else
- phba->hba_flag &= ~HBA_FIP_SUPPORT;
+ clear_bit(HBA_FIP_SUPPORT, &phba->hba_flag);
+
+ clear_bit(HBA_IOQ_FLUSH, &phba->hba_flag);
if (phba->sli_rev != LPFC_SLI_REV4) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0376 READ_REV Error. SLI Level %d "
"FCoE enabled %d\n",
- phba->sli_rev, phba->hba_flag & HBA_FCOE_MODE);
+ phba->sli_rev,
+ test_bit(HBA_FCOE_MODE, &phba->hba_flag) ? 1 : 0);
rc = -EIO;
kfree(vpd);
goto out_free_mbox;
}
+
+ rc = lpfc_set_host_tm(phba);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
+ "6468 Set host date / time: Status x%x:\n", rc);
+
+ /*
+ * Continue initialization with default values even if driver failed
+ * to read FCoE param config regions, only read parameters if the
+ * board is FCoE
+ */
+ if (test_bit(HBA_FCOE_MODE, &phba->hba_flag) &&
+ lpfc_sli4_read_fcoe_params(phba))
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_INIT,
+ "2570 Failed to read FCoE parameters\n");
+
+ /*
+ * Retrieve sli4 device physical port name, failure of doing it
+ * is considered as non-fatal.
+ */
+ rc = lpfc_sli4_retrieve_pport_name(phba);
+ if (!rc)
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "3080 Successful retrieving SLI4 device "
+ "physical port name: %s.\n", phba->Port);
+
+ rc = lpfc_sli4_get_ctl_attr(phba);
+ if (!rc)
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "8351 Successful retrieving SLI4 device "
+ "CTL ATTR\n");
+
/*
* Evaluate the read rev and vpd data. Populate the driver
* state with the results. If this routine fails, the failure
* is not fatal as the driver will use generic values.
*/
rc = lpfc_parse_vpd(phba, vpd, vpd_size);
- if (unlikely(!rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ if (unlikely(!rc))
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0377 Error %d parsing vpd. "
"Using defaults.\n", rc);
- rc = 0;
- }
kfree(vpd);
/* Save information as VPD data */
phba->vpd.rev.biuRev = mqe->un.read_rev.first_hw_rev;
phba->vpd.rev.smRev = mqe->un.read_rev.second_hw_rev;
+
+ /*
+ * This is because first G7 ASIC doesn't support the standard
+ * 0x5a NVME cmd descriptor type/subtype
+ */
+ if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_6) &&
+ (phba->vpd.rev.biuRev == LPFC_G7_ASIC_1) &&
+ (phba->vpd.rev.smRev == 0) &&
+ (phba->cfg_nvme_embed_cmd == 1))
+ phba->cfg_nvme_embed_cmd = 0;
+
phba->vpd.rev.endecRev = mqe->un.read_rev.third_hw_rev;
phba->vpd.rev.fcphHigh = bf_get(lpfc_mbx_rd_rev_fcph_high,
&mqe->un.read_rev);
@@ -5634,6 +8699,31 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
phba->vpd.rev.fcphHigh, phba->vpd.rev.fcphLow,
phba->vpd.rev.feaLevelHigh, phba->vpd.rev.feaLevelLow);
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_0) {
+ lpfc_set_features(phba, mboxq, LPFC_SET_UE_RECOVERY);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc == MBX_SUCCESS) {
+ set_bit(HBA_RECOVERABLE_UE, &phba->hba_flag);
+ /* Set 1Sec interval to detect UE */
+ phba->eratt_poll_interval = 1;
+ phba->sli4_hba.ue_to_sr = bf_get(
+ lpfc_mbx_set_feature_UESR,
+ &mboxq->u.mqe.un.set_feature);
+ phba->sli4_hba.ue_to_rp = bf_get(
+ lpfc_mbx_set_feature_UERP,
+ &mboxq->u.mqe.un.set_feature);
+ }
+ }
+
+ if (phba->cfg_enable_mds_diags && phba->mds_diags_support) {
+ /* Enable MDS Diagnostics only if the SLI Port supports it */
+ lpfc_set_features(phba, mboxq, LPFC_SET_MDS_DIAGS);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS)
+ phba->mds_diags_support = 0;
+ }
+
/*
* Discover the port's supported feature set and match it against the
* hosts requests.
@@ -5645,6 +8735,15 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
goto out_free_mbox;
}
+ /* Disable VMID if app header is not supported */
+ if (phba->cfg_vmid_app_header && !(bf_get(lpfc_mbx_rq_ftr_rsp_ashdr,
+ &mqe->un.req_ftrs))) {
+ bf_set(lpfc_ftr_ashdr, &phba->sli4_hba.sli4_flags, 0);
+ phba->cfg_vmid_app_header = 0;
+ lpfc_printf_log(phba, KERN_DEBUG, LOG_SLI,
+ "1242 vmid feature not supported\n");
+ }
+
/*
* The port must support FCP initiator mode as this is the
* only mode running in the host.
@@ -5654,18 +8753,27 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
"0378 No support for fcpi mode.\n");
ftr_rsp++;
}
- if (bf_get(lpfc_mbx_rq_ftr_rsp_perfh, &mqe->un.req_ftrs))
- phba->sli3_options |= LPFC_SLI4_PERFH_ENABLED;
- else
- phba->sli3_options &= ~LPFC_SLI4_PERFH_ENABLED;
+
+ /* Performance Hints are ONLY for FCoE */
+ if (test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ if (bf_get(lpfc_mbx_rq_ftr_rsp_perfh, &mqe->un.req_ftrs))
+ phba->sli3_options |= LPFC_SLI4_PERFH_ENABLED;
+ else
+ phba->sli3_options &= ~LPFC_SLI4_PERFH_ENABLED;
+ }
+
/*
* If the port cannot support the host's requested features
* then turn off the global config parameters to disable the
* feature in the driver. This is not a fatal error.
*/
- if ((phba->cfg_enable_bg) &&
- !(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs)))
- ftr_rsp++;
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ if (!(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs))) {
+ phba->cfg_enable_bg = 0;
+ phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
+ ftr_rsp++;
+ }
+ }
if (phba->max_vpi && phba->cfg_enable_npiv &&
!(bf_get(lpfc_mbx_rq_ftr_rsp_npiv, &mqe->un.req_ftrs)))
@@ -5688,18 +8796,47 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
phba->sli3_options |= (LPFC_SLI3_NPIV_ENABLED | LPFC_SLI3_HBQ_ENABLED);
spin_unlock_irq(&phba->hbalock);
+ /* Always try to enable dual dump feature if we can */
+ lpfc_set_features(phba, mboxq, LPFC_SET_DUAL_DUMP);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ dd = bf_get(lpfc_mbx_set_feature_dd, &mboxq->u.mqe.un.set_feature);
+ if ((rc == MBX_SUCCESS) && (dd == LPFC_ENABLE_DUAL_DUMP))
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "6448 Dual Dump is enabled\n");
+ else
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI | LOG_INIT,
+ "6447 Dual Dump Mailbox x%x (x%x/x%x) failed, "
+ "rc:x%x dd:x%x\n",
+ bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+ lpfc_sli_config_mbox_subsys_get(
+ phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(
+ phba, mboxq),
+ rc, dd);
+
/*
* Allocate all resources (xri,rpi,vpi,vfi) now. Subsequent
* calls depends on these resources to complete port setup.
*/
rc = lpfc_sli4_alloc_resource_identifiers(phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2920 Failed to alloc Resource IDs "
"rc = x%x\n", rc);
goto out_free_mbox;
}
+ lpfc_sli4_node_rpi_restore(phba);
+
+ lpfc_set_host_data(phba, mboxq);
+
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+ "2134 Failed to set host os driver version %x",
+ rc);
+ }
+
/* Read the port's service parameters. */
rc = lpfc_read_sparam(phba, mboxq, vport->vpi);
if (rc) {
@@ -5710,21 +8847,22 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
mboxq->vport = vport;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
- mp = (struct lpfc_dmabuf *) mboxq->context1;
+ mp = mboxq->ctx_buf;
if (rc == MBX_SUCCESS) {
memcpy(&vport->fc_sparam, mp->virt, sizeof(struct serv_parm));
rc = 0;
}
/*
- * This memory was allocated by the lpfc_read_sparam routine. Release
- * it to the mbuf pool.
+ * This memory was allocated by the lpfc_read_sparam routine but is
+ * no longer needed. It is released and ctx_buf NULLed to prevent
+ * unintended pointer access as the mbox is reused.
*/
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
- mboxq->context1 = NULL;
+ mboxq->ctx_buf = NULL;
if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0382 READ_SPARAM command failed "
"status %d, mbxStatus x%x\n",
rc, bf_get(lpfc_mqe_status, mqe));
@@ -5739,62 +8877,201 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn);
fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
- /* Register SGL pool to the device using non-embedded mailbox command */
- if (!phba->sli4_hba.extents_in_use) {
- rc = lpfc_sli4_post_els_sgl_list(phba);
+ /* Create all the SLI4 queues */
+ rc = lpfc_sli4_queue_create(phba);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3089 Failed to allocate queues\n");
+ rc = -ENODEV;
+ goto out_free_mbox;
+ }
+ /* Set up all the queues to the device */
+ rc = lpfc_sli4_queue_setup(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0381 Error %d during queue setup.\n", rc);
+ goto out_destroy_queue;
+ }
+ /* Initialize the driver internal SLI layer lists. */
+ lpfc_sli4_setup(phba);
+ lpfc_sli4_queue_init(phba);
+
+ /* update host els xri-sgl sizes and mappings */
+ rc = lpfc_sli4_els_sgl_update(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "1400 Failed to update xri-sgl size and "
+ "mapping: %d\n", rc);
+ goto out_destroy_queue;
+ }
+
+ /* register the els sgl pool to the port */
+ rc = lpfc_sli4_repost_sgl_list(phba, &phba->sli4_hba.lpfc_els_sgl_list,
+ phba->sli4_hba.els_xri_cnt);
+ if (unlikely(rc < 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0582 Error %d during els sgl post "
+ "operation\n", rc);
+ rc = -ENODEV;
+ goto out_destroy_queue;
+ }
+ phba->sli4_hba.els_xri_cnt = rc;
+
+ if (phba->nvmet_support) {
+ /* update host nvmet xri-sgl sizes and mappings */
+ rc = lpfc_sli4_nvmet_sgl_update(phba);
if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "0582 Error %d during els sgl post "
- "operation\n", rc);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6308 Failed to update nvmet-sgl size "
+ "and mapping: %d\n", rc);
+ goto out_destroy_queue;
+ }
+
+ /* register the nvmet sgl pool to the port */
+ rc = lpfc_sli4_repost_sgl_list(
+ phba,
+ &phba->sli4_hba.lpfc_nvmet_sgl_list,
+ phba->sli4_hba.nvmet_xri_cnt);
+ if (unlikely(rc < 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3117 Error %d during nvmet "
+ "sgl post\n", rc);
rc = -ENODEV;
- goto out_free_mbox;
+ goto out_destroy_queue;
}
+ phba->sli4_hba.nvmet_xri_cnt = rc;
+
+ /* We allocate an iocbq for every receive context SGL.
+ * The additional allocation is for abort and ls handling.
+ */
+ cnt = phba->sli4_hba.nvmet_xri_cnt +
+ phba->sli4_hba.max_cfg_param.max_xri;
} else {
- rc = lpfc_sli4_post_els_sgl_list_ext(phba);
+ /* update host common xri-sgl sizes and mappings */
+ rc = lpfc_sli4_io_sgl_update(phba);
if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "2560 Error %d during els sgl post "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6082 Failed to update nvme-sgl size "
+ "and mapping: %d\n", rc);
+ goto out_destroy_queue;
+ }
+
+ /* register the allocated common sgl pool to the port */
+ rc = lpfc_sli4_repost_io_sgl_list(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6116 Error %d during nvme sgl post "
"operation\n", rc);
+ /* Some NVME buffers were moved to abort nvme list */
+ /* A pci function reset will repost them */
rc = -ENODEV;
- goto out_free_mbox;
+ goto out_destroy_queue;
}
+ /* Each lpfc_io_buf job structure has an iocbq element.
+ * This cnt provides for abort, els, ct and ls requests.
+ */
+ cnt = phba->sli4_hba.max_cfg_param.max_xri;
}
- /* Register SCSI SGL pool to the device */
- rc = lpfc_sli4_repost_scsi_sgl_list(phba);
- if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "0383 Error %d during scsi sgl post "
- "operation\n", rc);
- /* Some Scsi buffers were moved to the abort scsi list */
- /* A pci function reset will repost them */
- rc = -ENODEV;
- goto out_free_mbox;
+ if (!phba->sli.iocbq_lookup) {
+ /* Initialize and populate the iocb list per host */
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2821 initialize iocb list with %d entries\n",
+ cnt);
+ rc = lpfc_init_iocb_list(phba, cnt);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "1413 Failed to init iocb list.\n");
+ goto out_destroy_queue;
+ }
+ }
+
+ if (phba->nvmet_support)
+ lpfc_nvmet_create_targetport(phba);
+
+ if (phba->nvmet_support && phba->cfg_nvmet_mrq) {
+ /* Post initial buffers to all RQs created */
+ for (i = 0; i < phba->cfg_nvmet_mrq; i++) {
+ rqbp = phba->sli4_hba.nvmet_mrq_hdr[i]->rqbp;
+ INIT_LIST_HEAD(&rqbp->rqb_buffer_list);
+ rqbp->rqb_alloc_buffer = lpfc_sli4_nvmet_alloc;
+ rqbp->rqb_free_buffer = lpfc_sli4_nvmet_free;
+ rqbp->entry_count = LPFC_NVMET_RQE_DEF_COUNT;
+ rqbp->buffer_count = 0;
+
+ lpfc_post_rq_buffer(
+ phba, phba->sli4_hba.nvmet_mrq_hdr[i],
+ phba->sli4_hba.nvmet_mrq_data[i],
+ phba->cfg_nvmet_mrq_post, i);
+ }
}
/* Post the rpi header region to the device. */
rc = lpfc_sli4_post_all_rpi_hdrs(phba);
if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0393 Error %d during rpi post operation\n",
rc);
rc = -ENODEV;
- goto out_free_mbox;
+ goto out_free_iocblist;
}
- /* Set up all the queues to the device */
- rc = lpfc_sli4_queue_setup(phba);
- if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "0381 Error %d during queue setup.\n ", rc);
- goto out_stop_timers;
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag)) {
+ if ((phba->nvmet_support == 0) || (phba->cfg_nvmet_mrq == 1)) {
+ /*
+ * The FC Port needs to register FCFI (index 0)
+ */
+ lpfc_reg_fcfi(phba, mboxq);
+ mboxq->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS)
+ goto out_unset_queue;
+ rc = 0;
+ phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_fcfi,
+ &mboxq->u.mqe.un.reg_fcfi);
+ } else {
+ /* We are a NVME Target mode with MRQ > 1 */
+
+ /* First register the FCFI */
+ lpfc_reg_fcfi_mrq(phba, mboxq, 0);
+ mboxq->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS)
+ goto out_unset_queue;
+ rc = 0;
+ phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_mrq_fcfi,
+ &mboxq->u.mqe.un.reg_fcfi_mrq);
+
+ /* Next register the MRQs */
+ lpfc_reg_fcfi_mrq(phba, mboxq, 1);
+ mboxq->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS)
+ goto out_unset_queue;
+ rc = 0;
+ }
+ /* Check if the port is configured to be disabled */
+ lpfc_sli_read_link_ste(phba);
}
- /* Arm the CQs and then EQs on device */
- lpfc_sli4_arm_cqeq_intr(phba);
+ /* Don't post more new bufs if repost already recovered
+ * the nvme sgls.
+ */
+ if (phba->nvmet_support == 0) {
+ if (phba->sli4_hba.io_xri_cnt == 0) {
+ len = lpfc_new_io_buf(
+ phba, phba->sli4_hba.io_xri_max);
+ if (len == 0) {
+ rc = -ENOMEM;
+ goto out_unset_queue;
+ }
- /* Indicate device interrupt mode */
- phba->sli4_hba.intr_enable = 1;
+ if (phba->cfg_xri_rebalancing)
+ lpfc_create_multixri_pools(phba);
+ }
+ } else {
+ phba->cfg_xri_rebalancing = 0;
+ }
/* Allow asynchronous mailbox command to go through */
spin_lock_irq(&phba->hbalock);
@@ -5810,64 +9087,93 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
/* Start the ELS watchdog timer */
mod_timer(&vport->els_tmofunc,
- jiffies + HZ * (phba->fc_ratov * 2));
+ jiffies + secs_to_jiffies(phba->fc_ratov * 2));
/* Start heart beat timer */
mod_timer(&phba->hb_tmofunc,
- jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
- phba->hb_outstanding = 0;
+ jiffies + secs_to_jiffies(LPFC_HB_MBOX_INTERVAL));
+ clear_bit(HBA_HBEAT_INP, &phba->hba_flag);
+ clear_bit(HBA_HBEAT_TMO, &phba->hba_flag);
phba->last_completion_time = jiffies;
- /* Start error attention (ERATT) polling timer */
- mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
+ /* start eq_delay heartbeat */
+ if (phba->cfg_auto_imax)
+ queue_delayed_work(phba->wq, &phba->eq_delay_work,
+ msecs_to_jiffies(LPFC_EQ_DELAY_MSECS));
- /* Enable PCIe device Advanced Error Reporting (AER) if configured */
- if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) {
- rc = pci_enable_pcie_error_reporting(phba->pcidev);
- if (!rc) {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2829 This device supports "
- "Advanced Error Reporting (AER)\n");
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag |= HBA_AER_ENABLED;
- spin_unlock_irq(&phba->hbalock);
- } else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2830 This device does not support "
- "Advanced Error Reporting (AER)\n");
- phba->cfg_aer_support = 0;
- }
- }
+ /* start per phba idle_stat_delay heartbeat */
+ lpfc_init_idle_stat_hb(phba);
+
+ /* Start error attention (ERATT) polling timer */
+ mod_timer(&phba->eratt_poll,
+ jiffies + secs_to_jiffies(phba->eratt_poll_interval));
- if (!(phba->hba_flag & HBA_FCOE_MODE)) {
- /*
- * The FC Port needs to register FCFI (index 0)
- */
- lpfc_reg_fcfi(phba, mboxq);
- mboxq->vport = phba->pport;
- rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
- if (rc != MBX_SUCCESS)
- goto out_unset_queue;
- rc = 0;
- phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_fcfi,
- &mboxq->u.mqe.un.reg_fcfi);
- }
/*
* The port is ready, set the host's link state to LINK_DOWN
* in preparation for link interrupts.
*/
spin_lock_irq(&phba->hbalock);
phba->link_state = LPFC_LINK_DOWN;
+
+ /* Check if physical ports are trunked */
+ if (bf_get(lpfc_conf_trunk_port0, &phba->sli4_hba))
+ phba->trunk_link.link0.state = LPFC_LINK_DOWN;
+ if (bf_get(lpfc_conf_trunk_port1, &phba->sli4_hba))
+ phba->trunk_link.link1.state = LPFC_LINK_DOWN;
+ if (bf_get(lpfc_conf_trunk_port2, &phba->sli4_hba))
+ phba->trunk_link.link2.state = LPFC_LINK_DOWN;
+ if (bf_get(lpfc_conf_trunk_port3, &phba->sli4_hba))
+ phba->trunk_link.link3.state = LPFC_LINK_DOWN;
spin_unlock_irq(&phba->hbalock);
- if (phba->cfg_suppress_link_up == LPFC_INITIALIZE_LINK)
- rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
+
+ /* Arm the CQs and then EQs on device */
+ lpfc_sli4_arm_cqeq_intr(phba);
+
+ /* Indicate device interrupt mode */
+ phba->sli4_hba.intr_enable = 1;
+
+ /* Setup CMF after HBA is initialized */
+ lpfc_cmf_setup(phba);
+
+ if (!test_bit(HBA_FCOE_MODE, &phba->hba_flag) &&
+ test_bit(LINK_DISABLED, &phba->hba_flag)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3103 Adapter Link is disabled.\n");
+ lpfc_down_link(phba, mboxq);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3104 Adapter failed to issue "
+ "DOWN_LINK mbox cmd, rc:x%x\n", rc);
+ goto out_io_buff_free;
+ }
+ } else if (phba->cfg_suppress_link_up == LPFC_INITIALIZE_LINK) {
+ /* don't perform init_link on SLI4 FC port loopback test */
+ if (!(phba->link_flag & LS_LOOPBACK_MODE)) {
+ rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
+ if (rc)
+ goto out_io_buff_free;
+ }
+ }
+ mempool_free(mboxq, phba->mbox_mem_pool);
+
+ /* Enable RAS FW log support */
+ lpfc_sli4_ras_setup(phba);
+
+ set_bit(HBA_SETUP, &phba->hba_flag);
+ return rc;
+
+out_io_buff_free:
+ /* Free allocated IO Buffers */
+ lpfc_io_free(phba);
out_unset_queue:
/* Unset all the queues set up in this routine when error out */
- if (rc)
- lpfc_sli4_queue_unset(phba);
-out_stop_timers:
- if (rc)
- lpfc_stop_hba_timers(phba);
+ lpfc_sli4_queue_unset(phba);
+out_free_iocblist:
+ lpfc_free_iocb_list(phba);
+out_destroy_queue:
+ lpfc_sli4_queue_destroy(phba);
+ lpfc_stop_hba_timers(phba);
out_free_mbox:
mempool_free(mboxq, phba->mbox_mem_pool);
return rc;
@@ -5875,7 +9181,7 @@ out_free_mbox:
/**
* lpfc_mbox_timeout - Timeout call back function for mbox timer
- * @ptr: context object - pointer to hba structure.
+ * @t: Context to fetch pointer to hba structure from.
*
* This is the callback function for mailbox timer. The mailbox
* timer is armed when a new mailbox command is issued and the timer
@@ -5886,9 +9192,9 @@ out_free_mbox:
* done by the worker thread function lpfc_mbox_timeout_handler.
**/
void
-lpfc_mbox_timeout(unsigned long ptr)
+lpfc_mbox_timeout(struct timer_list *t)
{
- struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
+ struct lpfc_hba *phba = timer_container_of(phba, t, sli.mbox_tmo);
unsigned long iflag;
uint32_t tmo_posted;
@@ -5903,6 +9209,114 @@ lpfc_mbox_timeout(unsigned long ptr)
return;
}
+/**
+ * lpfc_sli4_mbox_completions_pending - check to see if any mailbox completions
+ * are pending
+ * @phba: Pointer to HBA context object.
+ *
+ * This function checks if any mailbox completions are present on the mailbox
+ * completion queue.
+ **/
+static bool
+lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba)
+{
+
+ uint32_t idx;
+ struct lpfc_queue *mcq;
+ struct lpfc_mcqe *mcqe;
+ bool pending_completions = false;
+ uint8_t qe_valid;
+
+ if (unlikely(!phba) || (phba->sli_rev != LPFC_SLI_REV4))
+ return false;
+
+ /* Check for completions on mailbox completion queue */
+
+ mcq = phba->sli4_hba.mbx_cq;
+ idx = mcq->hba_index;
+ qe_valid = mcq->qe_valid;
+ while (bf_get_le32(lpfc_cqe_valid,
+ (struct lpfc_cqe *)lpfc_sli4_qe(mcq, idx)) == qe_valid) {
+ mcqe = (struct lpfc_mcqe *)(lpfc_sli4_qe(mcq, idx));
+ if (bf_get_le32(lpfc_trailer_completed, mcqe) &&
+ (!bf_get_le32(lpfc_trailer_async, mcqe))) {
+ pending_completions = true;
+ break;
+ }
+ idx = (idx + 1) % mcq->entry_count;
+ if (mcq->hba_index == idx)
+ break;
+
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.cqav && !idx)
+ qe_valid = (qe_valid) ? 0 : 1;
+ }
+ return pending_completions;
+
+}
+
+/**
+ * lpfc_sli4_process_missed_mbox_completions - process mbox completions
+ * that were missed.
+ * @phba: Pointer to HBA context object.
+ *
+ * For sli4, it is possible to miss an interrupt. As such mbox completions
+ * maybe missed causing erroneous mailbox timeouts to occur. This function
+ * checks to see if mbox completions are on the mailbox completion queue
+ * and will process all the completions associated with the eq for the
+ * mailbox completion queue.
+ **/
+static bool
+lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
+{
+ struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba;
+ uint32_t eqidx;
+ struct lpfc_queue *fpeq = NULL;
+ struct lpfc_queue *eq;
+ bool mbox_pending;
+
+ if (unlikely(!phba) || (phba->sli_rev != LPFC_SLI_REV4))
+ return false;
+
+ /* Find the EQ associated with the mbox CQ */
+ if (sli4_hba->hdwq) {
+ for (eqidx = 0; eqidx < phba->cfg_irq_chann; eqidx++) {
+ eq = phba->sli4_hba.hba_eq_hdl[eqidx].eq;
+ if (eq && eq->queue_id == sli4_hba->mbx_cq->assoc_qid) {
+ fpeq = eq;
+ break;
+ }
+ }
+ }
+ if (!fpeq)
+ return false;
+
+ /* Turn off interrupts from this EQ */
+
+ sli4_hba->sli4_eq_clr_intr(fpeq);
+
+ /* Check to see if a mbox completion is pending */
+
+ mbox_pending = lpfc_sli4_mbox_completions_pending(phba);
+
+ /*
+ * If a mbox completion is pending, process all the events on EQ
+ * associated with the mbox completion queue (this could include
+ * mailbox commands, async events, els commands, receive queue data
+ * and fcp commands)
+ */
+
+ if (mbox_pending)
+ /* process and rearm the EQ */
+ lpfc_sli4_process_eq(phba, fpeq, LPFC_QUEUE_REARM,
+ LPFC_QUEUE_WORK);
+ else
+ /* Always clear and re-arm the EQ */
+ sli4_hba->sli4_write_eq_db(phba, fpeq, 0, LPFC_QUEUE_REARM);
+
+ return mbox_pending;
+
+}
/**
* lpfc_mbox_timeout_handler - Worker thread function to handle mailbox timeout
@@ -5916,10 +9330,18 @@ void
lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
{
LPFC_MBOXQ_t *pmbox = phba->sli.mbox_active;
- MAILBOX_t *mb = &pmbox->u.mb;
+ MAILBOX_t *mb = NULL;
+
struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring;
+ /* If the mailbox completed, process the completion */
+ lpfc_sli4_process_missed_mbox_completions(phba);
+
+ if (!(psli->sli_flag & LPFC_SLI_ACTIVE))
+ return;
+
+ if (pmbox != NULL)
+ mb = &pmbox->u.mb;
/* Check the pmbox pointer first. There is a race condition
* between the mbox timeout handler getting executed in the
* worklist and the mailbox actually completing. When this
@@ -5936,8 +9358,8 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
}
/* Mbox cmd <mbxCommand> timeout */
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0310 Mailbox command x%x timeout Data: x%x x%x x%px\n",
mb->mbxCommand,
phba->pport->port_state,
phba->sli.sli_flag,
@@ -5948,6 +9370,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
* would get IOCB_ERROR from lpfc_sli_issue_iocb, allowing
* it to fail all outstanding SCSI IO.
*/
+ set_bit(MBX_TMO_ERR, &phba->bit_flags);
spin_lock_irq(&phba->pport->work_port_lock);
phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
spin_unlock_irq(&phba->pport->work_port_lock);
@@ -5956,10 +9379,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
psli->sli_flag &= ~LPFC_SLI_ACTIVE;
spin_unlock_irq(&phba->hbalock);
- pring = &psli->ring[psli->fcp_ring];
- lpfc_sli_abort_iocb_ring(phba, pring);
-
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0345 Resetting board due to mailbox timeout\n");
/* Reset the HBA device */
@@ -5996,7 +9416,7 @@ static int
lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
uint32_t flag)
{
- MAILBOX_t *mb;
+ MAILBOX_t *mbx;
struct lpfc_sli *psli = &phba->sli;
uint32_t status, evtctr;
uint32_t ha_copy, hc_copy;
@@ -6043,21 +9463,21 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
}
/* If HBA has a deferred error attention, fail the iocb. */
- if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+ if (unlikely(test_bit(DEFER_ERATT, &phba->hba_flag))) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
goto out_not_finished;
}
psli = &phba->sli;
- mb = &pmbox->u.mb;
+ mbx = &pmbox->u.mb;
status = MBX_SUCCESS;
if (phba->link_state == LPFC_HBA_ERROR) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"(%d):0311 Mailbox command x%x cannot "
"issue Data: x%x x%x\n",
pmbox->vport ? pmbox->vport->vpi : 0,
@@ -6065,11 +9485,11 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
goto out_not_finished;
}
- if (mb->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT) {
+ if (mbx->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT) {
if (lpfc_readl(phba->HCregaddr, &hc_copy) ||
!(hc_copy & HC_MBINT_ENA)) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"(%d):2528 Mailbox command x%x cannot "
"issue Data: x%x x%x\n",
pmbox->vport ? pmbox->vport->vpi : 0,
@@ -6088,7 +9508,7 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"(%d):2529 Mailbox command x%x "
"cannot issue Data: x%x x%x\n",
pmbox->vport ? pmbox->vport->vpi : 0,
@@ -6100,7 +9520,7 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
if (!(psli->sli_flag & LPFC_SLI_ACTIVE)) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"(%d):2530 Mailbox command x%x "
"cannot issue Data: x%x x%x\n",
pmbox->vport ? pmbox->vport->vpi : 0,
@@ -6119,7 +9539,8 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
"(%d):0308 Mbox cmd issue - BUSY Data: "
"x%x x%x x%x x%x\n",
pmbox->vport ? pmbox->vport->vpi : 0xffffff,
- mb->mbxCommand, phba->pport->port_state,
+ mbx->mbxCommand,
+ phba->pport ? phba->pport->port_state : 0xff,
psli->sli_flag, flag);
psli->slistat.mbox_busy++;
@@ -6129,15 +9550,15 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
lpfc_debugfs_disc_trc(pmbox->vport,
LPFC_DISC_TRC_MBOX_VPORT,
"MBOX Bsy vport: cmd:x%x mb:x%x x%x",
- (uint32_t)mb->mbxCommand,
- mb->un.varWords[0], mb->un.varWords[1]);
+ (uint32_t)mbx->mbxCommand,
+ mbx->un.varWords[0], mbx->un.varWords[1]);
}
else {
lpfc_debugfs_disc_trc(phba->pport,
LPFC_DISC_TRC_MBOX,
"MBOX Bsy: cmd:x%x mb:x%x x%x",
- (uint32_t)mb->mbxCommand,
- mb->un.varWords[0], mb->un.varWords[1]);
+ (uint32_t)mbx->mbxCommand,
+ mbx->un.varWords[0], mbx->un.varWords[1]);
}
return MBX_BUSY;
@@ -6148,11 +9569,11 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
/* If we are not polling, we MUST be in SLI2 mode */
if (flag != MBX_POLL) {
if (!(psli->sli_flag & LPFC_SLI_ACTIVE) &&
- (mb->mbxCommand != MBX_KILL_BOARD)) {
+ (mbx->mbxCommand != MBX_KILL_BOARD)) {
psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
/* Mbox command <mbxCommand> cannot issue */
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"(%d):2531 Mailbox command x%x "
"cannot issue Data: x%x x%x\n",
pmbox->vport ? pmbox->vport->vpi : 0,
@@ -6161,8 +9582,8 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
goto out_not_finished;
}
/* timeout active mbox command */
- mod_timer(&psli->mbox_tmo, (jiffies +
- (HZ * lpfc_mbox_tmo_val(phba, mb->mbxCommand))));
+ timeout = secs_to_jiffies(lpfc_mbox_tmo_val(phba, pmbox));
+ mod_timer(&psli->mbox_tmo, jiffies + timeout);
}
/* Mailbox cmd <cmd> issue */
@@ -6170,23 +9591,24 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
"(%d):0309 Mailbox cmd x%x issue Data: x%x x%x "
"x%x\n",
pmbox->vport ? pmbox->vport->vpi : 0,
- mb->mbxCommand, phba->pport->port_state,
+ mbx->mbxCommand,
+ phba->pport ? phba->pport->port_state : 0xff,
psli->sli_flag, flag);
- if (mb->mbxCommand != MBX_HEARTBEAT) {
+ if (mbx->mbxCommand != MBX_HEARTBEAT) {
if (pmbox->vport) {
lpfc_debugfs_disc_trc(pmbox->vport,
LPFC_DISC_TRC_MBOX_VPORT,
"MBOX Send vport: cmd:x%x mb:x%x x%x",
- (uint32_t)mb->mbxCommand,
- mb->un.varWords[0], mb->un.varWords[1]);
+ (uint32_t)mbx->mbxCommand,
+ mbx->un.varWords[0], mbx->un.varWords[1]);
}
else {
lpfc_debugfs_disc_trc(phba->pport,
LPFC_DISC_TRC_MBOX,
"MBOX Send: cmd:x%x mb:x%x x%x",
- (uint32_t)mb->mbxCommand,
- mb->un.varWords[0], mb->un.varWords[1]);
+ (uint32_t)mbx->mbxCommand,
+ mbx->un.varWords[0], mbx->un.varWords[1]);
}
}
@@ -6194,58 +9616,56 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
evtctr = psli->slistat.mbox_event;
/* next set own bit for the adapter and copy over command word */
- mb->mbxOwner = OWN_CHIP;
+ mbx->mbxOwner = OWN_CHIP;
if (psli->sli_flag & LPFC_SLI_ACTIVE) {
/* Populate mbox extension offset word. */
if (pmbox->in_ext_byte_len || pmbox->out_ext_byte_len) {
- *(((uint32_t *)mb) + pmbox->mbox_offset_word)
+ *(((uint32_t *)mbx) + pmbox->mbox_offset_word)
= (uint8_t *)phba->mbox_ext
- (uint8_t *)phba->mbox;
}
/* Copy the mailbox extension data */
- if (pmbox->in_ext_byte_len && pmbox->context2) {
- lpfc_sli_pcimem_bcopy(pmbox->context2,
- (uint8_t *)phba->mbox_ext,
- pmbox->in_ext_byte_len);
+ if (pmbox->in_ext_byte_len && pmbox->ext_buf) {
+ lpfc_sli_pcimem_bcopy(pmbox->ext_buf,
+ (uint8_t *)phba->mbox_ext,
+ pmbox->in_ext_byte_len);
}
/* Copy command data to host SLIM area */
- lpfc_sli_pcimem_bcopy(mb, phba->mbox, MAILBOX_CMD_SIZE);
+ lpfc_sli_pcimem_bcopy(mbx, phba->mbox, MAILBOX_CMD_SIZE);
} else {
/* Populate mbox extension offset word. */
if (pmbox->in_ext_byte_len || pmbox->out_ext_byte_len)
- *(((uint32_t *)mb) + pmbox->mbox_offset_word)
+ *(((uint32_t *)mbx) + pmbox->mbox_offset_word)
= MAILBOX_HBA_EXT_OFFSET;
/* Copy the mailbox extension data */
- if (pmbox->in_ext_byte_len && pmbox->context2) {
+ if (pmbox->in_ext_byte_len && pmbox->ext_buf)
lpfc_memcpy_to_slim(phba->MBslimaddr +
MAILBOX_HBA_EXT_OFFSET,
- pmbox->context2, pmbox->in_ext_byte_len);
+ pmbox->ext_buf, pmbox->in_ext_byte_len);
- }
- if (mb->mbxCommand == MBX_CONFIG_PORT) {
+ if (mbx->mbxCommand == MBX_CONFIG_PORT)
/* copy command data into host mbox for cmpl */
- lpfc_sli_pcimem_bcopy(mb, phba->mbox, MAILBOX_CMD_SIZE);
- }
+ lpfc_sli_pcimem_bcopy(mbx, phba->mbox,
+ MAILBOX_CMD_SIZE);
/* First copy mbox command data to HBA SLIM, skip past first
word */
to_slim = phba->MBslimaddr + sizeof (uint32_t);
- lpfc_memcpy_to_slim(to_slim, &mb->un.varWords[0],
+ lpfc_memcpy_to_slim(to_slim, &mbx->un.varWords[0],
MAILBOX_CMD_SIZE - sizeof (uint32_t));
/* Next copy over first word, with mbxOwner set */
- ldata = *((uint32_t *)mb);
+ ldata = *((uint32_t *)mbx);
to_slim = phba->MBslimaddr;
writel(ldata, to_slim);
readl(to_slim); /* flush */
- if (mb->mbxCommand == MBX_CONFIG_PORT) {
+ if (mbx->mbxCommand == MBX_CONFIG_PORT)
/* switch over to host mailbox */
psli->sli_flag |= LPFC_SLI_ACTIVE;
- }
}
wmb();
@@ -6286,9 +9706,7 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
drvr_flag);
goto out_not_finished;
}
- timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba,
- mb->mbxCommand) *
- 1000) + jiffies;
+ timeout = secs_to_jiffies(lpfc_mbox_tmo_val(phba, pmbox)) + jiffies;
i = 0;
/* Wait for command to complete */
while (((word0 & OWN_CHIP) == OWN_CHIP) ||
@@ -6318,7 +9736,7 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
/* First copy command data */
word0 = *((uint32_t *)phba->mbox);
word0 = le32_to_cpu(word0);
- if (mb->mbxCommand == MBX_CONFIG_PORT) {
+ if (mbx->mbxCommand == MBX_CONFIG_PORT) {
MAILBOX_t *slimmb;
uint32_t slimword0;
/* Check real SLIM for any errors */
@@ -6345,20 +9763,22 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
if (psli->sli_flag & LPFC_SLI_ACTIVE) {
/* copy results back to user */
- lpfc_sli_pcimem_bcopy(phba->mbox, mb, MAILBOX_CMD_SIZE);
+ lpfc_sli_pcimem_bcopy(phba->mbox, mbx,
+ MAILBOX_CMD_SIZE);
/* Copy the mailbox extension data */
- if (pmbox->out_ext_byte_len && pmbox->context2) {
+ if (pmbox->out_ext_byte_len && pmbox->ext_buf) {
lpfc_sli_pcimem_bcopy(phba->mbox_ext,
- pmbox->context2,
+ pmbox->ext_buf,
pmbox->out_ext_byte_len);
}
} else {
/* First copy command data */
- lpfc_memcpy_from_slim(mb, phba->MBslimaddr,
- MAILBOX_CMD_SIZE);
+ lpfc_memcpy_from_slim(mbx, phba->MBslimaddr,
+ MAILBOX_CMD_SIZE);
/* Copy the mailbox extension data */
- if (pmbox->out_ext_byte_len && pmbox->context2) {
- lpfc_memcpy_from_slim(pmbox->context2,
+ if (pmbox->out_ext_byte_len && pmbox->ext_buf) {
+ lpfc_memcpy_from_slim(
+ pmbox->ext_buf,
phba->MBslimaddr +
MAILBOX_HBA_EXT_OFFSET,
pmbox->out_ext_byte_len);
@@ -6369,7 +9789,7 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
readl(phba->HAregaddr); /* flush */
psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
- status = mb->mbxStatus;
+ status = mbx->mbxStatus;
}
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
@@ -6399,27 +9819,58 @@ static int
lpfc_sli4_async_mbox_block(struct lpfc_hba *phba)
{
struct lpfc_sli *psli = &phba->sli;
- uint8_t actcmd = MBX_HEARTBEAT;
+ LPFC_MBOXQ_t *mboxq;
int rc = 0;
- unsigned long timeout;
+ unsigned long timeout = 0;
+ u32 sli_flag;
+ u8 cmd, subsys, opcode;
/* Mark the asynchronous mailbox command posting as blocked */
spin_lock_irq(&phba->hbalock);
psli->sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
- if (phba->sli.mbox_active)
- actcmd = phba->sli.mbox_active->u.mb.mbxCommand;
- spin_unlock_irq(&phba->hbalock);
/* Determine how long we might wait for the active mailbox
* command to be gracefully completed by firmware.
*/
- timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, actcmd) * 1000) +
- jiffies;
- /* Wait for the outstnading mailbox command to complete */
+ if (phba->sli.mbox_active)
+ timeout = secs_to_jiffies(lpfc_mbox_tmo_val(phba,
+ phba->sli.mbox_active)) + jiffies;
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Make sure the mailbox is really active */
+ if (timeout)
+ lpfc_sli4_process_missed_mbox_completions(phba);
+
+ /* Wait for the outstanding mailbox command to complete */
while (phba->sli.mbox_active) {
/* Check active mailbox complete status every 2ms */
msleep(2);
if (time_after(jiffies, timeout)) {
- /* Timeout, marked the outstanding cmd not complete */
+ /* Timeout, mark the outstanding cmd not complete */
+
+ /* Sanity check sli.mbox_active has not completed or
+ * cancelled from another context during last 2ms sleep,
+ * so take hbalock to be sure before logging.
+ */
+ spin_lock_irq(&phba->hbalock);
+ if (phba->sli.mbox_active) {
+ mboxq = phba->sli.mbox_active;
+ cmd = mboxq->u.mb.mbxCommand;
+ subsys = lpfc_sli_config_mbox_subsys_get(phba,
+ mboxq);
+ opcode = lpfc_sli_config_mbox_opcode_get(phba,
+ mboxq);
+ sli_flag = psli->sli_flag;
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2352 Mailbox command x%x "
+ "(x%x/x%x) sli_flag x%x could "
+ "not complete\n",
+ cmd, subsys, opcode,
+ sli_flag);
+ } else {
+ spin_unlock_irq(&phba->hbalock);
+ }
+
rc = 1;
break;
}
@@ -6465,11 +9916,62 @@ lpfc_sli4_async_mbox_unblock(struct lpfc_hba *phba)
psli->sli_flag &= ~LPFC_SLI_ASYNC_MBX_BLK;
spin_unlock_irq(&phba->hbalock);
- /* wake up worker thread to post asynchronlous mailbox command */
+ /* wake up worker thread to post asynchronous mailbox command */
lpfc_worker_wake_up(phba);
}
/**
+ * lpfc_sli4_wait_bmbx_ready - Wait for bootstrap mailbox register ready
+ * @phba: Pointer to HBA context object.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * The function waits for the bootstrap mailbox register ready bit from
+ * port for twice the regular mailbox command timeout value.
+ *
+ * 0 - no timeout on waiting for bootstrap mailbox register ready.
+ * MBXERR_ERROR - wait for bootstrap mailbox register timed out or port
+ * is in an unrecoverable state.
+ **/
+static int
+lpfc_sli4_wait_bmbx_ready(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ uint32_t db_ready;
+ unsigned long timeout;
+ struct lpfc_register bmbx_reg;
+ struct lpfc_register portstat_reg = {-1};
+
+ /* Sanity check - there is no point to wait if the port is in an
+ * unrecoverable state.
+ */
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
+ LPFC_SLI_INTF_IF_TYPE_2) {
+ if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
+ &portstat_reg.word0) ||
+ lpfc_sli4_unrecoverable_port(&portstat_reg)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3858 Skipping bmbx ready because "
+ "Port Status x%x\n",
+ portstat_reg.word0);
+ return MBXERR_ERROR;
+ }
+ }
+
+ timeout = secs_to_jiffies(lpfc_mbox_tmo_val(phba, mboxq)) + jiffies;
+
+ do {
+ bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr);
+ db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg);
+ if (!db_ready)
+ mdelay(2);
+
+ if (time_after(jiffies, timeout))
+ return MBXERR_ERROR;
+ } while (!db_ready);
+
+ return 0;
+}
+
+/**
* lpfc_sli4_post_sync_mbox - Post an SLI4 mailbox to the bootstrap mailbox
* @phba: Pointer to HBA context object.
* @mboxq: Pointer to mailbox object.
@@ -6490,15 +9992,12 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
int rc = MBX_SUCCESS;
unsigned long iflag;
- uint32_t db_ready;
uint32_t mcqe_status;
uint32_t mbx_cmnd;
- unsigned long timeout;
struct lpfc_sli *psli = &phba->sli;
struct lpfc_mqe *mb = &mboxq->u.mqe;
struct lpfc_bmbx_create *mbox_rgn;
struct dma_address *dma_address;
- struct lpfc_register bmbx_reg;
/*
* Only one mailbox can be active to the bootstrap mailbox region
@@ -6507,12 +10006,13 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
spin_lock_irqsave(&phba->hbalock, iflag);
if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "(%d):2532 Mailbox command x%x (x%x) "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "(%d):2532 Mailbox command x%x (x%x/x%x) "
"cannot issue Data: x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0,
mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
psli->sli_flag, MBX_POLL);
return MBXERR_ERROR;
}
@@ -6521,6 +10021,10 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
phba->sli.mbox_active = mboxq;
spin_unlock_irqrestore(&phba->hbalock, iflag);
+ /* wait for bootstrap mbox register for readyness */
+ rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq);
+ if (rc)
+ goto exit;
/*
* Initialize the bootstrap memory region to avoid stale data areas
* in the mailbox post. Then copy the caller's mailbox contents to
@@ -6528,53 +10032,36 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
*/
mbx_cmnd = bf_get(lpfc_mqe_command, mb);
memset(phba->sli4_hba.bmbx.avirt, 0, sizeof(struct lpfc_bmbx_create));
- lpfc_sli_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt,
- sizeof(struct lpfc_mqe));
+ lpfc_sli4_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt,
+ sizeof(struct lpfc_mqe));
/* Post the high mailbox dma address to the port and wait for ready. */
dma_address = &phba->sli4_hba.bmbx.dma_address;
writel(dma_address->addr_hi, phba->sli4_hba.BMBXregaddr);
- timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mbx_cmnd)
- * 1000) + jiffies;
- do {
- bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr);
- db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg);
- if (!db_ready)
- msleep(2);
-
- if (time_after(jiffies, timeout)) {
- rc = MBXERR_ERROR;
- goto exit;
- }
- } while (!db_ready);
+ /* wait for bootstrap mbox register for hi-address write done */
+ rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq);
+ if (rc)
+ goto exit;
/* Post the low mailbox dma address to the port. */
writel(dma_address->addr_lo, phba->sli4_hba.BMBXregaddr);
- timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mbx_cmnd)
- * 1000) + jiffies;
- do {
- bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr);
- db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg);
- if (!db_ready)
- msleep(2);
- if (time_after(jiffies, timeout)) {
- rc = MBXERR_ERROR;
- goto exit;
- }
- } while (!db_ready);
+ /* wait for bootstrap mbox register for low address write done */
+ rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq);
+ if (rc)
+ goto exit;
/*
* Read the CQ to ensure the mailbox has completed.
* If so, update the mailbox status so that the upper layers
* can complete the request normally.
*/
- lpfc_sli_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb,
- sizeof(struct lpfc_mqe));
+ lpfc_sli4_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb,
+ sizeof(struct lpfc_mqe));
mbox_rgn = (struct lpfc_bmbx_create *) phba->sli4_hba.bmbx.avirt;
- lpfc_sli_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe,
- sizeof(struct lpfc_mcqe));
+ lpfc_sli4_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe,
+ sizeof(struct lpfc_mcqe));
mcqe_status = bf_get(lpfc_mcqe_status, &mbox_rgn->mcqe);
/*
* When the CQE status indicates a failure and the mailbox status
@@ -6590,11 +10077,12 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_sli4_swap_str(phba, mboxq);
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
- "(%d):0356 Mailbox cmd x%x (x%x) Status x%x "
+ "(%d):0356 Mailbox cmd x%x (x%x/x%x) Status x%x "
"Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x x%x x%x"
" x%x x%x CQ: x%x x%x x%x x%x\n",
- mboxq->vport ? mboxq->vport->vpi : 0,
- mbx_cmnd, lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ mboxq->vport ? mboxq->vport->vpi : 0, mbx_cmnd,
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
bf_get(lpfc_mqe_status, mb),
mb->un.mb_words[0], mb->un.mb_words[1],
mb->un.mb_words[2], mb->un.mb_words[3],
@@ -6617,7 +10105,7 @@ exit:
/**
* lpfc_sli_issue_mbox_s4 - Issue an SLI4 mailbox command to firmware
* @phba: Pointer to HBA context object.
- * @pmbox: Pointer to mailbox object.
+ * @mboxq: Pointer to mailbox object.
* @flag: Flag indicating how the mailbox need to be processed.
*
* This function is called by discovery code and HBA management code to submit
@@ -6634,14 +10122,18 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
unsigned long iflags;
int rc;
+ /* dump from issue mailbox command if setup */
+ lpfc_idiag_mbxacc_dump_issue_mbox(phba, &mboxq->u.mb);
+
rc = lpfc_mbox_dev_check(phba);
if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "(%d):2544 Mailbox command x%x (x%x) "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "(%d):2544 Mailbox command x%x (x%x/x%x) "
"cannot issue Data: x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0,
mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
psli->sli_flag, flag);
goto out_not_finished;
}
@@ -6655,20 +10147,30 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
if (rc != MBX_SUCCESS)
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
"(%d):2541 Mailbox command x%x "
- "(x%x) cannot issue Data: x%x x%x\n",
+ "(x%x/x%x) failure: "
+ "mqe_sta: x%x mcqe_sta: x%x/x%x "
+ "Data: x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0,
mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba,
+ mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba,
+ mboxq),
+ bf_get(lpfc_mqe_status, &mboxq->u.mqe),
+ bf_get(lpfc_mcqe_status, &mboxq->mcqe),
+ bf_get(lpfc_mcqe_ext_status,
+ &mboxq->mcqe),
psli->sli_flag, flag);
return rc;
} else if (flag == MBX_POLL) {
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
"(%d):2542 Try to issue mailbox command "
- "x%x (x%x) synchronously ahead of async"
+ "x%x (x%x/x%x) synchronously ahead of async "
"mailbox command queue: x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0,
mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
psli->sli_flag, flag);
/* Try to block the asynchronous mailbox posting */
rc = lpfc_sli4_async_mbox_block(phba);
@@ -6676,32 +10178,39 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
/* Successfully blocked, now issue sync mbox cmd */
rc = lpfc_sli4_post_sync_mbox(phba, mboxq);
if (rc != MBX_SUCCESS)
- lpfc_printf_log(phba, KERN_ERR,
- LOG_MBOX | LOG_SLI,
- "(%d):2597 Mailbox command "
- "x%x (x%x) cannot issue "
- "Data: x%x x%x\n",
- mboxq->vport ?
- mboxq->vport->vpi : 0,
- mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba,
- mboxq),
- psli->sli_flag, flag);
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_MBOX | LOG_SLI,
+ "(%d):2597 Sync Mailbox command "
+ "x%x (x%x/x%x) failure: "
+ "mqe_sta: x%x mcqe_sta: x%x/x%x "
+ "Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli_config_mbox_subsys_get(phba,
+ mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba,
+ mboxq),
+ bf_get(lpfc_mqe_status, &mboxq->u.mqe),
+ bf_get(lpfc_mcqe_status, &mboxq->mcqe),
+ bf_get(lpfc_mcqe_ext_status,
+ &mboxq->mcqe),
+ psli->sli_flag, flag);
/* Unblock the async mailbox posting afterward */
lpfc_sli4_async_mbox_unblock(phba);
}
return rc;
}
- /* Now, interrupt mode asynchrous mailbox command */
+ /* Now, interrupt mode asynchronous mailbox command */
rc = lpfc_mbox_cmd_check(phba, mboxq);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "(%d):2543 Mailbox command x%x (x%x) "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "(%d):2543 Mailbox command x%x (x%x/x%x) "
"cannot issue Data: x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0,
mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
psli->sli_flag, flag);
goto out_not_finished;
}
@@ -6713,10 +10222,12 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
spin_unlock_irqrestore(&phba->hbalock, iflags);
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
"(%d):0354 Mbox cmd issue - Enqueue Data: "
- "x%x (x%x) x%x x%x x%x\n",
+ "x%x (x%x/x%x) x%x x%x x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0xffffff,
bf_get(lpfc_mqe_command, &mboxq->u.mqe),
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
+ mboxq->u.mb.un.varUnregLogin.rpi,
phba->pport->port_state,
psli->sli_flag, MBX_NOWAIT);
/* Wake up worker thread to transport mailbox command from head */
@@ -6762,7 +10273,7 @@ lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
}
if (unlikely(phba->sli.mbox_active)) {
spin_unlock_irqrestore(&phba->hbalock, iflags);
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0384 There is pending active mailbox cmd\n");
return MBX_NOT_FINISHED;
}
@@ -6793,13 +10304,14 @@ lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
/* Start timer for the mbox_tmo and log some mailbox post messages */
mod_timer(&psli->mbox_tmo, (jiffies +
- (HZ * lpfc_mbox_tmo_val(phba, mbx_cmnd))));
+ secs_to_jiffies(lpfc_mbox_tmo_val(phba, mboxq))));
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
- "(%d):0355 Mailbox cmd x%x (x%x) issue Data: "
+ "(%d):0355 Mailbox cmd x%x (x%x/x%x) issue Data: "
"x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0, mbx_cmnd,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
phba->pport->port_state, psli->sli_flag);
if (mbx_cmnd != MBX_HEARTBEAT) {
@@ -6822,12 +10334,13 @@ lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
/* Post the mailbox command to the port */
rc = lpfc_sli4_mq_put(phba->sli4_hba.mbx_wq, mqe);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "(%d):2533 Mailbox command x%x (x%x) "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "(%d):2533 Mailbox command x%x (x%x/x%x) "
"cannot issue Data: x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0,
mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ lpfc_sli_config_mbox_subsys_get(phba, mboxq),
+ lpfc_sli_config_mbox_opcode_get(phba, mboxq),
psli->sli_flag, MBX_NOWAIT);
goto out_not_finished;
}
@@ -6836,11 +10349,13 @@ lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
out_not_finished:
spin_lock_irqsave(&phba->hbalock, iflags);
- mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
- __lpfc_mbox_cmpl_put(phba, mboxq);
- /* Release the token */
- psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
- phba->sli.mbox_active = NULL;
+ if (phba->sli.mbox_active) {
+ mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+ __lpfc_mbox_cmpl_put(phba, mboxq);
+ /* Release the token */
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = NULL;
+ }
spin_unlock_irqrestore(&phba->hbalock, iflags);
return MBX_NOT_FINISHED;
@@ -6899,7 +10414,6 @@ lpfc_mbox_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
"1420 Invalid HBA PCI-device group: 0x%x\n",
dev_grp);
return -ENODEV;
- break;
}
return 0;
}
@@ -6910,7 +10424,8 @@ lpfc_mbox_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
* @pring: Pointer to driver SLI ring object.
* @piocb: Pointer to address of newly added command iocb.
*
- * This function is called with hbalock held to add a command
+ * This function is called with hbalock held for SLI3 ports or
+ * the ring lock held for SLI4 ports to add a command
* iocb to the txq when SLI layer cannot submit the command iocb
* to the ring.
**/
@@ -6918,9 +10433,12 @@ void
__lpfc_sli_ringtx_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *piocb)
{
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ lockdep_assert_held(&pring->ring_lock);
+ else
+ lockdep_assert_held(&phba->hbalock);
/* Insert the caller's iocb in the txq tail for later processing. */
list_add_tail(&piocb->list, &pring->txq);
- pring->txq_cnt++;
}
/**
@@ -6946,6 +10464,8 @@ lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
{
struct lpfc_iocbq * nextiocb;
+ lockdep_assert_held(&phba->hbalock);
+
nextiocb = lpfc_sli_ringtx_get(phba, pring);
if (!nextiocb) {
nextiocb = *piocb;
@@ -6983,13 +10503,14 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
{
struct lpfc_iocbq *nextiocb;
IOCB_t *iocb;
- struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
+ struct lpfc_sli_ring *pring = &phba->sli.sli3_ring[ring_number];
+
+ lockdep_assert_held(&phba->hbalock);
- if (piocb->iocb_cmpl && (!piocb->vport) &&
+ if (piocb->cmd_cmpl && (!piocb->vport) &&
(piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
(piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN)) {
- lpfc_printf_log(phba, KERN_ERR,
- LOG_SLI | LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1807 IOCB x%x failed. No vport\n",
piocb->iocb.ulpCommand);
dump_stack();
@@ -7002,7 +10523,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
return IOCB_ERROR;
/* If HBA has a deferred error attention, fail the iocb. */
- if (unlikely(phba->hba_flag & DEFER_ERATT))
+ if (unlikely(test_bit(DEFER_ERATT, &phba->hba_flag)))
return IOCB_ERROR;
/*
@@ -7024,25 +10545,15 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
* can be issued if the link is not up.
*/
switch (piocb->iocb.ulpCommand) {
- case CMD_GEN_REQUEST64_CR:
- case CMD_GEN_REQUEST64_CX:
- if (!(phba->sli.sli_flag & LPFC_MENLO_MAINT) ||
- (piocb->iocb.un.genreq64.w5.hcsw.Rctl !=
- FC_RCTL_DD_UNSOL_CMD) ||
- (piocb->iocb.un.genreq64.w5.hcsw.Type !=
- MENLO_TRANSPORT_TYPE))
-
- goto iocb_busy;
- break;
case CMD_QUE_RING_BUF_CN:
case CMD_QUE_RING_BUF64_CN:
/*
* For IOCBs, like QUE_RING_BUF, that have no rsp ring
- * completion, iocb_cmpl MUST be 0.
+ * completion, cmd_cmpl MUST be 0.
*/
- if (piocb->iocb_cmpl)
- piocb->iocb_cmpl = NULL;
- /*FALLTHROUGH*/
+ if (piocb->cmd_cmpl)
+ piocb->cmd_cmpl = NULL;
+ fallthrough;
case CMD_CREATE_XRI_CR:
case CMD_CLOSE_XRI_CN:
case CMD_CLOSE_XRI_CX:
@@ -7055,7 +10566,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
* For FCP commands, we must be in a state where we can process link
* attention events.
*/
- } else if (unlikely(pring->ringno == phba->sli.fcp_ring &&
+ } else if (unlikely(pring->ringno == LPFC_FCP_RING &&
!(phba->sli.sli_flag & LPFC_PROCESS_LA))) {
goto iocb_busy;
}
@@ -7088,494 +10599,111 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
}
/**
- * lpfc_sli4_bpl2sgl - Convert the bpl/bde to a sgl.
+ * __lpfc_sli_issue_fcp_io_s3 - SLI3 device for sending fcp io iocb
* @phba: Pointer to HBA context object.
+ * @ring_number: SLI ring number to issue wqe on.
* @piocb: Pointer to command iocb.
- * @sglq: Pointer to the scatter gather queue object.
+ * @flag: Flag indicating if this command can be put into txq.
*
- * This routine converts the bpl or bde that is in the IOCB
- * to a sgl list for the sli4 hardware. The physical address
- * of the bpl/bde is converted back to a virtual address.
- * If the IOCB contains a BPL then the list of BDE's is
- * converted to sli4_sge's. If the IOCB contains a single
- * BDE then it is converted to a single sli_sge.
- * The IOCB is still in cpu endianess so the contents of
- * the bpl can be used without byte swapping.
+ * __lpfc_sli_issue_fcp_io_s3 is wrapper function to invoke lockless func to
+ * send an iocb command to an HBA with SLI-3 interface spec.
*
- * Returns valid XRI = Success, NO_XRI = Failure.
-**/
-static uint16_t
-lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
- struct lpfc_sglq *sglq)
+ * This function takes the hbalock before invoking the lockless version.
+ * The function will return success after it successfully submit the wqe to
+ * firmware or after adding to the txq.
+ **/
+static int
+__lpfc_sli_issue_fcp_io_s3(struct lpfc_hba *phba, uint32_t ring_number,
+ struct lpfc_iocbq *piocb, uint32_t flag)
{
- uint16_t xritag = NO_XRI;
- struct ulp_bde64 *bpl = NULL;
- struct ulp_bde64 bde;
- struct sli4_sge *sgl = NULL;
- IOCB_t *icmd;
- int numBdes = 0;
- int i = 0;
- uint32_t offset = 0; /* accumulated offset in the sg request list */
- int inbound = 0; /* number of sg reply entries inbound from firmware */
-
- if (!piocbq || !sglq)
- return xritag;
-
- sgl = (struct sli4_sge *)sglq->sgl;
- icmd = &piocbq->iocb;
- if (icmd->un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
- numBdes = icmd->un.genreq64.bdl.bdeSize /
- sizeof(struct ulp_bde64);
- /* The addrHigh and addrLow fields within the IOCB
- * have not been byteswapped yet so there is no
- * need to swap them back.
- */
- bpl = (struct ulp_bde64 *)
- ((struct lpfc_dmabuf *)piocbq->context3)->virt;
-
- if (!bpl)
- return xritag;
+ unsigned long iflags;
+ int rc;
- for (i = 0; i < numBdes; i++) {
- /* Should already be byte swapped. */
- sgl->addr_hi = bpl->addrHigh;
- sgl->addr_lo = bpl->addrLow;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ rc = __lpfc_sli_issue_iocb_s3(phba, ring_number, piocb, flag);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
- sgl->word2 = le32_to_cpu(sgl->word2);
- if ((i+1) == numBdes)
- bf_set(lpfc_sli4_sge_last, sgl, 1);
- else
- bf_set(lpfc_sli4_sge_last, sgl, 0);
- /* swap the size field back to the cpu so we
- * can assign it to the sgl.
- */
- bde.tus.w = le32_to_cpu(bpl->tus.w);
- sgl->sge_len = cpu_to_le32(bde.tus.f.bdeSize);
- /* The offsets in the sgl need to be accumulated
- * separately for the request and reply lists.
- * The request is always first, the reply follows.
- */
- if (piocbq->iocb.ulpCommand == CMD_GEN_REQUEST64_CR) {
- /* add up the reply sg entries */
- if (bpl->tus.f.bdeFlags == BUFF_TYPE_BDE_64I)
- inbound++;
- /* first inbound? reset the offset */
- if (inbound == 1)
- offset = 0;
- bf_set(lpfc_sli4_sge_offset, sgl, offset);
- offset += bde.tus.f.bdeSize;
- }
- sgl->word2 = cpu_to_le32(sgl->word2);
- bpl++;
- sgl++;
- }
- } else if (icmd->un.genreq64.bdl.bdeFlags == BUFF_TYPE_BDE_64) {
- /* The addrHigh and addrLow fields of the BDE have not
- * been byteswapped yet so they need to be swapped
- * before putting them in the sgl.
- */
- sgl->addr_hi =
- cpu_to_le32(icmd->un.genreq64.bdl.addrHigh);
- sgl->addr_lo =
- cpu_to_le32(icmd->un.genreq64.bdl.addrLow);
- sgl->word2 = le32_to_cpu(sgl->word2);
- bf_set(lpfc_sli4_sge_last, sgl, 1);
- sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->sge_len =
- cpu_to_le32(icmd->un.genreq64.bdl.bdeSize);
- }
- return sglq->sli4_xritag;
+ return rc;
}
/**
- * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
+ * __lpfc_sli_issue_fcp_io_s4 - SLI4 device for sending fcp io wqe
* @phba: Pointer to HBA context object.
+ * @ring_number: SLI ring number to issue wqe on.
+ * @piocb: Pointer to command iocb.
+ * @flag: Flag indicating if this command can be put into txq.
*
- * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index
- * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock
- * held.
+ * __lpfc_sli_issue_fcp_io_s4 is used by other functions in the driver to issue
+ * an wqe command to an HBA with SLI-4 interface spec.
*
- * Return: index into SLI4 fast-path FCP queue index.
+ * This function is a lockless version. The function will return success
+ * after it successfully submit the wqe to firmware or after adding to the
+ * txq.
**/
-static uint32_t
-lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba)
+static int
+__lpfc_sli_issue_fcp_io_s4(struct lpfc_hba *phba, uint32_t ring_number,
+ struct lpfc_iocbq *piocb, uint32_t flag)
{
- ++phba->fcp_qidx;
- if (phba->fcp_qidx >= phba->cfg_fcp_wq_count)
- phba->fcp_qidx = 0;
+ struct lpfc_io_buf *lpfc_cmd = piocb->io_buf;
- return phba->fcp_qidx;
+ lpfc_prep_embed_io(phba, lpfc_cmd);
+ return lpfc_sli4_issue_wqe(phba, lpfc_cmd->hdwq, piocb);
}
-/**
- * lpfc_sli_iocb2wqe - Convert the IOCB to a work queue entry.
- * @phba: Pointer to HBA context object.
- * @piocb: Pointer to command iocb.
- * @wqe: Pointer to the work queue entry.
- *
- * This routine converts the iocb command to its Work Queue Entry
- * equivalent. The wqe pointer should not have any fields set when
- * this routine is called because it will memcpy over them.
- * This routine does not set the CQ_ID or the WQEC bits in the
- * wqe.
- *
- * Returns: 0 = Success, IOCB_ERROR = Failure.
- **/
-static int
-lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
- union lpfc_wqe *wqe)
-{
- uint32_t xmit_len = 0, total_len = 0;
- uint8_t ct = 0;
- uint32_t fip;
- uint32_t abort_tag;
- uint8_t command_type = ELS_COMMAND_NON_FIP;
- uint8_t cmnd;
- uint16_t xritag;
- uint16_t abrt_iotag;
- struct lpfc_iocbq *abrtiocbq;
- struct ulp_bde64 *bpl = NULL;
- uint32_t els_id = LPFC_ELS_ID_DEFAULT;
- int numBdes, i;
- struct ulp_bde64 bde;
- struct lpfc_nodelist *ndlp;
+void
+lpfc_prep_embed_io(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd)
+{
+ struct lpfc_iocbq *piocb = &lpfc_cmd->cur_iocbq;
+ union lpfc_wqe128 *wqe = &lpfc_cmd->cur_iocbq.wqe;
+ struct sli4_sge_le *sgl;
+ u32 type_size;
- fip = phba->hba_flag & HBA_FIP_SUPPORT;
- /* The fcp commands will set command type */
- if (iocbq->iocb_flag & LPFC_IO_FCP)
- command_type = FCP_COMMAND;
- else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK))
- command_type = ELS_COMMAND_FIP;
- else
- command_type = ELS_COMMAND_NON_FIP;
+ /* 128 byte wqe support here */
+ sgl = (struct sli4_sge_le *)lpfc_cmd->dma_sgl;
- /* Some of the fields are in the right position already */
- memcpy(wqe, &iocbq->iocb, sizeof(union lpfc_wqe));
- abort_tag = (uint32_t) iocbq->iotag;
- xritag = iocbq->sli4_xritag;
- wqe->generic.wqe_com.word7 = 0; /* The ct field has moved so reset */
- /* words0-2 bpl convert bde */
- if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
- numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize /
- sizeof(struct ulp_bde64);
- bpl = (struct ulp_bde64 *)
- ((struct lpfc_dmabuf *)iocbq->context3)->virt;
- if (!bpl)
- return IOCB_ERROR;
+ if (phba->fcp_embed_io) {
+ struct fcp_cmnd *fcp_cmnd;
+ u32 *ptr;
- /* Should already be byte swapped. */
- wqe->generic.bde.addrHigh = le32_to_cpu(bpl->addrHigh);
- wqe->generic.bde.addrLow = le32_to_cpu(bpl->addrLow);
- /* swap the size field back to the cpu so we
- * can assign it to the sgl.
- */
- wqe->generic.bde.tus.w = le32_to_cpu(bpl->tus.w);
- xmit_len = wqe->generic.bde.tus.f.bdeSize;
- total_len = 0;
- for (i = 0; i < numBdes; i++) {
- bde.tus.w = le32_to_cpu(bpl[i].tus.w);
- total_len += bde.tus.f.bdeSize;
- }
- } else
- xmit_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
+ fcp_cmnd = lpfc_cmd->fcp_cmnd;
- iocbq->iocb.ulpIoTag = iocbq->iotag;
- cmnd = iocbq->iocb.ulpCommand;
+ /* Word 0-2 - FCP_CMND */
+ type_size = le32_to_cpu(sgl->sge_len);
+ type_size |= ULP_BDE64_TYPE_BDE_IMMED;
+ wqe->generic.bde.tus.w = type_size;
+ wqe->generic.bde.addrHigh = 0;
+ wqe->generic.bde.addrLow = 72; /* Word 18 */
- switch (iocbq->iocb.ulpCommand) {
- case CMD_ELS_REQUEST64_CR:
- ndlp = (struct lpfc_nodelist *)iocbq->context1;
- if (!iocbq->iocb.ulpLe) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2007 Only Limited Edition cmd Format"
- " supported 0x%x\n",
- iocbq->iocb.ulpCommand);
- return IOCB_ERROR;
- }
- wqe->els_req.payload_len = xmit_len;
- /* Els_reguest64 has a TMO */
- bf_set(wqe_tmo, &wqe->els_req.wqe_com,
- iocbq->iocb.ulpTimeout);
- /* Need a VF for word 4 set the vf bit*/
- bf_set(els_req64_vf, &wqe->els_req, 0);
- /* And a VFID for word 12 */
- bf_set(els_req64_vfid, &wqe->els_req, 0);
- ct = ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l);
- bf_set(wqe_ctxt_tag, &wqe->els_req.wqe_com,
- iocbq->iocb.ulpContext);
- bf_set(wqe_ct, &wqe->els_req.wqe_com, ct);
- bf_set(wqe_pu, &wqe->els_req.wqe_com, 0);
- /* CCP CCPE PV PRI in word10 were set in the memcpy */
- if (command_type == ELS_COMMAND_FIP) {
- els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK)
- >> LPFC_FIP_ELS_ID_SHIFT);
- }
- bf_set(wqe_temp_rpi, &wqe->els_req.wqe_com,
- phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
- bf_set(wqe_els_id, &wqe->els_req.wqe_com, els_id);
- bf_set(wqe_dbde, &wqe->els_req.wqe_com, 1);
- bf_set(wqe_iod, &wqe->els_req.wqe_com, LPFC_WQE_IOD_READ);
- bf_set(wqe_qosd, &wqe->els_req.wqe_com, 1);
- bf_set(wqe_lenloc, &wqe->els_req.wqe_com, LPFC_WQE_LENLOC_NONE);
- bf_set(wqe_ebde_cnt, &wqe->els_req.wqe_com, 0);
- break;
- case CMD_XMIT_SEQUENCE64_CX:
- bf_set(wqe_ctxt_tag, &wqe->xmit_sequence.wqe_com,
- iocbq->iocb.un.ulpWord[3]);
- bf_set(wqe_rcvoxid, &wqe->xmit_sequence.wqe_com,
- iocbq->iocb.ulpContext);
- /* The entire sequence is transmitted for this IOCB */
- xmit_len = total_len;
- cmnd = CMD_XMIT_SEQUENCE64_CR;
- case CMD_XMIT_SEQUENCE64_CR:
- /* word3 iocb=io_tag32 wqe=reserved */
- wqe->xmit_sequence.rsvd3 = 0;
- /* word4 relative_offset memcpy */
- /* word5 r_ctl/df_ctl memcpy */
- bf_set(wqe_pu, &wqe->xmit_sequence.wqe_com, 0);
- bf_set(wqe_dbde, &wqe->xmit_sequence.wqe_com, 1);
- bf_set(wqe_iod, &wqe->xmit_sequence.wqe_com,
- LPFC_WQE_IOD_WRITE);
- bf_set(wqe_lenloc, &wqe->xmit_sequence.wqe_com,
- LPFC_WQE_LENLOC_WORD12);
- bf_set(wqe_ebde_cnt, &wqe->xmit_sequence.wqe_com, 0);
- wqe->xmit_sequence.xmit_len = xmit_len;
- command_type = OTHER_COMMAND;
- break;
- case CMD_XMIT_BCAST64_CN:
- /* word3 iocb=iotag32 wqe=seq_payload_len */
- wqe->xmit_bcast64.seq_payload_len = xmit_len;
- /* word4 iocb=rsvd wqe=rsvd */
- /* word5 iocb=rctl/type/df_ctl wqe=rctl/type/df_ctl memcpy */
- /* word6 iocb=ctxt_tag/io_tag wqe=ctxt_tag/xri */
- bf_set(wqe_ct, &wqe->xmit_bcast64.wqe_com,
- ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
- bf_set(wqe_dbde, &wqe->xmit_bcast64.wqe_com, 1);
- bf_set(wqe_iod, &wqe->xmit_bcast64.wqe_com, LPFC_WQE_IOD_WRITE);
- bf_set(wqe_lenloc, &wqe->xmit_bcast64.wqe_com,
- LPFC_WQE_LENLOC_WORD3);
- bf_set(wqe_ebde_cnt, &wqe->xmit_bcast64.wqe_com, 0);
- break;
- case CMD_FCP_IWRITE64_CR:
- command_type = FCP_COMMAND_DATA_OUT;
- /* word3 iocb=iotag wqe=payload_offset_len */
- /* Add the FCP_CMD and FCP_RSP sizes to get the offset */
- wqe->fcp_iwrite.payload_offset_len =
- xmit_len + sizeof(struct fcp_rsp);
- /* word4 iocb=parameter wqe=total_xfer_length memcpy */
- /* word5 iocb=initial_xfer_len wqe=initial_xfer_len memcpy */
- bf_set(wqe_erp, &wqe->fcp_iwrite.wqe_com,
- iocbq->iocb.ulpFCP2Rcvy);
- bf_set(wqe_lnk, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpXS);
- /* Always open the exchange */
- bf_set(wqe_xc, &wqe->fcp_iwrite.wqe_com, 0);
- bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1);
- bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_IOD_WRITE);
- bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com,
- LPFC_WQE_LENLOC_WORD4);
- bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0);
- bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpPU);
- break;
- case CMD_FCP_IREAD64_CR:
- /* word3 iocb=iotag wqe=payload_offset_len */
- /* Add the FCP_CMD and FCP_RSP sizes to get the offset */
- wqe->fcp_iread.payload_offset_len =
- xmit_len + sizeof(struct fcp_rsp);
- /* word4 iocb=parameter wqe=total_xfer_length memcpy */
- /* word5 iocb=initial_xfer_len wqe=initial_xfer_len memcpy */
- bf_set(wqe_erp, &wqe->fcp_iread.wqe_com,
- iocbq->iocb.ulpFCP2Rcvy);
- bf_set(wqe_lnk, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpXS);
- /* Always open the exchange */
- bf_set(wqe_xc, &wqe->fcp_iread.wqe_com, 0);
- bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1);
- bf_set(wqe_iod, &wqe->fcp_iread.wqe_com, LPFC_WQE_IOD_READ);
- bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com,
- LPFC_WQE_LENLOC_WORD4);
- bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0);
- bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpPU);
- break;
- case CMD_FCP_ICMND64_CR:
- /* word3 iocb=IO_TAG wqe=reserved */
- wqe->fcp_icmd.rsrvd3 = 0;
- bf_set(wqe_pu, &wqe->fcp_icmd.wqe_com, 0);
- /* Always open the exchange */
- bf_set(wqe_xc, &wqe->fcp_icmd.wqe_com, 0);
- bf_set(wqe_dbde, &wqe->fcp_icmd.wqe_com, 1);
- bf_set(wqe_iod, &wqe->fcp_icmd.wqe_com, LPFC_WQE_IOD_WRITE);
- bf_set(wqe_qosd, &wqe->fcp_icmd.wqe_com, 1);
- bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com,
- LPFC_WQE_LENLOC_NONE);
- bf_set(wqe_ebde_cnt, &wqe->fcp_icmd.wqe_com, 0);
- break;
- case CMD_GEN_REQUEST64_CR:
- /* For this command calculate the xmit length of the
- * request bde.
- */
- xmit_len = 0;
- numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize /
- sizeof(struct ulp_bde64);
- for (i = 0; i < numBdes; i++) {
- bde.tus.w = le32_to_cpu(bpl[i].tus.w);
- if (bde.tus.f.bdeFlags != BUFF_TYPE_BDE_64)
- break;
- xmit_len += bde.tus.f.bdeSize;
- }
- /* word3 iocb=IO_TAG wqe=request_payload_len */
- wqe->gen_req.request_payload_len = xmit_len;
- /* word4 iocb=parameter wqe=relative_offset memcpy */
- /* word5 [rctl, type, df_ctl, la] copied in memcpy */
- /* word6 context tag copied in memcpy */
- if (iocbq->iocb.ulpCt_h || iocbq->iocb.ulpCt_l) {
- ct = ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l);
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2015 Invalid CT %x command 0x%x\n",
- ct, iocbq->iocb.ulpCommand);
- return IOCB_ERROR;
- }
- bf_set(wqe_ct, &wqe->gen_req.wqe_com, 0);
- bf_set(wqe_tmo, &wqe->gen_req.wqe_com, iocbq->iocb.ulpTimeout);
- bf_set(wqe_pu, &wqe->gen_req.wqe_com, iocbq->iocb.ulpPU);
- bf_set(wqe_dbde, &wqe->gen_req.wqe_com, 1);
- bf_set(wqe_iod, &wqe->gen_req.wqe_com, LPFC_WQE_IOD_READ);
- bf_set(wqe_qosd, &wqe->gen_req.wqe_com, 1);
- bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE);
- bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0);
- command_type = OTHER_COMMAND;
- break;
- case CMD_XMIT_ELS_RSP64_CX:
- ndlp = (struct lpfc_nodelist *)iocbq->context1;
- /* words0-2 BDE memcpy */
- /* word3 iocb=iotag32 wqe=response_payload_len */
- wqe->xmit_els_rsp.response_payload_len = xmit_len;
- /* word4 iocb=did wge=rsvd. */
- wqe->xmit_els_rsp.rsvd4 = 0;
- /* word5 iocb=rsvd wge=did */
- bf_set(wqe_els_did, &wqe->xmit_els_rsp.wqe_dest,
- iocbq->iocb.un.elsreq64.remoteID);
- bf_set(wqe_ct, &wqe->xmit_els_rsp.wqe_com,
- ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
- bf_set(wqe_pu, &wqe->xmit_els_rsp.wqe_com, iocbq->iocb.ulpPU);
- bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com,
- iocbq->iocb.ulpContext);
- if (!iocbq->iocb.ulpCt_h && iocbq->iocb.ulpCt_l)
- bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
- phba->vpi_ids[iocbq->vport->vpi]);
- bf_set(wqe_dbde, &wqe->xmit_els_rsp.wqe_com, 1);
- bf_set(wqe_iod, &wqe->xmit_els_rsp.wqe_com, LPFC_WQE_IOD_WRITE);
- bf_set(wqe_qosd, &wqe->xmit_els_rsp.wqe_com, 1);
- bf_set(wqe_lenloc, &wqe->xmit_els_rsp.wqe_com,
- LPFC_WQE_LENLOC_WORD3);
- bf_set(wqe_ebde_cnt, &wqe->xmit_els_rsp.wqe_com, 0);
- bf_set(wqe_rsp_temp_rpi, &wqe->xmit_els_rsp,
- phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
- command_type = OTHER_COMMAND;
- break;
- case CMD_CLOSE_XRI_CN:
- case CMD_ABORT_XRI_CN:
- case CMD_ABORT_XRI_CX:
- /* words 0-2 memcpy should be 0 rserved */
- /* port will send abts */
- abrt_iotag = iocbq->iocb.un.acxri.abortContextTag;
- if (abrt_iotag != 0 && abrt_iotag <= phba->sli.last_iotag) {
- abrtiocbq = phba->sli.iocbq_lookup[abrt_iotag];
- fip = abrtiocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK;
- } else
- fip = 0;
+ bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 0);
- if ((iocbq->iocb.ulpCommand == CMD_CLOSE_XRI_CN) || fip)
- /*
- * The link is down, or the command was ELS_FIP
- * so the fw does not need to send abts
- * on the wire.
- */
- bf_set(abort_cmd_ia, &wqe->abort_cmd, 1);
- else
- bf_set(abort_cmd_ia, &wqe->abort_cmd, 0);
- bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG);
- /* word5 iocb=CONTEXT_TAG|IO_TAG wqe=reserved */
- wqe->abort_cmd.rsrvd5 = 0;
- bf_set(wqe_ct, &wqe->abort_cmd.wqe_com,
- ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
- abort_tag = iocbq->iocb.un.acxri.abortIoTag;
- /*
- * The abort handler will send us CMD_ABORT_XRI_CN or
- * CMD_CLOSE_XRI_CN and the fw only accepts CMD_ABORT_XRI_CX
- */
- bf_set(wqe_cmnd, &wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX);
- bf_set(wqe_qosd, &wqe->abort_cmd.wqe_com, 1);
- bf_set(wqe_lenloc, &wqe->abort_cmd.wqe_com,
- LPFC_WQE_LENLOC_NONE);
- cmnd = CMD_ABORT_XRI_CX;
- command_type = OTHER_COMMAND;
- xritag = 0;
- break;
- case CMD_XMIT_BLS_RSP64_CX:
- /* As BLS ABTS RSP WQE is very different from other WQEs,
- * we re-construct this WQE here based on information in
- * iocbq from scratch.
- */
- memset(wqe, 0, sizeof(union lpfc_wqe));
- /* OX_ID is invariable to who sent ABTS to CT exchange */
- bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp,
- bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_rsp));
- if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_rsp) ==
- LPFC_ABTS_UNSOL_INT) {
- /* ABTS sent by initiator to CT exchange, the
- * RX_ID field will be filled with the newly
- * allocated responder XRI.
- */
- bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
- iocbq->sli4_xritag);
- } else {
- /* ABTS sent by responder to CT exchange, the
- * RX_ID field will be filled with the responder
- * RX_ID from ABTS.
- */
- bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
- bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_rsp));
- }
- bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
- bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
- bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
- iocbq->iocb.ulpContext);
- bf_set(wqe_qosd, &wqe->xmit_bls_rsp.wqe_com, 1);
- bf_set(wqe_lenloc, &wqe->xmit_bls_rsp.wqe_com,
- LPFC_WQE_LENLOC_NONE);
- /* Overwrite the pre-set comnd type with OTHER_COMMAND */
- command_type = OTHER_COMMAND;
- if (iocbq->iocb.un.xseq64.w5.hcsw.Rctl == FC_RCTL_BA_RJT) {
- bf_set(xmit_bls_rsp64_rjt_vspec, &wqe->xmit_bls_rsp,
- bf_get(lpfc_vndr_code, &iocbq->iocb.un.bls_rsp));
- bf_set(xmit_bls_rsp64_rjt_expc, &wqe->xmit_bls_rsp,
- bf_get(lpfc_rsn_expln, &iocbq->iocb.un.bls_rsp));
- bf_set(xmit_bls_rsp64_rjt_rsnc, &wqe->xmit_bls_rsp,
- bf_get(lpfc_rsn_code, &iocbq->iocb.un.bls_rsp));
+ /* Word 18-29 FCP CMND Payload */
+ ptr = &wqe->words[18];
+ lpfc_sli_pcimem_bcopy(fcp_cmnd, ptr, le32_to_cpu(sgl->sge_len));
+ } else {
+ /* Word 0-2 - Inline BDE */
+ wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ wqe->generic.bde.tus.f.bdeSize = le32_to_cpu(sgl->sge_len);
+ wqe->generic.bde.addrHigh = le32_to_cpu(sgl->addr_hi);
+ wqe->generic.bde.addrLow = le32_to_cpu(sgl->addr_lo);
+
+ /* Word 10 */
+ bf_set(wqe_dbde, &wqe->generic.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->generic.wqe_com, 0);
+ }
+
+ /* add the VMID tags as per switch response */
+ if (unlikely(piocb->cmd_flag & LPFC_IO_VMID)) {
+ if (phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO) {
+ bf_set(wqe_ccpe, &wqe->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_ccp, &wqe->fcp_iwrite.wqe_com,
+ (piocb->vmid_tag.cs_ctl_vmid));
+ } else if (phba->cfg_vmid_app_header) {
+ bf_set(wqe_appid, &wqe->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1);
+ wqe->words[31] = piocb->vmid_tag.app_id;
}
-
- break;
- case CMD_XRI_ABORTED_CX:
- case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
- case CMD_IOCB_FCP_IBIDIR64_CR: /* bidirectional xfer */
- case CMD_FCP_TSEND64_CX: /* Target mode send xfer-ready */
- case CMD_FCP_TRSP64_CX: /* Target mode rcv */
- case CMD_FCP_AUTO_TRSP_CX: /* Auto target rsp */
- default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2014 Invalid command 0x%x\n",
- iocbq->iocb.ulpCommand);
- return IOCB_ERROR;
- break;
}
-
- bf_set(wqe_xri_tag, &wqe->generic.wqe_com, xritag);
- bf_set(wqe_reqtag, &wqe->generic.wqe_com, iocbq->iotag);
- wqe->generic.wqe_com.abort_tag = abort_tag;
- bf_set(wqe_cmd_type, &wqe->generic.wqe_com, command_type);
- bf_set(wqe_cmnd, &wqe->generic.wqe_com, cmnd);
- bf_set(wqe_class, &wqe->generic.wqe_com, iocbq->iocb.ulpClass);
- bf_set(wqe_cqid, &wqe->generic.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
- return 0;
}
/**
@@ -7588,7 +10716,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
* __lpfc_sli_issue_iocb_s4 is used by other functions in the driver to issue
* an iocb command to an HBA with SLI-4 interface spec.
*
- * This function is called with hbalock held. The function will return success
+ * This function is called with ringlock held. The function will return success
* after it successfully submit the iocb to firmware or after adding to the
* txq.
**/
@@ -7597,45 +10725,54 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *piocb, uint32_t flag)
{
struct lpfc_sglq *sglq;
- union lpfc_wqe wqe;
- struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
+ union lpfc_wqe128 *wqe;
+ struct lpfc_queue *wq;
+ struct lpfc_sli_ring *pring;
+ u32 ulp_command = get_job_cmnd(phba, piocb);
+
+ /* Get the WQ */
+ if ((piocb->cmd_flag & LPFC_IO_FCP) ||
+ (piocb->cmd_flag & LPFC_USE_FCPWQIDX)) {
+ wq = phba->sli4_hba.hdwq[piocb->hba_wqidx].io_wq;
+ } else {
+ wq = phba->sli4_hba.els_wq;
+ }
+ /* Get corresponding ring */
+ pring = wq->pring;
+
+ /*
+ * The WQE can be either 64 or 128 bytes,
+ */
+
+ lockdep_assert_held(&pring->ring_lock);
+ wqe = &piocb->wqe;
if (piocb->sli4_xritag == NO_XRI) {
- if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
- piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN ||
- piocb->iocb.ulpCommand == CMD_XMIT_BLS_RSP64_CX)
+ if (ulp_command == CMD_ABORT_XRI_CX)
sglq = NULL;
else {
- if (pring->txq_cnt) {
+ sglq = __lpfc_sli_get_els_sglq(phba, piocb);
+ if (!sglq) {
if (!(flag & SLI_IOCB_RET_IOCB)) {
__lpfc_sli_ringtx_put(phba,
- pring, piocb);
+ pring,
+ piocb);
return IOCB_SUCCESS;
} else {
return IOCB_BUSY;
}
- } else {
- sglq = __lpfc_sli_get_sglq(phba, piocb);
- if (!sglq) {
- if (!(flag & SLI_IOCB_RET_IOCB)) {
- __lpfc_sli_ringtx_put(phba,
- pring,
- piocb);
- return IOCB_SUCCESS;
- } else
- return IOCB_BUSY;
- }
}
}
- } else if (piocb->iocb_flag & LPFC_IO_FCP) {
+ } else if (piocb->cmd_flag & LPFC_IO_FCP) {
/* These IO's already have an XRI and a mapped sgl. */
sglq = NULL;
- } else {
+ }
+ else {
/*
* This is a continuation of a commandi,(CX) so this
* sglq is on the active list
*/
- sglq = __lpfc_get_active_sglq(phba, piocb->sli4_xritag);
+ sglq = __lpfc_get_active_sglq(phba, piocb->sli4_lxritag);
if (!sglq)
return IOCB_ERROR;
}
@@ -7643,45 +10780,60 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
if (sglq) {
piocb->sli4_lxritag = sglq->sli4_lxritag;
piocb->sli4_xritag = sglq->sli4_xritag;
- if (NO_XRI == lpfc_sli4_bpl2sgl(phba, piocb, sglq))
+
+ /* ABTS sent by initiator to CT exchange, the
+ * RX_ID field will be filled with the newly
+ * allocated responder XRI.
+ */
+ if (ulp_command == CMD_XMIT_BLS_RSP64_CX &&
+ piocb->abort_bls == LPFC_ABTS_UNSOL_INT)
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+ piocb->sli4_xritag);
+
+ bf_set(wqe_xri_tag, &wqe->generic.wqe_com,
+ piocb->sli4_xritag);
+
+ if (lpfc_wqe_bpl2sgl(phba, piocb, sglq) == NO_XRI)
return IOCB_ERROR;
}
- if (lpfc_sli4_iocb2wqe(phba, piocb, &wqe))
+ if (lpfc_sli4_wq_put(wq, wqe))
return IOCB_ERROR;
- if ((piocb->iocb_flag & LPFC_IO_FCP) ||
- (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
- /*
- * For FCP command IOCB, get a new WQ index to distribute
- * WQE across the WQsr. On the other hand, for abort IOCB,
- * it carries the same WQ index to the original command
- * IOCB.
- */
- if (piocb->iocb_flag & LPFC_IO_FCP)
- piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
- if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx],
- &wqe))
- return IOCB_ERROR;
- } else {
- if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
- return IOCB_ERROR;
- }
lpfc_sli_ringtxcmpl_put(phba, pring, piocb);
return 0;
}
-/**
+/*
+ * lpfc_sli_issue_fcp_io - Wrapper func for issuing fcp i/o
+ *
+ * This routine wraps the actual fcp i/o function for issusing WQE for sli-4
+ * or IOCB for sli-3 function.
+ * pointer from the lpfc_hba struct.
+ *
+ * Return codes:
+ * IOCB_ERROR - Error
+ * IOCB_SUCCESS - Success
+ * IOCB_BUSY - Busy
+ **/
+int
+lpfc_sli_issue_fcp_io(struct lpfc_hba *phba, uint32_t ring_number,
+ struct lpfc_iocbq *piocb, uint32_t flag)
+{
+ return phba->__lpfc_sli_issue_fcp_io(phba, ring_number, piocb, flag);
+}
+
+/*
* __lpfc_sli_issue_iocb - Wrapper func of lockless version for issuing iocb
*
* This routine wraps the actual lockless version for issusing IOCB function
* pointer from the lpfc_hba struct.
*
* Return codes:
- * IOCB_ERROR - Error
- * IOCB_SUCCESS - Success
- * IOCB_BUSY - Busy
+ * IOCB_ERROR - Error
+ * IOCB_SUCCESS - Success
+ * IOCB_BUSY - Busy
**/
int
__lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
@@ -7690,6 +10842,419 @@ __lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
return phba->__lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
}
+static void
+__lpfc_sli_prep_els_req_rsp_s3(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_vport *vport,
+ struct lpfc_dmabuf *bmp, u16 cmd_size, u32 did,
+ u32 elscmd, u8 tmo, u8 expect_rsp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ IOCB_t *cmd;
+
+ cmd = &cmdiocbq->iocb;
+ memset(cmd, 0, sizeof(*cmd));
+
+ cmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+ cmd->un.elsreq64.bdl.addrLow = putPaddrLow(bmp->phys);
+ cmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+
+ if (expect_rsp) {
+ cmd->un.elsreq64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));
+ cmd->un.elsreq64.remoteID = did; /* DID */
+ cmd->ulpCommand = CMD_ELS_REQUEST64_CR;
+ cmd->ulpTimeout = tmo;
+ } else {
+ cmd->un.elsreq64.bdl.bdeSize = sizeof(struct ulp_bde64);
+ cmd->un.genreq64.xmit_els_remoteID = did; /* DID */
+ cmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX;
+ cmd->ulpPU = PARM_NPIV_DID;
+ }
+ cmd->ulpBdeCount = 1;
+ cmd->ulpLe = 1;
+ cmd->ulpClass = CLASS3;
+
+ /* If we have NPIV enabled, we want to send ELS traffic by VPI. */
+ if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
+ if (expect_rsp) {
+ cmd->un.elsreq64.myID = vport->fc_myDID;
+
+ /* For ELS_REQUEST64_CR, use the VPI by default */
+ cmd->ulpContext = phba->vpi_ids[vport->vpi];
+ }
+
+ cmd->ulpCt_h = 0;
+ /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
+ if (elscmd == ELS_CMD_ECHO)
+ cmd->ulpCt_l = 0; /* context = invalid RPI */
+ else
+ cmd->ulpCt_l = 1; /* context = VPI */
+ }
+}
+
+static void
+__lpfc_sli_prep_els_req_rsp_s4(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_vport *vport,
+ struct lpfc_dmabuf *bmp, u16 cmd_size, u32 did,
+ u32 elscmd, u8 tmo, u8 expect_rsp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ union lpfc_wqe128 *wqe;
+ struct ulp_bde64_le *bde;
+ u8 els_id;
+
+ wqe = &cmdiocbq->wqe;
+ memset(wqe, 0, sizeof(*wqe));
+
+ /* Word 0 - 2 BDE */
+ bde = (struct ulp_bde64_le *)&wqe->generic.bde;
+ bde->addr_low = cpu_to_le32(putPaddrLow(bmp->phys));
+ bde->addr_high = cpu_to_le32(putPaddrHigh(bmp->phys));
+ bde->type_size = cpu_to_le32(cmd_size);
+ bde->type_size |= cpu_to_le32(ULP_BDE64_TYPE_BDE_64);
+
+ if (expect_rsp) {
+ bf_set(wqe_cmnd, &wqe->els_req.wqe_com, CMD_ELS_REQUEST64_WQE);
+
+ /* Transfer length */
+ wqe->els_req.payload_len = cmd_size;
+ wqe->els_req.max_response_payload_len = FCELSSIZE;
+
+ /* DID */
+ bf_set(wqe_els_did, &wqe->els_req.wqe_dest, did);
+
+ /* Word 11 - ELS_ID */
+ switch (elscmd) {
+ case ELS_CMD_PLOGI:
+ els_id = LPFC_ELS_ID_PLOGI;
+ break;
+ case ELS_CMD_FLOGI:
+ els_id = LPFC_ELS_ID_FLOGI;
+ break;
+ case ELS_CMD_LOGO:
+ els_id = LPFC_ELS_ID_LOGO;
+ break;
+ case ELS_CMD_FDISC:
+ if (!vport->fc_myDID) {
+ els_id = LPFC_ELS_ID_FDISC;
+ break;
+ }
+ fallthrough;
+ default:
+ els_id = LPFC_ELS_ID_DEFAULT;
+ break;
+ }
+
+ bf_set(wqe_els_id, &wqe->els_req.wqe_com, els_id);
+ } else {
+ /* DID */
+ bf_set(wqe_els_did, &wqe->xmit_els_rsp.wqe_dest, did);
+
+ /* Transfer length */
+ wqe->xmit_els_rsp.response_payload_len = cmd_size;
+
+ bf_set(wqe_cmnd, &wqe->xmit_els_rsp.wqe_com,
+ CMD_XMIT_ELS_RSP64_WQE);
+ }
+
+ bf_set(wqe_tmo, &wqe->generic.wqe_com, tmo);
+ bf_set(wqe_reqtag, &wqe->generic.wqe_com, cmdiocbq->iotag);
+ bf_set(wqe_class, &wqe->generic.wqe_com, CLASS3);
+
+ /* If we have NPIV enabled, we want to send ELS traffic by VPI.
+ * For SLI4, since the driver controls VPIs we also want to include
+ * all ELS pt2pt protocol traffic as well.
+ */
+ if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) ||
+ test_bit(FC_PT2PT, &vport->fc_flag)) {
+ if (expect_rsp) {
+ bf_set(els_req64_sid, &wqe->els_req, vport->fc_myDID);
+
+ /* For ELS_REQUEST64_WQE, use the VPI by default */
+ bf_set(wqe_ctxt_tag, &wqe->els_req.wqe_com,
+ phba->vpi_ids[vport->vpi]);
+ }
+
+ /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
+ if (elscmd == ELS_CMD_ECHO)
+ bf_set(wqe_ct, &wqe->generic.wqe_com, 0);
+ else
+ bf_set(wqe_ct, &wqe->generic.wqe_com, 1);
+ }
+}
+
+void
+lpfc_sli_prep_els_req_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
+ u16 cmd_size, u32 did, u32 elscmd, u8 tmo,
+ u8 expect_rsp)
+{
+ phba->__lpfc_sli_prep_els_req_rsp(cmdiocbq, vport, bmp, cmd_size, did,
+ elscmd, tmo, expect_rsp);
+}
+
+static void
+__lpfc_sli_prep_gen_req_s3(struct lpfc_iocbq *cmdiocbq, struct lpfc_dmabuf *bmp,
+ u16 rpi, u32 num_entry, u8 tmo)
+{
+ IOCB_t *cmd;
+
+ cmd = &cmdiocbq->iocb;
+ memset(cmd, 0, sizeof(*cmd));
+
+ cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+ cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
+ cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ cmd->un.genreq64.bdl.bdeSize = num_entry * sizeof(struct ulp_bde64);
+
+ cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+ cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
+ cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
+
+ cmd->ulpContext = rpi;
+ cmd->ulpClass = CLASS3;
+ cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
+ cmd->ulpBdeCount = 1;
+ cmd->ulpLe = 1;
+ cmd->ulpOwner = OWN_CHIP;
+ cmd->ulpTimeout = tmo;
+}
+
+static void
+__lpfc_sli_prep_gen_req_s4(struct lpfc_iocbq *cmdiocbq, struct lpfc_dmabuf *bmp,
+ u16 rpi, u32 num_entry, u8 tmo)
+{
+ union lpfc_wqe128 *cmdwqe;
+ struct ulp_bde64_le *bde, *bpl;
+ u32 xmit_len = 0, total_len = 0, size, type, i;
+
+ cmdwqe = &cmdiocbq->wqe;
+ memset(cmdwqe, 0, sizeof(*cmdwqe));
+
+ /* Calculate total_len and xmit_len */
+ bpl = (struct ulp_bde64_le *)bmp->virt;
+ for (i = 0; i < num_entry; i++) {
+ size = le32_to_cpu(bpl[i].type_size) & ULP_BDE64_SIZE_MASK;
+ total_len += size;
+ }
+ for (i = 0; i < num_entry; i++) {
+ size = le32_to_cpu(bpl[i].type_size) & ULP_BDE64_SIZE_MASK;
+ type = le32_to_cpu(bpl[i].type_size) & ULP_BDE64_TYPE_MASK;
+ if (type != ULP_BDE64_TYPE_BDE_64)
+ break;
+ xmit_len += size;
+ }
+
+ /* Words 0 - 2 */
+ bde = (struct ulp_bde64_le *)&cmdwqe->generic.bde;
+ bde->addr_low = bpl->addr_low;
+ bde->addr_high = bpl->addr_high;
+ bde->type_size = cpu_to_le32(xmit_len);
+ bde->type_size |= cpu_to_le32(ULP_BDE64_TYPE_BDE_64);
+
+ /* Word 3 */
+ cmdwqe->gen_req.request_payload_len = xmit_len;
+
+ /* Word 5 */
+ bf_set(wqe_type, &cmdwqe->gen_req.wge_ctl, FC_TYPE_CT);
+ bf_set(wqe_rctl, &cmdwqe->gen_req.wge_ctl, FC_RCTL_DD_UNSOL_CTL);
+ bf_set(wqe_si, &cmdwqe->gen_req.wge_ctl, 1);
+ bf_set(wqe_la, &cmdwqe->gen_req.wge_ctl, 1);
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &cmdwqe->gen_req.wqe_com, rpi);
+
+ /* Word 7 */
+ bf_set(wqe_tmo, &cmdwqe->gen_req.wqe_com, tmo);
+ bf_set(wqe_class, &cmdwqe->gen_req.wqe_com, CLASS3);
+ bf_set(wqe_cmnd, &cmdwqe->gen_req.wqe_com, CMD_GEN_REQUEST64_CR);
+ bf_set(wqe_ct, &cmdwqe->gen_req.wqe_com, SLI4_CT_RPI);
+
+ /* Word 12 */
+ cmdwqe->gen_req.max_response_payload_len = total_len - xmit_len;
+}
+
+void
+lpfc_sli_prep_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi, u32 num_entry, u8 tmo)
+{
+ phba->__lpfc_sli_prep_gen_req(cmdiocbq, bmp, rpi, num_entry, tmo);
+}
+
+static void
+__lpfc_sli_prep_xmit_seq64_s3(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi, u16 ox_id,
+ u32 num_entry, u8 rctl, u8 last_seq, u8 cr_cx_cmd)
+{
+ IOCB_t *icmd;
+
+ icmd = &cmdiocbq->iocb;
+ memset(icmd, 0, sizeof(*icmd));
+
+ icmd->un.xseq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+ icmd->un.xseq64.bdl.addrLow = putPaddrLow(bmp->phys);
+ icmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ icmd->un.xseq64.bdl.bdeSize = (num_entry * sizeof(struct ulp_bde64));
+ icmd->un.xseq64.w5.hcsw.Fctl = LA;
+ if (last_seq)
+ icmd->un.xseq64.w5.hcsw.Fctl |= LS;
+ icmd->un.xseq64.w5.hcsw.Dfctl = 0;
+ icmd->un.xseq64.w5.hcsw.Rctl = rctl;
+ icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT;
+
+ icmd->ulpBdeCount = 1;
+ icmd->ulpLe = 1;
+ icmd->ulpClass = CLASS3;
+
+ switch (cr_cx_cmd) {
+ case CMD_XMIT_SEQUENCE64_CR:
+ icmd->ulpContext = rpi;
+ icmd->ulpCommand = CMD_XMIT_SEQUENCE64_CR;
+ break;
+ case CMD_XMIT_SEQUENCE64_CX:
+ icmd->ulpContext = ox_id;
+ icmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+__lpfc_sli_prep_xmit_seq64_s4(struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi, u16 ox_id,
+ u32 full_size, u8 rctl, u8 last_seq, u8 cr_cx_cmd)
+{
+ union lpfc_wqe128 *wqe;
+ struct ulp_bde64 *bpl;
+
+ wqe = &cmdiocbq->wqe;
+ memset(wqe, 0, sizeof(*wqe));
+
+ /* Words 0 - 2 */
+ bpl = (struct ulp_bde64 *)bmp->virt;
+ wqe->xmit_sequence.bde.addrHigh = bpl->addrHigh;
+ wqe->xmit_sequence.bde.addrLow = bpl->addrLow;
+ wqe->xmit_sequence.bde.tus.w = bpl->tus.w;
+
+ /* Word 5 */
+ bf_set(wqe_ls, &wqe->xmit_sequence.wge_ctl, last_seq);
+ bf_set(wqe_la, &wqe->xmit_sequence.wge_ctl, 1);
+ bf_set(wqe_dfctl, &wqe->xmit_sequence.wge_ctl, 0);
+ bf_set(wqe_rctl, &wqe->xmit_sequence.wge_ctl, rctl);
+ bf_set(wqe_type, &wqe->xmit_sequence.wge_ctl, FC_TYPE_CT);
+
+ /* Word 6 */
+ bf_set(wqe_ctxt_tag, &wqe->xmit_sequence.wqe_com, rpi);
+
+ bf_set(wqe_cmnd, &wqe->xmit_sequence.wqe_com,
+ CMD_XMIT_SEQUENCE64_WQE);
+
+ /* Word 7 */
+ bf_set(wqe_class, &wqe->xmit_sequence.wqe_com, CLASS3);
+
+ /* Word 9 */
+ bf_set(wqe_rcvoxid, &wqe->xmit_sequence.wqe_com, ox_id);
+
+ if (cmdiocbq->cmd_flag & (LPFC_IO_LIBDFC | LPFC_IO_LOOPBACK)) {
+ /* Word 10 */
+ if (cmdiocbq->cmd_flag & LPFC_IO_VMID) {
+ bf_set(wqe_appid, &wqe->xmit_sequence.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->xmit_sequence.wqe_com, 1);
+ wqe->words[31] = LOOPBACK_SRC_APPID;
+ }
+
+ /* Word 12 */
+ wqe->xmit_sequence.xmit_len = full_size;
+ }
+ else
+ wqe->xmit_sequence.xmit_len =
+ wqe->xmit_sequence.bde.tus.f.bdeSize;
+}
+
+void
+lpfc_sli_prep_xmit_seq64(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_dmabuf *bmp, u16 rpi, u16 ox_id,
+ u32 num_entry, u8 rctl, u8 last_seq, u8 cr_cx_cmd)
+{
+ phba->__lpfc_sli_prep_xmit_seq64(cmdiocbq, bmp, rpi, ox_id, num_entry,
+ rctl, last_seq, cr_cx_cmd);
+}
+
+static void
+__lpfc_sli_prep_abort_xri_s3(struct lpfc_iocbq *cmdiocbq, u16 ulp_context,
+ u16 iotag, u8 ulp_class, u16 cqid, bool ia,
+ bool wqec)
+{
+ IOCB_t *icmd = NULL;
+
+ icmd = &cmdiocbq->iocb;
+ memset(icmd, 0, sizeof(*icmd));
+
+ /* Word 5 */
+ icmd->un.acxri.abortContextTag = ulp_context;
+ icmd->un.acxri.abortIoTag = iotag;
+
+ if (ia) {
+ /* Word 7 */
+ icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+ } else {
+ /* Word 3 */
+ icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
+
+ /* Word 7 */
+ icmd->ulpClass = ulp_class;
+ icmd->ulpCommand = CMD_ABORT_XRI_CN;
+ }
+
+ /* Word 7 */
+ icmd->ulpLe = 1;
+}
+
+static void
+__lpfc_sli_prep_abort_xri_s4(struct lpfc_iocbq *cmdiocbq, u16 ulp_context,
+ u16 iotag, u8 ulp_class, u16 cqid, bool ia,
+ bool wqec)
+{
+ union lpfc_wqe128 *wqe;
+
+ wqe = &cmdiocbq->wqe;
+ memset(wqe, 0, sizeof(*wqe));
+
+ /* Word 3 */
+ bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG);
+ if (ia)
+ bf_set(abort_cmd_ia, &wqe->abort_cmd, 1);
+ else
+ bf_set(abort_cmd_ia, &wqe->abort_cmd, 0);
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_WQE);
+
+ /* Word 8 */
+ wqe->abort_cmd.wqe_com.abort_tag = ulp_context;
+
+ /* Word 9 */
+ bf_set(wqe_reqtag, &wqe->abort_cmd.wqe_com, iotag);
+
+ /* Word 10 */
+ bf_set(wqe_qosd, &wqe->abort_cmd.wqe_com, 1);
+
+ /* Word 11 */
+ if (wqec)
+ bf_set(wqe_wqec, &wqe->abort_cmd.wqe_com, 1);
+ bf_set(wqe_cqid, &wqe->abort_cmd.wqe_com, cqid);
+ bf_set(wqe_cmd_type, &wqe->abort_cmd.wqe_com, OTHER_COMMAND);
+}
+
+void
+lpfc_sli_prep_abort_xri(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq,
+ u16 ulp_context, u16 iotag, u8 ulp_class, u16 cqid,
+ bool ia, bool wqec)
+{
+ phba->__lpfc_sli_prep_abort_xri(cmdiocbq, ulp_context, iotag, ulp_class,
+ cqid, ia, wqec);
+}
+
/**
* lpfc_sli_api_table_setup - Set up sli api function jump table
* @phba: The hba struct for which this call is being executed.
@@ -7707,26 +11272,94 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
case LPFC_PCI_DEV_LP:
phba->__lpfc_sli_issue_iocb = __lpfc_sli_issue_iocb_s3;
phba->__lpfc_sli_release_iocbq = __lpfc_sli_release_iocbq_s3;
+ phba->__lpfc_sli_issue_fcp_io = __lpfc_sli_issue_fcp_io_s3;
+ phba->__lpfc_sli_prep_els_req_rsp = __lpfc_sli_prep_els_req_rsp_s3;
+ phba->__lpfc_sli_prep_gen_req = __lpfc_sli_prep_gen_req_s3;
+ phba->__lpfc_sli_prep_xmit_seq64 = __lpfc_sli_prep_xmit_seq64_s3;
+ phba->__lpfc_sli_prep_abort_xri = __lpfc_sli_prep_abort_xri_s3;
break;
case LPFC_PCI_DEV_OC:
phba->__lpfc_sli_issue_iocb = __lpfc_sli_issue_iocb_s4;
phba->__lpfc_sli_release_iocbq = __lpfc_sli_release_iocbq_s4;
+ phba->__lpfc_sli_issue_fcp_io = __lpfc_sli_issue_fcp_io_s4;
+ phba->__lpfc_sli_prep_els_req_rsp = __lpfc_sli_prep_els_req_rsp_s4;
+ phba->__lpfc_sli_prep_gen_req = __lpfc_sli_prep_gen_req_s4;
+ phba->__lpfc_sli_prep_xmit_seq64 = __lpfc_sli_prep_xmit_seq64_s4;
+ phba->__lpfc_sli_prep_abort_xri = __lpfc_sli_prep_abort_xri_s4;
break;
default:
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1419 Invalid HBA PCI-device group: 0x%x\n",
dev_grp);
return -ENODEV;
- break;
}
- phba->lpfc_get_iocb_from_iocbq = lpfc_get_iocb_from_iocbq;
return 0;
}
/**
+ * lpfc_sli4_calc_ring - Calculates which ring to use
+ * @phba: Pointer to HBA context object.
+ * @piocb: Pointer to command iocb.
+ *
+ * For SLI4 only, FCP IO can deferred to one fo many WQs, based on
+ * hba_wqidx, thus we need to calculate the corresponding ring.
+ * Since ABORTS must go on the same WQ of the command they are
+ * aborting, we use command's hba_wqidx.
+ */
+struct lpfc_sli_ring *
+lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb)
+{
+ struct lpfc_io_buf *lpfc_cmd;
+
+ if (piocb->cmd_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) {
+ if (unlikely(!phba->sli4_hba.hdwq))
+ return NULL;
+ /*
+ * for abort iocb hba_wqidx should already
+ * be setup based on what work queue we used.
+ */
+ if (!(piocb->cmd_flag & LPFC_USE_FCPWQIDX)) {
+ lpfc_cmd = piocb->io_buf;
+ piocb->hba_wqidx = lpfc_cmd->hdwq_no;
+ }
+ return phba->sli4_hba.hdwq[piocb->hba_wqidx].io_wq->pring;
+ } else {
+ if (unlikely(!phba->sli4_hba.els_wq))
+ return NULL;
+ piocb->hba_wqidx = 0;
+ return phba->sli4_hba.els_wq->pring;
+ }
+}
+
+inline void lpfc_sli4_poll_eq(struct lpfc_queue *eq)
+{
+ struct lpfc_hba *phba = eq->phba;
+
+ /*
+ * Unlocking an irq is one of the entry point to check
+ * for re-schedule, but we are good for io submission
+ * path as midlayer does a get_cpu to glue us in. Flush
+ * out the invalidate queue so we can see the updated
+ * value for flag.
+ */
+ smp_rmb();
+
+ if (READ_ONCE(eq->mode) == LPFC_EQ_POLL)
+ /* We will not likely get the completion for the caller
+ * during this iteration but i guess that's fine.
+ * Future io's coming on this eq should be able to
+ * pick it up. As for the case of single io's, they
+ * will be handled through a sched from polling timer
+ * function which is currently triggered every 1msec.
+ */
+ lpfc_sli4_process_eq(phba, eq, LPFC_QUEUE_NOARM,
+ LPFC_QUEUE_WORK);
+}
+
+/**
* lpfc_sli_issue_iocb - Wrapper function for __lpfc_sli_issue_iocb
* @phba: Pointer to HBA context object.
- * @pring: Pointer to driver SLI ring object.
+ * @ring_number: Ring number
* @piocb: Pointer to command iocb.
* @flag: Flag indicating if this command can be put into txq.
*
@@ -7740,13 +11373,35 @@ int
lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *piocb, uint32_t flag)
{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_queue *eq;
unsigned long iflags;
int rc;
- spin_lock_irqsave(&phba->hbalock, iflags);
- rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ /* If the PCI channel is in offline state, do not post iocbs. */
+ if (unlikely(pci_channel_offline(phba->pcidev)))
+ return IOCB_ERROR;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_sli_prep_wqe(phba, piocb);
+ eq = phba->sli4_hba.hdwq[piocb->hba_wqidx].hba_eq;
+
+ pring = lpfc_sli4_calc_ring(phba, piocb);
+ if (unlikely(pring == NULL))
+ return IOCB_ERROR;
+
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+ lpfc_sli4_poll_eq(eq);
+ } else {
+ /* For now, SLI2/3 will still use hbalock */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
return rc;
}
@@ -7759,7 +11414,7 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
* only when driver needs to support target mode functionality
* or IP over FC functionalities.
*
- * This function is called with no lock held.
+ * This function is called with no lock held. SLI3 only.
**/
static int
lpfc_extra_ring_setup( struct lpfc_hba *phba)
@@ -7772,19 +11427,19 @@ lpfc_extra_ring_setup( struct lpfc_hba *phba)
/* Adjust cmd/rsp ring iocb entries more evenly */
/* Take some away from the FCP ring */
- pring = &psli->ring[psli->fcp_ring];
- pring->numCiocb -= SLI2_IOCB_CMD_R1XTRA_ENTRIES;
- pring->numRiocb -= SLI2_IOCB_RSP_R1XTRA_ENTRIES;
- pring->numCiocb -= SLI2_IOCB_CMD_R3XTRA_ENTRIES;
- pring->numRiocb -= SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+ pring = &psli->sli3_ring[LPFC_FCP_RING];
+ pring->sli.sli3.numCiocb -= SLI2_IOCB_CMD_R1XTRA_ENTRIES;
+ pring->sli.sli3.numRiocb -= SLI2_IOCB_RSP_R1XTRA_ENTRIES;
+ pring->sli.sli3.numCiocb -= SLI2_IOCB_CMD_R3XTRA_ENTRIES;
+ pring->sli.sli3.numRiocb -= SLI2_IOCB_RSP_R3XTRA_ENTRIES;
/* and give them to the extra ring */
- pring = &psli->ring[psli->extra_ring];
+ pring = &psli->sli3_ring[LPFC_EXTRA_RING];
- pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES;
- pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES;
- pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES;
- pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+ pring->sli.sli3.numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES;
+ pring->sli.sli3.numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES;
+ pring->sli.sli3.numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES;
+ pring->sli.sli3.numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES;
/* Setup default profile for this ring */
pring->iotag_max = 4096;
@@ -7796,6 +11451,123 @@ lpfc_extra_ring_setup( struct lpfc_hba *phba)
return 0;
}
+static void
+lpfc_sli_post_recovery_event(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp)
+{
+ unsigned long iflags;
+ struct lpfc_work_evt *evtp = &ndlp->recovery_evt;
+
+ /* Hold a node reference for outstanding queued work */
+ if (!lpfc_nlp_get(ndlp))
+ return;
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ if (!list_empty(&evtp->evt_listp)) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ lpfc_nlp_put(ndlp);
+ return;
+ }
+
+ evtp->evt_arg1 = ndlp;
+ evtp->evt = LPFC_EVT_RECOVER_PORT;
+ list_add_tail(&evtp->evt_listp, &phba->work_list);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ lpfc_worker_wake_up(phba);
+}
+
+/* lpfc_sli_abts_err_handler - handle a failed ABTS request from an SLI3 port.
+ * @phba: Pointer to HBA context object.
+ * @iocbq: Pointer to iocb object.
+ *
+ * The async_event handler calls this routine when it receives
+ * an ASYNC_STATUS_CN event from the port. The port generates
+ * this event when an Abort Sequence request to an rport fails
+ * twice in succession. The abort could be originated by the
+ * driver or by the port. The ABTS could have been for an ELS
+ * or FCP IO. The port only generates this event when an ABTS
+ * fails to complete after one retry.
+ */
+static void
+lpfc_sli_abts_err_handler(struct lpfc_hba *phba,
+ struct lpfc_iocbq *iocbq)
+{
+ struct lpfc_nodelist *ndlp = NULL;
+ uint16_t rpi = 0, vpi = 0;
+ struct lpfc_vport *vport = NULL;
+
+ /* The rpi in the ulpContext is vport-sensitive. */
+ vpi = iocbq->iocb.un.asyncstat.sub_ctxt_tag;
+ rpi = iocbq->iocb.ulpContext;
+
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3092 Port generated ABTS async event "
+ "on vpi %d rpi %d status 0x%x\n",
+ vpi, rpi, iocbq->iocb.ulpStatus);
+
+ vport = lpfc_find_vport_by_vpid(phba, vpi);
+ if (!vport)
+ goto err_exit;
+ ndlp = lpfc_findnode_rpi(vport, rpi);
+ if (!ndlp)
+ goto err_exit;
+
+ if (iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
+ lpfc_sli_abts_recover_port(vport, ndlp);
+ return;
+
+ err_exit:
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3095 Event Context not found, no "
+ "action on vpi %d rpi %d status 0x%x, reason 0x%x\n",
+ vpi, rpi, iocbq->iocb.ulpStatus,
+ iocbq->iocb.ulpContext);
+}
+
+/* lpfc_sli4_abts_err_handler - handle a failed ABTS request from an SLI4 port.
+ * @phba: pointer to HBA context object.
+ * @ndlp: nodelist pointer for the impacted rport.
+ * @axri: pointer to the wcqe containing the failed exchange.
+ *
+ * The driver calls this routine when it receives an ABORT_XRI_FCP CQE from the
+ * port. The port generates this event when an abort exchange request to an
+ * rport fails twice in succession with no reply. The abort could be originated
+ * by the driver or by the port. The ABTS could have been for an ELS or FCP IO.
+ */
+void
+lpfc_sli4_abts_err_handler(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp,
+ struct sli4_wcqe_xri_aborted *axri)
+{
+ uint32_t ext_status = 0;
+
+ if (!ndlp) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3115 Node Context not found, driver "
+ "ignoring abts err event\n");
+ return;
+ }
+
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3116 Port generated FCP XRI ABORT event on "
+ "vpi %d rpi %d xri x%x status 0x%x parameter x%x\n",
+ ndlp->vport->vpi, phba->sli4_hba.rpi_ids[ndlp->nlp_rpi],
+ bf_get(lpfc_wcqe_xa_xri, axri),
+ bf_get(lpfc_wcqe_xa_status, axri),
+ axri->parameter);
+
+ /*
+ * Catch the ABTS protocol failure case. Older OCe FW releases returned
+ * LOCAL_REJECT and 0 for a failed ABTS exchange and later OCe and
+ * LPe FW releases returned LOCAL_REJECT and SEQUENCE_TIMEOUT.
+ */
+ ext_status = axri->parameter & IOERR_PARAM_MASK;
+ if ((bf_get(lpfc_wcqe_xa_status, axri) == IOSTAT_LOCAL_REJECT) &&
+ ((ext_status == IOERR_SEQUENCE_TIMEOUT) || (ext_status == 0)))
+ lpfc_sli_post_recovery_event(phba, ndlp);
+}
+
/**
* lpfc_sli_async_event_handler - ASYNC iocb handler function
* @phba: Pointer to HBA context object.
@@ -7815,65 +11587,105 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
{
IOCB_t *icmd;
uint16_t evt_code;
- uint16_t temp;
struct temp_event temp_event_data;
struct Scsi_Host *shost;
uint32_t *iocb_w;
icmd = &iocbq->iocb;
evt_code = icmd->un.asyncstat.evt_code;
- temp = icmd->ulpContext;
- if ((evt_code != ASYNC_TEMP_WARN) &&
- (evt_code != ASYNC_TEMP_SAFE)) {
+ switch (evt_code) {
+ case ASYNC_TEMP_WARN:
+ case ASYNC_TEMP_SAFE:
+ temp_event_data.data = (uint32_t) icmd->ulpContext;
+ temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
+ if (evt_code == ASYNC_TEMP_WARN) {
+ temp_event_data.event_code = LPFC_THRESHOLD_TEMP;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0347 Adapter is very hot, please take "
+ "corrective action. temperature : %d Celsius\n",
+ (uint32_t) icmd->ulpContext);
+ } else {
+ temp_event_data.event_code = LPFC_NORMAL_TEMP;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0340 Adapter temperature is OK now. "
+ "temperature : %d Celsius\n",
+ (uint32_t) icmd->ulpContext);
+ }
+
+ /* Send temperature change event to applications */
+ shost = lpfc_shost_from_vport(phba->pport);
+ fc_host_post_vendor_event(shost, fc_get_event_number(),
+ sizeof(temp_event_data), (char *) &temp_event_data,
+ LPFC_NL_VENDOR_ID);
+ break;
+ case ASYNC_STATUS_CN:
+ lpfc_sli_abts_err_handler(phba, iocbq);
+ break;
+ default:
iocb_w = (uint32_t *) icmd;
- lpfc_printf_log(phba,
- KERN_ERR,
- LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0346 Ring %d handler: unexpected ASYNC_STATUS"
" evt_code 0x%x\n"
"W0 0x%08x W1 0x%08x W2 0x%08x W3 0x%08x\n"
"W4 0x%08x W5 0x%08x W6 0x%08x W7 0x%08x\n"
"W8 0x%08x W9 0x%08x W10 0x%08x W11 0x%08x\n"
"W12 0x%08x W13 0x%08x W14 0x%08x W15 0x%08x\n",
- pring->ringno,
- icmd->un.asyncstat.evt_code,
+ pring->ringno, icmd->un.asyncstat.evt_code,
iocb_w[0], iocb_w[1], iocb_w[2], iocb_w[3],
iocb_w[4], iocb_w[5], iocb_w[6], iocb_w[7],
iocb_w[8], iocb_w[9], iocb_w[10], iocb_w[11],
iocb_w[12], iocb_w[13], iocb_w[14], iocb_w[15]);
- return;
- }
- temp_event_data.data = (uint32_t)temp;
- temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
- if (evt_code == ASYNC_TEMP_WARN) {
- temp_event_data.event_code = LPFC_THRESHOLD_TEMP;
- lpfc_printf_log(phba,
- KERN_ERR,
- LOG_TEMP,
- "0347 Adapter is very hot, please take "
- "corrective action. temperature : %d Celsius\n",
- temp);
- }
- if (evt_code == ASYNC_TEMP_SAFE) {
- temp_event_data.event_code = LPFC_NORMAL_TEMP;
- lpfc_printf_log(phba,
- KERN_ERR,
- LOG_TEMP,
- "0340 Adapter temperature is OK now. "
- "temperature : %d Celsius\n",
- temp);
+ break;
}
+}
- /* Send temperature change event to applications */
- shost = lpfc_shost_from_vport(phba->pport);
- fc_host_post_vendor_event(shost, fc_get_event_number(),
- sizeof(temp_event_data), (char *) &temp_event_data,
- LPFC_NL_VENDOR_ID);
-}
+/**
+ * lpfc_sli4_setup - SLI ring setup function
+ * @phba: Pointer to HBA context object.
+ *
+ * lpfc_sli_setup sets up rings of the SLI interface with
+ * number of iocbs per ring and iotags. This function is
+ * called while driver attach to the HBA and before the
+ * interrupts are enabled. So there is no need for locking.
+ *
+ * This function always returns 0.
+ **/
+int
+lpfc_sli4_setup(struct lpfc_hba *phba)
+{
+ struct lpfc_sli_ring *pring;
+ pring = phba->sli4_hba.els_wq->pring;
+ pring->num_mask = LPFC_MAX_RING_MASK;
+ pring->prt[0].profile = 0; /* Mask 0 */
+ pring->prt[0].rctl = FC_RCTL_ELS_REQ;
+ pring->prt[0].type = FC_TYPE_ELS;
+ pring->prt[0].lpfc_sli_rcv_unsol_event =
+ lpfc_els_unsol_event;
+ pring->prt[1].profile = 0; /* Mask 1 */
+ pring->prt[1].rctl = FC_RCTL_ELS_REP;
+ pring->prt[1].type = FC_TYPE_ELS;
+ pring->prt[1].lpfc_sli_rcv_unsol_event =
+ lpfc_els_unsol_event;
+ pring->prt[2].profile = 0; /* Mask 2 */
+ /* NameServer Inquiry */
+ pring->prt[2].rctl = FC_RCTL_DD_UNSOL_CTL;
+ /* NameServer */
+ pring->prt[2].type = FC_TYPE_CT;
+ pring->prt[2].lpfc_sli_rcv_unsol_event =
+ lpfc_ct_unsol_event;
+ pring->prt[3].profile = 0; /* Mask 3 */
+ /* NameServer response */
+ pring->prt[3].rctl = FC_RCTL_DD_SOL_CTL;
+ /* NameServer */
+ pring->prt[3].type = FC_TYPE_CT;
+ pring->prt[3].lpfc_sli_rcv_unsol_event =
+ lpfc_ct_unsol_event;
+ return 0;
+}
/**
* lpfc_sli_setup - SLI ring setup function
@@ -7884,7 +11696,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
* called while driver attach to the HBA and before the
* interrupts are enabled. So there is no need for locking.
*
- * This function always returns 0.
+ * This function always returns 0. SLI3 only.
**/
int
lpfc_sli_setup(struct lpfc_hba *phba)
@@ -7893,31 +11705,32 @@ lpfc_sli_setup(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
- psli->num_rings = MAX_CONFIGURED_RINGS;
+ psli->num_rings = MAX_SLI3_CONFIGURED_RINGS;
psli->sli_flag = 0;
- psli->fcp_ring = LPFC_FCP_RING;
- psli->next_ring = LPFC_FCP_NEXT_RING;
- psli->extra_ring = LPFC_EXTRA_RING;
psli->iocbq_lookup = NULL;
psli->iocbq_lookup_len = 0;
psli->last_iotag = 0;
for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
+ pring = &psli->sli3_ring[i];
switch (i) {
case LPFC_FCP_RING: /* ring 0 - FCP */
/* numCiocb and numRiocb are used in config_port */
- pring->numCiocb = SLI2_IOCB_CMD_R0_ENTRIES;
- pring->numRiocb = SLI2_IOCB_RSP_R0_ENTRIES;
- pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES;
- pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES;
- pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES;
- pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES;
- pring->sizeCiocb = (phba->sli_rev == 3) ?
+ pring->sli.sli3.numCiocb = SLI2_IOCB_CMD_R0_ENTRIES;
+ pring->sli.sli3.numRiocb = SLI2_IOCB_RSP_R0_ENTRIES;
+ pring->sli.sli3.numCiocb +=
+ SLI2_IOCB_CMD_R1XTRA_ENTRIES;
+ pring->sli.sli3.numRiocb +=
+ SLI2_IOCB_RSP_R1XTRA_ENTRIES;
+ pring->sli.sli3.numCiocb +=
+ SLI2_IOCB_CMD_R3XTRA_ENTRIES;
+ pring->sli.sli3.numRiocb +=
+ SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+ pring->sli.sli3.sizeCiocb = (phba->sli_rev == 3) ?
SLI3_IOCB_CMD_SIZE :
SLI2_IOCB_CMD_SIZE;
- pring->sizeRiocb = (phba->sli_rev == 3) ?
+ pring->sli.sli3.sizeRiocb = (phba->sli_rev == 3) ?
SLI3_IOCB_RSP_SIZE :
SLI2_IOCB_RSP_SIZE;
pring->iotag_ctr = 0;
@@ -7928,12 +11741,12 @@ lpfc_sli_setup(struct lpfc_hba *phba)
break;
case LPFC_EXTRA_RING: /* ring 1 - EXTRA */
/* numCiocb and numRiocb are used in config_port */
- pring->numCiocb = SLI2_IOCB_CMD_R1_ENTRIES;
- pring->numRiocb = SLI2_IOCB_RSP_R1_ENTRIES;
- pring->sizeCiocb = (phba->sli_rev == 3) ?
+ pring->sli.sli3.numCiocb = SLI2_IOCB_CMD_R1_ENTRIES;
+ pring->sli.sli3.numRiocb = SLI2_IOCB_RSP_R1_ENTRIES;
+ pring->sli.sli3.sizeCiocb = (phba->sli_rev == 3) ?
SLI3_IOCB_CMD_SIZE :
SLI2_IOCB_CMD_SIZE;
- pring->sizeRiocb = (phba->sli_rev == 3) ?
+ pring->sli.sli3.sizeRiocb = (phba->sli_rev == 3) ?
SLI3_IOCB_RSP_SIZE :
SLI2_IOCB_RSP_SIZE;
pring->iotag_max = phba->cfg_hba_queue_depth;
@@ -7941,12 +11754,12 @@ lpfc_sli_setup(struct lpfc_hba *phba)
break;
case LPFC_ELS_RING: /* ring 2 - ELS / CT */
/* numCiocb and numRiocb are used in config_port */
- pring->numCiocb = SLI2_IOCB_CMD_R2_ENTRIES;
- pring->numRiocb = SLI2_IOCB_RSP_R2_ENTRIES;
- pring->sizeCiocb = (phba->sli_rev == 3) ?
+ pring->sli.sli3.numCiocb = SLI2_IOCB_CMD_R2_ENTRIES;
+ pring->sli.sli3.numRiocb = SLI2_IOCB_RSP_R2_ENTRIES;
+ pring->sli.sli3.sizeCiocb = (phba->sli_rev == 3) ?
SLI3_IOCB_CMD_SIZE :
SLI2_IOCB_CMD_SIZE;
- pring->sizeRiocb = (phba->sli_rev == 3) ?
+ pring->sli.sli3.sizeRiocb = (phba->sli_rev == 3) ?
SLI3_IOCB_RSP_SIZE :
SLI2_IOCB_RSP_SIZE;
pring->fast_iotag = 0;
@@ -7979,16 +11792,11 @@ lpfc_sli_setup(struct lpfc_hba *phba)
pring->prt[3].type = FC_TYPE_CT;
pring->prt[3].lpfc_sli_rcv_unsol_event =
lpfc_ct_unsol_event;
- /* abort unsolicited sequence */
- pring->prt[4].profile = 0; /* Mask 4 */
- pring->prt[4].rctl = FC_RCTL_BA_ABTS;
- pring->prt[4].type = FC_TYPE_BLS;
- pring->prt[4].lpfc_sli_rcv_unsol_event =
- lpfc_sli4_ct_abort_unsol_event;
break;
}
- totiocbsize += (pring->numCiocb * pring->sizeCiocb) +
- (pring->numRiocb * pring->sizeRiocb);
+ totiocbsize += (pring->sli.sli3.numCiocb *
+ pring->sli.sli3.sizeCiocb) +
+ (pring->sli.sli3.numRiocb * pring->sli.sli3.sizeRiocb);
}
if (totiocbsize > MAX_SLIM_IOCB_SIZE) {
/* Too many cmd / rsp ring entries in SLI2 SLIM */
@@ -8004,18 +11812,18 @@ lpfc_sli_setup(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_queue_setup - Queue initialization function
+ * lpfc_sli4_queue_init - Queue initialization function
* @phba: Pointer to HBA context object.
*
- * lpfc_sli_queue_setup sets up mailbox queues and iocb queues for each
+ * lpfc_sli4_queue_init sets up mailbox queues and iocb queues for each
* ring. This function also initializes ring indices of each ring.
* This function is called during the initialization of the SLI
* interface of an HBA.
* This function is called with no lock held and always returns
* 1.
**/
-int
-lpfc_sli_queue_setup(struct lpfc_hba *phba)
+void
+lpfc_sli4_queue_init(struct lpfc_hba *phba)
{
struct lpfc_sli *psli;
struct lpfc_sli_ring *pring;
@@ -8026,20 +11834,77 @@ lpfc_sli_queue_setup(struct lpfc_hba *phba)
INIT_LIST_HEAD(&psli->mboxq);
INIT_LIST_HEAD(&psli->mboxq_cmpl);
/* Initialize list headers for txq and txcmplq as double linked lists */
- for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
- pring->ringno = i;
- pring->next_cmdidx = 0;
- pring->local_getidx = 0;
- pring->cmdidx = 0;
+ for (i = 0; i < phba->cfg_hdw_queue; i++) {
+ pring = phba->sli4_hba.hdwq[i].io_wq->pring;
+ pring->flag = 0;
+ pring->ringno = LPFC_FCP_RING;
+ pring->txcmplq_cnt = 0;
+ INIT_LIST_HEAD(&pring->txq);
+ INIT_LIST_HEAD(&pring->txcmplq);
+ INIT_LIST_HEAD(&pring->iocb_continueq);
+ spin_lock_init(&pring->ring_lock);
+ }
+ pring = phba->sli4_hba.els_wq->pring;
+ pring->flag = 0;
+ pring->ringno = LPFC_ELS_RING;
+ pring->txcmplq_cnt = 0;
+ INIT_LIST_HEAD(&pring->txq);
+ INIT_LIST_HEAD(&pring->txcmplq);
+ INIT_LIST_HEAD(&pring->iocb_continueq);
+ spin_lock_init(&pring->ring_lock);
+
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ pring = phba->sli4_hba.nvmels_wq->pring;
+ pring->flag = 0;
+ pring->ringno = LPFC_ELS_RING;
+ pring->txcmplq_cnt = 0;
INIT_LIST_HEAD(&pring->txq);
INIT_LIST_HEAD(&pring->txcmplq);
INIT_LIST_HEAD(&pring->iocb_continueq);
+ spin_lock_init(&pring->ring_lock);
+ }
+
+ spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli_queue_init - Queue initialization function
+ * @phba: Pointer to HBA context object.
+ *
+ * lpfc_sli_queue_init sets up mailbox queues and iocb queues for each
+ * ring. This function also initializes ring indices of each ring.
+ * This function is called during the initialization of the SLI
+ * interface of an HBA.
+ * This function is called with no lock held and always returns
+ * 1.
+ **/
+void
+lpfc_sli_queue_init(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli;
+ struct lpfc_sli_ring *pring;
+ int i;
+
+ psli = &phba->sli;
+ spin_lock_irq(&phba->hbalock);
+ INIT_LIST_HEAD(&psli->mboxq);
+ INIT_LIST_HEAD(&psli->mboxq_cmpl);
+ /* Initialize list headers for txq and txcmplq as double linked lists */
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->sli3_ring[i];
+ pring->ringno = i;
+ pring->sli.sli3.next_cmdidx = 0;
+ pring->sli.sli3.local_getidx = 0;
+ pring->sli.sli3.cmdidx = 0;
+ INIT_LIST_HEAD(&pring->iocb_continueq);
INIT_LIST_HEAD(&pring->iocb_continue_saveq);
INIT_LIST_HEAD(&pring->postbufq);
+ pring->flag = 0;
+ INIT_LIST_HEAD(&pring->txq);
+ INIT_LIST_HEAD(&pring->txcmplq);
+ spin_lock_init(&pring->ring_lock);
}
spin_unlock_irq(&phba->hbalock);
- return 1;
}
/**
@@ -8065,8 +11930,12 @@ lpfc_sli_mbox_sys_flush(struct lpfc_hba *phba)
LPFC_MBOXQ_t *pmb;
unsigned long iflag;
+ /* Disable softirqs, including timers from obtaining phba->hbalock */
+ local_bh_disable();
+
/* Flush all the mailbox commands in the mbox system */
spin_lock_irqsave(&phba->hbalock, iflag);
+
/* The pending mailbox command queue */
list_splice_init(&phba->sli.mboxq, &completions);
/* The outstanding active mailbox command */
@@ -8079,6 +11948,9 @@ lpfc_sli_mbox_sys_flush(struct lpfc_hba *phba)
list_splice_init(&phba->sli.mboxq_cmpl, &completions);
spin_unlock_irqrestore(&phba->hbalock, iflag);
+ /* Enable softirqs again, done with phba->hbalock */
+ local_bh_enable();
+
/* Return all flushed mailbox commands with MBX_NOT_FINISHED status */
while (!list_empty(&completions)) {
list_remove_head(&completions, pmb, LPFC_MBOXQ_t, list);
@@ -8111,6 +11983,7 @@ lpfc_sli_host_down(struct lpfc_vport *vport)
LIST_HEAD(completions);
struct lpfc_hba *phba = vport->phba;
struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_queue *qp = NULL;
struct lpfc_sli_ring *pring;
struct lpfc_iocbq *iocb, *next_iocb;
int i;
@@ -8120,39 +11993,71 @@ lpfc_sli_host_down(struct lpfc_vport *vport)
lpfc_cleanup_discovery_resources(vport);
spin_lock_irqsave(&phba->hbalock, flags);
- for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
- prev_pring_flag = pring->flag;
- /* Only slow rings */
- if (pring->ringno == LPFC_ELS_RING) {
- pring->flag |= LPFC_DEFERRED_RING_EVENT;
- /* Set the lpfc data pending flag */
- set_bit(LPFC_DATA_READY, &phba->data_flags);
- }
- /*
- * Error everything on the txq since these iocbs have not been
- * given to the FW yet.
- */
- list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
- if (iocb->vport != vport)
- continue;
- list_move_tail(&iocb->list, &completions);
- pring->txq_cnt--;
- }
- /* Next issue ABTS for everything on the txcmplq */
- list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq,
- list) {
- if (iocb->vport != vport)
+ /*
+ * Error everything on the txq since these iocbs
+ * have not been given to the FW yet.
+ * Also issue ABTS for everything on the txcmplq
+ */
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->sli3_ring[i];
+ prev_pring_flag = pring->flag;
+ /* Only slow rings */
+ if (pring->ringno == LPFC_ELS_RING) {
+ pring->flag |= LPFC_DEFERRED_RING_EVENT;
+ /* Set the lpfc data pending flag */
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
+ }
+ list_for_each_entry_safe(iocb, next_iocb,
+ &pring->txq, list) {
+ if (iocb->vport != vport)
+ continue;
+ list_move_tail(&iocb->list, &completions);
+ }
+ list_for_each_entry_safe(iocb, next_iocb,
+ &pring->txcmplq, list) {
+ if (iocb->vport != vport)
+ continue;
+ lpfc_sli_issue_abort_iotag(phba, pring, iocb,
+ NULL);
+ }
+ pring->flag = prev_pring_flag;
+ }
+ } else {
+ list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
+ pring = qp->pring;
+ if (!pring)
continue;
- lpfc_sli_issue_abort_iotag(phba, pring, iocb);
+ if (pring == phba->sli4_hba.els_wq->pring) {
+ pring->flag |= LPFC_DEFERRED_RING_EVENT;
+ /* Set the lpfc data pending flag */
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
+ }
+ prev_pring_flag = pring->flag;
+ spin_lock(&pring->ring_lock);
+ list_for_each_entry_safe(iocb, next_iocb,
+ &pring->txq, list) {
+ if (iocb->vport != vport)
+ continue;
+ list_move_tail(&iocb->list, &completions);
+ }
+ spin_unlock(&pring->ring_lock);
+ list_for_each_entry_safe(iocb, next_iocb,
+ &pring->txcmplq, list) {
+ if (iocb->vport != vport)
+ continue;
+ lpfc_sli_issue_abort_iotag(phba, pring, iocb,
+ NULL);
+ }
+ pring->flag = prev_pring_flag;
}
-
- pring->flag = prev_pring_flag;
}
-
spin_unlock_irqrestore(&phba->hbalock, flags);
+ /* Make sure HBA is alive */
+ lpfc_issue_hb_tmo(phba);
+
/* Cancel all the IOCBs from the completions list */
lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
IOERR_SLI_DOWN);
@@ -8179,35 +12084,53 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
{
LIST_HEAD(completions);
struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_queue *qp = NULL;
struct lpfc_sli_ring *pring;
struct lpfc_dmabuf *buf_ptr;
unsigned long flags = 0;
int i;
/* Shutdown the mailbox command sub-system */
- lpfc_sli_mbox_sys_shutdown(phba);
+ lpfc_sli_mbox_sys_shutdown(phba, LPFC_MBX_WAIT);
lpfc_hba_down_prep(phba);
+ /* Disable softirqs, including timers from obtaining phba->hbalock */
+ local_bh_disable();
+
lpfc_fabric_abort_hba(phba);
spin_lock_irqsave(&phba->hbalock, flags);
- for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
- /* Only slow rings */
- if (pring->ringno == LPFC_ELS_RING) {
- pring->flag |= LPFC_DEFERRED_RING_EVENT;
- /* Set the lpfc data pending flag */
- set_bit(LPFC_DATA_READY, &phba->data_flags);
- }
-
- /*
- * Error everything on the txq since these iocbs have not been
- * given to the FW yet.
- */
- list_splice_init(&pring->txq, &completions);
- pring->txq_cnt = 0;
+ /*
+ * Error everything on the txq since these iocbs
+ * have not been given to the FW yet.
+ */
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->sli3_ring[i];
+ /* Only slow rings */
+ if (pring->ringno == LPFC_ELS_RING) {
+ pring->flag |= LPFC_DEFERRED_RING_EVENT;
+ /* Set the lpfc data pending flag */
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
+ }
+ list_splice_init(&pring->txq, &completions);
+ }
+ } else {
+ list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
+ pring = qp->pring;
+ if (!pring)
+ continue;
+ spin_lock(&pring->ring_lock);
+ list_splice_init(&pring->txq, &completions);
+ spin_unlock(&pring->ring_lock);
+ if (pring == phba->sli4_hba.els_wq->pring) {
+ pring->flag |= LPFC_DEFERRED_RING_EVENT;
+ /* Set the lpfc data pending flag */
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
+ }
+ }
}
spin_unlock_irqrestore(&phba->hbalock, flags);
@@ -8228,8 +12151,11 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
kfree(buf_ptr);
}
+ /* Enable softirqs again, done with phba->hbalock */
+ local_bh_enable();
+
/* Return any active mbox cmds */
- del_timer_sync(&psli->mbox_tmo);
+ timer_delete_sync(&psli->mbox_tmo);
spin_lock_irqsave(&phba->pport->work_port_lock, flags);
phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
@@ -8377,9 +12303,9 @@ lpfc_sli_ring_taggedbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
spin_unlock_irq(&phba->hbalock);
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0402 Cannot find virtual addr for buffer tag on "
- "ring %d Data x%lx x%p x%p x%x\n",
+ "ring %d Data x%lx x%px x%px x%x\n",
pring->ringno, (unsigned long) tag,
slp->next, slp->prev, pring->postbufq_cnt);
@@ -8421,9 +12347,9 @@ lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
spin_unlock_irq(&phba->hbalock);
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0410 Cannot find virtual addr for mapped buf on "
- "ring %d Data x%llx x%p x%p x%x\n",
+ "ring %d Data x%llx x%px x%px x%x\n",
pring->ringno, (unsigned long long)phys,
slp->next, slp->prev, pring->postbufq_cnt);
return NULL;
@@ -8444,93 +12370,33 @@ static void
lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- IOCB_t *irsp = &rspiocb->iocb;
- uint16_t abort_iotag, abort_context;
- struct lpfc_iocbq *abort_iocb;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
-
- abort_iocb = NULL;
-
- if (irsp->ulpStatus) {
- abort_context = cmdiocb->iocb.un.acxri.abortContextTag;
- abort_iotag = cmdiocb->iocb.un.acxri.abortIoTag;
-
- spin_lock_irq(&phba->hbalock);
- if (phba->sli_rev < LPFC_SLI_REV4) {
- if (abort_iotag != 0 &&
- abort_iotag <= phba->sli.last_iotag)
- abort_iocb =
- phba->sli.iocbq_lookup[abort_iotag];
- } else
- /* For sli4 the abort_tag is the XRI,
- * so the abort routine puts the iotag of the iocb
- * being aborted in the context field of the abort
- * IOCB.
- */
- abort_iocb = phba->sli.iocbq_lookup[abort_context];
+ u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
+ u32 ulp_word4 = get_job_word4(phba, rspiocb);
+ u8 cmnd = get_job_cmnd(phba, cmdiocb);
+ if (ulp_status) {
/*
- * If the iocb is not found in Firmware queue the iocb
- * might have completed already. Do not free it again.
+ * Assume that the port already completed and returned, or
+ * will return the iocb. Just Log the message.
*/
- if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- if (irsp->un.ulpWord[4] != IOERR_NO_XRI) {
- spin_unlock_irq(&phba->hbalock);
- lpfc_sli_release_iocbq(phba, cmdiocb);
- return;
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ if (cmnd == CMD_ABORT_XRI_CX &&
+ ulp_status == IOSTAT_LOCAL_REJECT &&
+ ulp_word4 == IOERR_ABORT_REQUESTED) {
+ goto release_iocb;
}
- /* For SLI4 the ulpContext field for abort IOCB
- * holds the iotag of the IOCB being aborted so
- * the local abort_context needs to be reset to
- * match the aborted IOCBs ulpContext.
- */
- if (abort_iocb && phba->sli_rev == LPFC_SLI_REV4)
- abort_context = abort_iocb->iocb.ulpContext;
}
-
- lpfc_printf_log(phba, KERN_WARNING, LOG_ELS | LOG_SLI,
- "0327 Cannot abort els iocb %p "
- "with tag %x context %x, abort status %x, "
- "abort code %x\n",
- abort_iocb, abort_iotag, abort_context,
- irsp->ulpStatus, irsp->un.ulpWord[4]);
- /*
- * make sure we have the right iocbq before taking it
- * off the txcmplq and try to call completion routine.
- */
- if (!abort_iocb ||
- abort_iocb->iocb.ulpContext != abort_context ||
- (abort_iocb->iocb_flag & LPFC_DRIVER_ABORTED) == 0)
- spin_unlock_irq(&phba->hbalock);
- else if (phba->sli_rev < LPFC_SLI_REV4) {
- /*
- * leave the SLI4 aborted command on the txcmplq
- * list and the command complete WCQE's XB bit
- * will tell whether the SGL (XRI) can be released
- * immediately or to the aborted SGL list for the
- * following abort XRI from the HBA.
- */
- list_del_init(&abort_iocb->list);
- if (abort_iocb->iocb_flag & LPFC_IO_ON_Q) {
- abort_iocb->iocb_flag &= ~LPFC_IO_ON_Q;
- pring->txcmplq_cnt--;
- }
-
- /* Firmware could still be in progress of DMAing
- * payload, so don't free data buffer till after
- * a hbeat.
- */
- abort_iocb->iocb_flag |= LPFC_DELAY_MEM_FREE;
- abort_iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED;
- spin_unlock_irq(&phba->hbalock);
-
- abort_iocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
- abort_iocb->iocb.un.ulpWord[4] = IOERR_ABORT_REQUESTED;
- (abort_iocb->iocb_cmpl)(phba, abort_iocb, abort_iocb);
- } else
- spin_unlock_irq(&phba->hbalock);
}
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_SLI,
+ "0327 Abort els iocb complete x%px with io cmd xri %x "
+ "abort tag x%x abort status %x abort code %x\n",
+ cmdiocb, get_job_abtsiotag(phba, cmdiocb),
+ (phba->sli_rev == LPFC_SLI_REV4) ?
+ get_wqe_reqtag(cmdiocb) :
+ cmdiocb->iocb.ulpIoTag,
+ ulp_status, ulp_word4);
+release_iocb:
lpfc_sli_release_iocbq(phba, cmdiocb);
return;
}
@@ -8546,160 +12412,185 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* which are aborted. The function frees memory resources used for
* the aborted ELS commands.
**/
-static void
+void
lpfc_ignore_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
- IOCB_t *irsp = &rspiocb->iocb;
+ struct lpfc_nodelist *ndlp = cmdiocb->ndlp;
+ IOCB_t *irsp;
+ LPFC_MBOXQ_t *mbox;
+ u32 ulp_command, ulp_status, ulp_word4, iotag;
+
+ ulp_command = get_job_cmnd(phba, cmdiocb);
+ ulp_status = get_job_ulpstatus(phba, rspiocb);
+ ulp_word4 = get_job_word4(phba, rspiocb);
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ iotag = get_wqe_reqtag(cmdiocb);
+ } else {
+ irsp = &rspiocb->iocb;
+ iotag = irsp->ulpIoTag;
+
+ /* It is possible a PLOGI_RJT for NPIV ports to get aborted.
+ * The MBX_REG_LOGIN64 mbox command is freed back to the
+ * mbox_mem_pool here.
+ */
+ if (cmdiocb->context_un.mbox) {
+ mbox = cmdiocb->context_un.mbox;
+ lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+ cmdiocb->context_un.mbox = NULL;
+ }
+ }
/* ELS cmd tag <ulpIoTag> completes */
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
- "0139 Ignoring ELS cmd tag x%x completion Data: "
- "x%x x%x x%x\n",
- irsp->ulpIoTag, irsp->ulpStatus,
- irsp->un.ulpWord[4], irsp->ulpTimeout);
- if (cmdiocb->iocb.ulpCommand == CMD_GEN_REQUEST64_CR)
+ "0139 Ignoring ELS cmd code x%x ref cnt x%x Data: "
+ "x%x x%x x%x x%px\n",
+ ulp_command, kref_read(&cmdiocb->ndlp->kref),
+ ulp_status, ulp_word4, iotag, cmdiocb->ndlp);
+ /*
+ * Deref the ndlp after free_iocb. sli_release_iocb will access the ndlp
+ * if exchange is busy.
+ */
+ if (ulp_command == CMD_GEN_REQUEST64_CR)
lpfc_ct_free_iocb(phba, cmdiocb);
else
lpfc_els_free_iocb(phba, cmdiocb);
- return;
+
+ lpfc_nlp_put(ndlp);
}
/**
- * lpfc_sli_abort_iotag_issue - Issue abort for a command iocb
+ * lpfc_sli_issue_abort_iotag - Abort function for a command iocb
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
* @cmdiocb: Pointer to driver command iocb object.
+ * @cmpl: completion function.
+ *
+ * This function issues an abort iocb for the provided command iocb. In case
+ * of unloading, the abort iocb will not be issued to commands on the ELS
+ * ring. Instead, the callback function shall be changed to those commands
+ * so that nothing happens when them finishes. This function is called with
+ * hbalock held andno ring_lock held (SLI4). The function returns IOCB_SUCCESS
+ * when the command iocb is an abort request.
*
- * This function issues an abort iocb for the provided command iocb down to
- * the port. Other than the case the outstanding command iocb is an abort
- * request, this function issues abort out unconditionally. This function is
- * called with hbalock held. The function returns 0 when it fails due to
- * memory allocation failure or when the command iocb is an abort request.
**/
-static int
-lpfc_sli_abort_iotag_issue(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
- struct lpfc_iocbq *cmdiocb)
+int
+lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *cmdiocb, void *cmpl)
{
struct lpfc_vport *vport = cmdiocb->vport;
struct lpfc_iocbq *abtsiocbp;
- IOCB_t *icmd = NULL;
- IOCB_t *iabt = NULL;
- int retval;
+ int retval = IOCB_ERROR;
+ unsigned long iflags;
+ struct lpfc_nodelist *ndlp = NULL;
+ u32 ulp_command = get_job_cmnd(phba, cmdiocb);
+ u16 ulp_context, iotag;
+ bool ia;
/*
* There are certain command types we don't want to abort. And we
* don't want to abort commands that are already in the process of
* being aborted.
*/
- icmd = &cmdiocb->iocb;
- if (icmd->ulpCommand == CMD_ABORT_XRI_CN ||
- icmd->ulpCommand == CMD_CLOSE_XRI_CN ||
- (cmdiocb->iocb_flag & LPFC_DRIVER_ABORTED) != 0)
- return 0;
+ if (ulp_command == CMD_ABORT_XRI_WQE ||
+ ulp_command == CMD_ABORT_XRI_CN ||
+ ulp_command == CMD_CLOSE_XRI_CN ||
+ cmdiocb->cmd_flag & LPFC_DRIVER_ABORTED)
+ return IOCB_ABORTING;
+
+ if (!pring) {
+ if (cmdiocb->cmd_flag & LPFC_IO_FABRIC)
+ cmdiocb->fabric_cmd_cmpl = lpfc_ignore_els_cmpl;
+ else
+ cmdiocb->cmd_cmpl = lpfc_ignore_els_cmpl;
+ return retval;
+ }
- /* issue ABTS for this IOCB based on iotag */
+ /*
+ * Always abort the outstanding WQE and set the IA bit correctly
+ * for the context. This is necessary for correctly removing
+ * outstanding ndlp reference counts when the CQE completes with
+ * the XB bit set.
+ */
abtsiocbp = __lpfc_sli_get_iocbq(phba);
if (abtsiocbp == NULL)
- return 0;
+ return IOCB_NORESOURCE;
/* This signals the response to set the correct status
* before calling the completion handler
*/
- cmdiocb->iocb_flag |= LPFC_DRIVER_ABORTED;
+ cmdiocb->cmd_flag |= LPFC_DRIVER_ABORTED;
- iabt = &abtsiocbp->iocb;
- iabt->un.acxri.abortType = ABORT_TYPE_ABTS;
- iabt->un.acxri.abortContextTag = icmd->ulpContext;
if (phba->sli_rev == LPFC_SLI_REV4) {
- iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag;
- iabt->un.acxri.abortContextTag = cmdiocb->iotag;
+ ulp_context = cmdiocb->sli4_xritag;
+ iotag = abtsiocbp->iotag;
+ } else {
+ iotag = cmdiocb->iocb.ulpIoTag;
+ if (pring->ringno == LPFC_ELS_RING) {
+ ndlp = cmdiocb->ndlp;
+ ulp_context = ndlp->nlp_rpi;
+ } else {
+ ulp_context = cmdiocb->iocb.ulpContext;
+ }
}
+
+ /* Just close the exchange under certain conditions. */
+ if (test_bit(FC_UNLOADING, &vport->load_flag) ||
+ phba->link_state < LPFC_LINK_UP ||
+ (phba->sli_rev == LPFC_SLI_REV4 &&
+ phba->sli4_hba.link_state.status == LPFC_FC_LA_TYPE_LINK_DOWN) ||
+ (phba->link_flag & LS_EXTERNAL_LOOPBACK))
+ ia = true;
else
- iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
- iabt->ulpLe = 1;
- iabt->ulpClass = icmd->ulpClass;
+ ia = false;
+
+ lpfc_sli_prep_abort_xri(phba, abtsiocbp, ulp_context, iotag,
+ cmdiocb->iocb.ulpClass,
+ LPFC_WQE_CQ_ID_DEFAULT, ia, false);
/* ABTS WQE must go to the same WQ as the WQE to be aborted */
- abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx;
- if (cmdiocb->iocb_flag & LPFC_IO_FCP)
- abtsiocbp->iocb_flag |= LPFC_USE_FCPWQIDX;
+ abtsiocbp->hba_wqidx = cmdiocb->hba_wqidx;
+ if (cmdiocb->cmd_flag & LPFC_IO_FCP)
+ abtsiocbp->cmd_flag |= (LPFC_IO_FCP | LPFC_USE_FCPWQIDX);
+
+ if (cmdiocb->cmd_flag & LPFC_IO_FOF)
+ abtsiocbp->cmd_flag |= LPFC_IO_FOF;
- if (phba->link_state >= LPFC_LINK_UP)
- iabt->ulpCommand = CMD_ABORT_XRI_CN;
+ if (cmpl)
+ abtsiocbp->cmd_cmpl = cmpl;
else
- iabt->ulpCommand = CMD_CLOSE_XRI_CN;
+ abtsiocbp->cmd_cmpl = lpfc_sli_abort_els_cmpl;
+ abtsiocbp->vport = vport;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ pring = lpfc_sli4_calc_ring(phba, abtsiocbp);
+ if (unlikely(pring == NULL))
+ goto abort_iotag_exit;
+ /* Note: both hbalock and ring_lock need to be set here */
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ retval = __lpfc_sli_issue_iocb(phba, pring->ringno,
+ abtsiocbp, 0);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ } else {
+ retval = __lpfc_sli_issue_iocb(phba, pring->ringno,
+ abtsiocbp, 0);
+ }
- abtsiocbp->iocb_cmpl = lpfc_sli_abort_els_cmpl;
+abort_iotag_exit:
lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
- "0339 Abort xri x%x, original iotag x%x, "
- "abort cmd iotag x%x\n",
- iabt->un.acxri.abortIoTag,
- iabt->un.acxri.abortContextTag,
- abtsiocbp->iotag);
- retval = __lpfc_sli_issue_iocb(phba, pring->ringno, abtsiocbp, 0);
-
- if (retval)
+ "0339 Abort IO XRI x%x, Original iotag x%x, "
+ "abort tag x%x Cmdjob : x%px Abortjob : x%px "
+ "retval x%x : IA %d cmd_cmpl %ps\n",
+ ulp_context, (phba->sli_rev == LPFC_SLI_REV4) ?
+ cmdiocb->iotag : iotag, iotag, cmdiocb, abtsiocbp,
+ retval, ia, abtsiocbp->cmd_cmpl);
+ if (retval) {
+ cmdiocb->cmd_flag &= ~LPFC_DRIVER_ABORTED;
__lpfc_sli_release_iocbq(phba, abtsiocbp);
-
- /*
- * Caller to this routine should check for IOCB_ERROR
- * and handle it properly. This routine no longer removes
- * iocb off txcmplq and call compl in case of IOCB_ERROR.
- */
- return retval;
-}
-
-/**
- * lpfc_sli_issue_abort_iotag - Abort function for a command iocb
- * @phba: Pointer to HBA context object.
- * @pring: Pointer to driver SLI ring object.
- * @cmdiocb: Pointer to driver command iocb object.
- *
- * This function issues an abort iocb for the provided command iocb. In case
- * of unloading, the abort iocb will not be issued to commands on the ELS
- * ring. Instead, the callback function shall be changed to those commands
- * so that nothing happens when them finishes. This function is called with
- * hbalock held. The function returns 0 when the command iocb is an abort
- * request.
- **/
-int
-lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
- struct lpfc_iocbq *cmdiocb)
-{
- struct lpfc_vport *vport = cmdiocb->vport;
- int retval = IOCB_ERROR;
- IOCB_t *icmd = NULL;
-
- /*
- * There are certain command types we don't want to abort. And we
- * don't want to abort commands that are already in the process of
- * being aborted.
- */
- icmd = &cmdiocb->iocb;
- if (icmd->ulpCommand == CMD_ABORT_XRI_CN ||
- icmd->ulpCommand == CMD_CLOSE_XRI_CN ||
- (cmdiocb->iocb_flag & LPFC_DRIVER_ABORTED) != 0)
- return 0;
-
- /*
- * If we're unloading, don't abort iocb on the ELS ring, but change
- * the callback so that nothing happens when it finishes.
- */
- if ((vport->load_flag & FC_UNLOADING) &&
- (pring->ringno == LPFC_ELS_RING)) {
- if (cmdiocb->iocb_flag & LPFC_IO_FABRIC)
- cmdiocb->fabric_iocb_cmpl = lpfc_ignore_els_cmpl;
- else
- cmdiocb->iocb_cmpl = lpfc_ignore_els_cmpl;
- goto abort_iotag_exit;
}
- /* Now, we try to issue the abort to the cmdiocb out */
- retval = lpfc_sli_abort_iotag_issue(phba, pring, cmdiocb);
-
-abort_iotag_exit:
/*
* Caller to this routine should check for IOCB_ERROR
* and handle it properly. This routine no longer removes
@@ -8709,43 +12600,6 @@ abort_iotag_exit:
}
/**
- * lpfc_sli_iocb_ring_abort - Unconditionally abort all iocbs on an iocb ring
- * @phba: Pointer to HBA context object.
- * @pring: Pointer to driver SLI ring object.
- *
- * This function aborts all iocbs in the given ring and frees all the iocb
- * objects in txq. This function issues abort iocbs unconditionally for all
- * the iocb commands in txcmplq. The iocbs in the txcmplq is not guaranteed
- * to complete before the return of this function. The caller is not required
- * to hold any locks.
- **/
-static void
-lpfc_sli_iocb_ring_abort(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
-{
- LIST_HEAD(completions);
- struct lpfc_iocbq *iocb, *next_iocb;
-
- if (pring->ringno == LPFC_ELS_RING)
- lpfc_fabric_abort_hba(phba);
-
- spin_lock_irq(&phba->hbalock);
-
- /* Take off all the iocbs on txq for cancelling */
- list_splice_init(&pring->txq, &completions);
- pring->txq_cnt = 0;
-
- /* Next issue ABTS for everything on the txcmplq */
- list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list)
- lpfc_sli_abort_iotag_issue(phba, pring, iocb);
-
- spin_unlock_irq(&phba->hbalock);
-
- /* Cancel all the IOCBs from the completions list */
- lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
- IOERR_SLI_ABORTED);
-}
-
-/**
* lpfc_sli_hba_iocb_abort - Abort all iocbs to an hba.
* @phba: pointer to lpfc HBA data structure.
*
@@ -8756,24 +12610,74 @@ lpfc_sli_hba_iocb_abort(struct lpfc_hba *phba)
{
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
+ struct lpfc_queue *qp = NULL;
int i;
- for (i = 0; i < psli->num_rings; i++) {
- pring = &psli->ring[i];
- lpfc_sli_iocb_ring_abort(phba, pring);
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ for (i = 0; i < psli->num_rings; i++) {
+ pring = &psli->sli3_ring[i];
+ lpfc_sli_abort_iocb_ring(phba, pring);
+ }
+ return;
}
+ list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
+ pring = qp->pring;
+ if (!pring)
+ continue;
+ lpfc_sli_abort_iocb_ring(phba, pring);
+ }
+}
+
+/**
+ * lpfc_sli_validate_fcp_iocb_for_abort - filter iocbs appropriate for FCP aborts
+ * @iocbq: Pointer to iocb object.
+ * @vport: Pointer to driver virtual port object.
+ *
+ * This function acts as an iocb filter for functions which abort FCP iocbs.
+ *
+ * Return values
+ * -ENODEV, if a null iocb or vport ptr is encountered
+ * -EINVAL, if the iocb is not an FCP I/O, not on the TX cmpl queue, premarked as
+ * driver already started the abort process, or is an abort iocb itself
+ * 0, passes criteria for aborting the FCP I/O iocb
+ **/
+static int
+lpfc_sli_validate_fcp_iocb_for_abort(struct lpfc_iocbq *iocbq,
+ struct lpfc_vport *vport)
+{
+ u8 ulp_command;
+
+ /* No null ptr vports */
+ if (!iocbq || iocbq->vport != vport)
+ return -ENODEV;
+
+ /* iocb must be for FCP IO, already exists on the TX cmpl queue,
+ * can't be premarked as driver aborted, nor be an ABORT iocb itself
+ */
+ ulp_command = get_job_cmnd(vport->phba, iocbq);
+ if (!(iocbq->cmd_flag & LPFC_IO_FCP) ||
+ !(iocbq->cmd_flag & LPFC_IO_ON_TXCMPLQ) ||
+ (iocbq->cmd_flag & LPFC_DRIVER_ABORTED) ||
+ (ulp_command == CMD_ABORT_XRI_CN ||
+ ulp_command == CMD_CLOSE_XRI_CN ||
+ ulp_command == CMD_ABORT_XRI_WQE))
+ return -EINVAL;
+
+ return 0;
}
/**
- * lpfc_sli_validate_fcp_iocb - find commands associated with a vport or LUN
+ * lpfc_sli_validate_fcp_iocb - validate commands associated with a SCSI target
* @iocbq: Pointer to driver iocb object.
* @vport: Pointer to driver virtual port object.
* @tgt_id: SCSI ID of the target.
* @lun_id: LUN ID of the scsi device.
* @ctx_cmd: LPFC_CTX_LUN/LPFC_CTX_TGT/LPFC_CTX_HOST
*
- * This function acts as an iocb filter for functions which abort or count
- * all FCP iocbs pending on a lun/SCSI target/SCSI host. It will return
+ * This function acts as an iocb filter for validating a lun/SCSI target/SCSI
+ * host.
+ *
+ * It will return
* 0 if the filtering criteria is met for the given iocb and will return
* 1 if the filtering criteria is not met.
* If ctx_cmd == LPFC_CTX_LUN, the function returns 0 only if the
@@ -8791,29 +12695,23 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport,
uint16_t tgt_id, uint64_t lun_id,
lpfc_ctx_cmd ctx_cmd)
{
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
int rc = 1;
- if (!(iocbq->iocb_flag & LPFC_IO_FCP))
- return rc;
-
- if (iocbq->vport != vport)
- return rc;
-
- lpfc_cmd = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
+ lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq);
if (lpfc_cmd->pCmd == NULL)
return rc;
switch (ctx_cmd) {
case LPFC_CTX_LUN:
- if ((lpfc_cmd->rdata->pnode) &&
+ if ((lpfc_cmd->rdata) && (lpfc_cmd->rdata->pnode) &&
(lpfc_cmd->rdata->pnode->nlp_sid == tgt_id) &&
(scsilun_to_int(&lpfc_cmd->fcp_cmnd->fcp_lun) == lun_id))
rc = 0;
break;
case LPFC_CTX_TGT:
- if ((lpfc_cmd->rdata->pnode) &&
+ if ((lpfc_cmd->rdata) && (lpfc_cmd->rdata->pnode) &&
(lpfc_cmd->rdata->pnode->nlp_sid == tgt_id))
rc = 0;
break;
@@ -8855,14 +12753,33 @@ lpfc_sli_sum_iocb(struct lpfc_vport *vport, uint16_t tgt_id, uint64_t lun_id,
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *iocbq;
int sum, i;
+ unsigned long iflags;
+ u8 ulp_command;
+ spin_lock_irqsave(&phba->hbalock, iflags);
for (i = 1, sum = 0; i <= phba->sli.last_iotag; i++) {
iocbq = phba->sli.iocbq_lookup[i];
- if (lpfc_sli_validate_fcp_iocb (iocbq, vport, tgt_id, lun_id,
- ctx_cmd) == 0)
+ if (!iocbq || iocbq->vport != vport)
+ continue;
+ if (!(iocbq->cmd_flag & LPFC_IO_FCP) ||
+ !(iocbq->cmd_flag & LPFC_IO_ON_TXCMPLQ))
+ continue;
+
+ /* Include counting outstanding aborts */
+ ulp_command = get_job_cmnd(phba, iocbq);
+ if (ulp_command == CMD_ABORT_XRI_CN ||
+ ulp_command == CMD_CLOSE_XRI_CN ||
+ ulp_command == CMD_ABORT_XRI_WQE) {
+ sum++;
+ continue;
+ }
+
+ if (lpfc_sli_validate_fcp_iocb(iocbq, vport, tgt_id, lun_id,
+ ctx_cmd) == 0)
sum++;
}
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
return sum;
}
@@ -8881,6 +12798,16 @@ void
lpfc_sli_abort_fcp_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
{
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3096 ABORT_XRI_CX completing on rpi x%x "
+ "original iotag x%x, abort cmd iotag x%x "
+ "status 0x%x, reason 0x%x\n",
+ (phba->sli_rev == LPFC_SLI_REV4) ?
+ cmdiocb->sli4_xritag :
+ cmdiocb->iocb.un.acxri.abortContextTag,
+ get_job_abtsiotag(phba, cmdiocb),
+ cmdiocb->iotag, get_job_ulpstatus(phba, rspiocb),
+ get_job_word4(phba, rspiocb));
lpfc_sli_release_iocbq(phba, cmdiocb);
return;
}
@@ -8888,14 +12815,17 @@ lpfc_sli_abort_fcp_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/**
* lpfc_sli_abort_iocb - issue abort for all commands on a host/target/LUN
* @vport: Pointer to virtual port.
- * @pring: Pointer to driver SLI ring object.
* @tgt_id: SCSI ID of the target.
* @lun_id: LUN ID of the scsi device.
* @abort_cmd: LPFC_CTX_LUN/LPFC_CTX_TGT/LPFC_CTX_HOST.
*
* This function sends an abort command for every SCSI command
* associated with the given virtual port pending on the ring
- * filtered by lpfc_sli_validate_fcp_iocb function.
+ * filtered by lpfc_sli_validate_fcp_iocb_for_abort and then
+ * lpfc_sli_validate_fcp_iocb function. The ordering for validation before
+ * submitting abort iocbs must be lpfc_sli_validate_fcp_iocb_for_abort
+ * followed by lpfc_sli_validate_fcp_iocb.
+ *
* When abort_cmd == LPFC_CTX_LUN, the function sends abort only to the
* FCP iocbs associated with lun specified by tgt_id and lun_id
* parameters
@@ -8903,67 +12833,214 @@ lpfc_sli_abort_fcp_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* FCP iocbs associated with SCSI target specified by tgt_id parameter.
* When abort_cmd == LPFC_CTX_HOST, the function sends abort to all
* FCP iocbs associated with virtual port.
+ * The pring used for SLI3 is sli3_ring[LPFC_FCP_RING], for SLI4
+ * lpfc_sli4_calc_ring is used.
* This function returns number of iocbs it failed to abort.
* This function is called with no locks held.
**/
int
-lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
- uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd abort_cmd)
+lpfc_sli_abort_iocb(struct lpfc_vport *vport, u16 tgt_id, u64 lun_id,
+ lpfc_ctx_cmd abort_cmd)
{
struct lpfc_hba *phba = vport->phba;
+ struct lpfc_sli_ring *pring = NULL;
struct lpfc_iocbq *iocbq;
- struct lpfc_iocbq *abtsiocb;
- IOCB_t *cmd = NULL;
int errcnt = 0, ret_val = 0;
+ unsigned long iflags;
int i;
+ /* all I/Os are in process of being flushed */
+ if (test_bit(HBA_IOQ_FLUSH, &phba->hba_flag))
+ return errcnt;
+
for (i = 1; i <= phba->sli.last_iotag; i++) {
iocbq = phba->sli.iocbq_lookup[i];
+ if (lpfc_sli_validate_fcp_iocb_for_abort(iocbq, vport))
+ continue;
+
if (lpfc_sli_validate_fcp_iocb(iocbq, vport, tgt_id, lun_id,
abort_cmd) != 0)
continue;
- /* issue ABTS for this IOCB based on iotag */
- abtsiocb = lpfc_sli_get_iocbq(phba);
- if (abtsiocb == NULL) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ if (phba->sli_rev == LPFC_SLI_REV3) {
+ pring = &phba->sli.sli3_ring[LPFC_FCP_RING];
+ } else if (phba->sli_rev == LPFC_SLI_REV4) {
+ pring = lpfc_sli4_calc_ring(phba, iocbq);
+ }
+ ret_val = lpfc_sli_issue_abort_iotag(phba, pring, iocbq,
+ lpfc_sli_abort_fcp_cmpl);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ if (ret_val != IOCB_SUCCESS)
errcnt++;
+ }
+
+ return errcnt;
+}
+
+/**
+ * lpfc_sli_abort_taskmgmt - issue abort for all commands on a host/target/LUN
+ * @vport: Pointer to virtual port.
+ * @pring: Pointer to driver SLI ring object.
+ * @tgt_id: SCSI ID of the target.
+ * @lun_id: LUN ID of the scsi device.
+ * @cmd: LPFC_CTX_LUN/LPFC_CTX_TGT/LPFC_CTX_HOST.
+ *
+ * This function sends an abort command for every SCSI command
+ * associated with the given virtual port pending on the ring
+ * filtered by lpfc_sli_validate_fcp_iocb_for_abort and then
+ * lpfc_sli_validate_fcp_iocb function. The ordering for validation before
+ * submitting abort iocbs must be lpfc_sli_validate_fcp_iocb_for_abort
+ * followed by lpfc_sli_validate_fcp_iocb.
+ *
+ * When taskmgmt_cmd == LPFC_CTX_LUN, the function sends abort only to the
+ * FCP iocbs associated with lun specified by tgt_id and lun_id
+ * parameters
+ * When taskmgmt_cmd == LPFC_CTX_TGT, the function sends abort only to the
+ * FCP iocbs associated with SCSI target specified by tgt_id parameter.
+ * When taskmgmt_cmd == LPFC_CTX_HOST, the function sends abort to all
+ * FCP iocbs associated with virtual port.
+ * This function returns number of iocbs it aborted .
+ * This function is called with no locks held right after a taskmgmt
+ * command is sent.
+ **/
+int
+lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
+ uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd cmd)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_io_buf *lpfc_cmd;
+ struct lpfc_iocbq *abtsiocbq;
+ struct lpfc_nodelist *ndlp = NULL;
+ struct lpfc_iocbq *iocbq;
+ int sum, i, ret_val;
+ unsigned long iflags;
+ struct lpfc_sli_ring *pring_s4 = NULL;
+ u16 ulp_context, iotag, cqid = LPFC_WQE_CQ_ID_DEFAULT;
+ bool ia;
+
+ /* all I/Os are in process of being flushed */
+ if (test_bit(HBA_IOQ_FLUSH, &phba->hba_flag))
+ return 0;
+
+ sum = 0;
+
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ for (i = 1; i <= phba->sli.last_iotag; i++) {
+ iocbq = phba->sli.iocbq_lookup[i];
+
+ if (lpfc_sli_validate_fcp_iocb_for_abort(iocbq, vport))
+ continue;
+
+ if (lpfc_sli_validate_fcp_iocb(iocbq, vport, tgt_id, lun_id,
+ cmd) != 0)
+ continue;
+
+ /* Guard against IO completion being called at same time */
+ lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq);
+ spin_lock(&lpfc_cmd->buf_lock);
+
+ if (!lpfc_cmd->pCmd) {
+ spin_unlock(&lpfc_cmd->buf_lock);
continue;
}
- cmd = &iocbq->iocb;
- abtsiocb->iocb.un.acxri.abortType = ABORT_TYPE_ABTS;
- abtsiocb->iocb.un.acxri.abortContextTag = cmd->ulpContext;
- if (phba->sli_rev == LPFC_SLI_REV4)
- abtsiocb->iocb.un.acxri.abortIoTag = iocbq->sli4_xritag;
- else
- abtsiocb->iocb.un.acxri.abortIoTag = cmd->ulpIoTag;
- abtsiocb->iocb.ulpLe = 1;
- abtsiocb->iocb.ulpClass = cmd->ulpClass;
- abtsiocb->vport = phba->pport;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ pring_s4 =
+ phba->sli4_hba.hdwq[iocbq->hba_wqidx].io_wq->pring;
+ if (!pring_s4) {
+ spin_unlock(&lpfc_cmd->buf_lock);
+ continue;
+ }
+ /* Note: both hbalock and ring_lock must be set here */
+ spin_lock(&pring_s4->ring_lock);
+ }
- /* ABTS WQE must go to the same WQ as the WQE to be aborted */
- abtsiocb->fcp_wqidx = iocbq->fcp_wqidx;
- if (iocbq->iocb_flag & LPFC_IO_FCP)
- abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX;
+ /*
+ * If the iocbq is already being aborted, don't take a second
+ * action, but do count it.
+ */
+ if ((iocbq->cmd_flag & LPFC_DRIVER_ABORTED) ||
+ !(iocbq->cmd_flag & LPFC_IO_ON_TXCMPLQ)) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring_s4->ring_lock);
+ spin_unlock(&lpfc_cmd->buf_lock);
+ continue;
+ }
+
+ /* issue ABTS for this IOCB based on iotag */
+ abtsiocbq = __lpfc_sli_get_iocbq(phba);
+ if (!abtsiocbq) {
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ spin_unlock(&pring_s4->ring_lock);
+ spin_unlock(&lpfc_cmd->buf_lock);
+ continue;
+ }
- if (lpfc_is_link_up(phba))
- abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ iotag = abtsiocbq->iotag;
+ ulp_context = iocbq->sli4_xritag;
+ cqid = lpfc_cmd->hdwq->io_cq_map;
+ } else {
+ iotag = iocbq->iocb.ulpIoTag;
+ if (pring->ringno == LPFC_ELS_RING) {
+ ndlp = iocbq->ndlp;
+ ulp_context = ndlp->nlp_rpi;
+ } else {
+ ulp_context = iocbq->iocb.ulpContext;
+ }
+ }
+
+ ndlp = lpfc_cmd->rdata->pnode;
+
+ if (lpfc_is_link_up(phba) &&
+ (ndlp && ndlp->nlp_state == NLP_STE_MAPPED_NODE) &&
+ !(phba->link_flag & LS_EXTERNAL_LOOPBACK))
+ ia = false;
else
- abtsiocb->iocb.ulpCommand = CMD_CLOSE_XRI_CN;
+ ia = true;
+
+ lpfc_sli_prep_abort_xri(phba, abtsiocbq, ulp_context, iotag,
+ iocbq->iocb.ulpClass, cqid,
+ ia, false);
+
+ abtsiocbq->vport = vport;
+
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocbq->hba_wqidx = iocbq->hba_wqidx;
+ if (iocbq->cmd_flag & LPFC_IO_FCP)
+ abtsiocbq->cmd_flag |= LPFC_USE_FCPWQIDX;
+ if (iocbq->cmd_flag & LPFC_IO_FOF)
+ abtsiocbq->cmd_flag |= LPFC_IO_FOF;
/* Setup callback routine and issue the command. */
- abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
- ret_val = lpfc_sli_issue_iocb(phba, pring->ringno,
- abtsiocb, 0);
- if (ret_val == IOCB_ERROR) {
- lpfc_sli_release_iocbq(phba, abtsiocb);
- errcnt++;
- continue;
+ abtsiocbq->cmd_cmpl = lpfc_sli_abort_fcp_cmpl;
+
+ /*
+ * Indicate the IO is being aborted by the driver and set
+ * the caller's flag into the aborted IO.
+ */
+ iocbq->cmd_flag |= LPFC_DRIVER_ABORTED;
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno,
+ abtsiocbq, 0);
+ spin_unlock(&pring_s4->ring_lock);
+ } else {
+ ret_val = __lpfc_sli_issue_iocb(phba, pring->ringno,
+ abtsiocbq, 0);
}
- }
- return errcnt;
+ spin_unlock(&lpfc_cmd->buf_lock);
+
+ if (ret_val == IOCB_ERROR)
+ __lpfc_sli_release_iocbq(phba, abtsiocbq);
+ else
+ sum++;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return sum;
}
/**
@@ -8990,20 +13067,43 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
{
wait_queue_head_t *pdone_q;
unsigned long iflags;
- struct lpfc_scsi_buf *lpfc_cmd;
+ struct lpfc_io_buf *lpfc_cmd;
+ size_t offset = offsetof(struct lpfc_iocbq, wqe);
spin_lock_irqsave(&phba->hbalock, iflags);
- cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
- if (cmdiocbq->context2 && rspiocbq)
- memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
- &rspiocbq->iocb, sizeof(IOCB_t));
+ if (cmdiocbq->cmd_flag & LPFC_IO_WAKE_TMO) {
+
+ /*
+ * A time out has occurred for the iocb. If a time out
+ * completion handler has been supplied, call it. Otherwise,
+ * just free the iocbq.
+ */
+
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ cmdiocbq->cmd_cmpl = cmdiocbq->wait_cmd_cmpl;
+ cmdiocbq->wait_cmd_cmpl = NULL;
+ if (cmdiocbq->cmd_cmpl)
+ cmdiocbq->cmd_cmpl(phba, cmdiocbq, NULL);
+ else
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ return;
+ }
+
+ /* Copy the contents of the local rspiocb into the caller's buffer. */
+ cmdiocbq->cmd_flag |= LPFC_IO_WAKE;
+ if (cmdiocbq->rsp_iocb && rspiocbq)
+ memcpy((char *)cmdiocbq->rsp_iocb + offset,
+ (char *)rspiocbq + offset, sizeof(*rspiocbq) - offset);
/* Set the exchange busy flag for task management commands */
- if ((cmdiocbq->iocb_flag & LPFC_IO_FCP) &&
- !(cmdiocbq->iocb_flag & LPFC_IO_LIBDFC)) {
- lpfc_cmd = container_of(cmdiocbq, struct lpfc_scsi_buf,
- cur_iocbq);
- lpfc_cmd->exch_busy = rspiocbq->iocb_flag & LPFC_EXCHANGE_BUSY;
+ if ((cmdiocbq->cmd_flag & LPFC_IO_FCP) &&
+ !(cmdiocbq->cmd_flag & LPFC_IO_LIBDFC)) {
+ lpfc_cmd = container_of(cmdiocbq, struct lpfc_io_buf,
+ cur_iocbq);
+ if (rspiocbq && (rspiocbq->cmd_flag & LPFC_EXCHANGE_BUSY))
+ lpfc_cmd->flags |= LPFC_SBUF_XBUSY;
+ else
+ lpfc_cmd->flags &= ~LPFC_SBUF_XBUSY;
}
pdone_q = cmdiocbq->context_un.wait_queue;
@@ -9019,7 +13119,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
* @piocbq: Pointer to command iocb.
* @flag: Flag to test.
*
- * This routine grabs the hbalock and then test the iocb_flag to
+ * This routine grabs the hbalock and then test the cmd_flag to
* see if the passed in flag is set.
* Returns:
* 1 if flag is set.
@@ -9033,7 +13133,7 @@ lpfc_chk_iocb_flg(struct lpfc_hba *phba,
int ret;
spin_lock_irqsave(&phba->hbalock, iflags);
- ret = piocbq->iocb_flag & flag;
+ ret = piocbq->cmd_flag & flag;
spin_unlock_irqrestore(&phba->hbalock, iflags);
return ret;
@@ -9042,16 +13142,22 @@ lpfc_chk_iocb_flg(struct lpfc_hba *phba,
/**
* lpfc_sli_issue_iocb_wait - Synchronous function to issue iocb commands
* @phba: Pointer to HBA context object..
- * @pring: Pointer to sli ring.
+ * @ring_number: Ring number
* @piocb: Pointer to command iocb.
* @prspiocbq: Pointer to response iocb.
* @timeout: Timeout in number of seconds.
*
* This function issues the iocb to firmware and waits for the
- * iocb to complete. If the iocb command is not
- * completed within timeout seconds, it returns IOCB_TIMEDOUT.
- * Caller should not free the iocb resources if this function
- * returns IOCB_TIMEDOUT.
+ * iocb to complete. The cmd_cmpl field of the shall be used
+ * to handle iocbs which time out. If the field is NULL, the
+ * function shall free the iocbq structure. If more clean up is
+ * needed, the caller is expected to provide a completion function
+ * that will provide the needed clean up. If the iocb command is
+ * not completed within timeout seconds, the function will either
+ * free the iocbq structure (if cmd_cmpl == NULL) or execute the
+ * completion function set in the cmd_cmpl field and then return
+ * a status of IOCB_TIMEDOUT. The caller should not free the iocb
+ * resources if this function returns IOCB_TIMEDOUT.
* The function waits for the iocb completion using an
* non-interruptible wait.
* This function will sleep while waiting for iocb completion.
@@ -9061,7 +13167,7 @@ lpfc_chk_iocb_flg(struct lpfc_hba *phba,
* This function assumes that the iocb completions occur while
* this function sleep. So, this function cannot be called from
* the thread which process iocb completion for this ring.
- * This function clears the iocb_flag of the iocb object before
+ * This function clears the cmd_flag of the iocb object before
* issuing the iocb and the iocb completion handler sets this
* flag and wakes this thread when the iocb completes.
* The contents of the response iocb will be copied to prspiocbq
@@ -9080,20 +13186,33 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
long timeleft, timeout_req = 0;
int retval = IOCB_SUCCESS;
uint32_t creg_val;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_iocbq *iocb;
+ int txq_cnt = 0;
+ int txcmplq_cnt = 0;
+ struct lpfc_sli_ring *pring;
+ unsigned long iflags;
+ bool iocb_completed = true;
+
+ if (phba->sli_rev >= LPFC_SLI_REV4) {
+ lpfc_sli_prep_wqe(phba, piocb);
+
+ pring = lpfc_sli4_calc_ring(phba, piocb);
+ } else
+ pring = &phba->sli.sli3_ring[ring_number];
/*
- * If the caller has provided a response iocbq buffer, then context2
+ * If the caller has provided a response iocbq buffer, then rsp_iocb
* is NULL or its an error.
*/
if (prspiocbq) {
- if (piocb->context2)
+ if (piocb->rsp_iocb)
return IOCB_ERROR;
- piocb->context2 = prspiocbq;
+ piocb->rsp_iocb = prspiocbq;
}
- piocb->iocb_cmpl = lpfc_sli_wake_iocb_wait;
+ piocb->wait_cmd_cmpl = piocb->cmd_cmpl;
+ piocb->cmd_cmpl = lpfc_sli_wake_iocb_wait;
piocb->context_un.wait_queue = &done_q;
- piocb->iocb_flag &= ~LPFC_IO_WAKE;
+ piocb->cmd_flag &= ~(LPFC_IO_WAKE | LPFC_IO_WAKE_TMO);
if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
if (lpfc_readl(phba->HCregaddr, &creg_val))
@@ -9106,30 +13225,54 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
retval = lpfc_sli_issue_iocb(phba, ring_number, piocb,
SLI_IOCB_RET_IOCB);
if (retval == IOCB_SUCCESS) {
- timeout_req = timeout * HZ;
+ timeout_req = secs_to_jiffies(timeout);
timeleft = wait_event_timeout(done_q,
lpfc_chk_iocb_flg(phba, piocb, LPFC_IO_WAKE),
timeout_req);
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ if (!(piocb->cmd_flag & LPFC_IO_WAKE)) {
- if (piocb->iocb_flag & LPFC_IO_WAKE) {
+ /*
+ * IOCB timed out. Inform the wake iocb wait
+ * completion function and set local status
+ */
+
+ iocb_completed = false;
+ piocb->cmd_flag |= LPFC_IO_WAKE_TMO;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ if (iocb_completed) {
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"0331 IOCB wake signaled\n");
+ /* Note: we are not indicating if the IOCB has a success
+ * status or not - that's for the caller to check.
+ * IOCB_SUCCESS means just that the command was sent and
+ * completed. Not that it completed successfully.
+ * */
} else if (timeleft == 0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0338 IOCB wait timeout error - no "
"wake response Data x%x\n", timeout);
retval = IOCB_TIMEDOUT;
} else {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0330 IOCB wake NOT set, "
"Data x%x x%lx\n",
timeout, (timeleft / jiffies));
retval = IOCB_TIMEDOUT;
}
} else if (retval == IOCB_BUSY) {
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "2818 Max IOCBs %d txq cnt %d txcmplq cnt %d\n",
- phba->iocb_cnt, pring->txq_cnt, pring->txcmplq_cnt);
+ if (phba->cfg_log_verbose & LOG_SLI) {
+ list_for_each_entry(iocb, &pring->txq, list) {
+ txq_cnt++;
+ }
+ list_for_each_entry(iocb, &pring->txcmplq, list) {
+ txcmplq_cnt++;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2818 Max IOCBs %d txq cnt %d txcmplq cnt %d\n",
+ phba->iocb_cnt, txq_cnt, txcmplq_cnt);
+ }
return retval;
} else {
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
@@ -9147,10 +13290,10 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
}
if (prspiocbq)
- piocb->context2 = NULL;
+ piocb->rsp_iocb = NULL;
piocb->context_un.wait_queue = NULL;
- piocb->iocb_cmpl = NULL;
+ piocb->cmd_cmpl = NULL;
return retval;
}
@@ -9184,50 +13327,43 @@ int
lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
uint32_t timeout)
{
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_q);
+ struct completion mbox_done;
int retval;
unsigned long flag;
- /* The caller must leave context1 empty. */
- if (pmboxq->context1)
- return MBX_NOT_FINISHED;
-
pmboxq->mbox_flag &= ~LPFC_MBX_WAKE;
/* setup wake call as IOCB callback */
pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait;
- /* setup context field to pass wait_queue pointer to wake function */
- pmboxq->context1 = &done_q;
+ /* setup ctx_u field to pass wait_queue pointer to wake function */
+ init_completion(&mbox_done);
+ pmboxq->ctx_u.mbox_wait = &mbox_done;
/* now issue the command */
retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
-
if (retval == MBX_BUSY || retval == MBX_SUCCESS) {
- wait_event_interruptible_timeout(done_q,
- pmboxq->mbox_flag & LPFC_MBX_WAKE,
- timeout * HZ);
+ wait_for_completion_timeout(&mbox_done, secs_to_jiffies(timeout));
spin_lock_irqsave(&phba->hbalock, flag);
- pmboxq->context1 = NULL;
+ pmboxq->ctx_u.mbox_wait = NULL;
/*
* if LPFC_MBX_WAKE flag is set the mailbox is completed
* else do not free the resources.
*/
if (pmboxq->mbox_flag & LPFC_MBX_WAKE) {
retval = MBX_SUCCESS;
- lpfc_sli4_swap_str(phba, pmboxq);
} else {
retval = MBX_TIMEOUT;
pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
}
spin_unlock_irqrestore(&phba->hbalock, flag);
}
-
return retval;
}
/**
* lpfc_sli_mbox_sys_shutdown - shutdown mailbox command sub-system
* @phba: Pointer to HBA context.
+ * @mbx_action: Mailbox shutdown options.
*
* This function is called to shutdown the driver's mailbox sub-system.
* It first marks the mailbox sub-system is in a block state to prevent
@@ -9241,26 +13377,37 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
* sub-system flush routine to gracefully bring down mailbox sub-system.
**/
void
-lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
+lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba, int mbx_action)
{
struct lpfc_sli *psli = &phba->sli;
- uint8_t actcmd = MBX_HEARTBEAT;
unsigned long timeout;
+ if (mbx_action == LPFC_MBX_NO_WAIT) {
+ /* delay 100ms for port state */
+ msleep(100);
+ lpfc_sli_mbox_sys_flush(phba);
+ return;
+ }
+ timeout = secs_to_jiffies(LPFC_MBOX_TMO) + jiffies;
+
+ /* Disable softirqs, including timers from obtaining phba->hbalock */
+ local_bh_disable();
+
spin_lock_irq(&phba->hbalock);
psli->sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
- spin_unlock_irq(&phba->hbalock);
if (psli->sli_flag & LPFC_SLI_ACTIVE) {
- spin_lock_irq(&phba->hbalock);
- if (phba->sli.mbox_active)
- actcmd = phba->sli.mbox_active->u.mb.mbxCommand;
- spin_unlock_irq(&phba->hbalock);
/* Determine how long we might wait for the active mailbox
* command to be gracefully completed by firmware.
*/
- timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, actcmd) *
- 1000) + jiffies;
+ if (phba->sli.mbox_active)
+ timeout = secs_to_jiffies(lpfc_mbox_tmo_val(phba,
+ phba->sli.mbox_active)) + jiffies;
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Enable softirqs again, done with phba->hbalock */
+ local_bh_enable();
+
while (phba->sli.mbox_active) {
/* Check active mailbox complete status every 2ms */
msleep(2);
@@ -9270,7 +13417,13 @@ lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
*/
break;
}
+ } else {
+ spin_unlock_irq(&phba->hbalock);
+
+ /* Enable softirqs again, done with phba->hbalock */
+ local_bh_enable();
}
+
lpfc_sli_mbox_sys_flush(phba);
}
@@ -9303,7 +13456,7 @@ lpfc_sli_eratt_read(struct lpfc_hba *phba)
if ((HS_FFER1 & phba->work_hs) &&
((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
HS_FFER6 | HS_FFER7 | HS_FFER8) & phba->work_hs)) {
- phba->hba_flag |= DEFER_ERATT;
+ set_bit(DEFER_ERATT, &phba->hba_flag);
/* Clear all interrupt enable conditions */
writel(0, phba->HCregaddr);
readl(phba->HCregaddr);
@@ -9312,7 +13465,7 @@ lpfc_sli_eratt_read(struct lpfc_hba *phba)
/* Set the driver HA work bitmap */
phba->work_ha |= HA_ERATT;
/* Indicate polling handles this ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ set_bit(HBA_ERATT_HANDLED, &phba->hba_flag);
return 1;
}
return 0;
@@ -9323,7 +13476,7 @@ unplug_err:
/* Set the driver HA work bitmap */
phba->work_ha |= HA_ERATT;
/* Indicate polling handles this ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ set_bit(HBA_ERATT_HANDLED, &phba->hba_flag);
return 1;
}
@@ -9344,6 +13497,7 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba)
uint32_t uerr_sta_hi, uerr_sta_lo;
uint32_t if_type, portsmphr;
struct lpfc_register portstat_reg;
+ u32 logmask;
/*
* For now, use the SLI4 device internal unrecoverable error
@@ -9358,12 +13512,12 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba)
&uerr_sta_hi)) {
phba->work_hs |= UNPLUG_ERR;
phba->work_ha |= HA_ERATT;
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ set_bit(HBA_ERATT_HANDLED, &phba->hba_flag);
return 1;
}
if ((~phba->sli4_hba.ue_mask_lo & uerr_sta_lo) ||
(~phba->sli4_hba.ue_mask_hi & uerr_sta_hi)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1423 HBA Unrecoverable error: "
"uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
"ue_mask_lo_reg=0x%x, "
@@ -9374,18 +13528,19 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba)
phba->work_status[0] = uerr_sta_lo;
phba->work_status[1] = uerr_sta_hi;
phba->work_ha |= HA_ERATT;
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ set_bit(HBA_ERATT_HANDLED, &phba->hba_flag);
return 1;
}
break;
case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_6:
if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
&portstat_reg.word0) ||
lpfc_readl(phba->sli4_hba.PSMPHRregaddr,
&portsmphr)){
phba->work_hs |= UNPLUG_ERR;
phba->work_ha |= HA_ERATT;
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ set_bit(HBA_ERATT_HANDLED, &phba->hba_flag);
return 1;
}
if (bf_get(lpfc_sliport_status_err, &portstat_reg)) {
@@ -9393,8 +13548,13 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba)
readl(phba->sli4_hba.u.if_type2.ERR1regaddr);
phba->work_status[1] =
readl(phba->sli4_hba.u.if_type2.ERR2regaddr);
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2885 Port Error Detected: "
+ logmask = LOG_TRACE_EVENT;
+ if (phba->work_status[0] ==
+ SLIPORT_ERR1_REG_ERR_CODE_2 &&
+ phba->work_status[1] == SLIPORT_ERR2_REG_FW_RESTART)
+ logmask = LOG_SLI;
+ lpfc_printf_log(phba, KERN_ERR, logmask,
+ "2885 Port Status Event: "
"port status reg 0x%x, "
"port smphr reg 0x%x, "
"error 1=0x%x, error 2=0x%x\n",
@@ -9403,13 +13563,13 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba)
phba->work_status[0],
phba->work_status[1]);
phba->work_ha |= HA_ERATT;
- phba->hba_flag |= HBA_ERATT_HANDLED;
+ set_bit(HBA_ERATT_HANDLED, &phba->hba_flag);
return 1;
}
break;
case LPFC_SLI_INTF_IF_TYPE_1:
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2886 HBA Error Attention on unsupported "
"if type %d.", if_type);
return 1;
@@ -9440,22 +13600,18 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
return 0;
/* Check if interrupt handler handles this ERATT */
- spin_lock_irq(&phba->hbalock);
- if (phba->hba_flag & HBA_ERATT_HANDLED) {
+ if (test_bit(HBA_ERATT_HANDLED, &phba->hba_flag))
/* Interrupt handler has handled ERATT */
- spin_unlock_irq(&phba->hbalock);
return 0;
- }
/*
* If there is deferred error attention, do not check for error
* attention
*/
- if (unlikely(phba->hba_flag & DEFER_ERATT)) {
- spin_unlock_irq(&phba->hbalock);
+ if (unlikely(test_bit(DEFER_ERATT, &phba->hba_flag)))
return 0;
- }
+ spin_lock_irq(&phba->hbalock);
/* If PCI channel is offline, don't process it */
if (unlikely(pci_channel_offline(phba->pcidev))) {
spin_unlock_irq(&phba->hbalock);
@@ -9473,7 +13629,7 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
ha_copy = lpfc_sli4_eratt_read(phba);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0299 Invalid SLI revision (%d)\n",
phba->sli_rev);
ha_copy = 0;
@@ -9577,19 +13733,17 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
ha_copy &= ~HA_ERATT;
/* Check the need for handling ERATT in interrupt handler */
if (ha_copy & HA_ERATT) {
- if (phba->hba_flag & HBA_ERATT_HANDLED)
+ if (test_and_set_bit(HBA_ERATT_HANDLED,
+ &phba->hba_flag))
/* ERATT polling has handled ERATT */
ha_copy &= ~HA_ERATT;
- else
- /* Indicate interrupt handler handles ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
}
/*
* If there is deferred error attention, do not check for any
* interrupt.
*/
- if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+ if (unlikely(test_bit(DEFER_ERATT, &phba->hba_flag))) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
return IRQ_NONE;
}
@@ -9685,7 +13839,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
HS_FFER6 | HS_FFER7 | HS_FFER8) &
phba->work_hs)) {
- phba->hba_flag |= DEFER_ERATT;
+ set_bit(DEFER_ERATT, &phba->hba_flag);
/* Clear all interrupt enable conditions */
writel(0, phba->HCregaddr);
readl(phba->HCregaddr);
@@ -9706,8 +13860,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
* Stray Mailbox Interrupt, mbxCommand <cmd>
* mbxStatus <status>
*/
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
- LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"(%d):0304 Stray Mailbox "
"Interrupt mbxCommand x%x "
"mbxStatus x%x\n",
@@ -9720,15 +13873,15 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
phba->sli.mbox_active = NULL;
spin_unlock_irqrestore(&phba->hbalock, iflag);
phba->last_completion_time = jiffies;
- del_timer(&phba->sli.mbox_tmo);
+ timer_delete(&phba->sli.mbox_tmo);
if (pmb->mbox_cmpl) {
lpfc_sli_pcimem_bcopy(mbox, pmbox,
MAILBOX_CMD_SIZE);
if (pmb->out_ext_byte_len &&
- pmb->context2)
+ pmb->ext_buf)
lpfc_sli_pcimem_bcopy(
phba->mbox_ext,
- pmb->context2,
+ pmb->ext_buf,
pmb->out_ext_byte_len);
}
if (pmb->mbox_flag & LPFC_MBX_IMED_UNREG) {
@@ -9742,10 +13895,8 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
pmbox->un.varWords[0], 0);
if (!pmbox->mbxStatus) {
- mp = (struct lpfc_dmabuf *)
- (pmb->context1);
- ndlp = (struct lpfc_nodelist *)
- pmb->context2;
+ mp = pmb->ctx_buf;
+ ndlp = pmb->ctx_ndlp;
/* Reg_LOGIN of dflt RPI was
* successful. new lets get
@@ -9758,8 +13909,8 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
pmb);
pmb->mbox_cmpl =
lpfc_mbx_cmpl_dflt_rpi;
- pmb->context1 = mp;
- pmb->context2 = ndlp;
+ pmb->ctx_buf = mp;
+ pmb->ctx_ndlp = ndlp;
pmb->vport = vport;
rc = lpfc_sli_issue_mbox(phba,
pmb,
@@ -9767,7 +13918,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
if (rc != MBX_BUSY)
lpfc_printf_log(phba,
KERN_ERR,
- LOG_MBOX | LOG_SLI,
+ LOG_TRACE_EVENT,
"0350 rc should have"
"been MBX_BUSY\n");
if (rc != MBX_NOT_FINISHED)
@@ -9782,7 +13933,21 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
spin_unlock_irqrestore(
&phba->pport->work_port_lock,
iflag);
- lpfc_mbox_cmpl_put(phba, pmb);
+
+ /* Do NOT queue MBX_HEARTBEAT to the worker
+ * thread for processing.
+ */
+ if (pmbox->mbxCommand == MBX_HEARTBEAT) {
+ /* Process mbox now */
+ phba->sli.mbox_active = NULL;
+ phba->sli.sli_flag &=
+ ~LPFC_SLI_MBOX_ACTIVE;
+ if (pmb->mbox_cmpl)
+ pmb->mbox_cmpl(phba, pmb);
+ } else {
+ /* Queue to worker thread to process */
+ lpfc_mbox_cmpl_put(phba, pmb);
+ }
}
} else
spin_unlock_irqrestore(&phba->hbalock, iflag);
@@ -9796,8 +13961,9 @@ send_current_mbox:
MBX_NOWAIT);
} while (rc == MBX_NOT_FINISHED);
if (rc != MBX_SUCCESS)
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
- LOG_SLI, "0349 rc should be "
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "0349 rc should be "
"MBX_SUCCESS\n");
}
@@ -9839,6 +14005,7 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id)
uint32_t ha_copy;
unsigned long status;
unsigned long iflag;
+ struct lpfc_sli_ring *pring;
/* Get the driver's phba structure from the dev_id and
* assume the HBA is not interrupting.
@@ -9859,16 +14026,16 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id)
/* Need to read HA REG for FCP ring and other ring events */
if (lpfc_readl(phba->HAregaddr, &ha_copy))
return IRQ_HANDLED;
- /* Clear up only attention source related to fast-path */
- spin_lock_irqsave(&phba->hbalock, iflag);
+
/*
* If there is deferred error attention, do not check for
* any interrupt.
*/
- if (unlikely(phba->hba_flag & DEFER_ERATT)) {
- spin_unlock_irqrestore(&phba->hbalock, iflag);
+ if (unlikely(test_bit(DEFER_ERATT, &phba->hba_flag)))
return IRQ_NONE;
- }
+
+ /* Clear up only attention source related to fast-path */
+ spin_lock_irqsave(&phba->hbalock, iflag);
writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
@@ -9883,10 +14050,9 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id)
status = (ha_copy & (HA_RXMASK << (4*LPFC_FCP_RING)));
status >>= (4*LPFC_FCP_RING);
+ pring = &phba->sli.sli3_ring[LPFC_FCP_RING];
if (status & HA_RXMASK)
- lpfc_sli_handle_fast_ring_event(phba,
- &phba->sli.ring[LPFC_FCP_RING],
- status);
+ lpfc_sli_handle_fast_ring_event(phba, pring, status);
if (phba->cfg_multi_ring_support == 2) {
/*
@@ -9897,7 +14063,7 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id)
status >>= (4*LPFC_EXTRA_RING);
if (status & HA_RXMASK) {
lpfc_sli_handle_fast_ring_event(phba,
- &phba->sli.ring[LPFC_EXTRA_RING],
+ &phba->sli.sli3_ring[LPFC_EXTRA_RING],
status);
}
}
@@ -9952,18 +14118,15 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
spin_unlock(&phba->hbalock);
return IRQ_NONE;
} else if (phba->ha_copy & HA_ERATT) {
- if (phba->hba_flag & HBA_ERATT_HANDLED)
+ if (test_and_set_bit(HBA_ERATT_HANDLED, &phba->hba_flag))
/* ERATT polling has handled ERATT */
phba->ha_copy &= ~HA_ERATT;
- else
- /* Indicate interrupt handler handles ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
}
/*
* If there is deferred error attention, do not check for any interrupt.
*/
- if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+ if (unlikely(test_bit(DEFER_ERATT, &phba->hba_flag))) {
spin_unlock(&phba->hbalock);
return IRQ_NONE;
}
@@ -10022,35 +14185,6 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
} /* lpfc_sli_intr_handler */
/**
- * lpfc_sli4_fcp_xri_abort_event_proc - Process fcp xri abort event
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked by the worker thread to process all the pending
- * SLI4 FCP abort XRI events.
- **/
-void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *phba)
-{
- struct lpfc_cq_event *cq_event;
-
- /* First, declare the fcp xri abort event has been handled */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~FCP_XRI_ABORT_EVENT;
- spin_unlock_irq(&phba->hbalock);
- /* Now, handle all the fcp xri abort events */
- while (!list_empty(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue)) {
- /* Get the first event from the head of the event queue */
- spin_lock_irq(&phba->hbalock);
- list_remove_head(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue,
- cq_event, struct lpfc_cq_event, list);
- spin_unlock_irq(&phba->hbalock);
- /* Notify aborted XRI for FCP work queue */
- lpfc_sli4_fcp_xri_aborted(phba, &cq_event->cqe.wcqe_axri);
- /* Free the event processed back to the free pool */
- lpfc_sli4_cq_event_release(phba, cq_event);
- }
-}
-
-/**
* lpfc_sli4_els_xri_abort_event_proc - Process els xri abort event
* @phba: pointer to lpfc hba data structure.
*
@@ -10060,118 +14194,111 @@ void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *phba)
void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *phba)
{
struct lpfc_cq_event *cq_event;
+ unsigned long iflags;
/* First, declare the els xri abort event has been handled */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~ELS_XRI_ABORT_EVENT;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(ELS_XRI_ABORT_EVENT, &phba->hba_flag);
+
/* Now, handle all the els xri abort events */
+ spin_lock_irqsave(&phba->sli4_hba.els_xri_abrt_list_lock, iflags);
while (!list_empty(&phba->sli4_hba.sp_els_xri_aborted_work_queue)) {
/* Get the first event from the head of the event queue */
- spin_lock_irq(&phba->hbalock);
list_remove_head(&phba->sli4_hba.sp_els_xri_aborted_work_queue,
cq_event, struct lpfc_cq_event, list);
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->sli4_hba.els_xri_abrt_list_lock,
+ iflags);
/* Notify aborted XRI for ELS work queue */
lpfc_sli4_els_xri_aborted(phba, &cq_event->cqe.wcqe_axri);
+
/* Free the event processed back to the free pool */
lpfc_sli4_cq_event_release(phba, cq_event);
+ spin_lock_irqsave(&phba->sli4_hba.els_xri_abrt_list_lock,
+ iflags);
}
+ spin_unlock_irqrestore(&phba->sli4_hba.els_xri_abrt_list_lock, iflags);
}
/**
- * lpfc_sli4_iocb_param_transfer - Transfer pIocbOut and cmpl status to pIocbIn
- * @phba: pointer to lpfc hba data structure
- * @pIocbIn: pointer to the rspiocbq
- * @pIocbOut: pointer to the cmdiocbq
- * @wcqe: pointer to the complete wcqe
- *
- * This routine transfers the fields of a command iocbq to a response iocbq
- * by copying all the IOCB fields from command iocbq and transferring the
- * completion status information from the complete wcqe.
- **/
-static void
-lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba,
- struct lpfc_iocbq *pIocbIn,
- struct lpfc_iocbq *pIocbOut,
- struct lpfc_wcqe_complete *wcqe)
-{
- unsigned long iflags;
- size_t offset = offsetof(struct lpfc_iocbq, iocb);
-
- memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
- sizeof(struct lpfc_iocbq) - offset);
- /* Map WCQE parameters into irspiocb parameters */
- pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
- if (pIocbOut->iocb_flag & LPFC_IO_FCP)
- if (pIocbIn->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR)
- pIocbIn->iocb.un.fcpi.fcpi_parm =
- pIocbOut->iocb.un.fcpi.fcpi_parm -
- wcqe->total_data_placed;
- else
- pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
- else {
- pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
- pIocbIn->iocb.un.genreq64.bdl.bdeSize = wcqe->total_data_placed;
- }
-
- /* Pick up HBA exchange busy condition */
- if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- pIocbIn->iocb_flag |= LPFC_EXCHANGE_BUSY;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- }
-}
-
-/**
- * lpfc_sli4_els_wcqe_to_rspiocbq - Get response iocbq from els wcqe
+ * lpfc_sli4_els_preprocess_rspiocbq - Get response iocbq from els wcqe
* @phba: Pointer to HBA context object.
- * @wcqe: Pointer to work-queue completion queue entry.
+ * @irspiocbq: Pointer to work-queue completion queue entry.
*
* This routine handles an ELS work-queue completion event and construct
- * a pseudo response ELS IODBQ from the SLI4 ELS WCQE for the common
+ * a pseudo response ELS IOCBQ from the SLI4 ELS WCQE for the common
* discovery engine to handle.
*
* Return: Pointer to the receive IOCBQ, NULL otherwise.
**/
static struct lpfc_iocbq *
-lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba,
- struct lpfc_iocbq *irspiocbq)
+lpfc_sli4_els_preprocess_rspiocbq(struct lpfc_hba *phba,
+ struct lpfc_iocbq *irspiocbq)
{
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_sli_ring *pring;
struct lpfc_iocbq *cmdiocbq;
struct lpfc_wcqe_complete *wcqe;
unsigned long iflags;
+ pring = lpfc_phba_elsring(phba);
+ if (unlikely(!pring))
+ return NULL;
+
wcqe = &irspiocbq->cq_event.cqe.wcqe_cmpl;
- spin_lock_irqsave(&phba->hbalock, iflags);
+ spin_lock_irqsave(&pring->ring_lock, iflags);
pring->stats.iocb_event++;
/* Look up the ELS command IOCB and create pseudo response IOCB */
cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
bf_get(lpfc_wcqe_c_request_tag, wcqe));
- spin_unlock_irqrestore(&phba->hbalock, iflags);
-
if (unlikely(!cmdiocbq)) {
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"0386 ELS complete with no corresponding "
- "cmdiocb: iotag (%d)\n",
- bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ "cmdiocb: 0x%x 0x%x 0x%x 0x%x\n",
+ wcqe->word0, wcqe->total_data_placed,
+ wcqe->parameter, wcqe->word3);
lpfc_sli_release_iocbq(phba, irspiocbq);
return NULL;
}
- /* Fake the irspiocbq and copy necessary response information */
- lpfc_sli4_iocb_param_transfer(phba, irspiocbq, cmdiocbq, wcqe);
+ memcpy(&irspiocbq->wqe, &cmdiocbq->wqe, sizeof(union lpfc_wqe128));
+ memcpy(&irspiocbq->wcqe_cmpl, wcqe, sizeof(*wcqe));
+
+ /* Put the iocb back on the txcmplq */
+ lpfc_sli_ringtxcmpl_put(phba, pring, cmdiocbq);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+ if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ irspiocbq->cmd_flag |= LPFC_EXCHANGE_BUSY;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
return irspiocbq;
}
+inline struct lpfc_cq_event *
+lpfc_cq_event_setup(struct lpfc_hba *phba, void *entry, int size)
+{
+ struct lpfc_cq_event *cq_event;
+
+ /* Allocate a new internal CQ_EVENT entry */
+ cq_event = lpfc_sli4_cq_event_alloc(phba);
+ if (!cq_event) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0602 Failed to alloc CQ_EVENT entry\n");
+ return NULL;
+ }
+
+ /* Move the CQE into the event */
+ memcpy(&cq_event->cqe, entry, size);
+ return cq_event;
+}
+
/**
- * lpfc_sli4_sp_handle_async_event - Handle an asynchroous event
+ * lpfc_sli4_sp_handle_async_event - Handle an asynchronous event
* @phba: Pointer to HBA context object.
- * @cqe: Pointer to mailbox completion queue entry.
+ * @mcqe: Pointer to mailbox completion queue entry.
*
- * This routine process a mailbox completion queue entry with asynchrous
+ * This routine process a mailbox completion queue entry with asynchronous
* event.
*
* Return: true if work posted to worker thread, otherwise false.
@@ -10187,21 +14314,16 @@ lpfc_sli4_sp_handle_async_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
"word2:x%x, word3:x%x\n", mcqe->word0,
mcqe->mcqe_tag0, mcqe->mcqe_tag1, mcqe->trailer);
- /* Allocate a new internal CQ_EVENT entry */
- cq_event = lpfc_sli4_cq_event_alloc(phba);
- if (!cq_event) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0394 Failed to allocate CQ_EVENT entry\n");
+ cq_event = lpfc_cq_event_setup(phba, mcqe, sizeof(struct lpfc_mcqe));
+ if (!cq_event)
return false;
- }
- /* Move the CQE into an asynchronous event entry */
- memcpy(&cq_event->cqe, mcqe, sizeof(struct lpfc_mcqe));
- spin_lock_irqsave(&phba->hbalock, iflags);
+ spin_lock_irqsave(&phba->sli4_hba.asynce_list_lock, iflags);
list_add_tail(&cq_event->list, &phba->sli4_hba.sp_asynce_work_queue);
+ spin_unlock_irqrestore(&phba->sli4_hba.asynce_list_lock, iflags);
+
/* Set the async event flag */
- phba->hba_flag |= ASYNC_EVENT;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ set_bit(ASYNC_EVENT, &phba->hba_flag);
return true;
}
@@ -10209,7 +14331,7 @@ lpfc_sli4_sp_handle_async_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
/**
* lpfc_sli4_sp_handle_mbox_event - Handle a mailbox completion event
* @phba: Pointer to HBA context object.
- * @cqe: Pointer to mailbox completion queue entry.
+ * @mcqe: Pointer to mailbox completion queue entry.
*
* This routine process a mailbox completion queue entry with mailbox
* completion event.
@@ -10238,7 +14360,7 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
spin_lock_irqsave(&phba->hbalock, iflags);
pmb = phba->sli.mbox_active;
if (unlikely(!pmb)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1832 No pending MBOX command to handle\n");
spin_unlock_irqrestore(&phba->hbalock, iflags);
goto out_no_mqe_complete;
@@ -10251,17 +14373,22 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
/* Reset heartbeat timer */
phba->last_completion_time = jiffies;
- del_timer(&phba->sli.mbox_tmo);
+ timer_delete(&phba->sli.mbox_tmo);
/* Move mbox data to caller's mailbox region, do endian swapping */
if (pmb->mbox_cmpl && mbox)
- lpfc_sli_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe));
- /* Set the mailbox status with SLI4 range 0x4000 */
- mcqe_status = bf_get(lpfc_mcqe_status, mcqe);
- if (mcqe_status != MB_CQE_STATUS_SUCCESS)
- bf_set(lpfc_mqe_status, mqe,
- (LPFC_MBX_ERROR_RANGE | mcqe_status));
+ lpfc_sli4_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe));
+ /*
+ * For mcqe errors, conditionally move a modified error code to
+ * the mbox so that the error will not be missed.
+ */
+ mcqe_status = bf_get(lpfc_mcqe_status, mcqe);
+ if (mcqe_status != MB_CQE_STATUS_SUCCESS) {
+ if (bf_get(lpfc_mqe_status, mqe) == MBX_SUCCESS)
+ bf_set(lpfc_mqe_status, mqe,
+ (LPFC_MBX_ERROR_RANGE | mcqe_status));
+ }
if (pmb->mbox_flag & LPFC_MBX_IMED_UNREG) {
pmb->mbox_flag &= ~LPFC_MBX_IMED_UNREG;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_MBOX_VPORT,
@@ -10269,21 +14396,32 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
mcqe_status,
pmbox->un.varWords[0], 0);
if (mcqe_status == MB_CQE_STATUS_SUCCESS) {
- mp = (struct lpfc_dmabuf *)(pmb->context1);
- ndlp = (struct lpfc_nodelist *)pmb->context2;
- /* Reg_LOGIN of dflt RPI was successful. Now lets get
- * RID of the PPI using the same mbox buffer.
+ mp = pmb->ctx_buf;
+ ndlp = pmb->ctx_ndlp;
+
+ /* Reg_LOGIN of dflt RPI was successful. Mark the
+ * node as having an UNREG_LOGIN in progress to stop
+ * an unsolicited PLOGI from the same NPortId from
+ * starting another mailbox transaction.
*/
+ set_bit(NLP_UNREG_INP, &ndlp->nlp_flag);
lpfc_unreg_login(phba, vport->vpi,
pmbox->un.varWords[0], pmb);
pmb->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi;
- pmb->context1 = mp;
- pmb->context2 = ndlp;
+ pmb->ctx_buf = mp;
+
+ /* No reference taken here. This is a default
+ * RPI reg/immediate unreg cycle. The reference was
+ * taken in the reg rpi path and is released when
+ * this mailbox completes.
+ */
+ pmb->ctx_ndlp = ndlp;
pmb->vport = vport;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
if (rc != MBX_BUSY)
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
- LOG_SLI, "0385 rc should "
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_TRACE_EVENT,
+ "0385 rc should "
"have been MBX_BUSY\n");
if (rc != MBX_NOT_FINISHED)
goto send_current_mbox;
@@ -10293,7 +14431,26 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
spin_unlock_irqrestore(&phba->pport->work_port_lock, iflags);
- /* There is mailbox completion work to do */
+ /* Do NOT queue MBX_HEARTBEAT to the worker thread for processing. */
+ if (pmbox->mbxCommand == MBX_HEARTBEAT) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ /* Release the mailbox command posting token */
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = NULL;
+ if (bf_get(lpfc_trailer_consumed, mcqe))
+ lpfc_sli4_mq_release(phba->sli4_hba.mbx_wq);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ /* Post the next mbox command, if there is one */
+ lpfc_sli4_post_async_mbox(phba);
+
+ /* Process cmpl now */
+ if (pmb->mbox_cmpl)
+ pmb->mbox_cmpl(phba, pmb);
+ return false;
+ }
+
+ /* There is mailbox completion work to queue to the worker thread */
spin_lock_irqsave(&phba->hbalock, iflags);
__lpfc_mbox_cmpl_put(phba, pmb);
phba->work_ha |= HA_MBATT;
@@ -10306,34 +14463,44 @@ send_current_mbox:
phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
/* Setting active mailbox pointer need to be in sync to flag clear */
phba->sli.mbox_active = NULL;
+ if (bf_get(lpfc_trailer_consumed, mcqe))
+ lpfc_sli4_mq_release(phba->sli4_hba.mbx_wq);
spin_unlock_irqrestore(&phba->hbalock, iflags);
/* Wake up worker thread to post the next pending mailbox command */
lpfc_worker_wake_up(phba);
+ return workposted;
+
out_no_mqe_complete:
+ spin_lock_irqsave(&phba->hbalock, iflags);
if (bf_get(lpfc_trailer_consumed, mcqe))
lpfc_sli4_mq_release(phba->sli4_hba.mbx_wq);
- return workposted;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return false;
}
/**
* lpfc_sli4_sp_handle_mcqe - Process a mailbox completion queue entry
* @phba: Pointer to HBA context object.
+ * @cq: Pointer to associated CQ
* @cqe: Pointer to mailbox completion queue entry.
*
* This routine process a mailbox completion queue entry, it invokes the
- * proper mailbox complete handling or asynchrous event handling routine
+ * proper mailbox complete handling or asynchronous event handling routine
* according to the MCQE's async bit.
*
* Return: true if work posted to worker thread, otherwise false.
**/
static bool
-lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
{
struct lpfc_mcqe mcqe;
bool workposted;
+ cq->CQ_mbox++;
+
/* Copy the mailbox MCQE and convert endian order as needed */
- lpfc_sli_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe));
+ lpfc_sli4_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe));
/* Invoke the proper event handling routine */
if (!bf_get(lpfc_trailer_async, &mcqe))
@@ -10346,6 +14513,7 @@ lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
/**
* lpfc_sli4_sp_handle_els_wcqe - Handle els work-queue completion event
* @phba: Pointer to HBA context object.
+ * @cq: Pointer to associated CQ
* @wcqe: Pointer to work-queue completion queue entry.
*
* This routine handles an ELS work-queue completion event.
@@ -10353,22 +14521,38 @@ lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
* Return: true if work posted to worker thread, otherwise false.
**/
static bool
-lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
+lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
struct lpfc_wcqe_complete *wcqe)
{
struct lpfc_iocbq *irspiocbq;
unsigned long iflags;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_FCP_RING];
+ struct lpfc_sli_ring *pring = cq->pring;
+ int txq_cnt = 0;
+ int txcmplq_cnt = 0;
+
+ /* Check for response status */
+ if (unlikely(bf_get(lpfc_wcqe_c_status, wcqe))) {
+ /* Log the error status */
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0357 ELS CQE error: status=x%x: "
+ "CQE: %08x %08x %08x %08x\n",
+ bf_get(lpfc_wcqe_c_status, wcqe),
+ wcqe->word0, wcqe->total_data_placed,
+ wcqe->parameter, wcqe->word3);
+ }
/* Get an irspiocbq for later ELS response processing use */
irspiocbq = lpfc_sli_get_iocbq(phba);
if (!irspiocbq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ if (!list_empty(&pring->txq))
+ txq_cnt++;
+ if (!list_empty(&pring->txcmplq))
+ txcmplq_cnt++;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0387 NO IOCBQ data: txq_cnt=%d iocb_cnt=%d "
- "fcp_txcmplq_cnt=%d, els_txcmplq_cnt=%d\n",
- pring->txq_cnt, phba->iocb_cnt,
- phba->sli.ring[LPFC_FCP_RING].txcmplq_cnt,
- phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt);
+ "els_txcmplq_cnt=%d\n",
+ txq_cnt, phba->iocb_cnt,
+ txcmplq_cnt);
return false;
}
@@ -10377,8 +14561,8 @@ lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
spin_lock_irqsave(&phba->hbalock, iflags);
list_add_tail(&irspiocbq->cq_event.list,
&phba->sli4_hba.sp_queue_event);
- phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
+ set_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag);
return true;
}
@@ -10388,13 +14572,16 @@ lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
* @phba: Pointer to HBA context object.
* @wcqe: Pointer to work-queue completion queue entry.
*
- * This routine handles slow-path WQ entry comsumed event by invoking the
+ * This routine handles slow-path WQ entry consumed event by invoking the
* proper WQ release routine to the slow-path WQ.
**/
static void
lpfc_sli4_sp_handle_rel_wcqe(struct lpfc_hba *phba,
struct lpfc_wcqe_release *wcqe)
{
+ /* sanity check on queue memory */
+ if (unlikely(!phba->sli4_hba.els_wq))
+ return;
/* Check for the slow-path ELS work queue */
if (bf_get(lpfc_wcqe_r_wq_id, wcqe) == phba->sli4_hba.els_wq->queue_id)
lpfc_sli4_wq_release(phba->sli4_hba.els_wq,
@@ -10426,45 +14613,48 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
struct lpfc_cq_event *cq_event;
unsigned long iflags;
- /* Allocate a new internal CQ_EVENT entry */
- cq_event = lpfc_sli4_cq_event_alloc(phba);
- if (!cq_event) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0602 Failed to allocate CQ_EVENT entry\n");
- return false;
- }
-
- /* Move the CQE into the proper xri abort event list */
- memcpy(&cq_event->cqe, wcqe, sizeof(struct sli4_wcqe_xri_aborted));
switch (cq->subtype) {
- case LPFC_FCP:
- spin_lock_irqsave(&phba->hbalock, iflags);
- list_add_tail(&cq_event->list,
- &phba->sli4_hba.sp_fcp_xri_aborted_work_queue);
- /* Set the fcp xri abort event flag */
- phba->hba_flag |= FCP_XRI_ABORT_EVENT;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- workposted = true;
+ case LPFC_IO:
+ lpfc_sli4_io_xri_aborted(phba, wcqe, cq->hdwq);
+ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
+ /* Notify aborted XRI for NVME work queue */
+ if (phba->nvmet_support)
+ lpfc_sli4_nvmet_xri_aborted(phba, wcqe);
+ }
+ workposted = false;
break;
+ case LPFC_NVME_LS: /* NVME LS uses ELS resources */
case LPFC_ELS:
- spin_lock_irqsave(&phba->hbalock, iflags);
+ cq_event = lpfc_cq_event_setup(phba, wcqe, sizeof(*wcqe));
+ if (!cq_event) {
+ workposted = false;
+ break;
+ }
+ cq_event->hdwq = cq->hdwq;
+ spin_lock_irqsave(&phba->sli4_hba.els_xri_abrt_list_lock,
+ iflags);
list_add_tail(&cq_event->list,
&phba->sli4_hba.sp_els_xri_aborted_work_queue);
/* Set the els xri abort event flag */
- phba->hba_flag |= ELS_XRI_ABORT_EVENT;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ set_bit(ELS_XRI_ABORT_EVENT, &phba->hba_flag);
+ spin_unlock_irqrestore(&phba->sli4_hba.els_xri_abrt_list_lock,
+ iflags);
workposted = true;
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0603 Invalid work queue CQE subtype (x%x)\n",
- cq->subtype);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0603 Invalid CQ subtype %d: "
+ "%08x %08x %08x %08x\n",
+ cq->subtype, wcqe->word0, wcqe->parameter,
+ wcqe->word2, wcqe->word3);
workposted = false;
break;
}
return workposted;
}
+#define FC_RCTL_MDS_DIAGS 0xF4
+
/**
* lpfc_sli4_sp_handle_rcqe - Process a receive-queue completion queue entry
* @phba: Pointer to HBA context object.
@@ -10478,44 +14668,117 @@ static bool
lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe)
{
bool workposted = false;
+ struct fc_frame_header *fc_hdr;
struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq;
struct lpfc_queue *drq = phba->sli4_hba.dat_rq;
+ struct lpfc_nvmet_tgtport *tgtp;
struct hbq_dmabuf *dma_buf;
- uint32_t status;
+ uint32_t status, rq_id;
unsigned long iflags;
- if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id)
+ /* sanity check on queue memory */
+ if (unlikely(!hrq) || unlikely(!drq))
+ return workposted;
+
+ if (bf_get(lpfc_cqe_code, rcqe) == CQE_CODE_RECEIVE_V1)
+ rq_id = bf_get(lpfc_rcqe_rq_id_v1, rcqe);
+ else
+ rq_id = bf_get(lpfc_rcqe_rq_id, rcqe);
+ if (rq_id != hrq->queue_id)
goto out;
status = bf_get(lpfc_rcqe_status, rcqe);
switch (status) {
case FC_STATUS_RQ_BUF_LEN_EXCEEDED:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2537 Receive Frame Truncated!!\n");
+ fallthrough;
case FC_STATUS_RQ_SUCCESS:
- lpfc_sli4_rq_release(hrq, drq);
spin_lock_irqsave(&phba->hbalock, iflags);
+ lpfc_sli4_rq_release(hrq, drq);
dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list);
if (!dma_buf) {
+ hrq->RQ_no_buf_found++;
spin_unlock_irqrestore(&phba->hbalock, iflags);
goto out;
}
+ hrq->RQ_rcv_buf++;
+ hrq->RQ_buf_posted--;
memcpy(&dma_buf->cq_event.cqe.rcqe_cmpl, rcqe, sizeof(*rcqe));
- /* save off the frame for the word thread to process */
+
+ fc_hdr = (struct fc_frame_header *)dma_buf->hbuf.virt;
+
+ if (fc_hdr->fh_r_ctl == FC_RCTL_MDS_DIAGS ||
+ fc_hdr->fh_r_ctl == FC_RCTL_DD_UNSOL_DATA) {
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ /* Handle MDS Loopback frames */
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ lpfc_sli4_handle_mds_loopback(phba->pport,
+ dma_buf);
+ else
+ lpfc_in_buf_free(phba, &dma_buf->dbuf);
+ break;
+ }
+
+ /* save off the frame for the work thread to process */
list_add_tail(&dma_buf->cq_event.list,
&phba->sli4_hba.sp_queue_event);
- /* Frame received */
- phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
+ /* Frame received */
+ set_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag);
workposted = true;
break;
- case FC_STATUS_INSUFF_BUF_NEED_BUF:
case FC_STATUS_INSUFF_BUF_FRM_DISC:
+ if (phba->nvmet_support) {
+ tgtp = phba->targetport->private;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6402 RQE Error x%x, posted %d err_cnt "
+ "%d: %x %x %x\n",
+ status, hrq->RQ_buf_posted,
+ hrq->RQ_no_posted_buf,
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out),
+ atomic_read(&tgtp->xmt_fcp_release));
+ }
+ fallthrough;
+
+ case FC_STATUS_INSUFF_BUF_NEED_BUF:
+ hrq->RQ_no_posted_buf++;
/* Post more buffers if possible */
+ set_bit(HBA_POST_RECEIVE_BUFFER, &phba->hba_flag);
+ workposted = true;
+ break;
+ case FC_STATUS_RQ_DMA_FAILURE:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2564 RQE DMA Error x%x, x%08x x%08x x%08x "
+ "x%08x\n",
+ status, rcqe->word0, rcqe->word1,
+ rcqe->word2, rcqe->word3);
+
+ /* If IV set, no further recovery */
+ if (bf_get(lpfc_rcqe_iv, rcqe))
+ break;
+
+ /* recycle consumed resource */
spin_lock_irqsave(&phba->hbalock, iflags);
- phba->hba_flag |= HBA_POST_RECEIVE_BUFFER;
+ lpfc_sli4_rq_release(hrq, drq);
+ dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list);
+ if (!dma_buf) {
+ hrq->RQ_no_buf_found++;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ break;
+ }
+ hrq->RQ_rcv_buf++;
+ hrq->RQ_buf_posted--;
spin_unlock_irqrestore(&phba->hbalock, iflags);
- workposted = true;
+ lpfc_in_buf_free(phba, &dma_buf->dbuf);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2565 Unexpected RQE Status x%x, w0-3 x%08x "
+ "x%08x x%08x x%08x\n",
+ status, rcqe->word0, rcqe->word1,
+ rcqe->word2, rcqe->word3);
break;
}
out:
@@ -10526,7 +14789,7 @@ out:
* lpfc_sli4_sp_handle_cqe - Process a slow path completion queue entry
* @phba: Pointer to HBA context object.
* @cq: Pointer to the completion queue.
- * @wcqe: Pointer to a completion queue entry.
+ * @cqe: Pointer to a completion queue entry.
*
* This routine process a slow-path work-queue or receive queue completion queue
* entry.
@@ -10541,14 +14804,14 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
bool workposted = false;
/* Copy the work queue CQE and convert endian order if needed */
- lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe));
+ lpfc_sli4_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe));
/* Check and process for different type of WCQE and dispatch */
switch (bf_get(lpfc_cqe_code, &cqevt)) {
case CQE_CODE_COMPL_WQE:
/* Process the WQ/RQ complete event */
phba->last_completion_time = jiffies;
- workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
+ workposted = lpfc_sli4_sp_handle_els_wcqe(phba, cq,
(struct lpfc_wcqe_complete *)&cqevt);
break;
case CQE_CODE_RELEASE_WQE:
@@ -10563,13 +14826,14 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
(struct sli4_wcqe_xri_aborted *)&cqevt);
break;
case CQE_CODE_RECEIVE:
+ case CQE_CODE_RECEIVE_V1:
/* Process the RQ event */
phba->last_completion_time = jiffies;
workposted = lpfc_sli4_sp_handle_rcqe(phba,
(struct lpfc_rcqe *)&cqevt);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0388 Not a valid WCQE code: x%x\n",
bf_get(lpfc_cqe_code, &cqevt));
break;
@@ -10581,6 +14845,7 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
* lpfc_sli4_sp_handle_eqe - Process a slow-path event queue entry
* @phba: Pointer to HBA context object.
* @eqe: Pointer to fast-path event queue entry.
+ * @speq: Pointer to slow-path event queue.
*
* This routine process a event queue entry from the slow-path event queue.
* It will check the MajorCode and MinorCode to determine this is for a
@@ -10591,28 +14856,16 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
*
**/
static void
-lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
+lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
+ struct lpfc_queue *speq)
{
- struct lpfc_queue *cq = NULL, *childq, *speq;
- struct lpfc_cqe *cqe;
- bool workposted = false;
- int ecount = 0;
+ struct lpfc_queue *cq = NULL, *childq;
uint16_t cqid;
-
- if (bf_get_le32(lpfc_eqe_major_code, eqe) != 0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0359 Not a valid slow-path completion "
- "event: majorcode=x%x, minorcode=x%x\n",
- bf_get_le32(lpfc_eqe_major_code, eqe),
- bf_get_le32(lpfc_eqe_minor_code, eqe));
- return;
- }
+ int ret = 0;
/* Get the reference to the corresponding CQ */
cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
- /* Search for completion queue pointer matching this cqid */
- speq = phba->sli4_hba.sp_eq;
list_for_each_entry(childq, &speq->child_list, list) {
if (childq->queue_id == cqid) {
cq = childq;
@@ -10621,48 +14874,172 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
}
if (unlikely(!cq)) {
if (phba->sli.sli_flag & LPFC_SLI_ACTIVE)
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0365 Slow-path CQ identifier "
"(%d) does not exist\n", cqid);
return;
}
+ /* Save EQ associated with this CQ */
+ cq->assoc_qp = speq;
+
+ if (is_kdump_kernel())
+ ret = queue_work(phba->wq, &cq->spwork);
+ else
+ ret = queue_work_on(cq->chann, phba->wq, &cq->spwork);
+
+ if (!ret)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0390 Cannot schedule queue work "
+ "for CQ eqcqid=%d, cqid=%d on CPU %d\n",
+ cqid, cq->queue_id, raw_smp_processor_id());
+}
+
+/**
+ * __lpfc_sli4_process_cq - Process elements of a CQ
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to CQ to be processed
+ * @handler: Routine to process each cqe
+ * @delay: Pointer to usdelay to set in case of rescheduling of the handler
+ *
+ * This routine processes completion queue entries in a CQ. While a valid
+ * queue element is found, the handler is called. During processing checks
+ * are made for periodic doorbell writes to let the hardware know of
+ * element consumption.
+ *
+ * If the max limit on cqes to process is hit, or there are no more valid
+ * entries, the loop stops. If we processed a sufficient number of elements,
+ * meaning there is sufficient load, rather than rearming and generating
+ * another interrupt, a cq rescheduling delay will be set. A delay of 0
+ * indicates no rescheduling.
+ *
+ * Returns True if work scheduled, False otherwise.
+ **/
+static bool
+__lpfc_sli4_process_cq(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ bool (*handler)(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_cqe *), unsigned long *delay)
+{
+ struct lpfc_cqe *cqe;
+ bool workposted = false;
+ int count = 0, consumed = 0;
+ bool arm = true;
+
+ /* default - no reschedule */
+ *delay = 0;
+
+ if (cmpxchg(&cq->queue_claimed, 0, 1) != 0)
+ goto rearm_and_exit;
+
/* Process all the entries to the CQ */
+ cq->q_flag = 0;
+ cqe = lpfc_sli4_cq_get(cq);
+ while (cqe) {
+ workposted |= handler(phba, cq, cqe);
+ __lpfc_sli4_consume_cqe(phba, cq, cqe);
+
+ consumed++;
+ if (!(++count % cq->max_proc_limit))
+ break;
+
+ if (!(count % cq->notify_interval)) {
+ phba->sli4_hba.sli4_write_cq_db(phba, cq, consumed,
+ LPFC_QUEUE_NOARM);
+ consumed = 0;
+ cq->assoc_qp->q_flag |= HBA_EQ_DELAY_CHK;
+ }
+
+ if (count == LPFC_NVMET_CQ_NOTIFY)
+ cq->q_flag |= HBA_NVMET_CQ_NOTIFY;
+
+ cqe = lpfc_sli4_cq_get(cq);
+ }
+ if (count >= phba->cfg_cq_poll_threshold) {
+ *delay = 1;
+ arm = false;
+ }
+
+ /* Track the max number of CQEs processed in 1 EQ */
+ if (count > cq->CQ_max_cqe)
+ cq->CQ_max_cqe = count;
+
+ cq->assoc_qp->EQ_cqe_cnt += count;
+
+ /* Catch the no cq entry condition */
+ if (unlikely(count == 0))
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0369 No entry from completion queue "
+ "qid=%d\n", cq->queue_id);
+
+ xchg(&cq->queue_claimed, 0);
+
+rearm_and_exit:
+ phba->sli4_hba.sli4_write_cq_db(phba, cq, consumed,
+ arm ? LPFC_QUEUE_REARM : LPFC_QUEUE_NOARM);
+
+ return workposted;
+}
+
+/**
+ * __lpfc_sli4_sp_process_cq - Process a slow-path event queue entry
+ * @cq: pointer to CQ to process
+ *
+ * This routine calls the cq processing routine with a handler specific
+ * to the type of queue bound to it.
+ *
+ * The CQ routine returns two values: the first is the calling status,
+ * which indicates whether work was queued to the background discovery
+ * thread. If true, the routine should wakeup the discovery thread;
+ * the second is the delay parameter. If non-zero, rather than rearming
+ * the CQ and yet another interrupt, the CQ handler should be queued so
+ * that it is processed in a subsequent polling action. The value of
+ * the delay indicates when to reschedule it.
+ **/
+static void
+__lpfc_sli4_sp_process_cq(struct lpfc_queue *cq)
+{
+ struct lpfc_hba *phba = cq->phba;
+ unsigned long delay;
+ bool workposted = false;
+ int ret = 0;
+
+ /* Process and rearm the CQ */
switch (cq->type) {
case LPFC_MCQ:
- while ((cqe = lpfc_sli4_cq_get(cq))) {
- workposted |= lpfc_sli4_sp_handle_mcqe(phba, cqe);
- if (!(++ecount % LPFC_GET_QE_REL_INT))
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
- }
+ workposted |= __lpfc_sli4_process_cq(phba, cq,
+ lpfc_sli4_sp_handle_mcqe,
+ &delay);
break;
case LPFC_WCQ:
- while ((cqe = lpfc_sli4_cq_get(cq))) {
- if (cq->subtype == LPFC_FCP)
- workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq,
- cqe);
- else
- workposted |= lpfc_sli4_sp_handle_cqe(phba, cq,
- cqe);
- if (!(++ecount % LPFC_GET_QE_REL_INT))
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
- }
+ if (cq->subtype == LPFC_IO)
+ workposted |= __lpfc_sli4_process_cq(phba, cq,
+ lpfc_sli4_fp_handle_cqe,
+ &delay);
+ else
+ workposted |= __lpfc_sli4_process_cq(phba, cq,
+ lpfc_sli4_sp_handle_cqe,
+ &delay);
break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0370 Invalid completion queue type (%d)\n",
cq->type);
return;
}
- /* Catch the no cq entry condition, log an error */
- if (unlikely(ecount == 0))
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0371 No entry from the CQ: identifier "
- "(x%x), type (%d)\n", cq->queue_id, cq->type);
-
- /* In any case, flash and re-arm the RCQ */
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
+ if (delay) {
+ if (is_kdump_kernel())
+ ret = queue_delayed_work(phba->wq, &cq->sched_spwork,
+ delay);
+ else
+ ret = queue_delayed_work_on(cq->chann, phba->wq,
+ &cq->sched_spwork, delay);
+ if (!ret)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0394 Cannot schedule queue work "
+ "for cqid=%d on CPU %d\n",
+ cq->queue_id, cq->chann);
+ }
/* wake up worker thread if there are works to be done */
if (workposted)
@@ -10670,51 +15047,78 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
}
/**
+ * lpfc_sli4_sp_process_cq - slow-path work handler when started by
+ * interrupt
+ * @work: pointer to work element
+ *
+ * translates from the work handler and calls the slow-path handler.
+ **/
+static void
+lpfc_sli4_sp_process_cq(struct work_struct *work)
+{
+ struct lpfc_queue *cq = container_of(work, struct lpfc_queue, spwork);
+
+ __lpfc_sli4_sp_process_cq(cq);
+}
+
+/**
+ * lpfc_sli4_dly_sp_process_cq - slow-path work handler when started by timer
+ * @work: pointer to work element
+ *
+ * translates from the work handler and calls the slow-path handler.
+ **/
+static void
+lpfc_sli4_dly_sp_process_cq(struct work_struct *work)
+{
+ struct lpfc_queue *cq = container_of(to_delayed_work(work),
+ struct lpfc_queue, sched_spwork);
+
+ __lpfc_sli4_sp_process_cq(cq);
+}
+
+/**
* lpfc_sli4_fp_handle_fcp_wcqe - Process fast-path work queue completion entry
- * @eqe: Pointer to fast-path completion queue entry.
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to associated CQ
+ * @wcqe: Pointer to work-queue completion queue entry.
*
* This routine process a fast-path work queue completion entry from fast-path
* event queue for FCP command response completion.
**/
static void
-lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba,
+lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
struct lpfc_wcqe_complete *wcqe)
{
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_FCP_RING];
+ struct lpfc_sli_ring *pring = cq->pring;
struct lpfc_iocbq *cmdiocbq;
- struct lpfc_iocbq irspiocbq;
unsigned long iflags;
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->stats.iocb_event++;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
-
/* Check for response status */
if (unlikely(bf_get(lpfc_wcqe_c_status, wcqe))) {
/* If resource errors reported from HBA, reduce queue
* depth of the SCSI device.
*/
- if ((bf_get(lpfc_wcqe_c_status, wcqe) ==
- IOSTAT_LOCAL_REJECT) &&
- (wcqe->parameter == IOERR_NO_RESOURCES)) {
+ if (((bf_get(lpfc_wcqe_c_status, wcqe) ==
+ IOSTAT_LOCAL_REJECT)) &&
+ ((wcqe->parameter & IOERR_PARAM_MASK) ==
+ IOERR_NO_RESOURCES))
phba->lpfc_rampdown_queue_depth(phba);
- }
- /* Log the error status */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0373 FCP complete error: status=x%x, "
- "hw_status=x%x, total_data_specified=%d, "
- "parameter=x%x, word3=x%x\n",
+
+ /* Log the cmpl status */
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "0373 FCP CQE cmpl: status=x%x: "
+ "CQE: %08x %08x %08x %08x\n",
bf_get(lpfc_wcqe_c_status, wcqe),
- bf_get(lpfc_wcqe_c_hw_status, wcqe),
- wcqe->total_data_placed, wcqe->parameter,
- wcqe->word3);
+ wcqe->word0, wcqe->total_data_placed,
+ wcqe->parameter, wcqe->word3);
}
/* Look up the FCP command IOCB and create pseudo response IOCB */
- spin_lock_irqsave(&phba->hbalock, iflags);
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ pring->stats.iocb_event++;
cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
bf_get(lpfc_wcqe_c_request_tag, wcqe));
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
if (unlikely(!cmdiocbq)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"0374 FCP complete with no corresponding "
@@ -10722,25 +15126,34 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba,
bf_get(lpfc_wcqe_c_request_tag, wcqe));
return;
}
- if (unlikely(!cmdiocbq->iocb_cmpl)) {
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ cmdiocbq->isr_timestamp = cq->isr_timestamp;
+#endif
+ if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ cmdiocbq->cmd_flag |= LPFC_EXCHANGE_BUSY;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
+
+ if (cmdiocbq->cmd_cmpl) {
+ /* For FCP the flag is cleared in cmd_cmpl */
+ if (!(cmdiocbq->cmd_flag & LPFC_IO_FCP) &&
+ cmdiocbq->cmd_flag & LPFC_DRIVER_ABORTED) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ cmdiocbq->cmd_flag &= ~LPFC_DRIVER_ABORTED;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
+
+ /* Pass the cmd_iocb and the wcqe to the upper layer */
+ memcpy(&cmdiocbq->wcqe_cmpl, wcqe,
+ sizeof(struct lpfc_wcqe_complete));
+ cmdiocbq->cmd_cmpl(phba, cmdiocbq, cmdiocbq);
+ } else {
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"0375 FCP cmdiocb not callback function "
"iotag: (%d)\n",
bf_get(lpfc_wcqe_c_request_tag, wcqe));
- return;
- }
-
- /* Fake the irspiocb and copy necessary response information */
- lpfc_sli4_iocb_param_transfer(phba, &irspiocbq, cmdiocbq, wcqe);
-
- if (cmdiocbq->iocb_flag & LPFC_DRIVER_ABORTED) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- cmdiocbq->iocb_flag &= ~LPFC_DRIVER_ABORTED;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
}
-
- /* Pass the cmd_iocb and the rsp state to the upper layer */
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, &irspiocbq);
}
/**
@@ -10749,7 +15162,7 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba,
* @cq: Pointer to completion queue.
* @wcqe: Pointer to work-queue completion queue entry.
*
- * This routine handles an fast-path WQ entry comsumed event by invoking the
+ * This routine handles an fast-path WQ entry consumed event by invoking the
* proper WQ release routine to the slow-path WQ.
**/
static void
@@ -10758,14 +15171,16 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
{
struct lpfc_queue *childwq;
bool wqid_matched = false;
- uint16_t fcp_wqid;
+ uint16_t hba_wqid;
/* Check for fast-path FCP work queue release */
- fcp_wqid = bf_get(lpfc_wcqe_r_wq_id, wcqe);
+ hba_wqid = bf_get(lpfc_wcqe_r_wq_id, wcqe);
list_for_each_entry(childwq, &cq->child_list, list) {
- if (childwq->queue_id == fcp_wqid) {
+ if (childwq->queue_id == hba_wqid) {
lpfc_sli4_wq_release(childwq,
bf_get(lpfc_wcqe_r_wqe_index, wcqe));
+ if (childwq->q_flag & HBA_NVMET_WQFULL)
+ lpfc_nvmet_wqfull_process(phba, childwq);
wqid_matched = true;
break;
}
@@ -10774,49 +15189,205 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
if (wqid_matched != true)
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"2580 Fast-path wqe consume event carries "
- "miss-matched qid: wcqe-qid=x%x\n", fcp_wqid);
+ "miss-matched qid: wcqe-qid=x%x\n", hba_wqid);
}
/**
- * lpfc_sli4_fp_handle_wcqe - Process fast-path work queue completion entry
+ * lpfc_sli4_nvmet_handle_rcqe - Process a receive-queue completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to completion queue.
+ * @rcqe: Pointer to receive-queue completion queue entry.
+ *
+ * This routine process a receive-queue completion queue entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_nvmet_handle_rcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_rcqe *rcqe)
+{
+ bool workposted = false;
+ struct lpfc_queue *hrq;
+ struct lpfc_queue *drq;
+ struct rqb_dmabuf *dma_buf;
+ struct fc_frame_header *fc_hdr;
+ struct lpfc_nvmet_tgtport *tgtp;
+ uint32_t status, rq_id;
+ unsigned long iflags;
+ uint32_t fctl, idx;
+
+ if ((phba->nvmet_support == 0) ||
+ (phba->sli4_hba.nvmet_cqset == NULL))
+ return workposted;
+
+ idx = cq->queue_id - phba->sli4_hba.nvmet_cqset[0]->queue_id;
+ hrq = phba->sli4_hba.nvmet_mrq_hdr[idx];
+ drq = phba->sli4_hba.nvmet_mrq_data[idx];
+
+ /* sanity check on queue memory */
+ if (unlikely(!hrq) || unlikely(!drq))
+ return workposted;
+
+ if (bf_get(lpfc_cqe_code, rcqe) == CQE_CODE_RECEIVE_V1)
+ rq_id = bf_get(lpfc_rcqe_rq_id_v1, rcqe);
+ else
+ rq_id = bf_get(lpfc_rcqe_rq_id, rcqe);
+
+ if ((phba->nvmet_support == 0) ||
+ (rq_id != hrq->queue_id))
+ return workposted;
+
+ status = bf_get(lpfc_rcqe_status, rcqe);
+ switch (status) {
+ case FC_STATUS_RQ_BUF_LEN_EXCEEDED:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6126 Receive Frame Truncated!!\n");
+ fallthrough;
+ case FC_STATUS_RQ_SUCCESS:
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ lpfc_sli4_rq_release(hrq, drq);
+ dma_buf = lpfc_sli_rqbuf_get(phba, hrq);
+ if (!dma_buf) {
+ hrq->RQ_no_buf_found++;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ goto out;
+ }
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ hrq->RQ_rcv_buf++;
+ hrq->RQ_buf_posted--;
+ fc_hdr = (struct fc_frame_header *)dma_buf->hbuf.virt;
+
+ /* Just some basic sanity checks on FCP Command frame */
+ fctl = (fc_hdr->fh_f_ctl[0] << 16 |
+ fc_hdr->fh_f_ctl[1] << 8 |
+ fc_hdr->fh_f_ctl[2]);
+ if (((fctl &
+ (FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT)) !=
+ (FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT)) ||
+ (fc_hdr->fh_seq_cnt != 0)) /* 0 byte swapped is still 0 */
+ goto drop;
+
+ if (fc_hdr->fh_type == FC_TYPE_FCP) {
+ dma_buf->bytes_recv = bf_get(lpfc_rcqe_length, rcqe);
+ lpfc_nvmet_unsol_fcp_event(
+ phba, idx, dma_buf, cq->isr_timestamp,
+ cq->q_flag & HBA_NVMET_CQ_NOTIFY);
+ return false;
+ }
+drop:
+ lpfc_rq_buf_free(phba, &dma_buf->hbuf);
+ break;
+ case FC_STATUS_INSUFF_BUF_FRM_DISC:
+ if (phba->nvmet_support) {
+ tgtp = phba->targetport->private;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6401 RQE Error x%x, posted %d err_cnt "
+ "%d: %x %x %x\n",
+ status, hrq->RQ_buf_posted,
+ hrq->RQ_no_posted_buf,
+ atomic_read(&tgtp->rcv_fcp_cmd_in),
+ atomic_read(&tgtp->rcv_fcp_cmd_out),
+ atomic_read(&tgtp->xmt_fcp_release));
+ }
+ fallthrough;
+
+ case FC_STATUS_INSUFF_BUF_NEED_BUF:
+ hrq->RQ_no_posted_buf++;
+ /* Post more buffers if possible */
+ break;
+ case FC_STATUS_RQ_DMA_FAILURE:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2575 RQE DMA Error x%x, x%08x x%08x x%08x "
+ "x%08x\n",
+ status, rcqe->word0, rcqe->word1,
+ rcqe->word2, rcqe->word3);
+
+ /* If IV set, no further recovery */
+ if (bf_get(lpfc_rcqe_iv, rcqe))
+ break;
+
+ /* recycle consumed resource */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ lpfc_sli4_rq_release(hrq, drq);
+ dma_buf = lpfc_sli_rqbuf_get(phba, hrq);
+ if (!dma_buf) {
+ hrq->RQ_no_buf_found++;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ break;
+ }
+ hrq->RQ_rcv_buf++;
+ hrq->RQ_buf_posted--;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ lpfc_rq_buf_free(phba, &dma_buf->hbuf);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2576 Unexpected RQE Status x%x, w0-3 x%08x "
+ "x%08x x%08x x%08x\n",
+ status, rcqe->word0, rcqe->word1,
+ rcqe->word2, rcqe->word3);
+ break;
+ }
+out:
+ return workposted;
+}
+
+/**
+ * lpfc_sli4_fp_handle_cqe - Process fast-path work queue completion entry
+ * @phba: adapter with cq
* @cq: Pointer to the completion queue.
- * @eqe: Pointer to fast-path completion queue entry.
+ * @cqe: Pointer to fast-path completion queue entry.
*
* This routine process a fast-path work queue completion entry from fast-path
* event queue for FCP command response completion.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
**/
-static int
-lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+static bool
+lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
struct lpfc_cqe *cqe)
{
struct lpfc_wcqe_release wcqe;
bool workposted = false;
/* Copy the work queue CQE and convert endian order if needed */
- lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
+ lpfc_sli4_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
/* Check and process for different type of WCQE and dispatch */
switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
case CQE_CODE_COMPL_WQE:
+ case CQE_CODE_NVME_ERSP:
+ cq->CQ_wq++;
/* Process the WQ complete event */
phba->last_completion_time = jiffies;
- lpfc_sli4_fp_handle_fcp_wcqe(phba,
+ if (cq->subtype == LPFC_IO || cq->subtype == LPFC_NVME_LS)
+ lpfc_sli4_fp_handle_fcp_wcqe(phba, cq,
(struct lpfc_wcqe_complete *)&wcqe);
break;
case CQE_CODE_RELEASE_WQE:
+ cq->CQ_release_wqe++;
/* Process the WQ release event */
lpfc_sli4_fp_handle_rel_wcqe(phba, cq,
(struct lpfc_wcqe_release *)&wcqe);
break;
case CQE_CODE_XRI_ABORTED:
+ cq->CQ_xri_aborted++;
/* Process the WQ XRI abort event */
phba->last_completion_time = jiffies;
workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
(struct sli4_wcqe_xri_aborted *)&wcqe);
break;
+ case CQE_CODE_RECEIVE_V1:
+ case CQE_CODE_RECEIVE:
+ phba->last_completion_time = jiffies;
+ if (cq->subtype == LPFC_NVMET) {
+ workposted = lpfc_sli4_nvmet_handle_rcqe(
+ phba, cq, (struct lpfc_rcqe *)&wcqe);
+ }
+ break;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0144 Not a valid WCQE code: x%x\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0144 Not a valid CQE code: x%x\n",
bf_get(lpfc_wcqe_c_code, &wcqe));
break;
}
@@ -10824,9 +15395,72 @@ lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
}
/**
- * lpfc_sli4_fp_handle_eqe - Process a fast-path event queue entry
+ * __lpfc_sli4_hba_process_cq - Process a fast-path event queue entry
+ * @cq: Pointer to CQ to be processed
+ *
+ * This routine calls the cq processing routine with the handler for
+ * fast path CQEs.
+ *
+ * The CQ routine returns two values: the first is the calling status,
+ * which indicates whether work was queued to the background discovery
+ * thread. If true, the routine should wakeup the discovery thread;
+ * the second is the delay parameter. If non-zero, rather than rearming
+ * the CQ and yet another interrupt, the CQ handler should be queued so
+ * that it is processed in a subsequent polling action. The value of
+ * the delay indicates when to reschedule it.
+ **/
+static void
+__lpfc_sli4_hba_process_cq(struct lpfc_queue *cq)
+{
+ struct lpfc_hba *phba = cq->phba;
+ unsigned long delay;
+ bool workposted = false;
+ int ret;
+
+ /* process and rearm the CQ */
+ workposted |= __lpfc_sli4_process_cq(phba, cq, lpfc_sli4_fp_handle_cqe,
+ &delay);
+
+ if (delay) {
+ if (is_kdump_kernel())
+ ret = queue_delayed_work(phba->wq, &cq->sched_irqwork,
+ delay);
+ else
+ ret = queue_delayed_work_on(cq->chann, phba->wq,
+ &cq->sched_irqwork, delay);
+ if (!ret)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0367 Cannot schedule queue work "
+ "for cqid=%d on CPU %d\n",
+ cq->queue_id, cq->chann);
+ }
+
+ /* wake up worker thread if there are works to be done */
+ if (workposted)
+ lpfc_worker_wake_up(phba);
+}
+
+/**
+ * lpfc_sli4_hba_process_cq - fast-path work handler when started by
+ * interrupt
+ * @work: pointer to work element
+ *
+ * translates from the work handler and calls the fast-path handler.
+ **/
+static void
+lpfc_sli4_hba_process_cq(struct work_struct *work)
+{
+ struct lpfc_queue *cq = container_of(work, struct lpfc_queue, irqwork);
+
+ __lpfc_sli4_hba_process_cq(cq);
+}
+
+/**
+ * lpfc_sli4_hba_handle_eqe - Process a fast-path event queue entry
* @phba: Pointer to HBA context object.
+ * @eq: Pointer to the queue structure.
* @eqe: Pointer to fast-path event queue entry.
+ * @poll_mode: poll_mode to execute processing the cq.
*
* This routine process a event queue entry from the fast-path event queue.
* It will check the MajorCode and MinorCode to determine this is for a
@@ -10836,156 +15470,110 @@ lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
* completion queue, and then return.
**/
static void
-lpfc_sli4_fp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
- uint32_t fcp_cqidx)
+lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ struct lpfc_eqe *eqe, enum lpfc_poll_mode poll_mode)
{
- struct lpfc_queue *cq;
- struct lpfc_cqe *cqe;
- bool workposted = false;
- uint16_t cqid;
- int ecount = 0;
+ struct lpfc_queue *cq = NULL;
+ uint32_t qidx = eq->hdwq;
+ uint16_t cqid, id;
+ int ret;
if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0366 Not a valid fast-path completion "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0366 Not a valid completion "
"event: majorcode=x%x, minorcode=x%x\n",
bf_get_le32(lpfc_eqe_major_code, eqe),
bf_get_le32(lpfc_eqe_minor_code, eqe));
return;
}
- cq = phba->sli4_hba.fcp_cq[fcp_cqidx];
- if (unlikely(!cq)) {
- if (phba->sli.sli_flag & LPFC_SLI_ACTIVE)
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0367 Fast-path completion queue "
- "does not exist\n");
+ /* Get the reference to the corresponding CQ */
+ cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
+
+ /* Use the fast lookup method first */
+ if (cqid <= phba->sli4_hba.cq_max) {
+ cq = phba->sli4_hba.cq_lookup[cqid];
+ if (cq)
+ goto work_cq;
+ }
+
+ /* Next check for NVMET completion */
+ if (phba->cfg_nvmet_mrq && phba->sli4_hba.nvmet_cqset) {
+ id = phba->sli4_hba.nvmet_cqset[0]->queue_id;
+ if ((cqid >= id) && (cqid < (id + phba->cfg_nvmet_mrq))) {
+ /* Process NVMET unsol rcv */
+ cq = phba->sli4_hba.nvmet_cqset[cqid - id];
+ goto process_cq;
+ }
+ }
+
+ if (phba->sli4_hba.nvmels_cq &&
+ (cqid == phba->sli4_hba.nvmels_cq->queue_id)) {
+ /* Process NVME unsol rcv */
+ cq = phba->sli4_hba.nvmels_cq;
+ }
+
+ /* Otherwise this is a Slow path event */
+ if (cq == NULL) {
+ lpfc_sli4_sp_handle_eqe(phba, eqe,
+ phba->sli4_hba.hdwq[qidx].hba_eq);
return;
}
- /* Get the reference to the corresponding CQ */
- cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
+process_cq:
if (unlikely(cqid != cq->queue_id)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0368 Miss-matched fast-path completion "
"queue identifier: eqcqid=%d, fcpcqid=%d\n",
cqid, cq->queue_id);
return;
}
- /* Process all the entries to the CQ */
- while ((cqe = lpfc_sli4_cq_get(cq))) {
- workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, cqe);
- if (!(++ecount % LPFC_GET_QE_REL_INT))
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
- }
-
- /* Catch the no cq entry condition */
- if (unlikely(ecount == 0))
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0369 No entry from fast-path completion "
- "queue fcpcqid=%d\n", cq->queue_id);
-
- /* In any case, flash and re-arm the CQ */
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
-
- /* wake up worker thread if there are works to be done */
- if (workposted)
- lpfc_worker_wake_up(phba);
-}
-
-static void
-lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
-{
- struct lpfc_eqe *eqe;
-
- /* walk all the EQ entries and drop on the floor */
- while ((eqe = lpfc_sli4_eq_get(eq)))
- ;
+work_cq:
+#if defined(CONFIG_SCSI_LPFC_DEBUG_FS)
+ if (phba->ktime_on)
+ cq->isr_timestamp = ktime_get_ns();
+ else
+ cq->isr_timestamp = 0;
+#endif
- /* Clear and re-arm the EQ */
- lpfc_sli4_eq_release(eq, LPFC_QUEUE_REARM);
+ switch (poll_mode) {
+ case LPFC_THREADED_IRQ:
+ __lpfc_sli4_hba_process_cq(cq);
+ break;
+ case LPFC_QUEUE_WORK:
+ default:
+ if (is_kdump_kernel())
+ ret = queue_work(phba->wq, &cq->irqwork);
+ else
+ ret = queue_work_on(cq->chann, phba->wq, &cq->irqwork);
+ if (!ret)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0383 Cannot schedule queue work "
+ "for CQ eqcqid=%d, cqid=%d on CPU %d\n",
+ cqid, cq->queue_id,
+ raw_smp_processor_id());
+ break;
+ }
}
/**
- * lpfc_sli4_sp_intr_handler - Slow-path interrupt handler to SLI-4 device
- * @irq: Interrupt number.
- * @dev_id: The device context pointer.
- *
- * This function is directly called from the PCI layer as an interrupt
- * service routine when device with SLI-4 interface spec is enabled with
- * MSI-X multi-message interrupt mode and there are slow-path events in
- * the HBA. However, when the device is enabled with either MSI or Pin-IRQ
- * interrupt mode, this function is called as part of the device-level
- * interrupt handler. When the PCI slot is in error recovery or the HBA is
- * undergoing initialization, the interrupt handler will not process the
- * interrupt. The link attention and ELS ring attention events are handled
- * by the worker thread. The interrupt handler signals the worker thread
- * and returns for these events. This function is called without any lock
- * held. It gets the hbalock to access and update SLI data structures.
+ * lpfc_sli4_dly_hba_process_cq - fast-path work handler when started by timer
+ * @work: pointer to work element
*
- * This function returns IRQ_HANDLED when interrupt is handled else it
- * returns IRQ_NONE.
+ * translates from the work handler and calls the fast-path handler.
**/
-irqreturn_t
-lpfc_sli4_sp_intr_handler(int irq, void *dev_id)
+static void
+lpfc_sli4_dly_hba_process_cq(struct work_struct *work)
{
- struct lpfc_hba *phba;
- struct lpfc_queue *speq;
- struct lpfc_eqe *eqe;
- unsigned long iflag;
- int ecount = 0;
-
- /*
- * Get the driver's phba structure from the dev_id
- */
- phba = (struct lpfc_hba *)dev_id;
-
- if (unlikely(!phba))
- return IRQ_NONE;
-
- /* Get to the EQ struct associated with this vector */
- speq = phba->sli4_hba.sp_eq;
-
- /* Check device state for handling interrupt */
- if (unlikely(lpfc_intr_state_check(phba))) {
- /* Check again for link_state with lock held */
- spin_lock_irqsave(&phba->hbalock, iflag);
- if (phba->link_state < LPFC_LINK_DOWN)
- /* Flush, clear interrupt, and rearm the EQ */
- lpfc_sli4_eq_flush(phba, speq);
- spin_unlock_irqrestore(&phba->hbalock, iflag);
- return IRQ_NONE;
- }
-
- /*
- * Process all the event on FCP slow-path EQ
- */
- while ((eqe = lpfc_sli4_eq_get(speq))) {
- lpfc_sli4_sp_handle_eqe(phba, eqe);
- if (!(++ecount % LPFC_GET_QE_REL_INT))
- lpfc_sli4_eq_release(speq, LPFC_QUEUE_NOARM);
- }
-
- /* Always clear and re-arm the slow-path EQ */
- lpfc_sli4_eq_release(speq, LPFC_QUEUE_REARM);
-
- /* Catch the no cq entry condition */
- if (unlikely(ecount == 0)) {
- if (phba->intr_type == MSIX)
- /* MSI-X treated interrupt served as no EQ share INT */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0357 MSI-X interrupt with no EQE\n");
- else
- /* Non MSI-X treated on interrupt as EQ share INT */
- return IRQ_NONE;
- }
+ struct lpfc_queue *cq = container_of(to_delayed_work(work),
+ struct lpfc_queue, sched_irqwork);
- return IRQ_HANDLED;
-} /* lpfc_sli4_sp_intr_handler */
+ __lpfc_sli4_hba_process_cq(cq);
+}
/**
- * lpfc_sli4_fp_intr_handler - Fast-path interrupt handler to SLI-4 device
+ * lpfc_sli4_hba_intr_handler - HBA interrupt handler to SLI-4 device
* @irq: Interrupt number.
* @dev_id: The device context pointer.
*
@@ -11002,30 +15590,40 @@ lpfc_sli4_sp_intr_handler(int irq, void *dev_id)
* the FCP EQ to FCP CQ are one-to-one map such that the FCP EQ index is
* equal to that of FCP CQ index.
*
- * This function returns IRQ_HANDLED when interrupt is handled else it
- * returns IRQ_NONE.
+ * The link attention and ELS ring attention events are handled
+ * by the worker thread. The interrupt handler signals the worker thread
+ * and returns for these events. This function is called without any lock
+ * held. It gets the hbalock to access and update SLI data structures.
+ *
+ * This function returns IRQ_HANDLED when interrupt is handled, IRQ_WAKE_THREAD
+ * when interrupt is scheduled to be handled from a threaded irq context, or
+ * else returns IRQ_NONE.
**/
irqreturn_t
-lpfc_sli4_fp_intr_handler(int irq, void *dev_id)
+lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
- struct lpfc_fcp_eq_hdl *fcp_eq_hdl;
+ struct lpfc_hba_eq_hdl *hba_eq_hdl;
struct lpfc_queue *fpeq;
- struct lpfc_eqe *eqe;
unsigned long iflag;
+ int hba_eqidx;
int ecount = 0;
- uint32_t fcp_eqidx;
+ struct lpfc_eq_intr_info *eqi;
/* Get the driver's phba structure from the dev_id */
- fcp_eq_hdl = (struct lpfc_fcp_eq_hdl *)dev_id;
- phba = fcp_eq_hdl->phba;
- fcp_eqidx = fcp_eq_hdl->idx;
+ hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id;
+ phba = hba_eq_hdl->phba;
+ hba_eqidx = hba_eq_hdl->idx;
if (unlikely(!phba))
return IRQ_NONE;
+ if (unlikely(!phba->sli4_hba.hdwq))
+ return IRQ_NONE;
/* Get to the EQ struct associated with this vector */
- fpeq = phba->sli4_hba.fp_eq[fcp_eqidx];
+ fpeq = phba->sli4_hba.hba_eq_hdl[hba_eqidx].eq;
+ if (unlikely(!fpeq))
+ return IRQ_NONE;
/* Check device state for handling interrupt */
if (unlikely(lpfc_intr_state_check(phba))) {
@@ -11033,35 +15631,50 @@ lpfc_sli4_fp_intr_handler(int irq, void *dev_id)
spin_lock_irqsave(&phba->hbalock, iflag);
if (phba->link_state < LPFC_LINK_DOWN)
/* Flush, clear interrupt, and rearm the EQ */
- lpfc_sli4_eq_flush(phba, fpeq);
+ lpfc_sli4_eqcq_flush(phba, fpeq);
spin_unlock_irqrestore(&phba->hbalock, iflag);
return IRQ_NONE;
}
- /*
- * Process all the event on FCP fast-path EQ
- */
- while ((eqe = lpfc_sli4_eq_get(fpeq))) {
- lpfc_sli4_fp_handle_eqe(phba, eqe, fcp_eqidx);
- if (!(++ecount % LPFC_GET_QE_REL_INT))
- lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_NOARM);
- }
-
- /* Always clear and re-arm the fast-path EQ */
- lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
-
- if (unlikely(ecount == 0)) {
- if (phba->intr_type == MSIX)
- /* MSI-X treated interrupt served as no EQ share INT */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0358 MSI-X interrupt with no EQE\n");
- else
- /* Non MSI-X treated on interrupt as EQ share INT */
- return IRQ_NONE;
+ switch (fpeq->poll_mode) {
+ case LPFC_THREADED_IRQ:
+ /* CGN mgmt is mutually exclusive from irq processing */
+ if (phba->cmf_active_mode == LPFC_CFG_OFF)
+ return IRQ_WAKE_THREAD;
+ fallthrough;
+ case LPFC_QUEUE_WORK:
+ default:
+ eqi = this_cpu_ptr(phba->sli4_hba.eq_info);
+ eqi->icnt++;
+
+ fpeq->last_cpu = raw_smp_processor_id();
+
+ if (eqi->icnt > LPFC_EQD_ISR_TRIGGER &&
+ fpeq->q_flag & HBA_EQ_DELAY_CHK &&
+ phba->cfg_auto_imax &&
+ fpeq->q_mode != LPFC_MAX_AUTO_EQ_DELAY &&
+ phba->sli.sli_flag & LPFC_SLI_USE_EQDR)
+ lpfc_sli4_mod_hba_eq_delay(phba, fpeq,
+ LPFC_MAX_AUTO_EQ_DELAY);
+
+ /* process and rearm the EQ */
+ ecount = lpfc_sli4_process_eq(phba, fpeq, LPFC_QUEUE_REARM,
+ LPFC_QUEUE_WORK);
+
+ if (unlikely(ecount == 0)) {
+ fpeq->EQ_no_entry++;
+ if (phba->intr_type == MSIX)
+ /* MSI-X treated interrupt served as no EQ share INT */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0358 MSI-X interrupt with no EQE\n");
+ else
+ /* Non MSI-X treated on interrupt as EQ share INT */
+ return IRQ_NONE;
+ }
}
return IRQ_HANDLED;
-} /* lpfc_sli4_fp_intr_handler */
+} /* lpfc_sli4_hba_intr_handler */
/**
* lpfc_sli4_intr_handler - Device-level interrupt handler for SLI-4 device
@@ -11084,9 +15697,9 @@ irqreturn_t
lpfc_sli4_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
- irqreturn_t sp_irq_rc, fp_irq_rc;
- bool fp_handled = false;
- uint32_t fcp_eqidx;
+ irqreturn_t hba_irq_rc;
+ bool hba_handled = false;
+ int qidx;
/* Get the driver's phba structure from the dev_id */
phba = (struct lpfc_hba *)dev_id;
@@ -11095,23 +15708,129 @@ lpfc_sli4_intr_handler(int irq, void *dev_id)
return IRQ_NONE;
/*
- * Invokes slow-path host attention interrupt handling as appropriate.
- */
- sp_irq_rc = lpfc_sli4_sp_intr_handler(irq, dev_id);
-
- /*
* Invoke fast-path host attention interrupt handling as appropriate.
*/
- for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
- fp_irq_rc = lpfc_sli4_fp_intr_handler(irq,
- &phba->sli4_hba.fcp_eq_hdl[fcp_eqidx]);
- if (fp_irq_rc == IRQ_HANDLED)
- fp_handled |= true;
+ for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) {
+ hba_irq_rc = lpfc_sli4_hba_intr_handler(irq,
+ &phba->sli4_hba.hba_eq_hdl[qidx]);
+ if (hba_irq_rc == IRQ_HANDLED)
+ hba_handled |= true;
}
- return (fp_handled == true) ? IRQ_HANDLED : sp_irq_rc;
+ return (hba_handled == true) ? IRQ_HANDLED : IRQ_NONE;
} /* lpfc_sli4_intr_handler */
+void lpfc_sli4_poll_hbtimer(struct timer_list *t)
+{
+ struct lpfc_hba *phba = timer_container_of(phba, t, cpuhp_poll_timer);
+ struct lpfc_queue *eq;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(eq, &phba->poll_list, _poll_list)
+ lpfc_sli4_poll_eq(eq);
+ if (!list_empty(&phba->poll_list))
+ mod_timer(&phba->cpuhp_poll_timer,
+ jiffies + msecs_to_jiffies(LPFC_POLL_HB));
+
+ rcu_read_unlock();
+}
+
+static inline void lpfc_sli4_add_to_poll_list(struct lpfc_queue *eq)
+{
+ struct lpfc_hba *phba = eq->phba;
+
+ /* kickstart slowpath processing if needed */
+ if (list_empty(&phba->poll_list))
+ mod_timer(&phba->cpuhp_poll_timer,
+ jiffies + msecs_to_jiffies(LPFC_POLL_HB));
+
+ list_add_rcu(&eq->_poll_list, &phba->poll_list);
+ synchronize_rcu();
+}
+
+static inline void lpfc_sli4_remove_from_poll_list(struct lpfc_queue *eq)
+{
+ struct lpfc_hba *phba = eq->phba;
+
+ /* Disable slowpath processing for this eq. Kick start the eq
+ * by RE-ARMING the eq's ASAP
+ */
+ list_del_rcu(&eq->_poll_list);
+ synchronize_rcu();
+
+ if (list_empty(&phba->poll_list))
+ timer_delete_sync(&phba->cpuhp_poll_timer);
+}
+
+void lpfc_sli4_cleanup_poll_list(struct lpfc_hba *phba)
+{
+ struct lpfc_queue *eq, *next;
+
+ list_for_each_entry_safe(eq, next, &phba->poll_list, _poll_list)
+ list_del(&eq->_poll_list);
+
+ INIT_LIST_HEAD(&phba->poll_list);
+ synchronize_rcu();
+}
+
+static inline void
+__lpfc_sli4_switch_eqmode(struct lpfc_queue *eq, uint8_t mode)
+{
+ if (mode == eq->mode)
+ return;
+ /*
+ * currently this function is only called during a hotplug
+ * event and the cpu on which this function is executing
+ * is going offline. By now the hotplug has instructed
+ * the scheduler to remove this cpu from cpu active mask.
+ * So we don't need to work about being put aside by the
+ * scheduler for a high priority process. Yes, the inte-
+ * rrupts could come but they are known to retire ASAP.
+ */
+
+ /* Disable polling in the fastpath */
+ WRITE_ONCE(eq->mode, mode);
+ /* flush out the store buffer */
+ smp_wmb();
+
+ /*
+ * Add this eq to the polling list and start polling. For
+ * a grace period both interrupt handler and poller will
+ * try to process the eq _but_ that's fine. We have a
+ * synchronization mechanism in place (queue_claimed) to
+ * deal with it. This is just a draining phase for int-
+ * errupt handler (not eq's) as we have guranteed through
+ * barrier that all the CPUs have seen the new CQ_POLLED
+ * state. which will effectively disable the REARMING of
+ * the EQ. The whole idea is eq's die off eventually as
+ * we are not rearming EQ's anymore.
+ */
+ mode ? lpfc_sli4_add_to_poll_list(eq) :
+ lpfc_sli4_remove_from_poll_list(eq);
+}
+
+void lpfc_sli4_start_polling(struct lpfc_queue *eq)
+{
+ __lpfc_sli4_switch_eqmode(eq, LPFC_EQ_POLL);
+}
+
+void lpfc_sli4_stop_polling(struct lpfc_queue *eq)
+{
+ struct lpfc_hba *phba = eq->phba;
+
+ __lpfc_sli4_switch_eqmode(eq, LPFC_EQ_INTERRUPT);
+
+ /* Kick start for the pending io's in h/w.
+ * Once we switch back to interrupt processing on a eq
+ * the io path completion will only arm eq's when it
+ * receives a completion. But since eq's are in disa-
+ * rmed state it doesn't receive a completion. This
+ * creates a deadlock scenaro.
+ */
+ phba->sli4_hba.sli4_write_eq_db(phba, eq, 0, LPFC_QUEUE_REARM);
+}
+
/**
* lpfc_sli4_queue_free - free a queue structure and associated memory
* @queue: The queue structure to free.
@@ -11128,13 +15847,24 @@ lpfc_sli4_queue_free(struct lpfc_queue *queue)
if (!queue)
return;
+ if (!list_empty(&queue->wq_list))
+ list_del(&queue->wq_list);
+
while (!list_empty(&queue->page_list)) {
list_remove_head(&queue->page_list, dmabuf, struct lpfc_dmabuf,
list);
- dma_free_coherent(&queue->phba->pcidev->dev, SLI4_PAGE_SIZE,
+ dma_free_coherent(&queue->phba->pcidev->dev, queue->page_size,
dmabuf->virt, dmabuf->phys);
kfree(dmabuf);
}
+ if (queue->rqbp) {
+ lpfc_free_rq_buffer(queue->phba, queue);
+ kfree(queue->rqbp);
+ }
+
+ if (!list_empty(&queue->cpu_list))
+ list_del(&queue->cpu_list);
+
kfree(queue);
return;
}
@@ -11142,37 +15872,60 @@ lpfc_sli4_queue_free(struct lpfc_queue *queue)
/**
* lpfc_sli4_queue_alloc - Allocate and initialize a queue structure
* @phba: The HBA that this queue is being created on.
+ * @page_size: The size of a queue page
* @entry_size: The size of each queue entry for this queue.
- * @entry count: The number of entries that this queue will handle.
+ * @entry_count: The number of entries that this queue will handle.
+ * @cpu: The cpu that will primarily utilize this queue.
*
* This function allocates a queue structure and the DMAable memory used for
* the host resident queue. This function must be called before creating the
* queue on the HBA.
**/
struct lpfc_queue *
-lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t entry_size,
- uint32_t entry_count)
+lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size,
+ uint32_t entry_size, uint32_t entry_count, int cpu)
{
struct lpfc_queue *queue;
struct lpfc_dmabuf *dmabuf;
- int x, total_qe_count;
- void *dma_pointer;
uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
+ uint16_t x, pgcnt;
if (!phba->sli4_hba.pc_sli4_params.supported)
- hw_page_size = SLI4_PAGE_SIZE;
+ hw_page_size = page_size;
+
+ pgcnt = ALIGN(entry_size * entry_count, hw_page_size) / hw_page_size;
+
+ /* If needed, Adjust page count to match the max the adapter supports */
+ if (pgcnt > phba->sli4_hba.pc_sli4_params.wqpcnt)
+ pgcnt = phba->sli4_hba.pc_sli4_params.wqpcnt;
- queue = kzalloc(sizeof(struct lpfc_queue) +
- (sizeof(union sli4_qe) * entry_count), GFP_KERNEL);
+ queue = kzalloc_node(sizeof(*queue) + (sizeof(void *) * pgcnt),
+ GFP_KERNEL, cpu_to_node(cpu));
if (!queue)
return NULL;
- queue->page_count = (ALIGN(entry_size * entry_count,
- hw_page_size))/hw_page_size;
+
INIT_LIST_HEAD(&queue->list);
+ INIT_LIST_HEAD(&queue->_poll_list);
+ INIT_LIST_HEAD(&queue->wq_list);
+ INIT_LIST_HEAD(&queue->wqfull_list);
INIT_LIST_HEAD(&queue->page_list);
INIT_LIST_HEAD(&queue->child_list);
- for (x = 0, total_qe_count = 0; x < queue->page_count; x++) {
- dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ INIT_LIST_HEAD(&queue->cpu_list);
+
+ /* Set queue parameters now. If the system cannot provide memory
+ * resources, the free routine needs to know what was allocated.
+ */
+ queue->page_count = pgcnt;
+ queue->q_pgs = (void **)&queue[1];
+ queue->entry_cnt_per_pg = hw_page_size / entry_size;
+ queue->entry_size = entry_size;
+ queue->entry_count = entry_count;
+ queue->page_size = hw_page_size;
+ queue->phba = phba;
+
+ for (x = 0; x < queue->page_count; x++) {
+ dmabuf = kzalloc_node(sizeof(*dmabuf), GFP_KERNEL,
+ dev_to_node(&phba->pcidev->dev));
if (!dmabuf)
goto out_fail;
dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
@@ -11182,20 +15935,17 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t entry_size,
kfree(dmabuf);
goto out_fail;
}
- memset(dmabuf->virt, 0, hw_page_size);
dmabuf->buffer_tag = x;
list_add_tail(&dmabuf->list, &queue->page_list);
- /* initialize queue's entry array */
- dma_pointer = dmabuf->virt;
- for (; total_qe_count < entry_count &&
- dma_pointer < (hw_page_size + dmabuf->virt);
- total_qe_count++, dma_pointer += entry_size) {
- queue->qe[total_qe_count].address = dma_pointer;
- }
+ /* use lpfc_sli4_qe to index a paritcular entry in this page */
+ queue->q_pgs[x] = dmabuf->virt;
}
- queue->entry_size = entry_size;
- queue->entry_count = entry_count;
- queue->phba = phba;
+ INIT_WORK(&queue->irqwork, lpfc_sli4_hba_process_cq);
+ INIT_WORK(&queue->spwork, lpfc_sli4_sp_process_cq);
+ INIT_DELAYED_WORK(&queue->sched_irqwork, lpfc_sli4_dly_hba_process_cq);
+ INIT_DELAYED_WORK(&queue->sched_spwork, lpfc_sli4_dly_sp_process_cq);
+
+ /* notify_interval will be set during q creation */
return queue;
out_fail:
@@ -11204,6 +15954,149 @@ out_fail:
}
/**
+ * lpfc_dual_chute_pci_bar_map - Map pci base address register to host memory
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @pci_barset: PCI BAR set flag.
+ *
+ * This function shall perform iomap of the specified PCI BAR address to host
+ * memory address if not already done so and return it. The returned host
+ * memory address can be NULL.
+ */
+static void __iomem *
+lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset)
+{
+ if (!phba->pcidev)
+ return NULL;
+
+ switch (pci_barset) {
+ case WQ_PCI_BAR_0_AND_1:
+ return phba->pci_bar0_memmap_p;
+ case WQ_PCI_BAR_2_AND_3:
+ return phba->pci_bar2_memmap_p;
+ case WQ_PCI_BAR_4_AND_5:
+ return phba->pci_bar4_memmap_p;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * lpfc_modify_hba_eq_delay - Modify Delay Multiplier on EQs
+ * @phba: HBA structure that EQs are on.
+ * @startq: The starting EQ index to modify
+ * @numq: The number of EQs (consecutive indexes) to modify
+ * @usdelay: amount of delay
+ *
+ * This function revises the EQ delay on 1 or more EQs. The EQ delay
+ * is set either by writing to a register (if supported by the SLI Port)
+ * or by mailbox command. The mailbox command allows several EQs to be
+ * updated at once.
+ *
+ * The @phba struct is used to send a mailbox command to HBA. The @startq
+ * is used to get the starting EQ index to change. The @numq value is
+ * used to specify how many consecutive EQ indexes, starting at EQ index,
+ * are to be changed. This function is asynchronous and will wait for any
+ * mailbox commands to finish before returning.
+ *
+ * On success this function will return a zero. If unable to allocate
+ * enough memory this function will return -ENOMEM. If a mailbox command
+ * fails this function will return -ENXIO. Note: on ENXIO, some EQs may
+ * have had their delay multipler changed.
+ **/
+void
+lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
+ uint32_t numq, uint32_t usdelay)
+{
+ struct lpfc_mbx_modify_eq_delay *eq_delay;
+ LPFC_MBOXQ_t *mbox;
+ struct lpfc_queue *eq;
+ int cnt = 0, rc, length;
+ uint32_t shdr_status, shdr_add_status;
+ uint32_t dmult;
+ int qidx;
+ union lpfc_sli4_cfg_shdr *shdr;
+
+ if (startq >= phba->cfg_irq_chann)
+ return;
+
+ if (usdelay > 0xFFFF) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_FCP | LOG_NVME,
+ "6429 usdelay %d too large. Scaled down to "
+ "0xFFFF.\n", usdelay);
+ usdelay = 0xFFFF;
+ }
+
+ /* set values by EQ_DELAY register if supported */
+ if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) {
+ for (qidx = startq; qidx < phba->cfg_irq_chann; qidx++) {
+ eq = phba->sli4_hba.hba_eq_hdl[qidx].eq;
+ if (!eq)
+ continue;
+
+ lpfc_sli4_mod_hba_eq_delay(phba, eq, usdelay);
+
+ if (++cnt >= numq)
+ break;
+ }
+ return;
+ }
+
+ /* Otherwise, set values by mailbox cmd */
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6428 Failed allocating mailbox cmd buffer."
+ " EQ delay was not set.\n");
+ return;
+ }
+ length = (sizeof(struct lpfc_mbx_modify_eq_delay) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_MODIFY_EQ_DELAY,
+ length, LPFC_SLI4_MBX_EMBED);
+ eq_delay = &mbox->u.mqe.un.eq_delay;
+
+ /* Calculate delay multiper from maximum interrupt per second */
+ dmult = (usdelay * LPFC_DMULT_CONST) / LPFC_SEC_TO_USEC;
+ if (dmult)
+ dmult--;
+ if (dmult > LPFC_DMULT_MAX)
+ dmult = LPFC_DMULT_MAX;
+
+ for (qidx = startq; qidx < phba->cfg_irq_chann; qidx++) {
+ eq = phba->sli4_hba.hba_eq_hdl[qidx].eq;
+ if (!eq)
+ continue;
+ eq->q_mode = usdelay;
+ eq_delay->u.request.eq[cnt].eq_id = eq->queue_id;
+ eq_delay->u.request.eq[cnt].phase = 0;
+ eq_delay->u.request.eq[cnt].delay_multi = dmult;
+
+ if (++cnt >= numq)
+ break;
+ }
+ eq_delay->u.request.num_eq = cnt;
+
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->ctx_ndlp = NULL;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ shdr = (union lpfc_sli4_cfg_shdr *) &eq_delay->header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2512 MODIFY_EQ_DELAY mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ }
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return;
+}
+
+/**
* lpfc_eq_create - Create an Event Queue on the HBA
* @phba: HBA structure that indicates port to create a queue on.
* @eq: The queue structure to use to create the event queue.
@@ -11223,8 +16116,8 @@ out_fail:
* memory this function will return -ENOMEM. If the queue create mailbox command
* fails this function will return -ENXIO.
**/
-uint32_t
-lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
+int
+lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint32_t imax)
{
struct lpfc_mbx_eq_create *eq_create;
LPFC_MBOXQ_t *mbox;
@@ -11235,6 +16128,9 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
uint16_t dmult;
uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
+ /* sanity check on queue memory */
+ if (!eq)
+ return -ENODEV;
if (!phba->sli4_hba.pc_sli4_params.supported)
hw_page_size = SLI4_PAGE_SIZE;
@@ -11247,23 +16143,35 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
LPFC_MBOX_OPCODE_EQ_CREATE,
length, LPFC_SLI4_MBX_EMBED);
eq_create = &mbox->u.mqe.un.eq_create;
+ shdr = (union lpfc_sli4_cfg_shdr *) &eq_create->header.cfg_shdr;
bf_set(lpfc_mbx_eq_create_num_pages, &eq_create->u.request,
eq->page_count);
bf_set(lpfc_eq_context_size, &eq_create->u.request.context,
LPFC_EQE_SIZE);
bf_set(lpfc_eq_context_valid, &eq_create->u.request.context, 1);
- /* Calculate delay multiper from maximum interrupt per second */
- dmult = LPFC_DMULT_CONST/imax - 1;
+
+ /* Use version 2 of CREATE_EQ if eqav is set */
+ if (phba->sli4_hba.pc_sli4_params.eqav) {
+ bf_set(lpfc_mbox_hdr_version, &shdr->request,
+ LPFC_Q_CREATE_VERSION_2);
+ bf_set(lpfc_eq_context_autovalid, &eq_create->u.request.context,
+ phba->sli4_hba.pc_sli4_params.eqav);
+ }
+
+ /* don't setup delay multiplier using EQ_CREATE */
+ dmult = 0;
bf_set(lpfc_eq_context_delay_multi, &eq_create->u.request.context,
dmult);
switch (eq->entry_count) {
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0360 Unsupported EQ count. (%d)\n",
eq->entry_count);
- if (eq->entry_count < 256)
- return -EINVAL;
- /* otherwise default to smallest count (drop through) */
+ if (eq->entry_count < 256) {
+ status = -EINVAL;
+ goto out;
+ }
+ fallthrough; /* otherwise default to smallest count */
case 256:
bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
LPFC_EQ_CNT_256);
@@ -11294,13 +16202,13 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
}
mbox->vport = phba->pport;
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->context1 = NULL;
+ mbox->ctx_buf = NULL;
+ mbox->ctx_ndlp = NULL;
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
- shdr = (union lpfc_sli4_cfg_shdr *) &eq_create->header.cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2500 EQ_CREATE mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
@@ -11312,17 +16220,85 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
if (eq->queue_id == 0xFFFF)
status = -ENXIO;
eq->host_index = 0;
- eq->hba_index = 0;
-
+ eq->notify_interval = LPFC_EQ_NOTIFY_INTRVL;
+ eq->max_proc_limit = LPFC_EQ_MAX_PROC_LIMIT;
+out:
mempool_free(mbox, phba->mbox_mem_pool);
return status;
}
/**
+ * lpfc_sli4_hba_intr_handler_th - SLI4 HBA threaded interrupt handler
+ * @irq: Interrupt number.
+ * @dev_id: The device context pointer.
+ *
+ * This routine is a mirror of lpfc_sli4_hba_intr_handler, but executed within
+ * threaded irq context.
+ *
+ * Returns
+ * IRQ_HANDLED - interrupt is handled
+ * IRQ_NONE - otherwise
+ **/
+irqreturn_t lpfc_sli4_hba_intr_handler_th(int irq, void *dev_id)
+{
+ struct lpfc_hba *phba;
+ struct lpfc_hba_eq_hdl *hba_eq_hdl;
+ struct lpfc_queue *fpeq;
+ int ecount = 0;
+ int hba_eqidx;
+ struct lpfc_eq_intr_info *eqi;
+
+ /* Get the driver's phba structure from the dev_id */
+ hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id;
+ phba = hba_eq_hdl->phba;
+ hba_eqidx = hba_eq_hdl->idx;
+
+ if (unlikely(!phba))
+ return IRQ_NONE;
+ if (unlikely(!phba->sli4_hba.hdwq))
+ return IRQ_NONE;
+
+ /* Get to the EQ struct associated with this vector */
+ fpeq = phba->sli4_hba.hba_eq_hdl[hba_eqidx].eq;
+ if (unlikely(!fpeq))
+ return IRQ_NONE;
+
+ eqi = per_cpu_ptr(phba->sli4_hba.eq_info, raw_smp_processor_id());
+ eqi->icnt++;
+
+ fpeq->last_cpu = raw_smp_processor_id();
+
+ if (eqi->icnt > LPFC_EQD_ISR_TRIGGER &&
+ fpeq->q_flag & HBA_EQ_DELAY_CHK &&
+ phba->cfg_auto_imax &&
+ fpeq->q_mode != LPFC_MAX_AUTO_EQ_DELAY &&
+ phba->sli.sli_flag & LPFC_SLI_USE_EQDR)
+ lpfc_sli4_mod_hba_eq_delay(phba, fpeq, LPFC_MAX_AUTO_EQ_DELAY);
+
+ /* process and rearm the EQ */
+ ecount = lpfc_sli4_process_eq(phba, fpeq, LPFC_QUEUE_REARM,
+ LPFC_THREADED_IRQ);
+
+ if (unlikely(ecount == 0)) {
+ fpeq->EQ_no_entry++;
+ if (phba->intr_type == MSIX)
+ /* MSI-X treated interrupt served as no EQ share INT */
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "3358 MSI-X interrupt with no EQE\n");
+ else
+ /* Non MSI-X treated on interrupt as EQ share INT */
+ return IRQ_NONE;
+ }
+ return IRQ_HANDLED;
+}
+
+/**
* lpfc_cq_create - Create a Completion Queue on the HBA
* @phba: HBA structure that indicates port to create a queue on.
* @cq: The queue structure to use to create the completion queue.
* @eq: The event queue to bind this completion queue to.
+ * @type: Type of queue (EQ, GCQ, MCQ, WCQ, etc).
+ * @subtype: Functional purpose of the queue (MBOX, IO, ELS, NVMET, etc).
*
* This function creates a completion queue, as detailed in @wq, on a port,
* described by @phba by sending a CQ_CREATE mailbox command to the HBA.
@@ -11339,7 +16315,7 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
* memory this function will return -ENOMEM. If the queue create mailbox command
* fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
struct lpfc_queue *eq, uint32_t type, uint32_t subtype)
{
@@ -11349,10 +16325,10 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
int rc, length, status = 0;
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
- uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
- if (!phba->sli4_hba.pc_sli4_params.supported)
- hw_page_size = SLI4_PAGE_SIZE;
+ /* sanity check on queue memory */
+ if (!cq || !eq)
+ return -ENODEV;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
@@ -11371,22 +16347,40 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
bf_set(lpfc_mbox_hdr_version, &shdr->request,
phba->sli4_hba.pc_sli4_params.cqv);
if (phba->sli4_hba.pc_sli4_params.cqv == LPFC_Q_CREATE_VERSION_2) {
- /* FW only supports 1. Should be PAGE_SIZE/SLI4_PAGE_SIZE */
- bf_set(lpfc_mbx_cq_create_page_size, &cq_create->u.request, 1);
+ bf_set(lpfc_mbx_cq_create_page_size, &cq_create->u.request,
+ (cq->page_size / SLI4_PAGE_SIZE));
bf_set(lpfc_cq_eq_id_2, &cq_create->u.request.context,
eq->queue_id);
+ bf_set(lpfc_cq_context_autovalid, &cq_create->u.request.context,
+ phba->sli4_hba.pc_sli4_params.cqav);
} else {
bf_set(lpfc_cq_eq_id, &cq_create->u.request.context,
eq->queue_id);
}
switch (cq->entry_count) {
+ case 2048:
+ case 4096:
+ if (phba->sli4_hba.pc_sli4_params.cqv ==
+ LPFC_Q_CREATE_VERSION_2) {
+ cq_create->u.request.context.lpfc_cq_context_count =
+ cq->entry_count;
+ bf_set(lpfc_cq_context_count,
+ &cq_create->u.request.context,
+ LPFC_CQ_CNT_WORD7);
+ break;
+ }
+ fallthrough;
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0361 Unsupported CQ count. (%d)\n",
- cq->entry_count);
- if (cq->entry_count < 256)
- return -EINVAL;
- /* otherwise default to smallest count (drop through) */
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "0361 Unsupported CQ count: "
+ "entry cnt %d sz %d pg cnt %d\n",
+ cq->entry_count, cq->entry_size,
+ cq->page_count);
+ if (cq->entry_count < 256) {
+ status = -EINVAL;
+ goto out;
+ }
+ fallthrough; /* otherwise default to smallest count */
case 256:
bf_set(lpfc_cq_context_count, &cq_create->u.request.context,
LPFC_CQ_CNT_256);
@@ -11401,7 +16395,7 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
break;
}
list_for_each_entry(dmabuf, &cq->page_list, list) {
- memset(dmabuf->virt, 0, hw_page_size);
+ memset(dmabuf->virt, 0, cq->page_size);
cq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
putPaddrLow(dmabuf->phys);
cq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
@@ -11413,7 +16407,7 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2501 CQ_CREATE mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
@@ -11432,15 +16426,272 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
cq->subtype = subtype;
cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
cq->assoc_qid = eq->queue_id;
+ cq->assoc_qp = eq;
cq->host_index = 0;
- cq->hba_index = 0;
+ cq->notify_interval = LPFC_CQ_NOTIFY_INTRVL;
+ cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit, cq->entry_count);
+ if (cq->queue_id > phba->sli4_hba.cq_max)
+ phba->sli4_hba.cq_max = cq->queue_id;
out:
mempool_free(mbox, phba->mbox_mem_pool);
return status;
}
/**
+ * lpfc_cq_create_set - Create a set of Completion Queues on the HBA for MRQ
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @cqp: The queue structure array to use to create the completion queues.
+ * @hdwq: The hardware queue array with the EQ to bind completion queues to.
+ * @type: Type of queue (EQ, GCQ, MCQ, WCQ, etc).
+ * @subtype: Functional purpose of the queue (MBOX, IO, ELS, NVMET, etc).
+ *
+ * This function creates a set of completion queue, s to support MRQ
+ * as detailed in @cqp, on a port,
+ * described by @phba by sending a CREATE_CQ_SET mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @cq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. The @eq
+ * is used to indicate which event queue to bind this completion queue to. This
+ * function will send the CREATE_CQ_SET mailbox command to the HBA to setup the
+ * completion queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return -ENOMEM. If the queue create mailbox command
+ * fails this function will return -ENXIO.
+ **/
+int
+lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
+ struct lpfc_sli4_hdw_queue *hdwq, uint32_t type,
+ uint32_t subtype)
+{
+ struct lpfc_queue *cq;
+ struct lpfc_queue *eq;
+ struct lpfc_mbx_cq_create_set *cq_set;
+ struct lpfc_dmabuf *dmabuf;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, alloclen, status = 0;
+ int cnt, idx, numcq, page_idx = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
+
+ /* sanity check on queue memory */
+ numcq = phba->cfg_nvmet_mrq;
+ if (!cqp || !hdwq || !numcq)
+ return -ENODEV;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ length = sizeof(struct lpfc_mbx_cq_create_set);
+ length += ((numcq * cqp[0]->page_count) *
+ sizeof(struct dma_address));
+ alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_CQ_CREATE_SET, length,
+ LPFC_SLI4_MBX_NEMBED);
+ if (alloclen < length) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3098 Allocated DMA memory size (%d) is "
+ "less than the requested DMA memory size "
+ "(%d)\n", alloclen, length);
+ status = -ENOMEM;
+ goto out;
+ }
+ cq_set = mbox->sge_array->addr[0];
+ shdr = (union lpfc_sli4_cfg_shdr *)&cq_set->cfg_shdr;
+ bf_set(lpfc_mbox_hdr_version, &shdr->request, 0);
+
+ for (idx = 0; idx < numcq; idx++) {
+ cq = cqp[idx];
+ eq = hdwq[idx].hba_eq;
+ if (!cq || !eq) {
+ status = -ENOMEM;
+ goto out;
+ }
+ if (!phba->sli4_hba.pc_sli4_params.supported)
+ hw_page_size = cq->page_size;
+
+ switch (idx) {
+ case 0:
+ bf_set(lpfc_mbx_cq_create_set_page_size,
+ &cq_set->u.request,
+ (hw_page_size / SLI4_PAGE_SIZE));
+ bf_set(lpfc_mbx_cq_create_set_num_pages,
+ &cq_set->u.request, cq->page_count);
+ bf_set(lpfc_mbx_cq_create_set_evt,
+ &cq_set->u.request, 1);
+ bf_set(lpfc_mbx_cq_create_set_valid,
+ &cq_set->u.request, 1);
+ bf_set(lpfc_mbx_cq_create_set_cqe_size,
+ &cq_set->u.request, 0);
+ bf_set(lpfc_mbx_cq_create_set_num_cq,
+ &cq_set->u.request, numcq);
+ bf_set(lpfc_mbx_cq_create_set_autovalid,
+ &cq_set->u.request,
+ phba->sli4_hba.pc_sli4_params.cqav);
+ switch (cq->entry_count) {
+ case 2048:
+ case 4096:
+ if (phba->sli4_hba.pc_sli4_params.cqv ==
+ LPFC_Q_CREATE_VERSION_2) {
+ bf_set(lpfc_mbx_cq_create_set_cqe_cnt_lo,
+ &cq_set->u.request,
+ cq->entry_count);
+ bf_set(lpfc_mbx_cq_create_set_cqecnt,
+ &cq_set->u.request,
+ LPFC_CQ_CNT_WORD7);
+ break;
+ }
+ fallthrough;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3118 Bad CQ count. (%d)\n",
+ cq->entry_count);
+ if (cq->entry_count < 256) {
+ status = -EINVAL;
+ goto out;
+ }
+ fallthrough; /* otherwise default to smallest */
+ case 256:
+ bf_set(lpfc_mbx_cq_create_set_cqecnt,
+ &cq_set->u.request, LPFC_CQ_CNT_256);
+ break;
+ case 512:
+ bf_set(lpfc_mbx_cq_create_set_cqecnt,
+ &cq_set->u.request, LPFC_CQ_CNT_512);
+ break;
+ case 1024:
+ bf_set(lpfc_mbx_cq_create_set_cqecnt,
+ &cq_set->u.request, LPFC_CQ_CNT_1024);
+ break;
+ }
+ bf_set(lpfc_mbx_cq_create_set_eq_id0,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 1:
+ bf_set(lpfc_mbx_cq_create_set_eq_id1,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 2:
+ bf_set(lpfc_mbx_cq_create_set_eq_id2,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 3:
+ bf_set(lpfc_mbx_cq_create_set_eq_id3,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 4:
+ bf_set(lpfc_mbx_cq_create_set_eq_id4,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 5:
+ bf_set(lpfc_mbx_cq_create_set_eq_id5,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 6:
+ bf_set(lpfc_mbx_cq_create_set_eq_id6,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 7:
+ bf_set(lpfc_mbx_cq_create_set_eq_id7,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 8:
+ bf_set(lpfc_mbx_cq_create_set_eq_id8,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 9:
+ bf_set(lpfc_mbx_cq_create_set_eq_id9,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 10:
+ bf_set(lpfc_mbx_cq_create_set_eq_id10,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 11:
+ bf_set(lpfc_mbx_cq_create_set_eq_id11,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 12:
+ bf_set(lpfc_mbx_cq_create_set_eq_id12,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 13:
+ bf_set(lpfc_mbx_cq_create_set_eq_id13,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 14:
+ bf_set(lpfc_mbx_cq_create_set_eq_id14,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ case 15:
+ bf_set(lpfc_mbx_cq_create_set_eq_id15,
+ &cq_set->u.request, eq->queue_id);
+ break;
+ }
+
+ /* link the cq onto the parent eq child list */
+ list_add_tail(&cq->list, &eq->child_list);
+ /* Set up completion queue's type and subtype */
+ cq->type = type;
+ cq->subtype = subtype;
+ cq->assoc_qid = eq->queue_id;
+ cq->assoc_qp = eq;
+ cq->host_index = 0;
+ cq->notify_interval = LPFC_CQ_NOTIFY_INTRVL;
+ cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit,
+ cq->entry_count);
+ cq->chann = idx;
+
+ rc = 0;
+ list_for_each_entry(dmabuf, &cq->page_list, list) {
+ memset(dmabuf->virt, 0, hw_page_size);
+ cnt = page_idx + dmabuf->buffer_tag;
+ cq_set->u.request.page[cnt].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ cq_set->u.request.page[cnt].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ rc++;
+ }
+ page_idx += rc;
+ }
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3119 CQ_CREATE_SET mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ goto out;
+ }
+ rc = bf_get(lpfc_mbx_cq_create_set_base_id, &cq_set->u.response);
+ if (rc == 0xFFFF) {
+ status = -ENXIO;
+ goto out;
+ }
+
+ for (idx = 0; idx < numcq; idx++) {
+ cq = cqp[idx];
+ cq->queue_id = rc + idx;
+ if (cq->queue_id > phba->sli4_hba.cq_max)
+ phba->sli4_hba.cq_max = cq->queue_id;
+ }
+
+out:
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return status;
+}
+
+/**
* lpfc_mq_create_fb_init - Send MCC_CREATE without async events registration
* @phba: HBA structure that indicates port to create a queue on.
* @mq: The queue structure to use to create the mailbox queue.
@@ -11533,6 +16784,9 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
union lpfc_sli4_cfg_shdr *shdr;
uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
+ /* sanity check on queue memory */
+ if (!mq || !cq)
+ return -ENODEV;
if (!phba->sli4_hba.pc_sli4_params.supported)
hw_page_size = SLI4_PAGE_SIZE;
@@ -11570,12 +16824,14 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
cq->queue_id);
switch (mq->entry_count) {
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0362 Unsupported MQ count. (%d)\n",
mq->entry_count);
- if (mq->entry_count < 16)
- return -EINVAL;
- /* otherwise default to smallest count (drop through) */
+ if (mq->entry_count < 16) {
+ status = -EINVAL;
+ goto out;
+ }
+ fallthrough; /* otherwise default to smallest count */
case 16:
bf_set(lpfc_mq_context_ring_size,
&mq_create_ext->u.request.context,
@@ -11624,7 +16880,7 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2502 MQ_CREATE mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
@@ -11670,7 +16926,7 @@ out:
* memory this function will return -ENOMEM. If the queue create mailbox command
* fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
struct lpfc_queue *cq, uint32_t subtype)
{
@@ -11682,9 +16938,21 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
union lpfc_sli4_cfg_shdr *shdr;
uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
struct dma_address *page;
-
+ void __iomem *bar_memmap_p;
+ uint32_t db_offset;
+ uint16_t pci_barset;
+ uint8_t dpp_barset;
+ uint32_t dpp_offset;
+ uint8_t wq_create_version;
+#ifdef CONFIG_X86
+ unsigned long pg_addr;
+#endif
+
+ /* sanity check on queue memory */
+ if (!wq || !cq)
+ return -ENODEV;
if (!phba->sli4_hba.pc_sli4_params.supported)
- hw_page_size = SLI4_PAGE_SIZE;
+ hw_page_size = wq->page_size;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
@@ -11700,11 +16968,24 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
wq->page_count);
bf_set(lpfc_mbx_wq_create_cq_id, &wq_create->u.request,
cq->queue_id);
+
+ /* wqv is the earliest version supported, NOT the latest */
bf_set(lpfc_mbox_hdr_version, &shdr->request,
phba->sli4_hba.pc_sli4_params.wqv);
- if (phba->sli4_hba.pc_sli4_params.wqv == LPFC_Q_CREATE_VERSION_1) {
+
+ if ((phba->sli4_hba.pc_sli4_params.wqsize & LPFC_WQ_SZ128_SUPPORT) ||
+ (wq->page_size > SLI4_PAGE_SIZE))
+ wq_create_version = LPFC_Q_CREATE_VERSION_1;
+ else
+ wq_create_version = LPFC_Q_CREATE_VERSION_0;
+
+ switch (wq_create_version) {
+ case LPFC_Q_CREATE_VERSION_1:
bf_set(lpfc_mbx_wq_create_wqe_count, &wq_create->u.request_1,
wq->entry_count);
+ bf_set(lpfc_mbox_hdr_version, &shdr->request,
+ LPFC_Q_CREATE_VERSION_1);
+
switch (wq->entry_size) {
default:
case 64:
@@ -11718,39 +16999,165 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
LPFC_WQ_WQE_SIZE_128);
break;
}
- bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ /* Request DPP by default */
+ bf_set(lpfc_mbx_wq_create_dpp_req, &wq_create->u.request_1, 1);
+ bf_set(lpfc_mbx_wq_create_page_size,
+ &wq_create->u.request_1,
+ (wq->page_size / SLI4_PAGE_SIZE));
page = wq_create->u.request_1.page;
- } else {
+ break;
+ default:
page = wq_create->u.request.page;
+ break;
}
+
list_for_each_entry(dmabuf, &wq->page_list, list) {
memset(dmabuf->virt, 0, hw_page_size);
page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys);
page[dmabuf->buffer_tag].addr_hi = putPaddrHigh(dmabuf->phys);
}
+
+ if (phba->sli4_hba.fw_func_mode & LPFC_DUA_MODE)
+ bf_set(lpfc_mbx_wq_create_dua, &wq_create->u.request, 1);
+
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
/* The IOCTL status is embedded in the mailbox subheader. */
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2503 WQ_CREATE mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
status = -ENXIO;
goto out;
}
- wq->queue_id = bf_get(lpfc_mbx_wq_create_q_id, &wq_create->u.response);
+
+ if (wq_create_version == LPFC_Q_CREATE_VERSION_0)
+ wq->queue_id = bf_get(lpfc_mbx_wq_create_q_id,
+ &wq_create->u.response);
+ else
+ wq->queue_id = bf_get(lpfc_mbx_wq_create_v1_q_id,
+ &wq_create->u.response_1);
+
if (wq->queue_id == 0xFFFF) {
status = -ENXIO;
goto out;
}
+
+ wq->db_format = LPFC_DB_LIST_FORMAT;
+ if (wq_create_version == LPFC_Q_CREATE_VERSION_0) {
+ if (phba->sli4_hba.fw_func_mode & LPFC_DUA_MODE) {
+ wq->db_format = bf_get(lpfc_mbx_wq_create_db_format,
+ &wq_create->u.response);
+ if ((wq->db_format != LPFC_DB_LIST_FORMAT) &&
+ (wq->db_format != LPFC_DB_RING_FORMAT)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3265 WQ[%d] doorbell format "
+ "not supported: x%x\n",
+ wq->queue_id, wq->db_format);
+ status = -EINVAL;
+ goto out;
+ }
+ pci_barset = bf_get(lpfc_mbx_wq_create_bar_set,
+ &wq_create->u.response);
+ bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba,
+ pci_barset);
+ if (!bar_memmap_p) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3263 WQ[%d] failed to memmap "
+ "pci barset:x%x\n",
+ wq->queue_id, pci_barset);
+ status = -ENOMEM;
+ goto out;
+ }
+ db_offset = wq_create->u.response.doorbell_offset;
+ if ((db_offset != LPFC_ULP0_WQ_DOORBELL) &&
+ (db_offset != LPFC_ULP1_WQ_DOORBELL)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3252 WQ[%d] doorbell offset "
+ "not supported: x%x\n",
+ wq->queue_id, db_offset);
+ status = -EINVAL;
+ goto out;
+ }
+ wq->db_regaddr = bar_memmap_p + db_offset;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3264 WQ[%d]: barset:x%x, offset:x%x, "
+ "format:x%x\n", wq->queue_id,
+ pci_barset, db_offset, wq->db_format);
+ } else
+ wq->db_regaddr = phba->sli4_hba.WQDBregaddr;
+ } else {
+ /* Check if DPP was honored by the firmware */
+ wq->dpp_enable = bf_get(lpfc_mbx_wq_create_dpp_rsp,
+ &wq_create->u.response_1);
+ if (wq->dpp_enable) {
+ pci_barset = bf_get(lpfc_mbx_wq_create_v1_bar_set,
+ &wq_create->u.response_1);
+ bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba,
+ pci_barset);
+ if (!bar_memmap_p) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3267 WQ[%d] failed to memmap "
+ "pci barset:x%x\n",
+ wq->queue_id, pci_barset);
+ status = -ENOMEM;
+ goto out;
+ }
+ db_offset = wq_create->u.response_1.doorbell_offset;
+ wq->db_regaddr = bar_memmap_p + db_offset;
+ wq->dpp_id = bf_get(lpfc_mbx_wq_create_dpp_id,
+ &wq_create->u.response_1);
+ dpp_barset = bf_get(lpfc_mbx_wq_create_dpp_bar,
+ &wq_create->u.response_1);
+ bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba,
+ dpp_barset);
+ if (!bar_memmap_p) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3268 WQ[%d] failed to memmap "
+ "pci barset:x%x\n",
+ wq->queue_id, dpp_barset);
+ status = -ENOMEM;
+ goto out;
+ }
+ dpp_offset = wq_create->u.response_1.dpp_offset;
+ wq->dpp_regaddr = bar_memmap_p + dpp_offset;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3271 WQ[%d]: barset:x%x, offset:x%x, "
+ "dpp_id:x%x dpp_barset:x%x "
+ "dpp_offset:x%x\n",
+ wq->queue_id, pci_barset, db_offset,
+ wq->dpp_id, dpp_barset, dpp_offset);
+
+#ifdef CONFIG_X86
+ /* Enable combined writes for DPP aperture */
+ pg_addr = (unsigned long)(wq->dpp_regaddr) & PAGE_MASK;
+ rc = set_memory_wc(pg_addr, 1);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3272 Cannot setup Combined "
+ "Write on WQ[%d] - disable DPP\n",
+ wq->queue_id);
+ phba->cfg_enable_dpp = 0;
+ }
+#else
+ phba->cfg_enable_dpp = 0;
+#endif
+ } else
+ wq->db_regaddr = phba->sli4_hba.WQDBregaddr;
+ }
+ wq->pring = kzalloc(sizeof(struct lpfc_sli_ring), GFP_KERNEL);
+ if (wq->pring == NULL) {
+ status = -ENOMEM;
+ goto out;
+ }
wq->type = LPFC_WQ;
wq->assoc_qid = cq->queue_id;
wq->subtype = subtype;
wq->host_index = 0;
wq->hba_index = 0;
+ wq->notify_interval = LPFC_WQ_NOTIFY_INTRVL;
/* link the wq onto the parent cq child list */
list_add_tail(&wq->list, &cq->child_list);
@@ -11765,6 +17172,7 @@ out:
* @hrq: The queue structure to use to create the header receive queue.
* @drq: The queue structure to use to create the data receive queue.
* @cq: The completion queue to bind this work queue to.
+ * @subtype: The subtype of the work queue indicating its functionality.
*
* This function creates a receive buffer queue pair , as detailed in @hrq and
* @drq, on a port, described by @phba by sending a RQ_CREATE mailbox command
@@ -11782,7 +17190,7 @@ out:
* memory this function will return -ENOMEM. If the queue create mailbox command
* fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
struct lpfc_queue *drq, struct lpfc_queue *cq, uint32_t subtype)
{
@@ -11793,7 +17201,13 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
+ void __iomem *bar_memmap_p;
+ uint32_t db_offset;
+ uint16_t pci_barset;
+ /* sanity check on queue memory */
+ if (!hrq || !drq || !cq)
+ return -ENODEV;
if (!phba->sli4_hba.pc_sli4_params.supported)
hw_page_size = SLI4_PAGE_SIZE;
@@ -11821,16 +17235,18 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
LPFC_RQE_SIZE_8);
bf_set(lpfc_rq_context_page_size,
&rq_create->u.request.context,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ LPFC_RQ_PAGE_SIZE_4096);
} else {
switch (hrq->entry_count) {
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2535 Unsupported RQ count. (%d)\n",
hrq->entry_count);
- if (hrq->entry_count < 512)
- return -EINVAL;
- /* otherwise default to smallest count (drop through) */
+ if (hrq->entry_count < 512) {
+ status = -EINVAL;
+ goto out;
+ }
+ fallthrough; /* otherwise default to smallest count */
case 512:
bf_set(lpfc_rq_context_rqe_count,
&rq_create->u.request.context,
@@ -11866,12 +17282,15 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
rq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
putPaddrHigh(dmabuf->phys);
}
+ if (phba->sli4_hba.fw_func_mode & LPFC_DUA_MODE)
+ bf_set(lpfc_mbx_rq_create_dua, &rq_create->u.request, 1);
+
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
/* The IOCTL status is embedded in the mailbox subheader. */
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2504 RQ_CREATE mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
@@ -11883,11 +17302,57 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
status = -ENXIO;
goto out;
}
+
+ if (phba->sli4_hba.fw_func_mode & LPFC_DUA_MODE) {
+ hrq->db_format = bf_get(lpfc_mbx_rq_create_db_format,
+ &rq_create->u.response);
+ if ((hrq->db_format != LPFC_DB_LIST_FORMAT) &&
+ (hrq->db_format != LPFC_DB_RING_FORMAT)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3262 RQ [%d] doorbell format not "
+ "supported: x%x\n", hrq->queue_id,
+ hrq->db_format);
+ status = -EINVAL;
+ goto out;
+ }
+
+ pci_barset = bf_get(lpfc_mbx_rq_create_bar_set,
+ &rq_create->u.response);
+ bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba, pci_barset);
+ if (!bar_memmap_p) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3269 RQ[%d] failed to memmap pci "
+ "barset:x%x\n", hrq->queue_id,
+ pci_barset);
+ status = -ENOMEM;
+ goto out;
+ }
+
+ db_offset = rq_create->u.response.doorbell_offset;
+ if ((db_offset != LPFC_ULP0_RQ_DOORBELL) &&
+ (db_offset != LPFC_ULP1_RQ_DOORBELL)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3270 RQ[%d] doorbell offset not "
+ "supported: x%x\n", hrq->queue_id,
+ db_offset);
+ status = -EINVAL;
+ goto out;
+ }
+ hrq->db_regaddr = bar_memmap_p + db_offset;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3266 RQ[qid:%d]: barset:x%x, offset:x%x, "
+ "format:x%x\n", hrq->queue_id, pci_barset,
+ db_offset, hrq->db_format);
+ } else {
+ hrq->db_format = LPFC_DB_RING_FORMAT;
+ hrq->db_regaddr = phba->sli4_hba.RQDBregaddr;
+ }
hrq->type = LPFC_HRQ;
hrq->assoc_qid = cq->queue_id;
hrq->subtype = subtype;
hrq->host_index = 0;
hrq->hba_index = 0;
+ hrq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
/* now create the data queue */
lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
@@ -11898,7 +17363,12 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
if (phba->sli4_hba.pc_sli4_params.rqv == LPFC_Q_CREATE_VERSION_1) {
bf_set(lpfc_rq_context_rqe_count_1,
&rq_create->u.request.context, hrq->entry_count);
- rq_create->u.request.context.buffer_size = LPFC_DATA_BUF_SIZE;
+ if (subtype == LPFC_NVMET)
+ rq_create->u.request.context.buffer_size =
+ LPFC_NVMET_DATA_BUF_SIZE;
+ else
+ rq_create->u.request.context.buffer_size =
+ LPFC_DATA_BUF_SIZE;
bf_set(lpfc_rq_context_rqe_size, &rq_create->u.request.context,
LPFC_RQE_SIZE_8);
bf_set(lpfc_rq_context_page_size, &rq_create->u.request.context,
@@ -11906,12 +17376,14 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
} else {
switch (drq->entry_count) {
default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2536 Unsupported RQ count. (%d)\n",
drq->entry_count);
- if (drq->entry_count < 512)
- return -EINVAL;
- /* otherwise default to smallest count (drop through) */
+ if (drq->entry_count < 512) {
+ status = -EINVAL;
+ goto out;
+ }
+ fallthrough; /* otherwise default to smallest count */
case 512:
bf_set(lpfc_rq_context_rqe_count,
&rq_create->u.request.context,
@@ -11933,8 +17405,14 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
LPFC_RQ_RING_SIZE_4096);
break;
}
- bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context,
- LPFC_DATA_BUF_SIZE);
+ if (subtype == LPFC_NVMET)
+ bf_set(lpfc_rq_context_buf_size,
+ &rq_create->u.request.context,
+ LPFC_NVMET_DATA_BUF_SIZE);
+ else
+ bf_set(lpfc_rq_context_buf_size,
+ &rq_create->u.request.context,
+ LPFC_DATA_BUF_SIZE);
}
bf_set(lpfc_rq_context_cq_id, &rq_create->u.request.context,
cq->queue_id);
@@ -11946,6 +17424,8 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
rq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
putPaddrHigh(dmabuf->phys);
}
+ if (phba->sli4_hba.fw_func_mode & LPFC_DUA_MODE)
+ bf_set(lpfc_mbx_rq_create_dua, &rq_create->u.request, 1);
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
/* The IOCTL status is embedded in the mailbox subheader. */
shdr = (union lpfc_sli4_cfg_shdr *) &rq_create->header.cfg_shdr;
@@ -11965,6 +17445,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
drq->subtype = subtype;
drq->host_index = 0;
drq->hba_index = 0;
+ drq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
/* link the header and data RQs onto the parent cq child list */
list_add_tail(&hrq->list, &cq->child_list);
@@ -11976,7 +17457,202 @@ out:
}
/**
+ * lpfc_mrq_create - Create MRQ Receive Queues on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @hrqp: The queue structure array to use to create the header receive queues.
+ * @drqp: The queue structure array to use to create the data receive queues.
+ * @cqp: The completion queue array to bind these receive queues to.
+ * @subtype: Functional purpose of the queue (MBOX, IO, ELS, NVMET, etc).
+ *
+ * This function creates a receive buffer queue pair , as detailed in @hrq and
+ * @drq, on a port, described by @phba by sending a RQ_CREATE mailbox command
+ * to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @drq and @hrq
+ * struct is used to get the entry count that is necessary to determine the
+ * number of pages to use for this queue. The @cq is used to indicate which
+ * completion queue to bind received buffers that are posted to these queues to.
+ * This function will send the RQ_CREATE mailbox command to the HBA to setup the
+ * receive queue pair. This function is asynchronous and will wait for the
+ * mailbox command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return -ENOMEM. If the queue create mailbox command
+ * fails this function will return -ENXIO.
+ **/
+int
+lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp,
+ struct lpfc_queue **drqp, struct lpfc_queue **cqp,
+ uint32_t subtype)
+{
+ struct lpfc_queue *hrq, *drq, *cq;
+ struct lpfc_mbx_rq_create_v2 *rq_create;
+ struct lpfc_dmabuf *dmabuf;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, alloclen, status = 0;
+ int cnt, idx, numrq, page_idx = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz;
+
+ numrq = phba->cfg_nvmet_mrq;
+ /* sanity check on array memory */
+ if (!hrqp || !drqp || !cqp || !numrq)
+ return -ENODEV;
+ if (!phba->sli4_hba.pc_sli4_params.supported)
+ hw_page_size = SLI4_PAGE_SIZE;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ length = sizeof(struct lpfc_mbx_rq_create_v2);
+ length += ((2 * numrq * hrqp[0]->page_count) *
+ sizeof(struct dma_address));
+
+ alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_RQ_CREATE, length,
+ LPFC_SLI4_MBX_NEMBED);
+ if (alloclen < length) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3099 Allocated DMA memory size (%d) is "
+ "less than the requested DMA memory size "
+ "(%d)\n", alloclen, length);
+ status = -ENOMEM;
+ goto out;
+ }
+
+
+
+ rq_create = mbox->sge_array->addr[0];
+ shdr = (union lpfc_sli4_cfg_shdr *)&rq_create->cfg_shdr;
+
+ bf_set(lpfc_mbox_hdr_version, &shdr->request, LPFC_Q_CREATE_VERSION_2);
+ cnt = 0;
+
+ for (idx = 0; idx < numrq; idx++) {
+ hrq = hrqp[idx];
+ drq = drqp[idx];
+ cq = cqp[idx];
+
+ /* sanity check on queue memory */
+ if (!hrq || !drq || !cq) {
+ status = -ENODEV;
+ goto out;
+ }
+
+ if (hrq->entry_count != drq->entry_count) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (idx == 0) {
+ bf_set(lpfc_mbx_rq_create_num_pages,
+ &rq_create->u.request,
+ hrq->page_count);
+ bf_set(lpfc_mbx_rq_create_rq_cnt,
+ &rq_create->u.request, (numrq * 2));
+ bf_set(lpfc_mbx_rq_create_dnb, &rq_create->u.request,
+ 1);
+ bf_set(lpfc_rq_context_base_cq,
+ &rq_create->u.request.context,
+ cq->queue_id);
+ bf_set(lpfc_rq_context_data_size,
+ &rq_create->u.request.context,
+ LPFC_NVMET_DATA_BUF_SIZE);
+ bf_set(lpfc_rq_context_hdr_size,
+ &rq_create->u.request.context,
+ LPFC_HDR_BUF_SIZE);
+ bf_set(lpfc_rq_context_rqe_count_1,
+ &rq_create->u.request.context,
+ hrq->entry_count);
+ bf_set(lpfc_rq_context_rqe_size,
+ &rq_create->u.request.context,
+ LPFC_RQE_SIZE_8);
+ bf_set(lpfc_rq_context_page_size,
+ &rq_create->u.request.context,
+ (PAGE_SIZE/SLI4_PAGE_SIZE));
+ }
+ rc = 0;
+ list_for_each_entry(dmabuf, &hrq->page_list, list) {
+ memset(dmabuf->virt, 0, hw_page_size);
+ cnt = page_idx + dmabuf->buffer_tag;
+ rq_create->u.request.page[cnt].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ rq_create->u.request.page[cnt].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ rc++;
+ }
+ page_idx += rc;
+
+ rc = 0;
+ list_for_each_entry(dmabuf, &drq->page_list, list) {
+ memset(dmabuf->virt, 0, hw_page_size);
+ cnt = page_idx + dmabuf->buffer_tag;
+ rq_create->u.request.page[cnt].addr_lo =
+ putPaddrLow(dmabuf->phys);
+ rq_create->u.request.page[cnt].addr_hi =
+ putPaddrHigh(dmabuf->phys);
+ rc++;
+ }
+ page_idx += rc;
+
+ hrq->db_format = LPFC_DB_RING_FORMAT;
+ hrq->db_regaddr = phba->sli4_hba.RQDBregaddr;
+ hrq->type = LPFC_HRQ;
+ hrq->assoc_qid = cq->queue_id;
+ hrq->subtype = subtype;
+ hrq->host_index = 0;
+ hrq->hba_index = 0;
+ hrq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
+
+ drq->db_format = LPFC_DB_RING_FORMAT;
+ drq->db_regaddr = phba->sli4_hba.RQDBregaddr;
+ drq->type = LPFC_DRQ;
+ drq->assoc_qid = cq->queue_id;
+ drq->subtype = subtype;
+ drq->host_index = 0;
+ drq->hba_index = 0;
+ drq->notify_interval = LPFC_RQ_NOTIFY_INTRVL;
+
+ list_add_tail(&hrq->list, &cq->child_list);
+ list_add_tail(&drq->list, &cq->child_list);
+ }
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+ if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3120 RQ_CREATE mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ status = -ENXIO;
+ goto out;
+ }
+ rc = bf_get(lpfc_mbx_rq_create_q_id, &rq_create->u.response);
+ if (rc == 0xFFFF) {
+ status = -ENXIO;
+ goto out;
+ }
+
+ /* Initialize all RQs with associated queue id */
+ for (idx = 0; idx < numrq; idx++) {
+ hrq = hrqp[idx];
+ hrq->queue_id = rc + (2 * idx);
+ drq = drqp[idx];
+ drq->queue_id = rc + (2 * idx) + 1;
+ }
+
+out:
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return status;
+}
+
+/**
* lpfc_eq_destroy - Destroy an event Queue on the HBA
+ * @phba: HBA structure that indicates port to destroy a queue on.
* @eq: The queue structure associated with the queue to destroy.
*
* This function destroys a queue, as detailed in @eq by sending an mailbox
@@ -11987,7 +17663,7 @@ out:
* On success this function will return a zero. If the queue destroy mailbox
* command fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
{
LPFC_MBOXQ_t *mbox;
@@ -11995,8 +17671,13 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
+ /* sanity check on queue memory */
if (!eq)
return -ENODEV;
+
+ if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))
+ goto list_remove;
+
mbox = mempool_alloc(eq->phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -12017,21 +17698,24 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2505 EQ_DESTROY mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
status = -ENXIO;
}
+ mempool_free(mbox, eq->phba->mbox_mem_pool);
+list_remove:
/* Remove eq from any list */
list_del_init(&eq->list);
- mempool_free(mbox, eq->phba->mbox_mem_pool);
+
return status;
}
/**
* lpfc_cq_destroy - Destroy a Completion Queue on the HBA
+ * @phba: HBA structure that indicates port to destroy a queue on.
* @cq: The queue structure associated with the queue to destroy.
*
* This function destroys a queue, as detailed in @cq by sending an mailbox
@@ -12042,7 +17726,7 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
* On success this function will return a zero. If the queue destroy mailbox
* command fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq)
{
LPFC_MBOXQ_t *mbox;
@@ -12050,8 +17734,13 @@ lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq)
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
+ /* sanity check on queue memory */
if (!cq)
return -ENODEV;
+
+ if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))
+ goto list_remove;
+
mbox = mempool_alloc(cq->phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -12071,21 +17760,24 @@ lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq)
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2506 CQ_DESTROY mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
status = -ENXIO;
}
+ mempool_free(mbox, cq->phba->mbox_mem_pool);
+
+list_remove:
/* Remove cq from any list */
list_del_init(&cq->list);
- mempool_free(mbox, cq->phba->mbox_mem_pool);
return status;
}
/**
* lpfc_mq_destroy - Destroy a Mailbox Queue on the HBA
- * @qm: The queue structure associated with the queue to destroy.
+ * @phba: HBA structure that indicates port to destroy a queue on.
+ * @mq: The queue structure associated with the queue to destroy.
*
* This function destroys a queue, as detailed in @mq by sending an mailbox
* command, specific to the type of queue, to the HBA.
@@ -12095,7 +17787,7 @@ lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq)
* On success this function will return a zero. If the queue destroy mailbox
* command fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq)
{
LPFC_MBOXQ_t *mbox;
@@ -12103,8 +17795,13 @@ lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq)
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
+ /* sanity check on queue memory */
if (!mq)
return -ENODEV;
+
+ if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))
+ goto list_remove;
+
mbox = mempool_alloc(mq->phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -12124,20 +17821,23 @@ lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq)
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2507 MQ_DESTROY mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
status = -ENXIO;
}
+ mempool_free(mbox, mq->phba->mbox_mem_pool);
+
+list_remove:
/* Remove mq from any list */
list_del_init(&mq->list);
- mempool_free(mbox, mq->phba->mbox_mem_pool);
return status;
}
/**
* lpfc_wq_destroy - Destroy a Work Queue on the HBA
+ * @phba: HBA structure that indicates port to destroy a queue on.
* @wq: The queue structure associated with the queue to destroy.
*
* This function destroys a queue, as detailed in @wq by sending an mailbox
@@ -12148,7 +17848,7 @@ lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq)
* On success this function will return a zero. If the queue destroy mailbox
* command fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq)
{
LPFC_MBOXQ_t *mbox;
@@ -12156,8 +17856,13 @@ lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq)
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
+ /* sanity check on queue memory */
if (!wq)
return -ENODEV;
+
+ if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))
+ goto list_remove;
+
mbox = mempool_alloc(wq->phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -12176,21 +17881,27 @@ lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq)
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2508 WQ_DESTROY mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
status = -ENXIO;
}
+ mempool_free(mbox, wq->phba->mbox_mem_pool);
+
+list_remove:
/* Remove wq from any list */
list_del_init(&wq->list);
- mempool_free(mbox, wq->phba->mbox_mem_pool);
+ kfree(wq->pring);
+ wq->pring = NULL;
return status;
}
/**
* lpfc_rq_destroy - Destroy a Receive Queue on the HBA
- * @rq: The queue structure associated with the queue to destroy.
+ * @phba: HBA structure that indicates port to destroy a queue on.
+ * @hrq: The queue structure associated with the queue to destroy.
+ * @drq: The queue structure associated with the queue to destroy.
*
* This function destroys a queue, as detailed in @rq by sending an mailbox
* command, specific to the type of queue, to the HBA.
@@ -12200,7 +17911,7 @@ lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq)
* On success this function will return a zero. If the queue destroy mailbox
* command fails this function will return -ENXIO.
**/
-uint32_t
+int
lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
struct lpfc_queue *drq)
{
@@ -12209,8 +17920,13 @@ lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
+ /* sanity check on queue memory */
if (!hrq || !drq)
return -ENODEV;
+
+ if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))
+ goto list_remove;
+
mbox = mempool_alloc(hrq->phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -12230,12 +17946,11 @@ lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2509 RQ_DESTROY mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, hrq->phba->mbox_mem_pool);
+ mempool_free(mbox, hrq->phba->mbox_mem_pool);
return -ENXIO;
}
bf_set(lpfc_mbx_rq_destroy_q_id, &mbox->u.mqe.un.rq_destroy.u.request,
@@ -12246,15 +17961,17 @@ lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2510 RQ_DESTROY mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
status = -ENXIO;
}
+ mempool_free(mbox, hrq->phba->mbox_mem_pool);
+
+list_remove:
list_del_init(&hrq->list);
list_del_init(&drq->list);
- mempool_free(mbox, hrq->phba->mbox_mem_pool);
return status;
}
@@ -12294,7 +18011,7 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba,
union lpfc_sli4_cfg_shdr *shdr;
if (xritag == NO_XRI) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0364 Invalid param:\n");
return -EINVAL;
}
@@ -12325,40 +18042,40 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba,
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
/* The IOCTL status is embedded in the mailbox subheader. */
shdr = (union lpfc_sli4_cfg_shdr *) &post_sgl_pages->header.cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
- if (rc != MBX_TIMEOUT)
+ if (!phba->sli4_hba.intr_enable)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ else if (rc != MBX_TIMEOUT)
mempool_free(mbox, phba->mbox_mem_pool);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2511 POST_SGL mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
- rc = -ENXIO;
}
return 0;
}
/**
- * lpfc_sli4_init_rpi_hdrs - Post the rpi header memory region to the port
+ * lpfc_sli4_alloc_xri - Get an available rpi in the device's range
* @phba: pointer to lpfc hba data structure.
*
* This routine is invoked to post rpi header templates to the
- * port for those SLI4 ports that do not support extents. This routine
- * posts a PAGE_SIZE memory region to the port to hold up to
- * PAGE_SIZE modulo 64 rpi context headers. This is an initialization routine
- * and should be called only when interrupts are disabled.
+ * HBA consistent with the SLI-4 interface spec. This routine
+ * posts a SLI4_PAGE_SIZE memory region to the port to hold up to
+ * SLI4_PAGE_SIZE modulo 64 rpi context headers.
*
- * Return codes
- * 0 - successful
- * -ERROR - otherwise.
- */
-uint16_t
+ * Returns
+ * A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful
+ * LPFC_RPI_ALLOC_ERROR if no rpis are available.
+ **/
+static uint16_t
lpfc_sli4_alloc_xri(struct lpfc_hba *phba)
{
unsigned long xri;
@@ -12368,33 +18085,31 @@ lpfc_sli4_alloc_xri(struct lpfc_hba *phba)
* the driver starts at 0 each time.
*/
spin_lock_irq(&phba->hbalock);
- xri = find_next_zero_bit(phba->sli4_hba.xri_bmask,
- phba->sli4_hba.max_cfg_param.max_xri, 0);
+ xri = find_first_zero_bit(phba->sli4_hba.xri_bmask,
+ phba->sli4_hba.max_cfg_param.max_xri);
if (xri >= phba->sli4_hba.max_cfg_param.max_xri) {
spin_unlock_irq(&phba->hbalock);
return NO_XRI;
} else {
set_bit(xri, phba->sli4_hba.xri_bmask);
phba->sli4_hba.max_cfg_param.xri_used++;
- phba->sli4_hba.xri_count++;
}
-
spin_unlock_irq(&phba->hbalock);
return xri;
}
/**
- * lpfc_sli4_free_xri - Release an xri for reuse.
+ * __lpfc_sli4_free_xri - Release an xri for reuse.
* @phba: pointer to lpfc hba data structure.
+ * @xri: xri to release.
*
* This routine is invoked to release an xri to the pool of
* available rpis maintained by the driver.
**/
-void
+static void
__lpfc_sli4_free_xri(struct lpfc_hba *phba, int xri)
{
if (test_and_clear_bit(xri, phba->sli4_hba.xri_bmask)) {
- phba->sli4_hba.xri_count--;
phba->sli4_hba.max_cfg_param.xri_used--;
}
}
@@ -12402,6 +18117,7 @@ __lpfc_sli4_free_xri(struct lpfc_hba *phba, int xri)
/**
* lpfc_sli4_free_xri - Release an xri for reuse.
* @phba: pointer to lpfc hba data structure.
+ * @xri: xri to release.
*
* This routine is invoked to release an xri to the pool of
* available rpis maintained by the driver.
@@ -12430,53 +18146,53 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba)
uint16_t xri_index;
xri_index = lpfc_sli4_alloc_xri(phba);
- if (xri_index != NO_XRI)
- return xri_index;
-
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2004 Failed to allocate XRI.last XRITAG is %d"
- " Max XRI is %d, Used XRI is %d\n",
- xri_index,
- phba->sli4_hba.max_cfg_param.max_xri,
- phba->sli4_hba.max_cfg_param.xri_used);
- return NO_XRI;
+ if (xri_index == NO_XRI)
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2004 Failed to allocate XRI.last XRITAG is %d"
+ " Max XRI is %d, Used XRI is %d\n",
+ xri_index,
+ phba->sli4_hba.max_cfg_param.max_xri,
+ phba->sli4_hba.max_cfg_param.xri_used);
+ return xri_index;
}
/**
- * lpfc_sli4_post_els_sgl_list - post a block of ELS sgls to the port.
+ * lpfc_sli4_post_sgl_list - post a block of ELS sgls to the port.
* @phba: pointer to lpfc hba data structure.
+ * @post_sgl_list: pointer to els sgl entry list.
+ * @post_cnt: number of els sgl entries on the list.
*
* This routine is invoked to post a block of driver's sgl pages to the
* HBA using non-embedded mailbox command. No Lock is held. This routine
* is only called when the driver is loading and after all IO has been
* stopped.
**/
-int
-lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba)
+static int
+lpfc_sli4_post_sgl_list(struct lpfc_hba *phba,
+ struct list_head *post_sgl_list,
+ int post_cnt)
{
- struct lpfc_sglq *sglq_entry;
+ struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
struct sgl_page_pairs *sgl_pg_pairs;
void *viraddr;
LPFC_MBOXQ_t *mbox;
uint32_t reqlen, alloclen, pg_pairs;
uint32_t mbox_tmo;
- uint16_t xritag_start = 0, lxri = 0;
- int els_xri_cnt, rc = 0;
+ uint16_t xritag_start = 0;
+ int rc = 0;
uint32_t shdr_status, shdr_add_status;
union lpfc_sli4_cfg_shdr *shdr;
- /* The number of sgls to be posted */
- els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
-
- reqlen = els_xri_cnt * sizeof(struct sgl_page_pairs) +
+ reqlen = post_cnt * sizeof(struct sgl_page_pairs) +
sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
if (reqlen > SLI4_PAGE_SIZE) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2559 Block sgl registration required DMA "
"size (%d) great than a page\n", reqlen);
return -ENOMEM;
}
+
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
return -ENOMEM;
@@ -12487,7 +18203,7 @@ lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba)
LPFC_SLI4_MBX_NEMBED);
if (alloclen < reqlen) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"0285 Allocated DMA memory size (%d) is "
"less than the requested DMA memory "
"size (%d)\n", alloclen, reqlen);
@@ -12499,25 +18215,8 @@ lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba)
sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
sgl_pg_pairs = &sgl->sgl_pg_pairs;
- for (pg_pairs = 0; pg_pairs < els_xri_cnt; pg_pairs++) {
- sglq_entry = phba->sli4_hba.lpfc_els_sgl_array[pg_pairs];
-
- /*
- * Assign the sglq a physical xri only if the driver has not
- * initialized those resources. A port reset only needs
- * the sglq's posted.
- */
- if (bf_get(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags) !=
- LPFC_XRI_RSRC_RDY) {
- lxri = lpfc_sli4_next_xritag(phba);
- if (lxri == NO_XRI) {
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
- sglq_entry->sli4_lxritag = lxri;
- sglq_entry->sli4_xritag = phba->sli4_hba.xri_ids[lxri];
- }
-
+ pg_pairs = 0;
+ list_for_each_entry_safe(sglq_entry, sglq_next, post_sgl_list, list) {
/* Set up the sge entry */
sgl_pg_pairs->sgl_pg0_addr_lo =
cpu_to_le32(putPaddrLow(sglq_entry->phys));
@@ -12532,224 +18231,53 @@ lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba)
if (pg_pairs == 0)
xritag_start = sglq_entry->sli4_xritag;
sgl_pg_pairs++;
+ pg_pairs++;
}
/* Complete initialization and perform endian conversion. */
bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
- bf_set(lpfc_post_sgl_pages_xricnt, sgl, els_xri_cnt);
+ bf_set(lpfc_post_sgl_pages_xricnt, sgl, post_cnt);
sgl->word0 = cpu_to_le32(sgl->word0);
+
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
- if (rc != MBX_TIMEOUT)
+ if (!phba->sli4_hba.intr_enable)
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ else if (rc != MBX_TIMEOUT)
lpfc_sli4_mbox_cmd_free(phba, mbox);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2513 POST_SGL_BLOCK mailbox command failed "
"status x%x add_status x%x mbx status x%x\n",
shdr_status, shdr_add_status, rc);
rc = -ENXIO;
}
-
- if (rc == 0)
- bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags,
- LPFC_XRI_RSRC_RDY);
- return rc;
-}
-
-/**
- * lpfc_sli4_post_els_sgl_list_ext - post a block of ELS sgls to the port.
- * @phba: pointer to lpfc hba data structure.
- *
- * This routine is invoked to post a block of driver's sgl pages to the
- * HBA using non-embedded mailbox command. No Lock is held. This routine
- * is only called when the driver is loading and after all IO has been
- * stopped.
- **/
-int
-lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba)
-{
- struct lpfc_sglq *sglq_entry;
- struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
- struct sgl_page_pairs *sgl_pg_pairs;
- void *viraddr;
- LPFC_MBOXQ_t *mbox;
- uint32_t reqlen, alloclen, index;
- uint32_t mbox_tmo;
- uint16_t rsrc_start, rsrc_size, els_xri_cnt;
- uint16_t xritag_start = 0, lxri = 0;
- struct lpfc_rsrc_blks *rsrc_blk;
- int cnt, ttl_cnt, rc = 0;
- int loop_cnt;
- uint32_t shdr_status, shdr_add_status;
- union lpfc_sli4_cfg_shdr *shdr;
-
- /* The number of sgls to be posted */
- els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
-
- reqlen = els_xri_cnt * sizeof(struct sgl_page_pairs) +
- sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
- if (reqlen > SLI4_PAGE_SIZE) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2989 Block sgl registration required DMA "
- "size (%d) great than a page\n", reqlen);
- return -ENOMEM;
- }
-
- cnt = 0;
- ttl_cnt = 0;
- list_for_each_entry(rsrc_blk, &phba->sli4_hba.lpfc_xri_blk_list,
- list) {
- rsrc_start = rsrc_blk->rsrc_start;
- rsrc_size = rsrc_blk->rsrc_size;
-
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "3014 Working ELS Extent start %d, cnt %d\n",
- rsrc_start, rsrc_size);
-
- loop_cnt = min(els_xri_cnt, rsrc_size);
- if (ttl_cnt + loop_cnt >= els_xri_cnt) {
- loop_cnt = els_xri_cnt - ttl_cnt;
- ttl_cnt = els_xri_cnt;
- }
-
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox)
- return -ENOMEM;
- /*
- * Allocate DMA memory and set up the non-embedded mailbox
- * command.
- */
- alloclen = lpfc_sli4_config(phba, mbox,
- LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES,
- reqlen, LPFC_SLI4_MBX_NEMBED);
- if (alloclen < reqlen) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2987 Allocated DMA memory size (%d) "
- "is less than the requested DMA memory "
- "size (%d)\n", alloclen, reqlen);
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
-
- /* Set up the SGL pages in the non-embedded DMA pages */
- viraddr = mbox->sge_array->addr[0];
- sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
- sgl_pg_pairs = &sgl->sgl_pg_pairs;
-
- /*
- * The starting resource may not begin at zero. Control
- * the loop variants via the block resource parameters,
- * but handle the sge pointers with a zero-based index
- * that doesn't get reset per loop pass.
- */
- for (index = rsrc_start;
- index < rsrc_start + loop_cnt;
- index++) {
- sglq_entry = phba->sli4_hba.lpfc_els_sgl_array[cnt];
-
- /*
- * Assign the sglq a physical xri only if the driver
- * has not initialized those resources. A port reset
- * only needs the sglq's posted.
- */
- if (bf_get(lpfc_xri_rsrc_rdy,
- &phba->sli4_hba.sli4_flags) !=
- LPFC_XRI_RSRC_RDY) {
- lxri = lpfc_sli4_next_xritag(phba);
- if (lxri == NO_XRI) {
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- rc = -ENOMEM;
- goto err_exit;
- }
- sglq_entry->sli4_lxritag = lxri;
- sglq_entry->sli4_xritag =
- phba->sli4_hba.xri_ids[lxri];
- }
-
- /* Set up the sge entry */
- sgl_pg_pairs->sgl_pg0_addr_lo =
- cpu_to_le32(putPaddrLow(sglq_entry->phys));
- sgl_pg_pairs->sgl_pg0_addr_hi =
- cpu_to_le32(putPaddrHigh(sglq_entry->phys));
- sgl_pg_pairs->sgl_pg1_addr_lo =
- cpu_to_le32(putPaddrLow(0));
- sgl_pg_pairs->sgl_pg1_addr_hi =
- cpu_to_le32(putPaddrHigh(0));
-
- /* Track the starting physical XRI for the mailbox. */
- if (index == rsrc_start)
- xritag_start = sglq_entry->sli4_xritag;
- sgl_pg_pairs++;
- cnt++;
- }
-
- /* Complete initialization and perform endian conversion. */
- rsrc_blk->rsrc_used += loop_cnt;
- bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
- bf_set(lpfc_post_sgl_pages_xricnt, sgl, loop_cnt);
- sgl->word0 = cpu_to_le32(sgl->word0);
-
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "3015 Post ELS Extent SGL, start %d, "
- "cnt %d, used %d\n",
- xritag_start, loop_cnt, rsrc_blk->rsrc_used);
- if (!phba->sli4_hba.intr_enable)
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
- else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
- rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
- }
- shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
- shdr_status = bf_get(lpfc_mbox_hdr_status,
- &shdr->response);
- shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
- &shdr->response);
- if (rc != MBX_TIMEOUT)
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2988 POST_SGL_BLOCK mailbox "
- "command failed status x%x "
- "add_status x%x mbx status x%x\n",
- shdr_status, shdr_add_status, rc);
- rc = -ENXIO;
- goto err_exit;
- }
- if (ttl_cnt >= els_xri_cnt)
- break;
- }
-
- err_exit:
- if (rc == 0)
- bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags,
- LPFC_XRI_RSRC_RDY);
return rc;
}
/**
- * lpfc_sli4_post_scsi_sgl_block - post a block of scsi sgl list to firmware
+ * lpfc_sli4_post_io_sgl_block - post a block of nvme sgl list to firmware
* @phba: pointer to lpfc hba data structure.
- * @sblist: pointer to scsi buffer list.
+ * @nblist: pointer to nvme buffer list.
* @count: number of scsi buffers on the list.
*
* This routine is invoked to post a block of @count scsi sgl pages from a
- * SCSI buffer list @sblist to the HBA using non-embedded mailbox command.
+ * SCSI buffer list @nblist to the HBA using non-embedded mailbox command.
* No Lock is held.
*
**/
-int
-lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
- int cnt)
+static int
+lpfc_sli4_post_io_sgl_block(struct lpfc_hba *phba, struct list_head *nblist,
+ int count)
{
- struct lpfc_scsi_buf *psb;
+ struct lpfc_io_buf *lpfc_ncmd;
struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
struct sgl_page_pairs *sgl_pg_pairs;
void *viraddr;
@@ -12763,29 +18291,29 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
union lpfc_sli4_cfg_shdr *shdr;
/* Calculate the requested length of the dma memory */
- reqlen = cnt * sizeof(struct sgl_page_pairs) +
+ reqlen = count * sizeof(struct sgl_page_pairs) +
sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
if (reqlen > SLI4_PAGE_SIZE) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "0217 Block sgl registration required DMA "
+ "6118 Block sgl registration required DMA "
"size (%d) great than a page\n", reqlen);
return -ENOMEM;
}
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0283 Failed to allocate mbox cmd memory\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6119 Failed to allocate mbox cmd memory\n");
return -ENOMEM;
}
/* Allocate DMA memory and set up the non-embedded mailbox command */
alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen,
- LPFC_SLI4_MBX_NEMBED);
+ LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES,
+ reqlen, LPFC_SLI4_MBX_NEMBED);
if (alloclen < reqlen) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2561 Allocated DMA memory size (%d) is "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6120 Allocated DMA memory size (%d) is "
"less than the requested DMA memory "
"size (%d)\n", alloclen, reqlen);
lpfc_sli4_mbox_cmd_free(phba, mbox);
@@ -12800,14 +18328,15 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
sgl_pg_pairs = &sgl->sgl_pg_pairs;
pg_pairs = 0;
- list_for_each_entry(psb, sblist, list) {
+ list_for_each_entry(lpfc_ncmd, nblist, list) {
/* Set up the sge entry */
sgl_pg_pairs->sgl_pg0_addr_lo =
- cpu_to_le32(putPaddrLow(psb->dma_phys_bpl));
+ cpu_to_le32(putPaddrLow(lpfc_ncmd->dma_phys_sgl));
sgl_pg_pairs->sgl_pg0_addr_hi =
- cpu_to_le32(putPaddrHigh(psb->dma_phys_bpl));
+ cpu_to_le32(putPaddrHigh(lpfc_ncmd->dma_phys_sgl));
if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
- pdma_phys_bpl1 = psb->dma_phys_bpl + SGL_PAGE_SIZE;
+ pdma_phys_bpl1 = lpfc_ncmd->dma_phys_sgl +
+ SGL_PAGE_SIZE;
else
pdma_phys_bpl1 = 0;
sgl_pg_pairs->sgl_pg1_addr_lo =
@@ -12816,7 +18345,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
cpu_to_le32(putPaddrHigh(pdma_phys_bpl1));
/* Keep the first xritag on the list */
if (pg_pairs == 0)
- xritag_start = psb->cur_iocbq.sli4_xritag;
+ xritag_start = lpfc_ncmd->cur_iocbq.sli4_xritag;
sgl_pg_pairs++;
pg_pairs++;
}
@@ -12825,20 +18354,22 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
/* Perform endian conversion if necessary */
sgl->word0 = cpu_to_le32(sgl->word0);
- if (!phba->sli4_hba.intr_enable)
+ if (!phba->sli4_hba.intr_enable) {
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
- else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+ } else {
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
- shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
+ shdr = (union lpfc_sli4_cfg_shdr *)&sgl->cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
- if (rc != MBX_TIMEOUT)
+ if (!phba->sli4_hba.intr_enable)
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ else if (rc != MBX_TIMEOUT)
lpfc_sli4_mbox_cmd_free(phba, mbox);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2564 POST_SGL_BLOCK mailbox command failed "
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6125 POST_SGL_BLOCK mailbox command failed "
"status x%x add_status x%x mbx status x%x\n",
shdr_status, shdr_add_status, rc);
rc = -ENXIO;
@@ -12847,166 +18378,132 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
}
/**
- * lpfc_sli4_post_scsi_sgl_blk_ext - post a block of scsi sgls to the port.
+ * lpfc_sli4_post_io_sgl_list - Post blocks of nvme buffer sgls from a list
* @phba: pointer to lpfc hba data structure.
- * @sblist: pointer to scsi buffer list.
- * @count: number of scsi buffers on the list.
+ * @post_nblist: pointer to the nvme buffer list.
+ * @sb_count: number of nvme buffers.
*
- * This routine is invoked to post a block of @count scsi sgl pages from a
- * SCSI buffer list @sblist to the HBA using non-embedded mailbox command.
- * No Lock is held.
+ * This routine walks a list of nvme buffers that was passed in. It attempts
+ * to construct blocks of nvme buffer sgls which contains contiguous xris and
+ * uses the non-embedded SGL block post mailbox commands to post to the port.
+ * For single NVME buffer sgl with non-contiguous xri, if any, it shall use
+ * embedded SGL post mailbox command for posting. The @post_nblist passed in
+ * must be local list, thus no lock is needed when manipulate the list.
*
+ * Returns: 0 = failure, non-zero number of successfully posted buffers.
**/
int
-lpfc_sli4_post_scsi_sgl_blk_ext(struct lpfc_hba *phba, struct list_head *sblist,
- int cnt)
+lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba,
+ struct list_head *post_nblist, int sb_count)
{
- struct lpfc_scsi_buf *psb = NULL;
- struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
- struct sgl_page_pairs *sgl_pg_pairs;
- void *viraddr;
- LPFC_MBOXQ_t *mbox;
- uint32_t reqlen, alloclen, pg_pairs;
- uint32_t mbox_tmo;
- uint16_t xri_start = 0, scsi_xri_start;
- uint16_t rsrc_range;
- int rc = 0, avail_cnt;
- uint32_t shdr_status, shdr_add_status;
- dma_addr_t pdma_phys_bpl1;
- union lpfc_sli4_cfg_shdr *shdr;
- struct lpfc_rsrc_blks *rsrc_blk;
- uint32_t xri_cnt = 0;
+ struct lpfc_io_buf *lpfc_ncmd, *lpfc_ncmd_next;
+ int status, sgl_size;
+ int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0;
+ dma_addr_t pdma_phys_sgl1;
+ int last_xritag = NO_XRI;
+ int cur_xritag;
+ LIST_HEAD(prep_nblist);
+ LIST_HEAD(blck_nblist);
+ LIST_HEAD(nvme_nblist);
+
+ /* sanity check */
+ if (sb_count <= 0)
+ return -EINVAL;
- /* Calculate the total requested length of the dma memory */
- reqlen = cnt * sizeof(struct sgl_page_pairs) +
- sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
- if (reqlen > SLI4_PAGE_SIZE) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2932 Block sgl registration required DMA "
- "size (%d) great than a page\n", reqlen);
- return -ENOMEM;
- }
+ sgl_size = phba->cfg_sg_dma_buf_size;
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, post_nblist, list) {
+ list_del_init(&lpfc_ncmd->list);
+ block_cnt++;
+ if ((last_xritag != NO_XRI) &&
+ (lpfc_ncmd->cur_iocbq.sli4_xritag != last_xritag + 1)) {
+ /* a hole in xri block, form a sgl posting block */
+ list_splice_init(&prep_nblist, &blck_nblist);
+ post_cnt = block_cnt - 1;
+ /* prepare list for next posting block */
+ list_add_tail(&lpfc_ncmd->list, &prep_nblist);
+ block_cnt = 1;
+ } else {
+ /* prepare list for next posting block */
+ list_add_tail(&lpfc_ncmd->list, &prep_nblist);
+ /* enough sgls for non-embed sgl mbox command */
+ if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) {
+ list_splice_init(&prep_nblist, &blck_nblist);
+ post_cnt = block_cnt;
+ block_cnt = 0;
+ }
+ }
+ num_posting++;
+ last_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag;
+
+ /* end of repost sgl list condition for NVME buffers */
+ if (num_posting == sb_count) {
+ if (post_cnt == 0) {
+ /* last sgl posting block */
+ list_splice_init(&prep_nblist, &blck_nblist);
+ post_cnt = block_cnt;
+ } else if (block_cnt == 1) {
+ /* last single sgl with non-contiguous xri */
+ if (sgl_size > SGL_PAGE_SIZE)
+ pdma_phys_sgl1 =
+ lpfc_ncmd->dma_phys_sgl +
+ SGL_PAGE_SIZE;
+ else
+ pdma_phys_sgl1 = 0;
+ cur_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag;
+ status = lpfc_sli4_post_sgl(
+ phba, lpfc_ncmd->dma_phys_sgl,
+ pdma_phys_sgl1, cur_xritag);
+ if (status) {
+ /* Post error. Buffer unavailable. */
+ lpfc_ncmd->flags |=
+ LPFC_SBUF_NOT_POSTED;
+ } else {
+ /* Post success. Bffer available. */
+ lpfc_ncmd->flags &=
+ ~LPFC_SBUF_NOT_POSTED;
+ lpfc_ncmd->status = IOSTAT_SUCCESS;
+ num_posted++;
+ }
+ /* success, put on NVME buffer sgl list */
+ list_add_tail(&lpfc_ncmd->list, &nvme_nblist);
+ }
+ }
- /*
- * The use of extents requires the driver to post the sgl headers
- * in multiple postings to meet the contiguous resource assignment.
- */
- psb = list_prepare_entry(psb, sblist, list);
- scsi_xri_start = phba->sli4_hba.scsi_xri_start;
- list_for_each_entry(rsrc_blk, &phba->sli4_hba.lpfc_xri_blk_list,
- list) {
- rsrc_range = rsrc_blk->rsrc_start + rsrc_blk->rsrc_size;
- if (rsrc_range < scsi_xri_start)
- continue;
- else if (rsrc_blk->rsrc_used >= rsrc_blk->rsrc_size)
+ /* continue until a nembed page worth of sgls */
+ if (post_cnt == 0)
continue;
- else
- avail_cnt = rsrc_blk->rsrc_size - rsrc_blk->rsrc_used;
- reqlen = (avail_cnt * sizeof(struct sgl_page_pairs)) +
- sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
- /*
- * Allocate DMA memory and set up the non-embedded mailbox
- * command. The mbox is used to post an SGL page per loop
- * but the DMA memory has a use-once semantic so the mailbox
- * is used and freed per loop pass.
- */
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2933 Failed to allocate mbox cmd "
- "memory\n");
- return -ENOMEM;
- }
- alloclen = lpfc_sli4_config(phba, mbox,
- LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES,
- reqlen,
- LPFC_SLI4_MBX_NEMBED);
- if (alloclen < reqlen) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2934 Allocated DMA memory size (%d) "
- "is less than the requested DMA memory "
- "size (%d)\n", alloclen, reqlen);
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
+ /* post block of NVME buffer list sgls */
+ status = lpfc_sli4_post_io_sgl_block(phba, &blck_nblist,
+ post_cnt);
- /* Get the first SGE entry from the non-embedded DMA memory */
- viraddr = mbox->sge_array->addr[0];
-
- /* Set up the SGL pages in the non-embedded DMA pages */
- sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
- sgl_pg_pairs = &sgl->sgl_pg_pairs;
-
- /* pg_pairs tracks posted SGEs per loop iteration. */
- pg_pairs = 0;
- list_for_each_entry_continue(psb, sblist, list) {
- /* Set up the sge entry */
- sgl_pg_pairs->sgl_pg0_addr_lo =
- cpu_to_le32(putPaddrLow(psb->dma_phys_bpl));
- sgl_pg_pairs->sgl_pg0_addr_hi =
- cpu_to_le32(putPaddrHigh(psb->dma_phys_bpl));
- if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
- pdma_phys_bpl1 = psb->dma_phys_bpl +
- SGL_PAGE_SIZE;
- else
- pdma_phys_bpl1 = 0;
- sgl_pg_pairs->sgl_pg1_addr_lo =
- cpu_to_le32(putPaddrLow(pdma_phys_bpl1));
- sgl_pg_pairs->sgl_pg1_addr_hi =
- cpu_to_le32(putPaddrHigh(pdma_phys_bpl1));
- /* Keep the first xri for this extent. */
- if (pg_pairs == 0)
- xri_start = psb->cur_iocbq.sli4_xritag;
- sgl_pg_pairs++;
- pg_pairs++;
- xri_cnt++;
+ /* don't reset xirtag due to hole in xri block */
+ if (block_cnt == 0)
+ last_xritag = NO_XRI;
- /*
- * Track two exit conditions - the loop has constructed
- * all of the caller's SGE pairs or all available
- * resource IDs in this extent are consumed.
- */
- if ((xri_cnt == cnt) || (pg_pairs >= avail_cnt))
- break;
- }
- rsrc_blk->rsrc_used += pg_pairs;
- bf_set(lpfc_post_sgl_pages_xri, sgl, xri_start);
- bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
+ /* reset NVME buffer post count for next round of posting */
+ post_cnt = 0;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "3016 Post SCSI Extent SGL, start %d, cnt %d "
- "blk use %d\n",
- xri_start, pg_pairs, rsrc_blk->rsrc_used);
- /* Perform endian conversion if necessary */
- sgl->word0 = cpu_to_le32(sgl->word0);
- if (!phba->sli4_hba.intr_enable)
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
- else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
- rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
- }
- shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
- shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
- shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
- &shdr->response);
- if (rc != MBX_TIMEOUT)
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2935 POST_SGL_BLOCK mailbox command "
- "failed status x%x add_status x%x "
- "mbx status x%x\n",
- shdr_status, shdr_add_status, rc);
- return -ENXIO;
+ /* put posted NVME buffer-sgl posted on NVME buffer sgl list */
+ while (!list_empty(&blck_nblist)) {
+ list_remove_head(&blck_nblist, lpfc_ncmd,
+ struct lpfc_io_buf, list);
+ if (status) {
+ /* Post error. Mark buffer unavailable. */
+ lpfc_ncmd->flags |= LPFC_SBUF_NOT_POSTED;
+ } else {
+ /* Post success, Mark buffer available. */
+ lpfc_ncmd->flags &= ~LPFC_SBUF_NOT_POSTED;
+ lpfc_ncmd->status = IOSTAT_SUCCESS;
+ num_posted++;
+ }
+ list_add_tail(&lpfc_ncmd->list, &nvme_nblist);
}
-
- /* Post only what is requested. */
- if (xri_cnt >= cnt)
- break;
}
- return rc;
+ /* Push NVME buffers with sgl posted to the available list */
+ lpfc_io_buf_replenish(phba, &nvme_nblist);
+
+ return num_posted;
}
/**
@@ -13023,11 +18520,12 @@ static int
lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
{
/* make rctl_names static to save stack space */
- static char *rctl_names[] = FC_RCTL_NAMES_INIT;
- char *type_names[] = FC_TYPE_NAMES_INIT;
struct fc_vft_header *fc_vft_hdr;
+ struct fc_app_header *fc_app_hdr;
uint32_t *header = (uint32_t *) fc_hdr;
+#define FC_RCTL_MDS_DIAGS 0xF4
+
switch (fc_hdr->fh_r_ctl) {
case FC_RCTL_DD_UNCAT: /* uncategorized information */
case FC_RCTL_DD_SOL_DATA: /* solicited data */
@@ -13041,7 +18539,6 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
case FC_RCTL_ELS_REP: /* extended link services reply */
case FC_RCTL_ELS4_REQ: /* FC-4 ELS request */
case FC_RCTL_ELS4_REP: /* FC-4 ELS reply */
- case FC_RCTL_BA_NOP: /* basic link service NOP */
case FC_RCTL_BA_ABTS: /* basic link service abort */
case FC_RCTL_BA_RMC: /* remove connection */
case FC_RCTL_BA_ACC: /* basic accept */
@@ -13055,20 +18552,24 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
case FC_RCTL_F_BSY: /* fabric busy to data frame */
case FC_RCTL_F_BSYL: /* fabric busy to link control frame */
case FC_RCTL_LCR: /* link credit reset */
+ case FC_RCTL_MDS_DIAGS: /* MDS Diagnostics */
case FC_RCTL_END: /* end */
break;
case FC_RCTL_VFTH: /* Virtual Fabric tagging Header */
fc_vft_hdr = (struct fc_vft_header *)fc_hdr;
fc_hdr = &((struct fc_frame_header *)fc_vft_hdr)[1];
return lpfc_fc_frame_check(phba, fc_hdr);
+ case FC_RCTL_BA_NOP: /* basic link service NOP */
default:
goto drop;
}
+
switch (fc_hdr->fh_type) {
case FC_TYPE_BLS:
case FC_TYPE_ELS:
case FC_TYPE_FCP:
case FC_TYPE_CT:
+ case FC_TYPE_NVME:
break;
case FC_TYPE_IP:
case FC_TYPE_ILS:
@@ -13076,20 +18577,45 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
goto drop;
}
+ if (unlikely(phba->link_flag == LS_LOOPBACK_MODE &&
+ phba->cfg_vmid_app_header)) {
+ /* Application header is 16B device header */
+ if (fc_hdr->fh_df_ctl & LPFC_FC_16B_DEVICE_HEADER) {
+ fc_app_hdr = (struct fc_app_header *) (fc_hdr + 1);
+ if (be32_to_cpu(fc_app_hdr->src_app_id) !=
+ LOOPBACK_SRC_APPID) {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_ELS | LOG_LIBDFC,
+ "1932 Loopback src app id "
+ "not matched, app_id:x%x\n",
+ be32_to_cpu(fc_app_hdr->src_app_id));
+
+ goto drop;
+ }
+ } else {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_ELS | LOG_LIBDFC,
+ "1933 Loopback df_ctl bit not set, "
+ "df_ctl:x%x\n",
+ fc_hdr->fh_df_ctl);
+
+ goto drop;
+ }
+ }
+
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
- "2538 Received frame rctl:%s type:%s "
- "Frame Data:%08x %08x %08x %08x %08x %08x\n",
- rctl_names[fc_hdr->fh_r_ctl],
- type_names[fc_hdr->fh_type],
+ "2538 Received frame rctl:x%x, type:x%x, "
+ "frame Data:%08x %08x %08x %08x %08x %08x %08x\n",
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type,
be32_to_cpu(header[0]), be32_to_cpu(header[1]),
be32_to_cpu(header[2]), be32_to_cpu(header[3]),
- be32_to_cpu(header[4]), be32_to_cpu(header[5]));
+ be32_to_cpu(header[4]), be32_to_cpu(header[5]),
+ be32_to_cpu(header[6]));
return 0;
drop:
lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
- "2539 Dropped frame rctl:%s type:%s\n",
- rctl_names[fc_hdr->fh_r_ctl],
- type_names[fc_hdr->fh_type]);
+ "2539 Dropped frame rctl:x%x type:x%x\n",
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
return 1;
}
@@ -13116,6 +18642,7 @@ lpfc_fc_hdr_get_vfi(struct fc_frame_header *fc_hdr)
* @phba: Pointer to the HBA structure to search for the vport on
* @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
* @fcfi: The FC Fabric ID that the frame came from
+ * @did: Destination ID to match against
*
* This function searches the @phba for a vport that matches the content of the
* @fc_hdr passed in and the @fcfi. This function uses the @fc_hdr to fetch the
@@ -13125,17 +18652,20 @@ lpfc_fc_hdr_get_vfi(struct fc_frame_header *fc_hdr)
**/
static struct lpfc_vport *
lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
- uint16_t fcfi)
+ uint16_t fcfi, uint32_t did)
{
struct lpfc_vport **vports;
struct lpfc_vport *vport = NULL;
int i;
- uint32_t did = (fc_hdr->fh_d_id[0] << 16 |
- fc_hdr->fh_d_id[1] << 8 |
- fc_hdr->fh_d_id[2]);
+
+ if (did == Fabric_DID)
+ return phba->pport;
+ if (test_bit(FC_PT2PT, &phba->pport->fc_flag) &&
+ phba->link_state != LPFC_HBA_READY)
+ return phba->pport;
vports = lpfc_create_vport_work_array(phba);
- if (vports != NULL)
+ if (vports != NULL) {
for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
if (phba->fcf.fcfi == fcfi &&
vports[i]->vfi == lpfc_fc_hdr_get_vfi(fc_hdr) &&
@@ -13144,6 +18674,7 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
break;
}
}
+ }
lpfc_destroy_vport_work_array(phba, vports);
return vport;
}
@@ -13158,7 +18689,7 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
* the driver uses this time stamp to indicate if any received sequences have
* timed out.
**/
-void
+static void
lpfc_update_rcv_time_stamp(struct lpfc_vport *vport)
{
struct lpfc_dmabuf *h_buf;
@@ -13249,6 +18780,7 @@ lpfc_rcv_seq_check_edtov(struct lpfc_vport *vport)
/**
* lpfc_fc_frame_add - Adds a frame to the vport's list of received sequences
+ * @vport: pointer to a vitural port
* @dmabuf: pointer to a dmabuf that describes the hdr and data of the FC frame
*
* This function searches through the existing incomplete sequences that have
@@ -13268,10 +18800,12 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
struct lpfc_dmabuf *h_buf;
struct hbq_dmabuf *seq_dmabuf = NULL;
struct hbq_dmabuf *temp_dmabuf = NULL;
+ uint8_t found = 0;
INIT_LIST_HEAD(&dmabuf->dbuf.list);
dmabuf->time_stamp = jiffies;
new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+
/* Use the hdr_buf to find the sequence that this frame belongs to */
list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
temp_hdr = (struct fc_frame_header *)h_buf->virt;
@@ -13306,12 +18840,12 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
seq_dmabuf->time_stamp = jiffies;
lpfc_update_rcv_time_stamp(vport);
if (list_empty(&seq_dmabuf->dbuf.list)) {
- temp_hdr = dmabuf->hbuf.virt;
list_add_tail(&dmabuf->dbuf.list, &seq_dmabuf->dbuf.list);
return seq_dmabuf;
}
/* find the correct place in the sequence to insert this frame */
- list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) {
+ d_buf = list_entry(seq_dmabuf->dbuf.list.prev, typeof(*d_buf), list);
+ while (!found) {
temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
temp_hdr = (struct fc_frame_header *)temp_dmabuf->hbuf.virt;
/*
@@ -13321,9 +18855,17 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
if (be16_to_cpu(new_hdr->fh_seq_cnt) >
be16_to_cpu(temp_hdr->fh_seq_cnt)) {
list_add(&dmabuf->dbuf.list, &temp_dmabuf->dbuf.list);
- return seq_dmabuf;
+ found = 1;
+ break;
}
+
+ if (&d_buf->list == &seq_dmabuf->dbuf.list)
+ break;
+ d_buf = list_entry(d_buf->list.prev, typeof(*d_buf), list);
}
+
+ if (found)
+ return seq_dmabuf;
return NULL;
}
@@ -13380,6 +18922,40 @@ lpfc_sli4_abort_partial_seq(struct lpfc_vport *vport,
}
/**
+ * lpfc_sli4_abort_ulp_seq - Abort assembled unsol sequence from ulp
+ * @vport: pointer to a vitural port
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function tries to abort from the assembed sequence from upper level
+ * protocol, described by the information from basic abbort @dmabuf. It
+ * checks to see whether such pending context exists at upper level protocol.
+ * If so, it shall clean up the pending context.
+ *
+ * Return
+ * true -- if there is matching pending context of the sequence cleaned
+ * at ulp;
+ * false -- if there is no matching pending context of the sequence present
+ * at ulp.
+ **/
+static bool
+lpfc_sli4_abort_ulp_seq(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
+{
+ struct lpfc_hba *phba = vport->phba;
+ int handled;
+
+ /* Accepting abort at ulp with SLI4 only */
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ return false;
+
+ /* Register all caring upper level protocols to attend abort */
+ handled = lpfc_ct_handle_unsol_abort(phba, dmabuf);
+ if (handled)
+ return true;
+
+ return false;
+}
+
+/**
* lpfc_sli4_seq_abort_rsp_cmpl - BLS ABORT RSP seq abort iocb complete handler
* @phba: Pointer to HBA context object.
* @cmd_iocbq: pointer to the command iocbq structure.
@@ -13394,8 +18970,17 @@ lpfc_sli4_seq_abort_rsp_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmd_iocbq,
struct lpfc_iocbq *rsp_iocbq)
{
- if (cmd_iocbq)
+ if (cmd_iocbq) {
+ lpfc_nlp_put(cmd_iocbq->ndlp);
lpfc_sli_release_iocbq(phba, cmd_iocbq);
+ }
+
+ /* Failure means BLS ABORT RSP did not get delivered to remote node*/
+ if (rsp_iocbq && rsp_iocbq->iocb.ulpStatus)
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3154 BLS ABORT RSP failed, data: x%x/x%x\n",
+ get_job_ulpstatus(phba, rsp_iocbq),
+ get_job_word4(phba, rsp_iocbq));
}
/**
@@ -13406,11 +18991,11 @@ lpfc_sli4_seq_abort_rsp_cmpl(struct lpfc_hba *phba,
* This function validates the xri maps to the known range of XRIs allocated an
* used by the driver.
**/
-static uint16_t
+uint16_t
lpfc_sli4_xri_inrange(struct lpfc_hba *phba,
uint16_t xri)
{
- int i;
+ uint16_t i;
for (i = 0; i < phba->sli4_hba.max_cfg_param.max_xri; i++) {
if (xri == phba->sli4_hba.xri_ids[i])
@@ -13419,24 +19004,25 @@ lpfc_sli4_xri_inrange(struct lpfc_hba *phba,
return NO_XRI;
}
-
/**
* lpfc_sli4_seq_abort_rsp - bls rsp to sequence abort
- * @phba: Pointer to HBA context object.
+ * @vport: pointer to a virtual port.
* @fc_hdr: pointer to a FC frame header.
+ * @aborted: was the partially assembled receive sequence successfully aborted
*
* This function sends a basic response to a previous unsol sequence abort
* event after aborting the sequence handling.
**/
-static void
-lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
- struct fc_frame_header *fc_hdr)
+void
+lpfc_sli4_seq_abort_rsp(struct lpfc_vport *vport,
+ struct fc_frame_header *fc_hdr, bool aborted)
{
+ struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *ctiocb = NULL;
struct lpfc_nodelist *ndlp;
- uint16_t oxid, rxid;
+ uint16_t oxid, rxid, xri, lxri;
uint32_t sid, fctl;
- IOCB_t *icmd;
+ union lpfc_wqe128 *icmd;
int rc;
if (!lpfc_is_link_up(phba))
@@ -13446,57 +19032,78 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
oxid = be16_to_cpu(fc_hdr->fh_ox_id);
rxid = be16_to_cpu(fc_hdr->fh_rx_id);
- ndlp = lpfc_findnode_did(phba->pport, sid);
+ ndlp = lpfc_findnode_did(vport, sid);
if (!ndlp) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
- "1268 Find ndlp returned NULL for oxid:x%x "
- "SID:x%x\n", oxid, sid);
- return;
+ ndlp = lpfc_nlp_init(vport, sid);
+ if (!ndlp) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
+ "1268 Failed to allocate ndlp for "
+ "oxid:x%x SID:x%x\n", oxid, sid);
+ return;
+ }
+ /* Put ndlp onto vport node list */
+ lpfc_enqueue_node(vport, ndlp);
}
- if (lpfc_sli4_xri_inrange(phba, rxid))
- lpfc_set_rrq_active(phba, ndlp, rxid, oxid, 0);
/* Allocate buffer for rsp iocb */
ctiocb = lpfc_sli_get_iocbq(phba);
if (!ctiocb)
return;
+ icmd = &ctiocb->wqe;
+
/* Extract the F_CTL field from FC_HDR */
fctl = sli4_fctl_from_fc_hdr(fc_hdr);
- icmd = &ctiocb->iocb;
- icmd->un.xseq64.bdl.bdeSize = 0;
- icmd->un.xseq64.bdl.ulpIoTag32 = 0;
- icmd->un.xseq64.w5.hcsw.Dfctl = 0;
- icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_ACC;
- icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_BLS;
-
- /* Fill in the rest of iocb fields */
- icmd->ulpCommand = CMD_XMIT_BLS_RSP64_CX;
- icmd->ulpBdeCount = 0;
- icmd->ulpLe = 1;
- icmd->ulpClass = CLASS3;
- icmd->ulpContext = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
- ctiocb->context1 = ndlp;
+ ctiocb->ndlp = lpfc_nlp_get(ndlp);
+ if (!ctiocb->ndlp) {
+ lpfc_sli_release_iocbq(phba, ctiocb);
+ return;
+ }
- ctiocb->iocb_cmpl = NULL;
- ctiocb->vport = phba->pport;
- ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_rsp_cmpl;
+ ctiocb->vport = vport;
+ ctiocb->cmd_cmpl = lpfc_sli4_seq_abort_rsp_cmpl;
ctiocb->sli4_lxritag = NO_XRI;
ctiocb->sli4_xritag = NO_XRI;
+ ctiocb->abort_rctl = FC_RCTL_BA_ACC;
- /* If the oxid maps to the FCP XRI range or if it is out of range,
- * send a BLS_RJT. The driver no longer has that exchange.
- * Override the IOCB for a BA_RJT.
+ if (fctl & FC_FC_EX_CTX)
+ /* Exchange responder sent the abort so we
+ * own the oxid.
+ */
+ xri = oxid;
+ else
+ xri = rxid;
+ lxri = lpfc_sli4_xri_inrange(phba, xri);
+ if (lxri != NO_XRI)
+ lpfc_set_rrq_active(phba, ndlp, lxri,
+ (xri == oxid) ? rxid : oxid, 0);
+ /* For BA_ABTS from exchange responder, if the logical xri with
+ * the oxid maps to the FCP XRI range, the port no longer has
+ * that exchange context, send a BLS_RJT. Override the IOCB for
+ * a BA_RJT.
+ */
+ if ((fctl & FC_FC_EX_CTX) &&
+ (lxri > lpfc_sli4_get_iocb_cnt(phba))) {
+ ctiocb->abort_rctl = FC_RCTL_BA_RJT;
+ bf_set(xmit_bls_rsp64_rjt_vspec, &icmd->xmit_bls_rsp, 0);
+ bf_set(xmit_bls_rsp64_rjt_expc, &icmd->xmit_bls_rsp,
+ FC_BA_RJT_INV_XID);
+ bf_set(xmit_bls_rsp64_rjt_rsnc, &icmd->xmit_bls_rsp,
+ FC_BA_RJT_UNABLE);
+ }
+
+ /* If BA_ABTS failed to abort a partially assembled receive sequence,
+ * the driver no longer has that exchange, send a BLS_RJT. Override
+ * the IOCB for a BA_RJT.
*/
- if (oxid > (phba->sli4_hba.max_cfg_param.max_xri +
- phba->sli4_hba.max_cfg_param.xri_base) ||
- oxid > (lpfc_sli4_get_els_iocb_cnt(phba) +
- phba->sli4_hba.max_cfg_param.xri_base)) {
- icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_RJT;
- bf_set(lpfc_vndr_code, &icmd->un.bls_rsp, 0);
- bf_set(lpfc_rsn_expln, &icmd->un.bls_rsp, FC_BA_RJT_INV_XID);
- bf_set(lpfc_rsn_code, &icmd->un.bls_rsp, FC_BA_RJT_UNABLE);
+ if (aborted == false) {
+ ctiocb->abort_rctl = FC_RCTL_BA_RJT;
+ bf_set(xmit_bls_rsp64_rjt_vspec, &icmd->xmit_bls_rsp, 0);
+ bf_set(xmit_bls_rsp64_rjt_expc, &icmd->xmit_bls_rsp,
+ FC_BA_RJT_INV_XID);
+ bf_set(xmit_bls_rsp64_rjt_rsnc, &icmd->xmit_bls_rsp,
+ FC_BA_RJT_UNABLE);
}
if (fctl & FC_FC_EX_CTX) {
@@ -13504,32 +19111,53 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
* of BA_ACC will use OX_ID from ABTS for the XRI_TAG
* field and RX_ID from ABTS for RX_ID field.
*/
- bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_RSP);
- bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, rxid);
+ ctiocb->abort_bls = LPFC_ABTS_UNSOL_RSP;
+ bf_set(xmit_bls_rsp64_rxid, &icmd->xmit_bls_rsp, rxid);
} else {
/* ABTS sent by initiator to CT exchange, construction
* of BA_ACC will need to allocate a new XRI as for the
- * XRI_TAG and RX_ID fields.
+ * XRI_TAG field.
*/
- bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_INT);
- bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, NO_XRI);
+ ctiocb->abort_bls = LPFC_ABTS_UNSOL_INT;
}
- bf_set(lpfc_abts_oxid, &icmd->un.bls_rsp, oxid);
+
+ /* OX_ID is invariable to who sent ABTS to CT exchange */
+ bf_set(xmit_bls_rsp64_oxid, &icmd->xmit_bls_rsp, oxid);
+ bf_set(xmit_bls_rsp64_oxid, &icmd->xmit_bls_rsp, rxid);
+
+ /* Use CT=VPI */
+ bf_set(wqe_els_did, &icmd->xmit_bls_rsp.wqe_dest,
+ ndlp->nlp_DID);
+ bf_set(xmit_bls_rsp64_temprpi, &icmd->xmit_bls_rsp,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ bf_set(wqe_cmnd, &icmd->generic.wqe_com, CMD_XMIT_BLS_RSP64_CX);
/* Xmit CT abts response on exchange <xid> */
- lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
- "1200 Send BLS cmd x%x on oxid x%x Data: x%x\n",
- icmd->un.xseq64.w5.hcsw.Rctl, oxid, phba->link_state);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "1200 Send BLS cmd x%x on oxid x%x Data: x%x\n",
+ ctiocb->abort_rctl, oxid, phba->link_state);
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
if (rc == IOCB_ERROR) {
- lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
- "2925 Failed to issue CT ABTS RSP x%x on "
- "xri x%x, Data x%x\n",
- icmd->un.xseq64.w5.hcsw.Rctl, oxid,
- phba->link_state);
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "2925 Failed to issue CT ABTS RSP x%x on "
+ "xri x%x, Data x%x\n",
+ ctiocb->abort_rctl, oxid,
+ phba->link_state);
+ lpfc_nlp_put(ndlp);
+ ctiocb->ndlp = NULL;
lpfc_sli_release_iocbq(phba, ctiocb);
}
+
+ /* if only usage of this nodelist is BLS response, release initial ref
+ * to free ndlp when transmit completes
+ */
+ if (ndlp->nlp_state == NLP_STE_UNUSED_NODE &&
+ !test_bit(NLP_DROPPED, &ndlp->nlp_flag) &&
+ !(ndlp->fc4_xpt_flags & (NVME_XPT_REGD | SCSI_XPT_REGD))) {
+ set_bit(NLP_DROPPED, &ndlp->nlp_flag);
+ lpfc_nlp_put(ndlp);
+ }
}
/**
@@ -13541,43 +19169,41 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
* receive sequence is only partially assembed by the driver, it shall abort
* the partially assembled frames for the sequence. Otherwise, if the
* unsolicited receive sequence has been completely assembled and passed to
- * the Upper Layer Protocol (UPL), it then mark the per oxid status for the
+ * the Upper Layer Protocol (ULP), it then mark the per oxid status for the
* unsolicited sequence has been aborted. After that, it will issue a basic
* accept to accept the abort.
**/
-void
+static void
lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport,
struct hbq_dmabuf *dmabuf)
{
struct lpfc_hba *phba = vport->phba;
struct fc_frame_header fc_hdr;
uint32_t fctl;
- bool abts_par;
+ bool aborted;
/* Make a copy of fc_hdr before the dmabuf being released */
memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
fctl = sli4_fctl_from_fc_hdr(&fc_hdr);
if (fctl & FC_FC_EX_CTX) {
- /*
- * ABTS sent by responder to exchange, just free the buffer
- */
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ /* ABTS by responder to exchange, no cleanup needed */
+ aborted = true;
} else {
- /*
- * ABTS sent by initiator to exchange, need to do cleanup
- */
- /* Try to abort partially assembled seq */
- abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);
+ /* ABTS by initiator to exchange, need to do cleanup */
+ aborted = lpfc_sli4_abort_partial_seq(vport, dmabuf);
+ if (aborted == false)
+ aborted = lpfc_sli4_abort_ulp_seq(vport, dmabuf);
+ }
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
- /* Send abort to ULP if partially seq abort failed */
- if (abts_par == false)
- lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
- else
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ if (phba->nvmet_support) {
+ lpfc_nvmet_rcv_unsol_abort(vport, &fc_hdr);
+ return;
}
- /* Send basic accept (BA_ACC) to the abort requester */
- lpfc_sli4_seq_abort_rsp(phba, &fc_hdr);
+
+ /* Respond with BA_ACC or BA_RJT accordingly */
+ lpfc_sli4_seq_abort_rsp(vport, &fc_hdr, aborted);
}
/**
@@ -13630,7 +19256,7 @@ lpfc_seq_complete(struct hbq_dmabuf *dmabuf)
/**
* lpfc_prep_seq - Prep sequence for ULP processing
* @vport: Pointer to the vport on which this sequence was received
- * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ * @seq_dmabuf: pointer to a dmabuf that describes the FC sequence
*
* This function takes a sequence, described by a list of frames, and creates
* a list of iocbq structures to describe the sequence. This iocbq list will be
@@ -13643,11 +19269,12 @@ lpfc_seq_complete(struct hbq_dmabuf *dmabuf)
static struct lpfc_iocbq *
lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
{
+ struct hbq_dmabuf *hbq_buf;
struct lpfc_dmabuf *d_buf, *n_buf;
struct lpfc_iocbq *first_iocbq, *iocbq;
struct fc_frame_header *fc_hdr;
uint32_t sid;
- struct ulp_bde64 *pbde;
+ uint32_t len, tot_len;
fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
/* remove from receive buffer list */
@@ -13655,26 +19282,45 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
lpfc_update_rcv_time_stamp(vport);
/* get the Remote Port's SID */
sid = sli4_sid_from_fc_hdr(fc_hdr);
+ tot_len = 0;
/* Get an iocbq struct to fill in. */
first_iocbq = lpfc_sli_get_iocbq(vport->phba);
if (first_iocbq) {
/* Initialize the first IOCB. */
- first_iocbq->iocb.unsli3.rcvsli3.acc_len = 0;
- first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS;
- first_iocbq->iocb.ulpCommand = CMD_IOCB_RCV_SEQ64_CX;
- first_iocbq->iocb.ulpContext = be16_to_cpu(fc_hdr->fh_ox_id);
- /* iocbq is prepped for internal consumption. Logical vpi. */
- first_iocbq->iocb.unsli3.rcvsli3.vpi = vport->vpi;
- /* put the first buffer into the first IOCBq */
- first_iocbq->context2 = &seq_dmabuf->dbuf;
- first_iocbq->context3 = NULL;
- first_iocbq->iocb.ulpBdeCount = 1;
- first_iocbq->iocb.un.cont64[0].tus.f.bdeSize =
- LPFC_DATA_BUF_SIZE;
- first_iocbq->iocb.un.rcvels.remoteID = sid;
- first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length,
- &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
+ first_iocbq->wcqe_cmpl.total_data_placed = 0;
+ bf_set(lpfc_wcqe_c_status, &first_iocbq->wcqe_cmpl,
+ IOSTAT_SUCCESS);
+ first_iocbq->vport = vport;
+
+ /* Check FC Header to see what TYPE of frame we are rcv'ing */
+ if (sli4_type_from_fc_hdr(fc_hdr) == FC_TYPE_ELS) {
+ bf_set(els_rsp64_sid, &first_iocbq->wqe.xmit_els_rsp,
+ sli4_did_from_fc_hdr(fc_hdr));
+ }
+
+ bf_set(wqe_ctxt_tag, &first_iocbq->wqe.xmit_els_rsp.wqe_com,
+ NO_XRI);
+ bf_set(wqe_rcvoxid, &first_iocbq->wqe.xmit_els_rsp.wqe_com,
+ be16_to_cpu(fc_hdr->fh_ox_id));
+
+ /* put the first buffer into the first iocb */
+ tot_len = bf_get(lpfc_rcqe_length,
+ &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
+
+ first_iocbq->cmd_dmabuf = &seq_dmabuf->dbuf;
+ first_iocbq->bpl_dmabuf = NULL;
+ /* Keep track of the BDE count */
+ first_iocbq->wcqe_cmpl.word3 = 1;
+
+ if (tot_len > LPFC_DATA_BUF_SIZE)
+ first_iocbq->wqe.gen_req.bde.tus.f.bdeSize =
+ LPFC_DATA_BUF_SIZE;
+ else
+ first_iocbq->wqe.gen_req.bde.tus.f.bdeSize = tot_len;
+
+ first_iocbq->wcqe_cmpl.total_data_placed = tot_len;
+ bf_set(wqe_els_did, &first_iocbq->wqe.xmit_els_rsp.wqe_dest,
+ sid);
}
iocbq = first_iocbq;
/*
@@ -13686,39 +19332,55 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
lpfc_in_buf_free(vport->phba, d_buf);
continue;
}
- if (!iocbq->context3) {
- iocbq->context3 = d_buf;
- iocbq->iocb.ulpBdeCount++;
- pbde = (struct ulp_bde64 *)
- &iocbq->iocb.unsli3.sli3Words[4];
- pbde->tus.f.bdeSize = LPFC_DATA_BUF_SIZE;
- first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length,
- &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
+ if (!iocbq->bpl_dmabuf) {
+ iocbq->bpl_dmabuf = d_buf;
+ iocbq->wcqe_cmpl.word3++;
+ /* We need to get the size out of the right CQE */
+ hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+ len = bf_get(lpfc_rcqe_length,
+ &hbq_buf->cq_event.cqe.rcqe_cmpl);
+ iocbq->unsol_rcv_len = len;
+ iocbq->wcqe_cmpl.total_data_placed += len;
+ tot_len += len;
} else {
iocbq = lpfc_sli_get_iocbq(vport->phba);
if (!iocbq) {
if (first_iocbq) {
- first_iocbq->iocb.ulpStatus =
- IOSTAT_FCP_RSP_ERROR;
- first_iocbq->iocb.un.ulpWord[4] =
- IOERR_NO_RESOURCES;
+ bf_set(lpfc_wcqe_c_status,
+ &first_iocbq->wcqe_cmpl,
+ IOSTAT_SUCCESS);
+ first_iocbq->wcqe_cmpl.parameter =
+ IOERR_NO_RESOURCES;
}
lpfc_in_buf_free(vport->phba, d_buf);
continue;
}
- iocbq->context2 = d_buf;
- iocbq->context3 = NULL;
- iocbq->iocb.ulpBdeCount = 1;
- iocbq->iocb.un.cont64[0].tus.f.bdeSize =
- LPFC_DATA_BUF_SIZE;
- first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length,
- &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
- iocbq->iocb.un.rcvels.remoteID = sid;
+ /* We need to get the size out of the right CQE */
+ hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+ len = bf_get(lpfc_rcqe_length,
+ &hbq_buf->cq_event.cqe.rcqe_cmpl);
+ iocbq->cmd_dmabuf = d_buf;
+ iocbq->bpl_dmabuf = NULL;
+ iocbq->wcqe_cmpl.word3 = 1;
+
+ if (len > LPFC_DATA_BUF_SIZE)
+ iocbq->wqe.xmit_els_rsp.bde.tus.f.bdeSize =
+ LPFC_DATA_BUF_SIZE;
+ else
+ iocbq->wqe.xmit_els_rsp.bde.tus.f.bdeSize =
+ len;
+
+ tot_len += len;
+ iocbq->wcqe_cmpl.total_data_placed = tot_len;
+ bf_set(wqe_els_did, &iocbq->wqe.xmit_els_rsp.wqe_dest,
+ sid);
list_add_tail(&iocbq->list, &first_iocbq->list);
}
}
+ /* Free the sequence's header buffer */
+ if (!first_iocbq)
+ lpfc_in_buf_free(vport->phba, &seq_dmabuf->dbuf);
+
return first_iocbq;
}
@@ -13733,7 +19395,7 @@ lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport,
fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
iocbq = lpfc_prep_seq(vport, seq_dmabuf);
if (!iocbq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2707 Ring %d handler: Failed to allocate "
"iocb Rctl x%x Type x%x received\n",
LPFC_ELS_RING,
@@ -13741,33 +19403,148 @@ lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport,
return;
}
if (!lpfc_complete_unsol_iocb(phba,
- &phba->sli.ring[LPFC_ELS_RING],
+ phba->sli4_hba.els_wq->pring,
iocbq, fc_hdr->fh_r_ctl,
- fc_hdr->fh_type))
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ fc_hdr->fh_type)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2540 Ring %d handler: unexpected Rctl "
"x%x Type x%x received\n",
LPFC_ELS_RING,
fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+ lpfc_in_buf_free(phba, &seq_dmabuf->dbuf);
+ }
/* Free iocb created in lpfc_prep_seq */
list_for_each_entry_safe(curr_iocb, next_iocb,
- &iocbq->list, list) {
+ &iocbq->list, list) {
list_del_init(&curr_iocb->list);
lpfc_sli_release_iocbq(phba, curr_iocb);
}
lpfc_sli_release_iocbq(phba, iocbq);
}
+static void
+lpfc_sli4_mds_loopback_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
+{
+ struct lpfc_dmabuf *pcmd = cmdiocb->cmd_dmabuf;
+
+ if (pcmd && pcmd->virt)
+ dma_pool_free(phba->lpfc_drb_pool, pcmd->virt, pcmd->phys);
+ kfree(pcmd);
+ lpfc_sli_release_iocbq(phba, cmdiocb);
+ lpfc_drain_txq(phba);
+}
+
+static void
+lpfc_sli4_handle_mds_loopback(struct lpfc_vport *vport,
+ struct hbq_dmabuf *dmabuf)
+{
+ struct fc_frame_header *fc_hdr;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_iocbq *iocbq = NULL;
+ union lpfc_wqe128 *pwqe;
+ struct lpfc_dmabuf *pcmd = NULL;
+ uint32_t frame_len;
+ int rc;
+ unsigned long iflags;
+
+ fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ frame_len = bf_get(lpfc_rcqe_length, &dmabuf->cq_event.cqe.rcqe_cmpl);
+
+ /* Send the received frame back */
+ iocbq = lpfc_sli_get_iocbq(phba);
+ if (!iocbq) {
+ /* Queue cq event and wakeup worker thread to process it */
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ list_add_tail(&dmabuf->cq_event.list,
+ &phba->sli4_hba.sp_queue_event);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ set_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag);
+ lpfc_worker_wake_up(phba);
+ return;
+ }
+
+ /* Allocate buffer for command payload */
+ pcmd = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (pcmd)
+ pcmd->virt = dma_pool_alloc(phba->lpfc_drb_pool, GFP_KERNEL,
+ &pcmd->phys);
+ if (!pcmd || !pcmd->virt)
+ goto exit;
+
+ INIT_LIST_HEAD(&pcmd->list);
+
+ /* copyin the payload */
+ memcpy(pcmd->virt, dmabuf->dbuf.virt, frame_len);
+
+ iocbq->cmd_dmabuf = pcmd;
+ iocbq->vport = vport;
+ iocbq->cmd_flag &= ~LPFC_FIP_ELS_ID_MASK;
+ iocbq->cmd_flag |= LPFC_USE_FCPWQIDX;
+ iocbq->num_bdes = 0;
+
+ pwqe = &iocbq->wqe;
+ /* fill in BDE's for command */
+ pwqe->gen_req.bde.addrHigh = putPaddrHigh(pcmd->phys);
+ pwqe->gen_req.bde.addrLow = putPaddrLow(pcmd->phys);
+ pwqe->gen_req.bde.tus.f.bdeSize = frame_len;
+ pwqe->gen_req.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+
+ pwqe->send_frame.frame_len = frame_len;
+ pwqe->send_frame.fc_hdr_wd0 = be32_to_cpu(*((__be32 *)fc_hdr));
+ pwqe->send_frame.fc_hdr_wd1 = be32_to_cpu(*((__be32 *)fc_hdr + 1));
+ pwqe->send_frame.fc_hdr_wd2 = be32_to_cpu(*((__be32 *)fc_hdr + 2));
+ pwqe->send_frame.fc_hdr_wd3 = be32_to_cpu(*((__be32 *)fc_hdr + 3));
+ pwqe->send_frame.fc_hdr_wd4 = be32_to_cpu(*((__be32 *)fc_hdr + 4));
+ pwqe->send_frame.fc_hdr_wd5 = be32_to_cpu(*((__be32 *)fc_hdr + 5));
+
+ pwqe->generic.wqe_com.word7 = 0;
+ pwqe->generic.wqe_com.word10 = 0;
+
+ bf_set(wqe_cmnd, &pwqe->generic.wqe_com, CMD_SEND_FRAME);
+ bf_set(wqe_sof, &pwqe->generic.wqe_com, 0x2E); /* SOF byte */
+ bf_set(wqe_eof, &pwqe->generic.wqe_com, 0x41); /* EOF byte */
+ bf_set(wqe_lenloc, &pwqe->generic.wqe_com, 1);
+ bf_set(wqe_xbl, &pwqe->generic.wqe_com, 1);
+ bf_set(wqe_dbde, &pwqe->generic.wqe_com, 1);
+ bf_set(wqe_xc, &pwqe->generic.wqe_com, 1);
+ bf_set(wqe_cmd_type, &pwqe->generic.wqe_com, 0xA);
+ bf_set(wqe_cqid, &pwqe->generic.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_xri_tag, &pwqe->generic.wqe_com, iocbq->sli4_xritag);
+ bf_set(wqe_reqtag, &pwqe->generic.wqe_com, iocbq->iotag);
+ bf_set(wqe_class, &pwqe->generic.wqe_com, CLASS3);
+ pwqe->generic.wqe_com.abort_tag = iocbq->iotag;
+
+ iocbq->cmd_cmpl = lpfc_sli4_mds_loopback_cmpl;
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocbq, 0);
+ if (rc == IOCB_ERROR)
+ goto exit;
+
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+
+exit:
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2023 Unable to process MDS loopback frame\n");
+ if (pcmd && pcmd->virt)
+ dma_pool_free(phba->lpfc_drb_pool, pcmd->virt, pcmd->phys);
+ kfree(pcmd);
+ if (iocbq)
+ lpfc_sli_release_iocbq(phba, iocbq);
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+}
+
/**
* lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
* @phba: Pointer to HBA context object.
+ * @dmabuf: Pointer to a dmabuf that describes the FC sequence.
*
* This function is called with no lock held. This function processes all
* the received buffers and gives it to upper layers when a received buffer
* indicates that it is the final frame in the sequence. The interrupt
- * service routine processes received buffers at interrupt contexts and adds
- * received dma buffers to the rb_pend_list queue and signals the worker thread.
+ * service routine processes received buffers at interrupt contexts.
* Worker thread calls lpfc_sli4_handle_received_buffer, which will call the
* appropriate receive function when the final frame in a sequence is received.
**/
@@ -13779,21 +19556,72 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
struct fc_frame_header *fc_hdr;
struct lpfc_vport *vport;
uint32_t fcfi;
+ uint32_t did;
/* Process each received buffer */
fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+
+ if (fc_hdr->fh_r_ctl == FC_RCTL_MDS_DIAGS ||
+ fc_hdr->fh_r_ctl == FC_RCTL_DD_UNSOL_DATA) {
+ vport = phba->pport;
+ /* Handle MDS Loopback frames */
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
+ lpfc_sli4_handle_mds_loopback(vport, dmabuf);
+ else
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+
/* check to see if this a valid type of frame */
if (lpfc_fc_frame_check(phba, fc_hdr)) {
lpfc_in_buf_free(phba, &dmabuf->dbuf);
return;
}
- fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl);
- vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
- if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) {
+
+ if ((bf_get(lpfc_cqe_code,
+ &dmabuf->cq_event.cqe.rcqe_cmpl) == CQE_CODE_RECEIVE_V1))
+ fcfi = bf_get(lpfc_rcqe_fcf_id_v1,
+ &dmabuf->cq_event.cqe.rcqe_cmpl);
+ else
+ fcfi = bf_get(lpfc_rcqe_fcf_id,
+ &dmabuf->cq_event.cqe.rcqe_cmpl);
+
+ if (fc_hdr->fh_r_ctl == 0xF4 && fc_hdr->fh_type == 0xFF) {
+ vport = phba->pport;
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2023 MDS Loopback %d bytes\n",
+ bf_get(lpfc_rcqe_length,
+ &dmabuf->cq_event.cqe.rcqe_cmpl));
+ /* Handle MDS Loopback frames */
+ lpfc_sli4_handle_mds_loopback(vport, dmabuf);
+ return;
+ }
+
+ /* d_id this frame is directed to */
+ did = sli4_did_from_fc_hdr(fc_hdr);
+
+ vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi, did);
+ if (!vport) {
/* throw out the frame */
lpfc_in_buf_free(phba, &dmabuf->dbuf);
return;
}
+
+ /* vport is registered unless we rcv a FLOGI directed to Fabric_DID */
+ if (!(vport->vpi_state & LPFC_VPI_REGISTERED) &&
+ (did != Fabric_DID)) {
+ /*
+ * Throw out the frame if we are not pt2pt.
+ * The pt2pt protocol allows for discovery frames
+ * to be received without a registered VPI.
+ */
+ if (!test_bit(FC_PT2PT, &vport->fc_flag) ||
+ phba->link_state == LPFC_HBA_READY) {
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+ }
+
/* Handle the basic abort sequence (BA_ABTS) event */
if (fc_hdr->fh_r_ctl == FC_RCTL_BA_ABTS) {
lpfc_sli4_handle_unsol_abort(vport, dmabuf);
@@ -13861,7 +19689,7 @@ lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba)
rc = lpfc_sli4_post_rpi_hdr(phba, rpi_page);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2008 Error %d posting all rpi "
"headers\n", rc);
rc = -EIO;
@@ -13907,7 +19735,7 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page)
/* The port is notified of the header region via a mailbox command. */
mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2001 Unable to allocate memory for issuing "
"SLI_CONFIG_SPECIAL mailbox command\n");
return -ENOMEM;
@@ -13934,14 +19762,21 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page)
shdr = (union lpfc_sli4_cfg_shdr *) &hdr_tmpl->header.cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
- if (rc != MBX_TIMEOUT)
- mempool_free(mboxq, phba->mbox_mem_pool);
+ mempool_free(mboxq, phba->mbox_mem_pool);
if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2514 POST_RPI_HDR mailbox failed with "
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
rc = -ENXIO;
+ } else {
+ /*
+ * The next_rpi stores the next logical module-64 rpi value used
+ * to post physical rpis in subsequent rpi postings.
+ */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli4_hba.next_rpi = rpi_page->next_rpi;
+ spin_unlock_irq(&phba->hbalock);
}
return rc;
}
@@ -13966,16 +19801,17 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
uint16_t max_rpi, rpi_limit;
uint16_t rpi_remaining, lrpi = 0;
struct lpfc_rpi_hdr *rpi_hdr;
-
- max_rpi = phba->sli4_hba.max_cfg_param.max_rpi;
- rpi_limit = phba->sli4_hba.next_rpi;
+ unsigned long iflag;
/*
* Fetch the next logical rpi. Because this index is logical,
* the driver starts at 0 each time.
*/
- spin_lock_irq(&phba->hbalock);
- rpi = find_next_zero_bit(phba->sli4_hba.rpi_bmask, rpi_limit, 0);
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ max_rpi = phba->sli4_hba.max_cfg_param.max_rpi;
+ rpi_limit = phba->sli4_hba.next_rpi;
+
+ rpi = find_first_zero_bit(phba->sli4_hba.rpi_bmask, rpi_limit);
if (rpi >= rpi_limit)
rpi = LPFC_RPI_ALLOC_ERROR;
else {
@@ -13983,6 +19819,10 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
phba->sli4_hba.max_cfg_param.rpi_used++;
phba->sli4_hba.rpi_count++;
}
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "0001 Allocated rpi:x%x max:x%x lim:x%x\n",
+ (int) rpi, max_rpi, rpi_limit);
/*
* Don't try to allocate more rpi header regions if the device limit
@@ -13990,7 +19830,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
*/
if ((rpi == LPFC_RPI_ALLOC_ERROR) &&
(phba->sli4_hba.rpi_count >= max_rpi)) {
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
return rpi;
}
@@ -13999,7 +19839,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
* extents.
*/
if (!phba->sli4_hba.rpi_hdrs_in_use) {
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
return rpi;
}
@@ -14010,11 +19850,11 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
* how many are supported max by the device.
*/
rpi_remaining = phba->sli4_hba.next_rpi - phba->sli4_hba.rpi_count;
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
if (rpi_remaining < LPFC_RPI_LOW_WATER_MARK) {
rpi_hdr = lpfc_sli4_create_rpi_hdr(phba);
if (!rpi_hdr) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2002 Error Could not grow rpi "
"count\n");
} else {
@@ -14028,24 +19868,38 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
}
/**
- * lpfc_sli4_free_rpi - Release an rpi for reuse.
+ * __lpfc_sli4_free_rpi - Release an rpi for reuse.
* @phba: pointer to lpfc hba data structure.
+ * @rpi: rpi to free
*
* This routine is invoked to release an rpi to the pool of
* available rpis maintained by the driver.
**/
-void
+static void
__lpfc_sli4_free_rpi(struct lpfc_hba *phba, int rpi)
{
+ /*
+ * if the rpi value indicates a prior unreg has already
+ * been done, skip the unreg.
+ */
+ if (rpi == LPFC_RPI_ALLOC_ERROR)
+ return;
+
if (test_and_clear_bit(rpi, phba->sli4_hba.rpi_bmask)) {
phba->sli4_hba.rpi_count--;
phba->sli4_hba.max_cfg_param.rpi_used--;
+ } else {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_NODE | LOG_DISCOVERY,
+ "2016 rpi %x not inuse\n",
+ rpi);
}
}
/**
* lpfc_sli4_free_rpi - Release an rpi for reuse.
* @phba: pointer to lpfc hba data structure.
+ * @rpi: rpi to free
*
* This routine is invoked to release an rpi to the pool of
* available rpis maintained by the driver.
@@ -14074,14 +19928,20 @@ lpfc_sli4_remove_rpis(struct lpfc_hba *phba)
}
/**
- * lpfc_sli4_resume_rpi - Remove the rpi bitmask region
- * @phba: pointer to lpfc hba data structure.
+ * lpfc_sli4_resume_rpi - Resume traffic relative to an RPI
+ * @ndlp: pointer to lpfc nodelist data structure.
+ * @cmpl: completion call-back.
+ * @iocbq: data to load as mbox ctx_u information
*
- * This routine is invoked to remove the memory region that
- * provided rpi via a bitmask.
+ * Return codes
+ * 0 - successful
+ * -ENOMEM - No available memory
+ * -EIO - The mailbox failed to complete successfully.
**/
int
-lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp)
+lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp,
+ void (*cmpl)(struct lpfc_hba *, LPFC_MBOXQ_t *),
+ struct lpfc_iocbq *iocbq)
{
LPFC_MBOXQ_t *mboxq;
struct lpfc_hba *phba = ndlp->phba;
@@ -14092,14 +19952,35 @@ lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp)
if (!mboxq)
return -ENOMEM;
- /* Post all rpi memory regions to the port. */
+ /* If cmpl assigned, then this nlp_get pairs with
+ * lpfc_mbx_cmpl_resume_rpi.
+ *
+ * Else cmpl is NULL, then this nlp_get pairs with
+ * lpfc_sli_def_mbox_cmpl.
+ */
+ if (!lpfc_nlp_get(ndlp)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2122 %s: Failed to get nlp ref\n",
+ __func__);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return -EIO;
+ }
+
lpfc_resume_rpi(mboxq, ndlp);
+ if (cmpl) {
+ mboxq->mbox_cmpl = cmpl;
+ mboxq->ctx_u.save_iocb = iocbq;
+ } else
+ mboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mboxq->ctx_ndlp = ndlp;
+ mboxq->vport = ndlp->vport;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2010 Resume RPI Mailbox failed "
"status %d, mbxStatus x%x\n", rc,
bf_get(lpfc_mqe_status, &mboxq->u.mqe));
+ lpfc_nlp_put(ndlp);
mempool_free(mboxq, phba->mbox_mem_pool);
return -EIO;
}
@@ -14128,10 +20009,10 @@ lpfc_sli4_init_vpi(struct lpfc_vport *vport)
if (!mboxq)
return -ENOMEM;
lpfc_init_vpi(phba, mboxq, vport->vpi);
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_INIT_VPI);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mboxq);
rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
if (rc != MBX_SUCCESS) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_SLI,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"2022 INIT VPI Mailbox failed "
"status %d, mbxStatus x%x\n", rc,
bf_get(lpfc_mqe_status, &mboxq->u.mqe));
@@ -14167,7 +20048,7 @@ lpfc_mbx_cmpl_add_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
if ((shdr_status || shdr_add_status) &&
(shdr_status != STATUS_FCF_IN_USE))
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2558 ADD_FCF_RECORD mailbox failed with "
"status x%x add_status x%x\n",
shdr_status, shdr_add_status);
@@ -14191,14 +20072,13 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
LPFC_MBOXQ_t *mboxq;
uint8_t *bytep;
void *virt_addr;
- dma_addr_t phys_addr;
struct lpfc_mbx_sge sge;
uint32_t alloc_len, req_len;
uint32_t fcfindex;
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2009 Failed to allocate mbox for ADD_FCF cmd\n");
return -ENOMEM;
}
@@ -14211,7 +20091,7 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
LPFC_MBOX_OPCODE_FCOE_ADD_FCF,
req_len, LPFC_SLI4_MBX_NEMBED);
if (alloc_len < req_len) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2523 Allocated DMA memory size (x%x) is "
"less than the requested DMA memory "
"size (x%x)\n", alloc_len, req_len);
@@ -14224,7 +20104,6 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
* routine only uses a single SGE.
*/
lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
- phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
virt_addr = mboxq->sge_array->addr[0];
/*
* Configure the FCF record for FCFI 0. This is the driver's
@@ -14245,7 +20124,7 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
mboxq->mbox_cmpl = lpfc_mbx_cmpl_add_fcf_record;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2515 ADD_FCF_RECORD mailbox failed with "
"status 0x%x\n", rc);
lpfc_sli4_mbox_cmd_free(phba, mboxq);
@@ -14315,9 +20194,10 @@ lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
LPFC_MBOXQ_t *mboxq;
phba->fcoe_eventtag_at_fcf_scan = phba->fcoe_eventtag;
+ phba->fcoe_cvl_eventtag_attn = phba->fcoe_cvl_eventtag;
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2000 Failed to allocate mbox for "
"READ_FCF cmd\n");
error = -ENOMEM;
@@ -14333,9 +20213,7 @@ lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
mboxq->vport = phba->pport;
mboxq->mbox_cmpl = lpfc_mbx_cmpl_fcf_scan_read_fcf_rec;
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag |= FCF_TS_INPROG;
- spin_unlock_irq(&phba->hbalock);
+ set_bit(FCF_TS_INPROG, &phba->hba_flag);
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED)
@@ -14351,9 +20229,7 @@ fail_fcf_scan:
if (mboxq)
lpfc_sli4_mbox_cmd_free(phba, mboxq);
/* FCF scan failed, clear FCF_TS_INPROG flag */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~FCF_TS_INPROG;
- spin_unlock_irq(&phba->hbalock);
+ clear_bit(FCF_TS_INPROG, &phba->hba_flag);
}
return error;
}
@@ -14451,6 +20327,99 @@ fail_fcf_read:
}
/**
+ * lpfc_check_next_fcf_pri_level
+ * @phba: pointer to the lpfc_hba struct for this port.
+ * This routine is called from the lpfc_sli4_fcf_rr_next_index_get
+ * routine when the rr_bmask is empty. The FCF indecies are put into the
+ * rr_bmask based on their priority level. Starting from the highest priority
+ * to the lowest. The most likely FCF candidate will be in the highest
+ * priority group. When this routine is called it searches the fcf_pri list for
+ * next lowest priority group and repopulates the rr_bmask with only those
+ * fcf_indexes.
+ * returns:
+ * 1=success 0=failure
+ **/
+static int
+lpfc_check_next_fcf_pri_level(struct lpfc_hba *phba)
+{
+ uint16_t next_fcf_pri;
+ uint16_t last_index;
+ struct lpfc_fcf_pri *fcf_pri;
+ int rc;
+ int ret = 0;
+
+ last_index = find_first_bit(phba->fcf.fcf_rr_bmask,
+ LPFC_SLI4_FCF_TBL_INDX_MAX);
+ lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+ "3060 Last IDX %d\n", last_index);
+
+ /* Verify the priority list has 2 or more entries */
+ spin_lock_irq(&phba->hbalock);
+ if (list_empty(&phba->fcf.fcf_pri_list) ||
+ list_is_singular(&phba->fcf.fcf_pri_list)) {
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ "3061 Last IDX %d\n", last_index);
+ return 0; /* Empty rr list */
+ }
+ spin_unlock_irq(&phba->hbalock);
+
+ next_fcf_pri = 0;
+ /*
+ * Clear the rr_bmask and set all of the bits that are at this
+ * priority.
+ */
+ memset(phba->fcf.fcf_rr_bmask, 0,
+ sizeof(*phba->fcf.fcf_rr_bmask));
+ spin_lock_irq(&phba->hbalock);
+ list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
+ if (fcf_pri->fcf_rec.flag & LPFC_FCF_FLOGI_FAILED)
+ continue;
+ /*
+ * the 1st priority that has not FLOGI failed
+ * will be the highest.
+ */
+ if (!next_fcf_pri)
+ next_fcf_pri = fcf_pri->fcf_rec.priority;
+ spin_unlock_irq(&phba->hbalock);
+ if (fcf_pri->fcf_rec.priority == next_fcf_pri) {
+ rc = lpfc_sli4_fcf_rr_index_set(phba,
+ fcf_pri->fcf_rec.fcf_index);
+ if (rc)
+ return 0;
+ }
+ spin_lock_irq(&phba->hbalock);
+ }
+ /*
+ * if next_fcf_pri was not set above and the list is not empty then
+ * we have failed flogis on all of them. So reset flogi failed
+ * and start at the beginning.
+ */
+ if (!next_fcf_pri && !list_empty(&phba->fcf.fcf_pri_list)) {
+ list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
+ fcf_pri->fcf_rec.flag &= ~LPFC_FCF_FLOGI_FAILED;
+ /*
+ * the 1st priority that has not FLOGI failed
+ * will be the highest.
+ */
+ if (!next_fcf_pri)
+ next_fcf_pri = fcf_pri->fcf_rec.priority;
+ spin_unlock_irq(&phba->hbalock);
+ if (fcf_pri->fcf_rec.priority == next_fcf_pri) {
+ rc = lpfc_sli4_fcf_rr_index_set(phba,
+ fcf_pri->fcf_rec.fcf_index);
+ if (rc)
+ return 0;
+ }
+ spin_lock_irq(&phba->hbalock);
+ }
+ } else
+ ret = 1;
+ spin_unlock_irq(&phba->hbalock);
+
+ return ret;
+}
+/**
* lpfc_sli4_fcf_rr_next_index_get - Get next eligible fcf record index
* @phba: pointer to lpfc hba data structure.
*
@@ -14465,25 +20434,55 @@ lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba)
{
uint16_t next_fcf_index;
+initial_priority:
/* Search start from next bit of currently registered FCF index */
- next_fcf_index = (phba->fcf.current_rec.fcf_indx + 1) %
- LPFC_SLI4_FCF_TBL_INDX_MAX;
+ next_fcf_index = phba->fcf.current_rec.fcf_indx;
+
+next_priority:
+ /* Determine the next fcf index to check */
+ next_fcf_index = (next_fcf_index + 1) % LPFC_SLI4_FCF_TBL_INDX_MAX;
next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask,
LPFC_SLI4_FCF_TBL_INDX_MAX,
next_fcf_index);
/* Wrap around condition on phba->fcf.fcf_rr_bmask */
- if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX)
- next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask,
- LPFC_SLI4_FCF_TBL_INDX_MAX, 0);
+ if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+ /*
+ * If we have wrapped then we need to clear the bits that
+ * have been tested so that we can detect when we should
+ * change the priority level.
+ */
+ next_fcf_index = find_first_bit(phba->fcf.fcf_rr_bmask,
+ LPFC_SLI4_FCF_TBL_INDX_MAX);
+ }
+
/* Check roundrobin failover list empty condition */
- if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+ if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX ||
+ next_fcf_index == phba->fcf.current_rec.fcf_indx) {
+ /*
+ * If next fcf index is not found check if there are lower
+ * Priority level fcf's in the fcf_priority list.
+ * Set up the rr_bmask with all of the avaiable fcf bits
+ * at that level and continue the selection process.
+ */
+ if (lpfc_check_next_fcf_pri_level(phba))
+ goto initial_priority;
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP,
"2844 No roundrobin failover FCF available\n");
+
return LPFC_FCOE_FCF_NEXT_NONE;
}
+ if (next_fcf_index < LPFC_SLI4_FCF_TBL_INDX_MAX &&
+ phba->fcf.fcf_pri[next_fcf_index].fcf_rec.flag &
+ LPFC_FCF_FLOGI_FAILED) {
+ if (list_is_singular(&phba->fcf.fcf_pri_list))
+ return LPFC_FCOE_FCF_NEXT_NONE;
+
+ goto next_priority;
+ }
+
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2845 Get next roundrobin failover FCF (x%x)\n",
next_fcf_index);
@@ -14494,6 +20493,7 @@ lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba)
/**
* lpfc_sli4_fcf_rr_index_set - Set bmask with eligible fcf record index
* @phba: pointer to lpfc hba data structure.
+ * @fcf_index: index into the FCF table to 'set'
*
* This routine sets the FCF record index in to the eligible bmask for
* roundrobin failover search. It checks to make sure that the index
@@ -14526,6 +20526,7 @@ lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *phba, uint16_t fcf_index)
/**
* lpfc_sli4_fcf_rr_index_clear - Clear bmask from eligible fcf record index
* @phba: pointer to lpfc hba data structure.
+ * @fcf_index: index into the FCF table to 'clear'
*
* This routine clears the FCF record index from the eligible bmask for
* roundrobin failover search. It checks to make sure that the index
@@ -14535,6 +20536,7 @@ lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *phba, uint16_t fcf_index)
void
lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index)
{
+ struct lpfc_fcf_pri *fcf_pri, *fcf_pri_next;
if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
"2762 FCF (x%x) reached driver's book "
@@ -14543,6 +20545,15 @@ lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index)
return;
}
/* Clear the eligible FCF record index bmask */
+ spin_lock_irq(&phba->hbalock);
+ list_for_each_entry_safe(fcf_pri, fcf_pri_next, &phba->fcf.fcf_pri_list,
+ list) {
+ if (fcf_pri->fcf_rec.fcf_index == fcf_index) {
+ list_del_init(&fcf_pri->list);
+ break;
+ }
+ }
+ spin_unlock_irq(&phba->hbalock);
clear_bit(fcf_index, phba->fcf.fcf_rr_bmask);
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
@@ -14553,12 +20564,13 @@ lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index)
/**
* lpfc_mbx_cmpl_redisc_fcf_table - completion routine for rediscover FCF table
* @phba: pointer to lpfc hba data structure.
+ * @mbox: An allocated pointer to type LPFC_MBOXQ_t
*
* This routine is the completion routine for the rediscover FCF table mailbox
* command. If the mailbox command returned failure, it will try to stop the
* FCF rediscover wait timer.
**/
-void
+static void
lpfc_mbx_cmpl_redisc_fcf_table(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
{
struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf;
@@ -14627,7 +20639,7 @@ lpfc_sli4_redisc_fcf_table(struct lpfc_hba *phba)
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2745 Failed to allocate mbox for "
"requesting FCF rediscover.\n");
return -ENOMEM;
@@ -14681,45 +20693,42 @@ lpfc_sli4_fcf_dead_failthrough(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_read_link_ste - Read region 23 to decide if link is disabled.
+ * lpfc_sli_get_config_region23 - Get sli3 port region 23 data.
* @phba: pointer to lpfc hba data structure.
+ * @rgn23_data: pointer to configure region 23 data.
*
- * This function read region 23 and parse TLV for port status to
- * decide if the user disaled the port. If the TLV indicates the
- * port is disabled, the hba_flag is set accordingly.
+ * This function gets SLI3 port configure region 23 data through memory dump
+ * mailbox command. When it successfully retrieves data, the size of the data
+ * will be returned, otherwise, 0 will be returned.
**/
-void
-lpfc_sli_read_link_ste(struct lpfc_hba *phba)
+static uint32_t
+lpfc_sli_get_config_region23(struct lpfc_hba *phba, char *rgn23_data)
{
LPFC_MBOXQ_t *pmb = NULL;
MAILBOX_t *mb;
- uint8_t *rgn23_data = NULL;
- uint32_t offset = 0, data_size, sub_tlv_len, tlv_offset;
+ uint32_t offset = 0;
int rc;
+ if (!rgn23_data)
+ return 0;
+
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2600 lpfc_sli_read_serdes_param failed to"
- " allocate mailbox memory\n");
- goto out;
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2600 failed to allocate mailbox memory\n");
+ return 0;
}
mb = &pmb->u.mb;
- /* Get adapter Region 23 data */
- rgn23_data = kzalloc(DMP_RGN23_SIZE, GFP_KERNEL);
- if (!rgn23_data)
- goto out;
-
do {
lpfc_dump_mem(phba, pmb, offset, DMP_REGION_23);
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2601 lpfc_sli_read_link_ste failed to"
- " read config region 23 rc 0x%x Status 0x%x\n",
- rc, mb->mbxStatus);
+ "2601 failed to read config "
+ "region 23, rc 0x%x Status 0x%x\n",
+ rc, mb->mbxStatus);
mb->un.varDmp.word_cnt = 0;
}
/*
@@ -14728,24 +20737,104 @@ lpfc_sli_read_link_ste(struct lpfc_hba *phba)
*/
if (mb->un.varDmp.word_cnt == 0)
break;
+
if (mb->un.varDmp.word_cnt > DMP_RGN23_SIZE - offset)
mb->un.varDmp.word_cnt = DMP_RGN23_SIZE - offset;
lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
- rgn23_data + offset,
- mb->un.varDmp.word_cnt);
+ rgn23_data + offset,
+ mb->un.varDmp.word_cnt);
offset += mb->un.varDmp.word_cnt;
} while (mb->un.varDmp.word_cnt && offset < DMP_RGN23_SIZE);
- data_size = offset;
- offset = 0;
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return offset;
+}
+
+/**
+ * lpfc_sli4_get_config_region23 - Get sli4 port region 23 data.
+ * @phba: pointer to lpfc hba data structure.
+ * @rgn23_data: pointer to configure region 23 data.
+ *
+ * This function gets SLI4 port configure region 23 data through memory dump
+ * mailbox command. When it successfully retrieves data, the size of the data
+ * will be returned, otherwise, 0 will be returned.
+ **/
+static uint32_t
+lpfc_sli4_get_config_region23(struct lpfc_hba *phba, char *rgn23_data)
+{
+ LPFC_MBOXQ_t *mboxq = NULL;
+ struct lpfc_dmabuf *mp = NULL;
+ struct lpfc_mqe *mqe;
+ uint32_t data_length = 0;
+ int rc;
+
+ if (!rgn23_data)
+ return 0;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3105 failed to allocate mailbox memory\n");
+ return 0;
+ }
+
+ if (lpfc_sli4_dump_cfg_rg23(phba, mboxq))
+ goto out;
+ mqe = &mboxq->u.mqe;
+ mp = mboxq->ctx_buf;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ if (rc)
+ goto out;
+ data_length = mqe->un.mb_words[5];
+ if (data_length == 0)
+ goto out;
+ if (data_length > DMP_RGN23_SIZE) {
+ data_length = 0;
+ goto out;
+ }
+ lpfc_sli_pcimem_bcopy((char *)mp->virt, rgn23_data, data_length);
+out:
+ lpfc_mbox_rsrc_cleanup(phba, mboxq, MBOX_THD_UNLOCKED);
+ return data_length;
+}
+
+/**
+ * lpfc_sli_read_link_ste - Read region 23 to decide if link is disabled.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function read region 23 and parse TLV for port status to
+ * decide if the user disaled the port. If the TLV indicates the
+ * port is disabled, the hba_flag is set accordingly.
+ **/
+void
+lpfc_sli_read_link_ste(struct lpfc_hba *phba)
+{
+ uint8_t *rgn23_data = NULL;
+ uint32_t if_type, data_size, sub_tlv_len, tlv_offset;
+ uint32_t offset = 0;
+
+ /* Get adapter Region 23 data */
+ rgn23_data = kzalloc(DMP_RGN23_SIZE, GFP_KERNEL);
+ if (!rgn23_data)
+ goto out;
+
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ data_size = lpfc_sli_get_config_region23(phba, rgn23_data);
+ else {
+ if_type = bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf);
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0)
+ goto out;
+ data_size = lpfc_sli4_get_config_region23(phba, rgn23_data);
+ }
if (!data_size)
goto out;
/* Check the region signature first */
if (memcmp(&rgn23_data[offset], LPFC_REGION23_SIGNATURE, 4)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2619 Config region 23 has bad signature\n");
goto out;
}
@@ -14753,7 +20842,7 @@ lpfc_sli_read_link_ste(struct lpfc_hba *phba)
/* Check the data structure version */
if (rgn23_data[offset] != LPFC_REGION23_VERSION) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2620 Config region 23 has bad version\n");
goto out;
}
@@ -14797,19 +20886,103 @@ lpfc_sli_read_link_ste(struct lpfc_hba *phba)
/* This HBA contains PORT_STE configured */
if (!rgn23_data[offset + 2])
- phba->hba_flag |= LINK_DISABLED;
+ set_bit(LINK_DISABLED, &phba->hba_flag);
goto out;
}
}
+
out:
- if (pmb)
- mempool_free(pmb, phba->mbox_mem_pool);
kfree(rgn23_data);
return;
}
/**
+ * lpfc_log_fw_write_cmpl - logs firmware write completion status
+ * @phba: pointer to lpfc hba data structure
+ * @shdr_status: wr_object rsp's status field
+ * @shdr_add_status: wr_object rsp's add_status field
+ * @shdr_add_status_2: wr_object rsp's add_status_2 field
+ * @shdr_change_status: wr_object rsp's change_status field
+ * @shdr_csf: wr_object rsp's csf bit
+ *
+ * This routine is intended to be called after a firmware write completes.
+ * It will log next action items to be performed by the user to instantiate
+ * the newly downloaded firmware or reason for incompatibility.
+ **/
+static void
+lpfc_log_fw_write_cmpl(struct lpfc_hba *phba, u32 shdr_status,
+ u32 shdr_add_status, u32 shdr_add_status_2,
+ u32 shdr_change_status, u32 shdr_csf)
+{
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+ "4198 %s: flash_id x%02x, asic_rev x%02x, "
+ "status x%02x, add_status x%02x, add_status_2 x%02x, "
+ "change_status x%02x, csf %01x\n", __func__,
+ phba->sli4_hba.flash_id, phba->sli4_hba.asic_rev,
+ shdr_status, shdr_add_status, shdr_add_status_2,
+ shdr_change_status, shdr_csf);
+
+ if (shdr_add_status == LPFC_ADD_STATUS_INCOMPAT_OBJ) {
+ switch (shdr_add_status_2) {
+ case LPFC_ADD_STATUS_2_INCOMPAT_FLASH:
+ lpfc_log_msg(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+ "4199 Firmware write failed: "
+ "image incompatible with flash x%02x\n",
+ phba->sli4_hba.flash_id);
+ break;
+ case LPFC_ADD_STATUS_2_INCORRECT_ASIC:
+ lpfc_log_msg(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+ "4200 Firmware write failed: "
+ "image incompatible with ASIC "
+ "architecture x%02x\n",
+ phba->sli4_hba.asic_rev);
+ break;
+ default:
+ lpfc_log_msg(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+ "4210 Firmware write failed: "
+ "add_status_2 x%02x\n",
+ shdr_add_status_2);
+ break;
+ }
+ } else if (!shdr_status && !shdr_add_status) {
+ if (shdr_change_status == LPFC_CHANGE_STATUS_FW_RESET ||
+ shdr_change_status == LPFC_CHANGE_STATUS_PORT_MIGRATION) {
+ if (shdr_csf)
+ shdr_change_status =
+ LPFC_CHANGE_STATUS_PCI_RESET;
+ }
+
+ switch (shdr_change_status) {
+ case (LPFC_CHANGE_STATUS_PHYS_DEV_RESET):
+ lpfc_log_msg(phba, KERN_NOTICE, LOG_MBOX | LOG_SLI,
+ "3198 Firmware write complete: System "
+ "reboot required to instantiate\n");
+ break;
+ case (LPFC_CHANGE_STATUS_FW_RESET):
+ lpfc_log_msg(phba, KERN_NOTICE, LOG_MBOX | LOG_SLI,
+ "3199 Firmware write complete: "
+ "Firmware reset required to "
+ "instantiate\n");
+ break;
+ case (LPFC_CHANGE_STATUS_PORT_MIGRATION):
+ lpfc_log_msg(phba, KERN_NOTICE, LOG_MBOX | LOG_SLI,
+ "3200 Firmware write complete: Port "
+ "Migration or PCI Reset required to "
+ "instantiate\n");
+ break;
+ case (LPFC_CHANGE_STATUS_PCI_RESET):
+ lpfc_log_msg(phba, KERN_NOTICE, LOG_MBOX | LOG_SLI,
+ "3201 Firmware write complete: PCI "
+ "Reset required to instantiate\n");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/**
* lpfc_wr_object - write an object to the firmware
* @phba: HBA structure that indicates port to create a queue on.
* @dmabuf_list: list of dmabufs to write to the port.
@@ -14824,7 +20997,7 @@ out:
* the offset after the write object mailbox has completed. @size is used to
* determine the end of the object and whether the eof bit should be set.
*
- * Return 0 is successful and offset will contain the the new offset to use
+ * Return 0 is successful and offset will contain the new offset to use
* for the next write.
* Return negative value for error cases.
**/
@@ -14835,11 +21008,13 @@ lpfc_wr_object(struct lpfc_hba *phba, struct list_head *dmabuf_list,
struct lpfc_mbx_wr_object *wr_object;
LPFC_MBOXQ_t *mbox;
int rc = 0, i = 0;
- uint32_t shdr_status, shdr_add_status;
+ int mbox_status = 0;
+ uint32_t shdr_status, shdr_add_status, shdr_add_status_2;
+ uint32_t shdr_change_status = 0, shdr_csf = 0;
uint32_t mbox_tmo;
- union lpfc_sli4_cfg_shdr *shdr;
struct lpfc_dmabuf *dmabuf;
uint32_t written = 0;
+ bool check_change_status = false;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
@@ -14867,6 +21042,8 @@ lpfc_wr_object(struct lpfc_hba *phba, struct list_head *dmabuf_list,
(size - written);
written += (size - written);
bf_set(lpfc_wr_object_eof, &wr_object->u.request, 1);
+ bf_set(lpfc_wr_object_eas, &wr_object->u.request, 1);
+ check_change_status = true;
} else {
wr_object->u.request.bde[i].tus.f.bdeSize =
SLI4_PAGE_SIZE;
@@ -14877,25 +21054,52 @@ lpfc_wr_object(struct lpfc_hba *phba, struct list_head *dmabuf_list,
wr_object->u.request.bde_count = i;
bf_set(lpfc_wr_object_write_length, &wr_object->u.request, written);
if (!phba->sli4_hba.intr_enable)
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ mbox_status = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
else {
- mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
- rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+ mbox_tmo = lpfc_mbox_tmo_val(phba, mbox);
+ mbox_status = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
}
+
+ /* The mbox status needs to be maintained to detect MBOX_TIMEOUT. */
+ rc = mbox_status;
+
/* The IOCTL status is embedded in the mailbox subheader. */
- shdr = (union lpfc_sli4_cfg_shdr *) &wr_object->header.cfg_shdr;
- shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
- shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, phba->mbox_mem_pool);
- if (shdr_status || shdr_add_status || rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ shdr_status = bf_get(lpfc_mbox_hdr_status,
+ &wr_object->header.cfg_shdr.response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &wr_object->header.cfg_shdr.response);
+ shdr_add_status_2 = bf_get(lpfc_mbox_hdr_add_status_2,
+ &wr_object->header.cfg_shdr.response);
+ if (check_change_status) {
+ shdr_change_status = bf_get(lpfc_wr_object_change_status,
+ &wr_object->u.response);
+ shdr_csf = bf_get(lpfc_wr_object_csf,
+ &wr_object->u.response);
+ }
+
+ if (shdr_status || shdr_add_status || shdr_add_status_2 || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"3025 Write Object mailbox failed with "
- "status x%x add_status x%x, mbx status x%x\n",
- shdr_status, shdr_add_status, rc);
+ "status x%x add_status x%x, add_status_2 x%x, "
+ "mbx status x%x\n",
+ shdr_status, shdr_add_status, shdr_add_status_2,
+ rc);
rc = -ENXIO;
- } else
+ *offset = shdr_add_status;
+ } else {
*offset += wr_object->u.response.actual_write_length;
+ }
+
+ if (rc || check_change_status)
+ lpfc_log_fw_write_cmpl(phba, shdr_status, shdr_add_status,
+ shdr_add_status_2, shdr_change_status,
+ shdr_csf);
+
+ if (!phba->sli4_hba.intr_enable)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ else if (mbox_status != MBX_TIMEOUT)
+ mempool_free(mbox, phba->mbox_mem_pool);
+
return rc;
}
@@ -14913,10 +21117,8 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *mb, *nextmb;
- struct lpfc_dmabuf *mp;
struct lpfc_nodelist *ndlp;
struct lpfc_nodelist *act_mbx_ndlp = NULL;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
LIST_HEAD(mbox_cmd_list);
uint8_t restart_loop;
@@ -14930,8 +21132,7 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport)
(mb->u.mb.mbxCommand != MBX_REG_VPI))
continue;
- list_del(&mb->list);
- list_add_tail(&mb->list, &mbox_cmd_list);
+ list_move_tail(&mb->list, &mbox_cmd_list);
}
/* Clean up active mailbox command with the vport */
mb = phba->sli.mbox_active;
@@ -14940,9 +21141,13 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport)
(mb->u.mb.mbxCommand == MBX_REG_VPI))
mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
if (mb->u.mb.mbxCommand == MBX_REG_LOGIN64) {
- act_mbx_ndlp = (struct lpfc_nodelist *)mb->context2;
- /* Put reference count for delayed processing */
+ act_mbx_ndlp = mb->ctx_ndlp;
+
+ /* This reference is local to this routine. The
+ * reference is removed at routine exit.
+ */
act_mbx_ndlp = lpfc_nlp_get(act_mbx_ndlp);
+
/* Unregister the RPI when mailbox complete */
mb->mbox_flag |= LPFC_MBX_IMED_UNREG;
}
@@ -14965,15 +21170,11 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport)
mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
if (mb->u.mb.mbxCommand == MBX_REG_LOGIN64) {
- ndlp = (struct lpfc_nodelist *)mb->context2;
+ ndlp = mb->ctx_ndlp;
/* Unregister the RPI when mailbox complete */
mb->mbox_flag |= LPFC_MBX_IMED_UNREG;
restart_loop = 1;
- spin_unlock_irq(&phba->hbalock);
- spin_lock(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL;
- spin_unlock(shost->host_lock);
- spin_lock_irq(&phba->hbalock);
+ clear_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag);
break;
}
}
@@ -14985,28 +21186,19 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport)
while (!list_empty(&mbox_cmd_list)) {
list_remove_head(&mbox_cmd_list, mb, LPFC_MBOXQ_t, list);
if (mb->u.mb.mbxCommand == MBX_REG_LOGIN64) {
- mp = (struct lpfc_dmabuf *) (mb->context1);
- if (mp) {
- __lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- }
- ndlp = (struct lpfc_nodelist *) mb->context2;
- mb->context2 = NULL;
+ ndlp = mb->ctx_ndlp;
+ mb->ctx_ndlp = NULL;
if (ndlp) {
- spin_lock(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL;
- spin_unlock(shost->host_lock);
+ clear_bit(NLP_IGNR_REG_CMPL, &ndlp->nlp_flag);
lpfc_nlp_put(ndlp);
}
}
- mempool_free(mb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, mb, MBOX_THD_UNLOCKED);
}
/* Release the ndlp with the cleaned-up active mailbox command */
if (act_mbx_ndlp) {
- spin_lock(shost->host_lock);
- act_mbx_ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL;
- spin_unlock(shost->host_lock);
+ clear_bit(NLP_IGNR_REG_CMPL, &act_mbx_ndlp->nlp_flag);
lpfc_nlp_put(act_mbx_ndlp);
}
}
@@ -15026,72 +21218,1638 @@ uint32_t
lpfc_drain_txq(struct lpfc_hba *phba)
{
LIST_HEAD(completions);
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
- struct lpfc_iocbq *piocbq = 0;
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *piocbq = NULL;
unsigned long iflags = 0;
char *fail_msg = NULL;
- struct lpfc_sglq *sglq;
- union lpfc_wqe wqe;
+ uint32_t txq_cnt = 0;
+ struct lpfc_queue *wq;
+ int ret = 0;
+
+ if (phba->link_flag & LS_MDS_LOOPBACK) {
+ /* MDS WQE are posted only to first WQ*/
+ wq = phba->sli4_hba.hdwq[0].io_wq;
+ if (unlikely(!wq))
+ return 0;
+ pring = wq->pring;
+ } else {
+ wq = phba->sli4_hba.els_wq;
+ if (unlikely(!wq))
+ return 0;
+ pring = lpfc_phba_elsring(phba);
+ }
- spin_lock_irqsave(&phba->hbalock, iflags);
- if (pring->txq_cnt > pring->txq_max)
- pring->txq_max = pring->txq_cnt;
+ if (unlikely(!pring) || list_empty(&pring->txq))
+ return 0;
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ list_for_each_entry(piocbq, &pring->txq, list) {
+ txq_cnt++;
+ }
- while (pring->txq_cnt) {
- spin_lock_irqsave(&phba->hbalock, iflags);
+ if (txq_cnt > pring->txq_max)
+ pring->txq_max = txq_cnt;
+
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+ while (!list_empty(&pring->txq)) {
+ spin_lock_irqsave(&pring->ring_lock, iflags);
piocbq = lpfc_sli_ringtx_get(phba, pring);
- sglq = __lpfc_sli_get_sglq(phba, piocbq);
- if (!sglq) {
- __lpfc_sli_ringtx_put(phba, pring, piocbq);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ if (!piocbq) {
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "2823 txq empty and txq_cnt is %d\n",
+ txq_cnt);
break;
- } else {
- if (!piocbq) {
- /* The txq_cnt out of sync. This should
- * never happen
- */
- sglq = __lpfc_clear_active_sglq(phba,
- sglq->sli4_lxritag);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2823 txq empty and txq_cnt is %d\n ",
- pring->txq_cnt);
- break;
- }
}
+ txq_cnt--;
- /* The xri and iocb resources secured,
- * attempt to issue request
- */
- piocbq->sli4_lxritag = sglq->sli4_lxritag;
- piocbq->sli4_xritag = sglq->sli4_xritag;
- if (NO_XRI == lpfc_sli4_bpl2sgl(phba, piocbq, sglq))
- fail_msg = "to convert bpl to sgl";
- else if (lpfc_sli4_iocb2wqe(phba, piocbq, &wqe))
- fail_msg = "to convert iocb to wqe";
- else if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
- fail_msg = " - Wq is full";
- else
- lpfc_sli_ringtxcmpl_put(phba, pring, piocbq);
+ ret = __lpfc_sli_issue_iocb(phba, pring->ringno, piocbq, 0);
+ if (ret && ret != IOCB_BUSY) {
+ fail_msg = " - Cannot send IO ";
+ piocbq->cmd_flag &= ~LPFC_DRIVER_ABORTED;
+ }
if (fail_msg) {
+ piocbq->cmd_flag |= LPFC_DRIVER_ABORTED;
/* Failed means we can't issue and need to cancel */
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"2822 IOCB failed %s iotag 0x%x "
- "xri 0x%x\n",
- fail_msg,
- piocbq->iotag, piocbq->sli4_xritag);
+ "xri 0x%x %d flg x%x\n",
+ fail_msg, piocbq->iotag,
+ piocbq->sli4_xritag, ret,
+ piocbq->cmd_flag);
list_add_tail(&piocbq->list, &completions);
+ fail_msg = NULL;
}
- spin_unlock_irqrestore(&phba->hbalock, iflags);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ if (txq_cnt == 0 || ret == IOCB_BUSY)
+ break;
}
-
/* Cancel all the IOCBs that cannot be issued */
lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
- IOERR_SLI_ABORTED);
+ IOERR_SLI_ABORTED);
+
+ return txq_cnt;
+}
+
+/**
+ * lpfc_wqe_bpl2sgl - Convert the bpl/bde to a sgl.
+ * @phba: Pointer to HBA context object.
+ * @pwqeq: Pointer to command WQE.
+ * @sglq: Pointer to the scatter gather queue object.
+ *
+ * This routine converts the bpl or bde that is in the WQE
+ * to a sgl list for the sli4 hardware. The physical address
+ * of the bpl/bde is converted back to a virtual address.
+ * If the WQE contains a BPL then the list of BDE's is
+ * converted to sli4_sge's. If the WQE contains a single
+ * BDE then it is converted to a single sli_sge.
+ * The WQE is still in cpu endianness so the contents of
+ * the bpl can be used without byte swapping.
+ *
+ * Returns valid XRI = Success, NO_XRI = Failure.
+ */
+static uint16_t
+lpfc_wqe_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeq,
+ struct lpfc_sglq *sglq)
+{
+ uint16_t xritag = NO_XRI;
+ struct ulp_bde64 *bpl = NULL;
+ struct ulp_bde64 bde;
+ struct sli4_sge *sgl = NULL;
+ struct lpfc_dmabuf *dmabuf;
+ union lpfc_wqe128 *wqe;
+ int numBdes = 0;
+ int i = 0;
+ uint32_t offset = 0; /* accumulated offset in the sg request list */
+ int inbound = 0; /* number of sg reply entries inbound from firmware */
+ uint32_t cmd;
+
+ if (!pwqeq || !sglq)
+ return xritag;
+
+ sgl = (struct sli4_sge *)sglq->sgl;
+ wqe = &pwqeq->wqe;
+ pwqeq->iocb.ulpIoTag = pwqeq->iotag;
+
+ cmd = bf_get(wqe_cmnd, &wqe->generic.wqe_com);
+ if (cmd == CMD_XMIT_BLS_RSP64_WQE)
+ return sglq->sli4_xritag;
+ numBdes = pwqeq->num_bdes;
+ if (numBdes) {
+ /* The addrHigh and addrLow fields within the WQE
+ * have not been byteswapped yet so there is no
+ * need to swap them back.
+ */
+ if (pwqeq->bpl_dmabuf)
+ dmabuf = pwqeq->bpl_dmabuf;
+ else
+ return xritag;
+
+ bpl = (struct ulp_bde64 *)dmabuf->virt;
+ if (!bpl)
+ return xritag;
+
+ for (i = 0; i < numBdes; i++) {
+ /* Should already be byte swapped. */
+ sgl->addr_hi = bpl->addrHigh;
+ sgl->addr_lo = bpl->addrLow;
+
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ if ((i+1) == numBdes)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ else
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ /* swap the size field back to the cpu so we
+ * can assign it to the sgl.
+ */
+ bde.tus.w = le32_to_cpu(bpl->tus.w);
+ sgl->sge_len = cpu_to_le32(bde.tus.f.bdeSize);
+ /* The offsets in the sgl need to be accumulated
+ * separately for the request and reply lists.
+ * The request is always first, the reply follows.
+ */
+ switch (cmd) {
+ case CMD_GEN_REQUEST64_WQE:
+ /* add up the reply sg entries */
+ if (bpl->tus.f.bdeFlags == BUFF_TYPE_BDE_64I)
+ inbound++;
+ /* first inbound? reset the offset */
+ if (inbound == 1)
+ offset = 0;
+ bf_set(lpfc_sli4_sge_offset, sgl, offset);
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_DATA);
+ offset += bde.tus.f.bdeSize;
+ break;
+ case CMD_FCP_TRSP64_WQE:
+ bf_set(lpfc_sli4_sge_offset, sgl, 0);
+ bf_set(lpfc_sli4_sge_type, sgl,
+ LPFC_SGE_TYPE_DATA);
+ break;
+ case CMD_FCP_TSEND64_WQE:
+ case CMD_FCP_TRECEIVE64_WQE:
+ bf_set(lpfc_sli4_sge_type, sgl,
+ bpl->tus.f.bdeFlags);
+ if (i < 3)
+ offset = 0;
+ else
+ offset += bde.tus.f.bdeSize;
+ bf_set(lpfc_sli4_sge_offset, sgl, offset);
+ break;
+ }
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ bpl++;
+ sgl++;
+ }
+ } else if (wqe->gen_req.bde.tus.f.bdeFlags == BUFF_TYPE_BDE_64) {
+ /* The addrHigh and addrLow fields of the BDE have not
+ * been byteswapped yet so they need to be swapped
+ * before putting them in the sgl.
+ */
+ sgl->addr_hi = cpu_to_le32(wqe->gen_req.bde.addrHigh);
+ sgl->addr_lo = cpu_to_le32(wqe->gen_req.bde.addrLow);
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+ sgl->sge_len = cpu_to_le32(wqe->gen_req.bde.tus.f.bdeSize);
+ }
+ return sglq->sli4_xritag;
+}
+
+/**
+ * lpfc_sli4_issue_wqe - Issue an SLI4 Work Queue Entry (WQE)
+ * @phba: Pointer to HBA context object.
+ * @qp: Pointer to HDW queue.
+ * @pwqe: Pointer to command WQE.
+ **/
+int
+lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp,
+ struct lpfc_iocbq *pwqe)
+{
+ union lpfc_wqe128 *wqe = &pwqe->wqe;
+ struct lpfc_async_xchg_ctx *ctxp;
+ struct lpfc_queue *wq;
+ struct lpfc_sglq *sglq;
+ struct lpfc_sli_ring *pring;
+ unsigned long iflags;
+ int ret = 0;
+
+ /* NVME_LS and NVME_LS ABTS requests. */
+ if (pwqe->cmd_flag & LPFC_IO_NVME_LS) {
+ pring = phba->sli4_hba.nvmels_wq->pring;
+ lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags,
+ qp, wq_access);
+ sglq = __lpfc_sli_get_els_sglq(phba, pwqe);
+ if (!sglq) {
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ return WQE_BUSY;
+ }
+ pwqe->sli4_lxritag = sglq->sli4_lxritag;
+ pwqe->sli4_xritag = sglq->sli4_xritag;
+ if (lpfc_wqe_bpl2sgl(phba, pwqe, sglq) == NO_XRI) {
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ return WQE_ERROR;
+ }
+ bf_set(wqe_xri_tag, &pwqe->wqe.xmit_bls_rsp.wqe_com,
+ pwqe->sli4_xritag);
+ ret = lpfc_sli4_wq_put(phba->sli4_hba.nvmels_wq, wqe);
+ if (ret) {
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ return ret;
+ }
+
+ lpfc_sli_ringtxcmpl_put(phba, pring, pwqe);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+ lpfc_sli4_poll_eq(qp->hba_eq);
+ return 0;
+ }
+
+ /* NVME_FCREQ and NVME_ABTS requests */
+ if (pwqe->cmd_flag & (LPFC_IO_NVME | LPFC_IO_FCP | LPFC_IO_CMF)) {
+ /* Get the IO distribution (hba_wqidx) for WQ assignment. */
+ wq = qp->io_wq;
+ pring = wq->pring;
+
+ bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->io_cq_map);
+
+ lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags,
+ qp, wq_access);
+ ret = lpfc_sli4_wq_put(wq, wqe);
+ if (ret) {
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ return ret;
+ }
+ lpfc_sli_ringtxcmpl_put(phba, pring, pwqe);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+ lpfc_sli4_poll_eq(qp->hba_eq);
+ return 0;
+ }
+
+ /* NVMET requests */
+ if (pwqe->cmd_flag & LPFC_IO_NVMET) {
+ /* Get the IO distribution (hba_wqidx) for WQ assignment. */
+ wq = qp->io_wq;
+ pring = wq->pring;
+
+ ctxp = pwqe->context_un.axchg;
+ sglq = ctxp->ctxbuf->sglq;
+ if (pwqe->sli4_xritag == NO_XRI) {
+ pwqe->sli4_lxritag = sglq->sli4_lxritag;
+ pwqe->sli4_xritag = sglq->sli4_xritag;
+ }
+ bf_set(wqe_xri_tag, &pwqe->wqe.xmit_bls_rsp.wqe_com,
+ pwqe->sli4_xritag);
+ bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->io_cq_map);
+
+ lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags,
+ qp, wq_access);
+ ret = lpfc_sli4_wq_put(wq, wqe);
+ if (ret) {
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ return ret;
+ }
+ lpfc_sli_ringtxcmpl_put(phba, pring, pwqe);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+ lpfc_sli4_poll_eq(qp->hba_eq);
+ return 0;
+ }
+ return WQE_ERROR;
+}
+
+/**
+ * lpfc_sli4_issue_abort_iotag - SLI-4 WQE init & issue for the Abort
+ * @phba: Pointer to HBA context object.
+ * @cmdiocb: Pointer to driver command iocb object.
+ * @cmpl: completion function.
+ *
+ * Fill the appropriate fields for the abort WQE and call
+ * internal routine lpfc_sli4_issue_wqe to send the WQE
+ * This function is called with hbalock held and no ring_lock held.
+ *
+ * RETURNS 0 - SUCCESS
+ **/
+
+int
+lpfc_sli4_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ void *cmpl)
+{
+ struct lpfc_vport *vport = cmdiocb->vport;
+ struct lpfc_iocbq *abtsiocb = NULL;
+ union lpfc_wqe128 *abtswqe;
+ struct lpfc_io_buf *lpfc_cmd;
+ int retval = IOCB_ERROR;
+ u16 xritag = cmdiocb->sli4_xritag;
+
+ /*
+ * The scsi command can not be in txq and it is in flight because the
+ * pCmd is still pointing at the SCSI command we have to abort. There
+ * is no need to search the txcmplq. Just send an abort to the FW.
+ */
+
+ abtsiocb = __lpfc_sli_get_iocbq(phba);
+ if (!abtsiocb)
+ return WQE_NORESOURCE;
+
+ /* Indicate the IO is being aborted by the driver. */
+ cmdiocb->cmd_flag |= LPFC_DRIVER_ABORTED;
+
+ abtswqe = &abtsiocb->wqe;
+ memset(abtswqe, 0, sizeof(*abtswqe));
+
+ if (!lpfc_is_link_up(phba) || (phba->link_flag & LS_EXTERNAL_LOOPBACK))
+ bf_set(abort_cmd_ia, &abtswqe->abort_cmd, 1);
+ bf_set(abort_cmd_criteria, &abtswqe->abort_cmd, T_XRI_TAG);
+ abtswqe->abort_cmd.rsrvd5 = 0;
+ abtswqe->abort_cmd.wqe_com.abort_tag = xritag;
+ bf_set(wqe_reqtag, &abtswqe->abort_cmd.wqe_com, abtsiocb->iotag);
+ bf_set(wqe_cmnd, &abtswqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX);
+ bf_set(wqe_xri_tag, &abtswqe->generic.wqe_com, 0);
+ bf_set(wqe_qosd, &abtswqe->abort_cmd.wqe_com, 1);
+ bf_set(wqe_lenloc, &abtswqe->abort_cmd.wqe_com, LPFC_WQE_LENLOC_NONE);
+ bf_set(wqe_cmd_type, &abtswqe->abort_cmd.wqe_com, OTHER_COMMAND);
+
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocb->hba_wqidx = cmdiocb->hba_wqidx;
+ abtsiocb->cmd_flag |= LPFC_USE_FCPWQIDX;
+ if (cmdiocb->cmd_flag & LPFC_IO_FCP)
+ abtsiocb->cmd_flag |= LPFC_IO_FCP;
+ if (cmdiocb->cmd_flag & LPFC_IO_NVME)
+ abtsiocb->cmd_flag |= LPFC_IO_NVME;
+ if (cmdiocb->cmd_flag & LPFC_IO_FOF)
+ abtsiocb->cmd_flag |= LPFC_IO_FOF;
+ abtsiocb->vport = vport;
+ abtsiocb->cmd_cmpl = cmpl;
+
+ lpfc_cmd = container_of(cmdiocb, struct lpfc_io_buf, cur_iocbq);
+ retval = lpfc_sli4_issue_wqe(phba, lpfc_cmd->hdwq, abtsiocb);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI | LOG_NVME_ABTS | LOG_FCP,
+ "0359 Abort xri x%x, original iotag x%x, "
+ "abort cmd iotag x%x retval x%x\n",
+ xritag, cmdiocb->iotag, abtsiocb->iotag, retval);
+
+ if (retval) {
+ cmdiocb->cmd_flag &= ~LPFC_DRIVER_ABORTED;
+ __lpfc_sli_release_iocbq(phba, abtsiocb);
+ }
+
+ return retval;
+}
+
+#ifdef LPFC_MXP_STAT
+/**
+ * lpfc_snapshot_mxp - Snapshot pbl, pvt and busy count
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * The purpose of this routine is to take a snapshot of pbl, pvt and busy count
+ * 15 seconds after a test case is running.
+ *
+ * The user should call lpfc_debugfs_multixripools_write before running a test
+ * case to clear stat_snapshot_taken. Then the user starts a test case. During
+ * test case is running, stat_snapshot_taken is incremented by 1 every time when
+ * this routine is called from heartbeat timer. When stat_snapshot_taken is
+ * equal to LPFC_MXP_SNAPSHOT_TAKEN, a snapshot is taken.
+ **/
+void lpfc_snapshot_mxp(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ u32 txcmplq_cnt;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ return;
+
+ if (multixri_pool->stat_snapshot_taken == LPFC_MXP_SNAPSHOT_TAKEN) {
+ pvt_pool = &qp->p_multixri_pool->pvt_pool;
+ pbl_pool = &qp->p_multixri_pool->pbl_pool;
+ txcmplq_cnt = qp->io_wq->pring->txcmplq_cnt;
+
+ multixri_pool->stat_pbl_count = pbl_pool->count;
+ multixri_pool->stat_pvt_count = pvt_pool->count;
+ multixri_pool->stat_busy_count = txcmplq_cnt;
+ }
+
+ multixri_pool->stat_snapshot_taken++;
+}
+#endif
+
+/**
+ * lpfc_adjust_pvt_pool_count - Adjust private pool count
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * This routine moves some XRIs from private to public pool when private pool
+ * is not busy.
+ **/
+void lpfc_adjust_pvt_pool_count(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_multixri_pool *multixri_pool;
+ u32 io_req_count;
+ u32 prev_io_req_count;
+
+ multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool;
+ if (!multixri_pool)
+ return;
+ io_req_count = multixri_pool->io_req_count;
+ prev_io_req_count = multixri_pool->prev_io_req_count;
+
+ if (prev_io_req_count != io_req_count) {
+ /* Private pool is busy */
+ multixri_pool->prev_io_req_count = io_req_count;
+ } else {
+ /* Private pool is not busy.
+ * Move XRIs from private to public pool.
+ */
+ lpfc_move_xri_pvt_to_pbl(phba, hwqid);
+ }
+}
+
+/**
+ * lpfc_adjust_high_watermark - Adjust high watermark
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * This routine sets high watermark as number of outstanding XRIs,
+ * but make sure the new value is between xri_limit/2 and xri_limit.
+ **/
+void lpfc_adjust_high_watermark(struct lpfc_hba *phba, u32 hwqid)
+{
+ u32 new_watermark;
+ u32 watermark_max;
+ u32 watermark_min;
+ u32 xri_limit;
+ u32 txcmplq_cnt;
+ u32 abts_io_bufs;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_sli4_hdw_queue *qp;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool)
+ return;
+ xri_limit = multixri_pool->xri_limit;
+
+ watermark_max = xri_limit;
+ watermark_min = xri_limit / 2;
+
+ txcmplq_cnt = qp->io_wq->pring->txcmplq_cnt;
+ abts_io_bufs = qp->abts_scsi_io_bufs;
+ abts_io_bufs += qp->abts_nvme_io_bufs;
+
+ new_watermark = txcmplq_cnt + abts_io_bufs;
+ new_watermark = min(watermark_max, new_watermark);
+ new_watermark = max(watermark_min, new_watermark);
+ multixri_pool->pvt_pool.high_watermark = new_watermark;
+
+#ifdef LPFC_MXP_STAT
+ multixri_pool->stat_max_hwm = max(multixri_pool->stat_max_hwm,
+ new_watermark);
+#endif
+}
+
+/**
+ * lpfc_move_xri_pvt_to_pbl - Move some XRIs from private to public pool
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * This routine is called from hearbeat timer when pvt_pool is idle.
+ * All free XRIs are moved from private to public pool on hwqid with 2 steps.
+ * The first step moves (all - low_watermark) amount of XRIs.
+ * The second step moves the rest of XRIs.
+ **/
+void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct list_head tmp_list;
+ u32 tmp_count;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ pbl_pool = &qp->p_multixri_pool->pbl_pool;
+ pvt_pool = &qp->p_multixri_pool->pvt_pool;
+ tmp_count = 0;
+
+ lpfc_qp_spin_lock_irqsave(&pbl_pool->lock, iflag, qp, mv_to_pub_pool);
+ lpfc_qp_spin_lock(&pvt_pool->lock, qp, mv_from_pvt_pool);
+
+ if (pvt_pool->count > pvt_pool->low_watermark) {
+ /* Step 1: move (all - low_watermark) from pvt_pool
+ * to pbl_pool
+ */
+
+ /* Move low watermark of bufs from pvt_pool to tmp_list */
+ INIT_LIST_HEAD(&tmp_list);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pvt_pool->list, list) {
+ list_move_tail(&lpfc_ncmd->list, &tmp_list);
+ tmp_count++;
+ if (tmp_count >= pvt_pool->low_watermark)
+ break;
+ }
+
+ /* Move all bufs from pvt_pool to pbl_pool */
+ list_splice_init(&pvt_pool->list, &pbl_pool->list);
+
+ /* Move all bufs from tmp_list to pvt_pool */
+ list_splice(&tmp_list, &pvt_pool->list);
+
+ pbl_pool->count += (pvt_pool->count - tmp_count);
+ pvt_pool->count = tmp_count;
+ } else {
+ /* Step 2: move the rest from pvt_pool to pbl_pool */
+ list_splice_init(&pvt_pool->list, &pbl_pool->list);
+ pbl_pool->count += pvt_pool->count;
+ pvt_pool->count = 0;
+ }
+
+ spin_unlock(&pvt_pool->lock);
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+}
+
+/**
+ * _lpfc_move_xri_pbl_to_pvt - Move some XRIs from public to private pool
+ * @phba: pointer to lpfc hba data structure
+ * @qp: pointer to HDW queue
+ * @pbl_pool: specified public free XRI pool
+ * @pvt_pool: specified private free XRI pool
+ * @count: number of XRIs to move
+ *
+ * This routine tries to move some free common bufs from the specified pbl_pool
+ * to the specified pvt_pool. It might move less than count XRIs if there's not
+ * enough in public pool.
+ *
+ * Return:
+ * true - if XRIs are successfully moved from the specified pbl_pool to the
+ * specified pvt_pool
+ * false - if the specified pbl_pool is empty or locked by someone else
+ **/
+static bool
+_lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp,
+ struct lpfc_pbl_pool *pbl_pool,
+ struct lpfc_pvt_pool *pvt_pool, u32 count)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ int ret;
+
+ ret = spin_trylock_irqsave(&pbl_pool->lock, iflag);
+ if (ret) {
+ if (pbl_pool->count) {
+ /* Move a batch of XRIs from public to private pool */
+ lpfc_qp_spin_lock(&pvt_pool->lock, qp, mv_to_pvt_pool);
+ list_for_each_entry_safe(lpfc_ncmd,
+ lpfc_ncmd_next,
+ &pbl_pool->list,
+ list) {
+ list_move_tail(&lpfc_ncmd->list,
+ &pvt_pool->list);
+ pvt_pool->count++;
+ pbl_pool->count--;
+ count--;
+ if (count == 0)
+ break;
+ }
+
+ spin_unlock(&pvt_pool->lock);
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+ return true;
+ }
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+ }
+
+ return false;
+}
+
+/**
+ * lpfc_move_xri_pbl_to_pvt - Move some XRIs from public to private pool
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ * @count: number of XRIs to move
+ *
+ * This routine tries to find some free common bufs in one of public pools with
+ * Round Robin method. The search always starts from local hwqid, then the next
+ * HWQ which was found last time (rrb_next_hwqid). Once a public pool is found,
+ * a batch of free common bufs are moved to private pool on hwqid.
+ * It might move less than count XRIs if there's not enough in public pool.
+ **/
+void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 count)
+{
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_multixri_pool *next_multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_sli4_hdw_queue *qp;
+ u32 next_hwqid;
+ u32 hwq_count;
+ int ret;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ multixri_pool = qp->p_multixri_pool;
+ pvt_pool = &multixri_pool->pvt_pool;
+ pbl_pool = &multixri_pool->pbl_pool;
+
+ /* Check if local pbl_pool is available */
+ ret = _lpfc_move_xri_pbl_to_pvt(phba, qp, pbl_pool, pvt_pool, count);
+ if (ret) {
+#ifdef LPFC_MXP_STAT
+ multixri_pool->local_pbl_hit_count++;
+#endif
+ return;
+ }
+
+ hwq_count = phba->cfg_hdw_queue;
+
+ /* Get the next hwqid which was found last time */
+ next_hwqid = multixri_pool->rrb_next_hwqid;
+
+ do {
+ /* Go to next hwq */
+ next_hwqid = (next_hwqid + 1) % hwq_count;
+
+ next_multixri_pool =
+ phba->sli4_hba.hdwq[next_hwqid].p_multixri_pool;
+ pbl_pool = &next_multixri_pool->pbl_pool;
+
+ /* Check if the public free xri pool is available */
+ ret = _lpfc_move_xri_pbl_to_pvt(
+ phba, qp, pbl_pool, pvt_pool, count);
+
+ /* Exit while-loop if success or all hwqid are checked */
+ } while (!ret && next_hwqid != multixri_pool->rrb_next_hwqid);
+
+ /* Starting point for the next time */
+ multixri_pool->rrb_next_hwqid = next_hwqid;
+
+ if (!ret) {
+ /* stats: all public pools are empty*/
+ multixri_pool->pbl_empty_count++;
+ }
+
+#ifdef LPFC_MXP_STAT
+ if (ret) {
+ if (next_hwqid == hwqid)
+ multixri_pool->local_pbl_hit_count++;
+ else
+ multixri_pool->other_pbl_hit_count++;
+ }
+#endif
+}
+
+/**
+ * lpfc_keep_pvt_pool_above_lowwm - Keep pvt_pool above low watermark
+ * @phba: pointer to lpfc hba data structure.
+ * @hwqid: belong to which HWQ.
+ *
+ * This routine get a batch of XRIs from pbl_pool if pvt_pool is less than
+ * low watermark.
+ **/
+void lpfc_keep_pvt_pool_above_lowwm(struct lpfc_hba *phba, u32 hwqid)
+{
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+
+ multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool;
+ pvt_pool = &multixri_pool->pvt_pool;
+
+ if (pvt_pool->count < pvt_pool->low_watermark)
+ lpfc_move_xri_pbl_to_pvt(phba, hwqid, XRI_BATCH);
+}
- return pring->txq_cnt;
+/**
+ * lpfc_release_io_buf - Return one IO buf back to free pool
+ * @phba: pointer to lpfc hba data structure.
+ * @lpfc_ncmd: IO buf to be returned.
+ * @qp: belong to which HWQ.
+ *
+ * This routine returns one IO buf back to free pool. If this is an urgent IO,
+ * the IO buf is returned to expedite pool. If cfg_xri_rebalancing==1,
+ * the IO buf is returned to pbl_pool or pvt_pool based on watermark and
+ * xri_limit. If cfg_xri_rebalancing==0, the IO buf is returned to
+ * lpfc_io_buf_list_put.
+ **/
+void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd,
+ struct lpfc_sli4_hdw_queue *qp)
+{
+ unsigned long iflag;
+ struct lpfc_pbl_pool *pbl_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_epd_pool *epd_pool;
+ u32 txcmplq_cnt;
+ u32 xri_owned;
+ u32 xri_limit;
+ u32 abts_io_bufs;
+
+ /* MUST zero fields if buffer is reused by another protocol */
+ lpfc_ncmd->nvmeCmd = NULL;
+ lpfc_ncmd->cur_iocbq.cmd_cmpl = NULL;
+
+ if (phba->cfg_xpsgl && !phba->nvmet_support &&
+ !list_empty(&lpfc_ncmd->dma_sgl_xtra_list))
+ lpfc_put_sgl_per_hdwq(phba, lpfc_ncmd);
+
+ if (!list_empty(&lpfc_ncmd->dma_cmd_rsp_list))
+ lpfc_put_cmd_rsp_buf_per_hdwq(phba, lpfc_ncmd);
+
+ if (phba->cfg_xri_rebalancing) {
+ if (lpfc_ncmd->expedite) {
+ /* Return to expedite pool */
+ epd_pool = &phba->epd_pool;
+ spin_lock_irqsave(&epd_pool->lock, iflag);
+ list_add_tail(&lpfc_ncmd->list, &epd_pool->list);
+ epd_pool->count++;
+ spin_unlock_irqrestore(&epd_pool->lock, iflag);
+ return;
+ }
+
+ /* Avoid invalid access if an IO sneaks in and is being rejected
+ * just _after_ xri pools are destroyed in lpfc_offline.
+ * Nothing much can be done at this point.
+ */
+ if (!qp->p_multixri_pool)
+ return;
+
+ pbl_pool = &qp->p_multixri_pool->pbl_pool;
+ pvt_pool = &qp->p_multixri_pool->pvt_pool;
+
+ txcmplq_cnt = qp->io_wq->pring->txcmplq_cnt;
+ abts_io_bufs = qp->abts_scsi_io_bufs;
+ abts_io_bufs += qp->abts_nvme_io_bufs;
+
+ xri_owned = pvt_pool->count + txcmplq_cnt + abts_io_bufs;
+ xri_limit = qp->p_multixri_pool->xri_limit;
+
+#ifdef LPFC_MXP_STAT
+ if (xri_owned <= xri_limit)
+ qp->p_multixri_pool->below_limit_count++;
+ else
+ qp->p_multixri_pool->above_limit_count++;
+#endif
+
+ /* XRI goes to either public or private free xri pool
+ * based on watermark and xri_limit
+ */
+ if ((pvt_pool->count < pvt_pool->low_watermark) ||
+ (xri_owned < xri_limit &&
+ pvt_pool->count < pvt_pool->high_watermark)) {
+ lpfc_qp_spin_lock_irqsave(&pvt_pool->lock, iflag,
+ qp, free_pvt_pool);
+ list_add_tail(&lpfc_ncmd->list,
+ &pvt_pool->list);
+ pvt_pool->count++;
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+ } else {
+ lpfc_qp_spin_lock_irqsave(&pbl_pool->lock, iflag,
+ qp, free_pub_pool);
+ list_add_tail(&lpfc_ncmd->list,
+ &pbl_pool->list);
+ pbl_pool->count++;
+ spin_unlock_irqrestore(&pbl_pool->lock, iflag);
+ }
+ } else {
+ lpfc_qp_spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag,
+ qp, free_xri);
+ list_add_tail(&lpfc_ncmd->list,
+ &qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs++;
+ spin_unlock_irqrestore(&qp->io_buf_list_put_lock,
+ iflag);
+ }
+}
+
+/**
+ * lpfc_get_io_buf_from_private_pool - Get one free IO buf from private pool
+ * @phba: pointer to lpfc hba data structure.
+ * @qp: pointer to HDW queue
+ * @pvt_pool: pointer to private pool data structure.
+ * @ndlp: pointer to lpfc nodelist data structure.
+ *
+ * This routine tries to get one free IO buf from private pool.
+ *
+ * Return:
+ * pointer to one free IO buf - if private pool is not empty
+ * NULL - if private pool is empty
+ **/
+static struct lpfc_io_buf *
+lpfc_get_io_buf_from_private_pool(struct lpfc_hba *phba,
+ struct lpfc_sli4_hdw_queue *qp,
+ struct lpfc_pvt_pool *pvt_pool,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_io_buf *lpfc_ncmd;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+
+ lpfc_qp_spin_lock_irqsave(&pvt_pool->lock, iflag, qp, alloc_pvt_pool);
+ list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next,
+ &pvt_pool->list, list) {
+ if (lpfc_test_rrq_active(
+ phba, ndlp, lpfc_ncmd->cur_iocbq.sli4_lxritag))
+ continue;
+ list_del(&lpfc_ncmd->list);
+ pvt_pool->count--;
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+ return lpfc_ncmd;
+ }
+ spin_unlock_irqrestore(&pvt_pool->lock, iflag);
+
+ return NULL;
+}
+
+/**
+ * lpfc_get_io_buf_from_expedite_pool - Get one free IO buf from expedite pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine tries to get one free IO buf from expedite pool.
+ *
+ * Return:
+ * pointer to one free IO buf - if expedite pool is not empty
+ * NULL - if expedite pool is empty
+ **/
+static struct lpfc_io_buf *
+lpfc_get_io_buf_from_expedite_pool(struct lpfc_hba *phba)
+{
+ struct lpfc_io_buf *lpfc_ncmd = NULL, *iter;
+ struct lpfc_io_buf *lpfc_ncmd_next;
+ unsigned long iflag;
+ struct lpfc_epd_pool *epd_pool;
+
+ epd_pool = &phba->epd_pool;
+
+ spin_lock_irqsave(&epd_pool->lock, iflag);
+ if (epd_pool->count > 0) {
+ list_for_each_entry_safe(iter, lpfc_ncmd_next,
+ &epd_pool->list, list) {
+ list_del(&iter->list);
+ epd_pool->count--;
+ lpfc_ncmd = iter;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&epd_pool->lock, iflag);
+
+ return lpfc_ncmd;
+}
+
+/**
+ * lpfc_get_io_buf_from_multixri_pools - Get one free IO bufs
+ * @phba: pointer to lpfc hba data structure.
+ * @ndlp: pointer to lpfc nodelist data structure.
+ * @hwqid: belong to which HWQ
+ * @expedite: 1 means this request is urgent.
+ *
+ * This routine will do the following actions and then return a pointer to
+ * one free IO buf.
+ *
+ * 1. If private free xri count is empty, move some XRIs from public to
+ * private pool.
+ * 2. Get one XRI from private free xri pool.
+ * 3. If we fail to get one from pvt_pool and this is an expedite request,
+ * get one free xri from expedite pool.
+ *
+ * Note: ndlp is only used on SCSI side for RRQ testing.
+ * The caller should pass NULL for ndlp on NVME side.
+ *
+ * Return:
+ * pointer to one free IO buf - if private pool is not empty
+ * NULL - if private pool is empty
+ **/
+static struct lpfc_io_buf *
+lpfc_get_io_buf_from_multixri_pools(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp,
+ int hwqid, int expedite)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_multixri_pool *multixri_pool;
+ struct lpfc_pvt_pool *pvt_pool;
+ struct lpfc_io_buf *lpfc_ncmd;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ lpfc_ncmd = NULL;
+ if (!qp) {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_SLI | LOG_NVME_ABTS | LOG_FCP,
+ "5556 NULL qp for hwqid x%x\n", hwqid);
+ return lpfc_ncmd;
+ }
+ multixri_pool = qp->p_multixri_pool;
+ if (!multixri_pool) {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_SLI | LOG_NVME_ABTS | LOG_FCP,
+ "5557 NULL multixri for hwqid x%x\n", hwqid);
+ return lpfc_ncmd;
+ }
+ pvt_pool = &multixri_pool->pvt_pool;
+ if (!pvt_pool) {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_SLI | LOG_NVME_ABTS | LOG_FCP,
+ "5558 NULL pvt_pool for hwqid x%x\n", hwqid);
+ return lpfc_ncmd;
+ }
+ multixri_pool->io_req_count++;
+
+ /* If pvt_pool is empty, move some XRIs from public to private pool */
+ if (pvt_pool->count == 0)
+ lpfc_move_xri_pbl_to_pvt(phba, hwqid, XRI_BATCH);
+
+ /* Get one XRI from private free xri pool */
+ lpfc_ncmd = lpfc_get_io_buf_from_private_pool(phba, qp, pvt_pool, ndlp);
+
+ if (lpfc_ncmd) {
+ lpfc_ncmd->hdwq = qp;
+ lpfc_ncmd->hdwq_no = hwqid;
+ } else if (expedite) {
+ /* If we fail to get one from pvt_pool and this is an expedite
+ * request, get one free xri from expedite pool.
+ */
+ lpfc_ncmd = lpfc_get_io_buf_from_expedite_pool(phba);
+ }
+
+ return lpfc_ncmd;
+}
+
+static inline struct lpfc_io_buf *
+lpfc_io_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, int idx)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ struct lpfc_io_buf *lpfc_cmd, *lpfc_cmd_next;
+
+ qp = &phba->sli4_hba.hdwq[idx];
+ list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next,
+ &qp->lpfc_io_buf_list_get, list) {
+ if (lpfc_test_rrq_active(phba, ndlp,
+ lpfc_cmd->cur_iocbq.sli4_lxritag))
+ continue;
+
+ if (lpfc_cmd->flags & LPFC_SBUF_NOT_POSTED)
+ continue;
+
+ list_del_init(&lpfc_cmd->list);
+ qp->get_io_bufs--;
+ lpfc_cmd->hdwq = qp;
+ lpfc_cmd->hdwq_no = idx;
+ return lpfc_cmd;
+ }
+ return NULL;
+}
+
+/**
+ * lpfc_get_io_buf - Get one IO buffer from free pool
+ * @phba: The HBA for which this call is being executed.
+ * @ndlp: pointer to lpfc nodelist data structure.
+ * @hwqid: belong to which HWQ
+ * @expedite: 1 means this request is urgent.
+ *
+ * This routine gets one IO buffer from free pool. If cfg_xri_rebalancing==1,
+ * removes a IO buffer from multiXRI pools. If cfg_xri_rebalancing==0, removes
+ * a IO buffer from head of @hdwq io_buf_list and returns to caller.
+ *
+ * Note: ndlp is only used on SCSI side for RRQ testing.
+ * The caller should pass NULL for ndlp on NVME side.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_io_buf - Success
+ **/
+struct lpfc_io_buf *lpfc_get_io_buf(struct lpfc_hba *phba,
+ struct lpfc_nodelist *ndlp,
+ u32 hwqid, int expedite)
+{
+ struct lpfc_sli4_hdw_queue *qp;
+ unsigned long iflag;
+ struct lpfc_io_buf *lpfc_cmd;
+
+ qp = &phba->sli4_hba.hdwq[hwqid];
+ lpfc_cmd = NULL;
+ if (!qp) {
+ lpfc_printf_log(phba, KERN_WARNING,
+ LOG_SLI | LOG_NVME_ABTS | LOG_FCP,
+ "5555 NULL qp for hwqid x%x\n", hwqid);
+ return lpfc_cmd;
+ }
+
+ if (phba->cfg_xri_rebalancing)
+ lpfc_cmd = lpfc_get_io_buf_from_multixri_pools(
+ phba, ndlp, hwqid, expedite);
+ else {
+ lpfc_qp_spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag,
+ qp, alloc_xri_get);
+ if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite)
+ lpfc_cmd = lpfc_io_buf(phba, ndlp, hwqid);
+ if (!lpfc_cmd) {
+ lpfc_qp_spin_lock(&qp->io_buf_list_put_lock,
+ qp, alloc_xri_put);
+ list_splice(&qp->lpfc_io_buf_list_put,
+ &qp->lpfc_io_buf_list_get);
+ qp->get_io_bufs += qp->put_io_bufs;
+ INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put);
+ qp->put_io_bufs = 0;
+ spin_unlock(&qp->io_buf_list_put_lock);
+ if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT ||
+ expedite)
+ lpfc_cmd = lpfc_io_buf(phba, ndlp, hwqid);
+ }
+ spin_unlock_irqrestore(&qp->io_buf_list_get_lock, iflag);
+ }
+
+ return lpfc_cmd;
+}
+
+/**
+ * lpfc_read_object - Retrieve object data from HBA
+ * @phba: The HBA for which this call is being executed.
+ * @rdobject: Pathname of object data we want to read.
+ * @datap: Pointer to where data will be copied to.
+ * @datasz: size of data area
+ *
+ * This routine is limited to object sizes of LPFC_BPL_SIZE (1024) or less.
+ * The data will be truncated if datasz is not large enough.
+ * Version 1 is not supported with Embedded mbox cmd, so we must use version 0.
+ * Returns the actual bytes read from the object.
+ *
+ * This routine is hard coded to use a poll completion. Unlike other
+ * sli4_config mailboxes, it uses lpfc_mbuf memory which is not
+ * cleaned up in lpfc_sli4_cmd_mbox_free. If this routine is modified
+ * to use interrupt-based completions, code is needed to fully cleanup
+ * the memory.
+ */
+int
+lpfc_read_object(struct lpfc_hba *phba, char *rdobject, uint32_t *datap,
+ uint32_t datasz)
+{
+ struct lpfc_mbx_read_object *read_object;
+ LPFC_MBOXQ_t *mbox;
+ int rc, length, eof, j, byte_cnt = 0;
+ uint32_t shdr_status, shdr_add_status;
+ union lpfc_sli4_cfg_shdr *shdr;
+ struct lpfc_dmabuf *pcmd;
+ u32 rd_object_name[LPFC_MBX_OBJECT_NAME_LEN_DW] = {0};
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+ length = (sizeof(struct lpfc_mbx_read_object) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_READ_OBJECT,
+ length, LPFC_SLI4_MBX_EMBED);
+ read_object = &mbox->u.mqe.un.read_object;
+ shdr = (union lpfc_sli4_cfg_shdr *)&read_object->header.cfg_shdr;
+
+ bf_set(lpfc_mbox_hdr_version, &shdr->request, LPFC_Q_CREATE_VERSION_0);
+ bf_set(lpfc_mbx_rd_object_rlen, &read_object->u.request, datasz);
+ read_object->u.request.rd_object_offset = 0;
+ read_object->u.request.rd_object_cnt = 1;
+
+ memset((void *)read_object->u.request.rd_object_name, 0,
+ LPFC_OBJ_NAME_SZ);
+ scnprintf((char *)rd_object_name, sizeof(rd_object_name), rdobject);
+ for (j = 0; j < strlen(rdobject); j++)
+ read_object->u.request.rd_object_name[j] =
+ cpu_to_le32(rd_object_name[j]);
+
+ pcmd = kmalloc(sizeof(*pcmd), GFP_KERNEL);
+ if (pcmd)
+ pcmd->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &pcmd->phys);
+ if (!pcmd || !pcmd->virt) {
+ kfree(pcmd);
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return -ENOMEM;
+ }
+ memset((void *)pcmd->virt, 0, LPFC_BPL_SIZE);
+ read_object->u.request.rd_object_hbuf[0].pa_lo =
+ putPaddrLow(pcmd->phys);
+ read_object->u.request.rd_object_hbuf[0].pa_hi =
+ putPaddrHigh(pcmd->phys);
+ read_object->u.request.rd_object_hbuf[0].length = LPFC_BPL_SIZE;
+
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->ctx_ndlp = NULL;
+
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+
+ if (shdr_status == STATUS_FAILED &&
+ shdr_add_status == ADD_STATUS_INVALID_OBJECT_NAME) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_CGN_MGMT,
+ "4674 No port cfg file in FW.\n");
+ byte_cnt = -ENOENT;
+ } else if (shdr_status || shdr_add_status || rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_CGN_MGMT,
+ "2625 READ_OBJECT mailbox failed with "
+ "status x%x add_status x%x, mbx status x%x\n",
+ shdr_status, shdr_add_status, rc);
+ byte_cnt = -ENXIO;
+ } else {
+ /* Success */
+ length = read_object->u.response.rd_object_actual_rlen;
+ eof = bf_get(lpfc_mbx_rd_object_eof, &read_object->u.response);
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_CGN_MGMT,
+ "2626 READ_OBJECT Success len %d:%d, EOF %d\n",
+ length, datasz, eof);
+
+ /* Detect the port config file exists but is empty */
+ if (!length && eof) {
+ byte_cnt = 0;
+ goto exit;
+ }
+
+ byte_cnt = length;
+ lpfc_sli_pcimem_bcopy(pcmd->virt, datap, byte_cnt);
+ }
+
+ exit:
+ /* This is an embedded SLI4 mailbox with an external buffer allocated.
+ * Free the pcmd and then cleanup with the correct routine.
+ */
+ lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
+ kfree(pcmd);
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ return byte_cnt;
+}
+
+/**
+ * lpfc_get_sgl_per_hdwq - Get one SGL chunk from hdwq's pool
+ * @phba: The HBA for which this call is being executed.
+ * @lpfc_buf: IO buf structure to append the SGL chunk
+ *
+ * This routine gets one SGL chunk buffer from hdwq's SGL chunk pool,
+ * and will allocate an SGL chunk if the pool is empty.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to sli4_hybrid_sgl - Success
+ **/
+struct sli4_hybrid_sgl *
+lpfc_get_sgl_per_hdwq(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_buf)
+{
+ struct sli4_hybrid_sgl *list_entry = NULL;
+ struct sli4_hybrid_sgl *tmp = NULL;
+ struct sli4_hybrid_sgl *allocated_sgl = NULL;
+ struct lpfc_sli4_hdw_queue *hdwq = lpfc_buf->hdwq;
+ struct list_head *buf_list = &hdwq->sgl_list;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+
+ if (likely(!list_empty(buf_list))) {
+ /* break off 1 chunk from the sgl_list */
+ list_for_each_entry_safe(list_entry, tmp,
+ buf_list, list_node) {
+ list_move_tail(&list_entry->list_node,
+ &lpfc_buf->dma_sgl_xtra_list);
+ break;
+ }
+ } else {
+ /* allocate more */
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+ tmp = kmalloc_node(sizeof(*tmp), GFP_ATOMIC,
+ cpu_to_node(hdwq->io_wq->chann));
+ if (!tmp) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "8353 error kmalloc memory for HDWQ "
+ "%d %s\n",
+ lpfc_buf->hdwq_no, __func__);
+ return NULL;
+ }
+
+ tmp->dma_sgl = dma_pool_alloc(phba->lpfc_sg_dma_buf_pool,
+ GFP_ATOMIC, &tmp->dma_phys_sgl);
+ if (!tmp->dma_sgl) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "8354 error pool_alloc memory for HDWQ "
+ "%d %s\n",
+ lpfc_buf->hdwq_no, __func__);
+ kfree(tmp);
+ return NULL;
+ }
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+ list_add_tail(&tmp->list_node, &lpfc_buf->dma_sgl_xtra_list);
+ }
+
+ allocated_sgl = list_last_entry(&lpfc_buf->dma_sgl_xtra_list,
+ struct sli4_hybrid_sgl,
+ list_node);
+
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+
+ return allocated_sgl;
+}
+
+/**
+ * lpfc_put_sgl_per_hdwq - Put one SGL chunk into hdwq pool
+ * @phba: The HBA for which this call is being executed.
+ * @lpfc_buf: IO buf structure with the SGL chunk
+ *
+ * This routine puts one SGL chunk buffer into hdwq's SGL chunk pool.
+ *
+ * Return codes:
+ * 0 - Success
+ * -EINVAL - Error
+ **/
+int
+lpfc_put_sgl_per_hdwq(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_buf)
+{
+ int rc = 0;
+ struct sli4_hybrid_sgl *list_entry = NULL;
+ struct sli4_hybrid_sgl *tmp = NULL;
+ struct lpfc_sli4_hdw_queue *hdwq = lpfc_buf->hdwq;
+ struct list_head *buf_list = &hdwq->sgl_list;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+
+ if (likely(!list_empty(&lpfc_buf->dma_sgl_xtra_list))) {
+ list_for_each_entry_safe(list_entry, tmp,
+ &lpfc_buf->dma_sgl_xtra_list,
+ list_node) {
+ list_move_tail(&list_entry->list_node,
+ buf_list);
+ }
+ } else {
+ rc = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+ return rc;
+}
+
+/**
+ * lpfc_free_sgl_per_hdwq - Free all SGL chunks of hdwq pool
+ * @phba: phba object
+ * @hdwq: hdwq to cleanup sgl buff resources on
+ *
+ * This routine frees all SGL chunks of hdwq SGL chunk pool.
+ *
+ * Return codes:
+ * None
+ **/
+void
+lpfc_free_sgl_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_sli4_hdw_queue *hdwq)
+{
+ struct list_head *buf_list = &hdwq->sgl_list;
+ struct sli4_hybrid_sgl *list_entry = NULL;
+ struct sli4_hybrid_sgl *tmp = NULL;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+
+ /* Free sgl pool */
+ list_for_each_entry_safe(list_entry, tmp,
+ buf_list, list_node) {
+ list_del(&list_entry->list_node);
+ dma_pool_free(phba->lpfc_sg_dma_buf_pool,
+ list_entry->dma_sgl,
+ list_entry->dma_phys_sgl);
+ kfree(list_entry);
+ }
+
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+}
+
+/**
+ * lpfc_get_cmd_rsp_buf_per_hdwq - Get one CMD/RSP buffer from hdwq
+ * @phba: The HBA for which this call is being executed.
+ * @lpfc_buf: IO buf structure to attach the CMD/RSP buffer
+ *
+ * This routine gets one CMD/RSP buffer from hdwq's CMD/RSP pool,
+ * and will allocate an CMD/RSP buffer if the pool is empty.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to fcp_cmd_rsp_buf - Success
+ **/
+struct fcp_cmd_rsp_buf *
+lpfc_get_cmd_rsp_buf_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_io_buf *lpfc_buf)
+{
+ struct fcp_cmd_rsp_buf *list_entry = NULL;
+ struct fcp_cmd_rsp_buf *tmp = NULL;
+ struct fcp_cmd_rsp_buf *allocated_buf = NULL;
+ struct lpfc_sli4_hdw_queue *hdwq = lpfc_buf->hdwq;
+ struct list_head *buf_list = &hdwq->cmd_rsp_buf_list;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+
+ if (likely(!list_empty(buf_list))) {
+ /* break off 1 chunk from the list */
+ list_for_each_entry_safe(list_entry, tmp,
+ buf_list,
+ list_node) {
+ list_move_tail(&list_entry->list_node,
+ &lpfc_buf->dma_cmd_rsp_list);
+ break;
+ }
+ } else {
+ /* allocate more */
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+ tmp = kmalloc_node(sizeof(*tmp), GFP_ATOMIC,
+ cpu_to_node(hdwq->io_wq->chann));
+ if (!tmp) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "8355 error kmalloc memory for HDWQ "
+ "%d %s\n",
+ lpfc_buf->hdwq_no, __func__);
+ return NULL;
+ }
+
+ tmp->fcp_cmnd = dma_pool_zalloc(phba->lpfc_cmd_rsp_buf_pool,
+ GFP_ATOMIC,
+ &tmp->fcp_cmd_rsp_dma_handle);
+
+ if (!tmp->fcp_cmnd) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "8356 error pool_alloc memory for HDWQ "
+ "%d %s\n",
+ lpfc_buf->hdwq_no, __func__);
+ kfree(tmp);
+ return NULL;
+ }
+
+ tmp->fcp_rsp = (struct fcp_rsp *)((uint8_t *)tmp->fcp_cmnd +
+ sizeof(struct fcp_cmnd32));
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+ list_add_tail(&tmp->list_node, &lpfc_buf->dma_cmd_rsp_list);
+ }
+
+ allocated_buf = list_last_entry(&lpfc_buf->dma_cmd_rsp_list,
+ struct fcp_cmd_rsp_buf,
+ list_node);
+
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+
+ return allocated_buf;
+}
+
+/**
+ * lpfc_put_cmd_rsp_buf_per_hdwq - Put one CMD/RSP buffer into hdwq pool
+ * @phba: The HBA for which this call is being executed.
+ * @lpfc_buf: IO buf structure with the CMD/RSP buf
+ *
+ * This routine puts one CMD/RSP buffer into executing CPU's CMD/RSP pool.
+ *
+ * Return codes:
+ * 0 - Success
+ * -EINVAL - Error
+ **/
+int
+lpfc_put_cmd_rsp_buf_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_io_buf *lpfc_buf)
+{
+ int rc = 0;
+ struct fcp_cmd_rsp_buf *list_entry = NULL;
+ struct fcp_cmd_rsp_buf *tmp = NULL;
+ struct lpfc_sli4_hdw_queue *hdwq = lpfc_buf->hdwq;
+ struct list_head *buf_list = &hdwq->cmd_rsp_buf_list;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+
+ if (likely(!list_empty(&lpfc_buf->dma_cmd_rsp_list))) {
+ list_for_each_entry_safe(list_entry, tmp,
+ &lpfc_buf->dma_cmd_rsp_list,
+ list_node) {
+ list_move_tail(&list_entry->list_node,
+ buf_list);
+ }
+ } else {
+ rc = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+ return rc;
+}
+
+/**
+ * lpfc_free_cmd_rsp_buf_per_hdwq - Free all CMD/RSP chunks of hdwq pool
+ * @phba: phba object
+ * @hdwq: hdwq to cleanup cmd rsp buff resources on
+ *
+ * This routine frees all CMD/RSP buffers of hdwq's CMD/RSP buf pool.
+ *
+ * Return codes:
+ * None
+ **/
+void
+lpfc_free_cmd_rsp_buf_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_sli4_hdw_queue *hdwq)
+{
+ struct list_head *buf_list = &hdwq->cmd_rsp_buf_list;
+ struct fcp_cmd_rsp_buf *list_entry = NULL;
+ struct fcp_cmd_rsp_buf *tmp = NULL;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&hdwq->hdwq_lock, iflags);
+
+ /* Free cmd_rsp buf pool */
+ list_for_each_entry_safe(list_entry, tmp,
+ buf_list,
+ list_node) {
+ list_del(&list_entry->list_node);
+ dma_pool_free(phba->lpfc_cmd_rsp_buf_pool,
+ list_entry->fcp_cmnd,
+ list_entry->fcp_cmd_rsp_dma_handle);
+ kfree(list_entry);
+ }
+
+ spin_unlock_irqrestore(&hdwq->hdwq_lock, iflags);
+}
+
+/**
+ * lpfc_sli_prep_wqe - Prepare WQE for the command to be posted
+ * @phba: phba object
+ * @job: job entry of the command to be posted.
+ *
+ * Fill the common fields of the wqe for each of the command.
+ *
+ * Return codes:
+ * None
+ **/
+void
+lpfc_sli_prep_wqe(struct lpfc_hba *phba, struct lpfc_iocbq *job)
+{
+ u8 cmnd;
+ u32 *pcmd;
+ u32 if_type = 0;
+ u32 abort_tag;
+ bool fip;
+ struct lpfc_nodelist *ndlp = NULL;
+ union lpfc_wqe128 *wqe = &job->wqe;
+ u8 command_type = ELS_COMMAND_NON_FIP;
+
+ fip = test_bit(HBA_FIP_SUPPORT, &phba->hba_flag);
+ /* The fcp commands will set command type */
+ if (job->cmd_flag & LPFC_IO_FCP)
+ command_type = FCP_COMMAND;
+ else if (fip && (job->cmd_flag & LPFC_FIP_ELS_ID_MASK))
+ command_type = ELS_COMMAND_FIP;
+ else
+ command_type = ELS_COMMAND_NON_FIP;
+
+ abort_tag = job->iotag;
+ cmnd = bf_get(wqe_cmnd, &wqe->els_req.wqe_com);
+
+ switch (cmnd) {
+ case CMD_ELS_REQUEST64_WQE:
+ ndlp = job->ndlp;
+
+ if_type = bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf);
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
+ pcmd = (u32 *)job->cmd_dmabuf->virt;
+ if (pcmd && (*pcmd == ELS_CMD_FLOGI ||
+ *pcmd == ELS_CMD_SCR ||
+ *pcmd == ELS_CMD_RDF ||
+ *pcmd == ELS_CMD_EDC ||
+ *pcmd == ELS_CMD_RSCN_XMT ||
+ *pcmd == ELS_CMD_FDISC ||
+ *pcmd == ELS_CMD_LOGO ||
+ *pcmd == ELS_CMD_QFPA ||
+ *pcmd == ELS_CMD_UVEM ||
+ *pcmd == ELS_CMD_PLOGI)) {
+ bf_set(els_req64_sp, &wqe->els_req, 1);
+ bf_set(els_req64_sid, &wqe->els_req,
+ job->vport->fc_myDID);
+
+ if ((*pcmd == ELS_CMD_FLOGI) &&
+ !(phba->fc_topology ==
+ LPFC_TOPOLOGY_LOOP))
+ bf_set(els_req64_sid, &wqe->els_req, 0);
+
+ bf_set(wqe_ct, &wqe->els_req.wqe_com, 1);
+ bf_set(wqe_ctxt_tag, &wqe->els_req.wqe_com,
+ phba->vpi_ids[job->vport->vpi]);
+ } else if (pcmd) {
+ bf_set(wqe_ct, &wqe->els_req.wqe_com, 0);
+ bf_set(wqe_ctxt_tag, &wqe->els_req.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+ }
+ }
+
+ bf_set(wqe_temp_rpi, &wqe->els_req.wqe_com,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+
+ bf_set(wqe_dbde, &wqe->els_req.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->els_req.wqe_com, LPFC_WQE_IOD_READ);
+ bf_set(wqe_qosd, &wqe->els_req.wqe_com, 1);
+ bf_set(wqe_lenloc, &wqe->els_req.wqe_com, LPFC_WQE_LENLOC_NONE);
+ bf_set(wqe_ebde_cnt, &wqe->els_req.wqe_com, 0);
+ break;
+ case CMD_XMIT_ELS_RSP64_WQE:
+ ndlp = job->ndlp;
+
+ /* word4 */
+ wqe->xmit_els_rsp.word4 = 0;
+
+ if_type = bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf);
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
+ if (test_bit(FC_PT2PT, &job->vport->fc_flag)) {
+ bf_set(els_rsp64_sp, &wqe->xmit_els_rsp, 1);
+ bf_set(els_rsp64_sid, &wqe->xmit_els_rsp,
+ job->vport->fc_myDID);
+ if (job->vport->fc_myDID == Fabric_DID) {
+ bf_set(wqe_els_did,
+ &wqe->xmit_els_rsp.wqe_dest, 0);
+ }
+ }
+ }
+
+ bf_set(wqe_dbde, &wqe->xmit_els_rsp.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->xmit_els_rsp.wqe_com, LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_qosd, &wqe->xmit_els_rsp.wqe_com, 1);
+ bf_set(wqe_lenloc, &wqe->xmit_els_rsp.wqe_com,
+ LPFC_WQE_LENLOC_WORD3);
+ bf_set(wqe_ebde_cnt, &wqe->xmit_els_rsp.wqe_com, 0);
+
+ if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
+ bf_set(els_rsp64_sp, &wqe->xmit_els_rsp, 1);
+ bf_set(els_rsp64_sid, &wqe->xmit_els_rsp,
+ job->vport->fc_myDID);
+ bf_set(wqe_ct, &wqe->xmit_els_rsp.wqe_com, 1);
+ }
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ bf_set(wqe_rsp_temp_rpi, &wqe->xmit_els_rsp,
+ phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
+
+ if (bf_get(wqe_ct, &wqe->xmit_els_rsp.wqe_com))
+ bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com,
+ phba->vpi_ids[job->vport->vpi]);
+ }
+ command_type = OTHER_COMMAND;
+ break;
+ case CMD_GEN_REQUEST64_WQE:
+ /* Word 10 */
+ bf_set(wqe_dbde, &wqe->gen_req.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->gen_req.wqe_com, LPFC_WQE_IOD_READ);
+ bf_set(wqe_qosd, &wqe->gen_req.wqe_com, 1);
+ bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE);
+ bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0);
+ command_type = OTHER_COMMAND;
+ break;
+ case CMD_XMIT_SEQUENCE64_WQE:
+ if (phba->link_flag & LS_LOOPBACK_MODE)
+ bf_set(wqe_xo, &wqe->xmit_sequence.wge_ctl, 1);
+
+ wqe->xmit_sequence.rsvd3 = 0;
+ bf_set(wqe_pu, &wqe->xmit_sequence.wqe_com, 0);
+ bf_set(wqe_dbde, &wqe->xmit_sequence.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->xmit_sequence.wqe_com,
+ LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_lenloc, &wqe->xmit_sequence.wqe_com,
+ LPFC_WQE_LENLOC_WORD12);
+ bf_set(wqe_ebde_cnt, &wqe->xmit_sequence.wqe_com, 0);
+ command_type = OTHER_COMMAND;
+ break;
+ case CMD_XMIT_BLS_RSP64_WQE:
+ bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
+ bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
+ bf_set(wqe_ct, &wqe->xmit_bls_rsp.wqe_com, 1);
+ bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
+ phba->vpi_ids[phba->pport->vpi]);
+ bf_set(wqe_qosd, &wqe->xmit_bls_rsp.wqe_com, 1);
+ bf_set(wqe_lenloc, &wqe->xmit_bls_rsp.wqe_com,
+ LPFC_WQE_LENLOC_NONE);
+ /* Overwrite the pre-set comnd type with OTHER_COMMAND */
+ command_type = OTHER_COMMAND;
+ break;
+ case CMD_FCP_ICMND64_WQE: /* task mgmt commands */
+ case CMD_ABORT_XRI_WQE: /* abort iotag */
+ case CMD_SEND_FRAME: /* mds loopback */
+ /* cases already formatted for sli4 wqe - no chgs necessary */
+ return;
+ default:
+ dump_stack();
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "6207 Invalid command 0x%x\n",
+ cmnd);
+ break;
+ }
+
+ wqe->generic.wqe_com.abort_tag = abort_tag;
+ bf_set(wqe_reqtag, &wqe->generic.wqe_com, job->iotag);
+ bf_set(wqe_cmd_type, &wqe->generic.wqe_com, command_type);
+ bf_set(wqe_cqid, &wqe->generic.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
}
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index a0075b0af142..cf7c42ec0306 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2007 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -18,6 +20,10 @@
* included with this package. *
*******************************************************************/
+#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS)
+#define CONFIG_SCSI_LPFC_DEBUG_FS
+#endif
+
/* forward declaration for LPFC_IOCB_t's use */
struct lpfc_hba;
struct lpfc_vport;
@@ -29,8 +35,21 @@ typedef enum _lpfc_ctx_cmd {
LPFC_CTX_HOST
} lpfc_ctx_cmd;
+/* Enumeration to describe the thread lock context. */
+enum lpfc_mbox_ctx {
+ MBOX_THD_UNLOCKED,
+ MBOX_THD_LOCKED
+};
+
+union lpfc_vmid_tag {
+ uint32_t app_id;
+ uint8_t cs_ctl_vmid;
+ struct lpfc_vmid_context *vmid_context; /* UVEM context information */
+};
+
struct lpfc_cq_event {
struct list_head list;
+ uint16_t hdwq;
union {
struct lpfc_mcqe mcqe_cmpl;
struct lpfc_acqe_link acqe_link;
@@ -54,13 +73,30 @@ struct lpfc_iocbq {
uint16_t iotag; /* pre-assigned IO tag */
uint16_t sli4_lxritag; /* logical pre-assigned XRI. */
uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */
+ uint16_t hba_wqidx; /* index to HBA work queue */
struct lpfc_cq_event cq_event;
+ uint64_t isr_timestamp;
+
+ union lpfc_wqe128 wqe; /* SLI-4 */
+ IOCB_t iocb; /* SLI-3 */
+ struct lpfc_wcqe_complete wcqe_cmpl; /* WQE cmpl */
- IOCB_t iocb; /* IOCB cmd */
- uint8_t retry; /* retry counter for IOCB cmd - if needed */
- uint16_t iocb_flag;
+ u32 unsol_rcv_len; /* Receive len in usol path */
+
+ /* Pack the u8's together and make them module-4. */
+ u8 num_bdes; /* Number of BDEs */
+ u8 abort_bls; /* ABTS by initiator or responder */
+ u8 abort_rctl; /* ACC or RJT flag */
+ u8 priority; /* OAS priority */
+ u8 retry; /* retry counter for IOCB cmd - if needed */
+ u8 rsvd1; /* Pad for u32 */
+ u8 rsvd2; /* Pad for u32 */
+ u8 rsvd3; /* Pad for u32 */
+
+ u32 cmd_flag;
#define LPFC_IO_LIBDFC 1 /* libdfc iocb */
-#define LPFC_IO_WAKE 2 /* High Priority Queue signal flag */
+#define LPFC_IO_WAKE 2 /* Synchronous I/O completed */
+#define LPFC_IO_WAKE_TMO LPFC_IO_WAKE /* Synchronous I/O timed out */
#define LPFC_IO_FCP 4 /* FCP command -- iocbq in scsi_buf */
#define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */
#define LPFC_IO_FABRIC 0x10 /* Iocb send using fabric scheduler */
@@ -68,30 +104,51 @@ struct lpfc_iocbq {
#define LPFC_EXCHANGE_BUSY 0x40 /* SLI4 hba reported XB in response */
#define LPFC_USE_FCPWQIDX 0x80 /* Submit to specified FCPWQ index */
#define DSS_SECURITY_OP 0x100 /* security IO */
-#define LPFC_IO_ON_Q 0x200 /* The IO is still on the TXCMPLQ */
+#define LPFC_IO_ON_TXCMPLQ 0x200 /* The IO is still on the TXCMPLQ */
+#define LPFC_IO_DIF_PASS 0x400 /* T10 DIF IO pass-thru prot */
+#define LPFC_IO_DIF_STRIP 0x800 /* T10 DIF IO strip prot */
+#define LPFC_IO_DIF_INSERT 0x1000 /* T10 DIF IO insert prot */
+#define LPFC_IO_CMD_OUTSTANDING 0x2000 /* timeout handler abort window */
#define LPFC_FIP_ELS_ID_MASK 0xc000 /* ELS_ID range 0-3, non-shifted mask */
#define LPFC_FIP_ELS_ID_SHIFT 14
- uint8_t rsvd2;
+#define LPFC_IO_OAS 0x10000 /* OAS FCP IO */
+#define LPFC_IO_FOF 0x20000 /* FOF FCP IO */
+#define LPFC_IO_LOOPBACK 0x40000 /* Loopback IO */
+#define LPFC_PRLI_NVME_REQ 0x80000 /* This is an NVME PRLI. */
+#define LPFC_PRLI_FCP_REQ 0x100000 /* This is an NVME PRLI. */
+#define LPFC_IO_NVME 0x200000 /* NVME FCP command */
+#define LPFC_IO_NVME_LS 0x400000 /* NVME LS command */
+#define LPFC_IO_NVMET 0x800000 /* NVMET command */
+#define LPFC_IO_VMID 0x1000000 /* VMID tagged IO */
+#define LPFC_IO_CMF 0x4000000 /* CMF command */
+
uint32_t drvrTimeout; /* driver timeout in seconds */
- uint32_t fcp_wqidx; /* index to FCP work queue */
struct lpfc_vport *vport;/* virtual port pointer */
- void *context1; /* caller context information */
- void *context2; /* caller context information */
- void *context3; /* caller context information */
+ struct lpfc_dmabuf *cmd_dmabuf;
+ struct lpfc_dmabuf *rsp_dmabuf;
+ struct lpfc_dmabuf *bpl_dmabuf;
+ uint32_t event_tag; /* LA Event tag */
union {
wait_queue_head_t *wait_queue;
- struct lpfc_iocbq *rsp_iocb;
struct lpfcMboxq *mbox;
- struct lpfc_nodelist *ndlp;
struct lpfc_node_rrq *rrq;
+ struct nvmefc_ls_req *nvme_lsreq;
+ struct lpfc_async_xchg_ctx *axchg;
+ struct bsg_job_data *dd_data;
} context_un;
- void (*fabric_iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
- struct lpfc_iocbq *);
- void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
- struct lpfc_iocbq *);
+ struct lpfc_io_buf *io_buf;
+ struct lpfc_iocbq *rsp_iocb;
+ struct lpfc_nodelist *ndlp;
+ union lpfc_vmid_tag vmid_tag;
+ void (*fabric_cmd_cmpl)(struct lpfc_hba *phba, struct lpfc_iocbq *cmd,
+ struct lpfc_iocbq *rsp);
+ void (*wait_cmd_cmpl)(struct lpfc_hba *phba, struct lpfc_iocbq *cmd,
+ struct lpfc_iocbq *rsp);
+ void (*cmd_cmpl)(struct lpfc_hba *phba, struct lpfc_iocbq *cmd,
+ struct lpfc_iocbq *rsp);
};
#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */
@@ -100,6 +157,19 @@ struct lpfc_iocbq {
#define IOCB_BUSY 1
#define IOCB_ERROR 2
#define IOCB_TIMEDOUT 3
+#define IOCB_ABORTED 4
+#define IOCB_ABORTING 5
+#define IOCB_NORESOURCE 6
+
+#define SLI_WQE_RET_WQE 1 /* Return WQE if cmd ring full */
+
+#define WQE_SUCCESS 0
+#define WQE_BUSY 1
+#define WQE_ERROR 2
+#define WQE_TIMEDOUT 3
+#define WQE_ABORTED 4
+#define WQE_ABORTING 5
+#define WQE_NORESOURCE 6
#define LPFC_MBX_WAKE 1
#define LPFC_MBX_IMED_UNREG 2
@@ -111,9 +181,30 @@ typedef struct lpfcMboxq {
MAILBOX_t mb; /* Mailbox cmd */
struct lpfc_mqe mqe;
} u;
- struct lpfc_vport *vport;/* virtual port pointer */
- void *context1; /* caller context information */
- void *context2; /* caller context information */
+ struct lpfc_vport *vport; /* virtual port pointer */
+ struct lpfc_nodelist *ctx_ndlp; /* caller ndlp pointer */
+ struct lpfc_dmabuf *ctx_buf; /* caller buffer information */
+ void *ext_buf; /* extended buffer for extended mbox
+ * cmds. Not a generic pointer.
+ * Use for storing virtual address.
+ */
+
+ /* Pointers that are seldom used during mbox execution, but require
+ * a saved context.
+ */
+ union {
+ unsigned long ox_rx_id; /* Used in els_rsp_rls_acc */
+ struct lpfc_rdp_context *rdp; /* Used in get_rdp_info */
+ struct lpfc_lcb_context *lcb; /* Used in set_beacon */
+ struct completion *mbox_wait; /* Used in issue_mbox_wait */
+ struct bsg_job_data *dd_data; /* Used in bsg_issue_mbox_cmpl
+ * and
+ * bsg_issue_mbox_ext_handle_job
+ */
+ struct lpfc_iocbq *save_iocb; /* Used in defer_plogi_acc and
+ * lpfc_mbx_cmpl_resume_rpi
+ */
+ } ctx_u;
void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *);
uint8_t mbox_flag;
@@ -130,7 +221,9 @@ typedef struct lpfcMboxq {
#define LPFC_MAX_RING_MASK 5 /* max num of rctl/type masks allowed per
ring */
-#define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */
+#define LPFC_SLI3_MAX_RING 4 /* Max num of SLI3 rings used by driver.
+ For SLI4, an additional ring for each
+ FCP WQ will be allocated. */
struct lpfc_sli_ring;
@@ -157,6 +250,24 @@ struct lpfc_sli_ring_stat {
uint64_t iocb_rsp_full; /* IOCB rsp ring full */
};
+struct lpfc_sli3_ring {
+ uint32_t local_getidx; /* last available cmd index (from cmdGetInx) */
+ uint32_t next_cmdidx; /* next_cmd index */
+ uint32_t rspidx; /* current index in response ring */
+ uint32_t cmdidx; /* current index in command ring */
+ uint16_t numCiocb; /* number of command iocb's per ring */
+ uint16_t numRiocb; /* number of rsp iocb's per ring */
+ uint16_t sizeCiocb; /* Size of command iocb's in this ring */
+ uint16_t sizeRiocb; /* Size of response iocb's in this ring */
+ uint32_t *cmdringaddr; /* virtual address for cmd rings */
+ uint32_t *rspringaddr; /* virtual address for rsp rings */
+};
+
+struct lpfc_sli4_ring {
+ struct lpfc_queue *wqp; /* Pointer to associated WQ */
+};
+
+
/* Structure used to hold SLI ring information */
struct lpfc_sli_ring {
uint16_t flag; /* ring flags */
@@ -165,16 +276,10 @@ struct lpfc_sli_ring {
#define LPFC_STOP_IOCB_EVENT 0x020 /* Stop processing IOCB cmds event */
uint16_t abtsiotag; /* tracks next iotag to use for ABTS */
- uint32_t local_getidx; /* last available cmd index (from cmdGetInx) */
- uint32_t next_cmdidx; /* next_cmd index */
- uint32_t rspidx; /* current index in response ring */
- uint32_t cmdidx; /* current index in command ring */
uint8_t rsvd;
uint8_t ringno; /* ring number */
- uint16_t numCiocb; /* number of command iocb's per ring */
- uint16_t numRiocb; /* number of rsp iocb's per ring */
- uint16_t sizeCiocb; /* Size of command iocb's in this ring */
- uint16_t sizeRiocb; /* Size of response iocb's in this ring */
+
+ spinlock_t ring_lock; /* lock for issuing commands */
uint32_t fast_iotag; /* max fastlookup based iotag */
uint32_t iotag_ctr; /* keeps track of the next iotag to use */
@@ -185,8 +290,6 @@ struct lpfc_sli_ring {
struct list_head txcmplq;
uint16_t txcmplq_cnt; /* current length of queue */
uint16_t txcmplq_max; /* max length */
- uint32_t *cmdringaddr; /* virtual address for cmd rings */
- uint32_t *rspringaddr; /* virtual address for rsp rings */
uint32_t missbufcnt; /* keep track of buffers to post */
struct list_head postbufq;
uint16_t postbufq_cnt; /* current length of queue */
@@ -206,6 +309,10 @@ struct lpfc_sli_ring {
/* cmd ring available */
void (*lpfc_sli_cmd_available) (struct lpfc_hba *,
struct lpfc_sli_ring *);
+ union {
+ struct lpfc_sli3_ring sli3;
+ struct lpfc_sli4_ring sli4;
+ } sli;
};
/* Structure used for configuring rings to a specific profile or rctl / type */
@@ -238,6 +345,8 @@ struct lpfc_sli_stat {
uint64_t mbox_stat_err; /* Mbox cmds completed status error */
uint64_t mbox_cmd; /* Mailbox commands issued */
uint64_t sli_intr; /* Count of Host Attention interrupts */
+ uint64_t sli_prev_intr; /* Previous cnt of Host Attention interrupts */
+ uint64_t sli_ips; /* Host Attention interrupts per sec */
uint32_t err_attn_event; /* Error Attn event counters */
uint32_t link_event; /* Link event counters */
uint32_t mbox_event; /* Mailbox event counters */
@@ -266,14 +375,15 @@ struct lpfc_sli {
#define LPFC_SLI_ACTIVE 0x200 /* SLI in firmware is active */
#define LPFC_PROCESS_LA 0x400 /* Able to process link attention */
#define LPFC_BLOCK_MGMT_IO 0x800 /* Don't allow mgmt mbx or iocb cmds */
-#define LPFC_MENLO_MAINT 0x1000 /* need for menl fw download */
#define LPFC_SLI_ASYNC_MBX_BLK 0x2000 /* Async mailbox is blocked */
+#define LPFC_SLI_SUPPRESS_RSP 0x4000 /* Suppress RSP feature is supported */
+#define LPFC_SLI_USE_EQDR 0x8000 /* EQ Delay Register is supported */
+#define LPFC_QUEUE_FREE_INIT 0x10000 /* Queue freeing is in progress */
+#define LPFC_QUEUE_FREE_WAIT 0x20000 /* Hold Queue free as it is being
+ * used outside worker thread
+ */
- struct lpfc_sli_ring ring[LPFC_MAX_RING];
- int fcp_ring; /* ring used for FCP initiator commands */
- int next_ring;
-
- int extra_ring; /* extra ring used for other protocols */
+ struct lpfc_sli_ring *sli3_ring;
struct lpfc_sli_stat slistat; /* SLI statistical info */
struct list_head mboxq;
@@ -289,17 +399,105 @@ struct lpfc_sli {
struct lpfc_iocbq ** iocbq_lookup; /* array to lookup IOCB by IOTAG */
size_t iocbq_lookup_len; /* current lengs of the array */
uint16_t last_iotag; /* last allocated IOTAG */
- unsigned long stats_start; /* in seconds */
+ time64_t stats_start; /* in seconds */
struct lpfc_lnk_stat lnk_stat_offsets;
};
-#define LPFC_MBOX_TMO 30 /* Sec tmo for outstanding mbox
- command */
-#define LPFC_MBOX_SLI4_CONFIG_TMO 60 /* Sec tmo for outstanding mbox
- command */
-#define LPFC_MBOX_TMO_FLASH_CMD 300 /* Sec tmo for outstanding FLASH write
- * or erase cmds. This is especially
- * long because of the potential of
- * multiple flash erases that can be
- * spawned.
- */
+/* Timeout for normal outstanding mbox command (Seconds) */
+#define LPFC_MBOX_TMO 30
+/* Timeout for non-flash-based outstanding sli_config mbox command (Seconds) */
+#define LPFC_MBOX_SLI4_CONFIG_TMO 60
+/* Timeout for flash-based outstanding sli_config mbox command (Seconds) */
+#define LPFC_MBOX_SLI4_CONFIG_EXTENDED_TMO 300
+/* Timeout for other flash-based outstanding mbox command (Seconds) */
+#define LPFC_MBOX_TMO_FLASH_CMD 300
+
+struct lpfc_io_buf {
+ /* Common fields */
+ struct list_head list;
+ void *data;
+
+ dma_addr_t dma_handle;
+ dma_addr_t dma_phys_sgl;
+
+ struct sli4_sge *dma_sgl; /* initial segment chunk */
+
+ /* linked list of extra sli4_hybrid_sge */
+ struct list_head dma_sgl_xtra_list;
+
+ /* list head for fcp_cmd_rsp buf */
+ struct list_head dma_cmd_rsp_list;
+
+ struct lpfc_iocbq cur_iocbq;
+ struct lpfc_sli4_hdw_queue *hdwq;
+ uint16_t hdwq_no;
+ uint16_t cpu;
+
+ struct lpfc_nodelist *ndlp;
+ uint32_t timeout;
+ uint16_t flags;
+#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */
+#define LPFC_SBUF_BUMP_QDEPTH 0x2 /* bumped queue depth counter */
+ /* External DIF device IO conversions */
+#define LPFC_SBUF_NORMAL_DIF 0x4 /* normal mode to insert/strip */
+#define LPFC_SBUF_PASS_DIF 0x8 /* insert/strip mode to passthru */
+#define LPFC_SBUF_NOT_POSTED 0x10 /* SGL failed post to FW. */
+ uint16_t status; /* From IOCB Word 7- ulpStatus */
+ uint32_t result; /* From IOCB Word 4. */
+
+ uint32_t seg_cnt; /* Number of scatter-gather segments returned by
+ * dma_map_sg. The driver needs this for calls
+ * to dma_unmap_sg.
+ */
+ unsigned long start_time;
+ spinlock_t buf_lock; /* lock used in case of simultaneous abort */
+ bool expedite; /* this is an expedite io_buf */
+
+ union {
+ /* SCSI specific fields */
+ struct {
+ struct scsi_cmnd *pCmd;
+ struct lpfc_rport_data *rdata;
+ uint32_t prot_seg_cnt; /* seg_cnt's counterpart for
+ * protection data
+ */
+
+ /*
+ * data and dma_handle are the kernel virtual and bus
+ * address of the dma-able buffer containing the
+ * fcp_cmd, fcp_rsp and a scatter gather bde list that
+ * supports the sg_tablesize value.
+ */
+ struct fcp_cmnd *fcp_cmnd;
+ struct fcp_rsp *fcp_rsp;
+
+ wait_queue_head_t *waitq;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ /* Used to restore any changes to protection data for
+ * error injection
+ */
+ void *prot_data_segment;
+ uint32_t prot_data;
+ uint32_t prot_data_type;
+#define LPFC_INJERR_REFTAG 1
+#define LPFC_INJERR_APPTAG 2
+#define LPFC_INJERR_GUARD 3
+#endif
+ };
+
+ /* NVME specific fields */
+ struct {
+ struct nvmefc_fcp_req *nvmeCmd;
+ uint16_t qidx;
+ };
+ };
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ uint64_t ts_cmd_start;
+ uint64_t ts_last_cmd;
+ uint64_t ts_cmd_wqput;
+ uint64_t ts_isr_cmpl;
+ uint64_t ts_data_io;
+#endif
+ uint64_t rx_cmd_start;
+};
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 4b1703554a26..fd6dab157887 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2009-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2009-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -18,12 +20,17 @@
* included with this package. *
*******************************************************************/
+#include <linux/irq_poll.h>
+#include <linux/cpufreq.h>
+
+#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS)
+#define CONFIG_SCSI_LPFC_DEBUG_FS
+#endif
+
#define LPFC_ACTIVE_MBOX_WAIT_CNT 100
#define LPFC_XRI_EXCH_BUSY_WAIT_TMO 10000
#define LPFC_XRI_EXCH_BUSY_WAIT_T1 10
#define LPFC_XRI_EXCH_BUSY_WAIT_T2 30000
-#define LPFC_RELEASE_NOTIFICATION_INTERVAL 32
-#define LPFC_GET_QE_REL_INT 32
#define LPFC_RPI_LOW_WATER_MARK 10
#define LPFC_UNREG_FCF 1
@@ -35,18 +42,20 @@
/* Number of SGL entries can be posted in a 4KB nonembedded mbox command */
#define LPFC_NEMBED_MBOX_SGL_CNT 254
-/* Multi-queue arrangement for fast-path FCP work queues */
-#define LPFC_FN_EQN_MAX 8
-#define LPFC_SP_EQN_DEF 1
-#define LPFC_FP_EQN_DEF 4
-#define LPFC_FP_EQN_MIN 1
-#define LPFC_FP_EQN_MAX (LPFC_FN_EQN_MAX - LPFC_SP_EQN_DEF)
+/* Multi-queue arrangement for FCP EQ/CQ/WQ tuples */
+#define LPFC_HBA_HDWQ_MIN 0
+#define LPFC_HBA_HDWQ_MAX 256
+#define LPFC_HBA_HDWQ_DEF LPFC_HBA_HDWQ_MIN
-#define LPFC_FN_WQN_MAX 32
-#define LPFC_SP_WQN_DEF 1
-#define LPFC_FP_WQN_DEF 4
-#define LPFC_FP_WQN_MIN 1
-#define LPFC_FP_WQN_MAX (LPFC_FN_WQN_MAX - LPFC_SP_WQN_DEF)
+/* irq_chann range, values */
+#define LPFC_IRQ_CHANN_MIN 0
+#define LPFC_IRQ_CHANN_MAX 256
+#define LPFC_IRQ_CHANN_DEF LPFC_IRQ_CHANN_MIN
+
+/* FCP MQ queue count limiting */
+#define LPFC_FCP_MQ_THRESHOLD_MIN 0
+#define LPFC_FCP_MQ_THRESHOLD_MAX 256
+#define LPFC_FCP_MQ_THRESHOLD_DEF 8
/*
* Provide the default FCF Record attributes used by the driver
@@ -76,11 +85,24 @@
(fc_hdr)->fh_s_id[1] << 8 | \
(fc_hdr)->fh_s_id[2])
+#define sli4_did_from_fc_hdr(fc_hdr) \
+ ((fc_hdr)->fh_d_id[0] << 16 | \
+ (fc_hdr)->fh_d_id[1] << 8 | \
+ (fc_hdr)->fh_d_id[2])
+
#define sli4_fctl_from_fc_hdr(fc_hdr) \
((fc_hdr)->fh_f_ctl[0] << 16 | \
(fc_hdr)->fh_f_ctl[1] << 8 | \
(fc_hdr)->fh_f_ctl[2])
+#define sli4_type_from_fc_hdr(fc_hdr) \
+ ((fc_hdr)->fh_type)
+
+#define LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT 12000
+
+#define INT_FW_UPGRADE 0
+#define RUN_FW_UPGRADE 1
+
enum lpfc_sli4_queue_type {
LPFC_EQ,
LPFC_GCQ,
@@ -97,51 +119,179 @@ enum lpfc_sli4_queue_type {
enum lpfc_sli4_queue_subtype {
LPFC_NONE,
LPFC_MBOX,
- LPFC_FCP,
+ LPFC_IO,
LPFC_ELS,
+ LPFC_NVMET,
+ LPFC_NVME_LS,
LPFC_USOL
};
-union sli4_qe {
- void *address;
- struct lpfc_eqe *eqe;
- struct lpfc_cqe *cqe;
- struct lpfc_mcqe *mcqe;
- struct lpfc_wcqe_complete *wcqe_complete;
- struct lpfc_wcqe_release *wcqe_release;
- struct sli4_wcqe_xri_aborted *wcqe_xri_aborted;
- struct lpfc_rcqe_complete *rcqe_complete;
- struct lpfc_mqe *mqe;
- union lpfc_wqe *wqe;
- struct lpfc_rqe *rqe;
+/* RQ buffer list */
+struct lpfc_rqb {
+ uint16_t entry_count; /* Current number of RQ slots */
+ uint16_t buffer_count; /* Current number of buffers posted */
+ struct list_head rqb_buffer_list; /* buffers assigned to this HBQ */
+ /* Callback for HBQ buffer allocation */
+ struct rqb_dmabuf *(*rqb_alloc_buffer)(struct lpfc_hba *);
+ /* Callback for HBQ buffer free */
+ void (*rqb_free_buffer)(struct lpfc_hba *,
+ struct rqb_dmabuf *);
+};
+
+enum lpfc_poll_mode {
+ LPFC_QUEUE_WORK,
+ LPFC_THREADED_IRQ,
+};
+
+struct lpfc_idle_stat {
+ u64 prev_idle;
+ u64 prev_wall;
};
struct lpfc_queue {
struct list_head list;
+ struct list_head wq_list;
+
+ /*
+ * If interrupts are in effect on _all_ the eq's the footprint
+ * of polling code is zero (except mode). This memory is chec-
+ * ked for every io to see if the io needs to be polled and
+ * while completion to check if the eq's needs to be rearmed.
+ * Keep in same cacheline as the queue ptr to avoid cpu fetch
+ * stalls. Using 1B memory will leave us with 7B hole. Fill
+ * it with other frequently used members.
+ */
+ uint16_t last_cpu; /* most recent cpu */
+ uint16_t hdwq;
+ uint8_t qe_valid;
+ uint8_t mode; /* interrupt or polling */
+#define LPFC_EQ_INTERRUPT 0
+#define LPFC_EQ_POLL 1
+
+ struct list_head wqfull_list;
enum lpfc_sli4_queue_type type;
enum lpfc_sli4_queue_subtype subtype;
struct lpfc_hba *phba;
struct list_head child_list;
+ struct list_head page_list;
+ struct list_head sgl_list;
+ struct list_head cpu_list;
uint32_t entry_count; /* Number of entries to support on the queue */
uint32_t entry_size; /* Size of each queue entry. */
+ uint32_t entry_cnt_per_pg;
+ uint32_t notify_interval; /* Queue Notification Interval
+ * For chip->host queues (EQ, CQ, RQ):
+ * specifies the interval (number of
+ * entries) where the doorbell is rung to
+ * notify the chip of entry consumption.
+ * For host->chip queues (WQ):
+ * specifies the interval (number of
+ * entries) where consumption CQE is
+ * requested to indicate WQ entries
+ * consumed by the chip.
+ * Not used on an MQ.
+ */
+#define LPFC_EQ_NOTIFY_INTRVL 16
+#define LPFC_CQ_NOTIFY_INTRVL 16
+#define LPFC_WQ_NOTIFY_INTRVL 16
+#define LPFC_RQ_NOTIFY_INTRVL 16
+ uint32_t max_proc_limit; /* Queue Processing Limit
+ * For chip->host queues (EQ, CQ):
+ * specifies the maximum number of
+ * entries to be consumed in one
+ * processing iteration sequence. Queue
+ * will be rearmed after each iteration.
+ * Not used on an MQ, RQ or WQ.
+ */
+#define LPFC_EQ_MAX_PROC_LIMIT 256
+#define LPFC_CQ_MIN_PROC_LIMIT 64
+#define LPFC_CQ_MAX_PROC_LIMIT LPFC_CQE_EXP_COUNT // 4096
+#define LPFC_CQ_DEF_MAX_PROC_LIMIT LPFC_CQE_DEF_COUNT // 1024
+#define LPFC_CQ_MIN_THRESHOLD_TO_POLL 64
+#define LPFC_CQ_MAX_THRESHOLD_TO_POLL LPFC_CQ_DEF_MAX_PROC_LIMIT
+#define LPFC_CQ_DEF_THRESHOLD_TO_POLL LPFC_CQ_DEF_MAX_PROC_LIMIT
+ uint32_t queue_claimed; /* indicates queue is being processed */
uint32_t queue_id; /* Queue ID assigned by the hardware */
uint32_t assoc_qid; /* Queue ID associated with, for CQ/WQ/MQ */
- struct list_head page_list;
- uint32_t page_count; /* Number of pages allocated for this queue */
uint32_t host_index; /* The host's index for putting or getting */
uint32_t hba_index; /* The last known hba index for get or put */
- union sli4_qe qe[1]; /* array to index entries (must be last) */
+ uint32_t q_mode;
+
+ struct lpfc_sli_ring *pring; /* ptr to io ring associated with q */
+ struct lpfc_rqb *rqbp; /* ptr to RQ buffers */
+
+ uint16_t page_count; /* Number of pages allocated for this queue */
+ uint16_t page_size; /* size of page allocated for this queue */
+#define LPFC_EXPANDED_PAGE_SIZE 16384
+#define LPFC_DEFAULT_PAGE_SIZE 4096
+ uint16_t chann; /* Hardware Queue association WQ/CQ */
+ /* CPU affinity for EQ */
+#define LPFC_FIND_BY_EQ 0
+#define LPFC_FIND_BY_HDWQ 1
+ uint8_t db_format;
+#define LPFC_DB_RING_FORMAT 0x01
+#define LPFC_DB_LIST_FORMAT 0x02
+ uint8_t q_flag;
+#define HBA_NVMET_WQFULL 0x1 /* We hit WQ Full condition for NVMET */
+#define HBA_NVMET_CQ_NOTIFY 0x1 /* LPFC_NVMET_CQ_NOTIFY CQEs this EQE */
+#define HBA_EQ_DELAY_CHK 0x2 /* EQ is a candidate for coalescing */
+#define LPFC_NVMET_CQ_NOTIFY 4
+ void __iomem *db_regaddr;
+ uint16_t dpp_enable;
+ uint16_t dpp_id;
+ void __iomem *dpp_regaddr;
+
+ /* For q stats */
+ uint32_t q_cnt_1;
+ uint32_t q_cnt_2;
+ uint32_t q_cnt_3;
+ uint64_t q_cnt_4;
+/* defines for EQ stats */
+#define EQ_max_eqe q_cnt_1
+#define EQ_no_entry q_cnt_2
+#define EQ_cqe_cnt q_cnt_3
+#define EQ_processed q_cnt_4
+
+/* defines for CQ stats */
+#define CQ_mbox q_cnt_1
+#define CQ_max_cqe q_cnt_1
+#define CQ_release_wqe q_cnt_2
+#define CQ_xri_aborted q_cnt_3
+#define CQ_wq q_cnt_4
+
+/* defines for WQ stats */
+#define WQ_overflow q_cnt_1
+#define WQ_posted q_cnt_4
+
+/* defines for RQ stats */
+#define RQ_no_posted_buf q_cnt_1
+#define RQ_no_buf_found q_cnt_2
+#define RQ_buf_posted q_cnt_3
+#define RQ_rcv_buf q_cnt_4
+
+ struct work_struct irqwork;
+ struct work_struct spwork;
+ struct delayed_work sched_irqwork;
+ struct delayed_work sched_spwork;
+
+ uint64_t isr_timestamp;
+ struct lpfc_queue *assoc_qp;
+ struct list_head _poll_list;
+ void **q_pgs; /* array to index entries per page */
+
+ enum lpfc_poll_mode poll_mode;
};
struct lpfc_sli4_link {
- uint8_t speed;
+ uint32_t speed;
uint8_t duplex;
uint8_t status;
uint8_t type;
uint8_t number;
uint8_t fault;
- uint16_t logical_speed;
+ uint8_t link_status;
uint16_t topology;
+ uint32_t logical_speed;
};
struct lpfc_fcf_rec {
@@ -157,6 +307,25 @@ struct lpfc_fcf_rec {
#define RECORD_VALID 0x02
};
+struct lpfc_fcf_pri_rec {
+ uint16_t fcf_index;
+#define LPFC_FCF_ON_PRI_LIST 0x0001
+#define LPFC_FCF_FLOGI_FAILED 0x0002
+ uint16_t flag;
+ uint32_t priority;
+};
+
+struct lpfc_fcf_pri {
+ struct list_head list;
+ struct lpfc_fcf_pri_rec fcf_rec;
+};
+
+/*
+ * Maximum FCF table index, it is for driver internal book keeping, it
+ * just needs to be no less than the supported HBA's FCF table size.
+ */
+#define LPFC_SLI4_FCF_TBL_INDX_MAX 32
+
struct lpfc_fcf {
uint16_t fcfi;
uint32_t fcf_flag;
@@ -172,19 +341,18 @@ struct lpfc_fcf {
#define FCF_REDISC_EVT 0x100 /* FCF rediscovery event to worker thread */
#define FCF_REDISC_FOV 0x200 /* Post FCF rediscovery fast failover */
#define FCF_REDISC_PROG (FCF_REDISC_PEND | FCF_REDISC_EVT)
+ uint16_t fcf_redisc_attempted;
uint32_t addr_mode;
uint32_t eligible_fcf_cnt;
struct lpfc_fcf_rec current_rec;
struct lpfc_fcf_rec failover_rec;
+ struct list_head fcf_pri_list;
+ struct lpfc_fcf_pri fcf_pri[LPFC_SLI4_FCF_TBL_INDX_MAX];
+ uint32_t current_fcf_scan_pri;
struct timer_list redisc_wait;
unsigned long *fcf_rr_bmask; /* Eligible FCF indexes for RR failover */
};
-/*
- * Maximum FCF table index, it is for driver internal book keeping, it
- * just needs to be no less than the supported HBA's FCF table size.
- */
-#define LPFC_SLI4_FCF_TBL_INDX_MAX 32
#define LPFC_REGION23_SIGNATURE "RG23"
#define LPFC_REGION23_VERSION 1
@@ -267,12 +435,15 @@ struct lpfc_bmbx {
#define LPFC_EQE_SIZE_16B 16
#define LPFC_CQE_SIZE 16
#define LPFC_WQE_SIZE 64
+#define LPFC_WQE128_SIZE 128
#define LPFC_MQE_SIZE 256
#define LPFC_RQE_SIZE 8
#define LPFC_EQE_DEF_COUNT 1024
-#define LPFC_CQE_DEF_COUNT 256
+#define LPFC_CQE_DEF_COUNT 1024
+#define LPFC_CQE_EXP_COUNT 4096
#define LPFC_WQE_DEF_COUNT 256
+#define LPFC_WQE_EXP_COUNT 1024
#define LPFC_MQE_DEF_COUNT 16
#define LPFC_RQE_DEF_COUNT 512
@@ -288,11 +459,6 @@ struct lpfc_bmbx {
#define SLI4_CT_VFI 2
#define SLI4_CT_FCFI 3
-#define LPFC_SLI4_FL1_MAX_SEGMENT_SIZE 0x10000
-#define LPFC_SLI4_FL1_MAX_BUF_SIZE 0X2000
-#define LPFC_SLI4_MIN_BUF_SIZE 0x400
-#define LPFC_SLI4_MAX_BUF_SIZE 0x20000
-
/*
* SLI4 specific data structures
*/
@@ -319,9 +485,32 @@ struct lpfc_max_cfg_param {
struct lpfc_hba;
/* SLI4 HBA multi-fcp queue handler struct */
-struct lpfc_fcp_eq_hdl {
+#define LPFC_SLI4_HANDLER_NAME_SZ 16
+struct lpfc_hba_eq_hdl {
uint32_t idx;
+ int irq;
+ char handler_name[LPFC_SLI4_HANDLER_NAME_SZ];
struct lpfc_hba *phba;
+ struct lpfc_queue *eq;
+ struct cpumask aff_mask;
+};
+
+#define lpfc_get_eq_hdl(eqidx) (&phba->sli4_hba.hba_eq_hdl[eqidx])
+#define lpfc_get_aff_mask(eqidx) (&phba->sli4_hba.hba_eq_hdl[eqidx].aff_mask)
+#define lpfc_get_irq(eqidx) (phba->sli4_hba.hba_eq_hdl[eqidx].irq)
+
+/*BB Credit recovery value*/
+struct lpfc_bbscn_params {
+ uint32_t word0;
+#define lpfc_bbscn_min_SHIFT 0
+#define lpfc_bbscn_min_MASK 0x0000000F
+#define lpfc_bbscn_min_WORD word0
+#define lpfc_bbscn_max_SHIFT 4
+#define lpfc_bbscn_max_MASK 0x0000000F
+#define lpfc_bbscn_max_WORD word0
+#define lpfc_bbscn_def_SHIFT 8
+#define lpfc_bbscn_def_MASK 0x0000000F
+#define lpfc_bbscn_def_WORD word0
};
/* Port Capabilities for SLI4 Parameters */
@@ -342,6 +531,7 @@ struct lpfc_pc_sli4_params {
uint32_t if_page_sz;
uint32_t rq_db_window;
uint32_t loopbk_scope;
+ uint32_t oas_supported;
uint32_t eq_pages_max;
uint32_t eqe_size;
uint32_t cq_pages_max;
@@ -358,25 +548,243 @@ struct lpfc_pc_sli4_params {
uint32_t hdr_pp_align;
uint32_t sgl_pages_max;
uint32_t sgl_pp_align;
+ uint32_t mib_size;
+ uint16_t mi_ver;
+#define LPFC_MIB1_SUPPORT 1
+#define LPFC_MIB2_SUPPORT 2
+#define LPFC_MIB3_SUPPORT 3
+ uint16_t mi_value;
+#define LPFC_DFLT_MIB_VAL 2
+ uint8_t mi_cap;
+ uint8_t mib_bde_cnt;
+ uint8_t cmf;
uint8_t cqv;
uint8_t mqv;
uint8_t wqv;
uint8_t rqv;
+ uint8_t eqav;
+ uint8_t cqav;
+ uint8_t wqsize;
+ uint8_t bv1s;
+ uint8_t pls;
+#define LPFC_WQ_SZ64_SUPPORT 1
+#define LPFC_WQ_SZ128_SUPPORT 2
+ uint8_t wqpcnt;
+ uint8_t nvme;
};
+#define LPFC_CQ_4K_PAGE_SZ 0x1
+#define LPFC_CQ_16K_PAGE_SZ 0x4
+#define LPFC_CQ_32K_PAGE_SZ 0x8
+#define LPFC_WQ_4K_PAGE_SZ 0x1
+#define LPFC_WQ_16K_PAGE_SZ 0x4
+#define LPFC_WQ_32K_PAGE_SZ 0x8
+
struct lpfc_iov {
uint32_t pf_number;
uint32_t vf_number;
};
+struct lpfc_sli4_lnk_info {
+ uint8_t lnk_dv;
+#define LPFC_LNK_DAT_INVAL 0
+#define LPFC_LNK_DAT_VAL 1
+ uint8_t lnk_tp;
+#define LPFC_LNK_GE 0x0 /* FCoE */
+#define LPFC_LNK_FC 0x1 /* FC */
+#define LPFC_LNK_FC_TRUNKED 0x2 /* FC_Trunked */
+ uint8_t lnk_no;
+ uint8_t optic_state;
+};
+
+#define LPFC_SLI4_HANDLER_CNT (LPFC_HBA_IO_CHAN_MAX+ \
+ LPFC_FOF_IO_CHAN_NUM)
+
+/* Used for tracking CPU mapping attributes */
+struct lpfc_vector_map_info {
+ uint16_t phys_id;
+ uint16_t core_id;
+ uint16_t eq;
+ uint16_t hdwq;
+ uint16_t flag;
+#define LPFC_CPU_MAP_HYPER 0x1
+#define LPFC_CPU_MAP_UNASSIGN 0x2
+#define LPFC_CPU_FIRST_IRQ 0x4
+};
+#define LPFC_VECTOR_MAP_EMPTY 0xffff
+
+#define LPFC_IRQ_EMPTY 0xffffffff
+
+/* Multi-XRI pool */
+#define XRI_BATCH 8
+
+struct lpfc_pbl_pool {
+ struct list_head list;
+ u32 count;
+ spinlock_t lock; /* lock for pbl_pool*/
+};
+
+struct lpfc_pvt_pool {
+ u32 low_watermark;
+ u32 high_watermark;
+
+ struct list_head list;
+ u32 count;
+ spinlock_t lock; /* lock for pvt_pool */
+};
+
+struct lpfc_multixri_pool {
+ u32 xri_limit;
+
+ /* Starting point when searching a pbl_pool with round-robin method */
+ u32 rrb_next_hwqid;
+
+ /* Used by lpfc_adjust_pvt_pool_count.
+ * io_req_count is incremented by 1 during IO submission. The heartbeat
+ * handler uses these two variables to determine if pvt_pool is idle or
+ * busy.
+ */
+ u32 prev_io_req_count;
+ u32 io_req_count;
+
+ /* statistics */
+ u32 pbl_empty_count;
+#ifdef LPFC_MXP_STAT
+ u32 above_limit_count;
+ u32 below_limit_count;
+ u32 local_pbl_hit_count;
+ u32 other_pbl_hit_count;
+ u32 stat_max_hwm;
+
+#define LPFC_MXP_SNAPSHOT_TAKEN 3 /* snapshot is taken at 3rd heartbeats */
+ u32 stat_pbl_count;
+ u32 stat_pvt_count;
+ u32 stat_busy_count;
+ u32 stat_snapshot_taken;
+#endif
+
+ /* TODO: Separate pvt_pool into get and put list */
+ struct lpfc_pbl_pool pbl_pool; /* Public free XRI pool */
+ struct lpfc_pvt_pool pvt_pool; /* Private free XRI pool */
+};
+
+struct lpfc_fc4_ctrl_stat {
+ u32 input_requests;
+ u32 output_requests;
+ u32 control_requests;
+ u32 io_cmpls;
+};
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+struct lpfc_lock_stat {
+ uint32_t alloc_xri_get;
+ uint32_t alloc_xri_put;
+ uint32_t free_xri;
+ uint32_t wq_access;
+ uint32_t alloc_pvt_pool;
+ uint32_t mv_from_pvt_pool;
+ uint32_t mv_to_pub_pool;
+ uint32_t mv_to_pvt_pool;
+ uint32_t free_pub_pool;
+ uint32_t free_pvt_pool;
+};
+#endif
+
+struct lpfc_eq_intr_info {
+ struct list_head list;
+ uint32_t icnt;
+};
+
/* SLI4 HBA data structure entries */
+struct lpfc_sli4_hdw_queue {
+ /* Pointers to the constructed SLI4 queues */
+ struct lpfc_queue *hba_eq; /* Event queues for HBA */
+ struct lpfc_queue *io_cq; /* Fast-path FCP & NVME compl queue */
+ struct lpfc_queue *io_wq; /* Fast-path FCP & NVME work queue */
+ uint16_t io_cq_map;
+
+ /* Keep track of IO buffers for this hardware queue */
+ spinlock_t io_buf_list_get_lock; /* Common buf alloc list lock */
+ struct list_head lpfc_io_buf_list_get;
+ spinlock_t io_buf_list_put_lock; /* Common buf free list lock */
+ struct list_head lpfc_io_buf_list_put;
+ spinlock_t abts_io_buf_list_lock; /* list of aborted IOs */
+ struct list_head lpfc_abts_io_buf_list;
+ uint32_t total_io_bufs;
+ uint32_t get_io_bufs;
+ uint32_t put_io_bufs;
+ uint32_t empty_io_bufs;
+ uint32_t abts_scsi_io_bufs;
+ uint32_t abts_nvme_io_bufs;
+
+ /* Multi-XRI pool per HWQ */
+ struct lpfc_multixri_pool *p_multixri_pool;
+
+ /* FC-4 Stats counters */
+ struct lpfc_fc4_ctrl_stat nvme_cstat;
+ struct lpfc_fc4_ctrl_stat scsi_cstat;
+#ifdef LPFC_HDWQ_LOCK_STAT
+ struct lpfc_lock_stat lock_conflict;
+#endif
+
+ /* Per HDWQ pool resources */
+ struct list_head sgl_list;
+ struct list_head cmd_rsp_buf_list;
+
+ /* Lock for syncing Per HDWQ pool resources */
+ spinlock_t hdwq_lock;
+};
+
+#ifdef LPFC_HDWQ_LOCK_STAT
+/* compile time trylock stats */
+#define lpfc_qp_spin_lock_irqsave(lock, flag, qp, lstat) \
+ { \
+ int only_once = 1; \
+ while (spin_trylock_irqsave(lock, flag) == 0) { \
+ if (only_once) { \
+ only_once = 0; \
+ qp->lock_conflict.lstat++; \
+ } \
+ } \
+ }
+#define lpfc_qp_spin_lock(lock, qp, lstat) \
+ { \
+ int only_once = 1; \
+ while (spin_trylock(lock) == 0) { \
+ if (only_once) { \
+ only_once = 0; \
+ qp->lock_conflict.lstat++; \
+ } \
+ } \
+ }
+#else
+#define lpfc_qp_spin_lock_irqsave(lock, flag, qp, lstat) \
+ spin_lock_irqsave(lock, flag)
+#define lpfc_qp_spin_lock(lock, qp, lstat) spin_lock(lock)
+#endif
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+struct lpfc_hdwq_stat {
+ u32 hdwq_no;
+ u32 rcv_io;
+ u32 xmt_io;
+ u32 cmpl_io;
+};
+#endif
+
struct lpfc_sli4_hba {
void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for
- PCI BAR0, config space registers */
+ * config space registers
+ */
void __iomem *ctrl_regs_memmap_p; /* Kernel memory mapped address for
- PCI BAR1, control registers */
+ * control registers
+ */
void __iomem *drbl_regs_memmap_p; /* Kernel memory mapped address for
- PCI BAR2, doorbell registers */
+ * doorbell registers
+ */
+ void __iomem *dpp_regs_memmap_p; /* Kernel memory mapped address for
+ * dpp registers
+ */
union {
struct {
/* IF Type 0, BAR 0 PCI cfg space reg mem map */
@@ -390,7 +798,17 @@ struct lpfc_sli4_hba {
void __iomem *STATUSregaddr;
void __iomem *CTRLregaddr;
void __iomem *ERR1regaddr;
+#define SLIPORT_ERR1_REG_ERR_CODE_1 0x1
+#define SLIPORT_ERR1_REG_ERR_CODE_2 0x2
void __iomem *ERR2regaddr;
+#define SLIPORT_ERR2_REG_FW_RESTART 0x0
+#define SLIPORT_ERR2_REG_FUNC_PROVISON 0x1
+#define SLIPORT_ERR2_REG_FORCED_DUMP 0x2
+#define SLIPORT_ERR2_REG_FAILURE_EQ 0x3
+#define SLIPORT_ERR2_REG_FAILURE_CQ 0x4
+#define SLIPORT_ERR2_REG_FAILURE_BUS 0x5
+#define SLIPORT_ERR2_REG_FAILURE_RQ 0x6
+ void __iomem *EQDregaddr;
} if_type2;
} u;
@@ -407,29 +825,53 @@ struct lpfc_sli4_hba {
/* IF type 0, BAR 0 and if type 2, BAR 0 doorbell register memory map */
void __iomem *RQDBregaddr; /* RQ_DOORBELL register */
void __iomem *WQDBregaddr; /* WQ_DOORBELL register */
- void __iomem *EQCQDBregaddr; /* EQCQ_DOORBELL register */
+ void __iomem *CQDBregaddr; /* CQ_DOORBELL register */
+ void __iomem *EQDBregaddr; /* EQ_DOORBELL register */
void __iomem *MQDBregaddr; /* MQ_DOORBELL register */
void __iomem *BMBXregaddr; /* BootStrap MBX register */
uint32_t ue_mask_lo;
uint32_t ue_mask_hi;
+ uint32_t ue_to_sr;
+ uint32_t ue_to_rp;
struct lpfc_register sli_intf;
struct lpfc_pc_sli4_params pc_sli4_params;
- struct msix_entry *msix_entries;
- uint32_t cfg_eqn;
- uint32_t msix_vec_nr;
- struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */
+ struct lpfc_bbscn_params bbscn_params;
+ struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */
+
+ void (*sli4_eq_clr_intr)(struct lpfc_queue *q);
+ void (*sli4_write_eq_db)(struct lpfc_hba *phba, struct lpfc_queue *eq,
+ uint32_t count, bool arm);
+ void (*sli4_write_cq_db)(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ uint32_t count, bool arm);
+
/* Pointers to the constructed SLI4 queues */
- struct lpfc_queue **fp_eq; /* Fast-path event queue */
- struct lpfc_queue *sp_eq; /* Slow-path event queue */
- struct lpfc_queue **fcp_wq;/* Fast-path FCP work queue */
+ struct lpfc_sli4_hdw_queue *hdwq;
+ struct list_head lpfc_wq_list;
+
+ /* Pointers to the constructed SLI4 queues for NVMET */
+ struct lpfc_queue **nvmet_cqset; /* Fast-path NVMET CQ Set queues */
+ struct lpfc_queue **nvmet_mrq_hdr; /* Fast-path NVMET hdr MRQs */
+ struct lpfc_queue **nvmet_mrq_data; /* Fast-path NVMET data MRQs */
+
+ struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */
+ struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */
+ struct lpfc_queue *nvmels_cq; /* NVME LS complete queue */
struct lpfc_queue *mbx_wq; /* Slow-path MBOX work queue */
struct lpfc_queue *els_wq; /* Slow-path ELS work queue */
+ struct lpfc_queue *nvmels_wq; /* NVME LS work queue */
struct lpfc_queue *hdr_rq; /* Slow-path Header Receive queue */
struct lpfc_queue *dat_rq; /* Slow-path Data Receive queue */
- struct lpfc_queue **fcp_cq;/* Fast-path FCP compl queue */
- struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */
- struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */
+
+ struct lpfc_name wwnn;
+ struct lpfc_name wwpn;
+
+ uint32_t fw_func_mode; /* FW function protocol mode */
+
+ /* Optimized Access Storage specific queues/structures */
+ uint64_t oas_next_lun;
+ uint8_t oas_next_tgt_wwpn[8];
+ uint8_t oas_next_vpt_wwpn[8];
/* Setup information for various queue parameters */
int eq_esize;
@@ -452,16 +894,26 @@ struct lpfc_sli4_hba {
uint16_t rpi_hdrs_in_use; /* must post rpi hdrs if set. */
uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */
uint16_t next_rpi;
- uint16_t scsi_xri_max;
- uint16_t scsi_xri_cnt;
- uint16_t scsi_xri_start;
- struct list_head lpfc_free_sgl_list;
- struct list_head lpfc_sgl_list;
- struct lpfc_sglq **lpfc_els_sgl_array;
+ uint16_t io_xri_max;
+ uint16_t io_xri_cnt;
+ uint16_t io_xri_start;
+ uint16_t els_xri_cnt;
+ uint16_t nvmet_xri_cnt;
+ uint16_t nvmet_io_wait_cnt;
+ uint16_t nvmet_io_wait_total;
+ uint16_t cq_max;
+ struct lpfc_queue **cq_lookup;
+ struct list_head lpfc_els_sgl_list;
struct list_head lpfc_abts_els_sgl_list;
- struct lpfc_scsi_buf **lpfc_scsi_psb_array;
- struct list_head lpfc_abts_scsi_buf_list;
- uint32_t total_sglq_bufs;
+ spinlock_t abts_io_buf_list_lock; /* list of aborted SCSI IOs */
+ struct list_head lpfc_abts_io_buf_list;
+ struct list_head lpfc_nvmet_sgl_list;
+ spinlock_t abts_nvmet_buf_list_lock; /* list of aborted NVMET IOs */
+ struct list_head lpfc_abts_nvmet_ctx_list;
+ spinlock_t t_active_list_lock; /* list of active NVMET IOs */
+ struct list_head t_active_ctx_list;
+ struct list_head lpfc_nvmet_io_wait_list;
+ struct lpfc_nvmet_ctx_info *nvmet_ctx_info;
struct lpfc_sglq **lpfc_sglq_active_list;
struct list_head lpfc_rpi_hdr_list;
unsigned long *rpi_bmask;
@@ -470,7 +922,6 @@ struct lpfc_sli4_hba {
struct list_head lpfc_rpi_blk_list;
unsigned long *xri_bmask;
uint16_t *xri_ids;
- uint16_t xri_count;
struct list_head lpfc_xri_blk_list;
unsigned long *vfi_bmask;
uint16_t *vfi_ids;
@@ -480,18 +931,67 @@ struct lpfc_sli4_hba {
struct list_head sp_queue_event;
struct list_head sp_cqe_event_pool;
struct list_head sp_asynce_work_queue;
- struct list_head sp_fcp_xri_aborted_work_queue;
+ spinlock_t asynce_list_lock; /* protect sp_asynce_work_queue list */
struct list_head sp_els_xri_aborted_work_queue;
+ spinlock_t els_xri_abrt_list_lock; /* protect els_xri_aborted list */
struct list_head sp_unsol_work_queue;
struct lpfc_sli4_link link_state;
+ struct lpfc_sli4_lnk_info lnk_info;
+ uint32_t pport_name_sta;
+#define LPFC_SLI4_PPNAME_NON 0
+#define LPFC_SLI4_PPNAME_GET 1
struct lpfc_iov iov;
- spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
- spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */
+ spinlock_t sgl_list_lock; /* list of aborted els IOs */
+ spinlock_t nvmet_io_wait_lock; /* IOs waiting for ctx resources */
+ uint32_t physical_port;
+
+ /* CPU to vector mapping information */
+ struct lpfc_vector_map_info *cpu_map;
+ uint16_t num_possible_cpu;
+ uint16_t num_present_cpu;
+ struct cpumask irq_aff_mask;
+ uint16_t curr_disp_cpu;
+ struct lpfc_eq_intr_info __percpu *eq_info;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ struct lpfc_hdwq_stat __percpu *c_stat;
+#endif
+ struct lpfc_idle_stat *idle_stat;
+ uint32_t conf_trunk;
+#define lpfc_conf_trunk_port0_WORD conf_trunk
+#define lpfc_conf_trunk_port0_SHIFT 0
+#define lpfc_conf_trunk_port0_MASK 0x1
+#define lpfc_conf_trunk_port1_WORD conf_trunk
+#define lpfc_conf_trunk_port1_SHIFT 1
+#define lpfc_conf_trunk_port1_MASK 0x1
+#define lpfc_conf_trunk_port2_WORD conf_trunk
+#define lpfc_conf_trunk_port2_SHIFT 2
+#define lpfc_conf_trunk_port2_MASK 0x1
+#define lpfc_conf_trunk_port3_WORD conf_trunk
+#define lpfc_conf_trunk_port3_SHIFT 3
+#define lpfc_conf_trunk_port3_MASK 0x1
+#define lpfc_conf_trunk_port0_nd_WORD conf_trunk
+#define lpfc_conf_trunk_port0_nd_SHIFT 4
+#define lpfc_conf_trunk_port0_nd_MASK 0x1
+#define lpfc_conf_trunk_port1_nd_WORD conf_trunk
+#define lpfc_conf_trunk_port1_nd_SHIFT 5
+#define lpfc_conf_trunk_port1_nd_MASK 0x1
+#define lpfc_conf_trunk_port2_nd_WORD conf_trunk
+#define lpfc_conf_trunk_port2_nd_SHIFT 6
+#define lpfc_conf_trunk_port2_nd_MASK 0x1
+#define lpfc_conf_trunk_port3_nd_WORD conf_trunk
+#define lpfc_conf_trunk_port3_nd_SHIFT 7
+#define lpfc_conf_trunk_port3_nd_MASK 0x1
+ uint8_t flash_id;
+ uint8_t asic_rev;
+ uint16_t fawwpn_flag; /* FA-WWPN support state */
+#define LPFC_FAWWPN_CONFIG 0x1 /* FA-PWWN is configured */
+#define LPFC_FAWWPN_FABRIC 0x2 /* FA-PWWN success with Fabric */
};
enum lpfc_sge_type {
GEN_BUFF_TYPE,
- SCSI_BUFF_TYPE
+ SCSI_BUFF_TYPE,
+ NVMET_BUFF_TYPE
};
enum lpfc_sgl_state {
@@ -521,6 +1021,7 @@ struct lpfc_rpi_hdr {
struct lpfc_dmabuf *dmabuf;
uint32_t page_count;
uint32_t start_rpi;
+ uint16_t next_rpi;
};
struct lpfc_rsrc_blks {
@@ -530,10 +1031,33 @@ struct lpfc_rsrc_blks {
uint16_t rsrc_used;
};
+struct lpfc_rdp_context {
+ struct lpfc_nodelist *ndlp;
+ uint16_t ox_id;
+ uint16_t rx_id;
+ READ_LNK_VAR link_stat;
+ uint8_t page_a0[DMP_SFF_PAGE_A0_SIZE];
+ uint8_t page_a2[DMP_SFF_PAGE_A2_SIZE];
+ void (*cmpl)(struct lpfc_hba *, struct lpfc_rdp_context*, int);
+};
+
+struct lpfc_lcb_context {
+ uint8_t sub_command;
+ uint8_t type;
+ uint8_t capability;
+ uint8_t frequency;
+ uint16_t duration;
+ uint16_t ox_id;
+ uint16_t rx_id;
+ struct lpfc_nodelist *ndlp;
+};
+
+
/*
* SLI4 specific function prototypes
*/
int lpfc_pci_function_reset(struct lpfc_hba *);
+int lpfc_sli4_pdev_status_reg_wait(struct lpfc_hba *);
int lpfc_sli4_hba_setup(struct lpfc_hba *);
int lpfc_sli4_config(struct lpfc_hba *, struct lpfcMboxq *, uint8_t,
uint8_t, uint32_t, bool);
@@ -545,35 +1069,41 @@ int lpfc_sli4_mbx_read_fcf_rec(struct lpfc_hba *, struct lpfcMboxq *,
uint16_t);
void lpfc_sli4_hba_reset(struct lpfc_hba *);
-struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t,
- uint32_t);
+struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *phba,
+ uint32_t page_size,
+ uint32_t entry_size,
+ uint32_t entry_count, int cpu);
void lpfc_sli4_queue_free(struct lpfc_queue *);
-uint32_t lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint16_t);
-uint32_t lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *,
+int lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint32_t);
+void lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq,
+ uint32_t numq, uint32_t usdelay);
+int lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_queue *, uint32_t, uint32_t);
+int lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
+ struct lpfc_sli4_hdw_queue *hdwq, uint32_t type,
+ uint32_t subtype);
int32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_queue *, uint32_t);
-uint32_t lpfc_wq_create(struct lpfc_hba *, struct lpfc_queue *,
+int lpfc_wq_create(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_queue *, uint32_t);
-uint32_t lpfc_rq_create(struct lpfc_hba *, struct lpfc_queue *,
+int lpfc_rq_create(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_queue *, struct lpfc_queue *, uint32_t);
-uint32_t lpfc_eq_destroy(struct lpfc_hba *, struct lpfc_queue *);
-uint32_t lpfc_cq_destroy(struct lpfc_hba *, struct lpfc_queue *);
-uint32_t lpfc_mq_destroy(struct lpfc_hba *, struct lpfc_queue *);
-uint32_t lpfc_wq_destroy(struct lpfc_hba *, struct lpfc_queue *);
-uint32_t lpfc_rq_destroy(struct lpfc_hba *, struct lpfc_queue *,
+int lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp,
+ struct lpfc_queue **drqp, struct lpfc_queue **cqp,
+ uint32_t subtype);
+int lpfc_eq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+int lpfc_cq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+int lpfc_mq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+int lpfc_wq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+int lpfc_rq_destroy(struct lpfc_hba *, struct lpfc_queue *,
struct lpfc_queue *);
int lpfc_sli4_queue_setup(struct lpfc_hba *);
void lpfc_sli4_queue_unset(struct lpfc_hba *);
int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t);
-int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *);
+int lpfc_repost_io_sgl_list(struct lpfc_hba *phba);
uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *);
+void lpfc_sli4_free_xri(struct lpfc_hba *, int);
int lpfc_sli4_post_async_mbox(struct lpfc_hba *);
-int lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba);
-int lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba);
-int lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *, struct list_head *, int);
-int lpfc_sli4_post_scsi_sgl_blk_ext(struct lpfc_hba *, struct list_head *,
- int);
struct lpfc_cq_event *__lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
struct lpfc_cq_event *lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
void __lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *);
@@ -588,11 +1118,19 @@ void lpfc_sli4_free_rpi(struct lpfc_hba *, int);
void lpfc_sli4_remove_rpis(struct lpfc_hba *);
void lpfc_sli4_async_event_proc(struct lpfc_hba *);
void lpfc_sli4_fcf_redisc_event_proc(struct lpfc_hba *);
-int lpfc_sli4_resume_rpi(struct lpfc_nodelist *);
-void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *);
-void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *);
-void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *,
- struct sli4_wcqe_xri_aborted *);
+int lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp,
+ void (*cmpl)(struct lpfc_hba *, LPFC_MBOXQ_t *),
+ struct lpfc_iocbq *iocbq);
+void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *phba);
+void lpfc_sli4_nvme_pci_offline_aborted(struct lpfc_hba *phba,
+ struct lpfc_io_buf *lpfc_ncmd);
+void lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri,
+ struct lpfc_io_buf *lpfc_ncmd);
+void lpfc_sli4_io_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri, int idx);
+void lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba,
+ struct sli4_wcqe_xri_aborted *axri);
void lpfc_sli4_els_xri_aborted(struct lpfc_hba *,
struct sli4_wcqe_xri_aborted *);
void lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *);
@@ -601,9 +1139,18 @@ int lpfc_sli4_brdreset(struct lpfc_hba *);
int lpfc_sli4_add_fcf_record(struct lpfc_hba *, struct fcf_record *);
void lpfc_sli_remove_dflt_fcf(struct lpfc_hba *);
int lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *);
+int lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba);
int lpfc_sli4_init_vpi(struct lpfc_vport *);
-uint32_t lpfc_sli4_cq_release(struct lpfc_queue *, bool);
-uint32_t lpfc_sli4_eq_release(struct lpfc_queue *, bool);
+void lpfc_sli4_eq_clr_intr(struct lpfc_queue *);
+void lpfc_sli4_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
+void lpfc_sli4_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
+void lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q);
+void lpfc_sli4_if6_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
+void lpfc_sli4_if6_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
+ uint32_t count, bool arm);
void lpfc_sli4_fcfi_unreg(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_rr_read_fcf_rec(struct lpfc_hba *, uint16_t);
@@ -613,5 +1160,41 @@ void lpfc_mbx_cmpl_fcf_rr_read_fcf_rec(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_read_fcf_rec(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_sli4_unregister_fcf(struct lpfc_hba *);
int lpfc_sli4_post_status_check(struct lpfc_hba *);
-uint8_t lpfc_sli4_mbox_opcode_get(struct lpfc_hba *, struct lpfcMboxq *);
+uint8_t lpfc_sli_config_mbox_subsys_get(struct lpfc_hba *, LPFC_MBOXQ_t *);
+uint8_t lpfc_sli_config_mbox_opcode_get(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_sli4_ras_dma_free(struct lpfc_hba *phba);
+struct sli4_hybrid_sgl *lpfc_get_sgl_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_io_buf *buf);
+struct fcp_cmd_rsp_buf *lpfc_get_cmd_rsp_buf_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_io_buf *buf);
+int lpfc_put_sgl_per_hdwq(struct lpfc_hba *phba, struct lpfc_io_buf *buf);
+int lpfc_put_cmd_rsp_buf_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_io_buf *buf);
+void lpfc_free_sgl_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_sli4_hdw_queue *hdwq);
+void lpfc_free_cmd_rsp_buf_per_hdwq(struct lpfc_hba *phba,
+ struct lpfc_sli4_hdw_queue *hdwq);
+static inline void *lpfc_sli4_qe(struct lpfc_queue *q, uint16_t idx)
+{
+ return q->q_pgs[idx / q->entry_cnt_per_pg] +
+ (q->entry_size * (idx % q->entry_cnt_per_pg));
+}
+/**
+ * lpfc_sli4_unrecoverable_port - Check ERR and RN bits in portstat_reg
+ * @portstat_reg: portstat_reg pointer containing portstat_reg contents
+ *
+ * Description:
+ * Use only for SLI4 interface type-2 or later. If ERR is set && RN is 0, then
+ * port is deemed unrecoverable.
+ *
+ * Returns:
+ * true - ERR && !RN
+ * false - otherwise
+ */
+static inline bool
+lpfc_sli4_unrecoverable_port(struct lpfc_register *portstat_reg)
+{
+ return bf_get(lpfc_sliport_status_err, portstat_reg) &&
+ !bf_get(lpfc_sliport_status_rn, portstat_reg);
+}
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index c03921b1232c..f3dada5bf7c1 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of version 2 of the GNU General *
@@ -18,11 +20,18 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.23"
+#define LPFC_DRIVER_VERSION "14.4.0.12"
#define LPFC_DRIVER_NAME "lpfc"
+
+/* Used for SLI 2/3 */
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
#define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp"
+/* Used for SLI4 */
+#define LPFC_DRIVER_HANDLER_NAME "lpfc:"
+
#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \
LPFC_DRIVER_VERSION
-#define LPFC_COPYRIGHT "Copyright(c) 2004-2009 Emulex. All rights reserved."
+#define LPFC_COPYRIGHT "Copyright (C) 2017-2025 Broadcom. All Rights " \
+ "Reserved. The term \"Broadcom\" refers to Broadcom Inc. " \
+ "and/or its subsidiaries."
diff --git a/drivers/scsi/lpfc/lpfc_vmid.c b/drivers/scsi/lpfc/lpfc_vmid.c
new file mode 100644
index 000000000000..14dbfe954e42
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_vmid.c
@@ -0,0 +1,325 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.broadcom.com *
+ * Portions Copyright (C) 2004-2005 Christoph Hellwig *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *******************************************************************/
+
+#include <linux/interrupt.h>
+#include <linux/dma-direction.h>
+
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw4.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
+#include "lpfc_nl.h"
+#include "lpfc_disc.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+
+
+/*
+ * lpfc_get_vmid_from_hashtable - search the UUID in the hash table
+ * @vport: The virtual port for which this call is being executed.
+ * @hash: calculated hash value
+ * @buf: uuid associated with the VE
+ * Return the VMID entry associated with the UUID
+ * Make sure to acquire the appropriate lock before invoking this routine.
+ */
+struct lpfc_vmid *lpfc_get_vmid_from_hashtable(struct lpfc_vport *vport,
+ u32 hash, u8 *buf)
+{
+ struct lpfc_vmid *vmp;
+
+ hash_for_each_possible(vport->hash_table, vmp, hnode, hash) {
+ if (memcmp(&vmp->host_vmid[0], buf, 16) == 0)
+ return vmp;
+ }
+ return NULL;
+}
+
+/*
+ * lpfc_put_vmid_in_hashtable - put the VMID in the hash table
+ * @vport: The virtual port for which this call is being executed.
+ * @hash - calculated hash value
+ * @vmp: Pointer to a VMID entry representing a VM sending I/O
+ *
+ * This routine will insert the newly acquired VMID entity in the hash table.
+ * Make sure to acquire the appropriate lock before invoking this routine.
+ */
+static void
+lpfc_put_vmid_in_hashtable(struct lpfc_vport *vport, u32 hash,
+ struct lpfc_vmid *vmp)
+{
+ hash_add(vport->hash_table, &vmp->hnode, hash);
+}
+
+/*
+ * lpfc_vmid_hash_fn - create a hash value of the UUID
+ * @vmid: uuid associated with the VE
+ * @len: length of the VMID string
+ * Returns the calculated hash value
+ */
+int lpfc_vmid_hash_fn(const char *vmid, int len)
+{
+ int c;
+ int hash = 0;
+
+ if (len == 0)
+ return 0;
+ while (len--) {
+ c = *vmid++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+
+ hash = (hash + (c << LPFC_VMID_HASH_SHIFT) +
+ (c >> LPFC_VMID_HASH_SHIFT)) * 19;
+ }
+
+ return hash & LPFC_VMID_HASH_MASK;
+}
+
+/*
+ * lpfc_vmid_update_entry - update the vmid entry in the hash table
+ * @vport: The virtual port for which this call is being executed.
+ * @iodir: io direction
+ * @vmp: Pointer to a VMID entry representing a VM sending I/O
+ * @tag: VMID tag
+ */
+static void lpfc_vmid_update_entry(struct lpfc_vport *vport,
+ enum dma_data_direction iodir,
+ struct lpfc_vmid *vmp,
+ union lpfc_vmid_io_tag *tag)
+{
+ u64 *lta;
+
+ if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
+ tag->cs_ctl_vmid = vmp->un.cs_ctl_vmid;
+ else if (vport->phba->cfg_vmid_app_header)
+ tag->app_id = vmp->un.app_id;
+
+ if (iodir == DMA_TO_DEVICE)
+ vmp->io_wr_cnt++;
+ else if (iodir == DMA_FROM_DEVICE)
+ vmp->io_rd_cnt++;
+
+ /* update the last access timestamp in the table */
+ lta = per_cpu_ptr(vmp->last_io_time, raw_smp_processor_id());
+ *lta = jiffies;
+}
+
+static void lpfc_vmid_assign_cs_ctl(struct lpfc_vport *vport,
+ struct lpfc_vmid *vmid)
+{
+ u32 hash;
+ struct lpfc_vmid *pvmid;
+
+ if (vport->port_type == LPFC_PHYSICAL_PORT) {
+ vmid->un.cs_ctl_vmid = lpfc_vmid_get_cs_ctl(vport);
+ } else {
+ hash = lpfc_vmid_hash_fn(vmid->host_vmid, vmid->vmid_len);
+ pvmid =
+ lpfc_get_vmid_from_hashtable(vport->phba->pport, hash,
+ vmid->host_vmid);
+ if (pvmid)
+ vmid->un.cs_ctl_vmid = pvmid->un.cs_ctl_vmid;
+ else
+ vmid->un.cs_ctl_vmid = lpfc_vmid_get_cs_ctl(vport);
+ }
+}
+
+/*
+ * lpfc_vmid_get_appid - get the VMID associated with the UUID
+ * @vport: The virtual port for which this call is being executed.
+ * @uuid: UUID associated with the VE
+ * @cmd: address of scsi_cmd descriptor
+ * @iodir: io direction
+ * @tag: VMID tag
+ * Returns status of the function
+ */
+int lpfc_vmid_get_appid(struct lpfc_vport *vport, char *uuid,
+ enum dma_data_direction iodir,
+ union lpfc_vmid_io_tag *tag)
+{
+ struct lpfc_vmid *vmp = NULL;
+ int hash, len, rc = -EPERM, i;
+
+ /* check if QFPA is complete */
+ if (lpfc_vmid_is_type_priority_tag(vport) &&
+ !(vport->vmid_flag & LPFC_VMID_QFPA_CMPL) &&
+ (vport->vmid_flag & LPFC_VMID_ISSUE_QFPA)) {
+ vport->work_port_events |= WORKER_CHECK_VMID_ISSUE_QFPA;
+ return -EAGAIN;
+ }
+
+ /* search if the UUID has already been mapped to the VMID */
+ len = strlen(uuid);
+ hash = lpfc_vmid_hash_fn(uuid, len);
+
+ /* search for the VMID in the table */
+ read_lock(&vport->vmid_lock);
+ vmp = lpfc_get_vmid_from_hashtable(vport, hash, uuid);
+
+ /* if found, check if its already registered */
+ if (vmp && vmp->flag & LPFC_VMID_REGISTERED) {
+ read_unlock(&vport->vmid_lock);
+ lpfc_vmid_update_entry(vport, iodir, vmp, tag);
+ rc = 0;
+ } else if (vmp && (vmp->flag & LPFC_VMID_REQ_REGISTER ||
+ vmp->flag & LPFC_VMID_DE_REGISTER)) {
+ /* else if register or dereg request has already been sent */
+ /* Hence VMID tag will not be added for this I/O */
+ read_unlock(&vport->vmid_lock);
+ rc = -EBUSY;
+ } else {
+ /* The VMID was not found in the hashtable. At this point, */
+ /* drop the read lock first before proceeding further */
+ read_unlock(&vport->vmid_lock);
+ /* start the process to obtain one as per the */
+ /* type of the VMID indicated */
+ write_lock(&vport->vmid_lock);
+ vmp = lpfc_get_vmid_from_hashtable(vport, hash, uuid);
+
+ /* while the read lock was released, in case the entry was */
+ /* added by other context or is in process of being added */
+ if (vmp && vmp->flag & LPFC_VMID_REGISTERED) {
+ lpfc_vmid_update_entry(vport, iodir, vmp, tag);
+ write_unlock(&vport->vmid_lock);
+ return 0;
+ } else if (vmp && vmp->flag & LPFC_VMID_REQ_REGISTER) {
+ write_unlock(&vport->vmid_lock);
+ return -EBUSY;
+ }
+
+ /* else search and allocate a free slot in the hash table */
+ if (vport->cur_vmid_cnt < vport->max_vmid) {
+ for (i = 0; i < vport->max_vmid; i++) {
+ vmp = vport->vmid + i;
+ if (vmp->flag == LPFC_VMID_SLOT_FREE)
+ break;
+ }
+ if (i == vport->max_vmid)
+ vmp = NULL;
+ } else {
+ vmp = NULL;
+ }
+
+ if (!vmp) {
+ write_unlock(&vport->vmid_lock);
+ return -ENOMEM;
+ }
+
+ /* Add the vmid and register */
+ lpfc_put_vmid_in_hashtable(vport, hash, vmp);
+ vmp->vmid_len = len;
+ memcpy(vmp->host_vmid, uuid, vmp->vmid_len);
+ vmp->io_rd_cnt = 0;
+ vmp->io_wr_cnt = 0;
+ vmp->flag = LPFC_VMID_SLOT_USED;
+
+ vmp->delete_inactive =
+ vport->vmid_inactivity_timeout ? 1 : 0;
+
+ /* if type priority tag, get next available VMID */
+ if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
+ lpfc_vmid_assign_cs_ctl(vport, vmp);
+
+ /* allocate the per cpu variable for holding */
+ /* the last access time stamp only if VMID is enabled */
+ if (!vmp->last_io_time)
+ vmp->last_io_time = alloc_percpu_gfp(u64, GFP_ATOMIC);
+ if (!vmp->last_io_time) {
+ hash_del(&vmp->hnode);
+ vmp->flag = LPFC_VMID_SLOT_FREE;
+ write_unlock(&vport->vmid_lock);
+ return -EIO;
+ }
+
+ write_unlock(&vport->vmid_lock);
+
+ /* complete transaction with switch */
+ if (vport->phba->pport->vmid_flag & LPFC_VMID_TYPE_PRIO)
+ rc = lpfc_vmid_uvem(vport, vmp, true);
+ else if (vport->phba->cfg_vmid_app_header)
+ rc = lpfc_vmid_cmd(vport, SLI_CTAS_RAPP_IDENT, vmp);
+ if (!rc) {
+ write_lock(&vport->vmid_lock);
+ vport->cur_vmid_cnt++;
+ vmp->flag |= LPFC_VMID_REQ_REGISTER;
+ write_unlock(&vport->vmid_lock);
+ } else {
+ write_lock(&vport->vmid_lock);
+ hash_del(&vmp->hnode);
+ vmp->flag = LPFC_VMID_SLOT_FREE;
+ free_percpu(vmp->last_io_time);
+ write_unlock(&vport->vmid_lock);
+ return -EIO;
+ }
+
+ /* finally, enable the idle timer once */
+ if (!(vport->phba->pport->vmid_flag & LPFC_VMID_TIMER_ENBLD)) {
+ mod_timer(&vport->phba->inactive_vmid_poll,
+ jiffies +
+ secs_to_jiffies(LPFC_VMID_TIMER));
+ vport->phba->pport->vmid_flag |= LPFC_VMID_TIMER_ENBLD;
+ }
+ }
+ return rc;
+}
+
+/*
+ * lpfc_reinit_vmid - reinitializes the vmid data structure
+ * @vport: pointer to vport data structure
+ *
+ * This routine reinitializes the vmid post flogi completion
+ *
+ * Return codes
+ * None
+ */
+void
+lpfc_reinit_vmid(struct lpfc_vport *vport)
+{
+ u32 bucket, i, cpu;
+ struct lpfc_vmid *cur;
+ struct lpfc_vmid *vmp = NULL;
+ struct hlist_node *tmp;
+
+ write_lock(&vport->vmid_lock);
+ vport->cur_vmid_cnt = 0;
+
+ for (i = 0; i < vport->max_vmid; i++) {
+ vmp = &vport->vmid[i];
+ vmp->flag = LPFC_VMID_SLOT_FREE;
+ memset(vmp->host_vmid, 0, sizeof(vmp->host_vmid));
+ vmp->io_rd_cnt = 0;
+ vmp->io_wr_cnt = 0;
+
+ if (vmp->last_io_time)
+ for_each_possible_cpu(cpu)
+ *per_cpu_ptr(vmp->last_io_time, cpu) = 0;
+ }
+
+ /* for all elements in the hash table */
+ if (!hash_empty(vport->hash_table))
+ hash_for_each_safe(vport->hash_table, bucket, tmp, cur, hnode)
+ hash_del(&cur->hnode);
+ write_unlock(&vport->vmid_lock);
+}
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 1feb551a57bc..aff6c9d5e7c2 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2008 Emulex. All rights reserved. *
+ * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
+ * Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -28,11 +30,13 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/sched/signal.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
@@ -80,7 +84,7 @@ inline void lpfc_vport_set_state(struct lpfc_vport *vport,
}
}
-static int
+int
lpfc_alloc_vpi(struct lpfc_hba *phba)
{
unsigned long vpi;
@@ -131,47 +135,44 @@ lpfc_vport_sparm(struct lpfc_hba *phba, struct lpfc_vport *vport)
}
/*
- * Grab buffer pointer and clear context1 so we can use
- * lpfc_sli_issue_box_wait
+ * Wait for the read_sparams mailbox to complete. Driver needs
+ * this per vport to start the FDISC. If the mailbox fails,
+ * just cleanup and return an error unless the failure is a
+ * mailbox timeout. For MBX_TIMEOUT, allow the default
+ * mbox completion handler to take care of the cleanup. This
+ * is safe as the mailbox command isn't one that triggers
+ * another mailbox.
*/
- mp = (struct lpfc_dmabuf *) pmb->context1;
- pmb->context1 = NULL;
-
pmb->vport = vport;
rc = lpfc_sli_issue_mbox_wait(phba, pmb, phba->fc_ratov * 2);
if (rc != MBX_SUCCESS) {
if (signal_pending(current)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT | LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1830 Signal aborted mbxCmd x%x\n",
mb->mbxCommand);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
if (rc != MBX_TIMEOUT)
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb,
+ MBOX_THD_UNLOCKED);
return -EINTR;
} else {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT | LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1818 VPort failed init, mbxCmd x%x "
"READ_SPARM mbxStatus x%x, rc = x%x\n",
mb->mbxCommand, mb->mbxStatus, rc);
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
if (rc != MBX_TIMEOUT)
- mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_mbox_rsrc_cleanup(phba, pmb,
+ MBOX_THD_UNLOCKED);
return -EIO;
}
}
+ mp = pmb->ctx_buf;
memcpy(&vport->fc_sparam, mp->virt, sizeof (struct serv_parm));
memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName,
sizeof (struct lpfc_name));
memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
sizeof (struct lpfc_name));
-
- lpfc_mbuf_free(phba, mp->virt, mp->phys);
- kfree(mp);
- mempool_free(pmb, phba->mbox_mem_pool);
-
+ lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
return 0;
}
@@ -203,7 +204,7 @@ lpfc_unique_wwpn(struct lpfc_hba *phba, struct lpfc_vport *new_vport)
struct lpfc_vport *vport;
unsigned long flags;
- spin_lock_irqsave(&phba->hbalock, flags);
+ spin_lock_irqsave(&phba->port_list_lock, flags);
list_for_each_entry(vport, &phba->port_list, listentry) {
if (vport == new_vport)
continue;
@@ -211,11 +212,11 @@ lpfc_unique_wwpn(struct lpfc_hba *phba, struct lpfc_vport *new_vport)
if (memcmp(&vport->fc_sparam.portName,
&new_vport->fc_sparam.portName,
sizeof(struct lpfc_name)) == 0) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->port_list_lock, flags);
return 0;
}
}
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->port_list_lock, flags);
return 1;
}
@@ -237,31 +238,32 @@ lpfc_unique_wwpn(struct lpfc_hba *phba, struct lpfc_vport *new_vport)
static void lpfc_discovery_wait(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
- uint32_t wait_flags = 0;
unsigned long wait_time_max;
unsigned long start_time;
- wait_flags = FC_RSCN_MODE | FC_RSCN_DISCOVERY | FC_NLP_MORE |
- FC_RSCN_DEFERRED | FC_NDISC_ACTIVE | FC_DISC_TMO;
-
/*
* The time constraint on this loop is a balance between the
* fabric RA_TOV value and dev_loss tmo. The driver's
* devloss_tmo is 10 giving this loop a 3x multiplier minimally.
*/
- wait_time_max = msecs_to_jiffies(((phba->fc_ratov * 3) + 3) * 1000);
+ wait_time_max = secs_to_jiffies((phba->fc_ratov * 3) + 3);
wait_time_max += jiffies;
start_time = jiffies;
while (time_before(jiffies, wait_time_max)) {
if ((vport->num_disc_nodes > 0) ||
- (vport->fc_flag & wait_flags) ||
+ test_bit(FC_RSCN_MODE, &vport->fc_flag) ||
+ test_bit(FC_RSCN_DISCOVERY, &vport->fc_flag) ||
+ test_bit(FC_NLP_MORE, &vport->fc_flag) ||
+ test_bit(FC_RSCN_DEFERRED, &vport->fc_flag) ||
+ test_bit(FC_NDISC_ACTIVE, &vport->fc_flag) ||
+ test_bit(FC_DISC_TMO, &vport->fc_flag) ||
((vport->port_state > LPFC_VPORT_FAILED) &&
(vport->port_state < LPFC_VPORT_READY))) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT,
- "1833 Vport discovery quiesce Wait:"
- " state x%x fc_flags x%x"
- " num_nodes x%x, waiting 1000 msecs"
- " total wait msecs x%x\n",
+ "1833 Vport discovery quiesce Wait: "
+ "state x%x fc_flags x%lx "
+ "num_nodes x%x, waiting 1000 msecs "
+ "total wait msecs x%x\n",
vport->port_state, vport->fc_flag,
vport->num_disc_nodes,
jiffies_to_msecs(jiffies - start_time));
@@ -269,9 +271,9 @@ static void lpfc_discovery_wait(struct lpfc_vport *vport)
} else {
/* Base case. Wait variants satisfied. Break out */
lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT,
- "1834 Vport discovery quiesced:"
- " state x%x fc_flags x%x"
- " wait msecs x%x\n",
+ "1834 Vport discovery quiesced: "
+ "state x%x fc_flags x%lx "
+ "wait msecs x%x\n",
vport->port_state, vport->fc_flag,
jiffies_to_msecs(jiffies
- start_time));
@@ -280,11 +282,11 @@ static void lpfc_discovery_wait(struct lpfc_vport *vport)
}
if (time_after(jiffies, wait_time_max))
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
- "1835 Vport discovery quiesce failed:"
- " state x%x fc_flags x%x wait msecs x%x\n",
- vport->port_state, vport->fc_flag,
- jiffies_to_msecs(jiffies - start_time));
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
+ "1835 Vport discovery quiesce failed:"
+ " state x%x fc_flags x%lx wait msecs x%x\n",
+ vport->port_state, vport->fc_flag,
+ jiffies_to_msecs(jiffies - start_time));
}
int
@@ -301,7 +303,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
int status;
if ((phba->sli_rev < 3) || !(phba->cfg_enable_npiv)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1808 Create VPORT failed: "
"NPIV is not enabled: SLImode:%d\n",
phba->sli_rev);
@@ -309,9 +311,18 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
+ /* NPIV is not supported if HBA has NVME Target enabled */
+ if (phba->nvmet_support) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
+ "3189 Create VPORT failed: "
+ "NPIV is not supported on NVME Target\n");
+ rc = VPORT_INVAL;
+ goto error_out;
+ }
+
vpi = lpfc_alloc_vpi(phba);
if (vpi == 0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1809 Create VPORT failed: "
"Max VPORTs (%d) exceeded\n",
phba->max_vpi);
@@ -321,7 +332,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
/* Assign an unused board number */
if ((instance = lpfc_get_instance()) < 0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1810 Create VPORT failed: Cannot get "
"instance number\n");
lpfc_free_vpi(phba, vpi);
@@ -331,7 +342,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
vport = lpfc_create_port(phba, instance, &fc_vport->dev);
if (!vport) {
- lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1811 Create VPORT failed: vpi x%x\n", vpi);
lpfc_free_vpi(phba, vpi);
rc = VPORT_NORESOURCES;
@@ -343,11 +354,11 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
if ((status = lpfc_vport_sparm(phba, vport))) {
if (status == -EINTR) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1831 Create VPORT Interrupted.\n");
rc = VPORT_ERROR;
} else {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1813 Create VPORT failed. "
"Cannot get sparam\n");
rc = VPORT_NORESOURCES;
@@ -365,7 +376,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
if (!lpfc_valid_wwn_format(phba, &vport->fc_sparam.nodeName, "WWNN") ||
!lpfc_valid_wwn_format(phba, &vport->fc_sparam.portName, "WWPN")) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1821 Create VPORT failed. "
"Invalid WWN format\n");
lpfc_free_vpi(phba, vpi);
@@ -375,7 +386,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
}
if (!lpfc_unique_wwpn(phba, vport)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1823 Create VPORT failed. "
"Duplicate WWN on HBA\n");
lpfc_free_vpi(phba, vpi);
@@ -387,18 +398,33 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
/* Create binary sysfs attribute for vport */
lpfc_alloc_sysfs_attr(vport);
+ /* Set the DFT_LUN_Q_DEPTH accordingly */
+ vport->cfg_lun_queue_depth = phba->pport->cfg_lun_queue_depth;
+
+ /* Only the physical port can support NVME for now */
+ vport->cfg_enable_fc4_type = LPFC_ENABLE_FCP;
+
*(struct lpfc_vport **)fc_vport->dd_data = vport;
vport->fc_vport = fc_vport;
+ /* At this point we are fully registered with SCSI Layer. */
+ set_bit(FC_ALLOW_FDMI, &vport->load_flag);
+ if (phba->cfg_enable_SmartSAN ||
+ (phba->cfg_fdmi_on == LPFC_FDMI_SUPPORT)) {
+ /* Setup appropriate attribute masks */
+ vport->fdmi_hba_mask = phba->pport->fdmi_hba_mask;
+ vport->fdmi_port_mask = phba->pport->fdmi_port_mask;
+ }
+
/*
* In SLI4, the vpi must be activated before it can be used
* by the port.
*/
if ((phba->sli_rev == LPFC_SLI_REV4) &&
- (pport->fc_flag & FC_VFI_REGISTERED)) {
+ test_bit(FC_VFI_REGISTERED, &pport->fc_flag)) {
rc = lpfc_sli4_init_vpi(vport);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
"1838 Failed to INIT_VPI on vpi %d "
"status %d\n", vpi, rc);
rc = VPORT_NORESOURCES;
@@ -410,7 +436,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
* Driver cannot INIT_VPI now. Set the flags to
* init_vpi when reg_vfi complete.
*/
- vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ set_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag);
lpfc_vport_set_state(vport, FC_VPORT_LINKDOWN);
rc = VPORT_OK;
goto out;
@@ -434,14 +460,14 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
* up and ready to FDISC.
*/
ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
+ if (ndlp &&
ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) {
lpfc_set_disctmo(vport);
lpfc_initial_fdisc(vport);
} else {
lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0262 No NPIV Fabric support\n");
}
} else {
@@ -451,45 +477,75 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
out:
lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
- "1825 Vport Created.\n");
+ "1825 Vport Created.\n");
lpfc_host_attrib_init(lpfc_shost_from_vport(vport));
error_out:
return rc;
}
static int
+lpfc_send_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+ int rc;
+ struct lpfc_hba *phba = vport->phba;
+
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
+
+ spin_lock_irq(&ndlp->lock);
+ if (!test_bit(NLP_WAIT_FOR_LOGO, &ndlp->save_flags) &&
+ !ndlp->logo_waitq) {
+ ndlp->logo_waitq = &waitq;
+ ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ set_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag);
+ set_bit(NLP_WAIT_FOR_LOGO, &ndlp->save_flags);
+ }
+ spin_unlock_irq(&ndlp->lock);
+ rc = lpfc_issue_els_npiv_logo(vport, ndlp);
+ if (!rc) {
+ wait_event_timeout(waitq,
+ !test_bit(NLP_WAIT_FOR_LOGO,
+ &ndlp->save_flags),
+ secs_to_jiffies(phba->fc_ratov * 2));
+
+ if (!test_bit(NLP_WAIT_FOR_LOGO, &ndlp->save_flags))
+ goto logo_cmpl;
+ /* LOGO wait failed. Correct status. */
+ rc = -EINTR;
+ } else {
+ rc = -EIO;
+ }
+
+ /* Error - clean up node flags. */
+ clear_bit(NLP_ISSUE_LOGO, &ndlp->nlp_flag);
+ clear_bit(NLP_WAIT_FOR_LOGO, &ndlp->save_flags);
+
+ logo_cmpl:
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT,
+ "1824 Issue LOGO completes with status %d\n",
+ rc);
+ spin_lock_irq(&ndlp->lock);
+ ndlp->logo_waitq = NULL;
+ spin_unlock_irq(&ndlp->lock);
+ return rc;
+}
+
+static int
disable_vport(struct fc_vport *fc_vport)
{
struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
struct lpfc_hba *phba = vport->phba;
- struct lpfc_nodelist *ndlp = NULL, *next_ndlp = NULL;
- long timeout;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_nodelist *ndlp = NULL;
+
+ /* Can't disable during an outstanding delete. */
+ if (test_bit(FC_UNLOADING, &vport->load_flag))
+ return 0;
ndlp = lpfc_findnode_did(vport, Fabric_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)
- && phba->link_state >= LPFC_LINK_UP) {
- vport->unreg_vpi_cmpl = VPORT_INVAL;
- timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
- if (!lpfc_issue_els_npiv_logo(vport, ndlp))
- while (vport->unreg_vpi_cmpl == VPORT_INVAL && timeout)
- timeout = schedule_timeout(timeout);
- }
+ if (ndlp && phba->link_state >= LPFC_LINK_UP)
+ (void)lpfc_send_npiv_logo(vport, ndlp);
lpfc_sli_host_down(vport);
-
- /* Mark all nodes for discovery so we can remove them by
- * calling lpfc_cleanup_rpis(vport, 1)
- */
- list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
- if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
- continue;
- lpfc_disc_state_machine(vport, ndlp, NULL,
- NLP_EVT_DEVICE_RECOVERY);
- }
- lpfc_cleanup_rpis(vport, 1);
+ lpfc_cleanup_rpis(vport, 0);
lpfc_stop_vport_timers(vport);
lpfc_unreg_all_rpis(vport);
@@ -499,9 +555,8 @@ disable_vport(struct fc_vport *fc_vport)
* scsi_host_put() to release the vport.
*/
lpfc_mbx_unreg_vpi(vport);
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
- spin_unlock_irq(shost->host_lock);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ set_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag);
lpfc_vport_set_state(vport, FC_VPORT_DISABLED);
lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
@@ -515,7 +570,6 @@ enable_vport(struct fc_vport *fc_vport)
struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp = NULL;
- struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if ((phba->link_state < LPFC_LINK_UP) ||
(phba->fc_topology == LPFC_TOPOLOGY_LOOP)) {
@@ -523,28 +577,32 @@ enable_vport(struct fc_vport *fc_vport)
return VPORT_OK;
}
- spin_lock_irq(shost->host_lock);
- vport->load_flag |= FC_LOADING;
- vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- spin_unlock_irq(shost->host_lock);
+ set_bit(FC_LOADING, &vport->load_flag);
+ if (test_bit(FC_VPORT_NEEDS_INIT_VPI, &vport->fc_flag)) {
+ lpfc_issue_init_vpi(vport);
+ goto out;
+ }
+
+ set_bit(FC_VPORT_NEEDS_REG_VPI, &vport->fc_flag);
/* Use the Physical nodes Fabric NDLP to determine if the link is
* up and ready to FDISC.
*/
ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)
- && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
+ if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) {
lpfc_set_disctmo(vport);
lpfc_initial_fdisc(vport);
} else {
lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
- lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"0264 No NPIV Fabric support\n");
}
} else {
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
}
+
+out:
lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
"1827 Vport Enabled.\n");
return VPORT_OK;
@@ -559,18 +617,18 @@ lpfc_vport_disable(struct fc_vport *fc_vport, bool disable)
return enable_vport(fc_vport);
}
-
int
lpfc_vport_delete(struct fc_vport *fc_vport)
{
struct lpfc_nodelist *ndlp = NULL;
- struct Scsi_Host *shost = (struct Scsi_Host *) fc_vport->shost;
struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
- struct lpfc_hba *phba = vport->phba;
- long timeout;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_hba *phba = vport->phba;
+ int rc;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
if (vport->port_type == LPFC_PHYSICAL_PORT) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1812 vport_delete failed: Cannot delete "
"physical host\n");
return VPORT_ERROR;
@@ -578,20 +636,20 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
/* If the vport is a static vport fail the deletion. */
if ((vport->vport_flag & STATIC_VPORT) &&
- !(phba->pport->load_flag & FC_UNLOADING)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+ !test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT,
"1837 vport_delete failed: Cannot delete "
"static vport.\n");
return VPORT_ERROR;
}
- spin_lock_irq(&phba->hbalock);
- vport->load_flag |= FC_UNLOADING;
- spin_unlock_irq(&phba->hbalock);
+
+ set_bit(FC_UNLOADING, &vport->load_flag);
+
/*
* If we are not unloading the driver then prevent the vport_delete
* from happening until after this vport's discovery is finished.
*/
- if (!(phba->pport->load_flag & FC_UNLOADING)) {
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
int check_count = 0;
while (check_count < ((phba->fc_ratov * 3) + 3) &&
vport->port_state > LPFC_VPORT_FAILED &&
@@ -603,149 +661,120 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
vport->port_state < LPFC_VPORT_READY)
return -EAGAIN;
}
+
/*
- * This is a bit of a mess. We want to ensure the shost doesn't get
- * torn down until we're done with the embedded lpfc_vport structure.
- *
- * Beyond holding a reference for this function, we also need a
- * reference for outstanding I/O requests we schedule during delete
- * processing. But once we scsi_remove_host() we can no longer obtain
- * a reference through scsi_host_get().
- *
- * So we take two references here. We release one reference at the
- * bottom of the function -- after delinking the vport. And we
- * release the other at the completion of the unreg_vpi that get's
- * initiated after we've disposed of all other resources associated
- * with the port.
+ * Take early refcount for outstanding I/O requests we schedule during
+ * delete processing for unreg_vpi. Always keep this before
+ * scsi_remove_host() as we can no longer obtain a reference through
+ * scsi_host_get() after scsi_remove_host as shost is set to SHOST_DEL.
*/
if (!scsi_host_get(shost))
return VPORT_INVAL;
- if (!scsi_host_get(shost)) {
- scsi_host_put(shost);
- return VPORT_INVAL;
- }
- lpfc_free_sysfs_attr(vport);
+ lpfc_free_sysfs_attr(vport);
lpfc_debugfs_terminate(vport);
- /* Remove FC host and then SCSI host with the vport */
- fc_remove_host(lpfc_shost_from_vport(vport));
- scsi_remove_host(lpfc_shost_from_vport(vport));
-
- ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
-
- /* In case of driver unload, we shall not perform fabric logo as the
- * worker thread already stopped at this stage and, in this case, we
- * can safely skip the fabric logo.
- */
- if (phba->pport->load_flag & FC_UNLOADING) {
- if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
- ndlp->nlp_state == NLP_STE_UNMAPPED_NODE &&
- phba->link_state >= LPFC_LINK_UP) {
- /* First look for the Fabric ndlp */
- ndlp = lpfc_findnode_did(vport, Fabric_DID);
- if (!ndlp)
- goto skip_logo;
- else if (!NLP_CHK_NODE_ACT(ndlp)) {
- ndlp = lpfc_enable_node(vport, ndlp,
- NLP_STE_UNUSED_NODE);
- if (!ndlp)
- goto skip_logo;
- }
- /* Remove ndlp from vport npld list */
- lpfc_dequeue_node(vport, ndlp);
-
- /* Indicate free memory when release */
- spin_lock_irq(&phba->ndlp_lock);
- NLP_SET_FREE_REQ(ndlp);
- spin_unlock_irq(&phba->ndlp_lock);
- /* Kick off release ndlp when it can be safely done */
- lpfc_nlp_put(ndlp);
- }
+ /* Send the DA_ID and Fabric LOGO to cleanup Nameserver entries. */
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp)
goto skip_logo;
- }
- /* Otherwise, we will perform fabric logo as needed */
- if (ndlp && NLP_CHK_NODE_ACT(ndlp) &&
- ndlp->nlp_state == NLP_STE_UNMAPPED_NODE &&
+ /* Send the DA_ID and Fabric LOGO to cleanup the NPIV fabric entries. */
+ if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE &&
phba->link_state >= LPFC_LINK_UP &&
phba->fc_topology != LPFC_TOPOLOGY_LOOP) {
if (vport->cfg_enable_da_id) {
- timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
- if (!lpfc_ns_cmd(vport, SLI_CTNS_DA_ID, 0, 0))
- while (vport->ct_flags && timeout)
- timeout = schedule_timeout(timeout);
- else
- lpfc_printf_log(vport->phba, KERN_WARNING,
- LOG_VPORT,
- "1829 CT command failed to "
- "delete objects on fabric\n");
- }
- /* First look for the Fabric ndlp */
- ndlp = lpfc_findnode_did(vport, Fabric_DID);
- if (!ndlp) {
- /* Cannot find existing Fabric ndlp, allocate one */
- ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ /* Send DA_ID and wait for a completion. This is best
+ * effort. If the DA_ID fails, likely the fabric will
+ * "leak" NportIDs but at least the driver issued the
+ * command.
+ */
+ ndlp = lpfc_findnode_did(vport, NameServer_DID);
if (!ndlp)
- goto skip_logo;
- lpfc_nlp_init(vport, ndlp, Fabric_DID);
- /* Indicate free memory when release */
- NLP_SET_FREE_REQ(ndlp);
- } else {
- if (!NLP_CHK_NODE_ACT(ndlp))
- ndlp = lpfc_enable_node(vport, ndlp,
- NLP_STE_UNUSED_NODE);
- if (!ndlp)
- goto skip_logo;
-
- /* Remove ndlp from vport npld list */
- lpfc_dequeue_node(vport, ndlp);
- spin_lock_irq(&phba->ndlp_lock);
- if (!NLP_CHK_FREE_REQ(ndlp))
- /* Indicate free memory when release */
- NLP_SET_FREE_REQ(ndlp);
- else {
- /* Skip this if ndlp is already in free mode */
- spin_unlock_irq(&phba->ndlp_lock);
- goto skip_logo;
+ goto issue_logo;
+
+ spin_lock_irq(&ndlp->lock);
+ ndlp->da_id_waitq = &waitq;
+ spin_unlock_irq(&ndlp->lock);
+ set_bit(NLP_WAIT_FOR_DA_ID, &ndlp->save_flags);
+
+ rc = lpfc_ns_cmd(vport, SLI_CTNS_DA_ID, 0, 0);
+ if (!rc) {
+ wait_event_timeout(waitq,
+ !test_bit(NLP_WAIT_FOR_DA_ID,
+ &ndlp->save_flags),
+ secs_to_jiffies(phba->fc_ratov * 2));
}
- spin_unlock_irq(&phba->ndlp_lock);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT | LOG_ELS,
+ "1829 DA_ID issue status %d. "
+ "SFlag x%lx NState x%x, NFlag x%lx "
+ "Rpi x%x\n",
+ rc, ndlp->save_flags, ndlp->nlp_state,
+ ndlp->nlp_flag, ndlp->nlp_rpi);
+
+ /* Remove the waitq and save_flags. It no
+ * longer matters if the wake happened.
+ */
+ spin_lock_irq(&ndlp->lock);
+ ndlp->da_id_waitq = NULL;
+ spin_unlock_irq(&ndlp->lock);
+ clear_bit(NLP_WAIT_FOR_DA_ID, &ndlp->save_flags);
}
+
+issue_logo:
+ /*
+ * If the vpi is not registered, then a valid FDISC doesn't
+ * exist and there is no need for a ELS LOGO. Just cleanup
+ * the ndlp.
+ */
if (!(vport->vpi_state & LPFC_VPI_REGISTERED))
goto skip_logo;
- vport->unreg_vpi_cmpl = VPORT_INVAL;
- timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
- if (!lpfc_issue_els_npiv_logo(vport, ndlp))
- while (vport->unreg_vpi_cmpl == VPORT_INVAL && timeout)
- timeout = schedule_timeout(timeout);
+
+ /* Issue a Fabric LOGO to cleanup fabric resources. */
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp)
+ goto skip_logo;
+
+ rc = lpfc_send_npiv_logo(vport, ndlp);
+ if (rc)
+ goto skip_logo;
}
- if (!(phba->pport->load_flag & FC_UNLOADING))
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag))
lpfc_discovery_wait(vport);
skip_logo:
+
+ /* Remove FC host to break driver binding. */
+ fc_remove_host(shost);
+ scsi_remove_host(shost);
+
lpfc_cleanup(vport);
- lpfc_sli_host_down(vport);
+ /* Remove scsi host now. The nodes are cleaned up. */
+ lpfc_sli_host_down(vport);
lpfc_stop_vport_timers(vport);
- if (!(phba->pport->load_flag & FC_UNLOADING)) {
+ if (!test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
lpfc_unreg_all_rpis(vport);
lpfc_unreg_default_rpis(vport);
/*
* Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi)
* does the scsi_host_put() to release the vport.
*/
- if (lpfc_mbx_unreg_vpi(vport))
+ if (!(vport->vpi_state & LPFC_VPI_REGISTERED) ||
+ lpfc_mbx_unreg_vpi(vport))
scsi_host_put(shost);
- } else
+ } else {
scsi_host_put(shost);
+ }
lpfc_free_vpi(phba, vport->vpi);
vport->work_port_events = 0;
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(&phba->port_list_lock);
list_del_init(&vport->listentry);
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(&phba->port_list_lock);
lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
"1828 Vport Deleted.\n");
scsi_host_put(shost);
@@ -758,23 +787,24 @@ lpfc_create_vport_work_array(struct lpfc_hba *phba)
struct lpfc_vport *port_iterator;
struct lpfc_vport **vports;
int index = 0;
- vports = kzalloc((phba->max_vports + 1) * sizeof(struct lpfc_vport *),
+ vports = kcalloc(phba->max_vports + 1, sizeof(struct lpfc_vport *),
GFP_KERNEL);
if (vports == NULL)
return NULL;
- spin_lock_irq(&phba->hbalock);
+ spin_lock_irq(&phba->port_list_lock);
list_for_each_entry(port_iterator, &phba->port_list, listentry) {
+ if (test_bit(FC_UNLOADING, &port_iterator->load_flag))
+ continue;
if (!scsi_host_get(lpfc_shost_from_vport(port_iterator))) {
- if (!(port_iterator->load_flag & FC_UNLOADING))
- lpfc_printf_vlog(port_iterator, KERN_ERR,
- LOG_VPORT,
+ lpfc_printf_vlog(port_iterator, KERN_ERR,
+ LOG_TRACE_EVENT,
"1801 Create vport work array FAILED: "
"cannot do scsi_host_get\n");
continue;
}
vports[index++] = port_iterator;
}
- spin_unlock_irq(&phba->hbalock);
+ spin_unlock_irq(&phba->port_list_lock);
return vports;
}
@@ -789,79 +819,3 @@ lpfc_destroy_vport_work_array(struct lpfc_hba *phba, struct lpfc_vport **vports)
kfree(vports);
}
-
-/**
- * lpfc_vport_reset_stat_data - Reset the statistical data for the vport
- * @vport: Pointer to vport object.
- *
- * This function resets the statistical data for the vport. This function
- * is called with the host_lock held
- **/
-void
-lpfc_vport_reset_stat_data(struct lpfc_vport *vport)
-{
- struct lpfc_nodelist *ndlp = NULL, *next_ndlp = NULL;
-
- list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
- if (ndlp->lat_data)
- memset(ndlp->lat_data, 0, LPFC_MAX_BUCKET_COUNT *
- sizeof(struct lpfc_scsicmd_bkt));
- }
-}
-
-
-/**
- * lpfc_alloc_bucket - Allocate data buffer required for statistical data
- * @vport: Pointer to vport object.
- *
- * This function allocates data buffer required for all the FC
- * nodes of the vport to collect statistical data.
- **/
-void
-lpfc_alloc_bucket(struct lpfc_vport *vport)
-{
- struct lpfc_nodelist *ndlp = NULL, *next_ndlp = NULL;
-
- list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
-
- kfree(ndlp->lat_data);
- ndlp->lat_data = NULL;
-
- if (ndlp->nlp_state == NLP_STE_MAPPED_NODE) {
- ndlp->lat_data = kcalloc(LPFC_MAX_BUCKET_COUNT,
- sizeof(struct lpfc_scsicmd_bkt),
- GFP_ATOMIC);
-
- if (!ndlp->lat_data)
- lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
- "0287 lpfc_alloc_bucket failed to "
- "allocate statistical data buffer DID "
- "0x%x\n", ndlp->nlp_DID);
- }
- }
-}
-
-/**
- * lpfc_free_bucket - Free data buffer required for statistical data
- * @vport: Pointer to vport object.
- *
- * Th function frees statistical data buffer of all the FC
- * nodes of the vport.
- **/
-void
-lpfc_free_bucket(struct lpfc_vport *vport)
-{
- struct lpfc_nodelist *ndlp = NULL, *next_ndlp = NULL;
-
- list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
- if (!NLP_CHK_NODE_ACT(ndlp))
- continue;
-
- kfree(ndlp->lat_data);
- ndlp->lat_data = NULL;
- }
-}
diff --git a/drivers/scsi/lpfc/lpfc_vport.h b/drivers/scsi/lpfc/lpfc_vport.h
index 90828340acea..fa60c146c169 100644
--- a/drivers/scsi/lpfc/lpfc_vport.h
+++ b/drivers/scsi/lpfc/lpfc_vport.h
@@ -1,9 +1,11 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term *
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
* Copyright (C) 2004-2006 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
+ * www.broadcom.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
* *
* This program is free software; you can redistribute it and/or *
@@ -90,6 +92,7 @@ int lpfc_vport_getinfo(struct Scsi_Host *, struct vport_info *);
int lpfc_vport_tgt_remove(struct Scsi_Host *, uint, uint);
struct lpfc_vport **lpfc_create_vport_work_array(struct lpfc_hba *);
void lpfc_destroy_vport_work_array(struct lpfc_hba *, struct lpfc_vport **);
+int lpfc_alloc_vpi(struct lpfc_hba *phba);
/*
* queuecommand VPORT-specific return codes. Specified in the host byte code.
@@ -112,8 +115,4 @@ struct vport_cmd_tag {
void lpfc_vport_set_state(struct lpfc_vport *vport,
enum fc_vport_state new_state);
-void lpfc_vport_reset_stat_data(struct lpfc_vport *);
-void lpfc_alloc_bucket(struct lpfc_vport *);
-void lpfc_free_bucket(struct lpfc_vport *);
-
#endif /* H_LPFC_VPORT */
diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c
index 6c42dff0f4d3..377dcab32cd8 100644
--- a/drivers/scsi/mac53c94.c
+++ b/drivers/scsi/mac53c94.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SCSI low-level driver for the 53c94 SCSI bus adaptor found
* on Power Macintosh computers, controlling the external SCSI chain.
@@ -17,12 +18,12 @@
#include <linux/stat.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pgtable.h>
#include <asm/dbdma.h>
#include <asm/io.h>
-#include <asm/pgtable.h>
#include <asm/prom.h>
-#include <asm/system.h>
-#include <asm/pci-bridge.h>
#include <asm/macio.h>
#include <scsi/scsi.h>
@@ -65,8 +66,7 @@ static irqreturn_t do_mac53c94_interrupt(int, void *);
static void cmd_done(struct fsc_state *, int result);
static void set_dma_cmds(struct fsc_state *, struct scsi_cmnd *);
-
-static int mac53c94_queue_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+static int mac53c94_queue_lck(struct scsi_cmnd *cmd)
{
struct fsc_state *state;
@@ -82,7 +82,6 @@ static int mac53c94_queue_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cm
}
#endif
- cmd->scsi_done = done;
cmd->host_scribble = NULL;
state = (struct fsc_state *) cmd->device->host->hostdata;
@@ -126,7 +125,6 @@ static void mac53c94_init(struct fsc_state *state)
{
struct mac53c94_regs __iomem *regs = state->regs;
struct dbdma_regs __iomem *dma = state->dma;
- int x;
writeb(state->host->this_id | CF1_PAR_ENABLE, &regs->config1);
writeb(TIMO_VAL(250), &regs->sel_timeout); /* 250ms */
@@ -135,7 +133,7 @@ static void mac53c94_init(struct fsc_state *state)
writeb(0, &regs->config3);
writeb(0, &regs->sync_period);
writeb(0, &regs->sync_offset);
- x = readb(&regs->interrupt);
+ (void)readb(&regs->interrupt);
writel((RUN|PAUSE|FLUSH|WAKE) << 16, &dma->control);
}
@@ -195,7 +193,8 @@ static void mac53c94_interrupt(int irq, void *dev_id)
struct fsc_state *state = (struct fsc_state *) dev_id;
struct mac53c94_regs __iomem *regs = state->regs;
struct dbdma_regs __iomem *dma = state->dma;
- struct scsi_cmnd *cmd = state->current_req;
+ struct scsi_cmnd *const cmd = state->current_req;
+ struct mac53c94_cmd_priv *const mcmd = mac53c94_priv(cmd);
int nb, stat, seq, intr;
static int mac53c94_errors;
@@ -235,7 +234,7 @@ static void mac53c94_interrupt(int irq, void *dev_id)
++mac53c94_errors;
writeb(CMD_NOP + CMD_DMA_MODE, &regs->command);
}
- if (cmd == 0) {
+ if (!cmd) {
printk(KERN_DEBUG "53c94: interrupt with no command active?\n");
return;
}
@@ -265,10 +264,10 @@ static void mac53c94_interrupt(int irq, void *dev_id)
/* set DMA controller going if any data to transfer */
if ((stat & (STAT_MSG|STAT_CD)) == 0
&& (scsi_sg_count(cmd) > 0 || scsi_bufflen(cmd))) {
- nb = cmd->SCp.this_residual;
+ nb = mcmd->this_residual;
if (nb > 0xfff0)
nb = 0xfff0;
- cmd->SCp.this_residual -= nb;
+ mcmd->this_residual -= nb;
writeb(nb, &regs->count_lo);
writeb(nb >> 8, &regs->count_mid);
writeb(CMD_DMA_MODE + CMD_NOP, &regs->command);
@@ -295,13 +294,13 @@ static void mac53c94_interrupt(int irq, void *dev_id)
cmd_done(state, DID_ERROR << 16);
return;
}
- if (cmd->SCp.this_residual != 0
+ if (mcmd->this_residual != 0
&& (stat & (STAT_MSG|STAT_CD)) == 0) {
/* Set up the count regs to transfer more */
- nb = cmd->SCp.this_residual;
+ nb = mcmd->this_residual;
if (nb > 0xfff0)
nb = 0xfff0;
- cmd->SCp.this_residual -= nb;
+ mcmd->this_residual -= nb;
writeb(nb, &regs->count_lo);
writeb(nb >> 8, &regs->count_mid);
writeb(CMD_DMA_MODE + CMD_NOP, &regs->command);
@@ -323,9 +322,8 @@ static void mac53c94_interrupt(int irq, void *dev_id)
cmd_done(state, DID_ERROR << 16);
return;
}
- cmd->SCp.Status = readb(&regs->fifo);
- cmd->SCp.Message = readb(&regs->fifo);
- cmd->result = CMD_ACCEPT_MSG;
+ mcmd->status = readb(&regs->fifo);
+ mcmd->message = readb(&regs->fifo);
writeb(CMD_ACCEPT_MSG, &regs->command);
state->phase = busfreeing;
break;
@@ -333,8 +331,7 @@ static void mac53c94_interrupt(int irq, void *dev_id)
if (intr != INTR_DISCONNECT) {
printk(KERN_DEBUG "got intr %x when expected disconnect\n", intr);
}
- cmd_done(state, (DID_OK << 16) + (cmd->SCp.Message << 8)
- + cmd->SCp.Status);
+ cmd_done(state, (DID_OK << 16) + (mcmd->message << 8) + mcmd->status);
break;
default:
printk(KERN_DEBUG "don't know about phase %d\n", state->phase);
@@ -346,9 +343,9 @@ static void cmd_done(struct fsc_state *state, int result)
struct scsi_cmnd *cmd;
cmd = state->current_req;
- if (cmd != 0) {
+ if (cmd) {
cmd->result = result;
- (*cmd->scsi_done)(cmd);
+ scsi_done(cmd);
state->current_req = NULL;
}
state->phase = idle;
@@ -382,20 +379,20 @@ static void set_dma_cmds(struct fsc_state *state, struct scsi_cmnd *cmd)
if (dma_len > 0xffff)
panic("mac53c94: scatterlist element >= 64k");
total += dma_len;
- st_le16(&dcmds->req_count, dma_len);
- st_le16(&dcmds->command, dma_cmd);
- st_le32(&dcmds->phy_addr, dma_addr);
+ dcmds->req_count = cpu_to_le16(dma_len);
+ dcmds->command = cpu_to_le16(dma_cmd);
+ dcmds->phy_addr = cpu_to_le32(dma_addr);
dcmds->xfer_status = 0;
++dcmds;
}
dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
- st_le16(&dcmds[-1].command, dma_cmd);
- st_le16(&dcmds->command, DBDMA_STOP);
- cmd->SCp.this_residual = total;
+ dcmds[-1].command = cpu_to_le16(dma_cmd);
+ dcmds->command = cpu_to_le16(DBDMA_STOP);
+ mac53c94_priv(cmd)->this_residual = total;
}
-static struct scsi_host_template mac53c94_template = {
+static const struct scsi_host_template mac53c94_template = {
.proc_name = "53c94",
.name = "53C94",
.queuecommand = mac53c94_queue,
@@ -403,8 +400,8 @@ static struct scsi_host_template mac53c94_template = {
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
+ .max_segment_size = 65535,
+ .cmd_size = sizeof(struct mac53c94_cmd_priv),
};
static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *match)
@@ -449,15 +446,14 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat
ioremap(macio_resource_start(mdev, 1), 0x1000);
state->dmaintr = macio_irq(mdev, 1);
if (state->regs == NULL || state->dma == NULL) {
- printk(KERN_ERR "mac53c94: ioremap failed for %s\n",
- node->full_name);
+ printk(KERN_ERR "mac53c94: ioremap failed for %pOF\n", node);
goto out_free;
}
clkprop = of_get_property(node, "clock-frequency", &proplen);
if (clkprop == NULL || proplen != sizeof(int)) {
- printk(KERN_ERR "%s: can't get clock frequency, "
- "assuming 25MHz\n", node->full_name);
+ printk(KERN_ERR "%pOF: can't get clock frequency, "
+ "assuming 25MHz\n", node);
state->clk_freq = 25000000;
} else
state->clk_freq = *(int *)clkprop;
@@ -466,14 +462,16 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat
* +1 to allow for aligning.
* XXX FIXME: Use DMA consistent routines
*/
- dma_cmd_space = kmalloc((host->sg_tablesize + 2) *
- sizeof(struct dbdma_cmd), GFP_KERNEL);
- if (dma_cmd_space == 0) {
- printk(KERN_ERR "mac53c94: couldn't allocate dma "
- "command space for %s\n", node->full_name);
+ dma_cmd_space = kmalloc_array(host->sg_tablesize + 2,
+ sizeof(struct dbdma_cmd),
+ GFP_KERNEL);
+ if (!dma_cmd_space) {
+ printk(KERN_ERR "mac53c94: couldn't allocate dma "
+ "command space for %pOF\n", node);
rc = -ENOMEM;
- goto out_free;
- }
+ goto out_free;
+ }
+
state->dma_cmds = (struct dbdma_cmd *)DBDMA_ALIGN(dma_cmd_space);
memset(state->dma_cmds, 0, (host->sg_tablesize + 1)
* sizeof(struct dbdma_cmd));
@@ -482,8 +480,8 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat
mac53c94_init(state);
if (request_irq(state->intr, do_mac53c94_interrupt, 0, "53C94",state)) {
- printk(KERN_ERR "mac53C94: can't get irq %d for %s\n",
- state->intr, node->full_name);
+ printk(KERN_ERR "mac53C94: can't get irq %d for %pOF\n",
+ state->intr, node);
goto out_free_dma;
}
@@ -510,7 +508,7 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat
return rc;
}
-static int mac53c94_remove(struct macio_dev *mdev)
+static void mac53c94_remove(struct macio_dev *mdev)
{
struct fsc_state *fp = (struct fsc_state *)macio_get_drvdata(mdev);
struct Scsi_Host *host = fp->host;
@@ -528,11 +526,8 @@ static int mac53c94_remove(struct macio_dev *mdev)
scsi_host_put(host);
macio_release_resources(mdev);
-
- return 0;
}
-
static struct of_device_id mac53c94_match[] =
{
{
diff --git a/drivers/scsi/mac53c94.h b/drivers/scsi/mac53c94.h
index 1ad24e4f0a85..b4093027f9c3 100644
--- a/drivers/scsi/mac53c94.h
+++ b/drivers/scsi/mac53c94.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mac53c94.h: definitions for the driver for the 53c94 SCSI bus adaptor
* found on Power Macintosh computers, controlling the external SCSI chain.
@@ -211,4 +212,15 @@ struct mac53c94_regs {
#define CF4_TEST 0x02
#define CF4_BBTE 0x01
+struct mac53c94_cmd_priv {
+ int this_residual;
+ int status;
+ int message;
+};
+
+static inline struct mac53c94_cmd_priv *mac53c94_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
#endif /* _MAC53C94_H */
diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c
index 3893337e3dd3..ff0253d47a0e 100644
--- a/drivers/scsi/mac_esp.c
+++ b/drivers/scsi/mac_esp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* mac_esp.c: ESP front-end for Macintosh Quadra systems.
*
* Adapted from jazz_esp.c and the old mac_esp.c.
@@ -25,6 +26,7 @@
#include <asm/dma.h>
#include <asm/macints.h>
#include <asm/macintosh.h>
+#include <asm/mac_via.h>
#include <scsi/scsi_host.h>
@@ -51,13 +53,12 @@ struct mac_esp_priv {
struct esp *esp;
void __iomem *pdma_regs;
void __iomem *pdma_io;
- int error;
};
static struct esp *esp_chips[2];
+static DEFINE_SPINLOCK(esp_chips_lock);
#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
- platform_get_drvdata((struct platform_device *) \
- (esp->dev)))
+ dev_get_drvdata((esp)->dev))
static inline void mac_esp_write8(struct esp *esp, u8 val, unsigned long reg)
{
@@ -69,38 +70,6 @@ static inline u8 mac_esp_read8(struct esp *esp, unsigned long reg)
return nubus_readb(esp->regs + reg * 16);
}
-/* For pseudo DMA and PIO we need the virtual address
- * so this address mapping is the identity mapping.
- */
-
-static dma_addr_t mac_esp_map_single(struct esp *esp, void *buf,
- size_t sz, int dir)
-{
- return (dma_addr_t)buf;
-}
-
-static int mac_esp_map_sg(struct esp *esp, struct scatterlist *sg,
- int num_sg, int dir)
-{
- int i;
-
- for (i = 0; i < num_sg; i++)
- sg[i].dma_address = (u32)sg_virt(&sg[i]);
- return num_sg;
-}
-
-static void mac_esp_unmap_single(struct esp *esp, dma_addr_t addr,
- size_t sz, int dir)
-{
- /* Nothing to do. */
-}
-
-static void mac_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
- int num_sg, int dir)
-{
- /* Nothing to do. */
-}
-
static void mac_esp_reset_dma(struct esp *esp)
{
/* Nothing to do. */
@@ -118,12 +87,11 @@ static void mac_esp_dma_invalidate(struct esp *esp)
static int mac_esp_dma_error(struct esp *esp)
{
- return MAC_ESP_GET_PRIV(esp)->error;
+ return esp->send_cmd_error;
}
static inline int mac_esp_wait_for_empty_fifo(struct esp *esp)
{
- struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
int i = 500000;
do {
@@ -138,7 +106,7 @@ static inline int mac_esp_wait_for_empty_fifo(struct esp *esp)
printk(KERN_ERR PFX "FIFO is not empty (sreg %02x)\n",
esp_read8(ESP_STATUS));
- mep->error = 1;
+ esp->send_cmd_error = 1;
return 1;
}
@@ -149,7 +117,7 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
do {
if (mep->pdma_regs == NULL) {
- if (mac_irq_pending(IRQ_MAC_SCSIDRQ))
+ if (via2_scsi_drq_pending())
return 0;
} else {
if (nubus_readl(mep->pdma_regs) & 0x200)
@@ -164,7 +132,7 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
printk(KERN_ERR PFX "PDMA timeout (sreg %02x)\n",
esp_read8(ESP_STATUS));
- mep->error = 1;
+ esp->send_cmd_error = 1;
return 1;
}
@@ -230,11 +198,8 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
u32 dma_count, int write, u8 cmd)
{
struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
- unsigned long flags;
- local_irq_save(flags);
-
- mep->error = 0;
+ esp->send_cmd_error = 0;
if (!write)
scsi_esp_cmd(esp, ESP_CMD_FLUSH);
@@ -270,169 +235,6 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
esp_count = n;
}
} while (esp_count);
-
- local_irq_restore(flags);
-}
-
-/*
- * Programmed IO routines follow.
- */
-
-static inline unsigned int mac_esp_wait_for_fifo(struct esp *esp)
-{
- int i = 500000;
-
- do {
- unsigned int fbytes = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
-
- if (fbytes)
- return fbytes;
-
- udelay(2);
- } while (--i);
-
- printk(KERN_ERR PFX "FIFO is empty (sreg %02x)\n",
- esp_read8(ESP_STATUS));
- return 0;
-}
-
-static inline int mac_esp_wait_for_intr(struct esp *esp)
-{
- struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
- int i = 500000;
-
- do {
- esp->sreg = esp_read8(ESP_STATUS);
- if (esp->sreg & ESP_STAT_INTR)
- return 0;
-
- udelay(2);
- } while (--i);
-
- printk(KERN_ERR PFX "IRQ timeout (sreg %02x)\n", esp->sreg);
- mep->error = 1;
- return 1;
-}
-
-#define MAC_ESP_PIO_LOOP(operands, reg1) \
- asm volatile ( \
- "1: moveb " operands " \n" \
- " subqw #1,%1 \n" \
- " jbne 1b \n" \
- : "+a" (addr), "+r" (reg1) \
- : "a" (fifo))
-
-#define MAC_ESP_PIO_FILL(operands, reg1) \
- asm volatile ( \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " moveb " operands " \n" \
- " subqw #8,%1 \n" \
- " subqw #8,%1 \n" \
- : "+a" (addr), "+r" (reg1) \
- : "a" (fifo))
-
-#define MAC_ESP_FIFO_SIZE 16
-
-static void mac_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count,
- u32 dma_count, int write, u8 cmd)
-{
- struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
- u8 *fifo = esp->regs + ESP_FDATA * 16;
-
- disable_irq(esp->host->irq);
-
- cmd &= ~ESP_CMD_DMA;
- mep->error = 0;
-
- if (write) {
- scsi_esp_cmd(esp, cmd);
-
- while (1) {
- unsigned int n;
-
- n = mac_esp_wait_for_fifo(esp);
- if (!n)
- break;
-
- if (n > esp_count)
- n = esp_count;
- esp_count -= n;
-
- MAC_ESP_PIO_LOOP("%2@,%0@+", n);
-
- if (!esp_count)
- break;
-
- if (mac_esp_wait_for_intr(esp))
- break;
-
- if (((esp->sreg & ESP_STAT_PMASK) != ESP_DIP) &&
- ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP))
- break;
-
- esp->ireg = esp_read8(ESP_INTRPT);
- if ((esp->ireg & (ESP_INTR_DC | ESP_INTR_BSERV)) !=
- ESP_INTR_BSERV)
- break;
-
- scsi_esp_cmd(esp, ESP_CMD_TI);
- }
- } else {
- scsi_esp_cmd(esp, ESP_CMD_FLUSH);
-
- if (esp_count >= MAC_ESP_FIFO_SIZE)
- MAC_ESP_PIO_FILL("%0@+,%2@", esp_count);
- else
- MAC_ESP_PIO_LOOP("%0@+,%2@", esp_count);
-
- scsi_esp_cmd(esp, cmd);
-
- while (esp_count) {
- unsigned int n;
-
- if (mac_esp_wait_for_intr(esp))
- break;
-
- if (((esp->sreg & ESP_STAT_PMASK) != ESP_DOP) &&
- ((esp->sreg & ESP_STAT_PMASK) != ESP_MOP))
- break;
-
- esp->ireg = esp_read8(ESP_INTRPT);
- if ((esp->ireg & (ESP_INTR_DC | ESP_INTR_BSERV)) !=
- ESP_INTR_BSERV)
- break;
-
- n = MAC_ESP_FIFO_SIZE -
- (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES);
- if (n > esp_count)
- n = esp_count;
-
- if (n == MAC_ESP_FIFO_SIZE) {
- MAC_ESP_PIO_FILL("%0@+,%2@", esp_count);
- } else {
- esp_count -= n;
- MAC_ESP_PIO_LOOP("%0@+,%2@", n);
- }
-
- scsi_esp_cmd(esp, ESP_CMD_TI);
- }
- }
-
- enable_irq(esp->host->irq);
}
static int mac_esp_irq_pending(struct esp *esp)
@@ -476,10 +278,6 @@ static irqreturn_t mac_scsi_esp_intr(int irq, void *dev_id)
static struct esp_driver_ops mac_esp_ops = {
.esp_write8 = mac_esp_write8,
.esp_read8 = mac_esp_read8,
- .map_single = mac_esp_map_single,
- .map_sg = mac_esp_map_sg,
- .unmap_single = mac_esp_unmap_single,
- .unmap_sg = mac_esp_unmap_sg,
.irq_pending = mac_esp_irq_pending,
.dma_length_limit = mac_esp_dma_length_limit,
.reset_dma = mac_esp_reset_dma,
@@ -489,9 +287,9 @@ static struct esp_driver_ops mac_esp_ops = {
.dma_error = mac_esp_dma_error,
};
-static int __devinit esp_mac_probe(struct platform_device *dev)
+static int esp_mac_probe(struct platform_device *dev)
{
- struct scsi_host_template *tpnt = &scsi_esp_template;
+ const struct scsi_host_template *tpnt = &scsi_esp_template;
struct Scsi_Host *host;
struct esp *esp;
int err;
@@ -510,11 +308,11 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
goto fail;
host->max_id = 8;
- host->use_clustering = DISABLE_CLUSTERING;
+ host->dma_boundary = PAGE_SIZE - 1;
esp = shost_priv(host);
esp->host = host;
- esp->dev = dev;
+ esp->dev = &dev->dev;
esp->command_block = kzalloc(16, GFP_KERNEL);
if (!esp->command_block)
@@ -557,39 +355,48 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
mep->pdma_regs = NULL;
break;
}
+ esp->fifo_reg = esp->regs + ESP_FDATA * 16;
esp->ops = &mac_esp_ops;
+ esp->flags = ESP_FLAG_NO_DMA_MAP;
if (mep->pdma_io == NULL) {
printk(KERN_INFO PFX "using PIO for controller %d\n", dev->id);
esp_write8(0, ESP_TCLOW);
esp_write8(0, ESP_TCMED);
- esp->flags = ESP_FLAG_DISABLE_SYNC;
- mac_esp_ops.send_dma_cmd = mac_esp_send_pio_cmd;
+ esp->flags |= ESP_FLAG_DISABLE_SYNC;
+ mac_esp_ops.send_dma_cmd = esp_send_pio_cmd;
} else {
printk(KERN_INFO PFX "using PDMA for controller %d\n", dev->id);
}
host->irq = IRQ_MAC_SCSI;
- esp_chips[dev->id] = esp;
- mb();
- if (esp_chips[!dev->id] == NULL) {
- err = request_irq(host->irq, mac_scsi_esp_intr, 0,
- "Mac ESP", NULL);
- if (err < 0) {
- esp_chips[dev->id] = NULL;
- goto fail_free_priv;
- }
+
+ /* The request_irq() call is intended to succeed for the first device
+ * and fail for the second device.
+ */
+ err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL);
+ spin_lock(&esp_chips_lock);
+ if (err < 0 && esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
+ goto fail_free_priv;
}
+ esp_chips[dev->id] = esp;
+ spin_unlock(&esp_chips_lock);
- err = scsi_esp_register(esp, &dev->dev);
+ err = scsi_esp_register(esp);
if (err)
goto fail_free_irq;
return 0;
fail_free_irq:
- if (esp_chips[!dev->id] == NULL)
- free_irq(host->irq, esp);
+ spin_lock(&esp_chips_lock);
+ esp_chips[dev->id] = NULL;
+ if (esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
+ free_irq(host->irq, NULL);
+ } else
+ spin_unlock(&esp_chips_lock);
fail_free_priv:
kfree(mep);
fail_free_command_block:
@@ -600,7 +407,7 @@ fail:
return err;
}
-static int __devexit esp_mac_remove(struct platform_device *dev)
+static void esp_mac_remove(struct platform_device *dev)
{
struct mac_esp_priv *mep = platform_get_drvdata(dev);
struct esp *esp = mep->esp;
@@ -608,43 +415,32 @@ static int __devexit esp_mac_remove(struct platform_device *dev)
scsi_esp_unregister(esp);
+ spin_lock(&esp_chips_lock);
esp_chips[dev->id] = NULL;
- if (!(esp_chips[0] || esp_chips[1]))
+ if (esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
free_irq(irq, NULL);
+ } else
+ spin_unlock(&esp_chips_lock);
kfree(mep);
kfree(esp->command_block);
scsi_host_put(esp->host);
-
- return 0;
}
static struct platform_driver esp_mac_driver = {
.probe = esp_mac_probe,
- .remove = __devexit_p(esp_mac_remove),
+ .remove = esp_mac_remove,
.driver = {
.name = DRV_MODULE_NAME,
- .owner = THIS_MODULE,
},
};
-
-static int __init mac_esp_init(void)
-{
- return platform_driver_register(&esp_mac_driver);
-}
-
-static void __exit mac_esp_exit(void)
-{
- platform_driver_unregister(&esp_mac_driver);
-}
+module_platform_driver(esp_mac_driver);
MODULE_DESCRIPTION("Mac ESP SCSI driver");
-MODULE_AUTHOR("Finn Thain <fthain@telegraphics.com.au>");
+MODULE_AUTHOR("Finn Thain");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRV_VERSION);
MODULE_ALIAS("platform:" DRV_MODULE_NAME);
-
-module_init(mac_esp_init);
-module_exit(mac_esp_exit);
diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c
index bf2a1c516293..a86bd839d08e 100644
--- a/drivers/scsi/mac_scsi.c
+++ b/drivers/scsi/mac_scsi.c
@@ -1,598 +1,560 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Generic Macintosh NCR5380 driver
*
* Copyright 1998, Michael Schmitz <mschmitz@lbl.gov>
*
+ * Copyright 2019 Finn Thain
+ *
* derived in part from:
*/
/*
* Generic Generic NCR5380 driver
*
* Copyright 1995, Russell King
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * $Log: mac_NCR5380.c,v $
*/
-#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/ctype.h>
#include <linux/delay.h>
-
+#include <linux/types.h>
#include <linux/module.h>
-#include <linux/signal.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <asm/hwtest.h>
#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/mac_via.h>
+#include <asm/setup.h>
-#include "scsi.h"
#include <scsi/scsi_host.h>
-#include "mac_scsi.h"
-/* These control the behaviour of the generic 5380 core */
-#define AUTOSENSE
-#define PSEUDO_DMA
+/* Definitions for the core NCR5380 driver. */
-#include "NCR5380.h"
+#define NCR5380_implementation_fields int pdma_residual
-#if 0
-#define NDEBUG (NDEBUG_INTR | NDEBUG_PSEUDO_DMA | NDEBUG_ARBITRATION | NDEBUG_SELECTION | NDEBUG_RESELECTION)
-#else
-#define NDEBUG (NDEBUG_ABORT)
-#endif
+#define NCR5380_read(reg) in_8(hostdata->io + ((reg) << 4))
+#define NCR5380_write(reg, value) out_8(hostdata->io + ((reg) << 4), value)
-#define RESET_BOOT
-#define DRIVER_SETUP
+#define NCR5380_dma_xfer_len macscsi_dma_xfer_len
+#define NCR5380_dma_recv_setup macscsi_pread
+#define NCR5380_dma_send_setup macscsi_pwrite
+#define NCR5380_dma_residual macscsi_dma_residual
-extern void via_scsi_clear(void);
+#define NCR5380_intr macscsi_intr
+#define NCR5380_queue_command macscsi_queue_command
+#define NCR5380_abort macscsi_abort
+#define NCR5380_host_reset macscsi_host_reset
+#define NCR5380_info macscsi_info
-#ifdef RESET_BOOT
-static void mac_scsi_reset_boot(struct Scsi_Host *instance);
-#endif
+#include "NCR5380.h"
-static int setup_called = 0;
static int setup_can_queue = -1;
+module_param(setup_can_queue, int, 0);
static int setup_cmd_per_lun = -1;
+module_param(setup_cmd_per_lun, int, 0);
static int setup_sg_tablesize = -1;
-static int setup_use_pdma = -1;
-#ifdef SUPPORT_TAGS
-static int setup_use_tagged_queuing = -1;
-#endif
+module_param(setup_sg_tablesize, int, 0);
+static int setup_use_pdma = 512;
+module_param(setup_use_pdma, int, 0);
static int setup_hostid = -1;
+module_param(setup_hostid, int, 0);
+static int setup_toshiba_delay = -1;
+module_param(setup_toshiba_delay, int, 0);
-/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
- * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more
- * need ten times the standard value... */
-#define TOSHIBA_DELAY
+#ifndef MODULE
+static int __init mac_scsi_setup(char *str)
+{
+ int ints[8];
-#ifdef TOSHIBA_DELAY
-#define AFTER_RESET_DELAY (5*HZ/2)
-#else
-#define AFTER_RESET_DELAY (HZ/2)
-#endif
+ (void)get_options(str, ARRAY_SIZE(ints), ints);
-static volatile unsigned char *mac_scsi_regp = NULL;
-static volatile unsigned char *mac_scsi_drq = NULL;
-static volatile unsigned char *mac_scsi_nodrq = NULL;
+ if (ints[0] < 1) {
+ pr_err("Usage: mac5380=<can_queue>[,<cmd_per_lun>[,<sg_tablesize>[,<hostid>[,<use_tags>[,<use_pdma>[,<toshiba_delay>]]]]]]\n");
+ return 0;
+ }
+ if (ints[0] >= 1)
+ setup_can_queue = ints[1];
+ if (ints[0] >= 2)
+ setup_cmd_per_lun = ints[2];
+ if (ints[0] >= 3)
+ setup_sg_tablesize = ints[3];
+ if (ints[0] >= 4)
+ setup_hostid = ints[4];
+ /* ints[5] (use_tagged_queuing) is ignored */
+ if (ints[0] >= 6)
+ setup_use_pdma = ints[6];
+ if (ints[0] >= 7)
+ setup_toshiba_delay = ints[7];
+ return 1;
+}
+__setup("mac5380=", mac_scsi_setup);
+#endif /* !MODULE */
/*
- * NCR 5380 register access functions
+ * According to "Inside Macintosh: Devices", Mac OS requires disk drivers to
+ * specify the number of bytes between the delays expected from a SCSI target.
+ * This allows the operating system to "prevent bus errors when a target fails
+ * to deliver the next byte within the processor bus error timeout period."
+ * Linux SCSI drivers lack knowledge of the timing behaviour of SCSI targets
+ * so bus errors are unavoidable.
+ *
+ * If a MOVE.B instruction faults during a receive operation, we assume the
+ * target sent nothing and try again. That assumption probably depends on
+ * target firmware but it seems to hold up okay. If a fault happens during a
+ * send operation, the target may or may not have seen /ACK and got the byte.
+ * It's uncertain so the whole SCSI command gets retried.
+ *
+ * The NOP is needed for synchronization because the fault address in the
+ * exception stack frame may or may not be the instruction that actually
+ * caused the bus error. Post-increment addressing can't be used.
*/
-#if 0
-/* Debug versions */
-#define CTRL(p,v) (*ctrl = (v))
+#define MOVE_BYTE(operands) \
+ asm volatile ( \
+ "1: moveb " operands " \n" \
+ "11: nop \n" \
+ " addq #1,%0 \n" \
+ " subq #1,%1 \n" \
+ "40: \n" \
+ " \n" \
+ ".section .fixup,\"ax\" \n" \
+ ".even \n" \
+ "90: movel #1, %2 \n" \
+ " jra 40b \n" \
+ ".previous \n" \
+ " \n" \
+ ".section __ex_table,\"a\" \n" \
+ ".align 4 \n" \
+ ".long 1b,90b \n" \
+ ".long 11b,90b \n" \
+ ".previous \n" \
+ : "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
-static char macscsi_read(struct Scsi_Host *instance, int reg)
-{
- int iobase = instance->io_port;
- int i;
- int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+/*
+ * If a MOVE.W (or MOVE.L) instruction faults, it cannot be retried because
+ * the residual byte count would be uncertain. In that situation the MOVE_WORD
+ * macro clears n in the fixup section to abort the transfer.
+ */
- CTRL(iobase, 0);
- i = in_8(iobase + (reg<<4));
- CTRL(iobase, 0x40);
+#define MOVE_WORD(operands) \
+ asm volatile ( \
+ "1: movew " operands " \n" \
+ "11: nop \n" \
+ " subq #2,%1 \n" \
+ "40: \n" \
+ " \n" \
+ ".section .fixup,\"ax\" \n" \
+ ".even \n" \
+ "90: movel #0, %1 \n" \
+ " movel #2, %2 \n" \
+ " jra 40b \n" \
+ ".previous \n" \
+ " \n" \
+ ".section __ex_table,\"a\" \n" \
+ ".align 4 \n" \
+ ".long 1b,90b \n" \
+ ".long 11b,90b \n" \
+ ".previous \n" \
+ : "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
+
+#define MOVE_16_WORDS(operands) \
+ asm volatile ( \
+ "1: movew " operands " \n" \
+ "2: movew " operands " \n" \
+ "3: movew " operands " \n" \
+ "4: movew " operands " \n" \
+ "5: movew " operands " \n" \
+ "6: movew " operands " \n" \
+ "7: movew " operands " \n" \
+ "8: movew " operands " \n" \
+ "9: movew " operands " \n" \
+ "10: movew " operands " \n" \
+ "11: movew " operands " \n" \
+ "12: movew " operands " \n" \
+ "13: movew " operands " \n" \
+ "14: movew " operands " \n" \
+ "15: movew " operands " \n" \
+ "16: movew " operands " \n" \
+ "17: nop \n" \
+ " subl #32,%1 \n" \
+ "40: \n" \
+ " \n" \
+ ".section .fixup,\"ax\" \n" \
+ ".even \n" \
+ "90: movel #0, %1 \n" \
+ " movel #2, %2 \n" \
+ " jra 40b \n" \
+ ".previous \n" \
+ " \n" \
+ ".section __ex_table,\"a\" \n" \
+ ".align 4 \n" \
+ ".long 1b,90b \n" \
+ ".long 2b,90b \n" \
+ ".long 3b,90b \n" \
+ ".long 4b,90b \n" \
+ ".long 5b,90b \n" \
+ ".long 6b,90b \n" \
+ ".long 7b,90b \n" \
+ ".long 8b,90b \n" \
+ ".long 9b,90b \n" \
+ ".long 10b,90b \n" \
+ ".long 11b,90b \n" \
+ ".long 12b,90b \n" \
+ ".long 13b,90b \n" \
+ ".long 14b,90b \n" \
+ ".long 15b,90b \n" \
+ ".long 16b,90b \n" \
+ ".long 17b,90b \n" \
+ ".previous \n" \
+ : "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
+
+static inline int mac_pdma_recv(void __iomem *io, unsigned char *start, int n)
+{
+ unsigned char *addr = start;
+ int result = 0;
- return i;
+ if (n >= 1) {
+ MOVE_BYTE("%3@,%0@");
+ if (result)
+ goto out;
+ }
+ if (n >= 1 && ((unsigned long)addr & 1)) {
+ MOVE_BYTE("%3@,%0@");
+ if (result)
+ goto out;
+ }
+ while (n >= 32)
+ MOVE_16_WORDS("%3@,%0@+");
+ while (n >= 2)
+ MOVE_WORD("%3@,%0@+");
+ if (result)
+ return start - addr; /* Negated to indicate uncertain length */
+ if (n == 1)
+ MOVE_BYTE("%3@,%0@");
+out:
+ return addr - start;
}
-static void macscsi_write(struct Scsi_Host *instance, int reg, int value)
+static inline int mac_pdma_send(unsigned char *start, void __iomem *io, int n)
{
- int iobase = instance->io_port;
- int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+ unsigned char *addr = start;
+ int result = 0;
- CTRL(iobase, 0);
- out_8(iobase + (reg<<4), value);
- CTRL(iobase, 0x40);
+ if (n >= 1) {
+ MOVE_BYTE("%0@,%3@");
+ if (result)
+ return -1;
+ }
+ if (n >= 1 && ((unsigned long)addr & 1)) {
+ MOVE_BYTE("%0@,%3@");
+ if (result)
+ return -2;
+ }
+ while (n >= 32)
+ MOVE_16_WORDS("%0@+,%3@");
+ while (n >= 2)
+ MOVE_WORD("%0@+,%3@");
+ if (result)
+ return start - addr - 1; /* Negated to indicate uncertain length */
+ if (n == 1)
+ MOVE_BYTE("%0@,%3@");
+ return addr - start;
}
-#else
-/* Fast versions */
-static __inline__ char macscsi_read(struct Scsi_Host *instance, int reg)
+/* The "SCSI DMA" chip on the IIfx implements this register. */
+#define CTRL_REG 0x8
+#define CTRL_INTERRUPTS_ENABLE BIT(1)
+#define CTRL_HANDSHAKE_MODE BIT(3)
+
+static inline void write_ctrl_reg(struct NCR5380_hostdata *hostdata, u32 value)
{
- return in_8(instance->io_port + (reg<<4));
+ out_be32(hostdata->io + (CTRL_REG << 4), value);
}
-static __inline__ void macscsi_write(struct Scsi_Host *instance, int reg, int value)
+static inline int macscsi_wait_for_drq(struct NCR5380_hostdata *hostdata)
{
- out_8(instance->io_port + (reg<<4), value);
-}
-#endif
+ unsigned int n = 1; /* effectively multiplies NCR5380_REG_POLL_TIME */
+ unsigned char basr;
+again:
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
-/*
- * Function : mac_scsi_setup(char *str)
- *
- * Purpose : booter command line initialization of the overrides array,
- *
- * Inputs : str - comma delimited list of options
- *
- */
+ if (!(basr & BASR_PHASE_MATCH))
+ return 1;
-static int __init mac_scsi_setup(char *str) {
-#ifdef DRIVER_SETUP
- int ints[7];
-
- (void)get_options( str, ARRAY_SIZE(ints), ints);
-
- if (setup_called++ || ints[0] < 1 || ints[0] > 6) {
- printk(KERN_WARNING "scsi: <mac5380>"
- " Usage: mac5380=<can_queue>[,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>,<use_pdma>]\n");
- printk(KERN_ALERT "scsi: <mac5380> Bad Penguin parameters?\n");
- return 0;
- }
-
- if (ints[0] >= 1) {
- if (ints[1] > 0)
- /* no limits on this, just > 0 */
- setup_can_queue = ints[1];
- }
- if (ints[0] >= 2) {
- if (ints[2] > 0)
- setup_cmd_per_lun = ints[2];
- }
- if (ints[0] >= 3) {
- if (ints[3] >= 0) {
- setup_sg_tablesize = ints[3];
- /* Must be <= SG_ALL (255) */
- if (setup_sg_tablesize > SG_ALL)
- setup_sg_tablesize = SG_ALL;
- }
- }
- if (ints[0] >= 4) {
- /* Must be between 0 and 7 */
- if (ints[4] >= 0 && ints[4] <= 7)
- setup_hostid = ints[4];
- else if (ints[4] > 7)
- printk(KERN_WARNING "mac_scsi_setup: invalid host ID %d !\n", ints[4] );
- }
-#ifdef SUPPORT_TAGS
- if (ints[0] >= 5) {
- if (ints[5] >= 0)
- setup_use_tagged_queuing = !!ints[5];
- }
-
- if (ints[0] == 6) {
- if (ints[6] >= 0)
- setup_use_pdma = ints[6];
- }
-#else
- if (ints[0] == 5) {
- if (ints[5] >= 0)
- setup_use_pdma = ints[5];
+ if (basr & BASR_IRQ)
+ return -1;
+
+ if (basr & BASR_DRQ)
+ return 0;
+
+ if (n-- == 0) {
+ NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
+ dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host,
+ "%s: DRQ timeout\n", __func__);
+ return -1;
}
-#endif /* SUPPORT_TAGS */
-
-#endif /* DRIVER_SETUP */
- return 1;
+
+ NCR5380_poll_politely2(hostdata,
+ BUS_AND_STATUS_REG, BASR_DRQ, BASR_DRQ,
+ BUS_AND_STATUS_REG, BASR_PHASE_MATCH, 0, 0);
+ goto again;
}
-__setup("mac5380=", mac_scsi_setup);
+static inline int macscsi_pread(struct NCR5380_hostdata *hostdata,
+ unsigned char *dst, int len)
+{
+ u8 __iomem *s = hostdata->pdma_io + (INPUT_DATA_REG << 4);
+ unsigned char *d = dst;
-/*
- * If you want to find the instance with (k)gdb ...
- */
-#if NDEBUG
-static struct Scsi_Host *default_instance;
-#endif
+ hostdata->pdma_residual = len;
-/*
- * Function : int macscsi_detect(struct scsi_host_template * tpnt)
- *
- * Purpose : initializes mac NCR5380 driver based on the
- * command line / compile time port and irq definitions.
- *
- * Inputs : tpnt - template for this SCSI adapter.
- *
- * Returns : 1 if a host adapter was found, 0 if not.
- *
- */
-
-int macscsi_detect(struct scsi_host_template * tpnt)
-{
- static int called = 0;
- int flags = 0;
- struct Scsi_Host *instance;
-
- if (!MACH_IS_MAC || called)
- return( 0 );
-
- if (macintosh_config->scsi_type != MAC_SCSI_OLD)
- return( 0 );
-
- /* setup variables */
- tpnt->can_queue =
- (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
- tpnt->cmd_per_lun =
- (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
- tpnt->sg_tablesize =
- (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
-
- if (setup_hostid >= 0)
- tpnt->this_id = setup_hostid;
- else {
- /* use 7 as default */
- tpnt->this_id = 7;
- }
-
-#ifdef SUPPORT_TAGS
- if (setup_use_tagged_queuing < 0)
- setup_use_tagged_queuing = USE_TAGGED_QUEUING;
-#endif
-
- /* Once we support multiple 5380s (e.g. DuoDock) we'll do
- something different here */
- instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
-#if NDEBUG
- default_instance = instance;
-#endif
-
- if (macintosh_config->ident == MAC_MODEL_IIFX) {
- mac_scsi_regp = via1+0x8000;
- mac_scsi_drq = via1+0xE000;
- mac_scsi_nodrq = via1+0xC000;
- /* The IIFX should be able to do true DMA, but pseudo-dma doesn't work */
- flags = FLAG_NO_PSEUDO_DMA;
- } else {
- mac_scsi_regp = via1+0x10000;
- mac_scsi_drq = via1+0x6000;
- mac_scsi_nodrq = via1+0x12000;
- }
-
- if (! setup_use_pdma)
- flags = FLAG_NO_PSEUDO_DMA;
-
- instance->io_port = (unsigned long) mac_scsi_regp;
- instance->irq = IRQ_MAC_SCSI;
-
-#ifdef RESET_BOOT
- mac_scsi_reset_boot(instance);
-#endif
-
- NCR5380_init(instance, flags);
-
- instance->n_io_port = 255;
-
- ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
-
- if (instance->irq != SCSI_IRQ_NONE)
- if (request_irq(instance->irq, NCR5380_intr, IRQ_FLG_SLOW,
- "ncr5380", instance)) {
- printk(KERN_WARNING "scsi%d: IRQ%d not free, interrupts disabled\n",
- instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
+ while (macscsi_wait_for_drq(hostdata) == 0) {
+ int bytes, chunk_bytes;
+
+ if (macintosh_config->ident == MAC_MODEL_IIFX)
+ write_ctrl_reg(hostdata, CTRL_HANDSHAKE_MODE |
+ CTRL_INTERRUPTS_ENABLE);
+
+ chunk_bytes = min(hostdata->pdma_residual, 512);
+ bytes = mac_pdma_recv(s, d, chunk_bytes);
+
+ if (macintosh_config->ident == MAC_MODEL_IIFX)
+ write_ctrl_reg(hostdata, CTRL_INTERRUPTS_ENABLE);
+
+ if (bytes > 0) {
+ d += bytes;
+ hostdata->pdma_residual -= bytes;
+ }
+
+ if (hostdata->pdma_residual == 0)
+ break;
+
+ if (bytes > 0)
+ continue;
+
+ NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
+ dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host,
+ "%s: bus error [%d/%d] (%d/%d)\n",
+ __func__, d - dst, len, bytes, chunk_bytes);
+
+ if (bytes == 0)
+ continue;
+
+ if (macscsi_wait_for_drq(hostdata) <= 0)
+ set_host_byte(hostdata->connected, DID_ERROR);
+ break;
}
- printk(KERN_INFO "scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port);
- if (instance->irq == SCSI_IRQ_NONE)
- printk (KERN_INFO "s disabled");
- else
- printk (KERN_INFO " %d", instance->irq);
- printk(KERN_INFO " options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- instance->can_queue, instance->cmd_per_lun, MACSCSI_PUBLIC_RELEASE);
- printk(KERN_INFO "\nscsi%d:", instance->host_no);
- NCR5380_print_options(instance);
- printk("\n");
- called = 1;
- return 1;
+ return 0;
}
-int macscsi_release (struct Scsi_Host *shpnt)
+static inline int macscsi_pwrite(struct NCR5380_hostdata *hostdata,
+ unsigned char *src, int len)
{
- if (shpnt->irq != SCSI_IRQ_NONE)
- free_irq(shpnt->irq, shpnt);
- NCR5380_exit(shpnt);
+ unsigned char *s = src;
+ u8 __iomem *d = hostdata->pdma_io + (OUTPUT_DATA_REG << 4);
- return 0;
-}
+ hostdata->pdma_residual = len;
-#ifdef RESET_BOOT
-/*
- * Our 'bus reset on boot' function
- */
+ while (macscsi_wait_for_drq(hostdata) == 0) {
+ int bytes, chunk_bytes;
-static void mac_scsi_reset_boot(struct Scsi_Host *instance)
-{
- unsigned long end;
+ if (macintosh_config->ident == MAC_MODEL_IIFX)
+ write_ctrl_reg(hostdata, CTRL_HANDSHAKE_MODE |
+ CTRL_INTERRUPTS_ENABLE);
+
+ chunk_bytes = min(hostdata->pdma_residual, 512);
+ bytes = mac_pdma_send(s, d, chunk_bytes);
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- /*
- * Do a SCSI reset to clean up the bus during initialization. No messing
- * with the queues, interrupts, or locks necessary here.
- */
+ if (macintosh_config->ident == MAC_MODEL_IIFX)
+ write_ctrl_reg(hostdata, CTRL_INTERRUPTS_ENABLE);
- printk(KERN_INFO "Macintosh SCSI: resetting the SCSI bus..." );
+ if (bytes > 0) {
+ s += bytes;
+ hostdata->pdma_residual -= bytes;
+ }
- /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
- disable_irq(IRQ_MAC_SCSI);
+ if (hostdata->pdma_residual == 0)
+ break;
- /* get in phase */
- NCR5380_write( TARGET_COMMAND_REG,
- PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+ if (bytes > 0)
+ continue;
- /* assert RST */
- NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
- /* The min. reset hold time is 25us, so 40us should be enough */
- udelay( 50 );
- /* reset RST and interrupt */
- NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
- NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+ NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
+ dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host,
+ "%s: bus error [%d/%d] (%d/%d)\n",
+ __func__, s - src, len, bytes, chunk_bytes);
- for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
- barrier();
+ if (bytes == 0)
+ continue;
- /* switch on SCSI IRQ again */
- enable_irq(IRQ_MAC_SCSI);
+ if (macscsi_wait_for_drq(hostdata) <= 0)
+ set_host_byte(hostdata->connected, DID_ERROR);
+ break;
+ }
- printk(KERN_INFO " done\n" );
+ return 0;
}
-#endif
-const char * macscsi_info (struct Scsi_Host *spnt) {
- return "";
+static int macscsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
+ struct scsi_cmnd *cmd)
+{
+ int resid = NCR5380_to_ncmd(cmd)->this_residual;
+
+ if (hostdata->flags & FLAG_NO_PSEUDO_DMA || resid < setup_use_pdma)
+ return 0;
+
+ return resid;
}
-/*
- Pseudo-DMA: (Ove Edlund)
- The code attempts to catch bus errors that occur if one for example
- "trips over the cable".
- XXX: Since bus errors in the PDMA routines never happen on my
- computer, the bus error code is untested.
- If the code works as intended, a bus error results in Pseudo-DMA
- beeing disabled, meaning that the driver switches to slow handshake.
- If bus errors are NOT extremely rare, this has to be changed.
-*/
-
-#define CP_IO_TO_MEM(s,d,len) \
-__asm__ __volatile__ \
- (" cmp.w #4,%2\n" \
- " bls 8f\n" \
- " move.w %1,%%d0\n" \
- " neg.b %%d0\n" \
- " and.w #3,%%d0\n" \
- " sub.w %%d0,%2\n" \
- " bra 2f\n" \
- " 1: move.b (%0),(%1)+\n" \
- " 2: dbf %%d0,1b\n" \
- " move.w %2,%%d0\n" \
- " lsr.w #5,%%d0\n" \
- " bra 4f\n" \
- " 3: move.l (%0),(%1)+\n" \
- "31: move.l (%0),(%1)+\n" \
- "32: move.l (%0),(%1)+\n" \
- "33: move.l (%0),(%1)+\n" \
- "34: move.l (%0),(%1)+\n" \
- "35: move.l (%0),(%1)+\n" \
- "36: move.l (%0),(%1)+\n" \
- "37: move.l (%0),(%1)+\n" \
- " 4: dbf %%d0,3b\n" \
- " move.w %2,%%d0\n" \
- " lsr.w #2,%%d0\n" \
- " and.w #7,%%d0\n" \
- " bra 6f\n" \
- " 5: move.l (%0),(%1)+\n" \
- " 6: dbf %%d0,5b\n" \
- " and.w #3,%2\n" \
- " bra 8f\n" \
- " 7: move.b (%0),(%1)+\n" \
- " 8: dbf %2,7b\n" \
- " moveq.l #0, %2\n" \
- " 9: \n" \
- ".section .fixup,\"ax\"\n" \
- " .even\n" \
- "90: moveq.l #1, %2\n" \
- " jra 9b\n" \
- ".previous\n" \
- ".section __ex_table,\"a\"\n" \
- " .align 4\n" \
- " .long 1b,90b\n" \
- " .long 3b,90b\n" \
- " .long 31b,90b\n" \
- " .long 32b,90b\n" \
- " .long 33b,90b\n" \
- " .long 34b,90b\n" \
- " .long 35b,90b\n" \
- " .long 36b,90b\n" \
- " .long 37b,90b\n" \
- " .long 5b,90b\n" \
- " .long 7b,90b\n" \
- ".previous" \
- : "=a"(s), "=a"(d), "=d"(len) \
- : "0"(s), "1"(d), "2"(len) \
- : "d0")
-
-
-static int macscsi_pread (struct Scsi_Host *instance,
- unsigned char *dst, int len)
+static int macscsi_dma_residual(struct NCR5380_hostdata *hostdata)
{
- unsigned char *d;
- volatile unsigned char *s;
-
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- s = mac_scsi_drq+0x60;
- d = dst;
-
-/* These conditions are derived from MacOS */
-
- while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
- && !(NCR5380_read(STATUS_REG) & SR_REQ))
- ;
- if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
- && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
- printk(KERN_ERR "Error in macscsi_pread\n");
- return -1;
- }
-
- CP_IO_TO_MEM(s, d, len);
-
- if (len != 0) {
- printk(KERN_NOTICE "Bus error in macscsi_pread\n");
- return -1;
- }
-
- return 0;
+ return hostdata->pdma_residual;
}
+#include "NCR5380.c"
-#define CP_MEM_TO_IO(s,d,len) \
-__asm__ __volatile__ \
- (" cmp.w #4,%2\n" \
- " bls 8f\n" \
- " move.w %0,%%d0\n" \
- " neg.b %%d0\n" \
- " and.w #3,%%d0\n" \
- " sub.w %%d0,%2\n" \
- " bra 2f\n" \
- " 1: move.b (%0)+,(%1)\n" \
- " 2: dbf %%d0,1b\n" \
- " move.w %2,%%d0\n" \
- " lsr.w #5,%%d0\n" \
- " bra 4f\n" \
- " 3: move.l (%0)+,(%1)\n" \
- "31: move.l (%0)+,(%1)\n" \
- "32: move.l (%0)+,(%1)\n" \
- "33: move.l (%0)+,(%1)\n" \
- "34: move.l (%0)+,(%1)\n" \
- "35: move.l (%0)+,(%1)\n" \
- "36: move.l (%0)+,(%1)\n" \
- "37: move.l (%0)+,(%1)\n" \
- " 4: dbf %%d0,3b\n" \
- " move.w %2,%%d0\n" \
- " lsr.w #2,%%d0\n" \
- " and.w #7,%%d0\n" \
- " bra 6f\n" \
- " 5: move.l (%0)+,(%1)\n" \
- " 6: dbf %%d0,5b\n" \
- " and.w #3,%2\n" \
- " bra 8f\n" \
- " 7: move.b (%0)+,(%1)\n" \
- " 8: dbf %2,7b\n" \
- " moveq.l #0, %2\n" \
- " 9: \n" \
- ".section .fixup,\"ax\"\n" \
- " .even\n" \
- "90: moveq.l #1, %2\n" \
- " jra 9b\n" \
- ".previous\n" \
- ".section __ex_table,\"a\"\n" \
- " .align 4\n" \
- " .long 1b,90b\n" \
- " .long 3b,90b\n" \
- " .long 31b,90b\n" \
- " .long 32b,90b\n" \
- " .long 33b,90b\n" \
- " .long 34b,90b\n" \
- " .long 35b,90b\n" \
- " .long 36b,90b\n" \
- " .long 37b,90b\n" \
- " .long 5b,90b\n" \
- " .long 7b,90b\n" \
- ".previous" \
- : "=a"(s), "=a"(d), "=d"(len) \
- : "0"(s), "1"(d), "2"(len) \
- : "d0")
-
-static int macscsi_pwrite (struct Scsi_Host *instance,
- unsigned char *src, int len)
+#define DRV_MODULE_NAME "mac_scsi"
+#define PFX DRV_MODULE_NAME ": "
+
+static struct scsi_host_template mac_scsi_template = {
+ .module = THIS_MODULE,
+ .proc_name = DRV_MODULE_NAME,
+ .name = "Macintosh NCR5380 SCSI",
+ .info = macscsi_info,
+ .queuecommand = macscsi_queue_command,
+ .eh_abort_handler = macscsi_abort,
+ .eh_host_reset_handler = macscsi_host_reset,
+ .can_queue = 16,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct NCR5380_cmd),
+ .max_sectors = 128,
+};
+
+static int __init mac_scsi_probe(struct platform_device *pdev)
{
- unsigned char *s;
- volatile unsigned char *d;
-
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- s = src;
- d = mac_scsi_drq;
-
-/* These conditions are derived from MacOS */
-
- while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
- && (!(NCR5380_read(STATUS_REG) & SR_REQ)
- || (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)))
- ;
- if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) {
- printk(KERN_ERR "Error in macscsi_pwrite\n");
- return -1;
- }
-
- CP_MEM_TO_IO(s, d, len);
-
- if (len != 0) {
- printk(KERN_NOTICE "Bus error in macscsi_pwrite\n");
- return -1;
- }
-
- return 0;
+ struct Scsi_Host *instance;
+ struct NCR5380_hostdata *hostdata;
+ int error;
+ int host_flags = 0;
+ struct resource *irq, *pio_mem, *pdma_mem = NULL;
+
+ pio_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pio_mem)
+ return -ENODEV;
+
+ pdma_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ if (!hwreg_present((unsigned char *)pio_mem->start +
+ (STATUS_REG << 4))) {
+ pr_info(PFX "no device detected at %pap\n", &pio_mem->start);
+ return -ENODEV;
+ }
+
+ if (setup_can_queue > 0)
+ mac_scsi_template.can_queue = setup_can_queue;
+ if (setup_cmd_per_lun > 0)
+ mac_scsi_template.cmd_per_lun = setup_cmd_per_lun;
+ if (setup_sg_tablesize > 0)
+ mac_scsi_template.sg_tablesize = setup_sg_tablesize;
+ if (setup_hostid >= 0)
+ mac_scsi_template.this_id = setup_hostid & 7;
+
+ if (macintosh_config->ident == MAC_MODEL_IIFX)
+ mac_scsi_template.sg_tablesize = 1;
+
+ instance = scsi_host_alloc(&mac_scsi_template,
+ sizeof(struct NCR5380_hostdata));
+ if (!instance)
+ return -ENOMEM;
+
+ if (irq)
+ instance->irq = irq->start;
+ else
+ instance->irq = NO_IRQ;
+
+ hostdata = shost_priv(instance);
+ hostdata->base = pio_mem->start;
+ hostdata->io = (u8 __iomem *)pio_mem->start;
+
+ if (pdma_mem && setup_use_pdma)
+ hostdata->pdma_io = (u8 __iomem *)pdma_mem->start;
+ else
+ host_flags |= FLAG_NO_PSEUDO_DMA;
+
+ host_flags |= setup_toshiba_delay > 0 ? FLAG_TOSHIBA_DELAY : 0;
+
+ if (instance->sg_tablesize > 1)
+ host_flags |= FLAG_DMA_FIXUP;
+
+ error = NCR5380_init(instance, host_flags | FLAG_LATE_DMA_SETUP);
+ if (error)
+ goto fail_init;
+
+ if (instance->irq != NO_IRQ) {
+ error = request_irq(instance->irq, macscsi_intr, IRQF_SHARED,
+ "NCR5380", instance);
+ if (error)
+ goto fail_irq;
+ }
+
+ NCR5380_maybe_reset_bus(instance);
+
+ error = scsi_add_host(instance, NULL);
+ if (error)
+ goto fail_host;
+
+ platform_set_drvdata(pdev, instance);
+
+ scsi_scan_host(instance);
+ return 0;
+
+fail_host:
+ if (instance->irq != NO_IRQ)
+ free_irq(instance->irq, instance);
+fail_irq:
+ NCR5380_exit(instance);
+fail_init:
+ scsi_host_put(instance);
+ return error;
}
+static void __exit mac_scsi_remove(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance = platform_get_drvdata(pdev);
-#include "NCR5380.c"
+ scsi_remove_host(instance);
+ if (instance->irq != NO_IRQ)
+ free_irq(instance->irq, instance);
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+}
-static struct scsi_host_template driver_template = {
- .proc_name = "Mac5380",
- .proc_info = macscsi_proc_info,
- .name = "Macintosh NCR5380 SCSI",
- .detect = macscsi_detect,
- .release = macscsi_release,
- .info = macscsi_info,
- .queuecommand = macscsi_queue_command,
- .eh_abort_handler = macscsi_abort,
- .eh_bus_reset_handler = macscsi_bus_reset,
- .can_queue = CAN_QUEUE,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = CMD_PER_LUN,
- .use_clustering = DISABLE_CLUSTERING
+/*
+ * mac_scsi_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver mac_scsi_driver __refdata = {
+ .remove = __exit_p(mac_scsi_remove),
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ },
};
+module_platform_driver_probe(mac_scsi_driver, mac_scsi_probe);
-#include "scsi_module.c"
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
+MODULE_DESCRIPTION("Macintosh NCR5380 SCSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/mac_scsi.h b/drivers/scsi/mac_scsi.h
deleted file mode 100644
index d26e331c6c12..000000000000
--- a/drivers/scsi/mac_scsi.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Cumana Generic NCR5380 driver defines
- *
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 440-4894
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * $Log: cumana_NCR5380.h,v $
- */
-
-#ifndef MAC_NCR5380_H
-#define MAC_NCR5380_H
-
-#define MACSCSI_PUBLIC_RELEASE 2
-
-#ifndef ASM
-
-#ifndef CMD_PER_LUN
-#define CMD_PER_LUN 2
-#endif
-
-#ifndef CAN_QUEUE
-#define CAN_QUEUE 16
-#endif
-
-#ifndef SG_TABLESIZE
-#define SG_TABLESIZE SG_NONE
-#endif
-
-#ifndef USE_TAGGED_QUEUING
-#define USE_TAGGED_QUEUING 0
-#endif
-
-#include <scsi/scsicam.h>
-
-#ifndef HOSTS_C
-
-#define NCR5380_implementation_fields \
- int port, ctrl
-
-#define NCR5380_local_declare() \
- struct Scsi_Host *_instance
-
-#define NCR5380_setup(instance) \
- _instance = instance
-
-#define NCR5380_read(reg) macscsi_read(_instance, reg)
-#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value)
-
-#define NCR5380_pread macscsi_pread
-#define NCR5380_pwrite macscsi_pwrite
-
-#define NCR5380_intr macscsi_intr
-#define NCR5380_queue_command macscsi_queue_command
-#define NCR5380_abort macscsi_abort
-#define NCR5380_bus_reset macscsi_bus_reset
-#define NCR5380_proc_info macscsi_proc_info
-
-#define BOARD_NORMAL 0
-#define BOARD_NCR53C400 1
-
-#endif /* ndef HOSTS_C */
-#endif /* ndef ASM */
-#endif /* MAC_NCR5380_H */
-
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
index 5c1776406c96..a00622c0c526 100644
--- a/drivers/scsi/megaraid.c
+++ b/drivers/scsi/megaraid.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* Linux MegaRAID device driver
*
* Copyright (c) 2002 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* Copyright (c) 2002 Red Hat, Inc. All rights reserved.
* - fixes
* - speed-ups (list handling fixes, issued_list, optimizations.)
@@ -28,17 +24,17 @@
* This driver is supported by LSI Logic, with assistance from Red Hat, Dell,
* and others. Please send updates to the mailing list
* linux-scsi@vger.kernel.org .
- *
*/
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/reboot.h>
#include <linux/module.h>
#include <linux/list.h>
@@ -48,10 +44,14 @@
#include <linux/dma-mapping.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <scsi/scsicam.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
#include "megaraid.h"
@@ -128,7 +128,7 @@ static int trace_level;
/**
* mega_setup_mailbox()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Allocates a 8 byte aligned memory for the handshake mailbox.
*/
@@ -137,8 +137,10 @@ mega_setup_mailbox(adapter_t *adapter)
{
unsigned long align;
- adapter->una_mbox64 = pci_alloc_consistent(adapter->dev,
- sizeof(mbox64_t), &adapter->una_mbox64_dma);
+ adapter->una_mbox64 = dma_alloc_coherent(&adapter->dev->dev,
+ sizeof(mbox64_t),
+ &adapter->una_mbox64_dma,
+ GFP_KERNEL);
if( !adapter->una_mbox64 ) return -1;
@@ -194,23 +196,21 @@ mega_query_adapter(adapter_t *adapter)
{
dma_addr_t prod_info_dma_handle;
mega_inquiry3 *inquiry3;
- u8 raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
+ struct mbox_out mbox;
+ u8 *raw_mbox = (u8 *)&mbox;
int retval;
/* Initialize adapter inquiry mailbox */
- mbox = (mbox_t *)raw_mbox;
-
memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
- memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
/*
* Try to issue Inquiry3 command
* if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and
* update enquiry3 structure
*/
- mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+ mbox.xferaddr = (u32)adapter->buf_dma_handle;
inquiry3 = (mega_inquiry3 *)adapter->mega_buffer;
@@ -219,24 +219,25 @@ mega_query_adapter(adapter_t *adapter)
raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */
/* Issue a blocking command to the card */
- if ((retval = issue_scb_block(adapter, raw_mbox))) {
+ if (issue_scb_block(adapter, raw_mbox)) {
/* the adapter does not support 40ld */
mraid_ext_inquiry *ext_inq;
mraid_inquiry *inq;
dma_addr_t dma_handle;
- ext_inq = pci_alloc_consistent(adapter->dev,
- sizeof(mraid_ext_inquiry), &dma_handle);
+ ext_inq = dma_alloc_coherent(&adapter->dev->dev,
+ sizeof(mraid_ext_inquiry),
+ &dma_handle, GFP_KERNEL);
if( ext_inq == NULL ) return -1;
inq = &ext_inq->raid_inq;
- mbox->m_out.xferaddr = (u32)dma_handle;
+ mbox.xferaddr = (u32)dma_handle;
/*issue old 0x04 command to adapter */
- mbox->m_out.cmd = MEGA_MBOXCMD_ADPEXTINQ;
+ mbox.cmd = MEGA_MBOXCMD_ADPEXTINQ;
issue_scb_block(adapter, raw_mbox);
@@ -247,8 +248,9 @@ mega_query_adapter(adapter_t *adapter)
mega_8_to_40ld(inq, inquiry3,
(mega_product_info *)&adapter->product_info);
- pci_free_consistent(adapter->dev, sizeof(mraid_ext_inquiry),
- ext_inq, dma_handle);
+ dma_free_coherent(&adapter->dev->dev,
+ sizeof(mraid_ext_inquiry), ext_inq,
+ dma_handle);
} else { /*adapter supports 40ld */
adapter->flag |= BOARD_40LD;
@@ -257,22 +259,23 @@ mega_query_adapter(adapter_t *adapter)
* get product_info, which is static information and will be
* unchanged
*/
- prod_info_dma_handle = pci_map_single(adapter->dev, (void *)
- &adapter->product_info,
- sizeof(mega_product_info), PCI_DMA_FROMDEVICE);
+ prod_info_dma_handle = dma_map_single(&adapter->dev->dev,
+ (void *)&adapter->product_info,
+ sizeof(mega_product_info),
+ DMA_FROM_DEVICE);
- mbox->m_out.xferaddr = prod_info_dma_handle;
+ mbox.xferaddr = prod_info_dma_handle;
raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */
if ((retval = issue_scb_block(adapter, raw_mbox)))
- printk(KERN_WARNING
- "megaraid: Product_info cmd failed with error: %d\n",
+ dev_warn(&adapter->dev->dev,
+ "Product_info cmd failed with error: %d\n",
retval);
- pci_unmap_single(adapter->dev, prod_info_dma_handle,
- sizeof(mega_product_info), PCI_DMA_FROMDEVICE);
+ dma_unmap_single(&adapter->dev->dev, prod_info_dma_handle,
+ sizeof(mega_product_info), DMA_FROM_DEVICE);
}
@@ -305,20 +308,24 @@ mega_query_adapter(adapter_t *adapter)
adapter->host->sg_tablesize = adapter->sglen;
-
- /* use HP firmware and bios version encoding */
- if (adapter->product_info.subsysvid == HP_SUBSYS_VID) {
- sprintf (adapter->fw_version, "%c%d%d.%d%d",
+ /* use HP firmware and bios version encoding
+ Note: fw_version[0|1] and bios_version[0|1] were originally shifted
+ right 8 bits making them zero. This 0 value was hardcoded to fix
+ sparse warnings. */
+ if (adapter->product_info.subsysvid == PCI_VENDOR_ID_HP) {
+ snprintf(adapter->fw_version, sizeof(adapter->fw_version),
+ "%c%d%d.%d%d",
adapter->product_info.fw_version[2],
- adapter->product_info.fw_version[1] >> 8,
+ 0,
adapter->product_info.fw_version[1] & 0x0f,
- adapter->product_info.fw_version[0] >> 8,
+ 0,
adapter->product_info.fw_version[0] & 0x0f);
- sprintf (adapter->bios_version, "%c%d%d.%d%d",
+ snprintf(adapter->bios_version, sizeof(adapter->fw_version),
+ "%c%d%d.%d%d",
adapter->product_info.bios_version[2],
- adapter->product_info.bios_version[1] >> 8,
+ 0,
adapter->product_info.bios_version[1] & 0x0f,
- adapter->product_info.bios_version[0] >> 8,
+ 0,
adapter->product_info.bios_version[0] & 0x0f);
} else {
memcpy(adapter->fw_version,
@@ -331,7 +338,7 @@ mega_query_adapter(adapter_t *adapter)
adapter->bios_version[4] = 0;
}
- printk(KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives.\n",
+ dev_notice(&adapter->dev->dev, "[%s:%s] detected %d logical drives\n",
adapter->fw_version, adapter->bios_version, adapter->numldrv);
/*
@@ -339,7 +346,7 @@ mega_query_adapter(adapter_t *adapter)
*/
adapter->support_ext_cdb = mega_support_ext_cdb(adapter);
if (adapter->support_ext_cdb)
- printk(KERN_NOTICE "megaraid: supports extended CDBs.\n");
+ dev_notice(&adapter->dev->dev, "supports extended CDBs\n");
return 0;
@@ -347,7 +354,7 @@ mega_query_adapter(adapter_t *adapter)
/**
* mega_runpendq()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Runs through the list of pending requests.
*/
@@ -365,8 +372,7 @@ mega_runpendq(adapter_t *adapter)
*
* The command queuing entry point for the mid-layer.
*/
-static int
-megaraid_queue_lck(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *))
+static int megaraid_queue_lck(struct scsi_cmnd *scmd)
{
adapter_t *adapter;
scb_t *scb;
@@ -375,9 +381,6 @@ megaraid_queue_lck(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *))
adapter = (adapter_t *)scmd->device->host->hostdata;
- scmd->scsi_done = done;
-
-
/*
* Allocate and build a SCB request
* busy flag will be set if mega_build_cmd() command could not
@@ -413,14 +416,14 @@ static DEF_SCSI_QCMD(megaraid_queue)
/**
* mega_allocate_scb()
- * @adapter - pointer to our soft state
- * @cmd - scsi command from the mid-layer
+ * @adapter: pointer to our soft state
+ * @cmd: scsi command from the mid-layer
*
* Allocate a SCB structure. This is the central structure for controller
* commands.
*/
static inline scb_t *
-mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd)
+mega_allocate_scb(adapter_t *adapter, struct scsi_cmnd *cmd)
{
struct list_head *head = &adapter->free_list;
scb_t *scb;
@@ -444,15 +447,15 @@ mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd)
/**
* mega_get_ldrv_num()
- * @adapter - pointer to our soft state
- * @cmd - scsi mid layer command
- * @channel - channel on the controller
+ * @adapter: pointer to our soft state
+ * @cmd: scsi mid layer command
+ * @channel: channel on the controller
*
* Calculate the logical drive number based on the information in scsi command
* and the channel number.
*/
static inline int
-mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel)
+mega_get_ldrv_num(adapter_t *adapter, struct scsi_cmnd *cmd, int channel)
{
int tgt;
int ldrv_num;
@@ -491,9 +494,9 @@ mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel)
if (adapter->support_random_del && adapter->read_ldidmap )
switch (cmd->cmnd[0]) {
- case READ_6: /* fall through */
- case WRITE_6: /* fall through */
- case READ_10: /* fall through */
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
case WRITE_10:
ldrv_num += 0x80;
}
@@ -503,9 +506,9 @@ mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel)
/**
* mega_build_cmd()
- * @adapter - pointer to our soft state
- * @cmd - Prepare using this scsi command
- * @busy - busy flag if no resources
+ * @adapter: pointer to our soft state
+ * @cmd: Prepare using this scsi command
+ * @busy: busy flag if no resources
*
* Prepares a command and scatter gather list for the controller. This routine
* also finds out if the commands is intended for a logical drive or a
@@ -515,26 +518,18 @@ mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel)
* boot settings.
*/
static scb_t *
-mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
+mega_build_cmd(adapter_t *adapter, struct scsi_cmnd *cmd, int *busy)
{
- mega_ext_passthru *epthru;
mega_passthru *pthru;
scb_t *scb;
mbox_t *mbox;
- long seg;
+ u32 seg;
char islogical;
int max_ldrv_num;
int channel = 0;
int target = 0;
int ldrv_num = 0; /* logical drive number */
-
- /*
- * filter the internal and ioctl commands
- */
- if((cmd->cmnd[0] == MEGA_INTERNAL_CMD))
- return (scb_t *)cmd->host_scribble;
-
/*
* We know what channels our logical drives are on - mega_find_card()
*/
@@ -589,7 +584,7 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
/* have just LUN 0 for each target on virtual channels */
if (cmd->device->lun) {
cmd->result = (DID_BAD_TARGET << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
}
@@ -608,7 +603,7 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
if(ldrv_num > max_ldrv_num ) {
cmd->result = (DID_BAD_TARGET << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
}
@@ -620,7 +615,7 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
* devices
*/
cmd->result = (DID_BAD_TARGET << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
}
}
@@ -640,7 +635,7 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
*/
if( !adapter->has_cluster ) {
cmd->result = (DID_OK << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
}
@@ -653,12 +648,12 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
scb->raw_mbox[2] = MEGA_RESERVATION_STATUS;
scb->raw_mbox[3] = ldrv_num;
- scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_direction = DMA_NONE;
return scb;
#else
cmd->result = (DID_OK << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
#endif
@@ -667,13 +662,13 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
struct scatterlist *sg;
sg = scsi_sglist(cmd);
- buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset;
+ buf = kmap_atomic(sg_page(sg)) + sg->offset;
memset(buf, 0, cmd->cmnd[4]);
- kunmap_atomic(buf - sg->offset, KM_IRQ0);
+ kunmap_atomic(buf - sg->offset);
cmd->result = (DID_OK << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
}
@@ -682,11 +677,11 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
if(!(adapter->flag & (1L << cmd->device->channel))) {
- printk(KERN_NOTICE
- "scsi%d: scanning scsi channel %d ",
+ dev_notice(&adapter->dev->dev,
+ "scsi%d: scanning scsi channel %d "
+ "for logical drives\n",
adapter->host->host_no,
cmd->device->channel);
- printk("for logical drives.\n");
adapter->flag |= (1L << cmd->device->channel);
}
@@ -717,7 +712,7 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU;
}
- scb->dma_direction = PCI_DMA_FROMDEVICE;
+ scb->dma_direction = DMA_FROM_DEVICE;
pthru->numsgelements = mega_build_sglist(adapter, scb,
&pthru->dataxferaddr, &pthru->dataxferlen);
@@ -847,21 +842,21 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
* If it is a read command
*/
if( (*cmd->cmnd & 0x0F) == 0x08 ) {
- scb->dma_direction = PCI_DMA_FROMDEVICE;
+ scb->dma_direction = DMA_FROM_DEVICE;
}
else {
- scb->dma_direction = PCI_DMA_TODEVICE;
+ scb->dma_direction = DMA_TO_DEVICE;
}
/* Calculate Scatter-Gather info */
mbox->m_out.numsgelements = mega_build_sglist(adapter, scb,
- (u32 *)&mbox->m_out.xferaddr, (u32 *)&seg);
+ (u32 *)&mbox->m_out.xferaddr, &seg);
return scb;
#if MEGA_HAVE_CLUSTERING
- case RESERVE: /* Fall through */
- case RELEASE:
+ case RESERVE_6:
+ case RELEASE_6:
/*
* Do we support clustering and is the support enabled
@@ -869,7 +864,7 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
if( ! adapter->has_cluster ) {
cmd->result = (DID_BAD_TARGET << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
}
@@ -880,19 +875,19 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
}
scb->raw_mbox[0] = MEGA_CLUSTER_CMD;
- scb->raw_mbox[2] = ( *cmd->cmnd == RESERVE ) ?
+ scb->raw_mbox[2] = *cmd->cmnd == RESERVE_6 ?
MEGA_RESERVE_LD : MEGA_RELEASE_LD;
scb->raw_mbox[3] = ldrv_num;
- scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_direction = DMA_NONE;
return scb;
#endif
default:
cmd->result = (DID_BAD_TARGET << 16);
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return NULL;
}
}
@@ -912,7 +907,7 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
if( adapter->support_ext_cdb ) {
- epthru = mega_prepare_extpassthru(adapter, scb, cmd,
+ mega_prepare_extpassthru(adapter, scb, cmd,
channel, target);
mbox->m_out.cmd = MEGA_MBOXCMD_EXTPTHRU;
@@ -944,17 +939,17 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
/**
* mega_prepare_passthru()
- * @adapter - pointer to our soft state
- * @scb - our scsi control block
- * @cmd - scsi command from the mid-layer
- * @channel - actual channel on the controller
- * @target - actual id on the controller.
+ * @adapter: pointer to our soft state
+ * @scb: our scsi control block
+ * @cmd: scsi command from the mid-layer
+ * @channel: actual channel on the controller
+ * @target: actual id on the controller.
*
* prepare a command for the scsi physical devices.
*/
static mega_passthru *
-mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
- int channel, int target)
+mega_prepare_passthru(adapter_t *adapter, scb_t *scb, struct scsi_cmnd *cmd,
+ int channel, int target)
{
mega_passthru *pthru;
@@ -979,7 +974,7 @@ mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len);
/* Not sure about the direction */
- scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+ scb->dma_direction = DMA_BIDIRECTIONAL;
/* Special Code for Handling READ_CAPA/ INQ using bounce buffers */
switch (cmd->cmnd[0]) {
@@ -987,15 +982,15 @@ mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
case READ_CAPACITY:
if(!(adapter->flag & (1L << cmd->device->channel))) {
- printk(KERN_NOTICE
- "scsi%d: scanning scsi channel %d [P%d] ",
+ dev_notice(&adapter->dev->dev,
+ "scsi%d: scanning scsi channel %d [P%d] "
+ "for physical devices\n",
adapter->host->host_no,
cmd->device->channel, channel);
- printk("for physical devices.\n");
adapter->flag |= (1L << cmd->device->channel);
}
- /* Fall through */
+ fallthrough;
default:
pthru->numsgelements = mega_build_sglist(adapter, scb,
&pthru->dataxferaddr, &pthru->dataxferlen);
@@ -1007,18 +1002,19 @@ mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
/**
* mega_prepare_extpassthru()
- * @adapter - pointer to our soft state
- * @scb - our scsi control block
- * @cmd - scsi command from the mid-layer
- * @channel - actual channel on the controller
- * @target - actual id on the controller.
+ * @adapter: pointer to our soft state
+ * @scb: our scsi control block
+ * @cmd: scsi command from the mid-layer
+ * @channel: actual channel on the controller
+ * @target: actual id on the controller.
*
* prepare a command for the scsi physical devices. This rountine prepares
* commands for devices which can take extended CDBs (>10 bytes)
*/
static mega_ext_passthru *
-mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
- int channel, int target)
+mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb,
+ struct scsi_cmnd *cmd,
+ int channel, int target)
{
mega_ext_passthru *epthru;
@@ -1042,22 +1038,22 @@ mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
memcpy(epthru->cdb, cmd->cmnd, cmd->cmd_len);
/* Not sure about the direction */
- scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+ scb->dma_direction = DMA_BIDIRECTIONAL;
switch(cmd->cmnd[0]) {
case INQUIRY:
case READ_CAPACITY:
if(!(adapter->flag & (1L << cmd->device->channel))) {
- printk(KERN_NOTICE
- "scsi%d: scanning scsi channel %d [P%d] ",
+ dev_notice(&adapter->dev->dev,
+ "scsi%d: scanning scsi channel %d [P%d] "
+ "for physical devices\n",
adapter->host->host_no,
cmd->device->channel, channel);
- printk("for physical devices.\n");
adapter->flag |= (1L << cmd->device->channel);
}
- /* Fall through */
+ fallthrough;
default:
epthru->numsgelements = mega_build_sglist(adapter, scb,
&epthru->dataxferaddr, &epthru->dataxferlen);
@@ -1091,8 +1087,8 @@ __mega_runpendq(adapter_t *adapter)
/**
* issue_scb()
- * @adapter - pointer to our soft state
- * @scb - scsi control block
+ * @adapter: pointer to our soft state
+ * @scb: scsi control block
*
* Post a command to the card if the mailbox is available, otherwise return
* busy. We also take the scb from the pending list if the mailbox is
@@ -1172,8 +1168,8 @@ mega_busywait_mbox (adapter_t *adapter)
/**
* issue_scb_block()
- * @adapter - pointer to our soft state
- * @raw_mbox - the mailbox
+ * @adapter: pointer to our soft state
+ * @raw_mbox: the mailbox
*
* Issue a scb in synchronous and non-interrupt mode
*/
@@ -1245,7 +1241,7 @@ issue_scb_block(adapter_t *adapter, u_char *raw_mbox)
return mbox->m_in.status;
bug_blocked_mailbox:
- printk(KERN_WARNING "megaraid: Blocked mailbox......!!\n");
+ dev_warn(&adapter->dev->dev, "Blocked mailbox......!!\n");
udelay (1000);
return -1;
}
@@ -1253,8 +1249,8 @@ bug_blocked_mailbox:
/**
* megaraid_isr_iomapped()
- * @irq - irq
- * @devp - pointer to our soft state
+ * @irq: irq
+ * @devp: pointer to our soft state
*
* Interrupt service routine for io-mapped controllers.
* Find out if our device is interrupting. If yes, acknowledge the interrupt
@@ -1329,8 +1325,8 @@ megaraid_isr_iomapped(int irq, void *devp)
/**
* megaraid_isr_memmapped()
- * @irq - irq
- * @devp - pointer to our soft state
+ * @irq: irq
+ * @devp: pointer to our soft state
*
* Interrupt service routine for memory-mapped controllers.
* Find out if our device is interrupting. If yes, acknowledge the interrupt
@@ -1407,10 +1403,10 @@ megaraid_isr_memmapped(int irq, void *devp)
}
/**
* mega_cmd_done()
- * @adapter - pointer to our soft state
- * @completed - array of ids of completed commands
- * @nstatus - number of completed commands
- * @status - status of the last command completed
+ * @adapter: pointer to our soft state
+ * @completed: array of ids of completed commands
+ * @nstatus: number of completed commands
+ * @status: status of the last command completed
*
* Complete the commands and call the scsi mid-layer callback hooks.
*/
@@ -1419,7 +1415,7 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
{
mega_ext_passthru *epthru = NULL;
struct scatterlist *sgl;
- Scsi_Cmnd *cmd = NULL;
+ struct scsi_cmnd *cmd = NULL;
mega_passthru *pthru = NULL;
mbox_t *mbox = NULL;
u8 c;
@@ -1436,28 +1432,31 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
cmdid = completed[i];
- if( cmdid == CMDID_INT_CMDS ) { /* internal command */
+ /*
+ * Only free SCBs for the commands coming down from the
+ * mid-layer, not for which were issued internally
+ *
+ * For internal command, restore the status returned by the
+ * firmware so that user can interpret it.
+ */
+ if (cmdid == CMDID_INT_CMDS) {
scb = &adapter->int_scb;
cmd = scb->cmd;
- mbox = (mbox_t *)scb->raw_mbox;
- /*
- * Internal command interface do not fire the extended
- * passthru or 64-bit passthru
- */
- pthru = scb->pthru;
+ list_del_init(&scb->list);
+ scb->state = SCB_FREE;
- }
- else {
+ adapter->int_status = status;
+ complete(&adapter->int_waitq);
+ } else {
scb = &adapter->scb_list[cmdid];
/*
* Make sure f/w has completed a valid command
*/
if( !(scb->state & SCB_ISSUED) || scb->cmd == NULL ) {
- printk(KERN_CRIT
- "megaraid: invalid command ");
- printk("Id %d, scb->state:%x, scsi cmd:%p\n",
+ dev_crit(&adapter->dev->dev, "invalid command "
+ "Id %d, scb->state:%x, scsi cmd:%p\n",
cmdid, scb->state, scb->cmd);
continue;
@@ -1468,8 +1467,8 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
*/
if( scb->state & SCB_ABORT ) {
- printk(KERN_WARNING
- "megaraid: aborted cmd [%x] complete.\n",
+ dev_warn(&adapter->dev->dev,
+ "aborted cmd [%x] complete\n",
scb->idx);
scb->cmd->result = (DID_ABORT << 16);
@@ -1487,8 +1486,8 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
*/
if( scb->state & SCB_RESET ) {
- printk(KERN_WARNING
- "megaraid: reset cmd [%x] complete.\n",
+ dev_warn(&adapter->dev->dev,
+ "reset cmd [%x] complete\n",
scb->idx);
scb->cmd->result = (DID_RESET << 16);
@@ -1554,8 +1553,7 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
if( sg_page(sgl) ) {
c = *(unsigned char *) sg_virt(&sgl[0]);
} else {
- printk(KERN_WARNING
- "megaraid: invalid sg.\n");
+ dev_warn(&adapter->dev->dev, "invalid sg\n");
c = 0;
}
@@ -1584,9 +1582,7 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
memcpy(cmd->sense_buffer, pthru->reqsensearea,
14);
- cmd->result = (DRIVER_SENSE << 24) |
- (DID_OK << 16) |
- (CHECK_CONDITION << 1);
+ cmd->result = SAM_STAT_CHECK_CONDITION;
}
else {
if (mbox->m_out.cmd == MEGA_MBOXCMD_EXTPTHRU) {
@@ -1594,14 +1590,10 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
memcpy(cmd->sense_buffer,
epthru->reqsensearea, 14);
- cmd->result = (DRIVER_SENSE << 24) |
- (DID_OK << 16) |
- (CHECK_CONDITION << 1);
- } else {
- cmd->sense_buffer[0] = 0x70;
- cmd->sense_buffer[2] = ABORTED_COMMAND;
- cmd->result |= (CHECK_CONDITION << 1);
- }
+ cmd->result = SAM_STAT_CHECK_CONDITION;
+ } else
+ scsi_build_sense(cmd, 0,
+ ABORTED_COMMAND, 0, 0);
}
break;
@@ -1618,7 +1610,7 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
*/
if( cmd->cmnd[0] == TEST_UNIT_READY ) {
cmd->result |= (DID_ERROR << 16) |
- (RESERVATION_CONFLICT << 1);
+ SAM_STAT_RESERVATION_CONFLICT;
}
else
/*
@@ -1626,36 +1618,18 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
* failed or the input parameter is invalid
*/
if( status == 1 &&
- (cmd->cmnd[0] == RESERVE ||
- cmd->cmnd[0] == RELEASE) ) {
+ (cmd->cmnd[0] == RESERVE_6 ||
+ cmd->cmnd[0] == RELEASE_6) ) {
cmd->result |= (DID_ERROR << 16) |
- (RESERVATION_CONFLICT << 1);
+ SAM_STAT_RESERVATION_CONFLICT;
}
else
#endif
cmd->result |= (DID_BAD_TARGET << 16)|status;
}
- /*
- * Only free SCBs for the commands coming down from the
- * mid-layer, not for which were issued internally
- *
- * For internal command, restore the status returned by the
- * firmware so that user can interpret it.
- */
- if( cmdid == CMDID_INT_CMDS ) { /* internal command */
- cmd->result = status;
-
- /*
- * Remove the internal command from the pending list
- */
- list_del_init(&scb->list);
- scb->state = SCB_FREE;
- }
- else {
- mega_free_scb(adapter, scb);
- }
+ mega_free_scb(adapter, scb);
/* Add Scsi_Command to end of completed queue */
list_add_tail(SCSI_LIST(cmd), &adapter->completed_list);
@@ -1671,16 +1645,10 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
static void
mega_rundoneq (adapter_t *adapter)
{
- Scsi_Cmnd *cmd;
- struct list_head *pos;
-
- list_for_each(pos, &adapter->completed_list) {
-
- struct scsi_pointer* spos = (struct scsi_pointer *)pos;
+ struct megaraid_cmd_priv *cmd_priv;
- cmd = list_entry(spos, Scsi_Cmnd, SCp);
- cmd->scsi_done(cmd);
- }
+ list_for_each_entry(cmd_priv, &adapter->completed_list, entry)
+ scsi_done(megaraid_to_scsi_cmd(cmd_priv));
INIT_LIST_HEAD(&adapter->completed_list);
}
@@ -1741,7 +1709,7 @@ static int
mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len)
{
struct scatterlist *sg;
- Scsi_Cmnd *cmd;
+ struct scsi_cmnd *cmd;
int sgcnt;
int idx;
@@ -1837,25 +1805,25 @@ mega_free_sgl(adapter_t *adapter)
scb = &adapter->scb_list[i];
if( scb->sgl64 ) {
- pci_free_consistent(adapter->dev,
- sizeof(mega_sgl64) * adapter->sglen,
- scb->sgl64,
- scb->sgl_dma_addr);
+ dma_free_coherent(&adapter->dev->dev,
+ sizeof(mega_sgl64) * adapter->sglen,
+ scb->sgl64, scb->sgl_dma_addr);
scb->sgl64 = NULL;
}
if( scb->pthru ) {
- pci_free_consistent(adapter->dev, sizeof(mega_passthru),
- scb->pthru, scb->pthru_dma_addr);
+ dma_free_coherent(&adapter->dev->dev,
+ sizeof(mega_passthru), scb->pthru,
+ scb->pthru_dma_addr);
scb->pthru = NULL;
}
if( scb->epthru ) {
- pci_free_consistent(adapter->dev,
- sizeof(mega_ext_passthru),
- scb->epthru, scb->epthru_dma_addr);
+ dma_free_coherent(&adapter->dev->dev,
+ sizeof(mega_ext_passthru),
+ scb->epthru, scb->epthru_dma_addr);
scb->epthru = NULL;
}
@@ -1879,7 +1847,7 @@ megaraid_info(struct Scsi_Host *host)
"LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns",
adapter->fw_version, adapter->product_info.max_commands,
adapter->host->max_id, adapter->host->max_channel,
- adapter->host->max_lun);
+ (u32)adapter->host->max_lun);
return buffer;
}
@@ -1888,7 +1856,7 @@ megaraid_info(struct Scsi_Host *host)
* aborted. All the commands issued to the F/W must complete.
*/
static int
-megaraid_abort(Scsi_Cmnd *cmd)
+megaraid_abort(struct scsi_cmnd *cmd)
{
adapter_t *adapter;
int rval;
@@ -1921,17 +1889,16 @@ megaraid_reset(struct scsi_cmnd *cmd)
mc.opcode = MEGA_RESET_RESERVATIONS;
if( mega_internal_command(adapter, &mc, NULL) != 0 ) {
- printk(KERN_WARNING
- "megaraid: reservation reset failed.\n");
+ dev_warn(&adapter->dev->dev, "reservation reset failed\n");
}
else {
- printk(KERN_INFO "megaraid: reservation reset.\n");
+ dev_info(&adapter->dev->dev, "reservation reset\n");
}
#endif
spin_lock_irq(&adapter->lock);
- rval = megaraid_abort_and_reset(adapter, cmd, SCB_RESET);
+ rval = megaraid_abort_and_reset(adapter, NULL, SCB_RESET);
/*
* This is required here to complete any completed requests
@@ -1945,32 +1912,35 @@ megaraid_reset(struct scsi_cmnd *cmd)
/**
* megaraid_abort_and_reset()
- * @adapter - megaraid soft state
- * @cmd - scsi command to be aborted or reset
- * @aor - abort or reset flag
+ * @adapter: megaraid soft state
+ * @cmd: scsi command to be aborted or reset
+ * @aor: abort or reset flag
*
* Try to locate the scsi command in the pending queue. If found and is not
* issued to the controller, abort/reset it. Otherwise return failure
*/
static int
-megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor)
+megaraid_abort_and_reset(adapter_t *adapter, struct scsi_cmnd *cmd, int aor)
{
struct list_head *pos, *next;
scb_t *scb;
- printk(KERN_WARNING "megaraid: %s cmd=%x <c=%d t=%d l=%d>\n",
- (aor == SCB_ABORT)? "ABORTING":"RESET",
- cmd->cmnd[0], cmd->device->channel,
- cmd->device->id, cmd->device->lun);
+ if (aor == SCB_ABORT)
+ dev_warn(&adapter->dev->dev,
+ "ABORTING cmd=%x <c=%d t=%d l=%d>\n",
+ cmd->cmnd[0], cmd->device->channel,
+ cmd->device->id, (u32)cmd->device->lun);
+ else
+ dev_warn(&adapter->dev->dev, "RESETTING\n");
if(list_empty(&adapter->pending_list))
- return FALSE;
+ return FAILED;
list_for_each_safe(pos, next, &adapter->pending_list) {
scb = list_entry(pos, scb_t, list);
- if (scb->cmd == cmd) { /* Found command */
+ if (!cmd || scb->cmd == cmd) { /* Found command */
scb->state |= aor;
@@ -1982,54 +1952,46 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor)
*/
if( scb->state & SCB_ISSUED ) {
- printk(KERN_WARNING
- "megaraid: %s[%x], fw owner.\n",
+ dev_warn(&adapter->dev->dev,
+ "%s[%x], fw owner\n",
(aor==SCB_ABORT) ? "ABORTING":"RESET",
scb->idx);
- return FALSE;
+ return FAILED;
}
- else {
-
- /*
- * Not yet issued! Remove from the pending
- * list
- */
- printk(KERN_WARNING
- "megaraid: %s-[%x], driver owner.\n",
- (aor==SCB_ABORT) ? "ABORTING":"RESET",
- scb->idx);
-
- mega_free_scb(adapter, scb);
-
- if( aor == SCB_ABORT ) {
- cmd->result = (DID_ABORT << 16);
- }
- else {
- cmd->result = (DID_RESET << 16);
- }
+ /*
+ * Not yet issued! Remove from the pending
+ * list
+ */
+ dev_warn(&adapter->dev->dev,
+ "%s-[%x], driver owner\n",
+ (cmd) ? "ABORTING":"RESET",
+ scb->idx);
+ mega_free_scb(adapter, scb);
+ if (cmd) {
+ cmd->result = (DID_ABORT << 16);
list_add_tail(SCSI_LIST(cmd),
- &adapter->completed_list);
-
- return TRUE;
+ &adapter->completed_list);
}
+
+ return SUCCESS;
}
}
- return FALSE;
+ return FAILED;
}
static inline int
make_local_pdev(adapter_t *adapter, struct pci_dev **pdev)
{
- *pdev = alloc_pci_dev();
+ *pdev = pci_alloc_dev(NULL);
if( *pdev == NULL ) return -1;
memcpy(*pdev, adapter->dev, sizeof(struct pci_dev));
- if( pci_set_dma_mask(*pdev, DMA_BIT_MASK(32)) != 0 ) {
+ if (dma_set_mask(&(*pdev)->dev, DMA_BIT_MASK(32)) != 0) {
kfree(*pdev);
return -1;
}
@@ -2045,407 +2007,225 @@ free_local_pdev(struct pci_dev *pdev)
/**
* mega_allocate_inquiry()
- * @dma_handle - handle returned for dma address
- * @pdev - handle to pci device
+ * @dma_handle: handle returned for dma address
+ * @pdev: handle to pci device
*
* allocates memory for inquiry structure
*/
static inline void *
mega_allocate_inquiry(dma_addr_t *dma_handle, struct pci_dev *pdev)
{
- return pci_alloc_consistent(pdev, sizeof(mega_inquiry3), dma_handle);
+ return dma_alloc_coherent(&pdev->dev, sizeof(mega_inquiry3),
+ dma_handle, GFP_KERNEL);
}
static inline void
mega_free_inquiry(void *inquiry, dma_addr_t dma_handle, struct pci_dev *pdev)
{
- pci_free_consistent(pdev, sizeof(mega_inquiry3), inquiry, dma_handle);
+ dma_free_coherent(&pdev->dev, sizeof(mega_inquiry3), inquiry,
+ dma_handle);
}
#ifdef CONFIG_PROC_FS
/* Following code handles /proc fs */
-#define CREATE_READ_PROC(string, func) create_proc_read_entry(string, \
- S_IRUSR | S_IFREG, \
- controller_proc_dir_entry, \
- func, adapter)
-
/**
- * mega_create_proc_entry()
- * @index - index in soft state array
- * @parent - parent node for this /proc entry
- *
- * Creates /proc entries for our controllers.
- */
-static void
-mega_create_proc_entry(int index, struct proc_dir_entry *parent)
-{
- struct proc_dir_entry *controller_proc_dir_entry = NULL;
- u8 string[64] = { 0 };
- adapter_t *adapter = hba_soft_state[index];
-
- sprintf(string, "hba%d", adapter->host->host_no);
-
- controller_proc_dir_entry =
- adapter->controller_proc_dir_entry = proc_mkdir(string, parent);
-
- if(!controller_proc_dir_entry) {
- printk(KERN_WARNING "\nmegaraid: proc_mkdir failed\n");
- return;
- }
- adapter->proc_read = CREATE_READ_PROC("config", proc_read_config);
- adapter->proc_stat = CREATE_READ_PROC("stat", proc_read_stat);
- adapter->proc_mbox = CREATE_READ_PROC("mailbox", proc_read_mbox);
-#if MEGA_HAVE_ENH_PROC
- adapter->proc_rr = CREATE_READ_PROC("rebuild-rate", proc_rebuild_rate);
- adapter->proc_battery = CREATE_READ_PROC("battery-status",
- proc_battery);
-
- /*
- * Display each physical drive on its channel
- */
- adapter->proc_pdrvstat[0] = CREATE_READ_PROC("diskdrives-ch0",
- proc_pdrv_ch0);
- adapter->proc_pdrvstat[1] = CREATE_READ_PROC("diskdrives-ch1",
- proc_pdrv_ch1);
- adapter->proc_pdrvstat[2] = CREATE_READ_PROC("diskdrives-ch2",
- proc_pdrv_ch2);
- adapter->proc_pdrvstat[3] = CREATE_READ_PROC("diskdrives-ch3",
- proc_pdrv_ch3);
-
- /*
- * Display a set of up to 10 logical drive through each of following
- * /proc entries
- */
- adapter->proc_rdrvstat[0] = CREATE_READ_PROC("raiddrives-0-9",
- proc_rdrv_10);
- adapter->proc_rdrvstat[1] = CREATE_READ_PROC("raiddrives-10-19",
- proc_rdrv_20);
- adapter->proc_rdrvstat[2] = CREATE_READ_PROC("raiddrives-20-29",
- proc_rdrv_30);
- adapter->proc_rdrvstat[3] = CREATE_READ_PROC("raiddrives-30-39",
- proc_rdrv_40);
-#endif
-}
-
-
-/**
- * proc_read_config()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_config()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
* Display configuration information about the controller.
*/
static int
-proc_read_config(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_config(struct seq_file *m, void *v)
{
- adapter_t *adapter = (adapter_t *)data;
- int len = 0;
-
- len += sprintf(page+len, "%s", MEGARAID_VERSION);
+ adapter_t *adapter = m->private;
+ seq_puts(m, MEGARAID_VERSION);
if(adapter->product_info.product_name[0])
- len += sprintf(page+len, "%s\n",
- adapter->product_info.product_name);
+ seq_printf(m, "%s\n", adapter->product_info.product_name);
- len += sprintf(page+len, "Controller Type: ");
+ seq_puts(m, "Controller Type: ");
- if( adapter->flag & BOARD_MEMMAP ) {
- len += sprintf(page+len,
- "438/466/467/471/493/518/520/531/532\n");
- }
- else {
- len += sprintf(page+len,
- "418/428/434\n");
- }
-
- if(adapter->flag & BOARD_40LD) {
- len += sprintf(page+len,
- "Controller Supports 40 Logical Drives\n");
- }
+ if( adapter->flag & BOARD_MEMMAP )
+ seq_puts(m, "438/466/467/471/493/518/520/531/532\n");
+ else
+ seq_puts(m, "418/428/434\n");
- if(adapter->flag & BOARD_64BIT) {
- len += sprintf(page+len,
- "Controller capable of 64-bit memory addressing\n");
- }
- if( adapter->has_64bit_addr ) {
- len += sprintf(page+len,
- "Controller using 64-bit memory addressing\n");
- }
- else {
- len += sprintf(page+len,
- "Controller is not using 64-bit memory addressing\n");
- }
+ if(adapter->flag & BOARD_40LD)
+ seq_puts(m, "Controller Supports 40 Logical Drives\n");
- len += sprintf(page+len, "Base = %08lx, Irq = %d, ", adapter->base,
- adapter->host->irq);
-
- len += sprintf(page+len, "Logical Drives = %d, Channels = %d\n",
- adapter->numldrv, adapter->product_info.nchannels);
-
- len += sprintf(page+len, "Version =%s:%s, DRAM = %dMb\n",
- adapter->fw_version, adapter->bios_version,
- adapter->product_info.dram_size);
-
- len += sprintf(page+len,
- "Controller Queue Depth = %d, Driver Queue Depth = %d\n",
- adapter->product_info.max_commands, adapter->max_cmds);
-
- len += sprintf(page+len, "support_ext_cdb = %d\n",
- adapter->support_ext_cdb);
- len += sprintf(page+len, "support_random_del = %d\n",
- adapter->support_random_del);
- len += sprintf(page+len, "boot_ldrv_enabled = %d\n",
- adapter->boot_ldrv_enabled);
- len += sprintf(page+len, "boot_ldrv = %d\n",
- adapter->boot_ldrv);
- len += sprintf(page+len, "boot_pdrv_enabled = %d\n",
- adapter->boot_pdrv_enabled);
- len += sprintf(page+len, "boot_pdrv_ch = %d\n",
- adapter->boot_pdrv_ch);
- len += sprintf(page+len, "boot_pdrv_tgt = %d\n",
- adapter->boot_pdrv_tgt);
- len += sprintf(page+len, "quiescent = %d\n",
- atomic_read(&adapter->quiescent));
- len += sprintf(page+len, "has_cluster = %d\n",
- adapter->has_cluster);
-
- len += sprintf(page+len, "\nModule Parameters:\n");
- len += sprintf(page+len, "max_cmd_per_lun = %d\n",
- max_cmd_per_lun);
- len += sprintf(page+len, "max_sectors_per_io = %d\n",
- max_sectors_per_io);
-
- *eof = 1;
-
- return len;
+ if(adapter->flag & BOARD_64BIT)
+ seq_puts(m, "Controller capable of 64-bit memory addressing\n");
+ if( adapter->has_64bit_addr )
+ seq_puts(m, "Controller using 64-bit memory addressing\n");
+ else
+ seq_puts(m, "Controller is not using 64-bit memory addressing\n");
+
+ seq_printf(m, "Base = %08lx, Irq = %d, ",
+ adapter->base, adapter->host->irq);
+
+ seq_printf(m, "Logical Drives = %d, Channels = %d\n",
+ adapter->numldrv, adapter->product_info.nchannels);
+
+ seq_printf(m, "Version =%s:%s, DRAM = %dMb\n",
+ adapter->fw_version, adapter->bios_version,
+ adapter->product_info.dram_size);
+
+ seq_printf(m, "Controller Queue Depth = %d, Driver Queue Depth = %d\n",
+ adapter->product_info.max_commands, adapter->max_cmds);
+
+ seq_printf(m, "support_ext_cdb = %d\n", adapter->support_ext_cdb);
+ seq_printf(m, "support_random_del = %d\n", adapter->support_random_del);
+ seq_printf(m, "boot_ldrv_enabled = %d\n", adapter->boot_ldrv_enabled);
+ seq_printf(m, "boot_ldrv = %d\n", adapter->boot_ldrv);
+ seq_printf(m, "boot_pdrv_enabled = %d\n", adapter->boot_pdrv_enabled);
+ seq_printf(m, "boot_pdrv_ch = %d\n", adapter->boot_pdrv_ch);
+ seq_printf(m, "boot_pdrv_tgt = %d\n", adapter->boot_pdrv_tgt);
+ seq_printf(m, "quiescent = %d\n",
+ atomic_read(&adapter->quiescent));
+ seq_printf(m, "has_cluster = %d\n", adapter->has_cluster);
+
+ seq_puts(m, "\nModule Parameters:\n");
+ seq_printf(m, "max_cmd_per_lun = %d\n", max_cmd_per_lun);
+ seq_printf(m, "max_sectors_per_io = %d\n", max_sectors_per_io);
+ return 0;
}
-
-
/**
- * proc_read_stat()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_stat()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
- * Diaplay statistical information about the I/O activity.
+ * Display statistical information about the I/O activity.
*/
static int
-proc_read_stat(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_stat(struct seq_file *m, void *v)
{
- adapter_t *adapter;
- int len;
+ adapter_t *adapter = m->private;
+#if MEGA_HAVE_STATS
int i;
+#endif
- i = 0; /* avoid compilation warnings */
- len = 0;
- adapter = (adapter_t *)data;
-
- len = sprintf(page, "Statistical Information for this controller\n");
- len += sprintf(page+len, "pend_cmds = %d\n",
- atomic_read(&adapter->pend_cmds));
+ seq_puts(m, "Statistical Information for this controller\n");
+ seq_printf(m, "pend_cmds = %d\n", atomic_read(&adapter->pend_cmds));
#if MEGA_HAVE_STATS
for(i = 0; i < adapter->numldrv; i++) {
- len += sprintf(page+len, "Logical Drive %d:\n", i);
-
- len += sprintf(page+len,
- "\tReads Issued = %lu, Writes Issued = %lu\n",
- adapter->nreads[i], adapter->nwrites[i]);
-
- len += sprintf(page+len,
- "\tSectors Read = %lu, Sectors Written = %lu\n",
- adapter->nreadblocks[i], adapter->nwriteblocks[i]);
-
- len += sprintf(page+len,
- "\tRead errors = %lu, Write errors = %lu\n\n",
- adapter->rd_errors[i], adapter->wr_errors[i]);
+ seq_printf(m, "Logical Drive %d:\n", i);
+ seq_printf(m, "\tReads Issued = %lu, Writes Issued = %lu\n",
+ adapter->nreads[i], adapter->nwrites[i]);
+ seq_printf(m, "\tSectors Read = %lu, Sectors Written = %lu\n",
+ adapter->nreadblocks[i], adapter->nwriteblocks[i]);
+ seq_printf(m, "\tRead errors = %lu, Write errors = %lu\n\n",
+ adapter->rd_errors[i], adapter->wr_errors[i]);
}
#else
- len += sprintf(page+len,
- "IO and error counters not compiled in driver.\n");
+ seq_puts(m, "IO and error counters not compiled in driver.\n");
#endif
-
- *eof = 1;
-
- return len;
+ return 0;
}
/**
- * proc_read_mbox()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_mbox()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
* Display mailbox information for the last command issued. This information
* is good for debugging.
*/
static int
-proc_read_mbox(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_mbox(struct seq_file *m, void *v)
{
-
- adapter_t *adapter = (adapter_t *)data;
+ adapter_t *adapter = m->private;
volatile mbox_t *mbox = adapter->mbox;
- int len = 0;
-
- len = sprintf(page, "Contents of Mail Box Structure\n");
- len += sprintf(page+len, " Fw Command = 0x%02x\n",
- mbox->m_out.cmd);
- len += sprintf(page+len, " Cmd Sequence = 0x%02x\n",
- mbox->m_out.cmdid);
- len += sprintf(page+len, " No of Sectors= %04d\n",
- mbox->m_out.numsectors);
- len += sprintf(page+len, " LBA = 0x%02x\n",
- mbox->m_out.lba);
- len += sprintf(page+len, " DTA = 0x%08x\n",
- mbox->m_out.xferaddr);
- len += sprintf(page+len, " Logical Drive= 0x%02x\n",
- mbox->m_out.logdrv);
- len += sprintf(page+len, " No of SG Elmt= 0x%02x\n",
- mbox->m_out.numsgelements);
- len += sprintf(page+len, " Busy = %01x\n",
- mbox->m_in.busy);
- len += sprintf(page+len, " Status = 0x%02x\n",
- mbox->m_in.status);
-
- *eof = 1;
-
- return len;
+
+ seq_puts(m, "Contents of Mail Box Structure\n");
+ seq_printf(m, " Fw Command = 0x%02x\n", mbox->m_out.cmd);
+ seq_printf(m, " Cmd Sequence = 0x%02x\n", mbox->m_out.cmdid);
+ seq_printf(m, " No of Sectors= %04d\n", mbox->m_out.numsectors);
+ seq_printf(m, " LBA = 0x%02x\n", mbox->m_out.lba);
+ seq_printf(m, " DTA = 0x%08x\n", mbox->m_out.xferaddr);
+ seq_printf(m, " Logical Drive= 0x%02x\n", mbox->m_out.logdrv);
+ seq_printf(m, " No of SG Elmt= 0x%02x\n", mbox->m_out.numsgelements);
+ seq_printf(m, " Busy = %01x\n", mbox->m_in.busy);
+ seq_printf(m, " Status = 0x%02x\n", mbox->m_in.status);
+ return 0;
}
/**
- * proc_rebuild_rate()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_rebuild_rate()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
* Display current rebuild rate
*/
static int
-proc_rebuild_rate(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_rebuild_rate(struct seq_file *m, void *v)
{
- adapter_t *adapter = (adapter_t *)data;
+ adapter_t *adapter = m->private;
dma_addr_t dma_handle;
caddr_t inquiry;
struct pci_dev *pdev;
- int len = 0;
- if( make_local_pdev(adapter, &pdev) != 0 ) {
- *eof = 1;
- return len;
- }
+ if( make_local_pdev(adapter, &pdev) != 0 )
+ return 0;
- if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
- free_local_pdev(pdev);
- *eof = 1;
- return len;
- }
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL )
+ goto free_pdev;
if( mega_adapinq(adapter, dma_handle) != 0 ) {
-
- len = sprintf(page, "Adapter inquiry failed.\n");
-
- printk(KERN_WARNING "megaraid: inquiry failed.\n");
-
- mega_free_inquiry(inquiry, dma_handle, pdev);
-
- free_local_pdev(pdev);
-
- *eof = 1;
-
- return len;
+ seq_puts(m, "Adapter inquiry failed.\n");
+ dev_warn(&adapter->dev->dev, "inquiry failed\n");
+ goto free_inquiry;
}
- if( adapter->flag & BOARD_40LD ) {
- len = sprintf(page, "Rebuild Rate: [%d%%]\n",
- ((mega_inquiry3 *)inquiry)->rebuild_rate);
- }
- else {
- len = sprintf(page, "Rebuild Rate: [%d%%]\n",
+ if( adapter->flag & BOARD_40LD )
+ seq_printf(m, "Rebuild Rate: [%d%%]\n",
+ ((mega_inquiry3 *)inquiry)->rebuild_rate);
+ else
+ seq_printf(m, "Rebuild Rate: [%d%%]\n",
((mraid_ext_inquiry *)
- inquiry)->raid_inq.adapter_info.rebuild_rate);
- }
-
+ inquiry)->raid_inq.adapter_info.rebuild_rate);
+free_inquiry:
mega_free_inquiry(inquiry, dma_handle, pdev);
-
+free_pdev:
free_local_pdev(pdev);
-
- *eof = 1;
-
- return len;
+ return 0;
}
/**
- * proc_battery()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_battery()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
* Display information about the battery module on the controller.
*/
static int
-proc_battery(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_battery(struct seq_file *m, void *v)
{
- adapter_t *adapter = (adapter_t *)data;
+ adapter_t *adapter = m->private;
dma_addr_t dma_handle;
caddr_t inquiry;
struct pci_dev *pdev;
- u8 battery_status = 0;
- char str[256];
- int len = 0;
+ u8 battery_status;
- if( make_local_pdev(adapter, &pdev) != 0 ) {
- *eof = 1;
- return len;
- }
+ if( make_local_pdev(adapter, &pdev) != 0 )
+ return 0;
- if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
- free_local_pdev(pdev);
- *eof = 1;
- return len;
- }
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL )
+ goto free_pdev;
if( mega_adapinq(adapter, dma_handle) != 0 ) {
-
- len = sprintf(page, "Adapter inquiry failed.\n");
-
- printk(KERN_WARNING "megaraid: inquiry failed.\n");
-
- mega_free_inquiry(inquiry, dma_handle, pdev);
-
- free_local_pdev(pdev);
-
- *eof = 1;
-
- return len;
+ seq_puts(m, "Adapter inquiry failed.\n");
+ dev_warn(&adapter->dev->dev, "inquiry failed\n");
+ goto free_inquiry;
}
if( adapter->flag & BOARD_40LD ) {
@@ -2459,146 +2239,80 @@ proc_battery(char *page, char **start, off_t offset, int count, int *eof,
/*
* Decode the battery status
*/
- sprintf(str, "Battery Status:[%d]", battery_status);
+ seq_printf(m, "Battery Status:[%d]", battery_status);
if(battery_status == MEGA_BATT_CHARGE_DONE)
- strcat(str, " Charge Done");
+ seq_puts(m, " Charge Done");
if(battery_status & MEGA_BATT_MODULE_MISSING)
- strcat(str, " Module Missing");
+ seq_puts(m, " Module Missing");
if(battery_status & MEGA_BATT_LOW_VOLTAGE)
- strcat(str, " Low Voltage");
+ seq_puts(m, " Low Voltage");
if(battery_status & MEGA_BATT_TEMP_HIGH)
- strcat(str, " Temperature High");
+ seq_puts(m, " Temperature High");
if(battery_status & MEGA_BATT_PACK_MISSING)
- strcat(str, " Pack Missing");
+ seq_puts(m, " Pack Missing");
if(battery_status & MEGA_BATT_CHARGE_INPROG)
- strcat(str, " Charge In-progress");
+ seq_puts(m, " Charge In-progress");
if(battery_status & MEGA_BATT_CHARGE_FAIL)
- strcat(str, " Charge Fail");
+ seq_puts(m, " Charge Fail");
if(battery_status & MEGA_BATT_CYCLES_EXCEEDED)
- strcat(str, " Cycles Exceeded");
-
- len = sprintf(page, "%s\n", str);
+ seq_puts(m, " Cycles Exceeded");
+ seq_putc(m, '\n');
+free_inquiry:
mega_free_inquiry(inquiry, dma_handle, pdev);
-
+free_pdev:
free_local_pdev(pdev);
-
- *eof = 1;
-
- return len;
-}
-
-
-/**
- * proc_pdrv_ch0()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
- *
- * Display information about the physical drives on physical channel 0.
- */
-static int
-proc_pdrv_ch0(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
-{
- adapter_t *adapter = (adapter_t *)data;
-
- *eof = 1;
-
- return (proc_pdrv(adapter, page, 0));
-}
-
-
-/**
- * proc_pdrv_ch1()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
- *
- * Display information about the physical drives on physical channel 1.
- */
-static int
-proc_pdrv_ch1(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
-{
- adapter_t *adapter = (adapter_t *)data;
-
- *eof = 1;
-
- return (proc_pdrv(adapter, page, 1));
+ return 0;
}
-/**
- * proc_pdrv_ch2()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
- *
- * Display information about the physical drives on physical channel 2.
+/*
+ * Display scsi inquiry
*/
-static int
-proc_pdrv_ch2(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+static void
+mega_print_inquiry(struct seq_file *m, char *scsi_inq)
{
- adapter_t *adapter = (adapter_t *)data;
-
- *eof = 1;
-
- return (proc_pdrv(adapter, page, 2));
-}
+ int i;
+ seq_puts(m, " Vendor: ");
+ seq_write(m, scsi_inq + 8, 8);
+ seq_puts(m, " Model: ");
+ seq_write(m, scsi_inq + 16, 16);
+ seq_puts(m, " Rev: ");
+ seq_write(m, scsi_inq + 32, 4);
+ seq_putc(m, '\n');
-/**
- * proc_pdrv_ch3()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
- *
- * Display information about the physical drives on physical channel 3.
- */
-static int
-proc_pdrv_ch3(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
-{
- adapter_t *adapter = (adapter_t *)data;
+ i = scsi_inq[0] & 0x1f;
+ seq_printf(m, " Type: %s ", scsi_device_type(i));
- *eof = 1;
+ seq_printf(m, " ANSI SCSI revision: %02x",
+ scsi_inq[2] & 0x07);
- return (proc_pdrv(adapter, page, 3));
+ if( (scsi_inq[2] & 0x07) == 1 && (scsi_inq[3] & 0x0f) == 1 )
+ seq_puts(m, " CCS\n");
+ else
+ seq_putc(m, '\n');
}
-
/**
- * proc_pdrv()
- * @page - buffer to write the data in
- * @adapter - pointer to our soft state
+ * proc_show_pdrv()
+ * @m: Synthetic file construction data
+ * @adapter: pointer to our soft state
+ * @channel: channel
*
* Display information about the physical drives.
*/
static int
-proc_pdrv(adapter_t *adapter, char *page, int channel)
+proc_show_pdrv(struct seq_file *m, adapter_t *adapter, int channel)
{
dma_addr_t dma_handle;
char *scsi_inq;
@@ -2609,32 +2323,25 @@ proc_pdrv(adapter_t *adapter, char *page, int channel)
u8 state;
int tgt;
int max_channels;
- int len = 0;
- char str[80];
int i;
- if( make_local_pdev(adapter, &pdev) != 0 ) {
- return len;
- }
+ if( make_local_pdev(adapter, &pdev) != 0 )
+ return 0;
- if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL )
goto free_pdev;
- }
if( mega_adapinq(adapter, dma_handle) != 0 ) {
- len = sprintf(page, "Adapter inquiry failed.\n");
-
- printk(KERN_WARNING "megaraid: inquiry failed.\n");
-
+ seq_puts(m, "Adapter inquiry failed.\n");
+ dev_warn(&adapter->dev->dev, "inquiry failed\n");
goto free_inquiry;
}
- scsi_inq = pci_alloc_consistent(pdev, 256, &scsi_inq_dma_handle);
-
+ scsi_inq = dma_alloc_coherent(&pdev->dev, 256, &scsi_inq_dma_handle,
+ GFP_KERNEL);
if( scsi_inq == NULL ) {
- len = sprintf(page, "memory not available for scsi inq.\n");
-
+ seq_puts(m, "memory not available for scsi inq.\n");
goto free_inquiry;
}
@@ -2657,39 +2364,31 @@ proc_pdrv(adapter_t *adapter, char *page, int channel)
i = channel*16 + tgt;
state = *(pdrv_state + i);
-
switch( state & 0x0F ) {
-
case PDRV_ONLINE:
- sprintf(str,
- "Channel:%2d Id:%2d State: Online",
- channel, tgt);
+ seq_printf(m, "Channel:%2d Id:%2d State: Online",
+ channel, tgt);
break;
case PDRV_FAILED:
- sprintf(str,
- "Channel:%2d Id:%2d State: Failed",
- channel, tgt);
+ seq_printf(m, "Channel:%2d Id:%2d State: Failed",
+ channel, tgt);
break;
case PDRV_RBLD:
- sprintf(str,
- "Channel:%2d Id:%2d State: Rebuild",
- channel, tgt);
+ seq_printf(m, "Channel:%2d Id:%2d State: Rebuild",
+ channel, tgt);
break;
case PDRV_HOTSPARE:
- sprintf(str,
- "Channel:%2d Id:%2d State: Hot spare",
- channel, tgt);
+ seq_printf(m, "Channel:%2d Id:%2d State: Hot spare",
+ channel, tgt);
break;
default:
- sprintf(str,
- "Channel:%2d Id:%2d State: Un-configured",
- channel, tgt);
+ seq_printf(m, "Channel:%2d Id:%2d State: Un-configured",
+ channel, tgt);
break;
-
}
/*
@@ -2708,172 +2407,87 @@ proc_pdrv(adapter_t *adapter, char *page, int channel)
* Check for overflow. We print less than 240
* characters for inquiry
*/
- if( (len + 240) >= PAGE_SIZE ) break;
-
- len += sprintf(page+len, "%s.\n", str);
-
- len += mega_print_inquiry(page+len, scsi_inq);
+ seq_puts(m, ".\n");
+ mega_print_inquiry(m, scsi_inq);
}
free_pci:
- pci_free_consistent(pdev, 256, scsi_inq, scsi_inq_dma_handle);
+ dma_free_coherent(&pdev->dev, 256, scsi_inq, scsi_inq_dma_handle);
free_inquiry:
mega_free_inquiry(inquiry, dma_handle, pdev);
free_pdev:
free_local_pdev(pdev);
-
- return len;
-}
-
-
-/*
- * Display scsi inquiry
- */
-static int
-mega_print_inquiry(char *page, char *scsi_inq)
-{
- int len = 0;
- int i;
-
- len = sprintf(page, " Vendor: ");
- for( i = 8; i < 16; i++ ) {
- len += sprintf(page+len, "%c", scsi_inq[i]);
- }
-
- len += sprintf(page+len, " Model: ");
-
- for( i = 16; i < 32; i++ ) {
- len += sprintf(page+len, "%c", scsi_inq[i]);
- }
-
- len += sprintf(page+len, " Rev: ");
-
- for( i = 32; i < 36; i++ ) {
- len += sprintf(page+len, "%c", scsi_inq[i]);
- }
-
- len += sprintf(page+len, "\n");
-
- i = scsi_inq[0] & 0x1f;
-
- len += sprintf(page+len, " Type: %s ", scsi_device_type(i));
-
- len += sprintf(page+len,
- " ANSI SCSI revision: %02x", scsi_inq[2] & 0x07);
-
- if( (scsi_inq[2] & 0x07) == 1 && (scsi_inq[3] & 0x0f) == 1 )
- len += sprintf(page+len, " CCS\n");
- else
- len += sprintf(page+len, "\n");
-
- return len;
+ return 0;
}
-
/**
- * proc_rdrv_10()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_pdrv_ch0()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
- * Display real time information about the logical drives 0 through 9.
+ * Display information about the physical drives on physical channel 0.
*/
static int
-proc_rdrv_10(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_pdrv_ch0(struct seq_file *m, void *v)
{
- adapter_t *adapter = (adapter_t *)data;
-
- *eof = 1;
-
- return (proc_rdrv(adapter, page, 0, 9));
+ return proc_show_pdrv(m, m->private, 0);
}
/**
- * proc_rdrv_20()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_pdrv_ch1()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
- * Display real time information about the logical drives 0 through 9.
+ * Display information about the physical drives on physical channel 1.
*/
static int
-proc_rdrv_20(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_pdrv_ch1(struct seq_file *m, void *v)
{
- adapter_t *adapter = (adapter_t *)data;
-
- *eof = 1;
-
- return (proc_rdrv(adapter, page, 10, 19));
+ return proc_show_pdrv(m, m->private, 1);
}
/**
- * proc_rdrv_30()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_pdrv_ch2()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
- * Display real time information about the logical drives 0 through 9.
+ * Display information about the physical drives on physical channel 2.
*/
static int
-proc_rdrv_30(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_pdrv_ch2(struct seq_file *m, void *v)
{
- adapter_t *adapter = (adapter_t *)data;
-
- *eof = 1;
-
- return (proc_rdrv(adapter, page, 20, 29));
+ return proc_show_pdrv(m, m->private, 2);
}
/**
- * proc_rdrv_40()
- * @page - buffer to write the data in
- * @start - where the actual data has been written in page
- * @offset - same meaning as the read system call
- * @count - same meaning as the read system call
- * @eof - set if no more data needs to be returned
- * @data - pointer to our soft state
+ * proc_show_pdrv_ch3()
+ * @m: Synthetic file construction data
+ * @v: File iterator
*
- * Display real time information about the logical drives 0 through 9.
+ * Display information about the physical drives on physical channel 3.
*/
static int
-proc_rdrv_40(char *page, char **start, off_t offset, int count, int *eof,
- void *data)
+proc_show_pdrv_ch3(struct seq_file *m, void *v)
{
- adapter_t *adapter = (adapter_t *)data;
-
- *eof = 1;
-
- return (proc_rdrv(adapter, page, 30, 39));
+ return proc_show_pdrv(m, m->private, 3);
}
/**
- * proc_rdrv()
- * @page - buffer to write the data in
- * @adapter - pointer to our soft state
- * @start - starting logical drive to display
- * @end - ending logical drive to display
+ * proc_show_rdrv()
+ * @m: Synthetic file construction data
+ * @adapter: pointer to our soft state
+ * @start: starting logical drive to display
+ * @end: ending logical drive to display
*
* We do not print the inquiry information since its already available through
* /proc/scsi/scsi interface
*/
static int
-proc_rdrv(adapter_t *adapter, char *page, int start, int end )
+proc_show_rdrv(struct seq_file *m, adapter_t *adapter, int start, int end )
{
dma_addr_t dma_handle;
logdrv_param *lparam;
@@ -2885,29 +2499,18 @@ proc_rdrv(adapter_t *adapter, char *page, int start, int end )
u8 *rdrv_state;
int num_ldrv;
u32 array_sz;
- int len = 0;
int i;
- if( make_local_pdev(adapter, &pdev) != 0 ) {
- return len;
- }
+ if( make_local_pdev(adapter, &pdev) != 0 )
+ return 0;
- if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
- free_local_pdev(pdev);
- return len;
- }
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL )
+ goto free_pdev;
if( mega_adapinq(adapter, dma_handle) != 0 ) {
-
- len = sprintf(page, "Adapter inquiry failed.\n");
-
- printk(KERN_WARNING "megaraid: inquiry failed.\n");
-
- mega_free_inquiry(inquiry, dma_handle, pdev);
-
- free_local_pdev(pdev);
-
- return len;
+ seq_puts(m, "Adapter inquiry failed.\n");
+ dev_warn(&adapter->dev->dev, "inquiry failed\n");
+ goto free_inquiry;
}
memset(&mc, 0, sizeof(megacmd_t));
@@ -2929,17 +2532,12 @@ proc_rdrv(adapter_t *adapter, char *page, int start, int end )
raid_inq.logdrv_info.num_ldrv;
}
- disk_array = pci_alloc_consistent(pdev, array_sz,
- &disk_array_dma_handle);
+ disk_array = dma_alloc_coherent(&pdev->dev, array_sz,
+ &disk_array_dma_handle, GFP_KERNEL);
if( disk_array == NULL ) {
- len = sprintf(page, "memory not available.\n");
-
- mega_free_inquiry(inquiry, dma_handle, pdev);
-
- free_local_pdev(pdev);
-
- return len;
+ seq_puts(m, "memory not available.\n");
+ goto free_inquiry;
}
mc.xferaddr = (u32)disk_array_dma_handle;
@@ -2949,17 +2547,8 @@ proc_rdrv(adapter_t *adapter, char *page, int start, int end )
mc.opcode = OP_DCMD_READ_CONFIG;
if( mega_internal_command(adapter, &mc, NULL) ) {
-
- len = sprintf(page, "40LD read config failed.\n");
-
- mega_free_inquiry(inquiry, dma_handle, pdev);
-
- pci_free_consistent(pdev, array_sz, disk_array,
- disk_array_dma_handle);
-
- free_local_pdev(pdev);
-
- return len;
+ seq_puts(m, "40LD read config failed.\n");
+ goto free_pci;
}
}
@@ -2967,24 +2556,10 @@ proc_rdrv(adapter_t *adapter, char *page, int start, int end )
mc.cmd = NEW_READ_CONFIG_8LD;
if( mega_internal_command(adapter, &mc, NULL) ) {
-
mc.cmd = READ_CONFIG_8LD;
-
- if( mega_internal_command(adapter, &mc,
- NULL) ){
-
- len = sprintf(page,
- "8LD read config failed.\n");
-
- mega_free_inquiry(inquiry, dma_handle, pdev);
-
- pci_free_consistent(pdev, array_sz,
- disk_array,
- disk_array_dma_handle);
-
- free_local_pdev(pdev);
-
- return len;
+ if( mega_internal_command(adapter, &mc, NULL) ) {
+ seq_puts(m, "8LD read config failed.\n");
+ goto free_pci;
}
}
}
@@ -3004,29 +2579,23 @@ proc_rdrv(adapter_t *adapter, char *page, int start, int end )
* Check for overflow. We print less than 240 characters for
* information about each logical drive.
*/
- if( (len + 240) >= PAGE_SIZE ) break;
-
- len += sprintf(page+len, "Logical drive:%2d:, ", i);
+ seq_printf(m, "Logical drive:%2d:, ", i);
switch( rdrv_state[i] & 0x0F ) {
case RDRV_OFFLINE:
- len += sprintf(page+len, "state: offline");
+ seq_puts(m, "state: offline");
break;
-
case RDRV_DEGRADED:
- len += sprintf(page+len, "state: degraded");
+ seq_puts(m, "state: degraded");
break;
-
case RDRV_OPTIMAL:
- len += sprintf(page+len, "state: optimal");
+ seq_puts(m, "state: optimal");
break;
-
case RDRV_DELETED:
- len += sprintf(page+len, "state: deleted");
+ seq_puts(m, "state: deleted");
break;
-
default:
- len += sprintf(page+len, "state: unknown");
+ seq_puts(m, "state: unknown");
break;
}
@@ -3034,84 +2603,170 @@ proc_rdrv(adapter_t *adapter, char *page, int start, int end )
* Check if check consistency or initialization is going on
* for this logical drive.
*/
- if( (rdrv_state[i] & 0xF0) == 0x20 ) {
- len += sprintf(page+len,
- ", check-consistency in progress");
- }
- else if( (rdrv_state[i] & 0xF0) == 0x10 ) {
- len += sprintf(page+len,
- ", initialization in progress");
- }
+ if( (rdrv_state[i] & 0xF0) == 0x20 )
+ seq_puts(m, ", check-consistency in progress");
+ else if( (rdrv_state[i] & 0xF0) == 0x10 )
+ seq_puts(m, ", initialization in progress");
- len += sprintf(page+len, "\n");
-
- len += sprintf(page+len, "Span depth:%3d, ",
- lparam->span_depth);
-
- len += sprintf(page+len, "RAID level:%3d, ",
- lparam->level);
-
- len += sprintf(page+len, "Stripe size:%3d, ",
- lparam->stripe_sz ? lparam->stripe_sz/2: 128);
-
- len += sprintf(page+len, "Row size:%3d\n",
- lparam->row_size);
-
+ seq_putc(m, '\n');
- len += sprintf(page+len, "Read Policy: ");
+ seq_printf(m, "Span depth:%3d, ", lparam->span_depth);
+ seq_printf(m, "RAID level:%3d, ", lparam->level);
+ seq_printf(m, "Stripe size:%3d, ",
+ lparam->stripe_sz ? lparam->stripe_sz/2: 128);
+ seq_printf(m, "Row size:%3d\n", lparam->row_size);
+ seq_puts(m, "Read Policy: ");
switch(lparam->read_ahead) {
-
case NO_READ_AHEAD:
- len += sprintf(page+len, "No read ahead, ");
+ seq_puts(m, "No read ahead, ");
break;
-
case READ_AHEAD:
- len += sprintf(page+len, "Read ahead, ");
+ seq_puts(m, "Read ahead, ");
break;
-
case ADAP_READ_AHEAD:
- len += sprintf(page+len, "Adaptive, ");
+ seq_puts(m, "Adaptive, ");
break;
}
- len += sprintf(page+len, "Write Policy: ");
-
+ seq_puts(m, "Write Policy: ");
switch(lparam->write_mode) {
-
case WRMODE_WRITE_THRU:
- len += sprintf(page+len, "Write thru, ");
+ seq_puts(m, "Write thru, ");
break;
-
case WRMODE_WRITE_BACK:
- len += sprintf(page+len, "Write back, ");
+ seq_puts(m, "Write back, ");
break;
}
- len += sprintf(page+len, "Cache Policy: ");
-
+ seq_puts(m, "Cache Policy: ");
switch(lparam->direct_io) {
-
case CACHED_IO:
- len += sprintf(page+len, "Cached IO\n\n");
+ seq_puts(m, "Cached IO\n\n");
break;
-
case DIRECT_IO:
- len += sprintf(page+len, "Direct IO\n\n");
+ seq_puts(m, "Direct IO\n\n");
break;
}
}
+free_pci:
+ dma_free_coherent(&pdev->dev, array_sz, disk_array,
+ disk_array_dma_handle);
+free_inquiry:
mega_free_inquiry(inquiry, dma_handle, pdev);
+free_pdev:
+ free_local_pdev(pdev);
+ return 0;
+}
- pci_free_consistent(pdev, array_sz, disk_array,
- disk_array_dma_handle);
+/**
+ * proc_show_rdrv_10()
+ * @m: Synthetic file construction data
+ * @v: File iterator
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_show_rdrv_10(struct seq_file *m, void *v)
+{
+ return proc_show_rdrv(m, m->private, 0, 9);
+}
- free_local_pdev(pdev);
- return len;
+/**
+ * proc_show_rdrv_20()
+ * @m: Synthetic file construction data
+ * @v: File iterator
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_show_rdrv_20(struct seq_file *m, void *v)
+{
+ return proc_show_rdrv(m, m->private, 10, 19);
}
+
+
+/**
+ * proc_show_rdrv_30()
+ * @m: Synthetic file construction data
+ * @v: File iterator
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_show_rdrv_30(struct seq_file *m, void *v)
+{
+ return proc_show_rdrv(m, m->private, 20, 29);
+}
+
+
+/**
+ * proc_show_rdrv_40()
+ * @m: Synthetic file construction data
+ * @v: File iterator
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_show_rdrv_40(struct seq_file *m, void *v)
+{
+ return proc_show_rdrv(m, m->private, 30, 39);
+}
+
+/**
+ * mega_create_proc_entry()
+ * @index: index in soft state array
+ * @parent: parent node for this /proc entry
+ *
+ * Creates /proc entries for our controllers.
+ */
+static void
+mega_create_proc_entry(int index, struct proc_dir_entry *parent)
+{
+ adapter_t *adapter = hba_soft_state[index];
+ struct proc_dir_entry *dir;
+ u8 string[16];
+
+ sprintf(string, "hba%d", adapter->host->host_no);
+ dir = proc_mkdir_data(string, 0, parent, adapter);
+ if (!dir) {
+ dev_warn(&adapter->dev->dev, "proc_mkdir failed\n");
+ return;
+ }
+
+ proc_create_single_data("config", S_IRUSR, dir,
+ proc_show_config, adapter);
+ proc_create_single_data("stat", S_IRUSR, dir,
+ proc_show_stat, adapter);
+ proc_create_single_data("mailbox", S_IRUSR, dir,
+ proc_show_mbox, adapter);
+#if MEGA_HAVE_ENH_PROC
+ proc_create_single_data("rebuild-rate", S_IRUSR, dir,
+ proc_show_rebuild_rate, adapter);
+ proc_create_single_data("battery-status", S_IRUSR, dir,
+ proc_show_battery, adapter);
+ proc_create_single_data("diskdrives-ch0", S_IRUSR, dir,
+ proc_show_pdrv_ch0, adapter);
+ proc_create_single_data("diskdrives-ch1", S_IRUSR, dir,
+ proc_show_pdrv_ch1, adapter);
+ proc_create_single_data("diskdrives-ch2", S_IRUSR, dir,
+ proc_show_pdrv_ch2, adapter);
+ proc_create_single_data("diskdrives-ch3", S_IRUSR, dir,
+ proc_show_pdrv_ch3, adapter);
+ proc_create_single_data("raiddrives-0-9", S_IRUSR, dir,
+ proc_show_rdrv_10, adapter);
+ proc_create_single_data("raiddrives-10-19", S_IRUSR, dir,
+ proc_show_rdrv_20, adapter);
+ proc_create_single_data("raiddrives-20-29", S_IRUSR, dir,
+ proc_show_rdrv_30, adapter);
+ proc_create_single_data("raiddrives-30-39", S_IRUSR, dir,
+ proc_show_rdrv_40, adapter);
+#endif
+}
+
#else
static inline void mega_create_proc_entry(int index, struct proc_dir_entry *parent)
{
@@ -3119,21 +2774,19 @@ static inline void mega_create_proc_entry(int index, struct proc_dir_entry *pare
#endif
-/**
+/*
* megaraid_biosparam()
*
* Return the disk geometry for a particular disk
*/
static int
-megaraid_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+megaraid_biosparam(struct scsi_device *sdev, struct gendisk *disk,
sector_t capacity, int geom[])
{
adapter_t *adapter;
- unsigned char *bh;
int heads;
int sectors;
int cylinders;
- int rval;
/* Get pointer to host config structure */
adapter = (adapter_t *)sdev->host->hostdata;
@@ -3160,19 +2813,12 @@ megaraid_biosparam(struct scsi_device *sdev, struct block_device *bdev,
geom[2] = cylinders;
}
else {
- bh = scsi_bios_ptable(bdev);
-
- if( bh ) {
- rval = scsi_partsize(bh, capacity,
- &geom[2], &geom[0], &geom[1]);
- kfree(bh);
- if( rval != -1 )
- return rval;
- }
+ if (scsi_partsize(disk, capacity, geom))
+ return 0;
- printk(KERN_INFO
- "megaraid: invalid partition on this disk on channel %d\n",
- sdev->channel);
+ dev_info(&adapter->dev->dev,
+ "invalid partition on this disk on channel %d\n",
+ sdev->channel);
/* Default heads (64) & sectors (32) */
heads = 64;
@@ -3197,7 +2843,7 @@ megaraid_biosparam(struct scsi_device *sdev, struct block_device *bdev,
/**
* mega_init_scb()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Allocate memory for the various pointers in the scb structures:
* scatter-gather list pointer, passthru and extended passthru structure
@@ -3225,35 +2871,35 @@ mega_init_scb(adapter_t *adapter)
scb->idx = i;
- scb->sgl64 = pci_alloc_consistent(adapter->dev,
- sizeof(mega_sgl64) * adapter->sglen,
- &scb->sgl_dma_addr);
+ scb->sgl64 = dma_alloc_coherent(&adapter->dev->dev,
+ sizeof(mega_sgl64) * adapter->sglen,
+ &scb->sgl_dma_addr, GFP_KERNEL);
scb->sgl = (mega_sglist *)scb->sgl64;
if( !scb->sgl ) {
- printk(KERN_WARNING "RAID: Can't allocate sglist.\n");
+ dev_warn(&adapter->dev->dev, "RAID: Can't allocate sglist\n");
mega_free_sgl(adapter);
return -1;
}
- scb->pthru = pci_alloc_consistent(adapter->dev,
- sizeof(mega_passthru),
- &scb->pthru_dma_addr);
+ scb->pthru = dma_alloc_coherent(&adapter->dev->dev,
+ sizeof(mega_passthru),
+ &scb->pthru_dma_addr, GFP_KERNEL);
if( !scb->pthru ) {
- printk(KERN_WARNING "RAID: Can't allocate passthru.\n");
+ dev_warn(&adapter->dev->dev, "RAID: Can't allocate passthru\n");
mega_free_sgl(adapter);
return -1;
}
- scb->epthru = pci_alloc_consistent(adapter->dev,
- sizeof(mega_ext_passthru),
- &scb->epthru_dma_addr);
+ scb->epthru = dma_alloc_coherent(&adapter->dev->dev,
+ sizeof(mega_ext_passthru),
+ &scb->epthru_dma_addr, GFP_KERNEL);
if( !scb->epthru ) {
- printk(KERN_WARNING
- "Can't allocate extended passthru.\n");
+ dev_warn(&adapter->dev->dev,
+ "Can't allocate extended passthru\n");
mega_free_sgl(adapter);
return -1;
}
@@ -3277,8 +2923,8 @@ mega_init_scb(adapter_t *adapter)
/**
* megadev_open()
- * @inode - unused
- * @filep - unused
+ * @inode: unused
+ * @filep: unused
*
* Routines for the character/ioctl interface to the driver. Find out if this
* is a valid open.
@@ -3297,10 +2943,9 @@ megadev_open (struct inode *inode, struct file *filep)
/**
* megadev_ioctl()
- * @inode - Our device inode
- * @filep - unused
- * @cmd - ioctl command
- * @arg - user buffer
+ * @filep: Our device file
+ * @cmd: ioctl command
+ * @arg: user buffer
*
* ioctl entry point for our private ioctl interface. We move the data in from
* the user space, prepare the command (if necessary, convert the old MIMD
@@ -3320,14 +2965,13 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
void *data = NULL; /* data to be transferred */
dma_addr_t data_dma_hndl; /* dma handle for data xfer area */
megacmd_t mc;
- megastat_t __user *ustats;
- int num_ldrv;
+#if MEGA_HAVE_STATS
+ megastat_t __user *ustats = NULL;
+ int num_ldrv = 0;
+#endif
u32 uxferaddr = 0;
struct pci_dev *pdev;
- ustats = NULL; /* avoid compilation warnings */
- num_ldrv = 0;
-
/*
* Make sure only USCSICMD are issued through this interface.
* MIMD application would still fire different command.
@@ -3450,8 +3094,8 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
* Do we support this feature
*/
if( !adapter->support_random_del ) {
- printk(KERN_WARNING "megaraid: logdrv ");
- printk("delete on non-supporting F/W.\n");
+ dev_warn(&adapter->dev->dev, "logdrv "
+ "delete on non-supporting F/W\n");
return (-EINVAL);
}
@@ -3475,7 +3119,7 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU64 ||
uioc.uioc_rmbox[0] == MEGA_MBOXCMD_EXTPTHRU ) {
- printk(KERN_WARNING "megaraid: rejected passthru.\n");
+ dev_warn(&adapter->dev->dev, "rejected passthru\n");
return (-EINVAL);
}
@@ -3491,9 +3135,9 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU ) {
/* Passthru commands */
- pthru = pci_alloc_consistent(pdev,
- sizeof(mega_passthru),
- &pthru_dma_hndl);
+ pthru = dma_alloc_coherent(&pdev->dev,
+ sizeof(mega_passthru),
+ &pthru_dma_hndl, GFP_KERNEL);
if( pthru == NULL ) {
free_local_pdev(pdev);
@@ -3511,9 +3155,9 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
if( copy_from_user(pthru, upthru,
sizeof(mega_passthru)) ) {
- pci_free_consistent(pdev,
- sizeof(mega_passthru), pthru,
- pthru_dma_hndl);
+ dma_free_coherent(&pdev->dev,
+ sizeof(mega_passthru),
+ pthru, pthru_dma_hndl);
free_local_pdev(pdev);
@@ -3524,15 +3168,16 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
* Is there a data transfer
*/
if( pthru->dataxferlen ) {
- data = pci_alloc_consistent(pdev,
- pthru->dataxferlen,
- &data_dma_hndl);
+ data = dma_alloc_coherent(&pdev->dev,
+ pthru->dataxferlen,
+ &data_dma_hndl,
+ GFP_KERNEL);
if( data == NULL ) {
- pci_free_consistent(pdev,
- sizeof(mega_passthru),
- pthru,
- pthru_dma_hndl);
+ dma_free_coherent(&pdev->dev,
+ sizeof(mega_passthru),
+ pthru,
+ pthru_dma_hndl);
free_local_pdev(pdev);
@@ -3597,13 +3242,13 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
freemem_and_return:
if( pthru->dataxferlen ) {
- pci_free_consistent(pdev,
- pthru->dataxferlen, data,
- data_dma_hndl);
+ dma_free_coherent(&pdev->dev,
+ pthru->dataxferlen, data,
+ data_dma_hndl);
}
- pci_free_consistent(pdev, sizeof(mega_passthru),
- pthru, pthru_dma_hndl);
+ dma_free_coherent(&pdev->dev, sizeof(mega_passthru),
+ pthru, pthru_dma_hndl);
free_local_pdev(pdev);
@@ -3616,8 +3261,10 @@ freemem_and_return:
* Is there a data transfer
*/
if( uioc.xferlen ) {
- data = pci_alloc_consistent(pdev,
- uioc.xferlen, &data_dma_hndl);
+ data = dma_alloc_coherent(&pdev->dev,
+ uioc.xferlen,
+ &data_dma_hndl,
+ GFP_KERNEL);
if( data == NULL ) {
free_local_pdev(pdev);
@@ -3637,9 +3284,9 @@ freemem_and_return:
if( copy_from_user(data, (char __user *)(unsigned long) uxferaddr,
uioc.xferlen) ) {
- pci_free_consistent(pdev,
- uioc.xferlen,
- data, data_dma_hndl);
+ dma_free_coherent(&pdev->dev,
+ uioc.xferlen, data,
+ data_dma_hndl);
free_local_pdev(pdev);
@@ -3660,9 +3307,9 @@ freemem_and_return:
if( rval ) {
if( uioc.xferlen ) {
- pci_free_consistent(pdev,
- uioc.xferlen, data,
- data_dma_hndl);
+ dma_free_coherent(&pdev->dev,
+ uioc.xferlen, data,
+ data_dma_hndl);
}
free_local_pdev(pdev);
@@ -3682,9 +3329,8 @@ freemem_and_return:
}
if( uioc.xferlen ) {
- pci_free_consistent(pdev,
- uioc.xferlen, data,
- data_dma_hndl);
+ dma_free_coherent(&pdev->dev, uioc.xferlen,
+ data, data_dma_hndl);
}
free_local_pdev(pdev);
@@ -3713,8 +3359,8 @@ megadev_unlocked_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
/**
* mega_m_to_n()
- * @arg - user address
- * @uioc - new ioctl structure
+ * @arg: user address
+ * @uioc: new ioctl structure
*
* A thin layer to convert older mimd interface ioctl structure to NIT ioctl
* structure
@@ -3841,8 +3487,8 @@ mega_m_to_n(void __user *arg, nitioctl_t *uioc)
/*
* mega_n_to_m()
- * @arg - user address
- * @mc - mailbox command
+ * @arg: user address
+ * @mc: mailbox command
*
* Updates the status information to the application, depending on application
* conforms to older mimd ioctl interface or newer NIT ioctl interface
@@ -3908,30 +3554,26 @@ mega_n_to_m(void __user *arg, megacmd_t *mc)
/**
* mega_is_bios_enabled()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* issue command to find out if the BIOS is enabled for this controller
*/
static int
mega_is_bios_enabled(adapter_t *adapter)
{
- unsigned char raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
- int ret;
+ struct mbox_out mbox;
+ unsigned char *raw_mbox = (u8 *)&mbox;
- mbox = (mbox_t *)raw_mbox;
-
- memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
- mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+ mbox.xferaddr = (u32)adapter->buf_dma_handle;
raw_mbox[0] = IS_BIOS_ENABLED;
raw_mbox[2] = GET_BIOS;
-
- ret = issue_scb_block(adapter, raw_mbox);
+ issue_scb_block(adapter, raw_mbox);
return *(char *)adapter->mega_buffer;
}
@@ -3939,7 +3581,7 @@ mega_is_bios_enabled(adapter_t *adapter)
/**
* mega_enum_raid_scsi()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Find out what channels are RAID/SCSI. This information is used to
* differentiate the virtual channels and physical channels and to support
@@ -3948,13 +3590,11 @@ mega_is_bios_enabled(adapter_t *adapter)
static void
mega_enum_raid_scsi(adapter_t *adapter)
{
- unsigned char raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
+ struct mbox_out mbox;
+ unsigned char *raw_mbox = (u8 *)&mbox;
int i;
- mbox = (mbox_t *)raw_mbox;
-
- memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
/*
* issue command to find out what channels are raid/scsi
@@ -3964,7 +3604,7 @@ mega_enum_raid_scsi(adapter_t *adapter)
memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
- mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+ mbox.xferaddr = (u32)adapter->buf_dma_handle;
/*
* Non-ROMB firmware fail this command, so all channels
@@ -3979,11 +3619,11 @@ mega_enum_raid_scsi(adapter_t *adapter)
for( i = 0; i < adapter->product_info.nchannels; i++ ) {
if( (adapter->mega_ch_class >> i) & 0x01 ) {
- printk(KERN_INFO "megaraid: channel[%d] is raid.\n",
+ dev_info(&adapter->dev->dev, "channel[%d] is raid\n",
i);
}
else {
- printk(KERN_INFO "megaraid: channel[%d] is scsi.\n",
+ dev_info(&adapter->dev->dev, "channel[%d] is scsi\n",
i);
}
}
@@ -3994,7 +3634,7 @@ mega_enum_raid_scsi(adapter_t *adapter)
/**
* mega_get_boot_drv()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Find out which device is the boot device. Note, any logical drive or any
* phyical device (e.g., a CDROM) can be designated as a boot device.
@@ -4003,23 +3643,21 @@ static void
mega_get_boot_drv(adapter_t *adapter)
{
struct private_bios_data *prv_bios_data;
- unsigned char raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
+ struct mbox_out mbox;
+ unsigned char *raw_mbox = (u8 *)&mbox;
u16 cksum = 0;
u8 *cksum_p;
u8 boot_pdrv;
int i;
- mbox = (mbox_t *)raw_mbox;
-
- memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
raw_mbox[0] = BIOS_PVT_DATA;
raw_mbox[2] = GET_BIOS_PVT_DATA;
memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
- mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+ mbox.xferaddr = (u32)adapter->buf_dma_handle;
adapter->boot_ldrv_enabled = 0;
adapter->boot_ldrv = 0;
@@ -4061,7 +3699,7 @@ mega_get_boot_drv(adapter_t *adapter)
/**
* mega_support_random_del()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Find out if this controller supports random deletion and addition of
* logical drives
@@ -4069,13 +3707,11 @@ mega_get_boot_drv(adapter_t *adapter)
static int
mega_support_random_del(adapter_t *adapter)
{
- unsigned char raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
+ struct mbox_out mbox;
+ unsigned char *raw_mbox = (u8 *)&mbox;
int rval;
- mbox = (mbox_t *)raw_mbox;
-
- memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
/*
* issue command
@@ -4091,20 +3727,18 @@ mega_support_random_del(adapter_t *adapter)
/**
* mega_support_ext_cdb()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Find out if this firmware support cdblen > 10
*/
static int
mega_support_ext_cdb(adapter_t *adapter)
{
- unsigned char raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
+ struct mbox_out mbox;
+ unsigned char *raw_mbox = (u8 *)&mbox;
int rval;
- mbox = (mbox_t *)raw_mbox;
-
- memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
/*
* issue command to find out if controller supports extended CDBs.
*/
@@ -4119,8 +3753,8 @@ mega_support_ext_cdb(adapter_t *adapter)
/**
* mega_del_logdrv()
- * @adapter - pointer to our soft state
- * @logdrv - logical drive to be deleted
+ * @adapter: pointer to our soft state
+ * @logdrv: logical drive to be deleted
*
* Delete the specified logical drive. It is the responsibility of the user
* app to let the OS know about this operation.
@@ -4189,7 +3823,7 @@ mega_do_del_logdrv(adapter_t *adapter, int logdrv)
/* log this event */
if(rval) {
- printk(KERN_WARNING "megaraid: Delete LD-%d failed.", logdrv);
+ dev_warn(&adapter->dev->dev, "Delete LD-%d failed", logdrv);
return rval;
}
@@ -4205,7 +3839,7 @@ mega_do_del_logdrv(adapter_t *adapter, int logdrv)
/**
* mega_get_max_sgl()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Find out the maximum number of scatter-gather elements supported by this
* version of the firmware
@@ -4213,16 +3847,14 @@ mega_do_del_logdrv(adapter_t *adapter, int logdrv)
static void
mega_get_max_sgl(adapter_t *adapter)
{
- unsigned char raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
+ struct mbox_out mbox;
+ unsigned char *raw_mbox = (u8 *)&mbox;
- mbox = (mbox_t *)raw_mbox;
-
- memset(mbox, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
- mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+ mbox.xferaddr = (u32)adapter->buf_dma_handle;
raw_mbox[0] = MAIN_MISC_OPCODE;
raw_mbox[2] = GET_MAX_SG_SUPPORT;
@@ -4236,7 +3868,7 @@ mega_get_max_sgl(adapter_t *adapter)
}
else {
adapter->sglen = *((char *)adapter->mega_buffer);
-
+
/*
* Make sure this is not more than the resources we are
* planning to allocate
@@ -4251,23 +3883,21 @@ mega_get_max_sgl(adapter_t *adapter)
/**
* mega_support_cluster()
- * @adapter - pointer to our soft state
+ * @adapter: pointer to our soft state
*
* Find out if this firmware support cluster calls.
*/
static int
mega_support_cluster(adapter_t *adapter)
{
- unsigned char raw_mbox[sizeof(struct mbox_out)];
- mbox_t *mbox;
-
- mbox = (mbox_t *)raw_mbox;
+ struct mbox_out mbox;
+ unsigned char *raw_mbox = (u8 *)&mbox;
- memset(mbox, 0, sizeof(raw_mbox));
+ memset(&mbox, 0, sizeof(mbox));
memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
- mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+ mbox.xferaddr = (u32)adapter->buf_dma_handle;
/*
* Try to get the initiator id. This command will succeed iff the
@@ -4293,8 +3923,8 @@ mega_support_cluster(adapter_t *adapter)
#ifdef CONFIG_PROC_FS
/**
* mega_adapinq()
- * @adapter - pointer to our soft state
- * @dma_handle - DMA address of the buffer
+ * @adapter: pointer to our soft state
+ * @dma_handle: DMA address of the buffer
*
* Issue internal commands while interrupts are available.
* We only issue direct mailbox commands from within the driver. ioctl()
@@ -4326,11 +3956,12 @@ mega_adapinq(adapter_t *adapter, dma_addr_t dma_handle)
}
-/** mega_internal_dev_inquiry()
- * @adapter - pointer to our soft state
- * @ch - channel for this device
- * @tgt - ID of this device
- * @buf_dma_handle - DMA address of the buffer
+/**
+ * mega_internal_dev_inquiry()
+ * @adapter: pointer to our soft state
+ * @ch: channel for this device
+ * @tgt: ID of this device
+ * @buf_dma_handle: DMA address of the buffer
*
* Issue the scsi inquiry for the specified device.
*/
@@ -4351,8 +3982,8 @@ mega_internal_dev_inquiry(adapter_t *adapter, u8 ch, u8 tgt,
*/
if( make_local_pdev(adapter, &pdev) != 0 ) return -1;
- pthru = pci_alloc_consistent(pdev, sizeof(mega_passthru),
- &pthru_dma_handle);
+ pthru = dma_alloc_coherent(&pdev->dev, sizeof(mega_passthru),
+ &pthru_dma_handle, GFP_KERNEL);
if( pthru == NULL ) {
free_local_pdev(pdev);
@@ -4388,8 +4019,8 @@ mega_internal_dev_inquiry(adapter_t *adapter, u8 ch, u8 tgt,
rval = mega_internal_command(adapter, &mc, pthru);
- pci_free_consistent(pdev, sizeof(mega_passthru), pthru,
- pthru_dma_handle);
+ dma_free_coherent(&pdev->dev, sizeof(mega_passthru), pthru,
+ pthru_dma_handle);
free_local_pdev(pdev);
@@ -4399,31 +4030,23 @@ mega_internal_dev_inquiry(adapter_t *adapter, u8 ch, u8 tgt,
/**
* mega_internal_command()
- * @adapter - pointer to our soft state
- * @mc - the mailbox command
- * @pthru - Passthru structure for DCDB commands
+ * @adapter: pointer to our soft state
+ * @mc: the mailbox command
+ * @pthru: Passthru structure for DCDB commands
*
* Issue the internal commands in interrupt mode.
* The last argument is the address of the passthru structure if the command
* to be fired is a passthru command
*
- * lockscope specifies whether the caller has already acquired the lock. Of
- * course, the caller must know which lock we are talking about.
- *
* Note: parameter 'pthru' is null for non-passthru commands.
*/
static int
mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru)
{
- Scsi_Cmnd *scmd;
- struct scsi_device *sdev;
+ unsigned long flags;
scb_t *scb;
int rval;
- scmd = scsi_allocate_command(GFP_KERNEL);
- if (!scmd)
- return -ENOMEM;
-
/*
* The internal commands share one command id and hence are
* serialized. This is so because we want to reserve maximum number of
@@ -4434,74 +4057,46 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru)
scb = &adapter->int_scb;
memset(scb, 0, sizeof(scb_t));
- sdev = kzalloc(sizeof(struct scsi_device), GFP_KERNEL);
- scmd->device = sdev;
-
- memset(adapter->int_cdb, 0, sizeof(adapter->int_cdb));
- scmd->cmnd = adapter->int_cdb;
- scmd->device->host = adapter->host;
- scmd->host_scribble = (void *)scb;
- scmd->cmnd[0] = MEGA_INTERNAL_CMD;
-
- scb->state |= SCB_ACTIVE;
- scb->cmd = scmd;
+ scb->idx = CMDID_INT_CMDS;
+ scb->state |= SCB_ACTIVE | SCB_PENDQ;
memcpy(scb->raw_mbox, mc, sizeof(megacmd_t));
/*
* Is it a passthru command
*/
- if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) {
-
+ if (mc->cmd == MEGA_MBOXCMD_PASSTHRU)
scb->pthru = pthru;
- }
-
- scb->idx = CMDID_INT_CMDS;
- megaraid_queue_lck(scmd, mega_internal_done);
+ spin_lock_irqsave(&adapter->lock, flags);
+ list_add_tail(&scb->list, &adapter->pending_list);
+ /*
+ * Check if the HBA is in quiescent state, e.g., during a
+ * delete logical drive opertion. If it is, don't run
+ * the pending_list.
+ */
+ if (atomic_read(&adapter->quiescent) == 0)
+ mega_runpendq(adapter);
+ spin_unlock_irqrestore(&adapter->lock, flags);
wait_for_completion(&adapter->int_waitq);
- rval = scmd->result;
- mc->status = scmd->result;
- kfree(sdev);
+ mc->status = rval = adapter->int_status;
/*
* Print a debug message for all failed commands. Applications can use
* this information.
*/
- if( scmd->result && trace_level ) {
- printk("megaraid: cmd [%x, %x, %x] status:[%x]\n",
- mc->cmd, mc->opcode, mc->subopcode, scmd->result);
+ if (rval && trace_level) {
+ dev_info(&adapter->dev->dev, "cmd [%x, %x, %x] status:[%x]\n",
+ mc->cmd, mc->opcode, mc->subopcode, rval);
}
mutex_unlock(&adapter->int_mtx);
-
- scsi_free_command(GFP_KERNEL, scmd);
-
return rval;
}
-
-/**
- * mega_internal_done()
- * @scmd - internal scsi command
- *
- * Callback routine for internal commands.
- */
-static void
-mega_internal_done(Scsi_Cmnd *scmd)
-{
- adapter_t *adapter;
-
- adapter = (adapter_t *)scmd->device->host->hostdata;
-
- complete(&adapter->int_waitq);
-
-}
-
-
-static struct scsi_host_template megaraid_template = {
+static const struct scsi_host_template megaraid_template = {
.module = THIS_MODULE,
.name = "MegaRAID",
.proc_name = "megaraid_legacy",
@@ -4513,14 +4108,13 @@ static struct scsi_host_template megaraid_template = {
.this_id = DEFAULT_INITIATOR_ID,
.sg_tablesize = MAX_SGLIST,
.cmd_per_lun = DEF_CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
.eh_abort_handler = megaraid_abort,
- .eh_device_reset_handler = megaraid_reset,
- .eh_bus_reset_handler = megaraid_reset,
.eh_host_reset_handler = megaraid_reset,
+ .no_write_same = 1,
+ .cmd_size = sizeof(struct megaraid_cmd_priv),
};
-static int __devinit
+static int
megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct Scsi_Host *host;
@@ -4531,6 +4125,9 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
int irq, i, j;
int error = -ENODEV;
+ if (hba_count >= MAX_CONTROLLERS)
+ goto out;
+
if (pci_enable_device(pdev))
goto out;
pci_set_master(pdev);
@@ -4550,11 +4147,11 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
*/
if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ &&
pdev->subsystem_device == 0xC000)
- return -ENODEV;
+ goto out_disable_device;
/* Now check the magic signature byte */
pci_read_config_word(pdev, PCI_CONF_AMISIG, &magic);
if (magic != HBA_SIGNATURE_471 && magic != HBA_SIGNATURE)
- return -ENODEV;
+ goto out_disable_device;
/* Ok it is probably a megaraid */
}
@@ -4575,11 +4172,8 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
subsysvid = pdev->subsystem_vendor;
subsysid = pdev->subsystem_device;
- printk(KERN_NOTICE "megaraid: found 0x%4.04x:0x%4.04x:bus %d:",
- id->vendor, id->device, pci_bus);
-
- printk("slot %d:func %d\n",
- PCI_SLOT(pci_dev_func), PCI_FUNC(pci_dev_func));
+ dev_notice(&pdev->dev, "found 0x%4.04x:0x%4.04x\n",
+ id->vendor, id->device);
/* Read the base port and IRQ from PCI */
mega_baseport = pci_resource_start(pdev, 0);
@@ -4590,14 +4184,13 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
flag |= BOARD_MEMMAP;
if (!request_mem_region(mega_baseport, 128, "megaraid")) {
- printk(KERN_WARNING "megaraid: mem region busy!\n");
+ dev_warn(&pdev->dev, "mem region busy!\n");
goto out_disable_device;
}
mega_baseport = (unsigned long)ioremap(mega_baseport, 128);
if (!mega_baseport) {
- printk(KERN_WARNING
- "megaraid: could not map hba memory\n");
+ dev_warn(&pdev->dev, "could not map hba memory\n");
goto out_release_region;
}
} else {
@@ -4616,7 +4209,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
adapter = (adapter_t *)host->hostdata;
memset(adapter, 0, sizeof(adapter_t));
- printk(KERN_NOTICE
+ dev_notice(&pdev->dev,
"scsi%d:Found MegaRAID controller at 0x%lx, IRQ:%d\n",
host->host_no, mega_baseport, irq);
@@ -4651,24 +4244,26 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
/*
* Allocate buffer to issue internal commands.
*/
- adapter->mega_buffer = pci_alloc_consistent(adapter->dev,
- MEGA_BUFFER_SIZE, &adapter->buf_dma_handle);
+ adapter->mega_buffer = dma_alloc_coherent(&adapter->dev->dev,
+ MEGA_BUFFER_SIZE,
+ &adapter->buf_dma_handle,
+ GFP_KERNEL);
if (!adapter->mega_buffer) {
- printk(KERN_WARNING "megaraid: out of RAM.\n");
+ dev_warn(&pdev->dev, "out of RAM\n");
goto out_host_put;
}
- adapter->scb_list = kmalloc(sizeof(scb_t) * MAX_COMMANDS, GFP_KERNEL);
+ adapter->scb_list = kmalloc_array(MAX_COMMANDS, sizeof(scb_t),
+ GFP_KERNEL);
if (!adapter->scb_list) {
- printk(KERN_WARNING "megaraid: out of RAM.\n");
+ dev_warn(&pdev->dev, "out of RAM\n");
goto out_free_cmd_buffer;
}
if (request_irq(irq, (adapter->flag & BOARD_MEMMAP) ?
megaraid_isr_memmapped : megaraid_isr_iomapped,
IRQF_SHARED, "megaraid", adapter)) {
- printk(KERN_WARNING
- "megaraid: Couldn't register IRQ %d!\n", irq);
+ dev_warn(&pdev->dev, "Couldn't register IRQ %d!\n", irq);
goto out_free_scb_list;
}
@@ -4688,9 +4283,9 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (!strcmp(adapter->fw_version, "3.00") ||
!strcmp(adapter->fw_version, "3.01")) {
- printk( KERN_WARNING
- "megaraid: Your card is a Dell PERC "
- "2/SC RAID controller with "
+ dev_warn(&pdev->dev,
+ "Your card is a Dell PERC "
+ "2/SC RAID controller with "
"firmware\nmegaraid: 3.00 or 3.01. "
"This driver is known to have "
"corruption issues\nmegaraid: with "
@@ -4713,7 +4308,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
* support, since this firmware cannot handle 64 bit
* addressing
*/
- if ((subsysvid == HP_SUBSYS_VID) &&
+ if ((subsysvid == PCI_VENDOR_ID_HP) &&
((subsysid == 0x60E7) || (subsysid == 0x60E8))) {
/*
* which firmware
@@ -4721,12 +4316,12 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (!strcmp(adapter->fw_version, "H01.07") ||
!strcmp(adapter->fw_version, "H01.08") ||
!strcmp(adapter->fw_version, "H01.09") ) {
- printk(KERN_WARNING
- "megaraid: Firmware H.01.07, "
+ dev_warn(&pdev->dev,
+ "Firmware H.01.07, "
"H.01.08, and H.01.09 on 1M/2M "
"controllers\n"
- "megaraid: do not support 64 bit "
- "addressing.\nmegaraid: DISABLING "
+ "do not support 64 bit "
+ "addressing.\nDISABLING "
"64 bit support.\n");
adapter->flag &= ~BOARD_64BIT;
}
@@ -4811,10 +4406,10 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
/* Set the Mode of addressing to 64 bit if we can */
if ((adapter->flag & BOARD_64BIT) && (sizeof(dma_addr_t) == 8)) {
- pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
adapter->has_64bit_addr = 1;
} else {
- pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
adapter->has_64bit_addr = 0;
}
@@ -4834,8 +4429,8 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
*/
adapter->has_cluster = mega_support_cluster(adapter);
if (adapter->has_cluster) {
- printk(KERN_NOTICE
- "megaraid: Cluster driver, initiator id:%d\n",
+ dev_notice(&pdev->dev,
+ "Cluster driver, initiator id:%d\n",
adapter->this_id);
}
#endif
@@ -4853,15 +4448,15 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
out_free_mbox:
- pci_free_consistent(adapter->dev, sizeof(mbox64_t),
- adapter->una_mbox64, adapter->una_mbox64_dma);
+ dma_free_coherent(&adapter->dev->dev, sizeof(mbox64_t),
+ adapter->una_mbox64, adapter->una_mbox64_dma);
out_free_irq:
free_irq(adapter->host->irq, adapter);
out_free_scb_list:
kfree(adapter->scb_list);
out_free_cmd_buffer:
- pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE,
- adapter->mega_buffer, adapter->buf_dma_handle);
+ dma_free_coherent(&adapter->dev->dev, MEGA_BUFFER_SIZE,
+ adapter->mega_buffer, adapter->buf_dma_handle);
out_host_put:
scsi_host_put(host);
out_iounmap:
@@ -4902,7 +4497,7 @@ __megaraid_shutdown(adapter_t *adapter)
issue_scb_block(adapter, raw_mbox);
if (atomic_read(&adapter->pend_cmds) > 0)
- printk(KERN_WARNING "megaraid: pending commands!!\n");
+ dev_warn(&adapter->dev->dev, "pending commands!!\n");
/*
* Have a delibrate delay to make sure all the caches are
@@ -4912,11 +4507,12 @@ __megaraid_shutdown(adapter_t *adapter)
mdelay(1000);
}
-static void __devexit
+static void
megaraid_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *host = pci_get_drvdata(pdev);
adapter_t *adapter = (adapter_t *)host->hostdata;
+ char buf[12] = { 0 };
scsi_remove_host(host);
@@ -4931,50 +4527,14 @@ megaraid_remove_one(struct pci_dev *pdev)
mega_free_sgl(adapter);
-#ifdef CONFIG_PROC_FS
- if (adapter->controller_proc_dir_entry) {
- remove_proc_entry("stat", adapter->controller_proc_dir_entry);
- remove_proc_entry("config",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("mailbox",
- adapter->controller_proc_dir_entry);
-#if MEGA_HAVE_ENH_PROC
- remove_proc_entry("rebuild-rate",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("battery-status",
- adapter->controller_proc_dir_entry);
-
- remove_proc_entry("diskdrives-ch0",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("diskdrives-ch1",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("diskdrives-ch2",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("diskdrives-ch3",
- adapter->controller_proc_dir_entry);
-
- remove_proc_entry("raiddrives-0-9",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("raiddrives-10-19",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("raiddrives-20-29",
- adapter->controller_proc_dir_entry);
- remove_proc_entry("raiddrives-30-39",
- adapter->controller_proc_dir_entry);
-#endif
- {
- char buf[12] = { 0 };
- sprintf(buf, "hba%d", adapter->host->host_no);
- remove_proc_entry(buf, mega_proc_dir_entry);
- }
- }
-#endif
+ sprintf(buf, "hba%d", adapter->host->host_no);
+ remove_proc_subtree(buf, mega_proc_dir_entry);
- pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE,
- adapter->mega_buffer, adapter->buf_dma_handle);
+ dma_free_coherent(&adapter->dev->dev, MEGA_BUFFER_SIZE,
+ adapter->mega_buffer, adapter->buf_dma_handle);
kfree(adapter->scb_list);
- pci_free_consistent(adapter->dev, sizeof(mbox64_t),
- adapter->una_mbox64, adapter->una_mbox64_dma);
+ dma_free_coherent(&adapter->dev->dev, sizeof(mbox64_t),
+ adapter->una_mbox64, adapter->una_mbox64_dma);
scsi_host_put(host);
pci_disable_device(pdev);
@@ -4991,7 +4551,7 @@ megaraid_shutdown(struct pci_dev *pdev)
__megaraid_shutdown(adapter);
}
-static struct pci_device_id megaraid_pci_tbl[] = {
+static const struct pci_device_id megaraid_pci_tbl[] = {
{PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID2,
@@ -5006,7 +4566,7 @@ static struct pci_driver megaraid_pci_driver = {
.name = "megaraid_legacy",
.id_table = megaraid_pci_tbl,
.probe = megaraid_probe_one,
- .remove = __devexit_p(megaraid_remove_one),
+ .remove = megaraid_remove_one,
.shutdown = megaraid_shutdown,
};
@@ -5041,7 +4601,7 @@ static int __init megaraid_init(void)
* major number allocation.
*/
major = register_chrdev(0, "megadev_legacy", &megadev_fops);
- if (!major) {
+ if (major < 0) {
printk(KERN_WARNING
"megaraid: failed to register char device\n");
}
diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h
index 9a7897f8ca43..d6bfd26a8843 100644
--- a/drivers/scsi/megaraid.h
+++ b/drivers/scsi/megaraid.h
@@ -1,8 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __MEGARAID_H__
#define __MEGARAID_H__
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <scsi/scsi_cmnd.h>
#define MEGARAID_VERSION \
"v2.00.4 (Release Date: Thu Feb 9 08:51:30 EST 2006)\n"
@@ -45,45 +47,10 @@
#define MAX_DEV_TYPE 32
-#ifndef PCI_VENDOR_ID_LSI_LOGIC
-#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
-#endif
-
-#ifndef PCI_VENDOR_ID_AMI
-#define PCI_VENDOR_ID_AMI 0x101E
-#endif
-
-#ifndef PCI_VENDOR_ID_DELL
-#define PCI_VENDOR_ID_DELL 0x1028
-#endif
-
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL 0x8086
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID
-#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID2
-#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID3
-#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960
-#endif
-
#define PCI_DEVICE_ID_DISCOVERY 0x000E
#define PCI_DEVICE_ID_PERC4_DI 0x000F
#define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407
-/* Sub-System Vendor IDs */
-#define AMI_SUBSYS_VID 0x101E
-#define DELL_SUBSYS_VID 0x1028
-#define HP_SUBSYS_VID 0x103C
-#define LSI_SUBSYS_VID 0x1000
-#define INTEL_SUBSYS_VID 0x8086
-
#define HBA_SIGNATURE 0x3344
#define HBA_SIGNATURE_471 0xCCCC
#define HBA_SIGNATURE_64BIT 0x0299
@@ -225,7 +192,7 @@ typedef struct {
u32 dma_type;
u32 dma_direction;
- Scsi_Cmnd *cmd;
+ struct scsi_cmnd *cmd;
dma_addr_t dma_h_bulkdata;
dma_addr_t dma_h_sgdata;
@@ -790,8 +757,28 @@ struct private_bios_data {
#define CACHED_IO 0
#define DIRECT_IO 1
+struct megaraid_cmd_priv {
+ struct list_head entry;
+};
+
+#define SCSI_LIST(scp) \
+ (&((struct megaraid_cmd_priv *)scsi_cmd_priv(scp))->entry)
-#define SCSI_LIST(scp) ((struct list_head *)(&(scp)->SCp))
+struct scsi_cmd_and_priv {
+ struct scsi_cmnd cmd;
+ struct megaraid_cmd_priv priv;
+};
+
+static inline struct scsi_cmnd *
+megaraid_to_scsi_cmd(struct megaraid_cmd_priv *cmd_priv)
+{
+ /* See also scsi_mq_setup_tags() */
+ BUILD_BUG_ON(sizeof(struct scsi_cmd_and_priv) !=
+ sizeof(struct scsi_cmnd) +
+ sizeof(struct megaraid_cmd_priv));
+
+ return &container_of(cmd_priv, struct scsi_cmd_and_priv, priv)->cmd;
+}
/*
* Each controller's soft state
@@ -848,18 +835,6 @@ typedef struct {
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *controller_proc_dir_entry;
- struct proc_dir_entry *proc_read;
- struct proc_dir_entry *proc_stat;
- struct proc_dir_entry *proc_mbox;
-
-#if MEGA_HAVE_ENH_PROC
- struct proc_dir_entry *proc_rr;
- struct proc_dir_entry *proc_battery;
-#define MAX_PROC_CHANNELS 4
- struct proc_dir_entry *proc_pdrvstat[MAX_PROC_CHANNELS];
- struct proc_dir_entry *proc_rdrvstat[MAX_PROC_CHANNELS];
-#endif
-
#endif
int has_64bit_addr; /* are we using 64-bit addressing */
@@ -888,10 +863,10 @@ typedef struct {
u8 sglen; /* f/w supported scatter-gather list length */
- unsigned char int_cdb[MAX_COMMAND_SIZE];
scb_t int_scb;
struct mutex int_mtx; /* To synchronize the internal
commands */
+ int int_status; /* status of internal cmd */
struct completion int_waitq; /* wait queue for internal
cmds */
@@ -988,7 +963,7 @@ static int issue_scb(adapter_t *, scb_t *);
static int mega_setup_mailbox(adapter_t *);
static int megaraid_queue (struct Scsi_Host *, struct scsi_cmnd *);
-static scb_t * mega_build_cmd(adapter_t *, Scsi_Cmnd *, int *);
+static scb_t * mega_build_cmd(adapter_t *, struct scsi_cmnd *, int *);
static void __mega_runpendq(adapter_t *);
static int issue_scb_block(adapter_t *, u_char *);
@@ -997,10 +972,10 @@ static irqreturn_t megaraid_isr_iomapped(int, void *);
static void mega_free_scb(adapter_t *, scb_t *);
-static int megaraid_abort(Scsi_Cmnd *);
-static int megaraid_reset(Scsi_Cmnd *);
-static int megaraid_abort_and_reset(adapter_t *, Scsi_Cmnd *, int);
-static int megaraid_biosparam(struct scsi_device *, struct block_device *,
+static int megaraid_abort(struct scsi_cmnd *);
+static int megaraid_reset(struct scsi_cmnd *);
+static int megaraid_abort_and_reset(adapter_t *, struct scsi_cmnd *, int);
+static int megaraid_biosparam(struct scsi_device *, struct gendisk *,
sector_t, int []);
static int mega_build_sglist (adapter_t *adapter, scb_t *scb,
@@ -1022,33 +997,16 @@ static int mega_init_scb (adapter_t *);
static int mega_is_bios_enabled (adapter_t *);
#ifdef CONFIG_PROC_FS
-static int mega_print_inquiry(char *, char *);
static void mega_create_proc_entry(int, struct proc_dir_entry *);
-static int proc_read_config(char *, char **, off_t, int, int *, void *);
-static int proc_read_stat(char *, char **, off_t, int, int *, void *);
-static int proc_read_mbox(char *, char **, off_t, int, int *, void *);
-static int proc_rebuild_rate(char *, char **, off_t, int, int *, void *);
-static int proc_battery(char *, char **, off_t, int, int *, void *);
-static int proc_pdrv_ch0(char *, char **, off_t, int, int *, void *);
-static int proc_pdrv_ch1(char *, char **, off_t, int, int *, void *);
-static int proc_pdrv_ch2(char *, char **, off_t, int, int *, void *);
-static int proc_pdrv_ch3(char *, char **, off_t, int, int *, void *);
-static int proc_pdrv(adapter_t *, char *, int);
-static int proc_rdrv_10(char *, char **, off_t, int, int *, void *);
-static int proc_rdrv_20(char *, char **, off_t, int, int *, void *);
-static int proc_rdrv_30(char *, char **, off_t, int, int *, void *);
-static int proc_rdrv_40(char *, char **, off_t, int, int *, void *);
-static int proc_rdrv(adapter_t *, char *, int, int);
-
static int mega_adapinq(adapter_t *, dma_addr_t);
static int mega_internal_dev_inquiry(adapter_t *, u8, u8, dma_addr_t);
#endif
static int mega_support_ext_cdb(adapter_t *);
static mega_passthru* mega_prepare_passthru(adapter_t *, scb_t *,
- Scsi_Cmnd *, int, int);
+ struct scsi_cmnd *, int, int);
static mega_ext_passthru* mega_prepare_extpassthru(adapter_t *,
- scb_t *, Scsi_Cmnd *, int, int);
+ scb_t *, struct scsi_cmnd *, int, int);
static void mega_enum_raid_scsi(adapter_t *);
static void mega_get_boot_drv(adapter_t *);
static int mega_support_random_del(adapter_t *);
@@ -1056,7 +1014,6 @@ static int mega_del_logdrv(adapter_t *, int);
static int mega_do_del_logdrv(adapter_t *, int);
static void mega_get_max_sgl(adapter_t *);
static int mega_internal_command(adapter_t *, megacmd_t *, mega_passthru *);
-static void mega_internal_done(Scsi_Cmnd *);
static int mega_support_cluster(adapter_t *);
#endif
diff --git a/drivers/scsi/megaraid/Kconfig.megaraid b/drivers/scsi/megaraid/Kconfig.megaraid
index 17419e30ffc8..56b76d73895b 100644
--- a/drivers/scsi/megaraid/Kconfig.megaraid
+++ b/drivers/scsi/megaraid/Kconfig.megaraid
@@ -1,85 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0-only
config MEGARAID_NEWGEN
bool "LSI Logic New Generation RAID Device Drivers"
- depends on PCI && SCSI
+ depends on PCI && HAS_IOPORT && SCSI
help
- LSI Logic RAID Device Drivers
+ LSI Logic RAID Device Drivers
config MEGARAID_MM
tristate "LSI Logic Management Module (New Driver)"
- depends on PCI && SCSI && MEGARAID_NEWGEN
+ depends on PCI && HAS_IOPORT && SCSI && MEGARAID_NEWGEN
help
- Management Module provides ioctl, sysfs support for LSI Logic
- RAID controllers.
- To compile this driver as a module, choose M here: the
- module will be called megaraid_mm
+ Management Module provides ioctl, sysfs support for LSI Logic
+ RAID controllers.
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid_mm
config MEGARAID_MAILBOX
tristate "LSI Logic MegaRAID Driver (New Driver)"
depends on PCI && SCSI && MEGARAID_MM
help
- List of supported controllers
+ List of supported controllers
- OEM Product Name VID :DID :SVID:SSID
- --- ------------ ---- ---- ---- ----
- Dell PERC3/QC 101E:1960:1028:0471
- Dell PERC3/DC 101E:1960:1028:0493
- Dell PERC3/SC 101E:1960:1028:0475
- Dell PERC3/Di 1028:000E:1028:0123
- Dell PERC4/SC 1000:1960:1028:0520
- Dell PERC4/DC 1000:1960:1028:0518
- Dell PERC4/QC 1000:0407:1028:0531
- Dell PERC4/Di 1028:000F:1028:014A
- Dell PERC 4e/Si 1028:0013:1028:016c
- Dell PERC 4e/Di 1028:0013:1028:016d
- Dell PERC 4e/Di 1028:0013:1028:016e
- Dell PERC 4e/Di 1028:0013:1028:016f
- Dell PERC 4e/Di 1028:0013:1028:0170
- Dell PERC 4e/DC 1000:0408:1028:0002
- Dell PERC 4e/SC 1000:0408:1028:0001
- LSI MegaRAID SCSI 320-0 1000:1960:1000:A520
- LSI MegaRAID SCSI 320-1 1000:1960:1000:0520
- LSI MegaRAID SCSI 320-2 1000:1960:1000:0518
- LSI MegaRAID SCSI 320-0X 1000:0407:1000:0530
- LSI MegaRAID SCSI 320-2X 1000:0407:1000:0532
- LSI MegaRAID SCSI 320-4X 1000:0407:1000:0531
- LSI MegaRAID SCSI 320-1E 1000:0408:1000:0001
- LSI MegaRAID SCSI 320-2E 1000:0408:1000:0002
- LSI MegaRAID SATA 150-4 1000:1960:1000:4523
- LSI MegaRAID SATA 150-6 1000:1960:1000:0523
- LSI MegaRAID SATA 300-4X 1000:0409:1000:3004
- LSI MegaRAID SATA 300-8X 1000:0409:1000:3008
- INTEL RAID Controller SRCU42X 1000:0407:8086:0532
- INTEL RAID Controller SRCS16 1000:1960:8086:0523
- INTEL RAID Controller SRCU42E 1000:0408:8086:0002
- INTEL RAID Controller SRCZCRX 1000:0407:8086:0530
- INTEL RAID Controller SRCS28X 1000:0409:8086:3008
- INTEL RAID Controller SROMBU42E 1000:0408:8086:3431
- INTEL RAID Controller SROMBU42E 1000:0408:8086:3499
- INTEL RAID Controller SRCU51L 1000:1960:8086:0520
- FSC MegaRAID PCI Express ROMB 1000:0408:1734:1065
- ACER MegaRAID ROMB-2E 1000:0408:1025:004D
- NEC MegaRAID PCI Express ROMB 1000:0408:1033:8287
+ OEM Product Name VID :DID :SVID:SSID
+ --- ------------ ---- ---- ---- ----
+ Dell PERC3/QC 101E:1960:1028:0471
+ Dell PERC3/DC 101E:1960:1028:0493
+ Dell PERC3/SC 101E:1960:1028:0475
+ Dell PERC3/Di 1028:000E:1028:0123
+ Dell PERC4/SC 1000:1960:1028:0520
+ Dell PERC4/DC 1000:1960:1028:0518
+ Dell PERC4/QC 1000:0407:1028:0531
+ Dell PERC4/Di 1028:000F:1028:014A
+ Dell PERC 4e/Si 1028:0013:1028:016c
+ Dell PERC 4e/Di 1028:0013:1028:016d
+ Dell PERC 4e/Di 1028:0013:1028:016e
+ Dell PERC 4e/Di 1028:0013:1028:016f
+ Dell PERC 4e/Di 1028:0013:1028:0170
+ Dell PERC 4e/DC 1000:0408:1028:0002
+ Dell PERC 4e/SC 1000:0408:1028:0001
+ LSI MegaRAID SCSI 320-0 1000:1960:1000:A520
+ LSI MegaRAID SCSI 320-1 1000:1960:1000:0520
+ LSI MegaRAID SCSI 320-2 1000:1960:1000:0518
+ LSI MegaRAID SCSI 320-0X 1000:0407:1000:0530
+ LSI MegaRAID SCSI 320-2X 1000:0407:1000:0532
+ LSI MegaRAID SCSI 320-4X 1000:0407:1000:0531
+ LSI MegaRAID SCSI 320-1E 1000:0408:1000:0001
+ LSI MegaRAID SCSI 320-2E 1000:0408:1000:0002
+ LSI MegaRAID SATA 150-4 1000:1960:1000:4523
+ LSI MegaRAID SATA 150-6 1000:1960:1000:0523
+ LSI MegaRAID SATA 300-4X 1000:0409:1000:3004
+ LSI MegaRAID SATA 300-8X 1000:0409:1000:3008
+ INTEL RAID Controller SRCU42X 1000:0407:8086:0532
+ INTEL RAID Controller SRCS16 1000:1960:8086:0523
+ INTEL RAID Controller SRCU42E 1000:0408:8086:0002
+ INTEL RAID Controller SRCZCRX 1000:0407:8086:0530
+ INTEL RAID Controller SRCS28X 1000:0409:8086:3008
+ INTEL RAID Controller SROMBU42E 1000:0408:8086:3431
+ INTEL RAID Controller SROMBU42E 1000:0408:8086:3499
+ INTEL RAID Controller SRCU51L 1000:1960:8086:0520
+ FSC MegaRAID PCI Express ROMB 1000:0408:1734:1065
+ ACER MegaRAID ROMB-2E 1000:0408:1025:004D
+ NEC MegaRAID PCI Express ROMB 1000:0408:1033:8287
- To compile this driver as a module, choose M here: the
- module will be called megaraid_mbox
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid_mbox
config MEGARAID_LEGACY
tristate "LSI Logic Legacy MegaRAID Driver"
- depends on PCI && SCSI
+ depends on PCI && HAS_IOPORT && SCSI
help
- This driver supports the LSI MegaRAID 418, 428, 438, 466, 762, 490
- and 467 SCSI host adapters. This driver also support the all U320
- RAID controllers
+ This driver supports the LSI MegaRAID 418, 428, 438, 466, 762, 490
+ and 467 SCSI host adapters. This driver also support the all U320
+ RAID controllers
- To compile this driver as a module, choose M here: the
- module will be called megaraid
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid
config MEGARAID_SAS
tristate "LSI Logic MegaRAID SAS RAID Module"
depends on PCI && SCSI
+ select IRQ_POLL
help
- Module for LSI Logic's SAS based RAID controllers.
- To compile this driver as a module, choose 'm' here.
- Module will be called megaraid_sas
-
+ Module for LSI Logic's SAS based RAID controllers.
+ To compile this driver as a module, choose 'm' here.
+ Module will be called megaraid_sas
diff --git a/drivers/scsi/megaraid/Makefile b/drivers/scsi/megaraid/Makefile
index 5826ed509e3e..12177e4cae65 100644
--- a/drivers/scsi/megaraid/Makefile
+++ b/drivers/scsi/megaraid/Makefile
@@ -1,5 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o
obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o
obj-$(CONFIG_MEGARAID_SAS) += megaraid_sas.o
megaraid_sas-objs := megaraid_sas_base.o megaraid_sas_fusion.o \
- megaraid_sas_fp.o
+ megaraid_sas_fp.o megaraid_sas_debugfs.o
diff --git a/drivers/scsi/megaraid/mbox_defs.h b/drivers/scsi/megaraid/mbox_defs.h
index e01c6f7c2cac..f0ef8f7f82c1 100644
--- a/drivers/scsi/megaraid/mbox_defs.h
+++ b/drivers/scsi/megaraid/mbox_defs.h
@@ -1,16 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* Linux MegaRAID Unified device driver
*
* Copyright (c) 2003-2004 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* FILE : mbox_defs.h
- *
*/
#ifndef _MRAID_MBOX_DEFS_H_
#define _MRAID_MBOX_DEFS_H_
@@ -786,5 +781,3 @@ typedef struct {
} __attribute__ ((packed)) mbox_sgl32;
#endif // _MRAID_MBOX_DEFS_H_
-
-/* vim: set ts=8 sw=8 tw=78: */
diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h
index 5ead1283a844..2ad0aa2f837d 100644
--- a/drivers/scsi/megaraid/mega_common.h
+++ b/drivers/scsi/megaraid/mega_common.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* Linux MegaRAID device driver
*
* Copyright (c) 2003-2004 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* FILE : mega_common.h
*
* Libaray of common routine used by all low-level megaraid drivers
@@ -204,7 +200,7 @@ typedef struct {
#define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata // to soft state
#define SCP2CHANNEL(scp) (scp)->device->channel // to channel
#define SCP2TARGET(scp) (scp)->device->id // to target
-#define SCP2LUN(scp) (scp)->device->lun // to LUN
+#define SCP2LUN(scp) (u32)(scp)->device->lun // to LUN
// generic macro to convert scsi command and host to controller's soft state
#define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0])
@@ -286,5 +282,3 @@ struct mraid_pci_blk {
};
#endif // _MEGA_COMMON_H_
-
-// vim: set ts=8 sw=8 tw=78:
diff --git a/drivers/scsi/megaraid/megaraid_ioctl.h b/drivers/scsi/megaraid/megaraid_ioctl.h
index 05f6e4ec3453..ae9c2ff7edac 100644
--- a/drivers/scsi/megaraid/megaraid_ioctl.h
+++ b/drivers/scsi/megaraid/megaraid_ioctl.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* Linux MegaRAID device driver
*
* Copyright (c) 2003-2004 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* FILE : megaraid_ioctl.h
*
* Definitions to interface with user level applications
@@ -19,6 +15,7 @@
#include <linux/types.h>
#include <linux/semaphore.h>
+#include <linux/timer.h>
#include "mbox_defs.h"
@@ -153,6 +150,11 @@ typedef struct uioc {
} __attribute__ ((aligned(1024),packed)) uioc_t;
+/* For on-stack uioc timers. */
+struct uioc_timeout {
+ struct timer_list timer;
+ uioc_t *uioc;
+};
/**
* struct mraid_hba_info - information about the controller
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c
index 2e6619eff3ea..b610cad83321 100644
--- a/drivers/scsi/megaraid/megaraid_mbox.c
+++ b/drivers/scsi/megaraid/megaraid_mbox.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* Linux MegaRAID device driver
*
* Copyright (c) 2003-2004 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* FILE : megaraid_mbox.c
* Version : v2.20.5.1 (Nov 16 2006)
*
@@ -38,7 +34,6 @@
* Dell PERC 4e/DC 1000 0408 1028 0002
* Dell PERC 4e/SC 1000 0408 1028 0001
*
- *
* LSI MegaRAID SCSI 320-0 1000 1960 1000 A520
* LSI MegaRAID SCSI 320-1 1000 1960 1000 0520
* LSI MegaRAID SCSI 320-2 1000 1960 1000 0518
@@ -67,10 +62,11 @@
*
* NEC MegaRAID PCI Express ROMB 1000 0408 1033 8287
*
- * For history of changes, see Documentation/ChangeLog.megaraid
+ * For history of changes, see Documentation/scsi/ChangeLog.megaraid
*/
#include <linux/slab.h>
+#include <linux/module.h>
#include "megaraid_mbox.h"
static int megaraid_init(void);
@@ -125,8 +121,8 @@ static irqreturn_t megaraid_isr(int, void *);
static void megaraid_mbox_dpc(unsigned long);
-static ssize_t megaraid_sysfs_show_app_hndl(struct device *, struct device_attribute *attr, char *);
-static ssize_t megaraid_sysfs_show_ldnum(struct device *, struct device_attribute *attr, char *);
+static ssize_t megaraid_mbox_app_hndl_show(struct device *, struct device_attribute *attr, char *);
+static ssize_t megaraid_mbox_ld_show(struct device *, struct device_attribute *attr, char *);
static int megaraid_cmm_register(adapter_t *);
static int megaraid_cmm_unregister(adapter_t *);
@@ -185,7 +181,7 @@ MODULE_PARM_DESC(cmd_per_lun,
* This would result in non-disk devices being skipped during driver load
* time. These can be later added though, using /proc/scsi/scsi
*/
-static unsigned int megaraid_fast_load = 0;
+static unsigned int megaraid_fast_load;
module_param_named(fast_load, megaraid_fast_load, int, 0);
MODULE_PARM_DESC(fast_load,
"Faster loading of the driver, skips physical devices! (default=0)");
@@ -201,16 +197,9 @@ module_param_named(debug_level, mraid_debug_level, int, 0);
MODULE_PARM_DESC(debug_level, "Debug level for driver (default=0)");
/*
- * ### global data ###
- */
-static uint8_t megaraid_mbox_version[8] =
- { 0x02, 0x20, 0x04, 0x06, 3, 7, 20, 5 };
-
-
-/*
* PCI table for all supported controllers.
*/
-static struct pci_device_id pci_id_table_g[] = {
+static const struct pci_device_id pci_id_table_g[] = {
{
PCI_VENDOR_ID_DELL,
PCI_DEVICE_ID_PERC4_DI_DISCOVERY,
@@ -304,7 +293,7 @@ static struct pci_driver megaraid_pci_driver = {
.name = "megaraid",
.id_table = pci_id_table_g,
.probe = megaraid_probe_one,
- .remove = __devexit_p(megaraid_detach_one),
+ .remove = megaraid_detach_one,
.shutdown = megaraid_mbox_shutdown,
};
@@ -313,61 +302,40 @@ static struct pci_driver megaraid_pci_driver = {
// definitions for the device attributes for exporting logical drive number
// for a scsi address (Host, Channel, Id, Lun)
-DEVICE_ATTR(megaraid_mbox_app_hndl, S_IRUSR, megaraid_sysfs_show_app_hndl,
- NULL);
+static DEVICE_ATTR_ADMIN_RO(megaraid_mbox_app_hndl);
// Host template initializer for megaraid mbox sysfs device attributes
-static struct device_attribute *megaraid_shost_attrs[] = {
- &dev_attr_megaraid_mbox_app_hndl,
+static struct attribute *megaraid_shost_attrs[] = {
+ &dev_attr_megaraid_mbox_app_hndl.attr,
NULL,
};
+ATTRIBUTE_GROUPS(megaraid_shost);
-DEVICE_ATTR(megaraid_mbox_ld, S_IRUSR, megaraid_sysfs_show_ldnum, NULL);
+static DEVICE_ATTR_ADMIN_RO(megaraid_mbox_ld);
// Host template initializer for megaraid mbox sysfs device attributes
-static struct device_attribute *megaraid_sdev_attrs[] = {
- &dev_attr_megaraid_mbox_ld,
+static struct attribute *megaraid_sdev_attrs[] = {
+ &dev_attr_megaraid_mbox_ld.attr,
NULL,
};
-/**
- * megaraid_change_queue_depth - Change the device's queue depth
- * @sdev: scsi device struct
- * @qdepth: depth to set
- * @reason: calling context
- *
- * Return value:
- * actual depth set
- */
-static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (qdepth > MBOX_MAX_SCSI_CMDS)
- qdepth = MBOX_MAX_SCSI_CMDS;
- scsi_adjust_queue_depth(sdev, 0, qdepth);
- return sdev->queue_depth;
-}
+ATTRIBUTE_GROUPS(megaraid_sdev);
/*
* Scsi host template for megaraid unified driver
*/
-static struct scsi_host_template megaraid_template_g = {
+static const struct scsi_host_template megaraid_template_g = {
.module = THIS_MODULE,
.name = "LSI Logic MegaRAID driver",
.proc_name = "megaraid",
.queuecommand = megaraid_queue_command,
.eh_abort_handler = megaraid_abort_handler,
- .eh_device_reset_handler = megaraid_reset_handler,
- .eh_bus_reset_handler = megaraid_reset_handler,
.eh_host_reset_handler = megaraid_reset_handler,
- .change_queue_depth = megaraid_change_queue_depth,
- .use_clustering = ENABLE_CLUSTERING,
- .sdev_attrs = megaraid_sdev_attrs,
- .shost_attrs = megaraid_shost_attrs,
+ .change_queue_depth = scsi_change_queue_depth,
+ .no_write_same = 1,
+ .sdev_groups = megaraid_sdev_groups,
+ .shost_groups = megaraid_shost_groups,
};
@@ -433,7 +401,7 @@ megaraid_exit(void)
* This routine should be called whenever a new adapter is detected by the
* PCI hotplug susbsystem.
*/
-static int __devinit
+static int
megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
adapter_t *adapter;
@@ -470,7 +438,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
// set up PCI related soft state and other pre-known parameters
- adapter->unique_id = pdev->bus->number << 8 | pdev->devfn;
+ adapter->unique_id = pci_dev_id(pdev);
adapter->irq = pdev->irq;
adapter->pdev = pdev;
@@ -478,10 +446,9 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
// Setup the default DMA mask. This would be changed later on
// depending on hardware capabilities
- if (pci_set_dma_mask(adapter->pdev, DMA_BIT_MASK(32)) != 0) {
-
+ if (dma_set_mask(&adapter->pdev->dev, DMA_BIT_MASK(32))) {
con_log(CL_ANN, (KERN_WARNING
- "megaraid: pci_set_dma_mask failed:%d\n", __LINE__));
+ "megaraid: dma_set_mask failed:%d\n", __LINE__));
goto out_free_adapter;
}
@@ -505,7 +472,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
// Start the mailbox based controller
if (megaraid_init_mbox(adapter) != 0) {
con_log(CL_ANN, (KERN_WARNING
- "megaraid: maibox adapter did not initialize\n"));
+ "megaraid: mailbox adapter did not initialize\n"));
goto out_free_adapter;
}
@@ -533,7 +500,6 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
out_cmm_unreg:
- pci_set_drvdata(pdev, NULL);
megaraid_cmm_unregister(adapter);
out_fini_mbox:
megaraid_fini_mbox(adapter);
@@ -548,7 +514,7 @@ out_probe_one:
/**
* megaraid_detach_one - release framework resources and call LLD release routine
- * @pdev : handle for our PCI cofiguration space
+ * @pdev : handle for our PCI configuration space
*
* This routine is called during driver unload. We free all the allocated
* resources and call the corresponding LLD so that it can also release all
@@ -593,11 +559,6 @@ megaraid_detach_one(struct pci_dev *pdev)
// detach from the IO sub-system
megaraid_io_detach(adapter);
- // reset the device state in the PCI structure. We check this
- // condition when we enter here. If the device state is NULL,
- // that would mean the device has already been removed
- pci_set_drvdata(pdev, NULL);
-
// Unregister from common management module
//
// FIXME: this must return success or failure for conditions if there
@@ -660,7 +621,7 @@ megaraid_io_attach(adapter_t *adapter)
host = scsi_host_alloc(&megaraid_template_g, 8);
if (!host) {
con_log(CL_ANN, (KERN_WARNING
- "megaraid mbox: scsi_register failed\n"));
+ "megaraid mbox: scsi_host_alloc failed\n"));
return -1;
}
@@ -734,7 +695,7 @@ megaraid_io_detach(adapter_t *adapter)
* - Allocate memory required for all the commands
* - Use internal library of FW routines, build up complete soft state
*/
-static int __devinit
+static int
megaraid_init_mbox(adapter_t *adapter)
{
struct pci_dev *pdev;
@@ -772,7 +733,7 @@ megaraid_init_mbox(adapter_t *adapter)
goto out_free_raid_dev;
}
- raid_dev->baseaddr = ioremap_nocache(raid_dev->baseport, 128);
+ raid_dev->baseaddr = ioremap(raid_dev->baseport, 128);
if (!raid_dev->baseaddr) {
@@ -905,11 +866,12 @@ megaraid_init_mbox(adapter_t *adapter)
adapter->pdev->device == PCI_DEVICE_ID_PERC4_DI_EVERGLADES) ||
(adapter->pdev->vendor == PCI_VENDOR_ID_DELL &&
adapter->pdev->device == PCI_DEVICE_ID_PERC4E_DI_KOBUK)) {
- if (pci_set_dma_mask(adapter->pdev, DMA_BIT_MASK(64))) {
+ if (dma_set_mask(&adapter->pdev->dev, DMA_BIT_MASK(64))) {
con_log(CL_ANN, (KERN_WARNING
"megaraid: DMA mask for 64-bit failed\n"));
- if (pci_set_dma_mask (adapter->pdev, DMA_BIT_MASK(32))) {
+ if (dma_set_mask(&adapter->pdev->dev,
+ DMA_BIT_MASK(32))) {
con_log(CL_ANN, (KERN_WARNING
"megaraid: 32-bit DMA mask failed\n"));
goto out_free_sysfs_res;
@@ -977,8 +939,8 @@ megaraid_fini_mbox(adapter_t *adapter)
* megaraid_alloc_cmd_packets - allocate shared mailbox
* @adapter : soft state of the raid controller
*
- * Allocate and align the shared mailbox. This maibox is used to issue
- * all the commands. For IO based controllers, the mailbox is also regsitered
+ * Allocate and align the shared mailbox. This mailbox is used to issue
+ * all the commands. For IO based controllers, the mailbox is also registered
* with the FW. Allocate memory for all commands as well.
* This is our big allocator.
*/
@@ -1002,8 +964,10 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
* Allocate the common 16-byte aligned memory for the handshake
* mailbox.
*/
- raid_dev->una_mbox64 = pci_alloc_consistent(adapter->pdev,
- sizeof(mbox64_t), &raid_dev->una_mbox64_dma);
+ raid_dev->una_mbox64 = dma_alloc_coherent(&adapter->pdev->dev,
+ sizeof(mbox64_t),
+ &raid_dev->una_mbox64_dma,
+ GFP_KERNEL);
if (!raid_dev->una_mbox64) {
con_log(CL_ANN, (KERN_WARNING
@@ -1011,7 +975,6 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
__LINE__));
return -1;
}
- memset(raid_dev->una_mbox64, 0, sizeof(mbox64_t));
/*
* Align the mailbox at 16-byte boundary
@@ -1030,8 +993,8 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
align;
// Allocate memory for commands issued internally
- adapter->ibuf = pci_alloc_consistent(pdev, MBOX_IBUF_SIZE,
- &adapter->ibuf_dma_h);
+ adapter->ibuf = dma_alloc_coherent(&pdev->dev, MBOX_IBUF_SIZE,
+ &adapter->ibuf_dma_h, GFP_KERNEL);
if (!adapter->ibuf) {
con_log(CL_ANN, (KERN_WARNING
@@ -1040,7 +1003,6 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
goto out_free_common_mbox;
}
- memset(adapter->ibuf, 0, MBOX_IBUF_SIZE);
// Allocate memory for our SCSI Command Blocks and their associated
// memory
@@ -1110,7 +1072,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
scb->scp = NULL;
scb->state = SCB_FREE;
- scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_direction = DMA_NONE;
scb->dma_type = MRAID_DMA_NONE;
scb->dev_channel = -1;
scb->dev_target = -1;
@@ -1126,10 +1088,10 @@ out_teardown_dma_pools:
out_free_scb_list:
kfree(adapter->kscb_list);
out_free_ibuf:
- pci_free_consistent(pdev, MBOX_IBUF_SIZE, (void *)adapter->ibuf,
+ dma_free_coherent(&pdev->dev, MBOX_IBUF_SIZE, (void *)adapter->ibuf,
adapter->ibuf_dma_h);
out_free_common_mbox:
- pci_free_consistent(adapter->pdev, sizeof(mbox64_t),
+ dma_free_coherent(&adapter->pdev->dev, sizeof(mbox64_t),
(caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
return -1;
@@ -1151,10 +1113,10 @@ megaraid_free_cmd_packets(adapter_t *adapter)
kfree(adapter->kscb_list);
- pci_free_consistent(adapter->pdev, MBOX_IBUF_SIZE,
+ dma_free_coherent(&adapter->pdev->dev, MBOX_IBUF_SIZE,
(void *)adapter->ibuf, adapter->ibuf_dma_h);
- pci_free_consistent(adapter->pdev, sizeof(mbox64_t),
+ dma_free_coherent(&adapter->pdev->dev, sizeof(mbox64_t),
(caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
return;
}
@@ -1179,8 +1141,8 @@ megaraid_mbox_setup_dma_pools(adapter_t *adapter)
// Allocate memory for 16-bytes aligned mailboxes
- raid_dev->mbox_pool_handle = pci_pool_create("megaraid mbox pool",
- adapter->pdev,
+ raid_dev->mbox_pool_handle = dma_pool_create("megaraid mbox pool",
+ &adapter->pdev->dev,
sizeof(mbox64_t) + 16,
16, 0);
@@ -1190,7 +1152,7 @@ megaraid_mbox_setup_dma_pools(adapter_t *adapter)
mbox_pci_blk = raid_dev->mbox_pool;
for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
- mbox_pci_blk[i].vaddr = pci_pool_alloc(
+ mbox_pci_blk[i].vaddr = dma_pool_alloc(
raid_dev->mbox_pool_handle,
GFP_KERNEL,
&mbox_pci_blk[i].dma_addr);
@@ -1205,10 +1167,10 @@ megaraid_mbox_setup_dma_pools(adapter_t *adapter)
* structure
* Since passthru and extended passthru commands are exclusive, they
* share common memory pool. Passthru structures piggyback on memory
- * allocted to extended passthru since passthru is smaller of the two
+ * allocated to extended passthru since passthru is smaller of the two
*/
- raid_dev->epthru_pool_handle = pci_pool_create("megaraid mbox pthru",
- adapter->pdev, sizeof(mraid_epassthru_t), 128, 0);
+ raid_dev->epthru_pool_handle = dma_pool_create("megaraid mbox pthru",
+ &adapter->pdev->dev, sizeof(mraid_epassthru_t), 128, 0);
if (raid_dev->epthru_pool_handle == NULL) {
goto fail_setup_dma_pool;
@@ -1216,7 +1178,7 @@ megaraid_mbox_setup_dma_pools(adapter_t *adapter)
epthru_pci_blk = raid_dev->epthru_pool;
for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
- epthru_pci_blk[i].vaddr = pci_pool_alloc(
+ epthru_pci_blk[i].vaddr = dma_pool_alloc(
raid_dev->epthru_pool_handle,
GFP_KERNEL,
&epthru_pci_blk[i].dma_addr);
@@ -1228,8 +1190,8 @@ megaraid_mbox_setup_dma_pools(adapter_t *adapter)
// Allocate memory for each scatter-gather list. Request for 512 bytes
// alignment for each sg list
- raid_dev->sg_pool_handle = pci_pool_create("megaraid mbox sg",
- adapter->pdev,
+ raid_dev->sg_pool_handle = dma_pool_create("megaraid mbox sg",
+ &adapter->pdev->dev,
sizeof(mbox_sgl64) * MBOX_MAX_SG_SIZE,
512, 0);
@@ -1239,7 +1201,7 @@ megaraid_mbox_setup_dma_pools(adapter_t *adapter)
sg_pci_blk = raid_dev->sg_pool;
for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
- sg_pci_blk[i].vaddr = pci_pool_alloc(
+ sg_pci_blk[i].vaddr = dma_pool_alloc(
raid_dev->sg_pool_handle,
GFP_KERNEL,
&sg_pci_blk[i].dma_addr);
@@ -1275,29 +1237,26 @@ megaraid_mbox_teardown_dma_pools(adapter_t *adapter)
sg_pci_blk = raid_dev->sg_pool;
for (i = 0; i < MBOX_MAX_SCSI_CMDS && sg_pci_blk[i].vaddr; i++) {
- pci_pool_free(raid_dev->sg_pool_handle, sg_pci_blk[i].vaddr,
+ dma_pool_free(raid_dev->sg_pool_handle, sg_pci_blk[i].vaddr,
sg_pci_blk[i].dma_addr);
}
- if (raid_dev->sg_pool_handle)
- pci_pool_destroy(raid_dev->sg_pool_handle);
+ dma_pool_destroy(raid_dev->sg_pool_handle);
epthru_pci_blk = raid_dev->epthru_pool;
for (i = 0; i < MBOX_MAX_SCSI_CMDS && epthru_pci_blk[i].vaddr; i++) {
- pci_pool_free(raid_dev->epthru_pool_handle,
+ dma_pool_free(raid_dev->epthru_pool_handle,
epthru_pci_blk[i].vaddr, epthru_pci_blk[i].dma_addr);
}
- if (raid_dev->epthru_pool_handle)
- pci_pool_destroy(raid_dev->epthru_pool_handle);
+ dma_pool_destroy(raid_dev->epthru_pool_handle);
mbox_pci_blk = raid_dev->mbox_pool;
for (i = 0; i < MBOX_MAX_SCSI_CMDS && mbox_pci_blk[i].vaddr; i++) {
- pci_pool_free(raid_dev->mbox_pool_handle,
+ dma_pool_free(raid_dev->mbox_pool_handle,
mbox_pci_blk[i].vaddr, mbox_pci_blk[i].dma_addr);
}
- if (raid_dev->mbox_pool_handle)
- pci_pool_destroy(raid_dev->mbox_pool_handle);
+ dma_pool_destroy(raid_dev->mbox_pool_handle);
return;
}
@@ -1456,12 +1415,6 @@ mbox_post_cmd(adapter_t *adapter, scb_t *scb)
adapter->outstanding_cmds++;
- if (scb->dma_direction == PCI_DMA_TODEVICE)
- pci_dma_sync_sg_for_device(adapter->pdev,
- scsi_sglist(scb->scp),
- scsi_sg_count(scb->scp),
- PCI_DMA_TODEVICE);
-
mbox->busy = 1; // Set busy
mbox->poll = 0;
mbox->ack = 0;
@@ -1476,21 +1429,19 @@ mbox_post_cmd(adapter_t *adapter, scb_t *scb)
/**
- * megaraid_queue_command - generic queue entry point for all LLDs
+ * megaraid_queue_command_lck - generic queue entry point for all LLDs
* @scp : pointer to the scsi command to be executed
- * @done : callback routine to be called after the cmd has be completed
*
* Queue entry point for mailbox based controllers.
*/
-static int
-megaraid_queue_command_lck(struct scsi_cmnd *scp, void (*done)(struct scsi_cmnd *))
+static int megaraid_queue_command_lck(struct scsi_cmnd *scp)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
adapter_t *adapter;
scb_t *scb;
int if_busy;
adapter = SCP2ADAPTER(scp);
- scp->scsi_done = done;
scp->result = 0;
/*
@@ -1623,14 +1574,12 @@ megaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy)
}
if (scp->cmnd[1] & MEGA_SCSI_INQ_EVPD) {
- scp->sense_buffer[0] = 0x70;
- scp->sense_buffer[2] = ILLEGAL_REQUEST;
- scp->sense_buffer[12] = MEGA_INVALID_FIELD_IN_CDB;
- scp->result = CHECK_CONDITION << 1;
+ scsi_build_sense(scp, 0, ILLEGAL_REQUEST,
+ MEGA_INVALID_FIELD_IN_CDB, 0);
return NULL;
}
- /* Fall through */
+ fallthrough;
case READ_CAPACITY:
/*
@@ -1776,8 +1725,8 @@ megaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy)
return scb;
- case RESERVE:
- case RELEASE:
+ case RESERVE_6:
+ case RELEASE_6:
/*
* Do we support clustering and is the support enabled
*/
@@ -1799,7 +1748,7 @@ megaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy)
scb->dev_channel = 0xFF;
scb->dev_target = target;
ccb->raw_mbox[0] = CLUSTER_CMD;
- ccb->raw_mbox[2] = (scp->cmnd[0] == RESERVE) ?
+ ccb->raw_mbox[2] = scp->cmnd[0] == RESERVE_6 ?
RESERVE_LD : RELEASE_LD;
ccb->raw_mbox[3] = target;
@@ -2026,7 +1975,7 @@ megaraid_mbox_prepare_pthru(adapter_t *adapter, scb_t *scb,
* @scb : scsi control block
* @scp : scsi command from the mid-layer
*
- * Prepare a command for the scsi physical devices. This rountine prepares
+ * Prepare a command for the scsi physical devices. This routine prepares
* commands for devices which can take extended CDBs (>10 bytes).
*/
static void
@@ -2209,31 +2158,6 @@ megaraid_isr(int irq, void *devp)
/**
- * megaraid_mbox_sync_scb - sync kernel buffers
- * @adapter : controller's soft state
- * @scb : pointer to the resource packet
- *
- * DMA sync if required.
- */
-static void
-megaraid_mbox_sync_scb(adapter_t *adapter, scb_t *scb)
-{
- mbox_ccb_t *ccb;
-
- ccb = (mbox_ccb_t *)scb->ccb;
-
- if (scb->dma_direction == PCI_DMA_FROMDEVICE)
- pci_dma_sync_sg_for_cpu(adapter->pdev,
- scsi_sglist(scb->scp),
- scsi_sg_count(scb->scp),
- PCI_DMA_FROMDEVICE);
-
- scsi_dma_unmap(scb->scp);
- return;
-}
-
-
-/**
* megaraid_mbox_dpc - the tasklet to complete the commands from completed list
* @devp : pointer to HBA soft state
*
@@ -2375,8 +2299,7 @@ megaraid_mbox_dpc(unsigned long devp)
memcpy(scp->sense_buffer, pthru->reqsensearea,
14);
- scp->result = DRIVER_SENSE << 24 |
- DID_OK << 16 | CHECK_CONDITION << 1;
+ scp->result = SAM_STAT_CHECK_CONDITION;
}
else {
if (mbox->cmd == MBOXCMD_EXTPTHRU) {
@@ -2384,14 +2307,10 @@ megaraid_mbox_dpc(unsigned long devp)
memcpy(scp->sense_buffer,
epthru->reqsensearea, 14);
- scp->result = DRIVER_SENSE << 24 |
- DID_OK << 16 |
- CHECK_CONDITION << 1;
- } else {
- scp->sense_buffer[0] = 0x70;
- scp->sense_buffer[2] = ABORTED_COMMAND;
- scp->result = CHECK_CONDITION << 1;
- }
+ scp->result = SAM_STAT_CHECK_CONDITION;
+ } else
+ scsi_build_sense(scp, 0,
+ ABORTED_COMMAND, 0, 0);
}
break;
@@ -2408,18 +2327,18 @@ megaraid_mbox_dpc(unsigned long devp)
*/
if (scp->cmnd[0] == TEST_UNIT_READY) {
scp->result = DID_ERROR << 16 |
- RESERVATION_CONFLICT << 1;
+ SAM_STAT_RESERVATION_CONFLICT;
}
else
/*
* Error code returned is 1 if Reserve or Release
* failed or the input parameter is invalid
*/
- if (status == 1 && (scp->cmnd[0] == RESERVE ||
- scp->cmnd[0] == RELEASE)) {
+ if (status == 1 && (scp->cmnd[0] == RESERVE_6 ||
+ scp->cmnd[0] == RELEASE_6)) {
scp->result = DID_ERROR << 16 |
- RESERVATION_CONFLICT << 1;
+ SAM_STAT_RESERVATION_CONFLICT;
}
else {
scp->result = DID_BAD_TARGET << 16 | status;
@@ -2431,9 +2350,7 @@ megaraid_mbox_dpc(unsigned long devp)
megaraid_mbox_display_scb(adapter, scb);
}
- // Free our internal resources and call the mid-layer callback
- // routine
- megaraid_mbox_sync_scb(adapter, scb);
+ scsi_dma_unmap(scp);
// remove from local clist
list_del_init(&scb->list);
@@ -2442,7 +2359,7 @@ megaraid_mbox_dpc(unsigned long devp)
megaraid_dealloc_scb(adapter, scb);
// send the scsi packet back to kernel
- scp->scsi_done(scp);
+ scsi_done(scp);
}
return;
@@ -2500,7 +2417,7 @@ megaraid_abort_handler(struct scsi_cmnd *scp)
scb->sno, scb->dev_channel, scb->dev_target));
scp->result = (DID_ABORT << 16);
- scp->scsi_done(scp);
+ scsi_done(scp);
megaraid_dealloc_scb(adapter, scb);
@@ -2530,7 +2447,7 @@ megaraid_abort_handler(struct scsi_cmnd *scp)
scb->dev_channel, scb->dev_target));
scp->result = (DID_ABORT << 16);
- scp->scsi_done(scp);
+ scsi_done(scp);
megaraid_dealloc_scb(adapter, scb);
@@ -2585,7 +2502,7 @@ megaraid_abort_handler(struct scsi_cmnd *scp)
}
/**
- * megaraid_reset_handler - device reset hadler for mailbox based driver
+ * megaraid_reset_handler - device reset handler for mailbox based driver
* @scp : reference command
*
* Reset handler for the mailbox based controller. First try to find out if
@@ -2605,7 +2522,6 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
uint8_t raw_mbox[sizeof(mbox_t)];
int rval;
int recovery_window;
- int recovering;
int i;
uioc_t *kioc;
@@ -2619,7 +2535,6 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
return FAILED;
}
-
// Under exceptional conditions, FW can take up to 3 minutes to
// complete command processing. Wait for additional 2 minutes for the
// pending commands counter to go down to 0. If it doesn't, let the
@@ -2652,7 +2567,7 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
}
scb->scp->result = (DID_RESET << 16);
- scb->scp->scsi_done(scb->scp);
+ scsi_done(scb->scp);
megaraid_dealloc_scb(adapter, scb);
}
@@ -2668,8 +2583,6 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT;
- recovering = adapter->outstanding_cmds;
-
for (i = 0; i < recovery_window; i++) {
megaraid_ack_sequence(adapter);
@@ -2730,7 +2643,7 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
}
out:
- spin_unlock_irq(&adapter->lock);
+ spin_unlock(&adapter->lock);
return rval;
}
@@ -2753,13 +2666,10 @@ static int
mbox_post_sync_cmd(adapter_t *adapter, uint8_t raw_mbox[])
{
mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
- mbox64_t *mbox64;
mbox_t *mbox;
uint8_t status;
int i;
-
- mbox64 = raid_dev->mbox64;
mbox = raid_dev->mbox;
/*
@@ -2976,9 +2886,8 @@ megaraid_mbox_product_info(adapter_t *adapter)
* Issue an ENQUIRY3 command to find out certain adapter parameters,
* e.g., max channels, max commands etc.
*/
- pinfo = pci_alloc_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
- &pinfo_dma_h);
-
+ pinfo = dma_alloc_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t),
+ &pinfo_dma_h, GFP_KERNEL);
if (pinfo == NULL) {
con_log(CL_ANN, (KERN_WARNING
"megaraid: out of memory, %s %d\n", __func__,
@@ -2986,7 +2895,6 @@ megaraid_mbox_product_info(adapter_t *adapter)
return -1;
}
- memset(pinfo, 0, sizeof(mraid_pinfo_t));
mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
@@ -3000,7 +2908,7 @@ megaraid_mbox_product_info(adapter_t *adapter)
con_log(CL_ANN, (KERN_WARNING "megaraid: Inquiry3 failed\n"));
- pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ dma_free_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t),
pinfo, pinfo_dma_h);
return -1;
@@ -3031,7 +2939,7 @@ megaraid_mbox_product_info(adapter_t *adapter)
con_log(CL_ANN, (KERN_WARNING
"megaraid: product info failed\n"));
- pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ dma_free_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t),
pinfo, pinfo_dma_h);
return -1;
@@ -3067,7 +2975,7 @@ megaraid_mbox_product_info(adapter_t *adapter)
"megaraid: fw version:[%s] bios version:[%s]\n",
adapter->fw_version, adapter->bios_version));
- pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), pinfo,
+ dma_free_coherent(&adapter->pdev->dev, sizeof(mraid_pinfo_t), pinfo,
pinfo_dma_h);
return 0;
@@ -3164,7 +3072,6 @@ megaraid_mbox_support_ha(adapter_t *adapter, uint16_t *init_id)
static int
megaraid_mbox_support_random_del(adapter_t *adapter)
{
- mbox_t *mbox;
uint8_t raw_mbox[sizeof(mbox_t)];
int rval;
@@ -3186,8 +3093,6 @@ megaraid_mbox_support_random_del(adapter_t *adapter)
return 0;
}
- mbox = (mbox_t *)raw_mbox;
-
memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
raw_mbox[0] = FC_DEL_LOGDRV;
@@ -3292,12 +3197,8 @@ megaraid_mbox_enum_raid_scsi(adapter_t *adapter)
static void
megaraid_mbox_flush_cache(adapter_t *adapter)
{
- mbox_t *mbox;
uint8_t raw_mbox[sizeof(mbox_t)];
-
- mbox = (mbox_t *)raw_mbox;
-
memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
raw_mbox[0] = FLUSH_ADAPTER;
@@ -3328,18 +3229,14 @@ megaraid_mbox_fire_sync_cmd(adapter_t *adapter)
mbox_t *mbox;
uint8_t raw_mbox[sizeof(mbox_t)];
mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
- mbox64_t *mbox64;
int status = 0;
int i;
uint32_t dword;
- mbox = (mbox_t *)raw_mbox;
-
memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
raw_mbox[0] = 0xFF;
- mbox64 = raid_dev->mbox64;
mbox = raid_dev->mbox;
/* Wait until mailbox is free */
@@ -3398,7 +3295,6 @@ blocked_mailbox:
* megaraid_mbox_display_scb - display SCB information, mostly debug purposes
* @adapter : controller's soft state
* @scb : SCB to be displayed
- * @level : debug level for console print
*
* Diplay information about the given SCB iff the current debug level is
* verbose.
@@ -3445,7 +3341,7 @@ megaraid_mbox_display_scb(adapter_t *adapter, scb_t *scb)
* megaraid_mbox_setup_device_map - manage device ids
* @adapter : Driver's soft state
*
- * Manange the device ids to have an appropriate mapping between the kernel
+ * Manage the device ids to have an appropriate mapping between the kernel
* scsi addresses and megaraid scsi and logical drive addresses. We export
* scsi devices on their actual addresses, whereas the logical drives are
* exported on a virtual scsi channel.
@@ -3544,7 +3440,7 @@ megaraid_cmm_register(adapter_t *adapter)
scb->scp = NULL;
scb->state = SCB_FREE;
- scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_direction = DMA_NONE;
scb->dma_type = MRAID_DMA_NONE;
scb->dev_channel = -1;
scb->dev_target = -1;
@@ -3682,7 +3578,7 @@ megaraid_mbox_mm_command(adapter_t *adapter, uioc_t *kioc)
scb->state = SCB_ACTIVE;
scb->dma_type = MRAID_DMA_NONE;
- scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_direction = DMA_NONE;
ccb = (mbox_ccb_t *)scb->ccb;
mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
@@ -3823,10 +3719,6 @@ megaraid_mbox_mm_done(adapter_t *adapter, scb_t *scb)
static int
gather_hbainfo(adapter_t *adapter, mraid_hba_info_t *hinfo)
{
- uint8_t dmajor;
-
- dmajor = megaraid_mbox_version[0];
-
hinfo->pci_vendor_id = adapter->pdev->vendor;
hinfo->pci_device_id = adapter->pdev->device;
hinfo->subsys_vendor_id = adapter->pdev->subsystem_vendor;
@@ -3872,8 +3764,8 @@ megaraid_sysfs_alloc_resources(adapter_t *adapter)
raid_dev->sysfs_mbox64 = kmalloc(sizeof(mbox64_t), GFP_KERNEL);
- raid_dev->sysfs_buffer = pci_alloc_consistent(adapter->pdev,
- PAGE_SIZE, &raid_dev->sysfs_buffer_dma);
+ raid_dev->sysfs_buffer = dma_alloc_coherent(&adapter->pdev->dev,
+ PAGE_SIZE, &raid_dev->sysfs_buffer_dma, GFP_KERNEL);
if (!raid_dev->sysfs_uioc || !raid_dev->sysfs_mbox64 ||
!raid_dev->sysfs_buffer) {
@@ -3910,7 +3802,7 @@ megaraid_sysfs_free_resources(adapter_t *adapter)
kfree(raid_dev->sysfs_mbox64);
if (raid_dev->sysfs_buffer) {
- pci_free_consistent(adapter->pdev, PAGE_SIZE,
+ dma_free_coherent(&adapter->pdev->dev, PAGE_SIZE,
raid_dev->sysfs_buffer, raid_dev->sysfs_buffer_dma);
}
}
@@ -3933,19 +3825,19 @@ megaraid_sysfs_get_ldmap_done(uioc_t *uioc)
wake_up(&raid_dev->sysfs_wait_q);
}
-
/**
* megaraid_sysfs_get_ldmap_timeout - timeout handling for get ldmap
- * @data : timed out packet
+ * @t : timed out timer
*
* Timeout routine to recover and return to application, in case the adapter
* has stopped responding. A timeout of 60 seconds for this command seems like
* a good value.
*/
static void
-megaraid_sysfs_get_ldmap_timeout(unsigned long data)
+megaraid_sysfs_get_ldmap_timeout(struct timer_list *t)
{
- uioc_t *uioc = (uioc_t *)data;
+ struct uioc_timeout *timeout = timer_container_of(timeout, t, timer);
+ uioc_t *uioc = timeout->uioc;
adapter_t *adapter = (adapter_t *)uioc->buf_vaddr;
mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
@@ -3980,8 +3872,7 @@ megaraid_sysfs_get_ldmap(adapter_t *adapter)
mbox64_t *mbox64;
mbox_t *mbox;
char *raw_mbox;
- struct timer_list sysfs_timer;
- struct timer_list *timerp;
+ struct uioc_timeout timeout;
caddr_t ldmap;
int rval = 0;
@@ -4017,14 +3908,12 @@ megaraid_sysfs_get_ldmap(adapter_t *adapter)
/*
* Setup a timer to recover from a non-responding controller
*/
- timerp = &sysfs_timer;
- init_timer(timerp);
-
- timerp->function = megaraid_sysfs_get_ldmap_timeout;
- timerp->data = (unsigned long)uioc;
- timerp->expires = jiffies + 60 * HZ;
+ timeout.uioc = uioc;
+ timer_setup_on_stack(&timeout.timer,
+ megaraid_sysfs_get_ldmap_timeout, 0);
- add_timer(timerp);
+ timeout.timer.expires = jiffies + 60 * HZ;
+ add_timer(&timeout.timer);
/*
* Send the command to the firmware
@@ -4062,7 +3951,8 @@ megaraid_sysfs_get_ldmap(adapter_t *adapter)
}
- del_timer_sync(timerp);
+ timer_delete_sync(&timeout.timer);
+ timer_destroy_on_stack(&timeout.timer);
mutex_unlock(&raid_dev->sysfs_mtx);
@@ -4071,8 +3961,9 @@ megaraid_sysfs_get_ldmap(adapter_t *adapter)
/**
- * megaraid_sysfs_show_app_hndl - display application handle for this adapter
- * @cdev : class device object representation for the host
+ * megaraid_mbox_app_hndl_show - display application handle for this adapter
+ * @dev : class device object representation for the host
+ * @attr : device attribute (unused)
* @buf : buffer to send data to
*
* Display the handle used by the applications while executing management
@@ -4080,8 +3971,7 @@ megaraid_sysfs_get_ldmap(adapter_t *adapter)
* handle, since we do not interface with applications directly.
*/
static ssize_t
-megaraid_sysfs_show_app_hndl(struct device *dev, struct device_attribute *attr,
- char *buf)
+megaraid_mbox_app_hndl_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
adapter_t *adapter = (adapter_t *)SCSIHOST2ADAP(shost);
@@ -4089,12 +3979,12 @@ megaraid_sysfs_show_app_hndl(struct device *dev, struct device_attribute *attr,
app_hndl = mraid_mm_adapter_app_handle(adapter->unique_id);
- return snprintf(buf, 8, "%u\n", app_hndl);
+ return sysfs_emit(buf, "%u\n", app_hndl);
}
/**
- * megaraid_sysfs_show_ldnum - display the logical drive number for this device
+ * megaraid_mbox_ld_show - display the logical drive number for this device
* @dev : device object representation for the scsi device
* @attr : device attribute to show
* @buf : buffer to send data to
@@ -4109,7 +3999,7 @@ megaraid_sysfs_show_app_hndl(struct device *dev, struct device_attribute *attr,
* <int> <int> <int> <int>
*/
static ssize_t
-megaraid_sysfs_show_ldnum(struct device *dev, struct device_attribute *attr, char *buf)
+megaraid_mbox_ld_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev = to_scsi_device(dev);
adapter_t *adapter = (adapter_t *)SCSIHOST2ADAP(sdev->host);
@@ -4158,7 +4048,7 @@ megaraid_sysfs_show_ldnum(struct device *dev, struct device_attribute *attr, cha
}
}
- return snprintf(buf, 36, "%d %d %d %d\n", scsi_id, logical_drv,
+ return sysfs_emit(buf, "%d %d %d %d\n", scsi_id, logical_drv,
ldid_map, app_hndl);
}
@@ -4168,5 +4058,3 @@ megaraid_sysfs_show_ldnum(struct device *dev, struct device_attribute *attr, cha
*/
module_init(megaraid_init);
module_exit(megaraid_exit);
-
-/* vim: set ts=8 sw=8 tw=78 ai si: */
diff --git a/drivers/scsi/megaraid/megaraid_mbox.h b/drivers/scsi/megaraid/megaraid_mbox.h
index c1d86d961a92..d2fe7f69cd5d 100644
--- a/drivers/scsi/megaraid/megaraid_mbox.h
+++ b/drivers/scsi/megaraid/megaraid_mbox.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* Linux MegaRAID device driver
*
* Copyright (c) 2003-2004 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* FILE : megaraid_mbox.h
*/
@@ -117,7 +113,7 @@
* @raw_mbox : raw mailbox pointer
* @mbox : mailbox
* @mbox64 : extended mailbox
- * @mbox_dma_h : maibox dma address
+ * @mbox_dma_h : mailbox dma address
* @sgl64 : 64-bit scatter-gather list
* @sgl32 : 32-bit scatter-gather list
* @sgl_dma_h : dma handle for the scatter-gather list
@@ -234,5 +230,3 @@ typedef struct {
#define WROUTDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x2C)
#endif // _MEGARAID_H_
-
-// vim: set ts=8 sw=8 tw=78:
diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c
index 25506c777381..87184e2538b0 100644
--- a/drivers/scsi/megaraid/megaraid_mm.c
+++ b/drivers/scsi/megaraid/megaraid_mm.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* Linux MegaRAID device driver
*
* Copyright (c) 2003-2004 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* FILE : megaraid_mm.c
* Version : v2.20.2.7 (Jul 16 2006)
*
@@ -35,7 +31,7 @@ static int kioc_to_mimd(uioc_t *, mimd_t __user *);
static int handle_drvrcmd(void __user *, uint8_t, int *);
static int lld_ioctl(mraid_mmadp_t *, uioc_t *);
static void ioctl_done(uioc_t *);
-static void lld_timedout(unsigned long);
+static void lld_timedout(struct timer_list *);
static void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *);
static mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *);
static uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *);
@@ -45,10 +41,6 @@ static int mraid_mm_setup_dma_pools(mraid_mmadp_t *);
static void mraid_mm_free_adp_resources(mraid_mmadp_t *);
static void mraid_mm_teardown_dma_pools(mraid_mmadp_t *);
-#ifdef CONFIG_COMPAT
-static long mraid_mm_compat_ioctl(struct file *, unsigned int, unsigned long);
-#endif
-
MODULE_AUTHOR("LSI Logic Corporation");
MODULE_DESCRIPTION("LSI Logic Management Module");
MODULE_LICENSE("GPL");
@@ -72,9 +64,7 @@ static wait_queue_head_t wait_q;
static const struct file_operations lsi_fops = {
.open = mraid_mm_open,
.unlocked_ioctl = mraid_mm_unlocked_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = mraid_mm_compat_ioctl,
-#endif
+ .compat_ioctl = compat_ptr_ioctl,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
@@ -105,7 +95,6 @@ mraid_mm_open(struct inode *inode, struct file *filep)
/**
* mraid_mm_ioctl - module entry-point for ioctls
- * @inode : inode (ignored)
* @filep : file operations pointer (ignored)
* @cmd : ioctl command
* @arg : user ioctl packet
@@ -179,8 +168,12 @@ mraid_mm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
/*
* The following call will block till a kioc is available
+ * or return NULL if the list head is empty for the pointer
+ * of type mraid_mmapt passed to mraid_mm_alloc_kioc
*/
kioc = mraid_mm_alloc_kioc(adp);
+ if (!kioc)
+ return -ENXIO;
/*
* User sent the old mimd_t ioctl packet. Convert it to uioc_t.
@@ -224,7 +217,6 @@ mraid_mm_unlocked_ioctl(struct file *filep, unsigned int cmd,
{
int err;
- /* inconsistent: mraid_mm_compat_ioctl doesn't take the BKL */
mutex_lock(&mraid_mm_mutex);
err = mraid_mm_ioctl(filep, cmd, arg);
mutex_unlock(&mraid_mm_mutex);
@@ -246,7 +238,7 @@ mraid_mm_get_adapter(mimd_t __user *umimd, int *rval)
mimd_t mimd;
uint32_t adapno;
int iterator;
-
+ bool is_found;
if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) {
*rval = -EFAULT;
@@ -262,12 +254,16 @@ mraid_mm_get_adapter(mimd_t __user *umimd, int *rval)
adapter = NULL;
iterator = 0;
+ is_found = false;
list_for_each_entry(adapter, &adapters_list_g, list) {
- if (iterator++ == adapno) break;
+ if (iterator++ == adapno) {
+ is_found = true;
+ break;
+ }
}
- if (!adapter) {
+ if (!is_found) {
*rval = -ENODEV;
return NULL;
}
@@ -486,6 +482,8 @@ mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc)
pthru32->dataxferaddr = kioc->buf_paddr;
if (kioc->data_dir & UIOC_WR) {
+ if (pthru32->dataxferlen > kioc->xferlen)
+ return -EINVAL;
if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
pthru32->dataxferlen)) {
return (-EFAULT);
@@ -496,7 +494,7 @@ mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc)
}
/**
- * mraid_mm_attch_buf - Attach a free dma buffer for required size
+ * mraid_mm_attach_buf - Attach a free dma buffer for required size
* @adp : Adapter softstate
* @kioc : kioc that the buffer needs to be attached to
* @xferlen : required length for buffer
@@ -568,7 +566,7 @@ mraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen)
kioc->pool_index = right_pool;
kioc->free_buf = 1;
- kioc->buf_vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL,
+ kioc->buf_vaddr = dma_pool_alloc(pool->handle, GFP_ATOMIC,
&kioc->buf_paddr);
spin_unlock_irqrestore(&pool->lock, flags);
@@ -652,7 +650,7 @@ mraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc)
* not in use
*/
if (kioc->free_buf == 1)
- pci_pool_free(pool->handle, kioc->buf_vaddr,
+ dma_pool_free(pool->handle, kioc->buf_vaddr,
kioc->buf_paddr);
else
pool->in_use = 0;
@@ -680,8 +678,7 @@ static int
lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
{
int rval;
- struct timer_list timer;
- struct timer_list *tp = NULL;
+ struct uioc_timeout timeout = { };
kioc->status = -ENODATA;
rval = adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE);
@@ -692,14 +689,12 @@ lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
* Start the timer
*/
if (adp->timeout > 0) {
- tp = &timer;
- init_timer(tp);
+ timeout.uioc = kioc;
+ timer_setup_on_stack(&timeout.timer, lld_timedout, 0);
- tp->function = lld_timedout;
- tp->data = (unsigned long)kioc;
- tp->expires = jiffies + adp->timeout * HZ;
+ timeout.timer.expires = jiffies + adp->timeout * HZ;
- add_timer(tp);
+ add_timer(&timeout.timer);
}
/*
@@ -707,8 +702,9 @@ lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
* call, the ioctl either completed successfully or timedout.
*/
wait_event(wait_q, (kioc->status != -ENODATA));
- if (tp) {
- del_timer_sync(tp);
+ if (timeout.timer.function) {
+ timer_delete_sync(&timeout.timer);
+ timer_destroy_on_stack(&timeout.timer);
}
/*
@@ -733,6 +729,7 @@ ioctl_done(uioc_t *kioc)
uint32_t adapno;
int iterator;
mraid_mmadp_t* adapter;
+ bool is_found;
/*
* When the kioc returns from driver, make sure it still doesn't
@@ -755,19 +752,23 @@ ioctl_done(uioc_t *kioc)
iterator = 0;
adapter = NULL;
adapno = kioc->adapno;
+ is_found = false;
con_log(CL_ANN, ( KERN_WARNING "megaraid cmm: completed "
"ioctl that was timedout before\n"));
list_for_each_entry(adapter, &adapters_list_g, list) {
- if (iterator++ == adapno) break;
+ if (iterator++ == adapno) {
+ is_found = true;
+ break;
+ }
}
kioc->timedout = 0;
- if (adapter) {
+ if (is_found)
mraid_mm_dealloc_kioc( adapter, kioc );
- }
+
}
else {
wake_up(&wait_q);
@@ -777,12 +778,13 @@ ioctl_done(uioc_t *kioc)
/**
* lld_timedout - callback from the expired timer
- * @ptr : ioctl packet that timed out
+ * @t : timer that timed out
*/
static void
-lld_timedout(unsigned long ptr)
+lld_timedout(struct timer_list *t)
{
- uioc_t *kioc = (uioc_t *)ptr;
+ struct uioc_timeout *timeout = timer_container_of(timeout, t, timer);
+ uioc_t *kioc = timeout->uioc;
kioc->status = -ETIME;
kioc->timedout = 1;
@@ -896,7 +898,7 @@ hinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo)
/**
* mraid_mm_register_adp - Registration routine for low level drivers
- * @lld_adp : Adapter objejct
+ * @lld_adp : Adapter object
*/
int
mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
@@ -930,12 +932,14 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
* Allocate single blocks of memory for all required kiocs,
* mailboxes and passthru structures.
*/
- adapter->kioc_list = kmalloc(sizeof(uioc_t) * lld_adp->max_kioc,
- GFP_KERNEL);
- adapter->mbox_list = kmalloc(sizeof(mbox64_t) * lld_adp->max_kioc,
- GFP_KERNEL);
- adapter->pthru_dma_pool = pci_pool_create("megaraid mm pthru pool",
- adapter->pdev,
+ adapter->kioc_list = kmalloc_array(lld_adp->max_kioc,
+ sizeof(uioc_t),
+ GFP_KERNEL);
+ adapter->mbox_list = kmalloc_array(lld_adp->max_kioc,
+ sizeof(mbox64_t),
+ GFP_KERNEL);
+ adapter->pthru_dma_pool = dma_pool_create("megaraid mm pthru pool",
+ &adapter->pdev->dev,
sizeof(mraid_passthru_t),
16, 0);
@@ -964,7 +968,7 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
kioc = adapter->kioc_list + i;
kioc->cmdbuf = (uint64_t)(unsigned long)(mbox_list + i);
- kioc->pthru32 = pci_pool_alloc(adapter->pthru_dma_pool,
+ kioc->pthru32 = dma_pool_alloc(adapter->pthru_dma_pool,
GFP_KERNEL, &kioc->pthru32_h);
if (!kioc->pthru32) {
@@ -1000,7 +1004,7 @@ pthru_dma_pool_error:
for (i = 0; i < lld_adp->max_kioc; i++) {
kioc = adapter->kioc_list + i;
if (kioc->pthru32) {
- pci_pool_free(adapter->pthru_dma_pool, kioc->pthru32,
+ dma_pool_free(adapter->pthru_dma_pool, kioc->pthru32,
kioc->pthru32_h);
}
}
@@ -1010,8 +1014,7 @@ memalloc_error:
kfree(adapter->kioc_list);
kfree(adapter->mbox_list);
- if (adapter->pthru_dma_pool)
- pci_pool_destroy(adapter->pthru_dma_pool);
+ dma_pool_destroy(adapter->pthru_dma_pool);
kfree(adapter);
@@ -1080,14 +1083,15 @@ mraid_mm_setup_dma_pools(mraid_mmadp_t *adp)
pool->buf_size = bufsize;
spin_lock_init(&pool->lock);
- pool->handle = pci_pool_create("megaraid mm data buffer",
- adp->pdev, bufsize, 16, 0);
+ pool->handle = dma_pool_create("megaraid mm data buffer",
+ &adp->pdev->dev, bufsize,
+ 16, 0);
if (!pool->handle) {
goto dma_pool_setup_error;
}
- pool->vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL,
+ pool->vaddr = dma_pool_alloc(pool->handle, GFP_KERNEL,
&pool->paddr);
if (!pool->vaddr)
@@ -1157,14 +1161,14 @@ mraid_mm_free_adp_resources(mraid_mmadp_t *adp)
kioc = adp->kioc_list + i;
- pci_pool_free(adp->pthru_dma_pool, kioc->pthru32,
+ dma_pool_free(adp->pthru_dma_pool, kioc->pthru32,
kioc->pthru32_h);
}
kfree(adp->kioc_list);
kfree(adp->mbox_list);
- pci_pool_destroy(adp->pthru_dma_pool);
+ dma_pool_destroy(adp->pthru_dma_pool);
return;
@@ -1188,10 +1192,10 @@ mraid_mm_teardown_dma_pools(mraid_mmadp_t *adp)
if (pool->handle) {
if (pool->vaddr)
- pci_pool_free(pool->handle, pool->vaddr,
+ dma_pool_free(pool->handle, pool->vaddr,
pool->paddr);
- pci_pool_destroy(pool->handle);
+ dma_pool_destroy(pool->handle);
pool->handle = NULL;
}
}
@@ -1225,25 +1229,6 @@ mraid_mm_init(void)
}
-#ifdef CONFIG_COMPAT
-/**
- * mraid_mm_compat_ioctl - 32bit to 64bit ioctl conversion routine
- * @filep : file operations pointer (ignored)
- * @cmd : ioctl command
- * @arg : user ioctl packet
- */
-static long
-mraid_mm_compat_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg)
-{
- int err;
-
- err = mraid_mm_ioctl(filep, cmd, arg);
-
- return err;
-}
-#endif
-
/**
* mraid_mm_exit - Module exit point
*/
diff --git a/drivers/scsi/megaraid/megaraid_mm.h b/drivers/scsi/megaraid/megaraid_mm.h
index 55b425c0a654..bf4011590020 100644
--- a/drivers/scsi/megaraid/megaraid_mm.h
+++ b/drivers/scsi/megaraid/megaraid_mm.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* Linux MegaRAID device driver
*
* Copyright (c) 2003-2004 LSI Logic Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* FILE : megaraid_mm.h
*/
@@ -17,7 +13,7 @@
#include <linux/spinlock.h>
#include <linux/fs.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 7370c084b178..8ee2bfe47571 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -1,41 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2011 LSI Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Copyright (c) 2003-2013 LSI Corporation
+ * Copyright (c) 2013-2016 Avago Technologies
+ * Copyright (c) 2016-2018 Broadcom Inc.
*
* FILE: megaraid_sas.h
*
- * Authors: LSI Corporation
- *
- * Send feedback to: <megaraidlinux@lsi.com>
+ * Authors: Broadcom Inc.
+ * Kashyap Desai <kashyap.desai@broadcom.com>
+ * Sumit Saxena <sumit.saxena@broadcom.com>
*
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Send feedback to: megaraidlinux.pdl@broadcom.com
*/
#ifndef LSI_MEGARAID_SAS_H
#define LSI_MEGARAID_SAS_H
+#include <scsi/scsi_cmnd.h>
+
/*
* MegaRAID SAS Driver meta data
*/
-#define MEGASAS_VERSION "00.00.05.38-rc1"
-#define MEGASAS_RELDATE "May. 11, 2011"
-#define MEGASAS_EXT_VERSION "Wed. May. 11 17:00:00 PDT 2011"
+#define MEGASAS_VERSION "07.734.00.00-rc1"
+#define MEGASAS_RELDATE "Apr 03, 2025"
+
+#define MEGASAS_MSIX_NAME_LEN 32
/*
* Device IDs
@@ -48,6 +39,63 @@
#define PCI_DEVICE_ID_LSI_SAS0073SKINNY 0x0073
#define PCI_DEVICE_ID_LSI_SAS0071SKINNY 0x0071
#define PCI_DEVICE_ID_LSI_FUSION 0x005b
+#define PCI_DEVICE_ID_LSI_PLASMA 0x002f
+#define PCI_DEVICE_ID_LSI_INVADER 0x005d
+#define PCI_DEVICE_ID_LSI_FURY 0x005f
+#define PCI_DEVICE_ID_LSI_INTRUDER 0x00ce
+#define PCI_DEVICE_ID_LSI_INTRUDER_24 0x00cf
+#define PCI_DEVICE_ID_LSI_CUTLASS_52 0x0052
+#define PCI_DEVICE_ID_LSI_CUTLASS_53 0x0053
+#define PCI_DEVICE_ID_LSI_VENTURA 0x0014
+#define PCI_DEVICE_ID_LSI_CRUSADER 0x0015
+#define PCI_DEVICE_ID_LSI_HARPOON 0x0016
+#define PCI_DEVICE_ID_LSI_TOMCAT 0x0017
+#define PCI_DEVICE_ID_LSI_VENTURA_4PORT 0x001B
+#define PCI_DEVICE_ID_LSI_CRUSADER_4PORT 0x001C
+#define PCI_DEVICE_ID_LSI_AERO_10E1 0x10e1
+#define PCI_DEVICE_ID_LSI_AERO_10E2 0x10e2
+#define PCI_DEVICE_ID_LSI_AERO_10E5 0x10e5
+#define PCI_DEVICE_ID_LSI_AERO_10E6 0x10e6
+#define PCI_DEVICE_ID_LSI_AERO_10E0 0x10e0
+#define PCI_DEVICE_ID_LSI_AERO_10E3 0x10e3
+#define PCI_DEVICE_ID_LSI_AERO_10E4 0x10e4
+#define PCI_DEVICE_ID_LSI_AERO_10E7 0x10e7
+
+/*
+ * Intel HBA SSDIDs
+ */
+#define MEGARAID_INTEL_RS3DC080_SSDID 0x9360
+#define MEGARAID_INTEL_RS3DC040_SSDID 0x9362
+#define MEGARAID_INTEL_RS3SC008_SSDID 0x9380
+#define MEGARAID_INTEL_RS3MC044_SSDID 0x9381
+#define MEGARAID_INTEL_RS3WC080_SSDID 0x9341
+#define MEGARAID_INTEL_RS3WC040_SSDID 0x9343
+#define MEGARAID_INTEL_RMS3BC160_SSDID 0x352B
+
+/*
+ * Intruder HBA SSDIDs
+ */
+#define MEGARAID_INTRUDER_SSDID1 0x9371
+#define MEGARAID_INTRUDER_SSDID2 0x9390
+#define MEGARAID_INTRUDER_SSDID3 0x9370
+
+/*
+ * Intel HBA branding
+ */
+#define MEGARAID_INTEL_RS3DC080_BRANDING \
+ "Intel(R) RAID Controller RS3DC080"
+#define MEGARAID_INTEL_RS3DC040_BRANDING \
+ "Intel(R) RAID Controller RS3DC040"
+#define MEGARAID_INTEL_RS3SC008_BRANDING \
+ "Intel(R) RAID Controller RS3SC008"
+#define MEGARAID_INTEL_RS3MC044_BRANDING \
+ "Intel(R) RAID Controller RS3MC044"
+#define MEGARAID_INTEL_RS3WC080_BRANDING \
+ "Intel(R) RAID Controller RS3WC080"
+#define MEGARAID_INTEL_RS3WC040_BRANDING \
+ "Intel(R) RAID Controller RS3WC040"
+#define MEGARAID_INTEL_RMS3BC160_BRANDING \
+ "Intel(R) Integrated RAID Module RMS3BC160"
/*
* =====================================
@@ -56,7 +104,7 @@
*/
/*
- * MFI stands for MegaRAID SAS FW Interface. This is just a moniker for
+ * MFI stands for MegaRAID SAS FW Interface. This is just a moniker for
* protocol between the software and firmware. Commands are issued using
* "message frames"
*/
@@ -76,10 +124,15 @@
#define MFI_STATE_READY 0xB0000000
#define MFI_STATE_OPERATIONAL 0xC0000000
#define MFI_STATE_FAULT 0xF0000000
+#define MFI_STATE_FORCE_OCR 0x00000080
+#define MFI_STATE_DMADONE 0x00000008
+#define MFI_STATE_CRASH_DUMP_DONE 0x00000004
#define MFI_RESET_REQUIRED 0x00000001
#define MFI_RESET_ADAPTER 0x00000002
#define MEGAMFI_FRAME_SIZE 64
+#define MFI_STATE_FAULT_CODE 0x0FFF0000
+#define MFI_STATE_FAULT_SUBCODE 0x0000FF00
/*
* During FW init, clear pending cmds & reset state using inbound_msg_0
*
@@ -89,6 +142,7 @@
* CLR_HANDSHAKE: FW is waiting for HANDSHAKE from BIOS or Driver
* HOTPLUG : Resume from Hotplug
* MFI_STOP_ADP : Send signal to FW to stop processing
+ * MFI_ADP_TRIGGER_SNAP_DUMP: Inform firmware to initiate snap dump
*/
#define WRITE_SEQUENCE_OFFSET (0x0000000FC) /* I20 */
#define HOST_DIAGNOSTIC_OFFSET (0x000000F8) /* I20 */
@@ -105,6 +159,8 @@
#define MFI_RESET_FLAGS MFI_INIT_READY| \
MFI_INIT_MFIMODE| \
MFI_INIT_ABORT
+#define MFI_ADP_TRIGGER_SNAP_DUMP 0x00000100
+#define MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE (0x01)
/*
* MFI frame flags
@@ -121,6 +177,10 @@
#define MFI_FRAME_DIR_BOTH 0x0018
#define MFI_FRAME_IEEE 0x0020
+/* Driver internal */
+#define DRV_DCMD_POLLED_MODE 0x1
+#define DRV_DCMD_SKIP_REFIRE 0x2
+
/*
* Definition for cmd_status
*/
@@ -129,18 +189,25 @@
/*
* MFI command opcodes
*/
-#define MFI_CMD_INIT 0x00
-#define MFI_CMD_LD_READ 0x01
-#define MFI_CMD_LD_WRITE 0x02
-#define MFI_CMD_LD_SCSI_IO 0x03
-#define MFI_CMD_PD_SCSI_IO 0x04
-#define MFI_CMD_DCMD 0x05
-#define MFI_CMD_ABORT 0x06
-#define MFI_CMD_SMP 0x07
-#define MFI_CMD_STP 0x08
+enum MFI_CMD_OP {
+ MFI_CMD_INIT = 0x0,
+ MFI_CMD_LD_READ = 0x1,
+ MFI_CMD_LD_WRITE = 0x2,
+ MFI_CMD_LD_SCSI_IO = 0x3,
+ MFI_CMD_PD_SCSI_IO = 0x4,
+ MFI_CMD_DCMD = 0x5,
+ MFI_CMD_ABORT = 0x6,
+ MFI_CMD_SMP = 0x7,
+ MFI_CMD_STP = 0x8,
+ MFI_CMD_NVME = 0x9,
+ MFI_CMD_TOOLBOX = 0xa,
+ MFI_CMD_OP_COUNT,
+ MFI_CMD_INVALID = 0xff
+};
#define MR_DCMD_CTRL_GET_INFO 0x01010000
#define MR_DCMD_LD_GET_LIST 0x03010000
+#define MR_DCMD_LD_LIST_QUERY 0x03010100
#define MR_DCMD_CTRL_CACHE_FLUSH 0x01101000
#define MR_FLUSH_CTRL_CACHE 0x01
@@ -160,6 +227,16 @@
#define MR_DCMD_CLUSTER_RESET_LD 0x08010200
#define MR_DCMD_PD_LIST_QUERY 0x02010100
+#define MR_DCMD_CTRL_SET_CRASH_DUMP_PARAMS 0x01190100
+#define MR_DRIVER_SET_APP_CRASHDUMP_MODE (0xF0010000 | 0x0600)
+#define MR_DCMD_PD_GET_INFO 0x02020000
+
+/*
+ * Global functions
+ */
+extern u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id);
+
+
/*
* MFI command completion codes
*/
@@ -221,10 +298,40 @@ enum MFI_STAT {
MFI_STAT_RESERVATION_IN_PROGRESS = 0x36,
MFI_STAT_I2C_ERRORS_DETECTED = 0x37,
MFI_STAT_PCI_ERRORS_DETECTED = 0x38,
+ MFI_STAT_CONFIG_SEQ_MISMATCH = 0x67,
MFI_STAT_INVALID_STATUS = 0xFF
};
+enum mfi_evt_class {
+ MFI_EVT_CLASS_DEBUG = -2,
+ MFI_EVT_CLASS_PROGRESS = -1,
+ MFI_EVT_CLASS_INFO = 0,
+ MFI_EVT_CLASS_WARNING = 1,
+ MFI_EVT_CLASS_CRITICAL = 2,
+ MFI_EVT_CLASS_FATAL = 3,
+ MFI_EVT_CLASS_DEAD = 4
+};
+
+/*
+ * Crash dump related defines
+ */
+#define MAX_CRASH_DUMP_SIZE 512
+#define CRASH_DMA_BUF_SIZE (1024 * 1024)
+
+enum MR_FW_CRASH_DUMP_STATE {
+ UNAVAILABLE = 0,
+ AVAILABLE = 1,
+ COPYING = 2,
+ COPIED = 3,
+ COPY_ERROR = 4,
+};
+
+enum _MR_CRASH_BUF_STATUS {
+ MR_CRASH_BUF_TURN_OFF = 0,
+ MR_CRASH_BUF_TURN_ON = 1,
+};
+
/*
* Number of mailbox bytes in DCMD message frame
*/
@@ -297,6 +404,9 @@ enum MR_EVT_ARGS {
MR_EVT_ARGS_GENERIC,
};
+
+#define SGE_BUFFER_SIZE 4096
+#define MEGASAS_CLUSTER_ID_SIZE 16
/*
* define constants for device list query options
*/
@@ -309,6 +419,15 @@ enum MR_PD_QUERY_TYPE {
MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5,
};
+enum MR_LD_QUERY_TYPE {
+ MR_LD_QUERY_TYPE_ALL = 0,
+ MR_LD_QUERY_TYPE_EXPOSED_TO_HOST = 1,
+ MR_LD_QUERY_TYPE_USED_TGT_IDS = 2,
+ MR_LD_QUERY_TYPE_CLUSTER_ACCESS = 3,
+ MR_LD_QUERY_TYPE_CLUSTER_LOCALE = 4,
+};
+
+
#define MR_EVT_CFG_CLEARED 0x0004
#define MR_EVT_LD_STATE_CHANGE 0x0051
#define MR_EVT_PD_INSERTED 0x005b
@@ -318,7 +437,7 @@ enum MR_PD_QUERY_TYPE {
#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db
#define MR_EVT_LD_OFFLINE 0x00fc
#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152
-#define MAX_LOGICAL_DRIVES 64
+#define MR_EVT_CTRL_PROP_CHANGED 0x012f
enum MR_PD_STATE {
MR_PD_STATE_UNCONFIGURED_GOOD = 0x00,
@@ -332,12 +451,276 @@ enum MR_PD_STATE {
MR_PD_STATE_SYSTEM = 0x40,
};
+union MR_PD_REF {
+ struct {
+ u16 deviceId;
+ u16 seqNum;
+ } mrPdRef;
+ u32 ref;
+};
+
+/*
+ * define the DDF Type bit structure
+ */
+union MR_PD_DDF_TYPE {
+ struct {
+ union {
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u16 forcedPDGUID:1;
+ u16 inVD:1;
+ u16 isGlobalSpare:1;
+ u16 isSpare:1;
+ u16 isForeign:1;
+ u16 reserved:7;
+ u16 intf:4;
+#else
+ u16 intf:4;
+ u16 reserved:7;
+ u16 isForeign:1;
+ u16 isSpare:1;
+ u16 isGlobalSpare:1;
+ u16 inVD:1;
+ u16 forcedPDGUID:1;
+#endif
+ } pdType;
+ u16 type;
+ };
+ u16 reserved;
+ } ddf;
+ struct {
+ u32 reserved;
+ } nonDisk;
+ u32 type;
+} __packed;
+
+/*
+ * defines the progress structure
+ */
+union MR_PROGRESS {
+ struct {
+ u16 progress;
+ union {
+ u16 elapsedSecs;
+ u16 elapsedSecsForLastPercent;
+ };
+ } mrProgress;
+ u32 w;
+} __packed;
+
+/*
+ * defines the physical drive progress structure
+ */
+struct MR_PD_PROGRESS {
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u32 rbld:1;
+ u32 patrol:1;
+ u32 clear:1;
+ u32 copyBack:1;
+ u32 erase:1;
+ u32 locate:1;
+ u32 reserved:26;
+#else
+ u32 reserved:26;
+ u32 locate:1;
+ u32 erase:1;
+ u32 copyBack:1;
+ u32 clear:1;
+ u32 patrol:1;
+ u32 rbld:1;
+#endif
+ } active;
+ union MR_PROGRESS rbld;
+ union MR_PROGRESS patrol;
+ union {
+ union MR_PROGRESS clear;
+ union MR_PROGRESS erase;
+ };
+
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u32 rbld:1;
+ u32 patrol:1;
+ u32 clear:1;
+ u32 copyBack:1;
+ u32 erase:1;
+ u32 reserved:27;
+#else
+ u32 reserved:27;
+ u32 erase:1;
+ u32 copyBack:1;
+ u32 clear:1;
+ u32 patrol:1;
+ u32 rbld:1;
+#endif
+ } pause;
+
+ union MR_PROGRESS reserved[3];
+} __packed;
+
+struct MR_PD_INFO {
+ union MR_PD_REF ref;
+ u8 inquiryData[96];
+ u8 vpdPage83[64];
+ u8 notSupported;
+ u8 scsiDevType;
+
+ union {
+ u8 connectedPortBitmap;
+ u8 connectedPortNumbers;
+ };
+
+ u8 deviceSpeed;
+ u32 mediaErrCount;
+ u32 otherErrCount;
+ u32 predFailCount;
+ u32 lastPredFailEventSeqNum;
+
+ u16 fwState;
+ u8 disabledForRemoval;
+ u8 linkSpeed;
+ union MR_PD_DDF_TYPE state;
+
+ struct {
+ u8 count;
+#ifndef __BIG_ENDIAN_BITFIELD
+ u8 isPathBroken:4;
+ u8 reserved3:3;
+ u8 widePortCapable:1;
+#else
+ u8 widePortCapable:1;
+ u8 reserved3:3;
+ u8 isPathBroken:4;
+#endif
+
+ u8 connectorIndex[2];
+ u8 reserved[4];
+ u64 sasAddr[2];
+ u8 reserved2[16];
+ } pathInfo;
+
+ u64 rawSize;
+ u64 nonCoercedSize;
+ u64 coercedSize;
+ u16 enclDeviceId;
+ u8 enclIndex;
+
+ union {
+ u8 slotNumber;
+ u8 enclConnectorIndex;
+ };
+
+ struct MR_PD_PROGRESS progInfo;
+ u8 badBlockTableFull;
+ u8 unusableInCurrentConfig;
+ u8 vpdPage83Ext[64];
+ u8 powerState;
+ u8 enclPosition;
+ u32 allowedOps;
+ u16 copyBackPartnerId;
+ u16 enclPartnerDeviceId;
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u16 fdeCapable:1;
+ u16 fdeEnabled:1;
+ u16 secured:1;
+ u16 locked:1;
+ u16 foreign:1;
+ u16 needsEKM:1;
+ u16 reserved:10;
+#else
+ u16 reserved:10;
+ u16 needsEKM:1;
+ u16 foreign:1;
+ u16 locked:1;
+ u16 secured:1;
+ u16 fdeEnabled:1;
+ u16 fdeCapable:1;
+#endif
+ } security;
+ u8 mediaType;
+ u8 notCertified;
+ u8 bridgeVendor[8];
+ u8 bridgeProductIdentification[16];
+ u8 bridgeProductRevisionLevel[4];
+ u8 satBridgeExists;
+
+ u8 interfaceType;
+ u8 temperature;
+ u8 emulatedBlockSize;
+ u16 userDataBlockSize;
+ u16 reserved2;
+
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u32 piType:3;
+ u32 piFormatted:1;
+ u32 piEligible:1;
+ u32 NCQ:1;
+ u32 WCE:1;
+ u32 commissionedSpare:1;
+ u32 emergencySpare:1;
+ u32 ineligibleForSSCD:1;
+ u32 ineligibleForLd:1;
+ u32 useSSEraseType:1;
+ u32 wceUnchanged:1;
+ u32 supportScsiUnmap:1;
+ u32 reserved:18;
+#else
+ u32 reserved:18;
+ u32 supportScsiUnmap:1;
+ u32 wceUnchanged:1;
+ u32 useSSEraseType:1;
+ u32 ineligibleForLd:1;
+ u32 ineligibleForSSCD:1;
+ u32 emergencySpare:1;
+ u32 commissionedSpare:1;
+ u32 WCE:1;
+ u32 NCQ:1;
+ u32 piEligible:1;
+ u32 piFormatted:1;
+ u32 piType:3;
+#endif
+ } properties;
+
+ u64 shieldDiagCompletionTime;
+ u8 shieldCounter;
+
+ u8 linkSpeedOther;
+ u8 reserved4[2];
+
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u32 bbmErrCountSupported:1;
+ u32 bbmErrCount:31;
+#else
+ u32 bbmErrCount:31;
+ u32 bbmErrCountSupported:1;
+#endif
+ } bbmErr;
+
+ u8 reserved1[512-428];
+} __packed;
+
+/*
+ * Definition of structure used to expose attributes of VD or JBOD
+ * (this structure is to be filled by firmware when MR_DCMD_DRV_GET_TARGET_PROP
+ * is fired by driver)
+ */
+struct MR_TARGET_PROPERTIES {
+ u32 max_io_size_kb;
+ u32 device_qdepth;
+ u32 sector_size;
+ u8 reset_tmo;
+ u8 reserved[499];
+} __packed;
/*
* defines the physical drive address structure
*/
struct MR_PD_ADDRESS {
- u16 deviceId;
+ __le16 deviceId;
u16 enclDeviceId;
union {
@@ -362,8 +745,8 @@ struct MR_PD_ADDRESS {
* defines the physical drive list structure
*/
struct MR_PD_LIST {
- u32 size;
- u32 count;
+ __le32 size;
+ __le32 count;
struct MR_PD_ADDRESS addr[1];
} __packed;
@@ -380,25 +763,65 @@ union MR_LD_REF {
struct {
u8 targetId;
u8 reserved;
- u16 seqNum;
+ __le16 seqNum;
};
- u32 ref;
+ __le32 ref;
} __packed;
/*
* defines the logical drive list structure
*/
struct MR_LD_LIST {
- u32 ldCount;
- u32 reserved;
+ __le32 ldCount;
+ __le32 reserved;
struct {
union MR_LD_REF ref;
u8 state;
u8 reserved[3];
- u64 size;
- } ldList[MAX_LOGICAL_DRIVES];
+ __le64 size;
+ } ldList[MAX_LOGICAL_DRIVES_EXT];
+} __packed;
+
+struct MR_LD_TARGETID_LIST {
+ __le32 size;
+ __le32 count;
+ u8 pad[3];
+ u8 targetId[MAX_LOGICAL_DRIVES_EXT];
+};
+
+struct MR_HOST_DEVICE_LIST_ENTRY {
+ struct {
+ union {
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u8 reserved:7;
+ u8 is_sys_pd:1;
+#else
+ u8 is_sys_pd:1;
+ u8 reserved:7;
+#endif
+ } bits;
+ u8 byte;
+ } u;
+ } flags;
+ u8 scsi_type;
+ __le16 target_id;
+ u8 reserved[4];
+ __le64 sas_addr[2];
} __packed;
+struct MR_HOST_DEVICE_LIST {
+ __le32 size;
+ __le32 count;
+ __le32 reserved[2];
+ struct MR_HOST_DEVICE_LIST_ENTRY host_device_list[] __counted_by_le(count);
+} __packed;
+
+#define HOST_DEVICE_LIST_SZ (sizeof(struct MR_HOST_DEVICE_LIST) + \
+ (sizeof(struct MR_HOST_DEVICE_LIST_ENTRY) * \
+ (MEGASAS_MAX_PD + MAX_LOGICAL_DRIVES_EXT)))
+
+
/*
* SAS controller properties
*/
@@ -438,25 +861,61 @@ struct megasas_ctrl_prop {
* a bit in the following structure.
*/
struct {
- u32 copyBackDisabled : 1;
- u32 SMARTerEnabled : 1;
- u32 prCorrectUnconfiguredAreas : 1;
- u32 useFdeOnly : 1;
- u32 disableNCQ : 1;
- u32 SSDSMARTerEnabled : 1;
- u32 SSDPatrolReadEnabled : 1;
- u32 enableSpinDownUnconfigured : 1;
- u32 autoEnhancedImport : 1;
- u32 enableSecretKeyControl : 1;
- u32 disableOnlineCtrlReset : 1;
- u32 allowBootWithPinnedCache : 1;
- u32 disableSpinDownHS : 1;
- u32 enableJBOD : 1;
- u32 reserved :18;
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved:18;
+ u32 enableJBOD:1;
+ u32 disableSpinDownHS:1;
+ u32 allowBootWithPinnedCache:1;
+ u32 disableOnlineCtrlReset:1;
+ u32 enableSecretKeyControl:1;
+ u32 autoEnhancedImport:1;
+ u32 enableSpinDownUnconfigured:1;
+ u32 SSDPatrolReadEnabled:1;
+ u32 SSDSMARTerEnabled:1;
+ u32 disableNCQ:1;
+ u32 useFdeOnly:1;
+ u32 prCorrectUnconfiguredAreas:1;
+ u32 SMARTerEnabled:1;
+ u32 copyBackDisabled:1;
+#else
+ u32 copyBackDisabled:1;
+ u32 SMARTerEnabled:1;
+ u32 prCorrectUnconfiguredAreas:1;
+ u32 useFdeOnly:1;
+ u32 disableNCQ:1;
+ u32 SSDSMARTerEnabled:1;
+ u32 SSDPatrolReadEnabled:1;
+ u32 enableSpinDownUnconfigured:1;
+ u32 autoEnhancedImport:1;
+ u32 enableSecretKeyControl:1;
+ u32 disableOnlineCtrlReset:1;
+ u32 allowBootWithPinnedCache:1;
+ u32 disableSpinDownHS:1;
+ u32 enableJBOD:1;
+ u32 reserved:18;
+#endif
} OnOffProperties;
- u8 autoSnapVDSpace;
- u8 viewSpace;
- u16 spinDownTime;
+
+ union {
+ u8 autoSnapVDSpace;
+ u8 viewSpace;
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u16 reserved3:9;
+ u16 enable_fw_dev_list:1;
+ u16 reserved2:1;
+ u16 enable_snap_dump:1;
+ u16 reserved1:4;
+#else
+ u16 reserved1:4;
+ u16 enable_snap_dump:1;
+ u16 reserved2:1;
+ u16 enable_fw_dev_list:1;
+ u16 reserved3:9;
+#endif
+ } on_off_properties2;
+ };
+ __le16 spinDownTime;
u8 reserved[24];
} __packed;
@@ -470,10 +929,10 @@ struct megasas_ctrl_info {
*/
struct {
- u16 vendor_id;
- u16 device_id;
- u16 sub_vendor_id;
- u16 sub_device_id;
+ __le16 vendor_id;
+ __le16 device_id;
+ __le16 sub_vendor_id;
+ __le16 sub_device_id;
u8 reserved[24];
} __attribute__ ((packed)) pci;
@@ -487,7 +946,8 @@ struct megasas_ctrl_info {
u8 PCIE:1;
u8 iSCSI:1;
u8 SAS_3G:1;
- u8 reserved_0:4;
+ u8 SRIOV:1;
+ u8 reserved_0:3;
u8 reserved_1[6];
u8 port_count;
u64 port_addr[8];
@@ -513,8 +973,8 @@ struct megasas_ctrl_info {
/*
* List of components residing in flash. All str are null terminated
*/
- u32 image_check_word;
- u32 image_component_count;
+ __le32 image_check_word;
+ __le32 image_component_count;
struct {
@@ -531,7 +991,7 @@ struct megasas_ctrl_info {
* empty if a flash operation has not occurred. All stings are null
* terminated
*/
- u32 pending_image_component_count;
+ __le32 pending_image_component_count;
struct {
@@ -564,39 +1024,39 @@ struct megasas_ctrl_info {
} __attribute__ ((packed)) hw_present;
- u32 current_fw_time;
+ __le32 current_fw_time;
/*
* Maximum data transfer sizes
*/
- u16 max_concurrent_cmds;
- u16 max_sge_count;
- u32 max_request_size;
+ __le16 max_concurrent_cmds;
+ __le16 max_sge_count;
+ __le32 max_request_size;
/*
* Logical and physical device counts
*/
- u16 ld_present_count;
- u16 ld_degraded_count;
- u16 ld_offline_count;
+ __le16 ld_present_count;
+ __le16 ld_degraded_count;
+ __le16 ld_offline_count;
- u16 pd_present_count;
- u16 pd_disk_present_count;
- u16 pd_disk_pred_failure_count;
- u16 pd_disk_failed_count;
+ __le16 pd_present_count;
+ __le16 pd_disk_present_count;
+ __le16 pd_disk_pred_failure_count;
+ __le16 pd_disk_failed_count;
/*
* Memory size information
*/
- u16 nvram_size;
- u16 memory_size;
- u16 flash_size;
+ __le16 nvram_size;
+ __le16 memory_size;
+ __le16 flash_size;
/*
* Error counters
*/
- u16 mem_correctable_error_count;
- u16 mem_uncorrectable_error_count;
+ __le16 mem_correctable_error_count;
+ __le16 mem_uncorrectable_error_count;
/*
* Cluster information
@@ -607,7 +1067,7 @@ struct megasas_ctrl_info {
/*
* Additional max data transfer sizes
*/
- u16 max_strips_per_io;
+ __le16 max_strips_per_io;
/*
* Controller capabilities structures
@@ -699,8 +1159,346 @@ struct megasas_ctrl_info {
*/
char package_version[0x60];
- u8 pad[0x800 - 0x6a0];
+ /*
+ * If adapterOperations.supportMoreThan8Phys is set,
+ * and deviceInterface.portCount is greater than 8,
+ * SAS Addrs for first 8 ports shall be populated in
+ * deviceInterface.portAddr, and the rest shall be
+ * populated in deviceInterfacePortAddr2.
+ */
+ __le64 deviceInterfacePortAddr2[8]; /*6a0h */
+ u8 reserved3[128]; /*6e0h */
+
+ struct { /*760h */
+ u16 minPdRaidLevel_0:4;
+ u16 maxPdRaidLevel_0:12;
+
+ u16 minPdRaidLevel_1:4;
+ u16 maxPdRaidLevel_1:12;
+
+ u16 minPdRaidLevel_5:4;
+ u16 maxPdRaidLevel_5:12;
+
+ u16 minPdRaidLevel_1E:4;
+ u16 maxPdRaidLevel_1E:12;
+
+ u16 minPdRaidLevel_6:4;
+ u16 maxPdRaidLevel_6:12;
+
+ u16 minPdRaidLevel_10:4;
+ u16 maxPdRaidLevel_10:12;
+
+ u16 minPdRaidLevel_50:4;
+ u16 maxPdRaidLevel_50:12;
+
+ u16 minPdRaidLevel_60:4;
+ u16 maxPdRaidLevel_60:12;
+
+ u16 minPdRaidLevel_1E_RLQ0:4;
+ u16 maxPdRaidLevel_1E_RLQ0:12;
+
+ u16 minPdRaidLevel_1E0_RLQ0:4;
+ u16 maxPdRaidLevel_1E0_RLQ0:12;
+
+ u16 reserved[6];
+ } pdsForRaidLevels;
+
+ __le16 maxPds; /*780h */
+ __le16 maxDedHSPs; /*782h */
+ __le16 maxGlobalHSP; /*784h */
+ __le16 ddfSize; /*786h */
+ u8 maxLdsPerArray; /*788h */
+ u8 partitionsInDDF; /*789h */
+ u8 lockKeyBinding; /*78ah */
+ u8 maxPITsPerLd; /*78bh */
+ u8 maxViewsPerLd; /*78ch */
+ u8 maxTargetId; /*78dh */
+ __le16 maxBvlVdSize; /*78eh */
+
+ __le16 maxConfigurableSSCSize; /*790h */
+ __le16 currentSSCsize; /*792h */
+
+ char expanderFwVersion[12]; /*794h */
+
+ __le16 PFKTrialTimeRemaining; /*7A0h */
+
+ __le16 cacheMemorySize; /*7A2h */
+
+ struct { /*7A4h */
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved:5;
+ u32 activePassive:2;
+ u32 supportConfigAutoBalance:1;
+ u32 mpio:1;
+ u32 supportDataLDonSSCArray:1;
+ u32 supportPointInTimeProgress:1;
+ u32 supportUnevenSpans:1;
+ u32 dedicatedHotSparesLimited:1;
+ u32 headlessMode:1;
+ u32 supportEmulatedDrives:1;
+ u32 supportResetNow:1;
+ u32 realTimeScheduler:1;
+ u32 supportSSDPatrolRead:1;
+ u32 supportPerfTuning:1;
+ u32 disableOnlinePFKChange:1;
+ u32 supportJBOD:1;
+ u32 supportBootTimePFKChange:1;
+ u32 supportSetLinkSpeed:1;
+ u32 supportEmergencySpares:1;
+ u32 supportSuspendResumeBGops:1;
+ u32 blockSSDWriteCacheChange:1;
+ u32 supportShieldState:1;
+ u32 supportLdBBMInfo:1;
+ u32 supportLdPIType3:1;
+ u32 supportLdPIType2:1;
+ u32 supportLdPIType1:1;
+ u32 supportPIcontroller:1;
+#else
+ u32 supportPIcontroller:1;
+ u32 supportLdPIType1:1;
+ u32 supportLdPIType2:1;
+ u32 supportLdPIType3:1;
+ u32 supportLdBBMInfo:1;
+ u32 supportShieldState:1;
+ u32 blockSSDWriteCacheChange:1;
+ u32 supportSuspendResumeBGops:1;
+ u32 supportEmergencySpares:1;
+ u32 supportSetLinkSpeed:1;
+ u32 supportBootTimePFKChange:1;
+ u32 supportJBOD:1;
+ u32 disableOnlinePFKChange:1;
+ u32 supportPerfTuning:1;
+ u32 supportSSDPatrolRead:1;
+ u32 realTimeScheduler:1;
+
+ u32 supportResetNow:1;
+ u32 supportEmulatedDrives:1;
+ u32 headlessMode:1;
+ u32 dedicatedHotSparesLimited:1;
+
+
+ u32 supportUnevenSpans:1;
+ u32 supportPointInTimeProgress:1;
+ u32 supportDataLDonSSCArray:1;
+ u32 mpio:1;
+ u32 supportConfigAutoBalance:1;
+ u32 activePassive:2;
+ u32 reserved:5;
+#endif
+ } adapterOperations2;
+
+ u8 driverVersion[32]; /*7A8h */
+ u8 maxDAPdCountSpinup60; /*7C8h */
+ u8 temperatureROC; /*7C9h */
+ u8 temperatureCtrl; /*7CAh */
+ u8 reserved4; /*7CBh */
+ __le16 maxConfigurablePds; /*7CCh */
+
+
+ u8 reserved5[2]; /*0x7CDh */
+
+ /*
+ * HA cluster information
+ */
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved:25;
+ u32 passive:1;
+ u32 premiumFeatureMismatch:1;
+ u32 ctrlPropIncompatible:1;
+ u32 fwVersionMismatch:1;
+ u32 hwIncompatible:1;
+ u32 peerIsIncompatible:1;
+ u32 peerIsPresent:1;
+#else
+ u32 peerIsPresent:1;
+ u32 peerIsIncompatible:1;
+ u32 hwIncompatible:1;
+ u32 fwVersionMismatch:1;
+ u32 ctrlPropIncompatible:1;
+ u32 premiumFeatureMismatch:1;
+ u32 passive:1;
+ u32 reserved:25;
+#endif
+ } cluster;
+
+ char clusterId[MEGASAS_CLUSTER_ID_SIZE]; /*0x7D4 */
+ struct {
+ u8 maxVFsSupported; /*0x7E4*/
+ u8 numVFsEnabled; /*0x7E5*/
+ u8 requestorId; /*0x7E6 0:PF, 1:VF1, 2:VF2*/
+ u8 reserved; /*0x7E7*/
+ } iov;
+
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved:7;
+ u32 useSeqNumJbodFP:1;
+ u32 supportExtendedSSCSize:1;
+ u32 supportDiskCacheSettingForSysPDs:1;
+ u32 supportCPLDUpdate:1;
+ u32 supportTTYLogCompression:1;
+ u32 discardCacheDuringLDDelete:1;
+ u32 supportSecurityonJBOD:1;
+ u32 supportCacheBypassModes:1;
+ u32 supportDisableSESMonitoring:1;
+ u32 supportForceFlash:1;
+ u32 supportNVDRAM:1;
+ u32 supportDrvActivityLEDSetting:1;
+ u32 supportAllowedOpsforDrvRemoval:1;
+ u32 supportHOQRebuild:1;
+ u32 supportForceTo512e:1;
+ u32 supportNVCacheErase:1;
+ u32 supportDebugQueue:1;
+ u32 supportSwZone:1;
+ u32 supportCrashDump:1;
+ u32 supportMaxExtLDs:1;
+ u32 supportT10RebuildAssist:1;
+ u32 supportDisableImmediateIO:1;
+ u32 supportThermalPollInterval:1;
+ u32 supportPersonalityChange:2;
+#else
+ u32 supportPersonalityChange:2;
+ u32 supportThermalPollInterval:1;
+ u32 supportDisableImmediateIO:1;
+ u32 supportT10RebuildAssist:1;
+ u32 supportMaxExtLDs:1;
+ u32 supportCrashDump:1;
+ u32 supportSwZone:1;
+ u32 supportDebugQueue:1;
+ u32 supportNVCacheErase:1;
+ u32 supportForceTo512e:1;
+ u32 supportHOQRebuild:1;
+ u32 supportAllowedOpsforDrvRemoval:1;
+ u32 supportDrvActivityLEDSetting:1;
+ u32 supportNVDRAM:1;
+ u32 supportForceFlash:1;
+ u32 supportDisableSESMonitoring:1;
+ u32 supportCacheBypassModes:1;
+ u32 supportSecurityonJBOD:1;
+ u32 discardCacheDuringLDDelete:1;
+ u32 supportTTYLogCompression:1;
+ u32 supportCPLDUpdate:1;
+ u32 supportDiskCacheSettingForSysPDs:1;
+ u32 supportExtendedSSCSize:1;
+ u32 useSeqNumJbodFP:1;
+ u32 reserved:7;
+#endif
+ } adapterOperations3;
+
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u8 reserved:7;
+ /* Indicates whether the CPLD image is part of
+ * the package and stored in flash
+ */
+ u8 cpld_in_flash:1;
+#else
+ u8 cpld_in_flash:1;
+ u8 reserved:7;
+#endif
+ u8 reserved1[3];
+ /* Null terminated string. Has the version
+ * information if cpld_in_flash = FALSE
+ */
+ u8 userCodeDefinition[12];
+ } cpld; /* Valid only if upgradableCPLD is TRUE */
+
+ struct {
+ #if defined(__BIG_ENDIAN_BITFIELD)
+ u16 reserved:2;
+ u16 support_nvme_passthru:1;
+ u16 support_pl_debug_info:1;
+ u16 support_flash_comp_info:1;
+ u16 support_host_info:1;
+ u16 support_dual_fw_update:1;
+ u16 support_ssc_rev3:1;
+ u16 fw_swaps_bbu_vpd_info:1;
+ u16 support_pd_map_target_id:1;
+ u16 support_ses_ctrl_in_multipathcfg:1;
+ u16 image_upload_supported:1;
+ u16 support_encrypted_mfc:1;
+ u16 supported_enc_algo:1;
+ u16 support_ibutton_less:1;
+ u16 ctrl_info_ext_supported:1;
+ #else
+
+ u16 ctrl_info_ext_supported:1;
+ u16 support_ibutton_less:1;
+ u16 supported_enc_algo:1;
+ u16 support_encrypted_mfc:1;
+ u16 image_upload_supported:1;
+ /* FW supports LUN based association and target port based */
+ u16 support_ses_ctrl_in_multipathcfg:1;
+ /* association for the SES device connected in multipath mode */
+ /* FW defines Jbod target Id within MR_PD_CFG_SEQ */
+ u16 support_pd_map_target_id:1;
+ /* FW swaps relevant fields in MR_BBU_VPD_INFO_FIXED to
+ * provide the data in little endian order
+ */
+ u16 fw_swaps_bbu_vpd_info:1;
+ u16 support_ssc_rev3:1;
+ /* FW supports CacheCade 3.0, only one SSCD creation allowed */
+ u16 support_dual_fw_update:1;
+ /* FW supports dual firmware update feature */
+ u16 support_host_info:1;
+ /* FW supports MR_DCMD_CTRL_HOST_INFO_SET/GET */
+ u16 support_flash_comp_info:1;
+ /* FW supports MR_DCMD_CTRL_FLASH_COMP_INFO_GET */
+ u16 support_pl_debug_info:1;
+ /* FW supports retrieval of PL debug information through apps */
+ u16 support_nvme_passthru:1;
+ /* FW supports NVMe passthru commands */
+ u16 reserved:2;
+ #endif
+ } adapter_operations4;
+ u8 pad[0x800 - 0x7FE]; /* 0x7FE pad to 2K for expansion */
+
+ u32 size;
+ u32 pad1;
+
+ u8 reserved6[64];
+
+ struct {
+ #if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved:19;
+ u32 support_pci_lane_margining: 1;
+ u32 support_psoc_update:1;
+ u32 support_force_personality_change:1;
+ u32 support_fde_type_mix:1;
+ u32 support_snap_dump:1;
+ u32 support_nvme_tm:1;
+ u32 support_oce_only:1;
+ u32 support_ext_mfg_vpd:1;
+ u32 support_pcie:1;
+ u32 support_cvhealth_info:1;
+ u32 support_profile_change:2;
+ u32 mr_config_ext2_supported:1;
+ #else
+ u32 mr_config_ext2_supported:1;
+ u32 support_profile_change:2;
+ u32 support_cvhealth_info:1;
+ u32 support_pcie:1;
+ u32 support_ext_mfg_vpd:1;
+ u32 support_oce_only:1;
+ u32 support_nvme_tm:1;
+ u32 support_snap_dump:1;
+ u32 support_fde_type_mix:1;
+ u32 support_force_personality_change:1;
+ u32 support_psoc_update:1;
+ u32 support_pci_lane_margining: 1;
+ u32 reserved:19;
+ #endif
+ } adapter_operations5;
+
+ u32 rsvdForAdptOp[63];
+
+ u8 reserved7[3];
+
+ u8 TaskAbortTO; /* Timeout value in seconds used by Abort Task TM */
+ u8 MaxResetTO; /* Max Supported Reset timeout in seconds. */
+ u8 reserved8[3];
} __packed;
/*
@@ -715,19 +1513,50 @@ struct megasas_ctrl_info {
#define MEGASAS_MAX_DEV_PER_CHANNEL 128
#define MEGASAS_DEFAULT_INIT_ID -1
#define MEGASAS_MAX_LUN 8
-#define MEGASAS_MAX_LD 64
-#define MEGASAS_DEFAULT_CMD_PER_LUN 128
+#define MEGASAS_DEFAULT_CMD_PER_LUN 256
#define MEGASAS_MAX_PD (MEGASAS_MAX_PD_CHANNELS * \
MEGASAS_MAX_DEV_PER_CHANNEL)
#define MEGASAS_MAX_LD_IDS (MEGASAS_MAX_LD_CHANNELS * \
MEGASAS_MAX_DEV_PER_CHANNEL)
+#define MEGASAS_MAX_SUPPORTED_LD_IDS 240
+
#define MEGASAS_MAX_SECTORS (2*1024)
#define MEGASAS_MAX_SECTORS_IEEE (2*128)
#define MEGASAS_DBG_LVL 1
#define MEGASAS_FW_BUSY 1
+/* Driver's internal Logging levels*/
+#define OCR_DEBUG (1 << 0)
+#define TM_DEBUG (1 << 1)
+#define LD_PD_DEBUG (1 << 2)
+
+#define SCAN_PD_CHANNEL 0x1
+#define SCAN_VD_CHANNEL 0x2
+
+#define MEGASAS_KDUMP_QUEUE_DEPTH 100
+#define MR_LARGE_IO_MIN_SIZE (32 * 1024)
+#define MR_R1_LDIO_PIGGYBACK_DEFAULT 4
+
+enum MR_SCSI_CMD_TYPE {
+ READ_WRITE_LDIO = 0,
+ NON_READ_WRITE_LDIO = 1,
+ READ_WRITE_SYSPDIO = 2,
+ NON_READ_WRITE_SYSPDIO = 3,
+};
+
+enum DCMD_TIMEOUT_ACTION {
+ INITIATE_OCR = 0,
+ KILL_ADAPTER = 1,
+ IGNORE_TIMEOUT = 2,
+};
+
+enum FW_BOOT_CONTEXT {
+ PROBE_CONTEXT = 0,
+ OCR_CONTEXT = 1,
+};
+
/* Frame Type */
#define IO_FRAME 0
#define PTHRU_FRAME 1
@@ -744,7 +1573,8 @@ struct megasas_ctrl_info {
#define MEGASAS_RESET_NOTICE_INTERVAL 5
#define MEGASAS_IOCTL_CMD 0
#define MEGASAS_DEFAULT_CMD_TIMEOUT 90
-
+#define MEGASAS_THROTTLE_QUEUE_DEPTH 16
+#define MEGASAS_DEFAULT_TM_TIMEOUT 50
/*
* FW reports the maximum of number of commands that it can accept (maximum
* commands that can be outstanding) at any time. The driver must report a
@@ -754,7 +1584,11 @@ struct megasas_ctrl_info {
*/
#define MEGASAS_INT_CMDS 32
#define MEGASAS_SKINNY_INT_CMDS 5
+#define MEGASAS_FUSION_INTERNAL_CMDS 8
+#define MEGASAS_FUSION_IOCTL_CMDS 3
+#define MEGASAS_MFI_IOCTL_CMDS 27
+#define MEGASAS_MAX_MSIX_QUEUES 128
/*
* FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit
* SGLs based on the size of dma_addr_t
@@ -769,8 +1603,11 @@ struct megasas_ctrl_info {
#define MFI_OB_INTR_STATUS_MASK 0x00000002
#define MFI_POLL_TIMEOUT_SECS 60
-#define MEGASAS_COMPLETION_TIMER_INTERVAL (HZ/10)
-
+#define MFI_IO_TIMEOUT_SECS 180
+#define MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF (5 * HZ)
+#define MEGASAS_OCR_SETTLE_TIME_VF (1000 * 30)
+#define MEGASAS_SRIOV_MAX_RESET_TRIES_VF 1
+#define MEGASAS_ROUTINE_WAIT_TIME_VF 300
#define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000
#define MFI_REPLY_GEN2_MESSAGE_INTERRUPT 0x00000001
#define MFI_GEN2_ENABLE_INTERRUPT_MASK (0x00000001 | 0x00000004)
@@ -780,11 +1617,42 @@ struct megasas_ctrl_info {
#define MFI_1068_PCSR_OFFSET 0x84
#define MFI_1068_FW_HANDSHAKE_OFFSET 0x64
#define MFI_1068_FW_READY 0xDDDD0000
+
+#define MR_MAX_REPLY_QUEUES_OFFSET 0X0000001F
+#define MR_MAX_REPLY_QUEUES_EXT_OFFSET 0X003FC000
+#define MR_MAX_REPLY_QUEUES_EXT_OFFSET_SHIFT 14
+#define MR_MAX_MSIX_REG_ARRAY 16
+#define MR_RDPQ_MODE_OFFSET 0X00800000
+
+#define MR_MAX_RAID_MAP_SIZE_OFFSET_SHIFT 16
+#define MR_MAX_RAID_MAP_SIZE_MASK 0x1FF
+#define MR_MIN_MAP_SIZE 0x10000
+/* 64k */
+
+#define MR_CAN_HANDLE_SYNC_CACHE_OFFSET 0X01000000
+
+#define MR_ATOMIC_DESCRIPTOR_SUPPORT_OFFSET (1 << 24)
+
+#define MR_CAN_HANDLE_64_BIT_DMA_OFFSET (1 << 25)
+#define MR_INTR_COALESCING_SUPPORT_OFFSET (1 << 26)
+
+#define MEGASAS_WATCHDOG_THREAD_INTERVAL 1000
+#define MEGASAS_WAIT_FOR_NEXT_DMA_MSECS 20
+#define MEGASAS_WATCHDOG_WAIT_COUNT 50
+
+enum MR_ADAPTER_TYPE {
+ MFI_SERIES = 1,
+ THUNDERBOLT_SERIES = 2,
+ INVADER_SERIES = 3,
+ VENTURA_SERIES = 4,
+ AERO_SERIES = 5,
+};
+
/*
* register set for both 1068 and 1078 controllers
* structure extended for 1078 registers
*/
-
+
struct megasas_register_set {
u32 doorbell; /*0000h*/
u32 fusion_seq_offset; /*0004h*/
@@ -817,16 +1685,16 @@ struct megasas_register_set {
u32 reserved_3[3]; /*00A4h*/
- u32 outbound_scratch_pad ; /*00B0h*/
- u32 outbound_scratch_pad_2; /*00B4h*/
-
- u32 reserved_4[2]; /*00B8h*/
+ u32 outbound_scratch_pad_0; /*00B0h*/
+ u32 outbound_scratch_pad_1; /*00B4h*/
+ u32 outbound_scratch_pad_2; /*00B8h*/
+ u32 outbound_scratch_pad_3; /*00BCh*/
u32 inbound_low_queue_port ; /*00C0h*/
u32 inbound_high_queue_port ; /*00C4h*/
- u32 reserved_5; /*00C8h*/
+ u32 inbound_single_queue_port; /*00C8h*/
u32 res_6[11]; /*CCh*/
u32 host_diag;
u32 seq_offset;
@@ -835,30 +1703,28 @@ struct megasas_register_set {
struct megasas_sge32 {
- u32 phys_addr;
- u32 length;
+ __le32 phys_addr;
+ __le32 length;
} __attribute__ ((packed));
struct megasas_sge64 {
- u64 phys_addr;
- u32 length;
+ __le64 phys_addr;
+ __le32 length;
} __attribute__ ((packed));
struct megasas_sge_skinny {
- u64 phys_addr;
- u32 length;
- u32 flag;
+ __le64 phys_addr;
+ __le32 length;
+ __le32 flag;
} __packed;
union megasas_sgl {
-
- struct megasas_sge32 sge32[1];
- struct megasas_sge64 sge64[1];
- struct megasas_sge_skinny sge_skinny[1];
-
+ DECLARE_FLEX_ARRAY(struct megasas_sge32, sge32);
+ DECLARE_FLEX_ARRAY(struct megasas_sge64, sge64);
+ DECLARE_FLEX_ARRAY(struct megasas_sge_skinny, sge_skinny);
} __attribute__ ((packed));
struct megasas_header {
@@ -873,12 +1739,12 @@ struct megasas_header {
u8 cdb_len; /*06h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
- u32 data_xferlen; /*14h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
+ __le32 data_xferlen; /*14h */
} __attribute__ ((packed));
@@ -889,6 +1755,51 @@ union megasas_sgl_frame {
} __attribute__ ((packed));
+typedef union _MFI_CAPABILITIES {
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved:15;
+ u32 support_memdump:1;
+ u32 support_fw_exposed_dev_list:1;
+ u32 support_nvme_passthru:1;
+ u32 support_64bit_mode:1;
+ u32 support_pd_map_target_id:1;
+ u32 support_qd_throttling:1;
+ u32 support_fp_rlbypass:1;
+ u32 support_vfid_in_ioframe:1;
+ u32 support_ext_io_size:1;
+ u32 support_ext_queue_depth:1;
+ u32 security_protocol_cmds_fw:1;
+ u32 support_core_affinity:1;
+ u32 support_ndrive_r1_lb:1;
+ u32 support_max_255lds:1;
+ u32 support_fastpath_wb:1;
+ u32 support_additional_msix:1;
+ u32 support_fp_remote_lun:1;
+#else
+ u32 support_fp_remote_lun:1;
+ u32 support_additional_msix:1;
+ u32 support_fastpath_wb:1;
+ u32 support_max_255lds:1;
+ u32 support_ndrive_r1_lb:1;
+ u32 support_core_affinity:1;
+ u32 security_protocol_cmds_fw:1;
+ u32 support_ext_queue_depth:1;
+ u32 support_ext_io_size:1;
+ u32 support_vfid_in_ioframe:1;
+ u32 support_fp_rlbypass:1;
+ u32 support_qd_throttling:1;
+ u32 support_pd_map_target_id:1;
+ u32 support_64bit_mode:1;
+ u32 support_nvme_passthru:1;
+ u32 support_fw_exposed_dev_list:1;
+ u32 support_memdump:1;
+ u32 reserved:15;
+#endif
+ } mfi_capabilities;
+ __le32 reg;
+} MFI_CAPABILITIES;
+
struct megasas_init_frame {
u8 cmd; /*00h */
@@ -896,35 +1807,37 @@ struct megasas_init_frame {
u8 cmd_status; /*02h */
u8 reserved_1; /*03h */
- u32 reserved_2; /*04h */
-
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ MFI_CAPABILITIES driver_operations; /*04h*/
- u16 flags; /*10h */
- u16 reserved_3; /*12h */
- u32 data_xfer_len; /*14h */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u32 queue_info_new_phys_addr_lo; /*18h */
- u32 queue_info_new_phys_addr_hi; /*1Ch */
- u32 queue_info_old_phys_addr_lo; /*20h */
- u32 queue_info_old_phys_addr_hi; /*24h */
+ __le16 flags; /*10h */
+ __le16 replyqueue_mask; /*12h */
+ __le32 data_xfer_len; /*14h */
- u32 reserved_4[6]; /*28h */
+ __le32 queue_info_new_phys_addr_lo; /*18h */
+ __le32 queue_info_new_phys_addr_hi; /*1Ch */
+ __le32 queue_info_old_phys_addr_lo; /*20h */
+ __le32 queue_info_old_phys_addr_hi; /*24h */
+ __le32 reserved_4[2]; /*28h */
+ __le32 system_info_lo; /*30h */
+ __le32 system_info_hi; /*34h */
+ __le32 reserved_5[2]; /*38h */
} __attribute__ ((packed));
struct megasas_init_queue_info {
- u32 init_flags; /*00h */
- u32 reply_queue_entries; /*04h */
+ __le32 init_flags; /*00h */
+ __le32 reply_queue_entries; /*04h */
- u32 reply_queue_start_phys_addr_lo; /*08h */
- u32 reply_queue_start_phys_addr_hi; /*0Ch */
- u32 producer_index_phys_addr_lo; /*10h */
- u32 producer_index_phys_addr_hi; /*14h */
- u32 consumer_index_phys_addr_lo; /*18h */
- u32 consumer_index_phys_addr_hi; /*1Ch */
+ __le32 reply_queue_start_phys_addr_lo; /*08h */
+ __le32 reply_queue_start_phys_addr_hi; /*0Ch */
+ __le32 producer_index_phys_addr_lo; /*10h */
+ __le32 producer_index_phys_addr_hi; /*14h */
+ __le32 consumer_index_phys_addr_lo; /*18h */
+ __le32 consumer_index_phys_addr_hi; /*1Ch */
} __attribute__ ((packed));
@@ -940,18 +1853,18 @@ struct megasas_io_frame {
u8 reserved_0; /*06h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
- u32 lba_count; /*14h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
+ __le32 lba_count; /*14h */
- u32 sense_buf_phys_addr_lo; /*18h */
- u32 sense_buf_phys_addr_hi; /*1Ch */
+ __le32 sense_buf_phys_addr_lo; /*18h */
+ __le32 sense_buf_phys_addr_hi; /*1Ch */
- u32 start_lba_lo; /*20h */
- u32 start_lba_hi; /*24h */
+ __le32 start_lba_lo; /*20h */
+ __le32 start_lba_hi; /*24h */
union megasas_sgl sgl; /*28h */
@@ -969,15 +1882,15 @@ struct megasas_pthru_frame {
u8 cdb_len; /*06h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
+ __le32 data_xfer_len; /*14h */
- u32 sense_buf_phys_addr_lo; /*18h */
- u32 sense_buf_phys_addr_hi; /*1Ch */
+ __le32 sense_buf_phys_addr_lo; /*18h */
+ __le32 sense_buf_phys_addr_hi; /*1Ch */
u8 cdb[16]; /*20h */
union megasas_sgl sgl; /*30h */
@@ -992,19 +1905,19 @@ struct megasas_dcmd_frame {
u8 reserved_1[4]; /*03h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
- u32 opcode; /*18h */
+ __le32 data_xfer_len; /*14h */
+ __le32 opcode; /*18h */
union { /*1Ch */
u8 b[12];
- u16 s[6];
- u32 w[3];
+ __le16 s[6];
+ __le32 w[3];
} mbox;
union megasas_sgl sgl; /*28h */
@@ -1018,22 +1931,22 @@ struct megasas_abort_frame {
u8 cmd_status; /*02h */
u8 reserved_1; /*03h */
- u32 reserved_2; /*04h */
+ __le32 reserved_2; /*04h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 reserved_3; /*12h */
- u32 reserved_4; /*14h */
+ __le16 flags; /*10h */
+ __le16 reserved_3; /*12h */
+ __le32 reserved_4; /*14h */
- u32 abort_context; /*18h */
- u32 pad_1; /*1Ch */
+ __le32 abort_context; /*18h */
+ __le32 pad_1; /*1Ch */
- u32 abort_mfi_phys_addr_lo; /*20h */
- u32 abort_mfi_phys_addr_hi; /*24h */
+ __le32 abort_mfi_phys_addr_lo; /*20h */
+ __le32 abort_mfi_phys_addr_hi; /*24h */
- u32 reserved_5[6]; /*28h */
+ __le32 reserved_5[6]; /*28h */
} __attribute__ ((packed));
@@ -1047,14 +1960,14 @@ struct megasas_smp_frame {
u8 reserved_2[3]; /*04h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
- u64 sas_addr; /*18h */
+ __le32 data_xfer_len; /*14h */
+ __le64 sas_addr; /*18h */
union {
struct megasas_sge32 sge32[2]; /* [0]: resp [1]: req */
@@ -1074,16 +1987,16 @@ struct megasas_stp_frame {
u8 reserved_3[2]; /*05h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
+ __le32 data_xfer_len; /*14h */
- u16 fis[10]; /*18h */
- u32 stp_flags;
+ __le16 fis[10]; /*18h */
+ __le32 stp_flags;
union {
struct megasas_sge32 sge32[2]; /* [0]: resp [1]: data */
@@ -1106,14 +2019,35 @@ union megasas_frame {
u8 raw_bytes[64];
};
+/**
+ * struct MR_PRIV_DEVICE - sdev private hostdata
+ * @is_tm_capable: firmware managed tm_capable flag
+ * @tm_busy: TM request is in progress
+ * @sdev_priv_busy: pending command per sdev
+ */
+struct MR_PRIV_DEVICE {
+ bool is_tm_capable;
+ bool tm_busy;
+ atomic_t sdev_priv_busy;
+ atomic_t r1_ldio_hint;
+ u8 interface_type;
+ u8 task_abort_tmo;
+ u8 target_reset_tmo;
+};
struct megasas_cmd;
union megasas_evt_class_locale {
struct {
+#ifndef __BIG_ENDIAN_BITFIELD
u16 locale;
u8 reserved;
s8 class;
+#else
+ s8 class;
+ u8 reserved;
+ u16 locale;
+#endif
} __attribute__ ((packed)) members;
u32 word;
@@ -1121,18 +2055,18 @@ union megasas_evt_class_locale {
} __attribute__ ((packed));
struct megasas_evt_log_info {
- u32 newest_seq_num;
- u32 oldest_seq_num;
- u32 clear_seq_num;
- u32 shutdown_seq_num;
- u32 boot_seq_num;
+ __le32 newest_seq_num;
+ __le32 oldest_seq_num;
+ __le32 clear_seq_num;
+ __le32 shutdown_seq_num;
+ __le32 boot_seq_num;
} __attribute__ ((packed));
struct megasas_progress {
- u16 progress;
- u16 elapsed_seconds;
+ __le16 progress;
+ __le16 elapsed_seconds;
} __attribute__ ((packed));
@@ -1153,9 +2087,9 @@ struct megasas_evtarg_pd {
struct megasas_evt_detail {
- u32 seq_num;
- u32 time_stamp;
- u32 code;
+ __le32 seq_num;
+ __le32 time_stamp;
+ __le32 code;
union megasas_evt_class_locale cl;
u8 arg_type;
u8 reserved1[15];
@@ -1174,18 +2108,18 @@ struct megasas_evt_detail {
struct {
struct megasas_evtarg_ld ld;
- u64 count;
+ __le64 count;
} __attribute__ ((packed)) ld_count;
struct {
- u64 lba;
+ __le64 lba;
struct megasas_evtarg_ld ld;
} __attribute__ ((packed)) ld_lba;
struct {
struct megasas_evtarg_ld ld;
- u32 prevOwner;
- u32 newOwner;
+ __le32 prevOwner;
+ __le32 newOwner;
} __attribute__ ((packed)) ld_owner;
struct {
@@ -1242,7 +2176,7 @@ struct megasas_evt_detail {
struct {
u16 vendorId;
- u16 deviceId;
+ __le16 deviceId;
u16 subVendorId;
u16 subDeviceId;
} __attribute__ ((packed)) pci;
@@ -1262,9 +2196,9 @@ struct megasas_evt_detail {
} __attribute__ ((packed)) ecc;
u8 b[96];
- u16 s[48];
- u32 w[24];
- u64 d[12];
+ __le16 s[48];
+ __le32 w[24];
+ __le64 d[12];
} args;
char description[128];
@@ -1272,42 +2206,163 @@ struct megasas_evt_detail {
} __attribute__ ((packed));
struct megasas_aen_event {
- struct work_struct hotplug_work;
+ struct delayed_work hotplug_work;
struct megasas_instance *instance;
};
+struct megasas_irq_context {
+ char name[MEGASAS_MSIX_NAME_LEN];
+ struct megasas_instance *instance;
+ u32 MSIxIndex;
+ u32 os_irq;
+ struct irq_poll irqpoll;
+ bool irq_poll_scheduled;
+ bool irq_line_enable;
+ atomic_t in_used;
+};
+
+struct MR_DRV_SYSTEM_INFO {
+ u8 infoVersion;
+ u8 systemIdLength;
+ u16 reserved0;
+ u8 systemId[64];
+ u8 reserved[1980];
+};
+
+enum MR_PD_TYPE {
+ UNKNOWN_DRIVE = 0,
+ PARALLEL_SCSI = 1,
+ SAS_PD = 2,
+ SATA_PD = 3,
+ FC_PD = 4,
+ NVME_PD = 5,
+};
+
+/* JBOD Queue depth definitions */
+#define MEGASAS_SATA_QD 32
+#define MEGASAS_SAS_QD 256
+#define MEGASAS_DEFAULT_PD_QD 64
+#define MEGASAS_NVME_QD 64
+
+#define MR_DEFAULT_NVME_PAGE_SIZE 4096
+#define MR_DEFAULT_NVME_PAGE_SHIFT 12
+#define MR_DEFAULT_NVME_MDTS_KB 128
+#define MR_NVME_PAGE_SIZE_MASK 0x000000FF
+
+/*Aero performance parameters*/
+#define MR_HIGH_IOPS_QUEUE_COUNT 8
+#define MR_DEVICE_HIGH_IOPS_DEPTH 8
+#define MR_HIGH_IOPS_BATCH_COUNT 16
+
+enum MR_PERF_MODE {
+ MR_BALANCED_PERF_MODE = 0,
+ MR_IOPS_PERF_MODE = 1,
+ MR_LATENCY_PERF_MODE = 2,
+};
+
+#define MEGASAS_PERF_MODE_2STR(mode) \
+ ((mode) == MR_BALANCED_PERF_MODE ? "Balanced" : \
+ (mode) == MR_IOPS_PERF_MODE ? "IOPS" : \
+ (mode) == MR_LATENCY_PERF_MODE ? "Latency" : \
+ "Unknown")
+
+enum MEGASAS_LD_TARGET_ID_STATUS {
+ LD_TARGET_ID_INITIAL,
+ LD_TARGET_ID_ACTIVE,
+ LD_TARGET_ID_DELETED,
+};
+
+#define MEGASAS_TARGET_ID(sdev) \
+ (((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id)
+
struct megasas_instance {
- u32 *producer;
+ unsigned int *reply_map;
+ __le32 *producer;
dma_addr_t producer_h;
- u32 *consumer;
+ __le32 *consumer;
dma_addr_t consumer_h;
-
- u32 *reply_queue;
+ struct MR_DRV_SYSTEM_INFO *system_info_buf;
+ dma_addr_t system_info_h;
+ struct MR_LD_VF_AFFILIATION *vf_affiliation;
+ dma_addr_t vf_affiliation_h;
+ struct MR_LD_VF_AFFILIATION_111 *vf_affiliation_111;
+ dma_addr_t vf_affiliation_111_h;
+ struct MR_CTRL_HB_HOST_MEM *hb_host_mem;
+ dma_addr_t hb_host_mem_h;
+ struct MR_PD_INFO *pd_info;
+ dma_addr_t pd_info_h;
+ struct MR_TARGET_PROPERTIES *tgt_prop;
+ dma_addr_t tgt_prop_h;
+
+ __le32 *reply_queue;
dma_addr_t reply_queue_h;
- unsigned long base_addr;
- struct megasas_register_set __iomem *reg_set;
+ u32 *crash_dump_buf;
+ dma_addr_t crash_dump_h;
+
+ struct MR_PD_LIST *pd_list_buf;
+ dma_addr_t pd_list_buf_h;
+
+ struct megasas_ctrl_info *ctrl_info_buf;
+ dma_addr_t ctrl_info_buf_h;
+
+ struct MR_LD_LIST *ld_list_buf;
+ dma_addr_t ld_list_buf_h;
+
+ struct MR_LD_TARGETID_LIST *ld_targetid_list_buf;
+ dma_addr_t ld_targetid_list_buf_h;
+ struct MR_HOST_DEVICE_LIST *host_device_list_buf;
+ dma_addr_t host_device_list_buf_h;
+
+ struct MR_SNAPDUMP_PROPERTIES *snapdump_prop;
+ dma_addr_t snapdump_prop_h;
+
+ void *crash_buf[MAX_CRASH_DUMP_SIZE];
+ unsigned int fw_crash_buffer_size;
+ unsigned int fw_crash_state;
+ unsigned int fw_crash_buffer_offset;
+ u32 drv_buf_index;
+ u32 drv_buf_alloc;
+ u32 crash_dump_fw_support;
+ u32 crash_dump_drv_support;
+ u32 crash_dump_app_support;
+ u32 secure_jbod_support;
+ u32 support_morethan256jbod; /* FW support for more than 256 PD/JBOD */
+ bool use_seqnum_jbod_fp; /* Added for PD sequence */
+ bool smp_affinity_enable;
+ struct mutex crashdump_lock;
+
+ struct megasas_register_set __iomem *reg_set;
+ u32 __iomem *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY];
struct megasas_pd_list pd_list[MEGASAS_MAX_PD];
- u8 ld_ids[MEGASAS_MAX_LD_IDS];
+ struct megasas_pd_list local_pd_list[MEGASAS_MAX_PD];
+ u8 ld_ids[MEGASAS_MAX_LD_IDS];
+ u8 ld_tgtid_status[MEGASAS_MAX_LD_IDS];
+ u8 ld_ids_prev[MEGASAS_MAX_LD_IDS];
+ u8 ld_ids_from_raidmap[MEGASAS_MAX_LD_IDS];
s8 init_id;
u16 max_num_sge;
u16 max_fw_cmds;
- /* For Fusion its num IOCTL cmds, for others MFI based its
- max_fw_cmds */
+ u16 max_mpt_cmds;
u16 max_mfi_cmds;
+ u16 max_scsi_cmds;
+ u16 ldio_threshold;
+ u16 cur_can_queue;
u32 max_sectors_per_req;
+ bool msix_load_balance;
struct megasas_aen_event *ev;
struct megasas_cmd **cmd_list;
struct list_head cmd_pool;
/* used to sync fire the cmd to fw */
- spinlock_t cmd_pool_lock;
+ spinlock_t mfi_pool_lock;
/* used to sync fire the cmd to fw */
spinlock_t hba_lock;
/* used to synch producer, consumer ptrs in dpc */
+ spinlock_t stream_lock;
spinlock_t completion_lock;
struct dma_pool *frame_dma_pool;
struct dma_pool *sense_dma_pool;
@@ -1315,7 +2370,6 @@ struct megasas_instance {
struct megasas_evt_detail *evt_detail;
dma_addr_t evt_detail_h;
struct megasas_cmd *aen_cmd;
- struct mutex aen_mutex;
struct semaphore ioctl_sem;
struct Scsi_Host *host;
@@ -1326,36 +2380,150 @@ struct megasas_instance {
struct pci_dev *pdev;
u32 unique_id;
u32 fw_support_ieee;
+ u32 threshold_reply_count;
atomic_t fw_outstanding;
+ atomic_t ldio_outstanding;
atomic_t fw_reset_no_pci_access;
+ atomic64_t total_io_count;
+ atomic64_t high_iops_outstanding;
struct megasas_instance_template *instancet;
struct tasklet_struct isr_tasklet;
struct work_struct work_init;
+ struct delayed_work fw_fault_work;
+ struct workqueue_struct *fw_fault_work_q;
+ char fault_handler_work_q_name[48];
u8 flag;
u8 unload;
u8 flag_ieee;
u8 issuepend_done;
u8 disableOnlineCtrlReset;
- u8 adprecovery;
+ u8 UnevenSpanSupport;
+
+ u8 supportmax256vd;
+ u8 pd_list_not_supported;
+ u16 fw_supported_vd_count;
+ u16 fw_supported_pd_count;
+
+ u16 drv_supported_vd_count;
+ u16 drv_supported_pd_count;
+
+ atomic_t adprecovery;
unsigned long last_time;
u32 mfiStatus;
u32 last_seq_num;
- struct timer_list io_completion_timer;
struct list_head internal_reset_pending_q;
/* Ptr to hba specific information */
void *ctrl_context;
- u8 msi_flag;
- struct msix_entry msixentry;
+ unsigned int msix_vectors;
+ struct megasas_irq_context irq_context[MEGASAS_MAX_MSIX_QUEUES];
u64 map_id;
+ u64 pd_seq_map_id;
struct megasas_cmd *map_update_cmd;
+ struct megasas_cmd *jbod_seq_cmd;
unsigned long bar;
long reset_flags;
struct mutex reset_mutex;
+ struct timer_list sriov_heartbeat_timer;
+ char skip_heartbeat_timer_del;
+ u8 requestorId;
+ char PlasmaFW111;
+ char clusterId[MEGASAS_CLUSTER_ID_SIZE];
+ u8 peerIsPresent;
+ u8 passive;
+ u16 throttlequeuedepth;
+ u8 mask_interrupts;
+ u16 max_chain_frame_sz;
+ u8 is_imr;
+ u8 is_rdpq;
+ bool dev_handle;
+ bool fw_sync_cache_support;
+ u32 mfi_frame_size;
+ bool msix_combined;
+ u16 max_raid_mapsize;
+ /* preffered count to send as LDIO irrspective of FP capable.*/
+ u8 r1_ldio_hint_default;
+ u32 nvme_page_size;
+ u8 adapter_type;
+ bool consistent_mask_64bit;
+ bool support_nvme_passthru;
+ bool enable_sdev_max_qd;
+ u8 task_abort_tmo;
+ u8 max_reset_tmo;
+ u8 snapdump_wait_time;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_root;
+ struct dentry *raidmap_dump;
+#endif
+ u8 enable_fw_dev_list;
+ bool atomic_desc_support;
+ bool support_seqnum_jbod_fp;
+ bool support_pci_lane_margining;
+ u8 low_latency_index_start;
+ int perf_mode;
+ int iopoll_q_count;
+};
+
+struct MR_LD_VF_MAP {
+ u32 size;
+ union MR_LD_REF ref;
+ u8 ldVfCount;
+ u8 reserved[6];
+ u8 policy[];
+};
+
+struct MR_LD_VF_AFFILIATION {
+ u32 size;
+ u8 ldCount;
+ u8 vfCount;
+ u8 thisVf;
+ u8 reserved[9];
+ struct MR_LD_VF_MAP map[1];
+};
+
+/* Plasma 1.11 FW backward compatibility structures */
+#define IOV_111_OFFSET 0x7CE
+#define MAX_VIRTUAL_FUNCTIONS 8
+#define MR_LD_ACCESS_HIDDEN 15
+
+struct IOV_111 {
+ u8 maxVFsSupported;
+ u8 numVFsEnabled;
+ u8 requestorId;
+ u8 reserved[5];
+};
+
+struct MR_LD_VF_MAP_111 {
+ u8 targetId;
+ u8 reserved[3];
+ u8 policy[MAX_VIRTUAL_FUNCTIONS];
+};
+
+struct MR_LD_VF_AFFILIATION_111 {
+ u8 vdCount;
+ u8 vfCount;
+ u8 thisVf;
+ u8 reserved[5];
+ struct MR_LD_VF_MAP_111 map[MAX_LOGICAL_DRIVES];
+};
+
+struct MR_CTRL_HB_HOST_MEM {
+ struct {
+ u32 fwCounter; /* Firmware heart beat counter */
+ struct {
+ u32 debugmode:1; /* 1=Firmware is in debug mode.
+ Heart beat will not be updated. */
+ u32 reserved:31;
+ } debug;
+ u32 reserved_fw[6];
+ u32 driverCounter; /* Driver heart beat counter. 0x20 */
+ u32 reserved_driver[7];
+ } HB;
+ u8 pad[0x400-0x40];
};
enum {
@@ -1364,6 +2532,7 @@ enum {
MEGASAS_ADPRESET_SM_FW_RESET_SUCCESS = 2,
MEGASAS_ADPRESET_SM_OPERATIONAL = 3,
MEGASAS_HW_CRITICAL_ERROR = 4,
+ MEGASAS_ADPRESET_SM_POLLING = 5,
MEGASAS_ADPRESET_INPROG_SIGN = 0xDEADDEAD,
};
@@ -1371,12 +2540,12 @@ struct megasas_instance_template {
void (*fire_cmd)(struct megasas_instance *, dma_addr_t, \
u32, struct megasas_register_set __iomem *);
- void (*enable_intr)(struct megasas_register_set __iomem *) ;
- void (*disable_intr)(struct megasas_register_set __iomem *);
+ void (*enable_intr)(struct megasas_instance *);
+ void (*disable_intr)(struct megasas_instance *);
- int (*clear_intr)(struct megasas_register_set __iomem *);
+ int (*clear_intr)(struct megasas_instance *);
- u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
+ u32 (*read_fw_status_reg)(struct megasas_instance *);
int (*adp_reset)(struct megasas_instance *, \
struct megasas_register_set __iomem *);
int (*check_reset)(struct megasas_instance *, \
@@ -1386,16 +2555,23 @@ struct megasas_instance_template {
u32 (*init_adapter)(struct megasas_instance *);
u32 (*build_and_issue_cmd) (struct megasas_instance *,
struct scsi_cmnd *);
- void (*issue_dcmd) (struct megasas_instance *instance,
+ void (*issue_dcmd)(struct megasas_instance *instance,
struct megasas_cmd *cmd);
};
-#define MEGASAS_IS_LOGICAL(scp) \
- (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
+#define MEGASAS_IS_LOGICAL(sdev) \
+ ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1)
+
+#define MEGASAS_IS_LUN_VALID(sdev) \
+ (((sdev)->lun == 0) ? 1 : 0)
+
+#define MEGASAS_DEV_INDEX(scp) \
+ (((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
+ scp->device->id)
-#define MEGASAS_DEV_INDEX(inst, scp) \
- ((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
- scp->device->id
+#define MEGASAS_PD_INDEX(scp) \
+ ((scp->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + \
+ scp->device->id)
struct megasas_cmd {
@@ -1406,13 +2582,15 @@ struct megasas_cmd {
u32 index;
u8 sync_cmd;
- u8 cmd_status;
+ u8 cmd_status_drv;
u8 abort_aen;
u8 retry_for_fw_reset;
struct list_head list;
struct scsi_cmnd *scmd;
+ u8 flags;
+
struct megasas_instance *instance;
union {
struct {
@@ -1423,6 +2601,16 @@ struct megasas_cmd {
};
};
+struct megasas_cmd_priv {
+ void *cmd_priv;
+ u8 status;
+};
+
+static inline struct megasas_cmd_priv *megasas_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
#define MAX_MGMT_ADAPTERS 1024
#define MAX_IOCTL_SGE 16
@@ -1450,7 +2638,6 @@ struct megasas_aen {
u32 class_locale_word;
} __attribute__ ((packed));
-#ifdef CONFIG_COMPAT
struct compat_megasas_iocpacket {
u16 host_no;
u16 __pad1;
@@ -1466,7 +2653,6 @@ struct compat_megasas_iocpacket {
} __attribute__ ((packed));
#define MEGASAS_IOC_FIRMWARE32 _IOWR('M', 1, struct compat_megasas_iocpacket)
-#endif
#define MEGASAS_IOC_FIRMWARE _IOWR('M', 1, struct megasas_iocpacket)
#define MEGASAS_IOC_GET_AEN _IOW('M', 3, struct megasas_aen)
@@ -1478,7 +2664,101 @@ struct megasas_mgmt_info {
int max_index;
};
-#define msi_control_reg(base) (base + PCI_MSI_FLAGS)
-#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
+enum MEGASAS_OCR_CAUSE {
+ FW_FAULT_OCR = 0,
+ SCSIIO_TIMEOUT_OCR = 1,
+ MFI_IO_TIMEOUT_OCR = 2,
+};
+
+enum DCMD_RETURN_STATUS {
+ DCMD_SUCCESS = 0x00,
+ DCMD_TIMEOUT = 0x01,
+ DCMD_FAILED = 0x02,
+ DCMD_BUSY = 0x03,
+ DCMD_INIT = 0xff,
+};
+
+u8
+MR_BuildRaidContext(struct megasas_instance *instance,
+ struct IO_REQUEST_INFO *io_info,
+ struct RAID_CONTEXT *pRAID_Context,
+ struct MR_DRV_RAID_MAP_ALL *map, u8 **raidLUN);
+u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_DRV_RAID_MAP_ALL *map);
+struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_DRV_RAID_MAP_ALL *map);
+u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_DRV_RAID_MAP_ALL *map);
+u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map);
+__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map);
+u16 MR_GetLDTgtId(u32 ld, struct MR_DRV_RAID_MAP_ALL *map);
+
+__le16 get_updated_dev_handle(struct megasas_instance *instance,
+ struct LD_LOAD_BALANCE_INFO *lbInfo,
+ struct IO_REQUEST_INFO *in_info,
+ struct MR_DRV_RAID_MAP_ALL *drv_map);
+void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map,
+ struct LD_LOAD_BALANCE_INFO *lbInfo);
+int megasas_get_ctrl_info(struct megasas_instance *instance);
+/* PD sequence */
+int
+megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend);
+void megasas_set_dynamic_target_properties(struct scsi_device *sdev,
+ struct queue_limits *lim, bool is_target_prop);
+int megasas_get_target_prop(struct megasas_instance *instance,
+ struct scsi_device *sdev);
+void megasas_get_snapdump_properties(struct megasas_instance *instance);
+
+int megasas_set_crash_dump_params(struct megasas_instance *instance,
+ u8 crash_buf_state);
+void megasas_free_host_crash_buffer(struct megasas_instance *instance);
+
+void megasas_return_cmd_fusion(struct megasas_instance *instance,
+ struct megasas_cmd_fusion *cmd);
+int megasas_issue_blocked_cmd(struct megasas_instance *instance,
+ struct megasas_cmd *cmd, int timeout);
+void __megasas_return_cmd(struct megasas_instance *instance,
+ struct megasas_cmd *cmd);
+
+void megasas_return_mfi_mpt_pthr(struct megasas_instance *instance,
+ struct megasas_cmd *cmd_mfi, struct megasas_cmd_fusion *cmd_fusion);
+int megasas_cmd_type(struct scsi_cmnd *cmd);
+void megasas_setup_jbod_map(struct megasas_instance *instance);
+
+void megasas_update_sdev_properties(struct scsi_device *sdev);
+int megasas_reset_fusion(struct Scsi_Host *shost, int reason);
+int megasas_task_abort_fusion(struct scsi_cmnd *scmd);
+int megasas_reset_target_fusion(struct scsi_cmnd *scmd);
+u32 mega_mod64(u64 dividend, u32 divisor);
+int megasas_alloc_fusion_context(struct megasas_instance *instance);
+void megasas_free_fusion_context(struct megasas_instance *instance);
+int megasas_fusion_start_watchdog(struct megasas_instance *instance);
+void megasas_fusion_stop_watchdog(struct megasas_instance *instance);
+
+void megasas_set_dma_settings(struct megasas_instance *instance,
+ struct megasas_dcmd_frame *dcmd,
+ dma_addr_t dma_addr, u32 dma_len);
+int megasas_adp_reset_wait_for_ready(struct megasas_instance *instance,
+ bool do_adp_reset,
+ int ocr_context);
+int megasas_irqpoll(struct irq_poll *irqpoll, int budget);
+void megasas_dump_fusion_io(struct scsi_cmnd *scmd);
+u32 megasas_readl(struct megasas_instance *instance,
+ const volatile void __iomem *addr);
+struct megasas_cmd *megasas_get_cmd(struct megasas_instance *instance);
+void megasas_return_cmd(struct megasas_instance *instance,
+ struct megasas_cmd *cmd);
+int megasas_issue_polled(struct megasas_instance *instance,
+ struct megasas_cmd *cmd);
+void megaraid_sas_kill_hba(struct megasas_instance *instance);
+void megasas_check_and_restore_queue_depth(struct megasas_instance *instance);
+void megasas_start_timer(struct megasas_instance *instance);
+int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
+ int initial);
+int megasas_alloc_cmds(struct megasas_instance *instance);
+void megasas_free_cmds(struct megasas_instance *instance);
+
+void megasas_init_debugfs(void);
+void megasas_exit_debugfs(void);
+void megasas_setup_debugfs(struct megasas_instance *instance);
+void megasas_destroy_debugfs(struct megasas_instance *instance);
+int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num);
#endif /*LSI_MEGARAID_SAS_H */
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 2d8cdce7b2f5..abbbc4b36cd1 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1,35 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2011 LSI Corporation.
+ * Copyright (c) 2003-2013 LSI Corporation
+ * Copyright (c) 2013-2016 Avago Technologies
+ * Copyright (c) 2016-2018 Broadcom Inc.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * FILE: megaraid_sas_base.c
- * Version : v00.00.05.38-rc1
- *
- * Authors: LSI Corporation
+ * Authors: Broadcom Inc.
* Sreenivas Bagalkote
* Sumant Patro
* Bo Yang
- * Adam Radford <linuxraid@lsi.com>
- *
- * Send feedback to: <megaraidlinux@lsi.com>
+ * Adam Radford
+ * Kashyap Desai <kashyap.desai@broadcom.com>
+ * Sumit Saxena <sumit.saxena@broadcom.com>
*
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Send feedback to: megaraidlinux.pdl@broadcom.com
*/
#include <linux/kernel.h>
@@ -43,55 +28,125 @@
#include <linux/delay.h>
#include <linux/uio.h>
#include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/unaligned.h>
#include <linux/fs.h>
#include <linux/compat.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/irq_poll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
#include "megaraid_sas_fusion.h"
#include "megaraid_sas.h"
/*
- * poll_mode_io:1- schedule complete completion from q cmd
- */
-static unsigned int poll_mode_io;
-module_param_named(poll_mode_io, poll_mode_io, int, 0);
-MODULE_PARM_DESC(poll_mode_io,
- "Complete cmds from IO path, (default=0)");
-
-/*
* Number of sectors per IO command
* Will be set in megasas_init_mfi if user does not provide
*/
static unsigned int max_sectors;
-module_param_named(max_sectors, max_sectors, int, 0);
+module_param_named(max_sectors, max_sectors, int, 0444);
MODULE_PARM_DESC(max_sectors,
"Maximum number of sectors per IO command");
static int msix_disable;
-module_param(msix_disable, int, S_IRUGO);
+module_param(msix_disable, int, 0444);
MODULE_PARM_DESC(msix_disable, "Disable MSI-X interrupt handling. Default: 0");
+static unsigned int msix_vectors;
+module_param(msix_vectors, int, 0444);
+MODULE_PARM_DESC(msix_vectors, "MSI-X max vector count. Default: Set by FW");
+
+static int allow_vf_ioctls;
+module_param(allow_vf_ioctls, int, 0444);
+MODULE_PARM_DESC(allow_vf_ioctls, "Allow ioctls in SR-IOV VF mode. Default: 0");
+
+static unsigned int throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH;
+module_param(throttlequeuedepth, int, 0444);
+MODULE_PARM_DESC(throttlequeuedepth,
+ "Adapter queue depth when throttled due to I/O timeout. Default: 16");
+
+unsigned int resetwaittime = MEGASAS_RESET_WAIT_TIME;
+module_param(resetwaittime, int, 0444);
+MODULE_PARM_DESC(resetwaittime, "Wait time in (1-180s) after I/O timeout before resetting adapter. Default: 180s");
+
+static int smp_affinity_enable = 1;
+module_param(smp_affinity_enable, int, 0444);
+MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disable Default: enable(1)");
+
+static int rdpq_enable = 1;
+module_param(rdpq_enable, int, 0444);
+MODULE_PARM_DESC(rdpq_enable, "Allocate reply queue in chunks for large queue depth enable/disable Default: enable(1)");
+
+unsigned int dual_qdepth_disable;
+module_param(dual_qdepth_disable, int, 0444);
+MODULE_PARM_DESC(dual_qdepth_disable, "Disable dual queue depth feature. Default: 0");
+
+static unsigned int scmd_timeout = MEGASAS_DEFAULT_CMD_TIMEOUT;
+module_param(scmd_timeout, int, 0444);
+MODULE_PARM_DESC(scmd_timeout, "scsi command timeout (10-90s), default 90s. See megasas_reset_timer.");
+
+static int perf_mode = -1;
+module_param(perf_mode, int, 0444);
+MODULE_PARM_DESC(perf_mode, "Performance mode (only for Aero adapters), options:\n\t\t"
+ "0 - balanced: High iops and low latency queues are allocated &\n\t\t"
+ "interrupt coalescing is enabled only on high iops queues\n\t\t"
+ "1 - iops: High iops queues are not allocated &\n\t\t"
+ "interrupt coalescing is enabled on all queues\n\t\t"
+ "2 - latency: High iops queues are not allocated &\n\t\t"
+ "interrupt coalescing is disabled on all queues\n\t\t"
+ "default mode is 'balanced'"
+ );
+
+static int event_log_level = MFI_EVT_CLASS_CRITICAL;
+module_param(event_log_level, int, 0644);
+MODULE_PARM_DESC(event_log_level, "Asynchronous event logging level- range is: -2(CLASS_DEBUG) to 4(CLASS_DEAD), Default: 2(CLASS_CRITICAL)");
+
+static unsigned int enable_sdev_max_qd;
+module_param(enable_sdev_max_qd, int, 0444);
+MODULE_PARM_DESC(enable_sdev_max_qd, "Enable sdev max qd as can_queue. Default: 0");
+
+static int poll_queues;
+module_param(poll_queues, int, 0444);
+MODULE_PARM_DESC(poll_queues, "Number of queues to be use for io_uring poll mode.\n\t\t"
+ "This parameter is effective only if host_tagset_enable=1 &\n\t\t"
+ "It is not applicable for MFI_SERIES. &\n\t\t"
+ "Driver will work in latency mode. &\n\t\t"
+ "High iops queues are not allocated &\n\t\t"
+ );
+
+static int host_tagset_enable = 1;
+module_param(host_tagset_enable, int, 0444);
+MODULE_PARM_DESC(host_tagset_enable, "Shared host tagset enable/disable Default: enable(1)");
+
MODULE_LICENSE("GPL");
MODULE_VERSION(MEGASAS_VERSION);
-MODULE_AUTHOR("megaraidlinux@lsi.com");
-MODULE_DESCRIPTION("LSI MegaRAID SAS Driver");
+MODULE_AUTHOR("megaraidlinux.pdl@broadcom.com");
+MODULE_DESCRIPTION("Broadcom MegaRAID SAS Driver");
-int megasas_transition_to_ready(struct megasas_instance *instance);
+int megasas_transition_to_ready(struct megasas_instance *instance, int ocr);
static int megasas_get_pd_list(struct megasas_instance *instance);
+static int megasas_ld_list_query(struct megasas_instance *instance,
+ u8 query_type);
static int megasas_issue_init_mfi(struct megasas_instance *instance);
static int megasas_register_aen(struct megasas_instance *instance,
u32 seq_num, u32 class_locale_word);
+static void megasas_get_pd_info(struct megasas_instance *instance,
+ struct scsi_device *sdev);
+static void
+megasas_set_ld_removed_by_fw(struct megasas_instance *instance);
+
/*
* PCI ID table for all supported controllers
*/
-static struct pci_device_id megasas_pci_table[] = {
+static const struct pci_device_id megasas_pci_table[] = {
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)},
/* xscale IOP */
@@ -113,13 +168,40 @@ static struct pci_device_id megasas_pci_table[] = {
/* xscale IOP */
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FUSION)},
/* Fusion */
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_PLASMA)},
+ /* Plasma */
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INVADER)},
+ /* Invader */
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FURY)},
+ /* Fury */
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INTRUDER)},
+ /* Intruder */
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INTRUDER_24)},
+ /* Intruder 24 port*/
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CUTLASS_52)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CUTLASS_53)},
+ /* VENTURA */
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VENTURA)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CRUSADER)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_HARPOON)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_TOMCAT)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VENTURA_4PORT)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CRUSADER_4PORT)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E1)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E2)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E5)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E6)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E0)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E3)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E4)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E7)},
{}
};
MODULE_DEVICE_TABLE(pci, megasas_pci_table);
static int megasas_mgmt_majorno;
-static struct megasas_mgmt_info megasas_mgmt_info;
+struct megasas_mgmt_info megasas_mgmt_info;
static struct fasync_struct *megasas_async_queue;
static DEFINE_MUTEX(megasas_async_queue_mutex);
@@ -128,15 +210,20 @@ static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait);
static u32 support_poll_for_event;
u32 megasas_dbg_lvl;
static u32 support_device_change;
+static bool support_nvme_encapsulation;
+static bool support_pci_lane_margining;
/* define lock for aen poll */
-spinlock_t poll_aen_lock;
+static DEFINE_SPINLOCK(poll_aen_lock);
+
+extern struct dentry *megasas_debugfs_root;
+extern int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num);
void
megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
u8 alt_status);
static u32
-megasas_read_fw_status_reg_gen2(struct megasas_register_set __iomem *regs);
+megasas_read_fw_status_reg_gen2(struct megasas_instance *instance);
static int
megasas_adp_reset_gen2(struct megasas_instance *instance,
struct megasas_register_set __iomem *reg_set);
@@ -147,29 +234,78 @@ u32
megasas_build_and_issue_cmd(struct megasas_instance *instance,
struct scsi_cmnd *scmd);
static void megasas_complete_cmd_dpc(unsigned long instance_addr);
-void
-megasas_release_fusion(struct megasas_instance *instance);
int
-megasas_ioc_init_fusion(struct megasas_instance *instance);
-void
-megasas_free_cmds_fusion(struct megasas_instance *instance);
-u8
-megasas_get_map_info(struct megasas_instance *instance);
-int
-megasas_sync_map_info(struct megasas_instance *instance);
-int
-wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd);
-void megasas_reset_reply_desc(struct megasas_instance *instance);
-u8 MR_ValidateMapInfo(struct MR_FW_RAID_MAP_ALL *map,
- struct LD_LOAD_BALANCE_INFO *lbInfo);
-int megasas_reset_fusion(struct Scsi_Host *shost);
+wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
+ int seconds);
void megasas_fusion_ocr_wq(struct work_struct *work);
+static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance,
+ int initial);
+static int
+megasas_set_dma_mask(struct megasas_instance *instance);
+static int
+megasas_alloc_ctrl_mem(struct megasas_instance *instance);
+static inline void
+megasas_free_ctrl_mem(struct megasas_instance *instance);
+static inline int
+megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance);
+static inline void
+megasas_free_ctrl_dma_buffers(struct megasas_instance *instance);
+static inline void
+megasas_init_ctrl_params(struct megasas_instance *instance);
-void
+u32 megasas_readl(struct megasas_instance *instance,
+ const volatile void __iomem *addr)
+{
+ u32 i = 0, ret_val;
+ /*
+ * Due to a HW errata in Aero controllers, reads to certain
+ * Fusion registers could intermittently return all zeroes.
+ * This behavior is transient in nature and subsequent reads will
+ * return valid value. As a workaround in driver, retry readl for
+ * up to thirty times until a non-zero value is read.
+ */
+ if (instance->adapter_type == AERO_SERIES) {
+ do {
+ ret_val = readl(addr);
+ i++;
+ } while (ret_val == 0 && i < 30);
+ return ret_val;
+ } else {
+ return readl(addr);
+ }
+}
+
+/**
+ * megasas_set_dma_settings - Populate DMA address, length and flags for DCMDs
+ * @instance: Adapter soft state
+ * @dcmd: DCMD frame inside MFI command
+ * @dma_addr: DMA address of buffer to be passed to FW
+ * @dma_len: Length of DMA buffer to be passed to FW
+ * @return: void
+ */
+void megasas_set_dma_settings(struct megasas_instance *instance,
+ struct megasas_dcmd_frame *dcmd,
+ dma_addr_t dma_addr, u32 dma_len)
+{
+ if (instance->consistent_mask_64bit) {
+ dcmd->sgl.sge64[0].phys_addr = cpu_to_le64(dma_addr);
+ dcmd->sgl.sge64[0].length = cpu_to_le32(dma_len);
+ dcmd->flags = cpu_to_le16(dcmd->flags | MFI_FRAME_SGL64);
+
+ } else {
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(lower_32_bits(dma_addr));
+ dcmd->sgl.sge32[0].length = cpu_to_le32(dma_len);
+ dcmd->flags = cpu_to_le16(dcmd->flags);
+ }
+}
+
+static void
megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr, 0, instance->reg_set);
+ return;
}
/**
@@ -184,17 +320,17 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance
unsigned long flags;
struct megasas_cmd *cmd = NULL;
- spin_lock_irqsave(&instance->cmd_pool_lock, flags);
+ spin_lock_irqsave(&instance->mfi_pool_lock, flags);
if (!list_empty(&instance->cmd_pool)) {
cmd = list_entry((&instance->cmd_pool)->next,
struct megasas_cmd, list);
list_del_init(&cmd->list);
} else {
- printk(KERN_ERR "megasas: Command pool empty!\n");
+ dev_err(&instance->pdev->dev, "Command pool empty!\n");
}
- spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
+ spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
return cmd;
}
@@ -203,33 +339,127 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance
* @instance: Adapter soft state
* @cmd: Command packet to be returned to free command pool
*/
-inline void
+void
megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
unsigned long flags;
+ u32 blk_tags;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct fusion_context *fusion = instance->ctrl_context;
- spin_lock_irqsave(&instance->cmd_pool_lock, flags);
+ /* This flag is used only for fusion adapter.
+ * Wait for Interrupt for Polled mode DCMD
+ */
+ if (cmd->flags & DRV_DCMD_POLLED_MODE)
+ return;
+
+ spin_lock_irqsave(&instance->mfi_pool_lock, flags);
+ if (fusion) {
+ blk_tags = instance->max_scsi_cmds + cmd->index;
+ cmd_fusion = fusion->cmd_list[blk_tags];
+ megasas_return_cmd_fusion(instance, cmd_fusion);
+ }
cmd->scmd = NULL;
cmd->frame_count = 0;
- list_add_tail(&cmd->list, &instance->cmd_pool);
+ cmd->flags = 0;
+ memset(cmd->frame, 0, instance->mfi_frame_size);
+ cmd->frame->io.context = cpu_to_le32(cmd->index);
+ if (!fusion && reset_devices)
+ cmd->frame->hdr.cmd = MFI_CMD_INVALID;
+ list_add(&cmd->list, (&instance->cmd_pool)->next);
+
+ spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
+
+}
+
+static const char *
+format_timestamp(uint32_t timestamp)
+{
+ static char buffer[32];
- spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
+ if ((timestamp & 0xff000000) == 0xff000000)
+ snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
+ 0x00ffffff);
+ else
+ snprintf(buffer, sizeof(buffer), "%us", timestamp);
+ return buffer;
}
+static const char *
+format_class(int8_t class)
+{
+ static char buffer[6];
+
+ switch (class) {
+ case MFI_EVT_CLASS_DEBUG:
+ return "debug";
+ case MFI_EVT_CLASS_PROGRESS:
+ return "progress";
+ case MFI_EVT_CLASS_INFO:
+ return "info";
+ case MFI_EVT_CLASS_WARNING:
+ return "WARN";
+ case MFI_EVT_CLASS_CRITICAL:
+ return "CRIT";
+ case MFI_EVT_CLASS_FATAL:
+ return "FATAL";
+ case MFI_EVT_CLASS_DEAD:
+ return "DEAD";
+ default:
+ snprintf(buffer, sizeof(buffer), "%d", class);
+ return buffer;
+ }
+}
/**
-* The following functions are defined for xscale
-* (deviceid : 1064R, PERC5) controllers
-*/
+ * megasas_decode_evt: Decode FW AEN event and print critical event
+ * for information.
+ * @instance: Adapter soft state
+ */
+static void
+megasas_decode_evt(struct megasas_instance *instance)
+{
+ struct megasas_evt_detail *evt_detail = instance->evt_detail;
+ union megasas_evt_class_locale class_locale;
+ class_locale.word = le32_to_cpu(evt_detail->cl.word);
+
+ if ((event_log_level < MFI_EVT_CLASS_DEBUG) ||
+ (event_log_level > MFI_EVT_CLASS_DEAD)) {
+ printk(KERN_WARNING "megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n");
+ event_log_level = MFI_EVT_CLASS_CRITICAL;
+ }
+
+ if (class_locale.members.class >= event_log_level)
+ dev_info(&instance->pdev->dev, "%d (%s/0x%04x/%s) - %s\n",
+ le32_to_cpu(evt_detail->seq_num),
+ format_timestamp(le32_to_cpu(evt_detail->time_stamp)),
+ (class_locale.members.locale),
+ format_class(class_locale.members.class),
+ evt_detail->description);
+
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "evt_detail.args.ld.target_id/index %d/%d\n",
+ evt_detail->args.ld.target_id, evt_detail->args.ld.ld_index);
+
+}
+
+/*
+ * The following functions are defined for xscale
+ * (deviceid : 1064R, PERC5) controllers
+ */
/**
* megasas_enable_intr_xscale - Enables interrupts
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_enable_intr_xscale(struct megasas_register_set __iomem * regs)
+megasas_enable_intr_xscale(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
+
+ regs = instance->reg_set;
writel(0, &(regs)->outbound_intr_mask);
/* Dummy readl to force pci flush */
@@ -238,12 +468,15 @@ megasas_enable_intr_xscale(struct megasas_register_set __iomem * regs)
/**
* megasas_disable_intr_xscale -Disables interrupt
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_disable_intr_xscale(struct megasas_register_set __iomem * regs)
+megasas_disable_intr_xscale(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
u32 mask = 0x1f;
+
+ regs = instance->reg_set;
writel(mask, &regs->outbound_intr_mask);
/* Dummy readl to force pci flush */
readl(&regs->outbound_intr_mask);
@@ -251,22 +484,25 @@ megasas_disable_intr_xscale(struct megasas_register_set __iomem * regs)
/**
* megasas_read_fw_status_reg_xscale - returns the current FW status value
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static u32
-megasas_read_fw_status_reg_xscale(struct megasas_register_set __iomem * regs)
+megasas_read_fw_status_reg_xscale(struct megasas_instance *instance)
{
- return readl(&(regs)->outbound_msg_0);
+ return readl(&instance->reg_set->outbound_msg_0);
}
/**
- * megasas_clear_interrupt_xscale - Check & clear interrupt
- * @regs: MFI register set
+ * megasas_clear_intr_xscale - Check & clear interrupt
+ * @instance: Adapter soft state
*/
static int
-megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs)
+megasas_clear_intr_xscale(struct megasas_instance *instance)
{
u32 status;
u32 mfiStatus = 0;
+ struct megasas_register_set __iomem *regs;
+ regs = instance->reg_set;
+
/*
* Check if it is our interrupt
*/
@@ -291,9 +527,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs)
/**
* megasas_fire_cmd_xscale - Sends command to the FW
- * @frame_phys_addr : Physical address of cmd
- * @frame_count : Number of frames for the command
- * @regs : MFI register set
+ * @instance: Adapter soft state
+ * @frame_phys_addr : Physical address of cmd
+ * @frame_count : Number of frames for the command
+ * @regs : MFI register set
*/
static inline void
megasas_fire_cmd_xscale(struct megasas_instance *instance,
@@ -302,6 +539,7 @@ megasas_fire_cmd_xscale(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
unsigned long flags;
+
spin_lock_irqsave(&instance->hba_lock, flags);
writel((frame_phys_addr >> 3)|(frame_count),
&(regs)->inbound_queue_port);
@@ -310,7 +548,8 @@ megasas_fire_cmd_xscale(struct megasas_instance *instance,
/**
* megasas_adp_reset_xscale - For controller reset
- * @regs: MFI register set
+ * @instance: Adapter soft state
+ * @regs: MFI register set
*/
static int
megasas_adp_reset_xscale(struct megasas_instance *instance,
@@ -318,15 +557,16 @@ megasas_adp_reset_xscale(struct megasas_instance *instance,
{
u32 i;
u32 pcidata;
+
writel(MFI_ADP_RESET, &regs->inbound_doorbell);
for (i = 0; i < 3; i++)
msleep(1000); /* sleep for 3 secs */
pcidata = 0;
pci_read_config_dword(instance->pdev, MFI_1068_PCSR_OFFSET, &pcidata);
- printk(KERN_NOTICE "pcidata = %x\n", pcidata);
+ dev_notice(&instance->pdev->dev, "pcidata = %x\n", pcidata);
if (pcidata & 0x2) {
- printk(KERN_NOTICE "mfi 1068 offset read=%x\n", pcidata);
+ dev_notice(&instance->pdev->dev, "mfi 1068 offset read=%x\n", pcidata);
pcidata &= ~0x2;
pci_write_config_dword(instance->pdev,
MFI_1068_PCSR_OFFSET, pcidata);
@@ -337,9 +577,9 @@ megasas_adp_reset_xscale(struct megasas_instance *instance,
pcidata = 0;
pci_read_config_dword(instance->pdev,
MFI_1068_FW_HANDSHAKE_OFFSET, &pcidata);
- printk(KERN_NOTICE "1068 offset handshake read=%x\n", pcidata);
+ dev_notice(&instance->pdev->dev, "1068 offset handshake read=%x\n", pcidata);
if ((pcidata & 0xffff0000) == MFI_1068_FW_READY) {
- printk(KERN_NOTICE "1068 offset pcidt=%x\n", pcidata);
+ dev_notice(&instance->pdev->dev, "1068 offset pcidt=%x\n", pcidata);
pcidata = 0;
pci_write_config_dword(instance->pdev,
MFI_1068_FW_HANDSHAKE_OFFSET, pcidata);
@@ -350,19 +590,17 @@ megasas_adp_reset_xscale(struct megasas_instance *instance,
/**
* megasas_check_reset_xscale - For controller reset check
- * @regs: MFI register set
+ * @instance: Adapter soft state
+ * @regs: MFI register set
*/
static int
megasas_check_reset_xscale(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
- u32 consumer;
- consumer = *instance->consumer;
-
- if ((instance->adprecovery != MEGASAS_HBA_OPERATIONAL) &&
- (*instance->consumer == MEGASAS_ADPRESET_INPROG_SIGN)) {
+ if ((atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) &&
+ (le32_to_cpu(*instance->consumer) ==
+ MEGASAS_ADPRESET_INPROG_SIGN))
return 1;
- }
return 0;
}
@@ -382,23 +620,26 @@ static struct megasas_instance_template megasas_instance_template_xscale = {
.issue_dcmd = megasas_issue_dcmd,
};
-/**
-* This is the end of set of functions & definitions specific
-* to xscale (deviceid : 1064R, PERC5) controllers
-*/
+/*
+ * This is the end of set of functions & definitions specific
+ * to xscale (deviceid : 1064R, PERC5) controllers
+ */
-/**
-* The following functions are defined for ppc (deviceid : 0x60)
-* controllers
-*/
+/*
+ * The following functions are defined for ppc (deviceid : 0x60)
+ * controllers
+ */
/**
* megasas_enable_intr_ppc - Enables interrupts
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_enable_intr_ppc(struct megasas_register_set __iomem * regs)
+megasas_enable_intr_ppc(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
+
+ regs = instance->reg_set;
writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear);
writel(~0x80000000, &(regs)->outbound_intr_mask);
@@ -409,12 +650,15 @@ megasas_enable_intr_ppc(struct megasas_register_set __iomem * regs)
/**
* megasas_disable_intr_ppc - Disable interrupt
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_disable_intr_ppc(struct megasas_register_set __iomem * regs)
+megasas_disable_intr_ppc(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
u32 mask = 0xFFFFFFFF;
+
+ regs = instance->reg_set;
writel(mask, &regs->outbound_intr_mask);
/* Dummy readl to force pci flush */
readl(&regs->outbound_intr_mask);
@@ -422,22 +666,24 @@ megasas_disable_intr_ppc(struct megasas_register_set __iomem * regs)
/**
* megasas_read_fw_status_reg_ppc - returns the current FW status value
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static u32
-megasas_read_fw_status_reg_ppc(struct megasas_register_set __iomem * regs)
+megasas_read_fw_status_reg_ppc(struct megasas_instance *instance)
{
- return readl(&(regs)->outbound_scratch_pad);
+ return readl(&instance->reg_set->outbound_scratch_pad_0);
}
/**
- * megasas_clear_interrupt_ppc - Check & clear interrupt
- * @regs: MFI register set
+ * megasas_clear_intr_ppc - Check & clear interrupt
+ * @instance: Adapter soft state
*/
static int
-megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs)
+megasas_clear_intr_ppc(struct megasas_instance *instance)
{
u32 status, mfiStatus = 0;
+ struct megasas_register_set __iomem *regs;
+ regs = instance->reg_set;
/*
* Check if it is our interrupt
@@ -463,9 +709,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs)
/**
* megasas_fire_cmd_ppc - Sends command to the FW
- * @frame_phys_addr : Physical address of cmd
- * @frame_count : Number of frames for the command
- * @regs : MFI register set
+ * @instance: Adapter soft state
+ * @frame_phys_addr: Physical address of cmd
+ * @frame_count: Number of frames for the command
+ * @regs: MFI register set
*/
static inline void
megasas_fire_cmd_ppc(struct megasas_instance *instance,
@@ -474,6 +721,7 @@ megasas_fire_cmd_ppc(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
unsigned long flags;
+
spin_lock_irqsave(&instance->hba_lock, flags);
writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_queue_port);
@@ -482,13 +730,14 @@ megasas_fire_cmd_ppc(struct megasas_instance *instance,
/**
* megasas_check_reset_ppc - For controller reset check
- * @regs: MFI register set
+ * @instance: Adapter soft state
+ * @regs: MFI register set
*/
static int
megasas_check_reset_ppc(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
- if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL)
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL)
return 1;
return 0;
@@ -512,11 +761,14 @@ static struct megasas_instance_template megasas_instance_template_ppc = {
/**
* megasas_enable_intr_skinny - Enables interrupts
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_enable_intr_skinny(struct megasas_register_set __iomem *regs)
+megasas_enable_intr_skinny(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
+
+ regs = instance->reg_set;
writel(0xFFFFFFFF, &(regs)->outbound_intr_mask);
writel(~MFI_SKINNY_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask);
@@ -527,12 +779,15 @@ megasas_enable_intr_skinny(struct megasas_register_set __iomem *regs)
/**
* megasas_disable_intr_skinny - Disables interrupt
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_disable_intr_skinny(struct megasas_register_set __iomem *regs)
+megasas_disable_intr_skinny(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
u32 mask = 0xFFFFFFFF;
+
+ regs = instance->reg_set;
writel(mask, &regs->outbound_intr_mask);
/* Dummy readl to force pci flush */
readl(&regs->outbound_intr_mask);
@@ -540,23 +795,25 @@ megasas_disable_intr_skinny(struct megasas_register_set __iomem *regs)
/**
* megasas_read_fw_status_reg_skinny - returns the current FW status value
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static u32
-megasas_read_fw_status_reg_skinny(struct megasas_register_set __iomem *regs)
+megasas_read_fw_status_reg_skinny(struct megasas_instance *instance)
{
- return readl(&(regs)->outbound_scratch_pad);
+ return readl(&instance->reg_set->outbound_scratch_pad_0);
}
/**
- * megasas_clear_interrupt_skinny - Check & clear interrupt
- * @regs: MFI register set
+ * megasas_clear_intr_skinny - Check & clear interrupt
+ * @instance: Adapter soft state
*/
static int
-megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
+megasas_clear_intr_skinny(struct megasas_instance *instance)
{
u32 status;
u32 mfiStatus = 0;
+ struct megasas_register_set __iomem *regs;
+ regs = instance->reg_set;
/*
* Check if it is our interrupt
@@ -570,7 +827,7 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
/*
* Check if it is our interrupt
*/
- if ((megasas_read_fw_status_reg_gen2(regs) & MFI_STATE_MASK) ==
+ if ((megasas_read_fw_status_reg_skinny(instance) & MFI_STATE_MASK) ==
MFI_STATE_FAULT) {
mfiStatus = MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE;
} else
@@ -582,8 +839,8 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
writel(status, &regs->outbound_intr_status);
/*
- * dummy read to flush PCI
- */
+ * dummy read to flush PCI
+ */
readl(&regs->outbound_intr_status);
return mfiStatus;
@@ -591,9 +848,10 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
/**
* megasas_fire_cmd_skinny - Sends command to the FW
- * @frame_phys_addr : Physical address of cmd
- * @frame_count : Number of frames for the command
- * @regs : MFI register set
+ * @instance: Adapter soft state
+ * @frame_phys_addr: Physical address of cmd
+ * @frame_count: Number of frames for the command
+ * @regs: MFI register set
*/
static inline void
megasas_fire_cmd_skinny(struct megasas_instance *instance,
@@ -602,22 +860,25 @@ megasas_fire_cmd_skinny(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
unsigned long flags;
+
spin_lock_irqsave(&instance->hba_lock, flags);
- writel(0, &(regs)->inbound_high_queue_port);
- writel((frame_phys_addr | (frame_count<<1))|1,
- &(regs)->inbound_low_queue_port);
+ writel(upper_32_bits(frame_phys_addr),
+ &(regs)->inbound_high_queue_port);
+ writel((lower_32_bits(frame_phys_addr) | (frame_count<<1))|1,
+ &(regs)->inbound_low_queue_port);
spin_unlock_irqrestore(&instance->hba_lock, flags);
}
/**
* megasas_check_reset_skinny - For controller reset check
- * @regs: MFI register set
+ * @instance: Adapter soft state
+ * @regs: MFI register set
*/
static int
megasas_check_reset_skinny(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
- if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL)
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL)
return 1;
return 0;
@@ -640,18 +901,21 @@ static struct megasas_instance_template megasas_instance_template_skinny = {
};
-/**
-* The following functions are defined for gen2 (deviceid : 0x78 0x79)
-* controllers
-*/
+/*
+ * The following functions are defined for gen2 (deviceid : 0x78 0x79)
+ * controllers
+ */
/**
* megasas_enable_intr_gen2 - Enables interrupts
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_enable_intr_gen2(struct megasas_register_set __iomem *regs)
+megasas_enable_intr_gen2(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
+
+ regs = instance->reg_set;
writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear);
/* write ~0x00000005 (4 & 1) to the intr mask*/
@@ -663,12 +927,15 @@ megasas_enable_intr_gen2(struct megasas_register_set __iomem *regs)
/**
* megasas_disable_intr_gen2 - Disables interrupt
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static inline void
-megasas_disable_intr_gen2(struct megasas_register_set __iomem *regs)
+megasas_disable_intr_gen2(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
u32 mask = 0xFFFFFFFF;
+
+ regs = instance->reg_set;
writel(mask, &regs->outbound_intr_mask);
/* Dummy readl to force pci flush */
readl(&regs->outbound_intr_mask);
@@ -676,29 +943,32 @@ megasas_disable_intr_gen2(struct megasas_register_set __iomem *regs)
/**
* megasas_read_fw_status_reg_gen2 - returns the current FW status value
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static u32
-megasas_read_fw_status_reg_gen2(struct megasas_register_set __iomem *regs)
+megasas_read_fw_status_reg_gen2(struct megasas_instance *instance)
{
- return readl(&(regs)->outbound_scratch_pad);
+ return readl(&instance->reg_set->outbound_scratch_pad_0);
}
/**
- * megasas_clear_interrupt_gen2 - Check & clear interrupt
- * @regs: MFI register set
+ * megasas_clear_intr_gen2 - Check & clear interrupt
+ * @instance: Adapter soft state
*/
static int
-megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs)
+megasas_clear_intr_gen2(struct megasas_instance *instance)
{
u32 status;
u32 mfiStatus = 0;
+ struct megasas_register_set __iomem *regs;
+ regs = instance->reg_set;
+
/*
* Check if it is our interrupt
*/
status = readl(&regs->outbound_intr_status);
- if (status & MFI_GEN2_ENABLE_INTERRUPT_MASK) {
+ if (status & MFI_INTR_FLAG_REPLY_MESSAGE) {
mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE;
}
if (status & MFI_G2_OUTBOUND_DOORBELL_CHANGE_INTERRUPT) {
@@ -716,11 +986,13 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs)
return mfiStatus;
}
+
/**
* megasas_fire_cmd_gen2 - Sends command to the FW
- * @frame_phys_addr : Physical address of cmd
- * @frame_count : Number of frames for the command
- * @regs : MFI register set
+ * @instance: Adapter soft state
+ * @frame_phys_addr: Physical address of cmd
+ * @frame_count: Number of frames for the command
+ * @regs: MFI register set
*/
static inline void
megasas_fire_cmd_gen2(struct megasas_instance *instance,
@@ -729,6 +1001,7 @@ megasas_fire_cmd_gen2(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
unsigned long flags;
+
spin_lock_irqsave(&instance->hba_lock, flags);
writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_queue_port);
@@ -737,16 +1010,17 @@ megasas_fire_cmd_gen2(struct megasas_instance *instance,
/**
* megasas_adp_reset_gen2 - For controller reset
- * @regs: MFI register set
+ * @instance: Adapter soft state
+ * @reg_set: MFI register set
*/
static int
megasas_adp_reset_gen2(struct megasas_instance *instance,
struct megasas_register_set __iomem *reg_set)
{
- u32 retry = 0 ;
- u32 HostDiag;
- u32 *seq_offset = &reg_set->seq_offset;
- u32 *hostdiag_offset = &reg_set->host_diag;
+ u32 retry = 0 ;
+ u32 HostDiag;
+ u32 __iomem *seq_offset = &reg_set->seq_offset;
+ u32 __iomem *hostdiag_offset = &reg_set->host_diag;
if (instance->instancet == &megasas_instance_template_skinny) {
seq_offset = &reg_set->fusion_seq_offset;
@@ -764,10 +1038,10 @@ megasas_adp_reset_gen2(struct megasas_instance *instance,
HostDiag = (u32)readl(hostdiag_offset);
- while ( !( HostDiag & DIAG_WRITE_ENABLE) ) {
+ while (!(HostDiag & DIAG_WRITE_ENABLE)) {
msleep(100);
HostDiag = (u32)readl(hostdiag_offset);
- printk(KERN_NOTICE "RESETGEN2: retry=%x, hostdiag=%x\n",
+ dev_notice(&instance->pdev->dev, "RESETGEN2: retry=%x, hostdiag=%x\n",
retry, HostDiag);
if (retry++ >= 100)
@@ -775,17 +1049,17 @@ megasas_adp_reset_gen2(struct megasas_instance *instance,
}
- printk(KERN_NOTICE "ADP_RESET_GEN2: HostDiag=%x\n", HostDiag);
+ dev_notice(&instance->pdev->dev, "ADP_RESET_GEN2: HostDiag=%x\n", HostDiag);
writel((HostDiag | DIAG_RESET_ADAPTER), hostdiag_offset);
ssleep(10);
HostDiag = (u32)readl(hostdiag_offset);
- while ( ( HostDiag & DIAG_RESET_ADAPTER) ) {
+ while (HostDiag & DIAG_RESET_ADAPTER) {
msleep(100);
HostDiag = (u32)readl(hostdiag_offset);
- printk(KERN_NOTICE "RESET_GEN2: retry=%x, hostdiag=%x\n",
+ dev_notice(&instance->pdev->dev, "RESET_GEN2: retry=%x, hostdiag=%x\n",
retry, HostDiag);
if (retry++ >= 1000)
@@ -797,15 +1071,15 @@ megasas_adp_reset_gen2(struct megasas_instance *instance,
/**
* megasas_check_reset_gen2 - For controller reset check
- * @regs: MFI register set
+ * @instance: Adapter soft state
+ * @regs: MFI register set
*/
static int
megasas_check_reset_gen2(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
- if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) {
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL)
return 1;
- }
return 0;
}
@@ -826,10 +1100,10 @@ static struct megasas_instance_template megasas_instance_template_gen2 = {
.issue_dcmd = megasas_issue_dcmd,
};
-/**
-* This is the end of set of functions & definitions
-* specific to gen2 (deviceid : 0x78, 0x79) controllers
-*/
+/*
+ * This is the end of set of functions & definitions
+ * specific to gen2 (deviceid : 0x78, 0x79) controllers
+ */
/*
* Template added for TB (Fusion)
@@ -841,66 +1115,88 @@ extern struct megasas_instance_template megasas_instance_template_fusion;
* @instance: Adapter soft state
* @cmd: Command packet to be issued
*
- * For polling, MFI requires the cmd_status to be set to 0xFF before posting.
+ * For polling, MFI requires the cmd_status to be set to MFI_STAT_INVALID_STATUS before posting.
*/
int
megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
-
struct megasas_header *frame_hdr = &cmd->frame->hdr;
- frame_hdr->cmd_status = 0xFF;
- frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
+ frame_hdr->cmd_status = MFI_STAT_INVALID_STATUS;
+ frame_hdr->flags |= cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE);
+
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ return DCMD_INIT;
+ }
- /*
- * Issue the frame using inbound queue port
- */
instance->instancet->issue_dcmd(instance, cmd);
- /*
- * Wait for cmd_status to change
- */
- return wait_and_poll(instance, cmd);
+ return wait_and_poll(instance, cmd, instance->requestorId ?
+ MEGASAS_ROUTINE_WAIT_TIME_VF : MFI_IO_TIMEOUT_SECS);
}
/**
* megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds
* @instance: Adapter soft state
* @cmd: Command to be issued
+ * @timeout: Timeout in seconds
*
* This function waits on an event for the command to be returned from ISR.
* Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs
* Used to issue ioctl commands.
*/
-static int
+int
megasas_issue_blocked_cmd(struct megasas_instance *instance,
- struct megasas_cmd *cmd)
+ struct megasas_cmd *cmd, int timeout)
{
- cmd->cmd_status = ENODATA;
+ int ret = 0;
+ cmd->cmd_status_drv = DCMD_INIT;
+
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ return DCMD_INIT;
+ }
instance->instancet->issue_dcmd(instance, cmd);
- wait_event(instance->int_cmd_wait_q, cmd->cmd_status != ENODATA);
+ if (timeout) {
+ ret = wait_event_timeout(instance->int_cmd_wait_q,
+ cmd->cmd_status_drv != DCMD_INIT, timeout * HZ);
+ if (!ret) {
+ dev_err(&instance->pdev->dev,
+ "DCMD(opcode: 0x%x) is timed out, func:%s\n",
+ cmd->frame->dcmd.opcode, __func__);
+ return DCMD_TIMEOUT;
+ }
+ } else
+ wait_event(instance->int_cmd_wait_q,
+ cmd->cmd_status_drv != DCMD_INIT);
- return 0;
+ return cmd->cmd_status_drv;
}
/**
* megasas_issue_blocked_abort_cmd - Aborts previously issued cmd
* @instance: Adapter soft state
* @cmd_to_abort: Previously issued cmd to be aborted
+ * @timeout: Timeout in seconds
*
- * MFI firmware can abort previously issued AEN command (automatic event
+ * MFI firmware can abort previously issued AEN comamnd (automatic event
* notification). The megasas_issue_blocked_abort_cmd() issues such abort
* cmd and waits for return status.
* Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs
*/
static int
megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
- struct megasas_cmd *cmd_to_abort)
+ struct megasas_cmd *cmd_to_abort, int timeout)
{
struct megasas_cmd *cmd;
struct megasas_abort_frame *abort_fr;
+ int ret = 0;
+ u32 opcode;
cmd = megasas_get_cmd(instance);
@@ -913,25 +1209,43 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
* Prepare and issue the abort frame
*/
abort_fr->cmd = MFI_CMD_ABORT;
- abort_fr->cmd_status = 0xFF;
- abort_fr->flags = 0;
- abort_fr->abort_context = cmd_to_abort->index;
- abort_fr->abort_mfi_phys_addr_lo = cmd_to_abort->frame_phys_addr;
- abort_fr->abort_mfi_phys_addr_hi = 0;
+ abort_fr->cmd_status = MFI_STAT_INVALID_STATUS;
+ abort_fr->flags = cpu_to_le16(0);
+ abort_fr->abort_context = cpu_to_le32(cmd_to_abort->index);
+ abort_fr->abort_mfi_phys_addr_lo =
+ cpu_to_le32(lower_32_bits(cmd_to_abort->frame_phys_addr));
+ abort_fr->abort_mfi_phys_addr_hi =
+ cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr));
cmd->sync_cmd = 1;
- cmd->cmd_status = 0xFF;
+ cmd->cmd_status_drv = DCMD_INIT;
+
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ return DCMD_INIT;
+ }
instance->instancet->issue_dcmd(instance, cmd);
- /*
- * Wait for this cmd to complete
- */
- wait_event(instance->abort_cmd_wait_q, cmd->cmd_status != 0xFF);
+ if (timeout) {
+ ret = wait_event_timeout(instance->abort_cmd_wait_q,
+ cmd->cmd_status_drv != DCMD_INIT, timeout * HZ);
+ if (!ret) {
+ opcode = cmd_to_abort->frame->dcmd.opcode;
+ dev_err(&instance->pdev->dev,
+ "Abort(to be aborted DCMD opcode: 0x%x) is timed out func:%s\n",
+ opcode, __func__);
+ return DCMD_TIMEOUT;
+ }
+ } else
+ wait_event(instance->abort_cmd_wait_q,
+ cmd->cmd_status_drv != DCMD_INIT);
+
cmd->sync_cmd = 0;
megasas_return_cmd(instance, cmd);
- return 0;
+ return cmd->cmd_status_drv;
}
/**
@@ -956,8 +1270,8 @@ megasas_make_sgl32(struct megasas_instance *instance, struct scsi_cmnd *scp,
if (sge_count) {
scsi_for_each_sg(scp, os_sgl, sge_count, i) {
- mfi_sgl->sge32[i].length = sg_dma_len(os_sgl);
- mfi_sgl->sge32[i].phys_addr = sg_dma_address(os_sgl);
+ mfi_sgl->sge32[i].length = cpu_to_le32(sg_dma_len(os_sgl));
+ mfi_sgl->sge32[i].phys_addr = cpu_to_le32(sg_dma_address(os_sgl));
}
}
return sge_count;
@@ -985,8 +1299,8 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
if (sge_count) {
scsi_for_each_sg(scp, os_sgl, sge_count, i) {
- mfi_sgl->sge64[i].length = sg_dma_len(os_sgl);
- mfi_sgl->sge64[i].phys_addr = sg_dma_address(os_sgl);
+ mfi_sgl->sge64[i].length = cpu_to_le32(sg_dma_len(os_sgl));
+ mfi_sgl->sge64[i].phys_addr = cpu_to_le64(sg_dma_address(os_sgl));
}
}
return sge_count;
@@ -1013,10 +1327,11 @@ megasas_make_sgl_skinny(struct megasas_instance *instance,
if (sge_count) {
scsi_for_each_sg(scp, os_sgl, sge_count, i) {
- mfi_sgl->sge_skinny[i].length = sg_dma_len(os_sgl);
+ mfi_sgl->sge_skinny[i].length =
+ cpu_to_le32(sg_dma_len(os_sgl));
mfi_sgl->sge_skinny[i].phys_addr =
- sg_dma_address(os_sgl);
- mfi_sgl->sge_skinny[i].flag = 0;
+ cpu_to_le64(sg_dma_address(os_sgl));
+ mfi_sgl->sge_skinny[i].flag = cpu_to_le32(0);
}
}
return sge_count;
@@ -1036,7 +1351,7 @@ static u32 megasas_get_frame_count(struct megasas_instance *instance,
int num_cnt;
int sge_bytes;
u32 sge_sz;
- u32 frame_count=0;
+ u32 frame_count = 0;
sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
sizeof(struct megasas_sge32);
@@ -1067,14 +1382,14 @@ static u32 megasas_get_frame_count(struct megasas_instance *instance,
num_cnt = sge_count - 3;
}
- if(num_cnt>0){
+ if (num_cnt > 0) {
sge_bytes = sge_sz * num_cnt;
frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) ;
}
/* Main frame */
- frame_count +=1;
+ frame_count += 1;
if (frame_count > 7)
frame_count = 8;
@@ -1099,15 +1414,15 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
u16 flags = 0;
struct megasas_pthru_frame *pthru;
- is_logical = MEGASAS_IS_LOGICAL(scp);
- device_id = MEGASAS_DEV_INDEX(instance, scp);
+ is_logical = MEGASAS_IS_LOGICAL(scp->device);
+ device_id = MEGASAS_DEV_INDEX(scp);
pthru = (struct megasas_pthru_frame *)cmd->frame;
- if (scp->sc_data_direction == PCI_DMA_TODEVICE)
+ if (scp->sc_data_direction == DMA_TO_DEVICE)
flags = MFI_FRAME_DIR_WRITE;
- else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
+ else if (scp->sc_data_direction == DMA_FROM_DEVICE)
flags = MFI_FRAME_DIR_READ;
- else if (scp->sc_data_direction == PCI_DMA_NONE)
+ else if (scp->sc_data_direction == DMA_NONE)
flags = MFI_FRAME_DIR_NONE;
if (instance->flag_ieee == 1) {
@@ -1125,31 +1440,31 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
pthru->cdb_len = scp->cmd_len;
pthru->timeout = 0;
pthru->pad_0 = 0;
- pthru->flags = flags;
- pthru->data_xfer_len = scsi_bufflen(scp);
+ pthru->flags = cpu_to_le16(flags);
+ pthru->data_xfer_len = cpu_to_le32(scsi_bufflen(scp));
memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
/*
- * If the command is for the tape device, set the
- * pthru timeout to the os layer timeout value.
- */
+ * If the command is for the tape device, set the
+ * pthru timeout to the os layer timeout value.
+ */
if (scp->device->type == TYPE_TAPE) {
- if ((scp->request->timeout / HZ) > 0xFFFF)
- pthru->timeout = 0xFFFF;
+ if (scsi_cmd_to_rq(scp)->timeout / HZ > 0xFFFF)
+ pthru->timeout = cpu_to_le16(0xFFFF);
else
- pthru->timeout = scp->request->timeout / HZ;
+ pthru->timeout = cpu_to_le16(scsi_cmd_to_rq(scp)->timeout / HZ);
}
/*
* Construct SGL
*/
if (instance->flag_ieee == 1) {
- pthru->flags |= MFI_FRAME_SGL64;
+ pthru->flags |= cpu_to_le16(MFI_FRAME_SGL64);
pthru->sge_count = megasas_make_sgl_skinny(instance, scp,
&pthru->sgl);
} else if (IS_DMA64) {
- pthru->flags |= MFI_FRAME_SGL64;
+ pthru->flags |= cpu_to_le16(MFI_FRAME_SGL64);
pthru->sge_count = megasas_make_sgl64(instance, scp,
&pthru->sgl);
} else
@@ -1157,7 +1472,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
&pthru->sgl);
if (pthru->sge_count > instance->max_num_sge) {
- printk(KERN_ERR "megasas: DCDB two many SGE NUM=%x\n",
+ dev_err(&instance->pdev->dev, "DCDB too many SGE NUM=%x\n",
pthru->sge_count);
return 0;
}
@@ -1166,8 +1481,10 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Sense info specific
*/
pthru->sense_len = SCSI_SENSE_BUFFERSIZE;
- pthru->sense_buf_phys_addr_hi = 0;
- pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
+ pthru->sense_buf_phys_addr_hi =
+ cpu_to_le32(upper_32_bits(cmd->sense_phys_addr));
+ pthru->sense_buf_phys_addr_lo =
+ cpu_to_le32(lower_32_bits(cmd->sense_phys_addr));
/*
* Compute the total number of frames this command consumes. FW uses
@@ -1196,12 +1513,12 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
u16 flags = 0;
struct megasas_io_frame *ldio;
- device_id = MEGASAS_DEV_INDEX(instance, scp);
+ device_id = MEGASAS_DEV_INDEX(scp);
ldio = (struct megasas_io_frame *)cmd->frame;
- if (scp->sc_data_direction == PCI_DMA_TODEVICE)
+ if (scp->sc_data_direction == DMA_TO_DEVICE)
flags = MFI_FRAME_DIR_WRITE;
- else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
+ else if (scp->sc_data_direction == DMA_FROM_DEVICE)
flags = MFI_FRAME_DIR_READ;
if (instance->flag_ieee == 1) {
@@ -1218,7 +1535,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
ldio->timeout = 0;
ldio->reserved_0 = 0;
ldio->pad_0 = 0;
- ldio->flags = flags;
+ ldio->flags = cpu_to_le16(flags);
ldio->start_lba_hi = 0;
ldio->access_byte = (scp->cmd_len != 6) ? scp->cmnd[1] : 0;
@@ -1226,52 +1543,59 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
* 6-byte READ(0x08) or WRITE(0x0A) cdb
*/
if (scp->cmd_len == 6) {
- ldio->lba_count = (u32) scp->cmnd[4];
- ldio->start_lba_lo = ((u32) scp->cmnd[1] << 16) |
- ((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3];
+ ldio->lba_count = cpu_to_le32((u32) scp->cmnd[4]);
+ ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[1] << 16) |
+ ((u32) scp->cmnd[2] << 8) |
+ (u32) scp->cmnd[3]);
- ldio->start_lba_lo &= 0x1FFFFF;
+ ldio->start_lba_lo &= cpu_to_le32(0x1FFFFF);
}
/*
* 10-byte READ(0x28) or WRITE(0x2A) cdb
*/
else if (scp->cmd_len == 10) {
- ldio->lba_count = (u32) scp->cmnd[8] |
- ((u32) scp->cmnd[7] << 8);
- ldio->start_lba_lo = ((u32) scp->cmnd[2] << 24) |
- ((u32) scp->cmnd[3] << 16) |
- ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];
+ ldio->lba_count = cpu_to_le32((u32) scp->cmnd[8] |
+ ((u32) scp->cmnd[7] << 8));
+ ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[2] << 24) |
+ ((u32) scp->cmnd[3] << 16) |
+ ((u32) scp->cmnd[4] << 8) |
+ (u32) scp->cmnd[5]);
}
/*
* 12-byte READ(0xA8) or WRITE(0xAA) cdb
*/
else if (scp->cmd_len == 12) {
- ldio->lba_count = ((u32) scp->cmnd[6] << 24) |
- ((u32) scp->cmnd[7] << 16) |
- ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9];
+ ldio->lba_count = cpu_to_le32(((u32) scp->cmnd[6] << 24) |
+ ((u32) scp->cmnd[7] << 16) |
+ ((u32) scp->cmnd[8] << 8) |
+ (u32) scp->cmnd[9]);
- ldio->start_lba_lo = ((u32) scp->cmnd[2] << 24) |
- ((u32) scp->cmnd[3] << 16) |
- ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];
+ ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[2] << 24) |
+ ((u32) scp->cmnd[3] << 16) |
+ ((u32) scp->cmnd[4] << 8) |
+ (u32) scp->cmnd[5]);
}
/*
* 16-byte READ(0x88) or WRITE(0x8A) cdb
*/
else if (scp->cmd_len == 16) {
- ldio->lba_count = ((u32) scp->cmnd[10] << 24) |
- ((u32) scp->cmnd[11] << 16) |
- ((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13];
+ ldio->lba_count = cpu_to_le32(((u32) scp->cmnd[10] << 24) |
+ ((u32) scp->cmnd[11] << 16) |
+ ((u32) scp->cmnd[12] << 8) |
+ (u32) scp->cmnd[13]);
- ldio->start_lba_lo = ((u32) scp->cmnd[6] << 24) |
- ((u32) scp->cmnd[7] << 16) |
- ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9];
+ ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[6] << 24) |
+ ((u32) scp->cmnd[7] << 16) |
+ ((u32) scp->cmnd[8] << 8) |
+ (u32) scp->cmnd[9]);
- ldio->start_lba_hi = ((u32) scp->cmnd[2] << 24) |
- ((u32) scp->cmnd[3] << 16) |
- ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];
+ ldio->start_lba_hi = cpu_to_le32(((u32) scp->cmnd[2] << 24) |
+ ((u32) scp->cmnd[3] << 16) |
+ ((u32) scp->cmnd[4] << 8) |
+ (u32) scp->cmnd[5]);
}
@@ -1279,17 +1603,17 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Construct SGL
*/
if (instance->flag_ieee) {
- ldio->flags |= MFI_FRAME_SGL64;
+ ldio->flags |= cpu_to_le16(MFI_FRAME_SGL64);
ldio->sge_count = megasas_make_sgl_skinny(instance, scp,
&ldio->sgl);
} else if (IS_DMA64) {
- ldio->flags |= MFI_FRAME_SGL64;
+ ldio->flags |= cpu_to_le16(MFI_FRAME_SGL64);
ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl);
} else
ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl);
if (ldio->sge_count > instance->max_num_sge) {
- printk(KERN_ERR "megasas: build_ld_io: sge_count = %x\n",
+ dev_err(&instance->pdev->dev, "build_ld_io: sge_count = %x\n",
ldio->sge_count);
return 0;
}
@@ -1299,7 +1623,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
*/
ldio->sense_len = SCSI_SENSE_BUFFERSIZE;
ldio->sense_buf_phys_addr_hi = 0;
- ldio->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
+ ldio->sense_buf_phys_addr_lo = cpu_to_le32(cmd->sense_phys_addr);
/*
* Compute the total number of frames this command consumes. FW uses
@@ -1312,16 +1636,15 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
}
/**
- * megasas_is_ldio - Checks if the cmd is for logical drive
- * @scmd: SCSI command
+ * megasas_cmd_type - Checks if the cmd is for logical drive/sysPD
+ * and whether it's RW or non RW
+ * @cmd: SCSI command
*
- * Called by megasas_queue_command to find out if the command to be queued
- * is a logical drive command
*/
-inline int megasas_is_ldio(struct scsi_cmnd *cmd)
+inline int megasas_cmd_type(struct scsi_cmnd *cmd)
{
- if (!MEGASAS_IS_LOGICAL(cmd))
- return 0;
+ int ret;
+
switch (cmd->cmnd[0]) {
case READ_10:
case WRITE_10:
@@ -1331,15 +1654,19 @@ inline int megasas_is_ldio(struct scsi_cmnd *cmd)
case WRITE_6:
case READ_16:
case WRITE_16:
- return 1;
+ ret = (MEGASAS_IS_LOGICAL(cmd->device)) ?
+ READ_WRITE_LDIO : READ_WRITE_SYSPDIO;
+ break;
default:
- return 0;
+ ret = (MEGASAS_IS_LOGICAL(cmd->device)) ?
+ NON_READ_WRITE_LDIO : NON_READ_WRITE_SYSPDIO;
}
+ return ret;
}
/**
* megasas_dump_pending_frames - Dumps the frame address of all pending cmds
- * in FW
+ * in FW
* @instance: Adapter soft state
*/
static inline void
@@ -1351,53 +1678,62 @@ megasas_dump_pending_frames(struct megasas_instance *instance)
struct megasas_io_frame *ldio;
struct megasas_pthru_frame *pthru;
u32 sgcount;
- u32 max_cmd = instance->max_fw_cmds;
+ u16 max_cmd = instance->max_fw_cmds;
- printk(KERN_ERR "\nmegasas[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no);
- printk(KERN_ERR "megasas[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding));
+ dev_err(&instance->pdev->dev, "[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no);
+ dev_err(&instance->pdev->dev, "[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding));
if (IS_DMA64)
- printk(KERN_ERR "\nmegasas[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no);
+ dev_err(&instance->pdev->dev, "[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no);
else
- printk(KERN_ERR "\nmegasas[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no);
+ dev_err(&instance->pdev->dev, "[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no);
- printk(KERN_ERR "megasas[%d]: Pending OS cmds in FW : \n",instance->host->host_no);
+ dev_err(&instance->pdev->dev, "[%d]: Pending OS cmds in FW : \n",instance->host->host_no);
for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i];
- if(!cmd->scmd)
+ if (!cmd->scmd)
continue;
- printk(KERN_ERR "megasas[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr);
- if (megasas_is_ldio(cmd->scmd)){
+ dev_err(&instance->pdev->dev, "[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr);
+ if (megasas_cmd_type(cmd->scmd) == READ_WRITE_LDIO) {
ldio = (struct megasas_io_frame *)cmd->frame;
mfi_sgl = &ldio->sgl;
sgcount = ldio->sge_count;
- printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no, cmd->frame_count,ldio->cmd,ldio->target_id, ldio->start_lba_lo,ldio->start_lba_hi,ldio->sense_buf_phys_addr_lo,sgcount);
- }
- else {
+ dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x,"
+ " lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",
+ instance->host->host_no, cmd->frame_count, ldio->cmd, ldio->target_id,
+ le32_to_cpu(ldio->start_lba_lo), le32_to_cpu(ldio->start_lba_hi),
+ le32_to_cpu(ldio->sense_buf_phys_addr_lo), sgcount);
+ } else {
pthru = (struct megasas_pthru_frame *) cmd->frame;
mfi_sgl = &pthru->sgl;
sgcount = pthru->sge_count;
- printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no,cmd->frame_count,pthru->cmd,pthru->target_id,pthru->lun,pthru->cdb_len , pthru->data_xfer_len,pthru->sense_buf_phys_addr_lo,sgcount);
+ dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, "
+ "lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",
+ instance->host->host_no, cmd->frame_count, pthru->cmd, pthru->target_id,
+ pthru->lun, pthru->cdb_len, le32_to_cpu(pthru->data_xfer_len),
+ le32_to_cpu(pthru->sense_buf_phys_addr_lo), sgcount);
}
- if(megasas_dbg_lvl & MEGASAS_DBG_LVL){
- for (n = 0; n < sgcount; n++){
- if (IS_DMA64)
- printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%08lx ",mfi_sgl->sge64[n].length , (unsigned long)mfi_sgl->sge64[n].phys_addr) ;
- else
- printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%x ",mfi_sgl->sge32[n].length , mfi_sgl->sge32[n].phys_addr) ;
+ if (megasas_dbg_lvl & MEGASAS_DBG_LVL) {
+ for (n = 0; n < sgcount; n++) {
+ if (IS_DMA64)
+ dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%llx\n",
+ le32_to_cpu(mfi_sgl->sge64[n].length),
+ le64_to_cpu(mfi_sgl->sge64[n].phys_addr));
+ else
+ dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%x\n",
+ le32_to_cpu(mfi_sgl->sge32[n].length),
+ le32_to_cpu(mfi_sgl->sge32[n].phys_addr));
}
}
- printk(KERN_ERR "\n");
} /*for max_cmd*/
- printk(KERN_ERR "\nmegasas[%d]: Pending Internal cmds in FW : \n",instance->host->host_no);
+ dev_err(&instance->pdev->dev, "[%d]: Pending Internal cmds in FW : \n",instance->host->host_no);
for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i];
- if(cmd->sync_cmd == 1){
- printk(KERN_ERR "0x%08lx : ", (unsigned long)cmd->frame_phys_addr);
- }
+ if (cmd->sync_cmd == 1)
+ dev_err(&instance->pdev->dev, "0x%08lx : ", (unsigned long)cmd->frame_phys_addr);
}
- printk(KERN_ERR "megasas[%d]: Dumping Done.\n\n",instance->host->host_no);
+ dev_err(&instance->pdev->dev, "[%d]: Dumping Done\n\n",instance->host->host_no);
}
u32
@@ -1414,7 +1750,7 @@ megasas_build_and_issue_cmd(struct megasas_instance *instance,
/*
* Logical drive command
*/
- if (megasas_is_ldio(scmd))
+ if (megasas_cmd_type(scmd) == READ_WRITE_LDIO)
frame_count = megasas_build_ldio(instance, scmd, cmd);
else
frame_count = megasas_build_dcdb(instance, scmd, cmd);
@@ -1423,7 +1759,7 @@ megasas_build_and_issue_cmd(struct megasas_instance *instance,
goto out_return_cmd;
cmd->scmd = scmd;
- scmd->SCp.ptr = (char *)cmd;
+ megasas_priv(scmd)->cmd_priv = cmd;
/*
* Issue the command to the FW
@@ -1432,79 +1768,98 @@ megasas_build_and_issue_cmd(struct megasas_instance *instance,
instance->instancet->fire_cmd(instance, cmd->frame_phys_addr,
cmd->frame_count-1, instance->reg_set);
- /*
- * Check if we have pend cmds to be completed
- */
- if (poll_mode_io && atomic_read(&instance->fw_outstanding))
- tasklet_schedule(&instance->isr_tasklet);
return 0;
out_return_cmd:
megasas_return_cmd(instance, cmd);
- return 1;
+ return SCSI_MLQUEUE_HOST_BUSY;
}
/**
* megasas_queue_command - Queue entry point
+ * @shost: adapter SCSI host
* @scmd: SCSI command to be queued
- * @done: Callback entry point
*/
static int
-megasas_queue_command_lck(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
+megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
{
struct megasas_instance *instance;
- unsigned long flags;
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+ u32 ld_tgt_id;
instance = (struct megasas_instance *)
scmd->device->host->hostdata;
+ if (instance->unload == 1) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ }
+
if (instance->issuepend_done == 0)
return SCSI_MLQUEUE_HOST_BUSY;
- spin_lock_irqsave(&instance->hba_lock, flags);
- if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) {
- spin_unlock_irqrestore(&instance->hba_lock, flags);
- return SCSI_MLQUEUE_HOST_BUSY;
+
+ /* Check for an mpio path and adjust behavior */
+ if (atomic_read(&instance->adprecovery) == MEGASAS_ADPRESET_SM_INFAULT) {
+ if (megasas_check_mpio_paths(instance, scmd) ==
+ (DID_REQUEUE << 16)) {
+ return SCSI_MLQUEUE_HOST_BUSY;
+ } else {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ }
}
- spin_unlock_irqrestore(&instance->hba_lock, flags);
+ mr_device_priv_data = scmd->device->hostdata;
+ if (!mr_device_priv_data ||
+ (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ }
+
+ if (MEGASAS_IS_LOGICAL(scmd->device)) {
+ ld_tgt_id = MEGASAS_TARGET_ID(scmd->device);
+ if (instance->ld_tgtid_status[ld_tgt_id] == LD_TARGET_ID_DELETED) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ }
+ }
+
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ if (mr_device_priv_data->tm_busy)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
- scmd->scsi_done = done;
scmd->result = 0;
- if (MEGASAS_IS_LOGICAL(scmd) &&
- (scmd->device->id >= MEGASAS_MAX_LD || scmd->device->lun)) {
+ if (MEGASAS_IS_LOGICAL(scmd->device) &&
+ (scmd->device->id >= instance->fw_supported_vd_count ||
+ scmd->device->lun)) {
scmd->result = DID_BAD_TARGET << 16;
goto out_done;
}
- switch (scmd->cmnd[0]) {
- case SYNCHRONIZE_CACHE:
- /*
- * FW takes care of flush cache on its own
- * No need to send it down
- */
+ if ((scmd->cmnd[0] == SYNCHRONIZE_CACHE) &&
+ MEGASAS_IS_LOGICAL(scmd->device) &&
+ (!instance->fw_sync_cache_support)) {
scmd->result = DID_OK << 16;
goto out_done;
- default:
- break;
}
- if (instance->instancet->build_and_issue_cmd(instance, scmd)) {
- printk(KERN_ERR "megasas: Err returned from build_and_issue_cmd\n");
- return SCSI_MLQUEUE_HOST_BUSY;
- }
-
- return 0;
+ return instance->instancet->build_and_issue_cmd(instance, scmd);
out_done:
- done(scmd);
+ scsi_done(scmd);
return 0;
}
-static DEF_SCSI_QCMD(megasas_queue_command)
-
static struct megasas_instance *megasas_lookup_instance(u16 host_no)
{
int i;
@@ -1519,74 +1874,378 @@ static struct megasas_instance *megasas_lookup_instance(u16 host_no)
return NULL;
}
-static int megasas_slave_configure(struct scsi_device *sdev)
+/*
+* megasas_set_dynamic_target_properties -
+* Device property set by driver may not be static and it is required to be
+* updated after OCR
+*
+* set tm_capable.
+* set dma alignment (only for eedp protection enable vd).
+*
+* @sdev: OS provided scsi device
+*
+* Returns void
+*/
+void megasas_set_dynamic_target_properties(struct scsi_device *sdev,
+ struct queue_limits *lim, bool is_target_prop)
{
- u16 pd_index = 0;
- struct megasas_instance *instance ;
+ u16 pd_index = 0, ld;
+ u32 device_id;
+ struct megasas_instance *instance;
+ struct fusion_context *fusion;
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+ struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync;
+ struct MR_LD_RAID *raid;
+ struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
instance = megasas_lookup_instance(sdev->host->host_no);
+ fusion = instance->ctrl_context;
+ mr_device_priv_data = sdev->hostdata;
- /*
- * Don't export physical disk devices to the disk driver.
- *
- * FIXME: Currently we don't export them to the midlayer at all.
- * That will be fixed once LSI engineers have audited the
- * firmware for possible issues.
- */
- if (sdev->channel < MEGASAS_MAX_PD_CHANNELS &&
- sdev->type == TYPE_DISK) {
- pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
- sdev->id;
- if (instance->pd_list[pd_index].driveState ==
- MR_PD_STATE_SYSTEM) {
- blk_queue_rq_timeout(sdev->request_queue,
- MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
- return 0;
+ if (!fusion || !mr_device_priv_data)
+ return;
+
+ if (MEGASAS_IS_LOGICAL(sdev)) {
+ device_id = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL)
+ + sdev->id;
+ local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
+ ld = MR_TargetIdToLdGet(device_id, local_map_ptr);
+ if (ld >= instance->fw_supported_vd_count)
+ return;
+ raid = MR_LdRaidGet(ld, local_map_ptr);
+
+ if (raid->capability.ldPiMode == MR_PROT_INFO_TYPE_CONTROLLER) {
+ if (lim)
+ lim->dma_alignment = 0x7;
}
- return -ENXIO;
+
+ mr_device_priv_data->is_tm_capable =
+ raid->capability.tmCapable;
+
+ if (!raid->flags.isEPD)
+ sdev->no_write_same = 1;
+
+ } else if (instance->use_seqnum_jbod_fp) {
+ pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+ sdev->id;
+ pd_sync = (void *)fusion->pd_seq_sync
+ [(instance->pd_seq_map_id - 1) & 1];
+ mr_device_priv_data->is_tm_capable =
+ pd_sync->seq[pd_index].capability.tmCapable;
+ }
+
+ if (is_target_prop && instance->tgt_prop->reset_tmo) {
+ /*
+ * If FW provides a target reset timeout value, driver will use
+ * it. If not set, fallback to default values.
+ */
+ mr_device_priv_data->target_reset_tmo =
+ min_t(u8, instance->max_reset_tmo,
+ instance->tgt_prop->reset_tmo);
+ mr_device_priv_data->task_abort_tmo = instance->task_abort_tmo;
+ } else {
+ mr_device_priv_data->target_reset_tmo =
+ MEGASAS_DEFAULT_TM_TIMEOUT;
+ mr_device_priv_data->task_abort_tmo =
+ MEGASAS_DEFAULT_TM_TIMEOUT;
}
+}
+
+/*
+ * megasas_set_nvme_device_properties -
+ * set nomerges=2
+ * set virtual page boundary = 4K (current mr_nvme_pg_size is 4K).
+ * set maximum io transfer = MDTS of NVME device provided by MR firmware.
+ *
+ * MR firmware provides value in KB. Caller of this function converts
+ * kb into bytes.
+ *
+ * e.a MDTS=5 means 2^5 * nvme page size. (In case of 4K page size,
+ * MR firmware provides value 128 as (32 * 4K) = 128K.
+ *
+ * @sdev: scsi device
+ * @max_io_size: maximum io transfer size
+ *
+ */
+static inline void
+megasas_set_nvme_device_properties(struct scsi_device *sdev,
+ struct queue_limits *lim, u32 max_io_size)
+{
+ struct megasas_instance *instance;
+ u32 mr_nvme_pg_size;
+
+ instance = (struct megasas_instance *)sdev->host->hostdata;
+ mr_nvme_pg_size = max_t(u32, instance->nvme_page_size,
+ MR_DEFAULT_NVME_PAGE_SIZE);
+
+ lim->max_hw_sectors = max_io_size / 512;
+ lim->virt_boundary_mask = mr_nvme_pg_size - 1;
+}
+
+/*
+ * megasas_set_fw_assisted_qd -
+ * set device queue depth to can_queue
+ * set device queue depth to fw assisted qd
+ *
+ * @sdev: scsi device
+ * @is_target_prop true, if fw provided target properties.
+ */
+static void megasas_set_fw_assisted_qd(struct scsi_device *sdev,
+ bool is_target_prop)
+{
+ u8 interface_type;
+ u32 device_qd = MEGASAS_DEFAULT_CMD_PER_LUN;
+ u32 tgt_device_qd;
+ struct megasas_instance *instance;
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+
+ instance = megasas_lookup_instance(sdev->host->host_no);
+ mr_device_priv_data = sdev->hostdata;
+ interface_type = mr_device_priv_data->interface_type;
+
+ switch (interface_type) {
+ case SAS_PD:
+ device_qd = MEGASAS_SAS_QD;
+ break;
+ case SATA_PD:
+ device_qd = MEGASAS_SATA_QD;
+ break;
+ case NVME_PD:
+ device_qd = MEGASAS_NVME_QD;
+ break;
+ }
+
+ if (is_target_prop) {
+ tgt_device_qd = le32_to_cpu(instance->tgt_prop->device_qdepth);
+ if (tgt_device_qd)
+ device_qd = min(instance->host->can_queue,
+ (int)tgt_device_qd);
+ }
+
+ if (instance->enable_sdev_max_qd && interface_type != UNKNOWN_DRIVE)
+ device_qd = instance->host->can_queue;
+
+ scsi_change_queue_depth(sdev, device_qd);
+}
+
+/*
+ * megasas_set_static_target_properties -
+ * Device property set by driver are static and it is not required to be
+ * updated after OCR.
+ *
+ * set io timeout
+ * set device queue depth
+ * set nvme device properties. see - megasas_set_nvme_device_properties
+ *
+ * @sdev: scsi device
+ * @is_target_prop true, if fw provided target properties.
+ */
+static void megasas_set_static_target_properties(struct scsi_device *sdev,
+ struct queue_limits *lim, bool is_target_prop)
+{
+ u32 max_io_size_kb = MR_DEFAULT_NVME_MDTS_KB;
+ struct megasas_instance *instance;
+
+ instance = megasas_lookup_instance(sdev->host->host_no);
/*
- * The RAID firmware may require extended timeouts.
- */
- blk_queue_rq_timeout(sdev->request_queue,
- MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+ * The RAID firmware may require extended timeouts.
+ */
+ blk_queue_rq_timeout(sdev->request_queue, scmd_timeout * HZ);
+
+ /* max_io_size_kb will be set to non zero for
+ * nvme based vd and syspd.
+ */
+ if (is_target_prop)
+ max_io_size_kb = le32_to_cpu(instance->tgt_prop->max_io_size_kb);
+
+ if (instance->nvme_page_size && max_io_size_kb)
+ megasas_set_nvme_device_properties(sdev, lim,
+ max_io_size_kb << 10);
+
+ megasas_set_fw_assisted_qd(sdev, is_target_prop);
+}
+
+
+static int megasas_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ u16 pd_index = 0;
+ struct megasas_instance *instance;
+ int ret_target_prop = DCMD_FAILED;
+ bool is_target_prop = false;
+
+ instance = megasas_lookup_instance(sdev->host->host_no);
+ if (instance->pd_list_not_supported) {
+ if (!MEGASAS_IS_LOGICAL(sdev) && sdev->type == TYPE_DISK) {
+ pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+ sdev->id;
+ if (instance->pd_list[pd_index].driveState !=
+ MR_PD_STATE_SYSTEM)
+ return -ENXIO;
+ }
+ }
+
+ mutex_lock(&instance->reset_mutex);
+ /* Send DCMD to Firmware and cache the information */
+ if ((instance->pd_info) && !MEGASAS_IS_LOGICAL(sdev))
+ megasas_get_pd_info(instance, sdev);
+
+ /* Some ventura firmware may not have instance->nvme_page_size set.
+ * Do not send MR_DCMD_DRV_GET_TARGET_PROP
+ */
+ if ((instance->tgt_prop) && (instance->nvme_page_size))
+ ret_target_prop = megasas_get_target_prop(instance, sdev);
+
+ is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false;
+ megasas_set_static_target_properties(sdev, lim, is_target_prop);
+
+ /* This sdev property may change post OCR */
+ megasas_set_dynamic_target_properties(sdev, lim, is_target_prop);
+
+ if (!MEGASAS_IS_LOGICAL(sdev))
+ sdev->no_vpd_size = 1;
+
+ mutex_unlock(&instance->reset_mutex);
+
return 0;
}
-static int megasas_slave_alloc(struct scsi_device *sdev)
+static int megasas_sdev_init(struct scsi_device *sdev)
{
- u16 pd_index = 0;
+ u16 pd_index = 0, ld_tgt_id;
struct megasas_instance *instance ;
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+
instance = megasas_lookup_instance(sdev->host->host_no);
- if ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) &&
- (sdev->type == TYPE_DISK)) {
+ if (!MEGASAS_IS_LOGICAL(sdev)) {
/*
* Open the OS scan to the SYSTEM PD
*/
pd_index =
(sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
sdev->id;
- if ((instance->pd_list[pd_index].driveState ==
- MR_PD_STATE_SYSTEM) &&
- (instance->pd_list[pd_index].driveType ==
- TYPE_DISK)) {
- return 0;
+ if ((instance->pd_list_not_supported ||
+ instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM)) {
+ goto scan_target;
}
return -ENXIO;
+ } else if (!MEGASAS_IS_LUN_VALID(sdev)) {
+ sdev_printk(KERN_INFO, sdev, "%s: invalid LUN\n", __func__);
+ return -ENXIO;
+ }
+
+scan_target:
+ mr_device_priv_data = kzalloc(sizeof(*mr_device_priv_data),
+ GFP_KERNEL);
+ if (!mr_device_priv_data)
+ return -ENOMEM;
+
+ if (MEGASAS_IS_LOGICAL(sdev)) {
+ ld_tgt_id = MEGASAS_TARGET_ID(sdev);
+ instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_ACTIVE;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ sdev_printk(KERN_INFO, sdev, "LD target ID %d created.\n", ld_tgt_id);
}
+
+ sdev->hostdata = mr_device_priv_data;
+
+ atomic_set(&mr_device_priv_data->r1_ldio_hint,
+ instance->r1_ldio_hint_default);
return 0;
}
+static void megasas_sdev_destroy(struct scsi_device *sdev)
+{
+ u16 ld_tgt_id;
+ struct megasas_instance *instance;
+
+ instance = megasas_lookup_instance(sdev->host->host_no);
+
+ if (MEGASAS_IS_LOGICAL(sdev)) {
+ if (!MEGASAS_IS_LUN_VALID(sdev)) {
+ sdev_printk(KERN_INFO, sdev, "%s: invalid LUN\n", __func__);
+ return;
+ }
+ ld_tgt_id = MEGASAS_TARGET_ID(sdev);
+ instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_DELETED;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ sdev_printk(KERN_INFO, sdev,
+ "LD target ID %d removed from OS stack\n", ld_tgt_id);
+ }
+
+ kfree(sdev->hostdata);
+ sdev->hostdata = NULL;
+}
+
+/*
+* megasas_complete_outstanding_ioctls - Complete outstanding ioctls after a
+* kill adapter
+* @instance: Adapter soft state
+*
+*/
+static void megasas_complete_outstanding_ioctls(struct megasas_instance *instance)
+{
+ int i;
+ struct megasas_cmd *cmd_mfi;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct fusion_context *fusion = instance->ctrl_context;
+
+ /* Find all outstanding ioctls */
+ if (fusion) {
+ for (i = 0; i < instance->max_fw_cmds; i++) {
+ cmd_fusion = fusion->cmd_list[i];
+ if (cmd_fusion->sync_cmd_idx != (u32)ULONG_MAX) {
+ cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
+ if (cmd_mfi->sync_cmd &&
+ (cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT)) {
+ cmd_mfi->frame->hdr.cmd_status =
+ MFI_STAT_WRONG_STATE;
+ megasas_complete_cmd(instance,
+ cmd_mfi, DID_OK);
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < instance->max_fw_cmds; i++) {
+ cmd_mfi = instance->cmd_list[i];
+ if (cmd_mfi->sync_cmd && cmd_mfi->frame->hdr.cmd !=
+ MFI_CMD_ABORT)
+ megasas_complete_cmd(instance, cmd_mfi, DID_OK);
+ }
+ }
+}
+
+
void megaraid_sas_kill_hba(struct megasas_instance *instance)
{
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_warn(&instance->pdev->dev,
+ "Adapter already dead, skipping kill HBA\n");
+ return;
+ }
+
+ /* Set critical error to block I/O & ioctls in case caller didn't */
+ atomic_set(&instance->adprecovery, MEGASAS_HW_CRITICAL_ERROR);
+ /* Wait 1 second to ensure IO or ioctls in build have posted */
+ msleep(1000);
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
- (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
- (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)) {
- writel(MFI_STOP_ADP, &instance->reg_set->doorbell);
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
+ (instance->adapter_type != MFI_SERIES)) {
+ if (!instance->requestorId) {
+ writel(MFI_STOP_ADP, &instance->reg_set->doorbell);
+ /* Flush */
+ readl(&instance->reg_set->doorbell);
+ }
+ if (instance->requestorId && instance->peerIsPresent)
+ memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
} else {
- writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell);
+ writel(MFI_STOP_ADP,
+ &instance->reg_set->inbound_doorbell);
}
+ /* Complete outstanding ioctls when adapter is killed */
+ megasas_complete_outstanding_ioctls(instance);
}
/**
@@ -1599,22 +2258,16 @@ void
megasas_check_and_restore_queue_depth(struct megasas_instance *instance)
{
unsigned long flags;
+
if (instance->flag & MEGASAS_FW_BUSY
- && time_after(jiffies, instance->last_time + 5 * HZ)
- && atomic_read(&instance->fw_outstanding) < 17) {
+ && time_after(jiffies, instance->last_time + 5 * HZ)
+ && atomic_read(&instance->fw_outstanding) <
+ instance->throttlequeuedepth + 1) {
spin_lock_irqsave(instance->host->host_lock, flags);
instance->flag &= ~MEGASAS_FW_BUSY;
- if ((instance->pdev->device ==
- PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
- (instance->pdev->device ==
- PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
- instance->host->can_queue =
- instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
- } else
- instance->host->can_queue =
- instance->max_fw_cmds - MEGASAS_INT_CMDS;
+ instance->host->can_queue = instance->cur_can_queue;
spin_unlock_irqrestore(instance->host->host_lock, flags);
}
}
@@ -1636,18 +2289,18 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr)
unsigned long flags;
/* If we have already declared adapter dead, donot complete cmds */
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR )
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)
return;
spin_lock_irqsave(&instance->completion_lock, flags);
- producer = *instance->producer;
- consumer = *instance->consumer;
+ producer = le32_to_cpu(*instance->producer);
+ consumer = le32_to_cpu(*instance->consumer);
while (consumer != producer) {
- context = instance->reply_queue[consumer];
+ context = le32_to_cpu(instance->reply_queue[consumer]);
if (context >= instance->max_fw_cmds) {
- printk(KERN_ERR "Unexpected context value %x\n",
+ dev_err(&instance->pdev->dev, "Unexpected context value %x\n",
context);
BUG();
}
@@ -1662,7 +2315,7 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr)
}
}
- *instance->consumer = producer;
+ *instance->consumer = cpu_to_le32(producer);
spin_unlock_irqrestore(&instance->completion_lock, flags);
@@ -1672,21 +2325,37 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr)
megasas_check_and_restore_queue_depth(instance);
}
+static void megasas_sriov_heartbeat_handler(struct timer_list *t);
+
+/**
+ * megasas_start_timer - Initializes sriov heartbeat timer object
+ * @instance: Adapter soft state
+ *
+ */
+void megasas_start_timer(struct megasas_instance *instance)
+{
+ struct timer_list *timer = &instance->sriov_heartbeat_timer;
+
+ timer_setup(timer, megasas_sriov_heartbeat_handler, 0);
+ timer->expires = jiffies + MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF;
+ add_timer(timer);
+}
+
static void
megasas_internal_reset_defer_cmds(struct megasas_instance *instance);
static void
process_fw_state_change_wq(struct work_struct *work);
-void megasas_do_ocr(struct megasas_instance *instance)
+static void megasas_do_ocr(struct megasas_instance *instance)
{
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) ||
(instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) ||
(instance->pdev->device == PCI_DEVICE_ID_LSI_VERDE_ZCR)) {
- *instance->consumer = MEGASAS_ADPRESET_INPROG_SIGN;
+ *instance->consumer = cpu_to_le32(MEGASAS_ADPRESET_INPROG_SIGN);
}
- instance->instancet->disable_intr(instance->reg_set);
- instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT;
+ instance->instancet->disable_intr(instance);
+ atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
instance->issuepend_done = 0;
atomic_set(&instance->fw_outstanding, 0);
@@ -1694,6 +2363,382 @@ void megasas_do_ocr(struct megasas_instance *instance)
process_fw_state_change_wq(&instance->work_init);
}
+static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance,
+ int initial)
+{
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_LD_VF_AFFILIATION_111 *new_affiliation_111 = NULL;
+ dma_addr_t new_affiliation_111_h;
+ int ld, retval = 0;
+ u8 thisVf;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation_111:"
+ "Failed to get cmd for scsi%d\n",
+ instance->host->host_no);
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ if (!instance->vf_affiliation_111) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF "
+ "affiliation for scsi%d\n", instance->host->host_no);
+ megasas_return_cmd(instance, cmd);
+ return -ENOMEM;
+ }
+
+ if (initial)
+ memset(instance->vf_affiliation_111, 0,
+ sizeof(struct MR_LD_VF_AFFILIATION_111));
+ else {
+ new_affiliation_111 =
+ dma_alloc_coherent(&instance->pdev->dev,
+ sizeof(struct MR_LD_VF_AFFILIATION_111),
+ &new_affiliation_111_h, GFP_KERNEL);
+ if (!new_affiliation_111) {
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate "
+ "memory for new affiliation for scsi%d\n",
+ instance->host->host_no);
+ megasas_return_cmd(instance, cmd);
+ return -ENOMEM;
+ }
+ }
+
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len =
+ cpu_to_le32(sizeof(struct MR_LD_VF_AFFILIATION_111));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111);
+
+ if (initial)
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(instance->vf_affiliation_111_h);
+ else
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(new_affiliation_111_h);
+
+ dcmd->sgl.sge32[0].length = cpu_to_le32(
+ sizeof(struct MR_LD_VF_AFFILIATION_111));
+
+ dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for "
+ "scsi%d\n", instance->host->host_no);
+
+ if (megasas_issue_blocked_cmd(instance, cmd, 0) != DCMD_SUCCESS) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD"
+ " failed with status 0x%x for scsi%d\n",
+ dcmd->cmd_status, instance->host->host_no);
+ retval = 1; /* Do a scan if we couldn't get affiliation */
+ goto out;
+ }
+
+ if (!initial) {
+ thisVf = new_affiliation_111->thisVf;
+ for (ld = 0 ; ld < new_affiliation_111->vdCount; ld++)
+ if (instance->vf_affiliation_111->map[ld].policy[thisVf] !=
+ new_affiliation_111->map[ld].policy[thisVf]) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: "
+ "Got new LD/VF affiliation for scsi%d\n",
+ instance->host->host_no);
+ memcpy(instance->vf_affiliation_111,
+ new_affiliation_111,
+ sizeof(struct MR_LD_VF_AFFILIATION_111));
+ retval = 1;
+ goto out;
+ }
+ }
+out:
+ if (new_affiliation_111) {
+ dma_free_coherent(&instance->pdev->dev,
+ sizeof(struct MR_LD_VF_AFFILIATION_111),
+ new_affiliation_111,
+ new_affiliation_111_h);
+ }
+
+ megasas_return_cmd(instance, cmd);
+
+ return retval;
+}
+
+static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance,
+ int initial)
+{
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_LD_VF_AFFILIATION *new_affiliation = NULL;
+ struct MR_LD_VF_MAP *newmap = NULL, *savedmap = NULL;
+ dma_addr_t new_affiliation_h;
+ int i, j, retval = 0, found = 0, doscan = 0;
+ u8 thisVf;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation12: "
+ "Failed to get cmd for scsi%d\n",
+ instance->host->host_no);
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ if (!instance->vf_affiliation) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF "
+ "affiliation for scsi%d\n", instance->host->host_no);
+ megasas_return_cmd(instance, cmd);
+ return -ENOMEM;
+ }
+
+ if (initial)
+ memset(instance->vf_affiliation, 0, (MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION));
+ else {
+ new_affiliation =
+ dma_alloc_coherent(&instance->pdev->dev,
+ (MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION),
+ &new_affiliation_h, GFP_KERNEL);
+ if (!new_affiliation) {
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate "
+ "memory for new affiliation for scsi%d\n",
+ instance->host->host_no);
+ megasas_return_cmd(instance, cmd);
+ return -ENOMEM;
+ }
+ }
+
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS);
+
+ if (initial)
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(instance->vf_affiliation_h);
+ else
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(new_affiliation_h);
+
+ dcmd->sgl.sge32[0].length = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION));
+
+ dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for "
+ "scsi%d\n", instance->host->host_no);
+
+
+ if (megasas_issue_blocked_cmd(instance, cmd, 0) != DCMD_SUCCESS) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD"
+ " failed with status 0x%x for scsi%d\n",
+ dcmd->cmd_status, instance->host->host_no);
+ retval = 1; /* Do a scan if we couldn't get affiliation */
+ goto out;
+ }
+
+ if (!initial) {
+ if (!new_affiliation->ldCount) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: Got new LD/VF "
+ "affiliation for passive path for scsi%d\n",
+ instance->host->host_no);
+ retval = 1;
+ goto out;
+ }
+ newmap = new_affiliation->map;
+ savedmap = instance->vf_affiliation->map;
+ thisVf = new_affiliation->thisVf;
+ for (i = 0 ; i < new_affiliation->ldCount; i++) {
+ found = 0;
+ for (j = 0; j < instance->vf_affiliation->ldCount;
+ j++) {
+ if (newmap->ref.targetId ==
+ savedmap->ref.targetId) {
+ found = 1;
+ if (newmap->policy[thisVf] !=
+ savedmap->policy[thisVf]) {
+ doscan = 1;
+ goto out;
+ }
+ }
+ savedmap = (struct MR_LD_VF_MAP *)
+ ((unsigned char *)savedmap +
+ savedmap->size);
+ }
+ if (!found && newmap->policy[thisVf] !=
+ MR_LD_ACCESS_HIDDEN) {
+ doscan = 1;
+ goto out;
+ }
+ newmap = (struct MR_LD_VF_MAP *)
+ ((unsigned char *)newmap + newmap->size);
+ }
+
+ newmap = new_affiliation->map;
+ savedmap = instance->vf_affiliation->map;
+
+ for (i = 0 ; i < instance->vf_affiliation->ldCount; i++) {
+ found = 0;
+ for (j = 0 ; j < new_affiliation->ldCount; j++) {
+ if (savedmap->ref.targetId ==
+ newmap->ref.targetId) {
+ found = 1;
+ if (savedmap->policy[thisVf] !=
+ newmap->policy[thisVf]) {
+ doscan = 1;
+ goto out;
+ }
+ }
+ newmap = (struct MR_LD_VF_MAP *)
+ ((unsigned char *)newmap +
+ newmap->size);
+ }
+ if (!found && savedmap->policy[thisVf] !=
+ MR_LD_ACCESS_HIDDEN) {
+ doscan = 1;
+ goto out;
+ }
+ savedmap = (struct MR_LD_VF_MAP *)
+ ((unsigned char *)savedmap +
+ savedmap->size);
+ }
+ }
+out:
+ if (doscan) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: Got new LD/VF "
+ "affiliation for scsi%d\n", instance->host->host_no);
+ memcpy(instance->vf_affiliation, new_affiliation,
+ new_affiliation->size);
+ retval = 1;
+ }
+
+ if (new_affiliation)
+ dma_free_coherent(&instance->pdev->dev,
+ (MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION),
+ new_affiliation, new_affiliation_h);
+ megasas_return_cmd(instance, cmd);
+
+ return retval;
+}
+
+/* This function will get the current SR-IOV LD/VF affiliation */
+static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance,
+ int initial)
+{
+ int retval;
+
+ if (instance->PlasmaFW111)
+ retval = megasas_get_ld_vf_affiliation_111(instance, initial);
+ else
+ retval = megasas_get_ld_vf_affiliation_12(instance, initial);
+ return retval;
+}
+
+/* This function will tell FW to start the SR-IOV heartbeat */
+int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
+ int initial)
+{
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ int retval = 0;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_sriov_start_heartbeat: "
+ "Failed to get cmd for scsi%d\n",
+ instance->host->host_no);
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ if (initial) {
+ instance->hb_host_mem =
+ dma_alloc_coherent(&instance->pdev->dev,
+ sizeof(struct MR_CTRL_HB_HOST_MEM),
+ &instance->hb_host_mem_h,
+ GFP_KERNEL);
+ if (!instance->hb_host_mem) {
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate"
+ " memory for heartbeat host memory for scsi%d\n",
+ instance->host->host_no);
+ retval = -ENOMEM;
+ goto out;
+ }
+ }
+
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->mbox.s[0] = cpu_to_le16(sizeof(struct MR_CTRL_HB_HOST_MEM));
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC);
+
+ megasas_set_dma_settings(instance, dcmd, instance->hb_host_mem_h,
+ sizeof(struct MR_CTRL_HB_HOST_MEM));
+
+ dev_warn(&instance->pdev->dev, "SR-IOV: Starting heartbeat for scsi%d\n",
+ instance->host->host_no);
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts)
+ retval = megasas_issue_blocked_cmd(instance, cmd,
+ MEGASAS_ROUTINE_WAIT_TIME_VF);
+ else
+ retval = megasas_issue_polled(instance, cmd);
+
+ if (retval) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
+ "_MEM_ALLOC DCMD %s for scsi%d\n",
+ (dcmd->cmd_status == MFI_STAT_INVALID_STATUS) ?
+ "timed out" : "failed", instance->host->host_no);
+ retval = 1;
+ }
+
+out:
+ megasas_return_cmd(instance, cmd);
+
+ return retval;
+}
+
+/* Handler for SR-IOV heartbeat */
+static void megasas_sriov_heartbeat_handler(struct timer_list *t)
+{
+ struct megasas_instance *instance =
+ timer_container_of(instance, t, sriov_heartbeat_timer);
+
+ if (instance->hb_host_mem->HB.fwCounter !=
+ instance->hb_host_mem->HB.driverCounter) {
+ instance->hb_host_mem->HB.driverCounter =
+ instance->hb_host_mem->HB.fwCounter;
+ mod_timer(&instance->sriov_heartbeat_timer,
+ jiffies + MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
+ } else {
+ dev_warn(&instance->pdev->dev, "SR-IOV: Heartbeat never "
+ "completed for scsi%d\n", instance->host->host_no);
+ schedule_work(&instance->work_init);
+ }
+}
+
/**
* megasas_wait_for_outstanding - Wait for all outstanding cmds
* @instance: Adapter soft state
@@ -1704,21 +2749,21 @@ void megasas_do_ocr(struct megasas_instance *instance)
*/
static int megasas_wait_for_outstanding(struct megasas_instance *instance)
{
- int i;
+ int i, sl, outstanding;
u32 reset_index;
u32 wait_time = MEGASAS_RESET_WAIT_TIME;
- u8 adprecovery;
unsigned long flags;
struct list_head clist_local;
struct megasas_cmd *reset_cmd;
u32 fw_state;
- u8 kill_adapter_flag;
- spin_lock_irqsave(&instance->hba_lock, flags);
- adprecovery = instance->adprecovery;
- spin_unlock_irqrestore(&instance->hba_lock, flags);
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_info(&instance->pdev->dev, "%s:%d HBA is killed.\n",
+ __func__, __LINE__);
+ return FAILED;
+ }
- if (adprecovery != MEGASAS_HBA_OPERATIONAL) {
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) {
INIT_LIST_HEAD(&clist_local);
spin_lock_irqsave(&instance->hba_lock, flags);
@@ -1726,48 +2771,43 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
&clist_local);
spin_unlock_irqrestore(&instance->hba_lock, flags);
- printk(KERN_NOTICE "megasas: HBA reset wait ...\n");
+ dev_notice(&instance->pdev->dev, "HBA reset wait ...\n");
for (i = 0; i < wait_time; i++) {
msleep(1000);
- spin_lock_irqsave(&instance->hba_lock, flags);
- adprecovery = instance->adprecovery;
- spin_unlock_irqrestore(&instance->hba_lock, flags);
- if (adprecovery == MEGASAS_HBA_OPERATIONAL)
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HBA_OPERATIONAL)
break;
}
- if (adprecovery != MEGASAS_HBA_OPERATIONAL) {
- printk(KERN_NOTICE "megasas: reset: Stopping HBA.\n");
- spin_lock_irqsave(&instance->hba_lock, flags);
- instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
- spin_unlock_irqrestore(&instance->hba_lock, flags);
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) {
+ dev_notice(&instance->pdev->dev, "reset: Stopping HBA.\n");
+ atomic_set(&instance->adprecovery, MEGASAS_HW_CRITICAL_ERROR);
return FAILED;
}
- reset_index = 0;
+ reset_index = 0;
while (!list_empty(&clist_local)) {
- reset_cmd = list_entry((&clist_local)->next,
+ reset_cmd = list_entry((&clist_local)->next,
struct megasas_cmd, list);
list_del_init(&reset_cmd->list);
if (reset_cmd->scmd) {
- reset_cmd->scmd->result = DID_RESET << 16;
- printk(KERN_NOTICE "%d:%p reset [%02x]\n",
+ reset_cmd->scmd->result = DID_REQUEUE << 16;
+ dev_notice(&instance->pdev->dev, "%d:%p reset [%02x]\n",
reset_index, reset_cmd,
reset_cmd->scmd->cmnd[0]);
- reset_cmd->scmd->scsi_done(reset_cmd->scmd);
+ scsi_done(reset_cmd->scmd);
megasas_return_cmd(instance, reset_cmd);
} else if (reset_cmd->sync_cmd) {
- printk(KERN_NOTICE "megasas:%p synch cmds"
+ dev_notice(&instance->pdev->dev, "%p synch cmds"
"reset queue\n",
reset_cmd);
- reset_cmd->cmd_status = ENODATA;
+ reset_cmd->cmd_status_drv = DCMD_INIT;
instance->instancet->fire_cmd(instance,
reset_cmd->frame_phys_addr,
0, instance->reg_set);
} else {
- printk(KERN_NOTICE "megasas: %p unexpected"
+ dev_notice(&instance->pdev->dev, "%p unexpected"
"cmds lst\n",
reset_cmd);
}
@@ -1777,15 +2817,14 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
return SUCCESS;
}
- for (i = 0; i < wait_time; i++) {
-
- int outstanding = atomic_read(&instance->fw_outstanding);
+ for (i = 0; i < resetwaittime; i++) {
+ outstanding = atomic_read(&instance->fw_outstanding);
if (!outstanding)
break;
if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
- printk(KERN_NOTICE "megasas: [%2d]waiting for %d "
+ dev_notice(&instance->pdev->dev, "[%2d]waiting for %d "
"commands to complete\n",i,outstanding);
/*
* Call cmd completion routine. Cmd to be
@@ -1798,69 +2837,60 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
}
i = 0;
- kill_adapter_flag = 0;
+ outstanding = atomic_read(&instance->fw_outstanding);
+ fw_state = instance->instancet->read_fw_status_reg(instance) & MFI_STATE_MASK;
+
+ if ((!outstanding && (fw_state == MFI_STATE_OPERATIONAL)))
+ goto no_outstanding;
+
+ if (instance->disableOnlineCtrlReset)
+ goto kill_hba_and_failed;
do {
- fw_state = instance->instancet->read_fw_status_reg(
- instance->reg_set) & MFI_STATE_MASK;
- if ((fw_state == MFI_STATE_FAULT) &&
- (instance->disableOnlineCtrlReset == 0)) {
- if (i == 3) {
- kill_adapter_flag = 2;
- break;
- }
+ if ((fw_state == MFI_STATE_FAULT) || atomic_read(&instance->fw_outstanding)) {
+ dev_info(&instance->pdev->dev,
+ "%s:%d waiting_for_outstanding: before issue OCR. FW state = 0x%x, outstanding 0x%x\n",
+ __func__, __LINE__, fw_state, atomic_read(&instance->fw_outstanding));
+ if (i == 3)
+ goto kill_hba_and_failed;
megasas_do_ocr(instance);
- kill_adapter_flag = 1;
- /* wait for 1 secs to let FW finish the pending cmds */
- msleep(1000);
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_info(&instance->pdev->dev, "%s:%d OCR failed and HBA is killed.\n",
+ __func__, __LINE__);
+ return FAILED;
+ }
+ dev_info(&instance->pdev->dev, "%s:%d waiting_for_outstanding: after issue OCR.\n",
+ __func__, __LINE__);
+
+ for (sl = 0; sl < 10; sl++)
+ msleep(500);
+
+ outstanding = atomic_read(&instance->fw_outstanding);
+
+ fw_state = instance->instancet->read_fw_status_reg(instance) & MFI_STATE_MASK;
+ if ((!outstanding && (fw_state == MFI_STATE_OPERATIONAL)))
+ goto no_outstanding;
}
i++;
} while (i <= 3);
- if (atomic_read(&instance->fw_outstanding) &&
- !kill_adapter_flag) {
- if (instance->disableOnlineCtrlReset == 0) {
+no_outstanding:
- megasas_do_ocr(instance);
+ dev_info(&instance->pdev->dev, "%s:%d no more pending commands remain after reset handling.\n",
+ __func__, __LINE__);
+ return SUCCESS;
- /* wait for 5 secs to let FW finish the pending cmds */
- for (i = 0; i < wait_time; i++) {
- int outstanding =
- atomic_read(&instance->fw_outstanding);
- if (!outstanding)
- return SUCCESS;
- msleep(1000);
- }
- }
- }
+kill_hba_and_failed:
- if (atomic_read(&instance->fw_outstanding) ||
- (kill_adapter_flag == 2)) {
- printk(KERN_NOTICE "megaraid_sas: pending cmds after reset\n");
- /*
- * Send signal to FW to stop processing any pending cmds.
- * The controller will be taken offline by the OS now.
- */
- if ((instance->pdev->device ==
- PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
- (instance->pdev->device ==
- PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
- writel(MFI_STOP_ADP,
- &instance->reg_set->doorbell);
- } else {
- writel(MFI_STOP_ADP,
- &instance->reg_set->inbound_doorbell);
- }
- megasas_dump_pending_frames(instance);
- spin_lock_irqsave(&instance->hba_lock, flags);
- instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
- spin_unlock_irqrestore(&instance->hba_lock, flags);
- return FAILED;
- }
+ /* Reset not supported, kill adapter */
+ dev_info(&instance->pdev->dev, "%s:%d killing adapter scsi%d"
+ " disableOnlineCtrlReset %d fw_outstanding %d \n",
+ __func__, __LINE__, instance->host->host_no, instance->disableOnlineCtrlReset,
+ atomic_read(&instance->fw_outstanding));
+ megasas_dump_pending_frames(instance);
+ megaraid_sas_kill_hba(instance);
- printk(KERN_NOTICE "megaraid_sas: no pending cmds after reset\n");
-
- return SUCCESS;
+ return FAILED;
}
/**
@@ -1881,17 +2911,16 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd)
scmd_printk(KERN_NOTICE, scmd, "megasas: RESET cmd=%x retries=%x\n",
scmd->cmnd[0], scmd->retries);
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
- printk(KERN_ERR "megasas: cannot recover from previous reset "
- "failures\n");
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_err(&instance->pdev->dev, "cannot recover from previous reset failures\n");
return FAILED;
}
ret_val = megasas_wait_for_outstanding(instance);
if (ret_val == SUCCESS)
- printk(KERN_NOTICE "megasas: reset successful \n");
+ dev_notice(&instance->pdev->dev, "reset successful\n");
else
- printk(KERN_ERR "megasas: failed to do reset\n");
+ dev_err(&instance->pdev->dev, "failed to do reset\n");
return ret_val;
}
@@ -1903,82 +2932,224 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd)
* Sets the FW busy flag and reduces the host->can_queue if the
* cmd has not been completed within the timeout period.
*/
-static enum
-blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
+static enum scsi_timeout_action megasas_reset_timer(struct scsi_cmnd *scmd)
{
- struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr;
struct megasas_instance *instance;
unsigned long flags;
if (time_after(jiffies, scmd->jiffies_at_alloc +
- (MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) {
- return BLK_EH_NOT_HANDLED;
+ (scmd_timeout * 2) * HZ)) {
+ return SCSI_EH_NOT_HANDLED;
}
- instance = cmd->instance;
+ instance = (struct megasas_instance *)scmd->device->host->hostdata;
if (!(instance->flag & MEGASAS_FW_BUSY)) {
/* FW is busy, throttle IO */
spin_lock_irqsave(instance->host->host_lock, flags);
- instance->host->can_queue = 16;
+ instance->host->can_queue = instance->throttlequeuedepth;
instance->last_time = jiffies;
instance->flag |= MEGASAS_FW_BUSY;
spin_unlock_irqrestore(instance->host->host_lock, flags);
}
- return BLK_EH_RESET_TIMER;
+ return SCSI_EH_RESET_TIMER;
}
/**
- * megasas_reset_device - Device reset handler entry point
+ * megasas_dump - This function will print hexdump of provided buffer.
+ * @buf: Buffer to be dumped
+ * @sz: Size in bytes
+ * @format: Different formats of dumping e.g. format=n will
+ * cause only 'n' 32 bit words to be dumped in a single
+ * line.
*/
-static int megasas_reset_device(struct scsi_cmnd *scmd)
+inline void
+megasas_dump(void *buf, int sz, int format)
{
- int ret;
+ int i;
+ __le32 *buf_loc = (__le32 *)buf;
- /*
- * First wait for all commands to complete
- */
- ret = megasas_generic_reset(scmd);
+ for (i = 0; i < (sz / sizeof(__le32)); i++) {
+ if ((i % format) == 0) {
+ if (i != 0)
+ printk(KERN_CONT "\n");
+ printk(KERN_CONT "%08x: ", (i * 4));
+ }
+ printk(KERN_CONT "%08x ", le32_to_cpu(buf_loc[i]));
+ }
+ printk(KERN_CONT "\n");
+}
+
+/**
+ * megasas_dump_reg_set - This function will print hexdump of register set
+ * @reg_set: Register set to be dumped
+ */
+inline void
+megasas_dump_reg_set(void __iomem *reg_set)
+{
+ unsigned int i, sz = 256;
+ u32 __iomem *reg = (u32 __iomem *)reg_set;
+
+ for (i = 0; i < (sz / sizeof(u32)); i++)
+ printk("%08x: %08x\n", (i * 4), readl(&reg[i]));
+}
+
+/**
+ * megasas_dump_fusion_io - This function will print key details
+ * of SCSI IO
+ * @scmd: SCSI command pointer of SCSI IO
+ */
+void
+megasas_dump_fusion_io(struct scsi_cmnd *scmd)
+{
+ struct megasas_cmd_fusion *cmd = megasas_priv(scmd)->cmd_priv;
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
+ struct megasas_instance *instance;
+
+ instance = (struct megasas_instance *)scmd->device->host->hostdata;
+
+ scmd_printk(KERN_INFO, scmd,
+ "scmd: (0x%p) retries: 0x%x allowed: 0x%x\n",
+ scmd, scmd->retries, scmd->allowed);
+ scsi_print_command(scmd);
+
+ if (cmd) {
+ req_desc = (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)cmd->request_desc;
+ scmd_printk(KERN_INFO, scmd, "Request descriptor details:\n");
+ scmd_printk(KERN_INFO, scmd,
+ "RequestFlags:0x%x MSIxIndex:0x%x SMID:0x%x LMID:0x%x DevHandle:0x%x\n",
+ req_desc->SCSIIO.RequestFlags,
+ req_desc->SCSIIO.MSIxIndex, req_desc->SCSIIO.SMID,
+ req_desc->SCSIIO.LMID, req_desc->SCSIIO.DevHandle);
+
+ printk(KERN_INFO "IO request frame:\n");
+ megasas_dump(cmd->io_request,
+ MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE, 8);
+ printk(KERN_INFO "Chain frame:\n");
+ megasas_dump(cmd->sg_frame,
+ instance->max_chain_frame_sz, 8);
+ }
- return ret;
+}
+
+/*
+ * megasas_dump_sys_regs - This function will dump system registers through
+ * sysfs.
+ * @reg_set: Pointer to System register set.
+ * @buf: Buffer to which output is to be written.
+ * @return: Number of bytes written to buffer.
+ */
+static inline ssize_t
+megasas_dump_sys_regs(void __iomem *reg_set, char *buf)
+{
+ unsigned int i, sz = 256;
+ int bytes_wrote = 0;
+ char *loc = (char *)buf;
+ u32 __iomem *reg = (u32 __iomem *)reg_set;
+
+ for (i = 0; i < sz / sizeof(u32); i++) {
+ bytes_wrote += scnprintf(loc + bytes_wrote,
+ PAGE_SIZE - bytes_wrote,
+ "%08x: %08x\n", (i * 4),
+ readl(&reg[i]));
+ }
+ return bytes_wrote;
}
/**
* megasas_reset_bus_host - Bus & host reset handler entry point
+ * @scmd: Mid-layer SCSI command
*/
static int megasas_reset_bus_host(struct scsi_cmnd *scmd)
{
int ret;
struct megasas_instance *instance;
+
instance = (struct megasas_instance *)scmd->device->host->hostdata;
+ scmd_printk(KERN_INFO, scmd,
+ "OCR is requested due to IO timeout!!\n");
+
+ scmd_printk(KERN_INFO, scmd,
+ "SCSI host state: %d SCSI host busy: %d FW outstanding: %d\n",
+ scmd->device->host->shost_state,
+ scsi_host_busy(scmd->device->host),
+ atomic_read(&instance->fw_outstanding));
/*
* First wait for all commands to complete
*/
- if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)
- ret = megasas_reset_fusion(scmd->device->host);
- else
+ if (instance->adapter_type == MFI_SERIES) {
ret = megasas_generic_reset(scmd);
+ } else {
+ megasas_dump_fusion_io(scmd);
+ ret = megasas_reset_fusion(scmd->device->host,
+ SCSIIO_TIMEOUT_OCR);
+ }
+
+ return ret;
+}
+
+/**
+ * megasas_task_abort - Issues task abort request to firmware
+ * (supported only for fusion adapters)
+ * @scmd: SCSI command pointer
+ */
+static int megasas_task_abort(struct scsi_cmnd *scmd)
+{
+ int ret;
+ struct megasas_instance *instance;
+
+ instance = (struct megasas_instance *)scmd->device->host->hostdata;
+
+ if (instance->adapter_type != MFI_SERIES)
+ ret = megasas_task_abort_fusion(scmd);
+ else {
+ sdev_printk(KERN_NOTICE, scmd->device, "TASK ABORT not supported\n");
+ ret = FAILED;
+ }
+
+ return ret;
+}
+
+/**
+ * megasas_reset_target: Issues target reset request to firmware
+ * (supported only for fusion adapters)
+ * @scmd: SCSI command pointer
+ */
+static int megasas_reset_target(struct scsi_cmnd *scmd)
+{
+ int ret;
+ struct megasas_instance *instance;
+
+ instance = (struct megasas_instance *)scmd->device->host->hostdata;
+
+ if (instance->adapter_type != MFI_SERIES)
+ ret = megasas_reset_target_fusion(scmd);
+ else {
+ sdev_printk(KERN_NOTICE, scmd->device, "TARGET RESET not supported\n");
+ ret = FAILED;
+ }
return ret;
}
/**
* megasas_bios_param - Returns disk geometry for a disk
- * @sdev: device handle
- * @bdev: block device
+ * @sdev: device handle
+ * @unused: gendisk
* @capacity: drive capacity
* @geom: geometry parameters
*/
static int
-megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+megasas_bios_param(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int geom[])
{
int heads;
int sectors;
sector_t cylinders;
unsigned long tmp;
+
/* Default heads (64) & sectors (32) */
heads = 64;
sectors = 32;
@@ -2007,6 +3178,43 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
return 0;
}
+static void megasas_map_queues(struct Scsi_Host *shost)
+{
+ struct megasas_instance *instance;
+ int qoff = 0, offset;
+ struct blk_mq_queue_map *map;
+
+ instance = (struct megasas_instance *)shost->hostdata;
+
+ if (shost->nr_hw_queues == 1)
+ return;
+
+ offset = instance->low_latency_index_start;
+
+ /* Setup Default hctx */
+ map = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
+ map->nr_queues = instance->msix_vectors - offset;
+ map->queue_offset = 0;
+ blk_mq_map_hw_queues(map, &instance->pdev->dev, offset);
+ qoff += map->nr_queues;
+ offset += map->nr_queues;
+
+ /* we never use READ queue, so can't cheat blk-mq */
+ shost->tag_set.map[HCTX_TYPE_READ].nr_queues = 0;
+
+ /* Setup Poll hctx */
+ map = &shost->tag_set.map[HCTX_TYPE_POLL];
+ map->nr_queues = instance->iopoll_q_count;
+ if (map->nr_queues) {
+ /*
+ * The poll queue(s) doesn't have an IRQ (and hence IRQ
+ * affinity), so use the regular blk-mq cpu mapping
+ */
+ map->queue_offset = qoff;
+ blk_mq_map_queues(map);
+ }
+}
+
static void megasas_aen_polling(struct work_struct *work);
/**
@@ -2025,6 +3233,7 @@ static void
megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
unsigned long flags;
+
/*
* Don't signal app if it is just an aborted previously registered aen
*/
@@ -2039,41 +3248,285 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
cmd->abort_aen = 0;
instance->aen_cmd = NULL;
+
megasas_return_cmd(instance, cmd);
if ((instance->unload == 0) &&
((instance->issuepend_done == 1))) {
struct megasas_aen_event *ev;
+
ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
if (!ev) {
- printk(KERN_ERR "megasas_service_aen: out of memory\n");
+ dev_err(&instance->pdev->dev, "megasas_service_aen: out of memory\n");
} else {
ev->instance = instance;
instance->ev = ev;
- INIT_WORK(&ev->hotplug_work, megasas_aen_polling);
- schedule_delayed_work(
- (struct delayed_work *)&ev->hotplug_work, 0);
+ INIT_DELAYED_WORK(&ev->hotplug_work,
+ megasas_aen_polling);
+ schedule_delayed_work(&ev->hotplug_work, 0);
}
}
}
+static ssize_t
+fw_crash_buffer_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance =
+ (struct megasas_instance *) shost->hostdata;
+ int val = 0;
+
+ if (kstrtoint(buf, 0, &val) != 0)
+ return -EINVAL;
+
+ mutex_lock(&instance->crashdump_lock);
+ instance->fw_crash_buffer_offset = val;
+ mutex_unlock(&instance->crashdump_lock);
+ return strlen(buf);
+}
+
+static ssize_t
+fw_crash_buffer_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance =
+ (struct megasas_instance *) shost->hostdata;
+ u32 size;
+ unsigned long dmachunk = CRASH_DMA_BUF_SIZE;
+ unsigned long chunk_left_bytes;
+ unsigned long src_addr;
+ u32 buff_offset;
+
+ mutex_lock(&instance->crashdump_lock);
+ buff_offset = instance->fw_crash_buffer_offset;
+ if (!instance->crash_dump_buf ||
+ !((instance->fw_crash_state == AVAILABLE) ||
+ (instance->fw_crash_state == COPYING))) {
+ dev_err(&instance->pdev->dev,
+ "Firmware crash dump is not available\n");
+ mutex_unlock(&instance->crashdump_lock);
+ return -EINVAL;
+ }
+
+ if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) {
+ dev_err(&instance->pdev->dev,
+ "Firmware crash dump offset is out of range\n");
+ mutex_unlock(&instance->crashdump_lock);
+ return 0;
+ }
+
+ size = (instance->fw_crash_buffer_size * dmachunk) - buff_offset;
+ chunk_left_bytes = dmachunk - (buff_offset % dmachunk);
+ size = (size > chunk_left_bytes) ? chunk_left_bytes : size;
+ size = (size >= PAGE_SIZE) ? (PAGE_SIZE - 1) : size;
+
+ src_addr = (unsigned long)instance->crash_buf[buff_offset / dmachunk] +
+ (buff_offset % dmachunk);
+ memcpy(buf, (void *)src_addr, size);
+ mutex_unlock(&instance->crashdump_lock);
+
+ return size;
+}
+
+static ssize_t
+fw_crash_buffer_size_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance =
+ (struct megasas_instance *) shost->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)
+ ((instance->fw_crash_buffer_size) * 1024 * 1024)/PAGE_SIZE);
+}
+
+static ssize_t
+fw_crash_state_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance =
+ (struct megasas_instance *) shost->hostdata;
+ int val = 0;
+
+ if (kstrtoint(buf, 0, &val) != 0)
+ return -EINVAL;
+
+ if ((val <= AVAILABLE || val > COPY_ERROR)) {
+ dev_err(&instance->pdev->dev, "application updates invalid "
+ "firmware crash state\n");
+ return -EINVAL;
+ }
+
+ instance->fw_crash_state = val;
+
+ if ((val == COPIED) || (val == COPY_ERROR)) {
+ mutex_lock(&instance->crashdump_lock);
+ megasas_free_host_crash_buffer(instance);
+ mutex_unlock(&instance->crashdump_lock);
+ if (val == COPY_ERROR)
+ dev_info(&instance->pdev->dev, "application failed to "
+ "copy Firmware crash dump\n");
+ else
+ dev_info(&instance->pdev->dev, "Firmware crash dump "
+ "copied successfully\n");
+ }
+ return strlen(buf);
+}
+
+static ssize_t
+fw_crash_state_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance =
+ (struct megasas_instance *) shost->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", instance->fw_crash_state);
+}
+
+static ssize_t
+page_size_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)PAGE_SIZE - 1);
+}
+
+static ssize_t
+ldio_outstanding_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&instance->ldio_outstanding));
+}
+
+static ssize_t
+fw_cmds_outstanding_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&instance->fw_outstanding));
+}
+
+static ssize_t
+enable_sdev_max_qd_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", instance->enable_sdev_max_qd);
+}
+
+static ssize_t
+enable_sdev_max_qd_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata;
+ u32 val = 0;
+ bool is_target_prop;
+ int ret_target_prop = DCMD_FAILED;
+ struct scsi_device *sdev;
+
+ if (kstrtou32(buf, 0, &val) != 0) {
+ pr_err("megasas: could not set enable_sdev_max_qd\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&instance->reset_mutex);
+ if (val)
+ instance->enable_sdev_max_qd = true;
+ else
+ instance->enable_sdev_max_qd = false;
+
+ shost_for_each_device(sdev, shost) {
+ ret_target_prop = megasas_get_target_prop(instance, sdev);
+ is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false;
+ megasas_set_fw_assisted_qd(sdev, is_target_prop);
+ }
+ mutex_unlock(&instance->reset_mutex);
+
+ return strlen(buf);
+}
+
+static ssize_t
+dump_system_regs_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance =
+ (struct megasas_instance *)shost->hostdata;
+
+ return megasas_dump_sys_regs(instance->reg_set, buf);
+}
+
+static ssize_t
+raid_map_id_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct megasas_instance *instance =
+ (struct megasas_instance *)shost->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n",
+ (unsigned long)instance->map_id);
+}
+
+static DEVICE_ATTR_RW(fw_crash_buffer);
+static DEVICE_ATTR_RO(fw_crash_buffer_size);
+static DEVICE_ATTR_RW(fw_crash_state);
+static DEVICE_ATTR_RO(page_size);
+static DEVICE_ATTR_RO(ldio_outstanding);
+static DEVICE_ATTR_RO(fw_cmds_outstanding);
+static DEVICE_ATTR_RW(enable_sdev_max_qd);
+static DEVICE_ATTR_RO(dump_system_regs);
+static DEVICE_ATTR_RO(raid_map_id);
+
+static struct attribute *megaraid_host_attrs[] = {
+ &dev_attr_fw_crash_buffer_size.attr,
+ &dev_attr_fw_crash_buffer.attr,
+ &dev_attr_fw_crash_state.attr,
+ &dev_attr_page_size.attr,
+ &dev_attr_ldio_outstanding.attr,
+ &dev_attr_fw_cmds_outstanding.attr,
+ &dev_attr_enable_sdev_max_qd.attr,
+ &dev_attr_dump_system_regs.attr,
+ &dev_attr_raid_map_id.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(megaraid_host);
+
/*
* Scsi host template for megaraid_sas driver
*/
-static struct scsi_host_template megasas_template = {
+static const struct scsi_host_template megasas_template = {
.module = THIS_MODULE,
- .name = "LSI SAS based MegaRAID driver",
+ .name = "Avago SAS based MegaRAID driver",
.proc_name = "megaraid_sas",
- .slave_configure = megasas_slave_configure,
- .slave_alloc = megasas_slave_alloc,
+ .sdev_configure = megasas_sdev_configure,
+ .sdev_init = megasas_sdev_init,
+ .sdev_destroy = megasas_sdev_destroy,
.queuecommand = megasas_queue_command,
- .eh_device_reset_handler = megasas_reset_device,
- .eh_bus_reset_handler = megasas_reset_bus_host,
+ .eh_target_reset_handler = megasas_reset_target,
+ .eh_abort_handler = megasas_task_abort,
.eh_host_reset_handler = megasas_reset_bus_host,
.eh_timed_out = megasas_reset_timer,
+ .shost_groups = megaraid_host_groups,
.bios_param = megasas_bios_param,
- .use_clustering = ENABLE_CLUSTERING,
+ .map_queues = megasas_map_queues,
+ .mq_poll = megasas_blk_mq_poll,
+ .change_queue_depth = scsi_change_queue_depth,
+ .max_segment_size = 0xffffffff,
+ .cmd_size = sizeof(struct megasas_cmd_priv),
};
/**
@@ -2089,11 +3542,11 @@ static void
megasas_complete_int_cmd(struct megasas_instance *instance,
struct megasas_cmd *cmd)
{
- cmd->cmd_status = cmd->frame->io.cmd_status;
+ if (cmd->cmd_status_drv == DCMD_INIT)
+ cmd->cmd_status_drv =
+ (cmd->frame->io.cmd_status == MFI_STAT_OK) ?
+ DCMD_SUCCESS : DCMD_FAILED;
- if (cmd->cmd_status == ENODATA) {
- cmd->cmd_status = 0;
- }
wake_up(&instance->int_cmd_wait_q);
}
@@ -2112,11 +3565,25 @@ megasas_complete_abort(struct megasas_instance *instance,
{
if (cmd->sync_cmd) {
cmd->sync_cmd = 0;
- cmd->cmd_status = 0;
+ cmd->cmd_status_drv = DCMD_SUCCESS;
wake_up(&instance->abort_cmd_wait_q);
}
+}
- return;
+static void
+megasas_set_ld_removed_by_fw(struct megasas_instance *instance)
+{
+ uint i;
+
+ for (i = 0; (i < MEGASAS_MAX_LD_IDS); i++) {
+ if (instance->ld_ids_prev[i] != 0xff &&
+ instance->ld_ids_from_raidmap[i] == 0xff) {
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "LD target ID %d removed from RAID map\n", i);
+ instance->ld_tgtid_status[i] = LD_TARGET_ID_DELETED;
+ }
+ }
}
/**
@@ -2124,10 +3591,10 @@ megasas_complete_abort(struct megasas_instance *instance,
* @instance: Adapter soft state
* @cmd: Command to be completed
* @alt_status: If non-zero, use this value as status to
- * SCSI mid-layer instead of the value returned
- * by the FW. This should be used if caller wants
- * an alternate status (as in the case of aborted
- * commands)
+ * SCSI mid-layer instead of the value returned
+ * by the FW. This should be used if caller wants
+ * an alternate status (as in the case of aborted
+ * commands)
*/
void
megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
@@ -2137,15 +3604,25 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
struct megasas_header *hdr = &cmd->frame->hdr;
unsigned long flags;
struct fusion_context *fusion = instance->ctrl_context;
+ u32 opcode, status;
/* flag for the retry reset */
cmd->retry_for_fw_reset = 0;
if (cmd->scmd)
- cmd->scmd->SCp.ptr = NULL;
+ megasas_priv(cmd->scmd)->cmd_priv = NULL;
switch (hdr->cmd) {
-
+ case MFI_CMD_INVALID:
+ /* Some older 1068 controller FW may keep a pended
+ MR_DCMD_CTRL_EVENT_GET_INFO left over from the main kernel
+ when booting the kdump kernel. Ignore this command to
+ prevent a kernel panic on shutdown of the kdump kernel. */
+ dev_warn(&instance->pdev->dev, "MFI_CMD_INVALID command "
+ "completed\n");
+ dev_warn(&instance->pdev->dev, "If you have a controller "
+ "other than PERC5, please upgrade your firmware\n");
+ break;
case MFI_CMD_PD_SCSI_IO:
case MFI_CMD_LD_SCSI_IO:
@@ -2159,6 +3636,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
megasas_complete_int_cmd(instance, cmd);
break;
}
+ fallthrough;
case MFI_CMD_LD_READ:
case MFI_CMD_LD_WRITE:
@@ -2173,7 +3651,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
atomic_dec(&instance->fw_outstanding);
scsi_dma_unmap(cmd->scmd);
- cmd->scmd->scsi_done(cmd->scmd);
+ scsi_done(cmd->scmd);
megasas_return_cmd(instance, cmd);
break;
@@ -2187,8 +3665,10 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
case MFI_STAT_SCSI_IO_FAILED:
case MFI_STAT_LD_INIT_IN_PROGRESS:
- cmd->scmd->result =
- (DID_ERROR << 16) | hdr->scsi_status;
+ if (hdr->scsi_status == 0xf0)
+ cmd->scmd->result = (DID_ERROR << 16) | SAM_STAT_CHECK_CONDITION;
+ else
+ cmd->scmd->result = (DID_ERROR << 16) | hdr->scsi_status;
break;
case MFI_STAT_SCSI_DONE_WITH_ERROR:
@@ -2200,8 +3680,6 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
SCSI_SENSE_BUFFERSIZE);
memcpy(cmd->scmd->sense_buffer, cmd->sense,
hdr->sense_len);
-
- cmd->scmd->result |= DRIVER_SENSE << 24;
}
break;
@@ -2212,7 +3690,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
break;
default:
- printk(KERN_DEBUG "megasas: MFI FW status %#x\n",
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "MFI FW status %#x\n",
hdr->cmd_status);
cmd->scmd->result = DID_ERROR << 16;
break;
@@ -2221,23 +3699,30 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
atomic_dec(&instance->fw_outstanding);
scsi_dma_unmap(cmd->scmd);
- cmd->scmd->scsi_done(cmd->scmd);
+ scsi_done(cmd->scmd);
megasas_return_cmd(instance, cmd);
break;
case MFI_CMD_SMP:
case MFI_CMD_STP:
+ case MFI_CMD_NVME:
+ case MFI_CMD_TOOLBOX:
+ megasas_complete_int_cmd(instance, cmd);
+ break;
+
case MFI_CMD_DCMD:
+ opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
/* Check for LD map update */
- if ((cmd->frame->dcmd.opcode == MR_DCMD_LD_MAP_GET_INFO) &&
- (cmd->frame->dcmd.mbox.b[1] == 1)) {
+ if ((opcode == MR_DCMD_LD_MAP_GET_INFO)
+ && (cmd->frame->dcmd.mbox.b[1] == 1)) {
+ fusion->fast_path_io = 0;
spin_lock_irqsave(instance->host->host_lock, flags);
- if (cmd->frame->hdr.cmd_status != 0) {
- if (cmd->frame->hdr.cmd_status !=
- MFI_STAT_NOT_FOUND)
- printk(KERN_WARNING "megasas: map sync"
- "failed, status = 0x%x.\n",
+ status = cmd->frame->hdr.cmd_status;
+ instance->map_update_cmd = NULL;
+ if (status != MFI_STAT_OK) {
+ if (status != MFI_STAT_NOT_FOUND)
+ dev_warn(&instance->pdev->dev, "map syncfailed, status = 0x%x\n",
cmd->frame->hdr.cmd_status);
else {
megasas_return_cmd(instance, cmd);
@@ -2246,31 +3731,64 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
flags);
break;
}
- } else
- instance->map_id++;
+ }
+
megasas_return_cmd(instance, cmd);
- if (MR_ValidateMapInfo(
- fusion->ld_map[(instance->map_id & 1)],
- fusion->load_balance_info))
+
+ /*
+ * Set fast path IO to ZERO.
+ * Validate Map will set proper value.
+ * Meanwhile all IOs will go as LD IO.
+ */
+ if (status == MFI_STAT_OK &&
+ (MR_ValidateMapInfo(instance, (instance->map_id + 1)))) {
+ instance->map_id++;
fusion->fast_path_io = 1;
- else
+ } else {
fusion->fast_path_io = 0;
+ }
+
+ if (instance->adapter_type >= INVADER_SERIES)
+ megasas_set_ld_removed_by_fw(instance);
+
megasas_sync_map_info(instance);
spin_unlock_irqrestore(instance->host->host_lock,
flags);
+
break;
}
- if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET_INFO ||
- cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET) {
+ if (opcode == MR_DCMD_CTRL_EVENT_GET_INFO ||
+ opcode == MR_DCMD_CTRL_EVENT_GET) {
spin_lock_irqsave(&poll_aen_lock, flags);
megasas_poll_wait_aen = 0;
spin_unlock_irqrestore(&poll_aen_lock, flags);
}
+ /* FW has an updated PD sequence */
+ if ((opcode == MR_DCMD_SYSTEM_PD_MAP_GET_INFO) &&
+ (cmd->frame->dcmd.mbox.b[0] == 1)) {
+
+ spin_lock_irqsave(instance->host->host_lock, flags);
+ status = cmd->frame->hdr.cmd_status;
+ instance->jbod_seq_cmd = NULL;
+ megasas_return_cmd(instance, cmd);
+
+ if (status == MFI_STAT_OK) {
+ instance->pd_seq_map_id++;
+ /* Re-register a pd sync seq num cmd */
+ if (megasas_sync_pd_seq_num(instance, true))
+ instance->use_seqnum_jbod_fp = false;
+ } else
+ instance->use_seqnum_jbod_fp = false;
+
+ spin_unlock_irqrestore(instance->host->host_lock, flags);
+ break;
+ }
+
/*
* See if got an event notification
*/
- if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_WAIT)
+ if (opcode == MR_DCMD_CTRL_EVENT_WAIT)
megasas_service_aen(instance, cmd);
else
megasas_complete_int_cmd(instance, cmd);
@@ -2285,15 +3803,16 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
break;
default:
- printk("megasas: Unknown command completed! [0x%X]\n",
+ dev_info(&instance->pdev->dev, "Unknown command completed! [0x%X]\n",
hdr->cmd);
+ megasas_complete_int_cmd(instance, cmd);
break;
}
}
/**
* megasas_issue_pending_cmds_again - issue all pending cmds
- * in FW again because of the fw reset
+ * in FW again because of the fw reset
* @instance: Adapter soft state
*/
static inline void
@@ -2311,44 +3830,43 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
spin_unlock_irqrestore(&instance->hba_lock, flags);
while (!list_empty(&clist_local)) {
- cmd = list_entry((&clist_local)->next,
+ cmd = list_entry((&clist_local)->next,
struct megasas_cmd, list);
list_del_init(&cmd->list);
if (cmd->sync_cmd || cmd->scmd) {
- printk(KERN_NOTICE "megaraid_sas: command %p, %p:%d"
- "detected to be pending while HBA reset.\n",
+ dev_notice(&instance->pdev->dev, "command %p, %p:%d"
+ "detected to be pending while HBA reset\n",
cmd, cmd->scmd, cmd->sync_cmd);
cmd->retry_for_fw_reset++;
if (cmd->retry_for_fw_reset == 3) {
- printk(KERN_NOTICE "megaraid_sas: cmd %p, %p:%d"
+ dev_notice(&instance->pdev->dev, "cmd %p, %p:%d"
"was tried multiple times during reset."
"Shutting down the HBA\n",
cmd, cmd->scmd, cmd->sync_cmd);
+ instance->instancet->disable_intr(instance);
+ atomic_set(&instance->fw_reset_no_pci_access, 1);
megaraid_sas_kill_hba(instance);
-
- instance->adprecovery =
- MEGASAS_HW_CRITICAL_ERROR;
return;
}
}
if (cmd->sync_cmd == 1) {
if (cmd->scmd) {
- printk(KERN_NOTICE "megaraid_sas: unexpected"
+ dev_notice(&instance->pdev->dev, "unexpected"
"cmd attached to internal command!\n");
}
- printk(KERN_NOTICE "megasas: %p synchronous cmd"
+ dev_notice(&instance->pdev->dev, "%p synchronous cmd"
"on the internal reset queue,"
"issue it again.\n", cmd);
- cmd->cmd_status = ENODATA;
+ cmd->cmd_status_drv = DCMD_INIT;
instance->instancet->fire_cmd(instance,
- cmd->frame_phys_addr ,
+ cmd->frame_phys_addr,
0, instance->reg_set);
} else if (cmd->scmd) {
- printk(KERN_NOTICE "megasas: %p scsi cmd [%02x]"
+ dev_notice(&instance->pdev->dev, "%p scsi cmd [%02x]"
"detected on the internal queue, issue again.\n",
cmd, cmd->scmd->cmnd[0]);
@@ -2357,22 +3875,22 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
cmd->frame_phys_addr,
cmd->frame_count-1, instance->reg_set);
} else {
- printk(KERN_NOTICE "megasas: %p unexpected cmd on the"
+ dev_notice(&instance->pdev->dev, "%p unexpected cmd on the"
"internal reset defer list while re-issue!!\n",
cmd);
}
}
if (instance->aen_cmd) {
- printk(KERN_NOTICE "megaraid_sas: aen_cmd in def process\n");
+ dev_notice(&instance->pdev->dev, "aen_cmd in def process\n");
megasas_return_cmd(instance, instance->aen_cmd);
- instance->aen_cmd = NULL;
+ instance->aen_cmd = NULL;
}
/*
- * Initiate AEN (Asynchronous Event Notification)
- */
+ * Initiate AEN (Asynchronous Event Notification)
+ */
seq_num = instance->last_seq_num;
class_locale.members.reserved = 0;
class_locale.members.locale = MR_EVT_LOCALE_ALL;
@@ -2381,7 +3899,7 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
megasas_register_aen(instance, seq_num, class_locale.word);
}
-/**
+/*
* Move the internal reset pending commands to a deferred queue.
*
* We move the commands pending at internal reset time to a
@@ -2389,27 +3907,27 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
* completion of the internal reset sequence. if the internal reset
* did not complete in time, the kernel reset handler would flush
* these commands.
- **/
+ */
static void
megasas_internal_reset_defer_cmds(struct megasas_instance *instance)
{
struct megasas_cmd *cmd;
int i;
- u32 max_cmd = instance->max_fw_cmds;
+ u16 max_cmd = instance->max_fw_cmds;
u32 defer_index;
unsigned long flags;
- defer_index = 0;
- spin_lock_irqsave(&instance->cmd_pool_lock, flags);
+ defer_index = 0;
+ spin_lock_irqsave(&instance->mfi_pool_lock, flags);
for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i];
if (cmd->sync_cmd == 1 || cmd->scmd) {
- printk(KERN_NOTICE "megasas: moving cmd[%d]:%p:%d:%p"
+ dev_notice(&instance->pdev->dev, "moving cmd[%d]:%p:%d:%p"
"on the defer queue as internal\n",
defer_index, cmd, cmd->sync_cmd, cmd->scmd);
if (!list_empty(&cmd->list)) {
- printk(KERN_NOTICE "megaraid_sas: ERROR while"
+ dev_notice(&instance->pdev->dev, "ERROR while"
" moving this cmd:%p, %d %p, it was"
"discovered on some list?\n",
cmd, cmd->sync_cmd, cmd->scmd);
@@ -2421,7 +3939,7 @@ megasas_internal_reset_defer_cmds(struct megasas_instance *instance)
&instance->internal_reset_pending_q);
}
}
- spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
+ spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
}
@@ -2433,39 +3951,39 @@ process_fw_state_change_wq(struct work_struct *work)
u32 wait;
unsigned long flags;
- if (instance->adprecovery != MEGASAS_ADPRESET_SM_INFAULT) {
- printk(KERN_NOTICE "megaraid_sas: error, recovery st %x \n",
- instance->adprecovery);
+ if (atomic_read(&instance->adprecovery) != MEGASAS_ADPRESET_SM_INFAULT) {
+ dev_notice(&instance->pdev->dev, "error, recovery st %x\n",
+ atomic_read(&instance->adprecovery));
return ;
}
- if (instance->adprecovery == MEGASAS_ADPRESET_SM_INFAULT) {
- printk(KERN_NOTICE "megaraid_sas: FW detected to be in fault"
+ if (atomic_read(&instance->adprecovery) == MEGASAS_ADPRESET_SM_INFAULT) {
+ dev_notice(&instance->pdev->dev, "FW detected to be in fault"
"state, restarting it...\n");
- instance->instancet->disable_intr(instance->reg_set);
+ instance->instancet->disable_intr(instance);
atomic_set(&instance->fw_outstanding, 0);
atomic_set(&instance->fw_reset_no_pci_access, 1);
instance->instancet->adp_reset(instance, instance->reg_set);
- atomic_set(&instance->fw_reset_no_pci_access, 0 );
+ atomic_set(&instance->fw_reset_no_pci_access, 0);
- printk(KERN_NOTICE "megaraid_sas: FW restarted successfully,"
+ dev_notice(&instance->pdev->dev, "FW restarted successfully,"
"initiating next stage...\n");
- printk(KERN_NOTICE "megaraid_sas: HBA recovery state machine,"
+ dev_notice(&instance->pdev->dev, "HBA recovery state machine,"
"state 2 starting...\n");
- /*waitting for about 20 second before start the second init*/
+ /* waiting for about 20 second before start the second init */
for (wait = 0; wait < 30; wait++) {
msleep(1000);
}
- if (megasas_transition_to_ready(instance)) {
- printk(KERN_NOTICE "megaraid_sas:adapter not ready\n");
+ if (megasas_transition_to_ready(instance, 1)) {
+ dev_notice(&instance->pdev->dev, "adapter not ready\n");
+ atomic_set(&instance->fw_reset_no_pci_access, 1);
megaraid_sas_kill_hba(instance);
- instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
return ;
}
@@ -2482,22 +4000,21 @@ process_fw_state_change_wq(struct work_struct *work)
megasas_issue_init_mfi(instance);
spin_lock_irqsave(&instance->hba_lock, flags);
- instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
+ atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL);
spin_unlock_irqrestore(&instance->hba_lock, flags);
- instance->instancet->enable_intr(instance->reg_set);
+ instance->instancet->enable_intr(instance);
megasas_issue_pending_cmds_again(instance);
instance->issuepend_done = 1;
}
- return ;
}
/**
* megasas_deplete_reply_queue - Processes all completed commands
* @instance: Adapter soft state
* @alt_status: Alternate status to be returned to
- * SCSI mid-layer instead of the status
- * returned by the FW
+ * SCSI mid-layer instead of the status
+ * returned by the FW
* Note: this must be called with hba lock held
*/
static int
@@ -2507,16 +4024,13 @@ megasas_deplete_reply_queue(struct megasas_instance *instance,
u32 mfiStatus;
u32 fw_state;
- if ((mfiStatus = instance->instancet->check_reset(instance,
- instance->reg_set)) == 1) {
+ if (instance->instancet->check_reset(instance, instance->reg_set) == 1)
return IRQ_HANDLED;
- }
- if ((mfiStatus = instance->instancet->clear_intr(
- instance->reg_set)
- ) == 0) {
+ mfiStatus = instance->instancet->clear_intr(instance);
+ if (mfiStatus == 0) {
/* Hardware may not set outbound_intr_status in MSI-X mode */
- if (!instance->msi_flag)
+ if (!instance->msix_vectors)
return IRQ_NONE;
}
@@ -2524,16 +4038,16 @@ megasas_deplete_reply_queue(struct megasas_instance *instance,
if ((mfiStatus & MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE)) {
fw_state = instance->instancet->read_fw_status_reg(
- instance->reg_set) & MFI_STATE_MASK;
+ instance) & MFI_STATE_MASK;
if (fw_state != MFI_STATE_FAULT) {
- printk(KERN_NOTICE "megaraid_sas: fw state:%x\n",
+ dev_notice(&instance->pdev->dev, "fw state:%x\n",
fw_state);
}
if ((fw_state == MFI_STATE_FAULT) &&
(instance->disableOnlineCtrlReset == 0)) {
- printk(KERN_NOTICE "megaraid_sas: wait adp restart\n");
+ dev_notice(&instance->pdev->dev, "wait adp restart\n");
if ((instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS1064R) ||
@@ -2543,25 +4057,25 @@ megasas_deplete_reply_queue(struct megasas_instance *instance,
PCI_DEVICE_ID_LSI_VERDE_ZCR)) {
*instance->consumer =
- MEGASAS_ADPRESET_INPROG_SIGN;
+ cpu_to_le32(MEGASAS_ADPRESET_INPROG_SIGN);
}
- instance->instancet->disable_intr(instance->reg_set);
- instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT;
+ instance->instancet->disable_intr(instance);
+ atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
instance->issuepend_done = 0;
atomic_set(&instance->fw_outstanding, 0);
megasas_internal_reset_defer_cmds(instance);
- printk(KERN_NOTICE "megasas: fwState=%x, stage:%d\n",
- fw_state, instance->adprecovery);
+ dev_notice(&instance->pdev->dev, "fwState=%x, stage:%d\n",
+ fw_state, atomic_read(&instance->adprecovery));
schedule_work(&instance->work_init);
return IRQ_HANDLED;
} else {
- printk(KERN_NOTICE "megasas: fwstate:%x, dis_OCR=%x\n",
+ dev_notice(&instance->pdev->dev, "fwstate:%x, dis_OCR=%x\n",
fw_state, instance->disableOnlineCtrlReset);
}
}
@@ -2569,23 +4083,24 @@ megasas_deplete_reply_queue(struct megasas_instance *instance,
tasklet_schedule(&instance->isr_tasklet);
return IRQ_HANDLED;
}
+
/**
* megasas_isr - isr entry point
+ * @irq: IRQ number
+ * @devp: IRQ context address
*/
static irqreturn_t megasas_isr(int irq, void *devp)
{
- struct megasas_instance *instance;
+ struct megasas_irq_context *irq_context = devp;
+ struct megasas_instance *instance = irq_context->instance;
unsigned long flags;
- irqreturn_t rc;
+ irqreturn_t rc;
- if (atomic_read(
- &(((struct megasas_instance *)devp)->fw_reset_no_pci_access)))
+ if (atomic_read(&instance->fw_reset_no_pci_access))
return IRQ_HANDLED;
- instance = (struct megasas_instance *)devp;
-
spin_lock_irqsave(&instance->hba_lock, flags);
- rc = megasas_deplete_reply_queue(instance, DID_OK);
+ rc = megasas_deplete_reply_queue(instance, DID_OK);
spin_unlock_irqrestore(&instance->hba_lock, flags);
return rc;
@@ -2594,6 +4109,7 @@ static irqreturn_t megasas_isr(int irq, void *devp)
/**
* megasas_transition_to_ready - Move the FW to READY state
* @instance: Adapter soft state
+ * @ocr: Adapter reset state
*
* During the initialization, FW passes can potentially be in any one of
* several possible states. If the FW in operational, waiting-for-handshake
@@ -2601,33 +4117,37 @@ static irqreturn_t megasas_isr(int irq, void *devp)
* has to wait for the ready state.
*/
int
-megasas_transition_to_ready(struct megasas_instance* instance)
+megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
{
int i;
u8 max_wait;
u32 fw_state;
- u32 cur_state;
u32 abs_state, curr_abs_state;
- fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK;
+ abs_state = instance->instancet->read_fw_status_reg(instance);
+ fw_state = abs_state & MFI_STATE_MASK;
if (fw_state != MFI_STATE_READY)
- printk(KERN_INFO "megasas: Waiting for FW to come to ready"
+ dev_info(&instance->pdev->dev, "Waiting for FW to come to ready"
" state\n");
while (fw_state != MFI_STATE_READY) {
- abs_state =
- instance->instancet->read_fw_status_reg(instance->reg_set);
-
switch (fw_state) {
case MFI_STATE_FAULT:
-
- printk(KERN_DEBUG "megasas: FW in FAULT state!!\n");
- max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_FAULT;
- break;
+ dev_printk(KERN_ERR, &instance->pdev->dev,
+ "FW in FAULT state, Fault code:0x%x subcode:0x%x func:%s\n",
+ abs_state & MFI_STATE_FAULT_CODE,
+ abs_state & MFI_STATE_FAULT_SUBCODE, __func__);
+ if (ocr) {
+ max_wait = MEGASAS_RESET_WAIT_TIME;
+ break;
+ } else {
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n");
+ megasas_dump_reg_set(instance->reg_set);
+ return -ENODEV;
+ }
case MFI_STATE_WAIT_HANDSHAKE:
/*
@@ -2637,19 +4157,16 @@ megasas_transition_to_ready(struct megasas_instance* instance)
PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
- (instance->pdev->device ==
- PCI_DEVICE_ID_LSI_FUSION)) {
+ (instance->adapter_type != MFI_SERIES))
writel(
MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
&instance->reg_set->doorbell);
- } else {
+ else
writel(
MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
&instance->reg_set->inbound_doorbell);
- }
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_WAIT_HANDSHAKE;
break;
case MFI_STATE_BOOT_MESSAGE_PENDING:
@@ -2657,35 +4174,33 @@ megasas_transition_to_ready(struct megasas_instance* instance)
PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
- (instance->pdev->device ==
- PCI_DEVICE_ID_LSI_FUSION)) {
+ (instance->adapter_type != MFI_SERIES))
writel(MFI_INIT_HOTPLUG,
&instance->reg_set->doorbell);
- } else
+ else
writel(MFI_INIT_HOTPLUG,
&instance->reg_set->inbound_doorbell);
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_BOOT_MESSAGE_PENDING;
break;
case MFI_STATE_OPERATIONAL:
/*
* Bring it to READY state; assuming max wait 10 secs
*/
- instance->instancet->disable_intr(instance->reg_set);
+ instance->instancet->disable_intr(instance);
if ((instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device ==
PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
- (instance->pdev->device
- == PCI_DEVICE_ID_LSI_FUSION)) {
+ (instance->adapter_type != MFI_SERIES)) {
writel(MFI_RESET_FLAGS,
&instance->reg_set->doorbell);
- if (instance->pdev->device ==
- PCI_DEVICE_ID_LSI_FUSION) {
+
+ if (instance->adapter_type != MFI_SERIES) {
for (i = 0; i < (10 * 1000); i += 20) {
- if (readl(
+ if (megasas_readl(
+ instance,
&instance->
reg_set->
doorbell) & 1)
@@ -2699,7 +4214,6 @@ megasas_transition_to_ready(struct megasas_instance* instance)
&instance->reg_set->inbound_doorbell);
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_OPERATIONAL;
break;
case MFI_STATE_UNDEFINED:
@@ -2707,51 +4221,45 @@ megasas_transition_to_ready(struct megasas_instance* instance)
* This state should not last for more than 2 seconds
*/
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_UNDEFINED;
break;
case MFI_STATE_BB_INIT:
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_BB_INIT;
break;
case MFI_STATE_FW_INIT:
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_FW_INIT;
break;
case MFI_STATE_FW_INIT_2:
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_FW_INIT_2;
break;
case MFI_STATE_DEVICE_SCAN:
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_DEVICE_SCAN;
break;
case MFI_STATE_FLUSH_CACHE:
max_wait = MEGASAS_RESET_WAIT_TIME;
- cur_state = MFI_STATE_FLUSH_CACHE;
break;
default:
- printk(KERN_DEBUG "megasas: Unknown state 0x%x\n",
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Unknown state 0x%x\n",
fw_state);
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n");
+ megasas_dump_reg_set(instance->reg_set);
return -ENODEV;
}
/*
* The cur_state should not last for more than max_wait secs
*/
- for (i = 0; i < (max_wait * 1000); i++) {
- fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) &
- MFI_STATE_MASK ;
- curr_abs_state =
- instance->instancet->read_fw_status_reg(instance->reg_set);
+ for (i = 0; i < max_wait * 50; i++) {
+ curr_abs_state = instance->instancet->
+ read_fw_status_reg(instance);
if (abs_state == curr_abs_state) {
- msleep(1);
+ msleep(20);
} else
break;
}
@@ -2760,12 +4268,17 @@ megasas_transition_to_ready(struct megasas_instance* instance)
* Return error if fw_state hasn't changed after max_wait
*/
if (curr_abs_state == abs_state) {
- printk(KERN_DEBUG "FW state [%d] hasn't changed "
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "FW state [%d] hasn't changed "
"in %d secs\n", fw_state, max_wait);
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n");
+ megasas_dump_reg_set(instance->reg_set);
return -ENODEV;
}
+
+ abs_state = curr_abs_state;
+ fw_state = curr_abs_state & MFI_STATE_MASK;
}
- printk(KERN_INFO "megasas: FW now in Ready state\n");
+ dev_info(&instance->pdev->dev, "FW now in Ready state\n");
return 0;
}
@@ -2777,7 +4290,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
static void megasas_teardown_frame_pool(struct megasas_instance *instance)
{
int i;
- u32 max_cmd = instance->max_mfi_cmds;
+ u16 max_cmd = instance->max_mfi_cmds;
struct megasas_cmd *cmd;
if (!instance->frame_dma_pool)
@@ -2791,19 +4304,19 @@ static void megasas_teardown_frame_pool(struct megasas_instance *instance)
cmd = instance->cmd_list[i];
if (cmd->frame)
- pci_pool_free(instance->frame_dma_pool, cmd->frame,
+ dma_pool_free(instance->frame_dma_pool, cmd->frame,
cmd->frame_phys_addr);
if (cmd->sense)
- pci_pool_free(instance->sense_dma_pool, cmd->sense,
+ dma_pool_free(instance->sense_dma_pool, cmd->sense,
cmd->sense_phys_addr);
}
/*
* Now destroy the pool itself
*/
- pci_pool_destroy(instance->frame_dma_pool);
- pci_pool_destroy(instance->sense_dma_pool);
+ dma_pool_destroy(instance->frame_dma_pool);
+ dma_pool_destroy(instance->sense_dma_pool);
instance->frame_dma_pool = NULL;
instance->sense_dma_pool = NULL;
@@ -2821,58 +4334,46 @@ static void megasas_teardown_frame_pool(struct megasas_instance *instance)
static int megasas_create_frame_pool(struct megasas_instance *instance)
{
int i;
- u32 max_cmd;
- u32 sge_sz;
- u32 sgl_sz;
- u32 total_sz;
+ u16 max_cmd;
u32 frame_count;
struct megasas_cmd *cmd;
max_cmd = instance->max_mfi_cmds;
/*
- * Size of our frame is 64 bytes for MFI frame, followed by max SG
- * elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer
- */
- sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
- sizeof(struct megasas_sge32);
-
- if (instance->flag_ieee) {
- sge_sz = sizeof(struct megasas_sge_skinny);
- }
-
- /*
- * Calculated the number of 64byte frames required for SGL
- */
- sgl_sz = sge_sz * instance->max_num_sge;
- frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE - 1) / MEGAMFI_FRAME_SIZE;
- frame_count = 15;
-
- /*
- * We need one extra frame for the MFI command
+ * For MFI controllers.
+ * max_num_sge = 60
+ * max_sge_sz = 16 byte (sizeof megasas_sge_skinny)
+ * Total 960 byte (15 MFI frame of 64 byte)
+ *
+ * Fusion adapter require only 3 extra frame.
+ * max_num_sge = 16 (defined as MAX_IOCTL_SGE)
+ * max_sge_sz = 12 byte (sizeof megasas_sge64)
+ * Total 192 byte (3 MFI frame of 64 byte)
*/
- frame_count++;
-
- total_sz = MEGAMFI_FRAME_SIZE * frame_count;
+ frame_count = (instance->adapter_type == MFI_SERIES) ?
+ (15 + 1) : (3 + 1);
+ instance->mfi_frame_size = MEGAMFI_FRAME_SIZE * frame_count;
/*
* Use DMA pool facility provided by PCI layer
*/
- instance->frame_dma_pool = pci_pool_create("megasas frame pool",
- instance->pdev, total_sz, 64,
- 0);
+ instance->frame_dma_pool = dma_pool_create("megasas frame pool",
+ &instance->pdev->dev,
+ instance->mfi_frame_size, 256, 0);
if (!instance->frame_dma_pool) {
- printk(KERN_DEBUG "megasas: failed to setup frame pool\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup frame pool\n");
return -ENOMEM;
}
- instance->sense_dma_pool = pci_pool_create("megasas sense pool",
- instance->pdev, 128, 4, 0);
+ instance->sense_dma_pool = dma_pool_create("megasas sense pool",
+ &instance->pdev->dev, 128,
+ 4, 0);
if (!instance->sense_dma_pool) {
- printk(KERN_DEBUG "megasas: failed to setup sense pool\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup sense pool\n");
- pci_pool_destroy(instance->frame_dma_pool);
+ dma_pool_destroy(instance->frame_dma_pool);
instance->frame_dma_pool = NULL;
return -ENOMEM;
@@ -2887,10 +4388,10 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
cmd = instance->cmd_list[i];
- cmd->frame = pci_pool_alloc(instance->frame_dma_pool,
+ cmd->frame = dma_pool_zalloc(instance->frame_dma_pool,
GFP_KERNEL, &cmd->frame_phys_addr);
- cmd->sense = pci_pool_alloc(instance->sense_dma_pool,
+ cmd->sense = dma_pool_alloc(instance->sense_dma_pool,
GFP_KERNEL, &cmd->sense_phys_addr);
/*
@@ -2898,14 +4399,15 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
* whatever has been allocated
*/
if (!cmd->frame || !cmd->sense) {
- printk(KERN_DEBUG "megasas: pci_pool_alloc failed \n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "dma_pool_alloc failed\n");
megasas_teardown_frame_pool(instance);
return -ENOMEM;
}
- memset(cmd->frame, 0, total_sz);
- cmd->frame->io.context = cmd->index;
+ cmd->frame->io.context = cpu_to_le32(cmd->index);
cmd->frame->io.pad_0 = 0;
+ if ((instance->adapter_type == MFI_SERIES) && reset_devices)
+ cmd->frame->hdr.cmd = MFI_CMD_INVALID;
}
return 0;
@@ -2918,6 +4420,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
void megasas_free_cmds(struct megasas_instance *instance)
{
int i;
+
/* First free the MFI frame pool */
megasas_teardown_frame_pool(instance);
@@ -2955,7 +4458,7 @@ int megasas_alloc_cmds(struct megasas_instance *instance)
{
int i;
int j;
- u32 max_cmd;
+ u16 max_cmd;
struct megasas_cmd *cmd;
max_cmd = instance->max_mfi_cmds;
@@ -2968,12 +4471,10 @@ int megasas_alloc_cmds(struct megasas_instance *instance)
instance->cmd_list = kcalloc(max_cmd, sizeof(struct megasas_cmd*), GFP_KERNEL);
if (!instance->cmd_list) {
- printk(KERN_DEBUG "megasas: out of memory\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "out of memory\n");
return -ENOMEM;
}
- memset(instance->cmd_list, 0, sizeof(struct megasas_cmd *) *max_cmd);
-
for (i = 0; i < max_cmd; i++) {
instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd),
GFP_KERNEL);
@@ -2990,9 +4491,6 @@ int megasas_alloc_cmds(struct megasas_instance *instance)
}
}
- /*
- * Add all the commands to command pool (instance->cmd_pool)
- */
for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i];
memset(cmd, 0, sizeof(struct megasas_cmd));
@@ -3007,14 +4505,112 @@ int megasas_alloc_cmds(struct megasas_instance *instance)
* Create a frame pool and assign one frame to each cmd
*/
if (megasas_create_frame_pool(instance)) {
- printk(KERN_DEBUG "megasas: Error creating frame DMA pool\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error creating frame DMA pool\n");
megasas_free_cmds(instance);
+ return -ENOMEM;
}
return 0;
}
/*
+ * dcmd_timeout_ocr_possible - Check if OCR is possible based on Driver/FW state.
+ * @instance: Adapter soft state
+ *
+ * Return 0 for only Fusion adapter, if driver load/unload is not in progress
+ * or FW is not under OCR.
+ */
+inline int
+dcmd_timeout_ocr_possible(struct megasas_instance *instance) {
+
+ if (instance->adapter_type == MFI_SERIES)
+ return KILL_ADAPTER;
+ else if (instance->unload ||
+ test_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE,
+ &instance->reset_flags))
+ return IGNORE_TIMEOUT;
+ else
+ return INITIATE_OCR;
+}
+
+static void
+megasas_get_pd_info(struct megasas_instance *instance, struct scsi_device *sdev)
+{
+ int ret;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+ u16 device_id = 0;
+
+ device_id = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id;
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_err(&instance->pdev->dev, "Failed to get cmd %s\n", __func__);
+ return;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset(instance->pd_info, 0, sizeof(*instance->pd_info));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->mbox.s[0] = cpu_to_le16(device_id);
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0xFF;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_PD_INFO));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_PD_GET_INFO);
+
+ megasas_set_dma_settings(instance, dcmd, instance->pd_info_h,
+ sizeof(struct MR_PD_INFO));
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
+
+ switch (ret) {
+ case DCMD_SUCCESS:
+ mr_device_priv_data = sdev->hostdata;
+ le16_to_cpus((u16 *)&instance->pd_info->state.ddf.pdType);
+ mr_device_priv_data->interface_type =
+ instance->pd_info->state.ddf.pdType.intf;
+ break;
+
+ case DCMD_TIMEOUT:
+
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ break;
+ }
+
+ if (ret != DCMD_TIMEOUT)
+ megasas_return_cmd(instance, cmd);
+
+ return;
+}
+/*
* megasas_get_pd_list_info - Returns FW's pd_list structure
* @instance: Adapter soft state
* @pd_list: pd_list structure
@@ -3031,77 +4627,120 @@ megasas_get_pd_list(struct megasas_instance *instance)
struct megasas_dcmd_frame *dcmd;
struct MR_PD_LIST *ci;
struct MR_PD_ADDRESS *pd_addr;
- dma_addr_t ci_h = 0;
+
+ if (instance->pd_list_not_supported) {
+ dev_info(&instance->pdev->dev, "MR_DCMD_PD_LIST_QUERY "
+ "not supported by firmware\n");
+ return ret;
+ }
+
+ ci = instance->pd_list_buf;
cmd = megasas_get_cmd(instance);
if (!cmd) {
- printk(KERN_DEBUG "megasas (get_pd_list): Failed to get cmd\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "(get_pd_list): Failed to get cmd\n");
return -ENOMEM;
}
dcmd = &cmd->frame->dcmd;
- ci = pci_alloc_consistent(instance->pdev,
- MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), &ci_h);
-
- if (!ci) {
- printk(KERN_DEBUG "Failed to alloc mem for pd_list\n");
- megasas_return_cmd(instance, cmd);
- return -ENOMEM;
- }
-
memset(ci, 0, sizeof(*ci));
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
dcmd->mbox.b[1] = 0;
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
- dcmd->opcode = MR_DCMD_PD_LIST_QUERY;
- dcmd->sgl.sge32[0].phys_addr = ci_h;
- dcmd->sgl.sge32[0].length = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
+ dcmd->data_xfer_len = cpu_to_le32(MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_PD_LIST_QUERY);
- if (!megasas_issue_polled(instance, cmd)) {
- ret = 0;
- } else {
- ret = -1;
- }
+ megasas_set_dma_settings(instance, dcmd, instance->pd_list_buf_h,
+ (MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST)));
- /*
- * the following function will get the instance PD LIST.
- */
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance, cmd,
+ MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
+
+ switch (ret) {
+ case DCMD_FAILED:
+ dev_info(&instance->pdev->dev, "MR_DCMD_PD_LIST_QUERY "
+ "failed/not supported by firmware\n");
+
+ if (instance->adapter_type != MFI_SERIES)
+ megaraid_sas_kill_hba(instance);
+ else
+ instance->pd_list_not_supported = 1;
+ break;
+ case DCMD_TIMEOUT:
- pd_addr = ci->addr;
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ /*
+ * DCMD failed from AEN path.
+ * AEN path already hold reset_mutex to avoid PCI access
+ * while OCR is in progress.
+ */
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d \n",
+ __func__, __LINE__);
+ break;
+ }
- if ( ret == 0 &&
- (ci->count <
- (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) {
+ break;
- memset(instance->pd_list, 0,
- MEGASAS_MAX_PD * sizeof(struct megasas_pd_list));
+ case DCMD_SUCCESS:
+ pd_addr = ci->addr;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev, "%s, sysPD count: 0x%x\n",
+ __func__, le32_to_cpu(ci->count));
- for (pd_index = 0; pd_index < ci->count; pd_index++) {
+ if ((le32_to_cpu(ci->count) >
+ (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL)))
+ break;
- instance->pd_list[pd_addr->deviceId].tid =
- pd_addr->deviceId;
- instance->pd_list[pd_addr->deviceId].driveType =
- pd_addr->scsiDevType;
- instance->pd_list[pd_addr->deviceId].driveState =
- MR_PD_STATE_SYSTEM;
+ memset(instance->local_pd_list, 0,
+ MEGASAS_MAX_PD * sizeof(struct megasas_pd_list));
+
+ for (pd_index = 0; pd_index < le32_to_cpu(ci->count); pd_index++) {
+ instance->local_pd_list[le16_to_cpu(pd_addr->deviceId)].tid =
+ le16_to_cpu(pd_addr->deviceId);
+ instance->local_pd_list[le16_to_cpu(pd_addr->deviceId)].driveType =
+ pd_addr->scsiDevType;
+ instance->local_pd_list[le16_to_cpu(pd_addr->deviceId)].driveState =
+ MR_PD_STATE_SYSTEM;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "PD%d: targetID: 0x%03x deviceType:0x%x\n",
+ pd_index, le16_to_cpu(pd_addr->deviceId),
+ pd_addr->scsiDevType);
pd_addr++;
}
+
+ memcpy(instance->pd_list, instance->local_pd_list,
+ sizeof(instance->pd_list));
+ break;
+
}
- pci_free_consistent(instance->pdev,
- MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
- ci, ci_h);
- megasas_return_cmd(instance, cmd);
+ if (ret != DCMD_TIMEOUT)
+ megasas_return_cmd(instance, cmd);
return ret;
}
@@ -3123,81 +4762,510 @@ megasas_get_ld_list(struct megasas_instance *instance)
struct megasas_dcmd_frame *dcmd;
struct MR_LD_LIST *ci;
dma_addr_t ci_h = 0;
+ u32 ld_count;
+
+ ci = instance->ld_list_buf;
+ ci_h = instance->ld_list_buf_h;
cmd = megasas_get_cmd(instance);
if (!cmd) {
- printk(KERN_DEBUG "megasas_get_ld_list: Failed to get cmd\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_list: Failed to get cmd\n");
return -ENOMEM;
}
dcmd = &cmd->frame->dcmd;
- ci = pci_alloc_consistent(instance->pdev,
- sizeof(struct MR_LD_LIST),
- &ci_h);
+ memset(ci, 0, sizeof(*ci));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ if (instance->supportmax256vd)
+ dcmd->mbox.b[0] = 1;
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_LD_LIST));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_GET_LIST);
+ dcmd->pad_0 = 0;
+
+ megasas_set_dma_settings(instance, dcmd, ci_h,
+ sizeof(struct MR_LD_LIST));
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance, cmd,
+ MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
- if (!ci) {
- printk(KERN_DEBUG "Failed to alloc mem in get_ld_list\n");
+ ld_count = le32_to_cpu(ci->ldCount);
+
+ switch (ret) {
+ case DCMD_FAILED:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case DCMD_TIMEOUT:
+
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ /*
+ * DCMD failed from AEN path.
+ * AEN path already hold reset_mutex to avoid PCI access
+ * while OCR is in progress.
+ */
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ break;
+
+ case DCMD_SUCCESS:
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n",
+ __func__, ld_count);
+
+ if (ld_count > instance->fw_supported_vd_count)
+ break;
+
+ memset(instance->ld_ids, 0xff, MAX_LOGICAL_DRIVES_EXT);
+
+ for (ld_index = 0; ld_index < ld_count; ld_index++) {
+ if (ci->ldList[ld_index].state != 0) {
+ ids = ci->ldList[ld_index].ref.targetId;
+ instance->ld_ids[ids] = ci->ldList[ld_index].ref.targetId;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "LD%d: targetID: 0x%03x\n",
+ ld_index, ids);
+ }
+ }
+
+ break;
+ }
+
+ if (ret != DCMD_TIMEOUT)
megasas_return_cmd(instance, cmd);
+
+ return ret;
+}
+
+/**
+ * megasas_ld_list_query - Returns FW's ld_list structure
+ * @instance: Adapter soft state
+ * @query_type: ld_list structure type
+ *
+ * Issues an internal command (DCMD) to get the FW's controller PD
+ * list structure. This information is mainly used to find out SYSTEM
+ * supported by the FW.
+ */
+static int
+megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
+{
+ int ret = 0, ld_index = 0, ids = 0;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_LD_TARGETID_LIST *ci;
+ dma_addr_t ci_h = 0;
+ u32 tgtid_count;
+
+ ci = instance->ld_targetid_list_buf;
+ ci_h = instance->ld_targetid_list_buf_h;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_warn(&instance->pdev->dev,
+ "megasas_ld_list_query: Failed to get cmd\n");
return -ENOMEM;
}
+ dcmd = &cmd->frame->dcmd;
+
memset(ci, 0, sizeof(*ci));
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+ dcmd->mbox.b[0] = query_type;
+ if (instance->supportmax256vd)
+ dcmd->mbox.b[2] = 1;
+
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
- dcmd->data_xfer_len = sizeof(struct MR_LD_LIST);
- dcmd->opcode = MR_DCMD_LD_GET_LIST;
- dcmd->sgl.sge32[0].phys_addr = ci_h;
- dcmd->sgl.sge32[0].length = sizeof(struct MR_LD_LIST);
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_LD_TARGETID_LIST));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_LIST_QUERY);
dcmd->pad_0 = 0;
- if (!megasas_issue_polled(instance, cmd)) {
- ret = 0;
+ megasas_set_dma_settings(instance, dcmd, ci_h,
+ sizeof(struct MR_LD_TARGETID_LIST));
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
+
+ switch (ret) {
+ case DCMD_FAILED:
+ dev_info(&instance->pdev->dev,
+ "DCMD not supported by firmware - %s %d\n",
+ __func__, __LINE__);
+ ret = megasas_get_ld_list(instance);
+ break;
+ case DCMD_TIMEOUT:
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ /*
+ * DCMD failed from AEN path.
+ * AEN path already hold reset_mutex to avoid PCI access
+ * while OCR is in progress.
+ */
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ break;
+ case DCMD_SUCCESS:
+ tgtid_count = le32_to_cpu(ci->count);
+
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n",
+ __func__, tgtid_count);
+
+ if ((tgtid_count > (instance->fw_supported_vd_count)))
+ break;
+
+ memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
+ for (ld_index = 0; ld_index < tgtid_count; ld_index++) {
+ ids = ci->targetId[ld_index];
+ instance->ld_ids[ids] = ci->targetId[ld_index];
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev, "LD%d: targetID: 0x%03x\n",
+ ld_index, ci->targetId[ld_index]);
+ }
+
+ break;
+ }
+
+ if (ret != DCMD_TIMEOUT)
+ megasas_return_cmd(instance, cmd);
+
+ return ret;
+}
+
+/**
+ * megasas_host_device_list_query
+ * dcmd.opcode - MR_DCMD_CTRL_DEVICE_LIST_GET
+ * dcmd.mbox - reserved
+ * dcmd.sge IN - ptr to return MR_HOST_DEVICE_LIST structure
+ * Desc: This DCMD will return the combined device list
+ * Status: MFI_STAT_OK - List returned successfully
+ * MFI_STAT_INVALID_CMD - Firmware support for the feature has been
+ * disabled
+ * @instance: Adapter soft state
+ * @is_probe: Driver probe check
+ * Return: 0 if DCMD succeeded
+ * non-zero if failed
+ */
+static int
+megasas_host_device_list_query(struct megasas_instance *instance,
+ bool is_probe)
+{
+ int ret, i, target_id;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_HOST_DEVICE_LIST *ci;
+ u32 count;
+ dma_addr_t ci_h;
+
+ ci = instance->host_device_list_buf;
+ ci_h = instance->host_device_list_buf_h;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_warn(&instance->pdev->dev,
+ "%s: failed to get cmd\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset(ci, 0, sizeof(*ci));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->mbox.b[0] = is_probe ? 0 : 1;
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(HOST_DEVICE_LIST_SZ);
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_DEVICE_LIST_GET);
+
+ megasas_set_dma_settings(instance, dcmd, ci_h, HOST_DEVICE_LIST_SZ);
+
+ if (!instance->mask_interrupts) {
+ ret = megasas_issue_blocked_cmd(instance, cmd,
+ MFI_IO_TIMEOUT_SECS);
} else {
- ret = -1;
+ ret = megasas_issue_polled(instance, cmd);
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
}
- /* the following function will get the instance PD LIST */
+ switch (ret) {
+ case DCMD_SUCCESS:
+ /* Fill the internal pd_list and ld_ids array based on
+ * targetIds returned by FW
+ */
+ count = le32_to_cpu(ci->count);
- if ((ret == 0) && (ci->ldCount <= MAX_LOGICAL_DRIVES)) {
- memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
+ if (count > (MEGASAS_MAX_PD + MAX_LOGICAL_DRIVES_EXT))
+ break;
- for (ld_index = 0; ld_index < ci->ldCount; ld_index++) {
- if (ci->ldList[ld_index].state != 0) {
- ids = ci->ldList[ld_index].ref.targetId;
- instance->ld_ids[ids] =
- ci->ldList[ld_index].ref.targetId;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev, "%s, Device count: 0x%x\n",
+ __func__, count);
+
+ memset(instance->local_pd_list, 0,
+ MEGASAS_MAX_PD * sizeof(struct megasas_pd_list));
+ memset(instance->ld_ids, 0xff, MAX_LOGICAL_DRIVES_EXT);
+ for (i = 0; i < count; i++) {
+ target_id = le16_to_cpu(ci->host_device_list[i].target_id);
+ if (ci->host_device_list[i].flags.u.bits.is_sys_pd) {
+ instance->local_pd_list[target_id].tid = target_id;
+ instance->local_pd_list[target_id].driveType =
+ ci->host_device_list[i].scsi_type;
+ instance->local_pd_list[target_id].driveState =
+ MR_PD_STATE_SYSTEM;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "Device %d: PD targetID: 0x%03x deviceType:0x%x\n",
+ i, target_id, ci->host_device_list[i].scsi_type);
+ } else {
+ instance->ld_ids[target_id] = target_id;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "Device %d: LD targetID: 0x%03x\n",
+ i, target_id);
}
}
+
+ memcpy(instance->pd_list, instance->local_pd_list,
+ sizeof(instance->pd_list));
+ break;
+
+ case DCMD_TIMEOUT:
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ break;
+ case DCMD_FAILED:
+ dev_err(&instance->pdev->dev,
+ "%s: MR_DCMD_CTRL_DEVICE_LIST_GET failed\n",
+ __func__);
+ break;
}
- pci_free_consistent(instance->pdev,
- sizeof(struct MR_LD_LIST),
- ci,
- ci_h);
+ if (ret != DCMD_TIMEOUT)
+ megasas_return_cmd(instance, cmd);
- megasas_return_cmd(instance, cmd);
return ret;
}
+/*
+ * megasas_update_ext_vd_details : Update details w.r.t Extended VD
+ * instance : Controller's instance
+*/
+static void megasas_update_ext_vd_details(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion;
+ u32 ventura_map_sz = 0;
+
+ fusion = instance->ctrl_context;
+ /* For MFI based controllers return dummy success */
+ if (!fusion)
+ return;
+
+ instance->supportmax256vd =
+ instance->ctrl_info_buf->adapterOperations3.supportMaxExtLDs;
+ /* Below is additional check to address future FW enhancement */
+ if (instance->ctrl_info_buf->max_lds > 64)
+ instance->supportmax256vd = 1;
+
+ instance->drv_supported_vd_count = MEGASAS_MAX_LD_CHANNELS
+ * MEGASAS_MAX_DEV_PER_CHANNEL;
+ instance->drv_supported_pd_count = MEGASAS_MAX_PD_CHANNELS
+ * MEGASAS_MAX_DEV_PER_CHANNEL;
+ if (instance->supportmax256vd) {
+ instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES_EXT;
+ instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
+ } else {
+ instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES;
+ instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
+ }
+
+ dev_info(&instance->pdev->dev,
+ "FW provided supportMaxExtLDs: %d\tmax_lds: %d\n",
+ instance->ctrl_info_buf->adapterOperations3.supportMaxExtLDs ? 1 : 0,
+ instance->ctrl_info_buf->max_lds);
+
+ if (instance->max_raid_mapsize) {
+ ventura_map_sz = instance->max_raid_mapsize *
+ MR_MIN_MAP_SIZE; /* 64k */
+ fusion->current_map_sz = ventura_map_sz;
+ fusion->max_map_sz = ventura_map_sz;
+ } else {
+ fusion->old_map_sz =
+ struct_size_t(struct MR_FW_RAID_MAP, ldSpanMap,
+ instance->fw_supported_vd_count);
+ fusion->new_map_sz = sizeof(struct MR_FW_RAID_MAP_EXT);
+
+ fusion->max_map_sz =
+ max(fusion->old_map_sz, fusion->new_map_sz);
+
+ if (instance->supportmax256vd)
+ fusion->current_map_sz = fusion->new_map_sz;
+ else
+ fusion->current_map_sz = fusion->old_map_sz;
+ }
+ /* irrespective of FW raid maps, driver raid map is constant */
+ fusion->drv_map_sz = sizeof(struct MR_DRV_RAID_MAP_ALL);
+}
+
+/*
+ * dcmd.opcode - MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES
+ * dcmd.hdr.length - number of bytes to read
+ * dcmd.sge - Ptr to MR_SNAPDUMP_PROPERTIES
+ * Desc: Fill in snapdump properties
+ * Status: MFI_STAT_OK- Command successful
+ */
+void megasas_get_snapdump_properties(struct megasas_instance *instance)
+{
+ int ret = 0;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_SNAPDUMP_PROPERTIES *ci;
+ dma_addr_t ci_h = 0;
+
+ ci = instance->snapdump_prop;
+ ci_h = instance->snapdump_prop_h;
+
+ if (!ci)
+ return;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_dbg(&instance->pdev->dev, "Failed to get a free cmd\n");
+ return;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset(ci, 0, sizeof(*ci));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_SNAPDUMP_PROPERTIES));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES);
+
+ megasas_set_dma_settings(instance, dcmd, ci_h,
+ sizeof(struct MR_SNAPDUMP_PROPERTIES));
+
+ if (!instance->mask_interrupts) {
+ ret = megasas_issue_blocked_cmd(instance, cmd,
+ MFI_IO_TIMEOUT_SECS);
+ } else {
+ ret = megasas_issue_polled(instance, cmd);
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ }
+
+ switch (ret) {
+ case DCMD_SUCCESS:
+ instance->snapdump_wait_time =
+ min_t(u8, ci->trigger_min_num_sec_before_ocr,
+ MEGASAS_MAX_SNAP_DUMP_WAIT_TIME);
+ break;
+
+ case DCMD_TIMEOUT:
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+
+ if (ret != DCMD_TIMEOUT)
+ megasas_return_cmd(instance, cmd);
+}
+
/**
- * megasas_get_controller_info - Returns FW's controller structure
+ * megasas_get_ctrl_info - Returns FW's controller structure
* @instance: Adapter soft state
- * @ctrl_info: Controller information structure
*
* Issues an internal command (DCMD) to get the FW's controller structure.
* This information is mainly used to find out the maximum IO transfer per
* command supported by the FW.
*/
-static int
-megasas_get_ctrl_info(struct megasas_instance *instance,
- struct megasas_ctrl_info *ctrl_info)
+int
+megasas_get_ctrl_info(struct megasas_instance *instance)
{
int ret = 0;
struct megasas_cmd *cmd;
@@ -3205,49 +5273,206 @@ megasas_get_ctrl_info(struct megasas_instance *instance,
struct megasas_ctrl_info *ci;
dma_addr_t ci_h = 0;
+ ci = instance->ctrl_info_buf;
+ ci_h = instance->ctrl_info_buf_h;
+
cmd = megasas_get_cmd(instance);
if (!cmd) {
- printk(KERN_DEBUG "megasas: Failed to get a free cmd\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a free cmd\n");
return -ENOMEM;
}
dcmd = &cmd->frame->dcmd;
- ci = pci_alloc_consistent(instance->pdev,
- sizeof(struct megasas_ctrl_info), &ci_h);
-
- if (!ci) {
- printk(KERN_DEBUG "Failed to alloc mem for ctrl info\n");
- megasas_return_cmd(instance, cmd);
- return -ENOMEM;
- }
-
memset(ci, 0, sizeof(*ci));
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = sizeof(struct megasas_ctrl_info);
- dcmd->opcode = MR_DCMD_CTRL_GET_INFO;
- dcmd->sgl.sge32[0].phys_addr = ci_h;
- dcmd->sgl.sge32[0].length = sizeof(struct megasas_ctrl_info);
-
- if (!megasas_issue_polled(instance, cmd)) {
- ret = 0;
- memcpy(ctrl_info, ci, sizeof(struct megasas_ctrl_info));
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct megasas_ctrl_info));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_GET_INFO);
+ dcmd->mbox.b[0] = 1;
+
+ megasas_set_dma_settings(instance, dcmd, ci_h,
+ sizeof(struct megasas_ctrl_info));
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts) {
+ ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS);
} else {
- ret = -1;
+ ret = megasas_issue_polled(instance, cmd);
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
}
- pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info),
- ci, ci_h);
+ switch (ret) {
+ case DCMD_SUCCESS:
+ /* Save required controller information in
+ * CPU endianness format.
+ */
+ le32_to_cpus((u32 *)&ci->properties.OnOffProperties);
+ le16_to_cpus((u16 *)&ci->properties.on_off_properties2);
+ le32_to_cpus((u32 *)&ci->adapterOperations2);
+ le32_to_cpus((u32 *)&ci->adapterOperations3);
+ le16_to_cpus((u16 *)&ci->adapter_operations4);
+ le32_to_cpus((u32 *)&ci->adapter_operations5);
+
+ /* Update the latest Ext VD info.
+ * From Init path, store current firmware details.
+ * From OCR path, detect any firmware properties changes.
+ * in case of Firmware upgrade without system reboot.
+ */
+ megasas_update_ext_vd_details(instance);
+ instance->support_seqnum_jbod_fp =
+ ci->adapterOperations3.useSeqNumJbodFP;
+ instance->support_morethan256jbod =
+ ci->adapter_operations4.support_pd_map_target_id;
+ instance->support_nvme_passthru =
+ ci->adapter_operations4.support_nvme_passthru;
+ instance->support_pci_lane_margining =
+ ci->adapter_operations5.support_pci_lane_margining;
+ instance->task_abort_tmo = ci->TaskAbortTO;
+ instance->max_reset_tmo = ci->MaxResetTO;
+
+ /*Check whether controller is iMR or MR */
+ instance->is_imr = (ci->memory_size ? 0 : 1);
+
+ instance->snapdump_wait_time =
+ (ci->properties.on_off_properties2.enable_snap_dump ?
+ MEGASAS_DEFAULT_SNAP_DUMP_WAIT_TIME : 0);
+
+ instance->enable_fw_dev_list =
+ ci->properties.on_off_properties2.enable_fw_dev_list;
+
+ dev_info(&instance->pdev->dev,
+ "controller type\t: %s(%dMB)\n",
+ instance->is_imr ? "iMR" : "MR",
+ le16_to_cpu(ci->memory_size));
+
+ instance->disableOnlineCtrlReset =
+ ci->properties.OnOffProperties.disableOnlineCtrlReset;
+ instance->secure_jbod_support =
+ ci->adapterOperations3.supportSecurityonJBOD;
+ dev_info(&instance->pdev->dev, "Online Controller Reset(OCR)\t: %s\n",
+ instance->disableOnlineCtrlReset ? "Disabled" : "Enabled");
+ dev_info(&instance->pdev->dev, "Secure JBOD support\t: %s\n",
+ instance->secure_jbod_support ? "Yes" : "No");
+ dev_info(&instance->pdev->dev, "NVMe passthru support\t: %s\n",
+ instance->support_nvme_passthru ? "Yes" : "No");
+ dev_info(&instance->pdev->dev,
+ "FW provided TM TaskAbort/Reset timeout\t: %d secs/%d secs\n",
+ instance->task_abort_tmo, instance->max_reset_tmo);
+ dev_info(&instance->pdev->dev, "JBOD sequence map support\t: %s\n",
+ instance->support_seqnum_jbod_fp ? "Yes" : "No");
+ dev_info(&instance->pdev->dev, "PCI Lane Margining support\t: %s\n",
+ instance->support_pci_lane_margining ? "Yes" : "No");
+
+ break;
+
+ case DCMD_TIMEOUT:
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ break;
+ case DCMD_FAILED:
+ megaraid_sas_kill_hba(instance);
+ break;
+
+ }
+
+ if (ret != DCMD_TIMEOUT)
+ megasas_return_cmd(instance, cmd);
+
+ return ret;
+}
+
+/*
+ * megasas_set_crash_dump_params - Sends address of crash dump DMA buffer
+ * to firmware
+ *
+ * @instance: Adapter soft state
+ * @crash_buf_state - tell FW to turn ON/OFF crash dump feature
+ MR_CRASH_BUF_TURN_OFF = 0
+ MR_CRASH_BUF_TURN_ON = 1
+ * @return 0 on success non-zero on failure.
+ * Issues an internal command (DCMD) to set parameters for crash dump feature.
+ * Driver will send address of crash dump DMA buffer and set mbox to tell FW
+ * that driver supports crash dump feature. This DCMD will be sent only if
+ * crash dump feature is supported by the FW.
+ *
+ */
+int megasas_set_crash_dump_params(struct megasas_instance *instance,
+ u8 crash_buf_state)
+{
+ int ret = 0;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_err(&instance->pdev->dev, "Failed to get a free cmd\n");
+ return -ENOMEM;
+ }
+
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+ dcmd->mbox.b[0] = crash_buf_state;
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_NONE;
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(CRASH_DMA_BUF_SIZE);
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SET_CRASH_DUMP_PARAMS);
+
+ megasas_set_dma_settings(instance, dcmd, instance->crash_dump_h,
+ CRASH_DMA_BUF_SIZE);
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
+
+ if (ret == DCMD_TIMEOUT) {
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ } else
+ megasas_return_cmd(instance, cmd);
- megasas_return_cmd(instance, cmd);
return ret;
}
@@ -3260,10 +5485,8 @@ megasas_get_ctrl_info(struct megasas_instance *instance,
static int
megasas_issue_init_mfi(struct megasas_instance *instance)
{
- u32 context;
-
+ __le32 context;
struct megasas_cmd *cmd;
-
struct megasas_init_frame *init_frame;
struct megasas_init_queue_info *initq_info;
dma_addr_t init_frame_h;
@@ -3291,29 +5514,32 @@ megasas_issue_init_mfi(struct megasas_instance *instance)
memset(initq_info, 0, sizeof(struct megasas_init_queue_info));
init_frame->context = context;
- initq_info->reply_queue_entries = instance->max_fw_cmds + 1;
- initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h;
+ initq_info->reply_queue_entries = cpu_to_le32(instance->max_fw_cmds + 1);
+ initq_info->reply_queue_start_phys_addr_lo = cpu_to_le32(instance->reply_queue_h);
- initq_info->producer_index_phys_addr_lo = instance->producer_h;
- initq_info->consumer_index_phys_addr_lo = instance->consumer_h;
+ initq_info->producer_index_phys_addr_lo = cpu_to_le32(instance->producer_h);
+ initq_info->consumer_index_phys_addr_lo = cpu_to_le32(instance->consumer_h);
init_frame->cmd = MFI_CMD_INIT;
- init_frame->cmd_status = 0xFF;
- init_frame->queue_info_new_phys_addr_lo = initq_info_h;
+ init_frame->cmd_status = MFI_STAT_INVALID_STATUS;
+ init_frame->queue_info_new_phys_addr_lo =
+ cpu_to_le32(lower_32_bits(initq_info_h));
+ init_frame->queue_info_new_phys_addr_hi =
+ cpu_to_le32(upper_32_bits(initq_info_h));
- init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info);
+ init_frame->data_xfer_len = cpu_to_le32(sizeof(struct megasas_init_queue_info));
/*
* disable the intr before firing the init frame to FW
*/
- instance->instancet->disable_intr(instance->reg_set);
+ instance->instancet->disable_intr(instance);
/*
* Issue the init frame in polled mode
*/
if (megasas_issue_polled(instance, cmd)) {
- printk(KERN_ERR "megasas: Failed to init firmware\n");
+ dev_err(&instance->pdev->dev, "Failed to init firmware\n");
megasas_return_cmd(instance, cmd);
goto fail_fw_init;
}
@@ -3326,60 +5552,16 @@ fail_fw_init:
return -EINVAL;
}
-/**
- * megasas_start_timer - Initializes a timer object
- * @instance: Adapter soft state
- * @timer: timer object to be initialized
- * @fn: timer function
- * @interval: time interval between timer function call
- */
-static inline void
-megasas_start_timer(struct megasas_instance *instance,
- struct timer_list *timer,
- void *fn, unsigned long interval)
-{
- init_timer(timer);
- timer->expires = jiffies + interval;
- timer->data = (unsigned long)instance;
- timer->function = fn;
- add_timer(timer);
-}
-
-/**
- * megasas_io_completion_timer - Timer fn
- * @instance_addr: Address of adapter soft state
- *
- * Schedules tasklet for cmd completion
- * if poll_mode_io is set
- */
-static void
-megasas_io_completion_timer(unsigned long instance_addr)
-{
- struct megasas_instance *instance =
- (struct megasas_instance *)instance_addr;
-
- if (atomic_read(&instance->fw_outstanding))
- tasklet_schedule(&instance->isr_tasklet);
-
- /* Restart timer */
- if (poll_mode_io)
- mod_timer(&instance->io_completion_timer,
- jiffies + MEGASAS_COMPLETION_TIMER_INTERVAL);
-}
-
static u32
megasas_init_adapter_mfi(struct megasas_instance *instance)
{
- struct megasas_register_set __iomem *reg_set;
u32 context_sz;
u32 reply_q_sz;
- reg_set = instance->reg_set;
-
/*
* Get various operational parameters from status register
*/
- instance->max_fw_cmds = instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF;
+ instance->max_fw_cmds = instance->instancet->read_fw_status_reg(instance) & 0x00FFFF;
/*
* Reduce the max supported cmds by 1. This is to ensure that the
* reply_q_sz (1 more than the max cmd that driver may send)
@@ -3387,9 +5569,25 @@ megasas_init_adapter_mfi(struct megasas_instance *instance)
*/
instance->max_fw_cmds = instance->max_fw_cmds-1;
instance->max_mfi_cmds = instance->max_fw_cmds;
- instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >>
+ instance->max_num_sge = (instance->instancet->read_fw_status_reg(instance) & 0xFF0000) >>
0x10;
/*
+ * For MFI skinny adapters, MEGASAS_SKINNY_INT_CMDS commands
+ * are reserved for IOCTL + driver's internal DCMDs.
+ */
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ instance->max_scsi_cmds = (instance->max_fw_cmds -
+ MEGASAS_SKINNY_INT_CMDS);
+ sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
+ } else {
+ instance->max_scsi_cmds = (instance->max_fw_cmds -
+ MEGASAS_INT_CMDS);
+ sema_init(&instance->ioctl_sem, (MEGASAS_MFI_IOCTL_CMDS));
+ }
+
+ instance->cur_can_queue = instance->max_scsi_cmds;
+ /*
* Create a pool of commands
*/
if (megasas_alloc_cmds(instance))
@@ -3407,24 +5605,30 @@ megasas_init_adapter_mfi(struct megasas_instance *instance)
context_sz = sizeof(u32);
reply_q_sz = context_sz * (instance->max_fw_cmds + 1);
- instance->reply_queue = pci_alloc_consistent(instance->pdev,
- reply_q_sz,
- &instance->reply_queue_h);
+ instance->reply_queue = dma_alloc_coherent(&instance->pdev->dev,
+ reply_q_sz, &instance->reply_queue_h, GFP_KERNEL);
if (!instance->reply_queue) {
- printk(KERN_DEBUG "megasas: Out of DMA mem for reply queue\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Out of DMA mem for reply queue\n");
goto fail_reply_queue;
}
if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
+ if (megasas_get_ctrl_info(instance)) {
+ dev_err(&instance->pdev->dev, "(%d): Could get controller info "
+ "Fail from %s %d\n", instance->unique_id,
+ __func__, __LINE__);
+ goto fail_fw_init;
+ }
+
instance->fw_support_ieee = 0;
instance->fw_support_ieee =
- (instance->instancet->read_fw_status_reg(reg_set) &
+ (instance->instancet->read_fw_status_reg(instance) &
0x04000000);
- printk(KERN_NOTICE "megasas_init_mfi: fw_support_ieee=%d",
+ dev_notice(&instance->pdev->dev, "megasas_init_mfi: fw_support_ieee=%d",
instance->fw_support_ieee);
if (instance->fw_support_ieee)
@@ -3434,7 +5638,7 @@ megasas_init_adapter_mfi(struct megasas_instance *instance)
fail_fw_init:
- pci_free_consistent(instance->pdev, reply_q_sz,
+ dma_free_coherent(&instance->pdev->dev, reply_q_sz,
instance->reply_queue, instance->reply_queue_h);
fail_reply_queue:
megasas_free_cmds(instance);
@@ -3443,6 +5647,372 @@ fail_alloc_cmds:
return 1;
}
+static
+void megasas_setup_irq_poll(struct megasas_instance *instance)
+{
+ struct megasas_irq_context *irq_ctx;
+ u32 count, i;
+
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+
+ /* Initialize IRQ poll */
+ for (i = 0; i < count; i++) {
+ irq_ctx = &instance->irq_context[i];
+ irq_ctx->os_irq = pci_irq_vector(instance->pdev, i);
+ irq_ctx->irq_poll_scheduled = false;
+ irq_poll_init(&irq_ctx->irqpoll,
+ instance->threshold_reply_count,
+ megasas_irqpoll);
+ }
+}
+
+/*
+ * megasas_setup_irqs_ioapic - register legacy interrupts.
+ * @instance: Adapter soft state
+ *
+ * Do not enable interrupt, only setup ISRs.
+ *
+ * Return 0 on success.
+ */
+static int
+megasas_setup_irqs_ioapic(struct megasas_instance *instance)
+{
+ struct pci_dev *pdev;
+
+ pdev = instance->pdev;
+ instance->irq_context[0].instance = instance;
+ instance->irq_context[0].MSIxIndex = 0;
+ snprintf(instance->irq_context->name, MEGASAS_MSIX_NAME_LEN, "%s%u",
+ "megasas", instance->host->host_no);
+ if (request_irq(pci_irq_vector(pdev, 0),
+ instance->instancet->service_isr, IRQF_SHARED,
+ instance->irq_context->name, &instance->irq_context[0])) {
+ dev_err(&instance->pdev->dev,
+ "Failed to register IRQ from %s %d\n",
+ __func__, __LINE__);
+ return -1;
+ }
+ instance->perf_mode = MR_LATENCY_PERF_MODE;
+ instance->low_latency_index_start = 0;
+ return 0;
+}
+
+/**
+ * megasas_setup_irqs_msix - register MSI-x interrupts.
+ * @instance: Adapter soft state
+ * @is_probe: Driver probe check
+ *
+ * Do not enable interrupt, only setup ISRs.
+ *
+ * Return 0 on success.
+ */
+static int
+megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe)
+{
+ int i, j;
+ struct pci_dev *pdev;
+
+ pdev = instance->pdev;
+
+ /* Try MSI-x */
+ for (i = 0; i < instance->msix_vectors; i++) {
+ instance->irq_context[i].instance = instance;
+ instance->irq_context[i].MSIxIndex = i;
+ snprintf(instance->irq_context[i].name, MEGASAS_MSIX_NAME_LEN, "%s%u-msix%u",
+ "megasas", instance->host->host_no, i);
+ if (request_irq(pci_irq_vector(pdev, i),
+ instance->instancet->service_isr, 0, instance->irq_context[i].name,
+ &instance->irq_context[i])) {
+ dev_err(&instance->pdev->dev,
+ "Failed to register IRQ for vector %d.\n", i);
+ for (j = 0; j < i; j++) {
+ if (j < instance->low_latency_index_start)
+ irq_update_affinity_hint(
+ pci_irq_vector(pdev, j), NULL);
+ free_irq(pci_irq_vector(pdev, j),
+ &instance->irq_context[j]);
+ }
+ /* Retry irq register for IO_APIC*/
+ instance->msix_vectors = 0;
+ instance->msix_load_balance = false;
+ if (is_probe) {
+ pci_free_irq_vectors(instance->pdev);
+ return megasas_setup_irqs_ioapic(instance);
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * megasas_destroy_irqs- unregister interrupts.
+ * @instance: Adapter soft state
+ * return: void
+ */
+static void
+megasas_destroy_irqs(struct megasas_instance *instance) {
+
+ int i;
+ int count;
+ struct megasas_irq_context *irq_ctx;
+
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+ if (instance->adapter_type != MFI_SERIES) {
+ for (i = 0; i < count; i++) {
+ irq_ctx = &instance->irq_context[i];
+ irq_poll_disable(&irq_ctx->irqpoll);
+ }
+ }
+
+ if (instance->msix_vectors)
+ for (i = 0; i < instance->msix_vectors; i++) {
+ if (i < instance->low_latency_index_start)
+ irq_update_affinity_hint(
+ pci_irq_vector(instance->pdev, i), NULL);
+ free_irq(pci_irq_vector(instance->pdev, i),
+ &instance->irq_context[i]);
+ }
+ else
+ free_irq(pci_irq_vector(instance->pdev, 0),
+ &instance->irq_context[0]);
+}
+
+/**
+ * megasas_setup_jbod_map - setup jbod map for FP seq_number.
+ * @instance: Adapter soft state
+ *
+ * Return 0 on success.
+ */
+void
+megasas_setup_jbod_map(struct megasas_instance *instance)
+{
+ int i;
+ struct fusion_context *fusion = instance->ctrl_context;
+ size_t pd_seq_map_sz;
+
+ pd_seq_map_sz = struct_size_t(struct MR_PD_CFG_SEQ_NUM_SYNC, seq,
+ MAX_PHYSICAL_DEVICES);
+
+ instance->use_seqnum_jbod_fp =
+ instance->support_seqnum_jbod_fp;
+ if (reset_devices || !fusion ||
+ !instance->support_seqnum_jbod_fp) {
+ dev_info(&instance->pdev->dev,
+ "JBOD sequence map is disabled %s %d\n",
+ __func__, __LINE__);
+ instance->use_seqnum_jbod_fp = false;
+ return;
+ }
+
+ if (fusion->pd_seq_sync[0])
+ goto skip_alloc;
+
+ for (i = 0; i < JBOD_MAPS_COUNT; i++) {
+ fusion->pd_seq_sync[i] = dma_alloc_coherent
+ (&instance->pdev->dev, pd_seq_map_sz,
+ &fusion->pd_seq_phys[i], GFP_KERNEL);
+ if (!fusion->pd_seq_sync[i]) {
+ dev_err(&instance->pdev->dev,
+ "Failed to allocate memory from %s %d\n",
+ __func__, __LINE__);
+ if (i == 1) {
+ dma_free_coherent(&instance->pdev->dev,
+ pd_seq_map_sz, fusion->pd_seq_sync[0],
+ fusion->pd_seq_phys[0]);
+ fusion->pd_seq_sync[0] = NULL;
+ }
+ instance->use_seqnum_jbod_fp = false;
+ return;
+ }
+ }
+
+skip_alloc:
+ if (!megasas_sync_pd_seq_num(instance, false) &&
+ !megasas_sync_pd_seq_num(instance, true))
+ instance->use_seqnum_jbod_fp = true;
+ else
+ instance->use_seqnum_jbod_fp = false;
+}
+
+static void megasas_setup_reply_map(struct megasas_instance *instance)
+{
+ const struct cpumask *mask;
+ unsigned int queue, cpu, low_latency_index_start;
+
+ low_latency_index_start = instance->low_latency_index_start;
+
+ for (queue = low_latency_index_start; queue < instance->msix_vectors; queue++) {
+ mask = pci_irq_get_affinity(instance->pdev, queue);
+ if (!mask)
+ goto fallback;
+
+ for_each_cpu(cpu, mask)
+ instance->reply_map[cpu] = queue;
+ }
+ return;
+
+fallback:
+ queue = low_latency_index_start;
+ for_each_possible_cpu(cpu) {
+ instance->reply_map[cpu] = queue;
+ if (queue == (instance->msix_vectors - 1))
+ queue = low_latency_index_start;
+ else
+ queue++;
+ }
+}
+
+/**
+ * megasas_get_device_list - Get the PD and LD device list from FW.
+ * @instance: Adapter soft state
+ * @return: Success or failure
+ *
+ * Issue DCMDs to Firmware to get the PD and LD list.
+ * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination
+ * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list.
+ */
+static
+int megasas_get_device_list(struct megasas_instance *instance)
+{
+ if (instance->enable_fw_dev_list) {
+ if (megasas_host_device_list_query(instance, true))
+ return FAILED;
+ } else {
+ if (megasas_get_pd_list(instance) < 0) {
+ dev_err(&instance->pdev->dev, "failed to get PD list\n");
+ return FAILED;
+ }
+
+ if (megasas_ld_list_query(instance,
+ MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) {
+ dev_err(&instance->pdev->dev, "failed to get LD list\n");
+ return FAILED;
+ }
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * megasas_set_high_iops_queue_affinity_and_hint - Set affinity and hint
+ * for high IOPS queues
+ * @instance: Adapter soft state
+ * return: void
+ */
+static inline void
+megasas_set_high_iops_queue_affinity_and_hint(struct megasas_instance *instance)
+{
+ int i;
+ unsigned int irq;
+ const struct cpumask *mask;
+
+ if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
+ int nid = dev_to_node(&instance->pdev->dev);
+
+ if (nid == NUMA_NO_NODE)
+ nid = 0;
+ mask = cpumask_of_node(nid);
+
+ for (i = 0; i < instance->low_latency_index_start; i++) {
+ irq = pci_irq_vector(instance->pdev, i);
+ irq_set_affinity_and_hint(irq, mask);
+ }
+ }
+}
+
+static int
+__megasas_alloc_irq_vectors(struct megasas_instance *instance)
+{
+ int i, irq_flags;
+ struct irq_affinity desc = { .pre_vectors = instance->low_latency_index_start };
+ struct irq_affinity *descp = &desc;
+
+ irq_flags = PCI_IRQ_MSIX;
+
+ if (instance->smp_affinity_enable)
+ irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES;
+ else
+ descp = NULL;
+
+ /* Do not allocate msix vectors for poll_queues.
+ * msix_vectors is always within a range of FW supported reply queue.
+ */
+ i = pci_alloc_irq_vectors_affinity(instance->pdev,
+ instance->low_latency_index_start,
+ instance->msix_vectors - instance->iopoll_q_count, irq_flags, descp);
+
+ return i;
+}
+
+/**
+ * megasas_alloc_irq_vectors - Allocate IRQ vectors/enable MSI-x vectors
+ * @instance: Adapter soft state
+ * return: void
+ */
+static void
+megasas_alloc_irq_vectors(struct megasas_instance *instance)
+{
+ int i;
+ unsigned int num_msix_req;
+
+ instance->iopoll_q_count = 0;
+ if ((instance->adapter_type != MFI_SERIES) &&
+ poll_queues) {
+
+ instance->perf_mode = MR_LATENCY_PERF_MODE;
+ instance->low_latency_index_start = 1;
+
+ /* reserve for default and non-mananged pre-vector. */
+ if (instance->msix_vectors > (poll_queues + 2))
+ instance->iopoll_q_count = poll_queues;
+ else
+ instance->iopoll_q_count = 0;
+
+ num_msix_req = blk_mq_num_online_queues(0) +
+ instance->low_latency_index_start;
+ instance->msix_vectors = min(num_msix_req,
+ instance->msix_vectors);
+
+ }
+
+ i = __megasas_alloc_irq_vectors(instance);
+
+ if (((instance->perf_mode == MR_BALANCED_PERF_MODE)
+ || instance->iopoll_q_count) &&
+ (i != (instance->msix_vectors - instance->iopoll_q_count))) {
+ if (instance->msix_vectors)
+ pci_free_irq_vectors(instance->pdev);
+ /* Disable Balanced IOPS mode and try realloc vectors */
+ instance->perf_mode = MR_LATENCY_PERF_MODE;
+ instance->low_latency_index_start = 1;
+ num_msix_req = blk_mq_num_online_queues(0) +
+ instance->low_latency_index_start;
+
+ instance->msix_vectors = min(num_msix_req,
+ instance->msix_vectors);
+
+ instance->iopoll_q_count = 0;
+ i = __megasas_alloc_irq_vectors(instance);
+
+ }
+
+ dev_info(&instance->pdev->dev,
+ "requested/available msix %d/%d poll_queue %d\n",
+ instance->msix_vectors - instance->iopoll_q_count,
+ i, instance->iopoll_q_count);
+
+ if (i > 0)
+ instance->msix_vectors = i;
+ else
+ instance->msix_vectors = 0;
+
+ if (instance->smp_affinity_enable)
+ megasas_set_high_iops_queue_affinity_and_hint(instance);
+}
+
/**
* megasas_init_fw - Initializes the FW
* @instance: Adapter soft state
@@ -3453,85 +6023,385 @@ fail_alloc_cmds:
static int megasas_init_fw(struct megasas_instance *instance)
{
u32 max_sectors_1;
- u32 max_sectors_2;
- u32 tmp_sectors, msix_enable;
- struct megasas_register_set __iomem *reg_set;
- struct megasas_ctrl_info *ctrl_info;
+ u32 max_sectors_2, tmp_sectors, msix_enable;
+ u32 scratch_pad_1, scratch_pad_2, scratch_pad_3, status_reg;
+ resource_size_t base_addr;
+ void *base_addr_phys;
+ struct megasas_ctrl_info *ctrl_info = NULL;
unsigned long bar_list;
+ int i, j, loop;
+ struct IOV_111 *iovPtr;
+ struct fusion_context *fusion;
+ bool intr_coalescing;
+ unsigned int num_msix_req;
+ u16 lnksta, speed;
+
+ fusion = instance->ctrl_context;
/* Find first memory bar */
bar_list = pci_select_bars(instance->pdev, IORESOURCE_MEM);
- instance->bar = find_first_bit(&bar_list, sizeof(unsigned long));
- instance->base_addr = pci_resource_start(instance->pdev, instance->bar);
- if (pci_request_selected_regions(instance->pdev, instance->bar,
+ instance->bar = find_first_bit(&bar_list, BITS_PER_LONG);
+ if (pci_request_selected_regions(instance->pdev, 1<<instance->bar,
"megasas: LSI")) {
- printk(KERN_DEBUG "megasas: IO memory region busy!\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "IO memory region busy!\n");
return -EBUSY;
}
- instance->reg_set = ioremap_nocache(instance->base_addr, 8192);
+ base_addr = pci_resource_start(instance->pdev, instance->bar);
+ instance->reg_set = ioremap(base_addr, 8192);
if (!instance->reg_set) {
- printk(KERN_DEBUG "megasas: Failed to map IO mem\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to map IO mem\n");
goto fail_ioremap;
}
- reg_set = instance->reg_set;
+ base_addr_phys = &base_addr;
+ dev_printk(KERN_DEBUG, &instance->pdev->dev,
+ "BAR:0x%lx BAR's base_addr(phys):%pa mapped virt_addr:0x%p\n",
+ instance->bar, base_addr_phys, instance->reg_set);
- switch (instance->pdev->device) {
- case PCI_DEVICE_ID_LSI_FUSION:
+ if (instance->adapter_type != MFI_SERIES)
instance->instancet = &megasas_instance_template_fusion;
+ else {
+ switch (instance->pdev->device) {
+ case PCI_DEVICE_ID_LSI_SAS1078R:
+ case PCI_DEVICE_ID_LSI_SAS1078DE:
+ instance->instancet = &megasas_instance_template_ppc;
+ break;
+ case PCI_DEVICE_ID_LSI_SAS1078GEN2:
+ case PCI_DEVICE_ID_LSI_SAS0079GEN2:
+ instance->instancet = &megasas_instance_template_gen2;
+ break;
+ case PCI_DEVICE_ID_LSI_SAS0073SKINNY:
+ case PCI_DEVICE_ID_LSI_SAS0071SKINNY:
+ instance->instancet = &megasas_instance_template_skinny;
+ break;
+ case PCI_DEVICE_ID_LSI_SAS1064R:
+ case PCI_DEVICE_ID_DELL_PERC5:
+ default:
+ instance->instancet = &megasas_instance_template_xscale;
+ instance->pd_list_not_supported = 1;
+ break;
+ }
+ }
+
+ if (megasas_transition_to_ready(instance, 0)) {
+ dev_info(&instance->pdev->dev,
+ "Failed to transition controller to ready from %s!\n",
+ __func__);
+ if (instance->adapter_type != MFI_SERIES) {
+ status_reg = instance->instancet->read_fw_status_reg(
+ instance);
+ if (status_reg & MFI_RESET_ADAPTER) {
+ if (megasas_adp_reset_wait_for_ready
+ (instance, true, 0) == FAILED)
+ goto fail_ready_state;
+ } else {
+ goto fail_ready_state;
+ }
+ } else {
+ atomic_set(&instance->fw_reset_no_pci_access, 1);
+ instance->instancet->adp_reset
+ (instance, instance->reg_set);
+ atomic_set(&instance->fw_reset_no_pci_access, 0);
+
+ /*waiting for about 30 second before retry*/
+ ssleep(30);
+
+ if (megasas_transition_to_ready(instance, 0))
+ goto fail_ready_state;
+ }
+
+ dev_info(&instance->pdev->dev,
+ "FW restarted successfully from %s!\n",
+ __func__);
+ }
+
+ megasas_init_ctrl_params(instance);
+
+ if (megasas_set_dma_mask(instance))
+ goto fail_ready_state;
+
+ if (megasas_alloc_ctrl_mem(instance))
+ goto fail_alloc_dma_buf;
+
+ if (megasas_alloc_ctrl_dma_buffers(instance))
+ goto fail_alloc_dma_buf;
+
+ fusion = instance->ctrl_context;
+
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ scratch_pad_2 =
+ megasas_readl(instance,
+ &instance->reg_set->outbound_scratch_pad_2);
+ instance->max_raid_mapsize = ((scratch_pad_2 >>
+ MR_MAX_RAID_MAP_SIZE_OFFSET_SHIFT) &
+ MR_MAX_RAID_MAP_SIZE_MASK);
+ }
+
+ instance->enable_sdev_max_qd = enable_sdev_max_qd;
+
+ switch (instance->adapter_type) {
+ case VENTURA_SERIES:
+ fusion->pcie_bw_limitation = true;
break;
- case PCI_DEVICE_ID_LSI_SAS1078R:
- case PCI_DEVICE_ID_LSI_SAS1078DE:
- instance->instancet = &megasas_instance_template_ppc;
- break;
- case PCI_DEVICE_ID_LSI_SAS1078GEN2:
- case PCI_DEVICE_ID_LSI_SAS0079GEN2:
- instance->instancet = &megasas_instance_template_gen2;
- break;
- case PCI_DEVICE_ID_LSI_SAS0073SKINNY:
- case PCI_DEVICE_ID_LSI_SAS0071SKINNY:
- instance->instancet = &megasas_instance_template_skinny;
+ case AERO_SERIES:
+ fusion->r56_div_offload = true;
break;
- case PCI_DEVICE_ID_LSI_SAS1064R:
- case PCI_DEVICE_ID_DELL_PERC5:
default:
- instance->instancet = &megasas_instance_template_xscale;
break;
}
+ /* Check if MSI-X is supported while in ready state */
+ msix_enable = (instance->instancet->read_fw_status_reg(instance) &
+ 0x4000000) >> 0x1a;
+ if (msix_enable && !msix_disable) {
+
+ scratch_pad_1 = megasas_readl
+ (instance, &instance->reg_set->outbound_scratch_pad_1);
+ /* Check max MSI-X vectors */
+ if (fusion) {
+ if (instance->adapter_type == THUNDERBOLT_SERIES) {
+ /* Thunderbolt Series*/
+ instance->msix_vectors = (scratch_pad_1
+ & MR_MAX_REPLY_QUEUES_OFFSET) + 1;
+ } else {
+ instance->msix_vectors = ((scratch_pad_1
+ & MR_MAX_REPLY_QUEUES_EXT_OFFSET)
+ >> MR_MAX_REPLY_QUEUES_EXT_OFFSET_SHIFT) + 1;
+
+ /*
+ * For Invader series, > 8 MSI-x vectors
+ * supported by FW/HW implies combined
+ * reply queue mode is enabled.
+ * For Ventura series, > 16 MSI-x vectors
+ * supported by FW/HW implies combined
+ * reply queue mode is enabled.
+ */
+ switch (instance->adapter_type) {
+ case INVADER_SERIES:
+ if (instance->msix_vectors > 8)
+ instance->msix_combined = true;
+ break;
+ case AERO_SERIES:
+ case VENTURA_SERIES:
+ if (instance->msix_vectors > 16)
+ instance->msix_combined = true;
+ break;
+ }
+
+ if (rdpq_enable)
+ instance->is_rdpq = (scratch_pad_1 & MR_RDPQ_MODE_OFFSET) ?
+ 1 : 0;
+
+ if (instance->adapter_type >= INVADER_SERIES &&
+ !instance->msix_combined) {
+ instance->msix_load_balance = true;
+ instance->smp_affinity_enable = false;
+ }
+
+ /* Save 1-15 reply post index address to local memory
+ * Index 0 is already saved from reg offset
+ * MPI2_REPLY_POST_HOST_INDEX_OFFSET
+ */
+ for (loop = 1; loop < MR_MAX_MSIX_REG_ARRAY; loop++) {
+ instance->reply_post_host_index_addr[loop] =
+ (u32 __iomem *)
+ ((u8 __iomem *)instance->reg_set +
+ MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET
+ + (loop * 0x10));
+ }
+ }
+
+ dev_info(&instance->pdev->dev,
+ "firmware supports msix\t: (%d)",
+ instance->msix_vectors);
+ if (msix_vectors)
+ instance->msix_vectors = min(msix_vectors,
+ instance->msix_vectors);
+ } else /* MFI adapters */
+ instance->msix_vectors = 1;
+
+
+ /*
+ * For Aero (if some conditions are met), driver will configure a
+ * few additional reply queues with interrupt coalescing enabled.
+ * These queues with interrupt coalescing enabled are called
+ * High IOPS queues and rest of reply queues (based on number of
+ * logical CPUs) are termed as Low latency queues.
+ *
+ * Total Number of reply queues = High IOPS queues + low latency queues
+ *
+ * For rest of fusion adapters, 1 additional reply queue will be
+ * reserved for management commands, rest of reply queues
+ * (based on number of logical CPUs) will be used for IOs and
+ * referenced as IO queues.
+ * Total Number of reply queues = 1 + IO queues
+ *
+ * MFI adapters supports single MSI-x so single reply queue
+ * will be used for IO and management commands.
+ */
+
+ intr_coalescing = (scratch_pad_1 & MR_INTR_COALESCING_SUPPORT_OFFSET) ?
+ true : false;
+ if (intr_coalescing &&
+ (blk_mq_num_online_queues(0) >= MR_HIGH_IOPS_QUEUE_COUNT) &&
+ (instance->msix_vectors == MEGASAS_MAX_MSIX_QUEUES))
+ instance->perf_mode = MR_BALANCED_PERF_MODE;
+ else
+ instance->perf_mode = MR_LATENCY_PERF_MODE;
+
+
+ if (instance->adapter_type == AERO_SERIES) {
+ pcie_capability_read_word(instance->pdev, PCI_EXP_LNKSTA, &lnksta);
+ speed = lnksta & PCI_EXP_LNKSTA_CLS;
+
+ /*
+ * For Aero, if PCIe link speed is <16 GT/s, then driver should operate
+ * in latency perf mode and enable R1 PCI bandwidth algorithm
+ */
+ if (speed < 0x4) {
+ instance->perf_mode = MR_LATENCY_PERF_MODE;
+ fusion->pcie_bw_limitation = true;
+ }
+
+ /*
+ * Performance mode settings provided through module parameter-perf_mode will
+ * take affect only for:
+ * 1. Aero family of adapters.
+ * 2. When user sets module parameter- perf_mode in range of 0-2.
+ */
+ if ((perf_mode >= MR_BALANCED_PERF_MODE) &&
+ (perf_mode <= MR_LATENCY_PERF_MODE))
+ instance->perf_mode = perf_mode;
+ /*
+ * If intr coalescing is not supported by controller FW, then IOPS
+ * and Balanced modes are not feasible.
+ */
+ if (!intr_coalescing)
+ instance->perf_mode = MR_LATENCY_PERF_MODE;
+
+ }
+
+ if (instance->perf_mode == MR_BALANCED_PERF_MODE)
+ instance->low_latency_index_start =
+ MR_HIGH_IOPS_QUEUE_COUNT;
+ else
+ instance->low_latency_index_start = 1;
+
+ num_msix_req = blk_mq_num_online_queues(0) +
+ instance->low_latency_index_start;
+
+ instance->msix_vectors = min(num_msix_req,
+ instance->msix_vectors);
+
+ megasas_alloc_irq_vectors(instance);
+ if (!instance->msix_vectors)
+ instance->msix_load_balance = false;
+ }
/*
- * We expect the FW state to be READY
+ * MSI-X host index 0 is common for all adapter.
+ * It is used for all MPT based Adapters.
*/
- if (megasas_transition_to_ready(instance))
- goto fail_ready_state;
+ if (instance->msix_combined) {
+ instance->reply_post_host_index_addr[0] =
+ (u32 *)((u8 *)instance->reg_set +
+ MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET);
+ } else {
+ instance->reply_post_host_index_addr[0] =
+ (u32 *)((u8 *)instance->reg_set +
+ MPI2_REPLY_POST_HOST_INDEX_OFFSET);
+ }
- /* Check if MSI-X is supported while in ready state */
- msix_enable = (instance->instancet->read_fw_status_reg(reg_set) &
- 0x4000000) >> 0x1a;
- if (msix_enable && !msix_disable &&
- !pci_enable_msix(instance->pdev, &instance->msixentry, 1))
- instance->msi_flag = 1;
+ if (!instance->msix_vectors) {
+ i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_INTX);
+ if (i < 0)
+ goto fail_init_adapter;
+ }
+ megasas_setup_reply_map(instance);
+
+ dev_info(&instance->pdev->dev,
+ "current msix/max num queues\t: (%d/%u)\n",
+ instance->msix_vectors, blk_mq_num_online_queues(0));
+ dev_info(&instance->pdev->dev,
+ "RDPQ mode\t: (%s)\n", instance->is_rdpq ? "enabled" : "disabled");
+
+ tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
+ (unsigned long)instance);
+
+ /*
+ * Below are default value for legacy Firmware.
+ * non-fusion based controllers
+ */
+ instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES;
+ instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
/* Get operational params, sge flags, send init cmd to controller */
if (instance->instancet->init_adapter(instance))
goto fail_init_adapter;
- printk(KERN_ERR "megasas: INIT adapter done\n");
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ scratch_pad_3 =
+ megasas_readl(instance,
+ &instance->reg_set->outbound_scratch_pad_3);
+ if ((scratch_pad_3 & MR_NVME_PAGE_SIZE_MASK) >=
+ MR_DEFAULT_NVME_PAGE_SHIFT)
+ instance->nvme_page_size =
+ (1 << (scratch_pad_3 & MR_NVME_PAGE_SIZE_MASK));
+
+ dev_info(&instance->pdev->dev,
+ "NVME page size\t: (%d)\n", instance->nvme_page_size);
+ }
+
+ if (instance->msix_vectors ?
+ megasas_setup_irqs_msix(instance, 1) :
+ megasas_setup_irqs_ioapic(instance))
+ goto fail_init_adapter;
+
+ if (instance->adapter_type != MFI_SERIES)
+ megasas_setup_irq_poll(instance);
+
+ instance->instancet->enable_intr(instance);
- /** for passthrough
- * the following function will get the PD LIST.
- */
+ dev_info(&instance->pdev->dev, "INIT adapter done\n");
- memset(instance->pd_list, 0 ,
- (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
- megasas_get_pd_list(instance);
+ megasas_setup_jbod_map(instance);
- memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
- megasas_get_ld_list(instance);
+ if (megasas_get_device_list(instance) != SUCCESS) {
+ dev_err(&instance->pdev->dev,
+ "%s: megasas_get_device_list failed\n",
+ __func__);
+ goto fail_get_ld_pd_list;
+ }
- ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);
+ /* stream detection initialization */
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ fusion->stream_detect_by_ld =
+ kcalloc(MAX_LOGICAL_DRIVES_EXT,
+ sizeof(struct LD_STREAM_DETECT *),
+ GFP_KERNEL);
+ if (!fusion->stream_detect_by_ld) {
+ dev_err(&instance->pdev->dev,
+ "unable to allocate stream detection for pool of LDs\n");
+ goto fail_get_ld_pd_list;
+ }
+ for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i) {
+ fusion->stream_detect_by_ld[i] =
+ kzalloc(sizeof(struct LD_STREAM_DETECT),
+ GFP_KERNEL);
+ if (!fusion->stream_detect_by_ld[i]) {
+ dev_err(&instance->pdev->dev,
+ "unable to allocate stream detect by LD\n");
+ for (j = 0; j < i; ++j)
+ kfree(fusion->stream_detect_by_ld[j]);
+ kfree(fusion->stream_detect_by_ld);
+ fusion->stream_detect_by_ld = NULL;
+ goto fail_get_ld_pd_list;
+ }
+ fusion->stream_detect_by_ld[i]->mru_bit_map
+ = MR_STREAM_BITMAP;
+ }
+ }
/*
* Compute the max allowed sectors per IO: The controller info has two
@@ -3543,65 +6413,165 @@ static int megasas_init_fw(struct megasas_instance *instance)
* to calculate max_sectors_1. So the number ended up as zero always.
*/
tmp_sectors = 0;
- if (ctrl_info && !megasas_get_ctrl_info(instance, ctrl_info)) {
+ ctrl_info = instance->ctrl_info_buf;
+
+ max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) *
+ le16_to_cpu(ctrl_info->max_strips_per_io);
+ max_sectors_2 = le32_to_cpu(ctrl_info->max_request_size);
+
+ tmp_sectors = min_t(u32, max_sectors_1, max_sectors_2);
+
+ instance->peerIsPresent = ctrl_info->cluster.peerIsPresent;
+ instance->passive = ctrl_info->cluster.passive;
+ memcpy(instance->clusterId, ctrl_info->clusterId, sizeof(instance->clusterId));
+ instance->UnevenSpanSupport =
+ ctrl_info->adapterOperations2.supportUnevenSpans;
+ if (instance->UnevenSpanSupport) {
+ struct fusion_context *fusion = instance->ctrl_context;
+ if (MR_ValidateMapInfo(instance, instance->map_id))
+ fusion->fast_path_io = 1;
+ else
+ fusion->fast_path_io = 0;
+
+ }
+ if (ctrl_info->host_interface.SRIOV) {
+ instance->requestorId = ctrl_info->iov.requestorId;
+ if (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) {
+ if (!ctrl_info->adapterOperations2.activePassive)
+ instance->PlasmaFW111 = 1;
- max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) *
- ctrl_info->max_strips_per_io;
- max_sectors_2 = ctrl_info->max_request_size;
+ dev_info(&instance->pdev->dev, "SR-IOV: firmware type: %s\n",
+ instance->PlasmaFW111 ? "1.11" : "new");
- tmp_sectors = min_t(u32, max_sectors_1 , max_sectors_2);
- instance->disableOnlineCtrlReset =
- ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset;
+ if (instance->PlasmaFW111) {
+ iovPtr = (struct IOV_111 *)
+ ((unsigned char *)ctrl_info + IOV_111_OFFSET);
+ instance->requestorId = iovPtr->requestorId;
+ }
+ }
+ dev_info(&instance->pdev->dev, "SRIOV: VF requestorId %d\n",
+ instance->requestorId);
}
+ instance->crash_dump_fw_support =
+ ctrl_info->adapterOperations3.supportCrashDump;
+ instance->crash_dump_drv_support =
+ (instance->crash_dump_fw_support &&
+ instance->crash_dump_buf);
+ if (instance->crash_dump_drv_support)
+ megasas_set_crash_dump_params(instance,
+ MR_CRASH_BUF_TURN_OFF);
+
+ else {
+ if (instance->crash_dump_buf)
+ dma_free_coherent(&instance->pdev->dev,
+ CRASH_DMA_BUF_SIZE,
+ instance->crash_dump_buf,
+ instance->crash_dump_h);
+ instance->crash_dump_buf = NULL;
+ }
+
+ if (instance->snapdump_wait_time) {
+ megasas_get_snapdump_properties(instance);
+ dev_info(&instance->pdev->dev, "Snap dump wait time\t: %d\n",
+ instance->snapdump_wait_time);
+ }
+
+ dev_info(&instance->pdev->dev,
+ "pci id\t\t: (0x%04x)/(0x%04x)/(0x%04x)/(0x%04x)\n",
+ le16_to_cpu(ctrl_info->pci.vendor_id),
+ le16_to_cpu(ctrl_info->pci.device_id),
+ le16_to_cpu(ctrl_info->pci.sub_vendor_id),
+ le16_to_cpu(ctrl_info->pci.sub_device_id));
+ dev_info(&instance->pdev->dev, "unevenspan support : %s\n",
+ instance->UnevenSpanSupport ? "yes" : "no");
+ dev_info(&instance->pdev->dev, "firmware crash dump : %s\n",
+ instance->crash_dump_drv_support ? "yes" : "no");
+ dev_info(&instance->pdev->dev, "JBOD sequence map : %s\n",
+ instance->use_seqnum_jbod_fp ? "enabled" : "disabled");
+
instance->max_sectors_per_req = instance->max_num_sge *
- PAGE_SIZE / 512;
+ SGE_BUFFER_SIZE / 512;
if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors))
instance->max_sectors_per_req = tmp_sectors;
- kfree(ctrl_info);
+ /* Check for valid throttlequeuedepth module parameter */
+ if (throttlequeuedepth &&
+ throttlequeuedepth <= instance->max_scsi_cmds)
+ instance->throttlequeuedepth = throttlequeuedepth;
+ else
+ instance->throttlequeuedepth =
+ MEGASAS_THROTTLE_QUEUE_DEPTH;
- /*
- * Setup tasklet for cmd completion
- */
+ if ((resetwaittime < 1) ||
+ (resetwaittime > MEGASAS_RESET_WAIT_TIME))
+ resetwaittime = MEGASAS_RESET_WAIT_TIME;
- tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
- (unsigned long)instance);
+ if ((scmd_timeout < 10) || (scmd_timeout > MEGASAS_DEFAULT_CMD_TIMEOUT))
+ scmd_timeout = MEGASAS_DEFAULT_CMD_TIMEOUT;
+
+ /* Launch SR-IOV heartbeat timer */
+ if (instance->requestorId) {
+ if (!megasas_sriov_start_heartbeat(instance, 1)) {
+ megasas_start_timer(instance);
+ } else {
+ instance->skip_heartbeat_timer_del = 1;
+ goto fail_get_ld_pd_list;
+ }
+ }
+
+ /*
+ * Create and start watchdog thread which will monitor
+ * controller state every 1 sec and trigger OCR when
+ * it enters fault state
+ */
+ if (instance->adapter_type != MFI_SERIES)
+ if (megasas_fusion_start_watchdog(instance) != SUCCESS)
+ goto fail_start_watchdog;
- /* Initialize the cmd completion timer */
- if (poll_mode_io)
- megasas_start_timer(instance, &instance->io_completion_timer,
- megasas_io_completion_timer,
- MEGASAS_COMPLETION_TIMER_INTERVAL);
return 0;
+fail_start_watchdog:
+ if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+ timer_delete_sync(&instance->sriov_heartbeat_timer);
+fail_get_ld_pd_list:
+ instance->instancet->disable_intr(instance);
+ megasas_destroy_irqs(instance);
fail_init_adapter:
+ if (instance->msix_vectors)
+ pci_free_irq_vectors(instance->pdev);
+ instance->msix_vectors = 0;
+fail_alloc_dma_buf:
+ megasas_free_ctrl_dma_buffers(instance);
+ megasas_free_ctrl_mem(instance);
fail_ready_state:
iounmap(instance->reg_set);
- fail_ioremap:
- pci_release_selected_regions(instance->pdev, instance->bar);
+fail_ioremap:
+ pci_release_selected_regions(instance->pdev, 1<<instance->bar);
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
return -EINVAL;
}
/**
* megasas_release_mfi - Reverses the FW initialization
- * @intance: Adapter soft state
+ * @instance: Adapter soft state
*/
static void megasas_release_mfi(struct megasas_instance *instance)
{
u32 reply_q_sz = sizeof(u32) *(instance->max_mfi_cmds + 1);
if (instance->reply_queue)
- pci_free_consistent(instance->pdev, reply_q_sz,
+ dma_free_coherent(&instance->pdev->dev, reply_q_sz,
instance->reply_queue, instance->reply_queue_h);
megasas_free_cmds(instance);
iounmap(instance->reg_set);
- pci_release_selected_regions(instance->pdev, instance->bar);
+ pci_release_selected_regions(instance->pdev, 1<<instance->bar);
}
/**
@@ -3624,6 +6594,7 @@ megasas_get_seq_num(struct megasas_instance *instance,
struct megasas_dcmd_frame *dcmd;
struct megasas_evt_log_info *el_info;
dma_addr_t el_info_h = 0;
+ int ret;
cmd = megasas_get_cmd(instance);
@@ -3632,16 +6603,14 @@ megasas_get_seq_num(struct megasas_instance *instance,
}
dcmd = &cmd->frame->dcmd;
- el_info = pci_alloc_consistent(instance->pdev,
- sizeof(struct megasas_evt_log_info),
- &el_info_h);
-
+ el_info = dma_alloc_coherent(&instance->pdev->dev,
+ sizeof(struct megasas_evt_log_info),
+ &el_info_h, GFP_KERNEL);
if (!el_info) {
megasas_return_cmd(instance, cmd);
return -ENOMEM;
}
- memset(el_info, 0, sizeof(*el_info));
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
dcmd->cmd = MFI_CMD_DCMD;
@@ -3650,31 +6619,43 @@ megasas_get_seq_num(struct megasas_instance *instance,
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = sizeof(struct megasas_evt_log_info);
- dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO;
- dcmd->sgl.sge32[0].phys_addr = el_info_h;
- dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_log_info);
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct megasas_evt_log_info));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_EVENT_GET_INFO);
+
+ megasas_set_dma_settings(instance, dcmd, el_info_h,
+ sizeof(struct megasas_evt_log_info));
- megasas_issue_blocked_cmd(instance, cmd);
+ ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS);
+ if (ret != DCMD_SUCCESS) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ goto dcmd_failed;
+ }
/*
* Copy the data back into callers buffer
*/
- memcpy(eli, el_info, sizeof(struct megasas_evt_log_info));
+ eli->newest_seq_num = el_info->newest_seq_num;
+ eli->oldest_seq_num = el_info->oldest_seq_num;
+ eli->clear_seq_num = el_info->clear_seq_num;
+ eli->shutdown_seq_num = el_info->shutdown_seq_num;
+ eli->boot_seq_num = el_info->boot_seq_num;
- pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info),
- el_info, el_info_h);
+dcmd_failed:
+ dma_free_coherent(&instance->pdev->dev,
+ sizeof(struct megasas_evt_log_info),
+ el_info, el_info_h);
megasas_return_cmd(instance, cmd);
- return 0;
+ return ret;
}
/**
* megasas_register_aen - Registers for asynchronous event notification
* @instance: Adapter soft state
* @seq_num: The starting sequence number
- * @class_locale: Class of the event
+ * @class_locale_word: Class of the event
*
* This function subscribes for AEN for events beyond the @seq_num. It requests
* to be notified if and only if the event is of type @class_locale
@@ -3706,7 +6687,16 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
if (instance->aen_cmd) {
- prev_aen.word = instance->aen_cmd->frame->dcmd.mbox.w[1];
+ prev_aen.word =
+ le32_to_cpu(instance->aen_cmd->frame->dcmd.mbox.w[1]);
+
+ if ((curr_aen.members.class < MFI_EVT_CLASS_DEBUG) ||
+ (curr_aen.members.class > MFI_EVT_CLASS_DEAD)) {
+ dev_info(&instance->pdev->dev,
+ "%s %d out of range class %d send by application\n",
+ __func__, __LINE__, curr_aen.members.class);
+ return 0;
+ }
/*
* A class whose enum value is smaller is inclusive of all
@@ -3735,10 +6725,10 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
instance->aen_cmd->abort_aen = 1;
ret_val = megasas_issue_blocked_abort_cmd(instance,
instance->
- aen_cmd);
+ aen_cmd, 30);
if (ret_val) {
- printk(KERN_DEBUG "megasas: Failed to abort "
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to abort "
"previous AEN command\n");
return ret_val;
}
@@ -3765,13 +6755,14 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct megasas_evt_detail));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_EVENT_WAIT);
+ dcmd->mbox.w[0] = cpu_to_le32(seq_num);
instance->last_seq_num = seq_num;
- dcmd->data_xfer_len = sizeof(struct megasas_evt_detail);
- dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT;
- dcmd->mbox.w[0] = seq_num;
- dcmd->mbox.w[1] = curr_aen.word;
- dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h;
- dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail);
+ dcmd->mbox.w[1] = cpu_to_le32(curr_aen.word);
+
+ megasas_set_dma_settings(instance, dcmd, instance->evt_detail_h,
+ sizeof(struct megasas_evt_detail));
if (instance->aen_cmd != NULL) {
megasas_return_cmd(instance, cmd);
@@ -3793,6 +6784,101 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
return 0;
}
+/* megasas_get_target_prop - Send DCMD with below details to firmware.
+ *
+ * This DCMD will fetch few properties of LD/system PD defined
+ * in MR_TARGET_DEV_PROPERTIES. eg. Queue Depth, MDTS value.
+ *
+ * DCMD send by drivers whenever new target is added to the OS.
+ *
+ * dcmd.opcode - MR_DCMD_DEV_GET_TARGET_PROP
+ * dcmd.mbox.b[0] - DCMD is to be fired for LD or system PD.
+ * 0 = system PD, 1 = LD.
+ * dcmd.mbox.s[1] - TargetID for LD/system PD.
+ * dcmd.sge IN - Pointer to return MR_TARGET_DEV_PROPERTIES.
+ *
+ * @instance: Adapter soft state
+ * @sdev: OS provided scsi device
+ *
+ * Returns 0 on success non-zero on failure.
+ */
+int
+megasas_get_target_prop(struct megasas_instance *instance,
+ struct scsi_device *sdev)
+{
+ int ret;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ u16 targetId = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) +
+ sdev->id;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ dev_err(&instance->pdev->dev,
+ "Failed to get cmd %s\n", __func__);
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset(instance->tgt_prop, 0, sizeof(*instance->tgt_prop));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+ dcmd->mbox.b[0] = MEGASAS_IS_LOGICAL(sdev);
+
+ dcmd->mbox.s[1] = cpu_to_le16(targetId);
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0xFF;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len =
+ cpu_to_le32(sizeof(struct MR_TARGET_PROPERTIES));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_DRV_GET_TARGET_PROP);
+
+ megasas_set_dma_settings(instance, dcmd, instance->tgt_prop_h,
+ sizeof(struct MR_TARGET_PROPERTIES));
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ !instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance,
+ cmd, MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
+
+ switch (ret) {
+ case DCMD_TIMEOUT:
+ switch (dcmd_timeout_ocr_possible(instance)) {
+ case INITIATE_OCR:
+ cmd->flags |= DRV_DCMD_SKIP_REFIRE;
+ mutex_unlock(&instance->reset_mutex);
+ megasas_reset_fusion(instance->host,
+ MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ break;
+ case KILL_ADAPTER:
+ megaraid_sas_kill_hba(instance);
+ break;
+ case IGNORE_TIMEOUT:
+ dev_info(&instance->pdev->dev,
+ "Ignore DCMD timeout: %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ break;
+
+ default:
+ megasas_return_cmd(instance, cmd);
+ }
+ if (ret != DCMD_SUCCESS)
+ dev_err(&instance->pdev->dev,
+ "return from %s %d return value %d\n",
+ __func__, __LINE__, ret);
+
+ return ret;
+}
+
/**
* megasas_start_aen - Subscribes to AEN during driver load time
* @instance: Adapter soft state
@@ -3817,8 +6903,9 @@ static int megasas_start_aen(struct megasas_instance *instance)
class_locale.members.locale = MR_EVT_LOCALE_ALL;
class_locale.members.class = MR_EVT_CLASS_DEBUG;
- return megasas_register_aen(instance, eli.newest_seq_num + 1,
- class_locale.word);
+ return megasas_register_aen(instance,
+ le32_to_cpu(eli.newest_seq_num) + 1,
+ class_locale.word);
}
/**
@@ -3832,15 +6919,8 @@ static int megasas_io_attach(struct megasas_instance *instance)
/*
* Export parameters required by SCSI mid-layer
*/
- host->irq = instance->pdev->irq;
host->unique_id = instance->unique_id;
- if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
- (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
- host->can_queue =
- instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
- } else
- host->can_queue =
- instance->max_fw_cmds - MEGASAS_INT_CMDS;
+ host->can_queue = instance->max_scsi_cmds;
host->this_id = instance->init_id;
host->sg_tablesize = instance->max_num_sge;
@@ -3861,7 +6941,7 @@ static int megasas_io_attach(struct megasas_instance *instance)
(max_sectors <= MEGASAS_MAX_SECTORS)) {
instance->max_sectors_per_req = max_sectors;
} else {
- printk(KERN_INFO "megasas: max_sectors should be > 0"
+ dev_info(&instance->pdev->dev, "max_sectors should be > 0"
"and <= %d (or < 1MB for GEN2 controller)\n",
instance->max_sectors_per_req);
}
@@ -3875,47 +6955,508 @@ static int megasas_io_attach(struct megasas_instance *instance)
host->max_lun = MEGASAS_MAX_LUN;
host->max_cmd_len = 16;
- /* Fusion only supports host reset */
- if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) {
- host->hostt->eh_device_reset_handler = NULL;
- host->hostt->eh_bus_reset_handler = NULL;
+ /* Use shared host tagset only for fusion adaptors
+ * if there are managed interrupts (smp affinity enabled case).
+ * Single msix_vectors in kdump, so shared host tag is also disabled.
+ */
+
+ host->host_tagset = 0;
+ host->nr_hw_queues = 1;
+
+ if ((instance->adapter_type != MFI_SERIES) &&
+ (instance->msix_vectors > instance->low_latency_index_start) &&
+ host_tagset_enable &&
+ instance->smp_affinity_enable) {
+ host->host_tagset = 1;
+ host->nr_hw_queues = instance->msix_vectors -
+ instance->low_latency_index_start + instance->iopoll_q_count;
+ if (instance->iopoll_q_count)
+ host->nr_maps = 3;
+ } else {
+ instance->iopoll_q_count = 0;
}
+ dev_info(&instance->pdev->dev,
+ "Max firmware commands: %d shared with default "
+ "hw_queues = %d poll_queues %d\n", instance->max_fw_cmds,
+ host->nr_hw_queues - instance->iopoll_q_count,
+ instance->iopoll_q_count);
/*
* Notify the mid-layer about the new controller
*/
if (scsi_add_host(host, &instance->pdev->dev)) {
- printk(KERN_DEBUG "megasas: scsi_add_host failed\n");
+ dev_err(&instance->pdev->dev,
+ "Failed to add host from %s %d\n",
+ __func__, __LINE__);
return -ENODEV;
}
- /*
- * Trigger SCSI to scan our drives
- */
- scsi_scan_host(host);
return 0;
}
+/**
+ * megasas_set_dma_mask - Set DMA mask for supported controllers
+ *
+ * @instance: Adapter soft state
+ * Description:
+ *
+ * For Ventura, driver/FW will operate in 63bit DMA addresses.
+ *
+ * For invader-
+ * By default, driver/FW will operate in 32bit DMA addresses
+ * for consistent DMA mapping but if 32 bit consistent
+ * DMA mask fails, driver will try with 63 bit consistent
+ * mask provided FW is true 63bit DMA capable
+ *
+ * For older controllers(Thunderbolt and MFI based adapters)-
+ * driver/FW will operate in 32 bit consistent DMA addresses.
+ */
static int
-megasas_set_dma_mask(struct pci_dev *pdev)
+megasas_set_dma_mask(struct megasas_instance *instance)
{
- /*
- * All our contollers are capable of performing 64-bit DMA
- */
+ u64 consistent_mask;
+ struct pci_dev *pdev;
+ u32 scratch_pad_1;
+
+ pdev = instance->pdev;
+ consistent_mask = (instance->adapter_type >= VENTURA_SERIES) ?
+ DMA_BIT_MASK(63) : DMA_BIT_MASK(32);
+
if (IS_DMA64) {
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) {
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(63)) &&
+ dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
+ goto fail_set_dma_mask;
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)
+ if ((*pdev->dev.dma_mask == DMA_BIT_MASK(63)) &&
+ (dma_set_coherent_mask(&pdev->dev, consistent_mask) &&
+ dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))) {
+ /*
+ * If 32 bit DMA mask fails, then try for 64 bit mask
+ * for FW capable of handling 64 bit DMA.
+ */
+ scratch_pad_1 = megasas_readl
+ (instance, &instance->reg_set->outbound_scratch_pad_1);
+
+ if (!(scratch_pad_1 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET))
+ goto fail_set_dma_mask;
+ else if (dma_set_mask_and_coherent(&pdev->dev,
+ DMA_BIT_MASK(63)))
goto fail_set_dma_mask;
}
+ } else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
+ goto fail_set_dma_mask;
+
+ if (pdev->dev.coherent_dma_mask == DMA_BIT_MASK(32))
+ instance->consistent_mask_64bit = false;
+ else
+ instance->consistent_mask_64bit = true;
+
+ dev_info(&pdev->dev, "%s bit DMA mask and %s bit consistent mask\n",
+ ((*pdev->dev.dma_mask == DMA_BIT_MASK(63)) ? "63" : "32"),
+ (instance->consistent_mask_64bit ? "63" : "32"));
+
+ return 0;
+
+fail_set_dma_mask:
+ dev_err(&pdev->dev, "Failed to set DMA mask\n");
+ return -1;
+
+}
+
+/*
+ * megasas_set_adapter_type - Set adapter type.
+ * Supported controllers can be divided in
+ * different categories-
+ * enum MR_ADAPTER_TYPE {
+ * MFI_SERIES = 1,
+ * THUNDERBOLT_SERIES = 2,
+ * INVADER_SERIES = 3,
+ * VENTURA_SERIES = 4,
+ * AERO_SERIES = 5,
+ * };
+ * @instance: Adapter soft state
+ * return: void
+ */
+static inline void megasas_set_adapter_type(struct megasas_instance *instance)
+{
+ if ((instance->pdev->vendor == PCI_VENDOR_ID_DELL) &&
+ (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5)) {
+ instance->adapter_type = MFI_SERIES;
} else {
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)
- goto fail_set_dma_mask;
+ switch (instance->pdev->device) {
+ case PCI_DEVICE_ID_LSI_AERO_10E1:
+ case PCI_DEVICE_ID_LSI_AERO_10E2:
+ case PCI_DEVICE_ID_LSI_AERO_10E5:
+ case PCI_DEVICE_ID_LSI_AERO_10E6:
+ instance->adapter_type = AERO_SERIES;
+ break;
+ case PCI_DEVICE_ID_LSI_VENTURA:
+ case PCI_DEVICE_ID_LSI_CRUSADER:
+ case PCI_DEVICE_ID_LSI_HARPOON:
+ case PCI_DEVICE_ID_LSI_TOMCAT:
+ case PCI_DEVICE_ID_LSI_VENTURA_4PORT:
+ case PCI_DEVICE_ID_LSI_CRUSADER_4PORT:
+ instance->adapter_type = VENTURA_SERIES;
+ break;
+ case PCI_DEVICE_ID_LSI_FUSION:
+ case PCI_DEVICE_ID_LSI_PLASMA:
+ instance->adapter_type = THUNDERBOLT_SERIES;
+ break;
+ case PCI_DEVICE_ID_LSI_INVADER:
+ case PCI_DEVICE_ID_LSI_INTRUDER:
+ case PCI_DEVICE_ID_LSI_INTRUDER_24:
+ case PCI_DEVICE_ID_LSI_CUTLASS_52:
+ case PCI_DEVICE_ID_LSI_CUTLASS_53:
+ case PCI_DEVICE_ID_LSI_FURY:
+ instance->adapter_type = INVADER_SERIES;
+ break;
+ default: /* For all other supported controllers */
+ instance->adapter_type = MFI_SERIES;
+ break;
+ }
}
+}
+
+static inline int megasas_alloc_mfi_ctrl_mem(struct megasas_instance *instance)
+{
+ instance->producer = dma_alloc_coherent(&instance->pdev->dev,
+ sizeof(u32), &instance->producer_h, GFP_KERNEL);
+ instance->consumer = dma_alloc_coherent(&instance->pdev->dev,
+ sizeof(u32), &instance->consumer_h, GFP_KERNEL);
+
+ if (!instance->producer || !instance->consumer) {
+ dev_err(&instance->pdev->dev,
+ "Failed to allocate memory for producer, consumer\n");
+ return -1;
+ }
+
+ *instance->producer = 0;
+ *instance->consumer = 0;
return 0;
+}
-fail_set_dma_mask:
- return 1;
+/**
+ * megasas_alloc_ctrl_mem - Allocate per controller memory for core data
+ * structures which are not common across MFI
+ * adapters and fusion adapters.
+ * For MFI based adapters, allocate producer and
+ * consumer buffers. For fusion adapters, allocate
+ * memory for fusion context.
+ * @instance: Adapter soft state
+ * return: 0 for SUCCESS
+ */
+static int megasas_alloc_ctrl_mem(struct megasas_instance *instance)
+{
+ instance->reply_map = kcalloc(nr_cpu_ids, sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!instance->reply_map)
+ return -ENOMEM;
+
+ switch (instance->adapter_type) {
+ case MFI_SERIES:
+ if (megasas_alloc_mfi_ctrl_mem(instance))
+ return -ENOMEM;
+ break;
+ case AERO_SERIES:
+ case VENTURA_SERIES:
+ case THUNDERBOLT_SERIES:
+ case INVADER_SERIES:
+ if (megasas_alloc_fusion_context(instance))
+ return -ENOMEM;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * megasas_free_ctrl_mem - Free fusion context for fusion adapters and
+ * producer, consumer buffers for MFI adapters
+ *
+ * @instance - Adapter soft instance
+ *
+ */
+static inline void megasas_free_ctrl_mem(struct megasas_instance *instance)
+{
+ kfree(instance->reply_map);
+ if (instance->adapter_type == MFI_SERIES) {
+ if (instance->producer)
+ dma_free_coherent(&instance->pdev->dev, sizeof(u32),
+ instance->producer,
+ instance->producer_h);
+ if (instance->consumer)
+ dma_free_coherent(&instance->pdev->dev, sizeof(u32),
+ instance->consumer,
+ instance->consumer_h);
+ } else {
+ megasas_free_fusion_context(instance);
+ }
+}
+
+/**
+ * megasas_alloc_ctrl_dma_buffers - Allocate consistent DMA buffers during
+ * driver load time
+ *
+ * @instance: Adapter soft instance
+ *
+ * @return: O for SUCCESS
+ */
+static inline
+int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance)
+{
+ struct pci_dev *pdev = instance->pdev;
+ struct fusion_context *fusion = instance->ctrl_context;
+
+ instance->evt_detail = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct megasas_evt_detail),
+ &instance->evt_detail_h, GFP_KERNEL);
+
+ if (!instance->evt_detail) {
+ dev_err(&instance->pdev->dev,
+ "Failed to allocate event detail buffer\n");
+ return -ENOMEM;
+ }
+
+ if (fusion) {
+ fusion->ioc_init_request =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MPI2_IOC_INIT_REQUEST),
+ &fusion->ioc_init_request_phys,
+ GFP_KERNEL);
+
+ if (!fusion->ioc_init_request) {
+ dev_err(&pdev->dev,
+ "Failed to allocate ioc init request\n");
+ return -ENOMEM;
+ }
+
+ instance->snapdump_prop = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MR_SNAPDUMP_PROPERTIES),
+ &instance->snapdump_prop_h, GFP_KERNEL);
+
+ if (!instance->snapdump_prop)
+ dev_err(&pdev->dev,
+ "Failed to allocate snapdump properties buffer\n");
+
+ instance->host_device_list_buf = dma_alloc_coherent(&pdev->dev,
+ HOST_DEVICE_LIST_SZ,
+ &instance->host_device_list_buf_h,
+ GFP_KERNEL);
+
+ if (!instance->host_device_list_buf) {
+ dev_err(&pdev->dev,
+ "Failed to allocate targetid list buffer\n");
+ return -ENOMEM;
+ }
+
+ }
+
+ instance->pd_list_buf =
+ dma_alloc_coherent(&pdev->dev,
+ MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
+ &instance->pd_list_buf_h, GFP_KERNEL);
+
+ if (!instance->pd_list_buf) {
+ dev_err(&pdev->dev, "Failed to allocate PD list buffer\n");
+ return -ENOMEM;
+ }
+
+ instance->ctrl_info_buf =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct megasas_ctrl_info),
+ &instance->ctrl_info_buf_h, GFP_KERNEL);
+
+ if (!instance->ctrl_info_buf) {
+ dev_err(&pdev->dev,
+ "Failed to allocate controller info buffer\n");
+ return -ENOMEM;
+ }
+
+ instance->ld_list_buf =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MR_LD_LIST),
+ &instance->ld_list_buf_h, GFP_KERNEL);
+
+ if (!instance->ld_list_buf) {
+ dev_err(&pdev->dev, "Failed to allocate LD list buffer\n");
+ return -ENOMEM;
+ }
+
+ instance->ld_targetid_list_buf =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MR_LD_TARGETID_LIST),
+ &instance->ld_targetid_list_buf_h, GFP_KERNEL);
+
+ if (!instance->ld_targetid_list_buf) {
+ dev_err(&pdev->dev,
+ "Failed to allocate LD targetid list buffer\n");
+ return -ENOMEM;
+ }
+
+ if (!reset_devices) {
+ instance->system_info_buf =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MR_DRV_SYSTEM_INFO),
+ &instance->system_info_h, GFP_KERNEL);
+ instance->pd_info =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MR_PD_INFO),
+ &instance->pd_info_h, GFP_KERNEL);
+ instance->tgt_prop =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MR_TARGET_PROPERTIES),
+ &instance->tgt_prop_h, GFP_KERNEL);
+ instance->crash_dump_buf =
+ dma_alloc_coherent(&pdev->dev, CRASH_DMA_BUF_SIZE,
+ &instance->crash_dump_h, GFP_KERNEL);
+
+ if (!instance->system_info_buf)
+ dev_err(&instance->pdev->dev,
+ "Failed to allocate system info buffer\n");
+
+ if (!instance->pd_info)
+ dev_err(&instance->pdev->dev,
+ "Failed to allocate pd_info buffer\n");
+
+ if (!instance->tgt_prop)
+ dev_err(&instance->pdev->dev,
+ "Failed to allocate tgt_prop buffer\n");
+
+ if (!instance->crash_dump_buf)
+ dev_err(&instance->pdev->dev,
+ "Failed to allocate crash dump buffer\n");
+ }
+
+ return 0;
+}
+
+/*
+ * megasas_free_ctrl_dma_buffers - Free consistent DMA buffers allocated
+ * during driver load time
+ *
+ * @instance- Adapter soft instance
+ *
+ */
+static inline
+void megasas_free_ctrl_dma_buffers(struct megasas_instance *instance)
+{
+ struct pci_dev *pdev = instance->pdev;
+ struct fusion_context *fusion = instance->ctrl_context;
+
+ if (instance->evt_detail)
+ dma_free_coherent(&pdev->dev, sizeof(struct megasas_evt_detail),
+ instance->evt_detail,
+ instance->evt_detail_h);
+
+ if (fusion && fusion->ioc_init_request)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct MPI2_IOC_INIT_REQUEST),
+ fusion->ioc_init_request,
+ fusion->ioc_init_request_phys);
+
+ if (instance->pd_list_buf)
+ dma_free_coherent(&pdev->dev,
+ MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
+ instance->pd_list_buf,
+ instance->pd_list_buf_h);
+
+ if (instance->ld_list_buf)
+ dma_free_coherent(&pdev->dev, sizeof(struct MR_LD_LIST),
+ instance->ld_list_buf,
+ instance->ld_list_buf_h);
+
+ if (instance->ld_targetid_list_buf)
+ dma_free_coherent(&pdev->dev, sizeof(struct MR_LD_TARGETID_LIST),
+ instance->ld_targetid_list_buf,
+ instance->ld_targetid_list_buf_h);
+
+ if (instance->ctrl_info_buf)
+ dma_free_coherent(&pdev->dev, sizeof(struct megasas_ctrl_info),
+ instance->ctrl_info_buf,
+ instance->ctrl_info_buf_h);
+
+ if (instance->system_info_buf)
+ dma_free_coherent(&pdev->dev, sizeof(struct MR_DRV_SYSTEM_INFO),
+ instance->system_info_buf,
+ instance->system_info_h);
+
+ if (instance->pd_info)
+ dma_free_coherent(&pdev->dev, sizeof(struct MR_PD_INFO),
+ instance->pd_info, instance->pd_info_h);
+
+ if (instance->tgt_prop)
+ dma_free_coherent(&pdev->dev, sizeof(struct MR_TARGET_PROPERTIES),
+ instance->tgt_prop, instance->tgt_prop_h);
+
+ if (instance->crash_dump_buf)
+ dma_free_coherent(&pdev->dev, CRASH_DMA_BUF_SIZE,
+ instance->crash_dump_buf,
+ instance->crash_dump_h);
+
+ if (instance->snapdump_prop)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct MR_SNAPDUMP_PROPERTIES),
+ instance->snapdump_prop,
+ instance->snapdump_prop_h);
+
+ if (instance->host_device_list_buf)
+ dma_free_coherent(&pdev->dev,
+ HOST_DEVICE_LIST_SZ,
+ instance->host_device_list_buf,
+ instance->host_device_list_buf_h);
+
+}
+
+/*
+ * megasas_init_ctrl_params - Initialize controller's instance
+ * parameters before FW init
+ * @instance - Adapter soft instance
+ * @return - void
+ */
+static inline void megasas_init_ctrl_params(struct megasas_instance *instance)
+{
+ instance->fw_crash_state = UNAVAILABLE;
+
+ megasas_poll_wait_aen = 0;
+ instance->issuepend_done = 1;
+ atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL);
+
+ /*
+ * Initialize locks and queues
+ */
+ INIT_LIST_HEAD(&instance->cmd_pool);
+ INIT_LIST_HEAD(&instance->internal_reset_pending_q);
+
+ atomic_set(&instance->fw_outstanding, 0);
+ atomic64_set(&instance->total_io_count, 0);
+
+ init_waitqueue_head(&instance->int_cmd_wait_q);
+ init_waitqueue_head(&instance->abort_cmd_wait_q);
+
+ mutex_init(&instance->crashdump_lock);
+ spin_lock_init(&instance->mfi_pool_lock);
+ spin_lock_init(&instance->hba_lock);
+ spin_lock_init(&instance->stream_lock);
+ spin_lock_init(&instance->completion_lock);
+
+ mutex_init(&instance->reset_mutex);
+
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY))
+ instance->flag_ieee = 1;
+
+ instance->flag = 0;
+ instance->unload = 1;
+ instance->last_time = 0;
+ instance->disableOnlineCtrlReset = 1;
+ instance->UnevenSpanSupport = 0;
+ instance->smp_affinity_enable = smp_affinity_enable ? true : false;
+ instance->msix_load_balance = false;
+
+ if (instance->adapter_type != MFI_SERIES)
+ INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq);
+ else
+ INIT_WORK(&instance->work_init, process_fw_state_change_wq);
}
/**
@@ -3923,24 +7464,37 @@ fail_set_dma_mask:
* @pdev: PCI device structure
* @id: PCI ids of supported hotplugged adapter
*/
-static int __devinit
-megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+static int megasas_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
{
int rval, pos;
struct Scsi_Host *host;
struct megasas_instance *instance;
u16 control = 0;
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_LSI_AERO_10E0:
+ case PCI_DEVICE_ID_LSI_AERO_10E3:
+ case PCI_DEVICE_ID_LSI_AERO_10E4:
+ case PCI_DEVICE_ID_LSI_AERO_10E7:
+ dev_err(&pdev->dev, "Adapter is in non secure mode\n");
+ return 1;
+ case PCI_DEVICE_ID_LSI_AERO_10E1:
+ case PCI_DEVICE_ID_LSI_AERO_10E5:
+ dev_info(&pdev->dev, "Adapter is in configurable secure mode\n");
+ break;
+ }
+
/* Reset MSI-X in the kdump kernel */
if (reset_devices) {
pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
if (pos) {
- pci_read_config_word(pdev, msi_control_reg(pos),
+ pci_read_config_word(pdev, pos + PCI_MSIX_FLAGS,
&control);
if (control & PCI_MSIX_FLAGS_ENABLE) {
dev_info(&pdev->dev, "resetting MSI-X\n");
pci_write_config_word(pdev,
- msi_control_reg(pos),
+ pos + PCI_MSIX_FLAGS,
control &
~PCI_MSIX_FLAGS_ENABLE);
}
@@ -3948,16 +7502,6 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
}
/*
- * Announce PCI information
- */
- printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
- pdev->vendor, pdev->device, pdev->subsystem_vendor,
- pdev->subsystem_device);
-
- printk("bus %d:slot %d:func %d\n",
- pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
-
- /*
* PCI prepping: enable device set bus mastering and dma mask
*/
rval = pci_enable_device_mem(pdev);
@@ -3968,120 +7512,27 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
- if (megasas_set_dma_mask(pdev))
- goto fail_set_dma_mask;
-
host = scsi_host_alloc(&megasas_template,
sizeof(struct megasas_instance));
if (!host) {
- printk(KERN_DEBUG "megasas: scsi_host_alloc failed\n");
+ dev_printk(KERN_DEBUG, &pdev->dev, "scsi_host_alloc failed\n");
goto fail_alloc_instance;
}
instance = (struct megasas_instance *)host->hostdata;
memset(instance, 0, sizeof(*instance));
- atomic_set( &instance->fw_reset_no_pci_access, 0 );
- instance->pdev = pdev;
-
- switch (instance->pdev->device) {
- case PCI_DEVICE_ID_LSI_FUSION:
- {
- struct fusion_context *fusion;
-
- instance->ctrl_context =
- kzalloc(sizeof(struct fusion_context), GFP_KERNEL);
- if (!instance->ctrl_context) {
- printk(KERN_DEBUG "megasas: Failed to allocate "
- "memory for Fusion context info\n");
- goto fail_alloc_dma_buf;
- }
- fusion = instance->ctrl_context;
- INIT_LIST_HEAD(&fusion->cmd_pool);
- spin_lock_init(&fusion->cmd_pool_lock);
- }
- break;
- default: /* For all other supported controllers */
-
- instance->producer =
- pci_alloc_consistent(pdev, sizeof(u32),
- &instance->producer_h);
- instance->consumer =
- pci_alloc_consistent(pdev, sizeof(u32),
- &instance->consumer_h);
-
- if (!instance->producer || !instance->consumer) {
- printk(KERN_DEBUG "megasas: Failed to allocate"
- "memory for producer, consumer\n");
- goto fail_alloc_dma_buf;
- }
-
- *instance->producer = 0;
- *instance->consumer = 0;
- break;
- }
-
- megasas_poll_wait_aen = 0;
- instance->flag_ieee = 0;
- instance->ev = NULL;
- instance->issuepend_done = 1;
- instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
- megasas_poll_wait_aen = 0;
-
- instance->evt_detail = pci_alloc_consistent(pdev,
- sizeof(struct
- megasas_evt_detail),
- &instance->evt_detail_h);
-
- if (!instance->evt_detail) {
- printk(KERN_DEBUG "megasas: Failed to allocate memory for "
- "event detail structure\n");
- goto fail_alloc_dma_buf;
- }
-
- /*
- * Initialize locks and queues
- */
- INIT_LIST_HEAD(&instance->cmd_pool);
- INIT_LIST_HEAD(&instance->internal_reset_pending_q);
-
- atomic_set(&instance->fw_outstanding,0);
-
- init_waitqueue_head(&instance->int_cmd_wait_q);
- init_waitqueue_head(&instance->abort_cmd_wait_q);
-
- spin_lock_init(&instance->cmd_pool_lock);
- spin_lock_init(&instance->hba_lock);
- spin_lock_init(&instance->completion_lock);
- spin_lock_init(&poll_aen_lock);
-
- mutex_init(&instance->aen_mutex);
- mutex_init(&instance->reset_mutex);
+ atomic_set(&instance->fw_reset_no_pci_access, 0);
/*
* Initialize PCI related and misc parameters
*/
+ instance->pdev = pdev;
instance->host = host;
- instance->unique_id = pdev->bus->number << 8 | pdev->devfn;
+ instance->unique_id = pci_dev_id(pdev);
instance->init_id = MEGASAS_DEFAULT_INIT_ID;
- if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
- (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
- instance->flag_ieee = 1;
- sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
- } else
- sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
-
- megasas_dbg_lvl = 0;
- instance->flag = 0;
- instance->unload = 1;
- instance->last_time = 0;
- instance->disableOnlineCtrlReset = 1;
-
- if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)
- INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq);
- else
- INIT_WORK(&instance->work_init, process_fw_state_change_wq);
+ megasas_set_adapter_type(instance);
/*
* Initialize MFI Firmware
@@ -4089,18 +7540,29 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (megasas_init_fw(instance))
goto fail_init_mfi;
- /*
- * Register IRQ
- */
- if (request_irq(instance->msi_flag ? instance->msixentry.vector :
- pdev->irq, instance->instancet->service_isr,
- IRQF_SHARED, "megasas", instance)) {
- printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
- goto fail_irq;
+ if (instance->requestorId) {
+ if (instance->PlasmaFW111) {
+ instance->vf_affiliation_111 =
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct MR_LD_VF_AFFILIATION_111),
+ &instance->vf_affiliation_111_h,
+ GFP_KERNEL);
+ if (!instance->vf_affiliation_111)
+ dev_warn(&pdev->dev, "Can't allocate "
+ "memory for VF affiliation buffer\n");
+ } else {
+ instance->vf_affiliation =
+ dma_alloc_coherent(&pdev->dev,
+ (MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION),
+ &instance->vf_affiliation_h,
+ GFP_KERNEL);
+ if (!instance->vf_affiliation)
+ dev_warn(&pdev->dev, "Can't allocate "
+ "memory for VF affiliation buffer\n");
+ }
}
- instance->instancet->enable_intr(instance->reg_set);
-
/*
* Store instance in PCI softstate
*/
@@ -4121,51 +7583,60 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto fail_io_attach;
instance->unload = 0;
+ /*
+ * Trigger SCSI to scan our drives
+ */
+ if (!instance->enable_fw_dev_list ||
+ (instance->host_device_list_buf->count > 0))
+ scsi_scan_host(host);
/*
* Initiate AEN (Asynchronous Event Notification)
*/
if (megasas_start_aen(instance)) {
- printk(KERN_DEBUG "megasas: start aen failed\n");
+ dev_printk(KERN_DEBUG, &pdev->dev, "start aen failed\n");
goto fail_start_aen;
}
+ megasas_setup_debugfs(instance);
+
+ /* Get current SR-IOV LD/VF affiliation */
+ if (instance->requestorId)
+ megasas_get_ld_vf_affiliation(instance, 1);
+
return 0;
- fail_start_aen:
- fail_io_attach:
+fail_start_aen:
+ instance->unload = 1;
+ scsi_remove_host(instance->host);
+fail_io_attach:
megasas_mgmt_info.count--;
- megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL;
megasas_mgmt_info.max_index--;
+ megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL;
+
+ if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+ timer_delete_sync(&instance->sriov_heartbeat_timer);
+
+ instance->instancet->disable_intr(instance);
+ megasas_destroy_irqs(instance);
- pci_set_drvdata(pdev, NULL);
- instance->instancet->disable_intr(instance->reg_set);
- free_irq(instance->msi_flag ? instance->msixentry.vector :
- instance->pdev->irq, instance);
-fail_irq:
- if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)
+ if (instance->adapter_type != MFI_SERIES)
megasas_release_fusion(instance);
else
megasas_release_mfi(instance);
- fail_init_mfi:
- if (instance->msi_flag)
- pci_disable_msix(instance->pdev);
- fail_alloc_dma_buf:
- if (instance->evt_detail)
- pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
- instance->evt_detail,
- instance->evt_detail_h);
- if (instance->producer)
- pci_free_consistent(pdev, sizeof(u32), instance->producer,
- instance->producer_h);
- if (instance->consumer)
- pci_free_consistent(pdev, sizeof(u32), instance->consumer,
- instance->consumer_h);
- scsi_host_put(host);
+ if (instance->msix_vectors)
+ pci_free_irq_vectors(instance->pdev);
+ instance->msix_vectors = 0;
- fail_alloc_instance:
- fail_set_dma_mask:
+ if (instance->fw_crash_state != UNAVAILABLE)
+ megasas_free_host_crash_buffer(instance);
+
+ if (instance->adapter_type != MFI_SERIES)
+ megasas_fusion_stop_watchdog(instance);
+fail_init_mfi:
+ scsi_host_put(host);
+fail_alloc_instance:
pci_disable_device(pdev);
return -ENODEV;
@@ -4180,7 +7651,7 @@ static void megasas_flush_cache(struct megasas_instance *instance)
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR)
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)
return;
cmd = megasas_get_cmd(instance);
@@ -4195,18 +7666,21 @@ static void megasas_flush_cache(struct megasas_instance *instance)
dcmd->cmd = MFI_CMD_DCMD;
dcmd->cmd_status = 0x0;
dcmd->sge_count = 0;
- dcmd->flags = MFI_FRAME_DIR_NONE;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_NONE);
dcmd->timeout = 0;
dcmd->pad_0 = 0;
dcmd->data_xfer_len = 0;
- dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH;
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_CACHE_FLUSH);
dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE;
- megasas_issue_blocked_cmd(instance, cmd);
+ if (megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS)
+ != DCMD_SUCCESS) {
+ dev_err(&instance->pdev->dev,
+ "return from %s %d\n", __func__, __LINE__);
+ return;
+ }
megasas_return_cmd(instance, cmd);
-
- return;
}
/**
@@ -4220,7 +7694,7 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR)
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)
return;
cmd = megasas_get_cmd(instance);
@@ -4229,10 +7703,15 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
return;
if (instance->aen_cmd)
- megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd);
+ megasas_issue_blocked_abort_cmd(instance,
+ instance->aen_cmd, MFI_IO_TIMEOUT_SECS);
if (instance->map_update_cmd)
megasas_issue_blocked_abort_cmd(instance,
- instance->map_update_cmd);
+ instance->map_update_cmd, MFI_IO_TIMEOUT_SECS);
+ if (instance->jbod_seq_cmd)
+ megasas_issue_blocked_abort_cmd(instance,
+ instance->jbod_seq_cmd, MFI_IO_TIMEOUT_SECS);
+
dcmd = &cmd->frame->dcmd;
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
@@ -4240,37 +7719,47 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
dcmd->cmd = MFI_CMD_DCMD;
dcmd->cmd_status = 0x0;
dcmd->sge_count = 0;
- dcmd->flags = MFI_FRAME_DIR_NONE;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_NONE);
dcmd->timeout = 0;
dcmd->pad_0 = 0;
dcmd->data_xfer_len = 0;
- dcmd->opcode = opcode;
+ dcmd->opcode = cpu_to_le32(opcode);
- megasas_issue_blocked_cmd(instance, cmd);
+ if (megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS)
+ != DCMD_SUCCESS) {
+ dev_err(&instance->pdev->dev,
+ "return from %s %d\n", __func__, __LINE__);
+ return;
+ }
megasas_return_cmd(instance, cmd);
-
- return;
}
-#ifdef CONFIG_PM
/**
* megasas_suspend - driver suspend entry point
- * @pdev: PCI device structure
- * @state: PCI power state to suspend routine
+ * @dev: Device structure
*/
-static int
-megasas_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused
+megasas_suspend(struct device *dev)
{
- struct Scsi_Host *host;
struct megasas_instance *instance;
- instance = pci_get_drvdata(pdev);
- host = instance->host;
+ instance = dev_get_drvdata(dev);
+
+ if (!instance)
+ return 0;
+
instance->unload = 1;
- if (poll_mode_io)
- del_timer_sync(&instance->io_completion_timer);
+ dev_info(dev, "%s is called\n", __func__);
+
+ /* Shutdown SR-IOV heartbeat timer */
+ if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+ timer_delete_sync(&instance->sriov_heartbeat_timer);
+
+ /* Stop the FW fault detection watchdog */
+ if (instance->adapter_type != MFI_SERIES)
+ megasas_fusion_stop_watchdog(instance);
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
@@ -4278,58 +7767,77 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
/* cancel the delayed work if this work still in queue */
if (instance->ev != NULL) {
struct megasas_aen_event *ev = instance->ev;
- cancel_delayed_work_sync(
- (struct delayed_work *)&ev->hotplug_work);
+ cancel_delayed_work_sync(&ev->hotplug_work);
instance->ev = NULL;
}
tasklet_kill(&instance->isr_tasklet);
pci_set_drvdata(instance->pdev, instance);
- instance->instancet->disable_intr(instance->reg_set);
- free_irq(instance->msi_flag ? instance->msixentry.vector :
- instance->pdev->irq, instance);
- if (instance->msi_flag)
- pci_disable_msix(instance->pdev);
+ instance->instancet->disable_intr(instance);
- pci_save_state(pdev);
- pci_disable_device(pdev);
+ megasas_destroy_irqs(instance);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ if (instance->msix_vectors)
+ pci_free_irq_vectors(instance->pdev);
return 0;
}
/**
* megasas_resume- driver resume entry point
- * @pdev: PCI device structure
+ * @dev: Device structure
*/
-static int
-megasas_resume(struct pci_dev *pdev)
+static int __maybe_unused
+megasas_resume(struct device *dev)
{
int rval;
struct Scsi_Host *host;
struct megasas_instance *instance;
+ u32 status_reg;
+
+ instance = dev_get_drvdata(dev);
+
+ if (!instance)
+ return 0;
- instance = pci_get_drvdata(pdev);
host = instance->host;
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
+
+ dev_info(dev, "%s is called\n", __func__);
/*
- * PCI prepping: enable device set bus mastering and dma mask
+ * We expect the FW state to be READY
*/
- rval = pci_enable_device_mem(pdev);
- if (rval) {
- printk(KERN_ERR "megasas: Enable device failed\n");
- return rval;
- }
+ if (megasas_transition_to_ready(instance, 0)) {
+ dev_info(&instance->pdev->dev,
+ "Failed to transition controller to ready from %s!\n",
+ __func__);
+ if (instance->adapter_type != MFI_SERIES) {
+ status_reg =
+ instance->instancet->read_fw_status_reg(instance);
+ if (!(status_reg & MFI_RESET_ADAPTER) ||
+ ((megasas_adp_reset_wait_for_ready
+ (instance, true, 0)) == FAILED))
+ goto fail_ready_state;
+ } else {
+ atomic_set(&instance->fw_reset_no_pci_access, 1);
+ instance->instancet->adp_reset
+ (instance, instance->reg_set);
+ atomic_set(&instance->fw_reset_no_pci_access, 0);
- pci_set_master(pdev);
+ /* waiting for about 30 seconds before retry */
+ ssleep(30);
- if (megasas_set_dma_mask(pdev))
+ if (megasas_transition_to_ready(instance, 0))
+ goto fail_ready_state;
+ }
+
+ dev_info(&instance->pdev->dev,
+ "FW restarted successfully from %s!\n",
+ __func__);
+ }
+ if (megasas_set_dma_mask(instance))
goto fail_set_dma_mask;
/*
@@ -4337,20 +7845,22 @@ megasas_resume(struct pci_dev *pdev)
*/
atomic_set(&instance->fw_outstanding, 0);
-
- /*
- * We expect the FW state to be READY
- */
- if (megasas_transition_to_ready(instance))
- goto fail_ready_state;
+ atomic_set(&instance->ldio_outstanding, 0);
/* Now re-enable MSI-X */
- if (instance->msi_flag)
- pci_enable_msix(instance->pdev, &instance->msixentry, 1);
+ if (instance->msix_vectors)
+ megasas_alloc_irq_vectors(instance);
+
+ if (!instance->msix_vectors) {
+ rval = pci_alloc_irq_vectors(instance->pdev, 1, 1,
+ PCI_IRQ_INTX);
+ if (rval < 0)
+ goto fail_reenable_msix;
+ }
+
+ megasas_setup_reply_map(instance);
- switch (instance->pdev->device) {
- case PCI_DEVICE_ID_LSI_FUSION:
- {
+ if (instance->adapter_type != MFI_SERIES) {
megasas_reset_reply_desc(instance);
if (megasas_ioc_init_fusion(instance)) {
megasas_free_cmds(instance);
@@ -4359,104 +7869,148 @@ megasas_resume(struct pci_dev *pdev)
}
if (!megasas_get_map_info(instance))
megasas_sync_map_info(instance);
- }
- break;
- default:
+ } else {
*instance->producer = 0;
*instance->consumer = 0;
if (megasas_issue_init_mfi(instance))
goto fail_init_mfi;
- break;
}
+ if (megasas_get_ctrl_info(instance) != DCMD_SUCCESS)
+ goto fail_init_mfi;
+
tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
(unsigned long)instance);
- /*
- * Register IRQ
- */
- if (request_irq(instance->msi_flag ? instance->msixentry.vector :
- pdev->irq, instance->instancet->service_isr,
- IRQF_SHARED, "megasas", instance)) {
- printk(KERN_ERR "megasas: Failed to register IRQ\n");
- goto fail_irq;
- }
+ if (instance->msix_vectors ?
+ megasas_setup_irqs_msix(instance, 0) :
+ megasas_setup_irqs_ioapic(instance))
+ goto fail_init_mfi;
+
+ if (instance->adapter_type != MFI_SERIES)
+ megasas_setup_irq_poll(instance);
- instance->instancet->enable_intr(instance->reg_set);
+ /* Re-launch SR-IOV heartbeat timer */
+ if (instance->requestorId) {
+ if (!megasas_sriov_start_heartbeat(instance, 0))
+ megasas_start_timer(instance);
+ else {
+ instance->skip_heartbeat_timer_del = 1;
+ goto fail_init_mfi;
+ }
+ }
- /* Initialize the cmd completion timer */
- if (poll_mode_io)
- megasas_start_timer(instance, &instance->io_completion_timer,
- megasas_io_completion_timer,
- MEGASAS_COMPLETION_TIMER_INTERVAL);
+ instance->instancet->enable_intr(instance);
+ megasas_setup_jbod_map(instance);
instance->unload = 0;
/*
* Initiate AEN (Asynchronous Event Notification)
*/
if (megasas_start_aen(instance))
- printk(KERN_ERR "megasas: Start AEN failed\n");
+ dev_err(&instance->pdev->dev, "Start AEN failed\n");
+
+ /* Re-launch FW fault watchdog */
+ if (instance->adapter_type != MFI_SERIES)
+ if (megasas_fusion_start_watchdog(instance) != SUCCESS)
+ goto fail_start_watchdog;
return 0;
-fail_irq:
+fail_start_watchdog:
+ if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+ timer_delete_sync(&instance->sriov_heartbeat_timer);
fail_init_mfi:
- if (instance->evt_detail)
- pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
- instance->evt_detail,
- instance->evt_detail_h);
-
- if (instance->producer)
- pci_free_consistent(pdev, sizeof(u32), instance->producer,
- instance->producer_h);
- if (instance->consumer)
- pci_free_consistent(pdev, sizeof(u32), instance->consumer,
- instance->consumer_h);
+ megasas_free_ctrl_dma_buffers(instance);
+ megasas_free_ctrl_mem(instance);
scsi_host_put(host);
+fail_reenable_msix:
fail_set_dma_mask:
fail_ready_state:
- pci_disable_device(pdev);
-
return -ENODEV;
}
-#else
-#define megasas_suspend NULL
-#define megasas_resume NULL
-#endif
+
+static inline int
+megasas_wait_for_adapter_operational(struct megasas_instance *instance)
+{
+ int wait_time = MEGASAS_RESET_WAIT_TIME * 2;
+ int i;
+ u8 adp_state;
+
+ for (i = 0; i < wait_time; i++) {
+ adp_state = atomic_read(&instance->adprecovery);
+ if ((adp_state == MEGASAS_HBA_OPERATIONAL) ||
+ (adp_state == MEGASAS_HW_CRITICAL_ERROR))
+ break;
+
+ if (!(i % MEGASAS_RESET_NOTICE_INTERVAL))
+ dev_notice(&instance->pdev->dev, "waiting for controller reset to finish\n");
+
+ msleep(1000);
+ }
+
+ if (adp_state != MEGASAS_HBA_OPERATIONAL) {
+ dev_info(&instance->pdev->dev,
+ "%s HBA failed to become operational, adp_state %d\n",
+ __func__, adp_state);
+ return 1;
+ }
+
+ return 0;
+}
/**
* megasas_detach_one - PCI hot"un"plug entry point
* @pdev: PCI device structure
*/
-static void __devexit megasas_detach_one(struct pci_dev *pdev)
+static void megasas_detach_one(struct pci_dev *pdev)
{
int i;
struct Scsi_Host *host;
struct megasas_instance *instance;
struct fusion_context *fusion;
+ size_t pd_seq_map_sz;
instance = pci_get_drvdata(pdev);
- instance->unload = 1;
+
+ if (!instance)
+ return;
+
host = instance->host;
fusion = instance->ctrl_context;
- if (poll_mode_io)
- del_timer_sync(&instance->io_completion_timer);
+ /* Shutdown SR-IOV heartbeat timer */
+ if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+ timer_delete_sync(&instance->sriov_heartbeat_timer);
+
+ /* Stop the FW fault detection watchdog */
+ if (instance->adapter_type != MFI_SERIES)
+ megasas_fusion_stop_watchdog(instance);
+ if (instance->fw_crash_state != UNAVAILABLE)
+ megasas_free_host_crash_buffer(instance);
scsi_remove_host(instance->host);
+ instance->unload = 1;
+
+ if (megasas_wait_for_adapter_operational(instance))
+ goto skip_firing_dcmds;
+
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
+skip_firing_dcmds:
/* cancel the delayed work if this work still in queue*/
if (instance->ev != NULL) {
struct megasas_aen_event *ev = instance->ev;
- cancel_delayed_work_sync(
- (struct delayed_work *)&ev->hotplug_work);
+ cancel_delayed_work_sync(&ev->hotplug_work);
instance->ev = NULL;
}
+ /* cancel all wait events */
+ wake_up_all(&instance->int_cmd_wait_q);
+
tasklet_kill(&instance->isr_tasklet);
/*
@@ -4472,70 +8026,109 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
}
}
- pci_set_drvdata(instance->pdev, NULL);
+ instance->instancet->disable_intr(instance);
- instance->instancet->disable_intr(instance->reg_set);
+ megasas_destroy_irqs(instance);
- free_irq(instance->msi_flag ? instance->msixentry.vector :
- instance->pdev->irq, instance);
- if (instance->msi_flag)
- pci_disable_msix(instance->pdev);
+ if (instance->msix_vectors)
+ pci_free_irq_vectors(instance->pdev);
- switch (instance->pdev->device) {
- case PCI_DEVICE_ID_LSI_FUSION:
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i)
+ kfree(fusion->stream_detect_by_ld[i]);
+ kfree(fusion->stream_detect_by_ld);
+ fusion->stream_detect_by_ld = NULL;
+ }
+
+
+ if (instance->adapter_type != MFI_SERIES) {
megasas_release_fusion(instance);
- for (i = 0; i < 2 ; i++)
+ pd_seq_map_sz =
+ struct_size_t(struct MR_PD_CFG_SEQ_NUM_SYNC,
+ seq, MAX_PHYSICAL_DEVICES);
+ for (i = 0; i < 2 ; i++) {
if (fusion->ld_map[i])
dma_free_coherent(&instance->pdev->dev,
- fusion->map_sz,
+ fusion->max_map_sz,
fusion->ld_map[i],
- fusion->
- ld_map_phys[i]);
- kfree(instance->ctrl_context);
- break;
- default:
+ fusion->ld_map_phys[i]);
+ if (fusion->ld_drv_map[i]) {
+ if (is_vmalloc_addr(fusion->ld_drv_map[i]))
+ vfree(fusion->ld_drv_map[i]);
+ else
+ free_pages((ulong)fusion->ld_drv_map[i],
+ fusion->drv_map_pages);
+ }
+
+ if (fusion->pd_seq_sync[i])
+ dma_free_coherent(&instance->pdev->dev,
+ pd_seq_map_sz,
+ fusion->pd_seq_sync[i],
+ fusion->pd_seq_phys[i]);
+ }
+ } else {
megasas_release_mfi(instance);
- pci_free_consistent(pdev,
- sizeof(struct megasas_evt_detail),
- instance->evt_detail,
- instance->evt_detail_h);
- pci_free_consistent(pdev, sizeof(u32),
- instance->producer,
- instance->producer_h);
- pci_free_consistent(pdev, sizeof(u32),
- instance->consumer,
- instance->consumer_h);
- break;
}
- scsi_host_put(host);
+ if (instance->vf_affiliation)
+ dma_free_coherent(&pdev->dev, (MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION),
+ instance->vf_affiliation,
+ instance->vf_affiliation_h);
- pci_set_drvdata(pdev, NULL);
+ if (instance->vf_affiliation_111)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct MR_LD_VF_AFFILIATION_111),
+ instance->vf_affiliation_111,
+ instance->vf_affiliation_111_h);
- pci_disable_device(pdev);
+ if (instance->hb_host_mem)
+ dma_free_coherent(&pdev->dev, sizeof(struct MR_CTRL_HB_HOST_MEM),
+ instance->hb_host_mem,
+ instance->hb_host_mem_h);
- return;
+ megasas_free_ctrl_dma_buffers(instance);
+
+ megasas_free_ctrl_mem(instance);
+
+ megasas_destroy_debugfs(instance);
+
+ scsi_host_put(host);
+
+ pci_disable_device(pdev);
}
/**
* megasas_shutdown - Shutdown entry point
- * @device: Generic device structure
+ * @pdev: PCI device structure
*/
static void megasas_shutdown(struct pci_dev *pdev)
{
struct megasas_instance *instance = pci_get_drvdata(pdev);
+
+ if (!instance)
+ return;
+
instance->unload = 1;
+
+ if (megasas_wait_for_adapter_operational(instance))
+ goto skip_firing_dcmds;
+
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
- instance->instancet->disable_intr(instance->reg_set);
- free_irq(instance->msi_flag ? instance->msixentry.vector :
- instance->pdev->irq, instance);
- if (instance->msi_flag)
- pci_disable_msix(instance->pdev);
+
+skip_firing_dcmds:
+ instance->instancet->disable_intr(instance);
+ megasas_destroy_irqs(instance);
+
+ if (instance->msix_vectors)
+ pci_free_irq_vectors(instance->pdev);
}
-/**
+/*
* megasas_mgmt_open - char node "open" entry point
+ * @inode: char node inode
+ * @filep: char node file
*/
static int megasas_mgmt_open(struct inode *inode, struct file *filep)
{
@@ -4548,8 +8141,11 @@ static int megasas_mgmt_open(struct inode *inode, struct file *filep)
return 0;
}
-/**
+/*
* megasas_mgmt_fasync - Async notifier registration from applications
+ * @fd: char node file descriptor number
+ * @filep: char node file
+ * @mode: notifier on/off
*
* This function adds the calling process to a driver global queue. When an
* event occurs, SIGIO will be sent to all processes in this queue.
@@ -4575,53 +8171,110 @@ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode)
return rc;
}
-/**
+/*
* megasas_mgmt_poll - char node "poll" entry point
- * */
-static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait)
+ * @filep: char node file
+ * @wait: Events to poll for
+ */
+static __poll_t megasas_mgmt_poll(struct file *file, poll_table *wait)
{
- unsigned int mask;
+ __poll_t mask;
unsigned long flags;
+
poll_wait(file, &megasas_poll_wait, wait);
spin_lock_irqsave(&poll_aen_lock, flags);
if (megasas_poll_wait_aen)
- mask = (POLLIN | POLLRDNORM);
+ mask = (EPOLLIN | EPOLLRDNORM);
else
mask = 0;
+ megasas_poll_wait_aen = 0;
spin_unlock_irqrestore(&poll_aen_lock, flags);
return mask;
}
+/*
+ * megasas_set_crash_dump_params_ioctl:
+ * Send CRASH_DUMP_MODE DCMD to all controllers
+ * @cmd: MFI command frame
+ */
+
+static int megasas_set_crash_dump_params_ioctl(struct megasas_cmd *cmd)
+{
+ struct megasas_instance *local_instance;
+ int i, error = 0;
+ int crash_support;
+
+ crash_support = cmd->frame->dcmd.mbox.w[0];
+
+ for (i = 0; i < megasas_mgmt_info.max_index; i++) {
+ local_instance = megasas_mgmt_info.instance[i];
+ if (local_instance && local_instance->crash_dump_drv_support) {
+ if ((atomic_read(&local_instance->adprecovery) ==
+ MEGASAS_HBA_OPERATIONAL) &&
+ !megasas_set_crash_dump_params(local_instance,
+ crash_support)) {
+ local_instance->crash_dump_app_support =
+ crash_support;
+ dev_info(&local_instance->pdev->dev,
+ "Application firmware crash "
+ "dump mode set success\n");
+ error = 0;
+ } else {
+ dev_info(&local_instance->pdev->dev,
+ "Application firmware crash "
+ "dump mode set failed\n");
+ error = -1;
+ }
+ }
+ }
+ return error;
+}
+
/**
* megasas_mgmt_fw_ioctl - Issues management ioctls to FW
* @instance: Adapter soft state
- * @argp: User's ioctl packet
+ * @user_ioc: User's ioctl packet
+ * @ioc: ioctl packet
*/
static int
megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
struct megasas_iocpacket __user * user_ioc,
struct megasas_iocpacket *ioc)
{
- struct megasas_sge32 *kern_sge32;
+ struct megasas_sge64 *kern_sge64 = NULL;
+ struct megasas_sge32 *kern_sge32 = NULL;
struct megasas_cmd *cmd;
void *kbuff_arr[MAX_IOCTL_SGE];
dma_addr_t buf_handle = 0;
int error = 0, i;
void *sense = NULL;
dma_addr_t sense_handle;
- unsigned long *sense_ptr;
+ void *sense_ptr;
+ u32 opcode = 0;
+ int ret = DCMD_SUCCESS;
memset(kbuff_arr, 0, sizeof(kbuff_arr));
if (ioc->sge_count > MAX_IOCTL_SGE) {
- printk(KERN_DEBUG "megasas: SGE count [%d] > max limit [%d]\n",
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "SGE count [%d] > max limit [%d]\n",
ioc->sge_count, MAX_IOCTL_SGE);
return -EINVAL;
}
+ if ((ioc->frame.hdr.cmd >= MFI_CMD_OP_COUNT) ||
+ ((ioc->frame.hdr.cmd == MFI_CMD_NVME) &&
+ !instance->support_nvme_passthru) ||
+ ((ioc->frame.hdr.cmd == MFI_CMD_TOOLBOX) &&
+ !instance->support_pci_lane_margining)) {
+ dev_err(&instance->pdev->dev,
+ "Received invalid ioctl command 0x%x\n",
+ ioc->frame.hdr.cmd);
+ return -ENOTSUPP;
+ }
+
cmd = megasas_get_cmd(instance);
if (!cmd) {
- printk(KERN_DEBUG "megasas: Failed to get a cmd packet\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a cmd packet\n");
return -ENOMEM;
}
@@ -4632,9 +8285,37 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
* alone separately
*/
memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
- cmd->frame->hdr.context = cmd->index;
+ cmd->frame->hdr.context = cpu_to_le32(cmd->index);
cmd->frame->hdr.pad_0 = 0;
+ cmd->frame->hdr.flags &= (~MFI_FRAME_IEEE);
+
+ if (instance->consistent_mask_64bit)
+ cmd->frame->hdr.flags |= cpu_to_le16((MFI_FRAME_SGL64 |
+ MFI_FRAME_SENSE64));
+ else
+ cmd->frame->hdr.flags &= cpu_to_le16(~(MFI_FRAME_SGL64 |
+ MFI_FRAME_SENSE64));
+
+ if (cmd->frame->hdr.cmd == MFI_CMD_DCMD)
+ opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
+
+ if (opcode == MR_DCMD_CTRL_SHUTDOWN) {
+ mutex_lock(&instance->reset_mutex);
+ if (megasas_get_ctrl_info(instance) != DCMD_SUCCESS) {
+ megasas_return_cmd(instance, cmd);
+ mutex_unlock(&instance->reset_mutex);
+ return -1;
+ }
+ mutex_unlock(&instance->reset_mutex);
+ }
+
+ if (opcode == MR_DRIVER_SET_APP_CRASHDUMP_MODE) {
+ error = megasas_set_crash_dump_params_ioctl(cmd);
+ megasas_return_cmd(instance, cmd);
+ return error;
+ }
+
/*
* The management interface between applications and the fw uses
* MFI frames. E.g, RAID configuration changes, LD property changes
@@ -4643,8 +8324,12 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
* kernel buffers in SGLs. The location of SGL is embedded in the
* struct iocpacket itself.
*/
- kern_sge32 = (struct megasas_sge32 *)
- ((unsigned long)cmd->frame + ioc->sgl_off);
+ if (instance->consistent_mask_64bit)
+ kern_sge64 = (struct megasas_sge64 *)
+ ((unsigned long)cmd->frame + ioc->sgl_off);
+ else
+ kern_sge32 = (struct megasas_sge32 *)
+ ((unsigned long)cmd->frame + ioc->sgl_off);
/*
* For each user buffer, create a mirror buffer and copy in
@@ -4657,18 +8342,23 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
ioc->sgl[i].iov_len,
&buf_handle, GFP_KERNEL);
if (!kbuff_arr[i]) {
- printk(KERN_DEBUG "megasas: Failed to alloc "
- "kernel SGL buffer for IOCTL \n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc "
+ "kernel SGL buffer for IOCTL\n");
error = -ENOMEM;
goto out;
}
/*
* We don't change the dma_coherent_mask, so
- * pci_alloc_consistent only returns 32bit addresses
+ * dma_alloc_coherent only returns 32bit addresses
*/
- kern_sge32[i].phys_addr = (u32) buf_handle;
- kern_sge32[i].length = ioc->sgl[i].iov_len;
+ if (instance->consistent_mask_64bit) {
+ kern_sge64[i].phys_addr = cpu_to_le64(buf_handle);
+ kern_sge64[i].length = cpu_to_le32(ioc->sgl[i].iov_len);
+ } else {
+ kern_sge32[i].phys_addr = cpu_to_le32(buf_handle);
+ kern_sge32[i].length = cpu_to_le32(ioc->sgl[i].iov_len);
+ }
/*
* We created a kernel buffer corresponding to the
@@ -4682,6 +8372,13 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
}
if (ioc->sense_len) {
+ /* make sure the pointer is part of the frame */
+ if (ioc->sense_off >
+ (sizeof(union megasas_frame) - sizeof(__le64))) {
+ error = -EINVAL;
+ goto out;
+ }
+
sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len,
&sense_handle, GFP_KERNEL);
if (!sense) {
@@ -4689,9 +8386,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
goto out;
}
- sense_ptr =
- (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off);
- *sense_ptr = sense_handle;
+ /* always store 64 bits regardless of addressing */
+ sense_ptr = (void *)cmd->frame + ioc->sense_off;
+ put_unaligned_le64(sense_handle, sense_ptr);
}
/*
@@ -4699,9 +8396,27 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
* cmd to the SCSI mid-layer
*/
cmd->sync_cmd = 1;
- megasas_issue_blocked_cmd(instance, cmd);
+
+ ret = megasas_issue_blocked_cmd(instance, cmd, 0);
+ switch (ret) {
+ case DCMD_INIT:
+ case DCMD_BUSY:
+ cmd->sync_cmd = 0;
+ dev_err(&instance->pdev->dev,
+ "return -EBUSY from %s %d cmd 0x%x opcode 0x%x cmd->cmd_status_drv 0x%x\n",
+ __func__, __LINE__, cmd->frame->hdr.cmd, opcode,
+ cmd->cmd_status_drv);
+ error = -EBUSY;
+ goto out;
+ }
+
cmd->sync_cmd = 0;
+ if (instance->unload == 1) {
+ dev_info(&instance->pdev->dev, "Driver unload is in progress "
+ "don't submit data to application\n");
+ goto out;
+ }
/*
* copy out the kernel buffers to user buffers
*/
@@ -4717,16 +8432,20 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
* copy out the sense
*/
if (ioc->sense_len) {
+ void __user *uptr;
/*
* sense_ptr points to the location that has the user
* sense buffer address
*/
- sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw +
- ioc->sense_off);
+ sense_ptr = (void *)ioc->frame.raw + ioc->sense_off;
+ if (in_compat_syscall())
+ uptr = compat_ptr(get_unaligned((compat_uptr_t *)
+ sense_ptr));
+ else
+ uptr = get_unaligned((void __user **)sense_ptr);
- if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),
- sense, ioc->sense_len)) {
- printk(KERN_ERR "megasas: Failed to copy out to user "
+ if (copy_to_user(uptr, sense, ioc->sense_len)) {
+ dev_err(&instance->pdev->dev, "Failed to copy out to user "
"sense data\n");
error = -EFAULT;
goto out;
@@ -4738,26 +8457,68 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
*/
if (copy_to_user(&user_ioc->frame.hdr.cmd_status,
&cmd->frame->hdr.cmd_status, sizeof(u8))) {
- printk(KERN_DEBUG "megasas: Error copying out cmd_status\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error copying out cmd_status\n");
error = -EFAULT;
}
- out:
+out:
if (sense) {
dma_free_coherent(&instance->pdev->dev, ioc->sense_len,
sense, sense_handle);
}
- for (i = 0; i < ioc->sge_count && kbuff_arr[i]; i++) {
- dma_free_coherent(&instance->pdev->dev,
- kern_sge32[i].length,
- kbuff_arr[i], kern_sge32[i].phys_addr);
+ for (i = 0; i < ioc->sge_count; i++) {
+ if (kbuff_arr[i]) {
+ if (instance->consistent_mask_64bit)
+ dma_free_coherent(&instance->pdev->dev,
+ le32_to_cpu(kern_sge64[i].length),
+ kbuff_arr[i],
+ le64_to_cpu(kern_sge64[i].phys_addr));
+ else
+ dma_free_coherent(&instance->pdev->dev,
+ le32_to_cpu(kern_sge32[i].length),
+ kbuff_arr[i],
+ le32_to_cpu(kern_sge32[i].phys_addr));
+ kbuff_arr[i] = NULL;
+ }
}
megasas_return_cmd(instance, cmd);
return error;
}
+static struct megasas_iocpacket *
+megasas_compat_iocpacket_get_user(void __user *arg)
+{
+ struct megasas_iocpacket *ioc;
+ struct compat_megasas_iocpacket __user *cioc = arg;
+ size_t size;
+ int err = -EFAULT;
+ int i;
+
+ ioc = kzalloc(sizeof(*ioc), GFP_KERNEL);
+ if (!ioc)
+ return ERR_PTR(-ENOMEM);
+ size = offsetof(struct megasas_iocpacket, frame) + sizeof(ioc->frame);
+ if (copy_from_user(ioc, arg, size))
+ goto out;
+
+ for (i = 0; i < MAX_IOCTL_SGE; i++) {
+ compat_uptr_t iov_base;
+
+ if (get_user(iov_base, &cioc->sgl[i].iov_base) ||
+ get_user(ioc->sgl[i].iov_len, &cioc->sgl[i].iov_len))
+ goto out;
+
+ ioc->sgl[i].iov_base = compat_ptr(iov_base);
+ }
+
+ return ioc;
+out:
+ kfree(ioc);
+ return ERR_PTR(err);
+}
+
static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
{
struct megasas_iocpacket __user *user_ioc =
@@ -4765,18 +8526,14 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
struct megasas_iocpacket *ioc;
struct megasas_instance *instance;
int error;
- int i;
- unsigned long flags;
- u32 wait_time = MEGASAS_RESET_WAIT_TIME;
- ioc = kmalloc(sizeof(*ioc), GFP_KERNEL);
- if (!ioc)
- return -ENOMEM;
+ if (in_compat_syscall())
+ ioc = megasas_compat_iocpacket_get_user(user_ioc);
+ else
+ ioc = memdup_user(user_ioc, sizeof(struct megasas_iocpacket));
- if (copy_from_user(ioc, user_ioc, sizeof(*ioc))) {
- error = -EFAULT;
- goto out_kfree_ioc;
- }
+ if (IS_ERR(ioc))
+ return PTR_ERR(ioc);
instance = megasas_lookup_instance(ioc->host_no);
if (!instance) {
@@ -4784,8 +8541,14 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
goto out_kfree_ioc;
}
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
- printk(KERN_ERR "Controller in crit error\n");
+ /* Block ioctls in VF mode */
+ if (instance->requestorId && !allow_vf_ioctls) {
+ error = -ENODEV;
+ goto out_kfree_ioc;
+ }
+
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_err(&instance->pdev->dev, "Controller in crit error\n");
error = -ENODEV;
goto out_kfree_ioc;
}
@@ -4795,46 +8558,21 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
goto out_kfree_ioc;
}
- /*
- * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds
- */
if (down_interruptible(&instance->ioctl_sem)) {
error = -ERESTARTSYS;
goto out_kfree_ioc;
}
- for (i = 0; i < wait_time; i++) {
-
- spin_lock_irqsave(&instance->hba_lock, flags);
- if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL) {
- spin_unlock_irqrestore(&instance->hba_lock, flags);
- break;
- }
- spin_unlock_irqrestore(&instance->hba_lock, flags);
-
- if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
- printk(KERN_NOTICE "megasas: waiting"
- "for controller reset to finish\n");
- }
-
- msleep(1000);
- }
-
- spin_lock_irqsave(&instance->hba_lock, flags);
- if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) {
- spin_unlock_irqrestore(&instance->hba_lock, flags);
-
- printk(KERN_ERR "megaraid_sas: timed out while"
- "waiting for HBA to recover\n");
+ if (megasas_wait_for_adapter_operational(instance)) {
error = -ENODEV;
- goto out_kfree_ioc;
+ goto out_up;
}
- spin_unlock_irqrestore(&instance->hba_lock, flags);
error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc);
+out_up:
up(&instance->ioctl_sem);
- out_kfree_ioc:
+out_kfree_ioc:
kfree(ioc);
return error;
}
@@ -4844,9 +8582,6 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
struct megasas_instance *instance;
struct megasas_aen aen;
int error;
- int i;
- unsigned long flags;
- u32 wait_time = MEGASAS_RESET_WAIT_TIME;
if (file->private_data != file) {
printk(KERN_DEBUG "megasas: fasync_helper was not "
@@ -4862,7 +8597,7 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
if (!instance)
return -ENODEV;
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
return -ENODEV;
}
@@ -4870,43 +8605,21 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
return -ENODEV;
}
- for (i = 0; i < wait_time; i++) {
-
- spin_lock_irqsave(&instance->hba_lock, flags);
- if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL) {
- spin_unlock_irqrestore(&instance->hba_lock,
- flags);
- break;
- }
-
- spin_unlock_irqrestore(&instance->hba_lock, flags);
-
- if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
- printk(KERN_NOTICE "megasas: waiting for"
- "controller reset to finish\n");
- }
-
- msleep(1000);
- }
-
- spin_lock_irqsave(&instance->hba_lock, flags);
- if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) {
- spin_unlock_irqrestore(&instance->hba_lock, flags);
- printk(KERN_ERR "megaraid_sas: timed out while waiting"
- "for HBA to recover.\n");
+ if (megasas_wait_for_adapter_operational(instance))
return -ENODEV;
- }
- spin_unlock_irqrestore(&instance->hba_lock, flags);
- mutex_lock(&instance->aen_mutex);
+ mutex_lock(&instance->reset_mutex);
error = megasas_register_aen(instance, aen.seq_num,
aen.class_locale_word);
- mutex_unlock(&instance->aen_mutex);
+ mutex_unlock(&instance->reset_mutex);
return error;
}
/**
* megasas_mgmt_ioctl - char node ioctl entry point
+ * @file: char device file pointer
+ * @cmd: ioctl command
+ * @arg: ioctl command arguments address
*/
static long
megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -4923,67 +8636,13 @@ megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
#ifdef CONFIG_COMPAT
-static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg)
-{
- struct compat_megasas_iocpacket __user *cioc =
- (struct compat_megasas_iocpacket __user *)arg;
- struct megasas_iocpacket __user *ioc =
- compat_alloc_user_space(sizeof(struct megasas_iocpacket));
- int i;
- int error = 0;
- compat_uptr_t ptr;
-
- if (clear_user(ioc, sizeof(*ioc)))
- return -EFAULT;
-
- if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) ||
- copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) ||
- copy_in_user(&ioc->sense_off, &cioc->sense_off, sizeof(u32)) ||
- copy_in_user(&ioc->sense_len, &cioc->sense_len, sizeof(u32)) ||
- copy_in_user(ioc->frame.raw, cioc->frame.raw, 128) ||
- copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32)))
- return -EFAULT;
-
- /*
- * The sense_ptr is used in megasas_mgmt_fw_ioctl only when
- * sense_len is not null, so prepare the 64bit value under
- * the same condition.
- */
- if (ioc->sense_len) {
- void __user **sense_ioc_ptr =
- (void __user **)(ioc->frame.raw + ioc->sense_off);
- compat_uptr_t *sense_cioc_ptr =
- (compat_uptr_t *)(cioc->frame.raw + cioc->sense_off);
- if (get_user(ptr, sense_cioc_ptr) ||
- put_user(compat_ptr(ptr), sense_ioc_ptr))
- return -EFAULT;
- }
-
- for (i = 0; i < MAX_IOCTL_SGE; i++) {
- if (get_user(ptr, &cioc->sgl[i].iov_base) ||
- put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) ||
- copy_in_user(&ioc->sgl[i].iov_len,
- &cioc->sgl[i].iov_len, sizeof(compat_size_t)))
- return -EFAULT;
- }
-
- error = megasas_mgmt_ioctl_fw(file, (unsigned long)ioc);
-
- if (copy_in_user(&cioc->frame.hdr.cmd_status,
- &ioc->frame.hdr.cmd_status, sizeof(u8))) {
- printk(KERN_DEBUG "megasas: error copy_in_user cmd_status\n");
- return -EFAULT;
- }
- return error;
-}
-
static long
megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case MEGASAS_IOC_FIRMWARE32:
- return megasas_mgmt_compat_ioctl_fw(file, arg);
+ return megasas_mgmt_ioctl_fw(file, arg);
case MEGASAS_IOC_GET_AEN:
return megasas_mgmt_ioctl_aen(file, arg);
}
@@ -5007,6 +8666,8 @@ static const struct file_operations megasas_mgmt_fops = {
.llseek = noop_llseek,
};
+static SIMPLE_DEV_PM_OPS(megasas_pm_ops, megasas_suspend, megasas_resume);
+
/*
* PCI hotplug support registration structure
*/
@@ -5015,359 +8676,324 @@ static struct pci_driver megasas_pci_driver = {
.name = "megaraid_sas",
.id_table = megasas_pci_table,
.probe = megasas_probe_one,
- .remove = __devexit_p(megasas_detach_one),
- .suspend = megasas_suspend,
- .resume = megasas_resume,
+ .remove = megasas_detach_one,
+ .driver.pm = &megasas_pm_ops,
.shutdown = megasas_shutdown,
};
/*
* Sysfs driver attributes
*/
-static ssize_t megasas_sysfs_show_version(struct device_driver *dd, char *buf)
+static ssize_t version_show(struct device_driver *dd, char *buf)
{
return snprintf(buf, strlen(MEGASAS_VERSION) + 2, "%s\n",
MEGASAS_VERSION);
}
+static DRIVER_ATTR_RO(version);
-static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL);
-
-static ssize_t
-megasas_sysfs_show_release_date(struct device_driver *dd, char *buf)
+static ssize_t release_date_show(struct device_driver *dd, char *buf)
{
return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n",
- MEGASAS_RELDATE);
+ MEGASAS_RELDATE);
}
+static DRIVER_ATTR_RO(release_date);
-static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
- NULL);
-
-static ssize_t
-megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf)
+static ssize_t support_poll_for_event_show(struct device_driver *dd, char *buf)
{
return sprintf(buf, "%u\n", support_poll_for_event);
}
+static DRIVER_ATTR_RO(support_poll_for_event);
-static DRIVER_ATTR(support_poll_for_event, S_IRUGO,
- megasas_sysfs_show_support_poll_for_event, NULL);
-
- static ssize_t
-megasas_sysfs_show_support_device_change(struct device_driver *dd, char *buf)
+static ssize_t support_device_change_show(struct device_driver *dd, char *buf)
{
return sprintf(buf, "%u\n", support_device_change);
}
+static DRIVER_ATTR_RO(support_device_change);
-static DRIVER_ATTR(support_device_change, S_IRUGO,
- megasas_sysfs_show_support_device_change, NULL);
-
-static ssize_t
-megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
+static ssize_t dbg_lvl_show(struct device_driver *dd, char *buf)
{
return sprintf(buf, "%u\n", megasas_dbg_lvl);
}
-static ssize_t
-megasas_sysfs_set_dbg_lvl(struct device_driver *dd, const char *buf, size_t count)
+static ssize_t dbg_lvl_store(struct device_driver *dd, const char *buf,
+ size_t count)
{
int retval = count;
- if(sscanf(buf,"%u",&megasas_dbg_lvl)<1){
+
+ if (sscanf(buf, "%u", &megasas_dbg_lvl) < 1) {
printk(KERN_ERR "megasas: could not set dbg_lvl\n");
retval = -EINVAL;
}
return retval;
}
-
-static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUSR, megasas_sysfs_show_dbg_lvl,
- megasas_sysfs_set_dbg_lvl);
+static DRIVER_ATTR_RW(dbg_lvl);
static ssize_t
-megasas_sysfs_show_poll_mode_io(struct device_driver *dd, char *buf)
+support_nvme_encapsulation_show(struct device_driver *dd, char *buf)
{
- return sprintf(buf, "%u\n", poll_mode_io);
+ return sprintf(buf, "%u\n", support_nvme_encapsulation);
}
+static DRIVER_ATTR_RO(support_nvme_encapsulation);
+
static ssize_t
-megasas_sysfs_set_poll_mode_io(struct device_driver *dd,
- const char *buf, size_t count)
+support_pci_lane_margining_show(struct device_driver *dd, char *buf)
{
- int retval = count;
- int tmp = poll_mode_io;
- int i;
- struct megasas_instance *instance;
+ return sprintf(buf, "%u\n", support_pci_lane_margining);
+}
- if (sscanf(buf, "%u", &poll_mode_io) < 1) {
- printk(KERN_ERR "megasas: could not set poll_mode_io\n");
- retval = -EINVAL;
+static DRIVER_ATTR_RO(support_pci_lane_margining);
+
+static inline void megasas_remove_scsi_device(struct scsi_device *sdev)
+{
+ sdev_printk(KERN_INFO, sdev, "SCSI device is removed\n");
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+}
+
+/**
+ * megasas_update_device_list - Update the PD and LD device list from FW
+ * after an AEN event notification
+ * @instance: Adapter soft state
+ * @event_type: Indicates type of event (PD or LD event)
+ *
+ * @return: Success or failure
+ *
+ * Issue DCMDs to Firmware to update the internal device list in driver.
+ * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination
+ * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list.
+ */
+static
+int megasas_update_device_list(struct megasas_instance *instance,
+ int event_type)
+{
+ int dcmd_ret;
+
+ if (instance->enable_fw_dev_list) {
+ return megasas_host_device_list_query(instance, false);
+ } else {
+ if (event_type & SCAN_PD_CHANNEL) {
+ dcmd_ret = megasas_get_pd_list(instance);
+ if (dcmd_ret != DCMD_SUCCESS)
+ return dcmd_ret;
+ }
+
+ if (event_type & SCAN_VD_CHANNEL) {
+ if (!instance->requestorId ||
+ megasas_get_ld_vf_affiliation(instance, 0)) {
+ return megasas_ld_list_query(instance,
+ MR_LD_QUERY_TYPE_EXPOSED_TO_HOST);
+ }
+ }
}
+ return DCMD_SUCCESS;
+}
- /*
- * Check if poll_mode_io is already set or is same as previous value
- */
- if ((tmp && poll_mode_io) || (tmp == poll_mode_io))
- goto out;
+/**
+ * megasas_add_remove_devices - Add/remove devices to SCSI mid-layer
+ * after an AEN event notification
+ * @instance: Adapter soft state
+ * @scan_type: Indicates type of devices (PD/LD) to add
+ * @return void
+ */
+static
+void megasas_add_remove_devices(struct megasas_instance *instance,
+ int scan_type)
+{
+ int i, j;
+ u16 pd_index = 0;
+ u16 ld_index = 0;
+ u16 channel = 0, id = 0;
+ struct Scsi_Host *host;
+ struct scsi_device *sdev1;
+ struct MR_HOST_DEVICE_LIST *targetid_list = NULL;
+ struct MR_HOST_DEVICE_LIST_ENTRY *targetid_entry = NULL;
- if (poll_mode_io) {
- /*
- * Start timers for all adapters
- */
- for (i = 0; i < megasas_mgmt_info.max_index; i++) {
- instance = megasas_mgmt_info.instance[i];
- if (instance) {
- megasas_start_timer(instance,
- &instance->io_completion_timer,
- megasas_io_completion_timer,
- MEGASAS_COMPLETION_TIMER_INTERVAL);
+ host = instance->host;
+
+ if (instance->enable_fw_dev_list) {
+ targetid_list = instance->host_device_list_buf;
+ for (i = 0; i < targetid_list->count; i++) {
+ targetid_entry = &targetid_list->host_device_list[i];
+ if (targetid_entry->flags.u.bits.is_sys_pd) {
+ channel = le16_to_cpu(targetid_entry->target_id) /
+ MEGASAS_MAX_DEV_PER_CHANNEL;
+ id = le16_to_cpu(targetid_entry->target_id) %
+ MEGASAS_MAX_DEV_PER_CHANNEL;
+ } else {
+ channel = MEGASAS_MAX_PD_CHANNELS +
+ (le16_to_cpu(targetid_entry->target_id) /
+ MEGASAS_MAX_DEV_PER_CHANNEL);
+ id = le16_to_cpu(targetid_entry->target_id) %
+ MEGASAS_MAX_DEV_PER_CHANNEL;
+ }
+ sdev1 = scsi_device_lookup(host, channel, id, 0);
+ if (!sdev1) {
+ scsi_add_device(host, channel, id, 0);
+ } else {
+ scsi_device_put(sdev1);
}
}
- } else {
- /*
- * Delete timers for all adapters
- */
- for (i = 0; i < megasas_mgmt_info.max_index; i++) {
- instance = megasas_mgmt_info.instance[i];
- if (instance)
- del_timer_sync(&instance->io_completion_timer);
+ }
+
+ if (scan_type & SCAN_PD_CHANNEL) {
+ for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
+ for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+ pd_index = i * MEGASAS_MAX_DEV_PER_CHANNEL + j;
+ sdev1 = scsi_device_lookup(host, i, j, 0);
+ if (instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) {
+ if (!sdev1)
+ scsi_add_device(host, i, j, 0);
+ else
+ scsi_device_put(sdev1);
+ } else {
+ if (sdev1)
+ megasas_remove_scsi_device(sdev1);
+ }
+ }
+ }
+ }
+
+ if (scan_type & SCAN_VD_CHANNEL) {
+ for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+ for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+ ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+ sdev1 = scsi_device_lookup(host,
+ MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+ if (instance->ld_ids[ld_index] != 0xff) {
+ if (!sdev1)
+ scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+ else
+ scsi_device_put(sdev1);
+ } else {
+ if (sdev1)
+ megasas_remove_scsi_device(sdev1);
+ }
+ }
}
}
-out:
- return retval;
}
static void
megasas_aen_polling(struct work_struct *work)
{
struct megasas_aen_event *ev =
- container_of(work, struct megasas_aen_event, hotplug_work);
+ container_of(work, struct megasas_aen_event, hotplug_work.work);
struct megasas_instance *instance = ev->instance;
union megasas_evt_class_locale class_locale;
- struct Scsi_Host *host;
- struct scsi_device *sdev1;
- u16 pd_index = 0;
- u16 ld_index = 0;
- int i, j, doscan = 0;
+ int event_type = 0;
u32 seq_num;
+ u16 ld_target_id;
int error;
+ u8 dcmd_ret = DCMD_SUCCESS;
+ struct scsi_device *sdev1;
if (!instance) {
printk(KERN_ERR "invalid instance!\n");
kfree(ev);
return;
}
- instance->ev = NULL;
- host = instance->host;
- if (instance->evt_detail) {
- switch (instance->evt_detail->code) {
- case MR_EVT_PD_INSERTED:
- if (megasas_get_pd_list(instance) == 0) {
- for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
- for (j = 0;
- j < MEGASAS_MAX_DEV_PER_CHANNEL;
- j++) {
+ /* Don't run the event workqueue thread if OCR is running */
+ mutex_lock(&instance->reset_mutex);
- pd_index =
- (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
-
- sdev1 =
- scsi_device_lookup(host, i, j, 0);
+ instance->ev = NULL;
+ if (instance->evt_detail) {
+ megasas_decode_evt(instance);
- if (instance->pd_list[pd_index].driveState
- == MR_PD_STATE_SYSTEM) {
- if (!sdev1) {
- scsi_add_device(host, i, j, 0);
- }
-
- if (sdev1)
- scsi_device_put(sdev1);
- }
- }
- }
- }
- doscan = 0;
- break;
+ switch (le32_to_cpu(instance->evt_detail->code)) {
+ case MR_EVT_PD_INSERTED:
case MR_EVT_PD_REMOVED:
- if (megasas_get_pd_list(instance) == 0) {
- megasas_get_pd_list(instance);
- for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
- for (j = 0;
- j < MEGASAS_MAX_DEV_PER_CHANNEL;
- j++) {
-
- pd_index =
- (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
-
- sdev1 =
- scsi_device_lookup(host, i, j, 0);
-
- if (instance->pd_list[pd_index].driveState
- == MR_PD_STATE_SYSTEM) {
- if (sdev1) {
- scsi_device_put(sdev1);
- }
- } else {
- if (sdev1) {
- scsi_remove_device(sdev1);
- scsi_device_put(sdev1);
- }
- }
- }
- }
- }
- doscan = 0;
+ event_type = SCAN_PD_CHANNEL;
break;
case MR_EVT_LD_OFFLINE:
- case MR_EVT_CFG_CLEARED:
case MR_EVT_LD_DELETED:
- megasas_get_ld_list(instance);
- for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
- for (j = 0;
- j < MEGASAS_MAX_DEV_PER_CHANNEL;
- j++) {
-
- ld_index =
- (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
-
- sdev1 = scsi_device_lookup(host,
- i + MEGASAS_MAX_LD_CHANNELS,
- j,
- 0);
-
- if (instance->ld_ids[ld_index] != 0xff) {
- if (sdev1) {
- scsi_device_put(sdev1);
- }
- } else {
- if (sdev1) {
- scsi_remove_device(sdev1);
- scsi_device_put(sdev1);
- }
- }
- }
+ ld_target_id = instance->evt_detail->args.ld.target_id;
+ sdev1 = scsi_device_lookup(instance->host,
+ MEGASAS_MAX_PD_CHANNELS +
+ (ld_target_id / MEGASAS_MAX_DEV_PER_CHANNEL),
+ (ld_target_id % MEGASAS_MAX_DEV_PER_CHANNEL),
+ 0);
+ if (sdev1) {
+ mutex_unlock(&instance->reset_mutex);
+ megasas_remove_scsi_device(sdev1);
+ mutex_lock(&instance->reset_mutex);
}
- doscan = 0;
+
+ event_type = SCAN_VD_CHANNEL;
break;
case MR_EVT_LD_CREATED:
- megasas_get_ld_list(instance);
- for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
- for (j = 0;
- j < MEGASAS_MAX_DEV_PER_CHANNEL;
- j++) {
- ld_index =
- (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
-
- sdev1 = scsi_device_lookup(host,
- i+MEGASAS_MAX_LD_CHANNELS,
- j, 0);
-
- if (instance->ld_ids[ld_index] !=
- 0xff) {
- if (!sdev1) {
- scsi_add_device(host,
- i + 2,
- j, 0);
- }
- }
- if (sdev1) {
- scsi_device_put(sdev1);
- }
- }
- }
- doscan = 0;
+ event_type = SCAN_VD_CHANNEL;
break;
+
+ case MR_EVT_CFG_CLEARED:
case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
case MR_EVT_FOREIGN_CFG_IMPORTED:
case MR_EVT_LD_STATE_CHANGE:
- doscan = 1;
+ event_type = SCAN_PD_CHANNEL | SCAN_VD_CHANNEL;
+ dev_info(&instance->pdev->dev, "scanning for scsi%d...\n",
+ instance->host->host_no);
+ break;
+
+ case MR_EVT_CTRL_PROP_CHANGED:
+ dcmd_ret = megasas_get_ctrl_info(instance);
+ if (dcmd_ret == DCMD_SUCCESS &&
+ instance->snapdump_wait_time) {
+ megasas_get_snapdump_properties(instance);
+ dev_info(&instance->pdev->dev,
+ "Snap dump wait time\t: %d\n",
+ instance->snapdump_wait_time);
+ }
break;
default:
- doscan = 0;
+ event_type = 0;
break;
}
} else {
- printk(KERN_ERR "invalid evt_detail!\n");
+ dev_err(&instance->pdev->dev, "invalid evt_detail!\n");
+ mutex_unlock(&instance->reset_mutex);
kfree(ev);
return;
}
- if (doscan) {
- printk(KERN_INFO "scanning ...\n");
- megasas_get_pd_list(instance);
- for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
- for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
- pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j;
- sdev1 = scsi_device_lookup(host, i, j, 0);
- if (instance->pd_list[pd_index].driveState ==
- MR_PD_STATE_SYSTEM) {
- if (!sdev1) {
- scsi_add_device(host, i, j, 0);
- }
- if (sdev1)
- scsi_device_put(sdev1);
- } else {
- if (sdev1) {
- scsi_remove_device(sdev1);
- scsi_device_put(sdev1);
- }
- }
- }
- }
-
- megasas_get_ld_list(instance);
- for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
- for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
- ld_index =
- (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+ if (event_type)
+ dcmd_ret = megasas_update_device_list(instance, event_type);
- sdev1 = scsi_device_lookup(host,
- i+MEGASAS_MAX_LD_CHANNELS, j, 0);
- if (instance->ld_ids[ld_index] != 0xff) {
- if (!sdev1) {
- scsi_add_device(host,
- i+2,
- j, 0);
- } else {
- scsi_device_put(sdev1);
- }
- } else {
- if (sdev1) {
- scsi_remove_device(sdev1);
- scsi_device_put(sdev1);
- }
- }
- }
- }
- }
+ mutex_unlock(&instance->reset_mutex);
- if ( instance->aen_cmd != NULL ) {
- kfree(ev);
- return ;
- }
+ if (event_type && dcmd_ret == DCMD_SUCCESS)
+ megasas_add_remove_devices(instance, event_type);
- seq_num = instance->evt_detail->seq_num + 1;
+ if (dcmd_ret == DCMD_SUCCESS)
+ seq_num = le32_to_cpu(instance->evt_detail->seq_num) + 1;
+ else
+ seq_num = instance->last_seq_num;
/* Register AEN with FW for latest sequence number plus 1 */
class_locale.members.reserved = 0;
class_locale.members.locale = MR_EVT_LOCALE_ALL;
class_locale.members.class = MR_EVT_CLASS_DEBUG;
- mutex_lock(&instance->aen_mutex);
+
+ if (instance->aen_cmd != NULL) {
+ kfree(ev);
+ return;
+ }
+
+ mutex_lock(&instance->reset_mutex);
error = megasas_register_aen(instance, seq_num,
class_locale.word);
- mutex_unlock(&instance->aen_mutex);
-
if (error)
- printk(KERN_ERR "register aen failed error %x\n", error);
+ dev_err(&instance->pdev->dev,
+ "register aen failed error %x\n", error);
+ mutex_unlock(&instance->reset_mutex);
kfree(ev);
}
-
-static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUSR,
- megasas_sysfs_show_poll_mode_io,
- megasas_sysfs_set_poll_mode_io);
-
/**
* megasas_init - Driver load entry point
*/
@@ -5376,13 +9002,26 @@ static int __init megasas_init(void)
int rval;
/*
+ * Booted in kdump kernel, minimize memory footprints by
+ * disabling few features
+ */
+ if (reset_devices) {
+ msix_vectors = 1;
+ rdpq_enable = 0;
+ dual_qdepth_disable = 1;
+ poll_queues = 0;
+ }
+
+ /*
* Announce driver version and other information
*/
- printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
- MEGASAS_EXT_VERSION);
+ pr_info("megasas: %s\n", MEGASAS_VERSION);
+ megasas_dbg_lvl = 0;
support_poll_for_event = 2;
support_device_change = 1;
+ support_nvme_encapsulation = true;
+ support_pci_lane_margining = true;
memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info));
@@ -5398,20 +9037,29 @@ static int __init megasas_init(void)
megasas_mgmt_majorno = rval;
+ megasas_init_debugfs();
+
/*
* Register ourselves as PCI hotplug module
*/
rval = pci_register_driver(&megasas_pci_driver);
if (rval) {
- printk(KERN_DEBUG "megasas: PCI hotplug regisration failed \n");
+ printk(KERN_DEBUG "megasas: PCI hotplug registration failed \n");
goto err_pcidrv;
}
+ if ((event_log_level < MFI_EVT_CLASS_DEBUG) ||
+ (event_log_level > MFI_EVT_CLASS_DEAD)) {
+ pr_warn("megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n");
+ event_log_level = MFI_EVT_CLASS_CRITICAL;
+ }
+
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_version);
if (rval)
goto err_dcf_attr_ver;
+
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_release_date);
if (rval)
@@ -5427,37 +9075,45 @@ static int __init megasas_init(void)
if (rval)
goto err_dcf_dbg_lvl;
rval = driver_create_file(&megasas_pci_driver.driver,
- &driver_attr_poll_mode_io);
+ &driver_attr_support_device_change);
if (rval)
- goto err_dcf_poll_mode_io;
+ goto err_dcf_support_device_change;
rval = driver_create_file(&megasas_pci_driver.driver,
- &driver_attr_support_device_change);
+ &driver_attr_support_nvme_encapsulation);
if (rval)
- goto err_dcf_support_device_change;
+ goto err_dcf_support_nvme_encapsulation;
+
+ rval = driver_create_file(&megasas_pci_driver.driver,
+ &driver_attr_support_pci_lane_margining);
+ if (rval)
+ goto err_dcf_support_pci_lane_margining;
return rval;
-err_dcf_support_device_change:
+err_dcf_support_pci_lane_margining:
driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_poll_mode_io);
+ &driver_attr_support_nvme_encapsulation);
-err_dcf_poll_mode_io:
+err_dcf_support_nvme_encapsulation:
+ driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_support_device_change);
+
+err_dcf_support_device_change:
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_dbg_lvl);
err_dcf_dbg_lvl:
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_support_poll_for_event);
-
err_dcf_support_poll_for_event:
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_release_date);
-
err_dcf_rel_date:
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
err_dcf_attr_ver:
pci_unregister_driver(&megasas_pci_driver);
err_pcidrv:
+ megasas_exit_debugfs();
unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl");
return rval;
}
@@ -5468,8 +9124,6 @@ err_pcidrv:
static void __exit megasas_exit(void)
{
driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_poll_mode_io);
- driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_dbg_lvl);
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_support_poll_for_event);
@@ -5478,8 +9132,13 @@ static void __exit megasas_exit(void)
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_release_date);
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
+ driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_support_nvme_encapsulation);
+ driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_support_pci_lane_margining);
pci_unregister_driver(&megasas_pci_driver);
+ megasas_exit_debugfs();
unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl");
}
diff --git a/drivers/scsi/megaraid/megaraid_sas_debugfs.c b/drivers/scsi/megaraid/megaraid_sas_debugfs.c
new file mode 100644
index 000000000000..c69760775efa
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_sas_debugfs.c
@@ -0,0 +1,179 @@
+/*
+ * Linux MegaRAID driver for SAS based RAID controllers
+ *
+ * Copyright (c) 2003-2018 LSI Corporation.
+ * Copyright (c) 2003-2018 Avago Technologies.
+ * Copyright (c) 2003-2018 Broadcom Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Broadcom Inc.
+ * Kashyap Desai <kashyap.desai@broadcom.com>
+ * Sumit Saxena <sumit.saxena@broadcom.com>
+ * Shivasharan S <shivasharan.srikanteshwara@broadcom.com>
+ *
+ * Send feedback to: megaraidlinux.pdl@broadcom.com
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/compat.h>
+#include <linux/irq_poll.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "megaraid_sas_fusion.h"
+#include "megaraid_sas.h"
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+struct dentry *megasas_debugfs_root;
+
+static ssize_t
+megasas_debugfs_read(struct file *filp, char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct megasas_debugfs_buffer *debug = filp->private_data;
+
+ if (!debug || !debug->buf)
+ return 0;
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, debug->buf, debug->len);
+}
+
+static int
+megasas_debugfs_raidmap_open(struct inode *inode, struct file *file)
+{
+ struct megasas_instance *instance = inode->i_private;
+ struct megasas_debugfs_buffer *debug;
+ struct fusion_context *fusion;
+
+ fusion = instance->ctrl_context;
+
+ debug = kzalloc(sizeof(struct megasas_debugfs_buffer), GFP_KERNEL);
+ if (!debug)
+ return -ENOMEM;
+
+ debug->buf = (void *)fusion->ld_drv_map[(instance->map_id & 1)];
+ debug->len = fusion->drv_map_sz;
+ file->private_data = debug;
+
+ return 0;
+}
+
+static int
+megasas_debugfs_release(struct inode *inode, struct file *file)
+{
+ struct megasas_debug_buffer *debug = file->private_data;
+
+ if (!debug)
+ return 0;
+
+ file->private_data = NULL;
+ kfree(debug);
+ return 0;
+}
+
+static const struct file_operations megasas_debugfs_raidmap_fops = {
+ .owner = THIS_MODULE,
+ .open = megasas_debugfs_raidmap_open,
+ .read = megasas_debugfs_read,
+ .release = megasas_debugfs_release,
+};
+
+/*
+ * megasas_init_debugfs : Create debugfs root for megaraid_sas driver
+ */
+void megasas_init_debugfs(void)
+{
+ megasas_debugfs_root = debugfs_create_dir("megaraid_sas", NULL);
+ if (!megasas_debugfs_root)
+ pr_info("Cannot create debugfs root\n");
+}
+
+/*
+ * megasas_exit_debugfs : Remove debugfs root for megaraid_sas driver
+ */
+void megasas_exit_debugfs(void)
+{
+ debugfs_remove_recursive(megasas_debugfs_root);
+}
+
+/*
+ * megasas_setup_debugfs : Setup debugfs per Fusion adapter
+ * instance: Soft instance of adapter
+ */
+void
+megasas_setup_debugfs(struct megasas_instance *instance)
+{
+ char name[64];
+ struct fusion_context *fusion;
+
+ fusion = instance->ctrl_context;
+
+ if (fusion) {
+ snprintf(name, sizeof(name),
+ "scsi_host%d", instance->host->host_no);
+ if (!instance->debugfs_root) {
+ instance->debugfs_root =
+ debugfs_create_dir(name, megasas_debugfs_root);
+ if (!instance->debugfs_root) {
+ dev_err(&instance->pdev->dev,
+ "Cannot create per adapter debugfs directory\n");
+ return;
+ }
+ }
+
+ snprintf(name, sizeof(name), "raidmap_dump");
+ instance->raidmap_dump =
+ debugfs_create_file(name, S_IRUGO,
+ instance->debugfs_root, instance,
+ &megasas_debugfs_raidmap_fops);
+ if (!instance->raidmap_dump) {
+ dev_err(&instance->pdev->dev,
+ "Cannot create raidmap debugfs file\n");
+ debugfs_remove(instance->debugfs_root);
+ return;
+ }
+ }
+
+}
+
+/*
+ * megasas_destroy_debugfs : Destroy debugfs per Fusion adapter
+ * instance: Soft instance of adapter
+ */
+void megasas_destroy_debugfs(struct megasas_instance *instance)
+{
+ debugfs_remove_recursive(instance->debugfs_root);
+}
+
+#else
+void megasas_init_debugfs(void)
+{
+}
+void megasas_exit_debugfs(void)
+{
+}
+void megasas_setup_debugfs(struct megasas_instance *instance)
+{
+}
+void megasas_destroy_debugfs(struct megasas_instance *instance)
+{
+}
+#endif /*CONFIG_DEBUG_FS*/
diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c
index 8fe3a45794fc..b8b388a4e28f 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fp.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fp.c
@@ -1,33 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2011 LSI Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Copyright (c) 2009-2013 LSI Corporation
+ * Copyright (c) 2013-2016 Avago Technologies
+ * Copyright (c) 2016-2018 Broadcom Inc.
*
* FILE: megaraid_sas_fp.c
*
- * Authors: LSI Corporation
+ * Authors: Broadcom Inc.
* Sumant Patro
* Varad Talamacki
* Manoj Jose
+ * Kashyap Desai <kashyap.desai@broadcom.com>
+ * Sumit Saxena <sumit.saxena@broadcom.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
- *
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Send feedback to: megaraidlinux.pdl@broadcom.com
*/
#include <linux/kernel.h>
@@ -45,6 +33,7 @@
#include <linux/compat.h>
#include <linux/blkdev.h>
#include <linux/poll.h>
+#include <linux/irq_poll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -52,17 +41,31 @@
#include <scsi/scsi_host.h>
#include "megaraid_sas_fusion.h"
+#include "megaraid_sas.h"
#include <asm/div64.h>
+#define LB_PENDING_CMDS_DEFAULT 4
+static unsigned int lb_pending_cmds = LB_PENDING_CMDS_DEFAULT;
+module_param(lb_pending_cmds, int, 0444);
+MODULE_PARM_DESC(lb_pending_cmds, "Change raid-1 load balancing outstanding "
+ "threshold. Valid Values are 1-128. Default: 4");
+
+
#define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a)))
#define MR_LD_STATE_OPTIMAL 3
-#define FALSE 0
-#define TRUE 1
+
+#define SPAN_ROW_SIZE(map, ld, index_) (MR_LdSpanPtrGet(ld, index_, map)->spanRowSize)
+#define SPAN_ROW_DATA_SIZE(map_, ld, index_) (MR_LdSpanPtrGet(ld, index_, map)->spanRowDataSize)
+#define SPAN_INVALID 0xff
/* Prototypes */
-void
-mr_update_load_balance_params(struct MR_FW_RAID_MAP_ALL *map,
- struct LD_LOAD_BALANCE_INFO *lbInfo);
+static void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map,
+ PLD_SPAN_INFO ldSpanInfo);
+static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld,
+ u64 stripRow, u16 stripRef, struct IO_REQUEST_INFO *io_info,
+ struct RAID_CONTEXT *pRAID_Context, struct MR_DRV_RAID_MAP_ALL *map);
+static u64 get_row_from_strip(struct megasas_instance *instance, u32 ld,
+ u64 strip, struct MR_DRV_RAID_MAP_ALL *map);
u32 mega_mod64(u64 dividend, u32 divisor)
{
@@ -77,102 +80,298 @@ u32 mega_mod64(u64 dividend, u32 divisor)
}
/**
- * @param dividend : Dividend
- * @param divisor : Divisor
+ * mega_div64_32 - Do a 64-bit division
+ * @dividend: Dividend
+ * @divisor: Divisor
*
* @return quotient
**/
-u64 mega_div64_32(uint64_t dividend, uint32_t divisor)
+static u64 mega_div64_32(uint64_t dividend, uint32_t divisor)
{
- u32 remainder;
- u64 d;
+ u64 d = dividend;
if (!divisor)
printk(KERN_ERR "megasas : DIVISOR is zero in mod fn\n");
- d = dividend;
- remainder = do_div(d, divisor);
+ do_div(d, divisor);
return d;
}
-struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_FW_RAID_MAP_ALL *map)
+struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_DRV_RAID_MAP_ALL *map)
{
return &map->raidMap.ldSpanMap[ld].ldRaid;
}
static struct MR_SPAN_BLOCK_INFO *MR_LdSpanInfoGet(u32 ld,
- struct MR_FW_RAID_MAP_ALL
+ struct MR_DRV_RAID_MAP_ALL
*map)
{
return &map->raidMap.ldSpanMap[ld].spanBlock[0];
}
-static u8 MR_LdDataArmGet(u32 ld, u32 armIdx, struct MR_FW_RAID_MAP_ALL *map)
+static u8 MR_LdDataArmGet(u32 ld, u32 armIdx, struct MR_DRV_RAID_MAP_ALL *map)
{
return map->raidMap.ldSpanMap[ld].dataArmMap[armIdx];
}
-static u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_FW_RAID_MAP_ALL *map)
+u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_DRV_RAID_MAP_ALL *map)
{
- return map->raidMap.arMapInfo[ar].pd[arm];
+ return le16_to_cpu(map->raidMap.arMapInfo[ar].pd[arm]);
}
-static u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_FW_RAID_MAP_ALL *map)
+u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map)
{
- return map->raidMap.ldSpanMap[ld].spanBlock[span].span.arrayRef;
+ return le16_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].span.arrayRef);
}
-static u16 MR_PdDevHandleGet(u32 pd, struct MR_FW_RAID_MAP_ALL *map)
+__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map)
{
return map->raidMap.devHndlInfo[pd].curDevHdl;
}
-u16 MR_GetLDTgtId(u32 ld, struct MR_FW_RAID_MAP_ALL *map)
+static u8 MR_PdInterfaceTypeGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map)
+{
+ return map->raidMap.devHndlInfo[pd].interfaceType;
+}
+
+u16 MR_GetLDTgtId(u32 ld, struct MR_DRV_RAID_MAP_ALL *map)
{
- return map->raidMap.ldSpanMap[ld].ldRaid.targetId;
+ return le16_to_cpu(map->raidMap.ldSpanMap[ld].ldRaid.targetId);
}
-u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map)
+u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_DRV_RAID_MAP_ALL *map)
{
return map->raidMap.ldTgtIdToLd[ldTgtId];
}
static struct MR_LD_SPAN *MR_LdSpanPtrGet(u32 ld, u32 span,
- struct MR_FW_RAID_MAP_ALL *map)
+ struct MR_DRV_RAID_MAP_ALL *map)
{
return &map->raidMap.ldSpanMap[ld].spanBlock[span].span;
}
/*
+ * This function will Populate Driver Map using firmware raid map
+ */
+static int MR_PopulateDrvRaidMap(struct megasas_instance *instance, u64 map_id)
+{
+ struct fusion_context *fusion = instance->ctrl_context;
+ struct MR_FW_RAID_MAP_ALL *fw_map_old = NULL;
+ struct MR_FW_RAID_MAP *pFwRaidMap = NULL;
+ int i, j;
+ u16 ld_count;
+ struct MR_FW_RAID_MAP_DYNAMIC *fw_map_dyn;
+ struct MR_FW_RAID_MAP_EXT *fw_map_ext;
+ struct MR_RAID_MAP_DESC_TABLE *desc_table;
+
+
+ struct MR_DRV_RAID_MAP_ALL *drv_map =
+ fusion->ld_drv_map[(map_id & 1)];
+ struct MR_DRV_RAID_MAP *pDrvRaidMap = &drv_map->raidMap;
+ void *raid_map_data = NULL;
+
+ memset(drv_map, 0, fusion->drv_map_sz);
+ memset(pDrvRaidMap->ldTgtIdToLd,
+ 0xff, (sizeof(u16) * MAX_LOGICAL_DRIVES_DYN));
+
+ if (instance->max_raid_mapsize) {
+ fw_map_dyn = fusion->ld_map[(map_id & 1)];
+ desc_table =
+ (struct MR_RAID_MAP_DESC_TABLE *)((void *)fw_map_dyn + le32_to_cpu(fw_map_dyn->desc_table_offset));
+ if (desc_table != fw_map_dyn->raid_map_desc_table)
+ dev_dbg(&instance->pdev->dev, "offsets of desc table are not matching desc %p original %p\n",
+ desc_table, fw_map_dyn->raid_map_desc_table);
+
+ ld_count = (u16)le16_to_cpu(fw_map_dyn->ld_count);
+ pDrvRaidMap->ldCount = (__le16)cpu_to_le16(ld_count);
+ pDrvRaidMap->fpPdIoTimeoutSec =
+ fw_map_dyn->fp_pd_io_timeout_sec;
+ pDrvRaidMap->totalSize =
+ cpu_to_le32(sizeof(struct MR_DRV_RAID_MAP_ALL));
+ /* point to actual data starting point*/
+ raid_map_data = (void *)fw_map_dyn +
+ le32_to_cpu(fw_map_dyn->desc_table_offset) +
+ le32_to_cpu(fw_map_dyn->desc_table_size);
+
+ for (i = 0; i < le32_to_cpu(fw_map_dyn->desc_table_num_elements); ++i) {
+ switch (le32_to_cpu(desc_table->raid_map_desc_type)) {
+ case RAID_MAP_DESC_TYPE_DEVHDL_INFO:
+ fw_map_dyn->dev_hndl_info =
+ (struct MR_DEV_HANDLE_INFO *)(raid_map_data + le32_to_cpu(desc_table->raid_map_desc_offset));
+ memcpy(pDrvRaidMap->devHndlInfo,
+ fw_map_dyn->dev_hndl_info,
+ sizeof(struct MR_DEV_HANDLE_INFO) *
+ le32_to_cpu(desc_table->raid_map_desc_elements));
+ break;
+ case RAID_MAP_DESC_TYPE_TGTID_INFO:
+ fw_map_dyn->ld_tgt_id_to_ld =
+ (u16 *)(raid_map_data +
+ le32_to_cpu(desc_table->raid_map_desc_offset));
+ for (j = 0; j < le32_to_cpu(desc_table->raid_map_desc_elements); j++) {
+ pDrvRaidMap->ldTgtIdToLd[j] =
+ le16_to_cpu(fw_map_dyn->ld_tgt_id_to_ld[j]);
+ }
+ break;
+ case RAID_MAP_DESC_TYPE_ARRAY_INFO:
+ fw_map_dyn->ar_map_info =
+ (struct MR_ARRAY_INFO *)
+ (raid_map_data + le32_to_cpu(desc_table->raid_map_desc_offset));
+ memcpy(pDrvRaidMap->arMapInfo,
+ fw_map_dyn->ar_map_info,
+ sizeof(struct MR_ARRAY_INFO) *
+ le32_to_cpu(desc_table->raid_map_desc_elements));
+ break;
+ case RAID_MAP_DESC_TYPE_SPAN_INFO:
+ fw_map_dyn->ld_span_map =
+ (struct MR_LD_SPAN_MAP *)
+ (raid_map_data +
+ le32_to_cpu(desc_table->raid_map_desc_offset));
+ memcpy(pDrvRaidMap->ldSpanMap,
+ fw_map_dyn->ld_span_map,
+ sizeof(struct MR_LD_SPAN_MAP) *
+ le32_to_cpu(desc_table->raid_map_desc_elements));
+ break;
+ default:
+ dev_dbg(&instance->pdev->dev, "wrong number of desctableElements %d\n",
+ fw_map_dyn->desc_table_num_elements);
+ }
+ ++desc_table;
+ }
+
+ } else if (instance->supportmax256vd) {
+ fw_map_ext =
+ (struct MR_FW_RAID_MAP_EXT *)fusion->ld_map[(map_id & 1)];
+ ld_count = (u16)le16_to_cpu(fw_map_ext->ldCount);
+ if (ld_count > MAX_LOGICAL_DRIVES_EXT) {
+ dev_dbg(&instance->pdev->dev, "megaraid_sas: LD count exposed in RAID map in not valid\n");
+ return 1;
+ }
+
+ pDrvRaidMap->ldCount = (__le16)cpu_to_le16(ld_count);
+ pDrvRaidMap->fpPdIoTimeoutSec = fw_map_ext->fpPdIoTimeoutSec;
+ for (i = 0; i < (MAX_LOGICAL_DRIVES_EXT); i++)
+ pDrvRaidMap->ldTgtIdToLd[i] =
+ (u16)fw_map_ext->ldTgtIdToLd[i];
+ memcpy(pDrvRaidMap->ldSpanMap, fw_map_ext->ldSpanMap,
+ sizeof(struct MR_LD_SPAN_MAP) * ld_count);
+ memcpy(pDrvRaidMap->arMapInfo, fw_map_ext->arMapInfo,
+ sizeof(struct MR_ARRAY_INFO) * MAX_API_ARRAYS_EXT);
+ memcpy(pDrvRaidMap->devHndlInfo, fw_map_ext->devHndlInfo,
+ sizeof(struct MR_DEV_HANDLE_INFO) *
+ MAX_RAIDMAP_PHYSICAL_DEVICES);
+
+ /* New Raid map will not set totalSize, so keep expected value
+ * for legacy code in ValidateMapInfo
+ */
+ pDrvRaidMap->totalSize =
+ cpu_to_le32(sizeof(struct MR_FW_RAID_MAP_EXT));
+ } else {
+ fw_map_old = (struct MR_FW_RAID_MAP_ALL *)
+ fusion->ld_map[(map_id & 1)];
+ pFwRaidMap = &fw_map_old->raidMap;
+ ld_count = (u16)le32_to_cpu(pFwRaidMap->ldCount);
+ if (ld_count > MAX_LOGICAL_DRIVES) {
+ dev_dbg(&instance->pdev->dev,
+ "LD count exposed in RAID map in not valid\n");
+ return 1;
+ }
+
+ pDrvRaidMap->totalSize = pFwRaidMap->totalSize;
+ pDrvRaidMap->ldCount = (__le16)cpu_to_le16(ld_count);
+ pDrvRaidMap->fpPdIoTimeoutSec = pFwRaidMap->fpPdIoTimeoutSec;
+ for (i = 0; i < MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS; i++)
+ pDrvRaidMap->ldTgtIdToLd[i] =
+ (u8)pFwRaidMap->ldTgtIdToLd[i];
+ for (i = 0; i < ld_count; i++) {
+ pDrvRaidMap->ldSpanMap[i] = pFwRaidMap->ldSpanMap[i];
+ }
+ memcpy(pDrvRaidMap->arMapInfo, pFwRaidMap->arMapInfo,
+ sizeof(struct MR_ARRAY_INFO) * MAX_RAIDMAP_ARRAYS);
+ memcpy(pDrvRaidMap->devHndlInfo, pFwRaidMap->devHndlInfo,
+ sizeof(struct MR_DEV_HANDLE_INFO) *
+ MAX_RAIDMAP_PHYSICAL_DEVICES);
+ }
+
+ return 0;
+}
+
+/*
* This function will validate Map info data provided by FW
*/
-u8 MR_ValidateMapInfo(struct MR_FW_RAID_MAP_ALL *map,
- struct LD_LOAD_BALANCE_INFO *lbInfo)
+u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id)
{
- struct MR_FW_RAID_MAP *pFwRaidMap = &map->raidMap;
-
- if (pFwRaidMap->totalSize !=
- (sizeof(struct MR_FW_RAID_MAP) -sizeof(struct MR_LD_SPAN_MAP) +
- (sizeof(struct MR_LD_SPAN_MAP) *pFwRaidMap->ldCount))) {
- printk(KERN_ERR "megasas: map info structure size 0x%x is not matching with ld count\n",
- (unsigned int)((sizeof(struct MR_FW_RAID_MAP) -
- sizeof(struct MR_LD_SPAN_MAP)) +
- (sizeof(struct MR_LD_SPAN_MAP) *
- pFwRaidMap->ldCount)));
- printk(KERN_ERR "megasas: span map %x, pFwRaidMap->totalSize "
- ": %x\n", (unsigned int)sizeof(struct MR_LD_SPAN_MAP),
- pFwRaidMap->totalSize);
+ struct fusion_context *fusion;
+ struct MR_DRV_RAID_MAP_ALL *drv_map;
+ struct MR_DRV_RAID_MAP *pDrvRaidMap;
+ struct LD_LOAD_BALANCE_INFO *lbInfo;
+ PLD_SPAN_INFO ldSpanInfo;
+ struct MR_LD_RAID *raid;
+ u16 num_lds, i;
+ u16 ld;
+ u32 expected_size;
+
+ if (MR_PopulateDrvRaidMap(instance, map_id))
+ return 0;
+
+ fusion = instance->ctrl_context;
+ drv_map = fusion->ld_drv_map[(map_id & 1)];
+ pDrvRaidMap = &drv_map->raidMap;
+
+ lbInfo = fusion->load_balance_info;
+ ldSpanInfo = fusion->log_to_span;
+
+ if (instance->max_raid_mapsize)
+ expected_size = sizeof(struct MR_DRV_RAID_MAP_ALL);
+ else if (instance->supportmax256vd)
+ expected_size = sizeof(struct MR_FW_RAID_MAP_EXT);
+ else
+ expected_size = struct_size_t(struct MR_FW_RAID_MAP,
+ ldSpanMap,
+ le16_to_cpu(pDrvRaidMap->ldCount));
+
+ if (le32_to_cpu(pDrvRaidMap->totalSize) != expected_size) {
+ dev_dbg(&instance->pdev->dev, "megasas: map info structure size 0x%x",
+ le32_to_cpu(pDrvRaidMap->totalSize));
+ dev_dbg(&instance->pdev->dev, "is not matching expected size 0x%x\n",
+ (unsigned int)expected_size);
+ dev_err(&instance->pdev->dev, "megasas: span map %x, pDrvRaidMap->totalSize : %x\n",
+ (unsigned int)sizeof(struct MR_LD_SPAN_MAP),
+ le32_to_cpu(pDrvRaidMap->totalSize));
return 0;
}
- mr_update_load_balance_params(map, lbInfo);
+ if (instance->UnevenSpanSupport)
+ mr_update_span_set(drv_map, ldSpanInfo);
+
+ if (lbInfo)
+ mr_update_load_balance_params(drv_map, lbInfo);
+
+ num_lds = le16_to_cpu(drv_map->raidMap.ldCount);
+
+ memcpy(instance->ld_ids_prev,
+ instance->ld_ids_from_raidmap,
+ sizeof(instance->ld_ids_from_raidmap));
+ memset(instance->ld_ids_from_raidmap, 0xff, MEGASAS_MAX_LD_IDS);
+ /*Convert Raid capability values to CPU arch */
+ for (i = 0; (num_lds > 0) && (i < MAX_LOGICAL_DRIVES_EXT); i++) {
+ ld = MR_TargetIdToLdGet(i, drv_map);
+
+ /* For non existing VDs, iterate to next VD*/
+ if (ld >= MEGASAS_MAX_SUPPORTED_LD_IDS)
+ continue;
+
+ raid = MR_LdRaidGet(ld, drv_map);
+ le32_to_cpus((u32 *)&raid->capability);
+ instance->ld_ids_from_raidmap[i] = i;
+ num_lds--;
+ }
return 1;
}
-u32 MR_GetSpanBlock(u32 ld, u64 row, u64 *span_blk,
- struct MR_FW_RAID_MAP_ALL *map, int *div_error)
+static u32 MR_GetSpanBlock(u32 ld, u64 row, u64 *span_blk,
+ struct MR_DRV_RAID_MAP_ALL *map)
{
struct MR_SPAN_BLOCK_INFO *pSpanBlock = MR_LdSpanInfoGet(ld, map);
struct MR_QUAD_ELEMENT *quad;
@@ -181,32 +380,395 @@ u32 MR_GetSpanBlock(u32 ld, u64 row, u64 *span_blk,
for (span = 0; span < raid->spanDepth; span++, pSpanBlock++) {
- for (j = 0; j < pSpanBlock->block_span_info.noElements; j++) {
+ for (j = 0; j < le32_to_cpu(pSpanBlock->block_span_info.noElements); j++) {
quad = &pSpanBlock->block_span_info.quad[j];
- if (quad->diff == 0) {
- *div_error = 1;
- return span;
- }
- if (quad->logStart <= row && row <= quad->logEnd &&
- (mega_mod64(row-quad->logStart, quad->diff)) == 0) {
+ if (le32_to_cpu(quad->diff) == 0)
+ return SPAN_INVALID;
+ if (le64_to_cpu(quad->logStart) <= row && row <=
+ le64_to_cpu(quad->logEnd) && (mega_mod64(row - le64_to_cpu(quad->logStart),
+ le32_to_cpu(quad->diff))) == 0) {
if (span_blk != NULL) {
- u64 blk, debugBlk;
- blk =
- mega_div64_32(
- (row-quad->logStart),
- quad->diff);
- debugBlk = blk;
-
- blk = (blk + quad->offsetInSpan) <<
- raid->stripeShift;
+ u64 blk;
+ blk = mega_div64_32((row-le64_to_cpu(quad->logStart)), le32_to_cpu(quad->diff));
+
+ blk = (blk + le64_to_cpu(quad->offsetInSpan)) << raid->stripeShift;
*span_blk = blk;
}
return span;
}
}
}
- return span;
+ return SPAN_INVALID;
+}
+
+/*
+******************************************************************************
+*
+* This routine calculates the Span block for given row using spanset.
+*
+* Inputs :
+* instance - HBA instance
+* ld - Logical drive number
+* row - Row number
+* map - LD map
+*
+* Outputs :
+*
+* span - Span number
+* block - Absolute Block number in the physical disk
+* div_error - Devide error code.
+*/
+
+static u32 mr_spanset_get_span_block(struct megasas_instance *instance,
+ u32 ld, u64 row, u64 *span_blk, struct MR_DRV_RAID_MAP_ALL *map)
+{
+ struct fusion_context *fusion = instance->ctrl_context;
+ struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
+ LD_SPAN_SET *span_set;
+ struct MR_QUAD_ELEMENT *quad;
+ u32 span, info;
+ PLD_SPAN_INFO ldSpanInfo = fusion->log_to_span;
+
+ for (info = 0; info < MAX_QUAD_DEPTH; info++) {
+ span_set = &(ldSpanInfo[ld].span_set[info]);
+
+ if (span_set->span_row_data_width == 0)
+ break;
+
+ if (row > span_set->data_row_end)
+ continue;
+
+ for (span = 0; span < raid->spanDepth; span++)
+ if (le32_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].
+ block_span_info.noElements) >= info+1) {
+ quad = &map->raidMap.ldSpanMap[ld].
+ spanBlock[span].
+ block_span_info.quad[info];
+ if (le32_to_cpu(quad->diff) == 0)
+ return SPAN_INVALID;
+ if (le64_to_cpu(quad->logStart) <= row &&
+ row <= le64_to_cpu(quad->logEnd) &&
+ (mega_mod64(row - le64_to_cpu(quad->logStart),
+ le32_to_cpu(quad->diff))) == 0) {
+ if (span_blk != NULL) {
+ u64 blk;
+ blk = mega_div64_32
+ ((row - le64_to_cpu(quad->logStart)),
+ le32_to_cpu(quad->diff));
+ blk = (blk + le64_to_cpu(quad->offsetInSpan))
+ << raid->stripeShift;
+ *span_blk = blk;
+ }
+ return span;
+ }
+ }
+ }
+ return SPAN_INVALID;
+}
+
+/*
+******************************************************************************
+*
+* This routine calculates the row for given strip using spanset.
+*
+* Inputs :
+* instance - HBA instance
+* ld - Logical drive number
+* Strip - Strip
+* map - LD map
+*
+* Outputs :
+*
+* row - row associated with strip
+*/
+
+static u64 get_row_from_strip(struct megasas_instance *instance,
+ u32 ld, u64 strip, struct MR_DRV_RAID_MAP_ALL *map)
+{
+ struct fusion_context *fusion = instance->ctrl_context;
+ struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
+ LD_SPAN_SET *span_set;
+ PLD_SPAN_INFO ldSpanInfo = fusion->log_to_span;
+ u32 info, strip_offset, span, span_offset;
+ u64 span_set_Strip, span_set_Row, retval;
+
+ for (info = 0; info < MAX_QUAD_DEPTH; info++) {
+ span_set = &(ldSpanInfo[ld].span_set[info]);
+
+ if (span_set->span_row_data_width == 0)
+ break;
+ if (strip > span_set->data_strip_end)
+ continue;
+
+ span_set_Strip = strip - span_set->data_strip_start;
+ strip_offset = mega_mod64(span_set_Strip,
+ span_set->span_row_data_width);
+ span_set_Row = mega_div64_32(span_set_Strip,
+ span_set->span_row_data_width) * span_set->diff;
+ for (span = 0, span_offset = 0; span < raid->spanDepth; span++)
+ if (le32_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].
+ block_span_info.noElements) >= info+1) {
+ if (strip_offset >=
+ span_set->strip_offset[span])
+ span_offset++;
+ else
+ break;
+ }
+
+ retval = (span_set->data_row_start + span_set_Row +
+ (span_offset - 1));
+ return retval;
+ }
+ return -1LLU;
+}
+
+
+/*
+******************************************************************************
+*
+* This routine calculates the Start Strip for given row using spanset.
+*
+* Inputs :
+* instance - HBA instance
+* ld - Logical drive number
+* row - Row number
+* map - LD map
+*
+* Outputs :
+*
+* Strip - Start strip associated with row
+*/
+
+static u64 get_strip_from_row(struct megasas_instance *instance,
+ u32 ld, u64 row, struct MR_DRV_RAID_MAP_ALL *map)
+{
+ struct fusion_context *fusion = instance->ctrl_context;
+ struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
+ LD_SPAN_SET *span_set;
+ struct MR_QUAD_ELEMENT *quad;
+ PLD_SPAN_INFO ldSpanInfo = fusion->log_to_span;
+ u32 span, info;
+ u64 strip;
+
+ for (info = 0; info < MAX_QUAD_DEPTH; info++) {
+ span_set = &(ldSpanInfo[ld].span_set[info]);
+
+ if (span_set->span_row_data_width == 0)
+ break;
+ if (row > span_set->data_row_end)
+ continue;
+
+ for (span = 0; span < raid->spanDepth; span++)
+ if (le32_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].
+ block_span_info.noElements) >= info+1) {
+ quad = &map->raidMap.ldSpanMap[ld].
+ spanBlock[span].block_span_info.quad[info];
+ if (le64_to_cpu(quad->logStart) <= row &&
+ row <= le64_to_cpu(quad->logEnd) &&
+ mega_mod64((row - le64_to_cpu(quad->logStart)),
+ le32_to_cpu(quad->diff)) == 0) {
+ strip = mega_div64_32
+ (((row - span_set->data_row_start)
+ - le64_to_cpu(quad->logStart)),
+ le32_to_cpu(quad->diff));
+ strip *= span_set->span_row_data_width;
+ strip += span_set->data_strip_start;
+ strip += span_set->strip_offset[span];
+ return strip;
+ }
+ }
+ }
+ dev_err(&instance->pdev->dev, "get_strip_from_row"
+ "returns invalid strip for ld=%x, row=%lx\n",
+ ld, (long unsigned int)row);
+ return -1;
+}
+
+/*
+******************************************************************************
+*
+* This routine calculates the Physical Arm for given strip using spanset.
+*
+* Inputs :
+* instance - HBA instance
+* ld - Logical drive number
+* strip - Strip
+* map - LD map
+*
+* Outputs :
+*
+* Phys Arm - Phys Arm associated with strip
+*/
+
+static u32 get_arm_from_strip(struct megasas_instance *instance,
+ u32 ld, u64 strip, struct MR_DRV_RAID_MAP_ALL *map)
+{
+ struct fusion_context *fusion = instance->ctrl_context;
+ struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
+ LD_SPAN_SET *span_set;
+ PLD_SPAN_INFO ldSpanInfo = fusion->log_to_span;
+ u32 info, strip_offset, span, span_offset, retval;
+
+ for (info = 0 ; info < MAX_QUAD_DEPTH; info++) {
+ span_set = &(ldSpanInfo[ld].span_set[info]);
+
+ if (span_set->span_row_data_width == 0)
+ break;
+ if (strip > span_set->data_strip_end)
+ continue;
+
+ strip_offset = (uint)mega_mod64
+ ((strip - span_set->data_strip_start),
+ span_set->span_row_data_width);
+
+ for (span = 0, span_offset = 0; span < raid->spanDepth; span++)
+ if (le32_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].
+ block_span_info.noElements) >= info+1) {
+ if (strip_offset >=
+ span_set->strip_offset[span])
+ span_offset =
+ span_set->strip_offset[span];
+ else
+ break;
+ }
+
+ retval = (strip_offset - span_offset);
+ return retval;
+ }
+
+ dev_err(&instance->pdev->dev, "get_arm_from_strip"
+ "returns invalid arm for ld=%x strip=%lx\n",
+ ld, (long unsigned int)strip);
+
+ return -1;
+}
+
+/* This Function will return Phys arm */
+static u8 get_arm(struct megasas_instance *instance, u32 ld, u8 span, u64 stripe,
+ struct MR_DRV_RAID_MAP_ALL *map)
+{
+ struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
+ /* Need to check correct default value */
+ u32 arm = 0;
+
+ switch (raid->level) {
+ case 0:
+ case 5:
+ case 6:
+ arm = mega_mod64(stripe, SPAN_ROW_SIZE(map, ld, span));
+ break;
+ case 1:
+ /* start with logical arm */
+ arm = get_arm_from_strip(instance, ld, stripe, map);
+ if (arm != -1U)
+ arm *= 2;
+ break;
+ }
+
+ return arm;
+}
+
+
+/*
+******************************************************************************
+*
+* This routine calculates the arm, span and block for the specified stripe and
+* reference in stripe using spanset
+*
+* Inputs :
+*
+* ld - Logical drive number
+* stripRow - Stripe number
+* stripRef - Reference in stripe
+*
+* Outputs :
+*
+* span - Span number
+* block - Absolute Block number in the physical disk
+*/
+static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld,
+ u64 stripRow, u16 stripRef, struct IO_REQUEST_INFO *io_info,
+ struct RAID_CONTEXT *pRAID_Context,
+ struct MR_DRV_RAID_MAP_ALL *map)
+{
+ struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
+ u32 pd, arRef, r1_alt_pd;
+ u8 physArm, span;
+ u64 row;
+ u8 retval = true;
+ u64 *pdBlock = &io_info->pdBlock;
+ __le16 *pDevHandle = &io_info->devHandle;
+ u8 *pPdInterface = &io_info->pd_interface;
+ u32 logArm, rowMod, armQ, arm;
+
+ *pDevHandle = cpu_to_le16(MR_DEVHANDLE_INVALID);
+
+ /*Get row and span from io_info for Uneven Span IO.*/
+ row = io_info->start_row;
+ span = io_info->start_span;
+
+
+ if (raid->level == 6) {
+ logArm = get_arm_from_strip(instance, ld, stripRow, map);
+ if (logArm == -1U)
+ return false;
+ rowMod = mega_mod64(row, SPAN_ROW_SIZE(map, ld, span));
+ armQ = SPAN_ROW_SIZE(map, ld, span) - 1 - rowMod;
+ arm = armQ + 1 + logArm;
+ if (arm >= SPAN_ROW_SIZE(map, ld, span))
+ arm -= SPAN_ROW_SIZE(map, ld, span);
+ physArm = (u8)arm;
+ } else
+ /* Calculate the arm */
+ physArm = get_arm(instance, ld, span, stripRow, map);
+ if (physArm == 0xFF)
+ return false;
+
+ arRef = MR_LdSpanArrayGet(ld, span, map);
+ pd = MR_ArPdGet(arRef, physArm, map);
+
+ if (pd != MR_PD_INVALID) {
+ *pDevHandle = MR_PdDevHandleGet(pd, map);
+ *pPdInterface = MR_PdInterfaceTypeGet(pd, map);
+ /* get second pd also for raid 1/10 fast path writes*/
+ if ((instance->adapter_type >= VENTURA_SERIES) &&
+ (raid->level == 1) &&
+ !io_info->isRead) {
+ r1_alt_pd = MR_ArPdGet(arRef, physArm + 1, map);
+ if (r1_alt_pd != MR_PD_INVALID)
+ io_info->r1_alt_dev_handle =
+ MR_PdDevHandleGet(r1_alt_pd, map);
+ }
+ } else {
+ if ((raid->level >= 5) &&
+ ((instance->adapter_type == THUNDERBOLT_SERIES) ||
+ ((instance->adapter_type == INVADER_SERIES) &&
+ (raid->regTypeReqOnRead != REGION_TYPE_UNUSED))))
+ pRAID_Context->reg_lock_flags = REGION_TYPE_EXCLUSIVE;
+ else if (raid->level == 1) {
+ physArm = physArm + 1;
+ pd = MR_ArPdGet(arRef, physArm, map);
+ if (pd != MR_PD_INVALID) {
+ *pDevHandle = MR_PdDevHandleGet(pd, map);
+ *pPdInterface = MR_PdInterfaceTypeGet(pd, map);
+ }
+ }
+ }
+
+ *pdBlock += stripRef + le64_to_cpu(MR_LdSpanPtrGet(ld, span, map)->startBlk);
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ ((struct RAID_CONTEXT_G35 *)pRAID_Context)->span_arm =
+ (span << RAID_CTX_SPANARM_SPAN_SHIFT) | physArm;
+ io_info->span_arm =
+ (span << RAID_CTX_SPANARM_SPAN_SHIFT) | physArm;
+ } else {
+ pRAID_Context->span_arm =
+ (span << RAID_CTX_SPANARM_SPAN_SHIFT) | physArm;
+ io_info->span_arm = pRAID_Context->span_arm;
+ }
+ io_info->pd_after_lb = pd;
+ return retval;
}
/*
@@ -226,16 +788,21 @@ u32 MR_GetSpanBlock(u32 ld, u64 row, u64 *span_blk,
* span - Span number
* block - Absolute Block number in the physical disk
*/
-u8 MR_GetPhyParams(u32 ld, u64 stripRow, u16 stripRef, u64 *pdBlock,
- u16 *pDevHandle, struct RAID_CONTEXT *pRAID_Context,
- struct MR_FW_RAID_MAP_ALL *map)
+static u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow,
+ u16 stripRef, struct IO_REQUEST_INFO *io_info,
+ struct RAID_CONTEXT *pRAID_Context,
+ struct MR_DRV_RAID_MAP_ALL *map)
{
struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
- u32 pd, arRef;
+ u32 pd, arRef, r1_alt_pd;
u8 physArm, span;
u64 row;
- u8 retval = TRUE;
- int error_code = 0;
+ u8 retval = true;
+ u64 *pdBlock = &io_info->pdBlock;
+ __le16 *pDevHandle = &io_info->devHandle;
+ u8 *pPdInterface = &io_info->pd_interface;
+
+ *pDevHandle = cpu_to_le16(MR_DEVHANDLE_INVALID);
row = mega_div64_32(stripRow, raid->rowDataSize);
@@ -245,7 +812,7 @@ u8 MR_GetPhyParams(u32 ld, u64 stripRow, u16 stripRef, u64 *pdBlock,
u32 rowMod, armQ, arm;
if (raid->rowSize == 0)
- return FALSE;
+ return false;
/* get logical row mod */
rowMod = mega_mod64(row, raid->rowSize);
armQ = raid->rowSize-1-rowMod; /* index of Q drive */
@@ -255,7 +822,7 @@ u8 MR_GetPhyParams(u32 ld, u64 stripRow, u16 stripRef, u64 *pdBlock,
physArm = (u8)arm;
} else {
if (raid->modFactor == 0)
- return FALSE;
+ return false;
physArm = MR_LdDataArmGet(ld, mega_mod64(stripRow,
raid->modFactor),
map);
@@ -265,39 +832,133 @@ u8 MR_GetPhyParams(u32 ld, u64 stripRow, u16 stripRef, u64 *pdBlock,
span = 0;
*pdBlock = row << raid->stripeShift;
} else {
- span = (u8)MR_GetSpanBlock(ld, row, pdBlock, map, &error_code);
- if (error_code == 1)
- return FALSE;
+ span = (u8)MR_GetSpanBlock(ld, row, pdBlock, map);
+ if (span == SPAN_INVALID)
+ return false;
}
/* Get the array on which this span is present */
arRef = MR_LdSpanArrayGet(ld, span, map);
pd = MR_ArPdGet(arRef, physArm, map); /* Get the pd */
- if (pd != MR_PD_INVALID)
+ if (pd != MR_PD_INVALID) {
/* Get dev handle from Pd. */
*pDevHandle = MR_PdDevHandleGet(pd, map);
- else {
- *pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */
- if (raid->level >= 5)
- pRAID_Context->regLockFlags = REGION_TYPE_EXCLUSIVE;
+ *pPdInterface = MR_PdInterfaceTypeGet(pd, map);
+ /* get second pd also for raid 1/10 fast path writes*/
+ if ((instance->adapter_type >= VENTURA_SERIES) &&
+ (raid->level == 1) &&
+ !io_info->isRead) {
+ r1_alt_pd = MR_ArPdGet(arRef, physArm + 1, map);
+ if (r1_alt_pd != MR_PD_INVALID)
+ io_info->r1_alt_dev_handle =
+ MR_PdDevHandleGet(r1_alt_pd, map);
+ }
+ } else {
+ if ((raid->level >= 5) &&
+ ((instance->adapter_type == THUNDERBOLT_SERIES) ||
+ ((instance->adapter_type == INVADER_SERIES) &&
+ (raid->regTypeReqOnRead != REGION_TYPE_UNUSED))))
+ pRAID_Context->reg_lock_flags = REGION_TYPE_EXCLUSIVE;
else if (raid->level == 1) {
/* Get alternate Pd. */
- pd = MR_ArPdGet(arRef, physArm + 1, map);
- if (pd != MR_PD_INVALID)
+ physArm = physArm + 1;
+ pd = MR_ArPdGet(arRef, physArm, map);
+ if (pd != MR_PD_INVALID) {
/* Get dev handle from Pd */
*pDevHandle = MR_PdDevHandleGet(pd, map);
+ *pPdInterface = MR_PdInterfaceTypeGet(pd, map);
+ }
}
- retval = FALSE;
}
- *pdBlock += stripRef + MR_LdSpanPtrGet(ld, span, map)->startBlk;
- pRAID_Context->spanArm = (span << RAID_CTX_SPANARM_SPAN_SHIFT) |
- physArm;
+ *pdBlock += stripRef + le64_to_cpu(MR_LdSpanPtrGet(ld, span, map)->startBlk);
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ ((struct RAID_CONTEXT_G35 *)pRAID_Context)->span_arm =
+ (span << RAID_CTX_SPANARM_SPAN_SHIFT) | physArm;
+ io_info->span_arm =
+ (span << RAID_CTX_SPANARM_SPAN_SHIFT) | physArm;
+ } else {
+ pRAID_Context->span_arm =
+ (span << RAID_CTX_SPANARM_SPAN_SHIFT) | physArm;
+ io_info->span_arm = pRAID_Context->span_arm;
+ }
+ io_info->pd_after_lb = pd;
return retval;
}
/*
+ * mr_get_phy_params_r56_rmw - Calculate parameters for R56 CTIO write operation
+ * @instance: Adapter soft state
+ * @ld: LD index
+ * @stripNo: Strip Number
+ * @io_info: IO info structure pointer
+ * pRAID_Context: RAID context pointer
+ * map: RAID map pointer
+ *
+ * This routine calculates the logical arm, data Arm, row number and parity arm
+ * for R56 CTIO write operation.
+ */
+static void mr_get_phy_params_r56_rmw(struct megasas_instance *instance,
+ u32 ld, u64 stripNo,
+ struct IO_REQUEST_INFO *io_info,
+ struct RAID_CONTEXT_G35 *pRAID_Context,
+ struct MR_DRV_RAID_MAP_ALL *map)
+{
+ struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map);
+ u8 span, dataArms, arms, dataArm, logArm;
+ s8 rightmostParityArm, PParityArm;
+ u64 rowNum;
+ u64 *pdBlock = &io_info->pdBlock;
+
+ dataArms = raid->rowDataSize;
+ arms = raid->rowSize;
+
+ rowNum = mega_div64_32(stripNo, dataArms);
+ /* parity disk arm, first arm is 0 */
+ rightmostParityArm = (arms - 1) - mega_mod64(rowNum, arms);
+
+ /* logical arm within row */
+ logArm = mega_mod64(stripNo, dataArms);
+ /* physical arm for data */
+ dataArm = mega_mod64((rightmostParityArm + 1 + logArm), arms);
+
+ if (raid->spanDepth == 1) {
+ span = 0;
+ } else {
+ span = (u8)MR_GetSpanBlock(ld, rowNum, pdBlock, map);
+ if (span == SPAN_INVALID)
+ return;
+ }
+
+ if (raid->level == 6) {
+ /* P Parity arm, note this can go negative adjust if negative */
+ PParityArm = (arms - 2) - mega_mod64(rowNum, arms);
+
+ if (PParityArm < 0)
+ PParityArm += arms;
+
+ /* rightmostParityArm is P-Parity for RAID 5 and Q-Parity for RAID */
+ pRAID_Context->flow_specific.r56_arm_map = rightmostParityArm;
+ pRAID_Context->flow_specific.r56_arm_map |=
+ (u16)(PParityArm << RAID_CTX_R56_P_ARM_SHIFT);
+ } else {
+ pRAID_Context->flow_specific.r56_arm_map |=
+ (u16)(rightmostParityArm << RAID_CTX_R56_P_ARM_SHIFT);
+ }
+
+ pRAID_Context->reg_lock_row_lba = cpu_to_le64(rowNum);
+ pRAID_Context->flow_specific.r56_arm_map |=
+ (u16)(logArm << RAID_CTX_R56_LOG_ARM_SHIFT);
+ cpu_to_le16s(&pRAID_Context->flow_specific.r56_arm_map);
+ pRAID_Context->span_arm = (span << RAID_CTX_SPANARM_SPAN_SHIFT) | dataArm;
+ pRAID_Context->raid_flags = (MR_RAID_FLAGS_IO_SUB_TYPE_R56_DIV_OFFLOAD <<
+ MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT);
+
+ return;
+}
+
+/*
******************************************************************************
*
* MR_BuildRaidContext function
@@ -307,12 +968,14 @@ u8 MR_GetPhyParams(u32 ld, u64 stripRow, u16 stripRef, u64 *pdBlock,
* This function will return 0 if region lock was acquired OR return num strips
*/
u8
-MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
+MR_BuildRaidContext(struct megasas_instance *instance,
+ struct IO_REQUEST_INFO *io_info,
struct RAID_CONTEXT *pRAID_Context,
- struct MR_FW_RAID_MAP_ALL *map)
+ struct MR_DRV_RAID_MAP_ALL *map, u8 **raidLUN)
{
+ struct fusion_context *fusion;
struct MR_LD_RAID *raid;
- u32 ld, stripSize, stripe_mask;
+ u32 stripSize, stripe_mask;
u64 endLba, endStrip, endRow, start_row, start_strip;
u64 regStart;
u32 regSize;
@@ -322,17 +985,47 @@ MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
u32 numBlocks, ldTgtId;
u8 isRead;
u8 retval = 0;
+ u8 startlba_span = SPAN_INVALID;
+ u64 *pdBlock = &io_info->pdBlock;
+ u16 ld;
ldStartBlock = io_info->ldStartBlock;
numBlocks = io_info->numBlocks;
ldTgtId = io_info->ldTgtId;
isRead = io_info->isRead;
+ io_info->IoforUnevenSpan = 0;
+ io_info->start_span = SPAN_INVALID;
+ fusion = instance->ctrl_context;
ld = MR_TargetIdToLdGet(ldTgtId, map);
raid = MR_LdRaidGet(ld, map);
+ /*check read ahead bit*/
+ io_info->ra_capable = raid->capability.ra_capable;
+
+ /*
+ * if rowDataSize @RAID map and spanRowDataSize @SPAN INFO are zero
+ * return FALSE
+ */
+ if (raid->rowDataSize == 0) {
+ if (MR_LdSpanPtrGet(ld, 0, map)->spanRowDataSize == 0)
+ return false;
+ else if (instance->UnevenSpanSupport) {
+ io_info->IoforUnevenSpan = 1;
+ } else {
+ dev_info(&instance->pdev->dev,
+ "raid->rowDataSize is 0, but has SPAN[0]"
+ "rowDataSize = 0x%0x,"
+ "but there is _NO_ UnevenSpanSupport\n",
+ MR_LdSpanPtrGet(ld, 0, map)->spanRowDataSize);
+ return false;
+ }
+ }
stripSize = 1 << raid->stripeShift;
stripe_mask = stripSize-1;
+
+ io_info->data_arms = raid->rowDataSize;
+
/*
* calculate starting row and stripe, and number of strips and rows
*/
@@ -342,11 +1035,39 @@ MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
ref_in_end_stripe = (u16)(endLba & stripe_mask);
endStrip = endLba >> raid->stripeShift;
num_strips = (u8)(endStrip - start_strip + 1); /* End strip */
- if (raid->rowDataSize == 0)
- return FALSE;
- start_row = mega_div64_32(start_strip, raid->rowDataSize);
- endRow = mega_div64_32(endStrip, raid->rowDataSize);
- numRows = (u8)(endRow - start_row + 1);
+
+ if (io_info->IoforUnevenSpan) {
+ start_row = get_row_from_strip(instance, ld, start_strip, map);
+ endRow = get_row_from_strip(instance, ld, endStrip, map);
+ if (start_row == -1ULL || endRow == -1ULL) {
+ dev_info(&instance->pdev->dev, "return from %s %d."
+ "Send IO w/o region lock.\n",
+ __func__, __LINE__);
+ return false;
+ }
+
+ if (raid->spanDepth == 1) {
+ startlba_span = 0;
+ *pdBlock = start_row << raid->stripeShift;
+ } else
+ startlba_span = (u8)mr_spanset_get_span_block(instance,
+ ld, start_row, pdBlock, map);
+ if (startlba_span == SPAN_INVALID) {
+ dev_info(&instance->pdev->dev, "return from %s %d"
+ "for row 0x%llx,start strip %llx"
+ "endSrip %llx\n", __func__, __LINE__,
+ (unsigned long long)start_row,
+ (unsigned long long)start_strip,
+ (unsigned long long)endStrip);
+ return false;
+ }
+ io_info->start_span = startlba_span;
+ io_info->start_row = start_row;
+ } else {
+ start_row = mega_div64_32(start_strip, raid->rowDataSize);
+ endRow = mega_div64_32(endStrip, raid->rowDataSize);
+ }
+ numRows = (u8)(endRow - start_row + 1);
/*
* calculate region info.
@@ -357,15 +1078,22 @@ MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
/* assume this IO needs the full row - we'll adjust if not true */
regSize = stripSize;
- /* If IO spans more than 1 strip, fp is not possible
- FP is not possible for writes on non-0 raid levels
- FP is not possible if LD is not capable */
- if (num_strips > 1 || (!isRead && raid->level != 0) ||
- !raid->capability.fpCapable) {
- io_info->fpOkForIo = FALSE;
- } else {
- io_info->fpOkForIo = TRUE;
- }
+ io_info->do_fp_rlbypass = raid->capability.fpBypassRegionLock;
+
+ /* Check if we can send this I/O via FastPath */
+ if (raid->capability.fpCapable) {
+ if (isRead)
+ io_info->fpOkForIo = (raid->capability.fpReadCapable &&
+ ((num_strips == 1) ||
+ raid->capability.
+ fpReadAcrossStripe));
+ else
+ io_info->fpOkForIo = (raid->capability.fpWriteCapable &&
+ ((num_strips == 1) ||
+ raid->capability.
+ fpWriteAcrossStripe));
+ } else
+ io_info->fpOkForIo = false;
if (numRows == 1) {
/* single-strip IOs can always lock only the data needed */
@@ -374,142 +1102,324 @@ MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
regSize = numBlocks;
}
/* multi-strip IOs always need to full stripe locked */
- } else {
+ } else if (io_info->IoforUnevenSpan == 0) {
+ /*
+ * For Even span region lock optimization.
+ * If the start strip is the last in the start row
+ */
if (start_strip == (start_row + 1) * raid->rowDataSize - 1) {
- /* If the start strip is the last in the start row */
regStart += ref_in_start_stripe;
- regSize = stripSize - ref_in_start_stripe;
/* initialize count to sectors from startref to end
of strip */
+ regSize = stripSize - ref_in_start_stripe;
}
+ /* add complete rows in the middle of the transfer */
if (numRows > 2)
- /* Add complete rows in the middle of the transfer */
regSize += (numRows-2) << raid->stripeShift;
- /* if IO ends within first strip of last row */
+ /* if IO ends within first strip of last row*/
if (endStrip == endRow*raid->rowDataSize)
regSize += ref_in_end_stripe+1;
else
regSize += stripSize;
+ } else {
+ /*
+ * For Uneven span region lock optimization.
+ * If the start strip is the last in the start row
+ */
+ if (start_strip == (get_strip_from_row(instance, ld, start_row, map) +
+ SPAN_ROW_DATA_SIZE(map, ld, startlba_span) - 1)) {
+ regStart += ref_in_start_stripe;
+ /* initialize count to sectors from
+ * startRef to end of strip
+ */
+ regSize = stripSize - ref_in_start_stripe;
+ }
+ /* Add complete rows in the middle of the transfer*/
+
+ if (numRows > 2)
+ /* Add complete rows in the middle of the transfer*/
+ regSize += (numRows-2) << raid->stripeShift;
+
+ /* if IO ends within first strip of last row */
+ if (endStrip == get_strip_from_row(instance, ld, endRow, map))
+ regSize += ref_in_end_stripe + 1;
+ else
+ regSize += stripSize;
}
- pRAID_Context->timeoutValue = map->raidMap.fpPdIoTimeoutSec;
- pRAID_Context->regLockFlags = (isRead) ? REGION_TYPE_SHARED_READ :
- raid->regTypeReqOnWrite;
- pRAID_Context->VirtualDiskTgtId = raid->targetId;
- pRAID_Context->regLockRowLBA = regStart;
- pRAID_Context->regLockLength = regSize;
- pRAID_Context->configSeqNum = raid->seqNum;
+ pRAID_Context->timeout_value =
+ cpu_to_le16(raid->fpIoTimeoutForLd ?
+ raid->fpIoTimeoutForLd :
+ map->raidMap.fpPdIoTimeoutSec);
+ if (instance->adapter_type == INVADER_SERIES)
+ pRAID_Context->reg_lock_flags = (isRead) ?
+ raid->regTypeReqOnRead : raid->regTypeReqOnWrite;
+ else if (instance->adapter_type == THUNDERBOLT_SERIES)
+ pRAID_Context->reg_lock_flags = (isRead) ?
+ REGION_TYPE_SHARED_READ : raid->regTypeReqOnWrite;
+ pRAID_Context->virtual_disk_tgt_id = raid->targetId;
+ pRAID_Context->reg_lock_row_lba = cpu_to_le64(regStart);
+ pRAID_Context->reg_lock_length = cpu_to_le32(regSize);
+ pRAID_Context->config_seq_num = raid->seqNum;
+ /* save pointer to raid->LUN array */
+ *raidLUN = raid->LUN;
+
+ /* Aero R5/6 Division Offload for WRITE */
+ if (fusion->r56_div_offload && (raid->level >= 5) && !isRead) {
+ mr_get_phy_params_r56_rmw(instance, ld, start_strip, io_info,
+ (struct RAID_CONTEXT_G35 *)pRAID_Context,
+ map);
+ return true;
+ }
/*Get Phy Params only if FP capable, or else leave it to MR firmware
to do the calculation.*/
if (io_info->fpOkForIo) {
- retval = MR_GetPhyParams(ld, start_strip, ref_in_start_stripe,
- &io_info->pdBlock,
- &io_info->devHandle, pRAID_Context,
- map);
- /* If IO on an invalid Pd, then FP i snot possible */
- if (io_info->devHandle == MR_PD_INVALID)
- io_info->fpOkForIo = FALSE;
+ retval = io_info->IoforUnevenSpan ?
+ mr_spanset_get_phy_params(instance, ld,
+ start_strip, ref_in_start_stripe,
+ io_info, pRAID_Context, map) :
+ MR_GetPhyParams(instance, ld, start_strip,
+ ref_in_start_stripe, io_info,
+ pRAID_Context, map);
+ /* If IO on an invalid Pd, then FP is not possible.*/
+ if (io_info->devHandle == MR_DEVHANDLE_INVALID)
+ io_info->fpOkForIo = false;
return retval;
} else if (isRead) {
uint stripIdx;
for (stripIdx = 0; stripIdx < num_strips; stripIdx++) {
- if (!MR_GetPhyParams(ld, start_strip + stripIdx,
- ref_in_start_stripe,
- &io_info->pdBlock,
- &io_info->devHandle,
- pRAID_Context, map))
- return TRUE;
+ retval = io_info->IoforUnevenSpan ?
+ mr_spanset_get_phy_params(instance, ld,
+ start_strip + stripIdx,
+ ref_in_start_stripe, io_info,
+ pRAID_Context, map) :
+ MR_GetPhyParams(instance, ld,
+ start_strip + stripIdx, ref_in_start_stripe,
+ io_info, pRAID_Context, map);
+ if (!retval)
+ return true;
}
}
- return TRUE;
+ return true;
}
-void
-mr_update_load_balance_params(struct MR_FW_RAID_MAP_ALL *map,
- struct LD_LOAD_BALANCE_INFO *lbInfo)
+/*
+******************************************************************************
+*
+* This routine pepare spanset info from Valid Raid map and store it into
+* local copy of ldSpanInfo per instance data structure.
+*
+* Inputs :
+* map - LD map
+* ldSpanInfo - ldSpanInfo per HBA instance
+*
+*/
+void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map,
+ PLD_SPAN_INFO ldSpanInfo)
{
+ u8 span, count;
+ u32 element, span_row_width;
+ u64 span_row;
+ struct MR_LD_RAID *raid;
+ LD_SPAN_SET *span_set, *span_set_prev;
+ struct MR_QUAD_ELEMENT *quad;
int ldCount;
u16 ld;
- struct MR_LD_RAID *raid;
- for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES; ldCount++) {
+
+ for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES_EXT; ldCount++) {
ld = MR_TargetIdToLdGet(ldCount, map);
- if (ld >= MAX_LOGICAL_DRIVES) {
- lbInfo[ldCount].loadBalanceFlag = 0;
+ if (ld >= (MAX_LOGICAL_DRIVES_EXT - 1))
continue;
- }
-
raid = MR_LdRaidGet(ld, map);
+ for (element = 0; element < MAX_QUAD_DEPTH; element++) {
+ for (span = 0; span < raid->spanDepth; span++) {
+ if (le32_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].
+ block_span_info.noElements) <
+ element + 1)
+ continue;
+ span_set = &(ldSpanInfo[ld].span_set[element]);
+ quad = &map->raidMap.ldSpanMap[ld].
+ spanBlock[span].block_span_info.
+ quad[element];
+
+ span_set->diff = le32_to_cpu(quad->diff);
+
+ for (count = 0, span_row_width = 0;
+ count < raid->spanDepth; count++) {
+ if (le32_to_cpu(map->raidMap.ldSpanMap[ld].
+ spanBlock[count].
+ block_span_info.
+ noElements) >= element + 1) {
+ span_set->strip_offset[count] =
+ span_row_width;
+ span_row_width +=
+ MR_LdSpanPtrGet
+ (ld, count, map)->spanRowDataSize;
+ }
+ }
- /* Two drive Optimal RAID 1 */
- if ((raid->level == 1) && (raid->rowSize == 2) &&
- (raid->spanDepth == 1) && raid->ldState ==
- MR_LD_STATE_OPTIMAL) {
- u32 pd, arRef;
+ span_set->span_row_data_width = span_row_width;
+ span_row = mega_div64_32(((le64_to_cpu(quad->logEnd) -
+ le64_to_cpu(quad->logStart)) + le32_to_cpu(quad->diff)),
+ le32_to_cpu(quad->diff));
+
+ if (element == 0) {
+ span_set->log_start_lba = 0;
+ span_set->log_end_lba =
+ ((span_row << raid->stripeShift)
+ * span_row_width) - 1;
+
+ span_set->span_row_start = 0;
+ span_set->span_row_end = span_row - 1;
+
+ span_set->data_strip_start = 0;
+ span_set->data_strip_end =
+ (span_row * span_row_width) - 1;
+
+ span_set->data_row_start = 0;
+ span_set->data_row_end =
+ (span_row * le32_to_cpu(quad->diff)) - 1;
+ } else {
+ span_set_prev = &(ldSpanInfo[ld].
+ span_set[element - 1]);
+ span_set->log_start_lba =
+ span_set_prev->log_end_lba + 1;
+ span_set->log_end_lba =
+ span_set->log_start_lba +
+ ((span_row << raid->stripeShift)
+ * span_row_width) - 1;
+
+ span_set->span_row_start =
+ span_set_prev->span_row_end + 1;
+ span_set->span_row_end =
+ span_set->span_row_start + span_row - 1;
+
+ span_set->data_strip_start =
+ span_set_prev->data_strip_end + 1;
+ span_set->data_strip_end =
+ span_set->data_strip_start +
+ (span_row * span_row_width) - 1;
+
+ span_set->data_row_start =
+ span_set_prev->data_row_end + 1;
+ span_set->data_row_end =
+ span_set->data_row_start +
+ (span_row * le32_to_cpu(quad->diff)) - 1;
+ }
+ break;
+ }
+ if (span == raid->spanDepth)
+ break;
+ }
+ }
+}
- lbInfo[ldCount].loadBalanceFlag = 1;
+void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *drv_map,
+ struct LD_LOAD_BALANCE_INFO *lbInfo)
+{
+ int ldCount;
+ u16 ld;
+ struct MR_LD_RAID *raid;
- /* Get the array on which this span is present */
- arRef = MR_LdSpanArrayGet(ld, 0, map);
+ if (lb_pending_cmds > 128 || lb_pending_cmds < 1)
+ lb_pending_cmds = LB_PENDING_CMDS_DEFAULT;
- /* Get the Pd */
- pd = MR_ArPdGet(arRef, 0, map);
- /* Get dev handle from Pd */
- lbInfo[ldCount].raid1DevHandle[0] =
- MR_PdDevHandleGet(pd, map);
- /* Get the Pd */
- pd = MR_ArPdGet(arRef, 1, map);
+ for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES_EXT; ldCount++) {
+ ld = MR_TargetIdToLdGet(ldCount, drv_map);
+ if (ld >= MAX_LOGICAL_DRIVES_EXT - 1) {
+ lbInfo[ldCount].loadBalanceFlag = 0;
+ continue;
+ }
- /* Get the dev handle from Pd */
- lbInfo[ldCount].raid1DevHandle[1] =
- MR_PdDevHandleGet(pd, map);
- } else
+ raid = MR_LdRaidGet(ld, drv_map);
+ if ((raid->level != 1) ||
+ (raid->ldState != MR_LD_STATE_OPTIMAL)) {
lbInfo[ldCount].loadBalanceFlag = 0;
+ continue;
+ }
+ lbInfo[ldCount].loadBalanceFlag = 1;
}
}
-u8 megasas_get_best_arm(struct LD_LOAD_BALANCE_INFO *lbInfo, u8 arm, u64 block,
- u32 count)
+static u8 megasas_get_best_arm_pd(struct megasas_instance *instance,
+ struct LD_LOAD_BALANCE_INFO *lbInfo,
+ struct IO_REQUEST_INFO *io_info,
+ struct MR_DRV_RAID_MAP_ALL *drv_map)
{
- u16 pend0, pend1;
+ struct MR_LD_RAID *raid;
+ u16 pd1_dev_handle;
+ u16 pend0, pend1, ld;
u64 diff0, diff1;
- u8 bestArm;
+ u8 bestArm, pd0, pd1, span, arm;
+ u32 arRef, span_row_size;
- /* get the pending cmds for the data and mirror arms */
- pend0 = atomic_read(&lbInfo->scsi_pending_cmds[0]);
- pend1 = atomic_read(&lbInfo->scsi_pending_cmds[1]);
+ u64 block = io_info->ldStartBlock;
+ u32 count = io_info->numBlocks;
- /* Determine the disk whose head is nearer to the req. block */
- diff0 = ABS_DIFF(block, lbInfo->last_accessed_block[0]);
- diff1 = ABS_DIFF(block, lbInfo->last_accessed_block[1]);
- bestArm = (diff0 <= diff1 ? 0 : 1);
+ span = ((io_info->span_arm & RAID_CTX_SPANARM_SPAN_MASK)
+ >> RAID_CTX_SPANARM_SPAN_SHIFT);
+ arm = (io_info->span_arm & RAID_CTX_SPANARM_ARM_MASK);
- if ((bestArm == arm && pend0 > pend1 + 16) ||
- (bestArm != arm && pend1 > pend0 + 16))
- bestArm ^= 1;
+ ld = MR_TargetIdToLdGet(io_info->ldTgtId, drv_map);
+ raid = MR_LdRaidGet(ld, drv_map);
+ span_row_size = instance->UnevenSpanSupport ?
+ SPAN_ROW_SIZE(drv_map, ld, span) : raid->rowSize;
- /* Update the last accessed block on the correct pd */
- lbInfo->last_accessed_block[bestArm] = block + count - 1;
+ arRef = MR_LdSpanArrayGet(ld, span, drv_map);
+ pd0 = MR_ArPdGet(arRef, arm, drv_map);
+ pd1 = MR_ArPdGet(arRef, (arm + 1) >= span_row_size ?
+ (arm + 1 - span_row_size) : arm + 1, drv_map);
- return bestArm;
-}
+ /* Get PD1 Dev Handle */
-u16 get_updated_dev_handle(struct LD_LOAD_BALANCE_INFO *lbInfo,
- struct IO_REQUEST_INFO *io_info)
-{
- u8 arm, old_arm;
- u16 devHandle;
+ pd1_dev_handle = MR_PdDevHandleGet(pd1, drv_map);
- old_arm = lbInfo->raid1DevHandle[0] == io_info->devHandle ? 0 : 1;
+ if (pd1_dev_handle == MR_DEVHANDLE_INVALID) {
+ bestArm = arm;
+ } else {
+ /* get the pending cmds for the data and mirror arms */
+ pend0 = atomic_read(&lbInfo->scsi_pending_cmds[pd0]);
+ pend1 = atomic_read(&lbInfo->scsi_pending_cmds[pd1]);
+
+ /* Determine the disk whose head is nearer to the req. block */
+ diff0 = ABS_DIFF(block, lbInfo->last_accessed_block[pd0]);
+ diff1 = ABS_DIFF(block, lbInfo->last_accessed_block[pd1]);
+ bestArm = (diff0 <= diff1 ? arm : arm ^ 1);
+
+ /* Make balance count from 16 to 4 to
+ * keep driver in sync with Firmware
+ */
+ if ((bestArm == arm && pend0 > pend1 + lb_pending_cmds) ||
+ (bestArm != arm && pend1 > pend0 + lb_pending_cmds))
+ bestArm ^= 1;
+
+ /* Update the last accessed block on the correct pd */
+ io_info->span_arm =
+ (span << RAID_CTX_SPANARM_SPAN_SHIFT) | bestArm;
+ io_info->pd_after_lb = (bestArm == arm) ? pd0 : pd1;
+ }
+
+ lbInfo->last_accessed_block[io_info->pd_after_lb] = block + count - 1;
+ return io_info->pd_after_lb;
+}
- /* get best new arm */
- arm = megasas_get_best_arm(lbInfo, old_arm, io_info->ldStartBlock,
- io_info->numBlocks);
- devHandle = lbInfo->raid1DevHandle[arm];
- atomic_inc(&lbInfo->scsi_pending_cmds[arm]);
+__le16 get_updated_dev_handle(struct megasas_instance *instance,
+ struct LD_LOAD_BALANCE_INFO *lbInfo,
+ struct IO_REQUEST_INFO *io_info,
+ struct MR_DRV_RAID_MAP_ALL *drv_map)
+{
+ u8 arm_pd;
+ __le16 devHandle;
+
+ /* get best new arm (PD ID) */
+ arm_pd = megasas_get_best_arm_pd(instance, lbInfo, io_info, drv_map);
+ devHandle = MR_PdDevHandleGet(arm_pd, drv_map);
+ io_info->pd_interface = MR_PdInterfaceTypeGet(arm_pd, drv_map);
+ atomic_inc(&lbInfo->scsi_pending_cmds[arm_pd]);
return devHandle;
}
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index f13e7abd345a..a6794f49e9fa 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -1,32 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2011 LSI Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Copyright (c) 2009-2013 LSI Corporation
+ * Copyright (c) 2013-2016 Avago Technologies
+ * Copyright (c) 2016-2018 Broadcom Inc.
*
* FILE: megaraid_sas_fusion.c
*
- * Authors: LSI Corporation
+ * Authors: Broadcom Inc.
* Sumant Patro
- * Adam Radford <linuxraid@lsi.com>
+ * Adam Radford
+ * Kashyap Desai <kashyap.desai@broadcom.com>
+ * Sumit Saxena <sumit.saxena@broadcom.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
- *
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Send feedback to: megaraidlinux.pdl@broadcom.com
*/
#include <linux/kernel.h>
@@ -45,91 +33,181 @@
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/irq_poll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_dbg.h>
+#include <linux/dmi.h>
#include "megaraid_sas_fusion.h"
#include "megaraid_sas.h"
-extern void megasas_free_cmds(struct megasas_instance *instance);
-extern struct megasas_cmd *megasas_get_cmd(struct megasas_instance
- *instance);
+
extern void
megasas_complete_cmd(struct megasas_instance *instance,
struct megasas_cmd *cmd, u8 alt_status);
-int megasas_is_ldio(struct scsi_cmnd *cmd);
int
-wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd);
+wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
+ int seconds);
-void
-megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd);
-int megasas_alloc_cmds(struct megasas_instance *instance);
int
-megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs);
+megasas_clear_intr_fusion(struct megasas_instance *instance);
+
+int megasas_transition_to_ready(struct megasas_instance *instance, int ocr);
+
+extern u32 megasas_dbg_lvl;
+int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
+ int initial);
+extern struct megasas_mgmt_info megasas_mgmt_info;
+extern unsigned int resetwaittime;
+extern unsigned int dual_qdepth_disable;
+static void megasas_free_rdpq_fusion(struct megasas_instance *instance);
+static void megasas_free_reply_fusion(struct megasas_instance *instance);
+static inline
+void megasas_configure_queue_sizes(struct megasas_instance *instance);
+static void megasas_fusion_crash_dump(struct megasas_instance *instance);
+
+/**
+ * megasas_adp_reset_wait_for_ready - initiate chip reset and wait for
+ * controller to come to ready state
+ * @instance: adapter's soft state
+ * @do_adp_reset: If true, do a chip reset
+ * @ocr_context: If called from OCR context this will
+ * be set to 1, else 0
+ *
+ * This function initiates a chip reset followed by a wait for controller to
+ * transition to ready state.
+ * During this, driver will block all access to PCI config space from userspace
+ */
int
-megasas_issue_polled(struct megasas_instance *instance,
- struct megasas_cmd *cmd);
+megasas_adp_reset_wait_for_ready(struct megasas_instance *instance,
+ bool do_adp_reset,
+ int ocr_context)
+{
+ int ret = FAILED;
-u8
-MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
- struct RAID_CONTEXT *pRAID_Context,
- struct MR_FW_RAID_MAP_ALL *map);
-u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map);
-struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_FW_RAID_MAP_ALL *map);
+ /*
+ * Block access to PCI config space from userspace
+ * when diag reset is initiated from driver
+ */
+ if (megasas_dbg_lvl & OCR_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "Block access to PCI config space %s %d\n",
+ __func__, __LINE__);
-u16 MR_GetLDTgtId(u32 ld, struct MR_FW_RAID_MAP_ALL *map);
+ pci_cfg_access_lock(instance->pdev);
-void
-megasas_check_and_restore_queue_depth(struct megasas_instance *instance);
+ if (do_adp_reset) {
+ if (instance->instancet->adp_reset
+ (instance, instance->reg_set))
+ goto out;
+ }
-u8 MR_ValidateMapInfo(struct MR_FW_RAID_MAP_ALL *map,
- struct LD_LOAD_BALANCE_INFO *lbInfo);
-u16 get_updated_dev_handle(struct LD_LOAD_BALANCE_INFO *lbInfo,
- struct IO_REQUEST_INFO *in_info);
-int megasas_transition_to_ready(struct megasas_instance *instance);
-void megaraid_sas_kill_hba(struct megasas_instance *instance);
+ /* Wait for FW to become ready */
+ if (megasas_transition_to_ready(instance, ocr_context)) {
+ dev_warn(&instance->pdev->dev,
+ "Failed to transition controller to ready for scsi%d.\n",
+ instance->host->host_no);
+ goto out;
+ }
-extern u32 megasas_dbg_lvl;
+ ret = SUCCESS;
+out:
+ if (megasas_dbg_lvl & OCR_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "Unlock access to PCI config space %s %d\n",
+ __func__, __LINE__);
+
+ pci_cfg_access_unlock(instance->pdev);
+
+ return ret;
+}
+
+/**
+ * megasas_check_same_4gb_region - check if allocation
+ * crosses same 4GB boundary or not
+ * @instance: adapter's soft instance
+ * @start_addr: start address of DMA allocation
+ * @size: size of allocation in bytes
+ * @return: true : allocation does not cross same
+ * 4GB boundary
+ * false: allocation crosses same
+ * 4GB boundary
+ */
+static inline bool megasas_check_same_4gb_region
+ (struct megasas_instance *instance, dma_addr_t start_addr, size_t size)
+{
+ dma_addr_t end_addr;
+
+ end_addr = start_addr + size;
+
+ if (upper_32_bits(start_addr) != upper_32_bits(end_addr)) {
+ dev_err(&instance->pdev->dev,
+ "Failed to get same 4GB boundary: start_addr: 0x%llx end_addr: 0x%llx\n",
+ (unsigned long long)start_addr,
+ (unsigned long long)end_addr);
+ return false;
+ }
+
+ return true;
+}
/**
* megasas_enable_intr_fusion - Enables interrupts
- * @regs: MFI register set
+ * @instance: adapter's soft instance
*/
-void
-megasas_enable_intr_fusion(struct megasas_register_set __iomem *regs)
+static void
+megasas_enable_intr_fusion(struct megasas_instance *instance)
{
+ struct megasas_register_set __iomem *regs;
+ regs = instance->reg_set;
+
+ instance->mask_interrupts = 0;
+ /* For Thunderbolt/Invader also clear intr on enable */
+ writel(~0, &regs->outbound_intr_status);
+ readl(&regs->outbound_intr_status);
+
writel(~MFI_FUSION_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask);
/* Dummy readl to force pci flush */
- readl(&regs->outbound_intr_mask);
+ dev_info(&instance->pdev->dev, "%s is called outbound_intr_mask:0x%08x\n",
+ __func__, readl(&regs->outbound_intr_mask));
}
/**
* megasas_disable_intr_fusion - Disables interrupt
- * @regs: MFI register set
+ * @instance: adapter's soft instance
*/
-void
-megasas_disable_intr_fusion(struct megasas_register_set __iomem *regs)
+static void
+megasas_disable_intr_fusion(struct megasas_instance *instance)
{
u32 mask = 0xFFFFFFFF;
- u32 status;
+ struct megasas_register_set __iomem *regs;
+ regs = instance->reg_set;
+ instance->mask_interrupts = 1;
writel(mask, &regs->outbound_intr_mask);
/* Dummy readl to force pci flush */
- status = readl(&regs->outbound_intr_mask);
+ dev_info(&instance->pdev->dev, "%s is called outbound_intr_mask:0x%08x\n",
+ __func__, readl(&regs->outbound_intr_mask));
}
int
-megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs)
+megasas_clear_intr_fusion(struct megasas_instance *instance)
{
u32 status;
+ struct megasas_register_set __iomem *regs;
+ regs = instance->reg_set;
/*
* Check if it is our interrupt
*/
- status = readl(&regs->outbound_intr_status);
+ status = megasas_readl(instance,
+ &regs->outbound_intr_status);
if (status & 1) {
writel(status, &regs->outbound_intr_status);
@@ -139,40 +217,57 @@ megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs)
if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK))
return 0;
- /*
- * dummy read to flush PCI
- */
- readl(&regs->outbound_intr_status);
-
return 1;
}
+static inline void
+megasas_sdev_busy_inc(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd)
+{
+ if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
+ struct MR_PRIV_DEVICE *mr_device_priv_data =
+ scmd->device->hostdata;
+ atomic_inc(&mr_device_priv_data->sdev_priv_busy);
+ }
+}
+
+static inline void
+megasas_sdev_busy_dec(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd)
+{
+ if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
+ struct MR_PRIV_DEVICE *mr_device_priv_data =
+ scmd->device->hostdata;
+ atomic_dec(&mr_device_priv_data->sdev_priv_busy);
+ }
+}
+
+static inline int
+megasas_sdev_busy_read(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd)
+{
+ if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
+ struct MR_PRIV_DEVICE *mr_device_priv_data =
+ scmd->device->hostdata;
+ return atomic_read(&mr_device_priv_data->sdev_priv_busy);
+ }
+ return 0;
+}
+
/**
* megasas_get_cmd_fusion - Get a command from the free pool
* @instance: Adapter soft state
+ * @blk_tag: Command tag
*
- * Returns a free command from the pool
+ * Returns a blk_tag indexed mpt frame
*/
-struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
- *instance)
+inline struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
+ *instance, u32 blk_tag)
{
- unsigned long flags;
- struct fusion_context *fusion =
- (struct fusion_context *)instance->ctrl_context;
- struct megasas_cmd_fusion *cmd = NULL;
-
- spin_lock_irqsave(&fusion->cmd_pool_lock, flags);
-
- if (!list_empty(&fusion->cmd_pool)) {
- cmd = list_entry((&fusion->cmd_pool)->next,
- struct megasas_cmd_fusion, list);
- list_del_init(&cmd->list);
- } else {
- printk(KERN_ERR "megasas: Command pool (fusion) empty!\n");
- }
+ struct fusion_context *fusion;
- spin_unlock_irqrestore(&fusion->cmd_pool_lock, flags);
- return cmd;
+ fusion = instance->ctrl_context;
+ return fusion->cmd_list[blk_tag];
}
/**
@@ -180,68 +275,141 @@ struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
* @instance: Adapter soft state
* @cmd: Command packet to be returned to free command pool
*/
-static inline void
-megasas_return_cmd_fusion(struct megasas_instance *instance,
- struct megasas_cmd_fusion *cmd)
+inline void megasas_return_cmd_fusion(struct megasas_instance *instance,
+ struct megasas_cmd_fusion *cmd)
{
- unsigned long flags;
- struct fusion_context *fusion =
- (struct fusion_context *)instance->ctrl_context;
-
- spin_lock_irqsave(&fusion->cmd_pool_lock, flags);
-
cmd->scmd = NULL;
- cmd->sync_cmd_idx = (u32)ULONG_MAX;
- list_add_tail(&cmd->list, &fusion->cmd_pool);
-
- spin_unlock_irqrestore(&fusion->cmd_pool_lock, flags);
+ memset(cmd->io_request, 0, MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE);
+ cmd->r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
+ cmd->cmd_completed = false;
}
/**
- * megasas_teardown_frame_pool_fusion - Destroy the cmd frame DMA pool
+ * megasas_write_64bit_req_desc - PCI writes 64bit request descriptor
* @instance: Adapter soft state
+ * @req_desc: 64bit Request descriptor
*/
-static void megasas_teardown_frame_pool_fusion(
- struct megasas_instance *instance)
+static void
+megasas_write_64bit_req_desc(struct megasas_instance *instance,
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc)
{
- int i;
- struct fusion_context *fusion = instance->ctrl_context;
-
- u16 max_cmd = instance->max_fw_cmds;
-
- struct megasas_cmd_fusion *cmd;
+#if defined(writeq) && defined(CONFIG_64BIT)
+ u64 req_data = (((u64)le32_to_cpu(req_desc->u.high) << 32) |
+ le32_to_cpu(req_desc->u.low));
+ writeq(req_data, &instance->reg_set->inbound_low_queue_port);
+#else
+ unsigned long flags;
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ writel(le32_to_cpu(req_desc->u.low),
+ &instance->reg_set->inbound_low_queue_port);
+ writel(le32_to_cpu(req_desc->u.high),
+ &instance->reg_set->inbound_high_queue_port);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+#endif
+}
- if (!fusion->sg_dma_pool || !fusion->sense_dma_pool) {
- printk(KERN_ERR "megasas: dma pool is null. SG Pool %p, "
- "sense pool : %p\n", fusion->sg_dma_pool,
- fusion->sense_dma_pool);
- return;
- }
+/**
+ * megasas_fire_cmd_fusion - Sends command to the FW
+ * @instance: Adapter soft state
+ * @req_desc: 32bit or 64bit Request descriptor
+ *
+ * Perform PCI Write. AERO SERIES supports 32 bit Descriptor.
+ * Prior to AERO_SERIES support 64 bit Descriptor.
+ */
+static void
+megasas_fire_cmd_fusion(struct megasas_instance *instance,
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc)
+{
+ if (instance->atomic_desc_support)
+ writel(le32_to_cpu(req_desc->u.low),
+ &instance->reg_set->inbound_single_queue_port);
+ else
+ megasas_write_64bit_req_desc(instance, req_desc);
+}
- /*
- * Return all frames to pool
- */
- for (i = 0; i < max_cmd; i++) {
+/**
+ * megasas_fusion_update_can_queue - Do all Adapter Queue depth related calculations here
+ * @instance: Adapter soft state
+ * @fw_boot_context: Whether this function called during probe or after OCR
+ *
+ * This function is only for fusion controllers.
+ * Update host can queue, if firmware downgrade max supported firmware commands.
+ * Firmware upgrade case will be skipped because underlying firmware has
+ * more resource than exposed to the OS.
+ *
+ */
+static void
+megasas_fusion_update_can_queue(struct megasas_instance *instance, int fw_boot_context)
+{
+ u16 cur_max_fw_cmds = 0;
+ u16 ldio_threshold = 0;
- cmd = fusion->cmd_list[i];
+ /* ventura FW does not fill outbound_scratch_pad_2 with queue depth */
+ if (instance->adapter_type < VENTURA_SERIES)
+ cur_max_fw_cmds =
+ megasas_readl(instance,
+ &instance->reg_set->outbound_scratch_pad_2) & 0x00FFFF;
- if (cmd->sg_frame)
- pci_pool_free(fusion->sg_dma_pool, cmd->sg_frame,
- cmd->sg_frame_phys_addr);
+ if (dual_qdepth_disable || !cur_max_fw_cmds)
+ cur_max_fw_cmds = instance->instancet->read_fw_status_reg(instance) & 0x00FFFF;
+ else
+ ldio_threshold =
+ (instance->instancet->read_fw_status_reg(instance) & 0x00FFFF) - MEGASAS_FUSION_IOCTL_CMDS;
+
+ dev_info(&instance->pdev->dev,
+ "Current firmware supports maximum commands: %d\t LDIO threshold: %d\n",
+ cur_max_fw_cmds, ldio_threshold);
+
+ if (fw_boot_context == OCR_CONTEXT) {
+ cur_max_fw_cmds = cur_max_fw_cmds - 1;
+ if (cur_max_fw_cmds < instance->max_fw_cmds) {
+ instance->cur_can_queue =
+ cur_max_fw_cmds - (MEGASAS_FUSION_INTERNAL_CMDS +
+ MEGASAS_FUSION_IOCTL_CMDS);
+ instance->host->can_queue = instance->cur_can_queue;
+ instance->ldio_threshold = ldio_threshold;
+ }
+ } else {
+ instance->max_fw_cmds = cur_max_fw_cmds;
+ instance->ldio_threshold = ldio_threshold;
- if (cmd->sense)
- pci_pool_free(fusion->sense_dma_pool, cmd->sense,
- cmd->sense_phys_addr);
+ if (reset_devices)
+ instance->max_fw_cmds = min(instance->max_fw_cmds,
+ (u16)MEGASAS_KDUMP_QUEUE_DEPTH);
+ /*
+ * Reduce the max supported cmds by 1. This is to ensure that the
+ * reply_q_sz (1 more than the max cmd that driver may send)
+ * does not exceed max cmds that the FW can support
+ */
+ instance->max_fw_cmds = instance->max_fw_cmds-1;
}
+}
- /*
- * Now destroy the pool itself
- */
- pci_pool_destroy(fusion->sg_dma_pool);
- pci_pool_destroy(fusion->sense_dma_pool);
-
- fusion->sg_dma_pool = NULL;
- fusion->sense_dma_pool = NULL;
+static inline void
+megasas_get_msix_index(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd,
+ struct megasas_cmd_fusion *cmd,
+ u8 data_arms)
+{
+ if (instance->perf_mode == MR_BALANCED_PERF_MODE &&
+ (megasas_sdev_busy_read(instance, scmd) >
+ (data_arms * MR_DEVICE_HIGH_IOPS_DEPTH))) {
+ cmd->request_desc->SCSIIO.MSIxIndex =
+ mega_mod64((atomic64_add_return(1, &instance->high_iops_outstanding) /
+ MR_HIGH_IOPS_BATCH_COUNT), instance->low_latency_index_start);
+ } else if (instance->msix_load_balance) {
+ cmd->request_desc->SCSIIO.MSIxIndex =
+ (mega_mod64(atomic64_add_return(1, &instance->total_io_count),
+ instance->msix_vectors));
+ } else if (instance->host->nr_hw_queues > 1) {
+ u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+
+ cmd->request_desc->SCSIIO.MSIxIndex = blk_mq_unique_tag_to_hwq(tag) +
+ instance->low_latency_index_start;
+ } else {
+ cmd->request_desc->SCSIIO.MSIxIndex =
+ instance->reply_map[raw_smp_processor_id()];
+ }
}
/**
@@ -253,259 +421,596 @@ megasas_free_cmds_fusion(struct megasas_instance *instance)
{
int i;
struct fusion_context *fusion = instance->ctrl_context;
+ struct megasas_cmd_fusion *cmd;
- u32 max_cmds, req_sz, reply_sz, io_frames_sz;
-
-
- req_sz = fusion->request_alloc_sz;
- reply_sz = fusion->reply_alloc_sz;
- io_frames_sz = fusion->io_frames_alloc_sz;
-
- max_cmds = instance->max_fw_cmds;
-
- /* Free descriptors and request Frames memory */
- if (fusion->req_frames_desc)
- dma_free_coherent(&instance->pdev->dev, req_sz,
- fusion->req_frames_desc,
- fusion->req_frames_desc_phys);
-
- if (fusion->reply_frames_desc) {
- pci_pool_free(fusion->reply_frames_desc_pool,
- fusion->reply_frames_desc,
- fusion->reply_frames_desc_phys);
- pci_pool_destroy(fusion->reply_frames_desc_pool);
+ if (fusion->sense)
+ dma_pool_free(fusion->sense_dma_pool, fusion->sense,
+ fusion->sense_phys_addr);
+
+ /* SG */
+ if (fusion->cmd_list) {
+ for (i = 0; i < instance->max_mpt_cmds; i++) {
+ cmd = fusion->cmd_list[i];
+ if (cmd) {
+ if (cmd->sg_frame)
+ dma_pool_free(fusion->sg_dma_pool,
+ cmd->sg_frame,
+ cmd->sg_frame_phys_addr);
+ }
+ kfree(cmd);
+ }
+ kfree(fusion->cmd_list);
}
- if (fusion->io_request_frames) {
- pci_pool_free(fusion->io_request_frames_pool,
- fusion->io_request_frames,
- fusion->io_request_frames_phys);
- pci_pool_destroy(fusion->io_request_frames_pool);
+ if (fusion->sg_dma_pool) {
+ dma_pool_destroy(fusion->sg_dma_pool);
+ fusion->sg_dma_pool = NULL;
+ }
+ if (fusion->sense_dma_pool) {
+ dma_pool_destroy(fusion->sense_dma_pool);
+ fusion->sense_dma_pool = NULL;
}
- /* Free the Fusion frame pool */
- megasas_teardown_frame_pool_fusion(instance);
-
- /* Free all the commands in the cmd_list */
- for (i = 0; i < max_cmds; i++)
- kfree(fusion->cmd_list[i]);
- /* Free the cmd_list buffer itself */
- kfree(fusion->cmd_list);
- fusion->cmd_list = NULL;
+ /* Reply Frame, Desc*/
+ if (instance->is_rdpq)
+ megasas_free_rdpq_fusion(instance);
+ else
+ megasas_free_reply_fusion(instance);
- INIT_LIST_HEAD(&fusion->cmd_pool);
+ /* Request Frame, Desc*/
+ if (fusion->req_frames_desc)
+ dma_free_coherent(&instance->pdev->dev,
+ fusion->request_alloc_sz, fusion->req_frames_desc,
+ fusion->req_frames_desc_phys);
+ if (fusion->io_request_frames)
+ dma_pool_free(fusion->io_request_frames_pool,
+ fusion->io_request_frames,
+ fusion->io_request_frames_phys);
+ if (fusion->io_request_frames_pool) {
+ dma_pool_destroy(fusion->io_request_frames_pool);
+ fusion->io_request_frames_pool = NULL;
+ }
}
/**
- * megasas_create_frame_pool_fusion - Creates DMA pool for cmd frames
+ * megasas_create_sg_sense_fusion - Creates DMA pool for cmd frames
* @instance: Adapter soft state
*
*/
-static int megasas_create_frame_pool_fusion(struct megasas_instance *instance)
+static int megasas_create_sg_sense_fusion(struct megasas_instance *instance)
{
int i;
- u32 max_cmd;
+ u16 max_cmd;
struct fusion_context *fusion;
struct megasas_cmd_fusion *cmd;
- u32 total_sz_chain_frame;
+ int sense_sz;
+ u32 offset;
fusion = instance->ctrl_context;
max_cmd = instance->max_fw_cmds;
-
- total_sz_chain_frame = MEGASAS_MAX_SZ_CHAIN_FRAME;
-
- /*
- * Use DMA pool facility provided by PCI layer
- */
-
- fusion->sg_dma_pool = pci_pool_create("megasas sg pool fusion",
- instance->pdev,
- total_sz_chain_frame, 4,
- 0);
- if (!fusion->sg_dma_pool) {
- printk(KERN_DEBUG "megasas: failed to setup request pool "
- "fusion\n");
+ sense_sz = instance->max_mpt_cmds * SCSI_SENSE_BUFFERSIZE;
+
+ fusion->sg_dma_pool =
+ dma_pool_create("mr_sg", &instance->pdev->dev,
+ instance->max_chain_frame_sz,
+ MR_DEFAULT_NVME_PAGE_SIZE, 0);
+ /* SCSI_SENSE_BUFFERSIZE = 96 bytes */
+ fusion->sense_dma_pool =
+ dma_pool_create("mr_sense", &instance->pdev->dev,
+ sense_sz, 64, 0);
+
+ if (!fusion->sense_dma_pool || !fusion->sg_dma_pool) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
- fusion->sense_dma_pool = pci_pool_create("megasas sense pool fusion",
- instance->pdev,
- SCSI_SENSE_BUFFERSIZE, 64, 0);
- if (!fusion->sense_dma_pool) {
- printk(KERN_DEBUG "megasas: failed to setup sense pool "
- "fusion\n");
- pci_pool_destroy(fusion->sg_dma_pool);
- fusion->sg_dma_pool = NULL;
+ fusion->sense = dma_pool_alloc(fusion->sense_dma_pool,
+ GFP_KERNEL, &fusion->sense_phys_addr);
+ if (!fusion->sense) {
+ dev_err(&instance->pdev->dev,
+ "failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
+ /* sense buffer, request frame and reply desc pool requires to be in
+ * same 4 gb region. Below function will check this.
+ * In case of failure, new pci pool will be created with updated
+ * alignment.
+ * Older allocation and pool will be destroyed.
+ * Alignment will be used such a way that next allocation if success,
+ * will always meet same 4gb region requirement.
+ * Actual requirement is not alignment, but we need start and end of
+ * DMA address must have same upper 32 bit address.
+ */
+
+ if (!megasas_check_same_4gb_region(instance, fusion->sense_phys_addr,
+ sense_sz)) {
+ dma_pool_free(fusion->sense_dma_pool, fusion->sense,
+ fusion->sense_phys_addr);
+ fusion->sense = NULL;
+ dma_pool_destroy(fusion->sense_dma_pool);
+
+ fusion->sense_dma_pool =
+ dma_pool_create("mr_sense_align", &instance->pdev->dev,
+ sense_sz, roundup_pow_of_two(sense_sz),
+ 0);
+ if (!fusion->sense_dma_pool) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ fusion->sense = dma_pool_alloc(fusion->sense_dma_pool,
+ GFP_KERNEL,
+ &fusion->sense_phys_addr);
+ if (!fusion->sense) {
+ dev_err(&instance->pdev->dev,
+ "failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ }
+
/*
* Allocate and attach a frame to each of the commands in cmd_list
*/
for (i = 0; i < max_cmd; i++) {
+ cmd = fusion->cmd_list[i];
+ cmd->sg_frame = dma_pool_alloc(fusion->sg_dma_pool,
+ GFP_KERNEL, &cmd->sg_frame_phys_addr);
+
+ offset = SCSI_SENSE_BUFFERSIZE * i;
+ cmd->sense = (u8 *)fusion->sense + offset;
+ cmd->sense_phys_addr = fusion->sense_phys_addr + offset;
+
+ if (!cmd->sg_frame) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ }
+ /* create sense buffer for the raid 1/10 fp */
+ for (i = max_cmd; i < instance->max_mpt_cmds; i++) {
cmd = fusion->cmd_list[i];
+ offset = SCSI_SENSE_BUFFERSIZE * i;
+ cmd->sense = (u8 *)fusion->sense + offset;
+ cmd->sense_phys_addr = fusion->sense_phys_addr + offset;
- cmd->sg_frame = pci_pool_alloc(fusion->sg_dma_pool,
- GFP_KERNEL,
- &cmd->sg_frame_phys_addr);
+ }
- cmd->sense = pci_pool_alloc(fusion->sense_dma_pool,
- GFP_KERNEL, &cmd->sense_phys_addr);
- /*
- * megasas_teardown_frame_pool_fusion() takes care of freeing
- * whatever has been allocated
- */
- if (!cmd->sg_frame || !cmd->sense) {
- printk(KERN_DEBUG "megasas: pci_pool_alloc failed\n");
- megasas_teardown_frame_pool_fusion(instance);
+ return 0;
+}
+
+static int
+megasas_alloc_cmdlist_fusion(struct megasas_instance *instance)
+{
+ u32 max_mpt_cmd, i, j;
+ struct fusion_context *fusion;
+
+ fusion = instance->ctrl_context;
+
+ max_mpt_cmd = instance->max_mpt_cmds;
+
+ /*
+ * fusion->cmd_list is an array of struct megasas_cmd_fusion pointers.
+ * Allocate the dynamic array first and then allocate individual
+ * commands.
+ */
+ fusion->cmd_list =
+ kcalloc(max_mpt_cmd, sizeof(struct megasas_cmd_fusion *),
+ GFP_KERNEL);
+ if (!fusion->cmd_list) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < max_mpt_cmd; i++) {
+ fusion->cmd_list[i] = kzalloc(sizeof(struct megasas_cmd_fusion),
+ GFP_KERNEL);
+ if (!fusion->cmd_list[i]) {
+ for (j = 0; j < i; j++)
+ kfree(fusion->cmd_list[j]);
+ kfree(fusion->cmd_list);
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
}
+
return 0;
}
-/**
- * megasas_alloc_cmds_fusion - Allocates the command packets
- * @instance: Adapter soft state
- *
- *
- * Each frame has a 32-bit field called context. This context is used to get
- * back the megasas_cmd_fusion from the frame when a frame gets completed
- * In this driver, the 32 bit values are the indices into an array cmd_list.
- * This array is used only to look up the megasas_cmd_fusion given the context.
- * The free commands themselves are maintained in a linked list called cmd_pool.
- *
- * cmds are formed in the io_request and sg_frame members of the
- * megasas_cmd_fusion. The context field is used to get a request descriptor
- * and is used as SMID of the cmd.
- * SMID value range is from 1 to max_fw_cmds.
- */
-int
-megasas_alloc_cmds_fusion(struct megasas_instance *instance)
+static int
+megasas_alloc_request_fusion(struct megasas_instance *instance)
{
- int i, j;
- u32 max_cmd, io_frames_sz;
struct fusion_context *fusion;
- struct megasas_cmd_fusion *cmd;
- union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc;
- u32 offset;
- dma_addr_t io_req_base_phys;
- u8 *io_req_base;
fusion = instance->ctrl_context;
- max_cmd = instance->max_fw_cmds;
+retry_alloc:
+ fusion->io_request_frames_pool =
+ dma_pool_create("mr_ioreq", &instance->pdev->dev,
+ fusion->io_frames_alloc_sz, 16, 0);
+
+ if (!fusion->io_request_frames_pool) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ fusion->io_request_frames =
+ dma_pool_alloc(fusion->io_request_frames_pool,
+ GFP_KERNEL | __GFP_NOWARN,
+ &fusion->io_request_frames_phys);
+ if (!fusion->io_request_frames) {
+ if (instance->max_fw_cmds >= (MEGASAS_REDUCE_QD_COUNT * 2)) {
+ instance->max_fw_cmds -= MEGASAS_REDUCE_QD_COUNT;
+ dma_pool_destroy(fusion->io_request_frames_pool);
+ megasas_configure_queue_sizes(instance);
+ goto retry_alloc;
+ } else {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ }
+
+ if (!megasas_check_same_4gb_region(instance,
+ fusion->io_request_frames_phys,
+ fusion->io_frames_alloc_sz)) {
+ dma_pool_free(fusion->io_request_frames_pool,
+ fusion->io_request_frames,
+ fusion->io_request_frames_phys);
+ fusion->io_request_frames = NULL;
+ dma_pool_destroy(fusion->io_request_frames_pool);
+
+ fusion->io_request_frames_pool =
+ dma_pool_create("mr_ioreq_align",
+ &instance->pdev->dev,
+ fusion->io_frames_alloc_sz,
+ roundup_pow_of_two(fusion->io_frames_alloc_sz),
+ 0);
+
+ if (!fusion->io_request_frames_pool) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ fusion->io_request_frames =
+ dma_pool_alloc(fusion->io_request_frames_pool,
+ GFP_KERNEL | __GFP_NOWARN,
+ &fusion->io_request_frames_phys);
+
+ if (!fusion->io_request_frames) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ }
fusion->req_frames_desc =
dma_alloc_coherent(&instance->pdev->dev,
fusion->request_alloc_sz,
&fusion->req_frames_desc_phys, GFP_KERNEL);
-
if (!fusion->req_frames_desc) {
- printk(KERN_ERR "megasas; Could not allocate memory for "
- "request_frames\n");
- goto fail_req_desc;
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
}
+ return 0;
+}
+
+static int
+megasas_alloc_reply_fusion(struct megasas_instance *instance)
+{
+ int i, count;
+ struct fusion_context *fusion;
+ union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc;
+ fusion = instance->ctrl_context;
+
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+ count += instance->iopoll_q_count;
+
fusion->reply_frames_desc_pool =
- pci_pool_create("reply_frames pool", instance->pdev,
- fusion->reply_alloc_sz, 16, 0);
+ dma_pool_create("mr_reply", &instance->pdev->dev,
+ fusion->reply_alloc_sz * count, 16, 0);
if (!fusion->reply_frames_desc_pool) {
- printk(KERN_ERR "megasas; Could not allocate memory for "
- "reply_frame pool\n");
- goto fail_reply_desc;
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
}
- fusion->reply_frames_desc =
- pci_pool_alloc(fusion->reply_frames_desc_pool, GFP_KERNEL,
- &fusion->reply_frames_desc_phys);
- if (!fusion->reply_frames_desc) {
- printk(KERN_ERR "megasas; Could not allocate memory for "
- "reply_frame pool\n");
- pci_pool_destroy(fusion->reply_frames_desc_pool);
- goto fail_reply_desc;
+ fusion->reply_frames_desc[0] =
+ dma_pool_alloc(fusion->reply_frames_desc_pool,
+ GFP_KERNEL, &fusion->reply_frames_desc_phys[0]);
+ if (!fusion->reply_frames_desc[0]) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
}
- reply_desc = fusion->reply_frames_desc;
- for (i = 0; i < fusion->reply_q_depth; i++, reply_desc++)
- reply_desc->Words = ULLONG_MAX;
-
- io_frames_sz = fusion->io_frames_alloc_sz;
+ if (!megasas_check_same_4gb_region(instance,
+ fusion->reply_frames_desc_phys[0],
+ (fusion->reply_alloc_sz * count))) {
+ dma_pool_free(fusion->reply_frames_desc_pool,
+ fusion->reply_frames_desc[0],
+ fusion->reply_frames_desc_phys[0]);
+ fusion->reply_frames_desc[0] = NULL;
+ dma_pool_destroy(fusion->reply_frames_desc_pool);
+
+ fusion->reply_frames_desc_pool =
+ dma_pool_create("mr_reply_align",
+ &instance->pdev->dev,
+ fusion->reply_alloc_sz * count,
+ roundup_pow_of_two(fusion->reply_alloc_sz * count),
+ 0);
+
+ if (!fusion->reply_frames_desc_pool) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
- fusion->io_request_frames_pool =
- pci_pool_create("io_request_frames pool", instance->pdev,
- fusion->io_frames_alloc_sz, 16, 0);
+ fusion->reply_frames_desc[0] =
+ dma_pool_alloc(fusion->reply_frames_desc_pool,
+ GFP_KERNEL,
+ &fusion->reply_frames_desc_phys[0]);
- if (!fusion->io_request_frames_pool) {
- printk(KERN_ERR "megasas: Could not allocate memory for "
- "io_request_frame pool\n");
- goto fail_io_frames;
+ if (!fusion->reply_frames_desc[0]) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
}
- fusion->io_request_frames =
- pci_pool_alloc(fusion->io_request_frames_pool, GFP_KERNEL,
- &fusion->io_request_frames_phys);
- if (!fusion->io_request_frames) {
- printk(KERN_ERR "megasas: Could not allocate memory for "
- "io_request_frames frames\n");
- pci_pool_destroy(fusion->io_request_frames_pool);
- goto fail_io_frames;
- }
+ reply_desc = fusion->reply_frames_desc[0];
+ for (i = 0; i < fusion->reply_q_depth * count; i++, reply_desc++)
+ reply_desc->Words = cpu_to_le64(ULLONG_MAX);
- /*
- * fusion->cmd_list is an array of struct megasas_cmd_fusion pointers.
- * Allocate the dynamic array first and then allocate individual
- * commands.
+ /* This is not a rdpq mode, but driver still populate
+ * reply_frame_desc array to use same msix index in ISR path.
*/
- fusion->cmd_list = kmalloc(sizeof(struct megasas_cmd_fusion *)
- *max_cmd, GFP_KERNEL);
+ for (i = 0; i < (count - 1); i++)
+ fusion->reply_frames_desc[i + 1] =
+ fusion->reply_frames_desc[i] +
+ (fusion->reply_alloc_sz)/sizeof(union MPI2_REPLY_DESCRIPTORS_UNION);
- if (!fusion->cmd_list) {
- printk(KERN_DEBUG "megasas: out of memory. Could not alloc "
- "memory for cmd_list_fusion\n");
- goto fail_cmd_list;
+ return 0;
+}
+
+static int
+megasas_alloc_rdpq_fusion(struct megasas_instance *instance)
+{
+ int i, j, k, msix_count;
+ struct fusion_context *fusion;
+ union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc;
+ union MPI2_REPLY_DESCRIPTORS_UNION *rdpq_chunk_virt[RDPQ_MAX_CHUNK_COUNT];
+ dma_addr_t rdpq_chunk_phys[RDPQ_MAX_CHUNK_COUNT];
+ u8 dma_alloc_count, abs_index;
+ u32 chunk_size, array_size, offset;
+
+ fusion = instance->ctrl_context;
+ chunk_size = fusion->reply_alloc_sz * RDPQ_MAX_INDEX_IN_ONE_CHUNK;
+ array_size = sizeof(struct MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY) *
+ MAX_MSIX_QUEUES_FUSION;
+
+ fusion->rdpq_virt = dma_alloc_coherent(&instance->pdev->dev,
+ array_size, &fusion->rdpq_phys,
+ GFP_KERNEL);
+ if (!fusion->rdpq_virt) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
}
- memset(fusion->cmd_list, 0, sizeof(struct megasas_cmd_fusion *)
- *max_cmd);
+ msix_count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+ msix_count += instance->iopoll_q_count;
+
+ fusion->reply_frames_desc_pool = dma_pool_create("mr_rdpq",
+ &instance->pdev->dev,
+ chunk_size, 16, 0);
+ fusion->reply_frames_desc_pool_align =
+ dma_pool_create("mr_rdpq_align",
+ &instance->pdev->dev,
+ chunk_size,
+ roundup_pow_of_two(chunk_size),
+ 0);
+
+ if (!fusion->reply_frames_desc_pool ||
+ !fusion->reply_frames_desc_pool_align) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
- max_cmd = instance->max_fw_cmds;
- for (i = 0; i < max_cmd; i++) {
- fusion->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd_fusion),
- GFP_KERNEL);
- if (!fusion->cmd_list[i]) {
- printk(KERN_ERR "Could not alloc cmd list fusion\n");
+/*
+ * For INVADER_SERIES each set of 8 reply queues(0-7, 8-15, ..) and
+ * VENTURA_SERIES each set of 16 reply queues(0-15, 16-31, ..) should be
+ * within 4GB boundary and also reply queues in a set must have same
+ * upper 32-bits in their memory address. so here driver is allocating the
+ * DMA'able memory for reply queues according. Driver uses limitation of
+ * VENTURA_SERIES to manage INVADER_SERIES as well.
+ */
+ dma_alloc_count = DIV_ROUND_UP(msix_count, RDPQ_MAX_INDEX_IN_ONE_CHUNK);
+
+ for (i = 0; i < dma_alloc_count; i++) {
+ rdpq_chunk_virt[i] =
+ dma_pool_alloc(fusion->reply_frames_desc_pool,
+ GFP_KERNEL, &rdpq_chunk_phys[i]);
+ if (!rdpq_chunk_virt[i]) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ /* reply desc pool requires to be in same 4 gb region.
+ * Below function will check this.
+ * In case of failure, new pci pool will be created with updated
+ * alignment.
+ * For RDPQ buffers, driver always allocate two separate pci pool.
+ * Alignment will be used such a way that next allocation if
+ * success, will always meet same 4gb region requirement.
+ * rdpq_tracker keep track of each buffer's physical,
+ * virtual address and pci pool descriptor. It will help driver
+ * while freeing the resources.
+ *
+ */
+ if (!megasas_check_same_4gb_region(instance, rdpq_chunk_phys[i],
+ chunk_size)) {
+ dma_pool_free(fusion->reply_frames_desc_pool,
+ rdpq_chunk_virt[i],
+ rdpq_chunk_phys[i]);
+
+ rdpq_chunk_virt[i] =
+ dma_pool_alloc(fusion->reply_frames_desc_pool_align,
+ GFP_KERNEL, &rdpq_chunk_phys[i]);
+ if (!rdpq_chunk_virt[i]) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ fusion->rdpq_tracker[i].dma_pool_ptr =
+ fusion->reply_frames_desc_pool_align;
+ } else {
+ fusion->rdpq_tracker[i].dma_pool_ptr =
+ fusion->reply_frames_desc_pool;
+ }
- for (j = 0; j < i; j++)
- kfree(fusion->cmd_list[j]);
+ fusion->rdpq_tracker[i].pool_entry_phys = rdpq_chunk_phys[i];
+ fusion->rdpq_tracker[i].pool_entry_virt = rdpq_chunk_virt[i];
+ }
- kfree(fusion->cmd_list);
- fusion->cmd_list = NULL;
- goto fail_cmd_list;
+ for (k = 0; k < dma_alloc_count; k++) {
+ for (i = 0; i < RDPQ_MAX_INDEX_IN_ONE_CHUNK; i++) {
+ abs_index = (k * RDPQ_MAX_INDEX_IN_ONE_CHUNK) + i;
+
+ if (abs_index == msix_count)
+ break;
+ offset = fusion->reply_alloc_sz * i;
+ fusion->rdpq_virt[abs_index].RDPQBaseAddress =
+ cpu_to_le64(rdpq_chunk_phys[k] + offset);
+ fusion->reply_frames_desc_phys[abs_index] =
+ rdpq_chunk_phys[k] + offset;
+ fusion->reply_frames_desc[abs_index] =
+ (union MPI2_REPLY_DESCRIPTORS_UNION *)((u8 *)rdpq_chunk_virt[k] + offset);
+
+ reply_desc = fusion->reply_frames_desc[abs_index];
+ for (j = 0; j < fusion->reply_q_depth; j++, reply_desc++)
+ reply_desc->Words = ULLONG_MAX;
}
}
- /* The first 256 bytes (SMID 0) is not used. Don't add to cmd list */
- io_req_base = fusion->io_request_frames +
- MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
- io_req_base_phys = fusion->io_request_frames_phys +
- MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
+ return 0;
+}
+
+static void
+megasas_free_rdpq_fusion(struct megasas_instance *instance) {
+
+ int i;
+ struct fusion_context *fusion;
+
+ fusion = instance->ctrl_context;
+
+ for (i = 0; i < RDPQ_MAX_CHUNK_COUNT; i++) {
+ if (fusion->rdpq_tracker[i].pool_entry_virt)
+ dma_pool_free(fusion->rdpq_tracker[i].dma_pool_ptr,
+ fusion->rdpq_tracker[i].pool_entry_virt,
+ fusion->rdpq_tracker[i].pool_entry_phys);
+
+ }
+
+ dma_pool_destroy(fusion->reply_frames_desc_pool);
+ dma_pool_destroy(fusion->reply_frames_desc_pool_align);
+
+ if (fusion->rdpq_virt)
+ dma_free_coherent(&instance->pdev->dev,
+ sizeof(struct MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY) * MAX_MSIX_QUEUES_FUSION,
+ fusion->rdpq_virt, fusion->rdpq_phys);
+}
+
+static void
+megasas_free_reply_fusion(struct megasas_instance *instance) {
+
+ struct fusion_context *fusion;
+
+ fusion = instance->ctrl_context;
+
+ if (fusion->reply_frames_desc[0])
+ dma_pool_free(fusion->reply_frames_desc_pool,
+ fusion->reply_frames_desc[0],
+ fusion->reply_frames_desc_phys[0]);
+
+ dma_pool_destroy(fusion->reply_frames_desc_pool);
+
+}
+
+
+/**
+ * megasas_alloc_cmds_fusion - Allocates the command packets
+ * @instance: Adapter soft state
+ *
+ *
+ * Each frame has a 32-bit field called context. This context is used to get
+ * back the megasas_cmd_fusion from the frame when a frame gets completed
+ * In this driver, the 32 bit values are the indices into an array cmd_list.
+ * This array is used only to look up the megasas_cmd_fusion given the context.
+ * The free commands themselves are maintained in a linked list called cmd_pool.
+ *
+ * cmds are formed in the io_request and sg_frame members of the
+ * megasas_cmd_fusion. The context field is used to get a request descriptor
+ * and is used as SMID of the cmd.
+ * SMID value range is from 1 to max_fw_cmds.
+ */
+static int
+megasas_alloc_cmds_fusion(struct megasas_instance *instance)
+{
+ int i;
+ struct fusion_context *fusion;
+ struct megasas_cmd_fusion *cmd;
+ u32 offset;
+ dma_addr_t io_req_base_phys;
+ u8 *io_req_base;
+
+
+ fusion = instance->ctrl_context;
+
+ if (megasas_alloc_request_fusion(instance))
+ goto fail_exit;
+
+ if (instance->is_rdpq) {
+ if (megasas_alloc_rdpq_fusion(instance))
+ goto fail_exit;
+ } else
+ if (megasas_alloc_reply_fusion(instance))
+ goto fail_exit;
+
+ if (megasas_alloc_cmdlist_fusion(instance))
+ goto fail_exit;
+
+ /* The first 256 bytes (SMID 0) is not used. Don't add to the cmd list */
+ io_req_base = fusion->io_request_frames + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
+ io_req_base_phys = fusion->io_request_frames_phys + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
/*
* Add all the commands to command pool (fusion->cmd_pool)
*/
/* SMID 0 is reserved. Set SMID/index from 1 */
- for (i = 0; i < max_cmd; i++) {
+ for (i = 0; i < instance->max_mpt_cmds; i++) {
cmd = fusion->cmd_list[i];
offset = MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE * i;
memset(cmd, 0, sizeof(struct megasas_cmd_fusion));
cmd->index = i + 1;
cmd->scmd = NULL;
- cmd->sync_cmd_idx = (u32)ULONG_MAX; /* Set to Invalid */
+ cmd->sync_cmd_idx =
+ (i >= instance->max_scsi_cmds && i < instance->max_fw_cmds) ?
+ (i - instance->max_scsi_cmds) :
+ (u32)ULONG_MAX; /* Set to Invalid */
cmd->instance = instance;
cmd->io_request =
(struct MPI2_RAID_SCSI_IO_REQUEST *)
@@ -513,39 +1018,16 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
memset(cmd->io_request, 0,
sizeof(struct MPI2_RAID_SCSI_IO_REQUEST));
cmd->io_request_phys_addr = io_req_base_phys + offset;
-
- list_add_tail(&cmd->list, &fusion->cmd_pool);
+ cmd->r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
}
- /*
- * Create a frame pool and assign one frame to each cmd
- */
- if (megasas_create_frame_pool_fusion(instance)) {
- printk(KERN_DEBUG "megasas: Error creating frame DMA pool\n");
- megasas_free_cmds_fusion(instance);
- goto fail_req_desc;
- }
+ if (megasas_create_sg_sense_fusion(instance))
+ goto fail_exit;
return 0;
-fail_cmd_list:
- pci_pool_free(fusion->io_request_frames_pool, fusion->io_request_frames,
- fusion->io_request_frames_phys);
- pci_pool_destroy(fusion->io_request_frames_pool);
-fail_io_frames:
- dma_free_coherent(&instance->pdev->dev, fusion->request_alloc_sz,
- fusion->reply_frames_desc,
- fusion->reply_frames_desc_phys);
- pci_pool_free(fusion->reply_frames_desc_pool,
- fusion->reply_frames_desc,
- fusion->reply_frames_desc_phys);
- pci_pool_destroy(fusion->reply_frames_desc_pool);
-
-fail_reply_desc:
- dma_free_coherent(&instance->pdev->dev, fusion->request_alloc_sz,
- fusion->req_frames_desc,
- fusion->req_frames_desc_phys);
-fail_req_desc:
+fail_exit:
+ megasas_free_cmds_fusion(instance);
return -ENOMEM;
}
@@ -553,16 +1035,19 @@ fail_req_desc:
* wait_and_poll - Issues a polling command
* @instance: Adapter soft state
* @cmd: Command packet to be issued
+ * @seconds: Maximum poll time
*
* For polling, MFI requires the cmd_status to be set to 0xFF before posting.
*/
int
-wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd)
+wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
+ int seconds)
{
int i;
struct megasas_header *frame_hdr = &cmd->frame->hdr;
+ u32 status_reg;
- u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000;
+ u32 msecs = seconds * 1000;
/*
* Wait for cmd_status to change
@@ -570,12 +1055,20 @@ wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd)
for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i += 20) {
rmb();
msleep(20);
+ if (!(i % 5000)) {
+ status_reg = instance->instancet->read_fw_status_reg(instance)
+ & MFI_STATE_MASK;
+ if (status_reg == MFI_STATE_FAULT)
+ break;
+ }
}
- if (frame_hdr->cmd_status == 0xff)
- return -ETIME;
-
- return 0;
+ if (frame_hdr->cmd_status == MFI_STAT_INVALID_STATUS)
+ return DCMD_TIMEOUT;
+ else if (frame_hdr->cmd_status == MFI_STAT_OK)
+ return DCMD_SUCCESS;
+ else
+ return DCMD_FAILED;
}
/**
@@ -588,110 +1081,299 @@ int
megasas_ioc_init_fusion(struct megasas_instance *instance)
{
struct megasas_init_frame *init_frame;
- struct MPI2_IOC_INIT_REQUEST *IOCInitMessage;
+ struct MPI2_IOC_INIT_REQUEST *IOCInitMessage = NULL;
dma_addr_t ioc_init_handle;
- u32 context;
struct megasas_cmd *cmd;
- u8 ret;
+ u8 ret, cur_rdpq_mode;
struct fusion_context *fusion;
- union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION req_desc;
int i;
struct megasas_header *frame_hdr;
+ const char *sys_info;
+ MFI_CAPABILITIES *drv_ops;
+ u32 scratch_pad_1;
+ ktime_t time;
+ bool cur_fw_64bit_dma_capable;
+ bool cur_intr_coalescing;
fusion = instance->ctrl_context;
- cmd = megasas_get_cmd(instance);
+ ioc_init_handle = fusion->ioc_init_request_phys;
+ IOCInitMessage = fusion->ioc_init_request;
- if (!cmd) {
- printk(KERN_ERR "Could not allocate cmd for INIT Frame\n");
- ret = 1;
- goto fail_get_cmd;
- }
+ cmd = fusion->ioc_init_cmd;
+
+ scratch_pad_1 = megasas_readl
+ (instance, &instance->reg_set->outbound_scratch_pad_1);
+
+ cur_rdpq_mode = (scratch_pad_1 & MR_RDPQ_MODE_OFFSET) ? 1 : 0;
- IOCInitMessage =
- dma_alloc_coherent(&instance->pdev->dev,
- sizeof(struct MPI2_IOC_INIT_REQUEST),
- &ioc_init_handle, GFP_KERNEL);
+ if (instance->adapter_type == INVADER_SERIES) {
+ cur_fw_64bit_dma_capable =
+ (scratch_pad_1 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET) ? true : false;
- if (!IOCInitMessage) {
- printk(KERN_ERR "Could not allocate memory for "
- "IOCInitMessage\n");
+ if (instance->consistent_mask_64bit && !cur_fw_64bit_dma_capable) {
+ dev_err(&instance->pdev->dev, "Driver was operating on 64bit "
+ "DMA mask, but upcoming FW does not support 64bit DMA mask\n");
+ megaraid_sas_kill_hba(instance);
+ ret = 1;
+ goto fail_fw_init;
+ }
+ }
+
+ if (instance->is_rdpq && !cur_rdpq_mode) {
+ dev_err(&instance->pdev->dev, "Firmware downgrade *NOT SUPPORTED*"
+ " from RDPQ mode to non RDPQ mode\n");
ret = 1;
goto fail_fw_init;
}
+ cur_intr_coalescing = (scratch_pad_1 & MR_INTR_COALESCING_SUPPORT_OFFSET) ?
+ true : false;
+
+ if ((instance->low_latency_index_start ==
+ MR_HIGH_IOPS_QUEUE_COUNT) && cur_intr_coalescing)
+ instance->perf_mode = MR_BALANCED_PERF_MODE;
+
+ dev_info(&instance->pdev->dev, "Performance mode :%s (latency index = %d)\n",
+ MEGASAS_PERF_MODE_2STR(instance->perf_mode),
+ instance->low_latency_index_start);
+
+ instance->fw_sync_cache_support = (scratch_pad_1 &
+ MR_CAN_HANDLE_SYNC_CACHE_OFFSET) ? 1 : 0;
+ dev_info(&instance->pdev->dev, "FW supports sync cache\t: %s\n",
+ instance->fw_sync_cache_support ? "Yes" : "No");
+
memset(IOCInitMessage, 0, sizeof(struct MPI2_IOC_INIT_REQUEST));
IOCInitMessage->Function = MPI2_FUNCTION_IOC_INIT;
IOCInitMessage->WhoInit = MPI2_WHOINIT_HOST_DRIVER;
- IOCInitMessage->MsgVersion = MPI2_VERSION;
- IOCInitMessage->HeaderVersion = MPI2_HEADER_VERSION;
- IOCInitMessage->SystemRequestFrameSize =
- MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE / 4;
-
- IOCInitMessage->ReplyDescriptorPostQueueDepth = fusion->reply_q_depth;
- IOCInitMessage->ReplyDescriptorPostQueueAddress =
- fusion->reply_frames_desc_phys;
- IOCInitMessage->SystemRequestFrameBaseAddress =
- fusion->io_request_frames_phys;
+ IOCInitMessage->MsgVersion = cpu_to_le16(MPI2_VERSION);
+ IOCInitMessage->HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
+ IOCInitMessage->SystemRequestFrameSize = cpu_to_le16(MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE / 4);
+
+ IOCInitMessage->ReplyDescriptorPostQueueDepth = cpu_to_le16(fusion->reply_q_depth);
+ IOCInitMessage->ReplyDescriptorPostQueueAddress = instance->is_rdpq ?
+ cpu_to_le64(fusion->rdpq_phys) :
+ cpu_to_le64(fusion->reply_frames_desc_phys[0]);
+ IOCInitMessage->MsgFlags = instance->is_rdpq ?
+ MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE : 0;
+ IOCInitMessage->SystemRequestFrameBaseAddress = cpu_to_le64(fusion->io_request_frames_phys);
+ IOCInitMessage->SenseBufferAddressHigh = cpu_to_le32(upper_32_bits(fusion->sense_phys_addr));
+ IOCInitMessage->HostMSIxVectors = instance->msix_vectors + instance->iopoll_q_count;
+ IOCInitMessage->HostPageSize = MR_DEFAULT_NVME_PAGE_SHIFT;
+
+ time = ktime_get_real();
+ /* Convert to milliseconds as per FW requirement */
+ IOCInitMessage->TimeStamp = cpu_to_le64(ktime_to_ms(time));
init_frame = (struct megasas_init_frame *)cmd->frame;
- memset(init_frame, 0, MEGAMFI_FRAME_SIZE);
+ memset(init_frame, 0, IOC_INIT_FRAME_SIZE);
frame_hdr = &cmd->frame->hdr;
- context = init_frame->context;
- init_frame->context = context;
-
frame_hdr->cmd_status = 0xFF;
- frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
+ frame_hdr->flags |= cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE);
init_frame->cmd = MFI_CMD_INIT;
init_frame->cmd_status = 0xFF;
- init_frame->queue_info_new_phys_addr_lo = ioc_init_handle;
- init_frame->data_xfer_len = sizeof(struct MPI2_IOC_INIT_REQUEST);
+ drv_ops = (MFI_CAPABILITIES *) &(init_frame->driver_operations);
+
+ /* driver support Extended MSIX */
+ if (instance->adapter_type >= INVADER_SERIES)
+ drv_ops->mfi_capabilities.support_additional_msix = 1;
+ /* driver supports HA / Remote LUN over Fast Path interface */
+ drv_ops->mfi_capabilities.support_fp_remote_lun = 1;
- req_desc =
- (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)fusion->req_frames_desc;
+ drv_ops->mfi_capabilities.support_max_255lds = 1;
+ drv_ops->mfi_capabilities.support_ndrive_r1_lb = 1;
+ drv_ops->mfi_capabilities.security_protocol_cmds_fw = 1;
- req_desc->Words = cmd->frame_phys_addr;
- req_desc->MFAIo.RequestFlags =
+ if (instance->max_chain_frame_sz > MEGASAS_CHAIN_FRAME_SZ_MIN)
+ drv_ops->mfi_capabilities.support_ext_io_size = 1;
+
+ drv_ops->mfi_capabilities.support_fp_rlbypass = 1;
+ if (!dual_qdepth_disable)
+ drv_ops->mfi_capabilities.support_ext_queue_depth = 1;
+
+ drv_ops->mfi_capabilities.support_qd_throttling = 1;
+ drv_ops->mfi_capabilities.support_pd_map_target_id = 1;
+ drv_ops->mfi_capabilities.support_nvme_passthru = 1;
+ drv_ops->mfi_capabilities.support_fw_exposed_dev_list = 1;
+
+ if (reset_devices)
+ drv_ops->mfi_capabilities.support_memdump = 1;
+
+ if (instance->consistent_mask_64bit)
+ drv_ops->mfi_capabilities.support_64bit_mode = 1;
+
+ /* Convert capability to LE32 */
+ cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities);
+
+ sys_info = dmi_get_system_info(DMI_PRODUCT_UUID);
+ if (instance->system_info_buf && sys_info) {
+ memcpy(instance->system_info_buf->systemId, sys_info,
+ strlen(sys_info) > 64 ? 64 : strlen(sys_info));
+ instance->system_info_buf->systemIdLength =
+ strlen(sys_info) > 64 ? 64 : strlen(sys_info);
+ init_frame->system_info_lo = cpu_to_le32(lower_32_bits(instance->system_info_h));
+ init_frame->system_info_hi = cpu_to_le32(upper_32_bits(instance->system_info_h));
+ }
+
+ init_frame->queue_info_new_phys_addr_hi =
+ cpu_to_le32(upper_32_bits(ioc_init_handle));
+ init_frame->queue_info_new_phys_addr_lo =
+ cpu_to_le32(lower_32_bits(ioc_init_handle));
+ init_frame->data_xfer_len = cpu_to_le32(sizeof(struct MPI2_IOC_INIT_REQUEST));
+
+ /*
+ * Each bit in replyqueue_mask represents one group of MSI-x vectors
+ * (each group has 8 vectors)
+ */
+ switch (instance->perf_mode) {
+ case MR_BALANCED_PERF_MODE:
+ init_frame->replyqueue_mask =
+ cpu_to_le16(~(~0 << instance->low_latency_index_start/8));
+ break;
+ case MR_IOPS_PERF_MODE:
+ init_frame->replyqueue_mask =
+ cpu_to_le16(~(~0 << instance->msix_vectors/8));
+ break;
+ }
+
+
+ req_desc.u.low = cpu_to_le32(lower_32_bits(cmd->frame_phys_addr));
+ req_desc.u.high = cpu_to_le32(upper_32_bits(cmd->frame_phys_addr));
+ req_desc.MFAIo.RequestFlags =
(MEGASAS_REQ_DESCRIPT_FLAGS_MFA <<
- MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
/*
* disable the intr before firing the init frame
*/
- instance->instancet->disable_intr(instance->reg_set);
+ instance->instancet->disable_intr(instance);
for (i = 0; i < (10 * 1000); i += 20) {
- if (readl(&instance->reg_set->doorbell) & 1)
+ if (megasas_readl(instance, &instance->reg_set->doorbell) & 1)
msleep(20);
else
break;
}
- instance->instancet->fire_cmd(instance, req_desc->u.low,
- req_desc->u.high, instance->reg_set);
+ /* For AERO also, IOC_INIT requires 64 bit descriptor write */
+ megasas_write_64bit_req_desc(instance, &req_desc);
- wait_and_poll(instance, cmd);
+ wait_and_poll(instance, cmd, MFI_IO_TIMEOUT_SECS);
frame_hdr = &cmd->frame->hdr;
if (frame_hdr->cmd_status != 0) {
ret = 1;
goto fail_fw_init;
}
- printk(KERN_ERR "megasas:IOC Init cmd success\n");
- ret = 0;
+ if (instance->adapter_type >= AERO_SERIES) {
+ scratch_pad_1 = megasas_readl
+ (instance, &instance->reg_set->outbound_scratch_pad_1);
+
+ instance->atomic_desc_support =
+ (scratch_pad_1 & MR_ATOMIC_DESCRIPTOR_SUPPORT_OFFSET) ? 1 : 0;
+
+ dev_info(&instance->pdev->dev, "FW supports atomic descriptor\t: %s\n",
+ instance->atomic_desc_support ? "Yes" : "No");
+ }
+
+ return 0;
fail_fw_init:
+ dev_err(&instance->pdev->dev,
+ "Init cmd return status FAILED for SCSI host %d\n",
+ instance->host->host_no);
+
+ return ret;
+}
+
+/**
+ * megasas_sync_pd_seq_num - JBOD SEQ MAP
+ * @instance: Adapter soft state
+ * @pend: set to 1, if it is pended jbod map.
+ *
+ * Issue Jbod map to the firmware. If it is pended command,
+ * issue command and return. If it is first instance of jbod map
+ * issue and receive command.
+ */
+int
+megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend) {
+ int ret = 0;
+ size_t pd_seq_map_sz;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct fusion_context *fusion = instance->ctrl_context;
+ struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync;
+ dma_addr_t pd_seq_h;
+
+ pd_sync = (void *)fusion->pd_seq_sync[(instance->pd_seq_map_id & 1)];
+ pd_seq_h = fusion->pd_seq_phys[(instance->pd_seq_map_id & 1)];
+ pd_seq_map_sz = struct_size(pd_sync, seq, MAX_PHYSICAL_DEVICES);
+
+ cmd = megasas_get_cmd(instance);
+ if (!cmd) {
+ dev_err(&instance->pdev->dev,
+ "Could not get mfi cmd. Fail from %s %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset(pd_sync, 0, pd_seq_map_sz);
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ if (pend) {
+ dcmd->mbox.b[0] = MEGASAS_DCMD_MBOX_PEND_FLAG;
+ dcmd->flags = MFI_FRAME_DIR_WRITE;
+ instance->jbod_seq_cmd = cmd;
+ } else {
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ }
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0xFF;
+ dcmd->sge_count = 1;
+ dcmd->timeout = 0;
+ dcmd->pad_0 = 0;
+ dcmd->data_xfer_len = cpu_to_le32(pd_seq_map_sz);
+ dcmd->opcode = cpu_to_le32(MR_DCMD_SYSTEM_PD_MAP_GET_INFO);
+
+ megasas_set_dma_settings(instance, dcmd, pd_seq_h, pd_seq_map_sz);
+
+ if (pend) {
+ instance->instancet->issue_dcmd(instance, cmd);
+ return 0;
+ }
+
+ /* Below code is only for non pended DCMD */
+ if (!instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance, cmd,
+ MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
+
+ if (le32_to_cpu(pd_sync->count) > MAX_PHYSICAL_DEVICES) {
+ dev_warn(&instance->pdev->dev,
+ "driver supports max %d JBOD, but FW reports %d\n",
+ MAX_PHYSICAL_DEVICES, le32_to_cpu(pd_sync->count));
+ ret = -EINVAL;
+ }
+
+ if (ret == DCMD_TIMEOUT)
+ dev_warn(&instance->pdev->dev,
+ "%s DCMD timed out, continue without JBOD sequence map\n",
+ __func__);
+
+ if (ret == DCMD_SUCCESS)
+ instance->pd_seq_map_id++;
+
megasas_return_cmd(instance, cmd);
- if (IOCInitMessage)
- dma_free_coherent(&instance->pdev->dev,
- sizeof(struct MPI2_IOC_INIT_REQUEST),
- IOCInitMessage, ioc_init_handle);
-fail_get_cmd:
return ret;
}
@@ -702,6 +1384,13 @@ fail_get_cmd:
* Issues an internal command (DCMD) to get the FW's controller PD
* list structure. This information is mainly used to find out SYSTEM
* supported by the FW.
+ * dcmd.mbox value setting for MR_DCMD_LD_MAP_GET_INFO
+ * dcmd.mbox.b[0] - number of LDs being sync'd
+ * dcmd.mbox.b[1] - 0 - complete command immediately.
+ * - 1 - pend till config change
+ * dcmd.mbox.b[2] - 0 - supports max 64 lds and uses legacy MR_FW_RAID_MAP
+ * - 1 - supports max MAX_LOGICAL_DRIVES_EXT lds and
+ * uses extended struct MR_FW_RAID_MAP_EXT
*/
static int
megasas_get_ld_map_info(struct megasas_instance *instance)
@@ -709,7 +1398,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance)
int ret = 0;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
- struct MR_FW_RAID_MAP_ALL *ci;
+ void *ci;
dma_addr_t ci_h = 0;
u32 size_map_info;
struct fusion_context *fusion;
@@ -717,7 +1406,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance)
cmd = megasas_get_cmd(instance);
if (!cmd) {
- printk(KERN_DEBUG "megasas: Failed to get cmd for map info.\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get cmd for map info\n");
return -ENOMEM;
}
@@ -725,43 +1414,45 @@ megasas_get_ld_map_info(struct megasas_instance *instance)
if (!fusion) {
megasas_return_cmd(instance, cmd);
- return 1;
+ return -ENXIO;
}
dcmd = &cmd->frame->dcmd;
- size_map_info = sizeof(struct MR_FW_RAID_MAP) +
- (sizeof(struct MR_LD_SPAN_MAP) *(MAX_LOGICAL_DRIVES - 1));
+ size_map_info = fusion->current_map_sz;
- ci = fusion->ld_map[(instance->map_id & 1)];
+ ci = (void *) fusion->ld_map[(instance->map_id & 1)];
ci_h = fusion->ld_map_phys[(instance->map_id & 1)];
if (!ci) {
- printk(KERN_DEBUG "Failed to alloc mem for ld_map_info\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc mem for ld_map_info\n");
megasas_return_cmd(instance, cmd);
return -ENOMEM;
}
- memset(ci, 0, sizeof(*ci));
+ memset(ci, 0, fusion->max_map_sz);
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
-
dcmd->cmd = MFI_CMD_DCMD;
dcmd->cmd_status = 0xFF;
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = size_map_info;
- dcmd->opcode = MR_DCMD_LD_MAP_GET_INFO;
- dcmd->sgl.sge32[0].phys_addr = ci_h;
- dcmd->sgl.sge32[0].length = size_map_info;
+ dcmd->data_xfer_len = cpu_to_le32(size_map_info);
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO);
- if (!megasas_issue_polled(instance, cmd))
- ret = 0;
- else {
- printk(KERN_ERR "megasas: Get LD Map Info Failed\n");
- ret = -1;
- }
+ megasas_set_dma_settings(instance, dcmd, ci_h, size_map_info);
+
+ if (!instance->mask_interrupts)
+ ret = megasas_issue_blocked_cmd(instance, cmd,
+ MFI_IO_TIMEOUT_SECS);
+ else
+ ret = megasas_issue_polled(instance, cmd);
+
+ if (ret == DCMD_TIMEOUT)
+ dev_warn(&instance->pdev->dev,
+ "%s DCMD timed out, RAID map is disabled\n",
+ __func__);
megasas_return_cmd(instance, cmd);
@@ -775,8 +1466,7 @@ megasas_get_map_info(struct megasas_instance *instance)
fusion->fast_path_io = 0;
if (!megasas_get_ld_map_info(instance)) {
- if (MR_ValidateMapInfo(fusion->ld_map[(instance->map_id & 1)],
- fusion->load_balance_info)) {
+ if (MR_ValidateMapInfo(instance, instance->map_id)) {
fusion->fast_path_io = 1;
return 0;
}
@@ -795,13 +1485,13 @@ megasas_get_map_info(struct megasas_instance *instance)
int
megasas_sync_map_info(struct megasas_instance *instance)
{
- int ret = 0, i;
+ int i;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
- u32 size_sync_info, num_lds;
+ u16 num_lds;
struct fusion_context *fusion;
struct MR_LD_TARGET_SYNC *ci = NULL;
- struct MR_FW_RAID_MAP_ALL *map;
+ struct MR_DRV_RAID_MAP_ALL *map;
struct MR_LD_RAID *raid;
struct MR_LD_TARGET_SYNC *ld_sync;
dma_addr_t ci_h = 0;
@@ -810,8 +1500,7 @@ megasas_sync_map_info(struct megasas_instance *instance)
cmd = megasas_get_cmd(instance);
if (!cmd) {
- printk(KERN_DEBUG "megasas: Failed to get cmd for sync"
- "info.\n");
+ dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get cmd for sync info\n");
return -ENOMEM;
}
@@ -822,19 +1511,17 @@ megasas_sync_map_info(struct megasas_instance *instance)
return 1;
}
- map = fusion->ld_map[instance->map_id & 1];
+ map = fusion->ld_drv_map[instance->map_id & 1];
- num_lds = map->raidMap.ldCount;
+ num_lds = le16_to_cpu(map->raidMap.ldCount);
dcmd = &cmd->frame->dcmd;
- size_sync_info = sizeof(struct MR_LD_TARGET_SYNC) *num_lds;
-
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
ci = (struct MR_LD_TARGET_SYNC *)
fusion->ld_map[(instance->map_id - 1) & 1];
- memset(ci, 0, sizeof(struct MR_FW_RAID_MAP_ALL));
+ memset(ci, 0, fusion->max_map_sz);
ci_h = fusion->ld_map_phys[(instance->map_id - 1) & 1];
@@ -846,8 +1533,7 @@ megasas_sync_map_info(struct megasas_instance *instance)
ld_sync->seqNum = raid->seqNum;
}
- size_map_info = sizeof(struct MR_FW_RAID_MAP) +
- (sizeof(struct MR_LD_SPAN_MAP) *(MAX_LOGICAL_DRIVES - 1));
+ size_map_info = fusion->current_map_sz;
dcmd->cmd = MFI_CMD_DCMD;
dcmd->cmd_status = 0xFF;
@@ -855,75 +1541,312 @@ megasas_sync_map_info(struct megasas_instance *instance)
dcmd->flags = MFI_FRAME_DIR_WRITE;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = size_map_info;
+ dcmd->data_xfer_len = cpu_to_le32(size_map_info);
dcmd->mbox.b[0] = num_lds;
dcmd->mbox.b[1] = MEGASAS_DCMD_MBOX_PEND_FLAG;
- dcmd->opcode = MR_DCMD_LD_MAP_GET_INFO;
- dcmd->sgl.sge32[0].phys_addr = ci_h;
- dcmd->sgl.sge32[0].length = size_map_info;
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO);
+
+ megasas_set_dma_settings(instance, dcmd, ci_h, size_map_info);
instance->map_update_cmd = cmd;
instance->instancet->issue_dcmd(instance, cmd);
- return ret;
+ return 0;
+}
+
+/*
+ * meagasas_display_intel_branding - Display branding string
+ * @instance: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+megasas_display_intel_branding(struct megasas_instance *instance)
+{
+ if (instance->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL)
+ return;
+
+ switch (instance->pdev->device) {
+ case PCI_DEVICE_ID_LSI_INVADER:
+ switch (instance->pdev->subsystem_device) {
+ case MEGARAID_INTEL_RS3DC080_SSDID:
+ dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
+ instance->host->host_no,
+ MEGARAID_INTEL_RS3DC080_BRANDING);
+ break;
+ case MEGARAID_INTEL_RS3DC040_SSDID:
+ dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
+ instance->host->host_no,
+ MEGARAID_INTEL_RS3DC040_BRANDING);
+ break;
+ case MEGARAID_INTEL_RS3SC008_SSDID:
+ dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
+ instance->host->host_no,
+ MEGARAID_INTEL_RS3SC008_BRANDING);
+ break;
+ case MEGARAID_INTEL_RS3MC044_SSDID:
+ dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
+ instance->host->host_no,
+ MEGARAID_INTEL_RS3MC044_BRANDING);
+ break;
+ default:
+ break;
+ }
+ break;
+ case PCI_DEVICE_ID_LSI_FURY:
+ switch (instance->pdev->subsystem_device) {
+ case MEGARAID_INTEL_RS3WC080_SSDID:
+ dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
+ instance->host->host_no,
+ MEGARAID_INTEL_RS3WC080_BRANDING);
+ break;
+ case MEGARAID_INTEL_RS3WC040_SSDID:
+ dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
+ instance->host->host_no,
+ MEGARAID_INTEL_RS3WC040_BRANDING);
+ break;
+ default:
+ break;
+ }
+ break;
+ case PCI_DEVICE_ID_LSI_CUTLASS_52:
+ case PCI_DEVICE_ID_LSI_CUTLASS_53:
+ switch (instance->pdev->subsystem_device) {
+ case MEGARAID_INTEL_RMS3BC160_SSDID:
+ dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
+ instance->host->host_no,
+ MEGARAID_INTEL_RMS3BC160_BRANDING);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
}
/**
- * megasas_init_adapter_fusion - Initializes the FW
- * @instance: Adapter soft state
+ * megasas_allocate_raid_maps - Allocate memory for RAID maps
+ * @instance: Adapter soft state
*
- * This is the main function for initializing firmware.
+ * return: if success: return 0
+ * failed: return -ENOMEM
*/
-u32
-megasas_init_adapter_fusion(struct megasas_instance *instance)
+static inline int megasas_allocate_raid_maps(struct megasas_instance *instance)
{
- struct megasas_register_set __iomem *reg_set;
struct fusion_context *fusion;
- u32 max_cmd;
int i = 0;
fusion = instance->ctrl_context;
- reg_set = instance->reg_set;
+ fusion->drv_map_pages = get_order(fusion->drv_map_sz);
- /*
- * Get various operational parameters from status register
- */
- instance->max_fw_cmds =
- instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF;
- instance->max_fw_cmds = min(instance->max_fw_cmds, (u16)1008);
+ for (i = 0; i < 2; i++) {
+ fusion->ld_map[i] = NULL;
- /*
- * Reduce the max supported cmds by 1. This is to ensure that the
- * reply_q_sz (1 more than the max cmd that driver may send)
- * does not exceed max cmds that the FW can support
- */
- instance->max_fw_cmds = instance->max_fw_cmds-1;
- /* Only internal cmds (DCMD) need to have MFI frames */
- instance->max_mfi_cmds = MEGASAS_INT_CMDS;
+ fusion->ld_drv_map[i] = (void *)
+ __get_free_pages(__GFP_ZERO | GFP_KERNEL,
+ fusion->drv_map_pages);
+
+ if (!fusion->ld_drv_map[i]) {
+ fusion->ld_drv_map[i] = vzalloc(fusion->drv_map_sz);
+
+ if (!fusion->ld_drv_map[i]) {
+ dev_err(&instance->pdev->dev,
+ "Could not allocate memory for local map"
+ " size requested: %d\n",
+ fusion->drv_map_sz);
+ goto ld_drv_map_alloc_fail;
+ }
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ fusion->ld_map[i] = dma_alloc_coherent(&instance->pdev->dev,
+ fusion->max_map_sz,
+ &fusion->ld_map_phys[i],
+ GFP_KERNEL);
+ if (!fusion->ld_map[i]) {
+ dev_err(&instance->pdev->dev,
+ "Could not allocate memory for map info %s:%d\n",
+ __func__, __LINE__);
+ goto ld_map_alloc_fail;
+ }
+ }
+
+ return 0;
+
+ld_map_alloc_fail:
+ for (i = 0; i < 2; i++) {
+ if (fusion->ld_map[i])
+ dma_free_coherent(&instance->pdev->dev,
+ fusion->max_map_sz,
+ fusion->ld_map[i],
+ fusion->ld_map_phys[i]);
+ }
+
+ld_drv_map_alloc_fail:
+ for (i = 0; i < 2; i++) {
+ if (fusion->ld_drv_map[i]) {
+ if (is_vmalloc_addr(fusion->ld_drv_map[i]))
+ vfree(fusion->ld_drv_map[i]);
+ else
+ free_pages((ulong)fusion->ld_drv_map[i],
+ fusion->drv_map_pages);
+ }
+ }
+
+ return -ENOMEM;
+}
+/**
+ * megasas_configure_queue_sizes - Calculate size of request desc queue,
+ * reply desc queue,
+ * IO request frame queue, set can_queue.
+ * @instance: Adapter soft state
+ * @return: void
+ */
+static inline
+void megasas_configure_queue_sizes(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion;
+ u16 max_cmd;
+
+ fusion = instance->ctrl_context;
max_cmd = instance->max_fw_cmds;
- fusion->reply_q_depth = ((max_cmd + 1 + 15)/16)*16;
+ if (instance->adapter_type >= VENTURA_SERIES)
+ instance->max_mpt_cmds = instance->max_fw_cmds * RAID_1_PEER_CMDS;
+ else
+ instance->max_mpt_cmds = instance->max_fw_cmds;
+
+ instance->max_scsi_cmds = instance->max_fw_cmds - instance->max_mfi_cmds;
+ instance->cur_can_queue = instance->max_scsi_cmds;
+ instance->host->can_queue = instance->cur_can_queue;
+
+ fusion->reply_q_depth = 2 * ((max_cmd + 1 + 15) / 16) * 16;
- fusion->request_alloc_sz =
- sizeof(union MEGASAS_REQUEST_DESCRIPTOR_UNION) *max_cmd;
- fusion->reply_alloc_sz = sizeof(union MPI2_REPLY_DESCRIPTORS_UNION)
- *(fusion->reply_q_depth);
+ fusion->request_alloc_sz = sizeof(union MEGASAS_REQUEST_DESCRIPTOR_UNION) *
+ instance->max_mpt_cmds;
+ fusion->reply_alloc_sz = sizeof(union MPI2_REPLY_DESCRIPTORS_UNION) *
+ (fusion->reply_q_depth);
fusion->io_frames_alloc_sz = MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE +
- (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE *
- (max_cmd + 1)); /* Extra 1 for SMID 0 */
+ (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE
+ * (instance->max_mpt_cmds + 1)); /* Extra 1 for SMID 0 */
+}
+
+static int megasas_alloc_ioc_init_frame(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion;
+ struct megasas_cmd *cmd;
+
+ fusion = instance->ctrl_context;
+
+ cmd = kzalloc(sizeof(struct megasas_cmd), GFP_KERNEL);
+
+ if (!cmd) {
+ dev_err(&instance->pdev->dev, "Failed from func: %s line: %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ cmd->frame = dma_alloc_coherent(&instance->pdev->dev,
+ IOC_INIT_FRAME_SIZE,
+ &cmd->frame_phys_addr, GFP_KERNEL);
+
+ if (!cmd->frame) {
+ dev_err(&instance->pdev->dev, "Failed from func: %s line: %d\n",
+ __func__, __LINE__);
+ kfree(cmd);
+ return -ENOMEM;
+ }
+
+ fusion->ioc_init_cmd = cmd;
+ return 0;
+}
+
+/**
+ * megasas_free_ioc_init_cmd - Free IOC INIT command frame
+ * @instance: Adapter soft state
+ */
+static inline void megasas_free_ioc_init_cmd(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion;
+
+ fusion = instance->ctrl_context;
+
+ if (fusion->ioc_init_cmd && fusion->ioc_init_cmd->frame)
+ dma_free_coherent(&instance->pdev->dev,
+ IOC_INIT_FRAME_SIZE,
+ fusion->ioc_init_cmd->frame,
+ fusion->ioc_init_cmd->frame_phys_addr);
+
+ kfree(fusion->ioc_init_cmd);
+}
+
+/**
+ * megasas_init_adapter_fusion - Initializes the FW
+ * @instance: Adapter soft state
+ *
+ * This is the main function for initializing firmware.
+ */
+static u32
+megasas_init_adapter_fusion(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion;
+ u32 scratch_pad_1;
+ int i = 0, count;
+ u32 status_reg;
+
+ fusion = instance->ctrl_context;
+
+ megasas_fusion_update_can_queue(instance, PROBE_CONTEXT);
+
+ /*
+ * Only Driver's internal DCMDs and IOCTL DCMDs needs to have MFI frames
+ */
+ instance->max_mfi_cmds =
+ MEGASAS_FUSION_INTERNAL_CMDS + MEGASAS_FUSION_IOCTL_CMDS;
+
+ megasas_configure_queue_sizes(instance);
+
+ scratch_pad_1 = megasas_readl(instance,
+ &instance->reg_set->outbound_scratch_pad_1);
+ /* If scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK is set,
+ * Firmware support extended IO chain frame which is 4 times more than
+ * legacy Firmware.
+ * Legacy Firmware - Frame size is (8 * 128) = 1K
+ * 1M IO Firmware - Frame size is (8 * 128 * 4) = 4K
+ */
+ if (scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK)
+ instance->max_chain_frame_sz =
+ ((scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_MASK) >>
+ MEGASAS_MAX_CHAIN_SHIFT) * MEGASAS_1MB_IO;
+ else
+ instance->max_chain_frame_sz =
+ ((scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_MASK) >>
+ MEGASAS_MAX_CHAIN_SHIFT) * MEGASAS_256K_IO;
+
+ if (instance->max_chain_frame_sz < MEGASAS_CHAIN_FRAME_SZ_MIN) {
+ dev_warn(&instance->pdev->dev, "frame size %d invalid, fall back to legacy max frame size %d\n",
+ instance->max_chain_frame_sz,
+ MEGASAS_CHAIN_FRAME_SZ_MIN);
+ instance->max_chain_frame_sz = MEGASAS_CHAIN_FRAME_SZ_MIN;
+ }
fusion->max_sge_in_main_msg =
- (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE -
- offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL))/16;
+ (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE
+ - offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL))/16;
fusion->max_sge_in_chain =
- MEGASAS_MAX_SZ_CHAIN_FRAME / sizeof(union MPI2_SGE_IO_UNION);
+ instance->max_chain_frame_sz
+ / sizeof(union MPI2_SGE_IO_UNION);
- instance->max_num_sge = fusion->max_sge_in_main_msg +
- fusion->max_sge_in_chain - 2;
+ instance->max_num_sge =
+ rounddown_pow_of_two(fusion->max_sge_in_main_msg
+ + fusion->max_sge_in_chain - 2);
/* Used for pass thru MFI frame (DCMD) */
fusion->chain_offset_mfi_pthru =
@@ -933,7 +1856,26 @@ megasas_init_adapter_fusion(struct megasas_instance *instance)
(MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE -
sizeof(union MPI2_SGE_IO_UNION))/16;
- fusion->last_reply_idx = 0;
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+ count += instance->iopoll_q_count;
+
+ for (i = 0 ; i < count; i++)
+ fusion->last_reply_idx[i] = 0;
+
+ /*
+ * For fusion adapters, 3 commands for IOCTL and 8 commands
+ * for driver's internal DCMDs.
+ */
+ instance->max_scsi_cmds = instance->max_fw_cmds -
+ (MEGASAS_FUSION_INTERNAL_CMDS +
+ MEGASAS_FUSION_IOCTL_CMDS);
+ sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS);
+
+ for (i = 0; i < MAX_MSIX_QUEUES_FUSION; i++)
+ atomic_set(&fusion->busy_mq_poll[i], 0);
+
+ if (megasas_alloc_ioc_init_frame(instance))
+ return 1;
/*
* Allocate memory for descriptors
@@ -944,196 +1886,511 @@ megasas_init_adapter_fusion(struct megasas_instance *instance)
if (megasas_alloc_cmds_fusion(instance))
goto fail_alloc_cmds;
- if (megasas_ioc_init_fusion(instance))
+ if (megasas_ioc_init_fusion(instance)) {
+ status_reg = instance->instancet->read_fw_status_reg(instance);
+ if (((status_reg & MFI_STATE_MASK) == MFI_STATE_FAULT) &&
+ (status_reg & MFI_RESET_ADAPTER)) {
+ /* Do a chip reset and then retry IOC INIT once */
+ if (megasas_adp_reset_wait_for_ready
+ (instance, true, 0) == FAILED)
+ goto fail_ioc_init;
+
+ if (megasas_ioc_init_fusion(instance))
+ goto fail_ioc_init;
+ } else {
+ goto fail_ioc_init;
+ }
+ }
+
+ megasas_display_intel_branding(instance);
+ if (megasas_get_ctrl_info(instance)) {
+ dev_err(&instance->pdev->dev,
+ "Could not get controller info. Fail from %s %d\n",
+ __func__, __LINE__);
goto fail_ioc_init;
+ }
instance->flag_ieee = 1;
-
- fusion->map_sz = sizeof(struct MR_FW_RAID_MAP) +
- (sizeof(struct MR_LD_SPAN_MAP) *(MAX_LOGICAL_DRIVES - 1));
-
+ instance->r1_ldio_hint_default = MR_R1_LDIO_PIGGYBACK_DEFAULT;
+ instance->threshold_reply_count = instance->max_fw_cmds / 4;
fusion->fast_path_io = 0;
- for (i = 0; i < 2; i++) {
- fusion->ld_map[i] = dma_alloc_coherent(&instance->pdev->dev,
- fusion->map_sz,
- &fusion->ld_map_phys[i],
- GFP_KERNEL);
- if (!fusion->ld_map[i]) {
- printk(KERN_ERR "megasas: Could not allocate memory "
- "for map info\n");
- goto fail_map_info;
- }
- }
+ if (megasas_allocate_raid_maps(instance))
+ goto fail_ioc_init;
if (!megasas_get_map_info(instance))
megasas_sync_map_info(instance);
return 0;
-fail_map_info:
- if (i == 1)
- dma_free_coherent(&instance->pdev->dev, fusion->map_sz,
- fusion->ld_map[0], fusion->ld_map_phys[0]);
fail_ioc_init:
megasas_free_cmds_fusion(instance);
fail_alloc_cmds:
megasas_free_cmds(instance);
fail_alloc_mfi_cmds:
+ megasas_free_ioc_init_cmd(instance);
return 1;
}
/**
- * megasas_fire_cmd_fusion - Sends command to the FW
- * @frame_phys_addr : Physical address of cmd
- * @frame_count : Number of frames for the command
- * @regs : MFI register set
+ * megasas_fault_detect_work - Worker function of
+ * FW fault handling workqueue.
+ * @work: FW fault work struct
*/
-void
-megasas_fire_cmd_fusion(struct megasas_instance *instance,
- dma_addr_t req_desc_lo,
- u32 req_desc_hi,
- struct megasas_register_set __iomem *regs)
+static void
+megasas_fault_detect_work(struct work_struct *work)
{
- unsigned long flags;
+ struct megasas_instance *instance =
+ container_of(work, struct megasas_instance,
+ fw_fault_work.work);
+ u32 fw_state, dma_state, status;
+
+ /* Check the fw state */
+ fw_state = instance->instancet->read_fw_status_reg(instance) &
+ MFI_STATE_MASK;
+
+ if (fw_state == MFI_STATE_FAULT) {
+ dma_state = instance->instancet->read_fw_status_reg(instance) &
+ MFI_STATE_DMADONE;
+ /* Start collecting crash, if DMA bit is done */
+ if (instance->crash_dump_drv_support &&
+ instance->crash_dump_app_support && dma_state) {
+ megasas_fusion_crash_dump(instance);
+ } else {
+ if (instance->unload == 0) {
+ status = megasas_reset_fusion(instance->host, 0);
+ if (status != SUCCESS) {
+ dev_err(&instance->pdev->dev,
+ "Failed from %s %d, do not re-arm timer\n",
+ __func__, __LINE__);
+ return;
+ }
+ }
+ }
+ }
- spin_lock_irqsave(&instance->hba_lock, flags);
+ if (instance->fw_fault_work_q)
+ queue_delayed_work(instance->fw_fault_work_q,
+ &instance->fw_fault_work,
+ msecs_to_jiffies(MEGASAS_WATCHDOG_THREAD_INTERVAL));
+}
- writel(req_desc_lo,
- &(regs)->inbound_low_queue_port);
- writel(req_desc_hi, &(regs)->inbound_high_queue_port);
- spin_unlock_irqrestore(&instance->hba_lock, flags);
+int
+megasas_fusion_start_watchdog(struct megasas_instance *instance)
+{
+ /* Check if the Fault WQ is already started */
+ if (instance->fw_fault_work_q)
+ return SUCCESS;
+
+ INIT_DELAYED_WORK(&instance->fw_fault_work, megasas_fault_detect_work);
+
+ snprintf(instance->fault_handler_work_q_name,
+ sizeof(instance->fault_handler_work_q_name),
+ "poll_megasas%d_status", instance->host->host_no);
+
+ instance->fw_fault_work_q = alloc_ordered_workqueue(
+ "%s", WQ_MEM_RECLAIM, instance->fault_handler_work_q_name);
+ if (!instance->fw_fault_work_q) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ return FAILED;
+ }
+
+ queue_delayed_work(instance->fw_fault_work_q,
+ &instance->fw_fault_work,
+ msecs_to_jiffies(MEGASAS_WATCHDOG_THREAD_INTERVAL));
+
+ return SUCCESS;
+}
+
+void
+megasas_fusion_stop_watchdog(struct megasas_instance *instance)
+{
+ struct workqueue_struct *wq;
+
+ if (instance->fw_fault_work_q) {
+ wq = instance->fw_fault_work_q;
+ instance->fw_fault_work_q = NULL;
+ if (!cancel_delayed_work_sync(&instance->fw_fault_work))
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ }
}
/**
* map_cmd_status - Maps FW cmd status to OS cmd status
- * @cmd : Pointer to cmd
- * @status : status of cmd returned by FW
- * @ext_status : ext status of cmd returned by FW
+ * @fusion: fusion context
+ * @scmd: Pointer to cmd
+ * @status: status of cmd returned by FW
+ * @ext_status: ext status of cmd returned by FW
+ * @data_length: command data length
+ * @sense: command sense data
*/
-
-void
-map_cmd_status(struct megasas_cmd_fusion *cmd, u8 status, u8 ext_status)
+static void
+map_cmd_status(struct fusion_context *fusion,
+ struct scsi_cmnd *scmd, u8 status, u8 ext_status,
+ u32 data_length, u8 *sense)
{
+ u8 cmd_type;
+ int resid;
+ cmd_type = megasas_cmd_type(scmd);
switch (status) {
case MFI_STAT_OK:
- cmd->scmd->result = DID_OK << 16;
+ scmd->result = DID_OK << 16;
break;
case MFI_STAT_SCSI_IO_FAILED:
case MFI_STAT_LD_INIT_IN_PROGRESS:
- cmd->scmd->result = (DID_ERROR << 16) | ext_status;
+ if (ext_status == 0xf0)
+ scmd->result = (DID_ERROR << 16) | SAM_STAT_CHECK_CONDITION;
+ else
+ scmd->result = (DID_ERROR << 16) | ext_status;
break;
case MFI_STAT_SCSI_DONE_WITH_ERROR:
- cmd->scmd->result = (DID_OK << 16) | ext_status;
+ scmd->result = (DID_OK << 16) | ext_status;
if (ext_status == SAM_STAT_CHECK_CONDITION) {
- memset(cmd->scmd->sense_buffer, 0,
+ memcpy(scmd->sense_buffer, sense,
SCSI_SENSE_BUFFERSIZE);
- memcpy(cmd->scmd->sense_buffer, cmd->sense,
- SCSI_SENSE_BUFFERSIZE);
- cmd->scmd->result |= DRIVER_SENSE << 24;
}
+
+ /*
+ * If the IO request is partially completed, then MR FW will
+ * update "io_request->DataLength" field with actual number of
+ * bytes transferred.Driver will set residual bytes count in
+ * SCSI command structure.
+ */
+ resid = (scsi_bufflen(scmd) - data_length);
+ scsi_set_resid(scmd, resid);
+
+ if (resid &&
+ ((cmd_type == READ_WRITE_LDIO) ||
+ (cmd_type == READ_WRITE_SYSPDIO)))
+ scmd_printk(KERN_INFO, scmd, "BRCM Debug mfi stat 0x%x, data len"
+ " requested/completed 0x%x/0x%x\n",
+ status, scsi_bufflen(scmd), data_length);
break;
case MFI_STAT_LD_OFFLINE:
case MFI_STAT_DEVICE_NOT_FOUND:
- cmd->scmd->result = DID_BAD_TARGET << 16;
+ scmd->result = DID_BAD_TARGET << 16;
+ break;
+ case MFI_STAT_CONFIG_SEQ_MISMATCH:
+ scmd->result = DID_IMM_RETRY << 16;
break;
-
default:
- printk(KERN_DEBUG "megasas: FW status %#x\n", status);
- cmd->scmd->result = DID_ERROR << 16;
+ scmd->result = DID_ERROR << 16;
break;
}
}
/**
+ * megasas_is_prp_possible -
+ * Checks if native NVMe PRPs can be built for the IO
+ *
+ * @instance: Adapter soft state
+ * @scmd: SCSI command from the mid-layer
+ * @sge_count: scatter gather element count.
+ *
+ * Returns: true: PRPs can be built
+ * false: IEEE SGLs needs to be built
+ */
+static bool
+megasas_is_prp_possible(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd, int sge_count)
+{
+ u32 data_length = 0;
+ struct scatterlist *sg_scmd;
+ bool build_prp = false;
+ u32 mr_nvme_pg_size;
+
+ mr_nvme_pg_size = max_t(u32, instance->nvme_page_size,
+ MR_DEFAULT_NVME_PAGE_SIZE);
+ data_length = scsi_bufflen(scmd);
+ sg_scmd = scsi_sglist(scmd);
+
+ /*
+ * NVMe uses one PRP for each page (or part of a page)
+ * look at the data length - if 4 pages or less then IEEE is OK
+ * if > 5 pages then we need to build a native SGL
+ * if > 4 and <= 5 pages, then check physical address of 1st SG entry
+ * if this first size in the page is >= the residual beyond 4 pages
+ * then use IEEE, otherwise use native SGL
+ */
+
+ if (data_length > (mr_nvme_pg_size * 5)) {
+ build_prp = true;
+ } else if ((data_length > (mr_nvme_pg_size * 4)) &&
+ (data_length <= (mr_nvme_pg_size * 5))) {
+ /* check if 1st SG entry size is < residual beyond 4 pages */
+ if (sg_dma_len(sg_scmd) < (data_length - (mr_nvme_pg_size * 4)))
+ build_prp = true;
+ }
+
+ return build_prp;
+}
+
+/**
+ * megasas_make_prp_nvme -
+ * Prepare PRPs(Physical Region Page)- SGLs specific to NVMe drives only
+ *
+ * @instance: Adapter soft state
+ * @scmd: SCSI command from the mid-layer
+ * @sgl_ptr: SGL to be filled in
+ * @cmd: Fusion command frame
+ * @sge_count: scatter gather element count.
+ *
+ * Returns: true: PRPs are built
+ * false: IEEE SGLs needs to be built
+ */
+static bool
+megasas_make_prp_nvme(struct megasas_instance *instance, struct scsi_cmnd *scmd,
+ struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr,
+ struct megasas_cmd_fusion *cmd, int sge_count)
+{
+ int sge_len, offset, num_prp_in_chain = 0;
+ struct MPI25_IEEE_SGE_CHAIN64 *main_chain_element, *ptr_first_sgl;
+ u64 *ptr_sgl;
+ dma_addr_t ptr_sgl_phys;
+ u64 sge_addr;
+ u32 page_mask, page_mask_result;
+ struct scatterlist *sg_scmd;
+ u32 first_prp_len;
+ bool build_prp = false;
+ int data_len = scsi_bufflen(scmd);
+ u32 mr_nvme_pg_size = max_t(u32, instance->nvme_page_size,
+ MR_DEFAULT_NVME_PAGE_SIZE);
+
+ build_prp = megasas_is_prp_possible(instance, scmd, sge_count);
+
+ if (!build_prp)
+ return false;
+
+ /*
+ * Nvme has a very convoluted prp format. One prp is required
+ * for each page or partial page. Driver need to split up OS sg_list
+ * entries if it is longer than one page or cross a page
+ * boundary. Driver also have to insert a PRP list pointer entry as
+ * the last entry in each physical page of the PRP list.
+ *
+ * NOTE: The first PRP "entry" is actually placed in the first
+ * SGL entry in the main message as IEEE 64 format. The 2nd
+ * entry in the main message is the chain element, and the rest
+ * of the PRP entries are built in the contiguous pcie buffer.
+ */
+ page_mask = mr_nvme_pg_size - 1;
+ ptr_sgl = (u64 *)cmd->sg_frame;
+ ptr_sgl_phys = cmd->sg_frame_phys_addr;
+ memset(ptr_sgl, 0, instance->max_chain_frame_sz);
+
+ /* Build chain frame element which holds all prps except first*/
+ main_chain_element = (struct MPI25_IEEE_SGE_CHAIN64 *)
+ ((u8 *)sgl_ptr + sizeof(struct MPI25_IEEE_SGE_CHAIN64));
+
+ main_chain_element->Address = cpu_to_le64(ptr_sgl_phys);
+ main_chain_element->NextChainOffset = 0;
+ main_chain_element->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT |
+ IEEE_SGE_FLAGS_SYSTEM_ADDR |
+ MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP;
+
+ /* Build first prp, sge need not to be page aligned*/
+ ptr_first_sgl = sgl_ptr;
+ sg_scmd = scsi_sglist(scmd);
+ sge_addr = sg_dma_address(sg_scmd);
+ sge_len = sg_dma_len(sg_scmd);
+
+ offset = (u32)(sge_addr & page_mask);
+ first_prp_len = mr_nvme_pg_size - offset;
+
+ ptr_first_sgl->Address = cpu_to_le64(sge_addr);
+ ptr_first_sgl->Length = cpu_to_le32(first_prp_len);
+
+ data_len -= first_prp_len;
+
+ if (sge_len > first_prp_len) {
+ sge_addr += first_prp_len;
+ sge_len -= first_prp_len;
+ } else if (sge_len == first_prp_len) {
+ sg_scmd = sg_next(sg_scmd);
+ sge_addr = sg_dma_address(sg_scmd);
+ sge_len = sg_dma_len(sg_scmd);
+ }
+
+ for (;;) {
+ offset = (u32)(sge_addr & page_mask);
+
+ /* Put PRP pointer due to page boundary*/
+ page_mask_result = (uintptr_t)(ptr_sgl + 1) & page_mask;
+ if (unlikely(!page_mask_result)) {
+ scmd_printk(KERN_NOTICE,
+ scmd, "page boundary ptr_sgl: 0x%p\n",
+ ptr_sgl);
+ ptr_sgl_phys += 8;
+ *ptr_sgl = cpu_to_le64(ptr_sgl_phys);
+ ptr_sgl++;
+ num_prp_in_chain++;
+ }
+
+ *ptr_sgl = cpu_to_le64(sge_addr);
+ ptr_sgl++;
+ ptr_sgl_phys += 8;
+ num_prp_in_chain++;
+
+ sge_addr += mr_nvme_pg_size;
+ sge_len -= mr_nvme_pg_size;
+ data_len -= mr_nvme_pg_size;
+
+ if (data_len <= 0)
+ break;
+
+ if (sge_len > 0)
+ continue;
+
+ sg_scmd = sg_next(sg_scmd);
+ sge_addr = sg_dma_address(sg_scmd);
+ sge_len = sg_dma_len(sg_scmd);
+ }
+
+ main_chain_element->Length =
+ cpu_to_le32(num_prp_in_chain * sizeof(u64));
+
+ return build_prp;
+}
+
+/**
* megasas_make_sgl_fusion - Prepares 32-bit SGL
* @instance: Adapter soft state
* @scp: SCSI command from the mid-layer
* @sgl_ptr: SGL to be filled in
* @cmd: cmd we are working on
+ * @sge_count: sge count
*
- * If successful, this function returns the number of SG elements.
*/
-static int
+static void
megasas_make_sgl_fusion(struct megasas_instance *instance,
struct scsi_cmnd *scp,
struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr,
- struct megasas_cmd_fusion *cmd)
+ struct megasas_cmd_fusion *cmd, int sge_count)
{
int i, sg_processed;
- int sge_count, sge_idx;
struct scatterlist *os_sgl;
struct fusion_context *fusion;
fusion = instance->ctrl_context;
- cmd->io_request->ChainOffset = 0;
-
- sge_count = scsi_dma_map(scp);
-
- BUG_ON(sge_count < 0);
-
- if (sge_count > instance->max_num_sge || !sge_count)
- return sge_count;
-
- if (sge_count > fusion->max_sge_in_main_msg) {
- /* One element to store the chain info */
- sge_idx = fusion->max_sge_in_main_msg - 1;
- } else
- sge_idx = sge_count;
+ if (instance->adapter_type >= INVADER_SERIES) {
+ struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr_end = sgl_ptr;
+ sgl_ptr_end += fusion->max_sge_in_main_msg - 1;
+ sgl_ptr_end->Flags = 0;
+ }
scsi_for_each_sg(scp, os_sgl, sge_count, i) {
- sgl_ptr->Length = sg_dma_len(os_sgl);
- sgl_ptr->Address = sg_dma_address(os_sgl);
+ sgl_ptr->Length = cpu_to_le32(sg_dma_len(os_sgl));
+ sgl_ptr->Address = cpu_to_le64(sg_dma_address(os_sgl));
sgl_ptr->Flags = 0;
+ if (instance->adapter_type >= INVADER_SERIES)
+ if (i == sge_count - 1)
+ sgl_ptr->Flags = IEEE_SGE_FLAGS_END_OF_LIST;
sgl_ptr++;
-
sg_processed = i + 1;
if ((sg_processed == (fusion->max_sge_in_main_msg - 1)) &&
(sge_count > fusion->max_sge_in_main_msg)) {
struct MPI25_IEEE_SGE_CHAIN64 *sg_chain;
- cmd->io_request->ChainOffset =
- fusion->chain_offset_io_request;
+ if (instance->adapter_type >= INVADER_SERIES) {
+ if ((le16_to_cpu(cmd->io_request->IoFlags) &
+ MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) !=
+ MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH)
+ cmd->io_request->ChainOffset =
+ fusion->
+ chain_offset_io_request;
+ else
+ cmd->io_request->ChainOffset = 0;
+ } else
+ cmd->io_request->ChainOffset =
+ fusion->chain_offset_io_request;
+
sg_chain = sgl_ptr;
/* Prepare chain element */
sg_chain->NextChainOffset = 0;
- sg_chain->Flags = (IEEE_SGE_FLAGS_CHAIN_ELEMENT |
- MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR);
- sg_chain->Length = (sizeof(union MPI2_SGE_IO_UNION)
- *(sge_count - sg_processed));
- sg_chain->Address = cmd->sg_frame_phys_addr;
+ if (instance->adapter_type >= INVADER_SERIES)
+ sg_chain->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT;
+ else
+ sg_chain->Flags =
+ (IEEE_SGE_FLAGS_CHAIN_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR);
+ sg_chain->Length = cpu_to_le32((sizeof(union MPI2_SGE_IO_UNION) * (sge_count - sg_processed)));
+ sg_chain->Address = cpu_to_le64(cmd->sg_frame_phys_addr);
sgl_ptr =
(struct MPI25_IEEE_SGE_CHAIN64 *)cmd->sg_frame;
+ memset(sgl_ptr, 0, instance->max_chain_frame_sz);
}
}
+}
+
+/**
+ * megasas_make_sgl - Build Scatter Gather List(SGLs)
+ * @scp: SCSI command pointer
+ * @instance: Soft instance of controller
+ * @cmd: Fusion command pointer
+ *
+ * This function will build sgls based on device type.
+ * For nvme drives, there is different way of building sgls in nvme native
+ * format- PRPs(Physical Region Page).
+ *
+ * Returns the number of sg lists actually used, zero if the sg lists
+ * is NULL, or -ENOMEM if the mapping failed
+ */
+static
+int megasas_make_sgl(struct megasas_instance *instance, struct scsi_cmnd *scp,
+ struct megasas_cmd_fusion *cmd)
+{
+ int sge_count;
+ bool build_prp = false;
+ struct MPI25_IEEE_SGE_CHAIN64 *sgl_chain64;
+
+ sge_count = scsi_dma_map(scp);
+
+ if ((sge_count > instance->max_num_sge) || (sge_count <= 0))
+ return sge_count;
+
+ sgl_chain64 = (struct MPI25_IEEE_SGE_CHAIN64 *)&cmd->io_request->SGL;
+ if ((le16_to_cpu(cmd->io_request->IoFlags) &
+ MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) &&
+ (cmd->pd_interface == NVME_PD))
+ build_prp = megasas_make_prp_nvme(instance, scp, sgl_chain64,
+ cmd, sge_count);
+
+ if (!build_prp)
+ megasas_make_sgl_fusion(instance, scp, sgl_chain64,
+ cmd, sge_count);
return sge_count;
}
/**
* megasas_set_pd_lba - Sets PD LBA
- * @cdb: CDB
+ * @io_request: IO request
* @cdb_len: cdb length
- * @start_blk: Start block of IO
+ * @io_info: IO information
+ * @scp: SCSI command
+ * @local_map_ptr: Raid map
+ * @ref_tag: Primary reference tag
*
* Used to set the PD LBA in CDB for FP IOs
*/
-void
+static void
megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
struct IO_REQUEST_INFO *io_info, struct scsi_cmnd *scp,
- struct MR_FW_RAID_MAP_ALL *local_map_ptr, u32 ref_tag)
+ struct MR_DRV_RAID_MAP_ALL *local_map_ptr, u32 ref_tag)
{
struct MR_LD_RAID *raid;
- u32 ld;
+ u16 ld;
u64 start_blk = io_info->pdBlock;
u8 *cdb = io_request->CDB.CDB32;
u32 num_blocks = io_info->numBlocks;
@@ -1147,7 +2404,7 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
cdb[0] = MEGASAS_SCSI_VARIABLE_LENGTH_CMD;
cdb[7] = MEGASAS_SCSI_ADDL_CDB_LEN;
- if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
+ if (scp->sc_data_direction == DMA_FROM_DEVICE)
cdb[9] = MEGASAS_SCSI_SERVICE_ACTION_READ32;
else
cdb[9] = MEGASAS_SCSI_SERVICE_ACTION_WRITE32;
@@ -1166,10 +2423,8 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
/* Logical block reference tag */
io_request->CDB.EEDP32.PrimaryReferenceTag =
cpu_to_be32(ref_tag);
- io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0xffff;
-
- io_request->DataLength = num_blocks * 512;
- io_request->IoFlags = 32; /* Specify 32-byte cdb */
+ io_request->CDB.EEDP32.PrimaryApplicationTagMask = cpu_to_be16(0xffff);
+ io_request->IoFlags = cpu_to_le16(32); /* Specify 32-byte cdb */
/* Transfer length */
cdb[28] = (u8)((num_blocks >> 24) & 0xff);
@@ -1178,20 +2433,21 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
cdb[31] = (u8)(num_blocks & 0xff);
/* set SCSI IO EEDPFlags */
- if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) {
- io_request->EEDPFlags =
+ if (scp->sc_data_direction == DMA_FROM_DEVICE) {
+ io_request->EEDPFlags = cpu_to_le16(
MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP |
MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG |
- MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+ MPI25_SCSIIO_EEDPFLAGS_DO_NOT_DISABLE_MODE |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD);
} else {
- io_request->EEDPFlags =
+ io_request->EEDPFlags = cpu_to_le16(
MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
- MPI2_SCSIIO_EEDPFLAGS_INSERT_OP;
+ MPI2_SCSIIO_EEDPFLAGS_INSERT_OP);
}
- io_request->Control |= (0x4 << 26);
- io_request->EEDPBlockSize = MEGASAS_EEDPBLOCKSIZE;
+ io_request->Control |= cpu_to_le32((0x4 << 26));
+ io_request->EEDPBlockSize = cpu_to_le32(scp->device->sector_size);
} else {
/* Some drives don't support 16/12 byte CDB's, convert to 10 */
if (((cdb_len == 12) || (cdb_len == 16)) &&
@@ -1219,7 +2475,7 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
cdb[8] = (u8)(num_blocks & 0xff);
cdb[7] = (u8)((num_blocks >> 8) & 0xff);
- io_request->IoFlags = 10; /* Specify 10-byte cdb */
+ io_request->IoFlags = cpu_to_le16(10); /* Specify 10-byte cdb */
cdb_len = 10;
} else if ((cdb_len < 16) && (start_blk > 0xffffffff)) {
/* Convert to 16 byte CDB for large LBA's */
@@ -1257,7 +2513,7 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
cdb[11] = (u8)((num_blocks >> 16) & 0xff);
cdb[10] = (u8)((num_blocks >> 24) & 0xff);
- io_request->IoFlags = 16; /* Specify 16-byte cdb */
+ io_request->IoFlags = cpu_to_le16(16); /* Specify 16-byte cdb */
cdb_len = 16;
}
@@ -1298,6 +2554,169 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
}
/**
+ * megasas_stream_detect - stream detection on read and and write IOs
+ * @instance: Adapter soft state
+ * @cmd: Command to be prepared
+ * @io_info: IO Request info
+ *
+ */
+
+/** stream detection on read and and write IOs */
+static void megasas_stream_detect(struct megasas_instance *instance,
+ struct megasas_cmd_fusion *cmd,
+ struct IO_REQUEST_INFO *io_info)
+{
+ struct fusion_context *fusion = instance->ctrl_context;
+ u32 device_id = io_info->ldTgtId;
+ struct LD_STREAM_DETECT *current_ld_sd
+ = fusion->stream_detect_by_ld[device_id];
+ u32 *track_stream = &current_ld_sd->mru_bit_map, stream_num;
+ u32 shifted_values, unshifted_values;
+ u32 index_value_mask, shifted_values_mask;
+ int i;
+ bool is_read_ahead = false;
+ struct STREAM_DETECT *current_sd;
+ /* find possible stream */
+ for (i = 0; i < MAX_STREAMS_TRACKED; ++i) {
+ stream_num = (*track_stream >>
+ (i * BITS_PER_INDEX_STREAM)) &
+ STREAM_MASK;
+ current_sd = &current_ld_sd->stream_track[stream_num];
+ /* if we found a stream, update the raid
+ * context and also update the mruBitMap
+ */
+ /* boundary condition */
+ if ((current_sd->next_seq_lba) &&
+ (io_info->ldStartBlock >= current_sd->next_seq_lba) &&
+ (io_info->ldStartBlock <= (current_sd->next_seq_lba + 32)) &&
+ (current_sd->is_read == io_info->isRead)) {
+
+ if ((io_info->ldStartBlock != current_sd->next_seq_lba) &&
+ ((!io_info->isRead) || (!is_read_ahead)))
+ /*
+ * Once the API is available we need to change this.
+ * At this point we are not allowing any gap
+ */
+ continue;
+
+ SET_STREAM_DETECTED(cmd->io_request->RaidContext.raid_context_g35);
+ current_sd->next_seq_lba =
+ io_info->ldStartBlock + io_info->numBlocks;
+ /*
+ * update the mruBitMap LRU
+ */
+ shifted_values_mask =
+ (1 << i * BITS_PER_INDEX_STREAM) - 1;
+ shifted_values = ((*track_stream & shifted_values_mask)
+ << BITS_PER_INDEX_STREAM);
+ index_value_mask =
+ STREAM_MASK << i * BITS_PER_INDEX_STREAM;
+ unshifted_values =
+ *track_stream & ~(shifted_values_mask |
+ index_value_mask);
+ *track_stream =
+ unshifted_values | shifted_values | stream_num;
+ return;
+ }
+ }
+ /*
+ * if we did not find any stream, create a new one
+ * from the least recently used
+ */
+ stream_num = (*track_stream >>
+ ((MAX_STREAMS_TRACKED - 1) * BITS_PER_INDEX_STREAM)) &
+ STREAM_MASK;
+ current_sd = &current_ld_sd->stream_track[stream_num];
+ current_sd->is_read = io_info->isRead;
+ current_sd->next_seq_lba = io_info->ldStartBlock + io_info->numBlocks;
+ *track_stream = (((*track_stream & ZERO_LAST_STREAM) << 4) | stream_num);
+ return;
+}
+
+/**
+ * megasas_set_raidflag_cpu_affinity - This function sets the cpu
+ * affinity (cpu of the controller) and raid_flags in the raid context
+ * based on IO type.
+ *
+ * @fusion: Fusion context
+ * @praid_context: IO RAID context
+ * @raid: LD raid map
+ * @fp_possible: Is fast path possible?
+ * @is_read: Is read IO?
+ * @scsi_buff_len: SCSI command buffer length
+ *
+ */
+static void
+megasas_set_raidflag_cpu_affinity(struct fusion_context *fusion,
+ union RAID_CONTEXT_UNION *praid_context,
+ struct MR_LD_RAID *raid, bool fp_possible,
+ u8 is_read, u32 scsi_buff_len)
+{
+ u8 cpu_sel = MR_RAID_CTX_CPUSEL_0;
+ struct RAID_CONTEXT_G35 *rctx_g35;
+
+ rctx_g35 = &praid_context->raid_context_g35;
+ if (fp_possible) {
+ if (is_read) {
+ if ((raid->cpuAffinity.pdRead.cpu0) &&
+ (raid->cpuAffinity.pdRead.cpu1))
+ cpu_sel = MR_RAID_CTX_CPUSEL_FCFS;
+ else if (raid->cpuAffinity.pdRead.cpu1)
+ cpu_sel = MR_RAID_CTX_CPUSEL_1;
+ } else {
+ if ((raid->cpuAffinity.pdWrite.cpu0) &&
+ (raid->cpuAffinity.pdWrite.cpu1))
+ cpu_sel = MR_RAID_CTX_CPUSEL_FCFS;
+ else if (raid->cpuAffinity.pdWrite.cpu1)
+ cpu_sel = MR_RAID_CTX_CPUSEL_1;
+ /* Fast path cache by pass capable R0/R1 VD */
+ if ((raid->level <= 1) &&
+ (raid->capability.fp_cache_bypass_capable)) {
+ rctx_g35->routing_flags |=
+ (1 << MR_RAID_CTX_ROUTINGFLAGS_SLD_SHIFT);
+ rctx_g35->raid_flags =
+ (MR_RAID_FLAGS_IO_SUB_TYPE_CACHE_BYPASS
+ << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT);
+ }
+ }
+ } else {
+ if (is_read) {
+ if ((raid->cpuAffinity.ldRead.cpu0) &&
+ (raid->cpuAffinity.ldRead.cpu1))
+ cpu_sel = MR_RAID_CTX_CPUSEL_FCFS;
+ else if (raid->cpuAffinity.ldRead.cpu1)
+ cpu_sel = MR_RAID_CTX_CPUSEL_1;
+ } else {
+ if ((raid->cpuAffinity.ldWrite.cpu0) &&
+ (raid->cpuAffinity.ldWrite.cpu1))
+ cpu_sel = MR_RAID_CTX_CPUSEL_FCFS;
+ else if (raid->cpuAffinity.ldWrite.cpu1)
+ cpu_sel = MR_RAID_CTX_CPUSEL_1;
+
+ if (is_stream_detected(rctx_g35) &&
+ ((raid->level == 5) || (raid->level == 6)) &&
+ (raid->writeMode == MR_RL_WRITE_THROUGH_MODE) &&
+ (cpu_sel == MR_RAID_CTX_CPUSEL_FCFS))
+ cpu_sel = MR_RAID_CTX_CPUSEL_0;
+ }
+ }
+
+ rctx_g35->routing_flags |=
+ (cpu_sel << MR_RAID_CTX_ROUTINGFLAGS_CPUSEL_SHIFT);
+
+ /* Always give priority to MR_RAID_FLAGS_IO_SUB_TYPE_LDIO_BW_LIMIT
+ * vs MR_RAID_FLAGS_IO_SUB_TYPE_CACHE_BYPASS.
+ * IO Subtype is not bitmap.
+ */
+ if ((fusion->pcie_bw_limitation) && (raid->level == 1) && (!is_read) &&
+ (scsi_buff_len > MR_LARGE_IO_MIN_SIZE)) {
+ praid_context->raid_context_g35.raid_flags =
+ (MR_RAID_FLAGS_IO_SUB_TYPE_LDIO_BW_LIMIT
+ << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT);
+ }
+}
+
+/**
* megasas_build_ldio_fusion - Prepares IOs to devices
* @instance: Adapter soft state
* @scp: SCSI command
@@ -1306,39 +2725,47 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
* Prepares the io_request and chain elements (sg_frame) for IO
* The IO can be for PD (Fast Path) or LD
*/
-void
+static void
megasas_build_ldio_fusion(struct megasas_instance *instance,
struct scsi_cmnd *scp,
struct megasas_cmd_fusion *cmd)
{
- u8 fp_possible;
- u32 start_lba_lo, start_lba_hi, device_id;
+ bool fp_possible;
+ u16 ld;
+ u32 start_lba_lo, start_lba_hi, device_id, datalength = 0;
+ u32 scsi_buff_len;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
- union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
struct IO_REQUEST_INFO io_info;
struct fusion_context *fusion;
- struct MR_FW_RAID_MAP_ALL *local_map_ptr;
+ struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
+ u8 *raidLUN;
+ unsigned long spinlock_flags;
+ struct MR_LD_RAID *raid = NULL;
+ struct MR_PRIV_DEVICE *mrdev_priv;
+ struct RAID_CONTEXT *rctx;
+ struct RAID_CONTEXT_G35 *rctx_g35;
- device_id = MEGASAS_DEV_INDEX(instance, scp);
+ device_id = MEGASAS_DEV_INDEX(scp);
fusion = instance->ctrl_context;
io_request = cmd->io_request;
- io_request->RaidContext.VirtualDiskTgtId = device_id;
- io_request->RaidContext.status = 0;
- io_request->RaidContext.exStatus = 0;
+ rctx = &io_request->RaidContext.raid_context;
+ rctx_g35 = &io_request->RaidContext.raid_context_g35;
- req_desc = (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)cmd->request_desc;
+ rctx->virtual_disk_tgt_id = cpu_to_le16(device_id);
+ rctx->status = 0;
+ rctx->ex_status = 0;
start_lba_lo = 0;
start_lba_hi = 0;
- fp_possible = 0;
+ fp_possible = false;
/*
* 6-byte READ(0x08) or WRITE(0x0A) cdb
*/
if (scp->cmd_len == 6) {
- io_request->DataLength = (u32) scp->cmnd[4];
+ datalength = (u32) scp->cmnd[4];
start_lba_lo = ((u32) scp->cmnd[1] << 16) |
((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3];
@@ -1349,7 +2776,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
* 10-byte READ(0x28) or WRITE(0x2A) cdb
*/
else if (scp->cmd_len == 10) {
- io_request->DataLength = (u32) scp->cmnd[8] |
+ datalength = (u32) scp->cmnd[8] |
((u32) scp->cmnd[7] << 8);
start_lba_lo = ((u32) scp->cmnd[2] << 24) |
((u32) scp->cmnd[3] << 16) |
@@ -1360,7 +2787,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
* 12-byte READ(0xA8) or WRITE(0xAA) cdb
*/
else if (scp->cmd_len == 12) {
- io_request->DataLength = ((u32) scp->cmnd[6] << 24) |
+ datalength = ((u32) scp->cmnd[6] << 24) |
((u32) scp->cmnd[7] << 16) |
((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9];
start_lba_lo = ((u32) scp->cmnd[2] << 24) |
@@ -1372,7 +2799,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
* 16-byte READ(0x88) or WRITE(0x8A) cdb
*/
else if (scp->cmd_len == 16) {
- io_request->DataLength = ((u32) scp->cmnd[10] << 24) |
+ datalength = ((u32) scp->cmnd[10] << 24) |
((u32) scp->cmnd[11] << 16) |
((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13];
start_lba_lo = ((u32) scp->cmnd[6] << 24) |
@@ -1386,105 +2813,381 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
memset(&io_info, 0, sizeof(struct IO_REQUEST_INFO));
io_info.ldStartBlock = ((u64)start_lba_hi << 32) | start_lba_lo;
- io_info.numBlocks = io_request->DataLength;
+ io_info.numBlocks = datalength;
io_info.ldTgtId = device_id;
+ io_info.r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
+ scsi_buff_len = scsi_bufflen(scp);
+ io_request->DataLength = cpu_to_le32(scsi_buff_len);
+ io_info.data_arms = 1;
- if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
+ if (scp->sc_data_direction == DMA_FROM_DEVICE)
io_info.isRead = 1;
- local_map_ptr = fusion->ld_map[(instance->map_id & 1)];
+ local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
+ ld = MR_TargetIdToLdGet(device_id, local_map_ptr);
- if ((MR_TargetIdToLdGet(device_id, local_map_ptr) >=
- MAX_LOGICAL_DRIVES) || (!fusion->fast_path_io)) {
- io_request->RaidContext.regLockFlags = 0;
- fp_possible = 0;
+ if (ld < instance->fw_supported_vd_count)
+ raid = MR_LdRaidGet(ld, local_map_ptr);
+
+ if (!raid || (!fusion->fast_path_io)) {
+ rctx->reg_lock_flags = 0;
+ fp_possible = false;
} else {
- if (MR_BuildRaidContext(&io_info, &io_request->RaidContext,
- local_map_ptr))
- fp_possible = io_info.fpOkForIo;
+ if (MR_BuildRaidContext(instance, &io_info, rctx,
+ local_map_ptr, &raidLUN))
+ fp_possible = (io_info.fpOkForIo > 0) ? true : false;
+ }
+
+ megasas_get_msix_index(instance, scp, cmd, io_info.data_arms);
+
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ /* FP for Optimal raid level 1.
+ * All large RAID-1 writes (> 32 KiB, both WT and WB modes)
+ * are built by the driver as LD I/Os.
+ * All small RAID-1 WT writes (<= 32 KiB) are built as FP I/Os
+ * (there is never a reason to process these as buffered writes)
+ * All small RAID-1 WB writes (<= 32 KiB) are built as FP I/Os
+ * with the SLD bit asserted.
+ */
+ if (io_info.r1_alt_dev_handle != MR_DEVHANDLE_INVALID) {
+ mrdev_priv = scp->device->hostdata;
+
+ if (atomic_inc_return(&instance->fw_outstanding) >
+ (instance->host->can_queue)) {
+ fp_possible = false;
+ atomic_dec(&instance->fw_outstanding);
+ } else if (fusion->pcie_bw_limitation &&
+ ((scsi_buff_len > MR_LARGE_IO_MIN_SIZE) ||
+ (atomic_dec_if_positive(&mrdev_priv->r1_ldio_hint) > 0))) {
+ fp_possible = false;
+ atomic_dec(&instance->fw_outstanding);
+ if (scsi_buff_len > MR_LARGE_IO_MIN_SIZE)
+ atomic_set(&mrdev_priv->r1_ldio_hint,
+ instance->r1_ldio_hint_default);
+ }
+ }
+
+ if (!fp_possible ||
+ (io_info.isRead && io_info.ra_capable)) {
+ spin_lock_irqsave(&instance->stream_lock,
+ spinlock_flags);
+ megasas_stream_detect(instance, cmd, &io_info);
+ spin_unlock_irqrestore(&instance->stream_lock,
+ spinlock_flags);
+ /* In ventura if stream detected for a read and it is
+ * read ahead capable make this IO as LDIO
+ */
+ if (is_stream_detected(rctx_g35))
+ fp_possible = false;
+ }
+
+ /* If raid is NULL, set CPU affinity to default CPU0 */
+ if (raid)
+ megasas_set_raidflag_cpu_affinity(fusion, &io_request->RaidContext,
+ raid, fp_possible, io_info.isRead,
+ scsi_buff_len);
+ else
+ rctx_g35->routing_flags |=
+ (MR_RAID_CTX_CPUSEL_0 << MR_RAID_CTX_ROUTINGFLAGS_CPUSEL_SHIFT);
}
if (fp_possible) {
megasas_set_pd_lba(io_request, scp->cmd_len, &io_info, scp,
local_map_ptr, start_lba_lo);
- io_request->DataLength = scsi_bufflen(scp);
io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
cmd->request_desc->SCSIIO.RequestFlags =
- (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY
+ (MPI2_REQ_DESCRIPT_FLAGS_FP_IO
<< MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
- if ((fusion->load_balance_info[device_id].loadBalanceFlag) &&
- (io_info.isRead)) {
+ if (instance->adapter_type == INVADER_SERIES) {
+ rctx->type = MPI2_TYPE_CUDA;
+ rctx->nseg = 0x1;
+ io_request->IoFlags |= cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
+ rctx->reg_lock_flags |=
+ (MR_RL_FLAGS_GRANT_DESTINATION_CUDA |
+ MR_RL_FLAGS_SEQ_NUM_ENABLE);
+ } else if (instance->adapter_type >= VENTURA_SERIES) {
+ rctx_g35->nseg_type |= (1 << RAID_CONTEXT_NSEG_SHIFT);
+ rctx_g35->nseg_type |= (MPI2_TYPE_CUDA << RAID_CONTEXT_TYPE_SHIFT);
+ rctx_g35->routing_flags |= (1 << MR_RAID_CTX_ROUTINGFLAGS_SQN_SHIFT);
+ io_request->IoFlags |=
+ cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
+ }
+ if (fusion->load_balance_info &&
+ (fusion->load_balance_info[device_id].loadBalanceFlag) &&
+ (io_info.isRead)) {
io_info.devHandle =
- get_updated_dev_handle(
+ get_updated_dev_handle(instance,
&fusion->load_balance_info[device_id],
- &io_info);
- scp->SCp.Status |= MEGASAS_LOAD_BALANCE_FLAG;
+ &io_info, local_map_ptr);
+ megasas_priv(scp)->status |= MEGASAS_LOAD_BALANCE_FLAG;
+ cmd->pd_r1_lb = io_info.pd_after_lb;
+ if (instance->adapter_type >= VENTURA_SERIES)
+ rctx_g35->span_arm = io_info.span_arm;
+ else
+ rctx->span_arm = io_info.span_arm;
+
} else
- scp->SCp.Status &= ~MEGASAS_LOAD_BALANCE_FLAG;
+ megasas_priv(scp)->status &= ~MEGASAS_LOAD_BALANCE_FLAG;
+
+ if (instance->adapter_type >= VENTURA_SERIES)
+ cmd->r1_alt_dev_handle = io_info.r1_alt_dev_handle;
+ else
+ cmd->r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
+
+ if ((raidLUN[0] == 1) &&
+ (local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].validHandles > 1)) {
+ instance->dev_handle = !(instance->dev_handle);
+ io_info.devHandle =
+ local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].devHandle[instance->dev_handle];
+ }
+
cmd->request_desc->SCSIIO.DevHandle = io_info.devHandle;
io_request->DevHandle = io_info.devHandle;
+ cmd->pd_interface = io_info.pd_interface;
+ /* populate the LUN field */
+ memcpy(io_request->LUN, raidLUN, 8);
} else {
- io_request->RaidContext.timeoutValue =
- local_map_ptr->raidMap.fpPdIoTimeoutSec;
- io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
- io_request->DevHandle = device_id;
+ rctx->timeout_value =
+ cpu_to_le16(local_map_ptr->raidMap.fpPdIoTimeoutSec);
cmd->request_desc->SCSIIO.RequestFlags =
(MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO
<< MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ if (instance->adapter_type == INVADER_SERIES) {
+ if (io_info.do_fp_rlbypass ||
+ (rctx->reg_lock_flags == REGION_TYPE_UNUSED))
+ cmd->request_desc->SCSIIO.RequestFlags =
+ (MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ rctx->type = MPI2_TYPE_CUDA;
+ rctx->reg_lock_flags |=
+ (MR_RL_FLAGS_GRANT_DESTINATION_CPU0 |
+ MR_RL_FLAGS_SEQ_NUM_ENABLE);
+ rctx->nseg = 0x1;
+ } else if (instance->adapter_type >= VENTURA_SERIES) {
+ rctx_g35->routing_flags |= (1 << MR_RAID_CTX_ROUTINGFLAGS_SQN_SHIFT);
+ rctx_g35->nseg_type |= (1 << RAID_CONTEXT_NSEG_SHIFT);
+ rctx_g35->nseg_type |= (MPI2_TYPE_CUDA << RAID_CONTEXT_TYPE_SHIFT);
+ }
+ io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
+ io_request->DevHandle = cpu_to_le16(device_id);
+
} /* Not FP */
}
/**
- * megasas_build_dcdb_fusion - Prepares IOs to devices
+ * megasas_build_ld_nonrw_fusion - prepares non rw ios for virtual disk
* @instance: Adapter soft state
- * @scp: SCSI command
+ * @scmd: SCSI command
+ * @cmd: Command to be prepared
+ *
+ * Prepares the io_request frame for non-rw io cmds for vd.
+ */
+static void megasas_build_ld_nonrw_fusion(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd)
+{
+ u32 device_id;
+ struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
+ u16 ld;
+ struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
+ struct fusion_context *fusion = instance->ctrl_context;
+ u8 span, physArm;
+ __le16 devHandle;
+ u32 arRef, pd;
+ struct MR_LD_RAID *raid;
+ struct RAID_CONTEXT *pRAID_Context;
+ u8 fp_possible = 1;
+
+ io_request = cmd->io_request;
+ device_id = MEGASAS_DEV_INDEX(scmd);
+ local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
+ io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+ /* get RAID_Context pointer */
+ pRAID_Context = &io_request->RaidContext.raid_context;
+ /* Check with FW team */
+ pRAID_Context->virtual_disk_tgt_id = cpu_to_le16(device_id);
+ pRAID_Context->reg_lock_row_lba = 0;
+ pRAID_Context->reg_lock_length = 0;
+
+ if (fusion->fast_path_io && (
+ device_id < instance->fw_supported_vd_count)) {
+
+ ld = MR_TargetIdToLdGet(device_id, local_map_ptr);
+ if (ld >= instance->fw_supported_vd_count - 1)
+ fp_possible = 0;
+ else {
+ raid = MR_LdRaidGet(ld, local_map_ptr);
+ if (!(raid->capability.fpNonRWCapable))
+ fp_possible = 0;
+ }
+ } else
+ fp_possible = 0;
+
+ if (!fp_possible) {
+ io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
+ io_request->DevHandle = cpu_to_le16(device_id);
+ io_request->LUN[1] = scmd->device->lun;
+ pRAID_Context->timeout_value =
+ cpu_to_le16(scsi_cmd_to_rq(scmd)->timeout / HZ);
+ cmd->request_desc->SCSIIO.RequestFlags =
+ (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ } else {
+
+ /* set RAID context values */
+ pRAID_Context->config_seq_num = raid->seqNum;
+ if (instance->adapter_type < VENTURA_SERIES)
+ pRAID_Context->reg_lock_flags = REGION_TYPE_SHARED_READ;
+ pRAID_Context->timeout_value =
+ cpu_to_le16(raid->fpIoTimeoutForLd);
+
+ /* get the DevHandle for the PD (since this is
+ fpNonRWCapable, this is a single disk RAID0) */
+ span = physArm = 0;
+ arRef = MR_LdSpanArrayGet(ld, span, local_map_ptr);
+ pd = MR_ArPdGet(arRef, physArm, local_map_ptr);
+ devHandle = MR_PdDevHandleGet(pd, local_map_ptr);
+
+ /* build request descriptor */
+ cmd->request_desc->SCSIIO.RequestFlags =
+ (MPI2_REQ_DESCRIPT_FLAGS_FP_IO <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ cmd->request_desc->SCSIIO.DevHandle = devHandle;
+
+ /* populate the LUN field */
+ memcpy(io_request->LUN, raid->LUN, 8);
+
+ /* build the raidScsiIO structure */
+ io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ io_request->DevHandle = devHandle;
+ }
+}
+
+/**
+ * megasas_build_syspd_fusion - prepares rw/non-rw ios for syspd
+ * @instance: Adapter soft state
+ * @scmd: SCSI command
* @cmd: Command to be prepared
+ * @fp_possible: parameter to detect fast path or firmware path io.
*
- * Prepares the io_request frame for non-io cmds
+ * Prepares the io_request frame for rw/non-rw io cmds for syspds
*/
static void
-megasas_build_dcdb_fusion(struct megasas_instance *instance,
- struct scsi_cmnd *scmd,
- struct megasas_cmd_fusion *cmd)
+megasas_build_syspd_fusion(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd,
+ bool fp_possible)
{
u32 device_id;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
u16 pd_index = 0;
- struct MR_FW_RAID_MAP_ALL *local_map_ptr;
+ u16 os_timeout_value;
+ u16 timeout_limit;
+ struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
+ struct RAID_CONTEXT *pRAID_Context;
+ struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync;
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
struct fusion_context *fusion = instance->ctrl_context;
+ pd_sync = (void *)fusion->pd_seq_sync[(instance->pd_seq_map_id - 1) & 1];
+
+ device_id = MEGASAS_DEV_INDEX(scmd);
+ pd_index = MEGASAS_PD_INDEX(scmd);
+ os_timeout_value = scsi_cmd_to_rq(scmd)->timeout / HZ;
+ mr_device_priv_data = scmd->device->hostdata;
+ cmd->pd_interface = mr_device_priv_data->interface_type;
io_request = cmd->io_request;
- device_id = MEGASAS_DEV_INDEX(instance, scmd);
- pd_index = (scmd->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL)
- +scmd->device->id;
- local_map_ptr = fusion->ld_map[(instance->map_id & 1)];
-
- /* Check if this is a system PD I/O */
- if (instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) {
- io_request->Function = 0;
- io_request->DevHandle =
- local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl;
- io_request->RaidContext.timeoutValue =
- local_map_ptr->raidMap.fpPdIoTimeoutSec;
- io_request->RaidContext.regLockFlags = 0;
- io_request->RaidContext.regLockRowLBA = 0;
- io_request->RaidContext.regLockLength = 0;
- io_request->RaidContext.RAIDFlags =
- MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD <<
- MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT;
- cmd->request_desc->SCSIIO.RequestFlags =
- (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
- MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ /* get RAID_Context pointer */
+ pRAID_Context = &io_request->RaidContext.raid_context;
+ pRAID_Context->reg_lock_flags = 0;
+ pRAID_Context->reg_lock_row_lba = 0;
+ pRAID_Context->reg_lock_length = 0;
+ io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+ io_request->LUN[1] = scmd->device->lun;
+ pRAID_Context->raid_flags = MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD
+ << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT;
+
+ /* If FW supports PD sequence number */
+ if (instance->support_seqnum_jbod_fp) {
+ if (instance->use_seqnum_jbod_fp &&
+ instance->pd_list[pd_index].driveType == TYPE_DISK) {
+
+ /* More than 256 PD/JBOD support for Ventura */
+ if (instance->support_morethan256jbod)
+ pRAID_Context->virtual_disk_tgt_id =
+ pd_sync->seq[pd_index].pd_target_id;
+ else
+ pRAID_Context->virtual_disk_tgt_id =
+ cpu_to_le16(device_id +
+ (MAX_PHYSICAL_DEVICES - 1));
+ pRAID_Context->config_seq_num =
+ pd_sync->seq[pd_index].seqNum;
+ io_request->DevHandle =
+ pd_sync->seq[pd_index].devHandle;
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ io_request->RaidContext.raid_context_g35.routing_flags |=
+ (1 << MR_RAID_CTX_ROUTINGFLAGS_SQN_SHIFT);
+ io_request->RaidContext.raid_context_g35.nseg_type |=
+ (1 << RAID_CONTEXT_NSEG_SHIFT);
+ io_request->RaidContext.raid_context_g35.nseg_type |=
+ (MPI2_TYPE_CUDA << RAID_CONTEXT_TYPE_SHIFT);
+ } else {
+ pRAID_Context->type = MPI2_TYPE_CUDA;
+ pRAID_Context->nseg = 0x1;
+ pRAID_Context->reg_lock_flags |=
+ (MR_RL_FLAGS_SEQ_NUM_ENABLE |
+ MR_RL_FLAGS_GRANT_DESTINATION_CUDA);
+ }
+ } else {
+ pRAID_Context->virtual_disk_tgt_id =
+ cpu_to_le16(device_id +
+ (MAX_PHYSICAL_DEVICES - 1));
+ pRAID_Context->config_seq_num = 0;
+ io_request->DevHandle = cpu_to_le16(0xFFFF);
+ }
} else {
+ pRAID_Context->virtual_disk_tgt_id = cpu_to_le16(device_id);
+ pRAID_Context->config_seq_num = 0;
+
+ if (fusion->fast_path_io) {
+ local_map_ptr =
+ fusion->ld_drv_map[(instance->map_id & 1)];
+ io_request->DevHandle =
+ local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl;
+ } else {
+ io_request->DevHandle = cpu_to_le16(0xFFFF);
+ }
+ }
+
+ cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle;
+
+ megasas_get_msix_index(instance, scmd, cmd, 1);
+
+ if (!fp_possible) {
+ /* system pd firmware path */
io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
- io_request->DevHandle = device_id;
cmd->request_desc->SCSIIO.RequestFlags =
(MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
- MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ pRAID_Context->timeout_value = cpu_to_le16(os_timeout_value);
+ pRAID_Context->virtual_disk_tgt_id = cpu_to_le16(device_id);
+ } else {
+ if (os_timeout_value)
+ os_timeout_value++;
+
+ /* system pd Fast Path */
+ io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ timeout_limit = (scmd->device->type == TYPE_DISK) ?
+ 255 : 0xFFFF;
+ pRAID_Context->timeout_value =
+ cpu_to_le16((os_timeout_value > timeout_limit) ?
+ timeout_limit : os_timeout_value);
+ if (instance->adapter_type >= INVADER_SERIES)
+ io_request->IoFlags |=
+ cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
+
+ cmd->request_desc->SCSIIO.RequestFlags =
+ (MPI2_REQ_DESCRIPT_FLAGS_FP_IO <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
}
- io_request->RaidContext.VirtualDiskTgtId = device_id;
- io_request->LUN[1] = scmd->device->lun;
- io_request->DataLength = scsi_bufflen(scmd);
}
/**
@@ -1496,93 +3199,163 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance,
* Invokes helper functions to prepare request frames
* and sets flags appropriate for IO/Non-IO cmd
*/
-int
+static int
megasas_build_io_fusion(struct megasas_instance *instance,
struct scsi_cmnd *scp,
struct megasas_cmd_fusion *cmd)
{
- u32 device_id, sge_count;
+ int sge_count;
+ u16 pd_index = 0;
+ u8 drive_type = 0;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request;
-
- device_id = MEGASAS_DEV_INDEX(instance, scp);
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+ mr_device_priv_data = scp->device->hostdata;
/* Zero out some fields so they don't get reused */
- io_request->LUN[1] = 0;
+ memset(io_request->LUN, 0x0, 8);
io_request->CDB.EEDP32.PrimaryReferenceTag = 0;
io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0;
io_request->EEDPFlags = 0;
io_request->Control = 0;
io_request->EEDPBlockSize = 0;
- io_request->IoFlags = 0;
- io_request->RaidContext.RAIDFlags = 0;
+ io_request->ChainOffset = 0;
+ io_request->RaidContext.raid_context.raid_flags = 0;
+ io_request->RaidContext.raid_context.type = 0;
+ io_request->RaidContext.raid_context.nseg = 0;
memcpy(io_request->CDB.CDB32, scp->cmnd, scp->cmd_len);
/*
* Just the CDB length,rest of the Flags are zero
* This will be modified for FP in build_ldio_fusion
*/
- io_request->IoFlags = scp->cmd_len;
+ io_request->IoFlags = cpu_to_le16(scp->cmd_len);
- if (megasas_is_ldio(scp))
+ switch (megasas_cmd_type(scp)) {
+ case READ_WRITE_LDIO:
megasas_build_ldio_fusion(instance, scp, cmd);
- else
- megasas_build_dcdb_fusion(instance, scp, cmd);
+ break;
+ case NON_READ_WRITE_LDIO:
+ megasas_build_ld_nonrw_fusion(instance, scp, cmd);
+ break;
+ case READ_WRITE_SYSPDIO:
+ megasas_build_syspd_fusion(instance, scp, cmd, true);
+ break;
+ case NON_READ_WRITE_SYSPDIO:
+ pd_index = MEGASAS_PD_INDEX(scp);
+ drive_type = instance->pd_list[pd_index].driveType;
+ if ((instance->secure_jbod_support ||
+ mr_device_priv_data->is_tm_capable) ||
+ (instance->adapter_type >= VENTURA_SERIES &&
+ drive_type == TYPE_ENCLOSURE))
+ megasas_build_syspd_fusion(instance, scp, cmd, false);
+ else
+ megasas_build_syspd_fusion(instance, scp, cmd, true);
+ break;
+ default:
+ break;
+ }
/*
* Construct SGL
*/
- sge_count =
- megasas_make_sgl_fusion(instance, scp,
- (struct MPI25_IEEE_SGE_CHAIN64 *)
- &io_request->SGL, cmd);
+ sge_count = megasas_make_sgl(instance, scp, cmd);
- if (sge_count > instance->max_num_sge) {
- printk(KERN_ERR "megasas: Error. sge_count (0x%x) exceeds "
- "max (0x%x) allowed\n", sge_count,
- instance->max_num_sge);
+ if (sge_count > instance->max_num_sge || (sge_count < 0)) {
+ dev_err(&instance->pdev->dev,
+ "%s %d sge_count (%d) is out of range. Range is: 0-%d\n",
+ __func__, __LINE__, sge_count, instance->max_num_sge);
return 1;
}
- io_request->RaidContext.numSGE = sge_count;
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ set_num_sge(&io_request->RaidContext.raid_context_g35, sge_count);
+ cpu_to_le16s(&io_request->RaidContext.raid_context_g35.routing_flags);
+ cpu_to_le16s(&io_request->RaidContext.raid_context_g35.nseg_type);
+ } else {
+ /* numSGE store lower 8 bit of sge_count.
+ * numSGEExt store higher 8 bit of sge_count
+ */
+ io_request->RaidContext.raid_context.num_sge = sge_count;
+ io_request->RaidContext.raid_context.num_sge_ext =
+ (u8)(sge_count >> 8);
+ }
- io_request->SGLFlags = MPI2_SGE_FLAGS_64_BIT_ADDRESSING;
+ io_request->SGLFlags = cpu_to_le16(MPI2_SGE_FLAGS_64_BIT_ADDRESSING);
- if (scp->sc_data_direction == PCI_DMA_TODEVICE)
- io_request->Control |= MPI2_SCSIIO_CONTROL_WRITE;
- else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
- io_request->Control |= MPI2_SCSIIO_CONTROL_READ;
+ if (scp->sc_data_direction == DMA_TO_DEVICE)
+ io_request->Control |= cpu_to_le32(MPI2_SCSIIO_CONTROL_WRITE);
+ else if (scp->sc_data_direction == DMA_FROM_DEVICE)
+ io_request->Control |= cpu_to_le32(MPI2_SCSIIO_CONTROL_READ);
io_request->SGLOffset0 =
offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL) / 4;
- io_request->SenseBufferLowAddress = cmd->sense_phys_addr;
+ io_request->SenseBufferLowAddress =
+ cpu_to_le32(lower_32_bits(cmd->sense_phys_addr));
io_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
cmd->scmd = scp;
- scp->SCp.ptr = (char *)cmd;
+ megasas_priv(scp)->cmd_priv = cmd;
return 0;
}
-union MEGASAS_REQUEST_DESCRIPTOR_UNION *
+static union MEGASAS_REQUEST_DESCRIPTOR_UNION *
megasas_get_request_descriptor(struct megasas_instance *instance, u16 index)
{
u8 *p;
struct fusion_context *fusion;
- if (index >= instance->max_fw_cmds) {
- printk(KERN_ERR "megasas: Invalid SMID (0x%x)request for "
- "descriptor\n", index);
- return NULL;
- }
fusion = instance->ctrl_context;
- p = fusion->req_frames_desc
- +sizeof(union MEGASAS_REQUEST_DESCRIPTOR_UNION) *index;
+ p = fusion->req_frames_desc +
+ sizeof(union MEGASAS_REQUEST_DESCRIPTOR_UNION) * index;
return (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)p;
}
+
+/* megasas_prepate_secondRaid1_IO
+ * It prepares the raid 1 second IO
+ */
+static void megasas_prepare_secondRaid1_IO(struct megasas_instance *instance,
+ struct megasas_cmd_fusion *cmd,
+ struct megasas_cmd_fusion *r1_cmd)
+{
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc, *req_desc2 = NULL;
+ struct fusion_context *fusion;
+ fusion = instance->ctrl_context;
+ req_desc = cmd->request_desc;
+ /* copy the io request frame as well as 8 SGEs data for r1 command*/
+ memcpy(r1_cmd->io_request, cmd->io_request,
+ (sizeof(struct MPI2_RAID_SCSI_IO_REQUEST)));
+ memcpy(r1_cmd->io_request->SGLs, cmd->io_request->SGLs,
+ (fusion->max_sge_in_main_msg * sizeof(union MPI2_SGE_IO_UNION)));
+ /*sense buffer is different for r1 command*/
+ r1_cmd->io_request->SenseBufferLowAddress =
+ cpu_to_le32(lower_32_bits(r1_cmd->sense_phys_addr));
+ r1_cmd->scmd = cmd->scmd;
+ req_desc2 = megasas_get_request_descriptor(instance,
+ (r1_cmd->index - 1));
+ req_desc2->Words = 0;
+ r1_cmd->request_desc = req_desc2;
+ req_desc2->SCSIIO.SMID = cpu_to_le16(r1_cmd->index);
+ req_desc2->SCSIIO.RequestFlags = req_desc->SCSIIO.RequestFlags;
+ r1_cmd->request_desc->SCSIIO.DevHandle = cmd->r1_alt_dev_handle;
+ r1_cmd->io_request->DevHandle = cmd->r1_alt_dev_handle;
+ r1_cmd->r1_alt_dev_handle = cmd->io_request->DevHandle;
+ cmd->io_request->RaidContext.raid_context_g35.flow_specific.peer_smid =
+ cpu_to_le16(r1_cmd->index);
+ r1_cmd->io_request->RaidContext.raid_context_g35.flow_specific.peer_smid =
+ cpu_to_le16(cmd->index);
+ /*MSIxIndex of both commands request descriptors should be same*/
+ r1_cmd->request_desc->SCSIIO.MSIxIndex =
+ cmd->request_desc->SCSIIO.MSIxIndex;
+ /*span arm is different for r1 cmd*/
+ r1_cmd->io_request->RaidContext.raid_context_g35.span_arm =
+ cmd->io_request->RaidContext.raid_context_g35.span_arm + 1;
+}
+
/**
* megasas_build_and_issue_cmd_fusion -Main routine for building and
* issuing non IOCTL cmd
@@ -1593,61 +3366,186 @@ static u32
megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance,
struct scsi_cmnd *scmd)
{
- struct megasas_cmd_fusion *cmd;
+ struct megasas_cmd_fusion *cmd, *r1_cmd = NULL;
union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
u32 index;
- struct fusion_context *fusion;
- fusion = instance->ctrl_context;
+ if ((megasas_cmd_type(scmd) == READ_WRITE_LDIO) &&
+ instance->ldio_threshold &&
+ (atomic_inc_return(&instance->ldio_outstanding) >
+ instance->ldio_threshold)) {
+ atomic_dec(&instance->ldio_outstanding);
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
- cmd = megasas_get_cmd_fusion(instance);
- if (!cmd)
+ if (atomic_inc_return(&instance->fw_outstanding) >
+ instance->host->can_queue) {
+ atomic_dec(&instance->fw_outstanding);
return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ cmd = megasas_get_cmd_fusion(instance, scsi_cmd_to_rq(scmd)->tag);
+
+ if (!cmd) {
+ atomic_dec(&instance->fw_outstanding);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
index = cmd->index;
req_desc = megasas_get_request_descriptor(instance, index-1);
- if (!req_desc)
- return 1;
req_desc->Words = 0;
cmd->request_desc = req_desc;
- cmd->request_desc->Words = 0;
if (megasas_build_io_fusion(instance, scmd, cmd)) {
megasas_return_cmd_fusion(instance, cmd);
- printk(KERN_ERR "megasas: Error building command.\n");
+ dev_err(&instance->pdev->dev, "Error building command\n");
cmd->request_desc = NULL;
- return 1;
+ atomic_dec(&instance->fw_outstanding);
+ return SCSI_MLQUEUE_HOST_BUSY;
}
req_desc = cmd->request_desc;
- req_desc->SCSIIO.SMID = index;
+ req_desc->SCSIIO.SMID = cpu_to_le16(index);
if (cmd->io_request->ChainOffset != 0 &&
cmd->io_request->ChainOffset != 0xF)
- printk(KERN_ERR "megasas: The chain offset value is not "
+ dev_err(&instance->pdev->dev, "The chain offset value is not "
"correct : %x\n", cmd->io_request->ChainOffset);
+ /*
+ * if it is raid 1/10 fp write capable.
+ * try to get second command from pool and construct it.
+ * From FW, it has confirmed that lba values of two PDs
+ * corresponds to single R1/10 LD are always same
+ *
+ */
+ /* driver side count always should be less than max_fw_cmds
+ * to get new command
+ */
+ if (cmd->r1_alt_dev_handle != MR_DEVHANDLE_INVALID) {
+ r1_cmd = megasas_get_cmd_fusion(instance,
+ scsi_cmd_to_rq(scmd)->tag + instance->max_fw_cmds);
+ megasas_prepare_secondRaid1_IO(instance, cmd, r1_cmd);
+ }
+
/*
* Issue the command to the FW
*/
- atomic_inc(&instance->fw_outstanding);
- instance->instancet->fire_cmd(instance,
- req_desc->u.low, req_desc->u.high,
- instance->reg_set);
+ megasas_sdev_busy_inc(instance, scmd);
+ megasas_fire_cmd_fusion(instance, req_desc);
+
+ if (r1_cmd)
+ megasas_fire_cmd_fusion(instance, r1_cmd->request_desc);
+
return 0;
}
/**
+ * megasas_complete_r1_command -
+ * completes R1 FP write commands which has valid peer smid
+ * @instance: Adapter soft state
+ * @cmd: MPT command frame
+ *
+ */
+static inline void
+megasas_complete_r1_command(struct megasas_instance *instance,
+ struct megasas_cmd_fusion *cmd)
+{
+ u8 *sense, status, ex_status;
+ u32 data_length;
+ u16 peer_smid;
+ struct fusion_context *fusion;
+ struct megasas_cmd_fusion *r1_cmd = NULL;
+ struct scsi_cmnd *scmd_local = NULL;
+ struct RAID_CONTEXT_G35 *rctx_g35;
+
+ rctx_g35 = &cmd->io_request->RaidContext.raid_context_g35;
+ fusion = instance->ctrl_context;
+ peer_smid = le16_to_cpu(rctx_g35->flow_specific.peer_smid);
+
+ r1_cmd = fusion->cmd_list[peer_smid - 1];
+ scmd_local = cmd->scmd;
+ status = rctx_g35->status;
+ ex_status = rctx_g35->ex_status;
+ data_length = cmd->io_request->DataLength;
+ sense = cmd->sense;
+
+ cmd->cmd_completed = true;
+
+ /* Check if peer command is completed or not*/
+ if (r1_cmd->cmd_completed) {
+ rctx_g35 = &r1_cmd->io_request->RaidContext.raid_context_g35;
+ if (rctx_g35->status != MFI_STAT_OK) {
+ status = rctx_g35->status;
+ ex_status = rctx_g35->ex_status;
+ data_length = r1_cmd->io_request->DataLength;
+ sense = r1_cmd->sense;
+ }
+
+ megasas_return_cmd_fusion(instance, r1_cmd);
+ map_cmd_status(fusion, scmd_local, status, ex_status,
+ le32_to_cpu(data_length), sense);
+ if (instance->ldio_threshold &&
+ megasas_cmd_type(scmd_local) == READ_WRITE_LDIO)
+ atomic_dec(&instance->ldio_outstanding);
+ megasas_priv(scmd_local)->cmd_priv = NULL;
+ megasas_return_cmd_fusion(instance, cmd);
+ scsi_dma_unmap(scmd_local);
+ megasas_sdev_busy_dec(instance, scmd_local);
+ scsi_done(scmd_local);
+ }
+}
+
+/**
+ * access_irq_context: Access to reply processing
+ * @irq_context: IRQ context
+ *
+ * Synchronize access to reply processing.
+ *
+ * Return: true on success, false on failure.
+ */
+static inline
+bool access_irq_context(struct megasas_irq_context *irq_context)
+{
+ if (!irq_context)
+ return true;
+
+ if (atomic_add_unless(&irq_context->in_used, 1, 1))
+ return true;
+
+ return false;
+}
+
+/**
+ * release_irq_context: Release reply processing
+ * @irq_context: IRQ context
+ *
+ * Release access of reply processing.
+ *
+ * Return: Nothing.
+ */
+static inline
+void release_irq_context(struct megasas_irq_context *irq_context)
+{
+ if (irq_context)
+ atomic_dec(&irq_context->in_used);
+}
+
+/**
* complete_cmd_fusion - Completes command
* @instance: Adapter soft state
+ * @MSIxIndex: MSI number
+ * @irq_context: IRQ context
+ *
* Completes all commands that is in reply descriptor queue
*/
-int
-complete_cmd_fusion(struct megasas_instance *instance)
+static int
+complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex,
+ struct megasas_irq_context *irq_context)
{
union MPI2_REPLY_DESCRIPTORS_UNION *desc;
struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *reply_desc;
@@ -1656,18 +3554,25 @@ complete_cmd_fusion(struct megasas_instance *instance)
struct megasas_cmd *cmd_mfi;
struct megasas_cmd_fusion *cmd_fusion;
u16 smid, num_completed;
- u8 reply_descript_type, arm;
- u32 status, extStatus, device_id;
+ u8 reply_descript_type, *sense, status, extStatus;
+ u32 device_id, data_length;
union desc_value d_val;
struct LD_LOAD_BALANCE_INFO *lbinfo;
+ int threshold_reply_count = 0;
+ struct scsi_cmnd *scmd_local = NULL;
+ struct MR_TASK_MANAGE_REQUEST *mr_tm_req;
+ struct MPI2_SCSI_TASK_MANAGE_REQUEST *mpi_tm_req;
fusion = instance->ctrl_context;
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR)
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)
return IRQ_HANDLED;
- desc = fusion->reply_frames_desc;
- desc += fusion->last_reply_idx;
+ if (!access_irq_context(irq_context))
+ return 0;
+
+ desc = fusion->reply_frames_desc[MSIxIndex] +
+ fusion->last_reply_idx[MSIxIndex];
reply_desc = (struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *)desc;
@@ -1676,80 +3581,92 @@ complete_cmd_fusion(struct megasas_instance *instance)
reply_descript_type = reply_desc->ReplyFlags &
MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
- if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+ if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) {
+ release_irq_context(irq_context);
return IRQ_NONE;
-
- d_val.word = desc->Words;
+ }
num_completed = 0;
- while ((d_val.u.low != UINT_MAX) && (d_val.u.high != UINT_MAX)) {
- smid = reply_desc->SMID;
+ while (d_val.u.low != cpu_to_le32(UINT_MAX) &&
+ d_val.u.high != cpu_to_le32(UINT_MAX)) {
+ smid = le16_to_cpu(reply_desc->SMID);
cmd_fusion = fusion->cmd_list[smid - 1];
+ scsi_io_req = (struct MPI2_RAID_SCSI_IO_REQUEST *)
+ cmd_fusion->io_request;
- scsi_io_req =
- (struct MPI2_RAID_SCSI_IO_REQUEST *)
- cmd_fusion->io_request;
-
- if (cmd_fusion->scmd)
- cmd_fusion->scmd->SCp.ptr = NULL;
-
- status = scsi_io_req->RaidContext.status;
- extStatus = scsi_io_req->RaidContext.exStatus;
+ scmd_local = cmd_fusion->scmd;
+ status = scsi_io_req->RaidContext.raid_context.status;
+ extStatus = scsi_io_req->RaidContext.raid_context.ex_status;
+ sense = cmd_fusion->sense;
+ data_length = scsi_io_req->DataLength;
switch (scsi_io_req->Function) {
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ mr_tm_req = (struct MR_TASK_MANAGE_REQUEST *)
+ cmd_fusion->io_request;
+ mpi_tm_req = (struct MPI2_SCSI_TASK_MANAGE_REQUEST *)
+ &mr_tm_req->TmRequest;
+ dev_dbg(&instance->pdev->dev, "TM completion:"
+ "type: 0x%x TaskMID: 0x%x\n",
+ mpi_tm_req->TaskType, mpi_tm_req->TaskMID);
+ complete(&cmd_fusion->done);
+ break;
case MPI2_FUNCTION_SCSI_IO_REQUEST: /*Fast Path IO.*/
/* Update load balancing info */
- device_id = MEGASAS_DEV_INDEX(instance,
- cmd_fusion->scmd);
- lbinfo = &fusion->load_balance_info[device_id];
- if (cmd_fusion->scmd->SCp.Status &
- MEGASAS_LOAD_BALANCE_FLAG) {
- arm = lbinfo->raid1DevHandle[0] ==
- cmd_fusion->io_request->DevHandle ? 0 :
- 1;
- atomic_dec(&lbinfo->scsi_pending_cmds[arm]);
- cmd_fusion->scmd->SCp.Status &=
+ if (fusion->load_balance_info &&
+ (megasas_priv(cmd_fusion->scmd)->status &
+ MEGASAS_LOAD_BALANCE_FLAG)) {
+ device_id = MEGASAS_DEV_INDEX(scmd_local);
+ lbinfo = &fusion->load_balance_info[device_id];
+ atomic_dec(&lbinfo->scsi_pending_cmds[cmd_fusion->pd_r1_lb]);
+ megasas_priv(cmd_fusion->scmd)->status &=
~MEGASAS_LOAD_BALANCE_FLAG;
}
- if (reply_descript_type ==
- MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS) {
- if (megasas_dbg_lvl == 5)
- printk(KERN_ERR "\nmegasas: FAST Path "
- "IO Success\n");
- }
- /* Fall thru and complete IO */
+ fallthrough; /* and complete IO */
case MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST: /* LD-IO Path */
- /* Map the FW Cmd Status */
- map_cmd_status(cmd_fusion, status, extStatus);
- scsi_dma_unmap(cmd_fusion->scmd);
- cmd_fusion->scmd->scsi_done(cmd_fusion->scmd);
- scsi_io_req->RaidContext.status = 0;
- scsi_io_req->RaidContext.exStatus = 0;
- megasas_return_cmd_fusion(instance, cmd_fusion);
atomic_dec(&instance->fw_outstanding);
-
+ if (cmd_fusion->r1_alt_dev_handle == MR_DEVHANDLE_INVALID) {
+ map_cmd_status(fusion, scmd_local, status,
+ extStatus, le32_to_cpu(data_length),
+ sense);
+ if (instance->ldio_threshold &&
+ (megasas_cmd_type(scmd_local) == READ_WRITE_LDIO))
+ atomic_dec(&instance->ldio_outstanding);
+ megasas_priv(scmd_local)->cmd_priv = NULL;
+ megasas_return_cmd_fusion(instance, cmd_fusion);
+ scsi_dma_unmap(scmd_local);
+ megasas_sdev_busy_dec(instance, scmd_local);
+ scsi_done(scmd_local);
+ } else /* Optimal VD - R1 FP command completion. */
+ megasas_complete_r1_command(instance, cmd_fusion);
break;
case MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST: /*MFI command */
cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
- megasas_complete_cmd(instance, cmd_mfi, DID_OK);
- cmd_fusion->flags = 0;
- megasas_return_cmd_fusion(instance, cmd_fusion);
-
+ /* Poll mode. Dummy free.
+ * In case of Interrupt mode, caller has reverse check.
+ */
+ if (cmd_mfi->flags & DRV_DCMD_POLLED_MODE) {
+ cmd_mfi->flags &= ~DRV_DCMD_POLLED_MODE;
+ megasas_return_cmd(instance, cmd_mfi);
+ } else
+ megasas_complete_cmd(instance, cmd_mfi, DID_OK);
break;
}
- fusion->last_reply_idx++;
- if (fusion->last_reply_idx >= fusion->reply_q_depth)
- fusion->last_reply_idx = 0;
+ fusion->last_reply_idx[MSIxIndex]++;
+ if (fusion->last_reply_idx[MSIxIndex] >=
+ fusion->reply_q_depth)
+ fusion->last_reply_idx[MSIxIndex] = 0;
- desc->Words = ULLONG_MAX;
+ desc->Words = cpu_to_le64(ULLONG_MAX);
num_completed++;
+ threshold_reply_count++;
/* Get the next reply descriptor */
- if (!fusion->last_reply_idx)
- desc = fusion->reply_frames_desc;
+ if (!fusion->last_reply_idx[MSIxIndex])
+ desc = fusion->reply_frames_desc[MSIxIndex];
else
desc++;
@@ -1763,80 +3680,214 @@ complete_cmd_fusion(struct megasas_instance *instance)
if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
break;
+ /*
+ * Write to reply post host index register after completing threshold
+ * number of reply counts and still there are more replies in reply queue
+ * pending to be completed
+ */
+ if (threshold_reply_count >= instance->threshold_reply_count) {
+ if (instance->msix_combined)
+ writel(((MSIxIndex & 0x7) << 24) |
+ fusion->last_reply_idx[MSIxIndex],
+ instance->reply_post_host_index_addr[MSIxIndex/8]);
+ else
+ writel((MSIxIndex << 24) |
+ fusion->last_reply_idx[MSIxIndex],
+ instance->reply_post_host_index_addr[0]);
+ threshold_reply_count = 0;
+ if (irq_context) {
+ if (!irq_context->irq_poll_scheduled) {
+ irq_context->irq_poll_scheduled = true;
+ irq_context->irq_line_enable = true;
+ irq_poll_sched(&irq_context->irqpoll);
+ }
+ release_irq_context(irq_context);
+ return num_completed;
+ }
+ }
}
- if (!num_completed)
- return IRQ_NONE;
+ if (num_completed) {
+ wmb();
+ if (instance->msix_combined)
+ writel(((MSIxIndex & 0x7) << 24) |
+ fusion->last_reply_idx[MSIxIndex],
+ instance->reply_post_host_index_addr[MSIxIndex/8]);
+ else
+ writel((MSIxIndex << 24) |
+ fusion->last_reply_idx[MSIxIndex],
+ instance->reply_post_host_index_addr[0]);
+ megasas_check_and_restore_queue_depth(instance);
+ }
- wmb();
- writel(fusion->last_reply_idx,
- &instance->reg_set->reply_post_host_index);
- megasas_check_and_restore_queue_depth(instance);
- return IRQ_HANDLED;
+ release_irq_context(irq_context);
+
+ return num_completed;
+}
+
+int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num)
+{
+
+ struct megasas_instance *instance;
+ int num_entries = 0;
+ struct fusion_context *fusion;
+
+ instance = (struct megasas_instance *)shost->hostdata;
+
+ fusion = instance->ctrl_context;
+
+ queue_num = queue_num + instance->low_latency_index_start;
+
+ if (!atomic_add_unless(&fusion->busy_mq_poll[queue_num], 1, 1))
+ return 0;
+
+ num_entries = complete_cmd_fusion(instance, queue_num, NULL);
+ atomic_dec(&fusion->busy_mq_poll[queue_num]);
+
+ return num_entries;
}
/**
- * megasas_complete_cmd_dpc_fusion - Completes command
+ * megasas_enable_irq_poll() - enable irqpoll
* @instance: Adapter soft state
+ */
+static void megasas_enable_irq_poll(struct megasas_instance *instance)
+{
+ u32 count, i;
+ struct megasas_irq_context *irq_ctx;
+
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+
+ for (i = 0; i < count; i++) {
+ irq_ctx = &instance->irq_context[i];
+ irq_poll_enable(&irq_ctx->irqpoll);
+ }
+}
+
+/**
+ * megasas_sync_irqs - Synchronizes all IRQs owned by adapter
+ * @instance_addr: Adapter soft state address
+ */
+static void megasas_sync_irqs(unsigned long instance_addr)
+{
+ u32 count, i;
+ struct megasas_instance *instance =
+ (struct megasas_instance *)instance_addr;
+ struct megasas_irq_context *irq_ctx;
+
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+
+ for (i = 0; i < count; i++) {
+ synchronize_irq(pci_irq_vector(instance->pdev, i));
+ irq_ctx = &instance->irq_context[i];
+ irq_poll_disable(&irq_ctx->irqpoll);
+ if (irq_ctx->irq_poll_scheduled) {
+ irq_ctx->irq_poll_scheduled = false;
+ enable_irq(irq_ctx->os_irq);
+ complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx);
+ }
+ }
+}
+
+/**
+ * megasas_irqpoll() - process a queue for completed reply descriptors
+ * @irqpoll: IRQ poll structure associated with queue to poll.
+ * @budget: Threshold of reply descriptors to process per poll.
+ *
+ * Return: The number of entries processed.
+ */
+
+int megasas_irqpoll(struct irq_poll *irqpoll, int budget)
+{
+ struct megasas_irq_context *irq_ctx;
+ struct megasas_instance *instance;
+ int num_entries;
+
+ irq_ctx = container_of(irqpoll, struct megasas_irq_context, irqpoll);
+ instance = irq_ctx->instance;
+
+ if (irq_ctx->irq_line_enable) {
+ disable_irq_nosync(irq_ctx->os_irq);
+ irq_ctx->irq_line_enable = false;
+ }
+
+ num_entries = complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx);
+ if (num_entries < budget) {
+ irq_poll_complete(irqpoll);
+ irq_ctx->irq_poll_scheduled = false;
+ enable_irq(irq_ctx->os_irq);
+ complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx);
+ }
+
+ return num_entries;
+}
+
+/**
+ * megasas_complete_cmd_dpc_fusion - Completes command
+ * @instance_addr: Adapter soft state address
*
* Tasklet to complete cmds
*/
-void
+static void
megasas_complete_cmd_dpc_fusion(unsigned long instance_addr)
{
struct megasas_instance *instance =
(struct megasas_instance *)instance_addr;
- unsigned long flags;
+ struct megasas_irq_context *irq_ctx = NULL;
+ u32 count, MSIxIndex;
+
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
/* If we have already declared adapter dead, donot complete cmds */
- spin_lock_irqsave(&instance->hba_lock, flags);
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
- spin_unlock_irqrestore(&instance->hba_lock, flags);
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)
return;
- }
- spin_unlock_irqrestore(&instance->hba_lock, flags);
- spin_lock_irqsave(&instance->completion_lock, flags);
- complete_cmd_fusion(instance);
- spin_unlock_irqrestore(&instance->completion_lock, flags);
+ for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++) {
+ irq_ctx = &instance->irq_context[MSIxIndex];
+ complete_cmd_fusion(instance, MSIxIndex, irq_ctx);
+ }
}
/**
* megasas_isr_fusion - isr entry point
+ * @irq: IRQ number
+ * @devp: IRQ context
*/
-irqreturn_t megasas_isr_fusion(int irq, void *devp)
+static irqreturn_t megasas_isr_fusion(int irq, void *devp)
{
- struct megasas_instance *instance = (struct megasas_instance *)devp;
- u32 mfiStatus, fw_state;
+ struct megasas_irq_context *irq_context = devp;
+ struct megasas_instance *instance = irq_context->instance;
+ u32 mfiStatus;
- if (!instance->msi_flag) {
- mfiStatus = instance->instancet->clear_intr(instance->reg_set);
+ if (instance->mask_interrupts)
+ return IRQ_NONE;
+
+ if (irq_context->irq_poll_scheduled)
+ return IRQ_HANDLED;
+
+ if (!instance->msix_vectors) {
+ mfiStatus = instance->instancet->clear_intr(instance);
if (!mfiStatus)
return IRQ_NONE;
}
/* If we are resetting, bail */
- if (test_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags))
+ if (test_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags)) {
+ instance->instancet->clear_intr(instance);
return IRQ_HANDLED;
-
- if (!complete_cmd_fusion(instance)) {
- /* If we didn't complete any commands, check for FW fault */
- fw_state = instance->instancet->read_fw_status_reg(
- instance->reg_set) & MFI_STATE_MASK;
- if (fw_state == MFI_STATE_FAULT)
- schedule_work(&instance->work_init);
}
- return IRQ_HANDLED;
+ return complete_cmd_fusion(instance, irq_context->MSIxIndex, irq_context)
+ ? IRQ_HANDLED : IRQ_NONE;
}
/**
* build_mpt_mfi_pass_thru - builds a cmd fo MFI Pass thru
* @instance: Adapter soft state
- * mfi_cmd: megasas_cmd pointer
+ * @mfi_cmd: megasas_cmd pointer
*
*/
-u8
+static void
build_mpt_mfi_pass_thru(struct megasas_instance *instance,
struct megasas_cmd *mfi_cmd)
{
@@ -1846,26 +3897,32 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance,
struct fusion_context *fusion;
struct megasas_header *frame_hdr = &mfi_cmd->frame->hdr;
- cmd = megasas_get_cmd_fusion(instance);
- if (!cmd)
- return 1;
+ fusion = instance->ctrl_context;
+
+ cmd = megasas_get_cmd_fusion(instance,
+ instance->max_scsi_cmds + mfi_cmd->index);
/* Save the smid. To be used for returning the cmd */
mfi_cmd->context.smid = cmd->index;
- cmd->sync_cmd_idx = mfi_cmd->index;
-
/*
* For cmds where the flag is set, store the flag and check
* on completion. For cmds with this flag, don't call
* megasas_complete_cmd
*/
- if (frame_hdr->flags & MFI_FRAME_DONT_POST_IN_REPLY_QUEUE)
- cmd->flags = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
+ if (frame_hdr->flags & cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE))
+ mfi_cmd->flags |= DRV_DCMD_POLLED_MODE;
- fusion = instance->ctrl_context;
io_req = cmd->io_request;
+
+ if (instance->adapter_type >= INVADER_SERIES) {
+ struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr_end =
+ (struct MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL;
+ sgl_ptr_end += fusion->max_sge_in_main_msg - 1;
+ sgl_ptr_end->Flags = 0;
+ }
+
mpi25_ieee_chain =
(struct MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL.IeeeChain;
@@ -1874,14 +3931,12 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance,
SGL) / 4;
io_req->ChainOffset = fusion->chain_offset_mfi_pthru;
- mpi25_ieee_chain->Address = mfi_cmd->frame_phys_addr;
+ mpi25_ieee_chain->Address = cpu_to_le64(mfi_cmd->frame_phys_addr);
mpi25_ieee_chain->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT |
MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR;
- mpi25_ieee_chain->Length = MEGASAS_MAX_SZ_CHAIN_FRAME;
-
- return 0;
+ mpi25_ieee_chain->Length = cpu_to_le32(instance->mfi_frame_size);
}
/**
@@ -1890,29 +3945,22 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance,
* @cmd: mfi cmd to build
*
*/
-union MEGASAS_REQUEST_DESCRIPTOR_UNION *
+static union MEGASAS_REQUEST_DESCRIPTOR_UNION *
build_mpt_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
- union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc = NULL;
u16 index;
- if (build_mpt_mfi_pass_thru(instance, cmd)) {
- printk(KERN_ERR "Couldn't build MFI pass thru cmd\n");
- return NULL;
- }
-
+ build_mpt_mfi_pass_thru(instance, cmd);
index = cmd->context.smid;
req_desc = megasas_get_request_descriptor(instance, index - 1);
- if (!req_desc)
- return NULL;
-
req_desc->Words = 0;
req_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
- req_desc->SCSIIO.SMID = index;
+ req_desc->SCSIIO.SMID = cpu_to_le16(index);
return req_desc;
}
@@ -1923,62 +3971,163 @@ build_mpt_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
* @cmd: mfi cmd pointer
*
*/
-void
+static void
megasas_issue_dcmd_fusion(struct megasas_instance *instance,
struct megasas_cmd *cmd)
{
union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
- union desc_value d_val;
req_desc = build_mpt_cmd(instance, cmd);
- if (!req_desc) {
- printk(KERN_ERR "Couldn't issue MFI pass thru cmd\n");
- return;
- }
- d_val.word = req_desc->Words;
- instance->instancet->fire_cmd(instance, req_desc->u.low,
- req_desc->u.high, instance->reg_set);
+ megasas_fire_cmd_fusion(instance, req_desc);
+ return;
}
/**
* megasas_release_fusion - Reverses the FW initialization
- * @intance: Adapter soft state
+ * @instance: Adapter soft state
*/
void
megasas_release_fusion(struct megasas_instance *instance)
{
+ megasas_free_ioc_init_cmd(instance);
megasas_free_cmds(instance);
megasas_free_cmds_fusion(instance);
iounmap(instance->reg_set);
- pci_release_selected_regions(instance->pdev, instance->bar);
+ pci_release_selected_regions(instance->pdev, 1<<instance->bar);
}
/**
* megasas_read_fw_status_reg_fusion - returns the current FW status value
- * @regs: MFI register set
+ * @instance: Adapter soft state
*/
static u32
-megasas_read_fw_status_reg_fusion(struct megasas_register_set __iomem *regs)
+megasas_read_fw_status_reg_fusion(struct megasas_instance *instance)
+{
+ return megasas_readl(instance, &instance->reg_set->outbound_scratch_pad_0);
+}
+
+/**
+ * megasas_alloc_host_crash_buffer - Host buffers for Crash dump collection from Firmware
+ * @instance: Controller's soft instance
+ * @return: Number of allocated host crash buffers
+ */
+static void
+megasas_alloc_host_crash_buffer(struct megasas_instance *instance)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_CRASH_DUMP_SIZE; i++) {
+ instance->crash_buf[i] = vzalloc(CRASH_DMA_BUF_SIZE);
+ if (!instance->crash_buf[i]) {
+ dev_info(&instance->pdev->dev, "Firmware crash dump "
+ "memory allocation failed at index %d\n", i);
+ break;
+ }
+ }
+ instance->drv_buf_alloc = i;
+}
+
+/**
+ * megasas_free_host_crash_buffer - Host buffers for Crash dump collection from Firmware
+ * @instance: Controller's soft instance
+ */
+void
+megasas_free_host_crash_buffer(struct megasas_instance *instance)
{
- return readl(&(regs)->outbound_scratch_pad);
+ unsigned int i;
+ for (i = 0; i < instance->drv_buf_alloc; i++) {
+ vfree(instance->crash_buf[i]);
+ }
+ instance->drv_buf_index = 0;
+ instance->drv_buf_alloc = 0;
+ instance->fw_crash_state = UNAVAILABLE;
+ instance->fw_crash_buffer_size = 0;
}
/**
* megasas_adp_reset_fusion - For controller reset
+ * @instance: Controller's soft instance
* @regs: MFI register set
*/
static int
megasas_adp_reset_fusion(struct megasas_instance *instance,
struct megasas_register_set __iomem *regs)
{
+ u32 host_diag, abs_state, retry;
+
+ /* Now try to reset the chip */
+ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
+ writel(MPI2_WRSEQ_1ST_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
+ writel(MPI2_WRSEQ_2ND_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
+ writel(MPI2_WRSEQ_3RD_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
+ writel(MPI2_WRSEQ_4TH_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
+ writel(MPI2_WRSEQ_5TH_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
+ writel(MPI2_WRSEQ_6TH_KEY_VALUE, &instance->reg_set->fusion_seq_offset);
+
+ /* Check that the diag write enable (DRWE) bit is on */
+ host_diag = megasas_readl(instance, &instance->reg_set->fusion_host_diag);
+ retry = 0;
+ while (!(host_diag & HOST_DIAG_WRITE_ENABLE)) {
+ msleep(100);
+ host_diag = megasas_readl(instance,
+ &instance->reg_set->fusion_host_diag);
+ if (retry++ == 100) {
+ dev_warn(&instance->pdev->dev,
+ "Host diag unlock failed from %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+ if (!(host_diag & HOST_DIAG_WRITE_ENABLE))
+ return -1;
+
+ /* Send chip reset command */
+ writel(host_diag | HOST_DIAG_RESET_ADAPTER,
+ &instance->reg_set->fusion_host_diag);
+ msleep(3000);
+
+ /* Make sure reset adapter bit is cleared */
+ host_diag = megasas_readl(instance, &instance->reg_set->fusion_host_diag);
+ retry = 0;
+ while (host_diag & HOST_DIAG_RESET_ADAPTER) {
+ msleep(100);
+ host_diag = megasas_readl(instance,
+ &instance->reg_set->fusion_host_diag);
+ if (retry++ == 1000) {
+ dev_warn(&instance->pdev->dev,
+ "Diag reset adapter never cleared %s %d\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+ if (host_diag & HOST_DIAG_RESET_ADAPTER)
+ return -1;
+
+ abs_state = instance->instancet->read_fw_status_reg(instance)
+ & MFI_STATE_MASK;
+ retry = 0;
+
+ while ((abs_state <= MFI_STATE_FW_INIT) && (retry++ < 1000)) {
+ msleep(100);
+ abs_state = instance->instancet->
+ read_fw_status_reg(instance) & MFI_STATE_MASK;
+ }
+ if (abs_state <= MFI_STATE_FW_INIT) {
+ dev_warn(&instance->pdev->dev,
+ "fw state < MFI_STATE_FW_INIT, state = 0x%x %s %d\n",
+ abs_state, __func__, __LINE__);
+ return -1;
+ }
+
return 0;
}
/**
* megasas_check_reset_fusion - For controller reset check
+ * @instance: Controller's soft instance
* @regs: MFI register set
*/
static int
@@ -1988,285 +4137,1237 @@ megasas_check_reset_fusion(struct megasas_instance *instance,
return 0;
}
+/**
+ * megasas_trigger_snap_dump - Trigger snap dump in FW
+ * @instance: Soft instance of adapter
+ */
+static inline void megasas_trigger_snap_dump(struct megasas_instance *instance)
+{
+ int j;
+ u32 fw_state, abs_state;
+
+ if (!instance->disableOnlineCtrlReset) {
+ dev_info(&instance->pdev->dev, "Trigger snap dump\n");
+ writel(MFI_ADP_TRIGGER_SNAP_DUMP,
+ &instance->reg_set->doorbell);
+ readl(&instance->reg_set->doorbell);
+ }
+
+ for (j = 0; j < instance->snapdump_wait_time; j++) {
+ abs_state = instance->instancet->read_fw_status_reg(instance);
+ fw_state = abs_state & MFI_STATE_MASK;
+ if (fw_state == MFI_STATE_FAULT) {
+ dev_printk(KERN_ERR, &instance->pdev->dev,
+ "FW in FAULT state Fault code:0x%x subcode:0x%x func:%s\n",
+ abs_state & MFI_STATE_FAULT_CODE,
+ abs_state & MFI_STATE_FAULT_SUBCODE, __func__);
+ return;
+ }
+ msleep(1000);
+ }
+}
+
/* This function waits for outstanding commands on fusion to complete */
-int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance)
+static int
+megasas_wait_for_outstanding_fusion(struct megasas_instance *instance,
+ int reason, int *convert)
{
- int i, outstanding, retval = 0;
- u32 fw_state, wait_time = MEGASAS_RESET_WAIT_TIME;
+ int i, outstanding, retval = 0, hb_seconds_missed = 0;
+ u32 fw_state, abs_state;
+ u32 waittime_for_io_completion;
+
+ waittime_for_io_completion =
+ min_t(u32, resetwaittime,
+ (resetwaittime - instance->snapdump_wait_time));
+
+ if (reason == MFI_IO_TIMEOUT_OCR) {
+ dev_info(&instance->pdev->dev,
+ "MFI command is timed out\n");
+ megasas_complete_cmd_dpc_fusion((unsigned long)instance);
+ if (instance->snapdump_wait_time)
+ megasas_trigger_snap_dump(instance);
+ retval = 1;
+ goto out;
+ }
- for (i = 0; i < wait_time; i++) {
+ for (i = 0; i < waittime_for_io_completion; i++) {
/* Check if firmware is in fault state */
- fw_state = instance->instancet->read_fw_status_reg(
- instance->reg_set) & MFI_STATE_MASK;
+ abs_state = instance->instancet->read_fw_status_reg(instance);
+ fw_state = abs_state & MFI_STATE_MASK;
if (fw_state == MFI_STATE_FAULT) {
- printk(KERN_WARNING "megasas: Found FW in FAULT state,"
- " will reset adapter.\n");
+ dev_printk(KERN_ERR, &instance->pdev->dev,
+ "FW in FAULT state Fault code:0x%x subcode:0x%x func:%s\n",
+ abs_state & MFI_STATE_FAULT_CODE,
+ abs_state & MFI_STATE_FAULT_SUBCODE, __func__);
+ megasas_complete_cmd_dpc_fusion((unsigned long)instance);
+ if (instance->requestorId && reason) {
+ dev_warn(&instance->pdev->dev, "SR-IOV Found FW in FAULT"
+ " state while polling during"
+ " I/O timeout handling for %d\n",
+ instance->host->host_no);
+ *convert = 1;
+ }
+
+ retval = 1;
+ goto out;
+ }
+
+
+ /* If SR-IOV VF mode & heartbeat timeout, don't wait */
+ if (instance->requestorId && !reason) {
retval = 1;
goto out;
}
+ /* If SR-IOV VF mode & I/O timeout, check for HB timeout */
+ if (instance->requestorId && (reason == SCSIIO_TIMEOUT_OCR)) {
+ if (instance->hb_host_mem->HB.fwCounter !=
+ instance->hb_host_mem->HB.driverCounter) {
+ instance->hb_host_mem->HB.driverCounter =
+ instance->hb_host_mem->HB.fwCounter;
+ hb_seconds_missed = 0;
+ } else {
+ hb_seconds_missed++;
+ if (hb_seconds_missed ==
+ (MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF/HZ)) {
+ dev_warn(&instance->pdev->dev, "SR-IOV:"
+ " Heartbeat never completed "
+ " while polling during I/O "
+ " timeout handling for "
+ "scsi%d.\n",
+ instance->host->host_no);
+ *convert = 1;
+ retval = 1;
+ goto out;
+ }
+ }
+ }
+
+ megasas_complete_cmd_dpc_fusion((unsigned long)instance);
outstanding = atomic_read(&instance->fw_outstanding);
if (!outstanding)
goto out;
if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
- printk(KERN_NOTICE "megasas: [%2d]waiting for %d "
- "commands to complete\n", i, outstanding);
- megasas_complete_cmd_dpc_fusion(
- (unsigned long)instance);
+ dev_notice(&instance->pdev->dev, "[%2d]waiting for %d "
+ "commands to complete for scsi%d\n", i,
+ outstanding, instance->host->host_no);
}
msleep(1000);
}
+ if (instance->snapdump_wait_time) {
+ megasas_trigger_snap_dump(instance);
+ retval = 1;
+ goto out;
+ }
+
if (atomic_read(&instance->fw_outstanding)) {
- printk("megaraid_sas: pending commands remain after waiting, "
- "will reset adapter.\n");
+ dev_err(&instance->pdev->dev, "pending commands remain after waiting, "
+ "will reset adapter scsi%d.\n",
+ instance->host->host_no);
+ *convert = 1;
retval = 1;
}
+
out:
+ if (!retval && reason == SCSIIO_TIMEOUT_OCR)
+ dev_info(&instance->pdev->dev, "IO is completed, no OCR is required\n");
+
return retval;
}
void megasas_reset_reply_desc(struct megasas_instance *instance)
{
- int i;
+ int i, j, count;
struct fusion_context *fusion;
union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc;
fusion = instance->ctrl_context;
- fusion->last_reply_idx = 0;
- reply_desc = fusion->reply_frames_desc;
- for (i = 0 ; i < fusion->reply_q_depth; i++, reply_desc++)
- reply_desc->Words = ULLONG_MAX;
+ count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
+ count += instance->iopoll_q_count;
+
+ for (i = 0 ; i < count ; i++) {
+ fusion->last_reply_idx[i] = 0;
+ reply_desc = fusion->reply_frames_desc[i];
+ for (j = 0 ; j < fusion->reply_q_depth; j++, reply_desc++)
+ reply_desc->Words = cpu_to_le64(ULLONG_MAX);
+ }
}
-/* Core fusion reset function */
-int megasas_reset_fusion(struct Scsi_Host *shost)
+/*
+ * megasas_refire_mgmt_cmd : Re-fire management commands
+ * @instance: Controller's soft instance
+*/
+static void megasas_refire_mgmt_cmd(struct megasas_instance *instance,
+ bool return_ioctl)
{
- int retval = SUCCESS, i, j, retry = 0;
- struct megasas_instance *instance;
+ int j;
struct megasas_cmd_fusion *cmd_fusion;
struct fusion_context *fusion;
struct megasas_cmd *cmd_mfi;
union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
- u32 host_diag, abs_state, status_reg, reset_adapter;
+ struct MPI2_RAID_SCSI_IO_REQUEST *scsi_io_req;
+ u16 smid;
+ bool refire_cmd = false;
+ u8 result;
+ u32 opcode = 0;
+
+ fusion = instance->ctrl_context;
+
+ /* Re-fire management commands.
+ * Do not traverse complet MPT frame pool. Start from max_scsi_cmds.
+ */
+ for (j = instance->max_scsi_cmds ; j < instance->max_fw_cmds; j++) {
+ cmd_fusion = fusion->cmd_list[j];
+ cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
+ smid = le16_to_cpu(cmd_mfi->context.smid);
+ result = REFIRE_CMD;
+
+ if (!smid)
+ continue;
+
+ req_desc = megasas_get_request_descriptor(instance, smid - 1);
+
+ switch (cmd_mfi->frame->hdr.cmd) {
+ case MFI_CMD_DCMD:
+ opcode = le32_to_cpu(cmd_mfi->frame->dcmd.opcode);
+ /* Do not refire shutdown command */
+ if (opcode == MR_DCMD_CTRL_SHUTDOWN) {
+ cmd_mfi->frame->dcmd.cmd_status = MFI_STAT_OK;
+ result = COMPLETE_CMD;
+ break;
+ }
+
+ refire_cmd = ((opcode != MR_DCMD_LD_MAP_GET_INFO)) &&
+ (opcode != MR_DCMD_SYSTEM_PD_MAP_GET_INFO) &&
+ !(cmd_mfi->flags & DRV_DCMD_SKIP_REFIRE);
+
+ if (!refire_cmd)
+ result = RETURN_CMD;
+
+ break;
+ case MFI_CMD_NVME:
+ if (!instance->support_nvme_passthru) {
+ cmd_mfi->frame->hdr.cmd_status = MFI_STAT_INVALID_CMD;
+ result = COMPLETE_CMD;
+ }
+
+ break;
+ case MFI_CMD_TOOLBOX:
+ if (!instance->support_pci_lane_margining) {
+ cmd_mfi->frame->hdr.cmd_status = MFI_STAT_INVALID_CMD;
+ result = COMPLETE_CMD;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ if (return_ioctl && cmd_mfi->sync_cmd &&
+ cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT) {
+ dev_err(&instance->pdev->dev,
+ "return -EBUSY from %s %d cmd 0x%x opcode 0x%x\n",
+ __func__, __LINE__, cmd_mfi->frame->hdr.cmd,
+ le32_to_cpu(cmd_mfi->frame->dcmd.opcode));
+ cmd_mfi->cmd_status_drv = DCMD_BUSY;
+ result = COMPLETE_CMD;
+ }
+
+ scsi_io_req = (struct MPI2_RAID_SCSI_IO_REQUEST *)
+ cmd_fusion->io_request;
+ if (scsi_io_req->Function == MPI2_FUNCTION_SCSI_TASK_MGMT)
+ result = RETURN_CMD;
+
+ switch (result) {
+ case REFIRE_CMD:
+ megasas_fire_cmd_fusion(instance, req_desc);
+ break;
+ case RETURN_CMD:
+ megasas_return_cmd(instance, cmd_mfi);
+ break;
+ case COMPLETE_CMD:
+ megasas_complete_cmd(instance, cmd_mfi, DID_OK);
+ break;
+ }
+ }
+}
+
+/*
+ * megasas_return_polled_cmds: Return polled mode commands back to the pool
+ * before initiating an OCR.
+ * @instance: Controller's soft instance
+ */
+static void
+megasas_return_polled_cmds(struct megasas_instance *instance)
+{
+ int i;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct fusion_context *fusion;
+ struct megasas_cmd *cmd_mfi;
+
+ fusion = instance->ctrl_context;
+
+ for (i = instance->max_scsi_cmds; i < instance->max_fw_cmds; i++) {
+ cmd_fusion = fusion->cmd_list[i];
+ cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
+
+ if (cmd_mfi->flags & DRV_DCMD_POLLED_MODE) {
+ if (megasas_dbg_lvl & OCR_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "%s %d return cmd 0x%x opcode 0x%x\n",
+ __func__, __LINE__, cmd_mfi->frame->hdr.cmd,
+ le32_to_cpu(cmd_mfi->frame->dcmd.opcode));
+ cmd_mfi->flags &= ~DRV_DCMD_POLLED_MODE;
+ megasas_return_cmd(instance, cmd_mfi);
+ }
+ }
+}
+
+/*
+ * megasas_track_scsiio : Track SCSI IOs outstanding to a SCSI device
+ * @instance: per adapter struct
+ * @channel: the channel assigned by the OS
+ * @id: the id assigned by the OS
+ *
+ * Returns SUCCESS if no IOs pending to SCSI device, else return FAILED
+ */
+
+static int megasas_track_scsiio(struct megasas_instance *instance,
+ int id, int channel)
+{
+ int i, found = 0;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct fusion_context *fusion;
+ fusion = instance->ctrl_context;
+
+ for (i = 0 ; i < instance->max_scsi_cmds; i++) {
+ cmd_fusion = fusion->cmd_list[i];
+ if (cmd_fusion->scmd &&
+ (cmd_fusion->scmd->device->id == id &&
+ cmd_fusion->scmd->device->channel == channel)) {
+ dev_info(&instance->pdev->dev,
+ "SCSI commands pending to target"
+ "channel %d id %d \tSMID: 0x%x\n",
+ channel, id, cmd_fusion->index);
+ scsi_print_command(cmd_fusion->scmd);
+ found = 1;
+ break;
+ }
+ }
+
+ return found ? FAILED : SUCCESS;
+}
+
+/**
+ * megasas_tm_response_code - translation of device response code
+ * @instance: Controller's soft instance
+ * @mpi_reply: MPI reply returned by firmware
+ *
+ * Return nothing.
+ */
+static void
+megasas_tm_response_code(struct megasas_instance *instance,
+ struct MPI2_SCSI_TASK_MANAGE_REPLY *mpi_reply)
+{
+ char *desc;
+
+ switch (mpi_reply->ResponseCode) {
+ case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE:
+ desc = "task management request completed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME:
+ desc = "invalid frame";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
+ desc = "task management request not supported";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_FAILED:
+ desc = "task management request failed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED:
+ desc = "task management request succeeded";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN:
+ desc = "invalid lun";
+ break;
+ case 0xA:
+ desc = "overlapped tag attempted";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
+ desc = "task queued, however not sent to target";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+ dev_dbg(&instance->pdev->dev, "response_code(%01x): %s\n",
+ mpi_reply->ResponseCode, desc);
+ dev_dbg(&instance->pdev->dev,
+ "TerminationCount/DevHandle/Function/TaskType/IOCStat/IOCLoginfo"
+ " 0x%x/0x%x/0x%x/0x%x/0x%x/0x%x\n",
+ mpi_reply->TerminationCount, mpi_reply->DevHandle,
+ mpi_reply->Function, mpi_reply->TaskType,
+ mpi_reply->IOCStatus, mpi_reply->IOCLogInfo);
+}
+
+/**
+ * megasas_issue_tm - main routine for sending tm requests
+ * @instance: per adapter struct
+ * @device_handle: device handle
+ * @channel: the channel assigned by the OS
+ * @id: the id assigned by the OS
+ * @smid_task: smid assigned to the task
+ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in megaraid_sas_fusion.c)
+ * @mr_device_priv_data: private data
+ * Context: user
+ *
+ * MegaRaid use MPT interface for Task Magement request.
+ * A generic API for sending task management requests to firmware.
+ *
+ * Return SUCCESS or FAILED.
+ */
+static int
+megasas_issue_tm(struct megasas_instance *instance, u16 device_handle,
+ uint channel, uint id, u16 smid_task, u8 type,
+ struct MR_PRIV_DEVICE *mr_device_priv_data)
+{
+ struct MR_TASK_MANAGE_REQUEST *mr_request;
+ struct MPI2_SCSI_TASK_MANAGE_REQUEST *mpi_request;
+ unsigned long timeleft;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct megasas_cmd *cmd_mfi;
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
+ struct fusion_context *fusion = NULL;
+ struct megasas_cmd_fusion *scsi_lookup;
+ int rc;
+ int timeout = MEGASAS_DEFAULT_TM_TIMEOUT;
+ struct MPI2_SCSI_TASK_MANAGE_REPLY *mpi_reply;
- instance = (struct megasas_instance *)shost->hostdata;
fusion = instance->ctrl_context;
- if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
- printk(KERN_WARNING "megaraid_sas: Hardware critical error, "
- "returning FAILED.\n");
- retval = FAILED;
+ cmd_mfi = megasas_get_cmd(instance);
+
+ if (!cmd_mfi) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ cmd_fusion = megasas_get_cmd_fusion(instance,
+ instance->max_scsi_cmds + cmd_mfi->index);
+
+ /* Save the smid. To be used for returning the cmd */
+ cmd_mfi->context.smid = cmd_fusion->index;
+
+ req_desc = megasas_get_request_descriptor(instance,
+ (cmd_fusion->index - 1));
+
+ cmd_fusion->request_desc = req_desc;
+ req_desc->Words = 0;
+
+ mr_request = (struct MR_TASK_MANAGE_REQUEST *) cmd_fusion->io_request;
+ memset(mr_request, 0, sizeof(struct MR_TASK_MANAGE_REQUEST));
+ mpi_request = (struct MPI2_SCSI_TASK_MANAGE_REQUEST *) &mr_request->TmRequest;
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(device_handle);
+ mpi_request->TaskType = type;
+ mpi_request->TaskMID = cpu_to_le16(smid_task);
+ mpi_request->LUN[1] = 0;
+
+
+ req_desc = cmd_fusion->request_desc;
+ req_desc->HighPriority.SMID = cpu_to_le16(cmd_fusion->index);
+ req_desc->HighPriority.RequestFlags =
+ (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ req_desc->HighPriority.MSIxIndex = 0;
+ req_desc->HighPriority.LMID = 0;
+ req_desc->HighPriority.Reserved1 = 0;
+
+ if (channel < MEGASAS_MAX_PD_CHANNELS)
+ mr_request->tmReqFlags.isTMForPD = 1;
+ else
+ mr_request->tmReqFlags.isTMForLD = 1;
+
+ init_completion(&cmd_fusion->done);
+ megasas_fire_cmd_fusion(instance, req_desc);
+
+ switch (type) {
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
+ timeout = mr_device_priv_data->task_abort_tmo;
+ break;
+ case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ timeout = mr_device_priv_data->target_reset_tmo;
+ break;
+ }
+
+ timeleft = wait_for_completion_timeout(&cmd_fusion->done, timeout * HZ);
+
+ if (!timeleft) {
+ dev_err(&instance->pdev->dev,
+ "task mgmt type 0x%x timed out\n", type);
+ mutex_unlock(&instance->reset_mutex);
+ rc = megasas_reset_fusion(instance->host, MFI_IO_TIMEOUT_OCR);
+ mutex_lock(&instance->reset_mutex);
+ return rc;
+ }
+
+ mpi_reply = (struct MPI2_SCSI_TASK_MANAGE_REPLY *) &mr_request->TMReply;
+ megasas_tm_response_code(instance, mpi_reply);
+
+ megasas_return_cmd(instance, cmd_mfi);
+ rc = SUCCESS;
+ switch (type) {
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
+ scsi_lookup = fusion->cmd_list[smid_task - 1];
+
+ if (scsi_lookup->scmd == NULL)
+ break;
+ else {
+ instance->instancet->disable_intr(instance);
+ megasas_sync_irqs((unsigned long)instance);
+ instance->instancet->enable_intr(instance);
+ megasas_enable_irq_poll(instance);
+ if (scsi_lookup->scmd == NULL)
+ break;
+ }
+ rc = FAILED;
+ break;
+
+ case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ if ((channel == 0xFFFFFFFF) && (id == 0xFFFFFFFF))
+ break;
+ instance->instancet->disable_intr(instance);
+ megasas_sync_irqs((unsigned long)instance);
+ rc = megasas_track_scsiio(instance, id, channel);
+ instance->instancet->enable_intr(instance);
+ megasas_enable_irq_poll(instance);
+
+ break;
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET:
+ case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK:
+ break;
+ default:
+ rc = FAILED;
+ break;
+ }
+
+ return rc;
+
+}
+
+/*
+ * megasas_fusion_smid_lookup : Look for fusion command corresponding to SCSI
+ * @instance: per adapter struct
+ *
+ * Return Non Zero index, if SMID found in outstanding commands
+ */
+static u16 megasas_fusion_smid_lookup(struct scsi_cmnd *scmd)
+{
+ int i, ret = 0;
+ struct megasas_instance *instance;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct fusion_context *fusion;
+
+ instance = (struct megasas_instance *)scmd->device->host->hostdata;
+
+ fusion = instance->ctrl_context;
+
+ for (i = 0; i < instance->max_scsi_cmds; i++) {
+ cmd_fusion = fusion->cmd_list[i];
+ if (cmd_fusion->scmd && (cmd_fusion->scmd == scmd)) {
+ scmd_printk(KERN_NOTICE, scmd, "Abort request is for"
+ " SMID: %d\n", cmd_fusion->index);
+ ret = cmd_fusion->index;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+* megasas_get_tm_devhandle - Get devhandle for TM request
+* @sdev- OS provided scsi device
+*
+* Returns- devhandle/targetID of SCSI device
+*/
+static u16 megasas_get_tm_devhandle(struct scsi_device *sdev)
+{
+ u16 pd_index = 0;
+ u32 device_id;
+ struct megasas_instance *instance;
+ struct fusion_context *fusion;
+ struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync;
+ u16 devhandle = (u16)ULONG_MAX;
+
+ instance = (struct megasas_instance *)sdev->host->hostdata;
+ fusion = instance->ctrl_context;
+
+ if (!MEGASAS_IS_LOGICAL(sdev)) {
+ if (instance->use_seqnum_jbod_fp) {
+ pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL)
+ + sdev->id;
+ pd_sync = (void *)fusion->pd_seq_sync
+ [(instance->pd_seq_map_id - 1) & 1];
+ devhandle = pd_sync->seq[pd_index].devHandle;
+ } else
+ sdev_printk(KERN_ERR, sdev, "Firmware expose tmCapable"
+ " without JBOD MAP support from %s %d\n", __func__, __LINE__);
+ } else {
+ device_id = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL)
+ + sdev->id;
+ devhandle = device_id;
+ }
+
+ return devhandle;
+}
+
+/*
+ * megasas_task_abort_fusion : SCSI task abort function for fusion adapters
+ * @scmd : pointer to scsi command object
+ *
+ * Return SUCCESS, if command aborted else FAILED
+ */
+
+int megasas_task_abort_fusion(struct scsi_cmnd *scmd)
+{
+ struct megasas_instance *instance;
+ u16 smid, devhandle;
+ int ret;
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+ mr_device_priv_data = scmd->device->hostdata;
+
+ instance = (struct megasas_instance *)scmd->device->host->hostdata;
+
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) {
+ dev_err(&instance->pdev->dev, "Controller is not OPERATIONAL,"
+ "SCSI host:%d\n", instance->host->host_no);
+ ret = FAILED;
+ return ret;
+ }
+
+ if (!mr_device_priv_data) {
+ sdev_printk(KERN_INFO, scmd->device, "device been deleted! "
+ "scmd(%p)\n", scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ ret = SUCCESS;
+ goto out;
+ }
+
+ if (!mr_device_priv_data->is_tm_capable) {
+ ret = FAILED;
goto out;
}
mutex_lock(&instance->reset_mutex);
+
+ smid = megasas_fusion_smid_lookup(scmd);
+
+ if (!smid) {
+ ret = SUCCESS;
+ scmd_printk(KERN_NOTICE, scmd, "Command for which abort is"
+ " issued is not found in outstanding commands\n");
+ mutex_unlock(&instance->reset_mutex);
+ goto out;
+ }
+
+ devhandle = megasas_get_tm_devhandle(scmd->device);
+
+ if (devhandle == (u16)ULONG_MAX) {
+ ret = FAILED;
+ sdev_printk(KERN_INFO, scmd->device,
+ "task abort issued for invalid devhandle\n");
+ mutex_unlock(&instance->reset_mutex);
+ goto out;
+ }
+ sdev_printk(KERN_INFO, scmd->device,
+ "attempting task abort! scmd(0x%p) tm_dev_handle 0x%x\n",
+ scmd, devhandle);
+
+ mr_device_priv_data->tm_busy = true;
+ ret = megasas_issue_tm(instance, devhandle,
+ scmd->device->channel, scmd->device->id, smid,
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
+ mr_device_priv_data);
+ mr_device_priv_data->tm_busy = false;
+
+ mutex_unlock(&instance->reset_mutex);
+ scmd_printk(KERN_INFO, scmd, "task abort %s!! scmd(0x%p)\n",
+ ((ret == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+out:
+ scsi_print_command(scmd);
+ if (megasas_dbg_lvl & TM_DEBUG)
+ megasas_dump_fusion_io(scmd);
+
+ return ret;
+}
+
+/*
+ * megasas_reset_target_fusion : target reset function for fusion adapters
+ * scmd: SCSI command pointer
+ *
+ * Returns SUCCESS if all commands associated with target aborted else FAILED
+ */
+
+int megasas_reset_target_fusion(struct scsi_cmnd *scmd)
+{
+
+ struct megasas_instance *instance;
+ int ret = FAILED;
+ u16 devhandle;
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+ mr_device_priv_data = scmd->device->hostdata;
+
+ instance = (struct megasas_instance *)scmd->device->host->hostdata;
+
+ if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) {
+ dev_err(&instance->pdev->dev, "Controller is not OPERATIONAL,"
+ "SCSI host:%d\n", instance->host->host_no);
+ ret = FAILED;
+ return ret;
+ }
+
+ if (!mr_device_priv_data) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "device been deleted! scmd: (0x%p)\n", scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ ret = SUCCESS;
+ goto out;
+ }
+
+ if (!mr_device_priv_data->is_tm_capable) {
+ ret = FAILED;
+ goto out;
+ }
+
+ mutex_lock(&instance->reset_mutex);
+ devhandle = megasas_get_tm_devhandle(scmd->device);
+
+ if (devhandle == (u16)ULONG_MAX) {
+ ret = FAILED;
+ sdev_printk(KERN_INFO, scmd->device,
+ "target reset issued for invalid devhandle\n");
+ mutex_unlock(&instance->reset_mutex);
+ goto out;
+ }
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "attempting target reset! scmd(0x%p) tm_dev_handle: 0x%x\n",
+ scmd, devhandle);
+ mr_device_priv_data->tm_busy = true;
+ ret = megasas_issue_tm(instance, devhandle,
+ scmd->device->channel, scmd->device->id, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
+ mr_device_priv_data);
+ mr_device_priv_data->tm_busy = false;
+ mutex_unlock(&instance->reset_mutex);
+ scmd_printk(KERN_NOTICE, scmd, "target reset %s!!\n",
+ (ret == SUCCESS) ? "SUCCESS" : "FAILED");
+
+out:
+ return ret;
+}
+
+/*SRIOV get other instance in cluster if any*/
+static struct
+megasas_instance *megasas_get_peer_instance(struct megasas_instance *instance)
+{
+ int i;
+
+ for (i = 0; i < MAX_MGMT_ADAPTERS; i++) {
+ if (megasas_mgmt_info.instance[i] &&
+ (megasas_mgmt_info.instance[i] != instance) &&
+ megasas_mgmt_info.instance[i]->requestorId &&
+ megasas_mgmt_info.instance[i]->peerIsPresent &&
+ (memcmp((megasas_mgmt_info.instance[i]->clusterId),
+ instance->clusterId, MEGASAS_CLUSTER_ID_SIZE) == 0))
+ return megasas_mgmt_info.instance[i];
+ }
+ return NULL;
+}
+
+/* Check for a second path that is currently UP */
+int megasas_check_mpio_paths(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd)
+{
+ struct megasas_instance *peer_instance = NULL;
+ int retval = (DID_REQUEUE << 16);
+
+ if (instance->peerIsPresent) {
+ peer_instance = megasas_get_peer_instance(instance);
+ if ((peer_instance) &&
+ (atomic_read(&peer_instance->adprecovery) ==
+ MEGASAS_HBA_OPERATIONAL))
+ retval = (DID_NO_CONNECT << 16);
+ }
+ return retval;
+}
+
+/* Core fusion reset function */
+int megasas_reset_fusion(struct Scsi_Host *shost, int reason)
+{
+ int retval = SUCCESS, i, j, convert = 0;
+ struct megasas_instance *instance;
+ struct megasas_cmd_fusion *cmd_fusion, *r1_cmd;
+ struct fusion_context *fusion;
+ u32 abs_state, status_reg, reset_adapter, fpio_count = 0;
+ u32 io_timeout_in_crash_mode = 0;
+ struct scsi_cmnd *scmd_local = NULL;
+ struct scsi_device *sdev;
+ int ret_target_prop = DCMD_FAILED;
+ bool is_target_prop = false;
+ bool do_adp_reset = true;
+ int max_reset_tries = MEGASAS_FUSION_MAX_RESET_TRIES;
+
+ instance = (struct megasas_instance *)shost->hostdata;
+ fusion = instance->ctrl_context;
+
+ mutex_lock(&instance->reset_mutex);
+
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ dev_warn(&instance->pdev->dev, "Hardware critical error, "
+ "returning FAILED for scsi%d.\n",
+ instance->host->host_no);
+ mutex_unlock(&instance->reset_mutex);
+ return FAILED;
+ }
+ status_reg = instance->instancet->read_fw_status_reg(instance);
+ abs_state = status_reg & MFI_STATE_MASK;
+
+ /* IO timeout detected, forcibly put FW in FAULT state */
+ if (abs_state != MFI_STATE_FAULT && instance->crash_dump_buf &&
+ instance->crash_dump_app_support && reason) {
+ dev_info(&instance->pdev->dev, "IO/DCMD timeout is detected, "
+ "forcibly FAULT Firmware\n");
+ atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
+ status_reg = megasas_readl(instance, &instance->reg_set->doorbell);
+ writel(status_reg | MFI_STATE_FORCE_OCR,
+ &instance->reg_set->doorbell);
+ readl(&instance->reg_set->doorbell);
+ mutex_unlock(&instance->reset_mutex);
+ do {
+ ssleep(3);
+ io_timeout_in_crash_mode++;
+ dev_dbg(&instance->pdev->dev, "waiting for [%d] "
+ "seconds for crash dump collection and OCR "
+ "to be done\n", (io_timeout_in_crash_mode * 3));
+ } while ((atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) &&
+ (io_timeout_in_crash_mode < 80));
+
+ if (atomic_read(&instance->adprecovery) == MEGASAS_HBA_OPERATIONAL) {
+ dev_info(&instance->pdev->dev, "OCR done for IO "
+ "timeout case\n");
+ retval = SUCCESS;
+ } else {
+ dev_info(&instance->pdev->dev, "Controller is not "
+ "operational after 240 seconds wait for IO "
+ "timeout case in FW crash dump mode\n do "
+ "OCR/kill adapter\n");
+ retval = megasas_reset_fusion(shost, 0);
+ }
+ return retval;
+ }
+
+ if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+ timer_delete_sync(&instance->sriov_heartbeat_timer);
set_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags);
- instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT;
- instance->instancet->disable_intr(instance->reg_set);
- msleep(1000);
+ set_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, &instance->reset_flags);
+ atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_POLLING);
+ instance->instancet->disable_intr(instance);
+ megasas_sync_irqs((unsigned long)instance);
/* First try waiting for commands to complete */
- if (megasas_wait_for_outstanding_fusion(instance)) {
- printk(KERN_WARNING "megaraid_sas: resetting fusion "
- "adapter.\n");
+ if (megasas_wait_for_outstanding_fusion(instance, reason,
+ &convert)) {
+ atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
+ dev_warn(&instance->pdev->dev, "resetting fusion "
+ "adapter scsi%d.\n", instance->host->host_no);
+ if (convert)
+ reason = 0;
+
+ if (megasas_dbg_lvl & OCR_DEBUG)
+ dev_info(&instance->pdev->dev, "\nPending SCSI commands:\n");
+
/* Now return commands back to the OS */
- for (i = 0 ; i < instance->max_fw_cmds; i++) {
+ for (i = 0 ; i < instance->max_scsi_cmds; i++) {
cmd_fusion = fusion->cmd_list[i];
+ /*check for extra commands issued by driver*/
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ r1_cmd = fusion->cmd_list[i + instance->max_fw_cmds];
+ megasas_return_cmd_fusion(instance, r1_cmd);
+ }
+ scmd_local = cmd_fusion->scmd;
if (cmd_fusion->scmd) {
- scsi_dma_unmap(cmd_fusion->scmd);
- cmd_fusion->scmd->result = (DID_RESET << 16);
- cmd_fusion->scmd->scsi_done(cmd_fusion->scmd);
+ if (megasas_dbg_lvl & OCR_DEBUG) {
+ sdev_printk(KERN_INFO,
+ cmd_fusion->scmd->device, "SMID: 0x%x\n",
+ cmd_fusion->index);
+ megasas_dump_fusion_io(cmd_fusion->scmd);
+ }
+
+ if (cmd_fusion->io_request->Function ==
+ MPI2_FUNCTION_SCSI_IO_REQUEST)
+ fpio_count++;
+
+ scmd_local->result =
+ megasas_check_mpio_paths(instance,
+ scmd_local);
+ if (instance->ldio_threshold &&
+ megasas_cmd_type(scmd_local) == READ_WRITE_LDIO)
+ atomic_dec(&instance->ldio_outstanding);
megasas_return_cmd_fusion(instance, cmd_fusion);
- atomic_dec(&instance->fw_outstanding);
+ scsi_dma_unmap(scmd_local);
+ scsi_done(scmd_local);
}
}
- status_reg = instance->instancet->read_fw_status_reg(
- instance->reg_set);
+ dev_info(&instance->pdev->dev, "Outstanding fastpath IOs: %d\n",
+ fpio_count);
+
+ atomic_set(&instance->fw_outstanding, 0);
+
+ status_reg = instance->instancet->read_fw_status_reg(instance);
abs_state = status_reg & MFI_STATE_MASK;
reset_adapter = status_reg & MFI_RESET_ADAPTER;
if (instance->disableOnlineCtrlReset ||
(abs_state == MFI_STATE_FAULT && !reset_adapter)) {
/* Reset not supported, kill adapter */
- printk(KERN_WARNING "megaraid_sas: Reset not supported"
- ", killing adapter.\n");
- megaraid_sas_kill_hba(instance);
- instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
- retval = FAILED;
- goto out;
+ dev_warn(&instance->pdev->dev, "Reset not supported"
+ ", killing adapter scsi%d.\n",
+ instance->host->host_no);
+ goto kill_hba;
+ }
+
+ /* Let SR-IOV VF & PF sync up if there was a HB failure */
+ if (instance->requestorId && !reason) {
+ msleep(MEGASAS_OCR_SETTLE_TIME_VF);
+ do_adp_reset = false;
+ max_reset_tries = MEGASAS_SRIOV_MAX_RESET_TRIES_VF;
}
/* Now try to reset the chip */
- for (i = 0; i < MEGASAS_FUSION_MAX_RESET_TRIES; i++) {
- writel(MPI2_WRSEQ_FLUSH_KEY_VALUE,
- &instance->reg_set->fusion_seq_offset);
- writel(MPI2_WRSEQ_1ST_KEY_VALUE,
- &instance->reg_set->fusion_seq_offset);
- writel(MPI2_WRSEQ_2ND_KEY_VALUE,
- &instance->reg_set->fusion_seq_offset);
- writel(MPI2_WRSEQ_3RD_KEY_VALUE,
- &instance->reg_set->fusion_seq_offset);
- writel(MPI2_WRSEQ_4TH_KEY_VALUE,
- &instance->reg_set->fusion_seq_offset);
- writel(MPI2_WRSEQ_5TH_KEY_VALUE,
- &instance->reg_set->fusion_seq_offset);
- writel(MPI2_WRSEQ_6TH_KEY_VALUE,
- &instance->reg_set->fusion_seq_offset);
-
- /* Check that the diag write enable (DRWE) bit is on */
- host_diag = readl(&instance->reg_set->fusion_host_diag);
- retry = 0;
- while (!(host_diag & HOST_DIAG_WRITE_ENABLE)) {
- msleep(100);
- host_diag =
- readl(&instance->reg_set->fusion_host_diag);
- if (retry++ == 100) {
- printk(KERN_WARNING "megaraid_sas: "
- "Host diag unlock failed!\n");
- break;
- }
- }
- if (!(host_diag & HOST_DIAG_WRITE_ENABLE))
+ for (i = 0; i < max_reset_tries; i++) {
+ /*
+ * Do adp reset and wait for
+ * controller to transition to ready
+ */
+ if (megasas_adp_reset_wait_for_ready(instance,
+ do_adp_reset, 1) == FAILED)
continue;
- /* Send chip reset command */
- writel(host_diag | HOST_DIAG_RESET_ADAPTER,
- &instance->reg_set->fusion_host_diag);
- msleep(3000);
-
- /* Make sure reset adapter bit is cleared */
- host_diag = readl(&instance->reg_set->fusion_host_diag);
- retry = 0;
- while (host_diag & HOST_DIAG_RESET_ADAPTER) {
- msleep(100);
- host_diag =
- readl(&instance->reg_set->fusion_host_diag);
- if (retry++ == 1000) {
- printk(KERN_WARNING "megaraid_sas: "
- "Diag reset adapter never "
- "cleared!\n");
- break;
- }
- }
- if (host_diag & HOST_DIAG_RESET_ADAPTER)
+ /* Wait for FW to become ready */
+ if (megasas_transition_to_ready(instance, 1)) {
+ dev_warn(&instance->pdev->dev,
+ "Failed to transition controller to ready for "
+ "scsi%d.\n", instance->host->host_no);
continue;
-
- abs_state =
- instance->instancet->read_fw_status_reg(
- instance->reg_set) & MFI_STATE_MASK;
- retry = 0;
-
- while ((abs_state <= MFI_STATE_FW_INIT) &&
- (retry++ < 1000)) {
- msleep(100);
- abs_state =
- instance->instancet->read_fw_status_reg(
- instance->reg_set) & MFI_STATE_MASK;
}
- if (abs_state <= MFI_STATE_FW_INIT) {
- printk(KERN_WARNING "megaraid_sas: firmware "
- "state < MFI_STATE_FW_INIT, state = "
- "0x%x\n", abs_state);
+ megasas_reset_reply_desc(instance);
+ megasas_fusion_update_can_queue(instance, OCR_CONTEXT);
+
+ if (megasas_ioc_init_fusion(instance)) {
continue;
}
- /* Wait for FW to become ready */
- if (megasas_transition_to_ready(instance)) {
- printk(KERN_WARNING "megaraid_sas: Failed to "
- "transition controller to ready.\n");
- continue;
+ if (megasas_get_ctrl_info(instance)) {
+ dev_info(&instance->pdev->dev,
+ "Failed from %s %d\n",
+ __func__, __LINE__);
+ goto kill_hba;
}
- megasas_reset_reply_desc(instance);
- if (megasas_ioc_init_fusion(instance)) {
- printk(KERN_WARNING "megaraid_sas: "
- "megasas_ioc_init_fusion() failed!\n");
+ megasas_refire_mgmt_cmd(instance,
+ (i == (MEGASAS_FUSION_MAX_RESET_TRIES - 1)
+ ? 1 : 0));
+
+ /* Reset load balance info */
+ if (fusion->load_balance_info)
+ memset(fusion->load_balance_info, 0,
+ (sizeof(struct LD_LOAD_BALANCE_INFO) *
+ MAX_LOGICAL_DRIVES_EXT));
+
+ if (!megasas_get_map_info(instance)) {
+ megasas_sync_map_info(instance);
+ } else {
+ /*
+ * Return pending polled mode cmds before
+ * retrying OCR
+ */
+ megasas_return_polled_cmds(instance);
continue;
}
- instance->instancet->enable_intr(instance->reg_set);
- instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
-
- /* Re-fire management commands */
- for (j = 0 ; j < instance->max_fw_cmds; j++) {
- cmd_fusion = fusion->cmd_list[j];
- if (cmd_fusion->sync_cmd_idx !=
- (u32)ULONG_MAX) {
- cmd_mfi =
- instance->
- cmd_list[cmd_fusion->sync_cmd_idx];
- if (cmd_mfi->frame->dcmd.opcode ==
- MR_DCMD_LD_MAP_GET_INFO) {
- megasas_return_cmd(instance,
- cmd_mfi);
- megasas_return_cmd_fusion(
- instance, cmd_fusion);
- } else {
- req_desc =
- megasas_get_request_descriptor(
- instance,
- cmd_mfi->context.smid
- -1);
- if (!req_desc)
- printk(KERN_WARNING
- "req_desc NULL"
- "\n");
- else {
- instance->instancet->
- fire_cmd(instance,
- req_desc->
- u.low,
- req_desc->
- u.high,
- instance->
- reg_set);
- }
- }
+ megasas_setup_jbod_map(instance);
+
+ /* reset stream detection array */
+ if (instance->adapter_type >= VENTURA_SERIES) {
+ for (j = 0; j < MAX_LOGICAL_DRIVES_EXT; ++j) {
+ memset(fusion->stream_detect_by_ld[j],
+ 0, sizeof(struct LD_STREAM_DETECT));
+ fusion->stream_detect_by_ld[j]->mru_bit_map
+ = MR_STREAM_BITMAP;
}
}
- /* Reset load balance info */
- memset(fusion->load_balance_info, 0,
- sizeof(struct LD_LOAD_BALANCE_INFO)
- *MAX_LOGICAL_DRIVES);
+ clear_bit(MEGASAS_FUSION_IN_RESET,
+ &instance->reset_flags);
+ instance->instancet->enable_intr(instance);
+ megasas_enable_irq_poll(instance);
+ shost_for_each_device(sdev, shost) {
+ if ((instance->tgt_prop) &&
+ (instance->nvme_page_size))
+ ret_target_prop = megasas_get_target_prop(instance, sdev);
+
+ is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false;
+ megasas_set_dynamic_target_properties(sdev, NULL,
+ is_target_prop);
+ }
- if (!megasas_get_map_info(instance))
- megasas_sync_map_info(instance);
+ status_reg = instance->instancet->read_fw_status_reg
+ (instance);
+ abs_state = status_reg & MFI_STATE_MASK;
+ if (abs_state != MFI_STATE_OPERATIONAL) {
+ dev_info(&instance->pdev->dev,
+ "Adapter is not OPERATIONAL, state 0x%x for scsi:%d\n",
+ abs_state, instance->host->host_no);
+ goto out;
+ }
+ atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL);
+
+ dev_info(&instance->pdev->dev,
+ "Adapter is OPERATIONAL for scsi:%d\n",
+ instance->host->host_no);
+
+ /* Restart SR-IOV heartbeat */
+ if (instance->requestorId) {
+ if (!megasas_sriov_start_heartbeat(instance, 0))
+ megasas_start_timer(instance);
+ else
+ instance->skip_heartbeat_timer_del = 1;
+ }
+
+ if (instance->crash_dump_drv_support &&
+ instance->crash_dump_app_support)
+ megasas_set_crash_dump_params(instance,
+ MR_CRASH_BUF_TURN_ON);
+ else
+ megasas_set_crash_dump_params(instance,
+ MR_CRASH_BUF_TURN_OFF);
+
+ if (instance->snapdump_wait_time) {
+ megasas_get_snapdump_properties(instance);
+ dev_info(&instance->pdev->dev,
+ "Snap dump wait time\t: %d\n",
+ instance->snapdump_wait_time);
+ }
- /* Adapter reset completed successfully */
- printk(KERN_WARNING "megaraid_sas: Reset "
- "successful.\n");
retval = SUCCESS;
+
+ /* Adapter reset completed successfully */
+ dev_warn(&instance->pdev->dev,
+ "Reset successful for scsi%d.\n",
+ instance->host->host_no);
+
goto out;
}
/* Reset failed, kill the adapter */
- printk(KERN_WARNING "megaraid_sas: Reset failed, killing "
- "adapter.\n");
- megaraid_sas_kill_hba(instance);
- retval = FAILED;
+ dev_warn(&instance->pdev->dev, "Reset failed, killing "
+ "adapter scsi%d.\n", instance->host->host_no);
+ goto kill_hba;
} else {
- instance->instancet->enable_intr(instance->reg_set);
- instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
+ /* For VF: Restart HB timer if we didn't OCR */
+ if (instance->requestorId) {
+ megasas_start_timer(instance);
+ }
+ clear_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags);
+ instance->instancet->enable_intr(instance);
+ megasas_enable_irq_poll(instance);
+ atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL);
+ goto out;
}
+kill_hba:
+ megaraid_sas_kill_hba(instance);
+ megasas_enable_irq_poll(instance);
+ instance->skip_heartbeat_timer_del = 1;
+ retval = FAILED;
out:
- clear_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags);
+ clear_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, &instance->reset_flags);
mutex_unlock(&instance->reset_mutex);
return retval;
}
+/* Fusion Crash dump collection */
+static void megasas_fusion_crash_dump(struct megasas_instance *instance)
+{
+ u32 status_reg;
+ u8 partial_copy = 0;
+ int wait = 0;
+
+
+ status_reg = instance->instancet->read_fw_status_reg(instance);
+
+ /*
+ * Allocate host crash buffers to copy data from 1 MB DMA crash buffer
+ * to host crash buffers
+ */
+ if (instance->drv_buf_index == 0) {
+ /* Buffer is already allocated for old Crash dump.
+ * Do OCR and do not wait for crash dump collection
+ */
+ if (instance->drv_buf_alloc) {
+ dev_info(&instance->pdev->dev, "earlier crash dump is "
+ "not yet copied by application, ignoring this "
+ "crash dump and initiating OCR\n");
+ status_reg |= MFI_STATE_CRASH_DUMP_DONE;
+ writel(status_reg,
+ &instance->reg_set->outbound_scratch_pad_0);
+ readl(&instance->reg_set->outbound_scratch_pad_0);
+ return;
+ }
+ megasas_alloc_host_crash_buffer(instance);
+ dev_info(&instance->pdev->dev, "Number of host crash buffers "
+ "allocated: %d\n", instance->drv_buf_alloc);
+ }
+
+ while (!(status_reg & MFI_STATE_CRASH_DUMP_DONE) &&
+ (wait < MEGASAS_WATCHDOG_WAIT_COUNT)) {
+ if (!(status_reg & MFI_STATE_DMADONE)) {
+ /*
+ * Next crash dump buffer is not yet DMA'd by FW
+ * Check after 10ms. Wait for 1 second for FW to
+ * post the next buffer. If not bail out.
+ */
+ wait++;
+ msleep(MEGASAS_WAIT_FOR_NEXT_DMA_MSECS);
+ status_reg = instance->instancet->read_fw_status_reg(
+ instance);
+ continue;
+ }
+
+ wait = 0;
+ if (instance->drv_buf_index >= instance->drv_buf_alloc) {
+ dev_info(&instance->pdev->dev,
+ "Driver is done copying the buffer: %d\n",
+ instance->drv_buf_alloc);
+ status_reg |= MFI_STATE_CRASH_DUMP_DONE;
+ partial_copy = 1;
+ break;
+ } else {
+ memcpy(instance->crash_buf[instance->drv_buf_index],
+ instance->crash_dump_buf, CRASH_DMA_BUF_SIZE);
+ instance->drv_buf_index++;
+ status_reg &= ~MFI_STATE_DMADONE;
+ }
+
+ writel(status_reg, &instance->reg_set->outbound_scratch_pad_0);
+ readl(&instance->reg_set->outbound_scratch_pad_0);
+
+ msleep(MEGASAS_WAIT_FOR_NEXT_DMA_MSECS);
+ status_reg = instance->instancet->read_fw_status_reg(instance);
+ }
+
+ if (status_reg & MFI_STATE_CRASH_DUMP_DONE) {
+ dev_info(&instance->pdev->dev, "Crash Dump is available,number "
+ "of copied buffers: %d\n", instance->drv_buf_index);
+ instance->fw_crash_buffer_size = instance->drv_buf_index;
+ instance->fw_crash_state = AVAILABLE;
+ instance->drv_buf_index = 0;
+ writel(status_reg, &instance->reg_set->outbound_scratch_pad_0);
+ readl(&instance->reg_set->outbound_scratch_pad_0);
+ if (!partial_copy)
+ megasas_reset_fusion(instance->host, 0);
+ }
+}
+
+
/* Fusion OCR work queue */
void megasas_fusion_ocr_wq(struct work_struct *work)
{
struct megasas_instance *instance =
container_of(work, struct megasas_instance, work_init);
- megasas_reset_fusion(instance->host);
+ megasas_reset_fusion(instance->host, 0);
+}
+
+/* Allocate fusion context */
+int
+megasas_alloc_fusion_context(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion;
+
+ instance->ctrl_context = kzalloc(sizeof(struct fusion_context),
+ GFP_KERNEL);
+ if (!instance->ctrl_context) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ fusion = instance->ctrl_context;
+
+ fusion->log_to_span_pages = get_order(MAX_LOGICAL_DRIVES_EXT *
+ sizeof(LD_SPAN_INFO));
+ fusion->log_to_span =
+ (PLD_SPAN_INFO)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ fusion->log_to_span_pages);
+ if (!fusion->log_to_span) {
+ fusion->log_to_span =
+ vzalloc(array_size(MAX_LOGICAL_DRIVES_EXT,
+ sizeof(LD_SPAN_INFO)));
+ if (!fusion->log_to_span) {
+ dev_err(&instance->pdev->dev, "Failed from %s %d\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ }
+
+ fusion->load_balance_info_pages = get_order(MAX_LOGICAL_DRIVES_EXT *
+ sizeof(struct LD_LOAD_BALANCE_INFO));
+ fusion->load_balance_info =
+ (struct LD_LOAD_BALANCE_INFO *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ fusion->load_balance_info_pages);
+ if (!fusion->load_balance_info) {
+ fusion->load_balance_info =
+ vzalloc(array_size(MAX_LOGICAL_DRIVES_EXT,
+ sizeof(struct LD_LOAD_BALANCE_INFO)));
+ if (!fusion->load_balance_info)
+ dev_err(&instance->pdev->dev, "Failed to allocate load_balance_info, "
+ "continuing without Load Balance support\n");
+ }
+
+ return 0;
+}
+
+void
+megasas_free_fusion_context(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion = instance->ctrl_context;
+
+ if (fusion) {
+ if (fusion->load_balance_info) {
+ if (is_vmalloc_addr(fusion->load_balance_info))
+ vfree(fusion->load_balance_info);
+ else
+ free_pages((ulong)fusion->load_balance_info,
+ fusion->load_balance_info_pages);
+ }
+
+ if (fusion->log_to_span) {
+ if (is_vmalloc_addr(fusion->log_to_span))
+ vfree(fusion->log_to_span);
+ else
+ free_pages((ulong)fusion->log_to_span,
+ fusion->log_to_span_pages);
+ }
+
+ kfree(fusion);
+ }
}
struct megasas_instance_template megasas_instance_template_fusion = {
- .fire_cmd = megasas_fire_cmd_fusion,
.enable_intr = megasas_enable_intr_fusion,
.disable_intr = megasas_disable_intr_fusion,
.clear_intr = megasas_clear_intr_fusion,
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h
index 82b577a72c8b..ddeea0ee2834 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.h
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h
@@ -1,40 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2011 LSI Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Copyright (c) 2009-2013 LSI Corporation
+ * Copyright (c) 2013-2016 Avago Technologies
+ * Copyright (c) 2016-2018 Broadcom Inc.
*
* FILE: megaraid_sas_fusion.h
*
- * Authors: LSI Corporation
+ * Authors: Broadcom Inc.
* Manoj Jose
* Sumant Patro
+ * Kashyap Desai <kashyap.desai@broadcom.com>
+ * Sumit Saxena <sumit.saxena@broadcom.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
- *
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Send feedback to: megaraidlinux.pdl@broadcom.com
*/
#ifndef _MEGARAID_SAS_FUSION_H_
#define _MEGARAID_SAS_FUSION_H_
/* Fusion defines */
-#define MEGASAS_MAX_SZ_CHAIN_FRAME 1024
+#define MEGASAS_CHAIN_FRAME_SZ_MIN 1024
#define MFI_FUSION_ENABLE_INTERRUPT_MASK (0x00000009)
+#define MEGASAS_MAX_CHAIN_SHIFT 5
+#define MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK 0x400000
+#define MEGASAS_MAX_CHAIN_SIZE_MASK 0x3E0
+#define MEGASAS_256K_IO 128
+#define MEGASAS_1MB_IO (MEGASAS_256K_IO * 4)
#define MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE 256
#define MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST 0xF0
#define MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST 0xF1
@@ -43,6 +36,19 @@
#define HOST_DIAG_WRITE_ENABLE 0x80
#define HOST_DIAG_RESET_ADAPTER 0x4
#define MEGASAS_FUSION_MAX_RESET_TRIES 3
+#define MAX_MSIX_QUEUES_FUSION 128
+#define RDPQ_MAX_INDEX_IN_ONE_CHUNK 16
+#define RDPQ_MAX_CHUNK_COUNT (MAX_MSIX_QUEUES_FUSION / RDPQ_MAX_INDEX_IN_ONE_CHUNK)
+
+/* Invader defines */
+#define MPI2_TYPE_CUDA 0x2
+#define MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH 0x4000
+#define MR_RL_FLAGS_GRANT_DESTINATION_CPU0 0x00
+#define MR_RL_FLAGS_GRANT_DESTINATION_CPU1 0x10
+#define MR_RL_FLAGS_GRANT_DESTINATION_CUDA 0x80
+#define MR_RL_FLAGS_SEQ_NUM_ENABLE 0x8
+#define MR_RL_WRITE_THROUGH_MODE 0x00
+#define MR_RL_WRITE_BACK_MODE 0x01
/* T10 PI defines */
#define MR_PROT_INFO_TYPE_CONTROLLER 0x8
@@ -52,7 +58,9 @@
#define MEGASAS_SCSI_ADDL_CDB_LEN 0x18
#define MEGASAS_RD_WR_PROTECT_CHECK_ALL 0x20
#define MEGASAS_RD_WR_PROTECT_CHECK_NONE 0x60
-#define MEGASAS_EEDPBLOCKSIZE 512
+
+#define MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET (0x0000030C)
+#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C)
/*
* Raid context flags
@@ -63,6 +71,12 @@
enum MR_RAID_FLAGS_IO_SUB_TYPE {
MR_RAID_FLAGS_IO_SUB_TYPE_NONE = 0,
MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD = 1,
+ MR_RAID_FLAGS_IO_SUB_TYPE_RMW_DATA = 2,
+ MR_RAID_FLAGS_IO_SUB_TYPE_RMW_P = 3,
+ MR_RAID_FLAGS_IO_SUB_TYPE_RMW_Q = 4,
+ MR_RAID_FLAGS_IO_SUB_TYPE_CACHE_BYPASS = 6,
+ MR_RAID_FLAGS_IO_SUB_TYPE_LDIO_BW_LIMIT = 7,
+ MR_RAID_FLAGS_IO_SUB_TYPE_R56_DIV_OFFLOAD = 8
};
/*
@@ -70,33 +84,154 @@ enum MR_RAID_FLAGS_IO_SUB_TYPE {
*/
#define MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO 0x7
#define MEGASAS_REQ_DESCRIPT_FLAGS_MFA 0x1
-
+#define MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK 0x2
#define MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT 1
#define MEGASAS_FP_CMD_LEN 16
#define MEGASAS_FUSION_IN_RESET 0
+#define MEGASAS_FUSION_OCR_NOT_POSSIBLE 1
+#define RAID_1_PEER_CMDS 2
+#define JBOD_MAPS_COUNT 2
+#define MEGASAS_REDUCE_QD_COUNT 64
+#define IOC_INIT_FRAME_SIZE 4096
/*
- * Raid Context structure which describes MegaRAID specific IO Paramenters
+ * Raid Context structure which describes MegaRAID specific IO Parameters
* This resides at offset 0x60 where the SGL normally starts in MPT IO Frames
*/
struct RAID_CONTEXT {
- u16 resvd0;
- u16 timeoutValue;
- u8 regLockFlags;
- u8 resvd1;
- u16 VirtualDiskTgtId;
- u64 regLockRowLBA;
- u32 regLockLength;
- u16 nextLMId;
- u8 exStatus;
- u8 status;
- u8 RAIDFlags;
- u8 numSGE;
- u16 configSeqNum;
- u8 spanArm;
- u8 resvd2[3];
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u8 nseg:4;
+ u8 type:4;
+#else
+ u8 type:4;
+ u8 nseg:4;
+#endif
+ u8 resvd0;
+ __le16 timeout_value;
+ u8 reg_lock_flags;
+ u8 resvd1;
+ __le16 virtual_disk_tgt_id;
+ __le64 reg_lock_row_lba;
+ __le32 reg_lock_length;
+ __le16 next_lmid;
+ u8 ex_status;
+ u8 status;
+ u8 raid_flags;
+ u8 num_sge;
+ __le16 config_seq_num;
+ u8 span_arm;
+ u8 priority;
+ u8 num_sge_ext;
+ u8 resvd2;
+};
+
+/*
+ * Raid Context structure which describes ventura MegaRAID specific
+ * IO Paramenters ,This resides at offset 0x60 where the SGL normally
+ * starts in MPT IO Frames
+ */
+struct RAID_CONTEXT_G35 {
+ #define RAID_CONTEXT_NSEG_MASK 0x00F0
+ #define RAID_CONTEXT_NSEG_SHIFT 4
+ #define RAID_CONTEXT_TYPE_MASK 0x000F
+ #define RAID_CONTEXT_TYPE_SHIFT 0
+ u16 nseg_type;
+ u16 timeout_value; /* 0x02 -0x03 */
+ u16 routing_flags; // 0x04 -0x05 routing flags
+ u16 virtual_disk_tgt_id; /* 0x06 -0x07 */
+ __le64 reg_lock_row_lba; /* 0x08 - 0x0F */
+ u32 reg_lock_length; /* 0x10 - 0x13 */
+ union { // flow specific
+ u16 rmw_op_index; /* 0x14 - 0x15, R5/6 RMW: rmw operation index*/
+ u16 peer_smid; /* 0x14 - 0x15, R1 Write: peer smid*/
+ u16 r56_arm_map; /* 0x14 - 0x15, Unused [15], LogArm[14:10], P-Arm[9:5], Q-Arm[4:0] */
+
+ } flow_specific;
+
+ u8 ex_status; /* 0x16 : OUT */
+ u8 status; /* 0x17 status */
+ u8 raid_flags; /* 0x18 resvd[7:6], ioSubType[5:4],
+ * resvd[3:1], preferredCpu[0]
+ */
+ u8 span_arm; /* 0x1C span[7:5], arm[4:0] */
+ u16 config_seq_num; /* 0x1A -0x1B */
+ union {
+ /*
+ * Bit format:
+ * ---------------------------------
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * ---------------------------------
+ * Byte0 | numSGE[7]- numSGE[0] |
+ * ---------------------------------
+ * Byte1 |SD | resvd | numSGE 8-11 |
+ * --------------------------------
+ */
+ #define NUM_SGE_MASK_LOWER 0xFF
+ #define NUM_SGE_MASK_UPPER 0x0F
+ #define NUM_SGE_SHIFT_UPPER 8
+ #define STREAM_DETECT_SHIFT 7
+ #define STREAM_DETECT_MASK 0x80
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD) /* 0x1C - 0x1D */
+ u16 stream_detected:1;
+ u16 reserved:3;
+ u16 num_sge:12;
+#else
+ u16 num_sge:12;
+ u16 reserved:3;
+ u16 stream_detected:1;
+#endif
+ } bits;
+ u8 bytes[2];
+ } u;
+ u8 resvd2[2]; /* 0x1E-0x1F */
+};
+
+#define MR_RAID_CTX_ROUTINGFLAGS_SLD_SHIFT 1
+#define MR_RAID_CTX_ROUTINGFLAGS_C2D_SHIFT 2
+#define MR_RAID_CTX_ROUTINGFLAGS_FWD_SHIFT 3
+#define MR_RAID_CTX_ROUTINGFLAGS_SQN_SHIFT 4
+#define MR_RAID_CTX_ROUTINGFLAGS_SBS_SHIFT 5
+#define MR_RAID_CTX_ROUTINGFLAGS_RW_SHIFT 6
+#define MR_RAID_CTX_ROUTINGFLAGS_LOG_SHIFT 7
+#define MR_RAID_CTX_ROUTINGFLAGS_CPUSEL_SHIFT 8
+#define MR_RAID_CTX_ROUTINGFLAGS_CPUSEL_MASK 0x0F00
+#define MR_RAID_CTX_ROUTINGFLAGS_SETDIVERT_SHIFT 12
+#define MR_RAID_CTX_ROUTINGFLAGS_SETDIVERT_MASK 0xF000
+
+static inline void set_num_sge(struct RAID_CONTEXT_G35 *rctx_g35,
+ u16 sge_count)
+{
+ rctx_g35->u.bytes[0] = (u8)(sge_count & NUM_SGE_MASK_LOWER);
+ rctx_g35->u.bytes[1] |= (u8)((sge_count >> NUM_SGE_SHIFT_UPPER)
+ & NUM_SGE_MASK_UPPER);
+}
+
+static inline u16 get_num_sge(struct RAID_CONTEXT_G35 *rctx_g35)
+{
+ u16 sge_count;
+
+ sge_count = (u16)(((rctx_g35->u.bytes[1] & NUM_SGE_MASK_UPPER)
+ << NUM_SGE_SHIFT_UPPER) | (rctx_g35->u.bytes[0]));
+ return sge_count;
+}
+
+#define SET_STREAM_DETECTED(rctx_g35) \
+ (rctx_g35.u.bytes[1] |= STREAM_DETECT_MASK)
+
+#define CLEAR_STREAM_DETECTED(rctx_g35) \
+ (rctx_g35.u.bytes[1] &= ~(STREAM_DETECT_MASK))
+
+static inline bool is_stream_detected(struct RAID_CONTEXT_G35 *rctx_g35)
+{
+ return ((rctx_g35->u.bytes[1] & STREAM_DETECT_MASK));
+}
+
+union RAID_CONTEXT_UNION {
+ struct RAID_CONTEXT raid_context;
+ struct RAID_CONTEXT_G35 raid_context_g35;
};
#define RAID_CTX_SPANARM_ARM_SHIFT (0)
@@ -105,6 +240,21 @@ struct RAID_CONTEXT {
#define RAID_CTX_SPANARM_SPAN_SHIFT (5)
#define RAID_CTX_SPANARM_SPAN_MASK (0xE0)
+/* LogArm[14:10], P-Arm[9:5], Q-Arm[4:0] */
+#define RAID_CTX_R56_Q_ARM_MASK (0x1F)
+#define RAID_CTX_R56_P_ARM_SHIFT (5)
+#define RAID_CTX_R56_P_ARM_MASK (0x3E0)
+#define RAID_CTX_R56_LOG_ARM_SHIFT (10)
+#define RAID_CTX_R56_LOG_ARM_MASK (0x7C00)
+
+/* number of bits per index in U32 TrackStream */
+#define BITS_PER_INDEX_STREAM 4
+#define INVALID_STREAM_NUM 16
+#define MR_STREAM_BITMAP 0x76543210
+#define STREAM_MASK ((1 << BITS_PER_INDEX_STREAM) - 1)
+#define ZERO_LAST_STREAM 0x0fffffff
+#define MAX_STREAMS_TRACKED 8
+
/*
* define region lock types
*/
@@ -141,8 +291,12 @@ enum REGION_TYPE {
#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200)
#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100)
#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004)
+/* EEDP escape mode */
+#define MPI25_SCSIIO_EEDPFLAGS_DO_NOT_DISABLE_MODE (0x0040)
#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) /* SCSI IO */
-#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06)
+#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01)
+#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x03)
+#define MPI2_REQ_DESCRIPT_FLAGS_FP_IO (0x06)
#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00)
#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02)
#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000)
@@ -161,61 +315,61 @@ enum REGION_TYPE {
#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD)
struct MPI25_IEEE_SGE_CHAIN64 {
- u64 Address;
- u32 Length;
- u16 Reserved1;
+ __le64 Address;
+ __le32 Length;
+ __le16 Reserved1;
u8 NextChainOffset;
u8 Flags;
};
struct MPI2_SGE_SIMPLE_UNION {
- u32 FlagsLength;
+ __le32 FlagsLength;
union {
- u32 Address32;
- u64 Address64;
+ __le32 Address32;
+ __le64 Address64;
} u;
};
struct MPI2_SCSI_IO_CDB_EEDP32 {
u8 CDB[20]; /* 0x00 */
- u32 PrimaryReferenceTag; /* 0x14 */
- u16 PrimaryApplicationTag; /* 0x18 */
- u16 PrimaryApplicationTagMask; /* 0x1A */
- u32 TransferLength; /* 0x1C */
+ __be32 PrimaryReferenceTag; /* 0x14 */
+ __be16 PrimaryApplicationTag; /* 0x18 */
+ __be16 PrimaryApplicationTagMask; /* 0x1A */
+ __le32 TransferLength; /* 0x1C */
};
struct MPI2_SGE_CHAIN_UNION {
- u16 Length;
+ __le16 Length;
u8 NextChainOffset;
u8 Flags;
union {
- u32 Address32;
- u64 Address64;
+ __le32 Address32;
+ __le64 Address64;
} u;
};
struct MPI2_IEEE_SGE_SIMPLE32 {
- u32 Address;
- u32 FlagsLength;
+ __le32 Address;
+ __le32 FlagsLength;
};
struct MPI2_IEEE_SGE_CHAIN32 {
- u32 Address;
- u32 FlagsLength;
+ __le32 Address;
+ __le32 FlagsLength;
};
struct MPI2_IEEE_SGE_SIMPLE64 {
- u64 Address;
- u32 Length;
- u16 Reserved1;
+ __le64 Address;
+ __le32 Length;
+ __le16 Reserved1;
u8 Reserved2;
u8 Flags;
};
struct MPI2_IEEE_SGE_CHAIN64 {
- u64 Address;
- u32 Length;
- u16 Reserved1;
+ __le64 Address;
+ __le32 Length;
+ __le16 Reserved1;
u8 Reserved2;
u8 Flags;
};
@@ -243,42 +397,139 @@ union MPI2_SCSI_IO_CDB_UNION {
struct MPI2_SGE_SIMPLE_UNION SGE;
};
+/****************************************************************************
+* SCSI Task Management messages
+****************************************************************************/
+
+/*SCSI Task Management Request Message */
+struct MPI2_SCSI_TASK_MANAGE_REQUEST {
+ u16 DevHandle; /*0x00 */
+ u8 ChainOffset; /*0x02 */
+ u8 Function; /*0x03 */
+ u8 Reserved1; /*0x04 */
+ u8 TaskType; /*0x05 */
+ u8 Reserved2; /*0x06 */
+ u8 MsgFlags; /*0x07 */
+ u8 VP_ID; /*0x08 */
+ u8 VF_ID; /*0x09 */
+ u16 Reserved3; /*0x0A */
+ u8 LUN[8]; /*0x0C */
+ u32 Reserved4[7]; /*0x14 */
+ u16 TaskMID; /*0x30 */
+ u16 Reserved5; /*0x32 */
+};
+
+
+/*SCSI Task Management Reply Message */
+struct MPI2_SCSI_TASK_MANAGE_REPLY {
+ u16 DevHandle; /*0x00 */
+ u8 MsgLength; /*0x02 */
+ u8 Function; /*0x03 */
+ u8 ResponseCode; /*0x04 */
+ u8 TaskType; /*0x05 */
+ u8 Reserved1; /*0x06 */
+ u8 MsgFlags; /*0x07 */
+ u8 VP_ID; /*0x08 */
+ u8 VF_ID; /*0x09 */
+ u16 Reserved2; /*0x0A */
+ u16 Reserved3; /*0x0C */
+ u16 IOCStatus; /*0x0E */
+ u32 IOCLogInfo; /*0x10 */
+ u32 TerminationCount; /*0x14 */
+ u32 ResponseInfo; /*0x18 */
+};
+
+struct MR_TM_REQUEST {
+ char request[128];
+};
+
+struct MR_TM_REPLY {
+ char reply[128];
+};
+
+/* SCSI Task Management Request Message */
+struct MR_TASK_MANAGE_REQUEST {
+ /*To be type casted to struct MPI2_SCSI_TASK_MANAGE_REQUEST */
+ struct MR_TM_REQUEST TmRequest;
+ union {
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved1:30;
+ u32 isTMForPD:1;
+ u32 isTMForLD:1;
+#else
+ u32 isTMForLD:1;
+ u32 isTMForPD:1;
+ u32 reserved1:30;
+#endif
+ u32 reserved2;
+ } tmReqFlags;
+ struct MR_TM_REPLY TMReply;
+ };
+};
+
+/* TaskType values */
+
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01)
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02)
+#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
+#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A)
+
+/* ResponseCode values */
+
+#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00)
+#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02)
+#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04)
+#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05)
+#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08)
+#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09)
+#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A)
+#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80)
+
/*
* RAID SCSI IO Request Message
* Total SGE count will be one less than _MPI2_SCSI_IO_REQUEST
*/
struct MPI2_RAID_SCSI_IO_REQUEST {
- u16 DevHandle; /* 0x00 */
+ __le16 DevHandle; /* 0x00 */
u8 ChainOffset; /* 0x02 */
u8 Function; /* 0x03 */
- u16 Reserved1; /* 0x04 */
+ __le16 Reserved1; /* 0x04 */
u8 Reserved2; /* 0x06 */
u8 MsgFlags; /* 0x07 */
u8 VP_ID; /* 0x08 */
u8 VF_ID; /* 0x09 */
- u16 Reserved3; /* 0x0A */
- u32 SenseBufferLowAddress; /* 0x0C */
- u16 SGLFlags; /* 0x10 */
+ __le16 Reserved3; /* 0x0A */
+ __le32 SenseBufferLowAddress; /* 0x0C */
+ __le16 SGLFlags; /* 0x10 */
u8 SenseBufferLength; /* 0x12 */
u8 Reserved4; /* 0x13 */
u8 SGLOffset0; /* 0x14 */
u8 SGLOffset1; /* 0x15 */
u8 SGLOffset2; /* 0x16 */
u8 SGLOffset3; /* 0x17 */
- u32 SkipCount; /* 0x18 */
- u32 DataLength; /* 0x1C */
- u32 BidirectionalDataLength; /* 0x20 */
- u16 IoFlags; /* 0x24 */
- u16 EEDPFlags; /* 0x26 */
- u32 EEDPBlockSize; /* 0x28 */
- u32 SecondaryReferenceTag; /* 0x2C */
- u16 SecondaryApplicationTag; /* 0x30 */
- u16 ApplicationTagTranslationMask; /* 0x32 */
+ __le32 SkipCount; /* 0x18 */
+ __le32 DataLength; /* 0x1C */
+ __le32 BidirectionalDataLength; /* 0x20 */
+ __le16 IoFlags; /* 0x24 */
+ __le16 EEDPFlags; /* 0x26 */
+ __le32 EEDPBlockSize; /* 0x28 */
+ __le32 SecondaryReferenceTag; /* 0x2C */
+ __le16 SecondaryApplicationTag; /* 0x30 */
+ __le16 ApplicationTagTranslationMask; /* 0x32 */
u8 LUN[8]; /* 0x34 */
- u32 Control; /* 0x3C */
+ __le32 Control; /* 0x3C */
union MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */
- struct RAID_CONTEXT RaidContext; /* 0x60 */
- union MPI2_SGE_IO_UNION SGL; /* 0x80 */
+ union RAID_CONTEXT_UNION RaidContext; /* 0x60 */
+ union {
+ union MPI2_SGE_IO_UNION SGL; /* 0x80 */
+ DECLARE_FLEX_ARRAY(union MPI2_SGE_IO_UNION, SGLs);
+ };
};
/*
@@ -286,53 +537,53 @@ struct MPI2_RAID_SCSI_IO_REQUEST {
*/
struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR {
u32 RequestFlags:8;
- u32 MessageAddress1:24; /* bits 31:8*/
- u32 MessageAddress2; /* bits 61:32 */
+ u32 MessageAddress1:24;
+ u32 MessageAddress2;
};
/* Default Request Descriptor */
struct MPI2_DEFAULT_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 DescriptorTypeDependent; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 DescriptorTypeDependent; /* 0x06 */
};
/* High Priority Request Descriptor */
struct MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 Reserved1; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 Reserved1; /* 0x06 */
};
/* SCSI IO Request Descriptor */
struct MPI2_SCSI_IO_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 DevHandle; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 DevHandle; /* 0x06 */
};
/* SCSI Target Request Descriptor */
struct MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 IoIndex; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 IoIndex; /* 0x06 */
};
/* RAID Accelerator Request Descriptor */
struct MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 Reserved; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 Reserved; /* 0x06 */
};
/* union of Request Descriptors */
@@ -345,10 +596,10 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION {
struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR MFAIo;
union {
struct {
- u32 low;
- u32 high;
+ __le32 low;
+ __le32 high;
} u;
- u64 Words;
+ __le64 Words;
};
};
@@ -356,35 +607,35 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION {
struct MPI2_DEFAULT_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 DescriptorTypeDependent1; /* 0x02 */
- u32 DescriptorTypeDependent2; /* 0x04 */
+ __le16 DescriptorTypeDependent1; /* 0x02 */
+ __le32 DescriptorTypeDependent2; /* 0x04 */
};
/* Address Reply Descriptor */
struct MPI2_ADDRESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u32 ReplyFrameAddress; /* 0x04 */
+ __le16 SMID; /* 0x02 */
+ __le32 ReplyFrameAddress; /* 0x04 */
};
/* SCSI IO Success Reply Descriptor */
struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 TaskTag; /* 0x04 */
- u16 Reserved1; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 TaskTag; /* 0x04 */
+ __le16 Reserved1; /* 0x06 */
};
/* TargetAssist Success Reply Descriptor */
struct MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
+ __le16 SMID; /* 0x02 */
u8 SequenceNumber; /* 0x04 */
u8 Reserved1; /* 0x05 */
- u16 IoIndex; /* 0x06 */
+ __le16 IoIndex; /* 0x06 */
};
/* Target Command Buffer Reply Descriptor */
@@ -393,16 +644,16 @@ struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR {
u8 MSIxIndex; /* 0x01 */
u8 VP_ID; /* 0x02 */
u8 Flags; /* 0x03 */
- u16 InitiatorDevHandle; /* 0x04 */
- u16 IoIndex; /* 0x06 */
+ __le16 InitiatorDevHandle; /* 0x04 */
+ __le16 IoIndex; /* 0x06 */
};
/* RAID Accelerator Success Reply Descriptor */
struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u32 Reserved; /* 0x04 */
+ __le16 SMID; /* 0x02 */
+ __le32 Reserved; /* 0x04 */
};
/* union of Reply Descriptors */
@@ -414,7 +665,7 @@ union MPI2_REPLY_DESCRIPTORS_UNION {
struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer;
struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR
RAIDAcceleratorSuccess;
- u64 Words;
+ __le64 Words;
};
/* IOCInit Request message */
@@ -423,87 +674,167 @@ struct MPI2_IOC_INIT_REQUEST {
u8 Reserved1; /* 0x01 */
u8 ChainOffset; /* 0x02 */
u8 Function; /* 0x03 */
- u16 Reserved2; /* 0x04 */
+ __le16 Reserved2; /* 0x04 */
u8 Reserved3; /* 0x06 */
u8 MsgFlags; /* 0x07 */
u8 VP_ID; /* 0x08 */
u8 VF_ID; /* 0x09 */
- u16 Reserved4; /* 0x0A */
- u16 MsgVersion; /* 0x0C */
- u16 HeaderVersion; /* 0x0E */
+ __le16 Reserved4; /* 0x0A */
+ __le16 MsgVersion; /* 0x0C */
+ __le16 HeaderVersion; /* 0x0E */
u32 Reserved5; /* 0x10 */
- u16 Reserved6; /* 0x14 */
- u8 Reserved7; /* 0x16 */
+ __le16 Reserved6; /* 0x14 */
+ u8 HostPageSize; /* 0x16 */
u8 HostMSIxVectors; /* 0x17 */
- u16 Reserved8; /* 0x18 */
- u16 SystemRequestFrameSize; /* 0x1A */
- u16 ReplyDescriptorPostQueueDepth; /* 0x1C */
- u16 ReplyFreeQueueDepth; /* 0x1E */
- u32 SenseBufferAddressHigh; /* 0x20 */
- u32 SystemReplyAddressHigh; /* 0x24 */
- u64 SystemRequestFrameBaseAddress; /* 0x28 */
- u64 ReplyDescriptorPostQueueAddress;/* 0x30 */
- u64 ReplyFreeQueueAddress; /* 0x38 */
- u64 TimeStamp; /* 0x40 */
+ __le16 Reserved8; /* 0x18 */
+ __le16 SystemRequestFrameSize; /* 0x1A */
+ __le16 ReplyDescriptorPostQueueDepth; /* 0x1C */
+ __le16 ReplyFreeQueueDepth; /* 0x1E */
+ __le32 SenseBufferAddressHigh; /* 0x20 */
+ __le32 SystemReplyAddressHigh; /* 0x24 */
+ __le64 SystemRequestFrameBaseAddress; /* 0x28 */
+ __le64 ReplyDescriptorPostQueueAddress;/* 0x30 */
+ __le64 ReplyFreeQueueAddress; /* 0x38 */
+ __le64 TimeStamp; /* 0x40 */
};
/* mrpriv defines */
#define MR_PD_INVALID 0xFFFF
+#define MR_DEVHANDLE_INVALID 0xFFFF
#define MAX_SPAN_DEPTH 8
+#define MAX_QUAD_DEPTH MAX_SPAN_DEPTH
#define MAX_RAIDMAP_SPAN_DEPTH (MAX_SPAN_DEPTH)
#define MAX_ROW_SIZE 32
#define MAX_RAIDMAP_ROW_SIZE (MAX_ROW_SIZE)
#define MAX_LOGICAL_DRIVES 64
+#define MAX_LOGICAL_DRIVES_EXT 256
+#define MAX_LOGICAL_DRIVES_DYN 512
#define MAX_RAIDMAP_LOGICAL_DRIVES (MAX_LOGICAL_DRIVES)
#define MAX_RAIDMAP_VIEWS (MAX_LOGICAL_DRIVES)
#define MAX_ARRAYS 128
#define MAX_RAIDMAP_ARRAYS (MAX_ARRAYS)
+#define MAX_ARRAYS_EXT 256
+#define MAX_API_ARRAYS_EXT (MAX_ARRAYS_EXT)
+#define MAX_API_ARRAYS_DYN 512
#define MAX_PHYSICAL_DEVICES 256
#define MAX_RAIDMAP_PHYSICAL_DEVICES (MAX_PHYSICAL_DEVICES)
+#define MAX_RAIDMAP_PHYSICAL_DEVICES_DYN 512
#define MR_DCMD_LD_MAP_GET_INFO 0x0300e101
+#define MR_DCMD_SYSTEM_PD_MAP_GET_INFO 0x0200e102
+#define MR_DCMD_DRV_GET_TARGET_PROP 0x0200e103
+#define MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC 0x010e8485 /* SR-IOV HB alloc*/
+#define MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111 0x03200200
+#define MR_DCMD_LD_VF_MAP_GET_ALL_LDS 0x03150200
+#define MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES 0x01200100
+#define MR_DCMD_CTRL_DEVICE_LIST_GET 0x01190600
struct MR_DEV_HANDLE_INFO {
- u16 curDevHdl;
+ __le16 curDevHdl;
u8 validHandles;
- u8 reserved;
- u16 devHandle[2];
+ u8 interfaceType;
+ __le16 devHandle[2];
};
struct MR_ARRAY_INFO {
- u16 pd[MAX_RAIDMAP_ROW_SIZE];
+ __le16 pd[MAX_RAIDMAP_ROW_SIZE];
};
struct MR_QUAD_ELEMENT {
- u64 logStart;
- u64 logEnd;
- u64 offsetInSpan;
- u32 diff;
- u32 reserved1;
+ __le64 logStart;
+ __le64 logEnd;
+ __le64 offsetInSpan;
+ __le32 diff;
+ __le32 reserved1;
};
struct MR_SPAN_INFO {
- u32 noElements;
- u32 reserved1;
+ __le32 noElements;
+ __le32 reserved1;
struct MR_QUAD_ELEMENT quad[MAX_RAIDMAP_SPAN_DEPTH];
};
struct MR_LD_SPAN {
- u64 startBlk;
- u64 numBlks;
- u16 arrayRef;
- u8 reserved[6];
+ __le64 startBlk;
+ __le64 numBlks;
+ __le16 arrayRef;
+ u8 spanRowSize;
+ u8 spanRowDataSize;
+ u8 reserved[4];
};
struct MR_SPAN_BLOCK_INFO {
- u64 num_rows;
+ __le64 num_rows;
struct MR_LD_SPAN span;
struct MR_SPAN_INFO block_span_info;
};
+#define MR_RAID_CTX_CPUSEL_0 0
+#define MR_RAID_CTX_CPUSEL_1 1
+#define MR_RAID_CTX_CPUSEL_2 2
+#define MR_RAID_CTX_CPUSEL_3 3
+#define MR_RAID_CTX_CPUSEL_FCFS 0xF
+
+struct MR_CPU_AFFINITY_MASK {
+ union {
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u8 hw_path:1;
+ u8 cpu0:1;
+ u8 cpu1:1;
+ u8 cpu2:1;
+ u8 cpu3:1;
+ u8 reserved:3;
+#else
+ u8 reserved:3;
+ u8 cpu3:1;
+ u8 cpu2:1;
+ u8 cpu1:1;
+ u8 cpu0:1;
+ u8 hw_path:1;
+#endif
+ };
+ u8 core_mask;
+ };
+};
+
+struct MR_IO_AFFINITY {
+ union {
+ struct {
+ struct MR_CPU_AFFINITY_MASK pdRead;
+ struct MR_CPU_AFFINITY_MASK pdWrite;
+ struct MR_CPU_AFFINITY_MASK ldRead;
+ struct MR_CPU_AFFINITY_MASK ldWrite;
+ };
+ u32 word;
+ };
+ u8 maxCores; /* Total cores + HW Path in ROC */
+ u8 reserved[3];
+};
+
struct MR_LD_RAID {
struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u32 reserved4:2;
+ u32 fp_cache_bypass_capable:1;
+ u32 fp_rmw_capable:1;
+ u32 disable_coalescing:1;
+ u32 fpBypassRegionLock:1;
+ u32 tmCapable:1;
+ u32 fpNonRWCapable:1;
+ u32 fpReadAcrossStripe:1;
+ u32 fpWriteAcrossStripe:1;
+ u32 fpReadCapable:1;
+ u32 fpWriteCapable:1;
+ u32 encryptionType:8;
+ u32 pdPiMode:4;
+ u32 ldPiMode:4;
+ u32 reserved5:2;
+ u32 ra_capable:1;
u32 fpCapable:1;
- u32 reserved5:3;
+#else
+ u32 fpCapable:1;
+ u32 ra_capable:1;
+ u32 reserved5:2;
u32 ldPiMode:4;
u32 pdPiMode:4;
u32 encryptionType:8;
@@ -511,10 +842,17 @@ struct MR_LD_RAID {
u32 fpReadCapable:1;
u32 fpWriteAcrossStripe:1;
u32 fpReadAcrossStripe:1;
- u32 reserved4:8;
+ u32 fpNonRWCapable:1;
+ u32 tmCapable:1;
+ u32 fpBypassRegionLock:1;
+ u32 disable_coalescing:1;
+ u32 fp_rmw_capable:1;
+ u32 fp_cache_bypass_capable:1;
+ u32 reserved4:2;
+#endif
} capability;
- u32 reserved6;
- u64 size;
+ __le32 reserved6;
+ __le64 size;
u8 spanDepth;
u8 level;
u8 stripeShift;
@@ -523,19 +861,61 @@ struct MR_LD_RAID {
u8 writeMode;
u8 PRL;
u8 SRL;
- u16 targetId;
+ __le16 targetId;
u8 ldState;
u8 regTypeReqOnWrite;
u8 modFactor;
- u8 reserved2[1];
- u16 seqNum;
-
- struct {
- u32 ldSyncRequired:1;
- u32 reserved:31;
+ u8 regTypeReqOnRead;
+ __le16 seqNum;
+
+struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ u32 ldSyncRequired:1;
+ u32 regTypeReqOnReadIsValid:1;
+ u32 isEPD:1;
+ u32 enableSLDOnAllRWIOs:1;
+ u32 reserved:28;
+#else
+ u32 reserved:28;
+ u32 enableSLDOnAllRWIOs:1;
+ u32 isEPD:1;
+ u32 regTypeReqOnReadIsValid:1;
+ u32 ldSyncRequired:1;
+#endif
} flags;
- u8 reserved3[0x5C];
+ u8 LUN[8]; /* 0x24 8 byte LUN field used for SCSI IO's */
+ u8 fpIoTimeoutForLd;/*0x2C timeout value used by driver in FP IO*/
+ /* Ox2D This LD accept priority boost of this type */
+ u8 ld_accept_priority_type;
+ u8 reserved2[2]; /* 0x2E - 0x2F */
+ /* 0x30 - 0x33, Logical block size for the LD */
+ u32 logical_block_length;
+ struct {
+#ifndef __BIG_ENDIAN_BITFIELD
+ /* 0x34, P_I_EXPONENT from READ CAPACITY 16 */
+ u32 ld_pi_exp:4;
+ /* 0x34, LOGICAL BLOCKS PER PHYSICAL
+ * BLOCK EXPONENT from READ CAPACITY 16
+ */
+ u32 ld_logical_block_exp:4;
+ u32 reserved1:24; /* 0x34 */
+#else
+ u32 reserved1:24; /* 0x34 */
+ /* 0x34, LOGICAL BLOCKS PER PHYSICAL
+ * BLOCK EXPONENT from READ CAPACITY 16
+ */
+ u32 ld_logical_block_exp:4;
+ /* 0x34, P_I_EXPONENT from READ CAPACITY 16 */
+ u32 ld_pi_exp:4;
+#endif
+ }; /* 0x34 - 0x37 */
+ /* 0x38 - 0x3f, This will determine which
+ * core will process LD IO and PD IO.
+ */
+ struct MR_IO_AFFINITY cpuAffinity;
+ /* Bit definiations are specified by MR_IO_AFFINITY */
+ u8 reserved3[0x80 - 0x40]; /* 0x40 - 0x7f */
};
struct MR_LD_SPAN_MAP {
@@ -545,28 +925,27 @@ struct MR_LD_SPAN_MAP {
};
struct MR_FW_RAID_MAP {
- u32 totalSize;
+ __le32 totalSize;
union {
struct {
- u32 maxLd;
- u32 maxSpanDepth;
- u32 maxRowSize;
- u32 maxPdCount;
- u32 maxArrays;
+ __le32 maxLd;
+ __le32 maxSpanDepth;
+ __le32 maxRowSize;
+ __le32 maxPdCount;
+ __le32 maxArrays;
} validationInfo;
- u32 version[5];
- u32 reserved1[5];
+ __le32 version[5];
};
- u32 ldCount;
- u32 Reserved1;
+ __le32 ldCount;
+ __le32 Reserved1;
u8 ldTgtIdToLd[MAX_RAIDMAP_LOGICAL_DRIVES+
MAX_RAIDMAP_VIEWS];
u8 fpPdIoTimeoutSec;
u8 reserved2[7];
struct MR_ARRAY_INFO arMapInfo[MAX_RAIDMAP_ARRAYS];
struct MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES];
- struct MR_LD_SPAN_MAP ldSpanMap[1];
+ struct MR_LD_SPAN_MAP ldSpanMap[];
};
struct IO_REQUEST_INFO {
@@ -574,17 +953,112 @@ struct IO_REQUEST_INFO {
u32 numBlocks;
u16 ldTgtId;
u8 isRead;
- u16 devHandle;
+ __le16 devHandle;
+ u8 pd_interface;
u64 pdBlock;
u8 fpOkForIo;
+ u8 IoforUnevenSpan;
+ u8 start_span;
+ u8 do_fp_rlbypass;
+ u64 start_row;
+ u8 span_arm; /* span[7:5], arm[4:0] */
+ u8 pd_after_lb;
+ u16 r1_alt_dev_handle; /* raid 1/10 only */
+ bool ra_capable;
+ u8 data_arms;
};
struct MR_LD_TARGET_SYNC {
u8 targetId;
u8 reserved;
- u16 seqNum;
+ __le16 seqNum;
+};
+
+/*
+ * RAID Map descriptor Types.
+ * Each element should uniquely idetify one data structure in the RAID map
+ */
+enum MR_RAID_MAP_DESC_TYPE {
+ /* MR_DEV_HANDLE_INFO data */
+ RAID_MAP_DESC_TYPE_DEVHDL_INFO = 0x0,
+ /* target to Ld num Index map */
+ RAID_MAP_DESC_TYPE_TGTID_INFO = 0x1,
+ /* MR_ARRAY_INFO data */
+ RAID_MAP_DESC_TYPE_ARRAY_INFO = 0x2,
+ /* MR_LD_SPAN_MAP data */
+ RAID_MAP_DESC_TYPE_SPAN_INFO = 0x3,
+ RAID_MAP_DESC_TYPE_COUNT,
+};
+
+/*
+ * This table defines the offset, size and num elements of each descriptor
+ * type in the RAID Map buffer
+ */
+struct MR_RAID_MAP_DESC_TABLE {
+ /* Raid map descriptor type */
+ u32 raid_map_desc_type;
+ /* Offset into the RAID map buffer where
+ * descriptor data is saved
+ */
+ u32 raid_map_desc_offset;
+ /* total size of the
+ * descriptor buffer
+ */
+ u32 raid_map_desc_buffer_size;
+ /* Number of elements contained in the
+ * descriptor buffer
+ */
+ u32 raid_map_desc_elements;
};
+/*
+ * Dynamic Raid Map Structure.
+ */
+struct MR_FW_RAID_MAP_DYNAMIC {
+ u32 raid_map_size; /* total size of RAID Map structure */
+ u32 desc_table_offset;/* Offset of desc table into RAID map*/
+ u32 desc_table_size; /* Total Size of desc table */
+ /* Total Number of elements in the desc table */
+ u32 desc_table_num_elements;
+ u64 reserved1;
+ u32 reserved2[3]; /*future use */
+ /* timeout value used by driver in FP IOs */
+ u8 fp_pd_io_timeout_sec;
+ u8 reserved3[3];
+ /* when this seqNum increments, driver needs to
+ * release RMW buffers asap
+ */
+ u32 rmw_fp_seq_num;
+ u16 ld_count; /* count of lds. */
+ u16 ar_count; /* count of arrays */
+ u16 span_count; /* count of spans */
+ u16 reserved4[3];
+/*
+ * The below structure of pointers is only to be used by the driver.
+ * This is added in the ,API to reduce the amount of code changes
+ * needed in the driver to support dynamic RAID map Firmware should
+ * not update these pointers while preparing the raid map
+ */
+ union {
+ struct {
+ struct MR_DEV_HANDLE_INFO *dev_hndl_info;
+ u16 *ld_tgt_id_to_ld;
+ struct MR_ARRAY_INFO *ar_map_info;
+ struct MR_LD_SPAN_MAP *ld_span_map;
+ };
+ u64 ptr_structure_size[RAID_MAP_DESC_TYPE_COUNT];
+ };
+/*
+ * RAID Map descriptor table defines the layout of data in the RAID Map.
+ * The size of the descriptor table itself could change.
+ */
+ /* Variable Size descriptor Table. */
+ struct MR_RAID_MAP_DESC_TABLE
+ raid_map_desc_table[RAID_MAP_DESC_TYPE_COUNT];
+ /* Variable Size buffer containing all data */
+ u32 raid_map_desc_data[];
+}; /* Dynamicaly sized RAID MAp structure */
+
#define IEEE_SGE_FLAGS_ADDR_MASK (0x03)
#define IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00)
#define IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01)
@@ -593,6 +1067,19 @@ struct MR_LD_TARGET_SYNC {
#define IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80)
#define IEEE_SGE_FLAGS_END_OF_LIST (0x40)
+#define MPI2_SGE_FLAGS_SHIFT (0x02)
+#define IEEE_SGE_FLAGS_FORMAT_MASK (0xC0)
+#define IEEE_SGE_FLAGS_FORMAT_IEEE (0x00)
+#define IEEE_SGE_FLAGS_FORMAT_NVME (0x02)
+
+#define MPI26_IEEE_SGE_FLAGS_NSF_MASK (0x1C)
+#define MPI26_IEEE_SGE_FLAGS_NSF_MPI_IEEE (0x00)
+#define MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP (0x08)
+#define MPI26_IEEE_SGE_FLAGS_NSF_NVME_SGL (0x10)
+
+#define MEGASAS_DEFAULT_SNAP_DUMP_WAIT_TIME 15
+#define MEGASAS_MAX_SNAP_DUMP_WAIT_TIME 60
+
struct megasas_register_set;
struct megasas_instance;
@@ -627,28 +1114,192 @@ struct megasas_cmd_fusion {
*/
u32 sync_cmd_idx;
u32 index;
- u8 flags;
+ u8 pd_r1_lb;
+ struct completion done;
+ u8 pd_interface;
+ u16 r1_alt_dev_handle; /* raid 1/10 only*/
+ bool cmd_completed; /* raid 1/10 fp writes status holder */
+
};
struct LD_LOAD_BALANCE_INFO {
u8 loadBalanceFlag;
u8 reserved1;
- u16 raid1DevHandle[2];
- atomic_t scsi_pending_cmds[2];
- u64 last_accessed_block[2];
+ atomic_t scsi_pending_cmds[MAX_PHYSICAL_DEVICES];
+ u64 last_accessed_block[MAX_PHYSICAL_DEVICES];
};
+/* SPAN_SET is info caclulated from span info from Raid map per LD */
+typedef struct _LD_SPAN_SET {
+ u64 log_start_lba;
+ u64 log_end_lba;
+ u64 span_row_start;
+ u64 span_row_end;
+ u64 data_strip_start;
+ u64 data_strip_end;
+ u64 data_row_start;
+ u64 data_row_end;
+ u8 strip_offset[MAX_SPAN_DEPTH];
+ u32 span_row_data_width;
+ u32 diff;
+ u32 reserved[2];
+} LD_SPAN_SET, *PLD_SPAN_SET;
+
+typedef struct LOG_BLOCK_SPAN_INFO {
+ LD_SPAN_SET span_set[MAX_SPAN_DEPTH];
+} LD_SPAN_INFO, *PLD_SPAN_INFO;
+
struct MR_FW_RAID_MAP_ALL {
- struct MR_FW_RAID_MAP raidMap;
- struct MR_LD_SPAN_MAP ldSpanMap[MAX_LOGICAL_DRIVES - 1];
+ /* Must be last --ends in a flexible-array member. */
+ TRAILING_OVERLAP(struct MR_FW_RAID_MAP, raidMap, ldSpanMap,
+ struct MR_LD_SPAN_MAP ldSpanMap[MAX_LOGICAL_DRIVES];
+ );
} __attribute__ ((packed));
+static_assert(offsetof(struct MR_FW_RAID_MAP_ALL, raidMap.ldSpanMap) ==
+ offsetof(struct MR_FW_RAID_MAP_ALL, ldSpanMap));
+
+struct MR_DRV_RAID_MAP {
+ /* total size of this structure, including this field.
+ * This feild will be manupulated by driver for ext raid map,
+ * else pick the value from firmware raid map.
+ */
+ __le32 totalSize;
+
+ union {
+ struct {
+ __le32 maxLd;
+ __le32 maxSpanDepth;
+ __le32 maxRowSize;
+ __le32 maxPdCount;
+ __le32 maxArrays;
+ } validationInfo;
+ __le32 version[5];
+ };
+
+ /* timeout value used by driver in FP IOs*/
+ u8 fpPdIoTimeoutSec;
+ u8 reserved2[7];
+
+ __le16 ldCount;
+ __le16 arCount;
+ __le16 spanCount;
+ __le16 reserve3;
+
+ struct MR_DEV_HANDLE_INFO
+ devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES_DYN];
+ u16 ldTgtIdToLd[MAX_LOGICAL_DRIVES_DYN];
+ struct MR_ARRAY_INFO arMapInfo[MAX_API_ARRAYS_DYN];
+ struct MR_LD_SPAN_MAP ldSpanMap[];
+
+};
+
+/* Driver raid map size is same as raid map ext
+ * MR_DRV_RAID_MAP_ALL is created to sync with old raid.
+ * And it is mainly for code re-use purpose.
+ */
+struct MR_DRV_RAID_MAP_ALL {
+ /* Must be last --ends in a flexible-array member. */
+ TRAILING_OVERLAP(struct MR_DRV_RAID_MAP, raidMap, ldSpanMap,
+ struct MR_LD_SPAN_MAP ldSpanMap[MAX_LOGICAL_DRIVES_DYN];
+ );
+} __packed;
+static_assert(offsetof(struct MR_DRV_RAID_MAP_ALL, raidMap.ldSpanMap) ==
+ offsetof(struct MR_DRV_RAID_MAP_ALL, ldSpanMap));
+
-struct fusion_context {
- struct megasas_cmd_fusion **cmd_list;
- struct list_head cmd_pool;
- spinlock_t cmd_pool_lock;
+struct MR_FW_RAID_MAP_EXT {
+ /* Not usred in new map */
+ u32 reserved;
+ union {
+ struct {
+ u32 maxLd;
+ u32 maxSpanDepth;
+ u32 maxRowSize;
+ u32 maxPdCount;
+ u32 maxArrays;
+ } validationInfo;
+ u32 version[5];
+ };
+
+ u8 fpPdIoTimeoutSec;
+ u8 reserved2[7];
+
+ __le16 ldCount;
+ __le16 arCount;
+ __le16 spanCount;
+ __le16 reserve3;
+
+ struct MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES];
+ u8 ldTgtIdToLd[MAX_LOGICAL_DRIVES_EXT];
+ struct MR_ARRAY_INFO arMapInfo[MAX_API_ARRAYS_EXT];
+ struct MR_LD_SPAN_MAP ldSpanMap[MAX_LOGICAL_DRIVES_EXT];
+};
+
+/*
+ * * define MR_PD_CFG_SEQ structure for system PDs
+ * */
+struct MR_PD_CFG_SEQ {
+ u16 seqNum;
+ u16 devHandle;
+ struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ u8 reserved:7;
+ u8 tmCapable:1;
+#else
+ u8 tmCapable:1;
+ u8 reserved:7;
+#endif
+ } capability;
+ u8 reserved;
+ u16 pd_target_id;
+} __packed;
+
+struct MR_PD_CFG_SEQ_NUM_SYNC {
+ __le32 size;
+ __le32 count;
+ struct MR_PD_CFG_SEQ seq[];
+} __packed;
+
+/* stream detection */
+struct STREAM_DETECT {
+ u64 next_seq_lba; /* next LBA to match sequential access */
+ struct megasas_cmd_fusion *first_cmd_fusion; /* first cmd in group */
+ struct megasas_cmd_fusion *last_cmd_fusion; /* last cmd in group */
+ u32 count_cmds_in_stream; /* count of host commands in this stream */
+ u16 num_sges_in_group; /* total number of SGEs in grouped IOs */
+ u8 is_read; /* SCSI OpCode for this stream */
+ u8 group_depth; /* total number of host commands in group */
+ /* TRUE if cannot add any more commands to this group */
+ bool group_flush;
+ u8 reserved[7]; /* pad to 64-bit alignment */
+};
+
+struct LD_STREAM_DETECT {
+ bool write_back; /* TRUE if WB, FALSE if WT */
+ bool fp_write_enabled;
+ bool members_ssds;
+ bool fp_cache_bypass_capable;
+ u32 mru_bit_map; /* bitmap used to track MRU and LRU stream indicies */
+ /* this is the array of stream detect structures (one per stream) */
+ struct STREAM_DETECT stream_track[MAX_STREAMS_TRACKED];
+};
+
+struct MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY {
+ u64 RDPQBaseAddress;
+ u32 Reserved1;
+ u32 Reserved2;
+};
+
+struct rdpq_alloc_detail {
+ struct dma_pool *dma_pool_ptr;
+ dma_addr_t pool_entry_phys;
+ union MPI2_REPLY_DESCRIPTORS_UNION *pool_entry_virt;
+};
+
+struct fusion_context {
+ struct megasas_cmd_fusion **cmd_list;
dma_addr_t req_frames_desc_phys;
u8 *req_frames_desc;
@@ -659,37 +1310,94 @@ struct fusion_context {
struct dma_pool *sg_dma_pool;
struct dma_pool *sense_dma_pool;
- dma_addr_t reply_frames_desc_phys;
- union MPI2_REPLY_DESCRIPTORS_UNION *reply_frames_desc;
+ u8 *sense;
+ dma_addr_t sense_phys_addr;
+
+ atomic_t busy_mq_poll[MAX_MSIX_QUEUES_FUSION];
+
+ dma_addr_t reply_frames_desc_phys[MAX_MSIX_QUEUES_FUSION];
+ union MPI2_REPLY_DESCRIPTORS_UNION *reply_frames_desc[MAX_MSIX_QUEUES_FUSION];
+ struct rdpq_alloc_detail rdpq_tracker[RDPQ_MAX_CHUNK_COUNT];
struct dma_pool *reply_frames_desc_pool;
+ struct dma_pool *reply_frames_desc_pool_align;
- u16 last_reply_idx;
+ u16 last_reply_idx[MAX_MSIX_QUEUES_FUSION];
u32 reply_q_depth;
u32 request_alloc_sz;
u32 reply_alloc_sz;
u32 io_frames_alloc_sz;
+ struct MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY *rdpq_virt;
+ dma_addr_t rdpq_phys;
u16 max_sge_in_main_msg;
u16 max_sge_in_chain;
u8 chain_offset_io_request;
u8 chain_offset_mfi_pthru;
- struct MR_FW_RAID_MAP_ALL *ld_map[2];
+ struct MR_FW_RAID_MAP_DYNAMIC *ld_map[2];
dma_addr_t ld_map_phys[2];
- u32 map_sz;
+ /*Non dma-able memory. Driver local copy.*/
+ struct MR_DRV_RAID_MAP_ALL *ld_drv_map[2];
+
+ u32 max_map_sz;
+ u32 current_map_sz;
+ u32 old_map_sz;
+ u32 new_map_sz;
+ u32 drv_map_sz;
+ u32 drv_map_pages;
+ struct MR_PD_CFG_SEQ_NUM_SYNC *pd_seq_sync[JBOD_MAPS_COUNT];
+ dma_addr_t pd_seq_phys[JBOD_MAPS_COUNT];
u8 fast_path_io;
- struct LD_LOAD_BALANCE_INFO load_balance_info[MAX_LOGICAL_DRIVES];
+ struct LD_LOAD_BALANCE_INFO *load_balance_info;
+ u32 load_balance_info_pages;
+ LD_SPAN_INFO *log_to_span;
+ u32 log_to_span_pages;
+ struct LD_STREAM_DETECT **stream_detect_by_ld;
+ dma_addr_t ioc_init_request_phys;
+ struct MPI2_IOC_INIT_REQUEST *ioc_init_request;
+ struct megasas_cmd *ioc_init_cmd;
+ bool pcie_bw_limitation;
+ bool r56_div_offload;
};
union desc_value {
- u64 word;
+ __le64 word;
struct {
- u32 low;
- u32 high;
+ __le32 low;
+ __le32 high;
} u;
};
+enum CMD_RET_VALUES {
+ REFIRE_CMD = 1,
+ COMPLETE_CMD = 2,
+ RETURN_CMD = 3,
+};
+
+struct MR_SNAPDUMP_PROPERTIES {
+ u8 offload_num;
+ u8 max_num_supported;
+ u8 cur_num_supported;
+ u8 trigger_min_num_sec_before_ocr;
+ u8 reserved[12];
+};
+
+struct megasas_debugfs_buffer {
+ void *buf;
+ u32 len;
+};
+
+void megasas_free_cmds_fusion(struct megasas_instance *instance);
+int megasas_ioc_init_fusion(struct megasas_instance *instance);
+u8 megasas_get_map_info(struct megasas_instance *instance);
+int megasas_sync_map_info(struct megasas_instance *instance);
+void megasas_release_fusion(struct megasas_instance *instance);
+void megasas_reset_reply_desc(struct megasas_instance *instance);
+int megasas_check_mpio_paths(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd);
+void megasas_fusion_ocr_wq(struct work_struct *work);
+
#endif /* _MEGARAID_SAS_FUSION_H_ */
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index 494474779532..768b85eecc8f 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SCSI low-level driver for the MESH (Macintosh Enhanced SCSI Hardware)
* bus adaptor found on Power Macintosh computers.
@@ -29,17 +30,16 @@
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/pgtable.h>
#include <asm/dbdma.h>
#include <asm/io.h>
-#include <asm/pgtable.h>
#include <asm/prom.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/hydra.h>
#include <asm/processor.h>
-#include <asm/machdep.h>
+#include <asm/setup.h>
#include <asm/pmac_feature.h>
-#include <asm/pci-bridge.h>
#include <asm/macio.h>
#include <scsi/scsi.h>
@@ -54,7 +54,7 @@
#define KERN_DEBUG KERN_WARNING
#endif
-MODULE_AUTHOR("Paul Mackerras (paulus@samba.org)");
+MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
MODULE_DESCRIPTION("PowerMac MESH SCSI driver");
MODULE_LICENSE("GPL");
@@ -342,15 +342,6 @@ static inline void mesh_flush_io(volatile struct mesh_regs __iomem *mr)
}
-/*
- * Complete a SCSI command
- */
-static void mesh_completed(struct mesh_state *ms, struct scsi_cmnd *cmd)
-{
- (*cmd->scsi_done)(cmd);
-}
-
-
/* Called with meshinterrupt disabled, initialize the chipset
* and eventually do the initial bus reset. The lock must not be
* held since we can schedule.
@@ -595,9 +586,12 @@ static void mesh_done(struct mesh_state *ms, int start_next)
ms->current_req = NULL;
tp->current_req = NULL;
if (cmd) {
- cmd->result = (ms->stat << 16) + cmd->SCp.Status;
+ struct mesh_cmd_priv *mcmd = mesh_priv(cmd);
+
+ set_host_byte(cmd, ms->stat);
+ set_status_byte(cmd, mcmd->status);
if (ms->stat == DID_OK)
- cmd->result += (cmd->SCp.Message << 8);
+ scsi_msg_to_host_byte(cmd, mcmd->message);
if (DEBUG_TARGET(cmd)) {
printk(KERN_DEBUG "mesh_done: result = %x, data_ptr=%d, buflen=%d\n",
cmd->result, ms->data_ptr, scsi_bufflen(cmd));
@@ -611,8 +605,8 @@ static void mesh_done(struct mesh_state *ms, int start_next)
}
#endif
}
- cmd->SCp.this_residual -= ms->data_ptr;
- mesh_completed(ms, cmd);
+ mcmd->this_residual -= ms->data_ptr;
+ scsi_done(cmd);
}
if (start_next) {
out_8(&ms->mesh->sequence, SEQ_ENBRESEL);
@@ -993,9 +987,9 @@ static void handle_reset(struct mesh_state *ms)
for (tgt = 0; tgt < 8; ++tgt) {
tp = &ms->tgts[tgt];
if ((cmd = tp->current_req) != NULL) {
- cmd->result = DID_RESET << 16;
+ set_host_byte(cmd, DID_RESET);
tp->current_req = NULL;
- mesh_completed(ms, cmd);
+ scsi_done(cmd);
}
ms->tgts[tgt].sdtr_state = do_sdtr;
ms->tgts[tgt].sync_params = ASYNC_PARAMS;
@@ -1003,8 +997,8 @@ static void handle_reset(struct mesh_state *ms)
ms->current_req = NULL;
while ((cmd = ms->request_q) != NULL) {
ms->request_q = (struct scsi_cmnd *) cmd->host_scribble;
- cmd->result = DID_RESET << 16;
- mesh_completed(ms, cmd);
+ set_host_byte(cmd, DID_RESET);
+ scsi_done(cmd);
}
ms->phase = idle;
ms->msgphase = msg_none;
@@ -1045,6 +1039,8 @@ static void handle_error(struct mesh_state *ms)
while ((in_8(&mr->bus_status1) & BS1_RST) != 0)
udelay(1);
printk("done\n");
+ if (ms->dma_started)
+ halt_dma(ms);
handle_reset(ms);
/* request_q is empty, no point in mesh_start() */
return;
@@ -1177,7 +1173,7 @@ static void handle_msgin(struct mesh_state *ms)
if (ms->n_msgin < msgin_length(ms))
goto reject;
if (cmd)
- cmd->SCp.Message = code;
+ mesh_priv(cmd)->message = code;
switch (code) {
case COMMAND_COMPLETE:
break;
@@ -1231,7 +1227,7 @@ static void handle_msgin(struct mesh_state *ms)
ms->msgphase = msg_out;
} else if (code != cmd->device->lun + IDENTIFY_BASE) {
printk(KERN_WARNING "mesh: lun mismatch "
- "(%d != %d) on reselection from "
+ "(%d != %llu) on reselection from "
"target %d\n", code - IDENTIFY_BASE,
cmd->device->lun, ms->conn_tgt);
}
@@ -1268,7 +1264,7 @@ static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd)
if (cmd) {
int nseg;
- cmd->SCp.this_residual = scsi_bufflen(cmd);
+ mesh_priv(cmd)->this_residual = scsi_bufflen(cmd);
nseg = scsi_dma_map(cmd);
BUG_ON(nseg < 0);
@@ -1288,9 +1284,9 @@ static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd)
}
if (dma_len > 0xffff)
panic("mesh: scatterlist element >= 64k");
- st_le16(&dcmds->req_count, dma_len - off);
- st_le16(&dcmds->command, dma_cmd);
- st_le32(&dcmds->phy_addr, dma_addr + off);
+ dcmds->req_count = cpu_to_le16(dma_len - off);
+ dcmds->command = cpu_to_le16(dma_cmd);
+ dcmds->phy_addr = cpu_to_le32(dma_addr + off);
dcmds->xfer_status = 0;
++dcmds;
dtot += dma_len - off;
@@ -1304,15 +1300,15 @@ static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd)
static char mesh_extra_buf[64];
dtot = sizeof(mesh_extra_buf);
- st_le16(&dcmds->req_count, dtot);
- st_le32(&dcmds->phy_addr, virt_to_phys(mesh_extra_buf));
+ dcmds->req_count = cpu_to_le16(dtot);
+ dcmds->phy_addr = cpu_to_le32(virt_to_phys(mesh_extra_buf));
dcmds->xfer_status = 0;
++dcmds;
}
dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
- st_le16(&dcmds[-1].command, dma_cmd);
+ dcmds[-1].command = cpu_to_le16(dma_cmd);
memset(dcmds, 0, sizeof(*dcmds));
- st_le16(&dcmds->command, DBDMA_STOP);
+ dcmds->command = cpu_to_le16(DBDMA_STOP);
ms->dma_count = dtot;
}
@@ -1357,7 +1353,8 @@ static void halt_dma(struct mesh_state *ms)
ms->conn_tgt, ms->data_ptr, scsi_bufflen(cmd),
ms->tgts[ms->conn_tgt].data_goes_out);
}
- scsi_dma_unmap(cmd);
+ if (cmd)
+ scsi_dma_unmap(cmd);
ms->dma_started = 0;
}
@@ -1454,7 +1451,7 @@ static void cmd_complete(struct mesh_state *ms)
/* huh? we expected a phase mismatch */
ms->n_msgin = 0;
ms->msgphase = msg_in;
- /* fall through */
+ fallthrough;
case msg_in:
/* should have some message bytes in fifo */
@@ -1597,10 +1594,12 @@ static void cmd_complete(struct mesh_state *ms)
break;
case statusing:
if (cmd) {
- cmd->SCp.Status = mr->fifo;
+ struct mesh_cmd_priv *mcmd = mesh_priv(cmd);
+
+ mcmd->status = mr->fifo;
if (DEBUG_TARGET(cmd))
printk(KERN_DEBUG "mesh: status is %x\n",
- cmd->SCp.Status);
+ mcmd->status);
}
ms->msgphase = msg_in;
break;
@@ -1626,11 +1625,10 @@ static void cmd_complete(struct mesh_state *ms)
* Called by midlayer with host locked to queue a new
* request
*/
-static int mesh_queue_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+static int mesh_queue_lck(struct scsi_cmnd *cmd)
{
struct mesh_state *ms;
- cmd->scsi_done = done;
cmd->host_scribble = NULL;
ms = (struct mesh_state *) cmd->device->host->hostdata;
@@ -1712,6 +1710,9 @@ static int mesh_host_reset(struct scsi_cmnd *cmd)
spin_lock_irqsave(ms->host->host_lock, flags);
+ if (ms->dma_started)
+ halt_dma(ms);
+
/* Reset the controller & dbdma channel */
out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */
out_8(&mr->exception, 0xff); /* clear all exception bits */
@@ -1761,6 +1762,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
case PM_EVENT_SUSPEND:
case PM_EVENT_HIBERNATE:
case PM_EVENT_FREEZE:
+ case PM_EVENT_POWEROFF:
break;
default:
return 0;
@@ -1829,7 +1831,7 @@ static int mesh_shutdown(struct macio_dev *mdev)
return 0;
}
-static struct scsi_host_template mesh_template = {
+static const struct scsi_host_template mesh_template = {
.proc_name = "mesh",
.name = "MESH",
.queuecommand = mesh_queue,
@@ -1839,7 +1841,8 @@ static struct scsi_host_template mesh_template = {
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
- .use_clustering = DISABLE_CLUSTERING,
+ .max_segment_size = 65535,
+ .cmd_size = sizeof(struct mesh_cmd_priv),
};
static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match)
@@ -1880,11 +1883,6 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match)
goto out_release;
}
- /* Old junk for root discovery, that will die ultimately */
-#if !defined(MODULE)
- note_scsi_host(mesh, mesh_host);
-#endif
-
mesh_host->base = macio_resource_start(mdev, 0);
mesh_host->irq = macio_irq(mdev, 0);
ms = (struct mesh_state *) mesh_host->hostdata;
@@ -1916,14 +1914,13 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match)
/* We use the PCI APIs for now until the generic one gets fixed
* enough or until we get some macio-specific versions
*/
- dma_cmd_space = pci_alloc_consistent(macio_get_pci_dev(mdev),
- ms->dma_cmd_size,
- &dma_cmd_bus);
+ dma_cmd_space = dma_alloc_coherent(&macio_get_pci_dev(mdev)->dev,
+ ms->dma_cmd_size, &dma_cmd_bus,
+ GFP_KERNEL);
if (dma_cmd_space == NULL) {
printk(KERN_ERR "mesh: can't allocate DMA table\n");
goto out_unmap;
}
- memset(dma_cmd_space, 0, ms->dma_cmd_size);
ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
ms->dma_cmd_space = dma_cmd_space;
@@ -1977,7 +1974,7 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match)
*/
mesh_shutdown(mdev);
set_mesh_power(ms, 0);
- pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size,
+ dma_free_coherent(&macio_get_pci_dev(mdev)->dev, ms->dma_cmd_size,
ms->dma_cmd_space, ms->dma_cmd_bus);
out_unmap:
iounmap(ms->dma);
@@ -1990,7 +1987,7 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match)
return -ENODEV;
}
-static int mesh_remove(struct macio_dev *mdev)
+static void mesh_remove(struct macio_dev *mdev)
{
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
struct Scsi_Host *mesh_host = ms->host;
@@ -2010,18 +2007,15 @@ static int mesh_remove(struct macio_dev *mdev)
iounmap(ms->dma);
/* Free DMA commands memory */
- pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size,
+ dma_free_coherent(&macio_get_pci_dev(mdev)->dev, ms->dma_cmd_size,
ms->dma_cmd_space, ms->dma_cmd_bus);
/* Release memory resources */
macio_release_resources(mdev);
scsi_host_put(mesh_host);
-
- return 0;
}
-
static struct of_device_id mesh_match[] =
{
{
diff --git a/drivers/scsi/mesh.h b/drivers/scsi/mesh.h
index 4fdb81fa55e2..f70181acceac 100644
--- a/drivers/scsi/mesh.h
+++ b/drivers/scsi/mesh.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mesh.h: definitions for the driver for the MESH SCSI bus adaptor
* (Macintosh Enhanced SCSI Hardware) found on Power Macintosh computers.
@@ -7,6 +8,17 @@
#ifndef _MESH_H
#define _MESH_H
+struct mesh_cmd_priv {
+ int this_residual;
+ int message;
+ int status;
+};
+
+static inline struct mesh_cmd_priv *mesh_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
/*
* Registers in the MESH controller.
*/
diff --git a/drivers/scsi/mpi3mr/Kconfig b/drivers/scsi/mpi3mr/Kconfig
new file mode 100644
index 000000000000..f48740cd5b95
--- /dev/null
+++ b/drivers/scsi/mpi3mr/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config SCSI_MPI3MR
+ tristate "Broadcom MPI3 Storage Controller Device Driver"
+ depends on PCI && SCSI
+ select BLK_DEV_BSGLIB
+ select SCSI_SAS_ATTRS
+ help
+ MPI3 based Storage & RAID Controllers Driver.
diff --git a/drivers/scsi/mpi3mr/Makefile b/drivers/scsi/mpi3mr/Makefile
new file mode 100644
index 000000000000..3bf8cf34e1c3
--- /dev/null
+++ b/drivers/scsi/mpi3mr/Makefile
@@ -0,0 +1,6 @@
+# mpi3mr makefile
+obj-$(CONFIG_SCSI_MPI3MR) += mpi3mr.o
+mpi3mr-y += mpi3mr_os.o \
+ mpi3mr_fw.o \
+ mpi3mr_app.o \
+ mpi3mr_transport.o
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h
new file mode 100644
index 000000000000..8c8bfbbdd34e
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h
@@ -0,0 +1,2566 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2017-2023 Broadcom Inc. All rights reserved.
+ */
+#ifndef MPI30_CNFG_H
+#define MPI30_CNFG_H 1
+#define MPI3_CONFIG_PAGETYPE_IO_UNIT (0x00)
+#define MPI3_CONFIG_PAGETYPE_MANUFACTURING (0x01)
+#define MPI3_CONFIG_PAGETYPE_IOC (0x02)
+#define MPI3_CONFIG_PAGETYPE_DRIVER (0x03)
+#define MPI3_CONFIG_PAGETYPE_SECURITY (0x04)
+#define MPI3_CONFIG_PAGETYPE_ENCLOSURE (0x11)
+#define MPI3_CONFIG_PAGETYPE_DEVICE (0x12)
+#define MPI3_CONFIG_PAGETYPE_SAS_IO_UNIT (0x20)
+#define MPI3_CONFIG_PAGETYPE_SAS_EXPANDER (0x21)
+#define MPI3_CONFIG_PAGETYPE_SAS_PHY (0x23)
+#define MPI3_CONFIG_PAGETYPE_SAS_PORT (0x24)
+#define MPI3_CONFIG_PAGETYPE_PCIE_IO_UNIT (0x30)
+#define MPI3_CONFIG_PAGETYPE_PCIE_SWITCH (0x31)
+#define MPI3_CONFIG_PAGETYPE_PCIE_LINK (0x33)
+#define MPI3_CONFIG_PAGEATTR_MASK (0xf0)
+#define MPI3_CONFIG_PAGEATTR_SHIFT (4)
+#define MPI3_CONFIG_PAGEATTR_READ_ONLY (0x00)
+#define MPI3_CONFIG_PAGEATTR_CHANGEABLE (0x10)
+#define MPI3_CONFIG_PAGEATTR_PERSISTENT (0x20)
+#define MPI3_CONFIG_ACTION_PAGE_HEADER (0x00)
+#define MPI3_CONFIG_ACTION_READ_DEFAULT (0x01)
+#define MPI3_CONFIG_ACTION_READ_CURRENT (0x02)
+#define MPI3_CONFIG_ACTION_WRITE_CURRENT (0x03)
+#define MPI3_CONFIG_ACTION_READ_PERSISTENT (0x04)
+#define MPI3_CONFIG_ACTION_WRITE_PERSISTENT (0x05)
+#define MPI3_DEVICE_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_DEVICE_PGAD_FORM_SHIFT (28)
+#define MPI3_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_DEVICE_PGAD_FORM_HANDLE (0x20000000)
+#define MPI3_DEVICE_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_DEVICE_PGAD_HANDLE_SHIFT (0)
+#define MPI3_SAS_EXPAND_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SAS_EXPAND_PGAD_FORM_SHIFT (28)
+#define MPI3_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM (0x10000000)
+#define MPI3_SAS_EXPAND_PGAD_FORM_HANDLE (0x20000000)
+#define MPI3_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00ff0000)
+#define MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16)
+#define MPI3_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_SAS_PHY_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000)
+#define MPI3_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000ff)
+#define MPI3_SASPORT_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000)
+#define MPI3_SASPORT_PGAD_FORM_PORT_NUM (0x10000000)
+#define MPI3_SASPORT_PGAD_PORT_NUMBER_MASK (0x000000ff)
+#define MPI3_ENCLOS_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_ENCLOS_PGAD_FORM_HANDLE (0x10000000)
+#define MPI3_ENCLOS_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_HANDLE_PORT_NUM (0x10000000)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_HANDLE (0x20000000)
+#define MPI3_PCIE_SWITCH_PGAD_PORTNUM_MASK (0x00ff0000)
+#define MPI3_PCIE_SWITCH_PGAD_PORTNUM_SHIFT (16)
+#define MPI3_PCIE_SWITCH_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_PCIE_LINK_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_PCIE_LINK_PGAD_FORM_GET_NEXT_LINK (0x00000000)
+#define MPI3_PCIE_LINK_PGAD_FORM_LINK_NUM (0x10000000)
+#define MPI3_PCIE_LINK_PGAD_LINKNUM_MASK (0x000000ff)
+#define MPI3_SECURITY_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SECURITY_PGAD_FORM_GET_NEXT_SLOT (0x00000000)
+#define MPI3_SECURITY_PGAD_FORM_SLOT_NUM (0x10000000)
+#define MPI3_SECURITY_PGAD_SLOT_GROUP_MASK (0x0000ff00)
+#define MPI3_SECURITY_PGAD_SLOT_GROUP_SHIFT (8)
+#define MPI3_SECURITY_PGAD_SLOT_MASK (0x000000ff)
+#define MPI3_INSTANCE_PGAD_INSTANCE_MASK (0x0000ffff)
+struct mpi3_config_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 proxy_ioc_number;
+ u8 reserved0b;
+ u8 page_version;
+ u8 page_number;
+ u8 page_type;
+ u8 action;
+ __le32 page_address;
+ __le16 page_length;
+ __le16 reserved16;
+ __le32 reserved18[2];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_config_page_header {
+ u8 page_version;
+ u8 reserved01;
+ u8 page_number;
+ u8 page_attribute;
+ __le16 page_length;
+ u8 page_type;
+ u8 reserved07;
+};
+
+#define MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK (0xf0)
+#define MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT (4)
+#define MPI3_SAS_NEG_LINK_RATE_PHYSICAL_MASK (0x0f)
+#define MPI3_SAS_NEG_LINK_RATE_PHYSICAL_SHIFT (0)
+#define MPI3_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00)
+#define MPI3_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI3_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02)
+#define MPI3_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03)
+#define MPI3_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04)
+#define MPI3_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI3_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06)
+#define MPI3_SAS_NEG_LINK_RATE_1_5 (0x08)
+#define MPI3_SAS_NEG_LINK_RATE_3_0 (0x09)
+#define MPI3_SAS_NEG_LINK_RATE_6_0 (0x0a)
+#define MPI3_SAS_NEG_LINK_RATE_12_0 (0x0b)
+#define MPI3_SAS_NEG_LINK_RATE_22_5 (0x0c)
+#define MPI3_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040)
+#define MPI3_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020)
+#define MPI3_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010)
+#define MPI3_SAS_APHYINFO_REASON_MASK (0x0000000f)
+#define MPI3_SAS_APHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI3_SAS_APHYINFO_REASON_POWER_ON (0x00000001)
+#define MPI3_SAS_APHYINFO_REASON_HARD_RESET (0x00000002)
+#define MPI3_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003)
+#define MPI3_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004)
+#define MPI3_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005)
+#define MPI3_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006)
+#define MPI3_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007)
+#define MPI3_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008)
+#define MPI3_SAS_APHYINFO_REASON_EXP_REDUCED_FUNC (0x00000009)
+#define MPI3_SAS_PHYINFO_STATUS_MASK (0xc0000000)
+#define MPI3_SAS_PHYINFO_STATUS_SHIFT (30)
+#define MPI3_SAS_PHYINFO_STATUS_ACCESSIBLE (0x00000000)
+#define MPI3_SAS_PHYINFO_STATUS_NOT_EXIST (0x40000000)
+#define MPI3_SAS_PHYINFO_STATUS_VACANT (0x80000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_ACTIVE (0x00000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_PARTIAL (0x08000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_SLUMBER (0x10000000)
+#define MPI3_SAS_PHYINFO_REQUESTED_INSIDE_ZPSDS_CHANGED_MASK (0x04000000)
+#define MPI3_SAS_PHYINFO_REQUESTED_INSIDE_ZPSDS_CHANGED_SHIFT (26)
+#define MPI3_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT_MASK (0x02000000)
+#define MPI3_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT_SHIFT (25)
+#define MPI3_SAS_PHYINFO_REQUESTED_INSIDE_ZPSDS_MASK (0x01000000)
+#define MPI3_SAS_PHYINFO_REQUESTED_INSIDE_ZPSDS_SHIFT (24)
+#define MPI3_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000)
+#define MPI3_SAS_PHYINFO_INSIDE_ZPSDS_WITHIN (0x00200000)
+#define MPI3_SAS_PHYINFO_ZONING_ENABLED (0x00100000)
+#define MPI3_SAS_PHYINFO_REASON_MASK (0x000f0000)
+#define MPI3_SAS_PHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI3_SAS_PHYINFO_REASON_POWER_ON (0x00010000)
+#define MPI3_SAS_PHYINFO_REASON_HARD_RESET (0x00020000)
+#define MPI3_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000)
+#define MPI3_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000)
+#define MPI3_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000)
+#define MPI3_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000)
+#define MPI3_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000)
+#define MPI3_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000)
+#define MPI3_SAS_PHYINFO_REASON_EXP_REDUCED_FUNC (0x00090000)
+#define MPI3_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000)
+#define MPI3_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000)
+#define MPI3_SAS_PHYINFO_VIRTUAL_PHY (0x00001000)
+#define MPI3_SAS_PHYINFO_PARTIAL_PATHWAY_TIME_MASK (0x00000f00)
+#define MPI3_SAS_PHYINFO_PARTIAL_PATHWAY_TIME_SHIFT (8)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_MASK (0x000000f0)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_DIRECT (0x00000000)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_SUBTRACTIVE (0x00000010)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_TABLE (0x00000020)
+#define MPI3_SAS_PRATE_MAX_RATE_MASK (0xf0)
+#define MPI3_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI3_SAS_PRATE_MAX_RATE_1_5 (0x80)
+#define MPI3_SAS_PRATE_MAX_RATE_3_0 (0x90)
+#define MPI3_SAS_PRATE_MAX_RATE_6_0 (0xa0)
+#define MPI3_SAS_PRATE_MAX_RATE_12_0 (0xb0)
+#define MPI3_SAS_PRATE_MAX_RATE_22_5 (0xc0)
+#define MPI3_SAS_PRATE_MIN_RATE_MASK (0x0f)
+#define MPI3_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI3_SAS_PRATE_MIN_RATE_1_5 (0x08)
+#define MPI3_SAS_PRATE_MIN_RATE_3_0 (0x09)
+#define MPI3_SAS_PRATE_MIN_RATE_6_0 (0x0a)
+#define MPI3_SAS_PRATE_MIN_RATE_12_0 (0x0b)
+#define MPI3_SAS_PRATE_MIN_RATE_22_5 (0x0c)
+#define MPI3_SAS_HWRATE_MAX_RATE_MASK (0xf0)
+#define MPI3_SAS_HWRATE_MAX_RATE_1_5 (0x80)
+#define MPI3_SAS_HWRATE_MAX_RATE_3_0 (0x90)
+#define MPI3_SAS_HWRATE_MAX_RATE_6_0 (0xa0)
+#define MPI3_SAS_HWRATE_MAX_RATE_12_0 (0xb0)
+#define MPI3_SAS_HWRATE_MAX_RATE_22_5 (0xc0)
+#define MPI3_SAS_HWRATE_MIN_RATE_MASK (0x0f)
+#define MPI3_SAS_HWRATE_MIN_RATE_1_5 (0x08)
+#define MPI3_SAS_HWRATE_MIN_RATE_3_0 (0x09)
+#define MPI3_SAS_HWRATE_MIN_RATE_6_0 (0x0a)
+#define MPI3_SAS_HWRATE_MIN_RATE_12_0 (0x0b)
+#define MPI3_SAS_HWRATE_MIN_RATE_22_5 (0x0c)
+#define MPI3_SLOT_INVALID (0xffff)
+#define MPI3_SLOT_INDEX_INVALID (0xffff)
+#define MPI3_LINK_CHANGE_COUNT_INVALID (0xffff)
+#define MPI3_RATE_CHANGE_COUNT_INVALID (0xffff)
+#define MPI3_TEMP_SENSOR_LOCATION_INTERNAL (0x0)
+#define MPI3_TEMP_SENSOR_LOCATION_INLET (0x1)
+#define MPI3_TEMP_SENSOR_LOCATION_OUTLET (0x2)
+#define MPI3_TEMP_SENSOR_LOCATION_DRAM (0x3)
+#define MPI3_MFGPAGE_VENDORID_BROADCOM (0x1000)
+#define MPI3_MFGPAGE_DEVID_SAS4116 (0x00a5)
+#define MPI3_MFGPAGE_DEVID_SAS5116_MPI (0x00b3)
+#define MPI3_MFGPAGE_DEVID_SAS5116_NVME (0x00b4)
+#define MPI3_MFGPAGE_DEVID_SAS5116_MPI_MGMT (0x00b5)
+#define MPI3_MFGPAGE_DEVID_SAS5116_NVME_MGMT (0x00b6)
+#define MPI3_MFGPAGE_DEVID_SAS5116_PCIE_SWITCH (0x00b8)
+#define MPI3_MFGPAGE_DEVID_SAS5248_MPI (0x00f0)
+#define MPI3_MFGPAGE_DEVID_SAS5248_MPI_NS (0x00f1)
+#define MPI3_MFGPAGE_DEVID_SAS5248_PCIE_SWITCH (0x00f2)
+struct mpi3_man_page0 {
+ struct mpi3_config_page_header header;
+ u8 chip_revision[8];
+ u8 chip_name[32];
+ u8 board_name[32];
+ u8 board_assembly[32];
+ u8 board_tracer_number[32];
+ __le32 board_power;
+ __le32 reserved94;
+ __le32 reserved98;
+ u8 oem;
+ u8 profile_identifier;
+ __le16 flags;
+ u8 board_mfg_day;
+ u8 board_mfg_month;
+ __le16 board_mfg_year;
+ u8 board_rework_day;
+ u8 board_rework_month;
+ __le16 board_rework_year;
+ u8 board_revision[8];
+ u8 e_pack_fru[16];
+ u8 product_name[256];
+};
+
+#define MPI3_MAN0_PAGEVERSION (0x00)
+#define MPI3_MAN0_FLAGS_SWITCH_PRESENT (0x0002)
+#define MPI3_MAN0_FLAGS_EXPANDER_PRESENT (0x0001)
+#define MPI3_MAN1_VPD_SIZE (512)
+struct mpi3_man_page1 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08[2];
+ u8 vpd[MPI3_MAN1_VPD_SIZE];
+};
+
+#define MPI3_MAN1_PAGEVERSION (0x00)
+struct mpi3_man_page2 {
+ struct mpi3_config_page_header header;
+ u8 flags;
+ u8 reserved09[3];
+ __le32 reserved0c[3];
+ u8 oem_board_tracer_number[32];
+};
+#define MPI3_MAN2_PAGEVERSION (0x00)
+#define MPI3_MAN2_FLAGS_TRACER_PRESENT (0x01)
+struct mpi3_man5_phy_entry {
+ __le64 ioc_wwid;
+ __le64 device_name;
+ __le64 sata_wwid;
+};
+
+#ifndef MPI3_MAN5_PHY_MAX
+#define MPI3_MAN5_PHY_MAX (1)
+#endif
+struct mpi3_man_page5 {
+ struct mpi3_config_page_header header;
+ u8 num_phys;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_man5_phy_entry phy[MPI3_MAN5_PHY_MAX];
+};
+
+#define MPI3_MAN5_PAGEVERSION (0x00)
+struct mpi3_man6_gpio_entry {
+ u8 function_code;
+ u8 function_flags;
+ __le16 flags;
+ u8 param1;
+ u8 param2;
+ __le16 reserved06;
+ __le32 param3;
+};
+
+#define MPI3_MAN6_GPIO_FUNCTION_GENERIC (0x00)
+#define MPI3_MAN6_GPIO_FUNCTION_ALTERNATE (0x01)
+#define MPI3_MAN6_GPIO_FUNCTION_EXT_INTERRUPT (0x02)
+#define MPI3_MAN6_GPIO_FUNCTION_GLOBAL_ACTIVITY (0x03)
+#define MPI3_MAN6_GPIO_FUNCTION_OVER_TEMPERATURE (0x04)
+#define MPI3_MAN6_GPIO_FUNCTION_PORT_STATUS_GREEN (0x05)
+#define MPI3_MAN6_GPIO_FUNCTION_PORT_STATUS_YELLOW (0x06)
+#define MPI3_MAN6_GPIO_FUNCTION_CABLE_MANAGEMENT (0x07)
+#define MPI3_MAN6_GPIO_FUNCTION_BKPLANE_MGMT_TYPE (0x08)
+#define MPI3_MAN6_GPIO_FUNCTION_ISTWI_RESET (0x0a)
+#define MPI3_MAN6_GPIO_FUNCTION_BACKEND_PCIE_RESET (0x0b)
+#define MPI3_MAN6_GPIO_FUNCTION_GLOBAL_FAULT (0x0c)
+#define MPI3_MAN6_GPIO_FUNCTION_PBLP_STATUS_CHANGE (0x0d)
+#define MPI3_MAN6_GPIO_FUNCTION_EPACK_ONLINE (0x0e)
+#define MPI3_MAN6_GPIO_FUNCTION_EPACK_FAULT (0x0f)
+#define MPI3_MAN6_GPIO_FUNCTION_CTRL_TYPE (0x10)
+#define MPI3_MAN6_GPIO_FUNCTION_LICENSE (0x11)
+#define MPI3_MAN6_GPIO_FUNCTION_REFCLK_CONTROL (0x12)
+#define MPI3_MAN6_GPIO_FUNCTION_BACKEND_PCIE_RESET_CLAMP (0x13)
+#define MPI3_MAN6_GPIO_FUNCTION_AUXILIARY_POWER (0x14)
+#define MPI3_MAN6_GPIO_FUNCTION_RAID_DATA_CACHE_DIRTY (0x15)
+#define MPI3_MAN6_GPIO_FUNCTION_BOARD_FAN_CONTROL (0x16)
+#define MPI3_MAN6_GPIO_FUNCTION_BOARD_FAN_FAULT (0x17)
+#define MPI3_MAN6_GPIO_FUNCTION_POWER_BRAKE (0x18)
+#define MPI3_MAN6_GPIO_ISTWI_RESET_FUNCTIONFLAGS_DEVSELECT_MASK (0x01)
+#define MPI3_MAN6_GPIO_ISTWI_RESET_FUNCTIONFLAGS_DEVSELECT_ISTWI (0x00)
+#define MPI3_MAN6_GPIO_ISTWI_RESET_FUNCTIONFLAGS_DEVSELECT_RECEPTACLEID (0x01)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_MASK (0xf0)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_GENERIC (0x00)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_CABLE_MGMT (0x10)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_ACTIVE_CABLE_OVERCURRENT (0x20)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_ACK_REQUIRED (0x02)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_MASK (0x01)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_EDGE (0x00)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_LEVEL (0x01)
+#define MPI3_MAN6_GPIO_OVER_TEMP_PARAM1_LEVEL_WARNING (0x00)
+#define MPI3_MAN6_GPIO_OVER_TEMP_PARAM1_LEVEL_CRITICAL (0x01)
+#define MPI3_MAN6_GPIO_OVER_TEMP_PARAM1_LEVEL_FATAL (0x02)
+#define MPI3_MAN6_GPIO_PORT_GREEN_PARAM1_PHY_STATUS_ALL_UP (0x00)
+#define MPI3_MAN6_GPIO_PORT_GREEN_PARAM1_PHY_STATUS_ONE_OR_MORE_UP (0x01)
+#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_MODULE_PRESENT (0x00)
+#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_ACTIVE_CABLE_ENABLE (0x01)
+#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_CABLE_MGMT_ENABLE (0x02)
+#define MPI3_MAN6_GPIO_LICENSE_PARAM1_TYPE_IBUTTON (0x00)
+#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_MASK (0x0100)
+#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_FAST_EDGE (0x0100)
+#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_SLOW_EDGE (0x0000)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_MASK (0x00c0)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_100OHM (0x0000)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_66OHM (0x0040)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_50OHM (0x0080)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_33OHM (0x00c0)
+#define MPI3_MAN6_GPIO_FLAGS_ALT_DATA_SEL_MASK (0x0030)
+#define MPI3_MAN6_GPIO_FLAGS_ALT_DATA_SEL_SHIFT (4)
+#define MPI3_MAN6_GPIO_FLAGS_ACTIVE_HIGH (0x0008)
+#define MPI3_MAN6_GPIO_FLAGS_BI_DIR_ENABLED (0x0004)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_MASK (0x0003)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_INPUT (0x0000)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_OPEN_DRAIN_OUTPUT (0x0001)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_OPEN_SOURCE_OUTPUT (0x0002)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_PUSH_PULL_OUTPUT (0x0003)
+#ifndef MPI3_MAN6_GPIO_MAX
+#define MPI3_MAN6_GPIO_MAX (1)
+#endif
+struct mpi3_man_page6 {
+ struct mpi3_config_page_header header;
+ __le16 flags;
+ __le16 reserved0a;
+ u8 num_gpio;
+ u8 reserved0d[3];
+ struct mpi3_man6_gpio_entry gpio[MPI3_MAN6_GPIO_MAX];
+};
+
+#define MPI3_MAN6_PAGEVERSION (0x00)
+#define MPI3_MAN6_FLAGS_HEARTBEAT_LED_DISABLED (0x0001)
+struct mpi3_man7_receptacle_info {
+ __le32 name[4];
+ u8 location;
+ u8 connector_type;
+ u8 ped_clk;
+ u8 connector_id;
+ __le32 reserved14;
+};
+
+#define MPI3_MAN7_LOCATION_UNKNOWN (0x00)
+#define MPI3_MAN7_LOCATION_INTERNAL (0x01)
+#define MPI3_MAN7_LOCATION_EXTERNAL (0x02)
+#define MPI3_MAN7_LOCATION_VIRTUAL (0x03)
+#define MPI3_MAN7_LOCATION_HOST (0x04)
+#define MPI3_MAN7_CONNECTOR_TYPE_NO_INFO (0x00)
+#define MPI3_MAN7_PEDCLK_ROUTING_MASK (0x10)
+#define MPI3_MAN7_PEDCLK_ROUTING_DIRECT (0x00)
+#define MPI3_MAN7_PEDCLK_ROUTING_CLOCK_BUFFER (0x10)
+#define MPI3_MAN7_PEDCLK_ID_MASK (0x0f)
+#ifndef MPI3_MAN7_RECEPTACLE_INFO_MAX
+#define MPI3_MAN7_RECEPTACLE_INFO_MAX (1)
+#endif
+struct mpi3_man_page7 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ u8 num_receptacles;
+ u8 reserved0d[3];
+ __le32 enclosure_name[4];
+ struct mpi3_man7_receptacle_info receptacle_info[MPI3_MAN7_RECEPTACLE_INFO_MAX];
+};
+
+#define MPI3_MAN7_PAGEVERSION (0x00)
+#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_MASK (0x01)
+#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_0 (0x00)
+#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_1 (0x01)
+struct mpi3_man8_phy_info {
+ u8 receptacle_id;
+ u8 connector_lane;
+ __le16 reserved02;
+ __le16 slotx1;
+ __le16 slotx2;
+ __le16 slotx4;
+ __le16 reserved0a;
+ __le32 reserved0c;
+};
+
+#define MPI3_MAN8_PHY_INFO_RECEPTACLE_ID_NOT_ASSOCIATED (0xff)
+#define MPI3_MAN8_PHY_INFO_CONNECTOR_LANE_NOT_ASSOCIATED (0xff)
+#ifndef MPI3_MAN8_PHY_INFO_MAX
+#define MPI3_MAN8_PHY_INFO_MAX (1)
+#endif
+struct mpi3_man_page8 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phys;
+ u8 reserved0d[3];
+ struct mpi3_man8_phy_info phy_info[MPI3_MAN8_PHY_INFO_MAX];
+};
+
+#define MPI3_MAN8_PAGEVERSION (0x00)
+struct mpi3_man9_rsrc_entry {
+ __le32 maximum;
+ __le32 decrement;
+ __le32 minimum;
+ __le32 actual;
+};
+
+enum mpi3_man9_resources {
+ MPI3_MAN9_RSRC_OUTSTANDING_REQS = 0,
+ MPI3_MAN9_RSRC_TARGET_CMDS = 1,
+ MPI3_MAN9_RSRC_RESERVED02 = 2,
+ MPI3_MAN9_RSRC_NVME = 3,
+ MPI3_MAN9_RSRC_INITIATORS = 4,
+ MPI3_MAN9_RSRC_VDS = 5,
+ MPI3_MAN9_RSRC_ENCLOSURES = 6,
+ MPI3_MAN9_RSRC_ENCLOSURE_PHYS = 7,
+ MPI3_MAN9_RSRC_EXPANDERS = 8,
+ MPI3_MAN9_RSRC_PCIE_SWITCHES = 9,
+ MPI3_MAN9_RSRC_RESERVED10 = 10,
+ MPI3_MAN9_RSRC_HOST_PD_DRIVES = 11,
+ MPI3_MAN9_RSRC_ADV_HOST_PD_DRIVES = 12,
+ MPI3_MAN9_RSRC_RAID_PD_DRIVES = 13,
+ MPI3_MAN9_RSRC_DRV_DIAG_BUF = 14,
+ MPI3_MAN9_RSRC_NAMESPACE_COUNT = 15,
+ MPI3_MAN9_RSRC_NUM_RESOURCES
+};
+
+#define MPI3_MAN9_MIN_OUTSTANDING_REQS (1)
+#define MPI3_MAN9_MAX_OUTSTANDING_REQS (65000)
+#define MPI3_MAN9_MIN_TARGET_CMDS (0)
+#define MPI3_MAN9_MAX_TARGET_CMDS (65535)
+#define MPI3_MAN9_MIN_NVME_TARGETS (0)
+#define MPI3_MAN9_MIN_INITIATORS (0)
+#define MPI3_MAN9_MIN_VDS (0)
+#define MPI3_MAN9_MIN_ENCLOSURES (1)
+#define MPI3_MAN9_MAX_ENCLOSURES (65535)
+#define MPI3_MAN9_MIN_ENCLOSURE_PHYS (0)
+#define MPI3_MAN9_MIN_EXPANDERS (0)
+#define MPI3_MAN9_MAX_EXPANDERS (65535)
+#define MPI3_MAN9_MIN_PCIE_SWITCHES (0)
+#define MPI3_MAN9_MIN_HOST_PD_DRIVES (0)
+#define MPI3_MAN9_ADV_HOST_PD_DRIVES (0)
+#define MPI3_MAN9_RAID_PD_DRIVES (0)
+#define MPI3_MAN9_DRIVER_DIAG_BUFFER (0)
+#define MPI3_MAN9_MIN_NAMESPACE_COUNT (1)
+#define MPI3_MAN9_MIN_EXPANDERS (0)
+#define MPI3_MAN9_MAX_EXPANDERS (65535)
+struct mpi3_man_page9 {
+ struct mpi3_config_page_header header;
+ u8 num_resources;
+ u8 reserved09;
+ __le16 reserved0a;
+ __le32 reserved0c;
+ __le32 reserved10;
+ __le32 reserved14;
+ __le32 reserved18;
+ __le32 reserved1c;
+ struct mpi3_man9_rsrc_entry resource[MPI3_MAN9_RSRC_NUM_RESOURCES];
+};
+
+#define MPI3_MAN9_PAGEVERSION (0x00)
+struct mpi3_man10_istwi_ctrlr_entry {
+ __le16 target_address;
+ __le16 flags;
+ u8 scl_low_override;
+ u8 scl_high_override;
+ __le16 reserved06;
+};
+
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_MASK (0x000c)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_100K (0x0000)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_BUS_SPEED_400K (0x0004)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_TARGET_ENABLED (0x0002)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_INITIATOR_ENABLED (0x0001)
+#ifndef MPI3_MAN10_ISTWI_CTRLR_MAX
+#define MPI3_MAN10_ISTWI_CTRLR_MAX (1)
+#endif
+struct mpi3_man_page10 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_istwi_ctrl;
+ u8 reserved0d[3];
+ struct mpi3_man10_istwi_ctrlr_entry istwi_controller[MPI3_MAN10_ISTWI_CTRLR_MAX];
+};
+
+#define MPI3_MAN10_PAGEVERSION (0x00)
+struct mpi3_man11_mux_device_format {
+ u8 max_channel;
+ u8 reserved01[3];
+ __le32 reserved04;
+};
+
+struct mpi3_man11_temp_sensor_device_format {
+ u8 type;
+ u8 reserved01[3];
+ u8 temp_channel[4];
+};
+
+#define MPI3_MAN11_TEMP_SENSOR_TYPE_MAX6654 (0x00)
+#define MPI3_MAN11_TEMP_SENSOR_TYPE_EMC1442 (0x01)
+#define MPI3_MAN11_TEMP_SENSOR_TYPE_ADT7476 (0x02)
+#define MPI3_MAN11_TEMP_SENSOR_TYPE_SE97B (0x03)
+#define MPI3_MAN11_TEMP_SENSOR_CHANNEL_LOCATION_MASK (0xe0)
+#define MPI3_MAN11_TEMP_SENSOR_CHANNEL_LOCATION_SHIFT (5)
+#define MPI3_MAN11_TEMP_SENSOR_CHANNEL_ENABLED (0x01)
+struct mpi3_man11_seeprom_device_format {
+ u8 size;
+ u8 page_write_size;
+ __le16 reserved02;
+ __le32 reserved04;
+};
+
+#define MPI3_MAN11_SEEPROM_SIZE_1KBITS (0x01)
+#define MPI3_MAN11_SEEPROM_SIZE_2KBITS (0x02)
+#define MPI3_MAN11_SEEPROM_SIZE_4KBITS (0x03)
+#define MPI3_MAN11_SEEPROM_SIZE_8KBITS (0x04)
+#define MPI3_MAN11_SEEPROM_SIZE_16KBITS (0x05)
+#define MPI3_MAN11_SEEPROM_SIZE_32KBITS (0x06)
+#define MPI3_MAN11_SEEPROM_SIZE_64KBITS (0x07)
+#define MPI3_MAN11_SEEPROM_SIZE_128KBITS (0x08)
+struct mpi3_man11_ddr_spd_device_format {
+ u8 channel;
+ u8 reserved01[3];
+ __le32 reserved04;
+};
+
+struct mpi3_man11_cable_mgmt_device_format {
+ u8 type;
+ u8 receptacle_id;
+ __le16 reserved02;
+ __le32 reserved04;
+};
+
+#define MPI3_MAN11_CABLE_MGMT_TYPE_SFF_8636 (0x00)
+struct mpi3_man11_bkplane_spec_ubm_format {
+ __le16 flags;
+ __le16 reserved02;
+};
+
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_REFCLK_POLICY_ALWAYS_ENABLED (0x0200)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_FORCE_POLLING (0x0100)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_MAX_FRU_MASK (0x00f0)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_MAX_FRU_SHIFT (4)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_POLL_INTERVAL_MASK (0x000f)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_POLL_INTERVAL_SHIFT (0)
+struct mpi3_man11_bkplane_spec_non_ubm_format {
+ __le16 flags;
+ u8 reserved02;
+ u8 type;
+};
+
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_GROUP_MASK (0xf000)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_GROUP_SHIFT (12)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_REFCLK_POLICY_ALWAYS_ENABLED (0x0200)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_LINKWIDTH_MASK (0x00c0)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_LINKWIDTH_4 (0x0000)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_LINKWIDTH_2 (0x0040)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_LINKWIDTH_1 (0x0080)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_PRESENCE_DETECT_MASK (0x0030)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_PRESENCE_DETECT_GPIO (0x0000)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_PRESENCE_DETECT_REG (0x0010)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_POLL_INTERVAL_MASK (0x000f)
+#define MPI3_MAN11_BKPLANE_NON_UBM_FLAGS_POLL_INTERVAL_SHIFT (0)
+#define MPI3_MAN11_BKPLANE_NON_UBM_TYPE_VPP (0x00)
+union mpi3_man11_bkplane_spec_format {
+ struct mpi3_man11_bkplane_spec_ubm_format ubm;
+ struct mpi3_man11_bkplane_spec_non_ubm_format non_ubm;
+};
+
+struct mpi3_man11_bkplane_mgmt_device_format {
+ u8 type;
+ u8 receptacle_id;
+ u8 reset_info;
+ u8 reserved03;
+ union mpi3_man11_bkplane_spec_format backplane_mgmt_specific;
+};
+
+#define MPI3_MAN11_BKPLANE_MGMT_TYPE_UBM (0x00)
+#define MPI3_MAN11_BKPLANE_MGMT_TYPE_NON_UBM (0x01)
+#define MPI3_MAN11_BACKPLANE_RESETINFO_ASSERT_TIME_MASK (0xf0)
+#define MPI3_MAN11_BACKPLANE_RESETINFO_ASSERT_TIME_SHIFT (4)
+#define MPI3_MAN11_BACKPLANE_RESETINFO_READY_TIME_MASK (0x0f)
+#define MPI3_MAN11_BACKPLANE_RESETINFO_READY_TIME_SHIFT (0)
+struct mpi3_man11_gas_gauge_device_format {
+ u8 type;
+ u8 reserved01[3];
+ __le32 reserved04;
+};
+
+#define MPI3_MAN11_GAS_GAUGE_TYPE_STANDARD (0x00)
+struct mpi3_man11_mgmt_ctrlr_device_format {
+ __le32 reserved00;
+ __le32 reserved04;
+};
+struct mpi3_man11_board_fan_device_format {
+ u8 flags;
+ u8 reserved01;
+ u8 min_fan_speed;
+ u8 max_fan_speed;
+ __le32 reserved04;
+};
+#define MPI3_MAN11_BOARD_FAN_FLAGS_FAN_CTRLR_TYPE_MASK (0x07)
+#define MPI3_MAN11_BOARD_FAN_FLAGS_FAN_CTRLR_TYPE_AMC6821 (0x00)
+union mpi3_man11_device_specific_format {
+ struct mpi3_man11_mux_device_format mux;
+ struct mpi3_man11_temp_sensor_device_format temp_sensor;
+ struct mpi3_man11_seeprom_device_format seeprom;
+ struct mpi3_man11_ddr_spd_device_format ddr_spd;
+ struct mpi3_man11_cable_mgmt_device_format cable_mgmt;
+ struct mpi3_man11_bkplane_mgmt_device_format bkplane_mgmt;
+ struct mpi3_man11_gas_gauge_device_format gas_gauge;
+ struct mpi3_man11_mgmt_ctrlr_device_format mgmt_controller;
+ struct mpi3_man11_board_fan_device_format board_fan;
+ __le32 words[2];
+};
+struct mpi3_man11_istwi_device_format {
+ u8 device_type;
+ u8 controller;
+ u8 reserved02;
+ u8 flags;
+ __le16 device_address;
+ u8 mux_channel;
+ u8 mux_index;
+ union mpi3_man11_device_specific_format device_specific;
+};
+
+#define MPI3_MAN11_ISTWI_DEVTYPE_MUX (0x00)
+#define MPI3_MAN11_ISTWI_DEVTYPE_TEMP_SENSOR (0x01)
+#define MPI3_MAN11_ISTWI_DEVTYPE_SEEPROM (0x02)
+#define MPI3_MAN11_ISTWI_DEVTYPE_DDR_SPD (0x03)
+#define MPI3_MAN11_ISTWI_DEVTYPE_CABLE_MGMT (0x04)
+#define MPI3_MAN11_ISTWI_DEVTYPE_BACKPLANE_MGMT (0x05)
+#define MPI3_MAN11_ISTWI_DEVTYPE_GAS_GAUGE (0x06)
+#define MPI3_MAN11_ISTWI_DEVTYPE_MGMT_CONTROLLER (0x07)
+#define MPI3_MAN11_ISTWI_DEVTYPE_BOARD_FAN (0x08)
+#define MPI3_MAN11_ISTWI_FLAGS_MUX_PRESENT (0x01)
+#ifndef MPI3_MAN11_ISTWI_DEVICE_MAX
+#define MPI3_MAN11_ISTWI_DEVICE_MAX (1)
+#endif
+struct mpi3_man_page11 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_istwi_dev;
+ u8 reserved0d[3];
+ struct mpi3_man11_istwi_device_format istwi_device[MPI3_MAN11_ISTWI_DEVICE_MAX];
+};
+
+#define MPI3_MAN11_PAGEVERSION (0x00)
+#ifndef MPI3_MAN12_NUM_SGPIO_MAX
+#define MPI3_MAN12_NUM_SGPIO_MAX (1)
+#endif
+struct mpi3_man12_sgpio_info {
+ u8 slot_count;
+ u8 reserved01[3];
+ __le32 reserved04;
+ u8 phy_order[32];
+};
+
+struct mpi3_man_page12 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ __le32 s_clock_freq;
+ __le32 activity_modulation;
+ u8 num_sgpio;
+ u8 reserved15[3];
+ __le32 reserved18;
+ __le32 reserved1c;
+ __le32 pattern[8];
+ struct mpi3_man12_sgpio_info sgpio_info[MPI3_MAN12_NUM_SGPIO_MAX];
+};
+
+#define MPI3_MAN12_PAGEVERSION (0x00)
+#define MPI3_MAN12_FLAGS_ERROR_PRESENCE_ENABLED (0x0400)
+#define MPI3_MAN12_FLAGS_ACTIVITY_INVERT_ENABLED (0x0200)
+#define MPI3_MAN12_FLAGS_GROUP_ID_DISABLED (0x0100)
+#define MPI3_MAN12_FLAGS_SIO_CLK_FILTER_ENABLED (0x0004)
+#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_MASK (0x0002)
+#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_PUSH_PULL (0x0000)
+#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_OPEN_DRAIN (0x0002)
+#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_MASK (0x0001)
+#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_PUSH_PULL (0x0000)
+#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_OPEN_DRAIN (0x0001)
+#define MPI3_MAN12_SIO_CLK_FREQ_MIN (32)
+#define MPI3_MAN12_SIO_CLK_FREQ_MAX (100000)
+#define MPI3_MAN12_ACTIVITY_MODULATION_FORCE_OFF_MASK (0x0000f000)
+#define MPI3_MAN12_ACTIVITY_MODULATION_FORCE_OFF_SHIFT (12)
+#define MPI3_MAN12_ACTIVITY_MODULATION_MAX_ON_MASK (0x00000f00)
+#define MPI3_MAN12_ACTIVITY_MODULATION_MAX_ON_SHIFT (8)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_OFF_MASK (0x000000f0)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_OFF_SHIFT (4)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_ON_MASK (0x0000000f)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_ON_SHIFT (0)
+#define MPI3_MAN12_PATTERN_RATE_MASK (0xe0000000)
+#define MPI3_MAN12_PATTERN_RATE_2_HZ (0x00000000)
+#define MPI3_MAN12_PATTERN_RATE_4_HZ (0x20000000)
+#define MPI3_MAN12_PATTERN_RATE_8_HZ (0x40000000)
+#define MPI3_MAN12_PATTERN_RATE_16_HZ (0x60000000)
+#define MPI3_MAN12_PATTERN_RATE_10_HZ (0x80000000)
+#define MPI3_MAN12_PATTERN_RATE_20_HZ (0xa0000000)
+#define MPI3_MAN12_PATTERN_RATE_40_HZ (0xc0000000)
+#define MPI3_MAN12_PATTERN_LENGTH_MASK (0x1f000000)
+#define MPI3_MAN12_PATTERN_LENGTH_SHIFT (24)
+#define MPI3_MAN12_PATTERN_BIT_PATTERN_MASK (0x00ffffff)
+#define MPI3_MAN12_PATTERN_BIT_PATTERN_SHIFT (0)
+#ifndef MPI3_MAN13_NUM_TRANSLATION_MAX
+#define MPI3_MAN13_NUM_TRANSLATION_MAX (1)
+#endif
+struct mpi3_man13_translation_info {
+ __le32 slot_status;
+ __le32 mask;
+ u8 activity;
+ u8 locate;
+ u8 error;
+ u8 reserved0b;
+};
+
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_FAULT (0x20000000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_OFF (0x10000000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_ACTIVITY (0x00800000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DO_NOT_REMOVE (0x00400000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_MISSING (0x00100000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_INSERT (0x00080000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REMOVAL (0x00040000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IDENTIFY (0x00020000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_OK (0x00008000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_RESERVED_DEVICE (0x00004000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_HOT_SPARE (0x00002000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_CONSISTENCY_CHECK (0x00001000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000800)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IN_FAILED_ARRAY (0x00000400)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REBUILD_REMAP (0x00000200)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REBUILD_REMAP_ABORT (0x00000100)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_PREDICTED_FAILURE (0x00000040)
+#define MPI3_MAN13_BLINK_PATTERN_FORCE_OFF (0x00)
+#define MPI3_MAN13_BLINK_PATTERN_FORCE_ON (0x01)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_0 (0x02)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_1 (0x03)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_2 (0x04)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_3 (0x05)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_4 (0x06)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_5 (0x07)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_6 (0x08)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_7 (0x09)
+#define MPI3_MAN13_BLINK_PATTERN_ACTIVITY (0x0a)
+#define MPI3_MAN13_BLINK_PATTERN_ACTIVITY_TRAIL (0x0b)
+struct mpi3_man_page13 {
+ struct mpi3_config_page_header header;
+ u8 num_trans;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_man13_translation_info translation[MPI3_MAN13_NUM_TRANSLATION_MAX];
+};
+
+#define MPI3_MAN13_PAGEVERSION (0x00)
+struct mpi3_man_page14 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_slot_groups;
+ u8 num_slots;
+ __le16 max_cert_chain_length;
+ __le32 sealed_slots;
+ __le32 populated_slots;
+ __le32 mgmt_pt_updatable_slots;
+};
+#define MPI3_MAN14_PAGEVERSION (0x00)
+#define MPI3_MAN14_NUMSLOTS_MAX (32)
+#ifndef MPI3_MAN15_VERSION_RECORD_MAX
+#define MPI3_MAN15_VERSION_RECORD_MAX 1
+#endif
+struct mpi3_man15_version_record {
+ __le16 spdm_version;
+ __le16 reserved02;
+};
+
+struct mpi3_man_page15 {
+ struct mpi3_config_page_header header;
+ u8 num_version_records;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_man15_version_record version_record[MPI3_MAN15_VERSION_RECORD_MAX];
+};
+
+#define MPI3_MAN15_PAGEVERSION (0x00)
+#ifndef MPI3_MAN16_CERT_ALGO_MAX
+#define MPI3_MAN16_CERT_ALGO_MAX 1
+#endif
+struct mpi3_man16_certificate_algorithm {
+ u8 slot_group;
+ u8 reserved01[3];
+ __le32 base_asym_algo;
+ __le32 base_hash_algo;
+ __le32 reserved0c[3];
+};
+
+struct mpi3_man_page16 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_cert_algos;
+ u8 reserved0d[3];
+ struct mpi3_man16_certificate_algorithm certificate_algorithm[MPI3_MAN16_CERT_ALGO_MAX];
+};
+
+#define MPI3_MAN16_PAGEVERSION (0x00)
+#ifndef MPI3_MAN17_HASH_ALGORITHM_MAX
+#define MPI3_MAN17_HASH_ALGORITHM_MAX 1
+#endif
+struct mpi3_man17_hash_algorithm {
+ u8 meas_specification;
+ u8 reserved01[3];
+ __le32 measurement_hash_algo;
+ __le32 reserved08[2];
+};
+
+struct mpi3_man_page17 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_hash_algos;
+ u8 reserved0d[3];
+ struct mpi3_man17_hash_algorithm hash_algorithm[MPI3_MAN17_HASH_ALGORITHM_MAX];
+};
+
+#define MPI3_MAN17_PAGEVERSION (0x00)
+struct mpi3_man_page20 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 nonpremium_features;
+ u8 allowed_personalities;
+ u8 reserved11[3];
+};
+
+#define MPI3_MAN20_PAGEVERSION (0x00)
+#define MPI3_MAN20_ALLOWEDPERSON_RAID_MASK (0x02)
+#define MPI3_MAN20_ALLOWEDPERSON_RAID_ALLOWED (0x02)
+#define MPI3_MAN20_ALLOWEDPERSON_RAID_NOT_ALLOWED (0x00)
+#define MPI3_MAN20_ALLOWEDPERSON_EHBA_MASK (0x01)
+#define MPI3_MAN20_ALLOWEDPERSON_EHBA_ALLOWED (0x01)
+#define MPI3_MAN20_ALLOWEDPERSON_EHBA_NOT_ALLOWED (0x00)
+#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_MASK (0x01)
+#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_ENABLED (0x00)
+#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_DISABLED (0x01)
+struct mpi3_man_page21 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 flags;
+};
+
+#define MPI3_MAN21_PAGEVERSION (0x00)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_MASK (0x00000060)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_BLOCK (0x00000000)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_ALLOW (0x00000020)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_WARN (0x00000040)
+#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_MASK (0x00000008)
+#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_ALLOW (0x00000000)
+#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_PREVENT (0x00000008)
+#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_MASK (0x00000001)
+#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_DEFAULT (0x00000000)
+#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_OEM_SPECIFIC (0x00000001)
+#ifndef MPI3_MAN_PROD_SPECIFIC_MAX
+#define MPI3_MAN_PROD_SPECIFIC_MAX (1)
+#endif
+struct mpi3_man_page_product_specific {
+ struct mpi3_config_page_header header;
+ __le32 product_specific_info[MPI3_MAN_PROD_SPECIFIC_MAX];
+};
+
+struct mpi3_io_unit_page0 {
+ struct mpi3_config_page_header header;
+ __le64 unique_value;
+ __le32 nvdata_version_default;
+ __le32 nvdata_version_persistent;
+};
+
+#define MPI3_IOUNIT0_PAGEVERSION (0x00)
+struct mpi3_io_unit_page1 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ u8 dmd_io_delay;
+ u8 dmd_report_pcie;
+ u8 dmd_report_sata;
+ u8 dmd_report_sas;
+};
+
+#define MPI3_IOUNIT1_PAGEVERSION (0x00)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_MASK (0x00000030)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_ENABLE (0x00000000)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_DISABLE (0x00000010)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_NO_MODIFY (0x00000020)
+#define MPI3_IOUNIT1_FLAGS_ATA_SECURITY_FREEZE_LOCK (0x00000008)
+#define MPI3_IOUNIT1_FLAGS_WRITE_SAME_BUFFER (0x00000004)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_MASK (0x00000003)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_ENABLE (0x00000000)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_DISABLE (0x00000001)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_UNCHANGED (0x00000002)
+#define MPI3_IOUNIT1_DMD_REPORT_DELAY_TIME_MASK (0x7f)
+#define MPI3_IOUNIT1_DMD_REPORT_UNIT_16_SEC (0x80)
+#ifndef MPI3_IO_UNIT2_GPIO_VAL_MAX
+#define MPI3_IO_UNIT2_GPIO_VAL_MAX (1)
+#endif
+struct mpi3_io_unit_page2 {
+ struct mpi3_config_page_header header;
+ u8 gpio_count;
+ u8 reserved09[3];
+ __le16 gpio_val[MPI3_IO_UNIT2_GPIO_VAL_MAX];
+};
+
+#define MPI3_IOUNIT2_PAGEVERSION (0x00)
+#define MPI3_IOUNIT2_GPIO_FUNCTION_MASK (0xfffc)
+#define MPI3_IOUNIT2_GPIO_FUNCTION_SHIFT (2)
+#define MPI3_IOUNIT2_GPIO_SETTING_MASK (0x0001)
+#define MPI3_IOUNIT2_GPIO_SETTING_OFF (0x0000)
+#define MPI3_IOUNIT2_GPIO_SETTING_ON (0x0001)
+struct mpi3_io_unit3_sensor {
+ __le16 flags;
+ u8 threshold_margin;
+ u8 reserved03;
+ __le16 threshold[3];
+ __le16 reserved0a;
+ __le32 reserved0c;
+ __le32 reserved10;
+ __le32 reserved14;
+};
+
+#define MPI3_IOUNIT3_SENSOR_FLAGS_FATAL_EVENT_ENABLED (0x0010)
+#define MPI3_IOUNIT3_SENSOR_FLAGS_FATAL_ACTION_ENABLED (0x0008)
+#define MPI3_IOUNIT3_SENSOR_FLAGS_CRITICAL_EVENT_ENABLED (0x0004)
+#define MPI3_IOUNIT3_SENSOR_FLAGS_CRITICAL_ACTION_ENABLED (0x0002)
+#define MPI3_IOUNIT3_SENSOR_FLAGS_WARNING_EVENT_ENABLED (0x0001)
+#ifndef MPI3_IO_UNIT3_SENSOR_MAX
+#define MPI3_IO_UNIT3_SENSOR_MAX (1)
+#endif
+struct mpi3_io_unit_page3 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_sensors;
+ u8 nominal_poll_interval;
+ u8 warning_poll_interval;
+ u8 reserved0f;
+ struct mpi3_io_unit3_sensor sensor[MPI3_IO_UNIT3_SENSOR_MAX];
+};
+
+#define MPI3_IOUNIT3_PAGEVERSION (0x00)
+struct mpi3_io_unit4_sensor {
+ __le16 current_temperature;
+ __le16 reserved02;
+ u8 flags;
+ u8 reserved05[3];
+ __le16 istwi_index;
+ u8 channel;
+ u8 reserved0b;
+ __le32 reserved0c;
+};
+
+#define MPI3_IOUNIT4_SENSOR_FLAGS_LOC_MASK (0xe0)
+#define MPI3_IOUNIT4_SENSOR_FLAGS_LOC_SHIFT (5)
+#define MPI3_IOUNIT4_SENSOR_FLAGS_TEMP_VALID (0x01)
+#define MPI3_IOUNIT4_SENSOR_ISTWI_INDEX_INTERNAL (0xffff)
+#define MPI3_IOUNIT4_SENSOR_CHANNEL_RESERVED (0xff)
+#ifndef MPI3_IO_UNIT4_SENSOR_MAX
+#define MPI3_IO_UNIT4_SENSOR_MAX (1)
+#endif
+struct mpi3_io_unit_page4 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_sensors;
+ u8 reserved0d[3];
+ struct mpi3_io_unit4_sensor sensor[MPI3_IO_UNIT4_SENSOR_MAX];
+};
+
+#define MPI3_IOUNIT4_PAGEVERSION (0x00)
+struct mpi3_io_unit5_spinup_group {
+ u8 max_target_spinup;
+ u8 spinup_delay;
+ u8 spinup_flags;
+ u8 reserved03;
+};
+
+#define MPI3_IOUNIT5_SPINUP_FLAGS_DISABLE (0x01)
+#ifndef MPI3_IO_UNIT5_PHY_MAX
+#define MPI3_IO_UNIT5_PHY_MAX (4)
+#endif
+struct mpi3_io_unit_page5 {
+ struct mpi3_config_page_header header;
+ struct mpi3_io_unit5_spinup_group spinup_group_parameters[4];
+ __le32 reserved18;
+ __le32 reserved1c;
+ __le16 device_shutdown;
+ __le16 reserved22;
+ u8 pcie_device_wait_time;
+ u8 sata_device_wait_time;
+ u8 spinup_encl_drive_count;
+ u8 spinup_encl_delay;
+ u8 num_phys;
+ u8 pe_initial_spinup_delay;
+ u8 topology_stable_time;
+ u8 flags;
+ u8 phy[MPI3_IO_UNIT5_PHY_MAX];
+};
+
+#define MPI3_IOUNIT5_PAGEVERSION (0x00)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_NO_ACTION (0x00)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_DIRECT_ATTACHED (0x01)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_EXPANDER_ATTACHED (0x02)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SWITCH_ATTACHED (0x02)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_DIRECT_AND_EXPANDER (0x03)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_DIRECT_AND_SWITCH (0x03)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_HDD_MASK (0x0300)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_HDD_SHIFT (8)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SAS_HDD_MASK (0x00c0)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SAS_HDD_SHIFT (6)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_NVME_SSD_MASK (0x0030)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_NVME_SSD_SHIFT (4)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_SSD_MASK (0x000c)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SATA_SSD_SHIFT (2)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SAS_SSD_MASK (0x0003)
+#define MPI3_IOUNIT5_DEVICE_SHUTDOWN_SAS_SSD_SHIFT (0)
+#define MPI3_IOUNIT5_FLAGS_SATAPUIS_MASK (0x0c)
+#define MPI3_IOUNIT5_FLAGS_SATAPUIS_NOT_SUPPORTED (0x00)
+#define MPI3_IOUNIT5_FLAGS_SATAPUIS_OS_CONTROLLED (0x04)
+#define MPI3_IOUNIT5_FLAGS_SATAPUIS_APP_CONTROLLED (0x08)
+#define MPI3_IOUNIT5_FLAGS_SATAPUIS_BLOCKED (0x0c)
+#define MPI3_IOUNIT5_FLAGS_POWER_CAPABLE_SPINUP (0x02)
+#define MPI3_IOUNIT5_FLAGS_AUTO_PORT_ENABLE (0x01)
+#define MPI3_IOUNIT5_PHY_SPINUP_GROUP_MASK (0x03)
+struct mpi3_io_unit_page6 {
+ struct mpi3_config_page_header header;
+ __le32 board_power_requirement;
+ __le32 pci_slot_power_allocation;
+ u8 flags;
+ u8 reserved11[3];
+};
+
+#define MPI3_IOUNIT6_PAGEVERSION (0x00)
+#define MPI3_IOUNIT6_FLAGS_ACT_CABLE_PWR_EXC (0x01)
+#ifndef MPI3_IOUNIT8_DIGEST_MAX
+#define MPI3_IOUNIT8_DIGEST_MAX (1)
+#endif
+union mpi3_iounit8_digest {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+
+struct mpi3_io_unit_page8 {
+ struct mpi3_config_page_header header;
+ u8 sb_mode;
+ u8 sb_state;
+ __le16 reserved0a;
+ u8 num_slots;
+ u8 slots_available;
+ u8 current_key_encryption_algo;
+ u8 key_digest_hash_algo;
+ union mpi3_version_union current_svn;
+ __le32 reserved14;
+ __le32 current_key[128];
+ union mpi3_iounit8_digest digest[MPI3_IOUNIT8_DIGEST_MAX];
+};
+
+#define MPI3_IOUNIT8_PAGEVERSION (0x00)
+#define MPI3_IOUNIT8_SBMODE_SECURE_DEBUG (0x04)
+#define MPI3_IOUNIT8_SBMODE_HARD_SECURE (0x02)
+#define MPI3_IOUNIT8_SBMODE_CONFIG_SECURE (0x01)
+#define MPI3_IOUNIT8_SBSTATE_SVN_UPDATE_PENDING (0x04)
+#define MPI3_IOUNIT8_SBSTATE_KEY_UPDATE_PENDING (0x02)
+#define MPI3_IOUNIT8_SBSTATE_SECURE_BOOT_ENABLED (0x01)
+#define MPI3_IOUNIT8_SBMODE_CURRENT_KEY_IOUNIT17 (0x10)
+#define MPI3_IOUNIT8_SBMODE_HARD_SECURE_RECERTIFIED (0x08)
+struct mpi3_io_unit_page9 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ __le16 first_device;
+ __le16 reserved0e;
+};
+
+#define MPI3_IOUNIT9_PAGEVERSION (0x00)
+#define MPI3_IOUNIT9_FLAGS_UBM_ENCLOSURE_ORDER_MASK (0x00000006)
+#define MPI3_IOUNIT9_FLAGS_UBM_ENCLOSURE_ORDER_SHIFT (1)
+#define MPI3_IOUNIT9_FLAGS_UBM_ENCLOSURE_ORDER_NONE (0x00000000)
+#define MPI3_IOUNIT9_FLAGS_UBM_ENCLOSURE_ORDER_RECEPTACLE (0x00000002)
+#define MPI3_IOUNIT9_FLAGS_UBM_ENCLOSURE_ORDER_BACKPLANE_TYPE (0x00000004)
+#define MPI3_IOUNIT9_FLAGS_VDFIRST_ENABLED (0x00000001)
+#define MPI3_IOUNIT9_FIRSTDEVICE_UNKNOWN (0xffff)
+#define MPI3_IOUNIT9_FIRSTDEVICE_IN_DRIVER_PAGE_0 (0xfffe)
+
+struct mpi3_io_unit_page10 {
+ struct mpi3_config_page_header header;
+ u8 flags;
+ u8 reserved09[3];
+ __le32 silicon_id;
+ u8 fw_version_minor;
+ u8 fw_version_major;
+ u8 hw_version_minor;
+ u8 hw_version_major;
+ u8 part_number[16];
+};
+#define MPI3_IOUNIT10_PAGEVERSION (0x00)
+#define MPI3_IOUNIT10_FLAGS_VALID (0x01)
+#define MPI3_IOUNIT10_FLAGS_ACTIVEID_MASK (0x02)
+#define MPI3_IOUNIT10_FLAGS_ACTIVEID_FIRST_REGION (0x00)
+#define MPI3_IOUNIT10_FLAGS_ACTIVEID_SECOND_REGION (0x02)
+#define MPI3_IOUNIT10_FLAGS_PBLP_EXPECTED (0x80)
+#ifndef MPI3_IOUNIT11_PROFILE_MAX
+#define MPI3_IOUNIT11_PROFILE_MAX (1)
+#endif
+struct mpi3_iounit11_profile {
+ u8 profile_identifier;
+ u8 reserved01[3];
+ __le16 max_vds;
+ __le16 max_host_pds;
+ __le16 max_adv_host_pds;
+ __le16 max_raid_pds;
+ __le16 max_nvme;
+ __le16 max_outstanding_requests;
+ __le16 subsystem_id;
+ __le16 reserved12;
+ __le32 reserved14[2];
+};
+struct mpi3_io_unit_page11 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_profiles;
+ u8 current_profile_identifier;
+ __le16 reserved0e;
+ struct mpi3_iounit11_profile profile[MPI3_IOUNIT11_PROFILE_MAX];
+};
+#define MPI3_IOUNIT11_PAGEVERSION (0x00)
+#ifndef MPI3_IOUNIT12_BUCKET_MAX
+#define MPI3_IOUNIT12_BUCKET_MAX (1)
+#endif
+struct mpi3_iounit12_bucket {
+ u8 coalescing_depth;
+ u8 coalescing_timeout;
+ __le16 io_count_low_boundary;
+ __le32 reserved04;
+};
+struct mpi3_io_unit_page12 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ __le32 reserved0c[4];
+ u8 num_buckets;
+ u8 reserved1d[3];
+ struct mpi3_iounit12_bucket bucket[MPI3_IOUNIT12_BUCKET_MAX];
+};
+#define MPI3_IOUNIT12_PAGEVERSION (0x00)
+#define MPI3_IOUNIT12_FLAGS_NUMPASSES_MASK (0x00000300)
+#define MPI3_IOUNIT12_FLAGS_NUMPASSES_SHIFT (8)
+#define MPI3_IOUNIT12_FLAGS_NUMPASSES_8 (0x00000000)
+#define MPI3_IOUNIT12_FLAGS_NUMPASSES_16 (0x00000100)
+#define MPI3_IOUNIT12_FLAGS_NUMPASSES_32 (0x00000200)
+#define MPI3_IOUNIT12_FLAGS_NUMPASSES_64 (0x00000300)
+#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_MASK (0x00000003)
+#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_DISABLED (0x00000000)
+#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_500US (0x00000001)
+#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_1MS (0x00000002)
+#define MPI3_IOUNIT12_FLAGS_PASSPERIOD_2MS (0x00000003)
+#ifndef MPI3_IOUNIT13_FUNC_MAX
+#define MPI3_IOUNIT13_FUNC_MAX (1)
+#endif
+struct mpi3_iounit13_allowed_function {
+ __le16 sub_function;
+ u8 function_code;
+ u8 function_flags;
+};
+#define MPI3_IOUNIT13_FUNCTION_FLAGS_ADMIN_BLOCKED (0x04)
+#define MPI3_IOUNIT13_FUNCTION_FLAGS_OOB_BLOCKED (0x02)
+#define MPI3_IOUNIT13_FUNCTION_FLAGS_CHECK_SUBFUNCTION_ENABLED (0x01)
+struct mpi3_io_unit_page13 {
+ struct mpi3_config_page_header header;
+ __le16 flags;
+ __le16 reserved0a;
+ u8 num_allowed_functions;
+ u8 reserved0d[3];
+ struct mpi3_iounit13_allowed_function allowed_function[MPI3_IOUNIT13_FUNC_MAX];
+};
+#define MPI3_IOUNIT13_PAGEVERSION (0x00)
+#define MPI3_IOUNIT13_FLAGS_ADMIN_BLOCKED (0x0002)
+#define MPI3_IOUNIT13_FLAGS_OOB_BLOCKED (0x0001)
+#ifndef MPI3_IOUNIT14_MD_MAX
+#define MPI3_IOUNIT14_MD_MAX (1)
+#endif
+struct mpi3_iounit14_pagemetadata {
+ u8 page_type;
+ u8 page_number;
+ u8 reserved02;
+ u8 page_flags;
+};
+#define MPI3_IOUNIT14_PAGEMETADATA_PAGEFLAGS_OOBWRITE_ALLOWED (0x02)
+#define MPI3_IOUNIT14_PAGEMETADATA_PAGEFLAGS_HOSTWRITE_ALLOWED (0x01)
+struct mpi3_io_unit_page14 {
+ struct mpi3_config_page_header header;
+ u8 flags;
+ u8 reserved09[3];
+ u8 num_pages;
+ u8 reserved0d[3];
+ struct mpi3_iounit14_pagemetadata page_metadata[MPI3_IOUNIT14_MD_MAX];
+};
+#define MPI3_IOUNIT14_PAGEVERSION (0x00)
+#define MPI3_IOUNIT14_FLAGS_READONLY (0x01)
+#ifndef MPI3_IOUNIT15_PBD_MAX
+#define MPI3_IOUNIT15_PBD_MAX (1)
+#endif
+struct mpi3_io_unit_page15 {
+ struct mpi3_config_page_header header;
+ u8 flags;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ u8 power_budgeting_capability;
+ u8 reserved11[3];
+ u8 num_power_budget_data;
+ u8 reserved15[3];
+ __le32 power_budget_data[MPI3_IOUNIT15_PBD_MAX];
+};
+#define MPI3_IOUNIT15_PAGEVERSION (0x00)
+#define MPI3_IOUNIT15_FLAGS_EPRINIT_INITREQUIRED (0x04)
+#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_MASK (0x03)
+#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_NOT_SUPPORTED (0x00)
+#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_WITHOUT_POWER_BRAKE_GPIO (0x01)
+#define MPI3_IOUNIT15_FLAGS_EPRSUPPORT_WITH_POWER_BRAKE_GPIO (0x02)
+#define MPI3_IOUNIT15_NUMPOWERBUDGETDATA_POWER_BUDGETING_DISABLED (0x00)
+
+struct mpi3_io_unit_page17 {
+ struct mpi3_config_page_header header;
+ u8 num_instances;
+ u8 instance;
+ __le16 reserved0a;
+ __le32 reserved0c[4];
+ __le16 key_length;
+ u8 encryption_algorithm;
+ u8 reserved1f;
+ __le32 current_key[];
+};
+#define MPI3_IOUNIT17_PAGEVERSION (0x00)
+struct mpi3_io_unit_page18 {
+ struct mpi3_config_page_header header;
+ u8 flags;
+ u8 poll_interval;
+ __le16 reserved0a;
+ __le32 reserved0c;
+};
+
+#define MPI3_IOUNIT18_PAGEVERSION (0x00)
+#define MPI3_IOUNIT18_FLAGS_DIRECTATTACHED_ENABLE (0x01)
+#define MPI3_IOUNIT18_POLLINTERVAL_DISABLE (0x00)
+#ifndef MPI3_IOUNIT19_DEVICE_MAX
+#define MPI3_IOUNIT19_DEVICE_MAX (1)
+#endif
+struct mpi3_iounit19_device {
+ __le16 temperature;
+ __le16 dev_handle;
+ __le16 persistent_id;
+ __le16 reserved06;
+};
+
+#define MPI3_IOUNIT19_DEVICE_TEMPERATURE_UNAVAILABLE (0x8000)
+struct mpi3_io_unit_page19 {
+ struct mpi3_config_page_header header;
+ __le16 num_devices;
+ __le16 reserved0a;
+ __le32 reserved0c;
+ struct mpi3_iounit19_device device[MPI3_IOUNIT19_DEVICE_MAX];
+};
+
+#define MPI3_IOUNIT19_PAGEVERSION (0x00)
+struct mpi3_ioc_page0 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le16 vendor_id;
+ __le16 device_id;
+ u8 revision_id;
+ u8 reserved11[3];
+ __le32 class_code;
+ __le16 subsystem_vendor_id;
+ __le16 subsystem_id;
+};
+
+#define MPI3_IOC0_PAGEVERSION (0x00)
+struct mpi3_ioc_page1 {
+ struct mpi3_config_page_header header;
+ __le32 coalescing_timeout;
+ u8 coalescing_depth;
+ u8 obsolete;
+ __le16 reserved0e;
+};
+#define MPI3_IOC1_PAGEVERSION (0x00)
+#ifndef MPI3_IOC2_EVENTMASK_WORDS
+#define MPI3_IOC2_EVENTMASK_WORDS (4)
+#endif
+struct mpi3_ioc_page2 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le16 sas_broadcast_primitive_masks;
+ __le16 sas_notify_primitive_masks;
+ __le32 event_masks[MPI3_IOC2_EVENTMASK_WORDS];
+};
+
+#define MPI3_IOC2_PAGEVERSION (0x00)
+#define MPI3_DRIVER_FLAGS_ADMINRAIDPD_BLOCKED (0x0010)
+#define MPI3_DRIVER_FLAGS_OOBRAIDPD_BLOCKED (0x0008)
+#define MPI3_DRIVER_FLAGS_OOBRAIDVD_BLOCKED (0x0004)
+#define MPI3_DRIVER_FLAGS_OOBADVHOSTPD_BLOCKED (0x0002)
+#define MPI3_DRIVER_FLAGS_OOBHOSTPD_BLOCKED (0x0001)
+struct mpi3_allowed_cmd_scsi {
+ __le16 service_action;
+ u8 operation_code;
+ u8 command_flags;
+};
+
+struct mpi3_allowed_cmd_ata {
+ u8 subcommand;
+ u8 reserved01;
+ u8 command;
+ u8 command_flags;
+};
+
+struct mpi3_allowed_cmd_nvme {
+ u8 reserved00;
+ u8 nvme_cmd_flags;
+ u8 op_code;
+ u8 command_flags;
+};
+
+#define MPI3_DRIVER_ALLOWEDCMD_NVMECMDFLAGS_SUBQ_TYPE_MASK (0x80)
+#define MPI3_DRIVER_ALLOWEDCMD_NVMECMDFLAGS_SUBQ_TYPE_IO (0x00)
+#define MPI3_DRIVER_ALLOWEDCMD_NVMECMDFLAGS_SUBQ_TYPE_ADMIN (0x80)
+#define MPI3_DRIVER_ALLOWEDCMD_NVMECMDFLAGS_CMDSET_MASK (0x3f)
+#define MPI3_DRIVER_ALLOWEDCMD_NVMECMDFLAGS_CMDSET_NVM (0x00)
+union mpi3_allowed_cmd {
+ struct mpi3_allowed_cmd_scsi scsi;
+ struct mpi3_allowed_cmd_ata ata;
+ struct mpi3_allowed_cmd_nvme nvme;
+};
+
+#define MPI3_DRIVER_ALLOWEDCMD_CMDFLAGS_ADMINRAIDPD_BLOCKED (0x20)
+#define MPI3_DRIVER_ALLOWEDCMD_CMDFLAGS_OOBRAIDPD_BLOCKED (0x10)
+#define MPI3_DRIVER_ALLOWEDCMD_CMDFLAGS_OOBRAIDVD_BLOCKED (0x08)
+#define MPI3_DRIVER_ALLOWEDCMD_CMDFLAGS_OOBADVHOSTPD_BLOCKED (0x04)
+#define MPI3_DRIVER_ALLOWEDCMD_CMDFLAGS_OOBHOSTPD_BLOCKED (0x02)
+#define MPI3_DRIVER_ALLOWEDCMD_CMDFLAGS_CHECKSUBCMD_ENABLED (0x01)
+#ifndef MPI3_ALLOWED_CMDS_MAX
+#define MPI3_ALLOWED_CMDS_MAX (1)
+#endif
+struct mpi3_driver_page0 {
+ struct mpi3_config_page_header header;
+ __le32 bsd_options;
+ u8 ssu_timeout;
+ u8 io_timeout;
+ u8 tur_retries;
+ u8 tur_interval;
+ u8 reserved10;
+ u8 security_key_timeout;
+ __le16 first_device;
+ __le32 reserved14;
+ __le32 reserved18;
+};
+#define MPI3_DRIVER0_PAGEVERSION (0x00)
+#define MPI3_DRIVER0_BSDOPTS_DEVICEEXPOSURE_DISABLE (0x00000020)
+#define MPI3_DRIVER0_BSDOPTS_WRITECACHE_DISABLE (0x00000010)
+#define MPI3_DRIVER0_BSDOPTS_HEADLESS_MODE_ENABLE (0x00000008)
+#define MPI3_DRIVER0_BSDOPTS_DIS_HII_CONFIG_UTIL (0x00000004)
+#define MPI3_DRIVER0_BSDOPTS_REGISTRATION_MASK (0x00000003)
+#define MPI3_DRIVER0_BSDOPTS_REGISTRATION_IOC_AND_DEVS (0x00000000)
+#define MPI3_DRIVER0_BSDOPTS_REGISTRATION_IOC_ONLY (0x00000001)
+#define MPI3_DRIVER0_BSDOPTS_REGISTRATION_IOC_AND_INTERNAL_DEVS (0x00000002)
+#define MPI3_DRIVER0_FIRSTDEVICE_IGNORE1 (0x0000)
+#define MPI3_DRIVER0_FIRSTDEVICE_IGNORE2 (0xffff)
+struct mpi3_driver_page1 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ u8 time_stamp_update;
+ u8 reserved0d[3];
+ __le16 host_diag_trace_max_size;
+ __le16 host_diag_trace_min_size;
+ __le16 host_diag_trace_decrement_size;
+ __le16 reserved16;
+ __le16 host_diag_fw_max_size;
+ __le16 host_diag_fw_min_size;
+ __le16 host_diag_fw_decrement_size;
+ __le16 reserved1e;
+ __le16 host_diag_driver_max_size;
+ __le16 host_diag_driver_min_size;
+ __le16 host_diag_driver_decrement_size;
+ __le16 reserved26;
+};
+
+#define MPI3_DRIVER1_PAGEVERSION (0x00)
+#ifndef MPI3_DRIVER2_TRIGGER_MAX
+#define MPI3_DRIVER2_TRIGGER_MAX (1)
+#endif
+struct mpi3_driver2_trigger_event {
+ u8 type;
+ u8 flags;
+ u8 reserved02;
+ u8 event;
+ __le32 reserved04[3];
+};
+
+struct mpi3_driver2_trigger_scsi_sense {
+ u8 type;
+ u8 flags;
+ __le16 reserved02;
+ u8 ascq;
+ u8 asc;
+ u8 sense_key;
+ u8 reserved07;
+ __le32 reserved08[2];
+};
+
+#define MPI3_DRIVER2_TRIGGER_SCSI_SENSE_ASCQ_MATCH_ALL (0xff)
+#define MPI3_DRIVER2_TRIGGER_SCSI_SENSE_ASC_MATCH_ALL (0xff)
+#define MPI3_DRIVER2_TRIGGER_SCSI_SENSE_SENSE_KEY_MATCH_ALL (0xff)
+struct mpi3_driver2_trigger_reply {
+ u8 type;
+ u8 flags;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ __le32 ioc_log_info_mask;
+ __le32 reserved0c;
+};
+
+#define MPI3_DRIVER2_TRIGGER_REPLY_IOCSTATUS_MATCH_ALL (0xffff)
+union mpi3_driver2_trigger_element {
+ struct mpi3_driver2_trigger_event event;
+ struct mpi3_driver2_trigger_scsi_sense scsi_sense;
+ struct mpi3_driver2_trigger_reply reply;
+};
+
+#define MPI3_DRIVER2_TRIGGER_TYPE_EVENT (0x00)
+#define MPI3_DRIVER2_TRIGGER_TYPE_SCSI_SENSE (0x01)
+#define MPI3_DRIVER2_TRIGGER_TYPE_REPLY (0x02)
+#define MPI3_DRIVER2_TRIGGER_FLAGS_DIAG_TRACE_RELEASE (0x02)
+#define MPI3_DRIVER2_TRIGGER_FLAGS_DIAG_FW_RELEASE (0x01)
+struct mpi3_driver_page2 {
+ struct mpi3_config_page_header header;
+ __le64 global_trigger;
+ __le32 reserved10[3];
+ u8 num_triggers;
+ u8 reserved1d[3];
+ union mpi3_driver2_trigger_element trigger[MPI3_DRIVER2_TRIGGER_MAX];
+};
+
+#define MPI3_DRIVER2_PAGEVERSION (0x00)
+#define MPI3_DRIVER2_GLOBALTRIGGER_DIAG_TRACE_RELEASE (0x8000000000000000ULL)
+#define MPI3_DRIVER2_GLOBALTRIGGER_DIAG_FW_RELEASE (0x4000000000000000ULL)
+#define MPI3_DRIVER2_GLOBALTRIGGER_SNAPDUMP_ENABLED (0x2000000000000000ULL)
+#define MPI3_DRIVER2_GLOBALTRIGGER_POST_DIAG_TRACE_DISABLED (0x1000000000000000ULL)
+#define MPI3_DRIVER2_GLOBALTRIGGER_POST_DIAG_FW_DISABLED (0x0800000000000000ULL)
+#define MPI3_DRIVER2_GLOBALTRIGGER_DEVICE_REMOVAL_ENABLED (0x0000000000000004ULL)
+#define MPI3_DRIVER2_GLOBALTRIGGER_TASK_MANAGEMENT_ENABLED (0x0000000000000002ULL)
+struct mpi3_driver_page10 {
+ struct mpi3_config_page_header header;
+ __le16 flags;
+ __le16 reserved0a;
+ u8 num_allowed_commands;
+ u8 reserved0d[3];
+ union mpi3_allowed_cmd allowed_command[MPI3_ALLOWED_CMDS_MAX];
+};
+
+#define MPI3_DRIVER10_PAGEVERSION (0x00)
+struct mpi3_driver_page20 {
+ struct mpi3_config_page_header header;
+ __le16 flags;
+ __le16 reserved0a;
+ u8 num_allowed_commands;
+ u8 reserved0d[3];
+ union mpi3_allowed_cmd allowed_command[MPI3_ALLOWED_CMDS_MAX];
+};
+
+#define MPI3_DRIVER20_PAGEVERSION (0x00)
+struct mpi3_driver_page30 {
+ struct mpi3_config_page_header header;
+ __le16 flags;
+ __le16 reserved0a;
+ u8 num_allowed_commands;
+ u8 reserved0d[3];
+ union mpi3_allowed_cmd allowed_command[MPI3_ALLOWED_CMDS_MAX];
+};
+
+#define MPI3_DRIVER30_PAGEVERSION (0x00)
+union mpi3_security_mac {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+
+union mpi3_security_nonce {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+
+union mpi3_security_root_digest {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+
+union mpi3_security0_cert_chain {
+ __le32 dword[1024];
+ __le16 word[2048];
+ u8 byte[4096];
+};
+
+struct mpi3_security_page0 {
+ struct mpi3_config_page_header header;
+ u8 slot_num_group;
+ u8 slot_num;
+ __le16 cert_chain_length;
+ u8 cert_chain_flags;
+ u8 reserved0d[3];
+ __le32 base_asym_algo;
+ __le32 base_hash_algo;
+ __le32 reserved18[4];
+ union mpi3_security_mac mac;
+ union mpi3_security_nonce nonce;
+ union mpi3_security0_cert_chain certificate_chain;
+};
+
+#define MPI3_SECURITY0_PAGEVERSION (0x00)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_MASK (0x0e)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_UNUSED (0x00)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_CERBERUS (0x02)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_SPDM (0x04)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_SEALED (0x01)
+#ifndef MPI3_SECURITY1_KEY_RECORD_MAX
+#define MPI3_SECURITY1_KEY_RECORD_MAX 1
+#endif
+#ifndef MPI3_SECURITY1_PAD_MAX
+#define MPI3_SECURITY1_PAD_MAX 4
+#endif
+union mpi3_security1_key_data {
+ __le32 dword[128];
+ __le16 word[256];
+ u8 byte[512];
+};
+
+struct mpi3_security1_key_record {
+ u8 flags;
+ u8 consumer;
+ __le16 key_data_size;
+ __le32 additional_key_data;
+ __le32 reserved08[2];
+ union mpi3_security1_key_data key_data;
+};
+
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_MASK (0x1f)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_NOT_VALID (0x00)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_HMAC (0x01)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_AES (0x02)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_ECDSA_PRIVATE (0x03)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_ECDSA_PUBLIC (0x04)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_NOT_VALID (0x00)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_SAFESTORE (0x01)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_CERT_CHAIN (0x02)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_DEVICE_KEY (0x03)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_CACHE_OFFLOAD (0x04)
+struct mpi3_security_page1 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08[2];
+ union mpi3_security_mac mac;
+ union mpi3_security_nonce nonce;
+ u8 num_keys;
+ u8 reserved91[3];
+ __le32 reserved94[3];
+ struct mpi3_security1_key_record key_record[MPI3_SECURITY1_KEY_RECORD_MAX];
+ u8 pad[MPI3_SECURITY1_PAD_MAX];
+};
+
+#define MPI3_SECURITY1_PAGEVERSION (0x00)
+#ifndef MPI3_SECURITY2_TRUSTED_ROOT_MAX
+#define MPI3_SECURITY2_TRUSTED_ROOT_MAX 1
+#endif
+struct mpi3_security2_trusted_root {
+ u8 level;
+ u8 hash_algorithm;
+ __le16 trusted_root_flags;
+ __le32 reserved04[3];
+ union mpi3_security_root_digest root_digest;
+};
+#define MPI3_SECURITY2_TRUSTEDROOT_TRUSTEDROOTFLAGS_HASHALGOSOURCE_MASK (0x0006)
+#define MPI3_SECURITY2_TRUSTEDROOT_TRUSTEDROOTFLAGS_HASHALGOSOURCE_SHIFT (1)
+#define MPI3_SECURITY2_TRUSTEDROOT_TRUSTEDROOTFLAGS_HASHALGOSOURCE_HA_FIELD (0x0000)
+#define MPI3_SECURITY2_TRUSTEDROOT_TRUSTEDROOTFLAGS_HASHALGOSOURCE_AKI (0x0002)
+#define MPI3_SECURITY2_TRUSTEDROOT_TRUSTEDROOTFLAGS_USERPROVISIONED_YES (0x0001)
+struct mpi3_security_page2 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08[2];
+ union mpi3_security_mac mac;
+ union mpi3_security_nonce nonce;
+ __le32 reserved90[3];
+ u8 num_roots;
+ u8 reserved9d[3];
+ struct mpi3_security2_trusted_root trusted_root[MPI3_SECURITY2_TRUSTED_ROOT_MAX];
+};
+#define MPI3_SECURITY2_PAGEVERSION (0x00)
+struct mpi3_sas_io_unit0_phy_data {
+ u8 io_unit_port;
+ u8 port_flags;
+ u8 phy_flags;
+ u8 negotiated_link_rate;
+ __le16 controller_phy_device_info;
+ __le16 reserved06;
+ __le16 attached_dev_handle;
+ __le16 controller_dev_handle;
+ __le32 discovery_status;
+ __le32 reserved10;
+};
+
+struct mpi3_sas_io_unit_page0 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phys;
+ u8 init_status;
+ __le16 reserved0e;
+ struct mpi3_sas_io_unit0_phy_data phy_data[];
+};
+
+#define MPI3_SASIOUNIT0_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT0_INITSTATUS_NO_ERRORS (0x00)
+#define MPI3_SASIOUNIT0_INITSTATUS_NEEDS_INITIALIZATION (0x01)
+#define MPI3_SASIOUNIT0_INITSTATUS_NO_TARGETS_ALLOCATED (0x02)
+#define MPI3_SASIOUNIT0_INITSTATUS_BAD_NUM_PHYS (0x04)
+#define MPI3_SASIOUNIT0_INITSTATUS_UNSUPPORTED_CONFIG (0x05)
+#define MPI3_SASIOUNIT0_INITSTATUS_HOST_PHYS_ENABLED (0x06)
+#define MPI3_SASIOUNIT0_INITSTATUS_PRODUCT_SPECIFIC_MIN (0xf0)
+#define MPI3_SASIOUNIT0_INITSTATUS_PRODUCT_SPECIFIC_MAX (0xff)
+#define MPI3_SASIOUNIT0_PORTFLAGS_DISC_IN_PROGRESS (0x08)
+#define MPI3_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG_MASK (0x03)
+#define MPI3_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG_IOUNIT1 (0x00)
+#define MPI3_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG_DYNAMIC (0x01)
+#define MPI3_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG_BACKPLANE (0x02)
+#define MPI3_SASIOUNIT0_PHYFLAGS_INIT_PERSIST_CONNECT (0x40)
+#define MPI3_SASIOUNIT0_PHYFLAGS_TARG_PERSIST_CONNECT (0x20)
+#define MPI3_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+#define MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY (0x02)
+#define MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY (0x01)
+struct mpi3_sas_io_unit1_phy_data {
+ u8 io_unit_port;
+ u8 port_flags;
+ u8 phy_flags;
+ u8 max_min_link_rate;
+ __le16 controller_phy_device_info;
+ __le16 max_target_port_connect_time;
+ __le32 reserved08;
+};
+
+struct mpi3_sas_io_unit_page1 {
+ struct mpi3_config_page_header header;
+ __le16 control_flags;
+ __le16 sas_narrow_max_queue_depth;
+ __le16 additional_control_flags;
+ __le16 sas_wide_max_queue_depth;
+ u8 num_phys;
+ u8 sata_max_q_depth;
+ __le16 reserved12;
+ struct mpi3_sas_io_unit1_phy_data phy_data[];
+};
+
+#define MPI3_SASIOUNIT1_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT1_CONTROL_CONTROLLER_DEVICE_SELF_TEST (0x8000)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010)
+#define MPI3_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008)
+#define MPI3_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004)
+#define MPI3_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002)
+#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_MASK (0x0001)
+#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_DEVICE_NAME (0x0000)
+#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_SAS_ADDRESS (0x0001)
+#define MPI3_SASIOUNIT1_ACONTROL_DA_PERSIST_CONNECT (0x0100)
+#define MPI3_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080)
+#define MPI3_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040)
+#define MPI3_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020)
+#define MPI3_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010)
+#define MPI3_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008)
+#define MPI3_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004)
+#define MPI3_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002)
+#define MPI3_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001)
+#define MPI3_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
+#define MPI3_SASIOUNIT1_PHYFLAGS_INIT_PERSIST_CONNECT (0x40)
+#define MPI3_SASIOUNIT1_PHYFLAGS_TARG_PERSIST_CONNECT (0x20)
+#define MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_MASK (0xf0)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_SHIFT (4)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_6_0 (0xa0)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_12_0 (0xb0)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_22_5 (0xc0)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_MASK (0x0f)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_6_0 (0x0a)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_12_0 (0x0b)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_22_5 (0x0c)
+struct mpi3_sas_io_unit2_phy_pm_settings {
+ u8 control_flags;
+ u8 reserved01;
+ __le16 inactivity_timer_exponent;
+ u8 sata_partial_timeout;
+ u8 reserved05;
+ u8 sata_slumber_timeout;
+ u8 reserved07;
+ u8 sas_partial_timeout;
+ u8 reserved09;
+ u8 sas_slumber_timeout;
+ u8 reserved0b;
+};
+
+#ifndef MPI3_SAS_IO_UNIT2_PHY_MAX
+#define MPI3_SAS_IO_UNIT2_PHY_MAX (1)
+#endif
+struct mpi3_sas_io_unit_page2 {
+ struct mpi3_config_page_header header;
+ u8 num_phys;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_sas_io_unit2_phy_pm_settings sas_phy_power_management_settings[MPI3_SAS_IO_UNIT2_PHY_MAX];
+};
+
+#define MPI3_SASIOUNIT2_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT2_CONTROL_SAS_SLUMBER_ENABLE (0x08)
+#define MPI3_SASIOUNIT2_CONTROL_SAS_PARTIAL_ENABLE (0x04)
+#define MPI3_SASIOUNIT2_CONTROL_SATA_SLUMBER_ENABLE (0x02)
+#define MPI3_SASIOUNIT2_CONTROL_SATA_PARTIAL_ENABLE (0x01)
+#define MPI3_SASIOUNIT2_ITE_SAS_SLUMBER_MASK (0x7000)
+#define MPI3_SASIOUNIT2_ITE_SAS_SLUMBER_SHIFT (12)
+#define MPI3_SASIOUNIT2_ITE_SAS_PARTIAL_MASK (0x0700)
+#define MPI3_SASIOUNIT2_ITE_SAS_PARTIAL_SHIFT (8)
+#define MPI3_SASIOUNIT2_ITE_SATA_SLUMBER_MASK (0x0070)
+#define MPI3_SASIOUNIT2_ITE_SATA_SLUMBER_SHIFT (4)
+#define MPI3_SASIOUNIT2_ITE_SATA_PARTIAL_MASK (0x0007)
+#define MPI3_SASIOUNIT2_ITE_SATA_PARTIAL_SHIFT (0)
+#define MPI3_SASIOUNIT2_ITE_EXP_TEN_SECONDS (7)
+#define MPI3_SASIOUNIT2_ITE_EXP_ONE_SECOND (6)
+#define MPI3_SASIOUNIT2_ITE_EXP_HUNDRED_MILLISECONDS (5)
+#define MPI3_SASIOUNIT2_ITE_EXP_TEN_MILLISECONDS (4)
+#define MPI3_SASIOUNIT2_ITE_EXP_ONE_MILLISECOND (3)
+#define MPI3_SASIOUNIT2_ITE_EXP_HUNDRED_MICROSECONDS (2)
+#define MPI3_SASIOUNIT2_ITE_EXP_TEN_MICROSECONDS (1)
+#define MPI3_SASIOUNIT2_ITE_EXP_ONE_MICROSECOND (0)
+struct mpi3_sas_io_unit_page3 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 power_management_capabilities;
+};
+
+#define MPI3_SASIOUNIT3_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT3_PM_HOST_SAS_SLUMBER_MODE (0x00000800)
+#define MPI3_SASIOUNIT3_PM_HOST_SAS_PARTIAL_MODE (0x00000400)
+#define MPI3_SASIOUNIT3_PM_HOST_SATA_SLUMBER_MODE (0x00000200)
+#define MPI3_SASIOUNIT3_PM_HOST_SATA_PARTIAL_MODE (0x00000100)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001)
+struct mpi3_sas_expander_page0 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 report_gen_length;
+ __le16 enclosure_handle;
+ __le32 reserved0c;
+ __le64 sas_address;
+ __le32 discovery_status;
+ __le16 dev_handle;
+ __le16 parent_dev_handle;
+ __le16 expander_change_count;
+ __le16 expander_route_indexes;
+ u8 num_phys;
+ u8 sas_level;
+ __le16 flags;
+ __le16 stp_bus_inactivity_time_limit;
+ __le16 stp_max_connect_time_limit;
+ __le16 stp_smp_nexus_loss_time;
+ __le16 max_num_routed_sas_addresses;
+ __le64 active_zone_manager_sas_address;
+ __le16 zone_lock_inactivity_limit;
+ __le16 reserved3a;
+ u8 time_to_reduced_func;
+ u8 initial_time_to_reduced_func;
+ u8 max_reduced_func_time;
+ u8 exp_status;
+};
+
+#define MPI3_SASEXPANDER0_PAGEVERSION (0x00)
+#define MPI3_SASEXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000)
+#define MPI3_SASEXPANDER0_FLAGS_ZONE_LOCKED (0x1000)
+#define MPI3_SASEXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800)
+#define MPI3_SASEXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400)
+#define MPI3_SASEXPANDER0_FLAGS_ZONING_SUPPORT (0x0200)
+#define MPI3_SASEXPANDER0_FLAGS_ENABLED_ZONING (0x0100)
+#define MPI3_SASEXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080)
+#define MPI3_SASEXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010)
+#define MPI3_SASEXPANDER0_FLAGS_OTHERS_CONFIG (0x0004)
+#define MPI3_SASEXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002)
+#define MPI3_SASEXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001)
+#define MPI3_SASEXPANDER0_ES_NOT_RESPONDING (0x02)
+#define MPI3_SASEXPANDER0_ES_RESPONDING (0x03)
+#define MPI3_SASEXPANDER0_ES_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_sas_expander_page1 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 reserved09[3];
+ u8 num_phys;
+ u8 phy;
+ __le16 num_table_entries_programmed;
+ u8 programmed_link_rate;
+ u8 hw_link_rate;
+ __le16 attached_dev_handle;
+ __le32 phy_info;
+ __le16 attached_device_info;
+ __le16 reserved1a;
+ __le16 expander_dev_handle;
+ u8 change_count;
+ u8 negotiated_link_rate;
+ u8 phy_identifier;
+ u8 attached_phy_identifier;
+ u8 reserved22;
+ u8 discovery_info;
+ __le32 attached_phy_info;
+ u8 zone_group;
+ u8 self_config_status;
+ __le16 reserved2a;
+ __le16 slot;
+ __le16 slot_index;
+};
+
+#define MPI3_SASEXPANDER1_PAGEVERSION (0x00)
+#define MPI3_SASEXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04)
+#define MPI3_SASEXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02)
+#define MPI3_SASEXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01)
+#ifndef MPI3_SASEXPANDER2_MAX_NUM_PHYS
+#define MPI3_SASEXPANDER2_MAX_NUM_PHYS (1)
+#endif
+struct mpi3_sasexpander2_phy_element {
+ u8 link_change_count;
+ u8 reserved01;
+ __le16 rate_change_count;
+ __le32 reserved04;
+};
+
+struct mpi3_sas_expander_page2 {
+ struct mpi3_config_page_header header;
+ u8 num_phys;
+ u8 reserved09;
+ __le16 dev_handle;
+ __le32 reserved0c;
+ struct mpi3_sasexpander2_phy_element phy[MPI3_SASEXPANDER2_MAX_NUM_PHYS];
+};
+
+#define MPI3_SASEXPANDER2_PAGEVERSION (0x00)
+struct mpi3_sas_port_page0 {
+ struct mpi3_config_page_header header;
+ u8 port_number;
+ u8 reserved09;
+ u8 port_width;
+ u8 reserved0b;
+ u8 zone_group;
+ u8 reserved0d[3];
+ __le64 sas_address;
+ __le16 device_info;
+ __le16 reserved1a;
+ __le32 reserved1c;
+};
+
+#define MPI3_SASPORT0_PAGEVERSION (0x00)
+struct mpi3_sas_phy_page0 {
+ struct mpi3_config_page_header header;
+ __le16 owner_dev_handle;
+ __le16 reserved0a;
+ __le16 attached_dev_handle;
+ u8 attached_phy_identifier;
+ u8 reserved0f;
+ __le32 attached_phy_info;
+ u8 programmed_link_rate;
+ u8 hw_link_rate;
+ u8 change_count;
+ u8 flags;
+ __le32 phy_info;
+ u8 negotiated_link_rate;
+ u8 reserved1d[3];
+ __le16 slot;
+ __le16 slot_index;
+};
+
+#define MPI3_SASPHY0_PAGEVERSION (0x00)
+#define MPI3_SASPHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01)
+struct mpi3_sas_phy_page1 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 invalid_dword_count;
+ __le32 running_disparity_error_count;
+ __le32 loss_dword_synch_count;
+ __le32 phy_reset_problem_count;
+};
+
+#define MPI3_SASPHY1_PAGEVERSION (0x00)
+struct mpi3_sas_phy2_phy_event {
+ u8 phy_event_code;
+ u8 reserved01[3];
+ __le32 phy_event_info;
+};
+
+#ifndef MPI3_SAS_PHY2_PHY_EVENT_MAX
+#define MPI3_SAS_PHY2_PHY_EVENT_MAX (1)
+#endif
+struct mpi3_sas_phy_page2 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phy_events;
+ u8 reserved0d[3];
+ struct mpi3_sas_phy2_phy_event phy_event[MPI3_SAS_PHY2_PHY_EVENT_MAX];
+};
+
+#define MPI3_SASPHY2_PAGEVERSION (0x00)
+struct mpi3_sas_phy3_phy_event_config {
+ u8 phy_event_code;
+ u8 reserved01[3];
+ u8 counter_type;
+ u8 threshold_window;
+ u8 time_units;
+ u8 reserved07;
+ __le32 event_threshold;
+ __le16 threshold_flags;
+ __le16 reserved0e;
+};
+
+#define MPI3_SASPHY3_EVENT_CODE_NO_EVENT (0x00)
+#define MPI3_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01)
+#define MPI3_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02)
+#define MPI3_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03)
+#define MPI3_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04)
+#define MPI3_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05)
+#define MPI3_SASPHY3_EVENT_CODE_RX_ERROR (0x06)
+#define MPI3_SASPHY3_EVENT_CODE_INV_SPL_PACKETS (0x07)
+#define MPI3_SASPHY3_EVENT_CODE_LOSS_SPL_PACKET_SYNC (0x08)
+#define MPI3_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20)
+#define MPI3_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22)
+#define MPI3_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23)
+#define MPI3_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26)
+#define MPI3_SASPHY3_EVENT_CODE_TX_BREAK (0x27)
+#define MPI3_SASPHY3_EVENT_CODE_RX_BREAK (0x28)
+#define MPI3_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29)
+#define MPI3_SASPHY3_EVENT_CODE_CONNECTION (0x2a)
+#define MPI3_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2b)
+#define MPI3_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2c)
+#define MPI3_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2d)
+#define MPI3_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2e)
+#define MPI3_SASPHY3_EVENT_CODE_PERSIST_CONN (0x2f)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43)
+#define MPI3_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44)
+#define MPI3_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51)
+#define MPI3_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63)
+#define MPI3_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xd0)
+#define MPI3_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xd1)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AIP (0xd2)
+#define MPI3_SASPHY3_EVENT_CODE_LCARB_WAIT_TIME (0xd3)
+#define MPI3_SASPHY3_EVENT_CODE_RCVD_CONN_RESP_WAIT_TIME (0xd4)
+#define MPI3_SASPHY3_EVENT_CODE_LCCONN_TIME (0xd5)
+#define MPI3_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xd6)
+#define MPI3_SASPHY3_EVENT_CODE_SATA_TX_START (0xd7)
+#define MPI3_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xd8)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xd9)
+#define MPI3_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xda)
+#define MPI3_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xdb)
+#define MPI3_SASPHY3_EVENT_CODE_SMP_RX_START_RECEIVE (0xdc)
+#define MPI3_SASPHY3_COUNTER_TYPE_WRAPPING (0x00)
+#define MPI3_SASPHY3_COUNTER_TYPE_SATURATING (0x01)
+#define MPI3_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02)
+#define MPI3_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00)
+#define MPI3_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01)
+#define MPI3_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02)
+#define MPI3_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03)
+#define MPI3_SASPHY3_TFLAGS_PHY_RESET (0x0002)
+#define MPI3_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001)
+#ifndef MPI3_SAS_PHY3_PHY_EVENT_MAX
+#define MPI3_SAS_PHY3_PHY_EVENT_MAX (1)
+#endif
+struct mpi3_sas_phy_page3 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phy_events;
+ u8 reserved0d[3];
+ struct mpi3_sas_phy3_phy_event_config phy_event_config[MPI3_SAS_PHY3_PHY_EVENT_MAX];
+};
+
+#define MPI3_SASPHY3_PAGEVERSION (0x00)
+struct mpi3_sas_phy_page4 {
+ struct mpi3_config_page_header header;
+ u8 reserved08[3];
+ u8 flags;
+ u8 initial_frame[28];
+};
+
+#define MPI3_SASPHY4_PAGEVERSION (0x00)
+#define MPI3_SASPHY4_FLAGS_FRAME_VALID (0x02)
+#define MPI3_SASPHY4_FLAGS_SATA_FRAME (0x01)
+#define MPI3_PCIE_LINK_RETIMERS_MASK (0x30)
+#define MPI3_PCIE_LINK_RETIMERS_SHIFT (4)
+#define MPI3_PCIE_NEG_LINK_RATE_MASK (0x0f)
+#define MPI3_PCIE_NEG_LINK_RATE_UNKNOWN (0x00)
+#define MPI3_PCIE_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI3_PCIE_NEG_LINK_RATE_2_5 (0x02)
+#define MPI3_PCIE_NEG_LINK_RATE_5_0 (0x03)
+#define MPI3_PCIE_NEG_LINK_RATE_8_0 (0x04)
+#define MPI3_PCIE_NEG_LINK_RATE_16_0 (0x05)
+#define MPI3_PCIE_NEG_LINK_RATE_32_0 (0x06)
+#define MPI3_PCIE_ASPM_ENABLE_NONE (0x0)
+#define MPI3_PCIE_ASPM_ENABLE_L0S (0x1)
+#define MPI3_PCIE_ASPM_ENABLE_L1 (0x2)
+#define MPI3_PCIE_ASPM_ENABLE_L0S_L1 (0x3)
+#define MPI3_PCIE_ASPM_SUPPORT_NONE (0x0)
+#define MPI3_PCIE_ASPM_SUPPORT_L0S (0x1)
+#define MPI3_PCIE_ASPM_SUPPORT_L1 (0x2)
+#define MPI3_PCIE_ASPM_SUPPORT_L0S_L1 (0x3)
+struct mpi3_pcie_io_unit0_phy_data {
+ u8 link;
+ u8 link_flags;
+ u8 phy_flags;
+ u8 negotiated_link_rate;
+ __le16 attached_dev_handle;
+ __le16 controller_dev_handle;
+ __le32 enumeration_status;
+ u8 io_unit_port;
+ u8 reserved0d[3];
+};
+
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_MASK (0x10)
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_IOUNIT1 (0x00)
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_BKPLANE (0x10)
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_ENUM_IN_PROGRESS (0x08)
+#define MPI3_PCIEIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+#define MPI3_PCIEIOUNIT0_PHYFLAGS_HOST_PHY (0x01)
+#define MPI3_PCIEIOUNIT0_ES_MAX_SWITCH_DEPTH_EXCEEDED (0x80000000)
+#define MPI3_PCIEIOUNIT0_ES_MAX_SWITCHES_EXCEEDED (0x40000000)
+#define MPI3_PCIEIOUNIT0_ES_MAX_ENDPOINTS_EXCEEDED (0x20000000)
+#define MPI3_PCIEIOUNIT0_ES_INSUFFICIENT_RESOURCES (0x10000000)
+#ifndef MPI3_PCIE_IO_UNIT0_PHY_MAX
+#define MPI3_PCIE_IO_UNIT0_PHY_MAX (1)
+#endif
+struct mpi3_pcie_io_unit_page0 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phys;
+ u8 init_status;
+ u8 aspm;
+ u8 reserved0f;
+ struct mpi3_pcie_io_unit0_phy_data phy_data[MPI3_PCIE_IO_UNIT0_PHY_MAX];
+};
+
+#define MPI3_PCIEIOUNIT0_PAGEVERSION (0x00)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_NO_ERRORS (0x00)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_NEEDS_INITIALIZATION (0x01)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_NO_TARGETS_ALLOCATED (0x02)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_RESOURCE_ALLOC_FAILED (0x03)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_BAD_NUM_PHYS (0x04)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_UNSUPPORTED_CONFIG (0x05)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_HOST_PORT_MISMATCH (0x06)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_PHYS_NOT_CONSECUTIVE (0x07)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_BAD_CLOCKING_MODE (0x08)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_PROD_SPEC_START (0xf0)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_PROD_SPEC_END (0xff)
+#define MPI3_PCIEIOUNIT0_ASPM_SWITCH_STATES_MASK (0xc0)
+#define MPI3_PCIEIOUNIT0_ASPM_SWITCH_STATES_SHIFT (6)
+#define MPI3_PCIEIOUNIT0_ASPM_DIRECT_STATES_MASK (0x30)
+#define MPI3_PCIEIOUNIT0_ASPM_DIRECT_STATES_SHIFT (4)
+#define MPI3_PCIEIOUNIT0_ASPM_SWITCH_SUPPORT_MASK (0x0c)
+#define MPI3_PCIEIOUNIT0_ASPM_SWITCH_SUPPORT_SHIFT (2)
+#define MPI3_PCIEIOUNIT0_ASPM_DIRECT_SUPPORT_MASK (0x03)
+#define MPI3_PCIEIOUNIT0_ASPM_DIRECT_SUPPORT_SHIFT (0)
+struct mpi3_pcie_io_unit1_phy_data {
+ u8 link;
+ u8 link_flags;
+ u8 phy_flags;
+ u8 max_min_link_rate;
+ __le32 reserved04;
+ __le32 reserved08;
+};
+
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_MASK (0x03)
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_DIS_SEPARATE_REFCLK (0x00)
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_EN_SRIS (0x01)
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_EN_SRNS (0x02)
+#define MPI3_PCIEIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_MASK (0xf0)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_SHIFT (4)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_2_5 (0x20)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_5_0 (0x30)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_8_0 (0x40)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_16_0 (0x50)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_32_0 (0x60)
+#ifndef MPI3_PCIE_IO_UNIT1_PHY_MAX
+#define MPI3_PCIE_IO_UNIT1_PHY_MAX (1)
+#endif
+struct mpi3_pcie_io_unit_page1 {
+ struct mpi3_config_page_header header;
+ __le32 control_flags;
+ __le32 reserved0c;
+ u8 num_phys;
+ u8 reserved11;
+ u8 aspm;
+ u8 reserved13;
+ struct mpi3_pcie_io_unit1_phy_data phy_data[MPI3_PCIE_IO_UNIT1_PHY_MAX];
+};
+
+#define MPI3_PCIEIOUNIT1_PAGEVERSION (0x00)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_PERST_OVERRIDE_MASK (0xe0000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_PERST_OVERRIDE_NONE (0x00000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_PERST_OVERRIDE_DEASSERT (0x20000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_PERST_OVERRIDE_ASSERT (0x40000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_PERST_OVERRIDE_BACKPLANE_ERROR (0x60000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_REFCLK_OVERRIDE_MASK (0x1c000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_REFCLK_OVERRIDE_NONE (0x00000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_REFCLK_OVERRIDE_DEASSERT (0x04000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_REFCLK_OVERRIDE_ASSERT (0x08000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_REFCLK_OVERRIDE_BACKPLANE_ERROR (0x0c000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_OVERRIDE_DISABLE (0x00000080)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_CLOCK_OVERRIDE_DISABLE (0x00000040)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_CLOCK_OVERRIDE_MODE_MASK (0x00000030)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_CLOCK_OVERRIDE_MODE_SHIFT (4)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_CLOCK_OVERRIDE_MODE_SRIS_SRNS_DISABLED (0x00000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_CLOCK_OVERRIDE_MODE_SRIS_ENABLED (0x00000010)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_CLOCK_OVERRIDE_MODE_SRNS_ENABLED (0x00000020)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_RATE_OVERRIDE_MASK (0x0000000f)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_RATE_OVERRIDE_USE_BACKPLANE (0x00000000)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_RATE_OVERRIDE_MAX_2_5 (0x00000002)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_RATE_OVERRIDE_MAX_5_0 (0x00000003)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_RATE_OVERRIDE_MAX_8_0 (0x00000004)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_RATE_OVERRIDE_MAX_16_0 (0x00000005)
+#define MPI3_PCIEIOUNIT1_CONTROL_FLAGS_LINK_RATE_OVERRIDE_MAX_32_0 (0x00000006)
+#define MPI3_PCIEIOUNIT1_ASPM_SWITCH_MASK (0x0c)
+#define MPI3_PCIEIOUNIT1_ASPM_SWITCH_SHIFT (2)
+#define MPI3_PCIEIOUNIT1_ASPM_DIRECT_MASK (0x03)
+#define MPI3_PCIEIOUNIT1_ASPM_DIRECT_SHIFT (0)
+struct mpi3_pcie_io_unit_page2 {
+ struct mpi3_config_page_header header;
+ __le16 nvme_max_q_dx1;
+ __le16 nvme_max_q_dx2;
+ u8 nvme_abort_to;
+ u8 reserved0d;
+ __le16 nvme_max_q_dx4;
+};
+
+#define MPI3_PCIEIOUNIT2_PAGEVERSION (0x00)
+#define MPI3_PCIEIOUNIT3_ERROR_RECEIVER_ERROR (0)
+#define MPI3_PCIEIOUNIT3_ERROR_RECOVERY (1)
+#define MPI3_PCIEIOUNIT3_ERROR_CORRECTABLE_ERROR_MSG (2)
+#define MPI3_PCIEIOUNIT3_ERROR_BAD_DLLP (3)
+#define MPI3_PCIEIOUNIT3_ERROR_BAD_TLP (4)
+#define MPI3_PCIEIOUNIT3_NUM_ERROR_INDEX (5)
+struct mpi3_pcie_io_unit3_error {
+ __le16 threshold_count;
+ __le16 reserved02;
+};
+
+struct mpi3_pcie_io_unit_page3 {
+ struct mpi3_config_page_header header;
+ u8 threshold_window;
+ u8 threshold_action;
+ u8 escalation_count;
+ u8 escalation_action;
+ u8 num_errors;
+ u8 reserved0d[3];
+ struct mpi3_pcie_io_unit3_error error[MPI3_PCIEIOUNIT3_NUM_ERROR_INDEX];
+};
+
+#define MPI3_PCIEIOUNIT3_PAGEVERSION (0x00)
+#define MPI3_PCIEIOUNIT3_ACTION_NO_ACTION (0x00)
+#define MPI3_PCIEIOUNIT3_ACTION_HOT_RESET (0x01)
+#define MPI3_PCIEIOUNIT3_ACTION_REDUCE_LINK_RATE_ONLY (0x02)
+#define MPI3_PCIEIOUNIT3_ACTION_REDUCE_LINK_RATE_NO_ACCESS (0x03)
+struct mpi3_pcie_switch_page0 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 switch_status;
+ u8 reserved0a[2];
+ __le16 dev_handle;
+ __le16 parent_dev_handle;
+ u8 num_ports;
+ u8 pcie_level;
+ __le16 reserved12;
+ __le32 reserved14;
+ __le32 reserved18;
+ __le32 reserved1c;
+};
+
+#define MPI3_PCIESWITCH0_PAGEVERSION (0x00)
+#define MPI3_PCIESWITCH0_SS_NOT_RESPONDING (0x02)
+#define MPI3_PCIESWITCH0_SS_RESPONDING (0x03)
+#define MPI3_PCIESWITCH0_SS_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_pcie_switch_page1 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 flags;
+ __le16 reserved0a;
+ u8 num_ports;
+ u8 port_num;
+ __le16 attached_dev_handle;
+ __le16 switch_dev_handle;
+ u8 negotiated_port_width;
+ u8 negotiated_link_rate;
+ __le16 slot;
+ __le16 slot_index;
+ __le32 reserved18;
+};
+
+#define MPI3_PCIESWITCH1_PAGEVERSION (0x00)
+#define MPI3_PCIESWITCH1_FLAGS_ASPMSTATE_MASK (0x0c)
+#define MPI3_PCIESWITCH1_FLAGS_ASPMSTATE_SHIFT (2)
+#define MPI3_PCIESWITCH1_FLAGS_ASPMSUPPORT_MASK (0x03)
+#define MPI3_PCIESWITCH1_FLAGS_ASPMSUPPORT_SHIFT (0)
+#ifndef MPI3_PCIESWITCH2_MAX_NUM_PORTS
+#define MPI3_PCIESWITCH2_MAX_NUM_PORTS (1)
+#endif
+struct mpi3_pcieswitch2_port_element {
+ __le16 link_change_count;
+ __le16 rate_change_count;
+ __le32 reserved04;
+};
+
+struct mpi3_pcie_switch_page2 {
+ struct mpi3_config_page_header header;
+ u8 num_ports;
+ u8 reserved09;
+ __le16 dev_handle;
+ __le32 reserved0c;
+ struct mpi3_pcieswitch2_port_element port[MPI3_PCIESWITCH2_MAX_NUM_PORTS];
+};
+
+#define MPI3_PCIESWITCH2_PAGEVERSION (0x00)
+struct mpi3_pcie_link_page0 {
+ struct mpi3_config_page_header header;
+ u8 link;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ __le32 receiver_error_count;
+ __le32 recovery_count;
+ __le32 corr_error_msg_count;
+ __le32 non_fatal_error_msg_count;
+ __le32 fatal_error_msg_count;
+ __le32 non_fatal_error_count;
+ __le32 fatal_error_count;
+ __le32 bad_dllp_count;
+ __le32 bad_tlp_count;
+};
+
+#define MPI3_PCIELINK0_PAGEVERSION (0x00)
+struct mpi3_enclosure_page0 {
+ struct mpi3_config_page_header header;
+ __le64 enclosure_logical_id;
+ __le16 flags;
+ __le16 enclosure_handle;
+ __le16 num_slots;
+ __le16 reserved16;
+ u8 io_unit_port;
+ u8 enclosure_level;
+ __le16 sep_dev_handle;
+ u8 chassis_slot;
+ u8 reserved1d[3];
+};
+
+#define MPI3_ENCLOSURE0_PAGEVERSION (0x00)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_MASK (0xc000)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_VIRTUAL (0x0000)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_SAS (0x4000)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_PCIE (0x8000)
+#define MPI3_ENCLS0_FLAGS_CHASSIS_SLOT_VALID (0x0020)
+#define MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT_MASK (0x0010)
+#define MPI3_ENCLS0_FLAGS_ENCL_DEV_NOT_FOUND (0x0000)
+#define MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT (0x0010)
+#define MPI3_ENCLS0_FLAGS_MNG_MASK (0x000f)
+#define MPI3_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000)
+#define MPI3_ENCLS0_FLAGS_MNG_IOC_SES (0x0001)
+#define MPI3_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0002)
+#define MPI3_DEVICE_DEVFORM_SAS_SATA (0x00)
+#define MPI3_DEVICE_DEVFORM_PCIE (0x01)
+#define MPI3_DEVICE_DEVFORM_VD (0x02)
+struct mpi3_device0_sas_sata_format {
+ __le64 sas_address;
+ __le16 flags;
+ __le16 device_info;
+ u8 phy_num;
+ u8 attached_phy_identifier;
+ u8 max_port_connections;
+ u8 zone_group;
+};
+
+#define MPI3_DEVICE0_SASSATA_FLAGS_WRITE_SAME_UNMAP_NCQ (0x0400)
+#define MPI3_DEVICE0_SASSATA_FLAGS_SLUMBER_CAP (0x0200)
+#define MPI3_DEVICE0_SASSATA_FLAGS_PARTIAL_CAP (0x0100)
+#define MPI3_DEVICE0_SASSATA_FLAGS_ASYNC_NOTIFY (0x0080)
+#define MPI3_DEVICE0_SASSATA_FLAGS_SW_PRESERVE (0x0040)
+#define MPI3_DEVICE0_SASSATA_FLAGS_UNSUPP_DEV (0x0020)
+#define MPI3_DEVICE0_SASSATA_FLAGS_48BIT_LBA (0x0010)
+#define MPI3_DEVICE0_SASSATA_FLAGS_SMART_SUPP (0x0008)
+#define MPI3_DEVICE0_SASSATA_FLAGS_NCQ_SUPP (0x0004)
+#define MPI3_DEVICE0_SASSATA_FLAGS_FUA_SUPP (0x0002)
+#define MPI3_DEVICE0_SASSATA_FLAGS_PERSIST_CAP (0x0001)
+struct mpi3_device0_pcie_format {
+ u8 supported_link_rates;
+ u8 max_port_width;
+ u8 negotiated_port_width;
+ u8 negotiated_link_rate;
+ u8 port_num;
+ u8 controller_reset_to;
+ __le16 device_info;
+ __le32 maximum_data_transfer_size;
+ __le32 capabilities;
+ __le16 noiob;
+ u8 nvme_abort_to;
+ u8 page_size;
+ __le16 shutdown_latency;
+ u8 recovery_info;
+ u8 reserved17;
+};
+
+#define MPI3_DEVICE0_PCIE_LINK_RATE_32_0_SUPP (0x10)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_16_0_SUPP (0x08)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_8_0_SUPP (0x04)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_5_0_SUPP (0x02)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_2_5_SUPP (0x01)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK (0x0007)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NO_DEVICE (0x0000)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE (0x0001)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SWITCH_DEVICE (0x0002)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SCSI_DEVICE (0x0003)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_ASPM_MASK (0x0030)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_ASPM_SHIFT (4)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_MASK (0x00c0)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_SHIFT (6)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_0 (0x0000)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_1 (0x0040)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_2 (0x0080)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_3 (0x00c0)
+#define MPI3_DEVICE0_PCIE_CAP_SGL_EXTRA_LENGTH_SUPPORTED (0x00000020)
+#define MPI3_DEVICE0_PCIE_CAP_METADATA_SEPARATED (0x00000010)
+#define MPI3_DEVICE0_PCIE_CAP_SGL_DWORD_ALIGN_REQUIRED (0x00000008)
+#define MPI3_DEVICE0_PCIE_CAP_SGL_FORMAT_SGL (0x00000004)
+#define MPI3_DEVICE0_PCIE_CAP_SGL_FORMAT_PRP (0x00000000)
+#define MPI3_DEVICE0_PCIE_CAP_BIT_BUCKET_SGL_SUPP (0x00000002)
+#define MPI3_DEVICE0_PCIE_CAP_SGL_SUPP (0x00000001)
+#define MPI3_DEVICE0_PCIE_CAP_ASPM_MASK (0x000000c0)
+#define MPI3_DEVICE0_PCIE_CAP_ASPM_SHIFT (6)
+#define MPI3_DEVICE0_PCIE_RECOVER_METHOD_MASK (0xe0)
+#define MPI3_DEVICE0_PCIE_RECOVER_METHOD_NS_MGMT (0x00)
+#define MPI3_DEVICE0_PCIE_RECOVER_METHOD_FORMAT (0x20)
+#define MPI3_DEVICE0_PCIE_RECOVER_REASON_MASK (0x1f)
+#define MPI3_DEVICE0_PCIE_RECOVER_REASON_NO_NS (0x00)
+#define MPI3_DEVICE0_PCIE_RECOVER_REASON_NO_NSID_1 (0x01)
+#define MPI3_DEVICE0_PCIE_RECOVER_REASON_TOO_MANY_NS (0x02)
+#define MPI3_DEVICE0_PCIE_RECOVER_REASON_PROTECTION (0x03)
+#define MPI3_DEVICE0_PCIE_RECOVER_REASON_METADATA_SZ (0x04)
+#define MPI3_DEVICE0_PCIE_RECOVER_REASON_LBA_DATA_SZ (0x05)
+struct mpi3_device0_vd_format {
+ u8 vd_state;
+ u8 raid_level;
+ __le16 device_info;
+ __le16 flags;
+ __le16 io_throttle_group;
+ __le16 io_throttle_group_low;
+ __le16 io_throttle_group_high;
+ u8 vd_abort_to;
+ u8 vd_reset_to;
+ __le16 reserved0e;
+};
+#define MPI3_DEVICE0_VD_STATE_OFFLINE (0x00)
+#define MPI3_DEVICE0_VD_STATE_PARTIALLY_DEGRADED (0x01)
+#define MPI3_DEVICE0_VD_STATE_DEGRADED (0x02)
+#define MPI3_DEVICE0_VD_STATE_OPTIMAL (0x03)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_0 (0)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_1 (1)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_5 (5)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_6 (6)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_10 (10)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_50 (50)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_60 (60)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_HDD (0x0010)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_SSD (0x0008)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_NVME (0x0004)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_SATA (0x0002)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_SAS (0x0001)
+#define MPI3_DEVICE0_VD_FLAGS_IO_THROTTLE_GROUP_QD_MASK (0xf000)
+#define MPI3_DEVICE0_VD_FLAGS_IO_THROTTLE_GROUP_QD_SHIFT (12)
+#define MPI3_DEVICE0_VD_FLAGS_OSEXPOSURE_MASK (0x0003)
+#define MPI3_DEVICE0_VD_FLAGS_OSEXPOSURE_HDD (0x0000)
+#define MPI3_DEVICE0_VD_FLAGS_OSEXPOSURE_SSD (0x0001)
+#define MPI3_DEVICE0_VD_FLAGS_OSEXPOSURE_NO_GUIDANCE (0x0002)
+union mpi3_device0_dev_spec_format {
+ struct mpi3_device0_sas_sata_format sas_sata_format;
+ struct mpi3_device0_pcie_format pcie_format;
+ struct mpi3_device0_vd_format vd_format;
+};
+
+struct mpi3_device_page0 {
+ struct mpi3_config_page_header header;
+ __le16 dev_handle;
+ __le16 parent_dev_handle;
+ __le16 slot;
+ __le16 enclosure_handle;
+ __le64 wwid;
+ __le16 persistent_id;
+ u8 io_unit_port;
+ u8 access_status;
+ __le16 flags;
+ __le16 reserved1e;
+ __le16 slot_index;
+ __le16 queue_depth;
+ u8 reserved24[3];
+ u8 device_form;
+ union mpi3_device0_dev_spec_format device_specific;
+};
+
+#define MPI3_DEVICE0_PAGEVERSION (0x00)
+#define MPI3_DEVICE0_PARENT_INVALID (0xffff)
+#define MPI3_DEVICE0_ENCLOSURE_HANDLE_NO_ENCLOSURE (0x0000)
+#define MPI3_DEVICE0_WWID_INVALID (0xffffffffffffffff)
+#define MPI3_DEVICE0_PERSISTENTID_INVALID (0xffff)
+#define MPI3_DEVICE0_IOUNITPORT_INVALID (0xff)
+#define MPI3_DEVICE0_ASTATUS_NO_ERRORS (0x00)
+#define MPI3_DEVICE0_ASTATUS_NEEDS_INITIALIZATION (0x01)
+#define MPI3_DEVICE0_ASTATUS_CAP_UNSUPPORTED (0x02)
+#define MPI3_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x03)
+#define MPI3_DEVICE0_ASTATUS_UNAUTHORIZED (0x04)
+#define MPI3_DEVICE0_ASTATUS_DEVICE_MISSING_DELAY (0x05)
+#define MPI3_DEVICE0_ASTATUS_PREPARE (0x06)
+#define MPI3_DEVICE0_ASTATUS_SAFE_MODE (0x07)
+#define MPI3_DEVICE0_ASTATUS_GENERIC_MAX (0x0f)
+#define MPI3_DEVICE0_ASTATUS_SAS_UNKNOWN (0x10)
+#define MPI3_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x11)
+#define MPI3_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x12)
+#define MPI3_DEVICE0_ASTATUS_SAS_MAX (0x1f)
+#define MPI3_DEVICE0_ASTATUS_SIF_UNKNOWN (0x20)
+#define MPI3_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x21)
+#define MPI3_DEVICE0_ASTATUS_SIF_DIAG (0x22)
+#define MPI3_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x23)
+#define MPI3_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x24)
+#define MPI3_DEVICE0_ASTATUS_SIF_PIO_SN (0x25)
+#define MPI3_DEVICE0_ASTATUS_SIF_MDMA_SN (0x26)
+#define MPI3_DEVICE0_ASTATUS_SIF_UDMA_SN (0x27)
+#define MPI3_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x28)
+#define MPI3_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x29)
+#define MPI3_DEVICE0_ASTATUS_SIF_MAX (0x2f)
+#define MPI3_DEVICE0_ASTATUS_PCIE_UNKNOWN (0x30)
+#define MPI3_DEVICE0_ASTATUS_PCIE_MEM_SPACE_ACCESS (0x31)
+#define MPI3_DEVICE0_ASTATUS_PCIE_UNSUPPORTED (0x32)
+#define MPI3_DEVICE0_ASTATUS_PCIE_MSIX_REQUIRED (0x33)
+#define MPI3_DEVICE0_ASTATUS_PCIE_ECRC_REQUIRED (0x34)
+#define MPI3_DEVICE0_ASTATUS_PCIE_MAX (0x3f)
+#define MPI3_DEVICE0_ASTATUS_NVME_UNKNOWN (0x40)
+#define MPI3_DEVICE0_ASTATUS_NVME_READY_TIMEOUT (0x41)
+#define MPI3_DEVICE0_ASTATUS_NVME_DEVCFG_UNSUPPORTED (0x42)
+#define MPI3_DEVICE0_ASTATUS_NVME_IDENTIFY_FAILED (0x43)
+#define MPI3_DEVICE0_ASTATUS_NVME_QCONFIG_FAILED (0x44)
+#define MPI3_DEVICE0_ASTATUS_NVME_QCREATION_FAILED (0x45)
+#define MPI3_DEVICE0_ASTATUS_NVME_EVENTCFG_FAILED (0x46)
+#define MPI3_DEVICE0_ASTATUS_NVME_GET_FEATURE_STAT_FAILED (0x47)
+#define MPI3_DEVICE0_ASTATUS_NVME_IDLE_TIMEOUT (0x48)
+#define MPI3_DEVICE0_ASTATUS_NVME_CTRL_FAILURE_STATUS (0x49)
+#define MPI3_DEVICE0_ASTATUS_NVME_INSUFFICIENT_POWER (0x4a)
+#define MPI3_DEVICE0_ASTATUS_NVME_DOORBELL_STRIDE (0x4b)
+#define MPI3_DEVICE0_ASTATUS_NVME_MEM_PAGE_MIN_SIZE (0x4c)
+#define MPI3_DEVICE0_ASTATUS_NVME_MEMORY_ALLOCATION (0x4d)
+#define MPI3_DEVICE0_ASTATUS_NVME_COMPLETION_TIME (0x4e)
+#define MPI3_DEVICE0_ASTATUS_NVME_BAR (0x4f)
+#define MPI3_DEVICE0_ASTATUS_NVME_NS_DESCRIPTOR (0x50)
+#define MPI3_DEVICE0_ASTATUS_NVME_INCOMPATIBLE_SETTINGS (0x51)
+#define MPI3_DEVICE0_ASTATUS_NVME_TOO_MANY_ERRORS (0x52)
+#define MPI3_DEVICE0_ASTATUS_NVME_MAX (0x5f)
+#define MPI3_DEVICE0_ASTATUS_VD_UNKNOWN (0x80)
+#define MPI3_DEVICE0_ASTATUS_VD_MAX (0x8f)
+#define MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_MASK (0xe000)
+#define MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_NO_LIMIT (0x0000)
+#define MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_256_LB (0x2000)
+#define MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_2048_LB (0x4000)
+#define MPI3_DEVICE0_FLAGS_CONTROLLER_DEV_HANDLE (0x0080)
+#define MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED (0x0010)
+#define MPI3_DEVICE0_FLAGS_HIDDEN (0x0008)
+#define MPI3_DEVICE0_FLAGS_ATT_METHOD_VIRTUAL (0x0004)
+#define MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED (0x0002)
+#define MPI3_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001)
+#define MPI3_DEVICE0_QUEUE_DEPTH_NOT_APPLICABLE (0x0000)
+struct mpi3_device1_sas_sata_format {
+ __le32 reserved00;
+};
+struct mpi3_device1_pcie_format {
+ __le16 vendor_id;
+ __le16 device_id;
+ __le16 subsystem_vendor_id;
+ __le16 subsystem_id;
+ __le32 reserved08;
+ u8 revision_id;
+ u8 reserved0d;
+ __le16 pci_parameters;
+};
+
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_128B (0x0)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_256B (0x1)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_512B (0x2)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_1024B (0x3)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_2048B (0x4)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_4096B (0x5)
+#define MPI3_DEVICE1_PCIE_PARAMS_MAX_READ_REQ_MASK (0x01c0)
+#define MPI3_DEVICE1_PCIE_PARAMS_MAX_READ_REQ_SHIFT (6)
+#define MPI3_DEVICE1_PCIE_PARAMS_CURR_MAX_PAYLOAD_MASK (0x0038)
+#define MPI3_DEVICE1_PCIE_PARAMS_CURR_MAX_PAYLOAD_SHIFT (3)
+#define MPI3_DEVICE1_PCIE_PARAMS_SUPP_MAX_PAYLOAD_MASK (0x0007)
+#define MPI3_DEVICE1_PCIE_PARAMS_SUPP_MAX_PAYLOAD_SHIFT (0)
+struct mpi3_device1_vd_format {
+ __le32 reserved00;
+};
+
+union mpi3_device1_dev_spec_format {
+ struct mpi3_device1_sas_sata_format sas_sata_format;
+ struct mpi3_device1_pcie_format pcie_format;
+ struct mpi3_device1_vd_format vd_format;
+};
+
+struct mpi3_device_page1 {
+ struct mpi3_config_page_header header;
+ __le16 dev_handle;
+ __le16 reserved0a;
+ __le16 link_change_count;
+ __le16 rate_change_count;
+ __le16 tm_count;
+ __le16 reserved12;
+ __le32 reserved14[10];
+ u8 reserved3c[3];
+ u8 device_form;
+ union mpi3_device1_dev_spec_format device_specific;
+};
+
+#define MPI3_DEVICE1_PAGEVERSION (0x00)
+#define MPI3_DEVICE1_COUNTER_MAX (0xfffe)
+#define MPI3_DEVICE1_COUNTER_INVALID (0xffff)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_image.h b/drivers/scsi/mpi3mr/mpi/mpi30_image.h
new file mode 100644
index 000000000000..8d824107a678
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_image.h
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2018-2023 Broadcom Inc. All rights reserved.
+ */
+#ifndef MPI30_IMAGE_H
+#define MPI30_IMAGE_H 1
+struct mpi3_comp_image_version {
+ __le16 build_num;
+ __le16 customer_id;
+ u8 phase_minor;
+ u8 phase_major;
+ u8 gen_minor;
+ u8 gen_major;
+};
+
+struct mpi3_hash_exclusion_format {
+ __le32 offset;
+ __le32 size;
+};
+
+#define MPI3_IMAGE_HASH_EXCUSION_NUM (4)
+struct mpi3_component_image_header {
+ __le32 signature0;
+ __le32 load_address;
+ __le32 data_size;
+ __le32 start_offset;
+ __le32 signature1;
+ __le32 flash_offset;
+ __le32 image_size;
+ __le32 version_string_offset;
+ __le32 build_date_string_offset;
+ __le32 build_time_string_offset;
+ __le32 environment_variable_offset;
+ __le32 application_specific;
+ __le32 signature2;
+ __le32 header_size;
+ __le32 crc;
+ __le32 flags;
+ __le32 secondary_flash_offset;
+ __le32 etp_offset;
+ __le32 etp_size;
+ union mpi3_version_union rmc_interface_version;
+ union mpi3_version_union etp_interface_version;
+ struct mpi3_comp_image_version component_image_version;
+ struct mpi3_hash_exclusion_format hash_exclusion[MPI3_IMAGE_HASH_EXCUSION_NUM];
+ __le32 next_image_header_offset;
+ union mpi3_version_union security_version;
+ __le32 reserved84[31];
+};
+
+#define MPI3_IMAGE_HEADER_SIGNATURE0_MPI3 (0xeb00003e)
+#define MPI3_IMAGE_HEADER_LOAD_ADDRESS_INVALID (0x00000000)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_APPLICATION (0x20505041)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_FIRST_MUTABLE (0x20434d46)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_BSP (0x20505342)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_ROM_BIOS (0x534f4942)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_HII_X64 (0x4d494948)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_HII_ARM (0x41494948)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_CPLD (0x444c5043)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_SPD (0x20445053)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_GAS_GAUGE (0x20534147)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_PBLP (0x504c4250)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_MANIFEST (0x464e414d)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_OEM (0x204d454f)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_RMC (0x20434d52)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_SMM (0x204d4d53)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_PSW (0x20575350)
+#define MPI3_IMAGE_HEADER_SIGNATURE2_VALUE (0x50584546)
+#define MPI3_IMAGE_HEADER_FLAGS_SIGNED_UEFI_MASK (0x00000300)
+#define MPI3_IMAGE_HEADER_FLAGS_SIGNED_UEFI_SHIFT (8)
+#define MPI3_IMAGE_HEADER_FLAGS_CERT_CHAIN_FORMAT_MASK (0x000000c0)
+#define MPI3_IMAGE_HEADER_FLAGS_CERT_CHAIN_FORMAT_SHIFT (6)
+#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_MASK (0x00000030)
+#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_SHIFT (4)
+#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_CDI (0x00000000)
+#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_DI (0x00000010)
+#define MPI3_IMAGE_HEADER_FLAGS_SIGNED_NVDATA (0x00000008)
+#define MPI3_IMAGE_HEADER_FLAGS_REQUIRES_ACTIVATION (0x00000004)
+#define MPI3_IMAGE_HEADER_FLAGS_COMPRESSED (0x00000002)
+#define MPI3_IMAGE_HEADER_FLAGS_FLASH (0x00000001)
+#define MPI3_IMAGE_HEADER_SIGNATURE0_OFFSET (0x00)
+#define MPI3_IMAGE_HEADER_LOAD_ADDRESS_OFFSET (0x04)
+#define MPI3_IMAGE_HEADER_DATA_SIZE_OFFSET (0x08)
+#define MPI3_IMAGE_HEADER_START_OFFSET_OFFSET (0x0c)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_OFFSET (0x10)
+#define MPI3_IMAGE_HEADER_FLASH_OFFSET_OFFSET (0x14)
+#define MPI3_IMAGE_HEADER_FLASH_SIZE_OFFSET (0x18)
+#define MPI3_IMAGE_HEADER_VERSION_STRING_OFFSET_OFFSET (0x1c)
+#define MPI3_IMAGE_HEADER_BUILD_DATE_STRING_OFFSET_OFFSET (0x20)
+#define MPI3_IMAGE_HEADER_BUILD_TIME_OFFSET_OFFSET (0x24)
+#define MPI3_IMAGE_HEADER_ENVIROMENT_VAR_OFFSET_OFFSET (0x28)
+#define MPI3_IMAGE_HEADER_APPLICATION_SPECIFIC_OFFSET (0x2c)
+#define MPI3_IMAGE_HEADER_SIGNATURE2_OFFSET (0x30)
+#define MPI3_IMAGE_HEADER_HEADER_SIZE_OFFSET (0x34)
+#define MPI3_IMAGE_HEADER_CRC_OFFSET (0x38)
+#define MPI3_IMAGE_HEADER_FLAGS_OFFSET (0x3c)
+#define MPI3_IMAGE_HEADER_SECONDARY_FLASH_OFFSET_OFFSET (0x40)
+#define MPI3_IMAGE_HEADER_ETP_OFFSET_OFFSET (0x44)
+#define MPI3_IMAGE_HEADER_ETP_SIZE_OFFSET (0x48)
+#define MPI3_IMAGE_HEADER_RMC_INTERFACE_VER_OFFSET (0x4c)
+#define MPI3_IMAGE_HEADER_ETP_INTERFACE_VER_OFFSET (0x50)
+#define MPI3_IMAGE_HEADER_COMPONENT_IMAGE_VER_OFFSET (0x54)
+#define MPI3_IMAGE_HEADER_HASH_EXCLUSION_OFFSET (0x5c)
+#define MPI3_IMAGE_HEADER_NEXT_IMAGE_HEADER_OFFSET_OFFSET (0x7c)
+#define MPI3_IMAGE_HEADER_SIZE (0x100)
+#ifndef MPI3_CI_MANIFEST_MPI_MAX
+#define MPI3_CI_MANIFEST_MPI_MAX (1)
+#endif
+struct mpi3_ci_manifest_mpi_comp_image_ref {
+ __le32 signature1;
+ __le32 reserved04[3];
+ struct mpi3_comp_image_version component_image_version;
+ __le32 component_image_version_string_offset;
+ __le32 crc;
+};
+
+struct mpi3_ci_manifest_mpi {
+ u8 manifest_type;
+ u8 reserved01[3];
+ __le32 reserved04[3];
+ u8 num_image_references;
+ u8 release_level;
+ __le16 reserved12;
+ __le16 reserved14;
+ __le16 flags;
+ __le32 reserved18[2];
+ __le16 vendor_id;
+ __le16 device_id;
+ __le16 subsystem_vendor_id;
+ __le16 subsystem_id;
+ __le32 reserved28[2];
+ union mpi3_version_union package_security_version;
+ __le32 reserved34;
+ struct mpi3_comp_image_version package_version;
+ __le32 package_version_string_offset;
+ __le32 package_build_date_string_offset;
+ __le32 package_build_time_string_offset;
+ __le32 reserved4c;
+ __le32 diag_authorization_identifier[16];
+ struct mpi3_ci_manifest_mpi_comp_image_ref component_image_ref[MPI3_CI_MANIFEST_MPI_MAX];
+};
+
+#define MPI3_CI_MANIFEST_MPI_RELEASE_LEVEL_DEV (0x00)
+#define MPI3_CI_MANIFEST_MPI_RELEASE_LEVEL_PREALPHA (0x10)
+#define MPI3_CI_MANIFEST_MPI_RELEASE_LEVEL_ALPHA (0x20)
+#define MPI3_CI_MANIFEST_MPI_RELEASE_LEVEL_BETA (0x30)
+#define MPI3_CI_MANIFEST_MPI_RELEASE_LEVEL_RC (0x40)
+#define MPI3_CI_MANIFEST_MPI_RELEASE_LEVEL_GCA (0x50)
+#define MPI3_CI_MANIFEST_MPI_RELEASE_LEVEL_POINT (0x60)
+#define MPI3_CI_MANIFEST_MPI_FLAGS_DIAG_AUTHORIZATION (0x01)
+#define MPI3_CI_MANIFEST_MPI_SUBSYSTEMID_IGNORED (0xffff)
+#define MPI3_CI_MANIFEST_MPI_PKG_VER_STR_OFF_UNSPECIFIED (0x00000000)
+#define MPI3_CI_MANIFEST_MPI_PKG_BUILD_DATE_STR_OFF_UNSPECIFIED (0x00000000)
+#define MPI3_CI_MANIFEST_MPI_PKG_BUILD_TIME_STR_OFF_UNSPECIFIED (0x00000000)
+union mpi3_ci_manifest {
+ struct mpi3_ci_manifest_mpi mpi;
+ __le32 dword[1];
+};
+
+#define MPI3_CI_MANIFEST_TYPE_MPI (0x00)
+struct mpi3_extended_image_header {
+ u8 image_type;
+ u8 reserved01[3];
+ __le32 checksum;
+ __le32 image_size;
+ __le32 next_image_header_offset;
+ __le32 reserved10[4];
+ __le32 identify_string[8];
+};
+
+#define MPI3_EXT_IMAGE_IMAGETYPE_OFFSET (0x00)
+#define MPI3_EXT_IMAGE_IMAGESIZE_OFFSET (0x08)
+#define MPI3_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0c)
+#define MPI3_EXT_IMAGE_HEADER_SIZE (0x40)
+#define MPI3_EXT_IMAGE_TYPE_UNSPECIFIED (0x00)
+#define MPI3_EXT_IMAGE_TYPE_NVDATA (0x03)
+#define MPI3_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07)
+#define MPI3_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09)
+#define MPI3_EXT_IMAGE_TYPE_RDE (0x0a)
+#define MPI3_EXT_IMAGE_TYPE_AUXILIARY_PROCESSOR (0x0b)
+#define MPI3_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI3_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xff)
+struct mpi3_supported_device {
+ __le16 device_id;
+ __le16 vendor_id;
+ __le16 device_id_mask;
+ __le16 reserved06;
+ u8 low_pci_rev;
+ u8 high_pci_rev;
+ __le16 reserved0a;
+ __le32 reserved0c;
+};
+
+#ifndef MPI3_SUPPORTED_DEVICE_MAX
+#define MPI3_SUPPORTED_DEVICE_MAX (1)
+#endif
+struct mpi3_supported_devices_data {
+ u8 image_version;
+ u8 reserved01;
+ u8 num_devices;
+ u8 reserved03;
+ __le32 reserved04;
+ struct mpi3_supported_device supported_device[MPI3_SUPPORTED_DEVICE_MAX];
+};
+
+#ifndef MPI3_PUBLIC_KEY_MAX
+#define MPI3_PUBLIC_KEY_MAX (1)
+#endif
+struct mpi3_encrypted_hash_entry {
+ u8 hash_image_type;
+ u8 hash_algorithm;
+ u8 encryption_algorithm;
+ u8 flags;
+ __le16 public_key_size;
+ __le16 signature_size;
+ __le32 public_key[MPI3_PUBLIC_KEY_MAX];
+};
+#define MPI3_HASH_IMAGE_TYPE_KEY_WITH_HASH (0x03)
+#define MPI3_HASH_IMAGE_TYPE_KEY_WITH_HASH_1_OF_2 (0x04)
+#define MPI3_HASH_IMAGE_TYPE_KEY_WITH_HASH_2_OF_2 (0x05)
+#define MPI3_HASH_ALGORITHM_VERSION_MASK (0xe0)
+#define MPI3_HASH_ALGORITHM_VERSION_SHIFT (5)
+#define MPI3_HASH_ALGORITHM_VERSION_NONE (0x00)
+#define MPI3_HASH_ALGORITHM_VERSION_SHA1 (0x20)
+#define MPI3_HASH_ALGORITHM_VERSION_SHA2 (0x40)
+#define MPI3_HASH_ALGORITHM_VERSION_SHA3 (0x60)
+#define MPI3_HASH_ALGORITHM_SIZE_MASK (0x1f)
+#define MPI3_HASH_ALGORITHM_SIZE_SHIFT (0)
+#define MPI3_HASH_ALGORITHM_SIZE_UNUSED (0x00)
+#define MPI3_HASH_ALGORITHM_SIZE_SHA256 (0x01)
+#define MPI3_HASH_ALGORITHM_SIZE_SHA512 (0x02)
+#define MPI3_HASH_ALGORITHM_SIZE_SHA384 (0x03)
+#define MPI3_ENCRYPTION_ALGORITHM_UNUSED (0x00)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA256 (0x01)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA512 (0x02)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA1024 (0x03)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA2048 (0x04)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA4096 (0x05)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA3072 (0x06)
+
+/* hierarchical signature system (hss) */
+#define MPI3_ENCRYPTION_ALGORITHM_ML_DSA_87 (0x0b)
+#define MPI3_ENCRYPTION_ALGORITHM_ML_DSA_65 (0x0c)
+#define MPI3_ENCRYPTION_ALGORITHM_ML_DSA_44 (0x0d)
+#define MPI3_ENCRYPTED_HASH_ENTRY_FLAGS_PAIRED_KEY_MASK (0x0f)
+#define MPI3_ENCRYPTED_HASH_ENTRY_FLAGS_PAIRED_KEY_SHIFT (0)
+
+#ifndef MPI3_ENCRYPTED_HASH_ENTRY_MAX
+#define MPI3_ENCRYPTED_HASH_ENTRY_MAX (1)
+#endif
+struct mpi3_encrypted_hash_data {
+ u8 image_version;
+ u8 num_hash;
+ __le16 reserved02;
+ __le32 reserved04;
+ struct mpi3_encrypted_hash_entry encrypted_hash_entry[MPI3_ENCRYPTED_HASH_ENTRY_MAX];
+};
+
+#ifndef MPI3_AUX_PROC_DATA_MAX
+#define MPI3_AUX_PROC_DATA_MAX (1)
+#endif
+struct mpi3_aux_processor_data {
+ u8 boot_method;
+ u8 num_load_addr;
+ u8 reserved02;
+ u8 type;
+ __le32 version;
+ __le32 load_address[8];
+ __le32 reserved28[22];
+ __le32 aux_processor_data[MPI3_AUX_PROC_DATA_MAX];
+};
+
+#define MPI3_AUX_PROC_DATA_OFFSET (0x80)
+#define MPI3_AUXPROCESSOR_BOOT_METHOD_MO_MSG (0x00)
+#define MPI3_AUXPROCESSOR_BOOT_METHOD_MO_DOORBELL (0x01)
+#define MPI3_AUXPROCESSOR_BOOT_METHOD_COMPONENT (0x02)
+#define MPI3_AUXPROCESSOR_TYPE_ARM_A15 (0x00)
+#define MPI3_AUXPROCESSOR_TYPE_ARM_M0 (0x01)
+#define MPI3_AUXPROCESSOR_TYPE_ARM_R4 (0x02)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_init.h b/drivers/scsi/mpi3mr/mpi/mpi30_init.h
new file mode 100644
index 000000000000..bbef5bac92ed
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_init.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2023 Broadcom Inc. All rights reserved.
+ */
+#ifndef MPI30_INIT_H
+#define MPI30_INIT_H 1
+struct mpi3_scsi_io_cdb_eedp32 {
+ u8 cdb[20];
+ __be32 primary_reference_tag;
+ __le16 primary_application_tag;
+ __le16 primary_application_tag_mask;
+ __le32 transfer_length;
+};
+
+union mpi3_scsi_io_cdb_union {
+ u8 cdb32[32];
+ struct mpi3_scsi_io_cdb_eedp32 eedp32;
+ struct mpi3_sge_common sge;
+};
+
+struct mpi3_scsi_io_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 dev_handle;
+ __le32 flags;
+ __le32 skip_count;
+ __le32 data_length;
+ u8 lun[8];
+ union mpi3_scsi_io_cdb_union cdb;
+ union mpi3_sge_union sgl[4];
+};
+
+#define MPI3_SCSIIO_MSGFLAGS_METASGL_VALID (0x80)
+#define MPI3_SCSIIO_MSGFLAGS_DIVERT_TO_FIRMWARE (0x40)
+#define MPI3_SCSIIO_FLAGS_LARGE_CDB (0x60000000)
+#define MPI3_SCSIIO_FLAGS_LARGE_CDB_MASK (0x60000000)
+#define MPI3_SCSIIO_FLAGS_LARGE_CDB_SHIFT (29)
+#define MPI3_SCSIIO_FLAGS_IOC_USE_ONLY_27_MASK (0x18000000)
+#define MPI3_SCSIIO_FLAGS_IOC_USE_ONLY_27_SHIFT (27)
+#define MPI3_SCSIIO_FLAGS_CDB_16_OR_LESS (0x00000000)
+#define MPI3_SCSIIO_FLAGS_CDB_GREATER_THAN_16 (0x20000000)
+#define MPI3_SCSIIO_FLAGS_CDB_IN_SEPARATE_BUFFER (0x40000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_MASK (0x07000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SHIFT (24)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_MASK (0x000c0000)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_SHIFT (18)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SIMPLEQ (0x00000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_HEADOFQ (0x01000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_ORDEREDQ (0x02000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_ACAQ (0x04000000)
+#define MPI3_SCSIIO_FLAGS_CMDPRI_MASK (0x00f00000)
+#define MPI3_SCSIIO_FLAGS_CMDPRI_SHIFT (20)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_NO_DATA_TRANSFER (0x00000000)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_WRITE (0x00040000)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_READ (0x00080000)
+#define MPI3_SCSIIO_FLAGS_DMAOPERATION_MASK (0x00030000)
+#define MPI3_SCSIIO_FLAGS_DMAOPERATION_SHIFT (16)
+#define MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI (0x00010000)
+#define MPI3_SCSIIO_FLAGS_DIVERT_REASON_MASK (0x000000f0)
+#define MPI3_SCSIIO_FLAGS_DIVERT_REASON_SHIFT (4)
+#define MPI3_SCSIIO_FLAGS_DIVERT_REASON_IO_THROTTLING (0x00000010)
+#define MPI3_SCSIIO_FLAGS_DIVERT_REASON_WRITE_SAME_TOO_LARGE (0x00000020)
+#define MPI3_SCSIIO_FLAGS_DIVERT_REASON_PROD_SPECIFIC (0x00000080)
+#define MPI3_SCSIIO_METASGL_INDEX (3)
+struct mpi3_scsi_io_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 scsi_status;
+ u8 scsi_state;
+ __le16 dev_handle;
+ __le32 transfer_count;
+ __le32 sense_count;
+ __le32 response_data;
+ __le16 task_tag;
+ __le16 scsi_status_qualifier;
+ __le32 eedp_error_offset;
+ __le16 eedp_observed_app_tag;
+ __le16 eedp_observed_guard;
+ __le32 eedp_observed_ref_tag;
+ __le64 sense_data_buffer_address;
+};
+
+#define MPI3_SCSIIO_REPLY_MSGFLAGS_REFTAG_OBSERVED_VALID (0x01)
+#define MPI3_SCSIIO_REPLY_MSGFLAGS_APPTAG_OBSERVED_VALID (0x02)
+#define MPI3_SCSIIO_REPLY_MSGFLAGS_GUARD_OBSERVED_VALID (0x04)
+#define MPI3_SCSI_STATUS_GOOD (0x00)
+#define MPI3_SCSI_STATUS_CHECK_CONDITION (0x02)
+#define MPI3_SCSI_STATUS_CONDITION_MET (0x04)
+#define MPI3_SCSI_STATUS_BUSY (0x08)
+#define MPI3_SCSI_STATUS_INTERMEDIATE (0x10)
+#define MPI3_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
+#define MPI3_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
+#define MPI3_SCSI_STATUS_COMMAND_TERMINATED (0x22)
+#define MPI3_SCSI_STATUS_TASK_SET_FULL (0x28)
+#define MPI3_SCSI_STATUS_ACA_ACTIVE (0x30)
+#define MPI3_SCSI_STATUS_TASK_ABORTED (0x40)
+#define MPI3_SCSI_STATE_SENSE_MASK (0x03)
+#define MPI3_SCSI_STATE_SENSE_SHIFT (0)
+#define MPI3_SCSI_STATE_SENSE_VALID (0x00)
+#define MPI3_SCSI_STATE_SENSE_FAILED (0x01)
+#define MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY (0x02)
+#define MPI3_SCSI_STATE_SENSE_NOT_AVAILABLE (0x03)
+#define MPI3_SCSI_STATE_NO_SCSI_STATUS (0x04)
+#define MPI3_SCSI_STATE_TERMINATED (0x08)
+#define MPI3_SCSI_STATE_RESPONSE_DATA_VALID (0x10)
+#define MPI3_SCSI_RSP_RESPONSECODE_MASK (0x000000ff)
+#define MPI3_SCSI_RSP_RESPONSECODE_SHIFT (0)
+#define MPI3_SCSI_RSP_ARI2_MASK (0x0000ff00)
+#define MPI3_SCSI_RSP_ARI2_SHIFT (8)
+#define MPI3_SCSI_RSP_ARI1_MASK (0x00ff0000)
+#define MPI3_SCSI_RSP_ARI1_SHIFT (16)
+#define MPI3_SCSI_RSP_ARI0_MASK (0xff000000)
+#define MPI3_SCSI_RSP_ARI0_SHIFT (24)
+#define MPI3_SCSI_TASKTAG_UNKNOWN (0xffff)
+#define MPI3_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x08)
+#define MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01)
+#define MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK_SET (0x02)
+#define MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
+#define MPI3_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
+#define MPI3_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06)
+#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07)
+#define MPI3_SCSITASKMGMT_TASKTYPE_CLEAR_ACA (0x08)
+#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK_SET (0x09)
+#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_ASYNC_EVENT (0x0a)
+#define MPI3_SCSITASKMGMT_TASKTYPE_I_T_NEXUS_RESET (0x0b)
+#define MPI3_SCSITASKMGMT_RSPCODE_TM_COMPLETE (0x00)
+#define MPI3_SCSITASKMGMT_RSPCODE_INVALID_FRAME (0x02)
+#define MPI3_SCSITASKMGMT_RSPCODE_TM_FUNCTION_NOT_SUPPORTED (0x04)
+#define MPI3_SCSITASKMGMT_RSPCODE_TM_FAILED (0x05)
+#define MPI3_SCSITASKMGMT_RSPCODE_TM_SUCCEEDED (0x08)
+#define MPI3_SCSITASKMGMT_RSPCODE_TM_INVALID_LUN (0x09)
+#define MPI3_SCSITASKMGMT_RSPCODE_TM_OVERLAPPED_TAG (0x0a)
+#define MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC (0x80)
+#define MPI3_SCSITASKMGMT_RSPCODE_TM_NVME_DENIED (0x81)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h
new file mode 100644
index 000000000000..b42933fcd423
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h
@@ -0,0 +1,1092 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2023 Broadcom Inc. All rights reserved.
+ */
+#ifndef MPI30_IOC_H
+#define MPI30_IOC_H 1
+struct mpi3_ioc_init_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ union mpi3_version_union mpi_version;
+ __le64 time_stamp;
+ u8 reserved18;
+ u8 who_init;
+ __le16 reserved1a;
+ __le16 reply_free_queue_depth;
+ __le16 reserved1e;
+ __le64 reply_free_queue_address;
+ __le32 reserved28;
+ __le16 sense_buffer_free_queue_depth;
+ __le16 sense_buffer_length;
+ __le64 sense_buffer_free_queue_address;
+ __le64 driver_information_address;
+};
+#define MPI3_IOCINIT_MSGFLAGS_WRITESAMEDIVERT_SUPPORTED (0x08)
+#define MPI3_IOCINIT_MSGFLAGS_SCSIIOSTATUSREPLY_SUPPORTED (0x04)
+#define MPI3_IOCINIT_MSGFLAGS_HOSTMETADATA_MASK (0x03)
+#define MPI3_IOCINIT_MSGFLAGS_HOSTMETADATA_SHIFT (0)
+#define MPI3_IOCINIT_MSGFLAGS_HOSTMETADATA_NOT_USED (0x00)
+#define MPI3_IOCINIT_MSGFLAGS_HOSTMETADATA_SEPARATED (0x01)
+#define MPI3_IOCINIT_MSGFLAGS_HOSTMETADATA_INLINE (0x02)
+#define MPI3_IOCINIT_MSGFLAGS_HOSTMETADATA_BOTH (0x03)
+#define MPI3_WHOINIT_NOT_INITIALIZED (0x00)
+#define MPI3_WHOINIT_ROM_BIOS (0x02)
+#define MPI3_WHOINIT_HOST_DRIVER (0x03)
+#define MPI3_WHOINIT_MANUFACTURER (0x04)
+
+#define MPI3_IOCINIT_DRIVERCAP_OSEXPOSURE_MASK (0x00000003)
+#define MPI3_IOCINIT_DRIVERCAP_OSEXPOSURE_SHIFT (0)
+#define MPI3_IOCINIT_DRIVERCAP_OSEXPOSURE_NO_GUIDANCE (0x00000000)
+#define MPI3_IOCINIT_DRIVERCAP_OSEXPOSURE_NO_SPECIAL (0x00000001)
+#define MPI3_IOCINIT_DRIVERCAP_OSEXPOSURE_REPORT_AS_HDD (0x00000002)
+#define MPI3_IOCINIT_DRIVERCAP_OSEXPOSURE_REPORT_AS_SSD (0x00000003)
+
+struct mpi3_ioc_facts_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le32 reserved0c;
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_ioc_facts_data {
+ __le16 ioc_facts_data_length;
+ __le16 reserved02;
+ union mpi3_version_union mpi_version;
+ struct mpi3_comp_image_version fw_version;
+ __le32 ioc_capabilities;
+ u8 ioc_number;
+ u8 who_init;
+ __le16 max_msix_vectors;
+ __le16 max_outstanding_requests;
+ __le16 product_id;
+ __le16 ioc_request_frame_size;
+ __le16 reply_frame_size;
+ __le16 ioc_exceptions;
+ __le16 max_persistent_id;
+ u8 sge_modifier_mask;
+ u8 sge_modifier_value;
+ u8 sge_modifier_shift;
+ u8 protocol_flags;
+ __le16 max_sas_initiators;
+ __le16 max_data_length;
+ __le16 max_sas_expanders;
+ __le16 max_enclosures;
+ __le16 min_dev_handle;
+ __le16 max_dev_handle;
+ __le16 max_pcie_switches;
+ __le16 max_nvme;
+ __le16 reserved38;
+ __le16 max_vds;
+ __le16 max_host_pds;
+ __le16 max_adv_host_pds;
+ __le16 max_raid_pds;
+ __le16 max_posted_cmd_buffers;
+ __le32 flags;
+ __le16 max_operational_request_queues;
+ __le16 max_operational_reply_queues;
+ __le16 shutdown_timeout;
+ __le16 reserved4e;
+ __le32 diag_trace_size;
+ __le32 diag_fw_size;
+ __le32 diag_driver_size;
+ u8 max_host_pd_ns_count;
+ u8 max_adv_host_pd_ns_count;
+ u8 max_raidpd_ns_count;
+ u8 max_devices_per_throttle_group;
+ __le16 io_throttle_data_length;
+ __le16 max_io_throttle_group;
+ __le16 io_throttle_low;
+ __le16 io_throttle_high;
+ __le32 diag_fdl_size;
+ __le32 diag_tty_size;
+};
+#define MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_MASK (0x80000000)
+#define MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_SHIFT (31)
+#define MPI3_IOCFACTS_CAPABILITY_SUPERVISOR_IOC (0x00000000)
+#define MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_IOC (0x80000000)
+#define MPI3_IOCFACTS_CAPABILITY_INT_COALESCE_MASK (0x00000600)
+#define MPI3_IOCFACTS_CAPABILITY_INT_COALESCE_SHIFT (9)
+#define MPI3_IOCFACTS_CAPABILITY_INT_COALESCE_FIXED_THRESHOLD (0x00000000)
+#define MPI3_IOCFACTS_CAPABILITY_INT_COALESCE_OUTSTANDING_IO (0x00000200)
+#define MPI3_IOCFACTS_CAPABILITY_COMPLETE_RESET_SUPPORTED (0x00000100)
+#define MPI3_IOCFACTS_CAPABILITY_SEG_DIAG_TRACE_SUPPORTED (0x00000080)
+#define MPI3_IOCFACTS_CAPABILITY_SEG_DIAG_FW_SUPPORTED (0x00000040)
+#define MPI3_IOCFACTS_CAPABILITY_SEG_DIAG_DRIVER_SUPPORTED (0x00000020)
+#define MPI3_IOCFACTS_CAPABILITY_ADVANCED_HOST_PD_SUPPORTED (0x00000010)
+#define MPI3_IOCFACTS_CAPABILITY_RAID_SUPPORTED (0x00000008)
+#define MPI3_IOCFACTS_CAPABILITY_MULTIPATH_SUPPORTED (0x00000002)
+#define MPI3_IOCFACTS_CAPABILITY_COALESCE_CTRL_SUPPORTED (0x00000001)
+#define MPI3_IOCFACTS_PID_TYPE_MASK (0xf000)
+#define MPI3_IOCFACTS_PID_TYPE_SHIFT (12)
+#define MPI3_IOCFACTS_PID_PRODUCT_MASK (0x0f00)
+#define MPI3_IOCFACTS_PID_PRODUCT_SHIFT (8)
+#define MPI3_IOCFACTS_PID_FAMILY_MASK (0x00ff)
+#define MPI3_IOCFACTS_PID_FAMILY_SHIFT (0)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_REKEY (0x2000)
+#define MPI3_IOCFACTS_EXCEPT_SAS_DISABLED (0x1000)
+#define MPI3_IOCFACTS_EXCEPT_SAFE_MODE (0x0800)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_MASK (0x0700)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_SHIFT (8)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_NONE (0x0000)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_LOCAL_VIA_MGMT (0x0100)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_EXT_VIA_MGMT (0x0200)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_DRIVE_EXT_VIA_MGMT (0x0300)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_LOCAL_VIA_OOB (0x0400)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_EXT_VIA_OOB (0x0500)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_DRIVE_EXT_VIA_OOB (0x0600)
+#define MPI3_IOCFACTS_EXCEPT_PCIE_DISABLED (0x0080)
+#define MPI3_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0040)
+#define MPI3_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0020)
+#define MPI3_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0010)
+#define MPI3_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0008)
+#define MPI3_IOCFACTS_EXCEPT_BLOCKING_BOOT_EVENT (0x0004)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_SELFTEST_FAILURE (0x0002)
+#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x0001)
+#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_SHIFT (0)
+#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_PRIMARY (0x0000)
+#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_SECONDARY (0x0001)
+#define MPI3_IOCFACTS_PROTOCOL_SAS (0x0010)
+#define MPI3_IOCFACTS_PROTOCOL_SATA (0x0008)
+#define MPI3_IOCFACTS_PROTOCOL_NVME (0x0004)
+#define MPI3_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002)
+#define MPI3_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001)
+#define MPI3_IOCFACTS_MAX_DATA_LENGTH_NOT_REPORTED (0x0000)
+#define MPI3_IOCFACTS_FLAGS_SIGNED_NVDATA_REQUIRED (0x00010000)
+#define MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_MASK (0x0000ff00)
+#define MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_SHIFT (8)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK (0x00000030)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_SHIFT (4)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_NOT_STARTED (0x00000000)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_IN_PROGRESS (0x00000010)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_COMPLETE (0x00000020)
+#define MPI3_IOCFACTS_FLAGS_PERSONALITY_MASK (0x0000000f)
+#define MPI3_IOCFACTS_FLAGS_PERSONALITY_SHIFT (0)
+#define MPI3_IOCFACTS_FLAGS_PERSONALITY_EHBA (0x00000000)
+#define MPI3_IOCFACTS_FLAGS_PERSONALITY_RAID_DDR (0x00000002)
+#define MPI3_IOCFACTS_IO_THROTTLE_DATA_LENGTH_NOT_REQUIRED (0x0000)
+#define MPI3_IOCFACTS_MAX_IO_THROTTLE_GROUP_NOT_REQUIRED (0x0000)
+#define MPI3_IOCFACTS_DIAGFDLSIZE_NOT_SUPPORTED (0x00000000)
+#define MPI3_IOCFACTS_DIAGTTYSIZE_NOT_SUPPORTED (0x00000000)
+struct mpi3_mgmt_passthrough_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le32 reserved0c[5];
+ union mpi3_sge_union command_sgl;
+ union mpi3_sge_union response_sgl;
+};
+
+struct mpi3_create_request_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 flags;
+ u8 burst;
+ __le16 size;
+ __le16 queue_id;
+ __le16 reply_queue_id;
+ __le16 reserved12;
+ __le32 reserved14;
+ __le64 base_address;
+};
+
+#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_MASK (0x80)
+#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SHIFT (7)
+#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED (0x80)
+#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_CONTIGUOUS (0x00)
+#define MPI3_CREATE_REQUEST_QUEUE_SIZE_MINIMUM (2)
+struct mpi3_delete_request_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 queue_id;
+};
+
+struct mpi3_create_reply_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 flags;
+ u8 reserved0b;
+ __le16 size;
+ __le16 queue_id;
+ __le16 msix_index;
+ __le16 reserved12;
+ __le32 reserved14;
+ __le64 base_address;
+};
+
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_MASK (0x80)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_SHIFT (7)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_SEGMENTED (0x80)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_CONTIGUOUS (0x00)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_COALESCE_DISABLE (0x02)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_MASK (0x01)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_SHIFT (0)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_DISABLE (0x00)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_ENABLE (0x01)
+#define MPI3_CREATE_REPLY_QUEUE_SIZE_MINIMUM (2)
+struct mpi3_delete_reply_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 queue_id;
+};
+
+struct mpi3_port_enable_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+};
+
+#define MPI3_EVENT_LOG_DATA (0x01)
+#define MPI3_EVENT_CHANGE (0x02)
+#define MPI3_EVENT_GPIO_INTERRUPT (0x04)
+#define MPI3_EVENT_CABLE_MGMT (0x06)
+#define MPI3_EVENT_DEVICE_ADDED (0x07)
+#define MPI3_EVENT_DEVICE_INFO_CHANGED (0x08)
+#define MPI3_EVENT_PREPARE_FOR_RESET (0x09)
+#define MPI3_EVENT_COMP_IMAGE_ACT_START (0x0a)
+#define MPI3_EVENT_ENCL_DEVICE_ADDED (0x0b)
+#define MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE (0x0c)
+#define MPI3_EVENT_DEVICE_STATUS_CHANGE (0x0d)
+#define MPI3_EVENT_ENERGY_PACK_CHANGE (0x0e)
+#define MPI3_EVENT_SAS_DISCOVERY (0x11)
+#define MPI3_EVENT_SAS_BROADCAST_PRIMITIVE (0x12)
+#define MPI3_EVENT_SAS_NOTIFY_PRIMITIVE (0x13)
+#define MPI3_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x14)
+#define MPI3_EVENT_SAS_INIT_TABLE_OVERFLOW (0x15)
+#define MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x16)
+#define MPI3_EVENT_SAS_PHY_COUNTER (0x18)
+#define MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR (0x19)
+#define MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST (0x20)
+#define MPI3_EVENT_PCIE_ENUMERATION (0x22)
+#define MPI3_EVENT_PCIE_ERROR_THRESHOLD (0x23)
+#define MPI3_EVENT_HARD_RESET_RECEIVED (0x40)
+#define MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE (0x50)
+#define MPI3_EVENT_MIN_PRODUCT_SPECIFIC (0x60)
+#define MPI3_EVENT_MAX_PRODUCT_SPECIFIC (0x7f)
+#define MPI3_EVENT_NOTIFY_EVENTMASK_WORDS (4)
+struct mpi3_event_notification_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le16 sas_broadcast_primitive_masks;
+ __le16 sas_notify_primitive_masks;
+ __le32 event_masks[MPI3_EVENT_NOTIFY_EVENTMASK_WORDS];
+};
+
+struct mpi3_event_notification_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 event_data_length;
+ u8 event;
+ __le16 ioc_change_count;
+ __le32 event_context;
+ __le32 event_data[1];
+};
+
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_MASK (0x01)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_SHIFT (0)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_REQUIRED (0x01)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_NOT_REQUIRED (0x00)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_MASK (0x02)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_SHIFT (1)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_ORIGINAL (0x00)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_REPLAY (0x02)
+struct mpi3_event_data_gpio_interrupt {
+ u8 gpio_num;
+ u8 reserved01[3];
+};
+struct mpi3_event_data_cable_management {
+ __le32 active_cable_power_requirement;
+ u8 status;
+ u8 receptacle_id;
+ __le16 reserved06;
+};
+
+#define MPI3_EVENT_CABLE_MGMT_ACT_CABLE_PWR_INVALID (0xffffffff)
+#define MPI3_EVENT_CABLE_MGMT_STATUS_INSUFFICIENT_POWER (0x00)
+#define MPI3_EVENT_CABLE_MGMT_STATUS_PRESENT (0x01)
+#define MPI3_EVENT_CABLE_MGMT_STATUS_DEGRADED (0x02)
+struct mpi3_event_ack_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ u8 event;
+ u8 reserved0d[3];
+ __le32 event_context;
+};
+
+struct mpi3_event_data_prepare_for_reset {
+ u8 reason_code;
+ u8 reserved01;
+ __le16 reserved02;
+};
+
+#define MPI3_EVENT_PREPARE_RESET_RC_START (0x01)
+#define MPI3_EVENT_PREPARE_RESET_RC_ABORT (0x02)
+struct mpi3_event_data_comp_image_activation {
+ __le32 reserved00;
+};
+
+struct mpi3_event_data_device_status_change {
+ __le16 task_tag;
+ u8 reason_code;
+ u8 io_unit_port;
+ __le16 parent_dev_handle;
+ __le16 dev_handle;
+ __le64 wwid;
+ u8 lun[8];
+};
+
+#define MPI3_EVENT_DEV_STAT_RC_MOVED (0x01)
+#define MPI3_EVENT_DEV_STAT_RC_HIDDEN (0x02)
+#define MPI3_EVENT_DEV_STAT_RC_NOT_HIDDEN (0x03)
+#define MPI3_EVENT_DEV_STAT_RC_ASYNC_NOTIFICATION (0x04)
+#define MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_STRT (0x20)
+#define MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_CMP (0x21)
+#define MPI3_EVENT_DEV_STAT_RC_INT_TASK_ABORT_STRT (0x22)
+#define MPI3_EVENT_DEV_STAT_RC_INT_TASK_ABORT_CMP (0x23)
+#define MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_STRT (0x24)
+#define MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_CMP (0x25)
+#define MPI3_EVENT_DEV_STAT_RC_PCIE_HOT_RESET_FAILED (0x30)
+#define MPI3_EVENT_DEV_STAT_RC_EXPANDER_REDUCED_FUNC_STRT (0x40)
+#define MPI3_EVENT_DEV_STAT_RC_EXPANDER_REDUCED_FUNC_CMP (0x41)
+#define MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING (0x50)
+struct mpi3_event_data_energy_pack_change {
+ __le32 reserved00;
+ __le16 shutdown_timeout;
+ __le16 reserved06;
+};
+
+struct mpi3_event_data_sas_discovery {
+ u8 flags;
+ u8 reason_code;
+ u8 io_unit_port;
+ u8 reserved03;
+ __le32 discovery_status;
+};
+
+#define MPI3_EVENT_SAS_DISC_FLAGS_DEVICE_CHANGE (0x02)
+#define MPI3_EVENT_SAS_DISC_FLAGS_IN_PROGRESS (0x01)
+#define MPI3_EVENT_SAS_DISC_RC_STARTED (0x01)
+#define MPI3_EVENT_SAS_DISC_RC_COMPLETED (0x02)
+#define MPI3_SAS_DISC_STATUS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI3_SAS_DISC_STATUS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI3_SAS_DISC_STATUS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI3_SAS_DISC_STATUS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI3_SAS_DISC_STATUS_INVALID_CEI (0x00010000)
+#define MPI3_SAS_DISC_STATUS_FECEI_MISMATCH (0x00008000)
+#define MPI3_SAS_DISC_STATUS_MULTIPLE_DEVICES_IN_SLOT (0x00004000)
+#define MPI3_SAS_DISC_STATUS_NECEI_MISMATCH (0x00002000)
+#define MPI3_SAS_DISC_STATUS_TOO_MANY_SLOTS (0x00001000)
+#define MPI3_SAS_DISC_STATUS_EXP_MULTI_SUBTRACTIVE (0x00000800)
+#define MPI3_SAS_DISC_STATUS_MULTI_PORT_DOMAIN (0x00000400)
+#define MPI3_SAS_DISC_STATUS_TABLE_TO_SUBTRACTIVE_LINK (0x00000200)
+#define MPI3_SAS_DISC_STATUS_UNSUPPORTED_DEVICE (0x00000100)
+#define MPI3_SAS_DISC_STATUS_TABLE_LINK (0x00000080)
+#define MPI3_SAS_DISC_STATUS_SUBTRACTIVE_LINK (0x00000040)
+#define MPI3_SAS_DISC_STATUS_SMP_CRC_ERROR (0x00000020)
+#define MPI3_SAS_DISC_STATUS_SMP_FUNCTION_FAILED (0x00000010)
+#define MPI3_SAS_DISC_STATUS_SMP_TIMEOUT (0x00000008)
+#define MPI3_SAS_DISC_STATUS_MULTIPLE_PORTS (0x00000004)
+#define MPI3_SAS_DISC_STATUS_INVALID_SAS_ADDRESS (0x00000002)
+#define MPI3_SAS_DISC_STATUS_LOOP_DETECTED (0x00000001)
+struct mpi3_event_data_sas_broadcast_primitive {
+ u8 phy_num;
+ u8 io_unit_port;
+ u8 port_width;
+ u8 primitive;
+};
+
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE (0x01)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_SES (0x02)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_EXPANDER (0x03)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_RESERVED3 (0x05)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_RESERVED4 (0x06)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE0_RESERVED (0x07)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE1_RESERVED (0x08)
+struct mpi3_event_data_sas_notify_primitive {
+ u8 phy_num;
+ u8 io_unit_port;
+ u8 reserved02;
+ u8 primitive;
+};
+
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_ENABLE_SPINUP (0x01)
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_POWER_LOSS_EXPECTED (0x02)
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_RESERVED1 (0x03)
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_RESERVED2 (0x04)
+struct mpi3_event_sas_topo_phy_entry {
+ __le16 attached_dev_handle;
+ u8 link_rate;
+ u8 status;
+};
+
+#define MPI3_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xf0)
+#define MPI3_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4)
+#define MPI3_EVENT_SAS_TOPO_LR_PREV_MASK (0x0f)
+#define MPI3_EVENT_SAS_TOPO_LR_PREV_SHIFT (0)
+#define MPI3_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00)
+#define MPI3_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01)
+#define MPI3_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02)
+#define MPI3_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03)
+#define MPI3_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04)
+#define MPI3_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI3_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06)
+#define MPI3_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0a)
+#define MPI3_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0b)
+#define MPI3_EVENT_SAS_TOPO_LR_RATE_22_5 (0x0c)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_MASK (0xc0)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_SHIFT (6)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_ACCESSIBLE (0x00)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_NO_EXIST (0x40)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_VACANT (0x80)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_MASK (0x0f)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_SHIFT (0)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED (0x03)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE (0x04)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING (0x05)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING (0x06)
+struct mpi3_event_data_sas_topology_change_list {
+ __le16 enclosure_handle;
+ __le16 expander_dev_handle;
+ u8 num_phys;
+ u8 reserved05[3];
+ u8 num_entries;
+ u8 start_phy_num;
+ u8 exp_status;
+ u8 io_unit_port;
+ struct mpi3_event_sas_topo_phy_entry phy_entry[] __counted_by(num_entries);
+};
+
+#define MPI3_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00)
+#define MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
+#define MPI3_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_event_data_sas_phy_counter {
+ __le64 time_stamp;
+ __le32 reserved08;
+ u8 phy_event_code;
+ u8 phy_num;
+ __le16 reserved0e;
+ __le32 phy_event_info;
+ u8 counter_type;
+ u8 threshold_window;
+ u8 time_units;
+ u8 reserved17;
+ __le32 event_threshold;
+ __le16 threshold_flags;
+ __le16 reserved1e;
+};
+
+struct mpi3_event_data_sas_device_disc_err {
+ __le16 dev_handle;
+ u8 reason_code;
+ u8 io_unit_port;
+ __le32 reserved04;
+ __le64 sas_address;
+};
+
+#define MPI3_EVENT_SAS_DISC_ERR_RC_SMP_FAILED (0x01)
+#define MPI3_EVENT_SAS_DISC_ERR_RC_SMP_TIMEOUT (0x02)
+struct mpi3_event_data_pcie_enumeration {
+ u8 flags;
+ u8 reason_code;
+ u8 io_unit_port;
+ u8 reserved03;
+ __le32 enumeration_status;
+};
+
+#define MPI3_EVENT_PCIE_ENUM_FLAGS_DEVICE_CHANGE (0x02)
+#define MPI3_EVENT_PCIE_ENUM_FLAGS_IN_PROGRESS (0x01)
+#define MPI3_EVENT_PCIE_ENUM_RC_STARTED (0x01)
+#define MPI3_EVENT_PCIE_ENUM_RC_COMPLETED (0x02)
+#define MPI3_EVENT_PCIE_ENUM_ES_MAX_SWITCH_DEPTH_EXCEED (0x80000000)
+#define MPI3_EVENT_PCIE_ENUM_ES_MAX_SWITCHES_EXCEED (0x40000000)
+#define MPI3_EVENT_PCIE_ENUM_ES_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI3_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED (0x10000000)
+struct mpi3_event_pcie_topo_port_entry {
+ __le16 attached_dev_handle;
+ u8 port_status;
+ u8 reserved03;
+ u8 current_port_info;
+ u8 reserved05;
+ u8 previous_port_info;
+ u8 reserved07;
+};
+
+#define MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED (0x03)
+#define MPI3_EVENT_PCIE_TOPO_PS_NO_CHANGE (0x04)
+#define MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING (0x05)
+#define MPI3_EVENT_PCIE_TOPO_PS_RESPONDING (0x06)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_MASK (0xf0)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_SHIFT (4)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_UNKNOWN (0x00)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_1 (0x10)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_2 (0x20)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_4 (0x30)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_8 (0x40)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_16 (0x50)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK (0x0f)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_SHIFT (0)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_UNKNOWN (0x00)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_DISABLED (0x01)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_2_5 (0x02)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_5_0 (0x03)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_8_0 (0x04)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_16_0 (0x05)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_32_0 (0x06)
+struct mpi3_event_data_pcie_topology_change_list {
+ __le16 enclosure_handle;
+ __le16 switch_dev_handle;
+ u8 num_ports;
+ u8 reserved05[3];
+ u8 num_entries;
+ u8 start_port_num;
+ u8 switch_status;
+ u8 io_unit_port;
+ __le32 reserved0c;
+ struct mpi3_event_pcie_topo_port_entry port_entry[] __counted_by(num_entries);
+};
+
+#define MPI3_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH (0x00)
+#define MPI3_EVENT_PCIE_TOPO_SS_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_PCIE_TOPO_SS_RESPONDING (0x03)
+#define MPI3_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_event_data_pcie_error_threshold {
+ __le64 timestamp;
+ u8 reason_code;
+ u8 port;
+ __le16 switch_dev_handle;
+ u8 error;
+ u8 action;
+ __le16 threshold_count;
+ __le16 attached_dev_handle;
+ __le16 reserved12;
+ __le32 reserved14;
+};
+
+#define MPI3_EVENT_PCI_ERROR_RC_THRESHOLD_EXCEEDED (0x00)
+#define MPI3_EVENT_PCI_ERROR_RC_ESCALATION (0x01)
+struct mpi3_event_data_sas_init_dev_status_change {
+ u8 reason_code;
+ u8 io_unit_port;
+ __le16 dev_handle;
+ __le32 reserved04;
+ __le64 sas_address;
+};
+
+#define MPI3_EVENT_SAS_INIT_RC_ADDED (0x01)
+#define MPI3_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02)
+struct mpi3_event_data_sas_init_table_overflow {
+ __le16 max_init;
+ __le16 current_init;
+ __le32 reserved04;
+ __le64 sas_address;
+};
+
+struct mpi3_event_data_hard_reset_received {
+ u8 reserved00;
+ u8 io_unit_port;
+ __le16 reserved02;
+};
+
+struct mpi3_event_data_diag_buffer_status_change {
+ u8 type;
+ u8 reason_code;
+ __le16 reserved02;
+ __le32 reserved04;
+};
+
+#define MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_RELEASED (0x01)
+#define MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_PAUSED (0x02)
+#define MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_RESUMED (0x03)
+#define MPI3_PEL_LOCALE_FLAGS_NON_BLOCKING_BOOT_EVENT (0x0200)
+#define MPI3_PEL_LOCALE_FLAGS_BLOCKING_BOOT_EVENT (0x0100)
+#define MPI3_PEL_LOCALE_FLAGS_PCIE (0x0080)
+#define MPI3_PEL_LOCALE_FLAGS_CONFIGURATION (0x0040)
+#define MPI3_PEL_LOCALE_FLAGS_CONTROLER (0x0020)
+#define MPI3_PEL_LOCALE_FLAGS_SAS (0x0010)
+#define MPI3_PEL_LOCALE_FLAGS_EPACK (0x0008)
+#define MPI3_PEL_LOCALE_FLAGS_ENCLOSURE (0x0004)
+#define MPI3_PEL_LOCALE_FLAGS_PD (0x0002)
+#define MPI3_PEL_LOCALE_FLAGS_VD (0x0001)
+#define MPI3_PEL_CLASS_DEBUG (0x00)
+#define MPI3_PEL_CLASS_PROGRESS (0x01)
+#define MPI3_PEL_CLASS_INFORMATIONAL (0x02)
+#define MPI3_PEL_CLASS_WARNING (0x03)
+#define MPI3_PEL_CLASS_CRITICAL (0x04)
+#define MPI3_PEL_CLASS_FATAL (0x05)
+#define MPI3_PEL_CLASS_FAULT (0x06)
+#define MPI3_PEL_CLEARTYPE_CLEAR (0x00)
+#define MPI3_PEL_WAITTIME_INFINITE_WAIT (0x00)
+#define MPI3_PEL_ACTION_GET_SEQNUM (0x01)
+#define MPI3_PEL_ACTION_MARK_CLEAR (0x02)
+#define MPI3_PEL_ACTION_GET_LOG (0x03)
+#define MPI3_PEL_ACTION_GET_COUNT (0x04)
+#define MPI3_PEL_ACTION_WAIT (0x05)
+#define MPI3_PEL_ACTION_ABORT (0x06)
+#define MPI3_PEL_ACTION_GET_PRINT_STRINGS (0x07)
+#define MPI3_PEL_ACTION_ACKNOWLEDGE (0x08)
+#define MPI3_PEL_STATUS_SUCCESS (0x00)
+#define MPI3_PEL_STATUS_NOT_FOUND (0x01)
+#define MPI3_PEL_STATUS_ABORTED (0x02)
+#define MPI3_PEL_STATUS_NOT_READY (0x03)
+struct mpi3_pel_seq {
+ __le32 newest;
+ __le32 oldest;
+ __le32 clear;
+ __le32 shutdown;
+ __le32 boot;
+ __le32 last_acknowledged;
+};
+
+struct mpi3_pel_entry {
+ __le64 time_stamp;
+ __le32 sequence_number;
+ __le16 log_code;
+ __le16 arg_type;
+ __le16 locale;
+ u8 class;
+ u8 flags;
+ u8 ext_num;
+ u8 num_exts;
+ u8 arg_data_size;
+ u8 fixed_format_strings_size;
+ __le32 reserved18[2];
+ __le32 pel_info[24];
+};
+
+#define MPI3_PEL_FLAGS_COMPLETE_RESET_NEEDED (0x02)
+#define MPI3_PEL_FLAGS_ACK_NEEDED (0x01)
+struct mpi3_pel_list {
+ __le32 log_count;
+ __le32 reserved04;
+ struct mpi3_pel_entry entry[1];
+};
+
+struct mpi3_pel_arg_map {
+ u8 arg_type;
+ u8 length;
+ __le16 start_location;
+};
+
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_APPEND_STRING (0x00)
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_INTEGER (0x01)
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_STRING (0x02)
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_BIT_FIELD (0x03)
+struct mpi3_pel_print_string {
+ __le16 log_code;
+ __le16 string_length;
+ u8 num_arg_map;
+ u8 reserved05[3];
+ struct mpi3_pel_arg_map arg_map[1];
+};
+
+struct mpi3_pel_print_string_list {
+ __le32 num_print_strings;
+ __le32 residual_bytes_remain;
+ __le32 reserved08[2];
+ struct mpi3_pel_print_string print_string[1];
+};
+
+#ifndef MPI3_PEL_ACTION_SPECIFIC_MAX
+#define MPI3_PEL_ACTION_SPECIFIC_MAX (1)
+#endif
+struct mpi3_pel_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 action_specific[MPI3_PEL_ACTION_SPECIFIC_MAX];
+};
+
+struct mpi3_pel_req_action_get_sequence_numbers {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 reserved0c[5];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_clear_log_marker {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ u8 clear_type;
+ u8 reserved0d[3];
+};
+
+struct mpi3_pel_req_action_get_log {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 starting_sequence_number;
+ __le16 locale;
+ u8 class;
+ u8 reserved13;
+ __le32 reserved14[3];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_get_count {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 starting_sequence_number;
+ __le16 locale;
+ u8 class;
+ u8 reserved13;
+ __le32 reserved14[3];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_wait {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 starting_sequence_number;
+ __le16 locale;
+ u8 class;
+ u8 reserved13;
+ __le16 wait_time;
+ __le16 reserved16;
+ __le32 reserved18[2];
+};
+
+struct mpi3_pel_req_action_abort {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 reserved0c;
+ __le16 abort_host_tag;
+ __le16 reserved12;
+ __le32 reserved14;
+};
+
+struct mpi3_pel_req_action_get_print_strings {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 reserved0c;
+ __le16 start_log_code;
+ __le16 reserved12;
+ __le32 reserved14[3];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_acknowledge {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 sequence_number;
+ __le32 reserved10;
+};
+
+#define MPI3_PELACKNOWLEDGE_MSGFLAGS_SAFE_MODE_EXIT_MASK (0x03)
+#define MPI3_PELACKNOWLEDGE_MSGFLAGS_SAFE_MODE_EXIT_SHIFT (0)
+#define MPI3_PELACKNOWLEDGE_MSGFLAGS_SAFE_MODE_EXIT_NO_GUIDANCE (0x00)
+#define MPI3_PELACKNOWLEDGE_MSGFLAGS_SAFE_MODE_EXIT_CONTINUE_OP (0x01)
+#define MPI3_PELACKNOWLEDGE_MSGFLAGS_SAFE_MODE_EXIT_TRANSITION_TO_FAULT (0x02)
+struct mpi3_pel_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 action;
+ u8 reserved11;
+ __le16 reserved12;
+ __le16 pe_log_status;
+ __le16 reserved16;
+ __le32 transfer_length;
+};
+
+struct mpi3_ci_download_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 signature1;
+ __le32 total_image_size;
+ __le32 image_offset;
+ __le32 segment_size;
+ __le32 reserved1c;
+ union mpi3_sge_union sgl;
+};
+
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_LAST_SEGMENT (0x80)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_FORCE_FMC_ENABLE (0x40)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_SIGNED_NVDATA (0x20)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_MASK (0x03)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_SHIFT (0)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_FAST (0x00)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_MEDIUM (0x01)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_SLOW (0x02)
+#define MPI3_CI_DOWNLOAD_ACTION_DOWNLOAD (0x01)
+#define MPI3_CI_DOWNLOAD_ACTION_ONLINE_ACTIVATION (0x02)
+#define MPI3_CI_DOWNLOAD_ACTION_OFFLINE_ACTIVATION (0x03)
+#define MPI3_CI_DOWNLOAD_ACTION_GET_STATUS (0x04)
+#define MPI3_CI_DOWNLOAD_ACTION_CANCEL_OFFLINE_ACTIVATION (0x05)
+struct mpi3_ci_download_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 flags;
+ u8 cache_dirty;
+ u8 pending_count;
+ u8 reserved13;
+};
+
+#define MPI3_CI_DOWNLOAD_FLAGS_DOWNLOAD_IN_PROGRESS (0x80)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_FAILURE (0x40)
+#define MPI3_CI_DOWNLOAD_FLAGS_OFFLINE_ACTIVATION_REQUIRED (0x20)
+#define MPI3_CI_DOWNLOAD_FLAGS_KEY_UPDATE_PENDING (0x10)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_MASK (0x0e)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_SHIFT (1)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_NOT_NEEDED (0x00)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_AWAITING (0x02)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_ONLINE_PENDING (0x04)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_OFFLINE_PENDING (0x06)
+#define MPI3_CI_DOWNLOAD_FLAGS_COMPATIBLE (0x01)
+struct mpi3_ci_upload_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le32 signature1;
+ __le32 reserved10;
+ __le32 image_offset;
+ __le32 segment_size;
+ __le32 reserved1c;
+ union mpi3_sge_union sgl;
+};
+
+#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_MASK (0x01)
+#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_SHIFT (0)
+#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_PRIMARY (0x00)
+#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_SECONDARY (0x01)
+#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_MASK (0x02)
+#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_SHIFT (1)
+#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_FLASH (0x00)
+#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_EXECUTABLE (0x02)
+#define MPI3_CTRL_OP_FORCE_FULL_DISCOVERY (0x01)
+#define MPI3_CTRL_OP_LOOKUP_MAPPING (0x02)
+#define MPI3_CTRL_OP_UPDATE_TIMESTAMP (0x04)
+#define MPI3_CTRL_OP_GET_TIMESTAMP (0x05)
+#define MPI3_CTRL_OP_GET_IOC_CHANGE_COUNT (0x06)
+#define MPI3_CTRL_OP_CHANGE_PROFILE (0x07)
+#define MPI3_CTRL_OP_REMOVE_DEVICE (0x10)
+#define MPI3_CTRL_OP_CLOSE_PERSISTENT_CONNECTION (0x11)
+#define MPI3_CTRL_OP_HIDDEN_ACK (0x12)
+#define MPI3_CTRL_OP_CLEAR_DEVICE_COUNTERS (0x13)
+#define MPI3_CTRL_OP_SEND_SAS_PRIMITIVE (0x20)
+#define MPI3_CTRL_OP_SAS_PHY_CONTROL (0x21)
+#define MPI3_CTRL_OP_READ_INTERNAL_BUS (0x23)
+#define MPI3_CTRL_OP_WRITE_INTERNAL_BUS (0x24)
+#define MPI3_CTRL_OP_PCIE_LINK_CONTROL (0x30)
+#define MPI3_CTRL_OP_LOOKUP_MAPPING_PARAM8_LOOKUP_METHOD_INDEX (0x00)
+#define MPI3_CTRL_OP_UPDATE_TIMESTAMP_PARAM64_TIMESTAMP_INDEX (0x00)
+#define MPI3_CTRL_OP_CHANGE_PROFILE_PARAM8_PROFILE_ID_INDEX (0x00)
+#define MPI3_CTRL_OP_REMOVE_DEVICE_PARAM16_DEVHANDLE_INDEX (0x00)
+#define MPI3_CTRL_OP_CLOSE_PERSIST_CONN_PARAM16_DEVHANDLE_INDEX (0x00)
+#define MPI3_CTRL_OP_HIDDEN_ACK_PARAM16_DEVHANDLE_INDEX (0x00)
+#define MPI3_CTRL_OP_CLEAR_DEVICE_COUNTERS_PARAM16_DEVHANDLE_INDEX (0x00)
+#define MPI3_CTRL_OP_SEND_SAS_PRIM_PARAM8_PHY_INDEX (0x00)
+#define MPI3_CTRL_OP_SEND_SAS_PRIM_PARAM8_PRIMSEQ_INDEX (0x01)
+#define MPI3_CTRL_OP_SEND_SAS_PRIM_PARAM32_PRIMITIVE_INDEX (0x00)
+#define MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_ACTION_INDEX (0x00)
+#define MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_PHY_INDEX (0x01)
+#define MPI3_CTRL_OP_READ_INTERNAL_BUS_PARAM64_ADDRESS_INDEX (0x00)
+#define MPI3_CTRL_OP_WRITE_INTERNAL_BUS_PARAM64_ADDRESS_INDEX (0x00)
+#define MPI3_CTRL_OP_WRITE_INTERNAL_BUS_PARAM32_VALUE_INDEX (0x00)
+#define MPI3_CTRL_OP_PCIE_LINK_CONTROL_PARAM8_ACTION_INDEX (0x00)
+#define MPI3_CTRL_OP_PCIE_LINK_CONTROL_PARAM8_LINK_INDEX (0x01)
+#define MPI3_CTRL_LOOKUP_METHOD_WWID_ADDRESS (0x01)
+#define MPI3_CTRL_LOOKUP_METHOD_ENCLOSURE_SLOT (0x02)
+#define MPI3_CTRL_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03)
+#define MPI3_CTRL_LOOKUP_METHOD_PERSISTENT_ID (0x04)
+#define MPI3_CTRL_LOOKUP_METHOD_WWIDADDR_PARAM16_DEVH_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_WWIDADDR_PARAM64_WWID_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_ENCLSLOT_PARAM16_SLOTNUM_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_ENCLSLOT_PARAM64_ENCLOSURELID_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_SASDEVNAME_PARAM16_DEVH_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_SASDEVNAME_PARAM64_DEVNAME_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_PERSISTID_PARAM16_DEVH_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_PERSISTID_PARAM16_PERSISTENT_ID_INDEX (1)
+#define MPI3_CTRL_LOOKUP_METHOD_VALUE16_DEVH_INDEX (0)
+#define MPI3_CTRL_GET_TIMESTAMP_VALUE64_TIMESTAMP_INDEX (0)
+#define MPI3_CTRL_GET_IOC_CHANGE_COUNT_VALUE16_CHANGECOUNT_INDEX (0)
+#define MPI3_CTRL_READ_INTERNAL_BUS_VALUE32_VALUE_INDEX (0)
+#define MPI3_CTRL_PRIMFLAGS_SINGLE (0x01)
+#define MPI3_CTRL_PRIMFLAGS_TRIPLE (0x03)
+#define MPI3_CTRL_PRIMFLAGS_REDUNDANT (0x06)
+#define MPI3_CTRL_ACTION_NOP (0x00)
+#define MPI3_CTRL_ACTION_LINK_RESET (0x01)
+#define MPI3_CTRL_ACTION_HARD_RESET (0x02)
+#define MPI3_CTRL_ACTION_CLEAR_ERROR_LOG (0x05)
+struct mpi3_iounit_control_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 reserved0a;
+ u8 operation;
+ __le32 reserved0c;
+ __le64 param64[2];
+ __le32 param32[4];
+ __le16 param16[4];
+ u8 param8[8];
+};
+
+struct mpi3_iounit_control_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ __le64 value64[2];
+ __le32 value32[4];
+ __le16 value16[4];
+ u8 value8[8];
+};
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_pci.h b/drivers/scsi/mpi3mr/mpi/mpi30_pci.h
new file mode 100644
index 000000000000..4eeb11c3c73e
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_pci.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2023 Broadcom Inc. All rights reserved.
+ *
+ */
+#ifndef MPI30_PCI_H
+#define MPI30_PCI_H 1
+#ifndef MPI3_NVME_ENCAP_CMD_MAX
+#define MPI3_NVME_ENCAP_CMD_MAX (1)
+#endif
+#define MPI3_NVME_FLAGS_FORCE_ADMIN_ERR_REPLY_MASK (0x0002)
+#define MPI3_NVME_FLAGS_FORCE_ADMIN_ERR_REPLY_SHIFT (1)
+#define MPI3_NVME_FLAGS_FORCE_ADMIN_ERR_REPLY_FAIL_ONLY (0x0000)
+#define MPI3_NVME_FLAGS_FORCE_ADMIN_ERR_REPLY_ALL (0x0002)
+#define MPI3_NVME_FLAGS_SUBMISSIONQ_MASK (0x0001)
+#define MPI3_NVME_FLAGS_SUBMISSIONQ_SHIFT (0)
+#define MPI3_NVME_FLAGS_SUBMISSIONQ_IO (0x0000)
+#define MPI3_NVME_FLAGS_SUBMISSIONQ_ADMIN (0x0001)
+
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_sas.h b/drivers/scsi/mpi3mr/mpi/mpi30_sas.h
new file mode 100644
index 000000000000..190b06508b00
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_sas.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2023 Broadcom Inc. All rights reserved.
+ */
+#ifndef MPI30_SAS_H
+#define MPI30_SAS_H 1
+#define MPI3_SAS_DEVICE_INFO_SSP_TARGET (0x00000100)
+#define MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET (0x00000080)
+#define MPI3_SAS_DEVICE_INFO_SMP_TARGET (0x00000040)
+#define MPI3_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000020)
+#define MPI3_SAS_DEVICE_INFO_STP_INITIATOR (0x00000010)
+#define MPI3_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000008)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK (0x00000007)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_SHIFT (0)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_NO_DEVICE (0x00000000)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE (0x00000001)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER (0x00000002)
+struct mpi3_smp_passthrough_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 reserved0a;
+ u8 io_unit_port;
+ __le32 reserved0c[3];
+ __le64 sas_address;
+ struct mpi3_sge_common request_sge;
+ struct mpi3_sge_common response_sge;
+};
+
+struct mpi3_smp_passthrough_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ __le16 response_data_length;
+ __le16 reserved12;
+};
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_tool.h b/drivers/scsi/mpi3mr/mpi/mpi30_tool.h
new file mode 100644
index 000000000000..50a65b16a818
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_tool.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2024 Broadcom Inc. All rights reserved.
+ */
+#ifndef MPI30_TOOL_H
+#define MPI30_TOOL_H 1
+
+#define MPI3_DIAG_BUFFER_TYPE_TRACE (0x01)
+#define MPI3_DIAG_BUFFER_TYPE_FW (0x02)
+#define MPI3_DIAG_BUFFER_ACTION_RELEASE (0x01)
+
+#define MPI3_DIAG_BUFFER_POST_MSGFLAGS_SEGMENTED (0x01)
+struct mpi3_diag_buffer_post_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ u8 type;
+ u8 reserved0d;
+ __le16 reserved0e;
+ __le64 address;
+ __le32 length;
+ __le32 reserved1c;
+};
+
+struct mpi3_diag_buffer_manage_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ u8 type;
+ u8 action;
+ __le16 reserved0e;
+};
+
+
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h
new file mode 100644
index 000000000000..28ab2efb3baa
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h
@@ -0,0 +1,490 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2023 Broadcom Inc. All rights reserved.
+ */
+#ifndef MPI30_TRANSPORT_H
+#define MPI30_TRANSPORT_H 1
+struct mpi3_version_struct {
+ u8 dev;
+ u8 unit;
+ u8 minor;
+ u8 major;
+};
+
+union mpi3_version_union {
+ struct mpi3_version_struct mpi3_version;
+ __le32 word;
+};
+
+#define MPI3_VERSION_MAJOR (3)
+#define MPI3_VERSION_MINOR (0)
+#define MPI3_VERSION_UNIT (37)
+#define MPI3_VERSION_DEV (0)
+#define MPI3_DEVHANDLE_INVALID (0xffff)
+struct mpi3_sysif_oper_queue_indexes {
+ __le16 producer_index;
+ __le16 reserved02;
+ __le16 consumer_index;
+ __le16 reserved06;
+};
+
+struct mpi3_sysif_registers {
+ __le64 ioc_information;
+ union mpi3_version_union version;
+ __le32 reserved0c[2];
+ __le32 ioc_configuration;
+ __le32 reserved18;
+ __le32 ioc_status;
+ __le32 reserved20;
+ __le32 admin_queue_num_entries;
+ __le64 admin_request_queue_address;
+ __le64 admin_reply_queue_address;
+ __le32 reserved38[2];
+ __le32 coalesce_control;
+ __le32 reserved44[1007];
+ __le16 admin_request_queue_pi;
+ __le16 reserved1002;
+ __le16 admin_reply_queue_ci;
+ __le16 reserved1006;
+ struct mpi3_sysif_oper_queue_indexes oper_queue_indexes[383];
+ __le32 reserved1c00;
+ __le32 write_sequence;
+ __le32 host_diagnostic;
+ __le32 reserved1c0c;
+ __le32 fault;
+ __le32 fault_info[3];
+ __le32 reserved1c20[4];
+ __le64 hcb_address;
+ __le32 hcb_size;
+ __le32 reserved1c3c;
+ __le32 reply_free_host_index;
+ __le32 sense_buffer_free_host_index;
+ __le32 reserved1c48[2];
+ __le64 diag_rw_data;
+ __le64 diag_rw_address;
+ __le16 diag_rw_control;
+ __le16 diag_rw_status;
+ __le32 reserved1c64[35];
+ __le32 scratchpad[4];
+ __le32 reserved1d00[192];
+ __le32 device_assigned_registers[2048];
+};
+
+#define MPI3_SYSIF_IOC_INFO_LOW_OFFSET (0x00000000)
+#define MPI3_SYSIF_IOC_INFO_HIGH_OFFSET (0x00000004)
+#define MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_MASK (0xff000000)
+#define MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_SHIFT (24)
+#define MPI3_SYSIF_IOC_INFO_LOW_HCB_DISABLED (0x00000001)
+#define MPI3_SYSIF_IOC_CONFIG_OFFSET (0x00000014)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ (0x00f00000)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ_SHIFT (20)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ (0x000f0000)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ_SHIFT (16)
+#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_SHIFT (14)
+#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_MASK (0x0000c000)
+#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NO (0x00000000)
+#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL (0x00004000)
+#define MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ (0x00002000)
+#define MPI3_SYSIF_IOC_CONFIG_DIAG_SAVE (0x00000010)
+#define MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC (0x00000001)
+#define MPI3_SYSIF_IOC_STATUS_OFFSET (0x0000001c)
+#define MPI3_SYSIF_IOC_STATUS_RESET_HISTORY (0x00000010)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK (0x0000000c)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_SHIFT (0x00000002)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_NONE (0x00000000)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS (0x00000004)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_COMPLETE (0x00000008)
+#define MPI3_SYSIF_IOC_STATUS_FAULT (0x00000002)
+#define MPI3_SYSIF_IOC_STATUS_READY (0x00000001)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_OFFSET (0x00000024)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REQ_MASK (0x0fff)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REQ_SHIFT (0)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_OFFSET (0x00000026)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_MASK (0x0fff0000)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_SHIFT (16)
+#define MPI3_SYSIF_ADMIN_REQ_Q_ADDR_LOW_OFFSET (0x00000028)
+#define MPI3_SYSIF_ADMIN_REQ_Q_ADDR_HIGH_OFFSET (0x0000002c)
+#define MPI3_SYSIF_ADMIN_REPLY_Q_ADDR_LOW_OFFSET (0x00000030)
+#define MPI3_SYSIF_ADMIN_REPLY_Q_ADDR_HIGH_OFFSET (0x00000034)
+#define MPI3_SYSIF_COALESCE_CONTROL_OFFSET (0x00000040)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_MASK (0xc0000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_SHIFT (30)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_NO_CHANGE (0x00000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_DISABLE (0x40000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_ENABLE (0xc0000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_VALID (0x20000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_MSIX_IDX_MASK (0x01ff0000)
+#define MPI3_SYSIF_COALESCE_CONTROL_MSIX_IDX_SHIFT (16)
+#define MPI3_SYSIF_COALESCE_CONTROL_TIMEOUT_MASK (0x0000ff00)
+#define MPI3_SYSIF_COALESCE_CONTROL_TIMEOUT_SHIFT (8)
+#define MPI3_SYSIF_COALESCE_CONTROL_DEPTH_MASK (0x000000ff)
+#define MPI3_SYSIF_COALESCE_CONTROL_DEPTH_SHIFT (0)
+#define MPI3_SYSIF_ADMIN_REQ_Q_PI_OFFSET (0x00001000)
+#define MPI3_SYSIF_ADMIN_REPLY_Q_CI_OFFSET (0x00001004)
+#define MPI3_SYSIF_OPER_REQ_Q_PI_OFFSET (0x00001008)
+#define MPI3_SYSIF_OPER_REQ_Q_N_PI_OFFSET(N) (MPI3_SYSIF_OPER_REQ_Q_PI_OFFSET + (((N) - 1) * 8))
+#define MPI3_SYSIF_OPER_REPLY_Q_CI_OFFSET (0x0000100c)
+#define MPI3_SYSIF_OPER_REPLY_Q_N_CI_OFFSET(N) (MPI3_SYSIF_OPER_REPLY_Q_CI_OFFSET + (((N) - 1) * 8))
+#define MPI3_SYSIF_WRITE_SEQUENCE_OFFSET (0x00001c04)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_MASK (0x0000000f)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_SHIFT (0)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH (0x0)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST (0xf)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND (0x4)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD (0xb)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH (0x2)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH (0x7)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH (0xd)
+#define MPI3_SYSIF_HOST_DIAG_OFFSET (0x00001c08)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_MASK (0x00000700)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SHIFT (8)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_NO_RESET (0x00000000)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET (0x00000100)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_HOST_CONTROL_BOOT_RESET (0x00000200)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_COMPLETE_RESET (0x00000300)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT (0x00000700)
+#define MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS (0x00000080)
+#define MPI3_SYSIF_HOST_DIAG_SECURE_BOOT (0x00000040)
+#define MPI3_SYSIF_HOST_DIAG_CLEAR_INVALID_FW_IMAGE (0x00000020)
+#define MPI3_SYSIF_HOST_DIAG_INVALID_FW_IMAGE (0x00000010)
+#define MPI3_SYSIF_HOST_DIAG_HCBENABLE (0x00000008)
+#define MPI3_SYSIF_HOST_DIAG_HCBMODE (0x00000004)
+#define MPI3_SYSIF_HOST_DIAG_DIAG_RW_ENABLE (0x00000002)
+#define MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE (0x00000001)
+#define MPI3_SYSIF_FAULT_OFFSET (0x00001c10)
+#define MPI3_SYSIF_FAULT_FUNC_AREA_MASK (0xff000000)
+#define MPI3_SYSIF_FAULT_FUNC_AREA_SHIFT (24)
+#define MPI3_SYSIF_FAULT_FUNC_AREA_MPI_DEFINED (0x00000000)
+#define MPI3_SYSIF_FAULT_CODE_MASK (0x0000ffff)
+#define MPI3_SYSIF_FAULT_CODE_SHIFT (0)
+#define MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET (0x0000f000)
+#define MPI3_SYSIF_FAULT_CODE_CI_ACTIVATION_RESET (0x0000f001)
+#define MPI3_SYSIF_FAULT_CODE_SOFT_RESET_IN_PROGRESS (0x0000f002)
+#define MPI3_SYSIF_FAULT_CODE_COMPLETE_RESET_NEEDED (0x0000f003)
+#define MPI3_SYSIF_FAULT_CODE_SOFT_RESET_NEEDED (0x0000f004)
+#define MPI3_SYSIF_FAULT_CODE_POWER_CYCLE_REQUIRED (0x0000f005)
+#define MPI3_SYSIF_FAULT_CODE_TEMP_THRESHOLD_EXCEEDED (0x0000f006)
+#define MPI3_SYSIF_FAULT_CODE_INSUFFICIENT_PCI_SLOT_POWER (0x0000f007)
+#define MPI3_SYSIF_FAULT_INFO0_OFFSET (0x00001c14)
+#define MPI3_SYSIF_FAULT_INFO1_OFFSET (0x00001c18)
+#define MPI3_SYSIF_FAULT_INFO2_OFFSET (0x00001c1c)
+#define MPI3_SYSIF_HCB_ADDRESS_LOW_OFFSET (0x00001c30)
+#define MPI3_SYSIF_HCB_ADDRESS_HIGH_OFFSET (0x00001c34)
+#define MPI3_SYSIF_HCB_SIZE_OFFSET (0x00001c38)
+#define MPI3_SYSIF_HCB_SIZE_SIZE_MASK (0xfffff000)
+#define MPI3_SYSIF_HCB_SIZE_SIZE_SHIFT (12)
+#define MPI3_SYSIF_HCB_SIZE_HCDW_ENABLE (0x00000001)
+#define MPI3_SYSIF_REPLY_FREE_HOST_INDEX_OFFSET (0x00001c40)
+#define MPI3_SYSIF_SENSE_BUF_FREE_HOST_INDEX_OFFSET (0x00001c44)
+#define MPI3_SYSIF_DIAG_RW_DATA_LOW_OFFSET (0x00001c50)
+#define MPI3_SYSIF_DIAG_RW_DATA_HIGH_OFFSET (0x00001c54)
+#define MPI3_SYSIF_DIAG_RW_ADDRESS_LOW_OFFSET (0x00001c58)
+#define MPI3_SYSIF_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00001c5c)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_OFFSET (0x00001c60)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_MASK (0x00000030)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_SHIFT (4)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_1BYTE (0x00000000)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_2BYTES (0x00000010)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_4BYTES (0x00000020)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_8BYTES (0x00000030)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_RESET (0x00000004)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_MASK (0x00000002)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_SHIFT (1)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_READ (0x00000000)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_WRITE (0x00000002)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_START (0x00000001)
+#define MPI3_SYSIF_DIAG_RW_STATUS_OFFSET (0x00001c62)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_MASK (0x0000000e)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_SHIFT (1)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_SUCCESS (0x00000000)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_INV_ADDR (0x00000002)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_ACC_ERR (0x00000004)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_PAR_ERR (0x00000006)
+#define MPI3_SYSIF_DIAG_RW_STATUS_BUSY (0x00000001)
+#define MPI3_SYSIF_SCRATCHPAD0_OFFSET (0x00001cf0)
+#define MPI3_SYSIF_SCRATCHPAD1_OFFSET (0x00001cf4)
+#define MPI3_SYSIF_SCRATCHPAD2_OFFSET (0x00001cf8)
+#define MPI3_SYSIF_SCRATCHPAD3_OFFSET (0x00001cfc)
+#define MPI3_SYSIF_DEVICE_ASSIGNED_REGS_OFFSET (0x00002000)
+#define MPI3_SYSIF_DIAG_SAVE_TIMEOUT (60)
+struct mpi3_default_reply_descriptor {
+ __le32 descriptor_type_dependent1[2];
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 descriptor_type_dependent2;
+ __le16 reply_flags;
+};
+
+#define MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK (0x0001)
+#define MPI3_REPLY_DESCRIPT_FLAGS_PHASE_SHIFT (0)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK (0xf000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SHIFT (12)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY (0x0000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS (0x1000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_TARGET_COMMAND_BUFFER (0x2000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS (0x3000)
+#define MPI3_REPLY_DESCRIPT_REQUEST_QUEUE_ID_INVALID (0xffff)
+struct mpi3_address_reply_descriptor {
+ __le64 reply_frame_address;
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 reserved0c;
+ __le16 reply_flags;
+};
+
+struct mpi3_success_reply_descriptor {
+ __le32 reserved00[2];
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 host_tag;
+ __le16 reply_flags;
+};
+
+struct mpi3_target_command_buffer_reply_descriptor {
+ __le32 reserved00;
+ __le16 initiator_dev_handle;
+ u8 phy_num;
+ u8 reserved07;
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 io_index;
+ __le16 reply_flags;
+};
+
+struct mpi3_status_reply_descriptor {
+ __le16 ioc_status;
+ __le16 reserved02;
+ __le32 ioc_log_info;
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 host_tag;
+ __le16 reply_flags;
+};
+
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL (0x8000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK (0x7fff)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_MASK (0xf0000000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_NO_INFO (0x00000000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_SAS (0x30000000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_DATA_MASK (0x0fffffff)
+union mpi3_reply_descriptors_union {
+ struct mpi3_default_reply_descriptor default_reply;
+ struct mpi3_address_reply_descriptor address_reply;
+ struct mpi3_success_reply_descriptor success;
+ struct mpi3_target_command_buffer_reply_descriptor target_command_buffer;
+ struct mpi3_status_reply_descriptor status;
+ __le32 words[4];
+};
+
+struct mpi3_sge_common {
+ __le64 address;
+ __le32 length;
+ u8 reserved0c[3];
+ u8 flags;
+};
+
+struct mpi3_sge_bit_bucket {
+ __le64 reserved00;
+ __le32 length;
+ u8 reserved0c[3];
+ u8 flags;
+};
+
+struct mpi3_sge_extended_eedp {
+ u8 user_data_size;
+ u8 reserved01;
+ __le16 eedp_flags;
+ __le32 secondary_reference_tag;
+ __le16 secondary_application_tag;
+ __le16 application_tag_translation_mask;
+ __le16 reserved0c;
+ u8 extended_operation;
+ u8 flags;
+};
+
+union mpi3_sge_union {
+ struct mpi3_sge_common simple;
+ struct mpi3_sge_common chain;
+ struct mpi3_sge_common last_chain;
+ struct mpi3_sge_bit_bucket bit_bucket;
+ struct mpi3_sge_extended_eedp eedp;
+ __le32 words[4];
+};
+
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_MASK (0xf0)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_SHIFT (4)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE (0x00)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_BIT_BUCKET (0x10)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_CHAIN (0x20)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN (0x30)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED (0xf0)
+#define MPI3_SGE_FLAGS_END_OF_LIST (0x08)
+#define MPI3_SGE_FLAGS_END_OF_BUFFER (0x04)
+#define MPI3_SGE_FLAGS_DLAS_MASK (0x03)
+#define MPI3_SGE_FLAGS_DLAS_SHIFT (0)
+#define MPI3_SGE_FLAGS_DLAS_SYSTEM (0x00)
+#define MPI3_SGE_FLAGS_DLAS_IOC_UDP (0x01)
+#define MPI3_SGE_FLAGS_DLAS_IOC_CTL (0x02)
+#define MPI3_SGE_EXT_OPER_EEDP (0x00)
+#define MPI3_EEDPFLAGS_INCR_PRI_REF_TAG (0x8000)
+#define MPI3_EEDPFLAGS_INCR_SEC_REF_TAG (0x4000)
+#define MPI3_EEDPFLAGS_INCR_PRI_APP_TAG (0x2000)
+#define MPI3_EEDPFLAGS_INCR_SEC_APP_TAG (0x1000)
+#define MPI3_EEDPFLAGS_ESC_PASSTHROUGH (0x0800)
+#define MPI3_EEDPFLAGS_CHK_REF_TAG (0x0400)
+#define MPI3_EEDPFLAGS_CHK_APP_TAG (0x0200)
+#define MPI3_EEDPFLAGS_CHK_GUARD (0x0100)
+#define MPI3_EEDPFLAGS_ESC_MODE_MASK (0x00c0)
+#define MPI3_EEDPFLAGS_ESC_MODE_SHIFT (6)
+#define MPI3_EEDPFLAGS_ESC_MODE_DO_NOT_DISABLE (0x0040)
+#define MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE (0x0080)
+#define MPI3_EEDPFLAGS_ESC_MODE_APPTAG_REFTAG_DISABLE (0x00c0)
+#define MPI3_EEDPFLAGS_HOST_GUARD_MASK (0x0030)
+#define MPI3_EEDPFLAGS_HOST_GUARD_SHIFT (4)
+#define MPI3_EEDPFLAGS_HOST_GUARD_T10_CRC (0x0000)
+#define MPI3_EEDPFLAGS_HOST_GUARD_IP_CHKSUM (0x0010)
+#define MPI3_EEDPFLAGS_HOST_GUARD_OEM_SPECIFIC (0x0020)
+#define MPI3_EEDPFLAGS_PT_REF_TAG (0x0008)
+#define MPI3_EEDPFLAGS_EEDP_OP_MASK (0x0007)
+#define MPI3_EEDPFLAGS_EEDP_OP_SHIFT (0)
+#define MPI3_EEDPFLAGS_EEDP_OP_CHECK (0x0001)
+#define MPI3_EEDPFLAGS_EEDP_OP_STRIP (0x0002)
+#define MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE (0x0003)
+#define MPI3_EEDPFLAGS_EEDP_OP_INSERT (0x0004)
+#define MPI3_EEDPFLAGS_EEDP_OP_REPLACE (0x0006)
+#define MPI3_EEDPFLAGS_EEDP_OP_CHECK_REGEN (0x0007)
+#define MPI3_EEDP_UDS_512 (0x01)
+#define MPI3_EEDP_UDS_520 (0x02)
+#define MPI3_EEDP_UDS_4080 (0x03)
+#define MPI3_EEDP_UDS_4088 (0x04)
+#define MPI3_EEDP_UDS_4096 (0x05)
+#define MPI3_EEDP_UDS_4104 (0x06)
+#define MPI3_EEDP_UDS_4160 (0x07)
+struct mpi3_request_header {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 function_dependent;
+};
+
+struct mpi3_default_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+};
+
+#define MPI3_HOST_TAG_INVALID (0xffff)
+#define MPI3_FUNCTION_IOC_FACTS (0x01)
+#define MPI3_FUNCTION_IOC_INIT (0x02)
+#define MPI3_FUNCTION_PORT_ENABLE (0x03)
+#define MPI3_FUNCTION_EVENT_NOTIFICATION (0x04)
+#define MPI3_FUNCTION_EVENT_ACK (0x05)
+#define MPI3_FUNCTION_CI_DOWNLOAD (0x06)
+#define MPI3_FUNCTION_CI_UPLOAD (0x07)
+#define MPI3_FUNCTION_IO_UNIT_CONTROL (0x08)
+#define MPI3_FUNCTION_PERSISTENT_EVENT_LOG (0x09)
+#define MPI3_FUNCTION_MGMT_PASSTHROUGH (0x0a)
+#define MPI3_FUNCTION_CONFIG (0x10)
+#define MPI3_FUNCTION_SCSI_IO (0x20)
+#define MPI3_FUNCTION_SCSI_TASK_MGMT (0x21)
+#define MPI3_FUNCTION_SMP_PASSTHROUGH (0x22)
+#define MPI3_FUNCTION_NVME_ENCAPSULATED (0x24)
+#define MPI3_FUNCTION_TARGET_ASSIST (0x30)
+#define MPI3_FUNCTION_TARGET_STATUS_SEND (0x31)
+#define MPI3_FUNCTION_TARGET_MODE_ABORT (0x32)
+#define MPI3_FUNCTION_TARGET_CMD_BUF_POST_BASE (0x33)
+#define MPI3_FUNCTION_TARGET_CMD_BUF_POST_LIST (0x34)
+#define MPI3_FUNCTION_CREATE_REQUEST_QUEUE (0x70)
+#define MPI3_FUNCTION_DELETE_REQUEST_QUEUE (0x71)
+#define MPI3_FUNCTION_CREATE_REPLY_QUEUE (0x72)
+#define MPI3_FUNCTION_DELETE_REPLY_QUEUE (0x73)
+#define MPI3_FUNCTION_TOOLBOX (0x80)
+#define MPI3_FUNCTION_DIAG_BUFFER_POST (0x81)
+#define MPI3_FUNCTION_DIAG_BUFFER_MANAGE (0x82)
+#define MPI3_FUNCTION_DIAG_BUFFER_UPLOAD (0x83)
+#define MPI3_FUNCTION_MIN_IOC_USE_ONLY (0xc0)
+#define MPI3_FUNCTION_MAX_IOC_USE_ONLY (0xef)
+#define MPI3_FUNCTION_MIN_PRODUCT_SPECIFIC (0xf0)
+#define MPI3_FUNCTION_MAX_PRODUCT_SPECIFIC (0xff)
+#define MPI3_IOCSTATUS_LOG_INFO_AVAIL_MASK (0x8000)
+#define MPI3_IOCSTATUS_LOG_INFO_AVAILABLE (0x8000)
+#define MPI3_IOCSTATUS_STATUS_MASK (0x7fff)
+#define MPI3_IOCSTATUS_STATUS_SHIFT (0)
+#define MPI3_IOCSTATUS_SUCCESS (0x0000)
+#define MPI3_IOCSTATUS_INVALID_FUNCTION (0x0001)
+#define MPI3_IOCSTATUS_BUSY (0x0002)
+#define MPI3_IOCSTATUS_INVALID_SGL (0x0003)
+#define MPI3_IOCSTATUS_INTERNAL_ERROR (0x0004)
+#define MPI3_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006)
+#define MPI3_IOCSTATUS_INVALID_FIELD (0x0007)
+#define MPI3_IOCSTATUS_INVALID_STATE (0x0008)
+#define MPI3_IOCSTATUS_SHUTDOWN_ACTIVE (0x0009)
+#define MPI3_IOCSTATUS_INSUFFICIENT_POWER (0x000a)
+#define MPI3_IOCSTATUS_INVALID_CHANGE_COUNT (0x000b)
+#define MPI3_IOCSTATUS_ALLOWED_CMD_BLOCK (0x000c)
+#define MPI3_IOCSTATUS_SUPERVISOR_ONLY (0x000d)
+#define MPI3_IOCSTATUS_FAILURE (0x001f)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_DATA (0x0023)
+#define MPI3_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024)
+#define MPI3_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025)
+#define MPI3_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040)
+#define MPI3_IOCSTATUS_SCSI_TM_NOT_SUPPORTED (0x0041)
+#define MPI3_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042)
+#define MPI3_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043)
+#define MPI3_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044)
+#define MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045)
+#define MPI3_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046)
+#define MPI3_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047)
+#define MPI3_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048)
+#define MPI3_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049)
+#define MPI3_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004a)
+#define MPI3_IOCSTATUS_SCSI_IOC_TERMINATED (0x004b)
+#define MPI3_IOCSTATUS_SCSI_EXT_TERMINATED (0x004c)
+#define MPI3_IOCSTATUS_EEDP_GUARD_ERROR (0x004d)
+#define MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004e)
+#define MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004f)
+#define MPI3_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062)
+#define MPI3_IOCSTATUS_TARGET_ABORTED (0x0063)
+#define MPI3_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064)
+#define MPI3_IOCSTATUS_TARGET_NO_CONNECTION (0x0065)
+#define MPI3_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006a)
+#define MPI3_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006d)
+#define MPI3_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006e)
+#define MPI3_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006f)
+#define MPI3_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070)
+#define MPI3_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071)
+#define MPI3_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090)
+#define MPI3_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091)
+#define MPI3_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00a0)
+#define MPI3_IOCSTATUS_CI_UNSUPPORTED (0x00b0)
+#define MPI3_IOCSTATUS_CI_UPDATE_SEQUENCE (0x00b1)
+#define MPI3_IOCSTATUS_CI_VALIDATION_FAILED (0x00b2)
+#define MPI3_IOCSTATUS_CI_KEY_UPDATE_PENDING (0x00b3)
+#define MPI3_IOCSTATUS_CI_KEY_UPDATE_NOT_POSSIBLE (0x00b4)
+#define MPI3_IOCSTATUS_SECURITY_KEY_REQUIRED (0x00c0)
+#define MPI3_IOCSTATUS_SECURITY_VIOLATION (0x00c1)
+#define MPI3_IOCSTATUS_INVALID_QUEUE_ID (0x0f00)
+#define MPI3_IOCSTATUS_INVALID_QUEUE_SIZE (0x0f01)
+#define MPI3_IOCSTATUS_INVALID_MSIX_VECTOR (0x0f02)
+#define MPI3_IOCSTATUS_INVALID_REPLY_QUEUE_ID (0x0f03)
+#define MPI3_IOCSTATUS_INVALID_QUEUE_DELETION (0x0f04)
+#define MPI3_IOCLOGINFO_TYPE_MASK (0xf0000000)
+#define MPI3_IOCLOGINFO_TYPE_SHIFT (28)
+#define MPI3_IOCLOGINFO_TYPE_NONE (0x0)
+#define MPI3_IOCLOGINFO_TYPE_SAS (0x3)
+#define MPI3_IOCLOGINFO_LOG_DATA_MASK (0x0fffffff)
+#define MPI3_IOCLOGINFO_LOG_DATA_SHIFT (0)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
new file mode 100644
index 000000000000..6742684e2990
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -0,0 +1,1605 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2023 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#ifndef MPI3MR_H_INCLUDED
+#define MPI3MR_H_INCLUDED
+
+#include <linux/blkdev.h>
+#include <linux/blk-mq.h>
+#include <linux/delay.h>
+#include <linux/dmapool.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/aer.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/workqueue.h>
+#include <linux/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <uapi/scsi/scsi_bsg_mpi3mr.h>
+#include <scsi/scsi_transport_sas.h>
+
+#include "mpi/mpi30_transport.h"
+#include "mpi/mpi30_cnfg.h"
+#include "mpi/mpi30_image.h"
+#include "mpi/mpi30_init.h"
+#include "mpi/mpi30_ioc.h"
+#include "mpi/mpi30_sas.h"
+#include "mpi/mpi30_pci.h"
+#include "mpi/mpi30_tool.h"
+#include "mpi3mr_debug.h"
+
+/* Global list and lock for storing multiple adapters managed by the driver */
+extern spinlock_t mrioc_list_lock;
+extern struct list_head mrioc_list;
+extern int prot_mask;
+extern atomic64_t event_counter;
+
+#define MPI3MR_DRIVER_VERSION "8.15.0.5.50"
+#define MPI3MR_DRIVER_RELDATE "12-August-2025"
+
+#define MPI3MR_DRIVER_NAME "mpi3mr"
+#define MPI3MR_DRIVER_LICENSE "GPL"
+#define MPI3MR_DRIVER_AUTHOR "Broadcom Inc. <mpi3mr-linuxdrv.pdl@broadcom.com>"
+#define MPI3MR_DRIVER_DESC "MPI3 Storage Controller Device Driver"
+
+#define MPI3MR_NAME_LENGTH 64
+#define IOCNAME "%s: "
+
+#define MPI3MR_DEFAULT_MAX_IO_SIZE (1 * 1024 * 1024)
+
+/* Definitions for internal SGL and Chain SGL buffers */
+#define MPI3MR_PAGE_SIZE_4K 4096
+#define MPI3MR_DEFAULT_SGL_ENTRIES 256
+#define MPI3MR_MAX_SGL_ENTRIES 2048
+
+/* Definitions for MAX values for shost */
+#define MPI3MR_MAX_CMDS_LUN 128
+#define MPI3MR_MAX_CDB_LENGTH 32
+
+/* Admin queue management definitions */
+#define MPI3MR_ADMIN_REQ_Q_SIZE (2 * MPI3MR_PAGE_SIZE_4K)
+#define MPI3MR_ADMIN_REPLY_Q_SIZE (8 * MPI3MR_PAGE_SIZE_4K)
+#define MPI3MR_ADMIN_REQ_FRAME_SZ 128
+#define MPI3MR_ADMIN_REPLY_FRAME_SZ 16
+
+/* Operational queue management definitions */
+#define MPI3MR_OP_REQ_Q_QD 512
+#define MPI3MR_OP_REP_Q_QD 1024
+#define MPI3MR_OP_REP_Q_QD2K 2048
+#define MPI3MR_OP_REP_Q_QD4K 4096
+#define MPI3MR_OP_REQ_Q_SEG_SIZE 4096
+#define MPI3MR_OP_REP_Q_SEG_SIZE 4096
+#define MPI3MR_MAX_SEG_LIST_SIZE 4096
+
+/* Reserved Host Tag definitions */
+#define MPI3MR_HOSTTAG_INVALID 0xFFFF
+#define MPI3MR_HOSTTAG_INITCMDS 1
+#define MPI3MR_HOSTTAG_BSG_CMDS 2
+#define MPI3MR_HOSTTAG_PEL_ABORT 3
+#define MPI3MR_HOSTTAG_PEL_WAIT 4
+#define MPI3MR_HOSTTAG_BLK_TMS 5
+#define MPI3MR_HOSTTAG_CFG_CMDS 6
+#define MPI3MR_HOSTTAG_TRANSPORT_CMDS 7
+
+#define MPI3MR_NUM_DEVRMCMD 16
+#define MPI3MR_HOSTTAG_DEVRMCMD_MIN (MPI3MR_HOSTTAG_TRANSPORT_CMDS + 1)
+#define MPI3MR_HOSTTAG_DEVRMCMD_MAX (MPI3MR_HOSTTAG_DEVRMCMD_MIN + \
+ MPI3MR_NUM_DEVRMCMD - 1)
+
+#define MPI3MR_INTERNAL_CMDS_RESVD MPI3MR_HOSTTAG_DEVRMCMD_MAX
+#define MPI3MR_NUM_EVTACKCMD 4
+#define MPI3MR_HOSTTAG_EVTACKCMD_MIN (MPI3MR_HOSTTAG_DEVRMCMD_MAX + 1)
+#define MPI3MR_HOSTTAG_EVTACKCMD_MAX (MPI3MR_HOSTTAG_EVTACKCMD_MIN + \
+ MPI3MR_NUM_EVTACKCMD - 1)
+
+/* Reduced resource count definition for crash kernel */
+#define MPI3MR_HOST_IOS_KDUMP 128
+
+/* command/controller interaction timeout definitions in seconds */
+#define MPI3MR_INTADMCMD_TIMEOUT 60
+#define MPI3MR_PORTENABLE_TIMEOUT 300
+#define MPI3MR_PORTENABLE_POLL_INTERVAL 5
+#define MPI3MR_ABORTTM_TIMEOUT 60
+#define MPI3MR_RESETTM_TIMEOUT 60
+#define MPI3MR_RESET_HOST_IOWAIT_TIMEOUT 5
+#define MPI3MR_TSUPDATE_INTERVAL 900
+#define MPI3MR_DEFAULT_SHUTDOWN_TIME 120
+#define MPI3MR_RAID_ERRREC_RESET_TIMEOUT 180
+#define MPI3MR_PREPARE_FOR_RESET_TIMEOUT 180
+#define MPI3MR_RESET_ACK_TIMEOUT 30
+#define MPI3MR_MUR_TIMEOUT 120
+#define MPI3MR_RESET_TIMEOUT 510
+
+#define MPI3MR_WATCHDOG_INTERVAL 1000 /* in milli seconds */
+
+#define MPI3MR_RESET_TOPOLOGY_SETTLE_TIME 10
+
+#define MPI3MR_SCMD_TIMEOUT (60 * HZ)
+#define MPI3MR_EH_SCMD_TIMEOUT (60 * HZ)
+
+/* Internal admin command state definitions*/
+#define MPI3MR_CMD_NOTUSED 0x8000
+#define MPI3MR_CMD_COMPLETE 0x0001
+#define MPI3MR_CMD_PENDING 0x0002
+#define MPI3MR_CMD_REPLY_VALID 0x0004
+#define MPI3MR_CMD_RESET 0x0008
+
+/* Definitions for Event replies and sense buffer allocated per controller */
+#define MPI3MR_NUM_EVT_REPLIES 64
+#define MPI3MR_SENSE_BUF_SZ 256
+#define MPI3MR_SENSEBUF_FACTOR 3
+#define MPI3MR_CHAINBUF_FACTOR 3
+#define MPI3MR_CHAINBUFDIX_FACTOR 2
+
+/* Invalid target device handle */
+#define MPI3MR_INVALID_DEV_HANDLE 0xFFFF
+
+/* Controller Reset related definitions */
+#define MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT 5
+#define MPI3MR_MAX_RESET_RETRY_COUNT 3
+
+/* ResponseCode definitions */
+#define MPI3MR_RI_MASK_RESPCODE (0x000000FF)
+#define MPI3MR_RSP_IO_QUEUED_ON_IOC \
+ MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC
+
+#define MPI3MR_DEFAULT_MDTS (128 * 1024)
+#define MPI3MR_DEFAULT_PGSZEXP (12)
+
+/* Command retry count definitions */
+#define MPI3MR_DEV_RMHS_RETRY_COUNT 3
+#define MPI3MR_PEL_RETRY_COUNT 3
+
+/* Default target device queue depth */
+#define MPI3MR_DEFAULT_SDEV_QD 32
+
+/* Definitions for Threaded IRQ poll*/
+#define MPI3MR_IRQ_POLL_SLEEP 20
+#define MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT 8
+
+/* Definitions for the controller security status*/
+#define MPI3MR_CTLR_SECURITY_STATUS_MASK 0x0C
+#define MPI3MR_CTLR_SECURE_DBG_STATUS_MASK 0x02
+
+#define MPI3MR_INVALID_DEVICE 0x00
+#define MPI3MR_CONFIG_SECURE_DEVICE 0x04
+#define MPI3MR_HARD_SECURE_DEVICE 0x08
+#define MPI3MR_TAMPERED_DEVICE 0x0C
+
+#define MPI3MR_DEFAULT_HDB_MAX_SZ (4 * 1024 * 1024)
+#define MPI3MR_DEFAULT_HDB_DEC_SZ (1 * 1024 * 1024)
+#define MPI3MR_DEFAULT_HDB_MIN_SZ (2 * 1024 * 1024)
+#define MPI3MR_MAX_NUM_HDB 2
+
+#define MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN 0
+#define MPI3MR_HDB_TRIGGER_TYPE_FAULT 1
+#define MPI3MR_HDB_TRIGGER_TYPE_ELEMENT 2
+#define MPI3MR_HDB_TRIGGER_TYPE_GLOBAL 3
+#define MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET 4
+#define MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED 5
+
+#define MPI3MR_HDB_REFRESH_TYPE_RESERVED 0
+#define MPI3MR_HDB_REFRESH_TYPE_CURRENT 1
+#define MPI3MR_HDB_REFRESH_TYPE_DEFAULT 2
+#define MPI3MR_HDB_HDB_REFRESH_TYPE_PERSISTENT 3
+
+#define MPI3MR_DEFAULT_HDB_SZ (4 * 1024 * 1024)
+#define MPI3MR_MAX_NUM_HDB 2
+
+#define MPI3MR_HDB_QUERY_ELEMENT_TRIGGER_FORMAT_INDEX 0
+#define MPI3MR_HDB_QUERY_ELEMENT_TRIGGER_FORMAT_DATA 1
+
+#define MPI3MR_THRESHOLD_REPLY_COUNT 100
+
+/* SGE Flag definition */
+#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \
+ (MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \
+ MPI3_SGE_FLAGS_END_OF_LIST)
+
+/* MSI Index from Reply Queue Index */
+#define REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, offset) (qidx + offset)
+
+/*
+ * Maximum data transfer size definitions for management
+ * application commands
+ */
+#define MPI3MR_MAX_APP_XFER_SIZE (1 * 1024 * 1024)
+#define MPI3MR_MAX_APP_XFER_SEGMENTS 512
+/*
+ * 2048 sectors are for data buffers and additional 512 sectors for
+ * other buffers
+ */
+#define MPI3MR_MAX_APP_XFER_SECTORS (2048 + 512)
+
+#define MPI3MR_WRITE_SAME_MAX_LEN_256_BLKS 256
+#define MPI3MR_WRITE_SAME_MAX_LEN_2048_BLKS 2048
+
+#define MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER (0xFFFD)
+
+/**
+ * struct mpi3mr_nvme_pt_sge - Structure to store SGEs for NVMe
+ * Encapsulated commands.
+ *
+ * @base_addr: Physical address
+ * @length: SGE length
+ * @rsvd: Reserved
+ * @rsvd1: Reserved
+ * @sub_type: sgl sub type
+ * @type: sgl type
+ */
+struct mpi3mr_nvme_pt_sge {
+ __le64 base_addr;
+ __le32 length;
+ u16 rsvd;
+ u8 rsvd1;
+ u8 sub_type:4;
+ u8 type:4;
+};
+
+/**
+ * struct mpi3mr_buf_map - local structure to
+ * track kernel and user buffers associated with an BSG
+ * structure.
+ *
+ * @bsg_buf: BSG buffer virtual address
+ * @bsg_buf_len: BSG buffer length
+ * @kern_buf: Kernel buffer virtual address
+ * @kern_buf_len: Kernel buffer length
+ * @kern_buf_dma: Kernel buffer DMA address
+ * @data_dir: Data direction.
+ */
+struct mpi3mr_buf_map {
+ void *bsg_buf;
+ u32 bsg_buf_len;
+ void *kern_buf;
+ u32 kern_buf_len;
+ dma_addr_t kern_buf_dma;
+ u8 data_dir;
+ u16 num_dma_desc;
+ struct dma_memory_desc *dma_desc;
+};
+
+/* IOC State definitions */
+enum mpi3mr_iocstate {
+ MRIOC_STATE_READY = 1,
+ MRIOC_STATE_RESET,
+ MRIOC_STATE_FAULT,
+ MRIOC_STATE_BECOMING_READY,
+ MRIOC_STATE_RESET_REQUESTED,
+ MRIOC_STATE_UNRECOVERABLE,
+};
+
+/* Reset reason code definitions*/
+enum mpi3mr_reset_reason {
+ MPI3MR_RESET_FROM_BRINGUP = 1,
+ MPI3MR_RESET_FROM_FAULT_WATCH = 2,
+ MPI3MR_RESET_FROM_APP = 3,
+ MPI3MR_RESET_FROM_EH_HOS = 4,
+ MPI3MR_RESET_FROM_TM_TIMEOUT = 5,
+ MPI3MR_RESET_FROM_APP_TIMEOUT = 6,
+ MPI3MR_RESET_FROM_MUR_FAILURE = 7,
+ MPI3MR_RESET_FROM_CTLR_CLEANUP = 8,
+ MPI3MR_RESET_FROM_CIACTIV_FAULT = 9,
+ MPI3MR_RESET_FROM_PE_TIMEOUT = 10,
+ MPI3MR_RESET_FROM_TSU_TIMEOUT = 11,
+ MPI3MR_RESET_FROM_DELREQQ_TIMEOUT = 12,
+ MPI3MR_RESET_FROM_DELREPQ_TIMEOUT = 13,
+ MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT = 14,
+ MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT = 15,
+ MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT = 16,
+ MPI3MR_RESET_FROM_IOCINIT_TIMEOUT = 17,
+ MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT = 18,
+ MPI3MR_RESET_FROM_EVTACK_TIMEOUT = 19,
+ MPI3MR_RESET_FROM_CIACTVRST_TIMER = 20,
+ MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT = 21,
+ MPI3MR_RESET_FROM_PELABORT_TIMEOUT = 22,
+ MPI3MR_RESET_FROM_SYSFS = 23,
+ MPI3MR_RESET_FROM_SYSFS_TIMEOUT = 24,
+ MPI3MR_RESET_FROM_DIAG_BUFFER_POST_TIMEOUT = 25,
+ MPI3MR_RESET_FROM_DIAG_BUFFER_RELEASE_TIMEOUT = 26,
+ MPI3MR_RESET_FROM_FIRMWARE = 27,
+ MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT = 29,
+ MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT = 30,
+ MPI3MR_RESET_FROM_TRIGGER = 31,
+};
+
+#define MPI3MR_RESET_REASON_OSTYPE_LINUX 1
+#define MPI3MR_RESET_REASON_OSTYPE_SHIFT 28
+#define MPI3MR_RESET_REASON_IOCNUM_SHIFT 20
+
+
+/* Queue type definitions */
+enum queue_type {
+ MPI3MR_DEFAULT_QUEUE = 0,
+ MPI3MR_POLL_QUEUE,
+};
+
+/**
+ * struct mpi3mr_compimg_ver - replica of component image
+ * version defined in mpi30_image.h in host endianness
+ *
+ */
+struct mpi3mr_compimg_ver {
+ u16 build_num;
+ u16 cust_id;
+ u8 ph_minor;
+ u8 ph_major;
+ u8 gen_minor;
+ u8 gen_major;
+};
+
+/**
+ * struct mpi3mr_ioc_facs - replica of component image version
+ * defined in mpi30_ioc.h in host endianness
+ *
+ */
+struct mpi3mr_ioc_facts {
+ u32 ioc_capabilities;
+ struct mpi3mr_compimg_ver fw_ver;
+ u32 mpi_version;
+ u32 diag_trace_sz;
+ u32 diag_fw_sz;
+ u32 diag_drvr_sz;
+ u16 max_reqs;
+ u16 product_id;
+ u16 op_req_sz;
+ u16 reply_sz;
+ u16 exceptions;
+ u16 max_perids;
+ u16 max_pds;
+ u16 max_sasexpanders;
+ u32 max_data_length;
+ u16 max_sasinitiators;
+ u16 max_enclosures;
+ u16 max_pcie_switches;
+ u16 max_nvme;
+ u16 max_vds;
+ u16 max_hpds;
+ u16 max_advhpds;
+ u16 max_raid_pds;
+ u16 min_devhandle;
+ u16 max_devhandle;
+ u16 max_op_req_q;
+ u16 max_op_reply_q;
+ u16 shutdown_timeout;
+ u8 ioc_num;
+ u8 who_init;
+ u16 max_msix_vectors;
+ u8 personality;
+ u8 dma_mask;
+ bool max_req_limit;
+ u8 protocol_flags;
+ u8 sge_mod_mask;
+ u8 sge_mod_value;
+ u8 sge_mod_shift;
+ u8 max_dev_per_tg;
+ u16 max_io_throttle_group;
+ u16 io_throttle_data_length;
+ u16 io_throttle_low;
+ u16 io_throttle_high;
+
+};
+
+/**
+ * struct segments - memory descriptor structure to store
+ * virtual and dma addresses for operational queue segments.
+ *
+ * @segment: virtual address
+ * @segment_dma: dma address
+ */
+struct segments {
+ void *segment;
+ dma_addr_t segment_dma;
+};
+
+/**
+ * struct op_req_qinfo - Operational Request Queue Information
+ *
+ * @ci: consumer index
+ * @pi: producer index
+ * @num_request: Maximum number of entries in the queue
+ * @qid: Queue Id starting from 1
+ * @reply_qid: Associated reply queue Id
+ * @num_segments: Number of discontiguous memory segments
+ * @segment_qd: Depth of each segments
+ * @q_lock: Concurrent queue access lock
+ * @q_segments: Segment descriptor pointer
+ * @q_segment_list: Segment list base virtual address
+ * @q_segment_list_dma: Segment list base DMA address
+ */
+struct op_req_qinfo {
+ u16 ci;
+ u16 pi;
+ u16 num_requests;
+ u16 qid;
+ u16 reply_qid;
+ u16 num_segments;
+ u16 segment_qd;
+ spinlock_t q_lock;
+ struct segments *q_segments;
+ void *q_segment_list;
+ dma_addr_t q_segment_list_dma;
+};
+
+/**
+ * struct op_reply_qinfo - Operational Reply Queue Information
+ *
+ * @ci: consumer index
+ * @qid: Queue Id starting from 1
+ * @num_replies: Maximum number of entries in the queue
+ * @num_segments: Number of discontiguous memory segments
+ * @segment_qd: Depth of each segments
+ * @q_segments: Segment descriptor pointer
+ * @q_segment_list: Segment list base virtual address
+ * @q_segment_list_dma: Segment list base DMA address
+ * @ephase: Expected phased identifier for the reply queue
+ * @pend_ios: Number of IOs pending in HW for this queue
+ * @enable_irq_poll: Flag to indicate polling is enabled
+ * @in_use: Queue is handled by poll/ISR
+ * @qtype: Type of queue (types defined in enum queue_type)
+ * @qfull_watermark: Watermark defined in reply queue to avoid
+ * reply queue full
+ */
+struct op_reply_qinfo {
+ u16 ci;
+ u16 qid;
+ u16 num_replies;
+ u16 num_segments;
+ u16 segment_qd;
+ struct segments *q_segments;
+ void *q_segment_list;
+ dma_addr_t q_segment_list_dma;
+ u8 ephase;
+ atomic_t pend_ios;
+ bool enable_irq_poll;
+ atomic_t in_use;
+ enum queue_type qtype;
+ u16 qfull_watermark;
+};
+
+/**
+ * struct mpi3mr_intr_info - Interrupt cookie information
+ *
+ * @mrioc: Adapter instance reference
+ * @os_irq: irq number
+ * @msix_index: MSIx index
+ * @op_reply_q: Associated operational reply queue
+ * @name: Dev name for the irq claiming device
+ */
+struct mpi3mr_intr_info {
+ struct mpi3mr_ioc *mrioc;
+ int os_irq;
+ u16 msix_index;
+ struct op_reply_qinfo *op_reply_q;
+ char name[MPI3MR_NAME_LENGTH];
+};
+
+/**
+ * struct mpi3mr_throttle_group_info - Throttle group info
+ *
+ * @io_divert: Flag indicates io divert is on or off for the TG
+ * @need_qd_reduction: Flag to indicate QD reduction is needed
+ * @qd_reduction: Queue Depth reduction in units of 10%
+ * @fw_qd: QueueDepth value reported by the firmware
+ * @modified_qd: Modified QueueDepth value due to throttling
+ * @id: Throttle Group ID.
+ * @high: High limit to turn on throttling in 512 byte blocks
+ * @low: Low limit to turn off throttling in 512 byte blocks
+ * @pend_large_data_sz: Counter to track pending large data
+ */
+struct mpi3mr_throttle_group_info {
+ u8 io_divert;
+ u8 need_qd_reduction;
+ u8 qd_reduction;
+ u16 fw_qd;
+ u16 modified_qd;
+ u16 id;
+ u32 high;
+ u32 low;
+ atomic_t pend_large_data_sz;
+};
+
+/* HBA port flags */
+#define MPI3MR_HBA_PORT_FLAG_DIRTY 0x01
+#define MPI3MR_HBA_PORT_FLAG_NEW 0x02
+
+/* IOCTL data transfer sge*/
+#define MPI3MR_NUM_IOCTL_SGE 256
+#define MPI3MR_IOCTL_SGE_SIZE (8 * 1024)
+
+/**
+ * struct mpi3mr_hba_port - HBA's port information
+ * @port_id: Port number
+ * @flags: HBA port flags
+ */
+struct mpi3mr_hba_port {
+ struct list_head list;
+ u8 port_id;
+ u8 flags;
+};
+
+/**
+ * struct mpi3mr_sas_port - Internal SAS port information
+ * @port_list: List of ports belonging to a SAS node
+ * @num_phys: Number of phys associated with port
+ * @marked_responding: used while refresing the sas ports
+ * @lowest_phy: lowest phy ID of current sas port, valid for controller port
+ * @phy_mask: phy_mask of current sas port, valid for controller port
+ * @hba_port: HBA port entry
+ * @remote_identify: Attached device identification
+ * @rphy: SAS transport layer rphy object
+ * @port: SAS transport layer port object
+ * @phy_list: mpi3mr_sas_phy objects belonging to this port
+ */
+struct mpi3mr_sas_port {
+ struct list_head port_list;
+ u8 num_phys;
+ u8 marked_responding;
+ int lowest_phy;
+ u64 phy_mask;
+ struct mpi3mr_hba_port *hba_port;
+ struct sas_identify remote_identify;
+ struct sas_rphy *rphy;
+ struct sas_port *port;
+ struct list_head phy_list;
+};
+
+/**
+ * struct mpi3mr_sas_phy - Internal SAS Phy information
+ * @port_siblings: List of phys belonging to a port
+ * @identify: Phy identification
+ * @remote_identify: Attached device identification
+ * @phy: SAS transport layer Phy object
+ * @phy_id: Unique phy id within a port
+ * @handle: Firmware device handle for this phy
+ * @attached_handle: Firmware device handle for attached device
+ * @phy_belongs_to_port: Flag to indicate phy belongs to port
+ @hba_port: HBA port entry
+ */
+struct mpi3mr_sas_phy {
+ struct list_head port_siblings;
+ struct sas_identify identify;
+ struct sas_identify remote_identify;
+ struct sas_phy *phy;
+ u8 phy_id;
+ u16 handle;
+ u16 attached_handle;
+ u8 phy_belongs_to_port;
+ struct mpi3mr_hba_port *hba_port;
+};
+
+/**
+ * struct mpi3mr_sas_node - SAS host/expander information
+ * @list: List of sas nodes in a controller
+ * @parent_dev: Parent device class
+ * @num_phys: Number phys belonging to sas_node
+ * @sas_address: SAS address of sas_node
+ * @handle: Firmware device handle for this sas_host/expander
+ * @sas_address_parent: SAS address of parent expander or host
+ * @enclosure_handle: Firmware handle of enclosure of this node
+ * @device_info: Capabilities of this sas_host/expander
+ * @non_responding: used to refresh the expander devices during reset
+ * @host_node: Flag to indicate this is a host_node
+ * @hba_port: HBA port entry
+ * @phy: A list of phys that make up this sas_host/expander
+ * @sas_port_list: List of internal ports of this node
+ * @rphy: sas_rphy object of this expander node
+ */
+struct mpi3mr_sas_node {
+ struct list_head list;
+ struct device *parent_dev;
+ u8 num_phys;
+ u64 sas_address;
+ u16 handle;
+ u64 sas_address_parent;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u8 non_responding;
+ u8 host_node;
+ struct mpi3mr_hba_port *hba_port;
+ struct mpi3mr_sas_phy *phy;
+ struct list_head sas_port_list;
+ struct sas_rphy *rphy;
+};
+
+/**
+ * struct mpi3mr_enclosure_node - enclosure information
+ * @list: List of enclosures
+ * @pg0: Enclosure page 0;
+ */
+struct mpi3mr_enclosure_node {
+ struct list_head list;
+ struct mpi3_enclosure_page0 pg0;
+};
+
+/**
+ * struct tgt_dev_sas_sata - SAS/SATA device specific
+ * information cached from firmware given data
+ *
+ * @sas_address: World wide unique SAS address
+ * @sas_address_parent: Sas address of parent expander or host
+ * @dev_info: Device information bits
+ * @phy_id: Phy identifier provided in device page 0
+ * @attached_phy_id: Attached phy identifier provided in device page 0
+ * @sas_transport_attached: Is this device exposed to transport
+ * @pend_sas_rphy_add: Flag to check device is in process of add
+ * @hba_port: HBA port entry
+ * @rphy: SAS transport layer rphy object
+ */
+struct tgt_dev_sas_sata {
+ u64 sas_address;
+ u64 sas_address_parent;
+ u16 dev_info;
+ u8 phy_id;
+ u8 attached_phy_id;
+ u8 sas_transport_attached;
+ u8 pend_sas_rphy_add;
+ struct mpi3mr_hba_port *hba_port;
+ struct sas_rphy *rphy;
+};
+
+/**
+ * struct tgt_dev_pcie - PCIe device specific information cached
+ * from firmware given data
+ *
+ * @mdts: Maximum data transfer size
+ * @capb: Device capabilities
+ * @pgsz: Device page size
+ * @abort_to: Timeout for abort TM
+ * @reset_to: Timeout for Target/LUN reset TM
+ * @dev_info: Device information bits
+ */
+struct tgt_dev_pcie {
+ u32 mdts;
+ u16 capb;
+ u8 pgsz;
+ u8 abort_to;
+ u8 reset_to;
+ u16 dev_info;
+};
+
+/**
+ * struct tgt_dev_vd - virtual device specific information
+ * cached from firmware given data
+ *
+ * @state: State of the VD
+ * @tg_qd_reduction: Queue Depth reduction in units of 10%
+ * @tg_id: VDs throttle group ID
+ * @high: High limit to turn on throttling in 512 byte blocks
+ * @low: Low limit to turn off throttling in 512 byte blocks
+ * @tg: Pointer to throttle group info
+ */
+struct tgt_dev_vd {
+ u8 state;
+ u8 tg_qd_reduction;
+ u16 tg_id;
+ u32 tg_high;
+ u32 tg_low;
+ u8 abort_to;
+ u8 reset_to;
+ struct mpi3mr_throttle_group_info *tg;
+};
+
+
+/**
+ * union _form_spec_inf - union of device specific information
+ */
+union _form_spec_inf {
+ struct tgt_dev_sas_sata sas_sata_inf;
+ struct tgt_dev_pcie pcie_inf;
+ struct tgt_dev_vd vd_inf;
+};
+
+enum mpi3mr_dev_state {
+ MPI3MR_DEV_CREATED = 1,
+ MPI3MR_DEV_REMOVE_HS_STARTED = 2,
+ MPI3MR_DEV_DELETED = 3,
+};
+
+/**
+ * struct mpi3mr_tgt_dev - target device data structure
+ *
+ * @list: List pointer
+ * @starget: Scsi_target pointer
+ * @dev_handle: FW device handle
+ * @parent_handle: FW parent device handle
+ * @slot: Slot number
+ * @encl_handle: FW enclosure handle
+ * @perst_id: FW assigned Persistent ID
+ * @devpg0_flag: Device Page0 flag
+ * @dev_type: SAS/SATA/PCIE device type
+ * @is_hidden: Should be exposed to upper layers or not
+ * @host_exposed: Already exposed to host or not
+ * @io_unit_port: IO Unit port ID
+ * @non_stl: Is this device not to be attached with SAS TL
+ * @io_throttle_enabled: I/O throttling needed or not
+ * @wslen: Write same max length
+ * @q_depth: Device specific Queue Depth
+ * @wwid: World wide ID
+ * @enclosure_logical_id: Enclosure logical identifier
+ * @dev_spec: Device type specific information
+ * @abort_to: Timeout for abort TM
+ * @reset_to: Timeout for Target/LUN reset TM
+ * @ref_count: Reference count
+ * @state: device state
+ */
+struct mpi3mr_tgt_dev {
+ struct list_head list;
+ struct scsi_target *starget;
+ u16 dev_handle;
+ u16 parent_handle;
+ u16 slot;
+ u16 encl_handle;
+ u16 perst_id;
+ u16 devpg0_flag;
+ u8 dev_type;
+ u8 is_hidden;
+ u8 host_exposed;
+ u8 io_unit_port;
+ u8 non_stl;
+ u8 io_throttle_enabled;
+ u16 wslen;
+ u16 q_depth;
+ u64 wwid;
+ u64 enclosure_logical_id;
+ union _form_spec_inf dev_spec;
+ struct kref ref_count;
+ enum mpi3mr_dev_state state;
+};
+
+/**
+ * mpi3mr_tgtdev_get - k reference incrementor
+ * @s: Target device reference
+ *
+ * Increment target device reference count.
+ */
+static inline void mpi3mr_tgtdev_get(struct mpi3mr_tgt_dev *s)
+{
+ kref_get(&s->ref_count);
+}
+
+/**
+ * mpi3mr_free_tgtdev - target device memory dealloctor
+ * @r: k reference pointer of the target device
+ *
+ * Free target device memory when no reference.
+ */
+static inline void mpi3mr_free_tgtdev(struct kref *r)
+{
+ kfree(container_of(r, struct mpi3mr_tgt_dev, ref_count));
+}
+
+/**
+ * mpi3mr_tgtdev_put - k reference decrementor
+ * @s: Target device reference
+ *
+ * Decrement target device reference count.
+ */
+static inline void mpi3mr_tgtdev_put(struct mpi3mr_tgt_dev *s)
+{
+ kref_put(&s->ref_count, mpi3mr_free_tgtdev);
+}
+
+
+/**
+ * struct mpi3mr_stgt_priv_data - SCSI target private structure
+ *
+ * @starget: Scsi_target pointer
+ * @dev_handle: FW device handle
+ * @perst_id: FW assigned Persistent ID
+ * @num_luns: Number of Logical Units
+ * @block_io: I/O blocked to the device or not
+ * @dev_removed: Device removed in the Firmware
+ * @dev_removedelay: Device is waiting to be removed in FW
+ * @dev_type: Device type
+ * @dev_nvme_dif: Device is NVMe DIF enabled
+ * @wslen: Write same max length
+ * @io_throttle_enabled: I/O throttling needed or not
+ * @io_divert: Flag indicates io divert is on or off for the dev
+ * @throttle_group: Pointer to throttle group info
+ * @tgt_dev: Internal target device pointer
+ * @pend_count: Counter to track pending I/Os during error
+ * handling
+ */
+struct mpi3mr_stgt_priv_data {
+ struct scsi_target *starget;
+ u16 dev_handle;
+ u16 perst_id;
+ u32 num_luns;
+ atomic_t block_io;
+ u8 dev_removed;
+ u8 dev_removedelay;
+ u8 dev_type;
+ u8 dev_nvme_dif;
+ u16 wslen;
+ u8 io_throttle_enabled;
+ u8 io_divert;
+ struct mpi3mr_throttle_group_info *throttle_group;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ u32 pend_count;
+};
+
+/**
+ * struct mpi3mr_stgt_priv_data - SCSI device private structure
+ *
+ * @tgt_priv_data: Scsi_target private data pointer
+ * @lun_id: LUN ID of the device
+ * @ncq_prio_enable: NCQ priority enable for SATA device
+ * @pend_count: Counter to track pending I/Os during error
+ * handling
+ * @wslen: Write same max length
+ */
+struct mpi3mr_sdev_priv_data {
+ struct mpi3mr_stgt_priv_data *tgt_priv_data;
+ u32 lun_id;
+ u8 ncq_prio_enable;
+ u32 pend_count;
+ u16 wslen;
+};
+
+/**
+ * struct mpi3mr_drv_cmd - Internal command tracker
+ *
+ * @mutex: Command mutex
+ * @done: Completeor for wakeup
+ * @reply: Firmware reply for internal commands
+ * @sensebuf: Sensebuf for SCSI IO commands
+ * @iou_rc: IO Unit control reason code
+ * @state: Command State
+ * @dev_handle: Firmware handle for device specific commands
+ * @ioc_status: IOC status from the firmware
+ * @ioc_loginfo:IOC log info from the firmware
+ * @is_waiting: Is the command issued in block mode
+ * @is_sense: Is Sense data present
+ * @retry_count: Retry count for retriable commands
+ * @host_tag: Host tag used by the command
+ * @callback: Callback for non blocking commands
+ */
+struct mpi3mr_drv_cmd {
+ struct mutex mutex;
+ struct completion done;
+ void *reply;
+ u8 *sensebuf;
+ u8 iou_rc;
+ u16 state;
+ u16 dev_handle;
+ u16 ioc_status;
+ u32 ioc_loginfo;
+ u8 is_waiting;
+ u8 is_sense;
+ u8 retry_count;
+ u16 host_tag;
+
+ void (*callback)(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd);
+};
+
+/**
+ * union mpi3mr_trigger_data - Trigger data information
+ * @fault: Fault code
+ * @global: Global trigger data
+ * @element: element trigger data
+ */
+union mpi3mr_trigger_data {
+ u16 fault;
+ u64 global;
+ union mpi3_driver2_trigger_element element;
+};
+
+/**
+ * struct trigger_event_data - store trigger related
+ * information.
+ *
+ * @trace_hdb: Trace diag buffer descriptor reference
+ * @fw_hdb: FW diag buffer descriptor reference
+ * @trigger_type: Trigger type
+ * @trigger_specific_data: Trigger specific data
+ * @snapdump: Snapdump enable or disable flag
+ */
+struct trigger_event_data {
+ struct diag_buffer_desc *trace_hdb;
+ struct diag_buffer_desc *fw_hdb;
+ u8 trigger_type;
+ union mpi3mr_trigger_data trigger_specific_data;
+ bool snapdump;
+};
+
+/**
+ * struct diag_buffer_desc - memory descriptor structure to
+ * store virtual, dma addresses, size, buffer status for host
+ * diagnostic buffers.
+ *
+ * @type: Buffer type
+ * @trigger_data: Trigger data
+ * @trigger_type: Trigger type
+ * @status: Buffer status
+ * @size: Buffer size
+ * @addr: Virtual address
+ * @dma_addr: Buffer DMA address
+ * @is_segmented: The buffer is segmented or not
+ * @disabled_after_reset: The buffer is disabled after reset
+ */
+struct diag_buffer_desc {
+ u8 type;
+ union mpi3mr_trigger_data trigger_data;
+ u8 trigger_type;
+ u8 status;
+ u32 size;
+ void *addr;
+ dma_addr_t dma_addr;
+ bool is_segmented;
+ bool disabled_after_reset;
+};
+
+/**
+ * struct dma_memory_desc - memory descriptor structure to store
+ * virtual address, dma address and size for any generic dma
+ * memory allocations in the driver.
+ *
+ * @size: buffer size
+ * @addr: virtual address
+ * @dma_addr: dma address
+ */
+struct dma_memory_desc {
+ u32 size;
+ void *addr;
+ dma_addr_t dma_addr;
+};
+
+
+/**
+ * struct chain_element - memory descriptor structure to store
+ * virtual and dma addresses for chain elements.
+ *
+ * @addr: virtual address
+ * @dma_addr: dma address
+ */
+struct chain_element {
+ void *addr;
+ dma_addr_t dma_addr;
+};
+
+/**
+ * struct scmd_priv - SCSI command private data
+ *
+ * @host_tag: Host tag specific to operational queue
+ * @in_lld_scope: Command in LLD scope or not
+ * @meta_sg_valid: DIX command with meta data SGL or not
+ * @scmd: SCSI Command pointer
+ * @req_q_idx: Operational request queue index
+ * @chain_idx: Chain frame index
+ * @meta_chain_idx: Chain frame index of meta data SGL
+ * @mpi3mr_scsiio_req: MPI SCSI IO request
+ */
+struct scmd_priv {
+ u16 host_tag;
+ u8 in_lld_scope;
+ u8 meta_sg_valid;
+ struct scsi_cmnd *scmd;
+ u16 req_q_idx;
+ int chain_idx;
+ int meta_chain_idx;
+ u8 mpi3mr_scsiio_req[MPI3MR_ADMIN_REQ_FRAME_SZ];
+};
+
+/**
+ * struct mpi3mr_ioc - Adapter anchor structure stored in shost
+ * private data
+ *
+ * @list: List pointer
+ * @pdev: PCI device pointer
+ * @shost: Scsi_Host pointer
+ * @id: Controller ID
+ * @cpu_count: Number of online CPUs
+ * @irqpoll_sleep: usleep unit used in threaded isr irqpoll
+ * @name: Controller ASCII name
+ * @driver_name: Driver ASCII name
+ * @sysif_regs: System interface registers virtual address
+ * @sysif_regs_phys: System interface registers physical address
+ * @bars: PCI BARS
+ * @dma_mask: DMA mask
+ * @msix_count: Number of MSIX vectors used
+ * @intr_enabled: Is interrupts enabled
+ * @num_admin_req: Number of admin requests
+ * @admin_req_q_sz: Admin request queue size
+ * @admin_req_pi: Admin request queue producer index
+ * @admin_req_ci: Admin request queue consumer index
+ * @admin_req_base: Admin request queue base virtual address
+ * @admin_req_dma: Admin request queue base dma address
+ * @admin_req_lock: Admin queue access lock
+ * @num_admin_replies: Number of admin replies
+ * @admin_reply_q_sz: Admin reply queue size
+ * @admin_reply_ci: Admin reply queue consumer index
+ * @admin_reply_ephase:Admin reply queue expected phase
+ * @admin_reply_base: Admin reply queue base virtual address
+ * @admin_reply_dma: Admin reply queue base dma address
+ * @admin_reply_q_in_use: Queue is handled by poll/ISR
+ * @admin_pend_isr: Count of unprocessed admin ISR/poll calls
+ * due to another thread processing replies
+ * @ready_timeout: Controller ready timeout
+ * @intr_info: Interrupt cookie pointer
+ * @intr_info_count: Number of interrupt cookies
+ * @is_intr_info_set: Flag to indicate intr info is setup
+ * @num_queues: Number of operational queues
+ * @num_op_req_q: Number of operational request queues
+ * @req_qinfo: Operational request queue info pointer
+ * @num_op_reply_q: Number of operational reply queues
+ * @op_reply_qinfo: Operational reply queue info pointer
+ * @init_cmds: Command tracker for initialization commands
+ * @cfg_cmds: Command tracker for configuration requests
+ * @facts: Cached IOC facts data
+ * @op_reply_desc_sz: Operational reply descriptor size
+ * @num_reply_bufs: Number of reply buffers allocated
+ * @reply_buf_pool: Reply buffer pool
+ * @reply_buf: Reply buffer base virtual address
+ * @reply_buf_dma: Reply buffer DMA address
+ * @reply_buf_dma_max_address: Reply DMA address max limit
+ * @reply_free_qsz: Reply free queue size
+ * @reply_free_q_pool: Reply free queue pool
+ * @reply_free_q: Reply free queue base virtual address
+ * @reply_free_q_dma: Reply free queue base DMA address
+ * @reply_free_queue_lock: Reply free queue lock
+ * @reply_free_queue_host_index: Reply free queue host index
+ * @num_sense_bufs: Number of sense buffers
+ * @sense_buf_pool: Sense buffer pool
+ * @sense_buf: Sense buffer base virtual address
+ * @sense_buf_dma: Sense buffer base DMA address
+ * @sense_buf_q_sz: Sense buffer queue size
+ * @sense_buf_q_pool: Sense buffer queue pool
+ * @sense_buf_q: Sense buffer queue virtual address
+ * @sense_buf_q_dma: Sense buffer queue DMA address
+ * @sbq_lock: Sense buffer queue lock
+ * @sbq_host_index: Sense buffer queuehost index
+ * @event_masks: Event mask bitmap
+ * @fwevt_worker_thread: Firmware event worker thread
+ * @fwevt_lock: Firmware event lock
+ * @fwevt_list: Firmware event list
+ * @watchdog_work_q_name: Fault watchdog worker thread name
+ * @watchdog_work_q: Fault watchdog worker thread
+ * @watchdog_work: Fault watchdog work
+ * @watchdog_lock: Fault watchdog lock
+ * @is_driver_loading: Is driver still loading
+ * @scan_started: Async scan started
+ * @scan_failed: Asycn scan failed
+ * @stop_drv_processing: Stop all command processing
+ * @device_refresh_on: Don't process the events until devices are refreshed
+ * @max_host_ios: Maximum host I/O count
+ * @max_sgl_entries: Max SGL entries per I/O
+ * @chain_buf_count: Chain buffer count
+ * @chain_buf_pool: Chain buffer pool
+ * @chain_sgl_list: Chain SGL list
+ * @chain_bitmap: Chain buffer allocator bitmap
+ * @chain_buf_lock: Chain buffer list lock
+ * @bsg_cmds: Command tracker for BSG command
+ * @host_tm_cmds: Command tracker for task management commands
+ * @dev_rmhs_cmds: Command tracker for device removal commands
+ * @evtack_cmds: Command tracker for event ack commands
+ * @devrem_bitmap: Device removal bitmap
+ * @dev_handle_bitmap_bits: Number of bits in device handle bitmap
+ * @removepend_bitmap: Remove pending bitmap
+ * @delayed_rmhs_list: Delayed device removal list
+ * @evtack_cmds_bitmap: Event Ack bitmap
+ * @delayed_evtack_cmds_list: Delayed event acknowledgment list
+ * @ts_update_counter: Timestamp update counter
+ * @ts_update_interval: Timestamp update interval
+ * @reset_in_progress: Reset in progress flag
+ * @unrecoverable: Controller unrecoverable flag
+ * @io_admin_reset_sync: Manage state of I/O ops during an admin reset process
+ * @prev_reset_result: Result of previous reset
+ * @reset_mutex: Controller reset mutex
+ * @reset_waitq: Controller reset wait queue
+ * @prepare_for_reset: Prepare for reset event received
+ * @prepare_for_reset_timeout_counter: Prepare for reset timeout
+ * @prp_list_virt: NVMe encapsulated PRP list virtual base
+ * @prp_list_dma: NVMe encapsulated PRP list DMA
+ * @prp_sz: NVME encapsulated PRP list size
+ * @diagsave_timeout: Diagnostic information save timeout
+ * @logging_level: Controller debug logging level
+ * @flush_io_count: I/O count to flush after reset
+ * @current_event: Firmware event currently in process
+ * @driver_info: Driver, Kernel, OS information to firmware
+ * @change_count: Topology change count
+ * @pel_enabled: Persistent Event Log(PEL) enabled or not
+ * @pel_abort_requested: PEL abort is requested or not
+ * @pel_class: PEL Class identifier
+ * @pel_locale: PEL Locale identifier
+ * @pel_cmds: Command tracker for PEL wait command
+ * @pel_abort_cmd: Command tracker for PEL abort command
+ * @pel_newest_seqnum: Newest PEL sequenece number
+ * @pel_seqnum_virt: PEL sequence number virtual address
+ * @pel_seqnum_dma: PEL sequence number DMA address
+ * @pel_seqnum_sz: PEL sequenece number size
+ * @op_reply_q_offset: Operational reply queue offset with MSIx
+ * @default_qcount: Total Default queues
+ * @active_poll_qcount: Currently active poll queue count
+ * @requested_poll_qcount: User requested poll queue count
+ * @bsg_dev: BSG device structure
+ * @bsg_queue: Request queue for BSG device
+ * @stop_bsgs: Stop BSG request flag
+ * @logdata_buf: Circular buffer to store log data entries
+ * @logdata_buf_idx: Index of entry in buffer to store
+ * @logdata_entry_sz: log data entry size
+ * @adm_req_q_bar_writeq_lock: Admin request queue lock
+ * @adm_reply_q_bar_writeq_lock: Admin reply queue lock
+ * @pend_large_data_sz: Counter to track pending large data
+ * @io_throttle_data_length: I/O size to track in 512b blocks
+ * @io_throttle_high: I/O size to start throttle in 512b blocks
+ * @io_throttle_low: I/O size to stop throttle in 512b blocks
+ * @num_io_throttle_group: Maximum number of throttle groups
+ * @throttle_groups: Pointer to throttle group info structures
+ * @sas_transport_enabled: SAS transport enabled or not
+ * @scsi_device_channel: Channel ID for SCSI devices
+ * @transport_cmds: Command tracker for SAS transport commands
+ * @sas_hba: SAS node for the controller
+ * @sas_expander_list: SAS node list of expanders
+ * @sas_node_lock: Lock to protect SAS node list
+ * @hba_port_table_list: List of HBA Ports
+ * @enclosure_list: List of Enclosure objects
+ * @diag_buffers: Host diagnostic buffers
+ * @driver_pg2: Driver page 2 pointer
+ * @reply_trigger_present: Reply trigger present flag
+ * @event_trigger_present: Event trigger present flag
+ * @scsisense_trigger_present: Scsi sense trigger present flag
+ * @ioctl_dma_pool: DMA pool for IOCTL data buffers
+ * @ioctl_sge: DMA buffer descriptors for IOCTL data
+ * @ioctl_chain_sge: DMA buffer descriptor for IOCTL chain
+ * @ioctl_resp_sge: DMA buffer descriptor for Mgmt cmd response
+ * @ioctl_sges_allocated: Flag for IOCTL SGEs allocated or not
+ * @trace_release_trigger_active: Trace trigger active flag
+ * @fw_release_trigger_active: Fw release trigger active flag
+ * @snapdump_trigger_active: Snapdump trigger active flag
+ * @pci_err_recovery: PCI error recovery in progress
+ * @block_on_pci_err: Block IO during PCI error recovery
+ * @reply_qfull_count: Occurences of reply queue full avoidance kicking-in
+ * @prevent_reply_qfull: Enable reply queue prevention
+ * @seg_tb_support: Segmented trace buffer support
+ * @num_tb_segs: Number of Segments in Trace buffer
+ * @trace_buf_pool: DMA pool for Segmented trace buffer segments
+ * @trace_buf: Trace buffer segments memory descriptor
+ */
+struct mpi3mr_ioc {
+ struct list_head list;
+ struct pci_dev *pdev;
+ struct Scsi_Host *shost;
+ u8 id;
+ int cpu_count;
+ bool enable_segqueue;
+ u32 irqpoll_sleep;
+
+ char name[MPI3MR_NAME_LENGTH];
+ char driver_name[MPI3MR_NAME_LENGTH];
+
+ struct mpi3_sysif_registers __iomem *sysif_regs;
+ resource_size_t sysif_regs_phys;
+ int bars;
+ u64 dma_mask;
+
+ u16 msix_count;
+ u8 intr_enabled;
+
+ u16 num_admin_req;
+ u32 admin_req_q_sz;
+ u16 admin_req_pi;
+ u16 admin_req_ci;
+ void *admin_req_base;
+ dma_addr_t admin_req_dma;
+ spinlock_t admin_req_lock;
+
+ u16 num_admin_replies;
+ u32 admin_reply_q_sz;
+ u16 admin_reply_ci;
+ u8 admin_reply_ephase;
+ void *admin_reply_base;
+ dma_addr_t admin_reply_dma;
+ atomic_t admin_reply_q_in_use;
+ atomic_t admin_pend_isr;
+
+ u32 ready_timeout;
+
+ struct mpi3mr_intr_info *intr_info;
+ u16 intr_info_count;
+ bool is_intr_info_set;
+
+ u16 num_queues;
+ u16 num_op_req_q;
+ struct op_req_qinfo *req_qinfo;
+
+ u16 num_op_reply_q;
+ struct op_reply_qinfo *op_reply_qinfo;
+
+ struct mpi3mr_drv_cmd init_cmds;
+ struct mpi3mr_drv_cmd cfg_cmds;
+ struct mpi3mr_ioc_facts facts;
+ u16 op_reply_desc_sz;
+
+ u32 num_reply_bufs;
+ struct dma_pool *reply_buf_pool;
+ u8 *reply_buf;
+ dma_addr_t reply_buf_dma;
+ dma_addr_t reply_buf_dma_max_address;
+
+ u16 reply_free_qsz;
+ u16 reply_sz;
+ struct dma_pool *reply_free_q_pool;
+ __le64 *reply_free_q;
+ dma_addr_t reply_free_q_dma;
+ spinlock_t reply_free_queue_lock;
+ u32 reply_free_queue_host_index;
+
+ u32 num_sense_bufs;
+ struct dma_pool *sense_buf_pool;
+ u8 *sense_buf;
+ dma_addr_t sense_buf_dma;
+
+ u16 sense_buf_q_sz;
+ struct dma_pool *sense_buf_q_pool;
+ __le64 *sense_buf_q;
+ dma_addr_t sense_buf_q_dma;
+ spinlock_t sbq_lock;
+ u32 sbq_host_index;
+ u32 event_masks[MPI3_EVENT_NOTIFY_EVENTMASK_WORDS];
+
+ struct workqueue_struct *fwevt_worker_thread;
+ spinlock_t fwevt_lock;
+ struct list_head fwevt_list;
+
+ char watchdog_work_q_name[50];
+ struct workqueue_struct *watchdog_work_q;
+ struct delayed_work watchdog_work;
+ spinlock_t watchdog_lock;
+
+ u8 is_driver_loading;
+ u8 scan_started;
+ u16 scan_failed;
+ u8 stop_drv_processing;
+ u8 device_refresh_on;
+
+ u16 max_host_ios;
+ spinlock_t tgtdev_lock;
+ struct list_head tgtdev_list;
+ u16 max_sgl_entries;
+
+ u32 chain_buf_count;
+ struct dma_pool *chain_buf_pool;
+ struct chain_element *chain_sgl_list;
+ unsigned long *chain_bitmap;
+ spinlock_t chain_buf_lock;
+
+ struct mpi3mr_drv_cmd bsg_cmds;
+ struct mpi3mr_drv_cmd host_tm_cmds;
+ struct mpi3mr_drv_cmd dev_rmhs_cmds[MPI3MR_NUM_DEVRMCMD];
+ struct mpi3mr_drv_cmd evtack_cmds[MPI3MR_NUM_EVTACKCMD];
+ unsigned long *devrem_bitmap;
+ u16 dev_handle_bitmap_bits;
+ unsigned long *removepend_bitmap;
+ struct list_head delayed_rmhs_list;
+ unsigned long *evtack_cmds_bitmap;
+ struct list_head delayed_evtack_cmds_list;
+
+ u16 ts_update_counter;
+ u16 ts_update_interval;
+ u8 reset_in_progress;
+ u8 unrecoverable;
+ u8 io_admin_reset_sync;
+ int prev_reset_result;
+ struct mutex reset_mutex;
+ wait_queue_head_t reset_waitq;
+
+ u8 prepare_for_reset;
+ u16 prepare_for_reset_timeout_counter;
+
+ void *prp_list_virt;
+ dma_addr_t prp_list_dma;
+ u32 prp_sz;
+
+ u16 diagsave_timeout;
+ int logging_level;
+ u16 flush_io_count;
+
+ struct mpi3mr_fwevt *current_event;
+ struct mpi3_driver_info_layout driver_info;
+ u16 change_count;
+
+ u8 pel_enabled;
+ u8 pel_abort_requested;
+ u8 pel_class;
+ u16 pel_locale;
+ struct mpi3mr_drv_cmd pel_cmds;
+ struct mpi3mr_drv_cmd pel_abort_cmd;
+
+ u32 pel_newest_seqnum;
+ void *pel_seqnum_virt;
+ dma_addr_t pel_seqnum_dma;
+ u32 pel_seqnum_sz;
+
+ u16 op_reply_q_offset;
+ u16 default_qcount;
+ u16 active_poll_qcount;
+ u16 requested_poll_qcount;
+
+ struct device bsg_dev;
+ struct request_queue *bsg_queue;
+ u8 stop_bsgs;
+ u8 *logdata_buf;
+ u16 logdata_buf_idx;
+ u16 logdata_entry_sz;
+ spinlock_t adm_req_q_bar_writeq_lock;
+ spinlock_t adm_reply_q_bar_writeq_lock;
+
+ atomic_t pend_large_data_sz;
+ u32 io_throttle_data_length;
+ u32 io_throttle_high;
+ u32 io_throttle_low;
+ u16 num_io_throttle_group;
+ struct mpi3mr_throttle_group_info *throttle_groups;
+
+ u8 sas_transport_enabled;
+ u8 scsi_device_channel;
+ struct mpi3mr_drv_cmd transport_cmds;
+ struct mpi3mr_sas_node sas_hba;
+ struct list_head sas_expander_list;
+ spinlock_t sas_node_lock;
+ struct list_head hba_port_table_list;
+ struct list_head enclosure_list;
+
+ struct dma_pool *ioctl_dma_pool;
+ struct dma_memory_desc ioctl_sge[MPI3MR_NUM_IOCTL_SGE];
+ struct dma_memory_desc ioctl_chain_sge;
+ struct dma_memory_desc ioctl_resp_sge;
+ bool ioctl_sges_allocated;
+ bool reply_trigger_present;
+ bool event_trigger_present;
+ bool scsisense_trigger_present;
+ struct diag_buffer_desc diag_buffers[MPI3MR_MAX_NUM_HDB];
+ struct mpi3_driver_page2 *driver_pg2;
+ spinlock_t trigger_lock;
+ bool snapdump_trigger_active;
+ bool trace_release_trigger_active;
+ bool fw_release_trigger_active;
+ bool pci_err_recovery;
+ bool block_on_pci_err;
+ atomic_t reply_qfull_count;
+ bool prevent_reply_qfull;
+ bool seg_tb_support;
+ u32 num_tb_segs;
+ struct dma_pool *trace_buf_pool;
+ struct segments *trace_buf;
+
+};
+
+/**
+ * struct mpi3mr_fwevt - Firmware event structure.
+ *
+ * @list: list head
+ * @work: Work structure
+ * @mrioc: Adapter instance reference
+ * @event_id: MPI3 firmware event ID
+ * @send_ack: Event acknowledgment required or not
+ * @process_evt: Bottomhalf processing required or not
+ * @evt_ctx: Event context to send in Ack
+ * @event_data_size: size of the event data in bytes
+ * @pending_at_sml: waiting for device add/remove API to complete
+ * @discard: discard this event
+ * @ref_count: kref count
+ * @event_data: Actual MPI3 event data
+ */
+struct mpi3mr_fwevt {
+ struct list_head list;
+ struct work_struct work;
+ struct mpi3mr_ioc *mrioc;
+ u16 event_id;
+ bool send_ack;
+ bool process_evt;
+ u32 evt_ctx;
+ u16 event_data_size;
+ bool pending_at_sml;
+ bool discard;
+ struct kref ref_count;
+ char event_data[] __aligned(4);
+};
+
+
+/**
+ * struct delayed_dev_rmhs_node - Delayed device removal node
+ *
+ * @list: list head
+ * @handle: Device handle
+ * @iou_rc: IO Unit Control Reason Code
+ */
+struct delayed_dev_rmhs_node {
+ struct list_head list;
+ u16 handle;
+ u8 iou_rc;
+};
+
+/**
+ * struct delayed_evt_ack_node - Delayed event ack node
+ * @list: list head
+ * @event: MPI3 event ID
+ * @event_ctx: event context
+ */
+struct delayed_evt_ack_node {
+ struct list_head list;
+ u8 event;
+ u32 event_ctx;
+};
+
+int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc);
+void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc);
+int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc);
+int mpi3mr_reinit_ioc(struct mpi3mr_ioc *mrioc, u8 is_resume);
+void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc);
+int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async);
+int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req,
+u16 admin_req_sz, u8 ignore_reset);
+int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc,
+ struct op_req_qinfo *opreqq, u8 *req);
+void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length,
+ dma_addr_t dma_addr);
+void mpi3mr_build_zero_len_sge(void *paddr);
+void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr);
+void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr);
+void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
+ u64 sense_buf_dma);
+
+void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc);
+void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc);
+void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply);
+void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply_descriptor *reply_desc,
+ u64 *reply_dma, u16 qidx);
+void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc);
+void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc);
+
+int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
+ u16 reset_reason, u8 snapdump);
+void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc);
+void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc);
+
+enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc);
+int mpi3mr_process_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
+ u32 event_ctx);
+
+void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout);
+void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc);
+void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc);
+void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc);
+void mpi3mr_flush_delayed_cmd_lists(struct mpi3mr_ioc *mrioc);
+void mpi3mr_check_rh_fault_ioc(struct mpi3mr_ioc *mrioc, u32 reason_code);
+void mpi3mr_print_fault_info(struct mpi3mr_ioc *mrioc);
+void mpi3mr_check_rh_fault_ioc(struct mpi3mr_ioc *mrioc, u32 reason_code);
+int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
+ struct op_reply_qinfo *op_reply_q);
+int mpi3mr_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num);
+void mpi3mr_bsg_init(struct mpi3mr_ioc *mrioc);
+void mpi3mr_bsg_exit(struct mpi3mr_ioc *mrioc);
+int mpi3mr_issue_tm(struct mpi3mr_ioc *mrioc, u8 tm_type,
+ u16 handle, uint lun, u16 htag, ulong timeout,
+ struct mpi3mr_drv_cmd *drv_cmd,
+ u8 *resp_code, struct scsi_cmnd *scmd);
+struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle(
+ struct mpi3mr_ioc *mrioc, u16 handle);
+void mpi3mr_pel_get_seqnum_complete(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd);
+int mpi3mr_pel_get_seqnum_post(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd);
+void mpi3mr_app_save_logdata(struct mpi3mr_ioc *mrioc, char *event_data,
+ u16 event_data_size);
+struct mpi3mr_enclosure_node *mpi3mr_enclosure_find_by_handle(
+ struct mpi3mr_ioc *mrioc, u16 handle);
+extern const struct attribute_group *mpi3mr_host_groups[];
+extern const struct attribute_group *mpi3mr_dev_groups[];
+
+extern struct sas_function_template mpi3mr_transport_functions;
+extern struct scsi_transport_template *mpi3mr_transport_template;
+
+int mpi3mr_cfg_get_dev_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_device_page0 *dev_pg0, u16 pg_sz, u32 form, u32 form_spec);
+int mpi3mr_cfg_get_sas_phy_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_phy_page0 *phy_pg0, u16 pg_sz, u32 form,
+ u32 form_spec);
+int mpi3mr_cfg_get_sas_phy_pg1(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_phy_page1 *phy_pg1, u16 pg_sz, u32 form,
+ u32 form_spec);
+int mpi3mr_cfg_get_sas_exp_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_expander_page0 *exp_pg0, u16 pg_sz, u32 form,
+ u32 form_spec);
+int mpi3mr_cfg_get_sas_exp_pg1(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_expander_page1 *exp_pg1, u16 pg_sz, u32 form,
+ u32 form_spec);
+int mpi3mr_cfg_get_enclosure_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_enclosure_page0 *encl_pg0, u16 pg_sz, u32 form,
+ u32 form_spec);
+int mpi3mr_cfg_get_sas_io_unit_pg0(struct mpi3mr_ioc *mrioc,
+ struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0, u16 pg_sz);
+int mpi3mr_cfg_get_sas_io_unit_pg1(struct mpi3mr_ioc *mrioc,
+ struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1, u16 pg_sz);
+int mpi3mr_cfg_set_sas_io_unit_pg1(struct mpi3mr_ioc *mrioc,
+ struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1, u16 pg_sz);
+int mpi3mr_cfg_get_driver_pg1(struct mpi3mr_ioc *mrioc,
+ struct mpi3_driver_page1 *driver_pg1, u16 pg_sz);
+int mpi3mr_cfg_get_driver_pg2(struct mpi3mr_ioc *mrioc,
+ struct mpi3_driver_page2 *driver_pg2, u16 pg_sz, u8 page_type);
+
+u8 mpi3mr_is_expander_device(u16 device_info);
+int mpi3mr_expander_add(struct mpi3mr_ioc *mrioc, u16 handle);
+void mpi3mr_expander_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
+ struct mpi3mr_hba_port *hba_port);
+struct mpi3mr_sas_node *__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc
+ *mrioc, u16 handle);
+struct mpi3mr_hba_port *mpi3mr_get_hba_port_by_id(struct mpi3mr_ioc *mrioc,
+ u8 port_id);
+void mpi3mr_sas_host_refresh(struct mpi3mr_ioc *mrioc);
+void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc);
+void mpi3mr_update_links(struct mpi3mr_ioc *mrioc,
+ u64 sas_address_parent, u16 handle, u8 phy_number, u8 link_rate,
+ struct mpi3mr_hba_port *hba_port);
+void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev);
+int mpi3mr_report_tgtdev_to_sas_transport(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev);
+void mpi3mr_remove_tgtdev_from_sas_transport(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev);
+struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_addr_and_rphy(
+ struct mpi3mr_ioc *mrioc, u64 sas_address, struct sas_rphy *rphy);
+void mpi3mr_print_device_event_notice(struct mpi3mr_ioc *mrioc,
+ bool device_add);
+void mpi3mr_refresh_sas_ports(struct mpi3mr_ioc *mrioc);
+void mpi3mr_refresh_expanders(struct mpi3mr_ioc *mrioc);
+void mpi3mr_add_event_wait_for_device_refresh(struct mpi3mr_ioc *mrioc);
+void mpi3mr_flush_drv_cmds(struct mpi3mr_ioc *mrioc);
+void mpi3mr_flush_cmds_for_unrecovered_controller(struct mpi3mr_ioc *mrioc);
+void mpi3mr_free_enclosure_list(struct mpi3mr_ioc *mrioc);
+int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc);
+void mpi3mr_expander_node_remove(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_node *sas_expander);
+void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc);
+int mpi3mr_post_diag_bufs(struct mpi3mr_ioc *mrioc);
+int mpi3mr_issue_diag_buf_release(struct mpi3mr_ioc *mrioc,
+ struct diag_buffer_desc *diag_buffer);
+void mpi3mr_release_diag_bufs(struct mpi3mr_ioc *mrioc, u8 skip_rel_action);
+void mpi3mr_set_trigger_data_in_hdb(struct diag_buffer_desc *hdb,
+ u8 type, union mpi3mr_trigger_data *trigger_data, bool force);
+int mpi3mr_refresh_trigger(struct mpi3mr_ioc *mrioc, u8 page_type);
+struct diag_buffer_desc *mpi3mr_diag_buffer_for_type(struct mpi3mr_ioc *mrioc,
+ u8 buf_type);
+int mpi3mr_issue_diag_buf_post(struct mpi3mr_ioc *mrioc,
+ struct diag_buffer_desc *diag_buffer);
+void mpi3mr_set_trigger_data_in_all_hdb(struct mpi3mr_ioc *mrioc,
+ u8 type, union mpi3mr_trigger_data *trigger_data, bool force);
+void mpi3mr_reply_trigger(struct mpi3mr_ioc *mrioc, u16 iocstatus,
+ u32 iocloginfo);
+void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc,
+ struct trigger_event_data *event_data);
+void mpi3mr_scsisense_trigger(struct mpi3mr_ioc *mrioc, u8 senseky, u8 asc,
+ u8 ascq);
+void mpi3mr_event_trigger(struct mpi3mr_ioc *mrioc, u8 event);
+void mpi3mr_global_trigger(struct mpi3mr_ioc *mrioc, u64 trigger_data);
+void mpi3mr_hdbstatuschg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply);
+#endif /*MPI3MR_H_INCLUDED*/
diff --git a/drivers/scsi/mpi3mr/mpi3mr_app.c b/drivers/scsi/mpi3mr/mpi3mr_app.c
new file mode 100644
index 000000000000..0e5478d62580
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_app.c
@@ -0,0 +1,3462 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2023 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#include "mpi3mr.h"
+#include <linux/bsg-lib.h>
+#include <uapi/scsi/scsi_bsg_mpi3mr.h>
+
+/**
+ * mpi3mr_alloc_trace_buffer: Allocate segmented trace buffer
+ * @mrioc: Adapter instance reference
+ * @trace_size: Trace buffer size
+ *
+ * Allocate either segmented memory pools or contiguous buffer
+ * based on the controller capability for the host trace
+ * buffer.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_alloc_trace_buffer(struct mpi3mr_ioc *mrioc, u32 trace_size)
+{
+ struct diag_buffer_desc *diag_buffer = &mrioc->diag_buffers[0];
+ int i, sz;
+ u64 *diag_buffer_list = NULL;
+ dma_addr_t diag_buffer_list_dma;
+ u32 seg_count;
+
+ if (mrioc->seg_tb_support) {
+ seg_count = (trace_size) / MPI3MR_PAGE_SIZE_4K;
+ trace_size = seg_count * MPI3MR_PAGE_SIZE_4K;
+
+ diag_buffer_list = dma_alloc_coherent(&mrioc->pdev->dev,
+ sizeof(u64) * seg_count,
+ &diag_buffer_list_dma, GFP_KERNEL);
+ if (!diag_buffer_list)
+ return -1;
+
+ mrioc->num_tb_segs = seg_count;
+
+ sz = sizeof(struct segments) * seg_count;
+ mrioc->trace_buf = kzalloc(sz, GFP_KERNEL);
+ if (!mrioc->trace_buf)
+ goto trace_buf_failed;
+
+ mrioc->trace_buf_pool = dma_pool_create("trace_buf pool",
+ &mrioc->pdev->dev, MPI3MR_PAGE_SIZE_4K, MPI3MR_PAGE_SIZE_4K,
+ 0);
+ if (!mrioc->trace_buf_pool) {
+ ioc_err(mrioc, "trace buf pool: dma_pool_create failed\n");
+ goto trace_buf_pool_failed;
+ }
+
+ for (i = 0; i < seg_count; i++) {
+ mrioc->trace_buf[i].segment =
+ dma_pool_zalloc(mrioc->trace_buf_pool, GFP_KERNEL,
+ &mrioc->trace_buf[i].segment_dma);
+ diag_buffer_list[i] =
+ (u64) mrioc->trace_buf[i].segment_dma;
+ if (!diag_buffer_list[i])
+ goto tb_seg_alloc_failed;
+ }
+
+ diag_buffer->addr = diag_buffer_list;
+ diag_buffer->dma_addr = diag_buffer_list_dma;
+ diag_buffer->is_segmented = true;
+
+ dprint_init(mrioc, "segmented trace diag buffer\n"
+ "is allocated successfully seg_count:%d\n", seg_count);
+ return 0;
+ } else {
+ diag_buffer->addr = dma_alloc_coherent(&mrioc->pdev->dev,
+ trace_size, &diag_buffer->dma_addr, GFP_KERNEL);
+ if (diag_buffer->addr) {
+ dprint_init(mrioc, "trace diag buffer is allocated successfully\n");
+ return 0;
+ }
+ return -1;
+ }
+
+tb_seg_alloc_failed:
+ if (mrioc->trace_buf_pool) {
+ for (i = 0; i < mrioc->num_tb_segs; i++) {
+ if (mrioc->trace_buf[i].segment) {
+ dma_pool_free(mrioc->trace_buf_pool,
+ mrioc->trace_buf[i].segment,
+ mrioc->trace_buf[i].segment_dma);
+ mrioc->trace_buf[i].segment = NULL;
+ }
+ mrioc->trace_buf[i].segment = NULL;
+ }
+ dma_pool_destroy(mrioc->trace_buf_pool);
+ mrioc->trace_buf_pool = NULL;
+ }
+trace_buf_pool_failed:
+ kfree(mrioc->trace_buf);
+ mrioc->trace_buf = NULL;
+trace_buf_failed:
+ if (diag_buffer_list)
+ dma_free_coherent(&mrioc->pdev->dev,
+ sizeof(u64) * mrioc->num_tb_segs,
+ diag_buffer_list, diag_buffer_list_dma);
+ return -1;
+}
+
+/**
+ * mpi3mr_alloc_diag_bufs - Allocate memory for diag buffers
+ * @mrioc: Adapter instance reference
+ *
+ * This functions checks whether the driver defined buffer sizes
+ * are greater than IOCFacts provided controller local buffer
+ * sizes and if the driver defined sizes are more then the
+ * driver allocates the specific buffer by reading driver page1
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc)
+{
+ struct diag_buffer_desc *diag_buffer;
+ struct mpi3_driver_page1 driver_pg1;
+ u32 trace_dec_size, trace_min_size, fw_dec_size, fw_min_size,
+ trace_size, fw_size;
+ u16 pg_sz = sizeof(driver_pg1);
+ int retval = 0;
+ bool retry = false;
+
+ if (mrioc->diag_buffers[0].addr || mrioc->diag_buffers[1].addr)
+ return;
+
+ retval = mpi3mr_cfg_get_driver_pg1(mrioc, &driver_pg1, pg_sz);
+ if (retval) {
+ ioc_warn(mrioc,
+ "%s: driver page 1 read failed, allocating trace\n"
+ "and firmware diag buffers of default size\n", __func__);
+ trace_size = fw_size = MPI3MR_DEFAULT_HDB_MAX_SZ;
+ trace_dec_size = fw_dec_size = MPI3MR_DEFAULT_HDB_DEC_SZ;
+ trace_min_size = fw_min_size = MPI3MR_DEFAULT_HDB_MIN_SZ;
+
+ } else {
+ trace_size = driver_pg1.host_diag_trace_max_size * 1024;
+ trace_dec_size = driver_pg1.host_diag_trace_decrement_size
+ * 1024;
+ trace_min_size = driver_pg1.host_diag_trace_min_size * 1024;
+ fw_size = driver_pg1.host_diag_fw_max_size * 1024;
+ fw_dec_size = driver_pg1.host_diag_fw_decrement_size * 1024;
+ fw_min_size = driver_pg1.host_diag_fw_min_size * 1024;
+ dprint_init(mrioc,
+ "%s:trace diag buffer sizes read from driver\n"
+ "page1: maximum size = %dKB, decrement size = %dKB\n"
+ ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_trace_max_size,
+ driver_pg1.host_diag_trace_decrement_size,
+ driver_pg1.host_diag_trace_min_size);
+ dprint_init(mrioc,
+ "%s:firmware diag buffer sizes read from driver\n"
+ "page1: maximum size = %dKB, decrement size = %dKB\n"
+ ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_fw_max_size,
+ driver_pg1.host_diag_fw_decrement_size,
+ driver_pg1.host_diag_fw_min_size);
+ if ((trace_size == 0) && (fw_size == 0))
+ return;
+ }
+
+
+retry_trace:
+ diag_buffer = &mrioc->diag_buffers[0];
+ diag_buffer->type = MPI3_DIAG_BUFFER_TYPE_TRACE;
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED;
+ if ((mrioc->facts.diag_trace_sz < trace_size) && (trace_size >=
+ trace_min_size)) {
+ if (!retry)
+ dprint_init(mrioc,
+ "trying to allocate trace diag buffer of size = %dKB\n",
+ trace_size / 1024);
+ if ((!mrioc->seg_tb_support && (get_order(trace_size) > MAX_PAGE_ORDER)) ||
+ mpi3mr_alloc_trace_buffer(mrioc, trace_size)) {
+
+ retry = true;
+ trace_size -= trace_dec_size;
+ dprint_init(mrioc, "trace diag buffer allocation failed\n"
+ "retrying smaller size %dKB\n", trace_size / 1024);
+ goto retry_trace;
+ } else
+ diag_buffer->size = trace_size;
+ }
+
+ retry = false;
+retry_fw:
+
+ diag_buffer = &mrioc->diag_buffers[1];
+
+ diag_buffer->type = MPI3_DIAG_BUFFER_TYPE_FW;
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED;
+ if ((mrioc->facts.diag_fw_sz < fw_size) && (fw_size >= fw_min_size)) {
+ if (get_order(fw_size) <= MAX_PAGE_ORDER) {
+ diag_buffer->addr
+ = dma_alloc_coherent(&mrioc->pdev->dev, fw_size,
+ &diag_buffer->dma_addr,
+ GFP_KERNEL);
+ }
+ if (!retry)
+ dprint_init(mrioc,
+ "%s:trying to allocate firmware diag buffer of size = %dKB\n",
+ __func__, fw_size / 1024);
+ if (diag_buffer->addr) {
+ dprint_init(mrioc, "%s:firmware diag buffer allocated successfully\n",
+ __func__);
+ diag_buffer->size = fw_size;
+ } else {
+ retry = true;
+ fw_size -= fw_dec_size;
+ dprint_init(mrioc, "%s:trace diag buffer allocation failed,\n"
+ "retrying smaller size %dKB\n",
+ __func__, fw_size / 1024);
+ goto retry_fw;
+ }
+ }
+}
+
+/**
+ * mpi3mr_issue_diag_buf_post - Send diag buffer post req
+ * @mrioc: Adapter instance reference
+ * @diag_buffer: Diagnostic buffer descriptor
+ *
+ * Issue diagnostic buffer post MPI request through admin queue
+ * and wait for the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_issue_diag_buf_post(struct mpi3mr_ioc *mrioc,
+ struct diag_buffer_desc *diag_buffer)
+{
+ struct mpi3_diag_buffer_post_request diag_buf_post_req;
+ u8 prev_status;
+ int retval = 0;
+
+ if (diag_buffer->disabled_after_reset) {
+ dprint_bsg_err(mrioc, "%s: skipping diag buffer posting\n"
+ "as it is disabled after reset\n", __func__);
+ return -1;
+ }
+
+ memset(&diag_buf_post_req, 0, sizeof(diag_buf_post_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ dprint_bsg_err(mrioc, "%s: command is in use\n", __func__);
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return -1;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ diag_buf_post_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ diag_buf_post_req.function = MPI3_FUNCTION_DIAG_BUFFER_POST;
+ diag_buf_post_req.type = diag_buffer->type;
+ diag_buf_post_req.address = le64_to_cpu(diag_buffer->dma_addr);
+ diag_buf_post_req.length = le32_to_cpu(diag_buffer->size);
+
+ if (diag_buffer->is_segmented)
+ diag_buf_post_req.msg_flags |= MPI3_DIAG_BUFFER_POST_MSGFLAGS_SEGMENTED;
+
+ dprint_bsg_info(mrioc, "%s: posting diag buffer type %d segmented:%d\n", __func__,
+ diag_buffer->type, diag_buffer->is_segmented);
+
+ prev_status = diag_buffer->status;
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED;
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &diag_buf_post_req,
+ sizeof(diag_buf_post_req), 1);
+ if (retval) {
+ dprint_bsg_err(mrioc, "%s: admin request post failed\n",
+ __func__);
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mrioc->init_cmds.is_waiting = 0;
+ dprint_bsg_err(mrioc, "%s: command timedout\n", __func__);
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_DIAG_BUFFER_POST_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_bsg_err(mrioc,
+ "%s: command failed, buffer_type (%d) ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__, diag_buffer->type,
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ dprint_bsg_info(mrioc, "%s: diag buffer type %d posted successfully\n",
+ __func__, diag_buffer->type);
+
+out_unlock:
+ if (retval)
+ diag_buffer->status = prev_status;
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return retval;
+}
+
+/**
+ * mpi3mr_post_diag_bufs - Post diag buffers to the controller
+ * @mrioc: Adapter instance reference
+ *
+ * This function calls helper function to post both trace and
+ * firmware buffers to the controller.
+ *
+ * Return: None
+ */
+int mpi3mr_post_diag_bufs(struct mpi3mr_ioc *mrioc)
+{
+ u8 i;
+ struct diag_buffer_desc *diag_buffer;
+
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ diag_buffer = &mrioc->diag_buffers[i];
+ if (!(diag_buffer->addr))
+ continue;
+ if (mpi3mr_issue_diag_buf_post(mrioc, diag_buffer))
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * mpi3mr_issue_diag_buf_release - Send diag buffer release req
+ * @mrioc: Adapter instance reference
+ * @diag_buffer: Diagnostic buffer descriptor
+ *
+ * Issue diagnostic buffer manage MPI request with release
+ * action request through admin queue and wait for the
+ * completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_issue_diag_buf_release(struct mpi3mr_ioc *mrioc,
+ struct diag_buffer_desc *diag_buffer)
+{
+ struct mpi3_diag_buffer_manage_request diag_buf_manage_req;
+ int retval = 0;
+
+ if ((diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ return retval;
+
+ memset(&diag_buf_manage_req, 0, sizeof(diag_buf_manage_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ dprint_reset(mrioc, "%s: command is in use\n", __func__);
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return -1;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ diag_buf_manage_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ diag_buf_manage_req.function = MPI3_FUNCTION_DIAG_BUFFER_MANAGE;
+ diag_buf_manage_req.type = diag_buffer->type;
+ diag_buf_manage_req.action = MPI3_DIAG_BUFFER_ACTION_RELEASE;
+
+
+ dprint_reset(mrioc, "%s: releasing diag buffer type %d\n", __func__,
+ diag_buffer->type);
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &diag_buf_manage_req,
+ sizeof(diag_buf_manage_req), 1);
+ if (retval) {
+ dprint_reset(mrioc, "%s: admin request post failed\n", __func__);
+ mpi3mr_set_trigger_data_in_hdb(diag_buffer,
+ MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mrioc->init_cmds.is_waiting = 0;
+ dprint_reset(mrioc, "%s: command timedout\n", __func__);
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_DIAG_BUFFER_RELEASE_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_reset(mrioc,
+ "%s: command failed, buffer_type (%d) ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__, diag_buffer->type,
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ dprint_reset(mrioc, "%s: diag buffer type %d released successfully\n",
+ __func__, diag_buffer->type);
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return retval;
+}
+
+/**
+ * mpi3mr_process_trigger - Generic HDB Trigger handler
+ * @mrioc: Adapter instance reference
+ * @trigger_type: Trigger type
+ * @trigger_data: Trigger data
+ * @trigger_flags: Trigger flags
+ *
+ * This function checks validity of HDB, triggers and based on
+ * trigger information, creates an event to be processed in the
+ * firmware event worker thread .
+ *
+ * This function should be called with trigger spinlock held
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_process_trigger(struct mpi3mr_ioc *mrioc, u8 trigger_type,
+ union mpi3mr_trigger_data *trigger_data, u8 trigger_flags)
+{
+ struct trigger_event_data event_data;
+ struct diag_buffer_desc *trace_hdb = NULL;
+ struct diag_buffer_desc *fw_hdb = NULL;
+ u64 global_trigger;
+
+ trace_hdb = mpi3mr_diag_buffer_for_type(mrioc,
+ MPI3_DIAG_BUFFER_TYPE_TRACE);
+ if (trace_hdb &&
+ (trace_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (trace_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ trace_hdb = NULL;
+
+ fw_hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_FW);
+
+ if (fw_hdb &&
+ (fw_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (fw_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ fw_hdb = NULL;
+
+ if (mrioc->snapdump_trigger_active || (mrioc->fw_release_trigger_active
+ && mrioc->trace_release_trigger_active) ||
+ (!trace_hdb && !fw_hdb) || (!mrioc->driver_pg2) ||
+ ((trigger_type == MPI3MR_HDB_TRIGGER_TYPE_ELEMENT)
+ && (!mrioc->driver_pg2->num_triggers)))
+ return;
+
+ memset(&event_data, 0, sizeof(event_data));
+ event_data.trigger_type = trigger_type;
+ memcpy(&event_data.trigger_specific_data, trigger_data,
+ sizeof(*trigger_data));
+ global_trigger = le64_to_cpu(mrioc->driver_pg2->global_trigger);
+
+ if (global_trigger & MPI3_DRIVER2_GLOBALTRIGGER_SNAPDUMP_ENABLED) {
+ event_data.snapdump = true;
+ event_data.trace_hdb = trace_hdb;
+ event_data.fw_hdb = fw_hdb;
+ mrioc->snapdump_trigger_active = true;
+ } else if (trigger_type == MPI3MR_HDB_TRIGGER_TYPE_GLOBAL) {
+ if ((trace_hdb) && (global_trigger &
+ MPI3_DRIVER2_GLOBALTRIGGER_DIAG_TRACE_RELEASE) &&
+ (!mrioc->trace_release_trigger_active)) {
+ event_data.trace_hdb = trace_hdb;
+ mrioc->trace_release_trigger_active = true;
+ }
+ if ((fw_hdb) && (global_trigger &
+ MPI3_DRIVER2_GLOBALTRIGGER_DIAG_FW_RELEASE) &&
+ (!mrioc->fw_release_trigger_active)) {
+ event_data.fw_hdb = fw_hdb;
+ mrioc->fw_release_trigger_active = true;
+ }
+ } else if (trigger_type == MPI3MR_HDB_TRIGGER_TYPE_ELEMENT) {
+ if ((trace_hdb) && (trigger_flags &
+ MPI3_DRIVER2_TRIGGER_FLAGS_DIAG_TRACE_RELEASE) &&
+ (!mrioc->trace_release_trigger_active)) {
+ event_data.trace_hdb = trace_hdb;
+ mrioc->trace_release_trigger_active = true;
+ }
+ if ((fw_hdb) && (trigger_flags &
+ MPI3_DRIVER2_TRIGGER_FLAGS_DIAG_FW_RELEASE) &&
+ (!mrioc->fw_release_trigger_active)) {
+ event_data.fw_hdb = fw_hdb;
+ mrioc->fw_release_trigger_active = true;
+ }
+ }
+
+ if (event_data.trace_hdb || event_data.fw_hdb)
+ mpi3mr_hdb_trigger_data_event(mrioc, &event_data);
+}
+
+/**
+ * mpi3mr_global_trigger - Global HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @trigger_data: Trigger data
+ *
+ * This function checks whether the given global trigger is
+ * enabled in the driver page 2 and if so calls generic trigger
+ * handler to queue event for HDB release.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_global_trigger(struct mpi3mr_ioc *mrioc, u64 trigger_data)
+{
+ unsigned long flags;
+ union mpi3mr_trigger_data trigger_specific_data;
+
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ if (le64_to_cpu(mrioc->driver_pg2->global_trigger) & trigger_data) {
+ memset(&trigger_specific_data, 0,
+ sizeof(trigger_specific_data));
+ trigger_specific_data.global = trigger_data;
+ mpi3mr_process_trigger(mrioc, MPI3MR_HDB_TRIGGER_TYPE_GLOBAL,
+ &trigger_specific_data, 0);
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+}
+
+/**
+ * mpi3mr_scsisense_trigger - SCSI sense HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @sensekey: Sense Key
+ * @asc: Additional Sense Code
+ * @ascq: Additional Sense Code Qualifier
+ *
+ * This function compares SCSI sense trigger values with driver
+ * page 2 values and calls generic trigger handler to release
+ * HDBs if match found
+ *
+ * Return: Nothing
+ */
+void mpi3mr_scsisense_trigger(struct mpi3mr_ioc *mrioc, u8 sensekey, u8 asc,
+ u8 ascq)
+{
+ struct mpi3_driver2_trigger_scsi_sense *scsi_sense_trigger = NULL;
+ u64 i = 0;
+ unsigned long flags;
+ u8 num_triggers, trigger_flags;
+
+ if (mrioc->scsisense_trigger_present) {
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ scsi_sense_trigger = (struct mpi3_driver2_trigger_scsi_sense *)
+ mrioc->driver_pg2->trigger;
+ num_triggers = mrioc->driver_pg2->num_triggers;
+ for (i = 0; i < num_triggers; i++, scsi_sense_trigger++) {
+ if (scsi_sense_trigger->type !=
+ MPI3_DRIVER2_TRIGGER_TYPE_SCSI_SENSE)
+ continue;
+ if (!(scsi_sense_trigger->sense_key ==
+ MPI3_DRIVER2_TRIGGER_SCSI_SENSE_SENSE_KEY_MATCH_ALL
+ || scsi_sense_trigger->sense_key == sensekey))
+ continue;
+ if (!(scsi_sense_trigger->asc ==
+ MPI3_DRIVER2_TRIGGER_SCSI_SENSE_ASC_MATCH_ALL ||
+ scsi_sense_trigger->asc == asc))
+ continue;
+ if (!(scsi_sense_trigger->ascq ==
+ MPI3_DRIVER2_TRIGGER_SCSI_SENSE_ASCQ_MATCH_ALL ||
+ scsi_sense_trigger->ascq == ascq))
+ continue;
+ trigger_flags = scsi_sense_trigger->flags;
+ mpi3mr_process_trigger(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
+ (union mpi3mr_trigger_data *)scsi_sense_trigger,
+ trigger_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
+ * mpi3mr_event_trigger - MPI event HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @event: MPI Event
+ *
+ * This function compares event trigger values with driver page
+ * 2 values and calls generic trigger handler to release
+ * HDBs if match found.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_event_trigger(struct mpi3mr_ioc *mrioc, u8 event)
+{
+ struct mpi3_driver2_trigger_event *event_trigger = NULL;
+ u64 i = 0;
+ unsigned long flags;
+ u8 num_triggers, trigger_flags;
+
+ if (mrioc->event_trigger_present) {
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ event_trigger = (struct mpi3_driver2_trigger_event *)
+ mrioc->driver_pg2->trigger;
+ num_triggers = mrioc->driver_pg2->num_triggers;
+
+ for (i = 0; i < num_triggers; i++, event_trigger++) {
+ if (event_trigger->type !=
+ MPI3_DRIVER2_TRIGGER_TYPE_EVENT)
+ continue;
+ if (event_trigger->event != event)
+ continue;
+ trigger_flags = event_trigger->flags;
+ mpi3mr_process_trigger(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
+ (union mpi3mr_trigger_data *)event_trigger,
+ trigger_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
+ * mpi3mr_reply_trigger - MPI Reply HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Masked value of IOC Status from MPI Reply
+ * @ioc_loginfo: IOC Log Info from MPI Reply
+ *
+ * This function compares IOC status and IOC log info trigger
+ * values with driver page 2 values and calls generic trigger
+ * handler to release HDBs if match found.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_reply_trigger(struct mpi3mr_ioc *mrioc, u16 ioc_status,
+ u32 ioc_loginfo)
+{
+ struct mpi3_driver2_trigger_reply *reply_trigger = NULL;
+ u64 i = 0;
+ unsigned long flags;
+ u8 num_triggers, trigger_flags;
+
+ if (mrioc->reply_trigger_present) {
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ reply_trigger = (struct mpi3_driver2_trigger_reply *)
+ mrioc->driver_pg2->trigger;
+ num_triggers = mrioc->driver_pg2->num_triggers;
+ for (i = 0; i < num_triggers; i++, reply_trigger++) {
+ if (reply_trigger->type !=
+ MPI3_DRIVER2_TRIGGER_TYPE_REPLY)
+ continue;
+ if ((le16_to_cpu(reply_trigger->ioc_status) !=
+ ioc_status)
+ && (le16_to_cpu(reply_trigger->ioc_status) !=
+ MPI3_DRIVER2_TRIGGER_REPLY_IOCSTATUS_MATCH_ALL))
+ continue;
+ if ((le32_to_cpu(reply_trigger->ioc_log_info) !=
+ (le32_to_cpu(reply_trigger->ioc_log_info_mask) &
+ ioc_loginfo)))
+ continue;
+ trigger_flags = reply_trigger->flags;
+ mpi3mr_process_trigger(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
+ (union mpi3mr_trigger_data *)reply_trigger,
+ trigger_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
+ * mpi3mr_get_num_trigger - Gets number of HDB triggers
+ * @mrioc: Adapter instance reference
+ * @num_triggers: Number of triggers
+ * @page_action: Page action
+ *
+ * This function reads number of triggers by reading driver page
+ * 2
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static int mpi3mr_get_num_trigger(struct mpi3mr_ioc *mrioc, u8 *num_triggers,
+ u8 page_action)
+{
+ struct mpi3_driver_page2 drvr_page2;
+ int retval = 0;
+
+ *num_triggers = 0;
+
+ retval = mpi3mr_cfg_get_driver_pg2(mrioc, &drvr_page2,
+ sizeof(struct mpi3_driver_page2), page_action);
+
+ if (retval) {
+ dprint_init(mrioc, "%s: driver page 2 read failed\n", __func__);
+ return retval;
+ }
+ *num_triggers = drvr_page2.num_triggers;
+ return retval;
+}
+
+/**
+ * mpi3mr_refresh_trigger - Handler for Refresh trigger BSG
+ * @mrioc: Adapter instance reference
+ * @page_action: Page action
+ *
+ * This function caches the driver page 2 in the driver's memory
+ * by reading driver page 2 from the controller for a given page
+ * type and updates the HDB trigger values
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+int mpi3mr_refresh_trigger(struct mpi3mr_ioc *mrioc, u8 page_action)
+{
+ u16 pg_sz = sizeof(struct mpi3_driver_page2);
+ struct mpi3_driver_page2 *drvr_page2 = NULL;
+ u8 trigger_type, num_triggers;
+ int retval;
+ int i = 0;
+ unsigned long flags;
+
+ retval = mpi3mr_get_num_trigger(mrioc, &num_triggers, page_action);
+
+ if (retval)
+ goto out;
+
+ pg_sz = offsetof(struct mpi3_driver_page2, trigger) +
+ (num_triggers * sizeof(union mpi3_driver2_trigger_element));
+ drvr_page2 = kzalloc(pg_sz, GFP_KERNEL);
+ if (!drvr_page2) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ retval = mpi3mr_cfg_get_driver_pg2(mrioc, drvr_page2, pg_sz, page_action);
+ if (retval) {
+ dprint_init(mrioc, "%s: driver page 2 read failed\n", __func__);
+ kfree(drvr_page2);
+ goto out;
+ }
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ kfree(mrioc->driver_pg2);
+ mrioc->driver_pg2 = drvr_page2;
+ mrioc->reply_trigger_present = false;
+ mrioc->event_trigger_present = false;
+ mrioc->scsisense_trigger_present = false;
+
+ for (i = 0; (i < mrioc->driver_pg2->num_triggers); i++) {
+ trigger_type = mrioc->driver_pg2->trigger[i].event.type;
+ switch (trigger_type) {
+ case MPI3_DRIVER2_TRIGGER_TYPE_REPLY:
+ mrioc->reply_trigger_present = true;
+ break;
+ case MPI3_DRIVER2_TRIGGER_TYPE_EVENT:
+ mrioc->event_trigger_present = true;
+ break;
+ case MPI3_DRIVER2_TRIGGER_TYPE_SCSI_SENSE:
+ mrioc->scsisense_trigger_present = true;
+ break;
+ default:
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_release_diag_bufs - Release diag buffers
+ * @mrioc: Adapter instance reference
+ * @skip_rel_action: Skip release action and set buffer state
+ *
+ * This function calls helper function to release both trace and
+ * firmware buffers from the controller.
+ *
+ * Return: None
+ */
+void mpi3mr_release_diag_bufs(struct mpi3mr_ioc *mrioc, u8 skip_rel_action)
+{
+ u8 i;
+ struct diag_buffer_desc *diag_buffer;
+
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ diag_buffer = &mrioc->diag_buffers[i];
+ if (!(diag_buffer->addr))
+ continue;
+ if (diag_buffer->status == MPI3MR_HDB_BUFSTATUS_RELEASED)
+ continue;
+ if (!skip_rel_action)
+ mpi3mr_issue_diag_buf_release(mrioc, diag_buffer);
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_RELEASED;
+ atomic64_inc(&event_counter);
+ }
+}
+
+/**
+ * mpi3mr_set_trigger_data_in_hdb - Updates HDB trigger type and
+ * trigger data
+ *
+ * @hdb: HDB pointer
+ * @type: Trigger type
+ * @trigger_data: Pointer to trigger data information
+ * @force: Trigger overwrite flag
+ *
+ * Updates trigger type and trigger data based on parameter
+ * passed to this function
+ *
+ * Return: Nothing
+ */
+void mpi3mr_set_trigger_data_in_hdb(struct diag_buffer_desc *hdb,
+ u8 type, union mpi3mr_trigger_data *trigger_data, bool force)
+{
+ if ((!force) && (hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN))
+ return;
+ hdb->trigger_type = type;
+ if (!trigger_data)
+ memset(&hdb->trigger_data, 0, sizeof(*trigger_data));
+ else
+ memcpy(&hdb->trigger_data, trigger_data, sizeof(*trigger_data));
+}
+
+/**
+ * mpi3mr_set_trigger_data_in_all_hdb - Updates HDB trigger type
+ * and trigger data for all HDB
+ *
+ * @mrioc: Adapter instance reference
+ * @type: Trigger type
+ * @trigger_data: Pointer to trigger data information
+ * @force: Trigger overwrite flag
+ *
+ * Updates trigger type and trigger data based on parameter
+ * passed to this function
+ *
+ * Return: Nothing
+ */
+void mpi3mr_set_trigger_data_in_all_hdb(struct mpi3mr_ioc *mrioc,
+ u8 type, union mpi3mr_trigger_data *trigger_data, bool force)
+{
+ struct diag_buffer_desc *hdb = NULL;
+
+ hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_TRACE);
+ if (hdb)
+ mpi3mr_set_trigger_data_in_hdb(hdb, type, trigger_data, force);
+ hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_FW);
+ if (hdb)
+ mpi3mr_set_trigger_data_in_hdb(hdb, type, trigger_data, force);
+}
+
+/**
+ * mpi3mr_hdbstatuschg_evt_th - HDB status change evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Modifies the status of the applicable diag buffer descriptors
+ *
+ * Return: Nothing
+ */
+void mpi3mr_hdbstatuschg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_diag_buffer_status_change *evtdata;
+ struct diag_buffer_desc *diag_buffer;
+
+ evtdata = (struct mpi3_event_data_diag_buffer_status_change *)
+ event_reply->event_data;
+
+ diag_buffer = mpi3mr_diag_buffer_for_type(mrioc, evtdata->type);
+ if (!diag_buffer)
+ return;
+ if ((diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ return;
+ switch (evtdata->reason_code) {
+ case MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_RELEASED:
+ {
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_RELEASED;
+ mpi3mr_set_trigger_data_in_hdb(diag_buffer,
+ MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED, NULL, 0);
+ atomic64_inc(&event_counter);
+ break;
+ }
+ case MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_RESUMED:
+ {
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED;
+ break;
+ }
+ case MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_PAUSED:
+ {
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED;
+ break;
+ }
+ default:
+ dprint_event_th(mrioc, "%s: unknown reason_code(%d)\n",
+ __func__, evtdata->reason_code);
+ break;
+ }
+}
+
+/**
+ * mpi3mr_diag_buffer_for_type - returns buffer desc for type
+ * @mrioc: Adapter instance reference
+ * @buf_type: Diagnostic buffer type
+ *
+ * Identifies matching diag descriptor from mrioc for given diag
+ * buffer type.
+ *
+ * Return: diag buffer descriptor on success, NULL on failures.
+ */
+
+struct diag_buffer_desc *
+mpi3mr_diag_buffer_for_type(struct mpi3mr_ioc *mrioc, u8 buf_type)
+{
+ u8 i;
+
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ if (mrioc->diag_buffers[i].type == buf_type)
+ return &mrioc->diag_buffers[i];
+ }
+ return NULL;
+}
+
+/**
+ * mpi3mr_bsg_pel_abort - sends PEL abort request
+ * @mrioc: Adapter instance reference
+ *
+ * This function sends PEL abort request to the firmware through
+ * admin request queue.
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int mpi3mr_bsg_pel_abort(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3_pel_req_action_abort pel_abort_req;
+ struct mpi3_pel_reply *pel_reply;
+ int retval = 0;
+ u16 pe_log_status;
+
+ if (mrioc->reset_in_progress) {
+ dprint_bsg_err(mrioc, "%s: reset in progress\n", __func__);
+ return -1;
+ }
+ if (mrioc->stop_bsgs || mrioc->block_on_pci_err) {
+ dprint_bsg_err(mrioc, "%s: bsgs are blocked\n", __func__);
+ return -1;
+ }
+
+ memset(&pel_abort_req, 0, sizeof(pel_abort_req));
+ mutex_lock(&mrioc->pel_abort_cmd.mutex);
+ if (mrioc->pel_abort_cmd.state & MPI3MR_CMD_PENDING) {
+ dprint_bsg_err(mrioc, "%s: command is in use\n", __func__);
+ mutex_unlock(&mrioc->pel_abort_cmd.mutex);
+ return -1;
+ }
+ mrioc->pel_abort_cmd.state = MPI3MR_CMD_PENDING;
+ mrioc->pel_abort_cmd.is_waiting = 1;
+ mrioc->pel_abort_cmd.callback = NULL;
+ pel_abort_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_PEL_ABORT);
+ pel_abort_req.function = MPI3_FUNCTION_PERSISTENT_EVENT_LOG;
+ pel_abort_req.action = MPI3_PEL_ACTION_ABORT;
+ pel_abort_req.abort_host_tag = cpu_to_le16(MPI3MR_HOSTTAG_PEL_WAIT);
+
+ mrioc->pel_abort_requested = 1;
+ init_completion(&mrioc->pel_abort_cmd.done);
+ retval = mpi3mr_admin_request_post(mrioc, &pel_abort_req,
+ sizeof(pel_abort_req), 0);
+ if (retval) {
+ retval = -1;
+ dprint_bsg_err(mrioc, "%s: admin request post failed\n",
+ __func__);
+ mrioc->pel_abort_requested = 0;
+ goto out_unlock;
+ }
+
+ wait_for_completion_timeout(&mrioc->pel_abort_cmd.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->pel_abort_cmd.state & MPI3MR_CMD_COMPLETE)) {
+ mrioc->pel_abort_cmd.is_waiting = 0;
+ dprint_bsg_err(mrioc, "%s: command timedout\n", __func__);
+ if (!(mrioc->pel_abort_cmd.state & MPI3MR_CMD_RESET))
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_PELABORT_TIMEOUT, 1);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->pel_abort_cmd.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_bsg_err(mrioc,
+ "%s: command failed, ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__, (mrioc->pel_abort_cmd.ioc_status &
+ MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->pel_abort_cmd.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ if (mrioc->pel_abort_cmd.state & MPI3MR_CMD_REPLY_VALID) {
+ pel_reply = (struct mpi3_pel_reply *)mrioc->pel_abort_cmd.reply;
+ pe_log_status = le16_to_cpu(pel_reply->pe_log_status);
+ if (pe_log_status != MPI3_PEL_STATUS_SUCCESS) {
+ dprint_bsg_err(mrioc,
+ "%s: command failed, pel_status(0x%04x)\n",
+ __func__, pe_log_status);
+ retval = -1;
+ }
+ }
+
+out_unlock:
+ mrioc->pel_abort_cmd.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->pel_abort_cmd.mutex);
+ return retval;
+}
+/**
+ * mpi3mr_bsg_verify_adapter - verify adapter number is valid
+ * @ioc_number: Adapter number
+ *
+ * This function returns the adapter instance pointer of given
+ * adapter number. If adapter number does not match with the
+ * driver's adapter list, driver returns NULL.
+ *
+ * Return: adapter instance reference
+ */
+static struct mpi3mr_ioc *mpi3mr_bsg_verify_adapter(int ioc_number)
+{
+ struct mpi3mr_ioc *mrioc = NULL;
+
+ spin_lock(&mrioc_list_lock);
+ list_for_each_entry(mrioc, &mrioc_list, list) {
+ if (mrioc->id == ioc_number) {
+ spin_unlock(&mrioc_list_lock);
+ return mrioc;
+ }
+ }
+ spin_unlock(&mrioc_list_lock);
+ return NULL;
+}
+
+/**
+ * mpi3mr_bsg_refresh_hdb_triggers - Refresh HDB trigger data
+ * @mrioc: Adapter instance reference
+ * @job: BSG Job pointer
+ *
+ * This function reads the controller trigger config page as
+ * defined by the input page type and refreshes the driver's
+ * local trigger information structures with the controller's
+ * config page data.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long
+mpi3mr_bsg_refresh_hdb_triggers(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_bsg_out_refresh_hdb_triggers refresh_triggers;
+ uint32_t data_out_sz;
+ u8 page_action;
+ long rval = -EINVAL;
+
+ data_out_sz = job->request_payload.payload_len;
+
+ if (data_out_sz != sizeof(refresh_triggers)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return rval;
+ }
+
+ if (mrioc->unrecoverable) {
+ dprint_bsg_err(mrioc, "%s: unrecoverable controller\n",
+ __func__);
+ return -EFAULT;
+ }
+ if (mrioc->reset_in_progress) {
+ dprint_bsg_err(mrioc, "%s: reset in progress\n", __func__);
+ return -EAGAIN;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &refresh_triggers, sizeof(refresh_triggers));
+
+ switch (refresh_triggers.page_type) {
+ case MPI3MR_HDB_REFRESH_TYPE_CURRENT:
+ page_action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ break;
+ case MPI3MR_HDB_REFRESH_TYPE_DEFAULT:
+ page_action = MPI3_CONFIG_ACTION_READ_DEFAULT;
+ break;
+ case MPI3MR_HDB_HDB_REFRESH_TYPE_PERSISTENT:
+ page_action = MPI3_CONFIG_ACTION_READ_PERSISTENT;
+ break;
+ default:
+ dprint_bsg_err(mrioc,
+ "%s: unsupported refresh trigger, page_type %d\n",
+ __func__, refresh_triggers.page_type);
+ return rval;
+ }
+ rval = mpi3mr_refresh_trigger(mrioc, page_action);
+
+ return rval;
+}
+
+/**
+ * mpi3mr_bsg_upload_hdb - Upload a specific HDB to user space
+ * @mrioc: Adapter instance reference
+ * @job: BSG Job pointer
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_upload_hdb(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_bsg_out_upload_hdb upload_hdb;
+ struct diag_buffer_desc *diag_buffer;
+ uint32_t data_out_size;
+ uint32_t data_in_size;
+
+ data_out_size = job->request_payload.payload_len;
+ data_in_size = job->reply_payload.payload_len;
+
+ if (data_out_size != sizeof(upload_hdb)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &upload_hdb, sizeof(upload_hdb));
+
+ if ((!upload_hdb.length) || (data_in_size != upload_hdb.length)) {
+ dprint_bsg_err(mrioc, "%s: invalid length argument\n",
+ __func__);
+ return -EINVAL;
+ }
+ diag_buffer = mpi3mr_diag_buffer_for_type(mrioc, upload_hdb.buf_type);
+ if ((!diag_buffer) || (!diag_buffer->addr)) {
+ dprint_bsg_err(mrioc, "%s: invalid buffer type %d\n",
+ __func__, upload_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if ((diag_buffer->status != MPI3MR_HDB_BUFSTATUS_RELEASED) &&
+ (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED)) {
+ dprint_bsg_err(mrioc,
+ "%s: invalid buffer status %d for type %d\n",
+ __func__, diag_buffer->status, upload_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if ((upload_hdb.start_offset + upload_hdb.length) > diag_buffer->size) {
+ dprint_bsg_err(mrioc,
+ "%s: invalid start offset %d, length %d for type %d\n",
+ __func__, upload_hdb.start_offset, upload_hdb.length,
+ upload_hdb.buf_type);
+ return -EINVAL;
+ }
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ (diag_buffer->addr + upload_hdb.start_offset),
+ data_in_size);
+ return 0;
+}
+
+/**
+ * mpi3mr_bsg_repost_hdb - Re-post HDB
+ * @mrioc: Adapter instance reference
+ * @job: BSG job pointer
+ *
+ * This function retrieves the HDB descriptor corresponding to a
+ * given buffer type and if the HDB is in released status then
+ * posts the HDB with the firmware.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_repost_hdb(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_bsg_out_repost_hdb repost_hdb;
+ struct diag_buffer_desc *diag_buffer;
+ uint32_t data_out_sz;
+
+ data_out_sz = job->request_payload.payload_len;
+
+ if (data_out_sz != sizeof(repost_hdb)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (mrioc->unrecoverable) {
+ dprint_bsg_err(mrioc, "%s: unrecoverable controller\n",
+ __func__);
+ return -EFAULT;
+ }
+ if (mrioc->reset_in_progress) {
+ dprint_bsg_err(mrioc, "%s: reset in progress\n", __func__);
+ return -EAGAIN;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &repost_hdb, sizeof(repost_hdb));
+
+ diag_buffer = mpi3mr_diag_buffer_for_type(mrioc, repost_hdb.buf_type);
+ if ((!diag_buffer) || (!diag_buffer->addr)) {
+ dprint_bsg_err(mrioc, "%s: invalid buffer type %d\n",
+ __func__, repost_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_RELEASED) {
+ dprint_bsg_err(mrioc,
+ "%s: invalid buffer status %d for type %d\n",
+ __func__, diag_buffer->status, repost_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if (mpi3mr_issue_diag_buf_post(mrioc, diag_buffer)) {
+ dprint_bsg_err(mrioc, "%s: post failed for type %d\n",
+ __func__, repost_hdb.buf_type);
+ return -EFAULT;
+ }
+ mpi3mr_set_trigger_data_in_hdb(diag_buffer,
+ MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
+
+ return 0;
+}
+
+/**
+ * mpi3mr_bsg_query_hdb - Handler for query HDB command
+ * @mrioc: Adapter instance reference
+ * @job: BSG job pointer
+ *
+ * This function prepares and copies the host diagnostic buffer
+ * entries to the user buffer.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_query_hdb(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ long rval = 0;
+ struct mpi3mr_bsg_in_hdb_status *hbd_status;
+ struct mpi3mr_hdb_entry *hbd_status_entry;
+ u32 length, min_length;
+ u8 i;
+ struct diag_buffer_desc *diag_buffer;
+ uint32_t data_in_sz = 0;
+
+ data_in_sz = job->request_payload.payload_len;
+
+ length = (sizeof(*hbd_status) + ((MPI3MR_MAX_NUM_HDB - 1) *
+ sizeof(*hbd_status_entry)));
+ hbd_status = kmalloc(length, GFP_KERNEL);
+ if (!hbd_status)
+ return -ENOMEM;
+ hbd_status_entry = &hbd_status->entry[0];
+
+ hbd_status->num_hdb_types = MPI3MR_MAX_NUM_HDB;
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ diag_buffer = &mrioc->diag_buffers[i];
+ hbd_status_entry->buf_type = diag_buffer->type;
+ hbd_status_entry->status = diag_buffer->status;
+ hbd_status_entry->trigger_type = diag_buffer->trigger_type;
+ memcpy(&hbd_status_entry->trigger_data,
+ &diag_buffer->trigger_data,
+ sizeof(hbd_status_entry->trigger_data));
+ hbd_status_entry->size = (diag_buffer->size / 1024);
+ hbd_status_entry++;
+ }
+ hbd_status->element_trigger_format =
+ MPI3MR_HDB_QUERY_ELEMENT_TRIGGER_FORMAT_DATA;
+
+ if (data_in_sz < 4) {
+ dprint_bsg_err(mrioc, "%s: invalid size passed\n", __func__);
+ rval = -EINVAL;
+ goto out;
+ }
+ min_length = min(data_in_sz, length);
+ if (job->request_payload.payload_len >= min_length) {
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ hbd_status, min_length);
+ rval = 0;
+ }
+out:
+ kfree(hbd_status);
+ return rval;
+}
+
+
+/**
+ * mpi3mr_enable_logdata - Handler for log data enable
+ * @mrioc: Adapter instance reference
+ * @job: BSG job reference
+ *
+ * This function enables log data caching in the driver if not
+ * already enabled and return the maximum number of log data
+ * entries that can be cached in the driver.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_enable_logdata(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_logdata_enable logdata_enable;
+
+ if (!mrioc->logdata_buf) {
+ mrioc->logdata_entry_sz =
+ (mrioc->reply_sz - (sizeof(struct mpi3_event_notification_reply) - 4))
+ + MPI3MR_BSG_LOGDATA_ENTRY_HEADER_SZ;
+ mrioc->logdata_buf_idx = 0;
+ mrioc->logdata_buf = kcalloc(MPI3MR_BSG_LOGDATA_MAX_ENTRIES,
+ mrioc->logdata_entry_sz, GFP_KERNEL);
+
+ if (!mrioc->logdata_buf)
+ return -ENOMEM;
+ }
+
+ memset(&logdata_enable, 0, sizeof(logdata_enable));
+ logdata_enable.max_entries =
+ MPI3MR_BSG_LOGDATA_MAX_ENTRIES;
+ if (job->request_payload.payload_len >= sizeof(logdata_enable)) {
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &logdata_enable, sizeof(logdata_enable));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+/**
+ * mpi3mr_get_logdata - Handler for get log data
+ * @mrioc: Adapter instance reference
+ * @job: BSG job pointer
+ * This function copies the log data entries to the user buffer
+ * when log caching is enabled in the driver.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_get_logdata(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ u16 num_entries, sz, entry_sz = mrioc->logdata_entry_sz;
+
+ if ((!mrioc->logdata_buf) || (job->request_payload.payload_len < entry_sz))
+ return -EINVAL;
+
+ num_entries = job->request_payload.payload_len / entry_sz;
+ if (num_entries > MPI3MR_BSG_LOGDATA_MAX_ENTRIES)
+ num_entries = MPI3MR_BSG_LOGDATA_MAX_ENTRIES;
+ sz = num_entries * entry_sz;
+
+ if (job->request_payload.payload_len >= sz) {
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ mrioc->logdata_buf, sz);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * mpi3mr_bsg_pel_enable - Handler for PEL enable driver
+ * @mrioc: Adapter instance reference
+ * @job: BSG job pointer
+ *
+ * This function is the handler for PEL enable driver.
+ * Validates the application given class and locale and if
+ * requires aborts the existing PEL wait request and/or issues
+ * new PEL wait request to the firmware and returns.
+ *
+ * Return: 0 on success and proper error codes on failure.
+ */
+static long mpi3mr_bsg_pel_enable(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ long rval = -EINVAL;
+ struct mpi3mr_bsg_out_pel_enable pel_enable;
+ u8 issue_pel_wait;
+ u8 tmp_class;
+ u16 tmp_locale;
+
+ if (job->request_payload.payload_len != sizeof(pel_enable)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return rval;
+ }
+
+ if (mrioc->unrecoverable) {
+ dprint_bsg_err(mrioc, "%s: unrecoverable controller\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (mrioc->reset_in_progress) {
+ dprint_bsg_err(mrioc, "%s: reset in progress\n", __func__);
+ return -EAGAIN;
+ }
+
+ if (mrioc->stop_bsgs) {
+ dprint_bsg_err(mrioc, "%s: bsgs are blocked\n", __func__);
+ return -EAGAIN;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &pel_enable, sizeof(pel_enable));
+
+ if (pel_enable.pel_class > MPI3_PEL_CLASS_FAULT) {
+ dprint_bsg_err(mrioc, "%s: out of range class %d sent\n",
+ __func__, pel_enable.pel_class);
+ rval = 0;
+ goto out;
+ }
+ if (!mrioc->pel_enabled)
+ issue_pel_wait = 1;
+ else {
+ if ((mrioc->pel_class <= pel_enable.pel_class) &&
+ !((mrioc->pel_locale & pel_enable.pel_locale) ^
+ pel_enable.pel_locale)) {
+ issue_pel_wait = 0;
+ rval = 0;
+ } else {
+ pel_enable.pel_locale |= mrioc->pel_locale;
+
+ if (mrioc->pel_class < pel_enable.pel_class)
+ pel_enable.pel_class = mrioc->pel_class;
+
+ rval = mpi3mr_bsg_pel_abort(mrioc);
+ if (rval) {
+ dprint_bsg_err(mrioc,
+ "%s: pel_abort failed, status(%ld)\n",
+ __func__, rval);
+ goto out;
+ }
+ issue_pel_wait = 1;
+ }
+ }
+ if (issue_pel_wait) {
+ tmp_class = mrioc->pel_class;
+ tmp_locale = mrioc->pel_locale;
+ mrioc->pel_class = pel_enable.pel_class;
+ mrioc->pel_locale = pel_enable.pel_locale;
+ mrioc->pel_enabled = 1;
+ rval = mpi3mr_pel_get_seqnum_post(mrioc, NULL);
+ if (rval) {
+ mrioc->pel_class = tmp_class;
+ mrioc->pel_locale = tmp_locale;
+ mrioc->pel_enabled = 0;
+ dprint_bsg_err(mrioc,
+ "%s: pel get sequence number failed, status(%ld)\n",
+ __func__, rval);
+ }
+ }
+
+out:
+ return rval;
+}
+/**
+ * mpi3mr_get_all_tgt_info - Get all target information
+ * @mrioc: Adapter instance reference
+ * @job: BSG job reference
+ *
+ * This function copies the driver managed target devices device
+ * handle, persistent ID, bus ID and taret ID to the user
+ * provided buffer for the specific controller. This function
+ * also provides the number of devices managed by the driver for
+ * the specific controller.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_get_all_tgt_info(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ u16 num_devices = 0, i = 0, size;
+ unsigned long flags;
+ struct mpi3mr_tgt_dev *tgtdev;
+ struct mpi3mr_device_map_info *devmap_info = NULL;
+ struct mpi3mr_all_tgt_info *alltgt_info = NULL;
+ uint32_t min_entrylen = 0, kern_entrylen = 0, usr_entrylen = 0;
+
+ if (job->request_payload.payload_len < sizeof(u32)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+ num_devices++;
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ if ((job->request_payload.payload_len <= sizeof(u64)) ||
+ list_empty(&mrioc->tgtdev_list)) {
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &num_devices, sizeof(num_devices));
+ return 0;
+ }
+
+ kern_entrylen = num_devices * sizeof(*devmap_info);
+ size = sizeof(u64) + kern_entrylen;
+ alltgt_info = kzalloc(size, GFP_KERNEL);
+ if (!alltgt_info)
+ return -ENOMEM;
+
+ devmap_info = alltgt_info->dmi;
+ memset((u8 *)devmap_info, 0xFF, kern_entrylen);
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+ if (i < num_devices) {
+ devmap_info[i].handle = tgtdev->dev_handle;
+ devmap_info[i].perst_id = tgtdev->perst_id;
+ if (tgtdev->host_exposed && tgtdev->starget) {
+ devmap_info[i].target_id = tgtdev->starget->id;
+ devmap_info[i].bus_id =
+ tgtdev->starget->channel;
+ }
+ i++;
+ }
+ }
+ num_devices = i;
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ alltgt_info->num_devices = num_devices;
+
+ usr_entrylen = (job->request_payload.payload_len - sizeof(u64)) /
+ sizeof(*devmap_info);
+ usr_entrylen *= sizeof(*devmap_info);
+ min_entrylen = min(usr_entrylen, kern_entrylen);
+
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ alltgt_info, (min_entrylen + sizeof(u64)));
+ kfree(alltgt_info);
+ return 0;
+}
+/**
+ * mpi3mr_get_change_count - Get topology change count
+ * @mrioc: Adapter instance reference
+ * @job: BSG job reference
+ *
+ * This function copies the toplogy change count provided by the
+ * driver in events and cached in the driver to the user
+ * provided buffer for the specific controller.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_get_change_count(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_change_count chgcnt;
+
+ memset(&chgcnt, 0, sizeof(chgcnt));
+ chgcnt.change_count = mrioc->change_count;
+ if (job->request_payload.payload_len >= sizeof(chgcnt)) {
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &chgcnt, sizeof(chgcnt));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * mpi3mr_bsg_adp_reset - Issue controller reset
+ * @mrioc: Adapter instance reference
+ * @job: BSG job reference
+ *
+ * This function identifies the user provided reset type and
+ * issues approporiate reset to the controller and wait for that
+ * to complete and reinitialize the controller and then returns
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_adp_reset(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ long rval = -EINVAL;
+ u8 save_snapdump;
+ struct mpi3mr_bsg_adp_reset adpreset;
+
+ if (job->request_payload.payload_len !=
+ sizeof(adpreset)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ goto out;
+ }
+
+ if (mrioc->unrecoverable || mrioc->block_on_pci_err)
+ return -EINVAL;
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &adpreset, sizeof(adpreset));
+
+ switch (adpreset.reset_type) {
+ case MPI3MR_BSG_ADPRESET_SOFT:
+ save_snapdump = 0;
+ break;
+ case MPI3MR_BSG_ADPRESET_DIAG_FAULT:
+ save_snapdump = 1;
+ break;
+ default:
+ dprint_bsg_err(mrioc, "%s: unknown reset_type(%d)\n",
+ __func__, adpreset.reset_type);
+ goto out;
+ }
+
+ rval = mpi3mr_soft_reset_handler(mrioc, MPI3MR_RESET_FROM_APP,
+ save_snapdump);
+
+ if (rval)
+ dprint_bsg_err(mrioc,
+ "%s: reset handler returned error(%ld) for reset type %d\n",
+ __func__, rval, adpreset.reset_type);
+out:
+ return rval;
+}
+
+/**
+ * mpi3mr_bsg_populate_adpinfo - Get adapter info command handler
+ * @mrioc: Adapter instance reference
+ * @job: BSG job reference
+ *
+ * This function provides adapter information for the given
+ * controller
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_populate_adpinfo(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ enum mpi3mr_iocstate ioc_state;
+ struct mpi3mr_bsg_in_adpinfo adpinfo;
+
+ memset(&adpinfo, 0, sizeof(adpinfo));
+ adpinfo.adp_type = MPI3MR_BSG_ADPTYPE_AVGFAMILY;
+ adpinfo.pci_dev_id = mrioc->pdev->device;
+ adpinfo.pci_dev_hw_rev = mrioc->pdev->revision;
+ adpinfo.pci_subsys_dev_id = mrioc->pdev->subsystem_device;
+ adpinfo.pci_subsys_ven_id = mrioc->pdev->subsystem_vendor;
+ adpinfo.pci_bus = mrioc->pdev->bus->number;
+ adpinfo.pci_dev = PCI_SLOT(mrioc->pdev->devfn);
+ adpinfo.pci_func = PCI_FUNC(mrioc->pdev->devfn);
+ adpinfo.pci_seg_id = pci_domain_nr(mrioc->pdev->bus);
+ adpinfo.app_intfc_ver = MPI3MR_IOCTL_VERSION;
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state == MRIOC_STATE_UNRECOVERABLE)
+ adpinfo.adp_state = MPI3MR_BSG_ADPSTATE_UNRECOVERABLE;
+ else if ((mrioc->reset_in_progress) || (mrioc->stop_bsgs))
+ adpinfo.adp_state = MPI3MR_BSG_ADPSTATE_IN_RESET;
+ else if (ioc_state == MRIOC_STATE_FAULT)
+ adpinfo.adp_state = MPI3MR_BSG_ADPSTATE_FAULT;
+ else
+ adpinfo.adp_state = MPI3MR_BSG_ADPSTATE_OPERATIONAL;
+
+ memcpy((u8 *)&adpinfo.driver_info, (u8 *)&mrioc->driver_info,
+ sizeof(adpinfo.driver_info));
+
+ if (job->request_payload.payload_len >= sizeof(adpinfo)) {
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &adpinfo, sizeof(adpinfo));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * mpi3mr_bsg_process_drv_cmds - Driver Command handler
+ * @job: BSG job reference
+ *
+ * This function is the top level handler for driver commands,
+ * this does basic validation of the buffer and identifies the
+ * opcode and switches to correct sub handler.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_process_drv_cmds(struct bsg_job *job)
+{
+ long rval = -EINVAL;
+ struct mpi3mr_ioc *mrioc = NULL;
+ struct mpi3mr_bsg_packet *bsg_req = NULL;
+ struct mpi3mr_bsg_drv_cmd *drvrcmd = NULL;
+
+ bsg_req = job->request;
+ drvrcmd = &bsg_req->cmd.drvrcmd;
+
+ mrioc = mpi3mr_bsg_verify_adapter(drvrcmd->mrioc_id);
+ if (!mrioc)
+ return -ENODEV;
+
+ if (drvrcmd->opcode == MPI3MR_DRVBSG_OPCODE_ADPINFO) {
+ rval = mpi3mr_bsg_populate_adpinfo(mrioc, job);
+ return rval;
+ }
+
+ if (mutex_lock_interruptible(&mrioc->bsg_cmds.mutex))
+ return -ERESTARTSYS;
+
+ switch (drvrcmd->opcode) {
+ case MPI3MR_DRVBSG_OPCODE_ADPRESET:
+ rval = mpi3mr_bsg_adp_reset(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_ALLTGTDEVINFO:
+ rval = mpi3mr_get_all_tgt_info(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_GETCHGCNT:
+ rval = mpi3mr_get_change_count(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_LOGDATAENABLE:
+ rval = mpi3mr_enable_logdata(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_GETLOGDATA:
+ rval = mpi3mr_get_logdata(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_PELENABLE:
+ rval = mpi3mr_bsg_pel_enable(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_QUERY_HDB:
+ rval = mpi3mr_bsg_query_hdb(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_REPOST_HDB:
+ rval = mpi3mr_bsg_repost_hdb(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_UPLOAD_HDB:
+ rval = mpi3mr_bsg_upload_hdb(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_REFRESH_HDB_TRIGGERS:
+ rval = mpi3mr_bsg_refresh_hdb_triggers(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_UNKNOWN:
+ default:
+ pr_err("%s: unsupported driver command opcode %d\n",
+ MPI3MR_DRIVER_NAME, drvrcmd->opcode);
+ break;
+ }
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ return rval;
+}
+
+/**
+ * mpi3mr_total_num_ioctl_sges - Count number of SGEs required
+ * @drv_bufs: DMA address of the buffers to be placed in sgl
+ * @bufcnt: Number of DMA buffers
+ *
+ * This function returns total number of data SGEs required
+ * including zero length SGEs and excluding management request
+ * and response buffer for the given list of data buffer
+ * descriptors
+ *
+ * Return: Number of SGE elements needed
+ */
+static inline u16 mpi3mr_total_num_ioctl_sges(struct mpi3mr_buf_map *drv_bufs,
+ u8 bufcnt)
+{
+ u16 i, sge_count = 0;
+
+ for (i = 0; i < bufcnt; i++, drv_bufs++) {
+ if (drv_bufs->data_dir == DMA_NONE ||
+ drv_bufs->kern_buf)
+ continue;
+ sge_count += drv_bufs->num_dma_desc;
+ if (!drv_bufs->num_dma_desc)
+ sge_count++;
+ }
+ return sge_count;
+}
+
+/**
+ * mpi3mr_bsg_build_sgl - SGL construction for MPI commands
+ * @mrioc: Adapter instance reference
+ * @mpi_req: MPI request
+ * @sgl_offset: offset to start sgl in the MPI request
+ * @drv_bufs: DMA address of the buffers to be placed in sgl
+ * @bufcnt: Number of DMA buffers
+ * @is_rmc: Does the buffer list has management command buffer
+ * @is_rmr: Does the buffer list has management response buffer
+ * @num_datasges: Number of data buffers in the list
+ *
+ * This function places the DMA address of the given buffers in
+ * proper format as SGEs in the given MPI request.
+ *
+ * Return: 0 on success,-1 on failure
+ */
+static int mpi3mr_bsg_build_sgl(struct mpi3mr_ioc *mrioc, u8 *mpi_req,
+ u32 sgl_offset, struct mpi3mr_buf_map *drv_bufs,
+ u8 bufcnt, u8 is_rmc, u8 is_rmr, u8 num_datasges)
+{
+ struct mpi3_request_header *mpi_header =
+ (struct mpi3_request_header *)mpi_req;
+ u8 *sgl = (mpi_req + sgl_offset), count = 0;
+ struct mpi3_mgmt_passthrough_request *rmgmt_req =
+ (struct mpi3_mgmt_passthrough_request *)mpi_req;
+ struct mpi3mr_buf_map *drv_buf_iter = drv_bufs;
+ u8 flag, sgl_flags, sgl_flag_eob, sgl_flags_last, last_chain_sgl_flag;
+ u16 available_sges, i, sges_needed;
+ u32 sge_element_size = sizeof(struct mpi3_sge_common);
+ bool chain_used = false;
+
+ sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE |
+ MPI3_SGE_FLAGS_DLAS_SYSTEM;
+ sgl_flag_eob = sgl_flags | MPI3_SGE_FLAGS_END_OF_BUFFER;
+ sgl_flags_last = sgl_flag_eob | MPI3_SGE_FLAGS_END_OF_LIST;
+ last_chain_sgl_flag = MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN |
+ MPI3_SGE_FLAGS_DLAS_SYSTEM;
+
+ sges_needed = mpi3mr_total_num_ioctl_sges(drv_bufs, bufcnt);
+
+ if (is_rmc) {
+ mpi3mr_add_sg_single(&rmgmt_req->command_sgl,
+ sgl_flags_last, drv_buf_iter->kern_buf_len,
+ drv_buf_iter->kern_buf_dma);
+ sgl = (u8 *)drv_buf_iter->kern_buf +
+ drv_buf_iter->bsg_buf_len;
+ available_sges = (drv_buf_iter->kern_buf_len -
+ drv_buf_iter->bsg_buf_len) / sge_element_size;
+
+ if (sges_needed > available_sges)
+ return -1;
+
+ chain_used = true;
+ drv_buf_iter++;
+ count++;
+ if (is_rmr) {
+ mpi3mr_add_sg_single(&rmgmt_req->response_sgl,
+ sgl_flags_last, drv_buf_iter->kern_buf_len,
+ drv_buf_iter->kern_buf_dma);
+ drv_buf_iter++;
+ count++;
+ } else
+ mpi3mr_build_zero_len_sge(
+ &rmgmt_req->response_sgl);
+ if (num_datasges) {
+ i = 0;
+ goto build_sges;
+ }
+ } else {
+ if (sgl_offset >= MPI3MR_ADMIN_REQ_FRAME_SZ)
+ return -1;
+ available_sges = (MPI3MR_ADMIN_REQ_FRAME_SZ - sgl_offset) /
+ sge_element_size;
+ if (!available_sges)
+ return -1;
+ }
+ if (!num_datasges) {
+ mpi3mr_build_zero_len_sge(sgl);
+ return 0;
+ }
+ if (mpi_header->function == MPI3_BSG_FUNCTION_SMP_PASSTHROUGH) {
+ if ((sges_needed > 2) || (sges_needed > available_sges))
+ return -1;
+ for (; count < bufcnt; count++, drv_buf_iter++) {
+ if (drv_buf_iter->data_dir == DMA_NONE ||
+ !drv_buf_iter->num_dma_desc)
+ continue;
+ mpi3mr_add_sg_single(sgl, sgl_flags_last,
+ drv_buf_iter->dma_desc[0].size,
+ drv_buf_iter->dma_desc[0].dma_addr);
+ sgl += sge_element_size;
+ }
+ return 0;
+ }
+ i = 0;
+
+build_sges:
+ for (; count < bufcnt; count++, drv_buf_iter++) {
+ if (drv_buf_iter->data_dir == DMA_NONE)
+ continue;
+ if (!drv_buf_iter->num_dma_desc) {
+ if (chain_used && !available_sges)
+ return -1;
+ if (!chain_used && (available_sges == 1) &&
+ (sges_needed > 1))
+ goto setup_chain;
+ flag = sgl_flag_eob;
+ if (num_datasges == 1)
+ flag = sgl_flags_last;
+ mpi3mr_add_sg_single(sgl, flag, 0, 0);
+ sgl += sge_element_size;
+ sges_needed--;
+ available_sges--;
+ num_datasges--;
+ continue;
+ }
+ for (; i < drv_buf_iter->num_dma_desc; i++) {
+ if (chain_used && !available_sges)
+ return -1;
+ if (!chain_used && (available_sges == 1) &&
+ (sges_needed > 1))
+ goto setup_chain;
+ flag = sgl_flags;
+ if (i == (drv_buf_iter->num_dma_desc - 1)) {
+ if (num_datasges == 1)
+ flag = sgl_flags_last;
+ else
+ flag = sgl_flag_eob;
+ }
+
+ mpi3mr_add_sg_single(sgl, flag,
+ drv_buf_iter->dma_desc[i].size,
+ drv_buf_iter->dma_desc[i].dma_addr);
+ sgl += sge_element_size;
+ available_sges--;
+ sges_needed--;
+ }
+ num_datasges--;
+ i = 0;
+ }
+ return 0;
+
+setup_chain:
+ available_sges = mrioc->ioctl_chain_sge.size / sge_element_size;
+ if (sges_needed > available_sges)
+ return -1;
+ mpi3mr_add_sg_single(sgl, last_chain_sgl_flag,
+ (sges_needed * sge_element_size),
+ mrioc->ioctl_chain_sge.dma_addr);
+ memset(mrioc->ioctl_chain_sge.addr, 0, mrioc->ioctl_chain_sge.size);
+ sgl = (u8 *)mrioc->ioctl_chain_sge.addr;
+ chain_used = true;
+ goto build_sges;
+}
+
+/**
+ * mpi3mr_get_nvme_data_fmt - returns the NVMe data format
+ * @nvme_encap_request: NVMe encapsulated MPI request
+ *
+ * This function returns the type of the data format specified
+ * in user provided NVMe command in NVMe encapsulated request.
+ *
+ * Return: Data format of the NVMe command (PRP/SGL etc)
+ */
+static unsigned int mpi3mr_get_nvme_data_fmt(
+ struct mpi3_nvme_encapsulated_request *nvme_encap_request)
+{
+ u8 format = 0;
+
+ format = ((nvme_encap_request->command[0] & 0xc000) >> 14);
+ return format;
+
+}
+
+/**
+ * mpi3mr_build_nvme_sgl - SGL constructor for NVME
+ * encapsulated request
+ * @mrioc: Adapter instance reference
+ * @nvme_encap_request: NVMe encapsulated MPI request
+ * @drv_bufs: DMA address of the buffers to be placed in sgl
+ * @bufcnt: Number of DMA buffers
+ *
+ * This function places the DMA address of the given buffers in
+ * proper format as SGEs in the given NVMe encapsulated request.
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int mpi3mr_build_nvme_sgl(struct mpi3mr_ioc *mrioc,
+ struct mpi3_nvme_encapsulated_request *nvme_encap_request,
+ struct mpi3mr_buf_map *drv_bufs, u8 bufcnt)
+{
+ struct mpi3mr_nvme_pt_sge *nvme_sgl;
+ __le64 sgl_dma;
+ u8 count;
+ size_t length = 0;
+ u16 available_sges = 0, i;
+ u32 sge_element_size = sizeof(struct mpi3mr_nvme_pt_sge);
+ struct mpi3mr_buf_map *drv_buf_iter = drv_bufs;
+ u64 sgemod_mask = ((u64)((mrioc->facts.sge_mod_mask) <<
+ mrioc->facts.sge_mod_shift) << 32);
+ u64 sgemod_val = ((u64)(mrioc->facts.sge_mod_value) <<
+ mrioc->facts.sge_mod_shift) << 32;
+ u32 size;
+
+ nvme_sgl = (struct mpi3mr_nvme_pt_sge *)
+ ((u8 *)(nvme_encap_request->command) + MPI3MR_NVME_CMD_SGL_OFFSET);
+
+ /*
+ * Not all commands require a data transfer. If no data, just return
+ * without constructing any sgl.
+ */
+ for (count = 0; count < bufcnt; count++, drv_buf_iter++) {
+ if (drv_buf_iter->data_dir == DMA_NONE)
+ continue;
+ length = drv_buf_iter->kern_buf_len;
+ break;
+ }
+ if (!length || !drv_buf_iter->num_dma_desc)
+ return 0;
+
+ if (drv_buf_iter->num_dma_desc == 1) {
+ available_sges = 1;
+ goto build_sges;
+ }
+
+ sgl_dma = cpu_to_le64(mrioc->ioctl_chain_sge.dma_addr);
+ if (sgl_dma & sgemod_mask) {
+ dprint_bsg_err(mrioc,
+ "%s: SGL chain address collides with SGE modifier\n",
+ __func__);
+ return -1;
+ }
+
+ sgl_dma &= ~sgemod_mask;
+ sgl_dma |= sgemod_val;
+
+ memset(mrioc->ioctl_chain_sge.addr, 0, mrioc->ioctl_chain_sge.size);
+ available_sges = mrioc->ioctl_chain_sge.size / sge_element_size;
+ if (available_sges < drv_buf_iter->num_dma_desc)
+ return -1;
+ memset(nvme_sgl, 0, sizeof(struct mpi3mr_nvme_pt_sge));
+ nvme_sgl->base_addr = sgl_dma;
+ size = drv_buf_iter->num_dma_desc * sizeof(struct mpi3mr_nvme_pt_sge);
+ nvme_sgl->length = cpu_to_le32(size);
+ nvme_sgl->type = MPI3MR_NVMESGL_LAST_SEGMENT;
+ nvme_sgl = (struct mpi3mr_nvme_pt_sge *)mrioc->ioctl_chain_sge.addr;
+
+build_sges:
+ for (i = 0; i < drv_buf_iter->num_dma_desc; i++) {
+ sgl_dma = cpu_to_le64(drv_buf_iter->dma_desc[i].dma_addr);
+ if (sgl_dma & sgemod_mask) {
+ dprint_bsg_err(mrioc,
+ "%s: SGL address collides with SGE modifier\n",
+ __func__);
+ return -1;
+ }
+
+ sgl_dma &= ~sgemod_mask;
+ sgl_dma |= sgemod_val;
+
+ nvme_sgl->base_addr = sgl_dma;
+ nvme_sgl->length = cpu_to_le32(drv_buf_iter->dma_desc[i].size);
+ nvme_sgl->type = MPI3MR_NVMESGL_DATA_SEGMENT;
+ nvme_sgl++;
+ available_sges--;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_build_nvme_prp - PRP constructor for NVME
+ * encapsulated request
+ * @mrioc: Adapter instance reference
+ * @nvme_encap_request: NVMe encapsulated MPI request
+ * @drv_bufs: DMA address of the buffers to be placed in SGL
+ * @bufcnt: Number of DMA buffers
+ *
+ * This function places the DMA address of the given buffers in
+ * proper format as PRP entries in the given NVMe encapsulated
+ * request.
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int mpi3mr_build_nvme_prp(struct mpi3mr_ioc *mrioc,
+ struct mpi3_nvme_encapsulated_request *nvme_encap_request,
+ struct mpi3mr_buf_map *drv_bufs, u8 bufcnt)
+{
+ int prp_size = MPI3MR_NVME_PRP_SIZE;
+ __le64 *prp_entry, *prp1_entry, *prp2_entry;
+ __le64 *prp_page;
+ dma_addr_t prp_entry_dma, prp_page_dma, dma_addr;
+ u32 offset, entry_len, dev_pgsz;
+ u32 page_mask_result, page_mask;
+ size_t length = 0, desc_len;
+ u8 count;
+ struct mpi3mr_buf_map *drv_buf_iter = drv_bufs;
+ u64 sgemod_mask = ((u64)((mrioc->facts.sge_mod_mask) <<
+ mrioc->facts.sge_mod_shift) << 32);
+ u64 sgemod_val = ((u64)(mrioc->facts.sge_mod_value) <<
+ mrioc->facts.sge_mod_shift) << 32;
+ u16 dev_handle = nvme_encap_request->dev_handle;
+ struct mpi3mr_tgt_dev *tgtdev;
+ u16 desc_count = 0;
+
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (!tgtdev) {
+ dprint_bsg_err(mrioc, "%s: invalid device handle 0x%04x\n",
+ __func__, dev_handle);
+ return -1;
+ }
+
+ if (tgtdev->dev_spec.pcie_inf.pgsz == 0) {
+ dprint_bsg_err(mrioc,
+ "%s: NVMe device page size is zero for handle 0x%04x\n",
+ __func__, dev_handle);
+ mpi3mr_tgtdev_put(tgtdev);
+ return -1;
+ }
+
+ dev_pgsz = 1 << (tgtdev->dev_spec.pcie_inf.pgsz);
+ mpi3mr_tgtdev_put(tgtdev);
+ page_mask = dev_pgsz - 1;
+
+ if (dev_pgsz > MPI3MR_IOCTL_SGE_SIZE) {
+ dprint_bsg_err(mrioc,
+ "%s: NVMe device page size(%d) is greater than ioctl data sge size(%d) for handle 0x%04x\n",
+ __func__, dev_pgsz, MPI3MR_IOCTL_SGE_SIZE, dev_handle);
+ return -1;
+ }
+
+ if (MPI3MR_IOCTL_SGE_SIZE % dev_pgsz) {
+ dprint_bsg_err(mrioc,
+ "%s: ioctl data sge size(%d) is not a multiple of NVMe device page size(%d) for handle 0x%04x\n",
+ __func__, MPI3MR_IOCTL_SGE_SIZE, dev_pgsz, dev_handle);
+ return -1;
+ }
+
+ /*
+ * Not all commands require a data transfer. If no data, just return
+ * without constructing any PRP.
+ */
+ for (count = 0; count < bufcnt; count++, drv_buf_iter++) {
+ if (drv_buf_iter->data_dir == DMA_NONE)
+ continue;
+ length = drv_buf_iter->kern_buf_len;
+ break;
+ }
+
+ if (!length || !drv_buf_iter->num_dma_desc)
+ return 0;
+
+ for (count = 0; count < drv_buf_iter->num_dma_desc; count++) {
+ dma_addr = drv_buf_iter->dma_desc[count].dma_addr;
+ if (dma_addr & page_mask) {
+ dprint_bsg_err(mrioc,
+ "%s:dma_addr %pad is not aligned with page size 0x%x\n",
+ __func__, &dma_addr, dev_pgsz);
+ return -1;
+ }
+ }
+
+ dma_addr = drv_buf_iter->dma_desc[0].dma_addr;
+ desc_len = drv_buf_iter->dma_desc[0].size;
+
+ mrioc->prp_sz = 0;
+ mrioc->prp_list_virt = dma_alloc_coherent(&mrioc->pdev->dev,
+ dev_pgsz, &mrioc->prp_list_dma, GFP_KERNEL);
+
+ if (!mrioc->prp_list_virt)
+ return -1;
+ mrioc->prp_sz = dev_pgsz;
+
+ /*
+ * Set pointers to PRP1 and PRP2, which are in the NVMe command.
+ * PRP1 is located at a 24 byte offset from the start of the NVMe
+ * command. Then set the current PRP entry pointer to PRP1.
+ */
+ prp1_entry = (__le64 *)((u8 *)(nvme_encap_request->command) +
+ MPI3MR_NVME_CMD_PRP1_OFFSET);
+ prp2_entry = (__le64 *)((u8 *)(nvme_encap_request->command) +
+ MPI3MR_NVME_CMD_PRP2_OFFSET);
+ prp_entry = prp1_entry;
+ /*
+ * For the PRP entries, use the specially allocated buffer of
+ * contiguous memory.
+ */
+ prp_page = (__le64 *)mrioc->prp_list_virt;
+ prp_page_dma = mrioc->prp_list_dma;
+
+ /*
+ * Check if we are within 1 entry of a page boundary we don't
+ * want our first entry to be a PRP List entry.
+ */
+ page_mask_result = (uintptr_t)((u8 *)prp_page + prp_size) & page_mask;
+ if (!page_mask_result) {
+ dprint_bsg_err(mrioc, "%s: PRP page is not page aligned\n",
+ __func__);
+ goto err_out;
+ }
+
+ /*
+ * Set PRP physical pointer, which initially points to the current PRP
+ * DMA memory page.
+ */
+ prp_entry_dma = prp_page_dma;
+
+
+ /* Loop while the length is not zero. */
+ while (length) {
+ page_mask_result = (prp_entry_dma + prp_size) & page_mask;
+ if (!page_mask_result && (length > dev_pgsz)) {
+ dprint_bsg_err(mrioc,
+ "%s: single PRP page is not sufficient\n",
+ __func__);
+ goto err_out;
+ }
+
+ /* Need to handle if entry will be part of a page. */
+ offset = dma_addr & page_mask;
+ entry_len = dev_pgsz - offset;
+
+ if (prp_entry == prp1_entry) {
+ /*
+ * Must fill in the first PRP pointer (PRP1) before
+ * moving on.
+ */
+ *prp1_entry = cpu_to_le64(dma_addr);
+ if (*prp1_entry & sgemod_mask) {
+ dprint_bsg_err(mrioc,
+ "%s: PRP1 address collides with SGE modifier\n",
+ __func__);
+ goto err_out;
+ }
+ *prp1_entry &= ~sgemod_mask;
+ *prp1_entry |= sgemod_val;
+
+ /*
+ * Now point to the second PRP entry within the
+ * command (PRP2).
+ */
+ prp_entry = prp2_entry;
+ } else if (prp_entry == prp2_entry) {
+ /*
+ * Should the PRP2 entry be a PRP List pointer or just
+ * a regular PRP pointer? If there is more than one
+ * more page of data, must use a PRP List pointer.
+ */
+ if (length > dev_pgsz) {
+ /*
+ * PRP2 will contain a PRP List pointer because
+ * more PRP's are needed with this command. The
+ * list will start at the beginning of the
+ * contiguous buffer.
+ */
+ *prp2_entry = cpu_to_le64(prp_entry_dma);
+ if (*prp2_entry & sgemod_mask) {
+ dprint_bsg_err(mrioc,
+ "%s: PRP list address collides with SGE modifier\n",
+ __func__);
+ goto err_out;
+ }
+ *prp2_entry &= ~sgemod_mask;
+ *prp2_entry |= sgemod_val;
+
+ /*
+ * The next PRP Entry will be the start of the
+ * first PRP List.
+ */
+ prp_entry = prp_page;
+ continue;
+ } else {
+ /*
+ * After this, the PRP Entries are complete.
+ * This command uses 2 PRP's and no PRP list.
+ */
+ *prp2_entry = cpu_to_le64(dma_addr);
+ if (*prp2_entry & sgemod_mask) {
+ dprint_bsg_err(mrioc,
+ "%s: PRP2 collides with SGE modifier\n",
+ __func__);
+ goto err_out;
+ }
+ *prp2_entry &= ~sgemod_mask;
+ *prp2_entry |= sgemod_val;
+ }
+ } else {
+ /*
+ * Put entry in list and bump the addresses.
+ *
+ * After PRP1 and PRP2 are filled in, this will fill in
+ * all remaining PRP entries in a PRP List, one per
+ * each time through the loop.
+ */
+ *prp_entry = cpu_to_le64(dma_addr);
+ if (*prp_entry & sgemod_mask) {
+ dprint_bsg_err(mrioc,
+ "%s: PRP address collides with SGE modifier\n",
+ __func__);
+ goto err_out;
+ }
+ *prp_entry &= ~sgemod_mask;
+ *prp_entry |= sgemod_val;
+ prp_entry++;
+ prp_entry_dma += prp_size;
+ }
+
+ /* decrement length accounting for last partial page. */
+ if (entry_len >= length) {
+ length = 0;
+ } else {
+ if (entry_len <= desc_len) {
+ dma_addr += entry_len;
+ desc_len -= entry_len;
+ }
+ if (!desc_len) {
+ if ((++desc_count) >=
+ drv_buf_iter->num_dma_desc) {
+ dprint_bsg_err(mrioc,
+ "%s: Invalid len %zd while building PRP\n",
+ __func__, length);
+ goto err_out;
+ }
+ dma_addr =
+ drv_buf_iter->dma_desc[desc_count].dma_addr;
+ desc_len =
+ drv_buf_iter->dma_desc[desc_count].size;
+ }
+ length -= entry_len;
+ }
+ }
+
+ return 0;
+err_out:
+ if (mrioc->prp_list_virt) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->prp_sz,
+ mrioc->prp_list_virt, mrioc->prp_list_dma);
+ mrioc->prp_list_virt = NULL;
+ }
+ return -1;
+}
+
+/**
+ * mpi3mr_map_data_buffer_dma - build dma descriptors for data
+ * buffers
+ * @mrioc: Adapter instance reference
+ * @drv_buf: buffer map descriptor
+ * @desc_count: Number of already consumed dma descriptors
+ *
+ * This function computes how many pre-allocated DMA descriptors
+ * are required for the given data buffer and if those number of
+ * descriptors are free, then setup the mapping of the scattered
+ * DMA address to the given data buffer, if the data direction
+ * of the buffer is DMA_TO_DEVICE then the actual data is copied to
+ * the DMA buffers
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int mpi3mr_map_data_buffer_dma(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_buf_map *drv_buf,
+ u16 desc_count)
+{
+ u16 i, needed_desc = drv_buf->kern_buf_len / MPI3MR_IOCTL_SGE_SIZE;
+ u32 buf_len = drv_buf->kern_buf_len, copied_len = 0;
+
+ if (drv_buf->kern_buf_len % MPI3MR_IOCTL_SGE_SIZE)
+ needed_desc++;
+ if ((needed_desc + desc_count) > MPI3MR_NUM_IOCTL_SGE) {
+ dprint_bsg_err(mrioc, "%s: DMA descriptor mapping error %d:%d:%d\n",
+ __func__, needed_desc, desc_count, MPI3MR_NUM_IOCTL_SGE);
+ return -1;
+ }
+ drv_buf->dma_desc = kzalloc(sizeof(*drv_buf->dma_desc) * needed_desc,
+ GFP_KERNEL);
+ if (!drv_buf->dma_desc)
+ return -1;
+ for (i = 0; i < needed_desc; i++, desc_count++) {
+ drv_buf->dma_desc[i].addr = mrioc->ioctl_sge[desc_count].addr;
+ drv_buf->dma_desc[i].dma_addr =
+ mrioc->ioctl_sge[desc_count].dma_addr;
+ if (buf_len < mrioc->ioctl_sge[desc_count].size)
+ drv_buf->dma_desc[i].size = buf_len;
+ else
+ drv_buf->dma_desc[i].size =
+ mrioc->ioctl_sge[desc_count].size;
+ buf_len -= drv_buf->dma_desc[i].size;
+ memset(drv_buf->dma_desc[i].addr, 0,
+ mrioc->ioctl_sge[desc_count].size);
+ if (drv_buf->data_dir == DMA_TO_DEVICE) {
+ memcpy(drv_buf->dma_desc[i].addr,
+ drv_buf->bsg_buf + copied_len,
+ drv_buf->dma_desc[i].size);
+ copied_len += drv_buf->dma_desc[i].size;
+ }
+ }
+ drv_buf->num_dma_desc = needed_desc;
+ return 0;
+}
+/**
+ * mpi3mr_bsg_process_mpt_cmds - MPI Pass through BSG handler
+ * @job: BSG job reference
+ *
+ * This function is the top level handler for MPI Pass through
+ * command, this does basic validation of the input data buffers,
+ * identifies the given buffer types and MPI command, allocates
+ * DMAable memory for user given buffers, construstcs SGL
+ * properly and passes the command to the firmware.
+ *
+ * Once the MPI command is completed the driver copies the data
+ * if any and reply, sense information to user provided buffers.
+ * If the command is timed out then issues controller reset
+ * prior to returning.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+
+static long mpi3mr_bsg_process_mpt_cmds(struct bsg_job *job)
+{
+ long rval = -EINVAL;
+ struct mpi3mr_ioc *mrioc = NULL;
+ u8 *mpi_req = NULL, *sense_buff_k = NULL;
+ u8 mpi_msg_size = 0;
+ struct mpi3mr_bsg_packet *bsg_req = NULL;
+ struct mpi3mr_bsg_mptcmd *karg;
+ struct mpi3mr_buf_entry *buf_entries = NULL;
+ struct mpi3mr_buf_map *drv_bufs = NULL, *drv_buf_iter = NULL;
+ u8 count, bufcnt = 0, is_rmcb = 0, is_rmrb = 0;
+ u8 din_cnt = 0, dout_cnt = 0;
+ u8 invalid_be = 0, erb_offset = 0xFF, mpirep_offset = 0xFF;
+ u8 block_io = 0, nvme_fmt = 0, resp_code = 0;
+ struct mpi3_request_header *mpi_header = NULL;
+ struct mpi3_status_reply_descriptor *status_desc;
+ struct mpi3_scsi_task_mgmt_request *tm_req;
+ u32 erbsz = MPI3MR_SENSE_BUF_SZ, tmplen;
+ u16 dev_handle;
+ struct mpi3mr_tgt_dev *tgtdev;
+ struct mpi3mr_stgt_priv_data *stgt_priv = NULL;
+ struct mpi3mr_bsg_in_reply_buf *bsg_reply_buf = NULL;
+ u32 din_size = 0, dout_size = 0;
+ u8 *din_buf = NULL, *dout_buf = NULL;
+ u8 *sgl_iter = NULL, *sgl_din_iter = NULL, *sgl_dout_iter = NULL;
+ u16 rmc_size = 0, desc_count = 0;
+
+ bsg_req = job->request;
+ karg = (struct mpi3mr_bsg_mptcmd *)&bsg_req->cmd.mptcmd;
+
+ mrioc = mpi3mr_bsg_verify_adapter(karg->mrioc_id);
+ if (!mrioc)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&mrioc->bsg_cmds.mutex))
+ return -ERESTARTSYS;
+
+ if (mrioc->bsg_cmds.state & MPI3MR_CMD_PENDING) {
+ dprint_bsg_err(mrioc, "%s: command is in use\n", __func__);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ return -EAGAIN;
+ }
+
+ if (!mrioc->ioctl_sges_allocated) {
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ dprint_bsg_err(mrioc, "%s: DMA memory was not allocated\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (karg->timeout < MPI3MR_APP_DEFAULT_TIMEOUT)
+ karg->timeout = MPI3MR_APP_DEFAULT_TIMEOUT;
+
+ mpi_req = kzalloc(MPI3MR_ADMIN_REQ_FRAME_SZ, GFP_KERNEL);
+ if (!mpi_req) {
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ return -ENOMEM;
+ }
+ mpi_header = (struct mpi3_request_header *)mpi_req;
+
+ bufcnt = karg->buf_entry_list.num_of_entries;
+ drv_bufs = kzalloc((sizeof(*drv_bufs) * bufcnt), GFP_KERNEL);
+ if (!drv_bufs) {
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ dout_buf = kzalloc(job->request_payload.payload_len,
+ GFP_KERNEL);
+ if (!dout_buf) {
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ din_buf = kzalloc(job->reply_payload.payload_len,
+ GFP_KERNEL);
+ if (!din_buf) {
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ dout_buf, job->request_payload.payload_len);
+
+ buf_entries = karg->buf_entry_list.buf_entry;
+ sgl_din_iter = din_buf;
+ sgl_dout_iter = dout_buf;
+ drv_buf_iter = drv_bufs;
+
+ for (count = 0; count < bufcnt; count++, buf_entries++, drv_buf_iter++) {
+
+ switch (buf_entries->buf_type) {
+ case MPI3MR_BSG_BUFTYPE_RAIDMGMT_CMD:
+ sgl_iter = sgl_dout_iter;
+ sgl_dout_iter += buf_entries->buf_len;
+ drv_buf_iter->data_dir = DMA_TO_DEVICE;
+ is_rmcb = 1;
+ if ((count != 0) || !buf_entries->buf_len)
+ invalid_be = 1;
+ break;
+ case MPI3MR_BSG_BUFTYPE_RAIDMGMT_RESP:
+ sgl_iter = sgl_din_iter;
+ sgl_din_iter += buf_entries->buf_len;
+ drv_buf_iter->data_dir = DMA_FROM_DEVICE;
+ is_rmrb = 1;
+ if (count != 1 || !is_rmcb || !buf_entries->buf_len)
+ invalid_be = 1;
+ break;
+ case MPI3MR_BSG_BUFTYPE_DATA_IN:
+ sgl_iter = sgl_din_iter;
+ sgl_din_iter += buf_entries->buf_len;
+ drv_buf_iter->data_dir = DMA_FROM_DEVICE;
+ din_cnt++;
+ din_size += buf_entries->buf_len;
+ if ((din_cnt > 1) && !is_rmcb)
+ invalid_be = 1;
+ break;
+ case MPI3MR_BSG_BUFTYPE_DATA_OUT:
+ sgl_iter = sgl_dout_iter;
+ sgl_dout_iter += buf_entries->buf_len;
+ drv_buf_iter->data_dir = DMA_TO_DEVICE;
+ dout_cnt++;
+ dout_size += buf_entries->buf_len;
+ if ((dout_cnt > 1) && !is_rmcb)
+ invalid_be = 1;
+ break;
+ case MPI3MR_BSG_BUFTYPE_MPI_REPLY:
+ sgl_iter = sgl_din_iter;
+ sgl_din_iter += buf_entries->buf_len;
+ drv_buf_iter->data_dir = DMA_NONE;
+ mpirep_offset = count;
+ if (!buf_entries->buf_len)
+ invalid_be = 1;
+ break;
+ case MPI3MR_BSG_BUFTYPE_ERR_RESPONSE:
+ sgl_iter = sgl_din_iter;
+ sgl_din_iter += buf_entries->buf_len;
+ drv_buf_iter->data_dir = DMA_NONE;
+ erb_offset = count;
+ if (!buf_entries->buf_len)
+ invalid_be = 1;
+ break;
+ case MPI3MR_BSG_BUFTYPE_MPI_REQUEST:
+ sgl_iter = sgl_dout_iter;
+ sgl_dout_iter += buf_entries->buf_len;
+ drv_buf_iter->data_dir = DMA_NONE;
+ mpi_msg_size = buf_entries->buf_len;
+ if ((!mpi_msg_size || (mpi_msg_size % 4)) ||
+ (mpi_msg_size > MPI3MR_ADMIN_REQ_FRAME_SZ)) {
+ dprint_bsg_err(mrioc, "%s: invalid MPI message size\n",
+ __func__);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+ memcpy(mpi_req, sgl_iter, buf_entries->buf_len);
+ break;
+ default:
+ invalid_be = 1;
+ break;
+ }
+ if (invalid_be) {
+ dprint_bsg_err(mrioc, "%s: invalid buffer entries passed\n",
+ __func__);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+
+ if (sgl_dout_iter > (dout_buf + job->request_payload.payload_len)) {
+ dprint_bsg_err(mrioc, "%s: data_out buffer length mismatch\n",
+ __func__);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+ if (sgl_din_iter > (din_buf + job->reply_payload.payload_len)) {
+ dprint_bsg_err(mrioc, "%s: data_in buffer length mismatch\n",
+ __func__);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+
+ drv_buf_iter->bsg_buf = sgl_iter;
+ drv_buf_iter->bsg_buf_len = buf_entries->buf_len;
+ }
+
+ if (is_rmcb && ((din_size + dout_size) > MPI3MR_MAX_APP_XFER_SIZE)) {
+ dprint_bsg_err(mrioc, "%s:%d: invalid data transfer size passed for function 0x%x din_size = %d, dout_size = %d\n",
+ __func__, __LINE__, mpi_header->function, din_size,
+ dout_size);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+
+ if (din_size > MPI3MR_MAX_APP_XFER_SIZE) {
+ dprint_bsg_err(mrioc,
+ "%s:%d: invalid data transfer size passed for function 0x%x din_size=%d\n",
+ __func__, __LINE__, mpi_header->function, din_size);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+ if (dout_size > MPI3MR_MAX_APP_XFER_SIZE) {
+ dprint_bsg_err(mrioc,
+ "%s:%d: invalid data transfer size passed for function 0x%x dout_size = %d\n",
+ __func__, __LINE__, mpi_header->function, dout_size);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+
+ if (mpi_header->function == MPI3_BSG_FUNCTION_SMP_PASSTHROUGH) {
+ if (din_size > MPI3MR_IOCTL_SGE_SIZE ||
+ dout_size > MPI3MR_IOCTL_SGE_SIZE) {
+ dprint_bsg_err(mrioc, "%s:%d: invalid message size passed:%d:%d:%d:%d\n",
+ __func__, __LINE__, din_cnt, dout_cnt, din_size,
+ dout_size);
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ rval = -EINVAL;
+ goto out;
+ }
+ }
+
+ drv_buf_iter = drv_bufs;
+ for (count = 0; count < bufcnt; count++, drv_buf_iter++) {
+ if (drv_buf_iter->data_dir == DMA_NONE)
+ continue;
+
+ drv_buf_iter->kern_buf_len = drv_buf_iter->bsg_buf_len;
+ if (is_rmcb && !count) {
+ drv_buf_iter->kern_buf_len =
+ mrioc->ioctl_chain_sge.size;
+ drv_buf_iter->kern_buf =
+ mrioc->ioctl_chain_sge.addr;
+ drv_buf_iter->kern_buf_dma =
+ mrioc->ioctl_chain_sge.dma_addr;
+ drv_buf_iter->dma_desc = NULL;
+ drv_buf_iter->num_dma_desc = 0;
+ memset(drv_buf_iter->kern_buf, 0,
+ drv_buf_iter->kern_buf_len);
+ tmplen = min(drv_buf_iter->kern_buf_len,
+ drv_buf_iter->bsg_buf_len);
+ rmc_size = tmplen;
+ memcpy(drv_buf_iter->kern_buf, drv_buf_iter->bsg_buf, tmplen);
+ } else if (is_rmrb && (count == 1)) {
+ drv_buf_iter->kern_buf_len =
+ mrioc->ioctl_resp_sge.size;
+ drv_buf_iter->kern_buf =
+ mrioc->ioctl_resp_sge.addr;
+ drv_buf_iter->kern_buf_dma =
+ mrioc->ioctl_resp_sge.dma_addr;
+ drv_buf_iter->dma_desc = NULL;
+ drv_buf_iter->num_dma_desc = 0;
+ memset(drv_buf_iter->kern_buf, 0,
+ drv_buf_iter->kern_buf_len);
+ tmplen = min(drv_buf_iter->kern_buf_len,
+ drv_buf_iter->bsg_buf_len);
+ drv_buf_iter->kern_buf_len = tmplen;
+ memset(drv_buf_iter->bsg_buf, 0,
+ drv_buf_iter->bsg_buf_len);
+ } else {
+ if (!drv_buf_iter->kern_buf_len)
+ continue;
+ if (mpi3mr_map_data_buffer_dma(mrioc, drv_buf_iter, desc_count)) {
+ rval = -ENOMEM;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ dprint_bsg_err(mrioc, "%s:%d: mapping data buffers failed\n",
+ __func__, __LINE__);
+ goto out;
+ }
+ desc_count += drv_buf_iter->num_dma_desc;
+ }
+ }
+
+ if (erb_offset != 0xFF) {
+ sense_buff_k = kzalloc(erbsz, GFP_KERNEL);
+ if (!sense_buff_k) {
+ rval = -ENOMEM;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+ }
+
+ if (mrioc->unrecoverable) {
+ dprint_bsg_err(mrioc, "%s: unrecoverable controller\n",
+ __func__);
+ rval = -EFAULT;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+ if (mrioc->reset_in_progress) {
+ dprint_bsg_err(mrioc, "%s: reset in progress\n", __func__);
+ rval = -EAGAIN;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+ if (mrioc->stop_bsgs || mrioc->block_on_pci_err) {
+ dprint_bsg_err(mrioc, "%s: bsgs are blocked\n", __func__);
+ rval = -EAGAIN;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+
+ if (mpi_header->function == MPI3_BSG_FUNCTION_NVME_ENCAPSULATED) {
+ nvme_fmt = mpi3mr_get_nvme_data_fmt(
+ (struct mpi3_nvme_encapsulated_request *)mpi_req);
+ if (nvme_fmt == MPI3MR_NVME_DATA_FORMAT_PRP) {
+ if (mpi3mr_build_nvme_prp(mrioc,
+ (struct mpi3_nvme_encapsulated_request *)mpi_req,
+ drv_bufs, bufcnt)) {
+ rval = -ENOMEM;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+ } else if (nvme_fmt == MPI3MR_NVME_DATA_FORMAT_SGL1 ||
+ nvme_fmt == MPI3MR_NVME_DATA_FORMAT_SGL2) {
+ if (mpi3mr_build_nvme_sgl(mrioc,
+ (struct mpi3_nvme_encapsulated_request *)mpi_req,
+ drv_bufs, bufcnt)) {
+ rval = -EINVAL;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+ } else {
+ dprint_bsg_err(mrioc,
+ "%s:invalid NVMe command format\n", __func__);
+ rval = -EINVAL;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+ } else {
+ if (mpi3mr_bsg_build_sgl(mrioc, mpi_req, mpi_msg_size,
+ drv_bufs, bufcnt, is_rmcb, is_rmrb,
+ (dout_cnt + din_cnt))) {
+ dprint_bsg_err(mrioc, "%s: sgl build failed\n", __func__);
+ rval = -EAGAIN;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+ goto out;
+ }
+ }
+
+ if (mpi_header->function == MPI3_BSG_FUNCTION_SCSI_TASK_MGMT) {
+ tm_req = (struct mpi3_scsi_task_mgmt_request *)mpi_req;
+ if (tm_req->task_type !=
+ MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+ dev_handle = tm_req->dev_handle;
+ block_io = 1;
+ }
+ }
+ if (block_io) {
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata) {
+ stgt_priv = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ atomic_inc(&stgt_priv->block_io);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ }
+
+ mrioc->bsg_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->bsg_cmds.is_waiting = 1;
+ mrioc->bsg_cmds.callback = NULL;
+ mrioc->bsg_cmds.is_sense = 0;
+ mrioc->bsg_cmds.sensebuf = sense_buff_k;
+ memset(mrioc->bsg_cmds.reply, 0, mrioc->reply_sz);
+ mpi_header->host_tag = cpu_to_le16(MPI3MR_HOSTTAG_BSG_CMDS);
+ if (mrioc->logging_level & MPI3_DEBUG_BSG_INFO) {
+ dprint_bsg_info(mrioc,
+ "%s: posting bsg request to the controller\n", __func__);
+ dprint_dump(mpi_req, MPI3MR_ADMIN_REQ_FRAME_SZ,
+ "bsg_mpi3_req");
+ if (mpi_header->function == MPI3_BSG_FUNCTION_MGMT_PASSTHROUGH) {
+ drv_buf_iter = &drv_bufs[0];
+ dprint_dump(drv_buf_iter->kern_buf,
+ rmc_size, "mpi3_mgmt_req");
+ }
+ }
+
+ init_completion(&mrioc->bsg_cmds.done);
+ rval = mpi3mr_admin_request_post(mrioc, mpi_req,
+ MPI3MR_ADMIN_REQ_FRAME_SZ, 0);
+
+
+ if (rval) {
+ mrioc->bsg_cmds.is_waiting = 0;
+ dprint_bsg_err(mrioc,
+ "%s: posting bsg request is failed\n", __func__);
+ rval = -EAGAIN;
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->bsg_cmds.done,
+ (karg->timeout * HZ));
+ if (block_io && stgt_priv)
+ atomic_dec(&stgt_priv->block_io);
+ if (!(mrioc->bsg_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mrioc->bsg_cmds.is_waiting = 0;
+ rval = -EAGAIN;
+ if (mrioc->bsg_cmds.state & MPI3MR_CMD_RESET)
+ goto out_unlock;
+ if (((mpi_header->function != MPI3_FUNCTION_SCSI_IO) &&
+ (mpi_header->function != MPI3_FUNCTION_NVME_ENCAPSULATED))
+ || (mrioc->logging_level & MPI3_DEBUG_BSG_ERROR)) {
+ ioc_info(mrioc, "%s: bsg request timedout after %d seconds\n",
+ __func__, karg->timeout);
+ if (!(mrioc->logging_level & MPI3_DEBUG_BSG_INFO)) {
+ dprint_dump(mpi_req, MPI3MR_ADMIN_REQ_FRAME_SZ,
+ "bsg_mpi3_req");
+ if (mpi_header->function ==
+ MPI3_FUNCTION_MGMT_PASSTHROUGH) {
+ drv_buf_iter = &drv_bufs[0];
+ dprint_dump(drv_buf_iter->kern_buf,
+ rmc_size, "mpi3_mgmt_req");
+ }
+ }
+ }
+ if ((mpi_header->function == MPI3_BSG_FUNCTION_NVME_ENCAPSULATED) ||
+ (mpi_header->function == MPI3_BSG_FUNCTION_SCSI_IO)) {
+ dprint_bsg_err(mrioc, "%s: bsg request timedout after %d seconds,\n"
+ "issuing target reset to (0x%04x)\n", __func__,
+ karg->timeout, mpi_header->function_dependent);
+ mpi3mr_issue_tm(mrioc,
+ MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
+ mpi_header->function_dependent, 0,
+ MPI3MR_HOSTTAG_BLK_TMS, MPI3MR_RESETTM_TIMEOUT,
+ &mrioc->host_tm_cmds, &resp_code, NULL);
+ }
+ if (!(mrioc->bsg_cmds.state & MPI3MR_CMD_COMPLETE) &&
+ !(mrioc->bsg_cmds.state & MPI3MR_CMD_RESET))
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_APP_TIMEOUT, 1);
+ goto out_unlock;
+ }
+ dprint_bsg_info(mrioc, "%s: bsg request is completed\n", __func__);
+
+ if (mrioc->prp_list_virt) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->prp_sz,
+ mrioc->prp_list_virt, mrioc->prp_list_dma);
+ mrioc->prp_list_virt = NULL;
+ }
+
+ if ((mrioc->bsg_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_bsg_info(mrioc,
+ "%s: command failed, ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__,
+ (mrioc->bsg_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->bsg_cmds.ioc_loginfo);
+ }
+
+ if ((mpirep_offset != 0xFF) &&
+ drv_bufs[mpirep_offset].bsg_buf_len) {
+ drv_buf_iter = &drv_bufs[mpirep_offset];
+ drv_buf_iter->kern_buf_len = (sizeof(*bsg_reply_buf) +
+ mrioc->reply_sz);
+ bsg_reply_buf = kzalloc(drv_buf_iter->kern_buf_len, GFP_KERNEL);
+
+ if (!bsg_reply_buf) {
+ rval = -ENOMEM;
+ goto out_unlock;
+ }
+ if (mrioc->bsg_cmds.state & MPI3MR_CMD_REPLY_VALID) {
+ bsg_reply_buf->mpi_reply_type =
+ MPI3MR_BSG_MPI_REPLY_BUFTYPE_ADDRESS;
+ memcpy(bsg_reply_buf->reply_buf,
+ mrioc->bsg_cmds.reply, mrioc->reply_sz);
+ } else {
+ bsg_reply_buf->mpi_reply_type =
+ MPI3MR_BSG_MPI_REPLY_BUFTYPE_STATUS;
+ status_desc = (struct mpi3_status_reply_descriptor *)
+ bsg_reply_buf->reply_buf;
+ status_desc->ioc_status = mrioc->bsg_cmds.ioc_status;
+ status_desc->ioc_log_info = mrioc->bsg_cmds.ioc_loginfo;
+ }
+ tmplen = min(drv_buf_iter->kern_buf_len,
+ drv_buf_iter->bsg_buf_len);
+ memcpy(drv_buf_iter->bsg_buf, bsg_reply_buf, tmplen);
+ }
+
+ if (erb_offset != 0xFF && mrioc->bsg_cmds.sensebuf &&
+ mrioc->bsg_cmds.is_sense) {
+ drv_buf_iter = &drv_bufs[erb_offset];
+ tmplen = min(erbsz, drv_buf_iter->bsg_buf_len);
+ memcpy(drv_buf_iter->bsg_buf, sense_buff_k, tmplen);
+ }
+
+ drv_buf_iter = drv_bufs;
+ for (count = 0; count < bufcnt; count++, drv_buf_iter++) {
+ if (drv_buf_iter->data_dir == DMA_NONE)
+ continue;
+ if ((count == 1) && is_rmrb) {
+ memcpy(drv_buf_iter->bsg_buf,
+ drv_buf_iter->kern_buf,
+ drv_buf_iter->kern_buf_len);
+ } else if (drv_buf_iter->data_dir == DMA_FROM_DEVICE) {
+ tmplen = 0;
+ for (desc_count = 0;
+ desc_count < drv_buf_iter->num_dma_desc;
+ desc_count++) {
+ memcpy(((u8 *)drv_buf_iter->bsg_buf + tmplen),
+ drv_buf_iter->dma_desc[desc_count].addr,
+ drv_buf_iter->dma_desc[desc_count].size);
+ tmplen +=
+ drv_buf_iter->dma_desc[desc_count].size;
+ }
+ }
+ }
+
+out_unlock:
+ if (din_buf) {
+ job->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ din_buf, job->reply_payload.payload_len);
+ }
+ mrioc->bsg_cmds.is_sense = 0;
+ mrioc->bsg_cmds.sensebuf = NULL;
+ mrioc->bsg_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->bsg_cmds.mutex);
+out:
+ kfree(sense_buff_k);
+ kfree(dout_buf);
+ kfree(din_buf);
+ kfree(mpi_req);
+ if (drv_bufs) {
+ drv_buf_iter = drv_bufs;
+ for (count = 0; count < bufcnt; count++, drv_buf_iter++)
+ kfree(drv_buf_iter->dma_desc);
+ kfree(drv_bufs);
+ }
+ kfree(bsg_reply_buf);
+ return rval;
+}
+
+/**
+ * mpi3mr_app_save_logdata - Save Log Data events
+ * @mrioc: Adapter instance reference
+ * @event_data: event data associated with log data event
+ * @event_data_size: event data size to copy
+ *
+ * If log data event caching is enabled by the applicatiobns,
+ * then this function saves the log data in the circular queue
+ * and Sends async signal SIGIO to indicate there is an async
+ * event from the firmware to the event monitoring applications.
+ *
+ * Return:Nothing
+ */
+void mpi3mr_app_save_logdata(struct mpi3mr_ioc *mrioc, char *event_data,
+ u16 event_data_size)
+{
+ u32 index = mrioc->logdata_buf_idx, sz;
+ struct mpi3mr_logdata_entry *entry;
+
+ if (!(mrioc->logdata_buf))
+ return;
+
+ entry = (struct mpi3mr_logdata_entry *)
+ (mrioc->logdata_buf + (index * mrioc->logdata_entry_sz));
+ entry->valid_entry = 1;
+ sz = min(mrioc->logdata_entry_sz, event_data_size);
+ memcpy(entry->data, event_data, sz);
+ mrioc->logdata_buf_idx =
+ ((++index) % MPI3MR_BSG_LOGDATA_MAX_ENTRIES);
+ atomic64_inc(&event_counter);
+}
+
+/**
+ * mpi3mr_bsg_request - bsg request entry point
+ * @job: BSG job reference
+ *
+ * This is driver's entry point for bsg requests
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static int mpi3mr_bsg_request(struct bsg_job *job)
+{
+ long rval = -EINVAL;
+ unsigned int reply_payload_rcv_len = 0;
+
+ struct mpi3mr_bsg_packet *bsg_req = job->request;
+
+ switch (bsg_req->cmd_type) {
+ case MPI3MR_DRV_CMD:
+ rval = mpi3mr_bsg_process_drv_cmds(job);
+ break;
+ case MPI3MR_MPT_CMD:
+ rval = mpi3mr_bsg_process_mpt_cmds(job);
+ break;
+ default:
+ pr_err("%s: unsupported BSG command(0x%08x)\n",
+ MPI3MR_DRIVER_NAME, bsg_req->cmd_type);
+ break;
+ }
+
+ bsg_job_done(job, rval, reply_payload_rcv_len);
+
+ return 0;
+}
+
+/**
+ * mpi3mr_bsg_exit - de-registration from bsg layer
+ * @mrioc: Adapter instance reference
+ *
+ * This will be called during driver unload and all
+ * bsg resources allocated during load will be freed.
+ *
+ * Return:Nothing
+ */
+void mpi3mr_bsg_exit(struct mpi3mr_ioc *mrioc)
+{
+ struct device *bsg_dev = &mrioc->bsg_dev;
+ if (!mrioc->bsg_queue)
+ return;
+
+ bsg_remove_queue(mrioc->bsg_queue);
+ mrioc->bsg_queue = NULL;
+
+ device_del(bsg_dev);
+ put_device(bsg_dev);
+}
+
+/**
+ * mpi3mr_bsg_node_release -release bsg device node
+ * @dev: bsg device node
+ *
+ * decrements bsg dev parent reference count
+ *
+ * Return:Nothing
+ */
+static void mpi3mr_bsg_node_release(struct device *dev)
+{
+ put_device(dev->parent);
+}
+
+/**
+ * mpi3mr_bsg_init - registration with bsg layer
+ * @mrioc: Adapter instance reference
+ *
+ * This will be called during driver load and it will
+ * register driver with bsg layer
+ *
+ * Return:Nothing
+ */
+void mpi3mr_bsg_init(struct mpi3mr_ioc *mrioc)
+{
+ struct device *bsg_dev = &mrioc->bsg_dev;
+ struct device *parent = &mrioc->shost->shost_gendev;
+ struct queue_limits lim = {
+ .max_hw_sectors = MPI3MR_MAX_APP_XFER_SECTORS,
+ .max_segments = MPI3MR_MAX_APP_XFER_SEGMENTS,
+ };
+ struct request_queue *q;
+
+ device_initialize(bsg_dev);
+
+ bsg_dev->parent = get_device(parent);
+ bsg_dev->release = mpi3mr_bsg_node_release;
+
+ dev_set_name(bsg_dev, "mpi3mrctl%u", mrioc->id);
+
+ if (device_add(bsg_dev)) {
+ ioc_err(mrioc, "%s: bsg device add failed\n",
+ dev_name(bsg_dev));
+ put_device(bsg_dev);
+ return;
+ }
+
+ q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), &lim,
+ mpi3mr_bsg_request, NULL, 0);
+ if (IS_ERR(q)) {
+ ioc_err(mrioc, "%s: bsg registration failed\n",
+ dev_name(bsg_dev));
+ device_del(bsg_dev);
+ put_device(bsg_dev);
+ return;
+ }
+
+ mrioc->bsg_queue = q;
+}
+
+/**
+ * version_fw_show - SysFS callback for firmware version read
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying firmware version
+ */
+static ssize_t
+version_fw_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ struct mpi3mr_compimg_ver *fwver = &mrioc->facts.fw_ver;
+
+ return sysfs_emit(buf, "%d.%d.%d.%d.%05d-%05d\n",
+ fwver->gen_major, fwver->gen_minor, fwver->ph_major,
+ fwver->ph_minor, fwver->cust_id, fwver->build_num);
+}
+static DEVICE_ATTR_RO(version_fw);
+
+/**
+ * fw_queue_depth_show - SysFS callback for firmware max cmds
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying firmware max commands
+ */
+static ssize_t
+fw_queue_depth_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ return sysfs_emit(buf, "%d\n", mrioc->facts.max_reqs);
+}
+static DEVICE_ATTR_RO(fw_queue_depth);
+
+/**
+ * op_req_q_count_show - SysFS callback for request queue count
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying request queue count
+ */
+static ssize_t
+op_req_q_count_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ return sysfs_emit(buf, "%d\n", mrioc->num_op_req_q);
+}
+static DEVICE_ATTR_RO(op_req_q_count);
+
+/**
+ * reply_queue_count_show - SysFS callback for reply queue count
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying reply queue count
+ */
+static ssize_t
+reply_queue_count_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ return sysfs_emit(buf, "%d\n", mrioc->num_op_reply_q);
+}
+
+static DEVICE_ATTR_RO(reply_queue_count);
+
+/**
+ * reply_qfull_count_show - Show reply qfull count
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Retrieves the current value of the reply_qfull_count from the mrioc structure and
+ * formats it as a string for display.
+ *
+ * Return: sysfs_emit() return
+ */
+static ssize_t
+reply_qfull_count_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ return sysfs_emit(buf, "%u\n", atomic_read(&mrioc->reply_qfull_count));
+}
+
+static DEVICE_ATTR_RO(reply_qfull_count);
+
+/**
+ * logging_level_show - Show controller debug level
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * A sysfs 'read/write' shost attribute, to show the current
+ * debug log level used by the driver for the specific
+ * controller.
+ *
+ * Return: sysfs_emit() return
+ */
+static ssize_t
+logging_level_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ return sysfs_emit(buf, "%08xh\n", mrioc->logging_level);
+}
+
+/**
+ * logging_level_store- Change controller debug level
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ * @count: size of the buffer
+ *
+ * A sysfs 'read/write' shost attribute, to change the current
+ * debug log level used by the driver for the specific
+ * controller.
+ *
+ * Return: strlen() return
+ */
+static ssize_t
+logging_level_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ int val = 0;
+
+ if (kstrtoint(buf, 0, &val) != 0)
+ return -EINVAL;
+
+ mrioc->logging_level = val;
+ ioc_info(mrioc, "logging_level=%08xh\n", mrioc->logging_level);
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(logging_level);
+
+/**
+ * adp_state_show() - SysFS callback for adapter state show
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying adapter state
+ */
+static ssize_t
+adp_state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ enum mpi3mr_iocstate ioc_state;
+ uint8_t adp_state;
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state == MRIOC_STATE_UNRECOVERABLE)
+ adp_state = MPI3MR_BSG_ADPSTATE_UNRECOVERABLE;
+ else if (mrioc->reset_in_progress || mrioc->stop_bsgs ||
+ mrioc->block_on_pci_err)
+ adp_state = MPI3MR_BSG_ADPSTATE_IN_RESET;
+ else if (ioc_state == MRIOC_STATE_FAULT)
+ adp_state = MPI3MR_BSG_ADPSTATE_FAULT;
+ else
+ adp_state = MPI3MR_BSG_ADPSTATE_OPERATIONAL;
+
+ return sysfs_emit(buf, "%u\n", adp_state);
+}
+
+static DEVICE_ATTR_RO(adp_state);
+
+static struct attribute *mpi3mr_host_attrs[] = {
+ &dev_attr_version_fw.attr,
+ &dev_attr_fw_queue_depth.attr,
+ &dev_attr_op_req_q_count.attr,
+ &dev_attr_reply_queue_count.attr,
+ &dev_attr_reply_qfull_count.attr,
+ &dev_attr_logging_level.attr,
+ &dev_attr_adp_state.attr,
+ NULL,
+};
+
+static const struct attribute_group mpi3mr_host_attr_group = {
+ .attrs = mpi3mr_host_attrs
+};
+
+const struct attribute_group *mpi3mr_host_groups[] = {
+ &mpi3mr_host_attr_group,
+ NULL,
+};
+
+
+/*
+ * SCSI Device attributes under sysfs
+ */
+
+/**
+ * sas_address_show - SysFS callback for dev SASaddress display
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying SAS address of the
+ * specific SAS/SATA end device.
+ */
+static ssize_t
+sas_address_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ struct mpi3mr_stgt_priv_data *tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ sdev_priv_data = sdev->hostdata;
+ if (!sdev_priv_data)
+ return 0;
+
+ tgt_priv_data = sdev_priv_data->tgt_priv_data;
+ if (!tgt_priv_data)
+ return 0;
+ tgtdev = tgt_priv_data->tgt_dev;
+ if (!tgtdev || tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA)
+ return 0;
+ return sysfs_emit(buf, "0x%016llx\n",
+ (unsigned long long)tgtdev->dev_spec.sas_sata_inf.sas_address);
+}
+
+static DEVICE_ATTR_RO(sas_address);
+
+/**
+ * device_handle_show - SysFS callback for device handle display
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying firmware internal
+ * device handle of the specific device.
+ */
+static ssize_t
+device_handle_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ struct mpi3mr_stgt_priv_data *tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ sdev_priv_data = sdev->hostdata;
+ if (!sdev_priv_data)
+ return 0;
+
+ tgt_priv_data = sdev_priv_data->tgt_priv_data;
+ if (!tgt_priv_data)
+ return 0;
+ tgtdev = tgt_priv_data->tgt_dev;
+ if (!tgtdev)
+ return 0;
+ return sysfs_emit(buf, "0x%04x\n", tgtdev->dev_handle);
+}
+
+static DEVICE_ATTR_RO(device_handle);
+
+/**
+ * persistent_id_show - SysFS callback for persisten ID display
+ * @dev: class device
+ * @attr: Device attributes
+ * @buf: Buffer to copy
+ *
+ * Return: sysfs_emit() return after copying persistent ID of the
+ * of the specific device.
+ */
+static ssize_t
+persistent_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ struct mpi3mr_stgt_priv_data *tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ sdev_priv_data = sdev->hostdata;
+ if (!sdev_priv_data)
+ return 0;
+
+ tgt_priv_data = sdev_priv_data->tgt_priv_data;
+ if (!tgt_priv_data)
+ return 0;
+ tgtdev = tgt_priv_data->tgt_dev;
+ if (!tgtdev)
+ return 0;
+ return sysfs_emit(buf, "%d\n", tgtdev->perst_id);
+}
+static DEVICE_ATTR_RO(persistent_id);
+
+/**
+ * sas_ncq_prio_supported_show - Indicate if device supports NCQ priority
+ * @dev: pointer to embedded device
+ * @attr: sas_ncq_prio_supported attribute descriptor
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' sdev attribute, only works with SATA devices
+ *
+ * Returns: the number of characters written to @buf
+ */
+static ssize_t
+sas_ncq_prio_supported_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return sysfs_emit(buf, "%d\n", sas_ata_ncq_prio_supported(sdev));
+}
+static DEVICE_ATTR_RO(sas_ncq_prio_supported);
+
+/**
+ * sas_ncq_prio_enable_show - send prioritized io commands to device
+ * @dev: pointer to embedded device
+ * @attr: sas_ncq_prio_enable attribute descriptor
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' sdev attribute, only works with SATA devices
+ *
+ * Returns: the number of characters written to @buf
+ */
+static ssize_t
+sas_ncq_prio_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct mpi3mr_sdev_priv_data *sdev_priv_data = sdev->hostdata;
+
+ if (!sdev_priv_data)
+ return 0;
+
+ return sysfs_emit(buf, "%d\n", sdev_priv_data->ncq_prio_enable);
+}
+
+static ssize_t
+sas_ncq_prio_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct mpi3mr_sdev_priv_data *sdev_priv_data = sdev->hostdata;
+ bool ncq_prio_enable = 0;
+
+ if (kstrtobool(buf, &ncq_prio_enable))
+ return -EINVAL;
+
+ if (!sas_ata_ncq_prio_supported(sdev))
+ return -EINVAL;
+
+ sdev_priv_data->ncq_prio_enable = ncq_prio_enable;
+
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(sas_ncq_prio_enable);
+
+static struct attribute *mpi3mr_dev_attrs[] = {
+ &dev_attr_sas_address.attr,
+ &dev_attr_device_handle.attr,
+ &dev_attr_persistent_id.attr,
+ &dev_attr_sas_ncq_prio_supported.attr,
+ &dev_attr_sas_ncq_prio_enable.attr,
+ NULL,
+};
+
+static const struct attribute_group mpi3mr_dev_attr_group = {
+ .attrs = mpi3mr_dev_attrs
+};
+
+const struct attribute_group *mpi3mr_dev_groups[] = {
+ &mpi3mr_dev_attr_group,
+ NULL,
+};
diff --git a/drivers/scsi/mpi3mr/mpi3mr_debug.h b/drivers/scsi/mpi3mr/mpi3mr_debug.h
new file mode 100644
index 000000000000..e94f7520d153
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_debug.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2023 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#ifndef MPI3SAS_DEBUG_H_INCLUDED
+
+#define MPI3SAS_DEBUG_H_INCLUDED
+
+/*
+ * debug levels
+ */
+
+#define MPI3_DEBUG_EVENT 0x00000001
+#define MPI3_DEBUG_EVENT_WORK_TASK 0x00000002
+#define MPI3_DEBUG_INIT 0x00000004
+#define MPI3_DEBUG_EXIT 0x00000008
+#define MPI3_DEBUG_TM 0x00000010
+#define MPI3_DEBUG_RESET 0x00000020
+#define MPI3_DEBUG_SCSI_ERROR 0x00000040
+#define MPI3_DEBUG_REPLY 0x00000080
+#define MPI3_DEBUG_CFG_ERROR 0x00000100
+#define MPI3_DEBUG_TRANSPORT_ERROR 0x00000200
+#define MPI3_DEBUG_BSG_ERROR 0x00008000
+#define MPI3_DEBUG_BSG_INFO 0x00010000
+#define MPI3_DEBUG_SCSI_INFO 0x00020000
+#define MPI3_DEBUG_CFG_INFO 0x00040000
+#define MPI3_DEBUG_TRANSPORT_INFO 0x00080000
+#define MPI3_DEBUG 0x01000000
+#define MPI3_DEBUG_SG 0x02000000
+
+
+/*
+ * debug macros
+ */
+
+#define ioc_err(ioc, fmt, ...) \
+ pr_err("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_notice(ioc, fmt, ...) \
+ pr_notice("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_warn(ioc, fmt, ...) \
+ pr_warn("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_info(ioc, fmt, ...) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+
+#define dprint(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_event_th(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_EVENT) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_event_bh(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_EVENT_WORK_TASK) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_init(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_INIT) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_exit(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_EXIT) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_tm(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_TM) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_reply(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_REPLY) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_reset(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_RESET) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_scsi_info(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_SCSI_INFO) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_scsi_err(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_SCSI_ERROR) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_scsi_command(ioc, SCMD, LOG_LEVEL) \
+ do { \
+ if (ioc->logging_level & LOG_LEVEL) \
+ scsi_print_command(SCMD); \
+ } while (0)
+
+
+#define dprint_bsg_info(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_BSG_INFO) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_bsg_err(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_BSG_ERROR) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_cfg_info(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_CFG_INFO) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_cfg_err(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_CFG_ERROR) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+#define dprint_transport_info(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#define dprint_transport_err(ioc, fmt, ...) \
+ do { \
+ if (ioc->logging_level & MPI3_DEBUG_TRANSPORT_ERROR) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#endif /* MPT3SAS_DEBUG_H_INCLUDED */
+
+/**
+ * dprint_dump - print contents of a memory buffer
+ * @req: Pointer to a memory buffer
+ * @sz: Memory buffer size
+ * @namestr: Name String to identify the buffer type
+ */
+static inline void
+dprint_dump(void *req, int sz, const char *name_string)
+{
+ int i;
+ __le32 *mfp = (__le32 *)req;
+
+ sz = sz/4;
+ if (name_string)
+ pr_info("%s:\n\t", name_string);
+ else
+ pr_info("request:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
+ }
+ pr_info("\n");
+}
+
+/**
+ * dprint_dump_req - print message frame contents
+ * @req: pointer to message frame
+ * @sz: number of dwords
+ */
+static inline void
+dprint_dump_req(void *req, int sz)
+{
+ int i;
+ __le32 *mfp = (__le32 *)req;
+
+ pr_info("request:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
+ }
+ pr_info("\n");
+}
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
new file mode 100644
index 000000000000..8fe6e0bf342e
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -0,0 +1,6431 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2023 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#include "mpi3mr.h"
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+static int
+mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason);
+static int mpi3mr_setup_admin_qpair(struct mpi3mr_ioc *mrioc);
+static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc,
+ struct mpi3_ioc_facts_data *facts_data);
+static void mpi3mr_pel_wait_complete(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd);
+static int mpi3mr_check_op_admin_proc(struct mpi3mr_ioc *mrioc);
+static int poll_queues;
+module_param(poll_queues, int, 0444);
+MODULE_PARM_DESC(poll_queues, "Number of queues for io_uring poll mode. (Range 1 - 126)");
+
+#if defined(writeq) && defined(CONFIG_64BIT)
+static inline void mpi3mr_writeq(__u64 b, void __iomem *addr,
+ spinlock_t *write_queue_lock)
+{
+ writeq(b, addr);
+}
+#else
+static inline void mpi3mr_writeq(__u64 b, void __iomem *addr,
+ spinlock_t *write_queue_lock)
+{
+ __u64 data_out = b;
+ unsigned long flags;
+
+ spin_lock_irqsave(write_queue_lock, flags);
+ writel((u32)(data_out), addr);
+ writel((u32)(data_out >> 32), (addr + 4));
+ spin_unlock_irqrestore(write_queue_lock, flags);
+}
+#endif
+
+static inline bool
+mpi3mr_check_req_qfull(struct op_req_qinfo *op_req_q)
+{
+ u16 pi, ci, max_entries;
+ bool is_qfull = false;
+
+ pi = op_req_q->pi;
+ ci = READ_ONCE(op_req_q->ci);
+ max_entries = op_req_q->num_requests;
+
+ if ((ci == (pi + 1)) || ((!ci) && (pi == (max_entries - 1))))
+ is_qfull = true;
+
+ return is_qfull;
+}
+
+static void mpi3mr_sync_irqs(struct mpi3mr_ioc *mrioc)
+{
+ u16 i, max_vectors;
+
+ max_vectors = mrioc->intr_info_count;
+
+ for (i = 0; i < max_vectors; i++)
+ synchronize_irq(pci_irq_vector(mrioc->pdev, i));
+}
+
+void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc)
+{
+ mrioc->intr_enabled = 0;
+ mpi3mr_sync_irqs(mrioc);
+}
+
+void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc)
+{
+ mrioc->intr_enabled = 1;
+}
+
+static void mpi3mr_cleanup_isr(struct mpi3mr_ioc *mrioc)
+{
+ u16 i;
+
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ if (!mrioc->intr_info)
+ return;
+
+ for (i = 0; i < mrioc->intr_info_count; i++)
+ free_irq(pci_irq_vector(mrioc->pdev, i),
+ (mrioc->intr_info + i));
+
+ kfree(mrioc->intr_info);
+ mrioc->intr_info = NULL;
+ mrioc->intr_info_count = 0;
+ mrioc->is_intr_info_set = false;
+ pci_free_irq_vectors(mrioc->pdev);
+}
+
+void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length,
+ dma_addr_t dma_addr)
+{
+ struct mpi3_sge_common *sgel = paddr;
+
+ sgel->flags = flags;
+ sgel->length = cpu_to_le32(length);
+ sgel->address = cpu_to_le64(dma_addr);
+}
+
+void mpi3mr_build_zero_len_sge(void *paddr)
+{
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+
+ mpi3mr_add_sg_single(paddr, sgl_flags, 0, -1);
+}
+
+void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr)
+{
+ if (!phys_addr)
+ return NULL;
+
+ if ((phys_addr < mrioc->reply_buf_dma) ||
+ (phys_addr > mrioc->reply_buf_dma_max_address))
+ return NULL;
+
+ return mrioc->reply_buf + (phys_addr - mrioc->reply_buf_dma);
+}
+
+void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr)
+{
+ if (!phys_addr)
+ return NULL;
+
+ return mrioc->sense_buf + (phys_addr - mrioc->sense_buf_dma);
+}
+
+static void mpi3mr_repost_reply_buf(struct mpi3mr_ioc *mrioc,
+ u64 reply_dma)
+{
+ u32 old_idx = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->reply_free_queue_lock, flags);
+ old_idx = mrioc->reply_free_queue_host_index;
+ mrioc->reply_free_queue_host_index = (
+ (mrioc->reply_free_queue_host_index ==
+ (mrioc->reply_free_qsz - 1)) ? 0 :
+ (mrioc->reply_free_queue_host_index + 1));
+ mrioc->reply_free_q[old_idx] = cpu_to_le64(reply_dma);
+ writel(mrioc->reply_free_queue_host_index,
+ &mrioc->sysif_regs->reply_free_host_index);
+ spin_unlock_irqrestore(&mrioc->reply_free_queue_lock, flags);
+}
+
+void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
+ u64 sense_buf_dma)
+{
+ u32 old_idx = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->sbq_lock, flags);
+ old_idx = mrioc->sbq_host_index;
+ mrioc->sbq_host_index = ((mrioc->sbq_host_index ==
+ (mrioc->sense_buf_q_sz - 1)) ? 0 :
+ (mrioc->sbq_host_index + 1));
+ mrioc->sense_buf_q[old_idx] = cpu_to_le64(sense_buf_dma);
+ writel(mrioc->sbq_host_index,
+ &mrioc->sysif_regs->sense_buffer_free_host_index);
+ spin_unlock_irqrestore(&mrioc->sbq_lock, flags);
+}
+
+static void mpi3mr_print_event_data(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ char *desc = NULL;
+ u16 event;
+
+ if (!(mrioc->logging_level & MPI3_DEBUG_EVENT))
+ return;
+
+ event = event_reply->event;
+
+ switch (event) {
+ case MPI3_EVENT_LOG_DATA:
+ desc = "Log Data";
+ break;
+ case MPI3_EVENT_CHANGE:
+ desc = "Event Change";
+ break;
+ case MPI3_EVENT_GPIO_INTERRUPT:
+ desc = "GPIO Interrupt";
+ break;
+ case MPI3_EVENT_CABLE_MGMT:
+ desc = "Cable Management";
+ break;
+ case MPI3_EVENT_ENERGY_PACK_CHANGE:
+ desc = "Energy Pack Change";
+ break;
+ case MPI3_EVENT_DEVICE_ADDED:
+ {
+ struct mpi3_device_page0 *event_data =
+ (struct mpi3_device_page0 *)event_reply->event_data;
+ ioc_info(mrioc, "Device Added: dev=0x%04x Form=0x%x\n",
+ event_data->dev_handle, event_data->device_form);
+ return;
+ }
+ case MPI3_EVENT_DEVICE_INFO_CHANGED:
+ {
+ struct mpi3_device_page0 *event_data =
+ (struct mpi3_device_page0 *)event_reply->event_data;
+ ioc_info(mrioc, "Device Info Changed: dev=0x%04x Form=0x%x\n",
+ event_data->dev_handle, event_data->device_form);
+ return;
+ }
+ case MPI3_EVENT_DEVICE_STATUS_CHANGE:
+ {
+ struct mpi3_event_data_device_status_change *event_data =
+ (struct mpi3_event_data_device_status_change *)event_reply->event_data;
+ ioc_info(mrioc, "Device status Change: dev=0x%04x RC=0x%x\n",
+ event_data->dev_handle, event_data->reason_code);
+ return;
+ }
+ case MPI3_EVENT_SAS_DISCOVERY:
+ {
+ struct mpi3_event_data_sas_discovery *event_data =
+ (struct mpi3_event_data_sas_discovery *)event_reply->event_data;
+ ioc_info(mrioc, "SAS Discovery: (%s) status (0x%08x)\n",
+ (event_data->reason_code == MPI3_EVENT_SAS_DISC_RC_STARTED) ?
+ "start" : "stop",
+ le32_to_cpu(event_data->discovery_status));
+ return;
+ }
+ case MPI3_EVENT_SAS_BROADCAST_PRIMITIVE:
+ desc = "SAS Broadcast Primitive";
+ break;
+ case MPI3_EVENT_SAS_NOTIFY_PRIMITIVE:
+ desc = "SAS Notify Primitive";
+ break;
+ case MPI3_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
+ desc = "SAS Init Device Status Change";
+ break;
+ case MPI3_EVENT_SAS_INIT_TABLE_OVERFLOW:
+ desc = "SAS Init Table Overflow";
+ break;
+ case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ desc = "SAS Topology Change List";
+ break;
+ case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
+ desc = "Enclosure Device Status Change";
+ break;
+ case MPI3_EVENT_ENCL_DEVICE_ADDED:
+ desc = "Enclosure Added";
+ break;
+ case MPI3_EVENT_HARD_RESET_RECEIVED:
+ desc = "Hard Reset Received";
+ break;
+ case MPI3_EVENT_SAS_PHY_COUNTER:
+ desc = "SAS PHY Counter";
+ break;
+ case MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
+ desc = "SAS Device Discovery Error";
+ break;
+ case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ desc = "PCIE Topology Change List";
+ break;
+ case MPI3_EVENT_PCIE_ENUMERATION:
+ {
+ struct mpi3_event_data_pcie_enumeration *event_data =
+ (struct mpi3_event_data_pcie_enumeration *)event_reply->event_data;
+ ioc_info(mrioc, "PCIE Enumeration: (%s)",
+ (event_data->reason_code ==
+ MPI3_EVENT_PCIE_ENUM_RC_STARTED) ? "start" : "stop");
+ if (event_data->enumeration_status)
+ ioc_info(mrioc, "enumeration_status(0x%08x)\n",
+ le32_to_cpu(event_data->enumeration_status));
+ return;
+ }
+ case MPI3_EVENT_PREPARE_FOR_RESET:
+ desc = "Prepare For Reset";
+ break;
+ case MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE:
+ desc = "Diagnostic Buffer Status Change";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ ioc_info(mrioc, "%s\n", desc);
+}
+
+static void mpi3mr_handle_events(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply *def_reply)
+{
+ struct mpi3_event_notification_reply *event_reply =
+ (struct mpi3_event_notification_reply *)def_reply;
+
+ mrioc->change_count = le16_to_cpu(event_reply->ioc_change_count);
+ mpi3mr_print_event_data(mrioc, event_reply);
+ mpi3mr_os_handle_events(mrioc, event_reply);
+}
+
+static struct mpi3mr_drv_cmd *
+mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag,
+ struct mpi3_default_reply *def_reply)
+{
+ u16 idx;
+
+ switch (host_tag) {
+ case MPI3MR_HOSTTAG_INITCMDS:
+ return &mrioc->init_cmds;
+ case MPI3MR_HOSTTAG_CFG_CMDS:
+ return &mrioc->cfg_cmds;
+ case MPI3MR_HOSTTAG_BSG_CMDS:
+ return &mrioc->bsg_cmds;
+ case MPI3MR_HOSTTAG_BLK_TMS:
+ return &mrioc->host_tm_cmds;
+ case MPI3MR_HOSTTAG_PEL_ABORT:
+ return &mrioc->pel_abort_cmd;
+ case MPI3MR_HOSTTAG_PEL_WAIT:
+ return &mrioc->pel_cmds;
+ case MPI3MR_HOSTTAG_TRANSPORT_CMDS:
+ return &mrioc->transport_cmds;
+ case MPI3MR_HOSTTAG_INVALID:
+ if (def_reply && def_reply->function ==
+ MPI3_FUNCTION_EVENT_NOTIFICATION)
+ mpi3mr_handle_events(mrioc, def_reply);
+ return NULL;
+ default:
+ break;
+ }
+ if (host_tag >= MPI3MR_HOSTTAG_DEVRMCMD_MIN &&
+ host_tag <= MPI3MR_HOSTTAG_DEVRMCMD_MAX) {
+ idx = host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ return &mrioc->dev_rmhs_cmds[idx];
+ }
+
+ if (host_tag >= MPI3MR_HOSTTAG_EVTACKCMD_MIN &&
+ host_tag <= MPI3MR_HOSTTAG_EVTACKCMD_MAX) {
+ idx = host_tag - MPI3MR_HOSTTAG_EVTACKCMD_MIN;
+ return &mrioc->evtack_cmds[idx];
+ }
+
+ return NULL;
+}
+
+static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma)
+{
+ u16 reply_desc_type, host_tag = 0;
+ u16 ioc_status = MPI3_IOCSTATUS_SUCCESS;
+ u16 masked_ioc_status = MPI3_IOCSTATUS_SUCCESS;
+ u32 ioc_loginfo = 0, sense_count = 0;
+ struct mpi3_status_reply_descriptor *status_desc;
+ struct mpi3_address_reply_descriptor *addr_desc;
+ struct mpi3_success_reply_descriptor *success_desc;
+ struct mpi3_default_reply *def_reply = NULL;
+ struct mpi3mr_drv_cmd *cmdptr = NULL;
+ struct mpi3_scsi_io_reply *scsi_reply;
+ struct scsi_sense_hdr sshdr;
+ u8 *sense_buf = NULL;
+
+ *reply_dma = 0;
+ reply_desc_type = le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK;
+ switch (reply_desc_type) {
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS:
+ status_desc = (struct mpi3_status_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(status_desc->host_tag);
+ ioc_status = le16_to_cpu(status_desc->ioc_status);
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info);
+ masked_ioc_status = ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
+ mpi3mr_reply_trigger(mrioc, masked_ioc_status, ioc_loginfo);
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY:
+ addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc;
+ *reply_dma = le64_to_cpu(addr_desc->reply_frame_address);
+ def_reply = mpi3mr_get_reply_virt_addr(mrioc, *reply_dma);
+ if (!def_reply)
+ goto out;
+ host_tag = le16_to_cpu(def_reply->host_tag);
+ ioc_status = le16_to_cpu(def_reply->ioc_status);
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(def_reply->ioc_log_info);
+ masked_ioc_status = ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
+ if (def_reply->function == MPI3_FUNCTION_SCSI_IO) {
+ scsi_reply = (struct mpi3_scsi_io_reply *)def_reply;
+ sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+ sense_count = le32_to_cpu(scsi_reply->sense_count);
+ if (sense_buf) {
+ scsi_normalize_sense(sense_buf, sense_count,
+ &sshdr);
+ mpi3mr_scsisense_trigger(mrioc, sshdr.sense_key,
+ sshdr.asc, sshdr.ascq);
+ }
+ }
+ mpi3mr_reply_trigger(mrioc, masked_ioc_status, ioc_loginfo);
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS:
+ success_desc = (struct mpi3_success_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(success_desc->host_tag);
+ break;
+ default:
+ break;
+ }
+
+ cmdptr = mpi3mr_get_drv_cmd(mrioc, host_tag, def_reply);
+ if (cmdptr) {
+ if (cmdptr->state & MPI3MR_CMD_PENDING) {
+ cmdptr->state |= MPI3MR_CMD_COMPLETE;
+ cmdptr->ioc_loginfo = ioc_loginfo;
+ if (host_tag == MPI3MR_HOSTTAG_BSG_CMDS)
+ cmdptr->ioc_status = ioc_status;
+ else
+ cmdptr->ioc_status = masked_ioc_status;
+ cmdptr->state &= ~MPI3MR_CMD_PENDING;
+ if (def_reply) {
+ cmdptr->state |= MPI3MR_CMD_REPLY_VALID;
+ memcpy((u8 *)cmdptr->reply, (u8 *)def_reply,
+ mrioc->reply_sz);
+ }
+ if (sense_buf && cmdptr->sensebuf) {
+ cmdptr->is_sense = 1;
+ memcpy(cmdptr->sensebuf, sense_buf,
+ MPI3MR_SENSE_BUF_SZ);
+ }
+ if (cmdptr->is_waiting) {
+ cmdptr->is_waiting = 0;
+ complete(&cmdptr->done);
+ } else if (cmdptr->callback)
+ cmdptr->callback(mrioc, cmdptr);
+ }
+ }
+out:
+ if (sense_buf)
+ mpi3mr_repost_sense_buf(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+}
+
+int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc)
+{
+ u32 exp_phase = mrioc->admin_reply_ephase;
+ u32 admin_reply_ci = mrioc->admin_reply_ci;
+ u32 num_admin_replies = 0;
+ u64 reply_dma = 0;
+ u16 threshold_comps = 0;
+ struct mpi3_default_reply_descriptor *reply_desc;
+
+ if (!atomic_add_unless(&mrioc->admin_reply_q_in_use, 1, 1)) {
+ atomic_inc(&mrioc->admin_pend_isr);
+ return 0;
+ }
+
+ atomic_set(&mrioc->admin_pend_isr, 0);
+ reply_desc = (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base +
+ admin_reply_ci;
+
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
+ atomic_dec(&mrioc->admin_reply_q_in_use);
+ return 0;
+ }
+
+ do {
+ if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
+ break;
+
+ mrioc->admin_req_ci = le16_to_cpu(reply_desc->request_queue_ci);
+ mpi3mr_process_admin_reply_desc(mrioc, reply_desc, &reply_dma);
+ if (reply_dma)
+ mpi3mr_repost_reply_buf(mrioc, reply_dma);
+ num_admin_replies++;
+ threshold_comps++;
+ if (++admin_reply_ci == mrioc->num_admin_replies) {
+ admin_reply_ci = 0;
+ exp_phase ^= 1;
+ }
+ reply_desc =
+ (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base +
+ admin_reply_ci;
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
+ break;
+ if (threshold_comps == MPI3MR_THRESHOLD_REPLY_COUNT) {
+ writel(admin_reply_ci,
+ &mrioc->sysif_regs->admin_reply_queue_ci);
+ threshold_comps = 0;
+ }
+ } while (1);
+
+ writel(admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci);
+ mrioc->admin_reply_ci = admin_reply_ci;
+ mrioc->admin_reply_ephase = exp_phase;
+ atomic_dec(&mrioc->admin_reply_q_in_use);
+
+ return num_admin_replies;
+}
+
+/**
+ * mpi3mr_get_reply_desc - get reply descriptor frame corresponding to
+ * queue's consumer index from operational reply descriptor queue.
+ * @op_reply_q: op_reply_qinfo object
+ * @reply_ci: operational reply descriptor's queue consumer index
+ *
+ * Returns: reply descriptor frame address
+ */
+static inline struct mpi3_default_reply_descriptor *
+mpi3mr_get_reply_desc(struct op_reply_qinfo *op_reply_q, u32 reply_ci)
+{
+ void *segment_base_addr;
+ struct segments *segments = op_reply_q->q_segments;
+ struct mpi3_default_reply_descriptor *reply_desc = NULL;
+
+ segment_base_addr =
+ segments[reply_ci / op_reply_q->segment_qd].segment;
+ reply_desc = (struct mpi3_default_reply_descriptor *)segment_base_addr +
+ (reply_ci % op_reply_q->segment_qd);
+ return reply_desc;
+}
+
+/**
+ * mpi3mr_process_op_reply_q - Operational reply queue handler
+ * @mrioc: Adapter instance reference
+ * @op_reply_q: Operational reply queue info
+ *
+ * Checks the specific operational reply queue and drains the
+ * reply queue entries until the queue is empty and process the
+ * individual reply descriptors.
+ *
+ * Return: 0 if queue is already processed,or number of reply
+ * descriptors processed.
+ */
+int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
+ struct op_reply_qinfo *op_reply_q)
+{
+ struct op_req_qinfo *op_req_q;
+ u32 exp_phase;
+ u32 reply_ci;
+ u32 num_op_reply = 0;
+ u64 reply_dma = 0;
+ struct mpi3_default_reply_descriptor *reply_desc;
+ u16 req_q_idx = 0, reply_qidx, threshold_comps = 0;
+
+ reply_qidx = op_reply_q->qid - 1;
+
+ if (!atomic_add_unless(&op_reply_q->in_use, 1, 1))
+ return 0;
+
+ exp_phase = op_reply_q->ephase;
+ reply_ci = op_reply_q->ci;
+
+ reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
+ atomic_dec(&op_reply_q->in_use);
+ return 0;
+ }
+
+ do {
+ if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
+ break;
+
+ req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1;
+ op_req_q = &mrioc->req_qinfo[req_q_idx];
+
+ WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci));
+ mpi3mr_process_op_reply_desc(mrioc, reply_desc, &reply_dma,
+ reply_qidx);
+
+ if (reply_dma)
+ mpi3mr_repost_reply_buf(mrioc, reply_dma);
+ num_op_reply++;
+ threshold_comps++;
+
+ if (++reply_ci == op_reply_q->num_replies) {
+ reply_ci = 0;
+ exp_phase ^= 1;
+ }
+
+ reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
+
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
+ break;
+#ifndef CONFIG_PREEMPT_RT
+ /*
+ * Exit completion loop to avoid CPU lockup
+ * Ensure remaining completion happens from threaded ISR.
+ */
+ if (num_op_reply > mrioc->max_host_ios) {
+ op_reply_q->enable_irq_poll = true;
+ break;
+ }
+#endif
+ if (threshold_comps == MPI3MR_THRESHOLD_REPLY_COUNT) {
+ writel(reply_ci,
+ &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].consumer_index);
+ atomic_sub(threshold_comps, &op_reply_q->pend_ios);
+ threshold_comps = 0;
+ }
+ } while (1);
+
+ writel(reply_ci,
+ &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].consumer_index);
+ op_reply_q->ci = reply_ci;
+ op_reply_q->ephase = exp_phase;
+ atomic_sub(threshold_comps, &op_reply_q->pend_ios);
+ atomic_dec(&op_reply_q->in_use);
+ return num_op_reply;
+}
+
+/**
+ * mpi3mr_blk_mq_poll - Operational reply queue handler
+ * @shost: SCSI Host reference
+ * @queue_num: Request queue number (w.r.t OS it is hardware context number)
+ *
+ * Checks the specific operational reply queue and drains the
+ * reply queue entries until the queue is empty and process the
+ * individual reply descriptors.
+ *
+ * Return: 0 if queue is already processed,or number of reply
+ * descriptors processed.
+ */
+int mpi3mr_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num)
+{
+ int num_entries = 0;
+ struct mpi3mr_ioc *mrioc;
+
+ mrioc = (struct mpi3mr_ioc *)shost->hostdata;
+
+ if ((mrioc->reset_in_progress || mrioc->prepare_for_reset ||
+ mrioc->unrecoverable || mrioc->pci_err_recovery))
+ return 0;
+
+ num_entries = mpi3mr_process_op_reply_q(mrioc,
+ &mrioc->op_reply_qinfo[queue_num]);
+
+ return num_entries;
+}
+
+static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata)
+{
+ struct mpi3mr_intr_info *intr_info = privdata;
+ struct mpi3mr_ioc *mrioc;
+ u16 midx;
+ u32 num_admin_replies = 0, num_op_reply = 0;
+
+ if (!intr_info)
+ return IRQ_NONE;
+
+ mrioc = intr_info->mrioc;
+
+ if (!mrioc->intr_enabled)
+ return IRQ_NONE;
+
+ midx = intr_info->msix_index;
+
+ if (!midx)
+ num_admin_replies = mpi3mr_process_admin_reply_q(mrioc);
+ if (intr_info->op_reply_q)
+ num_op_reply = mpi3mr_process_op_reply_q(mrioc,
+ intr_info->op_reply_q);
+
+ if (num_admin_replies || num_op_reply)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+#ifndef CONFIG_PREEMPT_RT
+
+static irqreturn_t mpi3mr_isr(int irq, void *privdata)
+{
+ struct mpi3mr_intr_info *intr_info = privdata;
+ int ret;
+
+ if (!intr_info)
+ return IRQ_NONE;
+
+ /* Call primary ISR routine */
+ ret = mpi3mr_isr_primary(irq, privdata);
+
+ /*
+ * If more IOs are expected, schedule IRQ polling thread.
+ * Otherwise exit from ISR.
+ */
+ if (!intr_info->op_reply_q)
+ return ret;
+
+ if (!intr_info->op_reply_q->enable_irq_poll ||
+ !atomic_read(&intr_info->op_reply_q->pend_ios))
+ return ret;
+
+ disable_irq_nosync(intr_info->os_irq);
+
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * mpi3mr_isr_poll - Reply queue polling routine
+ * @irq: IRQ
+ * @privdata: Interrupt info
+ *
+ * poll for pending I/O completions in a loop until pending I/Os
+ * present or controller queue depth I/Os are processed.
+ *
+ * Return: IRQ_NONE or IRQ_HANDLED
+ */
+static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata)
+{
+ struct mpi3mr_intr_info *intr_info = privdata;
+ struct mpi3mr_ioc *mrioc;
+ u16 midx;
+ u32 num_op_reply = 0;
+
+ if (!intr_info || !intr_info->op_reply_q)
+ return IRQ_NONE;
+
+ mrioc = intr_info->mrioc;
+ midx = intr_info->msix_index;
+
+ /* Poll for pending IOs completions */
+ do {
+ if (!mrioc->intr_enabled || mrioc->unrecoverable)
+ break;
+
+ if (!midx)
+ mpi3mr_process_admin_reply_q(mrioc);
+ if (intr_info->op_reply_q)
+ num_op_reply +=
+ mpi3mr_process_op_reply_q(mrioc,
+ intr_info->op_reply_q);
+
+ usleep_range(MPI3MR_IRQ_POLL_SLEEP, MPI3MR_IRQ_POLL_SLEEP + 1);
+
+ } while (atomic_read(&intr_info->op_reply_q->pend_ios) &&
+ (num_op_reply < mrioc->max_host_ios));
+
+ intr_info->op_reply_q->enable_irq_poll = false;
+ enable_irq(intr_info->os_irq);
+
+ return IRQ_HANDLED;
+}
+
+#endif
+
+/**
+ * mpi3mr_request_irq - Request IRQ and register ISR
+ * @mrioc: Adapter instance reference
+ * @index: IRQ vector index
+ *
+ * Request threaded ISR with primary ISR and secondary
+ *
+ * Return: 0 on success and non zero on failures.
+ */
+static inline int mpi3mr_request_irq(struct mpi3mr_ioc *mrioc, u16 index)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+ struct mpi3mr_intr_info *intr_info = mrioc->intr_info + index;
+ int retval = 0;
+
+ intr_info->mrioc = mrioc;
+ intr_info->msix_index = index;
+ intr_info->op_reply_q = NULL;
+
+ snprintf(intr_info->name, MPI3MR_NAME_LENGTH, "%s%d-msix%d",
+ mrioc->driver_name, mrioc->id, index);
+
+#ifndef CONFIG_PREEMPT_RT
+ retval = request_threaded_irq(pci_irq_vector(pdev, index), mpi3mr_isr,
+ mpi3mr_isr_poll, IRQF_SHARED, intr_info->name, intr_info);
+#else
+ retval = request_threaded_irq(pci_irq_vector(pdev, index), mpi3mr_isr_primary,
+ NULL, IRQF_SHARED, intr_info->name, intr_info);
+#endif
+ if (retval) {
+ ioc_err(mrioc, "%s: Unable to allocate interrupt %d!\n",
+ intr_info->name, pci_irq_vector(pdev, index));
+ return retval;
+ }
+
+ intr_info->os_irq = pci_irq_vector(pdev, index);
+ return retval;
+}
+
+static void mpi3mr_calc_poll_queues(struct mpi3mr_ioc *mrioc, u16 max_vectors)
+{
+ if (!mrioc->requested_poll_qcount)
+ return;
+
+ /* Reserved for Admin and Default Queue */
+ if (max_vectors > 2 &&
+ (mrioc->requested_poll_qcount < max_vectors - 2)) {
+ ioc_info(mrioc,
+ "enabled polled queues (%d) msix (%d)\n",
+ mrioc->requested_poll_qcount, max_vectors);
+ } else {
+ ioc_info(mrioc,
+ "disabled polled queues (%d) msix (%d) because of no resources for default queue\n",
+ mrioc->requested_poll_qcount, max_vectors);
+ mrioc->requested_poll_qcount = 0;
+ }
+}
+
+/**
+ * mpi3mr_setup_isr - Setup ISR for the controller
+ * @mrioc: Adapter instance reference
+ * @setup_one: Request one IRQ or more
+ *
+ * Allocate IRQ vectors and call mpi3mr_request_irq to setup ISR
+ *
+ * Return: 0 on success and non zero on failures.
+ */
+static int mpi3mr_setup_isr(struct mpi3mr_ioc *mrioc, u8 setup_one)
+{
+ unsigned int irq_flags = PCI_IRQ_MSIX;
+ int max_vectors, min_vec;
+ int retval;
+ int i;
+ struct irq_affinity desc = { .pre_vectors = 1, .post_vectors = 1 };
+
+ if (mrioc->is_intr_info_set)
+ return 0;
+
+ mpi3mr_cleanup_isr(mrioc);
+
+ if (setup_one || reset_devices) {
+ max_vectors = 1;
+ retval = pci_alloc_irq_vectors(mrioc->pdev,
+ 1, max_vectors, irq_flags);
+ if (retval < 0) {
+ ioc_err(mrioc, "cannot allocate irq vectors, ret %d\n",
+ retval);
+ goto out_failed;
+ }
+ } else {
+ max_vectors =
+ min_t(int, mrioc->cpu_count + 1 +
+ mrioc->requested_poll_qcount, mrioc->msix_count);
+
+ mpi3mr_calc_poll_queues(mrioc, max_vectors);
+
+ ioc_info(mrioc,
+ "MSI-X vectors supported: %d, no of cores: %d,",
+ mrioc->msix_count, mrioc->cpu_count);
+ ioc_info(mrioc,
+ "MSI-x vectors requested: %d poll_queues %d\n",
+ max_vectors, mrioc->requested_poll_qcount);
+
+ desc.post_vectors = mrioc->requested_poll_qcount;
+ min_vec = desc.pre_vectors + desc.post_vectors;
+ irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES;
+
+ retval = pci_alloc_irq_vectors_affinity(mrioc->pdev,
+ min_vec, max_vectors, irq_flags, &desc);
+
+ if (retval < 0) {
+ ioc_err(mrioc, "cannot allocate irq vectors, ret %d\n",
+ retval);
+ goto out_failed;
+ }
+
+
+ /*
+ * If only one MSI-x is allocated, then MSI-x 0 will be shared
+ * between Admin queue and operational queue
+ */
+ if (retval == min_vec)
+ mrioc->op_reply_q_offset = 0;
+ else if (retval != (max_vectors)) {
+ ioc_info(mrioc,
+ "allocated vectors (%d) are less than configured (%d)\n",
+ retval, max_vectors);
+ }
+
+ max_vectors = retval;
+ mrioc->op_reply_q_offset = (max_vectors > 1) ? 1 : 0;
+
+ mpi3mr_calc_poll_queues(mrioc, max_vectors);
+
+ }
+
+ mrioc->intr_info = kzalloc(sizeof(struct mpi3mr_intr_info) * max_vectors,
+ GFP_KERNEL);
+ if (!mrioc->intr_info) {
+ retval = -ENOMEM;
+ pci_free_irq_vectors(mrioc->pdev);
+ goto out_failed;
+ }
+ for (i = 0; i < max_vectors; i++) {
+ retval = mpi3mr_request_irq(mrioc, i);
+ if (retval) {
+ mrioc->intr_info_count = i;
+ goto out_failed;
+ }
+ }
+ if (reset_devices || !setup_one)
+ mrioc->is_intr_info_set = true;
+ mrioc->intr_info_count = max_vectors;
+ mpi3mr_ioc_enable_intr(mrioc);
+ return 0;
+
+out_failed:
+ mpi3mr_cleanup_isr(mrioc);
+
+ return retval;
+}
+
+static const struct {
+ enum mpi3mr_iocstate value;
+ char *name;
+} mrioc_states[] = {
+ { MRIOC_STATE_READY, "ready" },
+ { MRIOC_STATE_FAULT, "fault" },
+ { MRIOC_STATE_RESET, "reset" },
+ { MRIOC_STATE_BECOMING_READY, "becoming ready" },
+ { MRIOC_STATE_RESET_REQUESTED, "reset requested" },
+ { MRIOC_STATE_UNRECOVERABLE, "unrecoverable error" },
+};
+
+static const char *mpi3mr_iocstate_name(enum mpi3mr_iocstate mrioc_state)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(mrioc_states); i++) {
+ if (mrioc_states[i].value == mrioc_state) {
+ name = mrioc_states[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
+/* Reset reason to name mapper structure*/
+static const struct {
+ enum mpi3mr_reset_reason value;
+ char *name;
+} mpi3mr_reset_reason_codes[] = {
+ { MPI3MR_RESET_FROM_BRINGUP, "timeout in bringup" },
+ { MPI3MR_RESET_FROM_FAULT_WATCH, "fault" },
+ { MPI3MR_RESET_FROM_APP, "application invocation" },
+ { MPI3MR_RESET_FROM_EH_HOS, "error handling" },
+ { MPI3MR_RESET_FROM_TM_TIMEOUT, "TM timeout" },
+ { MPI3MR_RESET_FROM_APP_TIMEOUT, "application command timeout" },
+ { MPI3MR_RESET_FROM_MUR_FAILURE, "MUR failure" },
+ { MPI3MR_RESET_FROM_CTLR_CLEANUP, "timeout in controller cleanup" },
+ { MPI3MR_RESET_FROM_CIACTIV_FAULT, "component image activation fault" },
+ { MPI3MR_RESET_FROM_PE_TIMEOUT, "port enable timeout" },
+ { MPI3MR_RESET_FROM_TSU_TIMEOUT, "time stamp update timeout" },
+ { MPI3MR_RESET_FROM_DELREQQ_TIMEOUT, "delete request queue timeout" },
+ { MPI3MR_RESET_FROM_DELREPQ_TIMEOUT, "delete reply queue timeout" },
+ {
+ MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT,
+ "create request queue timeout"
+ },
+ {
+ MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT,
+ "create reply queue timeout"
+ },
+ { MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT, "IOC facts timeout" },
+ { MPI3MR_RESET_FROM_IOCINIT_TIMEOUT, "IOC init timeout" },
+ { MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT, "event notify timeout" },
+ { MPI3MR_RESET_FROM_EVTACK_TIMEOUT, "event acknowledgment timeout" },
+ {
+ MPI3MR_RESET_FROM_CIACTVRST_TIMER,
+ "component image activation timeout"
+ },
+ {
+ MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT,
+ "get package version timeout"
+ },
+ { MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" },
+ { MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" },
+ {
+ MPI3MR_RESET_FROM_DIAG_BUFFER_POST_TIMEOUT,
+ "diagnostic buffer post timeout"
+ },
+ {
+ MPI3MR_RESET_FROM_DIAG_BUFFER_RELEASE_TIMEOUT,
+ "diagnostic buffer release timeout"
+ },
+ { MPI3MR_RESET_FROM_FIRMWARE, "firmware asynchronous reset" },
+ { MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT, "configuration request timeout"},
+ { MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT, "timeout of a SAS transport layer request" },
+};
+
+/**
+ * mpi3mr_reset_rc_name - get reset reason code name
+ * @reason_code: reset reason code value
+ *
+ * Map reset reason to an NULL terminated ASCII string
+ *
+ * Return: name corresponding to reset reason value or NULL.
+ */
+static const char *mpi3mr_reset_rc_name(enum mpi3mr_reset_reason reason_code)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_reason_codes); i++) {
+ if (mpi3mr_reset_reason_codes[i].value == reason_code) {
+ name = mpi3mr_reset_reason_codes[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
+/* Reset type to name mapper structure*/
+static const struct {
+ u16 reset_type;
+ char *name;
+} mpi3mr_reset_types[] = {
+ { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, "soft" },
+ { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, "diag fault" },
+};
+
+/**
+ * mpi3mr_reset_type_name - get reset type name
+ * @reset_type: reset type value
+ *
+ * Map reset type to an NULL terminated ASCII string
+ *
+ * Return: name corresponding to reset type value or NULL.
+ */
+static const char *mpi3mr_reset_type_name(u16 reset_type)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_types); i++) {
+ if (mpi3mr_reset_types[i].reset_type == reset_type) {
+ name = mpi3mr_reset_types[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
+/**
+ * mpi3mr_is_fault_recoverable - Read fault code and decide
+ * whether the controller can be recoverable
+ * @mrioc: Adapter instance reference
+ * Return: true if fault is recoverable, false otherwise.
+ */
+static inline bool mpi3mr_is_fault_recoverable(struct mpi3mr_ioc *mrioc)
+{
+ u32 fault;
+
+ fault = (readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK);
+
+ switch (fault) {
+ case MPI3_SYSIF_FAULT_CODE_COMPLETE_RESET_NEEDED:
+ case MPI3_SYSIF_FAULT_CODE_POWER_CYCLE_REQUIRED:
+ ioc_warn(mrioc,
+ "controller requires system power cycle, marking controller as unrecoverable\n");
+ return false;
+ case MPI3_SYSIF_FAULT_CODE_INSUFFICIENT_PCI_SLOT_POWER:
+ ioc_warn(mrioc,
+ "controller faulted due to insufficient power,\n"
+ " try by connecting it to a different slot\n");
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+/**
+ * mpi3mr_print_fault_info - Display fault information
+ * @mrioc: Adapter instance reference
+ *
+ * Display the controller fault information if there is a
+ * controller fault.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_print_fault_info(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_status, code, code1, code2, code3;
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) {
+ code = readl(&mrioc->sysif_regs->fault);
+ code1 = readl(&mrioc->sysif_regs->fault_info[0]);
+ code2 = readl(&mrioc->sysif_regs->fault_info[1]);
+ code3 = readl(&mrioc->sysif_regs->fault_info[2]);
+
+ ioc_info(mrioc,
+ "fault code(0x%08X): Additional code: (0x%08X:0x%08X:0x%08X)\n",
+ code, code1, code2, code3);
+ }
+}
+
+/**
+ * mpi3mr_get_iocstate - Get IOC State
+ * @mrioc: Adapter instance reference
+ *
+ * Return a proper IOC state enum based on the IOC status and
+ * IOC configuration and unrcoverable state of the controller.
+ *
+ * Return: Current IOC state.
+ */
+enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_status, ioc_config;
+ u8 ready, enabled;
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+
+ if (mrioc->unrecoverable)
+ return MRIOC_STATE_UNRECOVERABLE;
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)
+ return MRIOC_STATE_FAULT;
+
+ ready = (ioc_status & MPI3_SYSIF_IOC_STATUS_READY);
+ enabled = (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC);
+
+ if (ready && enabled)
+ return MRIOC_STATE_READY;
+ if ((!ready) && (!enabled))
+ return MRIOC_STATE_RESET;
+ if ((!ready) && (enabled))
+ return MRIOC_STATE_BECOMING_READY;
+
+ return MRIOC_STATE_RESET_REQUESTED;
+}
+
+/**
+ * mpi3mr_free_ioctl_dma_memory - free memory for ioctl dma
+ * @mrioc: Adapter instance reference
+ *
+ * Free the DMA memory allocated for IOCTL handling purpose.
+ *
+ * Return: None
+ */
+static void mpi3mr_free_ioctl_dma_memory(struct mpi3mr_ioc *mrioc)
+{
+ struct dma_memory_desc *mem_desc;
+ u16 i;
+
+ if (!mrioc->ioctl_dma_pool)
+ return;
+
+ for (i = 0; i < MPI3MR_NUM_IOCTL_SGE; i++) {
+ mem_desc = &mrioc->ioctl_sge[i];
+ if (mem_desc->addr) {
+ dma_pool_free(mrioc->ioctl_dma_pool,
+ mem_desc->addr,
+ mem_desc->dma_addr);
+ mem_desc->addr = NULL;
+ }
+ }
+ dma_pool_destroy(mrioc->ioctl_dma_pool);
+ mrioc->ioctl_dma_pool = NULL;
+ mem_desc = &mrioc->ioctl_chain_sge;
+
+ if (mem_desc->addr) {
+ dma_free_coherent(&mrioc->pdev->dev, mem_desc->size,
+ mem_desc->addr, mem_desc->dma_addr);
+ mem_desc->addr = NULL;
+ }
+ mem_desc = &mrioc->ioctl_resp_sge;
+ if (mem_desc->addr) {
+ dma_free_coherent(&mrioc->pdev->dev, mem_desc->size,
+ mem_desc->addr, mem_desc->dma_addr);
+ mem_desc->addr = NULL;
+ }
+
+ mrioc->ioctl_sges_allocated = false;
+}
+
+/**
+ * mpi3mr_alloc_ioctl_dma_memory - Alloc memory for ioctl dma
+ * @mrioc: Adapter instance reference
+ *
+ * This function allocates dmaable memory required to handle the
+ * application issued MPI3 IOCTL requests.
+ *
+ * Return: None
+ */
+static void mpi3mr_alloc_ioctl_dma_memory(struct mpi3mr_ioc *mrioc)
+
+{
+ struct dma_memory_desc *mem_desc;
+ u16 i;
+
+ mrioc->ioctl_dma_pool = dma_pool_create("ioctl dma pool",
+ &mrioc->pdev->dev,
+ MPI3MR_IOCTL_SGE_SIZE,
+ MPI3MR_PAGE_SIZE_4K, 0);
+
+ if (!mrioc->ioctl_dma_pool) {
+ ioc_err(mrioc, "ioctl_dma_pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+
+ for (i = 0; i < MPI3MR_NUM_IOCTL_SGE; i++) {
+ mem_desc = &mrioc->ioctl_sge[i];
+ mem_desc->size = MPI3MR_IOCTL_SGE_SIZE;
+ mem_desc->addr = dma_pool_zalloc(mrioc->ioctl_dma_pool,
+ GFP_KERNEL,
+ &mem_desc->dma_addr);
+ if (!mem_desc->addr)
+ goto out_failed;
+ }
+
+ mem_desc = &mrioc->ioctl_chain_sge;
+ mem_desc->size = MPI3MR_PAGE_SIZE_4K;
+ mem_desc->addr = dma_alloc_coherent(&mrioc->pdev->dev,
+ mem_desc->size,
+ &mem_desc->dma_addr,
+ GFP_KERNEL);
+ if (!mem_desc->addr)
+ goto out_failed;
+
+ mem_desc = &mrioc->ioctl_resp_sge;
+ mem_desc->size = MPI3MR_PAGE_SIZE_4K;
+ mem_desc->addr = dma_alloc_coherent(&mrioc->pdev->dev,
+ mem_desc->size,
+ &mem_desc->dma_addr,
+ GFP_KERNEL);
+ if (!mem_desc->addr)
+ goto out_failed;
+
+ mrioc->ioctl_sges_allocated = true;
+
+ return;
+out_failed:
+ ioc_warn(mrioc, "cannot allocate DMA memory for the mpt commands\n"
+ "from the applications, application interface for MPT command is disabled\n");
+ mpi3mr_free_ioctl_dma_memory(mrioc);
+}
+
+/**
+ * mpi3mr_clear_reset_history - clear reset history
+ * @mrioc: Adapter instance reference
+ *
+ * Write the reset history bit in IOC status to clear the bit,
+ * if it is already set.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_clear_reset_history(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_status;
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)
+ writel(ioc_status, &mrioc->sysif_regs->ioc_status);
+}
+
+/**
+ * mpi3mr_issue_and_process_mur - Message unit Reset handler
+ * @mrioc: Adapter instance reference
+ * @reset_reason: Reset reason code
+ *
+ * Issue Message unit Reset to the controller and wait for it to
+ * be complete.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int mpi3mr_issue_and_process_mur(struct mpi3mr_ioc *mrioc,
+ u32 reset_reason)
+{
+ u32 ioc_config, timeout, ioc_status, scratch_pad0;
+ int retval = -1;
+
+ ioc_info(mrioc, "Issuing Message unit Reset(MUR)\n");
+ if (mrioc->unrecoverable) {
+ ioc_info(mrioc, "IOC is unrecoverable MUR not issued\n");
+ return retval;
+ }
+ mpi3mr_clear_reset_history(mrioc);
+ scratch_pad0 = ((MPI3MR_RESET_REASON_OSTYPE_LINUX <<
+ MPI3MR_RESET_REASON_OSTYPE_SHIFT) |
+ (mrioc->facts.ioc_num <<
+ MPI3MR_RESET_REASON_IOCNUM_SHIFT) | reset_reason);
+ writel(scratch_pad0, &mrioc->sysif_regs->scratchpad[0]);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config &= ~MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC;
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+
+ timeout = MPI3MR_MUR_TIMEOUT * 10;
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)) {
+ mpi3mr_clear_reset_history(mrioc);
+ break;
+ }
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) {
+ mpi3mr_print_fault_info(mrioc);
+ break;
+ }
+ msleep(100);
+ } while (--timeout);
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ if (timeout && !((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) ||
+ (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) ||
+ (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC)))
+ retval = 0;
+
+ ioc_info(mrioc, "Base IOC Sts/Config after %s MUR is (0x%08x)/(0x%08x)\n",
+ (!retval) ? "successful" : "failed", ioc_status, ioc_config);
+ return retval;
+}
+
+/**
+ * mpi3mr_revalidate_factsdata - validate IOCFacts parameters
+ * during reset/resume
+ * @mrioc: Adapter instance reference
+ *
+ * Return: zero if the new IOCFacts parameters value is compatible with
+ * older values else return -EPERM
+ */
+static int
+mpi3mr_revalidate_factsdata(struct mpi3mr_ioc *mrioc)
+{
+ unsigned long *removepend_bitmap;
+
+ if (mrioc->facts.reply_sz > mrioc->reply_sz) {
+ ioc_err(mrioc,
+ "cannot increase reply size from %d to %d\n",
+ mrioc->reply_sz, mrioc->facts.reply_sz);
+ return -EPERM;
+ }
+
+ if (mrioc->facts.max_op_reply_q < mrioc->num_op_reply_q) {
+ ioc_err(mrioc,
+ "cannot reduce number of operational reply queues from %d to %d\n",
+ mrioc->num_op_reply_q,
+ mrioc->facts.max_op_reply_q);
+ return -EPERM;
+ }
+
+ if (mrioc->facts.max_op_req_q < mrioc->num_op_req_q) {
+ ioc_err(mrioc,
+ "cannot reduce number of operational request queues from %d to %d\n",
+ mrioc->num_op_req_q, mrioc->facts.max_op_req_q);
+ return -EPERM;
+ }
+
+ if (mrioc->shost->max_sectors != (mrioc->facts.max_data_length / 512))
+ ioc_err(mrioc, "Warning: The maximum data transfer length\n"
+ "\tchanged after reset: previous(%d), new(%d),\n"
+ "the driver cannot change this at run time\n",
+ mrioc->shost->max_sectors * 512, mrioc->facts.max_data_length);
+
+ if ((mrioc->sas_transport_enabled) && (mrioc->facts.ioc_capabilities &
+ MPI3_IOCFACTS_CAPABILITY_MULTIPATH_SUPPORTED))
+ ioc_err(mrioc,
+ "critical error: multipath capability is enabled at the\n"
+ "\tcontroller while sas transport support is enabled at the\n"
+ "\tdriver, please reboot the system or reload the driver\n");
+
+ if (mrioc->seg_tb_support) {
+ if (!(mrioc->facts.ioc_capabilities &
+ MPI3_IOCFACTS_CAPABILITY_SEG_DIAG_TRACE_SUPPORTED)) {
+ ioc_err(mrioc,
+ "critical error: previously enabled segmented trace\n"
+ " buffer capability is disabled after reset. Please\n"
+ " update the firmware or reboot the system or\n"
+ " reload the driver to enable trace diag buffer\n");
+ mrioc->diag_buffers[0].disabled_after_reset = true;
+ } else
+ mrioc->diag_buffers[0].disabled_after_reset = false;
+ }
+
+ if (mrioc->facts.max_devhandle > mrioc->dev_handle_bitmap_bits) {
+ removepend_bitmap = bitmap_zalloc(mrioc->facts.max_devhandle,
+ GFP_KERNEL);
+ if (!removepend_bitmap) {
+ ioc_err(mrioc,
+ "failed to increase removepend_bitmap bits from %d to %d\n",
+ mrioc->dev_handle_bitmap_bits,
+ mrioc->facts.max_devhandle);
+ return -EPERM;
+ }
+ bitmap_free(mrioc->removepend_bitmap);
+ mrioc->removepend_bitmap = removepend_bitmap;
+ ioc_info(mrioc,
+ "increased bits of dev_handle_bitmap from %d to %d\n",
+ mrioc->dev_handle_bitmap_bits,
+ mrioc->facts.max_devhandle);
+ mrioc->dev_handle_bitmap_bits = mrioc->facts.max_devhandle;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_bring_ioc_ready - Bring controller to ready state
+ * @mrioc: Adapter instance reference
+ *
+ * Set Enable IOC bit in IOC configuration register and wait for
+ * the controller to become ready.
+ *
+ * Return: 0 on success, appropriate error on failure.
+ */
+static int mpi3mr_bring_ioc_ready(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_config, ioc_status, timeout, host_diagnostic;
+ int retval = 0;
+ enum mpi3mr_iocstate ioc_state;
+ u64 base_info;
+ u8 retry = 0;
+ u64 start_time, elapsed_time_sec;
+
+retry_bring_ioc_ready:
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ base_info = lo_hi_readq(&mrioc->sysif_regs->ioc_information);
+ ioc_info(mrioc, "ioc_status(0x%08x), ioc_config(0x%08x), ioc_info(0x%016llx) at the bringup\n",
+ ioc_status, ioc_config, base_info);
+
+ if (!mpi3mr_is_fault_recoverable(mrioc)) {
+ mrioc->unrecoverable = 1;
+ goto out_device_not_present;
+ }
+
+ /*The timeout value is in 2sec unit, changing it to seconds*/
+ mrioc->ready_timeout =
+ ((base_info & MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_MASK) >>
+ MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_SHIFT) * 2;
+
+ ioc_info(mrioc, "ready timeout: %d seconds\n", mrioc->ready_timeout);
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ ioc_info(mrioc, "controller is in %s state during detection\n",
+ mpi3mr_iocstate_name(ioc_state));
+
+ timeout = mrioc->ready_timeout * 10;
+
+ do {
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+
+ if (ioc_state != MRIOC_STATE_BECOMING_READY &&
+ ioc_state != MRIOC_STATE_RESET_REQUESTED)
+ break;
+
+ if (!pci_device_is_present(mrioc->pdev)) {
+ mrioc->unrecoverable = 1;
+ ioc_err(mrioc, "controller is not present while waiting to reset\n");
+ goto out_device_not_present;
+ }
+
+ msleep(100);
+ } while (--timeout);
+
+ if (ioc_state == MRIOC_STATE_READY) {
+ ioc_info(mrioc, "issuing message unit reset (MUR) to bring to reset state\n");
+ retval = mpi3mr_issue_and_process_mur(mrioc,
+ MPI3MR_RESET_FROM_BRINGUP);
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (retval)
+ ioc_err(mrioc,
+ "message unit reset failed with error %d current state %s\n",
+ retval, mpi3mr_iocstate_name(ioc_state));
+ }
+ if (ioc_state != MRIOC_STATE_RESET) {
+ if (ioc_state == MRIOC_STATE_FAULT) {
+ timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10;
+ mpi3mr_print_fault_info(mrioc);
+ do {
+ host_diagnostic =
+ readl(&mrioc->sysif_regs->host_diagnostic);
+ if (!(host_diagnostic &
+ MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS))
+ break;
+ if (!pci_device_is_present(mrioc->pdev)) {
+ mrioc->unrecoverable = 1;
+ ioc_err(mrioc, "controller is not present at the bringup\n");
+ goto out_device_not_present;
+ }
+ msleep(100);
+ } while (--timeout);
+ }
+ mpi3mr_print_fault_info(mrioc);
+ ioc_info(mrioc, "issuing soft reset to bring to reset state\n");
+ retval = mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET,
+ MPI3MR_RESET_FROM_BRINGUP);
+ if (retval) {
+ ioc_err(mrioc,
+ "soft reset failed with error %d\n", retval);
+ goto out_failed;
+ }
+ }
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state != MRIOC_STATE_RESET) {
+ ioc_err(mrioc,
+ "cannot bring controller to reset state, current state: %s\n",
+ mpi3mr_iocstate_name(ioc_state));
+ goto out_failed;
+ }
+ mpi3mr_clear_reset_history(mrioc);
+ retval = mpi3mr_setup_admin_qpair(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to setup admin queues: error %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ ioc_info(mrioc, "bringing controller to ready state\n");
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC;
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+
+ if (retry == 0)
+ start_time = jiffies;
+
+ timeout = mrioc->ready_timeout * 10;
+ do {
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state == MRIOC_STATE_READY) {
+ ioc_info(mrioc,
+ "successfully transitioned to %s state\n",
+ mpi3mr_iocstate_name(ioc_state));
+ return 0;
+ }
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) ||
+ (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)) {
+ mpi3mr_print_fault_info(mrioc);
+ goto out_failed;
+ }
+ if (!pci_device_is_present(mrioc->pdev)) {
+ mrioc->unrecoverable = 1;
+ ioc_err(mrioc,
+ "controller is not present at the bringup\n");
+ retval = -1;
+ goto out_device_not_present;
+ }
+ msleep(100);
+ elapsed_time_sec = jiffies_to_msecs(jiffies - start_time)/1000;
+ } while (elapsed_time_sec < mrioc->ready_timeout);
+
+out_failed:
+ elapsed_time_sec = jiffies_to_msecs(jiffies - start_time)/1000;
+ if ((retry < 2) && (elapsed_time_sec < (mrioc->ready_timeout - 60))) {
+ retry++;
+
+ ioc_warn(mrioc, "retrying to bring IOC ready, retry_count:%d\n"
+ " elapsed time =%llu\n", retry, elapsed_time_sec);
+
+ goto retry_bring_ioc_ready;
+ }
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ ioc_err(mrioc,
+ "failed to bring to ready state, current state: %s\n",
+ mpi3mr_iocstate_name(ioc_state));
+out_device_not_present:
+ return retval;
+}
+
+/**
+ * mpi3mr_soft_reset_success - Check softreset is success or not
+ * @ioc_status: IOC status register value
+ * @ioc_config: IOC config register value
+ *
+ * Check whether the soft reset is successful or not based on
+ * IOC status and IOC config register values.
+ *
+ * Return: True when the soft reset is success, false otherwise.
+ */
+static inline bool
+mpi3mr_soft_reset_success(u32 ioc_status, u32 ioc_config)
+{
+ if (!((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) ||
+ (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC)))
+ return true;
+ return false;
+}
+
+/**
+ * mpi3mr_diagfault_success - Check diag fault is success or not
+ * @mrioc: Adapter reference
+ * @ioc_status: IOC status register value
+ *
+ * Check whether the controller hit diag reset fault code.
+ *
+ * Return: True when there is diag fault, false otherwise.
+ */
+static inline bool mpi3mr_diagfault_success(struct mpi3mr_ioc *mrioc,
+ u32 ioc_status)
+{
+ u32 fault;
+
+ if (!(ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT))
+ return false;
+ fault = readl(&mrioc->sysif_regs->fault) & MPI3_SYSIF_FAULT_CODE_MASK;
+ if (fault == MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET) {
+ mpi3mr_print_fault_info(mrioc);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * mpi3mr_set_diagsave - Set diag save bit for snapdump
+ * @mrioc: Adapter reference
+ *
+ * Set diag save bit in IOC configuration register to enable
+ * snapdump.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_set_diagsave(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_config;
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_DIAG_SAVE;
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+}
+
+/**
+ * mpi3mr_issue_reset - Issue reset to the controller
+ * @mrioc: Adapter reference
+ * @reset_type: Reset type
+ * @reset_reason: Reset reason code
+ *
+ * Unlock the host diagnostic registers and write the specific
+ * reset type to that, wait for reset acknowledgment from the
+ * controller, if the reset is not successful retry for the
+ * predefined number of times.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type,
+ u16 reset_reason)
+{
+ int retval = -1;
+ u8 unlock_retry_count = 0;
+ u32 host_diagnostic, ioc_status, ioc_config, scratch_pad0;
+ u32 timeout = MPI3MR_RESET_ACK_TIMEOUT * 10;
+
+ if ((reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) &&
+ (reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT))
+ return retval;
+ if (mrioc->unrecoverable)
+ return retval;
+ if (reset_reason == MPI3MR_RESET_FROM_FIRMWARE) {
+ retval = 0;
+ return retval;
+ }
+
+ ioc_info(mrioc, "%s reset due to %s(0x%x)\n",
+ mpi3mr_reset_type_name(reset_type),
+ mpi3mr_reset_rc_name(reset_reason), reset_reason);
+
+ mpi3mr_clear_reset_history(mrioc);
+ do {
+ ioc_info(mrioc,
+ "Write magic sequence to unlock host diag register (retry=%d)\n",
+ ++unlock_retry_count);
+ if (unlock_retry_count >= MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT) {
+ ioc_err(mrioc,
+ "%s reset failed due to unlock failure, host_diagnostic(0x%08x)\n",
+ mpi3mr_reset_type_name(reset_type),
+ host_diagnostic);
+ mrioc->unrecoverable = 1;
+ return retval;
+ }
+
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH,
+ &mrioc->sysif_regs->write_sequence);
+ usleep_range(1000, 1100);
+ host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
+ ioc_info(mrioc,
+ "wrote magic sequence: retry_count(%d), host_diagnostic(0x%08x)\n",
+ unlock_retry_count, host_diagnostic);
+ } while (!(host_diagnostic & MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE));
+
+ scratch_pad0 = ((MPI3MR_RESET_REASON_OSTYPE_LINUX <<
+ MPI3MR_RESET_REASON_OSTYPE_SHIFT) | (mrioc->facts.ioc_num <<
+ MPI3MR_RESET_REASON_IOCNUM_SHIFT) | reset_reason);
+ writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]);
+ writel(host_diagnostic | reset_type,
+ &mrioc->sysif_regs->host_diagnostic);
+ switch (reset_type) {
+ case MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET:
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config =
+ readl(&mrioc->sysif_regs->ioc_configuration);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)
+ && mpi3mr_soft_reset_success(ioc_status, ioc_config)
+ ) {
+ mpi3mr_clear_reset_history(mrioc);
+ retval = 0;
+ break;
+ }
+ msleep(100);
+ } while (--timeout);
+ mpi3mr_print_fault_info(mrioc);
+ break;
+ case MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT:
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if (mpi3mr_diagfault_success(mrioc, ioc_status)) {
+ retval = 0;
+ break;
+ }
+ msleep(100);
+ } while (--timeout);
+ break;
+ default:
+ break;
+ }
+
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
+ &mrioc->sysif_regs->write_sequence);
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_info(mrioc,
+ "ioc_status/ioc_config after %s reset is (0x%08x)/(0x%08x)\n",
+ (!retval)?"successful":"failed", ioc_status,
+ ioc_config);
+ if (retval)
+ mrioc->unrecoverable = 1;
+ return retval;
+}
+
+/**
+ * mpi3mr_admin_request_post - Post request to admin queue
+ * @mrioc: Adapter reference
+ * @admin_req: MPI3 request
+ * @admin_req_sz: Request size
+ * @ignore_reset: Ignore reset in process
+ *
+ * Post the MPI3 request into admin request queue and
+ * inform the controller, if the queue is full return
+ * appropriate error.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req,
+ u16 admin_req_sz, u8 ignore_reset)
+{
+ u16 areq_pi = 0, areq_ci = 0, max_entries = 0;
+ int retval = 0;
+ unsigned long flags;
+ u8 *areq_entry;
+
+ if (mrioc->unrecoverable) {
+ ioc_err(mrioc, "%s : Unrecoverable controller\n", __func__);
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&mrioc->admin_req_lock, flags);
+ areq_pi = mrioc->admin_req_pi;
+ areq_ci = mrioc->admin_req_ci;
+ max_entries = mrioc->num_admin_req;
+ if ((areq_ci == (areq_pi + 1)) || ((!areq_ci) &&
+ (areq_pi == (max_entries - 1)))) {
+ ioc_err(mrioc, "AdminReqQ full condition detected\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ if (!ignore_reset && mrioc->reset_in_progress) {
+ ioc_err(mrioc, "AdminReqQ submit reset in progress\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ if (mrioc->pci_err_recovery) {
+ ioc_err(mrioc, "admin request queue submission failed due to pci error recovery in progress\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ areq_entry = (u8 *)mrioc->admin_req_base +
+ (areq_pi * MPI3MR_ADMIN_REQ_FRAME_SZ);
+ memset(areq_entry, 0, MPI3MR_ADMIN_REQ_FRAME_SZ);
+ memcpy(areq_entry, (u8 *)admin_req, admin_req_sz);
+
+ if (++areq_pi == max_entries)
+ areq_pi = 0;
+ mrioc->admin_req_pi = areq_pi;
+
+ writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi);
+
+out:
+ spin_unlock_irqrestore(&mrioc->admin_req_lock, flags);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_free_op_req_q_segments - free request memory segments
+ * @mrioc: Adapter instance reference
+ * @q_idx: operational request queue index
+ *
+ * Free memory segments allocated for operational request queue
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
+{
+ u16 j;
+ int size;
+ struct segments *segments;
+
+ segments = mrioc->req_qinfo[q_idx].q_segments;
+ if (!segments)
+ return;
+
+ if (mrioc->enable_segqueue) {
+ size = MPI3MR_OP_REQ_Q_SEG_SIZE;
+ if (mrioc->req_qinfo[q_idx].q_segment_list) {
+ dma_free_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE,
+ mrioc->req_qinfo[q_idx].q_segment_list,
+ mrioc->req_qinfo[q_idx].q_segment_list_dma);
+ mrioc->req_qinfo[q_idx].q_segment_list = NULL;
+ }
+ } else
+ size = mrioc->req_qinfo[q_idx].segment_qd *
+ mrioc->facts.op_req_sz;
+
+ for (j = 0; j < mrioc->req_qinfo[q_idx].num_segments; j++) {
+ if (!segments[j].segment)
+ continue;
+ dma_free_coherent(&mrioc->pdev->dev,
+ size, segments[j].segment, segments[j].segment_dma);
+ segments[j].segment = NULL;
+ }
+ kfree(mrioc->req_qinfo[q_idx].q_segments);
+ mrioc->req_qinfo[q_idx].q_segments = NULL;
+ mrioc->req_qinfo[q_idx].qid = 0;
+}
+
+/**
+ * mpi3mr_free_op_reply_q_segments - free reply memory segments
+ * @mrioc: Adapter instance reference
+ * @q_idx: operational reply queue index
+ *
+ * Free memory segments allocated for operational reply queue
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
+{
+ u16 j;
+ int size;
+ struct segments *segments;
+
+ segments = mrioc->op_reply_qinfo[q_idx].q_segments;
+ if (!segments)
+ return;
+
+ if (mrioc->enable_segqueue) {
+ size = MPI3MR_OP_REP_Q_SEG_SIZE;
+ if (mrioc->op_reply_qinfo[q_idx].q_segment_list) {
+ dma_free_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE,
+ mrioc->op_reply_qinfo[q_idx].q_segment_list,
+ mrioc->op_reply_qinfo[q_idx].q_segment_list_dma);
+ mrioc->op_reply_qinfo[q_idx].q_segment_list = NULL;
+ }
+ } else
+ size = mrioc->op_reply_qinfo[q_idx].segment_qd *
+ mrioc->op_reply_desc_sz;
+
+ for (j = 0; j < mrioc->op_reply_qinfo[q_idx].num_segments; j++) {
+ if (!segments[j].segment)
+ continue;
+ dma_free_coherent(&mrioc->pdev->dev,
+ size, segments[j].segment, segments[j].segment_dma);
+ segments[j].segment = NULL;
+ }
+
+ kfree(mrioc->op_reply_qinfo[q_idx].q_segments);
+ mrioc->op_reply_qinfo[q_idx].q_segments = NULL;
+ mrioc->op_reply_qinfo[q_idx].qid = 0;
+}
+
+/**
+ * mpi3mr_delete_op_reply_q - delete operational reply queue
+ * @mrioc: Adapter instance reference
+ * @qidx: operational reply queue index
+ *
+ * Delete operatinal reply queue by issuing MPI request
+ * through admin queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_delete_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct mpi3_delete_reply_queue_request delq_req;
+ struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
+ int retval = 0;
+ u16 reply_qid = 0, midx;
+
+ reply_qid = op_reply_q->qid;
+
+ midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset);
+
+ if (!reply_qid) {
+ retval = -1;
+ ioc_err(mrioc, "Issue DelRepQ: called with invalid ReqQID\n");
+ goto out;
+ }
+
+ (op_reply_q->qtype == MPI3MR_DEFAULT_QUEUE) ? mrioc->default_qcount-- :
+ mrioc->active_poll_qcount--;
+
+ memset(&delq_req, 0, sizeof(delq_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue DelRepQ: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ delq_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ delq_req.function = MPI3_FUNCTION_DELETE_REPLY_QUEUE;
+ delq_req.queue_id = cpu_to_le16(reply_qid);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &delq_req, sizeof(delq_req),
+ 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue DelRepQ: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "delete reply queue timed out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_DELREPQ_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue DelRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ mrioc->intr_info[midx].op_reply_q = NULL;
+
+ mpi3mr_free_op_reply_q_segments(mrioc, qidx);
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+
+ return retval;
+}
+
+/**
+ * mpi3mr_alloc_op_reply_q_segments -Alloc segmented reply pool
+ * @mrioc: Adapter instance reference
+ * @qidx: request queue index
+ *
+ * Allocate segmented memory pools for operational reply
+ * queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_alloc_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
+ int i, size;
+ u64 *q_segment_list_entry = NULL;
+ struct segments *segments;
+
+ if (mrioc->enable_segqueue) {
+ op_reply_q->segment_qd =
+ MPI3MR_OP_REP_Q_SEG_SIZE / mrioc->op_reply_desc_sz;
+
+ size = MPI3MR_OP_REP_Q_SEG_SIZE;
+
+ op_reply_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE, &op_reply_q->q_segment_list_dma,
+ GFP_KERNEL);
+ if (!op_reply_q->q_segment_list)
+ return -ENOMEM;
+ q_segment_list_entry = (u64 *)op_reply_q->q_segment_list;
+ } else {
+ op_reply_q->segment_qd = op_reply_q->num_replies;
+ size = op_reply_q->num_replies * mrioc->op_reply_desc_sz;
+ }
+
+ op_reply_q->num_segments = DIV_ROUND_UP(op_reply_q->num_replies,
+ op_reply_q->segment_qd);
+
+ op_reply_q->q_segments = kcalloc(op_reply_q->num_segments,
+ sizeof(struct segments), GFP_KERNEL);
+ if (!op_reply_q->q_segments)
+ return -ENOMEM;
+
+ segments = op_reply_q->q_segments;
+ for (i = 0; i < op_reply_q->num_segments; i++) {
+ segments[i].segment =
+ dma_alloc_coherent(&mrioc->pdev->dev,
+ size, &segments[i].segment_dma, GFP_KERNEL);
+ if (!segments[i].segment)
+ return -ENOMEM;
+ if (mrioc->enable_segqueue)
+ q_segment_list_entry[i] =
+ (unsigned long)segments[i].segment_dma;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_alloc_op_req_q_segments - Alloc segmented req pool.
+ * @mrioc: Adapter instance reference
+ * @qidx: request queue index
+ *
+ * Allocate segmented memory pools for operational request
+ * queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_alloc_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_req_qinfo *op_req_q = mrioc->req_qinfo + qidx;
+ int i, size;
+ u64 *q_segment_list_entry = NULL;
+ struct segments *segments;
+
+ if (mrioc->enable_segqueue) {
+ op_req_q->segment_qd =
+ MPI3MR_OP_REQ_Q_SEG_SIZE / mrioc->facts.op_req_sz;
+
+ size = MPI3MR_OP_REQ_Q_SEG_SIZE;
+
+ op_req_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE, &op_req_q->q_segment_list_dma,
+ GFP_KERNEL);
+ if (!op_req_q->q_segment_list)
+ return -ENOMEM;
+ q_segment_list_entry = (u64 *)op_req_q->q_segment_list;
+
+ } else {
+ op_req_q->segment_qd = op_req_q->num_requests;
+ size = op_req_q->num_requests * mrioc->facts.op_req_sz;
+ }
+
+ op_req_q->num_segments = DIV_ROUND_UP(op_req_q->num_requests,
+ op_req_q->segment_qd);
+
+ op_req_q->q_segments = kcalloc(op_req_q->num_segments,
+ sizeof(struct segments), GFP_KERNEL);
+ if (!op_req_q->q_segments)
+ return -ENOMEM;
+
+ segments = op_req_q->q_segments;
+ for (i = 0; i < op_req_q->num_segments; i++) {
+ segments[i].segment =
+ dma_alloc_coherent(&mrioc->pdev->dev,
+ size, &segments[i].segment_dma, GFP_KERNEL);
+ if (!segments[i].segment)
+ return -ENOMEM;
+ if (mrioc->enable_segqueue)
+ q_segment_list_entry[i] =
+ (unsigned long)segments[i].segment_dma;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_create_op_reply_q - create operational reply queue
+ * @mrioc: Adapter instance reference
+ * @qidx: operational reply queue index
+ *
+ * Create operatinal reply queue by issuing MPI request
+ * through admin queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_create_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct mpi3_create_reply_queue_request create_req;
+ struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
+ int retval = 0;
+ u16 reply_qid = 0, midx;
+
+ reply_qid = op_reply_q->qid;
+
+ midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset);
+
+ if (reply_qid) {
+ retval = -1;
+ ioc_err(mrioc, "CreateRepQ: called for duplicate qid %d\n",
+ reply_qid);
+
+ return retval;
+ }
+
+ reply_qid = qidx + 1;
+
+ if (mrioc->pdev->device == MPI3_MFGPAGE_DEVID_SAS4116) {
+ if (mrioc->pdev->revision)
+ op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD;
+ else
+ op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD4K;
+ } else
+ op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD2K;
+
+ op_reply_q->ci = 0;
+ op_reply_q->ephase = 1;
+ atomic_set(&op_reply_q->pend_ios, 0);
+ atomic_set(&op_reply_q->in_use, 0);
+ op_reply_q->enable_irq_poll = false;
+ op_reply_q->qfull_watermark =
+ op_reply_q->num_replies - (MPI3MR_THRESHOLD_REPLY_COUNT * 2);
+
+ if (!op_reply_q->q_segments) {
+ retval = mpi3mr_alloc_op_reply_q_segments(mrioc, qidx);
+ if (retval) {
+ mpi3mr_free_op_reply_q_segments(mrioc, qidx);
+ goto out;
+ }
+ }
+
+ memset(&create_req, 0, sizeof(create_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "CreateRepQ: Init command is in use\n");
+ goto out_unlock;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ create_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ create_req.function = MPI3_FUNCTION_CREATE_REPLY_QUEUE;
+ create_req.queue_id = cpu_to_le16(reply_qid);
+
+ if (midx < (mrioc->intr_info_count - mrioc->requested_poll_qcount))
+ op_reply_q->qtype = MPI3MR_DEFAULT_QUEUE;
+ else
+ op_reply_q->qtype = MPI3MR_POLL_QUEUE;
+
+ if (op_reply_q->qtype == MPI3MR_DEFAULT_QUEUE) {
+ create_req.flags =
+ MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_ENABLE;
+ create_req.msix_index =
+ cpu_to_le16(mrioc->intr_info[midx].msix_index);
+ } else {
+ create_req.msix_index = cpu_to_le16(mrioc->intr_info_count - 1);
+ ioc_info(mrioc, "create reply queue(polled): for qid(%d), midx(%d)\n",
+ reply_qid, midx);
+ if (!mrioc->active_poll_qcount)
+ disable_irq_nosync(pci_irq_vector(mrioc->pdev,
+ mrioc->intr_info_count - 1));
+ }
+
+ if (mrioc->enable_segqueue) {
+ create_req.flags |=
+ MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED;
+ create_req.base_address = cpu_to_le64(
+ op_reply_q->q_segment_list_dma);
+ } else
+ create_req.base_address = cpu_to_le64(
+ op_reply_q->q_segments[0].segment_dma);
+
+ create_req.size = cpu_to_le16(op_reply_q->num_replies);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &create_req,
+ sizeof(create_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "CreateRepQ: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "create reply queue timed out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "CreateRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ op_reply_q->qid = reply_qid;
+ if (midx < mrioc->intr_info_count)
+ mrioc->intr_info[midx].op_reply_q = op_reply_q;
+
+ (op_reply_q->qtype == MPI3MR_DEFAULT_QUEUE) ? mrioc->default_qcount++ :
+ mrioc->active_poll_qcount++;
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+
+ return retval;
+}
+
+/**
+ * mpi3mr_create_op_req_q - create operational request queue
+ * @mrioc: Adapter instance reference
+ * @idx: operational request queue index
+ * @reply_qid: Reply queue ID
+ *
+ * Create operatinal request queue by issuing MPI request
+ * through admin queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_create_op_req_q(struct mpi3mr_ioc *mrioc, u16 idx,
+ u16 reply_qid)
+{
+ struct mpi3_create_request_queue_request create_req;
+ struct op_req_qinfo *op_req_q = mrioc->req_qinfo + idx;
+ int retval = 0;
+ u16 req_qid = 0;
+
+ req_qid = op_req_q->qid;
+
+ if (req_qid) {
+ retval = -1;
+ ioc_err(mrioc, "CreateReqQ: called for duplicate qid %d\n",
+ req_qid);
+
+ return retval;
+ }
+ req_qid = idx + 1;
+
+ op_req_q->num_requests = MPI3MR_OP_REQ_Q_QD;
+ op_req_q->ci = 0;
+ op_req_q->pi = 0;
+ op_req_q->reply_qid = reply_qid;
+ spin_lock_init(&op_req_q->q_lock);
+
+ if (!op_req_q->q_segments) {
+ retval = mpi3mr_alloc_op_req_q_segments(mrioc, idx);
+ if (retval) {
+ mpi3mr_free_op_req_q_segments(mrioc, idx);
+ goto out;
+ }
+ }
+
+ memset(&create_req, 0, sizeof(create_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "CreateReqQ: Init command is in use\n");
+ goto out_unlock;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ create_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ create_req.function = MPI3_FUNCTION_CREATE_REQUEST_QUEUE;
+ create_req.queue_id = cpu_to_le16(req_qid);
+ if (mrioc->enable_segqueue) {
+ create_req.flags =
+ MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED;
+ create_req.base_address = cpu_to_le64(
+ op_req_q->q_segment_list_dma);
+ } else
+ create_req.base_address = cpu_to_le64(
+ op_req_q->q_segments[0].segment_dma);
+ create_req.reply_queue_id = cpu_to_le16(reply_qid);
+ create_req.size = cpu_to_le16(op_req_q->num_requests);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &create_req,
+ sizeof(create_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "CreateReqQ: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "create request queue timed out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "CreateReqQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ op_req_q->qid = req_qid;
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+
+ return retval;
+}
+
+/**
+ * mpi3mr_create_op_queues - create operational queue pairs
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate memory for operational queue meta data and call
+ * create request and reply queue functions.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u16 num_queues = 0, i = 0, msix_count_op_q = 1;
+ u32 ioc_status;
+ enum mpi3mr_iocstate ioc_state;
+
+ num_queues = min_t(int, mrioc->facts.max_op_reply_q,
+ mrioc->facts.max_op_req_q);
+
+ msix_count_op_q =
+ mrioc->intr_info_count - mrioc->op_reply_q_offset;
+ if (!mrioc->num_queues)
+ mrioc->num_queues = min_t(int, num_queues, msix_count_op_q);
+ /*
+ * During reset set the num_queues to the number of queues
+ * that was set before the reset.
+ */
+ num_queues = mrioc->num_op_reply_q ?
+ mrioc->num_op_reply_q : mrioc->num_queues;
+ ioc_info(mrioc, "trying to create %d operational queue pairs\n",
+ num_queues);
+
+ if (!mrioc->req_qinfo) {
+ mrioc->req_qinfo = kcalloc(num_queues,
+ sizeof(struct op_req_qinfo), GFP_KERNEL);
+ if (!mrioc->req_qinfo) {
+ retval = -1;
+ goto out_failed;
+ }
+
+ mrioc->op_reply_qinfo = kzalloc(sizeof(struct op_reply_qinfo) *
+ num_queues, GFP_KERNEL);
+ if (!mrioc->op_reply_qinfo) {
+ retval = -1;
+ goto out_failed;
+ }
+ }
+
+ if (mrioc->enable_segqueue)
+ ioc_info(mrioc,
+ "allocating operational queues through segmented queues\n");
+
+ for (i = 0; i < num_queues; i++) {
+ if (mpi3mr_create_op_reply_q(mrioc, i)) {
+ ioc_err(mrioc, "Cannot create OP RepQ %d\n", i);
+ break;
+ }
+ if (mpi3mr_create_op_req_q(mrioc, i,
+ mrioc->op_reply_qinfo[i].qid)) {
+ ioc_err(mrioc, "Cannot create OP ReqQ %d\n", i);
+ mpi3mr_delete_op_reply_q(mrioc, i);
+ break;
+ }
+ }
+
+ if (i == 0) {
+ /* Not even one queue is created successfully*/
+ retval = -1;
+ goto out_failed;
+ }
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) ||
+ ioc_state != MRIOC_STATE_READY) {
+ mpi3mr_print_fault_info(mrioc);
+ retval = -1;
+ goto out_failed;
+ }
+ mrioc->num_op_reply_q = mrioc->num_op_req_q = i;
+ ioc_info(mrioc,
+ "successfully created %d operational queue pairs(default/polled) queue = (%d/%d)\n",
+ mrioc->num_op_reply_q, mrioc->default_qcount,
+ mrioc->active_poll_qcount);
+
+ return retval;
+out_failed:
+ kfree(mrioc->req_qinfo);
+ mrioc->req_qinfo = NULL;
+
+ kfree(mrioc->op_reply_qinfo);
+ mrioc->op_reply_qinfo = NULL;
+
+ return retval;
+}
+
+/**
+ * mpi3mr_op_request_post - Post request to operational queue
+ * @mrioc: Adapter reference
+ * @op_req_q: Operational request queue info
+ * @req: MPI3 request
+ *
+ * Post the MPI3 request into operational request queue and
+ * inform the controller, if the queue is full return
+ * appropriate error.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc,
+ struct op_req_qinfo *op_req_q, u8 *req)
+{
+ u16 pi = 0, max_entries, reply_qidx = 0, midx;
+ int retval = 0;
+ unsigned long flags;
+ u8 *req_entry;
+ void *segment_base_addr;
+ u16 req_sz = mrioc->facts.op_req_sz;
+ struct segments *segments = op_req_q->q_segments;
+ struct op_reply_qinfo *op_reply_q = NULL;
+
+ reply_qidx = op_req_q->reply_qid - 1;
+ op_reply_q = mrioc->op_reply_qinfo + reply_qidx;
+
+ if (mrioc->unrecoverable)
+ return -EFAULT;
+
+ spin_lock_irqsave(&op_req_q->q_lock, flags);
+ pi = op_req_q->pi;
+ max_entries = op_req_q->num_requests;
+
+ if (mpi3mr_check_req_qfull(op_req_q)) {
+ midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(
+ reply_qidx, mrioc->op_reply_q_offset);
+ mpi3mr_process_op_reply_q(mrioc, mrioc->intr_info[midx].op_reply_q);
+
+ if (mpi3mr_check_req_qfull(op_req_q)) {
+ retval = -EAGAIN;
+ goto out;
+ }
+ }
+
+ if (mrioc->reset_in_progress) {
+ ioc_err(mrioc, "OpReqQ submit reset in progress\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ if (mrioc->pci_err_recovery) {
+ ioc_err(mrioc, "operational request queue submission failed due to pci error recovery in progress\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ /* Reply queue is nearing to get full, push back IOs to SML */
+ if ((mrioc->prevent_reply_qfull == true) &&
+ (atomic_read(&op_reply_q->pend_ios) >
+ (op_reply_q->qfull_watermark))) {
+ atomic_inc(&mrioc->reply_qfull_count);
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ segment_base_addr = segments[pi / op_req_q->segment_qd].segment;
+ req_entry = (u8 *)segment_base_addr +
+ ((pi % op_req_q->segment_qd) * req_sz);
+
+ memset(req_entry, 0, req_sz);
+ memcpy(req_entry, req, MPI3MR_ADMIN_REQ_FRAME_SZ);
+
+ if (++pi == max_entries)
+ pi = 0;
+ op_req_q->pi = pi;
+
+#ifndef CONFIG_PREEMPT_RT
+ if (atomic_inc_return(&mrioc->op_reply_qinfo[reply_qidx].pend_ios)
+ > MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT)
+ mrioc->op_reply_qinfo[reply_qidx].enable_irq_poll = true;
+#else
+ atomic_inc_return(&mrioc->op_reply_qinfo[reply_qidx].pend_ios);
+#endif
+
+ writel(op_req_q->pi,
+ &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].producer_index);
+
+out:
+ spin_unlock_irqrestore(&op_req_q->q_lock, flags);
+ return retval;
+}
+
+/**
+ * mpi3mr_check_rh_fault_ioc - check reset history and fault
+ * controller
+ * @mrioc: Adapter instance reference
+ * @reason_code: reason code for the fault.
+ *
+ * This routine will save snapdump and fault the controller with
+ * the given reason code if it is not already in the fault or
+ * not asynchronosuly reset. This will be used to handle
+ * initilaization time faults/resets/timeout as in those cases
+ * immediate soft reset invocation is not required.
+ *
+ * Return: None.
+ */
+void mpi3mr_check_rh_fault_ioc(struct mpi3mr_ioc *mrioc, u32 reason_code)
+{
+ u32 ioc_status, host_diagnostic, timeout;
+ union mpi3mr_trigger_data trigger_data;
+
+ if (mrioc->unrecoverable) {
+ ioc_err(mrioc, "controller is unrecoverable\n");
+ return;
+ }
+
+ if (!pci_device_is_present(mrioc->pdev)) {
+ mrioc->unrecoverable = 1;
+ ioc_err(mrioc, "controller is not present\n");
+ return;
+ }
+ memset(&trigger_data, 0, sizeof(trigger_data));
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) {
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED, NULL, 0);
+ return;
+ } else if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) {
+ trigger_data.fault = (readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK);
+
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FAULT, &trigger_data, 0);
+ mpi3mr_print_fault_info(mrioc);
+ return;
+ }
+
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_issue_reset(mrioc, MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ reason_code);
+ trigger_data.fault = (readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK);
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc, MPI3MR_HDB_TRIGGER_TYPE_FAULT,
+ &trigger_data, 0);
+ timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10;
+ do {
+ host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
+ if (!(host_diagnostic & MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS))
+ break;
+ msleep(100);
+ } while (--timeout);
+}
+
+/**
+ * mpi3mr_sync_timestamp - Issue time stamp sync request
+ * @mrioc: Adapter reference
+ *
+ * Issue IO unit control MPI request to synchornize firmware
+ * timestamp with host time.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_sync_timestamp(struct mpi3mr_ioc *mrioc)
+{
+ ktime_t current_time;
+ struct mpi3_iounit_control_request iou_ctrl;
+ int retval = 0;
+
+ memset(&iou_ctrl, 0, sizeof(iou_ctrl));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue IOUCTL time_stamp: command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ iou_ctrl.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ iou_ctrl.function = MPI3_FUNCTION_IO_UNIT_CONTROL;
+ iou_ctrl.operation = MPI3_CTRL_OP_UPDATE_TIMESTAMP;
+ current_time = ktime_get_real();
+ iou_ctrl.param64[0] = cpu_to_le64(ktime_to_ms(current_time));
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &iou_ctrl,
+ sizeof(iou_ctrl), 0);
+ if (retval) {
+ ioc_err(mrioc, "Issue IOUCTL time_stamp: Admin Post failed\n");
+ goto out_unlock;
+ }
+
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue IOUCTL time_stamp: command timed out\n");
+ mrioc->init_cmds.is_waiting = 0;
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_RESET))
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_TSU_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue IOUCTL time_stamp: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_print_pkg_ver - display controller fw package version
+ * @mrioc: Adapter reference
+ *
+ * Retrieve firmware package version from the component image
+ * header of the controller flash and display it.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+static int mpi3mr_print_pkg_ver(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3_ci_upload_request ci_upload;
+ int retval = -1;
+ void *data = NULL;
+ dma_addr_t data_dma;
+ struct mpi3_ci_manifest_mpi *manifest;
+ u32 data_len = sizeof(struct mpi3_ci_manifest_mpi);
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+
+ data = dma_alloc_coherent(&mrioc->pdev->dev, data_len, &data_dma,
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memset(&ci_upload, 0, sizeof(ci_upload));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ ioc_err(mrioc, "sending get package version failed due to command in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ ci_upload.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ ci_upload.function = MPI3_FUNCTION_CI_UPLOAD;
+ ci_upload.msg_flags = MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_PRIMARY;
+ ci_upload.signature1 = cpu_to_le32(MPI3_IMAGE_HEADER_SIGNATURE1_MANIFEST);
+ ci_upload.image_offset = cpu_to_le32(MPI3_IMAGE_HEADER_SIZE);
+ ci_upload.segment_size = cpu_to_le32(data_len);
+
+ mpi3mr_add_sg_single(&ci_upload.sgl, sgl_flags, data_len,
+ data_dma);
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &ci_upload,
+ sizeof(ci_upload), 1);
+ if (retval) {
+ ioc_err(mrioc, "posting get package version failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "get package version timed out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ == MPI3_IOCSTATUS_SUCCESS) {
+ manifest = (struct mpi3_ci_manifest_mpi *) data;
+ if (manifest->manifest_type == MPI3_CI_MANIFEST_TYPE_MPI) {
+ ioc_info(mrioc,
+ "firmware package version(%d.%d.%d.%d.%05d-%05d)\n",
+ manifest->package_version.gen_major,
+ manifest->package_version.gen_minor,
+ manifest->package_version.phase_major,
+ manifest->package_version.phase_minor,
+ manifest->package_version.customer_id,
+ manifest->package_version.build_num);
+ }
+ }
+ retval = 0;
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+
+out:
+ if (data)
+ dma_free_coherent(&mrioc->pdev->dev, data_len, data,
+ data_dma);
+ return retval;
+}
+
+/**
+ * mpi3mr_watchdog_work - watchdog thread to monitor faults
+ * @work: work struct
+ *
+ * Watch dog work periodically executed (1 second interval) to
+ * monitor firmware fault and to issue periodic timer sync to
+ * the firmware.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_watchdog_work(struct work_struct *work)
+{
+ struct mpi3mr_ioc *mrioc =
+ container_of(work, struct mpi3mr_ioc, watchdog_work.work);
+ unsigned long flags;
+ enum mpi3mr_iocstate ioc_state;
+ u32 host_diagnostic, ioc_status;
+ union mpi3mr_trigger_data trigger_data;
+ u16 reset_reason = MPI3MR_RESET_FROM_FAULT_WATCH;
+
+ if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
+ return;
+
+ if (!mrioc->unrecoverable && !pci_device_is_present(mrioc->pdev)) {
+ ioc_err(mrioc, "watchdog could not detect the controller\n");
+ mrioc->unrecoverable = 1;
+ }
+
+ if (mrioc->unrecoverable) {
+ ioc_err(mrioc,
+ "flush pending commands for unrecoverable controller\n");
+ mpi3mr_flush_cmds_for_unrecovered_controller(mrioc);
+ return;
+ }
+
+ if (atomic_read(&mrioc->admin_pend_isr)) {
+ ioc_err(mrioc, "Unprocessed admin ISR instance found\n"
+ "flush admin replies\n");
+ mpi3mr_process_admin_reply_q(mrioc);
+ }
+
+ if (!(mrioc->facts.ioc_capabilities &
+ MPI3_IOCFACTS_CAPABILITY_NON_SUPERVISOR_IOC) &&
+ (mrioc->ts_update_counter++ >= mrioc->ts_update_interval)) {
+
+ mrioc->ts_update_counter = 0;
+ mpi3mr_sync_timestamp(mrioc);
+ }
+
+ if ((mrioc->prepare_for_reset) &&
+ ((mrioc->prepare_for_reset_timeout_counter++) >=
+ MPI3MR_PREPARE_FOR_RESET_TIMEOUT)) {
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_CIACTVRST_TIMER, 1);
+ return;
+ }
+
+ memset(&trigger_data, 0, sizeof(trigger_data));
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) {
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED, NULL, 0);
+ mpi3mr_soft_reset_handler(mrioc, MPI3MR_RESET_FROM_FIRMWARE, 0);
+ return;
+ }
+
+ /*Check for fault state every one second and issue Soft reset*/
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state != MRIOC_STATE_FAULT)
+ goto schedule_work;
+
+ trigger_data.fault = readl(&mrioc->sysif_regs->fault) & MPI3_SYSIF_FAULT_CODE_MASK;
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FAULT, &trigger_data, 0);
+ host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
+ if (host_diagnostic & MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS) {
+ if (!mrioc->diagsave_timeout) {
+ mpi3mr_print_fault_info(mrioc);
+ ioc_warn(mrioc, "diag save in progress\n");
+ }
+ if ((mrioc->diagsave_timeout++) <= MPI3_SYSIF_DIAG_SAVE_TIMEOUT)
+ goto schedule_work;
+ }
+
+ mpi3mr_print_fault_info(mrioc);
+ mrioc->diagsave_timeout = 0;
+
+ if (!mpi3mr_is_fault_recoverable(mrioc)) {
+ mrioc->unrecoverable = 1;
+ goto schedule_work;
+ }
+
+ switch (trigger_data.fault) {
+ case MPI3_SYSIF_FAULT_CODE_COMPLETE_RESET_NEEDED:
+ case MPI3_SYSIF_FAULT_CODE_POWER_CYCLE_REQUIRED:
+ ioc_warn(mrioc,
+ "controller requires system power cycle, marking controller as unrecoverable\n");
+ mrioc->unrecoverable = 1;
+ goto schedule_work;
+ case MPI3_SYSIF_FAULT_CODE_SOFT_RESET_IN_PROGRESS:
+ goto schedule_work;
+ case MPI3_SYSIF_FAULT_CODE_CI_ACTIVATION_RESET:
+ reset_reason = MPI3MR_RESET_FROM_CIACTIV_FAULT;
+ break;
+ default:
+ break;
+ }
+ mpi3mr_soft_reset_handler(mrioc, reset_reason, 0);
+ return;
+
+schedule_work:
+ spin_lock_irqsave(&mrioc->watchdog_lock, flags);
+ if (mrioc->watchdog_work_q)
+ queue_delayed_work(mrioc->watchdog_work_q,
+ &mrioc->watchdog_work,
+ msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL));
+ spin_unlock_irqrestore(&mrioc->watchdog_lock, flags);
+ return;
+}
+
+/**
+ * mpi3mr_start_watchdog - Start watchdog
+ * @mrioc: Adapter instance reference
+ *
+ * Create and start the watchdog thread to monitor controller
+ * faults.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc)
+{
+ if (mrioc->watchdog_work_q)
+ return;
+
+ INIT_DELAYED_WORK(&mrioc->watchdog_work, mpi3mr_watchdog_work);
+ snprintf(mrioc->watchdog_work_q_name,
+ sizeof(mrioc->watchdog_work_q_name), "watchdog_%s%d", mrioc->name,
+ mrioc->id);
+ mrioc->watchdog_work_q = alloc_ordered_workqueue(
+ "%s", WQ_MEM_RECLAIM, mrioc->watchdog_work_q_name);
+ if (!mrioc->watchdog_work_q) {
+ ioc_err(mrioc, "%s: failed (line=%d)\n", __func__, __LINE__);
+ return;
+ }
+
+ if (mrioc->watchdog_work_q)
+ queue_delayed_work(mrioc->watchdog_work_q,
+ &mrioc->watchdog_work,
+ msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL));
+}
+
+/**
+ * mpi3mr_stop_watchdog - Stop watchdog
+ * @mrioc: Adapter instance reference
+ *
+ * Stop the watchdog thread created to monitor controller
+ * faults.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc)
+{
+ unsigned long flags;
+ struct workqueue_struct *wq;
+
+ spin_lock_irqsave(&mrioc->watchdog_lock, flags);
+ wq = mrioc->watchdog_work_q;
+ mrioc->watchdog_work_q = NULL;
+ spin_unlock_irqrestore(&mrioc->watchdog_lock, flags);
+ if (wq) {
+ if (!cancel_delayed_work_sync(&mrioc->watchdog_work))
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ }
+}
+
+/**
+ * mpi3mr_setup_admin_qpair - Setup admin queue pair
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate memory for admin queue pair if required and register
+ * the admin queue with the controller.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_setup_admin_qpair(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u32 num_admin_entries = 0;
+
+ mrioc->admin_req_q_sz = MPI3MR_ADMIN_REQ_Q_SIZE;
+ mrioc->num_admin_req = mrioc->admin_req_q_sz /
+ MPI3MR_ADMIN_REQ_FRAME_SZ;
+ mrioc->admin_req_ci = mrioc->admin_req_pi = 0;
+
+ mrioc->admin_reply_q_sz = MPI3MR_ADMIN_REPLY_Q_SIZE;
+ mrioc->num_admin_replies = mrioc->admin_reply_q_sz /
+ MPI3MR_ADMIN_REPLY_FRAME_SZ;
+ mrioc->admin_reply_ci = 0;
+ mrioc->admin_reply_ephase = 1;
+ atomic_set(&mrioc->admin_reply_q_in_use, 0);
+ atomic_set(&mrioc->admin_pend_isr, 0);
+
+ if (!mrioc->admin_req_base) {
+ mrioc->admin_req_base = dma_alloc_coherent(&mrioc->pdev->dev,
+ mrioc->admin_req_q_sz, &mrioc->admin_req_dma, GFP_KERNEL);
+
+ if (!mrioc->admin_req_base) {
+ retval = -1;
+ goto out_failed;
+ }
+
+ mrioc->admin_reply_base = dma_alloc_coherent(&mrioc->pdev->dev,
+ mrioc->admin_reply_q_sz, &mrioc->admin_reply_dma,
+ GFP_KERNEL);
+
+ if (!mrioc->admin_reply_base) {
+ retval = -1;
+ goto out_failed;
+ }
+ }
+
+ num_admin_entries = (mrioc->num_admin_replies << 16) |
+ (mrioc->num_admin_req);
+ writel(num_admin_entries, &mrioc->sysif_regs->admin_queue_num_entries);
+ mpi3mr_writeq(mrioc->admin_req_dma,
+ &mrioc->sysif_regs->admin_request_queue_address,
+ &mrioc->adm_req_q_bar_writeq_lock);
+ mpi3mr_writeq(mrioc->admin_reply_dma,
+ &mrioc->sysif_regs->admin_reply_queue_address,
+ &mrioc->adm_reply_q_bar_writeq_lock);
+ writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi);
+ writel(mrioc->admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci);
+ return retval;
+
+out_failed:
+
+ if (mrioc->admin_reply_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_reply_q_sz,
+ mrioc->admin_reply_base, mrioc->admin_reply_dma);
+ mrioc->admin_reply_base = NULL;
+ }
+ if (mrioc->admin_req_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_req_q_sz,
+ mrioc->admin_req_base, mrioc->admin_req_dma);
+ mrioc->admin_req_base = NULL;
+ }
+ return retval;
+}
+
+/**
+ * mpi3mr_issue_iocfacts - Send IOC Facts
+ * @mrioc: Adapter instance reference
+ * @facts_data: Cached IOC facts data
+ *
+ * Issue IOC Facts MPI request through admin queue and wait for
+ * the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_issue_iocfacts(struct mpi3mr_ioc *mrioc,
+ struct mpi3_ioc_facts_data *facts_data)
+{
+ struct mpi3_ioc_facts_request iocfacts_req;
+ void *data = NULL;
+ dma_addr_t data_dma;
+ u32 data_len = sizeof(*facts_data);
+ int retval = 0;
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+
+ data = dma_alloc_coherent(&mrioc->pdev->dev, data_len, &data_dma,
+ GFP_KERNEL);
+
+ if (!data) {
+ retval = -1;
+ goto out;
+ }
+
+ memset(&iocfacts_req, 0, sizeof(iocfacts_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue IOCFacts: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ iocfacts_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ iocfacts_req.function = MPI3_FUNCTION_IOC_FACTS;
+
+ mpi3mr_add_sg_single(&iocfacts_req.sgl, sgl_flags, data_len,
+ data_dma);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &iocfacts_req,
+ sizeof(iocfacts_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue IOCFacts: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "ioc_facts timed out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue IOCFacts: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ memcpy(facts_data, (u8 *)data, data_len);
+ mpi3mr_process_factsdata(mrioc, facts_data);
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+
+out:
+ if (data)
+ dma_free_coherent(&mrioc->pdev->dev, data_len, data, data_dma);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_check_reset_dma_mask - Process IOC facts data
+ * @mrioc: Adapter instance reference
+ *
+ * Check whether the new DMA mask requested through IOCFacts by
+ * firmware needs to be set, if so set it .
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int mpi3mr_check_reset_dma_mask(struct mpi3mr_ioc *mrioc)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+ int r;
+ u64 facts_dma_mask = DMA_BIT_MASK(mrioc->facts.dma_mask);
+
+ if (!mrioc->facts.dma_mask || (mrioc->dma_mask <= facts_dma_mask))
+ return 0;
+
+ ioc_info(mrioc, "Changing DMA mask from 0x%016llx to 0x%016llx\n",
+ mrioc->dma_mask, facts_dma_mask);
+
+ r = dma_set_mask_and_coherent(&pdev->dev, facts_dma_mask);
+ if (r) {
+ ioc_err(mrioc, "Setting DMA mask to 0x%016llx failed: %d\n",
+ facts_dma_mask, r);
+ return r;
+ }
+ mrioc->dma_mask = facts_dma_mask;
+ return r;
+}
+
+/**
+ * mpi3mr_process_factsdata - Process IOC facts data
+ * @mrioc: Adapter instance reference
+ * @facts_data: Cached IOC facts data
+ *
+ * Convert IOC facts data into cpu endianness and cache it in
+ * the driver .
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc,
+ struct mpi3_ioc_facts_data *facts_data)
+{
+ u32 ioc_config, req_sz, facts_flags;
+
+ if ((le16_to_cpu(facts_data->ioc_facts_data_length)) !=
+ (sizeof(*facts_data) / 4)) {
+ ioc_warn(mrioc,
+ "IOCFactsdata length mismatch driver_sz(%zu) firmware_sz(%d)\n",
+ sizeof(*facts_data),
+ le16_to_cpu(facts_data->ioc_facts_data_length) * 4);
+ }
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ req_sz = 1 << ((ioc_config & MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ) >>
+ MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ_SHIFT);
+ if (le16_to_cpu(facts_data->ioc_request_frame_size) != (req_sz / 4)) {
+ ioc_err(mrioc,
+ "IOCFacts data reqFrameSize mismatch hw_size(%d) firmware_sz(%d)\n",
+ req_sz / 4, le16_to_cpu(facts_data->ioc_request_frame_size));
+ }
+
+ memset(&mrioc->facts, 0, sizeof(mrioc->facts));
+
+ facts_flags = le32_to_cpu(facts_data->flags);
+ mrioc->facts.op_req_sz = req_sz;
+ mrioc->op_reply_desc_sz = 1 << ((ioc_config &
+ MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ) >>
+ MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ_SHIFT);
+
+ mrioc->facts.ioc_num = facts_data->ioc_number;
+ mrioc->facts.who_init = facts_data->who_init;
+ mrioc->facts.max_msix_vectors = le16_to_cpu(facts_data->max_msix_vectors);
+ mrioc->facts.personality = (facts_flags &
+ MPI3_IOCFACTS_FLAGS_PERSONALITY_MASK);
+ mrioc->facts.dma_mask = (facts_flags &
+ MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_MASK) >>
+ MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_SHIFT;
+ mrioc->facts.dma_mask = (facts_flags &
+ MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_MASK) >>
+ MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_SHIFT;
+ mrioc->facts.protocol_flags = facts_data->protocol_flags;
+ mrioc->facts.mpi_version = le32_to_cpu(facts_data->mpi_version.word);
+ mrioc->facts.max_reqs = le16_to_cpu(facts_data->max_outstanding_requests);
+ mrioc->facts.product_id = le16_to_cpu(facts_data->product_id);
+ mrioc->facts.reply_sz = le16_to_cpu(facts_data->reply_frame_size) * 4;
+ mrioc->facts.exceptions = le16_to_cpu(facts_data->ioc_exceptions);
+ mrioc->facts.max_perids = le16_to_cpu(facts_data->max_persistent_id);
+ mrioc->facts.max_vds = le16_to_cpu(facts_data->max_vds);
+ mrioc->facts.max_hpds = le16_to_cpu(facts_data->max_host_pds);
+ mrioc->facts.max_advhpds = le16_to_cpu(facts_data->max_adv_host_pds);
+ mrioc->facts.max_raid_pds = le16_to_cpu(facts_data->max_raid_pds);
+ mrioc->facts.max_nvme = le16_to_cpu(facts_data->max_nvme);
+ mrioc->facts.max_pcie_switches =
+ le16_to_cpu(facts_data->max_pcie_switches);
+ mrioc->facts.max_sasexpanders =
+ le16_to_cpu(facts_data->max_sas_expanders);
+ mrioc->facts.max_data_length = le16_to_cpu(facts_data->max_data_length);
+ mrioc->facts.max_sasinitiators =
+ le16_to_cpu(facts_data->max_sas_initiators);
+ mrioc->facts.max_enclosures = le16_to_cpu(facts_data->max_enclosures);
+ mrioc->facts.min_devhandle = le16_to_cpu(facts_data->min_dev_handle);
+ mrioc->facts.max_devhandle = le16_to_cpu(facts_data->max_dev_handle);
+ mrioc->facts.max_op_req_q =
+ le16_to_cpu(facts_data->max_operational_request_queues);
+ mrioc->facts.max_op_reply_q =
+ le16_to_cpu(facts_data->max_operational_reply_queues);
+ mrioc->facts.ioc_capabilities =
+ le32_to_cpu(facts_data->ioc_capabilities);
+ mrioc->facts.fw_ver.build_num =
+ le16_to_cpu(facts_data->fw_version.build_num);
+ mrioc->facts.fw_ver.cust_id =
+ le16_to_cpu(facts_data->fw_version.customer_id);
+ mrioc->facts.fw_ver.ph_minor = facts_data->fw_version.phase_minor;
+ mrioc->facts.fw_ver.ph_major = facts_data->fw_version.phase_major;
+ mrioc->facts.fw_ver.gen_minor = facts_data->fw_version.gen_minor;
+ mrioc->facts.fw_ver.gen_major = facts_data->fw_version.gen_major;
+ mrioc->msix_count = min_t(int, mrioc->msix_count,
+ mrioc->facts.max_msix_vectors);
+ mrioc->facts.sge_mod_mask = facts_data->sge_modifier_mask;
+ mrioc->facts.sge_mod_value = facts_data->sge_modifier_value;
+ mrioc->facts.sge_mod_shift = facts_data->sge_modifier_shift;
+ mrioc->facts.shutdown_timeout =
+ le16_to_cpu(facts_data->shutdown_timeout);
+ mrioc->facts.diag_trace_sz =
+ le32_to_cpu(facts_data->diag_trace_size);
+ mrioc->facts.diag_fw_sz =
+ le32_to_cpu(facts_data->diag_fw_size);
+ mrioc->facts.diag_drvr_sz = le32_to_cpu(facts_data->diag_driver_size);
+ mrioc->facts.max_dev_per_tg =
+ facts_data->max_devices_per_throttle_group;
+ mrioc->facts.io_throttle_data_length =
+ le16_to_cpu(facts_data->io_throttle_data_length);
+ mrioc->facts.max_io_throttle_group =
+ le16_to_cpu(facts_data->max_io_throttle_group);
+ mrioc->facts.io_throttle_low = le16_to_cpu(facts_data->io_throttle_low);
+ mrioc->facts.io_throttle_high =
+ le16_to_cpu(facts_data->io_throttle_high);
+
+ if (mrioc->facts.max_data_length ==
+ MPI3_IOCFACTS_MAX_DATA_LENGTH_NOT_REPORTED)
+ mrioc->facts.max_data_length = MPI3MR_DEFAULT_MAX_IO_SIZE;
+ else
+ mrioc->facts.max_data_length *= MPI3MR_PAGE_SIZE_4K;
+ /* Store in 512b block count */
+ if (mrioc->facts.io_throttle_data_length)
+ mrioc->io_throttle_data_length =
+ (mrioc->facts.io_throttle_data_length * 2 * 4);
+ else
+ /* set the length to 1MB + 1K to disable throttle */
+ mrioc->io_throttle_data_length = (mrioc->facts.max_data_length / 512) + 2;
+
+ mrioc->io_throttle_high = (mrioc->facts.io_throttle_high * 2 * 1024);
+ mrioc->io_throttle_low = (mrioc->facts.io_throttle_low * 2 * 1024);
+
+ ioc_info(mrioc, "ioc_num(%d), maxopQ(%d), maxopRepQ(%d), maxdh(%d),",
+ mrioc->facts.ioc_num, mrioc->facts.max_op_req_q,
+ mrioc->facts.max_op_reply_q, mrioc->facts.max_devhandle);
+ ioc_info(mrioc,
+ "maxreqs(%d), mindh(%d) maxvectors(%d) maxperids(%d)\n",
+ mrioc->facts.max_reqs, mrioc->facts.min_devhandle,
+ mrioc->facts.max_msix_vectors, mrioc->facts.max_perids);
+ ioc_info(mrioc, "SGEModMask 0x%x SGEModVal 0x%x SGEModShift 0x%x ",
+ mrioc->facts.sge_mod_mask, mrioc->facts.sge_mod_value,
+ mrioc->facts.sge_mod_shift);
+ ioc_info(mrioc, "DMA mask %d InitialPE status 0x%x max_data_len (%d)\n",
+ mrioc->facts.dma_mask, (facts_flags &
+ MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK), mrioc->facts.max_data_length);
+ ioc_info(mrioc,
+ "max_dev_per_throttle_group(%d), max_throttle_groups(%d)\n",
+ mrioc->facts.max_dev_per_tg, mrioc->facts.max_io_throttle_group);
+ ioc_info(mrioc,
+ "io_throttle_data_len(%dKiB), io_throttle_high(%dMiB), io_throttle_low(%dMiB)\n",
+ mrioc->facts.io_throttle_data_length * 4,
+ mrioc->facts.io_throttle_high, mrioc->facts.io_throttle_low);
+}
+
+/**
+ * mpi3mr_alloc_reply_sense_bufs - Send IOC Init
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate and initialize the reply free buffers, sense
+ * buffers, reply free queue and sense buffer queue.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_alloc_reply_sense_bufs(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u32 sz, i;
+
+ if (mrioc->init_cmds.reply)
+ return retval;
+
+ mrioc->init_cmds.reply = kzalloc(mrioc->reply_sz, GFP_KERNEL);
+ if (!mrioc->init_cmds.reply)
+ goto out_failed;
+
+ mrioc->bsg_cmds.reply = kzalloc(mrioc->reply_sz, GFP_KERNEL);
+ if (!mrioc->bsg_cmds.reply)
+ goto out_failed;
+
+ mrioc->transport_cmds.reply = kzalloc(mrioc->reply_sz, GFP_KERNEL);
+ if (!mrioc->transport_cmds.reply)
+ goto out_failed;
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
+ mrioc->dev_rmhs_cmds[i].reply = kzalloc(mrioc->reply_sz,
+ GFP_KERNEL);
+ if (!mrioc->dev_rmhs_cmds[i].reply)
+ goto out_failed;
+ }
+
+ for (i = 0; i < MPI3MR_NUM_EVTACKCMD; i++) {
+ mrioc->evtack_cmds[i].reply = kzalloc(mrioc->reply_sz,
+ GFP_KERNEL);
+ if (!mrioc->evtack_cmds[i].reply)
+ goto out_failed;
+ }
+
+ mrioc->host_tm_cmds.reply = kzalloc(mrioc->reply_sz, GFP_KERNEL);
+ if (!mrioc->host_tm_cmds.reply)
+ goto out_failed;
+
+ mrioc->pel_cmds.reply = kzalloc(mrioc->reply_sz, GFP_KERNEL);
+ if (!mrioc->pel_cmds.reply)
+ goto out_failed;
+
+ mrioc->pel_abort_cmd.reply = kzalloc(mrioc->reply_sz, GFP_KERNEL);
+ if (!mrioc->pel_abort_cmd.reply)
+ goto out_failed;
+
+ mrioc->dev_handle_bitmap_bits = mrioc->facts.max_devhandle;
+ mrioc->removepend_bitmap = bitmap_zalloc(mrioc->dev_handle_bitmap_bits,
+ GFP_KERNEL);
+ if (!mrioc->removepend_bitmap)
+ goto out_failed;
+
+ mrioc->devrem_bitmap = bitmap_zalloc(MPI3MR_NUM_DEVRMCMD, GFP_KERNEL);
+ if (!mrioc->devrem_bitmap)
+ goto out_failed;
+
+ mrioc->evtack_cmds_bitmap = bitmap_zalloc(MPI3MR_NUM_EVTACKCMD,
+ GFP_KERNEL);
+ if (!mrioc->evtack_cmds_bitmap)
+ goto out_failed;
+
+ mrioc->num_reply_bufs = mrioc->facts.max_reqs + MPI3MR_NUM_EVT_REPLIES;
+ mrioc->reply_free_qsz = mrioc->num_reply_bufs + 1;
+ mrioc->num_sense_bufs = mrioc->facts.max_reqs / MPI3MR_SENSEBUF_FACTOR;
+ mrioc->sense_buf_q_sz = mrioc->num_sense_bufs + 1;
+
+ /* reply buffer pool, 16 byte align */
+ sz = mrioc->num_reply_bufs * mrioc->reply_sz;
+ mrioc->reply_buf_pool = dma_pool_create("reply_buf pool",
+ &mrioc->pdev->dev, sz, 16, 0);
+ if (!mrioc->reply_buf_pool) {
+ ioc_err(mrioc, "reply buf pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+
+ mrioc->reply_buf = dma_pool_zalloc(mrioc->reply_buf_pool, GFP_KERNEL,
+ &mrioc->reply_buf_dma);
+ if (!mrioc->reply_buf)
+ goto out_failed;
+
+ mrioc->reply_buf_dma_max_address = mrioc->reply_buf_dma + sz;
+
+ /* reply free queue, 8 byte align */
+ sz = mrioc->reply_free_qsz * 8;
+ mrioc->reply_free_q_pool = dma_pool_create("reply_free_q pool",
+ &mrioc->pdev->dev, sz, 8, 0);
+ if (!mrioc->reply_free_q_pool) {
+ ioc_err(mrioc, "reply_free_q pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+ mrioc->reply_free_q = dma_pool_zalloc(mrioc->reply_free_q_pool,
+ GFP_KERNEL, &mrioc->reply_free_q_dma);
+ if (!mrioc->reply_free_q)
+ goto out_failed;
+
+ /* sense buffer pool, 4 byte align */
+ sz = mrioc->num_sense_bufs * MPI3MR_SENSE_BUF_SZ;
+ mrioc->sense_buf_pool = dma_pool_create("sense_buf pool",
+ &mrioc->pdev->dev, sz, 4, 0);
+ if (!mrioc->sense_buf_pool) {
+ ioc_err(mrioc, "sense_buf pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+ mrioc->sense_buf = dma_pool_zalloc(mrioc->sense_buf_pool, GFP_KERNEL,
+ &mrioc->sense_buf_dma);
+ if (!mrioc->sense_buf)
+ goto out_failed;
+
+ /* sense buffer queue, 8 byte align */
+ sz = mrioc->sense_buf_q_sz * 8;
+ mrioc->sense_buf_q_pool = dma_pool_create("sense_buf_q pool",
+ &mrioc->pdev->dev, sz, 8, 0);
+ if (!mrioc->sense_buf_q_pool) {
+ ioc_err(mrioc, "sense_buf_q pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+ mrioc->sense_buf_q = dma_pool_zalloc(mrioc->sense_buf_q_pool,
+ GFP_KERNEL, &mrioc->sense_buf_q_dma);
+ if (!mrioc->sense_buf_q)
+ goto out_failed;
+
+ return retval;
+
+out_failed:
+ retval = -1;
+ return retval;
+}
+
+/**
+ * mpimr_initialize_reply_sbuf_queues - initialize reply sense
+ * buffers
+ * @mrioc: Adapter instance reference
+ *
+ * Helper function to initialize reply and sense buffers along
+ * with some debug prints.
+ *
+ * Return: None.
+ */
+static void mpimr_initialize_reply_sbuf_queues(struct mpi3mr_ioc *mrioc)
+{
+ u32 sz, i;
+ dma_addr_t phy_addr;
+
+ sz = mrioc->num_reply_bufs * mrioc->reply_sz;
+ ioc_info(mrioc,
+ "reply buf pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), reply_dma(0x%llx)\n",
+ mrioc->reply_buf, mrioc->num_reply_bufs, mrioc->reply_sz,
+ (sz / 1024), (unsigned long long)mrioc->reply_buf_dma);
+ sz = mrioc->reply_free_qsz * 8;
+ ioc_info(mrioc,
+ "reply_free_q pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), reply_dma(0x%llx)\n",
+ mrioc->reply_free_q, mrioc->reply_free_qsz, 8, (sz / 1024),
+ (unsigned long long)mrioc->reply_free_q_dma);
+ sz = mrioc->num_sense_bufs * MPI3MR_SENSE_BUF_SZ;
+ ioc_info(mrioc,
+ "sense_buf pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), sense_dma(0x%llx)\n",
+ mrioc->sense_buf, mrioc->num_sense_bufs, MPI3MR_SENSE_BUF_SZ,
+ (sz / 1024), (unsigned long long)mrioc->sense_buf_dma);
+ sz = mrioc->sense_buf_q_sz * 8;
+ ioc_info(mrioc,
+ "sense_buf_q pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), sense_dma(0x%llx)\n",
+ mrioc->sense_buf_q, mrioc->sense_buf_q_sz, 8, (sz / 1024),
+ (unsigned long long)mrioc->sense_buf_q_dma);
+
+ /* initialize Reply buffer Queue */
+ for (i = 0, phy_addr = mrioc->reply_buf_dma;
+ i < mrioc->num_reply_bufs; i++, phy_addr += mrioc->reply_sz)
+ mrioc->reply_free_q[i] = cpu_to_le64(phy_addr);
+ mrioc->reply_free_q[i] = cpu_to_le64(0);
+
+ /* initialize Sense Buffer Queue */
+ for (i = 0, phy_addr = mrioc->sense_buf_dma;
+ i < mrioc->num_sense_bufs; i++, phy_addr += MPI3MR_SENSE_BUF_SZ)
+ mrioc->sense_buf_q[i] = cpu_to_le64(phy_addr);
+ mrioc->sense_buf_q[i] = cpu_to_le64(0);
+}
+
+/**
+ * mpi3mr_issue_iocinit - Send IOC Init
+ * @mrioc: Adapter instance reference
+ *
+ * Issue IOC Init MPI request through admin queue and wait for
+ * the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_issue_iocinit(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3_ioc_init_request iocinit_req;
+ struct mpi3_driver_info_layout *drv_info;
+ dma_addr_t data_dma;
+ u32 data_len = sizeof(*drv_info);
+ int retval = 0;
+ ktime_t current_time;
+
+ drv_info = dma_alloc_coherent(&mrioc->pdev->dev, data_len, &data_dma,
+ GFP_KERNEL);
+ if (!drv_info) {
+ retval = -1;
+ goto out;
+ }
+ mpimr_initialize_reply_sbuf_queues(mrioc);
+
+ drv_info->information_length = cpu_to_le32(data_len);
+ strscpy(drv_info->driver_signature, "Broadcom", sizeof(drv_info->driver_signature));
+ strscpy(drv_info->os_name, utsname()->sysname, sizeof(drv_info->os_name));
+ strscpy(drv_info->os_version, utsname()->release, sizeof(drv_info->os_version));
+ strscpy(drv_info->driver_name, MPI3MR_DRIVER_NAME, sizeof(drv_info->driver_name));
+ strscpy(drv_info->driver_version, MPI3MR_DRIVER_VERSION, sizeof(drv_info->driver_version));
+ strscpy(drv_info->driver_release_date, MPI3MR_DRIVER_RELDATE,
+ sizeof(drv_info->driver_release_date));
+ drv_info->driver_capabilities = 0;
+ memcpy((u8 *)&mrioc->driver_info, (u8 *)drv_info,
+ sizeof(mrioc->driver_info));
+
+ memset(&iocinit_req, 0, sizeof(iocinit_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue IOCInit: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ iocinit_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ iocinit_req.function = MPI3_FUNCTION_IOC_INIT;
+ iocinit_req.mpi_version.mpi3_version.dev = MPI3_VERSION_DEV;
+ iocinit_req.mpi_version.mpi3_version.unit = MPI3_VERSION_UNIT;
+ iocinit_req.mpi_version.mpi3_version.major = MPI3_VERSION_MAJOR;
+ iocinit_req.mpi_version.mpi3_version.minor = MPI3_VERSION_MINOR;
+ iocinit_req.who_init = MPI3_WHOINIT_HOST_DRIVER;
+ iocinit_req.reply_free_queue_depth = cpu_to_le16(mrioc->reply_free_qsz);
+ iocinit_req.reply_free_queue_address =
+ cpu_to_le64(mrioc->reply_free_q_dma);
+ iocinit_req.sense_buffer_length = cpu_to_le16(MPI3MR_SENSE_BUF_SZ);
+ iocinit_req.sense_buffer_free_queue_depth =
+ cpu_to_le16(mrioc->sense_buf_q_sz);
+ iocinit_req.sense_buffer_free_queue_address =
+ cpu_to_le64(mrioc->sense_buf_q_dma);
+ iocinit_req.driver_information_address = cpu_to_le64(data_dma);
+
+ current_time = ktime_get_real();
+ iocinit_req.time_stamp = cpu_to_le64(ktime_to_ms(current_time));
+
+ iocinit_req.msg_flags |=
+ MPI3_IOCINIT_MSGFLAGS_SCSIIOSTATUSREPLY_SUPPORTED;
+ iocinit_req.msg_flags |=
+ MPI3_IOCINIT_MSGFLAGS_WRITESAMEDIVERT_SUPPORTED;
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &iocinit_req,
+ sizeof(iocinit_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue IOCInit: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_IOCINIT_TIMEOUT);
+ ioc_err(mrioc, "ioc_init timed out\n");
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue IOCInit: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+ mrioc->reply_free_queue_host_index = mrioc->num_reply_bufs;
+ writel(mrioc->reply_free_queue_host_index,
+ &mrioc->sysif_regs->reply_free_host_index);
+
+ mrioc->sbq_host_index = mrioc->num_sense_bufs;
+ writel(mrioc->sbq_host_index,
+ &mrioc->sysif_regs->sense_buffer_free_host_index);
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+
+out:
+ if (drv_info)
+ dma_free_coherent(&mrioc->pdev->dev, data_len, drv_info,
+ data_dma);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_unmask_events - Unmask events in event mask bitmap
+ * @mrioc: Adapter instance reference
+ * @event: MPI event ID
+ *
+ * Un mask the specific event by resetting the event_mask
+ * bitmap.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static void mpi3mr_unmask_events(struct mpi3mr_ioc *mrioc, u16 event)
+{
+ u32 desired_event;
+ u8 word;
+
+ if (event >= 128)
+ return;
+
+ desired_event = (1 << (event % 32));
+ word = event / 32;
+
+ mrioc->event_masks[word] &= ~desired_event;
+}
+
+/**
+ * mpi3mr_issue_event_notification - Send event notification
+ * @mrioc: Adapter instance reference
+ *
+ * Issue event notification MPI request through admin queue and
+ * wait for the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_issue_event_notification(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3_event_notification_request evtnotify_req;
+ int retval = 0;
+ u8 i;
+
+ memset(&evtnotify_req, 0, sizeof(evtnotify_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue EvtNotify: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ evtnotify_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ evtnotify_req.function = MPI3_FUNCTION_EVENT_NOTIFICATION;
+ for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ evtnotify_req.event_masks[i] =
+ cpu_to_le32(mrioc->event_masks[i]);
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &evtnotify_req,
+ sizeof(evtnotify_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue EvtNotify: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "event notification timed out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue EvtNotify: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_process_event_ack - Process event acknowledgment
+ * @mrioc: Adapter instance reference
+ * @event: MPI3 event ID
+ * @event_ctx: event context
+ *
+ * Send event acknowledgment through admin queue and wait for
+ * it to complete.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_process_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
+ u32 event_ctx)
+{
+ struct mpi3_event_ack_request evtack_req;
+ int retval = 0;
+
+ memset(&evtack_req, 0, sizeof(evtack_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Send EvtAck: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ evtack_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ evtack_req.function = MPI3_FUNCTION_EVENT_ACK;
+ evtack_req.event = event;
+ evtack_req.event_context = cpu_to_le32(event_ctx);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &evtack_req,
+ sizeof(evtack_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Send EvtAck: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue EvtNotify: command timed out\n");
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_RESET))
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_EVTACK_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Send EvtAck: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_alloc_chain_bufs - Allocate chain buffers
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate chain buffers and set a bitmap to indicate free
+ * chain buffers. Chain buffers are used to pass the SGE
+ * information along with MPI3 SCSI IO requests for host I/O.
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+static int mpi3mr_alloc_chain_bufs(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u32 sz, i;
+ u16 num_chains;
+
+ if (mrioc->chain_sgl_list)
+ return retval;
+
+ num_chains = mrioc->max_host_ios / MPI3MR_CHAINBUF_FACTOR;
+
+ if (prot_mask & (SHOST_DIX_TYPE0_PROTECTION
+ | SHOST_DIX_TYPE1_PROTECTION
+ | SHOST_DIX_TYPE2_PROTECTION
+ | SHOST_DIX_TYPE3_PROTECTION))
+ num_chains += (num_chains / MPI3MR_CHAINBUFDIX_FACTOR);
+
+ mrioc->chain_buf_count = num_chains;
+ sz = sizeof(struct chain_element) * num_chains;
+ mrioc->chain_sgl_list = kzalloc(sz, GFP_KERNEL);
+ if (!mrioc->chain_sgl_list)
+ goto out_failed;
+
+ if (mrioc->max_sgl_entries > (mrioc->facts.max_data_length /
+ MPI3MR_PAGE_SIZE_4K))
+ mrioc->max_sgl_entries = mrioc->facts.max_data_length /
+ MPI3MR_PAGE_SIZE_4K;
+ sz = mrioc->max_sgl_entries * sizeof(struct mpi3_sge_common);
+ ioc_info(mrioc, "number of sgl entries=%d chain buffer size=%dKB\n",
+ mrioc->max_sgl_entries, sz/1024);
+
+ mrioc->chain_buf_pool = dma_pool_create("chain_buf pool",
+ &mrioc->pdev->dev, sz, 16, 0);
+ if (!mrioc->chain_buf_pool) {
+ ioc_err(mrioc, "chain buf pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+
+ for (i = 0; i < num_chains; i++) {
+ mrioc->chain_sgl_list[i].addr =
+ dma_pool_zalloc(mrioc->chain_buf_pool, GFP_KERNEL,
+ &mrioc->chain_sgl_list[i].dma_addr);
+
+ if (!mrioc->chain_sgl_list[i].addr)
+ goto out_failed;
+ }
+ mrioc->chain_bitmap = bitmap_zalloc(num_chains, GFP_KERNEL);
+ if (!mrioc->chain_bitmap)
+ goto out_failed;
+ return retval;
+out_failed:
+ retval = -1;
+ return retval;
+}
+
+/**
+ * mpi3mr_port_enable_complete - Mark port enable complete
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Call back for asynchronous port enable request sets the
+ * driver command to indicate port enable request is complete.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_port_enable_complete(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ drv_cmd->callback = NULL;
+ mrioc->scan_started = 0;
+ if (drv_cmd->state & MPI3MR_CMD_RESET)
+ mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR;
+ else
+ mrioc->scan_failed = drv_cmd->ioc_status;
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+}
+
+/**
+ * mpi3mr_issue_port_enable - Issue Port Enable
+ * @mrioc: Adapter instance reference
+ * @async: Flag to wait for completion or not
+ *
+ * Issue Port Enable MPI request through admin queue and if the
+ * async flag is not set wait for the completion of the port
+ * enable or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async)
+{
+ struct mpi3_port_enable_request pe_req;
+ int retval = 0;
+ u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT;
+
+ memset(&pe_req, 0, sizeof(pe_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue PortEnable: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ if (async) {
+ mrioc->init_cmds.is_waiting = 0;
+ mrioc->init_cmds.callback = mpi3mr_port_enable_complete;
+ } else {
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ init_completion(&mrioc->init_cmds.done);
+ }
+ pe_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ pe_req.function = MPI3_FUNCTION_PORT_ENABLE;
+
+ retval = mpi3mr_admin_request_post(mrioc, &pe_req, sizeof(pe_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue PortEnable: Admin Post failed\n");
+ goto out_unlock;
+ }
+ if (async) {
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+
+ wait_for_completion_timeout(&mrioc->init_cmds.done, (pe_timeout * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "port enable timed out\n");
+ retval = -1;
+ mpi3mr_check_rh_fault_ioc(mrioc, MPI3MR_RESET_FROM_PE_TIMEOUT);
+ goto out_unlock;
+ }
+ mpi3mr_port_enable_complete(mrioc, &mrioc->init_cmds);
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+ return retval;
+}
+
+/* Protocol type to name mapper structure */
+static const struct {
+ u8 protocol;
+ char *name;
+} mpi3mr_protocols[] = {
+ { MPI3_IOCFACTS_PROTOCOL_SCSI_INITIATOR, "Initiator" },
+ { MPI3_IOCFACTS_PROTOCOL_SCSI_TARGET, "Target" },
+ { MPI3_IOCFACTS_PROTOCOL_NVME, "NVMe attachment" },
+};
+
+/* Capability to name mapper structure*/
+static const struct {
+ u32 capability;
+ char *name;
+} mpi3mr_capabilities[] = {
+ { MPI3_IOCFACTS_CAPABILITY_RAID_SUPPORTED, "RAID" },
+ { MPI3_IOCFACTS_CAPABILITY_MULTIPATH_SUPPORTED, "MultiPath" },
+};
+
+/**
+ * mpi3mr_repost_diag_bufs - repost host diag buffers
+ * @mrioc: Adapter instance reference
+ *
+ * repost firmware and trace diag buffers based on global
+ * trigger flag from driver page 2
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_repost_diag_bufs(struct mpi3mr_ioc *mrioc)
+{
+ u64 global_trigger;
+ union mpi3mr_trigger_data prev_trigger_data;
+ struct diag_buffer_desc *trace_hdb = NULL;
+ struct diag_buffer_desc *fw_hdb = NULL;
+ int retval = 0;
+ bool trace_repost_needed = false;
+ bool fw_repost_needed = false;
+ u8 prev_trigger_type;
+
+ retval = mpi3mr_refresh_trigger(mrioc, MPI3_CONFIG_ACTION_READ_CURRENT);
+ if (retval)
+ return -1;
+
+ trace_hdb = mpi3mr_diag_buffer_for_type(mrioc,
+ MPI3_DIAG_BUFFER_TYPE_TRACE);
+
+ if (trace_hdb &&
+ trace_hdb->status != MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED &&
+ trace_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_GLOBAL &&
+ trace_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_ELEMENT)
+ trace_repost_needed = true;
+
+ fw_hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_FW);
+
+ if (fw_hdb && fw_hdb->status != MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED &&
+ fw_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_GLOBAL &&
+ fw_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_ELEMENT)
+ fw_repost_needed = true;
+
+ if (trace_repost_needed || fw_repost_needed) {
+ global_trigger = le64_to_cpu(mrioc->driver_pg2->global_trigger);
+ if (global_trigger &
+ MPI3_DRIVER2_GLOBALTRIGGER_POST_DIAG_TRACE_DISABLED)
+ trace_repost_needed = false;
+ if (global_trigger &
+ MPI3_DRIVER2_GLOBALTRIGGER_POST_DIAG_FW_DISABLED)
+ fw_repost_needed = false;
+ }
+
+ if (trace_repost_needed) {
+ prev_trigger_type = trace_hdb->trigger_type;
+ memcpy(&prev_trigger_data, &trace_hdb->trigger_data,
+ sizeof(trace_hdb->trigger_data));
+ retval = mpi3mr_issue_diag_buf_post(mrioc, trace_hdb);
+ if (!retval) {
+ dprint_init(mrioc, "trace diag buffer reposted");
+ mpi3mr_set_trigger_data_in_hdb(trace_hdb,
+ MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
+ } else {
+ trace_hdb->trigger_type = prev_trigger_type;
+ memcpy(&trace_hdb->trigger_data, &prev_trigger_data,
+ sizeof(prev_trigger_data));
+ ioc_err(mrioc, "trace diag buffer repost failed");
+ return -1;
+ }
+ }
+
+ if (fw_repost_needed) {
+ prev_trigger_type = fw_hdb->trigger_type;
+ memcpy(&prev_trigger_data, &fw_hdb->trigger_data,
+ sizeof(fw_hdb->trigger_data));
+ retval = mpi3mr_issue_diag_buf_post(mrioc, fw_hdb);
+ if (!retval) {
+ dprint_init(mrioc, "firmware diag buffer reposted");
+ mpi3mr_set_trigger_data_in_hdb(fw_hdb,
+ MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
+ } else {
+ fw_hdb->trigger_type = prev_trigger_type;
+ memcpy(&fw_hdb->trigger_data, &prev_trigger_data,
+ sizeof(prev_trigger_data));
+ ioc_err(mrioc, "firmware diag buffer repost failed");
+ return -1;
+ }
+ }
+ return retval;
+}
+
+/**
+ * mpi3mr_read_tsu_interval - Update time stamp interval
+ * @mrioc: Adapter instance reference
+ *
+ * Update time stamp interval if its defined in driver page 1,
+ * otherwise use default value.
+ *
+ * Return: Nothing
+ */
+static void
+mpi3mr_read_tsu_interval(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3_driver_page1 driver_pg1;
+ u16 pg_sz = sizeof(driver_pg1);
+ int retval = 0;
+
+ mrioc->ts_update_interval = MPI3MR_TSUPDATE_INTERVAL;
+
+ retval = mpi3mr_cfg_get_driver_pg1(mrioc, &driver_pg1, pg_sz);
+ if (!retval && driver_pg1.time_stamp_update)
+ mrioc->ts_update_interval = (driver_pg1.time_stamp_update * 60);
+}
+
+/**
+ * mpi3mr_print_ioc_info - Display controller information
+ * @mrioc: Adapter instance reference
+ *
+ * Display controller personality, capability, supported
+ * protocols etc.
+ *
+ * Return: Nothing
+ */
+static void
+mpi3mr_print_ioc_info(struct mpi3mr_ioc *mrioc)
+{
+ int i = 0, bytes_written = 0;
+ const char *personality;
+ char protocol[50] = {0};
+ char capabilities[100] = {0};
+ struct mpi3mr_compimg_ver *fwver = &mrioc->facts.fw_ver;
+
+ switch (mrioc->facts.personality) {
+ case MPI3_IOCFACTS_FLAGS_PERSONALITY_EHBA:
+ personality = "Enhanced HBA";
+ break;
+ case MPI3_IOCFACTS_FLAGS_PERSONALITY_RAID_DDR:
+ personality = "RAID";
+ break;
+ default:
+ personality = "Unknown";
+ break;
+ }
+
+ ioc_info(mrioc, "Running in %s Personality", personality);
+
+ ioc_info(mrioc, "FW version(%d.%d.%d.%d.%d.%d)\n",
+ fwver->gen_major, fwver->gen_minor, fwver->ph_major,
+ fwver->ph_minor, fwver->cust_id, fwver->build_num);
+
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_protocols); i++) {
+ if (mrioc->facts.protocol_flags &
+ mpi3mr_protocols[i].protocol) {
+ bytes_written += scnprintf(protocol + bytes_written,
+ sizeof(protocol) - bytes_written, "%s%s",
+ bytes_written ? "," : "",
+ mpi3mr_protocols[i].name);
+ }
+ }
+
+ bytes_written = 0;
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_capabilities); i++) {
+ if (mrioc->facts.protocol_flags &
+ mpi3mr_capabilities[i].capability) {
+ bytes_written += scnprintf(capabilities + bytes_written,
+ sizeof(capabilities) - bytes_written, "%s%s",
+ bytes_written ? "," : "",
+ mpi3mr_capabilities[i].name);
+ }
+ }
+
+ ioc_info(mrioc, "Protocol=(%s), Capabilities=(%s)\n",
+ protocol, capabilities);
+}
+
+/**
+ * mpi3mr_cleanup_resources - Free PCI resources
+ * @mrioc: Adapter instance reference
+ *
+ * Unmap PCI device memory and disable PCI device.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+
+ mpi3mr_cleanup_isr(mrioc);
+
+ if (mrioc->sysif_regs) {
+ iounmap((void __iomem *)mrioc->sysif_regs);
+ mrioc->sysif_regs = NULL;
+ }
+
+ if (pci_is_enabled(pdev)) {
+ if (mrioc->bars)
+ pci_release_selected_regions(pdev, mrioc->bars);
+ pci_disable_device(pdev);
+ }
+}
+
+/**
+ * mpi3mr_setup_resources - Enable PCI resources
+ * @mrioc: Adapter instance reference
+ *
+ * Enable PCI device memory, MSI-x registers and set DMA mask.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+ u32 memap_sz = 0;
+ int i, retval = 0, capb = 0;
+ u16 message_control;
+ u64 dma_mask = mrioc->dma_mask ? mrioc->dma_mask :
+ ((sizeof(dma_addr_t) > 4) ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
+
+ if (pci_enable_device_mem(pdev)) {
+ ioc_err(mrioc, "pci_enable_device_mem: failed\n");
+ retval = -ENODEV;
+ goto out_failed;
+ }
+
+ capb = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+ if (!capb) {
+ ioc_err(mrioc, "Unable to find MSI-X Capabilities\n");
+ retval = -ENODEV;
+ goto out_failed;
+ }
+ mrioc->bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
+ if (pci_request_selected_regions(pdev, mrioc->bars,
+ mrioc->driver_name)) {
+ ioc_err(mrioc, "pci_request_selected_regions: failed\n");
+ retval = -ENODEV;
+ goto out_failed;
+ }
+
+ for (i = 0; (i < DEVICE_COUNT_RESOURCE); i++) {
+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+ mrioc->sysif_regs_phys = pci_resource_start(pdev, i);
+ memap_sz = pci_resource_len(pdev, i);
+ mrioc->sysif_regs =
+ ioremap(mrioc->sysif_regs_phys, memap_sz);
+ break;
+ }
+ }
+
+ pci_set_master(pdev);
+
+ retval = dma_set_mask_and_coherent(&pdev->dev, dma_mask);
+ if (retval) {
+ if (dma_mask != DMA_BIT_MASK(32)) {
+ ioc_warn(mrioc, "Setting 64 bit DMA mask failed\n");
+ dma_mask = DMA_BIT_MASK(32);
+ retval = dma_set_mask_and_coherent(&pdev->dev,
+ dma_mask);
+ }
+ if (retval) {
+ mrioc->dma_mask = 0;
+ ioc_err(mrioc, "Setting 32 bit DMA mask also failed\n");
+ goto out_failed;
+ }
+ }
+ mrioc->dma_mask = dma_mask;
+
+ if (!mrioc->sysif_regs) {
+ ioc_err(mrioc,
+ "Unable to map adapter memory or resource not found\n");
+ retval = -EINVAL;
+ goto out_failed;
+ }
+
+ pci_read_config_word(pdev, capb + 2, &message_control);
+ mrioc->msix_count = (message_control & 0x3FF) + 1;
+
+ pci_save_state(pdev);
+
+ pci_set_drvdata(pdev, mrioc->shost);
+
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ ioc_info(mrioc, "iomem(0x%016llx), mapped(0x%p), size(%d)\n",
+ (unsigned long long)mrioc->sysif_regs_phys,
+ mrioc->sysif_regs, memap_sz);
+ ioc_info(mrioc, "Number of MSI-X vectors found in capabilities: (%d)\n",
+ mrioc->msix_count);
+
+ if (!reset_devices && poll_queues > 0)
+ mrioc->requested_poll_qcount = min_t(int, poll_queues,
+ mrioc->msix_count - 2);
+ return retval;
+
+out_failed:
+ mpi3mr_cleanup_resources(mrioc);
+ return retval;
+}
+
+/**
+ * mpi3mr_enable_events - Enable required events
+ * @mrioc: Adapter instance reference
+ *
+ * This routine unmasks the events required by the driver by
+ * sennding appropriate event mask bitmapt through an event
+ * notification request.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+static int mpi3mr_enable_events(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u32 i;
+
+ for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ mrioc->event_masks[i] = -1;
+
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_ADDED);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_INFO_CHANGED);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_STATUS_CHANGE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENCL_DEVICE_ADDED);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DISCOVERY);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_BROADCAST_PRIMITIVE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_PCIE_ENUMERATION);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_PREPARE_FOR_RESET);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_CABLE_MGMT);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENERGY_PACK_CHANGE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE);
+
+ retval = mpi3mr_issue_event_notification(mrioc);
+ if (retval)
+ ioc_err(mrioc, "failed to issue event notification %d\n",
+ retval);
+ return retval;
+}
+
+/**
+ * mpi3mr_init_ioc - Initialize the controller
+ * @mrioc: Adapter instance reference
+ *
+ * This the controller initialization routine, executed either
+ * after soft reset or from pci probe callback.
+ * Setup the required resources, memory map the controller
+ * registers, create admin and operational reply queue pairs,
+ * allocate required memory for reply pool, sense buffer pool,
+ * issue IOC init request to the firmware, unmask the events and
+ * issue port enable to discover SAS/SATA/NVMe devies and RAID
+ * volumes.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u8 retry = 0;
+ struct mpi3_ioc_facts_data facts_data;
+ u32 sz;
+
+retry_init:
+ retval = mpi3mr_bring_ioc_ready(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to bring ioc ready: error %d\n",
+ retval);
+ goto out_failed_noretry;
+ }
+
+ retval = mpi3mr_setup_isr(mrioc, 1);
+ if (retval) {
+ ioc_err(mrioc, "Failed to setup ISR error %d\n",
+ retval);
+ goto out_failed_noretry;
+ }
+
+ retval = mpi3mr_issue_iocfacts(mrioc, &facts_data);
+ if (retval) {
+ ioc_err(mrioc, "Failed to Issue IOC Facts %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ mrioc->max_host_ios = mrioc->facts.max_reqs - MPI3MR_INTERNAL_CMDS_RESVD;
+ mrioc->shost->max_sectors = mrioc->facts.max_data_length / 512;
+ mrioc->num_io_throttle_group = mrioc->facts.max_io_throttle_group;
+ atomic_set(&mrioc->pend_large_data_sz, 0);
+
+ if (reset_devices)
+ mrioc->max_host_ios = min_t(int, mrioc->max_host_ios,
+ MPI3MR_HOST_IOS_KDUMP);
+
+ if (!(mrioc->facts.ioc_capabilities &
+ MPI3_IOCFACTS_CAPABILITY_MULTIPATH_SUPPORTED)) {
+ mrioc->sas_transport_enabled = 1;
+ mrioc->scsi_device_channel = 1;
+ mrioc->shost->max_channel = 1;
+ mrioc->shost->transportt = mpi3mr_transport_template;
+ }
+
+ if (mrioc->facts.max_req_limit)
+ mrioc->prevent_reply_qfull = true;
+
+ if (mrioc->facts.ioc_capabilities &
+ MPI3_IOCFACTS_CAPABILITY_SEG_DIAG_TRACE_SUPPORTED)
+ mrioc->seg_tb_support = true;
+
+ mrioc->reply_sz = mrioc->facts.reply_sz;
+
+ retval = mpi3mr_check_reset_dma_mask(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Resetting dma mask failed %d\n",
+ retval);
+ goto out_failed_noretry;
+ }
+
+ mpi3mr_read_tsu_interval(mrioc);
+ mpi3mr_print_ioc_info(mrioc);
+
+ dprint_init(mrioc, "allocating host diag buffers\n");
+ mpi3mr_alloc_diag_bufs(mrioc);
+
+ dprint_init(mrioc, "allocating ioctl dma buffers\n");
+ mpi3mr_alloc_ioctl_dma_memory(mrioc);
+
+ dprint_init(mrioc, "posting host diag buffers\n");
+ retval = mpi3mr_post_diag_bufs(mrioc);
+
+ if (retval)
+ ioc_warn(mrioc, "failed to post host diag buffers\n");
+
+ if (!mrioc->init_cmds.reply) {
+ retval = mpi3mr_alloc_reply_sense_bufs(mrioc);
+ if (retval) {
+ ioc_err(mrioc,
+ "%s :Failed to allocated reply sense buffers %d\n",
+ __func__, retval);
+ goto out_failed_noretry;
+ }
+ }
+
+ if (!mrioc->chain_sgl_list) {
+ retval = mpi3mr_alloc_chain_bufs(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to allocated chain buffers %d\n",
+ retval);
+ goto out_failed_noretry;
+ }
+ }
+
+ retval = mpi3mr_issue_iocinit(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to Issue IOC Init %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ retval = mpi3mr_print_pkg_ver(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to get package version\n");
+ goto out_failed;
+ }
+
+ retval = mpi3mr_setup_isr(mrioc, 0);
+ if (retval) {
+ ioc_err(mrioc, "Failed to re-setup ISR, error %d\n",
+ retval);
+ goto out_failed_noretry;
+ }
+
+ retval = mpi3mr_create_op_queues(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to create OpQueues error %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ if (!mrioc->pel_seqnum_virt) {
+ dprint_init(mrioc, "allocating memory for pel_seqnum_virt\n");
+ mrioc->pel_seqnum_sz = sizeof(struct mpi3_pel_seq);
+ mrioc->pel_seqnum_virt = dma_alloc_coherent(&mrioc->pdev->dev,
+ mrioc->pel_seqnum_sz, &mrioc->pel_seqnum_dma,
+ GFP_KERNEL);
+ if (!mrioc->pel_seqnum_virt) {
+ retval = -ENOMEM;
+ goto out_failed_noretry;
+ }
+ }
+
+ if (!mrioc->throttle_groups && mrioc->num_io_throttle_group) {
+ dprint_init(mrioc, "allocating memory for throttle groups\n");
+ sz = sizeof(struct mpi3mr_throttle_group_info);
+ mrioc->throttle_groups = kcalloc(mrioc->num_io_throttle_group, sz, GFP_KERNEL);
+ if (!mrioc->throttle_groups) {
+ retval = -1;
+ goto out_failed_noretry;
+ }
+ }
+
+ retval = mpi3mr_enable_events(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to enable events %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ retval = mpi3mr_refresh_trigger(mrioc, MPI3_CONFIG_ACTION_READ_CURRENT);
+ if (retval) {
+ ioc_err(mrioc, "failed to refresh triggers\n");
+ goto out_failed;
+ }
+
+ ioc_info(mrioc, "controller initialization completed successfully\n");
+ return retval;
+out_failed:
+ if (retry < 2) {
+ retry++;
+ ioc_warn(mrioc, "retrying controller initialization, retry_count:%d\n",
+ retry);
+ mpi3mr_memset_buffers(mrioc);
+ goto retry_init;
+ }
+ retval = -1;
+out_failed_noretry:
+ ioc_err(mrioc, "controller initialization failed\n");
+ mpi3mr_issue_reset(mrioc, MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_CTLR_CLEANUP);
+ mrioc->unrecoverable = 1;
+ return retval;
+}
+
+/**
+ * mpi3mr_reinit_ioc - Re-Initialize the controller
+ * @mrioc: Adapter instance reference
+ * @is_resume: Called from resume or reset path
+ *
+ * This the controller re-initialization routine, executed from
+ * the soft reset handler or resume callback. Creates
+ * operational reply queue pairs, allocate required memory for
+ * reply pool, sense buffer pool, issue IOC init request to the
+ * firmware, unmask the events and issue port enable to discover
+ * SAS/SATA/NVMe devices and RAID volumes.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+int mpi3mr_reinit_ioc(struct mpi3mr_ioc *mrioc, u8 is_resume)
+{
+ int retval = 0;
+ u8 retry = 0;
+ struct mpi3_ioc_facts_data facts_data;
+ u32 pe_timeout, ioc_status;
+
+retry_init:
+ pe_timeout =
+ (MPI3MR_PORTENABLE_TIMEOUT / MPI3MR_PORTENABLE_POLL_INTERVAL);
+
+ dprint_reset(mrioc, "bringing up the controller to ready state\n");
+ retval = mpi3mr_bring_ioc_ready(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to bring to ready state\n");
+ goto out_failed_noretry;
+ }
+
+ mrioc->io_admin_reset_sync = 0;
+ if (is_resume || mrioc->block_on_pci_err) {
+ dprint_reset(mrioc, "setting up single ISR\n");
+ retval = mpi3mr_setup_isr(mrioc, 1);
+ if (retval) {
+ ioc_err(mrioc, "failed to setup ISR\n");
+ goto out_failed_noretry;
+ }
+ } else
+ mpi3mr_ioc_enable_intr(mrioc);
+
+ dprint_reset(mrioc, "getting ioc_facts\n");
+ retval = mpi3mr_issue_iocfacts(mrioc, &facts_data);
+ if (retval) {
+ ioc_err(mrioc, "failed to get ioc_facts\n");
+ goto out_failed;
+ }
+
+ dprint_reset(mrioc, "validating ioc_facts\n");
+ retval = mpi3mr_revalidate_factsdata(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to revalidate ioc_facts data\n");
+ goto out_failed_noretry;
+ }
+
+ mpi3mr_read_tsu_interval(mrioc);
+ mpi3mr_print_ioc_info(mrioc);
+
+ if (is_resume) {
+ dprint_reset(mrioc, "posting host diag buffers\n");
+ retval = mpi3mr_post_diag_bufs(mrioc);
+ if (retval)
+ ioc_warn(mrioc, "failed to post host diag buffers\n");
+ } else {
+ retval = mpi3mr_repost_diag_bufs(mrioc);
+ if (retval)
+ ioc_warn(mrioc, "failed to re post host diag buffers\n");
+ }
+
+ dprint_reset(mrioc, "sending ioc_init\n");
+ retval = mpi3mr_issue_iocinit(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to send ioc_init\n");
+ goto out_failed;
+ }
+
+ dprint_reset(mrioc, "getting package version\n");
+ retval = mpi3mr_print_pkg_ver(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to get package version\n");
+ goto out_failed;
+ }
+
+ if (is_resume || mrioc->block_on_pci_err) {
+ dprint_reset(mrioc, "setting up multiple ISR\n");
+ retval = mpi3mr_setup_isr(mrioc, 0);
+ if (retval) {
+ ioc_err(mrioc, "failed to re-setup ISR\n");
+ goto out_failed_noretry;
+ }
+ }
+
+ dprint_reset(mrioc, "creating operational queue pairs\n");
+ retval = mpi3mr_create_op_queues(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to create operational queue pairs\n");
+ goto out_failed;
+ }
+
+ if (!mrioc->pel_seqnum_virt) {
+ dprint_reset(mrioc, "allocating memory for pel_seqnum_virt\n");
+ mrioc->pel_seqnum_sz = sizeof(struct mpi3_pel_seq);
+ mrioc->pel_seqnum_virt = dma_alloc_coherent(&mrioc->pdev->dev,
+ mrioc->pel_seqnum_sz, &mrioc->pel_seqnum_dma,
+ GFP_KERNEL);
+ if (!mrioc->pel_seqnum_virt) {
+ retval = -ENOMEM;
+ goto out_failed_noretry;
+ }
+ }
+
+ if (mrioc->shost->nr_hw_queues > mrioc->num_op_reply_q) {
+ ioc_err(mrioc,
+ "cannot create minimum number of operational queues expected:%d created:%d\n",
+ mrioc->shost->nr_hw_queues, mrioc->num_op_reply_q);
+ retval = -1;
+ goto out_failed_noretry;
+ }
+
+ dprint_reset(mrioc, "enabling events\n");
+ retval = mpi3mr_enable_events(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "failed to enable events\n");
+ goto out_failed;
+ }
+
+ mrioc->device_refresh_on = 1;
+ mpi3mr_add_event_wait_for_device_refresh(mrioc);
+
+ ioc_info(mrioc, "sending port enable\n");
+ retval = mpi3mr_issue_port_enable(mrioc, 1);
+ if (retval) {
+ ioc_err(mrioc, "failed to issue port enable\n");
+ goto out_failed;
+ }
+ do {
+ ssleep(MPI3MR_PORTENABLE_POLL_INTERVAL);
+ if (mrioc->init_cmds.state == MPI3MR_CMD_NOTUSED)
+ break;
+ if (!pci_device_is_present(mrioc->pdev))
+ mrioc->unrecoverable = 1;
+ if (mrioc->unrecoverable) {
+ retval = -1;
+ goto out_failed_noretry;
+ }
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) ||
+ (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)) {
+ mpi3mr_print_fault_info(mrioc);
+ mrioc->init_cmds.is_waiting = 0;
+ mrioc->init_cmds.callback = NULL;
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ goto out_failed;
+ }
+ } while (--pe_timeout);
+
+ if (!pe_timeout) {
+ ioc_err(mrioc, "port enable timed out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_PE_TIMEOUT);
+ mrioc->init_cmds.is_waiting = 0;
+ mrioc->init_cmds.callback = NULL;
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ goto out_failed;
+ } else if (mrioc->scan_failed) {
+ ioc_err(mrioc,
+ "port enable failed with status=0x%04x\n",
+ mrioc->scan_failed);
+ } else
+ ioc_info(mrioc, "port enable completed successfully\n");
+
+ ioc_info(mrioc, "controller %s completed successfully\n",
+ (is_resume)?"resume":"re-initialization");
+ return retval;
+out_failed:
+ if (retry < 2) {
+ retry++;
+ ioc_warn(mrioc, "retrying controller %s, retry_count:%d\n",
+ (is_resume)?"resume":"re-initialization", retry);
+ mpi3mr_memset_buffers(mrioc);
+ goto retry_init;
+ }
+ retval = -1;
+out_failed_noretry:
+ ioc_err(mrioc, "controller %s is failed\n",
+ (is_resume)?"resume":"re-initialization");
+ mpi3mr_issue_reset(mrioc, MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_CTLR_CLEANUP);
+ mrioc->unrecoverable = 1;
+ return retval;
+}
+
+/**
+ * mpi3mr_memset_op_reply_q_buffers - memset the operational reply queue's
+ * segments
+ * @mrioc: Adapter instance reference
+ * @qidx: Operational reply queue index
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_memset_op_reply_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
+ struct segments *segments;
+ int i, size;
+
+ if (!op_reply_q->q_segments)
+ return;
+
+ size = op_reply_q->segment_qd * mrioc->op_reply_desc_sz;
+ segments = op_reply_q->q_segments;
+ for (i = 0; i < op_reply_q->num_segments; i++)
+ memset(segments[i].segment, 0, size);
+}
+
+/**
+ * mpi3mr_memset_op_req_q_buffers - memset the operational request queue's
+ * segments
+ * @mrioc: Adapter instance reference
+ * @qidx: Operational request queue index
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_memset_op_req_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_req_qinfo *op_req_q = mrioc->req_qinfo + qidx;
+ struct segments *segments;
+ int i, size;
+
+ if (!op_req_q->q_segments)
+ return;
+
+ size = op_req_q->segment_qd * mrioc->facts.op_req_sz;
+ segments = op_req_q->q_segments;
+ for (i = 0; i < op_req_q->num_segments; i++)
+ memset(segments[i].segment, 0, size);
+}
+
+/**
+ * mpi3mr_memset_buffers - memset memory for a controller
+ * @mrioc: Adapter instance reference
+ *
+ * clear all the memory allocated for a controller, typically
+ * called post reset to reuse the memory allocated during the
+ * controller init.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc)
+{
+ u16 i;
+ struct mpi3mr_throttle_group_info *tg;
+
+ mrioc->change_count = 0;
+ mrioc->active_poll_qcount = 0;
+ mrioc->default_qcount = 0;
+ if (mrioc->admin_req_base)
+ memset(mrioc->admin_req_base, 0, mrioc->admin_req_q_sz);
+ if (mrioc->admin_reply_base)
+ memset(mrioc->admin_reply_base, 0, mrioc->admin_reply_q_sz);
+ atomic_set(&mrioc->admin_reply_q_in_use, 0);
+ atomic_set(&mrioc->admin_pend_isr, 0);
+
+ if (mrioc->init_cmds.reply) {
+ memset(mrioc->init_cmds.reply, 0, sizeof(*mrioc->init_cmds.reply));
+ memset(mrioc->bsg_cmds.reply, 0,
+ sizeof(*mrioc->bsg_cmds.reply));
+ memset(mrioc->host_tm_cmds.reply, 0,
+ sizeof(*mrioc->host_tm_cmds.reply));
+ memset(mrioc->pel_cmds.reply, 0,
+ sizeof(*mrioc->pel_cmds.reply));
+ memset(mrioc->pel_abort_cmd.reply, 0,
+ sizeof(*mrioc->pel_abort_cmd.reply));
+ memset(mrioc->transport_cmds.reply, 0,
+ sizeof(*mrioc->transport_cmds.reply));
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
+ memset(mrioc->dev_rmhs_cmds[i].reply, 0,
+ sizeof(*mrioc->dev_rmhs_cmds[i].reply));
+ for (i = 0; i < MPI3MR_NUM_EVTACKCMD; i++)
+ memset(mrioc->evtack_cmds[i].reply, 0,
+ sizeof(*mrioc->evtack_cmds[i].reply));
+ bitmap_clear(mrioc->removepend_bitmap, 0,
+ mrioc->dev_handle_bitmap_bits);
+ bitmap_clear(mrioc->devrem_bitmap, 0, MPI3MR_NUM_DEVRMCMD);
+ bitmap_clear(mrioc->evtack_cmds_bitmap, 0,
+ MPI3MR_NUM_EVTACKCMD);
+ }
+
+ for (i = 0; i < mrioc->num_queues; i++) {
+ mrioc->op_reply_qinfo[i].qid = 0;
+ mrioc->op_reply_qinfo[i].ci = 0;
+ mrioc->op_reply_qinfo[i].num_replies = 0;
+ mrioc->op_reply_qinfo[i].ephase = 0;
+ atomic_set(&mrioc->op_reply_qinfo[i].pend_ios, 0);
+ atomic_set(&mrioc->op_reply_qinfo[i].in_use, 0);
+ mpi3mr_memset_op_reply_q_buffers(mrioc, i);
+
+ mrioc->req_qinfo[i].ci = 0;
+ mrioc->req_qinfo[i].pi = 0;
+ mrioc->req_qinfo[i].num_requests = 0;
+ mrioc->req_qinfo[i].qid = 0;
+ mrioc->req_qinfo[i].reply_qid = 0;
+ spin_lock_init(&mrioc->req_qinfo[i].q_lock);
+ mpi3mr_memset_op_req_q_buffers(mrioc, i);
+ }
+
+ atomic_set(&mrioc->pend_large_data_sz, 0);
+ if (mrioc->throttle_groups) {
+ tg = mrioc->throttle_groups;
+ for (i = 0; i < mrioc->num_io_throttle_group; i++, tg++) {
+ tg->id = 0;
+ tg->fw_qd = 0;
+ tg->modified_qd = 0;
+ tg->io_divert = 0;
+ tg->need_qd_reduction = 0;
+ tg->high = 0;
+ tg->low = 0;
+ tg->qd_reduction = 0;
+ atomic_set(&tg->pend_large_data_sz, 0);
+ }
+ }
+}
+
+/**
+ * mpi3mr_free_mem - Free memory allocated for a controller
+ * @mrioc: Adapter instance reference
+ *
+ * Free all the memory allocated for a controller.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc)
+{
+ u16 i, j;
+ struct mpi3mr_intr_info *intr_info;
+ struct diag_buffer_desc *diag_buffer;
+
+ mpi3mr_free_enclosure_list(mrioc);
+ mpi3mr_free_ioctl_dma_memory(mrioc);
+
+ if (mrioc->sense_buf_pool) {
+ if (mrioc->sense_buf)
+ dma_pool_free(mrioc->sense_buf_pool, mrioc->sense_buf,
+ mrioc->sense_buf_dma);
+ dma_pool_destroy(mrioc->sense_buf_pool);
+ mrioc->sense_buf = NULL;
+ mrioc->sense_buf_pool = NULL;
+ }
+ if (mrioc->sense_buf_q_pool) {
+ if (mrioc->sense_buf_q)
+ dma_pool_free(mrioc->sense_buf_q_pool,
+ mrioc->sense_buf_q, mrioc->sense_buf_q_dma);
+ dma_pool_destroy(mrioc->sense_buf_q_pool);
+ mrioc->sense_buf_q = NULL;
+ mrioc->sense_buf_q_pool = NULL;
+ }
+
+ if (mrioc->reply_buf_pool) {
+ if (mrioc->reply_buf)
+ dma_pool_free(mrioc->reply_buf_pool, mrioc->reply_buf,
+ mrioc->reply_buf_dma);
+ dma_pool_destroy(mrioc->reply_buf_pool);
+ mrioc->reply_buf = NULL;
+ mrioc->reply_buf_pool = NULL;
+ }
+ if (mrioc->reply_free_q_pool) {
+ if (mrioc->reply_free_q)
+ dma_pool_free(mrioc->reply_free_q_pool,
+ mrioc->reply_free_q, mrioc->reply_free_q_dma);
+ dma_pool_destroy(mrioc->reply_free_q_pool);
+ mrioc->reply_free_q = NULL;
+ mrioc->reply_free_q_pool = NULL;
+ }
+
+ for (i = 0; i < mrioc->num_op_req_q; i++)
+ mpi3mr_free_op_req_q_segments(mrioc, i);
+
+ for (i = 0; i < mrioc->num_op_reply_q; i++)
+ mpi3mr_free_op_reply_q_segments(mrioc, i);
+
+ for (i = 0; i < mrioc->intr_info_count; i++) {
+ intr_info = mrioc->intr_info + i;
+ intr_info->op_reply_q = NULL;
+ }
+
+ kfree(mrioc->req_qinfo);
+ mrioc->req_qinfo = NULL;
+ mrioc->num_op_req_q = 0;
+
+ kfree(mrioc->op_reply_qinfo);
+ mrioc->op_reply_qinfo = NULL;
+ mrioc->num_op_reply_q = 0;
+
+ kfree(mrioc->init_cmds.reply);
+ mrioc->init_cmds.reply = NULL;
+
+ kfree(mrioc->bsg_cmds.reply);
+ mrioc->bsg_cmds.reply = NULL;
+
+ kfree(mrioc->host_tm_cmds.reply);
+ mrioc->host_tm_cmds.reply = NULL;
+
+ kfree(mrioc->pel_cmds.reply);
+ mrioc->pel_cmds.reply = NULL;
+
+ kfree(mrioc->pel_abort_cmd.reply);
+ mrioc->pel_abort_cmd.reply = NULL;
+
+ for (i = 0; i < MPI3MR_NUM_EVTACKCMD; i++) {
+ kfree(mrioc->evtack_cmds[i].reply);
+ mrioc->evtack_cmds[i].reply = NULL;
+ }
+
+ bitmap_free(mrioc->removepend_bitmap);
+ mrioc->removepend_bitmap = NULL;
+
+ bitmap_free(mrioc->devrem_bitmap);
+ mrioc->devrem_bitmap = NULL;
+
+ bitmap_free(mrioc->evtack_cmds_bitmap);
+ mrioc->evtack_cmds_bitmap = NULL;
+
+ bitmap_free(mrioc->chain_bitmap);
+ mrioc->chain_bitmap = NULL;
+
+ kfree(mrioc->transport_cmds.reply);
+ mrioc->transport_cmds.reply = NULL;
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
+ kfree(mrioc->dev_rmhs_cmds[i].reply);
+ mrioc->dev_rmhs_cmds[i].reply = NULL;
+ }
+
+ if (mrioc->chain_buf_pool) {
+ for (i = 0; i < mrioc->chain_buf_count; i++) {
+ if (mrioc->chain_sgl_list[i].addr) {
+ dma_pool_free(mrioc->chain_buf_pool,
+ mrioc->chain_sgl_list[i].addr,
+ mrioc->chain_sgl_list[i].dma_addr);
+ mrioc->chain_sgl_list[i].addr = NULL;
+ }
+ }
+ dma_pool_destroy(mrioc->chain_buf_pool);
+ mrioc->chain_buf_pool = NULL;
+ }
+
+ kfree(mrioc->chain_sgl_list);
+ mrioc->chain_sgl_list = NULL;
+
+ if (mrioc->admin_reply_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_reply_q_sz,
+ mrioc->admin_reply_base, mrioc->admin_reply_dma);
+ mrioc->admin_reply_base = NULL;
+ }
+ if (mrioc->admin_req_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_req_q_sz,
+ mrioc->admin_req_base, mrioc->admin_req_dma);
+ mrioc->admin_req_base = NULL;
+ }
+
+ if (mrioc->pel_seqnum_virt) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->pel_seqnum_sz,
+ mrioc->pel_seqnum_virt, mrioc->pel_seqnum_dma);
+ mrioc->pel_seqnum_virt = NULL;
+ }
+
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ diag_buffer = &mrioc->diag_buffers[i];
+ if ((i == 0) && mrioc->seg_tb_support) {
+ if (mrioc->trace_buf_pool) {
+ for (j = 0; j < mrioc->num_tb_segs; j++) {
+ if (mrioc->trace_buf[j].segment) {
+ dma_pool_free(mrioc->trace_buf_pool,
+ mrioc->trace_buf[j].segment,
+ mrioc->trace_buf[j].segment_dma);
+ mrioc->trace_buf[j].segment = NULL;
+ }
+
+ mrioc->trace_buf[j].segment = NULL;
+ }
+ dma_pool_destroy(mrioc->trace_buf_pool);
+ mrioc->trace_buf_pool = NULL;
+ }
+
+ kfree(mrioc->trace_buf);
+ mrioc->trace_buf = NULL;
+ diag_buffer->size = sizeof(u64) * mrioc->num_tb_segs;
+ }
+ if (diag_buffer->addr) {
+ dma_free_coherent(&mrioc->pdev->dev,
+ diag_buffer->size, diag_buffer->addr,
+ diag_buffer->dma_addr);
+ diag_buffer->addr = NULL;
+ diag_buffer->size = 0;
+ diag_buffer->type = 0;
+ diag_buffer->status = 0;
+ }
+ }
+
+ kfree(mrioc->throttle_groups);
+ mrioc->throttle_groups = NULL;
+
+ kfree(mrioc->logdata_buf);
+ mrioc->logdata_buf = NULL;
+
+}
+
+/**
+ * mpi3mr_issue_ioc_shutdown - shutdown controller
+ * @mrioc: Adapter instance reference
+ *
+ * Send shutodwn notification to the controller and wait for the
+ * shutdown_timeout for it to be completed.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_config, ioc_status;
+ u8 retval = 1;
+ u32 timeout = MPI3MR_DEFAULT_SHUTDOWN_TIME * 10;
+
+ ioc_info(mrioc, "Issuing shutdown Notification\n");
+ if (mrioc->unrecoverable) {
+ ioc_warn(mrioc,
+ "IOC is unrecoverable shutdown is not issued\n");
+ return;
+ }
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK)
+ == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS) {
+ ioc_info(mrioc, "shutdown already in progress\n");
+ return;
+ }
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL;
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ;
+
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+
+ if (mrioc->facts.shutdown_timeout)
+ timeout = mrioc->facts.shutdown_timeout * 10;
+
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK)
+ == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_COMPLETE) {
+ retval = 0;
+ break;
+ }
+ msleep(100);
+ } while (--timeout);
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+
+ if (retval) {
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK)
+ == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS)
+ ioc_warn(mrioc,
+ "shutdown still in progress after timeout\n");
+ }
+
+ ioc_info(mrioc,
+ "Base IOC Sts/Config after %s shutdown is (0x%08x)/(0x%08x)\n",
+ (!retval) ? "successful" : "failed", ioc_status,
+ ioc_config);
+}
+
+/**
+ * mpi3mr_cleanup_ioc - Cleanup controller
+ * @mrioc: Adapter instance reference
+ *
+ * controller cleanup handler, Message unit reset or soft reset
+ * and shutdown notification is issued to the controller.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc)
+{
+ enum mpi3mr_iocstate ioc_state;
+
+ dprint_exit(mrioc, "cleaning up the controller\n");
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+
+ if (!mrioc->unrecoverable && !mrioc->reset_in_progress &&
+ !mrioc->pci_err_recovery &&
+ (ioc_state == MRIOC_STATE_READY)) {
+ if (mpi3mr_issue_and_process_mur(mrioc,
+ MPI3MR_RESET_FROM_CTLR_CLEANUP))
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET,
+ MPI3MR_RESET_FROM_MUR_FAILURE);
+ mpi3mr_issue_ioc_shutdown(mrioc);
+ }
+ dprint_exit(mrioc, "controller cleanup completed\n");
+}
+
+/**
+ * mpi3mr_drv_cmd_comp_reset - Flush a internal driver command
+ * @mrioc: Adapter instance reference
+ * @cmdptr: Internal command tracker
+ *
+ * Complete an internal driver commands with state indicating it
+ * is completed due to reset.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_drv_cmd_comp_reset(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *cmdptr)
+{
+ if (cmdptr->state & MPI3MR_CMD_PENDING) {
+ cmdptr->state |= MPI3MR_CMD_RESET;
+ cmdptr->state &= ~MPI3MR_CMD_PENDING;
+ if (cmdptr->is_waiting) {
+ complete(&cmdptr->done);
+ cmdptr->is_waiting = 0;
+ } else if (cmdptr->callback)
+ cmdptr->callback(mrioc, cmdptr);
+ }
+}
+
+/**
+ * mpi3mr_flush_drv_cmds - Flush internaldriver commands
+ * @mrioc: Adapter instance reference
+ *
+ * Flush all internal driver commands post reset
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_flush_drv_cmds(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_drv_cmd *cmdptr;
+ u8 i;
+
+ cmdptr = &mrioc->init_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+ cmdptr = &mrioc->cfg_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+ cmdptr = &mrioc->bsg_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+ cmdptr = &mrioc->host_tm_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
+ cmdptr = &mrioc->dev_rmhs_cmds[i];
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+ }
+
+ for (i = 0; i < MPI3MR_NUM_EVTACKCMD; i++) {
+ cmdptr = &mrioc->evtack_cmds[i];
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+ }
+
+ cmdptr = &mrioc->pel_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+ cmdptr = &mrioc->pel_abort_cmd;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+ cmdptr = &mrioc->transport_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+}
+
+/**
+ * mpi3mr_pel_wait_post - Issue PEL Wait
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Issue PEL Wait MPI request through admin queue and return.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_pel_wait_post(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ struct mpi3_pel_req_action_wait pel_wait;
+
+ mrioc->pel_abort_requested = false;
+
+ memset(&pel_wait, 0, sizeof(pel_wait));
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 0;
+ drv_cmd->callback = mpi3mr_pel_wait_complete;
+ drv_cmd->ioc_status = 0;
+ drv_cmd->ioc_loginfo = 0;
+ pel_wait.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_PEL_WAIT);
+ pel_wait.function = MPI3_FUNCTION_PERSISTENT_EVENT_LOG;
+ pel_wait.action = MPI3_PEL_ACTION_WAIT;
+ pel_wait.starting_sequence_number = cpu_to_le32(mrioc->pel_newest_seqnum);
+ pel_wait.locale = cpu_to_le16(mrioc->pel_locale);
+ pel_wait.class = cpu_to_le16(mrioc->pel_class);
+ pel_wait.wait_time = MPI3_PEL_WAITTIME_INFINITE_WAIT;
+ dprint_bsg_info(mrioc, "sending pel_wait seqnum(%d), class(%d), locale(0x%08x)\n",
+ mrioc->pel_newest_seqnum, mrioc->pel_class, mrioc->pel_locale);
+
+ if (mpi3mr_admin_request_post(mrioc, &pel_wait, sizeof(pel_wait), 0)) {
+ dprint_bsg_err(mrioc,
+ "Issuing PELWait: Admin post failed\n");
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->retry_count = 0;
+ mrioc->pel_enabled = false;
+ }
+}
+
+/**
+ * mpi3mr_pel_get_seqnum_post - Issue PEL Get Sequence number
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Issue PEL get sequence number MPI request through admin queue
+ * and return.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_pel_get_seqnum_post(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ struct mpi3_pel_req_action_get_sequence_numbers pel_getseq_req;
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+ int retval = 0;
+
+ memset(&pel_getseq_req, 0, sizeof(pel_getseq_req));
+ mrioc->pel_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->pel_cmds.is_waiting = 0;
+ mrioc->pel_cmds.ioc_status = 0;
+ mrioc->pel_cmds.ioc_loginfo = 0;
+ mrioc->pel_cmds.callback = mpi3mr_pel_get_seqnum_complete;
+ pel_getseq_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_PEL_WAIT);
+ pel_getseq_req.function = MPI3_FUNCTION_PERSISTENT_EVENT_LOG;
+ pel_getseq_req.action = MPI3_PEL_ACTION_GET_SEQNUM;
+ mpi3mr_add_sg_single(&pel_getseq_req.sgl, sgl_flags,
+ mrioc->pel_seqnum_sz, mrioc->pel_seqnum_dma);
+
+ retval = mpi3mr_admin_request_post(mrioc, &pel_getseq_req,
+ sizeof(pel_getseq_req), 0);
+ if (retval) {
+ if (drv_cmd) {
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->retry_count = 0;
+ }
+ mrioc->pel_enabled = false;
+ }
+
+ return retval;
+}
+
+/**
+ * mpi3mr_pel_wait_complete - PELWait Completion callback
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * This is a callback handler for the PELWait request and
+ * firmware completes a PELWait request when it is aborted or a
+ * new PEL entry is available. This sends AEN to the application
+ * and if the PELwait completion is not due to PELAbort then
+ * this will send a request for new PEL Sequence number
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_pel_wait_complete(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ struct mpi3_pel_reply *pel_reply = NULL;
+ u16 ioc_status, pe_log_status;
+ bool do_retry = false;
+
+ if (drv_cmd->state & MPI3MR_CMD_RESET)
+ goto cleanup_drv_cmd;
+
+ ioc_status = drv_cmd->ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "%s: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ __func__, ioc_status, drv_cmd->ioc_loginfo);
+ dprint_bsg_err(mrioc,
+ "pel_wait: failed with ioc_status(0x%04x), log_info(0x%08x)\n",
+ ioc_status, drv_cmd->ioc_loginfo);
+ do_retry = true;
+ }
+
+ if (drv_cmd->state & MPI3MR_CMD_REPLY_VALID)
+ pel_reply = (struct mpi3_pel_reply *)drv_cmd->reply;
+
+ if (!pel_reply) {
+ dprint_bsg_err(mrioc,
+ "pel_wait: failed due to no reply\n");
+ goto out_failed;
+ }
+
+ pe_log_status = le16_to_cpu(pel_reply->pe_log_status);
+ if ((pe_log_status != MPI3_PEL_STATUS_SUCCESS) &&
+ (pe_log_status != MPI3_PEL_STATUS_ABORTED)) {
+ ioc_err(mrioc, "%s: Failed pe_log_status(0x%04x)\n",
+ __func__, pe_log_status);
+ dprint_bsg_err(mrioc,
+ "pel_wait: failed due to pel_log_status(0x%04x)\n",
+ pe_log_status);
+ do_retry = true;
+ }
+
+ if (do_retry) {
+ if (drv_cmd->retry_count < MPI3MR_PEL_RETRY_COUNT) {
+ drv_cmd->retry_count++;
+ dprint_bsg_err(mrioc, "pel_wait: retrying(%d)\n",
+ drv_cmd->retry_count);
+ mpi3mr_pel_wait_post(mrioc, drv_cmd);
+ return;
+ }
+ dprint_bsg_err(mrioc,
+ "pel_wait: failed after all retries(%d)\n",
+ drv_cmd->retry_count);
+ goto out_failed;
+ }
+ atomic64_inc(&event_counter);
+ if (!mrioc->pel_abort_requested) {
+ mrioc->pel_cmds.retry_count = 0;
+ mpi3mr_pel_get_seqnum_post(mrioc, &mrioc->pel_cmds);
+ }
+
+ return;
+out_failed:
+ mrioc->pel_enabled = false;
+cleanup_drv_cmd:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->retry_count = 0;
+}
+
+/**
+ * mpi3mr_pel_get_seqnum_complete - PELGetSeqNum Completion callback
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * This is a callback handler for the PEL get sequence number
+ * request and a new PEL wait request will be issued to the
+ * firmware from this
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_pel_get_seqnum_complete(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ struct mpi3_pel_reply *pel_reply = NULL;
+ struct mpi3_pel_seq *pel_seqnum_virt;
+ u16 ioc_status;
+ bool do_retry = false;
+
+ pel_seqnum_virt = (struct mpi3_pel_seq *)mrioc->pel_seqnum_virt;
+
+ if (drv_cmd->state & MPI3MR_CMD_RESET)
+ goto cleanup_drv_cmd;
+
+ ioc_status = drv_cmd->ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_bsg_err(mrioc,
+ "pel_get_seqnum: failed with ioc_status(0x%04x), log_info(0x%08x)\n",
+ ioc_status, drv_cmd->ioc_loginfo);
+ do_retry = true;
+ }
+
+ if (drv_cmd->state & MPI3MR_CMD_REPLY_VALID)
+ pel_reply = (struct mpi3_pel_reply *)drv_cmd->reply;
+ if (!pel_reply) {
+ dprint_bsg_err(mrioc,
+ "pel_get_seqnum: failed due to no reply\n");
+ goto out_failed;
+ }
+
+ if (le16_to_cpu(pel_reply->pe_log_status) != MPI3_PEL_STATUS_SUCCESS) {
+ dprint_bsg_err(mrioc,
+ "pel_get_seqnum: failed due to pel_log_status(0x%04x)\n",
+ le16_to_cpu(pel_reply->pe_log_status));
+ do_retry = true;
+ }
+
+ if (do_retry) {
+ if (drv_cmd->retry_count < MPI3MR_PEL_RETRY_COUNT) {
+ drv_cmd->retry_count++;
+ dprint_bsg_err(mrioc,
+ "pel_get_seqnum: retrying(%d)\n",
+ drv_cmd->retry_count);
+ mpi3mr_pel_get_seqnum_post(mrioc, drv_cmd);
+ return;
+ }
+
+ dprint_bsg_err(mrioc,
+ "pel_get_seqnum: failed after all retries(%d)\n",
+ drv_cmd->retry_count);
+ goto out_failed;
+ }
+ mrioc->pel_newest_seqnum = le32_to_cpu(pel_seqnum_virt->newest) + 1;
+ drv_cmd->retry_count = 0;
+ mpi3mr_pel_wait_post(mrioc, drv_cmd);
+
+ return;
+out_failed:
+ mrioc->pel_enabled = false;
+cleanup_drv_cmd:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->retry_count = 0;
+}
+
+/**
+ * mpi3mr_check_op_admin_proc -
+ * @mrioc: Adapter instance reference
+ *
+ * Check if any of the operation reply queues
+ * or the admin reply queue are currently in use.
+ * If any queue is in use, this function waits for
+ * a maximum of 10 seconds for them to become available.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_check_op_admin_proc(struct mpi3mr_ioc *mrioc)
+{
+
+ u16 timeout = 10 * 10;
+ u16 elapsed_time = 0;
+ bool op_admin_in_use = false;
+
+ do {
+ op_admin_in_use = false;
+
+ /* Check admin_reply queue first to exit early */
+ if (atomic_read(&mrioc->admin_reply_q_in_use) == 1)
+ op_admin_in_use = true;
+ else {
+ /* Check op_reply queues */
+ int i;
+
+ for (i = 0; i < mrioc->num_queues; i++) {
+ if (atomic_read(&mrioc->op_reply_qinfo[i].in_use) == 1) {
+ op_admin_in_use = true;
+ break;
+ }
+ }
+ }
+
+ if (!op_admin_in_use)
+ break;
+
+ msleep(100);
+
+ } while (++elapsed_time < timeout);
+
+ if (op_admin_in_use)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * mpi3mr_soft_reset_handler - Reset the controller
+ * @mrioc: Adapter instance reference
+ * @reset_reason: Reset reason code
+ * @snapdump: Flag to generate snapdump in firmware or not
+ *
+ * This is an handler for recovering controller by issuing soft
+ * reset are diag fault reset. This is a blocking function and
+ * when one reset is executed if any other resets they will be
+ * blocked. All BSG requests will be blocked during the reset. If
+ * controller reset is successful then the controller will be
+ * reinitalized, otherwise the controller will be marked as not
+ * recoverable
+ *
+ * In snapdump bit is set, the controller is issued with diag
+ * fault reset so that the firmware can create a snap dump and
+ * post that the firmware will result in F000 fault and the
+ * driver will issue soft reset to recover from that.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
+ u16 reset_reason, u8 snapdump)
+{
+ int retval = 0, i;
+ unsigned long flags;
+ u32 host_diagnostic, timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10;
+ union mpi3mr_trigger_data trigger_data;
+
+ /* Block the reset handler until diag save in progress*/
+ dprint_reset(mrioc,
+ "soft_reset_handler: check and block on diagsave_timeout(%d)\n",
+ mrioc->diagsave_timeout);
+ while (mrioc->diagsave_timeout)
+ ssleep(1);
+ /*
+ * Block new resets until the currently executing one is finished and
+ * return the status of the existing reset for all blocked resets
+ */
+ dprint_reset(mrioc, "soft_reset_handler: acquiring reset_mutex\n");
+ if (!mutex_trylock(&mrioc->reset_mutex)) {
+ ioc_info(mrioc,
+ "controller reset triggered by %s is blocked due to another reset in progress\n",
+ mpi3mr_reset_rc_name(reset_reason));
+ do {
+ ssleep(1);
+ } while (mrioc->reset_in_progress == 1);
+ ioc_info(mrioc,
+ "returning previous reset result(%d) for the reset triggered by %s\n",
+ mrioc->prev_reset_result,
+ mpi3mr_reset_rc_name(reset_reason));
+ return mrioc->prev_reset_result;
+ }
+ ioc_info(mrioc, "controller reset is triggered by %s\n",
+ mpi3mr_reset_rc_name(reset_reason));
+
+ mrioc->device_refresh_on = 0;
+ scsi_block_requests(mrioc->shost);
+ mrioc->reset_in_progress = 1;
+ mrioc->stop_bsgs = 1;
+ mrioc->prev_reset_result = -1;
+ memset(&trigger_data, 0, sizeof(trigger_data));
+
+ if ((!snapdump) && (reset_reason != MPI3MR_RESET_FROM_FAULT_WATCH) &&
+ (reset_reason != MPI3MR_RESET_FROM_FIRMWARE) &&
+ (reset_reason != MPI3MR_RESET_FROM_CIACTIV_FAULT)) {
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET, NULL, 0);
+ dprint_reset(mrioc,
+ "soft_reset_handler: releasing host diagnostic buffers\n");
+ mpi3mr_release_diag_bufs(mrioc, 0);
+ for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ mrioc->event_masks[i] = -1;
+
+ dprint_reset(mrioc, "soft_reset_handler: masking events\n");
+ mpi3mr_issue_event_notification(mrioc);
+ }
+
+ mpi3mr_wait_for_host_io(mrioc, MPI3MR_RESET_HOST_IOWAIT_TIMEOUT);
+
+ mpi3mr_ioc_disable_intr(mrioc);
+ mrioc->io_admin_reset_sync = 1;
+
+ if (snapdump) {
+ mpi3mr_set_diagsave(mrioc);
+ retval = mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason);
+ if (!retval) {
+ trigger_data.fault = (readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK);
+ do {
+ host_diagnostic =
+ readl(&mrioc->sysif_regs->host_diagnostic);
+ if (!(host_diagnostic &
+ MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS))
+ break;
+ msleep(100);
+ } while (--timeout);
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FAULT, &trigger_data, 0);
+ }
+ }
+
+ retval = mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, reset_reason);
+ if (retval) {
+ ioc_err(mrioc, "Failed to issue soft reset to the ioc\n");
+ goto out;
+ }
+
+ retval = mpi3mr_check_op_admin_proc(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Soft reset failed due to an Admin or I/O queue polling\n"
+ "thread still processing replies even after a 10 second\n"
+ "timeout. Marking the controller as unrecoverable!\n");
+
+ goto out;
+ }
+
+ if (mrioc->num_io_throttle_group !=
+ mrioc->facts.max_io_throttle_group) {
+ ioc_err(mrioc,
+ "max io throttle group doesn't match old(%d), new(%d)\n",
+ mrioc->num_io_throttle_group,
+ mrioc->facts.max_io_throttle_group);
+ retval = -EPERM;
+ goto out;
+ }
+
+ mpi3mr_flush_delayed_cmd_lists(mrioc);
+ mpi3mr_flush_drv_cmds(mrioc);
+ bitmap_clear(mrioc->devrem_bitmap, 0, MPI3MR_NUM_DEVRMCMD);
+ bitmap_clear(mrioc->removepend_bitmap, 0,
+ mrioc->dev_handle_bitmap_bits);
+ bitmap_clear(mrioc->evtack_cmds_bitmap, 0, MPI3MR_NUM_EVTACKCMD);
+ mpi3mr_flush_host_io(mrioc);
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ mpi3mr_invalidate_devhandles(mrioc);
+ mpi3mr_free_enclosure_list(mrioc);
+
+ if (mrioc->prepare_for_reset) {
+ mrioc->prepare_for_reset = 0;
+ mrioc->prepare_for_reset_timeout_counter = 0;
+ }
+ mpi3mr_memset_buffers(mrioc);
+ mpi3mr_release_diag_bufs(mrioc, 1);
+ mrioc->fw_release_trigger_active = false;
+ mrioc->trace_release_trigger_active = false;
+ mrioc->snapdump_trigger_active = false;
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET, NULL, 0);
+
+ dprint_reset(mrioc,
+ "soft_reset_handler: reinitializing the controller\n");
+ retval = mpi3mr_reinit_ioc(mrioc, 0);
+ if (retval) {
+ pr_err(IOCNAME "reinit after soft reset failed: reason %d\n",
+ mrioc->name, reset_reason);
+ goto out;
+ }
+ ssleep(MPI3MR_RESET_TOPOLOGY_SETTLE_TIME);
+
+out:
+ if (!retval) {
+ mrioc->diagsave_timeout = 0;
+ mrioc->reset_in_progress = 0;
+ scsi_unblock_requests(mrioc->shost);
+ mrioc->pel_abort_requested = 0;
+ if (mrioc->pel_enabled) {
+ mrioc->pel_cmds.retry_count = 0;
+ mpi3mr_pel_wait_post(mrioc, &mrioc->pel_cmds);
+ }
+
+ mrioc->device_refresh_on = 0;
+
+ mrioc->ts_update_counter = 0;
+ spin_lock_irqsave(&mrioc->watchdog_lock, flags);
+ if (mrioc->watchdog_work_q)
+ queue_delayed_work(mrioc->watchdog_work_q,
+ &mrioc->watchdog_work,
+ msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL));
+ spin_unlock_irqrestore(&mrioc->watchdog_lock, flags);
+ mrioc->stop_bsgs = 0;
+ if (mrioc->pel_enabled)
+ atomic64_inc(&event_counter);
+ } else {
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason);
+ mrioc->device_refresh_on = 0;
+ mrioc->unrecoverable = 1;
+ mrioc->reset_in_progress = 0;
+ scsi_unblock_requests(mrioc->shost);
+ mrioc->stop_bsgs = 0;
+ retval = -1;
+ mpi3mr_flush_cmds_for_unrecovered_controller(mrioc);
+ }
+ mrioc->prev_reset_result = retval;
+ mutex_unlock(&mrioc->reset_mutex);
+ ioc_info(mrioc, "controller reset is %s\n",
+ ((retval == 0) ? "successful" : "failed"));
+ return retval;
+}
+
+/**
+ * mpi3mr_post_cfg_req - Issue config requests and wait
+ * @mrioc: Adapter instance reference
+ * @cfg_req: Configuration request
+ * @timeout: Timeout in seconds
+ * @ioc_status: Pointer to return ioc status
+ *
+ * A generic function for posting MPI3 configuration request to
+ * the firmware. This blocks for the completion of request for
+ * timeout seconds and if the request times out this function
+ * faults the controller with proper reason code.
+ *
+ * On successful completion of the request this function returns
+ * appropriate ioc status from the firmware back to the caller.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_post_cfg_req(struct mpi3mr_ioc *mrioc,
+ struct mpi3_config_request *cfg_req, int timeout, u16 *ioc_status)
+{
+ int retval = 0;
+
+ mutex_lock(&mrioc->cfg_cmds.mutex);
+ if (mrioc->cfg_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "sending config request failed due to command in use\n");
+ mutex_unlock(&mrioc->cfg_cmds.mutex);
+ goto out;
+ }
+ mrioc->cfg_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->cfg_cmds.is_waiting = 1;
+ mrioc->cfg_cmds.callback = NULL;
+ mrioc->cfg_cmds.ioc_status = 0;
+ mrioc->cfg_cmds.ioc_loginfo = 0;
+
+ cfg_req->host_tag = cpu_to_le16(MPI3MR_HOSTTAG_CFG_CMDS);
+ cfg_req->function = MPI3_FUNCTION_CONFIG;
+
+ init_completion(&mrioc->cfg_cmds.done);
+ dprint_cfg_info(mrioc, "posting config request\n");
+ if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
+ dprint_dump(cfg_req, sizeof(struct mpi3_config_request),
+ "mpi3_cfg_req");
+ retval = mpi3mr_admin_request_post(mrioc, cfg_req, sizeof(*cfg_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "posting config request failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->cfg_cmds.done, (timeout * HZ));
+ if (!(mrioc->cfg_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT);
+ ioc_err(mrioc, "config request timed out\n");
+ retval = -1;
+ goto out_unlock;
+ }
+ *ioc_status = mrioc->cfg_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
+ if ((*ioc_status) != MPI3_IOCSTATUS_SUCCESS)
+ dprint_cfg_err(mrioc,
+ "cfg_page request returned with ioc_status(0x%04x), log_info(0x%08x)\n",
+ *ioc_status, mrioc->cfg_cmds.ioc_loginfo);
+
+out_unlock:
+ mrioc->cfg_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->cfg_cmds.mutex);
+
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_process_cfg_req - config page request processor
+ * @mrioc: Adapter instance reference
+ * @cfg_req: Configuration request
+ * @cfg_hdr: Configuration page header
+ * @timeout: Timeout in seconds
+ * @ioc_status: Pointer to return ioc status
+ * @cfg_buf: Memory pointer to copy config page or header
+ * @cfg_buf_sz: Size of the memory to get config page or header
+ *
+ * This is handler for config page read, write and config page
+ * header read operations.
+ *
+ * This function expects the cfg_req to be populated with page
+ * type, page number, action for the header read and with page
+ * address for all other operations.
+ *
+ * The cfg_hdr can be passed as null for reading required header
+ * details for read/write pages the cfg_hdr should point valid
+ * configuration page header.
+ *
+ * This allocates dmaable memory based on the size of the config
+ * buffer and set the SGE of the cfg_req.
+ *
+ * For write actions, the config page data has to be passed in
+ * the cfg_buf and size of the data has to be mentioned in the
+ * cfg_buf_sz.
+ *
+ * For read/header actions, on successful completion of the
+ * request with successful ioc_status the data will be copied
+ * into the cfg_buf limited to a minimum of actual page size and
+ * cfg_buf_sz
+ *
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_process_cfg_req(struct mpi3mr_ioc *mrioc,
+ struct mpi3_config_request *cfg_req,
+ struct mpi3_config_page_header *cfg_hdr, int timeout, u16 *ioc_status,
+ void *cfg_buf, u32 cfg_buf_sz)
+{
+ struct dma_memory_desc mem_desc;
+ int retval = -1;
+ u8 invalid_action = 0;
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+
+ memset(&mem_desc, 0, sizeof(struct dma_memory_desc));
+
+ if (cfg_req->action == MPI3_CONFIG_ACTION_PAGE_HEADER)
+ mem_desc.size = sizeof(struct mpi3_config_page_header);
+ else {
+ if (!cfg_hdr) {
+ ioc_err(mrioc, "null config header passed for config action(%d), page_type(0x%02x), page_num(%d)\n",
+ cfg_req->action, cfg_req->page_type,
+ cfg_req->page_number);
+ goto out;
+ }
+ switch (cfg_hdr->page_attribute & MPI3_CONFIG_PAGEATTR_MASK) {
+ case MPI3_CONFIG_PAGEATTR_READ_ONLY:
+ if (cfg_req->action
+ != MPI3_CONFIG_ACTION_READ_CURRENT)
+ invalid_action = 1;
+ break;
+ case MPI3_CONFIG_PAGEATTR_CHANGEABLE:
+ if ((cfg_req->action ==
+ MPI3_CONFIG_ACTION_READ_PERSISTENT) ||
+ (cfg_req->action ==
+ MPI3_CONFIG_ACTION_WRITE_PERSISTENT))
+ invalid_action = 1;
+ break;
+ case MPI3_CONFIG_PAGEATTR_PERSISTENT:
+ default:
+ break;
+ }
+ if (invalid_action) {
+ ioc_err(mrioc,
+ "config action(%d) is not allowed for page_type(0x%02x), page_num(%d) with page_attribute(0x%02x)\n",
+ cfg_req->action, cfg_req->page_type,
+ cfg_req->page_number, cfg_hdr->page_attribute);
+ goto out;
+ }
+ mem_desc.size = le16_to_cpu(cfg_hdr->page_length) * 4;
+ cfg_req->page_length = cfg_hdr->page_length;
+ cfg_req->page_version = cfg_hdr->page_version;
+ }
+
+ mem_desc.addr = dma_alloc_coherent(&mrioc->pdev->dev,
+ mem_desc.size, &mem_desc.dma_addr, GFP_KERNEL);
+
+ if (!mem_desc.addr)
+ return retval;
+
+ mpi3mr_add_sg_single(&cfg_req->sgl, sgl_flags, mem_desc.size,
+ mem_desc.dma_addr);
+
+ if ((cfg_req->action == MPI3_CONFIG_ACTION_WRITE_PERSISTENT) ||
+ (cfg_req->action == MPI3_CONFIG_ACTION_WRITE_CURRENT)) {
+ memcpy(mem_desc.addr, cfg_buf, min_t(u16, mem_desc.size,
+ cfg_buf_sz));
+ dprint_cfg_info(mrioc, "config buffer to be written\n");
+ if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
+ dprint_dump(mem_desc.addr, mem_desc.size, "cfg_buf");
+ }
+
+ if (mpi3mr_post_cfg_req(mrioc, cfg_req, timeout, ioc_status))
+ goto out;
+
+ retval = 0;
+ if ((*ioc_status == MPI3_IOCSTATUS_SUCCESS) &&
+ (cfg_req->action != MPI3_CONFIG_ACTION_WRITE_PERSISTENT) &&
+ (cfg_req->action != MPI3_CONFIG_ACTION_WRITE_CURRENT)) {
+ memcpy(cfg_buf, mem_desc.addr, min_t(u16, mem_desc.size,
+ cfg_buf_sz));
+ dprint_cfg_info(mrioc, "config buffer read\n");
+ if (mrioc->logging_level & MPI3_DEBUG_CFG_INFO)
+ dprint_dump(mem_desc.addr, mem_desc.size, "cfg_buf");
+ }
+
+out:
+ if (mem_desc.addr) {
+ dma_free_coherent(&mrioc->pdev->dev, mem_desc.size,
+ mem_desc.addr, mem_desc.dma_addr);
+ mem_desc.addr = NULL;
+ }
+
+ return retval;
+}
+
+/**
+ * mpi3mr_cfg_get_dev_pg0 - Read current device page0
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Pointer to return ioc status
+ * @dev_pg0: Pointer to return device page 0
+ * @pg_sz: Size of the memory allocated to the page pointer
+ * @form: The form to be used for addressing the page
+ * @form_spec: Form specific information like device handle
+ *
+ * This is handler for config page read for a specific device
+ * page0. The ioc_status has the controller returned ioc_status.
+ * This routine doesn't check ioc_status to decide whether the
+ * page read is success or not and it is the callers
+ * responsibility.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_dev_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_device_page0 *dev_pg0, u16 pg_sz, u32 form, u32 form_spec)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u32 page_address;
+
+ memset(dev_pg0, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_DEVICE;
+ cfg_req.page_number = 0;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "device page0 header read failed\n");
+ goto out_failed;
+ }
+ if (*ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "device page0 header read failed with ioc_status(0x%04x)\n",
+ *ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ page_address = ((form & MPI3_DEVICE_PGAD_FORM_MASK) |
+ (form_spec & MPI3_DEVICE_PGAD_HANDLE_MASK));
+ cfg_req.page_address = cpu_to_le32(page_address);
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, dev_pg0, pg_sz)) {
+ ioc_err(mrioc, "device page0 read failed\n");
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+
+/**
+ * mpi3mr_cfg_get_sas_phy_pg0 - Read current SAS Phy page0
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Pointer to return ioc status
+ * @phy_pg0: Pointer to return SAS Phy page 0
+ * @pg_sz: Size of the memory allocated to the page pointer
+ * @form: The form to be used for addressing the page
+ * @form_spec: Form specific information like phy number
+ *
+ * This is handler for config page read for a specific SAS Phy
+ * page0. The ioc_status has the controller returned ioc_status.
+ * This routine doesn't check ioc_status to decide whether the
+ * page read is success or not and it is the callers
+ * responsibility.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_sas_phy_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_phy_page0 *phy_pg0, u16 pg_sz, u32 form,
+ u32 form_spec)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u32 page_address;
+
+ memset(phy_pg0, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_SAS_PHY;
+ cfg_req.page_number = 0;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "sas phy page0 header read failed\n");
+ goto out_failed;
+ }
+ if (*ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas phy page0 header read failed with ioc_status(0x%04x)\n",
+ *ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ page_address = ((form & MPI3_SAS_PHY_PGAD_FORM_MASK) |
+ (form_spec & MPI3_SAS_PHY_PGAD_PHY_NUMBER_MASK));
+ cfg_req.page_address = cpu_to_le32(page_address);
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, phy_pg0, pg_sz)) {
+ ioc_err(mrioc, "sas phy page0 read failed\n");
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+/**
+ * mpi3mr_cfg_get_sas_phy_pg1 - Read current SAS Phy page1
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Pointer to return ioc status
+ * @phy_pg1: Pointer to return SAS Phy page 1
+ * @pg_sz: Size of the memory allocated to the page pointer
+ * @form: The form to be used for addressing the page
+ * @form_spec: Form specific information like phy number
+ *
+ * This is handler for config page read for a specific SAS Phy
+ * page1. The ioc_status has the controller returned ioc_status.
+ * This routine doesn't check ioc_status to decide whether the
+ * page read is success or not and it is the callers
+ * responsibility.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_sas_phy_pg1(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_phy_page1 *phy_pg1, u16 pg_sz, u32 form,
+ u32 form_spec)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u32 page_address;
+
+ memset(phy_pg1, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_SAS_PHY;
+ cfg_req.page_number = 1;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "sas phy page1 header read failed\n");
+ goto out_failed;
+ }
+ if (*ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas phy page1 header read failed with ioc_status(0x%04x)\n",
+ *ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ page_address = ((form & MPI3_SAS_PHY_PGAD_FORM_MASK) |
+ (form_spec & MPI3_SAS_PHY_PGAD_PHY_NUMBER_MASK));
+ cfg_req.page_address = cpu_to_le32(page_address);
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, phy_pg1, pg_sz)) {
+ ioc_err(mrioc, "sas phy page1 read failed\n");
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+
+/**
+ * mpi3mr_cfg_get_sas_exp_pg0 - Read current SAS Expander page0
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Pointer to return ioc status
+ * @exp_pg0: Pointer to return SAS Expander page 0
+ * @pg_sz: Size of the memory allocated to the page pointer
+ * @form: The form to be used for addressing the page
+ * @form_spec: Form specific information like device handle
+ *
+ * This is handler for config page read for a specific SAS
+ * Expander page0. The ioc_status has the controller returned
+ * ioc_status. This routine doesn't check ioc_status to decide
+ * whether the page read is success or not and it is the callers
+ * responsibility.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_sas_exp_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_expander_page0 *exp_pg0, u16 pg_sz, u32 form,
+ u32 form_spec)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u32 page_address;
+
+ memset(exp_pg0, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_SAS_EXPANDER;
+ cfg_req.page_number = 0;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "expander page0 header read failed\n");
+ goto out_failed;
+ }
+ if (*ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "expander page0 header read failed with ioc_status(0x%04x)\n",
+ *ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ page_address = ((form & MPI3_SAS_EXPAND_PGAD_FORM_MASK) |
+ (form_spec & (MPI3_SAS_EXPAND_PGAD_PHYNUM_MASK |
+ MPI3_SAS_EXPAND_PGAD_HANDLE_MASK)));
+ cfg_req.page_address = cpu_to_le32(page_address);
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, exp_pg0, pg_sz)) {
+ ioc_err(mrioc, "expander page0 read failed\n");
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+/**
+ * mpi3mr_cfg_get_sas_exp_pg1 - Read current SAS Expander page1
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Pointer to return ioc status
+ * @exp_pg1: Pointer to return SAS Expander page 1
+ * @pg_sz: Size of the memory allocated to the page pointer
+ * @form: The form to be used for addressing the page
+ * @form_spec: Form specific information like phy number
+ *
+ * This is handler for config page read for a specific SAS
+ * Expander page1. The ioc_status has the controller returned
+ * ioc_status. This routine doesn't check ioc_status to decide
+ * whether the page read is success or not and it is the callers
+ * responsibility.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_sas_exp_pg1(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_sas_expander_page1 *exp_pg1, u16 pg_sz, u32 form,
+ u32 form_spec)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u32 page_address;
+
+ memset(exp_pg1, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_SAS_EXPANDER;
+ cfg_req.page_number = 1;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "expander page1 header read failed\n");
+ goto out_failed;
+ }
+ if (*ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "expander page1 header read failed with ioc_status(0x%04x)\n",
+ *ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ page_address = ((form & MPI3_SAS_EXPAND_PGAD_FORM_MASK) |
+ (form_spec & (MPI3_SAS_EXPAND_PGAD_PHYNUM_MASK |
+ MPI3_SAS_EXPAND_PGAD_HANDLE_MASK)));
+ cfg_req.page_address = cpu_to_le32(page_address);
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, exp_pg1, pg_sz)) {
+ ioc_err(mrioc, "expander page1 read failed\n");
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+/**
+ * mpi3mr_cfg_get_enclosure_pg0 - Read current Enclosure page0
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Pointer to return ioc status
+ * @encl_pg0: Pointer to return Enclosure page 0
+ * @pg_sz: Size of the memory allocated to the page pointer
+ * @form: The form to be used for addressing the page
+ * @form_spec: Form specific information like device handle
+ *
+ * This is handler for config page read for a specific Enclosure
+ * page0. The ioc_status has the controller returned ioc_status.
+ * This routine doesn't check ioc_status to decide whether the
+ * page read is success or not and it is the callers
+ * responsibility.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_enclosure_pg0(struct mpi3mr_ioc *mrioc, u16 *ioc_status,
+ struct mpi3_enclosure_page0 *encl_pg0, u16 pg_sz, u32 form,
+ u32 form_spec)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u32 page_address;
+
+ memset(encl_pg0, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_ENCLOSURE;
+ cfg_req.page_number = 0;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "enclosure page0 header read failed\n");
+ goto out_failed;
+ }
+ if (*ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "enclosure page0 header read failed with ioc_status(0x%04x)\n",
+ *ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ page_address = ((form & MPI3_ENCLOS_PGAD_FORM_MASK) |
+ (form_spec & MPI3_ENCLOS_PGAD_HANDLE_MASK));
+ cfg_req.page_address = cpu_to_le32(page_address);
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, ioc_status, encl_pg0, pg_sz)) {
+ ioc_err(mrioc, "enclosure page0 read failed\n");
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+
+/**
+ * mpi3mr_cfg_get_sas_io_unit_pg0 - Read current SASIOUnit page0
+ * @mrioc: Adapter instance reference
+ * @sas_io_unit_pg0: Pointer to return SAS IO Unit page 0
+ * @pg_sz: Size of the memory allocated to the page pointer
+ *
+ * This is handler for config page read for the SAS IO Unit
+ * page0. This routine checks ioc_status to decide whether the
+ * page read is success or not.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_sas_io_unit_pg0(struct mpi3mr_ioc *mrioc,
+ struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0, u16 pg_sz)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u16 ioc_status = 0;
+
+ memset(sas_io_unit_pg0, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_SAS_IO_UNIT;
+ cfg_req.page_number = 0;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "sas io unit page0 header read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas io unit page0 header read failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, sas_io_unit_pg0, pg_sz)) {
+ ioc_err(mrioc, "sas io unit page0 read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas io unit page0 read failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+/**
+ * mpi3mr_cfg_get_sas_io_unit_pg1 - Read current SASIOUnit page1
+ * @mrioc: Adapter instance reference
+ * @sas_io_unit_pg1: Pointer to return SAS IO Unit page 1
+ * @pg_sz: Size of the memory allocated to the page pointer
+ *
+ * This is handler for config page read for the SAS IO Unit
+ * page1. This routine checks ioc_status to decide whether the
+ * page read is success or not.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_sas_io_unit_pg1(struct mpi3mr_ioc *mrioc,
+ struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1, u16 pg_sz)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u16 ioc_status = 0;
+
+ memset(sas_io_unit_pg1, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_SAS_IO_UNIT;
+ cfg_req.page_number = 1;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "sas io unit page1 header read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas io unit page1 header read failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, sas_io_unit_pg1, pg_sz)) {
+ ioc_err(mrioc, "sas io unit page1 read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas io unit page1 read failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+/**
+ * mpi3mr_cfg_set_sas_io_unit_pg1 - Write SASIOUnit page1
+ * @mrioc: Adapter instance reference
+ * @sas_io_unit_pg1: Pointer to the SAS IO Unit page 1 to write
+ * @pg_sz: Size of the memory allocated to the page pointer
+ *
+ * This is handler for config page write for the SAS IO Unit
+ * page1. This routine checks ioc_status to decide whether the
+ * page read is success or not. This will modify both current
+ * and persistent page.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_set_sas_io_unit_pg1(struct mpi3mr_ioc *mrioc,
+ struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1, u16 pg_sz)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u16 ioc_status = 0;
+
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_SAS_IO_UNIT;
+ cfg_req.page_number = 1;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "sas io unit page1 header read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas io unit page1 header read failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_WRITE_CURRENT;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, sas_io_unit_pg1, pg_sz)) {
+ ioc_err(mrioc, "sas io unit page1 write current failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas io unit page1 write current failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+
+ cfg_req.action = MPI3_CONFIG_ACTION_WRITE_PERSISTENT;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, sas_io_unit_pg1, pg_sz)) {
+ ioc_err(mrioc, "sas io unit page1 write persistent failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "sas io unit page1 write persistent failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+/**
+ * mpi3mr_cfg_get_driver_pg1 - Read current Driver page1
+ * @mrioc: Adapter instance reference
+ * @driver_pg1: Pointer to return Driver page 1
+ * @pg_sz: Size of the memory allocated to the page pointer
+ *
+ * This is handler for config page read for the Driver page1.
+ * This routine checks ioc_status to decide whether the page
+ * read is success or not.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_driver_pg1(struct mpi3mr_ioc *mrioc,
+ struct mpi3_driver_page1 *driver_pg1, u16 pg_sz)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u16 ioc_status = 0;
+
+ memset(driver_pg1, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_DRIVER;
+ cfg_req.page_number = 1;
+ cfg_req.page_address = 0;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "driver page1 header read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "driver page1 header read failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = MPI3_CONFIG_ACTION_READ_CURRENT;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, driver_pg1, pg_sz)) {
+ ioc_err(mrioc, "driver page1 read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "driver page1 read failed with ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
+/**
+ * mpi3mr_cfg_get_driver_pg2 - Read current driver page2
+ * @mrioc: Adapter instance reference
+ * @driver_pg2: Pointer to return driver page 2
+ * @pg_sz: Size of the memory allocated to the page pointer
+ * @page_action: Page action
+ *
+ * This is handler for config page read for the driver page2.
+ * This routine checks ioc_status to decide whether the page
+ * read is success or not.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_cfg_get_driver_pg2(struct mpi3mr_ioc *mrioc,
+ struct mpi3_driver_page2 *driver_pg2, u16 pg_sz, u8 page_action)
+{
+ struct mpi3_config_page_header cfg_hdr;
+ struct mpi3_config_request cfg_req;
+ u16 ioc_status = 0;
+
+ memset(driver_pg2, 0, pg_sz);
+ memset(&cfg_hdr, 0, sizeof(cfg_hdr));
+ memset(&cfg_req, 0, sizeof(cfg_req));
+
+ cfg_req.function = MPI3_FUNCTION_CONFIG;
+ cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
+ cfg_req.page_type = MPI3_CONFIG_PAGETYPE_DRIVER;
+ cfg_req.page_number = 2;
+ cfg_req.page_address = 0;
+ cfg_req.page_version = MPI3_DRIVER2_PAGEVERSION;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
+ ioc_err(mrioc, "driver page2 header read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "driver page2 header read failed with\n"
+ "ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ cfg_req.action = page_action;
+
+ if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, driver_pg2, pg_sz)) {
+ ioc_err(mrioc, "driver page2 read failed\n");
+ goto out_failed;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "driver page2 read failed with\n"
+ "ioc_status(0x%04x)\n",
+ ioc_status);
+ goto out_failed;
+ }
+ return 0;
+out_failed:
+ return -1;
+}
+
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
new file mode 100644
index 000000000000..b88633e1efe2
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -0,0 +1,6048 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2023 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#include "mpi3mr.h"
+#include <linux/idr.h>
+
+/* global driver scop variables */
+LIST_HEAD(mrioc_list);
+DEFINE_SPINLOCK(mrioc_list_lock);
+static DEFINE_IDA(mrioc_ida);
+static int warn_non_secure_ctlr;
+atomic64_t event_counter;
+
+MODULE_AUTHOR(MPI3MR_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(MPI3MR_DRIVER_DESC);
+MODULE_LICENSE(MPI3MR_DRIVER_LICENSE);
+MODULE_VERSION(MPI3MR_DRIVER_VERSION);
+
+/* Module parameters*/
+int prot_mask = -1;
+module_param(prot_mask, int, 0);
+MODULE_PARM_DESC(prot_mask, "Host protection capabilities mask, def=0x07");
+
+static int prot_guard_mask = 3;
+module_param(prot_guard_mask, int, 0);
+MODULE_PARM_DESC(prot_guard_mask, " Host protection guard mask, def=3");
+static int logging_level;
+module_param(logging_level, int, 0);
+MODULE_PARM_DESC(logging_level,
+ " bits for enabling additional logging info (default=0)");
+static int max_sgl_entries = MPI3MR_DEFAULT_SGL_ENTRIES;
+module_param(max_sgl_entries, int, 0444);
+MODULE_PARM_DESC(max_sgl_entries,
+ "Preferred max number of SG entries to be used for a single I/O\n"
+ "The actual value will be determined by the driver\n"
+ "(Minimum=256, Maximum=2048, default=256)");
+
+/* Forward declarations*/
+static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
+ struct mpi3mr_drv_cmd *cmdparam, u32 event_ctx);
+
+#define MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION (0xFFFF)
+
+#define MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH (0xFFFE)
+
+/*
+ * SAS Log info code for a NCQ collateral abort after an NCQ error:
+ * IOC_LOGINFO_PREFIX_PL | PL_LOGINFO_CODE_SATA_NCQ_FAIL_ALL_CMDS_AFTR_ERR
+ * See: drivers/message/fusion/lsi/mpi_log_sas.h
+ */
+#define IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR 0x31080000
+
+/**
+ * mpi3mr_host_tag_for_scmd - Get host tag for a scmd
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ *
+ * Calculate the host tag based on block tag for a given scmd.
+ *
+ * Return: Valid host tag or MPI3MR_HOSTTAG_INVALID.
+ */
+static u16 mpi3mr_host_tag_for_scmd(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd)
+{
+ struct scmd_priv *priv = NULL;
+ u32 unique_tag;
+ u16 host_tag, hw_queue;
+
+ unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+
+ hw_queue = blk_mq_unique_tag_to_hwq(unique_tag);
+ if (hw_queue >= mrioc->num_op_reply_q)
+ return MPI3MR_HOSTTAG_INVALID;
+ host_tag = blk_mq_unique_tag_to_tag(unique_tag);
+
+ if (WARN_ON(host_tag >= mrioc->max_host_ios))
+ return MPI3MR_HOSTTAG_INVALID;
+
+ priv = scsi_cmd_priv(scmd);
+ /*host_tag 0 is invalid hence incrementing by 1*/
+ priv->host_tag = host_tag + 1;
+ priv->scmd = scmd;
+ priv->in_lld_scope = 1;
+ priv->req_q_idx = hw_queue;
+ priv->meta_chain_idx = -1;
+ priv->chain_idx = -1;
+ priv->meta_sg_valid = 0;
+ return priv->host_tag;
+}
+
+/**
+ * mpi3mr_scmd_from_host_tag - Get SCSI command from host tag
+ * @mrioc: Adapter instance reference
+ * @host_tag: Host tag
+ * @qidx: Operational queue index
+ *
+ * Identify the block tag from the host tag and queue index and
+ * retrieve associated scsi command using scsi_host_find_tag().
+ *
+ * Return: SCSI command reference or NULL.
+ */
+static struct scsi_cmnd *mpi3mr_scmd_from_host_tag(
+ struct mpi3mr_ioc *mrioc, u16 host_tag, u16 qidx)
+{
+ struct scsi_cmnd *scmd = NULL;
+ struct scmd_priv *priv = NULL;
+ u32 unique_tag = host_tag - 1;
+
+ if (WARN_ON(host_tag > mrioc->max_host_ios))
+ goto out;
+
+ unique_tag |= (qidx << BLK_MQ_UNIQUE_TAG_BITS);
+
+ scmd = scsi_host_find_tag(mrioc->shost, unique_tag);
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ scmd = NULL;
+ }
+out:
+ return scmd;
+}
+
+/**
+ * mpi3mr_clear_scmd_priv - Cleanup SCSI command private date
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ *
+ * Invalidate the SCSI command private data to mark the command
+ * is not in LLD scope anymore.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_clear_scmd_priv(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd)
+{
+ struct scmd_priv *priv = NULL;
+
+ priv = scsi_cmd_priv(scmd);
+
+ if (WARN_ON(priv->in_lld_scope == 0))
+ return;
+ priv->host_tag = MPI3MR_HOSTTAG_INVALID;
+ priv->req_q_idx = 0xFFFF;
+ priv->scmd = NULL;
+ priv->in_lld_scope = 0;
+ priv->meta_sg_valid = 0;
+ if (priv->chain_idx >= 0) {
+ clear_bit(priv->chain_idx, mrioc->chain_bitmap);
+ priv->chain_idx = -1;
+ }
+ if (priv->meta_chain_idx >= 0) {
+ clear_bit(priv->meta_chain_idx, mrioc->chain_bitmap);
+ priv->meta_chain_idx = -1;
+ }
+}
+
+static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle,
+ struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc);
+static void mpi3mr_fwevt_worker(struct work_struct *work);
+
+/**
+ * mpi3mr_fwevt_free - firmware event memory dealloctor
+ * @r: k reference pointer of the firmware event
+ *
+ * Free firmware event memory when no reference.
+ */
+static void mpi3mr_fwevt_free(struct kref *r)
+{
+ kfree(container_of(r, struct mpi3mr_fwevt, ref_count));
+}
+
+/**
+ * mpi3mr_fwevt_get - k reference incrementor
+ * @fwevt: Firmware event reference
+ *
+ * Increment firmware event reference count.
+ */
+static void mpi3mr_fwevt_get(struct mpi3mr_fwevt *fwevt)
+{
+ kref_get(&fwevt->ref_count);
+}
+
+/**
+ * mpi3mr_fwevt_put - k reference decrementor
+ * @fwevt: Firmware event reference
+ *
+ * decrement firmware event reference count.
+ */
+static void mpi3mr_fwevt_put(struct mpi3mr_fwevt *fwevt)
+{
+ kref_put(&fwevt->ref_count, mpi3mr_fwevt_free);
+}
+
+/**
+ * mpi3mr_alloc_fwevt - Allocate firmware event
+ * @len: length of firmware event data to allocate
+ *
+ * Allocate firmware event with required length and initialize
+ * the reference counter.
+ *
+ * Return: firmware event reference.
+ */
+static struct mpi3mr_fwevt *mpi3mr_alloc_fwevt(int len)
+{
+ struct mpi3mr_fwevt *fwevt;
+
+ fwevt = kzalloc(sizeof(*fwevt) + len, GFP_ATOMIC);
+ if (!fwevt)
+ return NULL;
+
+ kref_init(&fwevt->ref_count);
+ return fwevt;
+}
+
+/**
+ * mpi3mr_fwevt_add_to_list - Add firmware event to the list
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Add the given firmware event to the firmware event list.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_add_to_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ unsigned long flags;
+
+ if (!mrioc->fwevt_worker_thread)
+ return;
+
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ /* get fwevt reference count while adding it to fwevt_list */
+ mpi3mr_fwevt_get(fwevt);
+ INIT_LIST_HEAD(&fwevt->list);
+ list_add_tail(&fwevt->list, &mrioc->fwevt_list);
+ INIT_WORK(&fwevt->work, mpi3mr_fwevt_worker);
+ /* get fwevt reference count while enqueueing it to worker queue */
+ mpi3mr_fwevt_get(fwevt);
+ queue_work(mrioc->fwevt_worker_thread, &fwevt->work);
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+}
+
+/**
+ * mpi3mr_hdb_trigger_data_event - Add hdb trigger data event to
+ * the list
+ * @mrioc: Adapter instance reference
+ * @event_data: Event data
+ *
+ * Add the given hdb trigger data event to the firmware event
+ * list.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc,
+ struct trigger_event_data *event_data)
+{
+ struct mpi3mr_fwevt *fwevt;
+ u16 sz = sizeof(*event_data);
+
+ fwevt = mpi3mr_alloc_fwevt(sz);
+ if (!fwevt) {
+ ioc_warn(mrioc, "failed to queue hdb trigger data event\n");
+ return;
+ }
+
+ fwevt->mrioc = mrioc;
+ fwevt->event_id = MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER;
+ fwevt->send_ack = 0;
+ fwevt->process_evt = 1;
+ fwevt->evt_ctx = 0;
+ fwevt->event_data_size = sz;
+ memcpy(fwevt->event_data, event_data, sz);
+
+ mpi3mr_fwevt_add_to_list(mrioc, fwevt);
+}
+
+/**
+ * mpi3mr_fwevt_del_from_list - Delete firmware event from list
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Delete the given firmware event from the firmware event list.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_del_from_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (!list_empty(&fwevt->list)) {
+ list_del_init(&fwevt->list);
+ /*
+ * Put fwevt reference count after
+ * removing it from fwevt_list
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+}
+
+/**
+ * mpi3mr_dequeue_fwevt - Dequeue firmware event from the list
+ * @mrioc: Adapter instance reference
+ *
+ * Dequeue a firmware event from the firmware event list.
+ *
+ * Return: firmware event.
+ */
+static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt(
+ struct mpi3mr_ioc *mrioc)
+{
+ unsigned long flags;
+ struct mpi3mr_fwevt *fwevt = NULL;
+
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (!list_empty(&mrioc->fwevt_list)) {
+ fwevt = list_first_entry(&mrioc->fwevt_list,
+ struct mpi3mr_fwevt, list);
+ list_del_init(&fwevt->list);
+ /*
+ * Put fwevt reference count after
+ * removing it from fwevt_list
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+
+ return fwevt;
+}
+
+/**
+ * mpi3mr_cancel_work - cancel firmware event
+ * @fwevt: fwevt object which needs to be canceled
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt)
+{
+ /*
+ * Wait on the fwevt to complete. If this returns 1, then
+ * the event was never executed.
+ *
+ * If it did execute, we wait for it to finish, and the put will
+ * happen from mpi3mr_process_fwevt()
+ */
+ if (cancel_work_sync(&fwevt->work)) {
+ /*
+ * Put fwevt reference count after
+ * dequeuing it from worker queue
+ */
+ mpi3mr_fwevt_put(fwevt);
+ /*
+ * Put fwevt reference count to neutralize
+ * kref_init increment
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
+}
+
+/**
+ * mpi3mr_cleanup_fwevt_list - Cleanup firmware event list
+ * @mrioc: Adapter instance reference
+ *
+ * Flush all pending firmware events from the firmware event
+ * list.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_fwevt *fwevt = NULL;
+
+ if ((list_empty(&mrioc->fwevt_list) && !mrioc->current_event) ||
+ !mrioc->fwevt_worker_thread)
+ return;
+
+ while ((fwevt = mpi3mr_dequeue_fwevt(mrioc)))
+ mpi3mr_cancel_work(fwevt);
+
+ if (mrioc->current_event) {
+ fwevt = mrioc->current_event;
+ /*
+ * Don't call cancel_work_sync() API for the
+ * fwevt work if the controller reset is
+ * get called as part of processing the
+ * same fwevt work (or) when worker thread is
+ * waiting for device add/remove APIs to complete.
+ * Otherwise we will see deadlock.
+ */
+ if (current_work() == &fwevt->work || fwevt->pending_at_sml) {
+ fwevt->discard = 1;
+ return;
+ }
+
+ mpi3mr_cancel_work(fwevt);
+ }
+}
+
+/**
+ * mpi3mr_queue_qd_reduction_event - Queue TG QD reduction event
+ * @mrioc: Adapter instance reference
+ * @tg: Throttle group information pointer
+ *
+ * Accessor to queue on synthetically generated driver event to
+ * the event worker thread, the driver event will be used to
+ * reduce the QD of all VDs in the TG from the worker thread.
+ *
+ * Return: None.
+ */
+static void mpi3mr_queue_qd_reduction_event(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_throttle_group_info *tg)
+{
+ struct mpi3mr_fwevt *fwevt;
+ u16 sz = sizeof(struct mpi3mr_throttle_group_info *);
+
+ /*
+ * If the QD reduction event is already queued due to throttle and if
+ * the QD is not restored through device info change event
+ * then dont queue further reduction events
+ */
+ if (tg->fw_qd != tg->modified_qd)
+ return;
+
+ fwevt = mpi3mr_alloc_fwevt(sz);
+ if (!fwevt) {
+ ioc_warn(mrioc, "failed to queue TG QD reduction event\n");
+ return;
+ }
+ *(struct mpi3mr_throttle_group_info **)fwevt->event_data = tg;
+ fwevt->mrioc = mrioc;
+ fwevt->event_id = MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION;
+ fwevt->send_ack = 0;
+ fwevt->process_evt = 1;
+ fwevt->evt_ctx = 0;
+ fwevt->event_data_size = sz;
+ tg->modified_qd = max_t(u16, (tg->fw_qd * tg->qd_reduction) / 10, 8);
+
+ dprint_event_bh(mrioc, "qd reduction event queued for tg_id(%d)\n",
+ tg->id);
+ mpi3mr_fwevt_add_to_list(mrioc, fwevt);
+}
+
+/**
+ * mpi3mr_invalidate_devhandles -Invalidate device handles
+ * @mrioc: Adapter instance reference
+ *
+ * Invalidate the device handles in the target device structures
+ * . Called post reset prior to reinitializing the controller.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+ struct mpi3mr_stgt_priv_data *tgt_priv;
+
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+ tgtdev->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ tgt_priv = tgtdev->starget->hostdata;
+ tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ tgt_priv->io_throttle_enabled = 0;
+ tgt_priv->io_divert = 0;
+ tgt_priv->throttle_group = NULL;
+ tgt_priv->wslen = 0;
+ if (tgtdev->host_exposed)
+ atomic_set(&tgt_priv->block_io, 1);
+ }
+ }
+}
+
+/**
+ * mpi3mr_print_scmd - print individual SCSI command
+ * @rq: Block request
+ * @data: Adapter instance reference
+ *
+ * Print the SCSI command details if it is in LLD scope.
+ *
+ * Return: true always.
+ */
+static bool mpi3mr_print_scmd(struct request *rq, void *data)
+{
+ struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data;
+ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
+ struct scmd_priv *priv = NULL;
+
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ goto out;
+
+ ioc_info(mrioc, "%s :Host Tag = %d, qid = %d\n",
+ __func__, priv->host_tag, priv->req_q_idx + 1);
+ scsi_print_command(scmd);
+ }
+
+out:
+ return(true);
+}
+
+/**
+ * mpi3mr_flush_scmd - Flush individual SCSI command
+ * @rq: Block request
+ * @data: Adapter instance reference
+ *
+ * Return the SCSI command to the upper layers if it is in LLD
+ * scope.
+ *
+ * Return: true always.
+ */
+
+static bool mpi3mr_flush_scmd(struct request *rq, void *data)
+{
+ struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data;
+ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
+ struct scmd_priv *priv = NULL;
+
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ goto out;
+
+ if (priv->meta_sg_valid)
+ dma_unmap_sg(&mrioc->pdev->dev, scsi_prot_sglist(scmd),
+ scsi_prot_sg_count(scmd), scmd->sc_data_direction);
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ scsi_dma_unmap(scmd);
+ scmd->result = DID_RESET << 16;
+ scsi_print_command(scmd);
+ scsi_done(scmd);
+ mrioc->flush_io_count++;
+ }
+
+out:
+ return(true);
+}
+
+/**
+ * mpi3mr_count_dev_pending - Count commands pending for a lun
+ * @rq: Block request
+ * @data: SCSI device reference
+ *
+ * This is an iterator function called for each SCSI command in
+ * a host and if the command is pending in the LLD for the
+ * specific device(lun) then device specific pending I/O counter
+ * is updated in the device structure.
+ *
+ * Return: true always.
+ */
+
+static bool mpi3mr_count_dev_pending(struct request *rq, void *data)
+{
+ struct scsi_device *sdev = (struct scsi_device *)data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data = sdev->hostdata;
+ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
+ struct scmd_priv *priv;
+
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ goto out;
+ if (scmd->device == sdev)
+ sdev_priv_data->pend_count++;
+ }
+
+out:
+ return true;
+}
+
+/**
+ * mpi3mr_count_tgt_pending - Count commands pending for target
+ * @rq: Block request
+ * @data: SCSI target reference
+ *
+ * This is an iterator function called for each SCSI command in
+ * a host and if the command is pending in the LLD for the
+ * specific target then target specific pending I/O counter is
+ * updated in the target structure.
+ *
+ * Return: true always.
+ */
+
+static bool mpi3mr_count_tgt_pending(struct request *rq, void *data)
+{
+ struct scsi_target *starget = (struct scsi_target *)data;
+ struct mpi3mr_stgt_priv_data *stgt_priv_data = starget->hostdata;
+ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
+ struct scmd_priv *priv;
+
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ goto out;
+ if (scmd->device && (scsi_target(scmd->device) == starget))
+ stgt_priv_data->pend_count++;
+ }
+
+out:
+ return true;
+}
+
+/**
+ * mpi3mr_flush_host_io - Flush host I/Os
+ * @mrioc: Adapter instance reference
+ *
+ * Flush all of the pending I/Os by calling
+ * blk_mq_tagset_busy_iter() for each possible tag. This is
+ * executed post controller reset
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc)
+{
+ struct Scsi_Host *shost = mrioc->shost;
+
+ mrioc->flush_io_count = 0;
+ ioc_info(mrioc, "%s :Flushing Host I/O cmds post reset\n", __func__);
+ blk_mq_tagset_busy_iter(&shost->tag_set,
+ mpi3mr_flush_scmd, (void *)mrioc);
+ ioc_info(mrioc, "%s :Flushed %d Host I/O cmds\n", __func__,
+ mrioc->flush_io_count);
+}
+
+/**
+ * mpi3mr_flush_cmds_for_unrecovered_controller - Flush all pending cmds
+ * @mrioc: Adapter instance reference
+ *
+ * This function waits for currently running IO poll threads to
+ * exit and then flushes all host I/Os and any internal pending
+ * cmds. This is executed after controller is marked as
+ * unrecoverable.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_flush_cmds_for_unrecovered_controller(struct mpi3mr_ioc *mrioc)
+{
+ struct Scsi_Host *shost = mrioc->shost;
+ int i;
+
+ if (!mrioc->unrecoverable)
+ return;
+
+ if (mrioc->op_reply_qinfo) {
+ for (i = 0; i < mrioc->num_queues; i++) {
+ while (atomic_read(&mrioc->op_reply_qinfo[i].in_use))
+ udelay(500);
+ atomic_set(&mrioc->op_reply_qinfo[i].pend_ios, 0);
+ }
+ }
+ mrioc->flush_io_count = 0;
+ blk_mq_tagset_busy_iter(&shost->tag_set,
+ mpi3mr_flush_scmd, (void *)mrioc);
+ mpi3mr_flush_delayed_cmd_lists(mrioc);
+ mpi3mr_flush_drv_cmds(mrioc);
+}
+
+/**
+ * mpi3mr_alloc_tgtdev - target device allocator
+ *
+ * Allocate target device instance and initialize the reference
+ * count
+ *
+ * Return: target device instance.
+ */
+static struct mpi3mr_tgt_dev *mpi3mr_alloc_tgtdev(void)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ tgtdev = kzalloc(sizeof(*tgtdev), GFP_ATOMIC);
+ if (!tgtdev)
+ return NULL;
+ kref_init(&tgtdev->ref_count);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_tgtdev_add_to_list -Add tgtdevice to the list
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device
+ *
+ * Add the target device to the target device list
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_tgtdev_add_to_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ mpi3mr_tgtdev_get(tgtdev);
+ INIT_LIST_HEAD(&tgtdev->list);
+ list_add_tail(&tgtdev->list, &mrioc->tgtdev_list);
+ tgtdev->state = MPI3MR_DEV_CREATED;
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+}
+
+/**
+ * mpi3mr_tgtdev_del_from_list -Delete tgtdevice from the list
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device
+ * @must_delete: Must delete the target device from the list irrespective
+ * of the device state.
+ *
+ * Remove the target device from the target device list
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_tgtdev_del_from_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev, bool must_delete)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ if ((tgtdev->state == MPI3MR_DEV_REMOVE_HS_STARTED) || (must_delete == true)) {
+ if (!list_empty(&tgtdev->list)) {
+ list_del_init(&tgtdev->list);
+ tgtdev->state = MPI3MR_DEV_DELETED;
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+}
+
+/**
+ * __mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle
+ * @mrioc: Adapter instance reference
+ * @handle: Device handle
+ *
+ * Accessor to retrieve target device from the device handle.
+ * Non Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_handle(
+ struct mpi3mr_ioc *mrioc, u16 handle)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+ if (tgtdev->dev_handle == handle)
+ goto found_tgtdev;
+ return NULL;
+
+found_tgtdev:
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle
+ * @mrioc: Adapter instance reference
+ * @handle: Device handle
+ *
+ * Accessor to retrieve target device from the device handle.
+ * Lock version
+ *
+ * Return: Target device reference.
+ */
+struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle(
+ struct mpi3mr_ioc *mrioc, u16 handle)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ return tgtdev;
+}
+
+/**
+ * __mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persist ID
+ * @mrioc: Adapter instance reference
+ * @persist_id: Persistent ID
+ *
+ * Accessor to retrieve target device from the Persistent ID.
+ * Non Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_perst_id(
+ struct mpi3mr_ioc *mrioc, u16 persist_id)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+ if (tgtdev->perst_id == persist_id)
+ goto found_tgtdev;
+ return NULL;
+
+found_tgtdev:
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persistent ID
+ * @mrioc: Adapter instance reference
+ * @persist_id: Persistent ID
+ *
+ * Accessor to retrieve target device from the Persistent ID.
+ * Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id(
+ struct mpi3mr_ioc *mrioc, u16 persist_id)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, persist_id);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ return tgtdev;
+}
+
+/**
+ * __mpi3mr_get_tgtdev_from_tgtpriv -Get tgtdev from tgt private
+ * @mrioc: Adapter instance reference
+ * @tgt_priv: Target private data
+ *
+ * Accessor to return target device from the target private
+ * data. Non Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_from_tgtpriv(
+ struct mpi3mr_ioc *mrioc, struct mpi3mr_stgt_priv_data *tgt_priv)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+ tgtdev = tgt_priv->tgt_dev;
+ if (tgtdev)
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_set_io_divert_for_all_vd_in_tg -set divert for TG VDs
+ * @mrioc: Adapter instance reference
+ * @tg: Throttle group information pointer
+ * @divert_value: 1 or 0
+ *
+ * Accessor to set io_divert flag for each device associated
+ * with the given throttle group with the given value.
+ *
+ * Return: None.
+ */
+static void mpi3mr_set_io_divert_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_throttle_group_info *tg, u8 divert_value)
+{
+ unsigned long flags;
+ struct mpi3mr_tgt_dev *tgtdev;
+ struct mpi3mr_stgt_priv_data *tgt_priv;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ tgt_priv = tgtdev->starget->hostdata;
+ if (tgt_priv->throttle_group == tg)
+ tgt_priv->io_divert = divert_value;
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+}
+
+/**
+ * mpi3mr_print_device_event_notice - print notice related to post processing of
+ * device event after controller reset.
+ *
+ * @mrioc: Adapter instance reference
+ * @device_add: true for device add event and false for device removal event
+ *
+ * Return: None.
+ */
+void mpi3mr_print_device_event_notice(struct mpi3mr_ioc *mrioc,
+ bool device_add)
+{
+ ioc_notice(mrioc, "Device %s was in progress before the reset and\n",
+ (device_add ? "addition" : "removal"));
+ ioc_notice(mrioc, "completed after reset, verify whether the exposed devices\n");
+ ioc_notice(mrioc, "are matched with attached devices for correctness\n");
+}
+
+/**
+ * mpi3mr_remove_tgtdev_from_host - Remove dev from upper layers
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device structure
+ *
+ * Checks whether the device is exposed to upper layers and if it
+ * is then remove the device from upper layers by calling
+ * scsi_remove_target().
+ *
+ * Return: 0 on success, non zero on failure.
+ */
+void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ struct mpi3mr_stgt_priv_data *tgt_priv;
+
+ ioc_info(mrioc, "%s :Removing handle(0x%04x), wwid(0x%016llx)\n",
+ __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ tgt_priv = tgtdev->starget->hostdata;
+ atomic_set(&tgt_priv->block_io, 0);
+ tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ }
+
+ if (!mrioc->sas_transport_enabled || (tgtdev->dev_type !=
+ MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl) {
+ if (tgtdev->starget) {
+ if (mrioc->current_event)
+ mrioc->current_event->pending_at_sml = 1;
+ scsi_remove_target(&tgtdev->starget->dev);
+ tgtdev->host_exposed = 0;
+ if (mrioc->current_event) {
+ mrioc->current_event->pending_at_sml = 0;
+ if (mrioc->current_event->discard) {
+ mpi3mr_print_device_event_notice(mrioc,
+ false);
+ return;
+ }
+ }
+ }
+ } else
+ mpi3mr_remove_tgtdev_from_sas_transport(mrioc, tgtdev);
+ mpi3mr_global_trigger(mrioc,
+ MPI3_DRIVER2_GLOBALTRIGGER_DEVICE_REMOVAL_ENABLED);
+
+ ioc_info(mrioc, "%s :Removed handle(0x%04x), wwid(0x%016llx)\n",
+ __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
+}
+
+/**
+ * mpi3mr_report_tgtdev_to_host - Expose device to upper layers
+ * @mrioc: Adapter instance reference
+ * @perst_id: Persistent ID of the device
+ *
+ * Checks whether the device can be exposed to upper layers and
+ * if it is not then expose the device to upper layers by
+ * calling scsi_scan_target().
+ *
+ * Return: 0 on success, non zero on failure.
+ */
+static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc,
+ u16 perst_id)
+{
+ int retval = 0;
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
+ return -1;
+
+ tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id);
+ if (!tgtdev) {
+ retval = -1;
+ goto out;
+ }
+ if (tgtdev->is_hidden || tgtdev->host_exposed) {
+ retval = -1;
+ goto out;
+ }
+ if (!mrioc->sas_transport_enabled || (tgtdev->dev_type !=
+ MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl){
+ tgtdev->host_exposed = 1;
+ if (mrioc->current_event)
+ mrioc->current_event->pending_at_sml = 1;
+ scsi_scan_target(&mrioc->shost->shost_gendev,
+ mrioc->scsi_device_channel, tgtdev->perst_id,
+ SCAN_WILD_CARD, SCSI_SCAN_INITIAL);
+ if (!tgtdev->starget)
+ tgtdev->host_exposed = 0;
+ if (mrioc->current_event) {
+ mrioc->current_event->pending_at_sml = 0;
+ if (mrioc->current_event->discard) {
+ mpi3mr_print_device_event_notice(mrioc, true);
+ goto out;
+ }
+ }
+ dprint_event_bh(mrioc,
+ "exposed target device with handle(0x%04x), perst_id(%d)\n",
+ tgtdev->dev_handle, perst_id);
+ goto out;
+ } else
+ mpi3mr_report_tgtdev_to_sas_transport(mrioc, tgtdev);
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_change_queue_depth- Change QD callback handler
+ * @sdev: SCSI device reference
+ * @q_depth: Queue depth
+ *
+ * Validate and limit QD and call scsi_change_queue_depth.
+ *
+ * Return: return value of scsi_change_queue_depth
+ */
+static int mpi3mr_change_queue_depth(struct scsi_device *sdev,
+ int q_depth)
+{
+ struct scsi_target *starget = scsi_target(sdev);
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ int retval = 0;
+
+ if (!sdev->tagged_supported)
+ q_depth = 1;
+ if (q_depth > shost->can_queue)
+ q_depth = shost->can_queue;
+ else if (!q_depth)
+ q_depth = MPI3MR_DEFAULT_SDEV_QD;
+ retval = scsi_change_queue_depth(sdev, q_depth);
+ sdev->max_queue_depth = sdev->queue_depth;
+
+ return retval;
+}
+
+static void mpi3mr_configure_nvme_dev(struct mpi3mr_tgt_dev *tgt_dev,
+ struct queue_limits *lim)
+{
+ u8 pgsz = tgt_dev->dev_spec.pcie_inf.pgsz ? : MPI3MR_DEFAULT_PGSZEXP;
+
+ lim->max_hw_sectors = tgt_dev->dev_spec.pcie_inf.mdts / 512;
+ lim->virt_boundary_mask = (1 << pgsz) - 1;
+}
+
+static void mpi3mr_configure_tgt_dev(struct mpi3mr_tgt_dev *tgt_dev,
+ struct queue_limits *lim)
+{
+ if (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_PCIE &&
+ (tgt_dev->dev_spec.pcie_inf.dev_info &
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) ==
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE)
+ mpi3mr_configure_nvme_dev(tgt_dev, lim);
+}
+
+/**
+ * mpi3mr_update_sdev - Update SCSI device information
+ * @sdev: SCSI device reference
+ * @data: target device reference
+ *
+ * This is an iterator function called for each SCSI device in a
+ * target to update the target specific information into each
+ * SCSI device.
+ *
+ * Return: Nothing.
+ */
+static void
+mpi3mr_update_sdev(struct scsi_device *sdev, void *data)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+ struct queue_limits lim;
+
+ tgtdev = (struct mpi3mr_tgt_dev *)data;
+ if (!tgtdev)
+ return;
+
+ mpi3mr_change_queue_depth(sdev, tgtdev->q_depth);
+
+ lim = queue_limits_start_update(sdev->request_queue);
+ mpi3mr_configure_tgt_dev(tgtdev, &lim);
+ WARN_ON_ONCE(queue_limits_commit_update(sdev->request_queue, &lim));
+}
+
+/**
+ * mpi3mr_refresh_tgtdevs - Refresh target device exposure
+ * @mrioc: Adapter instance reference
+ *
+ * This is executed post controller reset to identify any
+ * missing devices during reset and remove from the upper layers
+ * or expose any newly detected device to the upper layers.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_refresh_tgtdevs(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next;
+ struct mpi3mr_stgt_priv_data *tgt_priv;
+
+ dprint_reset(mrioc, "refresh target devices: check for removals\n");
+ list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list,
+ list) {
+ if (((tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) ||
+ tgtdev->is_hidden) &&
+ tgtdev->host_exposed && tgtdev->starget &&
+ tgtdev->starget->hostdata) {
+ tgt_priv = tgtdev->starget->hostdata;
+ tgt_priv->dev_removed = 1;
+ atomic_set(&tgt_priv->block_io, 0);
+ }
+ }
+
+ list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list,
+ list) {
+ if (tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) {
+ dprint_reset(mrioc, "removing target device with perst_id(%d)\n",
+ tgtdev->perst_id);
+ if (tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, true);
+ mpi3mr_tgtdev_put(tgtdev);
+ } else if (tgtdev->is_hidden & tgtdev->host_exposed) {
+ dprint_reset(mrioc, "hiding target device with perst_id(%d)\n",
+ tgtdev->perst_id);
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ }
+ }
+
+ tgtdev = NULL;
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+ if ((tgtdev->dev_handle != MPI3MR_INVALID_DEV_HANDLE) &&
+ !tgtdev->is_hidden) {
+ if (!tgtdev->host_exposed)
+ mpi3mr_report_tgtdev_to_host(mrioc,
+ tgtdev->perst_id);
+ else if (tgtdev->starget)
+ starget_for_each_device(tgtdev->starget,
+ (void *)tgtdev, mpi3mr_update_sdev);
+ }
+ }
+}
+
+/**
+ * mpi3mr_update_tgtdev - DevStatusChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device internal structure
+ * @dev_pg0: New device page0
+ * @is_added: Flag to indicate the device is just added
+ *
+ * Update the information from the device page0 into the driver
+ * cached target device structure.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0,
+ bool is_added)
+{
+ u16 flags = 0;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+ struct mpi3mr_enclosure_node *enclosure_dev = NULL;
+ u8 prot_mask = 0;
+
+ tgtdev->perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ tgtdev->dev_handle = le16_to_cpu(dev_pg0->dev_handle);
+ tgtdev->dev_type = dev_pg0->device_form;
+ tgtdev->io_unit_port = dev_pg0->io_unit_port;
+ tgtdev->encl_handle = le16_to_cpu(dev_pg0->enclosure_handle);
+ tgtdev->parent_handle = le16_to_cpu(dev_pg0->parent_dev_handle);
+ tgtdev->slot = le16_to_cpu(dev_pg0->slot);
+ tgtdev->q_depth = le16_to_cpu(dev_pg0->queue_depth);
+ tgtdev->wwid = le64_to_cpu(dev_pg0->wwid);
+ tgtdev->devpg0_flag = le16_to_cpu(dev_pg0->flags);
+
+ if (tgtdev->encl_handle)
+ enclosure_dev = mpi3mr_enclosure_find_by_handle(mrioc,
+ tgtdev->encl_handle);
+ if (enclosure_dev)
+ tgtdev->enclosure_logical_id = le64_to_cpu(
+ enclosure_dev->pg0.enclosure_logical_id);
+
+ flags = tgtdev->devpg0_flag;
+
+ tgtdev->is_hidden = (flags & MPI3_DEVICE0_FLAGS_HIDDEN);
+
+ if (is_added == true)
+ tgtdev->io_throttle_enabled =
+ (flags & MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED) ? 1 : 0;
+
+ switch (flags & MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_MASK) {
+ case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_256_LB:
+ tgtdev->wslen = MPI3MR_WRITE_SAME_MAX_LEN_256_BLKS;
+ break;
+ case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_2048_LB:
+ tgtdev->wslen = MPI3MR_WRITE_SAME_MAX_LEN_2048_BLKS;
+ break;
+ case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_NO_LIMIT:
+ default:
+ tgtdev->wslen = 0;
+ break;
+ }
+
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ scsi_tgt_priv_data->perst_id = tgtdev->perst_id;
+ scsi_tgt_priv_data->dev_handle = tgtdev->dev_handle;
+ scsi_tgt_priv_data->dev_type = tgtdev->dev_type;
+ scsi_tgt_priv_data->io_throttle_enabled =
+ tgtdev->io_throttle_enabled;
+ if (is_added == true)
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ scsi_tgt_priv_data->wslen = tgtdev->wslen;
+ }
+
+ switch (dev_pg0->access_status) {
+ case MPI3_DEVICE0_ASTATUS_NO_ERRORS:
+ case MPI3_DEVICE0_ASTATUS_PREPARE:
+ case MPI3_DEVICE0_ASTATUS_NEEDS_INITIALIZATION:
+ case MPI3_DEVICE0_ASTATUS_DEVICE_MISSING_DELAY:
+ break;
+ default:
+ tgtdev->is_hidden = 1;
+ break;
+ }
+
+ switch (tgtdev->dev_type) {
+ case MPI3_DEVICE_DEVFORM_SAS_SATA:
+ {
+ struct mpi3_device0_sas_sata_format *sasinf =
+ &dev_pg0->device_specific.sas_sata_format;
+ u16 dev_info = le16_to_cpu(sasinf->device_info);
+
+ tgtdev->dev_spec.sas_sata_inf.dev_info = dev_info;
+ tgtdev->dev_spec.sas_sata_inf.sas_address =
+ le64_to_cpu(sasinf->sas_address);
+ tgtdev->dev_spec.sas_sata_inf.phy_id = sasinf->phy_num;
+ tgtdev->dev_spec.sas_sata_inf.attached_phy_id =
+ sasinf->attached_phy_identifier;
+ if ((dev_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) !=
+ MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE)
+ tgtdev->is_hidden = 1;
+ else if (!(dev_info & (MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET |
+ MPI3_SAS_DEVICE_INFO_SSP_TARGET)))
+ tgtdev->is_hidden = 1;
+
+ if (((tgtdev->devpg0_flag &
+ MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED)
+ && (tgtdev->devpg0_flag &
+ MPI3_DEVICE0_FLAGS_ATT_METHOD_VIRTUAL)) ||
+ (tgtdev->parent_handle == 0xFFFF))
+ tgtdev->non_stl = 1;
+ if (tgtdev->dev_spec.sas_sata_inf.hba_port)
+ tgtdev->dev_spec.sas_sata_inf.hba_port->port_id =
+ dev_pg0->io_unit_port;
+ break;
+ }
+ case MPI3_DEVICE_DEVFORM_PCIE:
+ {
+ struct mpi3_device0_pcie_format *pcieinf =
+ &dev_pg0->device_specific.pcie_format;
+ u16 dev_info = le16_to_cpu(pcieinf->device_info);
+
+ tgtdev->dev_spec.pcie_inf.dev_info = dev_info;
+ tgtdev->dev_spec.pcie_inf.capb =
+ le32_to_cpu(pcieinf->capabilities);
+ tgtdev->dev_spec.pcie_inf.mdts = MPI3MR_DEFAULT_MDTS;
+ /* 2^12 = 4096 */
+ tgtdev->dev_spec.pcie_inf.pgsz = 12;
+ if (dev_pg0->access_status == MPI3_DEVICE0_ASTATUS_NO_ERRORS) {
+ tgtdev->dev_spec.pcie_inf.mdts =
+ le32_to_cpu(pcieinf->maximum_data_transfer_size);
+ tgtdev->dev_spec.pcie_inf.pgsz = pcieinf->page_size;
+ tgtdev->dev_spec.pcie_inf.reset_to =
+ max_t(u8, pcieinf->controller_reset_to,
+ MPI3MR_INTADMCMD_TIMEOUT);
+ tgtdev->dev_spec.pcie_inf.abort_to =
+ max_t(u8, pcieinf->nvme_abort_to,
+ MPI3MR_INTADMCMD_TIMEOUT);
+ }
+ if (tgtdev->dev_spec.pcie_inf.mdts > (1024 * 1024))
+ tgtdev->dev_spec.pcie_inf.mdts = (1024 * 1024);
+ if (((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) !=
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE) &&
+ ((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) !=
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SCSI_DEVICE))
+ tgtdev->is_hidden = 1;
+ tgtdev->non_stl = 1;
+ if (!mrioc->shost)
+ break;
+ prot_mask = scsi_host_get_prot(mrioc->shost);
+ if (prot_mask & SHOST_DIX_TYPE0_PROTECTION) {
+ scsi_host_set_prot(mrioc->shost, prot_mask & 0x77);
+ ioc_info(mrioc,
+ "%s : Disabling DIX0 prot capability\n", __func__);
+ ioc_info(mrioc,
+ "because HBA does not support DIX0 operation on NVME drives\n");
+ }
+ break;
+ }
+ case MPI3_DEVICE_DEVFORM_VD:
+ {
+ struct mpi3_device0_vd_format *vdinf =
+ &dev_pg0->device_specific.vd_format;
+ struct mpi3mr_throttle_group_info *tg = NULL;
+ u16 vdinf_io_throttle_group =
+ le16_to_cpu(vdinf->io_throttle_group);
+
+ tgtdev->dev_spec.vd_inf.state = vdinf->vd_state;
+ if (vdinf->vd_state == MPI3_DEVICE0_VD_STATE_OFFLINE)
+ tgtdev->is_hidden = 1;
+ tgtdev->non_stl = 1;
+ tgtdev->dev_spec.vd_inf.reset_to =
+ max_t(u8, vdinf->vd_reset_to,
+ MPI3MR_INTADMCMD_TIMEOUT);
+ tgtdev->dev_spec.vd_inf.abort_to =
+ max_t(u8, vdinf->vd_abort_to,
+ MPI3MR_INTADMCMD_TIMEOUT);
+ tgtdev->dev_spec.vd_inf.tg_id = vdinf_io_throttle_group;
+ tgtdev->dev_spec.vd_inf.tg_high =
+ le16_to_cpu(vdinf->io_throttle_group_high) * 2048;
+ tgtdev->dev_spec.vd_inf.tg_low =
+ le16_to_cpu(vdinf->io_throttle_group_low) * 2048;
+ if (vdinf_io_throttle_group < mrioc->num_io_throttle_group) {
+ tg = mrioc->throttle_groups + vdinf_io_throttle_group;
+ tg->id = vdinf_io_throttle_group;
+ tg->high = tgtdev->dev_spec.vd_inf.tg_high;
+ tg->low = tgtdev->dev_spec.vd_inf.tg_low;
+ tg->qd_reduction =
+ tgtdev->dev_spec.vd_inf.tg_qd_reduction;
+ if (is_added == true)
+ tg->fw_qd = tgtdev->q_depth;
+ tg->modified_qd = tgtdev->q_depth;
+ }
+ tgtdev->dev_spec.vd_inf.tg = tg;
+ if (scsi_tgt_priv_data)
+ scsi_tgt_priv_data->throttle_group = tg;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/**
+ * mpi3mr_devstatuschg_evt_bh - DevStatusChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event information.
+ *
+ * Process Device status Change event and based on device's new
+ * information, either expose the device to the upper layers, or
+ * remove the device from upper layers.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_devstatuschg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ u16 dev_handle = 0;
+ u8 uhide = 0, delete = 0, cleanup = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3_event_data_device_status_change *evtdata =
+ (struct mpi3_event_data_device_status_change *)fwevt->event_data;
+
+ dev_handle = le16_to_cpu(evtdata->dev_handle);
+ dprint_event_bh(mrioc,
+ "processing device status change event bottom half for handle(0x%04x), rc(0x%02x)\n",
+ dev_handle, evtdata->reason_code);
+ switch (evtdata->reason_code) {
+ case MPI3_EVENT_DEV_STAT_RC_HIDDEN:
+ delete = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_NOT_HIDDEN:
+ uhide = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING:
+ delete = 1;
+ cleanup = 1;
+ break;
+ default:
+ ioc_info(mrioc, "%s :Unhandled reason code(0x%x)\n", __func__,
+ evtdata->reason_code);
+ break;
+ }
+
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (!tgtdev) {
+ dprint_event_bh(mrioc,
+ "processing device status change event bottom half,\n"
+ "cannot identify target device for handle(0x%04x), rc(0x%02x)\n",
+ dev_handle, evtdata->reason_code);
+ goto out;
+ }
+ if (uhide) {
+ tgtdev->is_hidden = 0;
+ if (!tgtdev->host_exposed)
+ mpi3mr_report_tgtdev_to_host(mrioc, tgtdev->perst_id);
+ }
+
+ if (delete)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+
+ if (cleanup) {
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, false);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+}
+
+/**
+ * mpi3mr_devinfochg_evt_bh - DeviceInfoChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @dev_pg0: New device page0
+ *
+ * Process Device Info Change event and based on device's new
+ * information, either expose the device to the upper layers, or
+ * remove the device from upper layers or update the details of
+ * the device.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3_device_page0 *dev_pg0)
+{
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ u16 dev_handle = 0, perst_id = 0;
+
+ perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ dev_handle = le16_to_cpu(dev_pg0->dev_handle);
+ dprint_event_bh(mrioc,
+ "processing device info change event bottom half for handle(0x%04x), perst_id(%d)\n",
+ dev_handle, perst_id);
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (!tgtdev) {
+ dprint_event_bh(mrioc,
+ "cannot identify target device for device info\n"
+ "change event handle(0x%04x), perst_id(%d)\n",
+ dev_handle, perst_id);
+ goto out;
+ }
+ mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, false);
+ if (!tgtdev->is_hidden && !tgtdev->host_exposed)
+ mpi3mr_report_tgtdev_to_host(mrioc, perst_id);
+ if (tgtdev->is_hidden && tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ if (!tgtdev->is_hidden && tgtdev->host_exposed && tgtdev->starget)
+ starget_for_each_device(tgtdev->starget, (void *)tgtdev,
+ mpi3mr_update_sdev);
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+}
+
+/**
+ * mpi3mr_free_enclosure_list - release enclosures
+ * @mrioc: Adapter instance reference
+ *
+ * Free memory allocated during encloure add.
+ *
+ * Return nothing.
+ */
+void mpi3mr_free_enclosure_list(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_enclosure_node *enclosure_dev, *enclosure_dev_next;
+
+ list_for_each_entry_safe(enclosure_dev,
+ enclosure_dev_next, &mrioc->enclosure_list, list) {
+ list_del(&enclosure_dev->list);
+ kfree(enclosure_dev);
+ }
+}
+
+/**
+ * mpi3mr_enclosure_find_by_handle - enclosure search by handle
+ * @mrioc: Adapter instance reference
+ * @handle: Firmware device handle of the enclosure
+ *
+ * This searches for enclosure device based on handle, then returns the
+ * enclosure object.
+ *
+ * Return: Enclosure object reference or NULL
+ */
+struct mpi3mr_enclosure_node *mpi3mr_enclosure_find_by_handle(
+ struct mpi3mr_ioc *mrioc, u16 handle)
+{
+ struct mpi3mr_enclosure_node *enclosure_dev, *r = NULL;
+
+ list_for_each_entry(enclosure_dev, &mrioc->enclosure_list, list) {
+ if (le16_to_cpu(enclosure_dev->pg0.enclosure_handle) != handle)
+ continue;
+ r = enclosure_dev;
+ goto out;
+ }
+out:
+ return r;
+}
+
+/**
+ * mpi3mr_process_trigger_data_event_bh - Process trigger event
+ * data
+ * @mrioc: Adapter instance reference
+ * @event_data: Event data
+ *
+ * This function releases diage buffers or issues diag fault
+ * based on trigger conditions
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_process_trigger_data_event_bh(struct mpi3mr_ioc *mrioc,
+ struct trigger_event_data *event_data)
+{
+ struct diag_buffer_desc *trace_hdb = event_data->trace_hdb;
+ struct diag_buffer_desc *fw_hdb = event_data->fw_hdb;
+ unsigned long flags;
+ int retval = 0;
+ u8 trigger_type = event_data->trigger_type;
+ union mpi3mr_trigger_data *trigger_data =
+ &event_data->trigger_specific_data;
+
+ if (event_data->snapdump) {
+ if (trace_hdb)
+ mpi3mr_set_trigger_data_in_hdb(trace_hdb, trigger_type,
+ trigger_data, 1);
+ if (fw_hdb)
+ mpi3mr_set_trigger_data_in_hdb(fw_hdb, trigger_type,
+ trigger_data, 1);
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_TRIGGER, 1);
+ return;
+ }
+
+ if (trace_hdb) {
+ retval = mpi3mr_issue_diag_buf_release(mrioc, trace_hdb);
+ if (!retval) {
+ mpi3mr_set_trigger_data_in_hdb(trace_hdb, trigger_type,
+ trigger_data, 1);
+ }
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ mrioc->trace_release_trigger_active = false;
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+ if (fw_hdb) {
+ retval = mpi3mr_issue_diag_buf_release(mrioc, fw_hdb);
+ if (!retval) {
+ mpi3mr_set_trigger_data_in_hdb(fw_hdb, trigger_type,
+ trigger_data, 1);
+ }
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ mrioc->fw_release_trigger_active = false;
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
+ * mpi3mr_encldev_add_chg_evt_debug - debug for enclosure event
+ * @mrioc: Adapter instance reference
+ * @encl_pg0: Enclosure page 0.
+ * @is_added: Added event or not
+ *
+ * Return nothing.
+ */
+static void mpi3mr_encldev_add_chg_evt_debug(struct mpi3mr_ioc *mrioc,
+ struct mpi3_enclosure_page0 *encl_pg0, u8 is_added)
+{
+ char *reason_str = NULL;
+
+ if (!(mrioc->logging_level & MPI3_DEBUG_EVENT_WORK_TASK))
+ return;
+
+ if (is_added)
+ reason_str = "enclosure added";
+ else
+ reason_str = "enclosure dev status changed";
+
+ ioc_info(mrioc,
+ "%s: handle(0x%04x), enclosure logical id(0x%016llx)\n",
+ reason_str, le16_to_cpu(encl_pg0->enclosure_handle),
+ (unsigned long long)le64_to_cpu(encl_pg0->enclosure_logical_id));
+ ioc_info(mrioc,
+ "number of slots(%d), port(%d), flags(0x%04x), present(%d)\n",
+ le16_to_cpu(encl_pg0->num_slots), encl_pg0->io_unit_port,
+ le16_to_cpu(encl_pg0->flags),
+ ((le16_to_cpu(encl_pg0->flags) &
+ MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT_MASK) >> 4));
+}
+
+/**
+ * mpi3mr_encldev_add_chg_evt_bh - Enclosure evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Prints information about the Enclosure device status or
+ * Enclosure add events if logging is enabled and add or remove
+ * the enclosure from the controller's internal list of
+ * enclosures.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_encldev_add_chg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ struct mpi3mr_enclosure_node *enclosure_dev = NULL;
+ struct mpi3_enclosure_page0 *encl_pg0;
+ u16 encl_handle;
+ u8 added, present;
+
+ encl_pg0 = (struct mpi3_enclosure_page0 *) fwevt->event_data;
+ added = (fwevt->event_id == MPI3_EVENT_ENCL_DEVICE_ADDED) ? 1 : 0;
+ mpi3mr_encldev_add_chg_evt_debug(mrioc, encl_pg0, added);
+
+
+ encl_handle = le16_to_cpu(encl_pg0->enclosure_handle);
+ present = ((le16_to_cpu(encl_pg0->flags) &
+ MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT_MASK) >> 4);
+
+ if (encl_handle)
+ enclosure_dev = mpi3mr_enclosure_find_by_handle(mrioc,
+ encl_handle);
+ if (!enclosure_dev && present) {
+ enclosure_dev =
+ kzalloc(sizeof(struct mpi3mr_enclosure_node),
+ GFP_KERNEL);
+ if (!enclosure_dev)
+ return;
+ list_add_tail(&enclosure_dev->list,
+ &mrioc->enclosure_list);
+ }
+ if (enclosure_dev) {
+ if (!present) {
+ list_del(&enclosure_dev->list);
+ kfree(enclosure_dev);
+ } else
+ memcpy(&enclosure_dev->pg0, encl_pg0,
+ sizeof(enclosure_dev->pg0));
+
+ }
+}
+
+/**
+ * mpi3mr_sastopochg_evt_debug - SASTopoChange details
+ * @mrioc: Adapter instance reference
+ * @event_data: SAS topology change list event data
+ *
+ * Prints information about the SAS topology change event.
+ *
+ * Return: Nothing.
+ */
+static void
+mpi3mr_sastopochg_evt_debug(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_data_sas_topology_change_list *event_data)
+{
+ int i;
+ u16 handle;
+ u8 reason_code, phy_number;
+ char *status_str = NULL;
+ u8 link_rate, prev_link_rate;
+
+ switch (event_data->exp_status) {
+ case MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI3_EVENT_SAS_TOPO_ES_RESPONDING:
+ status_str = "responding";
+ break;
+ case MPI3_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ case MPI3_EVENT_SAS_TOPO_ES_NO_EXPANDER:
+ status_str = "direct attached";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ ioc_info(mrioc, "%s :sas topology change: (%s)\n",
+ __func__, status_str);
+ ioc_info(mrioc,
+ "%s :\texpander_handle(0x%04x), port(%d), enclosure_handle(0x%04x) start_phy(%02d), num_entries(%d)\n",
+ __func__, le16_to_cpu(event_data->expander_dev_handle),
+ event_data->io_unit_port,
+ le16_to_cpu(event_data->enclosure_handle),
+ event_data->start_phy_num, event_data->num_entries);
+ for (i = 0; i < event_data->num_entries; i++) {
+ handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ phy_number = event_data->start_phy_num + i;
+ reason_code = event_data->phy_entry[i].status &
+ MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;
+ switch (reason_code) {
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
+ status_str = "target remove";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING:
+ status_str = "delay target remove";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED:
+ status_str = "link status change";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE:
+ status_str = "link status no change";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING:
+ status_str = "target responding";
+ break;
+ default:
+ status_str = "unknown";
+ break;
+ }
+ link_rate = event_data->phy_entry[i].link_rate >> 4;
+ prev_link_rate = event_data->phy_entry[i].link_rate & 0xF;
+ ioc_info(mrioc,
+ "%s :\tphy(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n",
+ __func__, phy_number, handle, status_str, link_rate,
+ prev_link_rate);
+ }
+}
+
+/**
+ * mpi3mr_sastopochg_evt_bh - SASTopologyChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Prints information about the SAS topology change event and
+ * for "not responding" event code, removes the device from the
+ * upper layers.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_sastopochg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ struct mpi3_event_data_sas_topology_change_list *event_data =
+ (struct mpi3_event_data_sas_topology_change_list *)fwevt->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ u64 exp_sas_address = 0, parent_sas_address = 0;
+ struct mpi3mr_hba_port *hba_port = NULL;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_sas_node *sas_expander = NULL;
+ unsigned long flags;
+ u8 link_rate, prev_link_rate, parent_phy_number;
+
+ mpi3mr_sastopochg_evt_debug(mrioc, event_data);
+ if (mrioc->sas_transport_enabled) {
+ hba_port = mpi3mr_get_hba_port_by_id(mrioc,
+ event_data->io_unit_port);
+ if (le16_to_cpu(event_data->expander_dev_handle)) {
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ sas_expander = __mpi3mr_expander_find_by_handle(mrioc,
+ le16_to_cpu(event_data->expander_dev_handle));
+ if (sas_expander) {
+ exp_sas_address = sas_expander->sas_address;
+ hba_port = sas_expander->hba_port;
+ }
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ parent_sas_address = exp_sas_address;
+ } else
+ parent_sas_address = mrioc->sas_hba.sas_address;
+ }
+
+ for (i = 0; i < event_data->num_entries; i++) {
+ if (fwevt->discard)
+ return;
+ handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (!tgtdev)
+ continue;
+
+ reason_code = event_data->phy_entry[i].status &
+ MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;
+
+ switch (reason_code) {
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
+ if (tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, false);
+ mpi3mr_tgtdev_put(tgtdev);
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING:
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED:
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE:
+ {
+ if (!mrioc->sas_transport_enabled || tgtdev->non_stl
+ || tgtdev->is_hidden)
+ break;
+ link_rate = event_data->phy_entry[i].link_rate >> 4;
+ prev_link_rate = event_data->phy_entry[i].link_rate & 0xF;
+ if (link_rate == prev_link_rate)
+ break;
+ if (!parent_sas_address)
+ break;
+ parent_phy_number = event_data->start_phy_num + i;
+ mpi3mr_update_links(mrioc, parent_sas_address, handle,
+ parent_phy_number, link_rate, hba_port);
+ break;
+ }
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+
+ if (mrioc->sas_transport_enabled && (event_data->exp_status ==
+ MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING)) {
+ if (sas_expander)
+ mpi3mr_expander_remove(mrioc, exp_sas_address,
+ hba_port);
+ }
+}
+
+/**
+ * mpi3mr_pcietopochg_evt_debug - PCIeTopoChange details
+ * @mrioc: Adapter instance reference
+ * @event_data: PCIe topology change list event data
+ *
+ * Prints information about the PCIe topology change event.
+ *
+ * Return: Nothing.
+ */
+static void
+mpi3mr_pcietopochg_evt_debug(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_data_pcie_topology_change_list *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 port_number;
+ char *status_str = NULL;
+ u8 link_rate, prev_link_rate;
+
+ switch (event_data->switch_status) {
+ case MPI3_EVENT_PCIE_TOPO_SS_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_SS_RESPONDING:
+ status_str = "responding";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH:
+ status_str = "direct attached";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ ioc_info(mrioc, "%s :pcie topology change: (%s)\n",
+ __func__, status_str);
+ ioc_info(mrioc,
+ "%s :\tswitch_handle(0x%04x), enclosure_handle(0x%04x) start_port(%02d), num_entries(%d)\n",
+ __func__, le16_to_cpu(event_data->switch_dev_handle),
+ le16_to_cpu(event_data->enclosure_handle),
+ event_data->start_port_num, event_data->num_entries);
+ for (i = 0; i < event_data->num_entries; i++) {
+ handle =
+ le16_to_cpu(event_data->port_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ port_number = event_data->start_port_num + i;
+ reason_code = event_data->port_entry[i].port_status;
+ switch (reason_code) {
+ case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ status_str = "target remove";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING:
+ status_str = "delay target remove";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
+ status_str = "link status change";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_NO_CHANGE:
+ status_str = "link status no change";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_RESPONDING:
+ status_str = "target responding";
+ break;
+ default:
+ status_str = "unknown";
+ break;
+ }
+ link_rate = event_data->port_entry[i].current_port_info &
+ MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK;
+ prev_link_rate = event_data->port_entry[i].previous_port_info &
+ MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK;
+ ioc_info(mrioc,
+ "%s :\tport(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n",
+ __func__, port_number, handle, status_str, link_rate,
+ prev_link_rate);
+ }
+}
+
+/**
+ * mpi3mr_pcietopochg_evt_bh - PCIeTopologyChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Prints information about the PCIe topology change event and
+ * for "not responding" event code, removes the device from the
+ * upper layers.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_pcietopochg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ struct mpi3_event_data_pcie_topology_change_list *event_data =
+ (struct mpi3_event_data_pcie_topology_change_list *)fwevt->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+
+ mpi3mr_pcietopochg_evt_debug(mrioc, event_data);
+
+ for (i = 0; i < event_data->num_entries; i++) {
+ if (fwevt->discard)
+ return;
+ handle =
+ le16_to_cpu(event_data->port_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (!tgtdev)
+ continue;
+
+ reason_code = event_data->port_entry[i].port_status;
+
+ switch (reason_code) {
+ case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ if (tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, false);
+ mpi3mr_tgtdev_put(tgtdev);
+ break;
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * mpi3mr_logdata_evt_bh - Log data event bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Extracts the event data and calls application interfacing
+ * function to process the event further.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_logdata_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ mpi3mr_app_save_logdata(mrioc, fwevt->event_data,
+ fwevt->event_data_size);
+}
+
+/**
+ * mpi3mr_update_sdev_qd - Update SCSI device queue depath
+ * @sdev: SCSI device reference
+ * @data: Queue depth reference
+ *
+ * This is an iterator function called for each SCSI device in a
+ * target to update the QD of each SCSI device.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_update_sdev_qd(struct scsi_device *sdev, void *data)
+{
+ u16 *q_depth = (u16 *)data;
+
+ scsi_change_queue_depth(sdev, (int)*q_depth);
+ sdev->max_queue_depth = sdev->queue_depth;
+}
+
+/**
+ * mpi3mr_set_qd_for_all_vd_in_tg -set QD for TG VDs
+ * @mrioc: Adapter instance reference
+ * @tg: Throttle group information pointer
+ *
+ * Accessor to reduce QD for each device associated with the
+ * given throttle group.
+ *
+ * Return: None.
+ */
+static void mpi3mr_set_qd_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_throttle_group_info *tg)
+{
+ unsigned long flags;
+ struct mpi3mr_tgt_dev *tgtdev;
+ struct mpi3mr_stgt_priv_data *tgt_priv;
+
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ tgt_priv = tgtdev->starget->hostdata;
+ if (tgt_priv->throttle_group == tg) {
+ dprint_event_bh(mrioc,
+ "updating qd due to throttling for persist_id(%d) original_qd(%d), reduced_qd (%d)\n",
+ tgt_priv->perst_id, tgtdev->q_depth,
+ tg->modified_qd);
+ starget_for_each_device(tgtdev->starget,
+ (void *)&tg->modified_qd,
+ mpi3mr_update_sdev_qd);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+}
+
+/**
+ * mpi3mr_fwevt_bh - Firmware event bottomhalf handler
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Identifies the firmware event and calls corresponding bottomg
+ * half handler and sends event acknowledgment if required.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ struct mpi3_device_page0 *dev_pg0 = NULL;
+ u16 perst_id, handle, dev_info;
+ struct mpi3_device0_sas_sata_format *sasinf = NULL;
+ unsigned int timeout;
+
+ mpi3mr_fwevt_del_from_list(mrioc, fwevt);
+ mrioc->current_event = fwevt;
+
+ if (mrioc->stop_drv_processing) {
+ dprint_event_bh(mrioc, "ignoring event(0x%02x) in the bottom half handler\n"
+ "due to stop_drv_processing\n", fwevt->event_id);
+ goto out;
+ }
+
+ if (mrioc->unrecoverable) {
+ dprint_event_bh(mrioc,
+ "ignoring event(0x%02x) in bottom half handler due to unrecoverable controller\n",
+ fwevt->event_id);
+ goto out;
+ }
+
+ if (!fwevt->process_evt)
+ goto evt_ack;
+
+ dprint_event_bh(mrioc, "processing event(0x%02x) -(0x%08x) in the bottom half handler\n",
+ fwevt->event_id, fwevt->evt_ctx);
+
+ switch (fwevt->event_id) {
+ case MPI3_EVENT_DEVICE_ADDED:
+ {
+ dev_pg0 = (struct mpi3_device_page0 *)fwevt->event_data;
+ perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ handle = le16_to_cpu(dev_pg0->dev_handle);
+ if (perst_id != MPI3_DEVICE0_PERSISTENTID_INVALID)
+ mpi3mr_report_tgtdev_to_host(mrioc, perst_id);
+ else if (mrioc->sas_transport_enabled &&
+ (dev_pg0->device_form == MPI3_DEVICE_DEVFORM_SAS_SATA)) {
+ sasinf = &dev_pg0->device_specific.sas_sata_format;
+ dev_info = le16_to_cpu(sasinf->device_info);
+ if (!mrioc->sas_hba.num_phys)
+ mpi3mr_sas_host_add(mrioc);
+ else
+ mpi3mr_sas_host_refresh(mrioc);
+
+ if (mpi3mr_is_expander_device(dev_info))
+ mpi3mr_expander_add(mrioc, handle);
+ }
+ break;
+ }
+ case MPI3_EVENT_DEVICE_INFO_CHANGED:
+ {
+ dev_pg0 = (struct mpi3_device_page0 *)fwevt->event_data;
+ perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ if (perst_id != MPI3_DEVICE0_PERSISTENTID_INVALID)
+ mpi3mr_devinfochg_evt_bh(mrioc, dev_pg0);
+ break;
+ }
+ case MPI3_EVENT_DEVICE_STATUS_CHANGE:
+ {
+ mpi3mr_devstatuschg_evt_bh(mrioc, fwevt);
+ break;
+ }
+ case MPI3_EVENT_ENCL_DEVICE_ADDED:
+ case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
+ {
+ mpi3mr_encldev_add_chg_evt_bh(mrioc, fwevt);
+ break;
+ }
+
+ case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ {
+ mpi3mr_sastopochg_evt_bh(mrioc, fwevt);
+ break;
+ }
+ case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ {
+ mpi3mr_pcietopochg_evt_bh(mrioc, fwevt);
+ break;
+ }
+ case MPI3_EVENT_LOG_DATA:
+ {
+ mpi3mr_logdata_evt_bh(mrioc, fwevt);
+ break;
+ }
+ case MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION:
+ {
+ struct mpi3mr_throttle_group_info *tg;
+
+ tg = *(struct mpi3mr_throttle_group_info **)fwevt->event_data;
+ dprint_event_bh(mrioc,
+ "qd reduction event processed for tg_id(%d) reduction_needed(%d)\n",
+ tg->id, tg->need_qd_reduction);
+ if (tg->need_qd_reduction) {
+ mpi3mr_set_qd_for_all_vd_in_tg(mrioc, tg);
+ tg->need_qd_reduction = 0;
+ }
+ break;
+ }
+ case MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH:
+ {
+ timeout = MPI3MR_RESET_TIMEOUT * 2;
+ while ((mrioc->device_refresh_on || mrioc->block_on_pci_err) &&
+ !mrioc->unrecoverable && !mrioc->pci_err_recovery) {
+ msleep(500);
+ if (!timeout--) {
+ mrioc->unrecoverable = 1;
+ break;
+ }
+ }
+
+ if (mrioc->unrecoverable || mrioc->pci_err_recovery)
+ break;
+
+ dprint_event_bh(mrioc,
+ "scan for non responding and newly added devices after soft reset started\n");
+ if (mrioc->sas_transport_enabled) {
+ mpi3mr_refresh_sas_ports(mrioc);
+ mpi3mr_refresh_expanders(mrioc);
+ }
+ mpi3mr_refresh_tgtdevs(mrioc);
+ ioc_info(mrioc,
+ "scan for non responding and newly added devices after soft reset completed\n");
+ break;
+ }
+ case MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER:
+ {
+ mpi3mr_process_trigger_data_event_bh(mrioc,
+ (struct trigger_event_data *)fwevt->event_data);
+ break;
+ }
+ default:
+ break;
+ }
+
+evt_ack:
+ if (fwevt->send_ack)
+ mpi3mr_process_event_ack(mrioc, fwevt->event_id,
+ fwevt->evt_ctx);
+out:
+ /* Put fwevt reference count to neutralize kref_init increment */
+ mpi3mr_fwevt_put(fwevt);
+ mrioc->current_event = NULL;
+}
+
+/**
+ * mpi3mr_fwevt_worker - Firmware event worker
+ * @work: Work struct containing firmware event
+ *
+ * Extracts the firmware event and calls mpi3mr_fwevt_bh.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_worker(struct work_struct *work)
+{
+ struct mpi3mr_fwevt *fwevt = container_of(work, struct mpi3mr_fwevt,
+ work);
+ mpi3mr_fwevt_bh(fwevt->mrioc, fwevt);
+ /*
+ * Put fwevt reference count after
+ * dequeuing it from worker queue
+ */
+ mpi3mr_fwevt_put(fwevt);
+}
+
+/**
+ * mpi3mr_create_tgtdev - Create and add a target device
+ * @mrioc: Adapter instance reference
+ * @dev_pg0: Device Page 0 data
+ *
+ * If the device specified by the device page 0 data is not
+ * present in the driver's internal list, allocate the memory
+ * for the device, populate the data and add to the list, else
+ * update the device data. The key is persistent ID.
+ *
+ * Return: 0 on success, -ENOMEM on memory allocation failure
+ */
+static int mpi3mr_create_tgtdev(struct mpi3mr_ioc *mrioc,
+ struct mpi3_device_page0 *dev_pg0)
+{
+ int retval = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ u16 perst_id = 0;
+ unsigned long flags;
+
+ perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ if (perst_id == MPI3_DEVICE0_PERSISTENTID_INVALID)
+ return retval;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id);
+ if (tgtdev)
+ tgtdev->state = MPI3MR_DEV_CREATED;
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ if (tgtdev) {
+ mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true);
+ mpi3mr_tgtdev_put(tgtdev);
+ } else {
+ tgtdev = mpi3mr_alloc_tgtdev();
+ if (!tgtdev)
+ return -ENOMEM;
+ mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true);
+ mpi3mr_tgtdev_add_to_list(mrioc, tgtdev);
+ }
+
+ return retval;
+}
+
+/**
+ * mpi3mr_flush_delayed_cmd_lists - Flush pending commands
+ * @mrioc: Adapter instance reference
+ *
+ * Flush pending commands in the delayed lists due to a
+ * controller reset or driver removal as a cleanup.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_flush_delayed_cmd_lists(struct mpi3mr_ioc *mrioc)
+{
+ struct delayed_dev_rmhs_node *_rmhs_node;
+ struct delayed_evt_ack_node *_evtack_node;
+
+ dprint_reset(mrioc, "flushing delayed dev_remove_hs commands\n");
+ while (!list_empty(&mrioc->delayed_rmhs_list)) {
+ _rmhs_node = list_entry(mrioc->delayed_rmhs_list.next,
+ struct delayed_dev_rmhs_node, list);
+ list_del(&_rmhs_node->list);
+ kfree(_rmhs_node);
+ }
+ dprint_reset(mrioc, "flushing delayed event ack commands\n");
+ while (!list_empty(&mrioc->delayed_evtack_cmds_list)) {
+ _evtack_node = list_entry(mrioc->delayed_evtack_cmds_list.next,
+ struct delayed_evt_ack_node, list);
+ list_del(&_evtack_node->list);
+ kfree(_evtack_node);
+ }
+}
+
+/**
+ * mpi3mr_dev_rmhs_complete_iou - Device removal IOUC completion
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Issues a target reset TM to the firmware from the device
+ * removal TM pend list or retry the removal handshake sequence
+ * based on the IOU control request IOC status.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_dev_rmhs_complete_iou(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ struct delayed_dev_rmhs_node *delayed_dev_rmhs = NULL;
+
+ if (drv_cmd->state & MPI3MR_CMD_RESET)
+ goto clear_drv_cmd;
+
+ ioc_info(mrioc,
+ "%s :dev_rmhs_iouctrl_complete:handle(0x%04x), ioc_status(0x%04x), loginfo(0x%08x)\n",
+ __func__, drv_cmd->dev_handle, drv_cmd->ioc_status,
+ drv_cmd->ioc_loginfo);
+ if (drv_cmd->ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ if (drv_cmd->retry_count < MPI3MR_DEV_RMHS_RETRY_COUNT) {
+ drv_cmd->retry_count++;
+ ioc_info(mrioc,
+ "%s :dev_rmhs_iouctrl_complete: handle(0x%04x)retrying handshake retry=%d\n",
+ __func__, drv_cmd->dev_handle,
+ drv_cmd->retry_count);
+ mpi3mr_dev_rmhs_send_tm(mrioc, drv_cmd->dev_handle,
+ drv_cmd, drv_cmd->iou_rc);
+ return;
+ }
+ ioc_err(mrioc,
+ "%s :dev removal handshake failed after all retries: handle(0x%04x)\n",
+ __func__, drv_cmd->dev_handle);
+ } else {
+ ioc_info(mrioc,
+ "%s :dev removal handshake completed successfully: handle(0x%04x)\n",
+ __func__, drv_cmd->dev_handle);
+ clear_bit(drv_cmd->dev_handle, mrioc->removepend_bitmap);
+ }
+
+ if (!list_empty(&mrioc->delayed_rmhs_list)) {
+ delayed_dev_rmhs = list_entry(mrioc->delayed_rmhs_list.next,
+ struct delayed_dev_rmhs_node, list);
+ drv_cmd->dev_handle = delayed_dev_rmhs->handle;
+ drv_cmd->retry_count = 0;
+ drv_cmd->iou_rc = delayed_dev_rmhs->iou_rc;
+ ioc_info(mrioc,
+ "%s :dev_rmhs_iouctrl_complete: processing delayed TM: handle(0x%04x)\n",
+ __func__, drv_cmd->dev_handle);
+ mpi3mr_dev_rmhs_send_tm(mrioc, drv_cmd->dev_handle, drv_cmd,
+ drv_cmd->iou_rc);
+ list_del(&delayed_dev_rmhs->list);
+ kfree(delayed_dev_rmhs);
+ return;
+ }
+
+clear_drv_cmd:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->retry_count = 0;
+ drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ clear_bit(cmd_idx, mrioc->devrem_bitmap);
+}
+
+/**
+ * mpi3mr_dev_rmhs_complete_tm - Device removal TM completion
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Issues a target reset TM to the firmware from the device
+ * removal TM pend list or issue IO unit control request as
+ * part of device removal or hidden acknowledgment handshake.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_dev_rmhs_complete_tm(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ struct mpi3_iounit_control_request iou_ctrl;
+ u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ struct mpi3_scsi_task_mgmt_reply *tm_reply = NULL;
+ int retval;
+
+ if (drv_cmd->state & MPI3MR_CMD_RESET)
+ goto clear_drv_cmd;
+
+ if (drv_cmd->state & MPI3MR_CMD_REPLY_VALID)
+ tm_reply = (struct mpi3_scsi_task_mgmt_reply *)drv_cmd->reply;
+
+ if (tm_reply)
+ pr_info(IOCNAME
+ "dev_rmhs_tr_complete:handle(0x%04x), ioc_status(0x%04x), loginfo(0x%08x), term_count(%d)\n",
+ mrioc->name, drv_cmd->dev_handle, drv_cmd->ioc_status,
+ drv_cmd->ioc_loginfo,
+ le32_to_cpu(tm_reply->termination_count));
+
+ pr_info(IOCNAME "Issuing IOU CTL: handle(0x%04x) dev_rmhs idx(%d)\n",
+ mrioc->name, drv_cmd->dev_handle, cmd_idx);
+
+ memset(&iou_ctrl, 0, sizeof(iou_ctrl));
+
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 0;
+ drv_cmd->callback = mpi3mr_dev_rmhs_complete_iou;
+ iou_ctrl.operation = drv_cmd->iou_rc;
+ iou_ctrl.param16[0] = cpu_to_le16(drv_cmd->dev_handle);
+ iou_ctrl.host_tag = cpu_to_le16(drv_cmd->host_tag);
+ iou_ctrl.function = MPI3_FUNCTION_IO_UNIT_CONTROL;
+
+ retval = mpi3mr_admin_request_post(mrioc, &iou_ctrl, sizeof(iou_ctrl),
+ 1);
+ if (retval) {
+ pr_err(IOCNAME "Issue DevRmHsTMIOUCTL: Admin post failed\n",
+ mrioc->name);
+ goto clear_drv_cmd;
+ }
+
+ return;
+clear_drv_cmd:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ drv_cmd->retry_count = 0;
+ clear_bit(cmd_idx, mrioc->devrem_bitmap);
+}
+
+/**
+ * mpi3mr_dev_rmhs_send_tm - Issue TM for device removal
+ * @mrioc: Adapter instance reference
+ * @handle: Device handle
+ * @cmdparam: Internal command tracker
+ * @iou_rc: IO unit reason code
+ *
+ * Issues a target reset TM to the firmware or add it to a pend
+ * list as part of device removal or hidden acknowledgment
+ * handshake.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle,
+ struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc)
+{
+ struct mpi3_scsi_task_mgmt_request tm_req;
+ int retval = 0;
+ u16 cmd_idx = MPI3MR_NUM_DEVRMCMD;
+ u8 retrycount = 5;
+ struct mpi3mr_drv_cmd *drv_cmd = cmdparam;
+ struct delayed_dev_rmhs_node *delayed_dev_rmhs = NULL;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (tgtdev && (iou_rc == MPI3_CTRL_OP_REMOVE_DEVICE))
+ tgtdev->state = MPI3MR_DEV_REMOVE_HS_STARTED;
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ if (drv_cmd)
+ goto issue_cmd;
+ do {
+ cmd_idx = find_first_zero_bit(mrioc->devrem_bitmap,
+ MPI3MR_NUM_DEVRMCMD);
+ if (cmd_idx < MPI3MR_NUM_DEVRMCMD) {
+ if (!test_and_set_bit(cmd_idx, mrioc->devrem_bitmap))
+ break;
+ cmd_idx = MPI3MR_NUM_DEVRMCMD;
+ }
+ } while (retrycount--);
+
+ if (cmd_idx >= MPI3MR_NUM_DEVRMCMD) {
+ delayed_dev_rmhs = kzalloc(sizeof(*delayed_dev_rmhs),
+ GFP_ATOMIC);
+ if (!delayed_dev_rmhs)
+ return;
+ INIT_LIST_HEAD(&delayed_dev_rmhs->list);
+ delayed_dev_rmhs->handle = handle;
+ delayed_dev_rmhs->iou_rc = iou_rc;
+ list_add_tail(&delayed_dev_rmhs->list,
+ &mrioc->delayed_rmhs_list);
+ ioc_info(mrioc, "%s :DevRmHs: tr:handle(0x%04x) is postponed\n",
+ __func__, handle);
+ return;
+ }
+ drv_cmd = &mrioc->dev_rmhs_cmds[cmd_idx];
+
+issue_cmd:
+ cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ ioc_info(mrioc,
+ "%s :Issuing TR TM: for devhandle 0x%04x with dev_rmhs %d\n",
+ __func__, handle, cmd_idx);
+
+ memset(&tm_req, 0, sizeof(tm_req));
+ if (drv_cmd->state & MPI3MR_CMD_PENDING) {
+ ioc_err(mrioc, "%s :Issue TM: Command is in use\n", __func__);
+ goto out;
+ }
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 0;
+ drv_cmd->callback = mpi3mr_dev_rmhs_complete_tm;
+ drv_cmd->dev_handle = handle;
+ drv_cmd->iou_rc = iou_rc;
+ tm_req.dev_handle = cpu_to_le16(handle);
+ tm_req.task_type = MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+ tm_req.host_tag = cpu_to_le16(drv_cmd->host_tag);
+ tm_req.task_host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INVALID);
+ tm_req.function = MPI3_FUNCTION_SCSI_TASK_MGMT;
+
+ set_bit(handle, mrioc->removepend_bitmap);
+ retval = mpi3mr_admin_request_post(mrioc, &tm_req, sizeof(tm_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "%s :Issue DevRmHsTM: Admin Post failed\n",
+ __func__);
+ goto out_failed;
+ }
+out:
+ return;
+out_failed:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ drv_cmd->retry_count = 0;
+ clear_bit(cmd_idx, mrioc->devrem_bitmap);
+}
+
+/**
+ * mpi3mr_complete_evt_ack - event ack request completion
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * This is the completion handler for non blocking event
+ * acknowledgment sent to the firmware and this will issue any
+ * pending event acknowledgment request.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_complete_evt_ack(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_EVTACKCMD_MIN;
+ struct delayed_evt_ack_node *delayed_evtack = NULL;
+
+ if (drv_cmd->state & MPI3MR_CMD_RESET)
+ goto clear_drv_cmd;
+
+ if (drv_cmd->ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_event_th(mrioc,
+ "immediate event ack failed with ioc_status(0x%04x) log_info(0x%08x)\n",
+ (drv_cmd->ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ drv_cmd->ioc_loginfo);
+ }
+
+ if (!list_empty(&mrioc->delayed_evtack_cmds_list)) {
+ delayed_evtack =
+ list_entry(mrioc->delayed_evtack_cmds_list.next,
+ struct delayed_evt_ack_node, list);
+ mpi3mr_send_event_ack(mrioc, delayed_evtack->event, drv_cmd,
+ delayed_evtack->event_ctx);
+ list_del(&delayed_evtack->list);
+ kfree(delayed_evtack);
+ return;
+ }
+clear_drv_cmd:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ clear_bit(cmd_idx, mrioc->evtack_cmds_bitmap);
+}
+
+/**
+ * mpi3mr_send_event_ack - Issue event acknwoledgment request
+ * @mrioc: Adapter instance reference
+ * @event: MPI3 event id
+ * @cmdparam: Internal command tracker
+ * @event_ctx: event context
+ *
+ * Issues event acknowledgment request to the firmware if there
+ * is a free command to send the event ack else it to a pend
+ * list so that it will be processed on a completion of a prior
+ * event acknowledgment .
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
+ struct mpi3mr_drv_cmd *cmdparam, u32 event_ctx)
+{
+ struct mpi3_event_ack_request evtack_req;
+ int retval = 0;
+ u8 retrycount = 5;
+ u16 cmd_idx = MPI3MR_NUM_EVTACKCMD;
+ struct mpi3mr_drv_cmd *drv_cmd = cmdparam;
+ struct delayed_evt_ack_node *delayed_evtack = NULL;
+
+ if (drv_cmd) {
+ dprint_event_th(mrioc,
+ "sending delayed event ack in the top half for event(0x%02x), event_ctx(0x%08x)\n",
+ event, event_ctx);
+ goto issue_cmd;
+ }
+ dprint_event_th(mrioc,
+ "sending event ack in the top half for event(0x%02x), event_ctx(0x%08x)\n",
+ event, event_ctx);
+ do {
+ cmd_idx = find_first_zero_bit(mrioc->evtack_cmds_bitmap,
+ MPI3MR_NUM_EVTACKCMD);
+ if (cmd_idx < MPI3MR_NUM_EVTACKCMD) {
+ if (!test_and_set_bit(cmd_idx,
+ mrioc->evtack_cmds_bitmap))
+ break;
+ cmd_idx = MPI3MR_NUM_EVTACKCMD;
+ }
+ } while (retrycount--);
+
+ if (cmd_idx >= MPI3MR_NUM_EVTACKCMD) {
+ delayed_evtack = kzalloc(sizeof(*delayed_evtack),
+ GFP_ATOMIC);
+ if (!delayed_evtack)
+ return;
+ INIT_LIST_HEAD(&delayed_evtack->list);
+ delayed_evtack->event = event;
+ delayed_evtack->event_ctx = event_ctx;
+ list_add_tail(&delayed_evtack->list,
+ &mrioc->delayed_evtack_cmds_list);
+ dprint_event_th(mrioc,
+ "event ack in the top half for event(0x%02x), event_ctx(0x%08x) is postponed\n",
+ event, event_ctx);
+ return;
+ }
+ drv_cmd = &mrioc->evtack_cmds[cmd_idx];
+
+issue_cmd:
+ cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_EVTACKCMD_MIN;
+
+ memset(&evtack_req, 0, sizeof(evtack_req));
+ if (drv_cmd->state & MPI3MR_CMD_PENDING) {
+ dprint_event_th(mrioc,
+ "sending event ack failed due to command in use\n");
+ goto out;
+ }
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 0;
+ drv_cmd->callback = mpi3mr_complete_evt_ack;
+ evtack_req.host_tag = cpu_to_le16(drv_cmd->host_tag);
+ evtack_req.function = MPI3_FUNCTION_EVENT_ACK;
+ evtack_req.event = event;
+ evtack_req.event_context = cpu_to_le32(event_ctx);
+ retval = mpi3mr_admin_request_post(mrioc, &evtack_req,
+ sizeof(evtack_req), 1);
+ if (retval) {
+ dprint_event_th(mrioc,
+ "posting event ack request is failed\n");
+ goto out_failed;
+ }
+
+ dprint_event_th(mrioc,
+ "event ack in the top half for event(0x%02x), event_ctx(0x%08x) is posted\n",
+ event, event_ctx);
+out:
+ return;
+out_failed:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ clear_bit(cmd_idx, mrioc->evtack_cmds_bitmap);
+}
+
+/**
+ * mpi3mr_pcietopochg_evt_th - PCIETopologyChange evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Checks for the reason code and based on that either block I/O
+ * to device, or unblock I/O to the device, or start the device
+ * removal handshake with reason as remove with the firmware for
+ * PCIe devices.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_pcietopochg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_pcie_topology_change_list *topo_evt =
+ (struct mpi3_event_data_pcie_topology_change_list *)event_reply->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+
+ for (i = 0; i < topo_evt->num_entries; i++) {
+ handle = le16_to_cpu(topo_evt->port_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ reason_code = topo_evt->port_entry[i].port_status;
+ scsi_tgt_priv_data = NULL;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata)
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ switch (reason_code) {
+ case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removed = 1;
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ }
+ mpi3mr_dev_rmhs_send_tm(mrioc, handle, NULL,
+ MPI3_CTRL_OP_REMOVE_DEVICE);
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removedelay = 1;
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+ }
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_RESPONDING:
+ if (scsi_tgt_priv_data &&
+ scsi_tgt_priv_data->dev_removedelay) {
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_dec_if_positive
+ (&scsi_tgt_priv_data->block_io);
+ }
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * mpi3mr_sastopochg_evt_th - SASTopologyChange evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Checks for the reason code and based on that either block I/O
+ * to device, or unblock I/O to the device, or start the device
+ * removal handshake with reason as remove with the firmware for
+ * SAS/SATA devices.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_sastopochg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_sas_topology_change_list *topo_evt =
+ (struct mpi3_event_data_sas_topology_change_list *)event_reply->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+
+ for (i = 0; i < topo_evt->num_entries; i++) {
+ handle = le16_to_cpu(topo_evt->phy_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ reason_code = topo_evt->phy_entry[i].status &
+ MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;
+ scsi_tgt_priv_data = NULL;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata)
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ switch (reason_code) {
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removed = 1;
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ }
+ mpi3mr_dev_rmhs_send_tm(mrioc, handle, NULL,
+ MPI3_CTRL_OP_REMOVE_DEVICE);
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removedelay = 1;
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+ }
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING:
+ if (scsi_tgt_priv_data &&
+ scsi_tgt_priv_data->dev_removedelay) {
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_dec_if_positive
+ (&scsi_tgt_priv_data->block_io);
+ }
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED:
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * mpi3mr_devstatuschg_evt_th - DeviceStatusChange evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Checks for the reason code and based on that either block I/O
+ * to device, or unblock I/O to the device, or start the device
+ * removal handshake with reason as remove/hide acknowledgment
+ * with the firmware.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_devstatuschg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ u16 dev_handle = 0;
+ u8 ublock = 0, block = 0, hide = 0, delete = 0, remove = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+ struct mpi3_event_data_device_status_change *evtdata =
+ (struct mpi3_event_data_device_status_change *)event_reply->event_data;
+
+ if (mrioc->stop_drv_processing)
+ goto out;
+
+ dev_handle = le16_to_cpu(evtdata->dev_handle);
+ dprint_event_th(mrioc,
+ "device status change event top half with rc(0x%02x) for handle(0x%04x)\n",
+ evtdata->reason_code, dev_handle);
+
+ switch (evtdata->reason_code) {
+ case MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_STRT:
+ case MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_STRT:
+ block = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_HIDDEN:
+ delete = 1;
+ hide = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING:
+ delete = 1;
+ remove = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_CMP:
+ case MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_CMP:
+ ublock = 1;
+ break;
+ default:
+ break;
+ }
+
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (!tgtdev) {
+ dprint_event_th(mrioc,
+ "processing device status change event could not identify device for handle(0x%04x)\n",
+ dev_handle);
+ goto out;
+ }
+ if (hide)
+ tgtdev->is_hidden = hide;
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ if (block)
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+ if (delete)
+ scsi_tgt_priv_data->dev_removed = 1;
+ if (ublock)
+ atomic_dec_if_positive(&scsi_tgt_priv_data->block_io);
+ }
+ if (remove)
+ mpi3mr_dev_rmhs_send_tm(mrioc, dev_handle, NULL,
+ MPI3_CTRL_OP_REMOVE_DEVICE);
+ if (hide)
+ mpi3mr_dev_rmhs_send_tm(mrioc, dev_handle, NULL,
+ MPI3_CTRL_OP_HIDDEN_ACK);
+
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+}
+
+/**
+ * mpi3mr_preparereset_evt_th - Prepare for reset event tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Blocks and unblocks host level I/O based on the reason code
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_preparereset_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_prepare_for_reset *evtdata =
+ (struct mpi3_event_data_prepare_for_reset *)event_reply->event_data;
+
+ if (evtdata->reason_code == MPI3_EVENT_PREPARE_RESET_RC_START) {
+ dprint_event_th(mrioc,
+ "prepare for reset event top half with rc=start\n");
+ if (mrioc->prepare_for_reset)
+ return;
+ scsi_block_requests(mrioc->shost);
+ mrioc->prepare_for_reset = 1;
+ mrioc->prepare_for_reset_timeout_counter = 0;
+ } else if (evtdata->reason_code == MPI3_EVENT_PREPARE_RESET_RC_ABORT) {
+ dprint_event_th(mrioc,
+ "prepare for reset top half with rc=abort\n");
+ mrioc->prepare_for_reset = 0;
+ scsi_unblock_requests(mrioc->shost);
+ mrioc->prepare_for_reset_timeout_counter = 0;
+ }
+ if ((event_reply->msg_flags & MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_MASK)
+ == MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_REQUIRED)
+ mpi3mr_send_event_ack(mrioc, event_reply->event, NULL,
+ le32_to_cpu(event_reply->event_context));
+}
+
+/**
+ * mpi3mr_energypackchg_evt_th - Energy pack change evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Identifies the new shutdown timeout value and update.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_energypackchg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_energy_pack_change *evtdata =
+ (struct mpi3_event_data_energy_pack_change *)event_reply->event_data;
+ u16 shutdown_timeout = le16_to_cpu(evtdata->shutdown_timeout);
+
+ if (shutdown_timeout <= 0) {
+ dprint_event_th(mrioc,
+ "%s :Invalid Shutdown Timeout received = %d\n",
+ __func__, shutdown_timeout);
+ return;
+ }
+
+ dprint_event_th(mrioc,
+ "%s :Previous Shutdown Timeout Value = %d New Shutdown Timeout Value = %d\n",
+ __func__, mrioc->facts.shutdown_timeout, shutdown_timeout);
+ mrioc->facts.shutdown_timeout = shutdown_timeout;
+}
+
+/**
+ * mpi3mr_cablemgmt_evt_th - Cable management event tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Displays Cable manegemt event details.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_cablemgmt_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_cable_management *evtdata =
+ (struct mpi3_event_data_cable_management *)event_reply->event_data;
+
+ switch (evtdata->status) {
+ case MPI3_EVENT_CABLE_MGMT_STATUS_INSUFFICIENT_POWER:
+ {
+ ioc_info(mrioc, "An active cable with receptacle_id %d cannot be powered.\n"
+ "Devices connected to this cable are not detected.\n"
+ "This cable requires %d mW of power.\n",
+ evtdata->receptacle_id,
+ le32_to_cpu(evtdata->active_cable_power_requirement));
+ break;
+ }
+ case MPI3_EVENT_CABLE_MGMT_STATUS_DEGRADED:
+ {
+ ioc_info(mrioc, "A cable with receptacle_id %d is not running at optimal speed\n",
+ evtdata->receptacle_id);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/**
+ * mpi3mr_add_event_wait_for_device_refresh - Add Wait for Device Refresh Event
+ * @mrioc: Adapter instance reference
+ *
+ * Add driver specific event to make sure that the driver won't process the
+ * events until all the devices are refreshed during soft reset.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_add_event_wait_for_device_refresh(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_fwevt *fwevt = NULL;
+
+ fwevt = mpi3mr_alloc_fwevt(0);
+ if (!fwevt) {
+ dprint_event_th(mrioc,
+ "failed to schedule bottom half handler for event(0x%02x)\n",
+ MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH);
+ return;
+ }
+ fwevt->mrioc = mrioc;
+ fwevt->event_id = MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH;
+ fwevt->send_ack = 0;
+ fwevt->process_evt = 1;
+ fwevt->evt_ctx = 0;
+ fwevt->event_data_size = 0;
+ mpi3mr_fwevt_add_to_list(mrioc, fwevt);
+}
+
+/**
+ * mpi3mr_os_handle_events - Firmware event handler
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Identifies whether the event has to be handled and acknowledged,
+ * and either processes the event in the top-half and/or schedule a
+ * bottom-half through mpi3mr_fwevt_worker().
+ *
+ * Return: Nothing
+ */
+void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ u16 evt_type, sz;
+ struct mpi3mr_fwevt *fwevt = NULL;
+ bool ack_req = 0, process_evt_bh = 0;
+
+ if (mrioc->stop_drv_processing)
+ return;
+
+ if ((event_reply->msg_flags & MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_MASK)
+ == MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_REQUIRED)
+ ack_req = 1;
+
+ evt_type = event_reply->event;
+ mpi3mr_event_trigger(mrioc, event_reply->event);
+
+ switch (evt_type) {
+ case MPI3_EVENT_DEVICE_ADDED:
+ {
+ struct mpi3_device_page0 *dev_pg0 =
+ (struct mpi3_device_page0 *)event_reply->event_data;
+ if (mpi3mr_create_tgtdev(mrioc, dev_pg0))
+ dprint_event_th(mrioc,
+ "failed to process device added event for handle(0x%04x),\n"
+ "perst_id(%d) in the event top half handler\n",
+ le16_to_cpu(dev_pg0->dev_handle),
+ le16_to_cpu(dev_pg0->persistent_id));
+ else
+ process_evt_bh = 1;
+ break;
+ }
+ case MPI3_EVENT_DEVICE_STATUS_CHANGE:
+ {
+ process_evt_bh = 1;
+ mpi3mr_devstatuschg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ {
+ process_evt_bh = 1;
+ mpi3mr_sastopochg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ {
+ process_evt_bh = 1;
+ mpi3mr_pcietopochg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_PREPARE_FOR_RESET:
+ {
+ mpi3mr_preparereset_evt_th(mrioc, event_reply);
+ ack_req = 0;
+ break;
+ }
+ case MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE:
+ {
+ mpi3mr_hdbstatuschg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_DEVICE_INFO_CHANGED:
+ case MPI3_EVENT_LOG_DATA:
+ case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
+ case MPI3_EVENT_ENCL_DEVICE_ADDED:
+ {
+ process_evt_bh = 1;
+ break;
+ }
+ case MPI3_EVENT_ENERGY_PACK_CHANGE:
+ {
+ mpi3mr_energypackchg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_CABLE_MGMT:
+ {
+ mpi3mr_cablemgmt_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_SAS_DISCOVERY:
+ case MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
+ case MPI3_EVENT_SAS_BROADCAST_PRIMITIVE:
+ case MPI3_EVENT_PCIE_ENUMERATION:
+ break;
+ default:
+ ioc_info(mrioc, "%s :event 0x%02x is not handled\n",
+ __func__, evt_type);
+ break;
+ }
+ if (process_evt_bh || ack_req) {
+ dprint_event_th(mrioc,
+ "scheduling bottom half handler for event(0x%02x) - (0x%08x), ack_required=%d\n",
+ evt_type, le32_to_cpu(event_reply->event_context), ack_req);
+ sz = event_reply->event_data_length * 4;
+ fwevt = mpi3mr_alloc_fwevt(sz);
+ if (!fwevt) {
+ dprint_event_th(mrioc,
+ "failed to schedule bottom half handler for\n"
+ "event(0x%02x), ack_required=%d\n", evt_type, ack_req);
+ return;
+ }
+
+ memcpy(fwevt->event_data, event_reply->event_data, sz);
+ fwevt->mrioc = mrioc;
+ fwevt->event_id = evt_type;
+ fwevt->send_ack = ack_req;
+ fwevt->process_evt = process_evt_bh;
+ fwevt->evt_ctx = le32_to_cpu(event_reply->event_context);
+ mpi3mr_fwevt_add_to_list(mrioc, fwevt);
+ }
+}
+
+/**
+ * mpi3mr_setup_eedp - Setup EEDP information in MPI3 SCSI IO
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ * @scsiio_req: MPI3 SCSI IO request
+ *
+ * Identifies the protection information flags from the SCSI
+ * command and set appropriate flags in the MPI3 SCSI IO
+ * request.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_setup_eedp(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req)
+{
+ u16 eedp_flags = 0;
+ unsigned char prot_op = scsi_get_prot_op(scmd);
+
+ switch (prot_op) {
+ case SCSI_PROT_NORMAL:
+ return;
+ case SCSI_PROT_READ_STRIP:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE;
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_INSERT;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_INSERT;
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ case SCSI_PROT_WRITE_STRIP:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE;
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ case SCSI_PROT_READ_PASS:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK;
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ case SCSI_PROT_WRITE_PASS:
+ if (scmd->prot_flags & SCSI_PROT_IP_CHECKSUM) {
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REGEN;
+ scsiio_req->sgl[0].eedp.application_tag_translation_mask =
+ 0xffff;
+ } else
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK;
+
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ default:
+ return;
+ }
+
+ if (scmd->prot_flags & SCSI_PROT_GUARD_CHECK)
+ eedp_flags |= MPI3_EEDPFLAGS_CHK_GUARD;
+
+ if (scmd->prot_flags & SCSI_PROT_IP_CHECKSUM)
+ eedp_flags |= MPI3_EEDPFLAGS_HOST_GUARD_IP_CHKSUM;
+
+ if (scmd->prot_flags & SCSI_PROT_REF_CHECK) {
+ eedp_flags |= MPI3_EEDPFLAGS_CHK_REF_TAG |
+ MPI3_EEDPFLAGS_INCR_PRI_REF_TAG;
+ scsiio_req->cdb.eedp32.primary_reference_tag =
+ cpu_to_be32(scsi_prot_ref_tag(scmd));
+ }
+
+ if (scmd->prot_flags & SCSI_PROT_REF_INCREMENT)
+ eedp_flags |= MPI3_EEDPFLAGS_INCR_PRI_REF_TAG;
+
+ eedp_flags |= MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE;
+
+ switch (scsi_prot_interval(scmd)) {
+ case 512:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_512;
+ break;
+ case 520:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_520;
+ break;
+ case 4080:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4080;
+ break;
+ case 4088:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4088;
+ break;
+ case 4096:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4096;
+ break;
+ case 4104:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4104;
+ break;
+ case 4160:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4160;
+ break;
+ default:
+ break;
+ }
+
+ scsiio_req->sgl[0].eedp.eedp_flags = cpu_to_le16(eedp_flags);
+ scsiio_req->sgl[0].eedp.flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED;
+}
+
+/**
+ * mpi3mr_build_sense_buffer - Map sense information
+ * @desc: Sense type
+ * @buf: Sense buffer to populate
+ * @key: Sense key
+ * @asc: Additional sense code
+ * @ascq: Additional sense code qualifier
+ *
+ * Maps the given sense information into either descriptor or
+ * fixed format sense data.
+ *
+ * Return: Nothing
+ */
+static inline void mpi3mr_build_sense_buffer(int desc, u8 *buf, u8 key,
+ u8 asc, u8 ascq)
+{
+ if (desc) {
+ buf[0] = 0x72; /* descriptor, current */
+ buf[1] = key;
+ buf[2] = asc;
+ buf[3] = ascq;
+ buf[7] = 0;
+ } else {
+ buf[0] = 0x70; /* fixed, current */
+ buf[2] = key;
+ buf[7] = 0xa;
+ buf[12] = asc;
+ buf[13] = ascq;
+ }
+}
+
+/**
+ * mpi3mr_map_eedp_error - Map EEDP errors from IOC status
+ * @scmd: SCSI command reference
+ * @ioc_status: status of MPI3 request
+ *
+ * Maps the EEDP error status of the SCSI IO request to sense
+ * data.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_map_eedp_error(struct scsi_cmnd *scmd,
+ u16 ioc_status)
+{
+ u8 ascq = 0;
+
+ switch (ioc_status) {
+ case MPI3_IOCSTATUS_EEDP_GUARD_ERROR:
+ ascq = 0x01;
+ break;
+ case MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ ascq = 0x02;
+ break;
+ case MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ ascq = 0x03;
+ break;
+ default:
+ ascq = 0x00;
+ break;
+ }
+
+ mpi3mr_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, ascq);
+ scmd->result = (DID_ABORT << 16) | SAM_STAT_CHECK_CONDITION;
+}
+
+/**
+ * mpi3mr_process_op_reply_desc - reply descriptor handler
+ * @mrioc: Adapter instance reference
+ * @reply_desc: Operational reply descriptor
+ * @reply_dma: place holder for reply DMA address
+ * @qidx: Operational queue index
+ *
+ * Process the operational reply descriptor and identifies the
+ * descriptor type. Based on the descriptor map the MPI3 request
+ * status to a SCSI command status and calls scsi_done call
+ * back.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma, u16 qidx)
+{
+ u16 reply_desc_type, host_tag = 0;
+ u16 ioc_status = MPI3_IOCSTATUS_SUCCESS;
+ u32 ioc_loginfo = 0;
+ struct mpi3_status_reply_descriptor *status_desc = NULL;
+ struct mpi3_address_reply_descriptor *addr_desc = NULL;
+ struct mpi3_success_reply_descriptor *success_desc = NULL;
+ struct mpi3_scsi_io_reply *scsi_reply = NULL;
+ struct scsi_cmnd *scmd = NULL;
+ struct scmd_priv *priv = NULL;
+ u8 *sense_buf = NULL;
+ u8 scsi_state = 0, scsi_status = 0, sense_state = 0;
+ u32 xfer_count = 0, sense_count = 0, resp_data = 0;
+ u16 dev_handle = 0xFFFF;
+ struct scsi_sense_hdr sshdr;
+ struct mpi3mr_stgt_priv_data *stgt_priv_data = NULL;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data = NULL;
+ u32 ioc_pend_data_len = 0, tg_pend_data_len = 0, data_len_blks = 0;
+ struct mpi3mr_throttle_group_info *tg = NULL;
+ u8 throttle_enabled_dev = 0;
+
+ *reply_dma = 0;
+ reply_desc_type = le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK;
+ switch (reply_desc_type) {
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS:
+ status_desc = (struct mpi3_status_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(status_desc->host_tag);
+ ioc_status = le16_to_cpu(status_desc->ioc_status);
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info);
+ ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ mpi3mr_reply_trigger(mrioc, ioc_status, ioc_loginfo);
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY:
+ addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc;
+ *reply_dma = le64_to_cpu(addr_desc->reply_frame_address);
+ scsi_reply = mpi3mr_get_reply_virt_addr(mrioc,
+ *reply_dma);
+ if (!scsi_reply) {
+ panic("%s: scsi_reply is NULL, this shouldn't happen\n",
+ mrioc->name);
+ goto out;
+ }
+ host_tag = le16_to_cpu(scsi_reply->host_tag);
+ ioc_status = le16_to_cpu(scsi_reply->ioc_status);
+ scsi_status = scsi_reply->scsi_status;
+ scsi_state = scsi_reply->scsi_state;
+ dev_handle = le16_to_cpu(scsi_reply->dev_handle);
+ sense_state = (scsi_state & MPI3_SCSI_STATE_SENSE_MASK);
+ xfer_count = le32_to_cpu(scsi_reply->transfer_count);
+ sense_count = le32_to_cpu(scsi_reply->sense_count);
+ resp_data = le32_to_cpu(scsi_reply->response_data);
+ sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(scsi_reply->ioc_log_info);
+ ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ if (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY)
+ panic("%s: Ran out of sense buffers\n", mrioc->name);
+ if (sense_buf) {
+ scsi_normalize_sense(sense_buf, sense_count, &sshdr);
+ mpi3mr_scsisense_trigger(mrioc, sshdr.sense_key,
+ sshdr.asc, sshdr.ascq);
+ }
+ mpi3mr_reply_trigger(mrioc, ioc_status, ioc_loginfo);
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS:
+ success_desc = (struct mpi3_success_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(success_desc->host_tag);
+ break;
+ default:
+ break;
+ }
+ scmd = mpi3mr_scmd_from_host_tag(mrioc, host_tag, qidx);
+ if (!scmd) {
+ panic("%s: Cannot Identify scmd for host_tag 0x%x\n",
+ mrioc->name, host_tag);
+ goto out;
+ }
+ priv = scsi_cmd_priv(scmd);
+
+ data_len_blks = scsi_bufflen(scmd) >> 9;
+ sdev_priv_data = scmd->device->hostdata;
+ if (sdev_priv_data) {
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ if (stgt_priv_data) {
+ tg = stgt_priv_data->throttle_group;
+ throttle_enabled_dev =
+ stgt_priv_data->io_throttle_enabled;
+ dev_handle = stgt_priv_data->dev_handle;
+ }
+ }
+ if (unlikely((data_len_blks >= mrioc->io_throttle_data_length) &&
+ throttle_enabled_dev)) {
+ ioc_pend_data_len = atomic_sub_return(data_len_blks,
+ &mrioc->pend_large_data_sz);
+ if (tg) {
+ tg_pend_data_len = atomic_sub_return(data_len_blks,
+ &tg->pend_large_data_sz);
+ if (tg->io_divert && ((ioc_pend_data_len <=
+ mrioc->io_throttle_low) &&
+ (tg_pend_data_len <= tg->low))) {
+ tg->io_divert = 0;
+ mpi3mr_set_io_divert_for_all_vd_in_tg(
+ mrioc, tg, 0);
+ }
+ } else {
+ if (ioc_pend_data_len <= mrioc->io_throttle_low)
+ stgt_priv_data->io_divert = 0;
+ }
+ } else if (unlikely((stgt_priv_data && stgt_priv_data->io_divert))) {
+ ioc_pend_data_len = atomic_read(&mrioc->pend_large_data_sz);
+ if (!tg) {
+ if (ioc_pend_data_len <= mrioc->io_throttle_low)
+ stgt_priv_data->io_divert = 0;
+
+ } else if (ioc_pend_data_len <= mrioc->io_throttle_low) {
+ tg_pend_data_len = atomic_read(&tg->pend_large_data_sz);
+ if (tg->io_divert && (tg_pend_data_len <= tg->low)) {
+ tg->io_divert = 0;
+ mpi3mr_set_io_divert_for_all_vd_in_tg(
+ mrioc, tg, 0);
+ }
+ }
+ }
+
+ if (success_desc) {
+ scmd->result = DID_OK << 16;
+ goto out_success;
+ }
+
+ scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_count);
+ if (ioc_status == MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN &&
+ xfer_count == 0 && (scsi_status == MPI3_SCSI_STATUS_BUSY ||
+ scsi_status == MPI3_SCSI_STATUS_RESERVATION_CONFLICT ||
+ scsi_status == MPI3_SCSI_STATUS_TASK_SET_FULL))
+ ioc_status = MPI3_IOCSTATUS_SUCCESS;
+
+ if ((sense_state == MPI3_SCSI_STATE_SENSE_VALID) && sense_count &&
+ sense_buf) {
+ u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, sense_count);
+
+ memcpy(scmd->sense_buffer, sense_buf, sz);
+ }
+
+ switch (ioc_status) {
+ case MPI3_IOCSTATUS_BUSY:
+ case MPI3_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ scmd->result = SAM_STAT_BUSY;
+ break;
+ case MPI3_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ scmd->result = DID_NO_CONNECT << 16;
+ break;
+ case MPI3_IOCSTATUS_SCSI_IOC_TERMINATED:
+ if (ioc_loginfo == IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR) {
+ /*
+ * This is a ATA NCQ command aborted due to another NCQ
+ * command failure. We must retry this command
+ * immediately but without incrementing its retry
+ * counter.
+ */
+ WARN_ON_ONCE(xfer_count != 0);
+ scmd->result = DID_IMM_RETRY << 16;
+ } else {
+ scmd->result = DID_SOFT_ERROR << 16;
+ }
+ break;
+ case MPI3_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI3_IOCSTATUS_SCSI_EXT_TERMINATED:
+ scmd->result = DID_RESET << 16;
+ break;
+ case MPI3_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ if ((xfer_count == 0) || (scmd->underflow > xfer_count))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else
+ scmd->result = (DID_OK << 16) | scsi_status;
+ break;
+ case MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ scmd->result = (DID_OK << 16) | scsi_status;
+ if (sense_state == MPI3_SCSI_STATE_SENSE_VALID)
+ break;
+ if (xfer_count < scmd->underflow) {
+ if (scsi_status == SAM_STAT_BUSY)
+ scmd->result = SAM_STAT_BUSY;
+ else
+ scmd->result = DID_SOFT_ERROR << 16;
+ } else if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) ||
+ (sense_state != MPI3_SCSI_STATE_SENSE_NOT_AVAILABLE))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI3_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ break;
+ case MPI3_IOCSTATUS_SCSI_DATA_OVERRUN:
+ scsi_set_resid(scmd, 0);
+ fallthrough;
+ case MPI3_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI3_IOCSTATUS_SUCCESS:
+ scmd->result = (DID_OK << 16) | scsi_status;
+ if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) ||
+ (sense_state == MPI3_SCSI_STATE_SENSE_FAILED) ||
+ (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI3_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ break;
+ case MPI3_IOCSTATUS_EEDP_GUARD_ERROR:
+ case MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ case MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ mpi3mr_map_eedp_error(scmd, ioc_status);
+ break;
+ case MPI3_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI3_IOCSTATUS_INVALID_FUNCTION:
+ case MPI3_IOCSTATUS_INVALID_SGL:
+ case MPI3_IOCSTATUS_INTERNAL_ERROR:
+ case MPI3_IOCSTATUS_INVALID_FIELD:
+ case MPI3_IOCSTATUS_INVALID_STATE:
+ case MPI3_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI3_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ case MPI3_IOCSTATUS_INSUFFICIENT_POWER:
+ default:
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+ }
+
+ if (scmd->result != (DID_OK << 16) && (scmd->cmnd[0] != ATA_12) &&
+ (scmd->cmnd[0] != ATA_16) &&
+ mrioc->logging_level & MPI3_DEBUG_SCSI_ERROR) {
+ ioc_info(mrioc, "%s :scmd->result 0x%x\n", __func__,
+ scmd->result);
+ scsi_print_command(scmd);
+ ioc_info(mrioc,
+ "%s :Command issued to handle 0x%02x returned with error 0x%04x loginfo 0x%08x, qid %d\n",
+ __func__, dev_handle, ioc_status, ioc_loginfo,
+ priv->req_q_idx + 1);
+ ioc_info(mrioc,
+ " host_tag %d scsi_state 0x%02x scsi_status 0x%02x, xfer_cnt %d resp_data 0x%x\n",
+ host_tag, scsi_state, scsi_status, xfer_count, resp_data);
+ if (sense_buf) {
+ scsi_normalize_sense(sense_buf, sense_count, &sshdr);
+ ioc_info(mrioc,
+ "%s :sense_count 0x%x, sense_key 0x%x ASC 0x%x, ASCQ 0x%x\n",
+ __func__, sense_count, sshdr.sense_key,
+ sshdr.asc, sshdr.ascq);
+ }
+ }
+out_success:
+ if (priv->meta_sg_valid) {
+ dma_unmap_sg(&mrioc->pdev->dev, scsi_prot_sglist(scmd),
+ scsi_prot_sg_count(scmd), scmd->sc_data_direction);
+ }
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ scsi_dma_unmap(scmd);
+ scsi_done(scmd);
+out:
+ if (sense_buf)
+ mpi3mr_repost_sense_buf(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+}
+
+/**
+ * mpi3mr_get_chain_idx - get free chain buffer index
+ * @mrioc: Adapter instance reference
+ *
+ * Try to get a free chain buffer index from the free pool.
+ *
+ * Return: -1 on failure or the free chain buffer index
+ */
+static int mpi3mr_get_chain_idx(struct mpi3mr_ioc *mrioc)
+{
+ u8 retry_count = 5;
+ int cmd_idx = -1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->chain_buf_lock, flags);
+ do {
+ cmd_idx = find_first_zero_bit(mrioc->chain_bitmap,
+ mrioc->chain_buf_count);
+ if (cmd_idx < mrioc->chain_buf_count) {
+ set_bit(cmd_idx, mrioc->chain_bitmap);
+ break;
+ }
+ cmd_idx = -1;
+ } while (retry_count--);
+ spin_unlock_irqrestore(&mrioc->chain_buf_lock, flags);
+ return cmd_idx;
+}
+
+/**
+ * mpi3mr_prepare_sg_scmd - build scatter gather list
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ * @scsiio_req: MPI3 SCSI IO request
+ *
+ * This function maps SCSI command's data and protection SGEs to
+ * MPI request SGEs. If required additional 4K chain buffer is
+ * used to send the SGEs.
+ *
+ * Return: 0 on success, -ENOMEM on dma_map_sg failure
+ */
+static int mpi3mr_prepare_sg_scmd(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req)
+{
+ dma_addr_t chain_dma;
+ struct scatterlist *sg_scmd;
+ void *sg_local, *chain;
+ u32 chain_length;
+ int sges_left, chain_idx;
+ u32 sges_in_segment;
+ u8 simple_sgl_flags;
+ u8 simple_sgl_flags_last;
+ u8 last_chain_sgl_flags;
+ struct chain_element *chain_req;
+ struct scmd_priv *priv = NULL;
+ u32 meta_sg = le32_to_cpu(scsiio_req->flags) &
+ MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI;
+
+ priv = scsi_cmd_priv(scmd);
+
+ simple_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE |
+ MPI3_SGE_FLAGS_DLAS_SYSTEM;
+ simple_sgl_flags_last = simple_sgl_flags |
+ MPI3_SGE_FLAGS_END_OF_LIST;
+ last_chain_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN |
+ MPI3_SGE_FLAGS_DLAS_SYSTEM;
+
+ if (meta_sg)
+ sg_local = &scsiio_req->sgl[MPI3_SCSIIO_METASGL_INDEX];
+ else
+ sg_local = &scsiio_req->sgl;
+
+ if (!scsiio_req->data_length && !meta_sg) {
+ mpi3mr_build_zero_len_sge(sg_local);
+ return 0;
+ }
+
+ if (meta_sg) {
+ sg_scmd = scsi_prot_sglist(scmd);
+ sges_left = dma_map_sg(&mrioc->pdev->dev,
+ scsi_prot_sglist(scmd),
+ scsi_prot_sg_count(scmd),
+ scmd->sc_data_direction);
+ priv->meta_sg_valid = 1; /* To unmap meta sg DMA */
+ } else {
+ /*
+ * Some firmware versions byte-swap the REPORT ZONES command
+ * reply from ATA-ZAC devices by directly accessing in the host
+ * buffer. This does not respect the default command DMA
+ * direction and causes IOMMU page faults on some architectures
+ * with an IOMMU enforcing write mappings (e.g. AMD hosts).
+ * Avoid such issue by making the REPORT ZONES buffer mapping
+ * bi-directional.
+ */
+ if (scmd->cmnd[0] == ZBC_IN && scmd->cmnd[1] == ZI_REPORT_ZONES)
+ scmd->sc_data_direction = DMA_BIDIRECTIONAL;
+ sg_scmd = scsi_sglist(scmd);
+ sges_left = scsi_dma_map(scmd);
+ }
+
+ if (sges_left < 0) {
+ sdev_printk(KERN_ERR, scmd->device,
+ "scsi_dma_map failed: request for %d bytes!\n",
+ scsi_bufflen(scmd));
+ return -ENOMEM;
+ }
+ if (sges_left > mrioc->max_sgl_entries) {
+ sdev_printk(KERN_ERR, scmd->device,
+ "scsi_dma_map returned unsupported sge count %d!\n",
+ sges_left);
+ return -ENOMEM;
+ }
+
+ sges_in_segment = (mrioc->facts.op_req_sz -
+ offsetof(struct mpi3_scsi_io_request, sgl)) / sizeof(struct mpi3_sge_common);
+
+ if (scsiio_req->sgl[0].eedp.flags ==
+ MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED && !meta_sg) {
+ sg_local += sizeof(struct mpi3_sge_common);
+ sges_in_segment--;
+ /* Reserve 1st segment (scsiio_req->sgl[0]) for eedp */
+ }
+
+ if (scsiio_req->msg_flags ==
+ MPI3_SCSIIO_MSGFLAGS_METASGL_VALID && !meta_sg) {
+ sges_in_segment--;
+ /* Reserve last segment (scsiio_req->sgl[3]) for meta sg */
+ }
+
+ if (meta_sg)
+ sges_in_segment = 1;
+
+ if (sges_left <= sges_in_segment)
+ goto fill_in_last_segment;
+
+ /* fill in main message segment when there is a chain following */
+ while (sges_in_segment > 1) {
+ mpi3mr_add_sg_single(sg_local, simple_sgl_flags,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += sizeof(struct mpi3_sge_common);
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ chain_idx = mpi3mr_get_chain_idx(mrioc);
+ if (chain_idx < 0)
+ return -1;
+ chain_req = &mrioc->chain_sgl_list[chain_idx];
+ if (meta_sg)
+ priv->meta_chain_idx = chain_idx;
+ else
+ priv->chain_idx = chain_idx;
+
+ chain = chain_req->addr;
+ chain_dma = chain_req->dma_addr;
+ sges_in_segment = sges_left;
+ chain_length = sges_in_segment * sizeof(struct mpi3_sge_common);
+
+ mpi3mr_add_sg_single(sg_local, last_chain_sgl_flags,
+ chain_length, chain_dma);
+
+ sg_local = chain;
+
+fill_in_last_segment:
+ while (sges_left > 0) {
+ if (sges_left == 1)
+ mpi3mr_add_sg_single(sg_local,
+ simple_sgl_flags_last, sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ mpi3mr_add_sg_single(sg_local, simple_sgl_flags,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += sizeof(struct mpi3_sge_common);
+ sges_left--;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_build_sg_scmd - build scatter gather list for SCSI IO
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ * @scsiio_req: MPI3 SCSI IO request
+ *
+ * This function calls mpi3mr_prepare_sg_scmd for constructing
+ * both data SGEs and protection information SGEs in the MPI
+ * format from the SCSI Command as appropriate .
+ *
+ * Return: return value of mpi3mr_prepare_sg_scmd.
+ */
+static int mpi3mr_build_sg_scmd(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req)
+{
+ int ret;
+
+ ret = mpi3mr_prepare_sg_scmd(mrioc, scmd, scsiio_req);
+ if (ret)
+ return ret;
+
+ if (scsiio_req->msg_flags == MPI3_SCSIIO_MSGFLAGS_METASGL_VALID) {
+ /* There is a valid meta sg */
+ scsiio_req->flags |=
+ cpu_to_le32(MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI);
+ ret = mpi3mr_prepare_sg_scmd(mrioc, scmd, scsiio_req);
+ }
+
+ return ret;
+}
+
+/**
+ * mpi3mr_tm_response_name - get TM response as a string
+ * @resp_code: TM response code
+ *
+ * Convert known task management response code as a readable
+ * string.
+ *
+ * Return: response code string.
+ */
+static const char *mpi3mr_tm_response_name(u8 resp_code)
+{
+ char *desc;
+
+ switch (resp_code) {
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_COMPLETE:
+ desc = "task management request completed";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_INVALID_FRAME:
+ desc = "invalid frame";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_FUNCTION_NOT_SUPPORTED:
+ desc = "task management request not supported";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_FAILED:
+ desc = "task management request failed";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_SUCCEEDED:
+ desc = "task management request succeeded";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_INVALID_LUN:
+ desc = "invalid LUN";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_OVERLAPPED_TAG:
+ desc = "overlapped tag attempted";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC:
+ desc = "task queued, however not sent to target";
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_NVME_DENIED:
+ desc = "task management request denied by NVMe device";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+
+ return desc;
+}
+
+inline void mpi3mr_poll_pend_io_completions(struct mpi3mr_ioc *mrioc)
+{
+ int i;
+ int num_of_reply_queues =
+ mrioc->num_op_reply_q + mrioc->op_reply_q_offset;
+
+ for (i = mrioc->op_reply_q_offset; i < num_of_reply_queues; i++)
+ mpi3mr_process_op_reply_q(mrioc,
+ mrioc->intr_info[i].op_reply_q);
+}
+
+/**
+ * mpi3mr_issue_tm - Issue Task Management request
+ * @mrioc: Adapter instance reference
+ * @tm_type: Task Management type
+ * @handle: Device handle
+ * @lun: lun ID
+ * @htag: Host tag of the TM request
+ * @timeout: TM timeout value
+ * @drv_cmd: Internal command tracker
+ * @resp_code: Response code place holder
+ * @scmd: SCSI command
+ *
+ * Issues a Task Management Request to the controller for a
+ * specified target, lun and command and wait for its completion
+ * and check TM response. Recover the TM if it timed out by
+ * issuing controller reset.
+ *
+ * Return: 0 on success, non-zero on errors
+ */
+int mpi3mr_issue_tm(struct mpi3mr_ioc *mrioc, u8 tm_type,
+ u16 handle, uint lun, u16 htag, ulong timeout,
+ struct mpi3mr_drv_cmd *drv_cmd,
+ u8 *resp_code, struct scsi_cmnd *scmd)
+{
+ struct mpi3_scsi_task_mgmt_request tm_req;
+ struct mpi3_scsi_task_mgmt_reply *tm_reply = NULL;
+ int retval = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+ struct scmd_priv *cmd_priv = NULL;
+ struct scsi_device *sdev = NULL;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data = NULL;
+
+ ioc_info(mrioc, "%s :Issue TM: TM type (0x%x) for devhandle 0x%04x\n",
+ __func__, tm_type, handle);
+ if (mrioc->unrecoverable) {
+ retval = -1;
+ ioc_err(mrioc, "%s :Issue TM: Unrecoverable controller\n",
+ __func__);
+ goto out;
+ }
+
+ memset(&tm_req, 0, sizeof(tm_req));
+ mutex_lock(&drv_cmd->mutex);
+ if (drv_cmd->state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "%s :Issue TM: Command is in use\n", __func__);
+ mutex_unlock(&drv_cmd->mutex);
+ goto out;
+ }
+ if (mrioc->reset_in_progress) {
+ retval = -1;
+ ioc_err(mrioc, "%s :Issue TM: Reset in progress\n", __func__);
+ mutex_unlock(&drv_cmd->mutex);
+ goto out;
+ }
+ if (mrioc->block_on_pci_err) {
+ retval = -1;
+ dprint_tm(mrioc, "sending task management failed due to\n"
+ "pci error recovery in progress\n");
+ mutex_unlock(&drv_cmd->mutex);
+ goto out;
+ }
+
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 1;
+ drv_cmd->callback = NULL;
+ tm_req.dev_handle = cpu_to_le16(handle);
+ tm_req.task_type = tm_type;
+ tm_req.host_tag = cpu_to_le16(htag);
+
+ int_to_scsilun(lun, (struct scsi_lun *)tm_req.lun);
+ tm_req.function = MPI3_FUNCTION_SCSI_TASK_MGMT;
+
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+
+ if (scmd) {
+ if (tm_type == MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+ cmd_priv = scsi_cmd_priv(scmd);
+ if (!cmd_priv)
+ goto out_unlock;
+
+ struct op_req_qinfo *op_req_q;
+
+ op_req_q = &mrioc->req_qinfo[cmd_priv->req_q_idx];
+ tm_req.task_host_tag = cpu_to_le16(cmd_priv->host_tag);
+ tm_req.task_request_queue_id =
+ cpu_to_le16(op_req_q->qid);
+ }
+ sdev = scmd->device;
+ sdev_priv_data = sdev->hostdata;
+ scsi_tgt_priv_data = ((sdev_priv_data) ?
+ sdev_priv_data->tgt_priv_data : NULL);
+ } else {
+ if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata)
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ }
+
+ if (scsi_tgt_priv_data)
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+
+ if (tgtdev) {
+ if (tgtdev->dev_type == MPI3_DEVICE_DEVFORM_PCIE)
+ timeout = cmd_priv ? tgtdev->dev_spec.pcie_inf.abort_to
+ : tgtdev->dev_spec.pcie_inf.reset_to;
+ else if (tgtdev->dev_type == MPI3_DEVICE_DEVFORM_VD)
+ timeout = cmd_priv ? tgtdev->dev_spec.vd_inf.abort_to
+ : tgtdev->dev_spec.vd_inf.reset_to;
+ }
+
+ init_completion(&drv_cmd->done);
+ retval = mpi3mr_admin_request_post(mrioc, &tm_req, sizeof(tm_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "%s :Issue TM: Admin Post failed\n", __func__);
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&drv_cmd->done, (timeout * HZ));
+
+ if (!(drv_cmd->state & MPI3MR_CMD_COMPLETE)) {
+ drv_cmd->is_waiting = 0;
+ retval = -1;
+ if (!(drv_cmd->state & MPI3MR_CMD_RESET)) {
+ dprint_tm(mrioc,
+ "task management request timed out after %ld seconds\n",
+ timeout);
+ if (mrioc->logging_level & MPI3_DEBUG_TM)
+ dprint_dump_req(&tm_req, sizeof(tm_req)/4);
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_TM_TIMEOUT, 1);
+ }
+ goto out_unlock;
+ }
+
+ if (!(drv_cmd->state & MPI3MR_CMD_REPLY_VALID)) {
+ dprint_tm(mrioc, "invalid task management reply message\n");
+ retval = -1;
+ goto out_unlock;
+ }
+
+ tm_reply = (struct mpi3_scsi_task_mgmt_reply *)drv_cmd->reply;
+
+ switch (drv_cmd->ioc_status) {
+ case MPI3_IOCSTATUS_SUCCESS:
+ *resp_code = le32_to_cpu(tm_reply->response_data) &
+ MPI3MR_RI_MASK_RESPCODE;
+ break;
+ case MPI3_IOCSTATUS_SCSI_IOC_TERMINATED:
+ *resp_code = MPI3_SCSITASKMGMT_RSPCODE_TM_COMPLETE;
+ break;
+ default:
+ dprint_tm(mrioc,
+ "task management request to handle(0x%04x) is failed with ioc_status(0x%04x) log_info(0x%08x)\n",
+ handle, drv_cmd->ioc_status, drv_cmd->ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+ switch (*resp_code) {
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_SUCCEEDED:
+ case MPI3_SCSITASKMGMT_RSPCODE_TM_COMPLETE:
+ break;
+ case MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC:
+ if (tm_type != MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
+ retval = -1;
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+
+ dprint_tm(mrioc,
+ "task management request type(%d) completed for handle(0x%04x) with ioc_status(0x%04x), log_info(0x%08x), termination_count(%d), response:%s(0x%x)\n",
+ tm_type, handle, drv_cmd->ioc_status, drv_cmd->ioc_loginfo,
+ le32_to_cpu(tm_reply->termination_count),
+ mpi3mr_tm_response_name(*resp_code), *resp_code);
+
+ if (!retval) {
+ mpi3mr_ioc_disable_intr(mrioc);
+ mpi3mr_poll_pend_io_completions(mrioc);
+ mpi3mr_ioc_enable_intr(mrioc);
+ mpi3mr_poll_pend_io_completions(mrioc);
+ mpi3mr_process_admin_reply_q(mrioc);
+ }
+ switch (tm_type) {
+ case MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ if (!scsi_tgt_priv_data)
+ break;
+ scsi_tgt_priv_data->pend_count = 0;
+ blk_mq_tagset_busy_iter(&mrioc->shost->tag_set,
+ mpi3mr_count_tgt_pending,
+ (void *)scsi_tgt_priv_data->starget);
+ break;
+ case MPI3_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET:
+ if (!sdev_priv_data)
+ break;
+ sdev_priv_data->pend_count = 0;
+ blk_mq_tagset_busy_iter(&mrioc->shost->tag_set,
+ mpi3mr_count_dev_pending, (void *)sdev);
+ break;
+ default:
+ break;
+ }
+ mpi3mr_global_trigger(mrioc,
+ MPI3_DRIVER2_GLOBALTRIGGER_TASK_MANAGEMENT_ENABLED);
+
+out_unlock:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&drv_cmd->mutex);
+ if (scsi_tgt_priv_data)
+ atomic_dec_if_positive(&scsi_tgt_priv_data->block_io);
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_bios_param - BIOS param callback
+ * @sdev: SCSI device reference
+ * @unused: gendisk reference
+ * @capacity: Capacity in logical sectors
+ * @params: Parameter array
+ *
+ * Just the parameters with heads/secots/cylinders.
+ *
+ * Return: 0 always
+ */
+static int mpi3mr_bios_param(struct scsi_device *sdev,
+ struct gendisk *unused, sector_t capacity, int params[])
+{
+ int heads;
+ int sectors;
+ sector_t cylinders;
+ ulong dummy;
+
+ heads = 64;
+ sectors = 32;
+
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+
+ if ((ulong)capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+ }
+
+ params[0] = heads;
+ params[1] = sectors;
+ params[2] = cylinders;
+ return 0;
+}
+
+/**
+ * mpi3mr_map_queues - Map queues callback handler
+ * @shost: SCSI host reference
+ *
+ * Maps default and poll queues.
+ *
+ * Return: return zero.
+ */
+static void mpi3mr_map_queues(struct Scsi_Host *shost)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ int i, qoff, offset;
+ struct blk_mq_queue_map *map = NULL;
+
+ offset = mrioc->op_reply_q_offset;
+
+ for (i = 0, qoff = 0; i < HCTX_MAX_TYPES; i++) {
+ map = &shost->tag_set.map[i];
+
+ map->nr_queues = 0;
+
+ if (i == HCTX_TYPE_DEFAULT)
+ map->nr_queues = mrioc->default_qcount;
+ else if (i == HCTX_TYPE_POLL)
+ map->nr_queues = mrioc->active_poll_qcount;
+
+ if (!map->nr_queues) {
+ BUG_ON(i == HCTX_TYPE_DEFAULT);
+ continue;
+ }
+
+ /*
+ * The poll queue(s) doesn't have an IRQ (and hence IRQ
+ * affinity), so use the regular blk-mq cpu mapping
+ */
+ map->queue_offset = qoff;
+ if (i != HCTX_TYPE_POLL)
+ blk_mq_map_hw_queues(map, &mrioc->pdev->dev, offset);
+ else
+ blk_mq_map_queues(map);
+
+ qoff += map->nr_queues;
+ offset += map->nr_queues;
+ }
+}
+
+/**
+ * mpi3mr_get_fw_pending_ios - Calculate pending I/O count
+ * @mrioc: Adapter instance reference
+ *
+ * Calculate the pending I/Os for the controller and return.
+ *
+ * Return: Number of pending I/Os
+ */
+static inline int mpi3mr_get_fw_pending_ios(struct mpi3mr_ioc *mrioc)
+{
+ u16 i;
+ uint pend_ios = 0;
+
+ for (i = 0; i < mrioc->num_op_reply_q; i++)
+ pend_ios += atomic_read(&mrioc->op_reply_qinfo[i].pend_ios);
+ return pend_ios;
+}
+
+/**
+ * mpi3mr_print_pending_host_io - print pending I/Os
+ * @mrioc: Adapter instance reference
+ *
+ * Print number of pending I/Os and each I/O details prior to
+ * reset for debug purpose.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_print_pending_host_io(struct mpi3mr_ioc *mrioc)
+{
+ struct Scsi_Host *shost = mrioc->shost;
+
+ ioc_info(mrioc, "%s :Pending commands prior to reset: %d\n",
+ __func__, mpi3mr_get_fw_pending_ios(mrioc));
+ blk_mq_tagset_busy_iter(&shost->tag_set,
+ mpi3mr_print_scmd, (void *)mrioc);
+}
+
+/**
+ * mpi3mr_wait_for_host_io - block for I/Os to complete
+ * @mrioc: Adapter instance reference
+ * @timeout: time out in seconds
+ * Waits for pending I/Os for the given adapter to complete or
+ * to hit the timeout.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout)
+{
+ enum mpi3mr_iocstate iocstate;
+ int i = 0;
+
+ iocstate = mpi3mr_get_iocstate(mrioc);
+ if (iocstate != MRIOC_STATE_READY)
+ return;
+
+ if (!mpi3mr_get_fw_pending_ios(mrioc))
+ return;
+ ioc_info(mrioc,
+ "%s :Waiting for %d seconds prior to reset for %d I/O\n",
+ __func__, timeout, mpi3mr_get_fw_pending_ios(mrioc));
+
+ for (i = 0; i < timeout; i++) {
+ if (!mpi3mr_get_fw_pending_ios(mrioc))
+ break;
+ iocstate = mpi3mr_get_iocstate(mrioc);
+ if (iocstate != MRIOC_STATE_READY)
+ break;
+ msleep(1000);
+ }
+
+ ioc_info(mrioc, "%s :Pending I/Os after wait is: %d\n", __func__,
+ mpi3mr_get_fw_pending_ios(mrioc));
+}
+
+/**
+ * mpi3mr_setup_divert_ws - Setup Divert IO flag for write same
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ * @scsiio_req: MPI3 SCSI IO request
+ * @scsiio_flags: Pointer to MPI3 SCSI IO Flags
+ * @wslen: write same max length
+ *
+ * Gets values of unmap, ndob and number of blocks from write
+ * same scsi io and based on these values it sets divert IO flag
+ * and reason for diverting IO to firmware.
+ *
+ * Return: Nothing
+ */
+static inline void mpi3mr_setup_divert_ws(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req,
+ u32 *scsiio_flags, u16 wslen)
+{
+ u8 unmap = 0, ndob = 0;
+ u8 opcode = scmd->cmnd[0];
+ u32 num_blocks = 0;
+ u16 sa = (scmd->cmnd[8] << 8) | (scmd->cmnd[9]);
+
+ if (opcode == WRITE_SAME_16) {
+ unmap = scmd->cmnd[1] & 0x08;
+ ndob = scmd->cmnd[1] & 0x01;
+ num_blocks = get_unaligned_be32(scmd->cmnd + 10);
+ } else if ((opcode == VARIABLE_LENGTH_CMD) && (sa == WRITE_SAME_32)) {
+ unmap = scmd->cmnd[10] & 0x08;
+ ndob = scmd->cmnd[10] & 0x01;
+ num_blocks = get_unaligned_be32(scmd->cmnd + 28);
+ } else
+ return;
+
+ if ((unmap) && (ndob) && (num_blocks > wslen)) {
+ scsiio_req->msg_flags |=
+ MPI3_SCSIIO_MSGFLAGS_DIVERT_TO_FIRMWARE;
+ *scsiio_flags |=
+ MPI3_SCSIIO_FLAGS_DIVERT_REASON_WRITE_SAME_TOO_LARGE;
+ }
+}
+
+/**
+ * mpi3mr_eh_host_reset - Host reset error handling callback
+ * @scmd: SCSI command reference
+ *
+ * Issue controller reset
+ *
+ * Return: SUCCESS of successful reset else FAILED
+ */
+static int mpi3mr_eh_host_reset(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ int retval = FAILED, ret;
+
+ ret = mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_EH_HOS, 1);
+ if (ret)
+ goto out;
+
+ retval = SUCCESS;
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "Host reset is %s for scmd(%p)\n",
+ ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_eh_bus_reset - Bus reset error handling callback
+ * @scmd: SCSI command reference
+ *
+ * Checks whether pending I/Os are present for the RAID volume;
+ * if not there's no need to reset the adapter.
+ *
+ * Return: SUCCESS of successful reset else FAILED
+ */
+static int mpi3mr_eh_bus_reset(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ u8 dev_type = MPI3_DEVICE_DEVFORM_VD;
+ int retval = FAILED;
+ unsigned int timeout = MPI3MR_RESET_TIMEOUT;
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (sdev_priv_data && sdev_priv_data->tgt_priv_data) {
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_type = stgt_priv_data->dev_type;
+ }
+
+ if (dev_type == MPI3_DEVICE_DEVFORM_VD) {
+ mpi3mr_wait_for_host_io(mrioc,
+ MPI3MR_RAID_ERRREC_RESET_TIMEOUT);
+ if (!mpi3mr_get_fw_pending_ios(mrioc)) {
+ while (mrioc->reset_in_progress ||
+ mrioc->prepare_for_reset ||
+ mrioc->block_on_pci_err) {
+ ssleep(1);
+ if (!timeout--) {
+ retval = FAILED;
+ goto out;
+ }
+ }
+ retval = SUCCESS;
+ goto out;
+ }
+ }
+ if (retval == FAILED)
+ mpi3mr_print_pending_host_io(mrioc);
+
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "Bus reset is %s for scmd(%p)\n",
+ ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ return retval;
+}
+
+/**
+ * mpi3mr_eh_target_reset - Target reset error handling callback
+ * @scmd: SCSI command reference
+ *
+ * Issue Target reset Task Management and verify the scmd is
+ * terminated successfully and return status accordingly.
+ *
+ * Return: SUCCESS of successful termination of the scmd else
+ * FAILED
+ */
+static int mpi3mr_eh_target_reset(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ u16 dev_handle;
+ u8 resp_code = 0;
+ int retval = FAILED, ret = 0;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "Attempting Target Reset! scmd(%p)\n", scmd);
+ scsi_print_command(scmd);
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "SCSI device is not available\n");
+ retval = SUCCESS;
+ goto out;
+ }
+
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_handle = stgt_priv_data->dev_handle;
+ if (stgt_priv_data->dev_removed) {
+ struct scmd_priv *cmd_priv = scsi_cmd_priv(scmd);
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s:target(handle = 0x%04x) is removed, target reset is not issued\n",
+ mrioc->name, dev_handle);
+ if (!cmd_priv->in_lld_scope || cmd_priv->host_tag == MPI3MR_HOSTTAG_INVALID)
+ retval = SUCCESS;
+ else
+ retval = FAILED;
+ goto out;
+ }
+ sdev_printk(KERN_INFO, scmd->device,
+ "Target Reset is issued to handle(0x%04x)\n",
+ dev_handle);
+
+ ret = mpi3mr_issue_tm(mrioc,
+ MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET, dev_handle,
+ sdev_priv_data->lun_id, MPI3MR_HOSTTAG_BLK_TMS,
+ MPI3MR_RESETTM_TIMEOUT, &mrioc->host_tm_cmds, &resp_code, scmd);
+
+ if (ret)
+ goto out;
+
+ if (stgt_priv_data->pend_count) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: target has %d pending commands, target reset is failed\n",
+ mrioc->name, stgt_priv_data->pend_count);
+ goto out;
+ }
+
+ retval = SUCCESS;
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: target reset is %s for scmd(%p)\n", mrioc->name,
+ ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_eh_dev_reset- Device reset error handling callback
+ * @scmd: SCSI command reference
+ *
+ * Issue lun reset Task Management and verify the scmd is
+ * terminated successfully and return status accordingly.
+ *
+ * Return: SUCCESS of successful termination of the scmd else
+ * FAILED
+ */
+static int mpi3mr_eh_dev_reset(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ u16 dev_handle;
+ u8 resp_code = 0;
+ int retval = FAILED, ret = 0;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "Attempting Device(lun) Reset! scmd(%p)\n", scmd);
+ scsi_print_command(scmd);
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "SCSI device is not available\n");
+ retval = SUCCESS;
+ goto out;
+ }
+
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_handle = stgt_priv_data->dev_handle;
+ if (stgt_priv_data->dev_removed) {
+ struct scmd_priv *cmd_priv = scsi_cmd_priv(scmd);
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: device(handle = 0x%04x) is removed, device(LUN) reset is not issued\n",
+ mrioc->name, dev_handle);
+ if (!cmd_priv->in_lld_scope || cmd_priv->host_tag == MPI3MR_HOSTTAG_INVALID)
+ retval = SUCCESS;
+ else
+ retval = FAILED;
+ goto out;
+ }
+ sdev_printk(KERN_INFO, scmd->device,
+ "Device(lun) Reset is issued to handle(0x%04x)\n", dev_handle);
+
+ ret = mpi3mr_issue_tm(mrioc,
+ MPI3_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, dev_handle,
+ sdev_priv_data->lun_id, MPI3MR_HOSTTAG_BLK_TMS,
+ MPI3MR_RESETTM_TIMEOUT, &mrioc->host_tm_cmds, &resp_code, scmd);
+
+ if (ret)
+ goto out;
+
+ if (sdev_priv_data->pend_count) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: device has %d pending commands, device(LUN) reset is failed\n",
+ mrioc->name, sdev_priv_data->pend_count);
+ goto out;
+ }
+ retval = SUCCESS;
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: device(LUN) reset is %s for scmd(%p)\n", mrioc->name,
+ ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_eh_abort - Callback function for abort error handling
+ * @scmd: SCSI command reference
+ *
+ * Issues Abort Task Management if the command is in LLD scope
+ * and verifies if it is aborted successfully, and return status
+ * accordingly.
+ *
+ * Return: SUCCESS if the abort was successful, otherwise FAILED
+ */
+static int mpi3mr_eh_abort(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ struct scmd_priv *cmd_priv;
+ u16 dev_handle, timeout = MPI3MR_ABORTTM_TIMEOUT;
+ u8 resp_code = 0;
+ int retval = FAILED, ret = 0;
+ struct request *rq = scsi_cmd_to_rq(scmd);
+ unsigned long scmd_age_ms = jiffies_to_msecs(jiffies - scmd->jiffies_at_alloc);
+ unsigned long scmd_age_sec = scmd_age_ms / HZ;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: attempting abort task for scmd(%p)\n", mrioc->name, scmd);
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: scmd(0x%p) is outstanding for %lus %lums, timeout %us, retries %d, allowed %d\n",
+ mrioc->name, scmd, scmd_age_sec, scmd_age_ms % HZ, rq->timeout / HZ,
+ scmd->retries, scmd->allowed);
+
+ scsi_print_command(scmd);
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: Device not available, Skip issuing abort task\n",
+ mrioc->name);
+ retval = SUCCESS;
+ goto out;
+ }
+
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_handle = stgt_priv_data->dev_handle;
+
+ cmd_priv = scsi_cmd_priv(scmd);
+ if (!cmd_priv->in_lld_scope ||
+ cmd_priv->host_tag == MPI3MR_HOSTTAG_INVALID) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: scmd (0x%p) not in LLD scope, Skip issuing Abort Task\n",
+ mrioc->name, scmd);
+ retval = SUCCESS;
+ goto out;
+ }
+
+ if (stgt_priv_data->dev_removed) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: Device (handle = 0x%04x) removed, Skip issuing Abort Task\n",
+ mrioc->name, dev_handle);
+ retval = FAILED;
+ goto out;
+ }
+
+ ret = mpi3mr_issue_tm(mrioc, MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
+ dev_handle, sdev_priv_data->lun_id, MPI3MR_HOSTTAG_BLK_TMS,
+ timeout, &mrioc->host_tm_cmds, &resp_code, scmd);
+
+ if (ret)
+ goto out;
+
+ if (cmd_priv->in_lld_scope) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: Abort task failed. scmd (0x%p) was not terminated\n",
+ mrioc->name, scmd);
+ goto out;
+ }
+
+ retval = SUCCESS;
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "%s: Abort Task %s for scmd (0x%p)\n", mrioc->name,
+ ((retval == SUCCESS) ? "SUCCEEDED" : "FAILED"), scmd);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_scan_start - Scan start callback handler
+ * @shost: SCSI host reference
+ *
+ * Issue port enable request asynchronously.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_scan_start(struct Scsi_Host *shost)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ mrioc->scan_started = 1;
+ ioc_info(mrioc, "%s :Issuing Port Enable\n", __func__);
+ if (mpi3mr_issue_port_enable(mrioc, 1)) {
+ ioc_err(mrioc, "%s :Issuing port enable failed\n", __func__);
+ mrioc->scan_started = 0;
+ mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR;
+ }
+}
+
+/**
+ * mpi3mr_scan_finished - Scan finished callback handler
+ * @shost: SCSI host reference
+ * @time: Jiffies from the scan start
+ *
+ * Checks whether the port enable is completed or timedout or
+ * failed and set the scan status accordingly after taking any
+ * recovery if required.
+ *
+ * Return: 1 on scan finished or timed out, 0 for in progress
+ */
+static int mpi3mr_scan_finished(struct Scsi_Host *shost,
+ unsigned long time)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT;
+ u32 ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) ||
+ (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)) {
+ ioc_err(mrioc, "port enable failed due to fault or reset\n");
+ mpi3mr_print_fault_info(mrioc);
+ mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR;
+ mrioc->scan_started = 0;
+ mrioc->init_cmds.is_waiting = 0;
+ mrioc->init_cmds.callback = NULL;
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ }
+
+ if (time >= (pe_timeout * HZ)) {
+ ioc_err(mrioc, "port enable failed due to time out\n");
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_PE_TIMEOUT);
+ mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR;
+ mrioc->scan_started = 0;
+ mrioc->init_cmds.is_waiting = 0;
+ mrioc->init_cmds.callback = NULL;
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ }
+
+ if (mrioc->scan_started)
+ return 0;
+
+ if (mrioc->scan_failed) {
+ ioc_err(mrioc,
+ "port enable failed with status=0x%04x\n",
+ mrioc->scan_failed);
+ } else
+ ioc_info(mrioc, "port enable is successfully completed\n");
+
+ mpi3mr_start_watchdog(mrioc);
+ mrioc->is_driver_loading = 0;
+ mrioc->stop_bsgs = 0;
+ return 1;
+}
+
+/**
+ * mpi3mr_sdev_destroy - Slave destroy callback handler
+ * @sdev: SCSI device reference
+ *
+ * Cleanup and free per device(lun) private data.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_sdev_destroy(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev = NULL;
+ unsigned long flags;
+ struct scsi_target *starget;
+ struct sas_rphy *rphy = NULL;
+
+ if (!sdev->hostdata)
+ return;
+
+ starget = scsi_target(sdev);
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+ scsi_tgt_priv_data = starget->hostdata;
+
+ scsi_tgt_priv_data->num_luns--;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ if (starget->channel == mrioc->scsi_device_channel)
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+ else if (mrioc->sas_transport_enabled && !starget->channel) {
+ rphy = dev_to_rphy(starget->dev.parent);
+ tgt_dev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
+ rphy->identify.sas_address, rphy);
+ }
+
+ if (tgt_dev && (!scsi_tgt_priv_data->num_luns))
+ tgt_dev->starget = NULL;
+ if (tgt_dev)
+ mpi3mr_tgtdev_put(tgt_dev);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ kfree(sdev->hostdata);
+ sdev->hostdata = NULL;
+}
+
+/**
+ * mpi3mr_target_destroy - Target destroy callback handler
+ * @starget: SCSI target reference
+ *
+ * Cleanup and free per target private data.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_target_destroy(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ unsigned long flags;
+
+ if (!starget->hostdata)
+ return;
+
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+ scsi_tgt_priv_data = starget->hostdata;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgt_dev = __mpi3mr_get_tgtdev_from_tgtpriv(mrioc, scsi_tgt_priv_data);
+ if (tgt_dev && (tgt_dev->starget == starget) &&
+ (tgt_dev->perst_id == starget->id))
+ tgt_dev->starget = NULL;
+ if (tgt_dev) {
+ scsi_tgt_priv_data->tgt_dev = NULL;
+ scsi_tgt_priv_data->perst_id = 0;
+ mpi3mr_tgtdev_put(tgt_dev);
+ mpi3mr_tgtdev_put(tgt_dev);
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ kfree(starget->hostdata);
+ starget->hostdata = NULL;
+}
+
+/**
+ * mpi3mr_sdev_configure - Slave configure callback handler
+ * @sdev: SCSI device reference
+ * @lim: queue limits
+ *
+ * Configure queue depth, max hardware sectors and virt boundary
+ * as required
+ *
+ * Return: 0 always.
+ */
+static int mpi3mr_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ struct scsi_target *starget;
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_tgt_dev *tgt_dev = NULL;
+ unsigned long flags;
+ int retval = 0;
+ struct sas_rphy *rphy = NULL;
+
+ starget = scsi_target(sdev);
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ if (starget->channel == mrioc->scsi_device_channel)
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+ else if (mrioc->sas_transport_enabled && !starget->channel) {
+ rphy = dev_to_rphy(starget->dev.parent);
+ tgt_dev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
+ rphy->identify.sas_address, rphy);
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ if (!tgt_dev)
+ return -ENXIO;
+
+ mpi3mr_change_queue_depth(sdev, tgt_dev->q_depth);
+
+ sdev->eh_timeout = MPI3MR_EH_SCMD_TIMEOUT;
+ blk_queue_rq_timeout(sdev->request_queue, MPI3MR_SCMD_TIMEOUT);
+
+ mpi3mr_configure_tgt_dev(tgt_dev, lim);
+ mpi3mr_tgtdev_put(tgt_dev);
+ return retval;
+}
+
+/**
+ * mpi3mr_sdev_init -Slave alloc callback handler
+ * @sdev: SCSI device reference
+ *
+ * Allocate per device(lun) private data and initialize it.
+ *
+ * Return: 0 on success -ENOMEM on memory allocation failure.
+ */
+static int mpi3mr_sdev_init(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev = NULL;
+ struct mpi3mr_sdev_priv_data *scsi_dev_priv_data;
+ unsigned long flags;
+ struct scsi_target *starget;
+ int retval = 0;
+ struct sas_rphy *rphy = NULL;
+
+ starget = scsi_target(sdev);
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+ scsi_tgt_priv_data = starget->hostdata;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+
+ if (starget->channel == mrioc->scsi_device_channel)
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+ else if (mrioc->sas_transport_enabled && !starget->channel) {
+ rphy = dev_to_rphy(starget->dev.parent);
+ tgt_dev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
+ rphy->identify.sas_address, rphy);
+ }
+
+ if (tgt_dev) {
+ if (tgt_dev->starget == NULL)
+ tgt_dev->starget = starget;
+ mpi3mr_tgtdev_put(tgt_dev);
+ retval = 0;
+ } else {
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ return -ENXIO;
+ }
+
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ scsi_dev_priv_data = kzalloc(sizeof(*scsi_dev_priv_data), GFP_KERNEL);
+ if (!scsi_dev_priv_data)
+ return -ENOMEM;
+
+ scsi_dev_priv_data->lun_id = sdev->lun;
+ scsi_dev_priv_data->tgt_priv_data = scsi_tgt_priv_data;
+ sdev->hostdata = scsi_dev_priv_data;
+
+ scsi_tgt_priv_data->num_luns++;
+
+ return retval;
+}
+
+/**
+ * mpi3mr_target_alloc - Target alloc callback handler
+ * @starget: SCSI target reference
+ *
+ * Allocate per target private data and initialize it.
+ *
+ * Return: 0 on success -ENOMEM on memory allocation failure.
+ */
+static int mpi3mr_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ unsigned long flags;
+ int retval = 0;
+ struct sas_rphy *rphy = NULL;
+
+ scsi_tgt_priv_data = kzalloc(sizeof(*scsi_tgt_priv_data), GFP_KERNEL);
+ if (!scsi_tgt_priv_data)
+ return -ENOMEM;
+
+ starget->hostdata = scsi_tgt_priv_data;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ if (starget->channel == mrioc->scsi_device_channel) {
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+ if (tgt_dev && !tgt_dev->is_hidden) {
+ scsi_tgt_priv_data->starget = starget;
+ scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle;
+ scsi_tgt_priv_data->perst_id = tgt_dev->perst_id;
+ scsi_tgt_priv_data->dev_type = tgt_dev->dev_type;
+ scsi_tgt_priv_data->tgt_dev = tgt_dev;
+ tgt_dev->starget = starget;
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ retval = 0;
+ if ((tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_PCIE) &&
+ ((tgt_dev->dev_spec.pcie_inf.dev_info &
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) ==
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE) &&
+ ((tgt_dev->dev_spec.pcie_inf.dev_info &
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_MASK) !=
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_0))
+ scsi_tgt_priv_data->dev_nvme_dif = 1;
+ scsi_tgt_priv_data->io_throttle_enabled = tgt_dev->io_throttle_enabled;
+ scsi_tgt_priv_data->wslen = tgt_dev->wslen;
+ if (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_VD)
+ scsi_tgt_priv_data->throttle_group = tgt_dev->dev_spec.vd_inf.tg;
+ } else
+ retval = -ENXIO;
+ } else if (mrioc->sas_transport_enabled && !starget->channel) {
+ rphy = dev_to_rphy(starget->dev.parent);
+ tgt_dev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
+ rphy->identify.sas_address, rphy);
+ if (tgt_dev && !tgt_dev->is_hidden && !tgt_dev->non_stl &&
+ (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA)) {
+ scsi_tgt_priv_data->starget = starget;
+ scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle;
+ scsi_tgt_priv_data->perst_id = tgt_dev->perst_id;
+ scsi_tgt_priv_data->dev_type = tgt_dev->dev_type;
+ scsi_tgt_priv_data->tgt_dev = tgt_dev;
+ scsi_tgt_priv_data->io_throttle_enabled = tgt_dev->io_throttle_enabled;
+ scsi_tgt_priv_data->wslen = tgt_dev->wslen;
+ tgt_dev->starget = starget;
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ retval = 0;
+ } else
+ retval = -ENXIO;
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_check_return_unmap - Whether an unmap is allowed
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI Command reference
+ *
+ * The controller hardware cannot handle certain unmap commands
+ * for NVMe drives, this routine checks those and return true
+ * and completes the SCSI command with proper status and sense
+ * data.
+ *
+ * Return: TRUE for not allowed unmap, FALSE otherwise.
+ */
+static bool mpi3mr_check_return_unmap(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd)
+{
+ unsigned char *buf;
+ u16 param_len, desc_len, trunc_param_len;
+
+ trunc_param_len = param_len = get_unaligned_be16(scmd->cmnd + 7);
+
+ if (mrioc->pdev->revision) {
+ if ((param_len > 24) && ((param_len - 8) & 0xF)) {
+ trunc_param_len -= (param_len - 8) & 0xF;
+ dprint_scsi_command(mrioc, scmd, MPI3_DEBUG_SCSI_ERROR);
+ dprint_scsi_err(mrioc,
+ "truncating param_len from (%d) to (%d)\n",
+ param_len, trunc_param_len);
+ put_unaligned_be16(trunc_param_len, scmd->cmnd + 7);
+ dprint_scsi_command(mrioc, scmd, MPI3_DEBUG_SCSI_ERROR);
+ }
+ return false;
+ }
+
+ if (!param_len) {
+ ioc_warn(mrioc,
+ "%s: cdb received with zero parameter length\n",
+ __func__);
+ scsi_print_command(scmd);
+ scmd->result = DID_OK << 16;
+ scsi_done(scmd);
+ return true;
+ }
+
+ if (param_len < 24) {
+ ioc_warn(mrioc,
+ "%s: cdb received with invalid param_len: %d\n",
+ __func__, param_len);
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x1A, 0);
+ scsi_done(scmd);
+ return true;
+ }
+ if (param_len != scsi_bufflen(scmd)) {
+ ioc_warn(mrioc,
+ "%s: cdb received with param_len: %d bufflen: %d\n",
+ __func__, param_len, scsi_bufflen(scmd));
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x1A, 0);
+ scsi_done(scmd);
+ return true;
+ }
+ buf = kzalloc(scsi_bufflen(scmd), GFP_ATOMIC);
+ if (!buf) {
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x55, 0x03);
+ scsi_done(scmd);
+ return true;
+ }
+ scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
+ desc_len = get_unaligned_be16(&buf[2]);
+
+ if (desc_len < 16) {
+ ioc_warn(mrioc,
+ "%s: Invalid descriptor length in param list: %d\n",
+ __func__, desc_len);
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x26, 0);
+ scsi_done(scmd);
+ kfree(buf);
+ return true;
+ }
+
+ if (param_len > (desc_len + 8)) {
+ trunc_param_len = desc_len + 8;
+ scsi_print_command(scmd);
+ dprint_scsi_err(mrioc,
+ "truncating param_len(%d) to desc_len+8(%d)\n",
+ param_len, trunc_param_len);
+ put_unaligned_be16(trunc_param_len, scmd->cmnd + 7);
+ scsi_print_command(scmd);
+ }
+
+ kfree(buf);
+ return false;
+}
+
+/**
+ * mpi3mr_allow_scmd_to_fw - Command is allowed during shutdown
+ * @scmd: SCSI Command reference
+ *
+ * Checks whether a cdb is allowed during shutdown or not.
+ *
+ * Return: TRUE for allowed commands, FALSE otherwise.
+ */
+
+inline bool mpi3mr_allow_scmd_to_fw(struct scsi_cmnd *scmd)
+{
+ switch (scmd->cmnd[0]) {
+ case SYNCHRONIZE_CACHE:
+ case START_STOP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * mpi3mr_qcmd - I/O request despatcher
+ * @shost: SCSI Host reference
+ * @scmd: SCSI Command reference
+ *
+ * Issues the SCSI Command as an MPI3 request.
+ *
+ * Return: 0 on successful queueing of the request or if the
+ * request is completed with failure.
+ * SCSI_MLQUEUE_DEVICE_BUSY when the device is busy.
+ * SCSI_MLQUEUE_HOST_BUSY when the host queue is full.
+ */
+static int mpi3mr_qcmd(struct Scsi_Host *shost,
+ struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ struct scmd_priv *scmd_priv_data = NULL;
+ struct mpi3_scsi_io_request *scsiio_req = NULL;
+ struct op_req_qinfo *op_req_q = NULL;
+ int retval = 0;
+ u16 dev_handle;
+ u16 host_tag;
+ u32 scsiio_flags = 0, data_len_blks = 0;
+ struct request *rq = scsi_cmd_to_rq(scmd);
+ int iprio_class;
+ u8 is_pcie_dev = 0;
+ u32 tracked_io_sz = 0;
+ u32 ioc_pend_data_len = 0, tg_pend_data_len = 0;
+ struct mpi3mr_throttle_group_info *tg = NULL;
+
+ if (mrioc->unrecoverable) {
+ scmd->result = DID_ERROR << 16;
+ scsi_done(scmd);
+ goto out;
+ }
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ goto out;
+ }
+
+ if (mrioc->stop_drv_processing &&
+ !(mpi3mr_allow_scmd_to_fw(scmd))) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ goto out;
+ }
+
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_handle = stgt_priv_data->dev_handle;
+
+ /* Avoid error handling escalation when device is removed or blocked */
+
+ if (scmd->device->host->shost_state == SHOST_RECOVERY &&
+ scmd->cmnd[0] == TEST_UNIT_READY &&
+ (stgt_priv_data->dev_removed || (dev_handle == MPI3MR_INVALID_DEV_HANDLE))) {
+ scsi_build_sense(scmd, 0, UNIT_ATTENTION, 0x29, 0x07);
+ scsi_done(scmd);
+ goto out;
+ }
+
+ if (mrioc->reset_in_progress || mrioc->prepare_for_reset
+ || mrioc->block_on_pci_err) {
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ if (atomic_read(&stgt_priv_data->block_io)) {
+ if (mrioc->stop_drv_processing) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ goto out;
+ }
+ retval = SCSI_MLQUEUE_DEVICE_BUSY;
+ goto out;
+ }
+
+ if (dev_handle == MPI3MR_INVALID_DEV_HANDLE) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ goto out;
+ }
+ if (stgt_priv_data->dev_removed) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ goto out;
+ }
+
+ if (stgt_priv_data->dev_type == MPI3_DEVICE_DEVFORM_PCIE)
+ is_pcie_dev = 1;
+ if ((scmd->cmnd[0] == UNMAP) && is_pcie_dev &&
+ (mrioc->pdev->device == MPI3_MFGPAGE_DEVID_SAS4116) &&
+ mpi3mr_check_return_unmap(mrioc, scmd))
+ goto out;
+
+ host_tag = mpi3mr_host_tag_for_scmd(mrioc, scmd);
+ if (host_tag == MPI3MR_HOSTTAG_INVALID) {
+ scmd->result = DID_ERROR << 16;
+ scsi_done(scmd);
+ goto out;
+ }
+
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_READ;
+ else if (scmd->sc_data_direction == DMA_TO_DEVICE)
+ scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_WRITE;
+ else
+ scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_NO_DATA_TRANSFER;
+
+ scsiio_flags |= MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SIMPLEQ;
+
+ if (sdev_priv_data->ncq_prio_enable) {
+ iprio_class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
+ if (iprio_class == IOPRIO_CLASS_RT)
+ scsiio_flags |= 1 << MPI3_SCSIIO_FLAGS_CMDPRI_SHIFT;
+ }
+
+ if (scmd->cmd_len > 16)
+ scsiio_flags |= MPI3_SCSIIO_FLAGS_CDB_GREATER_THAN_16;
+
+ scmd_priv_data = scsi_cmd_priv(scmd);
+ memset(scmd_priv_data->mpi3mr_scsiio_req, 0, MPI3MR_ADMIN_REQ_FRAME_SZ);
+ scsiio_req = (struct mpi3_scsi_io_request *)scmd_priv_data->mpi3mr_scsiio_req;
+ scsiio_req->function = MPI3_FUNCTION_SCSI_IO;
+ scsiio_req->host_tag = cpu_to_le16(host_tag);
+
+ mpi3mr_setup_eedp(mrioc, scmd, scsiio_req);
+
+ if (stgt_priv_data->wslen)
+ mpi3mr_setup_divert_ws(mrioc, scmd, scsiio_req, &scsiio_flags,
+ stgt_priv_data->wslen);
+
+ memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len);
+ scsiio_req->data_length = cpu_to_le32(scsi_bufflen(scmd));
+ scsiio_req->dev_handle = cpu_to_le16(dev_handle);
+ scsiio_req->flags = cpu_to_le32(scsiio_flags);
+ int_to_scsilun(sdev_priv_data->lun_id,
+ (struct scsi_lun *)scsiio_req->lun);
+
+ if (mpi3mr_build_sg_scmd(mrioc, scmd, scsiio_req)) {
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+ op_req_q = &mrioc->req_qinfo[scmd_priv_data->req_q_idx];
+ data_len_blks = scsi_bufflen(scmd) >> 9;
+ if ((data_len_blks >= mrioc->io_throttle_data_length) &&
+ stgt_priv_data->io_throttle_enabled) {
+ tracked_io_sz = data_len_blks;
+ tg = stgt_priv_data->throttle_group;
+ if (tg) {
+ ioc_pend_data_len = atomic_add_return(data_len_blks,
+ &mrioc->pend_large_data_sz);
+ tg_pend_data_len = atomic_add_return(data_len_blks,
+ &tg->pend_large_data_sz);
+ if (!tg->io_divert && ((ioc_pend_data_len >=
+ mrioc->io_throttle_high) ||
+ (tg_pend_data_len >= tg->high))) {
+ tg->io_divert = 1;
+ tg->need_qd_reduction = 1;
+ mpi3mr_set_io_divert_for_all_vd_in_tg(mrioc,
+ tg, 1);
+ mpi3mr_queue_qd_reduction_event(mrioc, tg);
+ }
+ } else {
+ ioc_pend_data_len = atomic_add_return(data_len_blks,
+ &mrioc->pend_large_data_sz);
+ if (ioc_pend_data_len >= mrioc->io_throttle_high)
+ stgt_priv_data->io_divert = 1;
+ }
+ }
+
+ if (stgt_priv_data->io_divert) {
+ scsiio_req->msg_flags |=
+ MPI3_SCSIIO_MSGFLAGS_DIVERT_TO_FIRMWARE;
+ scsiio_flags |= MPI3_SCSIIO_FLAGS_DIVERT_REASON_IO_THROTTLING;
+ }
+ scsiio_req->flags |= cpu_to_le32(scsiio_flags);
+
+ if (mpi3mr_op_request_post(mrioc, op_req_q,
+ scmd_priv_data->mpi3mr_scsiio_req)) {
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ if (tracked_io_sz) {
+ atomic_sub(tracked_io_sz, &mrioc->pend_large_data_sz);
+ if (tg)
+ atomic_sub(tracked_io_sz,
+ &tg->pend_large_data_sz);
+ }
+ goto out;
+ }
+
+out:
+ return retval;
+}
+
+static const struct scsi_host_template mpi3mr_driver_template = {
+ .module = THIS_MODULE,
+ .name = "MPI3 Storage Controller",
+ .proc_name = MPI3MR_DRIVER_NAME,
+ .queuecommand = mpi3mr_qcmd,
+ .target_alloc = mpi3mr_target_alloc,
+ .sdev_init = mpi3mr_sdev_init,
+ .sdev_configure = mpi3mr_sdev_configure,
+ .target_destroy = mpi3mr_target_destroy,
+ .sdev_destroy = mpi3mr_sdev_destroy,
+ .scan_finished = mpi3mr_scan_finished,
+ .scan_start = mpi3mr_scan_start,
+ .change_queue_depth = mpi3mr_change_queue_depth,
+ .eh_abort_handler = mpi3mr_eh_abort,
+ .eh_device_reset_handler = mpi3mr_eh_dev_reset,
+ .eh_target_reset_handler = mpi3mr_eh_target_reset,
+ .eh_bus_reset_handler = mpi3mr_eh_bus_reset,
+ .eh_host_reset_handler = mpi3mr_eh_host_reset,
+ .bios_param = mpi3mr_bios_param,
+ .map_queues = mpi3mr_map_queues,
+ .mq_poll = mpi3mr_blk_mq_poll,
+ .no_write_same = 1,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = MPI3MR_DEFAULT_SGL_ENTRIES,
+ /* max xfer supported is 1M (2K in 512 byte sized sectors)
+ */
+ .max_sectors = (MPI3MR_DEFAULT_MAX_IO_SIZE / 512),
+ .cmd_per_lun = MPI3MR_MAX_CMDS_LUN,
+ .max_segment_size = 0xffffffff,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct scmd_priv),
+ .shost_groups = mpi3mr_host_groups,
+ .sdev_groups = mpi3mr_dev_groups,
+};
+
+/**
+ * mpi3mr_init_drv_cmd - Initialize internal command tracker
+ * @cmdptr: Internal command tracker
+ * @host_tag: Host tag used for the specific command
+ *
+ * Initialize the internal command tracker structure with
+ * specified host tag.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_init_drv_cmd(struct mpi3mr_drv_cmd *cmdptr,
+ u16 host_tag)
+{
+ mutex_init(&cmdptr->mutex);
+ cmdptr->reply = NULL;
+ cmdptr->state = MPI3MR_CMD_NOTUSED;
+ cmdptr->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ cmdptr->host_tag = host_tag;
+}
+
+/**
+ * osintfc_mrioc_security_status -Check controller secure status
+ * @pdev: PCI device instance
+ *
+ * Read the Device Serial Number capability from PCI config
+ * space and decide whether the controller is secure or not.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int
+osintfc_mrioc_security_status(struct pci_dev *pdev)
+{
+ u32 cap_data;
+ int base;
+ u32 ctlr_status;
+ u32 debug_status;
+ int retval = 0;
+
+ base = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DSN);
+ if (!base) {
+ dev_err(&pdev->dev,
+ "%s: PCI_EXT_CAP_ID_DSN is not supported\n", __func__);
+ return -1;
+ }
+
+ pci_read_config_dword(pdev, base + 4, &cap_data);
+
+ debug_status = cap_data & MPI3MR_CTLR_SECURE_DBG_STATUS_MASK;
+ ctlr_status = cap_data & MPI3MR_CTLR_SECURITY_STATUS_MASK;
+
+ switch (ctlr_status) {
+ case MPI3MR_INVALID_DEVICE:
+ dev_err(&pdev->dev,
+ "%s: Non secure ctlr (Invalid) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
+ __func__, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ retval = -1;
+ break;
+ case MPI3MR_CONFIG_SECURE_DEVICE:
+ if (!debug_status)
+ dev_info(&pdev->dev,
+ "%s: Config secure ctlr is detected\n",
+ __func__);
+ break;
+ case MPI3MR_HARD_SECURE_DEVICE:
+ break;
+ case MPI3MR_TAMPERED_DEVICE:
+ dev_err(&pdev->dev,
+ "%s: Non secure ctlr (Tampered) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
+ __func__, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ retval = -1;
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+
+ if (!retval && debug_status) {
+ dev_err(&pdev->dev,
+ "%s: Non secure ctlr (Secure Dbg) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
+ __func__, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ retval = -1;
+ }
+
+ return retval;
+}
+
+/**
+ * mpi3mr_probe - PCI probe callback
+ * @pdev: PCI device instance
+ * @id: PCI device ID details
+ *
+ * controller initialization routine. Checks the security status
+ * of the controller and if it is invalid or tampered return the
+ * probe without initializing the controller. Otherwise,
+ * allocate per adapter instance through shost_priv and
+ * initialize controller specific data structures, initializae
+ * the controller hardware, add shost to the SCSI subsystem.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+
+static int
+mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct mpi3mr_ioc *mrioc = NULL;
+ struct Scsi_Host *shost = NULL;
+ int retval = 0, i;
+
+ if (osintfc_mrioc_security_status(pdev)) {
+ warn_non_secure_ctlr = 1;
+ return 1; /* For Invalid and Tampered device */
+ }
+
+ shost = scsi_host_alloc(&mpi3mr_driver_template,
+ sizeof(struct mpi3mr_ioc));
+ if (!shost) {
+ retval = -ENODEV;
+ goto shost_failed;
+ }
+
+ mrioc = shost_priv(shost);
+ retval = ida_alloc_range(&mrioc_ida, 0, U8_MAX, GFP_KERNEL);
+ if (retval < 0)
+ goto id_alloc_failed;
+ mrioc->id = (u8)retval;
+ sprintf(mrioc->driver_name, "%s", MPI3MR_DRIVER_NAME);
+ sprintf(mrioc->name, "%s%d", mrioc->driver_name, mrioc->id);
+ INIT_LIST_HEAD(&mrioc->list);
+ spin_lock(&mrioc_list_lock);
+ list_add_tail(&mrioc->list, &mrioc_list);
+ spin_unlock(&mrioc_list_lock);
+
+ spin_lock_init(&mrioc->admin_req_lock);
+ spin_lock_init(&mrioc->reply_free_queue_lock);
+ spin_lock_init(&mrioc->sbq_lock);
+ spin_lock_init(&mrioc->fwevt_lock);
+ spin_lock_init(&mrioc->tgtdev_lock);
+ spin_lock_init(&mrioc->watchdog_lock);
+ spin_lock_init(&mrioc->chain_buf_lock);
+ spin_lock_init(&mrioc->adm_req_q_bar_writeq_lock);
+ spin_lock_init(&mrioc->adm_reply_q_bar_writeq_lock);
+ spin_lock_init(&mrioc->sas_node_lock);
+ spin_lock_init(&mrioc->trigger_lock);
+
+ INIT_LIST_HEAD(&mrioc->fwevt_list);
+ INIT_LIST_HEAD(&mrioc->tgtdev_list);
+ INIT_LIST_HEAD(&mrioc->delayed_rmhs_list);
+ INIT_LIST_HEAD(&mrioc->delayed_evtack_cmds_list);
+ INIT_LIST_HEAD(&mrioc->sas_expander_list);
+ INIT_LIST_HEAD(&mrioc->hba_port_table_list);
+ INIT_LIST_HEAD(&mrioc->enclosure_list);
+
+ mutex_init(&mrioc->reset_mutex);
+ mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS);
+ mpi3mr_init_drv_cmd(&mrioc->host_tm_cmds, MPI3MR_HOSTTAG_BLK_TMS);
+ mpi3mr_init_drv_cmd(&mrioc->bsg_cmds, MPI3MR_HOSTTAG_BSG_CMDS);
+ mpi3mr_init_drv_cmd(&mrioc->cfg_cmds, MPI3MR_HOSTTAG_CFG_CMDS);
+ mpi3mr_init_drv_cmd(&mrioc->transport_cmds,
+ MPI3MR_HOSTTAG_TRANSPORT_CMDS);
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
+ mpi3mr_init_drv_cmd(&mrioc->dev_rmhs_cmds[i],
+ MPI3MR_HOSTTAG_DEVRMCMD_MIN + i);
+
+ for (i = 0; i < MPI3MR_NUM_EVTACKCMD; i++)
+ mpi3mr_init_drv_cmd(&mrioc->evtack_cmds[i],
+ MPI3MR_HOSTTAG_EVTACKCMD_MIN + i);
+
+ if ((pdev->device == MPI3_MFGPAGE_DEVID_SAS4116) &&
+ !pdev->revision)
+ mrioc->enable_segqueue = false;
+ else
+ mrioc->enable_segqueue = true;
+
+ init_waitqueue_head(&mrioc->reset_waitq);
+ mrioc->logging_level = logging_level;
+ mrioc->shost = shost;
+ mrioc->pdev = pdev;
+ mrioc->stop_bsgs = 1;
+
+ mrioc->max_sgl_entries = max_sgl_entries;
+ if (max_sgl_entries > MPI3MR_MAX_SGL_ENTRIES)
+ mrioc->max_sgl_entries = MPI3MR_MAX_SGL_ENTRIES;
+ else if (max_sgl_entries < MPI3MR_DEFAULT_SGL_ENTRIES)
+ mrioc->max_sgl_entries = MPI3MR_DEFAULT_SGL_ENTRIES;
+ else {
+ mrioc->max_sgl_entries /= MPI3MR_DEFAULT_SGL_ENTRIES;
+ mrioc->max_sgl_entries *= MPI3MR_DEFAULT_SGL_ENTRIES;
+ }
+
+ /* init shost parameters */
+ shost->max_cmd_len = MPI3MR_MAX_CDB_LENGTH;
+ shost->max_lun = -1;
+ shost->unique_id = mrioc->id;
+
+ shost->max_channel = 0;
+ shost->max_id = 0xFFFFFFFF;
+
+ shost->host_tagset = 1;
+
+ if (prot_mask >= 0)
+ scsi_host_set_prot(shost, prot_mask);
+ else {
+ prot_mask = SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION;
+ scsi_host_set_prot(shost, prot_mask);
+ }
+
+ ioc_info(mrioc,
+ "%s :host protection capabilities enabled %s%s%s%s%s%s%s\n",
+ __func__,
+ (prot_mask & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "",
+ (prot_mask & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "",
+ (prot_mask & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "",
+ (prot_mask & SHOST_DIX_TYPE0_PROTECTION) ? " DIX0" : "",
+ (prot_mask & SHOST_DIX_TYPE1_PROTECTION) ? " DIX1" : "",
+ (prot_mask & SHOST_DIX_TYPE2_PROTECTION) ? " DIX2" : "",
+ (prot_mask & SHOST_DIX_TYPE3_PROTECTION) ? " DIX3" : "");
+
+ if (prot_guard_mask)
+ scsi_host_set_guard(shost, (prot_guard_mask & 3));
+ else
+ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+
+ mrioc->fwevt_worker_thread = alloc_ordered_workqueue(
+ "%s%d_fwevt_wrkr", 0, mrioc->driver_name, mrioc->id);
+ if (!mrioc->fwevt_worker_thread) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ retval = -ENODEV;
+ goto fwevtthread_failed;
+ }
+
+ mrioc->is_driver_loading = 1;
+ mrioc->cpu_count = num_online_cpus();
+ if (mpi3mr_setup_resources(mrioc)) {
+ ioc_err(mrioc, "setup resources failed\n");
+ retval = -ENODEV;
+ goto resource_alloc_failed;
+ }
+ if (mpi3mr_init_ioc(mrioc)) {
+ ioc_err(mrioc, "initializing IOC failed\n");
+ retval = -ENODEV;
+ goto init_ioc_failed;
+ }
+
+ shost->nr_hw_queues = mrioc->num_op_reply_q;
+ if (mrioc->active_poll_qcount)
+ shost->nr_maps = 3;
+
+ shost->can_queue = mrioc->max_host_ios;
+ shost->sg_tablesize = mrioc->max_sgl_entries;
+ shost->max_id = mrioc->facts.max_perids + 1;
+
+ retval = scsi_add_host(shost, &pdev->dev);
+ if (retval) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto addhost_failed;
+ }
+
+ scsi_scan_host(shost);
+ mpi3mr_bsg_init(mrioc);
+ return retval;
+
+addhost_failed:
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_cleanup_ioc(mrioc);
+init_ioc_failed:
+ mpi3mr_free_mem(mrioc);
+ mpi3mr_cleanup_resources(mrioc);
+resource_alloc_failed:
+ destroy_workqueue(mrioc->fwevt_worker_thread);
+fwevtthread_failed:
+ ida_free(&mrioc_ida, mrioc->id);
+ spin_lock(&mrioc_list_lock);
+ list_del(&mrioc->list);
+ spin_unlock(&mrioc_list_lock);
+id_alloc_failed:
+ scsi_host_put(shost);
+shost_failed:
+ return retval;
+}
+
+/**
+ * mpi3mr_remove - PCI remove callback
+ * @pdev: PCI device instance
+ *
+ * Cleanup the IOC by issuing MUR and shutdown notification.
+ * Free up all memory and resources associated with the
+ * controllerand target devices, unregister the shost.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+ struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next;
+ struct mpi3mr_hba_port *port, *hba_port_next;
+ struct mpi3mr_sas_node *sas_expander, *sas_expander_next;
+
+ if (!shost)
+ return;
+
+ mrioc = shost_priv(shost);
+ while (mrioc->reset_in_progress || mrioc->is_driver_loading)
+ ssleep(1);
+
+ if (mrioc->block_on_pci_err) {
+ mrioc->block_on_pci_err = false;
+ scsi_unblock_requests(shost);
+ mrioc->unrecoverable = 1;
+ }
+
+ if (!pci_device_is_present(mrioc->pdev) ||
+ mrioc->pci_err_recovery) {
+ mrioc->unrecoverable = 1;
+ mpi3mr_flush_cmds_for_unrecovered_controller(mrioc);
+ }
+
+ mpi3mr_bsg_exit(mrioc);
+ mrioc->stop_drv_processing = 1;
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ wq = mrioc->fwevt_worker_thread;
+ mrioc->fwevt_worker_thread = NULL;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+
+ if (mrioc->sas_transport_enabled)
+ sas_remove_host(shost);
+ else
+ scsi_remove_host(shost);
+
+ list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list,
+ list) {
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, true);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_cleanup_ioc(mrioc);
+ mpi3mr_free_mem(mrioc);
+ mpi3mr_cleanup_resources(mrioc);
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ list_for_each_entry_safe_reverse(sas_expander, sas_expander_next,
+ &mrioc->sas_expander_list, list) {
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ mpi3mr_expander_node_remove(mrioc, sas_expander);
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ }
+ list_for_each_entry_safe(port, hba_port_next, &mrioc->hba_port_table_list, list) {
+ ioc_info(mrioc,
+ "removing hba_port entry: %p port: %d from hba_port list\n",
+ port, port->port_id);
+ list_del(&port->list);
+ kfree(port);
+ }
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ if (mrioc->sas_hba.num_phys) {
+ kfree(mrioc->sas_hba.phy);
+ mrioc->sas_hba.phy = NULL;
+ mrioc->sas_hba.num_phys = 0;
+ }
+
+ ida_free(&mrioc_ida, mrioc->id);
+ spin_lock(&mrioc_list_lock);
+ list_del(&mrioc->list);
+ spin_unlock(&mrioc_list_lock);
+
+ scsi_host_put(shost);
+}
+
+/**
+ * mpi3mr_shutdown - PCI shutdown callback
+ * @pdev: PCI device instance
+ *
+ * Free up all memory and resources associated with the
+ * controller
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_shutdown(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+
+ if (!shost)
+ return;
+
+ mrioc = shost_priv(shost);
+ while (mrioc->reset_in_progress || mrioc->is_driver_loading)
+ ssleep(1);
+
+ mrioc->stop_drv_processing = 1;
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ wq = mrioc->fwevt_worker_thread;
+ mrioc->fwevt_worker_thread = NULL;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_cleanup_ioc(mrioc);
+ mpi3mr_cleanup_resources(mrioc);
+}
+
+/**
+ * mpi3mr_suspend - PCI power management suspend callback
+ * @dev: Device struct
+ *
+ * Change the power state to the given value and cleanup the IOC
+ * by issuing MUR and shutdown notification
+ *
+ * Return: 0 always.
+ */
+static int __maybe_unused
+mpi3mr_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+
+ if (!shost)
+ return 0;
+
+ mrioc = shost_priv(shost);
+ while (mrioc->reset_in_progress || mrioc->is_driver_loading)
+ ssleep(1);
+ mrioc->stop_drv_processing = 1;
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ scsi_block_requests(shost);
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_cleanup_ioc(mrioc);
+
+ ioc_info(mrioc, "pdev=0x%p, slot=%s, entering operating state\n",
+ pdev, pci_name(pdev));
+ mpi3mr_cleanup_resources(mrioc);
+
+ return 0;
+}
+
+/**
+ * mpi3mr_resume - PCI power management resume callback
+ * @dev: Device struct
+ *
+ * Restore the power state to D0 and reinitialize the controller
+ * and resume I/O operations to the target devices
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+static int __maybe_unused
+mpi3mr_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+ pci_power_t device_state = pdev->current_state;
+ int r;
+
+ if (!shost)
+ return 0;
+
+ mrioc = shost_priv(shost);
+
+ ioc_info(mrioc, "pdev=0x%p, slot=%s, previous operating state [D%d]\n",
+ pdev, pci_name(pdev), device_state);
+ mrioc->pdev = pdev;
+ mrioc->cpu_count = num_online_cpus();
+ r = mpi3mr_setup_resources(mrioc);
+ if (r) {
+ ioc_info(mrioc, "%s: Setup resources failed[%d]\n",
+ __func__, r);
+ return r;
+ }
+
+ mrioc->stop_drv_processing = 0;
+ mpi3mr_invalidate_devhandles(mrioc);
+ mpi3mr_free_enclosure_list(mrioc);
+ mpi3mr_memset_buffers(mrioc);
+ r = mpi3mr_reinit_ioc(mrioc, 1);
+ if (r) {
+ ioc_err(mrioc, "resuming controller failed[%d]\n", r);
+ return r;
+ }
+ ssleep(MPI3MR_RESET_TOPOLOGY_SETTLE_TIME);
+ scsi_unblock_requests(shost);
+ mrioc->device_refresh_on = 0;
+ mpi3mr_start_watchdog(mrioc);
+
+ return 0;
+}
+
+/**
+ * mpi3mr_pcierr_error_detected - PCI error detected callback
+ * @pdev: PCI device instance
+ * @state: channel state
+ *
+ * This function is called by the PCI error recovery driver and
+ * based on the state passed the driver decides what actions to
+ * be recommended back to PCI driver.
+ *
+ * For all of the states if there is no valid mrioc or scsi host
+ * references in the PCI device then this function will return
+ * the result as disconnect.
+ *
+ * For normal state, this function will return the result as can
+ * recover.
+ *
+ * For frozen state, this function will block for any pending
+ * controller initialization or re-initialization to complete,
+ * stop any new interactions with the controller and return
+ * status as reset required.
+ *
+ * For permanent failure state, this function will mark the
+ * controller as unrecoverable and return status as disconnect.
+ *
+ * Returns: PCI_ERS_RESULT_NEED_RESET or CAN_RECOVER or
+ * DISCONNECT based on the controller state.
+ */
+static pci_ers_result_t
+mpi3mr_pcierr_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ unsigned int timeout = MPI3MR_RESET_TIMEOUT;
+
+ dev_info(&pdev->dev, "%s: callback invoked state(%d)\n", __func__,
+ state);
+
+ shost = pci_get_drvdata(pdev);
+ mrioc = shost_priv(shost);
+
+ switch (state) {
+ case pci_channel_io_normal:
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ mrioc->pci_err_recovery = true;
+ mrioc->block_on_pci_err = true;
+ do {
+ if (mrioc->reset_in_progress || mrioc->is_driver_loading)
+ ssleep(1);
+ else
+ break;
+ } while (--timeout);
+
+ if (!timeout) {
+ mrioc->pci_err_recovery = true;
+ mrioc->block_on_pci_err = true;
+ mrioc->unrecoverable = 1;
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_flush_cmds_for_unrecovered_controller(mrioc);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ scsi_block_requests(mrioc->shost);
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_cleanup_resources(mrioc);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ mrioc->pci_err_recovery = true;
+ mrioc->block_on_pci_err = true;
+ mrioc->unrecoverable = 1;
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_flush_cmds_for_unrecovered_controller(mrioc);
+ return PCI_ERS_RESULT_DISCONNECT;
+ default:
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+}
+
+/**
+ * mpi3mr_pcierr_slot_reset - Post slot reset callback
+ * @pdev: PCI device instance
+ *
+ * This function is called by the PCI error recovery driver
+ * after a slot or link reset issued by it for the recovery, the
+ * driver is expected to bring back the controller and
+ * initialize it.
+ *
+ * This function restores PCI state and reinitializes controller
+ * resources and the controller, this blocks for any pending
+ * reset to complete.
+ *
+ * Returns: PCI_ERS_RESULT_DISCONNECT on failure or
+ * PCI_ERS_RESULT_RECOVERED
+ */
+static pci_ers_result_t mpi3mr_pcierr_slot_reset(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ unsigned int timeout = MPI3MR_RESET_TIMEOUT;
+
+ dev_info(&pdev->dev, "%s: callback invoked\n", __func__);
+
+ shost = pci_get_drvdata(pdev);
+ mrioc = shost_priv(shost);
+
+ do {
+ if (mrioc->reset_in_progress)
+ ssleep(1);
+ else
+ break;
+ } while (--timeout);
+
+ if (!timeout)
+ goto out_failed;
+
+ pci_restore_state(pdev);
+
+ if (mpi3mr_setup_resources(mrioc)) {
+ ioc_err(mrioc, "setup resources failed\n");
+ goto out_failed;
+ }
+ mrioc->unrecoverable = 0;
+ mrioc->pci_err_recovery = false;
+
+ if (mpi3mr_soft_reset_handler(mrioc, MPI3MR_RESET_FROM_FIRMWARE, 0))
+ goto out_failed;
+
+ return PCI_ERS_RESULT_RECOVERED;
+
+out_failed:
+ mrioc->unrecoverable = 1;
+ mrioc->block_on_pci_err = false;
+ scsi_unblock_requests(shost);
+ mpi3mr_start_watchdog(mrioc);
+ return PCI_ERS_RESULT_DISCONNECT;
+}
+
+/**
+ * mpi3mr_pcierr_resume - PCI error recovery resume
+ * callback
+ * @pdev: PCI device instance
+ *
+ * This function enables all I/O and IOCTLs post reset issued as
+ * part of the PCI error recovery
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_pcierr_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+
+ dev_info(&pdev->dev, "%s: callback invoked\n", __func__);
+
+ shost = pci_get_drvdata(pdev);
+ mrioc = shost_priv(shost);
+
+ if (mrioc->block_on_pci_err) {
+ mrioc->block_on_pci_err = false;
+ scsi_unblock_requests(shost);
+ mpi3mr_start_watchdog(mrioc);
+ }
+}
+
+/**
+ * mpi3mr_pcierr_mmio_enabled - PCI error recovery callback
+ * @pdev: PCI device instance
+ *
+ * This is called only if mpi3mr_pcierr_error_detected returns
+ * PCI_ERS_RESULT_CAN_RECOVER.
+ *
+ * Return: PCI_ERS_RESULT_DISCONNECT when the controller is
+ * unrecoverable or when the shost/mrioc reference cannot be
+ * found, else return PCI_ERS_RESULT_RECOVERED
+ */
+static pci_ers_result_t mpi3mr_pcierr_mmio_enabled(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+
+ dev_info(&pdev->dev, "%s: callback invoked\n", __func__);
+
+ shost = pci_get_drvdata(pdev);
+ mrioc = shost_priv(shost);
+
+ if (mrioc->unrecoverable)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static const struct pci_device_id mpi3mr_pci_id_table[] = {
+ {
+ PCI_DEVICE_SUB(MPI3_MFGPAGE_VENDORID_BROADCOM,
+ MPI3_MFGPAGE_DEVID_SAS4116, PCI_ANY_ID, PCI_ANY_ID)
+ },
+ {
+ PCI_DEVICE_SUB(MPI3_MFGPAGE_VENDORID_BROADCOM,
+ MPI3_MFGPAGE_DEVID_SAS5116_MPI, PCI_ANY_ID, PCI_ANY_ID)
+ },
+ {
+ PCI_DEVICE_SUB(MPI3_MFGPAGE_VENDORID_BROADCOM,
+ MPI3_MFGPAGE_DEVID_SAS5116_MPI_MGMT, PCI_ANY_ID, PCI_ANY_ID)
+ },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, mpi3mr_pci_id_table);
+
+static const struct pci_error_handlers mpi3mr_err_handler = {
+ .error_detected = mpi3mr_pcierr_error_detected,
+ .mmio_enabled = mpi3mr_pcierr_mmio_enabled,
+ .slot_reset = mpi3mr_pcierr_slot_reset,
+ .resume = mpi3mr_pcierr_resume,
+};
+
+static SIMPLE_DEV_PM_OPS(mpi3mr_pm_ops, mpi3mr_suspend, mpi3mr_resume);
+
+static struct pci_driver mpi3mr_pci_driver = {
+ .name = MPI3MR_DRIVER_NAME,
+ .id_table = mpi3mr_pci_id_table,
+ .probe = mpi3mr_probe,
+ .remove = mpi3mr_remove,
+ .shutdown = mpi3mr_shutdown,
+ .err_handler = &mpi3mr_err_handler,
+ .driver.pm = &mpi3mr_pm_ops,
+};
+
+static ssize_t event_counter_show(struct device_driver *dd, char *buf)
+{
+ return sprintf(buf, "%llu\n", atomic64_read(&event_counter));
+}
+static DRIVER_ATTR_RO(event_counter);
+
+static int __init mpi3mr_init(void)
+{
+ int ret_val;
+
+ pr_info("Loading %s version %s\n", MPI3MR_DRIVER_NAME,
+ MPI3MR_DRIVER_VERSION);
+
+ mpi3mr_transport_template =
+ sas_attach_transport(&mpi3mr_transport_functions);
+ if (!mpi3mr_transport_template) {
+ pr_err("%s failed to load due to sas transport attach failure\n",
+ MPI3MR_DRIVER_NAME);
+ return -ENODEV;
+ }
+
+ ret_val = pci_register_driver(&mpi3mr_pci_driver);
+ if (ret_val) {
+ pr_err("%s failed to load due to pci register driver failure\n",
+ MPI3MR_DRIVER_NAME);
+ goto err_pci_reg_fail;
+ }
+
+ ret_val = driver_create_file(&mpi3mr_pci_driver.driver,
+ &driver_attr_event_counter);
+ if (ret_val)
+ goto err_event_counter;
+
+ return ret_val;
+
+err_event_counter:
+ pci_unregister_driver(&mpi3mr_pci_driver);
+
+err_pci_reg_fail:
+ sas_release_transport(mpi3mr_transport_template);
+ return ret_val;
+}
+
+static void __exit mpi3mr_exit(void)
+{
+ if (warn_non_secure_ctlr)
+ pr_warn(
+ "Unloading %s version %s while managing a non secure controller\n",
+ MPI3MR_DRIVER_NAME, MPI3MR_DRIVER_VERSION);
+ else
+ pr_info("Unloading %s version %s\n", MPI3MR_DRIVER_NAME,
+ MPI3MR_DRIVER_VERSION);
+
+ driver_remove_file(&mpi3mr_pci_driver.driver,
+ &driver_attr_event_counter);
+ pci_unregister_driver(&mpi3mr_pci_driver);
+ sas_release_transport(mpi3mr_transport_template);
+ ida_destroy(&mrioc_ida);
+}
+
+module_init(mpi3mr_init);
+module_exit(mpi3mr_exit);
diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c
new file mode 100644
index 000000000000..d70f002d6487
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c
@@ -0,0 +1,3354 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2023 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#include <linux/vmalloc.h>
+
+#include "mpi3mr.h"
+
+/**
+ * mpi3mr_post_transport_req - Issue transport requests and wait
+ * @mrioc: Adapter instance reference
+ * @request: Properly populated MPI3 request
+ * @request_sz: Size of the MPI3 request
+ * @reply: Pointer to return MPI3 reply
+ * @reply_sz: Size of the MPI3 reply buffer
+ * @timeout: Timeout in seconds
+ * @ioc_status: Pointer to return ioc status
+ *
+ * A generic function for posting MPI3 requests from the SAS
+ * transport layer that uses transport command infrastructure.
+ * This blocks for the completion of request for timeout seconds
+ * and if the request times out this function faults the
+ * controller with proper reason code.
+ *
+ * On successful completion of the request this function returns
+ * appropriate ioc status from the firmware back to the caller.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_post_transport_req(struct mpi3mr_ioc *mrioc, void *request,
+ u16 request_sz, void *reply, u16 reply_sz, int timeout,
+ u16 *ioc_status)
+{
+ int retval = 0;
+
+ mutex_lock(&mrioc->transport_cmds.mutex);
+ if (mrioc->transport_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "sending transport request failed due to command in use\n");
+ mutex_unlock(&mrioc->transport_cmds.mutex);
+ goto out;
+ }
+ mrioc->transport_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->transport_cmds.is_waiting = 1;
+ mrioc->transport_cmds.callback = NULL;
+ mrioc->transport_cmds.ioc_status = 0;
+ mrioc->transport_cmds.ioc_loginfo = 0;
+
+ init_completion(&mrioc->transport_cmds.done);
+ dprint_cfg_info(mrioc, "posting transport request\n");
+ if (mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO)
+ dprint_dump(request, request_sz, "transport_req");
+ retval = mpi3mr_admin_request_post(mrioc, request, request_sz, 1);
+ if (retval) {
+ ioc_err(mrioc, "posting transport request failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->transport_cmds.done,
+ (timeout * HZ));
+ if (!(mrioc->transport_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT);
+ ioc_err(mrioc, "transport request timed out\n");
+ retval = -1;
+ goto out_unlock;
+ }
+ *ioc_status = mrioc->transport_cmds.ioc_status &
+ MPI3_IOCSTATUS_STATUS_MASK;
+ if ((*ioc_status) != MPI3_IOCSTATUS_SUCCESS)
+ dprint_transport_err(mrioc,
+ "transport request returned with ioc_status(0x%04x), log_info(0x%08x)\n",
+ *ioc_status, mrioc->transport_cmds.ioc_loginfo);
+
+ if ((reply) && (mrioc->transport_cmds.state & MPI3MR_CMD_REPLY_VALID))
+ memcpy((u8 *)reply, mrioc->transport_cmds.reply, reply_sz);
+
+out_unlock:
+ mrioc->transport_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->transport_cmds.mutex);
+
+out:
+ return retval;
+}
+
+/* report manufacture request structure */
+struct rep_manu_request {
+ u8 smp_frame_type;
+ u8 function;
+ u8 reserved;
+ u8 request_length;
+};
+
+/* report manufacture reply structure */
+struct rep_manu_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x01 */
+ u8 function_result;
+ u8 response_length;
+ u16 expander_change_count;
+ u8 reserved0[2];
+ u8 sas_format;
+ u8 reserved2[3];
+ u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] __nonstring;
+ u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN] __nonstring;
+ u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] __nonstring;
+ u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] __nonstring;
+ u16 component_id;
+ u8 component_revision_id;
+ u8 reserved3;
+ u8 vendor_specific[8];
+};
+
+/**
+ * mpi3mr_report_manufacture - obtain SMP report_manufacture
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of the expander device
+ * @edev: SAS transport layer sas_expander_device object
+ * @port_id: ID of the HBA port
+ *
+ * Fills in the sas_expander_device with manufacturing info.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int mpi3mr_report_manufacture(struct mpi3mr_ioc *mrioc,
+ u64 sas_address, struct sas_expander_device *edev, u8 port_id)
+{
+ struct mpi3_smp_passthrough_request mpi_request;
+ struct mpi3_smp_passthrough_reply mpi_reply;
+ struct rep_manu_reply *manufacture_reply;
+ struct rep_manu_request *manufacture_request;
+ int rc = 0;
+ void *psge;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ dma_addr_t data_in_dma;
+ size_t data_in_sz;
+ size_t data_out_sz;
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+ u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
+ u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
+ u16 ioc_status;
+ u8 *tmp;
+
+ if (mrioc->reset_in_progress) {
+ ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ if (mrioc->pci_err_recovery) {
+ ioc_err(mrioc, "%s: pci error recovery in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ data_out_sz = sizeof(struct rep_manu_request);
+ data_in_sz = sizeof(struct rep_manu_reply);
+ data_out = dma_alloc_coherent(&mrioc->pdev->dev,
+ data_out_sz + data_in_sz, &data_out_dma, GFP_KERNEL);
+ if (!data_out) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ data_in_dma = data_out_dma + data_out_sz;
+ manufacture_reply = data_out + data_out_sz;
+
+ manufacture_request = data_out;
+ manufacture_request->smp_frame_type = 0x40;
+ manufacture_request->function = 1;
+ manufacture_request->reserved = 0;
+ manufacture_request->request_length = 0;
+
+ memset(&mpi_request, 0, request_sz);
+ memset(&mpi_reply, 0, reply_sz);
+ mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
+ mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request.io_unit_port = (u8) port_id;
+ mpi_request.sas_address = cpu_to_le64(sas_address);
+
+ psge = &mpi_request.request_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
+
+ psge = &mpi_request.response_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
+
+ dprint_transport_info(mrioc,
+ "sending report manufacturer SMP request to sas_address(0x%016llx), port(%d)\n",
+ (unsigned long long)sas_address, port_id);
+
+ rc = mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
+ &mpi_reply, reply_sz,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status);
+ if (rc)
+ goto out;
+
+ dprint_transport_info(mrioc,
+ "report manufacturer SMP request completed with ioc_status(0x%04x)\n",
+ ioc_status);
+
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ dprint_transport_info(mrioc,
+ "report manufacturer - reply data transfer size(%d)\n",
+ le16_to_cpu(mpi_reply.response_data_length));
+
+ if (le16_to_cpu(mpi_reply.response_data_length) !=
+ sizeof(struct rep_manu_reply)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ memtostr(edev->vendor_id, manufacture_reply->vendor_id);
+ memtostr(edev->product_id, manufacture_reply->product_id);
+ memtostr(edev->product_rev, manufacture_reply->product_rev);
+ edev->level = manufacture_reply->sas_format & 1;
+ if (edev->level) {
+ memtostr(edev->component_vendor_id,
+ manufacture_reply->component_vendor_id);
+ tmp = (u8 *)&manufacture_reply->component_id;
+ edev->component_id = tmp[0] << 8 | tmp[1];
+ edev->component_revision_id =
+ manufacture_reply->component_revision_id;
+ }
+
+out:
+ if (data_out)
+ dma_free_coherent(&mrioc->pdev->dev, data_out_sz + data_in_sz,
+ data_out, data_out_dma);
+
+ return rc;
+}
+
+/**
+ * __mpi3mr_expander_find_by_handle - expander search by handle
+ * @mrioc: Adapter instance reference
+ * @handle: Firmware device handle of the expander
+ *
+ * Context: The caller should acquire sas_node_lock
+ *
+ * This searches for expander device based on handle, then
+ * returns the sas_node object.
+ *
+ * Return: Expander sas_node object reference or NULL
+ */
+struct mpi3mr_sas_node *__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc
+ *mrioc, u16 handle)
+{
+ struct mpi3mr_sas_node *sas_expander, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
+ if (sas_expander->handle != handle)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * mpi3mr_is_expander_device - if device is an expander
+ * @device_info: Bitfield providing information about the device
+ *
+ * Return: 1 if the device is expander device, else 0.
+ */
+u8 mpi3mr_is_expander_device(u16 device_info)
+{
+ if ((device_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) ==
+ MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * mpi3mr_get_sas_address - retrieve sas_address for handle
+ * @mrioc: Adapter instance reference
+ * @handle: Firmware device handle
+ * @sas_address: Address to hold sas address
+ *
+ * This function issues device page0 read for a given device
+ * handle and gets the SAS address and return it back
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+static int mpi3mr_get_sas_address(struct mpi3mr_ioc *mrioc, u16 handle,
+ u64 *sas_address)
+{
+ struct mpi3_device_page0 dev_pg0;
+ u16 ioc_status;
+ struct mpi3_device0_sas_sata_format *sasinf;
+
+ *sas_address = 0;
+
+ if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
+ sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
+ handle))) {
+ ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
+ return -ENXIO;
+ }
+
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
+ handle, ioc_status, __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ if (le16_to_cpu(dev_pg0.flags) &
+ MPI3_DEVICE0_FLAGS_CONTROLLER_DEV_HANDLE)
+ *sas_address = mrioc->sas_hba.sas_address;
+ else if (dev_pg0.device_form == MPI3_DEVICE_DEVFORM_SAS_SATA) {
+ sasinf = &dev_pg0.device_specific.sas_sata_format;
+ *sas_address = le64_to_cpu(sasinf->sas_address);
+ } else {
+ ioc_err(mrioc, "%s: device_form(%d) is not SAS_SATA\n",
+ __func__, dev_pg0.device_form);
+ return -ENXIO;
+ }
+ return 0;
+}
+
+/**
+ * __mpi3mr_get_tgtdev_by_addr - target device search
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of the device
+ * @hba_port: HBA port entry
+ *
+ * This searches for target device from sas address and hba port
+ * pointer then return mpi3mr_tgt_dev object.
+ *
+ * Return: Valid tget_dev or NULL
+ */
+static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc *mrioc,
+ u64 sas_address, struct mpi3mr_hba_port *hba_port)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+ if ((tgtdev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA) &&
+ (tgtdev->dev_spec.sas_sata_inf.sas_address == sas_address)
+ && (tgtdev->dev_spec.sas_sata_inf.hba_port == hba_port))
+ goto found_device;
+ return NULL;
+found_device:
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_get_tgtdev_by_addr - target device search
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of the device
+ * @hba_port: HBA port entry
+ *
+ * This searches for target device from sas address and hba port
+ * pointer then return mpi3mr_tgt_dev object.
+ *
+ * Context: This function will acquire tgtdev_lock and will
+ * release before returning the mpi3mr_tgt_dev object.
+ *
+ * Return: Valid tget_dev or NULL
+ */
+static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc *mrioc,
+ u64 sas_address, struct mpi3mr_hba_port *hba_port)
+{
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ unsigned long flags;
+
+ if (!hba_port)
+ goto out;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_addr(mrioc, sas_address, hba_port);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+out:
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_remove_device_by_sas_address - remove the device
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of the device
+ * @hba_port: HBA port entry
+ *
+ * This searches for target device using sas address and hba
+ * port pointer then removes it from the OS.
+ *
+ * Return: None
+ */
+static void mpi3mr_remove_device_by_sas_address(struct mpi3mr_ioc *mrioc,
+ u64 sas_address, struct mpi3mr_hba_port *hba_port)
+{
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ unsigned long flags;
+ u8 was_on_tgtdev_list = 0;
+
+ if (!hba_port)
+ return;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_addr(mrioc,
+ sas_address, hba_port);
+ if (tgtdev) {
+ if (!list_empty(&tgtdev->list)) {
+ was_on_tgtdev_list = 1;
+ if (tgtdev->state == MPI3MR_DEV_REMOVE_HS_STARTED) {
+ list_del_init(&tgtdev->list);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ if (was_on_tgtdev_list) {
+ if (tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * __mpi3mr_get_tgtdev_by_addr_and_rphy - target device search
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of the device
+ * @rphy: SAS transport layer rphy object
+ *
+ * This searches for target device from sas address and rphy
+ * pointer then return mpi3mr_tgt_dev object.
+ *
+ * Return: Valid tget_dev or NULL
+ */
+struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_addr_and_rphy(
+ struct mpi3mr_ioc *mrioc, u64 sas_address, struct sas_rphy *rphy)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+ if ((tgtdev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA) &&
+ (tgtdev->dev_spec.sas_sata_inf.sas_address == sas_address)
+ && (tgtdev->dev_spec.sas_sata_inf.rphy == rphy))
+ goto found_device;
+ return NULL;
+found_device:
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_expander_find_by_sas_address - sas expander search
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of expander
+ * @hba_port: HBA port entry
+ *
+ * Return: A valid SAS expander node or NULL.
+ *
+ */
+static struct mpi3mr_sas_node *mpi3mr_expander_find_by_sas_address(
+ struct mpi3mr_ioc *mrioc, u64 sas_address,
+ struct mpi3mr_hba_port *hba_port)
+{
+ struct mpi3mr_sas_node *sas_expander, *r = NULL;
+
+ if (!hba_port)
+ goto out;
+
+ list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
+ if ((sas_expander->sas_address != sas_address) ||
+ (sas_expander->hba_port != hba_port))
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+out:
+ return r;
+}
+
+/**
+ * __mpi3mr_sas_node_find_by_sas_address - sas node search
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of expander or sas host
+ * @hba_port: HBA port entry
+ * Context: Caller should acquire mrioc->sas_node_lock.
+ *
+ * If the SAS address indicates the device is direct attached to
+ * the controller (controller's SAS address) then the SAS node
+ * associated with the controller is returned back else the SAS
+ * address and hba port are used to identify the exact expander
+ * and the associated sas_node object is returned. If there is
+ * no match NULL is returned.
+ *
+ * Return: A valid SAS node or NULL.
+ *
+ */
+static struct mpi3mr_sas_node *__mpi3mr_sas_node_find_by_sas_address(
+ struct mpi3mr_ioc *mrioc, u64 sas_address,
+ struct mpi3mr_hba_port *hba_port)
+{
+
+ if (mrioc->sas_hba.sas_address == sas_address)
+ return &mrioc->sas_hba;
+ return mpi3mr_expander_find_by_sas_address(mrioc, sas_address,
+ hba_port);
+}
+
+/**
+ * mpi3mr_parent_present - Is parent present for a phy
+ * @mrioc: Adapter instance reference
+ * @phy: SAS transport layer phy object
+ *
+ * Return: 0 if parent is present else non-zero
+ */
+static int mpi3mr_parent_present(struct mpi3mr_ioc *mrioc, struct sas_phy *phy)
+{
+ unsigned long flags;
+ struct mpi3mr_hba_port *hba_port = phy->hostdata;
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ if (__mpi3mr_sas_node_find_by_sas_address(mrioc,
+ phy->identify.sas_address,
+ hba_port) == NULL) {
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ return -1;
+ }
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ return 0;
+}
+
+/**
+ * mpi3mr_convert_phy_link_rate -
+ * @link_rate: link rate as defined in the MPI header
+ *
+ * Convert link_rate from mpi format into sas_transport layer
+ * form.
+ *
+ * Return: A valid SAS transport layer defined link rate
+ */
+static enum sas_linkrate mpi3mr_convert_phy_link_rate(u8 link_rate)
+{
+ enum sas_linkrate rc;
+
+ switch (link_rate) {
+ case MPI3_SAS_NEG_LINK_RATE_1_5:
+ rc = SAS_LINK_RATE_1_5_GBPS;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_3_0:
+ rc = SAS_LINK_RATE_3_0_GBPS;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_6_0:
+ rc = SAS_LINK_RATE_6_0_GBPS;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_12_0:
+ rc = SAS_LINK_RATE_12_0_GBPS;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_22_5:
+ rc = SAS_LINK_RATE_22_5_GBPS;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_PHY_DISABLED:
+ rc = SAS_PHY_DISABLED;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
+ rc = SAS_LINK_RATE_FAILED;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_PORT_SELECTOR:
+ rc = SAS_SATA_PORT_SELECTOR;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
+ rc = SAS_PHY_RESET_IN_PROGRESS;
+ break;
+ case MPI3_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
+ case MPI3_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
+ default:
+ rc = SAS_LINK_RATE_UNKNOWN;
+ break;
+ }
+ return rc;
+}
+
+/**
+ * mpi3mr_delete_sas_phy - Remove a single phy from port
+ * @mrioc: Adapter instance reference
+ * @mr_sas_port: Internal Port object
+ * @mr_sas_phy: Internal Phy object
+ * @host_node: Flag to indicate this is a host_node
+ *
+ * Return: None.
+ */
+static void mpi3mr_delete_sas_phy(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_port *mr_sas_port,
+ struct mpi3mr_sas_phy *mr_sas_phy, u8 host_node)
+{
+ u64 sas_address = mr_sas_port->remote_identify.sas_address;
+
+ dev_info(&mr_sas_phy->phy->dev,
+ "remove: sas_address(0x%016llx), phy(%d)\n",
+ (unsigned long long) sas_address, mr_sas_phy->phy_id);
+
+ list_del(&mr_sas_phy->port_siblings);
+ mr_sas_port->num_phys--;
+
+ if (host_node) {
+ mr_sas_port->phy_mask &= ~(1 << mr_sas_phy->phy_id);
+
+ if (mr_sas_port->lowest_phy == mr_sas_phy->phy_id)
+ mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
+ }
+ sas_port_delete_phy(mr_sas_port->port, mr_sas_phy->phy);
+ mr_sas_phy->phy_belongs_to_port = 0;
+}
+
+/**
+ * mpi3mr_add_sas_phy - Adding a single phy to a port
+ * @mrioc: Adapter instance reference
+ * @mr_sas_port: Internal Port object
+ * @mr_sas_phy: Internal Phy object
+ * @host_node: Flag to indicate this is a host_node
+ *
+ * Return: None.
+ */
+static void mpi3mr_add_sas_phy(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_port *mr_sas_port,
+ struct mpi3mr_sas_phy *mr_sas_phy, u8 host_node)
+{
+ u64 sas_address = mr_sas_port->remote_identify.sas_address;
+
+ dev_info(&mr_sas_phy->phy->dev,
+ "add: sas_address(0x%016llx), phy(%d)\n", (unsigned long long)
+ sas_address, mr_sas_phy->phy_id);
+
+ list_add_tail(&mr_sas_phy->port_siblings, &mr_sas_port->phy_list);
+ mr_sas_port->num_phys++;
+ if (host_node) {
+ mr_sas_port->phy_mask |= (1 << mr_sas_phy->phy_id);
+
+ if (mr_sas_phy->phy_id < mr_sas_port->lowest_phy)
+ mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
+ }
+ sas_port_add_phy(mr_sas_port->port, mr_sas_phy->phy);
+ mr_sas_phy->phy_belongs_to_port = 1;
+}
+
+/**
+ * mpi3mr_add_phy_to_an_existing_port - add phy to existing port
+ * @mrioc: Adapter instance reference
+ * @mr_sas_node: Internal sas node object (expander or host)
+ * @mr_sas_phy: Internal Phy object *
+ * @sas_address: SAS address of device/expander were phy needs
+ * to be added to
+ * @hba_port: HBA port entry
+ *
+ * Return: None.
+ */
+static void mpi3mr_add_phy_to_an_existing_port(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_node *mr_sas_node, struct mpi3mr_sas_phy *mr_sas_phy,
+ u64 sas_address, struct mpi3mr_hba_port *hba_port)
+{
+ struct mpi3mr_sas_port *mr_sas_port;
+ struct mpi3mr_sas_phy *srch_phy;
+
+ if (mr_sas_phy->phy_belongs_to_port == 1)
+ return;
+
+ if (!hba_port)
+ return;
+
+ list_for_each_entry(mr_sas_port, &mr_sas_node->sas_port_list,
+ port_list) {
+ if (mr_sas_port->remote_identify.sas_address !=
+ sas_address)
+ continue;
+ if (mr_sas_port->hba_port != hba_port)
+ continue;
+ list_for_each_entry(srch_phy, &mr_sas_port->phy_list,
+ port_siblings) {
+ if (srch_phy == mr_sas_phy)
+ return;
+ }
+ mpi3mr_add_sas_phy(mrioc, mr_sas_port, mr_sas_phy, mr_sas_node->host_node);
+ return;
+ }
+}
+
+/**
+ * mpi3mr_delete_sas_port - helper function to removing a port
+ * @mrioc: Adapter instance reference
+ * @mr_sas_port: Internal Port object
+ *
+ * Return: None.
+ */
+static void mpi3mr_delete_sas_port(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_port *mr_sas_port)
+{
+ u64 sas_address = mr_sas_port->remote_identify.sas_address;
+ struct mpi3mr_hba_port *hba_port = mr_sas_port->hba_port;
+ enum sas_device_type device_type =
+ mr_sas_port->remote_identify.device_type;
+
+ dev_info(&mr_sas_port->port->dev,
+ "remove: sas_address(0x%016llx)\n",
+ (unsigned long long) sas_address);
+
+ if (device_type == SAS_END_DEVICE)
+ mpi3mr_remove_device_by_sas_address(mrioc, sas_address,
+ hba_port);
+
+ else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
+ device_type == SAS_FANOUT_EXPANDER_DEVICE)
+ mpi3mr_expander_remove(mrioc, sas_address, hba_port);
+}
+
+/**
+ * mpi3mr_del_phy_from_an_existing_port - del phy from a port
+ * @mrioc: Adapter instance reference
+ * @mr_sas_node: Internal sas node object (expander or host)
+ * @mr_sas_phy: Internal Phy object
+ *
+ * Return: None.
+ */
+static void mpi3mr_del_phy_from_an_existing_port(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_node *mr_sas_node, struct mpi3mr_sas_phy *mr_sas_phy)
+{
+ struct mpi3mr_sas_port *mr_sas_port, *next;
+ struct mpi3mr_sas_phy *srch_phy;
+
+ if (mr_sas_phy->phy_belongs_to_port == 0)
+ return;
+
+ list_for_each_entry_safe(mr_sas_port, next, &mr_sas_node->sas_port_list,
+ port_list) {
+ list_for_each_entry(srch_phy, &mr_sas_port->phy_list,
+ port_siblings) {
+ if (srch_phy != mr_sas_phy)
+ continue;
+ if ((mr_sas_port->num_phys == 1) &&
+ !mrioc->reset_in_progress)
+ mpi3mr_delete_sas_port(mrioc, mr_sas_port);
+ else
+ mpi3mr_delete_sas_phy(mrioc, mr_sas_port,
+ mr_sas_phy, mr_sas_node->host_node);
+ return;
+ }
+ }
+}
+
+/**
+ * mpi3mr_sas_port_sanity_check - sanity check while adding port
+ * @mrioc: Adapter instance reference
+ * @mr_sas_node: Internal sas node object (expander or host)
+ * @sas_address: SAS address of device/expander
+ * @hba_port: HBA port entry
+ *
+ * Verifies whether the Phys attached to a device with the given
+ * SAS address already belongs to an existing sas port if so
+ * will remove those phys from the sas port
+ *
+ * Return: None.
+ */
+static void mpi3mr_sas_port_sanity_check(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_node *mr_sas_node, u64 sas_address,
+ struct mpi3mr_hba_port *hba_port)
+{
+ int i;
+
+ for (i = 0; i < mr_sas_node->num_phys; i++) {
+ if ((mr_sas_node->phy[i].remote_identify.sas_address !=
+ sas_address) || (mr_sas_node->phy[i].hba_port != hba_port))
+ continue;
+ if (mr_sas_node->phy[i].phy_belongs_to_port == 1)
+ mpi3mr_del_phy_from_an_existing_port(mrioc,
+ mr_sas_node, &mr_sas_node->phy[i]);
+ }
+}
+
+/**
+ * mpi3mr_set_identify - set identify for phys and end devices
+ * @mrioc: Adapter instance reference
+ * @handle: Firmware device handle
+ * @identify: SAS transport layer's identify info
+ *
+ * Populates sas identify info for a specific device.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int mpi3mr_set_identify(struct mpi3mr_ioc *mrioc, u16 handle,
+ struct sas_identify *identify)
+{
+
+ struct mpi3_device_page0 device_pg0;
+ struct mpi3_device0_sas_sata_format *sasinf;
+ u16 device_info;
+ u16 ioc_status;
+
+ if (mrioc->reset_in_progress) {
+ ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ if (mrioc->pci_err_recovery) {
+ ioc_err(mrioc, "%s: pci error recovery in progress!\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &device_pg0,
+ sizeof(device_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
+ return -ENXIO;
+ }
+
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
+ handle, ioc_status, __FILE__, __LINE__, __func__);
+ return -EIO;
+ }
+
+ memset(identify, 0, sizeof(struct sas_identify));
+ sasinf = &device_pg0.device_specific.sas_sata_format;
+ device_info = le16_to_cpu(sasinf->device_info);
+
+ /* sas_address */
+ identify->sas_address = le64_to_cpu(sasinf->sas_address);
+
+ /* phy number of the parent device this device is linked to */
+ identify->phy_identifier = sasinf->phy_num;
+
+ /* device_type */
+ switch (device_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) {
+ case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_NO_DEVICE:
+ identify->device_type = SAS_PHY_UNUSED;
+ break;
+ case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE:
+ identify->device_type = SAS_END_DEVICE;
+ break;
+ case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER:
+ identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
+ break;
+ }
+
+ /* initiator_port_protocols */
+ if (device_info & MPI3_SAS_DEVICE_INFO_SSP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
+ /* MPI3.0 doesn't have define for SATA INIT so setting both here*/
+ if (device_info & MPI3_SAS_DEVICE_INFO_STP_INITIATOR)
+ identify->initiator_port_protocols |= (SAS_PROTOCOL_STP |
+ SAS_PROTOCOL_SATA);
+ if (device_info & MPI3_SAS_DEVICE_INFO_SMP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
+
+ /* target_port_protocols */
+ if (device_info & MPI3_SAS_DEVICE_INFO_SSP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_SSP;
+ /* MPI3.0 doesn't have define for STP Target so setting both here*/
+ if (device_info & MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET)
+ identify->target_port_protocols |= (SAS_PROTOCOL_STP |
+ SAS_PROTOCOL_SATA);
+ if (device_info & MPI3_SAS_DEVICE_INFO_SMP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_SMP;
+ return 0;
+}
+
+/**
+ * mpi3mr_add_host_phy - report sas_host phy to SAS transport
+ * @mrioc: Adapter instance reference
+ * @mr_sas_phy: Internal Phy object
+ * @phy_pg0: SAS phy page 0
+ * @parent_dev: Prent device class object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int mpi3mr_add_host_phy(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_phy *mr_sas_phy, struct mpi3_sas_phy_page0 phy_pg0,
+ struct device *parent_dev)
+{
+ struct sas_phy *phy;
+ int phy_index = mr_sas_phy->phy_id;
+
+
+ INIT_LIST_HEAD(&mr_sas_phy->port_siblings);
+ phy = sas_phy_alloc(parent_dev, phy_index);
+ if (!phy) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if ((mpi3mr_set_identify(mrioc, mr_sas_phy->handle,
+ &mr_sas_phy->identify))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ phy->identify = mr_sas_phy->identify;
+ mr_sas_phy->attached_handle = le16_to_cpu(phy_pg0.attached_dev_handle);
+ if (mr_sas_phy->attached_handle)
+ mpi3mr_set_identify(mrioc, mr_sas_phy->attached_handle,
+ &mr_sas_phy->remote_identify);
+ phy->identify.phy_identifier = mr_sas_phy->phy_id;
+ phy->negotiated_linkrate = mpi3mr_convert_phy_link_rate(
+ (phy_pg0.negotiated_link_rate &
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
+ phy->minimum_linkrate_hw = mpi3mr_convert_phy_link_rate(
+ phy_pg0.hw_link_rate & MPI3_SAS_HWRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate_hw = mpi3mr_convert_phy_link_rate(
+ phy_pg0.hw_link_rate >> 4);
+ phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
+ phy_pg0.programmed_link_rate & MPI3_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
+ phy_pg0.programmed_link_rate >> 4);
+ phy->hostdata = mr_sas_phy->hba_port;
+
+ if ((sas_phy_add(phy))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
+ dev_info(&phy->dev,
+ "add: handle(0x%04x), sas_address(0x%016llx)\n"
+ "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
+ mr_sas_phy->handle, (unsigned long long)
+ mr_sas_phy->identify.sas_address,
+ mr_sas_phy->attached_handle,
+ (unsigned long long)
+ mr_sas_phy->remote_identify.sas_address);
+ mr_sas_phy->phy = phy;
+ return 0;
+}
+
+/**
+ * mpi3mr_add_expander_phy - report expander phy to transport
+ * @mrioc: Adapter instance reference
+ * @mr_sas_phy: Internal Phy object
+ * @expander_pg1: SAS Expander page 1
+ * @parent_dev: Parent device class object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int mpi3mr_add_expander_phy(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_phy *mr_sas_phy,
+ struct mpi3_sas_expander_page1 expander_pg1,
+ struct device *parent_dev)
+{
+ struct sas_phy *phy;
+ int phy_index = mr_sas_phy->phy_id;
+
+ INIT_LIST_HEAD(&mr_sas_phy->port_siblings);
+ phy = sas_phy_alloc(parent_dev, phy_index);
+ if (!phy) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if ((mpi3mr_set_identify(mrioc, mr_sas_phy->handle,
+ &mr_sas_phy->identify))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ phy->identify = mr_sas_phy->identify;
+ mr_sas_phy->attached_handle =
+ le16_to_cpu(expander_pg1.attached_dev_handle);
+ if (mr_sas_phy->attached_handle)
+ mpi3mr_set_identify(mrioc, mr_sas_phy->attached_handle,
+ &mr_sas_phy->remote_identify);
+ phy->identify.phy_identifier = mr_sas_phy->phy_id;
+ phy->negotiated_linkrate = mpi3mr_convert_phy_link_rate(
+ (expander_pg1.negotiated_link_rate &
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
+ phy->minimum_linkrate_hw = mpi3mr_convert_phy_link_rate(
+ expander_pg1.hw_link_rate & MPI3_SAS_HWRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate_hw = mpi3mr_convert_phy_link_rate(
+ expander_pg1.hw_link_rate >> 4);
+ phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
+ expander_pg1.programmed_link_rate & MPI3_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
+ expander_pg1.programmed_link_rate >> 4);
+ phy->hostdata = mr_sas_phy->hba_port;
+
+ if ((sas_phy_add(phy))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
+ dev_info(&phy->dev,
+ "add: handle(0x%04x), sas_address(0x%016llx)\n"
+ "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
+ mr_sas_phy->handle, (unsigned long long)
+ mr_sas_phy->identify.sas_address,
+ mr_sas_phy->attached_handle,
+ (unsigned long long)
+ mr_sas_phy->remote_identify.sas_address);
+ mr_sas_phy->phy = phy;
+ return 0;
+}
+
+/**
+ * mpi3mr_alloc_hba_port - alloc hba port object
+ * @mrioc: Adapter instance reference
+ * @port_id: Port number
+ *
+ * Alloc memory for hba port object.
+ */
+static struct mpi3mr_hba_port *
+mpi3mr_alloc_hba_port(struct mpi3mr_ioc *mrioc, u16 port_id)
+{
+ struct mpi3mr_hba_port *hba_port;
+
+ hba_port = kzalloc(sizeof(struct mpi3mr_hba_port),
+ GFP_KERNEL);
+ if (!hba_port)
+ return NULL;
+ hba_port->port_id = port_id;
+ ioc_info(mrioc, "hba_port entry: %p, port: %d is added to hba_port list\n",
+ hba_port, hba_port->port_id);
+ if (mrioc->reset_in_progress ||
+ mrioc->pci_err_recovery)
+ hba_port->flags = MPI3MR_HBA_PORT_FLAG_NEW;
+ list_add_tail(&hba_port->list, &mrioc->hba_port_table_list);
+ return hba_port;
+}
+
+/**
+ * mpi3mr_get_hba_port_by_id - find hba port by id
+ * @mrioc: Adapter instance reference
+ * @port_id: Port ID to search
+ *
+ * Return: mpi3mr_hba_port reference for the matched port
+ */
+
+struct mpi3mr_hba_port *mpi3mr_get_hba_port_by_id(struct mpi3mr_ioc *mrioc,
+ u8 port_id)
+{
+ struct mpi3mr_hba_port *port, *port_next;
+
+ list_for_each_entry_safe(port, port_next,
+ &mrioc->hba_port_table_list, list) {
+ if (port->port_id != port_id)
+ continue;
+ if (port->flags & MPI3MR_HBA_PORT_FLAG_DIRTY)
+ continue;
+ return port;
+ }
+
+ return NULL;
+}
+
+/**
+ * mpi3mr_update_links - refreshing SAS phy link changes
+ * @mrioc: Adapter instance reference
+ * @sas_address_parent: SAS address of parent expander or host
+ * @handle: Firmware device handle of attached device
+ * @phy_number: Phy number
+ * @link_rate: New link rate
+ * @hba_port: HBA port entry
+ *
+ * Return: None.
+ */
+void mpi3mr_update_links(struct mpi3mr_ioc *mrioc,
+ u64 sas_address_parent, u16 handle, u8 phy_number, u8 link_rate,
+ struct mpi3mr_hba_port *hba_port)
+{
+ unsigned long flags;
+ struct mpi3mr_sas_node *mr_sas_node;
+ struct mpi3mr_sas_phy *mr_sas_phy;
+
+ if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
+ return;
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
+ sas_address_parent, hba_port);
+ if (!mr_sas_node) {
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ return;
+ }
+
+ mr_sas_phy = &mr_sas_node->phy[phy_number];
+ mr_sas_phy->attached_handle = handle;
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ if (handle && (link_rate >= MPI3_SAS_NEG_LINK_RATE_1_5)) {
+ mpi3mr_set_identify(mrioc, handle,
+ &mr_sas_phy->remote_identify);
+ mpi3mr_add_phy_to_an_existing_port(mrioc, mr_sas_node,
+ mr_sas_phy, mr_sas_phy->remote_identify.sas_address,
+ hba_port);
+ } else
+ memset(&mr_sas_phy->remote_identify, 0, sizeof(struct
+ sas_identify));
+
+ if (mr_sas_phy->phy)
+ mr_sas_phy->phy->negotiated_linkrate =
+ mpi3mr_convert_phy_link_rate(link_rate);
+
+ if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
+ dev_info(&mr_sas_phy->phy->dev,
+ "refresh: parent sas_address(0x%016llx),\n"
+ "\tlink_rate(0x%02x), phy(%d)\n"
+ "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
+ (unsigned long long)sas_address_parent,
+ link_rate, phy_number, handle, (unsigned long long)
+ mr_sas_phy->remote_identify.sas_address);
+}
+
+/**
+ * mpi3mr_sas_host_refresh - refreshing sas host object contents
+ * @mrioc: Adapter instance reference
+ *
+ * This function refreshes the controllers phy information and
+ * updates the SAS transport layer with updated information,
+ * this is executed for each device addition or device info
+ * change events
+ *
+ * Return: None.
+ */
+void mpi3mr_sas_host_refresh(struct mpi3mr_ioc *mrioc)
+{
+ int i;
+ u8 link_rate;
+ u16 sz, port_id, attached_handle;
+ struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
+
+ dprint_transport_info(mrioc,
+ "updating handles for sas_host(0x%016llx)\n",
+ (unsigned long long)mrioc->sas_hba.sas_address);
+
+ sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
+ (mrioc->sas_hba.num_phys *
+ sizeof(struct mpi3_sas_io_unit0_phy_data));
+ sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_io_unit_pg0)
+ return;
+ if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ mrioc->sas_hba.handle = 0;
+ for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
+ if (sas_io_unit_pg0->phy_data[i].phy_flags &
+ (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
+ MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))
+ continue;
+ link_rate =
+ sas_io_unit_pg0->phy_data[i].negotiated_link_rate >> 4;
+ if (!mrioc->sas_hba.handle)
+ mrioc->sas_hba.handle = le16_to_cpu(
+ sas_io_unit_pg0->phy_data[i].controller_dev_handle);
+ port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
+ if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id)))
+ if (!mpi3mr_alloc_hba_port(mrioc, port_id))
+ goto out;
+
+ mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle;
+ attached_handle = le16_to_cpu(
+ sas_io_unit_pg0->phy_data[i].attached_dev_handle);
+ if (attached_handle && link_rate < MPI3_SAS_NEG_LINK_RATE_1_5)
+ link_rate = MPI3_SAS_NEG_LINK_RATE_1_5;
+ mrioc->sas_hba.phy[i].hba_port =
+ mpi3mr_get_hba_port_by_id(mrioc, port_id);
+ mpi3mr_update_links(mrioc, mrioc->sas_hba.sas_address,
+ attached_handle, i, link_rate,
+ mrioc->sas_hba.phy[i].hba_port);
+ }
+ out:
+ kfree(sas_io_unit_pg0);
+}
+
+/**
+ * mpi3mr_sas_host_add - create sas host object
+ * @mrioc: Adapter instance reference
+ *
+ * This function creates the controllers phy information and
+ * updates the SAS transport layer with updated information,
+ * this is executed for first device addition or device info
+ * change event.
+ *
+ * Return: None.
+ */
+void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
+{
+ int i;
+ u16 sz, num_phys = 1, port_id, ioc_status;
+ struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
+ struct mpi3_sas_phy_page0 phy_pg0;
+ struct mpi3_device_page0 dev_pg0;
+ struct mpi3_enclosure_page0 encl_pg0;
+ struct mpi3_device0_sas_sata_format *sasinf;
+
+ sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
+ (num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data));
+ sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_io_unit_pg0)
+ return;
+
+ if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ num_phys = sas_io_unit_pg0->num_phys;
+ kfree(sas_io_unit_pg0);
+
+ mrioc->sas_hba.host_node = 1;
+ INIT_LIST_HEAD(&mrioc->sas_hba.sas_port_list);
+ mrioc->sas_hba.parent_dev = &mrioc->shost->shost_gendev;
+ mrioc->sas_hba.phy = kcalloc(num_phys,
+ sizeof(struct mpi3mr_sas_phy), GFP_KERNEL);
+ if (!mrioc->sas_hba.phy)
+ return;
+
+ mrioc->sas_hba.num_phys = num_phys;
+
+ sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
+ (num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data));
+ sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_io_unit_pg0)
+ return;
+
+ if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ mrioc->sas_hba.handle = 0;
+ for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
+ if (sas_io_unit_pg0->phy_data[i].phy_flags &
+ (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
+ MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))
+ continue;
+ if (mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
+ sizeof(struct mpi3_sas_phy_page0),
+ MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, i)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ if (!mrioc->sas_hba.handle)
+ mrioc->sas_hba.handle = le16_to_cpu(
+ sas_io_unit_pg0->phy_data[i].controller_dev_handle);
+ port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
+
+ if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id)))
+ if (!mpi3mr_alloc_hba_port(mrioc, port_id))
+ goto out;
+
+ mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle;
+ mrioc->sas_hba.phy[i].phy_id = i;
+ mrioc->sas_hba.phy[i].hba_port =
+ mpi3mr_get_hba_port_by_id(mrioc, port_id);
+ mpi3mr_add_host_phy(mrioc, &mrioc->sas_hba.phy[i],
+ phy_pg0, mrioc->sas_hba.parent_dev);
+ }
+ if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
+ sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
+ mrioc->sas_hba.handle))) {
+ ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
+ goto out;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
+ mrioc->sas_hba.handle, ioc_status, __FILE__, __LINE__,
+ __func__);
+ goto out;
+ }
+ mrioc->sas_hba.enclosure_handle =
+ le16_to_cpu(dev_pg0.enclosure_handle);
+ sasinf = &dev_pg0.device_specific.sas_sata_format;
+ mrioc->sas_hba.sas_address =
+ le64_to_cpu(sasinf->sas_address);
+ ioc_info(mrioc,
+ "host_add: handle(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
+ mrioc->sas_hba.handle,
+ (unsigned long long) mrioc->sas_hba.sas_address,
+ mrioc->sas_hba.num_phys);
+
+ if (mrioc->sas_hba.enclosure_handle) {
+ if (!(mpi3mr_cfg_get_enclosure_pg0(mrioc, &ioc_status,
+ &encl_pg0, sizeof(encl_pg0),
+ MPI3_ENCLOS_PGAD_FORM_HANDLE,
+ mrioc->sas_hba.enclosure_handle)) &&
+ (ioc_status == MPI3_IOCSTATUS_SUCCESS))
+ mrioc->sas_hba.enclosure_logical_id =
+ le64_to_cpu(encl_pg0.enclosure_logical_id);
+ }
+
+out:
+ kfree(sas_io_unit_pg0);
+}
+
+/**
+ * mpi3mr_sas_port_add - Expose the SAS device to the SAS TL
+ * @mrioc: Adapter instance reference
+ * @handle: Firmware device handle of the attached device
+ * @sas_address_parent: sas address of parent expander or host
+ * @hba_port: HBA port entry
+ *
+ * This function creates a new sas port object for the given end
+ * device matching sas address and hba_port and adds it to the
+ * sas_node's sas_port_list and expose the attached sas device
+ * to the SAS transport layer through sas_rphy_add.
+ *
+ * Returns a valid mpi3mr_sas_port reference or NULL.
+ */
+static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
+ u16 handle, u64 sas_address_parent, struct mpi3mr_hba_port *hba_port)
+{
+ struct mpi3mr_sas_phy *mr_sas_phy, *next;
+ struct mpi3mr_sas_port *mr_sas_port;
+ unsigned long flags;
+ struct mpi3mr_sas_node *mr_sas_node;
+ struct sas_rphy *rphy;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ int i;
+ struct sas_port *port;
+
+ if (!hba_port) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ mr_sas_port = kzalloc(sizeof(struct mpi3mr_sas_port), GFP_KERNEL);
+ if (!mr_sas_port)
+ return NULL;
+
+ INIT_LIST_HEAD(&mr_sas_port->port_list);
+ INIT_LIST_HEAD(&mr_sas_port->phy_list);
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
+ sas_address_parent, hba_port);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ if (!mr_sas_node) {
+ ioc_err(mrioc, "%s:could not find parent sas_address(0x%016llx)!\n",
+ __func__, (unsigned long long)sas_address_parent);
+ goto out_fail;
+ }
+
+ if ((mpi3mr_set_identify(mrioc, handle,
+ &mr_sas_port->remote_identify))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ if (mr_sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ mr_sas_port->hba_port = hba_port;
+ mpi3mr_sas_port_sanity_check(mrioc, mr_sas_node,
+ mr_sas_port->remote_identify.sas_address, hba_port);
+
+ if (mr_sas_node->host_node && mr_sas_node->num_phys >=
+ sizeof(mr_sas_port->phy_mask) * 8)
+ ioc_info(mrioc, "max port count %u could be too high\n",
+ mr_sas_node->num_phys);
+
+ for (i = 0; i < mr_sas_node->num_phys; i++) {
+ if ((mr_sas_node->phy[i].remote_identify.sas_address !=
+ mr_sas_port->remote_identify.sas_address) ||
+ (mr_sas_node->phy[i].hba_port != hba_port))
+ continue;
+
+ if (mr_sas_node->host_node && (i >= sizeof(mr_sas_port->phy_mask) * 8)) {
+ ioc_warn(mrioc, "skipping port %u, max allowed value is %zu\n",
+ i, sizeof(mr_sas_port->phy_mask) * 8);
+ goto out_fail;
+ }
+ list_add_tail(&mr_sas_node->phy[i].port_siblings,
+ &mr_sas_port->phy_list);
+ mr_sas_port->num_phys++;
+ if (mr_sas_node->host_node)
+ mr_sas_port->phy_mask |= (1 << i);
+ }
+
+ if (!mr_sas_port->num_phys) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ if (mr_sas_node->host_node)
+ mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
+
+ if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+ tgtdev = mpi3mr_get_tgtdev_by_addr(mrioc,
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_port->hba_port);
+
+ if (!tgtdev) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+ tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 1;
+ }
+
+ if (!mr_sas_node->parent_dev) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ port = sas_port_alloc_num(mr_sas_node->parent_dev);
+ if ((sas_port_add(port))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ list_for_each_entry(mr_sas_phy, &mr_sas_port->phy_list,
+ port_siblings) {
+ if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
+ dev_info(&port->dev,
+ "add: handle(0x%04x), sas_address(0x%016llx), phy(%d)\n",
+ handle, (unsigned long long)
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_phy->phy_id);
+ sas_port_add_phy(port, mr_sas_phy->phy);
+ mr_sas_phy->phy_belongs_to_port = 1;
+ mr_sas_phy->hba_port = hba_port;
+ }
+
+ mr_sas_port->port = port;
+ if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+ rphy = sas_end_device_alloc(port);
+ tgtdev->dev_spec.sas_sata_inf.rphy = rphy;
+ } else {
+ rphy = sas_expander_alloc(port,
+ mr_sas_port->remote_identify.device_type);
+ }
+ rphy->identify = mr_sas_port->remote_identify;
+
+ if (mrioc->current_event)
+ mrioc->current_event->pending_at_sml = 1;
+
+ if ((sas_rphy_add(rphy))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ }
+ if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+ tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 0;
+ tgtdev->dev_spec.sas_sata_inf.sas_transport_attached = 1;
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+
+ dev_info(&rphy->dev,
+ "%s: added: handle(0x%04x), sas_address(0x%016llx)\n",
+ __func__, handle, (unsigned long long)
+ mr_sas_port->remote_identify.sas_address);
+
+ mr_sas_port->rphy = rphy;
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ list_add_tail(&mr_sas_port->port_list, &mr_sas_node->sas_port_list);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ if (mrioc->current_event) {
+ mrioc->current_event->pending_at_sml = 0;
+ if (mrioc->current_event->discard)
+ mpi3mr_print_device_event_notice(mrioc, true);
+ }
+
+ /* fill in report manufacture */
+ if (mr_sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mr_sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE)
+ mpi3mr_report_manufacture(mrioc,
+ mr_sas_port->remote_identify.sas_address,
+ rphy_to_expander_device(rphy), hba_port->port_id);
+
+ return mr_sas_port;
+
+ out_fail:
+ list_for_each_entry_safe(mr_sas_phy, next, &mr_sas_port->phy_list,
+ port_siblings)
+ list_del(&mr_sas_phy->port_siblings);
+ kfree(mr_sas_port);
+ return NULL;
+}
+
+/**
+ * mpi3mr_sas_port_remove - remove port from the list
+ * @mrioc: Adapter instance reference
+ * @sas_address: SAS address of attached device
+ * @sas_address_parent: SAS address of parent expander or host
+ * @hba_port: HBA port entry
+ *
+ * Removing object and freeing associated memory from the
+ * sas_port_list.
+ *
+ * Return: None
+ */
+static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
+ u64 sas_address_parent, struct mpi3mr_hba_port *hba_port)
+{
+ int i;
+ unsigned long flags;
+ struct mpi3mr_sas_port *mr_sas_port, *next;
+ struct mpi3mr_sas_node *mr_sas_node;
+ u8 found = 0;
+ struct mpi3mr_sas_phy *mr_sas_phy, *next_phy;
+ struct mpi3mr_hba_port *srch_port, *hba_port_next = NULL;
+
+ if (!hba_port)
+ return;
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
+ sas_address_parent, hba_port);
+ if (!mr_sas_node) {
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ return;
+ }
+ list_for_each_entry_safe(mr_sas_port, next, &mr_sas_node->sas_port_list,
+ port_list) {
+ if (mr_sas_port->remote_identify.sas_address != sas_address)
+ continue;
+ if (mr_sas_port->hba_port != hba_port)
+ continue;
+ found = 1;
+ list_del(&mr_sas_port->port_list);
+ goto out;
+ }
+
+ out:
+ if (!found) {
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ return;
+ }
+
+ if (mr_sas_node->host_node) {
+ list_for_each_entry_safe(srch_port, hba_port_next,
+ &mrioc->hba_port_table_list, list) {
+ if (srch_port != hba_port)
+ continue;
+ ioc_info(mrioc,
+ "removing hba_port entry: %p port: %d from hba_port list\n",
+ srch_port, srch_port->port_id);
+ list_del(&hba_port->list);
+ kfree(hba_port);
+ break;
+ }
+ }
+
+ for (i = 0; i < mr_sas_node->num_phys; i++) {
+ if (mr_sas_node->phy[i].remote_identify.sas_address ==
+ sas_address)
+ memset(&mr_sas_node->phy[i].remote_identify, 0,
+ sizeof(struct sas_identify));
+ }
+
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ if (mrioc->current_event)
+ mrioc->current_event->pending_at_sml = 1;
+
+ list_for_each_entry_safe(mr_sas_phy, next_phy,
+ &mr_sas_port->phy_list, port_siblings) {
+ if ((!mrioc->stop_drv_processing) &&
+ (mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
+ dev_info(&mr_sas_port->port->dev,
+ "remove: sas_address(0x%016llx), phy(%d)\n",
+ (unsigned long long)
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_phy->phy_id);
+ mr_sas_phy->phy_belongs_to_port = 0;
+ if (!mrioc->stop_drv_processing)
+ sas_port_delete_phy(mr_sas_port->port,
+ mr_sas_phy->phy);
+ list_del(&mr_sas_phy->port_siblings);
+ }
+ if (!mrioc->stop_drv_processing)
+ sas_port_delete(mr_sas_port->port);
+ ioc_info(mrioc, "%s: removed sas_address(0x%016llx)\n",
+ __func__, (unsigned long long)sas_address);
+
+ if (mrioc->current_event) {
+ mrioc->current_event->pending_at_sml = 0;
+ if (mrioc->current_event->discard)
+ mpi3mr_print_device_event_notice(mrioc, false);
+ }
+
+ kfree(mr_sas_port);
+}
+
+/**
+ * struct host_port - host port details
+ * @sas_address: SAS Address of the attached device
+ * @phy_mask: phy mask of host port
+ * @handle: Device Handle of attached device
+ * @iounit_port_id: port ID
+ * @used: host port is already matched with sas port from sas_port_list
+ * @lowest_phy: lowest phy ID of host port
+ */
+struct host_port {
+ u64 sas_address;
+ u64 phy_mask;
+ u16 handle;
+ u8 iounit_port_id;
+ u8 used;
+ u8 lowest_phy;
+};
+
+/**
+ * mpi3mr_update_mr_sas_port - update sas port objects during reset
+ * @mrioc: Adapter instance reference
+ * @h_port: host_port object
+ * @mr_sas_port: sas_port objects which needs to be updated
+ *
+ * Update the port ID of sas port object. Also add the phys if new phys got
+ * added to current sas port and remove the phys if some phys are moved
+ * out of the current sas port.
+ *
+ * Return: Nothing.
+ */
+static void
+mpi3mr_update_mr_sas_port(struct mpi3mr_ioc *mrioc, struct host_port *h_port,
+ struct mpi3mr_sas_port *mr_sas_port)
+{
+ struct mpi3mr_sas_phy *mr_sas_phy;
+ u64 phy_mask_xor;
+ u64 phys_to_be_added, phys_to_be_removed;
+ int i;
+
+ h_port->used = 1;
+ mr_sas_port->marked_responding = 1;
+
+ dev_info(&mr_sas_port->port->dev,
+ "sas_address(0x%016llx), old: port_id %d phy_mask 0x%llx, new: port_id %d phy_mask:0x%llx\n",
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_port->hba_port->port_id, mr_sas_port->phy_mask,
+ h_port->iounit_port_id, h_port->phy_mask);
+
+ mr_sas_port->hba_port->port_id = h_port->iounit_port_id;
+ mr_sas_port->hba_port->flags &= ~MPI3MR_HBA_PORT_FLAG_DIRTY;
+
+ /* Get the newly added phys bit map & removed phys bit map */
+ phy_mask_xor = mr_sas_port->phy_mask ^ h_port->phy_mask;
+ phys_to_be_added = h_port->phy_mask & phy_mask_xor;
+ phys_to_be_removed = mr_sas_port->phy_mask & phy_mask_xor;
+
+ /*
+ * Register these new phys to current mr_sas_port's port.
+ * if these phys are previously registered with another port
+ * then delete these phys from that port first.
+ */
+ for_each_set_bit(i, (ulong *) &phys_to_be_added, BITS_PER_TYPE(u64)) {
+ mr_sas_phy = &mrioc->sas_hba.phy[i];
+ if (mr_sas_phy->phy_belongs_to_port)
+ mpi3mr_del_phy_from_an_existing_port(mrioc,
+ &mrioc->sas_hba, mr_sas_phy);
+ mpi3mr_add_phy_to_an_existing_port(mrioc,
+ &mrioc->sas_hba, mr_sas_phy,
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_port->hba_port);
+ }
+
+ /* Delete the phys which are not part of current mr_sas_port's port. */
+ for_each_set_bit(i, (ulong *) &phys_to_be_removed, BITS_PER_TYPE(u64)) {
+ mr_sas_phy = &mrioc->sas_hba.phy[i];
+ if (mr_sas_phy->phy_belongs_to_port)
+ mpi3mr_del_phy_from_an_existing_port(mrioc,
+ &mrioc->sas_hba, mr_sas_phy);
+ }
+}
+
+/**
+ * mpi3mr_refresh_sas_ports - update host's sas ports during reset
+ * @mrioc: Adapter instance reference
+ *
+ * Update the host's sas ports during reset by checking whether
+ * sas ports are still intact or not. Add/remove phys if any hba
+ * phys are (moved in)/(moved out) of sas port. Also update
+ * io_unit_port if it got changed during reset.
+ *
+ * Return: Nothing.
+ */
+void
+mpi3mr_refresh_sas_ports(struct mpi3mr_ioc *mrioc)
+{
+ struct host_port *h_port = NULL;
+ int i, j, found, host_port_count = 0, port_idx;
+ u16 sz, attached_handle, ioc_status;
+ struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
+ struct mpi3_device_page0 dev_pg0;
+ struct mpi3_device0_sas_sata_format *sasinf;
+ struct mpi3mr_sas_port *mr_sas_port;
+
+ sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
+ (mrioc->sas_hba.num_phys *
+ sizeof(struct mpi3_sas_io_unit0_phy_data));
+ sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_io_unit_pg0)
+ return;
+ h_port = kcalloc(64, sizeof(struct host_port), GFP_KERNEL);
+ if (!h_port)
+ goto out;
+
+ if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ /* Create a new expander port table */
+ for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
+ attached_handle = le16_to_cpu(
+ sas_io_unit_pg0->phy_data[i].attached_dev_handle);
+ if (!attached_handle)
+ continue;
+ found = 0;
+ for (j = 0; j < host_port_count; j++) {
+ if (h_port[j].handle == attached_handle) {
+ h_port[j].phy_mask |= (1 << i);
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ continue;
+ if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
+ sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
+ attached_handle))) {
+ dprint_reset(mrioc,
+ "failed to read dev_pg0 for handle(0x%04x) at %s:%d/%s()!\n",
+ attached_handle, __FILE__, __LINE__, __func__);
+ continue;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_reset(mrioc,
+ "ioc_status(0x%x) while reading dev_pg0 for handle(0x%04x) at %s:%d/%s()!\n",
+ ioc_status, attached_handle,
+ __FILE__, __LINE__, __func__);
+ continue;
+ }
+ sasinf = &dev_pg0.device_specific.sas_sata_format;
+
+ port_idx = host_port_count;
+ h_port[port_idx].sas_address = le64_to_cpu(sasinf->sas_address);
+ h_port[port_idx].handle = attached_handle;
+ h_port[port_idx].phy_mask = (1 << i);
+ h_port[port_idx].iounit_port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
+ h_port[port_idx].lowest_phy = sasinf->phy_num;
+ h_port[port_idx].used = 0;
+ host_port_count++;
+ }
+
+ if (!host_port_count)
+ goto out;
+
+ if (mrioc->logging_level & MPI3_DEBUG_RESET) {
+ ioc_info(mrioc, "Host port details before reset\n");
+ list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
+ port_list) {
+ ioc_info(mrioc,
+ "port_id:%d, sas_address:(0x%016llx), phy_mask:(0x%llx), lowest phy id:%d\n",
+ mr_sas_port->hba_port->port_id,
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_port->phy_mask, mr_sas_port->lowest_phy);
+ }
+ mr_sas_port = NULL;
+ ioc_info(mrioc, "Host port details after reset\n");
+ for (i = 0; i < host_port_count; i++) {
+ ioc_info(mrioc,
+ "port_id:%d, sas_address:(0x%016llx), phy_mask:(0x%llx), lowest phy id:%d\n",
+ h_port[i].iounit_port_id, h_port[i].sas_address,
+ h_port[i].phy_mask, h_port[i].lowest_phy);
+ }
+ }
+
+ /* mark all host sas port entries as dirty */
+ list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
+ port_list) {
+ mr_sas_port->marked_responding = 0;
+ mr_sas_port->hba_port->flags |= MPI3MR_HBA_PORT_FLAG_DIRTY;
+ }
+
+ /* First check for matching lowest phy */
+ for (i = 0; i < host_port_count; i++) {
+ mr_sas_port = NULL;
+ list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
+ port_list) {
+ if (mr_sas_port->marked_responding)
+ continue;
+ if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
+ continue;
+ if (h_port[i].lowest_phy == mr_sas_port->lowest_phy) {
+ mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
+ break;
+ }
+ }
+ }
+
+ /* In case if lowest phy is got enabled or disabled during reset */
+ for (i = 0; i < host_port_count; i++) {
+ if (h_port[i].used)
+ continue;
+ mr_sas_port = NULL;
+ list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
+ port_list) {
+ if (mr_sas_port->marked_responding)
+ continue;
+ if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
+ continue;
+ if (h_port[i].phy_mask & mr_sas_port->phy_mask) {
+ mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
+ break;
+ }
+ }
+ }
+
+ /* In case if expander cable is removed & connected to another HBA port during reset */
+ for (i = 0; i < host_port_count; i++) {
+ if (h_port[i].used)
+ continue;
+ mr_sas_port = NULL;
+ list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
+ port_list) {
+ if (mr_sas_port->marked_responding)
+ continue;
+ if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
+ continue;
+ mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
+ break;
+ }
+ }
+out:
+ kfree(h_port);
+ kfree(sas_io_unit_pg0);
+}
+
+/**
+ * mpi3mr_refresh_expanders - Refresh expander device exposure
+ * @mrioc: Adapter instance reference
+ *
+ * This is executed post controller reset to identify any
+ * missing expander devices during reset and remove from the upper layers
+ * or expose any newly detected expander device to the upper layers.
+ *
+ * Return: Nothing.
+ */
+void
+mpi3mr_refresh_expanders(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_sas_node *sas_expander, *sas_expander_next;
+ struct mpi3_sas_expander_page0 expander_pg0;
+ u16 ioc_status, handle;
+ u64 sas_address;
+ int i;
+ unsigned long flags;
+ struct mpi3mr_hba_port *hba_port;
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
+ sas_expander->non_responding = 1;
+ }
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ sas_expander = NULL;
+
+ handle = 0xffff;
+
+ /* Search for responding expander devices and add them if they are newly got added */
+ while (true) {
+ if ((mpi3mr_cfg_get_sas_exp_pg0(mrioc, &ioc_status, &expander_pg0,
+ sizeof(struct mpi3_sas_expander_page0),
+ MPI3_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ dprint_reset(mrioc,
+ "failed to read exp pg0 for handle(0x%04x) at %s:%d/%s()!\n",
+ handle, __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_reset(mrioc,
+ "ioc_status(0x%x) while reading exp pg0 for handle:(0x%04x), %s:%d/%s()!\n",
+ ioc_status, handle, __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ handle = le16_to_cpu(expander_pg0.dev_handle);
+ sas_address = le64_to_cpu(expander_pg0.sas_address);
+ hba_port = mpi3mr_get_hba_port_by_id(mrioc, expander_pg0.io_unit_port);
+
+ if (!hba_port) {
+ mpi3mr_sas_host_refresh(mrioc);
+ mpi3mr_expander_add(mrioc, handle);
+ continue;
+ }
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ sas_expander =
+ mpi3mr_expander_find_by_sas_address(mrioc,
+ sas_address, hba_port);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ if (!sas_expander) {
+ mpi3mr_sas_host_refresh(mrioc);
+ mpi3mr_expander_add(mrioc, handle);
+ continue;
+ }
+
+ sas_expander->non_responding = 0;
+ if (sas_expander->handle == handle)
+ continue;
+
+ sas_expander->handle = handle;
+ for (i = 0 ; i < sas_expander->num_phys ; i++)
+ sas_expander->phy[i].handle = handle;
+ }
+
+ /*
+ * Delete non responding expander devices and the corresponding
+ * hba_port if the non responding expander device's parent device
+ * is a host node.
+ */
+ sas_expander = NULL;
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ list_for_each_entry_safe_reverse(sas_expander, sas_expander_next,
+ &mrioc->sas_expander_list, list) {
+ if (sas_expander->non_responding) {
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ mpi3mr_expander_node_remove(mrioc, sas_expander);
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+}
+
+/**
+ * mpi3mr_expander_node_add - insert an expander to the list.
+ * @mrioc: Adapter instance reference
+ * @sas_expander: Expander sas node
+ * Context: This function will acquire sas_node_lock.
+ *
+ * Adding new object to the ioc->sas_expander_list.
+ *
+ * Return: None.
+ */
+static void mpi3mr_expander_node_add(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_node *sas_expander)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ list_add_tail(&sas_expander->list, &mrioc->sas_expander_list);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+}
+
+/**
+ * mpi3mr_expander_add - Create expander object
+ * @mrioc: Adapter instance reference
+ * @handle: Expander firmware device handle
+ *
+ * This function creating expander object, stored in
+ * sas_expander_list and expose it to the SAS transport
+ * layer.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int mpi3mr_expander_add(struct mpi3mr_ioc *mrioc, u16 handle)
+{
+ struct mpi3mr_sas_node *sas_expander;
+ struct mpi3mr_enclosure_node *enclosure_dev;
+ struct mpi3_sas_expander_page0 expander_pg0;
+ struct mpi3_sas_expander_page1 expander_pg1;
+ u16 ioc_status, parent_handle, temp_handle;
+ u64 sas_address, sas_address_parent = 0;
+ int i;
+ unsigned long flags;
+ u8 port_id, link_rate;
+ struct mpi3mr_sas_port *mr_sas_port = NULL;
+ struct mpi3mr_hba_port *hba_port;
+ u32 phynum_handle;
+ int rc = 0;
+
+ if (!handle)
+ return -1;
+
+ if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
+ return -1;
+
+ if ((mpi3mr_cfg_get_sas_exp_pg0(mrioc, &ioc_status, &expander_pg0,
+ sizeof(expander_pg0), MPI3_SAS_EXPAND_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ parent_handle = le16_to_cpu(expander_pg0.parent_dev_handle);
+ if (mpi3mr_get_sas_address(mrioc, parent_handle, &sas_address_parent)
+ != 0) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ port_id = expander_pg0.io_unit_port;
+ hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id);
+ if (!hba_port) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ if (sas_address_parent != mrioc->sas_hba.sas_address) {
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ sas_expander =
+ mpi3mr_expander_find_by_sas_address(mrioc,
+ sas_address_parent, hba_port);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ if (!sas_expander) {
+ rc = mpi3mr_expander_add(mrioc, parent_handle);
+ if (rc != 0)
+ return rc;
+ } else {
+ /*
+ * When there is a parent expander present, update it's
+ * phys where child expander is connected with the link
+ * speed, attached dev handle and sas address.
+ */
+ for (i = 0 ; i < sas_expander->num_phys ; i++) {
+ phynum_handle =
+ (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
+ parent_handle;
+ if (mpi3mr_cfg_get_sas_exp_pg1(mrioc,
+ &ioc_status, &expander_pg1,
+ sizeof(expander_pg1),
+ MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
+ phynum_handle)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ return rc;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ return rc;
+ }
+ temp_handle = le16_to_cpu(
+ expander_pg1.attached_dev_handle);
+ if (temp_handle != handle)
+ continue;
+ link_rate = (expander_pg1.negotiated_link_rate &
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
+ if (link_rate < MPI3_SAS_NEG_LINK_RATE_1_5)
+ link_rate = MPI3_SAS_NEG_LINK_RATE_1_5;
+ mpi3mr_update_links(mrioc, sas_address_parent,
+ handle, i, link_rate, hba_port);
+ }
+ }
+ }
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ sas_address = le64_to_cpu(expander_pg0.sas_address);
+ sas_expander = mpi3mr_expander_find_by_sas_address(mrioc,
+ sas_address, hba_port);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ if (sas_expander)
+ return 0;
+
+ sas_expander = kzalloc(sizeof(struct mpi3mr_sas_node),
+ GFP_KERNEL);
+ if (!sas_expander)
+ return -ENOMEM;
+
+ sas_expander->handle = handle;
+ sas_expander->num_phys = expander_pg0.num_phys;
+ sas_expander->sas_address_parent = sas_address_parent;
+ sas_expander->sas_address = sas_address;
+ sas_expander->hba_port = hba_port;
+
+ ioc_info(mrioc,
+ "expander_add: handle(0x%04x), parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
+ handle, parent_handle, (unsigned long long)
+ sas_expander->sas_address, sas_expander->num_phys);
+
+ if (!sas_expander->num_phys) {
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->phy = kcalloc(sas_expander->num_phys,
+ sizeof(struct mpi3mr_sas_phy), GFP_KERNEL);
+ if (!sas_expander->phy) {
+ rc = -1;
+ goto out_fail;
+ }
+
+ INIT_LIST_HEAD(&sas_expander->sas_port_list);
+ mr_sas_port = mpi3mr_sas_port_add(mrioc, handle, sas_address_parent,
+ sas_expander->hba_port);
+ if (!mr_sas_port) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->parent_dev = &mr_sas_port->rphy->dev;
+ sas_expander->rphy = mr_sas_port->rphy;
+
+ for (i = 0 ; i < sas_expander->num_phys ; i++) {
+ phynum_handle = (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
+ handle;
+ if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, &ioc_status,
+ &expander_pg1, sizeof(expander_pg1),
+ MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
+ phynum_handle)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+
+ sas_expander->phy[i].handle = handle;
+ sas_expander->phy[i].phy_id = i;
+ sas_expander->phy[i].hba_port = hba_port;
+
+ if ((mpi3mr_add_expander_phy(mrioc, &sas_expander->phy[i],
+ expander_pg1, sas_expander->parent_dev))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ }
+
+ if (sas_expander->enclosure_handle) {
+ enclosure_dev =
+ mpi3mr_enclosure_find_by_handle(mrioc,
+ sas_expander->enclosure_handle);
+ if (enclosure_dev)
+ sas_expander->enclosure_logical_id = le64_to_cpu(
+ enclosure_dev->pg0.enclosure_logical_id);
+ }
+
+ mpi3mr_expander_node_add(mrioc, sas_expander);
+ return 0;
+
+out_fail:
+
+ if (mr_sas_port)
+ mpi3mr_sas_port_remove(mrioc,
+ sas_expander->sas_address,
+ sas_address_parent, sas_expander->hba_port);
+ kfree(sas_expander->phy);
+ kfree(sas_expander);
+ return rc;
+}
+
+/**
+ * mpi3mr_expander_node_remove - recursive removal of expander.
+ * @mrioc: Adapter instance reference
+ * @sas_expander: Expander device object
+ *
+ * Removes expander object and freeing associated memory from
+ * the sas_expander_list and removes the same from SAS TL, if
+ * one of the attached device is an expander then it recursively
+ * removes the expander device too.
+ *
+ * Return nothing.
+ */
+void mpi3mr_expander_node_remove(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_sas_node *sas_expander)
+{
+ struct mpi3mr_sas_port *mr_sas_port, *next;
+ unsigned long flags;
+ u8 port_id;
+
+ /* remove sibling ports attached to this expander */
+ list_for_each_entry_safe(mr_sas_port, next,
+ &sas_expander->sas_port_list, port_list) {
+ if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
+ return;
+ if (mr_sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE)
+ mpi3mr_remove_device_by_sas_address(mrioc,
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_port->hba_port);
+ else if (mr_sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mr_sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE)
+ mpi3mr_expander_remove(mrioc,
+ mr_sas_port->remote_identify.sas_address,
+ mr_sas_port->hba_port);
+ }
+
+ port_id = sas_expander->hba_port->port_id;
+ mpi3mr_sas_port_remove(mrioc, sas_expander->sas_address,
+ sas_expander->sas_address_parent, sas_expander->hba_port);
+
+ ioc_info(mrioc, "expander_remove: handle(0x%04x), sas_addr(0x%016llx), port:%d\n",
+ sas_expander->handle, (unsigned long long)
+ sas_expander->sas_address, port_id);
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ list_del(&sas_expander->list);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+
+ kfree(sas_expander->phy);
+ kfree(sas_expander);
+}
+
+/**
+ * mpi3mr_expander_remove - Remove expander object
+ * @mrioc: Adapter instance reference
+ * @sas_address: Remove expander sas_address
+ * @hba_port: HBA port reference
+ *
+ * This function remove expander object, stored in
+ * mrioc->sas_expander_list and removes it from the SAS TL by
+ * calling mpi3mr_expander_node_remove().
+ *
+ * Return: None
+ */
+void mpi3mr_expander_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
+ struct mpi3mr_hba_port *hba_port)
+{
+ struct mpi3mr_sas_node *sas_expander;
+ unsigned long flags;
+
+ if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
+ return;
+
+ if (!hba_port)
+ return;
+
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ sas_expander = mpi3mr_expander_find_by_sas_address(mrioc, sas_address,
+ hba_port);
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ if (sas_expander)
+ mpi3mr_expander_node_remove(mrioc, sas_expander);
+
+}
+
+/**
+ * mpi3mr_get_sas_negotiated_logical_linkrate - get linkrate
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device
+ *
+ * This function identifies whether the target device is
+ * attached directly or through expander and issues sas phy
+ * page0 or expander phy page1 and gets the link rate, if there
+ * is any failure in reading the pages then this returns link
+ * rate of 1.5.
+ *
+ * Return: logical link rate.
+ */
+static u8 mpi3mr_get_sas_negotiated_logical_linkrate(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ u8 link_rate = MPI3_SAS_NEG_LINK_RATE_1_5, phy_number;
+ struct mpi3_sas_expander_page1 expander_pg1;
+ struct mpi3_sas_phy_page0 phy_pg0;
+ u32 phynum_handle;
+ u16 ioc_status;
+
+ phy_number = tgtdev->dev_spec.sas_sata_inf.phy_id;
+ if (!(tgtdev->devpg0_flag & MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED)) {
+ phynum_handle = ((phy_number<<MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT)
+ | tgtdev->parent_handle);
+ if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, &ioc_status,
+ &expander_pg1, sizeof(expander_pg1),
+ MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
+ phynum_handle)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ link_rate = (expander_pg1.negotiated_link_rate &
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
+ goto out;
+ }
+ if (mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
+ sizeof(struct mpi3_sas_phy_page0),
+ MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy_number)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ link_rate = (phy_pg0.negotiated_link_rate &
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
+out:
+ return link_rate;
+}
+
+/**
+ * mpi3mr_report_tgtdev_to_sas_transport - expose dev to SAS TL
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device
+ *
+ * This function exposes the target device after
+ * preparing host_phy, setting up link rate etc.
+ *
+ * Return: 0 on success, non-zero for failure.
+ */
+int mpi3mr_report_tgtdev_to_sas_transport(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ int retval = 0;
+ u8 link_rate, parent_phy_number;
+ u64 sas_address_parent, sas_address;
+ struct mpi3mr_hba_port *hba_port;
+ u8 port_id;
+
+ if ((tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) ||
+ !mrioc->sas_transport_enabled)
+ return -1;
+
+ sas_address = tgtdev->dev_spec.sas_sata_inf.sas_address;
+ if (!mrioc->sas_hba.num_phys)
+ mpi3mr_sas_host_add(mrioc);
+ else
+ mpi3mr_sas_host_refresh(mrioc);
+
+ if (mpi3mr_get_sas_address(mrioc, tgtdev->parent_handle,
+ &sas_address_parent) != 0) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ tgtdev->dev_spec.sas_sata_inf.sas_address_parent = sas_address_parent;
+
+ parent_phy_number = tgtdev->dev_spec.sas_sata_inf.phy_id;
+ port_id = tgtdev->io_unit_port;
+
+ hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id);
+ if (!hba_port) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ tgtdev->dev_spec.sas_sata_inf.hba_port = hba_port;
+
+ link_rate = mpi3mr_get_sas_negotiated_logical_linkrate(mrioc, tgtdev);
+
+ if (link_rate < MPI3_SAS_NEG_LINK_RATE_1_5)
+ link_rate = MPI3_SAS_NEG_LINK_RATE_1_5;
+
+ mpi3mr_update_links(mrioc, sas_address_parent, tgtdev->dev_handle,
+ parent_phy_number, link_rate, hba_port);
+
+ tgtdev->host_exposed = 1;
+ if (!mpi3mr_sas_port_add(mrioc, tgtdev->dev_handle,
+ sas_address_parent, hba_port)) {
+ retval = -1;
+ } else if ((!tgtdev->starget) && (!mrioc->is_driver_loading)) {
+ mpi3mr_sas_port_remove(mrioc, sas_address,
+ sas_address_parent, hba_port);
+ retval = -1;
+ }
+ if (retval) {
+ tgtdev->dev_spec.sas_sata_inf.hba_port = NULL;
+ tgtdev->host_exposed = 0;
+ }
+ return retval;
+}
+
+/**
+ * mpi3mr_remove_tgtdev_from_sas_transport - remove from SAS TL
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device
+ *
+ * This function removes the target device
+ *
+ * Return: None.
+ */
+void mpi3mr_remove_tgtdev_from_sas_transport(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ u64 sas_address_parent, sas_address;
+ struct mpi3mr_hba_port *hba_port;
+
+ if ((tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) ||
+ !mrioc->sas_transport_enabled)
+ return;
+
+ hba_port = tgtdev->dev_spec.sas_sata_inf.hba_port;
+ sas_address = tgtdev->dev_spec.sas_sata_inf.sas_address;
+ sas_address_parent = tgtdev->dev_spec.sas_sata_inf.sas_address_parent;
+ mpi3mr_sas_port_remove(mrioc, sas_address, sas_address_parent,
+ hba_port);
+ tgtdev->host_exposed = 0;
+ tgtdev->dev_spec.sas_sata_inf.hba_port = NULL;
+}
+
+/**
+ * mpi3mr_get_port_id_by_sas_phy - Get port ID of the given phy
+ * @phy: SAS transport layer phy object
+ *
+ * Return: Port number for valid ID else 0xFFFF
+ */
+static inline u8 mpi3mr_get_port_id_by_sas_phy(struct sas_phy *phy)
+{
+ u8 port_id = 0xFF;
+ struct mpi3mr_hba_port *hba_port = phy->hostdata;
+
+ if (hba_port)
+ port_id = hba_port->port_id;
+
+ return port_id;
+}
+
+/**
+ * mpi3mr_get_port_id_by_rphy - Get Port number from SAS rphy
+ *
+ * @mrioc: Adapter instance reference
+ * @rphy: SAS transport layer remote phy object
+ *
+ * Retrieves HBA port number in which the device pointed by the
+ * rphy object is attached with.
+ *
+ * Return: Valid port number on success else OxFFFF.
+ */
+static u8 mpi3mr_get_port_id_by_rphy(struct mpi3mr_ioc *mrioc, struct sas_rphy *rphy)
+{
+ struct mpi3mr_sas_node *sas_expander;
+ struct mpi3mr_tgt_dev *tgtdev;
+ unsigned long flags;
+ u8 port_id = 0xFF;
+
+ if (!rphy)
+ return port_id;
+
+ if (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
+ rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) {
+ spin_lock_irqsave(&mrioc->sas_node_lock, flags);
+ list_for_each_entry(sas_expander, &mrioc->sas_expander_list,
+ list) {
+ if (sas_expander->rphy == rphy) {
+ port_id = sas_expander->hba_port->port_id;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
+ } else if (rphy->identify.device_type == SAS_END_DEVICE) {
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+
+ tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
+ rphy->identify.sas_address, rphy);
+ if (tgtdev && tgtdev->dev_spec.sas_sata_inf.hba_port) {
+ port_id =
+ tgtdev->dev_spec.sas_sata_inf.hba_port->port_id;
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ }
+ return port_id;
+}
+
+static inline struct mpi3mr_ioc *phy_to_mrioc(struct sas_phy *phy)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+
+ return shost_priv(shost);
+}
+
+static inline struct mpi3mr_ioc *rphy_to_mrioc(struct sas_rphy *rphy)
+{
+ struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
+
+ return shost_priv(shost);
+}
+
+/* report phy error log structure */
+struct phy_error_log_request {
+ u8 smp_frame_type; /* 0x40 */
+ u8 function; /* 0x11 */
+ u8 allocated_response_length;
+ u8 request_length; /* 02 */
+ u8 reserved_1[5];
+ u8 phy_identifier;
+ u8 reserved_2[2];
+};
+
+/* report phy error log reply structure */
+struct phy_error_log_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x11 */
+ u8 function_result;
+ u8 response_length;
+ __be16 expander_change_count;
+ u8 reserved_1[3];
+ u8 phy_identifier;
+ u8 reserved_2[2];
+ __be32 invalid_dword;
+ __be32 running_disparity_error;
+ __be32 loss_of_dword_sync;
+ __be32 phy_reset_problem;
+};
+
+
+/**
+ * mpi3mr_get_expander_phy_error_log - return expander counters:
+ * @mrioc: Adapter instance reference
+ * @phy: The SAS transport layer phy object
+ *
+ * Return: 0 for success, non-zero for failure.
+ *
+ */
+static int mpi3mr_get_expander_phy_error_log(struct mpi3mr_ioc *mrioc,
+ struct sas_phy *phy)
+{
+ struct mpi3_smp_passthrough_request mpi_request;
+ struct mpi3_smp_passthrough_reply mpi_reply;
+ struct phy_error_log_request *phy_error_log_request;
+ struct phy_error_log_reply *phy_error_log_reply;
+ int rc;
+ void *psge;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma, data_in_dma;
+ u32 data_out_sz, data_in_sz, sz;
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+ u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
+ u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
+ u16 ioc_status;
+
+ if (mrioc->reset_in_progress) {
+ ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ if (mrioc->pci_err_recovery) {
+ ioc_err(mrioc, "%s: pci error recovery in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ data_out_sz = sizeof(struct phy_error_log_request);
+ data_in_sz = sizeof(struct phy_error_log_reply);
+ sz = data_out_sz + data_in_sz;
+ data_out = dma_alloc_coherent(&mrioc->pdev->dev, sz, &data_out_dma,
+ GFP_KERNEL);
+ if (!data_out) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ data_in_dma = data_out_dma + data_out_sz;
+ phy_error_log_reply = data_out + data_out_sz;
+
+ rc = -EINVAL;
+ memset(data_out, 0, sz);
+ phy_error_log_request = data_out;
+ phy_error_log_request->smp_frame_type = 0x40;
+ phy_error_log_request->function = 0x11;
+ phy_error_log_request->request_length = 2;
+ phy_error_log_request->allocated_response_length = 0;
+ phy_error_log_request->phy_identifier = phy->number;
+
+ memset(&mpi_request, 0, request_sz);
+ memset(&mpi_reply, 0, reply_sz);
+ mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
+ mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_sas_phy(phy);
+ mpi_request.sas_address = cpu_to_le64(phy->identify.sas_address);
+
+ psge = &mpi_request.request_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
+
+ psge = &mpi_request.response_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
+
+ dprint_transport_info(mrioc,
+ "sending phy error log SMP request to sas_address(0x%016llx), phy_id(%d)\n",
+ (unsigned long long)phy->identify.sas_address, phy->number);
+
+ if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
+ &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status))
+ goto out;
+
+ dprint_transport_info(mrioc,
+ "phy error log SMP request completed with ioc_status(0x%04x)\n",
+ ioc_status);
+
+ if (ioc_status == MPI3_IOCSTATUS_SUCCESS) {
+ dprint_transport_info(mrioc,
+ "phy error log - reply data transfer size(%d)\n",
+ le16_to_cpu(mpi_reply.response_data_length));
+
+ if (le16_to_cpu(mpi_reply.response_data_length) !=
+ sizeof(struct phy_error_log_reply))
+ goto out;
+
+ dprint_transport_info(mrioc,
+ "phy error log - function_result(%d)\n",
+ phy_error_log_reply->function_result);
+
+ phy->invalid_dword_count =
+ be32_to_cpu(phy_error_log_reply->invalid_dword);
+ phy->running_disparity_error_count =
+ be32_to_cpu(phy_error_log_reply->running_disparity_error);
+ phy->loss_of_dword_sync_count =
+ be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
+ phy->phy_reset_problem_count =
+ be32_to_cpu(phy_error_log_reply->phy_reset_problem);
+ rc = 0;
+ }
+
+out:
+ if (data_out)
+ dma_free_coherent(&mrioc->pdev->dev, sz, data_out,
+ data_out_dma);
+
+ return rc;
+}
+
+/**
+ * mpi3mr_transport_get_linkerrors - return phy error counters
+ * @phy: The SAS transport layer phy object
+ *
+ * This function retrieves the phy error log information of the
+ * HBA or expander for which the phy belongs to
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int mpi3mr_transport_get_linkerrors(struct sas_phy *phy)
+{
+ struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
+ struct mpi3_sas_phy_page1 phy_pg1;
+ int rc = 0;
+ u16 ioc_status;
+
+ rc = mpi3mr_parent_present(mrioc, phy);
+ if (rc)
+ return rc;
+
+ if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
+ return mpi3mr_get_expander_phy_error_log(mrioc, phy);
+
+ memset(&phy_pg1, 0, sizeof(struct mpi3_sas_phy_page1));
+ /* get hba phy error logs */
+ if ((mpi3mr_cfg_get_sas_phy_pg1(mrioc, &ioc_status, &phy_pg1,
+ sizeof(struct mpi3_sas_phy_page1),
+ MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy->number))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+ phy->invalid_dword_count = le32_to_cpu(phy_pg1.invalid_dword_count);
+ phy->running_disparity_error_count =
+ le32_to_cpu(phy_pg1.running_disparity_error_count);
+ phy->loss_of_dword_sync_count =
+ le32_to_cpu(phy_pg1.loss_dword_synch_count);
+ phy->phy_reset_problem_count =
+ le32_to_cpu(phy_pg1.phy_reset_problem_count);
+ return 0;
+}
+
+/**
+ * mpi3mr_transport_get_enclosure_identifier - Get Enclosure ID
+ * @rphy: The SAS transport layer remote phy object
+ * @identifier: Enclosure identifier to be returned
+ *
+ * Returns the enclosure id for the device pointed by the remote
+ * phy object.
+ *
+ * Return: 0 on success or -ENXIO
+ */
+static int
+mpi3mr_transport_get_enclosure_identifier(struct sas_rphy *rphy,
+ u64 *identifier)
+{
+ struct mpi3mr_ioc *mrioc = rphy_to_mrioc(rphy);
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
+ rphy->identify.sas_address, rphy);
+ if (tgtdev) {
+ *identifier =
+ tgtdev->enclosure_logical_id;
+ rc = 0;
+ mpi3mr_tgtdev_put(tgtdev);
+ } else {
+ *identifier = 0;
+ rc = -ENXIO;
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ return rc;
+}
+
+/**
+ * mpi3mr_transport_get_bay_identifier - Get bay ID
+ * @rphy: The SAS transport layer remote phy object
+ *
+ * Returns the slot id for the device pointed by the remote phy
+ * object.
+ *
+ * Return: Valid slot ID on success or -ENXIO
+ */
+static int
+mpi3mr_transport_get_bay_identifier(struct sas_rphy *rphy)
+{
+ struct mpi3mr_ioc *mrioc = rphy_to_mrioc(rphy);
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
+ rphy->identify.sas_address, rphy);
+ if (tgtdev) {
+ rc = tgtdev->slot;
+ mpi3mr_tgtdev_put(tgtdev);
+ } else
+ rc = -ENXIO;
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ return rc;
+}
+
+/* phy control request structure */
+struct phy_control_request {
+ u8 smp_frame_type; /* 0x40 */
+ u8 function; /* 0x91 */
+ u8 allocated_response_length;
+ u8 request_length; /* 0x09 */
+ u16 expander_change_count;
+ u8 reserved_1[3];
+ u8 phy_identifier;
+ u8 phy_operation;
+ u8 reserved_2[13];
+ u64 attached_device_name;
+ u8 programmed_min_physical_link_rate;
+ u8 programmed_max_physical_link_rate;
+ u8 reserved_3[6];
+};
+
+/* phy control reply structure */
+struct phy_control_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x11 */
+ u8 function_result;
+ u8 response_length;
+};
+
+#define SMP_PHY_CONTROL_LINK_RESET (0x01)
+#define SMP_PHY_CONTROL_HARD_RESET (0x02)
+#define SMP_PHY_CONTROL_DISABLE (0x03)
+
+/**
+ * mpi3mr_expander_phy_control - expander phy control
+ * @mrioc: Adapter instance reference
+ * @phy: The SAS transport layer phy object
+ * @phy_operation: The phy operation to be executed
+ *
+ * Issues SMP passthru phy control request to execute a specific
+ * phy operation for a given expander device.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+mpi3mr_expander_phy_control(struct mpi3mr_ioc *mrioc,
+ struct sas_phy *phy, u8 phy_operation)
+{
+ struct mpi3_smp_passthrough_request mpi_request;
+ struct mpi3_smp_passthrough_reply mpi_reply;
+ struct phy_control_request *phy_control_request;
+ struct phy_control_reply *phy_control_reply;
+ int rc;
+ void *psge;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ dma_addr_t data_in_dma;
+ size_t data_in_sz;
+ size_t data_out_sz;
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+ u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
+ u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
+ u16 ioc_status;
+ u16 sz;
+
+ if (mrioc->reset_in_progress) {
+ ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ if (mrioc->pci_err_recovery) {
+ ioc_err(mrioc, "%s: pci error recovery in progress!\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ data_out_sz = sizeof(struct phy_control_request);
+ data_in_sz = sizeof(struct phy_control_reply);
+ sz = data_out_sz + data_in_sz;
+ data_out = dma_alloc_coherent(&mrioc->pdev->dev, sz, &data_out_dma,
+ GFP_KERNEL);
+ if (!data_out) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ data_in_dma = data_out_dma + data_out_sz;
+ phy_control_reply = data_out + data_out_sz;
+
+ rc = -EINVAL;
+ memset(data_out, 0, sz);
+
+ phy_control_request = data_out;
+ phy_control_request->smp_frame_type = 0x40;
+ phy_control_request->function = 0x91;
+ phy_control_request->request_length = 9;
+ phy_control_request->allocated_response_length = 0;
+ phy_control_request->phy_identifier = phy->number;
+ phy_control_request->phy_operation = phy_operation;
+ phy_control_request->programmed_min_physical_link_rate =
+ phy->minimum_linkrate << 4;
+ phy_control_request->programmed_max_physical_link_rate =
+ phy->maximum_linkrate << 4;
+
+ memset(&mpi_request, 0, request_sz);
+ memset(&mpi_reply, 0, reply_sz);
+ mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
+ mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_sas_phy(phy);
+ mpi_request.sas_address = cpu_to_le64(phy->identify.sas_address);
+
+ psge = &mpi_request.request_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
+
+ psge = &mpi_request.response_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
+
+ dprint_transport_info(mrioc,
+ "sending phy control SMP request to sas_address(0x%016llx), phy_id(%d) opcode(%d)\n",
+ (unsigned long long)phy->identify.sas_address, phy->number,
+ phy_operation);
+
+ if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
+ &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status))
+ goto out;
+
+ dprint_transport_info(mrioc,
+ "phy control SMP request completed with ioc_status(0x%04x)\n",
+ ioc_status);
+
+ if (ioc_status == MPI3_IOCSTATUS_SUCCESS) {
+ dprint_transport_info(mrioc,
+ "phy control - reply data transfer size(%d)\n",
+ le16_to_cpu(mpi_reply.response_data_length));
+
+ if (le16_to_cpu(mpi_reply.response_data_length) !=
+ sizeof(struct phy_control_reply))
+ goto out;
+ dprint_transport_info(mrioc,
+ "phy control - function_result(%d)\n",
+ phy_control_reply->function_result);
+ rc = 0;
+ }
+ out:
+ if (data_out)
+ dma_free_coherent(&mrioc->pdev->dev, sz, data_out,
+ data_out_dma);
+
+ return rc;
+}
+
+/**
+ * mpi3mr_transport_phy_reset - Reset a given phy
+ * @phy: The SAS transport layer phy object
+ * @hard_reset: Flag to indicate the type of reset
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+mpi3mr_transport_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
+ struct mpi3_iounit_control_request mpi_request;
+ struct mpi3_iounit_control_reply mpi_reply;
+ u16 request_sz = sizeof(struct mpi3_iounit_control_request);
+ u16 reply_sz = sizeof(struct mpi3_iounit_control_reply);
+ int rc = 0;
+ u16 ioc_status;
+
+ rc = mpi3mr_parent_present(mrioc, phy);
+ if (rc)
+ return rc;
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
+ return mpi3mr_expander_phy_control(mrioc, phy,
+ (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
+ SMP_PHY_CONTROL_LINK_RESET);
+
+ /* handle hba phys */
+ memset(&mpi_request, 0, request_sz);
+ mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
+ mpi_request.function = MPI3_FUNCTION_IO_UNIT_CONTROL;
+ mpi_request.operation = MPI3_CTRL_OP_SAS_PHY_CONTROL;
+ mpi_request.param8[MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_ACTION_INDEX] =
+ (hard_reset ? MPI3_CTRL_ACTION_HARD_RESET :
+ MPI3_CTRL_ACTION_LINK_RESET);
+ mpi_request.param8[MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_PHY_INDEX] =
+ phy->number;
+
+ dprint_transport_info(mrioc,
+ "sending phy reset request to sas_address(0x%016llx), phy_id(%d) hard_reset(%d)\n",
+ (unsigned long long)phy->identify.sas_address, phy->number,
+ hard_reset);
+
+ if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
+ &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status)) {
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ dprint_transport_info(mrioc,
+ "phy reset request completed with ioc_status(0x%04x)\n",
+ ioc_status);
+out:
+ return rc;
+}
+
+/**
+ * mpi3mr_transport_phy_enable - enable/disable phys
+ * @phy: The SAS transport layer phy object
+ * @enable: flag to enable/disable, enable phy when true
+ *
+ * This function enables/disables a given by executing required
+ * configuration page changes or expander phy control command
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+mpi3mr_transport_phy_enable(struct sas_phy *phy, int enable)
+{
+ struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
+ struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
+ struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1 = NULL;
+ u16 sz;
+ int rc = 0;
+ int i, discovery_active;
+
+ rc = mpi3mr_parent_present(mrioc, phy);
+ if (rc)
+ return rc;
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
+ return mpi3mr_expander_phy_control(mrioc, phy,
+ (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
+ SMP_PHY_CONTROL_DISABLE);
+
+ /* handle hba phys */
+ sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
+ (mrioc->sas_hba.num_phys *
+ sizeof(struct mpi3_sas_io_unit0_phy_data));
+ sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_io_unit_pg0) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ /* unable to enable/disable phys when discovery is active */
+ for (i = 0, discovery_active = 0; i < mrioc->sas_hba.num_phys ; i++) {
+ if (sas_io_unit_pg0->phy_data[i].port_flags &
+ MPI3_SASIOUNIT0_PORTFLAGS_DISC_IN_PROGRESS) {
+ ioc_err(mrioc,
+ "discovery is active on port = %d, phy = %d\n"
+ "\tunable to enable/disable phys, try again later!\n",
+ sas_io_unit_pg0->phy_data[i].io_unit_port, i);
+ discovery_active = 1;
+ }
+ }
+
+ if (discovery_active) {
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ if ((sas_io_unit_pg0->phy_data[phy->number].phy_flags &
+ (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
+ MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ /* read sas_iounit page 1 */
+ sz = offsetof(struct mpi3_sas_io_unit_page1, phy_data) +
+ (mrioc->sas_hba.num_phys *
+ sizeof(struct mpi3_sas_io_unit1_phy_data));
+ sas_io_unit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_io_unit_pg1) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (mpi3mr_cfg_get_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ if (enable)
+ sas_io_unit_pg1->phy_data[phy->number].phy_flags
+ &= ~MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+ else
+ sas_io_unit_pg1->phy_data[phy->number].phy_flags
+ |= MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+
+ mpi3mr_cfg_set_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz);
+
+ /* link reset */
+ if (enable)
+ mpi3mr_transport_phy_reset(phy, 0);
+
+ out:
+ kfree(sas_io_unit_pg1);
+ kfree(sas_io_unit_pg0);
+ return rc;
+}
+
+/**
+ * mpi3mr_transport_phy_speed - set phy min/max speed
+ * @phy: The SAS transport later phy object
+ * @rates: Rates defined as in sas_phy_linkrates
+ *
+ * This function sets the link rates given in the rates
+ * argument to the given phy by executing required configuration
+ * page changes or expander phy control command
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+mpi3mr_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
+{
+ struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
+ struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1 = NULL;
+ struct mpi3_sas_phy_page0 phy_pg0;
+ u16 sz, ioc_status;
+ int rc = 0;
+
+ rc = mpi3mr_parent_present(mrioc, phy);
+ if (rc)
+ return rc;
+
+ if (!rates->minimum_linkrate)
+ rates->minimum_linkrate = phy->minimum_linkrate;
+ else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
+ rates->minimum_linkrate = phy->minimum_linkrate_hw;
+
+ if (!rates->maximum_linkrate)
+ rates->maximum_linkrate = phy->maximum_linkrate;
+ else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
+ rates->maximum_linkrate = phy->maximum_linkrate_hw;
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != mrioc->sas_hba.sas_address) {
+ phy->minimum_linkrate = rates->minimum_linkrate;
+ phy->maximum_linkrate = rates->maximum_linkrate;
+ return mpi3mr_expander_phy_control(mrioc, phy,
+ SMP_PHY_CONTROL_LINK_RESET);
+ }
+
+ /* handle hba phys */
+ sz = offsetof(struct mpi3_sas_io_unit_page1, phy_data) +
+ (mrioc->sas_hba.num_phys *
+ sizeof(struct mpi3_sas_io_unit1_phy_data));
+ sas_io_unit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_io_unit_pg1) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (mpi3mr_cfg_get_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ sas_io_unit_pg1->phy_data[phy->number].max_min_link_rate =
+ (rates->minimum_linkrate + (rates->maximum_linkrate << 4));
+
+ if (mpi3mr_cfg_set_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ /* link reset */
+ mpi3mr_transport_phy_reset(phy, 0);
+
+ /* read phy page 0, then update the rates in the sas transport phy */
+ if (!mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
+ sizeof(struct mpi3_sas_phy_page0),
+ MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy->number) &&
+ (ioc_status == MPI3_IOCSTATUS_SUCCESS)) {
+ phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
+ phy_pg0.programmed_link_rate &
+ MPI3_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
+ phy_pg0.programmed_link_rate >> 4);
+ phy->negotiated_linkrate =
+ mpi3mr_convert_phy_link_rate(
+ (phy_pg0.negotiated_link_rate &
+ MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK)
+ >> MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
+ }
+
+out:
+ kfree(sas_io_unit_pg1);
+ return rc;
+}
+
+/**
+ * mpi3mr_map_smp_buffer - map BSG dma buffer
+ * @dev: Generic device reference
+ * @buf: BSG buffer pointer
+ * @dma_addr: Physical address holder
+ * @dma_len: Mapped DMA buffer length.
+ * @p: Virtual address holder
+ *
+ * This function maps the DMAable buffer
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+static int
+mpi3mr_map_smp_buffer(struct device *dev, struct bsg_buffer *buf,
+ dma_addr_t *dma_addr, size_t *dma_len, void **p)
+{
+ /* Check if the request is split across multiple segments */
+ if (buf->sg_cnt > 1) {
+ *p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
+ GFP_KERNEL);
+ if (!*p)
+ return -ENOMEM;
+ *dma_len = buf->payload_len;
+ } else {
+ if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL))
+ return -ENOMEM;
+ *dma_addr = sg_dma_address(buf->sg_list);
+ *dma_len = sg_dma_len(buf->sg_list);
+ *p = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_unmap_smp_buffer - unmap BSG dma buffer
+ * @dev: Generic device reference
+ * @buf: BSG buffer pointer
+ * @dma_addr: Physical address to be unmapped
+ * @p: Virtual address
+ *
+ * This function unmaps the DMAable buffer
+ */
+static void
+mpi3mr_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf,
+ dma_addr_t dma_addr, void *p)
+{
+ if (p)
+ dma_free_coherent(dev, buf->payload_len, p, dma_addr);
+ else
+ dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
+}
+
+/**
+ * mpi3mr_transport_smp_handler - handler for smp passthru
+ * @job: BSG job reference
+ * @shost: SCSI host object reference
+ * @rphy: SAS transport rphy object pointing the expander
+ *
+ * This is used primarily by smp utils for sending the SMP
+ * commands to the expanders attached to the controller
+ */
+static void
+mpi3mr_transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
+ struct sas_rphy *rphy)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ struct mpi3_smp_passthrough_request mpi_request;
+ struct mpi3_smp_passthrough_reply mpi_reply;
+ int rc;
+ void *psge;
+ dma_addr_t dma_addr_in;
+ dma_addr_t dma_addr_out;
+ void *addr_in = NULL;
+ void *addr_out = NULL;
+ size_t dma_len_in;
+ size_t dma_len_out;
+ unsigned int reslen = 0;
+ u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
+ u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+ u16 ioc_status;
+
+ if (mrioc->reset_in_progress) {
+ ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (mrioc->pci_err_recovery) {
+ ioc_err(mrioc, "%s: pci error recovery in progress!\n", __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = mpi3mr_map_smp_buffer(&mrioc->pdev->dev, &job->request_payload,
+ &dma_addr_out, &dma_len_out, &addr_out);
+ if (rc)
+ goto out;
+
+ if (addr_out)
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt, addr_out,
+ job->request_payload.payload_len);
+
+ rc = mpi3mr_map_smp_buffer(&mrioc->pdev->dev, &job->reply_payload,
+ &dma_addr_in, &dma_len_in, &addr_in);
+ if (rc)
+ goto unmap_out;
+
+ memset(&mpi_request, 0, request_sz);
+ memset(&mpi_reply, 0, reply_sz);
+ mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
+ mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_rphy(mrioc, rphy);
+ mpi_request.sas_address = ((rphy) ?
+ cpu_to_le64(rphy->identify.sas_address) :
+ cpu_to_le64(mrioc->sas_hba.sas_address));
+ psge = &mpi_request.request_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, dma_len_out - 4, dma_addr_out);
+
+ psge = &mpi_request.response_sge;
+ mpi3mr_add_sg_single(psge, sgl_flags, dma_len_in - 4, dma_addr_in);
+
+ dprint_transport_info(mrioc, "sending SMP request\n");
+
+ rc = mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
+ &mpi_reply, reply_sz,
+ MPI3MR_INTADMCMD_TIMEOUT, &ioc_status);
+ if (rc)
+ goto unmap_in;
+
+ dprint_transport_info(mrioc,
+ "SMP request completed with ioc_status(0x%04x)\n", ioc_status);
+
+ dprint_transport_info(mrioc,
+ "SMP request - reply data transfer size(%d)\n",
+ le16_to_cpu(mpi_reply.response_data_length));
+
+ memcpy(job->reply, &mpi_reply, reply_sz);
+ job->reply_len = reply_sz;
+ reslen = le16_to_cpu(mpi_reply.response_data_length);
+
+ if (addr_in)
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, addr_in,
+ job->reply_payload.payload_len);
+
+ rc = 0;
+unmap_in:
+ mpi3mr_unmap_smp_buffer(&mrioc->pdev->dev, &job->reply_payload,
+ dma_addr_in, addr_in);
+unmap_out:
+ mpi3mr_unmap_smp_buffer(&mrioc->pdev->dev, &job->request_payload,
+ dma_addr_out, addr_out);
+out:
+ bsg_job_done(job, rc, reslen);
+}
+
+struct sas_function_template mpi3mr_transport_functions = {
+ .get_linkerrors = mpi3mr_transport_get_linkerrors,
+ .get_enclosure_identifier = mpi3mr_transport_get_enclosure_identifier,
+ .get_bay_identifier = mpi3mr_transport_get_bay_identifier,
+ .phy_reset = mpi3mr_transport_phy_reset,
+ .phy_enable = mpi3mr_transport_phy_enable,
+ .set_phy_speed = mpi3mr_transport_phy_speed,
+ .smp_handler = mpi3mr_transport_smp_handler,
+};
+
+struct scsi_transport_template *mpi3mr_transport_template;
diff --git a/drivers/scsi/mpt2sas/Makefile b/drivers/scsi/mpt2sas/Makefile
deleted file mode 100644
index 728f0475711d..000000000000
--- a/drivers/scsi/mpt2sas/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# mpt2sas makefile
-obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o
-mpt2sas-y += mpt2sas_base.o \
- mpt2sas_config.o \
- mpt2sas_scsih.o \
- mpt2sas_transport.o \
- mpt2sas_ctl.o
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h
deleted file mode 100644
index a3e60385787f..000000000000
--- a/drivers/scsi/mpt2sas/mpi/mpi2.h
+++ /dev/null
@@ -1,1134 +0,0 @@
-/*
- * Copyright (c) 2000-2010 LSI Corporation.
- *
- *
- * Name: mpi2.h
- * Title: MPI Message independent structures and definitions
- * including System Interface Register Set and
- * scatter/gather formats.
- * Creation Date: June 21, 2006
- *
- * mpi2.h Version: 02.00.17
- *
- * Version History
- * ---------------
- *
- * Date Version Description
- * -------- -------- ------------------------------------------------------
- * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
- * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT.
- * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT.
- * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT.
- * Moved ReplyPostHostIndex register to offset 0x6C of the
- * MPI2_SYSTEM_INTERFACE_REGS and modified the define for
- * MPI2_REPLY_POST_HOST_INDEX_OFFSET.
- * Added union of request descriptors.
- * Added union of reply descriptors.
- * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT.
- * Added define for MPI2_VERSION_02_00.
- * Fixed the size of the FunctionDependent5 field in the
- * MPI2_DEFAULT_REPLY structure.
- * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT.
- * Removed the MPI-defined Fault Codes and extended the
- * product specific codes up to 0xEFFF.
- * Added a sixth key value for the WriteSequence register
- * and changed the flush value to 0x0.
- * Added message function codes for Diagnostic Buffer Post
- * and Diagnsotic Release.
- * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED
- * Moved MPI2_VERSION_UNION from mpi2_ioc.h.
- * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT.
- * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT.
- * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT.
- * Added #defines for marking a reply descriptor as unused.
- * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT.
- * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT.
- * Moved LUN field defines from mpi2_init.h.
- * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT.
- * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT.
- * In all request and reply descriptors, replaced VF_ID
- * field with MSIxIndex field.
- * Removed DevHandle field from
- * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those
- * bytes reserved.
- * Added RAID Accelerator functionality.
- * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT.
- * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT.
- * Added MSI-x index mask and shift for Reply Post Host
- * Index register.
- * Added function code for Host Based Discovery Action.
- * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT.
- * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL.
- * Added defines for product-specific range of message
- * function codes, 0xF0 to 0xFF.
- * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT.
- * Added alternative defines for the SGE Direction bit.
- * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT.
- * --------------------------------------------------------------------------
- */
-
-#ifndef MPI2_H
-#define MPI2_H
-
-
-/*****************************************************************************
-*
-* MPI Version Definitions
-*
-*****************************************************************************/
-
-#define MPI2_VERSION_MAJOR (0x02)
-#define MPI2_VERSION_MINOR (0x00)
-#define MPI2_VERSION_MAJOR_MASK (0xFF00)
-#define MPI2_VERSION_MAJOR_SHIFT (8)
-#define MPI2_VERSION_MINOR_MASK (0x00FF)
-#define MPI2_VERSION_MINOR_SHIFT (0)
-#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \
- MPI2_VERSION_MINOR)
-
-#define MPI2_VERSION_02_00 (0x0200)
-
-/* versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT (0x11)
-#define MPI2_HEADER_VERSION_DEV (0x00)
-#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
-#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
-#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF)
-#define MPI2_HEADER_VERSION_DEV_SHIFT (0)
-#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | MPI2_HEADER_VERSION_DEV)
-
-
-/*****************************************************************************
-*
-* IOC State Definitions
-*
-*****************************************************************************/
-
-#define MPI2_IOC_STATE_RESET (0x00000000)
-#define MPI2_IOC_STATE_READY (0x10000000)
-#define MPI2_IOC_STATE_OPERATIONAL (0x20000000)
-#define MPI2_IOC_STATE_FAULT (0x40000000)
-
-#define MPI2_IOC_STATE_MASK (0xF0000000)
-#define MPI2_IOC_STATE_SHIFT (28)
-
-/* Fault state range for prodcut specific codes */
-#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000)
-#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF)
-
-
-/*****************************************************************************
-*
-* System Interface Register Definitions
-*
-*****************************************************************************/
-
-typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS
-{
- U32 Doorbell; /* 0x00 */
- U32 WriteSequence; /* 0x04 */
- U32 HostDiagnostic; /* 0x08 */
- U32 Reserved1; /* 0x0C */
- U32 DiagRWData; /* 0x10 */
- U32 DiagRWAddressLow; /* 0x14 */
- U32 DiagRWAddressHigh; /* 0x18 */
- U32 Reserved2[5]; /* 0x1C */
- U32 HostInterruptStatus; /* 0x30 */
- U32 HostInterruptMask; /* 0x34 */
- U32 DCRData; /* 0x38 */
- U32 DCRAddress; /* 0x3C */
- U32 Reserved3[2]; /* 0x40 */
- U32 ReplyFreeHostIndex; /* 0x48 */
- U32 Reserved4[8]; /* 0x4C */
- U32 ReplyPostHostIndex; /* 0x6C */
- U32 Reserved5; /* 0x70 */
- U32 HCBSize; /* 0x74 */
- U32 HCBAddressLow; /* 0x78 */
- U32 HCBAddressHigh; /* 0x7C */
- U32 Reserved6[16]; /* 0x80 */
- U32 RequestDescriptorPostLow; /* 0xC0 */
- U32 RequestDescriptorPostHigh; /* 0xC4 */
- U32 Reserved7[14]; /* 0xC8 */
-} MPI2_SYSTEM_INTERFACE_REGS, MPI2_POINTER PTR_MPI2_SYSTEM_INTERFACE_REGS,
- Mpi2SystemInterfaceRegs_t, MPI2_POINTER pMpi2SystemInterfaceRegs_t;
-
-/*
- * Defines for working with the Doorbell register.
- */
-#define MPI2_DOORBELL_OFFSET (0x00000000)
-
-/* IOC --> System values */
-#define MPI2_DOORBELL_USED (0x08000000)
-#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000)
-#define MPI2_DOORBELL_WHO_INIT_SHIFT (24)
-#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF)
-#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF)
-
-/* System --> IOC values */
-#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000)
-#define MPI2_DOORBELL_FUNCTION_SHIFT (24)
-#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000)
-#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16)
-
-
-/*
- * Defines for the WriteSequence register
- */
-#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004)
-#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F)
-#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0)
-#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF)
-#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4)
-#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB)
-#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2)
-#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7)
-#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD)
-
-/*
- * Defines for the HostDiagnostic register
- */
-#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008)
-
-#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800)
-#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000)
-#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800)
-
-#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400)
-#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200)
-#define MPI2_DIAG_HCB_MODE (0x00000100)
-#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080)
-#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040)
-#define MPI2_DIAG_RESET_HISTORY (0x00000020)
-#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010)
-#define MPI2_DIAG_RESET_ADAPTER (0x00000004)
-#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002)
-
-/*
- * Offsets for DiagRWData and address
- */
-#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010)
-#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014)
-#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018)
-
-/*
- * Defines for the HostInterruptStatus register
- */
-#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030)
-#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000)
-#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS
-#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000)
-#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008)
-#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001)
-#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS
-
-/*
- * Defines for the HostInterruptMask register
- */
-#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034)
-#define MPI2_HIM_RESET_IRQ_MASK (0x40000000)
-#define MPI2_HIM_REPLY_INT_MASK (0x00000008)
-#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK
-#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001)
-#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK
-
-/*
- * Offsets for DCRData and address
- */
-#define MPI2_DCR_DATA_OFFSET (0x00000038)
-#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C)
-
-/*
- * Offset for the Reply Free Queue
- */
-#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048)
-
-/*
- * Defines for the Reply Descriptor Post Queue
- */
-#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C)
-#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF)
-#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000)
-#define MPI2_RPHI_MSIX_INDEX_SHIFT (24)
-
-/*
- * Defines for the HCBSize and address
- */
-#define MPI2_HCB_SIZE_OFFSET (0x00000074)
-#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000)
-#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001)
-
-#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078)
-#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C)
-
-/*
- * Offsets for the Request Queue
- */
-#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0)
-#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4)
-
-
-/*****************************************************************************
-*
-* Message Descriptors
-*
-*****************************************************************************/
-
-/* Request Descriptors */
-
-/* Default Request Descriptor */
-typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR
-{
- U8 RequestFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U16 LMID; /* 0x04 */
- U16 DescriptorTypeDependent; /* 0x06 */
-} MPI2_DEFAULT_REQUEST_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR,
- Mpi2DefaultRequestDescriptor_t, MPI2_POINTER pMpi2DefaultRequestDescriptor_t;
-
-/* defines for the RequestFlags field */
-#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x0E)
-#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00)
-#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02)
-#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06)
-#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08)
-#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A)
-
-#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01)
-
-
-/* High Priority Request Descriptor */
-typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR
-{
- U8 RequestFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U16 LMID; /* 0x04 */
- U16 Reserved1; /* 0x06 */
-} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
- Mpi2HighPriorityRequestDescriptor_t,
- MPI2_POINTER pMpi2HighPriorityRequestDescriptor_t;
-
-
-/* SCSI IO Request Descriptor */
-typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR
-{
- U8 RequestFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U16 LMID; /* 0x04 */
- U16 DevHandle; /* 0x06 */
-} MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
- Mpi2SCSIIORequestDescriptor_t, MPI2_POINTER pMpi2SCSIIORequestDescriptor_t;
-
-
-/* SCSI Target Request Descriptor */
-typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR
-{
- U8 RequestFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U16 LMID; /* 0x04 */
- U16 IoIndex; /* 0x06 */
-} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
- Mpi2SCSITargetRequestDescriptor_t,
- MPI2_POINTER pMpi2SCSITargetRequestDescriptor_t;
-
-
-/* RAID Accelerator Request Descriptor */
-typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR {
- U8 RequestFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U16 LMID; /* 0x04 */
- U16 Reserved; /* 0x06 */
-} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR,
- Mpi2RAIDAcceleratorRequestDescriptor_t,
- MPI2_POINTER pMpi2RAIDAcceleratorRequestDescriptor_t;
-
-
-/* union of Request Descriptors */
-typedef union _MPI2_REQUEST_DESCRIPTOR_UNION
-{
- MPI2_DEFAULT_REQUEST_DESCRIPTOR Default;
- MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority;
- MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO;
- MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget;
- MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator;
- U64 Words;
-} MPI2_REQUEST_DESCRIPTOR_UNION, MPI2_POINTER PTR_MPI2_REQUEST_DESCRIPTOR_UNION,
- Mpi2RequestDescriptorUnion_t, MPI2_POINTER pMpi2RequestDescriptorUnion_t;
-
-
-/* Reply Descriptors */
-
-/* Default Reply Descriptor */
-typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR
-{
- U8 ReplyFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 DescriptorTypeDependent1; /* 0x02 */
- U32 DescriptorTypeDependent2; /* 0x04 */
-} MPI2_DEFAULT_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR,
- Mpi2DefaultReplyDescriptor_t, MPI2_POINTER pMpi2DefaultReplyDescriptor_t;
-
-/* defines for the ReplyFlags field */
-#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F)
-#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00)
-#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01)
-#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02)
-#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03)
-#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05)
-#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F)
-
-/* values for marking a reply descriptor as unused */
-#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF)
-#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF)
-
-/* Address Reply Descriptor */
-typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR
-{
- U8 ReplyFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U32 ReplyFrameAddress; /* 0x04 */
-} MPI2_ADDRESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR,
- Mpi2AddressReplyDescriptor_t, MPI2_POINTER pMpi2AddressReplyDescriptor_t;
-
-#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00)
-
-
-/* SCSI IO Success Reply Descriptor */
-typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR
-{
- U8 ReplyFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U16 TaskTag; /* 0x04 */
- U16 Reserved1; /* 0x06 */
-} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
- Mpi2SCSIIOSuccessReplyDescriptor_t,
- MPI2_POINTER pMpi2SCSIIOSuccessReplyDescriptor_t;
-
-
-/* TargetAssist Success Reply Descriptor */
-typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR
-{
- U8 ReplyFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U8 SequenceNumber; /* 0x04 */
- U8 Reserved1; /* 0x05 */
- U16 IoIndex; /* 0x06 */
-} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
- Mpi2TargetAssistSuccessReplyDescriptor_t,
- MPI2_POINTER pMpi2TargetAssistSuccessReplyDescriptor_t;
-
-
-/* Target Command Buffer Reply Descriptor */
-typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR
-{
- U8 ReplyFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U8 VP_ID; /* 0x02 */
- U8 Flags; /* 0x03 */
- U16 InitiatorDevHandle; /* 0x04 */
- U16 IoIndex; /* 0x06 */
-} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
- Mpi2TargetCommandBufferReplyDescriptor_t,
- MPI2_POINTER pMpi2TargetCommandBufferReplyDescriptor_t;
-
-/* defines for Flags field */
-#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F)
-
-
-/* RAID Accelerator Success Reply Descriptor */
-typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR {
- U8 ReplyFlags; /* 0x00 */
- U8 MSIxIndex; /* 0x01 */
- U16 SMID; /* 0x02 */
- U32 Reserved; /* 0x04 */
-} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR,
- MPI2_POINTER PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR,
- Mpi2RAIDAcceleratorSuccessReplyDescriptor_t,
- MPI2_POINTER pMpi2RAIDAcceleratorSuccessReplyDescriptor_t;
-
-
-/* union of Reply Descriptors */
-typedef union _MPI2_REPLY_DESCRIPTORS_UNION
-{
- MPI2_DEFAULT_REPLY_DESCRIPTOR Default;
- MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply;
- MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess;
- MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess;
- MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer;
- MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess;
- U64 Words;
-} MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION,
- Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t;
-
-
-
-/*****************************************************************************
-*
-* Message Functions
-*
-*****************************************************************************/
-
-#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) /* SCSI IO */
-#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01) /* SCSI Task Management */
-#define MPI2_FUNCTION_IOC_INIT (0x02) /* IOC Init */
-#define MPI2_FUNCTION_IOC_FACTS (0x03) /* IOC Facts */
-#define MPI2_FUNCTION_CONFIG (0x04) /* Configuration */
-#define MPI2_FUNCTION_PORT_FACTS (0x05) /* Port Facts */
-#define MPI2_FUNCTION_PORT_ENABLE (0x06) /* Port Enable */
-#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07) /* Event Notification */
-#define MPI2_FUNCTION_EVENT_ACK (0x08) /* Event Acknowledge */
-#define MPI2_FUNCTION_FW_DOWNLOAD (0x09) /* FW Download */
-#define MPI2_FUNCTION_TARGET_ASSIST (0x0B) /* Target Assist */
-#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C) /* Target Status Send */
-#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D) /* Target Mode Abort */
-#define MPI2_FUNCTION_FW_UPLOAD (0x12) /* FW Upload */
-#define MPI2_FUNCTION_RAID_ACTION (0x15) /* RAID Action */
-#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) /* SCSI IO RAID Passthrough */
-#define MPI2_FUNCTION_TOOLBOX (0x17) /* Toolbox */
-#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) /* SCSI Enclosure Processor */
-#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A) /* SMP Passthrough */
-#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) /* SAS IO Unit Control */
-#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C) /* SATA Passthrough */
-#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D) /* Diagnostic Buffer Post */
-#define MPI2_FUNCTION_DIAG_RELEASE (0x1E) /* Diagnostic Release */
-#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */
-#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */
-#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator*/
-/* Host Based Discovery Action */
-#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F)
-/* Power Management Control */
-#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30)
-/* beginning of product-specific range */
-#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0)
-/* end of product-specific range */
-#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF)
-
-
-
-
-/* Doorbell functions */
-#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40)
-#define MPI2_FUNCTION_HANDSHAKE (0x42)
-
-
-/*****************************************************************************
-*
-* IOC Status Values
-*
-*****************************************************************************/
-
-/* mask for IOCStatus status value */
-#define MPI2_IOCSTATUS_MASK (0x7FFF)
-
-/****************************************************************************
-* Common IOCStatus values for all replies
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_SUCCESS (0x0000)
-#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001)
-#define MPI2_IOCSTATUS_BUSY (0x0002)
-#define MPI2_IOCSTATUS_INVALID_SGL (0x0003)
-#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004)
-#define MPI2_IOCSTATUS_INVALID_VPID (0x0005)
-#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006)
-#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007)
-#define MPI2_IOCSTATUS_INVALID_STATE (0x0008)
-#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009)
-
-/****************************************************************************
-* Config IOCStatus values
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020)
-#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021)
-#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022)
-#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023)
-#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024)
-#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025)
-
-/****************************************************************************
-* SCSI IO Reply
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040)
-#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042)
-#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043)
-#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044)
-#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045)
-#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046)
-#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047)
-#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048)
-#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049)
-#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A)
-#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B)
-#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C)
-
-/****************************************************************************
-* For use by SCSI Initiator and SCSI Target end-to-end data protection
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D)
-#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E)
-#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F)
-
-/****************************************************************************
-* SCSI Target values
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062)
-#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063)
-#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064)
-#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065)
-#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A)
-#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D)
-#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E)
-#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F)
-#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070)
-#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071)
-
-/****************************************************************************
-* Serial Attached SCSI values
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090)
-#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091)
-
-/****************************************************************************
-* Diagnostic Buffer Post / Diagnostic Release values
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0)
-
-/****************************************************************************
-* RAID Accelerator values
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0)
-
-/****************************************************************************
-* IOCStatus flag to indicate that log info is available
-****************************************************************************/
-
-#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000)
-
-/****************************************************************************
-* IOCLogInfo Types
-****************************************************************************/
-
-#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000)
-#define MPI2_IOCLOGINFO_TYPE_SHIFT (28)
-#define MPI2_IOCLOGINFO_TYPE_NONE (0x0)
-#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1)
-#define MPI2_IOCLOGINFO_TYPE_FC (0x2)
-#define MPI2_IOCLOGINFO_TYPE_SAS (0x3)
-#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4)
-#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF)
-
-
-/*****************************************************************************
-*
-* Standard Message Structures
-*
-*****************************************************************************/
-
-/****************************************************************************
-* Request Message Header for all request messages
-****************************************************************************/
-
-typedef struct _MPI2_REQUEST_HEADER
-{
- U16 FunctionDependent1; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 FunctionDependent2; /* 0x04 */
- U8 FunctionDependent3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
-} MPI2_REQUEST_HEADER, MPI2_POINTER PTR_MPI2_REQUEST_HEADER,
- MPI2RequestHeader_t, MPI2_POINTER pMPI2RequestHeader_t;
-
-
-/****************************************************************************
-* Default Reply
-****************************************************************************/
-
-typedef struct _MPI2_DEFAULT_REPLY
-{
- U16 FunctionDependent1; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 FunctionDependent2; /* 0x04 */
- U8 FunctionDependent3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U16 FunctionDependent5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_DEFAULT_REPLY, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY,
- MPI2DefaultReply_t, MPI2_POINTER pMPI2DefaultReply_t;
-
-
-/* common version structure/union used in messages and configuration pages */
-
-typedef struct _MPI2_VERSION_STRUCT
-{
- U8 Dev; /* 0x00 */
- U8 Unit; /* 0x01 */
- U8 Minor; /* 0x02 */
- U8 Major; /* 0x03 */
-} MPI2_VERSION_STRUCT;
-
-typedef union _MPI2_VERSION_UNION
-{
- MPI2_VERSION_STRUCT Struct;
- U32 Word;
-} MPI2_VERSION_UNION;
-
-
-/* LUN field defines, common to many structures */
-#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF)
-#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000)
-#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF)
-#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000)
-#define MPI2_LUN_LEVEL_1_WORD (0xFF00)
-#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00)
-
-
-/*****************************************************************************
-*
-* Fusion-MPT MPI Scatter Gather Elements
-*
-*****************************************************************************/
-
-/****************************************************************************
-* MPI Simple Element structures
-****************************************************************************/
-
-typedef struct _MPI2_SGE_SIMPLE32
-{
- U32 FlagsLength;
- U32 Address;
-} MPI2_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_SGE_SIMPLE32,
- Mpi2SGESimple32_t, MPI2_POINTER pMpi2SGESimple32_t;
-
-typedef struct _MPI2_SGE_SIMPLE64
-{
- U32 FlagsLength;
- U64 Address;
-} MPI2_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_SGE_SIMPLE64,
- Mpi2SGESimple64_t, MPI2_POINTER pMpi2SGESimple64_t;
-
-typedef struct _MPI2_SGE_SIMPLE_UNION
-{
- U32 FlagsLength;
- union
- {
- U32 Address32;
- U64 Address64;
- } u;
-} MPI2_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_SIMPLE_UNION,
- Mpi2SGESimpleUnion_t, MPI2_POINTER pMpi2SGESimpleUnion_t;
-
-
-/****************************************************************************
-* MPI Chain Element structures
-****************************************************************************/
-
-typedef struct _MPI2_SGE_CHAIN32
-{
- U16 Length;
- U8 NextChainOffset;
- U8 Flags;
- U32 Address;
-} MPI2_SGE_CHAIN32, MPI2_POINTER PTR_MPI2_SGE_CHAIN32,
- Mpi2SGEChain32_t, MPI2_POINTER pMpi2SGEChain32_t;
-
-typedef struct _MPI2_SGE_CHAIN64
-{
- U16 Length;
- U8 NextChainOffset;
- U8 Flags;
- U64 Address;
-} MPI2_SGE_CHAIN64, MPI2_POINTER PTR_MPI2_SGE_CHAIN64,
- Mpi2SGEChain64_t, MPI2_POINTER pMpi2SGEChain64_t;
-
-typedef struct _MPI2_SGE_CHAIN_UNION
-{
- U16 Length;
- U8 NextChainOffset;
- U8 Flags;
- union
- {
- U32 Address32;
- U64 Address64;
- } u;
-} MPI2_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_SGE_CHAIN_UNION,
- Mpi2SGEChainUnion_t, MPI2_POINTER pMpi2SGEChainUnion_t;
-
-
-/****************************************************************************
-* MPI Transaction Context Element structures
-****************************************************************************/
-
-typedef struct _MPI2_SGE_TRANSACTION32
-{
- U8 Reserved;
- U8 ContextSize;
- U8 DetailsLength;
- U8 Flags;
- U32 TransactionContext[1];
- U32 TransactionDetails[1];
-} MPI2_SGE_TRANSACTION32, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION32,
- Mpi2SGETransaction32_t, MPI2_POINTER pMpi2SGETransaction32_t;
-
-typedef struct _MPI2_SGE_TRANSACTION64
-{
- U8 Reserved;
- U8 ContextSize;
- U8 DetailsLength;
- U8 Flags;
- U32 TransactionContext[2];
- U32 TransactionDetails[1];
-} MPI2_SGE_TRANSACTION64, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION64,
- Mpi2SGETransaction64_t, MPI2_POINTER pMpi2SGETransaction64_t;
-
-typedef struct _MPI2_SGE_TRANSACTION96
-{
- U8 Reserved;
- U8 ContextSize;
- U8 DetailsLength;
- U8 Flags;
- U32 TransactionContext[3];
- U32 TransactionDetails[1];
-} MPI2_SGE_TRANSACTION96, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION96,
- Mpi2SGETransaction96_t, MPI2_POINTER pMpi2SGETransaction96_t;
-
-typedef struct _MPI2_SGE_TRANSACTION128
-{
- U8 Reserved;
- U8 ContextSize;
- U8 DetailsLength;
- U8 Flags;
- U32 TransactionContext[4];
- U32 TransactionDetails[1];
-} MPI2_SGE_TRANSACTION128, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION128,
- Mpi2SGETransaction_t128, MPI2_POINTER pMpi2SGETransaction_t128;
-
-typedef struct _MPI2_SGE_TRANSACTION_UNION
-{
- U8 Reserved;
- U8 ContextSize;
- U8 DetailsLength;
- U8 Flags;
- union
- {
- U32 TransactionContext32[1];
- U32 TransactionContext64[2];
- U32 TransactionContext96[3];
- U32 TransactionContext128[4];
- } u;
- U32 TransactionDetails[1];
-} MPI2_SGE_TRANSACTION_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION_UNION,
- Mpi2SGETransactionUnion_t, MPI2_POINTER pMpi2SGETransactionUnion_t;
-
-
-/****************************************************************************
-* MPI SGE union for IO SGL's
-****************************************************************************/
-
-typedef struct _MPI2_MPI_SGE_IO_UNION
-{
- union
- {
- MPI2_SGE_SIMPLE_UNION Simple;
- MPI2_SGE_CHAIN_UNION Chain;
- } u;
-} MPI2_MPI_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_IO_UNION,
- Mpi2MpiSGEIOUnion_t, MPI2_POINTER pMpi2MpiSGEIOUnion_t;
-
-
-/****************************************************************************
-* MPI SGE union for SGL's with Simple and Transaction elements
-****************************************************************************/
-
-typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION
-{
- union
- {
- MPI2_SGE_SIMPLE_UNION Simple;
- MPI2_SGE_TRANSACTION_UNION Transaction;
- } u;
-} MPI2_SGE_TRANS_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANS_SIMPLE_UNION,
- Mpi2SGETransSimpleUnion_t, MPI2_POINTER pMpi2SGETransSimpleUnion_t;
-
-
-/****************************************************************************
-* All MPI SGE types union
-****************************************************************************/
-
-typedef struct _MPI2_MPI_SGE_UNION
-{
- union
- {
- MPI2_SGE_SIMPLE_UNION Simple;
- MPI2_SGE_CHAIN_UNION Chain;
- MPI2_SGE_TRANSACTION_UNION Transaction;
- } u;
-} MPI2_MPI_SGE_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_UNION,
- Mpi2MpiSgeUnion_t, MPI2_POINTER pMpi2MpiSgeUnion_t;
-
-
-/****************************************************************************
-* MPI SGE field definition and masks
-****************************************************************************/
-
-/* Flags field bit definitions */
-
-#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80)
-#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40)
-#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30)
-#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08)
-#define MPI2_SGE_FLAGS_DIRECTION (0x04)
-#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02)
-#define MPI2_SGE_FLAGS_END_OF_LIST (0x01)
-
-#define MPI2_SGE_FLAGS_SHIFT (24)
-
-#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF)
-#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF)
-
-/* Element Type */
-
-#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00)
-#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10)
-#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30)
-#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30)
-
-/* Address location */
-
-#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00)
-
-/* Direction */
-
-#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00)
-#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04)
-
-#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST)
-#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC)
-
-/* Address Size */
-
-#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00)
-#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02)
-
-/* Context Size */
-
-#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00)
-#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02)
-#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04)
-#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06)
-
-#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000)
-#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16)
-
-/****************************************************************************
-* MPI SGE operation Macros
-****************************************************************************/
-
-/* SIMPLE FlagsLength manipulations... */
-#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT)
-#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> MPI2_SGE_FLAGS_SHIFT)
-#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK)
-#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK)
-
-#define MPI2_SGE_SET_FLAGS_LENGTH(f,l) (MPI2_SGE_SET_FLAGS(f) | MPI2_SGE_LENGTH(l))
-
-#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength)
-#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength)
-#define MPI2_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_SGE_SET_FLAGS_LENGTH(f,l)
-
-/* CAUTION - The following are READ-MODIFY-WRITE! */
-#define MPI2_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_SGE_SET_FLAGS(f)
-#define MPI2_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_SGE_LENGTH(l)
-
-#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> MPI2_SGE_CHAIN_OFFSET_SHIFT)
-
-
-/*****************************************************************************
-*
-* Fusion-MPT IEEE Scatter Gather Elements
-*
-*****************************************************************************/
-
-/****************************************************************************
-* IEEE Simple Element structures
-****************************************************************************/
-
-typedef struct _MPI2_IEEE_SGE_SIMPLE32
-{
- U32 Address;
- U32 FlagsLength;
-} MPI2_IEEE_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE32,
- Mpi2IeeeSgeSimple32_t, MPI2_POINTER pMpi2IeeeSgeSimple32_t;
-
-typedef struct _MPI2_IEEE_SGE_SIMPLE64
-{
- U64 Address;
- U32 Length;
- U16 Reserved1;
- U8 Reserved2;
- U8 Flags;
-} MPI2_IEEE_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE64,
- Mpi2IeeeSgeSimple64_t, MPI2_POINTER pMpi2IeeeSgeSimple64_t;
-
-typedef union _MPI2_IEEE_SGE_SIMPLE_UNION
-{
- MPI2_IEEE_SGE_SIMPLE32 Simple32;
- MPI2_IEEE_SGE_SIMPLE64 Simple64;
-} MPI2_IEEE_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE_UNION,
- Mpi2IeeeSgeSimpleUnion_t, MPI2_POINTER pMpi2IeeeSgeSimpleUnion_t;
-
-
-/****************************************************************************
-* IEEE Chain Element structures
-****************************************************************************/
-
-typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32;
-
-typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64;
-
-typedef union _MPI2_IEEE_SGE_CHAIN_UNION
-{
- MPI2_IEEE_SGE_CHAIN32 Chain32;
- MPI2_IEEE_SGE_CHAIN64 Chain64;
-} MPI2_IEEE_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_CHAIN_UNION,
- Mpi2IeeeSgeChainUnion_t, MPI2_POINTER pMpi2IeeeSgeChainUnion_t;
-
-
-/****************************************************************************
-* All IEEE SGE types union
-****************************************************************************/
-
-typedef struct _MPI2_IEEE_SGE_UNION
-{
- union
- {
- MPI2_IEEE_SGE_SIMPLE_UNION Simple;
- MPI2_IEEE_SGE_CHAIN_UNION Chain;
- } u;
-} MPI2_IEEE_SGE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_UNION,
- Mpi2IeeeSgeUnion_t, MPI2_POINTER pMpi2IeeeSgeUnion_t;
-
-
-/****************************************************************************
-* IEEE SGE field definitions and masks
-****************************************************************************/
-
-/* Flags field bit definitions */
-
-#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80)
-
-#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24)
-
-#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF)
-
-/* Element Type */
-
-#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00)
-#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80)
-
-/* Data Location Address Space */
-
-#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03)
-#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00)
-#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01)
-#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02)
-#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03)
-
-
-/****************************************************************************
-* IEEE SGE operation Macros
-****************************************************************************/
-
-/* SIMPLE FlagsLength manipulations... */
-#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT)
-#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) >> MPI2_IEEE32_SGE_FLAGS_SHIFT)
-#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK)
-
-#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) | MPI2_IEEE32_SGE_LENGTH(l))
-
-#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength)
-#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength)
-#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f,l)
-
-/* CAUTION - The following are READ-MODIFY-WRITE! */
-#define MPI2_IEEE32_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_IEEE32_SGE_SET_FLAGS(f)
-#define MPI2_IEEE32_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_IEEE32_SGE_LENGTH(l)
-
-
-
-
-/*****************************************************************************
-*
-* Fusion-MPT MPI/IEEE Scatter Gather Unions
-*
-*****************************************************************************/
-
-typedef union _MPI2_SIMPLE_SGE_UNION
-{
- MPI2_SGE_SIMPLE_UNION MpiSimple;
- MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
-} MPI2_SIMPLE_SGE_UNION, MPI2_POINTER PTR_MPI2_SIMPLE_SGE_UNION,
- Mpi2SimpleSgeUntion_t, MPI2_POINTER pMpi2SimpleSgeUntion_t;
-
-
-typedef union _MPI2_SGE_IO_UNION
-{
- MPI2_SGE_SIMPLE_UNION MpiSimple;
- MPI2_SGE_CHAIN_UNION MpiChain;
- MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
- MPI2_IEEE_SGE_CHAIN_UNION IeeeChain;
-} MPI2_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_SGE_IO_UNION,
- Mpi2SGEIOUnion_t, MPI2_POINTER pMpi2SGEIOUnion_t;
-
-
-/****************************************************************************
-*
-* Values for SGLFlags field, used in many request messages with an SGL
-*
-****************************************************************************/
-
-/* values for MPI SGL Data Location Address Space subfield */
-#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C)
-#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00)
-#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04)
-#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08)
-#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C)
-/* values for SGL Type subfield */
-#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03)
-#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00)
-#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01)
-#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02)
-
-
-#endif
-
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
deleted file mode 100644
index f5b9c766e28f..000000000000
--- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
+++ /dev/null
@@ -1,2803 +0,0 @@
-/*
- * Copyright (c) 2000-2010 LSI Corporation.
- *
- *
- * Name: mpi2_cnfg.h
- * Title: MPI Configuration messages and pages
- * Creation Date: November 10, 2006
- *
- * mpi2_cnfg.h Version: 02.00.16
- *
- * Version History
- * ---------------
- *
- * Date Version Description
- * -------- -------- ------------------------------------------------------
- * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
- * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags.
- * Added Manufacturing Page 11.
- * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE
- * define.
- * 06-26-07 02.00.02 Adding generic structure for product-specific
- * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS.
- * Rework of BIOS Page 2 configuration page.
- * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the
- * forms.
- * Added configuration pages IOC Page 8 and Driver
- * Persistent Mapping Page 0.
- * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated
- * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1,
- * RAID Physical Disk Pages 0 and 1, RAID Configuration
- * Page 0).
- * Added new value for AccessStatus field of SAS Device
- * Page 0 (_SATA_NEEDS_INITIALIZATION).
- * 10-31-07 02.00.04 Added missing SEPDevHandle field to
- * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
- * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for
- * NVDATA.
- * Modified IOC Page 7 to use masks and added field for
- * SASBroadcastPrimitiveMasks.
- * Added MPI2_CONFIG_PAGE_BIOS_4.
- * Added MPI2_CONFIG_PAGE_LOG_0.
- * 02-29-08 02.00.06 Modified various names to make them 32-character unique.
- * Added SAS Device IDs.
- * Updated Integrated RAID configuration pages including
- * Manufacturing Page 4, IOC Page 6, and RAID Configuration
- * Page 0.
- * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA.
- * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION.
- * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING.
- * Added missing MaxNumRoutedSasAddresses field to
- * MPI2_CONFIG_PAGE_EXPANDER_0.
- * Added SAS Port Page 0.
- * Modified structure layout for
- * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0.
- * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use
- * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array.
- * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF
- * to 0x000000FF.
- * Added two new values for the Physical Disk Coercion Size
- * bits in the Flags field of Manufacturing Page 4.
- * Added product-specific Manufacturing pages 16 to 31.
- * Modified Flags bits for controlling write cache on SATA
- * drives in IO Unit Page 1.
- * Added new bit to AdditionalControlFlags of SAS IO Unit
- * Page 1 to control Invalid Topology Correction.
- * Added additional defines for RAID Volume Page 0
- * VolumeStatusFlags field.
- * Modified meaning of RAID Volume Page 0 VolumeSettings
- * define for auto-configure of hot-swap drives.
- * Added SupportedPhysDisks field to RAID Volume Page 1 and
- * added related defines.
- * Added PhysDiskAttributes field (and related defines) to
- * RAID Physical Disk Page 0.
- * Added MPI2_SAS_PHYINFO_PHY_VACANT define.
- * Added three new DiscoveryStatus bits for SAS IO Unit
- * Page 0 and SAS Expander Page 0.
- * Removed multiplexing information from SAS IO Unit pages.
- * Added BootDeviceWaitTime field to SAS IO Unit Page 4.
- * Removed Zone Address Resolved bit from PhyInfo and from
- * Expander Page 0 Flags field.
- * Added two new AccessStatus values to SAS Device Page 0
- * for indicating routing problems. Added 3 reserved words
- * to this page.
- * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3.
- * Inserted missing reserved field into structure for IOC
- * Page 6.
- * Added more pending task bits to RAID Volume Page 0
- * VolumeStatusFlags defines.
- * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define.
- * Added a new DiscoveryStatus bit for SAS IO Unit Page 0
- * and SAS Expander Page 0 to flag a downstream initiator
- * when in simplified routing mode.
- * Removed SATA Init Failure defines for DiscoveryStatus
- * fields of SAS IO Unit Page 0 and SAS Expander Page 0.
- * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define.
- * Added PortGroups, DmaGroup, and ControlGroup fields to
- * SAS Device Page 0.
- * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO
- * Unit Page 6.
- * Added expander reduced functionality data to SAS
- * Expander Page 0.
- * Added SAS PHY Page 2 and SAS PHY Page 3.
- * 07-30-09 02.00.12 Added IO Unit Page 7.
- * Added new device ids.
- * Added SAS IO Unit Page 5.
- * Added partial and slumber power management capable flags
- * to SAS Device Page 0 Flags field.
- * Added PhyInfo defines for power condition.
- * Added Ethernet configuration pages.
- * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
- * Added SAS PHY Page 4 structure and defines.
- * 02-10-10 02.00.14 Modified the comments for the configuration page
- * structures that contain an array of data. The host
- * should use the "count" field in the page data (e.g. the
- * NumPhys field) to determine the number of valid elements
- * in the array.
- * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines.
- * Added PowerManagementCapabilities to IO Unit Page 7.
- * Added PortWidthModGroup field to
- * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS.
- * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines.
- * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines.
- * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines.
- * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT
- * define.
- * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define.
- * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define.
- * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing)
- * defines.
- * --------------------------------------------------------------------------
- */
-
-#ifndef MPI2_CNFG_H
-#define MPI2_CNFG_H
-
-/*****************************************************************************
-* Configuration Page Header and defines
-*****************************************************************************/
-
-/* Config Page Header */
-typedef struct _MPI2_CONFIG_PAGE_HEADER
-{
- U8 PageVersion; /* 0x00 */
- U8 PageLength; /* 0x01 */
- U8 PageNumber; /* 0x02 */
- U8 PageType; /* 0x03 */
-} MPI2_CONFIG_PAGE_HEADER, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER,
- Mpi2ConfigPageHeader_t, MPI2_POINTER pMpi2ConfigPageHeader_t;
-
-typedef union _MPI2_CONFIG_PAGE_HEADER_UNION
-{
- MPI2_CONFIG_PAGE_HEADER Struct;
- U8 Bytes[4];
- U16 Word16[2];
- U32 Word32;
-} MPI2_CONFIG_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER_UNION,
- Mpi2ConfigPageHeaderUnion, MPI2_POINTER pMpi2ConfigPageHeaderUnion;
-
-/* Extended Config Page Header */
-typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER
-{
- U8 PageVersion; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 PageNumber; /* 0x02 */
- U8 PageType; /* 0x03 */
- U16 ExtPageLength; /* 0x04 */
- U8 ExtPageType; /* 0x06 */
- U8 Reserved2; /* 0x07 */
-} MPI2_CONFIG_EXTENDED_PAGE_HEADER,
- MPI2_POINTER PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER,
- Mpi2ConfigExtendedPageHeader_t, MPI2_POINTER pMpi2ConfigExtendedPageHeader_t;
-
-typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
-{
- MPI2_CONFIG_PAGE_HEADER Struct;
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext;
- U8 Bytes[8];
- U16 Word16[4];
- U32 Word32[2];
-} MPI2_CONFIG_EXT_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION,
- Mpi2ConfigPageExtendedHeaderUnion, MPI2_POINTER pMpi2ConfigPageExtendedHeaderUnion;
-
-
-/* PageType field values */
-#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00)
-#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10)
-#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20)
-#define MPI2_CONFIG_PAGEATTR_MASK (0xF0)
-
-#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00)
-#define MPI2_CONFIG_PAGETYPE_IOC (0x01)
-#define MPI2_CONFIG_PAGETYPE_BIOS (0x02)
-#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08)
-#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09)
-#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A)
-#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F)
-#define MPI2_CONFIG_PAGETYPE_MASK (0x0F)
-
-#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF)
-
-
-/* ExtPageType field values */
-#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10)
-#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11)
-#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12)
-#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13)
-#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14)
-#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15)
-#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16)
-#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17)
-#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18)
-#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19)
-
-
-/*****************************************************************************
-* PageAddress defines
-*****************************************************************************/
-
-/* RAID Volume PageAddress format */
-#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
-#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000)
-
-#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF)
-
-
-/* RAID Physical Disk PageAddress format */
-#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000)
-#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000)
-#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000)
-
-#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF)
-#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF)
-
-
-/* SAS Expander PageAddress format */
-#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000)
-#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000)
-#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000)
-
-#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF)
-#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000)
-#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16)
-
-
-/* SAS Device PageAddress format */
-#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
-#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000)
-
-#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF)
-
-
-/* SAS PHY PageAddress format */
-#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000)
-#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000)
-
-#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF)
-#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF)
-
-
-/* SAS Port PageAddress format */
-#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000)
-#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000)
-
-#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF)
-
-
-/* SAS Enclosure PageAddress format */
-#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
-#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000)
-
-#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF)
-
-
-/* RAID Configuration PageAddress format */
-#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000)
-#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000)
-#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000)
-
-#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF)
-
-
-/* Driver Persistent Mapping PageAddress format */
-#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000)
-
-#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000)
-#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16)
-#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF)
-
-
-/* Ethernet PageAddress format */
-#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000)
-#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000)
-
-#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF)
-
-
-
-/****************************************************************************
-* Configuration messages
-****************************************************************************/
-
-/* Configuration Request Message */
-typedef struct _MPI2_CONFIG_REQUEST
-{
- U8 Action; /* 0x00 */
- U8 SGLFlags; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 ExtPageLength; /* 0x04 */
- U8 ExtPageType; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U32 Reserved2; /* 0x0C */
- U32 Reserved3; /* 0x10 */
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */
- U32 PageAddress; /* 0x18 */
- MPI2_SGE_IO_UNION PageBufferSGE; /* 0x1C */
-} MPI2_CONFIG_REQUEST, MPI2_POINTER PTR_MPI2_CONFIG_REQUEST,
- Mpi2ConfigRequest_t, MPI2_POINTER pMpi2ConfigRequest_t;
-
-/* values for the Action field */
-#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00)
-#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01)
-#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02)
-#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03)
-#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04)
-#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05)
-#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06)
-#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07)
-
-/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
-
-
-/* Config Reply Message */
-typedef struct _MPI2_CONFIG_REPLY
-{
- U8 Action; /* 0x00 */
- U8 SGLFlags; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 ExtPageLength; /* 0x04 */
- U8 ExtPageType; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U16 Reserved2; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */
-} MPI2_CONFIG_REPLY, MPI2_POINTER PTR_MPI2_CONFIG_REPLY,
- Mpi2ConfigReply_t, MPI2_POINTER pMpi2ConfigReply_t;
-
-
-
-/*****************************************************************************
-*
-* C o n f i g u r a t i o n P a g e s
-*
-*****************************************************************************/
-
-/****************************************************************************
-* Manufacturing Config pages
-****************************************************************************/
-
-#define MPI2_MFGPAGE_VENDORID_LSI (0x1000)
-
-/* SAS */
-#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070)
-#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072)
-#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074)
-#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076)
-#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077)
-#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064)
-#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065)
-
-#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E)
-
-#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080)
-#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081)
-#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082)
-#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083)
-#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084)
-#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085)
-#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086)
-#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087)
-#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E)
-
-
-
-
-/* Manufacturing Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_0
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U8 ChipName[16]; /* 0x04 */
- U8 ChipRevision[8]; /* 0x14 */
- U8 BoardName[16]; /* 0x1C */
- U8 BoardAssembly[16]; /* 0x2C */
- U8 BoardTracerNumber[16]; /* 0x3C */
-} MPI2_CONFIG_PAGE_MAN_0,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_0,
- Mpi2ManufacturingPage0_t, MPI2_POINTER pMpi2ManufacturingPage0_t;
-
-#define MPI2_MANUFACTURING0_PAGEVERSION (0x00)
-
-
-/* Manufacturing Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_1
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U8 VPD[256]; /* 0x04 */
-} MPI2_CONFIG_PAGE_MAN_1,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_1,
- Mpi2ManufacturingPage1_t, MPI2_POINTER pMpi2ManufacturingPage1_t;
-
-#define MPI2_MANUFACTURING1_PAGEVERSION (0x00)
-
-
-typedef struct _MPI2_CHIP_REVISION_ID
-{
- U16 DeviceID; /* 0x00 */
- U8 PCIRevisionID; /* 0x02 */
- U8 Reserved; /* 0x03 */
-} MPI2_CHIP_REVISION_ID, MPI2_POINTER PTR_MPI2_CHIP_REVISION_ID,
- Mpi2ChipRevisionId_t, MPI2_POINTER pMpi2ChipRevisionId_t;
-
-
-/* Manufacturing Page 2 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength at runtime.
- */
-#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS
-#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_2
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */
- U32 HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/* 0x08 */
-} MPI2_CONFIG_PAGE_MAN_2,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_2,
- Mpi2ManufacturingPage2_t, MPI2_POINTER pMpi2ManufacturingPage2_t;
-
-#define MPI2_MANUFACTURING2_PAGEVERSION (0x00)
-
-
-/* Manufacturing Page 3 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength at runtime.
- */
-#ifndef MPI2_MAN_PAGE_3_INFO_WORDS
-#define MPI2_MAN_PAGE_3_INFO_WORDS (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_3
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */
- U32 Info[MPI2_MAN_PAGE_3_INFO_WORDS];/* 0x08 */
-} MPI2_CONFIG_PAGE_MAN_3,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_3,
- Mpi2ManufacturingPage3_t, MPI2_POINTER pMpi2ManufacturingPage3_t;
-
-#define MPI2_MANUFACTURING3_PAGEVERSION (0x00)
-
-
-/* Manufacturing Page 4 */
-
-typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS
-{
- U8 PowerSaveFlags; /* 0x00 */
- U8 InternalOperationsSleepTime; /* 0x01 */
- U8 InternalOperationsRunTime; /* 0x02 */
- U8 HostIdleTime; /* 0x03 */
-} MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
- MPI2_POINTER PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
- Mpi2ManPage4PwrSaveSettings_t, MPI2_POINTER pMpi2ManPage4PwrSaveSettings_t;
-
-/* defines for the PowerSaveFlags field */
-#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03)
-#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00)
-#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01)
-#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02)
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_4
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x04 */
- U32 Flags; /* 0x08 */
- U8 InquirySize; /* 0x0C */
- U8 Reserved2; /* 0x0D */
- U16 Reserved3; /* 0x0E */
- U8 InquiryData[56]; /* 0x10 */
- U32 RAID0VolumeSettings; /* 0x48 */
- U32 RAID1EVolumeSettings; /* 0x4C */
- U32 RAID1VolumeSettings; /* 0x50 */
- U32 RAID10VolumeSettings; /* 0x54 */
- U32 Reserved4; /* 0x58 */
- U32 Reserved5; /* 0x5C */
- MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /* 0x60 */
- U8 MaxOCEDisks; /* 0x64 */
- U8 ResyncRate; /* 0x65 */
- U16 DataScrubDuration; /* 0x66 */
- U8 MaxHotSpares; /* 0x68 */
- U8 MaxPhysDisksPerVol; /* 0x69 */
- U8 MaxPhysDisks; /* 0x6A */
- U8 MaxVolumes; /* 0x6B */
-} MPI2_CONFIG_PAGE_MAN_4,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_4,
- Mpi2ManufacturingPage4_t, MPI2_POINTER pMpi2ManufacturingPage4_t;
-
-#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A)
-
-/* Manufacturing Page 4 Flags field */
-#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000)
-#define MPI2_MANPAGE4_METADATA_512MB (0x00000000)
-
-#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000)
-#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000)
-#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000)
-
-#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00)
-#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000)
-#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400)
-#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800)
-#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00)
-
-#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300)
-#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000)
-#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100)
-#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200)
-
-#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080)
-#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040)
-#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020)
-#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010)
-#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008)
-#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004)
-#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002)
-#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001)
-
-
-/* Manufacturing Page 5 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhys at runtime.
- */
-#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES
-#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1)
-#endif
-
-typedef struct _MPI2_MANUFACTURING5_ENTRY
-{
- U64 WWID; /* 0x00 */
- U64 DeviceName; /* 0x08 */
-} MPI2_MANUFACTURING5_ENTRY, MPI2_POINTER PTR_MPI2_MANUFACTURING5_ENTRY,
- Mpi2Manufacturing5Entry_t, MPI2_POINTER pMpi2Manufacturing5Entry_t;
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_5
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U8 NumPhys; /* 0x04 */
- U8 Reserved1; /* 0x05 */
- U16 Reserved2; /* 0x06 */
- U32 Reserved3; /* 0x08 */
- U32 Reserved4; /* 0x0C */
- MPI2_MANUFACTURING5_ENTRY Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/* 0x08 */
-} MPI2_CONFIG_PAGE_MAN_5,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_5,
- Mpi2ManufacturingPage5_t, MPI2_POINTER pMpi2ManufacturingPage5_t;
-
-#define MPI2_MANUFACTURING5_PAGEVERSION (0x03)
-
-
-/* Manufacturing Page 6 */
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_6
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 ProductSpecificInfo;/* 0x04 */
-} MPI2_CONFIG_PAGE_MAN_6,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_6,
- Mpi2ManufacturingPage6_t, MPI2_POINTER pMpi2ManufacturingPage6_t;
-
-#define MPI2_MANUFACTURING6_PAGEVERSION (0x00)
-
-
-/* Manufacturing Page 7 */
-
-typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO
-{
- U32 Pinout; /* 0x00 */
- U8 Connector[16]; /* 0x04 */
- U8 Location; /* 0x14 */
- U8 Reserved1; /* 0x15 */
- U16 Slot; /* 0x16 */
- U32 Reserved2; /* 0x18 */
-} MPI2_MANPAGE7_CONNECTOR_INFO, MPI2_POINTER PTR_MPI2_MANPAGE7_CONNECTOR_INFO,
- Mpi2ManPage7ConnectorInfo_t, MPI2_POINTER pMpi2ManPage7ConnectorInfo_t;
-
-/* defines for the Pinout field */
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L4 (0x00080000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L3 (0x00040000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L2 (0x00020000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L1 (0x00010000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L4 (0x00000800)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L3 (0x00000400)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L2 (0x00000200)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L1 (0x00000100)
-#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x00000002)
-#define MPI2_MANPAGE7_PINOUT_CONNECTION_UNKNOWN (0x00000001)
-
-/* defines for the Location field */
-#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01)
-#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02)
-#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04)
-#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08)
-#define MPI2_MANPAGE7_LOCATION_AUTO (0x10)
-#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20)
-#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80)
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhys at runtime.
- */
-#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX
-#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_7
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x04 */
- U32 Reserved2; /* 0x08 */
- U32 Flags; /* 0x0C */
- U8 EnclosureName[16]; /* 0x10 */
- U8 NumPhys; /* 0x20 */
- U8 Reserved3; /* 0x21 */
- U16 Reserved4; /* 0x22 */
- MPI2_MANPAGE7_CONNECTOR_INFO ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /* 0x24 */
-} MPI2_CONFIG_PAGE_MAN_7,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_7,
- Mpi2ManufacturingPage7_t, MPI2_POINTER pMpi2ManufacturingPage7_t;
-
-#define MPI2_MANUFACTURING7_PAGEVERSION (0x00)
-
-/* defines for the Flags field */
-#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001)
-
-
-/*
- * Generic structure to use for product-specific manufacturing pages
- * (currently Manufacturing Page 8 through Manufacturing Page 31).
- */
-
-typedef struct _MPI2_CONFIG_PAGE_MAN_PS
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 ProductSpecificInfo;/* 0x04 */
-} MPI2_CONFIG_PAGE_MAN_PS,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_PS,
- Mpi2ManufacturingPagePS_t, MPI2_POINTER pMpi2ManufacturingPagePS_t;
-
-#define MPI2_MANUFACTURING8_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING9_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING10_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING11_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING12_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING13_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING14_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING15_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING16_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING17_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING18_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING19_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING20_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING21_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING22_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING23_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING24_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING25_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING26_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING27_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING28_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING29_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING30_PAGEVERSION (0x00)
-#define MPI2_MANUFACTURING31_PAGEVERSION (0x00)
-
-
-/****************************************************************************
-* IO Unit Config Pages
-****************************************************************************/
-
-/* IO Unit Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U64 UniqueValue; /* 0x04 */
- MPI2_VERSION_UNION NvdataVersionDefault; /* 0x08 */
- MPI2_VERSION_UNION NvdataVersionPersistent; /* 0x0A */
-} MPI2_CONFIG_PAGE_IO_UNIT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_0,
- Mpi2IOUnitPage0_t, MPI2_POINTER pMpi2IOUnitPage0_t;
-
-#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02)
-
-
-/* IO Unit Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 Flags; /* 0x04 */
-} MPI2_CONFIG_PAGE_IO_UNIT_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_1,
- Mpi2IOUnitPage1_t, MPI2_POINTER pMpi2IOUnitPage1_t;
-
-#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04)
-
-/* IO Unit Page 1 Flags defines */
-#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800)
-#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600)
-#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9)
-#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000)
-#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200)
-#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400)
-#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100)
-#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040)
-#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020)
-#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004)
-
-
-/* IO Unit Page 3 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for GPIOCount at runtime.
- */
-#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX
-#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U8 GPIOCount; /* 0x04 */
- U8 Reserved1; /* 0x05 */
- U16 Reserved2; /* 0x06 */
- U16 GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/* 0x08 */
-} MPI2_CONFIG_PAGE_IO_UNIT_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_3,
- Mpi2IOUnitPage3_t, MPI2_POINTER pMpi2IOUnitPage3_t;
-
-#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01)
-
-/* defines for IO Unit Page 3 GPIOVal field */
-#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC)
-#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2)
-#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000)
-#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001)
-
-
-/* IO Unit Page 5 */
-
-/*
- * Upper layer code (drivers, utilities, etc.) should leave this define set to
- * one and check the value returned for NumDmaEngines at runtime.
- */
-#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES
-#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 {
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U64 RaidAcceleratorBufferBaseAddress; /* 0x04 */
- U64 RaidAcceleratorBufferSize; /* 0x0C */
- U64 RaidAcceleratorControlBaseAddress; /* 0x14 */
- U8 RAControlSize; /* 0x1C */
- U8 NumDmaEngines; /* 0x1D */
- U8 RAMinControlSize; /* 0x1E */
- U8 RAMaxControlSize; /* 0x1F */
- U32 Reserved1; /* 0x20 */
- U32 Reserved2; /* 0x24 */
- U32 Reserved3; /* 0x28 */
- U32 DmaEngineCapabilities
- [MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /* 0x2C */
-} MPI2_CONFIG_PAGE_IO_UNIT_5, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_5,
- Mpi2IOUnitPage5_t, MPI2_POINTER pMpi2IOUnitPage5_t;
-
-#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00)
-
-/* defines for IO Unit Page 5 DmaEngineCapabilities field */
-#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFF00)
-#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16)
-
-#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008)
-#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004)
-#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002)
-#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001)
-
-
-/* IO Unit Page 6 */
-
-typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 {
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U16 Flags; /* 0x04 */
- U8 RAHostControlSize; /* 0x06 */
- U8 Reserved0; /* 0x07 */
- U64 RaidAcceleratorHostControlBaseAddress; /* 0x08 */
- U32 Reserved1; /* 0x10 */
- U32 Reserved2; /* 0x14 */
- U32 Reserved3; /* 0x18 */
-} MPI2_CONFIG_PAGE_IO_UNIT_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_6,
- Mpi2IOUnitPage6_t, MPI2_POINTER pMpi2IOUnitPage6_t;
-
-#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00)
-
-/* defines for IO Unit Page 6 Flags field */
-#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001)
-
-
-/* IO Unit Page 7 */
-
-typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 {
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U16 Reserved1; /* 0x04 */
- U8 PCIeWidth; /* 0x06 */
- U8 PCIeSpeed; /* 0x07 */
- U32 ProcessorState; /* 0x08 */
- U32 PowerManagementCapabilities; /* 0x0C */
- U16 IOCTemperature; /* 0x10 */
- U8 IOCTemperatureUnits; /* 0x12 */
- U8 IOCSpeed; /* 0x13 */
- U32 Reserved3; /* 0x14 */
-} MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
- Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t;
-
-#define MPI2_IOUNITPAGE7_PAGEVERSION (0x01)
-
-/* defines for IO Unit Page 7 PCIeWidth field */
-#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01)
-#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02)
-#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04)
-#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08)
-
-/* defines for IO Unit Page 7 PCIeSpeed field */
-#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00)
-#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01)
-#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02)
-
-/* defines for IO Unit Page 7 ProcessorState field */
-#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F)
-#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0)
-
-#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00)
-#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01)
-#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02)
-
-/* defines for IO Unit Page 7 PowerManagementCapabilities field */
-#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400)
-#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200)
-#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100)
-#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008)
-#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004)
-
-
-/* defines for IO Unit Page 7 IOCTemperatureUnits field */
-#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00)
-#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01)
-#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02)
-
-/* defines for IO Unit Page 7 IOCSpeed field */
-#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01)
-#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02)
-#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04)
-#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08)
-
-
-
-/****************************************************************************
-* IOC Config Pages
-****************************************************************************/
-
-/* IOC Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_IOC_0
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x04 */
- U32 Reserved2; /* 0x08 */
- U16 VendorID; /* 0x0C */
- U16 DeviceID; /* 0x0E */
- U8 RevisionID; /* 0x10 */
- U8 Reserved3; /* 0x11 */
- U16 Reserved4; /* 0x12 */
- U32 ClassCode; /* 0x14 */
- U16 SubsystemVendorID; /* 0x18 */
- U16 SubsystemID; /* 0x1A */
-} MPI2_CONFIG_PAGE_IOC_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_0,
- Mpi2IOCPage0_t, MPI2_POINTER pMpi2IOCPage0_t;
-
-#define MPI2_IOCPAGE0_PAGEVERSION (0x02)
-
-
-/* IOC Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_IOC_1
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 Flags; /* 0x04 */
- U32 CoalescingTimeout; /* 0x08 */
- U8 CoalescingDepth; /* 0x0C */
- U8 PCISlotNum; /* 0x0D */
- U8 PCIBusNum; /* 0x0E */
- U8 PCIDomainSegment; /* 0x0F */
- U32 Reserved1; /* 0x10 */
- U32 Reserved2; /* 0x14 */
-} MPI2_CONFIG_PAGE_IOC_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_1,
- Mpi2IOCPage1_t, MPI2_POINTER pMpi2IOCPage1_t;
-
-#define MPI2_IOCPAGE1_PAGEVERSION (0x05)
-
-/* defines for IOC Page 1 Flags field */
-#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001)
-
-#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF)
-#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF)
-#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF)
-
-/* IOC Page 6 */
-
-typedef struct _MPI2_CONFIG_PAGE_IOC_6
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 CapabilitiesFlags; /* 0x04 */
- U8 MaxDrivesRAID0; /* 0x08 */
- U8 MaxDrivesRAID1; /* 0x09 */
- U8 MaxDrivesRAID1E; /* 0x0A */
- U8 MaxDrivesRAID10; /* 0x0B */
- U8 MinDrivesRAID0; /* 0x0C */
- U8 MinDrivesRAID1; /* 0x0D */
- U8 MinDrivesRAID1E; /* 0x0E */
- U8 MinDrivesRAID10; /* 0x0F */
- U32 Reserved1; /* 0x10 */
- U8 MaxGlobalHotSpares; /* 0x14 */
- U8 MaxPhysDisks; /* 0x15 */
- U8 MaxVolumes; /* 0x16 */
- U8 MaxConfigs; /* 0x17 */
- U8 MaxOCEDisks; /* 0x18 */
- U8 Reserved2; /* 0x19 */
- U16 Reserved3; /* 0x1A */
- U32 SupportedStripeSizeMapRAID0; /* 0x1C */
- U32 SupportedStripeSizeMapRAID1E; /* 0x20 */
- U32 SupportedStripeSizeMapRAID10; /* 0x24 */
- U32 Reserved4; /* 0x28 */
- U32 Reserved5; /* 0x2C */
- U16 DefaultMetadataSize; /* 0x30 */
- U16 Reserved6; /* 0x32 */
- U16 MaxBadBlockTableEntries; /* 0x34 */
- U16 Reserved7; /* 0x36 */
- U32 IRNvsramVersion; /* 0x38 */
-} MPI2_CONFIG_PAGE_IOC_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_6,
- Mpi2IOCPage6_t, MPI2_POINTER pMpi2IOCPage6_t;
-
-#define MPI2_IOCPAGE6_PAGEVERSION (0x04)
-
-/* defines for IOC Page 6 CapabilitiesFlags */
-#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010)
-#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008)
-#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004)
-#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002)
-#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001)
-
-
-/* IOC Page 7 */
-
-#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4)
-
-typedef struct _MPI2_CONFIG_PAGE_IOC_7
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x04 */
- U32 EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/* 0x08 */
- U16 SASBroadcastPrimitiveMasks; /* 0x18 */
- U16 Reserved2; /* 0x1A */
- U32 Reserved3; /* 0x1C */
-} MPI2_CONFIG_PAGE_IOC_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_7,
- Mpi2IOCPage7_t, MPI2_POINTER pMpi2IOCPage7_t;
-
-#define MPI2_IOCPAGE7_PAGEVERSION (0x01)
-
-
-/* IOC Page 8 */
-
-typedef struct _MPI2_CONFIG_PAGE_IOC_8
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U8 NumDevsPerEnclosure; /* 0x04 */
- U8 Reserved1; /* 0x05 */
- U16 Reserved2; /* 0x06 */
- U16 MaxPersistentEntries; /* 0x08 */
- U16 MaxNumPhysicalMappedIDs; /* 0x0A */
- U16 Flags; /* 0x0C */
- U16 Reserved3; /* 0x0E */
- U16 IRVolumeMappingFlags; /* 0x10 */
- U16 Reserved4; /* 0x12 */
- U32 Reserved5; /* 0x14 */
-} MPI2_CONFIG_PAGE_IOC_8, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_8,
- Mpi2IOCPage8_t, MPI2_POINTER pMpi2IOCPage8_t;
-
-#define MPI2_IOCPAGE8_PAGEVERSION (0x00)
-
-/* defines for IOC Page 8 Flags field */
-#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020)
-#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010)
-
-#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E)
-#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000)
-#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002)
-
-#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001)
-#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000)
-
-/* defines for IOC Page 8 IRVolumeMappingFlags */
-#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003)
-#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000)
-#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001)
-
-
-/****************************************************************************
-* BIOS Config Pages
-****************************************************************************/
-
-/* BIOS Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_BIOS_1
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 BiosOptions; /* 0x04 */
- U32 IOCSettings; /* 0x08 */
- U32 Reserved1; /* 0x0C */
- U32 DeviceSettings; /* 0x10 */
- U16 NumberOfDevices; /* 0x14 */
- U16 Reserved2; /* 0x16 */
- U16 IOTimeoutBlockDevicesNonRM; /* 0x18 */
- U16 IOTimeoutSequential; /* 0x1A */
- U16 IOTimeoutOther; /* 0x1C */
- U16 IOTimeoutBlockDevicesRM; /* 0x1E */
-} MPI2_CONFIG_PAGE_BIOS_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_1,
- Mpi2BiosPage1_t, MPI2_POINTER pMpi2BiosPage1_t;
-
-#define MPI2_BIOSPAGE1_PAGEVERSION (0x04)
-
-/* values for BIOS Page 1 BiosOptions field */
-#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001)
-
-/* values for BIOS Page 1 IOCSettings field */
-#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000)
-#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000)
-#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000)
-
-#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0)
-#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000)
-#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040)
-#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080)
-
-#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030)
-#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000)
-#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010)
-#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020)
-#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030)
-
-#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008)
-
-/* values for BIOS Page 1 DeviceSettings field */
-#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010)
-#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008)
-#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004)
-#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002)
-#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001)
-
-
-/* BIOS Page 2 */
-
-typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER
-{
- U32 Reserved1; /* 0x00 */
- U32 Reserved2; /* 0x04 */
- U32 Reserved3; /* 0x08 */
- U32 Reserved4; /* 0x0C */
- U32 Reserved5; /* 0x10 */
- U32 Reserved6; /* 0x14 */
-} MPI2_BOOT_DEVICE_ADAPTER_ORDER,
- MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER,
- Mpi2BootDeviceAdapterOrder_t, MPI2_POINTER pMpi2BootDeviceAdapterOrder_t;
-
-typedef struct _MPI2_BOOT_DEVICE_SAS_WWID
-{
- U64 SASAddress; /* 0x00 */
- U8 LUN[8]; /* 0x08 */
- U32 Reserved1; /* 0x10 */
- U32 Reserved2; /* 0x14 */
-} MPI2_BOOT_DEVICE_SAS_WWID, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_SAS_WWID,
- Mpi2BootDeviceSasWwid_t, MPI2_POINTER pMpi2BootDeviceSasWwid_t;
-
-typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT
-{
- U64 EnclosureLogicalID; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U32 Reserved2; /* 0x0C */
- U16 SlotNumber; /* 0x10 */
- U16 Reserved3; /* 0x12 */
- U32 Reserved4; /* 0x14 */
-} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
- MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
- Mpi2BootDeviceEnclosureSlot_t, MPI2_POINTER pMpi2BootDeviceEnclosureSlot_t;
-
-typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME
-{
- U64 DeviceName; /* 0x00 */
- U8 LUN[8]; /* 0x08 */
- U32 Reserved1; /* 0x10 */
- U32 Reserved2; /* 0x14 */
-} MPI2_BOOT_DEVICE_DEVICE_NAME, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_DEVICE_NAME,
- Mpi2BootDeviceDeviceName_t, MPI2_POINTER pMpi2BootDeviceDeviceName_t;
-
-typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE
-{
- MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder;
- MPI2_BOOT_DEVICE_SAS_WWID SasWwid;
- MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot;
- MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName;
-} MPI2_BIOSPAGE2_BOOT_DEVICE, MPI2_POINTER PTR_MPI2_BIOSPAGE2_BOOT_DEVICE,
- Mpi2BiosPage2BootDevice_t, MPI2_POINTER pMpi2BiosPage2BootDevice_t;
-
-typedef struct _MPI2_CONFIG_PAGE_BIOS_2
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x04 */
- U32 Reserved2; /* 0x08 */
- U32 Reserved3; /* 0x0C */
- U32 Reserved4; /* 0x10 */
- U32 Reserved5; /* 0x14 */
- U32 Reserved6; /* 0x18 */
- U8 ReqBootDeviceForm; /* 0x1C */
- U8 Reserved7; /* 0x1D */
- U16 Reserved8; /* 0x1E */
- MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /* 0x20 */
- U8 ReqAltBootDeviceForm; /* 0x38 */
- U8 Reserved9; /* 0x39 */
- U16 Reserved10; /* 0x3A */
- MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /* 0x3C */
- U8 CurrentBootDeviceForm; /* 0x58 */
- U8 Reserved11; /* 0x59 */
- U16 Reserved12; /* 0x5A */
- MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /* 0x58 */
-} MPI2_CONFIG_PAGE_BIOS_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_2,
- Mpi2BiosPage2_t, MPI2_POINTER pMpi2BiosPage2_t;
-
-#define MPI2_BIOSPAGE2_PAGEVERSION (0x04)
-
-/* values for BIOS Page 2 BootDeviceForm fields */
-#define MPI2_BIOSPAGE2_FORM_MASK (0x0F)
-#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00)
-#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05)
-#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06)
-#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07)
-
-
-/* BIOS Page 3 */
-
-typedef struct _MPI2_ADAPTER_INFO
-{
- U8 PciBusNumber; /* 0x00 */
- U8 PciDeviceAndFunctionNumber; /* 0x01 */
- U16 AdapterFlags; /* 0x02 */
-} MPI2_ADAPTER_INFO, MPI2_POINTER PTR_MPI2_ADAPTER_INFO,
- Mpi2AdapterInfo_t, MPI2_POINTER pMpi2AdapterInfo_t;
-
-#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001)
-#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002)
-
-typedef struct _MPI2_CONFIG_PAGE_BIOS_3
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U32 GlobalFlags; /* 0x04 */
- U32 BiosVersion; /* 0x08 */
- MPI2_ADAPTER_INFO AdapterOrder[4]; /* 0x0C */
- U32 Reserved1; /* 0x1C */
-} MPI2_CONFIG_PAGE_BIOS_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_3,
- Mpi2BiosPage3_t, MPI2_POINTER pMpi2BiosPage3_t;
-
-#define MPI2_BIOSPAGE3_PAGEVERSION (0x00)
-
-/* values for BIOS Page 3 GlobalFlags */
-#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002)
-#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004)
-#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010)
-
-#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0)
-#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000)
-#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020)
-#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040)
-
-
-/* BIOS Page 4 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhys at runtime.
- */
-#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES
-#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1)
-#endif
-
-typedef struct _MPI2_BIOS4_ENTRY
-{
- U64 ReassignmentWWID; /* 0x00 */
- U64 ReassignmentDeviceName; /* 0x08 */
-} MPI2_BIOS4_ENTRY, MPI2_POINTER PTR_MPI2_BIOS4_ENTRY,
- Mpi2MBios4Entry_t, MPI2_POINTER pMpi2Bios4Entry_t;
-
-typedef struct _MPI2_CONFIG_PAGE_BIOS_4
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U8 NumPhys; /* 0x04 */
- U8 Reserved1; /* 0x05 */
- U16 Reserved2; /* 0x06 */
- MPI2_BIOS4_ENTRY Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /* 0x08 */
-} MPI2_CONFIG_PAGE_BIOS_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_4,
- Mpi2BiosPage4_t, MPI2_POINTER pMpi2BiosPage4_t;
-
-#define MPI2_BIOSPAGE4_PAGEVERSION (0x01)
-
-
-/****************************************************************************
-* RAID Volume Config Pages
-****************************************************************************/
-
-/* RAID Volume Page 0 */
-
-typedef struct _MPI2_RAIDVOL0_PHYS_DISK
-{
- U8 RAIDSetNum; /* 0x00 */
- U8 PhysDiskMap; /* 0x01 */
- U8 PhysDiskNum; /* 0x02 */
- U8 Reserved; /* 0x03 */
-} MPI2_RAIDVOL0_PHYS_DISK, MPI2_POINTER PTR_MPI2_RAIDVOL0_PHYS_DISK,
- Mpi2RaidVol0PhysDisk_t, MPI2_POINTER pMpi2RaidVol0PhysDisk_t;
-
-/* defines for the PhysDiskMap field */
-#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01)
-#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02)
-
-typedef struct _MPI2_RAIDVOL0_SETTINGS
-{
- U16 Settings; /* 0x00 */
- U8 HotSparePool; /* 0x01 */
- U8 Reserved; /* 0x02 */
-} MPI2_RAIDVOL0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDVOL0_SETTINGS,
- Mpi2RaidVol0Settings_t, MPI2_POINTER pMpi2RaidVol0Settings_t;
-
-/* RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */
-#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01)
-#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02)
-#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04)
-#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08)
-#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10)
-#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20)
-#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40)
-#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80)
-
-/* RAID Volume Page 0 VolumeSettings defines */
-#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008)
-#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004)
-
-#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003)
-#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000)
-#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001)
-#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002)
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhysDisks at runtime.
- */
-#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX
-#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U16 DevHandle; /* 0x04 */
- U8 VolumeState; /* 0x06 */
- U8 VolumeType; /* 0x07 */
- U32 VolumeStatusFlags; /* 0x08 */
- MPI2_RAIDVOL0_SETTINGS VolumeSettings; /* 0x0C */
- U64 MaxLBA; /* 0x10 */
- U32 StripeSize; /* 0x18 */
- U16 BlockSize; /* 0x1C */
- U16 Reserved1; /* 0x1E */
- U8 SupportedPhysDisks; /* 0x20 */
- U8 ResyncRate; /* 0x21 */
- U16 DataScrubDuration; /* 0x22 */
- U8 NumPhysDisks; /* 0x24 */
- U8 Reserved2; /* 0x25 */
- U8 Reserved3; /* 0x26 */
- U8 InactiveStatus; /* 0x27 */
- MPI2_RAIDVOL0_PHYS_DISK PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /* 0x28 */
-} MPI2_CONFIG_PAGE_RAID_VOL_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_0,
- Mpi2RaidVolPage0_t, MPI2_POINTER pMpi2RaidVolPage0_t;
-
-#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A)
-
-/* values for RAID VolumeState */
-#define MPI2_RAID_VOL_STATE_MISSING (0x00)
-#define MPI2_RAID_VOL_STATE_FAILED (0x01)
-#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02)
-#define MPI2_RAID_VOL_STATE_ONLINE (0x03)
-#define MPI2_RAID_VOL_STATE_DEGRADED (0x04)
-#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05)
-
-/* values for RAID VolumeType */
-#define MPI2_RAID_VOL_TYPE_RAID0 (0x00)
-#define MPI2_RAID_VOL_TYPE_RAID1E (0x01)
-#define MPI2_RAID_VOL_TYPE_RAID1 (0x02)
-#define MPI2_RAID_VOL_TYPE_RAID10 (0x05)
-#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF)
-
-/* values for RAID Volume Page 0 VolumeStatusFlags field */
-#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080)
-#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040)
-#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020)
-#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000)
-#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010)
-#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008)
-#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004)
-#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002)
-#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001)
-
-/* values for RAID Volume Page 0 SupportedPhysDisks field */
-#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08)
-#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04)
-#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02)
-#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01)
-
-/* values for RAID Volume Page 0 InactiveStatus field */
-#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00)
-#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01)
-#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02)
-#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03)
-#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04)
-#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05)
-#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06)
-
-
-/* RAID Volume Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U16 DevHandle; /* 0x04 */
- U16 Reserved0; /* 0x06 */
- U8 GUID[24]; /* 0x08 */
- U8 Name[16]; /* 0x20 */
- U64 WWID; /* 0x30 */
- U32 Reserved1; /* 0x38 */
- U32 Reserved2; /* 0x3C */
-} MPI2_CONFIG_PAGE_RAID_VOL_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_1,
- Mpi2RaidVolPage1_t, MPI2_POINTER pMpi2RaidVolPage1_t;
-
-#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03)
-
-
-/****************************************************************************
-* RAID Physical Disk Config Pages
-****************************************************************************/
-
-/* RAID Physical Disk Page 0 */
-
-typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS
-{
- U16 Reserved1; /* 0x00 */
- U8 HotSparePool; /* 0x02 */
- U8 Reserved2; /* 0x03 */
-} MPI2_RAIDPHYSDISK0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_SETTINGS,
- Mpi2RaidPhysDisk0Settings_t, MPI2_POINTER pMpi2RaidPhysDisk0Settings_t;
-
-/* use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */
-
-typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA
-{
- U8 VendorID[8]; /* 0x00 */
- U8 ProductID[16]; /* 0x08 */
- U8 ProductRevLevel[4]; /* 0x18 */
- U8 SerialNum[32]; /* 0x1C */
-} MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
- MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
- Mpi2RaidPhysDisk0InquiryData_t, MPI2_POINTER pMpi2RaidPhysDisk0InquiryData_t;
-
-typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U16 DevHandle; /* 0x04 */
- U8 Reserved1; /* 0x06 */
- U8 PhysDiskNum; /* 0x07 */
- MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /* 0x08 */
- U32 Reserved2; /* 0x0C */
- MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /* 0x10 */
- U32 Reserved3; /* 0x4C */
- U8 PhysDiskState; /* 0x50 */
- U8 OfflineReason; /* 0x51 */
- U8 IncompatibleReason; /* 0x52 */
- U8 PhysDiskAttributes; /* 0x53 */
- U32 PhysDiskStatusFlags; /* 0x54 */
- U64 DeviceMaxLBA; /* 0x58 */
- U64 HostMaxLBA; /* 0x60 */
- U64 CoercedMaxLBA; /* 0x68 */
- U16 BlockSize; /* 0x70 */
- U16 Reserved5; /* 0x72 */
- U32 Reserved6; /* 0x74 */
-} MPI2_CONFIG_PAGE_RD_PDISK_0,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_0,
- Mpi2RaidPhysDiskPage0_t, MPI2_POINTER pMpi2RaidPhysDiskPage0_t;
-
-#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05)
-
-/* PhysDiskState defines */
-#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00)
-#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01)
-#define MPI2_RAID_PD_STATE_OFFLINE (0x02)
-#define MPI2_RAID_PD_STATE_ONLINE (0x03)
-#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04)
-#define MPI2_RAID_PD_STATE_DEGRADED (0x05)
-#define MPI2_RAID_PD_STATE_REBUILDING (0x06)
-#define MPI2_RAID_PD_STATE_OPTIMAL (0x07)
-
-/* OfflineReason defines */
-#define MPI2_PHYSDISK0_ONLINE (0x00)
-#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01)
-#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03)
-#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04)
-#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05)
-#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06)
-#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF)
-
-/* IncompatibleReason defines */
-#define MPI2_PHYSDISK0_COMPATIBLE (0x00)
-#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01)
-#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02)
-#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03)
-#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04)
-#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05)
-#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06)
-#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF)
-
-/* PhysDiskAttributes defines */
-#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C)
-#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08)
-#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04)
-
-#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03)
-#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02)
-#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01)
-
-/* PhysDiskStatusFlags defines */
-#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040)
-#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020)
-#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010)
-#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000)
-#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008)
-#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004)
-#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002)
-#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001)
-
-
-/* RAID Physical Disk Page 1 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhysDiskPaths at runtime.
- */
-#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX
-#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1)
-#endif
-
-typedef struct _MPI2_RAIDPHYSDISK1_PATH
-{
- U16 DevHandle; /* 0x00 */
- U16 Reserved1; /* 0x02 */
- U64 WWID; /* 0x04 */
- U64 OwnerWWID; /* 0x0C */
- U8 OwnerIdentifier; /* 0x14 */
- U8 Reserved2; /* 0x15 */
- U16 Flags; /* 0x16 */
-} MPI2_RAIDPHYSDISK1_PATH, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK1_PATH,
- Mpi2RaidPhysDisk1Path_t, MPI2_POINTER pMpi2RaidPhysDisk1Path_t;
-
-/* RAID Physical Disk Page 1 Physical Disk Path Flags field defines */
-#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004)
-#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002)
-#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001)
-
-typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1
-{
- MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
- U8 NumPhysDiskPaths; /* 0x04 */
- U8 PhysDiskNum; /* 0x05 */
- U16 Reserved1; /* 0x06 */
- U32 Reserved2; /* 0x08 */
- MPI2_RAIDPHYSDISK1_PATH PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/* 0x0C */
-} MPI2_CONFIG_PAGE_RD_PDISK_1,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_1,
- Mpi2RaidPhysDiskPage1_t, MPI2_POINTER pMpi2RaidPhysDiskPage1_t;
-
-#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02)
-
-
-/****************************************************************************
-* values for fields used by several types of SAS Config Pages
-****************************************************************************/
-
-/* values for NegotiatedLinkRates fields */
-#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0)
-#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4)
-#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F)
-/* link rates used for Negotiated Physical and Logical Link Rate */
-#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00)
-#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01)
-#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02)
-#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03)
-#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04)
-#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05)
-#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06)
-#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08)
-#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09)
-#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A)
-
-
-/* values for AttachedPhyInfo fields */
-#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040)
-#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020)
-#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010)
-
-#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F)
-#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000)
-#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001)
-#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002)
-#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003)
-#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004)
-#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005)
-#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006)
-#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007)
-#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008)
-
-
-/* values for PhyInfo fields */
-#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000)
-
-#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
-#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27)
-#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000)
-#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000)
-#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000)
-
-#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000)
-#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000)
-#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000)
-#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000)
-#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000)
-#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000)
-
-#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000)
-#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000)
-#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000)
-#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000)
-#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000)
-#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000)
-#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000)
-#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000)
-#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000)
-#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000)
-
-#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000)
-#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000)
-#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000)
-#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000)
-
-#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00)
-#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8)
-
-#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0)
-#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000)
-#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010)
-#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020)
-
-
-/* values for SAS ProgrammedLinkRate fields */
-#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0)
-#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00)
-#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80)
-#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90)
-#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0)
-#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F)
-#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00)
-#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08)
-#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09)
-#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A)
-
-
-/* values for SAS HwLinkRate fields */
-#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0)
-#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80)
-#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90)
-#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0)
-#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F)
-#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08)
-#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09)
-#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A)
-
-
-
-/****************************************************************************
-* SAS IO Unit Config Pages
-****************************************************************************/
-
-/* SAS IO Unit Page 0 */
-
-typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA
-{
- U8 Port; /* 0x00 */
- U8 PortFlags; /* 0x01 */
- U8 PhyFlags; /* 0x02 */
- U8 NegotiatedLinkRate; /* 0x03 */
- U32 ControllerPhyDeviceInfo;/* 0x04 */
- U16 AttachedDevHandle; /* 0x08 */
- U16 ControllerDevHandle; /* 0x0A */
- U32 DiscoveryStatus; /* 0x0C */
- U32 Reserved; /* 0x10 */
-} MPI2_SAS_IO_UNIT0_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT0_PHY_DATA,
- Mpi2SasIOUnit0PhyData_t, MPI2_POINTER pMpi2SasIOUnit0PhyData_t;
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhys at runtime.
- */
-#ifndef MPI2_SAS_IOUNIT0_PHY_MAX
-#define MPI2_SAS_IOUNIT0_PHY_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U8 NumPhys; /* 0x0C */
- U8 Reserved2; /* 0x0D */
- U16 Reserved3; /* 0x0E */
- MPI2_SAS_IO_UNIT0_PHY_DATA PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /* 0x10 */
-} MPI2_CONFIG_PAGE_SASIOUNIT_0,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0,
- Mpi2SasIOUnitPage0_t, MPI2_POINTER pMpi2SasIOUnitPage0_t;
-
-#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05)
-
-/* values for SAS IO Unit Page 0 PortFlags */
-#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08)
-#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01)
-
-/* values for SAS IO Unit Page 0 PhyFlags */
-#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10)
-#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
-
-/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
-
-/* see mpi2_sas.h for values for SAS IO Unit Page 0 ControllerPhyDeviceInfo values */
-
-/* values for SAS IO Unit Page 0 DiscoveryStatus */
-#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
-#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
-#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000)
-#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
-#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000)
-#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
-#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
-#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000)
-#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
-#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800)
-#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400)
-#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200)
-#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100)
-#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080)
-#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040)
-#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020)
-#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010)
-#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004)
-#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002)
-#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001)
-
-
-/* SAS IO Unit Page 1 */
-
-typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA
-{
- U8 Port; /* 0x00 */
- U8 PortFlags; /* 0x01 */
- U8 PhyFlags; /* 0x02 */
- U8 MaxMinLinkRate; /* 0x03 */
- U32 ControllerPhyDeviceInfo; /* 0x04 */
- U16 MaxTargetPortConnectTime; /* 0x08 */
- U16 Reserved1; /* 0x0A */
-} MPI2_SAS_IO_UNIT1_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT1_PHY_DATA,
- Mpi2SasIOUnit1PhyData_t, MPI2_POINTER pMpi2SasIOUnit1PhyData_t;
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhys at runtime.
- */
-#ifndef MPI2_SAS_IOUNIT1_PHY_MAX
-#define MPI2_SAS_IOUNIT1_PHY_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U16 ControlFlags; /* 0x08 */
- U16 SASNarrowMaxQueueDepth; /* 0x0A */
- U16 AdditionalControlFlags; /* 0x0C */
- U16 SASWideMaxQueueDepth; /* 0x0E */
- U8 NumPhys; /* 0x10 */
- U8 SATAMaxQDepth; /* 0x11 */
- U8 ReportDeviceMissingDelay; /* 0x12 */
- U8 IODeviceMissingDelay; /* 0x13 */
- MPI2_SAS_IO_UNIT1_PHY_DATA PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /* 0x14 */
-} MPI2_CONFIG_PAGE_SASIOUNIT_1,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1,
- Mpi2SasIOUnitPage1_t, MPI2_POINTER pMpi2SasIOUnitPage1_t;
-
-#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09)
-
-/* values for SAS IO Unit Page 1 ControlFlags */
-#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000)
-#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000)
-#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000)
-#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000)
-
-#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600)
-#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9)
-#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0)
-#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1)
-#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2)
-
-#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080)
-#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040)
-#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020)
-#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010)
-#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008)
-#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004)
-#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002)
-#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001)
-
-/* values for SAS IO Unit Page 1 AdditionalControlFlags */
-#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080)
-#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040)
-#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020)
-#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010)
-#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008)
-#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004)
-#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002)
-#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001)
-
-/* defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */
-#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F)
-#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80)
-
-/* values for SAS IO Unit Page 1 PortFlags */
-#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
-
-/* values for SAS IO Unit Page 1 PhyFlags */
-#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10)
-#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
-
-/* values for SAS IO Unit Page 1 MaxMinLinkRate */
-#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0)
-#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80)
-#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90)
-#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0)
-#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F)
-#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08)
-#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09)
-#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A)
-
-/* see mpi2_sas.h for values for SAS IO Unit Page 1 ControllerPhyDeviceInfo values */
-
-
-/* SAS IO Unit Page 4 */
-
-typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP
-{
- U8 MaxTargetSpinup; /* 0x00 */
- U8 SpinupDelay; /* 0x01 */
- U16 Reserved1; /* 0x02 */
-} MPI2_SAS_IOUNIT4_SPINUP_GROUP, MPI2_POINTER PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP,
- Mpi2SasIOUnit4SpinupGroup_t, MPI2_POINTER pMpi2SasIOUnit4SpinupGroup_t;
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhys at runtime.
- */
-#ifndef MPI2_SAS_IOUNIT4_PHY_MAX
-#define MPI2_SAS_IOUNIT4_PHY_MAX (4)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- MPI2_SAS_IOUNIT4_SPINUP_GROUP SpinupGroupParameters[4]; /* 0x08 */
- U32 Reserved1; /* 0x18 */
- U32 Reserved2; /* 0x1C */
- U32 Reserved3; /* 0x20 */
- U8 BootDeviceWaitTime; /* 0x24 */
- U8 Reserved4; /* 0x25 */
- U16 Reserved5; /* 0x26 */
- U8 NumPhys; /* 0x28 */
- U8 PEInitialSpinupDelay; /* 0x29 */
- U8 PEReplyDelay; /* 0x2A */
- U8 Flags; /* 0x2B */
- U8 PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /* 0x2C */
-} MPI2_CONFIG_PAGE_SASIOUNIT_4,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4,
- Mpi2SasIOUnitPage4_t, MPI2_POINTER pMpi2SasIOUnitPage4_t;
-
-#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02)
-
-/* defines for Flags field */
-#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01)
-
-/* defines for PHY field */
-#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03)
-
-
-/* SAS IO Unit Page 5 */
-
-typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS {
- U8 ControlFlags; /* 0x00 */
- U8 PortWidthModGroup; /* 0x01 */
- U16 InactivityTimerExponent; /* 0x02 */
- U8 SATAPartialTimeout; /* 0x04 */
- U8 Reserved2; /* 0x05 */
- U8 SATASlumberTimeout; /* 0x06 */
- U8 Reserved3; /* 0x07 */
- U8 SASPartialTimeout; /* 0x08 */
- U8 Reserved4; /* 0x09 */
- U8 SASSlumberTimeout; /* 0x0A */
- U8 Reserved5; /* 0x0B */
-} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
- MPI2_POINTER PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
- Mpi2SasIOUnit5PhyPmSettings_t, MPI2_POINTER pMpi2SasIOUnit5PhyPmSettings_t;
-
-/* defines for ControlFlags field */
-#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08)
-#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04)
-#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02)
-#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01)
-
-/* defines for PortWidthModeGroup field */
-#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF)
-
-/* defines for InactivityTimerExponent field */
-#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000)
-#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12)
-#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700)
-#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8)
-#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070)
-#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4)
-#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007)
-#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0)
-
-#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7)
-#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6)
-#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5)
-#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4)
-#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3)
-#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2)
-#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1)
-#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0)
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhys at runtime.
- */
-#ifndef MPI2_SAS_IOUNIT5_PHY_MAX
-#define MPI2_SAS_IOUNIT5_PHY_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U8 NumPhys; /* 0x08 */
- U8 Reserved1; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U32 Reserved3; /* 0x0C */
- MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS SASPhyPowerManagementSettings
- [MPI2_SAS_IOUNIT5_PHY_MAX]; /* 0x10 */
-} MPI2_CONFIG_PAGE_SASIOUNIT_5,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
- Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t;
-
-#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01)
-
-
-/* SAS IO Unit Page 6 */
-
-typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS {
- U8 CurrentStatus; /* 0x00 */
- U8 CurrentModulation; /* 0x01 */
- U8 CurrentUtilization; /* 0x02 */
- U8 Reserved1; /* 0x03 */
- U32 Reserved2; /* 0x04 */
-} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
- MPI2_POINTER PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
- Mpi2SasIOUnit6PortWidthModGroupStatus_t,
- MPI2_POINTER pMpi2SasIOUnit6PortWidthModGroupStatus_t;
-
-/* defines for CurrentStatus field */
-#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00)
-#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01)
-#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02)
-#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03)
-#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04)
-#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05)
-#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06)
-#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07)
-
-/* defines for CurrentModulation field */
-#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00)
-#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01)
-#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02)
-#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03)
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumGroups at runtime.
- */
-#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX
-#define MPI2_SAS_IOUNIT6_GROUP_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U32 Reserved2; /* 0x0C */
- U8 NumGroups; /* 0x10 */
- U8 Reserved3; /* 0x11 */
- U16 Reserved4; /* 0x12 */
- MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS
- PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /* 0x14 */
-} MPI2_CONFIG_PAGE_SASIOUNIT_6,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6,
- Mpi2SasIOUnitPage6_t, MPI2_POINTER pMpi2SasIOUnitPage6_t;
-
-#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00)
-
-
-/* SAS IO Unit Page 7 */
-
-typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS {
- U8 Flags; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
- U8 Threshold75Pct; /* 0x04 */
- U8 Threshold50Pct; /* 0x05 */
- U8 Threshold25Pct; /* 0x06 */
- U8 Reserved3; /* 0x07 */
-} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
- MPI2_POINTER PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
- Mpi2SasIOUnit7PortWidthModGroupSettings_t,
- MPI2_POINTER pMpi2SasIOUnit7PortWidthModGroupSettings_t;
-
-/* defines for Flags field */
-#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01)
-
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumGroups at runtime.
- */
-#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX
-#define MPI2_SAS_IOUNIT7_GROUP_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U8 SamplingInterval; /* 0x08 */
- U8 WindowLength; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U32 Reserved2; /* 0x0C */
- U32 Reserved3; /* 0x10 */
- U8 NumGroups; /* 0x14 */
- U8 Reserved4; /* 0x15 */
- U16 Reserved5; /* 0x16 */
- MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS
- PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX]; /* 0x18 */
-} MPI2_CONFIG_PAGE_SASIOUNIT_7,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7,
- Mpi2SasIOUnitPage7_t, MPI2_POINTER pMpi2SasIOUnitPage7_t;
-
-#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00)
-
-
-/* SAS IO Unit Page 8 */
-
-typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U32 PowerManagementCapabilities;/* 0x0C */
- U32 Reserved2; /* 0x10 */
-} MPI2_CONFIG_PAGE_SASIOUNIT_8,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8,
- Mpi2SasIOUnitPage8_t, MPI2_POINTER pMpi2SasIOUnitPage8_t;
-
-#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00)
-
-/* defines for PowerManagementCapabilities field */
-#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x000001000)
-#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x000000800)
-#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x000000400)
-#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x000000200)
-#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x000000100)
-#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x000000010)
-#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x000000008)
-#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x000000004)
-#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x000000002)
-#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x000000001)
-
-
-
-
-/****************************************************************************
-* SAS Expander Config Pages
-****************************************************************************/
-
-/* SAS Expander Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U8 PhysicalPort; /* 0x08 */
- U8 ReportGenLength; /* 0x09 */
- U16 EnclosureHandle; /* 0x0A */
- U64 SASAddress; /* 0x0C */
- U32 DiscoveryStatus; /* 0x14 */
- U16 DevHandle; /* 0x18 */
- U16 ParentDevHandle; /* 0x1A */
- U16 ExpanderChangeCount; /* 0x1C */
- U16 ExpanderRouteIndexes; /* 0x1E */
- U8 NumPhys; /* 0x20 */
- U8 SASLevel; /* 0x21 */
- U16 Flags; /* 0x22 */
- U16 STPBusInactivityTimeLimit; /* 0x24 */
- U16 STPMaxConnectTimeLimit; /* 0x26 */
- U16 STP_SMP_NexusLossTime; /* 0x28 */
- U16 MaxNumRoutedSasAddresses; /* 0x2A */
- U64 ActiveZoneManagerSASAddress;/* 0x2C */
- U16 ZoneLockInactivityLimit; /* 0x34 */
- U16 Reserved1; /* 0x36 */
- U8 TimeToReducedFunc; /* 0x38 */
- U8 InitialTimeToReducedFunc; /* 0x39 */
- U8 MaxReducedFuncTime; /* 0x3A */
- U8 Reserved2; /* 0x3B */
-} MPI2_CONFIG_PAGE_EXPANDER_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_0,
- Mpi2ExpanderPage0_t, MPI2_POINTER pMpi2ExpanderPage0_t;
-
-#define MPI2_SASEXPANDER0_PAGEVERSION (0x06)
-
-/* values for SAS Expander Page 0 DiscoveryStatus field */
-#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
-#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
-#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000)
-#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
-#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000)
-#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
-#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
-#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000)
-#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
-#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800)
-#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400)
-#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200)
-#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100)
-#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080)
-#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040)
-#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020)
-#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010)
-#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004)
-#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002)
-#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001)
-
-/* values for SAS Expander Page 0 Flags field */
-#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000)
-#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000)
-#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800)
-#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400)
-#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200)
-#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100)
-#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080)
-#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010)
-#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004)
-#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002)
-#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001)
-
-
-/* SAS Expander Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U8 PhysicalPort; /* 0x08 */
- U8 Reserved1; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U8 NumPhys; /* 0x0C */
- U8 Phy; /* 0x0D */
- U16 NumTableEntriesProgrammed; /* 0x0E */
- U8 ProgrammedLinkRate; /* 0x10 */
- U8 HwLinkRate; /* 0x11 */
- U16 AttachedDevHandle; /* 0x12 */
- U32 PhyInfo; /* 0x14 */
- U32 AttachedDeviceInfo; /* 0x18 */
- U16 ExpanderDevHandle; /* 0x1C */
- U8 ChangeCount; /* 0x1E */
- U8 NegotiatedLinkRate; /* 0x1F */
- U8 PhyIdentifier; /* 0x20 */
- U8 AttachedPhyIdentifier; /* 0x21 */
- U8 Reserved3; /* 0x22 */
- U8 DiscoveryInfo; /* 0x23 */
- U32 AttachedPhyInfo; /* 0x24 */
- U8 ZoneGroup; /* 0x28 */
- U8 SelfConfigStatus; /* 0x29 */
- U16 Reserved4; /* 0x2A */
-} MPI2_CONFIG_PAGE_EXPANDER_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_1,
- Mpi2ExpanderPage1_t, MPI2_POINTER pMpi2ExpanderPage1_t;
-
-#define MPI2_SASEXPANDER1_PAGEVERSION (0x02)
-
-/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
-
-/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
-
-/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */
-
-/* see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines used for the AttachedDeviceInfo field */
-
-/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
-
-/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
-
-/* values for SAS Expander Page 1 DiscoveryInfo field */
-#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04)
-#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02)
-#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01)
-
-
-/****************************************************************************
-* SAS Device Config Pages
-****************************************************************************/
-
-/* SAS Device Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U16 Slot; /* 0x08 */
- U16 EnclosureHandle; /* 0x0A */
- U64 SASAddress; /* 0x0C */
- U16 ParentDevHandle; /* 0x14 */
- U8 PhyNum; /* 0x16 */
- U8 AccessStatus; /* 0x17 */
- U16 DevHandle; /* 0x18 */
- U8 AttachedPhyIdentifier; /* 0x1A */
- U8 ZoneGroup; /* 0x1B */
- U32 DeviceInfo; /* 0x1C */
- U16 Flags; /* 0x20 */
- U8 PhysicalPort; /* 0x22 */
- U8 MaxPortConnections; /* 0x23 */
- U64 DeviceName; /* 0x24 */
- U8 PortGroups; /* 0x2C */
- U8 DmaGroup; /* 0x2D */
- U8 ControlGroup; /* 0x2E */
- U8 Reserved1; /* 0x2F */
- U32 Reserved2; /* 0x30 */
- U32 Reserved3; /* 0x34 */
-} MPI2_CONFIG_PAGE_SAS_DEV_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_0,
- Mpi2SasDevicePage0_t, MPI2_POINTER pMpi2SasDevicePage0_t;
-
-#define MPI2_SASDEVICE0_PAGEVERSION (0x08)
-
-/* values for SAS Device Page 0 AccessStatus field */
-#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00)
-#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01)
-#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02)
-#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03)
-#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04)
-#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05)
-#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06)
-#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07)
-/* specific values for SATA Init failures */
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19)
-#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F)
-
-/* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */
-
-/* values for SAS Device Page 0 Flags field */
-#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000)
-#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800)
-#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400)
-#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200)
-#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100)
-#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080)
-#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040)
-#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020)
-#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010)
-#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008)
-#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001)
-
-
-/* SAS Device Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U64 SASAddress; /* 0x0C */
- U32 Reserved2; /* 0x14 */
- U16 DevHandle; /* 0x18 */
- U16 Reserved3; /* 0x1A */
- U8 InitialRegDeviceFIS[20];/* 0x1C */
-} MPI2_CONFIG_PAGE_SAS_DEV_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_1,
- Mpi2SasDevicePage1_t, MPI2_POINTER pMpi2SasDevicePage1_t;
-
-#define MPI2_SASDEVICE1_PAGEVERSION (0x01)
-
-
-/****************************************************************************
-* SAS PHY Config Pages
-****************************************************************************/
-
-/* SAS PHY Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U16 OwnerDevHandle; /* 0x08 */
- U16 Reserved1; /* 0x0A */
- U16 AttachedDevHandle; /* 0x0C */
- U8 AttachedPhyIdentifier; /* 0x0E */
- U8 Reserved2; /* 0x0F */
- U32 AttachedPhyInfo; /* 0x10 */
- U8 ProgrammedLinkRate; /* 0x14 */
- U8 HwLinkRate; /* 0x15 */
- U8 ChangeCount; /* 0x16 */
- U8 Flags; /* 0x17 */
- U32 PhyInfo; /* 0x18 */
- U8 NegotiatedLinkRate; /* 0x1C */
- U8 Reserved3; /* 0x1D */
- U16 Reserved4; /* 0x1E */
-} MPI2_CONFIG_PAGE_SAS_PHY_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_0,
- Mpi2SasPhyPage0_t, MPI2_POINTER pMpi2SasPhyPage0_t;
-
-#define MPI2_SASPHY0_PAGEVERSION (0x03)
-
-/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
-
-/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
-
-/* values for SAS PHY Page 0 Flags field */
-#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01)
-
-/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
-
-/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
-
-/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */
-
-
-/* SAS PHY Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U32 InvalidDwordCount; /* 0x0C */
- U32 RunningDisparityErrorCount; /* 0x10 */
- U32 LossDwordSynchCount; /* 0x14 */
- U32 PhyResetProblemCount; /* 0x18 */
-} MPI2_CONFIG_PAGE_SAS_PHY_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_1,
- Mpi2SasPhyPage1_t, MPI2_POINTER pMpi2SasPhyPage1_t;
-
-#define MPI2_SASPHY1_PAGEVERSION (0x01)
-
-
-/* SAS PHY Page 2 */
-
-typedef struct _MPI2_SASPHY2_PHY_EVENT {
- U8 PhyEventCode; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
- U32 PhyEventInfo; /* 0x04 */
-} MPI2_SASPHY2_PHY_EVENT, MPI2_POINTER PTR_MPI2_SASPHY2_PHY_EVENT,
- Mpi2SasPhy2PhyEvent_t, MPI2_POINTER pMpi2SasPhy2PhyEvent_t;
-
-/* use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */
-
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhyEvents at runtime.
- */
-#ifndef MPI2_SASPHY2_PHY_EVENT_MAX
-#define MPI2_SASPHY2_PHY_EVENT_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U8 NumPhyEvents; /* 0x0C */
- U8 Reserved2; /* 0x0D */
- U16 Reserved3; /* 0x0E */
- MPI2_SASPHY2_PHY_EVENT PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX];
- /* 0x10 */
-} MPI2_CONFIG_PAGE_SAS_PHY_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_2,
- Mpi2SasPhyPage2_t, MPI2_POINTER pMpi2SasPhyPage2_t;
-
-#define MPI2_SASPHY2_PAGEVERSION (0x00)
-
-
-/* SAS PHY Page 3 */
-
-typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG {
- U8 PhyEventCode; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
- U8 CounterType; /* 0x04 */
- U8 ThresholdWindow; /* 0x05 */
- U8 TimeUnits; /* 0x06 */
- U8 Reserved3; /* 0x07 */
- U32 EventThreshold; /* 0x08 */
- U16 ThresholdFlags; /* 0x0C */
- U16 Reserved4; /* 0x0E */
-} MPI2_SASPHY3_PHY_EVENT_CONFIG, MPI2_POINTER PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG,
- Mpi2SasPhy3PhyEventConfig_t, MPI2_POINTER pMpi2SasPhy3PhyEventConfig_t;
-
-/* values for PhyEventCode field */
-#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00)
-#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01)
-#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02)
-#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03)
-#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04)
-#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05)
-#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06)
-#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20)
-#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21)
-#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22)
-#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23)
-#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24)
-#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25)
-#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26)
-#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27)
-#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28)
-#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29)
-#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A)
-#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B)
-#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C)
-#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D)
-#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E)
-#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40)
-#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41)
-#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42)
-#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43)
-#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44)
-#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45)
-#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50)
-#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51)
-#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52)
-#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60)
-#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61)
-#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63)
-#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0)
-#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1)
-#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2)
-
-/* values for the CounterType field */
-#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00)
-#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01)
-#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02)
-
-/* values for the TimeUnits field */
-#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00)
-#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01)
-#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02)
-#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03)
-
-/* values for the ThresholdFlags field */
-#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002)
-#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001)
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumPhyEvents at runtime.
- */
-#ifndef MPI2_SASPHY3_PHY_EVENT_MAX
-#define MPI2_SASPHY3_PHY_EVENT_MAX (1)
-#endif
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U8 NumPhyEvents; /* 0x0C */
- U8 Reserved2; /* 0x0D */
- U16 Reserved3; /* 0x0E */
- MPI2_SASPHY3_PHY_EVENT_CONFIG PhyEventConfig
- [MPI2_SASPHY3_PHY_EVENT_MAX]; /* 0x10 */
-} MPI2_CONFIG_PAGE_SAS_PHY_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_3,
- Mpi2SasPhyPage3_t, MPI2_POINTER pMpi2SasPhyPage3_t;
-
-#define MPI2_SASPHY3_PAGEVERSION (0x00)
-
-
-/* SAS PHY Page 4 */
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U16 Reserved1; /* 0x08 */
- U8 Reserved2; /* 0x0A */
- U8 Flags; /* 0x0B */
- U8 InitialFrame[28]; /* 0x0C */
-} MPI2_CONFIG_PAGE_SAS_PHY_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_4,
- Mpi2SasPhyPage4_t, MPI2_POINTER pMpi2SasPhyPage4_t;
-
-#define MPI2_SASPHY4_PAGEVERSION (0x00)
-
-/* values for the Flags field */
-#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02)
-#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01)
-
-
-
-
-/****************************************************************************
-* SAS Port Config Pages
-****************************************************************************/
-
-/* SAS Port Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U8 PortNumber; /* 0x08 */
- U8 PhysicalPort; /* 0x09 */
- U8 PortWidth; /* 0x0A */
- U8 PhysicalPortWidth; /* 0x0B */
- U8 ZoneGroup; /* 0x0C */
- U8 Reserved1; /* 0x0D */
- U16 Reserved2; /* 0x0E */
- U64 SASAddress; /* 0x10 */
- U32 DeviceInfo; /* 0x18 */
- U32 Reserved3; /* 0x1C */
- U32 Reserved4; /* 0x20 */
-} MPI2_CONFIG_PAGE_SAS_PORT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PORT_0,
- Mpi2SasPortPage0_t, MPI2_POINTER pMpi2SasPortPage0_t;
-
-#define MPI2_SASPORT0_PAGEVERSION (0x00)
-
-/* see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */
-
-
-/****************************************************************************
-* SAS Enclosure Config Pages
-****************************************************************************/
-
-/* SAS Enclosure Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U64 EnclosureLogicalID; /* 0x0C */
- U16 Flags; /* 0x14 */
- U16 EnclosureHandle; /* 0x16 */
- U16 NumSlots; /* 0x18 */
- U16 StartSlot; /* 0x1A */
- U16 Reserved2; /* 0x1C */
- U16 SEPDevHandle; /* 0x1E */
- U32 Reserved3; /* 0x20 */
- U32 Reserved4; /* 0x24 */
-} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
- Mpi2SasEnclosurePage0_t, MPI2_POINTER pMpi2SasEnclosurePage0_t;
-
-#define MPI2_SASENCLOSURE0_PAGEVERSION (0x03)
-
-/* values for SAS Enclosure Page 0 Flags field */
-#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F)
-#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000)
-#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001)
-#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002)
-#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003)
-#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004)
-#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005)
-
-
-/****************************************************************************
-* Log Config Page
-****************************************************************************/
-
-/* Log Page 0 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumLogEntries at runtime.
- */
-#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES
-#define MPI2_LOG_0_NUM_LOG_ENTRIES (1)
-#endif
-
-#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C)
-
-typedef struct _MPI2_LOG_0_ENTRY
-{
- U64 TimeStamp; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U16 LogSequence; /* 0x0C */
- U16 LogEntryQualifier; /* 0x0E */
- U8 VP_ID; /* 0x10 */
- U8 VF_ID; /* 0x11 */
- U16 Reserved2; /* 0x12 */
- U8 LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/* 0x14 */
-} MPI2_LOG_0_ENTRY, MPI2_POINTER PTR_MPI2_LOG_0_ENTRY,
- Mpi2Log0Entry_t, MPI2_POINTER pMpi2Log0Entry_t;
-
-/* values for Log Page 0 LogEntry LogEntryQualifier field */
-#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000)
-#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001)
-#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002)
-#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000)
-#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF)
-
-typedef struct _MPI2_CONFIG_PAGE_LOG_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U32 Reserved2; /* 0x0C */
- U16 NumLogEntries; /* 0x10 */
- U16 Reserved3; /* 0x12 */
- MPI2_LOG_0_ENTRY LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /* 0x14 */
-} MPI2_CONFIG_PAGE_LOG_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_LOG_0,
- Mpi2LogPage0_t, MPI2_POINTER pMpi2LogPage0_t;
-
-#define MPI2_LOG_0_PAGEVERSION (0x02)
-
-
-/****************************************************************************
-* RAID Config Page
-****************************************************************************/
-
-/* RAID Page 0 */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check the value returned for NumElements at runtime.
- */
-#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS
-#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1)
-#endif
-
-typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT
-{
- U16 ElementFlags; /* 0x00 */
- U16 VolDevHandle; /* 0x02 */
- U8 HotSparePool; /* 0x04 */
- U8 PhysDiskNum; /* 0x05 */
- U16 PhysDiskDevHandle; /* 0x06 */
-} MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
- MPI2_POINTER PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
- Mpi2RaidConfig0ConfigElement_t, MPI2_POINTER pMpi2RaidConfig0ConfigElement_t;
-
-/* values for the ElementFlags field */
-#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F)
-#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000)
-#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001)
-#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002)
-#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003)
-
-
-typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U8 NumHotSpares; /* 0x08 */
- U8 NumPhysDisks; /* 0x09 */
- U8 NumVolumes; /* 0x0A */
- U8 ConfigNum; /* 0x0B */
- U32 Flags; /* 0x0C */
- U8 ConfigGUID[24]; /* 0x10 */
- U32 Reserved1; /* 0x28 */
- U8 NumElements; /* 0x2C */
- U8 Reserved2; /* 0x2D */
- U16 Reserved3; /* 0x2E */
- MPI2_RAIDCONFIG0_CONFIG_ELEMENT ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /* 0x30 */
-} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
- Mpi2RaidConfigurationPage0_t, MPI2_POINTER pMpi2RaidConfigurationPage0_t;
-
-#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00)
-
-/* values for RAID Configuration Page 0 Flags field */
-#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001)
-
-
-/****************************************************************************
-* Driver Persistent Mapping Config Pages
-****************************************************************************/
-
-/* Driver Persistent Mapping Page 0 */
-
-typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY
-{
- U64 PhysicalIdentifier; /* 0x00 */
- U16 MappingInformation; /* 0x08 */
- U16 DeviceIndex; /* 0x0A */
- U32 PhysicalBitsMapping; /* 0x0C */
- U32 Reserved1; /* 0x10 */
-} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
- Mpi2DriverMap0Entry_t, MPI2_POINTER pMpi2DriverMap0Entry_t;
-
-typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0
-{
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /* 0x08 */
-} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
- Mpi2DriverMappingPage0_t, MPI2_POINTER pMpi2DriverMappingPage0_t;
-
-#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00)
-
-/* values for Driver Persistent Mapping Page 0 MappingInformation field */
-#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0)
-#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4)
-#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F)
-
-
-/****************************************************************************
-* Ethernet Config Pages
-****************************************************************************/
-
-/* Ethernet Page 0 */
-
-/* IP address (union of IPv4 and IPv6) */
-typedef union _MPI2_ETHERNET_IP_ADDR {
- U32 IPv4Addr;
- U32 IPv6Addr[4];
-} MPI2_ETHERNET_IP_ADDR, MPI2_POINTER PTR_MPI2_ETHERNET_IP_ADDR,
- Mpi2EthernetIpAddr_t, MPI2_POINTER pMpi2EthernetIpAddr_t;
-
-#define MPI2_ETHERNET_HOST_NAME_LENGTH (32)
-
-typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U8 NumInterfaces; /* 0x08 */
- U8 Reserved0; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U32 Status; /* 0x0C */
- U8 MediaState; /* 0x10 */
- U8 Reserved2; /* 0x11 */
- U16 Reserved3; /* 0x12 */
- U8 MacAddress[6]; /* 0x14 */
- U8 Reserved4; /* 0x1A */
- U8 Reserved5; /* 0x1B */
- MPI2_ETHERNET_IP_ADDR IpAddress; /* 0x1C */
- MPI2_ETHERNET_IP_ADDR SubnetMask; /* 0x2C */
- MPI2_ETHERNET_IP_ADDR GatewayIpAddress; /* 0x3C */
- MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /* 0x4C */
- MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /* 0x5C */
- MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /* 0x6C */
- U8 HostName
- [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */
-} MPI2_CONFIG_PAGE_ETHERNET_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_0,
- Mpi2EthernetPage0_t, MPI2_POINTER pMpi2EthernetPage0_t;
-
-#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00)
-
-/* values for Ethernet Page 0 Status field */
-#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000)
-#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000)
-#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000)
-#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100)
-#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080)
-#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040)
-#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020)
-#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010)
-#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008)
-#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004)
-#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002)
-#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001)
-
-/* values for Ethernet Page 0 MediaState field */
-#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80)
-#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00)
-#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80)
-
-#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07)
-#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00)
-#define MPI2_ETHPG0_MS_10MBIT (0x01)
-#define MPI2_ETHPG0_MS_100MBIT (0x02)
-#define MPI2_ETHPG0_MS_1GBIT (0x03)
-
-
-/* Ethernet Page 1 */
-
-typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 {
- MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
- U32 Reserved0; /* 0x08 */
- U32 Flags; /* 0x0C */
- U8 MediaState; /* 0x10 */
- U8 Reserved1; /* 0x11 */
- U16 Reserved2; /* 0x12 */
- U8 MacAddress[6]; /* 0x14 */
- U8 Reserved3; /* 0x1A */
- U8 Reserved4; /* 0x1B */
- MPI2_ETHERNET_IP_ADDR StaticIpAddress; /* 0x1C */
- MPI2_ETHERNET_IP_ADDR StaticSubnetMask; /* 0x2C */
- MPI2_ETHERNET_IP_ADDR StaticGatewayIpAddress; /* 0x3C */
- MPI2_ETHERNET_IP_ADDR StaticDNS1IpAddress; /* 0x4C */
- MPI2_ETHERNET_IP_ADDR StaticDNS2IpAddress; /* 0x5C */
- U32 Reserved5; /* 0x6C */
- U32 Reserved6; /* 0x70 */
- U32 Reserved7; /* 0x74 */
- U32 Reserved8; /* 0x78 */
- U8 HostName
- [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */
-} MPI2_CONFIG_PAGE_ETHERNET_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_1,
- Mpi2EthernetPage1_t, MPI2_POINTER pMpi2EthernetPage1_t;
-
-#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00)
-
-/* values for Ethernet Page 1 Flags field */
-#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100)
-#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080)
-#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040)
-#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020)
-#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010)
-#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008)
-#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004)
-#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002)
-#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001)
-
-/* values for Ethernet Page 1 MediaState field */
-#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80)
-#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00)
-#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80)
-
-#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07)
-#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00)
-#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01)
-#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02)
-#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03)
-
-
-#endif
-
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h
deleted file mode 100644
index 165454d52591..000000000000
--- a/drivers/scsi/mpt2sas/mpi/mpi2_init.h
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Copyright (c) 2000-2010 LSI Corporation.
- *
- *
- * Name: mpi2_init.h
- * Title: MPI SCSI initiator mode messages and structures
- * Creation Date: June 23, 2006
- *
- * mpi2_init.h Version: 02.00.10
- *
- * Version History
- * ---------------
- *
- * Date Version Description
- * -------- -------- ------------------------------------------------------
- * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
- * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t.
- * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines.
- * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention.
- * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY.
- * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t.
- * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO
- * Control field Task Attribute flags.
- * Moved LUN field defines to mpi2.h because they are
- * common to many structures.
- * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to
- * Query Asynchronous Event.
- * Defined two new bits in the SlotStatus field of the SCSI
- * Enclosure Processor Request and Reply.
- * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for
- * both SCSI IO Error Reply and SCSI Task Management Reply.
- * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
- * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
- * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it.
- * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request.
- * --------------------------------------------------------------------------
- */
-
-#ifndef MPI2_INIT_H
-#define MPI2_INIT_H
-
-/*****************************************************************************
-*
-* SCSI Initiator Messages
-*
-*****************************************************************************/
-
-/****************************************************************************
-* SCSI IO messages and associated structures
-****************************************************************************/
-
-typedef struct
-{
- U8 CDB[20]; /* 0x00 */
- U32 PrimaryReferenceTag; /* 0x14 */
- U16 PrimaryApplicationTag; /* 0x18 */
- U16 PrimaryApplicationTagMask; /* 0x1A */
- U32 TransferLength; /* 0x1C */
-} MPI2_SCSI_IO_CDB_EEDP32, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_EEDP32,
- Mpi2ScsiIoCdbEedp32_t, MPI2_POINTER pMpi2ScsiIoCdbEedp32_t;
-
-typedef union
-{
- U8 CDB32[32];
- MPI2_SCSI_IO_CDB_EEDP32 EEDP32;
- MPI2_SGE_SIMPLE_UNION SGE;
-} MPI2_SCSI_IO_CDB_UNION, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_UNION,
- Mpi2ScsiIoCdb_t, MPI2_POINTER pMpi2ScsiIoCdb_t;
-
-/* SCSI IO Request Message */
-typedef struct _MPI2_SCSI_IO_REQUEST
-{
- U16 DevHandle; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved1; /* 0x04 */
- U8 Reserved2; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved3; /* 0x0A */
- U32 SenseBufferLowAddress; /* 0x0C */
- U16 SGLFlags; /* 0x10 */
- U8 SenseBufferLength; /* 0x12 */
- U8 Reserved4; /* 0x13 */
- U8 SGLOffset0; /* 0x14 */
- U8 SGLOffset1; /* 0x15 */
- U8 SGLOffset2; /* 0x16 */
- U8 SGLOffset3; /* 0x17 */
- U32 SkipCount; /* 0x18 */
- U32 DataLength; /* 0x1C */
- U32 BidirectionalDataLength; /* 0x20 */
- U16 IoFlags; /* 0x24 */
- U16 EEDPFlags; /* 0x26 */
- U32 EEDPBlockSize; /* 0x28 */
- U32 SecondaryReferenceTag; /* 0x2C */
- U16 SecondaryApplicationTag; /* 0x30 */
- U16 ApplicationTagTranslationMask; /* 0x32 */
- U8 LUN[8]; /* 0x34 */
- U32 Control; /* 0x3C */
- MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */
-
-#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /* typically this is left undefined */
- MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion;
-#endif
-
- MPI2_SGE_IO_UNION SGL; /* 0x60 */
-
-} MPI2_SCSI_IO_REQUEST, MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST,
- Mpi2SCSIIORequest_t, MPI2_POINTER pMpi2SCSIIORequest_t;
-
-/* SCSI IO MsgFlags bits */
-
-/* MsgFlags for SenseBufferAddressSpace */
-#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C)
-#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00)
-#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04)
-#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08)
-#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C)
-
-/* SCSI IO SGLFlags bits */
-
-/* base values for Data Location Address Space */
-#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C)
-#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00)
-#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04)
-#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08)
-#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C)
-
-/* base values for Type */
-#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03)
-#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00)
-#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01)
-#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02)
-
-/* shift values for each sub-field */
-#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12)
-#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8)
-#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4)
-#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0)
-
-/* SCSI IO IoFlags bits */
-
-/* Large CDB Address Space */
-#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000)
-#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000)
-#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000)
-#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000)
-#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000)
-
-#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000)
-#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800)
-#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400)
-#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200)
-#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF)
-
-/* SCSI IO EEDPFlags bits */
-
-#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000)
-#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000)
-#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000)
-#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000)
-
-#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400)
-#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200)
-#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100)
-
-#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008)
-
-#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007)
-#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000)
-#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001)
-#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002)
-#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003)
-#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004)
-#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006)
-#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007)
-
-/* SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */
-
-/* SCSI IO Control bits */
-#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000)
-#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26)
-
-#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000)
-#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000)
-#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000)
-#define MPI2_SCSIIO_CONTROL_READ (0x02000000)
-#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000)
-
-#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800)
-#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11)
-
-#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700)
-#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000)
-#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100)
-#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200)
-#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400)
-
-#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0)
-#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000)
-#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040)
-#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080)
-
-
-/* SCSI IO Error Reply Message */
-typedef struct _MPI2_SCSI_IO_REPLY
-{
- U16 DevHandle; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved1; /* 0x04 */
- U8 Reserved2; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved3; /* 0x0A */
- U8 SCSIStatus; /* 0x0C */
- U8 SCSIState; /* 0x0D */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U32 TransferCount; /* 0x14 */
- U32 SenseCount; /* 0x18 */
- U32 ResponseInfo; /* 0x1C */
- U16 TaskTag; /* 0x20 */
- U16 Reserved4; /* 0x22 */
- U32 BidirectionalTransferCount; /* 0x24 */
- U32 Reserved5; /* 0x28 */
- U32 Reserved6; /* 0x2C */
-} MPI2_SCSI_IO_REPLY, MPI2_POINTER PTR_MPI2_SCSI_IO_REPLY,
- Mpi2SCSIIOReply_t, MPI2_POINTER pMpi2SCSIIOReply_t;
-
-/* SCSI IO Reply SCSIStatus values (SAM-4 status codes) */
-
-#define MPI2_SCSI_STATUS_GOOD (0x00)
-#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02)
-#define MPI2_SCSI_STATUS_CONDITION_MET (0x04)
-#define MPI2_SCSI_STATUS_BUSY (0x08)
-#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10)
-#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
-#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
-#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /* obsolete */
-#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28)
-#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30)
-#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40)
-
-/* SCSI IO Reply SCSIState flags */
-
-#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10)
-#define MPI2_SCSI_STATE_TERMINATED (0x08)
-#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04)
-#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02)
-#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01)
-
-/* masks and shifts for the ResponseInfo field */
-
-#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF)
-#define MPI2_SCSI_RI_SHIFT_REASONCODE (0)
-
-#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF)
-
-
-/****************************************************************************
-* SCSI Task Management messages
-****************************************************************************/
-
-/* SCSI Task Management Request Message */
-typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST
-{
- U16 DevHandle; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U8 Reserved1; /* 0x04 */
- U8 TaskType; /* 0x05 */
- U8 Reserved2; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved3; /* 0x0A */
- U8 LUN[8]; /* 0x0C */
- U32 Reserved4[7]; /* 0x14 */
- U16 TaskMID; /* 0x30 */
- U16 Reserved5; /* 0x32 */
-} MPI2_SCSI_TASK_MANAGE_REQUEST,
- MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REQUEST,
- Mpi2SCSITaskManagementRequest_t,
- MPI2_POINTER pMpi2SCSITaskManagementRequest_t;
-
-/* TaskType values */
-
-#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01)
-#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02)
-#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
-#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
-#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06)
-#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07)
-#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08)
-#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09)
-#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A)
-
-/* obsolete TaskType name */
-#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \
- (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT)
-
-/* MsgFlags bits */
-
-#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18)
-#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00)
-#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08)
-#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10)
-
-#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01)
-
-
-
-/* SCSI Task Management Reply Message */
-typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY
-{
- U16 DevHandle; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U8 ResponseCode; /* 0x04 */
- U8 TaskType; /* 0x05 */
- U8 Reserved1; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U16 Reserved3; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U32 TerminationCount; /* 0x14 */
- U32 ResponseInfo; /* 0x18 */
-} MPI2_SCSI_TASK_MANAGE_REPLY,
- MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REPLY,
- Mpi2SCSITaskManagementReply_t, MPI2_POINTER pMpi2SCSIManagementReply_t;
-
-/* ResponseCode values */
-
-#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00)
-#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02)
-#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04)
-#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05)
-#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08)
-#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09)
-#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A)
-#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80)
-
-/* masks and shifts for the ResponseInfo field */
-
-#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF)
-#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0)
-#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00)
-#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8)
-#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000)
-#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16)
-#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000)
-#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24)
-
-
-/****************************************************************************
-* SCSI Enclosure Processor messages
-****************************************************************************/
-
-/* SCSI Enclosure Processor Request Message */
-typedef struct _MPI2_SEP_REQUEST
-{
- U16 DevHandle; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U8 Action; /* 0x04 */
- U8 Flags; /* 0x05 */
- U8 Reserved1; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U32 SlotStatus; /* 0x0C */
- U32 Reserved3; /* 0x10 */
- U32 Reserved4; /* 0x14 */
- U32 Reserved5; /* 0x18 */
- U16 Slot; /* 0x1C */
- U16 EnclosureHandle; /* 0x1E */
-} MPI2_SEP_REQUEST, MPI2_POINTER PTR_MPI2_SEP_REQUEST,
- Mpi2SepRequest_t, MPI2_POINTER pMpi2SepRequest_t;
-
-/* Action defines */
-#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00)
-#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01)
-
-/* Flags defines */
-#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00)
-#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01)
-
-/* SlotStatus defines */
-#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000)
-#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
-#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
-#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100)
-#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080)
-#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
-#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
-#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
-#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004)
-#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002)
-#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001)
-
-
-/* SCSI Enclosure Processor Reply Message */
-typedef struct _MPI2_SEP_REPLY
-{
- U16 DevHandle; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U8 Action; /* 0x04 */
- U8 Flags; /* 0x05 */
- U8 Reserved1; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U16 Reserved3; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U32 SlotStatus; /* 0x14 */
- U32 Reserved4; /* 0x18 */
- U16 Slot; /* 0x1C */
- U16 EnclosureHandle; /* 0x1E */
-} MPI2_SEP_REPLY, MPI2_POINTER PTR_MPI2_SEP_REPLY,
- Mpi2SepReply_t, MPI2_POINTER pMpi2SepReply_t;
-
-/* SlotStatus defines */
-#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000)
-#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
-#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
-#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100)
-#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080)
-#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
-#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
-#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
-#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004)
-#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002)
-#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001)
-
-
-#endif
-
-
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
deleted file mode 100644
index 761cbdb8a033..000000000000
--- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
+++ /dev/null
@@ -1,1530 +0,0 @@
-/*
- * Copyright (c) 2000-2010 LSI Corporation.
- *
- *
- * Name: mpi2_ioc.h
- * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
- * Creation Date: October 11, 2006
- *
- * mpi2_ioc.h Version: 02.00.15
- *
- * Version History
- * ---------------
- *
- * Date Version Description
- * -------- -------- ------------------------------------------------------
- * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
- * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to
- * MaxTargets.
- * Added TotalImageSize field to FWDownload Request.
- * Added reserved words to FWUpload Request.
- * 06-26-07 02.00.02 Added IR Configuration Change List Event.
- * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit
- * request and replaced it with
- * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth.
- * Replaced the MinReplyQueueDepth field of the IOCFacts
- * reply with MaxReplyDescriptorPostQueueDepth.
- * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum
- * depth for the Reply Descriptor Post Queue.
- * Added SASAddress field to Initiator Device Table
- * Overflow Event data.
- * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING
- * for SAS Initiator Device Status Change Event data.
- * Modified Reason Code defines for SAS Topology Change
- * List Event data, including adding a bit for PHY Vacant
- * status, and adding a mask for the Reason Code.
- * Added define for
- * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING.
- * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID.
- * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of
- * the IOCFacts Reply.
- * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
- * Moved MPI2_VERSION_UNION to mpi2.h.
- * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks
- * instead of enables, and added SASBroadcastPrimitiveMasks
- * field.
- * Added Log Entry Added Event and related structure.
- * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID.
- * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET.
- * Added MaxVolumes and MaxPersistentEntries fields to
- * IOCFacts reply.
- * Added ProtocalFlags and IOCCapabilities fields to
- * MPI2_FW_IMAGE_HEADER.
- * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT.
- * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to
- * a U16 (from a U32).
- * Removed extra 's' from EventMasks name.
- * 06-27-08 02.00.08 Fixed an offset in a comment.
- * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST.
- * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and
- * renamed MinReplyFrameSize to ReplyFrameSize.
- * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX.
- * Added two new RAIDOperation values for Integrated RAID
- * Operations Status Event data.
- * Added four new IR Configuration Change List Event data
- * ReasonCode values.
- * Added two new ReasonCode defines for SAS Device Status
- * Change Event data.
- * Added three new DiscoveryStatus bits for the SAS
- * Discovery event data.
- * Added Multiplexing Status Change bit to the PhyStatus
- * field of the SAS Topology Change List event data.
- * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY.
- * BootFlags are now product-specific.
- * Added defines for the indivdual signature bytes
- * for MPI2_INIT_IMAGE_FOOTER.
- * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define.
- * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR
- * define.
- * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE
- * define.
- * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define.
- * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define.
- * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define.
- * Added two new reason codes for SAS Device Status Change
- * Event.
- * Added new event: SAS PHY Counter.
- * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure.
- * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
- * Added new product id family for 2208.
- * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST.
- * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY.
- * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY.
- * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY.
- * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define.
- * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define.
- * Added Host Based Discovery Phy Event data.
- * Added defines for ProductID Product field
- * (MPI2_FW_HEADER_PID_).
- * Modified values for SAS ProductID Family
- * (MPI2_FW_HEADER_PID_FAMILY_).
- * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines.
- * Added PowerManagementControl Request structures and
- * defines.
- * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete.
- * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define.
- * --------------------------------------------------------------------------
- */
-
-#ifndef MPI2_IOC_H
-#define MPI2_IOC_H
-
-/*****************************************************************************
-*
-* IOC Messages
-*
-*****************************************************************************/
-
-/****************************************************************************
-* IOCInit message
-****************************************************************************/
-
-/* IOCInit Request message */
-typedef struct _MPI2_IOC_INIT_REQUEST
-{
- U8 WhoInit; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 MsgVersion; /* 0x0C */
- U16 HeaderVersion; /* 0x0E */
- U32 Reserved5; /* 0x10 */
- U16 Reserved6; /* 0x14 */
- U8 Reserved7; /* 0x16 */
- U8 HostMSIxVectors; /* 0x17 */
- U16 Reserved8; /* 0x18 */
- U16 SystemRequestFrameSize; /* 0x1A */
- U16 ReplyDescriptorPostQueueDepth; /* 0x1C */
- U16 ReplyFreeQueueDepth; /* 0x1E */
- U32 SenseBufferAddressHigh; /* 0x20 */
- U32 SystemReplyAddressHigh; /* 0x24 */
- U64 SystemRequestFrameBaseAddress; /* 0x28 */
- U64 ReplyDescriptorPostQueueAddress;/* 0x30 */
- U64 ReplyFreeQueueAddress; /* 0x38 */
- U64 TimeStamp; /* 0x40 */
-} MPI2_IOC_INIT_REQUEST, MPI2_POINTER PTR_MPI2_IOC_INIT_REQUEST,
- Mpi2IOCInitRequest_t, MPI2_POINTER pMpi2IOCInitRequest_t;
-
-/* WhoInit values */
-#define MPI2_WHOINIT_NOT_INITIALIZED (0x00)
-#define MPI2_WHOINIT_SYSTEM_BIOS (0x01)
-#define MPI2_WHOINIT_ROM_BIOS (0x02)
-#define MPI2_WHOINIT_PCI_PEER (0x03)
-#define MPI2_WHOINIT_HOST_DRIVER (0x04)
-#define MPI2_WHOINIT_MANUFACTURER (0x05)
-
-/* MsgVersion */
-#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00)
-#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8)
-#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF)
-#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0)
-
-/* HeaderVersion */
-#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00)
-#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8)
-#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF)
-#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0)
-
-/* minimum depth for the Reply Descriptor Post Queue */
-#define MPI2_RDPQ_DEPTH_MIN (16)
-
-
-/* IOCInit Reply message */
-typedef struct _MPI2_IOC_INIT_REPLY
-{
- U8 WhoInit; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_IOC_INIT_REPLY, MPI2_POINTER PTR_MPI2_IOC_INIT_REPLY,
- Mpi2IOCInitReply_t, MPI2_POINTER pMpi2IOCInitReply_t;
-
-
-/****************************************************************************
-* IOCFacts message
-****************************************************************************/
-
-/* IOCFacts Request message */
-typedef struct _MPI2_IOC_FACTS_REQUEST
-{
- U16 Reserved1; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
-} MPI2_IOC_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_IOC_FACTS_REQUEST,
- Mpi2IOCFactsRequest_t, MPI2_POINTER pMpi2IOCFactsRequest_t;
-
-
-/* IOCFacts Reply message */
-typedef struct _MPI2_IOC_FACTS_REPLY
-{
- U16 MsgVersion; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 HeaderVersion; /* 0x04 */
- U8 IOCNumber; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U16 IOCExceptions; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U8 MaxChainDepth; /* 0x14 */
- U8 WhoInit; /* 0x15 */
- U8 NumberOfPorts; /* 0x16 */
- U8 MaxMSIxVectors; /* 0x17 */
- U16 RequestCredit; /* 0x18 */
- U16 ProductID; /* 0x1A */
- U32 IOCCapabilities; /* 0x1C */
- MPI2_VERSION_UNION FWVersion; /* 0x20 */
- U16 IOCRequestFrameSize; /* 0x24 */
- U16 Reserved3; /* 0x26 */
- U16 MaxInitiators; /* 0x28 */
- U16 MaxTargets; /* 0x2A */
- U16 MaxSasExpanders; /* 0x2C */
- U16 MaxEnclosures; /* 0x2E */
- U16 ProtocolFlags; /* 0x30 */
- U16 HighPriorityCredit; /* 0x32 */
- U16 MaxReplyDescriptorPostQueueDepth; /* 0x34 */
- U8 ReplyFrameSize; /* 0x36 */
- U8 MaxVolumes; /* 0x37 */
- U16 MaxDevHandle; /* 0x38 */
- U16 MaxPersistentEntries; /* 0x3A */
- U16 MinDevHandle; /* 0x3C */
- U16 Reserved4; /* 0x3E */
-} MPI2_IOC_FACTS_REPLY, MPI2_POINTER PTR_MPI2_IOC_FACTS_REPLY,
- Mpi2IOCFactsReply_t, MPI2_POINTER pMpi2IOCFactsReply_t;
-
-/* MsgVersion */
-#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00)
-#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8)
-#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF)
-#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0)
-
-/* HeaderVersion */
-#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00)
-#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8)
-#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF)
-#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0)
-
-/* IOCExceptions */
-#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100)
-
-#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0)
-#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000)
-#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020)
-#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040)
-#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060)
-
-#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010)
-#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008)
-#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004)
-#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002)
-#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001)
-
-/* defines for WhoInit field are after the IOCInit Request */
-
-/* ProductID field uses MPI2_FW_HEADER_PID_ */
-
-/* IOCCapabilities */
-#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000)
-#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000)
-#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000)
-#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000)
-#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000)
-#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800)
-#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100)
-#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080)
-#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040)
-#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020)
-#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010)
-#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008)
-#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004)
-
-/* ProtocolFlags */
-#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001)
-#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002)
-
-
-/****************************************************************************
-* PortFacts message
-****************************************************************************/
-
-/* PortFacts Request message */
-typedef struct _MPI2_PORT_FACTS_REQUEST
-{
- U16 Reserved1; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 PortNumber; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved3; /* 0x0A */
-} MPI2_PORT_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_PORT_FACTS_REQUEST,
- Mpi2PortFactsRequest_t, MPI2_POINTER pMpi2PortFactsRequest_t;
-
-/* PortFacts Reply message */
-typedef struct _MPI2_PORT_FACTS_REPLY
-{
- U16 Reserved1; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 PortNumber; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved3; /* 0x0A */
- U16 Reserved4; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U8 Reserved5; /* 0x14 */
- U8 PortType; /* 0x15 */
- U16 Reserved6; /* 0x16 */
- U16 MaxPostedCmdBuffers; /* 0x18 */
- U16 Reserved7; /* 0x1A */
-} MPI2_PORT_FACTS_REPLY, MPI2_POINTER PTR_MPI2_PORT_FACTS_REPLY,
- Mpi2PortFactsReply_t, MPI2_POINTER pMpi2PortFactsReply_t;
-
-/* PortType values */
-#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00)
-#define MPI2_PORTFACTS_PORTTYPE_FC (0x10)
-#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20)
-#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30)
-#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31)
-
-
-/****************************************************************************
-* PortEnable message
-****************************************************************************/
-
-/* PortEnable Request message */
-typedef struct _MPI2_PORT_ENABLE_REQUEST
-{
- U16 Reserved1; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U8 Reserved2; /* 0x04 */
- U8 PortFlags; /* 0x05 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
-} MPI2_PORT_ENABLE_REQUEST, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REQUEST,
- Mpi2PortEnableRequest_t, MPI2_POINTER pMpi2PortEnableRequest_t;
-
-
-/* PortEnable Reply message */
-typedef struct _MPI2_PORT_ENABLE_REPLY
-{
- U16 Reserved1; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U8 Reserved2; /* 0x04 */
- U8 PortFlags; /* 0x05 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_PORT_ENABLE_REPLY, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REPLY,
- Mpi2PortEnableReply_t, MPI2_POINTER pMpi2PortEnableReply_t;
-
-
-/****************************************************************************
-* EventNotification message
-****************************************************************************/
-
-/* EventNotification Request message */
-#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4)
-
-typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST
-{
- U16 Reserved1; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U32 Reserved5; /* 0x0C */
- U32 Reserved6; /* 0x10 */
- U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];/* 0x14 */
- U16 SASBroadcastPrimitiveMasks; /* 0x24 */
- U16 Reserved7; /* 0x26 */
- U32 Reserved8; /* 0x28 */
-} MPI2_EVENT_NOTIFICATION_REQUEST,
- MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REQUEST,
- Mpi2EventNotificationRequest_t, MPI2_POINTER pMpi2EventNotificationRequest_t;
-
-
-/* EventNotification Reply message */
-typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
-{
- U16 EventDataLength; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved1; /* 0x04 */
- U8 AckRequired; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U16 Reserved3; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U16 Event; /* 0x14 */
- U16 Reserved4; /* 0x16 */
- U32 EventContext; /* 0x18 */
- U32 EventData[1]; /* 0x1C */
-} MPI2_EVENT_NOTIFICATION_REPLY, MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REPLY,
- Mpi2EventNotificationReply_t, MPI2_POINTER pMpi2EventNotificationReply_t;
-
-/* AckRequired */
-#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00)
-#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01)
-
-/* Event */
-#define MPI2_EVENT_LOG_DATA (0x0001)
-#define MPI2_EVENT_STATE_CHANGE (0x0002)
-#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005)
-#define MPI2_EVENT_EVENT_CHANGE (0x000A)
-#define MPI2_EVENT_TASK_SET_FULL (0x000E) /* obsolete */
-#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F)
-#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014)
-#define MPI2_EVENT_SAS_DISCOVERY (0x0016)
-#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017)
-#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018)
-#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019)
-#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C)
-#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D)
-#define MPI2_EVENT_IR_VOLUME (0x001E)
-#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F)
-#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020)
-#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021)
-#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022)
-#define MPI2_EVENT_GPIO_INTERRUPT (0x0023)
-#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024)
-#define MPI2_EVENT_SAS_QUIESCE (0x0025)
-
-
-/* Log Entry Added Event data */
-
-/* the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */
-#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C)
-
-typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED
-{
- U64 TimeStamp; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U16 LogSequence; /* 0x0C */
- U16 LogEntryQualifier; /* 0x0E */
- U8 VP_ID; /* 0x10 */
- U8 VF_ID; /* 0x11 */
- U16 Reserved2; /* 0x12 */
- U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH];/* 0x14 */
-} MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
- Mpi2EventDataLogEntryAdded_t, MPI2_POINTER pMpi2EventDataLogEntryAdded_t;
-
-/* GPIO Interrupt Event data */
-
-typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT {
- U8 GPIONum; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
-} MPI2_EVENT_DATA_GPIO_INTERRUPT,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT,
- Mpi2EventDataGpioInterrupt_t, MPI2_POINTER pMpi2EventDataGpioInterrupt_t;
-
-/* Hard Reset Received Event data */
-
-typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED
-{
- U8 Reserved1; /* 0x00 */
- U8 Port; /* 0x01 */
- U16 Reserved2; /* 0x02 */
-} MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
- Mpi2EventDataHardResetReceived_t,
- MPI2_POINTER pMpi2EventDataHardResetReceived_t;
-
-/* Task Set Full Event data */
-/* this event is obsolete */
-
-typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL
-{
- U16 DevHandle; /* 0x00 */
- U16 CurrentDepth; /* 0x02 */
-} MPI2_EVENT_DATA_TASK_SET_FULL, MPI2_POINTER PTR_MPI2_EVENT_DATA_TASK_SET_FULL,
- Mpi2EventDataTaskSetFull_t, MPI2_POINTER pMpi2EventDataTaskSetFull_t;
-
-
-/* SAS Device Status Change Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE
-{
- U16 TaskTag; /* 0x00 */
- U8 ReasonCode; /* 0x02 */
- U8 Reserved1; /* 0x03 */
- U8 ASC; /* 0x04 */
- U8 ASCQ; /* 0x05 */
- U16 DevHandle; /* 0x06 */
- U32 Reserved2; /* 0x08 */
- U64 SASAddress; /* 0x0C */
- U8 LUN[8]; /* 0x14 */
-} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
- Mpi2EventDataSasDeviceStatusChange_t,
- MPI2_POINTER pMpi2EventDataSasDeviceStatusChange_t;
-
-/* SAS Device Status Change Event data ReasonCode values */
-#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11)
-#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12)
-
-
-/* Integrated RAID Operation Status Event data */
-
-typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS
-{
- U16 VolDevHandle; /* 0x00 */
- U16 Reserved1; /* 0x02 */
- U8 RAIDOperation; /* 0x04 */
- U8 PercentComplete; /* 0x05 */
- U16 Reserved2; /* 0x06 */
- U32 Resereved3; /* 0x08 */
-} MPI2_EVENT_DATA_IR_OPERATION_STATUS,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS,
- Mpi2EventDataIrOperationStatus_t,
- MPI2_POINTER pMpi2EventDataIrOperationStatus_t;
-
-/* Integrated RAID Operation Status Event data RAIDOperation values */
-#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00)
-#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01)
-#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02)
-#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03)
-#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04)
-
-
-/* Integrated RAID Volume Event data */
-
-typedef struct _MPI2_EVENT_DATA_IR_VOLUME
-{
- U16 VolDevHandle; /* 0x00 */
- U8 ReasonCode; /* 0x02 */
- U8 Reserved1; /* 0x03 */
- U32 NewValue; /* 0x04 */
- U32 PreviousValue; /* 0x08 */
-} MPI2_EVENT_DATA_IR_VOLUME, MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_VOLUME,
- Mpi2EventDataIrVolume_t, MPI2_POINTER pMpi2EventDataIrVolume_t;
-
-/* Integrated RAID Volume Event data ReasonCode values */
-#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01)
-#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02)
-#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03)
-
-
-/* Integrated RAID Physical Disk Event data */
-
-typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK
-{
- U16 Reserved1; /* 0x00 */
- U8 ReasonCode; /* 0x02 */
- U8 PhysDiskNum; /* 0x03 */
- U16 PhysDiskDevHandle; /* 0x04 */
- U16 Reserved2; /* 0x06 */
- U16 Slot; /* 0x08 */
- U16 EnclosureHandle; /* 0x0A */
- U32 NewValue; /* 0x0C */
- U32 PreviousValue; /* 0x10 */
-} MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
- Mpi2EventDataIrPhysicalDisk_t, MPI2_POINTER pMpi2EventDataIrPhysicalDisk_t;
-
-/* Integrated RAID Physical Disk Event data ReasonCode values */
-#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01)
-#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02)
-#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03)
-
-
-/* Integrated RAID Configuration Change List Event data */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check NumElements at runtime.
- */
-#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT
-#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1)
-#endif
-
-typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT
-{
- U16 ElementFlags; /* 0x00 */
- U16 VolDevHandle; /* 0x02 */
- U8 ReasonCode; /* 0x04 */
- U8 PhysDiskNum; /* 0x05 */
- U16 PhysDiskDevHandle; /* 0x06 */
-} MPI2_EVENT_IR_CONFIG_ELEMENT, MPI2_POINTER PTR_MPI2_EVENT_IR_CONFIG_ELEMENT,
- Mpi2EventIrConfigElement_t, MPI2_POINTER pMpi2EventIrConfigElement_t;
-
-/* IR Configuration Change List Event data ElementFlags values */
-#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F)
-#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000)
-#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001)
-#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002)
-
-/* IR Configuration Change List Event data ReasonCode values */
-#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01)
-#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02)
-#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03)
-#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04)
-#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05)
-#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06)
-#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07)
-#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08)
-#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09)
-
-typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST
-{
- U8 NumElements; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 Reserved2; /* 0x02 */
- U8 ConfigNum; /* 0x03 */
- U32 Flags; /* 0x04 */
- MPI2_EVENT_IR_CONFIG_ELEMENT ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT]; /* 0x08 */
-} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
- Mpi2EventDataIrConfigChangeList_t,
- MPI2_POINTER pMpi2EventDataIrConfigChangeList_t;
-
-/* IR Configuration Change List Event data Flags values */
-#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001)
-
-
-/* SAS Discovery Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY
-{
- U8 Flags; /* 0x00 */
- U8 ReasonCode; /* 0x01 */
- U8 PhysicalPort; /* 0x02 */
- U8 Reserved1; /* 0x03 */
- U32 DiscoveryStatus; /* 0x04 */
-} MPI2_EVENT_DATA_SAS_DISCOVERY,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DISCOVERY,
- Mpi2EventDataSasDiscovery_t, MPI2_POINTER pMpi2EventDataSasDiscovery_t;
-
-/* SAS Discovery Event data Flags values */
-#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02)
-#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01)
-
-/* SAS Discovery Event data ReasonCode values */
-#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01)
-#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02)
-
-/* SAS Discovery Event data DiscoveryStatus values */
-#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
-#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000)
-#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000)
-#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
-#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000)
-#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
-#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
-#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000)
-#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
-#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800)
-#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400)
-#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200)
-#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100)
-#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080)
-#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040)
-#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020)
-#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010)
-#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004)
-#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002)
-#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001)
-
-
-/* SAS Broadcast Primitive Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE
-{
- U8 PhyNum; /* 0x00 */
- U8 Port; /* 0x01 */
- U8 PortWidth; /* 0x02 */
- U8 Primitive; /* 0x03 */
-} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
- Mpi2EventDataSasBroadcastPrimitive_t,
- MPI2_POINTER pMpi2EventDataSasBroadcastPrimitive_t;
-
-/* defines for the Primitive field */
-#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01)
-#define MPI2_EVENT_PRIMITIVE_SES (0x02)
-#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03)
-#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04)
-#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05)
-#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06)
-#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07)
-#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08)
-
-
-/* SAS Initiator Device Status Change Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE
-{
- U8 ReasonCode; /* 0x00 */
- U8 PhysicalPort; /* 0x01 */
- U16 DevHandle; /* 0x02 */
- U64 SASAddress; /* 0x04 */
-} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
- Mpi2EventDataSasInitDevStatusChange_t,
- MPI2_POINTER pMpi2EventDataSasInitDevStatusChange_t;
-
-/* SAS Initiator Device Status Change event ReasonCode values */
-#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01)
-#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02)
-
-
-/* SAS Initiator Device Table Overflow Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW
-{
- U16 MaxInit; /* 0x00 */
- U16 CurrentInit; /* 0x02 */
- U64 SASAddress; /* 0x04 */
-} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
- Mpi2EventDataSasInitTableOverflow_t,
- MPI2_POINTER pMpi2EventDataSasInitTableOverflow_t;
-
-
-/* SAS Topology Change List Event data */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check NumEntries at runtime.
- */
-#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT
-#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1)
-#endif
-
-typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY
-{
- U16 AttachedDevHandle; /* 0x00 */
- U8 LinkRate; /* 0x02 */
- U8 PhyStatus; /* 0x03 */
-} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, MPI2_POINTER PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY,
- Mpi2EventSasTopoPhyEntry_t, MPI2_POINTER pMpi2EventSasTopoPhyEntry_t;
-
-typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST
-{
- U16 EnclosureHandle; /* 0x00 */
- U16 ExpanderDevHandle; /* 0x02 */
- U8 NumPhys; /* 0x04 */
- U8 Reserved1; /* 0x05 */
- U16 Reserved2; /* 0x06 */
- U8 NumEntries; /* 0x08 */
- U8 StartPhyNum; /* 0x09 */
- U8 ExpStatus; /* 0x0A */
- U8 PhysicalPort; /* 0x0B */
- MPI2_EVENT_SAS_TOPO_PHY_ENTRY PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /* 0x0C*/
-} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
- Mpi2EventDataSasTopologyChangeList_t,
- MPI2_POINTER pMpi2EventDataSasTopologyChangeList_t;
-
-/* values for the ExpStatus field */
-#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00)
-#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01)
-#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
-#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
-#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04)
-
-/* defines for the LinkRate field */
-#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0)
-#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4)
-#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F)
-#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0)
-
-#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00)
-#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01)
-#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02)
-#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03)
-#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04)
-#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05)
-#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06)
-#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08)
-#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09)
-#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A)
-
-/* values for the PhyStatus field */
-#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80)
-#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10)
-/* values for the PhyStatus ReasonCode sub-field */
-#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F)
-#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01)
-#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02)
-#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03)
-#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04)
-#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05)
-
-
-/* SAS Enclosure Device Status Change Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE
-{
- U16 EnclosureHandle; /* 0x00 */
- U8 ReasonCode; /* 0x02 */
- U8 PhysicalPort; /* 0x03 */
- U64 EnclosureLogicalID; /* 0x04 */
- U16 NumSlots; /* 0x0C */
- U16 StartSlot; /* 0x0E */
- U32 PhyBits; /* 0x10 */
-} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
- Mpi2EventDataSasEnclDevStatusChange_t,
- MPI2_POINTER pMpi2EventDataSasEnclDevStatusChange_t;
-
-/* SAS Enclosure Device Status Change event ReasonCode values */
-#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01)
-#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02)
-
-
-/* SAS PHY Counter Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER {
- U64 TimeStamp; /* 0x00 */
- U32 Reserved1; /* 0x08 */
- U8 PhyEventCode; /* 0x0C */
- U8 PhyNum; /* 0x0D */
- U16 Reserved2; /* 0x0E */
- U32 PhyEventInfo; /* 0x10 */
- U8 CounterType; /* 0x14 */
- U8 ThresholdWindow; /* 0x15 */
- U8 TimeUnits; /* 0x16 */
- U8 Reserved3; /* 0x17 */
- U32 EventThreshold; /* 0x18 */
- U16 ThresholdFlags; /* 0x1C */
- U16 Reserved4; /* 0x1E */
-} MPI2_EVENT_DATA_SAS_PHY_COUNTER,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER,
- Mpi2EventDataSasPhyCounter_t, MPI2_POINTER pMpi2EventDataSasPhyCounter_t;
-
-/* use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h for the
- * PhyEventCode field
- * use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h for the
- * CounterType field
- * use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h for the
- * TimeUnits field
- * use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h for the
- * ThresholdFlags field
- * */
-
-
-/* SAS Quiesce Event data */
-
-typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE {
- U8 ReasonCode; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
- U32 Reserved3; /* 0x04 */
-} MPI2_EVENT_DATA_SAS_QUIESCE,
- MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_QUIESCE,
- Mpi2EventDataSasQuiesce_t, MPI2_POINTER pMpi2EventDataSasQuiesce_t;
-
-/* SAS Quiesce Event data ReasonCode values */
-#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01)
-#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02)
-
-
-/* Host Based Discovery Phy Event data */
-
-typedef struct _MPI2_EVENT_HBD_PHY_SAS {
- U8 Flags; /* 0x00 */
- U8 NegotiatedLinkRate; /* 0x01 */
- U8 PhyNum; /* 0x02 */
- U8 PhysicalPort; /* 0x03 */
- U32 Reserved1; /* 0x04 */
- U8 InitialFrame[28]; /* 0x08 */
-} MPI2_EVENT_HBD_PHY_SAS, MPI2_POINTER PTR_MPI2_EVENT_HBD_PHY_SAS,
- Mpi2EventHbdPhySas_t, MPI2_POINTER pMpi2EventHbdPhySas_t;
-
-/* values for the Flags field */
-#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02)
-#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01)
-
-/* use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h for
- * the NegotiatedLinkRate field */
-
-typedef union _MPI2_EVENT_HBD_DESCRIPTOR {
- MPI2_EVENT_HBD_PHY_SAS Sas;
-} MPI2_EVENT_HBD_DESCRIPTOR, MPI2_POINTER PTR_MPI2_EVENT_HBD_DESCRIPTOR,
- Mpi2EventHbdDescriptor_t, MPI2_POINTER pMpi2EventHbdDescriptor_t;
-
-typedef struct _MPI2_EVENT_DATA_HBD_PHY {
- U8 DescriptorType; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
- U32 Reserved3; /* 0x04 */
- MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /* 0x08 */
-} MPI2_EVENT_DATA_HBD_PHY, MPI2_POINTER PTR_MPI2_EVENT_DATA_HBD_PHY,
- Mpi2EventDataHbdPhy_t, MPI2_POINTER pMpi2EventDataMpi2EventDataHbdPhy_t;
-
-/* values for the DescriptorType field */
-#define MPI2_EVENT_HBD_DT_SAS (0x01)
-
-
-
-/****************************************************************************
-* EventAck message
-****************************************************************************/
-
-/* EventAck Request message */
-typedef struct _MPI2_EVENT_ACK_REQUEST
-{
- U16 Reserved1; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Event; /* 0x0C */
- U16 Reserved5; /* 0x0E */
- U32 EventContext; /* 0x10 */
-} MPI2_EVENT_ACK_REQUEST, MPI2_POINTER PTR_MPI2_EVENT_ACK_REQUEST,
- Mpi2EventAckRequest_t, MPI2_POINTER pMpi2EventAckRequest_t;
-
-
-/* EventAck Reply message */
-typedef struct _MPI2_EVENT_ACK_REPLY
-{
- U16 Reserved1; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_EVENT_ACK_REPLY, MPI2_POINTER PTR_MPI2_EVENT_ACK_REPLY,
- Mpi2EventAckReply_t, MPI2_POINTER pMpi2EventAckReply_t;
-
-
-/****************************************************************************
-* FWDownload message
-****************************************************************************/
-
-/* FWDownload Request message */
-typedef struct _MPI2_FW_DOWNLOAD_REQUEST
-{
- U8 ImageType; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U32 TotalImageSize; /* 0x0C */
- U32 Reserved5; /* 0x10 */
- MPI2_MPI_SGE_UNION SGL; /* 0x14 */
-} MPI2_FW_DOWNLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REQUEST,
- Mpi2FWDownloadRequest, MPI2_POINTER pMpi2FWDownloadRequest;
-
-#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01)
-
-#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01)
-#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02)
-#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06)
-#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07)
-#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08)
-#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09)
-#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A)
-#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
-
-/* FWDownload TransactionContext Element */
-typedef struct _MPI2_FW_DOWNLOAD_TCSGE
-{
- U8 Reserved1; /* 0x00 */
- U8 ContextSize; /* 0x01 */
- U8 DetailsLength; /* 0x02 */
- U8 Flags; /* 0x03 */
- U32 Reserved2; /* 0x04 */
- U32 ImageOffset; /* 0x08 */
- U32 ImageSize; /* 0x0C */
-} MPI2_FW_DOWNLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_TCSGE,
- Mpi2FWDownloadTCSGE_t, MPI2_POINTER pMpi2FWDownloadTCSGE_t;
-
-/* FWDownload Reply message */
-typedef struct _MPI2_FW_DOWNLOAD_REPLY
-{
- U8 ImageType; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_FW_DOWNLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REPLY,
- Mpi2FWDownloadReply_t, MPI2_POINTER pMpi2FWDownloadReply_t;
-
-
-/****************************************************************************
-* FWUpload message
-****************************************************************************/
-
-/* FWUpload Request message */
-typedef struct _MPI2_FW_UPLOAD_REQUEST
-{
- U8 ImageType; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U32 Reserved5; /* 0x0C */
- U32 Reserved6; /* 0x10 */
- MPI2_MPI_SGE_UNION SGL; /* 0x14 */
-} MPI2_FW_UPLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REQUEST,
- Mpi2FWUploadRequest_t, MPI2_POINTER pMpi2FWUploadRequest_t;
-
-#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00)
-#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01)
-#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02)
-#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05)
-#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06)
-#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07)
-#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08)
-#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09)
-#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A)
-#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
-
-typedef struct _MPI2_FW_UPLOAD_TCSGE
-{
- U8 Reserved1; /* 0x00 */
- U8 ContextSize; /* 0x01 */
- U8 DetailsLength; /* 0x02 */
- U8 Flags; /* 0x03 */
- U32 Reserved2; /* 0x04 */
- U32 ImageOffset; /* 0x08 */
- U32 ImageSize; /* 0x0C */
-} MPI2_FW_UPLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_UPLOAD_TCSGE,
- Mpi2FWUploadTCSGE_t, MPI2_POINTER pMpi2FWUploadTCSGE_t;
-
-/* FWUpload Reply message */
-typedef struct _MPI2_FW_UPLOAD_REPLY
-{
- U8 ImageType; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U32 ActualImageSize; /* 0x14 */
-} MPI2_FW_UPLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REPLY,
- Mpi2FWUploadReply_t, MPI2_POINTER pMPi2FWUploadReply_t;
-
-
-/* FW Image Header */
-typedef struct _MPI2_FW_IMAGE_HEADER
-{
- U32 Signature; /* 0x00 */
- U32 Signature0; /* 0x04 */
- U32 Signature1; /* 0x08 */
- U32 Signature2; /* 0x0C */
- MPI2_VERSION_UNION MPIVersion; /* 0x10 */
- MPI2_VERSION_UNION FWVersion; /* 0x14 */
- MPI2_VERSION_UNION NVDATAVersion; /* 0x18 */
- MPI2_VERSION_UNION PackageVersion; /* 0x1C */
- U16 VendorID; /* 0x20 */
- U16 ProductID; /* 0x22 */
- U16 ProtocolFlags; /* 0x24 */
- U16 Reserved26; /* 0x26 */
- U32 IOCCapabilities; /* 0x28 */
- U32 ImageSize; /* 0x2C */
- U32 NextImageHeaderOffset; /* 0x30 */
- U32 Checksum; /* 0x34 */
- U32 Reserved38; /* 0x38 */
- U32 Reserved3C; /* 0x3C */
- U32 Reserved40; /* 0x40 */
- U32 Reserved44; /* 0x44 */
- U32 Reserved48; /* 0x48 */
- U32 Reserved4C; /* 0x4C */
- U32 Reserved50; /* 0x50 */
- U32 Reserved54; /* 0x54 */
- U32 Reserved58; /* 0x58 */
- U32 Reserved5C; /* 0x5C */
- U32 Reserved60; /* 0x60 */
- U32 FirmwareVersionNameWhat; /* 0x64 */
- U8 FirmwareVersionName[32]; /* 0x68 */
- U32 VendorNameWhat; /* 0x88 */
- U8 VendorName[32]; /* 0x8C */
- U32 PackageNameWhat; /* 0x88 */
- U8 PackageName[32]; /* 0x8C */
- U32 ReservedD0; /* 0xD0 */
- U32 ReservedD4; /* 0xD4 */
- U32 ReservedD8; /* 0xD8 */
- U32 ReservedDC; /* 0xDC */
- U32 ReservedE0; /* 0xE0 */
- U32 ReservedE4; /* 0xE4 */
- U32 ReservedE8; /* 0xE8 */
- U32 ReservedEC; /* 0xEC */
- U32 ReservedF0; /* 0xF0 */
- U32 ReservedF4; /* 0xF4 */
- U32 ReservedF8; /* 0xF8 */
- U32 ReservedFC; /* 0xFC */
-} MPI2_FW_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_FW_IMAGE_HEADER,
- Mpi2FWImageHeader_t, MPI2_POINTER pMpi2FWImageHeader_t;
-
-/* Signature field */
-#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00)
-#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000)
-#define MPI2_FW_HEADER_SIGNATURE (0xEA000000)
-
-/* Signature0 field */
-#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04)
-#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A)
-
-/* Signature1 field */
-#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08)
-#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5)
-
-/* Signature2 field */
-#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C)
-#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA)
-
-
-/* defines for using the ProductID field */
-#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000)
-#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000)
-
-#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
-#define MPI2_FW_HEADER_PID_PROD_A (0x0000)
-#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200)
-#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700)
-
-
-#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF)
-/* SAS */
-#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013)
-#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014)
-
-/* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
-
-/* use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */
-
-
-#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C)
-#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30)
-#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64)
-
-#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840)
-
-#define MPI2_FW_HEADER_SIZE (0x100)
-
-
-/* Extended Image Header */
-typedef struct _MPI2_EXT_IMAGE_HEADER
-
-{
- U8 ImageType; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
- U32 Checksum; /* 0x04 */
- U32 ImageSize; /* 0x08 */
- U32 NextImageHeaderOffset; /* 0x0C */
- U32 PackageVersion; /* 0x10 */
- U32 Reserved3; /* 0x14 */
- U32 Reserved4; /* 0x18 */
- U32 Reserved5; /* 0x1C */
- U8 IdentifyString[32]; /* 0x20 */
-} MPI2_EXT_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_EXT_IMAGE_HEADER,
- Mpi2ExtImageHeader_t, MPI2_POINTER pMpi2ExtImageHeader_t;
-
-/* useful offsets */
-#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00)
-#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08)
-#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C)
-
-#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40)
-
-/* defines for the ImageType field */
-#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00)
-#define MPI2_EXT_IMAGE_TYPE_FW (0x01)
-#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03)
-#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04)
-#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05)
-#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06)
-#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07)
-#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08)
-
-#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MEGARAID)
-
-
-
-/* FLASH Layout Extended Image Data */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check RegionsPerLayout at runtime.
- */
-#ifndef MPI2_FLASH_NUMBER_OF_REGIONS
-#define MPI2_FLASH_NUMBER_OF_REGIONS (1)
-#endif
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check NumberOfLayouts at runtime.
- */
-#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS
-#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1)
-#endif
-
-typedef struct _MPI2_FLASH_REGION
-{
- U8 RegionType; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 Reserved2; /* 0x02 */
- U32 RegionOffset; /* 0x04 */
- U32 RegionSize; /* 0x08 */
- U32 Reserved3; /* 0x0C */
-} MPI2_FLASH_REGION, MPI2_POINTER PTR_MPI2_FLASH_REGION,
- Mpi2FlashRegion_t, MPI2_POINTER pMpi2FlashRegion_t;
-
-typedef struct _MPI2_FLASH_LAYOUT
-{
- U32 FlashSize; /* 0x00 */
- U32 Reserved1; /* 0x04 */
- U32 Reserved2; /* 0x08 */
- U32 Reserved3; /* 0x0C */
- MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS];/* 0x10 */
-} MPI2_FLASH_LAYOUT, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT,
- Mpi2FlashLayout_t, MPI2_POINTER pMpi2FlashLayout_t;
-
-typedef struct _MPI2_FLASH_LAYOUT_DATA
-{
- U8 ImageRevision; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 SizeOfRegion; /* 0x02 */
- U8 Reserved2; /* 0x03 */
- U16 NumberOfLayouts; /* 0x04 */
- U16 RegionsPerLayout; /* 0x06 */
- U16 MinimumSectorAlignment; /* 0x08 */
- U16 Reserved3; /* 0x0A */
- U32 Reserved4; /* 0x0C */
- MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS];/* 0x10 */
-} MPI2_FLASH_LAYOUT_DATA, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT_DATA,
- Mpi2FlashLayoutData_t, MPI2_POINTER pMpi2FlashLayoutData_t;
-
-/* defines for the RegionType field */
-#define MPI2_FLASH_REGION_UNUSED (0x00)
-#define MPI2_FLASH_REGION_FIRMWARE (0x01)
-#define MPI2_FLASH_REGION_BIOS (0x02)
-#define MPI2_FLASH_REGION_NVDATA (0x03)
-#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05)
-#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06)
-#define MPI2_FLASH_REGION_CONFIG_1 (0x07)
-#define MPI2_FLASH_REGION_CONFIG_2 (0x08)
-#define MPI2_FLASH_REGION_MEGARAID (0x09)
-#define MPI2_FLASH_REGION_INIT (0x0A)
-
-/* ImageRevision */
-#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00)
-
-
-
-/* Supported Devices Extended Image Data */
-
-/*
- * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check NumberOfDevices at runtime.
- */
-#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES
-#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1)
-#endif
-
-typedef struct _MPI2_SUPPORTED_DEVICE
-{
- U16 DeviceID; /* 0x00 */
- U16 VendorID; /* 0x02 */
- U16 DeviceIDMask; /* 0x04 */
- U16 Reserved1; /* 0x06 */
- U8 LowPCIRev; /* 0x08 */
- U8 HighPCIRev; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U32 Reserved3; /* 0x0C */
-} MPI2_SUPPORTED_DEVICE, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICE,
- Mpi2SupportedDevice_t, MPI2_POINTER pMpi2SupportedDevice_t;
-
-typedef struct _MPI2_SUPPORTED_DEVICES_DATA
-{
- U8 ImageRevision; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 NumberOfDevices; /* 0x02 */
- U8 Reserved2; /* 0x03 */
- U32 Reserved3; /* 0x04 */
- MPI2_SUPPORTED_DEVICE SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES]; /* 0x08 */
-} MPI2_SUPPORTED_DEVICES_DATA, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICES_DATA,
- Mpi2SupportedDevicesData_t, MPI2_POINTER pMpi2SupportedDevicesData_t;
-
-/* ImageRevision */
-#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00)
-
-
-/* Init Extended Image Data */
-
-typedef struct _MPI2_INIT_IMAGE_FOOTER
-
-{
- U32 BootFlags; /* 0x00 */
- U32 ImageSize; /* 0x04 */
- U32 Signature0; /* 0x08 */
- U32 Signature1; /* 0x0C */
- U32 Signature2; /* 0x10 */
- U32 ResetVector; /* 0x14 */
-} MPI2_INIT_IMAGE_FOOTER, MPI2_POINTER PTR_MPI2_INIT_IMAGE_FOOTER,
- Mpi2InitImageFooter_t, MPI2_POINTER pMpi2InitImageFooter_t;
-
-/* defines for the BootFlags field */
-#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00)
-
-/* defines for the ImageSize field */
-#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04)
-
-/* defines for the Signature0 field */
-#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08)
-#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA)
-
-/* defines for the Signature1 field */
-#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C)
-#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5)
-
-/* defines for the Signature2 field */
-#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10)
-#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A)
-
-/* Signature fields as individual bytes */
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A)
-
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5)
-
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA)
-#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A)
-
-/* defines for the ResetVector field */
-#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14)
-
-
-/****************************************************************************
-* PowerManagementControl message
-****************************************************************************/
-
-/* PowerManagementControl Request message */
-typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST {
- U8 Feature; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U8 Parameter1; /* 0x0C */
- U8 Parameter2; /* 0x0D */
- U8 Parameter3; /* 0x0E */
- U8 Parameter4; /* 0x0F */
- U32 Reserved5; /* 0x10 */
- U32 Reserved6; /* 0x14 */
-} MPI2_PWR_MGMT_CONTROL_REQUEST, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REQUEST,
- Mpi2PwrMgmtControlRequest_t, MPI2_POINTER pMpi2PwrMgmtControlRequest_t;
-
-/* defines for the Feature field */
-#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01)
-#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02)
-#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03)
-#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04)
-#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80)
-#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF)
-
-/* parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */
-/* Parameter1 contains a PHY number */
-/* Parameter2 indicates power condition action using these defines */
-#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01)
-#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02)
-#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03)
-/* Parameter3 and Parameter4 are reserved */
-
-/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION
- * Feature */
-/* Parameter1 contains SAS port width modulation group number */
-/* Parameter2 indicates IOC action using these defines */
-#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01)
-#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02)
-#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03)
-/* Parameter3 indicates desired modulation level using these defines */
-#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00)
-#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01)
-#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02)
-#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03)
-/* Parameter4 is reserved */
-
-/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */
-/* Parameter1 indicates desired PCIe link speed using these defines */
-#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00)
-#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01)
-#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02)
-/* Parameter2 indicates desired PCIe link width using these defines */
-#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01)
-#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02)
-#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04)
-#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08)
-/* Parameter3 and Parameter4 are reserved */
-
-/* parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */
-/* Parameter1 indicates desired IOC hardware clock speed using these defines */
-#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01)
-#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02)
-#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04)
-#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08)
-/* Parameter2, Parameter3, and Parameter4 are reserved */
-
-
-/* PowerManagementControl Reply message */
-typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY {
- U8 Feature; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_PWR_MGMT_CONTROL_REPLY, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REPLY,
- Mpi2PwrMgmtControlReply_t, MPI2_POINTER pMpi2PwrMgmtControlReply_t;
-
-
-#endif
-
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
deleted file mode 100644
index bd61a7b60a2b..000000000000
--- a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (c) 2000-2010 LSI Corporation.
- *
- *
- * Name: mpi2_raid.h
- * Title: MPI Integrated RAID messages and structures
- * Creation Date: April 26, 2007
- *
- * mpi2_raid.h Version: 02.00.05
- *
- * Version History
- * ---------------
- *
- * Date Version Description
- * -------- -------- ------------------------------------------------------
- * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
- * 08-31-07 02.00.01 Modifications to RAID Action request and reply,
- * including the Actions and ActionData.
- * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD.
- * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that
- * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT
- * can be sized by the build environment.
- * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of
- * VolumeCreationFlags and marked the old one as obsolete.
- * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define.
- * --------------------------------------------------------------------------
- */
-
-#ifndef MPI2_RAID_H
-#define MPI2_RAID_H
-
-/*****************************************************************************
-*
-* Integrated RAID Messages
-*
-*****************************************************************************/
-
-/****************************************************************************
-* RAID Action messages
-****************************************************************************/
-
-/* ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */
-#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000)
-#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001)
-
-/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
-
-/* ActionDataWord defines for use with MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */
-#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001)
-
-/* ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */
-typedef struct _MPI2_RAID_ACTION_RATE_DATA
-{
- U8 RateToChange; /* 0x00 */
- U8 RateOrMode; /* 0x01 */
- U16 DataScrubDuration; /* 0x02 */
-} MPI2_RAID_ACTION_RATE_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_RATE_DATA,
- Mpi2RaidActionRateData_t, MPI2_POINTER pMpi2RaidActionRateData_t;
-
-#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00)
-#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01)
-#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02)
-
-/* ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */
-typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION
-{
- U8 RAIDFunction; /* 0x00 */
- U8 Flags; /* 0x01 */
- U16 Reserved1; /* 0x02 */
-} MPI2_RAID_ACTION_START_RAID_FUNCTION,
- MPI2_POINTER PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION,
- Mpi2RaidActionStartRaidFunction_t,
- MPI2_POINTER pMpi2RaidActionStartRaidFunction_t;
-
-/* defines for the RAIDFunction field */
-#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00)
-#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01)
-#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02)
-
-/* defines for the Flags field */
-#define MPI2_RAID_ACTION_START_NEW (0x00)
-#define MPI2_RAID_ACTION_START_RESUME (0x01)
-
-/* ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */
-typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION
-{
- U8 RAIDFunction; /* 0x00 */
- U8 Flags; /* 0x01 */
- U16 Reserved1; /* 0x02 */
-} MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
- MPI2_POINTER PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
- Mpi2RaidActionStopRaidFunction_t,
- MPI2_POINTER pMpi2RaidActionStopRaidFunction_t;
-
-/* defines for the RAIDFunction field */
-#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00)
-#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01)
-#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02)
-
-/* defines for the Flags field */
-#define MPI2_RAID_ACTION_STOP_ABORT (0x00)
-#define MPI2_RAID_ACTION_STOP_PAUSE (0x01)
-
-/* ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */
-typedef struct _MPI2_RAID_ACTION_HOT_SPARE
-{
- U8 HotSparePool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U16 DevHandle; /* 0x02 */
-} MPI2_RAID_ACTION_HOT_SPARE, MPI2_POINTER PTR_MPI2_RAID_ACTION_HOT_SPARE,
- Mpi2RaidActionHotSpare_t, MPI2_POINTER pMpi2RaidActionHotSpare_t;
-
-/* ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */
-typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE
-{
- U8 Flags; /* 0x00 */
- U8 DeviceFirmwareUpdateModeTimeout; /* 0x01 */
- U16 Reserved1; /* 0x02 */
-} MPI2_RAID_ACTION_FW_UPDATE_MODE,
- MPI2_POINTER PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE,
- Mpi2RaidActionFwUpdateMode_t, MPI2_POINTER pMpi2RaidActionFwUpdateMode_t;
-
-/* ActionDataWord defines for use with MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */
-#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00)
-#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01)
-
-typedef union _MPI2_RAID_ACTION_DATA
-{
- U32 Word;
- MPI2_RAID_ACTION_RATE_DATA Rates;
- MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction;
- MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction;
- MPI2_RAID_ACTION_HOT_SPARE HotSpare;
- MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode;
-} MPI2_RAID_ACTION_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_DATA,
- Mpi2RaidActionData_t, MPI2_POINTER pMpi2RaidActionData_t;
-
-
-/* RAID Action Request Message */
-typedef struct _MPI2_RAID_ACTION_REQUEST
-{
- U8 Action; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 VolDevHandle; /* 0x04 */
- U8 PhysDiskNum; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U32 Reserved3; /* 0x0C */
- MPI2_RAID_ACTION_DATA ActionDataWord; /* 0x10 */
- MPI2_SGE_SIMPLE_UNION ActionDataSGE; /* 0x14 */
-} MPI2_RAID_ACTION_REQUEST, MPI2_POINTER PTR_MPI2_RAID_ACTION_REQUEST,
- Mpi2RaidActionRequest_t, MPI2_POINTER pMpi2RaidActionRequest_t;
-
-/* RAID Action request Action values */
-
-#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01)
-#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02)
-#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03)
-#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04)
-#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05)
-#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A)
-#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B)
-#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F)
-#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11)
-#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15)
-#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17)
-#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18)
-#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19)
-#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C)
-#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D)
-#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E)
-#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20)
-#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21)
-#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22)
-
-
-/* RAID Volume Creation Structure */
-
-/*
- * The following define can be customized for the targeted product.
- */
-#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS
-#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1)
-#endif
-
-typedef struct _MPI2_RAID_VOLUME_PHYSDISK
-{
- U8 RAIDSetNum; /* 0x00 */
- U8 PhysDiskMap; /* 0x01 */
- U16 PhysDiskDevHandle; /* 0x02 */
-} MPI2_RAID_VOLUME_PHYSDISK, MPI2_POINTER PTR_MPI2_RAID_VOLUME_PHYSDISK,
- Mpi2RaidVolumePhysDisk_t, MPI2_POINTER pMpi2RaidVolumePhysDisk_t;
-
-/* defines for the PhysDiskMap field */
-#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01)
-#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02)
-
-typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT
-{
- U8 NumPhysDisks; /* 0x00 */
- U8 VolumeType; /* 0x01 */
- U16 Reserved1; /* 0x02 */
- U32 VolumeCreationFlags; /* 0x04 */
- U32 VolumeSettings; /* 0x08 */
- U8 Reserved2; /* 0x0C */
- U8 ResyncRate; /* 0x0D */
- U16 DataScrubDuration; /* 0x0E */
- U64 VolumeMaxLBA; /* 0x10 */
- U32 StripeSize; /* 0x18 */
- U8 Name[16]; /* 0x1C */
- MPI2_RAID_VOLUME_PHYSDISK PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS];/* 0x2C */
-} MPI2_RAID_VOLUME_CREATION_STRUCT,
- MPI2_POINTER PTR_MPI2_RAID_VOLUME_CREATION_STRUCT,
- Mpi2RaidVolumeCreationStruct_t, MPI2_POINTER pMpi2RaidVolumeCreationStruct_t;
-
-/* use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */
-
-/* defines for the VolumeCreationFlags field */
-#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000)
-#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004)
-#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002)
-#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001)
-/* The following is an obsolete define.
- * It must be shifted left 24 bits in order to set the proper bit.
- */
-#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80)
-
-
-/* RAID Online Capacity Expansion Structure */
-
-typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION
-{
- U32 Flags; /* 0x00 */
- U16 DevHandle0; /* 0x04 */
- U16 Reserved1; /* 0x06 */
- U16 DevHandle1; /* 0x08 */
- U16 Reserved2; /* 0x0A */
-} MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
- MPI2_POINTER PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
- Mpi2RaidOnlineCapacityExpansion_t,
- MPI2_POINTER pMpi2RaidOnlineCapacityExpansion_t;
-
-
-/* RAID Volume Indicator Structure */
-
-typedef struct _MPI2_RAID_VOL_INDICATOR
-{
- U64 TotalBlocks; /* 0x00 */
- U64 BlocksRemaining; /* 0x08 */
- U32 Flags; /* 0x10 */
-} MPI2_RAID_VOL_INDICATOR, MPI2_POINTER PTR_MPI2_RAID_VOL_INDICATOR,
- Mpi2RaidVolIndicator_t, MPI2_POINTER pMpi2RaidVolIndicator_t;
-
-/* defines for RAID Volume Indicator Flags field */
-#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F)
-#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000)
-#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001)
-#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002)
-#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003)
-#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004)
-
-
-/* RAID Action Reply ActionData union */
-typedef union _MPI2_RAID_ACTION_REPLY_DATA
-{
- U32 Word[5];
- MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator;
- U16 VolDevHandle;
- U8 VolumeState;
- U8 PhysDiskNum;
-} MPI2_RAID_ACTION_REPLY_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY_DATA,
- Mpi2RaidActionReplyData_t, MPI2_POINTER pMpi2RaidActionReplyData_t;
-
-/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
-
-
-/* RAID Action Reply Message */
-typedef struct _MPI2_RAID_ACTION_REPLY
-{
- U8 Action; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 VolDevHandle; /* 0x04 */
- U8 PhysDiskNum; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved2; /* 0x0A */
- U16 Reserved3; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- MPI2_RAID_ACTION_REPLY_DATA ActionData; /* 0x14 */
-} MPI2_RAID_ACTION_REPLY, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY,
- Mpi2RaidActionReply_t, MPI2_POINTER pMpi2RaidActionReply_t;
-
-
-#endif
-
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h
deleted file mode 100644
index fdffde1ebc0f..000000000000
--- a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (c) 2000-2010 LSI Corporation.
- *
- *
- * Name: mpi2_sas.h
- * Title: MPI Serial Attached SCSI structures and definitions
- * Creation Date: February 9, 2007
- *
- * mpi2_sas.h Version: 02.00.05
- *
- * Version History
- * ---------------
- *
- * Date Version Description
- * -------- -------- ------------------------------------------------------
- * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
- * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit
- * Control Request.
- * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control
- * Request.
- * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
- * to MPI2_SGE_IO_UNION since it supports chained SGLs.
- * 05-12-10 02.00.04 Modified some comments.
- * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control.
- * --------------------------------------------------------------------------
- */
-
-#ifndef MPI2_SAS_H
-#define MPI2_SAS_H
-
-/*
- * Values for SASStatus.
- */
-#define MPI2_SASSTATUS_SUCCESS (0x00)
-#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01)
-#define MPI2_SASSTATUS_INVALID_FRAME (0x02)
-#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03)
-#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04)
-#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05)
-#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06)
-#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07)
-#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08)
-#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09)
-#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A)
-#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B)
-#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C)
-#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D)
-#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E)
-#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F)
-#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10)
-#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11)
-#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12)
-#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13)
-#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14)
-
-
-/*
- * Values for the SAS DeviceInfo field used in SAS Device Status Change Event
- * data and SAS Configuration pages.
- */
-#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000)
-#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000)
-#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000)
-#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800)
-#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400)
-#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200)
-#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100)
-#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080)
-#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040)
-#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020)
-#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010)
-#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008)
-
-#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007)
-#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000)
-#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001)
-#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002)
-#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003)
-
-
-/*****************************************************************************
-*
-* SAS Messages
-*
-*****************************************************************************/
-
-/****************************************************************************
-* SMP Passthrough messages
-****************************************************************************/
-
-/* SMP Passthrough Request Message */
-typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST
-{
- U8 PassthroughFlags; /* 0x00 */
- U8 PhysicalPort; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 RequestDataLength; /* 0x04 */
- U8 SGLFlags; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U32 Reserved2; /* 0x0C */
- U64 SASAddress; /* 0x10 */
- U32 Reserved3; /* 0x18 */
- U32 Reserved4; /* 0x1C */
- MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */
-} MPI2_SMP_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REQUEST,
- Mpi2SmpPassthroughRequest_t, MPI2_POINTER pMpi2SmpPassthroughRequest_t;
-
-/* values for PassthroughFlags field */
-#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80)
-
-/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
-
-
-/* SMP Passthrough Reply Message */
-typedef struct _MPI2_SMP_PASSTHROUGH_REPLY
-{
- U8 PassthroughFlags; /* 0x00 */
- U8 PhysicalPort; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 ResponseDataLength; /* 0x04 */
- U8 SGLFlags; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U8 Reserved2; /* 0x0C */
- U8 SASStatus; /* 0x0D */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U32 Reserved3; /* 0x14 */
- U8 ResponseData[4]; /* 0x18 */
-} MPI2_SMP_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REPLY,
- Mpi2SmpPassthroughReply_t, MPI2_POINTER pMpi2SmpPassthroughReply_t;
-
-/* values for PassthroughFlags field */
-#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80)
-
-/* values for SASStatus field are at the top of this file */
-
-
-/****************************************************************************
-* SATA Passthrough messages
-****************************************************************************/
-
-/* SATA Passthrough Request Message */
-typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST
-{
- U16 DevHandle; /* 0x00 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 PassthroughFlags; /* 0x04 */
- U8 SGLFlags; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U32 Reserved2; /* 0x0C */
- U32 Reserved3; /* 0x10 */
- U32 Reserved4; /* 0x14 */
- U32 DataLength; /* 0x18 */
- U8 CommandFIS[20]; /* 0x1C */
- MPI2_SGE_IO_UNION SGL; /* 0x30 */
-} MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST,
- Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t;
-
-/* values for PassthroughFlags field */
-#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100)
-#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020)
-#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010)
-#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004)
-#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002)
-#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001)
-
-/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
-
-
-/* SATA Passthrough Reply Message */
-typedef struct _MPI2_SATA_PASSTHROUGH_REPLY
-{
- U16 DevHandle; /* 0x00 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 PassthroughFlags; /* 0x04 */
- U8 SGLFlags; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved1; /* 0x0A */
- U8 Reserved2; /* 0x0C */
- U8 SASStatus; /* 0x0D */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U8 StatusFIS[20]; /* 0x14 */
- U32 StatusControlRegisters; /* 0x28 */
- U32 TransferCount; /* 0x2C */
-} MPI2_SATA_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REPLY,
- Mpi2SataPassthroughReply_t, MPI2_POINTER pMpi2SataPassthroughReply_t;
-
-/* values for SASStatus field are at the top of this file */
-
-
-/****************************************************************************
-* SAS IO Unit Control messages
-****************************************************************************/
-
-/* SAS IO Unit Control Request Message */
-typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST
-{
- U8 Operation; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 DevHandle; /* 0x04 */
- U8 IOCParameter; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved3; /* 0x0A */
- U16 Reserved4; /* 0x0C */
- U8 PhyNum; /* 0x0E */
- U8 PrimFlags; /* 0x0F */
- U32 Primitive; /* 0x10 */
- U8 LookupMethod; /* 0x14 */
- U8 Reserved5; /* 0x15 */
- U16 SlotNumber; /* 0x16 */
- U64 LookupAddress; /* 0x18 */
- U32 IOCParameterValue; /* 0x20 */
- U32 Reserved7; /* 0x24 */
- U32 Reserved8; /* 0x28 */
-} MPI2_SAS_IOUNIT_CONTROL_REQUEST,
- MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST,
- Mpi2SasIoUnitControlRequest_t, MPI2_POINTER pMpi2SasIoUnitControlRequest_t;
-
-/* values for the Operation field */
-#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02)
-#define MPI2_SAS_OP_PHY_LINK_RESET (0x06)
-#define MPI2_SAS_OP_PHY_HARD_RESET (0x07)
-#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08)
-#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A)
-#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B)
-#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C)
-#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D)
-#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E)
-#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F)
-#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14)
-#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15)
-#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80)
-
-/* values for the PrimFlags field */
-#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08)
-#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02)
-#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01)
-
-/* values for the LookupMethod field */
-#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01)
-#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02)
-#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03)
-
-
-/* SAS IO Unit Control Reply Message */
-typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY
-{
- U8 Operation; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 DevHandle; /* 0x04 */
- U8 IOCParameter; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved3; /* 0x0A */
- U16 Reserved4; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_SAS_IOUNIT_CONTROL_REPLY,
- MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY,
- Mpi2SasIoUnitControlReply_t, MPI2_POINTER pMpi2SasIoUnitControlReply_t;
-
-
-#endif
-
-
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
deleted file mode 100644
index 2a4bceda364b..000000000000
--- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright (c) 2000-2010 LSI Corporation.
- *
- *
- * Name: mpi2_tool.h
- * Title: MPI diagnostic tool structures and definitions
- * Creation Date: March 26, 2007
- *
- * mpi2_tool.h Version: 02.00.06
- *
- * Version History
- * ---------------
- *
- * Date Version Description
- * -------- -------- ------------------------------------------------------
- * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
- * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release
- * structures and defines.
- * 02-29-08 02.00.02 Modified various names to make them 32-character unique.
- * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool.
- * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request
- * and reply messages.
- * Added MPI2_DIAG_BUF_TYPE_EXTENDED.
- * Incremented MPI2_DIAG_BUF_TYPE_COUNT.
- * 05-12-10 02.00.05 Added Diagnostic Data Upload tool.
- * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer
- * Post Request.
- * --------------------------------------------------------------------------
- */
-
-#ifndef MPI2_TOOL_H
-#define MPI2_TOOL_H
-
-/*****************************************************************************
-*
-* Toolbox Messages
-*
-*****************************************************************************/
-
-/* defines for the Tools */
-#define MPI2_TOOLBOX_CLEAN_TOOL (0x00)
-#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01)
-#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02)
-#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03)
-#define MPI2_TOOLBOX_BEACON_TOOL (0x05)
-#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06)
-
-
-/****************************************************************************
-* Toolbox reply
-****************************************************************************/
-
-typedef struct _MPI2_TOOLBOX_REPLY
-{
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_TOOLBOX_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_REPLY,
- Mpi2ToolboxReply_t, MPI2_POINTER pMpi2ToolboxReply_t;
-
-
-/****************************************************************************
-* Toolbox Clean Tool request
-****************************************************************************/
-
-typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST
-{
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U32 Flags; /* 0x0C */
- } MPI2_TOOLBOX_CLEAN_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_CLEAN_REQUEST,
- Mpi2ToolboxCleanRequest_t, MPI2_POINTER pMpi2ToolboxCleanRequest_t;
-
-/* values for the Flags field */
-#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000)
-#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000)
-#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000)
-#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000)
-#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000)
-#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000)
-#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000)
-#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004)
-#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002)
-#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001)
-
-
-/****************************************************************************
-* Toolbox Memory Move request
-****************************************************************************/
-
-typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST {
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- MPI2_SGE_SIMPLE_UNION SGL; /* 0x0C */
-} MPI2_TOOLBOX_MEM_MOVE_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST,
- Mpi2ToolboxMemMoveRequest_t, MPI2_POINTER pMpi2ToolboxMemMoveRequest_t;
-
-
-/****************************************************************************
-* Toolbox Diagnostic Data Upload request
-****************************************************************************/
-
-typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST {
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U8 SGLFlags; /* 0x0C */
- U8 Reserved5; /* 0x0D */
- U16 Reserved6; /* 0x0E */
- U32 Flags; /* 0x10 */
- U32 DataLength; /* 0x14 */
- MPI2_SGE_SIMPLE_UNION SGL; /* 0x18 */
-} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
-MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
-Mpi2ToolboxDiagDataUploadRequest_t,
-MPI2_POINTER pMpi2ToolboxDiagDataUploadRequest_t;
-
-/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
-
-
-typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER {
- U32 DiagDataLength; /* 00h */
- U8 FormatCode; /* 04h */
- U8 Reserved1; /* 05h */
- U16 Reserved2; /* 06h */
-} MPI2_DIAG_DATA_UPLOAD_HEADER, MPI2_POINTER PTR_MPI2_DIAG_DATA_UPLOAD_HEADER,
-Mpi2DiagDataUploadHeader_t, MPI2_POINTER pMpi2DiagDataUploadHeader_t;
-
-
-/****************************************************************************
-* Toolbox ISTWI Read Write Tool
-****************************************************************************/
-
-/* Toolbox ISTWI Read Write Tool request message */
-typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST {
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U32 Reserved5; /* 0x0C */
- U32 Reserved6; /* 0x10 */
- U8 DevIndex; /* 0x14 */
- U8 Action; /* 0x15 */
- U8 SGLFlags; /* 0x16 */
- U8 Reserved7; /* 0x17 */
- U16 TxDataLength; /* 0x18 */
- U16 RxDataLength; /* 0x1A */
- U32 Reserved8; /* 0x1C */
- U32 Reserved9; /* 0x20 */
- U32 Reserved10; /* 0x24 */
- U32 Reserved11; /* 0x28 */
- U32 Reserved12; /* 0x2C */
- MPI2_SGE_SIMPLE_UNION SGL; /* 0x30 */
-} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST,
- MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST,
- Mpi2ToolboxIstwiReadWriteRequest_t,
- MPI2_POINTER pMpi2ToolboxIstwiReadWriteRequest_t;
-
-/* values for the Action field */
-#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01)
-#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02)
-#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03)
-#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10)
-#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11)
-#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12)
-
-/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
-
-
-/* Toolbox ISTWI Read Write Tool reply message */
-typedef struct _MPI2_TOOLBOX_ISTWI_REPLY {
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U8 DevIndex; /* 0x14 */
- U8 Action; /* 0x15 */
- U8 IstwiStatus; /* 0x16 */
- U8 Reserved6; /* 0x17 */
- U16 TxDataCount; /* 0x18 */
- U16 RxDataCount; /* 0x1A */
-} MPI2_TOOLBOX_ISTWI_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_REPLY,
- Mpi2ToolboxIstwiReply_t, MPI2_POINTER pMpi2ToolboxIstwiReply_t;
-
-
-/****************************************************************************
-* Toolbox Beacon Tool request
-****************************************************************************/
-
-typedef struct _MPI2_TOOLBOX_BEACON_REQUEST
-{
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U8 Reserved5; /* 0x0C */
- U8 PhysicalPort; /* 0x0D */
- U8 Reserved6; /* 0x0E */
- U8 Flags; /* 0x0F */
-} MPI2_TOOLBOX_BEACON_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_BEACON_REQUEST,
- Mpi2ToolboxBeaconRequest_t, MPI2_POINTER pMpi2ToolboxBeaconRequest_t;
-
-/* values for the Flags field */
-#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00)
-#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01)
-
-
-/****************************************************************************
-* Toolbox Diagnostic CLI Tool
-****************************************************************************/
-
-#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C)
-
-/* Toolbox Diagnostic CLI Tool request message */
-typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST {
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U8 SGLFlags; /* 0x0C */
- U8 Reserved5; /* 0x0D */
- U16 Reserved6; /* 0x0E */
- U32 DataLength; /* 0x10 */
- U8 DiagnosticCliCommand
- [MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH]; /* 0x14 */
- MPI2_SGE_SIMPLE_UNION SGL; /* 0x70 */
-} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
- MPI2_POINTER PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
- Mpi2ToolboxDiagnosticCliRequest_t,
- MPI2_POINTER pMpi2ToolboxDiagnosticCliRequest_t;
-
-/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
-
-
-/* Toolbox Diagnostic CLI Tool reply message */
-typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY {
- U8 Tool; /* 0x00 */
- U8 Reserved1; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U32 ReturnedDataLength; /* 0x14 */
-} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY,
- MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY,
- Mpi2ToolboxDiagnosticCliReply_t,
- MPI2_POINTER pMpi2ToolboxDiagnosticCliReply_t;
-
-
-/*****************************************************************************
-*
-* Diagnostic Buffer Messages
-*
-*****************************************************************************/
-
-
-/****************************************************************************
-* Diagnostic Buffer Post request
-****************************************************************************/
-
-typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
-{
- U8 ExtendedType; /* 0x00 */
- U8 BufferType; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U64 BufferAddress; /* 0x0C */
- U32 BufferLength; /* 0x14 */
- U32 Reserved5; /* 0x18 */
- U32 Reserved6; /* 0x1C */
- U32 Flags; /* 0x20 */
- U32 ProductSpecific[23]; /* 0x24 */
-} MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST,
- Mpi2DiagBufferPostRequest_t, MPI2_POINTER pMpi2DiagBufferPostRequest_t;
-
-/* values for the ExtendedType field */
-#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02)
-
-/* values for the BufferType field */
-#define MPI2_DIAG_BUF_TYPE_TRACE (0x00)
-#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01)
-#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02)
-/* count of the number of buffer types */
-#define MPI2_DIAG_BUF_TYPE_COUNT (0x03)
-
-/* values for the Flags field */
-#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002)
-#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001)
-
-
-/****************************************************************************
-* Diagnostic Buffer Post reply
-****************************************************************************/
-
-typedef struct _MPI2_DIAG_BUFFER_POST_REPLY
-{
- U8 ExtendedType; /* 0x00 */
- U8 BufferType; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
- U32 TransferLength; /* 0x14 */
-} MPI2_DIAG_BUFFER_POST_REPLY, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REPLY,
- Mpi2DiagBufferPostReply_t, MPI2_POINTER pMpi2DiagBufferPostReply_t;
-
-
-/****************************************************************************
-* Diagnostic Release request
-****************************************************************************/
-
-typedef struct _MPI2_DIAG_RELEASE_REQUEST
-{
- U8 Reserved1; /* 0x00 */
- U8 BufferType; /* 0x01 */
- U8 ChainOffset; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
-} MPI2_DIAG_RELEASE_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REQUEST,
- Mpi2DiagReleaseRequest_t, MPI2_POINTER pMpi2DiagReleaseRequest_t;
-
-
-/****************************************************************************
-* Diagnostic Buffer Post reply
-****************************************************************************/
-
-typedef struct _MPI2_DIAG_RELEASE_REPLY
-{
- U8 Reserved1; /* 0x00 */
- U8 BufferType; /* 0x01 */
- U8 MsgLength; /* 0x02 */
- U8 Function; /* 0x03 */
- U16 Reserved2; /* 0x04 */
- U8 Reserved3; /* 0x06 */
- U8 MsgFlags; /* 0x07 */
- U8 VP_ID; /* 0x08 */
- U8 VF_ID; /* 0x09 */
- U16 Reserved4; /* 0x0A */
- U16 Reserved5; /* 0x0C */
- U16 IOCStatus; /* 0x0E */
- U32 IOCLogInfo; /* 0x10 */
-} MPI2_DIAG_RELEASE_REPLY, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REPLY,
- Mpi2DiagReleaseReply_t, MPI2_POINTER pMpi2DiagReleaseReply_t;
-
-
-#endif
-
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
deleted file mode 100644
index efa0255491c2..000000000000
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ /dev/null
@@ -1,4192 +0,0 @@
-/*
- * This is the Fusion MPT base driver providing common API layer interface
- * for access to MPT (Message Passing Technology) firmware.
- *
- * This code is based on drivers/scsi/mpt2sas/mpt2_base.c
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * NO WARRANTY
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- * solely responsible for determining the appropriateness of using and
- * distributing the Program and assumes all risks associated with its
- * exercise of rights under this Agreement, including but not limited to
- * the risks and costs of program errors, damage to or loss of data,
- * programs or equipment, and unavailability or interruption of operations.
-
- * DISCLAIMER OF LIABILITY
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/kdev_t.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <linux/sort.h>
-#include <linux/io.h>
-#include <linux/time.h>
-#include <linux/aer.h>
-
-#include "mpt2sas_base.h"
-
-static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS];
-
-#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */
-
-static int max_queue_depth = -1;
-module_param(max_queue_depth, int, 0);
-MODULE_PARM_DESC(max_queue_depth, " max controller queue depth ");
-
-static int max_sgl_entries = -1;
-module_param(max_sgl_entries, int, 0);
-MODULE_PARM_DESC(max_sgl_entries, " max sg entries ");
-
-static int msix_disable = -1;
-module_param(msix_disable, int, 0);
-MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
-
-static int missing_delay[2] = {-1, -1};
-module_param_array(missing_delay, int, NULL, 0);
-MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay");
-
-/* diag_buffer_enable is bitwise
- * bit 0 set = TRACE
- * bit 1 set = SNAPSHOT
- * bit 2 set = EXTENDED
- *
- * Either bit can be set, or both
- */
-static int diag_buffer_enable;
-module_param(diag_buffer_enable, int, 0);
-MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers "
- "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
-
-int mpt2sas_fwfault_debug;
-MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault "
- "and halt firmware - (default=0)");
-
-static int disable_discovery = -1;
-module_param(disable_discovery, int, 0);
-MODULE_PARM_DESC(disable_discovery, " disable discovery ");
-
-/**
- * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug.
- *
- */
-static int
-_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
-{
- int ret = param_set_int(val, kp);
- struct MPT2SAS_ADAPTER *ioc;
-
- if (ret)
- return ret;
-
- printk(KERN_INFO "setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug);
- list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
- ioc->fwfault_debug = mpt2sas_fwfault_debug;
- return 0;
-}
-module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug,
- param_get_int, &mpt2sas_fwfault_debug, 0644);
-
-/**
- * _base_fault_reset_work - workq handling ioc fault conditions
- * @work: input argument, used to derive ioc
- * Context: sleep.
- *
- * Return nothing.
- */
-static void
-_base_fault_reset_work(struct work_struct *work)
-{
- struct MPT2SAS_ADAPTER *ioc =
- container_of(work, struct MPT2SAS_ADAPTER, fault_reset_work.work);
- unsigned long flags;
- u32 doorbell;
- int rc;
-
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->shost_recovery)
- goto rearm_timer;
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
-
- doorbell = mpt2sas_base_get_iocstate(ioc, 0);
- if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
- rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- printk(MPT2SAS_WARN_FMT "%s: hard reset: %s\n", ioc->name,
- __func__, (rc == 0) ? "success" : "failed");
- doorbell = mpt2sas_base_get_iocstate(ioc, 0);
- if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
- mpt2sas_base_fault_info(ioc, doorbell &
- MPI2_DOORBELL_DATA_MASK);
- }
-
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- rearm_timer:
- if (ioc->fault_reset_work_q)
- queue_delayed_work(ioc->fault_reset_work_q,
- &ioc->fault_reset_work,
- msecs_to_jiffies(FAULT_POLLING_INTERVAL));
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
-}
-
-/**
- * mpt2sas_base_start_watchdog - start the fault_reset_work_q
- * @ioc: per adapter object
- * Context: sleep.
- *
- * Return nothing.
- */
-void
-mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc)
-{
- unsigned long flags;
-
- if (ioc->fault_reset_work_q)
- return;
-
- /* initialize fault polling */
- INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work);
- snprintf(ioc->fault_reset_work_q_name,
- sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id);
- ioc->fault_reset_work_q =
- create_singlethread_workqueue(ioc->fault_reset_work_q_name);
- if (!ioc->fault_reset_work_q) {
- printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n",
- ioc->name, __func__, __LINE__);
- return;
- }
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->fault_reset_work_q)
- queue_delayed_work(ioc->fault_reset_work_q,
- &ioc->fault_reset_work,
- msecs_to_jiffies(FAULT_POLLING_INTERVAL));
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
-}
-
-/**
- * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q
- * @ioc: per adapter object
- * Context: sleep.
- *
- * Return nothing.
- */
-void
-mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc)
-{
- unsigned long flags;
- struct workqueue_struct *wq;
-
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- wq = ioc->fault_reset_work_q;
- ioc->fault_reset_work_q = NULL;
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
- if (wq) {
- if (!cancel_delayed_work(&ioc->fault_reset_work))
- flush_workqueue(wq);
- destroy_workqueue(wq);
- }
-}
-
-/**
- * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
- * @ioc: per adapter object
- * @fault_code: fault code
- *
- * Return nothing.
- */
-void
-mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
-{
- printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
- ioc->name, fault_code);
-}
-
-/**
- * mpt2sas_halt_firmware - halt's mpt controller firmware
- * @ioc: per adapter object
- *
- * For debugging timeout related issues. Writing 0xCOFFEE00
- * to the doorbell register will halt controller firmware. With
- * the purpose to stop both driver and firmware, the enduser can
- * obtain a ring buffer from controller UART.
- */
-void
-mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc)
-{
- u32 doorbell;
-
- if (!ioc->fwfault_debug)
- return;
-
- dump_stack();
-
- doorbell = readl(&ioc->chip->Doorbell);
- if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
- mpt2sas_base_fault_info(ioc , doorbell);
- else {
- writel(0xC0FFEE00, &ioc->chip->Doorbell);
- printk(MPT2SAS_ERR_FMT "Firmware is halted due to command "
- "timeout\n", ioc->name);
- }
-
- panic("panic in %s\n", __func__);
-}
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _base_sas_ioc_info - verbose translation of the ioc status
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @request_hdr: request mf
- *
- * Return nothing.
- */
-static void
-_base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
- MPI2RequestHeader_t *request_hdr)
-{
- u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- char *desc = NULL;
- u16 frame_sz;
- char *func_str = NULL;
-
- /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */
- if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
- request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
- request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION)
- return;
-
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- return;
-
- switch (ioc_status) {
-
-/****************************************************************************
-* Common IOCStatus values for all replies
-****************************************************************************/
-
- case MPI2_IOCSTATUS_INVALID_FUNCTION:
- desc = "invalid function";
- break;
- case MPI2_IOCSTATUS_BUSY:
- desc = "busy";
- break;
- case MPI2_IOCSTATUS_INVALID_SGL:
- desc = "invalid sgl";
- break;
- case MPI2_IOCSTATUS_INTERNAL_ERROR:
- desc = "internal error";
- break;
- case MPI2_IOCSTATUS_INVALID_VPID:
- desc = "invalid vpid";
- break;
- case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
- desc = "insufficient resources";
- break;
- case MPI2_IOCSTATUS_INVALID_FIELD:
- desc = "invalid field";
- break;
- case MPI2_IOCSTATUS_INVALID_STATE:
- desc = "invalid state";
- break;
- case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED:
- desc = "op state not supported";
- break;
-
-/****************************************************************************
-* Config IOCStatus values
-****************************************************************************/
-
- case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION:
- desc = "config invalid action";
- break;
- case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE:
- desc = "config invalid type";
- break;
- case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE:
- desc = "config invalid page";
- break;
- case MPI2_IOCSTATUS_CONFIG_INVALID_DATA:
- desc = "config invalid data";
- break;
- case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS:
- desc = "config no defaults";
- break;
- case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT:
- desc = "config cant commit";
- break;
-
-/****************************************************************************
-* SCSI IO Reply
-****************************************************************************/
-
- case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
- case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
- case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
- case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
- case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
- case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
- case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
- case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
- case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
- case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
- case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
- case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
- break;
-
-/****************************************************************************
-* For use by SCSI Initiator and SCSI Target end-to-end data protection
-****************************************************************************/
-
- case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
- desc = "eedp guard error";
- break;
- case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
- desc = "eedp ref tag error";
- break;
- case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
- desc = "eedp app tag error";
- break;
-
-/****************************************************************************
-* SCSI Target values
-****************************************************************************/
-
- case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX:
- desc = "target invalid io index";
- break;
- case MPI2_IOCSTATUS_TARGET_ABORTED:
- desc = "target aborted";
- break;
- case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE:
- desc = "target no conn retryable";
- break;
- case MPI2_IOCSTATUS_TARGET_NO_CONNECTION:
- desc = "target no connection";
- break;
- case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH:
- desc = "target xfer count mismatch";
- break;
- case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR:
- desc = "target data offset error";
- break;
- case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA:
- desc = "target too much write data";
- break;
- case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT:
- desc = "target iu too short";
- break;
- case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT:
- desc = "target ack nak timeout";
- break;
- case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED:
- desc = "target nak received";
- break;
-
-/****************************************************************************
-* Serial Attached SCSI values
-****************************************************************************/
-
- case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED:
- desc = "smp request failed";
- break;
- case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN:
- desc = "smp data overrun";
- break;
-
-/****************************************************************************
-* Diagnostic Buffer Post / Diagnostic Release values
-****************************************************************************/
-
- case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED:
- desc = "diagnostic released";
- break;
- default:
- break;
- }
-
- if (!desc)
- return;
-
- switch (request_hdr->Function) {
- case MPI2_FUNCTION_CONFIG:
- frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size;
- func_str = "config_page";
- break;
- case MPI2_FUNCTION_SCSI_TASK_MGMT:
- frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t);
- func_str = "task_mgmt";
- break;
- case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
- frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t);
- func_str = "sas_iounit_ctl";
- break;
- case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR:
- frame_sz = sizeof(Mpi2SepRequest_t);
- func_str = "enclosure";
- break;
- case MPI2_FUNCTION_IOC_INIT:
- frame_sz = sizeof(Mpi2IOCInitRequest_t);
- func_str = "ioc_init";
- break;
- case MPI2_FUNCTION_PORT_ENABLE:
- frame_sz = sizeof(Mpi2PortEnableRequest_t);
- func_str = "port_enable";
- break;
- case MPI2_FUNCTION_SMP_PASSTHROUGH:
- frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size;
- func_str = "smp_passthru";
- break;
- default:
- frame_sz = 32;
- func_str = "unknown";
- break;
- }
-
- printk(MPT2SAS_WARN_FMT "ioc_status: %s(0x%04x), request(0x%p),"
- " (%s)\n", ioc->name, desc, ioc_status, request_hdr, func_str);
-
- _debug_dump_mf(request_hdr, frame_sz/4);
-}
-
-/**
- * _base_display_event_data - verbose translation of firmware asyn events
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- *
- * Return nothing.
- */
-static void
-_base_display_event_data(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventNotificationReply_t *mpi_reply)
-{
- char *desc = NULL;
- u16 event;
-
- if (!(ioc->logging_level & MPT_DEBUG_EVENTS))
- return;
-
- event = le16_to_cpu(mpi_reply->Event);
-
- switch (event) {
- case MPI2_EVENT_LOG_DATA:
- desc = "Log Data";
- break;
- case MPI2_EVENT_STATE_CHANGE:
- desc = "Status Change";
- break;
- case MPI2_EVENT_HARD_RESET_RECEIVED:
- desc = "Hard Reset Received";
- break;
- case MPI2_EVENT_EVENT_CHANGE:
- desc = "Event Change";
- break;
- case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
- desc = "Device Status Change";
- break;
- case MPI2_EVENT_IR_OPERATION_STATUS:
- if (!ioc->hide_ir_msg)
- desc = "IR Operation Status";
- break;
- case MPI2_EVENT_SAS_DISCOVERY:
- {
- Mpi2EventDataSasDiscovery_t *event_data =
- (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData;
- printk(MPT2SAS_INFO_FMT "Discovery: (%s)", ioc->name,
- (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ?
- "start" : "stop");
- if (event_data->DiscoveryStatus)
- printk("discovery_status(0x%08x)",
- le32_to_cpu(event_data->DiscoveryStatus));
- printk("\n");
- return;
- }
- case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
- desc = "SAS Broadcast Primitive";
- break;
- case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
- desc = "SAS Init Device Status Change";
- break;
- case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW:
- desc = "SAS Init Table Overflow";
- break;
- case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
- desc = "SAS Topology Change List";
- break;
- case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
- desc = "SAS Enclosure Device Status Change";
- break;
- case MPI2_EVENT_IR_VOLUME:
- if (!ioc->hide_ir_msg)
- desc = "IR Volume";
- break;
- case MPI2_EVENT_IR_PHYSICAL_DISK:
- if (!ioc->hide_ir_msg)
- desc = "IR Physical Disk";
- break;
- case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
- if (!ioc->hide_ir_msg)
- desc = "IR Configuration Change List";
- break;
- case MPI2_EVENT_LOG_ENTRY_ADDED:
- if (!ioc->hide_ir_msg)
- desc = "Log Entry Added";
- break;
- }
-
- if (!desc)
- return;
-
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, desc);
-}
-#endif
-
-/**
- * _base_sas_log_info - verbose translation of firmware log info
- * @ioc: per adapter object
- * @log_info: log info
- *
- * Return nothing.
- */
-static void
-_base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
-{
- union loginfo_type {
- u32 loginfo;
- struct {
- u32 subcode:16;
- u32 code:8;
- u32 originator:4;
- u32 bus_type:4;
- } dw;
- };
- union loginfo_type sas_loginfo;
- char *originator_str = NULL;
-
- sas_loginfo.loginfo = log_info;
- if (sas_loginfo.dw.bus_type != 3 /*SAS*/)
- return;
-
- /* each nexus loss loginfo */
- if (log_info == 0x31170000)
- return;
-
- /* eat the loginfos associated with task aborts */
- if (ioc->ignore_loginfos && (log_info == 30050000 || log_info ==
- 0x31140000 || log_info == 0x31130000))
- return;
-
- switch (sas_loginfo.dw.originator) {
- case 0:
- originator_str = "IOP";
- break;
- case 1:
- originator_str = "PL";
- break;
- case 2:
- if (!ioc->hide_ir_msg)
- originator_str = "IR";
- else
- originator_str = "WarpDrive";
- break;
- }
-
- printk(MPT2SAS_WARN_FMT "log_info(0x%08x): originator(%s), "
- "code(0x%02x), sub_code(0x%04x)\n", ioc->name, log_info,
- originator_str, sas_loginfo.dw.code,
- sas_loginfo.dw.subcode);
-}
-
-/**
- * _base_display_reply_info -
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- *
- * Return nothing.
- */
-static void
-_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply)
-{
- MPI2DefaultReply_t *mpi_reply;
- u16 ioc_status;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if ((ioc_status & MPI2_IOCSTATUS_MASK) &&
- (ioc->logging_level & MPT_DEBUG_REPLY)) {
- _base_sas_ioc_info(ioc , mpi_reply,
- mpt2sas_base_get_msg_frame(ioc, smid));
- }
-#endif
- if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
- _base_sas_log_info(ioc, le32_to_cpu(mpi_reply->IOCLogInfo));
-}
-
-/**
- * mpt2sas_base_done - base internal command completion routine
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-u8
-mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply)
-{
- MPI2DefaultReply_t *mpi_reply;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK)
- return 1;
-
- if (ioc->base_cmds.status == MPT2_CMD_NOT_USED)
- return 1;
-
- ioc->base_cmds.status |= MPT2_CMD_COMPLETE;
- if (mpi_reply) {
- ioc->base_cmds.status |= MPT2_CMD_REPLY_VALID;
- memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
- }
- ioc->base_cmds.status &= ~MPT2_CMD_PENDING;
- complete(&ioc->base_cmds.done);
- return 1;
-}
-
-/**
- * _base_async_event - main callback handler for firmware asyn events
- * @ioc: per adapter object
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply)
-{
- Mpi2EventNotificationReply_t *mpi_reply;
- Mpi2EventAckRequest_t *ack_request;
- u16 smid;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- if (!mpi_reply)
- return 1;
- if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION)
- return 1;
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- _base_display_event_data(ioc, mpi_reply);
-#endif
- if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED))
- goto out;
- smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- goto out;
- }
-
- ack_request = mpt2sas_base_get_msg_frame(ioc, smid);
- memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t));
- ack_request->Function = MPI2_FUNCTION_EVENT_ACK;
- ack_request->Event = mpi_reply->Event;
- ack_request->EventContext = mpi_reply->EventContext;
- ack_request->VF_ID = 0; /* TODO */
- ack_request->VP_ID = 0;
- mpt2sas_base_put_smid_default(ioc, smid);
-
- out:
-
- /* scsih callback handler */
- mpt2sas_scsih_event_callback(ioc, msix_index, reply);
-
- /* ctl callback handler */
- mpt2sas_ctl_event_callback(ioc, msix_index, reply);
-
- return 1;
-}
-
-/**
- * _base_get_cb_idx - obtain the callback index
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Return callback index.
- */
-static u8
-_base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- int i;
- u8 cb_idx;
-
- if (smid < ioc->hi_priority_smid) {
- i = smid - 1;
- cb_idx = ioc->scsi_lookup[i].cb_idx;
- } else if (smid < ioc->internal_smid) {
- i = smid - ioc->hi_priority_smid;
- cb_idx = ioc->hpr_lookup[i].cb_idx;
- } else if (smid <= ioc->hba_queue_depth) {
- i = smid - ioc->internal_smid;
- cb_idx = ioc->internal_lookup[i].cb_idx;
- } else
- cb_idx = 0xFF;
- return cb_idx;
-}
-
-/**
- * _base_mask_interrupts - disable interrupts
- * @ioc: per adapter object
- *
- * Disabling ResetIRQ, Reply and Doorbell Interrupts
- *
- * Return nothing.
- */
-static void
-_base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc)
-{
- u32 him_register;
-
- ioc->mask_interrupts = 1;
- him_register = readl(&ioc->chip->HostInterruptMask);
- him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK;
- writel(him_register, &ioc->chip->HostInterruptMask);
- readl(&ioc->chip->HostInterruptMask);
-}
-
-/**
- * _base_unmask_interrupts - enable interrupts
- * @ioc: per adapter object
- *
- * Enabling only Reply Interrupts
- *
- * Return nothing.
- */
-static void
-_base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc)
-{
- u32 him_register;
-
- him_register = readl(&ioc->chip->HostInterruptMask);
- him_register &= ~MPI2_HIM_RIM;
- writel(him_register, &ioc->chip->HostInterruptMask);
- ioc->mask_interrupts = 0;
-}
-
-union reply_descriptor {
- u64 word;
- struct {
- u32 low;
- u32 high;
- } u;
-};
-
-/**
- * _base_interrupt - MPT adapter (IOC) specific interrupt handler.
- * @irq: irq number (not used)
- * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure
- * @r: pt_regs pointer (not used)
- *
- * Return IRQ_HANDLE if processed, else IRQ_NONE.
- */
-static irqreturn_t
-_base_interrupt(int irq, void *bus_id)
-{
- union reply_descriptor rd;
- u32 completed_cmds;
- u8 request_desript_type;
- u16 smid;
- u8 cb_idx;
- u32 reply;
- u8 msix_index;
- struct MPT2SAS_ADAPTER *ioc = bus_id;
- Mpi2ReplyDescriptorsUnion_t *rpf;
- u8 rc;
-
- if (ioc->mask_interrupts)
- return IRQ_NONE;
-
- rpf = &ioc->reply_post_free[ioc->reply_post_host_index];
- request_desript_type = rpf->Default.ReplyFlags
- & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
- if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
- return IRQ_NONE;
-
- completed_cmds = 0;
- cb_idx = 0xFF;
- do {
- rd.word = rpf->Words;
- if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX)
- goto out;
- reply = 0;
- cb_idx = 0xFF;
- smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1);
- msix_index = rpf->Default.MSIxIndex;
- if (request_desript_type ==
- MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) {
- reply = le32_to_cpu
- (rpf->AddressReply.ReplyFrameAddress);
- if (reply > ioc->reply_dma_max_address ||
- reply < ioc->reply_dma_min_address)
- reply = 0;
- } else if (request_desript_type ==
- MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER)
- goto next;
- else if (request_desript_type ==
- MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS)
- goto next;
- if (smid)
- cb_idx = _base_get_cb_idx(ioc, smid);
- if (smid && cb_idx != 0xFF) {
- rc = mpt_callbacks[cb_idx](ioc, smid, msix_index,
- reply);
- if (reply)
- _base_display_reply_info(ioc, smid, msix_index,
- reply);
- if (rc)
- mpt2sas_base_free_smid(ioc, smid);
- }
- if (!smid)
- _base_async_event(ioc, msix_index, reply);
-
- /* reply free queue handling */
- if (reply) {
- ioc->reply_free_host_index =
- (ioc->reply_free_host_index ==
- (ioc->reply_free_queue_depth - 1)) ?
- 0 : ioc->reply_free_host_index + 1;
- ioc->reply_free[ioc->reply_free_host_index] =
- cpu_to_le32(reply);
- wmb();
- writel(ioc->reply_free_host_index,
- &ioc->chip->ReplyFreeHostIndex);
- }
-
- next:
-
- rpf->Words = ULLONG_MAX;
- ioc->reply_post_host_index = (ioc->reply_post_host_index ==
- (ioc->reply_post_queue_depth - 1)) ? 0 :
- ioc->reply_post_host_index + 1;
- request_desript_type =
- ioc->reply_post_free[ioc->reply_post_host_index].Default.
- ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
- completed_cmds++;
- if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
- goto out;
- if (!ioc->reply_post_host_index)
- rpf = ioc->reply_post_free;
- else
- rpf++;
- } while (1);
-
- out:
-
- if (!completed_cmds)
- return IRQ_NONE;
-
- wmb();
- writel(ioc->reply_post_host_index, &ioc->chip->ReplyPostHostIndex);
- return IRQ_HANDLED;
-}
-
-/**
- * mpt2sas_base_release_callback_handler - clear interrupt callback handler
- * @cb_idx: callback index
- *
- * Return nothing.
- */
-void
-mpt2sas_base_release_callback_handler(u8 cb_idx)
-{
- mpt_callbacks[cb_idx] = NULL;
-}
-
-/**
- * mpt2sas_base_register_callback_handler - obtain index for the interrupt callback handler
- * @cb_func: callback function
- *
- * Returns cb_func.
- */
-u8
-mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func)
-{
- u8 cb_idx;
-
- for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--)
- if (mpt_callbacks[cb_idx] == NULL)
- break;
-
- mpt_callbacks[cb_idx] = cb_func;
- return cb_idx;
-}
-
-/**
- * mpt2sas_base_initialize_callback_handler - initialize the interrupt callback handler
- *
- * Return nothing.
- */
-void
-mpt2sas_base_initialize_callback_handler(void)
-{
- u8 cb_idx;
-
- for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++)
- mpt2sas_base_release_callback_handler(cb_idx);
-}
-
-/**
- * mpt2sas_base_build_zero_len_sge - build zero length sg entry
- * @ioc: per adapter object
- * @paddr: virtual address for SGE
- *
- * Create a zero length scatter gather entry to insure the IOCs hardware has
- * something to use if the target device goes brain dead and tries
- * to send data even when none is asked for.
- *
- * Return nothing.
- */
-void
-mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr)
-{
- u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST |
- MPI2_SGE_FLAGS_SIMPLE_ELEMENT) <<
- MPI2_SGE_FLAGS_SHIFT);
- ioc->base_add_sg_single(paddr, flags_length, -1);
-}
-
-/**
- * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr.
- * @paddr: virtual address for SGE
- * @flags_length: SGE flags and data transfer length
- * @dma_addr: Physical address
- *
- * Return nothing.
- */
-static void
-_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr)
-{
- Mpi2SGESimple32_t *sgel = paddr;
-
- flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING |
- MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
- sgel->FlagsLength = cpu_to_le32(flags_length);
- sgel->Address = cpu_to_le32(dma_addr);
-}
-
-
-/**
- * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr.
- * @paddr: virtual address for SGE
- * @flags_length: SGE flags and data transfer length
- * @dma_addr: Physical address
- *
- * Return nothing.
- */
-static void
-_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr)
-{
- Mpi2SGESimple64_t *sgel = paddr;
-
- flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
- MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
- sgel->FlagsLength = cpu_to_le32(flags_length);
- sgel->Address = cpu_to_le64(dma_addr);
-}
-
-#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10))
-
-/**
- * _base_config_dma_addressing - set dma addressing
- * @ioc: per adapter object
- * @pdev: PCI device struct
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_config_dma_addressing(struct MPT2SAS_ADAPTER *ioc, struct pci_dev *pdev)
-{
- struct sysinfo s;
- char *desc = NULL;
-
- if (sizeof(dma_addr_t) > 4) {
- const uint64_t required_mask =
- dma_get_required_mask(&pdev->dev);
- if ((required_mask > DMA_BIT_MASK(32)) && !pci_set_dma_mask(pdev,
- DMA_BIT_MASK(64)) && !pci_set_consistent_dma_mask(pdev,
- DMA_BIT_MASK(64))) {
- ioc->base_add_sg_single = &_base_add_sg_single_64;
- ioc->sge_size = sizeof(Mpi2SGESimple64_t);
- desc = "64";
- goto out;
- }
- }
-
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
- && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
- ioc->base_add_sg_single = &_base_add_sg_single_32;
- ioc->sge_size = sizeof(Mpi2SGESimple32_t);
- desc = "32";
- } else
- return -ENODEV;
-
- out:
- si_meminfo(&s);
- printk(MPT2SAS_INFO_FMT "%s BIT PCI BUS DMA ADDRESSING SUPPORTED, "
- "total mem (%ld kB)\n", ioc->name, desc, convert_to_kb(s.totalram));
-
- return 0;
-}
-
-/**
- * _base_save_msix_table - backup msix vector table
- * @ioc: per adapter object
- *
- * This address an errata where diag reset clears out the table
- */
-static void
-_base_save_msix_table(struct MPT2SAS_ADAPTER *ioc)
-{
- int i;
-
- if (!ioc->msix_enable || ioc->msix_table_backup == NULL)
- return;
-
- for (i = 0; i < ioc->msix_vector_count; i++)
- ioc->msix_table_backup[i] = ioc->msix_table[i];
-}
-
-/**
- * _base_restore_msix_table - this restores the msix vector table
- * @ioc: per adapter object
- *
- */
-static void
-_base_restore_msix_table(struct MPT2SAS_ADAPTER *ioc)
-{
- int i;
-
- if (!ioc->msix_enable || ioc->msix_table_backup == NULL)
- return;
-
- for (i = 0; i < ioc->msix_vector_count; i++)
- ioc->msix_table[i] = ioc->msix_table_backup[i];
-}
-
-/**
- * _base_check_enable_msix - checks MSIX capabable.
- * @ioc: per adapter object
- *
- * Check to see if card is capable of MSIX, and set number
- * of available msix vectors
- */
-static int
-_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
-{
- int base;
- u16 message_control;
- u32 msix_table_offset;
-
- base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
- if (!base) {
- dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
- "supported\n", ioc->name));
- return -EINVAL;
- }
-
- /* get msix vector count */
- pci_read_config_word(ioc->pdev, base + 2, &message_control);
- ioc->msix_vector_count = (message_control & 0x3FF) + 1;
-
- /* get msix table */
- pci_read_config_dword(ioc->pdev, base + 4, &msix_table_offset);
- msix_table_offset &= 0xFFFFFFF8;
- ioc->msix_table = (u32 *)((void *)ioc->chip + msix_table_offset);
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, "
- "vector_count(%d), table_offset(0x%08x), table(%p)\n", ioc->name,
- ioc->msix_vector_count, msix_table_offset, ioc->msix_table));
- return 0;
-}
-
-/**
- * _base_disable_msix - disables msix
- * @ioc: per adapter object
- *
- */
-static void
-_base_disable_msix(struct MPT2SAS_ADAPTER *ioc)
-{
- if (ioc->msix_enable) {
- pci_disable_msix(ioc->pdev);
- kfree(ioc->msix_table_backup);
- ioc->msix_table_backup = NULL;
- ioc->msix_enable = 0;
- }
-}
-
-/**
- * _base_enable_msix - enables msix, failback to io_apic
- * @ioc: per adapter object
- *
- */
-static int
-_base_enable_msix(struct MPT2SAS_ADAPTER *ioc)
-{
- struct msix_entry entries;
- int r;
- u8 try_msix = 0;
-
- if (msix_disable == -1 || msix_disable == 0)
- try_msix = 1;
-
- if (!try_msix)
- goto try_ioapic;
-
- if (_base_check_enable_msix(ioc) != 0)
- goto try_ioapic;
-
- ioc->msix_table_backup = kcalloc(ioc->msix_vector_count,
- sizeof(u32), GFP_KERNEL);
- if (!ioc->msix_table_backup) {
- dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for "
- "msix_table_backup failed!!!\n", ioc->name));
- goto try_ioapic;
- }
-
- memset(&entries, 0, sizeof(struct msix_entry));
- r = pci_enable_msix(ioc->pdev, &entries, 1);
- if (r) {
- dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "pci_enable_msix "
- "failed (r=%d) !!!\n", ioc->name, r));
- goto try_ioapic;
- }
-
- r = request_irq(entries.vector, _base_interrupt, IRQF_SHARED,
- ioc->name, ioc);
- if (r) {
- dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "unable to allocate "
- "interrupt %d !!!\n", ioc->name, entries.vector));
- pci_disable_msix(ioc->pdev);
- goto try_ioapic;
- }
-
- ioc->pci_irq = entries.vector;
- ioc->msix_enable = 1;
- return 0;
-
-/* failback to io_apic interrupt routing */
- try_ioapic:
-
- r = request_irq(ioc->pdev->irq, _base_interrupt, IRQF_SHARED,
- ioc->name, ioc);
- if (r) {
- printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n",
- ioc->name, ioc->pdev->irq);
- r = -EBUSY;
- goto out_fail;
- }
-
- ioc->pci_irq = ioc->pdev->irq;
- return 0;
-
- out_fail:
- return r;
-}
-
-/**
- * mpt2sas_base_map_resources - map in controller resources (io/irq/memap)
- * @ioc: per adapter object
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
-{
- struct pci_dev *pdev = ioc->pdev;
- u32 memap_sz;
- u32 pio_sz;
- int i, r = 0;
- u64 pio_chip = 0;
- u64 chip_phys = 0;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n",
- ioc->name, __func__));
-
- ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM);
- if (pci_enable_device_mem(pdev)) {
- printk(MPT2SAS_WARN_FMT "pci_enable_device_mem: "
- "failed\n", ioc->name);
- return -ENODEV;
- }
-
-
- if (pci_request_selected_regions(pdev, ioc->bars,
- MPT2SAS_DRIVER_NAME)) {
- printk(MPT2SAS_WARN_FMT "pci_request_selected_regions: "
- "failed\n", ioc->name);
- r = -ENODEV;
- goto out_fail;
- }
-
- /* AER (Advanced Error Reporting) hooks */
- pci_enable_pcie_error_reporting(pdev);
-
- pci_set_master(pdev);
-
- if (_base_config_dma_addressing(ioc, pdev) != 0) {
- printk(MPT2SAS_WARN_FMT "no suitable DMA mask for %s\n",
- ioc->name, pci_name(pdev));
- r = -ENODEV;
- goto out_fail;
- }
-
- for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) {
- if (pci_resource_flags(pdev, i) & IORESOURCE_IO) {
- if (pio_sz)
- continue;
- pio_chip = (u64)pci_resource_start(pdev, i);
- pio_sz = pci_resource_len(pdev, i);
- } else {
- if (memap_sz)
- continue;
- /* verify memory resource is valid before using */
- if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
- ioc->chip_phys = pci_resource_start(pdev, i);
- chip_phys = (u64)ioc->chip_phys;
- memap_sz = pci_resource_len(pdev, i);
- ioc->chip = ioremap(ioc->chip_phys, memap_sz);
- if (ioc->chip == NULL) {
- printk(MPT2SAS_ERR_FMT "unable to map "
- "adapter memory!\n", ioc->name);
- r = -EINVAL;
- goto out_fail;
- }
- }
- }
- }
-
- _base_mask_interrupts(ioc);
- r = _base_enable_msix(ioc);
- if (r)
- goto out_fail;
-
- printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n",
- ioc->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" :
- "IO-APIC enabled"), ioc->pci_irq);
- printk(MPT2SAS_INFO_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n",
- ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz);
- printk(MPT2SAS_INFO_FMT "ioport(0x%016llx), size(%d)\n",
- ioc->name, (unsigned long long)pio_chip, pio_sz);
-
- /* Save PCI configuration state for recovery from PCI AER/EEH errors */
- pci_save_state(pdev);
-
- return 0;
-
- out_fail:
- if (ioc->chip_phys)
- iounmap(ioc->chip);
- ioc->chip_phys = 0;
- ioc->pci_irq = -1;
- pci_release_selected_regions(ioc->pdev, ioc->bars);
- pci_disable_pcie_error_reporting(pdev);
- pci_disable_device(pdev);
- return r;
-}
-
-/**
- * mpt2sas_base_get_msg_frame - obtain request mf pointer
- * @ioc: per adapter object
- * @smid: system request message index(smid zero is invalid)
- *
- * Returns virt pointer to message frame.
- */
-void *
-mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- return (void *)(ioc->request + (smid * ioc->request_sz));
-}
-
-/**
- * mpt2sas_base_get_sense_buffer - obtain a sense buffer assigned to a mf request
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Returns virt pointer to sense buffer.
- */
-void *
-mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
-}
-
-/**
- * mpt2sas_base_get_sense_buffer_dma - obtain a sense buffer assigned to a mf request
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Returns phys pointer to the low 32bit address of the sense buffer.
- */
-__le32
-mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- return cpu_to_le32(ioc->sense_dma +
- ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
-}
-
-/**
- * mpt2sas_base_get_reply_virt_addr - obtain reply frames virt address
- * @ioc: per adapter object
- * @phys_addr: lower 32 physical addr of the reply
- *
- * Converts 32bit lower physical addr into a virt address.
- */
-void *
-mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr)
-{
- if (!phys_addr)
- return NULL;
- return ioc->reply + (phys_addr - (u32)ioc->reply_dma);
-}
-
-/**
- * mpt2sas_base_get_smid - obtain a free smid from internal queue
- * @ioc: per adapter object
- * @cb_idx: callback index
- *
- * Returns smid (zero is invalid)
- */
-u16
-mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx)
-{
- unsigned long flags;
- struct request_tracker *request;
- u16 smid;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- if (list_empty(&ioc->internal_free_list)) {
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- printk(MPT2SAS_ERR_FMT "%s: smid not available\n",
- ioc->name, __func__);
- return 0;
- }
-
- request = list_entry(ioc->internal_free_list.next,
- struct request_tracker, tracker_list);
- request->cb_idx = cb_idx;
- smid = request->smid;
- list_del(&request->tracker_list);
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return smid;
-}
-
-/**
- * mpt2sas_base_get_smid_scsiio - obtain a free smid from scsiio queue
- * @ioc: per adapter object
- * @cb_idx: callback index
- * @scmd: pointer to scsi command object
- *
- * Returns smid (zero is invalid)
- */
-u16
-mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx,
- struct scsi_cmnd *scmd)
-{
- unsigned long flags;
- struct scsiio_tracker *request;
- u16 smid;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- if (list_empty(&ioc->free_list)) {
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- printk(MPT2SAS_ERR_FMT "%s: smid not available\n",
- ioc->name, __func__);
- return 0;
- }
-
- request = list_entry(ioc->free_list.next,
- struct scsiio_tracker, tracker_list);
- request->scmd = scmd;
- request->cb_idx = cb_idx;
- smid = request->smid;
- list_del(&request->tracker_list);
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return smid;
-}
-
-/**
- * mpt2sas_base_get_smid_hpr - obtain a free smid from hi-priority queue
- * @ioc: per adapter object
- * @cb_idx: callback index
- *
- * Returns smid (zero is invalid)
- */
-u16
-mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx)
-{
- unsigned long flags;
- struct request_tracker *request;
- u16 smid;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- if (list_empty(&ioc->hpr_free_list)) {
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return 0;
- }
-
- request = list_entry(ioc->hpr_free_list.next,
- struct request_tracker, tracker_list);
- request->cb_idx = cb_idx;
- smid = request->smid;
- list_del(&request->tracker_list);
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return smid;
-}
-
-
-/**
- * mpt2sas_base_free_smid - put smid back on free_list
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Return nothing.
- */
-void
-mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- unsigned long flags;
- int i;
- struct chain_tracker *chain_req, *next;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- if (smid < ioc->hi_priority_smid) {
- /* scsiio queue */
- i = smid - 1;
- if (!list_empty(&ioc->scsi_lookup[i].chain_list)) {
- list_for_each_entry_safe(chain_req, next,
- &ioc->scsi_lookup[i].chain_list, tracker_list) {
- list_del_init(&chain_req->tracker_list);
- list_add_tail(&chain_req->tracker_list,
- &ioc->free_chain_list);
- }
- }
- ioc->scsi_lookup[i].cb_idx = 0xFF;
- ioc->scsi_lookup[i].scmd = NULL;
- ioc->scsi_lookup[i].direct_io = 0;
- list_add_tail(&ioc->scsi_lookup[i].tracker_list,
- &ioc->free_list);
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-
- /*
- * See _wait_for_commands_to_complete() call with regards
- * to this code.
- */
- if (ioc->shost_recovery && ioc->pending_io_count) {
- if (ioc->pending_io_count == 1)
- wake_up(&ioc->reset_wq);
- ioc->pending_io_count--;
- }
- return;
- } else if (smid < ioc->internal_smid) {
- /* hi-priority */
- i = smid - ioc->hi_priority_smid;
- ioc->hpr_lookup[i].cb_idx = 0xFF;
- list_add_tail(&ioc->hpr_lookup[i].tracker_list,
- &ioc->hpr_free_list);
- } else if (smid <= ioc->hba_queue_depth) {
- /* internal queue */
- i = smid - ioc->internal_smid;
- ioc->internal_lookup[i].cb_idx = 0xFF;
- list_add_tail(&ioc->internal_lookup[i].tracker_list,
- &ioc->internal_free_list);
- }
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-}
-
-/**
- * _base_writeq - 64 bit write to MMIO
- * @ioc: per adapter object
- * @b: data payload
- * @addr: address in MMIO space
- * @writeq_lock: spin lock
- *
- * Glue for handling an atomic 64 bit word to MMIO. This special handling takes
- * care of 32 bit environment where its not quarenteed to send the entire word
- * in one transfer.
- */
-#ifndef writeq
-static inline void _base_writeq(__u64 b, volatile void __iomem *addr,
- spinlock_t *writeq_lock)
-{
- unsigned long flags;
- __u64 data_out = cpu_to_le64(b);
-
- spin_lock_irqsave(writeq_lock, flags);
- writel((u32)(data_out), addr);
- writel((u32)(data_out >> 32), (addr + 4));
- spin_unlock_irqrestore(writeq_lock, flags);
-}
-#else
-static inline void _base_writeq(__u64 b, volatile void __iomem *addr,
- spinlock_t *writeq_lock)
-{
- writeq(cpu_to_le64(b), addr);
-}
-#endif
-
-/**
- * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware
- * @ioc: per adapter object
- * @smid: system request message index
- * @handle: device handle
- *
- * Return nothing.
- */
-void
-mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle)
-{
- Mpi2RequestDescriptorUnion_t descriptor;
- u64 *request = (u64 *)&descriptor;
-
-
- descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
- descriptor.SCSIIO.MSIxIndex = 0; /* TODO */
- descriptor.SCSIIO.SMID = cpu_to_le16(smid);
- descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
- descriptor.SCSIIO.LMID = 0;
- _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
- &ioc->scsi_lookup_lock);
-}
-
-
-/**
- * mpt2sas_base_put_smid_hi_priority - send Task Management request to firmware
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Return nothing.
- */
-void
-mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- Mpi2RequestDescriptorUnion_t descriptor;
- u64 *request = (u64 *)&descriptor;
-
- descriptor.HighPriority.RequestFlags =
- MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
- descriptor.HighPriority.MSIxIndex = 0; /* TODO */
- descriptor.HighPriority.SMID = cpu_to_le16(smid);
- descriptor.HighPriority.LMID = 0;
- descriptor.HighPriority.Reserved1 = 0;
- _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
- &ioc->scsi_lookup_lock);
-}
-
-/**
- * mpt2sas_base_put_smid_default - Default, primarily used for config pages
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Return nothing.
- */
-void
-mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- Mpi2RequestDescriptorUnion_t descriptor;
- u64 *request = (u64 *)&descriptor;
-
- descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
- descriptor.Default.MSIxIndex = 0; /* TODO */
- descriptor.Default.SMID = cpu_to_le16(smid);
- descriptor.Default.LMID = 0;
- descriptor.Default.DescriptorTypeDependent = 0;
- _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
- &ioc->scsi_lookup_lock);
-}
-
-/**
- * mpt2sas_base_put_smid_target_assist - send Target Assist/Status to firmware
- * @ioc: per adapter object
- * @smid: system request message index
- * @io_index: value used to track the IO
- *
- * Return nothing.
- */
-void
-mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid,
- u16 io_index)
-{
- Mpi2RequestDescriptorUnion_t descriptor;
- u64 *request = (u64 *)&descriptor;
-
- descriptor.SCSITarget.RequestFlags =
- MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET;
- descriptor.SCSITarget.MSIxIndex = 0; /* TODO */
- descriptor.SCSITarget.SMID = cpu_to_le16(smid);
- descriptor.SCSITarget.LMID = 0;
- descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index);
- _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
- &ioc->scsi_lookup_lock);
-}
-
-/**
- * _base_display_dell_branding - Disply branding string
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_base_display_dell_branding(struct MPT2SAS_ADAPTER *ioc)
-{
- char dell_branding[MPT2SAS_DELL_BRANDING_SIZE];
-
- if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_DELL)
- return;
-
- memset(dell_branding, 0, MPT2SAS_DELL_BRANDING_SIZE);
- switch (ioc->pdev->subsystem_device) {
- case MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID:
- strncpy(dell_branding, MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING,
- MPT2SAS_DELL_BRANDING_SIZE - 1);
- break;
- case MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID:
- strncpy(dell_branding, MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING,
- MPT2SAS_DELL_BRANDING_SIZE - 1);
- break;
- case MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID:
- strncpy(dell_branding,
- MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING,
- MPT2SAS_DELL_BRANDING_SIZE - 1);
- break;
- case MPT2SAS_DELL_PERC_H200_MODULAR_SSDID:
- strncpy(dell_branding,
- MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING,
- MPT2SAS_DELL_BRANDING_SIZE - 1);
- break;
- case MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID:
- strncpy(dell_branding,
- MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING,
- MPT2SAS_DELL_BRANDING_SIZE - 1);
- break;
- case MPT2SAS_DELL_PERC_H200_SSDID:
- strncpy(dell_branding, MPT2SAS_DELL_PERC_H200_BRANDING,
- MPT2SAS_DELL_BRANDING_SIZE - 1);
- break;
- case MPT2SAS_DELL_6GBPS_SAS_SSDID:
- strncpy(dell_branding, MPT2SAS_DELL_6GBPS_SAS_BRANDING,
- MPT2SAS_DELL_BRANDING_SIZE - 1);
- break;
- default:
- sprintf(dell_branding, "0x%4X", ioc->pdev->subsystem_device);
- break;
- }
-
- printk(MPT2SAS_INFO_FMT "%s: Vendor(0x%04X), Device(0x%04X),"
- " SSVID(0x%04X), SSDID(0x%04X)\n", ioc->name, dell_branding,
- ioc->pdev->vendor, ioc->pdev->device, ioc->pdev->subsystem_vendor,
- ioc->pdev->subsystem_device);
-}
-
-/**
- * _base_display_intel_branding - Display branding string
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_base_display_intel_branding(struct MPT2SAS_ADAPTER *ioc)
-{
- if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_INTEL &&
- ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008) {
-
- switch (ioc->pdev->subsystem_device) {
- case MPT2SAS_INTEL_RMS2LL080_SSDID:
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- MPT2SAS_INTEL_RMS2LL080_BRANDING);
- break;
- case MPT2SAS_INTEL_RMS2LL040_SSDID:
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- MPT2SAS_INTEL_RMS2LL040_BRANDING);
- break;
- }
- }
-}
-
-/**
- * _base_display_hp_branding - Display branding string
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_base_display_hp_branding(struct MPT2SAS_ADAPTER *ioc)
-{
- if (ioc->pdev->subsystem_vendor != MPT2SAS_HP_3PAR_SSVID)
- return;
-
- switch (ioc->pdev->device) {
- case MPI2_MFGPAGE_DEVID_SAS2004:
- switch (ioc->pdev->subsystem_device) {
- case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID:
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING);
- break;
- default:
- break;
- }
- case MPI2_MFGPAGE_DEVID_SAS2308_2:
- switch (ioc->pdev->subsystem_device) {
- case MPT2SAS_HP_2_4_INTERNAL_SSDID:
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- MPT2SAS_HP_2_4_INTERNAL_BRANDING);
- break;
- case MPT2SAS_HP_2_4_EXTERNAL_SSDID:
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- MPT2SAS_HP_2_4_EXTERNAL_BRANDING);
- break;
- case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID:
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING);
- break;
- case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID:
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING);
- break;
- default:
- break;
- }
- default:
- break;
- }
-}
-
-/**
- * _base_display_ioc_capabilities - Disply IOC's capabilities.
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
-{
- int i = 0;
- char desc[16];
- u8 revision;
- u32 iounit_pg1_flags;
-
- pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
- strncpy(desc, ioc->manu_pg0.ChipName, 16);
- printk(MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), "
- "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n",
- ioc->name, desc,
- (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
- (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
- (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
- ioc->facts.FWVersion.Word & 0x000000FF,
- revision,
- (ioc->bios_pg3.BiosVersion & 0xFF000000) >> 24,
- (ioc->bios_pg3.BiosVersion & 0x00FF0000) >> 16,
- (ioc->bios_pg3.BiosVersion & 0x0000FF00) >> 8,
- ioc->bios_pg3.BiosVersion & 0x000000FF);
-
- _base_display_dell_branding(ioc);
- _base_display_intel_branding(ioc);
- _base_display_hp_branding(ioc);
-
- printk(MPT2SAS_INFO_FMT "Protocol=(", ioc->name);
-
- if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) {
- printk("Initiator");
- i++;
- }
-
- if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) {
- printk("%sTarget", i ? "," : "");
- i++;
- }
-
- i = 0;
- printk("), ");
- printk("Capabilities=(");
-
- if (!ioc->hide_ir_msg) {
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
- printk("Raid");
- i++;
- }
- }
-
- if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) {
- printk("%sTLR", i ? "," : "");
- i++;
- }
-
- if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) {
- printk("%sMulticast", i ? "," : "");
- i++;
- }
-
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) {
- printk("%sBIDI Target", i ? "," : "");
- i++;
- }
-
- if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) {
- printk("%sEEDP", i ? "," : "");
- i++;
- }
-
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) {
- printk("%sSnapshot Buffer", i ? "," : "");
- i++;
- }
-
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) {
- printk("%sDiag Trace Buffer", i ? "," : "");
- i++;
- }
-
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
- printk(KERN_INFO "%sDiag Extended Buffer", i ? "," : "");
- i++;
- }
-
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
- printk("%sTask Set Full", i ? "," : "");
- i++;
- }
-
- iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
- if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) {
- printk("%sNCQ", i ? "," : "");
- i++;
- }
-
- printk(")\n");
-}
-
-/**
- * _base_update_missing_delay - change the missing delay timers
- * @ioc: per adapter object
- * @device_missing_delay: amount of time till device is reported missing
- * @io_missing_delay: interval IO is returned when there is a missing device
- *
- * Return nothing.
- *
- * Passed on the command line, this function will modify the device missing
- * delay, as well as the io missing delay. This should be called at driver
- * load time.
- */
-static void
-_base_update_missing_delay(struct MPT2SAS_ADAPTER *ioc,
- u16 device_missing_delay, u8 io_missing_delay)
-{
- u16 dmd, dmd_new, dmd_orignal;
- u8 io_missing_delay_original;
- u16 sz;
- Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
- Mpi2ConfigReply_t mpi_reply;
- u8 num_phys = 0;
- u16 ioc_status;
-
- mpt2sas_config_get_number_hba_phys(ioc, &num_phys);
- if (!num_phys)
- return;
-
- sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (num_phys *
- sizeof(Mpi2SasIOUnit1PhyData_t));
- sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
- if (!sas_iounit_pg1) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
- sas_iounit_pg1, sz))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
-
- /* device missing delay */
- dmd = sas_iounit_pg1->ReportDeviceMissingDelay;
- if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
- dmd = (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
- else
- dmd = dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
- dmd_orignal = dmd;
- if (device_missing_delay > 0x7F) {
- dmd = (device_missing_delay > 0x7F0) ? 0x7F0 :
- device_missing_delay;
- dmd = dmd / 16;
- dmd |= MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16;
- } else
- dmd = device_missing_delay;
- sas_iounit_pg1->ReportDeviceMissingDelay = dmd;
-
- /* io missing delay */
- io_missing_delay_original = sas_iounit_pg1->IODeviceMissingDelay;
- sas_iounit_pg1->IODeviceMissingDelay = io_missing_delay;
-
- if (!mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
- sz)) {
- if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
- dmd_new = (dmd &
- MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
- else
- dmd_new =
- dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
- printk(MPT2SAS_INFO_FMT "device_missing_delay: old(%d), "
- "new(%d)\n", ioc->name, dmd_orignal, dmd_new);
- printk(MPT2SAS_INFO_FMT "ioc_missing_delay: old(%d), "
- "new(%d)\n", ioc->name, io_missing_delay_original,
- io_missing_delay);
- ioc->device_missing_delay = dmd_new;
- ioc->io_missing_delay = io_missing_delay;
- }
-
-out:
- kfree(sas_iounit_pg1);
-}
-
-/**
- * _base_static_config_pages - static start of day config pages
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_base_static_config_pages(struct MPT2SAS_ADAPTER *ioc)
-{
- Mpi2ConfigReply_t mpi_reply;
- u32 iounit_pg1_flags;
-
- mpt2sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0);
- if (ioc->ir_firmware)
- mpt2sas_config_get_manufacturing_pg10(ioc, &mpi_reply,
- &ioc->manu_pg10);
- mpt2sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2);
- mpt2sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3);
- mpt2sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8);
- mpt2sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0);
- mpt2sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
- _base_display_ioc_capabilities(ioc);
-
- /*
- * Enable task_set_full handling in iounit_pg1 when the
- * facts capabilities indicate that its supported.
- */
- iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
- if ((ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING))
- iounit_pg1_flags &=
- ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
- else
- iounit_pg1_flags |=
- MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
- ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags);
- mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
-
-}
-
-/**
- * _base_release_memory_pools - release memory
- * @ioc: per adapter object
- *
- * Free memory allocated from _base_allocate_memory_pools.
- *
- * Return nothing.
- */
-static void
-_base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc)
-{
- int i;
-
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- if (ioc->request) {
- pci_free_consistent(ioc->pdev, ioc->request_dma_sz,
- ioc->request, ioc->request_dma);
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "request_pool(0x%p)"
- ": free\n", ioc->name, ioc->request));
- ioc->request = NULL;
- }
-
- if (ioc->sense) {
- pci_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma);
- if (ioc->sense_dma_pool)
- pci_pool_destroy(ioc->sense_dma_pool);
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_pool(0x%p)"
- ": free\n", ioc->name, ioc->sense));
- ioc->sense = NULL;
- }
-
- if (ioc->reply) {
- pci_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma);
- if (ioc->reply_dma_pool)
- pci_pool_destroy(ioc->reply_dma_pool);
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_pool(0x%p)"
- ": free\n", ioc->name, ioc->reply));
- ioc->reply = NULL;
- }
-
- if (ioc->reply_free) {
- pci_pool_free(ioc->reply_free_dma_pool, ioc->reply_free,
- ioc->reply_free_dma);
- if (ioc->reply_free_dma_pool)
- pci_pool_destroy(ioc->reply_free_dma_pool);
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_pool"
- "(0x%p): free\n", ioc->name, ioc->reply_free));
- ioc->reply_free = NULL;
- }
-
- if (ioc->reply_post_free) {
- pci_pool_free(ioc->reply_post_free_dma_pool,
- ioc->reply_post_free, ioc->reply_post_free_dma);
- if (ioc->reply_post_free_dma_pool)
- pci_pool_destroy(ioc->reply_post_free_dma_pool);
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT
- "reply_post_free_pool(0x%p): free\n", ioc->name,
- ioc->reply_post_free));
- ioc->reply_post_free = NULL;
- }
-
- if (ioc->config_page) {
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT
- "config_page(0x%p): free\n", ioc->name,
- ioc->config_page));
- pci_free_consistent(ioc->pdev, ioc->config_page_sz,
- ioc->config_page, ioc->config_page_dma);
- }
-
- if (ioc->scsi_lookup) {
- free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages);
- ioc->scsi_lookup = NULL;
- }
- kfree(ioc->hpr_lookup);
- kfree(ioc->internal_lookup);
- if (ioc->chain_lookup) {
- for (i = 0; i < ioc->chain_depth; i++) {
- if (ioc->chain_lookup[i].chain_buffer)
- pci_pool_free(ioc->chain_dma_pool,
- ioc->chain_lookup[i].chain_buffer,
- ioc->chain_lookup[i].chain_buffer_dma);
- }
- if (ioc->chain_dma_pool)
- pci_pool_destroy(ioc->chain_dma_pool);
- }
- if (ioc->chain_lookup) {
- free_pages((ulong)ioc->chain_lookup, ioc->chain_pages);
- ioc->chain_lookup = NULL;
- }
-}
-
-
-/**
- * _base_allocate_memory_pools - allocate start of day memory pools
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 success, anything else error
- */
-static int
-_base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- Mpi2IOCFactsReply_t *facts;
- u32 queue_size, queue_diff;
- u16 max_sge_elements;
- u16 num_of_reply_frames;
- u16 chains_needed_per_io;
- u32 sz, total_sz;
- u32 retry_sz;
- u16 max_request_credit;
- int i;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- retry_sz = 0;
- facts = &ioc->facts;
-
- /* command line tunables for max sgl entries */
- if (max_sgl_entries != -1) {
- ioc->shost->sg_tablesize = (max_sgl_entries <
- MPT2SAS_SG_DEPTH) ? max_sgl_entries :
- MPT2SAS_SG_DEPTH;
- } else {
- ioc->shost->sg_tablesize = MPT2SAS_SG_DEPTH;
- }
-
- /* command line tunables for max controller queue depth */
- if (max_queue_depth != -1)
- max_request_credit = (max_queue_depth < facts->RequestCredit)
- ? max_queue_depth : facts->RequestCredit;
- else
- max_request_credit = facts->RequestCredit;
-
- ioc->hba_queue_depth = max_request_credit;
- ioc->hi_priority_depth = facts->HighPriorityCredit;
- ioc->internal_depth = ioc->hi_priority_depth + 5;
-
- /* request frame size */
- ioc->request_sz = facts->IOCRequestFrameSize * 4;
-
- /* reply frame size */
- ioc->reply_sz = facts->ReplyFrameSize * 4;
-
- retry_allocation:
- total_sz = 0;
- /* calculate number of sg elements left over in the 1st frame */
- max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) -
- sizeof(Mpi2SGEIOUnion_t)) + ioc->sge_size);
- ioc->max_sges_in_main_message = max_sge_elements/ioc->sge_size;
-
- /* now do the same for a chain buffer */
- max_sge_elements = ioc->request_sz - ioc->sge_size;
- ioc->max_sges_in_chain_message = max_sge_elements/ioc->sge_size;
-
- ioc->chain_offset_value_for_main_message =
- ((sizeof(Mpi2SCSIIORequest_t) - sizeof(Mpi2SGEIOUnion_t)) +
- (ioc->max_sges_in_chain_message * ioc->sge_size)) / 4;
-
- /*
- * MPT2SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE
- */
- chains_needed_per_io = ((ioc->shost->sg_tablesize -
- ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message)
- + 1;
- if (chains_needed_per_io > facts->MaxChainDepth) {
- chains_needed_per_io = facts->MaxChainDepth;
- ioc->shost->sg_tablesize = min_t(u16,
- ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message
- * chains_needed_per_io), ioc->shost->sg_tablesize);
- }
- ioc->chains_needed_per_io = chains_needed_per_io;
-
- /* reply free queue sizing - taking into account for events */
- num_of_reply_frames = ioc->hba_queue_depth + 32;
-
- /* number of replies frames can't be a multiple of 16 */
- /* decrease number of reply frames by 1 */
- if (!(num_of_reply_frames % 16))
- num_of_reply_frames--;
-
- /* calculate number of reply free queue entries
- * (must be multiple of 16)
- */
-
- /* (we know reply_free_queue_depth is not a multiple of 16) */
- queue_size = num_of_reply_frames;
- queue_size += 16 - (queue_size % 16);
- ioc->reply_free_queue_depth = queue_size;
-
- /* reply descriptor post queue sizing */
- /* this size should be the number of request frames + number of reply
- * frames
- */
-
- queue_size = ioc->hba_queue_depth + num_of_reply_frames + 1;
- /* round up to 16 byte boundary */
- if (queue_size % 16)
- queue_size += 16 - (queue_size % 16);
-
- /* check against IOC maximum reply post queue depth */
- if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) {
- queue_diff = queue_size -
- facts->MaxReplyDescriptorPostQueueDepth;
-
- /* round queue_diff up to multiple of 16 */
- if (queue_diff % 16)
- queue_diff += 16 - (queue_diff % 16);
-
- /* adjust hba_queue_depth, reply_free_queue_depth,
- * and queue_size
- */
- ioc->hba_queue_depth -= (queue_diff / 2);
- ioc->reply_free_queue_depth -= (queue_diff / 2);
- queue_size = facts->MaxReplyDescriptorPostQueueDepth;
- }
- ioc->reply_post_queue_depth = queue_size;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: "
- "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), "
- "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message,
- ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize,
- ioc->chains_needed_per_io));
-
- ioc->scsiio_depth = ioc->hba_queue_depth -
- ioc->hi_priority_depth - ioc->internal_depth;
-
- /* set the scsi host can_queue depth
- * with some internal commands that could be outstanding
- */
- ioc->shost->can_queue = ioc->scsiio_depth - (2);
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host: "
- "can_queue depth (%d)\n", ioc->name, ioc->shost->can_queue));
-
- /* contiguous pool for request and chains, 16 byte align, one extra "
- * "frame for smid=0
- */
- ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth;
- sz = ((ioc->scsiio_depth + 1) * ioc->request_sz);
-
- /* hi-priority queue */
- sz += (ioc->hi_priority_depth * ioc->request_sz);
-
- /* internal queue */
- sz += (ioc->internal_depth * ioc->request_sz);
-
- ioc->request_dma_sz = sz;
- ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma);
- if (!ioc->request) {
- printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent "
- "failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), "
- "total(%d kB)\n", ioc->name, ioc->hba_queue_depth,
- ioc->chains_needed_per_io, ioc->request_sz, sz/1024);
- if (ioc->scsiio_depth < MPT2SAS_SAS_QUEUE_DEPTH)
- goto out;
- retry_sz += 64;
- ioc->hba_queue_depth = max_request_credit - retry_sz;
- goto retry_allocation;
- }
-
- if (retry_sz)
- printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent "
- "succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), "
- "total(%d kb)\n", ioc->name, ioc->hba_queue_depth,
- ioc->chains_needed_per_io, ioc->request_sz, sz/1024);
-
-
- /* hi-priority queue */
- ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) *
- ioc->request_sz);
- ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) *
- ioc->request_sz);
-
- /* internal queue */
- ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth *
- ioc->request_sz);
- ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth *
- ioc->request_sz);
-
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool(0x%p): "
- "depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name,
- ioc->request, ioc->hba_queue_depth, ioc->request_sz,
- (ioc->hba_queue_depth * ioc->request_sz)/1024));
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool: dma(0x%llx)\n",
- ioc->name, (unsigned long long) ioc->request_dma));
- total_sz += sz;
-
- sz = ioc->scsiio_depth * sizeof(struct scsiio_tracker);
- ioc->scsi_lookup_pages = get_order(sz);
- ioc->scsi_lookup = (struct scsiio_tracker *)__get_free_pages(
- GFP_KERNEL, ioc->scsi_lookup_pages);
- if (!ioc->scsi_lookup) {
- printk(MPT2SAS_ERR_FMT "scsi_lookup: get_free_pages failed, "
- "sz(%d)\n", ioc->name, (int)sz);
- goto out;
- }
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsiio(0x%p): "
- "depth(%d)\n", ioc->name, ioc->request,
- ioc->scsiio_depth));
-
- /* loop till the allocation succeeds */
- do {
- sz = ioc->chain_depth * sizeof(struct chain_tracker);
- ioc->chain_pages = get_order(sz);
- ioc->chain_lookup = (struct chain_tracker *)__get_free_pages(
- GFP_KERNEL, ioc->chain_pages);
- if (ioc->chain_lookup == NULL)
- ioc->chain_depth -= 100;
- } while (ioc->chain_lookup == NULL);
- ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev,
- ioc->request_sz, 16, 0);
- if (!ioc->chain_dma_pool) {
- printk(MPT2SAS_ERR_FMT "chain_dma_pool: pci_pool_create "
- "failed\n", ioc->name);
- goto out;
- }
- for (i = 0; i < ioc->chain_depth; i++) {
- ioc->chain_lookup[i].chain_buffer = pci_pool_alloc(
- ioc->chain_dma_pool , GFP_KERNEL,
- &ioc->chain_lookup[i].chain_buffer_dma);
- if (!ioc->chain_lookup[i].chain_buffer) {
- ioc->chain_depth = i;
- goto chain_done;
- }
- total_sz += ioc->request_sz;
- }
-chain_done:
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool depth"
- "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name,
- ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth *
- ioc->request_sz))/1024));
-
- /* initialize hi-priority queue smid's */
- ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth,
- sizeof(struct request_tracker), GFP_KERNEL);
- if (!ioc->hpr_lookup) {
- printk(MPT2SAS_ERR_FMT "hpr_lookup: kcalloc failed\n",
- ioc->name);
- goto out;
- }
- ioc->hi_priority_smid = ioc->scsiio_depth + 1;
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hi_priority(0x%p): "
- "depth(%d), start smid(%d)\n", ioc->name, ioc->hi_priority,
- ioc->hi_priority_depth, ioc->hi_priority_smid));
-
- /* initialize internal queue smid's */
- ioc->internal_lookup = kcalloc(ioc->internal_depth,
- sizeof(struct request_tracker), GFP_KERNEL);
- if (!ioc->internal_lookup) {
- printk(MPT2SAS_ERR_FMT "internal_lookup: kcalloc failed\n",
- ioc->name);
- goto out;
- }
- ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth;
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "internal(0x%p): "
- "depth(%d), start smid(%d)\n", ioc->name, ioc->internal,
- ioc->internal_depth, ioc->internal_smid));
-
- /* sense buffers, 4 byte align */
- sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE;
- ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4,
- 0);
- if (!ioc->sense_dma_pool) {
- printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_create failed\n",
- ioc->name);
- goto out;
- }
- ioc->sense = pci_pool_alloc(ioc->sense_dma_pool , GFP_KERNEL,
- &ioc->sense_dma);
- if (!ioc->sense) {
- printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_alloc failed\n",
- ioc->name);
- goto out;
- }
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT
- "sense pool(0x%p): depth(%d), element_size(%d), pool_size"
- "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth,
- SCSI_SENSE_BUFFERSIZE, sz/1024));
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_dma(0x%llx)\n",
- ioc->name, (unsigned long long)ioc->sense_dma));
- total_sz += sz;
-
- /* reply pool, 4 byte align */
- sz = ioc->reply_free_queue_depth * ioc->reply_sz;
- ioc->reply_dma_pool = pci_pool_create("reply pool", ioc->pdev, sz, 4,
- 0);
- if (!ioc->reply_dma_pool) {
- printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_create failed\n",
- ioc->name);
- goto out;
- }
- ioc->reply = pci_pool_alloc(ioc->reply_dma_pool , GFP_KERNEL,
- &ioc->reply_dma);
- if (!ioc->reply) {
- printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_alloc failed\n",
- ioc->name);
- goto out;
- }
- ioc->reply_dma_min_address = (u32)(ioc->reply_dma);
- ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz;
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply pool(0x%p): depth"
- "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->reply,
- ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024));
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_dma(0x%llx)\n",
- ioc->name, (unsigned long long)ioc->reply_dma));
- total_sz += sz;
-
- /* reply free queue, 16 byte align */
- sz = ioc->reply_free_queue_depth * 4;
- ioc->reply_free_dma_pool = pci_pool_create("reply_free pool",
- ioc->pdev, sz, 16, 0);
- if (!ioc->reply_free_dma_pool) {
- printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_create "
- "failed\n", ioc->name);
- goto out;
- }
- ioc->reply_free = pci_pool_alloc(ioc->reply_free_dma_pool , GFP_KERNEL,
- &ioc->reply_free_dma);
- if (!ioc->reply_free) {
- printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_alloc "
- "failed\n", ioc->name);
- goto out;
- }
- memset(ioc->reply_free, 0, sz);
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free pool(0x%p): "
- "depth(%d), element_size(%d), pool_size(%d kB)\n", ioc->name,
- ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024));
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_dma"
- "(0x%llx)\n", ioc->name, (unsigned long long)ioc->reply_free_dma));
- total_sz += sz;
-
- /* reply post queue, 16 byte align */
- sz = ioc->reply_post_queue_depth * sizeof(Mpi2DefaultReplyDescriptor_t);
- ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool",
- ioc->pdev, sz, 16, 0);
- if (!ioc->reply_post_free_dma_pool) {
- printk(MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_create "
- "failed\n", ioc->name);
- goto out;
- }
- ioc->reply_post_free = pci_pool_alloc(ioc->reply_post_free_dma_pool ,
- GFP_KERNEL, &ioc->reply_post_free_dma);
- if (!ioc->reply_post_free) {
- printk(MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_alloc "
- "failed\n", ioc->name);
- goto out;
- }
- memset(ioc->reply_post_free, 0, sz);
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply post free pool"
- "(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n",
- ioc->name, ioc->reply_post_free, ioc->reply_post_queue_depth, 8,
- sz/1024));
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_post_free_dma = "
- "(0x%llx)\n", ioc->name, (unsigned long long)
- ioc->reply_post_free_dma));
- total_sz += sz;
-
- ioc->config_page_sz = 512;
- ioc->config_page = pci_alloc_consistent(ioc->pdev,
- ioc->config_page_sz, &ioc->config_page_dma);
- if (!ioc->config_page) {
- printk(MPT2SAS_ERR_FMT "config page: pci_pool_alloc "
- "failed\n", ioc->name);
- goto out;
- }
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config page(0x%p): size"
- "(%d)\n", ioc->name, ioc->config_page, ioc->config_page_sz));
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config_page_dma"
- "(0x%llx)\n", ioc->name, (unsigned long long)ioc->config_page_dma));
- total_sz += ioc->config_page_sz;
-
- printk(MPT2SAS_INFO_FMT "Allocated physical memory: size(%d kB)\n",
- ioc->name, total_sz/1024);
- printk(MPT2SAS_INFO_FMT "Current Controller Queue Depth(%d), "
- "Max Controller Queue Depth(%d)\n",
- ioc->name, ioc->shost->can_queue, facts->RequestCredit);
- printk(MPT2SAS_INFO_FMT "Scatter Gather Elements per IO(%d)\n",
- ioc->name, ioc->shost->sg_tablesize);
- return 0;
-
- out:
- return -ENOMEM;
-}
-
-
-/**
- * mpt2sas_base_get_iocstate - Get the current state of a MPT adapter.
- * @ioc: Pointer to MPT_ADAPTER structure
- * @cooked: Request raw or cooked IOC state
- *
- * Returns all IOC Doorbell register bits if cooked==0, else just the
- * Doorbell bits in MPI_IOC_STATE_MASK.
- */
-u32
-mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked)
-{
- u32 s, sc;
-
- s = readl(&ioc->chip->Doorbell);
- sc = s & MPI2_IOC_STATE_MASK;
- return cooked ? sc : s;
-}
-
-/**
- * _base_wait_on_iocstate - waiting on a particular ioc state
- * @ioc_state: controller state { READY, OPERATIONAL, or RESET }
- * @timeout: timeout in second
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_wait_on_iocstate(struct MPT2SAS_ADAPTER *ioc, u32 ioc_state, int timeout,
- int sleep_flag)
-{
- u32 count, cntdn;
- u32 current_state;
-
- count = 0;
- cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
- do {
- current_state = mpt2sas_base_get_iocstate(ioc, 1);
- if (current_state == ioc_state)
- return 0;
- if (count && current_state == MPI2_IOC_STATE_FAULT)
- break;
- if (sleep_flag == CAN_SLEEP)
- msleep(1);
- else
- udelay(500);
- count++;
- } while (--cntdn);
-
- return current_state;
-}
-
-/**
- * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by
- * a write to the doorbell)
- * @ioc: per adapter object
- * @timeout: timeout in second
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- *
- * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell.
- */
-static int
-_base_wait_for_doorbell_int(struct MPT2SAS_ADAPTER *ioc, int timeout,
- int sleep_flag)
-{
- u32 cntdn, count;
- u32 int_status;
-
- count = 0;
- cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
- do {
- int_status = readl(&ioc->chip->HostInterruptStatus);
- if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
- dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "successful count(%d), timeout(%d)\n", ioc->name,
- __func__, count, timeout));
- return 0;
- }
- if (sleep_flag == CAN_SLEEP)
- msleep(1);
- else
- udelay(500);
- count++;
- } while (--cntdn);
-
- printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), "
- "int_status(%x)!\n", ioc->name, __func__, count, int_status);
- return -EFAULT;
-}
-
-/**
- * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell.
- * @ioc: per adapter object
- * @timeout: timeout in second
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- *
- * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to
- * doorbell.
- */
-static int
-_base_wait_for_doorbell_ack(struct MPT2SAS_ADAPTER *ioc, int timeout,
- int sleep_flag)
-{
- u32 cntdn, count;
- u32 int_status;
- u32 doorbell;
-
- count = 0;
- cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
- do {
- int_status = readl(&ioc->chip->HostInterruptStatus);
- if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) {
- dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "successful count(%d), timeout(%d)\n", ioc->name,
- __func__, count, timeout));
- return 0;
- } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
- doorbell = readl(&ioc->chip->Doorbell);
- if ((doorbell & MPI2_IOC_STATE_MASK) ==
- MPI2_IOC_STATE_FAULT) {
- mpt2sas_base_fault_info(ioc , doorbell);
- return -EFAULT;
- }
- } else if (int_status == 0xFFFFFFFF)
- goto out;
-
- if (sleep_flag == CAN_SLEEP)
- msleep(1);
- else
- udelay(500);
- count++;
- } while (--cntdn);
-
- out:
- printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), "
- "int_status(%x)!\n", ioc->name, __func__, count, int_status);
- return -EFAULT;
-}
-
-/**
- * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use
- * @ioc: per adapter object
- * @timeout: timeout in second
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- *
- */
-static int
-_base_wait_for_doorbell_not_used(struct MPT2SAS_ADAPTER *ioc, int timeout,
- int sleep_flag)
-{
- u32 cntdn, count;
- u32 doorbell_reg;
-
- count = 0;
- cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
- do {
- doorbell_reg = readl(&ioc->chip->Doorbell);
- if (!(doorbell_reg & MPI2_DOORBELL_USED)) {
- dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "successful count(%d), timeout(%d)\n", ioc->name,
- __func__, count, timeout));
- return 0;
- }
- if (sleep_flag == CAN_SLEEP)
- msleep(1);
- else
- udelay(500);
- count++;
- } while (--cntdn);
-
- printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), "
- "doorbell_reg(%x)!\n", ioc->name, __func__, count, doorbell_reg);
- return -EFAULT;
-}
-
-/**
- * _base_send_ioc_reset - send doorbell reset
- * @ioc: per adapter object
- * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET
- * @timeout: timeout in second
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_send_ioc_reset(struct MPT2SAS_ADAPTER *ioc, u8 reset_type, int timeout,
- int sleep_flag)
-{
- u32 ioc_state;
- int r = 0;
-
- if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) {
- printk(MPT2SAS_ERR_FMT "%s: unknown reset_type\n",
- ioc->name, __func__);
- return -EFAULT;
- }
-
- if (!(ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY))
- return -EFAULT;
-
- printk(MPT2SAS_INFO_FMT "sending message unit reset !!\n", ioc->name);
-
- writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT,
- &ioc->chip->Doorbell);
- if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) {
- r = -EFAULT;
- goto out;
- }
- ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY,
- timeout, sleep_flag);
- if (ioc_state) {
- printk(MPT2SAS_ERR_FMT "%s: failed going to ready state "
- " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
- r = -EFAULT;
- goto out;
- }
- out:
- printk(MPT2SAS_INFO_FMT "message unit reset: %s\n",
- ioc->name, ((r == 0) ? "SUCCESS" : "FAILED"));
- return r;
-}
-
-/**
- * _base_handshake_req_reply_wait - send request thru doorbell interface
- * @ioc: per adapter object
- * @request_bytes: request length
- * @request: pointer having request payload
- * @reply_bytes: reply length
- * @reply: pointer to reply payload
- * @timeout: timeout in second
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes,
- u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag)
-{
- MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply;
- int i;
- u8 failed;
- u16 dummy;
- u32 *mfp;
-
- /* make sure doorbell is not in use */
- if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) {
- printk(MPT2SAS_ERR_FMT "doorbell is in use "
- " (line=%d)\n", ioc->name, __LINE__);
- return -EFAULT;
- }
-
- /* clear pending doorbell interrupts from previous state changes */
- if (readl(&ioc->chip->HostInterruptStatus) &
- MPI2_HIS_IOC2SYS_DB_STATUS)
- writel(0, &ioc->chip->HostInterruptStatus);
-
- /* send message to ioc */
- writel(((MPI2_FUNCTION_HANDSHAKE<<MPI2_DOORBELL_FUNCTION_SHIFT) |
- ((request_bytes/4)<<MPI2_DOORBELL_ADD_DWORDS_SHIFT)),
- &ioc->chip->Doorbell);
-
- if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) {
- printk(MPT2SAS_ERR_FMT "doorbell handshake "
- "int failed (line=%d)\n", ioc->name, __LINE__);
- return -EFAULT;
- }
- writel(0, &ioc->chip->HostInterruptStatus);
-
- if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) {
- printk(MPT2SAS_ERR_FMT "doorbell handshake "
- "ack failed (line=%d)\n", ioc->name, __LINE__);
- return -EFAULT;
- }
-
- /* send message 32-bits at a time */
- for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) {
- writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell);
- if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag)))
- failed = 1;
- }
-
- if (failed) {
- printk(MPT2SAS_ERR_FMT "doorbell handshake "
- "sending request failed (line=%d)\n", ioc->name, __LINE__);
- return -EFAULT;
- }
-
- /* now wait for the reply */
- if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) {
- printk(MPT2SAS_ERR_FMT "doorbell handshake "
- "int failed (line=%d)\n", ioc->name, __LINE__);
- return -EFAULT;
- }
-
- /* read the first two 16-bits, it gives the total length of the reply */
- reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell)
- & MPI2_DOORBELL_DATA_MASK);
- writel(0, &ioc->chip->HostInterruptStatus);
- if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) {
- printk(MPT2SAS_ERR_FMT "doorbell handshake "
- "int failed (line=%d)\n", ioc->name, __LINE__);
- return -EFAULT;
- }
- reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell)
- & MPI2_DOORBELL_DATA_MASK);
- writel(0, &ioc->chip->HostInterruptStatus);
-
- for (i = 2; i < default_reply->MsgLength * 2; i++) {
- if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) {
- printk(MPT2SAS_ERR_FMT "doorbell "
- "handshake int failed (line=%d)\n", ioc->name,
- __LINE__);
- return -EFAULT;
- }
- if (i >= reply_bytes/2) /* overflow case */
- dummy = readl(&ioc->chip->Doorbell);
- else
- reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell)
- & MPI2_DOORBELL_DATA_MASK);
- writel(0, &ioc->chip->HostInterruptStatus);
- }
-
- _base_wait_for_doorbell_int(ioc, 5, sleep_flag);
- if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) {
- dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "doorbell is in use "
- " (line=%d)\n", ioc->name, __LINE__));
- }
- writel(0, &ioc->chip->HostInterruptStatus);
-
- if (ioc->logging_level & MPT_DEBUG_INIT) {
- mfp = (u32 *)reply;
- printk(KERN_INFO "\toffset:data\n");
- for (i = 0; i < reply_bytes/4; i++)
- printk(KERN_INFO "\t[0x%02x]:%08x\n", i*4,
- le32_to_cpu(mfp[i]));
- }
- return 0;
-}
-
-/**
- * mpt2sas_base_sas_iounit_control - send sas iounit control to FW
- * @ioc: per adapter object
- * @mpi_reply: the reply payload from FW
- * @mpi_request: the request payload sent to FW
- *
- * The SAS IO Unit Control Request message allows the host to perform low-level
- * operations, such as resets on the PHYs of the IO Unit, also allows the host
- * to obtain the IOC assigned device handles for a device if it has other
- * identifying information about the device, in addition allows the host to
- * remove IOC resources associated with the device.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc,
- Mpi2SasIoUnitControlReply_t *mpi_reply,
- Mpi2SasIoUnitControlRequest_t *mpi_request)
-{
- u16 smid;
- u32 ioc_state;
- unsigned long timeleft;
- u8 issue_reset;
- int rc;
- void *request;
- u16 wait_state_count;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- mutex_lock(&ioc->base_cmds.mutex);
-
- if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == 10) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
-
- smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- rc = 0;
- ioc->base_cmds.status = MPT2_CMD_PENDING;
- request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->base_cmds.smid = smid;
- memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t));
- if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
- mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET)
- ioc->ioc_link_reset_in_progress = 1;
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->base_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
- msecs_to_jiffies(10000));
- if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
- mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) &&
- ioc->ioc_link_reset_in_progress)
- ioc->ioc_link_reset_in_progress = 0;
- if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SasIoUnitControlRequest_t)/4);
- if (!(ioc->base_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
- if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID)
- memcpy(mpi_reply, ioc->base_cmds.reply,
- sizeof(Mpi2SasIoUnitControlReply_t));
- else
- memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t));
- ioc->base_cmds.status = MPT2_CMD_NOT_USED;
- goto out;
-
- issue_host_reset:
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- ioc->base_cmds.status = MPT2_CMD_NOT_USED;
- rc = -EFAULT;
- out:
- mutex_unlock(&ioc->base_cmds.mutex);
- return rc;
-}
-
-
-/**
- * mpt2sas_base_scsi_enclosure_processor - sending request to sep device
- * @ioc: per adapter object
- * @mpi_reply: the reply payload from FW
- * @mpi_request: the request payload sent to FW
- *
- * The SCSI Enclosure Processor request message causes the IOC to
- * communicate with SES devices to control LED status signals.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
- Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request)
-{
- u16 smid;
- u32 ioc_state;
- unsigned long timeleft;
- u8 issue_reset;
- int rc;
- void *request;
- u16 wait_state_count;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- mutex_lock(&ioc->base_cmds.mutex);
-
- if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == 10) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
-
- smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- rc = 0;
- ioc->base_cmds.status = MPT2_CMD_PENDING;
- request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->base_cmds.smid = smid;
- memcpy(request, mpi_request, sizeof(Mpi2SepReply_t));
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->base_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
- msecs_to_jiffies(10000));
- if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SepRequest_t)/4);
- if (!(ioc->base_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
- if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID)
- memcpy(mpi_reply, ioc->base_cmds.reply,
- sizeof(Mpi2SepReply_t));
- else
- memset(mpi_reply, 0, sizeof(Mpi2SepReply_t));
- ioc->base_cmds.status = MPT2_CMD_NOT_USED;
- goto out;
-
- issue_host_reset:
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- ioc->base_cmds.status = MPT2_CMD_NOT_USED;
- rc = -EFAULT;
- out:
- mutex_unlock(&ioc->base_cmds.mutex);
- return rc;
-}
-
-/**
- * _base_get_port_facts - obtain port facts reply and save in ioc
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_get_port_facts(struct MPT2SAS_ADAPTER *ioc, int port, int sleep_flag)
-{
- Mpi2PortFactsRequest_t mpi_request;
- Mpi2PortFactsReply_t mpi_reply, *pfacts;
- int mpi_reply_sz, mpi_request_sz, r;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- mpi_reply_sz = sizeof(Mpi2PortFactsReply_t);
- mpi_request_sz = sizeof(Mpi2PortFactsRequest_t);
- memset(&mpi_request, 0, mpi_request_sz);
- mpi_request.Function = MPI2_FUNCTION_PORT_FACTS;
- mpi_request.PortNumber = port;
- r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
- (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP);
-
- if (r != 0) {
- printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n",
- ioc->name, __func__, r);
- return r;
- }
-
- pfacts = &ioc->pfacts[port];
- memset(pfacts, 0, sizeof(Mpi2PortFactsReply_t));
- pfacts->PortNumber = mpi_reply.PortNumber;
- pfacts->VP_ID = mpi_reply.VP_ID;
- pfacts->VF_ID = mpi_reply.VF_ID;
- pfacts->MaxPostedCmdBuffers =
- le16_to_cpu(mpi_reply.MaxPostedCmdBuffers);
-
- return 0;
-}
-
-/**
- * _base_get_ioc_facts - obtain ioc facts reply and save in ioc
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- Mpi2IOCFactsRequest_t mpi_request;
- Mpi2IOCFactsReply_t mpi_reply, *facts;
- int mpi_reply_sz, mpi_request_sz, r;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t);
- mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t);
- memset(&mpi_request, 0, mpi_request_sz);
- mpi_request.Function = MPI2_FUNCTION_IOC_FACTS;
- r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
- (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP);
-
- if (r != 0) {
- printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n",
- ioc->name, __func__, r);
- return r;
- }
-
- facts = &ioc->facts;
- memset(facts, 0, sizeof(Mpi2IOCFactsReply_t));
- facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion);
- facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion);
- facts->VP_ID = mpi_reply.VP_ID;
- facts->VF_ID = mpi_reply.VF_ID;
- facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions);
- facts->MaxChainDepth = mpi_reply.MaxChainDepth;
- facts->WhoInit = mpi_reply.WhoInit;
- facts->NumberOfPorts = mpi_reply.NumberOfPorts;
- facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit);
- facts->MaxReplyDescriptorPostQueueDepth =
- le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth);
- facts->ProductID = le16_to_cpu(mpi_reply.ProductID);
- facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities);
- if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID))
- ioc->ir_firmware = 1;
- facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word);
- facts->IOCRequestFrameSize =
- le16_to_cpu(mpi_reply.IOCRequestFrameSize);
- facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators);
- facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets);
- ioc->shost->max_id = -1;
- facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders);
- facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures);
- facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags);
- facts->HighPriorityCredit =
- le16_to_cpu(mpi_reply.HighPriorityCredit);
- facts->ReplyFrameSize = mpi_reply.ReplyFrameSize;
- facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle);
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hba queue depth(%d), "
- "max chains per io(%d)\n", ioc->name, facts->RequestCredit,
- facts->MaxChainDepth));
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request frame size(%d), "
- "reply frame size(%d)\n", ioc->name,
- facts->IOCRequestFrameSize * 4, facts->ReplyFrameSize * 4));
- return 0;
-}
-
-/**
- * _base_send_ioc_init - send ioc_init to firmware
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- Mpi2IOCInitRequest_t mpi_request;
- Mpi2IOCInitReply_t mpi_reply;
- int r;
- struct timeval current_time;
- u16 ioc_status;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t));
- mpi_request.Function = MPI2_FUNCTION_IOC_INIT;
- mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER;
- mpi_request.VF_ID = 0; /* TODO */
- mpi_request.VP_ID = 0;
- mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION);
- mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
-
- /* In MPI Revision I (0xA), the SystemReplyFrameSize(offset 0x18) was
- * removed and made reserved. For those with older firmware will need
- * this fix. It was decided that the Reply and Request frame sizes are
- * the same.
- */
- if ((ioc->facts.HeaderVersion >> 8) < 0xA) {
- mpi_request.Reserved7 = cpu_to_le16(ioc->reply_sz);
-/* mpi_request.SystemReplyFrameSize =
- * cpu_to_le16(ioc->reply_sz);
- */
- }
-
- mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4);
- mpi_request.ReplyDescriptorPostQueueDepth =
- cpu_to_le16(ioc->reply_post_queue_depth);
- mpi_request.ReplyFreeQueueDepth =
- cpu_to_le16(ioc->reply_free_queue_depth);
-
-#if BITS_PER_LONG > 32
- mpi_request.SenseBufferAddressHigh =
- cpu_to_le32(ioc->sense_dma >> 32);
- mpi_request.SystemReplyAddressHigh =
- cpu_to_le32(ioc->reply_dma >> 32);
- mpi_request.SystemRequestFrameBaseAddress =
- cpu_to_le64(ioc->request_dma);
- mpi_request.ReplyFreeQueueAddress =
- cpu_to_le64(ioc->reply_free_dma);
- mpi_request.ReplyDescriptorPostQueueAddress =
- cpu_to_le64(ioc->reply_post_free_dma);
-#else
- mpi_request.SystemRequestFrameBaseAddress =
- cpu_to_le32(ioc->request_dma);
- mpi_request.ReplyFreeQueueAddress =
- cpu_to_le32(ioc->reply_free_dma);
- mpi_request.ReplyDescriptorPostQueueAddress =
- cpu_to_le32(ioc->reply_post_free_dma);
-#endif
-
- /* This time stamp specifies number of milliseconds
- * since epoch ~ midnight January 1, 1970.
- */
- do_gettimeofday(&current_time);
- mpi_request.TimeStamp = cpu_to_le64((u64)current_time.tv_sec * 1000 +
- (current_time.tv_usec / 1000));
-
- if (ioc->logging_level & MPT_DEBUG_INIT) {
- u32 *mfp;
- int i;
-
- mfp = (u32 *)&mpi_request;
- printk(KERN_INFO "\toffset:data\n");
- for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++)
- printk(KERN_INFO "\t[0x%02x]:%08x\n", i*4,
- le32_to_cpu(mfp[i]));
- }
-
- r = _base_handshake_req_reply_wait(ioc,
- sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request,
- sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10,
- sleep_flag);
-
- if (r != 0) {
- printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n",
- ioc->name, __func__, r);
- return r;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS ||
- mpi_reply.IOCLogInfo) {
- printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__);
- r = -EIO;
- }
-
- return 0;
-}
-
-/**
- * _base_send_port_enable - send port_enable(discovery stuff) to firmware
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- Mpi2PortEnableRequest_t *mpi_request;
- u32 ioc_state;
- unsigned long timeleft;
- int r = 0;
- u16 smid;
-
- printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name);
-
- if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
- printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
- ioc->name, __func__);
- return -EAGAIN;
- }
-
- smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- return -EAGAIN;
- }
-
- ioc->base_cmds.status = MPT2_CMD_PENDING;
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->base_cmds.smid = smid;
- memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
- mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
-
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->base_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
- 300*HZ);
- if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2PortEnableRequest_t)/4);
- if (ioc->base_cmds.status & MPT2_CMD_RESET)
- r = -EFAULT;
- else
- r = -ETIME;
- goto out;
- } else
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: complete\n",
- ioc->name, __func__));
-
- ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_OPERATIONAL,
- 60, sleep_flag);
- if (ioc_state) {
- printk(MPT2SAS_ERR_FMT "%s: failed going to operational state "
- " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
- r = -EFAULT;
- }
- out:
- ioc->base_cmds.status = MPT2_CMD_NOT_USED;
- printk(MPT2SAS_INFO_FMT "port enable: %s\n",
- ioc->name, ((r == 0) ? "SUCCESS" : "FAILED"));
- return r;
-}
-
-/**
- * _base_unmask_events - turn on notification for this event
- * @ioc: per adapter object
- * @event: firmware event
- *
- * The mask is stored in ioc->event_masks.
- */
-static void
-_base_unmask_events(struct MPT2SAS_ADAPTER *ioc, u16 event)
-{
- u32 desired_event;
-
- if (event >= 128)
- return;
-
- desired_event = (1 << (event % 32));
-
- if (event < 32)
- ioc->event_masks[0] &= ~desired_event;
- else if (event < 64)
- ioc->event_masks[1] &= ~desired_event;
- else if (event < 96)
- ioc->event_masks[2] &= ~desired_event;
- else if (event < 128)
- ioc->event_masks[3] &= ~desired_event;
-}
-
-/**
- * _base_event_notification - send event notification
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- Mpi2EventNotificationRequest_t *mpi_request;
- unsigned long timeleft;
- u16 smid;
- int r = 0;
- int i;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
- printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
- ioc->name, __func__);
- return -EAGAIN;
- }
-
- smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- return -EAGAIN;
- }
- ioc->base_cmds.status = MPT2_CMD_PENDING;
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->base_cmds.smid = smid;
- memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t));
- mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
- for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
- mpi_request->EventMasks[i] =
- cpu_to_le32(ioc->event_masks[i]);
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->base_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ);
- if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2EventNotificationRequest_t)/4);
- if (ioc->base_cmds.status & MPT2_CMD_RESET)
- r = -EFAULT;
- else
- r = -ETIME;
- } else
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: complete\n",
- ioc->name, __func__));
- ioc->base_cmds.status = MPT2_CMD_NOT_USED;
- return r;
-}
-
-/**
- * mpt2sas_base_validate_event_type - validating event types
- * @ioc: per adapter object
- * @event: firmware event
- *
- * This will turn on firmware event notification when application
- * ask for that event. We don't mask events that are already enabled.
- */
-void
-mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type)
-{
- int i, j;
- u32 event_mask, desired_event;
- u8 send_update_to_fw;
-
- for (i = 0, send_update_to_fw = 0; i <
- MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) {
- event_mask = ~event_type[i];
- desired_event = 1;
- for (j = 0; j < 32; j++) {
- if (!(event_mask & desired_event) &&
- (ioc->event_masks[i] & desired_event)) {
- ioc->event_masks[i] &= ~desired_event;
- send_update_to_fw = 1;
- }
- desired_event = (desired_event << 1);
- }
- }
-
- if (!send_update_to_fw)
- return;
-
- mutex_lock(&ioc->base_cmds.mutex);
- _base_event_notification(ioc, CAN_SLEEP);
- mutex_unlock(&ioc->base_cmds.mutex);
-}
-
-/**
- * _base_diag_reset - the "big hammer" start of day reset
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- u32 host_diagnostic;
- u32 ioc_state;
- u32 count;
- u32 hcb_size;
-
- printk(MPT2SAS_INFO_FMT "sending diag reset !!\n", ioc->name);
-
- _base_save_msix_table(ioc);
-
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "clear interrupts\n",
- ioc->name));
-
- count = 0;
- do {
- /* Write magic sequence to WriteSequence register
- * Loop until in diagnostic mode
- */
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "write magic "
- "sequence\n", ioc->name));
- writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
- writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence);
- writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence);
- writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence);
- writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence);
- writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence);
- writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence);
-
- /* wait 100 msec */
- if (sleep_flag == CAN_SLEEP)
- msleep(100);
- else
- mdelay(100);
-
- if (count++ > 20)
- goto out;
-
- host_diagnostic = readl(&ioc->chip->HostDiagnostic);
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "wrote magic "
- "sequence: count(%d), host_diagnostic(0x%08x)\n",
- ioc->name, count, host_diagnostic));
-
- } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0);
-
- hcb_size = readl(&ioc->chip->HCBSize);
-
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "diag reset: issued\n",
- ioc->name));
- writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER,
- &ioc->chip->HostDiagnostic);
-
- /* don't access any registers for 50 milliseconds */
- msleep(50);
-
- /* 300 second max wait */
- for (count = 0; count < 3000000 ; count++) {
-
- host_diagnostic = readl(&ioc->chip->HostDiagnostic);
-
- if (host_diagnostic == 0xFFFFFFFF)
- goto out;
- if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER))
- break;
-
- /* wait 100 msec */
- if (sleep_flag == CAN_SLEEP)
- msleep(1);
- else
- mdelay(1);
- }
-
- if (host_diagnostic & MPI2_DIAG_HCB_MODE) {
-
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "restart the adapter "
- "assuming the HCB Address points to good F/W\n",
- ioc->name));
- host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK;
- host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW;
- writel(host_diagnostic, &ioc->chip->HostDiagnostic);
-
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT
- "re-enable the HCDW\n", ioc->name));
- writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE,
- &ioc->chip->HCBSize);
- }
-
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "restart the adapter\n",
- ioc->name));
- writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET,
- &ioc->chip->HostDiagnostic);
-
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "disable writes to the "
- "diagnostic register\n", ioc->name));
- writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
-
- drsprintk(ioc, printk(MPT2SAS_INFO_FMT "Wait for FW to go to the "
- "READY state\n", ioc->name));
- ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20,
- sleep_flag);
- if (ioc_state) {
- printk(MPT2SAS_ERR_FMT "%s: failed going to ready state "
- " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
- goto out;
- }
-
- _base_restore_msix_table(ioc);
- printk(MPT2SAS_INFO_FMT "diag reset: SUCCESS\n", ioc->name);
- return 0;
-
- out:
- printk(MPT2SAS_ERR_FMT "diag reset: FAILED\n", ioc->name);
- return -EFAULT;
-}
-
-/**
- * _base_make_ioc_ready - put controller in READY state
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- * @type: FORCE_BIG_HAMMER or SOFT_RESET
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
- enum reset_type type)
-{
- u32 ioc_state;
- int rc;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- if (ioc->pci_error_recovery)
- return 0;
-
- ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
- dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: ioc_state(0x%08x)\n",
- ioc->name, __func__, ioc_state));
-
- if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY)
- return 0;
-
- if (ioc_state & MPI2_DOORBELL_USED) {
- dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "unexpected doorbell "
- "active!\n", ioc->name));
- goto issue_diag_reset;
- }
-
- if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
- mpt2sas_base_fault_info(ioc, ioc_state &
- MPI2_DOORBELL_DATA_MASK);
- goto issue_diag_reset;
- }
-
- if (type == FORCE_BIG_HAMMER)
- goto issue_diag_reset;
-
- if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL)
- if (!(_base_send_ioc_reset(ioc,
- MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) {
- ioc->ioc_reset_count++;
- return 0;
- }
-
- issue_diag_reset:
- rc = _base_diag_reset(ioc, CAN_SLEEP);
- ioc->ioc_reset_count++;
- return rc;
-}
-
-/**
- * _base_make_ioc_operational - put controller in OPERATIONAL state
- * @ioc: per adapter object
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- int r, i;
- unsigned long flags;
- u32 reply_address;
- u16 smid;
- struct _tr_list *delayed_tr, *delayed_tr_next;
- u8 hide_flag;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- /* clean the delayed target reset list */
- list_for_each_entry_safe(delayed_tr, delayed_tr_next,
- &ioc->delayed_tr_list, list) {
- list_del(&delayed_tr->list);
- kfree(delayed_tr);
- }
-
- list_for_each_entry_safe(delayed_tr, delayed_tr_next,
- &ioc->delayed_tr_volume_list, list) {
- list_del(&delayed_tr->list);
- kfree(delayed_tr);
- }
-
- /* initialize the scsi lookup free list */
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- INIT_LIST_HEAD(&ioc->free_list);
- smid = 1;
- for (i = 0; i < ioc->scsiio_depth; i++, smid++) {
- INIT_LIST_HEAD(&ioc->scsi_lookup[i].chain_list);
- ioc->scsi_lookup[i].cb_idx = 0xFF;
- ioc->scsi_lookup[i].smid = smid;
- ioc->scsi_lookup[i].scmd = NULL;
- ioc->scsi_lookup[i].direct_io = 0;
- list_add_tail(&ioc->scsi_lookup[i].tracker_list,
- &ioc->free_list);
- }
-
- /* hi-priority queue */
- INIT_LIST_HEAD(&ioc->hpr_free_list);
- smid = ioc->hi_priority_smid;
- for (i = 0; i < ioc->hi_priority_depth; i++, smid++) {
- ioc->hpr_lookup[i].cb_idx = 0xFF;
- ioc->hpr_lookup[i].smid = smid;
- list_add_tail(&ioc->hpr_lookup[i].tracker_list,
- &ioc->hpr_free_list);
- }
-
- /* internal queue */
- INIT_LIST_HEAD(&ioc->internal_free_list);
- smid = ioc->internal_smid;
- for (i = 0; i < ioc->internal_depth; i++, smid++) {
- ioc->internal_lookup[i].cb_idx = 0xFF;
- ioc->internal_lookup[i].smid = smid;
- list_add_tail(&ioc->internal_lookup[i].tracker_list,
- &ioc->internal_free_list);
- }
-
- /* chain pool */
- INIT_LIST_HEAD(&ioc->free_chain_list);
- for (i = 0; i < ioc->chain_depth; i++)
- list_add_tail(&ioc->chain_lookup[i].tracker_list,
- &ioc->free_chain_list);
-
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-
- /* initialize Reply Free Queue */
- for (i = 0, reply_address = (u32)ioc->reply_dma ;
- i < ioc->reply_free_queue_depth ; i++, reply_address +=
- ioc->reply_sz)
- ioc->reply_free[i] = cpu_to_le32(reply_address);
-
- /* initialize Reply Post Free Queue */
- for (i = 0; i < ioc->reply_post_queue_depth; i++)
- ioc->reply_post_free[i].Words = ULLONG_MAX;
-
- r = _base_send_ioc_init(ioc, sleep_flag);
- if (r)
- return r;
-
- /* initialize the index's */
- ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1;
- ioc->reply_post_host_index = 0;
- writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex);
- writel(0, &ioc->chip->ReplyPostHostIndex);
-
- _base_unmask_interrupts(ioc);
- r = _base_event_notification(ioc, sleep_flag);
- if (r)
- return r;
-
- if (sleep_flag == CAN_SLEEP)
- _base_static_config_pages(ioc);
-
- if (ioc->wait_for_port_enable_to_complete && ioc->is_warpdrive) {
- if (ioc->manu_pg10.OEMIdentifier == 0x80) {
- hide_flag = (u8) (ioc->manu_pg10.OEMSpecificFlags0 &
- MFG_PAGE10_HIDE_SSDS_MASK);
- if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK)
- ioc->mfg_pg10_hide_flag = hide_flag;
- }
- }
-
- if (ioc->wait_for_port_enable_to_complete) {
- if (diag_buffer_enable != 0)
- mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
- if (disable_discovery > 0)
- return r;
- }
-
- r = _base_send_port_enable(ioc, sleep_flag);
- if (r)
- return r;
-
- return r;
-}
-
-/**
- * mpt2sas_base_free_resources - free resources controller resources (io/irq/memap)
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-void
-mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc)
-{
- struct pci_dev *pdev = ioc->pdev;
-
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- _base_mask_interrupts(ioc);
- ioc->shost_recovery = 1;
- _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
- ioc->shost_recovery = 0;
- if (ioc->pci_irq) {
- synchronize_irq(pdev->irq);
- free_irq(ioc->pci_irq, ioc);
- }
- _base_disable_msix(ioc);
- if (ioc->chip_phys)
- iounmap(ioc->chip);
- ioc->pci_irq = -1;
- ioc->chip_phys = 0;
- pci_release_selected_regions(ioc->pdev, ioc->bars);
- pci_disable_pcie_error_reporting(pdev);
- pci_disable_device(pdev);
- return;
-}
-
-/**
- * mpt2sas_base_attach - attach controller instance
- * @ioc: per adapter object
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
-{
- int r, i;
-
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- r = mpt2sas_base_map_resources(ioc);
- if (r)
- return r;
-
- pci_set_drvdata(ioc->pdev, ioc->shost);
- r = _base_get_ioc_facts(ioc, CAN_SLEEP);
- if (r)
- goto out_free_resources;
-
- r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
- if (r)
- goto out_free_resources;
-
- ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts,
- sizeof(Mpi2PortFactsReply_t), GFP_KERNEL);
- if (!ioc->pfacts) {
- r = -ENOMEM;
- goto out_free_resources;
- }
-
- for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) {
- r = _base_get_port_facts(ioc, i, CAN_SLEEP);
- if (r)
- goto out_free_resources;
- }
-
- r = _base_allocate_memory_pools(ioc, CAN_SLEEP);
- if (r)
- goto out_free_resources;
-
- init_waitqueue_head(&ioc->reset_wq);
-
- /* allocate memory pd handle bitmask list */
- ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8);
- if (ioc->facts.MaxDevHandle % 8)
- ioc->pd_handles_sz++;
- ioc->pd_handles = kzalloc(ioc->pd_handles_sz,
- GFP_KERNEL);
- if (!ioc->pd_handles) {
- r = -ENOMEM;
- goto out_free_resources;
- }
-
- ioc->fwfault_debug = mpt2sas_fwfault_debug;
-
- /* base internal command bits */
- mutex_init(&ioc->base_cmds.mutex);
- ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
- ioc->base_cmds.status = MPT2_CMD_NOT_USED;
-
- /* transport internal command bits */
- ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
- ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
- mutex_init(&ioc->transport_cmds.mutex);
-
- /* scsih internal command bits */
- ioc->scsih_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
- ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
- mutex_init(&ioc->scsih_cmds.mutex);
-
- /* task management internal command bits */
- ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
- ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
- mutex_init(&ioc->tm_cmds.mutex);
-
- /* config page internal command bits */
- ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
- ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- mutex_init(&ioc->config_cmds.mutex);
-
- /* ctl module internal command bits */
- ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
- ioc->ctl_cmds.sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
- ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
- mutex_init(&ioc->ctl_cmds.mutex);
-
- if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply ||
- !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply ||
- !ioc->config_cmds.reply || !ioc->ctl_cmds.reply ||
- !ioc->ctl_cmds.sense) {
- r = -ENOMEM;
- goto out_free_resources;
- }
-
- if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply ||
- !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply ||
- !ioc->config_cmds.reply || !ioc->ctl_cmds.reply) {
- r = -ENOMEM;
- goto out_free_resources;
- }
-
- init_completion(&ioc->shost_recovery_done);
-
- for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
- ioc->event_masks[i] = -1;
-
- /* here we enable the events we care about */
- _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY);
- _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE);
- _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
- _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
- _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
- _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
- _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME);
- _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK);
- _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS);
- _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED);
- r = _base_make_ioc_operational(ioc, CAN_SLEEP);
- if (r)
- goto out_free_resources;
-
- if (missing_delay[0] != -1 && missing_delay[1] != -1)
- _base_update_missing_delay(ioc, missing_delay[0],
- missing_delay[1]);
-
- mpt2sas_base_start_watchdog(ioc);
- return 0;
-
- out_free_resources:
-
- ioc->remove_host = 1;
- mpt2sas_base_free_resources(ioc);
- _base_release_memory_pools(ioc);
- pci_set_drvdata(ioc->pdev, NULL);
- kfree(ioc->pd_handles);
- kfree(ioc->tm_cmds.reply);
- kfree(ioc->transport_cmds.reply);
- kfree(ioc->scsih_cmds.reply);
- kfree(ioc->config_cmds.reply);
- kfree(ioc->base_cmds.reply);
- kfree(ioc->ctl_cmds.reply);
- kfree(ioc->ctl_cmds.sense);
- kfree(ioc->pfacts);
- ioc->ctl_cmds.reply = NULL;
- ioc->base_cmds.reply = NULL;
- ioc->tm_cmds.reply = NULL;
- ioc->scsih_cmds.reply = NULL;
- ioc->transport_cmds.reply = NULL;
- ioc->config_cmds.reply = NULL;
- ioc->pfacts = NULL;
- return r;
-}
-
-
-/**
- * mpt2sas_base_detach - remove controller instance
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-void
-mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc)
-{
-
- dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- mpt2sas_base_stop_watchdog(ioc);
- mpt2sas_base_free_resources(ioc);
- _base_release_memory_pools(ioc);
- pci_set_drvdata(ioc->pdev, NULL);
- kfree(ioc->pd_handles);
- kfree(ioc->pfacts);
- kfree(ioc->ctl_cmds.reply);
- kfree(ioc->ctl_cmds.sense);
- kfree(ioc->base_cmds.reply);
- kfree(ioc->tm_cmds.reply);
- kfree(ioc->transport_cmds.reply);
- kfree(ioc->scsih_cmds.reply);
- kfree(ioc->config_cmds.reply);
-}
-
-/**
- * _base_reset_handler - reset callback handler (for base)
- * @ioc: per adapter object
- * @reset_phase: phase
- *
- * The handler for doing any required cleanup or initialization.
- *
- * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
- * MPT2_IOC_DONE_RESET
- *
- * Return nothing.
- */
-static void
-_base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
-{
- mpt2sas_scsih_reset_handler(ioc, reset_phase);
- mpt2sas_ctl_reset_handler(ioc, reset_phase);
- switch (reset_phase) {
- case MPT2_IOC_PRE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
- break;
- case MPT2_IOC_AFTER_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
- if (ioc->transport_cmds.status & MPT2_CMD_PENDING) {
- ioc->transport_cmds.status |= MPT2_CMD_RESET;
- mpt2sas_base_free_smid(ioc, ioc->transport_cmds.smid);
- complete(&ioc->transport_cmds.done);
- }
- if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
- ioc->base_cmds.status |= MPT2_CMD_RESET;
- mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid);
- complete(&ioc->base_cmds.done);
- }
- if (ioc->config_cmds.status & MPT2_CMD_PENDING) {
- ioc->config_cmds.status |= MPT2_CMD_RESET;
- mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid);
- ioc->config_cmds.smid = USHRT_MAX;
- complete(&ioc->config_cmds.done);
- }
- break;
- case MPT2_IOC_DONE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
- break;
- }
-}
-
-/**
- * _wait_for_commands_to_complete - reset controller
- * @ioc: Pointer to MPT_ADAPTER structure
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- *
- * This function waiting(3s) for all pending commands to complete
- * prior to putting controller in reset.
- */
-static void
-_wait_for_commands_to_complete(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
-{
- u32 ioc_state;
- unsigned long flags;
- u16 i;
-
- ioc->pending_io_count = 0;
- if (sleep_flag != CAN_SLEEP)
- return;
-
- ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
- if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL)
- return;
-
- /* pending command count */
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- for (i = 0; i < ioc->scsiio_depth; i++)
- if (ioc->scsi_lookup[i].cb_idx != 0xFF)
- ioc->pending_io_count++;
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-
- if (!ioc->pending_io_count)
- return;
-
- /* wait for pending commands to complete */
- wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 10 * HZ);
-}
-
-/**
- * mpt2sas_base_hard_reset_handler - reset controller
- * @ioc: Pointer to MPT_ADAPTER structure
- * @sleep_flag: CAN_SLEEP or NO_SLEEP
- * @type: FORCE_BIG_HAMMER or SOFT_RESET
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
- enum reset_type type)
-{
- int r;
- unsigned long flags;
- u8 pe_complete = ioc->wait_for_port_enable_to_complete;
-
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
- __func__));
-
- if (ioc->pci_error_recovery) {
- printk(MPT2SAS_ERR_FMT "%s: pci error recovery reset\n",
- ioc->name, __func__);
- r = 0;
- goto out;
- }
-
- if (mpt2sas_fwfault_debug)
- mpt2sas_halt_firmware(ioc);
-
- /* TODO - What we really should be doing is pulling
- * out all the code associated with NO_SLEEP; its never used.
- * That is legacy code from mpt fusion driver, ported over.
- * I will leave this BUG_ON here for now till its been resolved.
- */
- BUG_ON(sleep_flag == NO_SLEEP);
-
- /* wait for an active reset in progress to complete */
- if (!mutex_trylock(&ioc->reset_in_progress_mutex)) {
- do {
- ssleep(1);
- } while (ioc->shost_recovery == 1);
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit\n", ioc->name,
- __func__));
- return ioc->ioc_reset_in_progress_status;
- }
-
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- ioc->shost_recovery = 1;
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
-
- _base_reset_handler(ioc, MPT2_IOC_PRE_RESET);
- _wait_for_commands_to_complete(ioc, sleep_flag);
- _base_mask_interrupts(ioc);
- r = _base_make_ioc_ready(ioc, sleep_flag, type);
- if (r)
- goto out;
- _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET);
-
- /* If this hard reset is called while port enable is active, then
- * there is no reason to call make_ioc_operational
- */
- if (pe_complete) {
- r = -EFAULT;
- goto out;
- }
- r = _base_make_ioc_operational(ioc, sleep_flag);
- if (!r)
- _base_reset_handler(ioc, MPT2_IOC_DONE_RESET);
- out:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: %s\n",
- ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED")));
-
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- ioc->ioc_reset_in_progress_status = r;
- ioc->shost_recovery = 0;
- complete(&ioc->shost_recovery_done);
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
- mutex_unlock(&ioc->reset_in_progress_mutex);
-
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit\n", ioc->name,
- __func__));
- return r;
-}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
deleted file mode 100644
index dcc289c25459..000000000000
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
- * This is the Fusion MPT base driver providing common API layer interface
- * for access to MPT (Message Passing Technology) firmware.
- *
- * This code is based on drivers/scsi/mpt2sas/mpt2_base.h
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * NO WARRANTY
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- * solely responsible for determining the appropriateness of using and
- * distributing the Program and assumes all risks associated with its
- * exercise of rights under this Agreement, including but not limited to
- * the risks and costs of program errors, damage to or loss of data,
- * programs or equipment, and unavailability or interruption of operations.
-
- * DISCLAIMER OF LIABILITY
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-#ifndef MPT2SAS_BASE_H_INCLUDED
-#define MPT2SAS_BASE_H_INCLUDED
-
-#include "mpi/mpi2_type.h"
-#include "mpi/mpi2.h"
-#include "mpi/mpi2_ioc.h"
-#include "mpi/mpi2_cnfg.h"
-#include "mpi/mpi2_init.h"
-#include "mpi/mpi2_raid.h"
-#include "mpi/mpi2_tool.h"
-#include "mpi/mpi2_sas.h"
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsi_transport_sas.h>
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_eh.h>
-
-#include "mpt2sas_debug.h"
-
-/* driver versioning info */
-#define MPT2SAS_DRIVER_NAME "mpt2sas"
-#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
-#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION "08.100.00.02"
-#define MPT2SAS_MAJOR_VERSION 08
-#define MPT2SAS_MINOR_VERSION 100
-#define MPT2SAS_BUILD_VERSION 00
-#define MPT2SAS_RELEASE_VERSION 02
-
-/*
- * Set MPT2SAS_SG_DEPTH value based on user input.
- */
-#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE
-#if CONFIG_SCSI_MPT2SAS_MAX_SGE < 16
-#define MPT2SAS_SG_DEPTH 16
-#elif CONFIG_SCSI_MPT2SAS_MAX_SGE > 128
-#define MPT2SAS_SG_DEPTH 128
-#else
-#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE
-#endif
-#else
-#define MPT2SAS_SG_DEPTH 128 /* MAX_HW_SEGMENTS */
-#endif
-
-
-/*
- * Generic Defines
- */
-#define MPT2SAS_SATA_QUEUE_DEPTH 32
-#define MPT2SAS_SAS_QUEUE_DEPTH 254
-#define MPT2SAS_RAID_QUEUE_DEPTH 128
-
-#define MPT_NAME_LENGTH 32 /* generic length of strings */
-#define MPT_STRING_LENGTH 64
-
-#define MPT_MAX_CALLBACKS 16
-
-
-#define CAN_SLEEP 1
-#define NO_SLEEP 0
-
-#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */
-
-#define MPI2_HIM_MASK 0xFFFFFFFF /* mask every bit*/
-
-#define MPT2SAS_INVALID_DEVICE_HANDLE 0xFFFF
-
-
-/*
- * reset phases
- */
-#define MPT2_IOC_PRE_RESET 1 /* prior to host reset */
-#define MPT2_IOC_AFTER_RESET 2 /* just after host reset */
-#define MPT2_IOC_DONE_RESET 3 /* links re-initialized */
-
-/*
- * logging format
- */
-#define MPT2SAS_FMT "%s: "
-#define MPT2SAS_INFO_FMT KERN_INFO MPT2SAS_FMT
-#define MPT2SAS_NOTE_FMT KERN_NOTICE MPT2SAS_FMT
-#define MPT2SAS_WARN_FMT KERN_WARNING MPT2SAS_FMT
-#define MPT2SAS_ERR_FMT KERN_ERR MPT2SAS_FMT
-
-/*
- * Dell HBA branding
- */
-#define MPT2SAS_DELL_BRANDING_SIZE 32
-
-#define MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING "Dell 6Gbps SAS HBA"
-#define MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING "Dell PERC H200 Adapter"
-#define MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING "Dell PERC H200 Integrated"
-#define MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING "Dell PERC H200 Modular"
-#define MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING "Dell PERC H200 Embedded"
-#define MPT2SAS_DELL_PERC_H200_BRANDING "Dell PERC H200"
-#define MPT2SAS_DELL_6GBPS_SAS_BRANDING "Dell 6Gbps SAS"
-
-/*
- * Dell HBA SSDIDs
- */
-#define MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID 0x1F1C
-#define MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID 0x1F1D
-#define MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID 0x1F1E
-#define MPT2SAS_DELL_PERC_H200_MODULAR_SSDID 0x1F1F
-#define MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID 0x1F20
-#define MPT2SAS_DELL_PERC_H200_SSDID 0x1F21
-#define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22
-
-/*
- * Intel HBA branding
- */
-#define MPT2SAS_INTEL_RMS2LL080_BRANDING \
- "Intel Integrated RAID Module RMS2LL080"
-#define MPT2SAS_INTEL_RMS2LL040_BRANDING \
- "Intel Integrated RAID Module RMS2LL040"
-
-/*
- * Intel HBA SSDIDs
- */
-#define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E
-#define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F
-
-
-/*
- * HP HBA branding
- */
-#define MPT2SAS_HP_3PAR_SSVID 0x1590
-#define MPT2SAS_HP_2_4_INTERNAL_BRANDING "HP H220 Host Bus Adapter"
-#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING "HP H221 Host Bus Adapter"
-#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING "HP H222 Host Bus Adapter"
-#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING "HP H220i Host Bus Adapter"
-#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING "HP H210i Host Bus Adapter"
-
-/*
- * HO HBA SSDIDs
- */
-#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041
-#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042
-#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043
-#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044
-#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046
-
-/*
- * WarpDrive Specific Log codes
- */
-
-#define MPT2_WARPDRIVE_LOGENTRY (0x8002)
-#define MPT2_WARPDRIVE_LC_SSDT (0x41)
-#define MPT2_WARPDRIVE_LC_SSDLW (0x43)
-#define MPT2_WARPDRIVE_LC_SSDLF (0x44)
-#define MPT2_WARPDRIVE_LC_BRMF (0x4D)
-
-/*
- * per target private data
- */
-#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01
-#define MPT_TARGET_FLAGS_VOLUME 0x02
-#define MPT_TARGET_FLAGS_DELETED 0x04
-
-/**
- * struct MPT2SAS_TARGET - starget private hostdata
- * @starget: starget object
- * @sas_address: target sas address
- * @raid_device: raid_device pointer to access volume data
- * @handle: device handle
- * @num_luns: number luns
- * @flags: MPT_TARGET_FLAGS_XXX flags
- * @deleted: target flaged for deletion
- * @tm_busy: target is busy with TM request.
- */
-struct MPT2SAS_TARGET {
- struct scsi_target *starget;
- u64 sas_address;
- struct _raid_device *raid_device;
- u16 handle;
- int num_luns;
- u32 flags;
- u8 deleted;
- u8 tm_busy;
-};
-
-
-/*
- * per device private data
- */
-#define MPT_DEVICE_FLAGS_INIT 0x01
-#define MPT_DEVICE_TLR_ON 0x02
-
-/**
- * struct MPT2SAS_DEVICE - sdev private hostdata
- * @sas_target: starget private hostdata
- * @lun: lun number
- * @flags: MPT_DEVICE_XXX flags
- * @configured_lun: lun is configured
- * @block: device is in SDEV_BLOCK state
- * @tlr_snoop_check: flag used in determining whether to disable TLR
- */
-
-/* OEM Identifiers */
-#define MFG10_OEM_ID_INVALID (0x00000000)
-#define MFG10_OEM_ID_DELL (0x00000001)
-#define MFG10_OEM_ID_FSC (0x00000002)
-#define MFG10_OEM_ID_SUN (0x00000003)
-#define MFG10_OEM_ID_IBM (0x00000004)
-
-/* GENERIC Flags 0*/
-#define MFG10_GF0_OCE_DISABLED (0x00000001)
-#define MFG10_GF0_R1E_DRIVE_COUNT (0x00000002)
-#define MFG10_GF0_R10_DISPLAY (0x00000004)
-#define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008)
-#define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010)
-
-/* OEM Specific Flags will come from OEM specific header files */
-typedef struct _MPI2_CONFIG_PAGE_MAN_10 {
- MPI2_CONFIG_PAGE_HEADER Header; /* 00h */
- U8 OEMIdentifier; /* 04h */
- U8 Reserved1; /* 05h */
- U16 Reserved2; /* 08h */
- U32 Reserved3; /* 0Ch */
- U32 GenericFlags0; /* 10h */
- U32 GenericFlags1; /* 14h */
- U32 Reserved4; /* 18h */
- U32 OEMSpecificFlags0; /* 1Ch */
- U32 OEMSpecificFlags1; /* 20h */
- U32 Reserved5[18]; /* 24h-60h*/
-} MPI2_CONFIG_PAGE_MAN_10,
- MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_10,
- Mpi2ManufacturingPage10_t, MPI2_POINTER pMpi2ManufacturingPage10_t;
-
-#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003)
-#define MFG_PAGE10_HIDE_ALL_DISKS (0x00)
-#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01)
-#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02)
-
-
-struct MPT2SAS_DEVICE {
- struct MPT2SAS_TARGET *sas_target;
- unsigned int lun;
- u32 flags;
- u8 configured_lun;
- u8 block;
- u8 tlr_snoop_check;
-};
-
-#define MPT2_CMD_NOT_USED 0x8000 /* free */
-#define MPT2_CMD_COMPLETE 0x0001 /* completed */
-#define MPT2_CMD_PENDING 0x0002 /* pending */
-#define MPT2_CMD_REPLY_VALID 0x0004 /* reply is valid */
-#define MPT2_CMD_RESET 0x0008 /* host reset dropped the command */
-
-/**
- * struct _internal_cmd - internal commands struct
- * @mutex: mutex
- * @done: completion
- * @reply: reply message pointer
- * @sense: sense data
- * @status: MPT2_CMD_XXX status
- * @smid: system message id
- */
-struct _internal_cmd {
- struct mutex mutex;
- struct completion done;
- void *reply;
- void *sense;
- u16 status;
- u16 smid;
-};
-
-
-/**
- * struct _sas_device - attached device information
- * @list: sas device list
- * @starget: starget object
- * @sas_address: device sas address
- * @device_name: retrieved from the SAS IDENTIFY frame.
- * @handle: device handle
- * @sas_address_parent: sas address of parent expander or sas host
- * @enclosure_handle: enclosure handle
- * @enclosure_logical_id: enclosure logical identifier
- * @volume_handle: volume handle (valid when hidden raid member)
- * @volume_wwid: volume unique identifier
- * @device_info: bitfield provides detailed info about the device
- * @id: target id
- * @channel: target channel
- * @slot: number number
- * @phy: phy identifier provided in sas device page 0
- * @responding: used in _scsih_sas_device_mark_responding
- */
-struct _sas_device {
- struct list_head list;
- struct scsi_target *starget;
- u64 sas_address;
- u64 device_name;
- u16 handle;
- u64 sas_address_parent;
- u16 enclosure_handle;
- u64 enclosure_logical_id;
- u16 volume_handle;
- u64 volume_wwid;
- u32 device_info;
- int id;
- int channel;
- u16 slot;
- u8 phy;
- u8 responding;
-};
-
-/**
- * struct _raid_device - raid volume link list
- * @list: sas device list
- * @starget: starget object
- * @sdev: scsi device struct (volumes are single lun)
- * @wwid: unique identifier for the volume
- * @handle: device handle
- * @block_size: Block size of the volume
- * @id: target id
- * @channel: target channel
- * @volume_type: the raid level
- * @device_info: bitfield provides detailed info about the hidden components
- * @num_pds: number of hidden raid components
- * @responding: used in _scsih_raid_device_mark_responding
- * @percent_complete: resync percent complete
- * @direct_io_enabled: Whether direct io to PDs are allowed or not
- * @stripe_exponent: X where 2powX is the stripe sz in blocks
- * @max_lba: Maximum number of LBA in the volume
- * @stripe_sz: Stripe Size of the volume
- * @device_info: Device info of the volume member disk
- * @pd_handle: Array of handles of the physical drives for direct I/O in le16
- */
-#define MPT_MAX_WARPDRIVE_PDS 8
-struct _raid_device {
- struct list_head list;
- struct scsi_target *starget;
- struct scsi_device *sdev;
- u64 wwid;
- u16 handle;
- u16 block_sz;
- int id;
- int channel;
- u8 volume_type;
- u8 num_pds;
- u8 responding;
- u8 percent_complete;
- u8 direct_io_enabled;
- u8 stripe_exponent;
- u64 max_lba;
- u32 stripe_sz;
- u32 device_info;
- u16 pd_handle[MPT_MAX_WARPDRIVE_PDS];
-};
-
-/**
- * struct _boot_device - boot device info
- * @is_raid: flag to indicate whether this is volume
- * @device: holds pointer for either struct _sas_device or
- * struct _raid_device
- */
-struct _boot_device {
- u8 is_raid;
- void *device;
-};
-
-/**
- * struct _sas_port - wide/narrow sas port information
- * @port_list: list of ports belonging to expander
- * @num_phys: number of phys belonging to this port
- * @remote_identify: attached device identification
- * @rphy: sas transport rphy object
- * @port: sas transport wide/narrow port object
- * @phy_list: _sas_phy list objects belonging to this port
- */
-struct _sas_port {
- struct list_head port_list;
- u8 num_phys;
- struct sas_identify remote_identify;
- struct sas_rphy *rphy;
- struct sas_port *port;
- struct list_head phy_list;
-};
-
-/**
- * struct _sas_phy - phy information
- * @port_siblings: list of phys belonging to a port
- * @identify: phy identification
- * @remote_identify: attached device identification
- * @phy: sas transport phy object
- * @phy_id: unique phy id
- * @handle: device handle for this phy
- * @attached_handle: device handle for attached device
- * @phy_belongs_to_port: port has been created for this phy
- */
-struct _sas_phy {
- struct list_head port_siblings;
- struct sas_identify identify;
- struct sas_identify remote_identify;
- struct sas_phy *phy;
- u8 phy_id;
- u16 handle;
- u16 attached_handle;
- u8 phy_belongs_to_port;
-};
-
-/**
- * struct _sas_node - sas_host/expander information
- * @list: list of expanders
- * @parent_dev: parent device class
- * @num_phys: number phys belonging to this sas_host/expander
- * @sas_address: sas address of this sas_host/expander
- * @handle: handle for this sas_host/expander
- * @sas_address_parent: sas address of parent expander or sas host
- * @enclosure_handle: handle for this a member of an enclosure
- * @device_info: bitwise defining capabilities of this sas_host/expander
- * @responding: used in _scsih_expander_device_mark_responding
- * @phy: a list of phys that make up this sas_host/expander
- * @sas_port_list: list of ports attached to this sas_host/expander
- */
-struct _sas_node {
- struct list_head list;
- struct device *parent_dev;
- u8 num_phys;
- u64 sas_address;
- u16 handle;
- u64 sas_address_parent;
- u16 enclosure_handle;
- u64 enclosure_logical_id;
- u8 responding;
- struct _sas_phy *phy;
- struct list_head sas_port_list;
-};
-
-/**
- * enum reset_type - reset state
- * @FORCE_BIG_HAMMER: issue diagnostic reset
- * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer
- */
-enum reset_type {
- FORCE_BIG_HAMMER,
- SOFT_RESET,
-};
-
-/**
- * struct chain_tracker - firmware chain tracker
- * @chain_buffer: chain buffer
- * @chain_buffer_dma: physical address
- * @tracker_list: list of free request (ioc->free_chain_list)
- */
-struct chain_tracker {
- void *chain_buffer;
- dma_addr_t chain_buffer_dma;
- struct list_head tracker_list;
-};
-
-/**
- * struct scsiio_tracker - scsi mf request tracker
- * @smid: system message id
- * @scmd: scsi request pointer
- * @cb_idx: callback index
- * @direct_io: To indicate whether I/O is direct (WARPDRIVE)
- * @chain_list: list of chains associated to this IO
- * @tracker_list: list of free request (ioc->free_list)
- */
-struct scsiio_tracker {
- u16 smid;
- struct scsi_cmnd *scmd;
- u8 cb_idx;
- u8 direct_io;
- struct list_head chain_list;
- struct list_head tracker_list;
-};
-
-/**
- * struct request_tracker - firmware request tracker
- * @smid: system message id
- * @cb_idx: callback index
- * @tracker_list: list of free request (ioc->free_list)
- */
-struct request_tracker {
- u16 smid;
- u8 cb_idx;
- struct list_head tracker_list;
-};
-
-/**
- * struct _tr_list - target reset list
- * @handle: device handle
- * @state: state machine
- */
-struct _tr_list {
- struct list_head list;
- u16 handle;
- u16 state;
-};
-
-typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
-
-/**
- * struct MPT2SAS_ADAPTER - per adapter struct
- * @list: ioc_list
- * @shost: shost object
- * @id: unique adapter id
- * @pci_irq: irq number
- * @name: generic ioc string
- * @tmp_string: tmp string used for logging
- * @pdev: pci pdev object
- * @chip: memory mapped register space
- * @chip_phys: physical addrss prior to mapping
- * @logging_level: see mpt2sas_debug.h
- * @fwfault_debug: debuging FW timeouts
- * @ir_firmware: IR firmware present
- * @bars: bitmask of BAR's that must be configured
- * @mask_interrupts: ignore interrupt
- * @fault_reset_work_q_name: fw fault work queue
- * @fault_reset_work_q: ""
- * @fault_reset_work: ""
- * @firmware_event_name: fw event work queue
- * @firmware_event_thread: ""
- * @fw_events_off: flag to turn off fw event handling
- * @fw_event_lock:
- * @fw_event_list: list of fw events
- * @aen_event_read_flag: event log was read
- * @broadcast_aen_busy: broadcast aen waiting to be serviced
- * @shost_recovery: host reset in progress
- * @ioc_reset_in_progress_lock:
- * @ioc_link_reset_in_progress: phy/hard reset in progress
- * @ignore_loginfos: ignore loginfos during task management
- * @remove_host: flag for when driver unloads, to avoid sending dev resets
- * @pci_error_recovery: flag to prevent ioc access until slot reset completes
- * @wait_for_port_enable_to_complete:
- * @msix_enable: flag indicating msix is enabled
- * @msix_vector_count: number msix vectors
- * @msix_table: virt address to the msix table
- * @msix_table_backup: backup msix table
- * @scsi_io_cb_idx: shost generated commands
- * @tm_cb_idx: task management commands
- * @scsih_cb_idx: scsih internal commands
- * @transport_cb_idx: transport internal commands
- * @ctl_cb_idx: clt internal commands
- * @base_cb_idx: base internal commands
- * @config_cb_idx: base internal commands
- * @tm_tr_cb_idx : device removal target reset handshake
- * @tm_tr_volume_cb_idx : volume removal target reset
- * @base_cmds:
- * @transport_cmds:
- * @scsih_cmds:
- * @tm_cmds:
- * @ctl_cmds:
- * @config_cmds:
- * @base_add_sg_single: handler for either 32/64 bit sgl's
- * @event_type: bits indicating which events to log
- * @event_context: unique id for each logged event
- * @event_log: event log pointer
- * @event_masks: events that are masked
- * @facts: static facts data
- * @pfacts: static port facts data
- * @manu_pg0: static manufacturing page 0
- * @manu_pg10: static manufacturing page 10
- * @bios_pg2: static bios page 2
- * @bios_pg3: static bios page 3
- * @ioc_pg8: static ioc page 8
- * @iounit_pg0: static iounit page 0
- * @iounit_pg1: static iounit page 1
- * @sas_hba: sas host object
- * @sas_expander_list: expander object list
- * @sas_node_lock:
- * @sas_device_list: sas device object list
- * @sas_device_init_list: sas device object list (used only at init time)
- * @sas_device_lock:
- * @io_missing_delay: time for IO completed by fw when PDR enabled
- * @device_missing_delay: time for device missing by fw when PDR enabled
- * @sas_id : used for setting volume target IDs
- * @pd_handles : bitmask for PD handles
- * @pd_handles_sz : size of pd_handle bitmask
- * @config_page_sz: config page size
- * @config_page: reserve memory for config page payload
- * @config_page_dma:
- * @hba_queue_depth: hba request queue depth
- * @sge_size: sg element size for either 32/64 bit
- * @scsiio_depth: SCSI_IO queue depth
- * @request_sz: per request frame size
- * @request: pool of request frames
- * @request_dma:
- * @request_dma_sz:
- * @scsi_lookup: firmware request tracker list
- * @scsi_lookup_lock:
- * @free_list: free list of request
- * @chain: pool of chains
- * @pending_io_count:
- * @reset_wq:
- * @chain_dma:
- * @max_sges_in_main_message: number sg elements in main message
- * @max_sges_in_chain_message: number sg elements per chain
- * @chains_needed_per_io: max chains per io
- * @chain_offset_value_for_main_message: location 1st sg in main
- * @chain_depth: total chains allocated
- * @hi_priority_smid:
- * @hi_priority:
- * @hi_priority_dma:
- * @hi_priority_depth:
- * @hpr_lookup:
- * @hpr_free_list:
- * @internal_smid:
- * @internal:
- * @internal_dma:
- * @internal_depth:
- * @internal_lookup:
- * @internal_free_list:
- * @sense: pool of sense
- * @sense_dma:
- * @sense_dma_pool:
- * @reply_depth: hba reply queue depth:
- * @reply_sz: per reply frame size:
- * @reply: pool of replys:
- * @reply_dma:
- * @reply_dma_pool:
- * @reply_free_queue_depth: reply free depth
- * @reply_free: pool for reply free queue (32 bit addr)
- * @reply_free_dma:
- * @reply_free_dma_pool:
- * @reply_free_host_index: tail index in pool to insert free replys
- * @reply_post_queue_depth: reply post queue depth
- * @reply_post_free: pool for reply post (64bit descriptor)
- * @reply_post_free_dma:
- * @reply_post_free_dma_pool:
- * @reply_post_host_index: head index in the pool where FW completes IO
- * @delayed_tr_list: target reset link list
- * @delayed_tr_volume_list: volume target reset link list
- */
-struct MPT2SAS_ADAPTER {
- struct list_head list;
- struct Scsi_Host *shost;
- u8 id;
- u32 pci_irq;
- char name[MPT_NAME_LENGTH];
- char tmp_string[MPT_STRING_LENGTH];
- struct pci_dev *pdev;
- Mpi2SystemInterfaceRegs_t __iomem *chip;
- resource_size_t chip_phys;
- int logging_level;
- int fwfault_debug;
- u8 ir_firmware;
- int bars;
- u8 mask_interrupts;
-
- /* fw fault handler */
- char fault_reset_work_q_name[20];
- struct workqueue_struct *fault_reset_work_q;
- struct delayed_work fault_reset_work;
-
- /* fw event handler */
- char firmware_event_name[20];
- struct workqueue_struct *firmware_event_thread;
- spinlock_t fw_event_lock;
- struct list_head fw_event_list;
-
- /* misc flags */
- int aen_event_read_flag;
- u8 broadcast_aen_busy;
- u8 shost_recovery;
-
- struct mutex reset_in_progress_mutex;
- struct completion shost_recovery_done;
- spinlock_t ioc_reset_in_progress_lock;
- u8 ioc_link_reset_in_progress;
- int ioc_reset_in_progress_status;
-
- u8 ignore_loginfos;
- u8 remove_host;
- u8 pci_error_recovery;
- u8 wait_for_port_enable_to_complete;
-
- u8 msix_enable;
- u16 msix_vector_count;
- u32 *msix_table;
- u32 *msix_table_backup;
- u32 ioc_reset_count;
-
- /* internal commands, callback index */
- u8 scsi_io_cb_idx;
- u8 tm_cb_idx;
- u8 transport_cb_idx;
- u8 scsih_cb_idx;
- u8 ctl_cb_idx;
- u8 base_cb_idx;
- u8 config_cb_idx;
- u8 tm_tr_cb_idx;
- u8 tm_tr_volume_cb_idx;
- u8 tm_sas_control_cb_idx;
- struct _internal_cmd base_cmds;
- struct _internal_cmd transport_cmds;
- struct _internal_cmd scsih_cmds;
- struct _internal_cmd tm_cmds;
- struct _internal_cmd ctl_cmds;
- struct _internal_cmd config_cmds;
-
- MPT_ADD_SGE base_add_sg_single;
-
- /* event log */
- u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
- u32 event_context;
- void *event_log;
- u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
-
- /* static config pages */
- Mpi2IOCFactsReply_t facts;
- Mpi2PortFactsReply_t *pfacts;
- Mpi2ManufacturingPage0_t manu_pg0;
- Mpi2BiosPage2_t bios_pg2;
- Mpi2BiosPage3_t bios_pg3;
- Mpi2IOCPage8_t ioc_pg8;
- Mpi2IOUnitPage0_t iounit_pg0;
- Mpi2IOUnitPage1_t iounit_pg1;
-
- struct _boot_device req_boot_device;
- struct _boot_device req_alt_boot_device;
- struct _boot_device current_boot_device;
-
- /* sas hba, expander, and device list */
- struct _sas_node sas_hba;
- struct list_head sas_expander_list;
- spinlock_t sas_node_lock;
- struct list_head sas_device_list;
- struct list_head sas_device_init_list;
- spinlock_t sas_device_lock;
- struct list_head raid_device_list;
- spinlock_t raid_device_lock;
- u8 io_missing_delay;
- u16 device_missing_delay;
- int sas_id;
-
- void *pd_handles;
- u16 pd_handles_sz;
-
- /* config page */
- u16 config_page_sz;
- void *config_page;
- dma_addr_t config_page_dma;
-
- /* scsiio request */
- u16 hba_queue_depth;
- u16 sge_size;
- u16 scsiio_depth;
- u16 request_sz;
- u8 *request;
- dma_addr_t request_dma;
- u32 request_dma_sz;
- struct scsiio_tracker *scsi_lookup;
- ulong scsi_lookup_pages;
- spinlock_t scsi_lookup_lock;
- struct list_head free_list;
- int pending_io_count;
- wait_queue_head_t reset_wq;
-
- /* chain */
- struct chain_tracker *chain_lookup;
- struct list_head free_chain_list;
- struct dma_pool *chain_dma_pool;
- ulong chain_pages;
- u16 max_sges_in_main_message;
- u16 max_sges_in_chain_message;
- u16 chains_needed_per_io;
- u16 chain_offset_value_for_main_message;
- u32 chain_depth;
-
- /* hi-priority queue */
- u16 hi_priority_smid;
- u8 *hi_priority;
- dma_addr_t hi_priority_dma;
- u16 hi_priority_depth;
- struct request_tracker *hpr_lookup;
- struct list_head hpr_free_list;
-
- /* internal queue */
- u16 internal_smid;
- u8 *internal;
- dma_addr_t internal_dma;
- u16 internal_depth;
- struct request_tracker *internal_lookup;
- struct list_head internal_free_list;
-
- /* sense */
- u8 *sense;
- dma_addr_t sense_dma;
- struct dma_pool *sense_dma_pool;
-
- /* reply */
- u16 reply_sz;
- u8 *reply;
- dma_addr_t reply_dma;
- u32 reply_dma_max_address;
- u32 reply_dma_min_address;
- struct dma_pool *reply_dma_pool;
-
- /* reply free queue */
- u16 reply_free_queue_depth;
- u32 *reply_free;
- dma_addr_t reply_free_dma;
- struct dma_pool *reply_free_dma_pool;
- u32 reply_free_host_index;
-
- /* reply post queue */
- u16 reply_post_queue_depth;
- Mpi2ReplyDescriptorsUnion_t *reply_post_free;
- dma_addr_t reply_post_free_dma;
- struct dma_pool *reply_post_free_dma_pool;
- u32 reply_post_host_index;
-
- struct list_head delayed_tr_list;
- struct list_head delayed_tr_volume_list;
-
- /* diag buffer support */
- u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT];
- u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT];
- dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT];
- u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT];
- u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT];
- Mpi2ManufacturingPage10_t manu_pg10;
- u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23];
- u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT];
- u32 ring_buffer_offset;
- u32 ring_buffer_sz;
- u8 is_warpdrive;
- u8 hide_ir_msg;
- u8 mfg_pg10_hide_flag;
- u8 hide_drives;
-
-};
-
-typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply);
-
-
-/* base shared API */
-extern struct list_head mpt2sas_ioc_list;
-void mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc);
-void mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc);
-
-int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc);
-void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc);
-int mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc);
-void mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc);
-int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
- enum reset_type type);
-
-void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid);
-void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid);
-void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr);
-__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc,
- u16 smid);
-
-/* hi-priority queue */
-u16 mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx);
-u16 mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx,
- struct scsi_cmnd *scmd);
-
-u16 mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx);
-void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid);
-void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid,
- u16 handle);
-void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid);
-void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid,
- u16 io_index);
-void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid);
-void mpt2sas_base_initialize_callback_handler(void);
-u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func);
-void mpt2sas_base_release_callback_handler(u8 cb_idx);
-
-u8 mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply);
-void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr);
-
-u32 mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked);
-
-void mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code);
-int mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc,
- Mpi2SasIoUnitControlReply_t *mpi_reply, Mpi2SasIoUnitControlRequest_t
- *mpi_request);
-int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
- Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request);
-void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type);
-
-void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc);
-
-/* scsih shared API */
-u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
- u32 reply);
-int mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- uint channel, uint id, uint lun, u8 type, u16 smid_task,
- ulong timeout, struct scsi_cmnd *scmd);
-void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle);
-void mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle);
-void mpt2sas_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address);
-void mpt2sas_device_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address);
-struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc,
- u16 handle);
-struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER
- *ioc, u64 sas_address);
-struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address(
- struct MPT2SAS_ADAPTER *ioc, u64 sas_address);
-
-void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase);
-
-/* config shared API */
-u8 mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply);
-int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys);
-int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page);
-int mpt2sas_config_get_manufacturing_pg10(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage10_t *config_page);
-int mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2BiosPage2_t *config_page);
-int mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2BiosPage3_t *config_page);
-int mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2IOUnitPage0_t *config_page);
-int mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle);
-int mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle);
-int mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz);
-int mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2IOUnitPage1_t *config_page);
-int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2IOUnitPage1_t *config_page);
-int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
-int mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
-int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2IOCPage8_t *config_page);
-int mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle);
-int mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, u16 handle);
-int mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle);
-int mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number);
-int mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number);
-int mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, u32 handle);
-int mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 *num_pds);
-int mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, u32 handle, u16 sz);
-int mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form,
- u32 form_specific);
-int mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
- u16 *volume_handle);
-int mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle,
- u64 *wwid);
-/* ctl shared API */
-extern struct device_attribute *mpt2sas_host_attrs[];
-extern struct device_attribute *mpt2sas_dev_attrs[];
-void mpt2sas_ctl_init(void);
-void mpt2sas_ctl_exit(void);
-u8 mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply);
-void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase);
-u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
- u32 reply);
-void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventNotificationReply_t *mpi_reply);
-
-void mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc,
- u8 bits_to_regsiter);
-
-/* transport shared API */
-u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply);
-struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc,
- u16 handle, u64 sas_address);
-void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u64 sas_address_parent);
-int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
- *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
-int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
- *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev);
-void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
- u64 sas_address, u16 handle, u8 phy_number, u8 link_rate);
-extern struct sas_function_template mpt2sas_transport_functions;
-extern struct scsi_transport_template *mpt2sas_transport_template;
-extern int scsi_internal_device_block(struct scsi_device *sdev);
-extern u8 mpt2sas_stm_zero_smid_handler(struct MPT2SAS_ADAPTER *ioc,
- u8 msix_index, u32 reply);
-extern int scsi_internal_device_unblock(struct scsi_device *sdev);
-
-#endif /* MPT2SAS_BASE_H_INCLUDED */
diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c
deleted file mode 100644
index 6861244249a3..000000000000
--- a/drivers/scsi/mpt2sas/mpt2sas_config.c
+++ /dev/null
@@ -1,1434 +0,0 @@
-/*
- * This module provides common API for accessing firmware configuration pages
- *
- * This code is based on drivers/scsi/mpt2sas/mpt2_base.c
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * NO WARRANTY
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- * solely responsible for determining the appropriateness of using and
- * distributing the Program and assumes all risks associated with its
- * exercise of rights under this Agreement, including but not limited to
- * the risks and costs of program errors, damage to or loss of data,
- * programs or equipment, and unavailability or interruption of operations.
-
- * DISCLAIMER OF LIABILITY
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/blkdev.h>
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-
-#include "mpt2sas_base.h"
-
-/* local definitions */
-
-/* Timeout for config page request (in seconds) */
-#define MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT 15
-
-/* Common sgl flags for READING a config page. */
-#define MPT2_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
- | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT)
-
-/* Common sgl flags for WRITING a config page. */
-#define MPT2_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
- | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \
- << MPI2_SGE_FLAGS_SHIFT)
-
-/**
- * struct config_request - obtain dma memory via routine
- * @sz: size
- * @page: virt pointer
- * @page_dma: phys pointer
- *
- */
-struct config_request{
- u16 sz;
- void *page;
- dma_addr_t page_dma;
-};
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _config_display_some_debug - debug routine
- * @ioc: per adapter object
- * @smid: system request message index
- * @calling_function_name: string pass from calling function
- * @mpi_reply: reply message frame
- * Context: none.
- *
- * Function for displaying debug info helpful when debugging issues
- * in this module.
- */
-static void
-_config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid,
- char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
-{
- Mpi2ConfigRequest_t *mpi_request;
- char *desc = NULL;
-
- if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
- return;
-
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) {
- case MPI2_CONFIG_PAGETYPE_IO_UNIT:
- desc = "io_unit";
- break;
- case MPI2_CONFIG_PAGETYPE_IOC:
- desc = "ioc";
- break;
- case MPI2_CONFIG_PAGETYPE_BIOS:
- desc = "bios";
- break;
- case MPI2_CONFIG_PAGETYPE_RAID_VOLUME:
- desc = "raid_volume";
- break;
- case MPI2_CONFIG_PAGETYPE_MANUFACTURING:
- desc = "manufaucturing";
- break;
- case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK:
- desc = "physdisk";
- break;
- case MPI2_CONFIG_PAGETYPE_EXTENDED:
- switch (mpi_request->ExtPageType) {
- case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT:
- desc = "sas_io_unit";
- break;
- case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER:
- desc = "sas_expander";
- break;
- case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE:
- desc = "sas_device";
- break;
- case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY:
- desc = "sas_phy";
- break;
- case MPI2_CONFIG_EXTPAGETYPE_LOG:
- desc = "log";
- break;
- case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE:
- desc = "enclosure";
- break;
- case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG:
- desc = "raid_config";
- break;
- case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING:
- desc = "driver_mappping";
- break;
- }
- break;
- }
-
- if (!desc)
- return;
-
- printk(MPT2SAS_INFO_FMT "%s: %s(%d), action(%d), form(0x%08x), "
- "smid(%d)\n", ioc->name, calling_function_name, desc,
- mpi_request->Header.PageNumber, mpi_request->Action,
- le32_to_cpu(mpi_request->PageAddress), smid);
-
- if (!mpi_reply)
- return;
-
- if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
- printk(MPT2SAS_INFO_FMT
- "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
- ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo));
-}
-#endif
-
-/**
- * _config_alloc_config_dma_memory - obtain physical memory
- * @ioc: per adapter object
- * @mem: struct config_request
- *
- * A wrapper for obtaining dma-able memory for config page request.
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_config_alloc_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
- struct config_request *mem)
-{
- int r = 0;
-
- if (mem->sz > ioc->config_page_sz) {
- mem->page = dma_alloc_coherent(&ioc->pdev->dev, mem->sz,
- &mem->page_dma, GFP_KERNEL);
- if (!mem->page) {
- printk(MPT2SAS_ERR_FMT "%s: dma_alloc_coherent"
- " failed asking for (%d) bytes!!\n",
- ioc->name, __func__, mem->sz);
- r = -ENOMEM;
- }
- } else { /* use tmp buffer if less than 512 bytes */
- mem->page = ioc->config_page;
- mem->page_dma = ioc->config_page_dma;
- }
- return r;
-}
-
-/**
- * _config_free_config_dma_memory - wrapper to free the memory
- * @ioc: per adapter object
- * @mem: struct config_request
- *
- * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory.
- *
- * Returns 0 for success, non-zero for failure.
- */
-static void
-_config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
- struct config_request *mem)
-{
- if (mem->sz > ioc->config_page_sz)
- dma_free_coherent(&ioc->pdev->dev, mem->sz, mem->page,
- mem->page_dma);
-}
-
-/**
- * mpt2sas_config_done - config page completion routine
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: none.
- *
- * The callback handler when using _config_request.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-u8
-mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply)
-{
- MPI2DefaultReply_t *mpi_reply;
-
- if (ioc->config_cmds.status == MPT2_CMD_NOT_USED)
- return 1;
- if (ioc->config_cmds.smid != smid)
- return 1;
- ioc->config_cmds.status |= MPT2_CMD_COMPLETE;
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- if (mpi_reply) {
- ioc->config_cmds.status |= MPT2_CMD_REPLY_VALID;
- memcpy(ioc->config_cmds.reply, mpi_reply,
- mpi_reply->MsgLength*4);
- }
- ioc->config_cmds.status &= ~MPT2_CMD_PENDING;
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- _config_display_some_debug(ioc, smid, "config_done", mpi_reply);
-#endif
- ioc->config_cmds.smid = USHRT_MAX;
- complete(&ioc->config_cmds.done);
- return 1;
-}
-
-/**
- * _config_request - main routine for sending config page requests
- * @ioc: per adapter object
- * @mpi_request: request message frame
- * @mpi_reply: reply mf payload returned from firmware
- * @timeout: timeout in seconds
- * @config_page: contents of the config page
- * @config_page_sz: size of config page
- * Context: sleep
- *
- * A generic API for config page requests to firmware.
- *
- * The ioc->config_cmds.status flag should be MPT2_CMD_NOT_USED before calling
- * this API.
- *
- * The callback index is set inside `ioc->config_cb_idx.
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
- *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout,
- void *config_page, u16 config_page_sz)
-{
- u16 smid;
- u32 ioc_state;
- unsigned long timeleft;
- Mpi2ConfigRequest_t *config_request;
- int r;
- u8 retry_count, issue_host_reset = 0;
- u16 wait_state_count;
- struct config_request mem;
-
- mutex_lock(&ioc->config_cmds.mutex);
- if (ioc->config_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: config_cmd in use\n",
- ioc->name, __func__);
- mutex_unlock(&ioc->config_cmds.mutex);
- return -EAGAIN;
- }
-
- retry_count = 0;
- memset(&mem, 0, sizeof(struct config_request));
-
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
-
- if (config_page) {
- mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request->Header.PageType = mpi_reply->Header.PageType;
- mpi_request->Header.PageLength = mpi_reply->Header.PageLength;
- mpi_request->ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request->ExtPageType = mpi_reply->ExtPageType;
- if (mpi_request->Header.PageLength)
- mem.sz = mpi_request->Header.PageLength * 4;
- else
- mem.sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r != 0)
- goto out;
- if (mpi_request->Action ==
- MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
- mpi_request->Action ==
- MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
- ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
- MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz,
- mem.page_dma);
- memcpy(mem.page, config_page, min_t(u16, mem.sz,
- config_page_sz));
- } else {
- memset(config_page, 0, config_page_sz);
- ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.sz, mem.page_dma);
- }
- }
-
- retry_config:
- if (retry_count) {
- if (retry_count > 2) { /* attempt only 2 retries */
- r = -EFAULT;
- goto free_mem;
- }
- printk(MPT2SAS_INFO_FMT "%s: attempting retry (%d)\n",
- ioc->name, __func__, retry_count);
- }
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- r = -EFAULT;
- goto free_mem;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
- if (wait_state_count)
- printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
- ioc->name, __func__);
-
- smid = mpt2sas_base_get_smid(ioc, ioc->config_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- r = -EAGAIN;
- goto free_mem;
- }
-
- r = 0;
- memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t));
- ioc->config_cmds.status = MPT2_CMD_PENDING;
- config_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->config_cmds.smid = smid;
- memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t));
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- _config_display_some_debug(ioc, smid, "config_request", NULL);
-#endif
- init_completion(&ioc->config_cmds.done);
- mpt2sas_base_put_smid_default(ioc, smid);
- timeleft = wait_for_completion_timeout(&ioc->config_cmds.done,
- timeout*HZ);
- if (!(ioc->config_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2ConfigRequest_t)/4);
- retry_count++;
- if (ioc->config_cmds.smid == smid)
- mpt2sas_base_free_smid(ioc, smid);
- if ((ioc->shost_recovery) || (ioc->config_cmds.status &
- MPT2_CMD_RESET) || ioc->pci_error_recovery)
- goto retry_config;
- issue_host_reset = 1;
- r = -EFAULT;
- goto free_mem;
- }
-
- if (ioc->config_cmds.status & MPT2_CMD_REPLY_VALID)
- memcpy(mpi_reply, ioc->config_cmds.reply,
- sizeof(Mpi2ConfigReply_t));
- if (retry_count)
- printk(MPT2SAS_INFO_FMT "%s: retry (%d) completed!!\n",
- ioc->name, __func__, retry_count);
- if (config_page && mpi_request->Action ==
- MPI2_CONFIG_ACTION_PAGE_READ_CURRENT)
- memcpy(config_page, mem.page, min_t(u16, mem.sz,
- config_page_sz));
- free_mem:
- if (config_page)
- _config_free_config_dma_memory(ioc, &mem);
- out:
- ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- mutex_unlock(&ioc->config_cmds.mutex);
-
- if (issue_host_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- return r;
-}
-
-/**
- * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_manufacturing_pg10 - obtain manufacturing page 10
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_manufacturing_pg10(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage10_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
- mpi_request.Header.PageNumber = 10;
- mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_bios_pg2 - obtain bios page 2
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
- mpi_request.Header.PageNumber = 2;
- mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_bios_pg3 - obtain bios page 3
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2BiosPage3_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
- mpi_request.Header.PageNumber = 3;
- mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_iounit_pg0 - obtain iounit page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_iounit_pg1 - obtain iounit page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
- mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_set_iounit_pg1 - set iounit page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
- mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_ioc_pg8 - obtain ioc page 8
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
- mpi_request.Header.PageNumber = 8;
- mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_sas_device_pg0 - obtain sas device page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @form: GET_NEXT_HANDLE or HANDLE
- * @handle: device handle
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
- mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
- mpi_request.Header.PageNumber = 0;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress = cpu_to_le32(form | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_sas_device_pg1 - obtain sas device page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @form: GET_NEXT_HANDLE or HANDLE
- * @handle: device handle
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
- mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION;
- mpi_request.Header.PageNumber = 1;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress = cpu_to_le32(form | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_number_hba_phys - obtain number of phys on the host
- * @ioc: per adapter object
- * @num_phys: pointer returned with the number of phys
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
- u16 ioc_status;
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasIOUnitPage0_t config_page;
-
- *num_phys = 0;
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
- sizeof(Mpi2SasIOUnitPage0_t));
- if (!r) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
- *num_phys = config_page.NumPhys;
- }
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @sz: size of buffer passed in config_page
- * Context: sleep.
- *
- * Calling function should call config_get_number_hba_phys prior to
- * this function, so enough memory is allocated for config_page.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @sz: size of buffer passed in config_page
- * Context: sleep.
- *
- * Calling function should call config_get_number_hba_phys prior to
- * this function, so enough memory is allocated for config_page.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
- mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_set_sas_iounit_pg1 - send sas iounit page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @sz: size of buffer passed in config_page
- * Context: sleep.
- *
- * Calling function should call config_get_number_hba_phys prior to
- * this function, so enough memory is allocated for config_page.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
- mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
- _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_expander_pg0 - obtain expander page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @form: GET_NEXT_HANDLE or HANDLE
- * @handle: expander handle
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress = cpu_to_le32(form | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_expander_pg1 - obtain expander page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @phy_number: phy number
- * @handle: expander handle
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number,
- u16 handle)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
- mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress =
- cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
- (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_enclosure_pg0 - obtain enclosure page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @form: GET_NEXT_HANDLE or HANDLE
- * @handle: expander handle
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress = cpu_to_le32(form | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_phy_pg0 - obtain phy page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @phy_number: phy number
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress =
- cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_phy_pg1 - obtain phy page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @phy_number: phy number
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
- mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress =
- cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_raid_volume_pg1 - obtain raid volume page 1
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @form: GET_NEXT_HANDLE or HANDLE
- * @handle: volume handle
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
- u32 handle)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
- mpi_request.Header.PageNumber = 1;
- mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress = cpu_to_le32(form | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_number_pds - obtain number of phys disk assigned to volume
- * @ioc: per adapter object
- * @handle: volume handle
- * @num_pds: returns pds count
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- u8 *num_pds)
-{
- Mpi2ConfigRequest_t mpi_request;
- Mpi2RaidVolPage0_t config_page;
- Mpi2ConfigReply_t mpi_reply;
- int r;
- u16 ioc_status;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- *num_pds = 0;
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress =
- cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
- sizeof(Mpi2RaidVolPage0_t));
- if (!r) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
- *num_pds = config_page.NumPhysDisks;
- }
-
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_raid_volume_pg0 - obtain raid volume page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @form: GET_NEXT_HANDLE or HANDLE
- * @handle: volume handle
- * @sz: size of buffer passed in config_page
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form,
- u32 handle, u16 sz)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress = cpu_to_le32(form | handle);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_phys_disk_pg0 - obtain phys disk page 0
- * @ioc: per adapter object
- * @mpi_reply: reply mf payload returned from firmware
- * @config_page: contents of the config page
- * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE
- * @form_specific: specific to the form
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form,
- u32 form_specific)
-{
- Mpi2ConfigRequest_t mpi_request;
- int r;
-
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
- mpi_request.Header.PageNumber = 0;
- mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress = cpu_to_le32(form | form_specific);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- sizeof(*config_page));
- out:
- return r;
-}
-
-/**
- * mpt2sas_config_get_volume_handle - returns volume handle for give hidden raid components
- * @ioc: per adapter object
- * @pd_handle: phys disk handle
- * @volume_handle: volume handle
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
- u16 *volume_handle)
-{
- Mpi2RaidConfigurationPage0_t *config_page = NULL;
- Mpi2ConfigRequest_t mpi_request;
- Mpi2ConfigReply_t mpi_reply;
- int r, i, config_page_sz;
- u16 ioc_status;
-
- *volume_handle = 0;
- memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- mpi_request.Function = MPI2_FUNCTION_CONFIG;
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
- mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
- mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG;
- mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION;
- mpi_request.Header.PageNumber = 0;
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
- r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
- if (r)
- goto out;
-
- mpi_request.PageAddress =
- cpu_to_le32(MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG);
- mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4);
- config_page = kmalloc(config_page_sz, GFP_KERNEL);
- if (!config_page)
- goto out;
- r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
- config_page_sz);
- if (r)
- goto out;
-
- r = -1;
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
- goto out;
- for (i = 0; i < config_page->NumElements; i++) {
- if ((le16_to_cpu(config_page->ConfigElement[i].ElementFlags) &
- MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) !=
- MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT)
- continue;
- if (le16_to_cpu(config_page->ConfigElement[i].
- PhysDiskDevHandle) == pd_handle) {
- *volume_handle = le16_to_cpu(config_page->
- ConfigElement[i].VolDevHandle);
- r = 0;
- goto out;
- }
- }
- out:
- kfree(config_page);
- return r;
-}
-
-/**
- * mpt2sas_config_get_volume_wwid - returns wwid given the volume handle
- * @ioc: per adapter object
- * @volume_handle: volume handle
- * @wwid: volume wwid
- * Context: sleep.
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle,
- u64 *wwid)
-{
- Mpi2ConfigReply_t mpi_reply;
- Mpi2RaidVolPage1_t raid_vol_pg1;
-
- *wwid = 0;
- if (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
- &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE,
- volume_handle))) {
- *wwid = le64_to_cpu(raid_vol_pg1.WWID);
- return 0;
- } else
- return -1;
-}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
deleted file mode 100644
index 437c2d94c45a..000000000000
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ /dev/null
@@ -1,3028 +0,0 @@
-/*
- * Management Module Support for MPT (Message Passing Technology) based
- * controllers
- *
- * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * NO WARRANTY
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- * solely responsible for determining the appropriateness of using and
- * distributing the Program and assumes all risks associated with its
- * exercise of rights under this Agreement, including but not limited to
- * the risks and costs of program errors, damage to or loss of data,
- * programs or equipment, and unavailability or interruption of operations.
-
- * DISCLAIMER OF LIABILITY
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <linux/compat.h>
-#include <linux/poll.h>
-
-#include <linux/io.h>
-#include <linux/uaccess.h>
-
-#include "mpt2sas_base.h"
-#include "mpt2sas_ctl.h"
-
-static DEFINE_MUTEX(_ctl_mutex);
-static struct fasync_struct *async_queue;
-static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait);
-
-static int _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type,
- u8 *issue_reset);
-
-/**
- * enum block_state - blocking state
- * @NON_BLOCKING: non blocking
- * @BLOCKING: blocking
- *
- * These states are for ioctls that need to wait for a response
- * from firmware, so they probably require sleep.
- */
-enum block_state {
- NON_BLOCKING,
- BLOCKING,
-};
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _ctl_sas_device_find_by_handle - sas device search
- * @ioc: per adapter object
- * @handle: sas device handle (assigned by firmware)
- * Context: Calling function should acquire ioc->sas_device_lock
- *
- * This searches for sas_device based on sas_address, then return sas_device
- * object.
- */
-static struct _sas_device *
-_ctl_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _sas_device *sas_device, *r;
-
- r = NULL;
- list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
- if (sas_device->handle != handle)
- continue;
- r = sas_device;
- goto out;
- }
-
- out:
- return r;
-}
-
-/**
- * _ctl_display_some_debug - debug routine
- * @ioc: per adapter object
- * @smid: system request message index
- * @calling_function_name: string pass from calling function
- * @mpi_reply: reply message frame
- * Context: none.
- *
- * Function for displaying debug info helpful when debugging issues
- * in this module.
- */
-static void
-_ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid,
- char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
-{
- Mpi2ConfigRequest_t *mpi_request;
- char *desc = NULL;
-
- if (!(ioc->logging_level & MPT_DEBUG_IOCTL))
- return;
-
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- switch (mpi_request->Function) {
- case MPI2_FUNCTION_SCSI_IO_REQUEST:
- {
- Mpi2SCSIIORequest_t *scsi_request =
- (Mpi2SCSIIORequest_t *)mpi_request;
-
- snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
- "scsi_io, cmd(0x%02x), cdb_len(%d)",
- scsi_request->CDB.CDB32[0],
- le16_to_cpu(scsi_request->IoFlags) & 0xF);
- desc = ioc->tmp_string;
- break;
- }
- case MPI2_FUNCTION_SCSI_TASK_MGMT:
- desc = "task_mgmt";
- break;
- case MPI2_FUNCTION_IOC_INIT:
- desc = "ioc_init";
- break;
- case MPI2_FUNCTION_IOC_FACTS:
- desc = "ioc_facts";
- break;
- case MPI2_FUNCTION_CONFIG:
- {
- Mpi2ConfigRequest_t *config_request =
- (Mpi2ConfigRequest_t *)mpi_request;
-
- snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
- "config, type(0x%02x), ext_type(0x%02x), number(%d)",
- (config_request->Header.PageType &
- MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType,
- config_request->Header.PageNumber);
- desc = ioc->tmp_string;
- break;
- }
- case MPI2_FUNCTION_PORT_FACTS:
- desc = "port_facts";
- break;
- case MPI2_FUNCTION_PORT_ENABLE:
- desc = "port_enable";
- break;
- case MPI2_FUNCTION_EVENT_NOTIFICATION:
- desc = "event_notification";
- break;
- case MPI2_FUNCTION_FW_DOWNLOAD:
- desc = "fw_download";
- break;
- case MPI2_FUNCTION_FW_UPLOAD:
- desc = "fw_upload";
- break;
- case MPI2_FUNCTION_RAID_ACTION:
- desc = "raid_action";
- break;
- case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
- {
- Mpi2SCSIIORequest_t *scsi_request =
- (Mpi2SCSIIORequest_t *)mpi_request;
-
- snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
- "raid_pass, cmd(0x%02x), cdb_len(%d)",
- scsi_request->CDB.CDB32[0],
- le16_to_cpu(scsi_request->IoFlags) & 0xF);
- desc = ioc->tmp_string;
- break;
- }
- case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
- desc = "sas_iounit_cntl";
- break;
- case MPI2_FUNCTION_SATA_PASSTHROUGH:
- desc = "sata_pass";
- break;
- case MPI2_FUNCTION_DIAG_BUFFER_POST:
- desc = "diag_buffer_post";
- break;
- case MPI2_FUNCTION_DIAG_RELEASE:
- desc = "diag_release";
- break;
- case MPI2_FUNCTION_SMP_PASSTHROUGH:
- desc = "smp_passthrough";
- break;
- }
-
- if (!desc)
- return;
-
- printk(MPT2SAS_INFO_FMT "%s: %s, smid(%d)\n",
- ioc->name, calling_function_name, desc, smid);
-
- if (!mpi_reply)
- return;
-
- if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
- printk(MPT2SAS_INFO_FMT
- "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
- ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo));
-
- if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
- mpi_request->Function ==
- MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
- Mpi2SCSIIOReply_t *scsi_reply =
- (Mpi2SCSIIOReply_t *)mpi_reply;
- struct _sas_device *sas_device = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _ctl_sas_device_find_by_handle(ioc,
- le16_to_cpu(scsi_reply->DevHandle));
- if (sas_device) {
- printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), "
- "phy(%d)\n", ioc->name, (unsigned long long)
- sas_device->sas_address, sas_device->phy);
- printk(MPT2SAS_WARN_FMT
- "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
- ioc->name, sas_device->enclosure_logical_id,
- sas_device->slot);
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (scsi_reply->SCSIState || scsi_reply->SCSIStatus)
- printk(MPT2SAS_INFO_FMT
- "\tscsi_state(0x%02x), scsi_status"
- "(0x%02x)\n", ioc->name,
- scsi_reply->SCSIState,
- scsi_reply->SCSIStatus);
- }
-}
-#endif
-
-/**
- * mpt2sas_ctl_done - ctl module completion routine
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: none.
- *
- * The callback handler when using ioc->ctl_cb_idx.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-u8
-mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply)
-{
- MPI2DefaultReply_t *mpi_reply;
- Mpi2SCSIIOReply_t *scsiio_reply;
- const void *sense_data;
- u32 sz;
-
- if (ioc->ctl_cmds.status == MPT2_CMD_NOT_USED)
- return 1;
- if (ioc->ctl_cmds.smid != smid)
- return 1;
- ioc->ctl_cmds.status |= MPT2_CMD_COMPLETE;
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- if (mpi_reply) {
- memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
- ioc->ctl_cmds.status |= MPT2_CMD_REPLY_VALID;
- /* get sense data */
- if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
- mpi_reply->Function ==
- MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
- scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply;
- if (scsiio_reply->SCSIState &
- MPI2_SCSI_STATE_AUTOSENSE_VALID) {
- sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
- le32_to_cpu(scsiio_reply->SenseCount));
- sense_data = mpt2sas_base_get_sense_buffer(ioc,
- smid);
- memcpy(ioc->ctl_cmds.sense, sense_data, sz);
- }
- }
- }
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply);
-#endif
- ioc->ctl_cmds.status &= ~MPT2_CMD_PENDING;
- complete(&ioc->ctl_cmds.done);
- return 1;
-}
-
-/**
- * _ctl_check_event_type - determines when an event needs logging
- * @ioc: per adapter object
- * @event: firmware event
- *
- * The bitmask in ioc->event_type[] indicates which events should be
- * be saved in the driver event_log. This bitmask is set by application.
- *
- * Returns 1 when event should be captured, or zero means no match.
- */
-static int
-_ctl_check_event_type(struct MPT2SAS_ADAPTER *ioc, u16 event)
-{
- u16 i;
- u32 desired_event;
-
- if (event >= 128 || !event || !ioc->event_log)
- return 0;
-
- desired_event = (1 << (event % 32));
- if (!desired_event)
- desired_event = 1;
- i = event / 32;
- return desired_event & ioc->event_type[i];
-}
-
-/**
- * mpt2sas_ctl_add_to_event_log - add event
- * @ioc: per adapter object
- * @mpi_reply: reply message frame
- *
- * Return nothing.
- */
-void
-mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventNotificationReply_t *mpi_reply)
-{
- struct MPT2_IOCTL_EVENTS *event_log;
- u16 event;
- int i;
- u32 sz, event_data_sz;
- u8 send_aen = 0;
-
- if (!ioc->event_log)
- return;
-
- event = le16_to_cpu(mpi_reply->Event);
-
- if (_ctl_check_event_type(ioc, event)) {
-
- /* insert entry into circular event_log */
- i = ioc->event_context % MPT2SAS_CTL_EVENT_LOG_SIZE;
- event_log = ioc->event_log;
- event_log[i].event = event;
- event_log[i].context = ioc->event_context++;
-
- event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4;
- sz = min_t(u32, event_data_sz, MPT2_EVENT_DATA_SIZE);
- memset(event_log[i].data, 0, MPT2_EVENT_DATA_SIZE);
- memcpy(event_log[i].data, mpi_reply->EventData, sz);
- send_aen = 1;
- }
-
- /* This aen_event_read_flag flag is set until the
- * application has read the event log.
- * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify.
- */
- if (event == MPI2_EVENT_LOG_ENTRY_ADDED ||
- (send_aen && !ioc->aen_event_read_flag)) {
- ioc->aen_event_read_flag = 1;
- wake_up_interruptible(&ctl_poll_wait);
- if (async_queue)
- kill_fasync(&async_queue, SIGIO, POLL_IN);
- }
-}
-
-/**
- * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time)
- * @ioc: per adapter object
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: interrupt.
- *
- * This function merely adds a new work task into ioc->firmware_event_thread.
- * The tasks are worked from _firmware_event_work in user context.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-u8
-mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
- u32 reply)
-{
- Mpi2EventNotificationReply_t *mpi_reply;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- mpt2sas_ctl_add_to_event_log(ioc, mpi_reply);
- return 1;
-}
-
-/**
- * _ctl_verify_adapter - validates ioc_number passed from application
- * @ioc: per adapter object
- * @iocpp: The ioc pointer is returned in this.
- *
- * Return (-1) means error, else ioc_number.
- */
-static int
-_ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp)
-{
- struct MPT2SAS_ADAPTER *ioc;
-
- list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
- if (ioc->id != ioc_number)
- continue;
- *iocpp = ioc;
- return ioc_number;
- }
- *iocpp = NULL;
- return -1;
-}
-
-/**
- * mpt2sas_ctl_reset_handler - reset callback handler (for ctl)
- * @ioc: per adapter object
- * @reset_phase: phase
- *
- * The handler for doing any required cleanup or initialization.
- *
- * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
- * MPT2_IOC_DONE_RESET
- */
-void
-mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
-{
- int i;
- u8 issue_reset;
-
- switch (reset_phase) {
- case MPT2_IOC_PRE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
- for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
- if (!(ioc->diag_buffer_status[i] &
- MPT2_DIAG_BUFFER_IS_REGISTERED))
- continue;
- if ((ioc->diag_buffer_status[i] &
- MPT2_DIAG_BUFFER_IS_RELEASED))
- continue;
- _ctl_send_release(ioc, i, &issue_reset);
- }
- break;
- case MPT2_IOC_AFTER_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
- if (ioc->ctl_cmds.status & MPT2_CMD_PENDING) {
- ioc->ctl_cmds.status |= MPT2_CMD_RESET;
- mpt2sas_base_free_smid(ioc, ioc->ctl_cmds.smid);
- complete(&ioc->ctl_cmds.done);
- }
- break;
- case MPT2_IOC_DONE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
-
- for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
- if (!(ioc->diag_buffer_status[i] &
- MPT2_DIAG_BUFFER_IS_REGISTERED))
- continue;
- if ((ioc->diag_buffer_status[i] &
- MPT2_DIAG_BUFFER_IS_RELEASED))
- continue;
- ioc->diag_buffer_status[i] |=
- MPT2_DIAG_BUFFER_IS_DIAG_RESET;
- }
- break;
- }
-}
-
-/**
- * _ctl_fasync -
- * @fd -
- * @filep -
- * @mode -
- *
- * Called when application request fasyn callback handler.
- */
-static int
-_ctl_fasync(int fd, struct file *filep, int mode)
-{
- return fasync_helper(fd, filep, mode, &async_queue);
-}
-
-/**
- * _ctl_release -
- * @inode -
- * @filep -
- *
- * Called when application releases the fasyn callback handler.
- */
-static int
-_ctl_release(struct inode *inode, struct file *filep)
-{
- return fasync_helper(-1, filep, 0, &async_queue);
-}
-
-/**
- * _ctl_poll -
- * @file -
- * @wait -
- *
- */
-static unsigned int
-_ctl_poll(struct file *filep, poll_table *wait)
-{
- struct MPT2SAS_ADAPTER *ioc;
-
- poll_wait(filep, &ctl_poll_wait, wait);
-
- list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
- if (ioc->aen_event_read_flag)
- return POLLIN | POLLRDNORM;
- }
- return 0;
-}
-
-/**
- * _ctl_set_task_mid - assign an active smid to tm request
- * @ioc: per adapter object
- * @karg - (struct mpt2_ioctl_command)
- * @tm_request - pointer to mf from user space
- *
- * Returns 0 when an smid if found, else fail.
- * during failure, the reply frame is filled.
- */
-static int
-_ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
- Mpi2SCSITaskManagementRequest_t *tm_request)
-{
- u8 found = 0;
- u16 i;
- u16 handle;
- struct scsi_cmnd *scmd;
- struct MPT2SAS_DEVICE *priv_data;
- unsigned long flags;
- Mpi2SCSITaskManagementReply_t *tm_reply;
- u32 sz;
- u32 lun;
- char *desc = NULL;
-
- if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
- desc = "abort_task";
- else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
- desc = "query_task";
- else
- return 0;
-
- lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN);
-
- handle = le16_to_cpu(tm_request->DevHandle);
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- for (i = ioc->scsiio_depth; i && !found; i--) {
- scmd = ioc->scsi_lookup[i - 1].scmd;
- if (scmd == NULL || scmd->device == NULL ||
- scmd->device->hostdata == NULL)
- continue;
- if (lun != scmd->device->lun)
- continue;
- priv_data = scmd->device->hostdata;
- if (priv_data->sas_target == NULL)
- continue;
- if (priv_data->sas_target->handle != handle)
- continue;
- tm_request->TaskMID = cpu_to_le16(ioc->scsi_lookup[i - 1].smid);
- found = 1;
- }
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-
- if (!found) {
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "handle(0x%04x), lun(%d), no active mid!!\n", ioc->name,
- desc, le16_to_cpu(tm_request->DevHandle), lun));
- tm_reply = ioc->ctl_cmds.reply;
- tm_reply->DevHandle = tm_request->DevHandle;
- tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
- tm_reply->TaskType = tm_request->TaskType;
- tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4;
- tm_reply->VP_ID = tm_request->VP_ID;
- tm_reply->VF_ID = tm_request->VF_ID;
- sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz);
- if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply,
- sz))
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- return 1;
- }
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name,
- desc, le16_to_cpu(tm_request->DevHandle), lun,
- le16_to_cpu(tm_request->TaskMID)));
- return 0;
-}
-
-/**
- * _ctl_do_mpt_command - main handler for MPT2COMMAND opcode
- * @ioc: per adapter object
- * @karg - (struct mpt2_ioctl_command)
- * @mf - pointer to mf in user space
- * @state - NON_BLOCKING or BLOCKING
- */
-static long
-_ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
- struct mpt2_ioctl_command karg, void __user *mf, enum block_state state)
-{
- MPI2RequestHeader_t *mpi_request = NULL, *request;
- MPI2DefaultReply_t *mpi_reply;
- u32 ioc_state;
- u16 ioc_status;
- u16 smid;
- unsigned long timeout, timeleft;
- u8 issue_reset;
- u32 sz;
- void *psge;
- void *data_out = NULL;
- dma_addr_t data_out_dma;
- size_t data_out_sz = 0;
- void *data_in = NULL;
- dma_addr_t data_in_dma;
- size_t data_in_sz = 0;
- u32 sgl_flags;
- long ret;
- u16 wait_state_count;
-
- issue_reset = 0;
-
- if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
- return -EAGAIN;
- else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
- return -ERESTARTSYS;
-
- if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
- ioc->name, __func__);
- ret = -EAGAIN;
- goto out;
- }
-
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == 10) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- ret = -EFAULT;
- goto out;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
- if (wait_state_count)
- printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
- ioc->name, __func__);
-
- mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL);
- if (!mpi_request) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a memory for "
- "mpi_request\n", ioc->name, __func__);
- ret = -ENOMEM;
- goto out;
- }
-
- /* Check for overflow and wraparound */
- if (karg.data_sge_offset * 4 > ioc->request_sz ||
- karg.data_sge_offset > (UINT_MAX / 4)) {
- ret = -EINVAL;
- goto out;
- }
-
- /* copy in request message frame from user */
- if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__,
- __func__);
- ret = -EFAULT;
- goto out;
- }
-
- if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
- smid = mpt2sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- ret = -EAGAIN;
- goto out;
- }
- } else {
-
- smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- ret = -EAGAIN;
- goto out;
- }
- }
-
- ret = 0;
- ioc->ctl_cmds.status = MPT2_CMD_PENDING;
- memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
- request = mpt2sas_base_get_msg_frame(ioc, smid);
- memcpy(request, mpi_request, karg.data_sge_offset*4);
- ioc->ctl_cmds.smid = smid;
- data_out_sz = karg.data_out_size;
- data_in_sz = karg.data_in_size;
-
- if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
- mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
- if (!le16_to_cpu(mpi_request->FunctionDependent1) ||
- le16_to_cpu(mpi_request->FunctionDependent1) >
- ioc->facts.MaxDevHandle) {
- ret = -EINVAL;
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
- }
-
- /* obtain dma-able memory for data transfer */
- if (data_out_sz) /* WRITE */ {
- data_out = pci_alloc_consistent(ioc->pdev, data_out_sz,
- &data_out_dma);
- if (!data_out) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- ret = -ENOMEM;
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
- if (copy_from_user(data_out, karg.data_out_buf_ptr,
- data_out_sz)) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- ret = -EFAULT;
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
- }
-
- if (data_in_sz) /* READ */ {
- data_in = pci_alloc_consistent(ioc->pdev, data_in_sz,
- &data_in_dma);
- if (!data_in) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- ret = -ENOMEM;
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
- }
-
- /* add scatter gather elements */
- psge = (void *)request + (karg.data_sge_offset*4);
-
- if (!data_out_sz && !data_in_sz) {
- mpt2sas_base_build_zero_len_sge(ioc, psge);
- } else if (data_out_sz && data_in_sz) {
- /* WRITE sgel first */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- data_out_sz, data_out_dma);
-
- /* incr sgel */
- psge += ioc->sge_size;
-
- /* READ sgel last */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- data_in_sz, data_in_dma);
- } else if (data_out_sz) /* WRITE */ {
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- data_out_sz, data_out_dma);
- } else if (data_in_sz) /* READ */ {
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- data_in_sz, data_in_dma);
- }
-
- /* send command to firmware */
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- _ctl_display_some_debug(ioc, smid, "ctl_request", NULL);
-#endif
-
- switch (mpi_request->Function) {
- case MPI2_FUNCTION_SCSI_IO_REQUEST:
- case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
- {
- Mpi2SCSIIORequest_t *scsiio_request =
- (Mpi2SCSIIORequest_t *)request;
- scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
- scsiio_request->SenseBufferLowAddress =
- mpt2sas_base_get_sense_buffer_dma(ioc, smid);
- memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE);
- if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)
- mpt2sas_base_put_smid_scsi_io(ioc, smid,
- le16_to_cpu(mpi_request->FunctionDependent1));
- else
- mpt2sas_base_put_smid_default(ioc, smid);
- break;
- }
- case MPI2_FUNCTION_SCSI_TASK_MGMT:
- {
- Mpi2SCSITaskManagementRequest_t *tm_request =
- (Mpi2SCSITaskManagementRequest_t *)request;
-
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "TASK_MGMT: "
- "handle(0x%04x), task_type(0x%02x)\n", ioc->name,
- le16_to_cpu(tm_request->DevHandle), tm_request->TaskType));
-
- if (tm_request->TaskType ==
- MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK ||
- tm_request->TaskType ==
- MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
- if (_ctl_set_task_mid(ioc, &karg, tm_request)) {
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
- }
-
- mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu(
- tm_request->DevHandle));
- mpt2sas_base_put_smid_hi_priority(ioc, smid);
- break;
- }
- case MPI2_FUNCTION_SMP_PASSTHROUGH:
- {
- Mpi2SmpPassthroughRequest_t *smp_request =
- (Mpi2SmpPassthroughRequest_t *)mpi_request;
- u8 *data;
-
- /* ioc determines which port to use */
- smp_request->PhysicalPort = 0xFF;
- if (smp_request->PassthroughFlags &
- MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE)
- data = (u8 *)&smp_request->SGL;
- else
- data = data_out;
-
- if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) {
- ioc->ioc_link_reset_in_progress = 1;
- ioc->ignore_loginfos = 1;
- }
- mpt2sas_base_put_smid_default(ioc, smid);
- break;
- }
- case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
- {
- Mpi2SasIoUnitControlRequest_t *sasiounit_request =
- (Mpi2SasIoUnitControlRequest_t *)mpi_request;
-
- if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET
- || sasiounit_request->Operation ==
- MPI2_SAS_OP_PHY_LINK_RESET) {
- ioc->ioc_link_reset_in_progress = 1;
- ioc->ignore_loginfos = 1;
- }
- mpt2sas_base_put_smid_default(ioc, smid);
- break;
- }
- default:
- mpt2sas_base_put_smid_default(ioc, smid);
- break;
- }
-
- if (karg.timeout < MPT2_IOCTL_DEFAULT_TIMEOUT)
- timeout = MPT2_IOCTL_DEFAULT_TIMEOUT;
- else
- timeout = karg.timeout;
- init_completion(&ioc->ctl_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
- timeout*HZ);
- if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
- Mpi2SCSITaskManagementRequest_t *tm_request =
- (Mpi2SCSITaskManagementRequest_t *)mpi_request;
- mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu(
- tm_request->DevHandle));
- } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH ||
- mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) &&
- ioc->ioc_link_reset_in_progress) {
- ioc->ioc_link_reset_in_progress = 0;
- ioc->ignore_loginfos = 0;
- }
- if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
- __func__);
- _debug_dump_mf(mpi_request, karg.data_sge_offset);
- if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
-
- mpi_reply = ioc->ctl_cmds.reply;
- ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT &&
- (ioc->logging_level & MPT_DEBUG_TM)) {
- Mpi2SCSITaskManagementReply_t *tm_reply =
- (Mpi2SCSITaskManagementReply_t *)mpi_reply;
-
- printk(MPT2SAS_INFO_FMT "TASK_MGMT: "
- "IOCStatus(0x%04x), IOCLogInfo(0x%08x), "
- "TerminationCount(0x%08x)\n", ioc->name,
- le16_to_cpu(tm_reply->IOCStatus),
- le32_to_cpu(tm_reply->IOCLogInfo),
- le32_to_cpu(tm_reply->TerminationCount));
- }
-#endif
- /* copy out xdata to user */
- if (data_in_sz) {
- if (copy_to_user(karg.data_in_buf_ptr, data_in,
- data_in_sz)) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- ret = -ENODATA;
- goto out;
- }
- }
-
- /* copy out reply message frame to user */
- if (karg.max_reply_bytes) {
- sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz);
- if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply,
- sz)) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- ret = -ENODATA;
- goto out;
- }
- }
-
- /* copy out sense to user */
- if (karg.max_sense_bytes && (mpi_request->Function ==
- MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function ==
- MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
- sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE);
- if (copy_to_user(karg.sense_data_ptr,
- ioc->ctl_cmds.sense, sz)) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- ret = -ENODATA;
- goto out;
- }
- }
-
- issue_host_reset:
- if (issue_reset) {
- ret = -ENODATA;
- if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
- mpi_request->Function ==
- MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
- printk(MPT2SAS_INFO_FMT "issue target reset: handle "
- "= (0x%04x)\n", ioc->name,
- le16_to_cpu(mpi_request->FunctionDependent1));
- mpt2sas_halt_firmware(ioc);
- mpt2sas_scsih_issue_tm(ioc,
- le16_to_cpu(mpi_request->FunctionDependent1), 0, 0,
- 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10,
- NULL);
- ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
- } else
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- }
-
- out:
-
- /* free memory associated with sg buffers */
- if (data_in)
- pci_free_consistent(ioc->pdev, data_in_sz, data_in,
- data_in_dma);
-
- if (data_out)
- pci_free_consistent(ioc->pdev, data_out_sz, data_out,
- data_out_dma);
-
- kfree(mpi_request);
- ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
- mutex_unlock(&ioc->ctl_cmds.mutex);
- return ret;
-}
-
-/**
- * _ctl_getiocinfo - main handler for MPT2IOCINFO opcode
- * @arg - user space buffer containing ioctl content
- */
-static long
-_ctl_getiocinfo(void __user *arg)
-{
- struct mpt2_ioctl_iocinfo karg;
- struct MPT2SAS_ADAPTER *ioc;
- u8 revision;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
- __func__));
-
- memset(&karg, 0 , sizeof(karg));
- if (ioc->is_warpdrive)
- karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200;
- else
- karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
- if (ioc->pfacts)
- karg.port_number = ioc->pfacts[0].PortNumber;
- pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
- karg.hw_rev = revision;
- karg.pci_id = ioc->pdev->device;
- karg.subsystem_device = ioc->pdev->subsystem_device;
- karg.subsystem_vendor = ioc->pdev->subsystem_vendor;
- karg.pci_information.u.bits.bus = ioc->pdev->bus->number;
- karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn);
- karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn);
- karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus);
- karg.firmware_version = ioc->facts.FWVersion.Word;
- strcpy(karg.driver_version, MPT2SAS_DRIVER_NAME);
- strcat(karg.driver_version, "-");
- strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION);
- karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
-
- if (copy_to_user(arg, &karg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- return 0;
-}
-
-/**
- * _ctl_eventquery - main handler for MPT2EVENTQUERY opcode
- * @arg - user space buffer containing ioctl content
- */
-static long
-_ctl_eventquery(void __user *arg)
-{
- struct mpt2_ioctl_eventquery karg;
- struct MPT2SAS_ADAPTER *ioc;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
- __func__));
-
- karg.event_entries = MPT2SAS_CTL_EVENT_LOG_SIZE;
- memcpy(karg.event_types, ioc->event_type,
- MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
-
- if (copy_to_user(arg, &karg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- return 0;
-}
-
-/**
- * _ctl_eventenable - main handler for MPT2EVENTENABLE opcode
- * @arg - user space buffer containing ioctl content
- */
-static long
-_ctl_eventenable(void __user *arg)
-{
- struct mpt2_ioctl_eventenable karg;
- struct MPT2SAS_ADAPTER *ioc;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
- __func__));
-
- if (ioc->event_log)
- return 0;
- memcpy(ioc->event_type, karg.event_types,
- MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
- mpt2sas_base_validate_event_type(ioc, ioc->event_type);
-
- /* initialize event_log */
- ioc->event_context = 0;
- ioc->aen_event_read_flag = 0;
- ioc->event_log = kcalloc(MPT2SAS_CTL_EVENT_LOG_SIZE,
- sizeof(struct MPT2_IOCTL_EVENTS), GFP_KERNEL);
- if (!ioc->event_log) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -ENOMEM;
- }
- return 0;
-}
-
-/**
- * _ctl_eventreport - main handler for MPT2EVENTREPORT opcode
- * @arg - user space buffer containing ioctl content
- */
-static long
-_ctl_eventreport(void __user *arg)
-{
- struct mpt2_ioctl_eventreport karg;
- struct MPT2SAS_ADAPTER *ioc;
- u32 number_bytes, max_events, max;
- struct mpt2_ioctl_eventreport __user *uarg = arg;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
- __func__));
-
- number_bytes = karg.hdr.max_data_size -
- sizeof(struct mpt2_ioctl_header);
- max_events = number_bytes/sizeof(struct MPT2_IOCTL_EVENTS);
- max = min_t(u32, MPT2SAS_CTL_EVENT_LOG_SIZE, max_events);
-
- /* If fewer than 1 event is requested, there must have
- * been some type of error.
- */
- if (!max || !ioc->event_log)
- return -ENODATA;
-
- number_bytes = max * sizeof(struct MPT2_IOCTL_EVENTS);
- if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
-
- /* reset flag so SIGIO can restart */
- ioc->aen_event_read_flag = 0;
- return 0;
-}
-
-/**
- * _ctl_do_reset - main handler for MPT2HARDRESET opcode
- * @arg - user space buffer containing ioctl content
- */
-static long
-_ctl_do_reset(void __user *arg)
-{
- struct mpt2_ioctl_diag_reset karg;
- struct MPT2SAS_ADAPTER *ioc;
- int retval;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
- __func__));
-
- retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- printk(MPT2SAS_INFO_FMT "host reset: %s\n",
- ioc->name, ((!retval) ? "SUCCESS" : "FAILED"));
- return 0;
-}
-
-/**
- * _ctl_btdh_search_sas_device - searching for sas device
- * @ioc: per adapter object
- * @btdh: btdh ioctl payload
- */
-static int
-_ctl_btdh_search_sas_device(struct MPT2SAS_ADAPTER *ioc,
- struct mpt2_ioctl_btdh_mapping *btdh)
-{
- struct _sas_device *sas_device;
- unsigned long flags;
- int rc = 0;
-
- if (list_empty(&ioc->sas_device_list))
- return rc;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
- if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
- btdh->handle == sas_device->handle) {
- btdh->bus = sas_device->channel;
- btdh->id = sas_device->id;
- rc = 1;
- goto out;
- } else if (btdh->bus == sas_device->channel && btdh->id ==
- sas_device->id && btdh->handle == 0xFFFF) {
- btdh->handle = sas_device->handle;
- rc = 1;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- return rc;
-}
-
-/**
- * _ctl_btdh_search_raid_device - searching for raid device
- * @ioc: per adapter object
- * @btdh: btdh ioctl payload
- */
-static int
-_ctl_btdh_search_raid_device(struct MPT2SAS_ADAPTER *ioc,
- struct mpt2_ioctl_btdh_mapping *btdh)
-{
- struct _raid_device *raid_device;
- unsigned long flags;
- int rc = 0;
-
- if (list_empty(&ioc->raid_device_list))
- return rc;
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
- if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
- btdh->handle == raid_device->handle) {
- btdh->bus = raid_device->channel;
- btdh->id = raid_device->id;
- rc = 1;
- goto out;
- } else if (btdh->bus == raid_device->channel && btdh->id ==
- raid_device->id && btdh->handle == 0xFFFF) {
- btdh->handle = raid_device->handle;
- rc = 1;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- return rc;
-}
-
-/**
- * _ctl_btdh_mapping - main handler for MPT2BTDHMAPPING opcode
- * @arg - user space buffer containing ioctl content
- */
-static long
-_ctl_btdh_mapping(void __user *arg)
-{
- struct mpt2_ioctl_btdh_mapping karg;
- struct MPT2SAS_ADAPTER *ioc;
- int rc;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- rc = _ctl_btdh_search_sas_device(ioc, &karg);
- if (!rc)
- _ctl_btdh_search_raid_device(ioc, &karg);
-
- if (copy_to_user(arg, &karg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- return 0;
-}
-
-/**
- * _ctl_diag_capability - return diag buffer capability
- * @ioc: per adapter object
- * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
- *
- * returns 1 when diag buffer support is enabled in firmware
- */
-static u8
-_ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type)
-{
- u8 rc = 0;
-
- switch (buffer_type) {
- case MPI2_DIAG_BUF_TYPE_TRACE:
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER)
- rc = 1;
- break;
- case MPI2_DIAG_BUF_TYPE_SNAPSHOT:
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
- rc = 1;
- break;
- case MPI2_DIAG_BUF_TYPE_EXTENDED:
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
- rc = 1;
- }
-
- return rc;
-}
-
-/**
- * _ctl_diag_register_2 - wrapper for registering diag buffer support
- * @ioc: per adapter object
- * @diag_register: the diag_register struct passed in from user space
- *
- */
-static long
-_ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc,
- struct mpt2_diag_register *diag_register)
-{
- int rc, i;
- void *request_data = NULL;
- dma_addr_t request_data_dma;
- u32 request_data_sz = 0;
- Mpi2DiagBufferPostRequest_t *mpi_request;
- Mpi2DiagBufferPostReply_t *mpi_reply;
- u8 buffer_type;
- unsigned long timeleft;
- u16 smid;
- u16 ioc_status;
- u8 issue_reset = 0;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- buffer_type = diag_register->buffer_type;
- if (!_ctl_diag_capability(ioc, buffer_type)) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -EPERM;
- }
-
- if (ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) {
- printk(MPT2SAS_ERR_FMT "%s: already has a registered "
- "buffer for buffer_type(0x%02x)\n", ioc->name, __func__,
- buffer_type);
- return -EINVAL;
- }
-
- if (diag_register->requested_buffer_size % 4) {
- printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size "
- "is not 4 byte aligned\n", ioc->name, __func__);
- return -EINVAL;
- }
-
- smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- rc = 0;
- ioc->ctl_cmds.status = MPT2_CMD_PENDING;
- memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->ctl_cmds.smid = smid;
-
- request_data = ioc->diag_buffer[buffer_type];
- request_data_sz = diag_register->requested_buffer_size;
- ioc->unique_id[buffer_type] = diag_register->unique_id;
- ioc->diag_buffer_status[buffer_type] = 0;
- memcpy(ioc->product_specific[buffer_type],
- diag_register->product_specific, MPT2_PRODUCT_SPECIFIC_DWORDS);
- ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags;
-
- if (request_data) {
- request_data_dma = ioc->diag_buffer_dma[buffer_type];
- if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) {
- pci_free_consistent(ioc->pdev,
- ioc->diag_buffer_sz[buffer_type],
- request_data, request_data_dma);
- request_data = NULL;
- }
- }
-
- if (request_data == NULL) {
- ioc->diag_buffer_sz[buffer_type] = 0;
- ioc->diag_buffer_dma[buffer_type] = 0;
- request_data = pci_alloc_consistent(
- ioc->pdev, request_data_sz, &request_data_dma);
- if (request_data == NULL) {
- printk(MPT2SAS_ERR_FMT "%s: failed allocating memory"
- " for diag buffers, requested size(%d)\n",
- ioc->name, __func__, request_data_sz);
- mpt2sas_base_free_smid(ioc, smid);
- return -ENOMEM;
- }
- ioc->diag_buffer[buffer_type] = request_data;
- ioc->diag_buffer_sz[buffer_type] = request_data_sz;
- ioc->diag_buffer_dma[buffer_type] = request_data_dma;
- }
-
- mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
- mpi_request->BufferType = diag_register->buffer_type;
- mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags);
- mpi_request->BufferAddress = cpu_to_le64(request_data_dma);
- mpi_request->BufferLength = cpu_to_le32(request_data_sz);
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(0x%p), "
- "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data,
- (unsigned long long)request_data_dma,
- le32_to_cpu(mpi_request->BufferLength)));
-
- for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++)
- mpi_request->ProductSpecific[i] =
- cpu_to_le32(ioc->product_specific[buffer_type][i]);
-
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->ctl_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
- MPT2_IOCTL_DEFAULT_TIMEOUT*HZ);
-
- if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
- __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2DiagBufferPostRequest_t)/4);
- if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
-
- /* process the completed Reply Message Frame */
- if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: no reply message\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
-
- mpi_reply = ioc->ctl_cmds.reply;
- ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
-
- if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
- ioc->diag_buffer_status[buffer_type] |=
- MPT2_DIAG_BUFFER_IS_REGISTERED;
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n",
- ioc->name, __func__));
- } else {
- printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) "
- "log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
- rc = -EFAULT;
- }
-
- issue_host_reset:
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
-
- out:
-
- if (rc && request_data)
- pci_free_consistent(ioc->pdev, request_data_sz,
- request_data, request_data_dma);
-
- ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
- return rc;
-}
-
-/**
- * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time
- * @ioc: per adapter object
- * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1
- *
- * This is called when command line option diag_buffer_enable is enabled
- * at driver load time.
- */
-void
-mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register)
-{
- struct mpt2_diag_register diag_register;
-
- memset(&diag_register, 0, sizeof(struct mpt2_diag_register));
-
- if (bits_to_register & 1) {
- printk(MPT2SAS_INFO_FMT "registering trace buffer support\n",
- ioc->name);
- diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
- /* register for 1MB buffers */
- diag_register.requested_buffer_size = (1024 * 1024);
- diag_register.unique_id = 0x7075900;
- _ctl_diag_register_2(ioc, &diag_register);
- }
-
- if (bits_to_register & 2) {
- printk(MPT2SAS_INFO_FMT "registering snapshot buffer support\n",
- ioc->name);
- diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT;
- /* register for 2MB buffers */
- diag_register.requested_buffer_size = 2 * (1024 * 1024);
- diag_register.unique_id = 0x7075901;
- _ctl_diag_register_2(ioc, &diag_register);
- }
-
- if (bits_to_register & 4) {
- printk(MPT2SAS_INFO_FMT "registering extended buffer support\n",
- ioc->name);
- diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED;
- /* register for 2MB buffers */
- diag_register.requested_buffer_size = 2 * (1024 * 1024);
- diag_register.unique_id = 0x7075901;
- _ctl_diag_register_2(ioc, &diag_register);
- }
-}
-
-/**
- * _ctl_diag_register - application register with driver
- * @arg - user space buffer containing ioctl content
- * @state - NON_BLOCKING or BLOCKING
- *
- * This will allow the driver to setup any required buffers that will be
- * needed by firmware to communicate with the driver.
- */
-static long
-_ctl_diag_register(void __user *arg, enum block_state state)
-{
- struct mpt2_diag_register karg;
- struct MPT2SAS_ADAPTER *ioc;
- long rc;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
- return -EAGAIN;
- else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
- return -ERESTARTSYS;
- rc = _ctl_diag_register_2(ioc, &karg);
- mutex_unlock(&ioc->ctl_cmds.mutex);
- return rc;
-}
-
-/**
- * _ctl_diag_unregister - application unregister with driver
- * @arg - user space buffer containing ioctl content
- *
- * This will allow the driver to cleanup any memory allocated for diag
- * messages and to free up any resources.
- */
-static long
-_ctl_diag_unregister(void __user *arg)
-{
- struct mpt2_diag_unregister karg;
- struct MPT2SAS_ADAPTER *ioc;
- void *request_data;
- dma_addr_t request_data_dma;
- u32 request_data_sz;
- u8 buffer_type;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- buffer_type = karg.unique_id & 0x000000ff;
- if (!_ctl_diag_capability(ioc, buffer_type)) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -EPERM;
- }
-
- if ((ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
- "registered\n", ioc->name, __func__, buffer_type);
- return -EINVAL;
- }
- if ((ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_RELEASED) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) has not been "
- "released\n", ioc->name, __func__, buffer_type);
- return -EINVAL;
- }
-
- if (karg.unique_id != ioc->unique_id[buffer_type]) {
- printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
- "registered\n", ioc->name, __func__, karg.unique_id);
- return -EINVAL;
- }
-
- request_data = ioc->diag_buffer[buffer_type];
- if (!request_data) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -ENOMEM;
- }
-
- request_data_sz = ioc->diag_buffer_sz[buffer_type];
- request_data_dma = ioc->diag_buffer_dma[buffer_type];
- pci_free_consistent(ioc->pdev, request_data_sz,
- request_data, request_data_dma);
- ioc->diag_buffer[buffer_type] = NULL;
- ioc->diag_buffer_status[buffer_type] = 0;
- return 0;
-}
-
-/**
- * _ctl_diag_query - query relevant info associated with diag buffers
- * @arg - user space buffer containing ioctl content
- *
- * The application will send only buffer_type and unique_id. Driver will
- * inspect unique_id first, if valid, fill in all the info. If unique_id is
- * 0x00, the driver will return info specified by Buffer Type.
- */
-static long
-_ctl_diag_query(void __user *arg)
-{
- struct mpt2_diag_query karg;
- struct MPT2SAS_ADAPTER *ioc;
- void *request_data;
- int i;
- u8 buffer_type;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- karg.application_flags = 0;
- buffer_type = karg.buffer_type;
-
- if (!_ctl_diag_capability(ioc, buffer_type)) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -EPERM;
- }
-
- if ((ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
- "registered\n", ioc->name, __func__, buffer_type);
- return -EINVAL;
- }
-
- if (karg.unique_id & 0xffffff00) {
- if (karg.unique_id != ioc->unique_id[buffer_type]) {
- printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
- "registered\n", ioc->name, __func__,
- karg.unique_id);
- return -EINVAL;
- }
- }
-
- request_data = ioc->diag_buffer[buffer_type];
- if (!request_data) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -ENOMEM;
- }
-
- if (ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED)
- karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED |
- MPT2_APP_FLAGS_BUFFER_VALID);
- else
- karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED |
- MPT2_APP_FLAGS_BUFFER_VALID |
- MPT2_APP_FLAGS_FW_BUFFER_ACCESS);
-
- for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++)
- karg.product_specific[i] =
- ioc->product_specific[buffer_type][i];
-
- karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type];
- karg.driver_added_buffer_size = 0;
- karg.unique_id = ioc->unique_id[buffer_type];
- karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type];
-
- if (copy_to_user(arg, &karg, sizeof(struct mpt2_diag_query))) {
- printk(MPT2SAS_ERR_FMT "%s: unable to write mpt2_diag_query "
- "data @ %p\n", ioc->name, __func__, arg);
- return -EFAULT;
- }
- return 0;
-}
-
-/**
- * _ctl_send_release - Diag Release Message
- * @ioc: per adapter object
- * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
- * @issue_reset - specifies whether host reset is required.
- *
- */
-static int
-_ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset)
-{
- Mpi2DiagReleaseRequest_t *mpi_request;
- Mpi2DiagReleaseReply_t *mpi_reply;
- u16 smid;
- u16 ioc_status;
- u32 ioc_state;
- int rc;
- unsigned long timeleft;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- rc = 0;
- *issue_reset = 0;
-
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "skipping due to FAULT state\n", ioc->name,
- __func__));
- rc = -EAGAIN;
- goto out;
- }
-
- if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- ioc->ctl_cmds.status = MPT2_CMD_PENDING;
- memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->ctl_cmds.smid = smid;
-
- mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE;
- mpi_request->BufferType = buffer_type;
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
-
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->ctl_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
- MPT2_IOCTL_DEFAULT_TIMEOUT*HZ);
-
- if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
- __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2DiagReleaseRequest_t)/4);
- if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
- *issue_reset = 1;
- rc = -EFAULT;
- goto out;
- }
-
- /* process the completed Reply Message Frame */
- if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: no reply message\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
-
- mpi_reply = ioc->ctl_cmds.reply;
- ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
-
- if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
- ioc->diag_buffer_status[buffer_type] |=
- MPT2_DIAG_BUFFER_IS_RELEASED;
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n",
- ioc->name, __func__));
- } else {
- printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) "
- "log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
- rc = -EFAULT;
- }
-
- out:
- ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
- return rc;
-}
-
-/**
- * _ctl_diag_release - request to send Diag Release Message to firmware
- * @arg - user space buffer containing ioctl content
- * @state - NON_BLOCKING or BLOCKING
- *
- * This allows ownership of the specified buffer to returned to the driver,
- * allowing an application to read the buffer without fear that firmware is
- * overwritting information in the buffer.
- */
-static long
-_ctl_diag_release(void __user *arg, enum block_state state)
-{
- struct mpt2_diag_release karg;
- struct MPT2SAS_ADAPTER *ioc;
- void *request_data;
- int rc;
- u8 buffer_type;
- u8 issue_reset = 0;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- buffer_type = karg.unique_id & 0x000000ff;
- if (!_ctl_diag_capability(ioc, buffer_type)) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -EPERM;
- }
-
- if ((ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not "
- "registered\n", ioc->name, __func__, buffer_type);
- return -EINVAL;
- }
-
- if (karg.unique_id != ioc->unique_id[buffer_type]) {
- printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
- "registered\n", ioc->name, __func__, karg.unique_id);
- return -EINVAL;
- }
-
- if (ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_RELEASED) {
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) "
- "is already released\n", ioc->name, __func__,
- buffer_type);
- return 0;
- }
-
- request_data = ioc->diag_buffer[buffer_type];
-
- if (!request_data) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -ENOMEM;
- }
-
- /* buffers were released by due to host reset */
- if ((ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_DIAG_RESET)) {
- ioc->diag_buffer_status[buffer_type] |=
- MPT2_DIAG_BUFFER_IS_RELEASED;
- ioc->diag_buffer_status[buffer_type] &=
- ~MPT2_DIAG_BUFFER_IS_DIAG_RESET;
- printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) "
- "was released due to host reset\n", ioc->name, __func__,
- buffer_type);
- return 0;
- }
-
- if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
- return -EAGAIN;
- else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
- return -ERESTARTSYS;
-
- rc = _ctl_send_release(ioc, buffer_type, &issue_reset);
-
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
-
- mutex_unlock(&ioc->ctl_cmds.mutex);
- return rc;
-}
-
-/**
- * _ctl_diag_read_buffer - request for copy of the diag buffer
- * @arg - user space buffer containing ioctl content
- * @state - NON_BLOCKING or BLOCKING
- */
-static long
-_ctl_diag_read_buffer(void __user *arg, enum block_state state)
-{
- struct mpt2_diag_read_buffer karg;
- struct mpt2_diag_read_buffer __user *uarg = arg;
- struct MPT2SAS_ADAPTER *ioc;
- void *request_data, *diag_data;
- Mpi2DiagBufferPostRequest_t *mpi_request;
- Mpi2DiagBufferPostReply_t *mpi_reply;
- int rc, i;
- u8 buffer_type;
- unsigned long timeleft, request_size, copy_size;
- u16 smid;
- u16 ioc_status;
- u8 issue_reset = 0;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
- __func__));
-
- buffer_type = karg.unique_id & 0x000000ff;
- if (!_ctl_diag_capability(ioc, buffer_type)) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -EPERM;
- }
-
- if (karg.unique_id != ioc->unique_id[buffer_type]) {
- printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not "
- "registered\n", ioc->name, __func__, karg.unique_id);
- return -EINVAL;
- }
-
- request_data = ioc->diag_buffer[buffer_type];
- if (!request_data) {
- printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
- return -ENOMEM;
- }
-
- request_size = ioc->diag_buffer_sz[buffer_type];
-
- if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) {
- printk(MPT2SAS_ERR_FMT "%s: either the starting_offset "
- "or bytes_to_read are not 4 byte aligned\n", ioc->name,
- __func__);
- return -EINVAL;
- }
-
- if (karg.starting_offset > request_size)
- return -EINVAL;
-
- diag_data = (void *)(request_data + karg.starting_offset);
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), "
- "offset(%d), sz(%d)\n", ioc->name, __func__,
- diag_data, karg.starting_offset, karg.bytes_to_read));
-
- /* Truncate data on requests that are too large */
- if ((diag_data + karg.bytes_to_read < diag_data) ||
- (diag_data + karg.bytes_to_read > request_data + request_size))
- copy_size = request_size - karg.starting_offset;
- else
- copy_size = karg.bytes_to_read;
-
- if (copy_to_user((void __user *)uarg->diagnostic_data,
- diag_data, copy_size)) {
- printk(MPT2SAS_ERR_FMT "%s: Unable to write "
- "mpt_diag_read_buffer_t data @ %p\n", ioc->name,
- __func__, diag_data);
- return -EFAULT;
- }
-
- if ((karg.flags & MPT2_FLAGS_REREGISTER) == 0)
- return 0;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: Reregister "
- "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type));
- if ((ioc->diag_buffer_status[buffer_type] &
- MPT2_DIAG_BUFFER_IS_RELEASED) == 0) {
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "buffer_type(0x%02x) is still registered\n", ioc->name,
- __func__, buffer_type));
- return 0;
- }
- /* Get a free request frame and save the message context.
- */
- if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
- return -EAGAIN;
- else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
- return -ERESTARTSYS;
-
- if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- rc = 0;
- ioc->ctl_cmds.status = MPT2_CMD_PENDING;
- memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->ctl_cmds.smid = smid;
-
- mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
- mpi_request->BufferType = buffer_type;
- mpi_request->BufferLength =
- cpu_to_le32(ioc->diag_buffer_sz[buffer_type]);
- mpi_request->BufferAddress =
- cpu_to_le64(ioc->diag_buffer_dma[buffer_type]);
- for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++)
- mpi_request->ProductSpecific[i] =
- cpu_to_le32(ioc->product_specific[buffer_type][i]);
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
-
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->ctl_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
- MPT2_IOCTL_DEFAULT_TIMEOUT*HZ);
-
- if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name,
- __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2DiagBufferPostRequest_t)/4);
- if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
-
- /* process the completed Reply Message Frame */
- if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: no reply message\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
-
- mpi_reply = ioc->ctl_cmds.reply;
- ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
-
- if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
- ioc->diag_buffer_status[buffer_type] |=
- MPT2_DIAG_BUFFER_IS_REGISTERED;
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n",
- ioc->name, __func__));
- } else {
- printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) "
- "log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
- rc = -EFAULT;
- }
-
- issue_host_reset:
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
-
- out:
-
- ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
- mutex_unlock(&ioc->ctl_cmds.mutex);
- return rc;
-}
-
-/**
- * _ctl_ioctl_main - main ioctl entry point
- * @file - (struct file)
- * @cmd - ioctl opcode
- * @arg -
- */
-static long
-_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg)
-{
- enum block_state state;
- long ret = -EINVAL;
-
- state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING :
- BLOCKING;
-
- switch (cmd) {
- case MPT2IOCINFO:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_iocinfo))
- ret = _ctl_getiocinfo(arg);
- break;
- case MPT2COMMAND:
- {
- struct mpt2_ioctl_command karg;
- struct mpt2_ioctl_command __user *uarg;
- struct MPT2SAS_ADAPTER *ioc;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
-
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 ||
- !ioc)
- return -ENODEV;
-
- if (ioc->shost_recovery || ioc->pci_error_recovery)
- return -EAGAIN;
-
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) {
- uarg = arg;
- ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf, state);
- }
- break;
- }
- case MPT2EVENTQUERY:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventquery))
- ret = _ctl_eventquery(arg);
- break;
- case MPT2EVENTENABLE:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventenable))
- ret = _ctl_eventenable(arg);
- break;
- case MPT2EVENTREPORT:
- ret = _ctl_eventreport(arg);
- break;
- case MPT2HARDRESET:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_diag_reset))
- ret = _ctl_do_reset(arg);
- break;
- case MPT2BTDHMAPPING:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_btdh_mapping))
- ret = _ctl_btdh_mapping(arg);
- break;
- case MPT2DIAGREGISTER:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_register))
- ret = _ctl_diag_register(arg, state);
- break;
- case MPT2DIAGUNREGISTER:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_unregister))
- ret = _ctl_diag_unregister(arg);
- break;
- case MPT2DIAGQUERY:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_query))
- ret = _ctl_diag_query(arg);
- break;
- case MPT2DIAGRELEASE:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_release))
- ret = _ctl_diag_release(arg, state);
- break;
- case MPT2DIAGREADBUFFER:
- if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_read_buffer))
- ret = _ctl_diag_read_buffer(arg, state);
- break;
- default:
- {
- struct mpt2_ioctl_command karg;
- struct MPT2SAS_ADAPTER *ioc;
-
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
-
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 ||
- !ioc)
- return -ENODEV;
-
- dctlprintk(ioc, printk(MPT2SAS_INFO_FMT
- "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd));
- break;
- }
- }
- return ret;
-}
-
-/**
- * _ctl_ioctl - main ioctl entry point (unlocked)
- * @file - (struct file)
- * @cmd - ioctl opcode
- * @arg -
- */
-static long
-_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- long ret;
-
- mutex_lock(&_ctl_mutex);
- ret = _ctl_ioctl_main(file, cmd, (void __user *)arg);
- mutex_unlock(&_ctl_mutex);
- return ret;
-}
-
-#ifdef CONFIG_COMPAT
-/**
- * _ctl_compat_mpt_command - convert 32bit pointers to 64bit.
- * @file - (struct file)
- * @cmd - ioctl opcode
- * @arg - (struct mpt2_ioctl_command32)
- *
- * MPT2COMMAND32 - Handle 32bit applications running on 64bit os.
- */
-static long
-_ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg)
-{
- struct mpt2_ioctl_command32 karg32;
- struct mpt2_ioctl_command32 __user *uarg;
- struct mpt2_ioctl_command karg;
- struct MPT2SAS_ADAPTER *ioc;
- enum block_state state;
-
- if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32))
- return -EINVAL;
-
- uarg = (struct mpt2_ioctl_command32 __user *) arg;
-
- if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg32.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
- if (ioc->shost_recovery || ioc->pci_error_recovery)
- return -EAGAIN;
-
- memset(&karg, 0, sizeof(struct mpt2_ioctl_command));
- karg.hdr.ioc_number = karg32.hdr.ioc_number;
- karg.hdr.port_number = karg32.hdr.port_number;
- karg.hdr.max_data_size = karg32.hdr.max_data_size;
- karg.timeout = karg32.timeout;
- karg.max_reply_bytes = karg32.max_reply_bytes;
- karg.data_in_size = karg32.data_in_size;
- karg.data_out_size = karg32.data_out_size;
- karg.max_sense_bytes = karg32.max_sense_bytes;
- karg.data_sge_offset = karg32.data_sge_offset;
- karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr);
- karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr);
- karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr);
- karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr);
- state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING;
- return _ctl_do_mpt_command(ioc, karg, &uarg->mf, state);
-}
-
-/**
- * _ctl_ioctl_compat - main ioctl entry point (compat)
- * @file -
- * @cmd -
- * @arg -
- *
- * This routine handles 32 bit applications in 64bit os.
- */
-static long
-_ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg)
-{
- long ret;
-
- mutex_lock(&_ctl_mutex);
- if (cmd == MPT2COMMAND32)
- ret = _ctl_compat_mpt_command(file, cmd, arg);
- else
- ret = _ctl_ioctl_main(file, cmd, (void __user *)arg);
- mutex_unlock(&_ctl_mutex);
- return ret;
-}
-#endif
-
-/* scsi host attributes */
-
-/**
- * _ctl_version_fw_show - firmware version
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
- (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
- (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
- (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
- ioc->facts.FWVersion.Word & 0x000000FF);
-}
-static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL);
-
-/**
- * _ctl_version_bios_show - bios version
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
-
- return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
- (version & 0xFF000000) >> 24,
- (version & 0x00FF0000) >> 16,
- (version & 0x0000FF00) >> 8,
- version & 0x000000FF);
-}
-static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL);
-
-/**
- * _ctl_version_mpi_show - MPI (message passing interface) version
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%03x.%02x\n",
- ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8);
-}
-static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL);
-
-/**
- * _ctl_version_product_show - product name
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_version_product_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName);
-}
-static DEVICE_ATTR(version_product, S_IRUGO,
- _ctl_version_product_show, NULL);
-
-/**
- * _ctl_version_nvdata_persistent_show - ndvata persistent version
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_version_nvdata_persistent_show(struct device *cdev,
- struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%08xh\n",
- le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word));
-}
-static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO,
- _ctl_version_nvdata_persistent_show, NULL);
-
-/**
- * _ctl_version_nvdata_default_show - nvdata default version
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_version_nvdata_default_show(struct device *cdev,
- struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%08xh\n",
- le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word));
-}
-static DEVICE_ATTR(version_nvdata_default, S_IRUGO,
- _ctl_version_nvdata_default_show, NULL);
-
-/**
- * _ctl_board_name_show - board name
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_board_name_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName);
-}
-static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL);
-
-/**
- * _ctl_board_assembly_show - board assembly name
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly);
-}
-static DEVICE_ATTR(board_assembly, S_IRUGO,
- _ctl_board_assembly_show, NULL);
-
-/**
- * _ctl_board_tracer_show - board tracer number
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber);
-}
-static DEVICE_ATTR(board_tracer, S_IRUGO,
- _ctl_board_tracer_show, NULL);
-
-/**
- * _ctl_io_delay_show - io missing delay
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * This is for firmware implemention for deboucing device
- * removal events.
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay);
-}
-static DEVICE_ATTR(io_delay, S_IRUGO,
- _ctl_io_delay_show, NULL);
-
-/**
- * _ctl_device_delay_show - device missing delay
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * This is for firmware implemention for deboucing device
- * removal events.
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay);
-}
-static DEVICE_ATTR(device_delay, S_IRUGO,
- _ctl_device_delay_show, NULL);
-
-/**
- * _ctl_fw_queue_depth_show - global credits
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * This is firmware queue depth limit
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit);
-}
-static DEVICE_ATTR(fw_queue_depth, S_IRUGO,
- _ctl_fw_queue_depth_show, NULL);
-
-/**
- * _ctl_sas_address_show - sas address
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * This is the controller sas address
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
- (unsigned long long)ioc->sas_hba.sas_address);
-}
-static DEVICE_ATTR(host_sas_address, S_IRUGO,
- _ctl_host_sas_address_show, NULL);
-
-/**
- * _ctl_logging_level_show - logging level
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read/write' shost attribute.
- */
-static ssize_t
-_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level);
-}
-static ssize_t
-_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- int val = 0;
-
- if (sscanf(buf, "%x", &val) != 1)
- return -EINVAL;
-
- ioc->logging_level = val;
- printk(MPT2SAS_INFO_FMT "logging_level=%08xh\n", ioc->name,
- ioc->logging_level);
- return strlen(buf);
-}
-static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
- _ctl_logging_level_show, _ctl_logging_level_store);
-
-/* device attributes */
-/*
- * _ctl_fwfault_debug_show - show/store fwfault_debug
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * mpt2sas_fwfault_debug is command line option
- * A sysfs 'read/write' shost attribute.
- */
-static ssize_t
-_ctl_fwfault_debug_show(struct device *cdev,
- struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug);
-}
-static ssize_t
-_ctl_fwfault_debug_store(struct device *cdev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- int val = 0;
-
- if (sscanf(buf, "%d", &val) != 1)
- return -EINVAL;
-
- ioc->fwfault_debug = val;
- printk(MPT2SAS_INFO_FMT "fwfault_debug=%d\n", ioc->name,
- ioc->fwfault_debug);
- return strlen(buf);
-}
-static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR,
- _ctl_fwfault_debug_show, _ctl_fwfault_debug_store);
-
-
-/**
- * _ctl_ioc_reset_count_show - ioc reset count
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * This is firmware queue depth limit
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- return snprintf(buf, PAGE_SIZE, "%08d\n", ioc->ioc_reset_count);
-}
-static DEVICE_ATTR(ioc_reset_count, S_IRUGO,
- _ctl_ioc_reset_count_show, NULL);
-
-struct DIAG_BUFFER_START {
- u32 Size;
- u32 DiagVersion;
- u8 BufferType;
- u8 Reserved[3];
- u32 Reserved1;
- u32 Reserved2;
- u32 Reserved3;
-};
-/**
- * _ctl_host_trace_buffer_size_show - host buffer size (trace only)
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_host_trace_buffer_size_show(struct device *cdev,
- struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- u32 size = 0;
- struct DIAG_BUFFER_START *request_data;
-
- if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) {
- printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not "
- "registered\n", ioc->name, __func__);
- return 0;
- }
-
- if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not "
- "registered\n", ioc->name, __func__);
- return 0;
- }
-
- request_data = (struct DIAG_BUFFER_START *)
- ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE];
- if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 ||
- le32_to_cpu(request_data->DiagVersion) == 0x01000000) &&
- le32_to_cpu(request_data->Reserved3) == 0x4742444c)
- size = le32_to_cpu(request_data->Size);
-
- ioc->ring_buffer_sz = size;
- return snprintf(buf, PAGE_SIZE, "%d\n", size);
-}
-static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO,
- _ctl_host_trace_buffer_size_show, NULL);
-
-/**
- * _ctl_host_trace_buffer_show - firmware ring buffer (trace only)
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read/write' shost attribute.
- *
- * You will only be able to read 4k bytes of ring buffer at a time.
- * In order to read beyond 4k bytes, you will have to write out the
- * offset to the same attribute, it will move the pointer.
- */
-static ssize_t
-_ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- void *request_data;
- u32 size;
-
- if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) {
- printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not "
- "registered\n", ioc->name, __func__);
- return 0;
- }
-
- if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) {
- printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not "
- "registered\n", ioc->name, __func__);
- return 0;
- }
-
- if (ioc->ring_buffer_offset > ioc->ring_buffer_sz)
- return 0;
-
- size = ioc->ring_buffer_sz - ioc->ring_buffer_offset;
- size = (size > PAGE_SIZE) ? PAGE_SIZE : size;
- request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset;
- memcpy(buf, request_data, size);
- return size;
-}
-
-static ssize_t
-_ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- int val = 0;
-
- if (sscanf(buf, "%d", &val) != 1)
- return -EINVAL;
-
- ioc->ring_buffer_offset = val;
- return strlen(buf);
-}
-static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR,
- _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store);
-
-/*****************************************/
-
-/**
- * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only)
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * A sysfs 'read/write' shost attribute.
- *
- * This is a mechnism to post/release host_trace_buffers
- */
-static ssize_t
-_ctl_host_trace_buffer_enable_show(struct device *cdev,
- struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) ||
- ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0))
- return snprintf(buf, PAGE_SIZE, "off\n");
- else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_RELEASED))
- return snprintf(buf, PAGE_SIZE, "release\n");
- else
- return snprintf(buf, PAGE_SIZE, "post\n");
-}
-
-static ssize_t
-_ctl_host_trace_buffer_enable_store(struct device *cdev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct Scsi_Host *shost = class_to_shost(cdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- char str[10] = "";
- struct mpt2_diag_register diag_register;
- u8 issue_reset = 0;
-
- if (sscanf(buf, "%s", str) != 1)
- return -EINVAL;
-
- if (!strcmp(str, "post")) {
- /* exit out if host buffers are already posted */
- if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) &&
- (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) &&
- ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_RELEASED) == 0))
- goto out;
- memset(&diag_register, 0, sizeof(struct mpt2_diag_register));
- printk(MPT2SAS_INFO_FMT "posting host trace buffers\n",
- ioc->name);
- diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
- diag_register.requested_buffer_size = (1024 * 1024);
- diag_register.unique_id = 0x7075900;
- ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0;
- _ctl_diag_register_2(ioc, &diag_register);
- } else if (!strcmp(str, "release")) {
- /* exit out if host buffers are already released */
- if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE])
- goto out;
- if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_REGISTERED) == 0)
- goto out;
- if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
- MPT2_DIAG_BUFFER_IS_RELEASED))
- goto out;
- printk(MPT2SAS_INFO_FMT "releasing host trace buffer\n",
- ioc->name);
- _ctl_send_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, &issue_reset);
- }
-
- out:
- return strlen(buf);
-}
-static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR,
- _ctl_host_trace_buffer_enable_show, _ctl_host_trace_buffer_enable_store);
-
-struct device_attribute *mpt2sas_host_attrs[] = {
- &dev_attr_version_fw,
- &dev_attr_version_bios,
- &dev_attr_version_mpi,
- &dev_attr_version_product,
- &dev_attr_version_nvdata_persistent,
- &dev_attr_version_nvdata_default,
- &dev_attr_board_name,
- &dev_attr_board_assembly,
- &dev_attr_board_tracer,
- &dev_attr_io_delay,
- &dev_attr_device_delay,
- &dev_attr_logging_level,
- &dev_attr_fwfault_debug,
- &dev_attr_fw_queue_depth,
- &dev_attr_host_sas_address,
- &dev_attr_ioc_reset_count,
- &dev_attr_host_trace_buffer_size,
- &dev_attr_host_trace_buffer,
- &dev_attr_host_trace_buffer_enable,
- NULL,
-};
-
-/**
- * _ctl_device_sas_address_show - sas address
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * This is the sas address for the target
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
- struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
-
- return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
- (unsigned long long)sas_device_priv_data->sas_target->sas_address);
-}
-static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL);
-
-/**
- * _ctl_device_handle_show - device handle
- * @cdev - pointer to embedded class device
- * @buf - the buffer returned
- *
- * This is the firmware assigned device handle
- *
- * A sysfs 'read-only' shost attribute.
- */
-static ssize_t
-_ctl_device_handle_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
- struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
-
- return snprintf(buf, PAGE_SIZE, "0x%04x\n",
- sas_device_priv_data->sas_target->handle);
-}
-static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL);
-
-struct device_attribute *mpt2sas_dev_attrs[] = {
- &dev_attr_sas_address,
- &dev_attr_sas_device_handle,
- NULL,
-};
-
-static const struct file_operations ctl_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = _ctl_ioctl,
- .release = _ctl_release,
- .poll = _ctl_poll,
- .fasync = _ctl_fasync,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = _ctl_ioctl_compat,
-#endif
- .llseek = noop_llseek,
-};
-
-static struct miscdevice ctl_dev = {
- .minor = MPT2SAS_MINOR,
- .name = MPT2SAS_DEV_NAME,
- .fops = &ctl_fops,
-};
-
-/**
- * mpt2sas_ctl_init - main entry point for ctl.
- *
- */
-void
-mpt2sas_ctl_init(void)
-{
- async_queue = NULL;
- if (misc_register(&ctl_dev) < 0)
- printk(KERN_ERR "%s can't register misc device [minor=%d]\n",
- MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR);
-
- init_waitqueue_head(&ctl_poll_wait);
-}
-
-/**
- * mpt2sas_ctl_exit - exit point for ctl
- *
- */
-void
-mpt2sas_ctl_exit(void)
-{
- struct MPT2SAS_ADAPTER *ioc;
- int i;
-
- list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
-
- /* free memory associated to diag buffers */
- for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
- if (!ioc->diag_buffer[i])
- continue;
- pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i],
- ioc->diag_buffer[i], ioc->diag_buffer_dma[i]);
- ioc->diag_buffer[i] = NULL;
- ioc->diag_buffer_status[i] = 0;
- }
-
- kfree(ioc->event_log);
- }
- misc_deregister(&ctl_dev);
-}
-
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
deleted file mode 100644
index a7dbc6825f5f..000000000000
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ /dev/null
@@ -1,7807 +0,0 @@
-/*
- * Scsi Host Layer for MPT (Message Passing Technology) based controllers
- *
- * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * NO WARRANTY
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- * solely responsible for determining the appropriateness of using and
- * distributing the Program and assumes all risks associated with its
- * exercise of rights under this Agreement, including but not limited to
- * the risks and costs of program errors, damage to or loss of data,
- * programs or equipment, and unavailability or interruption of operations.
-
- * DISCLAIMER OF LIABILITY
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/blkdev.h>
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/aer.h>
-#include <linux/raid_class.h>
-#include <linux/slab.h>
-
-#include "mpt2sas_base.h"
-
-MODULE_AUTHOR(MPT2SAS_AUTHOR);
-MODULE_DESCRIPTION(MPT2SAS_DESCRIPTION);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(MPT2SAS_DRIVER_VERSION);
-
-#define RAID_CHANNEL 1
-
-/* forward proto's */
-static void _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_node *sas_expander);
-static void _firmware_event_work(struct work_struct *work);
-
-static u8 _scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid);
-
-/* global parameters */
-LIST_HEAD(mpt2sas_ioc_list);
-
-/* local parameters */
-static u8 scsi_io_cb_idx = -1;
-static u8 tm_cb_idx = -1;
-static u8 ctl_cb_idx = -1;
-static u8 base_cb_idx = -1;
-static u8 transport_cb_idx = -1;
-static u8 scsih_cb_idx = -1;
-static u8 config_cb_idx = -1;
-static int mpt_ids;
-
-static u8 tm_tr_cb_idx = -1 ;
-static u8 tm_tr_volume_cb_idx = -1 ;
-static u8 tm_sas_control_cb_idx = -1;
-
-/* command line options */
-static u32 logging_level;
-MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info "
- "(default=0)");
-
-/* scsi-mid layer global parmeter is max_report_luns, which is 511 */
-#define MPT2SAS_MAX_LUN (16895)
-static int max_lun = MPT2SAS_MAX_LUN;
-module_param(max_lun, int, 0);
-MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
-
-/**
- * struct sense_info - common structure for obtaining sense keys
- * @skey: sense key
- * @asc: additional sense code
- * @ascq: additional sense code qualifier
- */
-struct sense_info {
- u8 skey;
- u8 asc;
- u8 ascq;
-};
-
-
-#define MPT2SAS_TURN_ON_FAULT_LED (0xFFFC)
-#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF)
-
-/**
- * struct fw_event_work - firmware event struct
- * @list: link list framework
- * @work: work object (ioc->fault_reset_work_q)
- * @cancel_pending_work: flag set during reset handling
- * @ioc: per adapter object
- * @device_handle: device handle
- * @VF_ID: virtual function id
- * @VP_ID: virtual port id
- * @ignore: flag meaning this event has been marked to ignore
- * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h
- * @event_data: reply event data payload follows
- *
- * This object stored on ioc->fw_event_list.
- */
-struct fw_event_work {
- struct list_head list;
- u8 cancel_pending_work;
- struct delayed_work delayed_work;
- struct MPT2SAS_ADAPTER *ioc;
- u16 device_handle;
- u8 VF_ID;
- u8 VP_ID;
- u8 ignore;
- u16 event;
- void *event_data;
-};
-
-/* raid transport support */
-static struct raid_template *mpt2sas_raid_template;
-
-/**
- * struct _scsi_io_transfer - scsi io transfer
- * @handle: sas device handle (assigned by firmware)
- * @is_raid: flag set for hidden raid components
- * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE,
- * @data_length: data transfer length
- * @data_dma: dma pointer to data
- * @sense: sense data
- * @lun: lun number
- * @cdb_length: cdb length
- * @cdb: cdb contents
- * @timeout: timeout for this command
- * @VF_ID: virtual function id
- * @VP_ID: virtual port id
- * @valid_reply: flag set for reply message
- * @sense_length: sense length
- * @ioc_status: ioc status
- * @scsi_state: scsi state
- * @scsi_status: scsi staus
- * @log_info: log information
- * @transfer_length: data length transfer when there is a reply message
- *
- * Used for sending internal scsi commands to devices within this module.
- * Refer to _scsi_send_scsi_io().
- */
-struct _scsi_io_transfer {
- u16 handle;
- u8 is_raid;
- enum dma_data_direction dir;
- u32 data_length;
- dma_addr_t data_dma;
- u8 sense[SCSI_SENSE_BUFFERSIZE];
- u32 lun;
- u8 cdb_length;
- u8 cdb[32];
- u8 timeout;
- u8 VF_ID;
- u8 VP_ID;
- u8 valid_reply;
- /* the following bits are only valid when 'valid_reply = 1' */
- u32 sense_length;
- u16 ioc_status;
- u8 scsi_state;
- u8 scsi_status;
- u32 log_info;
- u32 transfer_length;
-};
-
-/*
- * The pci device ids are defined in mpi/mpi2_cnfg.h.
- */
-static struct pci_device_id scsih_pci_table[] = {
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004,
- PCI_ANY_ID, PCI_ANY_ID },
- /* Falcon ~ 2008*/
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008,
- PCI_ANY_ID, PCI_ANY_ID },
- /* Liberator ~ 2108 */
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
- PCI_ANY_ID, PCI_ANY_ID },
- /* Meteor ~ 2116 */
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
- PCI_ANY_ID, PCI_ANY_ID },
- /* Thunderbolt ~ 2208 */
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
- PCI_ANY_ID, PCI_ANY_ID },
- /* Mustang ~ 2308 */
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
- PCI_ANY_ID, PCI_ANY_ID },
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
- PCI_ANY_ID, PCI_ANY_ID },
- /* SSS6200 */
- { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
- PCI_ANY_ID, PCI_ANY_ID },
- {0} /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(pci, scsih_pci_table);
-
-/**
- * _scsih_set_debug_level - global setting of ioc->logging_level.
- *
- * Note: The logging levels are defined in mpt2sas_debug.h.
- */
-static int
-_scsih_set_debug_level(const char *val, struct kernel_param *kp)
-{
- int ret = param_set_int(val, kp);
- struct MPT2SAS_ADAPTER *ioc;
-
- if (ret)
- return ret;
-
- printk(KERN_INFO "setting logging_level(0x%08x)\n", logging_level);
- list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
- ioc->logging_level = logging_level;
- return 0;
-}
-module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
- &logging_level, 0644);
-
-/**
- * _scsih_srch_boot_sas_address - search based on sas_address
- * @sas_address: sas address
- * @boot_device: boot device object from bios page 2
- *
- * Returns 1 when there's a match, 0 means no match.
- */
-static inline int
-_scsih_srch_boot_sas_address(u64 sas_address,
- Mpi2BootDeviceSasWwid_t *boot_device)
-{
- return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0;
-}
-
-/**
- * _scsih_srch_boot_device_name - search based on device name
- * @device_name: device name specified in INDENTIFY fram
- * @boot_device: boot device object from bios page 2
- *
- * Returns 1 when there's a match, 0 means no match.
- */
-static inline int
-_scsih_srch_boot_device_name(u64 device_name,
- Mpi2BootDeviceDeviceName_t *boot_device)
-{
- return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0;
-}
-
-/**
- * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot
- * @enclosure_logical_id: enclosure logical id
- * @slot_number: slot number
- * @boot_device: boot device object from bios page 2
- *
- * Returns 1 when there's a match, 0 means no match.
- */
-static inline int
-_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number,
- Mpi2BootDeviceEnclosureSlot_t *boot_device)
-{
- return (enclosure_logical_id == le64_to_cpu(boot_device->
- EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device->
- SlotNumber)) ? 1 : 0;
-}
-
-/**
- * _scsih_is_boot_device - search for matching boot device.
- * @sas_address: sas address
- * @device_name: device name specified in INDENTIFY fram
- * @enclosure_logical_id: enclosure logical id
- * @slot_number: slot number
- * @form: specifies boot device form
- * @boot_device: boot device object from bios page 2
- *
- * Returns 1 when there's a match, 0 means no match.
- */
-static int
-_scsih_is_boot_device(u64 sas_address, u64 device_name,
- u64 enclosure_logical_id, u16 slot, u8 form,
- Mpi2BiosPage2BootDevice_t *boot_device)
-{
- int rc = 0;
-
- switch (form) {
- case MPI2_BIOSPAGE2_FORM_SAS_WWID:
- if (!sas_address)
- break;
- rc = _scsih_srch_boot_sas_address(
- sas_address, &boot_device->SasWwid);
- break;
- case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT:
- if (!enclosure_logical_id)
- break;
- rc = _scsih_srch_boot_encl_slot(
- enclosure_logical_id,
- slot, &boot_device->EnclosureSlot);
- break;
- case MPI2_BIOSPAGE2_FORM_DEVICE_NAME:
- if (!device_name)
- break;
- rc = _scsih_srch_boot_device_name(
- device_name, &boot_device->DeviceName);
- break;
- case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED:
- break;
- }
-
- return rc;
-}
-
-/**
- * _scsih_get_sas_address - set the sas_address for given device handle
- * @handle: device handle
- * @sas_address: sas address
- *
- * Returns 0 success, non-zero when failure
- */
-static int
-_scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- u64 *sas_address)
-{
- Mpi2SasDevicePage0_t sas_device_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u32 ioc_status;
-
- if (handle <= ioc->sas_hba.num_phys) {
- *sas_address = ioc->sas_hba.sas_address;
- return 0;
- } else
- *sas_address = 0;
-
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -ENXIO;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
- "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
- __FILE__, __LINE__, __func__);
- return -EIO;
- }
-
- *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
- return 0;
-}
-
-/**
- * _scsih_determine_boot_device - determine boot device.
- * @ioc: per adapter object
- * @device: either sas_device or raid_device object
- * @is_raid: [flag] 1 = raid object, 0 = sas object
- *
- * Determines whether this device should be first reported device to
- * to scsi-ml or sas transport, this purpose is for persistent boot device.
- * There are primary, alternate, and current entries in bios page 2. The order
- * priority is primary, alternate, then current. This routine saves
- * the corresponding device object and is_raid flag in the ioc object.
- * The saved data to be used later in _scsih_probe_boot_devices().
- */
-static void
-_scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc,
- void *device, u8 is_raid)
-{
- struct _sas_device *sas_device;
- struct _raid_device *raid_device;
- u64 sas_address;
- u64 device_name;
- u64 enclosure_logical_id;
- u16 slot;
-
- /* only process this function when driver loads */
- if (!ioc->wait_for_port_enable_to_complete)
- return;
-
- if (!is_raid) {
- sas_device = device;
- sas_address = sas_device->sas_address;
- device_name = sas_device->device_name;
- enclosure_logical_id = sas_device->enclosure_logical_id;
- slot = sas_device->slot;
- } else {
- raid_device = device;
- sas_address = raid_device->wwid;
- device_name = 0;
- enclosure_logical_id = 0;
- slot = 0;
- }
-
- if (!ioc->req_boot_device.device) {
- if (_scsih_is_boot_device(sas_address, device_name,
- enclosure_logical_id, slot,
- (ioc->bios_pg2.ReqBootDeviceForm &
- MPI2_BIOSPAGE2_FORM_MASK),
- &ioc->bios_pg2.RequestedBootDevice)) {
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT
- "%s: req_boot_device(0x%016llx)\n",
- ioc->name, __func__,
- (unsigned long long)sas_address));
- ioc->req_boot_device.device = device;
- ioc->req_boot_device.is_raid = is_raid;
- }
- }
-
- if (!ioc->req_alt_boot_device.device) {
- if (_scsih_is_boot_device(sas_address, device_name,
- enclosure_logical_id, slot,
- (ioc->bios_pg2.ReqAltBootDeviceForm &
- MPI2_BIOSPAGE2_FORM_MASK),
- &ioc->bios_pg2.RequestedAltBootDevice)) {
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT
- "%s: req_alt_boot_device(0x%016llx)\n",
- ioc->name, __func__,
- (unsigned long long)sas_address));
- ioc->req_alt_boot_device.device = device;
- ioc->req_alt_boot_device.is_raid = is_raid;
- }
- }
-
- if (!ioc->current_boot_device.device) {
- if (_scsih_is_boot_device(sas_address, device_name,
- enclosure_logical_id, slot,
- (ioc->bios_pg2.CurrentBootDeviceForm &
- MPI2_BIOSPAGE2_FORM_MASK),
- &ioc->bios_pg2.CurrentBootDevice)) {
- dinitprintk(ioc, printk(MPT2SAS_INFO_FMT
- "%s: current_boot_device(0x%016llx)\n",
- ioc->name, __func__,
- (unsigned long long)sas_address));
- ioc->current_boot_device.device = device;
- ioc->current_boot_device.is_raid = is_raid;
- }
- }
-}
-
-/**
- * mpt2sas_scsih_sas_device_find_by_sas_address - sas device search
- * @ioc: per adapter object
- * @sas_address: sas address
- * Context: Calling function should acquire ioc->sas_device_lock
- *
- * This searches for sas_device based on sas_address, then return sas_device
- * object.
- */
-struct _sas_device *
-mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
- u64 sas_address)
-{
- struct _sas_device *sas_device;
-
- list_for_each_entry(sas_device, &ioc->sas_device_list, list)
- if (sas_device->sas_address == sas_address)
- return sas_device;
-
- list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
- if (sas_device->sas_address == sas_address)
- return sas_device;
-
- return NULL;
-}
-
-/**
- * _scsih_sas_device_find_by_handle - sas device search
- * @ioc: per adapter object
- * @handle: sas device handle (assigned by firmware)
- * Context: Calling function should acquire ioc->sas_device_lock
- *
- * This searches for sas_device based on sas_address, then return sas_device
- * object.
- */
-static struct _sas_device *
-_scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _sas_device *sas_device;
-
- list_for_each_entry(sas_device, &ioc->sas_device_list, list)
- if (sas_device->handle == handle)
- return sas_device;
-
- list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
- if (sas_device->handle == handle)
- return sas_device;
-
- return NULL;
-}
-
-/**
- * _scsih_sas_device_remove - remove sas_device from list.
- * @ioc: per adapter object
- * @sas_device: the sas_device object
- * Context: This function will acquire ioc->sas_device_lock.
- *
- * Removing object and freeing associated memory from the ioc->sas_device_list.
- */
-static void
-_scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_device *sas_device)
-{
- unsigned long flags;
-
- if (!sas_device)
- return;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- if (mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- sas_device->sas_address)) {
- list_del(&sas_device->list);
- kfree(sas_device);
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-}
-
-/**
- * _scsih_sas_device_add - insert sas_device to the list.
- * @ioc: per adapter object
- * @sas_device: the sas_device object
- * Context: This function will acquire ioc->sas_device_lock.
- *
- * Adding new object to the ioc->sas_device_list.
- */
-static void
-_scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_device *sas_device)
-{
- unsigned long flags;
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle"
- "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
- sas_device->handle, (unsigned long long)sas_device->sas_address));
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- list_add_tail(&sas_device->list, &ioc->sas_device_list);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
- sas_device->sas_address_parent))
- _scsih_sas_device_remove(ioc, sas_device);
-}
-
-/**
- * _scsih_sas_device_init_add - insert sas_device to the list.
- * @ioc: per adapter object
- * @sas_device: the sas_device object
- * Context: This function will acquire ioc->sas_device_lock.
- *
- * Adding new object at driver load time to the ioc->sas_device_init_list.
- */
-static void
-_scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_device *sas_device)
-{
- unsigned long flags;
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle"
- "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
- sas_device->handle, (unsigned long long)sas_device->sas_address));
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- _scsih_determine_boot_device(ioc, sas_device, 0);
-}
-
-/**
- * _scsih_raid_device_find_by_id - raid device search
- * @ioc: per adapter object
- * @id: sas device target id
- * @channel: sas device channel
- * Context: Calling function should acquire ioc->raid_device_lock
- *
- * This searches for raid_device based on target id, then return raid_device
- * object.
- */
-static struct _raid_device *
-_scsih_raid_device_find_by_id(struct MPT2SAS_ADAPTER *ioc, int id, int channel)
-{
- struct _raid_device *raid_device, *r;
-
- r = NULL;
- list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
- if (raid_device->id == id && raid_device->channel == channel) {
- r = raid_device;
- goto out;
- }
- }
-
- out:
- return r;
-}
-
-/**
- * _scsih_raid_device_find_by_handle - raid device search
- * @ioc: per adapter object
- * @handle: sas device handle (assigned by firmware)
- * Context: Calling function should acquire ioc->raid_device_lock
- *
- * This searches for raid_device based on handle, then return raid_device
- * object.
- */
-static struct _raid_device *
-_scsih_raid_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _raid_device *raid_device, *r;
-
- r = NULL;
- list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
- if (raid_device->handle != handle)
- continue;
- r = raid_device;
- goto out;
- }
-
- out:
- return r;
-}
-
-/**
- * _scsih_raid_device_find_by_wwid - raid device search
- * @ioc: per adapter object
- * @handle: sas device handle (assigned by firmware)
- * Context: Calling function should acquire ioc->raid_device_lock
- *
- * This searches for raid_device based on wwid, then return raid_device
- * object.
- */
-static struct _raid_device *
-_scsih_raid_device_find_by_wwid(struct MPT2SAS_ADAPTER *ioc, u64 wwid)
-{
- struct _raid_device *raid_device, *r;
-
- r = NULL;
- list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
- if (raid_device->wwid != wwid)
- continue;
- r = raid_device;
- goto out;
- }
-
- out:
- return r;
-}
-
-/**
- * _scsih_raid_device_add - add raid_device object
- * @ioc: per adapter object
- * @raid_device: raid_device object
- *
- * This is added to the raid_device_list link list.
- */
-static void
-_scsih_raid_device_add(struct MPT2SAS_ADAPTER *ioc,
- struct _raid_device *raid_device)
-{
- unsigned long flags;
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle"
- "(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__,
- raid_device->handle, (unsigned long long)raid_device->wwid));
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- list_add_tail(&raid_device->list, &ioc->raid_device_list);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-}
-
-/**
- * _scsih_raid_device_remove - delete raid_device object
- * @ioc: per adapter object
- * @raid_device: raid_device object
- *
- * This is removed from the raid_device_list link list.
- */
-static void
-_scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc,
- struct _raid_device *raid_device)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- list_del(&raid_device->list);
- memset(raid_device, 0, sizeof(struct _raid_device));
- kfree(raid_device);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-}
-
-/**
- * mpt2sas_scsih_expander_find_by_handle - expander device search
- * @ioc: per adapter object
- * @handle: expander handle (assigned by firmware)
- * Context: Calling function should acquire ioc->sas_device_lock
- *
- * This searches for expander device based on handle, then returns the
- * sas_node object.
- */
-struct _sas_node *
-mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _sas_node *sas_expander, *r;
-
- r = NULL;
- list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
- if (sas_expander->handle != handle)
- continue;
- r = sas_expander;
- goto out;
- }
- out:
- return r;
-}
-
-/**
- * mpt2sas_scsih_expander_find_by_sas_address - expander device search
- * @ioc: per adapter object
- * @sas_address: sas address
- * Context: Calling function should acquire ioc->sas_node_lock.
- *
- * This searches for expander device based on sas_address, then returns the
- * sas_node object.
- */
-struct _sas_node *
-mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
- u64 sas_address)
-{
- struct _sas_node *sas_expander, *r;
-
- r = NULL;
- list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
- if (sas_expander->sas_address != sas_address)
- continue;
- r = sas_expander;
- goto out;
- }
- out:
- return r;
-}
-
-/**
- * _scsih_expander_node_add - insert expander device to the list.
- * @ioc: per adapter object
- * @sas_expander: the sas_device object
- * Context: This function will acquire ioc->sas_node_lock.
- *
- * Adding new object to the ioc->sas_expander_list.
- *
- * Return nothing.
- */
-static void
-_scsih_expander_node_add(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_node *sas_expander)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- list_add_tail(&sas_expander->list, &ioc->sas_expander_list);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-}
-
-/**
- * _scsih_is_end_device - determines if device is an end device
- * @device_info: bitfield providing information about the device.
- * Context: none
- *
- * Returns 1 if end device.
- */
-static int
-_scsih_is_end_device(u32 device_info)
-{
- if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE &&
- ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) |
- (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) |
- (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))
- return 1;
- else
- return 0;
-}
-
-/**
- * _scsih_scsi_lookup_get - returns scmd entry
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Returns the smid stored scmd pointer.
- */
-static struct scsi_cmnd *
-_scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- return ioc->scsi_lookup[smid - 1].scmd;
-}
-
-/**
- * _scsih_scsi_lookup_get_clear - returns scmd entry
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Returns the smid stored scmd pointer.
- * Then will derefrence the stored scmd pointer.
- */
-static inline struct scsi_cmnd *
-_scsih_scsi_lookup_get_clear(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- unsigned long flags;
- struct scsi_cmnd *scmd;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- scmd = ioc->scsi_lookup[smid - 1].scmd;
- ioc->scsi_lookup[smid - 1].scmd = NULL;
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-
- return scmd;
-}
-
-/**
- * _scsih_scsi_lookup_find_by_scmd - scmd lookup
- * @ioc: per adapter object
- * @smid: system request message index
- * @scmd: pointer to scsi command object
- * Context: This function will acquire ioc->scsi_lookup_lock.
- *
- * This will search for a scmd pointer in the scsi_lookup array,
- * returning the revelent smid. A returned value of zero means invalid.
- */
-static u16
-_scsih_scsi_lookup_find_by_scmd(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd
- *scmd)
-{
- u16 smid;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- smid = 0;
- for (i = 0; i < ioc->scsiio_depth; i++) {
- if (ioc->scsi_lookup[i].scmd == scmd) {
- smid = ioc->scsi_lookup[i].smid;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return smid;
-}
-
-/**
- * _scsih_scsi_lookup_find_by_target - search for matching channel:id
- * @ioc: per adapter object
- * @id: target id
- * @channel: channel
- * Context: This function will acquire ioc->scsi_lookup_lock.
- *
- * This will search for a matching channel:id in the scsi_lookup array,
- * returning 1 if found.
- */
-static u8
-_scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id,
- int channel)
-{
- u8 found;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- found = 0;
- for (i = 0 ; i < ioc->scsiio_depth; i++) {
- if (ioc->scsi_lookup[i].scmd &&
- (ioc->scsi_lookup[i].scmd->device->id == id &&
- ioc->scsi_lookup[i].scmd->device->channel == channel)) {
- found = 1;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return found;
-}
-
-/**
- * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun
- * @ioc: per adapter object
- * @id: target id
- * @lun: lun number
- * @channel: channel
- * Context: This function will acquire ioc->scsi_lookup_lock.
- *
- * This will search for a matching channel:id:lun in the scsi_lookup array,
- * returning 1 if found.
- */
-static u8
-_scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id,
- unsigned int lun, int channel)
-{
- u8 found;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- found = 0;
- for (i = 0 ; i < ioc->scsiio_depth; i++) {
- if (ioc->scsi_lookup[i].scmd &&
- (ioc->scsi_lookup[i].scmd->device->id == id &&
- ioc->scsi_lookup[i].scmd->device->channel == channel &&
- ioc->scsi_lookup[i].scmd->device->lun == lun)) {
- found = 1;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return found;
-}
-
-/**
- * _scsih_get_chain_buffer_tracker - obtain chain tracker
- * @ioc: per adapter object
- * @smid: smid associated to an IO request
- *
- * Returns chain tracker(from ioc->free_chain_list)
- */
-static struct chain_tracker *
-_scsih_get_chain_buffer_tracker(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- struct chain_tracker *chain_req;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- if (list_empty(&ioc->free_chain_list)) {
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- printk(MPT2SAS_WARN_FMT "chain buffers not available\n",
- ioc->name);
- return NULL;
- }
- chain_req = list_entry(ioc->free_chain_list.next,
- struct chain_tracker, tracker_list);
- list_del_init(&chain_req->tracker_list);
- list_add_tail(&chain_req->tracker_list,
- &ioc->scsi_lookup[smid - 1].chain_list);
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- return chain_req;
-}
-
-/**
- * _scsih_build_scatter_gather - main sg creation routine
- * @ioc: per adapter object
- * @scmd: scsi command
- * @smid: system request message index
- * Context: none.
- *
- * The main routine that builds scatter gather table from a given
- * scsi request sent via the .queuecommand main handler.
- *
- * Returns 0 success, anything else error
- */
-static int
-_scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
- struct scsi_cmnd *scmd, u16 smid)
-{
- Mpi2SCSIIORequest_t *mpi_request;
- dma_addr_t chain_dma;
- struct scatterlist *sg_scmd;
- void *sg_local, *chain;
- u32 chain_offset;
- u32 chain_length;
- u32 chain_flags;
- int sges_left;
- u32 sges_in_segment;
- u32 sgl_flags;
- u32 sgl_flags_last_element;
- u32 sgl_flags_end_buffer;
- struct chain_tracker *chain_req;
-
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
-
- /* init scatter gather flags */
- sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT;
- if (scmd->sc_data_direction == DMA_TO_DEVICE)
- sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC;
- sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT)
- << MPI2_SGE_FLAGS_SHIFT;
- sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST)
- << MPI2_SGE_FLAGS_SHIFT;
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
-
- sg_scmd = scsi_sglist(scmd);
- sges_left = scsi_dma_map(scmd);
- if (sges_left < 0) {
- sdev_printk(KERN_ERR, scmd->device, "pci_map_sg"
- " failed: request for %d bytes!\n", scsi_bufflen(scmd));
- return -ENOMEM;
- }
-
- sg_local = &mpi_request->SGL;
- sges_in_segment = ioc->max_sges_in_main_message;
- if (sges_left <= sges_in_segment)
- goto fill_in_last_segment;
-
- mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) +
- (sges_in_segment * ioc->sge_size))/4;
-
- /* fill in main message segment when there is a chain following */
- while (sges_in_segment) {
- if (sges_in_segment == 1)
- ioc->base_add_sg_single(sg_local,
- sgl_flags_last_element | sg_dma_len(sg_scmd),
- sg_dma_address(sg_scmd));
- else
- ioc->base_add_sg_single(sg_local, sgl_flags |
- sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
- sg_scmd = sg_next(sg_scmd);
- sg_local += ioc->sge_size;
- sges_left--;
- sges_in_segment--;
- }
-
- /* initializing the chain flags and pointers */
- chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT;
- chain_req = _scsih_get_chain_buffer_tracker(ioc, smid);
- if (!chain_req)
- return -1;
- chain = chain_req->chain_buffer;
- chain_dma = chain_req->chain_buffer_dma;
- do {
- sges_in_segment = (sges_left <=
- ioc->max_sges_in_chain_message) ? sges_left :
- ioc->max_sges_in_chain_message;
- chain_offset = (sges_left == sges_in_segment) ?
- 0 : (sges_in_segment * ioc->sge_size)/4;
- chain_length = sges_in_segment * ioc->sge_size;
- if (chain_offset) {
- chain_offset = chain_offset <<
- MPI2_SGE_CHAIN_OFFSET_SHIFT;
- chain_length += ioc->sge_size;
- }
- ioc->base_add_sg_single(sg_local, chain_flags | chain_offset |
- chain_length, chain_dma);
- sg_local = chain;
- if (!chain_offset)
- goto fill_in_last_segment;
-
- /* fill in chain segments */
- while (sges_in_segment) {
- if (sges_in_segment == 1)
- ioc->base_add_sg_single(sg_local,
- sgl_flags_last_element |
- sg_dma_len(sg_scmd),
- sg_dma_address(sg_scmd));
- else
- ioc->base_add_sg_single(sg_local, sgl_flags |
- sg_dma_len(sg_scmd),
- sg_dma_address(sg_scmd));
- sg_scmd = sg_next(sg_scmd);
- sg_local += ioc->sge_size;
- sges_left--;
- sges_in_segment--;
- }
-
- chain_req = _scsih_get_chain_buffer_tracker(ioc, smid);
- if (!chain_req)
- return -1;
- chain = chain_req->chain_buffer;
- chain_dma = chain_req->chain_buffer_dma;
- } while (1);
-
-
- fill_in_last_segment:
-
- /* fill the last segment */
- while (sges_left) {
- if (sges_left == 1)
- ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer |
- sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
- else
- ioc->base_add_sg_single(sg_local, sgl_flags |
- sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
- sg_scmd = sg_next(sg_scmd);
- sg_local += ioc->sge_size;
- sges_left--;
- }
-
- return 0;
-}
-
-/**
- * _scsih_adjust_queue_depth - setting device queue depth
- * @sdev: scsi device struct
- * @qdepth: requested queue depth
- *
- *
- * Returns nothing
- */
-static void
-_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth)
-{
- struct Scsi_Host *shost = sdev->host;
- int max_depth;
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct _sas_device *sas_device;
- unsigned long flags;
-
- max_depth = shost->can_queue;
-
- /* limit max device queue for SATA to 32 */
- sas_device_priv_data = sdev->hostdata;
- if (!sas_device_priv_data)
- goto not_sata;
- sas_target_priv_data = sas_device_priv_data->sas_target;
- if (!sas_target_priv_data)
- goto not_sata;
- if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
- goto not_sata;
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- sas_device_priv_data->sas_target->sas_address);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device && sas_device->device_info &
- MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
- max_depth = MPT2SAS_SATA_QUEUE_DEPTH;
-
- not_sata:
-
- if (!sdev->tagged_supported)
- max_depth = 1;
- if (qdepth > max_depth)
- qdepth = max_depth;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
-}
-
-/**
- * _scsih_change_queue_depth - setting device queue depth
- * @sdev: scsi device struct
- * @qdepth: requested queue depth
- * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP
- * (see include/scsi/scsi_host.h for definition)
- *
- * Returns queue depth.
- */
-static int
-_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP)
- _scsih_adjust_queue_depth(sdev, qdepth);
- else if (reason == SCSI_QDEPTH_QFULL)
- scsi_track_queue_full(sdev, qdepth);
- else
- return -EOPNOTSUPP;
-
- if (sdev->inquiry_len > 7)
- sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), "
- "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n",
- sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags,
- sdev->ordered_tags, sdev->scsi_level,
- (sdev->inquiry[7] & 2) >> 1);
-
- return sdev->queue_depth;
-}
-
-/**
- * _scsih_change_queue_type - changing device queue tag type
- * @sdev: scsi device struct
- * @tag_type: requested tag type
- *
- * Returns queue tag type.
- */
-static int
-_scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
-}
-
-/**
- * _scsih_target_alloc - target add routine
- * @starget: scsi target struct
- *
- * Returns 0 if ok. Any other return is assumed to be an error and
- * the device is ignored.
- */
-static int
-_scsih_target_alloc(struct scsi_target *starget)
-{
- struct Scsi_Host *shost = dev_to_shost(&starget->dev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct _sas_device *sas_device;
- struct _raid_device *raid_device;
- unsigned long flags;
- struct sas_rphy *rphy;
-
- sas_target_priv_data = kzalloc(sizeof(struct scsi_target), GFP_KERNEL);
- if (!sas_target_priv_data)
- return -ENOMEM;
-
- starget->hostdata = sas_target_priv_data;
- sas_target_priv_data->starget = starget;
- sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE;
-
- /* RAID volumes */
- if (starget->channel == RAID_CHANNEL) {
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
- starget->channel);
- if (raid_device) {
- sas_target_priv_data->handle = raid_device->handle;
- sas_target_priv_data->sas_address = raid_device->wwid;
- sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME;
- sas_target_priv_data->raid_device = raid_device;
- raid_device->starget = starget;
- }
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- return 0;
- }
-
- /* sas/sata devices */
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- rphy = dev_to_rphy(starget->dev.parent);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- rphy->identify.sas_address);
-
- if (sas_device) {
- sas_target_priv_data->handle = sas_device->handle;
- sas_target_priv_data->sas_address = sas_device->sas_address;
- sas_device->starget = starget;
- sas_device->id = starget->id;
- sas_device->channel = starget->channel;
- if (test_bit(sas_device->handle, ioc->pd_handles))
- sas_target_priv_data->flags |=
- MPT_TARGET_FLAGS_RAID_COMPONENT;
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- return 0;
-}
-
-/**
- * _scsih_target_destroy - target destroy routine
- * @starget: scsi target struct
- *
- * Returns nothing.
- */
-static void
-_scsih_target_destroy(struct scsi_target *starget)
-{
- struct Scsi_Host *shost = dev_to_shost(&starget->dev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct _sas_device *sas_device;
- struct _raid_device *raid_device;
- unsigned long flags;
- struct sas_rphy *rphy;
-
- sas_target_priv_data = starget->hostdata;
- if (!sas_target_priv_data)
- return;
-
- if (starget->channel == RAID_CHANNEL) {
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
- starget->channel);
- if (raid_device) {
- raid_device->starget = NULL;
- raid_device->sdev = NULL;
- }
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- goto out;
- }
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- rphy = dev_to_rphy(starget->dev.parent);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- rphy->identify.sas_address);
- if (sas_device && (sas_device->starget == starget) &&
- (sas_device->id == starget->id) &&
- (sas_device->channel == starget->channel))
- sas_device->starget = NULL;
-
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- out:
- kfree(sas_target_priv_data);
- starget->hostdata = NULL;
-}
-
-/**
- * _scsih_slave_alloc - device add routine
- * @sdev: scsi device struct
- *
- * Returns 0 if ok. Any other return is assumed to be an error and
- * the device is ignored.
- */
-static int
-_scsih_slave_alloc(struct scsi_device *sdev)
-{
- struct Scsi_Host *shost;
- struct MPT2SAS_ADAPTER *ioc;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct scsi_target *starget;
- struct _raid_device *raid_device;
- unsigned long flags;
-
- sas_device_priv_data = kzalloc(sizeof(struct scsi_device), GFP_KERNEL);
- if (!sas_device_priv_data)
- return -ENOMEM;
-
- sas_device_priv_data->lun = sdev->lun;
- sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT;
-
- starget = scsi_target(sdev);
- sas_target_priv_data = starget->hostdata;
- sas_target_priv_data->num_luns++;
- sas_device_priv_data->sas_target = sas_target_priv_data;
- sdev->hostdata = sas_device_priv_data;
- if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT))
- sdev->no_uld_attach = 1;
-
- shost = dev_to_shost(&starget->dev);
- ioc = shost_priv(shost);
- if (starget->channel == RAID_CHANNEL) {
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_id(ioc,
- starget->id, starget->channel);
- if (raid_device)
- raid_device->sdev = sdev; /* raid is single lun */
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- }
-
- return 0;
-}
-
-/**
- * _scsih_slave_destroy - device destroy routine
- * @sdev: scsi device struct
- *
- * Returns nothing.
- */
-static void
-_scsih_slave_destroy(struct scsi_device *sdev)
-{
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct scsi_target *starget;
-
- if (!sdev->hostdata)
- return;
-
- starget = scsi_target(sdev);
- sas_target_priv_data = starget->hostdata;
- sas_target_priv_data->num_luns--;
- kfree(sdev->hostdata);
- sdev->hostdata = NULL;
-}
-
-/**
- * _scsih_display_sata_capabilities - sata capabilities
- * @ioc: per adapter object
- * @sas_device: the sas_device object
- * @sdev: scsi device struct
- */
-static void
-_scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_device *sas_device, struct scsi_device *sdev)
-{
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasDevicePage0_t sas_device_pg0;
- u32 ioc_status;
- u16 flags;
- u32 device_info;
-
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, sas_device->handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- flags = le16_to_cpu(sas_device_pg0.Flags);
- device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
-
- sdev_printk(KERN_INFO, sdev,
- "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), "
- "sw_preserve(%s)\n",
- (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n",
- (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n",
- (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" :
- "n",
- (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n",
- (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n",
- (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n");
-}
-
-/**
- * _scsih_is_raid - return boolean indicating device is raid volume
- * @dev the device struct object
- */
-static int
-_scsih_is_raid(struct device *dev)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host);
-
- if (ioc->is_warpdrive)
- return 0;
- return (sdev->channel == RAID_CHANNEL) ? 1 : 0;
-}
-
-/**
- * _scsih_get_resync - get raid volume resync percent complete
- * @dev the device struct object
- */
-static void
-_scsih_get_resync(struct device *dev)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host);
- static struct _raid_device *raid_device;
- unsigned long flags;
- Mpi2RaidVolPage0_t vol_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u32 volume_status_flags;
- u8 percent_complete = 0;
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
- sdev->channel);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-
- if (!raid_device || ioc->is_warpdrive)
- goto out;
-
- if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
- MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle,
- sizeof(Mpi2RaidVolPage0_t))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
-
- volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags);
- if (volume_status_flags & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS)
- percent_complete = raid_device->percent_complete;
- out:
- raid_set_resync(mpt2sas_raid_template, dev, percent_complete);
-}
-
-/**
- * _scsih_get_state - get raid volume level
- * @dev the device struct object
- */
-static void
-_scsih_get_state(struct device *dev)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host);
- static struct _raid_device *raid_device;
- unsigned long flags;
- Mpi2RaidVolPage0_t vol_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u32 volstate;
- enum raid_state state = RAID_STATE_UNKNOWN;
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
- sdev->channel);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-
- if (!raid_device)
- goto out;
-
- if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
- MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle,
- sizeof(Mpi2RaidVolPage0_t))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
-
- volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags);
- if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) {
- state = RAID_STATE_RESYNCING;
- goto out;
- }
-
- switch (vol_pg0.VolumeState) {
- case MPI2_RAID_VOL_STATE_OPTIMAL:
- case MPI2_RAID_VOL_STATE_ONLINE:
- state = RAID_STATE_ACTIVE;
- break;
- case MPI2_RAID_VOL_STATE_DEGRADED:
- state = RAID_STATE_DEGRADED;
- break;
- case MPI2_RAID_VOL_STATE_FAILED:
- case MPI2_RAID_VOL_STATE_MISSING:
- state = RAID_STATE_OFFLINE;
- break;
- }
- out:
- raid_set_state(mpt2sas_raid_template, dev, state);
-}
-
-/**
- * _scsih_set_level - set raid level
- * @sdev: scsi device struct
- * @raid_device: raid_device object
- */
-static void
-_scsih_set_level(struct scsi_device *sdev, struct _raid_device *raid_device)
-{
- enum raid_level level = RAID_LEVEL_UNKNOWN;
-
- switch (raid_device->volume_type) {
- case MPI2_RAID_VOL_TYPE_RAID0:
- level = RAID_LEVEL_0;
- break;
- case MPI2_RAID_VOL_TYPE_RAID10:
- level = RAID_LEVEL_10;
- break;
- case MPI2_RAID_VOL_TYPE_RAID1E:
- level = RAID_LEVEL_1E;
- break;
- case MPI2_RAID_VOL_TYPE_RAID1:
- level = RAID_LEVEL_1;
- break;
- }
-
- raid_set_level(mpt2sas_raid_template, &sdev->sdev_gendev, level);
-}
-
-/**
- * _scsih_get_volume_capabilities - volume capabilities
- * @ioc: per adapter object
- * @sas_device: the raid_device object
- */
-static void
-_scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
- struct _raid_device *raid_device)
-{
- Mpi2RaidVolPage0_t *vol_pg0;
- Mpi2RaidPhysDiskPage0_t pd_pg0;
- Mpi2SasDevicePage0_t sas_device_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u16 sz;
- u8 num_pds;
-
- if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle,
- &num_pds)) || !num_pds) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- raid_device->num_pds = num_pds;
- sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds *
- sizeof(Mpi2RaidVol0PhysDisk_t));
- vol_pg0 = kzalloc(sz, GFP_KERNEL);
- if (!vol_pg0) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
- MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- kfree(vol_pg0);
- return;
- }
-
- raid_device->volume_type = vol_pg0->VolumeType;
-
- /* figure out what the underlying devices are by
- * obtaining the device_info bits for the 1st device
- */
- if (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
- &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
- vol_pg0->PhysDisk[0].PhysDiskNum))) {
- if (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
- &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
- le16_to_cpu(pd_pg0.DevHandle)))) {
- raid_device->device_info =
- le32_to_cpu(sas_device_pg0.DeviceInfo);
- }
- }
-
- kfree(vol_pg0);
-}
-/**
- * _scsih_disable_ddio - Disable direct I/O for all the volumes
- * @ioc: per adapter object
- */
-static void
-_scsih_disable_ddio(struct MPT2SAS_ADAPTER *ioc)
-{
- Mpi2RaidVolPage1_t vol_pg1;
- Mpi2ConfigReply_t mpi_reply;
- struct _raid_device *raid_device;
- u16 handle;
- u16 ioc_status;
-
- handle = 0xFFFF;
- while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
- &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
- handle = le16_to_cpu(vol_pg1.DevHandle);
- raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
- if (raid_device)
- raid_device->direct_io_enabled = 0;
- }
- return;
-}
-
-
-/**
- * _scsih_get_num_volumes - Get number of volumes in the ioc
- * @ioc: per adapter object
- */
-static u8
-_scsih_get_num_volumes(struct MPT2SAS_ADAPTER *ioc)
-{
- Mpi2RaidVolPage1_t vol_pg1;
- Mpi2ConfigReply_t mpi_reply;
- u16 handle;
- u8 vol_cnt = 0;
- u16 ioc_status;
-
- handle = 0xFFFF;
- while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
- &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
- vol_cnt++;
- handle = le16_to_cpu(vol_pg1.DevHandle);
- }
- return vol_cnt;
-}
-
-
-/**
- * _scsih_init_warpdrive_properties - Set properties for warpdrive direct I/O.
- * @ioc: per adapter object
- * @raid_device: the raid_device object
- */
-static void
-_scsih_init_warpdrive_properties(struct MPT2SAS_ADAPTER *ioc,
- struct _raid_device *raid_device)
-{
- Mpi2RaidVolPage0_t *vol_pg0;
- Mpi2RaidPhysDiskPage0_t pd_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u16 sz;
- u8 num_pds, count;
- u64 mb = 1024 * 1024;
- u64 tb_2 = 2 * mb * mb;
- u64 capacity;
- u32 stripe_sz;
- u8 i, stripe_exp;
-
- if (!ioc->is_warpdrive)
- return;
-
- if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "globally as drives are exposed\n", ioc->name);
- return;
- }
- if (_scsih_get_num_volumes(ioc) > 1) {
- _scsih_disable_ddio(ioc);
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "globally as number of drives > 1\n", ioc->name);
- return;
- }
- if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle,
- &num_pds)) || !num_pds) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "Failure in computing number of drives\n", ioc->name);
- return;
- }
-
- sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds *
- sizeof(Mpi2RaidVol0PhysDisk_t));
- vol_pg0 = kzalloc(sz, GFP_KERNEL);
- if (!vol_pg0) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "Memory allocation failure for RVPG0\n", ioc->name);
- return;
- }
-
- if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
- MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "Failure in retrieving RVPG0\n", ioc->name);
- kfree(vol_pg0);
- return;
- }
-
- /*
- * WARPDRIVE:If number of physical disks in a volume exceeds the max pds
- * assumed for WARPDRIVE, disable direct I/O
- */
- if (num_pds > MPT_MAX_WARPDRIVE_PDS) {
- printk(MPT2SAS_WARN_FMT "WarpDrive : Direct IO is disabled "
- "for the drive with handle(0x%04x): num_mem=%d, "
- "max_mem_allowed=%d\n", ioc->name, raid_device->handle,
- num_pds, MPT_MAX_WARPDRIVE_PDS);
- kfree(vol_pg0);
- return;
- }
- for (count = 0; count < num_pds; count++) {
- if (mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
- &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
- vol_pg0->PhysDisk[count].PhysDiskNum) ||
- pd_pg0.DevHandle == MPT2SAS_INVALID_DEVICE_HANDLE) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is "
- "disabled for the drive with handle(0x%04x) member"
- "handle retrieval failed for member number=%d\n",
- ioc->name, raid_device->handle,
- vol_pg0->PhysDisk[count].PhysDiskNum);
- goto out_error;
- }
- raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle);
- }
-
- /*
- * Assumption for WD: Direct I/O is not supported if the volume is
- * not RAID0, if the stripe size is not 64KB, if the block size is
- * not 512 and if the volume size is >2TB
- */
- if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0 ||
- le16_to_cpu(vol_pg0->BlockSize) != 512) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "for the drive with handle(0x%04x): type=%d, "
- "s_sz=%uK, blk_size=%u\n", ioc->name,
- raid_device->handle, raid_device->volume_type,
- le32_to_cpu(vol_pg0->StripeSize)/2,
- le16_to_cpu(vol_pg0->BlockSize));
- goto out_error;
- }
-
- capacity = (u64) le16_to_cpu(vol_pg0->BlockSize) *
- (le64_to_cpu(vol_pg0->MaxLBA) + 1);
-
- if (capacity > tb_2) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "for the drive with handle(0x%04x) since drive sz > 2TB\n",
- ioc->name, raid_device->handle);
- goto out_error;
- }
-
- stripe_sz = le32_to_cpu(vol_pg0->StripeSize);
- stripe_exp = 0;
- for (i = 0; i < 32; i++) {
- if (stripe_sz & 1)
- break;
- stripe_exp++;
- stripe_sz >>= 1;
- }
- if (i == 32) {
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
- "for the drive with handle(0x%04x) invalid stripe sz %uK\n",
- ioc->name, raid_device->handle,
- le32_to_cpu(vol_pg0->StripeSize)/2);
- goto out_error;
- }
- raid_device->stripe_exponent = stripe_exp;
- raid_device->direct_io_enabled = 1;
-
- printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is Enabled for the drive"
- " with handle(0x%04x)\n", ioc->name, raid_device->handle);
- /*
- * WARPDRIVE: Though the following fields are not used for direct IO,
- * stored for future purpose:
- */
- raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA);
- raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize);
- raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize);
-
-
- kfree(vol_pg0);
- return;
-
-out_error:
- raid_device->direct_io_enabled = 0;
- for (count = 0; count < num_pds; count++)
- raid_device->pd_handle[count] = 0;
- kfree(vol_pg0);
- return;
-}
-
-/**
- * _scsih_enable_tlr - setting TLR flags
- * @ioc: per adapter object
- * @sdev: scsi device struct
- *
- * Enabling Transaction Layer Retries for tape devices when
- * vpd page 0x90 is present
- *
- */
-static void
-_scsih_enable_tlr(struct MPT2SAS_ADAPTER *ioc, struct scsi_device *sdev)
-{
- /* only for TAPE */
- if (sdev->type != TYPE_TAPE)
- return;
-
- if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR))
- return;
-
- sas_enable_tlr(sdev);
- sdev_printk(KERN_INFO, sdev, "TLR %s\n",
- sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled");
- return;
-
-}
-
-/**
- * _scsih_slave_configure - device configure routine.
- * @sdev: scsi device struct
- *
- * Returns 0 if ok. Any other return is assumed to be an error and
- * the device is ignored.
- */
-static int
-_scsih_slave_configure(struct scsi_device *sdev)
-{
- struct Scsi_Host *shost = sdev->host;
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct _sas_device *sas_device;
- struct _raid_device *raid_device;
- unsigned long flags;
- int qdepth;
- u8 ssp_target = 0;
- char *ds = "";
- char *r_level = "";
-
- qdepth = 1;
- sas_device_priv_data = sdev->hostdata;
- sas_device_priv_data->configured_lun = 1;
- sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT;
- sas_target_priv_data = sas_device_priv_data->sas_target;
-
- /* raid volume handling */
- if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) {
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_handle(ioc,
- sas_target_priv_data->handle);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- if (!raid_device) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return 0;
- }
-
- _scsih_get_volume_capabilities(ioc, raid_device);
-
- /*
- * WARPDRIVE: Initialize the required data for Direct IO
- */
- _scsih_init_warpdrive_properties(ioc, raid_device);
-
- /* RAID Queue Depth Support
- * IS volume = underlying qdepth of drive type, either
- * MPT2SAS_SAS_QUEUE_DEPTH or MPT2SAS_SATA_QUEUE_DEPTH
- * IM/IME/R10 = 128 (MPT2SAS_RAID_QUEUE_DEPTH)
- */
- if (raid_device->device_info &
- MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
- qdepth = MPT2SAS_SAS_QUEUE_DEPTH;
- ds = "SSP";
- } else {
- qdepth = MPT2SAS_SATA_QUEUE_DEPTH;
- if (raid_device->device_info &
- MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
- ds = "SATA";
- else
- ds = "STP";
- }
-
- switch (raid_device->volume_type) {
- case MPI2_RAID_VOL_TYPE_RAID0:
- r_level = "RAID0";
- break;
- case MPI2_RAID_VOL_TYPE_RAID1E:
- qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
- if (ioc->manu_pg10.OEMIdentifier &&
- (ioc->manu_pg10.GenericFlags0 &
- MFG10_GF0_R10_DISPLAY) &&
- !(raid_device->num_pds % 2))
- r_level = "RAID10";
- else
- r_level = "RAID1E";
- break;
- case MPI2_RAID_VOL_TYPE_RAID1:
- qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
- r_level = "RAID1";
- break;
- case MPI2_RAID_VOL_TYPE_RAID10:
- qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
- r_level = "RAID10";
- break;
- case MPI2_RAID_VOL_TYPE_UNKNOWN:
- default:
- qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
- r_level = "RAIDX";
- break;
- }
-
- if (!ioc->hide_ir_msg)
- sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), "
- "wwid(0x%016llx), pd_count(%d), type(%s)\n",
- r_level, raid_device->handle,
- (unsigned long long)raid_device->wwid,
- raid_device->num_pds, ds);
- _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
- /* raid transport support */
- if (!ioc->is_warpdrive)
- _scsih_set_level(sdev, raid_device);
- return 0;
- }
-
- /* non-raid handling */
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- sas_device_priv_data->sas_target->sas_address);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device) {
- if (sas_target_priv_data->flags &
- MPT_TARGET_FLAGS_RAID_COMPONENT) {
- mpt2sas_config_get_volume_handle(ioc,
- sas_device->handle, &sas_device->volume_handle);
- mpt2sas_config_get_volume_wwid(ioc,
- sas_device->volume_handle,
- &sas_device->volume_wwid);
- }
- if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
- qdepth = MPT2SAS_SAS_QUEUE_DEPTH;
- ssp_target = 1;
- ds = "SSP";
- } else {
- qdepth = MPT2SAS_SATA_QUEUE_DEPTH;
- if (sas_device->device_info &
- MPI2_SAS_DEVICE_INFO_STP_TARGET)
- ds = "STP";
- else if (sas_device->device_info &
- MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
- ds = "SATA";
- }
-
- sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), "
- "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n",
- ds, sas_device->handle,
- (unsigned long long)sas_device->sas_address,
- sas_device->phy,
- (unsigned long long)sas_device->device_name);
- sdev_printk(KERN_INFO, sdev, "%s: "
- "enclosure_logical_id(0x%016llx), slot(%d)\n", ds,
- (unsigned long long) sas_device->enclosure_logical_id,
- sas_device->slot);
-
- if (!ssp_target)
- _scsih_display_sata_capabilities(ioc, sas_device, sdev);
- }
-
- _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
-
- if (ssp_target) {
- sas_read_port_mode_page(sdev);
- _scsih_enable_tlr(ioc, sdev);
- }
- return 0;
-}
-
-/**
- * _scsih_bios_param - fetch head, sector, cylinder info for a disk
- * @sdev: scsi device struct
- * @bdev: pointer to block device context
- * @capacity: device size (in 512 byte sectors)
- * @params: three element array to place output:
- * params[0] number of heads (max 255)
- * params[1] number of sectors (max 63)
- * params[2] number of cylinders
- *
- * Return nothing.
- */
-static int
-_scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int params[])
-{
- int heads;
- int sectors;
- sector_t cylinders;
- ulong dummy;
-
- heads = 64;
- sectors = 32;
-
- dummy = heads * sectors;
- cylinders = capacity;
- sector_div(cylinders, dummy);
-
- /*
- * Handle extended translation size for logical drives
- * > 1Gb
- */
- if ((ulong)capacity >= 0x200000) {
- heads = 255;
- sectors = 63;
- dummy = heads * sectors;
- cylinders = capacity;
- sector_div(cylinders, dummy);
- }
-
- /* return result */
- params[0] = heads;
- params[1] = sectors;
- params[2] = cylinders;
-
- return 0;
-}
-
-/**
- * _scsih_response_code - translation of device response code
- * @ioc: per adapter object
- * @response_code: response code returned by the device
- *
- * Return nothing.
- */
-static void
-_scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code)
-{
- char *desc;
-
- switch (response_code) {
- case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE:
- desc = "task management request completed";
- break;
- case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME:
- desc = "invalid frame";
- break;
- case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
- desc = "task management request not supported";
- break;
- case MPI2_SCSITASKMGMT_RSP_TM_FAILED:
- desc = "task management request failed";
- break;
- case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED:
- desc = "task management request succeeded";
- break;
- case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN:
- desc = "invalid lun";
- break;
- case 0xA:
- desc = "overlapped tag attempted";
- break;
- case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
- desc = "task queued, however not sent to target";
- break;
- default:
- desc = "unknown";
- break;
- }
- printk(MPT2SAS_WARN_FMT "response_code(0x%01x): %s\n",
- ioc->name, response_code, desc);
-}
-
-/**
- * _scsih_tm_done - tm completion routine
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: none.
- *
- * The callback handler when using scsih_issue_tm.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
-{
- MPI2DefaultReply_t *mpi_reply;
-
- if (ioc->tm_cmds.status == MPT2_CMD_NOT_USED)
- return 1;
- if (ioc->tm_cmds.smid != smid)
- return 1;
- ioc->tm_cmds.status |= MPT2_CMD_COMPLETE;
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- if (mpi_reply) {
- memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
- ioc->tm_cmds.status |= MPT2_CMD_REPLY_VALID;
- }
- ioc->tm_cmds.status &= ~MPT2_CMD_PENDING;
- complete(&ioc->tm_cmds.done);
- return 1;
-}
-
-/**
- * mpt2sas_scsih_set_tm_flag - set per target tm_busy
- * @ioc: per adapter object
- * @handle: device handle
- *
- * During taskmangement request, we need to freeze the device queue.
- */
-void
-mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct scsi_device *sdev;
- u8 skip = 0;
-
- shost_for_each_device(sdev, ioc->shost) {
- if (skip)
- continue;
- sas_device_priv_data = sdev->hostdata;
- if (!sas_device_priv_data)
- continue;
- if (sas_device_priv_data->sas_target->handle == handle) {
- sas_device_priv_data->sas_target->tm_busy = 1;
- skip = 1;
- ioc->ignore_loginfos = 1;
- }
- }
-}
-
-/**
- * mpt2sas_scsih_clear_tm_flag - clear per target tm_busy
- * @ioc: per adapter object
- * @handle: device handle
- *
- * During taskmangement request, we need to freeze the device queue.
- */
-void
-mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct scsi_device *sdev;
- u8 skip = 0;
-
- shost_for_each_device(sdev, ioc->shost) {
- if (skip)
- continue;
- sas_device_priv_data = sdev->hostdata;
- if (!sas_device_priv_data)
- continue;
- if (sas_device_priv_data->sas_target->handle == handle) {
- sas_device_priv_data->sas_target->tm_busy = 0;
- skip = 1;
- ioc->ignore_loginfos = 0;
- }
- }
-}
-
-
-/**
- * mpt2sas_scsih_issue_tm - main routine for sending tm requests
- * @ioc: per adapter struct
- * @device_handle: device handle
- * @channel: the channel assigned by the OS
- * @id: the id assigned by the OS
- * @lun: lun number
- * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h)
- * @smid_task: smid assigned to the task
- * @timeout: timeout in seconds
- * Context: user
- *
- * A generic API for sending task management requests to firmware.
- *
- * The callback index is set inside `ioc->tm_cb_idx`.
- *
- * Return SUCCESS or FAILED.
- */
-int
-mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint channel,
- uint id, uint lun, u8 type, u16 smid_task, ulong timeout,
- struct scsi_cmnd *scmd)
-{
- Mpi2SCSITaskManagementRequest_t *mpi_request;
- Mpi2SCSITaskManagementReply_t *mpi_reply;
- u16 smid = 0;
- u32 ioc_state;
- unsigned long timeleft;
- struct scsi_cmnd *scmd_lookup;
- int rc;
-
- mutex_lock(&ioc->tm_cmds.mutex);
- if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n",
- __func__, ioc->name);
- rc = FAILED;
- goto err_out;
- }
-
- if (ioc->shost_recovery || ioc->remove_host ||
- ioc->pci_error_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
- rc = FAILED;
- goto err_out;
- }
-
- ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
- if (ioc_state & MPI2_DOORBELL_USED) {
- dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "unexpected doorbell "
- "active!\n", ioc->name));
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- rc = SUCCESS;
- goto err_out;
- }
-
- if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
- mpt2sas_base_fault_info(ioc, ioc_state &
- MPI2_DOORBELL_DATA_MASK);
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- rc = SUCCESS;
- goto err_out;
- }
-
- smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = FAILED;
- goto err_out;
- }
-
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x),"
- " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type,
- smid_task));
- ioc->tm_cmds.status = MPT2_CMD_PENDING;
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->tm_cmds.smid = smid;
- memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
- mpi_request->DevHandle = cpu_to_le16(handle);
- mpi_request->TaskType = type;
- mpi_request->TaskMID = cpu_to_le16(smid_task);
- int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);
- mpt2sas_scsih_set_tm_flag(ioc, handle);
- init_completion(&ioc->tm_cmds.done);
- mpt2sas_base_put_smid_hi_priority(ioc, smid);
- timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ);
- if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SCSITaskManagementRequest_t)/4);
- if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) {
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- rc = SUCCESS;
- ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
- mpt2sas_scsih_clear_tm_flag(ioc, handle);
- goto err_out;
- }
- }
-
- if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) {
- mpi_reply = ioc->tm_cmds.reply;
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "complete tm: "
- "ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n",
- ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo),
- le32_to_cpu(mpi_reply->TerminationCount)));
- if (ioc->logging_level & MPT_DEBUG_TM) {
- _scsih_response_code(ioc, mpi_reply->ResponseCode);
- if (mpi_reply->IOCStatus)
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SCSITaskManagementRequest_t)/4);
- }
- }
-
- /* sanity check:
- * Check to see the commands were terminated.
- * This is only needed for eh callbacks, hence the scmd check.
- */
- rc = FAILED;
- if (scmd == NULL)
- goto bypass_sanity_checks;
- switch (type) {
- case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
- scmd_lookup = _scsih_scsi_lookup_get(ioc, smid_task);
- if (scmd_lookup)
- rc = FAILED;
- else
- rc = SUCCESS;
- break;
-
- case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
- if (_scsih_scsi_lookup_find_by_target(ioc, id, channel))
- rc = FAILED;
- else
- rc = SUCCESS;
- break;
-
- case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET:
- if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel))
- rc = FAILED;
- else
- rc = SUCCESS;
- break;
- }
-
- bypass_sanity_checks:
-
- mpt2sas_scsih_clear_tm_flag(ioc, handle);
- ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
- mutex_unlock(&ioc->tm_cmds.mutex);
-
- return rc;
-
- err_out:
- mutex_unlock(&ioc->tm_cmds.mutex);
- return rc;
-}
-
-/**
- * _scsih_tm_display_info - displays info about the device
- * @ioc: per adapter struct
- * @scmd: pointer to scsi command object
- *
- * Called by task management callback handlers.
- */
-static void
-_scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
-{
- struct scsi_target *starget = scmd->device->sdev_target;
- struct MPT2SAS_TARGET *priv_target = starget->hostdata;
- struct _sas_device *sas_device = NULL;
- unsigned long flags;
- char *device_str = NULL;
-
- if (!priv_target)
- return;
- if (ioc->hide_ir_msg)
- device_str = "WarpDrive";
- else
- device_str = "volume";
-
- scsi_print_command(scmd);
- if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
- starget_printk(KERN_INFO, starget, "%s handle(0x%04x), "
- "%s wwid(0x%016llx)\n", device_str, priv_target->handle,
- device_str, (unsigned long long)priv_target->sas_address);
- } else {
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- priv_target->sas_address);
- if (sas_device) {
- if (priv_target->flags &
- MPT_TARGET_FLAGS_RAID_COMPONENT) {
- starget_printk(KERN_INFO, starget,
- "volume handle(0x%04x), "
- "volume wwid(0x%016llx)\n",
- sas_device->volume_handle,
- (unsigned long long)sas_device->volume_wwid);
- }
- starget_printk(KERN_INFO, starget,
- "handle(0x%04x), sas_address(0x%016llx), phy(%d)\n",
- sas_device->handle,
- (unsigned long long)sas_device->sas_address,
- sas_device->phy);
- starget_printk(KERN_INFO, starget,
- "enclosure_logical_id(0x%016llx), slot(%d)\n",
- (unsigned long long)sas_device->enclosure_logical_id,
- sas_device->slot);
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- }
-}
-
-/**
- * _scsih_abort - eh threads main abort routine
- * @scmd: pointer to scsi command object
- *
- * Returns SUCCESS if command aborted else FAILED
- */
-static int
-_scsih_abort(struct scsi_cmnd *scmd)
-{
- struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- u16 smid;
- u16 handle;
- int r;
-
- sdev_printk(KERN_INFO, scmd->device, "attempting task abort! "
- "scmd(%p)\n", scmd);
- _scsih_tm_display_info(ioc, scmd);
-
- sas_device_priv_data = scmd->device->hostdata;
- if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
- sdev_printk(KERN_INFO, scmd->device, "device been deleted! "
- "scmd(%p)\n", scmd);
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- r = SUCCESS;
- goto out;
- }
-
- /* search for the command */
- smid = _scsih_scsi_lookup_find_by_scmd(ioc, scmd);
- if (!smid) {
- scmd->result = DID_RESET << 16;
- r = SUCCESS;
- goto out;
- }
-
- /* for hidden raid components and volumes this is not supported */
- if (sas_device_priv_data->sas_target->flags &
- MPT_TARGET_FLAGS_RAID_COMPONENT ||
- sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) {
- scmd->result = DID_RESET << 16;
- r = FAILED;
- goto out;
- }
-
- mpt2sas_halt_firmware(ioc);
-
- handle = sas_device_priv_data->sas_target->handle;
- r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel,
- scmd->device->id, scmd->device->lun,
- MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, scmd);
-
- out:
- sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n",
- ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
- return r;
-}
-
-/**
- * _scsih_dev_reset - eh threads main device reset routine
- * @scmd: pointer to scsi command object
- *
- * Returns SUCCESS if command aborted else FAILED
- */
-static int
-_scsih_dev_reset(struct scsi_cmnd *scmd)
-{
- struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct _sas_device *sas_device;
- unsigned long flags;
- u16 handle;
- int r;
-
- struct scsi_target *starget = scmd->device->sdev_target;
-
- starget_printk(KERN_INFO, starget, "attempting device reset! "
- "scmd(%p)\n", scmd);
- _scsih_tm_display_info(ioc, scmd);
-
- sas_device_priv_data = scmd->device->hostdata;
- if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
- starget_printk(KERN_INFO, starget, "device been deleted! "
- "scmd(%p)\n", scmd);
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- r = SUCCESS;
- goto out;
- }
-
- /* for hidden raid components obtain the volume_handle */
- handle = 0;
- if (sas_device_priv_data->sas_target->flags &
- MPT_TARGET_FLAGS_RAID_COMPONENT) {
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc,
- sas_device_priv_data->sas_target->handle);
- if (sas_device)
- handle = sas_device->volume_handle;
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- } else
- handle = sas_device_priv_data->sas_target->handle;
-
- if (!handle) {
- scmd->result = DID_RESET << 16;
- r = FAILED;
- goto out;
- }
-
- r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel,
- scmd->device->id, scmd->device->lun,
- MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, scmd);
-
- out:
- sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n",
- ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
- return r;
-}
-
-/**
- * _scsih_target_reset - eh threads main target reset routine
- * @scmd: pointer to scsi command object
- *
- * Returns SUCCESS if command aborted else FAILED
- */
-static int
-_scsih_target_reset(struct scsi_cmnd *scmd)
-{
- struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct _sas_device *sas_device;
- unsigned long flags;
- u16 handle;
- int r;
- struct scsi_target *starget = scmd->device->sdev_target;
-
- starget_printk(KERN_INFO, starget, "attempting target reset! "
- "scmd(%p)\n", scmd);
- _scsih_tm_display_info(ioc, scmd);
-
- sas_device_priv_data = scmd->device->hostdata;
- if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
- starget_printk(KERN_INFO, starget, "target been deleted! "
- "scmd(%p)\n", scmd);
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- r = SUCCESS;
- goto out;
- }
-
- /* for hidden raid components obtain the volume_handle */
- handle = 0;
- if (sas_device_priv_data->sas_target->flags &
- MPT_TARGET_FLAGS_RAID_COMPONENT) {
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc,
- sas_device_priv_data->sas_target->handle);
- if (sas_device)
- handle = sas_device->volume_handle;
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- } else
- handle = sas_device_priv_data->sas_target->handle;
-
- if (!handle) {
- scmd->result = DID_RESET << 16;
- r = FAILED;
- goto out;
- }
-
- r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel,
- scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0,
- 30, scmd);
-
- out:
- starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n",
- ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
- return r;
-}
-
-/**
- * _scsih_host_reset - eh threads main host reset routine
- * @scmd: pointer to scsi command object
- *
- * Returns SUCCESS if command aborted else FAILED
- */
-static int
-_scsih_host_reset(struct scsi_cmnd *scmd)
-{
- struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
- int r, retval;
-
- printk(MPT2SAS_INFO_FMT "attempting host reset! scmd(%p)\n",
- ioc->name, scmd);
- scsi_print_command(scmd);
-
- retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- r = (retval < 0) ? FAILED : SUCCESS;
- printk(MPT2SAS_INFO_FMT "host reset: %s scmd(%p)\n",
- ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
-
- return r;
-}
-
-/**
- * _scsih_fw_event_add - insert and queue up fw_event
- * @ioc: per adapter object
- * @fw_event: object describing the event
- * Context: This function will acquire ioc->fw_event_lock.
- *
- * This adds the firmware event object into link list, then queues it up to
- * be processed from user context.
- *
- * Return nothing.
- */
-static void
-_scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
-{
- unsigned long flags;
-
- if (ioc->firmware_event_thread == NULL)
- return;
-
- spin_lock_irqsave(&ioc->fw_event_lock, flags);
- list_add_tail(&fw_event->list, &ioc->fw_event_list);
- INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work);
- queue_delayed_work(ioc->firmware_event_thread,
- &fw_event->delayed_work, 0);
- spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
-}
-
-/**
- * _scsih_fw_event_free - delete fw_event
- * @ioc: per adapter object
- * @fw_event: object describing the event
- * Context: This function will acquire ioc->fw_event_lock.
- *
- * This removes firmware event object from link list, frees associated memory.
- *
- * Return nothing.
- */
-static void
-_scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
- *fw_event)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->fw_event_lock, flags);
- list_del(&fw_event->list);
- kfree(fw_event->event_data);
- kfree(fw_event);
- spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
-}
-
-
-/**
- * _scsih_queue_rescan - queue a topology rescan from user context
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc)
-{
- struct fw_event_work *fw_event;
-
- if (ioc->wait_for_port_enable_to_complete)
- return;
- fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
- if (!fw_event)
- return;
- fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET;
- fw_event->ioc = ioc;
- _scsih_fw_event_add(ioc, fw_event);
-}
-
-/**
- * _scsih_fw_event_cleanup_queue - cleanup event queue
- * @ioc: per adapter object
- *
- * Walk the firmware event queue, either killing timers, or waiting
- * for outstanding events to complete
- *
- * Return nothing.
- */
-static void
-_scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc)
-{
- struct fw_event_work *fw_event, *next;
-
- if (list_empty(&ioc->fw_event_list) ||
- !ioc->firmware_event_thread || in_interrupt())
- return;
-
- list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) {
- if (cancel_delayed_work(&fw_event->delayed_work)) {
- _scsih_fw_event_free(ioc, fw_event);
- continue;
- }
- fw_event->cancel_pending_work = 1;
- }
-}
-
-/**
- * _scsih_ublock_io_device - set the device state to SDEV_RUNNING
- * @ioc: per adapter object
- * @handle: device handle
- *
- * During device pull we need to appropiately set the sdev state.
- */
-static void
-_scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct scsi_device *sdev;
-
- shost_for_each_device(sdev, ioc->shost) {
- sas_device_priv_data = sdev->hostdata;
- if (!sas_device_priv_data)
- continue;
- if (!sas_device_priv_data->block)
- continue;
- if (sas_device_priv_data->sas_target->handle == handle) {
- dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
- MPT2SAS_INFO_FMT "SDEV_RUNNING: "
- "handle(0x%04x)\n", ioc->name, handle));
- sas_device_priv_data->block = 0;
- scsi_internal_device_unblock(sdev);
- }
- }
-}
-
-/**
- * _scsih_block_io_device - set the device state to SDEV_BLOCK
- * @ioc: per adapter object
- * @handle: device handle
- *
- * During device pull we need to appropiately set the sdev state.
- */
-static void
-_scsih_block_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct scsi_device *sdev;
-
- shost_for_each_device(sdev, ioc->shost) {
- sas_device_priv_data = sdev->hostdata;
- if (!sas_device_priv_data)
- continue;
- if (sas_device_priv_data->block)
- continue;
- if (sas_device_priv_data->sas_target->handle == handle) {
- dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
- MPT2SAS_INFO_FMT "SDEV_BLOCK: "
- "handle(0x%04x)\n", ioc->name, handle));
- sas_device_priv_data->block = 1;
- scsi_internal_device_block(sdev);
- }
- }
-}
-
-/**
- * _scsih_block_io_to_children_attached_to_ex
- * @ioc: per adapter object
- * @sas_expander: the sas_device object
- *
- * This routine set sdev state to SDEV_BLOCK for all devices
- * attached to this expander. This function called when expander is
- * pulled.
- */
-static void
-_scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_node *sas_expander)
-{
- struct _sas_port *mpt2sas_port;
- struct _sas_device *sas_device;
- struct _sas_node *expander_sibling;
- unsigned long flags;
-
- if (!sas_expander)
- return;
-
- list_for_each_entry(mpt2sas_port,
- &sas_expander->sas_port_list, port_list) {
- if (mpt2sas_port->remote_identify.device_type ==
- SAS_END_DEVICE) {
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device =
- mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- mpt2sas_port->remote_identify.sas_address);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (!sas_device)
- continue;
- _scsih_block_io_device(ioc, sas_device->handle);
- }
- }
-
- list_for_each_entry(mpt2sas_port,
- &sas_expander->sas_port_list, port_list) {
-
- if (mpt2sas_port->remote_identify.device_type ==
- SAS_EDGE_EXPANDER_DEVICE ||
- mpt2sas_port->remote_identify.device_type ==
- SAS_FANOUT_EXPANDER_DEVICE) {
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- expander_sibling =
- mpt2sas_scsih_expander_find_by_sas_address(
- ioc, mpt2sas_port->remote_identify.sas_address);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- _scsih_block_io_to_children_attached_to_ex(ioc,
- expander_sibling);
- }
- }
-}
-
-/**
- * _scsih_block_io_to_children_attached_directly
- * @ioc: per adapter object
- * @event_data: topology change event data
- *
- * This routine set sdev state to SDEV_BLOCK for all devices
- * direct attached during device pull.
- */
-static void
-_scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataSasTopologyChangeList_t *event_data)
-{
- int i;
- u16 handle;
- u16 reason_code;
- u8 phy_number;
-
- for (i = 0; i < event_data->NumEntries; i++) {
- handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
- if (!handle)
- continue;
- phy_number = event_data->StartPhyNum + i;
- reason_code = event_data->PHY[i].PhyStatus &
- MPI2_EVENT_SAS_TOPO_RC_MASK;
- if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
- _scsih_block_io_device(ioc, handle);
- }
-}
-
-/**
- * _scsih_tm_tr_send - send task management request
- * @ioc: per adapter object
- * @handle: device handle
- * Context: interrupt time.
- *
- * This code is to initiate the device removal handshake protocol
- * with controller firmware. This function will issue target reset
- * using high priority request queue. It will send a sas iounit
- * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion.
- *
- * This is designed to send muliple task management request at the same
- * time to the fifo. If the fifo is full, we will append the request,
- * and process it in a future completion.
- */
-static void
-_scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- Mpi2SCSITaskManagementRequest_t *mpi_request;
- u16 smid;
- struct _sas_device *sas_device;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- unsigned long flags;
- struct _tr_list *delayed_tr;
-
- if (ioc->shost_recovery || ioc->remove_host ||
- ioc->pci_error_recovery) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in "
- "progress!\n", __func__, ioc->name));
- return;
- }
-
- /* if PD, then return */
- if (test_bit(handle, ioc->pd_handles))
- return;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- if (sas_device && sas_device->starget &&
- sas_device->starget->hostdata) {
- sas_target_priv_data = sas_device->starget->hostdata;
- sas_target_priv_data->deleted = 1;
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "setting delete flag: handle(0x%04x), "
- "sas_addr(0x%016llx)\n", ioc->name, handle,
- (unsigned long long) sas_device->sas_address));
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
- if (!smid) {
- delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
- if (!delayed_tr)
- return;
- INIT_LIST_HEAD(&delayed_tr->list);
- delayed_tr->handle = handle;
- list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "DELAYED:tr:handle(0x%04x), (open)\n",
- ioc->name, handle));
- return;
- }
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), "
- "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid,
- ioc->tm_tr_cb_idx));
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
- mpi_request->DevHandle = cpu_to_le16(handle);
- mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
- mpt2sas_base_put_smid_hi_priority(ioc, smid);
-}
-
-
-
-/**
- * _scsih_sas_control_complete - completion routine
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: interrupt time.
- *
- * This is the sas iounit control completion routine.
- * This code is part of the code to initiate the device removal
- * handshake protocol with controller firmware.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid,
- u8 msix_index, u32 reply)
-{
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- Mpi2SasIoUnitControlReply_t *mpi_reply =
- mpt2sas_base_get_reply_virt_addr(ioc, reply);
-#endif
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "sc_complete:handle(0x%04x), (open) "
- "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
- ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid,
- le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo)));
- return 1;
-}
-
-/**
- * _scsih_tm_tr_volume_send - send target reset request for volumes
- * @ioc: per adapter object
- * @handle: device handle
- * Context: interrupt time.
- *
- * This is designed to send muliple task management request at the same
- * time to the fifo. If the fifo is full, we will append the request,
- * and process it in a future completion.
- */
-static void
-_scsih_tm_tr_volume_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- Mpi2SCSITaskManagementRequest_t *mpi_request;
- u16 smid;
- struct _tr_list *delayed_tr;
-
- if (ioc->shost_recovery || ioc->remove_host ||
- ioc->pci_error_recovery) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in "
- "progress!\n", __func__, ioc->name));
- return;
- }
-
- smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_volume_cb_idx);
- if (!smid) {
- delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
- if (!delayed_tr)
- return;
- INIT_LIST_HEAD(&delayed_tr->list);
- delayed_tr->handle = handle;
- list_add_tail(&delayed_tr->list, &ioc->delayed_tr_volume_list);
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "DELAYED:tr:handle(0x%04x), (open)\n",
- ioc->name, handle));
- return;
- }
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), "
- "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid,
- ioc->tm_tr_volume_cb_idx));
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
- mpi_request->DevHandle = cpu_to_le16(handle);
- mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
- mpt2sas_base_put_smid_hi_priority(ioc, smid);
-}
-
-/**
- * _scsih_tm_volume_tr_complete - target reset completion
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: interrupt time.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_scsih_tm_volume_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid,
- u8 msix_index, u32 reply)
-{
- u16 handle;
- Mpi2SCSITaskManagementRequest_t *mpi_request_tm;
- Mpi2SCSITaskManagementReply_t *mpi_reply =
- mpt2sas_base_get_reply_virt_addr(ioc, reply);
-
- if (ioc->shost_recovery || ioc->remove_host ||
- ioc->pci_error_recovery) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in "
- "progress!\n", __func__, ioc->name));
- return 1;
- }
-
- mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid);
- handle = le16_to_cpu(mpi_request_tm->DevHandle);
- if (handle != le16_to_cpu(mpi_reply->DevHandle)) {
- dewtprintk(ioc, printk("spurious interrupt: "
- "handle(0x%04x:0x%04x), smid(%d)!!!\n", handle,
- le16_to_cpu(mpi_reply->DevHandle), smid));
- return 0;
- }
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), "
- "loginfo(0x%08x), completed(%d)\n", ioc->name,
- handle, smid, le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo),
- le32_to_cpu(mpi_reply->TerminationCount)));
-
- return _scsih_check_for_pending_tm(ioc, smid);
-}
-
-/**
- * _scsih_tm_tr_complete -
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: interrupt time.
- *
- * This is the target reset completion routine.
- * This code is part of the code to initiate the device removal
- * handshake protocol with controller firmware.
- * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE)
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply)
-{
- u16 handle;
- Mpi2SCSITaskManagementRequest_t *mpi_request_tm;
- Mpi2SCSITaskManagementReply_t *mpi_reply =
- mpt2sas_base_get_reply_virt_addr(ioc, reply);
- Mpi2SasIoUnitControlRequest_t *mpi_request;
- u16 smid_sas_ctrl;
-
- if (ioc->shost_recovery || ioc->remove_host ||
- ioc->pci_error_recovery) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in "
- "progress!\n", __func__, ioc->name));
- return 1;
- }
-
- mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid);
- handle = le16_to_cpu(mpi_request_tm->DevHandle);
- if (handle != le16_to_cpu(mpi_reply->DevHandle)) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "spurious interrupt: "
- "handle(0x%04x:0x%04x), smid(%d)!!!\n", ioc->name, handle,
- le16_to_cpu(mpi_reply->DevHandle), smid));
- return 0;
- }
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), "
- "loginfo(0x%08x), completed(%d)\n", ioc->name,
- handle, smid, le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo),
- le32_to_cpu(mpi_reply->TerminationCount)));
-
- smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx);
- if (!smid_sas_ctrl) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- return 1;
- }
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sc_send:handle(0x%04x), "
- "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid_sas_ctrl,
- ioc->tm_sas_control_cb_idx));
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl);
- memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
- mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
- mpi_request->DevHandle = mpi_request_tm->DevHandle;
- mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl);
-
- return _scsih_check_for_pending_tm(ioc, smid);
-}
-
-/**
- * _scsih_check_for_pending_tm - check for pending task management
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * This will check delayed target reset list, and feed the
- * next reqeust.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- struct _tr_list *delayed_tr;
-
- if (!list_empty(&ioc->delayed_tr_volume_list)) {
- delayed_tr = list_entry(ioc->delayed_tr_volume_list.next,
- struct _tr_list, list);
- mpt2sas_base_free_smid(ioc, smid);
- _scsih_tm_tr_volume_send(ioc, delayed_tr->handle);
- list_del(&delayed_tr->list);
- kfree(delayed_tr);
- return 0;
- }
-
- if (!list_empty(&ioc->delayed_tr_list)) {
- delayed_tr = list_entry(ioc->delayed_tr_list.next,
- struct _tr_list, list);
- mpt2sas_base_free_smid(ioc, smid);
- _scsih_tm_tr_send(ioc, delayed_tr->handle);
- list_del(&delayed_tr->list);
- kfree(delayed_tr);
- return 0;
- }
-
- return 1;
-}
-
-/**
- * _scsih_check_topo_delete_events - sanity check on topo events
- * @ioc: per adapter object
- * @event_data: the event data payload
- *
- * This routine added to better handle cable breaker.
- *
- * This handles the case where driver receives multiple expander
- * add and delete events in a single shot. When there is a delete event
- * the routine will void any pending add events waiting in the event queue.
- *
- * Return nothing.
- */
-static void
-_scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataSasTopologyChangeList_t *event_data)
-{
- struct fw_event_work *fw_event;
- Mpi2EventDataSasTopologyChangeList_t *local_event_data;
- u16 expander_handle;
- struct _sas_node *sas_expander;
- unsigned long flags;
- int i, reason_code;
- u16 handle;
-
- for (i = 0 ; i < event_data->NumEntries; i++) {
- handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
- if (!handle)
- continue;
- reason_code = event_data->PHY[i].PhyStatus &
- MPI2_EVENT_SAS_TOPO_RC_MASK;
- if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)
- _scsih_tm_tr_send(ioc, handle);
- }
-
- expander_handle = le16_to_cpu(event_data->ExpanderDevHandle);
- if (expander_handle < ioc->sas_hba.num_phys) {
- _scsih_block_io_to_children_attached_directly(ioc, event_data);
- return;
- }
-
- if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING
- || event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) {
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
- expander_handle);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander);
- } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING)
- _scsih_block_io_to_children_attached_directly(ioc, event_data);
-
- if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
- return;
-
- /* mark ignore flag for pending events */
- spin_lock_irqsave(&ioc->fw_event_lock, flags);
- list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
- if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
- fw_event->ignore)
- continue;
- local_event_data = fw_event->event_data;
- if (local_event_data->ExpStatus ==
- MPI2_EVENT_SAS_TOPO_ES_ADDED ||
- local_event_data->ExpStatus ==
- MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
- if (le16_to_cpu(local_event_data->ExpanderDevHandle) ==
- expander_handle) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "setting ignoring flag\n", ioc->name));
- fw_event->ignore = 1;
- }
- }
- }
- spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
-}
-
-/**
- * _scsih_set_volume_delete_flag - setting volume delete flag
- * @ioc: per adapter object
- * @handle: device handle
- *
- * This
- * Return nothing.
- */
-static void
-_scsih_set_volume_delete_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _raid_device *raid_device;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
- if (raid_device && raid_device->starget &&
- raid_device->starget->hostdata) {
- sas_target_priv_data =
- raid_device->starget->hostdata;
- sas_target_priv_data->deleted = 1;
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "setting delete flag: handle(0x%04x), "
- "wwid(0x%016llx)\n", ioc->name, handle,
- (unsigned long long) raid_device->wwid));
- }
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-}
-
-/**
- * _scsih_set_volume_handle_for_tr - set handle for target reset to volume
- * @handle: input handle
- * @a: handle for volume a
- * @b: handle for volume b
- *
- * IR firmware only supports two raid volumes. The purpose of this
- * routine is to set the volume handle in either a or b. When the given
- * input handle is non-zero, or when a and b have not been set before.
- */
-static void
-_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b)
-{
- if (!handle || handle == *a || handle == *b)
- return;
- if (!*a)
- *a = handle;
- else if (!*b)
- *b = handle;
-}
-
-/**
- * _scsih_check_ir_config_unhide_events - check for UNHIDE events
- * @ioc: per adapter object
- * @event_data: the event data payload
- * Context: interrupt time.
- *
- * This routine will send target reset to volume, followed by target
- * resets to the PDs. This is called when a PD has been removed, or
- * volume has been deleted or removed. When the target reset is sent
- * to volume, the PD target resets need to be queued to start upon
- * completion of the volume target reset.
- *
- * Return nothing.
- */
-static void
-_scsih_check_ir_config_unhide_events(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataIrConfigChangeList_t *event_data)
-{
- Mpi2EventIrConfigElement_t *element;
- int i;
- u16 handle, volume_handle, a, b;
- struct _tr_list *delayed_tr;
-
- a = 0;
- b = 0;
-
- if (ioc->is_warpdrive)
- return;
-
- /* Volume Resets for Deleted or Removed */
- element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
- for (i = 0; i < event_data->NumElements; i++, element++) {
- if (element->ReasonCode ==
- MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED ||
- element->ReasonCode ==
- MPI2_EVENT_IR_CHANGE_RC_REMOVED) {
- volume_handle = le16_to_cpu(element->VolDevHandle);
- _scsih_set_volume_delete_flag(ioc, volume_handle);
- _scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
- }
- }
-
- /* Volume Resets for UNHIDE events */
- element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
- for (i = 0; i < event_data->NumElements; i++, element++) {
- if (le32_to_cpu(event_data->Flags) &
- MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
- continue;
- if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) {
- volume_handle = le16_to_cpu(element->VolDevHandle);
- _scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
- }
- }
-
- if (a)
- _scsih_tm_tr_volume_send(ioc, a);
- if (b)
- _scsih_tm_tr_volume_send(ioc, b);
-
- /* PD target resets */
- element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
- for (i = 0; i < event_data->NumElements; i++, element++) {
- if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE)
- continue;
- handle = le16_to_cpu(element->PhysDiskDevHandle);
- volume_handle = le16_to_cpu(element->VolDevHandle);
- clear_bit(handle, ioc->pd_handles);
- if (!volume_handle)
- _scsih_tm_tr_send(ioc, handle);
- else if (volume_handle == a || volume_handle == b) {
- delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
- BUG_ON(!delayed_tr);
- INIT_LIST_HEAD(&delayed_tr->list);
- delayed_tr->handle = handle;
- list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
- "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name,
- handle));
- } else
- _scsih_tm_tr_send(ioc, handle);
- }
-}
-
-
-/**
- * _scsih_check_volume_delete_events - set delete flag for volumes
- * @ioc: per adapter object
- * @event_data: the event data payload
- * Context: interrupt time.
- *
- * This will handle the case when the cable connected to entire volume is
- * pulled. We will take care of setting the deleted flag so normal IO will
- * not be sent.
- *
- * Return nothing.
- */
-static void
-_scsih_check_volume_delete_events(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataIrVolume_t *event_data)
-{
- u32 state;
-
- if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
- return;
- state = le32_to_cpu(event_data->NewValue);
- if (state == MPI2_RAID_VOL_STATE_MISSING || state ==
- MPI2_RAID_VOL_STATE_FAILED)
- _scsih_set_volume_delete_flag(ioc,
- le16_to_cpu(event_data->VolDevHandle));
-}
-
-/**
- * _scsih_flush_running_cmds - completing outstanding commands.
- * @ioc: per adapter object
- *
- * The flushing out of all pending scmd commands following host reset,
- * where all IO is dropped to the floor.
- *
- * Return nothing.
- */
-static void
-_scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc)
-{
- struct scsi_cmnd *scmd;
- u16 smid;
- u16 count = 0;
-
- for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
- scmd = _scsih_scsi_lookup_get_clear(ioc, smid);
- if (!scmd)
- continue;
- count++;
- mpt2sas_base_free_smid(ioc, smid);
- scsi_dma_unmap(scmd);
- if (ioc->pci_error_recovery)
- scmd->result = DID_NO_CONNECT << 16;
- else
- scmd->result = DID_RESET << 16;
- scmd->scsi_done(scmd);
- }
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "completing %d cmds\n",
- ioc->name, count));
-}
-
-/**
- * _scsih_setup_eedp - setup MPI request for EEDP transfer
- * @scmd: pointer to scsi command object
- * @mpi_request: pointer to the SCSI_IO reqest message frame
- *
- * Supporting protection 1 and 3.
- *
- * Returns nothing
- */
-static void
-_scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
-{
- u16 eedp_flags;
- unsigned char prot_op = scsi_get_prot_op(scmd);
- unsigned char prot_type = scsi_get_prot_type(scmd);
-
- if (prot_type == SCSI_PROT_DIF_TYPE0 || prot_op == SCSI_PROT_NORMAL)
- return;
-
- if (prot_op == SCSI_PROT_READ_STRIP)
- eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP;
- else if (prot_op == SCSI_PROT_WRITE_INSERT)
- eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP;
- else
- return;
-
- switch (prot_type) {
- case SCSI_PROT_DIF_TYPE1:
- case SCSI_PROT_DIF_TYPE2:
-
- /*
- * enable ref/guard checking
- * auto increment ref tag
- */
- eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
- MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
- MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
- mpi_request->CDB.EEDP32.PrimaryReferenceTag =
- cpu_to_be32(scsi_get_lba(scmd));
- break;
-
- case SCSI_PROT_DIF_TYPE3:
-
- /*
- * enable guard checking
- */
- eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
- break;
- }
- mpi_request->EEDPBlockSize = cpu_to_le32(scmd->device->sector_size);
- mpi_request->EEDPFlags = cpu_to_le16(eedp_flags);
-}
-
-/**
- * _scsih_eedp_error_handling - return sense code for EEDP errors
- * @scmd: pointer to scsi command object
- * @ioc_status: ioc status
- *
- * Returns nothing
- */
-static void
-_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
-{
- u8 ascq;
- u8 sk;
- u8 host_byte;
-
- switch (ioc_status) {
- case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
- ascq = 0x01;
- break;
- case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
- ascq = 0x02;
- break;
- case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
- ascq = 0x03;
- break;
- default:
- ascq = 0x00;
- break;
- }
-
- if (scmd->sc_data_direction == DMA_TO_DEVICE) {
- sk = ILLEGAL_REQUEST;
- host_byte = DID_ABORT;
- } else {
- sk = ABORTED_COMMAND;
- host_byte = DID_OK;
- }
-
- scsi_build_sense_buffer(0, scmd->sense_buffer, sk, 0x10, ascq);
- scmd->result = DRIVER_SENSE << 24 | (host_byte << 16) |
- SAM_STAT_CHECK_CONDITION;
-}
-
-/**
- * _scsih_scsi_direct_io_get - returns direct io flag
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Returns the smid stored scmd pointer.
- */
-static inline u8
-_scsih_scsi_direct_io_get(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
- return ioc->scsi_lookup[smid - 1].direct_io;
-}
-
-/**
- * _scsih_scsi_direct_io_set - sets direct io flag
- * @ioc: per adapter object
- * @smid: system request message index
- * @direct_io: Zero or non-zero value to set in the direct_io flag
- *
- * Returns Nothing.
- */
-static inline void
-_scsih_scsi_direct_io_set(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 direct_io)
-{
- ioc->scsi_lookup[smid - 1].direct_io = direct_io;
-}
-
-
-/**
- * _scsih_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O
- * @ioc: per adapter object
- * @scmd: pointer to scsi command object
- * @raid_device: pointer to raid device data structure
- * @mpi_request: pointer to the SCSI_IO reqest message frame
- * @smid: system request message index
- *
- * Returns nothing
- */
-static void
-_scsih_setup_direct_io(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
- struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request,
- u16 smid)
-{
- u32 v_lba, p_lba, stripe_off, stripe_unit, column, io_size;
- u32 stripe_sz, stripe_exp;
- u8 num_pds, *cdb_ptr, *tmp_ptr, *lba_ptr1, *lba_ptr2;
- u8 cdb0 = scmd->cmnd[0];
-
- /*
- * Try Direct I/O to RAID memeber disks
- */
- if (cdb0 == READ_16 || cdb0 == READ_10 ||
- cdb0 == WRITE_16 || cdb0 == WRITE_10) {
- cdb_ptr = mpi_request->CDB.CDB32;
-
- if ((cdb0 < READ_16) || !(cdb_ptr[2] | cdb_ptr[3] | cdb_ptr[4]
- | cdb_ptr[5])) {
- io_size = scsi_bufflen(scmd) >> 9;
- /* get virtual lba */
- lba_ptr1 = lba_ptr2 = (cdb0 < READ_16) ? &cdb_ptr[2] :
- &cdb_ptr[6];
- tmp_ptr = (u8 *)&v_lba + 3;
- *tmp_ptr-- = *lba_ptr1++;
- *tmp_ptr-- = *lba_ptr1++;
- *tmp_ptr-- = *lba_ptr1++;
- *tmp_ptr = *lba_ptr1;
-
- if (((u64)v_lba + (u64)io_size - 1) <=
- (u32)raid_device->max_lba) {
- stripe_sz = raid_device->stripe_sz;
- stripe_exp = raid_device->stripe_exponent;
- stripe_off = v_lba & (stripe_sz - 1);
-
- /* Check whether IO falls within a stripe */
- if ((stripe_off + io_size) <= stripe_sz) {
- num_pds = raid_device->num_pds;
- p_lba = v_lba >> stripe_exp;
- stripe_unit = p_lba / num_pds;
- column = p_lba % num_pds;
- p_lba = (stripe_unit << stripe_exp) +
- stripe_off;
- mpi_request->DevHandle =
- cpu_to_le16(raid_device->
- pd_handle[column]);
- tmp_ptr = (u8 *)&p_lba + 3;
- *lba_ptr2++ = *tmp_ptr--;
- *lba_ptr2++ = *tmp_ptr--;
- *lba_ptr2++ = *tmp_ptr--;
- *lba_ptr2 = *tmp_ptr;
- /*
- * WD: To indicate this I/O is directI/O
- */
- _scsih_scsi_direct_io_set(ioc, smid, 1);
- }
- }
- }
- }
-}
-
-/**
- * _scsih_qcmd - main scsi request entry point
- * @scmd: pointer to scsi command object
- * @done: function pointer to be invoked on completion
- *
- * The callback index is set inside `ioc->scsi_io_cb_idx`.
- *
- * Returns 0 on success. If there's a failure, return either:
- * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or
- * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full
- */
-static int
-_scsih_qcmd_lck(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
-{
- struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct _raid_device *raid_device;
- Mpi2SCSIIORequest_t *mpi_request;
- u32 mpi_control;
- u16 smid;
-
- scmd->scsi_done = done;
- sas_device_priv_data = scmd->device->hostdata;
- if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- return 0;
- }
-
- if (ioc->pci_error_recovery) {
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- return 0;
- }
-
- sas_target_priv_data = sas_device_priv_data->sas_target;
- /* invalid device handle */
- if (sas_target_priv_data->handle == MPT2SAS_INVALID_DEVICE_HANDLE) {
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- return 0;
- }
-
- /* host recovery or link resets sent via IOCTLs */
- if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)
- return SCSI_MLQUEUE_HOST_BUSY;
- /* device busy with task management */
- else if (sas_device_priv_data->block || sas_target_priv_data->tm_busy)
- return SCSI_MLQUEUE_DEVICE_BUSY;
- /* device has been deleted */
- else if (sas_target_priv_data->deleted) {
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- return 0;
- }
-
- if (scmd->sc_data_direction == DMA_FROM_DEVICE)
- mpi_control = MPI2_SCSIIO_CONTROL_READ;
- else if (scmd->sc_data_direction == DMA_TO_DEVICE)
- mpi_control = MPI2_SCSIIO_CONTROL_WRITE;
- else
- mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
-
- /* set tags */
- if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) {
- if (scmd->device->tagged_supported) {
- if (scmd->device->ordered_tags)
- mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ;
- else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
- } else
-/* MPI Revision I (UNIT = 0xA) - removed MPI2_SCSIIO_CONTROL_UNTAGGED */
-/* mpi_control |= MPI2_SCSIIO_CONTROL_UNTAGGED;
- */
- mpi_control |= (0x500);
-
- } else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
- /* Make sure Device is not raid volume.
- * We do not expose raid functionality to upper layer for warpdrive.
- */
- if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) &&
- sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32)
- mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
-
- smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- goto out;
- }
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t));
- _scsih_setup_eedp(scmd, mpi_request);
- if (scmd->cmd_len == 32)
- mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT;
- mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
- if (sas_device_priv_data->sas_target->flags &
- MPT_TARGET_FLAGS_RAID_COMPONENT)
- mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
- else
- mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
- mpi_request->DevHandle =
- cpu_to_le16(sas_device_priv_data->sas_target->handle);
- mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
- mpi_request->Control = cpu_to_le32(mpi_control);
- mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len);
- mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
- mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
- mpi_request->SenseBufferLowAddress =
- mpt2sas_base_get_sense_buffer_dma(ioc, smid);
- mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4;
- mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI +
- MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR);
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
- int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *)
- mpi_request->LUN);
- memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
-
- if (!mpi_request->DataLength) {
- mpt2sas_base_build_zero_len_sge(ioc, &mpi_request->SGL);
- } else {
- if (_scsih_build_scatter_gather(ioc, scmd, smid)) {
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
- }
-
- raid_device = sas_target_priv_data->raid_device;
- if (raid_device && raid_device->direct_io_enabled)
- _scsih_setup_direct_io(ioc, scmd, raid_device, mpi_request,
- smid);
-
- if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST))
- mpt2sas_base_put_smid_scsi_io(ioc, smid,
- le16_to_cpu(mpi_request->DevHandle));
- else
- mpt2sas_base_put_smid_default(ioc, smid);
- return 0;
-
- out:
- return SCSI_MLQUEUE_HOST_BUSY;
-}
-
-static DEF_SCSI_QCMD(_scsih_qcmd)
-
-/**
- * _scsih_normalize_sense - normalize descriptor and fixed format sense data
- * @sense_buffer: sense data returned by target
- * @data: normalized skey/asc/ascq
- *
- * Return nothing.
- */
-static void
-_scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
-{
- if ((sense_buffer[0] & 0x7F) >= 0x72) {
- /* descriptor format */
- data->skey = sense_buffer[1] & 0x0F;
- data->asc = sense_buffer[2];
- data->ascq = sense_buffer[3];
- } else {
- /* fixed format */
- data->skey = sense_buffer[2] & 0x0F;
- data->asc = sense_buffer[12];
- data->ascq = sense_buffer[13];
- }
-}
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _scsih_scsi_ioc_info - translated non-successful SCSI_IO request
- * @ioc: per adapter object
- * @scmd: pointer to scsi command object
- * @mpi_reply: reply mf payload returned from firmware
- *
- * scsi_status - SCSI Status code returned from target device
- * scsi_state - state info associated with SCSI_IO determined by ioc
- * ioc_status - ioc supplied status info
- *
- * Return nothing.
- */
-static void
-_scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
- Mpi2SCSIIOReply_t *mpi_reply, u16 smid)
-{
- u32 response_info;
- u8 *response_bytes;
- u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- u8 scsi_state = mpi_reply->SCSIState;
- u8 scsi_status = mpi_reply->SCSIStatus;
- char *desc_ioc_state = NULL;
- char *desc_scsi_status = NULL;
- char *desc_scsi_state = ioc->tmp_string;
- u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
- struct _sas_device *sas_device = NULL;
- unsigned long flags;
- struct scsi_target *starget = scmd->device->sdev_target;
- struct MPT2SAS_TARGET *priv_target = starget->hostdata;
- char *device_str = NULL;
-
- if (!priv_target)
- return;
-
- if (ioc->hide_ir_msg)
- device_str = "WarpDrive";
- else
- device_str = "volume";
-
- if (log_info == 0x31170000)
- return;
-
- switch (ioc_status) {
- case MPI2_IOCSTATUS_SUCCESS:
- desc_ioc_state = "success";
- break;
- case MPI2_IOCSTATUS_INVALID_FUNCTION:
- desc_ioc_state = "invalid function";
- break;
- case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
- desc_ioc_state = "scsi recovered error";
- break;
- case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
- desc_ioc_state = "scsi invalid dev handle";
- break;
- case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
- desc_ioc_state = "scsi device not there";
- break;
- case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
- desc_ioc_state = "scsi data overrun";
- break;
- case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
- desc_ioc_state = "scsi data underrun";
- break;
- case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
- desc_ioc_state = "scsi io data error";
- break;
- case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
- desc_ioc_state = "scsi protocol error";
- break;
- case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
- desc_ioc_state = "scsi task terminated";
- break;
- case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
- desc_ioc_state = "scsi residual mismatch";
- break;
- case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
- desc_ioc_state = "scsi task mgmt failed";
- break;
- case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
- desc_ioc_state = "scsi ioc terminated";
- break;
- case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
- desc_ioc_state = "scsi ext terminated";
- break;
- case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
- desc_ioc_state = "eedp guard error";
- break;
- case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
- desc_ioc_state = "eedp ref tag error";
- break;
- case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
- desc_ioc_state = "eedp app tag error";
- break;
- default:
- desc_ioc_state = "unknown";
- break;
- }
-
- switch (scsi_status) {
- case MPI2_SCSI_STATUS_GOOD:
- desc_scsi_status = "good";
- break;
- case MPI2_SCSI_STATUS_CHECK_CONDITION:
- desc_scsi_status = "check condition";
- break;
- case MPI2_SCSI_STATUS_CONDITION_MET:
- desc_scsi_status = "condition met";
- break;
- case MPI2_SCSI_STATUS_BUSY:
- desc_scsi_status = "busy";
- break;
- case MPI2_SCSI_STATUS_INTERMEDIATE:
- desc_scsi_status = "intermediate";
- break;
- case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET:
- desc_scsi_status = "intermediate condmet";
- break;
- case MPI2_SCSI_STATUS_RESERVATION_CONFLICT:
- desc_scsi_status = "reservation conflict";
- break;
- case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
- desc_scsi_status = "command terminated";
- break;
- case MPI2_SCSI_STATUS_TASK_SET_FULL:
- desc_scsi_status = "task set full";
- break;
- case MPI2_SCSI_STATUS_ACA_ACTIVE:
- desc_scsi_status = "aca active";
- break;
- case MPI2_SCSI_STATUS_TASK_ABORTED:
- desc_scsi_status = "task aborted";
- break;
- default:
- desc_scsi_status = "unknown";
- break;
- }
-
- desc_scsi_state[0] = '\0';
- if (!scsi_state)
- desc_scsi_state = " ";
- if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
- strcat(desc_scsi_state, "response info ");
- if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
- strcat(desc_scsi_state, "state terminated ");
- if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS)
- strcat(desc_scsi_state, "no status ");
- if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
- strcat(desc_scsi_state, "autosense failed ");
- if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)
- strcat(desc_scsi_state, "autosense valid ");
-
- scsi_print_command(scmd);
-
- if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
- printk(MPT2SAS_WARN_FMT "\t%s wwid(0x%016llx)\n", ioc->name,
- device_str, (unsigned long long)priv_target->sas_address);
- } else {
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- priv_target->sas_address);
- if (sas_device) {
- printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), "
- "phy(%d)\n", ioc->name, sas_device->sas_address,
- sas_device->phy);
- printk(MPT2SAS_WARN_FMT
- "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
- ioc->name, sas_device->enclosure_logical_id,
- sas_device->slot);
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- }
-
- printk(MPT2SAS_WARN_FMT "\thandle(0x%04x), ioc_status(%s)(0x%04x), "
- "smid(%d)\n", ioc->name, le16_to_cpu(mpi_reply->DevHandle),
- desc_ioc_state, ioc_status, smid);
- printk(MPT2SAS_WARN_FMT "\trequest_len(%d), underflow(%d), "
- "resid(%d)\n", ioc->name, scsi_bufflen(scmd), scmd->underflow,
- scsi_get_resid(scmd));
- printk(MPT2SAS_WARN_FMT "\ttag(%d), transfer_count(%d), "
- "sc->result(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply->TaskTag),
- le32_to_cpu(mpi_reply->TransferCount), scmd->result);
- printk(MPT2SAS_WARN_FMT "\tscsi_status(%s)(0x%02x), "
- "scsi_state(%s)(0x%02x)\n", ioc->name, desc_scsi_status,
- scsi_status, desc_scsi_state, scsi_state);
-
- if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
- struct sense_info data;
- _scsih_normalize_sense(scmd->sense_buffer, &data);
- printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: "
- "[0x%02x,0x%02x,0x%02x], count(%d)\n", ioc->name, data.skey,
- data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount));
- }
-
- if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
- response_info = le32_to_cpu(mpi_reply->ResponseInfo);
- response_bytes = (u8 *)&response_info;
- _scsih_response_code(ioc, response_bytes[0]);
- }
-}
-#endif
-
-/**
- * _scsih_turn_on_fault_led - illuminate Fault LED
- * @ioc: per adapter object
- * @handle: device handle
- * Context: process
- *
- * Return nothing.
- */
-static void
-_scsih_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- Mpi2SepReply_t mpi_reply;
- Mpi2SepRequest_t mpi_request;
-
- memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t));
- mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
- mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
- mpi_request.SlotStatus =
- cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT);
- mpi_request.DevHandle = cpu_to_le16(handle);
- mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS;
- if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply,
- &mpi_request)) != 0) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name,
- __FILE__, __LINE__, __func__);
- return;
- }
-
- if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "enclosure_processor: "
- "ioc_status (0x%04x), loginfo(0x%08x)\n", ioc->name,
- le16_to_cpu(mpi_reply.IOCStatus),
- le32_to_cpu(mpi_reply.IOCLogInfo)));
- return;
- }
-}
-
-/**
- * _scsih_send_event_to_turn_on_fault_led - fire delayed event
- * @ioc: per adapter object
- * @handle: device handle
- * Context: interrupt.
- *
- * Return nothing.
- */
-static void
-_scsih_send_event_to_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct fw_event_work *fw_event;
-
- fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
- if (!fw_event)
- return;
- fw_event->event = MPT2SAS_TURN_ON_FAULT_LED;
- fw_event->device_handle = handle;
- fw_event->ioc = ioc;
- _scsih_fw_event_add(ioc, fw_event);
-}
-
-/**
- * _scsih_smart_predicted_fault - process smart errors
- * @ioc: per adapter object
- * @handle: device handle
- * Context: interrupt.
- *
- * Return nothing.
- */
-static void
-_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct scsi_target *starget;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- Mpi2EventNotificationReply_t *event_reply;
- Mpi2EventDataSasDeviceStatusChange_t *event_data;
- struct _sas_device *sas_device;
- ssize_t sz;
- unsigned long flags;
-
- /* only handle non-raid devices */
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- if (!sas_device) {
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- return;
- }
- starget = sas_device->starget;
- sas_target_priv_data = starget->hostdata;
-
- if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) ||
- ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) {
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- return;
- }
- starget_printk(KERN_WARNING, starget, "predicted fault\n");
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
- _scsih_send_event_to_turn_on_fault_led(ioc, handle);
-
- /* insert into event log */
- sz = offsetof(Mpi2EventNotificationReply_t, EventData) +
- sizeof(Mpi2EventDataSasDeviceStatusChange_t);
- event_reply = kzalloc(sz, GFP_KERNEL);
- if (!event_reply) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
- event_reply->Event =
- cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
- event_reply->MsgLength = sz/4;
- event_reply->EventDataLength =
- cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4);
- event_data = (Mpi2EventDataSasDeviceStatusChange_t *)
- event_reply->EventData;
- event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA;
- event_data->ASC = 0x5D;
- event_data->DevHandle = cpu_to_le16(handle);
- event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address);
- mpt2sas_ctl_add_to_event_log(ioc, event_reply);
- kfree(event_reply);
-}
-
-/**
- * _scsih_io_done - scsi request callback
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- *
- * Callback handler when using _scsih_qcmd.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
-{
- Mpi2SCSIIORequest_t *mpi_request;
- Mpi2SCSIIOReply_t *mpi_reply;
- struct scsi_cmnd *scmd;
- u16 ioc_status;
- u32 xfer_cnt;
- u8 scsi_state;
- u8 scsi_status;
- u32 log_info;
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- u32 response_code = 0;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- scmd = _scsih_scsi_lookup_get_clear(ioc, smid);
- if (scmd == NULL)
- return 1;
-
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
-
- if (mpi_reply == NULL) {
- scmd->result = DID_OK << 16;
- goto out;
- }
-
- sas_device_priv_data = scmd->device->hostdata;
- if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
- sas_device_priv_data->sas_target->deleted) {
- scmd->result = DID_NO_CONNECT << 16;
- goto out;
- }
- /*
- * WARPDRIVE: If direct_io is set then it is directIO,
- * the failed direct I/O should be redirected to volume
- */
- if (_scsih_scsi_direct_io_get(ioc, smid)) {
- _scsih_scsi_direct_io_set(ioc, smid, 0);
- memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
- mpi_request->DevHandle =
- cpu_to_le16(sas_device_priv_data->sas_target->handle);
- mpt2sas_base_put_smid_scsi_io(ioc, smid,
- sas_device_priv_data->sas_target->handle);
- return 0;
- }
-
-
- /* turning off TLR */
- scsi_state = mpi_reply->SCSIState;
- if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
- response_code =
- le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
- if (!sas_device_priv_data->tlr_snoop_check) {
- sas_device_priv_data->tlr_snoop_check++;
- /* Make sure Device is not raid volume.
- * We do not expose raid functionality to upper layer for warpdrive.
- */
- if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) &&
- sas_is_tlr_enabled(scmd->device) &&
- response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) {
- sas_disable_tlr(scmd->device);
- sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n");
- }
- }
-
- xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
- scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
- ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
- if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
- log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
- else
- log_info = 0;
- ioc_status &= MPI2_IOCSTATUS_MASK;
- scsi_status = mpi_reply->SCSIStatus;
-
- if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
- (scsi_status == MPI2_SCSI_STATUS_BUSY ||
- scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT ||
- scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) {
- ioc_status = MPI2_IOCSTATUS_SUCCESS;
- }
-
- if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
- struct sense_info data;
- const void *sense_data = mpt2sas_base_get_sense_buffer(ioc,
- smid);
- u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
- le32_to_cpu(mpi_reply->SenseCount));
- memcpy(scmd->sense_buffer, sense_data, sz);
- _scsih_normalize_sense(scmd->sense_buffer, &data);
- /* failure prediction threshold exceeded */
- if (data.asc == 0x5D)
- _scsih_smart_predicted_fault(ioc,
- le16_to_cpu(mpi_reply->DevHandle));
- }
-
- switch (ioc_status) {
- case MPI2_IOCSTATUS_BUSY:
- case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
- scmd->result = SAM_STAT_BUSY;
- break;
-
- case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
- scmd->result = DID_NO_CONNECT << 16;
- break;
-
- case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
- if (sas_device_priv_data->block) {
- scmd->result = DID_TRANSPORT_DISRUPTED << 16;
- goto out;
- }
- case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
- case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
- scmd->result = DID_RESET << 16;
- break;
-
- case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
- if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt))
- scmd->result = DID_SOFT_ERROR << 16;
- else
- scmd->result = (DID_OK << 16) | scsi_status;
- break;
-
- case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
- scmd->result = (DID_OK << 16) | scsi_status;
-
- if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID))
- break;
-
- if (xfer_cnt < scmd->underflow) {
- if (scsi_status == SAM_STAT_BUSY)
- scmd->result = SAM_STAT_BUSY;
- else
- scmd->result = DID_SOFT_ERROR << 16;
- } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
- MPI2_SCSI_STATE_NO_SCSI_STATUS))
- scmd->result = DID_SOFT_ERROR << 16;
- else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
- scmd->result = DID_RESET << 16;
- else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) {
- mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID;
- mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION;
- scmd->result = (DRIVER_SENSE << 24) |
- SAM_STAT_CHECK_CONDITION;
- scmd->sense_buffer[0] = 0x70;
- scmd->sense_buffer[2] = ILLEGAL_REQUEST;
- scmd->sense_buffer[12] = 0x20;
- scmd->sense_buffer[13] = 0;
- }
- break;
-
- case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
- scsi_set_resid(scmd, 0);
- case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
- case MPI2_IOCSTATUS_SUCCESS:
- scmd->result = (DID_OK << 16) | scsi_status;
- if (response_code ==
- MPI2_SCSITASKMGMT_RSP_INVALID_FRAME ||
- (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
- MPI2_SCSI_STATE_NO_SCSI_STATUS)))
- scmd->result = DID_SOFT_ERROR << 16;
- else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
- scmd->result = DID_RESET << 16;
- break;
-
- case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
- case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
- case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
- _scsih_eedp_error_handling(scmd, ioc_status);
- break;
- case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
- case MPI2_IOCSTATUS_INVALID_FUNCTION:
- case MPI2_IOCSTATUS_INVALID_SGL:
- case MPI2_IOCSTATUS_INTERNAL_ERROR:
- case MPI2_IOCSTATUS_INVALID_FIELD:
- case MPI2_IOCSTATUS_INVALID_STATE:
- case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
- case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
- default:
- scmd->result = DID_SOFT_ERROR << 16;
- break;
-
- }
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY))
- _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid);
-#endif
-
- out:
- scsi_dma_unmap(scmd);
- scmd->scsi_done(scmd);
- return 1;
-}
-
-/**
- * _scsih_sas_host_refresh - refreshing sas host object contents
- * @ioc: per adapter object
- * Context: user
- *
- * During port enable, fw will send topology events for every device. Its
- * possible that the handles may change from the previous setting, so this
- * code keeping handles updating if changed.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc)
-{
- u16 sz;
- u16 ioc_status;
- int i;
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
- u16 attached_handle;
- u8 link_rate;
-
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT
- "updating handles for sas_host(0x%016llx)\n",
- ioc->name, (unsigned long long)ioc->sas_hba.sas_address));
-
- sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys
- * sizeof(Mpi2SasIOUnit0PhyData_t));
- sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
- if (!sas_iounit_pg0) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
- sas_iounit_pg0, sz)) != 0)
- goto out;
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
- goto out;
- for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
- link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4;
- if (i == 0)
- ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
- PhyData[0].ControllerDevHandle);
- ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
- attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i].
- AttachedDevHandle);
- if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5)
- link_rate = MPI2_SAS_NEG_LINK_RATE_1_5;
- mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address,
- attached_handle, i, link_rate);
- }
- out:
- kfree(sas_iounit_pg0);
-}
-
-/**
- * _scsih_sas_host_add - create sas host object
- * @ioc: per adapter object
- *
- * Creating host side data object, stored in ioc->sas_hba
- *
- * Return nothing.
- */
-static void
-_scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc)
-{
- int i;
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
- Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
- Mpi2SasPhyPage0_t phy_pg0;
- Mpi2SasDevicePage0_t sas_device_pg0;
- Mpi2SasEnclosurePage0_t enclosure_pg0;
- u16 ioc_status;
- u16 sz;
- u16 device_missing_delay;
-
- mpt2sas_config_get_number_hba_phys(ioc, &ioc->sas_hba.num_phys);
- if (!ioc->sas_hba.num_phys) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- /* sas_iounit page 0 */
- sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys *
- sizeof(Mpi2SasIOUnit0PhyData_t));
- sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
- if (!sas_iounit_pg0) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
- if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
- sas_iounit_pg0, sz))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
-
- /* sas_iounit page 1 */
- sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
- sizeof(Mpi2SasIOUnit1PhyData_t));
- sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
- if (!sas_iounit_pg1) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
- sas_iounit_pg1, sz))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
-
- ioc->io_missing_delay =
- le16_to_cpu(sas_iounit_pg1->IODeviceMissingDelay);
- device_missing_delay =
- le16_to_cpu(sas_iounit_pg1->ReportDeviceMissingDelay);
- if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
- ioc->device_missing_delay = (device_missing_delay &
- MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
- else
- ioc->device_missing_delay = device_missing_delay &
- MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
-
- ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev;
- ioc->sas_hba.phy = kcalloc(ioc->sas_hba.num_phys,
- sizeof(struct _sas_phy), GFP_KERNEL);
- if (!ioc->sas_hba.phy) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
- if ((mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
- i))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
-
- if (i == 0)
- ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
- PhyData[0].ControllerDevHandle);
- ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
- ioc->sas_hba.phy[i].phy_id = i;
- mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
- phy_pg0, ioc->sas_hba.parent_dev);
- }
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out;
- }
- ioc->sas_hba.enclosure_handle =
- le16_to_cpu(sas_device_pg0.EnclosureHandle);
- ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
- printk(MPT2SAS_INFO_FMT "host_add: handle(0x%04x), "
- "sas_addr(0x%016llx), phys(%d)\n", ioc->name, ioc->sas_hba.handle,
- (unsigned long long) ioc->sas_hba.sas_address,
- ioc->sas_hba.num_phys) ;
-
- if (ioc->sas_hba.enclosure_handle) {
- if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply,
- &enclosure_pg0,
- MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
- ioc->sas_hba.enclosure_handle))) {
- ioc->sas_hba.enclosure_logical_id =
- le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
- }
- }
-
- out:
- kfree(sas_iounit_pg1);
- kfree(sas_iounit_pg0);
-}
-
-/**
- * _scsih_expander_add - creating expander object
- * @ioc: per adapter object
- * @handle: expander handle
- *
- * Creating expander object, stored in ioc->sas_expander_list.
- *
- * Return 0 for success, else error.
- */
-static int
-_scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _sas_node *sas_expander;
- Mpi2ConfigReply_t mpi_reply;
- Mpi2ExpanderPage0_t expander_pg0;
- Mpi2ExpanderPage1_t expander_pg1;
- Mpi2SasEnclosurePage0_t enclosure_pg0;
- u32 ioc_status;
- u16 parent_handle;
- __le64 sas_address, sas_address_parent = 0;
- int i;
- unsigned long flags;
- struct _sas_port *mpt2sas_port = NULL;
- int rc = 0;
-
- if (!handle)
- return -1;
-
- if (ioc->shost_recovery || ioc->pci_error_recovery)
- return -1;
-
- if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
- MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
-
- /* handle out of order topology events */
- parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
- if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent)
- != 0) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
- if (sas_address_parent != ioc->sas_hba.sas_address) {
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
- sas_address_parent);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- if (!sas_expander) {
- rc = _scsih_expander_add(ioc, parent_handle);
- if (rc != 0)
- return rc;
- }
- }
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_address = le64_to_cpu(expander_pg0.SASAddress);
- sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
- sas_address);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-
- if (sas_expander)
- return 0;
-
- sas_expander = kzalloc(sizeof(struct _sas_node),
- GFP_KERNEL);
- if (!sas_expander) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
-
- sas_expander->handle = handle;
- sas_expander->num_phys = expander_pg0.NumPhys;
- sas_expander->sas_address_parent = sas_address_parent;
- sas_expander->sas_address = sas_address;
-
- printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x),"
- " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name,
- handle, parent_handle, (unsigned long long)
- sas_expander->sas_address, sas_expander->num_phys);
-
- if (!sas_expander->num_phys)
- goto out_fail;
- sas_expander->phy = kcalloc(sas_expander->num_phys,
- sizeof(struct _sas_phy), GFP_KERNEL);
- if (!sas_expander->phy) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -1;
- goto out_fail;
- }
-
- INIT_LIST_HEAD(&sas_expander->sas_port_list);
- mpt2sas_port = mpt2sas_transport_port_add(ioc, handle,
- sas_address_parent);
- if (!mpt2sas_port) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -1;
- goto out_fail;
- }
- sas_expander->parent_dev = &mpt2sas_port->rphy->dev;
-
- for (i = 0 ; i < sas_expander->num_phys ; i++) {
- if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply,
- &expander_pg1, i, handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -1;
- goto out_fail;
- }
- sas_expander->phy[i].handle = handle;
- sas_expander->phy[i].phy_id = i;
-
- if ((mpt2sas_transport_add_expander_phy(ioc,
- &sas_expander->phy[i], expander_pg1,
- sas_expander->parent_dev))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -1;
- goto out_fail;
- }
- }
-
- if (sas_expander->enclosure_handle) {
- if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply,
- &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
- sas_expander->enclosure_handle))) {
- sas_expander->enclosure_logical_id =
- le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
- }
- }
-
- _scsih_expander_node_add(ioc, sas_expander);
- return 0;
-
- out_fail:
-
- if (mpt2sas_port)
- mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
- sas_address_parent);
- kfree(sas_expander);
- return rc;
-}
-
-/**
- * _scsih_done - scsih callback handler.
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- *
- * Callback handler when sending internal generated message frames.
- * The callback index passed is `ioc->scsih_cb_idx`
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-static u8
-_scsih_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
-{
- MPI2DefaultReply_t *mpi_reply;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- if (ioc->scsih_cmds.status == MPT2_CMD_NOT_USED)
- return 1;
- if (ioc->scsih_cmds.smid != smid)
- return 1;
- ioc->scsih_cmds.status |= MPT2_CMD_COMPLETE;
- if (mpi_reply) {
- memcpy(ioc->scsih_cmds.reply, mpi_reply,
- mpi_reply->MsgLength*4);
- ioc->scsih_cmds.status |= MPT2_CMD_REPLY_VALID;
- }
- ioc->scsih_cmds.status &= ~MPT2_CMD_PENDING;
- complete(&ioc->scsih_cmds.done);
- return 1;
-}
-
-/**
- * mpt2sas_expander_remove - removing expander object
- * @ioc: per adapter object
- * @sas_address: expander sas_address
- *
- * Return nothing.
- */
-void
-mpt2sas_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address)
-{
- struct _sas_node *sas_expander;
- unsigned long flags;
-
- if (ioc->shost_recovery)
- return;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
- sas_address);
- if (!sas_expander) {
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- return;
- }
- list_del(&sas_expander->list);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- _scsih_expander_node_remove(ioc, sas_expander);
-}
-
-/**
- * _scsih_check_access_status - check access flags
- * @ioc: per adapter object
- * @sas_address: sas address
- * @handle: sas device handle
- * @access_flags: errors returned during discovery of the device
- *
- * Return 0 for success, else failure
- */
-static u8
-_scsih_check_access_status(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u16 handle, u8 access_status)
-{
- u8 rc = 1;
- char *desc = NULL;
-
- switch (access_status) {
- case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS:
- case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION:
- rc = 0;
- break;
- case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED:
- desc = "sata capability failed";
- break;
- case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT:
- desc = "sata affiliation conflict";
- break;
- case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE:
- desc = "route not addressable";
- break;
- case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE:
- desc = "smp error not addressable";
- break;
- case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED:
- desc = "device blocked";
- break;
- case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE:
- case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX:
- desc = "sata initialization failed";
- break;
- default:
- desc = "unknown";
- break;
- }
-
- if (!rc)
- return 0;
-
- printk(MPT2SAS_ERR_FMT "discovery errors(%s): sas_address(0x%016llx), "
- "handle(0x%04x)\n", ioc->name, desc,
- (unsigned long long)sas_address, handle);
- return rc;
-}
-
-static void
-_scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasDevicePage0_t sas_device_pg0;
- struct _sas_device *sas_device;
- u32 ioc_status;
- unsigned long flags;
- u64 sas_address;
- struct scsi_target *starget;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- u32 device_info;
-
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle)))
- return;
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
- return;
-
- /* check if this is end device */
- device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
- if (!(_scsih_is_end_device(device_info)))
- return;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- sas_address);
-
- if (!sas_device) {
- printk(MPT2SAS_ERR_FMT "device is not present "
- "handle(0x%04x), no sas_device!!!\n", ioc->name, handle);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- return;
- }
-
- if (unlikely(sas_device->handle != handle)) {
- starget = sas_device->starget;
- sas_target_priv_data = starget->hostdata;
- starget_printk(KERN_INFO, starget, "handle changed from(0x%04x)"
- " to (0x%04x)!!!\n", sas_device->handle, handle);
- sas_target_priv_data->handle = handle;
- sas_device->handle = handle;
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- /* check if device is present */
- if (!(le16_to_cpu(sas_device_pg0.Flags) &
- MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
- printk(MPT2SAS_ERR_FMT "device is not present "
- "handle(0x%04x), flags!!!\n", ioc->name, handle);
- return;
- }
-
- /* check if there were any issues with discovery */
- if (_scsih_check_access_status(ioc, sas_address, handle,
- sas_device_pg0.AccessStatus))
- return;
- _scsih_ublock_io_device(ioc, handle);
-
-}
-
-/**
- * _scsih_add_device - creating sas device object
- * @ioc: per adapter object
- * @handle: sas device handle
- * @phy_num: phy number end device attached to
- * @is_pd: is this hidden raid component
- *
- * Creating end device object, stored in ioc->sas_device_list.
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
-{
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasDevicePage0_t sas_device_pg0;
- Mpi2SasEnclosurePage0_t enclosure_pg0;
- struct _sas_device *sas_device;
- u32 ioc_status;
- __le64 sas_address;
- u32 device_info;
- unsigned long flags;
-
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
-
- sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
-
- /* check if device is present */
- if (!(le16_to_cpu(sas_device_pg0.Flags) &
- MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- printk(MPT2SAS_ERR_FMT "Flags = 0x%04x\n",
- ioc->name, le16_to_cpu(sas_device_pg0.Flags));
- return -1;
- }
-
- /* check if there were any issues with discovery */
- if (_scsih_check_access_status(ioc, sas_address, handle,
- sas_device_pg0.AccessStatus))
- return -1;
-
- /* check if this is end device */
- device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
- if (!(_scsih_is_end_device(device_info))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
-
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- sas_address);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (sas_device)
- return 0;
-
- sas_device = kzalloc(sizeof(struct _sas_device),
- GFP_KERNEL);
- if (!sas_device) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
-
- sas_device->handle = handle;
- if (_scsih_get_sas_address(ioc, le16_to_cpu
- (sas_device_pg0.ParentDevHandle),
- &sas_device->sas_address_parent) != 0)
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- sas_device->enclosure_handle =
- le16_to_cpu(sas_device_pg0.EnclosureHandle);
- sas_device->slot =
- le16_to_cpu(sas_device_pg0.Slot);
- sas_device->device_info = device_info;
- sas_device->sas_address = sas_address;
- sas_device->phy = sas_device_pg0.PhyNum;
-
- /* get enclosure_logical_id */
- if (sas_device->enclosure_handle && !(mpt2sas_config_get_enclosure_pg0(
- ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
- sas_device->enclosure_handle)))
- sas_device->enclosure_logical_id =
- le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
-
- /* get device name */
- sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName);
-
- if (ioc->wait_for_port_enable_to_complete)
- _scsih_sas_device_init_add(ioc, sas_device);
- else
- _scsih_sas_device_add(ioc, sas_device);
-
- return 0;
-}
-
-/**
- * _scsih_remove_device - removing sas device object
- * @ioc: per adapter object
- * @sas_device_delete: the sas_device object
- *
- * Return nothing.
- */
-static void
-_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_device *sas_device)
-{
- struct _sas_device sas_device_backup;
- struct MPT2SAS_TARGET *sas_target_priv_data;
-
- if (!sas_device)
- return;
-
- memcpy(&sas_device_backup, sas_device, sizeof(struct _sas_device));
- _scsih_sas_device_remove(ioc, sas_device);
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: "
- "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
- sas_device_backup.handle, (unsigned long long)
- sas_device_backup.sas_address));
-
- if (sas_device_backup.starget && sas_device_backup.starget->hostdata) {
- sas_target_priv_data = sas_device_backup.starget->hostdata;
- sas_target_priv_data->deleted = 1;
- }
-
- _scsih_ublock_io_device(ioc, sas_device_backup.handle);
-
- if (!ioc->hide_drives)
- mpt2sas_transport_port_remove(ioc,
- sas_device_backup.sas_address,
- sas_device_backup.sas_address_parent);
-
- printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr"
- "(0x%016llx)\n", ioc->name, sas_device_backup.handle,
- (unsigned long long) sas_device_backup.sas_address);
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: "
- "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
- sas_device_backup.handle, (unsigned long long)
- sas_device_backup.sas_address));
-}
-
-/**
- * mpt2sas_device_remove - removing device object
- * @ioc: per adapter object
- * @sas_address: expander sas_address
- *
- * Return nothing.
- */
-void
-mpt2sas_device_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address)
-{
- struct _sas_device *sas_device;
- unsigned long flags;
-
- if (ioc->shost_recovery)
- return;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- sas_address);
- if (!sas_device) {
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- return;
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- _scsih_remove_device(ioc, sas_device);
-}
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _scsih_sas_topology_change_event_debug - debug for topology event
- * @ioc: per adapter object
- * @event_data: event data payload
- * Context: user.
- */
-static void
-_scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataSasTopologyChangeList_t *event_data)
-{
- int i;
- u16 handle;
- u16 reason_code;
- u8 phy_number;
- char *status_str = NULL;
- u8 link_rate, prev_link_rate;
-
- switch (event_data->ExpStatus) {
- case MPI2_EVENT_SAS_TOPO_ES_ADDED:
- status_str = "add";
- break;
- case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING:
- status_str = "remove";
- break;
- case MPI2_EVENT_SAS_TOPO_ES_RESPONDING:
- case 0:
- status_str = "responding";
- break;
- case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
- status_str = "remove delay";
- break;
- default:
- status_str = "unknown status";
- break;
- }
- printk(MPT2SAS_INFO_FMT "sas topology change: (%s)\n",
- ioc->name, status_str);
- printk(KERN_INFO "\thandle(0x%04x), enclosure_handle(0x%04x) "
- "start_phy(%02d), count(%d)\n",
- le16_to_cpu(event_data->ExpanderDevHandle),
- le16_to_cpu(event_data->EnclosureHandle),
- event_data->StartPhyNum, event_data->NumEntries);
- for (i = 0; i < event_data->NumEntries; i++) {
- handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
- if (!handle)
- continue;
- phy_number = event_data->StartPhyNum + i;
- reason_code = event_data->PHY[i].PhyStatus &
- MPI2_EVENT_SAS_TOPO_RC_MASK;
- switch (reason_code) {
- case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
- status_str = "target add";
- break;
- case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
- status_str = "target remove";
- break;
- case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
- status_str = "delay target remove";
- break;
- case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
- status_str = "link rate change";
- break;
- case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
- status_str = "target responding";
- break;
- default:
- status_str = "unknown";
- break;
- }
- link_rate = event_data->PHY[i].LinkRate >> 4;
- prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
- printk(KERN_INFO "\tphy(%02d), attached_handle(0x%04x): %s:"
- " link rate: new(0x%02x), old(0x%02x)\n", phy_number,
- handle, status_str, link_rate, prev_link_rate);
-
- }
-}
-#endif
-
-/**
- * _scsih_sas_topology_change_event - handle topology changes
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- */
-static void
-_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- int i;
- u16 parent_handle, handle;
- u16 reason_code;
- u8 phy_number, max_phys;
- struct _sas_node *sas_expander;
- struct _sas_device *sas_device;
- u64 sas_address;
- unsigned long flags;
- u8 link_rate, prev_link_rate;
- Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data;
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
- _scsih_sas_topology_change_event_debug(ioc, event_data);
-#endif
-
- if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery)
- return;
-
- if (!ioc->sas_hba.num_phys)
- _scsih_sas_host_add(ioc);
- else
- _scsih_sas_host_refresh(ioc);
-
- if (fw_event->ignore) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "ignoring expander "
- "event\n", ioc->name));
- return;
- }
-
- parent_handle = le16_to_cpu(event_data->ExpanderDevHandle);
-
- /* handle expander add */
- if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED)
- if (_scsih_expander_add(ioc, parent_handle) != 0)
- return;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
- parent_handle);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- if (sas_expander) {
- sas_address = sas_expander->sas_address;
- max_phys = sas_expander->num_phys;
- } else if (parent_handle < ioc->sas_hba.num_phys) {
- sas_address = ioc->sas_hba.sas_address;
- max_phys = ioc->sas_hba.num_phys;
- } else
- return;
-
- /* handle siblings events */
- for (i = 0; i < event_data->NumEntries; i++) {
- if (fw_event->ignore) {
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "ignoring "
- "expander event\n", ioc->name));
- return;
- }
- if (ioc->shost_recovery || ioc->remove_host ||
- ioc->pci_error_recovery)
- return;
- phy_number = event_data->StartPhyNum + i;
- if (phy_number >= max_phys)
- continue;
- reason_code = event_data->PHY[i].PhyStatus &
- MPI2_EVENT_SAS_TOPO_RC_MASK;
- if ((event_data->PHY[i].PhyStatus &
- MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code !=
- MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING))
- continue;
- handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
- if (!handle)
- continue;
- link_rate = event_data->PHY[i].LinkRate >> 4;
- prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
- switch (reason_code) {
- case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
-
- if (link_rate == prev_link_rate)
- break;
-
- mpt2sas_transport_update_links(ioc, sas_address,
- handle, phy_number, link_rate);
-
- if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5)
- break;
-
- _scsih_check_device(ioc, handle);
- break;
- case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
-
- mpt2sas_transport_update_links(ioc, sas_address,
- handle, phy_number, link_rate);
-
- _scsih_add_device(ioc, handle, phy_number, 0);
- break;
- case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc,
- handle);
- if (!sas_device) {
- spin_unlock_irqrestore(&ioc->sas_device_lock,
- flags);
- break;
- }
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- _scsih_remove_device(ioc, sas_device);
- break;
- }
- }
-
- /* handle expander removal */
- if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING &&
- sas_expander)
- mpt2sas_expander_remove(ioc, sas_address);
-
-}
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _scsih_sas_device_status_change_event_debug - debug for device event
- * @event_data: event data payload
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataSasDeviceStatusChange_t *event_data)
-{
- char *reason_str = NULL;
-
- switch (event_data->ReasonCode) {
- case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
- reason_str = "smart data";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED:
- reason_str = "unsupported device discovered";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
- reason_str = "internal device reset";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL:
- reason_str = "internal task abort";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
- reason_str = "internal task abort set";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
- reason_str = "internal clear task set";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL:
- reason_str = "internal query task";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE:
- reason_str = "sata init failure";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
- reason_str = "internal device reset complete";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL:
- reason_str = "internal task abort complete";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION:
- reason_str = "internal async notification";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY:
- reason_str = "expander reduced functionality";
- break;
- case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY:
- reason_str = "expander reduced functionality complete";
- break;
- default:
- reason_str = "unknown reason";
- break;
- }
- printk(MPT2SAS_INFO_FMT "device status change: (%s)\n"
- "\thandle(0x%04x), sas address(0x%016llx)", ioc->name,
- reason_str, le16_to_cpu(event_data->DevHandle),
- (unsigned long long)le64_to_cpu(event_data->SASAddress));
- if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA)
- printk(MPT2SAS_INFO_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name,
- event_data->ASC, event_data->ASCQ);
- printk(KERN_INFO "\n");
-}
-#endif
-
-/**
- * _scsih_sas_device_status_change_event - handle device status change
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- struct MPT2SAS_TARGET *target_priv_data;
- struct _sas_device *sas_device;
- __le64 sas_address;
- unsigned long flags;
- Mpi2EventDataSasDeviceStatusChange_t *event_data =
- fw_event->event_data;
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
- _scsih_sas_device_status_change_event_debug(ioc,
- event_data);
-#endif
-
- /* In MPI Revision K (0xC), the internal device reset complete was
- * implemented, so avoid setting tm_busy flag for older firmware.
- */
- if ((ioc->facts.HeaderVersion >> 8) < 0xC)
- return;
-
- if (event_data->ReasonCode !=
- MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
- event_data->ReasonCode !=
- MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)
- return;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_address = le64_to_cpu(event_data->SASAddress);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- sas_address);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (!sas_device || !sas_device->starget)
- return;
-
- target_priv_data = sas_device->starget->hostdata;
- if (!target_priv_data)
- return;
-
- if (event_data->ReasonCode ==
- MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET)
- target_priv_data->tm_busy = 1;
- else
- target_priv_data->tm_busy = 0;
-}
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure event
- * @ioc: per adapter object
- * @event_data: event data payload
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataSasEnclDevStatusChange_t *event_data)
-{
- char *reason_str = NULL;
-
- switch (event_data->ReasonCode) {
- case MPI2_EVENT_SAS_ENCL_RC_ADDED:
- reason_str = "enclosure add";
- break;
- case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING:
- reason_str = "enclosure remove";
- break;
- default:
- reason_str = "unknown reason";
- break;
- }
-
- printk(MPT2SAS_INFO_FMT "enclosure status change: (%s)\n"
- "\thandle(0x%04x), enclosure logical id(0x%016llx)"
- " number slots(%d)\n", ioc->name, reason_str,
- le16_to_cpu(event_data->EnclosureHandle),
- (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID),
- le16_to_cpu(event_data->StartSlot));
-}
-#endif
-
-/**
- * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
- _scsih_sas_enclosure_dev_status_change_event_debug(ioc,
- fw_event->event_data);
-#endif
-}
-
-/**
- * _scsih_sas_broadcast_primative_event - handle broadcast events
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- struct scsi_cmnd *scmd;
- struct scsi_device *sdev;
- u16 smid, handle;
- u32 lun;
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- u32 termination_count;
- u32 query_count;
- Mpi2SCSITaskManagementReply_t *mpi_reply;
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data;
-#endif
- u16 ioc_status;
- unsigned long flags;
- int r;
-
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "broadcast primitive: "
- "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum,
- event_data->PortWidth));
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
- __func__));
-
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- ioc->broadcast_aen_busy = 0;
- termination_count = 0;
- query_count = 0;
- mpi_reply = ioc->tm_cmds.reply;
- for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
- scmd = _scsih_scsi_lookup_get(ioc, smid);
- if (!scmd)
- continue;
- sdev = scmd->device;
- sas_device_priv_data = sdev->hostdata;
- if (!sas_device_priv_data || !sas_device_priv_data->sas_target)
- continue;
- /* skip hidden raid components */
- if (sas_device_priv_data->sas_target->flags &
- MPT_TARGET_FLAGS_RAID_COMPONENT)
- continue;
- /* skip volumes */
- if (sas_device_priv_data->sas_target->flags &
- MPT_TARGET_FLAGS_VOLUME)
- continue;
-
- handle = sas_device_priv_data->sas_target->handle;
- lun = sas_device_priv_data->lun;
- query_count++;
-
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun,
- MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, NULL);
- ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
- ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
- & MPI2_IOCSTATUS_MASK;
- if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) &&
- (mpi_reply->ResponseCode ==
- MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
- mpi_reply->ResponseCode ==
- MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC)) {
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- continue;
- }
- r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id,
- sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30,
- scmd);
- if (r == FAILED)
- sdev_printk(KERN_WARNING, sdev, "task abort: FAILED "
- "scmd(%p)\n", scmd);
- termination_count += le32_to_cpu(mpi_reply->TerminationCount);
- spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
- }
- spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT
- "%s - exit, query_count = %d termination_count = %d\n",
- ioc->name, __func__, query_count, termination_count));
-}
-
-/**
- * _scsih_sas_discovery_event - handle discovery events
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- Mpi2EventDataSasDiscovery_t *event_data = fw_event->event_data;
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) {
- printk(MPT2SAS_INFO_FMT "discovery event: (%s)", ioc->name,
- (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ?
- "start" : "stop");
- if (event_data->DiscoveryStatus)
- printk("discovery_status(0x%08x)",
- le32_to_cpu(event_data->DiscoveryStatus));
- printk("\n");
- }
-#endif
-
- if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED &&
- !ioc->sas_hba.num_phys)
- _scsih_sas_host_add(ioc);
-}
-
-/**
- * _scsih_reprobe_lun - reprobing lun
- * @sdev: scsi device struct
- * @no_uld_attach: sdev->no_uld_attach flag setting
- *
- **/
-static void
-_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach)
-{
- int rc;
-
- sdev->no_uld_attach = no_uld_attach ? 1 : 0;
- sdev_printk(KERN_INFO, sdev, "%s raid component\n",
- sdev->no_uld_attach ? "hidding" : "exposing");
- rc = scsi_device_reprobe(sdev);
-}
-
-/**
- * _scsih_reprobe_target - reprobing target
- * @starget: scsi target struct
- * @no_uld_attach: sdev->no_uld_attach flag setting
- *
- * Note: no_uld_attach flag determines whether the disk device is attached
- * to block layer. A value of `1` means to not attach.
- **/
-static void
-_scsih_reprobe_target(struct scsi_target *starget, int no_uld_attach)
-{
- struct MPT2SAS_TARGET *sas_target_priv_data = starget->hostdata;
-
- if (no_uld_attach)
- sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
- else
- sas_target_priv_data->flags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT;
-
- starget_for_each_device(starget, no_uld_attach ? (void *)1 : NULL,
- _scsih_reprobe_lun);
-}
-/**
- * _scsih_sas_volume_add - add new volume
- * @ioc: per adapter object
- * @element: IR config element data
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventIrConfigElement_t *element)
-{
- struct _raid_device *raid_device;
- unsigned long flags;
- u64 wwid;
- u16 handle = le16_to_cpu(element->VolDevHandle);
- int rc;
-
- mpt2sas_config_get_volume_wwid(ioc, handle, &wwid);
- if (!wwid) {
- printk(MPT2SAS_ERR_FMT
- "failure at %s:%d/%s()!\n", ioc->name,
- __FILE__, __LINE__, __func__);
- return;
- }
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-
- if (raid_device)
- return;
-
- raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
- if (!raid_device) {
- printk(MPT2SAS_ERR_FMT
- "failure at %s:%d/%s()!\n", ioc->name,
- __FILE__, __LINE__, __func__);
- return;
- }
-
- raid_device->id = ioc->sas_id++;
- raid_device->channel = RAID_CHANNEL;
- raid_device->handle = handle;
- raid_device->wwid = wwid;
- _scsih_raid_device_add(ioc, raid_device);
- if (!ioc->wait_for_port_enable_to_complete) {
- rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
- raid_device->id, 0);
- if (rc)
- _scsih_raid_device_remove(ioc, raid_device);
- } else
- _scsih_determine_boot_device(ioc, raid_device, 1);
-}
-
-/**
- * _scsih_sas_volume_delete - delete volume
- * @ioc: per adapter object
- * @handle: volume device handle
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _raid_device *raid_device;
- unsigned long flags;
- struct MPT2SAS_TARGET *sas_target_priv_data;
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- if (!raid_device)
- return;
- if (raid_device->starget) {
- sas_target_priv_data = raid_device->starget->hostdata;
- sas_target_priv_data->deleted = 1;
- scsi_remove_target(&raid_device->starget->dev);
- }
- printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid"
- "(0x%016llx)\n", ioc->name, raid_device->handle,
- (unsigned long long) raid_device->wwid);
- _scsih_raid_device_remove(ioc, raid_device);
-}
-
-/**
- * _scsih_sas_pd_expose - expose pd component to /dev/sdX
- * @ioc: per adapter object
- * @element: IR config element data
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventIrConfigElement_t *element)
-{
- struct _sas_device *sas_device;
- unsigned long flags;
- u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (!sas_device)
- return;
-
- /* exposing raid component */
- sas_device->volume_handle = 0;
- sas_device->volume_wwid = 0;
- clear_bit(handle, ioc->pd_handles);
- _scsih_reprobe_target(sas_device->starget, 0);
-}
-
-/**
- * _scsih_sas_pd_hide - hide pd component from /dev/sdX
- * @ioc: per adapter object
- * @element: IR config element data
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventIrConfigElement_t *element)
-{
- struct _sas_device *sas_device;
- unsigned long flags;
- u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (!sas_device)
- return;
-
- /* hiding raid component */
- mpt2sas_config_get_volume_handle(ioc, handle,
- &sas_device->volume_handle);
- mpt2sas_config_get_volume_wwid(ioc, sas_device->volume_handle,
- &sas_device->volume_wwid);
- set_bit(handle, ioc->pd_handles);
- _scsih_reprobe_target(sas_device->starget, 1);
-
-}
-
-/**
- * _scsih_sas_pd_delete - delete pd component
- * @ioc: per adapter object
- * @element: IR config element data
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventIrConfigElement_t *element)
-{
- struct _sas_device *sas_device;
- unsigned long flags;
- u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (!sas_device)
- return;
- _scsih_remove_device(ioc, sas_device);
-}
-
-/**
- * _scsih_sas_pd_add - remove pd component
- * @ioc: per adapter object
- * @element: IR config element data
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventIrConfigElement_t *element)
-{
- struct _sas_device *sas_device;
- unsigned long flags;
- u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasDevicePage0_t sas_device_pg0;
- u32 ioc_status;
- u64 sas_address;
- u16 parent_handle;
-
- set_bit(handle, ioc->pd_handles);
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device)
- return;
-
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
- if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
- mpt2sas_transport_update_links(ioc, sas_address, handle,
- sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
-
- _scsih_add_device(ioc, handle, 0, 1);
-}
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events
- * @ioc: per adapter object
- * @event_data: event data payload
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataIrConfigChangeList_t *event_data)
-{
- Mpi2EventIrConfigElement_t *element;
- u8 element_type;
- int i;
- char *reason_str = NULL, *element_str = NULL;
-
- element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
-
- printk(MPT2SAS_INFO_FMT "raid config change: (%s), elements(%d)\n",
- ioc->name, (le32_to_cpu(event_data->Flags) &
- MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ?
- "foreign" : "native", event_data->NumElements);
- for (i = 0; i < event_data->NumElements; i++, element++) {
- switch (element->ReasonCode) {
- case MPI2_EVENT_IR_CHANGE_RC_ADDED:
- reason_str = "add";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
- reason_str = "remove";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE:
- reason_str = "no change";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_HIDE:
- reason_str = "hide";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
- reason_str = "unhide";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
- reason_str = "volume_created";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
- reason_str = "volume_deleted";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
- reason_str = "pd_created";
- break;
- case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
- reason_str = "pd_deleted";
- break;
- default:
- reason_str = "unknown reason";
- break;
- }
- element_type = le16_to_cpu(element->ElementFlags) &
- MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK;
- switch (element_type) {
- case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT:
- element_str = "volume";
- break;
- case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT:
- element_str = "phys disk";
- break;
- case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT:
- element_str = "hot spare";
- break;
- default:
- element_str = "unknown element";
- break;
- }
- printk(KERN_INFO "\t(%s:%s), vol handle(0x%04x), "
- "pd handle(0x%04x), pd num(0x%02x)\n", element_str,
- reason_str, le16_to_cpu(element->VolDevHandle),
- le16_to_cpu(element->PhysDiskDevHandle),
- element->PhysDiskNum);
- }
-}
-#endif
-
-/**
- * _scsih_sas_ir_config_change_event - handle ir configuration change events
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- Mpi2EventIrConfigElement_t *element;
- int i;
- u8 foreign_config;
- Mpi2EventDataIrConfigChangeList_t *event_data = fw_event->event_data;
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
- && !ioc->hide_ir_msg)
- _scsih_sas_ir_config_change_event_debug(ioc, event_data);
-
-#endif
- foreign_config = (le32_to_cpu(event_data->Flags) &
- MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
-
- element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
- for (i = 0; i < event_data->NumElements; i++, element++) {
-
- switch (element->ReasonCode) {
- case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
- case MPI2_EVENT_IR_CHANGE_RC_ADDED:
- if (!foreign_config)
- _scsih_sas_volume_add(ioc, element);
- break;
- case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
- case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
- if (!foreign_config)
- _scsih_sas_volume_delete(ioc,
- le16_to_cpu(element->VolDevHandle));
- break;
- case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
- if (!ioc->is_warpdrive)
- _scsih_sas_pd_hide(ioc, element);
- break;
- case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
- if (!ioc->is_warpdrive)
- _scsih_sas_pd_expose(ioc, element);
- break;
- case MPI2_EVENT_IR_CHANGE_RC_HIDE:
- if (!ioc->is_warpdrive)
- _scsih_sas_pd_add(ioc, element);
- break;
- case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
- if (!ioc->is_warpdrive)
- _scsih_sas_pd_delete(ioc, element);
- break;
- }
- }
-}
-
-/**
- * _scsih_sas_ir_volume_event - IR volume event
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- u64 wwid;
- unsigned long flags;
- struct _raid_device *raid_device;
- u16 handle;
- u32 state;
- int rc;
- Mpi2EventDataIrVolume_t *event_data = fw_event->event_data;
-
- if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
- return;
-
- handle = le16_to_cpu(event_data->VolDevHandle);
- state = le32_to_cpu(event_data->NewValue);
- if (!ioc->hide_ir_msg)
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), "
- "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
- le32_to_cpu(event_data->PreviousValue), state));
-
- switch (state) {
- case MPI2_RAID_VOL_STATE_MISSING:
- case MPI2_RAID_VOL_STATE_FAILED:
- _scsih_sas_volume_delete(ioc, handle);
- break;
-
- case MPI2_RAID_VOL_STATE_ONLINE:
- case MPI2_RAID_VOL_STATE_DEGRADED:
- case MPI2_RAID_VOL_STATE_OPTIMAL:
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-
- if (raid_device)
- break;
-
- mpt2sas_config_get_volume_wwid(ioc, handle, &wwid);
- if (!wwid) {
- printk(MPT2SAS_ERR_FMT
- "failure at %s:%d/%s()!\n", ioc->name,
- __FILE__, __LINE__, __func__);
- break;
- }
-
- raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
- if (!raid_device) {
- printk(MPT2SAS_ERR_FMT
- "failure at %s:%d/%s()!\n", ioc->name,
- __FILE__, __LINE__, __func__);
- break;
- }
-
- raid_device->id = ioc->sas_id++;
- raid_device->channel = RAID_CHANNEL;
- raid_device->handle = handle;
- raid_device->wwid = wwid;
- _scsih_raid_device_add(ioc, raid_device);
- rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
- raid_device->id, 0);
- if (rc)
- _scsih_raid_device_remove(ioc, raid_device);
- break;
-
- case MPI2_RAID_VOL_STATE_INITIALIZING:
- default:
- break;
- }
-}
-
-/**
- * _scsih_sas_ir_physical_disk_event - PD event
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- u16 handle, parent_handle;
- u32 state;
- struct _sas_device *sas_device;
- unsigned long flags;
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasDevicePage0_t sas_device_pg0;
- u32 ioc_status;
- Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;
- u64 sas_address;
-
- if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
- return;
-
- handle = le16_to_cpu(event_data->PhysDiskDevHandle);
- state = le32_to_cpu(event_data->NewValue);
-
- if (!ioc->hide_ir_msg)
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), "
- "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
- le32_to_cpu(event_data->PreviousValue), state));
-
- switch (state) {
- case MPI2_RAID_PD_STATE_ONLINE:
- case MPI2_RAID_PD_STATE_DEGRADED:
- case MPI2_RAID_PD_STATE_REBUILDING:
- case MPI2_RAID_PD_STATE_OPTIMAL:
- case MPI2_RAID_PD_STATE_HOT_SPARE:
-
- if (!ioc->is_warpdrive)
- set_bit(handle, ioc->pd_handles);
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (sas_device)
- return;
-
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
- &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
- handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return;
- }
-
- parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
- if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
- mpt2sas_transport_update_links(ioc, sas_address, handle,
- sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
-
- _scsih_add_device(ioc, handle, 0, 1);
-
- break;
-
- case MPI2_RAID_PD_STATE_OFFLINE:
- case MPI2_RAID_PD_STATE_NOT_CONFIGURED:
- case MPI2_RAID_PD_STATE_NOT_COMPATIBLE:
- default:
- break;
- }
-}
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
-/**
- * _scsih_sas_ir_operation_status_event_debug - debug for IR op event
- * @ioc: per adapter object
- * @event_data: event data payload
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc,
- Mpi2EventDataIrOperationStatus_t *event_data)
-{
- char *reason_str = NULL;
-
- switch (event_data->RAIDOperation) {
- case MPI2_EVENT_IR_RAIDOP_RESYNC:
- reason_str = "resync";
- break;
- case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION:
- reason_str = "online capacity expansion";
- break;
- case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
- reason_str = "consistency check";
- break;
- case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT:
- reason_str = "background init";
- break;
- case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT:
- reason_str = "make data consistent";
- break;
- }
-
- if (!reason_str)
- return;
-
- printk(MPT2SAS_INFO_FMT "raid operational status: (%s)"
- "\thandle(0x%04x), percent complete(%d)\n",
- ioc->name, reason_str,
- le16_to_cpu(event_data->VolDevHandle),
- event_data->PercentComplete);
-}
-#endif
-
-/**
- * _scsih_sas_ir_operation_status_event - handle RAID operation events
- * @ioc: per adapter object
- * @fw_event: The fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc,
- struct fw_event_work *fw_event)
-{
- Mpi2EventDataIrOperationStatus_t *event_data = fw_event->event_data;
- static struct _raid_device *raid_device;
- unsigned long flags;
- u16 handle;
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
- && !ioc->hide_ir_msg)
- _scsih_sas_ir_operation_status_event_debug(ioc,
- event_data);
-#endif
-
- /* code added for raid transport support */
- if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) {
-
- handle = le16_to_cpu(event_data->VolDevHandle);
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-
- if (!raid_device)
- return;
-
- if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC)
- raid_device->percent_complete =
- event_data->PercentComplete;
- }
-}
-
-/**
- * _scsih_prep_device_scan - initialize parameters prior to device scan
- * @ioc: per adapter object
- *
- * Set the deleted flag prior to device scan. If the device is found during
- * the scan, then we clear the deleted flag.
- */
-static void
-_scsih_prep_device_scan(struct MPT2SAS_ADAPTER *ioc)
-{
- struct MPT2SAS_DEVICE *sas_device_priv_data;
- struct scsi_device *sdev;
-
- shost_for_each_device(sdev, ioc->shost) {
- sas_device_priv_data = sdev->hostdata;
- if (sas_device_priv_data && sas_device_priv_data->sas_target)
- sas_device_priv_data->sas_target->deleted = 1;
- }
-}
-
-/**
- * _scsih_mark_responding_sas_device - mark a sas_devices as responding
- * @ioc: per adapter object
- * @sas_address: sas address
- * @slot: enclosure slot id
- * @handle: device handle
- *
- * After host reset, find out whether devices are still responding.
- * Used in _scsi_remove_unresponsive_sas_devices.
- *
- * Return nothing.
- */
-static void
-_scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u16 slot, u16 handle)
-{
- struct MPT2SAS_TARGET *sas_target_priv_data = NULL;
- struct scsi_target *starget;
- struct _sas_device *sas_device;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
- if (sas_device->sas_address == sas_address &&
- sas_device->slot == slot) {
- sas_device->responding = 1;
- starget = sas_device->starget;
- if (starget && starget->hostdata) {
- sas_target_priv_data = starget->hostdata;
- sas_target_priv_data->tm_busy = 0;
- sas_target_priv_data->deleted = 0;
- } else
- sas_target_priv_data = NULL;
- if (starget)
- starget_printk(KERN_INFO, starget,
- "handle(0x%04x), sas_addr(0x%016llx), "
- "enclosure logical id(0x%016llx), "
- "slot(%d)\n", handle,
- (unsigned long long)sas_device->sas_address,
- (unsigned long long)
- sas_device->enclosure_logical_id,
- sas_device->slot);
- if (sas_device->handle == handle)
- goto out;
- printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
- sas_device->handle);
- sas_device->handle = handle;
- if (sas_target_priv_data)
- sas_target_priv_data->handle = handle;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-}
-
-/**
- * _scsih_search_responding_sas_devices -
- * @ioc: per adapter object
- *
- * After host reset, find out whether devices are still responding.
- * If not remove.
- *
- * Return nothing.
- */
-static void
-_scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
-{
- Mpi2SasDevicePage0_t sas_device_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u16 ioc_status;
- __le64 sas_address;
- u16 handle;
- u32 device_info;
- u16 slot;
-
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
-
- if (list_empty(&ioc->sas_device_list))
- return;
-
- handle = 0xFFFF;
- while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
- &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE,
- handle))) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
- handle = le16_to_cpu(sas_device_pg0.DevHandle);
- device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
- if (!(_scsih_is_end_device(device_info)))
- continue;
- sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
- slot = le16_to_cpu(sas_device_pg0.Slot);
- _scsih_mark_responding_sas_device(ioc, sas_address, slot,
- handle);
- }
-}
-
-/**
- * _scsih_mark_responding_raid_device - mark a raid_device as responding
- * @ioc: per adapter object
- * @wwid: world wide identifier for raid volume
- * @handle: device handle
- *
- * After host reset, find out whether devices are still responding.
- * Used in _scsi_remove_unresponsive_raid_devices.
- *
- * Return nothing.
- */
-static void
-_scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid,
- u16 handle)
-{
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct scsi_target *starget;
- struct _raid_device *raid_device;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->raid_device_lock, flags);
- list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
- if (raid_device->wwid == wwid && raid_device->starget) {
- starget = raid_device->starget;
- if (starget && starget->hostdata) {
- sas_target_priv_data = starget->hostdata;
- sas_target_priv_data->deleted = 0;
- } else
- sas_target_priv_data = NULL;
- raid_device->responding = 1;
- starget_printk(KERN_INFO, raid_device->starget,
- "handle(0x%04x), wwid(0x%016llx)\n", handle,
- (unsigned long long)raid_device->wwid);
- /*
- * WARPDRIVE: The handles of the PDs might have changed
- * across the host reset so re-initialize the
- * required data for Direct IO
- */
- _scsih_init_warpdrive_properties(ioc, raid_device);
- if (raid_device->handle == handle)
- goto out;
- printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
- raid_device->handle);
- raid_device->handle = handle;
- if (sas_target_priv_data)
- sas_target_priv_data->handle = handle;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
-}
-
-/**
- * _scsih_search_responding_raid_devices -
- * @ioc: per adapter object
- *
- * After host reset, find out whether devices are still responding.
- * If not remove.
- *
- * Return nothing.
- */
-static void
-_scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc)
-{
- Mpi2RaidVolPage1_t volume_pg1;
- Mpi2RaidVolPage0_t volume_pg0;
- Mpi2RaidPhysDiskPage0_t pd_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u16 ioc_status;
- u16 handle;
- u8 phys_disk_num;
-
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
-
- if (list_empty(&ioc->raid_device_list))
- return;
-
- handle = 0xFFFF;
- while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
- &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
- handle = le16_to_cpu(volume_pg1.DevHandle);
-
- if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply,
- &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
- sizeof(Mpi2RaidVolPage0_t)))
- continue;
-
- if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL ||
- volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE ||
- volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED)
- _scsih_mark_responding_raid_device(ioc,
- le64_to_cpu(volume_pg1.WWID), handle);
- }
-
- /* refresh the pd_handles */
- if (!ioc->is_warpdrive) {
- phys_disk_num = 0xFF;
- memset(ioc->pd_handles, 0, ioc->pd_handles_sz);
- while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
- &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM,
- phys_disk_num))) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
- phys_disk_num = pd_pg0.PhysDiskNum;
- handle = le16_to_cpu(pd_pg0.DevHandle);
- set_bit(handle, ioc->pd_handles);
- }
- }
-}
-
-/**
- * _scsih_mark_responding_expander - mark a expander as responding
- * @ioc: per adapter object
- * @sas_address: sas address
- * @handle:
- *
- * After host reset, find out whether devices are still responding.
- * Used in _scsi_remove_unresponsive_expanders.
- *
- * Return nothing.
- */
-static void
-_scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u16 handle)
-{
- struct _sas_node *sas_expander;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
- if (sas_expander->sas_address != sas_address)
- continue;
- sas_expander->responding = 1;
- if (sas_expander->handle == handle)
- goto out;
- printk(KERN_INFO "\texpander(0x%016llx): handle changed"
- " from(0x%04x) to (0x%04x)!!!\n",
- (unsigned long long)sas_expander->sas_address,
- sas_expander->handle, handle);
- sas_expander->handle = handle;
- for (i = 0 ; i < sas_expander->num_phys ; i++)
- sas_expander->phy[i].handle = handle;
- goto out;
- }
- out:
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-}
-
-/**
- * _scsih_search_responding_expanders -
- * @ioc: per adapter object
- *
- * After host reset, find out whether devices are still responding.
- * If not remove.
- *
- * Return nothing.
- */
-static void
-_scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc)
-{
- Mpi2ExpanderPage0_t expander_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u16 ioc_status;
- __le64 sas_address;
- u16 handle;
-
- printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__);
-
- if (list_empty(&ioc->sas_expander_list))
- return;
-
- handle = 0xFFFF;
- while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
- MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) {
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
-
- handle = le16_to_cpu(expander_pg0.DevHandle);
- sas_address = le64_to_cpu(expander_pg0.SASAddress);
- printk(KERN_INFO "\texpander present: handle(0x%04x), "
- "sas_addr(0x%016llx)\n", handle,
- (unsigned long long)sas_address);
- _scsih_mark_responding_expander(ioc, sas_address, handle);
- }
-
-}
-
-/**
- * _scsih_remove_unresponding_sas_devices - removing unresponding devices
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
-{
- struct _sas_device *sas_device, *sas_device_next;
- struct _sas_node *sas_expander;
- struct _raid_device *raid_device, *raid_device_next;
-
-
- list_for_each_entry_safe(sas_device, sas_device_next,
- &ioc->sas_device_list, list) {
- if (sas_device->responding) {
- sas_device->responding = 0;
- continue;
- }
- if (sas_device->starget)
- starget_printk(KERN_INFO, sas_device->starget,
- "removing: handle(0x%04x), sas_addr(0x%016llx), "
- "enclosure logical id(0x%016llx), slot(%d)\n",
- sas_device->handle,
- (unsigned long long)sas_device->sas_address,
- (unsigned long long)
- sas_device->enclosure_logical_id,
- sas_device->slot);
- _scsih_remove_device(ioc, sas_device);
- }
-
- list_for_each_entry_safe(raid_device, raid_device_next,
- &ioc->raid_device_list, list) {
- if (raid_device->responding) {
- raid_device->responding = 0;
- continue;
- }
- if (raid_device->starget) {
- starget_printk(KERN_INFO, raid_device->starget,
- "removing: handle(0x%04x), wwid(0x%016llx)\n",
- raid_device->handle,
- (unsigned long long)raid_device->wwid);
- scsi_remove_target(&raid_device->starget->dev);
- }
- _scsih_raid_device_remove(ioc, raid_device);
- }
-
- retry_expander_search:
- sas_expander = NULL;
- list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
- if (sas_expander->responding) {
- sas_expander->responding = 0;
- continue;
- }
- mpt2sas_expander_remove(ioc, sas_expander->sas_address);
- goto retry_expander_search;
- }
-}
-
-/**
- * _scsih_hide_unhide_sas_devices - add/remove device to/from OS
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_scsih_hide_unhide_sas_devices(struct MPT2SAS_ADAPTER *ioc)
-{
- struct _sas_device *sas_device, *sas_device_next;
-
- if (!ioc->is_warpdrive || ioc->mfg_pg10_hide_flag !=
- MFG_PAGE10_HIDE_IF_VOL_PRESENT)
- return;
-
- if (ioc->hide_drives) {
- if (_scsih_get_num_volumes(ioc))
- return;
- ioc->hide_drives = 0;
- list_for_each_entry_safe(sas_device, sas_device_next,
- &ioc->sas_device_list, list) {
- if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
- sas_device->sas_address_parent)) {
- _scsih_sas_device_remove(ioc, sas_device);
- } else if (!sas_device->starget) {
- mpt2sas_transport_port_remove(ioc,
- sas_device->sas_address,
- sas_device->sas_address_parent);
- _scsih_sas_device_remove(ioc, sas_device);
- }
- }
- } else {
- if (!_scsih_get_num_volumes(ioc))
- return;
- ioc->hide_drives = 1;
- list_for_each_entry_safe(sas_device, sas_device_next,
- &ioc->sas_device_list, list) {
- mpt2sas_transport_port_remove(ioc,
- sas_device->sas_address,
- sas_device->sas_address_parent);
- }
- }
-}
-
-/**
- * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
- * @ioc: per adapter object
- * @reset_phase: phase
- *
- * The handler for doing any required cleanup or initialization.
- *
- * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
- * MPT2_IOC_DONE_RESET
- *
- * Return nothing.
- */
-void
-mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
-{
- switch (reset_phase) {
- case MPT2_IOC_PRE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
- break;
- case MPT2_IOC_AFTER_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
- if (ioc->scsih_cmds.status & MPT2_CMD_PENDING) {
- ioc->scsih_cmds.status |= MPT2_CMD_RESET;
- mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid);
- complete(&ioc->scsih_cmds.done);
- }
- if (ioc->tm_cmds.status & MPT2_CMD_PENDING) {
- ioc->tm_cmds.status |= MPT2_CMD_RESET;
- mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid);
- complete(&ioc->tm_cmds.done);
- }
- _scsih_fw_event_cleanup_queue(ioc);
- _scsih_flush_running_cmds(ioc);
- _scsih_queue_rescan(ioc);
- break;
- case MPT2_IOC_DONE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: "
- "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
- _scsih_sas_host_refresh(ioc);
- _scsih_prep_device_scan(ioc);
- _scsih_search_responding_sas_devices(ioc);
- _scsih_search_responding_raid_devices(ioc);
- _scsih_search_responding_expanders(ioc);
- break;
- }
-}
-
-/**
- * _firmware_event_work - delayed task for processing firmware events
- * @ioc: per adapter object
- * @work: equal to the fw_event_work object
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_firmware_event_work(struct work_struct *work)
-{
- struct fw_event_work *fw_event = container_of(work,
- struct fw_event_work, delayed_work.work);
- unsigned long flags;
- struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;
-
- /* the queue is being flushed so ignore this event */
- if (ioc->remove_host || fw_event->cancel_pending_work ||
- ioc->pci_error_recovery) {
- _scsih_fw_event_free(ioc, fw_event);
- return;
- }
-
- if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) {
- _scsih_fw_event_free(ioc, fw_event);
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->shost_recovery) {
- init_completion(&ioc->shost_recovery_done);
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
- flags);
- wait_for_completion(&ioc->shost_recovery_done);
- } else
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
- flags);
- _scsih_remove_unresponding_sas_devices(ioc);
- _scsih_hide_unhide_sas_devices(ioc);
- return;
- }
-
- switch (fw_event->event) {
- case MPT2SAS_TURN_ON_FAULT_LED:
- _scsih_turn_on_fault_led(ioc, fw_event->device_handle);
- break;
- case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
- _scsih_sas_topology_change_event(ioc, fw_event);
- break;
- case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
- _scsih_sas_device_status_change_event(ioc,
- fw_event);
- break;
- case MPI2_EVENT_SAS_DISCOVERY:
- _scsih_sas_discovery_event(ioc,
- fw_event);
- break;
- case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
- _scsih_sas_broadcast_primative_event(ioc,
- fw_event);
- break;
- case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
- _scsih_sas_enclosure_dev_status_change_event(ioc,
- fw_event);
- break;
- case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
- _scsih_sas_ir_config_change_event(ioc, fw_event);
- break;
- case MPI2_EVENT_IR_VOLUME:
- _scsih_sas_ir_volume_event(ioc, fw_event);
- break;
- case MPI2_EVENT_IR_PHYSICAL_DISK:
- _scsih_sas_ir_physical_disk_event(ioc, fw_event);
- break;
- case MPI2_EVENT_IR_OPERATION_STATUS:
- _scsih_sas_ir_operation_status_event(ioc, fw_event);
- break;
- }
- _scsih_fw_event_free(ioc, fw_event);
-}
-
-/**
- * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time)
- * @ioc: per adapter object
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- * Context: interrupt.
- *
- * This function merely adds a new work task into ioc->firmware_event_thread.
- * The tasks are worked from _firmware_event_work in user context.
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-u8
-mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
- u32 reply)
-{
- struct fw_event_work *fw_event;
- Mpi2EventNotificationReply_t *mpi_reply;
- u16 event;
- u16 sz;
-
- /* events turned off due to host reset or driver unloading */
- if (ioc->remove_host || ioc->pci_error_recovery)
- return 1;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- event = le16_to_cpu(mpi_reply->Event);
-
- switch (event) {
- /* handle these */
- case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
- {
- Mpi2EventDataSasBroadcastPrimitive_t *baen_data =
- (Mpi2EventDataSasBroadcastPrimitive_t *)
- mpi_reply->EventData;
-
- if (baen_data->Primitive !=
- MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT ||
- ioc->broadcast_aen_busy)
- return 1;
- ioc->broadcast_aen_busy = 1;
- break;
- }
-
- case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
- _scsih_check_topo_delete_events(ioc,
- (Mpi2EventDataSasTopologyChangeList_t *)
- mpi_reply->EventData);
- break;
- case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
- _scsih_check_ir_config_unhide_events(ioc,
- (Mpi2EventDataIrConfigChangeList_t *)
- mpi_reply->EventData);
- break;
- case MPI2_EVENT_IR_VOLUME:
- _scsih_check_volume_delete_events(ioc,
- (Mpi2EventDataIrVolume_t *)
- mpi_reply->EventData);
- break;
- case MPI2_EVENT_LOG_ENTRY_ADDED:
- {
- Mpi2EventDataLogEntryAdded_t *log_entry;
- u32 *log_code;
-
- if (!ioc->is_warpdrive)
- break;
-
- log_entry = (Mpi2EventDataLogEntryAdded_t *)
- mpi_reply->EventData;
- log_code = (u32 *)log_entry->LogData;
-
- if (le16_to_cpu(log_entry->LogEntryQualifier)
- != MPT2_WARPDRIVE_LOGENTRY)
- break;
-
- switch (le32_to_cpu(*log_code)) {
- case MPT2_WARPDRIVE_LC_SSDT:
- printk(MPT2SAS_WARN_FMT "WarpDrive Warning: "
- "IO Throttling has occurred in the WarpDrive "
- "subsystem. Check WarpDrive documentation for "
- "additional details.\n", ioc->name);
- break;
- case MPT2_WARPDRIVE_LC_SSDLW:
- printk(MPT2SAS_WARN_FMT "WarpDrive Warning: "
- "Program/Erase Cycles for the WarpDrive subsystem "
- "in degraded range. Check WarpDrive documentation "
- "for additional details.\n", ioc->name);
- break;
- case MPT2_WARPDRIVE_LC_SSDLF:
- printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: "
- "There are no Program/Erase Cycles for the "
- "WarpDrive subsystem. The storage device will be "
- "in read-only mode. Check WarpDrive documentation "
- "for additional details.\n", ioc->name);
- break;
- case MPT2_WARPDRIVE_LC_BRMF:
- printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: "
- "The Backup Rail Monitor has failed on the "
- "WarpDrive subsystem. Check WarpDrive "
- "documentation for additional details.\n",
- ioc->name);
- break;
- }
-
- break;
- }
- case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
- case MPI2_EVENT_IR_OPERATION_STATUS:
- case MPI2_EVENT_SAS_DISCOVERY:
- case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
- case MPI2_EVENT_IR_PHYSICAL_DISK:
- break;
-
- default: /* ignore the rest */
- return 1;
- }
-
- fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
- if (!fw_event) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return 1;
- }
- sz = le16_to_cpu(mpi_reply->EventDataLength) * 4;
- fw_event->event_data = kzalloc(sz, GFP_ATOMIC);
- if (!fw_event->event_data) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- kfree(fw_event);
- return 1;
- }
-
- memcpy(fw_event->event_data, mpi_reply->EventData,
- sz);
- fw_event->ioc = ioc;
- fw_event->VF_ID = mpi_reply->VF_ID;
- fw_event->VP_ID = mpi_reply->VP_ID;
- fw_event->event = event;
- _scsih_fw_event_add(ioc, fw_event);
- return 1;
-}
-
-/* shost template */
-static struct scsi_host_template scsih_driver_template = {
- .module = THIS_MODULE,
- .name = "Fusion MPT SAS Host",
- .proc_name = MPT2SAS_DRIVER_NAME,
- .queuecommand = _scsih_qcmd,
- .target_alloc = _scsih_target_alloc,
- .slave_alloc = _scsih_slave_alloc,
- .slave_configure = _scsih_slave_configure,
- .target_destroy = _scsih_target_destroy,
- .slave_destroy = _scsih_slave_destroy,
- .change_queue_depth = _scsih_change_queue_depth,
- .change_queue_type = _scsih_change_queue_type,
- .eh_abort_handler = _scsih_abort,
- .eh_device_reset_handler = _scsih_dev_reset,
- .eh_target_reset_handler = _scsih_target_reset,
- .eh_host_reset_handler = _scsih_host_reset,
- .bios_param = _scsih_bios_param,
- .can_queue = 1,
- .this_id = -1,
- .sg_tablesize = MPT2SAS_SG_DEPTH,
- .max_sectors = 8192,
- .cmd_per_lun = 7,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = mpt2sas_host_attrs,
- .sdev_attrs = mpt2sas_dev_attrs,
-};
-
-/**
- * _scsih_expander_node_remove - removing expander device from list.
- * @ioc: per adapter object
- * @sas_expander: the sas_device object
- * Context: Calling function should acquire ioc->sas_node_lock.
- *
- * Removing object and freeing associated memory from the
- * ioc->sas_expander_list.
- *
- * Return nothing.
- */
-static void
-_scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_node *sas_expander)
-{
- struct _sas_port *mpt2sas_port, *next;
-
- /* remove sibling ports attached to this expander */
- list_for_each_entry_safe(mpt2sas_port, next,
- &sas_expander->sas_port_list, port_list) {
- if (ioc->shost_recovery)
- return;
- if (mpt2sas_port->remote_identify.device_type ==
- SAS_END_DEVICE)
- mpt2sas_device_remove(ioc,
- mpt2sas_port->remote_identify.sas_address);
- else if (mpt2sas_port->remote_identify.device_type ==
- SAS_EDGE_EXPANDER_DEVICE ||
- mpt2sas_port->remote_identify.device_type ==
- SAS_FANOUT_EXPANDER_DEVICE)
- mpt2sas_expander_remove(ioc,
- mpt2sas_port->remote_identify.sas_address);
- }
-
- mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
- sas_expander->sas_address_parent);
-
- printk(MPT2SAS_INFO_FMT "expander_remove: handle"
- "(0x%04x), sas_addr(0x%016llx)\n", ioc->name,
- sas_expander->handle, (unsigned long long)
- sas_expander->sas_address);
-
- kfree(sas_expander->phy);
- kfree(sas_expander);
-}
-
-/**
- * _scsih_ir_shutdown - IR shutdown notification
- * @ioc: per adapter object
- *
- * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
- * the host system is shutting down.
- *
- * Return nothing.
- */
-static void
-_scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc)
-{
- Mpi2RaidActionRequest_t *mpi_request;
- Mpi2RaidActionReply_t *mpi_reply;
- u16 smid;
-
- /* is IR firmware build loaded ? */
- if (!ioc->ir_firmware)
- return;
-
- /* are there any volumes ? */
- if (list_empty(&ioc->raid_device_list))
- return;
-
- mutex_lock(&ioc->scsih_cmds.mutex);
-
- if (ioc->scsih_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: scsih_cmd in use\n",
- ioc->name, __func__);
- goto out;
- }
- ioc->scsih_cmds.status = MPT2_CMD_PENDING;
-
- smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
- goto out;
- }
-
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->scsih_cmds.smid = smid;
- memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
-
- mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
- mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
-
- if (!ioc->hide_ir_msg)
- printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name);
- init_completion(&ioc->scsih_cmds.done);
- mpt2sas_base_put_smid_default(ioc, smid);
- wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
-
- if (!(ioc->scsih_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- goto out;
- }
-
- if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) {
- mpi_reply = ioc->scsih_cmds.reply;
-
- if (!ioc->hide_ir_msg)
- printk(MPT2SAS_INFO_FMT "IR shutdown (complete): "
- "ioc_status(0x%04x), loginfo(0x%08x)\n",
- ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo));
- }
-
- out:
- ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
- mutex_unlock(&ioc->scsih_cmds.mutex);
-}
-
-/**
- * _scsih_shutdown - routine call during system shutdown
- * @pdev: PCI device struct
- *
- * Return nothing.
- */
-static void
-_scsih_shutdown(struct pci_dev *pdev)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- struct workqueue_struct *wq;
- unsigned long flags;
-
- ioc->remove_host = 1;
- _scsih_fw_event_cleanup_queue(ioc);
-
- spin_lock_irqsave(&ioc->fw_event_lock, flags);
- wq = ioc->firmware_event_thread;
- ioc->firmware_event_thread = NULL;
- spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
- if (wq)
- destroy_workqueue(wq);
-
- _scsih_ir_shutdown(ioc);
- mpt2sas_base_detach(ioc);
-}
-
-/**
- * _scsih_remove - detach and remove add host
- * @pdev: PCI device struct
- *
- * Routine called when unloading the driver.
- * Return nothing.
- */
-static void __devexit
-_scsih_remove(struct pci_dev *pdev)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- struct _sas_port *mpt2sas_port, *next_port;
- struct _raid_device *raid_device, *next;
- struct MPT2SAS_TARGET *sas_target_priv_data;
- struct workqueue_struct *wq;
- unsigned long flags;
-
- ioc->remove_host = 1;
- _scsih_fw_event_cleanup_queue(ioc);
-
- spin_lock_irqsave(&ioc->fw_event_lock, flags);
- wq = ioc->firmware_event_thread;
- ioc->firmware_event_thread = NULL;
- spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
- if (wq)
- destroy_workqueue(wq);
-
- /* release all the volumes */
- _scsih_ir_shutdown(ioc);
- list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list,
- list) {
- if (raid_device->starget) {
- sas_target_priv_data =
- raid_device->starget->hostdata;
- sas_target_priv_data->deleted = 1;
- scsi_remove_target(&raid_device->starget->dev);
- }
- printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid"
- "(0x%016llx)\n", ioc->name, raid_device->handle,
- (unsigned long long) raid_device->wwid);
- _scsih_raid_device_remove(ioc, raid_device);
- }
-
- /* free ports attached to the sas_host */
- list_for_each_entry_safe(mpt2sas_port, next_port,
- &ioc->sas_hba.sas_port_list, port_list) {
- if (mpt2sas_port->remote_identify.device_type ==
- SAS_END_DEVICE)
- mpt2sas_device_remove(ioc,
- mpt2sas_port->remote_identify.sas_address);
- else if (mpt2sas_port->remote_identify.device_type ==
- SAS_EDGE_EXPANDER_DEVICE ||
- mpt2sas_port->remote_identify.device_type ==
- SAS_FANOUT_EXPANDER_DEVICE)
- mpt2sas_expander_remove(ioc,
- mpt2sas_port->remote_identify.sas_address);
- }
-
- /* free phys attached to the sas_host */
- if (ioc->sas_hba.num_phys) {
- kfree(ioc->sas_hba.phy);
- ioc->sas_hba.phy = NULL;
- ioc->sas_hba.num_phys = 0;
- }
-
- sas_remove_host(shost);
- _scsih_shutdown(pdev);
- list_del(&ioc->list);
- scsi_remove_host(shost);
- scsi_host_put(shost);
-}
-
-/**
- * _scsih_probe_boot_devices - reports 1st device
- * @ioc: per adapter object
- *
- * If specified in bios page 2, this routine reports the 1st
- * device scsi-ml or sas transport for persistent boot device
- * purposes. Please refer to function _scsih_determine_boot_device()
- */
-static void
-_scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
-{
- u8 is_raid;
- void *device;
- struct _sas_device *sas_device;
- struct _raid_device *raid_device;
- u16 handle;
- u64 sas_address_parent;
- u64 sas_address;
- unsigned long flags;
- int rc;
-
- device = NULL;
- if (ioc->req_boot_device.device) {
- device = ioc->req_boot_device.device;
- is_raid = ioc->req_boot_device.is_raid;
- } else if (ioc->req_alt_boot_device.device) {
- device = ioc->req_alt_boot_device.device;
- is_raid = ioc->req_alt_boot_device.is_raid;
- } else if (ioc->current_boot_device.device) {
- device = ioc->current_boot_device.device;
- is_raid = ioc->current_boot_device.is_raid;
- }
-
- if (!device)
- return;
-
- if (is_raid) {
- raid_device = device;
- rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
- raid_device->id, 0);
- if (rc)
- _scsih_raid_device_remove(ioc, raid_device);
- } else {
- sas_device = device;
- handle = sas_device->handle;
- sas_address_parent = sas_device->sas_address_parent;
- sas_address = sas_device->sas_address;
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- list_move_tail(&sas_device->list, &ioc->sas_device_list);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (ioc->hide_drives)
- return;
- if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
- sas_device->sas_address_parent)) {
- _scsih_sas_device_remove(ioc, sas_device);
- } else if (!sas_device->starget) {
- mpt2sas_transport_port_remove(ioc, sas_address,
- sas_address_parent);
- _scsih_sas_device_remove(ioc, sas_device);
- }
- }
-}
-
-/**
- * _scsih_probe_raid - reporting raid volumes to scsi-ml
- * @ioc: per adapter object
- *
- * Called during initial loading of the driver.
- */
-static void
-_scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc)
-{
- struct _raid_device *raid_device, *raid_next;
- int rc;
-
- list_for_each_entry_safe(raid_device, raid_next,
- &ioc->raid_device_list, list) {
- if (raid_device->starget)
- continue;
- rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
- raid_device->id, 0);
- if (rc)
- _scsih_raid_device_remove(ioc, raid_device);
- }
-}
-
-/**
- * _scsih_probe_sas - reporting sas devices to sas transport
- * @ioc: per adapter object
- *
- * Called during initial loading of the driver.
- */
-static void
-_scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
-{
- struct _sas_device *sas_device, *next;
- unsigned long flags;
-
- /* SAS Device List */
- list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
- list) {
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- list_move_tail(&sas_device->list, &ioc->sas_device_list);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (ioc->hide_drives)
- continue;
-
- if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
- sas_device->sas_address_parent)) {
- _scsih_sas_device_remove(ioc, sas_device);
- } else if (!sas_device->starget) {
- mpt2sas_transport_port_remove(ioc,
- sas_device->sas_address,
- sas_device->sas_address_parent);
- _scsih_sas_device_remove(ioc, sas_device);
- }
- }
-}
-
-/**
- * _scsih_probe_devices - probing for devices
- * @ioc: per adapter object
- *
- * Called during initial loading of the driver.
- */
-static void
-_scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)
-{
- u16 volume_mapping_flags =
- le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) &
- MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
-
- if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR))
- return; /* return when IOC doesn't support initiator mode */
-
- _scsih_probe_boot_devices(ioc);
-
- if (ioc->ir_firmware) {
- if ((volume_mapping_flags &
- MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING)) {
- _scsih_probe_sas(ioc);
- _scsih_probe_raid(ioc);
- } else {
- _scsih_probe_raid(ioc);
- _scsih_probe_sas(ioc);
- }
- } else
- _scsih_probe_sas(ioc);
-}
-
-/**
- * _scsih_probe - attach and add scsi host
- * @pdev: PCI device struct
- * @id: pci device id
- *
- * Returns 0 success, anything else error.
- */
-static int
-_scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct MPT2SAS_ADAPTER *ioc;
- struct Scsi_Host *shost;
-
- shost = scsi_host_alloc(&scsih_driver_template,
- sizeof(struct MPT2SAS_ADAPTER));
- if (!shost)
- return -ENODEV;
-
- /* init local params */
- ioc = shost_priv(shost);
- memset(ioc, 0, sizeof(struct MPT2SAS_ADAPTER));
- INIT_LIST_HEAD(&ioc->list);
- list_add_tail(&ioc->list, &mpt2sas_ioc_list);
- ioc->shost = shost;
- ioc->id = mpt_ids++;
- sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id);
- ioc->pdev = pdev;
- if (id->device == MPI2_MFGPAGE_DEVID_SSS6200) {
- ioc->is_warpdrive = 1;
- ioc->hide_ir_msg = 1;
- } else
- ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS;
- ioc->scsi_io_cb_idx = scsi_io_cb_idx;
- ioc->tm_cb_idx = tm_cb_idx;
- ioc->ctl_cb_idx = ctl_cb_idx;
- ioc->base_cb_idx = base_cb_idx;
- ioc->transport_cb_idx = transport_cb_idx;
- ioc->scsih_cb_idx = scsih_cb_idx;
- ioc->config_cb_idx = config_cb_idx;
- ioc->tm_tr_cb_idx = tm_tr_cb_idx;
- ioc->tm_tr_volume_cb_idx = tm_tr_volume_cb_idx;
- ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx;
- ioc->logging_level = logging_level;
- /* misc semaphores and spin locks */
- mutex_init(&ioc->reset_in_progress_mutex);
- spin_lock_init(&ioc->ioc_reset_in_progress_lock);
- spin_lock_init(&ioc->scsi_lookup_lock);
- spin_lock_init(&ioc->sas_device_lock);
- spin_lock_init(&ioc->sas_node_lock);
- spin_lock_init(&ioc->fw_event_lock);
- spin_lock_init(&ioc->raid_device_lock);
-
- INIT_LIST_HEAD(&ioc->sas_device_list);
- INIT_LIST_HEAD(&ioc->sas_device_init_list);
- INIT_LIST_HEAD(&ioc->sas_expander_list);
- INIT_LIST_HEAD(&ioc->fw_event_list);
- INIT_LIST_HEAD(&ioc->raid_device_list);
- INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list);
- INIT_LIST_HEAD(&ioc->delayed_tr_list);
- INIT_LIST_HEAD(&ioc->delayed_tr_volume_list);
-
- /* init shost parameters */
- shost->max_cmd_len = 32;
- shost->max_lun = max_lun;
- shost->transportt = mpt2sas_transport_template;
- shost->unique_id = ioc->id;
-
- if ((scsi_add_host(shost, &pdev->dev))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- list_del(&ioc->list);
- goto out_add_shost_fail;
- }
-
- scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION
- | SHOST_DIF_TYPE2_PROTECTION | SHOST_DIF_TYPE3_PROTECTION);
- scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
-
- /* event thread */
- snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
- "fw_event%d", ioc->id);
- ioc->firmware_event_thread = create_singlethread_workqueue(
- ioc->firmware_event_name);
- if (!ioc->firmware_event_thread) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out_thread_fail;
- }
-
- ioc->wait_for_port_enable_to_complete = 1;
- if ((mpt2sas_base_attach(ioc))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out_attach_fail;
- }
-
- ioc->wait_for_port_enable_to_complete = 0;
- if (ioc->is_warpdrive) {
- if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS)
- ioc->hide_drives = 0;
- else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS)
- ioc->hide_drives = 1;
- else {
- if (_scsih_get_num_volumes(ioc))
- ioc->hide_drives = 1;
- else
- ioc->hide_drives = 0;
- }
- } else
- ioc->hide_drives = 0;
-
- _scsih_probe_devices(ioc);
- return 0;
-
- out_attach_fail:
- destroy_workqueue(ioc->firmware_event_thread);
- out_thread_fail:
- list_del(&ioc->list);
- scsi_remove_host(shost);
- out_add_shost_fail:
- return -ENODEV;
-}
-
-#ifdef CONFIG_PM
-/**
- * _scsih_suspend - power management suspend main entry point
- * @pdev: PCI device struct
- * @state: PM state change to (usually PCI_D3)
- *
- * Returns 0 success, anything else error.
- */
-static int
-_scsih_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- u32 device_state;
-
- mpt2sas_base_stop_watchdog(ioc);
- scsi_block_requests(shost);
- device_state = pci_choose_state(pdev, state);
- printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, entering "
- "operating state [D%d]\n", ioc->name, pdev,
- pci_name(pdev), device_state);
-
- mpt2sas_base_free_resources(ioc);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, device_state);
- return 0;
-}
-
-/**
- * _scsih_resume - power management resume main entry point
- * @pdev: PCI device struct
- *
- * Returns 0 success, anything else error.
- */
-static int
-_scsih_resume(struct pci_dev *pdev)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- u32 device_state = pdev->current_state;
- int r;
-
- printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, previous "
- "operating state [D%d]\n", ioc->name, pdev,
- pci_name(pdev), device_state);
-
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
- ioc->pdev = pdev;
- r = mpt2sas_base_map_resources(ioc);
- if (r)
- return r;
-
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET);
- scsi_unblock_requests(shost);
- mpt2sas_base_start_watchdog(ioc);
- return 0;
-}
-#endif /* CONFIG_PM */
-
-/**
- * _scsih_pci_error_detected - Called when a PCI error is detected.
- * @pdev: PCI device struct
- * @state: PCI channel state
- *
- * Description: Called when a PCI error is detected.
- *
- * Return value:
- * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT
- */
-static pci_ers_result_t
-_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- printk(MPT2SAS_INFO_FMT "PCI error: detected callback, state(%d)!!\n",
- ioc->name, state);
-
- switch (state) {
- case pci_channel_io_normal:
- return PCI_ERS_RESULT_CAN_RECOVER;
- case pci_channel_io_frozen:
- /* Fatal error, prepare for slot reset */
- ioc->pci_error_recovery = 1;
- scsi_block_requests(ioc->shost);
- mpt2sas_base_stop_watchdog(ioc);
- mpt2sas_base_free_resources(ioc);
- return PCI_ERS_RESULT_NEED_RESET;
- case pci_channel_io_perm_failure:
- /* Permanent error, prepare for device removal */
- ioc->pci_error_recovery = 1;
- mpt2sas_base_stop_watchdog(ioc);
- _scsih_flush_running_cmds(ioc);
- return PCI_ERS_RESULT_DISCONNECT;
- }
- return PCI_ERS_RESULT_NEED_RESET;
-}
-
-/**
- * _scsih_pci_slot_reset - Called when PCI slot has been reset.
- * @pdev: PCI device struct
- *
- * Description: This routine is called by the pci error recovery
- * code after the PCI slot has been reset, just before we
- * should resume normal operations.
- */
-static pci_ers_result_t
-_scsih_pci_slot_reset(struct pci_dev *pdev)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- int rc;
-
- printk(MPT2SAS_INFO_FMT "PCI error: slot reset callback!!\n",
- ioc->name);
-
- ioc->pci_error_recovery = 0;
- ioc->pdev = pdev;
- pci_restore_state(pdev);
- rc = mpt2sas_base_map_resources(ioc);
- if (rc)
- return PCI_ERS_RESULT_DISCONNECT;
-
-
- rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
-
- printk(MPT2SAS_WARN_FMT "hard reset: %s\n", ioc->name,
- (rc == 0) ? "success" : "failed");
-
- if (!rc)
- return PCI_ERS_RESULT_RECOVERED;
- else
- return PCI_ERS_RESULT_DISCONNECT;
-}
-
-/**
- * _scsih_pci_resume() - resume normal ops after PCI reset
- * @pdev: pointer to PCI device
- *
- * Called when the error recovery driver tells us that its
- * OK to resume normal operation. Use completion to allow
- * halted scsi ops to resume.
- */
-static void
-_scsih_pci_resume(struct pci_dev *pdev)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- printk(MPT2SAS_INFO_FMT "PCI error: resume callback!!\n", ioc->name);
-
- pci_cleanup_aer_uncorrect_error_status(pdev);
- mpt2sas_base_start_watchdog(ioc);
- scsi_unblock_requests(ioc->shost);
-}
-
-/**
- * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers
- * @pdev: pointer to PCI device
- */
-static pci_ers_result_t
-_scsih_pci_mmio_enabled(struct pci_dev *pdev)
-{
- struct Scsi_Host *shost = pci_get_drvdata(pdev);
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
-
- printk(MPT2SAS_INFO_FMT "PCI error: mmio enabled callback!!\n",
- ioc->name);
-
- /* TODO - dump whatever for debugging purposes */
-
- /* Request a slot reset. */
- return PCI_ERS_RESULT_NEED_RESET;
-}
-
-static struct pci_error_handlers _scsih_err_handler = {
- .error_detected = _scsih_pci_error_detected,
- .mmio_enabled = _scsih_pci_mmio_enabled,
- .slot_reset = _scsih_pci_slot_reset,
- .resume = _scsih_pci_resume,
-};
-
-static struct pci_driver scsih_driver = {
- .name = MPT2SAS_DRIVER_NAME,
- .id_table = scsih_pci_table,
- .probe = _scsih_probe,
- .remove = __devexit_p(_scsih_remove),
- .shutdown = _scsih_shutdown,
- .err_handler = &_scsih_err_handler,
-#ifdef CONFIG_PM
- .suspend = _scsih_suspend,
- .resume = _scsih_resume,
-#endif
-};
-
-/* raid transport support */
-static struct raid_function_template mpt2sas_raid_functions = {
- .cookie = &scsih_driver_template,
- .is_raid = _scsih_is_raid,
- .get_resync = _scsih_get_resync,
- .get_state = _scsih_get_state,
-};
-
-/**
- * _scsih_init - main entry point for this driver.
- *
- * Returns 0 success, anything else error.
- */
-static int __init
-_scsih_init(void)
-{
- int error;
-
- mpt_ids = 0;
- printk(KERN_INFO "%s version %s loaded\n", MPT2SAS_DRIVER_NAME,
- MPT2SAS_DRIVER_VERSION);
-
- mpt2sas_transport_template =
- sas_attach_transport(&mpt2sas_transport_functions);
- if (!mpt2sas_transport_template)
- return -ENODEV;
- /* raid transport support */
- mpt2sas_raid_template = raid_class_attach(&mpt2sas_raid_functions);
- if (!mpt2sas_raid_template) {
- sas_release_transport(mpt2sas_transport_template);
- return -ENODEV;
- }
-
- mpt2sas_base_initialize_callback_handler();
-
- /* queuecommand callback hander */
- scsi_io_cb_idx = mpt2sas_base_register_callback_handler(_scsih_io_done);
-
- /* task management callback handler */
- tm_cb_idx = mpt2sas_base_register_callback_handler(_scsih_tm_done);
-
- /* base internal commands callback handler */
- base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done);
-
- /* transport internal commands callback handler */
- transport_cb_idx = mpt2sas_base_register_callback_handler(
- mpt2sas_transport_done);
-
- /* scsih internal commands callback handler */
- scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done);
-
- /* configuration page API internal commands callback handler */
- config_cb_idx = mpt2sas_base_register_callback_handler(
- mpt2sas_config_done);
-
- /* ctl module callback handler */
- ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done);
-
- tm_tr_cb_idx = mpt2sas_base_register_callback_handler(
- _scsih_tm_tr_complete);
-
- tm_tr_volume_cb_idx = mpt2sas_base_register_callback_handler(
- _scsih_tm_volume_tr_complete);
-
- tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler(
- _scsih_sas_control_complete);
-
- mpt2sas_ctl_init();
-
- error = pci_register_driver(&scsih_driver);
- if (error) {
- /* raid transport support */
- raid_class_release(mpt2sas_raid_template);
- sas_release_transport(mpt2sas_transport_template);
- }
-
- return error;
-}
-
-/**
- * _scsih_exit - exit point for this driver (when it is a module).
- *
- * Returns 0 success, anything else error.
- */
-static void __exit
-_scsih_exit(void)
-{
- printk(KERN_INFO "mpt2sas version %s unloading\n",
- MPT2SAS_DRIVER_VERSION);
-
- pci_unregister_driver(&scsih_driver);
-
- mpt2sas_ctl_exit();
-
- mpt2sas_base_release_callback_handler(scsi_io_cb_idx);
- mpt2sas_base_release_callback_handler(tm_cb_idx);
- mpt2sas_base_release_callback_handler(base_cb_idx);
- mpt2sas_base_release_callback_handler(transport_cb_idx);
- mpt2sas_base_release_callback_handler(scsih_cb_idx);
- mpt2sas_base_release_callback_handler(config_cb_idx);
- mpt2sas_base_release_callback_handler(ctl_cb_idx);
-
- mpt2sas_base_release_callback_handler(tm_tr_cb_idx);
- mpt2sas_base_release_callback_handler(tm_tr_volume_cb_idx);
- mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx);
-
- /* raid transport support */
- raid_class_release(mpt2sas_raid_template);
- sas_release_transport(mpt2sas_transport_template);
-
-}
-
-module_init(_scsih_init);
-module_exit(_scsih_exit);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c
deleted file mode 100644
index cb1cdecbe0f8..000000000000
--- a/drivers/scsi/mpt2sas/mpt2sas_transport.c
+++ /dev/null
@@ -1,2027 +0,0 @@
-/*
- * SAS Transport Layer for MPT (Message Passing Technology) based controllers
- *
- * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * NO WARRANTY
- * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- * solely responsible for determining the appropriateness of using and
- * distributing the Program and assumes all risks associated with its
- * exercise of rights under this Agreement, including but not limited to
- * the risks and costs of program errors, damage to or loss of data,
- * programs or equipment, and unavailability or interruption of operations.
-
- * DISCLAIMER OF LIABILITY
- * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_transport_sas.h>
-#include <scsi/scsi_dbg.h>
-
-#include "mpt2sas_base.h"
-/**
- * _transport_sas_node_find_by_sas_address - sas node search
- * @ioc: per adapter object
- * @sas_address: sas address of expander or sas host
- * Context: Calling function should acquire ioc->sas_node_lock.
- *
- * Search for either hba phys or expander device based on handle, then returns
- * the sas_node object.
- */
-static struct _sas_node *
-_transport_sas_node_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
- u64 sas_address)
-{
- if (ioc->sas_hba.sas_address == sas_address)
- return &ioc->sas_hba;
- else
- return mpt2sas_scsih_expander_find_by_sas_address(ioc,
- sas_address);
-}
-
-/**
- * _transport_convert_phy_link_rate -
- * @link_rate: link rate returned from mpt firmware
- *
- * Convert link_rate from mpi fusion into sas_transport form.
- */
-static enum sas_linkrate
-_transport_convert_phy_link_rate(u8 link_rate)
-{
- enum sas_linkrate rc;
-
- switch (link_rate) {
- case MPI2_SAS_NEG_LINK_RATE_1_5:
- rc = SAS_LINK_RATE_1_5_GBPS;
- break;
- case MPI2_SAS_NEG_LINK_RATE_3_0:
- rc = SAS_LINK_RATE_3_0_GBPS;
- break;
- case MPI2_SAS_NEG_LINK_RATE_6_0:
- rc = SAS_LINK_RATE_6_0_GBPS;
- break;
- case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED:
- rc = SAS_PHY_DISABLED;
- break;
- case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
- rc = SAS_LINK_RATE_FAILED;
- break;
- case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR:
- rc = SAS_SATA_PORT_SELECTOR;
- break;
- case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
- rc = SAS_PHY_RESET_IN_PROGRESS;
- break;
- default:
- case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
- case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
- rc = SAS_LINK_RATE_UNKNOWN;
- break;
- }
- return rc;
-}
-
-/**
- * _transport_set_identify - set identify for phys and end devices
- * @ioc: per adapter object
- * @handle: device handle
- * @identify: sas identify info
- *
- * Populates sas identify info.
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- struct sas_identify *identify)
-{
- Mpi2SasDevicePage0_t sas_device_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u32 device_info;
- u32 ioc_status;
-
- if (ioc->shost_recovery || ioc->pci_error_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
- return -EFAULT;
- }
-
- if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
-
- ioc->name, __FILE__, __LINE__, __func__);
- return -ENXIO;
- }
-
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
- "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
- __FILE__, __LINE__, __func__);
- return -EIO;
- }
-
- memset(identify, 0, sizeof(identify));
- device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
-
- /* sas_address */
- identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
-
- /* device_type */
- switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
- case MPI2_SAS_DEVICE_INFO_NO_DEVICE:
- identify->device_type = SAS_PHY_UNUSED;
- break;
- case MPI2_SAS_DEVICE_INFO_END_DEVICE:
- identify->device_type = SAS_END_DEVICE;
- break;
- case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER:
- identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
- break;
- case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER:
- identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
- break;
- }
-
- /* initiator_port_protocols */
- if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR)
- identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
- if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR)
- identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
- if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR)
- identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
- if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST)
- identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
-
- /* target_port_protocols */
- if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET)
- identify->target_port_protocols |= SAS_PROTOCOL_SSP;
- if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
- identify->target_port_protocols |= SAS_PROTOCOL_STP;
- if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET)
- identify->target_port_protocols |= SAS_PROTOCOL_SMP;
- if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
- identify->target_port_protocols |= SAS_PROTOCOL_SATA;
-
- return 0;
-}
-
-/**
- * mpt2sas_transport_done - internal transport layer callback handler.
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_index: MSIX table index supplied by the OS
- * @reply: reply message frame(lower 32bit addr)
- *
- * Callback handler when sending internal generated transport cmds.
- * The callback index passed is `ioc->transport_cb_idx`
- *
- * Return 1 meaning mf should be freed from _base_interrupt
- * 0 means the mf is freed from this function.
- */
-u8
-mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
- u32 reply)
-{
- MPI2DefaultReply_t *mpi_reply;
-
- mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
- if (ioc->transport_cmds.status == MPT2_CMD_NOT_USED)
- return 1;
- if (ioc->transport_cmds.smid != smid)
- return 1;
- ioc->transport_cmds.status |= MPT2_CMD_COMPLETE;
- if (mpi_reply) {
- memcpy(ioc->transport_cmds.reply, mpi_reply,
- mpi_reply->MsgLength*4);
- ioc->transport_cmds.status |= MPT2_CMD_REPLY_VALID;
- }
- ioc->transport_cmds.status &= ~MPT2_CMD_PENDING;
- complete(&ioc->transport_cmds.done);
- return 1;
-}
-
-/* report manufacture request structure */
-struct rep_manu_request{
- u8 smp_frame_type;
- u8 function;
- u8 reserved;
- u8 request_length;
-};
-
-/* report manufacture reply structure */
-struct rep_manu_reply{
- u8 smp_frame_type; /* 0x41 */
- u8 function; /* 0x01 */
- u8 function_result;
- u8 response_length;
- u16 expander_change_count;
- u8 reserved0[2];
- u8 sas_format;
- u8 reserved2[3];
- u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
- u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
- u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
- u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
- u16 component_id;
- u8 component_revision_id;
- u8 reserved3;
- u8 vendor_specific[8];
-};
-
-/**
- * _transport_expander_report_manufacture - obtain SMP report_manufacture
- * @ioc: per adapter object
- * @sas_address: expander sas address
- * @edev: the sas_expander_device object
- *
- * Fills in the sas_expander_device object when SMP port is created.
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
- u64 sas_address, struct sas_expander_device *edev)
-{
- Mpi2SmpPassthroughRequest_t *mpi_request;
- Mpi2SmpPassthroughReply_t *mpi_reply;
- struct rep_manu_reply *manufacture_reply;
- struct rep_manu_request *manufacture_request;
- int rc;
- u16 smid;
- u32 ioc_state;
- unsigned long timeleft;
- void *psge;
- u32 sgl_flags;
- u8 issue_reset = 0;
- void *data_out = NULL;
- dma_addr_t data_out_dma;
- u32 sz;
- u64 *sas_address_le;
- u16 wait_state_count;
-
- if (ioc->shost_recovery || ioc->pci_error_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
- return -EFAULT;
- }
-
- mutex_lock(&ioc->transport_cmds.mutex);
-
- if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
- ioc->transport_cmds.status = MPT2_CMD_PENDING;
-
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == 10) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
- if (wait_state_count)
- printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
- ioc->name, __func__);
-
- smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- rc = 0;
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->transport_cmds.smid = smid;
-
- sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply);
- data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma);
-
- if (!data_out) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- rc = -ENOMEM;
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
-
- manufacture_request = data_out;
- manufacture_request->smp_frame_type = 0x40;
- manufacture_request->function = 1;
- manufacture_request->reserved = 0;
- manufacture_request->request_length = 0;
-
- memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
- mpi_request->PhysicalPort = 0xFF;
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
- sas_address_le = (u64 *)&mpi_request->SASAddress;
- *sas_address_le = cpu_to_le64(sas_address);
- mpi_request->RequestDataLength =
- cpu_to_le16(sizeof(struct rep_manu_request));
- psge = &mpi_request->SGL;
-
- /* WRITE sgel first */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- sizeof(struct rep_manu_request), data_out_dma);
-
- /* incr sgel */
- psge += ioc->sge_size;
-
- /* READ sgel last */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- sizeof(struct rep_manu_reply), data_out_dma +
- sizeof(struct rep_manu_request));
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - "
- "send to sas_addr(0x%016llx)\n", ioc->name,
- (unsigned long long)sas_address));
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->transport_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
- 10*HZ);
-
- if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SmpPassthroughRequest_t)/4);
- if (!(ioc->transport_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - "
- "complete\n", ioc->name));
-
- if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) {
- u8 *tmp;
-
- mpi_reply = ioc->transport_cmds.reply;
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "report_manufacture - reply data transfer size(%d)\n",
- ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength)));
-
- if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
- sizeof(struct rep_manu_reply))
- goto out;
-
- manufacture_reply = data_out + sizeof(struct rep_manu_request);
- strncpy(edev->vendor_id, manufacture_reply->vendor_id,
- SAS_EXPANDER_VENDOR_ID_LEN);
- strncpy(edev->product_id, manufacture_reply->product_id,
- SAS_EXPANDER_PRODUCT_ID_LEN);
- strncpy(edev->product_rev, manufacture_reply->product_rev,
- SAS_EXPANDER_PRODUCT_REV_LEN);
- edev->level = manufacture_reply->sas_format & 1;
- if (edev->level) {
- strncpy(edev->component_vendor_id,
- manufacture_reply->component_vendor_id,
- SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
- tmp = (u8 *)&manufacture_reply->component_id;
- edev->component_id = tmp[0] << 8 | tmp[1];
- edev->component_revision_id =
- manufacture_reply->component_revision_id;
- }
- } else
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "report_manufacture - no reply\n", ioc->name));
-
- issue_host_reset:
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- out:
- ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
- if (data_out)
- pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma);
-
- mutex_unlock(&ioc->transport_cmds.mutex);
- return rc;
-}
-
-/**
- * _transport_delete_port - helper function to removing a port
- * @ioc: per adapter object
- * @mpt2sas_port: mpt2sas per port object
- *
- * Returns nothing.
- */
-static void
-_transport_delete_port(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_port *mpt2sas_port)
-{
- u64 sas_address = mpt2sas_port->remote_identify.sas_address;
- enum sas_device_type device_type =
- mpt2sas_port->remote_identify.device_type;
-
- dev_printk(KERN_INFO, &mpt2sas_port->port->dev,
- "remove: sas_addr(0x%016llx)\n",
- (unsigned long long) sas_address);
-
- ioc->logging_level |= MPT_DEBUG_TRANSPORT;
- if (device_type == SAS_END_DEVICE)
- mpt2sas_device_remove(ioc, sas_address);
- else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
- device_type == SAS_FANOUT_EXPANDER_DEVICE)
- mpt2sas_expander_remove(ioc, sas_address);
- ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
-}
-
-/**
- * _transport_delete_phy - helper function to removing single phy from port
- * @ioc: per adapter object
- * @mpt2sas_port: mpt2sas per port object
- * @mpt2sas_phy: mpt2sas per phy object
- *
- * Returns nothing.
- */
-static void
-_transport_delete_phy(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_port *mpt2sas_port, struct _sas_phy *mpt2sas_phy)
-{
- u64 sas_address = mpt2sas_port->remote_identify.sas_address;
-
- dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
- "remove: sas_addr(0x%016llx), phy(%d)\n",
- (unsigned long long) sas_address, mpt2sas_phy->phy_id);
-
- list_del(&mpt2sas_phy->port_siblings);
- mpt2sas_port->num_phys--;
- sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy);
- mpt2sas_phy->phy_belongs_to_port = 0;
-}
-
-/**
- * _transport_add_phy - helper function to adding single phy to port
- * @ioc: per adapter object
- * @mpt2sas_port: mpt2sas per port object
- * @mpt2sas_phy: mpt2sas per phy object
- *
- * Returns nothing.
- */
-static void
-_transport_add_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_port *mpt2sas_port,
- struct _sas_phy *mpt2sas_phy)
-{
- u64 sas_address = mpt2sas_port->remote_identify.sas_address;
-
- dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
- "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long)
- sas_address, mpt2sas_phy->phy_id);
-
- list_add_tail(&mpt2sas_phy->port_siblings, &mpt2sas_port->phy_list);
- mpt2sas_port->num_phys++;
- sas_port_add_phy(mpt2sas_port->port, mpt2sas_phy->phy);
- mpt2sas_phy->phy_belongs_to_port = 1;
-}
-
-/**
- * _transport_add_phy_to_an_existing_port - adding new phy to existing port
- * @ioc: per adapter object
- * @sas_node: sas node object (either expander or sas host)
- * @mpt2sas_phy: mpt2sas per phy object
- * @sas_address: sas address of device/expander were phy needs to be added to
- *
- * Returns nothing.
- */
-static void
-_transport_add_phy_to_an_existing_port(struct MPT2SAS_ADAPTER *ioc,
-struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy, u64 sas_address)
-{
- struct _sas_port *mpt2sas_port;
- struct _sas_phy *phy_srch;
-
- if (mpt2sas_phy->phy_belongs_to_port == 1)
- return;
-
- list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list,
- port_list) {
- if (mpt2sas_port->remote_identify.sas_address !=
- sas_address)
- continue;
- list_for_each_entry(phy_srch, &mpt2sas_port->phy_list,
- port_siblings) {
- if (phy_srch == mpt2sas_phy)
- return;
- }
- _transport_add_phy(ioc, mpt2sas_port, mpt2sas_phy);
- return;
- }
-
-}
-
-/**
- * _transport_del_phy_from_an_existing_port - delete phy from existing port
- * @ioc: per adapter object
- * @sas_node: sas node object (either expander or sas host)
- * @mpt2sas_phy: mpt2sas per phy object
- *
- * Returns nothing.
- */
-static void
-_transport_del_phy_from_an_existing_port(struct MPT2SAS_ADAPTER *ioc,
- struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy)
-{
- struct _sas_port *mpt2sas_port, *next;
- struct _sas_phy *phy_srch;
-
- if (mpt2sas_phy->phy_belongs_to_port == 0)
- return;
-
- list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list,
- port_list) {
- list_for_each_entry(phy_srch, &mpt2sas_port->phy_list,
- port_siblings) {
- if (phy_srch != mpt2sas_phy)
- continue;
- if (mpt2sas_port->num_phys == 1)
- _transport_delete_port(ioc, mpt2sas_port);
- else
- _transport_delete_phy(ioc, mpt2sas_port,
- mpt2sas_phy);
- return;
- }
- }
-}
-
-/**
- * _transport_sanity_check - sanity check when adding a new port
- * @ioc: per adapter object
- * @sas_node: sas node object (either expander or sas host)
- * @sas_address: sas address of device being added
- *
- * See the explanation above from _transport_delete_duplicate_port
- */
-static void
-_transport_sanity_check(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node,
- u64 sas_address)
-{
- int i;
-
- for (i = 0; i < sas_node->num_phys; i++) {
- if (sas_node->phy[i].remote_identify.sas_address != sas_address)
- continue;
- if (sas_node->phy[i].phy_belongs_to_port == 1)
- _transport_del_phy_from_an_existing_port(ioc, sas_node,
- &sas_node->phy[i]);
- }
-}
-
-/**
- * mpt2sas_transport_port_add - insert port to the list
- * @ioc: per adapter object
- * @handle: handle of attached device
- * @sas_address: sas address of parent expander or sas host
- * Context: This function will acquire ioc->sas_node_lock.
- *
- * Adding new port object to the sas_node->sas_port_list.
- *
- * Returns mpt2sas_port.
- */
-struct _sas_port *
-mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- u64 sas_address)
-{
- struct _sas_phy *mpt2sas_phy, *next;
- struct _sas_port *mpt2sas_port;
- unsigned long flags;
- struct _sas_node *sas_node;
- struct sas_rphy *rphy;
- int i;
- struct sas_port *port;
-
- mpt2sas_port = kzalloc(sizeof(struct _sas_port),
- GFP_KERNEL);
- if (!mpt2sas_port) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return NULL;
- }
-
- INIT_LIST_HEAD(&mpt2sas_port->port_list);
- INIT_LIST_HEAD(&mpt2sas_port->phy_list);
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-
- if (!sas_node) {
- printk(MPT2SAS_ERR_FMT "%s: Could not find "
- "parent sas_address(0x%016llx)!\n", ioc->name,
- __func__, (unsigned long long)sas_address);
- goto out_fail;
- }
-
- if ((_transport_set_identify(ioc, handle,
- &mpt2sas_port->remote_identify))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out_fail;
- }
-
- if (mpt2sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out_fail;
- }
-
- _transport_sanity_check(ioc, sas_node,
- mpt2sas_port->remote_identify.sas_address);
-
- for (i = 0; i < sas_node->num_phys; i++) {
- if (sas_node->phy[i].remote_identify.sas_address !=
- mpt2sas_port->remote_identify.sas_address)
- continue;
- list_add_tail(&sas_node->phy[i].port_siblings,
- &mpt2sas_port->phy_list);
- mpt2sas_port->num_phys++;
- }
-
- if (!mpt2sas_port->num_phys) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out_fail;
- }
-
- port = sas_port_alloc_num(sas_node->parent_dev);
- if ((sas_port_add(port))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- goto out_fail;
- }
-
- list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list,
- port_siblings) {
- if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
- dev_printk(KERN_INFO, &port->dev, "add: handle(0x%04x)"
- ", sas_addr(0x%016llx), phy(%d)\n", handle,
- (unsigned long long)
- mpt2sas_port->remote_identify.sas_address,
- mpt2sas_phy->phy_id);
- sas_port_add_phy(port, mpt2sas_phy->phy);
- mpt2sas_phy->phy_belongs_to_port = 1;
- }
-
- mpt2sas_port->port = port;
- if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE)
- rphy = sas_end_device_alloc(port);
- else
- rphy = sas_expander_alloc(port,
- mpt2sas_port->remote_identify.device_type);
-
- rphy->identify = mpt2sas_port->remote_identify;
- if ((sas_rphy_add(rphy))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- }
- if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
- dev_printk(KERN_INFO, &rphy->dev, "add: handle(0x%04x), "
- "sas_addr(0x%016llx)\n", handle,
- (unsigned long long)
- mpt2sas_port->remote_identify.sas_address);
- mpt2sas_port->rphy = rphy;
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- list_add_tail(&mpt2sas_port->port_list, &sas_node->sas_port_list);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-
- /* fill in report manufacture */
- if (mpt2sas_port->remote_identify.device_type ==
- MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
- mpt2sas_port->remote_identify.device_type ==
- MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
- _transport_expander_report_manufacture(ioc,
- mpt2sas_port->remote_identify.sas_address,
- rphy_to_expander_device(rphy));
-
- return mpt2sas_port;
-
- out_fail:
- list_for_each_entry_safe(mpt2sas_phy, next, &mpt2sas_port->phy_list,
- port_siblings)
- list_del(&mpt2sas_phy->port_siblings);
- kfree(mpt2sas_port);
- return NULL;
-}
-
-/**
- * mpt2sas_transport_port_remove - remove port from the list
- * @ioc: per adapter object
- * @sas_address: sas address of attached device
- * @sas_address_parent: sas address of parent expander or sas host
- * Context: This function will acquire ioc->sas_node_lock.
- *
- * Removing object and freeing associated memory from the
- * ioc->sas_port_list.
- *
- * Return nothing.
- */
-void
-mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u64 sas_address_parent)
-{
- int i;
- unsigned long flags;
- struct _sas_port *mpt2sas_port, *next;
- struct _sas_node *sas_node;
- u8 found = 0;
- struct _sas_phy *mpt2sas_phy, *next_phy;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_sas_address(ioc,
- sas_address_parent);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- if (!sas_node)
- return;
- list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list,
- port_list) {
- if (mpt2sas_port->remote_identify.sas_address != sas_address)
- continue;
- found = 1;
- list_del(&mpt2sas_port->port_list);
- goto out;
- }
- out:
- if (!found)
- return;
-
- for (i = 0; i < sas_node->num_phys; i++) {
- if (sas_node->phy[i].remote_identify.sas_address == sas_address)
- memset(&sas_node->phy[i].remote_identify, 0 ,
- sizeof(struct sas_identify));
- }
-
- list_for_each_entry_safe(mpt2sas_phy, next_phy,
- &mpt2sas_port->phy_list, port_siblings) {
- if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
- dev_printk(KERN_INFO, &mpt2sas_port->port->dev,
- "remove: sas_addr(0x%016llx), phy(%d)\n",
- (unsigned long long)
- mpt2sas_port->remote_identify.sas_address,
- mpt2sas_phy->phy_id);
- mpt2sas_phy->phy_belongs_to_port = 0;
- sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy);
- list_del(&mpt2sas_phy->port_siblings);
- }
- sas_port_delete(mpt2sas_port->port);
- kfree(mpt2sas_port);
-}
-
-/**
- * mpt2sas_transport_add_host_phy - report sas_host phy to transport
- * @ioc: per adapter object
- * @mpt2sas_phy: mpt2sas per phy object
- * @phy_pg0: sas phy page 0
- * @parent_dev: parent device class object
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
- *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev)
-{
- struct sas_phy *phy;
- int phy_index = mpt2sas_phy->phy_id;
-
-
- INIT_LIST_HEAD(&mpt2sas_phy->port_siblings);
- phy = sas_phy_alloc(parent_dev, phy_index);
- if (!phy) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
- if ((_transport_set_identify(ioc, mpt2sas_phy->handle,
- &mpt2sas_phy->identify))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
- phy->identify = mpt2sas_phy->identify;
- mpt2sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle);
- if (mpt2sas_phy->attached_handle)
- _transport_set_identify(ioc, mpt2sas_phy->attached_handle,
- &mpt2sas_phy->remote_identify);
- phy->identify.phy_identifier = mpt2sas_phy->phy_id;
- phy->negotiated_linkrate = _transport_convert_phy_link_rate(
- phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
- phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
- phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
- phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
- phy_pg0.HwLinkRate >> 4);
- phy->minimum_linkrate = _transport_convert_phy_link_rate(
- phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
- phy->maximum_linkrate = _transport_convert_phy_link_rate(
- phy_pg0.ProgrammedLinkRate >> 4);
-
- if ((sas_phy_add(phy))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- sas_phy_free(phy);
- return -1;
- }
- if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
- dev_printk(KERN_INFO, &phy->dev,
- "add: handle(0x%04x), sas_addr(0x%016llx)\n"
- "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
- mpt2sas_phy->handle, (unsigned long long)
- mpt2sas_phy->identify.sas_address,
- mpt2sas_phy->attached_handle,
- (unsigned long long)
- mpt2sas_phy->remote_identify.sas_address);
- mpt2sas_phy->phy = phy;
- return 0;
-}
-
-
-/**
- * mpt2sas_transport_add_expander_phy - report expander phy to transport
- * @ioc: per adapter object
- * @mpt2sas_phy: mpt2sas per phy object
- * @expander_pg1: expander page 1
- * @parent_dev: parent device class object
- *
- * Returns 0 for success, non-zero for failure.
- */
-int
-mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
- *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev)
-{
- struct sas_phy *phy;
- int phy_index = mpt2sas_phy->phy_id;
-
- INIT_LIST_HEAD(&mpt2sas_phy->port_siblings);
- phy = sas_phy_alloc(parent_dev, phy_index);
- if (!phy) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
- if ((_transport_set_identify(ioc, mpt2sas_phy->handle,
- &mpt2sas_phy->identify))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -1;
- }
- phy->identify = mpt2sas_phy->identify;
- mpt2sas_phy->attached_handle =
- le16_to_cpu(expander_pg1.AttachedDevHandle);
- if (mpt2sas_phy->attached_handle)
- _transport_set_identify(ioc, mpt2sas_phy->attached_handle,
- &mpt2sas_phy->remote_identify);
- phy->identify.phy_identifier = mpt2sas_phy->phy_id;
- phy->negotiated_linkrate = _transport_convert_phy_link_rate(
- expander_pg1.NegotiatedLinkRate &
- MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
- phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
- expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
- phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
- expander_pg1.HwLinkRate >> 4);
- phy->minimum_linkrate = _transport_convert_phy_link_rate(
- expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
- phy->maximum_linkrate = _transport_convert_phy_link_rate(
- expander_pg1.ProgrammedLinkRate >> 4);
-
- if ((sas_phy_add(phy))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- sas_phy_free(phy);
- return -1;
- }
- if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
- dev_printk(KERN_INFO, &phy->dev,
- "add: handle(0x%04x), sas_addr(0x%016llx)\n"
- "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
- mpt2sas_phy->handle, (unsigned long long)
- mpt2sas_phy->identify.sas_address,
- mpt2sas_phy->attached_handle,
- (unsigned long long)
- mpt2sas_phy->remote_identify.sas_address);
- mpt2sas_phy->phy = phy;
- return 0;
-}
-
-/**
- * mpt2sas_transport_update_links - refreshing phy link changes
- * @ioc: per adapter object
- * @sas_address: sas address of parent expander or sas host
- * @handle: attached device handle
- * @phy_numberv: phy number
- * @link_rate: new link rate
- *
- * Returns nothing.
- */
-void
-mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
- u64 sas_address, u16 handle, u8 phy_number, u8 link_rate)
-{
- unsigned long flags;
- struct _sas_node *sas_node;
- struct _sas_phy *mpt2sas_phy;
-
- if (ioc->shost_recovery || ioc->pci_error_recovery)
- return;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- if (!sas_node)
- return;
-
- mpt2sas_phy = &sas_node->phy[phy_number];
- mpt2sas_phy->attached_handle = handle;
- if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) {
- _transport_set_identify(ioc, handle,
- &mpt2sas_phy->remote_identify);
- _transport_add_phy_to_an_existing_port(ioc, sas_node,
- mpt2sas_phy, mpt2sas_phy->remote_identify.sas_address);
- } else
- memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct
- sas_identify));
-
- if (mpt2sas_phy->phy)
- mpt2sas_phy->phy->negotiated_linkrate =
- _transport_convert_phy_link_rate(link_rate);
-
- if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
- dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
- "refresh: parent sas_addr(0x%016llx),\n"
- "\tlink_rate(0x%02x), phy(%d)\n"
- "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
- (unsigned long long)sas_address,
- link_rate, phy_number, handle, (unsigned long long)
- mpt2sas_phy->remote_identify.sas_address);
-}
-
-static inline void *
-phy_to_ioc(struct sas_phy *phy)
-{
- struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
- return shost_priv(shost);
-}
-
-static inline void *
-rphy_to_ioc(struct sas_rphy *rphy)
-{
- struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
- return shost_priv(shost);
-}
-
-
-/* report phy error log structure */
-struct phy_error_log_request{
- u8 smp_frame_type; /* 0x40 */
- u8 function; /* 0x11 */
- u8 allocated_response_length;
- u8 request_length; /* 02 */
- u8 reserved_1[5];
- u8 phy_identifier;
- u8 reserved_2[2];
-};
-
-/* report phy error log reply structure */
-struct phy_error_log_reply{
- u8 smp_frame_type; /* 0x41 */
- u8 function; /* 0x11 */
- u8 function_result;
- u8 response_length;
- u16 expander_change_count;
- u8 reserved_1[3];
- u8 phy_identifier;
- u8 reserved_2[2];
- u32 invalid_dword;
- u32 running_disparity_error;
- u32 loss_of_dword_sync;
- u32 phy_reset_problem;
-};
-
-/**
- * _transport_get_expander_phy_error_log - return expander counters
- * @ioc: per adapter object
- * @phy: The sas phy object
- *
- * Returns 0 for success, non-zero for failure.
- *
- */
-static int
-_transport_get_expander_phy_error_log(struct MPT2SAS_ADAPTER *ioc,
- struct sas_phy *phy)
-{
- Mpi2SmpPassthroughRequest_t *mpi_request;
- Mpi2SmpPassthroughReply_t *mpi_reply;
- struct phy_error_log_request *phy_error_log_request;
- struct phy_error_log_reply *phy_error_log_reply;
- int rc;
- u16 smid;
- u32 ioc_state;
- unsigned long timeleft;
- void *psge;
- u32 sgl_flags;
- u8 issue_reset = 0;
- void *data_out = NULL;
- dma_addr_t data_out_dma;
- u32 sz;
- u64 *sas_address_le;
- u16 wait_state_count;
-
- if (ioc->shost_recovery || ioc->pci_error_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
- return -EFAULT;
- }
-
- mutex_lock(&ioc->transport_cmds.mutex);
-
- if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
- ioc->transport_cmds.status = MPT2_CMD_PENDING;
-
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == 10) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
- if (wait_state_count)
- printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
- ioc->name, __func__);
-
- smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->transport_cmds.smid = smid;
-
- sz = sizeof(struct phy_error_log_request) +
- sizeof(struct phy_error_log_reply);
- data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma);
- if (!data_out) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- rc = -ENOMEM;
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
-
- rc = -EINVAL;
- memset(data_out, 0, sz);
- phy_error_log_request = data_out;
- phy_error_log_request->smp_frame_type = 0x40;
- phy_error_log_request->function = 0x11;
- phy_error_log_request->request_length = 2;
- phy_error_log_request->allocated_response_length = 0;
- phy_error_log_request->phy_identifier = phy->number;
-
- memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
- mpi_request->PhysicalPort = 0xFF;
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
- sas_address_le = (u64 *)&mpi_request->SASAddress;
- *sas_address_le = cpu_to_le64(phy->identify.sas_address);
- mpi_request->RequestDataLength =
- cpu_to_le16(sizeof(struct phy_error_log_request));
- psge = &mpi_request->SGL;
-
- /* WRITE sgel first */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- sizeof(struct phy_error_log_request), data_out_dma);
-
- /* incr sgel */
- psge += ioc->sge_size;
-
- /* READ sgel last */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- sizeof(struct phy_error_log_reply), data_out_dma +
- sizeof(struct phy_error_log_request));
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_error_log - "
- "send to sas_addr(0x%016llx), phy(%d)\n", ioc->name,
- (unsigned long long)phy->identify.sas_address, phy->number));
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->transport_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
- 10*HZ);
-
- if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SmpPassthroughRequest_t)/4);
- if (!(ioc->transport_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_error_log - "
- "complete\n", ioc->name));
-
- if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) {
-
- mpi_reply = ioc->transport_cmds.reply;
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "phy_error_log - reply data transfer size(%d)\n",
- ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength)));
-
- if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
- sizeof(struct phy_error_log_reply))
- goto out;
-
- phy_error_log_reply = data_out +
- sizeof(struct phy_error_log_request);
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "phy_error_log - function_result(%d)\n",
- ioc->name, phy_error_log_reply->function_result));
-
- phy->invalid_dword_count =
- be32_to_cpu(phy_error_log_reply->invalid_dword);
- phy->running_disparity_error_count =
- be32_to_cpu(phy_error_log_reply->running_disparity_error);
- phy->loss_of_dword_sync_count =
- be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
- phy->phy_reset_problem_count =
- be32_to_cpu(phy_error_log_reply->phy_reset_problem);
- rc = 0;
- } else
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "phy_error_log - no reply\n", ioc->name));
-
- issue_host_reset:
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- out:
- ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
- if (data_out)
- pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma);
-
- mutex_unlock(&ioc->transport_cmds.mutex);
- return rc;
-}
-
-/**
- * _transport_get_linkerrors - return phy counters for both hba and expanders
- * @phy: The sas phy object
- *
- * Returns 0 for success, non-zero for failure.
- *
- */
-static int
-_transport_get_linkerrors(struct sas_phy *phy)
-{
- struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
- unsigned long flags;
- Mpi2ConfigReply_t mpi_reply;
- Mpi2SasPhyPage1_t phy_pg1;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- if (_transport_sas_node_find_by_sas_address(ioc,
- phy->identify.sas_address) == NULL) {
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-
- if (phy->identify.sas_address != ioc->sas_hba.sas_address)
- return _transport_get_expander_phy_error_log(ioc, phy);
-
- /* get hba phy error logs */
- if ((mpt2sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1,
- phy->number))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -ENXIO;
- }
-
- if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
- printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status"
- "(0x%04x), loginfo(0x%08x)\n", ioc->name,
- phy->number, le16_to_cpu(mpi_reply.IOCStatus),
- le32_to_cpu(mpi_reply.IOCLogInfo));
-
- phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount);
- phy->running_disparity_error_count =
- le32_to_cpu(phy_pg1.RunningDisparityErrorCount);
- phy->loss_of_dword_sync_count =
- le32_to_cpu(phy_pg1.LossDwordSynchCount);
- phy->phy_reset_problem_count =
- le32_to_cpu(phy_pg1.PhyResetProblemCount);
- return 0;
-}
-
-/**
- * _transport_get_enclosure_identifier -
- * @phy: The sas phy object
- *
- * Obtain the enclosure logical id for an expander.
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
-{
- struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
- struct _sas_device *sas_device;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- rphy->identify.sas_address);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (!sas_device)
- return -ENXIO;
-
- *identifier = sas_device->enclosure_logical_id;
- return 0;
-}
-
-/**
- * _transport_get_bay_identifier -
- * @phy: The sas phy object
- *
- * Returns the slot id for a device that resides inside an enclosure.
- */
-static int
-_transport_get_bay_identifier(struct sas_rphy *rphy)
-{
- struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
- struct _sas_device *sas_device;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
- rphy->identify.sas_address);
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
- if (!sas_device)
- return -ENXIO;
-
- return sas_device->slot;
-}
-
-/* phy control request structure */
-struct phy_control_request{
- u8 smp_frame_type; /* 0x40 */
- u8 function; /* 0x91 */
- u8 allocated_response_length;
- u8 request_length; /* 0x09 */
- u16 expander_change_count;
- u8 reserved_1[3];
- u8 phy_identifier;
- u8 phy_operation;
- u8 reserved_2[13];
- u64 attached_device_name;
- u8 programmed_min_physical_link_rate;
- u8 programmed_max_physical_link_rate;
- u8 reserved_3[6];
-};
-
-/* phy control reply structure */
-struct phy_control_reply{
- u8 smp_frame_type; /* 0x41 */
- u8 function; /* 0x11 */
- u8 function_result;
- u8 response_length;
-};
-
-#define SMP_PHY_CONTROL_LINK_RESET (0x01)
-#define SMP_PHY_CONTROL_HARD_RESET (0x02)
-#define SMP_PHY_CONTROL_DISABLE (0x03)
-
-/**
- * _transport_expander_phy_control - expander phy control
- * @ioc: per adapter object
- * @phy: The sas phy object
- *
- * Returns 0 for success, non-zero for failure.
- *
- */
-static int
-_transport_expander_phy_control(struct MPT2SAS_ADAPTER *ioc,
- struct sas_phy *phy, u8 phy_operation)
-{
- Mpi2SmpPassthroughRequest_t *mpi_request;
- Mpi2SmpPassthroughReply_t *mpi_reply;
- struct phy_control_request *phy_control_request;
- struct phy_control_reply *phy_control_reply;
- int rc;
- u16 smid;
- u32 ioc_state;
- unsigned long timeleft;
- void *psge;
- u32 sgl_flags;
- u8 issue_reset = 0;
- void *data_out = NULL;
- dma_addr_t data_out_dma;
- u32 sz;
- u64 *sas_address_le;
- u16 wait_state_count;
-
- if (ioc->shost_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
- return -EFAULT;
- }
-
- mutex_lock(&ioc->transport_cmds.mutex);
-
- if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
- ioc->transport_cmds.status = MPT2_CMD_PENDING;
-
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == 10) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
- if (wait_state_count)
- printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
- ioc->name, __func__);
-
- smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->transport_cmds.smid = smid;
-
- sz = sizeof(struct phy_control_request) +
- sizeof(struct phy_control_reply);
- data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma);
- if (!data_out) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
- __LINE__, __func__);
- rc = -ENOMEM;
- mpt2sas_base_free_smid(ioc, smid);
- goto out;
- }
-
- rc = -EINVAL;
- memset(data_out, 0, sz);
- phy_control_request = data_out;
- phy_control_request->smp_frame_type = 0x40;
- phy_control_request->function = 0x91;
- phy_control_request->request_length = 9;
- phy_control_request->allocated_response_length = 0;
- phy_control_request->phy_identifier = phy->number;
- phy_control_request->phy_operation = phy_operation;
- phy_control_request->programmed_min_physical_link_rate =
- phy->minimum_linkrate << 4;
- phy_control_request->programmed_max_physical_link_rate =
- phy->maximum_linkrate << 4;
-
- memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
- mpi_request->PhysicalPort = 0xFF;
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
- sas_address_le = (u64 *)&mpi_request->SASAddress;
- *sas_address_le = cpu_to_le64(phy->identify.sas_address);
- mpi_request->RequestDataLength =
- cpu_to_le16(sizeof(struct phy_error_log_request));
- psge = &mpi_request->SGL;
-
- /* WRITE sgel first */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- sizeof(struct phy_control_request), data_out_dma);
-
- /* incr sgel */
- psge += ioc->sge_size;
-
- /* READ sgel last */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- ioc->base_add_sg_single(psge, sgl_flags |
- sizeof(struct phy_control_reply), data_out_dma +
- sizeof(struct phy_control_request));
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_control - "
- "send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n", ioc->name,
- (unsigned long long)phy->identify.sas_address, phy->number,
- phy_operation));
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->transport_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
- 10*HZ);
-
- if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s: timeout\n",
- ioc->name, __func__);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SmpPassthroughRequest_t)/4);
- if (!(ioc->transport_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_control - "
- "complete\n", ioc->name));
-
- if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) {
-
- mpi_reply = ioc->transport_cmds.reply;
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "phy_control - reply data transfer size(%d)\n",
- ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength)));
-
- if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
- sizeof(struct phy_control_reply))
- goto out;
-
- phy_control_reply = data_out +
- sizeof(struct phy_control_request);
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "phy_control - function_result(%d)\n",
- ioc->name, phy_control_reply->function_result));
-
- rc = 0;
- } else
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "phy_control - no reply\n", ioc->name));
-
- issue_host_reset:
- if (issue_reset)
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- out:
- ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
- if (data_out)
- pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma);
-
- mutex_unlock(&ioc->transport_cmds.mutex);
- return rc;
-}
-
-/**
- * _transport_phy_reset -
- * @phy: The sas phy object
- * @hard_reset:
- *
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_transport_phy_reset(struct sas_phy *phy, int hard_reset)
-{
- struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
- Mpi2SasIoUnitControlReply_t mpi_reply;
- Mpi2SasIoUnitControlRequest_t mpi_request;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- if (_transport_sas_node_find_by_sas_address(ioc,
- phy->identify.sas_address) == NULL) {
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-
- /* handle expander phys */
- if (phy->identify.sas_address != ioc->sas_hba.sas_address)
- return _transport_expander_phy_control(ioc, phy,
- (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
- SMP_PHY_CONTROL_LINK_RESET);
-
- /* handle hba phys */
- memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlReply_t));
- mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
- mpi_request.Operation = hard_reset ?
- MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET;
- mpi_request.PhyNum = phy->number;
-
- if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- return -ENXIO;
- }
-
- if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
- printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status"
- "(0x%04x), loginfo(0x%08x)\n", ioc->name,
- phy->number, le16_to_cpu(mpi_reply.IOCStatus),
- le32_to_cpu(mpi_reply.IOCLogInfo));
-
- return 0;
-}
-
-/**
- * _transport_phy_enable - enable/disable phys
- * @phy: The sas phy object
- * @enable: enable phy when true
- *
- * Only support sas_host direct attached phys.
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_transport_phy_enable(struct sas_phy *phy, int enable)
-{
- struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
- Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
- Mpi2ConfigReply_t mpi_reply;
- u16 ioc_status;
- u16 sz;
- int rc = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- if (_transport_sas_node_find_by_sas_address(ioc,
- phy->identify.sas_address) == NULL) {
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-
- /* handle expander phys */
- if (phy->identify.sas_address != ioc->sas_hba.sas_address)
- return _transport_expander_phy_control(ioc, phy,
- (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
- SMP_PHY_CONTROL_DISABLE);
-
- /* handle hba phys */
-
- /* sas_iounit page 1 */
- sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
- sizeof(Mpi2SasIOUnit1PhyData_t));
- sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
- if (!sas_iounit_pg1) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -ENOMEM;
- goto out;
- }
- if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
- sas_iounit_pg1, sz))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -ENXIO;
- goto out;
- }
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -EIO;
- goto out;
- }
-
- if (enable)
- sas_iounit_pg1->PhyData[phy->number].PhyFlags
- &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
- else
- sas_iounit_pg1->PhyData[phy->number].PhyFlags
- |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
-
- mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);
-
- /* link reset */
- if (enable)
- _transport_phy_reset(phy, 0);
-
- out:
- kfree(sas_iounit_pg1);
- return rc;
-}
-
-/**
- * _transport_phy_speed - set phy min/max link rates
- * @phy: The sas phy object
- * @rates: rates defined in sas_phy_linkrates
- *
- * Only support sas_host direct attached phys.
- * Returns 0 for success, non-zero for failure.
- */
-static int
-_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
-{
- struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
- Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
- Mpi2SasPhyPage0_t phy_pg0;
- Mpi2ConfigReply_t mpi_reply;
- u16 ioc_status;
- u16 sz;
- int i;
- int rc = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- if (_transport_sas_node_find_by_sas_address(ioc,
- phy->identify.sas_address) == NULL) {
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-
- if (!rates->minimum_linkrate)
- rates->minimum_linkrate = phy->minimum_linkrate;
- else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
- rates->minimum_linkrate = phy->minimum_linkrate_hw;
-
- if (!rates->maximum_linkrate)
- rates->maximum_linkrate = phy->maximum_linkrate;
- else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
- rates->maximum_linkrate = phy->maximum_linkrate_hw;
-
- /* handle expander phys */
- if (phy->identify.sas_address != ioc->sas_hba.sas_address) {
- phy->minimum_linkrate = rates->minimum_linkrate;
- phy->maximum_linkrate = rates->maximum_linkrate;
- return _transport_expander_phy_control(ioc, phy,
- SMP_PHY_CONTROL_LINK_RESET);
- }
-
- /* handle hba phys */
-
- /* sas_iounit page 1 */
- sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
- sizeof(Mpi2SasIOUnit1PhyData_t));
- sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
- if (!sas_iounit_pg1) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -ENOMEM;
- goto out;
- }
- if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
- sas_iounit_pg1, sz))) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -ENXIO;
- goto out;
- }
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -EIO;
- goto out;
- }
-
- for (i = 0; i < ioc->sas_hba.num_phys; i++) {
- if (phy->number != i) {
- sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
- (ioc->sas_hba.phy[i].phy->minimum_linkrate +
- (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
- } else {
- sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
- (rates->minimum_linkrate +
- (rates->maximum_linkrate << 4));
- }
- }
-
- if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
- sz)) {
- printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
- ioc->name, __FILE__, __LINE__, __func__);
- rc = -ENXIO;
- goto out;
- }
-
- /* link reset */
- _transport_phy_reset(phy, 0);
-
- /* read phy page 0, then update the rates in the sas transport phy */
- if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
- phy->number)) {
- phy->minimum_linkrate = _transport_convert_phy_link_rate(
- phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
- phy->maximum_linkrate = _transport_convert_phy_link_rate(
- phy_pg0.ProgrammedLinkRate >> 4);
- phy->negotiated_linkrate = _transport_convert_phy_link_rate(
- phy_pg0.NegotiatedLinkRate &
- MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
- }
-
- out:
- kfree(sas_iounit_pg1);
- return rc;
-}
-
-
-/**
- * _transport_smp_handler - transport portal for smp passthru
- * @shost: shost object
- * @rphy: sas transport rphy object
- * @req:
- *
- * This used primarily for smp_utils.
- * Example:
- * smp_rep_general /sys/class/bsg/expander-5:0
- */
-static int
-_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
- struct request *req)
-{
- struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
- Mpi2SmpPassthroughRequest_t *mpi_request;
- Mpi2SmpPassthroughReply_t *mpi_reply;
- int rc;
- u16 smid;
- u32 ioc_state;
- unsigned long timeleft;
- void *psge;
- u32 sgl_flags;
- u8 issue_reset = 0;
- dma_addr_t dma_addr_in = 0;
- dma_addr_t dma_addr_out = 0;
- u16 wait_state_count;
- struct request *rsp = req->next_rq;
-
- if (!rsp) {
- printk(MPT2SAS_ERR_FMT "%s: the smp response space is "
- "missing\n", ioc->name, __func__);
- return -EINVAL;
- }
-
- /* do we need to support multiple segments? */
- if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
- printk(MPT2SAS_ERR_FMT "%s: multiple segments req %u %u, "
- "rsp %u %u\n", ioc->name, __func__, req->bio->bi_vcnt,
- blk_rq_bytes(req), rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
- return -EINVAL;
- }
-
- if (ioc->shost_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
- return -EFAULT;
- }
-
- rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
- if (rc)
- return rc;
-
- if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", ioc->name,
- __func__);
- rc = -EAGAIN;
- goto out;
- }
- ioc->transport_cmds.status = MPT2_CMD_PENDING;
-
- wait_state_count = 0;
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
- if (wait_state_count++ == 10) {
- printk(MPT2SAS_ERR_FMT
- "%s: failed due to ioc not operational\n",
- ioc->name, __func__);
- rc = -EFAULT;
- goto out;
- }
- ssleep(1);
- ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
- printk(MPT2SAS_INFO_FMT "%s: waiting for "
- "operational state(count=%d)\n", ioc->name,
- __func__, wait_state_count);
- }
- if (wait_state_count)
- printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
- ioc->name, __func__);
-
- smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx);
- if (!smid) {
- printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
- rc = 0;
- mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
- ioc->transport_cmds.smid = smid;
-
- memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
- mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
- mpi_request->PhysicalPort = 0xFF;
- mpi_request->VF_ID = 0; /* TODO */
- mpi_request->VP_ID = 0;
- *((u64 *)&mpi_request->SASAddress) = (rphy) ?
- cpu_to_le64(rphy->identify.sas_address) :
- cpu_to_le64(ioc->sas_hba.sas_address);
- mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4);
- psge = &mpi_request->SGL;
-
- /* WRITE sgel first */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
- blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
- if (!dma_addr_out) {
- mpt2sas_base_free_smid(ioc, smid);
- goto unmap;
- }
-
- ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(req) - 4),
- dma_addr_out);
-
- /* incr sgel */
- psge += ioc->sge_size;
-
- /* READ sgel last */
- sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
- MPI2_SGE_FLAGS_END_OF_LIST);
- sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
- dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
- blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
- if (!dma_addr_in) {
- mpt2sas_base_free_smid(ioc, smid);
- goto unmap;
- }
-
- ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(rsp) + 4),
- dma_addr_in);
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - "
- "sending smp request\n", ioc->name, __func__));
-
- mpt2sas_base_put_smid_default(ioc, smid);
- init_completion(&ioc->transport_cmds.done);
- timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
- 10*HZ);
-
- if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) {
- printk(MPT2SAS_ERR_FMT "%s : timeout\n",
- __func__, ioc->name);
- _debug_dump_mf(mpi_request,
- sizeof(Mpi2SmpPassthroughRequest_t)/4);
- if (!(ioc->transport_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
- }
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - "
- "complete\n", ioc->name, __func__));
-
- if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) {
-
- mpi_reply = ioc->transport_cmds.reply;
-
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "%s - reply data transfer size(%d)\n",
- ioc->name, __func__,
- le16_to_cpu(mpi_reply->ResponseDataLength)));
-
- memcpy(req->sense, mpi_reply, sizeof(*mpi_reply));
- req->sense_len = sizeof(*mpi_reply);
- req->resid_len = 0;
- rsp->resid_len -=
- le16_to_cpu(mpi_reply->ResponseDataLength);
- } else {
- dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
- "%s - no reply\n", ioc->name, __func__));
- rc = -ENXIO;
- }
-
- issue_host_reset:
- if (issue_reset) {
- mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
- FORCE_BIG_HAMMER);
- rc = -ETIMEDOUT;
- }
-
- unmap:
- if (dma_addr_out)
- pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req),
- PCI_DMA_BIDIRECTIONAL);
- if (dma_addr_in)
- pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp),
- PCI_DMA_BIDIRECTIONAL);
-
- out:
- ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
- mutex_unlock(&ioc->transport_cmds.mutex);
- return rc;
-}
-
-struct sas_function_template mpt2sas_transport_functions = {
- .get_linkerrors = _transport_get_linkerrors,
- .get_enclosure_identifier = _transport_get_enclosure_identifier,
- .get_bay_identifier = _transport_get_bay_identifier,
- .phy_reset = _transport_phy_reset,
- .phy_enable = _transport_phy_enable,
- .set_phy_speed = _transport_phy_speed,
- .smp_handler = _transport_smp_handler,
-};
-
-struct scsi_transport_template *mpt2sas_transport_template;
diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt3sas/Kconfig
index bbb7e4bf30a3..c299f7e078fb 100644
--- a/drivers/scsi/mpt2sas/Kconfig
+++ b/drivers/scsi/mpt3sas/Kconfig
@@ -1,8 +1,8 @@
#
-# Kernel configuration file for the MPT2SAS
+# Kernel configuration file for the MPT3SAS
#
-# This code is based on drivers/scsi/mpt2sas/Kconfig
-# Copyright (C) 2007-2010 LSI Corporation
+# This code is based on drivers/scsi/mpt3sas/Kconfig
+# Copyright (C) 2012-2014 LSI Corporation
# (mailto:DL-MPTFusionLinux@lsi.com)
# This program is free software; you can redistribute it and/or
@@ -40,28 +40,44 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
-config SCSI_MPT2SAS
- tristate "LSI MPT Fusion SAS 2.0 Device Driver"
+config SCSI_MPT3SAS
+ tristate "LSI MPT Fusion SAS 3.0 & SAS 2.0 Device Driver"
depends on PCI && SCSI
select SCSI_SAS_ATTRS
select RAID_ATTRS
- ---help---
- This driver supports PCI-Express SAS 6Gb/s Host Adapters.
+ select IRQ_POLL
+ help
+ This driver supports PCI-Express SAS 12Gb/s Host Adapters.
config SCSI_MPT2SAS_MAX_SGE
- int "LSI MPT Fusion Max number of SG Entries (16 - 128)"
- depends on PCI && SCSI && SCSI_MPT2SAS
+ int "LSI MPT Fusion SAS 2.0 Max number of SG Entries (16 - 256)"
+ depends on PCI && SCSI && SCSI_MPT3SAS
default "128"
- range 16 128
- ---help---
+ range 16 256
+ help
This option allows you to specify the maximum number of scatter-
gather entries per I/O. The driver default is 128, which matches
- SAFE_PHYS_SEGMENTS. However, it may decreased down to 16.
- Decreasing this parameter will reduce memory requirements
- on a per controller instance.
+ MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this
+ can be 256. However, it may decreased down to 16. Decreasing this
+ parameter will reduce memory requirements on a per controller instance.
-config SCSI_MPT2SAS_LOGGING
- bool "LSI MPT Fusion logging facility"
- depends on PCI && SCSI && SCSI_MPT2SAS
- ---help---
- This turns on a logging facility.
+config SCSI_MPT3SAS_MAX_SGE
+ int "LSI MPT Fusion SAS 3.0 Max number of SG Entries (16 - 256)"
+ depends on PCI && SCSI && SCSI_MPT3SAS
+ default "128"
+ range 16 256
+ help
+ This option allows you to specify the maximum number of scatter-
+ gather entries per I/O. The driver default is 128, which matches
+ MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this
+ can be 256. However, it may decreased down to 16. Decreasing this
+ parameter will reduce memory requirements on a per controller instance.
+
+config SCSI_MPT2SAS
+ tristate "Legacy MPT2SAS config option"
+ default n
+ select SCSI_MPT3SAS
+ depends on PCI && SCSI
+ help
+ Dummy config option for backwards compatibility: configure the MPT3SAS
+ driver instead.
diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile
new file mode 100644
index 000000000000..e76d994dbed3
--- /dev/null
+++ b/drivers/scsi/mpt3sas/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+# mpt3sas makefile
+obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas.o
+mpt3sas-y += mpt3sas_base.o \
+ mpt3sas_config.o \
+ mpt3sas_scsih.o \
+ mpt3sas_transport.o \
+ mpt3sas_ctl.o \
+ mpt3sas_trigger_diag.o \
+ mpt3sas_warpdrive.o \
+ mpt3sas_debugfs.o \
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2.h b/drivers/scsi/mpt3sas/mpi/mpi2.h
new file mode 100644
index 000000000000..b181b113fc80
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2.h
@@ -0,0 +1,1307 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2.h
+ * Title: MPI Message independent structures and definitions
+ * including System Interface Register Set and
+ * scatter/gather formats.
+ * Creation Date: June 21, 2006
+ *
+ * mpi2.h Version: 02.00.54
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Moved ReplyPostHostIndex register to offset 0x6C of the
+ * MPI2_SYSTEM_INTERFACE_REGS and modified the define for
+ * MPI2_REPLY_POST_HOST_INDEX_OFFSET.
+ * Added union of request descriptors.
+ * Added union of reply descriptors.
+ * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added define for MPI2_VERSION_02_00.
+ * Fixed the size of the FunctionDependent5 field in the
+ * MPI2_DEFAULT_REPLY structure.
+ * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Removed the MPI-defined Fault Codes and extended the
+ * product specific codes up to 0xEFFF.
+ * Added a sixth key value for the WriteSequence register
+ * and changed the flush value to 0x0.
+ * Added message function codes for Diagnostic Buffer Post
+ * and Diagnsotic Release.
+ * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED
+ * Moved MPI2_VERSION_UNION from mpi2_ioc.h.
+ * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added #defines for marking a reply descriptor as unused.
+ * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Moved LUN field defines from mpi2_init.h.
+ * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT.
+ * In all request and reply descriptors, replaced VF_ID
+ * field with MSIxIndex field.
+ * Removed DevHandle field from
+ * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those
+ * bytes reserved.
+ * Added RAID Accelerator functionality.
+ * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MSI-x index mask and shift for Reply Post Host
+ * Index register.
+ * Added function code for Host Based Discovery Action.
+ * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL.
+ * Added defines for product-specific range of message
+ * function codes, 0xF0 to 0xFF.
+ * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added alternative defines for the SGE Direction bit.
+ * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define.
+ * 02-23-11 02.00.19 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI2_FUNCTION_SEND_HOST_MESSAGE.
+ * 03-09-11 02.00.20 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-25-11 02.00.21 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-24-11 02.00.22 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-18-11 02.00.23 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Incorporating additions for MPI v2.5.
+ * 02-06-12 02.00.24 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 03-29-12 02.00.25 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added Hard Reset delay timings.
+ * 07-10-12 02.00.26 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 07-26-12 02.00.27 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-27-12 02.00.28 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 12-20-12 02.00.29 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET.
+ * 04-09-13 02.00.30 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 04-17-13 02.00.31 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-19-13 02.00.32 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 12-05-13 02.00.33 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 01-08-14 02.00.34 Bumped MPI2_HEADER_VERSION_UNIT
+ * 06-13-14 02.00.35 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-18-14 02.00.36 Updated copyright information.
+ * Bumped MPI2_HEADER_VERSION_UNIT.
+ * 03-16-15 02.00.37 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added Scratchpad registers to
+ * MPI2_SYSTEM_INTERFACE_REGS.
+ * Added MPI2_DIAG_SBR_RELOAD.
+ * 03-19-15 02.00.38 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-25-15 02.00.39 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-25-15 02.00.40 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 12-15-15 02.00.41 Bumped MPI_HEADER_VERSION_UNIT
+ * 01-01-16 02.00.42 Bumped MPI_HEADER_VERSION_UNIT
+ * 04-05-16 02.00.43 Modified MPI26_DIAG_BOOT_DEVICE_SELECT defines
+ * to be unique within first 32 characters.
+ * Removed AHCI support.
+ * Removed SOP support.
+ * Bumped MPI2_HEADER_VERSION_UNIT.
+ * 04-10-16 02.00.44 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 07-06-16 02.00.45 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 09-02-16 02.00.46 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-23-16 02.00.47 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 02-03-17 02.00.48 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 06-13-17 02.00.49 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 09-29-17 02.00.50 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 07-22-18 02.00.51 Added SECURE_BOOT define.
+ * Bumped MPI2_HEADER_VERSION_UNIT
+ * 08-15-18 02.00.52 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-28-18 02.00.53 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI2_IOCSTATUS_FAILURE
+ * 12-17-18 02.00.54 Bumped MPI2_HEADER_VERSION_UNIT
+ * 06-24-19 02.00.55 Bumped MPI2_HEADER_VERSION_UNIT
+ * 08-01-19 02.00.56 Bumped MPI2_HEADER_VERSION_UNIT
+ * 10-02-19 02.00.57 Bumped MPI2_HEADER_VERSION_UNIT
+ * 07-20-20 02.00.58 Bumped MPI2_HEADER_VERSION_UNIT
+ * 03-30-21 02.00.59 Bumped MPI2_HEADER_VERSION_UNIT
+ * 06-03-22 02.00.60 Bumped MPI2_HEADER_VERSION_UNIT
+ * 09-20-23 02.00.61 Bumped MPI2_HEADER_VERSION_UNIT
+ * 09-13-24 02.00.62 Bumped MPI2_HEADER_VERSION_UNIT
+ * Added MPI2_FUNCTION_MCTP_PASSTHROUGH
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_H
+#define MPI2_H
+
+/*****************************************************************************
+*
+* MPI Version Definitions
+*
+*****************************************************************************/
+
+#define MPI2_VERSION_MAJOR_MASK (0xFF00)
+#define MPI2_VERSION_MAJOR_SHIFT (8)
+#define MPI2_VERSION_MINOR_MASK (0x00FF)
+#define MPI2_VERSION_MINOR_SHIFT (0)
+
+/*major version for all MPI v2.x */
+#define MPI2_VERSION_MAJOR (0x02)
+
+/*minor version for MPI v2.0 compatible products */
+#define MPI2_VERSION_MINOR (0x00)
+#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \
+ MPI2_VERSION_MINOR)
+#define MPI2_VERSION_02_00 (0x0200)
+
+/*minor version for MPI v2.5 compatible products */
+#define MPI25_VERSION_MINOR (0x05)
+#define MPI25_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \
+ MPI25_VERSION_MINOR)
+#define MPI2_VERSION_02_05 (0x0205)
+
+/*minor version for MPI v2.6 compatible products */
+#define MPI26_VERSION_MINOR (0x06)
+#define MPI26_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \
+ MPI26_VERSION_MINOR)
+#define MPI2_VERSION_02_06 (0x0206)
+
+
+/* Unit and Dev versioning for this MPI header set */
+#define MPI2_HEADER_VERSION_UNIT (0x3E)
+#define MPI2_HEADER_VERSION_DEV (0x00)
+#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
+#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
+#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF)
+#define MPI2_HEADER_VERSION_DEV_SHIFT (0)
+#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | \
+ MPI2_HEADER_VERSION_DEV)
+
+/*****************************************************************************
+*
+* IOC State Definitions
+*
+*****************************************************************************/
+
+#define MPI2_IOC_STATE_RESET (0x00000000)
+#define MPI2_IOC_STATE_READY (0x10000000)
+#define MPI2_IOC_STATE_OPERATIONAL (0x20000000)
+#define MPI2_IOC_STATE_FAULT (0x40000000)
+#define MPI2_IOC_STATE_COREDUMP (0x50000000)
+
+#define MPI2_IOC_STATE_MASK (0xF0000000)
+#define MPI2_IOC_STATE_SHIFT (28)
+
+/*Fault state range for prodcut specific codes */
+#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000)
+#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF)
+
+/*****************************************************************************
+*
+* System Interface Register Definitions
+*
+*****************************************************************************/
+
+typedef struct _MPI2_SYSTEM_INTERFACE_REGS {
+ U32 Doorbell; /*0x00 */
+ U32 WriteSequence; /*0x04 */
+ U32 HostDiagnostic; /*0x08 */
+ U32 Reserved1; /*0x0C */
+ U32 DiagRWData; /*0x10 */
+ U32 DiagRWAddressLow; /*0x14 */
+ U32 DiagRWAddressHigh; /*0x18 */
+ U32 Reserved2[5]; /*0x1C */
+ U32 HostInterruptStatus; /*0x30 */
+ U32 HostInterruptMask; /*0x34 */
+ U32 DCRData; /*0x38 */
+ U32 DCRAddress; /*0x3C */
+ U32 Reserved3[2]; /*0x40 */
+ U32 ReplyFreeHostIndex; /*0x48 */
+ U32 Reserved4[8]; /*0x4C */
+ U32 ReplyPostHostIndex; /*0x6C */
+ U32 Reserved5; /*0x70 */
+ U32 HCBSize; /*0x74 */
+ U32 HCBAddressLow; /*0x78 */
+ U32 HCBAddressHigh; /*0x7C */
+ U32 Reserved6[12]; /*0x80 */
+ U32 Scratchpad[4]; /*0xB0 */
+ U32 RequestDescriptorPostLow; /*0xC0 */
+ U32 RequestDescriptorPostHigh; /*0xC4 */
+ U32 AtomicRequestDescriptorPost;/*0xC8 */
+ U32 Reserved7[13]; /*0xCC */
+} MPI2_SYSTEM_INTERFACE_REGS,
+ *PTR_MPI2_SYSTEM_INTERFACE_REGS,
+ Mpi2SystemInterfaceRegs_t,
+ *pMpi2SystemInterfaceRegs_t;
+
+/*
+ *Defines for working with the Doorbell register.
+ */
+#define MPI2_DOORBELL_OFFSET (0x00000000)
+
+/*IOC --> System values */
+#define MPI2_DOORBELL_USED (0x08000000)
+#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000)
+#define MPI2_DOORBELL_WHO_INIT_SHIFT (24)
+#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF)
+#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF)
+
+/*System --> IOC values */
+#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000)
+#define MPI2_DOORBELL_FUNCTION_SHIFT (24)
+#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000)
+#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16)
+
+/*
+ *Defines for the WriteSequence register
+ */
+#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004)
+#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F)
+#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0)
+#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF)
+#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4)
+#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB)
+#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2)
+#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7)
+#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD)
+
+/*
+ *Defines for the HostDiagnostic register
+ */
+#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008)
+
+#define MPI26_DIAG_SECURE_BOOT (0x80000000)
+
+#define MPI2_DIAG_SBR_RELOAD (0x00002000)
+
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800)
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000)
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800)
+
+/* Defines for V7A/V7R HostDiagnostic Register */
+#define MPI26_DIAG_BOOT_DEVICE_SEL_64FLASH (0x00000000)
+#define MPI26_DIAG_BOOT_DEVICE_SEL_64HCDW (0x00000800)
+#define MPI26_DIAG_BOOT_DEVICE_SEL_32FLASH (0x00001000)
+#define MPI26_DIAG_BOOT_DEVICE_SEL_32HCDW (0x00001800)
+
+#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400)
+#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200)
+#define MPI2_DIAG_HCB_MODE (0x00000100)
+#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080)
+#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040)
+#define MPI2_DIAG_RESET_HISTORY (0x00000020)
+#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010)
+#define MPI2_DIAG_RESET_ADAPTER (0x00000004)
+#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002)
+
+/*
+ *Offsets for DiagRWData and address
+ */
+#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010)
+#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014)
+#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018)
+
+/*
+ *Defines for the HostInterruptStatus register
+ */
+#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030)
+#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000)
+#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS
+#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000)
+#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008)
+#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001)
+#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS
+
+/*
+ *Defines for the HostInterruptMask register
+ */
+#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034)
+#define MPI2_HIM_RESET_IRQ_MASK (0x40000000)
+#define MPI2_HIM_REPLY_INT_MASK (0x00000008)
+#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK
+#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001)
+#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK
+
+/*
+ *Offsets for DCRData and address
+ */
+#define MPI2_DCR_DATA_OFFSET (0x00000038)
+#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C)
+
+/*
+ *Offset for the Reply Free Queue
+ */
+#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048)
+
+/*
+ *Defines for the Reply Descriptor Post Queue
+ */
+#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C)
+#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF)
+#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000)
+#define MPI2_RPHI_MSIX_INDEX_SHIFT (24)
+#define MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET (0x0000030C) /*MPI v2.5 only*/
+
+
+/*
+ *Defines for the HCBSize and address
+ */
+#define MPI2_HCB_SIZE_OFFSET (0x00000074)
+#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000)
+#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001)
+
+#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078)
+#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C)
+
+/*
+ *Offsets for the Scratchpad registers
+ */
+#define MPI26_SCRATCHPAD0_OFFSET (0x000000B0)
+#define MPI26_SCRATCHPAD1_OFFSET (0x000000B4)
+#define MPI26_SCRATCHPAD2_OFFSET (0x000000B8)
+#define MPI26_SCRATCHPAD3_OFFSET (0x000000BC)
+
+/*
+ *Offsets for the Request Descriptor Post Queue
+ */
+#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0)
+#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4)
+#define MPI26_ATOMIC_REQUEST_DESCRIPTOR_POST_OFFSET (0x000000C8)
+
+/*Hard Reset delay timings */
+#define MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC (50000)
+#define MPI2_HARD_RESET_PCIE_RESET_READ_WINDOW_MICRO_SEC (255000)
+#define MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC (256000)
+
+/*****************************************************************************
+*
+* Message Descriptors
+*
+*****************************************************************************/
+
+/*Request Descriptors */
+
+/*Default Request Descriptor */
+typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 DescriptorTypeDependent; /*0x06 */
+} MPI2_DEFAULT_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR,
+ Mpi2DefaultRequestDescriptor_t,
+ *pMpi2DefaultRequestDescriptor_t;
+
+/*defines for the RequestFlags field */
+#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x1E)
+#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_RSHIFT (1)
+#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00)
+#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02)
+#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06)
+#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08)
+#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A)
+#define MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO (0x0C)
+#define MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED (0x10)
+
+#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01)
+
+/*High Priority Request Descriptor */
+typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 Reserved1; /*0x06 */
+} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
+ Mpi2HighPriorityRequestDescriptor_t,
+ *pMpi2HighPriorityRequestDescriptor_t;
+
+/*SCSI IO Request Descriptor */
+typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 DevHandle; /*0x06 */
+} MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
+ Mpi2SCSIIORequestDescriptor_t,
+ *pMpi2SCSIIORequestDescriptor_t;
+
+/*SCSI Target Request Descriptor */
+typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 IoIndex; /*0x06 */
+} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
+ Mpi2SCSITargetRequestDescriptor_t,
+ *pMpi2SCSITargetRequestDescriptor_t;
+
+/*RAID Accelerator Request Descriptor */
+typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 Reserved; /*0x06 */
+} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR,
+ Mpi2RAIDAcceleratorRequestDescriptor_t,
+ *pMpi2RAIDAcceleratorRequestDescriptor_t;
+
+/*Fast Path SCSI IO Request Descriptor */
+typedef MPI2_SCSI_IO_REQUEST_DESCRIPTOR
+ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR,
+ *PTR_MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR,
+ Mpi25FastPathSCSIIORequestDescriptor_t,
+ *pMpi25FastPathSCSIIORequestDescriptor_t;
+
+/*PCIe Encapsulated Request Descriptor */
+typedef MPI2_SCSI_IO_REQUEST_DESCRIPTOR
+ MPI26_PCIE_ENCAPSULATED_REQUEST_DESCRIPTOR,
+ *PTR_MPI26_PCIE_ENCAPSULATED_REQUEST_DESCRIPTOR,
+ Mpi26PCIeEncapsulatedRequestDescriptor_t,
+ *pMpi26PCIeEncapsulatedRequestDescriptor_t;
+
+/*union of Request Descriptors */
+typedef union _MPI2_REQUEST_DESCRIPTOR_UNION {
+ MPI2_DEFAULT_REQUEST_DESCRIPTOR Default;
+ MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority;
+ MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO;
+ MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget;
+ MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator;
+ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR FastPathSCSIIO;
+ MPI26_PCIE_ENCAPSULATED_REQUEST_DESCRIPTOR PCIeEncapsulated;
+ U64 Words;
+} MPI2_REQUEST_DESCRIPTOR_UNION,
+ *PTR_MPI2_REQUEST_DESCRIPTOR_UNION,
+ Mpi2RequestDescriptorUnion_t,
+ *pMpi2RequestDescriptorUnion_t;
+
+/*Atomic Request Descriptors */
+
+/*
+ * All Atomic Request Descriptors have the same format, so the following
+ * structure is used for all Atomic Request Descriptors:
+ * Atomic Default Request Descriptor
+ * Atomic High Priority Request Descriptor
+ * Atomic SCSI IO Request Descriptor
+ * Atomic SCSI Target Request Descriptor
+ * Atomic RAID Accelerator Request Descriptor
+ * Atomic Fast Path SCSI IO Request Descriptor
+ * Atomic PCIe Encapsulated Request Descriptor
+ */
+
+/*Atomic Request Descriptor */
+typedef struct _MPI26_ATOMIC_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /* 0x00 */
+ U8 MSIxIndex; /* 0x01 */
+ U16 SMID; /* 0x02 */
+} MPI26_ATOMIC_REQUEST_DESCRIPTOR,
+ *PTR_MPI26_ATOMIC_REQUEST_DESCRIPTOR,
+ Mpi26AtomicRequestDescriptor_t,
+ *pMpi26AtomicRequestDescriptor_t;
+
+/*for the RequestFlags field, use the same
+ *defines as MPI2_DEFAULT_REQUEST_DESCRIPTOR
+ */
+
+/*Reply Descriptors */
+
+/*Default Reply Descriptor */
+typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 DescriptorTypeDependent1; /*0x02 */
+ U32 DescriptorTypeDependent2; /*0x04 */
+} MPI2_DEFAULT_REPLY_DESCRIPTOR,
+ *PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR,
+ Mpi2DefaultReplyDescriptor_t,
+ *pMpi2DefaultReplyDescriptor_t;
+
+/*defines for the ReplyFlags field */
+#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F)
+#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00)
+#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01)
+#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02)
+#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03)
+#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05)
+#define MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS (0x06)
+#define MPI26_RPY_DESCRIPT_FLAGS_PCIE_ENCAPSULATED_SUCCESS (0x08)
+#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F)
+
+/*values for marking a reply descriptor as unused */
+#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF)
+#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF)
+
+/*Address Reply Descriptor */
+typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U32 ReplyFrameAddress; /*0x04 */
+} MPI2_ADDRESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR,
+ Mpi2AddressReplyDescriptor_t,
+ *pMpi2AddressReplyDescriptor_t;
+
+#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00)
+
+/*SCSI IO Success Reply Descriptor */
+typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 TaskTag; /*0x04 */
+ U16 Reserved1; /*0x06 */
+} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2SCSIIOSuccessReplyDescriptor_t,
+ *pMpi2SCSIIOSuccessReplyDescriptor_t;
+
+/*TargetAssist Success Reply Descriptor */
+typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U8 SequenceNumber; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 IoIndex; /*0x06 */
+} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2TargetAssistSuccessReplyDescriptor_t,
+ *pMpi2TargetAssistSuccessReplyDescriptor_t;
+
+/*Target Command Buffer Reply Descriptor */
+typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U8 VP_ID; /*0x02 */
+ U8 Flags; /*0x03 */
+ U16 InitiatorDevHandle; /*0x04 */
+ U16 IoIndex; /*0x06 */
+} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
+ *PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
+ Mpi2TargetCommandBufferReplyDescriptor_t,
+ *pMpi2TargetCommandBufferReplyDescriptor_t;
+
+/*defines for Flags field */
+#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F)
+
+/*RAID Accelerator Success Reply Descriptor */
+typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U32 Reserved; /*0x04 */
+} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2RAIDAcceleratorSuccessReplyDescriptor_t,
+ *pMpi2RAIDAcceleratorSuccessReplyDescriptor_t;
+
+/*Fast Path SCSI IO Success Reply Descriptor */
+typedef MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR
+ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi25FastPathSCSIIOSuccessReplyDescriptor_t,
+ *pMpi25FastPathSCSIIOSuccessReplyDescriptor_t;
+
+/*PCIe Encapsulated Success Reply Descriptor */
+typedef MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR
+ MPI26_PCIE_ENCAPSULATED_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI26_PCIE_ENCAPSULATED_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi26PCIeEncapsulatedSuccessReplyDescriptor_t,
+ *pMpi26PCIeEncapsulatedSuccessReplyDescriptor_t;
+
+/*union of Reply Descriptors */
+typedef union _MPI2_REPLY_DESCRIPTORS_UNION {
+ MPI2_DEFAULT_REPLY_DESCRIPTOR Default;
+ MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply;
+ MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess;
+ MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess;
+ MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer;
+ MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess;
+ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR FastPathSCSIIOSuccess;
+ MPI26_PCIE_ENCAPSULATED_SUCCESS_REPLY_DESCRIPTOR
+ PCIeEncapsulatedSuccess;
+ U64 Words;
+} MPI2_REPLY_DESCRIPTORS_UNION,
+ *PTR_MPI2_REPLY_DESCRIPTORS_UNION,
+ Mpi2ReplyDescriptorsUnion_t,
+ *pMpi2ReplyDescriptorsUnion_t;
+
+/*****************************************************************************
+*
+* Message Functions
+*
+*****************************************************************************/
+
+#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00)
+#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01)
+#define MPI2_FUNCTION_IOC_INIT (0x02)
+#define MPI2_FUNCTION_IOC_FACTS (0x03)
+#define MPI2_FUNCTION_CONFIG (0x04)
+#define MPI2_FUNCTION_PORT_FACTS (0x05)
+#define MPI2_FUNCTION_PORT_ENABLE (0x06)
+#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07)
+#define MPI2_FUNCTION_EVENT_ACK (0x08)
+#define MPI2_FUNCTION_FW_DOWNLOAD (0x09)
+#define MPI2_FUNCTION_TARGET_ASSIST (0x0B)
+#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C)
+#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D)
+#define MPI2_FUNCTION_FW_UPLOAD (0x12)
+#define MPI2_FUNCTION_RAID_ACTION (0x15)
+#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16)
+#define MPI2_FUNCTION_TOOLBOX (0x17)
+#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18)
+#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A)
+#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B)
+#define MPI2_FUNCTION_IO_UNIT_CONTROL (0x1B)
+#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C)
+#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D)
+#define MPI2_FUNCTION_DIAG_RELEASE (0x1E)
+#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24)
+#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25)
+#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C)
+#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F)
+#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30)
+#define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31)
+#define MPI2_FUNCTION_NVME_ENCAPSULATED (0x33)
+#define MPI2_FUNCTION_MCTP_PASSTHROUGH (0x34)
+#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0)
+#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF)
+
+/*Doorbell functions */
+#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40)
+#define MPI2_FUNCTION_HANDSHAKE (0x42)
+
+/*****************************************************************************
+*
+* IOC Status Values
+*
+*****************************************************************************/
+
+/*mask for IOCStatus status value */
+#define MPI2_IOCSTATUS_MASK (0x7FFF)
+
+/****************************************************************************
+* Common IOCStatus values for all replies
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SUCCESS (0x0000)
+#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001)
+#define MPI2_IOCSTATUS_BUSY (0x0002)
+#define MPI2_IOCSTATUS_INVALID_SGL (0x0003)
+#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004)
+#define MPI2_IOCSTATUS_INVALID_VPID (0x0005)
+#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006)
+#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007)
+#define MPI2_IOCSTATUS_INVALID_STATE (0x0008)
+#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009)
+/*MPI v2.6 and later */
+#define MPI2_IOCSTATUS_INSUFFICIENT_POWER (0x000A)
+#define MPI2_IOCSTATUS_FAILURE (0x000F)
+
+/****************************************************************************
+* Config IOCStatus values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023)
+#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024)
+#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025)
+
+/****************************************************************************
+* SCSI IO Reply
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040)
+#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042)
+#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043)
+#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044)
+#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045)
+#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046)
+#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047)
+#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048)
+#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049)
+#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A)
+#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B)
+#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C)
+
+/****************************************************************************
+* For use by SCSI Initiator and SCSI Target end-to-end data protection
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D)
+#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E)
+#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F)
+
+/****************************************************************************
+* SCSI Target values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062)
+#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063)
+#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064)
+#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065)
+#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A)
+#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D)
+#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E)
+#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F)
+#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070)
+#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071)
+
+/****************************************************************************
+* Serial Attached SCSI values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090)
+#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091)
+
+/****************************************************************************
+* Diagnostic Buffer Post / Diagnostic Release values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0)
+
+/****************************************************************************
+* RAID Accelerator values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0)
+
+/****************************************************************************
+* IOCStatus flag to indicate that log info is available
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000)
+
+/****************************************************************************
+* IOCLogInfo Types
+****************************************************************************/
+
+#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000)
+#define MPI2_IOCLOGINFO_TYPE_SHIFT (28)
+#define MPI2_IOCLOGINFO_TYPE_NONE (0x0)
+#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1)
+#define MPI2_IOCLOGINFO_TYPE_FC (0x2)
+#define MPI2_IOCLOGINFO_TYPE_SAS (0x3)
+#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4)
+#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF)
+
+/*****************************************************************************
+*
+* Standard Message Structures
+*
+*****************************************************************************/
+
+/****************************************************************************
+*Request Message Header for all request messages
+****************************************************************************/
+
+typedef struct _MPI2_REQUEST_HEADER {
+ U16 FunctionDependent1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 FunctionDependent2; /*0x04 */
+ U8 FunctionDependent3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+} MPI2_REQUEST_HEADER, *PTR_MPI2_REQUEST_HEADER,
+ MPI2RequestHeader_t, *pMPI2RequestHeader_t;
+
+/****************************************************************************
+* Default Reply
+****************************************************************************/
+
+typedef struct _MPI2_DEFAULT_REPLY {
+ U16 FunctionDependent1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 FunctionDependent2; /*0x04 */
+ U8 FunctionDependent3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U16 FunctionDependent5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_DEFAULT_REPLY, *PTR_MPI2_DEFAULT_REPLY,
+ MPI2DefaultReply_t, *pMPI2DefaultReply_t;
+
+/*common version structure/union used in messages and configuration pages */
+
+typedef struct _MPI2_VERSION_STRUCT {
+ U8 Dev; /*0x00 */
+ U8 Unit; /*0x01 */
+ U8 Minor; /*0x02 */
+ U8 Major; /*0x03 */
+} MPI2_VERSION_STRUCT;
+
+typedef union _MPI2_VERSION_UNION {
+ MPI2_VERSION_STRUCT Struct;
+ U32 Word;
+} MPI2_VERSION_UNION;
+
+/*LUN field defines, common to many structures */
+#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI2_LUN_LEVEL_1_WORD (0xFF00)
+#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00)
+
+/*****************************************************************************
+*
+* Fusion-MPT MPI Scatter Gather Elements
+*
+*****************************************************************************/
+
+/****************************************************************************
+* MPI Simple Element structures
+****************************************************************************/
+
+typedef struct _MPI2_SGE_SIMPLE32 {
+ U32 FlagsLength;
+ U32 Address;
+} MPI2_SGE_SIMPLE32, *PTR_MPI2_SGE_SIMPLE32,
+ Mpi2SGESimple32_t, *pMpi2SGESimple32_t;
+
+typedef struct _MPI2_SGE_SIMPLE64 {
+ U32 FlagsLength;
+ U64 Address;
+} MPI2_SGE_SIMPLE64, *PTR_MPI2_SGE_SIMPLE64,
+ Mpi2SGESimple64_t, *pMpi2SGESimple64_t;
+
+typedef struct _MPI2_SGE_SIMPLE_UNION {
+ U32 FlagsLength;
+ union {
+ U32 Address32;
+ U64 Address64;
+ } u;
+} MPI2_SGE_SIMPLE_UNION,
+ *PTR_MPI2_SGE_SIMPLE_UNION,
+ Mpi2SGESimpleUnion_t,
+ *pMpi2SGESimpleUnion_t;
+
+/****************************************************************************
+* MPI Chain Element structures - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_SGE_CHAIN32 {
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ U32 Address;
+} MPI2_SGE_CHAIN32, *PTR_MPI2_SGE_CHAIN32,
+ Mpi2SGEChain32_t, *pMpi2SGEChain32_t;
+
+typedef struct _MPI2_SGE_CHAIN64 {
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ U64 Address;
+} MPI2_SGE_CHAIN64, *PTR_MPI2_SGE_CHAIN64,
+ Mpi2SGEChain64_t, *pMpi2SGEChain64_t;
+
+typedef struct _MPI2_SGE_CHAIN_UNION {
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ union {
+ U32 Address32;
+ U64 Address64;
+ } u;
+} MPI2_SGE_CHAIN_UNION,
+ *PTR_MPI2_SGE_CHAIN_UNION,
+ Mpi2SGEChainUnion_t,
+ *pMpi2SGEChainUnion_t;
+
+/****************************************************************************
+* MPI Transaction Context Element structures - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_SGE_TRANSACTION32 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[1];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION32,
+ *PTR_MPI2_SGE_TRANSACTION32,
+ Mpi2SGETransaction32_t,
+ *pMpi2SGETransaction32_t;
+
+typedef struct _MPI2_SGE_TRANSACTION64 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[2];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION64,
+ *PTR_MPI2_SGE_TRANSACTION64,
+ Mpi2SGETransaction64_t,
+ *pMpi2SGETransaction64_t;
+
+typedef struct _MPI2_SGE_TRANSACTION96 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[3];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION96, *PTR_MPI2_SGE_TRANSACTION96,
+ Mpi2SGETransaction96_t, *pMpi2SGETransaction96_t;
+
+typedef struct _MPI2_SGE_TRANSACTION128 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[4];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION128, *PTR_MPI2_SGE_TRANSACTION128,
+ Mpi2SGETransaction_t128, *pMpi2SGETransaction_t128;
+
+typedef struct _MPI2_SGE_TRANSACTION_UNION {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ union {
+ U32 TransactionContext32[1];
+ U32 TransactionContext64[2];
+ U32 TransactionContext96[3];
+ U32 TransactionContext128[4];
+ } u;
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION_UNION,
+ *PTR_MPI2_SGE_TRANSACTION_UNION,
+ Mpi2SGETransactionUnion_t,
+ *pMpi2SGETransactionUnion_t;
+
+/****************************************************************************
+* MPI SGE union for IO SGL's - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_MPI_SGE_IO_UNION {
+ union {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_CHAIN_UNION Chain;
+ } u;
+} MPI2_MPI_SGE_IO_UNION, *PTR_MPI2_MPI_SGE_IO_UNION,
+ Mpi2MpiSGEIOUnion_t, *pMpi2MpiSGEIOUnion_t;
+
+/****************************************************************************
+* MPI SGE union for SGL's with Simple and Transaction elements - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION {
+ union {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_TRANSACTION_UNION Transaction;
+ } u;
+} MPI2_SGE_TRANS_SIMPLE_UNION,
+ *PTR_MPI2_SGE_TRANS_SIMPLE_UNION,
+ Mpi2SGETransSimpleUnion_t,
+ *pMpi2SGETransSimpleUnion_t;
+
+/****************************************************************************
+* All MPI SGE types union
+****************************************************************************/
+
+typedef struct _MPI2_MPI_SGE_UNION {
+ union {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_CHAIN_UNION Chain;
+ MPI2_SGE_TRANSACTION_UNION Transaction;
+ } u;
+} MPI2_MPI_SGE_UNION, *PTR_MPI2_MPI_SGE_UNION,
+ Mpi2MpiSgeUnion_t, *pMpi2MpiSgeUnion_t;
+
+/****************************************************************************
+* MPI SGE field definition and masks
+****************************************************************************/
+
+/*Flags field bit definitions */
+
+#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80)
+#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40)
+#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30)
+#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08)
+#define MPI2_SGE_FLAGS_DIRECTION (0x04)
+#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02)
+#define MPI2_SGE_FLAGS_END_OF_LIST (0x01)
+
+#define MPI2_SGE_FLAGS_SHIFT (24)
+
+#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF)
+#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF)
+
+/*Element Type */
+
+#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00)
+#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10)
+#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30)
+#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30)
+
+/*Address location */
+
+#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00)
+
+/*Direction */
+
+#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00)
+#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04)
+
+#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST)
+#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC)
+
+/*Address Size */
+
+#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00)
+#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02)
+
+/*Context Size */
+
+#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00)
+#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02)
+#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04)
+#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06)
+
+#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000)
+#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16)
+
+/****************************************************************************
+* MPI SGE operation Macros
+****************************************************************************/
+
+/*SIMPLE FlagsLength manipulations... */
+#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT)
+#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> \
+ MPI2_SGE_FLAGS_SHIFT)
+#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK)
+#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK)
+
+#define MPI2_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_SGE_SET_FLAGS(f) | \
+ MPI2_SGE_LENGTH(l))
+
+#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength)
+#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength)
+#define MPI2_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \
+ MPI2_SGE_SET_FLAGS_LENGTH(f, l))
+
+/*CAUTION - The following are READ-MODIFY-WRITE! */
+#define MPI2_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \
+ MPI2_SGE_SET_FLAGS(f))
+#define MPI2_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \
+ MPI2_SGE_LENGTH(l))
+
+#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> \
+ MPI2_SGE_CHAIN_OFFSET_SHIFT)
+
+/*****************************************************************************
+*
+* Fusion-MPT IEEE Scatter Gather Elements
+*
+*****************************************************************************/
+
+/****************************************************************************
+* IEEE Simple Element structures
+****************************************************************************/
+
+/*MPI2_IEEE_SGE_SIMPLE32 is for MPI v2.0 products only */
+typedef struct _MPI2_IEEE_SGE_SIMPLE32 {
+ U32 Address;
+ U32 FlagsLength;
+} MPI2_IEEE_SGE_SIMPLE32, *PTR_MPI2_IEEE_SGE_SIMPLE32,
+ Mpi2IeeeSgeSimple32_t, *pMpi2IeeeSgeSimple32_t;
+
+typedef struct _MPI2_IEEE_SGE_SIMPLE64 {
+ U64 Address;
+ U32 Length;
+ U16 Reserved1;
+ U8 Reserved2;
+ U8 Flags;
+} MPI2_IEEE_SGE_SIMPLE64, *PTR_MPI2_IEEE_SGE_SIMPLE64,
+ Mpi2IeeeSgeSimple64_t, *pMpi2IeeeSgeSimple64_t;
+
+typedef union _MPI2_IEEE_SGE_SIMPLE_UNION {
+ MPI2_IEEE_SGE_SIMPLE32 Simple32;
+ MPI2_IEEE_SGE_SIMPLE64 Simple64;
+} MPI2_IEEE_SGE_SIMPLE_UNION,
+ *PTR_MPI2_IEEE_SGE_SIMPLE_UNION,
+ Mpi2IeeeSgeSimpleUnion_t,
+ *pMpi2IeeeSgeSimpleUnion_t;
+
+/****************************************************************************
+* IEEE Chain Element structures
+****************************************************************************/
+
+/*MPI2_IEEE_SGE_CHAIN32 is for MPI v2.0 products only */
+typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32;
+
+/*MPI2_IEEE_SGE_CHAIN64 is for MPI v2.0 products only */
+typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64;
+
+typedef union _MPI2_IEEE_SGE_CHAIN_UNION {
+ MPI2_IEEE_SGE_CHAIN32 Chain32;
+ MPI2_IEEE_SGE_CHAIN64 Chain64;
+} MPI2_IEEE_SGE_CHAIN_UNION,
+ *PTR_MPI2_IEEE_SGE_CHAIN_UNION,
+ Mpi2IeeeSgeChainUnion_t,
+ *pMpi2IeeeSgeChainUnion_t;
+
+/*MPI25_IEEE_SGE_CHAIN64 is for MPI v2.5 and later */
+typedef struct _MPI25_IEEE_SGE_CHAIN64 {
+ U64 Address;
+ U32 Length;
+ U16 Reserved1;
+ U8 NextChainOffset;
+ U8 Flags;
+} MPI25_IEEE_SGE_CHAIN64,
+ *PTR_MPI25_IEEE_SGE_CHAIN64,
+ Mpi25IeeeSgeChain64_t,
+ *pMpi25IeeeSgeChain64_t;
+
+/****************************************************************************
+* All IEEE SGE types union
+****************************************************************************/
+
+/*MPI2_IEEE_SGE_UNION is for MPI v2.0 products only */
+typedef struct _MPI2_IEEE_SGE_UNION {
+ union {
+ MPI2_IEEE_SGE_SIMPLE_UNION Simple;
+ MPI2_IEEE_SGE_CHAIN_UNION Chain;
+ } u;
+} MPI2_IEEE_SGE_UNION, *PTR_MPI2_IEEE_SGE_UNION,
+ Mpi2IeeeSgeUnion_t, *pMpi2IeeeSgeUnion_t;
+
+/****************************************************************************
+* IEEE SGE union for IO SGL's
+****************************************************************************/
+
+typedef union _MPI25_SGE_IO_UNION {
+ MPI2_IEEE_SGE_SIMPLE64 IeeeSimple;
+ MPI25_IEEE_SGE_CHAIN64 IeeeChain;
+} MPI25_SGE_IO_UNION, *PTR_MPI25_SGE_IO_UNION,
+ Mpi25SGEIOUnion_t, *pMpi25SGEIOUnion_t;
+
+/****************************************************************************
+* IEEE SGE field definitions and masks
+****************************************************************************/
+
+/*Flags field bit definitions */
+
+#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80)
+#define MPI25_IEEE_SGE_FLAGS_END_OF_LIST (0x40)
+
+#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24)
+
+#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF)
+
+/*Element Type */
+
+#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00)
+#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80)
+
+/*Next Segment Format */
+
+#define MPI26_IEEE_SGE_FLAGS_NSF_MASK (0x1C)
+#define MPI26_IEEE_SGE_FLAGS_NSF_MPI_IEEE (0x00)
+#define MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP (0x08)
+#define MPI26_IEEE_SGE_FLAGS_NSF_NVME_SGL (0x10)
+
+/*Data Location Address Space */
+
+#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00)
+#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01)
+#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02)
+#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR (0x03)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR \
+ (MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR)
+#define MPI26_IEEE_SGE_FLAGS_IOCCTL_ADDR (0x02)
+
+/****************************************************************************
+* IEEE SGE operation Macros
+****************************************************************************/
+
+/*SIMPLE FlagsLength manipulations... */
+#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT)
+#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) \
+ >> MPI2_IEEE32_SGE_FLAGS_SHIFT)
+#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK)
+
+#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) |\
+ MPI2_IEEE32_SGE_LENGTH(l))
+
+#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) \
+ MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength)
+#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) \
+ MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength)
+#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \
+ MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l))
+
+/*CAUTION - The following are READ-MODIFY-WRITE! */
+#define MPI2_IEEE32_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \
+ MPI2_IEEE32_SGE_SET_FLAGS(f))
+#define MPI2_IEEE32_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \
+ MPI2_IEEE32_SGE_LENGTH(l))
+
+/*****************************************************************************
+*
+* Fusion-MPT MPI/IEEE Scatter Gather Unions
+*
+*****************************************************************************/
+
+typedef union _MPI2_SIMPLE_SGE_UNION {
+ MPI2_SGE_SIMPLE_UNION MpiSimple;
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+} MPI2_SIMPLE_SGE_UNION, *PTR_MPI2_SIMPLE_SGE_UNION,
+ Mpi2SimpleSgeUntion_t, *pMpi2SimpleSgeUntion_t;
+
+typedef union _MPI2_SGE_IO_UNION {
+ MPI2_SGE_SIMPLE_UNION MpiSimple;
+ MPI2_SGE_CHAIN_UNION MpiChain;
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain;
+} MPI2_SGE_IO_UNION, *PTR_MPI2_SGE_IO_UNION,
+ Mpi2SGEIOUnion_t, *pMpi2SGEIOUnion_t;
+
+/****************************************************************************
+*
+* Values for SGLFlags field, used in many request messages with an SGL
+*
+****************************************************************************/
+
+/*values for MPI SGL Data Location Address Space subfield */
+#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C)
+#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00)
+#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04)
+#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08)
+#define MPI26_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08)
+#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C)
+/*values for SGL Type subfield */
+#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03)
+#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00)
+#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01)
+#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02)
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
new file mode 100644
index 000000000000..02bf26ca976e
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
@@ -0,0 +1,4013 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2_cnfg.h
+ * Title: MPI Configuration messages and pages
+ * Creation Date: November 10, 2006
+ *
+ * mpi2_cnfg.h Version: 02.00.47
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags.
+ * Added Manufacturing Page 11.
+ * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE
+ * define.
+ * 06-26-07 02.00.02 Adding generic structure for product-specific
+ * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS.
+ * Rework of BIOS Page 2 configuration page.
+ * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the
+ * forms.
+ * Added configuration pages IOC Page 8 and Driver
+ * Persistent Mapping Page 0.
+ * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated
+ * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1,
+ * RAID Physical Disk Pages 0 and 1, RAID Configuration
+ * Page 0).
+ * Added new value for AccessStatus field of SAS Device
+ * Page 0 (_SATA_NEEDS_INITIALIZATION).
+ * 10-31-07 02.00.04 Added missing SEPDevHandle field to
+ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
+ * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for
+ * NVDATA.
+ * Modified IOC Page 7 to use masks and added field for
+ * SASBroadcastPrimitiveMasks.
+ * Added MPI2_CONFIG_PAGE_BIOS_4.
+ * Added MPI2_CONFIG_PAGE_LOG_0.
+ * 02-29-08 02.00.06 Modified various names to make them 32-character unique.
+ * Added SAS Device IDs.
+ * Updated Integrated RAID configuration pages including
+ * Manufacturing Page 4, IOC Page 6, and RAID Configuration
+ * Page 0.
+ * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA.
+ * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION.
+ * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING.
+ * Added missing MaxNumRoutedSasAddresses field to
+ * MPI2_CONFIG_PAGE_EXPANDER_0.
+ * Added SAS Port Page 0.
+ * Modified structure layout for
+ * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0.
+ * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use
+ * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array.
+ * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF
+ * to 0x000000FF.
+ * Added two new values for the Physical Disk Coercion Size
+ * bits in the Flags field of Manufacturing Page 4.
+ * Added product-specific Manufacturing pages 16 to 31.
+ * Modified Flags bits for controlling write cache on SATA
+ * drives in IO Unit Page 1.
+ * Added new bit to AdditionalControlFlags of SAS IO Unit
+ * Page 1 to control Invalid Topology Correction.
+ * Added additional defines for RAID Volume Page 0
+ * VolumeStatusFlags field.
+ * Modified meaning of RAID Volume Page 0 VolumeSettings
+ * define for auto-configure of hot-swap drives.
+ * Added SupportedPhysDisks field to RAID Volume Page 1 and
+ * added related defines.
+ * Added PhysDiskAttributes field (and related defines) to
+ * RAID Physical Disk Page 0.
+ * Added MPI2_SAS_PHYINFO_PHY_VACANT define.
+ * Added three new DiscoveryStatus bits for SAS IO Unit
+ * Page 0 and SAS Expander Page 0.
+ * Removed multiplexing information from SAS IO Unit pages.
+ * Added BootDeviceWaitTime field to SAS IO Unit Page 4.
+ * Removed Zone Address Resolved bit from PhyInfo and from
+ * Expander Page 0 Flags field.
+ * Added two new AccessStatus values to SAS Device Page 0
+ * for indicating routing problems. Added 3 reserved words
+ * to this page.
+ * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3.
+ * Inserted missing reserved field into structure for IOC
+ * Page 6.
+ * Added more pending task bits to RAID Volume Page 0
+ * VolumeStatusFlags defines.
+ * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define.
+ * Added a new DiscoveryStatus bit for SAS IO Unit Page 0
+ * and SAS Expander Page 0 to flag a downstream initiator
+ * when in simplified routing mode.
+ * Removed SATA Init Failure defines for DiscoveryStatus
+ * fields of SAS IO Unit Page 0 and SAS Expander Page 0.
+ * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define.
+ * Added PortGroups, DmaGroup, and ControlGroup fields to
+ * SAS Device Page 0.
+ * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO
+ * Unit Page 6.
+ * Added expander reduced functionality data to SAS
+ * Expander Page 0.
+ * Added SAS PHY Page 2 and SAS PHY Page 3.
+ * 07-30-09 02.00.12 Added IO Unit Page 7.
+ * Added new device ids.
+ * Added SAS IO Unit Page 5.
+ * Added partial and slumber power management capable flags
+ * to SAS Device Page 0 Flags field.
+ * Added PhyInfo defines for power condition.
+ * Added Ethernet configuration pages.
+ * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
+ * Added SAS PHY Page 4 structure and defines.
+ * 02-10-10 02.00.14 Modified the comments for the configuration page
+ * structures that contain an array of data. The host
+ * should use the "count" field in the page data (e.g. the
+ * NumPhys field) to determine the number of valid elements
+ * in the array.
+ * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines.
+ * Added PowerManagementCapabilities to IO Unit Page 7.
+ * Added PortWidthModGroup field to
+ * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines.
+ * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT
+ * define.
+ * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define.
+ * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define.
+ * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing)
+ * defines.
+ * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to
+ * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for
+ * the Pinout field.
+ * Added BoardTemperature and BoardTemperatureUnits fields
+ * to MPI2_CONFIG_PAGE_IO_UNIT_7.
+ * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define
+ * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure.
+ * 02-23-11 02.00.18 Added ProxyVF_ID field to MPI2_CONFIG_REQUEST.
+ * Added IO Unit Page 8, IO Unit Page 9,
+ * and IO Unit Page 10.
+ * Added SASNotifyPrimitiveMasks field to
+ * MPI2_CONFIG_PAGE_IOC_7.
+ * 03-09-11 02.00.19 Fixed IO Unit Page 10 (to match the spec).
+ * 05-25-11 02.00.20 Cleaned up a few comments.
+ * 08-24-11 02.00.21 Marked the IO Unit Page 7 PowerManagementCapabilities
+ * for PCIe link as obsolete.
+ * Added SpinupFlags field containing a Disable Spin-up bit
+ * to the MPI2_SAS_IOUNIT4_SPINUP_GROUP fields of SAS IO
+ * Unit Page 4.
+ * 11-18-11 02.00.22 Added define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT.
+ * Added UEFIVersion field to BIOS Page 1 and defined new
+ * BiosOptions bits.
+ * Incorporating additions for MPI v2.5.
+ * 11-27-12 02.00.23 Added MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER.
+ * Added MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID.
+ * 12-20-12 02.00.24 Marked MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION as
+ * obsolete for MPI v2.5 and later.
+ * Added some defines for 12G SAS speeds.
+ * 04-09-13 02.00.25 Added MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK.
+ * Fixed MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS to
+ * match the specification.
+ * 08-19-13 02.00.26 Added reserved words to MPI2_CONFIG_PAGE_IO_UNIT_7 for
+ * future use.
+ * 12-05-13 02.00.27 Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for
+ * MPI2_CONFIG_PAGE_MAN_7.
+ * Added EnclosureLevel and ConnectorName fields to
+ * MPI2_CONFIG_PAGE_SAS_DEV_0.
+ * Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for
+ * MPI2_CONFIG_PAGE_SAS_DEV_0.
+ * Added EnclosureLevel field to
+ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
+ * Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for
+ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
+ * 01-08-14 02.00.28 Added more defines for the BiosOptions field of
+ * MPI2_CONFIG_PAGE_BIOS_1.
+ * 06-13-14 02.00.29 Added SSUTimeout field to MPI2_CONFIG_PAGE_BIOS_1, and
+ * more defines for the BiosOptions field.
+ * 11-18-14 02.00.30 Updated copyright information.
+ * Added MPI2_BIOSPAGE1_OPTIONS_ADVANCED_CONFIG.
+ * Added AdapterOrderAux fields to BIOS Page 3.
+ * 03-16-15 02.00.31 Updated for MPI v2.6.
+ * Added Flags field to IO Unit Page 7.
+ * Added new SAS Phy Event codes
+ * 05-25-15 02.00.33 Added more defines for the BiosOptions field of
+ * MPI2_CONFIG_PAGE_BIOS_1.
+ * 08-25-15 02.00.34 Bumped Header Version.
+ * 12-18-15 02.00.35 Added SATADeviceWaitTime to SAS IO Unit Page 4.
+ * 01-21-16 02.00.36 Added/modified MPI2_MFGPAGE_DEVID_SAS defines.
+ * Added Link field to PCIe Link Pages
+ * Added EnclosureLevel and ConnectorName to PCIe
+ * Device Page 0.
+ * Added define for PCIE IoUnit page 1 max rate shift.
+ * Added comment for reserved ExtPageTypes.
+ * Added SAS 4 22.5 gbs speed support.
+ * Added PCIe 4 16.0 GT/sec speec support.
+ * Removed AHCI support.
+ * Removed SOP support.
+ * Added NegotiatedLinkRate and NegotiatedPortWidth to
+ * PCIe device page 0.
+ * 04-10-16 02.00.37 Fixed MPI2_MFGPAGE_DEVID_SAS3616/3708 defines
+ * 07-01-16 02.00.38 Added Manufacturing page 7 Connector types.
+ * Changed declaration of ConnectorName in PCIe DevicePage0
+ * to match SAS DevicePage 0.
+ * Added SATADeviceWaitTime to IO Unit Page 11.
+ * Added MPI26_MFGPAGE_DEVID_SAS4008
+ * Added x16 PCIe width to IO Unit Page 7
+ * Added LINKFLAGS to control SRIS in PCIe IO Unit page 1
+ * phy data.
+ * Added InitStatus to PCIe IO Unit Page 1 header.
+ * 09-01-16 02.00.39 Added MPI26_CONFIG_PAGE_ENCLOSURE_0 and related defines.
+ * Added MPI26_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE and
+ * MPI26_ENCLOS_PGAD_FORM_HANDLE page address formats.
+ * 02-02-17 02.00.40 Added MPI2_MANPAGE7_SLOT_UNKNOWN.
+ * Added ChassisSlot field to SAS Enclosure Page 0.
+ * Added ChassisSlot Valid bit (bit 5) to the Flags field
+ * in SAS Enclosure Page 0.
+ * 06-13-17 02.00.41 Added MPI26_MFGPAGE_DEVID_SAS3816 and
+ * MPI26_MFGPAGE_DEVID_SAS3916 defines.
+ * Removed MPI26_MFGPAGE_DEVID_SAS4008 define.
+ * Added MPI26_PCIEIOUNIT1_LINKFLAGS_SRNS_EN define.
+ * Renamed PI26_PCIEIOUNIT1_LINKFLAGS_EN_SRIS to
+ * PI26_PCIEIOUNIT1_LINKFLAGS_SRIS_EN.
+ * Renamed MPI26_PCIEIOUNIT1_LINKFLAGS_DIS_SRIS to
+ * MPI26_PCIEIOUNIT1_LINKFLAGS_DIS_SEPARATE_REFCLK.
+ * 09-29-17 02.00.42 Added ControllerResetTO field to PCIe Device Page 2.
+ * Added NOIOB field to PCIe Device Page 2.
+ * Added MPI26_PCIEDEV2_CAP_DATA_BLK_ALIGN_AND_GRAN to
+ * the Capabilities field of PCIe Device Page 2.
+ * 07-22-18 02.00.43 Added defines for SAS3916 and SAS3816.
+ * Added WRiteCache defines to IO Unit Page 1.
+ * Added MaxEnclosureLevel to BIOS Page 1.
+ * Added OEMRD to SAS Enclosure Page 1.
+ * Added DMDReportPCIe to PCIe IO Unit Page 1.
+ * Added Flags field and flags for Retimers to
+ * PCIe Switch Page 1.
+ * 08-02-18 02.00.44 Added Slotx2, Slotx4 to ManPage 7.
+ * 08-15-18 02.00.45 Added ProductSpecific field at end of IOC Page 1
+ * 08-28-18 02.00.46 Added NVMs Write Cache flag to IOUnitPage1
+ * Added DMDReport Delay Time defines to
+ * PCIeIOUnitPage1
+ * --------------------------------------------------------------------------
+ * 08-02-18 02.00.44 Added Slotx2, Slotx4 to ManPage 7.
+ * 08-15-18 02.00.45 Added ProductSpecific field at end of IOC Page 1
+ * 08-28-18 02.00.46 Added NVMs Write Cache flag to IOUnitPage1
+ * Added DMDReport Delay Time defines to PCIeIOUnitPage1
+ * 12-17-18 02.00.47 Swap locations of Slotx2 and Slotx4 in ManPage 7.
+ * 08-01-19 02.00.49 Add MPI26_MANPAGE7_FLAG_X2_X4_SLOT_INFO_VALID
+ * Add MPI26_IOUNITPAGE1_NVME_WRCACHE_SHIFT
+ * 09-13-24 02.00.50 Added PCIe 32 GT/s link rate
+ */
+
+#ifndef MPI2_CNFG_H
+#define MPI2_CNFG_H
+
+/*****************************************************************************
+* Configuration Page Header and defines
+*****************************************************************************/
+
+/*Config Page Header */
+typedef struct _MPI2_CONFIG_PAGE_HEADER {
+ U8 PageVersion; /*0x00 */
+ U8 PageLength; /*0x01 */
+ U8 PageNumber; /*0x02 */
+ U8 PageType; /*0x03 */
+} MPI2_CONFIG_PAGE_HEADER, *PTR_MPI2_CONFIG_PAGE_HEADER,
+ Mpi2ConfigPageHeader_t, *pMpi2ConfigPageHeader_t;
+
+typedef union _MPI2_CONFIG_PAGE_HEADER_UNION {
+ MPI2_CONFIG_PAGE_HEADER Struct;
+ U8 Bytes[4];
+ U16 Word16[2];
+ U32 Word32;
+} MPI2_CONFIG_PAGE_HEADER_UNION, *PTR_MPI2_CONFIG_PAGE_HEADER_UNION,
+ Mpi2ConfigPageHeaderUnion, *pMpi2ConfigPageHeaderUnion;
+
+/*Extended Config Page Header */
+typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER {
+ U8 PageVersion; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 PageNumber; /*0x02 */
+ U8 PageType; /*0x03 */
+ U16 ExtPageLength; /*0x04 */
+ U8 ExtPageType; /*0x06 */
+ U8 Reserved2; /*0x07 */
+} MPI2_CONFIG_EXTENDED_PAGE_HEADER,
+ *PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER,
+ Mpi2ConfigExtendedPageHeader_t,
+ *pMpi2ConfigExtendedPageHeader_t;
+
+typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION {
+ MPI2_CONFIG_PAGE_HEADER Struct;
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext;
+ U8 Bytes[8];
+ U16 Word16[4];
+ U32 Word32[2];
+} MPI2_CONFIG_EXT_PAGE_HEADER_UNION,
+ *PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION,
+ Mpi2ConfigPageExtendedHeaderUnion,
+ *pMpi2ConfigPageExtendedHeaderUnion;
+
+
+/*PageType field values */
+#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00)
+#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10)
+#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20)
+#define MPI2_CONFIG_PAGEATTR_MASK (0xF0)
+
+#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00)
+#define MPI2_CONFIG_PAGETYPE_IOC (0x01)
+#define MPI2_CONFIG_PAGETYPE_BIOS (0x02)
+#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08)
+#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09)
+#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A)
+#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F)
+#define MPI2_CONFIG_PAGETYPE_MASK (0x0F)
+
+#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF)
+
+
+/*ExtPageType field values */
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13)
+#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14)
+#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15)
+#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16)
+#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18)
+#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19)
+#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A)
+#define MPI2_CONFIG_EXTPAGETYPE_PCIE_IO_UNIT (0x1B)
+#define MPI2_CONFIG_EXTPAGETYPE_PCIE_SWITCH (0x1C)
+#define MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE (0x1D)
+#define MPI2_CONFIG_EXTPAGETYPE_PCIE_LINK (0x1E)
+
+
+/*****************************************************************************
+* PageAddress defines
+*****************************************************************************/
+
+/*RAID Volume PageAddress format */
+#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000)
+
+#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/*RAID Physical Disk PageAddress format */
+#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000)
+#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000)
+#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000)
+
+#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF)
+#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF)
+
+
+/*SAS Expander PageAddress format */
+#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000)
+
+#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF)
+#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000)
+#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16)
+
+
+/*SAS Device PageAddress format */
+#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000)
+
+#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/*SAS PHY PageAddress format */
+#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000)
+#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000)
+
+#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF)
+#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF)
+
+
+/*SAS Port PageAddress format */
+#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000)
+#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000)
+
+#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF)
+
+
+/*SAS Enclosure PageAddress format */
+#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000)
+
+#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF)
+
+/*Enclosure PageAddress format */
+#define MPI26_ENCLOS_PGAD_FORM_MASK (0xF0000000)
+#define MPI26_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI26_ENCLOS_PGAD_FORM_HANDLE (0x10000000)
+
+#define MPI26_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF)
+
+/*RAID Configuration PageAddress format */
+#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000)
+#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000)
+#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000)
+
+#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF)
+
+
+/*Driver Persistent Mapping PageAddress format */
+#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000)
+
+#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000)
+#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16)
+#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF)
+
+
+/*Ethernet PageAddress format */
+#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000)
+
+#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF)
+
+
+/*PCIe Switch PageAddress format */
+#define MPI26_PCIE_SWITCH_PGAD_FORM_MASK (0xF0000000)
+#define MPI26_PCIE_SWITCH_PGAD_FORM_GET_NEXT_HNDL (0x00000000)
+#define MPI26_PCIE_SWITCH_PGAD_FORM_HNDL_PORTNUM (0x10000000)
+#define MPI26_PCIE_SWITCH_EXPAND_PGAD_FORM_HNDL (0x20000000)
+
+#define MPI26_PCIE_SWITCH_PGAD_HANDLE_MASK (0x0000FFFF)
+#define MPI26_PCIE_SWITCH_PGAD_PORTNUM_MASK (0x00FF0000)
+#define MPI26_PCIE_SWITCH_PGAD_PORTNUM_SHIFT (16)
+
+
+/*PCIe Device PageAddress format */
+#define MPI26_PCIE_DEVICE_PGAD_FORM_MASK (0xF0000000)
+#define MPI26_PCIE_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE (0x20000000)
+
+#define MPI26_PCIE_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF)
+
+/*PCIe Link PageAddress format */
+#define MPI26_PCIE_LINK_PGAD_FORM_MASK (0xF0000000)
+#define MPI26_PCIE_LINK_PGAD_FORM_GET_NEXT_LINK (0x00000000)
+#define MPI26_PCIE_LINK_PGAD_FORM_LINK_NUM (0x10000000)
+
+#define MPI26_PCIE_DEVICE_PGAD_LINKNUM_MASK (0x000000FF)
+
+
+
+/****************************************************************************
+* Configuration messages
+****************************************************************************/
+
+/*Configuration Request Message */
+typedef struct _MPI2_CONFIG_REQUEST {
+ U8 Action; /*0x00 */
+ U8 SGLFlags; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 ExtPageLength; /*0x04 */
+ U8 ExtPageType; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U8 Reserved2; /*0x0C */
+ U8 ProxyVF_ID; /*0x0D */
+ U16 Reserved4; /*0x0E */
+ U32 Reserved3; /*0x10 */
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */
+ U32 PageAddress; /*0x18 */
+ MPI2_SGE_IO_UNION PageBufferSGE; /*0x1C */
+} MPI2_CONFIG_REQUEST, *PTR_MPI2_CONFIG_REQUEST,
+ Mpi2ConfigRequest_t, *pMpi2ConfigRequest_t;
+
+/*values for the Action field */
+#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00)
+#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01)
+#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02)
+#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03)
+#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04)
+#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05)
+#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06)
+#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07)
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+
+/*Config Reply Message */
+typedef struct _MPI2_CONFIG_REPLY {
+ U8 Action; /*0x00 */
+ U8 SGLFlags; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 ExtPageLength; /*0x04 */
+ U8 ExtPageType; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U16 Reserved2; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */
+} MPI2_CONFIG_REPLY, *PTR_MPI2_CONFIG_REPLY,
+ Mpi2ConfigReply_t, *pMpi2ConfigReply_t;
+
+
+
+/*****************************************************************************
+*
+* C o n f i g u r a t i o n P a g e s
+*
+*****************************************************************************/
+
+/****************************************************************************
+* Manufacturing Config pages
+****************************************************************************/
+
+#define MPI2_MFGPAGE_VENDORID_LSI (0x1000)
+#define MPI2_MFGPAGE_VENDORID_ATTO (0x117C)
+
+/*MPI v2.0 SAS products */
+#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070)
+#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072)
+#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074)
+#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076)
+#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077)
+#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064)
+#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065)
+
+#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E)
+
+#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080)
+#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081)
+#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082)
+#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083)
+#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084)
+#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085)
+#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086)
+#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087)
+#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E)
+#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP (0x02B0)
+#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1 (0x02B1)
+
+/*MPI v2.5 SAS products */
+#define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096)
+#define MPI25_MFGPAGE_DEVID_SAS3008 (0x0097)
+#define MPI25_MFGPAGE_DEVID_SAS3108_1 (0x0090)
+#define MPI25_MFGPAGE_DEVID_SAS3108_2 (0x0091)
+#define MPI25_MFGPAGE_DEVID_SAS3108_5 (0x0094)
+#define MPI25_MFGPAGE_DEVID_SAS3108_6 (0x0095)
+
+/* MPI v2.6 SAS Products */
+#define MPI26_MFGPAGE_DEVID_SAS3216 (0x00C9)
+#define MPI26_MFGPAGE_DEVID_SAS3224 (0x00C4)
+#define MPI26_MFGPAGE_DEVID_SAS3316_1 (0x00C5)
+#define MPI26_MFGPAGE_DEVID_SAS3316_2 (0x00C6)
+#define MPI26_MFGPAGE_DEVID_SAS3316_3 (0x00C7)
+#define MPI26_MFGPAGE_DEVID_SAS3316_4 (0x00C8)
+#define MPI26_MFGPAGE_DEVID_SAS3324_1 (0x00C0)
+#define MPI26_MFGPAGE_DEVID_SAS3324_2 (0x00C1)
+#define MPI26_MFGPAGE_DEVID_SAS3324_3 (0x00C2)
+#define MPI26_MFGPAGE_DEVID_SAS3324_4 (0x00C3)
+
+#define MPI26_MFGPAGE_DEVID_SAS3516 (0x00AA)
+#define MPI26_MFGPAGE_DEVID_SAS3516_1 (0x00AB)
+#define MPI26_MFGPAGE_DEVID_SAS3416 (0x00AC)
+#define MPI26_MFGPAGE_DEVID_SAS3508 (0x00AD)
+#define MPI26_MFGPAGE_DEVID_SAS3508_1 (0x00AE)
+#define MPI26_MFGPAGE_DEVID_SAS3408 (0x00AF)
+#define MPI26_MFGPAGE_DEVID_SAS3716 (0x00D0)
+#define MPI26_MFGPAGE_DEVID_SAS3616 (0x00D1)
+#define MPI26_MFGPAGE_DEVID_SAS3708 (0x00D2)
+
+#define MPI26_MFGPAGE_DEVID_SEC_MASK_3916 (0x0003)
+#define MPI26_MFGPAGE_DEVID_INVALID0_3916 (0x00E0)
+#define MPI26_MFGPAGE_DEVID_CFG_SEC_3916 (0x00E1)
+#define MPI26_MFGPAGE_DEVID_HARD_SEC_3916 (0x00E2)
+#define MPI26_MFGPAGE_DEVID_INVALID1_3916 (0x00E3)
+
+#define MPI26_MFGPAGE_DEVID_SEC_MASK_3816 (0x0003)
+#define MPI26_MFGPAGE_DEVID_INVALID0_3816 (0x00E4)
+#define MPI26_MFGPAGE_DEVID_CFG_SEC_3816 (0x00E5)
+#define MPI26_MFGPAGE_DEVID_HARD_SEC_3816 (0x00E6)
+#define MPI26_MFGPAGE_DEVID_INVALID1_3816 (0x00E7)
+
+
+/*Manufacturing Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 ChipName[16] __nonstring; /*0x04 */
+ U8 ChipRevision[8]; /*0x14 */
+ U8 BoardName[16]; /*0x1C */
+ U8 BoardAssembly[16]; /*0x2C */
+ U8 BoardTracerNumber[16]; /*0x3C */
+} MPI2_CONFIG_PAGE_MAN_0,
+ *PTR_MPI2_CONFIG_PAGE_MAN_0,
+ Mpi2ManufacturingPage0_t,
+ *pMpi2ManufacturingPage0_t;
+
+#define MPI2_MANUFACTURING0_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 VPD[256]; /*0x04 */
+} MPI2_CONFIG_PAGE_MAN_1,
+ *PTR_MPI2_CONFIG_PAGE_MAN_1,
+ Mpi2ManufacturingPage1_t,
+ *pMpi2ManufacturingPage1_t;
+
+#define MPI2_MANUFACTURING1_PAGEVERSION (0x00)
+
+
+typedef struct _MPI2_CHIP_REVISION_ID {
+ U16 DeviceID; /*0x00 */
+ U8 PCIRevisionID; /*0x02 */
+ U8 Reserved; /*0x03 */
+} MPI2_CHIP_REVISION_ID, *PTR_MPI2_CHIP_REVISION_ID,
+ Mpi2ChipRevisionId_t, *pMpi2ChipRevisionId_t;
+
+
+/*Manufacturing Page 2 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check Header.PageLength at
+ *runtime before using HwSettings[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_2 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */
+ U32 HwSettings[]; /*0x08 */
+} MPI2_CONFIG_PAGE_MAN_2,
+ *PTR_MPI2_CONFIG_PAGE_MAN_2,
+ Mpi2ManufacturingPage2_t,
+ *pMpi2ManufacturingPage2_t;
+
+#define MPI2_MANUFACTURING2_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 3 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check Header.PageLength at
+ *runtime before using Info[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_3 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */
+ U32 Info[]; /*0x08 */
+} MPI2_CONFIG_PAGE_MAN_3,
+ *PTR_MPI2_CONFIG_PAGE_MAN_3,
+ Mpi2ManufacturingPage3_t,
+ *pMpi2ManufacturingPage3_t;
+
+#define MPI2_MANUFACTURING3_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 4 */
+
+typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS {
+ U8 PowerSaveFlags; /*0x00 */
+ U8 InternalOperationsSleepTime; /*0x01 */
+ U8 InternalOperationsRunTime; /*0x02 */
+ U8 HostIdleTime; /*0x03 */
+} MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
+ *PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
+ Mpi2ManPage4PwrSaveSettings_t,
+ *pMpi2ManPage4PwrSaveSettings_t;
+
+/*defines for the PowerSaveFlags field */
+#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03)
+#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00)
+#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01)
+#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02)
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_4 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Flags; /*0x08 */
+ U8 InquirySize; /*0x0C */
+ U8 Reserved2; /*0x0D */
+ U16 Reserved3; /*0x0E */
+ U8 InquiryData[56]; /*0x10 */
+ U32 RAID0VolumeSettings; /*0x48 */
+ U32 RAID1EVolumeSettings; /*0x4C */
+ U32 RAID1VolumeSettings; /*0x50 */
+ U32 RAID10VolumeSettings; /*0x54 */
+ U32 Reserved4; /*0x58 */
+ U32 Reserved5; /*0x5C */
+ MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /*0x60 */
+ U8 MaxOCEDisks; /*0x64 */
+ U8 ResyncRate; /*0x65 */
+ U16 DataScrubDuration; /*0x66 */
+ U8 MaxHotSpares; /*0x68 */
+ U8 MaxPhysDisksPerVol; /*0x69 */
+ U8 MaxPhysDisks; /*0x6A */
+ U8 MaxVolumes; /*0x6B */
+} MPI2_CONFIG_PAGE_MAN_4,
+ *PTR_MPI2_CONFIG_PAGE_MAN_4,
+ Mpi2ManufacturingPage4_t,
+ *pMpi2ManufacturingPage4_t;
+
+#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A)
+
+/*Manufacturing Page 4 Flags field */
+#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000)
+#define MPI2_MANPAGE4_METADATA_512MB (0x00000000)
+
+#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000)
+#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000)
+#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000)
+
+#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00)
+#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000)
+#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400)
+#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800)
+#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00)
+
+#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300)
+#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000)
+#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100)
+#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200)
+
+#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080)
+#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040)
+#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020)
+#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010)
+#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008)
+#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004)
+#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002)
+#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001)
+
+
+/*Manufacturing Page 5 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using Phy[].
+ */
+
+typedef struct _MPI2_MANUFACTURING5_ENTRY {
+ U64 WWID; /*0x00 */
+ U64 DeviceName; /*0x08 */
+} MPI2_MANUFACTURING5_ENTRY,
+ *PTR_MPI2_MANUFACTURING5_ENTRY,
+ Mpi2Manufacturing5Entry_t,
+ *pMpi2Manufacturing5Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_5 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhys; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+ MPI2_MANUFACTURING5_ENTRY Phy[]; /*0x10 */
+} MPI2_CONFIG_PAGE_MAN_5,
+ *PTR_MPI2_CONFIG_PAGE_MAN_5,
+ Mpi2ManufacturingPage5_t,
+ *pMpi2ManufacturingPage5_t;
+
+#define MPI2_MANUFACTURING5_PAGEVERSION (0x03)
+
+
+/*Manufacturing Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_6 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 ProductSpecificInfo;/*0x04 */
+} MPI2_CONFIG_PAGE_MAN_6,
+ *PTR_MPI2_CONFIG_PAGE_MAN_6,
+ Mpi2ManufacturingPage6_t,
+ *pMpi2ManufacturingPage6_t;
+
+#define MPI2_MANUFACTURING6_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 7 */
+
+typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO {
+ U32 Pinout; /*0x00 */
+ U8 Connector[16]; /*0x04 */
+ U8 Location; /*0x14 */
+ U8 ReceptacleID; /*0x15 */
+ U16 Slot; /*0x16 */
+ U16 Slotx2; /*0x18 */
+ U16 Slotx4; /*0x1A */
+} MPI2_MANPAGE7_CONNECTOR_INFO,
+ *PTR_MPI2_MANPAGE7_CONNECTOR_INFO,
+ Mpi2ManPage7ConnectorInfo_t,
+ *pMpi2ManPage7ConnectorInfo_t;
+
+/*defines for the Pinout field */
+#define MPI2_MANPAGE7_PINOUT_LANE_MASK (0x0000FF00)
+#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT (8)
+
+#define MPI2_MANPAGE7_PINOUT_TYPE_MASK (0x000000FF)
+#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN (0x00)
+#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE (0x01)
+#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x02)
+#define MPI2_MANPAGE7_PINOUT_SFF_8486 (0x03)
+#define MPI2_MANPAGE7_PINOUT_SFF_8484 (0x04)
+#define MPI2_MANPAGE7_PINOUT_SFF_8087 (0x05)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I (0x06)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I (0x07)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470 (0x08)
+#define MPI2_MANPAGE7_PINOUT_SFF_8088 (0x09)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X (0x0A)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C)
+#define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D)
+#define MPI2_MANPAGE7_PINOUT_SFF_8088_A (0x0E)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_16i (0x0F)
+#define MPI2_MANPAGE7_PINOUT_SFF_8654_4i (0x10)
+#define MPI2_MANPAGE7_PINOUT_SFF_8654_8i (0x11)
+#define MPI2_MANPAGE7_PINOUT_SFF_8611_4i (0x12)
+#define MPI2_MANPAGE7_PINOUT_SFF_8611_8i (0x13)
+
+/*defines for the Location field */
+#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01)
+#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02)
+#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04)
+#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08)
+#define MPI2_MANPAGE7_LOCATION_AUTO (0x10)
+#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20)
+#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80)
+
+/*defines for the Slot field */
+#define MPI2_MANPAGE7_SLOT_UNKNOWN (0xFFFF)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using ConnectorInfo[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_7 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U32 Flags; /*0x0C */
+ U8 EnclosureName[16]; /*0x10 */
+ U8 NumPhys; /*0x20 */
+ U8 Reserved3; /*0x21 */
+ U16 Reserved4; /*0x22 */
+ MPI2_MANPAGE7_CONNECTOR_INFO ConnectorInfo[]; /*0x24 */
+} MPI2_CONFIG_PAGE_MAN_7,
+ *PTR_MPI2_CONFIG_PAGE_MAN_7,
+ Mpi2ManufacturingPage7_t,
+ *pMpi2ManufacturingPage7_t;
+
+#define MPI2_MANUFACTURING7_PAGEVERSION (0x01)
+
+/*defines for the Flags field */
+#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL (0x00000008)
+#define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002)
+#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001)
+
+#define MPI26_MANPAGE7_FLAG_CONN_LANE_USE_PINOUT (0x00000020)
+#define MPI26_MANPAGE7_FLAG_X2_X4_SLOT_INFO_VALID (0x00000010)
+
+/*
+ *Generic structure to use for product-specific manufacturing pages
+ *(currently Manufacturing Page 8 through Manufacturing Page 31).
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_PS {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 ProductSpecificInfo;/*0x04 */
+} MPI2_CONFIG_PAGE_MAN_PS,
+ *PTR_MPI2_CONFIG_PAGE_MAN_PS,
+ Mpi2ManufacturingPagePS_t,
+ *pMpi2ManufacturingPagePS_t;
+
+#define MPI2_MANUFACTURING8_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING9_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING10_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING11_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING12_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING13_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING14_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING15_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING16_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING17_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING18_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING19_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING20_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING21_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING22_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING23_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING24_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING25_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING26_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING27_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING28_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING29_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING30_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING31_PAGEVERSION (0x00)
+
+
+/****************************************************************************
+* IO Unit Config Pages
+****************************************************************************/
+
+/*IO Unit Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U64 UniqueValue; /*0x04 */
+ MPI2_VERSION_UNION NvdataVersionDefault; /*0x08 */
+ MPI2_VERSION_UNION NvdataVersionPersistent; /*0x0A */
+} MPI2_CONFIG_PAGE_IO_UNIT_0,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_0,
+ Mpi2IOUnitPage0_t, *pMpi2IOUnitPage0_t;
+
+#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02)
+
+
+/*IO Unit Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Flags; /*0x04 */
+} MPI2_CONFIG_PAGE_IO_UNIT_1,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_1,
+ Mpi2IOUnitPage1_t, *pMpi2IOUnitPage1_t;
+
+#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04)
+
+/* IO Unit Page 1 Flags defines */
+#define MPI26_IOUNITPAGE1_NVME_WRCACHE_MASK (0x00030000)
+#define MPI26_IOUNITPAGE1_NVME_WRCACHE_SHIFT (16)
+#define MPI26_IOUNITPAGE1_NVME_WRCACHE_NO_CHANGE (0x00000000)
+#define MPI26_IOUNITPAGE1_NVME_WRCACHE_ENABLE (0x00010000)
+#define MPI26_IOUNITPAGE1_NVME_WRCACHE_DISABLE (0x00020000)
+#define MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK (0x00004000)
+#define MPI25_IOUNITPAGE1_NEW_DEVICE_FAST_PATH_DISABLE (0x00002000)
+#define MPI25_IOUNITPAGE1_DISABLE_FAST_PATH (0x00001000)
+#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800)
+#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600)
+#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9)
+#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000)
+#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200)
+#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400)
+#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100)
+#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040)
+#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020)
+#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004)
+
+
+/*IO Unit Page 3 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *36 and check the value returned for GPIOCount at runtime.
+ */
+#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX
+#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (36)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 GPIOCount; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U16
+ GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/*0x08 */
+} MPI2_CONFIG_PAGE_IO_UNIT_3,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_3,
+ Mpi2IOUnitPage3_t, *pMpi2IOUnitPage3_t;
+
+#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01)
+
+/*defines for IO Unit Page 3 GPIOVal field */
+#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC)
+#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2)
+#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000)
+#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001)
+
+
+/*IO Unit Page 5 */
+
+/*
+ *Upper layer code (drivers, utilities, etc.) should check the value returned
+ *for NumDmaEngines at runtime before using DmaEngineCapabilities[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U64
+ RaidAcceleratorBufferBaseAddress; /*0x04 */
+ U64
+ RaidAcceleratorBufferSize; /*0x0C */
+ U64
+ RaidAcceleratorControlBaseAddress; /*0x14 */
+ U8 RAControlSize; /*0x1C */
+ U8 NumDmaEngines; /*0x1D */
+ U8 RAMinControlSize; /*0x1E */
+ U8 RAMaxControlSize; /*0x1F */
+ U32 Reserved1; /*0x20 */
+ U32 Reserved2; /*0x24 */
+ U32 Reserved3; /*0x28 */
+ U32
+ DmaEngineCapabilities[]; /*0x2C */
+} MPI2_CONFIG_PAGE_IO_UNIT_5,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_5,
+ Mpi2IOUnitPage5_t, *pMpi2IOUnitPage5_t;
+
+#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00)
+
+/*defines for IO Unit Page 5 DmaEngineCapabilities field */
+#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFFFF0000)
+#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16)
+
+#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008)
+#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004)
+#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002)
+#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001)
+
+
+/*IO Unit Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 Flags; /*0x04 */
+ U8 RAHostControlSize; /*0x06 */
+ U8 Reserved0; /*0x07 */
+ U64
+ RaidAcceleratorHostControlBaseAddress; /*0x08 */
+ U32 Reserved1; /*0x10 */
+ U32 Reserved2; /*0x14 */
+ U32 Reserved3; /*0x18 */
+} MPI2_CONFIG_PAGE_IO_UNIT_6,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_6,
+ Mpi2IOUnitPage6_t, *pMpi2IOUnitPage6_t;
+
+#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00)
+
+/*defines for IO Unit Page 6 Flags field */
+#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001)
+
+
+/*IO Unit Page 7 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 CurrentPowerMode; /*0x04 */
+ U8 PreviousPowerMode; /*0x05 */
+ U8 PCIeWidth; /*0x06 */
+ U8 PCIeSpeed; /*0x07 */
+ U32 ProcessorState; /*0x08 */
+ U32
+ PowerManagementCapabilities; /*0x0C */
+ U16 IOCTemperature; /*0x10 */
+ U8
+ IOCTemperatureUnits; /*0x12 */
+ U8 IOCSpeed; /*0x13 */
+ U16 BoardTemperature; /*0x14 */
+ U8
+ BoardTemperatureUnits; /*0x16 */
+ U8 Reserved3; /*0x17 */
+ U32 BoardPowerRequirement; /*0x18 */
+ U32 PCISlotPowerAllocation; /*0x1C */
+/* reserved prior to MPI v2.6 */
+ U8 Flags; /* 0x20 */
+ U8 Reserved6; /* 0x21 */
+ U16 Reserved7; /* 0x22 */
+ U32 Reserved8; /* 0x24 */
+} MPI2_CONFIG_PAGE_IO_UNIT_7,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
+ Mpi2IOUnitPage7_t, *pMpi2IOUnitPage7_t;
+
+#define MPI2_IOUNITPAGE7_PAGEVERSION (0x05)
+
+/*defines for IO Unit Page 7 CurrentPowerMode and PreviousPowerMode fields */
+#define MPI25_IOUNITPAGE7_PM_INIT_MASK (0xC0)
+#define MPI25_IOUNITPAGE7_PM_INIT_UNAVAILABLE (0x00)
+#define MPI25_IOUNITPAGE7_PM_INIT_HOST (0x40)
+#define MPI25_IOUNITPAGE7_PM_INIT_IO_UNIT (0x80)
+#define MPI25_IOUNITPAGE7_PM_INIT_PCIE_DPA (0xC0)
+
+#define MPI25_IOUNITPAGE7_PM_MODE_MASK (0x07)
+#define MPI25_IOUNITPAGE7_PM_MODE_UNAVAILABLE (0x00)
+#define MPI25_IOUNITPAGE7_PM_MODE_UNKNOWN (0x01)
+#define MPI25_IOUNITPAGE7_PM_MODE_FULL_POWER (0x04)
+#define MPI25_IOUNITPAGE7_PM_MODE_REDUCED_POWER (0x05)
+#define MPI25_IOUNITPAGE7_PM_MODE_STANDBY (0x06)
+
+
+/*defines for IO Unit Page 7 PCIeWidth field */
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X16 (0x10)
+
+/*defines for IO Unit Page 7 PCIeSpeed field */
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_16_0_GBPS (0x03)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_32_0_GBPS (0x04)
+
+/*defines for IO Unit Page 7 ProcessorState field */
+#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F)
+#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0)
+
+#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01)
+#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02)
+
+/*defines for IO Unit Page 7 PowerManagementCapabilities field */
+#define MPI25_IOUNITPAGE7_PMCAP_DPA_FULL_PWR_MODE (0x00400000)
+#define MPI25_IOUNITPAGE7_PMCAP_DPA_REDUCED_PWR_MODE (0x00200000)
+#define MPI25_IOUNITPAGE7_PMCAP_DPA_STANDBY_MODE (0x00100000)
+#define MPI25_IOUNITPAGE7_PMCAP_HOST_FULL_PWR_MODE (0x00040000)
+#define MPI25_IOUNITPAGE7_PMCAP_HOST_REDUCED_PWR_MODE (0x00020000)
+#define MPI25_IOUNITPAGE7_PMCAP_HOST_STANDBY_MODE (0x00010000)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_FULL_PWR_MODE (0x00004000)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_REDUCED_PWR_MODE (0x00002000)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_STANDBY_MODE (0x00001000)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_12_5_PCT_IOCSPEED (0x00000400)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_25_0_PCT_IOCSPEED (0x00000200)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_50_0_PCT_IOCSPEED (0x00000100)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_12_5_PCT_IOCSPEED (0x00000040)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_25_0_PCT_IOCSPEED (0x00000020)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_50_0_PCT_IOCSPEED (0x00000010)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_WIDTH_CHANGE_PCIE (0x00000008)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_SPEED_CHANGE_PCIE (0x00000004)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_WIDTH_CHANGE_PCIE (0x00000002)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_SPEED_CHANGE_PCIE (0x00000001)
+
+/*obsolete names for the PowerManagementCapabilities bits (above) */
+#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400)
+#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200)
+#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100)
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008) /*obsolete */
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004) /*obsolete */
+
+
+/*defines for IO Unit Page 7 IOCTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02)
+
+/*defines for IO Unit Page 7 IOCSpeed field */
+#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08)
+
+/*defines for IO Unit Page 7 BoardTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT (0x01)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS (0x02)
+
+/* defines for IO Unit Page 7 Flags field */
+#define MPI2_IOUNITPAGE7_FLAG_CABLE_POWER_EXC (0x01)
+
+/*IO Unit Page 8 */
+
+#define MPI2_IOUNIT8_NUM_THRESHOLDS (4)
+
+typedef struct _MPI2_IOUNIT8_SENSOR {
+ U16 Flags; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U16
+ Threshold[MPI2_IOUNIT8_NUM_THRESHOLDS]; /*0x04 */
+ U32 Reserved2; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U32 Reserved4; /*0x14 */
+} MPI2_IOUNIT8_SENSOR, *PTR_MPI2_IOUNIT8_SENSOR,
+ Mpi2IOUnit8Sensor_t, *pMpi2IOUnit8Sensor_t;
+
+/*defines for IO Unit Page 8 Sensor Flags field */
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T3_ENABLE (0x0008)
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T2_ENABLE (0x0004)
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T1_ENABLE (0x0002)
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T0_ENABLE (0x0001)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumSensors at runtime before using Sensor[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U8 NumSensors; /*0x0C */
+ U8 PollingInterval; /*0x0D */
+ U16 Reserved3; /*0x0E */
+ MPI2_IOUNIT8_SENSOR Sensor[]; /*0x10 */
+} MPI2_CONFIG_PAGE_IO_UNIT_8,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_8,
+ Mpi2IOUnitPage8_t, *pMpi2IOUnitPage8_t;
+
+#define MPI2_IOUNITPAGE8_PAGEVERSION (0x00)
+
+
+/*IO Unit Page 9 */
+
+typedef struct _MPI2_IOUNIT9_SENSOR {
+ U16 CurrentTemperature; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U8 Flags; /*0x04 */
+ U8 Reserved2; /*0x05 */
+ U16 Reserved3; /*0x06 */
+ U32 Reserved4; /*0x08 */
+ U32 Reserved5; /*0x0C */
+} MPI2_IOUNIT9_SENSOR, *PTR_MPI2_IOUNIT9_SENSOR,
+ Mpi2IOUnit9Sensor_t, *pMpi2IOUnit9Sensor_t;
+
+/*defines for IO Unit Page 9 Sensor Flags field */
+#define MPI2_IOUNIT9_SENSOR_FLAGS_TEMP_VALID (0x01)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumSensors at runtime before using Sensor[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U8 NumSensors; /*0x0C */
+ U8 Reserved4; /*0x0D */
+ U16 Reserved3; /*0x0E */
+ MPI2_IOUNIT9_SENSOR Sensor[]; /*0x10 */
+} MPI2_CONFIG_PAGE_IO_UNIT_9,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_9,
+ Mpi2IOUnitPage9_t, *pMpi2IOUnitPage9_t;
+
+#define MPI2_IOUNITPAGE9_PAGEVERSION (0x00)
+
+
+/*IO Unit Page 10 */
+
+typedef struct _MPI2_IOUNIT10_FUNCTION {
+ U8 CreditPercent; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+} MPI2_IOUNIT10_FUNCTION,
+ *PTR_MPI2_IOUNIT10_FUNCTION,
+ Mpi2IOUnit10Function_t,
+ *pMpi2IOUnit10Function_t;
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumFunctions at runtime before using Function[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumFunctions; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+ MPI2_IOUNIT10_FUNCTION Function[]; /*0x10 */
+} MPI2_CONFIG_PAGE_IO_UNIT_10,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_10,
+ Mpi2IOUnitPage10_t, *pMpi2IOUnitPage10_t;
+
+#define MPI2_IOUNITPAGE10_PAGEVERSION (0x01)
+
+
+/* IO Unit Page 11 (for MPI v2.6 and later) */
+
+typedef struct _MPI26_IOUNIT11_SPINUP_GROUP {
+ U8 MaxTargetSpinup; /* 0x00 */
+ U8 SpinupDelay; /* 0x01 */
+ U8 SpinupFlags; /* 0x02 */
+ U8 Reserved1; /* 0x03 */
+} MPI26_IOUNIT11_SPINUP_GROUP,
+ *PTR_MPI26_IOUNIT11_SPINUP_GROUP,
+ Mpi26IOUnit11SpinupGroup_t,
+ *pMpi26IOUnit11SpinupGroup_t;
+
+/* defines for IO Unit Page 11 SpinupFlags */
+#define MPI26_IOUNITPAGE11_SPINUP_DISABLE_FLAG (0x01)
+
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * four and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI26_IOUNITPAGE11_PHY_MAX
+#define MPI26_IOUNITPAGE11_PHY_MAX (4)
+#endif
+
+typedef struct _MPI26_CONFIG_PAGE_IO_UNIT_11 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ MPI26_IOUNIT11_SPINUP_GROUP SpinupGroupParameters[4]; /*0x08 */
+ U32 Reserved2; /*0x18 */
+ U32 Reserved3; /*0x1C */
+ U32 Reserved4; /*0x20 */
+ U8 BootDeviceWaitTime; /*0x24 */
+ U8 Reserved5; /*0x25 */
+ U16 Reserved6; /*0x26 */
+ U8 NumPhys; /*0x28 */
+ U8 PEInitialSpinupDelay; /*0x29 */
+ U8 PEReplyDelay; /*0x2A */
+ U8 Flags; /*0x2B */
+ U8 PHY[MPI26_IOUNITPAGE11_PHY_MAX];/*0x2C */
+} MPI26_CONFIG_PAGE_IO_UNIT_11,
+ *PTR_MPI26_CONFIG_PAGE_IO_UNIT_11,
+ Mpi26IOUnitPage11_t,
+ *pMpi26IOUnitPage11_t;
+
+#define MPI26_IOUNITPAGE11_PAGEVERSION (0x00)
+
+/* defines for Flags field */
+#define MPI26_IOUNITPAGE11_FLAGS_AUTO_PORTENABLE (0x01)
+
+/* defines for PHY field */
+#define MPI26_IOUNITPAGE11_PHY_SPINUP_GROUP_MASK (0x03)
+
+
+
+
+
+
+/****************************************************************************
+* IOC Config Pages
+****************************************************************************/
+
+/*IOC Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U16 VendorID; /*0x0C */
+ U16 DeviceID; /*0x0E */
+ U8 RevisionID; /*0x10 */
+ U8 Reserved3; /*0x11 */
+ U16 Reserved4; /*0x12 */
+ U32 ClassCode; /*0x14 */
+ U16 SubsystemVendorID; /*0x18 */
+ U16 SubsystemID; /*0x1A */
+} MPI2_CONFIG_PAGE_IOC_0,
+ *PTR_MPI2_CONFIG_PAGE_IOC_0,
+ Mpi2IOCPage0_t, *pMpi2IOCPage0_t;
+
+#define MPI2_IOCPAGE0_PAGEVERSION (0x02)
+
+
+/*IOC Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Flags; /*0x04 */
+ U32 CoalescingTimeout; /*0x08 */
+ U8 CoalescingDepth; /*0x0C */
+ U8 PCISlotNum; /*0x0D */
+ U8 PCIBusNum; /*0x0E */
+ U8 PCIDomainSegment; /*0x0F */
+ U32 Reserved1; /*0x10 */
+ U32 ProductSpecific; /* 0x14 */
+} MPI2_CONFIG_PAGE_IOC_1,
+ *PTR_MPI2_CONFIG_PAGE_IOC_1,
+ Mpi2IOCPage1_t, *pMpi2IOCPage1_t;
+
+#define MPI2_IOCPAGE1_PAGEVERSION (0x05)
+
+/*defines for IOC Page 1 Flags field */
+#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001)
+
+#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF)
+#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF)
+#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF)
+
+/*IOC Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_6 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32
+ CapabilitiesFlags; /*0x04 */
+ U8 MaxDrivesRAID0; /*0x08 */
+ U8 MaxDrivesRAID1; /*0x09 */
+ U8
+ MaxDrivesRAID1E; /*0x0A */
+ U8
+ MaxDrivesRAID10; /*0x0B */
+ U8 MinDrivesRAID0; /*0x0C */
+ U8 MinDrivesRAID1; /*0x0D */
+ U8
+ MinDrivesRAID1E; /*0x0E */
+ U8
+ MinDrivesRAID10; /*0x0F */
+ U32 Reserved1; /*0x10 */
+ U8
+ MaxGlobalHotSpares; /*0x14 */
+ U8 MaxPhysDisks; /*0x15 */
+ U8 MaxVolumes; /*0x16 */
+ U8 MaxConfigs; /*0x17 */
+ U8 MaxOCEDisks; /*0x18 */
+ U8 Reserved2; /*0x19 */
+ U16 Reserved3; /*0x1A */
+ U32
+ SupportedStripeSizeMapRAID0; /*0x1C */
+ U32
+ SupportedStripeSizeMapRAID1E; /*0x20 */
+ U32
+ SupportedStripeSizeMapRAID10; /*0x24 */
+ U32 Reserved4; /*0x28 */
+ U32 Reserved5; /*0x2C */
+ U16
+ DefaultMetadataSize; /*0x30 */
+ U16 Reserved6; /*0x32 */
+ U16
+ MaxBadBlockTableEntries; /*0x34 */
+ U16 Reserved7; /*0x36 */
+ U32
+ IRNvsramVersion; /*0x38 */
+} MPI2_CONFIG_PAGE_IOC_6,
+ *PTR_MPI2_CONFIG_PAGE_IOC_6,
+ Mpi2IOCPage6_t, *pMpi2IOCPage6_t;
+
+#define MPI2_IOCPAGE6_PAGEVERSION (0x05)
+
+/*defines for IOC Page 6 CapabilitiesFlags */
+#define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT (0x00000020)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002)
+#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001)
+
+
+/*IOC Page 7 */
+
+#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4)
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_7 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32
+ EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/*0x08 */
+ U16 SASBroadcastPrimitiveMasks; /*0x18 */
+ U16 SASNotifyPrimitiveMasks; /*0x1A */
+ U32 Reserved3; /*0x1C */
+} MPI2_CONFIG_PAGE_IOC_7,
+ *PTR_MPI2_CONFIG_PAGE_IOC_7,
+ Mpi2IOCPage7_t, *pMpi2IOCPage7_t;
+
+#define MPI2_IOCPAGE7_PAGEVERSION (0x02)
+
+
+/*IOC Page 8 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_8 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumDevsPerEnclosure; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U16 MaxPersistentEntries; /*0x08 */
+ U16 MaxNumPhysicalMappedIDs; /*0x0A */
+ U16 Flags; /*0x0C */
+ U16 Reserved3; /*0x0E */
+ U16 IRVolumeMappingFlags; /*0x10 */
+ U16 Reserved4; /*0x12 */
+ U32 Reserved5; /*0x14 */
+} MPI2_CONFIG_PAGE_IOC_8,
+ *PTR_MPI2_CONFIG_PAGE_IOC_8,
+ Mpi2IOCPage8_t, *pMpi2IOCPage8_t;
+
+#define MPI2_IOCPAGE8_PAGEVERSION (0x00)
+
+/*defines for IOC Page 8 Flags field */
+#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020)
+#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010)
+
+#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E)
+#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000)
+#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002)
+
+#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001)
+#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000)
+
+/*defines for IOC Page 8 IRVolumeMappingFlags */
+#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003)
+#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000)
+#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001)
+
+
+/****************************************************************************
+* BIOS Config Pages
+****************************************************************************/
+
+/*BIOS Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 BiosOptions; /*0x04 */
+ U32 IOCSettings; /*0x08 */
+ U8 SSUTimeout; /*0x0C */
+ U8 MaxEnclosureLevel; /*0x0D */
+ U16 Reserved2; /*0x0E */
+ U32 DeviceSettings; /*0x10 */
+ U16 NumberOfDevices; /*0x14 */
+ U16 UEFIVersion; /*0x16 */
+ U16 IOTimeoutBlockDevicesNonRM; /*0x18 */
+ U16 IOTimeoutSequential; /*0x1A */
+ U16 IOTimeoutOther; /*0x1C */
+ U16 IOTimeoutBlockDevicesRM; /*0x1E */
+} MPI2_CONFIG_PAGE_BIOS_1,
+ *PTR_MPI2_CONFIG_PAGE_BIOS_1,
+ Mpi2BiosPage1_t, *pMpi2BiosPage1_t;
+
+#define MPI2_BIOSPAGE1_PAGEVERSION (0x07)
+
+/*values for BIOS Page 1 BiosOptions field */
+#define MPI2_BIOSPAGE1_OPTIONS_BOOT_LIST_ADD_ALT_BOOT_DEVICE (0x00008000)
+#define MPI2_BIOSPAGE1_OPTIONS_ADVANCED_CONFIG (0x00004000)
+
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_PBDHL (0x00000000)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_ENCSLOSURE (0x00000800)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_LWWID (0x00001000)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_PSENS (0x00001800)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_ESPHY (0x00002000)
+
+#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS (0x00000400)
+
+#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD (0x00000300)
+#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD (0x00000000)
+#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD (0x00000100)
+#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD (0x00000200)
+#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD (0x00000300)
+
+#define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0)
+#define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000)
+
+#define MPI2_BIOSPAGE1_OPTIONS_MASK_UEFI_HII_REGISTRATION (0x00000006)
+#define MPI2_BIOSPAGE1_OPTIONS_ENABLE_UEFI_HII (0x00000000)
+#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_UEFI_HII (0x00000002)
+#define MPI2_BIOSPAGE1_OPTIONS_VERSION_CHECK_UEFI_HII (0x00000004)
+
+#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001)
+
+/*values for BIOS Page 1 IOCSettings field */
+#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000)
+#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000)
+
+#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0)
+#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040)
+#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080)
+
+#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030)
+#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010)
+#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020)
+#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030)
+
+#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008)
+
+/*values for BIOS Page 1 DeviceSettings field */
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001)
+
+/*defines for BIOS Page 1 UEFIVersion field */
+#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_MASK (0xFF00)
+#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_SHIFT (8)
+#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_MASK (0x00FF)
+#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_SHIFT (0)
+
+
+
+/*BIOS Page 2 */
+
+typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER {
+ U32 Reserved1; /*0x00 */
+ U32 Reserved2; /*0x04 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+ U32 Reserved5; /*0x10 */
+ U32 Reserved6; /*0x14 */
+} MPI2_BOOT_DEVICE_ADAPTER_ORDER,
+ *PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER,
+ Mpi2BootDeviceAdapterOrder_t,
+ *pMpi2BootDeviceAdapterOrder_t;
+
+typedef struct _MPI2_BOOT_DEVICE_SAS_WWID {
+ U64 SASAddress; /*0x00 */
+ U8 LUN[8]; /*0x08 */
+ U32 Reserved1; /*0x10 */
+ U32 Reserved2; /*0x14 */
+} MPI2_BOOT_DEVICE_SAS_WWID,
+ *PTR_MPI2_BOOT_DEVICE_SAS_WWID,
+ Mpi2BootDeviceSasWwid_t,
+ *pMpi2BootDeviceSasWwid_t;
+
+typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT {
+ U64 EnclosureLogicalID; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ U16 SlotNumber; /*0x10 */
+ U16 Reserved3; /*0x12 */
+ U32 Reserved4; /*0x14 */
+} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
+ *PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
+ Mpi2BootDeviceEnclosureSlot_t,
+ *pMpi2BootDeviceEnclosureSlot_t;
+
+typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME {
+ U64 DeviceName; /*0x00 */
+ U8 LUN[8]; /*0x08 */
+ U32 Reserved1; /*0x10 */
+ U32 Reserved2; /*0x14 */
+} MPI2_BOOT_DEVICE_DEVICE_NAME,
+ *PTR_MPI2_BOOT_DEVICE_DEVICE_NAME,
+ Mpi2BootDeviceDeviceName_t,
+ *pMpi2BootDeviceDeviceName_t;
+
+typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE {
+ MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder;
+ MPI2_BOOT_DEVICE_SAS_WWID SasWwid;
+ MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot;
+ MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName;
+} MPI2_BIOSPAGE2_BOOT_DEVICE,
+ *PTR_MPI2_BIOSPAGE2_BOOT_DEVICE,
+ Mpi2BiosPage2BootDevice_t,
+ *pMpi2BiosPage2BootDevice_t;
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_2 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U32 Reserved3; /*0x0C */
+ U32 Reserved4; /*0x10 */
+ U32 Reserved5; /*0x14 */
+ U32 Reserved6; /*0x18 */
+ U8 ReqBootDeviceForm; /*0x1C */
+ U8 Reserved7; /*0x1D */
+ U16 Reserved8; /*0x1E */
+ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /*0x20 */
+ U8 ReqAltBootDeviceForm; /*0x38 */
+ U8 Reserved9; /*0x39 */
+ U16 Reserved10; /*0x3A */
+ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /*0x3C */
+ U8 CurrentBootDeviceForm; /*0x58 */
+ U8 Reserved11; /*0x59 */
+ U16 Reserved12; /*0x5A */
+ MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /*0x58 */
+} MPI2_CONFIG_PAGE_BIOS_2, *PTR_MPI2_CONFIG_PAGE_BIOS_2,
+ Mpi2BiosPage2_t, *pMpi2BiosPage2_t;
+
+#define MPI2_BIOSPAGE2_PAGEVERSION (0x04)
+
+/*values for BIOS Page 2 BootDeviceForm fields */
+#define MPI2_BIOSPAGE2_FORM_MASK (0x0F)
+#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00)
+#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05)
+#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06)
+#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07)
+
+
+/*BIOS Page 3 */
+
+#define MPI2_BIOSPAGE3_NUM_ADAPTER (4)
+
+typedef struct _MPI2_ADAPTER_INFO {
+ U8 PciBusNumber; /*0x00 */
+ U8 PciDeviceAndFunctionNumber; /*0x01 */
+ U16 AdapterFlags; /*0x02 */
+} MPI2_ADAPTER_INFO, *PTR_MPI2_ADAPTER_INFO,
+ Mpi2AdapterInfo_t, *pMpi2AdapterInfo_t;
+
+#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001)
+#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002)
+
+typedef struct _MPI2_ADAPTER_ORDER_AUX {
+ U64 WWID; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+} MPI2_ADAPTER_ORDER_AUX, *PTR_MPI2_ADAPTER_ORDER_AUX,
+ Mpi2AdapterOrderAux_t, *pMpi2AdapterOrderAux_t;
+
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_3 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 GlobalFlags; /*0x04 */
+ U32 BiosVersion; /*0x08 */
+ MPI2_ADAPTER_INFO AdapterOrder[MPI2_BIOSPAGE3_NUM_ADAPTER];
+ U32 Reserved1; /*0x1C */
+ MPI2_ADAPTER_ORDER_AUX AdapterOrderAux[MPI2_BIOSPAGE3_NUM_ADAPTER];
+} MPI2_CONFIG_PAGE_BIOS_3,
+ *PTR_MPI2_CONFIG_PAGE_BIOS_3,
+ Mpi2BiosPage3_t, *pMpi2BiosPage3_t;
+
+#define MPI2_BIOSPAGE3_PAGEVERSION (0x01)
+
+/*values for BIOS Page 3 GlobalFlags */
+#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002)
+#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004)
+#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010)
+
+#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0)
+#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000)
+#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020)
+#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040)
+
+
+/*BIOS Page 4 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using Phy[].
+ */
+
+typedef struct _MPI2_BIOS4_ENTRY {
+ U64 ReassignmentWWID; /*0x00 */
+ U64 ReassignmentDeviceName; /*0x08 */
+} MPI2_BIOS4_ENTRY, *PTR_MPI2_BIOS4_ENTRY,
+ Mpi2MBios4Entry_t, *pMpi2Bios4Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_4 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhys; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ MPI2_BIOS4_ENTRY Phy[]; /*0x08 */
+} MPI2_CONFIG_PAGE_BIOS_4, *PTR_MPI2_CONFIG_PAGE_BIOS_4,
+ Mpi2BiosPage4_t, *pMpi2BiosPage4_t;
+
+#define MPI2_BIOSPAGE4_PAGEVERSION (0x01)
+
+
+/****************************************************************************
+* RAID Volume Config Pages
+****************************************************************************/
+
+/*RAID Volume Page 0 */
+
+typedef struct _MPI2_RAIDVOL0_PHYS_DISK {
+ U8 RAIDSetNum; /*0x00 */
+ U8 PhysDiskMap; /*0x01 */
+ U8 PhysDiskNum; /*0x02 */
+ U8 Reserved; /*0x03 */
+} MPI2_RAIDVOL0_PHYS_DISK, *PTR_MPI2_RAIDVOL0_PHYS_DISK,
+ Mpi2RaidVol0PhysDisk_t, *pMpi2RaidVol0PhysDisk_t;
+
+/*defines for the PhysDiskMap field */
+#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01)
+#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02)
+
+typedef struct _MPI2_RAIDVOL0_SETTINGS {
+ U16 Settings; /*0x00 */
+ U8 HotSparePool; /*0x01 */
+ U8 Reserved; /*0x02 */
+} MPI2_RAIDVOL0_SETTINGS, *PTR_MPI2_RAIDVOL0_SETTINGS,
+ Mpi2RaidVol0Settings_t,
+ *pMpi2RaidVol0Settings_t;
+
+/*RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */
+#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01)
+#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02)
+#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04)
+#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08)
+#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10)
+#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20)
+#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40)
+#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80)
+
+/*RAID Volume Page 0 VolumeSettings defines */
+#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008)
+#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004)
+
+#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003)
+#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000)
+#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001)
+#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhysDisks at runtime before using PhysDisk[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 DevHandle; /*0x04 */
+ U8 VolumeState; /*0x06 */
+ U8 VolumeType; /*0x07 */
+ U32 VolumeStatusFlags; /*0x08 */
+ MPI2_RAIDVOL0_SETTINGS VolumeSettings; /*0x0C */
+ U64 MaxLBA; /*0x10 */
+ U32 StripeSize; /*0x18 */
+ U16 BlockSize; /*0x1C */
+ U16 Reserved1; /*0x1E */
+ U8 SupportedPhysDisks;/*0x20 */
+ U8 ResyncRate; /*0x21 */
+ U16 DataScrubDuration; /*0x22 */
+ U8 NumPhysDisks; /*0x24 */
+ U8 Reserved2; /*0x25 */
+ U8 Reserved3; /*0x26 */
+ U8 InactiveStatus; /*0x27 */
+ MPI2_RAIDVOL0_PHYS_DISK PhysDisk[]; /*0x28 */
+} MPI2_CONFIG_PAGE_RAID_VOL_0,
+ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_0,
+ Mpi2RaidVolPage0_t, *pMpi2RaidVolPage0_t;
+
+#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A)
+
+/*values for RAID VolumeState */
+#define MPI2_RAID_VOL_STATE_MISSING (0x00)
+#define MPI2_RAID_VOL_STATE_FAILED (0x01)
+#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02)
+#define MPI2_RAID_VOL_STATE_ONLINE (0x03)
+#define MPI2_RAID_VOL_STATE_DEGRADED (0x04)
+#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05)
+
+/*values for RAID VolumeType */
+#define MPI2_RAID_VOL_TYPE_RAID0 (0x00)
+#define MPI2_RAID_VOL_TYPE_RAID1E (0x01)
+#define MPI2_RAID_VOL_TYPE_RAID1 (0x02)
+#define MPI2_RAID_VOL_TYPE_RAID10 (0x05)
+#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF)
+
+/*values for RAID Volume Page 0 VolumeStatusFlags field */
+#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080)
+#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020)
+#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008)
+#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004)
+#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002)
+#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001)
+
+/*values for RAID Volume Page 0 SupportedPhysDisks field */
+#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08)
+#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04)
+#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02)
+#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01)
+
+/*values for RAID Volume Page 0 InactiveStatus field */
+#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00)
+#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01)
+#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02)
+#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03)
+#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04)
+#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05)
+#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06)
+
+
+/*RAID Volume Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 DevHandle; /*0x04 */
+ U16 Reserved0; /*0x06 */
+ U8 GUID[24]; /*0x08 */
+ U8 Name[16]; /*0x20 */
+ U64 WWID; /*0x30 */
+ U32 Reserved1; /*0x38 */
+ U32 Reserved2; /*0x3C */
+} MPI2_CONFIG_PAGE_RAID_VOL_1,
+ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_1,
+ Mpi2RaidVolPage1_t, *pMpi2RaidVolPage1_t;
+
+#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03)
+
+
+/****************************************************************************
+* RAID Physical Disk Config Pages
+****************************************************************************/
+
+/*RAID Physical Disk Page 0 */
+
+typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS {
+ U16 Reserved1; /*0x00 */
+ U8 HotSparePool; /*0x02 */
+ U8 Reserved2; /*0x03 */
+} MPI2_RAIDPHYSDISK0_SETTINGS,
+ *PTR_MPI2_RAIDPHYSDISK0_SETTINGS,
+ Mpi2RaidPhysDisk0Settings_t,
+ *pMpi2RaidPhysDisk0Settings_t;
+
+/*use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */
+
+typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA {
+ U8 VendorID[8]; /*0x00 */
+ U8 ProductID[16]; /*0x08 */
+ U8 ProductRevLevel[4]; /*0x18 */
+ U8 SerialNum[32]; /*0x1C */
+} MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
+ *PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
+ Mpi2RaidPhysDisk0InquiryData_t,
+ *pMpi2RaidPhysDisk0InquiryData_t;
+
+typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 DevHandle; /*0x04 */
+ U8 Reserved1; /*0x06 */
+ U8 PhysDiskNum; /*0x07 */
+ MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /*0x10 */
+ U32 Reserved3; /*0x4C */
+ U8 PhysDiskState; /*0x50 */
+ U8 OfflineReason; /*0x51 */
+ U8 IncompatibleReason; /*0x52 */
+ U8 PhysDiskAttributes; /*0x53 */
+ U32 PhysDiskStatusFlags;/*0x54 */
+ U64 DeviceMaxLBA; /*0x58 */
+ U64 HostMaxLBA; /*0x60 */
+ U64 CoercedMaxLBA; /*0x68 */
+ U16 BlockSize; /*0x70 */
+ U16 Reserved5; /*0x72 */
+ U32 Reserved6; /*0x74 */
+} MPI2_CONFIG_PAGE_RD_PDISK_0,
+ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_0,
+ Mpi2RaidPhysDiskPage0_t,
+ *pMpi2RaidPhysDiskPage0_t;
+
+#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05)
+
+/*PhysDiskState defines */
+#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00)
+#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01)
+#define MPI2_RAID_PD_STATE_OFFLINE (0x02)
+#define MPI2_RAID_PD_STATE_ONLINE (0x03)
+#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04)
+#define MPI2_RAID_PD_STATE_DEGRADED (0x05)
+#define MPI2_RAID_PD_STATE_REBUILDING (0x06)
+#define MPI2_RAID_PD_STATE_OPTIMAL (0x07)
+
+/*OfflineReason defines */
+#define MPI2_PHYSDISK0_ONLINE (0x00)
+#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01)
+#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03)
+#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04)
+#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05)
+#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06)
+#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF)
+
+/*IncompatibleReason defines */
+#define MPI2_PHYSDISK0_COMPATIBLE (0x00)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF)
+
+/*PhysDiskAttributes defines */
+#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C)
+#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08)
+#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04)
+
+#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03)
+#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02)
+#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01)
+
+/*PhysDiskStatusFlags defines */
+#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020)
+#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000)
+#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008)
+#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004)
+#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001)
+
+
+/*RAID Physical Disk Page 1 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhysDiskPaths at runtime before using PhysicalDiskPath[].
+ */
+
+typedef struct _MPI2_RAIDPHYSDISK1_PATH {
+ U16 DevHandle; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U64 WWID; /*0x04 */
+ U64 OwnerWWID; /*0x0C */
+ U8 OwnerIdentifier; /*0x14 */
+ U8 Reserved2; /*0x15 */
+ U16 Flags; /*0x16 */
+} MPI2_RAIDPHYSDISK1_PATH, *PTR_MPI2_RAIDPHYSDISK1_PATH,
+ Mpi2RaidPhysDisk1Path_t,
+ *pMpi2RaidPhysDisk1Path_t;
+
+/*RAID Physical Disk Page 1 Physical Disk Path Flags field defines */
+#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004)
+#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002)
+#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001)
+
+typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhysDiskPaths; /*0x04 */
+ U8 PhysDiskNum; /*0x05 */
+ U16 Reserved1; /*0x06 */
+ U32 Reserved2; /*0x08 */
+ MPI2_RAIDPHYSDISK1_PATH PhysicalDiskPath[]; /*0x0C */
+} MPI2_CONFIG_PAGE_RD_PDISK_1,
+ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_1,
+ Mpi2RaidPhysDiskPage1_t,
+ *pMpi2RaidPhysDiskPage1_t;
+
+#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02)
+
+
+/****************************************************************************
+* values for fields used by several types of SAS Config Pages
+****************************************************************************/
+
+/*values for NegotiatedLinkRates fields */
+#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0)
+#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4)
+#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F)
+/*link rates used for Negotiated Physical and Logical Link Rate */
+#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00)
+#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02)
+#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03)
+#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04)
+#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06)
+#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08)
+#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09)
+#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A)
+#define MPI25_SAS_NEG_LINK_RATE_12_0 (0x0B)
+#define MPI26_SAS_NEG_LINK_RATE_22_5 (0x0C)
+
+
+/*values for AttachedPhyInfo fields */
+#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040)
+#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020)
+#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010)
+
+#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F)
+#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001)
+#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002)
+#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003)
+#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004)
+#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005)
+#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006)
+#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007)
+#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008)
+
+
+/*values for PhyInfo fields */
+#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000)
+
+#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
+#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27)
+#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000)
+
+#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000)
+#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000)
+#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000)
+#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000)
+#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000)
+#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000)
+
+#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000)
+#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000)
+#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000)
+#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000)
+#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000)
+#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000)
+#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000)
+#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000)
+#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000)
+
+#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000)
+#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000)
+#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000)
+#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000)
+
+#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00)
+#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8)
+
+#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0)
+#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000)
+#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010)
+#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020)
+
+
+/*values for SAS ProgrammedLinkRate fields */
+#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0)
+#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80)
+#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90)
+#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0)
+#define MPI25_SAS_PRATE_MAX_RATE_12_0 (0xB0)
+#define MPI26_SAS_PRATE_MAX_RATE_22_5 (0xC0)
+#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F)
+#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08)
+#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09)
+#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A)
+#define MPI25_SAS_PRATE_MIN_RATE_12_0 (0x0B)
+#define MPI26_SAS_PRATE_MIN_RATE_22_5 (0x0C)
+
+
+/*values for SAS HwLinkRate fields */
+#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0)
+#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80)
+#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90)
+#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0)
+#define MPI25_SAS_HWRATE_MAX_RATE_12_0 (0xB0)
+#define MPI26_SAS_HWRATE_MAX_RATE_22_5 (0xC0)
+#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F)
+#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08)
+#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09)
+#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A)
+#define MPI25_SAS_HWRATE_MIN_RATE_12_0 (0x0B)
+#define MPI26_SAS_HWRATE_MIN_RATE_22_5 (0x0C)
+
+
+
+/****************************************************************************
+* SAS IO Unit Config Pages
+****************************************************************************/
+
+/*SAS IO Unit Page 0 */
+
+typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA {
+ U8 Port; /*0x00 */
+ U8 PortFlags; /*0x01 */
+ U8 PhyFlags; /*0x02 */
+ U8 NegotiatedLinkRate; /*0x03 */
+ U32 ControllerPhyDeviceInfo;/*0x04 */
+ U16 AttachedDevHandle; /*0x08 */
+ U16 ControllerDevHandle; /*0x0A */
+ U32 DiscoveryStatus; /*0x0C */
+ U32 Reserved; /*0x10 */
+} MPI2_SAS_IO_UNIT0_PHY_DATA,
+ *PTR_MPI2_SAS_IO_UNIT0_PHY_DATA,
+ Mpi2SasIOUnit0PhyData_t,
+ *pMpi2SasIOUnit0PhyData_t;
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using PhyData[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1;/*0x08 */
+ U8 NumPhys; /*0x0C */
+ U8 Reserved2;/*0x0D */
+ U16 Reserved3;/*0x0E */
+ MPI2_SAS_IO_UNIT0_PHY_DATA PhyData[];/*0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_0,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0,
+ Mpi2SasIOUnitPage0_t, *pMpi2SasIOUnitPage0_t;
+
+#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05)
+
+/*values for SAS IO Unit Page 0 PortFlags */
+#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08)
+#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01)
+
+/*values for SAS IO Unit Page 0 PhyFlags */
+#define MPI2_SASIOUNIT0_PHYFLAGS_INIT_PERSIST_CONNECT (0x40)
+#define MPI2_SASIOUNIT0_PHYFLAGS_TARG_PERSIST_CONNECT (0x20)
+#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10)
+#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/*see mpi2_sas.h for values for
+ *SAS IO Unit Page 0 ControllerPhyDeviceInfo values */
+
+/*values for SAS IO Unit Page 0 DiscoveryStatus */
+#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400)
+#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001)
+
+
+/*SAS IO Unit Page 1 */
+
+typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA {
+ U8 Port; /*0x00 */
+ U8 PortFlags; /*0x01 */
+ U8 PhyFlags; /*0x02 */
+ U8 MaxMinLinkRate; /*0x03 */
+ U32 ControllerPhyDeviceInfo; /*0x04 */
+ U16 MaxTargetPortConnectTime; /*0x08 */
+ U16 Reserved1; /*0x0A */
+} MPI2_SAS_IO_UNIT1_PHY_DATA,
+ *PTR_MPI2_SAS_IO_UNIT1_PHY_DATA,
+ Mpi2SasIOUnit1PhyData_t,
+ *pMpi2SasIOUnit1PhyData_t;
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using PhyData[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U16
+ ControlFlags; /*0x08 */
+ U16
+ SASNarrowMaxQueueDepth; /*0x0A */
+ U16
+ AdditionalControlFlags; /*0x0C */
+ U16
+ SASWideMaxQueueDepth; /*0x0E */
+ U8
+ NumPhys; /*0x10 */
+ U8
+ SATAMaxQDepth; /*0x11 */
+ U8
+ ReportDeviceMissingDelay; /*0x12 */
+ U8
+ IODeviceMissingDelay; /*0x13 */
+ MPI2_SAS_IO_UNIT1_PHY_DATA
+ PhyData[]; /*0x14 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_1,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1,
+ Mpi2SasIOUnitPage1_t, *pMpi2SasIOUnitPage1_t;
+
+#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09)
+
+/*values for SAS IO Unit Page 1 ControlFlags */
+#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000)
+
+#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600)
+#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2)
+
+#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010)
+#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008)
+#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004)
+#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002)
+#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001)
+
+/*values for SAS IO Unit Page 1 AdditionalControlFlags */
+#define MPI2_SASIOUNIT1_ACONTROL_PROD_SPECIFIC_1 (0x8000)
+#define MPI2_SASIOUNIT1_ACONTROL_DA_PERSIST_CONNECT (0x0100)
+#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080)
+#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040)
+#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020)
+#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010)
+#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008)
+#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004)
+#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002)
+#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001)
+
+/*defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */
+#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F)
+#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80)
+
+/*values for SAS IO Unit Page 1 PortFlags */
+#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
+
+/*values for SAS IO Unit Page 1 PhyFlags */
+#define MPI2_SASIOUNIT1_PHYFLAGS_INIT_PERSIST_CONNECT (0x40)
+#define MPI2_SASIOUNIT1_PHYFLAGS_TARG_PERSIST_CONNECT (0x20)
+#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10)
+#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+
+/*values for SAS IO Unit Page 1 MaxMinLinkRate */
+#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0)
+#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80)
+#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90)
+#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0)
+#define MPI25_SASIOUNIT1_MAX_RATE_12_0 (0xB0)
+#define MPI26_SASIOUNIT1_MAX_RATE_22_5 (0xC0)
+#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F)
+#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08)
+#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09)
+#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A)
+#define MPI25_SASIOUNIT1_MIN_RATE_12_0 (0x0B)
+#define MPI26_SASIOUNIT1_MIN_RATE_22_5 (0x0C)
+
+/*see mpi2_sas.h for values for
+ *SAS IO Unit Page 1 ControllerPhyDeviceInfo values */
+
+
+/*SAS IO Unit Page 4 (for MPI v2.5 and earlier) */
+
+typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP {
+ U8 MaxTargetSpinup; /*0x00 */
+ U8 SpinupDelay; /*0x01 */
+ U8 SpinupFlags; /*0x02 */
+ U8 Reserved1; /*0x03 */
+} MPI2_SAS_IOUNIT4_SPINUP_GROUP,
+ *PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP,
+ Mpi2SasIOUnit4SpinupGroup_t,
+ *pMpi2SasIOUnit4SpinupGroup_t;
+/*defines for SAS IO Unit Page 4 SpinupFlags */
+#define MPI2_SASIOUNIT4_SPINUP_DISABLE_FLAG (0x01)
+
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT4_PHY_MAX
+#define MPI2_SAS_IOUNIT4_PHY_MAX (4)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header;/*0x00 */
+ MPI2_SAS_IOUNIT4_SPINUP_GROUP
+ SpinupGroupParameters[4]; /*0x08 */
+ U32
+ Reserved1; /*0x18 */
+ U32
+ Reserved2; /*0x1C */
+ U32
+ Reserved3; /*0x20 */
+ U8
+ BootDeviceWaitTime; /*0x24 */
+ U8
+ SATADeviceWaitTime; /*0x25 */
+ U16
+ Reserved5; /*0x26 */
+ U8
+ NumPhys; /*0x28 */
+ U8
+ PEInitialSpinupDelay; /*0x29 */
+ U8
+ PEReplyDelay; /*0x2A */
+ U8
+ Flags; /*0x2B */
+ U8
+ PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /*0x2C */
+} MPI2_CONFIG_PAGE_SASIOUNIT_4,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4,
+ Mpi2SasIOUnitPage4_t, *pMpi2SasIOUnitPage4_t;
+
+#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02)
+
+/*defines for Flags field */
+#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01)
+
+/*defines for PHY field */
+#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03)
+
+
+/*SAS IO Unit Page 5 */
+
+typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS {
+ U8 ControlFlags; /*0x00 */
+ U8 PortWidthModGroup; /*0x01 */
+ U16 InactivityTimerExponent; /*0x02 */
+ U8 SATAPartialTimeout; /*0x04 */
+ U8 Reserved2; /*0x05 */
+ U8 SATASlumberTimeout; /*0x06 */
+ U8 Reserved3; /*0x07 */
+ U8 SASPartialTimeout; /*0x08 */
+ U8 Reserved4; /*0x09 */
+ U8 SASSlumberTimeout; /*0x0A */
+ U8 Reserved5; /*0x0B */
+} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+ *PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+ Mpi2SasIOUnit5PhyPmSettings_t,
+ *pMpi2SasIOUnit5PhyPmSettings_t;
+
+/*defines for ControlFlags field */
+#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08)
+#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01)
+
+/*defines for PortWidthModeGroup field */
+#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF)
+
+/*defines for InactivityTimerExponent field */
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12)
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0)
+
+#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7)
+#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5)
+#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4)
+#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2)
+#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1)
+#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using SASPhyPowerManagementSettings[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhys; /*0x08 */
+ U8 Reserved1;/*0x09 */
+ U16 Reserved2;/*0x0A */
+ U32 Reserved3;/*0x0C */
+ MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS
+ SASPhyPowerManagementSettings[]; /*0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_5,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
+ Mpi2SasIOUnitPage5_t, *pMpi2SasIOUnitPage5_t;
+
+#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01)
+
+
+/*SAS IO Unit Page 6 */
+
+typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS {
+ U8 CurrentStatus; /*0x00 */
+ U8 CurrentModulation; /*0x01 */
+ U8 CurrentUtilization; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 Reserved2; /*0x04 */
+} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+ *PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+ Mpi2SasIOUnit6PortWidthModGroupStatus_t,
+ *pMpi2SasIOUnit6PortWidthModGroupStatus_t;
+
+/*defines for CurrentStatus field */
+#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00)
+#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01)
+#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02)
+#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03)
+#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04)
+#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07)
+
+/*defines for CurrentModulation field */
+#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00)
+#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01)
+#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02)
+#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumGroups at runtime before using PortWidthModulationGroupStatus[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ U8 NumGroups; /*0x10 */
+ U8 Reserved3; /*0x11 */
+ U16 Reserved4; /*0x12 */
+ MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS
+ PortWidthModulationGroupStatus[]; /*0x14 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_6,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6,
+ Mpi2SasIOUnitPage6_t, *pMpi2SasIOUnitPage6_t;
+
+#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00)
+
+
+/*SAS IO Unit Page 7 */
+
+typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS {
+ U8 Flags; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U8 Threshold75Pct; /*0x04 */
+ U8 Threshold50Pct; /*0x05 */
+ U8 Threshold25Pct; /*0x06 */
+ U8 Reserved3; /*0x07 */
+} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+ *PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+ Mpi2SasIOUnit7PortWidthModGroupSettings_t,
+ *pMpi2SasIOUnit7PortWidthModGroupSettings_t;
+
+/*defines for Flags field */
+#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01)
+
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumGroups at runtime before using PortWidthModulationGroupSettings[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 SamplingInterval; /*0x08 */
+ U8 WindowLength; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Reserved2; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U8 NumGroups; /*0x14 */
+ U8 Reserved4; /*0x15 */
+ U16 Reserved5; /*0x16 */
+ MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS
+ PortWidthModulationGroupSettings[]; /*0x18 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_7,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7,
+ Mpi2SasIOUnitPage7_t, *pMpi2SasIOUnitPage7_t;
+
+#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00)
+
+
+/*SAS IO Unit Page 8 */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U32
+ PowerManagementCapabilities; /*0x0C */
+ U8
+ TxRxSleepStatus; /*0x10 */
+ U8
+ Reserved2; /*0x11 */
+ U16
+ Reserved3; /*0x12 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_8,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8,
+ Mpi2SasIOUnitPage8_t, *pMpi2SasIOUnitPage8_t;
+
+#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00)
+
+/*defines for PowerManagementCapabilities field */
+#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x00001000)
+#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x00000800)
+#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x00000400)
+#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x00000200)
+#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x00000100)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x00000010)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001)
+
+/*defines for TxRxSleepStatus field */
+#define MPI25_SASIOUNIT8_TXRXSLEEP_UNSUPPORTED (0x00)
+#define MPI25_SASIOUNIT8_TXRXSLEEP_DISENGAGED (0x01)
+#define MPI25_SASIOUNIT8_TXRXSLEEP_ACTIVE (0x02)
+#define MPI25_SASIOUNIT8_TXRXSLEEP_SHUTDOWN (0x03)
+
+
+
+/*SAS IO Unit Page 16 */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT16 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U64
+ TimeStamp; /*0x08 */
+ U32
+ Reserved1; /*0x10 */
+ U32
+ Reserved2; /*0x14 */
+ U32
+ FastPathPendedRequests; /*0x18 */
+ U32
+ FastPathUnPendedRequests; /*0x1C */
+ U32
+ FastPathHostRequestStarts; /*0x20 */
+ U32
+ FastPathFirmwareRequestStarts; /*0x24 */
+ U32
+ FastPathHostCompletions; /*0x28 */
+ U32
+ FastPathFirmwareCompletions; /*0x2C */
+ U32
+ NonFastPathRequestStarts; /*0x30 */
+ U32
+ NonFastPathHostCompletions; /*0x30 */
+} MPI2_CONFIG_PAGE_SASIOUNIT16,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT16,
+ Mpi2SasIOUnitPage16_t, *pMpi2SasIOUnitPage16_t;
+
+#define MPI2_SASIOUNITPAGE16_PAGEVERSION (0x00)
+
+
+/****************************************************************************
+* SAS Expander Config Pages
+****************************************************************************/
+
+/*SAS Expander Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U8
+ PhysicalPort; /*0x08 */
+ U8
+ ReportGenLength; /*0x09 */
+ U16
+ EnclosureHandle; /*0x0A */
+ U64
+ SASAddress; /*0x0C */
+ U32
+ DiscoveryStatus; /*0x14 */
+ U16
+ DevHandle; /*0x18 */
+ U16
+ ParentDevHandle; /*0x1A */
+ U16
+ ExpanderChangeCount; /*0x1C */
+ U16
+ ExpanderRouteIndexes; /*0x1E */
+ U8
+ NumPhys; /*0x20 */
+ U8
+ SASLevel; /*0x21 */
+ U16
+ Flags; /*0x22 */
+ U16
+ STPBusInactivityTimeLimit; /*0x24 */
+ U16
+ STPMaxConnectTimeLimit; /*0x26 */
+ U16
+ STP_SMP_NexusLossTime; /*0x28 */
+ U16
+ MaxNumRoutedSasAddresses; /*0x2A */
+ U64
+ ActiveZoneManagerSASAddress;/*0x2C */
+ U16
+ ZoneLockInactivityLimit; /*0x34 */
+ U16
+ Reserved1; /*0x36 */
+ U8
+ TimeToReducedFunc; /*0x38 */
+ U8
+ InitialTimeToReducedFunc; /*0x39 */
+ U8
+ MaxReducedFuncTime; /*0x3A */
+ U8
+ Reserved2; /*0x3B */
+} MPI2_CONFIG_PAGE_EXPANDER_0,
+ *PTR_MPI2_CONFIG_PAGE_EXPANDER_0,
+ Mpi2ExpanderPage0_t, *pMpi2ExpanderPage0_t;
+
+#define MPI2_SASEXPANDER0_PAGEVERSION (0x06)
+
+/*values for SAS Expander Page 0 DiscoveryStatus field */
+#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400)
+#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001)
+
+/*values for SAS Expander Page 0 Flags field */
+#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000)
+#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000)
+#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800)
+#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400)
+#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200)
+#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100)
+#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080)
+#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010)
+#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004)
+#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002)
+#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001)
+
+
+/*SAS Expander Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U8
+ PhysicalPort; /*0x08 */
+ U8
+ Reserved1; /*0x09 */
+ U16
+ Reserved2; /*0x0A */
+ U8
+ NumPhys; /*0x0C */
+ U8
+ Phy; /*0x0D */
+ U16
+ NumTableEntriesProgrammed; /*0x0E */
+ U8
+ ProgrammedLinkRate; /*0x10 */
+ U8
+ HwLinkRate; /*0x11 */
+ U16
+ AttachedDevHandle; /*0x12 */
+ U32
+ PhyInfo; /*0x14 */
+ U32
+ AttachedDeviceInfo; /*0x18 */
+ U16
+ ExpanderDevHandle; /*0x1C */
+ U8
+ ChangeCount; /*0x1E */
+ U8
+ NegotiatedLinkRate; /*0x1F */
+ U8
+ PhyIdentifier; /*0x20 */
+ U8
+ AttachedPhyIdentifier; /*0x21 */
+ U8
+ Reserved3; /*0x22 */
+ U8
+ DiscoveryInfo; /*0x23 */
+ U32
+ AttachedPhyInfo; /*0x24 */
+ U8
+ ZoneGroup; /*0x28 */
+ U8
+ SelfConfigStatus; /*0x29 */
+ U16
+ Reserved4; /*0x2A */
+} MPI2_CONFIG_PAGE_EXPANDER_1,
+ *PTR_MPI2_CONFIG_PAGE_EXPANDER_1,
+ Mpi2ExpanderPage1_t, *pMpi2ExpanderPage1_t;
+
+#define MPI2_SASEXPANDER1_PAGEVERSION (0x02)
+
+/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
+
+/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
+
+/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */
+
+/*see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines
+ *used for the AttachedDeviceInfo field */
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/*values for SAS Expander Page 1 DiscoveryInfo field */
+#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04)
+#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02)
+#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01)
+
+/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
+
+
+/****************************************************************************
+* SAS Device Config Pages
+****************************************************************************/
+
+/*SAS Device Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U16
+ Slot; /*0x08 */
+ U16
+ EnclosureHandle; /*0x0A */
+ U64
+ SASAddress; /*0x0C */
+ U16
+ ParentDevHandle; /*0x14 */
+ U8
+ PhyNum; /*0x16 */
+ U8
+ AccessStatus; /*0x17 */
+ U16
+ DevHandle; /*0x18 */
+ U8
+ AttachedPhyIdentifier; /*0x1A */
+ U8
+ ZoneGroup; /*0x1B */
+ U32
+ DeviceInfo; /*0x1C */
+ U16
+ Flags; /*0x20 */
+ U8
+ PhysicalPort; /*0x22 */
+ U8
+ MaxPortConnections; /*0x23 */
+ U64
+ DeviceName; /*0x24 */
+ U8
+ PortGroups; /*0x2C */
+ U8
+ DmaGroup; /*0x2D */
+ U8
+ ControlGroup; /*0x2E */
+ U8
+ EnclosureLevel; /*0x2F */
+ U32
+ ConnectorName[4]; /*0x30 */
+ U32
+ Reserved3; /*0x34 */
+} MPI2_CONFIG_PAGE_SAS_DEV_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_0,
+ Mpi2SasDevicePage0_t,
+ *pMpi2SasDevicePage0_t;
+
+#define MPI2_SASDEVICE0_PAGEVERSION (0x09)
+
+/*values for SAS Device Page 0 AccessStatus field */
+#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04)
+#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05)
+#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06)
+#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07)
+/*specific values for SATA Init failures */
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F)
+
+/*see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */
+
+/*values for SAS Device Page 0 Flags field */
+#define MPI2_SAS_DEVICE0_FLAGS_UNAUTHORIZED_DEVICE (0x8000)
+#define MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH (0x4000)
+#define MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE (0x2000)
+#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000)
+#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200)
+#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010)
+#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008)
+#define MPI2_SAS_DEVICE0_FLAGS_PERSIST_CAPABLE (0x0004)
+#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002)
+#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001)
+
+
+/*SAS Device Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U64
+ SASAddress; /*0x0C */
+ U32
+ Reserved2; /*0x14 */
+ U16
+ DevHandle; /*0x18 */
+ U16
+ Reserved3; /*0x1A */
+ U8
+ InitialRegDeviceFIS[20];/*0x1C */
+} MPI2_CONFIG_PAGE_SAS_DEV_1,
+ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_1,
+ Mpi2SasDevicePage1_t,
+ *pMpi2SasDevicePage1_t;
+
+#define MPI2_SASDEVICE1_PAGEVERSION (0x01)
+
+
+/****************************************************************************
+* SAS PHY Config Pages
+****************************************************************************/
+
+/*SAS PHY Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U16
+ OwnerDevHandle; /*0x08 */
+ U16
+ Reserved1; /*0x0A */
+ U16
+ AttachedDevHandle; /*0x0C */
+ U8
+ AttachedPhyIdentifier; /*0x0E */
+ U8
+ Reserved2; /*0x0F */
+ U32
+ AttachedPhyInfo; /*0x10 */
+ U8
+ ProgrammedLinkRate; /*0x14 */
+ U8
+ HwLinkRate; /*0x15 */
+ U8
+ ChangeCount; /*0x16 */
+ U8
+ Flags; /*0x17 */
+ U32
+ PhyInfo; /*0x18 */
+ U8
+ NegotiatedLinkRate; /*0x1C */
+ U8
+ Reserved3; /*0x1D */
+ U16
+ Reserved4; /*0x1E */
+} MPI2_CONFIG_PAGE_SAS_PHY_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_0,
+ Mpi2SasPhyPage0_t, *pMpi2SasPhyPage0_t;
+
+#define MPI2_SASPHY0_PAGEVERSION (0x03)
+
+/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
+
+/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
+
+/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
+
+/*values for SAS PHY Page 0 Flags field */
+#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01)
+
+/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+
+/*SAS PHY Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U32
+ InvalidDwordCount; /*0x0C */
+ U32
+ RunningDisparityErrorCount; /*0x10 */
+ U32
+ LossDwordSynchCount; /*0x14 */
+ U32
+ PhyResetProblemCount; /*0x18 */
+} MPI2_CONFIG_PAGE_SAS_PHY_1,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_1,
+ Mpi2SasPhyPage1_t, *pMpi2SasPhyPage1_t;
+
+#define MPI2_SASPHY1_PAGEVERSION (0x01)
+
+
+/*SAS PHY Page 2 */
+
+typedef struct _MPI2_SASPHY2_PHY_EVENT {
+ U8 PhyEventCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 PhyEventInfo; /*0x04 */
+} MPI2_SASPHY2_PHY_EVENT, *PTR_MPI2_SASPHY2_PHY_EVENT,
+ Mpi2SasPhy2PhyEvent_t, *pMpi2SasPhy2PhyEvent_t;
+
+/*use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */
+
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhyEvents at runtime before using PhyEvent[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U8
+ NumPhyEvents; /*0x0C */
+ U8
+ Reserved2; /*0x0D */
+ U16
+ Reserved3; /*0x0E */
+ MPI2_SASPHY2_PHY_EVENT
+ PhyEvent[]; /*0x10 */
+} MPI2_CONFIG_PAGE_SAS_PHY_2,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_2,
+ Mpi2SasPhyPage2_t,
+ *pMpi2SasPhyPage2_t;
+
+#define MPI2_SASPHY2_PAGEVERSION (0x00)
+
+
+/*SAS PHY Page 3 */
+
+typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG {
+ U8 PhyEventCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U8 CounterType; /*0x04 */
+ U8 ThresholdWindow; /*0x05 */
+ U8 TimeUnits; /*0x06 */
+ U8 Reserved3; /*0x07 */
+ U32 EventThreshold; /*0x08 */
+ U16 ThresholdFlags; /*0x0C */
+ U16 Reserved4; /*0x0E */
+} MPI2_SASPHY3_PHY_EVENT_CONFIG,
+ *PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG,
+ Mpi2SasPhy3PhyEventConfig_t,
+ *pMpi2SasPhy3PhyEventConfig_t;
+
+/*values for PhyEventCode field */
+#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00)
+#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01)
+#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02)
+#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03)
+#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04)
+#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05)
+#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06)
+#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20)
+#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22)
+#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23)
+#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26)
+#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27)
+#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28)
+#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29)
+#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A)
+#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B)
+#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C)
+#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D)
+#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43)
+#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44)
+#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51)
+#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63)
+#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0)
+#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2)
+
+/*Following codes are product specific and in MPI v2.6 and later */
+#define MPI2_SASPHY3_EVENT_CODE_LCARB_WAIT_TIME (0xD3)
+#define MPI2_SASPHY3_EVENT_CODE_RCVD_CONN_RESP_WAIT_TIME (0xD4)
+#define MPI2_SASPHY3_EVENT_CODE_LCCONN_TIME (0xD5)
+#define MPI2_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xD6)
+#define MPI2_SASPHY3_EVENT_CODE_SATA_TX_START (0xD7)
+#define MPI2_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xD8)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xD9)
+#define MPI2_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xDA)
+#define MPI2_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xDB)
+#define MPI2_SASPHY3_EVENT_CODE_SMP_RX_START_RECEIVE (0xDC)
+
+
+/*values for the CounterType field */
+#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00)
+#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01)
+#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02)
+
+/*values for the TimeUnits field */
+#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00)
+#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01)
+#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02)
+#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03)
+
+/*values for the ThresholdFlags field */
+#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002)
+#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhyEvents at runtime before using PhyEventConfig[].
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U8
+ NumPhyEvents; /*0x0C */
+ U8
+ Reserved2; /*0x0D */
+ U16
+ Reserved3; /*0x0E */
+ MPI2_SASPHY3_PHY_EVENT_CONFIG
+ PhyEventConfig[]; /*0x10 */
+} MPI2_CONFIG_PAGE_SAS_PHY_3,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_3,
+ Mpi2SasPhyPage3_t, *pMpi2SasPhyPage3_t;
+
+#define MPI2_SASPHY3_PAGEVERSION (0x00)
+
+
+/*SAS PHY Page 4 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U16
+ Reserved1; /*0x08 */
+ U8
+ Reserved2; /*0x0A */
+ U8
+ Flags; /*0x0B */
+ U8
+ InitialFrame[28]; /*0x0C */
+} MPI2_CONFIG_PAGE_SAS_PHY_4,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_4,
+ Mpi2SasPhyPage4_t, *pMpi2SasPhyPage4_t;
+
+#define MPI2_SASPHY4_PAGEVERSION (0x00)
+
+/*values for the Flags field */
+#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02)
+#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01)
+
+
+
+
+/****************************************************************************
+* SAS Port Config Pages
+****************************************************************************/
+
+/*SAS Port Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U8
+ PortNumber; /*0x08 */
+ U8
+ PhysicalPort; /*0x09 */
+ U8
+ PortWidth; /*0x0A */
+ U8
+ PhysicalPortWidth; /*0x0B */
+ U8
+ ZoneGroup; /*0x0C */
+ U8
+ Reserved1; /*0x0D */
+ U16
+ Reserved2; /*0x0E */
+ U64
+ SASAddress; /*0x10 */
+ U32
+ DeviceInfo; /*0x18 */
+ U32
+ Reserved3; /*0x1C */
+ U32
+ Reserved4; /*0x20 */
+} MPI2_CONFIG_PAGE_SAS_PORT_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PORT_0,
+ Mpi2SasPortPage0_t, *pMpi2SasPortPage0_t;
+
+#define MPI2_SASPORT0_PAGEVERSION (0x00)
+
+/*see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */
+
+
+/****************************************************************************
+* SAS Enclosure Config Pages
+****************************************************************************/
+
+/*SAS Enclosure Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U64 EnclosureLogicalID; /*0x0C */
+ U16 Flags; /*0x14 */
+ U16 EnclosureHandle; /*0x16 */
+ U16 NumSlots; /*0x18 */
+ U16 StartSlot; /*0x1A */
+ U8 ChassisSlot; /*0x1C */
+ U8 EnclosureLevel; /*0x1D */
+ U16 SEPDevHandle; /*0x1E */
+ U8 OEMRD; /*0x20 */
+ U8 Reserved1a; /*0x21 */
+ U16 Reserved2; /*0x22 */
+ U32 Reserved3; /*0x24 */
+} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
+ Mpi2SasEnclosurePage0_t, *pMpi2SasEnclosurePage0_t,
+ MPI26_CONFIG_PAGE_ENCLOSURE_0,
+ *PTR_MPI26_CONFIG_PAGE_ENCLOSURE_0,
+ Mpi26EnclosurePage0_t, *pMpi26EnclosurePage0_t;
+
+#define MPI2_SASENCLOSURE0_PAGEVERSION (0x04)
+
+/*values for SAS Enclosure Page 0 Flags field */
+#define MPI26_SAS_ENCLS0_FLAGS_OEMRD_VALID (0x0080)
+#define MPI26_SAS_ENCLS0_FLAGS_OEMRD_COLLECTING (0x0040)
+#define MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID (0x0020)
+#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005)
+
+#define MPI26_ENCLOSURE0_PAGEVERSION (0x04)
+
+/*Values for Enclosure Page 0 Flags field */
+#define MPI26_ENCLS0_FLAGS_OEMRD_VALID (0x0080)
+#define MPI26_ENCLS0_FLAGS_OEMRD_COLLECTING (0x0040)
+#define MPI26_ENCLS0_FLAGS_CHASSIS_SLOT_VALID (0x0020)
+#define MPI26_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010)
+#define MPI26_ENCLS0_FLAGS_MNG_MASK (0x000F)
+#define MPI26_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000)
+#define MPI26_ENCLS0_FLAGS_MNG_IOC_SES (0x0001)
+#define MPI26_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002)
+#define MPI26_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003)
+#define MPI26_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004)
+#define MPI26_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005)
+
+/****************************************************************************
+* Log Config Page
+****************************************************************************/
+
+/*Log Page 0 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumLogEntries at runtime before using LogEntry[].
+ */
+
+#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C)
+
+typedef struct _MPI2_LOG_0_ENTRY {
+ U64 TimeStamp; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U16 LogSequence; /*0x0C */
+ U16 LogEntryQualifier; /*0x0E */
+ U8 VP_ID; /*0x10 */
+ U8 VF_ID; /*0x11 */
+ U16 Reserved2; /*0x12 */
+ U8
+ LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/*0x14 */
+} MPI2_LOG_0_ENTRY, *PTR_MPI2_LOG_0_ENTRY,
+ Mpi2Log0Entry_t, *pMpi2Log0Entry_t;
+
+/*values for Log Page 0 LogEntry LogEntryQualifier field */
+#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000)
+#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001)
+#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002)
+#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000)
+#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF)
+
+typedef struct _MPI2_CONFIG_PAGE_LOG_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ U16 NumLogEntries;/*0x10 */
+ U16 Reserved3; /*0x12 */
+ MPI2_LOG_0_ENTRY LogEntry[]; /*0x14 */
+} MPI2_CONFIG_PAGE_LOG_0, *PTR_MPI2_CONFIG_PAGE_LOG_0,
+ Mpi2LogPage0_t, *pMpi2LogPage0_t;
+
+#define MPI2_LOG_0_PAGEVERSION (0x02)
+
+
+/****************************************************************************
+* RAID Config Page
+****************************************************************************/
+
+/*RAID Page 0 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumElements at runtime before using ConfigElement[].
+ */
+
+typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT {
+ U16 ElementFlags; /*0x00 */
+ U16 VolDevHandle; /*0x02 */
+ U8 HotSparePool; /*0x04 */
+ U8 PhysDiskNum; /*0x05 */
+ U16 PhysDiskDevHandle; /*0x06 */
+} MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
+ *PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
+ Mpi2RaidConfig0ConfigElement_t,
+ *pMpi2RaidConfig0ConfigElement_t;
+
+/*values for the ElementFlags field */
+#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F)
+#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000)
+#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001)
+#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002)
+#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003)
+
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 NumHotSpares; /*0x08 */
+ U8 NumPhysDisks; /*0x09 */
+ U8 NumVolumes; /*0x0A */
+ U8 ConfigNum; /*0x0B */
+ U32 Flags; /*0x0C */
+ U8 ConfigGUID[24]; /*0x10 */
+ U32 Reserved1; /*0x28 */
+ U8 NumElements; /*0x2C */
+ U8 Reserved2; /*0x2D */
+ U16 Reserved3; /*0x2E */
+ MPI2_RAIDCONFIG0_CONFIG_ELEMENT ConfigElement[];/*0x30 */
+} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
+ *PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
+ Mpi2RaidConfigurationPage0_t,
+ *pMpi2RaidConfigurationPage0_t;
+
+#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00)
+
+/*values for RAID Configuration Page 0 Flags field */
+#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001)
+
+
+/****************************************************************************
+* Driver Persistent Mapping Config Pages
+****************************************************************************/
+
+/*Driver Persistent Mapping Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY {
+ U64 PhysicalIdentifier; /*0x00 */
+ U16 MappingInformation; /*0x08 */
+ U16 DeviceIndex; /*0x0A */
+ U32 PhysicalBitsMapping; /*0x0C */
+ U32 Reserved1; /*0x10 */
+} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
+ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
+ Mpi2DriverMap0Entry_t, *pMpi2DriverMap0Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /*0x08 */
+} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
+ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
+ Mpi2DriverMappingPage0_t, *pMpi2DriverMappingPage0_t;
+
+#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00)
+
+/*values for Driver Persistent Mapping Page 0 MappingInformation field */
+#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0)
+#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4)
+#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F)
+
+
+/****************************************************************************
+* Ethernet Config Pages
+****************************************************************************/
+
+/*Ethernet Page 0 */
+
+/*IP address (union of IPv4 and IPv6) */
+typedef union _MPI2_ETHERNET_IP_ADDR {
+ U32 IPv4Addr;
+ U32 IPv6Addr[4];
+} MPI2_ETHERNET_IP_ADDR, *PTR_MPI2_ETHERNET_IP_ADDR,
+ Mpi2EthernetIpAddr_t, *pMpi2EthernetIpAddr_t;
+
+#define MPI2_ETHERNET_HOST_NAME_LENGTH (32)
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 NumInterfaces; /*0x08 */
+ U8 Reserved0; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Status; /*0x0C */
+ U8 MediaState; /*0x10 */
+ U8 Reserved2; /*0x11 */
+ U16 Reserved3; /*0x12 */
+ U8 MacAddress[6]; /*0x14 */
+ U8 Reserved4; /*0x1A */
+ U8 Reserved5; /*0x1B */
+ MPI2_ETHERNET_IP_ADDR IpAddress; /*0x1C */
+ MPI2_ETHERNET_IP_ADDR SubnetMask; /*0x2C */
+ MPI2_ETHERNET_IP_ADDR GatewayIpAddress;/*0x3C */
+ MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /*0x4C */
+ MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /*0x5C */
+ MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /*0x6C */
+ U8
+ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_0,
+ *PTR_MPI2_CONFIG_PAGE_ETHERNET_0,
+ Mpi2EthernetPage0_t, *pMpi2EthernetPage0_t;
+
+#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00)
+
+/*values for Ethernet Page 0 Status field */
+#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000)
+#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000)
+#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000)
+#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100)
+#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080)
+#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040)
+#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020)
+#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010)
+#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008)
+#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004)
+#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002)
+#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001)
+
+/*values for Ethernet Page 0 MediaState field */
+#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80)
+#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00)
+#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80)
+
+#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07)
+#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00)
+#define MPI2_ETHPG0_MS_10MBIT (0x01)
+#define MPI2_ETHPG0_MS_100MBIT (0x02)
+#define MPI2_ETHPG0_MS_1GBIT (0x03)
+
+
+/*Ethernet Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved0; /*0x08 */
+ U32
+ Flags; /*0x0C */
+ U8
+ MediaState; /*0x10 */
+ U8
+ Reserved1; /*0x11 */
+ U16
+ Reserved2; /*0x12 */
+ U8
+ MacAddress[6]; /*0x14 */
+ U8
+ Reserved3; /*0x1A */
+ U8
+ Reserved4; /*0x1B */
+ MPI2_ETHERNET_IP_ADDR
+ StaticIpAddress; /*0x1C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticSubnetMask; /*0x2C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticGatewayIpAddress; /*0x3C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticDNS1IpAddress; /*0x4C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticDNS2IpAddress; /*0x5C */
+ U32
+ Reserved5; /*0x6C */
+ U32
+ Reserved6; /*0x70 */
+ U32
+ Reserved7; /*0x74 */
+ U32
+ Reserved8; /*0x78 */
+ U8
+ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_1,
+ *PTR_MPI2_CONFIG_PAGE_ETHERNET_1,
+ Mpi2EthernetPage1_t, *pMpi2EthernetPage1_t;
+
+#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00)
+
+/*values for Ethernet Page 1 Flags field */
+#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100)
+#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080)
+#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040)
+#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020)
+#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004)
+#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002)
+#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001)
+
+/*values for Ethernet Page 1 MediaState field */
+#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80)
+#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00)
+#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80)
+
+#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07)
+#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00)
+#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01)
+#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02)
+#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03)
+
+
+/****************************************************************************
+* Extended Manufacturing Config Pages
+****************************************************************************/
+
+/*
+ *Generic structure to use for product-specific extended manufacturing pages
+ *(currently Extended Manufacturing Page 40 through Extended Manufacturing
+ *Page 60).
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ ProductSpecificInfo; /*0x08 */
+} MPI2_CONFIG_PAGE_EXT_MAN_PS,
+ *PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS,
+ Mpi2ExtManufacturingPagePS_t,
+ *pMpi2ExtManufacturingPagePS_t;
+
+/*PageVersion should be provided by product-specific code */
+
+
+
+/****************************************************************************
+* values for fields used by several types of PCIe Config Pages
+****************************************************************************/
+
+/*values for NegotiatedLinkRates fields */
+#define MPI26_PCIE_NEG_LINK_RATE_MASK_PHYSICAL (0x0F)
+/*link rates used for Negotiated Physical Link Rate */
+#define MPI26_PCIE_NEG_LINK_RATE_UNKNOWN (0x00)
+#define MPI26_PCIE_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI26_PCIE_NEG_LINK_RATE_2_5 (0x02)
+#define MPI26_PCIE_NEG_LINK_RATE_5_0 (0x03)
+#define MPI26_PCIE_NEG_LINK_RATE_8_0 (0x04)
+#define MPI26_PCIE_NEG_LINK_RATE_16_0 (0x05)
+#define MPI26_PCIE_NEG_LINK_RATE_32_0 (0x06)
+
+
+/****************************************************************************
+* PCIe IO Unit Config Pages (MPI v2.6 and later)
+****************************************************************************/
+
+/*PCIe IO Unit Page 0 */
+
+typedef struct _MPI26_PCIE_IO_UNIT0_PHY_DATA {
+ U8 Link; /*0x00 */
+ U8 LinkFlags; /*0x01 */
+ U8 PhyFlags; /*0x02 */
+ U8 NegotiatedLinkRate; /*0x03 */
+ U32 ControllerPhyDeviceInfo;/*0x04 */
+ U16 AttachedDevHandle; /*0x08 */
+ U16 ControllerDevHandle; /*0x0A */
+ U32 EnumerationStatus; /*0x0C */
+ U32 Reserved1; /*0x10 */
+} MPI26_PCIE_IO_UNIT0_PHY_DATA,
+ *PTR_MPI26_PCIE_IO_UNIT0_PHY_DATA,
+ Mpi26PCIeIOUnit0PhyData_t, *pMpi26PCIeIOUnit0PhyData_t;
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using PhyData[].
+ */
+
+typedef struct _MPI26_CONFIG_PAGE_PIOUNIT_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U8 NumPhys; /*0x0C */
+ U8 InitStatus; /*0x0D */
+ U16 Reserved3; /*0x0E */
+ MPI26_PCIE_IO_UNIT0_PHY_DATA
+ PhyData[]; /*0x10 */
+} MPI26_CONFIG_PAGE_PIOUNIT_0,
+ *PTR_MPI26_CONFIG_PAGE_PIOUNIT_0,
+ Mpi26PCIeIOUnitPage0_t, *pMpi26PCIeIOUnitPage0_t;
+
+#define MPI26_PCIEIOUNITPAGE0_PAGEVERSION (0x00)
+
+/*values for PCIe IO Unit Page 0 LinkFlags */
+#define MPI26_PCIEIOUNIT0_LINKFLAGS_ENUMERATION_IN_PROGRESS (0x08)
+
+/*values for PCIe IO Unit Page 0 PhyFlags */
+#define MPI26_PCIEIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+
+/*use MPI26_PCIE_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/*see mpi2_pci.h for values for PCIe IO Unit Page 0 ControllerPhyDeviceInfo
+ *values
+ */
+
+/*values for PCIe IO Unit Page 0 EnumerationStatus */
+#define MPI26_PCIEIOUNIT0_ES_MAX_SWITCHES_EXCEEDED (0x40000000)
+#define MPI26_PCIEIOUNIT0_ES_MAX_DEVICES_EXCEEDED (0x20000000)
+
+
+/*PCIe IO Unit Page 1 */
+
+typedef struct _MPI26_PCIE_IO_UNIT1_PHY_DATA {
+ U8 Link; /*0x00 */
+ U8 LinkFlags; /*0x01 */
+ U8 PhyFlags; /*0x02 */
+ U8 MaxMinLinkRate; /*0x03 */
+ U32 ControllerPhyDeviceInfo; /*0x04 */
+ U32 Reserved1; /*0x08 */
+} MPI26_PCIE_IO_UNIT1_PHY_DATA,
+ *PTR_MPI26_PCIE_IO_UNIT1_PHY_DATA,
+ Mpi26PCIeIOUnit1PhyData_t, *pMpi26PCIeIOUnit1PhyData_t;
+
+/*values for LinkFlags */
+#define MPI26_PCIEIOUNIT1_LINKFLAGS_DIS_SEPARATE_REFCLK (0x00)
+#define MPI26_PCIEIOUNIT1_LINKFLAGS_SRIS_EN (0x01)
+#define MPI26_PCIEIOUNIT1_LINKFLAGS_SRNS_EN (0x02)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumPhys at runtime before using PhyData[].
+ */
+
+typedef struct _MPI26_CONFIG_PAGE_PIOUNIT_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U16 ControlFlags; /*0x08 */
+ U16 Reserved; /*0x0A */
+ U16 AdditionalControlFlags; /*0x0C */
+ U16 NVMeMaxQueueDepth; /*0x0E */
+ U8 NumPhys; /*0x10 */
+ U8 DMDReportPCIe; /*0x11 */
+ U16 Reserved2; /*0x12 */
+ MPI26_PCIE_IO_UNIT1_PHY_DATA
+ PhyData[]; /*0x14 */
+} MPI26_CONFIG_PAGE_PIOUNIT_1,
+ *PTR_MPI26_CONFIG_PAGE_PIOUNIT_1,
+ Mpi26PCIeIOUnitPage1_t, *pMpi26PCIeIOUnitPage1_t;
+
+#define MPI26_PCIEIOUNITPAGE1_PAGEVERSION (0x00)
+
+/*values for PCIe IO Unit Page 1 PhyFlags */
+#define MPI26_PCIEIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+#define MPI26_PCIEIOUNIT1_PHYFLAGS_ENDPOINT_ONLY (0x01)
+
+/*values for PCIe IO Unit Page 1 MaxMinLinkRate */
+#define MPI26_PCIEIOUNIT1_MAX_RATE_MASK (0xF0)
+#define MPI26_PCIEIOUNIT1_MAX_RATE_SHIFT (4)
+#define MPI26_PCIEIOUNIT1_MAX_RATE_2_5 (0x20)
+#define MPI26_PCIEIOUNIT1_MAX_RATE_5_0 (0x30)
+#define MPI26_PCIEIOUNIT1_MAX_RATE_8_0 (0x40)
+#define MPI26_PCIEIOUNIT1_MAX_RATE_16_0 (0x50)
+#define MPI26_PCIEIOUNIT1_MAX_RATE_32_0 (0x60)
+
+/*values for PCIe IO Unit Page 1 DMDReportPCIe */
+#define MPI26_PCIEIOUNIT1_DMDRPT_UNIT_MASK (0x80)
+#define MPI26_PCIEIOUNIT1_DMDRPT_UNIT_1_SEC (0x00)
+#define MPI26_PCIEIOUNIT1_DMDRPT_UNIT_16_SEC (0x80)
+#define MPI26_PCIEIOUNIT1_DMDRPT_DELAY_TIME_MASK (0x7F)
+
+/*see mpi2_pci.h for values for PCIe IO Unit Page 0 ControllerPhyDeviceInfo
+ *values
+ */
+
+
+/****************************************************************************
+* PCIe Switch Config Pages (MPI v2.6 and later)
+****************************************************************************/
+
+/*PCIe Switch Page 0 */
+
+typedef struct _MPI26_CONFIG_PAGE_PSWITCH_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 PhysicalPort; /*0x08 */
+ U8 Reserved1; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 DevHandle; /*0x0C */
+ U16 ParentDevHandle; /*0x0E */
+ U8 NumPorts; /*0x10 */
+ U8 PCIeLevel; /*0x11 */
+ U16 Reserved3; /*0x12 */
+ U32 Reserved4; /*0x14 */
+ U32 Reserved5; /*0x18 */
+ U32 Reserved6; /*0x1C */
+} MPI26_CONFIG_PAGE_PSWITCH_0, *PTR_MPI26_CONFIG_PAGE_PSWITCH_0,
+ Mpi26PCIeSwitchPage0_t, *pMpi26PCIeSwitchPage0_t;
+
+#define MPI26_PCIESWITCH0_PAGEVERSION (0x00)
+
+
+/*PCIe Switch Page 1 */
+
+typedef struct _MPI26_CONFIG_PAGE_PSWITCH_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 PhysicalPort; /*0x08 */
+ U8 Reserved1; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U8 NumPorts; /*0x0C */
+ U8 PortNum; /*0x0D */
+ U16 AttachedDevHandle; /*0x0E */
+ U16 SwitchDevHandle; /*0x10 */
+ U8 NegotiatedPortWidth; /*0x12 */
+ U8 NegotiatedLinkRate; /*0x13 */
+ U32 Reserved4; /*0x14 */
+ U32 Reserved5; /*0x18 */
+} MPI26_CONFIG_PAGE_PSWITCH_1, *PTR_MPI26_CONFIG_PAGE_PSWITCH_1,
+ Mpi26PCIeSwitchPage1_t, *pMpi26PCIeSwitchPage1_t;
+
+#define MPI26_PCIESWITCH1_PAGEVERSION (0x00)
+
+/*use MPI26_PCIE_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/* defines for the Flags field */
+#define MPI26_PCIESWITCH1_2_RETIMER_PRESENCE (0x0002)
+#define MPI26_PCIESWITCH1_RETIMER_PRESENCE (0x0001)
+
+/****************************************************************************
+* PCIe Device Config Pages (MPI v2.6 and later)
+****************************************************************************/
+
+/*PCIe Device Page 0 */
+
+typedef struct _MPI26_CONFIG_PAGE_PCIEDEV_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U16 Slot; /*0x08 */
+ U16 EnclosureHandle; /*0x0A */
+ U64 WWID; /*0x0C */
+ U16 ParentDevHandle; /*0x14 */
+ U8 PortNum; /*0x16 */
+ U8 AccessStatus; /*0x17 */
+ U16 DevHandle; /*0x18 */
+ U8 PhysicalPort; /*0x1A */
+ U8 Reserved1; /*0x1B */
+ U32 DeviceInfo; /*0x1C */
+ U32 Flags; /*0x20 */
+ U8 SupportedLinkRates; /*0x24 */
+ U8 MaxPortWidth; /*0x25 */
+ U8 NegotiatedPortWidth; /*0x26 */
+ U8 NegotiatedLinkRate; /*0x27 */
+ U8 EnclosureLevel; /*0x28 */
+ U8 Reserved2; /*0x29 */
+ U16 Reserved3; /*0x2A */
+ U8 ConnectorName[4]; /*0x2C */
+ U32 Reserved4; /*0x30 */
+ U32 Reserved5; /*0x34 */
+} MPI26_CONFIG_PAGE_PCIEDEV_0, *PTR_MPI26_CONFIG_PAGE_PCIEDEV_0,
+ Mpi26PCIeDevicePage0_t, *pMpi26PCIeDevicePage0_t;
+
+#define MPI26_PCIEDEVICE0_PAGEVERSION (0x01)
+
+/*values for PCIe Device Page 0 AccessStatus field */
+#define MPI26_PCIEDEV0_ASTATUS_NO_ERRORS (0x00)
+#define MPI26_PCIEDEV0_ASTATUS_NEEDS_INITIALIZATION (0x04)
+#define MPI26_PCIEDEV0_ASTATUS_CAPABILITY_FAILED (0x02)
+#define MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED (0x07)
+#define MPI26_PCIEDEV0_ASTATUS_MEMORY_SPACE_ACCESS_FAILED (0x08)
+#define MPI26_PCIEDEV0_ASTATUS_UNSUPPORTED_DEVICE (0x09)
+#define MPI26_PCIEDEV0_ASTATUS_MSIX_REQUIRED (0x0A)
+#define MPI26_PCIEDEV0_ASTATUS_UNKNOWN (0x10)
+
+#define MPI26_PCIEDEV0_ASTATUS_NVME_READY_TIMEOUT (0x30)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_DEVCFG_UNSUPPORTED (0x31)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_IDENTIFY_FAILED (0x32)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_QCONFIG_FAILED (0x33)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_QCREATION_FAILED (0x34)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_EVENTCFG_FAILED (0x35)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_GET_FEATURE_STAT_FAILED (0x36)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_IDLE_TIMEOUT (0x37)
+#define MPI26_PCIEDEV0_ASTATUS_NVME_FAILURE_STATUS (0x38)
+
+#define MPI26_PCIEDEV0_ASTATUS_INIT_FAIL_MAX (0x3F)
+
+/*see mpi2_pci.h for the MPI26_PCIE_DEVINFO_ defines used for the DeviceInfo
+ *field
+ */
+
+/*values for PCIe Device Page 0 Flags field*/
+#define MPI26_PCIEDEV0_FLAGS_2_RETIMER_PRESENCE (0x00020000)
+#define MPI26_PCIEDEV0_FLAGS_RETIMER_PRESENCE (0x00010000)
+#define MPI26_PCIEDEV0_FLAGS_UNAUTHORIZED_DEVICE (0x00008000)
+#define MPI26_PCIEDEV0_FLAGS_ENABLED_FAST_PATH (0x00004000)
+#define MPI26_PCIEDEV0_FLAGS_FAST_PATH_CAPABLE (0x00002000)
+#define MPI26_PCIEDEV0_FLAGS_ASYNCHRONOUS_NOTIFICATION (0x00000400)
+#define MPI26_PCIEDEV0_FLAGS_ATA_SW_PRESERVATION (0x00000200)
+#define MPI26_PCIEDEV0_FLAGS_UNSUPPORTED_DEVICE (0x00000100)
+#define MPI26_PCIEDEV0_FLAGS_ATA_48BIT_LBA_SUPPORTED (0x00000080)
+#define MPI26_PCIEDEV0_FLAGS_ATA_SMART_SUPPORTED (0x00000040)
+#define MPI26_PCIEDEV0_FLAGS_ATA_NCQ_SUPPORTED (0x00000020)
+#define MPI26_PCIEDEV0_FLAGS_ATA_FUA_SUPPORTED (0x00000010)
+#define MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID (0x00000002)
+#define MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT (0x00000001)
+
+/* values for PCIe Device Page 0 SupportedLinkRates field */
+#define MPI26_PCIEDEV0_LINK_RATE_16_0_SUPPORTED (0x08)
+#define MPI26_PCIEDEV0_LINK_RATE_8_0_SUPPORTED (0x04)
+#define MPI26_PCIEDEV0_LINK_RATE_5_0_SUPPORTED (0x02)
+#define MPI26_PCIEDEV0_LINK_RATE_2_5_SUPPORTED (0x01)
+
+/*use MPI26_PCIE_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+
+/*PCIe Device Page 2 */
+
+typedef struct _MPI26_CONFIG_PAGE_PCIEDEV_2 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U16 DevHandle; /*0x08 */
+ U8 ControllerResetTO; /* 0x0A */
+ U8 Reserved1; /* 0x0B */
+ U32 MaximumDataTransferSize; /*0x0C */
+ U32 Capabilities; /*0x10 */
+ U16 NOIOB; /* 0x14 */
+ U16 ShutdownLatency; /* 0x16 */
+ U16 VendorID; /* 0x18 */
+ U16 DeviceID; /* 0x1A */
+ U16 SubsystemVendorID; /* 0x1C */
+ U16 SubsystemID; /* 0x1E */
+ U8 RevisionID; /* 0x20 */
+ U8 Reserved21[3]; /* 0x21 */
+} MPI26_CONFIG_PAGE_PCIEDEV_2, *PTR_MPI26_CONFIG_PAGE_PCIEDEV_2,
+ Mpi26PCIeDevicePage2_t, *pMpi26PCIeDevicePage2_t;
+
+#define MPI26_PCIEDEVICE2_PAGEVERSION (0x01)
+
+/*defines for PCIe Device Page 2 Capabilities field */
+#define MPI26_PCIEDEV2_CAP_DATA_BLK_ALIGN_AND_GRAN (0x00000008)
+#define MPI26_PCIEDEV2_CAP_SGL_FORMAT (0x00000004)
+#define MPI26_PCIEDEV2_CAP_BIT_BUCKET_SUPPORT (0x00000002)
+#define MPI26_PCIEDEV2_CAP_SGL_SUPPORT (0x00000001)
+
+/* Defines for the NOIOB field */
+#define MPI26_PCIEDEV2_NOIOB_UNSUPPORTED (0x0000)
+
+/****************************************************************************
+* PCIe Link Config Pages (MPI v2.6 and later)
+****************************************************************************/
+
+/*PCIe Link Page 1 */
+
+typedef struct _MPI26_CONFIG_PAGE_PCIELINK_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 Link; /*0x08 */
+ U8 Reserved1; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 CorrectableErrorCount; /*0x0C */
+ U16 NonFatalErrorCount; /*0x10 */
+ U16 Reserved3; /*0x12 */
+ U16 FatalErrorCount; /*0x14 */
+ U16 Reserved4; /*0x16 */
+} MPI26_CONFIG_PAGE_PCIELINK_1, *PTR_MPI26_CONFIG_PAGE_PCIELINK_1,
+ Mpi26PcieLinkPage1_t, *pMpi26PcieLinkPage1_t;
+
+#define MPI26_PCIELINK1_PAGEVERSION (0x00)
+
+/*PCIe Link Page 2 */
+
+typedef struct _MPI26_PCIELINK2_LINK_EVENT {
+ U8 LinkEventCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 LinkEventInfo; /*0x04 */
+} MPI26_PCIELINK2_LINK_EVENT, *PTR_MPI26_PCIELINK2_LINK_EVENT,
+ Mpi26PcieLink2LinkEvent_t, *pMpi26PcieLink2LinkEvent_t;
+
+/*use MPI26_PCIELINK3_EVTCODE_ for the LinkEventCode field */
+
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumLinkEvents at runtime before using LinkEvent[].
+ */
+
+typedef struct _MPI26_CONFIG_PAGE_PCIELINK_2 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 Link; /*0x08 */
+ U8 Reserved1; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U8 NumLinkEvents; /*0x0C */
+ U8 Reserved3; /*0x0D */
+ U16 Reserved4; /*0x0E */
+ MPI26_PCIELINK2_LINK_EVENT
+ LinkEvent[]; /*0x10 */
+} MPI26_CONFIG_PAGE_PCIELINK_2, *PTR_MPI26_CONFIG_PAGE_PCIELINK_2,
+ Mpi26PcieLinkPage2_t, *pMpi26PcieLinkPage2_t;
+
+#define MPI26_PCIELINK2_PAGEVERSION (0x00)
+
+/*PCIe Link Page 3 */
+
+typedef struct _MPI26_PCIELINK3_LINK_EVENT_CONFIG {
+ U8 LinkEventCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U8 CounterType; /*0x04 */
+ U8 ThresholdWindow; /*0x05 */
+ U8 TimeUnits; /*0x06 */
+ U8 Reserved3; /*0x07 */
+ U32 EventThreshold; /*0x08 */
+ U16 ThresholdFlags; /*0x0C */
+ U16 Reserved4; /*0x0E */
+} MPI26_PCIELINK3_LINK_EVENT_CONFIG, *PTR_MPI26_PCIELINK3_LINK_EVENT_CONFIG,
+ Mpi26PcieLink3LinkEventConfig_t, *pMpi26PcieLink3LinkEventConfig_t;
+
+/*values for LinkEventCode field */
+#define MPI26_PCIELINK3_EVTCODE_NO_EVENT (0x00)
+#define MPI26_PCIELINK3_EVTCODE_CORRECTABLE_ERROR_RECEIVED (0x01)
+#define MPI26_PCIELINK3_EVTCODE_NON_FATAL_ERROR_RECEIVED (0x02)
+#define MPI26_PCIELINK3_EVTCODE_FATAL_ERROR_RECEIVED (0x03)
+#define MPI26_PCIELINK3_EVTCODE_DATA_LINK_ERROR_DETECTED (0x04)
+#define MPI26_PCIELINK3_EVTCODE_TRANSACTION_LAYER_ERROR_DETECTED (0x05)
+#define MPI26_PCIELINK3_EVTCODE_TLP_ECRC_ERROR_DETECTED (0x06)
+#define MPI26_PCIELINK3_EVTCODE_POISONED_TLP (0x07)
+#define MPI26_PCIELINK3_EVTCODE_RECEIVED_NAK_DLLP (0x08)
+#define MPI26_PCIELINK3_EVTCODE_SENT_NAK_DLLP (0x09)
+#define MPI26_PCIELINK3_EVTCODE_LTSSM_RECOVERY_STATE (0x0A)
+#define MPI26_PCIELINK3_EVTCODE_LTSSM_RXL0S_STATE (0x0B)
+#define MPI26_PCIELINK3_EVTCODE_LTSSM_TXL0S_STATE (0x0C)
+#define MPI26_PCIELINK3_EVTCODE_LTSSM_L1_STATE (0x0D)
+#define MPI26_PCIELINK3_EVTCODE_LTSSM_DISABLED_STATE (0x0E)
+#define MPI26_PCIELINK3_EVTCODE_LTSSM_HOT_RESET_STATE (0x0F)
+#define MPI26_PCIELINK3_EVTCODE_SYSTEM_ERROR (0x10)
+#define MPI26_PCIELINK3_EVTCODE_DECODE_ERROR (0x11)
+#define MPI26_PCIELINK3_EVTCODE_DISPARITY_ERROR (0x12)
+
+/*values for the CounterType field */
+#define MPI26_PCIELINK3_COUNTER_TYPE_WRAPPING (0x00)
+#define MPI26_PCIELINK3_COUNTER_TYPE_SATURATING (0x01)
+#define MPI26_PCIELINK3_COUNTER_TYPE_PEAK_VALUE (0x02)
+
+/*values for the TimeUnits field */
+#define MPI26_PCIELINK3_TM_UNITS_10_MICROSECONDS (0x00)
+#define MPI26_PCIELINK3_TM_UNITS_100_MICROSECONDS (0x01)
+#define MPI26_PCIELINK3_TM_UNITS_1_MILLISECOND (0x02)
+#define MPI26_PCIELINK3_TM_UNITS_10_MILLISECONDS (0x03)
+
+/*values for the ThresholdFlags field */
+#define MPI26_PCIELINK3_TFLAGS_EVENT_NOTIFY (0x0001)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check the value returned
+ *for NumLinkEvents at runtime before using LinkEventConfig[].
+ */
+
+typedef struct _MPI26_CONFIG_PAGE_PCIELINK_3 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 Link; /*0x08 */
+ U8 Reserved1; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U8 NumLinkEvents; /*0x0C */
+ U8 Reserved3; /*0x0D */
+ U16 Reserved4; /*0x0E */
+ MPI26_PCIELINK3_LINK_EVENT_CONFIG
+ LinkEventConfig[]; /*0x10 */
+} MPI26_CONFIG_PAGE_PCIELINK_3, *PTR_MPI26_CONFIG_PAGE_PCIELINK_3,
+ Mpi26PcieLinkPage3_t, *pMpi26PcieLinkPage3_t;
+
+#define MPI26_PCIELINK3_PAGEVERSION (0x00)
+
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_image.h b/drivers/scsi/mpt3sas/mpi/mpi2_image.h
new file mode 100644
index 000000000000..798ab6e33eb9
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_image.h
@@ -0,0 +1,502 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2016-2020 Broadcom Limited. All rights reserved.
+ *
+ * Name: mpi2_image.h
+ * Description: Contains definitions for firmware and other component images
+ * Creation Date: 04/02/2018
+ * Version: 02.06.04
+ *
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 08-01-18 02.06.00 Initial version for MPI 2.6.5.
+ * 08-14-18 02.06.01 Corrected define for MPI26_IMAGE_HEADER_SIGNATURE0_MPI26
+ * 08-28-18 02.06.02 Added MPI2_EXT_IMAGE_TYPE_RDE
+ * 09-07-18 02.06.03 Added MPI26_EVENT_PCIE_TOPO_PI_16_LANES
+ * 12-17-18 02.06.04 Addd MPI2_EXT_IMAGE_TYPE_PBLP
+ * Shorten some defines to be compatible with DOS
+ * 06-24-19 02.06.05 Whitespace adjustments to help with identifier
+ * checking tool.
+ * 10-02-19 02.06.06 Added MPI26_IMAGE_HEADER_SIG1_COREDUMP
+ * Added MPI2_FLASH_REGION_COREDUMP
+ */
+#ifndef MPI2_IMAGE_H
+#define MPI2_IMAGE_H
+
+
+/*FW Image Header */
+typedef struct _MPI2_FW_IMAGE_HEADER {
+ U32 Signature; /*0x00 */
+ U32 Signature0; /*0x04 */
+ U32 Signature1; /*0x08 */
+ U32 Signature2; /*0x0C */
+ MPI2_VERSION_UNION MPIVersion; /*0x10 */
+ MPI2_VERSION_UNION FWVersion; /*0x14 */
+ MPI2_VERSION_UNION NVDATAVersion; /*0x18 */
+ MPI2_VERSION_UNION PackageVersion; /*0x1C */
+ U16 VendorID; /*0x20 */
+ U16 ProductID; /*0x22 */
+ U16 ProtocolFlags; /*0x24 */
+ U16 Reserved26; /*0x26 */
+ U32 IOCCapabilities; /*0x28 */
+ U32 ImageSize; /*0x2C */
+ U32 NextImageHeaderOffset; /*0x30 */
+ U32 Checksum; /*0x34 */
+ U32 Reserved38; /*0x38 */
+ U32 Reserved3C; /*0x3C */
+ U32 Reserved40; /*0x40 */
+ U32 Reserved44; /*0x44 */
+ U32 Reserved48; /*0x48 */
+ U32 Reserved4C; /*0x4C */
+ U32 Reserved50; /*0x50 */
+ U32 Reserved54; /*0x54 */
+ U32 Reserved58; /*0x58 */
+ U32 Reserved5C; /*0x5C */
+ U32 BootFlags; /*0x60 */
+ U32 FirmwareVersionNameWhat; /*0x64 */
+ U8 FirmwareVersionName[32]; /*0x68 */
+ U32 VendorNameWhat; /*0x88 */
+ U8 VendorName[32]; /*0x8C */
+ U32 PackageNameWhat; /*0x88 */
+ U8 PackageName[32]; /*0x8C */
+ U32 ReservedD0; /*0xD0 */
+ U32 ReservedD4; /*0xD4 */
+ U32 ReservedD8; /*0xD8 */
+ U32 ReservedDC; /*0xDC */
+ U32 ReservedE0; /*0xE0 */
+ U32 ReservedE4; /*0xE4 */
+ U32 ReservedE8; /*0xE8 */
+ U32 ReservedEC; /*0xEC */
+ U32 ReservedF0; /*0xF0 */
+ U32 ReservedF4; /*0xF4 */
+ U32 ReservedF8; /*0xF8 */
+ U32 ReservedFC; /*0xFC */
+} MPI2_FW_IMAGE_HEADER, *PTR_MPI2_FW_IMAGE_HEADER,
+ Mpi2FWImageHeader_t, *pMpi2FWImageHeader_t;
+
+/*Signature field */
+#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00)
+#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000)
+#define MPI2_FW_HEADER_SIGNATURE (0xEA000000)
+#define MPI26_FW_HEADER_SIGNATURE (0xEB000000)
+
+/*Signature0 field */
+#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04)
+#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A)
+/*Last byte is defined by architecture */
+#define MPI26_FW_HEADER_SIGNATURE0_BASE (0x5AEAA500)
+#define MPI26_FW_HEADER_SIGNATURE0_ARC_0 (0x5A)
+#define MPI26_FW_HEADER_SIGNATURE0_ARC_1 (0x00)
+#define MPI26_FW_HEADER_SIGNATURE0_ARC_2 (0x01)
+/*legacy (0x5AEAA55A) */
+#define MPI26_FW_HEADER_SIGNATURE0_ARC_3 (0x02)
+#define MPI26_FW_HEADER_SIGNATURE0 \
+ (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_0)
+#define MPI26_FW_HEADER_SIGNATURE0_3516 \
+ (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_1)
+#define MPI26_FW_HEADER_SIGNATURE0_4008 \
+ (MPI26_FW_HEADER_SIGNATURE0_BASE+MPI26_FW_HEADER_SIGNATURE0_ARC_3)
+
+/*Signature1 field */
+#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08)
+#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5)
+#define MPI26_FW_HEADER_SIGNATURE1 (0xA55AEAA5)
+
+/*Signature2 field */
+#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C)
+#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA)
+#define MPI26_FW_HEADER_SIGNATURE2 (0x5AA55AEA)
+
+/*defines for using the ProductID field */
+#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000)
+#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000)
+
+#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
+#define MPI2_FW_HEADER_PID_PROD_A (0x0000)
+#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200)
+#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700)
+
+#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF)
+/*SAS ProductID Family bits */
+#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013)
+#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014)
+#define MPI25_FW_HEADER_PID_FAMILY_3108_SAS (0x0021)
+#define MPI26_FW_HEADER_PID_FAMILY_3324_SAS (0x0028)
+#define MPI26_FW_HEADER_PID_FAMILY_3516_SAS (0x0031)
+
+/*use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
+
+/*use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */
+
+#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C)
+#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30)
+
+#define MPI26_FW_HEADER_BOOTFLAGS_OFFSET (0x60)
+#define MPI2_FW_HEADER_BOOTFLAGS_ISSI32M_FLAG (0x00000001)
+#define MPI2_FW_HEADER_BOOTFLAGS_W25Q256JW_FLAG (0x00000002)
+/*This image has a auto-discovery version of SPI */
+#define MPI2_FW_HEADER_BOOTFLAGS_AUTO_SPI_FLAG (0x00000004)
+
+
+#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64)
+
+#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840)
+
+#define MPI2_FW_HEADER_SIZE (0x100)
+
+
+/****************************************************************************
+ * Component Image Format and related defines *
+ ****************************************************************************/
+
+/*Maximum number of Hash Exclusion entries in a Component Image Header */
+#define MPI26_COMP_IMG_HDR_NUM_HASH_EXCL (4)
+
+/*Hash Exclusion Format */
+typedef struct _MPI26_HASH_EXCLUSION_FORMAT {
+ U32 Offset; /*0x00 */
+ U32 Size; /*0x04 */
+} MPI26_HASH_EXCLUSION_FORMAT,
+ *PTR_MPI26_HASH_EXCLUSION_FORMAT,
+ Mpi26HashSxclusionFormat_t,
+ *pMpi26HashExclusionFormat_t;
+
+/*FW Image Header */
+typedef struct _MPI26_COMPONENT_IMAGE_HEADER {
+ U32 Signature0; /*0x00 */
+ U32 LoadAddress; /*0x04 */
+ U32 DataSize; /*0x08 */
+ U32 StartAddress; /*0x0C */
+ U32 Signature1; /*0x10 */
+ U32 FlashOffset; /*0x14 */
+ U32 FlashSize; /*0x18 */
+ U32 VersionStringOffset; /*0x1C */
+ U32 BuildDateStringOffset; /*0x20 */
+ U32 BuildTimeStringOffset; /*0x24 */
+ U32 EnvironmentVariableOffset; /*0x28 */
+ U32 ApplicationSpecific; /*0x2C */
+ U32 Signature2; /*0x30 */
+ U32 HeaderSize; /*0x34 */
+ U32 Crc; /*0x38 */
+ U8 NotFlashImage; /*0x3C */
+ U8 Compressed; /*0x3D */
+ U16 Reserved3E; /*0x3E */
+ U32 SecondaryFlashOffset; /*0x40 */
+ U32 Reserved44; /*0x44 */
+ U32 Reserved48; /*0x48 */
+ MPI2_VERSION_UNION RMCInterfaceVersion; /*0x4C */
+ MPI2_VERSION_UNION Reserved50; /*0x50 */
+ MPI2_VERSION_UNION FWVersion; /*0x54 */
+ MPI2_VERSION_UNION NvdataVersion; /*0x58 */
+ MPI26_HASH_EXCLUSION_FORMAT
+ HashExclusion[MPI26_COMP_IMG_HDR_NUM_HASH_EXCL];/*0x5C */
+ U32 NextImageHeaderOffset; /*0x7C */
+ U32 Reserved80[32]; /*0x80 -- 0xFC */
+} MPI26_COMPONENT_IMAGE_HEADER,
+ *PTR_MPI26_COMPONENT_IMAGE_HEADER,
+ Mpi26ComponentImageHeader_t,
+ *pMpi26ComponentImageHeader_t;
+
+
+/**** Definitions for Signature0 field ****/
+#define MPI26_IMAGE_HEADER_SIGNATURE0_MPI26 (0xEB000042)
+
+/**** Definitions for Signature1 field ****/
+#define MPI26_IMAGE_HEADER_SIG1_APPLICATION (0x20505041)
+#define MPI26_IMAGE_HEADER_SIG1_CBB (0x20424243)
+#define MPI26_IMAGE_HEADER_SIG1_MFG (0x2047464D)
+#define MPI26_IMAGE_HEADER_SIG1_BIOS (0x534F4942)
+#define MPI26_IMAGE_HEADER_SIG1_HIIM (0x4D494948)
+#define MPI26_IMAGE_HEADER_SIG1_HIIA (0x41494948)
+#define MPI26_IMAGE_HEADER_SIG1_CPLD (0x444C5043)
+#define MPI26_IMAGE_HEADER_SIG1_SPD (0x20445053)
+#define MPI26_IMAGE_HEADER_SIG1_NVDATA (0x5444564E)
+#define MPI26_IMAGE_HEADER_SIG1_GAS_GAUGE (0x20534147)
+#define MPI26_IMAGE_HEADER_SIG1_PBLP (0x504C4250)
+/* little-endian "DUMP" */
+#define MPI26_IMAGE_HEADER_SIG1_COREDUMP (0x504D5544)
+
+/**** Definitions for Signature2 field ****/
+#define MPI26_IMAGE_HEADER_SIGNATURE2_VALUE (0x50584546)
+
+/**** Offsets for Image Header Fields ****/
+#define MPI26_IMAGE_HEADER_SIGNATURE0_OFFSET (0x00)
+#define MPI26_IMAGE_HEADER_LOAD_ADDRESS_OFFSET (0x04)
+#define MPI26_IMAGE_HEADER_DATA_SIZE_OFFSET (0x08)
+#define MPI26_IMAGE_HEADER_START_ADDRESS_OFFSET (0x0C)
+#define MPI26_IMAGE_HEADER_SIGNATURE1_OFFSET (0x10)
+#define MPI26_IMAGE_HEADER_FLASH_OFFSET_OFFSET (0x14)
+#define MPI26_IMAGE_HEADER_FLASH_SIZE_OFFSET (0x18)
+#define MPI26_IMAGE_HEADER_VERSION_STRING_OFFSET_OFFSET (0x1C)
+#define MPI26_IMAGE_HEADER_BUILD_DATE_STRING_OFFSET_OFFSET (0x20)
+#define MPI26_IMAGE_HEADER_BUILD_TIME_OFFSET_OFFSET (0x24)
+#define MPI26_IMAGE_HEADER_ENVIROMENT_VAR_OFFSET_OFFSET (0x28)
+#define MPI26_IMAGE_HEADER_APPLICATION_SPECIFIC_OFFSET (0x2C)
+#define MPI26_IMAGE_HEADER_SIGNATURE2_OFFSET (0x30)
+#define MPI26_IMAGE_HEADER_HEADER_SIZE_OFFSET (0x34)
+#define MPI26_IMAGE_HEADER_CRC_OFFSET (0x38)
+#define MPI26_IMAGE_HEADER_NOT_FLASH_IMAGE_OFFSET (0x3C)
+#define MPI26_IMAGE_HEADER_COMPRESSED_OFFSET (0x3D)
+#define MPI26_IMAGE_HEADER_SECONDARY_FLASH_OFFSET_OFFSET (0x40)
+#define MPI26_IMAGE_HEADER_RMC_INTERFACE_VER_OFFSET (0x4C)
+#define MPI26_IMAGE_HEADER_COMPONENT_IMAGE_VER_OFFSET (0x54)
+#define MPI26_IMAGE_HEADER_HASH_EXCLUSION_OFFSET (0x5C)
+#define MPI26_IMAGE_HEADER_NEXT_IMAGE_HEADER_OFFSET_OFFSET (0x7C)
+
+
+#define MPI26_IMAGE_HEADER_SIZE (0x100)
+
+
+/*Extended Image Header */
+typedef struct _MPI2_EXT_IMAGE_HEADER {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Checksum; /*0x04 */
+ U32 ImageSize; /*0x08 */
+ U32 NextImageHeaderOffset; /*0x0C */
+ U32 PackageVersion; /*0x10 */
+ U32 Reserved3; /*0x14 */
+ U32 Reserved4; /*0x18 */
+ U32 Reserved5; /*0x1C */
+ U8 IdentifyString[32]; /*0x20 */
+} MPI2_EXT_IMAGE_HEADER, *PTR_MPI2_EXT_IMAGE_HEADER,
+ Mpi2ExtImageHeader_t, *pMpi2ExtImageHeader_t;
+
+/*useful offsets */
+#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00)
+#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08)
+#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C)
+#define MPI2_EXT_IMAGE_PACKAGEVERSION_OFFSET (0x10)
+
+#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40)
+
+/*defines for the ImageType field */
+#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00)
+#define MPI2_EXT_IMAGE_TYPE_FW (0x01)
+#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03)
+#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04)
+#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05)
+#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06)
+#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07)
+#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08)
+#define MPI2_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09)
+#define MPI2_EXT_IMAGE_TYPE_RDE (0x0A)
+#define MPI2_EXT_IMAGE_TYPE_PBLP (0x0B)
+#define MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xFF)
+
+#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC)
+
+/*FLASH Layout Extended Image Data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check NumberOfLayouts and
+ *RegionsPerLayout at runtime before using Layout[] and Region[].
+ */
+
+typedef struct _MPI2_FLASH_REGION {
+ U8 RegionType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 RegionOffset; /*0x04 */
+ U32 RegionSize; /*0x08 */
+ U32 Reserved3; /*0x0C */
+} MPI2_FLASH_REGION, *PTR_MPI2_FLASH_REGION,
+ Mpi2FlashRegion_t, *pMpi2FlashRegion_t;
+
+typedef struct _MPI2_FLASH_LAYOUT {
+ U32 FlashSize; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U32 Reserved3; /*0x0C */
+ MPI2_FLASH_REGION Region[]; /*0x10 */
+} MPI2_FLASH_LAYOUT, *PTR_MPI2_FLASH_LAYOUT,
+ Mpi2FlashLayout_t, *pMpi2FlashLayout_t;
+
+typedef struct _MPI2_FLASH_LAYOUT_DATA {
+ U8 ImageRevision; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 SizeOfRegion; /*0x02 */
+ U8 Reserved2; /*0x03 */
+ U16 NumberOfLayouts; /*0x04 */
+ U16 RegionsPerLayout; /*0x06 */
+ U16 MinimumSectorAlignment; /*0x08 */
+ U16 Reserved3; /*0x0A */
+ U32 Reserved4; /*0x0C */
+ MPI2_FLASH_LAYOUT Layout[]; /*0x10 */
+} MPI2_FLASH_LAYOUT_DATA, *PTR_MPI2_FLASH_LAYOUT_DATA,
+ Mpi2FlashLayoutData_t, *pMpi2FlashLayoutData_t;
+
+/*defines for the RegionType field */
+#define MPI2_FLASH_REGION_UNUSED (0x00)
+#define MPI2_FLASH_REGION_FIRMWARE (0x01)
+#define MPI2_FLASH_REGION_BIOS (0x02)
+#define MPI2_FLASH_REGION_NVDATA (0x03)
+#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05)
+#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06)
+#define MPI2_FLASH_REGION_CONFIG_1 (0x07)
+#define MPI2_FLASH_REGION_CONFIG_2 (0x08)
+#define MPI2_FLASH_REGION_MEGARAID (0x09)
+#define MPI2_FLASH_REGION_COMMON_BOOT_BLOCK (0x0A)
+#define MPI2_FLASH_REGION_INIT (MPI2_FLASH_REGION_COMMON_BOOT_BLOCK)
+#define MPI2_FLASH_REGION_CBB_BACKUP (0x0D)
+#define MPI2_FLASH_REGION_SBR (0x0E)
+#define MPI2_FLASH_REGION_SBR_BACKUP (0x0F)
+#define MPI2_FLASH_REGION_HIIM (0x10)
+#define MPI2_FLASH_REGION_HIIA (0x11)
+#define MPI2_FLASH_REGION_CTLR (0x12)
+#define MPI2_FLASH_REGION_IMR_FIRMWARE (0x13)
+#define MPI2_FLASH_REGION_MR_NVDATA (0x14)
+#define MPI2_FLASH_REGION_CPLD (0x15)
+#define MPI2_FLASH_REGION_PSOC (0x16)
+#define MPI2_FLASH_REGION_COREDUMP (0x17)
+
+/*ImageRevision */
+#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00)
+
+/*Supported Devices Extended Image Data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check NumberOfDevices at
+ *runtime before using SupportedDevice[].
+ */
+
+typedef struct _MPI2_SUPPORTED_DEVICE {
+ U16 DeviceID; /*0x00 */
+ U16 VendorID; /*0x02 */
+ U16 DeviceIDMask; /*0x04 */
+ U16 Reserved1; /*0x06 */
+ U8 LowPCIRev; /*0x08 */
+ U8 HighPCIRev; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 Reserved3; /*0x0C */
+} MPI2_SUPPORTED_DEVICE, *PTR_MPI2_SUPPORTED_DEVICE,
+ Mpi2SupportedDevice_t, *pMpi2SupportedDevice_t;
+
+typedef struct _MPI2_SUPPORTED_DEVICES_DATA {
+ U8 ImageRevision; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 NumberOfDevices; /*0x02 */
+ U8 Reserved2; /*0x03 */
+ U32 Reserved3; /*0x04 */
+ MPI2_SUPPORTED_DEVICE
+ SupportedDevice[]; /*0x08 */
+} MPI2_SUPPORTED_DEVICES_DATA, *PTR_MPI2_SUPPORTED_DEVICES_DATA,
+ Mpi2SupportedDevicesData_t, *pMpi2SupportedDevicesData_t;
+
+/*ImageRevision */
+#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00)
+
+/*Init Extended Image Data */
+
+typedef struct _MPI2_INIT_IMAGE_FOOTER {
+ U32 BootFlags; /*0x00 */
+ U32 ImageSize; /*0x04 */
+ U32 Signature0; /*0x08 */
+ U32 Signature1; /*0x0C */
+ U32 Signature2; /*0x10 */
+ U32 ResetVector; /*0x14 */
+} MPI2_INIT_IMAGE_FOOTER, *PTR_MPI2_INIT_IMAGE_FOOTER,
+ Mpi2InitImageFooter_t, *pMpi2InitImageFooter_t;
+
+/*defines for the BootFlags field */
+#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00)
+
+/*defines for the ImageSize field */
+#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04)
+
+/*defines for the Signature0 field */
+#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08)
+#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA)
+
+/*defines for the Signature1 field */
+#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C)
+#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5)
+
+/*defines for the Signature2 field */
+#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10)
+#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A)
+
+/*Signature fields as individual bytes */
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A)
+
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5)
+
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A)
+
+/*defines for the ResetVector field */
+#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14)
+
+
+/* Encrypted Hash Extended Image Data */
+
+typedef struct _MPI25_ENCRYPTED_HASH_ENTRY {
+ U8 HashImageType; /*0x00 */
+ U8 HashAlgorithm; /*0x01 */
+ U8 EncryptionAlgorithm; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 Reserved2; /*0x04 */
+ U32 EncryptedHash[]; /*0x08 */
+} MPI25_ENCRYPTED_HASH_ENTRY, *PTR_MPI25_ENCRYPTED_HASH_ENTRY,
+Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t;
+
+/* values for HashImageType */
+#define MPI25_HASH_IMAGE_TYPE_UNUSED (0x00)
+#define MPI25_HASH_IMAGE_TYPE_FIRMWARE (0x01)
+#define MPI25_HASH_IMAGE_TYPE_BIOS (0x02)
+
+#define MPI26_HASH_IMAGE_TYPE_UNUSED (0x00)
+#define MPI26_HASH_IMAGE_TYPE_FIRMWARE (0x01)
+#define MPI26_HASH_IMAGE_TYPE_BIOS (0x02)
+#define MPI26_HASH_IMAGE_TYPE_KEY_HASH (0x03)
+
+/* values for HashAlgorithm */
+#define MPI25_HASH_ALGORITHM_UNUSED (0x00)
+#define MPI25_HASH_ALGORITHM_SHA256 (0x01)
+
+#define MPI26_HASH_ALGORITHM_VER_MASK (0xE0)
+#define MPI26_HASH_ALGORITHM_VER_NONE (0x00)
+#define MPI26_HASH_ALGORITHM_VER_SHA1 (0x20)
+#define MPI26_HASH_ALGORITHM_VER_SHA2 (0x40)
+#define MPI26_HASH_ALGORITHM_VER_SHA3 (0x60)
+#define MPI26_HASH_ALGORITHM_SIZE_MASK (0x1F)
+#define MPI26_HASH_ALGORITHM_SIZE_256 (0x01)
+#define MPI26_HASH_ALGORITHM_SIZE_512 (0x02)
+
+
+/* values for EncryptionAlgorithm */
+#define MPI25_ENCRYPTION_ALG_UNUSED (0x00)
+#define MPI25_ENCRYPTION_ALG_RSA256 (0x01)
+
+#define MPI26_ENCRYPTION_ALG_UNUSED (0x00)
+#define MPI26_ENCRYPTION_ALG_RSA256 (0x01)
+#define MPI26_ENCRYPTION_ALG_RSA512 (0x02)
+#define MPI26_ENCRYPTION_ALG_RSA1024 (0x03)
+#define MPI26_ENCRYPTION_ALG_RSA2048 (0x04)
+#define MPI26_ENCRYPTION_ALG_RSA4096 (0x05)
+
+typedef struct _MPI25_ENCRYPTED_HASH_DATA {
+ U8 ImageVersion; /*0x00 */
+ U8 NumHash; /*0x01 */
+ U16 Reserved1; /*0x02 */
+ U32 Reserved2; /*0x04 */
+ MPI25_ENCRYPTED_HASH_ENTRY EncryptedHashEntry[]; /*0x08 */
+} MPI25_ENCRYPTED_HASH_DATA, *PTR_MPI25_ENCRYPTED_HASH_DATA,
+Mpi25EncryptedHashData_t, *pMpi25EncryptedHashData_t;
+
+
+#endif /* MPI2_IMAGE_H */
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_init.h b/drivers/scsi/mpt3sas/mpi/mpi2_init.h
new file mode 100644
index 000000000000..8f1b903fe0a9
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_init.h
@@ -0,0 +1,591 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2_init.h
+ * Title: MPI SCSI initiator mode messages and structures
+ * Creation Date: June 23, 2006
+ *
+ * mpi2_init.h Version: 02.00.21
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t.
+ * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines.
+ * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention.
+ * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY.
+ * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t.
+ * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO
+ * Control field Task Attribute flags.
+ * Moved LUN field defines to mpi2.h becasue they are
+ * common to many structures.
+ * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to
+ * Query Asynchronous Event.
+ * Defined two new bits in the SlotStatus field of the SCSI
+ * Enclosure Processor Request and Reply.
+ * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for
+ * both SCSI IO Error Reply and SCSI Task Management Reply.
+ * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
+ * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
+ * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it.
+ * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request.
+ * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define.
+ * 11-18-11 02.00.12 Incorporating additions for MPI v2.5.
+ * 02-06-12 02.00.13 Added alternate defines for Task Priority / Command
+ * Priority to match SAM-4.
+ * Added EEDPErrorOffset to MPI2_SCSI_IO_REPLY.
+ * 07-10-12 02.00.14 Added MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION.
+ * 04-09-13 02.00.15 Added SCSIStatusQualifier field to MPI2_SCSI_IO_REPLY,
+ * replacing the Reserved4 field.
+ * 11-18-14 02.00.16 Updated copyright information.
+ * 03-16-15 02.00.17 Updated for MPI v2.6.
+ * Added MPI26_SCSIIO_IOFLAGS_ESCAPE_PASSTHROUGH.
+ * Added MPI2_SEP_REQ_SLOTSTATUS_DEV_OFF and
+ * MPI2_SEP_REPLY_SLOTSTATUS_DEV_OFF.
+ * 08-26-15 02.00.18 Added SCSITASKMGMT_MSGFLAGS for Target Reset.
+ * 12-18-15 02.00.19 Added EEDPObservedValue added to SCSI IO Reply message.
+ * 01-04-16 02.00.20 Modified EEDP reported values in SCSI IO Reply message.
+ * 01-21-16 02.00.21 Modified MPI26_SCSITASKMGMT_MSGFLAGS_PCIE* defines to
+ * be unique within first 32 characters.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_INIT_H
+#define MPI2_INIT_H
+
+/*****************************************************************************
+*
+* SCSI Initiator Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* SCSI IO messages and associated structures
+****************************************************************************/
+
+typedef struct _MPI2_SCSI_IO_CDB_EEDP32 {
+ U8 CDB[20]; /*0x00 */
+ __be32 PrimaryReferenceTag; /*0x14 */
+ U16 PrimaryApplicationTag; /*0x18 */
+ U16 PrimaryApplicationTagMask; /*0x1A */
+ U32 TransferLength; /*0x1C */
+} MPI2_SCSI_IO_CDB_EEDP32, *PTR_MPI2_SCSI_IO_CDB_EEDP32,
+ Mpi2ScsiIoCdbEedp32_t, *pMpi2ScsiIoCdbEedp32_t;
+
+/*MPI v2.0 CDB field */
+typedef union _MPI2_SCSI_IO_CDB_UNION {
+ U8 CDB32[32];
+ MPI2_SCSI_IO_CDB_EEDP32 EEDP32;
+ MPI2_SGE_SIMPLE_UNION SGE;
+} MPI2_SCSI_IO_CDB_UNION, *PTR_MPI2_SCSI_IO_CDB_UNION,
+ Mpi2ScsiIoCdb_t, *pMpi2ScsiIoCdb_t;
+
+/*MPI v2.0 SCSI IO Request Message */
+typedef struct _MPI2_SCSI_IO_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U32 SenseBufferLowAddress; /*0x0C */
+ U16 SGLFlags; /*0x10 */
+ U8 SenseBufferLength; /*0x12 */
+ U8 Reserved4; /*0x13 */
+ U8 SGLOffset0; /*0x14 */
+ U8 SGLOffset1; /*0x15 */
+ U8 SGLOffset2; /*0x16 */
+ U8 SGLOffset3; /*0x17 */
+ U32 SkipCount; /*0x18 */
+ U32 DataLength; /*0x1C */
+ U32 BidirectionalDataLength; /*0x20 */
+ U16 IoFlags; /*0x24 */
+ U16 EEDPFlags; /*0x26 */
+ U32 EEDPBlockSize; /*0x28 */
+ U32 SecondaryReferenceTag; /*0x2C */
+ U16 SecondaryApplicationTag; /*0x30 */
+ U16 ApplicationTagTranslationMask; /*0x32 */
+ U8 LUN[8]; /*0x34 */
+ U32 Control; /*0x3C */
+ MPI2_SCSI_IO_CDB_UNION CDB; /*0x40 */
+
+#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */
+ MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion;
+#endif
+
+ MPI2_SGE_IO_UNION SGL; /*0x60 */
+
+} MPI2_SCSI_IO_REQUEST, *PTR_MPI2_SCSI_IO_REQUEST,
+ Mpi2SCSIIORequest_t, *pMpi2SCSIIORequest_t;
+
+/*SCSI IO MsgFlags bits */
+
+/*MsgFlags for SenseBufferAddressSpace */
+#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C)
+#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00)
+#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04)
+#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08)
+#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C)
+#define MPI26_SCSIIO_MSGFLAGS_IOCCTL_SENSE_ADDR (0x08)
+
+/*SCSI IO SGLFlags bits */
+
+/*base values for Data Location Address Space */
+#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C)
+#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00)
+#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04)
+#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08)
+#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C)
+
+/*base values for Type */
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02)
+
+/*shift values for each sub-field */
+#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12)
+#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8)
+#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4)
+#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0)
+
+/*number of SGLOffset fields */
+#define MPI2_SCSIIO_NUM_SGLOFFSETS (4)
+
+/*SCSI IO IoFlags bits */
+
+/*Large CDB Address Space */
+#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000)
+#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000)
+
+#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000)
+#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800)
+#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400)
+#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200)
+#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF)
+
+/*SCSI IO EEDPFlags bits */
+
+#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000)
+
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100)
+
+#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008)
+
+#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007)
+#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001)
+#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003)
+#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004)
+#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007)
+
+/*SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */
+
+/*SCSI IO Control bits */
+#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000)
+#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26)
+
+#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000)
+#define MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION (24)
+#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000)
+#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000)
+#define MPI2_SCSIIO_CONTROL_READ (0x02000000)
+#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000)
+
+#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800)
+#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11)
+/*alternate name for the previous field; called Command Priority in SAM-4 */
+#define MPI2_SCSIIO_CONTROL_CMDPRI_MASK (0x00007800)
+#define MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT (11)
+
+#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700)
+#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000)
+#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100)
+#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200)
+#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400)
+
+#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0)
+#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000)
+#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040)
+#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080)
+
+/*MPI v2.5 CDB field */
+typedef union _MPI25_SCSI_IO_CDB_UNION {
+ U8 CDB32[32];
+ MPI2_SCSI_IO_CDB_EEDP32 EEDP32;
+ MPI2_IEEE_SGE_SIMPLE64 SGE;
+} MPI25_SCSI_IO_CDB_UNION, *PTR_MPI25_SCSI_IO_CDB_UNION,
+ Mpi25ScsiIoCdb_t, *pMpi25ScsiIoCdb_t;
+
+/*MPI v2.5/2.6 SCSI IO Request Message */
+typedef struct _MPI25_SCSI_IO_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U32 SenseBufferLowAddress; /*0x0C */
+ U8 DMAFlags; /*0x10 */
+ U8 Reserved5; /*0x11 */
+ U8 SenseBufferLength; /*0x12 */
+ U8 Reserved4; /*0x13 */
+ U8 SGLOffset0; /*0x14 */
+ U8 SGLOffset1; /*0x15 */
+ U8 SGLOffset2; /*0x16 */
+ U8 SGLOffset3; /*0x17 */
+ U32 SkipCount; /*0x18 */
+ U32 DataLength; /*0x1C */
+ U32 BidirectionalDataLength; /*0x20 */
+ U16 IoFlags; /*0x24 */
+ U16 EEDPFlags; /*0x26 */
+ U16 EEDPBlockSize; /*0x28 */
+ U16 Reserved6; /*0x2A */
+ U32 SecondaryReferenceTag; /*0x2C */
+ U16 SecondaryApplicationTag; /*0x30 */
+ U16 ApplicationTagTranslationMask; /*0x32 */
+ U8 LUN[8]; /*0x34 */
+ U32 Control; /*0x3C */
+ MPI25_SCSI_IO_CDB_UNION CDB; /*0x40 */
+
+#ifdef MPI25_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */
+ MPI25_SCSI_IO_VENDOR_UNIQUE VendorRegion;
+#endif
+
+ MPI25_SGE_IO_UNION SGL; /*0x60 */
+
+} MPI25_SCSI_IO_REQUEST, *PTR_MPI25_SCSI_IO_REQUEST,
+ Mpi25SCSIIORequest_t, *pMpi25SCSIIORequest_t;
+
+/*use MPI2_SCSIIO_MSGFLAGS_ defines for the MsgFlags field */
+
+/*Defines for the DMAFlags field
+ * Each setting affects 4 SGLS, from SGL0 to SGL3.
+ * D = Data
+ * C = Cache DIF
+ * I = Interleaved
+ * H = Host DIF
+ */
+#define MPI25_SCSIIO_DMAFLAGS_OP_MASK (0x0F)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_D (0x00)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_C (0x01)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_I (0x02)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_C (0x03)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_I (0x04)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_I_I (0x05)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_C (0x06)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_I (0x07)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_I_I (0x08)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_I_I_I (0x09)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_D (0x0A)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_C (0x0B)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_I (0x0C)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_C (0x0D)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_I (0x0E)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_I_I (0x0F)
+
+/*number of SGLOffset fields */
+#define MPI25_SCSIIO_NUM_SGLOFFSETS (4)
+
+/*defines for the IoFlags field */
+#define MPI25_SCSIIO_IOFLAGS_IO_PATH_MASK (0xC000)
+#define MPI25_SCSIIO_IOFLAGS_NORMAL_PATH (0x0000)
+#define MPI25_SCSIIO_IOFLAGS_FAST_PATH (0x4000)
+
+#define MPI26_SCSIIO_IOFLAGS_ESCAPE_PASSTHROUGH (0x2000)
+#define MPI25_SCSIIO_IOFLAGS_LARGE_CDB (0x1000)
+#define MPI25_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800)
+#define MPI26_SCSIIO_IOFLAGS_PORT_REQUEST (0x0400)
+#define MPI25_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF)
+
+/*MPI v2.5 defines for the EEDPFlags bits */
+/*use MPI2_SCSIIO_EEDPFLAGS_ defines for the other EEDPFlags bits */
+#define MPI25_SCSIIO_EEDPFLAGS_ESCAPE_MODE_MASK (0x00C0)
+#define MPI25_SCSIIO_EEDPFLAGS_COMPATIBLE_MODE (0x0000)
+#define MPI25_SCSIIO_EEDPFLAGS_DO_NOT_DISABLE_MODE (0x0040)
+#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE (0x0080)
+#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_REFTAG_DISABLE_MODE (0x00C0)
+
+#define MPI25_SCSIIO_EEDPFLAGS_HOST_GUARD_METHOD_MASK (0x0030)
+#define MPI25_SCSIIO_EEDPFLAGS_T10_CRC_HOST_GUARD (0x0000)
+#define MPI25_SCSIIO_EEDPFLAGS_IP_CHKSUM_HOST_GUARD (0x0010)
+
+/*use MPI2_LUN_ defines from mpi2.h for the LUN field */
+
+/*use MPI2_SCSIIO_CONTROL_ defines for the Control field */
+
+/*NOTE: The SCSI IO Reply is nearly the same for MPI 2.0 and MPI 2.5, so
+ * MPI2_SCSI_IO_REPLY is used for both.
+ */
+
+/*SCSI IO Error Reply Message */
+typedef struct _MPI2_SCSI_IO_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U8 SCSIStatus; /*0x0C */
+ U8 SCSIState; /*0x0D */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 TransferCount; /*0x14 */
+ U32 SenseCount; /*0x18 */
+ U32 ResponseInfo; /*0x1C */
+ U16 TaskTag; /*0x20 */
+ U16 SCSIStatusQualifier; /* 0x22 */
+ U32 BidirectionalTransferCount; /*0x24 */
+ /* MPI 2.5+ only; Reserved in MPI 2.0 */
+ U32 EEDPErrorOffset; /* 0x28 */
+ /* MPI 2.5+ only; Reserved in MPI 2.0 */
+ U16 EEDPObservedAppTag; /* 0x2C */
+ /* MPI 2.5+ only; Reserved in MPI 2.0 */
+ U16 EEDPObservedGuard; /* 0x2E */
+ /* MPI 2.5+ only; Reserved in MPI 2.0 */
+ U32 EEDPObservedRefTag; /* 0x30 */
+} MPI2_SCSI_IO_REPLY, *PTR_MPI2_SCSI_IO_REPLY,
+ Mpi2SCSIIOReply_t, *pMpi2SCSIIOReply_t;
+
+/*SCSI IO Reply MsgFlags bits */
+#define MPI26_SCSIIO_REPLY_MSGFLAGS_REFTAG_OBSERVED_VALID (0x01)
+#define MPI26_SCSIIO_REPLY_MSGFLAGS_GUARD_OBSERVED_VALID (0x02)
+#define MPI26_SCSIIO_REPLY_MSGFLAGS_APPTAG_OBSERVED_VALID (0x04)
+
+/*SCSI IO Reply SCSIStatus values (SAM-4 status codes) */
+
+#define MPI2_SCSI_STATUS_GOOD (0x00)
+#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02)
+#define MPI2_SCSI_STATUS_CONDITION_MET (0x04)
+#define MPI2_SCSI_STATUS_BUSY (0x08)
+#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10)
+#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
+#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
+#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /*obsolete */
+#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28)
+#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30)
+#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40)
+
+/*SCSI IO Reply SCSIState flags */
+
+#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10)
+#define MPI2_SCSI_STATE_TERMINATED (0x08)
+#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04)
+#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02)
+#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01)
+
+/*masks and shifts for the ResponseInfo field */
+
+#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF)
+#define MPI2_SCSI_RI_SHIFT_REASONCODE (0)
+
+#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF)
+
+/****************************************************************************
+* SCSI Task Management messages
+****************************************************************************/
+
+/*SCSI Task Management Request Message */
+typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Reserved1; /*0x04 */
+ U8 TaskType; /*0x05 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U8 LUN[8]; /*0x0C */
+ U32 Reserved4[7]; /*0x14 */
+ U16 TaskMID; /*0x30 */
+ U16 Reserved5; /*0x32 */
+} MPI2_SCSI_TASK_MANAGE_REQUEST,
+ *PTR_MPI2_SCSI_TASK_MANAGE_REQUEST,
+ Mpi2SCSITaskManagementRequest_t,
+ *pMpi2SCSITaskManagementRequest_t;
+
+/*TaskType values */
+
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01)
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02)
+#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
+#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A)
+
+/*obsolete TaskType name */
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \
+ (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT)
+
+/*MsgFlags bits */
+
+#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18)
+#define MPI26_SCSITASKMGMT_MSGFLAGS_HOT_RESET_PCIE (0x00)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10)
+
+#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01)
+#define MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE (0x18)
+
+/*SCSI Task Management Reply Message */
+typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 ResponseCode; /*0x04 */
+ U8 TaskType; /*0x05 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 TerminationCount; /*0x14 */
+ U32 ResponseInfo; /*0x18 */
+} MPI2_SCSI_TASK_MANAGE_REPLY,
+ *PTR_MPI2_SCSI_TASK_MANAGE_REPLY,
+ Mpi2SCSITaskManagementReply_t, *pMpi2SCSIManagementReply_t;
+
+/*ResponseCode values */
+
+#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00)
+#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02)
+#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04)
+#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05)
+#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08)
+#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09)
+#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A)
+#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80)
+
+/*masks and shifts for the ResponseInfo field */
+
+#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24)
+
+/****************************************************************************
+* SCSI Enclosure Processor messages
+****************************************************************************/
+
+/*SCSI Enclosure Processor Request Message */
+typedef struct _MPI2_SEP_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Action; /*0x04 */
+ U8 Flags; /*0x05 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 SlotStatus; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U32 Reserved4; /*0x14 */
+ U32 Reserved5; /*0x18 */
+ U16 Slot; /*0x1C */
+ U16 EnclosureHandle; /*0x1E */
+} MPI2_SEP_REQUEST, *PTR_MPI2_SEP_REQUEST,
+ Mpi2SepRequest_t, *pMpi2SepRequest_t;
+
+/*Action defines */
+#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00)
+#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01)
+
+/*Flags defines */
+#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00)
+#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01)
+
+/*SlotStatus defines */
+#define MPI2_SEP_REQ_SLOTSTATUS_DEV_OFF (0x00080000)
+#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000)
+#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
+#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
+#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001)
+
+/*SCSI Enclosure Processor Reply Message */
+typedef struct _MPI2_SEP_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Action; /*0x04 */
+ U8 Flags; /*0x05 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 SlotStatus; /*0x14 */
+ U32 Reserved4; /*0x18 */
+ U16 Slot; /*0x1C */
+ U16 EnclosureHandle; /*0x1E */
+} MPI2_SEP_REPLY, *PTR_MPI2_SEP_REPLY,
+ Mpi2SepReply_t, *pMpi2SepReply_t;
+
+/*SlotStatus defines */
+#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_OFF (0x00080000)
+#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000)
+#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
+#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
+#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001)
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
new file mode 100644
index 000000000000..1a279c6e1a9f
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
@@ -0,0 +1,1856 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2_ioc.h
+ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
+ * Creation Date: October 11, 2006
+ *
+ * mpi2_ioc.h Version: 02.00.37
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to
+ * MaxTargets.
+ * Added TotalImageSize field to FWDownload Request.
+ * Added reserved words to FWUpload Request.
+ * 06-26-07 02.00.02 Added IR Configuration Change List Event.
+ * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit
+ * request and replaced it with
+ * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth.
+ * Replaced the MinReplyQueueDepth field of the IOCFacts
+ * reply with MaxReplyDescriptorPostQueueDepth.
+ * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum
+ * depth for the Reply Descriptor Post Queue.
+ * Added SASAddress field to Initiator Device Table
+ * Overflow Event data.
+ * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING
+ * for SAS Initiator Device Status Change Event data.
+ * Modified Reason Code defines for SAS Topology Change
+ * List Event data, including adding a bit for PHY Vacant
+ * status, and adding a mask for the Reason Code.
+ * Added define for
+ * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING.
+ * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID.
+ * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of
+ * the IOCFacts Reply.
+ * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ * Moved MPI2_VERSION_UNION to mpi2.h.
+ * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks
+ * instead of enables, and added SASBroadcastPrimitiveMasks
+ * field.
+ * Added Log Entry Added Event and related structure.
+ * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID.
+ * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET.
+ * Added MaxVolumes and MaxPersistentEntries fields to
+ * IOCFacts reply.
+ * Added ProtocalFlags and IOCCapabilities fields to
+ * MPI2_FW_IMAGE_HEADER.
+ * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT.
+ * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to
+ * a U16 (from a U32).
+ * Removed extra 's' from EventMasks name.
+ * 06-27-08 02.00.08 Fixed an offset in a comment.
+ * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST.
+ * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and
+ * renamed MinReplyFrameSize to ReplyFrameSize.
+ * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX.
+ * Added two new RAIDOperation values for Integrated RAID
+ * Operations Status Event data.
+ * Added four new IR Configuration Change List Event data
+ * ReasonCode values.
+ * Added two new ReasonCode defines for SAS Device Status
+ * Change Event data.
+ * Added three new DiscoveryStatus bits for the SAS
+ * Discovery event data.
+ * Added Multiplexing Status Change bit to the PhyStatus
+ * field of the SAS Topology Change List event data.
+ * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY.
+ * BootFlags are now product-specific.
+ * Added defines for the indivdual signature bytes
+ * for MPI2_INIT_IMAGE_FOOTER.
+ * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define.
+ * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR
+ * define.
+ * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE
+ * define.
+ * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define.
+ * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define.
+ * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define.
+ * Added two new reason codes for SAS Device Status Change
+ * Event.
+ * Added new event: SAS PHY Counter.
+ * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure.
+ * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ * Added new product id family for 2208.
+ * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST.
+ * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY.
+ * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY.
+ * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY.
+ * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define.
+ * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define.
+ * Added Host Based Discovery Phy Event data.
+ * Added defines for ProductID Product field
+ * (MPI2_FW_HEADER_PID_).
+ * Modified values for SAS ProductID Family
+ * (MPI2_FW_HEADER_PID_FAMILY_).
+ * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines.
+ * Added PowerManagementControl Request structures and
+ * defines.
+ * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete.
+ * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define.
+ * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC.
+ * 02-23-11 02.00.17 Added SAS NOTIFY Primitive event, and added
+ * SASNotifyPrimitiveMasks field to
+ * MPI2_EVENT_NOTIFICATION_REQUEST.
+ * Added Temperature Threshold Event.
+ * Added Host Message Event.
+ * Added Send Host Message request and reply.
+ * 05-25-11 02.00.18 For Extended Image Header, added
+ * MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC and
+ * MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC defines.
+ * Deprecated MPI2_EXT_IMAGE_TYPE_MAX define.
+ * 08-24-11 02.00.19 Added PhysicalPort field to
+ * MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE structure.
+ * Marked MPI2_PM_CONTROL_FEATURE_PCIE_LINK as obsolete.
+ * 11-18-11 02.00.20 Incorporating additions for MPI v2.5.
+ * 03-29-12 02.00.21 Added a product specific range to event values.
+ * 07-26-12 02.00.22 Added MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE.
+ * Added ElapsedSeconds field to
+ * MPI2_EVENT_DATA_IR_OPERATION_STATUS.
+ * 08-19-13 02.00.23 For IOCInit, added MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE
+ * and MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY.
+ * Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE.
+ * Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY.
+ * Added Encrypted Hash Extended Image.
+ * 12-05-13 02.00.24 Added MPI25_HASH_IMAGE_TYPE_BIOS.
+ * 11-18-14 02.00.25 Updated copyright information.
+ * 03-16-15 02.00.26 Updated for MPI v2.6.
+ * Added MPI2_EVENT_ACTIVE_CABLE_EXCEPTION and
+ * MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT.
+ * Added MPI26_FW_HEADER_PID_FAMILY_3324_SAS and
+ * MPI26_FW_HEADER_PID_FAMILY_3516_SAS.
+ * Added MPI26_CTRL_OP_SHUTDOWN.
+ * 08-25-15 02.00.27 Added IC ARCH Class based signature defines.
+ * Added MPI26_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED event.
+ * Added ConigurationFlags field to IOCInit message to
+ * support NVMe SGL format control.
+ * Added PCIe SRIOV support.
+ * 02-17-16 02.00.28 Added SAS 4 22.5 gbs speed support.
+ * Added PCIe 4 16.0 GT/sec speec support.
+ * Removed AHCI support.
+ * Removed SOP support.
+ * 07-01-16 02.00.29 Added Archclass for 4008 product.
+ * Added IOCException MPI2_IOCFACTS_EXCEPT_PCIE_DISABLED
+ * 08-23-16 02.00.30 Added new defines for the ImageType field of FWDownload
+ * Request Message.
+ * Added new defines for the ImageType field of FWUpload
+ * Request Message.
+ * Added new values for the RegionType field in the Layout
+ * Data sections of the FLASH Layout Extended Image Data.
+ * Added new defines for the ReasonCode field of
+ * Active Cable Exception Event.
+ * Added MPI2_EVENT_ENCL_DEVICE_STATUS_CHANGE and
+ * MPI26_EVENT_DATA_ENCL_DEV_STATUS_CHANGE.
+ * 11-23-16 02.00.31 Added MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR and
+ * MPI25_EVENT_DATA_SAS_DEVICE_DISCOVERY_ERROR.
+ * 02-02-17 02.00.32 Added MPI2_FW_DOWNLOAD_ITYPE_CBB_BACKUP.
+ * Added MPI25_EVENT_DATA_ACTIVE_CABLE_EXCEPT and related
+ * defines for the ReasonCode field.
+ * 06-13-17 02.00.33 Added MPI2_FW_DOWNLOAD_ITYPE_CPLD.
+ * 09-29-17 02.00.34 Added MPI26_EVENT_PCIDEV_STAT_RC_PCIE_HOT_RESET_FAILED
+ * to the ReasonCode field in PCIe Device Status Change
+ * Event Data.
+ * 07-22-18 02.00.35 Added FW_DOWNLOAD_ITYPE_CPLD and _PSOC.
+ * Moved FW image definitions ionto new mpi2_image,h
+ * 08-14-18 02.00.36 Fixed definition of MPI2_FW_DOWNLOAD_ITYPE_PSOC (0x16)
+ * 09-07-18 02.00.37 Added MPI26_EVENT_PCIE_TOPO_PI_16_LANES
+ * 10-02-19 02.00.38 Added MPI26_IOCINIT_CFGFLAGS_COREDUMP_ENABLE
+ * Added MPI26_IOCFACTS_CAPABILITY_COREDUMP_ENABLED
+ * Added MPI2_FW_DOWNLOAD_ITYPE_COREDUMP
+ * Added MPI2_FW_UPLOAD_ITYPE_COREDUMP
+ * 9-13-24 02.00.39 Added MPI26_MCTP_PASSTHROUGH messages
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_IOC_H
+#define MPI2_IOC_H
+
+/*****************************************************************************
+*
+* IOC Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* IOCInit message
+****************************************************************************/
+
+/*IOCInit Request message */
+typedef struct _MPI2_IOC_INIT_REQUEST {
+ U8 WhoInit; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 MsgVersion; /*0x0C */
+ U16 HeaderVersion; /*0x0E */
+ U32 Reserved5; /*0x10 */
+ U16 ConfigurationFlags; /* 0x14 */
+ U8 HostPageSize; /*0x16 */
+ U8 HostMSIxVectors; /*0x17 */
+ U16 Reserved8; /*0x18 */
+ U16 SystemRequestFrameSize; /*0x1A */
+ U16 ReplyDescriptorPostQueueDepth; /*0x1C */
+ U16 ReplyFreeQueueDepth; /*0x1E */
+ U32 SenseBufferAddressHigh; /*0x20 */
+ U32 SystemReplyAddressHigh; /*0x24 */
+ U64 SystemRequestFrameBaseAddress; /*0x28 */
+ U64 ReplyDescriptorPostQueueAddress; /*0x30 */
+ U64 ReplyFreeQueueAddress; /*0x38 */
+ U64 TimeStamp; /*0x40 */
+} MPI2_IOC_INIT_REQUEST, *PTR_MPI2_IOC_INIT_REQUEST,
+ Mpi2IOCInitRequest_t, *pMpi2IOCInitRequest_t;
+
+/*WhoInit values */
+#define MPI2_WHOINIT_NOT_INITIALIZED (0x00)
+#define MPI2_WHOINIT_SYSTEM_BIOS (0x01)
+#define MPI2_WHOINIT_ROM_BIOS (0x02)
+#define MPI2_WHOINIT_PCI_PEER (0x03)
+#define MPI2_WHOINIT_HOST_DRIVER (0x04)
+#define MPI2_WHOINIT_MANUFACTURER (0x05)
+
+/* MsgFlags */
+#define MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE (0x01)
+
+
+/*MsgVersion */
+#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00)
+#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8)
+#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF)
+#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0)
+
+/*HeaderVersion */
+#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00)
+#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8)
+#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF)
+#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0)
+
+/*ConfigurationFlags */
+#define MPI26_IOCINIT_CFGFLAGS_NVME_SGL_FORMAT (0x0001)
+#define MPI26_IOCINIT_CFGFLAGS_COREDUMP_ENABLE (0x0002)
+
+/*minimum depth for a Reply Descriptor Post Queue */
+#define MPI2_RDPQ_DEPTH_MIN (16)
+
+/* Reply Descriptor Post Queue Array Entry */
+typedef struct _MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY {
+ U64 RDPQBaseAddress; /* 0x00 */
+ U32 Reserved1; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+} MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY,
+*PTR_MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY,
+Mpi2IOCInitRDPQArrayEntry, *pMpi2IOCInitRDPQArrayEntry;
+
+
+/*IOCInit Reply message */
+typedef struct _MPI2_IOC_INIT_REPLY {
+ U8 WhoInit; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_IOC_INIT_REPLY, *PTR_MPI2_IOC_INIT_REPLY,
+ Mpi2IOCInitReply_t, *pMpi2IOCInitReply_t;
+
+/****************************************************************************
+* IOCFacts message
+****************************************************************************/
+
+/*IOCFacts Request message */
+typedef struct _MPI2_IOC_FACTS_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+} MPI2_IOC_FACTS_REQUEST, *PTR_MPI2_IOC_FACTS_REQUEST,
+ Mpi2IOCFactsRequest_t, *pMpi2IOCFactsRequest_t;
+
+/*IOCFacts Reply message */
+typedef struct _MPI2_IOC_FACTS_REPLY {
+ U16 MsgVersion; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 HeaderVersion; /*0x04 */
+ U8 IOCNumber; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U16 IOCExceptions; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 MaxChainDepth; /*0x14 */
+ U8 WhoInit; /*0x15 */
+ U8 NumberOfPorts; /*0x16 */
+ U8 MaxMSIxVectors; /*0x17 */
+ U16 RequestCredit; /*0x18 */
+ U16 ProductID; /*0x1A */
+ U32 IOCCapabilities; /*0x1C */
+ MPI2_VERSION_UNION FWVersion; /*0x20 */
+ U16 IOCRequestFrameSize; /*0x24 */
+ U16 IOCMaxChainSegmentSize; /*0x26 */
+ U16 MaxInitiators; /*0x28 */
+ U16 MaxTargets; /*0x2A */
+ U16 MaxSasExpanders; /*0x2C */
+ U16 MaxEnclosures; /*0x2E */
+ U16 ProtocolFlags; /*0x30 */
+ U16 HighPriorityCredit; /*0x32 */
+ U16 MaxReplyDescriptorPostQueueDepth; /*0x34 */
+ U8 ReplyFrameSize; /*0x36 */
+ U8 MaxVolumes; /*0x37 */
+ U16 MaxDevHandle; /*0x38 */
+ U16 MaxPersistentEntries; /*0x3A */
+ U16 MinDevHandle; /*0x3C */
+ U8 CurrentHostPageSize; /* 0x3E */
+ U8 Reserved4; /* 0x3F */
+ U8 SGEModifierMask; /*0x40 */
+ U8 SGEModifierValue; /*0x41 */
+ U8 SGEModifierShift; /*0x42 */
+ U8 Reserved5; /*0x43 */
+} MPI2_IOC_FACTS_REPLY, *PTR_MPI2_IOC_FACTS_REPLY,
+ Mpi2IOCFactsReply_t, *pMpi2IOCFactsReply_t;
+
+/*MsgVersion */
+#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00)
+#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8)
+#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF)
+#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0)
+
+/*HeaderVersion */
+#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00)
+#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8)
+#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF)
+#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0)
+
+/*IOCExceptions */
+#define MPI2_IOCFACTS_EXCEPT_PCIE_DISABLED (0x0400)
+#define MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0200)
+#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100)
+
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060)
+
+#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010)
+#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008)
+#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004)
+#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002)
+#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001)
+
+/*defines for WhoInit field are after the IOCInit Request */
+
+/*ProductID field uses MPI2_FW_HEADER_PID_ */
+
+/*IOCCapabilities */
+#define MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU (0x00800000)
+#define MPI26_IOCFACTS_CAPABILITY_COREDUMP_ENABLED (0x00200000)
+#define MPI26_IOCFACTS_CAPABILITY_PCIE_SRIOV (0x00100000)
+#define MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ (0x00080000)
+#define MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE (0x00040000)
+#define MPI25_IOCFACTS_CAPABILITY_FAST_PATH_CAPABLE (0x00020000)
+#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000)
+#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000)
+#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000)
+#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000)
+#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000)
+#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800)
+#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100)
+#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080)
+#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040)
+#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020)
+#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010)
+#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008)
+#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004)
+
+/*ProtocolFlags */
+#define MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES (0x0008)
+#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002)
+#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001)
+
+/****************************************************************************
+* PortFacts message
+****************************************************************************/
+
+/*PortFacts Request message */
+typedef struct _MPI2_PORT_FACTS_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 PortNumber; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+} MPI2_PORT_FACTS_REQUEST, *PTR_MPI2_PORT_FACTS_REQUEST,
+ Mpi2PortFactsRequest_t, *pMpi2PortFactsRequest_t;
+
+/*PortFacts Reply message */
+typedef struct _MPI2_PORT_FACTS_REPLY {
+ U16 Reserved1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 PortNumber; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 Reserved5; /*0x14 */
+ U8 PortType; /*0x15 */
+ U16 Reserved6; /*0x16 */
+ U16 MaxPostedCmdBuffers; /*0x18 */
+ U16 Reserved7; /*0x1A */
+} MPI2_PORT_FACTS_REPLY, *PTR_MPI2_PORT_FACTS_REPLY,
+ Mpi2PortFactsReply_t, *pMpi2PortFactsReply_t;
+
+/*PortType values */
+#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00)
+#define MPI2_PORTFACTS_PORTTYPE_FC (0x10)
+#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20)
+#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30)
+#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31)
+#define MPI2_PORTFACTS_PORTTYPE_TRI_MODE (0x40)
+
+
+/****************************************************************************
+* PortEnable message
+****************************************************************************/
+
+/*PortEnable Request message */
+typedef struct _MPI2_PORT_ENABLE_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Reserved2; /*0x04 */
+ U8 PortFlags; /*0x05 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+} MPI2_PORT_ENABLE_REQUEST, *PTR_MPI2_PORT_ENABLE_REQUEST,
+ Mpi2PortEnableRequest_t, *pMpi2PortEnableRequest_t;
+
+/*PortEnable Reply message */
+typedef struct _MPI2_PORT_ENABLE_REPLY {
+ U16 Reserved1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Reserved2; /*0x04 */
+ U8 PortFlags; /*0x05 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_PORT_ENABLE_REPLY, *PTR_MPI2_PORT_ENABLE_REPLY,
+ Mpi2PortEnableReply_t, *pMpi2PortEnableReply_t;
+
+/****************************************************************************
+* EventNotification message
+****************************************************************************/
+
+/*EventNotification Request message */
+#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4)
+
+typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; /*0x14 */
+ U16 SASBroadcastPrimitiveMasks; /*0x24 */
+ U16 SASNotifyPrimitiveMasks; /*0x26 */
+ U32 Reserved8; /*0x28 */
+} MPI2_EVENT_NOTIFICATION_REQUEST,
+ *PTR_MPI2_EVENT_NOTIFICATION_REQUEST,
+ Mpi2EventNotificationRequest_t,
+ *pMpi2EventNotificationRequest_t;
+
+/*EventNotification Reply message */
+typedef struct _MPI2_EVENT_NOTIFICATION_REPLY {
+ U16 EventDataLength; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 AckRequired; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U16 Event; /*0x14 */
+ U16 Reserved4; /*0x16 */
+ U32 EventContext; /*0x18 */
+ U32 EventData[]; /*0x1C */
+} MPI2_EVENT_NOTIFICATION_REPLY, *PTR_MPI2_EVENT_NOTIFICATION_REPLY,
+ Mpi2EventNotificationReply_t,
+ *pMpi2EventNotificationReply_t;
+
+/*AckRequired */
+#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00)
+#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01)
+
+/*Event */
+#define MPI2_EVENT_LOG_DATA (0x0001)
+#define MPI2_EVENT_STATE_CHANGE (0x0002)
+#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005)
+#define MPI2_EVENT_EVENT_CHANGE (0x000A)
+#define MPI2_EVENT_TASK_SET_FULL (0x000E) /*obsolete */
+#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F)
+#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014)
+#define MPI2_EVENT_SAS_DISCOVERY (0x0016)
+#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017)
+#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018)
+#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019)
+#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C)
+#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D)
+#define MPI2_EVENT_ENCL_DEVICE_STATUS_CHANGE (0x001D)
+#define MPI2_EVENT_IR_VOLUME (0x001E)
+#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F)
+#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020)
+#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021)
+#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022)
+#define MPI2_EVENT_GPIO_INTERRUPT (0x0023)
+#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024)
+#define MPI2_EVENT_SAS_QUIESCE (0x0025)
+#define MPI2_EVENT_SAS_NOTIFY_PRIMITIVE (0x0026)
+#define MPI2_EVENT_TEMP_THRESHOLD (0x0027)
+#define MPI2_EVENT_HOST_MESSAGE (0x0028)
+#define MPI2_EVENT_POWER_PERFORMANCE_CHANGE (0x0029)
+#define MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE (0x0030)
+#define MPI2_EVENT_PCIE_ENUMERATION (0x0031)
+#define MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST (0x0032)
+#define MPI2_EVENT_PCIE_LINK_COUNTER (0x0033)
+#define MPI2_EVENT_ACTIVE_CABLE_EXCEPTION (0x0034)
+#define MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR (0x0035)
+#define MPI2_EVENT_MIN_PRODUCT_SPECIFIC (0x006E)
+#define MPI2_EVENT_MAX_PRODUCT_SPECIFIC (0x007F)
+
+/*Log Entry Added Event data */
+
+/*the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */
+#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C)
+
+typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED {
+ U64 TimeStamp; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U16 LogSequence; /*0x0C */
+ U16 LogEntryQualifier; /*0x0E */
+ U8 VP_ID; /*0x10 */
+ U8 VF_ID; /*0x11 */
+ U16 Reserved2; /*0x12 */
+ U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH]; /*0x14 */
+} MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
+ *PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
+ Mpi2EventDataLogEntryAdded_t,
+ *pMpi2EventDataLogEntryAdded_t;
+
+/*GPIO Interrupt Event data */
+
+typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT {
+ U8 GPIONum; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+} MPI2_EVENT_DATA_GPIO_INTERRUPT,
+ *PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT,
+ Mpi2EventDataGpioInterrupt_t,
+ *pMpi2EventDataGpioInterrupt_t;
+
+/*Temperature Threshold Event data */
+
+typedef struct _MPI2_EVENT_DATA_TEMPERATURE {
+ U16 Status; /*0x00 */
+ U8 SensorNum; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U16 CurrentTemperature; /*0x04 */
+ U16 Reserved2; /*0x06 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+} MPI2_EVENT_DATA_TEMPERATURE,
+ *PTR_MPI2_EVENT_DATA_TEMPERATURE,
+ Mpi2EventDataTemperature_t, *pMpi2EventDataTemperature_t;
+
+/*Temperature Threshold Event data Status bits */
+#define MPI2_EVENT_TEMPERATURE3_EXCEEDED (0x0008)
+#define MPI2_EVENT_TEMPERATURE2_EXCEEDED (0x0004)
+#define MPI2_EVENT_TEMPERATURE1_EXCEEDED (0x0002)
+#define MPI2_EVENT_TEMPERATURE0_EXCEEDED (0x0001)
+
+/*Host Message Event data */
+
+typedef struct _MPI2_EVENT_DATA_HOST_MESSAGE {
+ U8 SourceVF_ID; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Reserved3; /*0x04 */
+ U32 HostData[]; /*0x08 */
+} MPI2_EVENT_DATA_HOST_MESSAGE, *PTR_MPI2_EVENT_DATA_HOST_MESSAGE,
+ Mpi2EventDataHostMessage_t, *pMpi2EventDataHostMessage_t;
+
+/*Power Performance Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_POWER_PERF_CHANGE {
+ U8 CurrentPowerMode; /*0x00 */
+ U8 PreviousPowerMode; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_EVENT_DATA_POWER_PERF_CHANGE,
+ *PTR_MPI2_EVENT_DATA_POWER_PERF_CHANGE,
+ Mpi2EventDataPowerPerfChange_t,
+ *pMpi2EventDataPowerPerfChange_t;
+
+/*defines for CurrentPowerMode and PreviousPowerMode fields */
+#define MPI2_EVENT_PM_INIT_MASK (0xC0)
+#define MPI2_EVENT_PM_INIT_UNAVAILABLE (0x00)
+#define MPI2_EVENT_PM_INIT_HOST (0x40)
+#define MPI2_EVENT_PM_INIT_IO_UNIT (0x80)
+#define MPI2_EVENT_PM_INIT_PCIE_DPA (0xC0)
+
+#define MPI2_EVENT_PM_MODE_MASK (0x07)
+#define MPI2_EVENT_PM_MODE_UNAVAILABLE (0x00)
+#define MPI2_EVENT_PM_MODE_UNKNOWN (0x01)
+#define MPI2_EVENT_PM_MODE_FULL_POWER (0x04)
+#define MPI2_EVENT_PM_MODE_REDUCED_POWER (0x05)
+#define MPI2_EVENT_PM_MODE_STANDBY (0x06)
+
+/* Active Cable Exception Event data */
+
+typedef struct _MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT {
+ U32 ActiveCablePowerRequirement; /* 0x00 */
+ U8 ReasonCode; /* 0x04 */
+ U8 ReceptacleID; /* 0x05 */
+ U16 Reserved1; /* 0x06 */
+} MPI25_EVENT_DATA_ACTIVE_CABLE_EXCEPT,
+ *PTR_MPI25_EVENT_DATA_ACTIVE_CABLE_EXCEPT,
+ Mpi25EventDataActiveCableExcept_t,
+ *pMpi25EventDataActiveCableExcept_t,
+ MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT,
+ *PTR_MPI26_EVENT_DATA_ACTIVE_CABLE_EXCEPT,
+ Mpi26EventDataActiveCableExcept_t,
+ *pMpi26EventDataActiveCableExcept_t;
+
+/*MPI2.5 defines for the ReasonCode field */
+#define MPI25_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER (0x00)
+#define MPI25_EVENT_ACTIVE_CABLE_PRESENT (0x01)
+#define MPI25_EVENT_ACTIVE_CABLE_DEGRADED (0x02)
+
+/* defines for ReasonCode field */
+#define MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER (0x00)
+#define MPI26_EVENT_ACTIVE_CABLE_PRESENT (0x01)
+#define MPI26_EVENT_ACTIVE_CABLE_DEGRADED (0x02)
+
+/*Hard Reset Received Event data */
+
+typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED {
+ U8 Reserved1; /*0x00 */
+ U8 Port; /*0x01 */
+ U16 Reserved2; /*0x02 */
+} MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
+ *PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
+ Mpi2EventDataHardResetReceived_t,
+ *pMpi2EventDataHardResetReceived_t;
+
+/*Task Set Full Event data */
+/* this event is obsolete */
+
+typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL {
+ U16 DevHandle; /*0x00 */
+ U16 CurrentDepth; /*0x02 */
+} MPI2_EVENT_DATA_TASK_SET_FULL, *PTR_MPI2_EVENT_DATA_TASK_SET_FULL,
+ Mpi2EventDataTaskSetFull_t, *pMpi2EventDataTaskSetFull_t;
+
+/*SAS Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE {
+ U16 TaskTag; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U8 ASC; /*0x04 */
+ U8 ASCQ; /*0x05 */
+ U16 DevHandle; /*0x06 */
+ U32 Reserved2; /*0x08 */
+ U64 SASAddress; /*0x0C */
+ U8 LUN[8]; /*0x14 */
+} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
+ *PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
+ Mpi2EventDataSasDeviceStatusChange_t,
+ *pMpi2EventDataSasDeviceStatusChange_t;
+
+/*SAS Device Status Change Event data ReasonCode values */
+#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12)
+
+/*Integrated RAID Operation Status Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS {
+ U16 VolDevHandle; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U8 RAIDOperation; /*0x04 */
+ U8 PercentComplete; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U32 ElapsedSeconds; /*0x08 */
+} MPI2_EVENT_DATA_IR_OPERATION_STATUS,
+ *PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS,
+ Mpi2EventDataIrOperationStatus_t,
+ *pMpi2EventDataIrOperationStatus_t;
+
+/*Integrated RAID Operation Status Event data RAIDOperation values */
+#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00)
+#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02)
+#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03)
+#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04)
+
+/*Integrated RAID Volume Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_VOLUME {
+ U16 VolDevHandle; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 NewValue; /*0x04 */
+ U32 PreviousValue; /*0x08 */
+} MPI2_EVENT_DATA_IR_VOLUME, *PTR_MPI2_EVENT_DATA_IR_VOLUME,
+ Mpi2EventDataIrVolume_t, *pMpi2EventDataIrVolume_t;
+
+/*Integrated RAID Volume Event data ReasonCode values */
+#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01)
+#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02)
+#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03)
+
+/*Integrated RAID Physical Disk Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK {
+ U16 Reserved1; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysDiskNum; /*0x03 */
+ U16 PhysDiskDevHandle; /*0x04 */
+ U16 Reserved2; /*0x06 */
+ U16 Slot; /*0x08 */
+ U16 EnclosureHandle; /*0x0A */
+ U32 NewValue; /*0x0C */
+ U32 PreviousValue; /*0x10 */
+} MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
+ *PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
+ Mpi2EventDataIrPhysicalDisk_t,
+ *pMpi2EventDataIrPhysicalDisk_t;
+
+/*Integrated RAID Physical Disk Event data ReasonCode values */
+#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01)
+#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02)
+#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03)
+
+/*Integrated RAID Configuration Change List Event data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check NumElements at
+ *runtime before using ConfigElement[].
+ */
+
+typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT {
+ U16 ElementFlags; /*0x00 */
+ U16 VolDevHandle; /*0x02 */
+ U8 ReasonCode; /*0x04 */
+ U8 PhysDiskNum; /*0x05 */
+ U16 PhysDiskDevHandle; /*0x06 */
+} MPI2_EVENT_IR_CONFIG_ELEMENT, *PTR_MPI2_EVENT_IR_CONFIG_ELEMENT,
+ Mpi2EventIrConfigElement_t, *pMpi2EventIrConfigElement_t;
+
+/*IR Configuration Change List Event data ElementFlags values */
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002)
+
+/*IR Configuration Change List Event data ReasonCode values */
+#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01)
+#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02)
+#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03)
+#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04)
+#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05)
+#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06)
+#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07)
+#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08)
+#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09)
+
+typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST {
+ U8 NumElements; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 Reserved2; /*0x02 */
+ U8 ConfigNum; /*0x03 */
+ U32 Flags; /*0x04 */
+ MPI2_EVENT_IR_CONFIG_ELEMENT
+ ConfigElement[];/*0x08 */
+} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
+ *PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
+ Mpi2EventDataIrConfigChangeList_t,
+ *pMpi2EventDataIrConfigChangeList_t;
+
+/*IR Configuration Change List Event data Flags values */
+#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001)
+
+/*SAS Discovery Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY {
+ U8 Flags; /*0x00 */
+ U8 ReasonCode; /*0x01 */
+ U8 PhysicalPort; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 DiscoveryStatus; /*0x04 */
+} MPI2_EVENT_DATA_SAS_DISCOVERY,
+ *PTR_MPI2_EVENT_DATA_SAS_DISCOVERY,
+ Mpi2EventDataSasDiscovery_t, *pMpi2EventDataSasDiscovery_t;
+
+/*SAS Discovery Event data Flags values */
+#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02)
+#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01)
+
+/*SAS Discovery Event data ReasonCode values */
+#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01)
+#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02)
+
+/*SAS Discovery Event data DiscoveryStatus values */
+#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400)
+#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001)
+
+/*SAS Broadcast Primitive Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE {
+ U8 PhyNum; /*0x00 */
+ U8 Port; /*0x01 */
+ U8 PortWidth; /*0x02 */
+ U8 Primitive; /*0x03 */
+} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
+ *PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
+ Mpi2EventDataSasBroadcastPrimitive_t,
+ *pMpi2EventDataSasBroadcastPrimitive_t;
+
+/*defines for the Primitive field */
+#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01)
+#define MPI2_EVENT_PRIMITIVE_SES (0x02)
+#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03)
+#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04)
+#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05)
+#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06)
+#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07)
+#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08)
+
+/*SAS Notify Primitive Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE {
+ U8 PhyNum; /*0x00 */
+ U8 Port; /*0x01 */
+ U8 Reserved1; /*0x02 */
+ U8 Primitive; /*0x03 */
+} MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE,
+ *PTR_MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE,
+ Mpi2EventDataSasNotifyPrimitive_t,
+ *pMpi2EventDataSasNotifyPrimitive_t;
+
+/*defines for the Primitive field */
+#define MPI2_EVENT_NOTIFY_ENABLE_SPINUP (0x01)
+#define MPI2_EVENT_NOTIFY_POWER_LOSS_EXPECTED (0x02)
+#define MPI2_EVENT_NOTIFY_RESERVED1 (0x03)
+#define MPI2_EVENT_NOTIFY_RESERVED2 (0x04)
+
+/*SAS Initiator Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE {
+ U8 ReasonCode; /*0x00 */
+ U8 PhysicalPort; /*0x01 */
+ U16 DevHandle; /*0x02 */
+ U64 SASAddress; /*0x04 */
+} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
+ *PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
+ Mpi2EventDataSasInitDevStatusChange_t,
+ *pMpi2EventDataSasInitDevStatusChange_t;
+
+/*SAS Initiator Device Status Change event ReasonCode values */
+#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01)
+#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02)
+
+/*SAS Initiator Device Table Overflow Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW {
+ U16 MaxInit; /*0x00 */
+ U16 CurrentInit; /*0x02 */
+ U64 SASAddress; /*0x04 */
+} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
+ *PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
+ Mpi2EventDataSasInitTableOverflow_t,
+ *pMpi2EventDataSasInitTableOverflow_t;
+
+/*SAS Topology Change List Event data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check NumEntries at
+ *runtime before using PHY[].
+ */
+
+typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY {
+ U16 AttachedDevHandle; /*0x00 */
+ U8 LinkRate; /*0x02 */
+ U8 PhyStatus; /*0x03 */
+} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, *PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY,
+ Mpi2EventSasTopoPhyEntry_t, *pMpi2EventSasTopoPhyEntry_t;
+
+typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST {
+ U16 EnclosureHandle; /*0x00 */
+ U16 ExpanderDevHandle; /*0x02 */
+ U8 NumPhys; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U8 NumEntries; /*0x08 */
+ U8 StartPhyNum; /*0x09 */
+ U8 ExpStatus; /*0x0A */
+ U8 PhysicalPort; /*0x0B */
+ MPI2_EVENT_SAS_TOPO_PHY_ENTRY
+ PHY[]; /*0x0C */
+} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
+ *PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
+ Mpi2EventDataSasTopologyChangeList_t,
+ *pMpi2EventDataSasTopologyChangeList_t;
+
+/*values for the ExpStatus field */
+#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00)
+#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01)
+#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
+#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
+#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04)
+
+/*defines for the LinkRate field */
+#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0)
+#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4)
+#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F)
+#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0)
+
+#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00)
+#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01)
+#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02)
+#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03)
+#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04)
+#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A)
+#define MPI25_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0B)
+#define MPI26_EVENT_SAS_TOPO_LR_RATE_22_5 (0x0C)
+
+/*values for the PhyStatus field */
+#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80)
+#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10)
+/*values for the PhyStatus ReasonCode sub-field */
+#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F)
+#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01)
+#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02)
+#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03)
+#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04)
+#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05)
+
+/*SAS Enclosure Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE {
+ U16 EnclosureHandle; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U64 EnclosureLogicalID; /*0x04 */
+ U16 NumSlots; /*0x0C */
+ U16 StartSlot; /*0x0E */
+ U32 PhyBits; /*0x10 */
+} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
+ *PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
+ Mpi2EventDataSasEnclDevStatusChange_t,
+ *pMpi2EventDataSasEnclDevStatusChange_t,
+ MPI26_EVENT_DATA_ENCL_DEV_STATUS_CHANGE,
+ *PTR_MPI26_EVENT_DATA_ENCL_DEV_STATUS_CHANGE,
+ Mpi26EventDataEnclDevStatusChange_t,
+ *pMpi26EventDataEnclDevStatusChange_t;
+
+/*SAS Enclosure Device Status Change event ReasonCode values */
+#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01)
+#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02)
+
+/*Enclosure Device Status Change event ReasonCode values */
+#define MPI26_EVENT_ENCL_RC_ADDED (0x01)
+#define MPI26_EVENT_ENCL_RC_NOT_RESPONDING (0x02)
+
+
+typedef struct _MPI25_EVENT_DATA_SAS_DEVICE_DISCOVERY_ERROR {
+ U16 DevHandle; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U32 Reserved1[2]; /*0x04 */
+ U64 SASAddress; /*0x0C */
+ U32 Reserved2[2]; /*0x14 */
+} MPI25_EVENT_DATA_SAS_DEVICE_DISCOVERY_ERROR,
+ *PTR_MPI25_EVENT_DATA_SAS_DEVICE_DISCOVERY_ERROR,
+ Mpi25EventDataSasDeviceDiscoveryError_t,
+ *pMpi25EventDataSasDeviceDiscoveryError_t;
+
+/*SAS Device Discovery Error Event data ReasonCode values */
+#define MPI25_EVENT_SAS_DISC_ERR_SMP_FAILED (0x01)
+#define MPI25_EVENT_SAS_DISC_ERR_SMP_TIMEOUT (0x02)
+
+/*SAS PHY Counter Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER {
+ U64 TimeStamp; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U8 PhyEventCode; /*0x0C */
+ U8 PhyNum; /*0x0D */
+ U16 Reserved2; /*0x0E */
+ U32 PhyEventInfo; /*0x10 */
+ U8 CounterType; /*0x14 */
+ U8 ThresholdWindow; /*0x15 */
+ U8 TimeUnits; /*0x16 */
+ U8 Reserved3; /*0x17 */
+ U32 EventThreshold; /*0x18 */
+ U16 ThresholdFlags; /*0x1C */
+ U16 Reserved4; /*0x1E */
+} MPI2_EVENT_DATA_SAS_PHY_COUNTER,
+ *PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER,
+ Mpi2EventDataSasPhyCounter_t,
+ *pMpi2EventDataSasPhyCounter_t;
+
+/*use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h
+ *for the PhyEventCode field */
+
+/*use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h
+ *for the CounterType field */
+
+/*use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h
+ *for the TimeUnits field */
+
+/*use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h
+ *for the ThresholdFlags field */
+
+/*SAS Quiesce Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE {
+ U8 ReasonCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Reserved3; /*0x04 */
+} MPI2_EVENT_DATA_SAS_QUIESCE,
+ *PTR_MPI2_EVENT_DATA_SAS_QUIESCE,
+ Mpi2EventDataSasQuiesce_t, *pMpi2EventDataSasQuiesce_t;
+
+/*SAS Quiesce Event data ReasonCode values */
+#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01)
+#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02)
+
+/*Host Based Discovery Phy Event data */
+
+typedef struct _MPI2_EVENT_HBD_PHY_SAS {
+ U8 Flags; /*0x00 */
+ U8 NegotiatedLinkRate; /*0x01 */
+ U8 PhyNum; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U32 Reserved1; /*0x04 */
+ U8 InitialFrame[28]; /*0x08 */
+} MPI2_EVENT_HBD_PHY_SAS, *PTR_MPI2_EVENT_HBD_PHY_SAS,
+ Mpi2EventHbdPhySas_t, *pMpi2EventHbdPhySas_t;
+
+/*values for the Flags field */
+#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02)
+#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01)
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h
+ *for the NegotiatedLinkRate field */
+
+typedef union _MPI2_EVENT_HBD_DESCRIPTOR {
+ MPI2_EVENT_HBD_PHY_SAS Sas;
+} MPI2_EVENT_HBD_DESCRIPTOR, *PTR_MPI2_EVENT_HBD_DESCRIPTOR,
+ Mpi2EventHbdDescriptor_t, *pMpi2EventHbdDescriptor_t;
+
+typedef struct _MPI2_EVENT_DATA_HBD_PHY {
+ U8 DescriptorType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Reserved3; /*0x04 */
+ MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /*0x08 */
+} MPI2_EVENT_DATA_HBD_PHY, *PTR_MPI2_EVENT_DATA_HBD_PHY,
+ Mpi2EventDataHbdPhy_t,
+ *pMpi2EventDataMpi2EventDataHbdPhy_t;
+
+/*values for the DescriptorType field */
+#define MPI2_EVENT_HBD_DT_SAS (0x01)
+
+
+/*PCIe Device Status Change Event data (MPI v2.6 and later) */
+
+typedef struct _MPI26_EVENT_DATA_PCIE_DEVICE_STATUS_CHANGE {
+ U16 TaskTag; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U8 ASC; /*0x04 */
+ U8 ASCQ; /*0x05 */
+ U16 DevHandle; /*0x06 */
+ U32 Reserved2; /*0x08 */
+ U64 WWID; /*0x0C */
+ U8 LUN[8]; /*0x14 */
+} MPI26_EVENT_DATA_PCIE_DEVICE_STATUS_CHANGE,
+ *PTR_MPI26_EVENT_DATA_PCIE_DEVICE_STATUS_CHANGE,
+ Mpi26EventDataPCIeDeviceStatusChange_t,
+ *pMpi26EventDataPCIeDeviceStatusChange_t;
+
+/*PCIe Device Status Change Event data ReasonCode values */
+#define MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA (0x05)
+#define MPI26_EVENT_PCIDEV_STAT_RC_UNSUPPORTED (0x07)
+#define MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08)
+#define MPI26_EVENT_PCIDEV_STAT_RC_TASK_ABORT_INTERNAL (0x09)
+#define MPI26_EVENT_PCIDEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A)
+#define MPI26_EVENT_PCIDEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B)
+#define MPI26_EVENT_PCIDEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C)
+#define MPI26_EVENT_PCIDEV_STAT_RC_ASYNC_NOTIFICATION (0x0D)
+#define MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E)
+#define MPI26_EVENT_PCIDEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F)
+#define MPI26_EVENT_PCIDEV_STAT_RC_DEV_INIT_FAILURE (0x10)
+#define MPI26_EVENT_PCIDEV_STAT_RC_PCIE_HOT_RESET_FAILED (0x11)
+
+
+/*PCIe Enumeration Event data (MPI v2.6 and later) */
+
+typedef struct _MPI26_EVENT_DATA_PCIE_ENUMERATION {
+ U8 Flags; /*0x00 */
+ U8 ReasonCode; /*0x01 */
+ U8 PhysicalPort; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 EnumerationStatus; /*0x04 */
+} MPI26_EVENT_DATA_PCIE_ENUMERATION,
+ *PTR_MPI26_EVENT_DATA_PCIE_ENUMERATION,
+ Mpi26EventDataPCIeEnumeration_t,
+ *pMpi26EventDataPCIeEnumeration_t;
+
+/*PCIe Enumeration Event data Flags values */
+#define MPI26_EVENT_PCIE_ENUM_DEVICE_CHANGE (0x02)
+#define MPI26_EVENT_PCIE_ENUM_IN_PROGRESS (0x01)
+
+/*PCIe Enumeration Event data ReasonCode values */
+#define MPI26_EVENT_PCIE_ENUM_RC_STARTED (0x01)
+#define MPI26_EVENT_PCIE_ENUM_RC_COMPLETED (0x02)
+
+/*PCIe Enumeration Event data EnumerationStatus values */
+#define MPI26_EVENT_PCIE_ENUM_ES_MAX_SWITCHES_EXCEED (0x40000000)
+#define MPI26_EVENT_PCIE_ENUM_ES_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI26_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED (0x10000000)
+
+
+/*PCIe Topology Change List Event data (MPI v2.6 and later) */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should check NumEntries at
+ *runtime before using PortEntry[].
+ */
+
+typedef struct _MPI26_EVENT_PCIE_TOPO_PORT_ENTRY {
+ U16 AttachedDevHandle; /*0x00 */
+ U8 PortStatus; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U8 CurrentPortInfo; /*0x04 */
+ U8 Reserved2; /*0x05 */
+ U8 PreviousPortInfo; /*0x06 */
+ U8 Reserved3; /*0x07 */
+} MPI26_EVENT_PCIE_TOPO_PORT_ENTRY,
+ *PTR_MPI26_EVENT_PCIE_TOPO_PORT_ENTRY,
+ Mpi26EventPCIeTopoPortEntry_t,
+ *pMpi26EventPCIeTopoPortEntry_t;
+
+/*PCIe Topology Change List Event data PortStatus values */
+#define MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED (0x01)
+#define MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING (0x02)
+#define MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED (0x03)
+#define MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE (0x04)
+#define MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING (0x05)
+
+/*PCIe Topology Change List Event data defines for CurrentPortInfo and
+ *PreviousPortInfo
+ */
+#define MPI26_EVENT_PCIE_TOPO_PI_LANE_MASK (0xF0)
+#define MPI26_EVENT_PCIE_TOPO_PI_LANES_UNKNOWN (0x00)
+#define MPI26_EVENT_PCIE_TOPO_PI_1_LANE (0x10)
+#define MPI26_EVENT_PCIE_TOPO_PI_2_LANES (0x20)
+#define MPI26_EVENT_PCIE_TOPO_PI_4_LANES (0x30)
+#define MPI26_EVENT_PCIE_TOPO_PI_8_LANES (0x40)
+#define MPI26_EVENT_PCIE_TOPO_PI_16_LANES (0x50)
+
+#define MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK (0x0F)
+#define MPI26_EVENT_PCIE_TOPO_PI_RATE_UNKNOWN (0x00)
+#define MPI26_EVENT_PCIE_TOPO_PI_RATE_DISABLED (0x01)
+#define MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5 (0x02)
+#define MPI26_EVENT_PCIE_TOPO_PI_RATE_5_0 (0x03)
+#define MPI26_EVENT_PCIE_TOPO_PI_RATE_8_0 (0x04)
+#define MPI26_EVENT_PCIE_TOPO_PI_RATE_16_0 (0x05)
+
+typedef struct _MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST {
+ U16 EnclosureHandle; /*0x00 */
+ U16 SwitchDevHandle; /*0x02 */
+ U8 NumPorts; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U8 NumEntries; /*0x08 */
+ U8 StartPortNum; /*0x09 */
+ U8 SwitchStatus; /*0x0A */
+ U8 PhysicalPort; /*0x0B */
+ MPI26_EVENT_PCIE_TOPO_PORT_ENTRY
+ PortEntry[]; /*0x0C */
+} MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST,
+ *PTR_MPI26_EVENT_DATA_PCIE_TOPOLOGY_CHANGE_LIST,
+ Mpi26EventDataPCIeTopologyChangeList_t,
+ *pMpi26EventDataPCIeTopologyChangeList_t;
+
+/*PCIe Topology Change List Event data SwitchStatus values */
+#define MPI26_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH (0x00)
+#define MPI26_EVENT_PCIE_TOPO_SS_ADDED (0x01)
+#define MPI26_EVENT_PCIE_TOPO_SS_NOT_RESPONDING (0x02)
+#define MPI26_EVENT_PCIE_TOPO_SS_RESPONDING (0x03)
+#define MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING (0x04)
+
+/*PCIe Link Counter Event data (MPI v2.6 and later) */
+
+typedef struct _MPI26_EVENT_DATA_PCIE_LINK_COUNTER {
+ U64 TimeStamp; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U8 LinkEventCode; /*0x0C */
+ U8 LinkNum; /*0x0D */
+ U16 Reserved2; /*0x0E */
+ U32 LinkEventInfo; /*0x10 */
+ U8 CounterType; /*0x14 */
+ U8 ThresholdWindow; /*0x15 */
+ U8 TimeUnits; /*0x16 */
+ U8 Reserved3; /*0x17 */
+ U32 EventThreshold; /*0x18 */
+ U16 ThresholdFlags; /*0x1C */
+ U16 Reserved4; /*0x1E */
+} MPI26_EVENT_DATA_PCIE_LINK_COUNTER,
+ *PTR_MPI26_EVENT_DATA_PCIE_LINK_COUNTER,
+ Mpi26EventDataPcieLinkCounter_t, *pMpi26EventDataPcieLinkCounter_t;
+
+
+/*use MPI26_PCIELINK3_EVTCODE_ values from mpi2_cnfg.h for the LinkEventCode
+ *field
+ */
+
+/*use MPI26_PCIELINK3_COUNTER_TYPE_ values from mpi2_cnfg.h for the CounterType
+ *field
+ */
+
+/*use MPI26_PCIELINK3_TIME_UNITS_ values from mpi2_cnfg.h for the TimeUnits
+ *field
+ */
+
+/*use MPI26_PCIELINK3_TFLAGS_ values from mpi2_cnfg.h for the ThresholdFlags
+ *field
+ */
+
+/****************************************************************************
+* EventAck message
+****************************************************************************/
+
+/*EventAck Request message */
+typedef struct _MPI2_EVENT_ACK_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Event; /*0x0C */
+ U16 Reserved5; /*0x0E */
+ U32 EventContext; /*0x10 */
+} MPI2_EVENT_ACK_REQUEST, *PTR_MPI2_EVENT_ACK_REQUEST,
+ Mpi2EventAckRequest_t, *pMpi2EventAckRequest_t;
+
+/*EventAck Reply message */
+typedef struct _MPI2_EVENT_ACK_REPLY {
+ U16 Reserved1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_EVENT_ACK_REPLY, *PTR_MPI2_EVENT_ACK_REPLY,
+ Mpi2EventAckReply_t, *pMpi2EventAckReply_t;
+
+/****************************************************************************
+* SendHostMessage message
+****************************************************************************/
+
+/*SendHostMessage Request message */
+typedef struct _MPI2_SEND_HOST_MESSAGE_REQUEST {
+ U16 HostDataLength; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U8 Reserved4; /*0x0C */
+ U8 DestVF_ID; /*0x0D */
+ U16 Reserved5; /*0x0E */
+ U32 Reserved6; /*0x10 */
+ U32 Reserved7; /*0x14 */
+ U32 Reserved8; /*0x18 */
+ U32 Reserved9; /*0x1C */
+ U32 Reserved10; /*0x20 */
+ U32 HostData[]; /*0x24 */
+} MPI2_SEND_HOST_MESSAGE_REQUEST,
+ *PTR_MPI2_SEND_HOST_MESSAGE_REQUEST,
+ Mpi2SendHostMessageRequest_t,
+ *pMpi2SendHostMessageRequest_t;
+
+/*SendHostMessage Reply message */
+typedef struct _MPI2_SEND_HOST_MESSAGE_REPLY {
+ U16 HostDataLength; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_SEND_HOST_MESSAGE_REPLY, *PTR_MPI2_SEND_HOST_MESSAGE_REPLY,
+ Mpi2SendHostMessageReply_t, *pMpi2SendHostMessageReply_t;
+
+/****************************************************************************
+* FWDownload message
+****************************************************************************/
+
+/*MPI v2.0 FWDownload Request message */
+typedef struct _MPI2_FW_DOWNLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 TotalImageSize; /*0x0C */
+ U32 Reserved5; /*0x10 */
+ MPI2_MPI_SGE_UNION SGL; /*0x14 */
+} MPI2_FW_DOWNLOAD_REQUEST, *PTR_MPI2_FW_DOWNLOAD_REQUEST,
+ Mpi2FWDownloadRequest, *pMpi2FWDownloadRequest;
+
+#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01)
+
+#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01)
+#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02)
+#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06)
+#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07)
+#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08)
+#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09)
+#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A)
+#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
+#define MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY (0x0C)
+#define MPI2_FW_DOWNLOAD_ITYPE_CBB_BACKUP (0x0D)
+#define MPI2_FW_DOWNLOAD_ITYPE_SBR (0x0E)
+#define MPI2_FW_DOWNLOAD_ITYPE_SBR_BACKUP (0x0F)
+#define MPI2_FW_DOWNLOAD_ITYPE_HIIM (0x10)
+#define MPI2_FW_DOWNLOAD_ITYPE_HIIA (0x11)
+#define MPI2_FW_DOWNLOAD_ITYPE_CTLR (0x12)
+#define MPI2_FW_DOWNLOAD_ITYPE_IMR_FIRMWARE (0x13)
+#define MPI2_FW_DOWNLOAD_ITYPE_MR_NVDATA (0x14)
+/*MPI v2.6 and newer */
+#define MPI2_FW_DOWNLOAD_ITYPE_CPLD (0x15)
+#define MPI2_FW_DOWNLOAD_ITYPE_PSOC (0x16)
+#define MPI2_FW_DOWNLOAD_ITYPE_COREDUMP (0x17)
+#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0)
+
+/*MPI v2.0 FWDownload TransactionContext Element */
+typedef struct _MPI2_FW_DOWNLOAD_TCSGE {
+ U8 Reserved1; /*0x00 */
+ U8 ContextSize; /*0x01 */
+ U8 DetailsLength; /*0x02 */
+ U8 Flags; /*0x03 */
+ U32 Reserved2; /*0x04 */
+ U32 ImageOffset; /*0x08 */
+ U32 ImageSize; /*0x0C */
+} MPI2_FW_DOWNLOAD_TCSGE, *PTR_MPI2_FW_DOWNLOAD_TCSGE,
+ Mpi2FWDownloadTCSGE_t, *pMpi2FWDownloadTCSGE_t;
+
+/*MPI v2.5 FWDownload Request message */
+typedef struct _MPI25_FW_DOWNLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 TotalImageSize; /*0x0C */
+ U32 Reserved5; /*0x10 */
+ U32 Reserved6; /*0x14 */
+ U32 ImageOffset; /*0x18 */
+ U32 ImageSize; /*0x1C */
+ MPI25_SGE_IO_UNION SGL; /*0x20 */
+} MPI25_FW_DOWNLOAD_REQUEST, *PTR_MPI25_FW_DOWNLOAD_REQUEST,
+ Mpi25FWDownloadRequest, *pMpi25FWDownloadRequest;
+
+/*FWDownload Reply message */
+typedef struct _MPI2_FW_DOWNLOAD_REPLY {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_FW_DOWNLOAD_REPLY, *PTR_MPI2_FW_DOWNLOAD_REPLY,
+ Mpi2FWDownloadReply_t, *pMpi2FWDownloadReply_t;
+
+/****************************************************************************
+* FWUpload message
+****************************************************************************/
+
+/*MPI v2.0 FWUpload Request message */
+typedef struct _MPI2_FW_UPLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ MPI2_MPI_SGE_UNION SGL; /*0x14 */
+} MPI2_FW_UPLOAD_REQUEST, *PTR_MPI2_FW_UPLOAD_REQUEST,
+ Mpi2FWUploadRequest_t, *pMpi2FWUploadRequest_t;
+
+#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00)
+#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01)
+#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02)
+#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05)
+#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06)
+#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07)
+#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08)
+#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09)
+#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A)
+#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
+#define MPI2_FW_UPLOAD_ITYPE_CBB_BACKUP (0x0D)
+#define MPI2_FW_UPLOAD_ITYPE_SBR (0x0E)
+#define MPI2_FW_UPLOAD_ITYPE_SBR_BACKUP (0x0F)
+#define MPI2_FW_UPLOAD_ITYPE_HIIM (0x10)
+#define MPI2_FW_UPLOAD_ITYPE_HIIA (0x11)
+#define MPI2_FW_UPLOAD_ITYPE_CTLR (0x12)
+#define MPI2_FW_UPLOAD_ITYPE_IMR_FIRMWARE (0x13)
+#define MPI2_FW_UPLOAD_ITYPE_MR_NVDATA (0x14)
+
+
+/*MPI v2.0 FWUpload TransactionContext Element */
+typedef struct _MPI2_FW_UPLOAD_TCSGE {
+ U8 Reserved1; /*0x00 */
+ U8 ContextSize; /*0x01 */
+ U8 DetailsLength; /*0x02 */
+ U8 Flags; /*0x03 */
+ U32 Reserved2; /*0x04 */
+ U32 ImageOffset; /*0x08 */
+ U32 ImageSize; /*0x0C */
+} MPI2_FW_UPLOAD_TCSGE, *PTR_MPI2_FW_UPLOAD_TCSGE,
+ Mpi2FWUploadTCSGE_t, *pMpi2FWUploadTCSGE_t;
+
+/*MPI v2.5 FWUpload Request message */
+typedef struct _MPI25_FW_UPLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ U32 Reserved7; /*0x14 */
+ U32 ImageOffset; /*0x18 */
+ U32 ImageSize; /*0x1C */
+ MPI25_SGE_IO_UNION SGL; /*0x20 */
+} MPI25_FW_UPLOAD_REQUEST, *PTR_MPI25_FW_UPLOAD_REQUEST,
+ Mpi25FWUploadRequest_t, *pMpi25FWUploadRequest_t;
+
+/*FWUpload Reply message */
+typedef struct _MPI2_FW_UPLOAD_REPLY {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 ActualImageSize; /*0x14 */
+} MPI2_FW_UPLOAD_REPLY, *PTR_MPI2_FW_UPLOAD_REPLY,
+ Mpi2FWUploadReply_t, *pMPi2FWUploadReply_t;
+
+
+/****************************************************************************
+* PowerManagementControl message
+****************************************************************************/
+
+/*PowerManagementControl Request message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST {
+ U8 Feature; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 Parameter1; /*0x0C */
+ U8 Parameter2; /*0x0D */
+ U8 Parameter3; /*0x0E */
+ U8 Parameter4; /*0x0F */
+ U32 Reserved5; /*0x10 */
+ U32 Reserved6; /*0x14 */
+} MPI2_PWR_MGMT_CONTROL_REQUEST, *PTR_MPI2_PWR_MGMT_CONTROL_REQUEST,
+ Mpi2PwrMgmtControlRequest_t, *pMpi2PwrMgmtControlRequest_t;
+
+/*defines for the Feature field */
+#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01)
+#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02)
+#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03) /*obsolete */
+#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04)
+#define MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE (0x05)
+#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF)
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */
+/*Parameter1 contains a PHY number */
+/*Parameter2 indicates power condition action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01)
+#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02)
+#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03)
+/*Parameter3 and Parameter4 are reserved */
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION
+ * Feature */
+/*Parameter1 contains SAS port width modulation group number */
+/*Parameter2 indicates IOC action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01)
+#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02)
+#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03)
+/*Parameter3 indicates desired modulation level using these defines */
+#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00)
+#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01)
+#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02)
+#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03)
+/*Parameter4 is reserved */
+
+/*this next set (_PCIE_LINK) is obsolete */
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */
+/*Parameter1 indicates desired PCIe link speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02) /*obsolete */
+/*Parameter2 indicates desired PCIe link width using these defines */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08) /*obsolete */
+/*Parameter3 and Parameter4 are reserved */
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */
+/*Parameter1 indicates desired IOC hardware clock speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01)
+#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02)
+#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04)
+#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08)
+/*Parameter2, Parameter3, and Parameter4 are reserved */
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE Feature*/
+/*Parameter1 indicates host action regarding global power management mode */
+#define MPI2_PM_CONTROL_PARAM1_TAKE_CONTROL (0x01)
+#define MPI2_PM_CONTROL_PARAM1_CHANGE_GLOBAL_MODE (0x02)
+#define MPI2_PM_CONTROL_PARAM1_RELEASE_CONTROL (0x03)
+/*Parameter2 indicates the requested global power management mode */
+#define MPI2_PM_CONTROL_PARAM2_FULL_PWR_PERF (0x01)
+#define MPI2_PM_CONTROL_PARAM2_REDUCED_PWR_PERF (0x08)
+#define MPI2_PM_CONTROL_PARAM2_STANDBY (0x40)
+/*Parameter3 and Parameter4 are reserved */
+
+/*PowerManagementControl Reply message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY {
+ U8 Feature; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_PWR_MGMT_CONTROL_REPLY, *PTR_MPI2_PWR_MGMT_CONTROL_REPLY,
+ Mpi2PwrMgmtControlReply_t, *pMpi2PwrMgmtControlReply_t;
+
+/****************************************************************************
+* IO Unit Control messages (MPI v2.6 and later only.)
+****************************************************************************/
+
+/* IO Unit Control Request Message */
+typedef struct _MPI26_IOUNIT_CONTROL_REQUEST {
+ U8 Operation; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 DevHandle; /* 0x04 */
+ U8 IOCParameter; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U16 Reserved4; /* 0x0C */
+ U8 PhyNum; /* 0x0E */
+ U8 PrimFlags; /* 0x0F */
+ U32 Primitive; /* 0x10 */
+ U8 LookupMethod; /* 0x14 */
+ U8 Reserved5; /* 0x15 */
+ U16 SlotNumber; /* 0x16 */
+ U64 LookupAddress; /* 0x18 */
+ U32 IOCParameterValue; /* 0x20 */
+ U32 Reserved7; /* 0x24 */
+ U32 Reserved8; /* 0x28 */
+} MPI26_IOUNIT_CONTROL_REQUEST,
+ *PTR_MPI26_IOUNIT_CONTROL_REQUEST,
+ Mpi26IoUnitControlRequest_t,
+ *pMpi26IoUnitControlRequest_t;
+
+/* values for the Operation field */
+#define MPI26_CTRL_OP_CLEAR_ALL_PERSISTENT (0x02)
+#define MPI26_CTRL_OP_SAS_PHY_LINK_RESET (0x06)
+#define MPI26_CTRL_OP_SAS_PHY_HARD_RESET (0x07)
+#define MPI26_CTRL_OP_PHY_CLEAR_ERROR_LOG (0x08)
+#define MPI26_CTRL_OP_LINK_CLEAR_ERROR_LOG (0x09)
+#define MPI26_CTRL_OP_SAS_SEND_PRIMITIVE (0x0A)
+#define MPI26_CTRL_OP_FORCE_FULL_DISCOVERY (0x0B)
+#define MPI26_CTRL_OP_REMOVE_DEVICE (0x0D)
+#define MPI26_CTRL_OP_LOOKUP_MAPPING (0x0E)
+#define MPI26_CTRL_OP_SET_IOC_PARAMETER (0x0F)
+#define MPI26_CTRL_OP_ENABLE_FP_DEVICE (0x10)
+#define MPI26_CTRL_OP_DISABLE_FP_DEVICE (0x11)
+#define MPI26_CTRL_OP_ENABLE_FP_ALL (0x12)
+#define MPI26_CTRL_OP_DISABLE_FP_ALL (0x13)
+#define MPI26_CTRL_OP_DEV_ENABLE_NCQ (0x14)
+#define MPI26_CTRL_OP_DEV_DISABLE_NCQ (0x15)
+#define MPI26_CTRL_OP_SHUTDOWN (0x16)
+#define MPI26_CTRL_OP_DEV_ENABLE_PERSIST_CONNECTION (0x17)
+#define MPI26_CTRL_OP_DEV_DISABLE_PERSIST_CONNECTION (0x18)
+#define MPI26_CTRL_OP_DEV_CLOSE_PERSIST_CONNECTION (0x19)
+#define MPI26_CTRL_OP_ENABLE_NVME_SGL_FORMAT (0x1A)
+#define MPI26_CTRL_OP_DISABLE_NVME_SGL_FORMAT (0x1B)
+#define MPI26_CTRL_OP_PRODUCT_SPECIFIC_MIN (0x80)
+
+/* values for the PrimFlags field */
+#define MPI26_CTRL_PRIMFLAGS_SINGLE (0x08)
+#define MPI26_CTRL_PRIMFLAGS_TRIPLE (0x02)
+#define MPI26_CTRL_PRIMFLAGS_REDUNDANT (0x01)
+
+/* values for the LookupMethod field */
+#define MPI26_CTRL_LOOKUP_METHOD_WWID_ADDRESS (0x01)
+#define MPI26_CTRL_LOOKUP_METHOD_ENCLOSURE_SLOT (0x02)
+#define MPI26_CTRL_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03)
+
+
+/* IO Unit Control Reply Message */
+typedef struct _MPI26_IOUNIT_CONTROL_REPLY {
+ U8 Operation; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 DevHandle; /* 0x04 */
+ U8 IOCParameter; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U16 Reserved4; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+} MPI26_IOUNIT_CONTROL_REPLY,
+ *PTR_MPI26_IOUNIT_CONTROL_REPLY,
+ Mpi26IoUnitControlReply_t,
+ *pMpi26IoUnitControlReply_t;
+
+/****************************************************************************
+ * MCTP Passthrough messages (MPI v2.6 and later only.)
+ ****************************************************************************/
+
+/* MCTP Passthrough Request Message */
+typedef struct _MPI26_MCTP_PASSTHROUGH_REQUEST {
+ U8 MsgContext; /* 0x00 */
+ U8 Reserved1[2]; /* 0x01 */
+ U8 Function; /* 0x03 */
+ U8 Reserved2[3]; /* 0x04 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U32 Reserved4; /* 0x0C */
+ U8 Flags; /* 0x10 */
+ U8 Reserved5[3]; /* 0x11 */
+ U32 Reserved6; /* 0x14 */
+ U32 H2DLength; /* 0x18 */
+ U32 D2HLength; /* 0x1C */
+ MPI25_SGE_IO_UNION H2DSGL; /* 0x20 */
+ MPI25_SGE_IO_UNION D2HSGL; /* 0x30 */
+} MPI26_MCTP_PASSTHROUGH_REQUEST,
+ *PTR_MPI26_MCTP_PASSTHROUGH_REQUEST,
+ Mpi26MctpPassthroughRequest_t,
+ *pMpi26MctpPassthroughRequest_t;
+
+/* values for the MsgContext field */
+#define MPI26_MCTP_MSG_CONEXT_UNUSED (0x00)
+
+/* values for the Flags field */
+#define MPI26_MCTP_FLAGS_MSG_FORMAT_MPT (0x01)
+
+/* MCTP Passthrough Reply Message */
+typedef struct _MPI26_MCTP_PASSTHROUGH_REPLY {
+ U8 MsgContext; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U8 Reserved2[3]; /* 0x04 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved3; /* 0x0A */
+ U16 Reserved4; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U32 ResponseDataLength; /* 0x14 */
+} MPI26_MCTP_PASSTHROUGH_REPLY,
+ *PTR_MPI26_MCTP_PASSTHROUGH_REPLY,
+ Mpi26MctpPassthroughReply_t,
+ *pMpi26MctpPassthroughReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_pci.h b/drivers/scsi/mpt3sas/mpi/mpi2_pci.h
new file mode 100644
index 000000000000..bb7b79cfa558
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_pci.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2_pci.h
+ * Title: MPI PCIe Attached Devices structures and definitions.
+ * Creation Date: October 9, 2012
+ *
+ * mpi2_pci.h Version: 02.00.04
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 03-16-15 02.00.00 Initial version.
+ * 02-17-16 02.00.01 Removed AHCI support.
+ * Removed SOP support.
+ * 07-01-16 02.00.02 Added MPI26_NVME_FLAGS_FORCE_ADMIN_ERR_RESP to
+ * NVME Encapsulated Request.
+ * 07-22-18 02.00.03 Updted flags field for NVME Encapsulated req
+ * 12-17-18 02.00.04 Added MPI26_PCIE_DEVINFO_SCSI
+ * Shortten some defines to be compatible with DOS
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_PCI_H
+#define MPI2_PCI_H
+
+
+/*
+ *Values for the PCIe DeviceInfo field used in PCIe Device Status Change Event
+ *data and PCIe Configuration pages.
+ */
+#define MPI26_PCIE_DEVINFO_DIRECT_ATTACH (0x00000010)
+
+#define MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE (0x0000000F)
+#define MPI26_PCIE_DEVINFO_NO_DEVICE (0x00000000)
+#define MPI26_PCIE_DEVINFO_PCI_SWITCH (0x00000001)
+#define MPI26_PCIE_DEVINFO_NVME (0x00000003)
+#define MPI26_PCIE_DEVINFO_SCSI (0x00000004)
+
+/****************************************************************************
+* NVMe Encapsulated message
+****************************************************************************/
+
+/*NVME Encapsulated Request Message */
+typedef struct _MPI26_NVME_ENCAPSULATED_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 EncapsulatedCommandLength; /*0x04 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 Reserved3; /*0x0C */
+ U64 ErrorResponseBaseAddress; /*0x10 */
+ U16 ErrorResponseAllocationLength; /*0x18 */
+ U16 Flags; /*0x1A */
+ U32 DataLength; /*0x1C */
+ U8 NVMe_Command[4]; /*0x20 */
+
+} MPI26_NVME_ENCAPSULATED_REQUEST, *PTR_MPI26_NVME_ENCAPSULATED_REQUEST,
+ Mpi26NVMeEncapsulatedRequest_t, *pMpi26NVMeEncapsulatedRequest_t;
+
+/*defines for the Flags field */
+#define MPI26_NVME_FLAGS_FORCE_ADMIN_ERR_RESP (0x0020)
+/*Submission Queue Type*/
+#define MPI26_NVME_FLAGS_SUBMISSIONQ_MASK (0x0010)
+#define MPI26_NVME_FLAGS_SUBMISSIONQ_IO (0x0000)
+#define MPI26_NVME_FLAGS_SUBMISSIONQ_ADMIN (0x0010)
+/*Error Response Address Space */
+#define MPI26_NVME_FLAGS_ERR_RSP_ADDR_MASK (0x000C)
+#define MPI26_NVME_FLAGS_ERR_RSP_ADDR_SYSTEM (0x0000)
+#define MPI26_NVME_FLAGS_ERR_RSP_ADDR_IOCTL (0x0008)
+/* Data Direction*/
+#define MPI26_NVME_FLAGS_DATADIRECTION_MASK (0x0003)
+#define MPI26_NVME_FLAGS_NODATATRANSFER (0x0000)
+#define MPI26_NVME_FLAGS_WRITE (0x0001)
+#define MPI26_NVME_FLAGS_READ (0x0002)
+#define MPI26_NVME_FLAGS_BIDIRECTIONAL (0x0003)
+
+
+/*NVMe Encapuslated Reply Message */
+typedef struct _MPI26_NVME_ENCAPSULATED_ERROR_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 EncapsulatedCommandLength; /*0x04 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U16 ErrorResponseCount; /*0x14 */
+ U16 Reserved4; /*0x16 */
+} MPI26_NVME_ENCAPSULATED_ERROR_REPLY,
+ *PTR_MPI26_NVME_ENCAPSULATED_ERROR_REPLY,
+ Mpi26NVMeEncapsulatedErrorReply_t,
+ *pMpi26NVMeEncapsulatedErrorReply_t;
+
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_raid.h b/drivers/scsi/mpt3sas/mpi/mpi2_raid.h
new file mode 100644
index 000000000000..b770eb516c14
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_raid.h
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2_raid.h
+ * Title: MPI Integrated RAID messages and structures
+ * Creation Date: April 26, 2007
+ *
+ * mpi2_raid.h Version: 02.00.11
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 08-31-07 02.00.01 Modifications to RAID Action request and reply,
+ * including the Actions and ActionData.
+ * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD.
+ * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that
+ * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT
+ * can be sized by the build environment.
+ * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of
+ * VolumeCreationFlags and marked the old one as obsolete.
+ * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define.
+ * 08-24-10 02.00.06 Added MPI2_RAID_ACTION_COMPATIBILITY_CHECK along with
+ * related structures and defines.
+ * Added product-specific range to RAID Action values.
+ * 11-18-11 02.00.07 Incorporating additions for MPI v2.5.
+ * 02-06-12 02.00.08 Added MPI2_RAID_ACTION_PHYSDISK_HIDDEN.
+ * 07-26-12 02.00.09 Added ElapsedSeconds field to MPI2_RAID_VOL_INDICATOR.
+ * Added MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID define.
+ * 04-17-13 02.00.10 Added MPI25_RAID_ACTION_ADATA_ALLOW_PI.
+ * 11-18-14 02.00.11 Updated copyright information.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_RAID_H
+#define MPI2_RAID_H
+
+/*****************************************************************************
+*
+* Integrated RAID Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* RAID Action messages
+****************************************************************************/
+
+/* ActionDataWord defines for use with MPI2_RAID_ACTION_CREATE_VOLUME action */
+#define MPI25_RAID_ACTION_ADATA_ALLOW_PI (0x80000000)
+
+/*ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */
+#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000)
+#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001)
+
+/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for
+ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
+
+/*ActionDataWord defines for use with
+ *MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */
+#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001)
+
+/*ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */
+typedef struct _MPI2_RAID_ACTION_RATE_DATA {
+ U8 RateToChange; /*0x00 */
+ U8 RateOrMode; /*0x01 */
+ U16 DataScrubDuration; /*0x02 */
+} MPI2_RAID_ACTION_RATE_DATA, *PTR_MPI2_RAID_ACTION_RATE_DATA,
+ Mpi2RaidActionRateData_t, *pMpi2RaidActionRateData_t;
+
+#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00)
+#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01)
+#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02)
+
+/*ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */
+typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION {
+ U8 RAIDFunction; /*0x00 */
+ U8 Flags; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_RAID_ACTION_START_RAID_FUNCTION,
+ *PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION,
+ Mpi2RaidActionStartRaidFunction_t,
+ *pMpi2RaidActionStartRaidFunction_t;
+
+/*defines for the RAIDFunction field */
+#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00)
+#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02)
+
+/*defines for the Flags field */
+#define MPI2_RAID_ACTION_START_NEW (0x00)
+#define MPI2_RAID_ACTION_START_RESUME (0x01)
+
+/*ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */
+typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION {
+ U8 RAIDFunction; /*0x00 */
+ U8 Flags; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
+ *PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
+ Mpi2RaidActionStopRaidFunction_t,
+ *pMpi2RaidActionStopRaidFunction_t;
+
+/*defines for the RAIDFunction field */
+#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00)
+#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02)
+
+/*defines for the Flags field */
+#define MPI2_RAID_ACTION_STOP_ABORT (0x00)
+#define MPI2_RAID_ACTION_STOP_PAUSE (0x01)
+
+/*ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */
+typedef struct _MPI2_RAID_ACTION_HOT_SPARE {
+ U8 HotSparePool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 DevHandle; /*0x02 */
+} MPI2_RAID_ACTION_HOT_SPARE, *PTR_MPI2_RAID_ACTION_HOT_SPARE,
+ Mpi2RaidActionHotSpare_t, *pMpi2RaidActionHotSpare_t;
+
+/*ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */
+typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE {
+ U8 Flags; /*0x00 */
+ U8 DeviceFirmwareUpdateModeTimeout; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_RAID_ACTION_FW_UPDATE_MODE,
+ *PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE,
+ Mpi2RaidActionFwUpdateMode_t,
+ *pMpi2RaidActionFwUpdateMode_t;
+
+/*ActionDataWord defines for use with
+ *MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */
+#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00)
+#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01)
+
+typedef union _MPI2_RAID_ACTION_DATA {
+ U32 Word;
+ MPI2_RAID_ACTION_RATE_DATA Rates;
+ MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction;
+ MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction;
+ MPI2_RAID_ACTION_HOT_SPARE HotSpare;
+ MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode;
+} MPI2_RAID_ACTION_DATA, *PTR_MPI2_RAID_ACTION_DATA,
+ Mpi2RaidActionData_t, *pMpi2RaidActionData_t;
+
+/*RAID Action Request Message */
+typedef struct _MPI2_RAID_ACTION_REQUEST {
+ U8 Action; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 VolDevHandle; /*0x04 */
+ U8 PhysDiskNum; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 Reserved3; /*0x0C */
+ MPI2_RAID_ACTION_DATA ActionDataWord; /*0x10 */
+ MPI2_SGE_SIMPLE_UNION ActionDataSGE; /*0x14 */
+} MPI2_RAID_ACTION_REQUEST, *PTR_MPI2_RAID_ACTION_REQUEST,
+ Mpi2RaidActionRequest_t, *pMpi2RaidActionRequest_t;
+
+/*RAID Action request Action values */
+
+#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01)
+#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02)
+#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03)
+#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04)
+#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05)
+#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A)
+#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B)
+#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F)
+#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11)
+#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15)
+#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17)
+#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18)
+#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19)
+#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C)
+#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D)
+#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E)
+#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20)
+#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21)
+#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22)
+#define MPI2_RAID_ACTION_COMPATIBILITY_CHECK (0x23)
+#define MPI2_RAID_ACTION_PHYSDISK_HIDDEN (0x24)
+#define MPI2_RAID_ACTION_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI2_RAID_ACTION_MAX_PRODUCT_SPECIFIC (0xFF)
+
+/*RAID Volume Creation Structure */
+
+/*
+ *The following define can be customized for the targeted product.
+ */
+#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS
+#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1)
+#endif
+
+typedef struct _MPI2_RAID_VOLUME_PHYSDISK {
+ U8 RAIDSetNum; /*0x00 */
+ U8 PhysDiskMap; /*0x01 */
+ U16 PhysDiskDevHandle; /*0x02 */
+} MPI2_RAID_VOLUME_PHYSDISK, *PTR_MPI2_RAID_VOLUME_PHYSDISK,
+ Mpi2RaidVolumePhysDisk_t, *pMpi2RaidVolumePhysDisk_t;
+
+/*defines for the PhysDiskMap field */
+#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01)
+#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02)
+
+typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT {
+ U8 NumPhysDisks; /*0x00 */
+ U8 VolumeType; /*0x01 */
+ U16 Reserved1; /*0x02 */
+ U32 VolumeCreationFlags; /*0x04 */
+ U32 VolumeSettings; /*0x08 */
+ U8 Reserved2; /*0x0C */
+ U8 ResyncRate; /*0x0D */
+ U16 DataScrubDuration; /*0x0E */
+ U64 VolumeMaxLBA; /*0x10 */
+ U32 StripeSize; /*0x18 */
+ U8 Name[16]; /*0x1C */
+ MPI2_RAID_VOLUME_PHYSDISK
+ PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS]; /*0x2C */
+} MPI2_RAID_VOLUME_CREATION_STRUCT,
+ *PTR_MPI2_RAID_VOLUME_CREATION_STRUCT,
+ Mpi2RaidVolumeCreationStruct_t,
+ *pMpi2RaidVolumeCreationStruct_t;
+
+/*use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */
+
+/*defines for the VolumeCreationFlags field */
+#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000)
+#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004)
+#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002)
+#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001)
+/*The following is an obsolete define.
+ *It must be shifted left 24 bits in order to set the proper bit.
+ */
+#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80)
+
+/*RAID Online Capacity Expansion Structure */
+
+typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION {
+ U32 Flags; /*0x00 */
+ U16 DevHandle0; /*0x04 */
+ U16 Reserved1; /*0x06 */
+ U16 DevHandle1; /*0x08 */
+ U16 Reserved2; /*0x0A */
+} MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
+ *PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
+ Mpi2RaidOnlineCapacityExpansion_t,
+ *pMpi2RaidOnlineCapacityExpansion_t;
+
+/*RAID Compatibility Input Structure */
+
+typedef struct _MPI2_RAID_COMPATIBILITY_INPUT_STRUCT {
+ U16 SourceDevHandle; /*0x00 */
+ U16 CandidateDevHandle; /*0x02 */
+ U32 Flags; /*0x04 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+} MPI2_RAID_COMPATIBILITY_INPUT_STRUCT,
+ *PTR_MPI2_RAID_COMPATIBILITY_INPUT_STRUCT,
+ Mpi2RaidCompatibilityInputStruct_t,
+ *pMpi2RaidCompatibilityInputStruct_t;
+
+/*defines for RAID Compatibility Structure Flags field */
+#define MPI2_RAID_COMPAT_SOURCE_IS_VOLUME_FLAG (0x00000002)
+#define MPI2_RAID_COMPAT_REPORT_SOURCE_INFO_FLAG (0x00000001)
+
+/*RAID Volume Indicator Structure */
+
+typedef struct _MPI2_RAID_VOL_INDICATOR {
+ U64 TotalBlocks; /*0x00 */
+ U64 BlocksRemaining; /*0x08 */
+ U32 Flags; /*0x10 */
+ U32 ElapsedSeconds; /* 0x14 */
+} MPI2_RAID_VOL_INDICATOR, *PTR_MPI2_RAID_VOL_INDICATOR,
+ Mpi2RaidVolIndicator_t, *pMpi2RaidVolIndicator_t;
+
+/*defines for RAID Volume Indicator Flags field */
+#define MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID (0x80000000)
+#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F)
+#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000)
+#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001)
+#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002)
+#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003)
+#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004)
+
+/*RAID Compatibility Result Structure */
+
+typedef struct _MPI2_RAID_COMPATIBILITY_RESULT_STRUCT {
+ U8 State; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 GenericAttributes; /*0x04 */
+ U32 OEMSpecificAttributes; /*0x08 */
+ U32 Reserved3; /*0x0C */
+ U32 Reserved4; /*0x10 */
+} MPI2_RAID_COMPATIBILITY_RESULT_STRUCT,
+ *PTR_MPI2_RAID_COMPATIBILITY_RESULT_STRUCT,
+ Mpi2RaidCompatibilityResultStruct_t,
+ *pMpi2RaidCompatibilityResultStruct_t;
+
+/*defines for RAID Compatibility Result Structure State field */
+#define MPI2_RAID_COMPAT_STATE_COMPATIBLE (0x00)
+#define MPI2_RAID_COMPAT_STATE_NOT_COMPATIBLE (0x01)
+
+/*defines for RAID Compatibility Result Structure GenericAttributes field */
+#define MPI2_RAID_COMPAT_GENATTRIB_4K_SECTOR (0x00000010)
+
+#define MPI2_RAID_COMPAT_GENATTRIB_MEDIA_MASK (0x0000000C)
+#define MPI2_RAID_COMPAT_GENATTRIB_SOLID_STATE_DRIVE (0x00000008)
+#define MPI2_RAID_COMPAT_GENATTRIB_HARD_DISK_DRIVE (0x00000004)
+
+#define MPI2_RAID_COMPAT_GENATTRIB_PROTOCOL_MASK (0x00000003)
+#define MPI2_RAID_COMPAT_GENATTRIB_SAS_PROTOCOL (0x00000002)
+#define MPI2_RAID_COMPAT_GENATTRIB_SATA_PROTOCOL (0x00000001)
+
+/*RAID Action Reply ActionData union */
+typedef union _MPI2_RAID_ACTION_REPLY_DATA {
+ U32 Word[6];
+ MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator;
+ U16 VolDevHandle;
+ U8 VolumeState;
+ U8 PhysDiskNum;
+ MPI2_RAID_COMPATIBILITY_RESULT_STRUCT RaidCompatibilityResult;
+} MPI2_RAID_ACTION_REPLY_DATA, *PTR_MPI2_RAID_ACTION_REPLY_DATA,
+ Mpi2RaidActionReplyData_t, *pMpi2RaidActionReplyData_t;
+
+/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for
+ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
+
+/*RAID Action Reply Message */
+typedef struct _MPI2_RAID_ACTION_REPLY {
+ U8 Action; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 VolDevHandle; /*0x04 */
+ U8 PhysDiskNum; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ MPI2_RAID_ACTION_REPLY_DATA ActionData; /*0x14 */
+} MPI2_RAID_ACTION_REPLY, *PTR_MPI2_RAID_ACTION_REPLY,
+ Mpi2RaidActionReply_t, *pMpi2RaidActionReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_sas.h b/drivers/scsi/mpt3sas/mpi/mpi2_sas.h
new file mode 100644
index 000000000000..16c922a8a02b
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_sas.h
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2_sas.h
+ * Title: MPI Serial Attached SCSI structures and definitions
+ * Creation Date: February 9, 2007
+ *
+ * mpi2_sas.h Version: 02.00.10
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit
+ * Control Request.
+ * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control
+ * Request.
+ * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
+ * to MPI2_SGE_IO_UNION since it supports chained SGLs.
+ * 05-12-10 02.00.04 Modified some comments.
+ * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control.
+ * 11-18-11 02.00.06 Incorporating additions for MPI v2.5.
+ * 07-10-12 02.00.07 Added MPI2_SATA_PT_SGE_UNION for use in the SATA
+ * Passthrough Request message.
+ * 08-19-13 02.00.08 Made MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL obsolete
+ * for anything newer than MPI v2.0.
+ * 11-18-14 02.00.09 Updated copyright information.
+ * 03-16-15 02.00.10 Updated for MPI v2.6.
+ * Added MPI2_SATA_PT_REQ_PT_FLAGS_FPDMA.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_SAS_H
+#define MPI2_SAS_H
+
+/*
+ *Values for SASStatus.
+ */
+#define MPI2_SASSTATUS_SUCCESS (0x00)
+#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01)
+#define MPI2_SASSTATUS_INVALID_FRAME (0x02)
+#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03)
+#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04)
+#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05)
+#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06)
+#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07)
+#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08)
+#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09)
+#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A)
+#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B)
+#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C)
+#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D)
+#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E)
+#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F)
+#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10)
+#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11)
+#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12)
+#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13)
+#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14)
+
+/*
+ *Values for the SAS DeviceInfo field used in SAS Device Status Change Event
+ *data and SAS Configuration pages.
+ */
+#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000)
+#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000)
+#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000)
+#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800)
+#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400)
+#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200)
+#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100)
+#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080)
+#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040)
+#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020)
+#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010)
+#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008)
+
+#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007)
+#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000)
+#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001)
+#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002)
+#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003)
+
+/*****************************************************************************
+*
+* SAS Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* SMP Passthrough messages
+****************************************************************************/
+
+/*SMP Passthrough Request Message */
+typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST {
+ U8 PassthroughFlags; /*0x00 */
+ U8 PhysicalPort; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 RequestDataLength; /*0x04 */
+ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Reserved2; /*0x0C */
+ U64 SASAddress; /*0x10 */
+ U32 Reserved3; /*0x18 */
+ U32 Reserved4; /*0x1C */
+ MPI2_SIMPLE_SGE_UNION SGL;/*0x20 */
+} MPI2_SMP_PASSTHROUGH_REQUEST, *PTR_MPI2_SMP_PASSTHROUGH_REQUEST,
+ Mpi2SmpPassthroughRequest_t, *pMpi2SmpPassthroughRequest_t;
+
+/*values for PassthroughFlags field */
+#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80)
+
+/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*SMP Passthrough Reply Message */
+typedef struct _MPI2_SMP_PASSTHROUGH_REPLY {
+ U8 PassthroughFlags; /*0x00 */
+ U8 PhysicalPort; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 ResponseDataLength; /*0x04 */
+ U8 SGLFlags; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U8 Reserved2; /*0x0C */
+ U8 SASStatus; /*0x0D */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 Reserved3; /*0x14 */
+ U8 ResponseData[4]; /*0x18 */
+} MPI2_SMP_PASSTHROUGH_REPLY, *PTR_MPI2_SMP_PASSTHROUGH_REPLY,
+ Mpi2SmpPassthroughReply_t, *pMpi2SmpPassthroughReply_t;
+
+/*values for PassthroughFlags field */
+#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80)
+
+/*values for SASStatus field are at the top of this file */
+
+/****************************************************************************
+* SATA Passthrough messages
+****************************************************************************/
+
+typedef union _MPI2_SATA_PT_SGE_UNION {
+ MPI2_SGE_SIMPLE_UNION MpiSimple; /*MPI v2.0 only */
+ MPI2_SGE_CHAIN_UNION MpiChain; /*MPI v2.0 only */
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; /*MPI v2.0 only */
+ MPI25_IEEE_SGE_CHAIN64 IeeeChain64; /*MPI v2.5 only */
+} MPI2_SATA_PT_SGE_UNION, *PTR_MPI2_SATA_PT_SGE_UNION,
+ Mpi2SataPTSGEUnion_t, *pMpi2SataPTSGEUnion_t;
+
+/*SATA Passthrough Request Message */
+typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 PassthroughFlags; /*0x04 */
+ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Reserved2; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U32 Reserved4; /*0x14 */
+ U32 DataLength; /*0x18 */
+ U8 CommandFIS[20]; /*0x1C */
+ MPI2_SATA_PT_SGE_UNION SGL;/*0x30*//*MPI v2.5: IEEE 64 elements only*/
+} MPI2_SATA_PASSTHROUGH_REQUEST, *PTR_MPI2_SATA_PASSTHROUGH_REQUEST,
+ Mpi2SataPassthroughRequest_t,
+ *pMpi2SataPassthroughRequest_t;
+
+/*values for PassthroughFlags field */
+#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_FPDMA (0x0040)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001)
+
+/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*SATA Passthrough Reply Message */
+typedef struct _MPI2_SATA_PASSTHROUGH_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 PassthroughFlags; /*0x04 */
+ U8 SGLFlags; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U8 Reserved2; /*0x0C */
+ U8 SASStatus; /*0x0D */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 StatusFIS[20]; /*0x14 */
+ U32 StatusControlRegisters; /*0x28 */
+ U32 TransferCount; /*0x2C */
+} MPI2_SATA_PASSTHROUGH_REPLY, *PTR_MPI2_SATA_PASSTHROUGH_REPLY,
+ Mpi2SataPassthroughReply_t, *pMpi2SataPassthroughReply_t;
+
+/*values for SASStatus field are at the top of this file */
+
+/****************************************************************************
+* SAS IO Unit Control messages
+* (MPI v2.5 and earlier only.
+* Replaced by IO Unit Control messages in MPI v2.6 and later.)
+****************************************************************************/
+
+/*SAS IO Unit Control Request Message */
+typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST {
+ U8 Operation; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 DevHandle; /*0x04 */
+ U8 IOCParameter; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U8 PhyNum; /*0x0E */
+ U8 PrimFlags; /*0x0F */
+ U32 Primitive; /*0x10 */
+ U8 LookupMethod; /*0x14 */
+ U8 Reserved5; /*0x15 */
+ U16 SlotNumber; /*0x16 */
+ U64 LookupAddress; /*0x18 */
+ U32 IOCParameterValue; /*0x20 */
+ U32 Reserved7; /*0x24 */
+ U32 Reserved8; /*0x28 */
+} MPI2_SAS_IOUNIT_CONTROL_REQUEST,
+ *PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST,
+ Mpi2SasIoUnitControlRequest_t,
+ *pMpi2SasIoUnitControlRequest_t;
+
+/*values for the Operation field */
+#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02)
+#define MPI2_SAS_OP_PHY_LINK_RESET (0x06)
+#define MPI2_SAS_OP_PHY_HARD_RESET (0x07)
+#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08)
+#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A)
+#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B)
+#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C) /* MPI v2.0 only */
+#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D)
+#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E)
+#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F)
+#define MPI25_SAS_OP_ENABLE_FP_DEVICE (0x10)
+#define MPI25_SAS_OP_DISABLE_FP_DEVICE (0x11)
+#define MPI25_SAS_OP_ENABLE_FP_ALL (0x12)
+#define MPI25_SAS_OP_DISABLE_FP_ALL (0x13)
+#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14)
+#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15)
+#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80)
+
+/*values for the PrimFlags field */
+#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08)
+#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02)
+#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01)
+
+/*values for the LookupMethod field */
+#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01)
+#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02)
+#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03)
+
+/*SAS IO Unit Control Reply Message */
+typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY {
+ U8 Operation; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 DevHandle; /*0x04 */
+ U8 IOCParameter; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_SAS_IOUNIT_CONTROL_REPLY,
+ *PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY,
+ Mpi2SasIoUnitControlReply_t, *pMpi2SasIoUnitControlReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h
new file mode 100644
index 000000000000..17ef7f63b938
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h
@@ -0,0 +1,565 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2000-2020 Broadcom Inc. All rights reserved.
+ *
+ *
+ * Name: mpi2_tool.h
+ * Title: MPI diagnostic tool structures and definitions
+ * Creation Date: March 26, 2007
+ *
+ * mpi2_tool.h Version: 02.00.16
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release
+ * structures and defines.
+ * 02-29-08 02.00.02 Modified various names to make them 32-character unique.
+ * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool.
+ * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request
+ * and reply messages.
+ * Added MPI2_DIAG_BUF_TYPE_EXTENDED.
+ * Incremented MPI2_DIAG_BUF_TYPE_COUNT.
+ * 05-12-10 02.00.05 Added Diagnostic Data Upload tool.
+ * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer
+ * Post Request.
+ * 05-25-11 02.00.07 Added Flags field and related defines to
+ * MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST.
+ * 11-18-11 02.00.08 Incorporating additions for MPI v2.5.
+ * 07-10-12 02.00.09 Add MPI v2.5 Toolbox Diagnostic CLI Tool Request
+ * message.
+ * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that
+ * it uses MPI Chain SGE as well as MPI Simple SGE.
+ * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info.
+ * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC.
+ * 11-18-14 02.00.13 Updated copyright information.
+ * 08-25-16 02.00.14 Added new values for the Flags field of Toolbox Clean
+ * Tool Request Message.
+ * 07-22-18 02.00.15 Added defines for new TOOLBOX_PCIE_LANE_MARGINING tool.
+ * Added option for DeviceInfo field in ISTWI tool.
+ * 12-17-18 02.00.16 Shorten some defines to be compatible with DOS.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_TOOL_H
+#define MPI2_TOOL_H
+
+/*****************************************************************************
+*
+* Toolbox Messages
+*
+*****************************************************************************/
+
+/*defines for the Tools */
+#define MPI2_TOOLBOX_CLEAN_TOOL (0x00)
+#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01)
+#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02)
+#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03)
+#define MPI2_TOOLBOX_BEACON_TOOL (0x05)
+#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06)
+#define MPI2_TOOLBOX_TEXT_DISPLAY_TOOL (0x07)
+#define MPI26_TOOLBOX_BACKEND_PCIE_LANE_MARGIN (0x08)
+
+/****************************************************************************
+* Toolbox reply
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_REPLY {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_TOOLBOX_REPLY, *PTR_MPI2_TOOLBOX_REPLY,
+ Mpi2ToolboxReply_t, *pMpi2ToolboxReply_t;
+
+/****************************************************************************
+* Toolbox Clean Tool request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Flags; /*0x0C */
+} MPI2_TOOLBOX_CLEAN_REQUEST, *PTR_MPI2_TOOLBOX_CLEAN_REQUEST,
+ Mpi2ToolboxCleanRequest_t, *pMpi2ToolboxCleanRequest_t;
+
+/*values for the Flags field */
+#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000)
+#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000)
+#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000)
+#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000)
+#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000)
+#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000)
+#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000)
+#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000)
+#define MPI2_TOOLBOX_CLEAN_SBR (0x00800000)
+#define MPI2_TOOLBOX_CLEAN_SBR_BACKUP (0x00400000)
+#define MPI2_TOOLBOX_CLEAN_HIIM (0x00200000)
+#define MPI2_TOOLBOX_CLEAN_HIIA (0x00100000)
+#define MPI2_TOOLBOX_CLEAN_CTLR (0x00080000)
+#define MPI2_TOOLBOX_CLEAN_IMR_FIRMWARE (0x00040000)
+#define MPI2_TOOLBOX_CLEAN_MR_NVDATA (0x00020000)
+#define MPI2_TOOLBOX_CLEAN_RESERVED_5_16 (0x0001FFE0)
+#define MPI2_TOOLBOX_CLEAN_ALL_BUT_MPB (0x00000010)
+#define MPI2_TOOLBOX_CLEAN_ENTIRE_FLASH (0x00000008)
+#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004)
+#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002)
+#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001)
+
+/****************************************************************************
+* Toolbox Memory Move request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ MPI2_SGE_SIMPLE_UNION SGL; /*0x0C */
+} MPI2_TOOLBOX_MEM_MOVE_REQUEST, *PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST,
+ Mpi2ToolboxMemMoveRequest_t, *pMpi2ToolboxMemMoveRequest_t;
+
+/****************************************************************************
+* Toolbox Diagnostic Data Upload request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 SGLFlags; /*0x0C */
+ U8 Reserved5; /*0x0D */
+ U16 Reserved6; /*0x0E */
+ U32 Flags; /*0x10 */
+ U32 DataLength; /*0x14 */
+ MPI2_SGE_SIMPLE_UNION SGL; /*0x18 */
+} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+ *PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+ Mpi2ToolboxDiagDataUploadRequest_t,
+ *pMpi2ToolboxDiagDataUploadRequest_t;
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER {
+ U32 DiagDataLength; /*00h */
+ U8 FormatCode; /*04h */
+ U8 Reserved1; /*05h */
+ U16 Reserved2; /*06h */
+} MPI2_DIAG_DATA_UPLOAD_HEADER, *PTR_MPI2_DIAG_DATA_UPLOAD_HEADER,
+ Mpi2DiagDataUploadHeader_t, *pMpi2DiagDataUploadHeader_t;
+
+/****************************************************************************
+* Toolbox ISTWI Read Write Tool
+****************************************************************************/
+
+/*Toolbox ISTWI Read Write Tool request message */
+typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ U8 DevIndex; /*0x14 */
+ U8 Action; /*0x15 */
+ U8 SGLFlags; /*0x16 */
+ U8 Flags; /*0x17 */
+ U16 TxDataLength; /*0x18 */
+ U16 RxDataLength; /*0x1A */
+ U32 Reserved8; /*0x1C */
+ U32 Reserved9; /*0x20 */
+ U32 Reserved10; /*0x24 */
+ U32 Reserved11; /*0x28 */
+ U32 Reserved12; /*0x2C */
+ MPI2_SGE_SIMPLE_UNION SGL; /*0x30 */
+} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST,
+ *PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST,
+ Mpi2ToolboxIstwiReadWriteRequest_t,
+ *pMpi2ToolboxIstwiReadWriteRequest_t;
+
+/*values for the Action field */
+#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01)
+#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02)
+#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03)
+#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10)
+#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11)
+#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12)
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*values for the Flags field */
+#define MPI2_TOOL_ISTWI_FLAG_AUTO_RESERVE_RELEASE (0x80)
+#define MPI2_TOOL_ISTWI_FLAG_PAGE_ADDR_MASK (0x07)
+
+/*MPI26 TOOLBOX Request MsgFlags defines */
+#define MPI26_TOOL_ISTWI_MSGFLG_ADDR_MASK (0x01)
+/*Request uses Man Page 43 device index addressing */
+#define MPI26_TOOL_ISTWI_MSGFLG_ADDR_INDEX (0x00)
+/*Request uses Man Page 43 device info struct addressing */
+#define MPI26_TOOL_ISTWI_MSGFLG_ADDR_INFO (0x01)
+
+/*Toolbox ISTWI Read Write Tool reply message */
+typedef struct _MPI2_TOOLBOX_ISTWI_REPLY {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 DevIndex; /*0x14 */
+ U8 Action; /*0x15 */
+ U8 IstwiStatus; /*0x16 */
+ U8 Reserved6; /*0x17 */
+ U16 TxDataCount; /*0x18 */
+ U16 RxDataCount; /*0x1A */
+} MPI2_TOOLBOX_ISTWI_REPLY, *PTR_MPI2_TOOLBOX_ISTWI_REPLY,
+ Mpi2ToolboxIstwiReply_t, *pMpi2ToolboxIstwiReply_t;
+
+/****************************************************************************
+* Toolbox Beacon Tool request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_BEACON_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 Reserved5; /*0x0C */
+ U8 PhysicalPort; /*0x0D */
+ U8 Reserved6; /*0x0E */
+ U8 Flags; /*0x0F */
+} MPI2_TOOLBOX_BEACON_REQUEST, *PTR_MPI2_TOOLBOX_BEACON_REQUEST,
+ Mpi2ToolboxBeaconRequest_t, *pMpi2ToolboxBeaconRequest_t;
+
+/*values for the Flags field */
+#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00)
+#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01)
+
+/****************************************************************************
+* Toolbox Diagnostic CLI Tool
+****************************************************************************/
+
+#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C)
+
+/*MPI v2.0 Toolbox Diagnostic CLI Tool request message */
+typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 SGLFlags; /*0x0C */
+ U8 Reserved5; /*0x0D */
+ U16 Reserved6; /*0x0E */
+ U32 DataLength; /*0x10 */
+ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */
+ MPI2_MPI_SGE_IO_UNION SGL; /*0x70 */
+} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ *PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ Mpi2ToolboxDiagnosticCliRequest_t,
+ *pMpi2ToolboxDiagnosticCliRequest_t;
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*MPI v2.5 Toolbox Diagnostic CLI Tool request message */
+typedef struct _MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 DataLength; /*0x10 */
+ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */
+ MPI25_SGE_IO_UNION SGL; /* 0x70 */
+} MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ *PTR_MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ Mpi25ToolboxDiagnosticCliRequest_t,
+ *pMpi25ToolboxDiagnosticCliRequest_t;
+
+/*Toolbox Diagnostic CLI Tool reply message */
+typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 ReturnedDataLength; /*0x14 */
+} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY,
+ *PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY,
+ Mpi2ToolboxDiagnosticCliReply_t,
+ *pMpi2ToolboxDiagnosticCliReply_t;
+
+
+/****************************************************************************
+* Toolbox Console Text Display Tool
+****************************************************************************/
+
+/* Toolbox Console Text Display Tool request message */
+typedef struct _MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST {
+ U8 Tool; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 Reserved2; /* 0x04 */
+ U8 Reserved3; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved4; /* 0x0A */
+ U8 Console; /* 0x0C */
+ U8 Flags; /* 0x0D */
+ U16 Reserved6; /* 0x0E */
+ U8 TextToDisplay[4]; /* 0x10 */
+} MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST,
+*PTR_MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST,
+Mpi2ToolboxTextDisplayRequest_t,
+*pMpi2ToolboxTextDisplayRequest_t;
+
+/* defines for the Console field */
+#define MPI2_TOOLBOX_CONSOLE_TYPE_MASK (0xF0)
+#define MPI2_TOOLBOX_CONSOLE_TYPE_DEFAULT (0x00)
+#define MPI2_TOOLBOX_CONSOLE_TYPE_UART (0x10)
+#define MPI2_TOOLBOX_CONSOLE_TYPE_ETHERNET (0x20)
+
+#define MPI2_TOOLBOX_CONSOLE_NUMBER_MASK (0x0F)
+
+/* defines for the Flags field */
+#define MPI2_TOOLBOX_CONSOLE_FLAG_TIMESTAMP (0x01)
+
+
+/***************************************************************************
+ * Toolbox Backend Lane Margining Tool
+ ***************************************************************************
+ */
+
+/*Toolbox Backend Lane Margining Tool request message */
+typedef struct _MPI26_TOOLBOX_LANE_MARGIN_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 Command; /*0x0C */
+ U8 SwitchPort; /*0x0D */
+ U16 DevHandle; /*0x0E */
+ U8 RegisterOffset; /*0x10 */
+ U8 Reserved5; /*0x11 */
+ U16 DataLength; /*0x12 */
+ MPI25_SGE_IO_UNION SGL; /*0x14 */
+} MPI26_TOOLBOX_LANE_MARGINING_REQUEST,
+ *PTR_MPI2_TOOLBOX_LANE_MARGINING_REQUEST,
+ Mpi26ToolboxLaneMarginingRequest_t,
+ *pMpi2ToolboxLaneMarginingRequest_t;
+
+/* defines for the Command field */
+#define MPI26_TOOL_MARGIN_COMMAND_ENTER_MARGIN_MODE (0x01)
+#define MPI26_TOOL_MARGIN_COMMAND_READ_REGISTER_DATA (0x02)
+#define MPI26_TOOL_MARGIN_COMMAND_WRITE_REGISTER_DATA (0x03)
+#define MPI26_TOOL_MARGIN_COMMAND_EXIT_MARGIN_MODE (0x04)
+
+
+/*Toolbox Backend Lane Margining Tool reply message */
+typedef struct _MPI26_TOOLBOX_LANE_MARGIN_REPLY {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U16 ReturnedDataLength; /*0x14 */
+ U16 Reserved6; /*0x16 */
+} MPI26_TOOLBOX_LANE_MARGINING_REPLY,
+ *PTR_MPI26_TOOLBOX_LANE_MARGINING_REPLY,
+ Mpi26ToolboxLaneMarginingReply_t,
+ *pMpi26ToolboxLaneMarginingReply_t;
+
+
+/*****************************************************************************
+*
+* Diagnostic Buffer Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* Diagnostic Buffer Post request
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST {
+ U8 ExtendedType; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U64 BufferAddress; /*0x0C */
+ U32 BufferLength; /*0x14 */
+ U32 Reserved5; /*0x18 */
+ U32 Reserved6; /*0x1C */
+ U32 Flags; /*0x20 */
+ U32 ProductSpecific[23]; /*0x24 */
+} MPI2_DIAG_BUFFER_POST_REQUEST, *PTR_MPI2_DIAG_BUFFER_POST_REQUEST,
+ Mpi2DiagBufferPostRequest_t, *pMpi2DiagBufferPostRequest_t;
+
+/*values for the ExtendedType field */
+#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02)
+
+/*values for the BufferType field */
+#define MPI2_DIAG_BUF_TYPE_TRACE (0x00)
+#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01)
+#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02)
+/*count of the number of buffer types */
+#define MPI2_DIAG_BUF_TYPE_COUNT (0x03)
+
+/*values for the Flags field */
+#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002)
+#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001)
+
+/****************************************************************************
+* Diagnostic Buffer Post reply
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_BUFFER_POST_REPLY {
+ U8 ExtendedType; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 TransferLength; /*0x14 */
+} MPI2_DIAG_BUFFER_POST_REPLY, *PTR_MPI2_DIAG_BUFFER_POST_REPLY,
+ Mpi2DiagBufferPostReply_t, *pMpi2DiagBufferPostReply_t;
+
+/****************************************************************************
+* Diagnostic Release request
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_RELEASE_REQUEST {
+ U8 Reserved1; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+} MPI2_DIAG_RELEASE_REQUEST, *PTR_MPI2_DIAG_RELEASE_REQUEST,
+ Mpi2DiagReleaseRequest_t, *pMpi2DiagReleaseRequest_t;
+
+/****************************************************************************
+* Diagnostic Buffer Post reply
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_RELEASE_REPLY {
+ U8 Reserved1; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_DIAG_RELEASE_REPLY, *PTR_MPI2_DIAG_RELEASE_REPLY,
+ Mpi2DiagReleaseReply_t, *pMpi2DiagReleaseReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_type.h b/drivers/scsi/mpt3sas/mpi/mpi2_type.h
index cfde017bf16e..36494439a419 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_type.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_type.h
@@ -1,12 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2000-2007 LSI Corporation.
+ * Copyright 2000-2014 Avago Technologies. All rights reserved.
*
*
* Name: mpi2_type.h
* Title: MPI basic type definitions
* Creation Date: August 16, 2006
*
- * mpi2_type.h Version: 02.00.00
+ * mpi2_type.h Version: 02.00.01
*
* Version History
* ---------------
@@ -14,22 +15,19 @@
* Date Version Description
* -------- -------- ------------------------------------------------------
* 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 11-18-14 02.00.01 Updated copyright information.
* --------------------------------------------------------------------------
*/
#ifndef MPI2_TYPE_H
#define MPI2_TYPE_H
-
/*******************************************************************************
- * Define MPI2_POINTER if it hasn't already been defined. By default
- * MPI2_POINTER is defined to be a near pointer. MPI2_POINTER can be defined as
- * a far pointer by defining MPI2_POINTER as "far *" before this header file is
+ * Define * if it hasn't already been defined. By default
+ * * is defined to be a near pointer. MPI2_POINTER can be defined as
+ * a far pointer by defining * as "far *" before this header file is
* included.
*/
-#ifndef MPI2_POINTER
-#define MPI2_POINTER *
-#endif
/* the basic types may have already been included by mpi_type.h */
#ifndef MPI_TYPE_H
@@ -42,7 +40,7 @@
typedef u8 U8;
typedef __le16 U16;
typedef __le32 U32;
-typedef __le64 U64 __attribute__((aligned(4)));
+typedef __le64 U64 __attribute__ ((aligned(4)));
/*****************************************************************************
*
@@ -50,12 +48,11 @@ typedef __le64 U64 __attribute__((aligned(4)));
*
*****************************************************************************/
-typedef U8 *PU8;
-typedef U16 *PU16;
-typedef U32 *PU32;
-typedef U64 *PU64;
+typedef U8 *PU8;
+typedef U16 *PU16;
+typedef U32 *PU32;
+typedef U64 *PU64;
#endif
#endif
-
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
new file mode 100644
index 000000000000..0d652db8fe24
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -0,0 +1,9044 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * for access to MPT (Message Passing Technology) firmware.
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/time.h>
+#include <linux/ktime.h>
+#include <linux/kthread.h>
+#include <asm/page.h> /* To get host page size per arch */
+
+
+#include "mpt3sas_base.h"
+
+static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS];
+
+
+#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */
+
+ /* maximum controller queue depth */
+#define MAX_HBA_QUEUE_DEPTH 30000
+#define MAX_CHAIN_DEPTH 100000
+static int max_queue_depth = -1;
+module_param(max_queue_depth, int, 0444);
+MODULE_PARM_DESC(max_queue_depth, " max controller queue depth ");
+
+static int max_sgl_entries = -1;
+module_param(max_sgl_entries, int, 0444);
+MODULE_PARM_DESC(max_sgl_entries, " max sg entries ");
+
+static int msix_disable = -1;
+module_param(msix_disable, int, 0444);
+MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
+
+static int smp_affinity_enable = 1;
+module_param(smp_affinity_enable, int, 0444);
+MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disable Default: enable(1)");
+
+static int max_msix_vectors = -1;
+module_param(max_msix_vectors, int, 0444);
+MODULE_PARM_DESC(max_msix_vectors,
+ " max msix vectors");
+
+static int irqpoll_weight = -1;
+module_param(irqpoll_weight, int, 0444);
+MODULE_PARM_DESC(irqpoll_weight,
+ "irq poll weight (default= one fourth of HBA queue depth)");
+
+static int mpt3sas_fwfault_debug;
+MODULE_PARM_DESC(mpt3sas_fwfault_debug,
+ " enable detection of firmware fault and halt firmware - (default=0)");
+
+static int perf_mode = -1;
+module_param(perf_mode, int, 0444);
+MODULE_PARM_DESC(perf_mode,
+ "Performance mode (only for Aero/Sea Generation), options:\n\t\t"
+ "0 - balanced: high iops mode is enabled &\n\t\t"
+ "interrupt coalescing is enabled only on high iops queues,\n\t\t"
+ "1 - iops: high iops mode is disabled &\n\t\t"
+ "interrupt coalescing is enabled on all queues,\n\t\t"
+ "2 - latency: high iops mode is disabled &\n\t\t"
+ "interrupt coalescing is enabled on all queues with timeout value 0xA,\n"
+ "\t\tdefault - default perf_mode is 'balanced'"
+ );
+
+static int poll_queues;
+module_param(poll_queues, int, 0444);
+MODULE_PARM_DESC(poll_queues, "Number of queues to be use for io_uring poll mode.\n\t\t"
+ "This parameter is effective only if host_tagset_enable=1. &\n\t\t"
+ "when poll_queues are enabled then &\n\t\t"
+ "perf_mode is set to latency mode. &\n\t\t"
+ );
+
+enum mpt3sas_perf_mode {
+ MPT_PERF_MODE_DEFAULT = -1,
+ MPT_PERF_MODE_BALANCED = 0,
+ MPT_PERF_MODE_IOPS = 1,
+ MPT_PERF_MODE_LATENCY = 2,
+};
+
+static int
+_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc,
+ u32 ioc_state, int timeout);
+static int
+_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc);
+static void
+_base_clear_outstanding_commands(struct MPT3SAS_ADAPTER *ioc);
+
+static u32
+_base_readl_ext_retry(const void __iomem *addr);
+
+/**
+ * mpt3sas_base_check_cmd_timeout - Function
+ * to check timeout and command termination due
+ * to Host reset.
+ *
+ * @ioc: per adapter object.
+ * @status: Status of issued command.
+ * @mpi_request:mf request pointer.
+ * @sz: size of buffer.
+ *
+ * Return: 1/0 Reset to be done or Not
+ */
+u8
+mpt3sas_base_check_cmd_timeout(struct MPT3SAS_ADAPTER *ioc,
+ u8 status, void *mpi_request, int sz)
+{
+ u8 issue_reset = 0;
+
+ if (!(status & MPT3_CMD_RESET))
+ issue_reset = 1;
+
+ ioc_err(ioc, "Command %s\n",
+ issue_reset == 0 ? "terminated due to Host Reset" : "Timeout");
+ _debug_dump_mf(mpi_request, sz);
+
+ return issue_reset;
+}
+
+/**
+ * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug.
+ * @val: ?
+ * @kp: ?
+ *
+ * Return: ?
+ */
+static int
+_scsih_set_fwfault_debug(const char *val, const struct kernel_param *kp)
+{
+ int ret = param_set_int(val, kp);
+ struct MPT3SAS_ADAPTER *ioc;
+
+ if (ret)
+ return ret;
+
+ /* global ioc spinlock to protect controller list on list operations */
+ pr_info("setting fwfault_debug(%d)\n", mpt3sas_fwfault_debug);
+ spin_lock(&gioc_lock);
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
+ ioc->fwfault_debug = mpt3sas_fwfault_debug;
+ spin_unlock(&gioc_lock);
+ return 0;
+}
+module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug,
+ param_get_int, &mpt3sas_fwfault_debug, 0644);
+
+/**
+ * _base_readl_aero - retry readl for max three times.
+ * @addr: MPT Fusion system interface register address
+ *
+ * Retry the readl() for max three times if it gets zero value
+ * while reading the system interface register.
+ */
+static inline u32
+_base_readl_aero(const void __iomem *addr)
+{
+ u32 i = 0, ret_val;
+
+ do {
+ ret_val = readl(addr);
+ i++;
+ } while (ret_val == 0 && i < 3);
+
+ return ret_val;
+}
+
+static u32
+_base_readl_ext_retry(const void __iomem *addr)
+{
+ u32 i, ret_val;
+
+ for (i = 0 ; i < 30 ; i++) {
+ ret_val = readl(addr);
+ if (ret_val != 0)
+ break;
+ }
+
+ return ret_val;
+}
+
+static inline u32
+_base_readl(const void __iomem *addr)
+{
+ return readl(addr);
+}
+
+/**
+ * _base_clone_reply_to_sys_mem - copies reply to reply free iomem
+ * in BAR0 space.
+ *
+ * @ioc: per adapter object
+ * @reply: reply message frame(lower 32bit addr)
+ * @index: System request message index.
+ */
+static void
+_base_clone_reply_to_sys_mem(struct MPT3SAS_ADAPTER *ioc, u32 reply,
+ u32 index)
+{
+ /*
+ * 256 is offset within sys register.
+ * 256 offset MPI frame starts. Max MPI frame supported is 32.
+ * 32 * 128 = 4K. From here, Clone of reply free for mcpu starts
+ */
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+ void __iomem *reply_free_iomem = (void __iomem *)ioc->chip +
+ MPI_FRAME_START_OFFSET +
+ (cmd_credit * ioc->request_sz) + (index * sizeof(u32));
+
+ writel(reply, reply_free_iomem);
+}
+
+/**
+ * _base_clone_mpi_to_sys_mem - Writes/copies MPI frames
+ * to system/BAR0 region.
+ *
+ * @dst_iomem: Pointer to the destination location in BAR0 space.
+ * @src: Pointer to the Source data.
+ * @size: Size of data to be copied.
+ */
+static void
+_base_clone_mpi_to_sys_mem(void *dst_iomem, void *src, u32 size)
+{
+ int i;
+ u32 *src_virt_mem = (u32 *)src;
+
+ for (i = 0; i < size/4; i++)
+ writel((u32)src_virt_mem[i],
+ (void __iomem *)dst_iomem + (i * 4));
+}
+
+/**
+ * _base_clone_to_sys_mem - Writes/copies data to system/BAR0 region
+ *
+ * @dst_iomem: Pointer to the destination location in BAR0 space.
+ * @src: Pointer to the Source data.
+ * @size: Size of data to be copied.
+ */
+static void
+_base_clone_to_sys_mem(void __iomem *dst_iomem, void *src, u32 size)
+{
+ int i;
+ u32 *src_virt_mem = (u32 *)(src);
+
+ for (i = 0; i < size/4; i++)
+ writel((u32)src_virt_mem[i],
+ (void __iomem *)dst_iomem + (i * 4));
+}
+
+/**
+ * _base_get_chain - Calculates and Returns virtual chain address
+ * for the provided smid in BAR0 space.
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @sge_chain_count: Scatter gather chain count.
+ *
+ * Return: the chain address.
+ */
+static inline void __iomem*
+_base_get_chain(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 sge_chain_count)
+{
+ void __iomem *base_chain, *chain_virt;
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+
+ base_chain = (void __iomem *)ioc->chip + MPI_FRAME_START_OFFSET +
+ (cmd_credit * ioc->request_sz) +
+ REPLY_FREE_POOL_SIZE;
+ chain_virt = base_chain + (smid * ioc->facts.MaxChainDepth *
+ ioc->request_sz) + (sge_chain_count * ioc->request_sz);
+ return chain_virt;
+}
+
+/**
+ * _base_get_chain_phys - Calculates and Returns physical address
+ * in BAR0 for scatter gather chains, for
+ * the provided smid.
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @sge_chain_count: Scatter gather chain count.
+ *
+ * Return: Physical chain address.
+ */
+static inline phys_addr_t
+_base_get_chain_phys(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 sge_chain_count)
+{
+ phys_addr_t base_chain_phys, chain_phys;
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+
+ base_chain_phys = ioc->chip_phys + MPI_FRAME_START_OFFSET +
+ (cmd_credit * ioc->request_sz) +
+ REPLY_FREE_POOL_SIZE;
+ chain_phys = base_chain_phys + (smid * ioc->facts.MaxChainDepth *
+ ioc->request_sz) + (sge_chain_count * ioc->request_sz);
+ return chain_phys;
+}
+
+/**
+ * _base_get_buffer_bar0 - Calculates and Returns BAR0 mapped Host
+ * buffer address for the provided smid.
+ * (Each smid can have 64K starts from 17024)
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: Pointer to buffer location in BAR0.
+ */
+
+static void __iomem *
+_base_get_buffer_bar0(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+ // Added extra 1 to reach end of chain.
+ void __iomem *chain_end = _base_get_chain(ioc,
+ cmd_credit + 1,
+ ioc->facts.MaxChainDepth);
+ return chain_end + (smid * 64 * 1024);
+}
+
+/**
+ * _base_get_buffer_phys_bar0 - Calculates and Returns BAR0 mapped
+ * Host buffer Physical address for the provided smid.
+ * (Each smid can have 64K starts from 17024)
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: Pointer to buffer location in BAR0.
+ */
+static phys_addr_t
+_base_get_buffer_phys_bar0(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+ phys_addr_t chain_end_phys = _base_get_chain_phys(ioc,
+ cmd_credit + 1,
+ ioc->facts.MaxChainDepth);
+ return chain_end_phys + (smid * 64 * 1024);
+}
+
+/**
+ * _base_get_chain_buffer_dma_to_chain_buffer - Iterates chain
+ * lookup list and Provides chain_buffer
+ * address for the matching dma address.
+ * (Each smid can have 64K starts from 17024)
+ *
+ * @ioc: per adapter object
+ * @chain_buffer_dma: Chain buffer dma address.
+ *
+ * Return: Pointer to chain buffer. Or Null on Failure.
+ */
+static void *
+_base_get_chain_buffer_dma_to_chain_buffer(struct MPT3SAS_ADAPTER *ioc,
+ dma_addr_t chain_buffer_dma)
+{
+ u16 index, j;
+ struct chain_tracker *ct;
+
+ for (index = 0; index < ioc->scsiio_depth; index++) {
+ for (j = 0; j < ioc->chains_needed_per_io; j++) {
+ ct = &ioc->chain_lookup[index].chains_per_smid[j];
+ if (ct && ct->chain_buffer_dma == chain_buffer_dma)
+ return ct->chain_buffer;
+ }
+ }
+ ioc_info(ioc, "Provided chain_buffer_dma address is not in the lookup list\n");
+ return NULL;
+}
+
+/**
+ * _clone_sg_entries - MPI EP's scsiio and config requests
+ * are handled here. Base function for
+ * double buffering, before submitting
+ * the requests.
+ *
+ * @ioc: per adapter object.
+ * @mpi_request: mf request pointer.
+ * @smid: system request message index.
+ */
+static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc,
+ void *mpi_request, u16 smid)
+{
+ Mpi2SGESimple32_t *sgel, *sgel_next;
+ u32 sgl_flags, sge_chain_count = 0;
+ bool is_write = false;
+ u16 i = 0;
+ void __iomem *buffer_iomem;
+ phys_addr_t buffer_iomem_phys;
+ void __iomem *buff_ptr;
+ phys_addr_t buff_ptr_phys;
+ void __iomem *dst_chain_addr[MCPU_MAX_CHAINS_PER_IO];
+ void *src_chain_addr[MCPU_MAX_CHAINS_PER_IO];
+ phys_addr_t dst_addr_phys;
+ MPI2RequestHeader_t *request_hdr;
+ struct scsi_cmnd *scmd;
+ struct scatterlist *sg_scmd = NULL;
+ int is_scsiio_req = 0;
+
+ request_hdr = (MPI2RequestHeader_t *) mpi_request;
+
+ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) {
+ Mpi25SCSIIORequest_t *scsiio_request =
+ (Mpi25SCSIIORequest_t *)mpi_request;
+ sgel = (Mpi2SGESimple32_t *) &scsiio_request->SGL;
+ is_scsiio_req = 1;
+ } else if (request_hdr->Function == MPI2_FUNCTION_CONFIG) {
+ Mpi2ConfigRequest_t *config_req =
+ (Mpi2ConfigRequest_t *)mpi_request;
+ sgel = (Mpi2SGESimple32_t *) &config_req->PageBufferSGE;
+ } else
+ return;
+
+ /* From smid we can get scsi_cmd, once we have sg_scmd,
+ * we just need to get sg_virt and sg_next to get virtual
+ * address associated with sgel->Address.
+ */
+
+ if (is_scsiio_req) {
+ /* Get scsi_cmd using smid */
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (scmd == NULL) {
+ ioc_err(ioc, "scmd is NULL\n");
+ return;
+ }
+
+ /* Get sg_scmd from scmd provided */
+ sg_scmd = scsi_sglist(scmd);
+ }
+
+ /*
+ * 0 - 255 System register
+ * 256 - 4352 MPI Frame. (This is based on maxCredit 32)
+ * 4352 - 4864 Reply_free pool (512 byte is reserved
+ * considering maxCredit 32. Reply need extra
+ * room, for mCPU case kept four times of
+ * maxCredit).
+ * 4864 - 17152 SGE chain element. (32cmd * 3 chain of
+ * 128 byte size = 12288)
+ * 17152 - x Host buffer mapped with smid.
+ * (Each smid can have 64K Max IO.)
+ * BAR0+Last 1K MSIX Addr and Data
+ * Total size in use 2113664 bytes of 4MB BAR0
+ */
+
+ buffer_iomem = _base_get_buffer_bar0(ioc, smid);
+ buffer_iomem_phys = _base_get_buffer_phys_bar0(ioc, smid);
+
+ buff_ptr = buffer_iomem;
+ buff_ptr_phys = buffer_iomem_phys;
+ WARN_ON(buff_ptr_phys > U32_MAX);
+
+ if (le32_to_cpu(sgel->FlagsLength) &
+ (MPI2_SGE_FLAGS_HOST_TO_IOC << MPI2_SGE_FLAGS_SHIFT))
+ is_write = true;
+
+ for (i = 0; i < MPT_MIN_PHYS_SEGMENTS + ioc->facts.MaxChainDepth; i++) {
+
+ sgl_flags =
+ (le32_to_cpu(sgel->FlagsLength) >> MPI2_SGE_FLAGS_SHIFT);
+
+ switch (sgl_flags & MPI2_SGE_FLAGS_ELEMENT_MASK) {
+ case MPI2_SGE_FLAGS_CHAIN_ELEMENT:
+ /*
+ * Helper function which on passing
+ * chain_buffer_dma returns chain_buffer. Get
+ * the virtual address for sgel->Address
+ */
+ sgel_next =
+ _base_get_chain_buffer_dma_to_chain_buffer(ioc,
+ le32_to_cpu(sgel->Address));
+ if (sgel_next == NULL)
+ return;
+ /*
+ * This is coping 128 byte chain
+ * frame (not a host buffer)
+ */
+ dst_chain_addr[sge_chain_count] =
+ _base_get_chain(ioc,
+ smid, sge_chain_count);
+ src_chain_addr[sge_chain_count] =
+ (void *) sgel_next;
+ dst_addr_phys = _base_get_chain_phys(ioc,
+ smid, sge_chain_count);
+ WARN_ON(dst_addr_phys > U32_MAX);
+ sgel->Address =
+ cpu_to_le32(lower_32_bits(dst_addr_phys));
+ sgel = sgel_next;
+ sge_chain_count++;
+ break;
+ case MPI2_SGE_FLAGS_SIMPLE_ELEMENT:
+ if (is_write) {
+ if (is_scsiio_req) {
+ _base_clone_to_sys_mem(buff_ptr,
+ sg_virt(sg_scmd),
+ (le32_to_cpu(sgel->FlagsLength) &
+ 0x00ffffff));
+ /*
+ * FIXME: this relies on a a zero
+ * PCI mem_offset.
+ */
+ sgel->Address =
+ cpu_to_le32((u32)buff_ptr_phys);
+ } else {
+ _base_clone_to_sys_mem(buff_ptr,
+ ioc->config_vaddr,
+ (le32_to_cpu(sgel->FlagsLength) &
+ 0x00ffffff));
+ sgel->Address =
+ cpu_to_le32((u32)buff_ptr_phys);
+ }
+ }
+ buff_ptr += (le32_to_cpu(sgel->FlagsLength) &
+ 0x00ffffff);
+ buff_ptr_phys += (le32_to_cpu(sgel->FlagsLength) &
+ 0x00ffffff);
+ if ((le32_to_cpu(sgel->FlagsLength) &
+ (MPI2_SGE_FLAGS_END_OF_BUFFER
+ << MPI2_SGE_FLAGS_SHIFT)))
+ goto eob_clone_chain;
+ else {
+ /*
+ * Every single element in MPT will have
+ * associated sg_next. Better to sanity that
+ * sg_next is not NULL, but it will be a bug
+ * if it is null.
+ */
+ if (is_scsiio_req) {
+ sg_scmd = sg_next(sg_scmd);
+ if (sg_scmd)
+ sgel++;
+ else
+ goto eob_clone_chain;
+ }
+ }
+ break;
+ }
+ }
+
+eob_clone_chain:
+ for (i = 0; i < sge_chain_count; i++) {
+ if (is_scsiio_req)
+ _base_clone_to_sys_mem(dst_chain_addr[i],
+ src_chain_addr[i], ioc->request_sz);
+ }
+}
+
+/**
+ * mpt3sas_remove_dead_ioc_func - kthread context to remove dead ioc
+ * @arg: input argument, used to derive ioc
+ *
+ * Return:
+ * 0 if controller is removed from pci subsystem.
+ * -1 for other case.
+ */
+static int mpt3sas_remove_dead_ioc_func(void *arg)
+{
+ struct MPT3SAS_ADAPTER *ioc = (struct MPT3SAS_ADAPTER *)arg;
+ struct pci_dev *pdev;
+
+ if (!ioc)
+ return -1;
+
+ pdev = ioc->pdev;
+ if (!pdev)
+ return -1;
+ pci_stop_and_remove_bus_device_locked(pdev);
+ return 0;
+}
+
+/**
+ * _base_sync_drv_fw_timestamp - Sync Drive-Fw TimeStamp.
+ * @ioc: Per Adapter Object
+ *
+ * Return: nothing.
+ */
+static void _base_sync_drv_fw_timestamp(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi26IoUnitControlRequest_t *mpi_request;
+ Mpi26IoUnitControlReply_t *mpi_reply;
+ u16 smid;
+ ktime_t current_time;
+ u64 TimeStamp = 0;
+ u8 issue_reset = 0;
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "scsih_cmd in use %s\n", __func__);
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT3_CMD_PENDING;
+ smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "Failed obtaining a smid %s\n", __func__);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+ }
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi26IoUnitControlRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_IO_UNIT_CONTROL;
+ mpi_request->Operation = MPI26_CTRL_OP_SET_IOC_PARAMETER;
+ mpi_request->IOCParameter = MPI26_SET_IOC_PARAMETER_SYNC_TIMESTAMP;
+ current_time = ktime_get_real();
+ TimeStamp = ktime_to_ms(current_time);
+ mpi_request->Reserved7 = cpu_to_le32(TimeStamp >> 32);
+ mpi_request->IOCParameterValue = cpu_to_le32(TimeStamp & 0xFFFFFFFF);
+ init_completion(&ioc->scsih_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ dinitprintk(ioc, ioc_info(ioc,
+ "Io Unit Control Sync TimeStamp (sending), @time %lld ms\n",
+ TimeStamp));
+ wait_for_completion_timeout(&ioc->scsih_cmds.done,
+ MPT3SAS_TIMESYNC_TIMEOUT_SECONDS*HZ);
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->scsih_cmds.status, mpi_request,
+ sizeof(Mpi2SasIoUnitControlRequest_t)/4, issue_reset);
+ goto issue_host_reset;
+ }
+ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
+ mpi_reply = ioc->scsih_cmds.reply;
+ dinitprintk(ioc, ioc_info(ioc,
+ "Io Unit Control sync timestamp (complete): ioc_status(0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo)));
+ }
+issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+out:
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+/**
+ * _base_fault_reset_work - workq handling ioc fault conditions
+ * @work: input argument, used to derive ioc
+ *
+ * Context: sleep.
+ */
+static void
+_base_fault_reset_work(struct work_struct *work)
+{
+ struct MPT3SAS_ADAPTER *ioc =
+ container_of(work, struct MPT3SAS_ADAPTER, fault_reset_work.work);
+ unsigned long flags;
+ u32 doorbell;
+ int rc;
+ struct task_struct *p;
+
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if ((ioc->shost_recovery && (ioc->ioc_coredump_loop == 0)) ||
+ ioc->pci_error_recovery)
+ goto rearm_timer;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ doorbell = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) {
+ ioc_err(ioc, "SAS host is non-operational !!!!\n");
+
+ /* It may be possible that EEH recovery can resolve some of
+ * pci bus failure issues rather removing the dead ioc function
+ * by considering controller is in a non-operational state. So
+ * here priority is given to the EEH recovery. If it doesn't
+ * not resolve this issue, mpt3sas driver will consider this
+ * controller to non-operational state and remove the dead ioc
+ * function.
+ */
+ if (ioc->non_operational_loop++ < 5) {
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock,
+ flags);
+ goto rearm_timer;
+ }
+
+ /*
+ * Call _scsih_flush_pending_cmds callback so that we flush all
+ * pending commands back to OS. This call is required to avoid
+ * deadlock at block layer. Dead IOC will fail to do diag reset,
+ * and this call is safe since dead ioc will never return any
+ * command back from HW.
+ */
+ mpt3sas_base_pause_mq_polling(ioc);
+ ioc->schedule_dead_ioc_flush_running_cmds(ioc);
+ /*
+ * Set remove_host flag early since kernel thread will
+ * take some time to execute.
+ */
+ ioc->remove_host = 1;
+ /*Remove the Dead Host */
+ p = kthread_run(mpt3sas_remove_dead_ioc_func, ioc,
+ "%s_dead_ioc_%d", ioc->driver_name, ioc->id);
+ if (IS_ERR(p))
+ ioc_err(ioc, "%s: Running mpt3sas_dead_ioc thread failed !!!!\n",
+ __func__);
+ else
+ ioc_err(ioc, "%s: Running mpt3sas_dead_ioc thread success !!!!\n",
+ __func__);
+ return; /* don't rearm timer */
+ }
+
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_COREDUMP) {
+ u8 timeout = (ioc->manu_pg11.CoreDumpTOSec) ?
+ ioc->manu_pg11.CoreDumpTOSec :
+ MPT3SAS_DEFAULT_COREDUMP_TIMEOUT_SECONDS;
+
+ timeout /= (FAULT_POLLING_INTERVAL/1000);
+
+ if (ioc->ioc_coredump_loop == 0) {
+ mpt3sas_print_coredump_info(ioc,
+ doorbell & MPI2_DOORBELL_DATA_MASK);
+ /* do not accept any IOs and disable the interrupts */
+ spin_lock_irqsave(
+ &ioc->ioc_reset_in_progress_lock, flags);
+ ioc->shost_recovery = 1;
+ spin_unlock_irqrestore(
+ &ioc->ioc_reset_in_progress_lock, flags);
+ mpt3sas_base_mask_interrupts(ioc);
+ mpt3sas_base_pause_mq_polling(ioc);
+ _base_clear_outstanding_commands(ioc);
+ }
+
+ ioc_info(ioc, "%s: CoreDump loop %d.",
+ __func__, ioc->ioc_coredump_loop);
+
+ /* Wait until CoreDump completes or times out */
+ if (ioc->ioc_coredump_loop++ < timeout) {
+ spin_lock_irqsave(
+ &ioc->ioc_reset_in_progress_lock, flags);
+ goto rearm_timer;
+ }
+ }
+
+ if (ioc->ioc_coredump_loop) {
+ if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_COREDUMP)
+ ioc_err(ioc, "%s: CoreDump completed. LoopCount: %d",
+ __func__, ioc->ioc_coredump_loop);
+ else
+ ioc_err(ioc, "%s: CoreDump Timed out. LoopCount: %d",
+ __func__, ioc->ioc_coredump_loop);
+ ioc->ioc_coredump_loop = MPT3SAS_COREDUMP_LOOP_DONE;
+ }
+ ioc->non_operational_loop = 0;
+ if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) {
+ rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ ioc_warn(ioc, "%s: hard reset: %s\n",
+ __func__, rc == 0 ? "success" : "failed");
+ doorbell = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, doorbell &
+ MPI2_DOORBELL_DATA_MASK);
+ } else if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP)
+ mpt3sas_print_coredump_info(ioc, doorbell &
+ MPI2_DOORBELL_DATA_MASK);
+ if (rc && (doorbell & MPI2_IOC_STATE_MASK) !=
+ MPI2_IOC_STATE_OPERATIONAL)
+ return; /* don't rearm timer */
+ }
+ ioc->ioc_coredump_loop = 0;
+ if (ioc->time_sync_interval &&
+ ++ioc->timestamp_update_count >= ioc->time_sync_interval) {
+ ioc->timestamp_update_count = 0;
+ _base_sync_drv_fw_timestamp(ioc);
+ }
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ rearm_timer:
+ if (ioc->fault_reset_work_q)
+ queue_delayed_work(ioc->fault_reset_work_q,
+ &ioc->fault_reset_work,
+ msecs_to_jiffies(FAULT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+}
+
+/**
+ * mpt3sas_base_start_watchdog - start the fault_reset_work_q
+ * @ioc: per adapter object
+ *
+ * Context: sleep.
+ */
+void
+mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+
+ if (ioc->fault_reset_work_q)
+ return;
+
+ ioc->timestamp_update_count = 0;
+ /* initialize fault polling */
+
+ INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work);
+ snprintf(ioc->fault_reset_work_q_name,
+ sizeof(ioc->fault_reset_work_q_name), "poll_%s%d_status",
+ ioc->driver_name, ioc->id);
+ ioc->fault_reset_work_q = alloc_ordered_workqueue(
+ "%s", WQ_MEM_RECLAIM, ioc->fault_reset_work_q_name);
+ if (!ioc->fault_reset_work_q) {
+ ioc_err(ioc, "%s: failed (line=%d)\n", __func__, __LINE__);
+ return;
+ }
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->fault_reset_work_q)
+ queue_delayed_work(ioc->fault_reset_work_q,
+ &ioc->fault_reset_work,
+ msecs_to_jiffies(FAULT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+}
+
+/**
+ * mpt3sas_base_stop_watchdog - stop the fault_reset_work_q
+ * @ioc: per adapter object
+ *
+ * Context: sleep.
+ */
+void
+mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+ struct workqueue_struct *wq;
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ wq = ioc->fault_reset_work_q;
+ ioc->fault_reset_work_q = NULL;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (wq) {
+ if (!cancel_delayed_work_sync(&ioc->fault_reset_work))
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ }
+}
+
+/**
+ * mpt3sas_base_fault_info - verbose translation of firmware FAULT code
+ * @ioc: per adapter object
+ * @fault_code: fault code
+ */
+void
+mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc, u16 fault_code)
+{
+ ioc_err(ioc, "fault_state(0x%04x)!\n", fault_code);
+}
+
+/**
+ * mpt3sas_base_coredump_info - verbose translation of firmware CoreDump state
+ * @ioc: per adapter object
+ * @fault_code: fault code
+ *
+ * Return: nothing.
+ */
+void
+mpt3sas_base_coredump_info(struct MPT3SAS_ADAPTER *ioc, u16 fault_code)
+{
+ ioc_err(ioc, "coredump_state(0x%04x)!\n", fault_code);
+}
+
+/**
+ * mpt3sas_base_wait_for_coredump_completion - Wait until coredump
+ * completes or times out
+ * @ioc: per adapter object
+ * @caller: caller function name
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_wait_for_coredump_completion(struct MPT3SAS_ADAPTER *ioc,
+ const char *caller)
+{
+ u8 timeout = (ioc->manu_pg11.CoreDumpTOSec) ?
+ ioc->manu_pg11.CoreDumpTOSec :
+ MPT3SAS_DEFAULT_COREDUMP_TIMEOUT_SECONDS;
+
+ int ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_FAULT,
+ timeout);
+
+ if (ioc_state)
+ ioc_err(ioc,
+ "%s: CoreDump timed out. (ioc_state=0x%x)\n",
+ caller, ioc_state);
+ else
+ ioc_info(ioc,
+ "%s: CoreDump completed. (ioc_state=0x%x)\n",
+ caller, ioc_state);
+
+ return ioc_state;
+}
+
+/**
+ * mpt3sas_halt_firmware - halt's mpt controller firmware
+ * @ioc: per adapter object
+ *
+ * For debugging timeout related issues. Writing 0xCOFFEE00
+ * to the doorbell register will halt controller firmware. With
+ * the purpose to stop both driver and firmware, the enduser can
+ * obtain a ring buffer from controller UART.
+ */
+void
+mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 doorbell;
+
+ if (!ioc->fwfault_debug)
+ return;
+
+ dump_stack();
+
+ doorbell = ioc->base_readl_ext_retry(&ioc->chip->Doorbell);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, doorbell &
+ MPI2_DOORBELL_DATA_MASK);
+ } else if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ mpt3sas_print_coredump_info(ioc, doorbell &
+ MPI2_DOORBELL_DATA_MASK);
+ } else {
+ writel(0xC0FFEE00, &ioc->chip->Doorbell);
+ ioc_err(ioc, "Firmware is halted due to command timeout\n");
+ }
+
+ if (ioc->fwfault_debug == 2)
+ for (;;)
+ ;
+ else
+ panic("panic in %s\n", __func__);
+}
+
+/**
+ * _base_sas_ioc_info - verbose translation of the ioc status
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @request_hdr: request mf
+ */
+static void
+_base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
+ MPI2RequestHeader_t *request_hdr)
+{
+ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ char *desc = NULL;
+ u16 frame_sz;
+ char *func_str = NULL;
+
+ /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */
+ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
+ request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION)
+ return;
+
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ return;
+ /*
+ * Older Firmware version doesn't support driver trigger pages.
+ * So, skip displaying 'config invalid type' type
+ * of error message.
+ */
+ if (request_hdr->Function == MPI2_FUNCTION_CONFIG) {
+ Mpi2ConfigRequest_t *rqst = (Mpi2ConfigRequest_t *)request_hdr;
+
+ if ((rqst->ExtPageType ==
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER) &&
+ !(ioc->logging_level & MPT_DEBUG_CONFIG)) {
+ return;
+ }
+ }
+
+ switch (ioc_status) {
+
+/****************************************************************************
+* Common IOCStatus values for all replies
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ desc = "invalid function";
+ break;
+ case MPI2_IOCSTATUS_BUSY:
+ desc = "busy";
+ break;
+ case MPI2_IOCSTATUS_INVALID_SGL:
+ desc = "invalid sgl";
+ break;
+ case MPI2_IOCSTATUS_INTERNAL_ERROR:
+ desc = "internal error";
+ break;
+ case MPI2_IOCSTATUS_INVALID_VPID:
+ desc = "invalid vpid";
+ break;
+ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ desc = "insufficient resources";
+ break;
+ case MPI2_IOCSTATUS_INSUFFICIENT_POWER:
+ desc = "insufficient power";
+ break;
+ case MPI2_IOCSTATUS_INVALID_FIELD:
+ desc = "invalid field";
+ break;
+ case MPI2_IOCSTATUS_INVALID_STATE:
+ desc = "invalid state";
+ break;
+ case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED:
+ desc = "op state not supported";
+ break;
+
+/****************************************************************************
+* Config IOCStatus values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION:
+ desc = "config invalid action";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE:
+ desc = "config invalid type";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE:
+ desc = "config invalid page";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_DATA:
+ desc = "config invalid data";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS:
+ desc = "config no defaults";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT:
+ desc = "config can't commit";
+ break;
+
+/****************************************************************************
+* SCSI IO Reply
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ break;
+
+/****************************************************************************
+* For use by SCSI Initiator and SCSI Target end-to-end data protection
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ desc = "eedp guard error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ desc = "eedp ref tag error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ desc = "eedp app tag error";
+ break;
+
+/****************************************************************************
+* SCSI Target values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX:
+ desc = "target invalid io index";
+ break;
+ case MPI2_IOCSTATUS_TARGET_ABORTED:
+ desc = "target aborted";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE:
+ desc = "target no conn retryable";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NO_CONNECTION:
+ desc = "target no connection";
+ break;
+ case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH:
+ desc = "target xfer count mismatch";
+ break;
+ case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR:
+ desc = "target data offset error";
+ break;
+ case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA:
+ desc = "target too much write data";
+ break;
+ case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT:
+ desc = "target iu too short";
+ break;
+ case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT:
+ desc = "target ack nak timeout";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED:
+ desc = "target nak received";
+ break;
+
+/****************************************************************************
+* Serial Attached SCSI values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED:
+ desc = "smp request failed";
+ break;
+ case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN:
+ desc = "smp data overrun";
+ break;
+
+/****************************************************************************
+* Diagnostic Buffer Post / Diagnostic Release values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED:
+ desc = "diagnostic released";
+ break;
+ default:
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ switch (request_hdr->Function) {
+ case MPI2_FUNCTION_CONFIG:
+ frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size;
+ func_str = "config_page";
+ break;
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t);
+ func_str = "task_mgmt";
+ break;
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t);
+ func_str = "sas_iounit_ctl";
+ break;
+ case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR:
+ frame_sz = sizeof(Mpi2SepRequest_t);
+ func_str = "enclosure";
+ break;
+ case MPI2_FUNCTION_IOC_INIT:
+ frame_sz = sizeof(Mpi2IOCInitRequest_t);
+ func_str = "ioc_init";
+ break;
+ case MPI2_FUNCTION_PORT_ENABLE:
+ frame_sz = sizeof(Mpi2PortEnableRequest_t);
+ func_str = "port_enable";
+ break;
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size;
+ func_str = "smp_passthru";
+ break;
+ case MPI2_FUNCTION_NVME_ENCAPSULATED:
+ frame_sz = sizeof(Mpi26NVMeEncapsulatedRequest_t) +
+ ioc->sge_size;
+ func_str = "nvme_encapsulated";
+ break;
+ case MPI2_FUNCTION_MCTP_PASSTHROUGH:
+ frame_sz = sizeof(Mpi26MctpPassthroughRequest_t) +
+ ioc->sge_size;
+ func_str = "mctp_passthru";
+ break;
+ default:
+ frame_sz = 32;
+ func_str = "unknown";
+ break;
+ }
+
+ ioc_warn(ioc, "ioc_status: %s(0x%04x), request(0x%p),(%s)\n",
+ desc, ioc_status, request_hdr, func_str);
+
+ _debug_dump_mf(request_hdr, frame_sz/4);
+}
+
+/**
+ * _base_display_event_data - verbose translation of firmware asyn events
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ */
+static void
+_base_display_event_data(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply)
+{
+ char *desc = NULL;
+ u16 event;
+
+ if (!(ioc->logging_level & MPT_DEBUG_EVENTS))
+ return;
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ switch (event) {
+ case MPI2_EVENT_LOG_DATA:
+ desc = "Log Data";
+ break;
+ case MPI2_EVENT_STATE_CHANGE:
+ desc = "Status Change";
+ break;
+ case MPI2_EVENT_HARD_RESET_RECEIVED:
+ desc = "Hard Reset Received";
+ break;
+ case MPI2_EVENT_EVENT_CHANGE:
+ desc = "Event Change";
+ break;
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ desc = "Device Status Change";
+ break;
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ if (!ioc->hide_ir_msg)
+ desc = "IR Operation Status";
+ break;
+ case MPI2_EVENT_SAS_DISCOVERY:
+ {
+ Mpi2EventDataSasDiscovery_t *event_data =
+ (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData;
+ ioc_info(ioc, "Discovery: (%s)",
+ event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED ?
+ "start" : "stop");
+ if (event_data->DiscoveryStatus)
+ pr_cont(" discovery_status(0x%08x)",
+ le32_to_cpu(event_data->DiscoveryStatus));
+ pr_cont("\n");
+ return;
+ }
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ desc = "SAS Broadcast Primitive";
+ break;
+ case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
+ desc = "SAS Init Device Status Change";
+ break;
+ case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW:
+ desc = "SAS Init Table Overflow";
+ break;
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ desc = "SAS Topology Change List";
+ break;
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ desc = "SAS Enclosure Device Status Change";
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ if (!ioc->hide_ir_msg)
+ desc = "IR Volume";
+ break;
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ if (!ioc->hide_ir_msg)
+ desc = "IR Physical Disk";
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ if (!ioc->hide_ir_msg)
+ desc = "IR Configuration Change List";
+ break;
+ case MPI2_EVENT_LOG_ENTRY_ADDED:
+ if (!ioc->hide_ir_msg)
+ desc = "Log Entry Added";
+ break;
+ case MPI2_EVENT_TEMP_THRESHOLD:
+ desc = "Temperature Threshold";
+ break;
+ case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION:
+ desc = "Cable Event";
+ break;
+ case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
+ desc = "SAS Device Discovery Error";
+ break;
+ case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE:
+ desc = "PCIE Device Status Change";
+ break;
+ case MPI2_EVENT_PCIE_ENUMERATION:
+ {
+ Mpi26EventDataPCIeEnumeration_t *event_data =
+ (Mpi26EventDataPCIeEnumeration_t *)mpi_reply->EventData;
+ ioc_info(ioc, "PCIE Enumeration: (%s)",
+ event_data->ReasonCode == MPI26_EVENT_PCIE_ENUM_RC_STARTED ?
+ "start" : "stop");
+ if (event_data->EnumerationStatus)
+ pr_cont("enumeration_status(0x%08x)",
+ le32_to_cpu(event_data->EnumerationStatus));
+ pr_cont("\n");
+ return;
+ }
+ case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ desc = "PCIE Topology Change List";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ ioc_info(ioc, "%s\n", desc);
+}
+
+/**
+ * _base_sas_log_info - verbose translation of firmware log info
+ * @ioc: per adapter object
+ * @log_info: log info
+ */
+static void
+_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc, u32 log_info)
+{
+ union loginfo_type {
+ u32 loginfo;
+ struct {
+ u32 subcode:16;
+ u32 code:8;
+ u32 originator:4;
+ u32 bus_type:4;
+ } dw;
+ };
+ union loginfo_type sas_loginfo;
+ char *originator_str = NULL;
+
+ sas_loginfo.loginfo = log_info;
+ if (sas_loginfo.dw.bus_type != 3 /*SAS*/)
+ return;
+
+ /* each nexus loss loginfo */
+ if (log_info == 0x31170000)
+ return;
+
+ /* eat the loginfos associated with task aborts */
+ if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info ==
+ 0x31140000 || log_info == 0x31130000))
+ return;
+
+ switch (sas_loginfo.dw.originator) {
+ case 0:
+ originator_str = "IOP";
+ break;
+ case 1:
+ originator_str = "PL";
+ break;
+ case 2:
+ if (!ioc->hide_ir_msg)
+ originator_str = "IR";
+ else
+ originator_str = "WarpDrive";
+ break;
+ }
+
+ ioc_warn(ioc, "log_info(0x%08x): originator(%s), code(0x%02x), sub_code(0x%04x)\n",
+ log_info,
+ originator_str, sas_loginfo.dw.code, sas_loginfo.dw.subcode);
+}
+
+/**
+ * _base_display_reply_info - handle reply descriptors depending on IOC Status
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame (lower 32bit addr)
+ */
+static void
+_base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+ u16 ioc_status;
+ u32 loginfo = 0;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (unlikely(!mpi_reply)) {
+ ioc_err(ioc, "mpi_reply not valid at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+
+ if ((ioc_status & MPI2_IOCSTATUS_MASK) &&
+ (ioc->logging_level & MPT_DEBUG_REPLY)) {
+ _base_sas_ioc_info(ioc, mpi_reply,
+ mpt3sas_base_get_msg_frame(ioc, smid));
+ }
+
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) {
+ loginfo = le32_to_cpu(mpi_reply->IOCLogInfo);
+ if (ioc->logging_level & MPT_DEBUG_REPLY)
+ _base_sas_log_info(ioc, loginfo);
+ else {
+ if (!((ioc_status & MPI2_IOCSTATUS_MASK) &
+ MPI2_IOCSTATUS_CONFIG_INVALID_PAGE))
+ _base_sas_log_info(ioc, loginfo);
+ }
+ }
+
+ if (ioc_status || loginfo) {
+ ioc_status &= MPI2_IOCSTATUS_MASK;
+ mpt3sas_trigger_mpi(ioc, ioc_status, loginfo);
+ }
+}
+
+/**
+ * mpt3sas_base_done - base internal command completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return:
+ * 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK)
+ return mpt3sas_check_for_pending_internal_cmds(ioc, smid);
+
+ if (ioc->base_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+
+ ioc->base_cmds.status |= MPT3_CMD_COMPLETE;
+ if (mpi_reply) {
+ ioc->base_cmds.status |= MPT3_CMD_REPLY_VALID;
+ memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ }
+ ioc->base_cmds.status &= ~MPT3_CMD_PENDING;
+
+ complete(&ioc->base_cmds.done);
+ return 1;
+}
+
+/**
+ * _base_async_event - main callback handler for firmware asyn events
+ * @ioc: per adapter object
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return:
+ * 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+ Mpi2EventAckRequest_t *ack_request;
+ u16 smid;
+ struct _event_ack_list *delayed_event_ack;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (!mpi_reply)
+ return 1;
+ if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION)
+ return 1;
+
+ _base_display_event_data(ioc, mpi_reply);
+
+ if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED))
+ goto out;
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ delayed_event_ack = kzalloc(sizeof(*delayed_event_ack),
+ GFP_ATOMIC);
+ if (!delayed_event_ack)
+ goto out;
+ INIT_LIST_HEAD(&delayed_event_ack->list);
+ delayed_event_ack->Event = mpi_reply->Event;
+ delayed_event_ack->EventContext = mpi_reply->EventContext;
+ list_add_tail(&delayed_event_ack->list,
+ &ioc->delayed_event_ack_list);
+ dewtprintk(ioc,
+ ioc_info(ioc, "DELAYED: EVENT ACK: event (0x%04x)\n",
+ le16_to_cpu(mpi_reply->Event)));
+ goto out;
+ }
+
+ ack_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t));
+ ack_request->Function = MPI2_FUNCTION_EVENT_ACK;
+ ack_request->Event = mpi_reply->Event;
+ ack_request->EventContext = mpi_reply->EventContext;
+ ack_request->VF_ID = 0; /* TODO */
+ ack_request->VP_ID = 0;
+ ioc->put_smid_default(ioc, smid);
+
+ out:
+
+ /* scsih callback handler */
+ mpt3sas_scsih_event_callback(ioc, msix_index, reply);
+
+ /* ctl callback handler */
+ mpt3sas_ctl_event_callback(ioc, msix_index, reply);
+
+ return 1;
+}
+
+static struct scsiio_tracker *
+_get_st_from_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ struct scsi_cmnd *cmd;
+
+ if (WARN_ON(!smid) ||
+ WARN_ON(smid >= ioc->hi_priority_smid))
+ return NULL;
+
+ cmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (cmd)
+ return scsi_cmd_priv(cmd);
+
+ return NULL;
+}
+
+/**
+ * _base_get_cb_idx - obtain the callback index
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: callback index.
+ */
+static u8
+_base_get_cb_idx(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ int i;
+ u16 ctl_smid = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT + 1;
+ u8 cb_idx = 0xFF;
+
+ if (smid < ioc->hi_priority_smid) {
+ struct scsiio_tracker *st;
+
+ if (smid < ctl_smid) {
+ st = _get_st_from_smid(ioc, smid);
+ if (st)
+ cb_idx = st->cb_idx;
+ } else if (smid == ctl_smid)
+ cb_idx = ioc->ctl_cb_idx;
+ } else if (smid < ioc->internal_smid) {
+ i = smid - ioc->hi_priority_smid;
+ cb_idx = ioc->hpr_lookup[i].cb_idx;
+ } else if (smid <= ioc->hba_queue_depth) {
+ i = smid - ioc->internal_smid;
+ cb_idx = ioc->internal_lookup[i].cb_idx;
+ }
+ return cb_idx;
+}
+
+/**
+ * mpt3sas_base_pause_mq_polling - pause polling on the mq poll queues
+ * when driver is flushing out the IOs.
+ * @ioc: per adapter object
+ *
+ * Pause polling on the mq poll (io uring) queues when driver is flushing
+ * out the IOs. Otherwise we may see the race condition of completing the same
+ * IO from two paths.
+ *
+ * Returns nothing.
+ */
+void
+mpt3sas_base_pause_mq_polling(struct MPT3SAS_ADAPTER *ioc)
+{
+ int iopoll_q_count =
+ ioc->reply_queue_count - ioc->iopoll_q_start_index;
+ int qid;
+
+ for (qid = 0; qid < iopoll_q_count; qid++)
+ atomic_set(&ioc->io_uring_poll_queues[qid].pause, 1);
+
+ /*
+ * wait for current poll to complete.
+ */
+ for (qid = 0; qid < iopoll_q_count; qid++) {
+ while (atomic_read(&ioc->io_uring_poll_queues[qid].busy)) {
+ cpu_relax();
+ udelay(500);
+ }
+ }
+}
+
+/**
+ * mpt3sas_base_resume_mq_polling - Resume polling on mq poll queues.
+ * @ioc: per adapter object
+ *
+ * Returns nothing.
+ */
+void
+mpt3sas_base_resume_mq_polling(struct MPT3SAS_ADAPTER *ioc)
+{
+ int iopoll_q_count =
+ ioc->reply_queue_count - ioc->iopoll_q_start_index;
+ int qid;
+
+ for (qid = 0; qid < iopoll_q_count; qid++)
+ atomic_set(&ioc->io_uring_poll_queues[qid].pause, 0);
+}
+
+/**
+ * mpt3sas_base_mask_interrupts - disable interrupts
+ * @ioc: per adapter object
+ *
+ * Disabling ResetIRQ, Reply and Doorbell Interrupts
+ */
+void
+mpt3sas_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 him_register;
+
+ ioc->mask_interrupts = 1;
+ him_register = ioc->base_readl(&ioc->chip->HostInterruptMask);
+ him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK;
+ writel(him_register, &ioc->chip->HostInterruptMask);
+ ioc->base_readl(&ioc->chip->HostInterruptMask);
+}
+
+/**
+ * mpt3sas_base_unmask_interrupts - enable interrupts
+ * @ioc: per adapter object
+ *
+ * Enabling only Reply Interrupts
+ */
+void
+mpt3sas_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 him_register;
+
+ him_register = ioc->base_readl(&ioc->chip->HostInterruptMask);
+ him_register &= ~MPI2_HIM_RIM;
+ writel(him_register, &ioc->chip->HostInterruptMask);
+ ioc->mask_interrupts = 0;
+}
+
+union reply_descriptor {
+ u64 word;
+ struct {
+ u32 low;
+ u32 high;
+ } u;
+};
+
+static u32 base_mod64(u64 dividend, u32 divisor)
+{
+ u32 remainder;
+
+ if (!divisor)
+ pr_err("mpt3sas: DIVISOR is zero, in div fn\n");
+ remainder = do_div(dividend, divisor);
+ return remainder;
+}
+
+/**
+ * _base_process_reply_queue - Process reply descriptors from reply
+ * descriptor post queue.
+ * @reply_q: per IRQ's reply queue object.
+ *
+ * Return: number of reply descriptors processed from reply
+ * descriptor queue.
+ */
+static int
+_base_process_reply_queue(struct adapter_reply_queue *reply_q)
+{
+ union reply_descriptor rd;
+ u64 completed_cmds;
+ u8 request_descript_type;
+ u16 smid;
+ u8 cb_idx;
+ u32 reply;
+ u8 msix_index = reply_q->msix_index;
+ struct MPT3SAS_ADAPTER *ioc = reply_q->ioc;
+ Mpi2ReplyDescriptorsUnion_t *rpf;
+ u8 rc;
+
+ completed_cmds = 0;
+ if (!atomic_add_unless(&reply_q->busy, 1, 1))
+ return completed_cmds;
+
+ rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index];
+ request_descript_type = rpf->Default.ReplyFlags
+ & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ if (request_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) {
+ atomic_dec(&reply_q->busy);
+ return completed_cmds;
+ }
+
+ cb_idx = 0xFF;
+ do {
+ rd.word = le64_to_cpu(rpf->Words);
+ if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX)
+ goto out;
+ reply = 0;
+ smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1);
+ if (request_descript_type ==
+ MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS ||
+ request_descript_type ==
+ MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS ||
+ request_descript_type ==
+ MPI26_RPY_DESCRIPT_FLAGS_PCIE_ENCAPSULATED_SUCCESS) {
+ cb_idx = _base_get_cb_idx(ioc, smid);
+ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) &&
+ (likely(mpt_callbacks[cb_idx] != NULL))) {
+ rc = mpt_callbacks[cb_idx](ioc, smid,
+ msix_index, 0);
+ if (rc)
+ mpt3sas_base_free_smid(ioc, smid);
+ }
+ } else if (request_descript_type ==
+ MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) {
+ reply = le32_to_cpu(
+ rpf->AddressReply.ReplyFrameAddress);
+ if (reply > ioc->reply_dma_max_address ||
+ reply < ioc->reply_dma_min_address)
+ reply = 0;
+ if (smid) {
+ cb_idx = _base_get_cb_idx(ioc, smid);
+ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) &&
+ (likely(mpt_callbacks[cb_idx] != NULL))) {
+ rc = mpt_callbacks[cb_idx](ioc, smid,
+ msix_index, reply);
+ if (reply)
+ _base_display_reply_info(ioc,
+ smid, msix_index, reply);
+ if (rc)
+ mpt3sas_base_free_smid(ioc,
+ smid);
+ }
+ } else {
+ _base_async_event(ioc, msix_index, reply);
+ }
+
+ /* reply free queue handling */
+ if (reply) {
+ ioc->reply_free_host_index =
+ (ioc->reply_free_host_index ==
+ (ioc->reply_free_queue_depth - 1)) ?
+ 0 : ioc->reply_free_host_index + 1;
+ ioc->reply_free[ioc->reply_free_host_index] =
+ cpu_to_le32(reply);
+ if (ioc->is_mcpu_endpoint)
+ _base_clone_reply_to_sys_mem(ioc,
+ reply,
+ ioc->reply_free_host_index);
+ writel(ioc->reply_free_host_index,
+ &ioc->chip->ReplyFreeHostIndex);
+ }
+ }
+
+ rpf->Words = cpu_to_le64(ULLONG_MAX);
+ reply_q->reply_post_host_index =
+ (reply_q->reply_post_host_index ==
+ (ioc->reply_post_queue_depth - 1)) ? 0 :
+ reply_q->reply_post_host_index + 1;
+ request_descript_type =
+ reply_q->reply_post_free[reply_q->reply_post_host_index].
+ Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ completed_cmds++;
+ /* Update the reply post host index after continuously
+ * processing the threshold number of Reply Descriptors.
+ * So that FW can find enough entries to post the Reply
+ * Descriptors in the reply descriptor post queue.
+ */
+ if (completed_cmds >= ioc->thresh_hold) {
+ if (ioc->combined_reply_queue) {
+ writel(reply_q->reply_post_host_index |
+ ((msix_index & 7) <<
+ MPI2_RPHI_MSIX_INDEX_SHIFT),
+ ioc->replyPostRegisterIndex[msix_index/8]);
+ } else {
+ writel(reply_q->reply_post_host_index |
+ (msix_index <<
+ MPI2_RPHI_MSIX_INDEX_SHIFT),
+ &ioc->chip->ReplyPostHostIndex);
+ }
+ if (!reply_q->is_iouring_poll_q &&
+ !reply_q->irq_poll_scheduled) {
+ reply_q->irq_poll_scheduled = true;
+ irq_poll_sched(&reply_q->irqpoll);
+ }
+ atomic_dec(&reply_q->busy);
+ return completed_cmds;
+ }
+ if (request_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+ goto out;
+ if (!reply_q->reply_post_host_index)
+ rpf = reply_q->reply_post_free;
+ else
+ rpf++;
+ } while (1);
+
+ out:
+
+ if (!completed_cmds) {
+ atomic_dec(&reply_q->busy);
+ return completed_cmds;
+ }
+
+ if (ioc->is_warpdrive) {
+ writel(reply_q->reply_post_host_index,
+ ioc->reply_post_host_index[msix_index]);
+ atomic_dec(&reply_q->busy);
+ return completed_cmds;
+ }
+
+ /* Update Reply Post Host Index.
+ * For those HBA's which support combined reply queue feature
+ * 1. Get the correct Supplemental Reply Post Host Index Register.
+ * i.e. (msix_index / 8)th entry from Supplemental Reply Post Host
+ * Index Register address bank i.e replyPostRegisterIndex[],
+ * 2. Then update this register with new reply host index value
+ * in ReplyPostIndex field and the MSIxIndex field with
+ * msix_index value reduced to a value between 0 and 7,
+ * using a modulo 8 operation. Since each Supplemental Reply Post
+ * Host Index Register supports 8 MSI-X vectors.
+ *
+ * For other HBA's just update the Reply Post Host Index register with
+ * new reply host index value in ReplyPostIndex Field and msix_index
+ * value in MSIxIndex field.
+ */
+ if (ioc->combined_reply_queue)
+ writel(reply_q->reply_post_host_index | ((msix_index & 7) <<
+ MPI2_RPHI_MSIX_INDEX_SHIFT),
+ ioc->replyPostRegisterIndex[msix_index/8]);
+ else
+ writel(reply_q->reply_post_host_index | (msix_index <<
+ MPI2_RPHI_MSIX_INDEX_SHIFT),
+ &ioc->chip->ReplyPostHostIndex);
+ atomic_dec(&reply_q->busy);
+ return completed_cmds;
+}
+
+/**
+ * mpt3sas_blk_mq_poll - poll the blk mq poll queue
+ * @shost: Scsi_Host object
+ * @queue_num: hw ctx queue number
+ *
+ * Return number of entries that has been processed from poll queue.
+ */
+int mpt3sas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num)
+{
+ struct MPT3SAS_ADAPTER *ioc =
+ (struct MPT3SAS_ADAPTER *)shost->hostdata;
+ struct adapter_reply_queue *reply_q;
+ int num_entries = 0;
+ int qid = queue_num - ioc->iopoll_q_start_index;
+
+ if (atomic_read(&ioc->io_uring_poll_queues[qid].pause) ||
+ !atomic_add_unless(&ioc->io_uring_poll_queues[qid].busy, 1, 1))
+ return 0;
+
+ reply_q = ioc->io_uring_poll_queues[qid].reply_q;
+
+ num_entries = _base_process_reply_queue(reply_q);
+ atomic_dec(&ioc->io_uring_poll_queues[qid].busy);
+
+ return num_entries;
+}
+
+/**
+ * _base_interrupt - MPT adapter (IOC) specific interrupt handler.
+ * @irq: irq number (not used)
+ * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure
+ *
+ * Return: IRQ_HANDLED if processed, else IRQ_NONE.
+ */
+static irqreturn_t
+_base_interrupt(int irq, void *bus_id)
+{
+ struct adapter_reply_queue *reply_q = bus_id;
+ struct MPT3SAS_ADAPTER *ioc = reply_q->ioc;
+
+ if (ioc->mask_interrupts)
+ return IRQ_NONE;
+ if (reply_q->irq_poll_scheduled)
+ return IRQ_HANDLED;
+ return ((_base_process_reply_queue(reply_q) > 0) ?
+ IRQ_HANDLED : IRQ_NONE);
+}
+
+/**
+ * _base_irqpoll - IRQ poll callback handler
+ * @irqpoll: irq_poll object
+ * @budget: irq poll weight
+ *
+ * Return: number of reply descriptors processed
+ */
+static int
+_base_irqpoll(struct irq_poll *irqpoll, int budget)
+{
+ struct adapter_reply_queue *reply_q;
+ int num_entries = 0;
+
+ reply_q = container_of(irqpoll, struct adapter_reply_queue,
+ irqpoll);
+ if (reply_q->irq_line_enable) {
+ disable_irq_nosync(reply_q->os_irq);
+ reply_q->irq_line_enable = false;
+ }
+ num_entries = _base_process_reply_queue(reply_q);
+ if (num_entries < budget) {
+ irq_poll_complete(irqpoll);
+ reply_q->irq_poll_scheduled = false;
+ reply_q->irq_line_enable = true;
+ enable_irq(reply_q->os_irq);
+ /*
+ * Go for one more round of processing the
+ * reply descriptor post queue in case the HBA
+ * Firmware has posted some reply descriptors
+ * while reenabling the IRQ.
+ */
+ _base_process_reply_queue(reply_q);
+ }
+
+ return num_entries;
+}
+
+/**
+ * _base_init_irqpolls - initliaze IRQ polls
+ * @ioc: per adapter object
+ *
+ * Return: nothing
+ */
+static void
+_base_init_irqpolls(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct adapter_reply_queue *reply_q, *next;
+
+ if (list_empty(&ioc->reply_queue_list))
+ return;
+
+ list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) {
+ if (reply_q->is_iouring_poll_q)
+ continue;
+ irq_poll_init(&reply_q->irqpoll,
+ ioc->hba_queue_depth/4, _base_irqpoll);
+ reply_q->irq_poll_scheduled = false;
+ reply_q->irq_line_enable = true;
+ reply_q->os_irq = pci_irq_vector(ioc->pdev,
+ reply_q->msix_index);
+ }
+}
+
+/**
+ * _base_is_controller_msix_enabled - is controller support muli-reply queues
+ * @ioc: per adapter object
+ *
+ * Return: Whether or not MSI/X is enabled.
+ */
+static inline int
+_base_is_controller_msix_enabled(struct MPT3SAS_ADAPTER *ioc)
+{
+ return (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable;
+}
+
+/**
+ * mpt3sas_base_sync_reply_irqs - flush pending MSIX interrupts
+ * @ioc: per adapter object
+ * @poll: poll over reply descriptor pools incase interrupt for
+ * timed-out SCSI command got delayed
+ * Context: non-ISR context
+ *
+ * Called when a Task Management request has completed.
+ */
+void
+mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc, u8 poll)
+{
+ struct adapter_reply_queue *reply_q;
+
+ /* If MSIX capability is turned off
+ * then multi-queues are not enabled
+ */
+ if (!_base_is_controller_msix_enabled(ioc))
+ return;
+
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery)
+ return;
+ /* TMs are on msix_index == 0 */
+ if (reply_q->msix_index == 0)
+ continue;
+
+ if (reply_q->is_iouring_poll_q) {
+ _base_process_reply_queue(reply_q);
+ continue;
+ }
+
+ synchronize_irq(pci_irq_vector(ioc->pdev, reply_q->msix_index));
+ if (reply_q->irq_poll_scheduled) {
+ /* Calling irq_poll_disable will wait for any pending
+ * callbacks to have completed.
+ */
+ irq_poll_disable(&reply_q->irqpoll);
+ irq_poll_enable(&reply_q->irqpoll);
+ /* check how the scheduled poll has ended,
+ * clean up only if necessary
+ */
+ if (reply_q->irq_poll_scheduled) {
+ reply_q->irq_poll_scheduled = false;
+ reply_q->irq_line_enable = true;
+ enable_irq(reply_q->os_irq);
+ }
+ }
+
+ if (poll)
+ _base_process_reply_queue(reply_q);
+ }
+}
+
+/**
+ * mpt3sas_base_release_callback_handler - clear interrupt callback handler
+ * @cb_idx: callback index
+ */
+void
+mpt3sas_base_release_callback_handler(u8 cb_idx)
+{
+ mpt_callbacks[cb_idx] = NULL;
+}
+
+/**
+ * mpt3sas_base_register_callback_handler - obtain index for the interrupt callback handler
+ * @cb_func: callback function
+ *
+ * Return: Index of @cb_func.
+ */
+u8
+mpt3sas_base_register_callback_handler(MPT_CALLBACK cb_func)
+{
+ u8 cb_idx;
+
+ for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--)
+ if (mpt_callbacks[cb_idx] == NULL)
+ break;
+
+ mpt_callbacks[cb_idx] = cb_func;
+ return cb_idx;
+}
+
+/**
+ * mpt3sas_base_initialize_callback_handler - initialize the interrupt callback handler
+ */
+void
+mpt3sas_base_initialize_callback_handler(void)
+{
+ u8 cb_idx;
+
+ for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++)
+ mpt3sas_base_release_callback_handler(cb_idx);
+}
+
+
+/**
+ * _base_build_zero_len_sge - build zero length sg entry
+ * @ioc: per adapter object
+ * @paddr: virtual address for SGE
+ *
+ * Create a zero length scatter gather entry to insure the IOCs hardware has
+ * something to use if the target device goes brain dead and tries
+ * to send data even when none is asked for.
+ */
+static void
+_base_build_zero_len_sge(struct MPT3SAS_ADAPTER *ioc, void *paddr)
+{
+ u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST |
+ MPI2_SGE_FLAGS_SIMPLE_ELEMENT) <<
+ MPI2_SGE_FLAGS_SHIFT);
+ ioc->base_add_sg_single(paddr, flags_length, -1);
+}
+
+/**
+ * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr.
+ * @paddr: virtual address for SGE
+ * @flags_length: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ */
+static void
+_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr)
+{
+ Mpi2SGESimple32_t *sgel = paddr;
+
+ flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING |
+ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
+ sgel->FlagsLength = cpu_to_le32(flags_length);
+ sgel->Address = cpu_to_le32(dma_addr);
+}
+
+
+/**
+ * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr.
+ * @paddr: virtual address for SGE
+ * @flags_length: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ */
+static void
+_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr)
+{
+ Mpi2SGESimple64_t *sgel = paddr;
+
+ flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
+ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
+ sgel->FlagsLength = cpu_to_le32(flags_length);
+ sgel->Address = cpu_to_le64(dma_addr);
+}
+
+/**
+ * _base_get_chain_buffer_tracker - obtain chain tracker
+ * @ioc: per adapter object
+ * @scmd: SCSI commands of the IO request
+ *
+ * Return: chain tracker from chain_lookup table using key as
+ * smid and smid's chain_offset.
+ */
+static struct chain_tracker *
+_base_get_chain_buffer_tracker(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd)
+{
+ struct chain_tracker *chain_req;
+ struct scsiio_tracker *st = scsi_cmd_priv(scmd);
+ u16 smid = st->smid;
+ u8 chain_offset =
+ atomic_read(&ioc->chain_lookup[smid - 1].chain_offset);
+
+ if (chain_offset == ioc->chains_needed_per_io)
+ return NULL;
+
+ chain_req = &ioc->chain_lookup[smid - 1].chains_per_smid[chain_offset];
+ atomic_inc(&ioc->chain_lookup[smid - 1].chain_offset);
+ return chain_req;
+}
+
+
+/**
+ * _base_build_sg - build generic sg
+ * @ioc: per adapter object
+ * @psge: virtual address for SGE
+ * @data_out_dma: physical address for WRITES
+ * @data_out_sz: data xfer size for WRITES
+ * @data_in_dma: physical address for READS
+ * @data_in_sz: data xfer size for READS
+ */
+static void
+_base_build_sg(struct MPT3SAS_ADAPTER *ioc, void *psge,
+ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma,
+ size_t data_in_sz)
+{
+ u32 sgl_flags;
+
+ if (!data_out_sz && !data_in_sz) {
+ _base_build_zero_len_sge(ioc, psge);
+ return;
+ }
+
+ if (data_out_sz && data_in_sz) {
+ /* WRITE sgel first */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_out_sz, data_out_dma);
+
+ /* incr sgel */
+ psge += ioc->sge_size;
+
+ /* READ sgel last */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_in_sz, data_in_dma);
+ } else if (data_out_sz) /* WRITE */ {
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_out_sz, data_out_dma);
+ } else if (data_in_sz) /* READ */ {
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_in_sz, data_in_dma);
+ }
+}
+
+/* IEEE format sgls */
+
+/**
+ * _base_build_nvme_prp - This function is called for NVMe end devices to build
+ * a native SGL (NVMe PRP).
+ * @ioc: per adapter object
+ * @smid: system request message index for getting asscociated SGL
+ * @nvme_encap_request: the NVMe request msg frame pointer
+ * @data_out_dma: physical address for WRITES
+ * @data_out_sz: data xfer size for WRITES
+ * @data_in_dma: physical address for READS
+ * @data_in_sz: data xfer size for READS
+ *
+ * The native SGL is built starting in the first PRP
+ * entry of the NVMe message (PRP1). If the data buffer is small enough to be
+ * described entirely using PRP1, then PRP2 is not used. If needed, PRP2 is
+ * used to describe a larger data buffer. If the data buffer is too large to
+ * describe using the two PRP entriess inside the NVMe message, then PRP1
+ * describes the first data memory segment, and PRP2 contains a pointer to a PRP
+ * list located elsewhere in memory to describe the remaining data memory
+ * segments. The PRP list will be contiguous.
+ *
+ * The native SGL for NVMe devices is a Physical Region Page (PRP). A PRP
+ * consists of a list of PRP entries to describe a number of noncontigous
+ * physical memory segments as a single memory buffer, just as a SGL does. Note
+ * however, that this function is only used by the IOCTL call, so the memory
+ * given will be guaranteed to be contiguous. There is no need to translate
+ * non-contiguous SGL into a PRP in this case. All PRPs will describe
+ * contiguous space that is one page size each.
+ *
+ * Each NVMe message contains two PRP entries. The first (PRP1) either contains
+ * a PRP list pointer or a PRP element, depending upon the command. PRP2
+ * contains the second PRP element if the memory being described fits within 2
+ * PRP entries, or a PRP list pointer if the PRP spans more than two entries.
+ *
+ * A PRP list pointer contains the address of a PRP list, structured as a linear
+ * array of PRP entries. Each PRP entry in this list describes a segment of
+ * physical memory.
+ *
+ * Each 64-bit PRP entry comprises an address and an offset field. The address
+ * always points at the beginning of a 4KB physical memory page, and the offset
+ * describes where within that 4KB page the memory segment begins. Only the
+ * first element in a PRP list may contain a non-zero offset, implying that all
+ * memory segments following the first begin at the start of a 4KB page.
+ *
+ * Each PRP element normally describes 4KB of physical memory, with exceptions
+ * for the first and last elements in the list. If the memory being described
+ * by the list begins at a non-zero offset within the first 4KB page, then the
+ * first PRP element will contain a non-zero offset indicating where the region
+ * begins within the 4KB page. The last memory segment may end before the end
+ * of the 4KB segment, depending upon the overall size of the memory being
+ * described by the PRP list.
+ *
+ * Since PRP entries lack any indication of size, the overall data buffer length
+ * is used to determine where the end of the data memory buffer is located, and
+ * how many PRP entries are required to describe it.
+ */
+static void
+_base_build_nvme_prp(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request,
+ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma,
+ size_t data_in_sz)
+{
+ int prp_size = NVME_PRP_SIZE;
+ __le64 *prp_entry, *prp1_entry, *prp2_entry;
+ __le64 *prp_page;
+ dma_addr_t prp_entry_dma, prp_page_dma, dma_addr;
+ u32 offset, entry_len;
+ u32 page_mask_result, page_mask;
+ size_t length;
+ struct mpt3sas_nvme_cmd *nvme_cmd =
+ (void *)nvme_encap_request->NVMe_Command;
+
+ /*
+ * Not all commands require a data transfer. If no data, just return
+ * without constructing any PRP.
+ */
+ if (!data_in_sz && !data_out_sz)
+ return;
+ prp1_entry = &nvme_cmd->prp1;
+ prp2_entry = &nvme_cmd->prp2;
+ prp_entry = prp1_entry;
+ /*
+ * For the PRP entries, use the specially allocated buffer of
+ * contiguous memory.
+ */
+ prp_page = (__le64 *)mpt3sas_base_get_pcie_sgl(ioc, smid);
+ prp_page_dma = mpt3sas_base_get_pcie_sgl_dma(ioc, smid);
+
+ /*
+ * Check if we are within 1 entry of a page boundary we don't
+ * want our first entry to be a PRP List entry.
+ */
+ page_mask = ioc->page_size - 1;
+ page_mask_result = (uintptr_t)((u8 *)prp_page + prp_size) & page_mask;
+ if (!page_mask_result) {
+ /* Bump up to next page boundary. */
+ prp_page = (__le64 *)((u8 *)prp_page + prp_size);
+ prp_page_dma = prp_page_dma + prp_size;
+ }
+
+ /*
+ * Set PRP physical pointer, which initially points to the current PRP
+ * DMA memory page.
+ */
+ prp_entry_dma = prp_page_dma;
+
+ /* Get physical address and length of the data buffer. */
+ if (data_in_sz) {
+ dma_addr = data_in_dma;
+ length = data_in_sz;
+ } else {
+ dma_addr = data_out_dma;
+ length = data_out_sz;
+ }
+
+ /* Loop while the length is not zero. */
+ while (length) {
+ /*
+ * Check if we need to put a list pointer here if we are at
+ * page boundary - prp_size (8 bytes).
+ */
+ page_mask_result = (prp_entry_dma + prp_size) & page_mask;
+ if (!page_mask_result) {
+ /*
+ * This is the last entry in a PRP List, so we need to
+ * put a PRP list pointer here. What this does is:
+ * - bump the current memory pointer to the next
+ * address, which will be the next full page.
+ * - set the PRP Entry to point to that page. This
+ * is now the PRP List pointer.
+ * - bump the PRP Entry pointer the start of the
+ * next page. Since all of this PRP memory is
+ * contiguous, no need to get a new page - it's
+ * just the next address.
+ */
+ prp_entry_dma++;
+ *prp_entry = cpu_to_le64(prp_entry_dma);
+ prp_entry++;
+ }
+
+ /* Need to handle if entry will be part of a page. */
+ offset = dma_addr & page_mask;
+ entry_len = ioc->page_size - offset;
+
+ if (prp_entry == prp1_entry) {
+ /*
+ * Must fill in the first PRP pointer (PRP1) before
+ * moving on.
+ */
+ *prp1_entry = cpu_to_le64(dma_addr);
+
+ /*
+ * Now point to the second PRP entry within the
+ * command (PRP2).
+ */
+ prp_entry = prp2_entry;
+ } else if (prp_entry == prp2_entry) {
+ /*
+ * Should the PRP2 entry be a PRP List pointer or just
+ * a regular PRP pointer? If there is more than one
+ * more page of data, must use a PRP List pointer.
+ */
+ if (length > ioc->page_size) {
+ /*
+ * PRP2 will contain a PRP List pointer because
+ * more PRP's are needed with this command. The
+ * list will start at the beginning of the
+ * contiguous buffer.
+ */
+ *prp2_entry = cpu_to_le64(prp_entry_dma);
+
+ /*
+ * The next PRP Entry will be the start of the
+ * first PRP List.
+ */
+ prp_entry = prp_page;
+ } else {
+ /*
+ * After this, the PRP Entries are complete.
+ * This command uses 2 PRP's and no PRP list.
+ */
+ *prp2_entry = cpu_to_le64(dma_addr);
+ }
+ } else {
+ /*
+ * Put entry in list and bump the addresses.
+ *
+ * After PRP1 and PRP2 are filled in, this will fill in
+ * all remaining PRP entries in a PRP List, one per
+ * each time through the loop.
+ */
+ *prp_entry = cpu_to_le64(dma_addr);
+ prp_entry++;
+ prp_entry_dma++;
+ }
+
+ /*
+ * Bump the phys address of the command's data buffer by the
+ * entry_len.
+ */
+ dma_addr += entry_len;
+
+ /* Decrement length accounting for last partial page. */
+ if (entry_len > length)
+ length = 0;
+ else
+ length -= entry_len;
+ }
+}
+
+/**
+ * base_make_prp_nvme - Prepare PRPs (Physical Region Page) -
+ * SGLs specific to NVMe drives only
+ *
+ * @ioc: per adapter object
+ * @scmd: SCSI command from the mid-layer
+ * @mpi_request: mpi request
+ * @smid: msg Index
+ * @sge_count: scatter gather element count.
+ *
+ * Return: true: PRPs are built
+ * false: IEEE SGLs needs to be built
+ */
+static void
+base_make_prp_nvme(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd,
+ Mpi25SCSIIORequest_t *mpi_request,
+ u16 smid, int sge_count)
+{
+ int sge_len, num_prp_in_chain = 0;
+ Mpi25IeeeSgeChain64_t *main_chain_element, *ptr_first_sgl;
+ __le64 *curr_buff;
+ dma_addr_t msg_dma, sge_addr, offset;
+ u32 page_mask, page_mask_result;
+ struct scatterlist *sg_scmd;
+ u32 first_prp_len;
+ int data_len = scsi_bufflen(scmd);
+ u32 nvme_pg_size;
+
+ nvme_pg_size = max_t(u32, ioc->page_size, NVME_PRP_PAGE_SIZE);
+ /*
+ * Nvme has a very convoluted prp format. One prp is required
+ * for each page or partial page. Driver need to split up OS sg_list
+ * entries if it is longer than one page or cross a page
+ * boundary. Driver also have to insert a PRP list pointer entry as
+ * the last entry in each physical page of the PRP list.
+ *
+ * NOTE: The first PRP "entry" is actually placed in the first
+ * SGL entry in the main message as IEEE 64 format. The 2nd
+ * entry in the main message is the chain element, and the rest
+ * of the PRP entries are built in the contiguous pcie buffer.
+ */
+ page_mask = nvme_pg_size - 1;
+
+ /*
+ * Native SGL is needed.
+ * Put a chain element in main message frame that points to the first
+ * chain buffer.
+ *
+ * NOTE: The ChainOffset field must be 0 when using a chain pointer to
+ * a native SGL.
+ */
+
+ /* Set main message chain element pointer */
+ main_chain_element = (pMpi25IeeeSgeChain64_t)&mpi_request->SGL;
+ /*
+ * For NVMe the chain element needs to be the 2nd SG entry in the main
+ * message.
+ */
+ main_chain_element = (Mpi25IeeeSgeChain64_t *)
+ ((u8 *)main_chain_element + sizeof(MPI25_IEEE_SGE_CHAIN64));
+
+ /*
+ * For the PRP entries, use the specially allocated buffer of
+ * contiguous memory. Normal chain buffers can't be used
+ * because each chain buffer would need to be the size of an OS
+ * page (4k).
+ */
+ curr_buff = mpt3sas_base_get_pcie_sgl(ioc, smid);
+ msg_dma = mpt3sas_base_get_pcie_sgl_dma(ioc, smid);
+
+ main_chain_element->Address = cpu_to_le64(msg_dma);
+ main_chain_element->NextChainOffset = 0;
+ main_chain_element->Flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR |
+ MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP;
+
+ /* Build first prp, sge need not to be page aligned*/
+ ptr_first_sgl = (pMpi25IeeeSgeChain64_t)&mpi_request->SGL;
+ sg_scmd = scsi_sglist(scmd);
+ sge_addr = sg_dma_address(sg_scmd);
+ sge_len = sg_dma_len(sg_scmd);
+
+ offset = sge_addr & page_mask;
+ first_prp_len = nvme_pg_size - offset;
+
+ ptr_first_sgl->Address = cpu_to_le64(sge_addr);
+ ptr_first_sgl->Length = cpu_to_le32(first_prp_len);
+
+ data_len -= first_prp_len;
+
+ if (sge_len > first_prp_len) {
+ sge_addr += first_prp_len;
+ sge_len -= first_prp_len;
+ } else if (data_len && (sge_len == first_prp_len)) {
+ sg_scmd = sg_next(sg_scmd);
+ sge_addr = sg_dma_address(sg_scmd);
+ sge_len = sg_dma_len(sg_scmd);
+ }
+
+ for (;;) {
+ offset = sge_addr & page_mask;
+
+ /* Put PRP pointer due to page boundary*/
+ page_mask_result = (uintptr_t)(curr_buff + 1) & page_mask;
+ if (unlikely(!page_mask_result)) {
+ scmd_printk(KERN_NOTICE,
+ scmd, "page boundary curr_buff: 0x%p\n",
+ curr_buff);
+ msg_dma += 8;
+ *curr_buff = cpu_to_le64(msg_dma);
+ curr_buff++;
+ num_prp_in_chain++;
+ }
+
+ *curr_buff = cpu_to_le64(sge_addr);
+ curr_buff++;
+ msg_dma += 8;
+ num_prp_in_chain++;
+
+ sge_addr += nvme_pg_size;
+ sge_len -= nvme_pg_size;
+ data_len -= nvme_pg_size;
+
+ if (data_len <= 0)
+ break;
+
+ if (sge_len > 0)
+ continue;
+
+ sg_scmd = sg_next(sg_scmd);
+ sge_addr = sg_dma_address(sg_scmd);
+ sge_len = sg_dma_len(sg_scmd);
+ }
+
+ main_chain_element->Length =
+ cpu_to_le32(num_prp_in_chain * sizeof(u64));
+ return;
+}
+
+static bool
+base_is_prp_possible(struct MPT3SAS_ADAPTER *ioc,
+ struct _pcie_device *pcie_device, struct scsi_cmnd *scmd, int sge_count)
+{
+ u32 data_length = 0;
+ bool build_prp = true;
+
+ data_length = scsi_bufflen(scmd);
+ if (pcie_device &&
+ (mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info))) {
+ build_prp = false;
+ return build_prp;
+ }
+
+ /* If Datalenth is <= 16K and number of SGE’s entries are <= 2
+ * we built IEEE SGL
+ */
+ if ((data_length <= NVME_PRP_PAGE_SIZE*4) && (sge_count <= 2))
+ build_prp = false;
+
+ return build_prp;
+}
+
+/**
+ * _base_check_pcie_native_sgl - This function is called for PCIe end devices to
+ * determine if the driver needs to build a native SGL. If so, that native
+ * SGL is built in the special contiguous buffers allocated especially for
+ * PCIe SGL creation. If the driver will not build a native SGL, return
+ * TRUE and a normal IEEE SGL will be built. Currently this routine
+ * supports NVMe.
+ * @ioc: per adapter object
+ * @mpi_request: mf request pointer
+ * @smid: system request message index
+ * @scmd: scsi command
+ * @pcie_device: points to the PCIe device's info
+ *
+ * Return: 0 if native SGL was built, 1 if no SGL was built
+ */
+static int
+_base_check_pcie_native_sgl(struct MPT3SAS_ADAPTER *ioc,
+ Mpi25SCSIIORequest_t *mpi_request, u16 smid, struct scsi_cmnd *scmd,
+ struct _pcie_device *pcie_device)
+{
+ int sges_left;
+
+ /* Get the SG list pointer and info. */
+ sges_left = scsi_dma_map(scmd);
+ if (sges_left < 0)
+ return 1;
+
+ /* Check if we need to build a native SG list. */
+ if (!base_is_prp_possible(ioc, pcie_device,
+ scmd, sges_left)) {
+ /* We built a native SG list, just return. */
+ goto out;
+ }
+
+ /*
+ * Build native NVMe PRP.
+ */
+ base_make_prp_nvme(ioc, scmd, mpi_request,
+ smid, sges_left);
+
+ return 0;
+out:
+ scsi_dma_unmap(scmd);
+ return 1;
+}
+
+/**
+ * _base_add_sg_single_ieee - add sg element for IEEE format
+ * @paddr: virtual address for SGE
+ * @flags: SGE flags
+ * @chain_offset: number of 128 byte elements from start of segment
+ * @length: data transfer length
+ * @dma_addr: Physical address
+ */
+static void
+_base_add_sg_single_ieee(void *paddr, u8 flags, u8 chain_offset, u32 length,
+ dma_addr_t dma_addr)
+{
+ Mpi25IeeeSgeChain64_t *sgel = paddr;
+
+ sgel->Flags = flags;
+ sgel->NextChainOffset = chain_offset;
+ sgel->Length = cpu_to_le32(length);
+ sgel->Address = cpu_to_le64(dma_addr);
+}
+
+/**
+ * _base_build_zero_len_sge_ieee - build zero length sg entry for IEEE format
+ * @ioc: per adapter object
+ * @paddr: virtual address for SGE
+ *
+ * Create a zero length scatter gather entry to insure the IOCs hardware has
+ * something to use if the target device goes brain dead and tries
+ * to send data even when none is asked for.
+ */
+static void
+_base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr)
+{
+ u8 sgl_flags = (MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST);
+
+ _base_add_sg_single_ieee(paddr, sgl_flags, 0, 0, -1);
+}
+
+static inline int _base_scsi_dma_map(struct scsi_cmnd *cmd)
+{
+ /*
+ * Some firmware versions byte-swap the REPORT ZONES command reply from
+ * ATA-ZAC devices by directly accessing in the host buffer. This does
+ * not respect the default command DMA direction and causes IOMMU page
+ * faults on some architectures with an IOMMU enforcing write mappings
+ * (e.g. AMD hosts). Avoid such issue by making the report zones buffer
+ * mapping bi-directional.
+ */
+ if (cmd->cmnd[0] == ZBC_IN && cmd->cmnd[1] == ZI_REPORT_ZONES)
+ cmd->sc_data_direction = DMA_BIDIRECTIONAL;
+
+ return scsi_dma_map(cmd);
+}
+
+/**
+ * _base_build_sg_scmd - main sg creation routine
+ * pcie_device is unused here!
+ * @ioc: per adapter object
+ * @scmd: scsi command
+ * @smid: system request message index
+ * @unused: unused pcie_device pointer
+ * Context: none.
+ *
+ * The main routine that builds scatter gather table from a given
+ * scsi request sent via the .queuecommand main handler.
+ *
+ * Return: 0 success, anything else error
+ */
+static int
+_base_build_sg_scmd(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd, u16 smid, struct _pcie_device *unused)
+{
+ Mpi2SCSIIORequest_t *mpi_request;
+ dma_addr_t chain_dma;
+ struct scatterlist *sg_scmd;
+ void *sg_local, *chain;
+ u32 chain_offset;
+ u32 chain_length;
+ u32 chain_flags;
+ int sges_left;
+ u32 sges_in_segment;
+ u32 sgl_flags;
+ u32 sgl_flags_last_element;
+ u32 sgl_flags_end_buffer;
+ struct chain_tracker *chain_req;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+
+ /* init scatter gather flags */
+ sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT;
+ if (scmd->sc_data_direction == DMA_TO_DEVICE)
+ sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC;
+ sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT)
+ << MPI2_SGE_FLAGS_SHIFT;
+ sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST)
+ << MPI2_SGE_FLAGS_SHIFT;
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+
+ sg_scmd = scsi_sglist(scmd);
+ sges_left = _base_scsi_dma_map(scmd);
+ if (sges_left < 0)
+ return -ENOMEM;
+
+ sg_local = &mpi_request->SGL;
+ sges_in_segment = ioc->max_sges_in_main_message;
+ if (sges_left <= sges_in_segment)
+ goto fill_in_last_segment;
+
+ mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) +
+ (sges_in_segment * ioc->sge_size))/4;
+
+ /* fill in main message segment when there is a chain following */
+ while (sges_in_segment) {
+ if (sges_in_segment == 1)
+ ioc->base_add_sg_single(sg_local,
+ sgl_flags_last_element | sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ ioc->base_add_sg_single(sg_local, sgl_flags |
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ /* initializing the chain flags and pointers */
+ chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT;
+ chain_req = _base_get_chain_buffer_tracker(ioc, scmd);
+ if (!chain_req)
+ return -1;
+ chain = chain_req->chain_buffer;
+ chain_dma = chain_req->chain_buffer_dma;
+ do {
+ sges_in_segment = (sges_left <=
+ ioc->max_sges_in_chain_message) ? sges_left :
+ ioc->max_sges_in_chain_message;
+ chain_offset = (sges_left == sges_in_segment) ?
+ 0 : (sges_in_segment * ioc->sge_size)/4;
+ chain_length = sges_in_segment * ioc->sge_size;
+ if (chain_offset) {
+ chain_offset = chain_offset <<
+ MPI2_SGE_CHAIN_OFFSET_SHIFT;
+ chain_length += ioc->sge_size;
+ }
+ ioc->base_add_sg_single(sg_local, chain_flags | chain_offset |
+ chain_length, chain_dma);
+ sg_local = chain;
+ if (!chain_offset)
+ goto fill_in_last_segment;
+
+ /* fill in chain segments */
+ while (sges_in_segment) {
+ if (sges_in_segment == 1)
+ ioc->base_add_sg_single(sg_local,
+ sgl_flags_last_element |
+ sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ ioc->base_add_sg_single(sg_local, sgl_flags |
+ sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ chain_req = _base_get_chain_buffer_tracker(ioc, scmd);
+ if (!chain_req)
+ return -1;
+ chain = chain_req->chain_buffer;
+ chain_dma = chain_req->chain_buffer_dma;
+ } while (1);
+
+
+ fill_in_last_segment:
+
+ /* fill the last segment */
+ while (sges_left) {
+ if (sges_left == 1)
+ ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer |
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ else
+ ioc->base_add_sg_single(sg_local, sgl_flags |
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size;
+ sges_left--;
+ }
+
+ return 0;
+}
+
+/**
+ * _base_build_sg_scmd_ieee - main sg creation routine for IEEE format
+ * @ioc: per adapter object
+ * @scmd: scsi command
+ * @smid: system request message index
+ * @pcie_device: Pointer to pcie_device. If set, the pcie native sgl will be
+ * constructed on need.
+ * Context: none.
+ *
+ * The main routine that builds scatter gather table from a given
+ * scsi request sent via the .queuecommand main handler.
+ *
+ * Return: 0 success, anything else error
+ */
+static int
+_base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd, u16 smid, struct _pcie_device *pcie_device)
+{
+ Mpi25SCSIIORequest_t *mpi_request;
+ dma_addr_t chain_dma;
+ struct scatterlist *sg_scmd;
+ void *sg_local, *chain;
+ u32 chain_offset;
+ u32 chain_length;
+ int sges_left;
+ u32 sges_in_segment;
+ u8 simple_sgl_flags;
+ u8 simple_sgl_flags_last;
+ u8 chain_sgl_flags;
+ struct chain_tracker *chain_req;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+
+ /* init scatter gather flags */
+ simple_sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ simple_sgl_flags_last = simple_sgl_flags |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
+ chain_sgl_flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+
+ /* Check if we need to build a native SG list. */
+ if ((pcie_device) && (_base_check_pcie_native_sgl(ioc, mpi_request,
+ smid, scmd, pcie_device) == 0)) {
+ /* We built a native SG list, just return. */
+ return 0;
+ }
+
+ sg_scmd = scsi_sglist(scmd);
+ sges_left = _base_scsi_dma_map(scmd);
+ if (sges_left < 0)
+ return -ENOMEM;
+
+ sg_local = &mpi_request->SGL;
+ sges_in_segment = (ioc->request_sz -
+ offsetof(Mpi25SCSIIORequest_t, SGL))/ioc->sge_size_ieee;
+ if (sges_left <= sges_in_segment)
+ goto fill_in_last_segment;
+
+ mpi_request->ChainOffset = (sges_in_segment - 1 /* chain element */) +
+ (offsetof(Mpi25SCSIIORequest_t, SGL)/ioc->sge_size_ieee);
+
+ /* fill in main message segment when there is a chain following */
+ while (sges_in_segment > 1) {
+ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size_ieee;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ /* initializing the pointers */
+ chain_req = _base_get_chain_buffer_tracker(ioc, scmd);
+ if (!chain_req)
+ return -1;
+ chain = chain_req->chain_buffer;
+ chain_dma = chain_req->chain_buffer_dma;
+ do {
+ sges_in_segment = (sges_left <=
+ ioc->max_sges_in_chain_message) ? sges_left :
+ ioc->max_sges_in_chain_message;
+ chain_offset = (sges_left == sges_in_segment) ?
+ 0 : sges_in_segment;
+ chain_length = sges_in_segment * ioc->sge_size_ieee;
+ if (chain_offset)
+ chain_length += ioc->sge_size_ieee;
+ _base_add_sg_single_ieee(sg_local, chain_sgl_flags,
+ chain_offset, chain_length, chain_dma);
+
+ sg_local = chain;
+ if (!chain_offset)
+ goto fill_in_last_segment;
+
+ /* fill in chain segments */
+ while (sges_in_segment) {
+ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size_ieee;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ chain_req = _base_get_chain_buffer_tracker(ioc, scmd);
+ if (!chain_req)
+ return -1;
+ chain = chain_req->chain_buffer;
+ chain_dma = chain_req->chain_buffer_dma;
+ } while (1);
+
+
+ fill_in_last_segment:
+
+ /* fill the last segment */
+ while (sges_left > 0) {
+ if (sges_left == 1)
+ _base_add_sg_single_ieee(sg_local,
+ simple_sgl_flags_last, 0, sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size_ieee;
+ sges_left--;
+ }
+
+ return 0;
+}
+
+/**
+ * _base_build_sg_ieee - build generic sg for IEEE format
+ * @ioc: per adapter object
+ * @psge: virtual address for SGE
+ * @data_out_dma: physical address for WRITES
+ * @data_out_sz: data xfer size for WRITES
+ * @data_in_dma: physical address for READS
+ * @data_in_sz: data xfer size for READS
+ */
+static void
+_base_build_sg_ieee(struct MPT3SAS_ADAPTER *ioc, void *psge,
+ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma,
+ size_t data_in_sz)
+{
+ u8 sgl_flags;
+
+ if (!data_out_sz && !data_in_sz) {
+ _base_build_zero_len_sge_ieee(ioc, psge);
+ return;
+ }
+
+ if (data_out_sz && data_in_sz) {
+ /* WRITE sgel first */
+ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz,
+ data_out_dma);
+
+ /* incr sgel */
+ psge += ioc->sge_size_ieee;
+
+ /* READ sgel last */
+ sgl_flags |= MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz,
+ data_in_dma);
+ } else if (data_out_sz) /* WRITE */ {
+ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz,
+ data_out_dma);
+ } else if (data_in_sz) /* READ */ {
+ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz,
+ data_in_dma);
+ }
+}
+
+#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10))
+
+/**
+ * _base_config_dma_addressing - set dma addressing
+ * @ioc: per adapter object
+ * @pdev: PCI device struct
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev)
+{
+ struct sysinfo s;
+ u64 coherent_dma_mask, dma_mask;
+
+ if (ioc->is_mcpu_endpoint || sizeof(dma_addr_t) == 4) {
+ ioc->dma_mask = 32;
+ coherent_dma_mask = dma_mask = DMA_BIT_MASK(32);
+ /* Set 63 bit DMA mask for all SAS3 and SAS35 controllers */
+ } else if (ioc->hba_mpi_version_belonged > MPI2_VERSION) {
+ ioc->dma_mask = 63;
+ coherent_dma_mask = dma_mask = DMA_BIT_MASK(63);
+ } else {
+ ioc->dma_mask = 64;
+ coherent_dma_mask = dma_mask = DMA_BIT_MASK(64);
+ }
+
+ if (ioc->use_32bit_dma)
+ coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (dma_set_mask(&pdev->dev, dma_mask) ||
+ dma_set_coherent_mask(&pdev->dev, coherent_dma_mask))
+ return -ENODEV;
+
+ if (ioc->dma_mask > 32) {
+ ioc->base_add_sg_single = &_base_add_sg_single_64;
+ ioc->sge_size = sizeof(Mpi2SGESimple64_t);
+ } else {
+ ioc->base_add_sg_single = &_base_add_sg_single_32;
+ ioc->sge_size = sizeof(Mpi2SGESimple32_t);
+ }
+
+ si_meminfo(&s);
+ ioc_info(ioc, "%d BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n",
+ ioc->dma_mask, convert_to_kb(s.totalram));
+
+ return 0;
+}
+
+/**
+ * _base_check_enable_msix - checks MSIX capabable.
+ * @ioc: per adapter object
+ *
+ * Check to see if card is capable of MSIX, and set number
+ * of available msix vectors
+ */
+static int
+_base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc)
+{
+ int base;
+ u16 message_control;
+
+ /* Check whether controller SAS2008 B0 controller,
+ * if it is SAS2008 B0 controller use IO-APIC instead of MSIX
+ */
+ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 &&
+ ioc->pdev->revision == SAS2_PCI_DEVICE_B0_REVISION) {
+ return -EINVAL;
+ }
+
+ base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
+ if (!base) {
+ dfailprintk(ioc, ioc_info(ioc, "msix not supported\n"));
+ return -EINVAL;
+ }
+
+ /* get msix vector count */
+ /* NUMA_IO not supported for older controllers */
+ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2004 ||
+ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 ||
+ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_1 ||
+ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_2 ||
+ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_3 ||
+ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_1 ||
+ ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_2)
+ ioc->msix_vector_count = 1;
+ else {
+ pci_read_config_word(ioc->pdev, base + 2, &message_control);
+ ioc->msix_vector_count = (message_control & 0x3FF) + 1;
+ }
+ dinitprintk(ioc, ioc_info(ioc, "msix is supported, vector_count(%d)\n",
+ ioc->msix_vector_count));
+ return 0;
+}
+
+/**
+ * mpt3sas_base_free_irq - free irq
+ * @ioc: per adapter object
+ *
+ * Freeing respective reply_queue from the list.
+ */
+void
+mpt3sas_base_free_irq(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned int irq;
+ struct adapter_reply_queue *reply_q, *next;
+
+ if (list_empty(&ioc->reply_queue_list))
+ return;
+
+ list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) {
+ list_del(&reply_q->list);
+ if (reply_q->is_iouring_poll_q) {
+ kfree(reply_q);
+ continue;
+ }
+
+ if (ioc->smp_affinity_enable) {
+ irq = pci_irq_vector(ioc->pdev, reply_q->msix_index);
+ irq_update_affinity_hint(irq, NULL);
+ }
+ free_irq(pci_irq_vector(ioc->pdev, reply_q->msix_index),
+ reply_q);
+ kfree(reply_q);
+ }
+}
+
+/**
+ * _base_request_irq - request irq
+ * @ioc: per adapter object
+ * @index: msix index into vector table
+ *
+ * Inserting respective reply_queue into the list.
+ */
+static int
+_base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index)
+{
+ struct pci_dev *pdev = ioc->pdev;
+ struct adapter_reply_queue *reply_q;
+ int r, qid;
+
+ reply_q = kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL);
+ if (!reply_q) {
+ ioc_err(ioc, "unable to allocate memory %zu!\n",
+ sizeof(struct adapter_reply_queue));
+ return -ENOMEM;
+ }
+ reply_q->ioc = ioc;
+ reply_q->msix_index = index;
+
+ atomic_set(&reply_q->busy, 0);
+
+ if (index >= ioc->iopoll_q_start_index) {
+ qid = index - ioc->iopoll_q_start_index;
+ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-mq-poll%d",
+ ioc->driver_name, ioc->id, qid);
+ reply_q->is_iouring_poll_q = 1;
+ ioc->io_uring_poll_queues[qid].reply_q = reply_q;
+ goto out;
+ }
+
+
+ if (ioc->msix_enable)
+ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d",
+ ioc->driver_name, ioc->id, index);
+ else
+ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d",
+ ioc->driver_name, ioc->id);
+ r = request_irq(pci_irq_vector(pdev, index), _base_interrupt,
+ IRQF_SHARED, reply_q->name, reply_q);
+ if (r) {
+ pr_err("%s: unable to allocate interrupt %d!\n",
+ reply_q->name, pci_irq_vector(pdev, index));
+ kfree(reply_q);
+ return -EBUSY;
+ }
+out:
+ INIT_LIST_HEAD(&reply_q->list);
+ list_add_tail(&reply_q->list, &ioc->reply_queue_list);
+ return 0;
+}
+
+/**
+ * _base_assign_reply_queues - assigning msix index for each cpu
+ * @ioc: per adapter object
+ *
+ * The enduser would need to set the affinity via /proc/irq/#/smp_affinity
+ */
+static void
+_base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned int cpu, nr_cpus, nr_msix, index = 0, irq;
+ struct adapter_reply_queue *reply_q;
+ int iopoll_q_count = ioc->reply_queue_count -
+ ioc->iopoll_q_start_index;
+ const struct cpumask *mask;
+
+ if (!_base_is_controller_msix_enabled(ioc))
+ return;
+
+ if (ioc->msix_load_balance)
+ return;
+
+ memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz);
+
+ nr_cpus = num_online_cpus();
+ nr_msix = ioc->reply_queue_count = min(ioc->reply_queue_count,
+ ioc->facts.MaxMSIxVectors);
+ if (!nr_msix)
+ return;
+
+ if (ioc->smp_affinity_enable) {
+
+ /*
+ * set irq affinity to local numa node for those irqs
+ * corresponding to high iops queues.
+ */
+ if (ioc->high_iops_queues) {
+ mask = cpumask_of_node(dev_to_node(&ioc->pdev->dev));
+ for (index = 0; index < ioc->high_iops_queues;
+ index++) {
+ irq = pci_irq_vector(ioc->pdev, index);
+ irq_set_affinity_and_hint(irq, mask);
+ }
+ }
+
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ const cpumask_t *mask;
+
+ if (reply_q->msix_index < ioc->high_iops_queues ||
+ reply_q->msix_index >= ioc->iopoll_q_start_index)
+ continue;
+
+ mask = pci_irq_get_affinity(ioc->pdev,
+ reply_q->msix_index);
+ if (!mask) {
+ ioc_warn(ioc, "no affinity for msi %x\n",
+ reply_q->msix_index);
+ goto fall_back;
+ }
+
+ for_each_cpu_and(cpu, mask, cpu_online_mask) {
+ if (cpu >= ioc->cpu_msix_table_sz)
+ break;
+ ioc->cpu_msix_table[cpu] = reply_q->msix_index;
+ }
+ }
+ return;
+ }
+
+fall_back:
+ cpu = cpumask_first(cpu_online_mask);
+ nr_msix -= (ioc->high_iops_queues - iopoll_q_count);
+ index = 0;
+
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ unsigned int i, group = nr_cpus / nr_msix;
+
+ if (reply_q->msix_index < ioc->high_iops_queues ||
+ reply_q->msix_index >= ioc->iopoll_q_start_index)
+ continue;
+
+ if (cpu >= nr_cpus)
+ break;
+
+ if (index < nr_cpus % nr_msix)
+ group++;
+
+ for (i = 0 ; i < group ; i++) {
+ ioc->cpu_msix_table[cpu] = reply_q->msix_index;
+ cpu = cpumask_next(cpu, cpu_online_mask);
+ }
+ index++;
+ }
+}
+
+/**
+ * _base_check_and_enable_high_iops_queues - enable high iops mode
+ * @ioc: per adapter object
+ * @hba_msix_vector_count: msix vectors supported by HBA
+ *
+ * Enable high iops queues only if
+ * - HBA is a SEA/AERO controller and
+ * - MSI-Xs vector supported by the HBA is 128 and
+ * - total CPU count in the system >=16 and
+ * - loaded driver with default max_msix_vectors module parameter and
+ * - system booted in non kdump mode
+ *
+ * Return: nothing.
+ */
+static void
+_base_check_and_enable_high_iops_queues(struct MPT3SAS_ADAPTER *ioc,
+ int hba_msix_vector_count)
+{
+ u16 lnksta, speed;
+
+ /*
+ * Disable high iops queues if io uring poll queues are enabled.
+ */
+ if (perf_mode == MPT_PERF_MODE_IOPS ||
+ perf_mode == MPT_PERF_MODE_LATENCY ||
+ ioc->io_uring_poll_queues) {
+ ioc->high_iops_queues = 0;
+ return;
+ }
+
+ if (perf_mode == MPT_PERF_MODE_DEFAULT) {
+
+ pcie_capability_read_word(ioc->pdev, PCI_EXP_LNKSTA, &lnksta);
+ speed = lnksta & PCI_EXP_LNKSTA_CLS;
+
+ if (speed < 0x4) {
+ ioc->high_iops_queues = 0;
+ return;
+ }
+ }
+
+ if (!reset_devices && ioc->is_aero_ioc &&
+ hba_msix_vector_count == MPT3SAS_GEN35_MAX_MSIX_QUEUES &&
+ num_online_cpus() >= MPT3SAS_HIGH_IOPS_REPLY_QUEUES &&
+ max_msix_vectors == -1)
+ ioc->high_iops_queues = MPT3SAS_HIGH_IOPS_REPLY_QUEUES;
+ else
+ ioc->high_iops_queues = 0;
+}
+
+/**
+ * mpt3sas_base_disable_msix - disables msix
+ * @ioc: per adapter object
+ *
+ */
+void
+mpt3sas_base_disable_msix(struct MPT3SAS_ADAPTER *ioc)
+{
+ if (!ioc->msix_enable)
+ return;
+ pci_free_irq_vectors(ioc->pdev);
+ ioc->msix_enable = 0;
+ kfree(ioc->io_uring_poll_queues);
+}
+
+/**
+ * _base_alloc_irq_vectors - allocate msix vectors
+ * @ioc: per adapter object
+ *
+ */
+static int
+_base_alloc_irq_vectors(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i, irq_flags = PCI_IRQ_MSIX;
+ struct irq_affinity desc = { .pre_vectors = ioc->high_iops_queues };
+ struct irq_affinity *descp = &desc;
+ /*
+ * Don't allocate msix vectors for poll_queues.
+ * msix_vectors is always within a range of FW supported reply queue.
+ */
+ int nr_msix_vectors = ioc->iopoll_q_start_index;
+
+
+ if (ioc->smp_affinity_enable)
+ irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES;
+ else
+ descp = NULL;
+
+ ioc_info(ioc, " %d %d %d\n", ioc->high_iops_queues,
+ ioc->reply_queue_count, nr_msix_vectors);
+
+ i = pci_alloc_irq_vectors_affinity(ioc->pdev,
+ ioc->high_iops_queues,
+ nr_msix_vectors, irq_flags, descp);
+
+ return i;
+}
+
+/**
+ * _base_enable_msix - enables msix, failback to io_apic
+ * @ioc: per adapter object
+ *
+ */
+static int
+_base_enable_msix(struct MPT3SAS_ADAPTER *ioc)
+{
+ int r;
+ int i, local_max_msix_vectors;
+ u8 try_msix = 0;
+ int iopoll_q_count = 0;
+
+ ioc->msix_load_balance = false;
+
+ if (msix_disable == -1 || msix_disable == 0)
+ try_msix = 1;
+
+ if (!try_msix)
+ goto try_ioapic;
+
+ if (_base_check_enable_msix(ioc) != 0)
+ goto try_ioapic;
+
+ ioc_info(ioc, "MSI-X vectors supported: %d\n", ioc->msix_vector_count);
+ pr_info("\t no of cores: %d, max_msix_vectors: %d\n",
+ ioc->cpu_count, max_msix_vectors);
+
+ ioc->reply_queue_count =
+ min_t(int, ioc->cpu_count, ioc->msix_vector_count);
+
+ if (!ioc->rdpq_array_enable && max_msix_vectors == -1)
+ local_max_msix_vectors = (reset_devices) ? 1 : 8;
+ else
+ local_max_msix_vectors = max_msix_vectors;
+
+ if (local_max_msix_vectors == 0)
+ goto try_ioapic;
+
+ /*
+ * Enable msix_load_balance only if combined reply queue mode is
+ * disabled on SAS3 & above generation HBA devices.
+ */
+ if (!ioc->combined_reply_queue &&
+ ioc->hba_mpi_version_belonged != MPI2_VERSION) {
+ ioc_info(ioc,
+ "combined ReplyQueue is off, Enabling msix load balance\n");
+ ioc->msix_load_balance = true;
+ }
+
+ /*
+ * smp affinity setting is not need when msix load balance
+ * is enabled.
+ */
+ if (ioc->msix_load_balance)
+ ioc->smp_affinity_enable = 0;
+
+ if (!ioc->smp_affinity_enable || ioc->reply_queue_count <= 1)
+ ioc->shost->host_tagset = 0;
+
+ /*
+ * Enable io uring poll queues only if host_tagset is enabled.
+ */
+ if (ioc->shost->host_tagset)
+ iopoll_q_count = poll_queues;
+
+ if (iopoll_q_count) {
+ ioc->io_uring_poll_queues = kcalloc(iopoll_q_count,
+ sizeof(struct io_uring_poll_queue), GFP_KERNEL);
+ if (!ioc->io_uring_poll_queues)
+ iopoll_q_count = 0;
+ }
+
+ if (ioc->is_aero_ioc)
+ _base_check_and_enable_high_iops_queues(ioc,
+ ioc->msix_vector_count);
+
+ /*
+ * Add high iops queues count to reply queue count if high iops queues
+ * are enabled.
+ */
+ ioc->reply_queue_count = min_t(int,
+ ioc->reply_queue_count + ioc->high_iops_queues,
+ ioc->msix_vector_count);
+
+ /*
+ * Adjust the reply queue count incase reply queue count
+ * exceeds the user provided MSIx vectors count.
+ */
+ if (local_max_msix_vectors > 0)
+ ioc->reply_queue_count = min_t(int, local_max_msix_vectors,
+ ioc->reply_queue_count);
+ /*
+ * Add io uring poll queues count to reply queues count
+ * if io uring is enabled in driver.
+ */
+ if (iopoll_q_count) {
+ if (ioc->reply_queue_count < (iopoll_q_count + MPT3_MIN_IRQS))
+ iopoll_q_count = 0;
+ ioc->reply_queue_count = min_t(int,
+ ioc->reply_queue_count + iopoll_q_count,
+ ioc->msix_vector_count);
+ }
+
+ /*
+ * Starting index of io uring poll queues in reply queue list.
+ */
+ ioc->iopoll_q_start_index =
+ ioc->reply_queue_count - iopoll_q_count;
+
+ r = _base_alloc_irq_vectors(ioc);
+ if (r < 0) {
+ ioc_info(ioc, "pci_alloc_irq_vectors failed (r=%d) !!!\n", r);
+ goto try_ioapic;
+ }
+
+ /*
+ * Adjust the reply queue count if the allocated
+ * MSIx vectors is less then the requested number
+ * of MSIx vectors.
+ */
+ if (r < ioc->iopoll_q_start_index) {
+ ioc->reply_queue_count = r + iopoll_q_count;
+ ioc->iopoll_q_start_index =
+ ioc->reply_queue_count - iopoll_q_count;
+ }
+
+ ioc->msix_enable = 1;
+ for (i = 0; i < ioc->reply_queue_count; i++) {
+ r = _base_request_irq(ioc, i);
+ if (r) {
+ mpt3sas_base_free_irq(ioc);
+ mpt3sas_base_disable_msix(ioc);
+ goto try_ioapic;
+ }
+ }
+
+ ioc_info(ioc, "High IOPs queues : %s\n",
+ ioc->high_iops_queues ? "enabled" : "disabled");
+
+ return 0;
+
+/* failback to io_apic interrupt routing */
+ try_ioapic:
+ ioc->high_iops_queues = 0;
+ ioc_info(ioc, "High IOPs queues : disabled\n");
+ ioc->reply_queue_count = 1;
+ ioc->iopoll_q_start_index = ioc->reply_queue_count - 0;
+ r = pci_alloc_irq_vectors(ioc->pdev, 1, 1, PCI_IRQ_INTX);
+ if (r < 0) {
+ dfailprintk(ioc,
+ ioc_info(ioc, "pci_alloc_irq_vector(legacy) failed (r=%d) !!!\n",
+ r));
+ } else
+ r = _base_request_irq(ioc, 0);
+
+ return r;
+}
+
+/**
+ * mpt3sas_base_unmap_resources - free controller resources
+ * @ioc: per adapter object
+ */
+static void
+mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct pci_dev *pdev = ioc->pdev;
+
+ dexitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ mpt3sas_base_free_irq(ioc);
+ mpt3sas_base_disable_msix(ioc);
+
+ kfree(ioc->replyPostRegisterIndex);
+ ioc->replyPostRegisterIndex = NULL;
+
+
+ if (ioc->chip_phys) {
+ iounmap(ioc->chip);
+ ioc->chip_phys = 0;
+ }
+
+ if (pci_is_enabled(pdev)) {
+ pci_release_selected_regions(ioc->pdev, ioc->bars);
+ pci_disable_device(pdev);
+ }
+}
+
+static int
+_base_diag_reset(struct MPT3SAS_ADAPTER *ioc);
+
+/**
+ * mpt3sas_base_check_for_fault_and_issue_reset - check if IOC is in fault state
+ * and if it is in fault state then issue diag reset.
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_check_for_fault_and_issue_reset(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 ioc_state;
+ int rc = -EFAULT;
+
+ dinitprintk(ioc, pr_info("%s\n", __func__));
+ if (ioc->pci_error_recovery)
+ return 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ dhsprintk(ioc, pr_info("%s: ioc_state(0x%08x)\n", __func__, ioc_state));
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ mpt3sas_base_mask_interrupts(ioc);
+ rc = _base_diag_reset(ioc);
+ } else if ((ioc_state & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ mpt3sas_print_coredump_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ mpt3sas_base_wait_for_coredump_completion(ioc, __func__);
+ mpt3sas_base_mask_interrupts(ioc);
+ rc = _base_diag_reset(ioc);
+ }
+
+ return rc;
+}
+
+/**
+ * mpt3sas_base_map_resources - map in controller resources (io/irq/memap)
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct pci_dev *pdev = ioc->pdev;
+ u32 memap_sz;
+ u32 pio_sz;
+ int i, r = 0, rc;
+ u64 pio_chip = 0;
+ phys_addr_t chip_phys = 0;
+ struct adapter_reply_queue *reply_q;
+ int iopoll_q_count = 0;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM);
+ if (pci_enable_device_mem(pdev)) {
+ ioc_warn(ioc, "pci_enable_device_mem: failed\n");
+ ioc->bars = 0;
+ return -ENODEV;
+ }
+
+
+ if (pci_request_selected_regions(pdev, ioc->bars,
+ ioc->driver_name)) {
+ ioc_warn(ioc, "pci_request_selected_regions: failed\n");
+ ioc->bars = 0;
+ r = -ENODEV;
+ goto out_fail;
+ }
+
+ pci_set_master(pdev);
+
+
+ if (_base_config_dma_addressing(ioc, pdev) != 0) {
+ ioc_warn(ioc, "no suitable DMA mask for %s\n", pci_name(pdev));
+ r = -ENODEV;
+ goto out_fail;
+ }
+
+ for (i = 0, memap_sz = 0, pio_sz = 0; (i < DEVICE_COUNT_RESOURCE) &&
+ (!memap_sz || !pio_sz); i++) {
+ if (pci_resource_flags(pdev, i) & IORESOURCE_IO) {
+ if (pio_sz)
+ continue;
+ pio_chip = (u64)pci_resource_start(pdev, i);
+ pio_sz = pci_resource_len(pdev, i);
+ } else if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+ if (memap_sz)
+ continue;
+ ioc->chip_phys = pci_resource_start(pdev, i);
+ chip_phys = ioc->chip_phys;
+ memap_sz = pci_resource_len(pdev, i);
+ ioc->chip = ioremap(ioc->chip_phys, memap_sz);
+ }
+ }
+
+ if (ioc->chip == NULL) {
+ ioc_err(ioc,
+ "unable to map adapter memory! or resource not found\n");
+ r = -EINVAL;
+ goto out_fail;
+ }
+
+ mpt3sas_base_mask_interrupts(ioc);
+
+ r = _base_get_ioc_facts(ioc);
+ if (r) {
+ rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc);
+ if (rc || (_base_get_ioc_facts(ioc)))
+ goto out_fail;
+ }
+
+ if (!ioc->rdpq_array_enable_assigned) {
+ ioc->rdpq_array_enable = ioc->rdpq_array_capable;
+ ioc->rdpq_array_enable_assigned = 1;
+ }
+
+ r = _base_enable_msix(ioc);
+ if (r)
+ goto out_fail;
+
+ iopoll_q_count = ioc->reply_queue_count - ioc->iopoll_q_start_index;
+ for (i = 0; i < iopoll_q_count; i++) {
+ atomic_set(&ioc->io_uring_poll_queues[i].busy, 0);
+ atomic_set(&ioc->io_uring_poll_queues[i].pause, 0);
+ }
+
+ if (!ioc->is_driver_loading)
+ _base_init_irqpolls(ioc);
+ /* Use the Combined reply queue feature only for SAS3 C0 & higher
+ * revision HBAs and also only when reply queue count is greater than 8
+ */
+ if (ioc->combined_reply_queue) {
+ /* Determine the Supplemental Reply Post Host Index Registers
+ * Addresse. Supplemental Reply Post Host Index Registers
+ * starts at offset MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET and
+ * each register is at offset bytes of
+ * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET from previous one.
+ */
+ ioc->replyPostRegisterIndex = kcalloc(
+ ioc->combined_reply_index_count,
+ sizeof(resource_size_t *), GFP_KERNEL);
+ if (!ioc->replyPostRegisterIndex) {
+ ioc_err(ioc,
+ "allocation for replyPostRegisterIndex failed!\n");
+ r = -ENOMEM;
+ goto out_fail;
+ }
+
+ for (i = 0; i < ioc->combined_reply_index_count; i++) {
+ ioc->replyPostRegisterIndex[i] =
+ (resource_size_t __iomem *)
+ ((u8 __force *)&ioc->chip->Doorbell +
+ MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET +
+ (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET));
+ }
+ }
+
+ if (ioc->is_warpdrive) {
+ ioc->reply_post_host_index[0] = (resource_size_t __iomem *)
+ &ioc->chip->ReplyPostHostIndex;
+
+ for (i = 1; i < ioc->cpu_msix_table_sz; i++)
+ ioc->reply_post_host_index[i] =
+ (resource_size_t __iomem *)
+ ((u8 __iomem *)&ioc->chip->Doorbell + (0x4000 + ((i - 1)
+ * 4)));
+ }
+
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ if (reply_q->msix_index >= ioc->iopoll_q_start_index) {
+ pr_info("%s: enabled: index: %d\n",
+ reply_q->name, reply_q->msix_index);
+ continue;
+ }
+
+ pr_info("%s: %s enabled: IRQ %d\n",
+ reply_q->name,
+ ioc->msix_enable ? "PCI-MSI-X" : "IO-APIC",
+ pci_irq_vector(ioc->pdev, reply_q->msix_index));
+ }
+
+ ioc_info(ioc, "iomem(%pap), mapped(0x%p), size(%d)\n",
+ &chip_phys, ioc->chip, memap_sz);
+ ioc_info(ioc, "ioport(0x%016llx), size(%d)\n",
+ (unsigned long long)pio_chip, pio_sz);
+
+ /* Save PCI configuration state for recovery from PCI AER/EEH errors */
+ pci_save_state(pdev);
+ return 0;
+
+ out_fail:
+ mpt3sas_base_unmap_resources(ioc);
+ return r;
+}
+
+/**
+ * mpt3sas_base_get_msg_frame - obtain request mf pointer
+ * @ioc: per adapter object
+ * @smid: system request message index(smid zero is invalid)
+ *
+ * Return: virt pointer to message frame.
+ */
+void *
+mpt3sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->request + (smid * ioc->request_sz));
+}
+
+/**
+ * mpt3sas_base_get_sense_buffer - obtain a sense buffer virt addr
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: virt pointer to sense buffer.
+ */
+void *
+mpt3sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
+}
+
+/**
+ * mpt3sas_base_get_sense_buffer_dma - obtain a sense buffer dma addr
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: phys pointer to the low 32bit address of the sense buffer.
+ */
+__le32
+mpt3sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return cpu_to_le32(ioc->sense_dma + ((smid - 1) *
+ SCSI_SENSE_BUFFERSIZE));
+}
+
+/**
+ * mpt3sas_base_get_pcie_sgl - obtain a PCIe SGL virt addr
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: virt pointer to a PCIe SGL.
+ */
+void *
+mpt3sas_base_get_pcie_sgl(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->pcie_sg_lookup[smid - 1].pcie_sgl);
+}
+
+/**
+ * mpt3sas_base_get_pcie_sgl_dma - obtain a PCIe SGL dma addr
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: phys pointer to the address of the PCIe buffer.
+ */
+dma_addr_t
+mpt3sas_base_get_pcie_sgl_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return ioc->pcie_sg_lookup[smid - 1].pcie_sgl_dma;
+}
+
+/**
+ * mpt3sas_base_get_reply_virt_addr - obtain reply frames virt address
+ * @ioc: per adapter object
+ * @phys_addr: lower 32 physical addr of the reply
+ *
+ * Converts 32bit lower physical addr into a virt address.
+ */
+void *
+mpt3sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, u32 phys_addr)
+{
+ if (!phys_addr)
+ return NULL;
+ return ioc->reply + (phys_addr - (u32)ioc->reply_dma);
+}
+
+/**
+ * _base_get_msix_index - get the msix index
+ * @ioc: per adapter object
+ * @scmd: scsi_cmnd object
+ *
+ * Return: msix index of general reply queues,
+ * i.e. reply queue on which IO request's reply
+ * should be posted by the HBA firmware.
+ */
+static inline u8
+_base_get_msix_index(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd)
+{
+ /* Enables reply_queue load balancing */
+ if (ioc->msix_load_balance)
+ return ioc->reply_queue_count ?
+ base_mod64(atomic64_add_return(1,
+ &ioc->total_io_cnt), ioc->reply_queue_count) : 0;
+
+ if (scmd && ioc->shost->nr_hw_queues > 1) {
+ u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+
+ return blk_mq_unique_tag_to_hwq(tag) +
+ ioc->high_iops_queues;
+ }
+
+ return ioc->cpu_msix_table[raw_smp_processor_id()];
+}
+
+/**
+ * _base_get_high_iops_msix_index - get the msix index of
+ * high iops queues
+ * @ioc: per adapter object
+ * @scmd: scsi_cmnd object
+ *
+ * Return: msix index of high iops reply queues.
+ * i.e. high iops reply queue on which IO request's
+ * reply should be posted by the HBA firmware.
+ */
+static inline u8
+_base_get_high_iops_msix_index(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd)
+{
+ /**
+ * Round robin the IO interrupts among the high iops
+ * reply queues in terms of batch count 16 when outstanding
+ * IOs on the target device is >=8.
+ */
+
+ if (scsi_device_busy(scmd->device) > MPT3SAS_DEVICE_HIGH_IOPS_DEPTH)
+ return base_mod64((
+ atomic64_add_return(1, &ioc->high_iops_outstanding) /
+ MPT3SAS_HIGH_IOPS_BATCH_COUNT),
+ MPT3SAS_HIGH_IOPS_REPLY_QUEUES);
+
+ return _base_get_msix_index(ioc, scmd);
+}
+
+/**
+ * mpt3sas_base_get_smid - obtain a free smid from internal queue
+ * @ioc: per adapter object
+ * @cb_idx: callback index
+ *
+ * Return: smid (zero is invalid)
+ */
+u16
+mpt3sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx)
+{
+ unsigned long flags;
+ struct request_tracker *request;
+ u16 smid;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (list_empty(&ioc->internal_free_list)) {
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ ioc_err(ioc, "%s: smid not available\n", __func__);
+ return 0;
+ }
+
+ request = list_entry(ioc->internal_free_list.next,
+ struct request_tracker, tracker_list);
+ request->cb_idx = cb_idx;
+ smid = request->smid;
+ list_del(&request->tracker_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+/**
+ * mpt3sas_base_get_smid_scsiio - obtain a free smid from scsiio queue
+ * @ioc: per adapter object
+ * @cb_idx: callback index
+ * @scmd: pointer to scsi command object
+ *
+ * Return: smid (zero is invalid)
+ */
+u16
+mpt3sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx,
+ struct scsi_cmnd *scmd)
+{
+ struct scsiio_tracker *request = scsi_cmd_priv(scmd);
+ u16 smid;
+ u32 tag, unique_tag;
+
+ unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+ tag = blk_mq_unique_tag_to_tag(unique_tag);
+
+ /*
+ * Store hw queue number corresponding to the tag.
+ * This hw queue number is used later to determine
+ * the unique_tag using the logic below. This unique_tag
+ * is used to retrieve the scmd pointer corresponding
+ * to tag using scsi_host_find_tag() API.
+ *
+ * tag = smid - 1;
+ * unique_tag = ioc->io_queue_num[tag] << BLK_MQ_UNIQUE_TAG_BITS | tag;
+ */
+ ioc->io_queue_num[tag] = blk_mq_unique_tag_to_hwq(unique_tag);
+
+ smid = tag + 1;
+ request->cb_idx = cb_idx;
+ request->smid = smid;
+ request->scmd = scmd;
+ INIT_LIST_HEAD(&request->chain_list);
+ return smid;
+}
+
+/**
+ * mpt3sas_base_get_smid_hpr - obtain a free smid from hi-priority queue
+ * @ioc: per adapter object
+ * @cb_idx: callback index
+ *
+ * Return: smid (zero is invalid)
+ */
+u16
+mpt3sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx)
+{
+ unsigned long flags;
+ struct request_tracker *request;
+ u16 smid;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (list_empty(&ioc->hpr_free_list)) {
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return 0;
+ }
+
+ request = list_entry(ioc->hpr_free_list.next,
+ struct request_tracker, tracker_list);
+ request->cb_idx = cb_idx;
+ smid = request->smid;
+ list_del(&request->tracker_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+static void
+_base_recovery_check(struct MPT3SAS_ADAPTER *ioc)
+{
+ /*
+ * See _wait_for_commands_to_complete() call with regards to this code.
+ */
+ if (ioc->shost_recovery && ioc->pending_io_count) {
+ ioc->pending_io_count = scsi_host_busy(ioc->shost);
+ if (ioc->pending_io_count == 0)
+ wake_up(&ioc->reset_wq);
+ }
+}
+
+void mpt3sas_base_clear_st(struct MPT3SAS_ADAPTER *ioc,
+ struct scsiio_tracker *st)
+{
+ if (WARN_ON(st->smid == 0))
+ return;
+ st->cb_idx = 0xFF;
+ st->direct_io = 0;
+ st->scmd = NULL;
+ atomic_set(&ioc->chain_lookup[st->smid - 1].chain_offset, 0);
+ st->smid = 0;
+}
+
+/**
+ * mpt3sas_base_free_smid - put smid back on free_list
+ * @ioc: per adapter object
+ * @smid: system request message index
+ */
+void
+mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ unsigned long flags;
+ int i;
+
+ if (smid < ioc->hi_priority_smid) {
+ struct scsiio_tracker *st;
+ void *request;
+
+ st = _get_st_from_smid(ioc, smid);
+ if (!st) {
+ _base_recovery_check(ioc);
+ return;
+ }
+
+ /* Clear MPI request frame */
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(request, 0, ioc->request_sz);
+
+ mpt3sas_base_clear_st(ioc, st);
+ _base_recovery_check(ioc);
+ ioc->io_queue_num[smid - 1] = 0;
+ return;
+ }
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (smid < ioc->internal_smid) {
+ /* hi-priority */
+ i = smid - ioc->hi_priority_smid;
+ ioc->hpr_lookup[i].cb_idx = 0xFF;
+ list_add(&ioc->hpr_lookup[i].tracker_list, &ioc->hpr_free_list);
+ } else if (smid <= ioc->hba_queue_depth) {
+ /* internal queue */
+ i = smid - ioc->internal_smid;
+ ioc->internal_lookup[i].cb_idx = 0xFF;
+ list_add(&ioc->internal_lookup[i].tracker_list,
+ &ioc->internal_free_list);
+ }
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+}
+
+/**
+ * _base_mpi_ep_writeq - 32 bit write to MMIO
+ * @b: data payload
+ * @addr: address in MMIO space
+ * @writeq_lock: spin lock
+ *
+ * This special handling for MPI EP to take care of 32 bit
+ * environment where its not quarenteed to send the entire word
+ * in one transfer.
+ */
+static inline void
+_base_mpi_ep_writeq(__u64 b, volatile void __iomem *addr,
+ spinlock_t *writeq_lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(writeq_lock, flags);
+ __raw_writel((u32)(b), addr);
+ __raw_writel((u32)(b >> 32), (addr + 4));
+ spin_unlock_irqrestore(writeq_lock, flags);
+}
+
+/**
+ * _base_writeq - 64 bit write to MMIO
+ * @b: data payload
+ * @addr: address in MMIO space
+ * @writeq_lock: spin lock
+ *
+ * Glue for handling an atomic 64 bit word to MMIO. This special handling takes
+ * care of 32 bit environment where its not quarenteed to send the entire word
+ * in one transfer.
+ */
+#if defined(writeq) && defined(CONFIG_64BIT)
+static inline void
+_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock)
+{
+ wmb();
+ __raw_writeq(b, addr);
+ barrier();
+}
+#else
+static inline void
+_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock)
+{
+ _base_mpi_ep_writeq(b, addr, writeq_lock);
+}
+#endif
+
+/**
+ * _base_set_and_get_msix_index - get the msix index and assign to msix_io
+ * variable of scsi tracker
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: msix index.
+ */
+static u8
+_base_set_and_get_msix_index(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ struct scsiio_tracker *st = NULL;
+
+ if (smid < ioc->hi_priority_smid)
+ st = _get_st_from_smid(ioc, smid);
+
+ if (st == NULL)
+ return _base_get_msix_index(ioc, NULL);
+
+ st->msix_io = ioc->get_msix_index_for_smlio(ioc, st->scmd);
+ return st->msix_io;
+}
+
+/**
+ * _base_put_smid_mpi_ep_scsi_io - send SCSI_IO request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle
+ */
+static void
+_base_put_smid_mpi_ep_scsi_io(struct MPT3SAS_ADAPTER *ioc,
+ u16 smid, u16 handle)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+ void *mpi_req_iomem;
+ __le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid);
+
+ _clone_sg_entries(ioc, (void *) mfp, smid);
+ mpi_req_iomem = (void __force *)ioc->chip +
+ MPI_FRAME_START_OFFSET + (smid * ioc->request_sz);
+ _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp,
+ ioc->request_sz);
+ descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+ descriptor.SCSIIO.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.SCSIIO.SMID = cpu_to_le16(smid);
+ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
+ descriptor.SCSIIO.LMID = 0;
+ _base_mpi_ep_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * _base_put_smid_scsi_io - send SCSI_IO request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle
+ */
+static void
+_base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+
+ descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+ descriptor.SCSIIO.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.SCSIIO.SMID = cpu_to_le16(smid);
+ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
+ descriptor.SCSIIO.LMID = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * _base_put_smid_fast_path - send fast path request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle
+ */
+static void
+_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.SCSIIO.RequestFlags =
+ MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO;
+ descriptor.SCSIIO.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.SCSIIO.SMID = cpu_to_le16(smid);
+ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
+ descriptor.SCSIIO.LMID = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * _base_put_smid_hi_priority - send Task Management request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_task: msix_task will be same as msix of IO in case of task abort else 0
+ */
+static void
+_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 msix_task)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ void *mpi_req_iomem;
+ u64 *request;
+
+ if (ioc->is_mcpu_endpoint) {
+ __le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid);
+
+ /* TBD 256 is offset within sys register. */
+ mpi_req_iomem = (void __force *)ioc->chip
+ + MPI_FRAME_START_OFFSET
+ + (smid * ioc->request_sz);
+ _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp,
+ ioc->request_sz);
+ }
+
+ request = (u64 *)&descriptor;
+
+ descriptor.HighPriority.RequestFlags =
+ MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ descriptor.HighPriority.MSIxIndex = msix_task;
+ descriptor.HighPriority.SMID = cpu_to_le16(smid);
+ descriptor.HighPriority.LMID = 0;
+ descriptor.HighPriority.Reserved1 = 0;
+ if (ioc->is_mcpu_endpoint)
+ _base_mpi_ep_writeq(*request,
+ &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+ else
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * mpt3sas_base_put_smid_nvme_encap - send NVMe encapsulated request to
+ * firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ */
+void
+mpt3sas_base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.Default.RequestFlags =
+ MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED;
+ descriptor.Default.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.Default.SMID = cpu_to_le16(smid);
+ descriptor.Default.LMID = 0;
+ descriptor.Default.DescriptorTypeDependent = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * _base_put_smid_default - Default, primarily used for config pages
+ * @ioc: per adapter object
+ * @smid: system request message index
+ */
+static void
+_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ void *mpi_req_iomem;
+ u64 *request;
+
+ if (ioc->is_mcpu_endpoint) {
+ __le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid);
+
+ _clone_sg_entries(ioc, (void *) mfp, smid);
+ /* TBD 256 is offset within sys register */
+ mpi_req_iomem = (void __force *)ioc->chip +
+ MPI_FRAME_START_OFFSET + (smid * ioc->request_sz);
+ _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp,
+ ioc->request_sz);
+ }
+ request = (u64 *)&descriptor;
+ descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ descriptor.Default.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.Default.SMID = cpu_to_le16(smid);
+ descriptor.Default.LMID = 0;
+ descriptor.Default.DescriptorTypeDependent = 0;
+ if (ioc->is_mcpu_endpoint)
+ _base_mpi_ep_writeq(*request,
+ &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+ else
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * _base_put_smid_scsi_io_atomic - send SCSI_IO request to firmware using
+ * Atomic Request Descriptor
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle, unused in this function, for function type match
+ *
+ * Return: nothing.
+ */
+static void
+_base_put_smid_scsi_io_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle)
+{
+ Mpi26AtomicRequestDescriptor_t descriptor;
+ u32 *request = (u32 *)&descriptor;
+
+ descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+ descriptor.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.SMID = cpu_to_le16(smid);
+
+ writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
+}
+
+/**
+ * _base_put_smid_fast_path_atomic - send fast path request to firmware
+ * using Atomic Request Descriptor
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle, unused in this function, for function type match
+ * Return: nothing
+ */
+static void
+_base_put_smid_fast_path_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle)
+{
+ Mpi26AtomicRequestDescriptor_t descriptor;
+ u32 *request = (u32 *)&descriptor;
+
+ descriptor.RequestFlags = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO;
+ descriptor.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.SMID = cpu_to_le16(smid);
+
+ writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
+}
+
+/**
+ * _base_put_smid_hi_priority_atomic - send Task Management request to
+ * firmware using Atomic Request Descriptor
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_task: msix_task will be same as msix of IO in case of task abort else 0
+ *
+ * Return: nothing.
+ */
+static void
+_base_put_smid_hi_priority_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 msix_task)
+{
+ Mpi26AtomicRequestDescriptor_t descriptor;
+ u32 *request = (u32 *)&descriptor;
+
+ descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ descriptor.MSIxIndex = msix_task;
+ descriptor.SMID = cpu_to_le16(smid);
+
+ writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
+}
+
+/**
+ * _base_put_smid_default_atomic - Default, primarily used for config pages
+ * use Atomic Request Descriptor
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: nothing.
+ */
+static void
+_base_put_smid_default_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ Mpi26AtomicRequestDescriptor_t descriptor;
+ u32 *request = (u32 *)&descriptor;
+
+ descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ descriptor.MSIxIndex = _base_set_and_get_msix_index(ioc, smid);
+ descriptor.SMID = cpu_to_le16(smid);
+
+ writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
+}
+
+/**
+ * _base_display_OEMs_branding - Display branding string
+ * @ioc: per adapter object
+ */
+static void
+_base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc)
+{
+ if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL)
+ return;
+
+ switch (ioc->pdev->subsystem_vendor) {
+ case PCI_VENDOR_ID_INTEL:
+ switch (ioc->pdev->device) {
+ case MPI2_MFGPAGE_DEVID_SAS2008:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT2SAS_INTEL_RMS2LL080_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS2LL080_BRANDING);
+ break;
+ case MPT2SAS_INTEL_RMS2LL040_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS2LL040_BRANDING);
+ break;
+ case MPT2SAS_INTEL_SSD910_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_SSD910_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "Intel(R) Controller: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case MPI2_MFGPAGE_DEVID_SAS2308_2:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT2SAS_INTEL_RS25GB008_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RS25GB008_BRANDING);
+ break;
+ case MPT2SAS_INTEL_RMS25JB080_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS25JB080_BRANDING);
+ break;
+ case MPT2SAS_INTEL_RMS25JB040_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS25JB040_BRANDING);
+ break;
+ case MPT2SAS_INTEL_RMS25KB080_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS25KB080_BRANDING);
+ break;
+ case MPT2SAS_INTEL_RMS25KB040_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS25KB040_BRANDING);
+ break;
+ case MPT2SAS_INTEL_RMS25LB040_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS25LB040_BRANDING);
+ break;
+ case MPT2SAS_INTEL_RMS25LB080_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_INTEL_RMS25LB080_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "Intel(R) Controller: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case MPI25_MFGPAGE_DEVID_SAS3008:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT3SAS_INTEL_RMS3JC080_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_INTEL_RMS3JC080_BRANDING);
+ break;
+
+ case MPT3SAS_INTEL_RS3GC008_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_INTEL_RS3GC008_BRANDING);
+ break;
+ case MPT3SAS_INTEL_RS3FC044_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_INTEL_RS3FC044_BRANDING);
+ break;
+ case MPT3SAS_INTEL_RS3UC080_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_INTEL_RS3UC080_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "Intel(R) Controller: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ default:
+ ioc_info(ioc, "Intel(R) Controller: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case PCI_VENDOR_ID_DELL:
+ switch (ioc->pdev->device) {
+ case MPI2_MFGPAGE_DEVID_SAS2008:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING);
+ break;
+ case MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING);
+ break;
+ case MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING);
+ break;
+ case MPT2SAS_DELL_PERC_H200_MODULAR_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING);
+ break;
+ case MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING);
+ break;
+ case MPT2SAS_DELL_PERC_H200_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_DELL_PERC_H200_BRANDING);
+ break;
+ case MPT2SAS_DELL_6GBPS_SAS_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_DELL_6GBPS_SAS_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "Dell 6Gbps HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case MPI25_MFGPAGE_DEVID_SAS3008:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT3SAS_DELL_12G_HBA_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_DELL_12G_HBA_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "Dell 12Gbps HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ default:
+ ioc_info(ioc, "Dell HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case PCI_VENDOR_ID_CISCO:
+ switch (ioc->pdev->device) {
+ case MPI25_MFGPAGE_DEVID_SAS3008:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT3SAS_CISCO_12G_8E_HBA_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_CISCO_12G_8E_HBA_BRANDING);
+ break;
+ case MPT3SAS_CISCO_12G_8I_HBA_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_CISCO_12G_8I_HBA_BRANDING);
+ break;
+ case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case MPI25_MFGPAGE_DEVID_SAS3108_1:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING);
+ break;
+ case MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ default:
+ ioc_info(ioc, "Cisco SAS HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case MPT2SAS_HP_3PAR_SSVID:
+ switch (ioc->pdev->device) {
+ case MPI2_MFGPAGE_DEVID_SAS2004:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ case MPI2_MFGPAGE_DEVID_SAS2308_2:
+ switch (ioc->pdev->subsystem_device) {
+ case MPT2SAS_HP_2_4_INTERNAL_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_HP_2_4_INTERNAL_BRANDING);
+ break;
+ case MPT2SAS_HP_2_4_EXTERNAL_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_HP_2_4_EXTERNAL_BRANDING);
+ break;
+ case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING);
+ break;
+ case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID:
+ ioc_info(ioc, "%s\n",
+ MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING);
+ break;
+ default:
+ ioc_info(ioc, "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ default:
+ ioc_info(ioc, "HP SAS HBA: Subsystem ID: 0x%X\n",
+ ioc->pdev->subsystem_device);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * _base_display_fwpkg_version - sends FWUpload request to pull FWPkg
+ * version from FW Image Header.
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+ static int
+_base_display_fwpkg_version(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2FWImageHeader_t *fw_img_hdr;
+ Mpi26ComponentImageHeader_t *cmp_img_hdr;
+ Mpi25FWUploadRequest_t *mpi_request;
+ Mpi2FWUploadReply_t mpi_reply;
+ int r = 0, issue_diag_reset = 0;
+ u32 package_version = 0;
+ void *fwpkg_data = NULL;
+ dma_addr_t fwpkg_data_dma;
+ u16 smid, ioc_status;
+ size_t data_length;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ if (ioc->base_cmds.status & MPT3_CMD_PENDING) {
+ ioc_err(ioc, "%s: internal command already in use\n", __func__);
+ return -EAGAIN;
+ }
+
+ data_length = sizeof(Mpi2FWImageHeader_t);
+ fwpkg_data = dma_alloc_coherent(&ioc->pdev->dev, data_length,
+ &fwpkg_data_dma, GFP_KERNEL);
+ if (!fwpkg_data) {
+ ioc_err(ioc,
+ "Memory allocation for fwpkg data failed at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENOMEM;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ r = -EAGAIN;
+ goto out;
+ }
+
+ ioc->base_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi25FWUploadRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_FW_UPLOAD;
+ mpi_request->ImageType = MPI2_FW_UPLOAD_ITYPE_FW_FLASH;
+ mpi_request->ImageSize = cpu_to_le32(data_length);
+ ioc->build_sg(ioc, &mpi_request->SGL, 0, 0, fwpkg_data_dma,
+ data_length);
+ init_completion(&ioc->base_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ /* Wait for 15 seconds */
+ wait_for_completion_timeout(&ioc->base_cmds.done,
+ FW_IMG_HDR_READ_TIMEOUT*HZ);
+ ioc_info(ioc, "%s: complete\n", __func__);
+ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi25FWUploadRequest_t)/4);
+ issue_diag_reset = 1;
+ } else {
+ memset(&mpi_reply, 0, sizeof(Mpi2FWUploadReply_t));
+ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) {
+ memcpy(&mpi_reply, ioc->base_cmds.reply,
+ sizeof(Mpi2FWUploadReply_t));
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ fw_img_hdr = (Mpi2FWImageHeader_t *)fwpkg_data;
+ if (le32_to_cpu(fw_img_hdr->Signature) ==
+ MPI26_IMAGE_HEADER_SIGNATURE0_MPI26) {
+ cmp_img_hdr =
+ (Mpi26ComponentImageHeader_t *)
+ (fwpkg_data);
+ package_version =
+ le32_to_cpu(
+ cmp_img_hdr->ApplicationSpecific);
+ } else
+ package_version =
+ le32_to_cpu(
+ fw_img_hdr->PackageVersion.Word);
+ if (package_version)
+ ioc_info(ioc,
+ "FW Package Ver(%02d.%02d.%02d.%02d)\n",
+ ((package_version) & 0xFF000000) >> 24,
+ ((package_version) & 0x00FF0000) >> 16,
+ ((package_version) & 0x0000FF00) >> 8,
+ (package_version) & 0x000000FF);
+ } else {
+ _debug_dump_mf(&mpi_reply,
+ sizeof(Mpi2FWUploadReply_t)/4);
+ }
+ }
+ }
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+out:
+ if (fwpkg_data)
+ dma_free_coherent(&ioc->pdev->dev, data_length, fwpkg_data,
+ fwpkg_data_dma);
+ if (issue_diag_reset) {
+ if (ioc->drv_internal_flags & MPT_DRV_INTERNAL_FIRST_PE_ISSUED)
+ return -EFAULT;
+ if (mpt3sas_base_check_for_fault_and_issue_reset(ioc))
+ return -EFAULT;
+ r = -EAGAIN;
+ }
+ return r;
+}
+
+/**
+ * _base_display_ioc_capabilities - Display IOC's capabilities.
+ * @ioc: per adapter object
+ */
+static void
+_base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i = 0;
+ char desc[17] = {0};
+ u32 iounit_pg1_flags;
+
+ memtostr(desc, ioc->manu_pg0.ChipName);
+ ioc_info(ioc, "%s: FWVersion(%02d.%02d.%02d.%02d), ChipRevision(0x%02x)\n",
+ desc,
+ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
+ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
+ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
+ ioc->facts.FWVersion.Word & 0x000000FF,
+ ioc->pdev->revision);
+
+ _base_display_OEMs_branding(ioc);
+
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES) {
+ pr_info("%sNVMe", i ? "," : "");
+ i++;
+ }
+
+ ioc_info(ioc, "Protocol=(");
+
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) {
+ pr_cont("Initiator");
+ i++;
+ }
+
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) {
+ pr_cont("%sTarget", i ? "," : "");
+ i++;
+ }
+
+ i = 0;
+ pr_cont("), Capabilities=(");
+
+ if (!ioc->hide_ir_msg) {
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
+ pr_cont("Raid");
+ i++;
+ }
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) {
+ pr_cont("%sTLR", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) {
+ pr_cont("%sMulticast", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) {
+ pr_cont("%sBIDI Target", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) {
+ pr_cont("%sEEDP", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) {
+ pr_cont("%sSnapshot Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) {
+ pr_cont("%sDiag Trace Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
+ pr_cont("%sDiag Extended Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
+ pr_cont("%sTask Set Full", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU) {
+ pr_cont("%sMCTP Passthru", i ? "," : "");
+ i++;
+ }
+
+ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
+ if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) {
+ pr_cont("%sNCQ", i ? "," : "");
+ i++;
+ }
+
+ pr_cont(")\n");
+}
+
+/**
+ * mpt3sas_base_update_missing_delay - change the missing delay timers
+ * @ioc: per adapter object
+ * @device_missing_delay: amount of time till device is reported missing
+ * @io_missing_delay: interval IO is returned when there is a missing device
+ *
+ * Passed on the command line, this function will modify the device missing
+ * delay, as well as the io missing delay. This should be called at driver
+ * load time.
+ */
+void
+mpt3sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc,
+ u16 device_missing_delay, u8 io_missing_delay)
+{
+ u16 dmd, dmd_new, dmd_orignal;
+ u8 io_missing_delay_original;
+ u16 sz;
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2ConfigReply_t mpi_reply;
+ u8 num_phys = 0;
+ u16 ioc_status;
+
+ mpt3sas_config_get_number_hba_phys(ioc, &num_phys);
+ if (!num_phys)
+ return;
+
+ sz = struct_size(sas_iounit_pg1, PhyData, num_phys);
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ /* device missing delay */
+ dmd = sas_iounit_pg1->ReportDeviceMissingDelay;
+ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
+ dmd = (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
+ else
+ dmd = dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
+ dmd_orignal = dmd;
+ if (device_missing_delay > 0x7F) {
+ dmd = (device_missing_delay > 0x7F0) ? 0x7F0 :
+ device_missing_delay;
+ dmd = dmd / 16;
+ dmd |= MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16;
+ } else
+ dmd = device_missing_delay;
+ sas_iounit_pg1->ReportDeviceMissingDelay = dmd;
+
+ /* io missing delay */
+ io_missing_delay_original = sas_iounit_pg1->IODeviceMissingDelay;
+ sas_iounit_pg1->IODeviceMissingDelay = io_missing_delay;
+
+ if (!mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
+ sz)) {
+ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
+ dmd_new = (dmd &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
+ else
+ dmd_new =
+ dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
+ ioc_info(ioc, "device_missing_delay: old(%d), new(%d)\n",
+ dmd_orignal, dmd_new);
+ ioc_info(ioc, "ioc_missing_delay: old(%d), new(%d)\n",
+ io_missing_delay_original,
+ io_missing_delay);
+ ioc->device_missing_delay = dmd_new;
+ ioc->io_missing_delay = io_missing_delay;
+ }
+
+out:
+ kfree(sas_iounit_pg1);
+}
+
+/**
+ * _base_update_ioc_page1_inlinewith_perf_mode - Update IOC Page1 fields
+ * according to performance mode.
+ * @ioc : per adapter object
+ *
+ * Return: zero on success; otherwise return EAGAIN error code asking the
+ * caller to retry.
+ */
+static int
+_base_update_ioc_page1_inlinewith_perf_mode(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2IOCPage1_t ioc_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ int rc;
+
+ rc = mpt3sas_config_get_ioc_pg1(ioc, &mpi_reply, &ioc->ioc_pg1_copy);
+ if (rc)
+ return rc;
+ memcpy(&ioc_pg1, &ioc->ioc_pg1_copy, sizeof(Mpi2IOCPage1_t));
+
+ switch (perf_mode) {
+ case MPT_PERF_MODE_DEFAULT:
+ case MPT_PERF_MODE_BALANCED:
+ if (ioc->high_iops_queues) {
+ ioc_info(ioc,
+ "Enable interrupt coalescing only for first\t"
+ "%d reply queues\n",
+ MPT3SAS_HIGH_IOPS_REPLY_QUEUES);
+ /*
+ * If 31st bit is zero then interrupt coalescing is
+ * enabled for all reply descriptor post queues.
+ * If 31st bit is set to one then user can
+ * enable/disable interrupt coalescing on per reply
+ * descriptor post queue group(8) basis. So to enable
+ * interrupt coalescing only on first reply descriptor
+ * post queue group 31st bit and zero th bit is enabled.
+ */
+ ioc_pg1.ProductSpecific = cpu_to_le32(0x80000000 |
+ ((1 << MPT3SAS_HIGH_IOPS_REPLY_QUEUES/8) - 1));
+ rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1);
+ if (rc)
+ return rc;
+ ioc_info(ioc, "performance mode: balanced\n");
+ return 0;
+ }
+ fallthrough;
+ case MPT_PERF_MODE_LATENCY:
+ /*
+ * Enable interrupt coalescing on all reply queues
+ * with timeout value 0xA
+ */
+ ioc_pg1.CoalescingTimeout = cpu_to_le32(0xa);
+ ioc_pg1.Flags |= cpu_to_le32(MPI2_IOCPAGE1_REPLY_COALESCING);
+ ioc_pg1.ProductSpecific = 0;
+ rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1);
+ if (rc)
+ return rc;
+ ioc_info(ioc, "performance mode: latency\n");
+ break;
+ case MPT_PERF_MODE_IOPS:
+ /*
+ * Enable interrupt coalescing on all reply queues.
+ */
+ ioc_info(ioc,
+ "performance mode: iops with coalescing timeout: 0x%x\n",
+ le32_to_cpu(ioc_pg1.CoalescingTimeout));
+ ioc_pg1.Flags |= cpu_to_le32(MPI2_IOCPAGE1_REPLY_COALESCING);
+ ioc_pg1.ProductSpecific = 0;
+ rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1);
+ if (rc)
+ return rc;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * _base_get_event_diag_triggers - get event diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return: nothing.
+ */
+static int
+_base_get_event_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi26DriverTriggerPage2_t trigger_pg2;
+ struct SL_WH_EVENT_TRIGGER_T *event_tg;
+ MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY *mpi_event_tg;
+ Mpi2ConfigReply_t mpi_reply;
+ int r = 0, i = 0;
+ u16 count = 0;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg2(ioc, &mpi_reply,
+ &trigger_pg2);
+ if (r)
+ return r;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return 0;
+ }
+
+ if (le16_to_cpu(trigger_pg2.NumMPIEventTrigger)) {
+ count = le16_to_cpu(trigger_pg2.NumMPIEventTrigger);
+ count = min_t(u16, NUM_VALID_ENTRIES, count);
+ ioc->diag_trigger_event.ValidEntries = count;
+
+ event_tg = &ioc->diag_trigger_event.EventTriggerEntry[0];
+ mpi_event_tg = &trigger_pg2.MPIEventTriggers[0];
+ for (i = 0; i < count; i++) {
+ event_tg->EventValue = le16_to_cpu(
+ mpi_event_tg->MPIEventCode);
+ event_tg->LogEntryQualifier = le16_to_cpu(
+ mpi_event_tg->MPIEventCodeSpecific);
+ event_tg++;
+ mpi_event_tg++;
+ }
+ }
+ return 0;
+}
+
+/**
+ * _base_get_scsi_diag_triggers - get scsi diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return: 0 on success; otherwise return failure status.
+ */
+static int
+_base_get_scsi_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi26DriverTriggerPage3_t trigger_pg3;
+ struct SL_WH_SCSI_TRIGGER_T *scsi_tg;
+ MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY *mpi_scsi_tg;
+ Mpi2ConfigReply_t mpi_reply;
+ int r = 0, i = 0;
+ u16 count = 0;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg3(ioc, &mpi_reply,
+ &trigger_pg3);
+ if (r)
+ return r;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg3, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return 0;
+ }
+
+ if (le16_to_cpu(trigger_pg3.NumSCSISenseTrigger)) {
+ count = le16_to_cpu(trigger_pg3.NumSCSISenseTrigger);
+ count = min_t(u16, NUM_VALID_ENTRIES, count);
+ ioc->diag_trigger_scsi.ValidEntries = count;
+
+ scsi_tg = &ioc->diag_trigger_scsi.SCSITriggerEntry[0];
+ mpi_scsi_tg = &trigger_pg3.SCSISenseTriggers[0];
+ for (i = 0; i < count; i++) {
+ scsi_tg->ASCQ = mpi_scsi_tg->ASCQ;
+ scsi_tg->ASC = mpi_scsi_tg->ASC;
+ scsi_tg->SenseKey = mpi_scsi_tg->SenseKey;
+
+ scsi_tg++;
+ mpi_scsi_tg++;
+ }
+ }
+ return 0;
+}
+
+/**
+ * _base_get_mpi_diag_triggers - get mpi diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return: 0 on success; otherwise return failure status.
+ */
+static int
+_base_get_mpi_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi26DriverTriggerPage4_t trigger_pg4;
+ struct SL_WH_MPI_TRIGGER_T *status_tg;
+ MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY *mpi_status_tg;
+ Mpi2ConfigReply_t mpi_reply;
+ int r = 0, i = 0;
+ u16 count = 0;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg4(ioc, &mpi_reply,
+ &trigger_pg4);
+ if (r)
+ return r;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg4, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return 0;
+ }
+
+ if (le16_to_cpu(trigger_pg4.NumIOCStatusLogInfoTrigger)) {
+ count = le16_to_cpu(trigger_pg4.NumIOCStatusLogInfoTrigger);
+ count = min_t(u16, NUM_VALID_ENTRIES, count);
+ ioc->diag_trigger_mpi.ValidEntries = count;
+
+ status_tg = &ioc->diag_trigger_mpi.MPITriggerEntry[0];
+ mpi_status_tg = &trigger_pg4.IOCStatusLoginfoTriggers[0];
+
+ for (i = 0; i < count; i++) {
+ status_tg->IOCStatus = le16_to_cpu(
+ mpi_status_tg->IOCStatus);
+ status_tg->IocLogInfo = le32_to_cpu(
+ mpi_status_tg->LogInfo);
+
+ status_tg++;
+ mpi_status_tg++;
+ }
+ }
+ return 0;
+}
+
+/**
+ * _base_get_master_diag_triggers - get master diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return: nothing.
+ */
+static int
+_base_get_master_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi26DriverTriggerPage1_t trigger_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ int r;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg1(ioc, &mpi_reply,
+ &trigger_pg1);
+ if (r)
+ return r;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return 0;
+ }
+
+ if (le16_to_cpu(trigger_pg1.NumMasterTrigger))
+ ioc->diag_trigger_master.MasterData |=
+ le32_to_cpu(
+ trigger_pg1.MasterTriggers[0].MasterTriggerFlags);
+ return 0;
+}
+
+/**
+ * _base_check_for_trigger_pages_support - checks whether HBA FW supports
+ * driver trigger pages or not
+ * @ioc : per adapter object
+ * @trigger_flags : address where trigger page0's TriggerFlags value is copied
+ *
+ * Return: trigger flags mask if HBA FW supports driver trigger pages;
+ * otherwise returns %-EFAULT if driver trigger pages are not supported by FW or
+ * return EAGAIN if diag reset occurred due to FW fault and asking the
+ * caller to retry the command.
+ *
+ */
+static int
+_base_check_for_trigger_pages_support(struct MPT3SAS_ADAPTER *ioc, u32 *trigger_flags)
+{
+ Mpi26DriverTriggerPage0_t trigger_pg0;
+ int r = 0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg0(ioc, &mpi_reply,
+ &trigger_pg0);
+ if (r)
+ return r;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ return -EFAULT;
+
+ *trigger_flags = le16_to_cpu(trigger_pg0.TriggerFlags);
+ return 0;
+}
+
+/**
+ * _base_get_diag_triggers - Retrieve diag trigger values from
+ * persistent pages.
+ * @ioc : per adapter object
+ *
+ * Return: zero on success; otherwise return EAGAIN error codes
+ * asking the caller to retry.
+ */
+static int
+_base_get_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+{
+ int trigger_flags;
+ int r;
+
+ /*
+ * Default setting of master trigger.
+ */
+ ioc->diag_trigger_master.MasterData =
+ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET);
+
+ r = _base_check_for_trigger_pages_support(ioc, &trigger_flags);
+ if (r) {
+ if (r == -EAGAIN)
+ return r;
+ /*
+ * Don't go for error handling when FW doesn't support
+ * driver trigger pages.
+ */
+ return 0;
+ }
+
+ ioc->supports_trigger_pages = 1;
+
+ /*
+ * Retrieve master diag trigger values from driver trigger pg1
+ * if master trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID) {
+ r = _base_get_master_diag_triggers(ioc);
+ if (r)
+ return r;
+ }
+
+ /*
+ * Retrieve event diag trigger values from driver trigger pg2
+ * if event trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID) {
+ r = _base_get_event_diag_triggers(ioc);
+ if (r)
+ return r;
+ }
+
+ /*
+ * Retrieve scsi diag trigger values from driver trigger pg3
+ * if scsi trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID) {
+ r = _base_get_scsi_diag_triggers(ioc);
+ if (r)
+ return r;
+ }
+ /*
+ * Retrieve mpi error diag trigger values from driver trigger pg4
+ * if loginfo trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID) {
+ r = _base_get_mpi_diag_triggers(ioc);
+ if (r)
+ return r;
+ }
+ return 0;
+}
+
+/**
+ * _base_update_diag_trigger_pages - Update the driver trigger pages after
+ * online FW update, in case updated FW supports driver
+ * trigger pages.
+ * @ioc : per adapter object
+ *
+ * Return: nothing.
+ */
+static void
+_base_update_diag_trigger_pages(struct MPT3SAS_ADAPTER *ioc)
+{
+
+ if (ioc->diag_trigger_master.MasterData)
+ mpt3sas_config_update_driver_trigger_pg1(ioc,
+ &ioc->diag_trigger_master, 1);
+
+ if (ioc->diag_trigger_event.ValidEntries)
+ mpt3sas_config_update_driver_trigger_pg2(ioc,
+ &ioc->diag_trigger_event, 1);
+
+ if (ioc->diag_trigger_scsi.ValidEntries)
+ mpt3sas_config_update_driver_trigger_pg3(ioc,
+ &ioc->diag_trigger_scsi, 1);
+
+ if (ioc->diag_trigger_mpi.ValidEntries)
+ mpt3sas_config_update_driver_trigger_pg4(ioc,
+ &ioc->diag_trigger_mpi, 1);
+}
+
+/**
+ * _base_assign_fw_reported_qd - Get FW reported QD for SAS/SATA devices.
+ * - On failure set default QD values.
+ * @ioc : per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ */
+static int _base_assign_fw_reported_qd(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage1_t sas_iounit_pg1;
+ Mpi26PCIeIOUnitPage1_t pcie_iounit_pg1;
+ u16 depth;
+ int rc = 0;
+
+ ioc->max_wideport_qd = MPT3SAS_SAS_QUEUE_DEPTH;
+ ioc->max_narrowport_qd = MPT3SAS_SAS_QUEUE_DEPTH;
+ ioc->max_sata_qd = MPT3SAS_SATA_QUEUE_DEPTH;
+ ioc->max_nvme_qd = MPT3SAS_NVME_QUEUE_DEPTH;
+ if (!ioc->is_gen35_ioc)
+ goto out;
+ /* sas iounit page 1 */
+ rc = mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ &sas_iounit_pg1, sizeof(Mpi2SasIOUnitPage1_t));
+ if (rc) {
+ pr_err("%s: failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ depth = le16_to_cpu(sas_iounit_pg1.SASWideMaxQueueDepth);
+ ioc->max_wideport_qd = (depth ? depth : MPT3SAS_SAS_QUEUE_DEPTH);
+
+ depth = le16_to_cpu(sas_iounit_pg1.SASNarrowMaxQueueDepth);
+ ioc->max_narrowport_qd = (depth ? depth : MPT3SAS_SAS_QUEUE_DEPTH);
+
+ depth = sas_iounit_pg1.SATAMaxQDepth;
+ ioc->max_sata_qd = (depth ? depth : MPT3SAS_SATA_QUEUE_DEPTH);
+
+ /* pcie iounit page 1 */
+ rc = mpt3sas_config_get_pcie_iounit_pg1(ioc, &mpi_reply,
+ &pcie_iounit_pg1, sizeof(Mpi26PCIeIOUnitPage1_t));
+ if (rc) {
+ pr_err("%s: failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc->max_nvme_qd = (le16_to_cpu(pcie_iounit_pg1.NVMeMaxQueueDepth)) ?
+ (le16_to_cpu(pcie_iounit_pg1.NVMeMaxQueueDepth)) :
+ MPT3SAS_NVME_QUEUE_DEPTH;
+out:
+ dinitprintk(ioc, pr_err(
+ "MaxWidePortQD: 0x%x MaxNarrowPortQD: 0x%x MaxSataQD: 0x%x MaxNvmeQD: 0x%x\n",
+ ioc->max_wideport_qd, ioc->max_narrowport_qd,
+ ioc->max_sata_qd, ioc->max_nvme_qd));
+ return rc;
+}
+
+/**
+ * mpt3sas_atto_validate_nvram - validate the ATTO nvram read from mfg pg1
+ *
+ * @ioc : per adapter object
+ * @n : ptr to the ATTO nvram structure
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+mpt3sas_atto_validate_nvram(struct MPT3SAS_ADAPTER *ioc,
+ struct ATTO_SAS_NVRAM *n)
+{
+ int r = -EINVAL;
+ union ATTO_SAS_ADDRESS *s1;
+ u32 len;
+ u8 *pb;
+ u8 ckSum;
+
+ /* validate nvram checksum */
+ pb = (u8 *) n;
+ ckSum = ATTO_SASNVR_CKSUM_SEED;
+ len = sizeof(struct ATTO_SAS_NVRAM);
+
+ while (len--)
+ ckSum = ckSum + pb[len];
+
+ if (ckSum) {
+ ioc_err(ioc, "Invalid ATTO NVRAM checksum\n");
+ return r;
+ }
+
+ s1 = (union ATTO_SAS_ADDRESS *) n->SasAddr;
+
+ if (n->Signature[0] != 'E'
+ || n->Signature[1] != 'S'
+ || n->Signature[2] != 'A'
+ || n->Signature[3] != 'S')
+ ioc_err(ioc, "Invalid ATTO NVRAM signature\n");
+ else if (n->Version > ATTO_SASNVR_VERSION)
+ ioc_info(ioc, "Invalid ATTO NVRAM version");
+ else if ((n->SasAddr[7] & (ATTO_SAS_ADDR_ALIGN - 1))
+ || s1->b[0] != 0x50
+ || s1->b[1] != 0x01
+ || s1->b[2] != 0x08
+ || (s1->b[3] & 0xF0) != 0x60
+ || ((s1->b[3] & 0x0F) | le32_to_cpu(s1->d[1])) == 0) {
+ ioc_err(ioc, "Invalid ATTO SAS address\n");
+ } else
+ r = 0;
+ return r;
+}
+
+/**
+ * mpt3sas_atto_get_sas_addr - get the ATTO SAS address from mfg page 1
+ *
+ * @ioc : per adapter object
+ * @sas_addr : return sas address
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+mpt3sas_atto_get_sas_addr(struct MPT3SAS_ADAPTER *ioc, union ATTO_SAS_ADDRESS *sas_addr)
+{
+ Mpi2ManufacturingPage1_t mfg_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ struct ATTO_SAS_NVRAM *nvram;
+ int r;
+ __be64 addr;
+
+ r = mpt3sas_config_get_manufacturing_pg1(ioc, &mpi_reply, &mfg_pg1);
+ if (r) {
+ ioc_err(ioc, "Failed to read manufacturing page 1\n");
+ return r;
+ }
+
+ /* validate nvram */
+ nvram = (struct ATTO_SAS_NVRAM *) mfg_pg1.VPD;
+ r = mpt3sas_atto_validate_nvram(ioc, nvram);
+ if (r)
+ return r;
+
+ addr = *((__be64 *) nvram->SasAddr);
+ sas_addr->q = cpu_to_le64(be64_to_cpu(addr));
+ return r;
+}
+
+/**
+ * mpt3sas_atto_init - perform initializaion for ATTO branded
+ * adapter.
+ * @ioc : per adapter object
+ *5
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+mpt3sas_atto_init(struct MPT3SAS_ADAPTER *ioc)
+{
+ int sz = 0;
+ Mpi2BiosPage4_t *bios_pg4 = NULL;
+ Mpi2ConfigReply_t mpi_reply;
+ int r;
+ int ix;
+ union ATTO_SAS_ADDRESS sas_addr;
+ union ATTO_SAS_ADDRESS temp;
+ union ATTO_SAS_ADDRESS bias;
+
+ r = mpt3sas_atto_get_sas_addr(ioc, &sas_addr);
+ if (r)
+ return r;
+
+ /* get header first to get size */
+ r = mpt3sas_config_get_bios_pg4(ioc, &mpi_reply, NULL, 0);
+ if (r) {
+ ioc_err(ioc, "Failed to read ATTO bios page 4 header.\n");
+ return r;
+ }
+
+ sz = mpi_reply.Header.PageLength * sizeof(u32);
+ bios_pg4 = kzalloc(sz, GFP_KERNEL);
+ if (!bios_pg4) {
+ ioc_err(ioc, "Failed to allocate memory for ATTO bios page.\n");
+ return -ENOMEM;
+ }
+
+ /* read bios page 4 */
+ r = mpt3sas_config_get_bios_pg4(ioc, &mpi_reply, bios_pg4, sz);
+ if (r) {
+ ioc_err(ioc, "Failed to read ATTO bios page 4\n");
+ goto out;
+ }
+
+ /* Update bios page 4 with the ATTO WWID */
+ bias.q = sas_addr.q;
+ bias.b[7] += ATTO_SAS_ADDR_DEVNAME_BIAS;
+
+ for (ix = 0; ix < bios_pg4->NumPhys; ix++) {
+ temp.q = sas_addr.q;
+ temp.b[7] += ix;
+ bios_pg4->Phy[ix].ReassignmentWWID = temp.q;
+ bios_pg4->Phy[ix].ReassignmentDeviceName = bias.q;
+ }
+ r = mpt3sas_config_set_bios_pg4(ioc, &mpi_reply, bios_pg4, sz);
+
+out:
+ kfree(bios_pg4);
+ return r;
+}
+
+/**
+ * _base_static_config_pages - static start of day config pages
+ * @ioc: per adapter object
+ */
+static int
+_base_static_config_pages(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2IOUnitPage8_t iounit_pg8;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 iounit_pg1_flags;
+ int tg_flags = 0;
+ int rc;
+ ioc->nvme_abort_timeout = 30;
+
+ rc = mpt3sas_config_get_manufacturing_pg0(ioc, &mpi_reply,
+ &ioc->manu_pg0);
+ if (rc)
+ return rc;
+ if (ioc->ir_firmware) {
+ rc = mpt3sas_config_get_manufacturing_pg10(ioc, &mpi_reply,
+ &ioc->manu_pg10);
+ if (rc)
+ return rc;
+ }
+
+ if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO) {
+ rc = mpt3sas_atto_init(ioc);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Ensure correct T10 PI operation if vendor left EEDPTagMode
+ * flag unset in NVDATA.
+ */
+ rc = mpt3sas_config_get_manufacturing_pg11(ioc, &mpi_reply,
+ &ioc->manu_pg11);
+ if (rc)
+ return rc;
+ if (!ioc->is_gen35_ioc && ioc->manu_pg11.EEDPTagMode == 0) {
+ pr_err("%s: overriding NVDATA EEDPTagMode setting from 0 to 1\n",
+ ioc->name);
+ ioc->manu_pg11.EEDPTagMode = 0x1;
+ mpt3sas_config_set_manufacturing_pg11(ioc, &mpi_reply,
+ &ioc->manu_pg11);
+ }
+ if (ioc->manu_pg11.AddlFlags2 & NVME_TASK_MNGT_CUSTOM_MASK)
+ ioc->tm_custom_handling = 1;
+ else {
+ ioc->tm_custom_handling = 0;
+ if (ioc->manu_pg11.NVMeAbortTO < NVME_TASK_ABORT_MIN_TIMEOUT)
+ ioc->nvme_abort_timeout = NVME_TASK_ABORT_MIN_TIMEOUT;
+ else if (ioc->manu_pg11.NVMeAbortTO >
+ NVME_TASK_ABORT_MAX_TIMEOUT)
+ ioc->nvme_abort_timeout = NVME_TASK_ABORT_MAX_TIMEOUT;
+ else
+ ioc->nvme_abort_timeout = ioc->manu_pg11.NVMeAbortTO;
+ }
+ ioc->time_sync_interval =
+ ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_MASK;
+ if (ioc->time_sync_interval) {
+ if (ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_UNIT_MASK)
+ ioc->time_sync_interval =
+ ioc->time_sync_interval * SECONDS_PER_HOUR;
+ else
+ ioc->time_sync_interval =
+ ioc->time_sync_interval * SECONDS_PER_MIN;
+ dinitprintk(ioc, ioc_info(ioc,
+ "Driver-FW TimeSync interval is %d seconds. ManuPg11 TimeSync Unit is in %s\n",
+ ioc->time_sync_interval, (ioc->manu_pg11.TimeSyncInterval &
+ MPT3SAS_TIMESYNC_UNIT_MASK) ? "Hour" : "Minute"));
+ } else {
+ if (ioc->is_gen35_ioc)
+ ioc_warn(ioc,
+ "TimeSync Interval in Manuf page-11 is not enabled. Periodic Time-Sync will be disabled\n");
+ }
+ rc = _base_assign_fw_reported_qd(ioc);
+ if (rc)
+ return rc;
+
+ /*
+ * ATTO doesn't use bios page 2 and 3 for bios settings.
+ */
+ if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO)
+ ioc->bios_pg3.BiosVersion = 0;
+ else {
+ rc = mpt3sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2);
+ if (rc)
+ return rc;
+ rc = mpt3sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3);
+ if (rc)
+ return rc;
+ }
+
+ rc = mpt3sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8);
+ if (rc)
+ return rc;
+ rc = mpt3sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0);
+ if (rc)
+ return rc;
+ rc = mpt3sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
+ if (rc)
+ return rc;
+ rc = mpt3sas_config_get_iounit_pg8(ioc, &mpi_reply, &iounit_pg8);
+ if (rc)
+ return rc;
+ _base_display_ioc_capabilities(ioc);
+
+ /*
+ * Enable task_set_full handling in iounit_pg1 when the
+ * facts capabilities indicate that its supported.
+ */
+ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
+ if ((ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING))
+ iounit_pg1_flags &=
+ ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
+ else
+ iounit_pg1_flags |=
+ MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
+ ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags);
+ rc = mpt3sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
+ if (rc)
+ return rc;
+
+ if (iounit_pg8.NumSensors)
+ ioc->temp_sensors_count = iounit_pg8.NumSensors;
+ if (ioc->is_aero_ioc) {
+ rc = _base_update_ioc_page1_inlinewith_perf_mode(ioc);
+ if (rc)
+ return rc;
+ }
+ if (ioc->is_gen35_ioc) {
+ if (ioc->is_driver_loading) {
+ rc = _base_get_diag_triggers(ioc);
+ if (rc)
+ return rc;
+ } else {
+ /*
+ * In case of online HBA FW update operation,
+ * check whether updated FW supports the driver trigger
+ * pages or not.
+ * - If previous FW has not supported driver trigger
+ * pages and newer FW supports them then update these
+ * pages with current diag trigger values.
+ * - If previous FW has supported driver trigger pages
+ * and new FW doesn't support them then disable
+ * support_trigger_pages flag.
+ */
+ _base_check_for_trigger_pages_support(ioc, &tg_flags);
+ if (!ioc->supports_trigger_pages && tg_flags != -EFAULT)
+ _base_update_diag_trigger_pages(ioc);
+ else if (ioc->supports_trigger_pages &&
+ tg_flags == -EFAULT)
+ ioc->supports_trigger_pages = 0;
+ }
+ }
+ return 0;
+}
+
+/**
+ * mpt3sas_free_enclosure_list - release memory
+ * @ioc: per adapter object
+ *
+ * Free memory allocated during enclosure add.
+ */
+void
+mpt3sas_free_enclosure_list(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _enclosure_node *enclosure_dev, *enclosure_dev_next;
+
+ /* Free enclosure list */
+ list_for_each_entry_safe(enclosure_dev,
+ enclosure_dev_next, &ioc->enclosure_list, list) {
+ list_del(&enclosure_dev->list);
+ kfree(enclosure_dev);
+ }
+}
+
+/**
+ * _base_release_memory_pools - release memory
+ * @ioc: per adapter object
+ *
+ * Free memory allocated from _base_allocate_memory_pools.
+ */
+static void
+_base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i = 0;
+ int j = 0;
+ int dma_alloc_count = 0;
+ struct chain_tracker *ct;
+ int count = ioc->rdpq_array_enable ? ioc->reply_queue_count : 1;
+
+ dexitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ if (ioc->request) {
+ dma_free_coherent(&ioc->pdev->dev, ioc->request_dma_sz,
+ ioc->request, ioc->request_dma);
+ dexitprintk(ioc,
+ ioc_info(ioc, "request_pool(0x%p): free\n",
+ ioc->request));
+ ioc->request = NULL;
+ }
+
+ if (ioc->sense) {
+ dma_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma);
+ dma_pool_destroy(ioc->sense_dma_pool);
+ dexitprintk(ioc,
+ ioc_info(ioc, "sense_pool(0x%p): free\n",
+ ioc->sense));
+ ioc->sense = NULL;
+ }
+
+ if (ioc->reply) {
+ dma_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma);
+ dma_pool_destroy(ioc->reply_dma_pool);
+ dexitprintk(ioc,
+ ioc_info(ioc, "reply_pool(0x%p): free\n",
+ ioc->reply));
+ ioc->reply = NULL;
+ }
+
+ if (ioc->reply_free) {
+ dma_pool_free(ioc->reply_free_dma_pool, ioc->reply_free,
+ ioc->reply_free_dma);
+ dma_pool_destroy(ioc->reply_free_dma_pool);
+ dexitprintk(ioc,
+ ioc_info(ioc, "reply_free_pool(0x%p): free\n",
+ ioc->reply_free));
+ ioc->reply_free = NULL;
+ }
+
+ if (ioc->reply_post) {
+ dma_alloc_count = DIV_ROUND_UP(count,
+ RDPQ_MAX_INDEX_IN_ONE_CHUNK);
+ for (i = 0; i < count; i++) {
+ if (i % RDPQ_MAX_INDEX_IN_ONE_CHUNK == 0
+ && dma_alloc_count) {
+ if (ioc->reply_post[i].reply_post_free) {
+ dma_pool_free(
+ ioc->reply_post_free_dma_pool,
+ ioc->reply_post[i].reply_post_free,
+ ioc->reply_post[i].reply_post_free_dma);
+ dexitprintk(ioc, ioc_info(ioc,
+ "reply_post_free_pool(0x%p): free\n",
+ ioc->reply_post[i].reply_post_free));
+ ioc->reply_post[i].reply_post_free =
+ NULL;
+ }
+ --dma_alloc_count;
+ }
+ }
+ dma_pool_destroy(ioc->reply_post_free_dma_pool);
+ if (ioc->reply_post_free_array &&
+ ioc->rdpq_array_enable) {
+ dma_pool_free(ioc->reply_post_free_array_dma_pool,
+ ioc->reply_post_free_array,
+ ioc->reply_post_free_array_dma);
+ ioc->reply_post_free_array = NULL;
+ }
+ dma_pool_destroy(ioc->reply_post_free_array_dma_pool);
+ kfree(ioc->reply_post);
+ }
+
+ if (ioc->pcie_sgl_dma_pool) {
+ for (i = 0; i < ioc->scsiio_depth; i++) {
+ dma_pool_free(ioc->pcie_sgl_dma_pool,
+ ioc->pcie_sg_lookup[i].pcie_sgl,
+ ioc->pcie_sg_lookup[i].pcie_sgl_dma);
+ ioc->pcie_sg_lookup[i].pcie_sgl = NULL;
+ }
+ dma_pool_destroy(ioc->pcie_sgl_dma_pool);
+ }
+ kfree(ioc->pcie_sg_lookup);
+ ioc->pcie_sg_lookup = NULL;
+
+ if (ioc->config_page) {
+ dexitprintk(ioc,
+ ioc_info(ioc, "config_page(0x%p): free\n",
+ ioc->config_page));
+ dma_free_coherent(&ioc->pdev->dev, ioc->config_page_sz,
+ ioc->config_page, ioc->config_page_dma);
+ }
+
+ kfree(ioc->hpr_lookup);
+ ioc->hpr_lookup = NULL;
+ kfree(ioc->internal_lookup);
+ ioc->internal_lookup = NULL;
+ if (ioc->chain_lookup) {
+ for (i = 0; i < ioc->scsiio_depth; i++) {
+ for (j = ioc->chains_per_prp_buffer;
+ j < ioc->chains_needed_per_io; j++) {
+ ct = &ioc->chain_lookup[i].chains_per_smid[j];
+ if (ct && ct->chain_buffer)
+ dma_pool_free(ioc->chain_dma_pool,
+ ct->chain_buffer,
+ ct->chain_buffer_dma);
+ }
+ kfree(ioc->chain_lookup[i].chains_per_smid);
+ }
+ dma_pool_destroy(ioc->chain_dma_pool);
+ kfree(ioc->chain_lookup);
+ ioc->chain_lookup = NULL;
+ }
+
+ kfree(ioc->io_queue_num);
+ ioc->io_queue_num = NULL;
+}
+
+/**
+ * mpt3sas_check_same_4gb_region - checks whether all reply queues in a set are
+ * having same upper 32bits in their base memory address.
+ * @start_address: Base address of a reply queue set
+ * @pool_sz: Size of single Reply Descriptor Post Queues pool size
+ *
+ * Return: 1 if reply queues in a set have a same upper 32bits in their base
+ * memory address, else 0.
+ */
+static int
+mpt3sas_check_same_4gb_region(dma_addr_t start_address, u32 pool_sz)
+{
+ dma_addr_t end_address;
+
+ end_address = start_address + pool_sz - 1;
+
+ if (upper_32_bits(start_address) == upper_32_bits(end_address))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * _base_reduce_hba_queue_depth- Retry with reduced queue depth
+ * @ioc: Adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ **/
+static inline int
+_base_reduce_hba_queue_depth(struct MPT3SAS_ADAPTER *ioc)
+{
+ int reduce_sz = 64;
+
+ if ((ioc->hba_queue_depth - reduce_sz) >
+ (ioc->internal_depth + INTERNAL_SCSIIO_CMDS_COUNT)) {
+ ioc->hba_queue_depth -= reduce_sz;
+ return 0;
+ } else
+ return -ENOMEM;
+}
+
+/**
+ * _base_allocate_pcie_sgl_pool - Allocating DMA'able memory
+ * for pcie sgl pools.
+ * @ioc: Adapter object
+ * @sz: DMA Pool size
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+
+static int
+_base_allocate_pcie_sgl_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz)
+{
+ int i = 0, j = 0;
+ struct chain_tracker *ct;
+
+ ioc->pcie_sgl_dma_pool =
+ dma_pool_create("PCIe SGL pool", &ioc->pdev->dev, sz,
+ ioc->page_size, 0);
+ if (!ioc->pcie_sgl_dma_pool) {
+ ioc_err(ioc, "PCIe SGL pool: dma_pool_create failed\n");
+ return -ENOMEM;
+ }
+
+ ioc->chains_per_prp_buffer = sz/ioc->chain_segment_sz;
+ ioc->chains_per_prp_buffer =
+ min(ioc->chains_per_prp_buffer, ioc->chains_needed_per_io);
+ for (i = 0; i < ioc->scsiio_depth; i++) {
+ ioc->pcie_sg_lookup[i].pcie_sgl =
+ dma_pool_alloc(ioc->pcie_sgl_dma_pool, GFP_KERNEL,
+ &ioc->pcie_sg_lookup[i].pcie_sgl_dma);
+ if (!ioc->pcie_sg_lookup[i].pcie_sgl) {
+ ioc_err(ioc, "PCIe SGL pool: dma_pool_alloc failed\n");
+ return -EAGAIN;
+ }
+
+ if (!mpt3sas_check_same_4gb_region(
+ ioc->pcie_sg_lookup[i].pcie_sgl_dma, sz)) {
+ ioc_err(ioc, "PCIE SGLs are not in same 4G !! pcie sgl (0x%p) dma = (0x%llx)\n",
+ ioc->pcie_sg_lookup[i].pcie_sgl,
+ (unsigned long long)
+ ioc->pcie_sg_lookup[i].pcie_sgl_dma);
+ ioc->use_32bit_dma = true;
+ return -EAGAIN;
+ }
+
+ for (j = 0; j < ioc->chains_per_prp_buffer; j++) {
+ ct = &ioc->chain_lookup[i].chains_per_smid[j];
+ ct->chain_buffer =
+ ioc->pcie_sg_lookup[i].pcie_sgl +
+ (j * ioc->chain_segment_sz);
+ ct->chain_buffer_dma =
+ ioc->pcie_sg_lookup[i].pcie_sgl_dma +
+ (j * ioc->chain_segment_sz);
+ }
+ }
+ dinitprintk(ioc, ioc_info(ioc,
+ "PCIe sgl pool depth(%d), element_size(%d), pool_size(%d kB)\n",
+ ioc->scsiio_depth, sz, (sz * ioc->scsiio_depth)/1024));
+ dinitprintk(ioc, ioc_info(ioc,
+ "Number of chains can fit in a PRP page(%d)\n",
+ ioc->chains_per_prp_buffer));
+ return 0;
+}
+
+/**
+ * _base_allocate_chain_dma_pool - Allocating DMA'able memory
+ * for chain dma pool.
+ * @ioc: Adapter object
+ * @sz: DMA Pool size
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_allocate_chain_dma_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz)
+{
+ int i = 0, j = 0;
+ struct chain_tracker *ctr;
+
+ ioc->chain_dma_pool = dma_pool_create("chain pool", &ioc->pdev->dev,
+ ioc->chain_segment_sz, 16, 0);
+ if (!ioc->chain_dma_pool)
+ return -ENOMEM;
+
+ for (i = 0; i < ioc->scsiio_depth; i++) {
+ for (j = ioc->chains_per_prp_buffer;
+ j < ioc->chains_needed_per_io; j++) {
+ ctr = &ioc->chain_lookup[i].chains_per_smid[j];
+ ctr->chain_buffer = dma_pool_alloc(ioc->chain_dma_pool,
+ GFP_KERNEL, &ctr->chain_buffer_dma);
+ if (!ctr->chain_buffer)
+ return -EAGAIN;
+ if (!mpt3sas_check_same_4gb_region(
+ ctr->chain_buffer_dma, ioc->chain_segment_sz)) {
+ ioc_err(ioc,
+ "Chain buffers are not in same 4G !!! Chain buff (0x%p) dma = (0x%llx)\n",
+ ctr->chain_buffer,
+ (unsigned long long)ctr->chain_buffer_dma);
+ ioc->use_32bit_dma = true;
+ return -EAGAIN;
+ }
+ }
+ }
+ dinitprintk(ioc, ioc_info(ioc,
+ "chain_lookup depth (%d), frame_size(%d), pool_size(%d kB)\n",
+ ioc->scsiio_depth, ioc->chain_segment_sz, ((ioc->scsiio_depth *
+ (ioc->chains_needed_per_io - ioc->chains_per_prp_buffer) *
+ ioc->chain_segment_sz))/1024));
+ return 0;
+}
+
+/**
+ * _base_allocate_sense_dma_pool - Allocating DMA'able memory
+ * for sense dma pool.
+ * @ioc: Adapter object
+ * @sz: DMA Pool size
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_allocate_sense_dma_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz)
+{
+ ioc->sense_dma_pool =
+ dma_pool_create("sense pool", &ioc->pdev->dev, sz, 4, 0);
+ if (!ioc->sense_dma_pool)
+ return -ENOMEM;
+ ioc->sense = dma_pool_alloc(ioc->sense_dma_pool,
+ GFP_KERNEL, &ioc->sense_dma);
+ if (!ioc->sense)
+ return -EAGAIN;
+ if (!mpt3sas_check_same_4gb_region(ioc->sense_dma, sz)) {
+ dinitprintk(ioc, pr_err(
+ "Bad Sense Pool! sense (0x%p) sense_dma = (0x%llx)\n",
+ ioc->sense, (unsigned long long) ioc->sense_dma));
+ ioc->use_32bit_dma = true;
+ return -EAGAIN;
+ }
+ ioc_info(ioc,
+ "sense pool(0x%p) - dma(0x%llx): depth(%d), element_size(%d), pool_size (%d kB)\n",
+ ioc->sense, (unsigned long long)ioc->sense_dma,
+ ioc->scsiio_depth, SCSI_SENSE_BUFFERSIZE, sz/1024);
+ return 0;
+}
+
+/**
+ * _base_allocate_reply_pool - Allocating DMA'able memory
+ * for reply pool.
+ * @ioc: Adapter object
+ * @sz: DMA Pool size
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_allocate_reply_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz)
+{
+ /* reply pool, 4 byte align */
+ ioc->reply_dma_pool = dma_pool_create("reply pool",
+ &ioc->pdev->dev, sz, 4, 0);
+ if (!ioc->reply_dma_pool)
+ return -ENOMEM;
+ ioc->reply = dma_pool_alloc(ioc->reply_dma_pool, GFP_KERNEL,
+ &ioc->reply_dma);
+ if (!ioc->reply)
+ return -EAGAIN;
+ if (!mpt3sas_check_same_4gb_region(ioc->reply_dma, sz)) {
+ dinitprintk(ioc, pr_err(
+ "Bad Reply Pool! Reply (0x%p) Reply dma = (0x%llx)\n",
+ ioc->reply, (unsigned long long) ioc->reply_dma));
+ ioc->use_32bit_dma = true;
+ return -EAGAIN;
+ }
+ ioc->reply_dma_min_address = (u32)(ioc->reply_dma);
+ ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz;
+ ioc_info(ioc,
+ "reply pool(0x%p) - dma(0x%llx): depth(%d), frame_size(%d), pool_size(%d kB)\n",
+ ioc->reply, (unsigned long long)ioc->reply_dma,
+ ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024);
+ return 0;
+}
+
+/**
+ * _base_allocate_reply_free_dma_pool - Allocating DMA'able memory
+ * for reply free dma pool.
+ * @ioc: Adapter object
+ * @sz: DMA Pool size
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_allocate_reply_free_dma_pool(struct MPT3SAS_ADAPTER *ioc, u32 sz)
+{
+ /* reply free queue, 16 byte align */
+ ioc->reply_free_dma_pool = dma_pool_create(
+ "reply_free pool", &ioc->pdev->dev, sz, 16, 0);
+ if (!ioc->reply_free_dma_pool)
+ return -ENOMEM;
+ ioc->reply_free = dma_pool_alloc(ioc->reply_free_dma_pool,
+ GFP_KERNEL, &ioc->reply_free_dma);
+ if (!ioc->reply_free)
+ return -EAGAIN;
+ if (!mpt3sas_check_same_4gb_region(ioc->reply_free_dma, sz)) {
+ dinitprintk(ioc,
+ pr_err("Bad Reply Free Pool! Reply Free (0x%p) Reply Free dma = (0x%llx)\n",
+ ioc->reply_free, (unsigned long long) ioc->reply_free_dma));
+ ioc->use_32bit_dma = true;
+ return -EAGAIN;
+ }
+ memset(ioc->reply_free, 0, sz);
+ dinitprintk(ioc, ioc_info(ioc,
+ "reply_free pool(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n",
+ ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024));
+ dinitprintk(ioc, ioc_info(ioc,
+ "reply_free_dma (0x%llx)\n",
+ (unsigned long long)ioc->reply_free_dma));
+ return 0;
+}
+
+/**
+ * _base_allocate_reply_post_free_array - Allocating DMA'able memory
+ * for reply post free array.
+ * @ioc: Adapter object
+ * @reply_post_free_array_sz: DMA Pool size
+ * Return: 0 for success, non-zero for failure.
+ */
+
+static int
+_base_allocate_reply_post_free_array(struct MPT3SAS_ADAPTER *ioc,
+ u32 reply_post_free_array_sz)
+{
+ ioc->reply_post_free_array_dma_pool =
+ dma_pool_create("reply_post_free_array pool",
+ &ioc->pdev->dev, reply_post_free_array_sz, 16, 0);
+ if (!ioc->reply_post_free_array_dma_pool)
+ return -ENOMEM;
+ ioc->reply_post_free_array =
+ dma_pool_alloc(ioc->reply_post_free_array_dma_pool,
+ GFP_KERNEL, &ioc->reply_post_free_array_dma);
+ if (!ioc->reply_post_free_array)
+ return -EAGAIN;
+ if (!mpt3sas_check_same_4gb_region(ioc->reply_post_free_array_dma,
+ reply_post_free_array_sz)) {
+ dinitprintk(ioc, pr_err(
+ "Bad Reply Free Pool! Reply Free (0x%p) Reply Free dma = (0x%llx)\n",
+ ioc->reply_free,
+ (unsigned long long) ioc->reply_free_dma));
+ ioc->use_32bit_dma = true;
+ return -EAGAIN;
+ }
+ return 0;
+}
+/**
+ * base_alloc_rdpq_dma_pool - Allocating DMA'able memory
+ * for reply queues.
+ * @ioc: per adapter object
+ * @sz: DMA Pool size
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+base_alloc_rdpq_dma_pool(struct MPT3SAS_ADAPTER *ioc, int sz)
+{
+ int i = 0;
+ u32 dma_alloc_count = 0;
+ int reply_post_free_sz = ioc->reply_post_queue_depth *
+ sizeof(Mpi2DefaultReplyDescriptor_t);
+ int count = ioc->rdpq_array_enable ? ioc->reply_queue_count : 1;
+
+ ioc->reply_post = kcalloc(count, sizeof(struct reply_post_struct),
+ GFP_KERNEL);
+ if (!ioc->reply_post)
+ return -ENOMEM;
+ /*
+ * For INVADER_SERIES each set of 8 reply queues(0-7, 8-15, ..) and
+ * VENTURA_SERIES each set of 16 reply queues(0-15, 16-31, ..) should
+ * be within 4GB boundary i.e reply queues in a set must have same
+ * upper 32-bits in their memory address. so here driver is allocating
+ * the DMA'able memory for reply queues according.
+ * Driver uses limitation of
+ * VENTURA_SERIES to manage INVADER_SERIES as well.
+ */
+ dma_alloc_count = DIV_ROUND_UP(count,
+ RDPQ_MAX_INDEX_IN_ONE_CHUNK);
+ ioc->reply_post_free_dma_pool =
+ dma_pool_create("reply_post_free pool",
+ &ioc->pdev->dev, sz, 16, 0);
+ if (!ioc->reply_post_free_dma_pool)
+ return -ENOMEM;
+ for (i = 0; i < count; i++) {
+ if ((i % RDPQ_MAX_INDEX_IN_ONE_CHUNK == 0) && dma_alloc_count) {
+ ioc->reply_post[i].reply_post_free =
+ dma_pool_zalloc(ioc->reply_post_free_dma_pool,
+ GFP_KERNEL,
+ &ioc->reply_post[i].reply_post_free_dma);
+ if (!ioc->reply_post[i].reply_post_free)
+ return -ENOMEM;
+ /*
+ * Each set of RDPQ pool must satisfy 4gb boundary
+ * restriction.
+ * 1) Check if allocated resources for RDPQ pool are in
+ * the same 4GB range.
+ * 2) If #1 is true, continue with 64 bit DMA.
+ * 3) If #1 is false, return 1. which means free all the
+ * resources and set DMA mask to 32 and allocate.
+ */
+ if (!mpt3sas_check_same_4gb_region(
+ ioc->reply_post[i].reply_post_free_dma, sz)) {
+ dinitprintk(ioc,
+ ioc_err(ioc, "bad Replypost free pool(0x%p)"
+ "reply_post_free_dma = (0x%llx)\n",
+ ioc->reply_post[i].reply_post_free,
+ (unsigned long long)
+ ioc->reply_post[i].reply_post_free_dma));
+ return -EAGAIN;
+ }
+ dma_alloc_count--;
+
+ } else {
+ ioc->reply_post[i].reply_post_free =
+ (Mpi2ReplyDescriptorsUnion_t *)
+ ((long)ioc->reply_post[i-1].reply_post_free
+ + reply_post_free_sz);
+ ioc->reply_post[i].reply_post_free_dma =
+ (dma_addr_t)
+ (ioc->reply_post[i-1].reply_post_free_dma +
+ reply_post_free_sz);
+ }
+ }
+ return 0;
+}
+
+/**
+ * _base_allocate_memory_pools - allocate start of day memory pools
+ * @ioc: per adapter object
+ *
+ * Return: 0 success, anything else error.
+ */
+static int
+_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct mpt3sas_facts *facts;
+ u16 max_sge_elements;
+ u16 chains_needed_per_io;
+ u32 sz, total_sz, reply_post_free_sz, reply_post_free_array_sz;
+ u32 retry_sz;
+ u32 rdpq_sz = 0, sense_sz = 0;
+ u16 max_request_credit, nvme_blocks_needed;
+ unsigned short sg_tablesize;
+ u16 sge_size;
+ int i;
+ int ret = 0, rc = 0;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+
+ retry_sz = 0;
+ facts = &ioc->facts;
+
+ /* command line tunables for max sgl entries */
+ if (max_sgl_entries != -1)
+ sg_tablesize = max_sgl_entries;
+ else {
+ if (ioc->hba_mpi_version_belonged == MPI2_VERSION)
+ sg_tablesize = MPT2SAS_SG_DEPTH;
+ else
+ sg_tablesize = MPT3SAS_SG_DEPTH;
+ }
+
+ /* max sgl entries <= MPT_KDUMP_MIN_PHYS_SEGMENTS in KDUMP mode */
+ if (reset_devices)
+ sg_tablesize = min_t(unsigned short, sg_tablesize,
+ MPT_KDUMP_MIN_PHYS_SEGMENTS);
+
+ if (ioc->is_mcpu_endpoint)
+ ioc->shost->sg_tablesize = MPT_MIN_PHYS_SEGMENTS;
+ else {
+ if (sg_tablesize < MPT_MIN_PHYS_SEGMENTS)
+ sg_tablesize = MPT_MIN_PHYS_SEGMENTS;
+ else if (sg_tablesize > MPT_MAX_PHYS_SEGMENTS) {
+ sg_tablesize = min_t(unsigned short, sg_tablesize,
+ SG_MAX_SEGMENTS);
+ ioc_warn(ioc, "sg_tablesize(%u) is bigger than kernel defined SG_CHUNK_SIZE(%u)\n",
+ sg_tablesize, MPT_MAX_PHYS_SEGMENTS);
+ }
+ ioc->shost->sg_tablesize = sg_tablesize;
+ }
+
+ ioc->internal_depth = min_t(int, (facts->HighPriorityCredit + (5)),
+ (facts->RequestCredit / 4));
+ if (ioc->internal_depth < INTERNAL_CMDS_COUNT) {
+ if (facts->RequestCredit <= (INTERNAL_CMDS_COUNT +
+ INTERNAL_SCSIIO_CMDS_COUNT)) {
+ ioc_err(ioc, "IOC doesn't have enough Request Credits, it has just %d number of credits\n",
+ facts->RequestCredit);
+ return -ENOMEM;
+ }
+ ioc->internal_depth = 10;
+ }
+
+ ioc->hi_priority_depth = ioc->internal_depth - (5);
+ /* command line tunables for max controller queue depth */
+ if (max_queue_depth != -1 && max_queue_depth != 0) {
+ max_request_credit = min_t(u16, max_queue_depth +
+ ioc->internal_depth, facts->RequestCredit);
+ if (max_request_credit > MAX_HBA_QUEUE_DEPTH)
+ max_request_credit = MAX_HBA_QUEUE_DEPTH;
+ } else if (reset_devices)
+ max_request_credit = min_t(u16, facts->RequestCredit,
+ (MPT3SAS_KDUMP_SCSI_IO_DEPTH + ioc->internal_depth));
+ else
+ max_request_credit = min_t(u16, facts->RequestCredit,
+ MAX_HBA_QUEUE_DEPTH);
+
+ /* Firmware maintains additional facts->HighPriorityCredit number of
+ * credits for HiPriprity Request messages, so hba queue depth will be
+ * sum of max_request_credit and high priority queue depth.
+ */
+ ioc->hba_queue_depth = max_request_credit + ioc->hi_priority_depth;
+
+ /* request frame size */
+ ioc->request_sz = facts->IOCRequestFrameSize * 4;
+
+ /* reply frame size */
+ ioc->reply_sz = facts->ReplyFrameSize * 4;
+
+ /* chain segment size */
+ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) {
+ if (facts->IOCMaxChainSegmentSize)
+ ioc->chain_segment_sz =
+ facts->IOCMaxChainSegmentSize *
+ MAX_CHAIN_ELEMT_SZ;
+ else
+ /* set to 128 bytes size if IOCMaxChainSegmentSize is zero */
+ ioc->chain_segment_sz = DEFAULT_NUM_FWCHAIN_ELEMTS *
+ MAX_CHAIN_ELEMT_SZ;
+ } else
+ ioc->chain_segment_sz = ioc->request_sz;
+
+ /* calculate the max scatter element size */
+ sge_size = max_t(u16, ioc->sge_size, ioc->sge_size_ieee);
+
+ retry_allocation:
+ total_sz = 0;
+ /* calculate number of sg elements left over in the 1st frame */
+ max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) -
+ sizeof(Mpi2SGEIOUnion_t)) + sge_size);
+ ioc->max_sges_in_main_message = max_sge_elements/sge_size;
+
+ /* now do the same for a chain buffer */
+ max_sge_elements = ioc->chain_segment_sz - sge_size;
+ ioc->max_sges_in_chain_message = max_sge_elements/sge_size;
+
+ /*
+ * MPT3SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE
+ */
+ chains_needed_per_io = ((ioc->shost->sg_tablesize -
+ ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message)
+ + 1;
+ if (chains_needed_per_io > facts->MaxChainDepth) {
+ chains_needed_per_io = facts->MaxChainDepth;
+ ioc->shost->sg_tablesize = min_t(u16,
+ ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message
+ * chains_needed_per_io), ioc->shost->sg_tablesize);
+ }
+ ioc->chains_needed_per_io = chains_needed_per_io;
+
+ /* reply free queue sizing - taking into account for 64 FW events */
+ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64;
+
+ /* mCPU manage single counters for simplicity */
+ if (ioc->is_mcpu_endpoint)
+ ioc->reply_post_queue_depth = ioc->reply_free_queue_depth;
+ else {
+ /* calculate reply descriptor post queue depth */
+ ioc->reply_post_queue_depth = ioc->hba_queue_depth +
+ ioc->reply_free_queue_depth + 1;
+ /* align the reply post queue on the next 16 count boundary */
+ if (ioc->reply_post_queue_depth % 16)
+ ioc->reply_post_queue_depth += 16 -
+ (ioc->reply_post_queue_depth % 16);
+ }
+
+ if (ioc->reply_post_queue_depth >
+ facts->MaxReplyDescriptorPostQueueDepth) {
+ ioc->reply_post_queue_depth =
+ facts->MaxReplyDescriptorPostQueueDepth -
+ (facts->MaxReplyDescriptorPostQueueDepth % 16);
+ ioc->hba_queue_depth =
+ ((ioc->reply_post_queue_depth - 64) / 2) - 1;
+ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64;
+ }
+
+ ioc_info(ioc,
+ "scatter gather: sge_in_main_msg(%d), sge_per_chain(%d), "
+ "sge_per_io(%d), chains_per_io(%d)\n",
+ ioc->max_sges_in_main_message,
+ ioc->max_sges_in_chain_message,
+ ioc->shost->sg_tablesize,
+ ioc->chains_needed_per_io);
+
+ /* reply post queue, 16 byte align */
+ reply_post_free_sz = ioc->reply_post_queue_depth *
+ sizeof(Mpi2DefaultReplyDescriptor_t);
+ rdpq_sz = reply_post_free_sz * RDPQ_MAX_INDEX_IN_ONE_CHUNK;
+ if ((_base_is_controller_msix_enabled(ioc) && !ioc->rdpq_array_enable)
+ || (ioc->reply_queue_count < RDPQ_MAX_INDEX_IN_ONE_CHUNK))
+ rdpq_sz = reply_post_free_sz * ioc->reply_queue_count;
+ ret = base_alloc_rdpq_dma_pool(ioc, rdpq_sz);
+ if (ret == -EAGAIN) {
+ /*
+ * Free allocated bad RDPQ memory pools.
+ * Change dma coherent mask to 32 bit and reallocate RDPQ
+ */
+ _base_release_memory_pools(ioc);
+ ioc->use_32bit_dma = true;
+ if (_base_config_dma_addressing(ioc, ioc->pdev) != 0) {
+ ioc_err(ioc,
+ "32 DMA mask failed %s\n", pci_name(ioc->pdev));
+ return -ENODEV;
+ }
+ if (base_alloc_rdpq_dma_pool(ioc, rdpq_sz))
+ return -ENOMEM;
+ } else if (ret == -ENOMEM)
+ return -ENOMEM;
+ total_sz = rdpq_sz * (!ioc->rdpq_array_enable ? 1 :
+ DIV_ROUND_UP(ioc->reply_queue_count, RDPQ_MAX_INDEX_IN_ONE_CHUNK));
+ ioc->scsiio_depth = ioc->hba_queue_depth -
+ ioc->hi_priority_depth - ioc->internal_depth;
+
+ /* set the scsi host can_queue depth
+ * with some internal commands that could be outstanding
+ */
+ ioc->shost->can_queue = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT;
+ dinitprintk(ioc,
+ ioc_info(ioc, "scsi host: can_queue depth (%d)\n",
+ ioc->shost->can_queue));
+
+ /* contiguous pool for request and chains, 16 byte align, one extra "
+ * "frame for smid=0
+ */
+ ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth;
+ sz = ((ioc->scsiio_depth + 1) * ioc->request_sz);
+
+ /* hi-priority queue */
+ sz += (ioc->hi_priority_depth * ioc->request_sz);
+
+ /* internal queue */
+ sz += (ioc->internal_depth * ioc->request_sz);
+
+ ioc->request_dma_sz = sz;
+ ioc->request = dma_alloc_coherent(&ioc->pdev->dev, sz,
+ &ioc->request_dma, GFP_KERNEL);
+ if (!ioc->request) {
+ ioc_err(ioc, "request pool: dma_alloc_coherent failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), total(%d kB)\n",
+ ioc->hba_queue_depth, ioc->chains_needed_per_io,
+ ioc->request_sz, sz / 1024);
+ if (ioc->scsiio_depth < MPT3SAS_SAS_QUEUE_DEPTH)
+ goto out;
+ retry_sz = 64;
+ ioc->hba_queue_depth -= retry_sz;
+ _base_release_memory_pools(ioc);
+ goto retry_allocation;
+ }
+
+ if (retry_sz)
+ ioc_err(ioc, "request pool: dma_alloc_coherent succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), total(%d kb)\n",
+ ioc->hba_queue_depth, ioc->chains_needed_per_io,
+ ioc->request_sz, sz / 1024);
+
+ /* hi-priority queue */
+ ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) *
+ ioc->request_sz);
+ ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) *
+ ioc->request_sz);
+
+ /* internal queue */
+ ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth *
+ ioc->request_sz);
+ ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth *
+ ioc->request_sz);
+
+ ioc_info(ioc,
+ "request pool(0x%p) - dma(0x%llx): "
+ "depth(%d), frame_size(%d), pool_size(%d kB)\n",
+ ioc->request, (unsigned long long) ioc->request_dma,
+ ioc->hba_queue_depth, ioc->request_sz,
+ (ioc->hba_queue_depth * ioc->request_sz) / 1024);
+
+ total_sz += sz;
+
+ dinitprintk(ioc,
+ ioc_info(ioc, "scsiio(0x%p): depth(%d)\n",
+ ioc->request, ioc->scsiio_depth));
+
+ ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH);
+ sz = ioc->scsiio_depth * sizeof(struct chain_lookup);
+ ioc->chain_lookup = kzalloc(sz, GFP_KERNEL);
+ if (!ioc->chain_lookup) {
+ ioc_err(ioc, "chain_lookup: __get_free_pages failed\n");
+ goto out;
+ }
+
+ sz = ioc->chains_needed_per_io * sizeof(struct chain_tracker);
+ for (i = 0; i < ioc->scsiio_depth; i++) {
+ ioc->chain_lookup[i].chains_per_smid = kzalloc(sz, GFP_KERNEL);
+ if (!ioc->chain_lookup[i].chains_per_smid) {
+ ioc_err(ioc, "chain_lookup: kzalloc failed\n");
+ goto out;
+ }
+ }
+
+ /* initialize hi-priority queue smid's */
+ ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth,
+ sizeof(struct request_tracker), GFP_KERNEL);
+ if (!ioc->hpr_lookup) {
+ ioc_err(ioc, "hpr_lookup: kcalloc failed\n");
+ goto out;
+ }
+ ioc->hi_priority_smid = ioc->scsiio_depth + 1;
+ dinitprintk(ioc,
+ ioc_info(ioc, "hi_priority(0x%p): depth(%d), start smid(%d)\n",
+ ioc->hi_priority,
+ ioc->hi_priority_depth, ioc->hi_priority_smid));
+
+ /* initialize internal queue smid's */
+ ioc->internal_lookup = kcalloc(ioc->internal_depth,
+ sizeof(struct request_tracker), GFP_KERNEL);
+ if (!ioc->internal_lookup) {
+ ioc_err(ioc, "internal_lookup: kcalloc failed\n");
+ goto out;
+ }
+ ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth;
+ dinitprintk(ioc,
+ ioc_info(ioc, "internal(0x%p): depth(%d), start smid(%d)\n",
+ ioc->internal,
+ ioc->internal_depth, ioc->internal_smid));
+
+ ioc->io_queue_num = kcalloc(ioc->scsiio_depth,
+ sizeof(u16), GFP_KERNEL);
+ if (!ioc->io_queue_num)
+ goto out;
+ /*
+ * The number of NVMe page sized blocks needed is:
+ * (((sg_tablesize * 8) - 1) / (page_size - 8)) + 1
+ * ((sg_tablesize * 8) - 1) is the max PRP's minus the first PRP entry
+ * that is placed in the main message frame. 8 is the size of each PRP
+ * entry or PRP list pointer entry. 8 is subtracted from page_size
+ * because of the PRP list pointer entry at the end of a page, so this
+ * is not counted as a PRP entry. The 1 added page is a round up.
+ *
+ * To avoid allocation failures due to the amount of memory that could
+ * be required for NVMe PRP's, only each set of NVMe blocks will be
+ * contiguous, so a new set is allocated for each possible I/O.
+ */
+
+ ioc->chains_per_prp_buffer = 0;
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES) {
+ nvme_blocks_needed =
+ (ioc->shost->sg_tablesize * NVME_PRP_SIZE) - 1;
+ nvme_blocks_needed /= (ioc->page_size - NVME_PRP_SIZE);
+ nvme_blocks_needed++;
+
+ sz = sizeof(struct pcie_sg_list) * ioc->scsiio_depth;
+ ioc->pcie_sg_lookup = kzalloc(sz, GFP_KERNEL);
+ if (!ioc->pcie_sg_lookup) {
+ ioc_info(ioc, "PCIe SGL lookup: kzalloc failed\n");
+ goto out;
+ }
+ sz = nvme_blocks_needed * ioc->page_size;
+ rc = _base_allocate_pcie_sgl_pool(ioc, sz);
+ if (rc == -ENOMEM)
+ return -ENOMEM;
+ else if (rc == -EAGAIN)
+ goto try_32bit_dma;
+ total_sz += sz * ioc->scsiio_depth;
+ }
+
+ rc = _base_allocate_chain_dma_pool(ioc, ioc->chain_segment_sz);
+ if (rc == -ENOMEM)
+ return -ENOMEM;
+ else if (rc == -EAGAIN)
+ goto try_32bit_dma;
+ total_sz += ioc->chain_segment_sz * ((ioc->chains_needed_per_io -
+ ioc->chains_per_prp_buffer) * ioc->scsiio_depth);
+ dinitprintk(ioc,
+ ioc_info(ioc, "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n",
+ ioc->chain_depth, ioc->chain_segment_sz,
+ (ioc->chain_depth * ioc->chain_segment_sz) / 1024));
+ /* sense buffers, 4 byte align */
+ sense_sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE;
+ rc = _base_allocate_sense_dma_pool(ioc, sense_sz);
+ if (rc == -ENOMEM)
+ return -ENOMEM;
+ else if (rc == -EAGAIN)
+ goto try_32bit_dma;
+ total_sz += sense_sz;
+ /* reply pool, 4 byte align */
+ sz = ioc->reply_free_queue_depth * ioc->reply_sz;
+ rc = _base_allocate_reply_pool(ioc, sz);
+ if (rc == -ENOMEM)
+ return -ENOMEM;
+ else if (rc == -EAGAIN)
+ goto try_32bit_dma;
+ total_sz += sz;
+
+ /* reply free queue, 16 byte align */
+ sz = ioc->reply_free_queue_depth * 4;
+ rc = _base_allocate_reply_free_dma_pool(ioc, sz);
+ if (rc == -ENOMEM)
+ return -ENOMEM;
+ else if (rc == -EAGAIN)
+ goto try_32bit_dma;
+ dinitprintk(ioc,
+ ioc_info(ioc, "reply_free_dma (0x%llx)\n",
+ (unsigned long long)ioc->reply_free_dma));
+ total_sz += sz;
+ if (ioc->rdpq_array_enable) {
+ reply_post_free_array_sz = ioc->reply_queue_count *
+ sizeof(Mpi2IOCInitRDPQArrayEntry);
+ rc = _base_allocate_reply_post_free_array(ioc,
+ reply_post_free_array_sz);
+ if (rc == -ENOMEM)
+ return -ENOMEM;
+ else if (rc == -EAGAIN)
+ goto try_32bit_dma;
+ }
+ ioc->config_page_sz = 512;
+ ioc->config_page = dma_alloc_coherent(&ioc->pdev->dev,
+ ioc->config_page_sz, &ioc->config_page_dma, GFP_KERNEL);
+ if (!ioc->config_page) {
+ ioc_err(ioc, "config page: dma_pool_alloc failed\n");
+ goto out;
+ }
+
+ ioc_info(ioc, "config page(0x%p) - dma(0x%llx): size(%d)\n",
+ ioc->config_page, (unsigned long long)ioc->config_page_dma,
+ ioc->config_page_sz);
+ total_sz += ioc->config_page_sz;
+
+ ioc_info(ioc, "Allocated physical memory: size(%d kB)\n",
+ total_sz / 1024);
+ ioc_info(ioc, "Current Controller Queue Depth(%d),Max Controller Queue Depth(%d)\n",
+ ioc->shost->can_queue, facts->RequestCredit);
+ ioc_info(ioc, "Scatter Gather Elements per IO(%d)\n",
+ ioc->shost->sg_tablesize);
+ return 0;
+
+try_32bit_dma:
+ _base_release_memory_pools(ioc);
+ if (ioc->use_32bit_dma && (ioc->dma_mask > 32)) {
+ /* Change dma coherent mask to 32 bit and reallocate */
+ if (_base_config_dma_addressing(ioc, ioc->pdev) != 0) {
+ pr_err("Setting 32 bit coherent DMA mask Failed %s\n",
+ pci_name(ioc->pdev));
+ return -ENODEV;
+ }
+ } else if (_base_reduce_hba_queue_depth(ioc) != 0)
+ return -ENOMEM;
+ goto retry_allocation;
+
+ out:
+ return -ENOMEM;
+}
+
+/**
+ * mpt3sas_base_get_iocstate - Get the current state of a MPT adapter.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @cooked: Request raw or cooked IOC state
+ *
+ * Return: all IOC Doorbell register bits if cooked==0, else just the
+ * Doorbell bits in MPI_IOC_STATE_MASK.
+ */
+u32
+mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked)
+{
+ u32 s, sc;
+
+ s = ioc->base_readl_ext_retry(&ioc->chip->Doorbell);
+ sc = s & MPI2_IOC_STATE_MASK;
+ return cooked ? sc : s;
+}
+
+/**
+ * _base_wait_on_iocstate - waiting on a particular ioc state
+ * @ioc: ?
+ * @ioc_state: controller state { READY, OPERATIONAL, or RESET }
+ * @timeout: timeout in second
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout)
+{
+ u32 count, cntdn;
+ u32 current_state;
+
+ count = 0;
+ cntdn = 1000 * timeout;
+ do {
+ current_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (current_state == ioc_state)
+ return 0;
+ if (count && current_state == MPI2_IOC_STATE_FAULT)
+ break;
+ if (count && current_state == MPI2_IOC_STATE_COREDUMP)
+ break;
+
+ usleep_range(1000, 1500);
+ count++;
+ } while (--cntdn);
+
+ return current_state;
+}
+
+/**
+ * _base_dump_reg_set - This function will print hexdump of register set.
+ * @ioc: per adapter object
+ *
+ * Return: nothing.
+ */
+static inline void
+_base_dump_reg_set(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned int i, sz = 256;
+ u32 __iomem *reg = (u32 __iomem *)ioc->chip;
+
+ ioc_info(ioc, "System Register set:\n");
+ for (i = 0; i < (sz / sizeof(u32)); i++)
+ pr_info("%08x: %08x\n", (i * 4), readl(&reg[i]));
+}
+
+/**
+ * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by
+ * a write to the doorbell)
+ * @ioc: per adapter object
+ * @timeout: timeout in seconds
+ *
+ * Return: 0 for success, non-zero for failure.
+ *
+ * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell.
+ */
+
+static int
+_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout)
+{
+ u32 cntdn, count;
+ u32 int_status;
+
+ count = 0;
+ cntdn = 1000 * timeout;
+ do {
+ int_status = ioc->base_readl(&ioc->chip->HostInterruptStatus);
+ if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
+ dhsprintk(ioc,
+ ioc_info(ioc, "%s: successful count(%d), timeout(%d)\n",
+ __func__, count, timeout));
+ return 0;
+ }
+
+ usleep_range(1000, 1500);
+ count++;
+ } while (--cntdn);
+
+ ioc_err(ioc, "%s: failed due to timeout count(%d), int_status(%x)!\n",
+ __func__, count, int_status);
+ return -EFAULT;
+}
+
+static int
+_base_spin_on_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout)
+{
+ u32 cntdn, count;
+ u32 int_status;
+
+ count = 0;
+ cntdn = 2000 * timeout;
+ do {
+ int_status = ioc->base_readl(&ioc->chip->HostInterruptStatus);
+ if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
+ dhsprintk(ioc,
+ ioc_info(ioc, "%s: successful count(%d), timeout(%d)\n",
+ __func__, count, timeout));
+ return 0;
+ }
+
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ ioc_err(ioc, "%s: failed due to timeout count(%d), int_status(%x)!\n",
+ __func__, count, int_status);
+ return -EFAULT;
+
+}
+
+/**
+ * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell.
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ *
+ * Return: 0 for success, non-zero for failure.
+ *
+ * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to
+ * doorbell.
+ */
+static int
+_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout)
+{
+ u32 cntdn, count;
+ u32 int_status;
+ u32 doorbell;
+
+ count = 0;
+ cntdn = 1000 * timeout;
+ do {
+ int_status = ioc->base_readl(&ioc->chip->HostInterruptStatus);
+ if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) {
+ dhsprintk(ioc,
+ ioc_info(ioc, "%s: successful count(%d), timeout(%d)\n",
+ __func__, count, timeout));
+ return 0;
+ } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
+ doorbell = ioc->base_readl_ext_retry(&ioc->chip->Doorbell);
+ if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, doorbell);
+ return -EFAULT;
+ }
+ if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ mpt3sas_print_coredump_info(ioc, doorbell);
+ return -EFAULT;
+ }
+ } else if (int_status == 0xFFFFFFFF)
+ goto out;
+
+ usleep_range(1000, 1500);
+ count++;
+ } while (--cntdn);
+
+ out:
+ ioc_err(ioc, "%s: failed due to timeout count(%d), int_status(%x)!\n",
+ __func__, count, int_status);
+ return -EFAULT;
+}
+
+/**
+ * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout)
+{
+ u32 cntdn, count;
+ u32 doorbell_reg;
+
+ count = 0;
+ cntdn = 1000 * timeout;
+ do {
+ doorbell_reg = ioc->base_readl_ext_retry(&ioc->chip->Doorbell);
+ if (!(doorbell_reg & MPI2_DOORBELL_USED)) {
+ dhsprintk(ioc,
+ ioc_info(ioc, "%s: successful count(%d), timeout(%d)\n",
+ __func__, count, timeout));
+ return 0;
+ }
+
+ usleep_range(1000, 1500);
+ count++;
+ } while (--cntdn);
+
+ ioc_err(ioc, "%s: failed due to timeout count(%d), doorbell_reg(%x)!\n",
+ __func__, count, doorbell_reg);
+ return -EFAULT;
+}
+
+/**
+ * _base_send_ioc_reset - send doorbell reset
+ * @ioc: per adapter object
+ * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET
+ * @timeout: timeout in second
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout)
+{
+ u32 ioc_state;
+ int r = 0;
+ unsigned long flags;
+
+ if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) {
+ ioc_err(ioc, "%s: unknown reset_type\n", __func__);
+ return -EFAULT;
+ }
+
+ if (!(ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY))
+ return -EFAULT;
+
+ ioc_info(ioc, "sending message unit reset !!\n");
+
+ writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT,
+ &ioc->chip->Doorbell);
+ if ((_base_wait_for_doorbell_ack(ioc, 15))) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout);
+ if (ioc_state) {
+ ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n",
+ __func__, ioc_state);
+ r = -EFAULT;
+ goto out;
+ }
+ out:
+ if (r != 0) {
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ /*
+ * Wait for IOC state CoreDump to clear only during
+ * HBA initialization & release time.
+ */
+ if ((ioc_state & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP && (ioc->is_driver_loading == 1 ||
+ ioc->fault_reset_work_q == NULL)) {
+ spin_unlock_irqrestore(
+ &ioc->ioc_reset_in_progress_lock, flags);
+ mpt3sas_print_coredump_info(ioc, ioc_state);
+ mpt3sas_base_wait_for_coredump_completion(ioc,
+ __func__);
+ spin_lock_irqsave(
+ &ioc->ioc_reset_in_progress_lock, flags);
+ }
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ }
+ ioc_info(ioc, "message unit reset: %s\n",
+ r == 0 ? "SUCCESS" : "FAILED");
+ return r;
+}
+
+/**
+ * mpt3sas_wait_for_ioc - IOC's operational state is checked here.
+ * @ioc: per adapter object
+ * @timeout: timeout in seconds
+ *
+ * Return: Waits up to timeout seconds for the IOC to
+ * become operational. Returns 0 if IOC is present
+ * and operational; otherwise returns %-EFAULT.
+ */
+
+int
+mpt3sas_wait_for_ioc(struct MPT3SAS_ADAPTER *ioc, int timeout)
+{
+ int wait_state_count = 0;
+ u32 ioc_state;
+
+ do {
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state == MPI2_IOC_STATE_OPERATIONAL)
+ break;
+
+ /*
+ * Watchdog thread will be started after IOC Initialization, so
+ * no need to wait here for IOC state to become operational
+ * when IOC Initialization is on. Instead the driver will
+ * return ETIME status, so that calling function can issue
+ * diag reset operation and retry the command.
+ */
+ if (ioc->is_driver_loading)
+ return -ETIME;
+
+ ssleep(1);
+ ioc_info(ioc, "%s: waiting for operational state(count=%d)\n",
+ __func__, ++wait_state_count);
+ } while (--timeout);
+ if (!timeout) {
+ ioc_err(ioc, "%s: failed due to ioc not operational\n", __func__);
+ return -EFAULT;
+ }
+ if (wait_state_count)
+ ioc_info(ioc, "ioc is operational\n");
+ return 0;
+}
+
+/**
+ * _base_handshake_req_reply_wait - send request thru doorbell interface
+ * @ioc: per adapter object
+ * @request_bytes: request length
+ * @request: pointer having request payload
+ * @reply_bytes: reply length
+ * @reply: pointer to reply payload
+ * @timeout: timeout in second
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes,
+ u32 *request, int reply_bytes, u16 *reply, int timeout)
+{
+ MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply;
+ int i;
+ u8 failed;
+ __le32 *mfp;
+ int ret_val;
+
+ /* make sure doorbell is not in use */
+ if ((ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) {
+ ioc_err(ioc, "doorbell is in use (line=%d)\n", __LINE__);
+ goto doorbell_diag_reset;
+ }
+
+ /* clear pending doorbell interrupts from previous state changes */
+ if (ioc->base_readl(&ioc->chip->HostInterruptStatus) &
+ MPI2_HIS_IOC2SYS_DB_STATUS)
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ /* send message to ioc */
+ writel(((MPI2_FUNCTION_HANDSHAKE<<MPI2_DOORBELL_FUNCTION_SHIFT) |
+ ((request_bytes/4)<<MPI2_DOORBELL_ADD_DWORDS_SHIFT)),
+ &ioc->chip->Doorbell);
+
+ if ((_base_spin_on_doorbell_int(ioc, 5))) {
+ ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
+ __LINE__);
+ return -EFAULT;
+ }
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ if ((_base_wait_for_doorbell_ack(ioc, 5))) {
+ ioc_err(ioc, "doorbell handshake ack failed (line=%d)\n",
+ __LINE__);
+ return -EFAULT;
+ }
+
+ /* send message 32-bits at a time */
+ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) {
+ writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell);
+ if ((_base_wait_for_doorbell_ack(ioc, 5)))
+ failed = 1;
+ }
+
+ if (failed) {
+ ioc_err(ioc, "doorbell handshake sending request failed (line=%d)\n",
+ __LINE__);
+ return -EFAULT;
+ }
+
+ /* now wait for the reply */
+ if ((_base_wait_for_doorbell_int(ioc, timeout))) {
+ ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
+ __LINE__);
+ return -EFAULT;
+ }
+
+ /* read the first two 16-bits, it gives the total length of the reply */
+ reply[0] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+ if ((_base_wait_for_doorbell_int(ioc, 5))) {
+ ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
+ __LINE__);
+ return -EFAULT;
+ }
+ reply[1] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ for (i = 2; i < default_reply->MsgLength * 2; i++) {
+ if ((_base_wait_for_doorbell_int(ioc, 5))) {
+ ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
+ __LINE__);
+ return -EFAULT;
+ }
+ if (i >= reply_bytes/2) /* overflow case */
+ ioc->base_readl_ext_retry(&ioc->chip->Doorbell);
+ else
+ reply[i] = le16_to_cpu(
+ ioc->base_readl_ext_retry(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+ }
+
+ _base_wait_for_doorbell_int(ioc, 5);
+ if (_base_wait_for_doorbell_not_used(ioc, 5) != 0) {
+ dhsprintk(ioc,
+ ioc_info(ioc, "doorbell is in use (line=%d)\n",
+ __LINE__));
+ }
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ if (ioc->logging_level & MPT_DEBUG_INIT) {
+ mfp = (__le32 *)reply;
+ pr_info("\toffset:data\n");
+ for (i = 0; i < reply_bytes/4; i++)
+ ioc_info(ioc, "\t[0x%02x]:%08x\n", i*4,
+ le32_to_cpu(mfp[i]));
+ }
+ return 0;
+
+doorbell_diag_reset:
+ ret_val = _base_diag_reset(ioc);
+ return ret_val;
+}
+
+/**
+ * mpt3sas_base_sas_iounit_control - send sas iounit control to FW
+ * @ioc: per adapter object
+ * @mpi_reply: the reply payload from FW
+ * @mpi_request: the request payload sent to FW
+ *
+ * The SAS IO Unit Control Request message allows the host to perform low-level
+ * operations, such as resets on the PHYs of the IO Unit, also allows the host
+ * to obtain the IOC assigned device handles for a device if it has other
+ * identifying information about the device, in addition allows the host to
+ * remove IOC resources associated with the device.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SasIoUnitControlReply_t *mpi_reply,
+ Mpi2SasIoUnitControlRequest_t *mpi_request)
+{
+ u16 smid;
+ u8 issue_reset = 0;
+ int rc;
+ void *request;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ mutex_lock(&ioc->base_cmds.mutex);
+
+ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: base_cmd in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (rc)
+ goto out;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->base_cmds.status = MPT3_CMD_PENDING;
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t));
+ if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
+ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET)
+ ioc->ioc_link_reset_in_progress = 1;
+ init_completion(&ioc->base_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->base_cmds.done,
+ msecs_to_jiffies(10000));
+ if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
+ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) &&
+ ioc->ioc_link_reset_in_progress)
+ ioc->ioc_link_reset_in_progress = 0;
+ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc, ioc->base_cmds.status,
+ mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)/4,
+ issue_reset);
+ goto issue_host_reset;
+ }
+ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID)
+ memcpy(mpi_reply, ioc->base_cmds.reply,
+ sizeof(Mpi2SasIoUnitControlReply_t));
+ else
+ memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t));
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&ioc->base_cmds.mutex);
+ return rc;
+}
+
+/**
+ * mpt3sas_base_scsi_enclosure_processor - sending request to sep device
+ * @ioc: per adapter object
+ * @mpi_reply: the reply payload from FW
+ * @mpi_request: the request payload sent to FW
+ *
+ * The SCSI Enclosure Processor request message causes the IOC to
+ * communicate with SES devices to control LED status signals.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request)
+{
+ u16 smid;
+ u8 issue_reset = 0;
+ int rc;
+ void *request;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ mutex_lock(&ioc->base_cmds.mutex);
+
+ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: base_cmd in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (rc)
+ goto out;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->base_cmds.status = MPT3_CMD_PENDING;
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memset(request, 0, ioc->request_sz);
+ memcpy(request, mpi_request, sizeof(Mpi2SepReply_t));
+ init_completion(&ioc->base_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->base_cmds.done,
+ msecs_to_jiffies(10000));
+ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->base_cmds.status, mpi_request,
+ sizeof(Mpi2SepRequest_t)/4, issue_reset);
+ goto issue_host_reset;
+ }
+ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID)
+ memcpy(mpi_reply, ioc->base_cmds.reply,
+ sizeof(Mpi2SepReply_t));
+ else
+ memset(mpi_reply, 0, sizeof(Mpi2SepReply_t));
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&ioc->base_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _base_get_port_facts - obtain port facts reply and save in ioc
+ * @ioc: per adapter object
+ * @port: ?
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port)
+{
+ Mpi2PortFactsRequest_t mpi_request;
+ Mpi2PortFactsReply_t mpi_reply;
+ struct mpt3sas_port_facts *pfacts;
+ int mpi_reply_sz, mpi_request_sz, r;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ mpi_reply_sz = sizeof(Mpi2PortFactsReply_t);
+ mpi_request_sz = sizeof(Mpi2PortFactsRequest_t);
+ memset(&mpi_request, 0, mpi_request_sz);
+ mpi_request.Function = MPI2_FUNCTION_PORT_FACTS;
+ mpi_request.PortNumber = port;
+ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
+ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5);
+
+ if (r != 0) {
+ ioc_err(ioc, "%s: handshake failed (r=%d)\n", __func__, r);
+ return r;
+ }
+
+ pfacts = &ioc->pfacts[port];
+ memset(pfacts, 0, sizeof(struct mpt3sas_port_facts));
+ pfacts->PortNumber = mpi_reply.PortNumber;
+ pfacts->VP_ID = mpi_reply.VP_ID;
+ pfacts->VF_ID = mpi_reply.VF_ID;
+ pfacts->MaxPostedCmdBuffers =
+ le16_to_cpu(mpi_reply.MaxPostedCmdBuffers);
+
+ return 0;
+}
+
+/**
+ * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL
+ * @ioc: per adapter object
+ * @timeout:
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout)
+{
+ u32 ioc_state;
+ int rc;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ if (ioc->pci_error_recovery) {
+ dfailprintk(ioc,
+ ioc_info(ioc, "%s: host in pci error recovery\n",
+ __func__));
+ return -EFAULT;
+ }
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ dhsprintk(ioc,
+ ioc_info(ioc, "%s: ioc_state(0x%08x)\n",
+ __func__, ioc_state));
+
+ if (((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) ||
+ (ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL)
+ return 0;
+
+ if (ioc_state & MPI2_DOORBELL_USED) {
+ dhsprintk(ioc, ioc_info(ioc, "unexpected doorbell active!\n"));
+ goto issue_diag_reset;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ goto issue_diag_reset;
+ } else if ((ioc_state & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ ioc_info(ioc,
+ "%s: Skipping the diag reset here. (ioc_state=0x%x)\n",
+ __func__, ioc_state);
+ return -EFAULT;
+ }
+
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout);
+ if (ioc_state) {
+ dfailprintk(ioc,
+ ioc_info(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n",
+ __func__, ioc_state));
+ return -EFAULT;
+ }
+
+ return 0;
+
+issue_diag_reset:
+ rc = _base_diag_reset(ioc);
+ return rc;
+}
+
+/**
+ * _base_get_ioc_facts - obtain ioc facts reply and save in ioc
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2IOCFactsRequest_t mpi_request;
+ Mpi2IOCFactsReply_t mpi_reply;
+ struct mpt3sas_facts *facts;
+ int mpi_reply_sz, mpi_request_sz, r;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ r = _base_wait_for_iocstate(ioc, 10);
+ if (r) {
+ dfailprintk(ioc,
+ ioc_info(ioc, "%s: failed getting to correct state\n",
+ __func__));
+ return r;
+ }
+ mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t);
+ mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t);
+ memset(&mpi_request, 0, mpi_request_sz);
+ mpi_request.Function = MPI2_FUNCTION_IOC_FACTS;
+ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
+ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5);
+
+ if (r != 0) {
+ ioc_err(ioc, "%s: handshake failed (r=%d)\n", __func__, r);
+ return r;
+ }
+
+ facts = &ioc->facts;
+ memset(facts, 0, sizeof(struct mpt3sas_facts));
+ facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion);
+ facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion);
+ facts->VP_ID = mpi_reply.VP_ID;
+ facts->VF_ID = mpi_reply.VF_ID;
+ facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions);
+ facts->MaxChainDepth = mpi_reply.MaxChainDepth;
+ facts->WhoInit = mpi_reply.WhoInit;
+ facts->NumberOfPorts = mpi_reply.NumberOfPorts;
+ facts->MaxMSIxVectors = mpi_reply.MaxMSIxVectors;
+ if (ioc->msix_enable && (facts->MaxMSIxVectors <=
+ MAX_COMBINED_MSIX_VECTORS(ioc->is_gen35_ioc)))
+ ioc->combined_reply_queue = 0;
+ facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit);
+ facts->MaxReplyDescriptorPostQueueDepth =
+ le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth);
+ facts->ProductID = le16_to_cpu(mpi_reply.ProductID);
+ facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities);
+ if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID))
+ ioc->ir_firmware = 1;
+ if ((facts->IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE) && (!reset_devices))
+ ioc->rdpq_array_capable = 1;
+ if ((facts->IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ)
+ && ioc->is_aero_ioc)
+ ioc->atomic_desc_capable = 1;
+ facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word);
+ facts->IOCRequestFrameSize =
+ le16_to_cpu(mpi_reply.IOCRequestFrameSize);
+ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) {
+ facts->IOCMaxChainSegmentSize =
+ le16_to_cpu(mpi_reply.IOCMaxChainSegmentSize);
+ }
+ facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators);
+ facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets);
+ ioc->shost->max_id = -1;
+ facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders);
+ facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures);
+ facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags);
+ facts->HighPriorityCredit =
+ le16_to_cpu(mpi_reply.HighPriorityCredit);
+ facts->ReplyFrameSize = mpi_reply.ReplyFrameSize;
+ facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle);
+ facts->CurrentHostPageSize = mpi_reply.CurrentHostPageSize;
+
+ /*
+ * Get the Page Size from IOC Facts. If it's 0, default to 4k.
+ */
+ ioc->page_size = 1 << facts->CurrentHostPageSize;
+ if (ioc->page_size == 1) {
+ ioc_info(ioc, "CurrentHostPageSize is 0: Setting default host page size to 4k\n");
+ ioc->page_size = 1 << MPT3SAS_HOST_PAGE_SIZE_4K;
+ }
+ dinitprintk(ioc,
+ ioc_info(ioc, "CurrentHostPageSize(%d)\n",
+ facts->CurrentHostPageSize));
+
+ dinitprintk(ioc,
+ ioc_info(ioc, "hba queue depth(%d), max chains per io(%d)\n",
+ facts->RequestCredit, facts->MaxChainDepth));
+ dinitprintk(ioc,
+ ioc_info(ioc, "request frame size(%d), reply frame size(%d)\n",
+ facts->IOCRequestFrameSize * 4,
+ facts->ReplyFrameSize * 4));
+ return 0;
+}
+
+/**
+ * _base_send_ioc_init - send ioc_init to firmware
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2IOCInitRequest_t mpi_request;
+ Mpi2IOCInitReply_t mpi_reply;
+ int i, r = 0;
+ ktime_t current_time;
+ u16 ioc_status;
+ u32 reply_post_free_array_sz = 0;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_IOC_INIT;
+ mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER;
+ mpi_request.VF_ID = 0; /* TODO */
+ mpi_request.VP_ID = 0;
+ mpi_request.MsgVersion = cpu_to_le16(ioc->hba_mpi_version_belonged);
+ mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
+ mpi_request.HostPageSize = MPT3SAS_HOST_PAGE_SIZE_4K;
+
+ if (_base_is_controller_msix_enabled(ioc))
+ mpi_request.HostMSIxVectors = ioc->reply_queue_count;
+ mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4);
+ mpi_request.ReplyDescriptorPostQueueDepth =
+ cpu_to_le16(ioc->reply_post_queue_depth);
+ mpi_request.ReplyFreeQueueDepth =
+ cpu_to_le16(ioc->reply_free_queue_depth);
+
+ mpi_request.SenseBufferAddressHigh =
+ cpu_to_le32((u64)ioc->sense_dma >> 32);
+ mpi_request.SystemReplyAddressHigh =
+ cpu_to_le32((u64)ioc->reply_dma >> 32);
+ mpi_request.SystemRequestFrameBaseAddress =
+ cpu_to_le64((u64)ioc->request_dma);
+ mpi_request.ReplyFreeQueueAddress =
+ cpu_to_le64((u64)ioc->reply_free_dma);
+
+ if (ioc->rdpq_array_enable) {
+ reply_post_free_array_sz = ioc->reply_queue_count *
+ sizeof(Mpi2IOCInitRDPQArrayEntry);
+ memset(ioc->reply_post_free_array, 0, reply_post_free_array_sz);
+ for (i = 0; i < ioc->reply_queue_count; i++)
+ ioc->reply_post_free_array[i].RDPQBaseAddress =
+ cpu_to_le64(
+ (u64)ioc->reply_post[i].reply_post_free_dma);
+ mpi_request.MsgFlags = MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE;
+ mpi_request.ReplyDescriptorPostQueueAddress =
+ cpu_to_le64((u64)ioc->reply_post_free_array_dma);
+ } else {
+ mpi_request.ReplyDescriptorPostQueueAddress =
+ cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma);
+ }
+
+ /*
+ * Set the flag to enable CoreDump state feature in IOC firmware.
+ */
+ mpi_request.ConfigurationFlags |=
+ cpu_to_le16(MPI26_IOCINIT_CFGFLAGS_COREDUMP_ENABLE);
+
+ /* This time stamp specifies number of milliseconds
+ * since epoch ~ midnight January 1, 1970.
+ */
+ current_time = ktime_get_real();
+ mpi_request.TimeStamp = cpu_to_le64(ktime_to_ms(current_time));
+
+ if (ioc->logging_level & MPT_DEBUG_INIT) {
+ __le32 *mfp;
+ int i;
+
+ mfp = (__le32 *)&mpi_request;
+ ioc_info(ioc, "\toffset:data\n");
+ for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++)
+ ioc_info(ioc, "\t[0x%02x]:%08x\n", i*4,
+ le32_to_cpu(mfp[i]));
+ }
+
+ r = _base_handshake_req_reply_wait(ioc,
+ sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request,
+ sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 30);
+
+ if (r != 0) {
+ ioc_err(ioc, "%s: handshake failed (r=%d)\n", __func__, r);
+ return r;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS ||
+ mpi_reply.IOCLogInfo) {
+ ioc_err(ioc, "%s: failed\n", __func__);
+ r = -EIO;
+ }
+
+ /* Reset TimeSync Counter*/
+ ioc->timestamp_update_count = 0;
+ return r;
+}
+
+/**
+ * mpt3sas_port_enable_done - command completion routine for port enable
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+ u16 ioc_status;
+
+ if (ioc->port_enable_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (!mpi_reply)
+ return 1;
+
+ if (mpi_reply->Function != MPI2_FUNCTION_PORT_ENABLE)
+ return 1;
+
+ ioc->port_enable_cmds.status &= ~MPT3_CMD_PENDING;
+ ioc->port_enable_cmds.status |= MPT3_CMD_COMPLETE;
+ ioc->port_enable_cmds.status |= MPT3_CMD_REPLY_VALID;
+ memcpy(ioc->port_enable_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ ioc->port_enable_failed = 1;
+
+ if (ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE_ASYNC) {
+ ioc->port_enable_cmds.status &= ~MPT3_CMD_COMPLETE_ASYNC;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ mpt3sas_port_enable_complete(ioc);
+ return 1;
+ } else {
+ ioc->start_scan_failed = ioc_status;
+ ioc->start_scan = 0;
+ return 1;
+ }
+ }
+ complete(&ioc->port_enable_cmds.done);
+ return 1;
+}
+
+/**
+ * _base_send_port_enable - send port_enable(discovery stuff) to firmware
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2PortEnableRequest_t *mpi_request;
+ Mpi2PortEnableReply_t *mpi_reply;
+ int r = 0;
+ u16 smid;
+ u16 ioc_status;
+
+ ioc_info(ioc, "sending port enable !!\n");
+
+ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) {
+ ioc_err(ioc, "%s: internal command already in use\n", __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ return -EAGAIN;
+ }
+
+ ioc->port_enable_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->port_enable_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
+
+ init_completion(&ioc->port_enable_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->port_enable_cmds.done, 300*HZ);
+ if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2PortEnableRequest_t)/4);
+ if (ioc->port_enable_cmds.status & MPT3_CMD_RESET)
+ r = -EFAULT;
+ else
+ r = -ETIME;
+ goto out;
+ }
+
+ mpi_reply = ioc->port_enable_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "%s: failed with (ioc_status=0x%08x)\n",
+ __func__, ioc_status);
+ r = -EFAULT;
+ goto out;
+ }
+
+ out:
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+ ioc_info(ioc, "port enable: %s\n", r == 0 ? "SUCCESS" : "FAILED");
+ return r;
+}
+
+/**
+ * mpt3sas_port_enable - initiate firmware discovery (don't wait for reply)
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_port_enable(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2PortEnableRequest_t *mpi_request;
+ u16 smid;
+
+ ioc_info(ioc, "sending port enable !!\n");
+
+ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) {
+ ioc_err(ioc, "%s: internal command already in use\n", __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ return -EAGAIN;
+ }
+ ioc->drv_internal_flags |= MPT_DRV_INTERNAL_FIRST_PE_ISSUED;
+ ioc->port_enable_cmds.status = MPT3_CMD_PENDING;
+ ioc->port_enable_cmds.status |= MPT3_CMD_COMPLETE_ASYNC;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->port_enable_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
+
+ ioc->put_smid_default(ioc, smid);
+ return 0;
+}
+
+/**
+ * _base_determine_wait_on_discovery - desposition
+ * @ioc: per adapter object
+ *
+ * Decide whether to wait on discovery to complete. Used to either
+ * locate boot device, or report volumes ahead of physical devices.
+ *
+ * Return: 1 for wait, 0 for don't wait.
+ */
+static int
+_base_determine_wait_on_discovery(struct MPT3SAS_ADAPTER *ioc)
+{
+ /* We wait for discovery to complete if IR firmware is loaded.
+ * The sas topology events arrive before PD events, so we need time to
+ * turn on the bit in ioc->pd_handles to indicate PD
+ * Also, it maybe required to report Volumes ahead of physical
+ * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set.
+ */
+ if (ioc->ir_firmware)
+ return 1;
+
+ /* if no Bios, then we don't need to wait */
+ if (!ioc->bios_pg3.BiosVersion)
+ return 0;
+
+ /* Bios is present, then we drop down here.
+ *
+ * If there any entries in the Bios Page 2, then we wait
+ * for discovery to complete.
+ */
+
+ /* Current Boot Device */
+ if ((ioc->bios_pg2.CurrentBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK) ==
+ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+ /* Request Boot Device */
+ (ioc->bios_pg2.ReqBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK) ==
+ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+ /* Alternate Request Boot Device */
+ (ioc->bios_pg2.ReqAltBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK) ==
+ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * _base_unmask_events - turn on notification for this event
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * The mask is stored in ioc->event_masks.
+ */
+static void
+_base_unmask_events(struct MPT3SAS_ADAPTER *ioc, u16 event)
+{
+ u32 desired_event;
+
+ if (event >= 128)
+ return;
+
+ desired_event = (1 << (event % 32));
+
+ if (event < 32)
+ ioc->event_masks[0] &= ~desired_event;
+ else if (event < 64)
+ ioc->event_masks[1] &= ~desired_event;
+ else if (event < 96)
+ ioc->event_masks[2] &= ~desired_event;
+ else if (event < 128)
+ ioc->event_masks[3] &= ~desired_event;
+}
+
+/**
+ * _base_event_notification - send event notification
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_event_notification(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2EventNotificationRequest_t *mpi_request;
+ u16 smid;
+ int r = 0;
+ int i, issue_diag_reset = 0;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ if (ioc->base_cmds.status & MPT3_CMD_PENDING) {
+ ioc_err(ioc, "%s: internal command already in use\n", __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ return -EAGAIN;
+ }
+ ioc->base_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ mpi_request->EventMasks[i] =
+ cpu_to_le32(ioc->event_masks[i]);
+ init_completion(&ioc->base_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ);
+ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2EventNotificationRequest_t)/4);
+ if (ioc->base_cmds.status & MPT3_CMD_RESET)
+ r = -EFAULT;
+ else
+ issue_diag_reset = 1;
+
+ } else
+ dinitprintk(ioc, ioc_info(ioc, "%s: complete\n", __func__));
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+
+ if (issue_diag_reset) {
+ if (ioc->drv_internal_flags & MPT_DRV_INTERNAL_FIRST_PE_ISSUED)
+ return -EFAULT;
+ if (mpt3sas_base_check_for_fault_and_issue_reset(ioc))
+ return -EFAULT;
+ r = -EAGAIN;
+ }
+ return r;
+}
+
+/**
+ * mpt3sas_base_validate_event_type - validating event types
+ * @ioc: per adapter object
+ * @event_type: firmware event
+ *
+ * This will turn on firmware event notification when application
+ * ask for that event. We don't mask events that are already enabled.
+ */
+void
+mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type)
+{
+ int i, j;
+ u32 event_mask, desired_event;
+ u8 send_update_to_fw;
+
+ for (i = 0, send_update_to_fw = 0; i <
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) {
+ event_mask = ~event_type[i];
+ desired_event = 1;
+ for (j = 0; j < 32; j++) {
+ if (!(event_mask & desired_event) &&
+ (ioc->event_masks[i] & desired_event)) {
+ ioc->event_masks[i] &= ~desired_event;
+ send_update_to_fw = 1;
+ }
+ desired_event = (desired_event << 1);
+ }
+ }
+
+ if (!send_update_to_fw)
+ return;
+
+ mutex_lock(&ioc->base_cmds.mutex);
+ _base_event_notification(ioc);
+ mutex_unlock(&ioc->base_cmds.mutex);
+}
+
+/**
+* mpt3sas_base_unlock_and_get_host_diagnostic- enable Host Diagnostic Register writes
+* @ioc: per adapter object
+* @host_diagnostic: host diagnostic register content
+*
+* Return: 0 for success, non-zero for failure.
+*/
+
+int
+mpt3sas_base_unlock_and_get_host_diagnostic(struct MPT3SAS_ADAPTER *ioc,
+ u32 *host_diagnostic)
+{
+
+ u32 count;
+ *host_diagnostic = 0;
+ count = 0;
+
+ do {
+ /* Write magic sequence to WriteSequence register
+ * Loop until in diagnostic mode
+ */
+ drsprintk(ioc, ioc_info(ioc, "write magic sequence\n"));
+ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence);
+
+ /* wait 100 msec */
+ msleep(100);
+
+ if (count++ > 20) {
+ ioc_info(ioc,
+ "Stop writing magic sequence after 20 retries\n");
+ _base_dump_reg_set(ioc);
+ return -EFAULT;
+ }
+
+ *host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic);
+ drsprintk(ioc,
+ ioc_info(ioc, "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n",
+ count, *host_diagnostic));
+
+ } while ((*host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0);
+ return 0;
+}
+
+/**
+ * mpt3sas_base_lock_host_diagnostic: Disable Host Diagnostic Register writes
+ * @ioc: per adapter object
+ */
+
+void
+mpt3sas_base_lock_host_diagnostic(struct MPT3SAS_ADAPTER *ioc)
+{
+ drsprintk(ioc, ioc_info(ioc, "disable writes to the diagnostic register\n"));
+ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
+}
+
+/**
+ * _base_diag_reset - the "big hammer" start of day reset
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 host_diagnostic;
+ u32 ioc_state;
+ u32 count;
+ u32 hcb_size;
+
+ ioc_info(ioc, "sending diag reset !!\n");
+
+ pci_cfg_access_lock(ioc->pdev);
+
+ drsprintk(ioc, ioc_info(ioc, "clear interrupts\n"));
+
+ mutex_lock(&ioc->hostdiag_unlock_mutex);
+ if (mpt3sas_base_unlock_and_get_host_diagnostic(ioc, &host_diagnostic))
+ goto unlock;
+
+ hcb_size = ioc->base_readl(&ioc->chip->HCBSize);
+ drsprintk(ioc, ioc_info(ioc, "diag reset: issued\n"));
+ writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER,
+ &ioc->chip->HostDiagnostic);
+
+ /* This delay allows the chip PCIe hardware time to finish reset tasks */
+ msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000);
+
+ /* Approximately 300 second max wait */
+ for (count = 0; count < (300000000 /
+ MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) {
+
+ host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic);
+
+ if (host_diagnostic == 0xFFFFFFFF) {
+ ioc_info(ioc,
+ "Invalid host diagnostic register value\n");
+ _base_dump_reg_set(ioc);
+ goto unlock;
+ }
+ if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER))
+ break;
+
+ /* Wait to pass the second read delay window */
+ msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC/1000);
+ }
+
+ if (host_diagnostic & MPI2_DIAG_HCB_MODE) {
+
+ drsprintk(ioc,
+ ioc_info(ioc, "restart the adapter assuming the\n"
+ "HCB Address points to good F/W\n"));
+ host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK;
+ host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW;
+ writel(host_diagnostic, &ioc->chip->HostDiagnostic);
+
+ drsprintk(ioc, ioc_info(ioc, "re-enable the HCDW\n"));
+ writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE,
+ &ioc->chip->HCBSize);
+ }
+
+ drsprintk(ioc, ioc_info(ioc, "restart the adapter\n"));
+ writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET,
+ &ioc->chip->HostDiagnostic);
+
+ mpt3sas_base_lock_host_diagnostic(ioc);
+ mutex_unlock(&ioc->hostdiag_unlock_mutex);
+
+ drsprintk(ioc, ioc_info(ioc, "Wait for FW to go to the READY state\n"));
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20);
+ if (ioc_state) {
+ ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n",
+ __func__, ioc_state);
+ _base_dump_reg_set(ioc);
+ goto fail;
+ }
+
+ pci_cfg_access_unlock(ioc->pdev);
+ ioc_info(ioc, "diag reset: SUCCESS\n");
+ return 0;
+
+unlock:
+ mutex_unlock(&ioc->hostdiag_unlock_mutex);
+
+fail:
+ pci_cfg_access_unlock(ioc->pdev);
+ ioc_err(ioc, "diag reset: FAILED\n");
+ return -EFAULT;
+}
+
+/**
+ * mpt3sas_base_make_ioc_ready - put controller in READY state
+ * @ioc: per adapter object
+ * @type: FORCE_BIG_HAMMER or SOFT_RESET
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type)
+{
+ u32 ioc_state;
+ int rc;
+ int count;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ if (ioc->pci_error_recovery)
+ return 0;
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ dhsprintk(ioc,
+ ioc_info(ioc, "%s: ioc_state(0x%08x)\n",
+ __func__, ioc_state));
+
+ /* if in RESET state, it should move to READY state shortly */
+ count = 0;
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_RESET) {
+ while ((ioc_state & MPI2_IOC_STATE_MASK) !=
+ MPI2_IOC_STATE_READY) {
+ if (count++ == 10) {
+ ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n",
+ __func__, ioc_state);
+ return -EFAULT;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ }
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY)
+ return 0;
+
+ if (ioc_state & MPI2_DOORBELL_USED) {
+ ioc_info(ioc, "unexpected doorbell active!\n");
+ goto issue_diag_reset;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ goto issue_diag_reset;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_COREDUMP) {
+ /*
+ * if host reset is invoked while watch dog thread is waiting
+ * for IOC state to be changed to Fault state then driver has
+ * to wait here for CoreDump state to clear otherwise reset
+ * will be issued to the FW and FW move the IOC state to
+ * reset state without copying the FW logs to coredump region.
+ */
+ if (ioc->ioc_coredump_loop != MPT3SAS_COREDUMP_LOOP_DONE) {
+ mpt3sas_print_coredump_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ mpt3sas_base_wait_for_coredump_completion(ioc,
+ __func__);
+ }
+ goto issue_diag_reset;
+ }
+
+ if (type == FORCE_BIG_HAMMER)
+ goto issue_diag_reset;
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL)
+ if (!(_base_send_ioc_reset(ioc,
+ MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15))) {
+ return 0;
+ }
+
+ issue_diag_reset:
+ rc = _base_diag_reset(ioc);
+ return rc;
+}
+
+/**
+ * _base_make_ioc_operational - put controller in OPERATIONAL state
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc)
+{
+ int r, i, index, rc;
+ unsigned long flags;
+ u32 reply_address;
+ u16 smid;
+ struct _tr_list *delayed_tr, *delayed_tr_next;
+ struct _sc_list *delayed_sc, *delayed_sc_next;
+ struct _event_ack_list *delayed_event_ack, *delayed_event_ack_next;
+ u8 hide_flag;
+ struct adapter_reply_queue *reply_q;
+ Mpi2ReplyDescriptorsUnion_t *reply_post_free_contig;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ /* clean the delayed target reset list */
+ list_for_each_entry_safe(delayed_tr, delayed_tr_next,
+ &ioc->delayed_tr_list, list) {
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ }
+
+
+ list_for_each_entry_safe(delayed_tr, delayed_tr_next,
+ &ioc->delayed_tr_volume_list, list) {
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ }
+
+ list_for_each_entry_safe(delayed_sc, delayed_sc_next,
+ &ioc->delayed_sc_list, list) {
+ list_del(&delayed_sc->list);
+ kfree(delayed_sc);
+ }
+
+ list_for_each_entry_safe(delayed_event_ack, delayed_event_ack_next,
+ &ioc->delayed_event_ack_list, list) {
+ list_del(&delayed_event_ack->list);
+ kfree(delayed_event_ack);
+ }
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+
+ /* hi-priority queue */
+ INIT_LIST_HEAD(&ioc->hpr_free_list);
+ smid = ioc->hi_priority_smid;
+ for (i = 0; i < ioc->hi_priority_depth; i++, smid++) {
+ ioc->hpr_lookup[i].cb_idx = 0xFF;
+ ioc->hpr_lookup[i].smid = smid;
+ list_add_tail(&ioc->hpr_lookup[i].tracker_list,
+ &ioc->hpr_free_list);
+ }
+
+ /* internal queue */
+ INIT_LIST_HEAD(&ioc->internal_free_list);
+ smid = ioc->internal_smid;
+ for (i = 0; i < ioc->internal_depth; i++, smid++) {
+ ioc->internal_lookup[i].cb_idx = 0xFF;
+ ioc->internal_lookup[i].smid = smid;
+ list_add_tail(&ioc->internal_lookup[i].tracker_list,
+ &ioc->internal_free_list);
+ }
+
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ /* initialize Reply Free Queue */
+ for (i = 0, reply_address = (u32)ioc->reply_dma ;
+ i < ioc->reply_free_queue_depth ; i++, reply_address +=
+ ioc->reply_sz) {
+ ioc->reply_free[i] = cpu_to_le32(reply_address);
+ if (ioc->is_mcpu_endpoint)
+ _base_clone_reply_to_sys_mem(ioc,
+ reply_address, i);
+ }
+
+ /* initialize reply queues */
+ if (ioc->is_driver_loading)
+ _base_assign_reply_queues(ioc);
+
+ /* initialize Reply Post Free Queue */
+ index = 0;
+ reply_post_free_contig = ioc->reply_post[0].reply_post_free;
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ /*
+ * If RDPQ is enabled, switch to the next allocation.
+ * Otherwise advance within the contiguous region.
+ */
+ if (ioc->rdpq_array_enable) {
+ reply_q->reply_post_free =
+ ioc->reply_post[index++].reply_post_free;
+ } else {
+ reply_q->reply_post_free = reply_post_free_contig;
+ reply_post_free_contig += ioc->reply_post_queue_depth;
+ }
+
+ reply_q->reply_post_host_index = 0;
+ for (i = 0; i < ioc->reply_post_queue_depth; i++)
+ reply_q->reply_post_free[i].Words =
+ cpu_to_le64(ULLONG_MAX);
+ if (!_base_is_controller_msix_enabled(ioc))
+ goto skip_init_reply_post_free_queue;
+ }
+ skip_init_reply_post_free_queue:
+
+ r = _base_send_ioc_init(ioc);
+ if (r) {
+ /*
+ * No need to check IOC state for fault state & issue
+ * diag reset during host reset. This check is need
+ * only during driver load time.
+ */
+ if (!ioc->is_driver_loading)
+ return r;
+
+ rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc);
+ if (rc || (_base_send_ioc_init(ioc)))
+ return r;
+ }
+
+ /* initialize reply free host index */
+ ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1;
+ writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex);
+
+ /* initialize reply post host index */
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ if (ioc->combined_reply_queue)
+ writel((reply_q->msix_index & 7)<<
+ MPI2_RPHI_MSIX_INDEX_SHIFT,
+ ioc->replyPostRegisterIndex[reply_q->msix_index/8]);
+ else
+ writel(reply_q->msix_index <<
+ MPI2_RPHI_MSIX_INDEX_SHIFT,
+ &ioc->chip->ReplyPostHostIndex);
+
+ if (!_base_is_controller_msix_enabled(ioc))
+ goto skip_init_reply_post_host_index;
+ }
+
+ skip_init_reply_post_host_index:
+
+ mpt3sas_base_unmask_interrupts(ioc);
+
+ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) {
+ r = _base_display_fwpkg_version(ioc);
+ if (r)
+ return r;
+ }
+
+ r = _base_static_config_pages(ioc);
+ if (r)
+ return r;
+
+ r = _base_event_notification(ioc);
+ if (r)
+ return r;
+
+ if (!ioc->shost_recovery) {
+
+ if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier
+ == 0x80) {
+ hide_flag = (u8) (
+ le32_to_cpu(ioc->manu_pg10.OEMSpecificFlags0) &
+ MFG_PAGE10_HIDE_SSDS_MASK);
+ if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK)
+ ioc->mfg_pg10_hide_flag = hide_flag;
+ }
+
+ ioc->wait_for_discovery_to_complete =
+ _base_determine_wait_on_discovery(ioc);
+
+ return r; /* scan_start and scan_finished support */
+ }
+
+ r = _base_send_port_enable(ioc);
+ if (r)
+ return r;
+
+ return r;
+}
+
+/**
+ * mpt3sas_base_free_resources - free resources controller resources
+ * @ioc: per adapter object
+ */
+void
+mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc)
+{
+ dexitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ /* synchronizing freeing resource with pci_access_mutex lock */
+ mutex_lock(&ioc->pci_access_mutex);
+ if (ioc->chip_phys && ioc->chip) {
+ mpt3sas_base_mask_interrupts(ioc);
+ ioc->shost_recovery = 1;
+ mpt3sas_base_make_ioc_ready(ioc, SOFT_RESET);
+ ioc->shost_recovery = 0;
+ }
+
+ mpt3sas_base_unmap_resources(ioc);
+ mutex_unlock(&ioc->pci_access_mutex);
+ return;
+}
+
+/**
+ * mpt3sas_base_attach - attach controller instance
+ * @ioc: per adapter object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
+{
+ int r, i, rc;
+ int cpu_id, last_cpu_id = 0;
+
+ dinitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ /* setup cpu_msix_table */
+ ioc->cpu_count = num_online_cpus();
+ for_each_online_cpu(cpu_id)
+ last_cpu_id = cpu_id;
+ ioc->cpu_msix_table_sz = last_cpu_id + 1;
+ ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL);
+ ioc->reply_queue_count = 1;
+ if (!ioc->cpu_msix_table) {
+ ioc_info(ioc, "Allocation for cpu_msix_table failed!!!\n");
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ if (ioc->is_warpdrive) {
+ ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz,
+ sizeof(resource_size_t *), GFP_KERNEL);
+ if (!ioc->reply_post_host_index) {
+ ioc_info(ioc, "Allocation for reply_post_host_index failed!!!\n");
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+ }
+
+ ioc->smp_affinity_enable = smp_affinity_enable;
+
+ ioc->rdpq_array_enable_assigned = 0;
+ ioc->use_32bit_dma = false;
+ ioc->dma_mask = 64;
+ if (ioc->is_aero_ioc) {
+ ioc->base_readl = &_base_readl_aero;
+ ioc->base_readl_ext_retry = &_base_readl_ext_retry;
+ } else {
+ ioc->base_readl = &_base_readl;
+ ioc->base_readl_ext_retry = &_base_readl;
+ }
+ r = mpt3sas_base_map_resources(ioc);
+ if (r)
+ goto out_free_resources;
+
+ pci_set_drvdata(ioc->pdev, ioc->shost);
+ r = _base_get_ioc_facts(ioc);
+ if (r) {
+ rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc);
+ if (rc || (_base_get_ioc_facts(ioc)))
+ goto out_free_resources;
+ }
+
+ switch (ioc->hba_mpi_version_belonged) {
+ case MPI2_VERSION:
+ ioc->build_sg_scmd = &_base_build_sg_scmd;
+ ioc->build_sg = &_base_build_sg;
+ ioc->build_zero_len_sge = &_base_build_zero_len_sge;
+ ioc->get_msix_index_for_smlio = &_base_get_msix_index;
+ break;
+ case MPI25_VERSION:
+ case MPI26_VERSION:
+ /*
+ * In SAS3.0,
+ * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and
+ * Target Status - all require the IEEE formatted scatter gather
+ * elements.
+ */
+ ioc->build_sg_scmd = &_base_build_sg_scmd_ieee;
+ ioc->build_sg = &_base_build_sg_ieee;
+ ioc->build_nvme_prp = &_base_build_nvme_prp;
+ ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee;
+ ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t);
+ if (ioc->high_iops_queues)
+ ioc->get_msix_index_for_smlio =
+ &_base_get_high_iops_msix_index;
+ else
+ ioc->get_msix_index_for_smlio = &_base_get_msix_index;
+ break;
+ }
+ if (ioc->atomic_desc_capable) {
+ ioc->put_smid_default = &_base_put_smid_default_atomic;
+ ioc->put_smid_scsi_io = &_base_put_smid_scsi_io_atomic;
+ ioc->put_smid_fast_path =
+ &_base_put_smid_fast_path_atomic;
+ ioc->put_smid_hi_priority =
+ &_base_put_smid_hi_priority_atomic;
+ } else {
+ ioc->put_smid_default = &_base_put_smid_default;
+ ioc->put_smid_fast_path = &_base_put_smid_fast_path;
+ ioc->put_smid_hi_priority = &_base_put_smid_hi_priority;
+ if (ioc->is_mcpu_endpoint)
+ ioc->put_smid_scsi_io =
+ &_base_put_smid_mpi_ep_scsi_io;
+ else
+ ioc->put_smid_scsi_io = &_base_put_smid_scsi_io;
+ }
+ /*
+ * These function pointers for other requests that don't
+ * the require IEEE scatter gather elements.
+ *
+ * For example Configuration Pages and SAS IOUNIT Control don't.
+ */
+ ioc->build_sg_mpi = &_base_build_sg;
+ ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge;
+
+ r = mpt3sas_base_make_ioc_ready(ioc, SOFT_RESET);
+ if (r)
+ goto out_free_resources;
+
+ ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts,
+ sizeof(struct mpt3sas_port_facts), GFP_KERNEL);
+ if (!ioc->pfacts) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) {
+ r = _base_get_port_facts(ioc, i);
+ if (r) {
+ rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc);
+ if (rc || (_base_get_port_facts(ioc, i)))
+ goto out_free_resources;
+ }
+ }
+
+ r = _base_allocate_memory_pools(ioc);
+ if (r)
+ goto out_free_resources;
+
+ if (irqpoll_weight > 0)
+ ioc->thresh_hold = irqpoll_weight;
+ else
+ ioc->thresh_hold = ioc->hba_queue_depth/4;
+
+ _base_init_irqpolls(ioc);
+ init_waitqueue_head(&ioc->reset_wq);
+
+ /* allocate memory pd handle bitmask list */
+ ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8);
+ if (ioc->facts.MaxDevHandle % 8)
+ ioc->pd_handles_sz++;
+ /*
+ * pd_handles_sz should have, at least, the minimal room for
+ * set_bit()/test_bit(), otherwise out-of-memory touch may occur.
+ */
+ ioc->pd_handles_sz = ALIGN(ioc->pd_handles_sz, sizeof(unsigned long));
+
+ ioc->pd_handles = kzalloc(ioc->pd_handles_sz,
+ GFP_KERNEL);
+ if (!ioc->pd_handles) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+ ioc->blocking_handles = kzalloc(ioc->pd_handles_sz,
+ GFP_KERNEL);
+ if (!ioc->blocking_handles) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ /* allocate memory for pending OS device add list */
+ ioc->pend_os_device_add_sz = (ioc->facts.MaxDevHandle / 8);
+ if (ioc->facts.MaxDevHandle % 8)
+ ioc->pend_os_device_add_sz++;
+
+ /*
+ * pend_os_device_add_sz should have, at least, the minimal room for
+ * set_bit()/test_bit(), otherwise out-of-memory may occur.
+ */
+ ioc->pend_os_device_add_sz = ALIGN(ioc->pend_os_device_add_sz,
+ sizeof(unsigned long));
+ ioc->pend_os_device_add = kzalloc(ioc->pend_os_device_add_sz,
+ GFP_KERNEL);
+ if (!ioc->pend_os_device_add) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ ioc->device_remove_in_progress_sz = ioc->pend_os_device_add_sz;
+ ioc->device_remove_in_progress =
+ kzalloc(ioc->device_remove_in_progress_sz, GFP_KERNEL);
+ if (!ioc->device_remove_in_progress) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ ioc->fwfault_debug = mpt3sas_fwfault_debug;
+
+ /* base internal command bits */
+ mutex_init(&ioc->base_cmds.mutex);
+ ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+
+ /* port_enable command bits */
+ ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+
+ /* transport internal command bits */
+ ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->transport_cmds.mutex);
+
+ /* scsih internal command bits */
+ ioc->scsih_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->scsih_cmds.mutex);
+
+ /* task management internal command bits */
+ ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->tm_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->tm_cmds.mutex);
+
+ /* config page internal command bits */
+ ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->config_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->config_cmds.mutex);
+
+ /* ctl module internal command bits */
+ ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->ctl_cmds.sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->ctl_cmds.mutex);
+
+ if (!ioc->base_cmds.reply || !ioc->port_enable_cmds.reply ||
+ !ioc->transport_cmds.reply || !ioc->scsih_cmds.reply ||
+ !ioc->tm_cmds.reply || !ioc->config_cmds.reply ||
+ !ioc->ctl_cmds.reply || !ioc->ctl_cmds.sense) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ ioc->event_masks[i] = -1;
+
+ /* here we enable the events we care about */
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS);
+ _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED);
+ _base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD);
+ _base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR);
+ if (ioc->hba_mpi_version_belonged == MPI26_VERSION) {
+ if (ioc->is_gen35_ioc) {
+ _base_unmask_events(ioc,
+ MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE);
+ _base_unmask_events(ioc, MPI2_EVENT_PCIE_ENUMERATION);
+ _base_unmask_events(ioc,
+ MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST);
+ }
+ }
+ r = _base_make_ioc_operational(ioc);
+ if (r == -EAGAIN) {
+ r = _base_make_ioc_operational(ioc);
+ if (r)
+ goto out_free_resources;
+ }
+
+ /*
+ * Copy current copy of IOCFacts in prev_fw_facts
+ * and it will be used during online firmware upgrade.
+ */
+ memcpy(&ioc->prev_fw_facts, &ioc->facts,
+ sizeof(struct mpt3sas_facts));
+
+ ioc->non_operational_loop = 0;
+ ioc->ioc_coredump_loop = 0;
+ ioc->got_task_abort_from_ioctl = 0;
+ return 0;
+
+ out_free_resources:
+
+ ioc->remove_host = 1;
+
+ mpt3sas_base_free_resources(ioc);
+ _base_release_memory_pools(ioc);
+ pci_set_drvdata(ioc->pdev, NULL);
+ kfree(ioc->cpu_msix_table);
+ if (ioc->is_warpdrive)
+ kfree(ioc->reply_post_host_index);
+ kfree(ioc->pd_handles);
+ kfree(ioc->blocking_handles);
+ kfree(ioc->device_remove_in_progress);
+ kfree(ioc->pend_os_device_add);
+ kfree(ioc->tm_cmds.reply);
+ kfree(ioc->transport_cmds.reply);
+ kfree(ioc->scsih_cmds.reply);
+ kfree(ioc->config_cmds.reply);
+ kfree(ioc->base_cmds.reply);
+ kfree(ioc->port_enable_cmds.reply);
+ kfree(ioc->ctl_cmds.reply);
+ kfree(ioc->ctl_cmds.sense);
+ kfree(ioc->pfacts);
+ ioc->ctl_cmds.reply = NULL;
+ ioc->base_cmds.reply = NULL;
+ ioc->tm_cmds.reply = NULL;
+ ioc->scsih_cmds.reply = NULL;
+ ioc->transport_cmds.reply = NULL;
+ ioc->config_cmds.reply = NULL;
+ ioc->pfacts = NULL;
+ return r;
+}
+
+
+/**
+ * mpt3sas_base_detach - remove controller instance
+ * @ioc: per adapter object
+ */
+void
+mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc)
+{
+ dexitprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+
+ mpt3sas_base_stop_watchdog(ioc);
+ mpt3sas_base_free_resources(ioc);
+ _base_release_memory_pools(ioc);
+ mpt3sas_free_enclosure_list(ioc);
+ pci_set_drvdata(ioc->pdev, NULL);
+ kfree(ioc->cpu_msix_table);
+ if (ioc->is_warpdrive)
+ kfree(ioc->reply_post_host_index);
+ kfree(ioc->pd_handles);
+ kfree(ioc->blocking_handles);
+ kfree(ioc->device_remove_in_progress);
+ kfree(ioc->pend_os_device_add);
+ kfree(ioc->pfacts);
+ kfree(ioc->ctl_cmds.reply);
+ kfree(ioc->ctl_cmds.sense);
+ kfree(ioc->base_cmds.reply);
+ kfree(ioc->port_enable_cmds.reply);
+ kfree(ioc->tm_cmds.reply);
+ kfree(ioc->transport_cmds.reply);
+ kfree(ioc->scsih_cmds.reply);
+ kfree(ioc->config_cmds.reply);
+}
+
+/**
+ * _base_pre_reset_handler - pre reset handler
+ * @ioc: per adapter object
+ */
+static void _base_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc)
+{
+ mpt3sas_scsih_pre_reset_handler(ioc);
+ mpt3sas_ctl_pre_reset_handler(ioc);
+ dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_PRE_RESET\n", __func__));
+}
+
+/**
+ * _base_clear_outstanding_mpt_commands - clears outstanding mpt commands
+ * @ioc: per adapter object
+ */
+static void
+_base_clear_outstanding_mpt_commands(struct MPT3SAS_ADAPTER *ioc)
+{
+ dtmprintk(ioc,
+ ioc_info(ioc, "%s: clear outstanding mpt cmds\n", __func__));
+ if (ioc->transport_cmds.status & MPT3_CMD_PENDING) {
+ ioc->transport_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->transport_cmds.smid);
+ complete(&ioc->transport_cmds.done);
+ }
+ if (ioc->base_cmds.status & MPT3_CMD_PENDING) {
+ ioc->base_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->base_cmds.smid);
+ complete(&ioc->base_cmds.done);
+ }
+ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) {
+ ioc->port_enable_failed = 1;
+ ioc->port_enable_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->port_enable_cmds.smid);
+ if (ioc->is_driver_loading) {
+ ioc->start_scan_failed =
+ MPI2_IOCSTATUS_INTERNAL_ERROR;
+ ioc->start_scan = 0;
+ } else {
+ complete(&ioc->port_enable_cmds.done);
+ }
+ }
+ if (ioc->config_cmds.status & MPT3_CMD_PENDING) {
+ ioc->config_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->config_cmds.smid);
+ ioc->config_cmds.smid = USHRT_MAX;
+ complete(&ioc->config_cmds.done);
+ }
+}
+
+/**
+ * _base_clear_outstanding_commands - clear all outstanding commands
+ * @ioc: per adapter object
+ */
+static void _base_clear_outstanding_commands(struct MPT3SAS_ADAPTER *ioc)
+{
+ mpt3sas_scsih_clear_outstanding_scsi_tm_commands(ioc);
+ mpt3sas_ctl_clear_outstanding_ioctls(ioc);
+ _base_clear_outstanding_mpt_commands(ioc);
+}
+
+/**
+ * _base_reset_done_handler - reset done handler
+ * @ioc: per adapter object
+ */
+static void _base_reset_done_handler(struct MPT3SAS_ADAPTER *ioc)
+{
+ mpt3sas_scsih_reset_done_handler(ioc);
+ mpt3sas_ctl_reset_done_handler(ioc);
+ dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_DONE_RESET\n", __func__));
+}
+
+/**
+ * mpt3sas_wait_for_commands_to_complete - reset controller
+ * @ioc: Pointer to MPT_ADAPTER structure
+ *
+ * This function is waiting 10s for all pending commands to complete
+ * prior to putting controller in reset.
+ */
+void
+mpt3sas_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 ioc_state;
+
+ ioc->pending_io_count = 0;
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL)
+ return;
+
+ /* pending command count */
+ ioc->pending_io_count = scsi_host_busy(ioc->shost);
+
+ if (!ioc->pending_io_count)
+ return;
+
+ /* wait for pending commands to complete */
+ wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 10 * HZ);
+}
+
+/**
+ * _base_check_ioc_facts_changes - Look for increase/decrease of IOCFacts
+ * attributes during online firmware upgrade and update the corresponding
+ * IOC variables accordingly.
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ */
+static int
+_base_check_ioc_facts_changes(struct MPT3SAS_ADAPTER *ioc)
+{
+ u16 pd_handles_sz;
+ void *pd_handles = NULL, *blocking_handles = NULL;
+ void *pend_os_device_add = NULL, *device_remove_in_progress = NULL;
+ struct mpt3sas_facts *old_facts = &ioc->prev_fw_facts;
+
+ if (ioc->facts.MaxDevHandle > old_facts->MaxDevHandle) {
+ pd_handles_sz = (ioc->facts.MaxDevHandle / 8);
+ if (ioc->facts.MaxDevHandle % 8)
+ pd_handles_sz++;
+
+ /*
+ * pd_handles should have, at least, the minimal room for
+ * set_bit()/test_bit(), otherwise out-of-memory touch may
+ * occur.
+ */
+ pd_handles_sz = ALIGN(pd_handles_sz, sizeof(unsigned long));
+ pd_handles = krealloc(ioc->pd_handles, pd_handles_sz,
+ GFP_KERNEL);
+ if (!pd_handles) {
+ ioc_info(ioc,
+ "Unable to allocate the memory for pd_handles of sz: %d\n",
+ pd_handles_sz);
+ return -ENOMEM;
+ }
+ memset(pd_handles + ioc->pd_handles_sz, 0,
+ (pd_handles_sz - ioc->pd_handles_sz));
+ ioc->pd_handles = pd_handles;
+
+ blocking_handles = krealloc(ioc->blocking_handles,
+ pd_handles_sz, GFP_KERNEL);
+ if (!blocking_handles) {
+ ioc_info(ioc,
+ "Unable to allocate the memory for "
+ "blocking_handles of sz: %d\n",
+ pd_handles_sz);
+ return -ENOMEM;
+ }
+ memset(blocking_handles + ioc->pd_handles_sz, 0,
+ (pd_handles_sz - ioc->pd_handles_sz));
+ ioc->blocking_handles = blocking_handles;
+ ioc->pd_handles_sz = pd_handles_sz;
+
+ pend_os_device_add = krealloc(ioc->pend_os_device_add,
+ pd_handles_sz, GFP_KERNEL);
+ if (!pend_os_device_add) {
+ ioc_info(ioc,
+ "Unable to allocate the memory for pend_os_device_add of sz: %d\n",
+ pd_handles_sz);
+ return -ENOMEM;
+ }
+ memset(pend_os_device_add + ioc->pend_os_device_add_sz, 0,
+ (pd_handles_sz - ioc->pend_os_device_add_sz));
+ ioc->pend_os_device_add = pend_os_device_add;
+ ioc->pend_os_device_add_sz = pd_handles_sz;
+
+ device_remove_in_progress = krealloc(
+ ioc->device_remove_in_progress, pd_handles_sz, GFP_KERNEL);
+ if (!device_remove_in_progress) {
+ ioc_info(ioc,
+ "Unable to allocate the memory for device_remove_in_progress of sz: %d\n",
+ pd_handles_sz);
+ return -ENOMEM;
+ }
+ memset(device_remove_in_progress +
+ ioc->device_remove_in_progress_sz, 0,
+ (pd_handles_sz - ioc->device_remove_in_progress_sz));
+ ioc->device_remove_in_progress = device_remove_in_progress;
+ ioc->device_remove_in_progress_sz = pd_handles_sz;
+ }
+
+ memcpy(&ioc->prev_fw_facts, &ioc->facts, sizeof(struct mpt3sas_facts));
+ return 0;
+}
+
+/**
+ * mpt3sas_base_hard_reset_handler - reset controller
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @type: FORCE_BIG_HAMMER or SOFT_RESET
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc,
+ enum reset_type type)
+{
+ int r;
+ unsigned long flags;
+ u32 ioc_state;
+ u8 is_fault = 0, is_trigger = 0;
+
+ dtmprintk(ioc, ioc_info(ioc, "%s: enter\n", __func__));
+
+ if (ioc->pci_error_recovery) {
+ ioc_err(ioc, "%s: pci error recovery reset\n", __func__);
+ r = 0;
+ goto out_unlocked;
+ }
+
+ if (mpt3sas_fwfault_debug)
+ mpt3sas_halt_firmware(ioc);
+
+ /* wait for an active reset in progress to complete */
+ mutex_lock(&ioc->reset_in_progress_mutex);
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->shost_recovery = 1;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) &&
+ (!(ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))) {
+ is_trigger = 1;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT ||
+ (ioc_state & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ is_fault = 1;
+ ioc->htb_rel.trigger_info_dwords[1] =
+ (ioc_state & MPI2_DOORBELL_DATA_MASK);
+ }
+ }
+ _base_pre_reset_handler(ioc);
+ mpt3sas_wait_for_commands_to_complete(ioc);
+ mpt3sas_base_mask_interrupts(ioc);
+ mpt3sas_base_pause_mq_polling(ioc);
+ r = mpt3sas_base_make_ioc_ready(ioc, type);
+ if (r)
+ goto out;
+ _base_clear_outstanding_commands(ioc);
+
+ /* If this hard reset is called while port enable is active, then
+ * there is no reason to call make_ioc_operational
+ */
+ if (ioc->is_driver_loading && ioc->port_enable_failed) {
+ ioc->remove_host = 1;
+ r = -EFAULT;
+ goto out;
+ }
+ r = _base_get_ioc_facts(ioc);
+ if (r)
+ goto out;
+
+ r = _base_check_ioc_facts_changes(ioc);
+ if (r) {
+ ioc_info(ioc,
+ "Some of the parameters got changed in this new firmware"
+ " image and it requires system reboot\n");
+ goto out;
+ }
+ if (ioc->rdpq_array_enable && !ioc->rdpq_array_capable)
+ panic("%s: Issue occurred with flashing controller firmware."
+ "Please reboot the system and ensure that the correct"
+ " firmware version is running\n", ioc->name);
+
+ r = _base_make_ioc_operational(ioc);
+ if (!r)
+ _base_reset_done_handler(ioc);
+
+ out:
+ ioc_info(ioc, "%s: %s\n", __func__, r == 0 ? "SUCCESS" : "FAILED");
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->shost_recovery = 0;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->ioc_reset_count++;
+ mutex_unlock(&ioc->reset_in_progress_mutex);
+ mpt3sas_base_resume_mq_polling(ioc);
+
+ out_unlocked:
+ if ((r == 0) && is_trigger) {
+ if (is_fault)
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_FW_FAULT);
+ else
+ mpt3sas_trigger_master(ioc,
+ MASTER_TRIGGER_ADAPTER_RESET);
+ }
+ dtmprintk(ioc, ioc_info(ioc, "%s: exit\n", __func__));
+ return r;
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
new file mode 100644
index 000000000000..e6a6f21d309b
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -0,0 +1,2064 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * for access to MPT (Message Passing Technology) firmware.
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef MPT3SAS_BASE_H_INCLUDED
+#define MPT3SAS_BASE_H_INCLUDED
+
+#include "mpi/mpi2_type.h"
+#include "mpi/mpi2.h"
+#include "mpi/mpi2_ioc.h"
+#include "mpi/mpi2_cnfg.h"
+#include "mpi/mpi2_init.h"
+#include "mpi/mpi2_raid.h"
+#include "mpi/mpi2_tool.h"
+#include "mpi/mpi2_sas.h"
+#include "mpi/mpi2_pci.h"
+#include "mpi/mpi2_image.h"
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_sas.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/irq_poll.h>
+
+#include "mpt3sas_debug.h"
+#include "mpt3sas_trigger_diag.h"
+#include "mpt3sas_trigger_pages.h"
+
+/* driver versioning info */
+#define MPT3SAS_DRIVER_NAME "mpt3sas"
+#define MPT3SAS_AUTHOR "Avago Technologies <MPT-FusionLinux.pdl@avagotech.com>"
+#define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver"
+#define MPT3SAS_DRIVER_VERSION "54.100.00.00"
+#define MPT3SAS_MAJOR_VERSION 54
+#define MPT3SAS_MINOR_VERSION 100
+#define MPT3SAS_BUILD_VERSION 00
+#define MPT3SAS_RELEASE_VERSION 00
+
+#define MPT2SAS_DRIVER_NAME "mpt2sas"
+#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
+#define MPT2SAS_DRIVER_VERSION "20.102.00.00"
+#define MPT2SAS_MAJOR_VERSION 20
+#define MPT2SAS_MINOR_VERSION 102
+#define MPT2SAS_BUILD_VERSION 0
+#define MPT2SAS_RELEASE_VERSION 00
+
+/* CoreDump: Default timeout */
+#define MPT3SAS_DEFAULT_COREDUMP_TIMEOUT_SECONDS (15) /*15 seconds*/
+#define MPT3SAS_COREDUMP_LOOP_DONE (0xFF)
+#define MPT3SAS_TIMESYNC_TIMEOUT_SECONDS (10) /* 10 seconds */
+#define MPT3SAS_TIMESYNC_UPDATE_INTERVAL (900) /* 15 minutes */
+#define MPT3SAS_TIMESYNC_UNIT_MASK (0x80) /* bit 7 */
+#define MPT3SAS_TIMESYNC_MASK (0x7F) /* 0 - 6 bits */
+#define SECONDS_PER_MIN (60)
+#define SECONDS_PER_HOUR (3600)
+#define MPT3SAS_COREDUMP_LOOP_DONE (0xFF)
+#define MPI26_SET_IOC_PARAMETER_SYNC_TIMESTAMP (0x81)
+
+/*
+ * Set MPT3SAS_SG_DEPTH value based on user input.
+ */
+#define MPT_MAX_PHYS_SEGMENTS SG_CHUNK_SIZE
+#define MPT_MIN_PHYS_SEGMENTS 16
+#define MPT_KDUMP_MIN_PHYS_SEGMENTS 32
+
+#define MCPU_MAX_CHAINS_PER_IO 3
+
+#ifdef CONFIG_SCSI_MPT3SAS_MAX_SGE
+#define MPT3SAS_SG_DEPTH CONFIG_SCSI_MPT3SAS_MAX_SGE
+#else
+#define MPT3SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS
+#endif
+
+#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE
+#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE
+#else
+#define MPT2SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS
+#endif
+
+/*
+ * Generic Defines
+ */
+#define MPT3SAS_SATA_QUEUE_DEPTH 32
+#define MPT3SAS_SAS_QUEUE_DEPTH 254
+#define MPT3SAS_RAID_QUEUE_DEPTH 128
+#define MPT3SAS_KDUMP_SCSI_IO_DEPTH 200
+
+#define MPT3SAS_RAID_MAX_SECTORS 8192
+#define MPT3SAS_HOST_PAGE_SIZE_4K 12
+#define MPT3SAS_NVME_QUEUE_DEPTH 128
+#define MPT_NAME_LENGTH 32 /* generic length of strings */
+#define MPT_STRING_LENGTH 64
+#define MPI_FRAME_START_OFFSET 256
+#define REPLY_FREE_POOL_SIZE 512 /*(32 maxcredix *4)*(4 times)*/
+
+#define MPT_MAX_CALLBACKS 32
+
+#define MPT_MAX_HBA_NUM_PHYS 32
+
+#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */
+/* reserved for issuing internally framed scsi io cmds */
+#define INTERNAL_SCSIIO_CMDS_COUNT 3
+
+#define MPI3_HIM_MASK 0xFFFFFFFF /* mask every bit*/
+
+#define MPT3SAS_INVALID_DEVICE_HANDLE 0xFFFF
+
+#define MAX_CHAIN_ELEMT_SZ 16
+#define DEFAULT_NUM_FWCHAIN_ELEMTS 8
+
+#define IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT 6
+#define FW_IMG_HDR_READ_TIMEOUT 15
+
+#define IOC_OPERATIONAL_WAIT_COUNT 10
+
+/*
+ * NVMe defines
+ */
+#define NVME_PRP_SIZE 8 /* PRP size */
+#define NVME_ERROR_RESPONSE_SIZE 16 /* Max NVME Error Response */
+#define NVME_TASK_ABORT_MIN_TIMEOUT 6
+#define NVME_TASK_ABORT_MAX_TIMEOUT 60
+#define NVME_TASK_MNGT_CUSTOM_MASK (0x0010)
+#define NVME_PRP_PAGE_SIZE 4096 /* Page size */
+
+struct mpt3sas_nvme_cmd {
+ u8 rsvd[24];
+ __le64 prp1;
+ __le64 prp2;
+};
+
+/*
+ * logging format
+ */
+#define ioc_err(ioc, fmt, ...) \
+ pr_err("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_notice(ioc, fmt, ...) \
+ pr_notice("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_warn(ioc, fmt, ...) \
+ pr_warn("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_info(ioc, fmt, ...) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+
+/*
+ * WarpDrive Specific Log codes
+ */
+
+#define MPT2_WARPDRIVE_LOGENTRY (0x8002)
+#define MPT2_WARPDRIVE_LC_SSDT (0x41)
+#define MPT2_WARPDRIVE_LC_SSDLW (0x43)
+#define MPT2_WARPDRIVE_LC_SSDLF (0x44)
+#define MPT2_WARPDRIVE_LC_BRMF (0x4D)
+
+/*
+ * per target private data
+ */
+#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01
+#define MPT_TARGET_FLAGS_VOLUME 0x02
+#define MPT_TARGET_FLAGS_DELETED 0x04
+#define MPT_TARGET_FASTPATH_IO 0x08
+#define MPT_TARGET_FLAGS_PCIE_DEVICE 0x10
+
+#define SAS2_PCI_DEVICE_B0_REVISION (0x01)
+#define SAS3_PCI_DEVICE_C0_REVISION (0x02)
+
+/* Atlas PCIe Switch Management Port */
+#define MPI26_ATLAS_PCIe_SWITCH_DEVID (0x00B2)
+
+/*
+ * Intel HBA branding
+ */
+#define MPT2SAS_INTEL_RMS25JB080_BRANDING \
+ "Intel(R) Integrated RAID Module RMS25JB080"
+#define MPT2SAS_INTEL_RMS25JB040_BRANDING \
+ "Intel(R) Integrated RAID Module RMS25JB040"
+#define MPT2SAS_INTEL_RMS25KB080_BRANDING \
+ "Intel(R) Integrated RAID Module RMS25KB080"
+#define MPT2SAS_INTEL_RMS25KB040_BRANDING \
+ "Intel(R) Integrated RAID Module RMS25KB040"
+#define MPT2SAS_INTEL_RMS25LB040_BRANDING \
+ "Intel(R) Integrated RAID Module RMS25LB040"
+#define MPT2SAS_INTEL_RMS25LB080_BRANDING \
+ "Intel(R) Integrated RAID Module RMS25LB080"
+#define MPT2SAS_INTEL_RMS2LL080_BRANDING \
+ "Intel Integrated RAID Module RMS2LL080"
+#define MPT2SAS_INTEL_RMS2LL040_BRANDING \
+ "Intel Integrated RAID Module RMS2LL040"
+#define MPT2SAS_INTEL_RS25GB008_BRANDING \
+ "Intel(R) RAID Controller RS25GB008"
+#define MPT2SAS_INTEL_SSD910_BRANDING \
+ "Intel(R) SSD 910 Series"
+
+#define MPT3SAS_INTEL_RMS3JC080_BRANDING \
+ "Intel(R) Integrated RAID Module RMS3JC080"
+#define MPT3SAS_INTEL_RS3GC008_BRANDING \
+ "Intel(R) RAID Controller RS3GC008"
+#define MPT3SAS_INTEL_RS3FC044_BRANDING \
+ "Intel(R) RAID Controller RS3FC044"
+#define MPT3SAS_INTEL_RS3UC080_BRANDING \
+ "Intel(R) RAID Controller RS3UC080"
+
+/*
+ * Intel HBA SSDIDs
+ */
+#define MPT2SAS_INTEL_RMS25JB080_SSDID 0x3516
+#define MPT2SAS_INTEL_RMS25JB040_SSDID 0x3517
+#define MPT2SAS_INTEL_RMS25KB080_SSDID 0x3518
+#define MPT2SAS_INTEL_RMS25KB040_SSDID 0x3519
+#define MPT2SAS_INTEL_RMS25LB040_SSDID 0x351A
+#define MPT2SAS_INTEL_RMS25LB080_SSDID 0x351B
+#define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E
+#define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F
+#define MPT2SAS_INTEL_RS25GB008_SSDID 0x3000
+#define MPT2SAS_INTEL_SSD910_SSDID 0x3700
+
+#define MPT3SAS_INTEL_RMS3JC080_SSDID 0x3521
+#define MPT3SAS_INTEL_RS3GC008_SSDID 0x3522
+#define MPT3SAS_INTEL_RS3FC044_SSDID 0x3523
+#define MPT3SAS_INTEL_RS3UC080_SSDID 0x3524
+
+/*
+ * Dell HBA branding
+ */
+#define MPT2SAS_DELL_BRANDING_SIZE 32
+
+#define MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING "Dell 6Gbps SAS HBA"
+#define MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING "Dell PERC H200 Adapter"
+#define MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING "Dell PERC H200 Integrated"
+#define MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING "Dell PERC H200 Modular"
+#define MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING "Dell PERC H200 Embedded"
+#define MPT2SAS_DELL_PERC_H200_BRANDING "Dell PERC H200"
+#define MPT2SAS_DELL_6GBPS_SAS_BRANDING "Dell 6Gbps SAS"
+
+#define MPT3SAS_DELL_12G_HBA_BRANDING \
+ "Dell 12Gbps HBA"
+
+/*
+ * Dell HBA SSDIDs
+ */
+#define MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID 0x1F1C
+#define MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID 0x1F1D
+#define MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID 0x1F1E
+#define MPT2SAS_DELL_PERC_H200_MODULAR_SSDID 0x1F1F
+#define MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID 0x1F20
+#define MPT2SAS_DELL_PERC_H200_SSDID 0x1F21
+#define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22
+
+#define MPT3SAS_DELL_12G_HBA_SSDID 0x1F46
+
+/*
+ * Cisco HBA branding
+ */
+#define MPT3SAS_CISCO_12G_8E_HBA_BRANDING \
+ "Cisco 9300-8E 12G SAS HBA"
+#define MPT3SAS_CISCO_12G_8I_HBA_BRANDING \
+ "Cisco 9300-8i 12G SAS HBA"
+#define MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING \
+ "Cisco 12G Modular SAS Pass through Controller"
+#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING \
+ "UCS C3X60 12G SAS Pass through Controller"
+/*
+ * Cisco HBA SSSDIDs
+ */
+#define MPT3SAS_CISCO_12G_8E_HBA_SSDID 0x14C
+#define MPT3SAS_CISCO_12G_8I_HBA_SSDID 0x154
+#define MPT3SAS_CISCO_12G_AVILA_HBA_SSDID 0x155
+#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID 0x156
+
+/*
+ * status bits for ioc->diag_buffer_status
+ */
+#define MPT3_DIAG_BUFFER_IS_REGISTERED (0x01)
+#define MPT3_DIAG_BUFFER_IS_RELEASED (0x02)
+#define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04)
+#define MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED (0x08)
+#define MPT3_DIAG_BUFFER_IS_APP_OWNED (0x10)
+
+/*
+ * HP HBA branding
+ */
+#define MPT2SAS_HP_3PAR_SSVID 0x1590
+
+#define MPT2SAS_HP_2_4_INTERNAL_BRANDING \
+ "HP H220 Host Bus Adapter"
+#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING \
+ "HP H221 Host Bus Adapter"
+#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING \
+ "HP H222 Host Bus Adapter"
+#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING \
+ "HP H220i Host Bus Adapter"
+#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING \
+ "HP H210i Host Bus Adapter"
+
+/*
+ * HO HBA SSDIDs
+ */
+#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041
+#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042
+#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043
+#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044
+#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046
+
+/*
+ * Combined Reply Queue constants,
+ * There are twelve Supplemental Reply Post Host Index Registers
+ * and each register is at offset 0x10 bytes from the previous one.
+ */
+#define MAX_COMBINED_MSIX_VECTORS(gen35) ((gen35 == 1) ? 16 : 8)
+#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G3 12
+#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G35 16
+#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET (0x10)
+#define MPT3_MIN_IRQS 1
+
+/* OEM Identifiers */
+#define MFG10_OEM_ID_INVALID (0x00000000)
+#define MFG10_OEM_ID_DELL (0x00000001)
+#define MFG10_OEM_ID_FSC (0x00000002)
+#define MFG10_OEM_ID_SUN (0x00000003)
+#define MFG10_OEM_ID_IBM (0x00000004)
+
+/* GENERIC Flags 0*/
+#define MFG10_GF0_OCE_DISABLED (0x00000001)
+#define MFG10_GF0_R1E_DRIVE_COUNT (0x00000002)
+#define MFG10_GF0_R10_DISPLAY (0x00000004)
+#define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008)
+#define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010)
+
+#define VIRTUAL_IO_FAILED_RETRY (0x32010081)
+
+/* High IOPs definitions */
+#define MPT3SAS_DEVICE_HIGH_IOPS_DEPTH 8
+#define MPT3SAS_HIGH_IOPS_REPLY_QUEUES 8
+#define MPT3SAS_HIGH_IOPS_BATCH_COUNT 16
+#define MPT3SAS_GEN35_MAX_MSIX_QUEUES 128
+#define RDPQ_MAX_INDEX_IN_ONE_CHUNK 16
+
+/* OEM Specific Flags will come from OEM specific header files */
+struct Mpi2ManufacturingPage10_t {
+ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */
+ U8 OEMIdentifier; /* 04h */
+ U8 Reserved1; /* 05h */
+ U16 Reserved2; /* 08h */
+ U32 Reserved3; /* 0Ch */
+ U32 GenericFlags0; /* 10h */
+ U32 GenericFlags1; /* 14h */
+ U32 Reserved4; /* 18h */
+ U32 OEMSpecificFlags0; /* 1Ch */
+ U32 OEMSpecificFlags1; /* 20h */
+ U32 Reserved5[18]; /* 24h - 60h*/
+};
+
+
+/* Miscellaneous options */
+struct Mpi2ManufacturingPage11_t {
+ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */
+ __le32 Reserved1; /* 04h */
+ u8 Reserved2; /* 08h */
+ u8 EEDPTagMode; /* 09h */
+ u8 Reserved3; /* 0Ah */
+ u8 Reserved4; /* 0Bh */
+ __le32 Reserved5[8]; /* 0Ch-2Ch */
+ u16 AddlFlags2; /* 2Ch */
+ u8 AddlFlags3; /* 2Eh */
+ u8 Reserved6; /* 2Fh */
+ __le32 Reserved7[7]; /* 30h - 4Bh */
+ u8 NVMeAbortTO; /* 4Ch */
+ u8 NumPerDevEvents; /* 4Dh */
+ u8 HostTraceBufferDecrementSizeKB; /* 4Eh */
+ u8 HostTraceBufferFlags; /* 4Fh */
+ u16 HostTraceBufferMaxSizeKB; /* 50h */
+ u16 HostTraceBufferMinSizeKB; /* 52h */
+ u8 CoreDumpTOSec; /* 54h */
+ u8 TimeSyncInterval; /* 55h */
+ u16 Reserved9; /* 56h */
+ __le32 Reserved10; /* 58h */
+};
+
+/**
+ * struct MPT3SAS_TARGET - starget private hostdata
+ * @starget: starget object
+ * @sas_address: target sas address
+ * @raid_device: raid_device pointer to access volume data
+ * @handle: device handle
+ * @num_luns: number luns
+ * @flags: MPT_TARGET_FLAGS_XXX flags
+ * @deleted: target flaged for deletion
+ * @tm_busy: target is busy with TM request.
+ * @port: hba port entry containing target's port number info
+ * @sas_dev: The sas_device associated with this target
+ * @pcie_dev: The pcie device associated with this target
+ */
+struct MPT3SAS_TARGET {
+ struct scsi_target *starget;
+ u64 sas_address;
+ struct _raid_device *raid_device;
+ u16 handle;
+ int num_luns;
+ u32 flags;
+ u8 deleted;
+ u8 tm_busy;
+ struct hba_port *port;
+ struct _sas_device *sas_dev;
+ struct _pcie_device *pcie_dev;
+};
+
+
+/*
+ * per device private data
+ */
+#define MPT_DEVICE_FLAGS_INIT 0x01
+
+#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003)
+#define MFG_PAGE10_HIDE_ALL_DISKS (0x00)
+#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01)
+#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02)
+
+/**
+ * struct MPT3SAS_DEVICE - sdev private hostdata
+ * @sas_target: starget private hostdata
+ * @lun: lun number
+ * @flags: MPT_DEVICE_XXX flags
+ * @configured_lun: lun is configured
+ * @block: device is in SDEV_BLOCK state
+ * @tlr_snoop_check: flag used in determining whether to disable TLR
+ * @eedp_enable: eedp support enable bit
+ * @eedp_type: 0(type_1), 1(type_2), 2(type_3)
+ * @eedp_block_length: block size
+ * @ata_command_pending: SATL passthrough outstanding for device
+ */
+struct MPT3SAS_DEVICE {
+ struct MPT3SAS_TARGET *sas_target;
+ unsigned int lun;
+ u32 flags;
+ u8 configured_lun;
+ u8 block;
+ u8 tlr_snoop_check;
+ u8 ignore_delay_remove;
+ /* Iopriority Command Handling */
+ u8 ncq_prio_enable;
+ /*
+ * Bug workaround for SATL handling: the mpt2/3sas firmware
+ * doesn't return BUSY or TASK_SET_FULL for subsequent
+ * commands while a SATL pass through is in operation as the
+ * spec requires, it simply does nothing with them until the
+ * pass through completes, causing them possibly to timeout if
+ * the passthrough is a long executing command (like format or
+ * secure erase). This variable allows us to do the right
+ * thing while a SATL command is pending.
+ */
+ unsigned long ata_command_pending;
+
+};
+
+#define MPT3_CMD_NOT_USED 0x8000 /* free */
+#define MPT3_CMD_COMPLETE 0x0001 /* completed */
+#define MPT3_CMD_PENDING 0x0002 /* pending */
+#define MPT3_CMD_REPLY_VALID 0x0004 /* reply is valid */
+#define MPT3_CMD_RESET 0x0008 /* host reset dropped the command */
+#define MPT3_CMD_COMPLETE_ASYNC 0x0010 /* tells whether cmd completes in same thread or not */
+
+/**
+ * struct _internal_cmd - internal commands struct
+ * @mutex: mutex
+ * @done: completion
+ * @reply: reply message pointer
+ * @sense: sense data
+ * @status: MPT3_CMD_XXX status
+ * @smid: system message id
+ */
+struct _internal_cmd {
+ struct mutex mutex;
+ struct completion done;
+ void *reply;
+ void *sense;
+ u16 status;
+ u16 smid;
+};
+
+
+
+/**
+ * struct _sas_device - attached device information
+ * @list: sas device list
+ * @starget: starget object
+ * @sas_address: device sas address
+ * @device_name: retrieved from the SAS IDENTIFY frame.
+ * @handle: device handle
+ * @sas_address_parent: sas address of parent expander or sas host
+ * @enclosure_handle: enclosure handle
+ * @enclosure_logical_id: enclosure logical identifier
+ * @volume_handle: volume handle (valid when hidden raid member)
+ * @volume_wwid: volume unique identifier
+ * @device_info: bitfield provides detailed info about the device
+ * @id: target id
+ * @channel: target channel
+ * @slot: number number
+ * @phy: phy identifier provided in sas device page 0
+ * @responding: used in _scsih_sas_device_mark_responding
+ * @fast_path: fast path feature enable bit
+ * @pfa_led_on: flag for PFA LED status
+ * @pend_sas_rphy_add: flag to check if device is in sas_rphy_add()
+ * addition routine.
+ * @chassis_slot: chassis slot
+ * @is_chassis_slot_valid: chassis slot valid or not
+ * @port: hba port entry containing device's port number info
+ * @rphy: device's sas_rphy address used to identify this device structure in
+ * target_alloc callback function
+ */
+struct _sas_device {
+ struct list_head list;
+ struct scsi_target *starget;
+ u64 sas_address;
+ u64 device_name;
+ u16 handle;
+ u64 sas_address_parent;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u16 volume_handle;
+ u64 volume_wwid;
+ u32 device_info;
+ int id;
+ int channel;
+ u16 slot;
+ u8 phy;
+ u8 responding;
+ u8 fast_path;
+ u8 pfa_led_on;
+ u8 pend_sas_rphy_add;
+ u8 enclosure_level;
+ u8 chassis_slot;
+ u8 is_chassis_slot_valid;
+ u8 connector_name[5];
+ struct kref refcount;
+ u8 port_type;
+ struct hba_port *port;
+ struct sas_rphy *rphy;
+};
+
+static inline void sas_device_get(struct _sas_device *s)
+{
+ kref_get(&s->refcount);
+}
+
+static inline void sas_device_free(struct kref *r)
+{
+ kfree(container_of(r, struct _sas_device, refcount));
+}
+
+static inline void sas_device_put(struct _sas_device *s)
+{
+ kref_put(&s->refcount, sas_device_free);
+}
+
+/*
+ * struct _pcie_device - attached PCIe device information
+ * @list: pcie device list
+ * @starget: starget object
+ * @wwid: device WWID
+ * @handle: device handle
+ * @device_info: bitfield provides detailed info about the device
+ * @id: target id
+ * @channel: target channel
+ * @slot: slot number
+ * @port_num: port number
+ * @responding: used in _scsih_pcie_device_mark_responding
+ * @fast_path: fast path feature enable bit
+ * @nvme_mdts: MaximumDataTransferSize from PCIe Device Page 2 for
+ * NVMe device only
+ * @enclosure_handle: enclosure handle
+ * @enclosure_logical_id: enclosure logical identifier
+ * @enclosure_level: The level of device's enclosure from the controller
+ * @connector_name: ASCII value of the Connector's name
+ * @serial_number: pointer of serial number string allocated runtime
+ * @access_status: Device's Access Status
+ * @shutdown_latency: NVMe device's RTD3 Entry Latency
+ * @refcount: reference count for deletion
+ */
+struct _pcie_device {
+ struct list_head list;
+ struct scsi_target *starget;
+ u64 wwid;
+ u16 handle;
+ u32 device_info;
+ int id;
+ int channel;
+ u16 slot;
+ u8 port_num;
+ u8 responding;
+ u8 fast_path;
+ u32 nvme_mdts;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u8 enclosure_level;
+ u8 connector_name[4];
+ u8 *serial_number;
+ u8 reset_timeout;
+ u8 access_status;
+ u16 shutdown_latency;
+ struct kref refcount;
+};
+/**
+ * pcie_device_get - Increment the pcie device reference count
+ *
+ * @p: pcie_device object
+ *
+ * When ever this function called it will increment the
+ * reference count of the pcie device for which this function called.
+ *
+ */
+static inline void pcie_device_get(struct _pcie_device *p)
+{
+ kref_get(&p->refcount);
+}
+
+/**
+ * pcie_device_free - Release the pcie device object
+ * @r - kref object
+ *
+ * Free's the pcie device object. It will be called when reference count
+ * reaches to zero.
+ */
+static inline void pcie_device_free(struct kref *r)
+{
+ kfree(container_of(r, struct _pcie_device, refcount));
+}
+
+/**
+ * pcie_device_put - Decrement the pcie device reference count
+ *
+ * @p: pcie_device object
+ *
+ * When ever this function called it will decrement the
+ * reference count of the pcie device for which this function called.
+ *
+ * When refernce count reaches to Zero, this will call pcie_device_free to the
+ * pcie_device object.
+ */
+static inline void pcie_device_put(struct _pcie_device *p)
+{
+ kref_put(&p->refcount, pcie_device_free);
+}
+/**
+ * struct _raid_device - raid volume link list
+ * @list: sas device list
+ * @starget: starget object
+ * @sdev: scsi device struct (volumes are single lun)
+ * @wwid: unique identifier for the volume
+ * @handle: device handle
+ * @block_size: Block size of the volume
+ * @id: target id
+ * @channel: target channel
+ * @volume_type: the raid level
+ * @device_info: bitfield provides detailed info about the hidden components
+ * @num_pds: number of hidden raid components
+ * @responding: used in _scsih_raid_device_mark_responding
+ * @percent_complete: resync percent complete
+ * @direct_io_enabled: Whether direct io to PDs are allowed or not
+ * @stripe_exponent: X where 2powX is the stripe sz in blocks
+ * @block_exponent: X where 2powX is the block sz in bytes
+ * @max_lba: Maximum number of LBA in the volume
+ * @stripe_sz: Stripe Size of the volume
+ * @device_info: Device info of the volume member disk
+ * @pd_handle: Array of handles of the physical drives for direct I/O in le16
+ */
+#define MPT_MAX_WARPDRIVE_PDS 8
+struct _raid_device {
+ struct list_head list;
+ struct scsi_target *starget;
+ struct scsi_device *sdev;
+ u64 wwid;
+ u16 handle;
+ u16 block_sz;
+ int id;
+ int channel;
+ u8 volume_type;
+ u8 num_pds;
+ u8 responding;
+ u8 percent_complete;
+ u8 direct_io_enabled;
+ u8 stripe_exponent;
+ u8 block_exponent;
+ u64 max_lba;
+ u32 stripe_sz;
+ u32 device_info;
+ u16 pd_handle[MPT_MAX_WARPDRIVE_PDS];
+};
+
+/**
+ * struct _boot_device - boot device info
+ *
+ * @channel: sas, raid, or pcie channel
+ * @device: holds pointer for struct _sas_device, struct _raid_device or
+ * struct _pcie_device
+ */
+struct _boot_device {
+ int channel;
+ void *device;
+};
+
+/**
+ * struct _sas_port - wide/narrow sas port information
+ * @port_list: list of ports belonging to expander
+ * @num_phys: number of phys belonging to this port
+ * @remote_identify: attached device identification
+ * @rphy: sas transport rphy object
+ * @port: sas transport wide/narrow port object
+ * @hba_port: hba port entry containing port's port number info
+ * @phy_list: _sas_phy list objects belonging to this port
+ */
+struct _sas_port {
+ struct list_head port_list;
+ u8 num_phys;
+ struct sas_identify remote_identify;
+ struct sas_rphy *rphy;
+ struct sas_port *port;
+ struct hba_port *hba_port;
+ struct list_head phy_list;
+};
+
+/**
+ * struct _sas_phy - phy information
+ * @port_siblings: list of phys belonging to a port
+ * @identify: phy identification
+ * @remote_identify: attached device identification
+ * @phy: sas transport phy object
+ * @phy_id: unique phy id
+ * @handle: device handle for this phy
+ * @attached_handle: device handle for attached device
+ * @phy_belongs_to_port: port has been created for this phy
+ * @port: hba port entry containing port number info
+ */
+struct _sas_phy {
+ struct list_head port_siblings;
+ struct sas_identify identify;
+ struct sas_identify remote_identify;
+ struct sas_phy *phy;
+ u8 phy_id;
+ u16 handle;
+ u16 attached_handle;
+ u8 phy_belongs_to_port;
+ u8 hba_vphy;
+ struct hba_port *port;
+};
+
+/**
+ * struct _sas_node - sas_host/expander information
+ * @list: list of expanders
+ * @parent_dev: parent device class
+ * @num_phys: number phys belonging to this sas_host/expander
+ * @sas_address: sas address of this sas_host/expander
+ * @handle: handle for this sas_host/expander
+ * @sas_address_parent: sas address of parent expander or sas host
+ * @enclosure_handle: handle for this a member of an enclosure
+ * @device_info: bitwise defining capabilities of this sas_host/expander
+ * @responding: used in _scsih_expander_device_mark_responding
+ * @nr_phys_allocated: Allocated memory for this many count phys
+ * @phy: a list of phys that make up this sas_host/expander
+ * @sas_port_list: list of ports attached to this sas_host/expander
+ * @port: hba port entry containing node's port number info
+ * @rphy: sas_rphy object of this expander
+ */
+struct _sas_node {
+ struct list_head list;
+ struct device *parent_dev;
+ u8 num_phys;
+ u64 sas_address;
+ u16 handle;
+ u64 sas_address_parent;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u8 responding;
+ u8 nr_phys_allocated;
+ struct hba_port *port;
+ struct _sas_phy *phy;
+ struct list_head sas_port_list;
+ struct sas_rphy *rphy;
+};
+
+/**
+ * struct _enclosure_node - enclosure information
+ * @list: list of enclosures
+ * @pg0: enclosure pg0;
+ */
+struct _enclosure_node {
+ struct list_head list;
+ Mpi2SasEnclosurePage0_t pg0;
+};
+
+/**
+ * enum reset_type - reset state
+ * @FORCE_BIG_HAMMER: issue diagnostic reset
+ * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer
+ */
+enum reset_type {
+ FORCE_BIG_HAMMER,
+ SOFT_RESET,
+};
+
+/**
+ * struct pcie_sg_list - PCIe SGL buffer (contiguous per I/O)
+ * @pcie_sgl: PCIe native SGL for NVMe devices
+ * @pcie_sgl_dma: physical address
+ */
+struct pcie_sg_list {
+ void *pcie_sgl;
+ dma_addr_t pcie_sgl_dma;
+};
+
+/**
+ * struct chain_tracker - firmware chain tracker
+ * @chain_buffer: chain buffer
+ * @chain_buffer_dma: physical address
+ * @tracker_list: list of free request (ioc->free_chain_list)
+ */
+struct chain_tracker {
+ void *chain_buffer;
+ dma_addr_t chain_buffer_dma;
+};
+
+struct chain_lookup {
+ struct chain_tracker *chains_per_smid;
+ atomic_t chain_offset;
+};
+
+/**
+ * struct scsiio_tracker - scsi mf request tracker
+ * @smid: system message id
+ * @cb_idx: callback index
+ * @direct_io: To indicate whether I/O is direct (WARPDRIVE)
+ * @chain_list: list of associated firmware chain tracker
+ * @msix_io: IO's msix
+ */
+struct scsiio_tracker {
+ u16 smid;
+ struct scsi_cmnd *scmd;
+ u8 cb_idx;
+ u8 direct_io;
+ struct pcie_sg_list pcie_sg_list;
+ struct list_head chain_list;
+ u16 msix_io;
+};
+
+/**
+ * struct request_tracker - firmware request tracker
+ * @smid: system message id
+ * @cb_idx: callback index
+ * @tracker_list: list of free request (ioc->free_list)
+ */
+struct request_tracker {
+ u16 smid;
+ u8 cb_idx;
+ struct list_head tracker_list;
+};
+
+/**
+ * struct _tr_list - target reset list
+ * @handle: device handle
+ * @state: state machine
+ */
+struct _tr_list {
+ struct list_head list;
+ u16 handle;
+ u16 state;
+};
+
+/**
+ * struct _sc_list - delayed SAS_IO_UNIT_CONTROL message list
+ * @handle: device handle
+ */
+struct _sc_list {
+ struct list_head list;
+ u16 handle;
+};
+
+/**
+ * struct _event_ack_list - delayed event acknowledgment list
+ * @Event: Event ID
+ * @EventContext: used to track the event uniquely
+ */
+struct _event_ack_list {
+ struct list_head list;
+ U16 Event;
+ U32 EventContext;
+};
+
+/**
+ * struct adapter_reply_queue - the reply queue struct
+ * @ioc: per adapter object
+ * @msix_index: msix index into vector table
+ * @vector: irq vector
+ * @reply_post_host_index: head index in the pool where FW completes IO
+ * @reply_post_free: reply post base virt address
+ * @name: the name registered to request_irq()
+ * @busy: isr is actively processing replies on another cpu
+ * @os_irq: irq number
+ * @irqpoll: irq_poll object
+ * @irq_poll_scheduled: Tells whether irq poll is scheduled or not
+ * @is_iouring_poll_q: Tells whether reply queues is assigned
+ * to io uring poll queues or not
+ * @list: this list
+*/
+struct adapter_reply_queue {
+ struct MPT3SAS_ADAPTER *ioc;
+ u8 msix_index;
+ u32 reply_post_host_index;
+ Mpi2ReplyDescriptorsUnion_t *reply_post_free;
+ char name[MPT_NAME_LENGTH];
+ atomic_t busy;
+ u32 os_irq;
+ struct irq_poll irqpoll;
+ bool irq_poll_scheduled;
+ bool irq_line_enable;
+ bool is_iouring_poll_q;
+ struct list_head list;
+};
+
+/**
+ * struct io_uring_poll_queue - the io uring poll queue structure
+ * @busy: Tells whether io uring poll queue is busy or not
+ * @pause: Tells whether IOs are paused on io uring poll queue or not
+ * @reply_q: reply queue mapped for io uring poll queue
+ */
+struct io_uring_poll_queue {
+ atomic_t busy;
+ atomic_t pause;
+ struct adapter_reply_queue *reply_q;
+};
+
+typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
+
+/* SAS3.0 support */
+typedef int (*MPT_BUILD_SG_SCMD)(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd, u16 smid, struct _pcie_device *pcie_device);
+typedef void (*MPT_BUILD_SG)(struct MPT3SAS_ADAPTER *ioc, void *psge,
+ dma_addr_t data_out_dma, size_t data_out_sz,
+ dma_addr_t data_in_dma, size_t data_in_sz);
+typedef void (*MPT_BUILD_ZERO_LEN_SGE)(struct MPT3SAS_ADAPTER *ioc,
+ void *paddr);
+
+/* SAS3.5 support */
+typedef void (*NVME_BUILD_PRP)(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request,
+ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma,
+ size_t data_in_sz);
+
+/* To support atomic and non atomic descriptors*/
+typedef void (*PUT_SMID_IO_FP_HIP) (struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 funcdep);
+typedef void (*PUT_SMID_DEFAULT) (struct MPT3SAS_ADAPTER *ioc, u16 smid);
+typedef u32 (*BASE_READ_REG) (const void __iomem *addr);
+/*
+ * To get high iops reply queue's msix index when high iops mode is enabled
+ * else get the msix index of general reply queues.
+ */
+typedef u8 (*GET_MSIX_INDEX) (struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd);
+
+/* IOC Facts and Port Facts converted from little endian to cpu */
+union mpi3_version_union {
+ MPI2_VERSION_STRUCT Struct;
+ u32 Word;
+};
+
+struct mpt3sas_facts {
+ u16 MsgVersion;
+ u16 HeaderVersion;
+ u8 IOCNumber;
+ u8 VP_ID;
+ u8 VF_ID;
+ u16 IOCExceptions;
+ u16 IOCStatus;
+ u32 IOCLogInfo;
+ u8 MaxChainDepth;
+ u8 WhoInit;
+ u8 NumberOfPorts;
+ u8 MaxMSIxVectors;
+ u16 RequestCredit;
+ u16 ProductID;
+ u32 IOCCapabilities;
+ union mpi3_version_union FWVersion;
+ u16 IOCRequestFrameSize;
+ u16 IOCMaxChainSegmentSize;
+ u16 MaxInitiators;
+ u16 MaxTargets;
+ u16 MaxSasExpanders;
+ u16 MaxEnclosures;
+ u16 ProtocolFlags;
+ u16 HighPriorityCredit;
+ u16 MaxReplyDescriptorPostQueueDepth;
+ u8 ReplyFrameSize;
+ u8 MaxVolumes;
+ u16 MaxDevHandle;
+ u16 MaxPersistentEntries;
+ u16 MinDevHandle;
+ u8 CurrentHostPageSize;
+};
+
+struct mpt3sas_port_facts {
+ u8 PortNumber;
+ u8 VP_ID;
+ u8 VF_ID;
+ u8 PortType;
+ u16 MaxPostedCmdBuffers;
+};
+
+struct reply_post_struct {
+ Mpi2ReplyDescriptorsUnion_t *reply_post_free;
+ dma_addr_t reply_post_free_dma;
+};
+
+/**
+ * struct virtual_phy - vSES phy structure
+ * sas_address: SAS Address of vSES device
+ * phy_mask: vSES device's phy number
+ * flags: flags used to manage this structure
+ */
+struct virtual_phy {
+ struct list_head list;
+ u64 sas_address;
+ u32 phy_mask;
+ u8 flags;
+};
+
+#define MPT_VPHY_FLAG_DIRTY_PHY 0x01
+
+/**
+ * struct hba_port - Saves each HBA's Wide/Narrow port info
+ * @sas_address: sas address of this wide/narrow port's attached device
+ * @phy_mask: HBA PHY's belonging to this port
+ * @port_id: port number
+ * @flags: hba port flags
+ * @vphys_mask : mask of vSES devices Phy number
+ * @vphys_list : list containing vSES device structures
+ */
+struct hba_port {
+ struct list_head list;
+ u64 sas_address;
+ u32 phy_mask;
+ u8 port_id;
+ u8 flags;
+ u32 vphys_mask;
+ struct list_head vphys_list;
+};
+
+/* hba port flags */
+#define HBA_PORT_FLAG_DIRTY_PORT 0x01
+#define HBA_PORT_FLAG_NEW_PORT 0x02
+
+#define MULTIPATH_DISABLED_PORT_ID 0xFF
+
+/**
+ * struct htb_rel_query - diagnostic buffer release reason
+ * @unique_id - unique id associated with this buffer.
+ * @buffer_rel_condition - Release condition ioctl/sysfs/reset
+ * @reserved
+ * @trigger_type - Master/Event/scsi/MPI
+ * @trigger_info_dwords - Data Correspondig to trigger type
+ */
+struct htb_rel_query {
+ u16 buffer_rel_condition;
+ u16 reserved;
+ u32 trigger_type;
+ u32 trigger_info_dwords[2];
+};
+
+/* Buffer_rel_condition bit fields */
+
+/* Bit 0 - Diag Buffer not Released */
+#define MPT3_DIAG_BUFFER_NOT_RELEASED (0x00)
+/* Bit 0 - Diag Buffer Released */
+#define MPT3_DIAG_BUFFER_RELEASED (0x01)
+
+/*
+ * Bit 1 - Diag Buffer Released by IOCTL,
+ * This bit is valid only if Bit 0 is one
+ */
+#define MPT3_DIAG_BUFFER_REL_IOCTL (0x02 | MPT3_DIAG_BUFFER_RELEASED)
+
+/*
+ * Bit 2 - Diag Buffer Released by Trigger,
+ * This bit is valid only if Bit 0 is one
+ */
+#define MPT3_DIAG_BUFFER_REL_TRIGGER (0x04 | MPT3_DIAG_BUFFER_RELEASED)
+
+/*
+ * Bit 3 - Diag Buffer Released by SysFs,
+ * This bit is valid only if Bit 0 is one
+ */
+#define MPT3_DIAG_BUFFER_REL_SYSFS (0x08 | MPT3_DIAG_BUFFER_RELEASED)
+
+/* DIAG RESET Master trigger flags */
+#define MPT_DIAG_RESET_ISSUED_BY_DRIVER 0x00000000
+#define MPT_DIAG_RESET_ISSUED_BY_USER 0x00000001
+
+typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
+/**
+ * struct MPT3SAS_ADAPTER - per adapter struct
+ * @list: ioc_list
+ * @shost: shost object
+ * @id: unique adapter id
+ * @cpu_count: number online cpus
+ * @name: generic ioc string
+ * @tmp_string: tmp string used for logging
+ * @pdev: pci pdev object
+ * @pio_chip: physical io register space
+ * @chip: memory mapped register space
+ * @chip_phys: physical addrss prior to mapping
+ * @logging_level: see mpt3sas_debug.h
+ * @fwfault_debug: debuging FW timeouts
+ * @ir_firmware: IR firmware present
+ * @bars: bitmask of BAR's that must be configured
+ * @mask_interrupts: ignore interrupt
+ * @pci_access_mutex: Mutex to synchronize ioctl, sysfs show path and
+ * pci resource handling
+ * @fault_reset_work_q_name: fw fault work queue
+ * @fault_reset_work_q: ""
+ * @fault_reset_work: ""
+ * @firmware_event_thread: fw event work queue
+ * @fw_event_lock:
+ * @fw_event_list: list of fw events
+ * @current_evet: current processing firmware event
+ * @fw_event_cleanup: set to one while cleaning up the fw events
+ * @aen_event_read_flag: event log was read
+ * @broadcast_aen_busy: broadcast aen waiting to be serviced
+ * @shost_recovery: host reset in progress
+ * @ioc_reset_in_progress_lock:
+ * @ioc_link_reset_in_progress: phy/hard reset in progress
+ * @ignore_loginfos: ignore loginfos during task management
+ * @remove_host: flag for when driver unloads, to avoid sending dev resets
+ * @pci_error_recovery: flag to prevent ioc access until slot reset completes
+ * @wait_for_discovery_to_complete: flag set at driver load time when
+ * waiting on reporting devices
+ * @is_driver_loading: flag set at driver load time
+ * @port_enable_failed: flag set when port enable has failed
+ * @start_scan: flag set from scan_start callback, cleared from _mpt3sas_fw_work
+ * @start_scan_failed: means port enable failed, return's the ioc_status
+ * @msix_enable: flag indicating msix is enabled
+ * @msix_vector_count: number msix vectors
+ * @cpu_msix_table: table for mapping cpus to msix index
+ * @cpu_msix_table_sz: table size
+ * @total_io_cnt: Gives total IO count, used to load balance the interrupts
+ * @ioc_coredump_loop: will have non-zero value when FW is in CoreDump state
+ * @timestamp_update_count: Counter to fire timeSync command
+ * time_sync_interval: Time sync interval read from man page 11
+ * @high_iops_outstanding: used to load balance the interrupts
+ * within high iops reply queues
+ * @msix_load_balance: Enables load balancing of interrupts across
+ * the multiple MSIXs
+ * @schedule_dead_ioc_flush_running_cmds: callback to flush pending commands
+ * @thresh_hold: Max number of reply descriptors processed
+ * before updating Host Index
+ * @iopoll_q_start_index: starting index of io uring poll queues
+ * in reply queue list
+ * @drv_internal_flags: Bit map internal to driver
+ * @drv_support_bitmap: driver's supported feature bit map
+ * @use_32bit_dma: Flag to use 32 bit consistent dma mask
+ * @scsi_io_cb_idx: shost generated commands
+ * @tm_cb_idx: task management commands
+ * @scsih_cb_idx: scsih internal commands
+ * @transport_cb_idx: transport internal commands
+ * @ctl_cb_idx: clt internal commands
+ * @base_cb_idx: base internal commands
+ * @config_cb_idx: base internal commands
+ * @tm_tr_cb_idx : device removal target reset handshake
+ * @tm_tr_volume_cb_idx : volume removal target reset
+ * @base_cmds:
+ * @transport_cmds:
+ * @scsih_cmds:
+ * @tm_cmds:
+ * @ctl_cmds:
+ * @config_cmds:
+ * @base_add_sg_single: handler for either 32/64 bit sgl's
+ * @event_type: bits indicating which events to log
+ * @event_context: unique id for each logged event
+ * @event_log: event log pointer
+ * @event_masks: events that are masked
+ * @max_shutdown_latency: timeout value for NVMe shutdown operation,
+ * which is equal that NVMe drive's RTD3 Entry Latency
+ * which has reported maximum RTD3 Entry Latency value
+ * among attached NVMe drives.
+ * @facts: static facts data
+ * @prev_fw_facts: previous fw facts data
+ * @pfacts: static port facts data
+ * @manu_pg0: static manufacturing page 0
+ * @manu_pg10: static manufacturing page 10
+ * @manu_pg11: static manufacturing page 11
+ * @bios_pg2: static bios page 2
+ * @bios_pg3: static bios page 3
+ * @ioc_pg8: static ioc page 8
+ * @iounit_pg0: static iounit page 0
+ * @iounit_pg1: static iounit page 1
+ * @sas_hba: sas host object
+ * @sas_expander_list: expander object list
+ * @enclosure_list: enclosure object list
+ * @sas_node_lock:
+ * @sas_device_list: sas device object list
+ * @sas_device_init_list: sas device object list (used only at init time)
+ * @sas_device_lock:
+ * @pcie_device_list: pcie device object list
+ * @pcie_device_init_list: pcie device object list (used only at init time)
+ * @pcie_device_lock:
+ * @io_missing_delay: time for IO completed by fw when PDR enabled
+ * @device_missing_delay: time for device missing by fw when PDR enabled
+ * @sas_id : used for setting volume target IDs
+ * @pcie_target_id: used for setting pcie target IDs
+ * @blocking_handles: bitmask used to identify which devices need blocking
+ * @pd_handles : bitmask for PD handles
+ * @pd_handles_sz : size of pd_handle bitmask
+ * @config_page_sz: config page size
+ * @config_page: reserve memory for config page payload
+ * @config_page_dma:
+ * @hba_queue_depth: hba request queue depth
+ * @sge_size: sg element size for either 32/64 bit
+ * @scsiio_depth: SCSI_IO queue depth
+ * @request_sz: per request frame size
+ * @request: pool of request frames
+ * @request_dma:
+ * @request_dma_sz:
+ * @scsi_lookup: firmware request tracker list
+ * @scsi_lookup_lock:
+ * @free_list: free list of request
+ * @pending_io_count:
+ * @reset_wq:
+ * @chain: pool of chains
+ * @chain_dma:
+ * @max_sges_in_main_message: number sg elements in main message
+ * @max_sges_in_chain_message: number sg elements per chain
+ * @chains_needed_per_io: max chains per io
+ * @chain_depth: total chains allocated
+ * @chain_segment_sz: gives the max number of
+ * SGEs accommodate on single chain buffer
+ * @hi_priority_smid:
+ * @hi_priority:
+ * @hi_priority_dma:
+ * @hi_priority_depth:
+ * @hpr_lookup:
+ * @hpr_free_list:
+ * @internal_smid:
+ * @internal:
+ * @internal_dma:
+ * @internal_depth:
+ * @internal_lookup:
+ * @internal_free_list:
+ * @sense: pool of sense
+ * @sense_dma:
+ * @sense_dma_pool:
+ * @reply_depth: hba reply queue depth:
+ * @reply_sz: per reply frame size:
+ * @reply: pool of replys:
+ * @reply_dma:
+ * @reply_dma_pool:
+ * @reply_free_queue_depth: reply free depth
+ * @reply_free: pool for reply free queue (32 bit addr)
+ * @reply_free_dma:
+ * @reply_free_dma_pool:
+ * @reply_free_host_index: tail index in pool to insert free replys
+ * @reply_post_queue_depth: reply post queue depth
+ * @reply_post_struct: struct for reply_post_free physical & virt address
+ * @rdpq_array_capable: FW supports multiple reply queue addresses in ioc_init
+ * @rdpq_array_enable: rdpq_array support is enabled in the driver
+ * @rdpq_array_enable_assigned: this ensures that rdpq_array_enable flag
+ * is assigned only ones
+ * @reply_queue_count: number of reply queue's
+ * @reply_queue_list: link list contaning the reply queue info
+ * @msix96_vector: 96 MSI-X vector support
+ * @replyPostRegisterIndex: index of next position in Reply Desc Post Queue
+ * @delayed_tr_list: target reset link list
+ * @delayed_tr_volume_list: volume target reset link list
+ * @delayed_sc_list:
+ * @delayed_event_ack_list:
+ * @temp_sensors_count: flag to carry the number of temperature sensors
+ * @pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and
+ * pci resource handling. PCI resource freeing will lead to free
+ * vital hardware/memory resource, which might be in use by cli/sysfs
+ * path functions resulting in Null pointer reference followed by kernel
+ * crash. To avoid the above race condition we use mutex syncrhonization
+ * which ensures the syncrhonization between cli/sysfs_show path.
+ * @atomic_desc_capable: Atomic Request Descriptor support.
+ * @GET_MSIX_INDEX: Get the msix index of high iops queues.
+ * @multipath_on_hba: flag to determine multipath on hba is enabled or not
+ * @port_table_list: list containing HBA's wide/narrow port's info
+ */
+struct MPT3SAS_ADAPTER {
+ struct list_head list;
+ struct Scsi_Host *shost;
+ u8 id;
+ int cpu_count;
+ char name[MPT_NAME_LENGTH];
+ char driver_name[MPT_NAME_LENGTH - 8];
+ char tmp_string[MPT_STRING_LENGTH];
+ struct pci_dev *pdev;
+ Mpi2SystemInterfaceRegs_t __iomem *chip;
+ phys_addr_t chip_phys;
+ int logging_level;
+ int fwfault_debug;
+ u8 ir_firmware;
+ int bars;
+ u8 mask_interrupts;
+
+ /* fw fault handler */
+ char fault_reset_work_q_name[20];
+ struct workqueue_struct *fault_reset_work_q;
+ struct delayed_work fault_reset_work;
+
+ /* fw event handler */
+ struct workqueue_struct *firmware_event_thread;
+ spinlock_t fw_event_lock;
+ struct list_head fw_event_list;
+ struct fw_event_work *current_event;
+ u8 fw_events_cleanup;
+
+ /* misc flags */
+ int aen_event_read_flag;
+ u8 broadcast_aen_busy;
+ u16 broadcast_aen_pending;
+ u8 shost_recovery;
+ u8 got_task_abort_from_ioctl;
+
+ struct mutex reset_in_progress_mutex;
+ struct mutex hostdiag_unlock_mutex;
+ spinlock_t ioc_reset_in_progress_lock;
+ u8 ioc_link_reset_in_progress;
+
+ u8 ignore_loginfos;
+ u8 remove_host;
+ u8 pci_error_recovery;
+ u8 wait_for_discovery_to_complete;
+ u8 is_driver_loading;
+ u8 port_enable_failed;
+ u8 start_scan;
+ u16 start_scan_failed;
+
+ u8 msix_enable;
+ u16 msix_vector_count;
+ u8 *cpu_msix_table;
+ u16 cpu_msix_table_sz;
+ resource_size_t __iomem **reply_post_host_index;
+ u32 ioc_reset_count;
+ MPT3SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds;
+ u32 non_operational_loop;
+ u8 ioc_coredump_loop;
+ u32 timestamp_update_count;
+ u32 time_sync_interval;
+ atomic64_t total_io_cnt;
+ atomic64_t high_iops_outstanding;
+ bool msix_load_balance;
+ u16 thresh_hold;
+ u8 high_iops_queues;
+ u8 iopoll_q_start_index;
+ u32 drv_internal_flags;
+ u32 drv_support_bitmap;
+ u32 dma_mask;
+ bool enable_sdev_max_qd;
+ bool use_32bit_dma;
+ struct io_uring_poll_queue *io_uring_poll_queues;
+
+ /* internal commands, callback index */
+ u8 scsi_io_cb_idx;
+ u8 tm_cb_idx;
+ u8 transport_cb_idx;
+ u8 scsih_cb_idx;
+ u8 ctl_cb_idx;
+ u8 base_cb_idx;
+ u8 port_enable_cb_idx;
+ u8 config_cb_idx;
+ u8 tm_tr_cb_idx;
+ u8 tm_tr_volume_cb_idx;
+ u8 tm_sas_control_cb_idx;
+ struct _internal_cmd base_cmds;
+ struct _internal_cmd port_enable_cmds;
+ struct _internal_cmd transport_cmds;
+ struct _internal_cmd scsih_cmds;
+ struct _internal_cmd tm_cmds;
+ struct _internal_cmd ctl_cmds;
+ struct _internal_cmd config_cmds;
+
+ MPT_ADD_SGE base_add_sg_single;
+
+ /* function ptr for either IEEE or MPI sg elements */
+ MPT_BUILD_SG_SCMD build_sg_scmd;
+ MPT_BUILD_SG build_sg;
+ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge;
+ u16 sge_size_ieee;
+ u16 hba_mpi_version_belonged;
+
+ /* function ptr for MPI sg elements only */
+ MPT_BUILD_SG build_sg_mpi;
+ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge_mpi;
+
+ /* function ptr for NVMe PRP elements only */
+ NVME_BUILD_PRP build_nvme_prp;
+
+ /* event log */
+ u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+ u32 event_context;
+ void *event_log;
+ u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+
+ u8 tm_custom_handling;
+ u8 nvme_abort_timeout;
+ u16 max_shutdown_latency;
+ u16 max_wideport_qd;
+ u16 max_narrowport_qd;
+ u16 max_nvme_qd;
+ u8 max_sata_qd;
+
+ /* static config pages */
+ struct mpt3sas_facts facts;
+ struct mpt3sas_facts prev_fw_facts;
+ struct mpt3sas_port_facts *pfacts;
+ Mpi2ManufacturingPage0_t manu_pg0;
+ struct Mpi2ManufacturingPage10_t manu_pg10;
+ struct Mpi2ManufacturingPage11_t manu_pg11;
+ Mpi2BiosPage2_t bios_pg2;
+ Mpi2BiosPage3_t bios_pg3;
+ Mpi2IOCPage8_t ioc_pg8;
+ Mpi2IOUnitPage0_t iounit_pg0;
+ Mpi2IOUnitPage1_t iounit_pg1;
+ Mpi2IOCPage1_t ioc_pg1_copy;
+
+ struct _boot_device req_boot_device;
+ struct _boot_device req_alt_boot_device;
+ struct _boot_device current_boot_device;
+
+ /* sas hba, expander, and device list */
+ struct _sas_node sas_hba;
+ struct list_head sas_expander_list;
+ struct list_head enclosure_list;
+ spinlock_t sas_node_lock;
+ struct list_head sas_device_list;
+ struct list_head sas_device_init_list;
+ spinlock_t sas_device_lock;
+ struct list_head pcie_device_list;
+ struct list_head pcie_device_init_list;
+ spinlock_t pcie_device_lock;
+
+ struct list_head raid_device_list;
+ spinlock_t raid_device_lock;
+ u8 io_missing_delay;
+ u16 device_missing_delay;
+ int sas_id;
+ int pcie_target_id;
+
+ void *blocking_handles;
+ void *pd_handles;
+ u16 pd_handles_sz;
+
+ void *pend_os_device_add;
+ u16 pend_os_device_add_sz;
+
+ /* config page */
+ u16 config_page_sz;
+ void *config_page;
+ dma_addr_t config_page_dma;
+ void *config_vaddr;
+
+ /* scsiio request */
+ u16 hba_queue_depth;
+ u16 sge_size;
+ u16 scsiio_depth;
+ u16 request_sz;
+ u8 *request;
+ dma_addr_t request_dma;
+ u32 request_dma_sz;
+ struct pcie_sg_list *pcie_sg_lookup;
+ spinlock_t scsi_lookup_lock;
+ int pending_io_count;
+ wait_queue_head_t reset_wq;
+ u16 *io_queue_num;
+
+ /* PCIe SGL */
+ struct dma_pool *pcie_sgl_dma_pool;
+ /* Host Page Size */
+ u32 page_size;
+
+ /* chain */
+ struct chain_lookup *chain_lookup;
+ struct list_head free_chain_list;
+ struct dma_pool *chain_dma_pool;
+ ulong chain_pages;
+ u16 max_sges_in_main_message;
+ u16 max_sges_in_chain_message;
+ u16 chains_needed_per_io;
+ u32 chain_depth;
+ u16 chain_segment_sz;
+ u16 chains_per_prp_buffer;
+
+ /* hi-priority queue */
+ u16 hi_priority_smid;
+ u8 *hi_priority;
+ dma_addr_t hi_priority_dma;
+ u16 hi_priority_depth;
+ struct request_tracker *hpr_lookup;
+ struct list_head hpr_free_list;
+
+ /* internal queue */
+ u16 internal_smid;
+ u8 *internal;
+ dma_addr_t internal_dma;
+ u16 internal_depth;
+ struct request_tracker *internal_lookup;
+ struct list_head internal_free_list;
+
+ /* sense */
+ u8 *sense;
+ dma_addr_t sense_dma;
+ struct dma_pool *sense_dma_pool;
+
+ /* reply */
+ u16 reply_sz;
+ u8 *reply;
+ dma_addr_t reply_dma;
+ u32 reply_dma_max_address;
+ u32 reply_dma_min_address;
+ struct dma_pool *reply_dma_pool;
+
+ /* reply free queue */
+ u16 reply_free_queue_depth;
+ __le32 *reply_free;
+ dma_addr_t reply_free_dma;
+ struct dma_pool *reply_free_dma_pool;
+ u32 reply_free_host_index;
+
+ /* reply post queue */
+ u16 reply_post_queue_depth;
+ struct reply_post_struct *reply_post;
+ u8 rdpq_array_capable;
+ u8 rdpq_array_enable;
+ u8 rdpq_array_enable_assigned;
+ struct dma_pool *reply_post_free_dma_pool;
+ struct dma_pool *reply_post_free_array_dma_pool;
+ Mpi2IOCInitRDPQArrayEntry *reply_post_free_array;
+ dma_addr_t reply_post_free_array_dma;
+ u8 reply_queue_count;
+ struct list_head reply_queue_list;
+
+ u8 combined_reply_queue;
+ u8 combined_reply_index_count;
+ u8 smp_affinity_enable;
+ /* reply post register index */
+ resource_size_t __iomem **replyPostRegisterIndex;
+
+ struct list_head delayed_tr_list;
+ struct list_head delayed_tr_volume_list;
+ struct list_head delayed_sc_list;
+ struct list_head delayed_event_ack_list;
+ u8 temp_sensors_count;
+ struct mutex pci_access_mutex;
+
+ /* diag buffer support */
+ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT];
+ dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT];
+ u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23];
+ u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 ring_buffer_offset;
+ u32 ring_buffer_sz;
+ struct htb_rel_query htb_rel;
+ u8 reset_from_user;
+ u8 is_warpdrive;
+ u8 is_mcpu_endpoint;
+ u8 hide_ir_msg;
+ u8 mfg_pg10_hide_flag;
+ u8 hide_drives;
+ spinlock_t diag_trigger_lock;
+ u8 diag_trigger_active;
+ u8 atomic_desc_capable;
+ BASE_READ_REG base_readl;
+ BASE_READ_REG base_readl_ext_retry;
+ struct SL_WH_MASTER_TRIGGER_T diag_trigger_master;
+ struct SL_WH_EVENT_TRIGGERS_T diag_trigger_event;
+ struct SL_WH_SCSI_TRIGGERS_T diag_trigger_scsi;
+ struct SL_WH_MPI_TRIGGERS_T diag_trigger_mpi;
+ u8 supports_trigger_pages;
+ void *device_remove_in_progress;
+ u16 device_remove_in_progress_sz;
+ u8 is_gen35_ioc;
+ u8 is_aero_ioc;
+ struct dentry *debugfs_root;
+ struct dentry *ioc_dump;
+ PUT_SMID_IO_FP_HIP put_smid_scsi_io;
+ PUT_SMID_IO_FP_HIP put_smid_fast_path;
+ PUT_SMID_IO_FP_HIP put_smid_hi_priority;
+ PUT_SMID_DEFAULT put_smid_default;
+ GET_MSIX_INDEX get_msix_index_for_smlio;
+
+ u8 multipath_on_hba;
+ struct list_head port_table_list;
+};
+
+struct mpt3sas_debugfs_buffer {
+ void *buf;
+ u32 len;
+};
+
+#define MPT_DRV_SUPPORT_BITMAP_MEMMOVE 0x00000001
+#define MPT_DRV_SUPPORT_BITMAP_ADDNLQUERY 0x00000002
+
+#define MPT_DRV_INTERNAL_FIRST_PE_ISSUED 0x00000001
+
+typedef u8 (*MPT_CALLBACK)(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+
+/*
+ * struct ATTO_SAS_NVRAM - ATTO NVRAM settings stored
+ * in Manufacturing page 1 used to get
+ * ATTO SasAddr.
+ */
+struct ATTO_SAS_NVRAM {
+ u8 Signature[4];
+ u8 Version;
+#define ATTO_SASNVR_VERSION 0
+
+ u8 Checksum;
+#define ATTO_SASNVR_CKSUM_SEED 0x5A
+ u8 Pad[10];
+ u8 SasAddr[8];
+#define ATTO_SAS_ADDR_ALIGN 64
+ u8 Reserved[232];
+};
+
+#define ATTO_SAS_ADDR_DEVNAME_BIAS 63
+
+union ATTO_SAS_ADDRESS {
+ U8 b[8];
+ U16 w[4];
+ U32 d[2];
+ U64 q;
+};
+
+/* base shared API */
+extern struct list_head mpt3sas_ioc_list;
+extern char driver_name[MPT_NAME_LENGTH];
+/* spinlock on list operations over IOCs
+ * Case: when multiple warpdrive cards(IOCs) are in use
+ * Each IOC will added to the ioc list structure on initialization.
+ * Watchdog threads run at regular intervals to check IOC for any
+ * fault conditions which will trigger the dead_ioc thread to
+ * deallocate pci resource, resulting deleting the IOC netry from list,
+ * this deletion need to protected by spinlock to enusre that
+ * ioc removal is syncrhonized, if not synchronized it might lead to
+ * list_del corruption as the ioc list is traversed in cli path.
+ */
+extern spinlock_t gioc_lock;
+
+void mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc);
+
+int mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc);
+int mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_free_enclosure_list(struct MPT3SAS_ADAPTER *ioc);
+int mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc,
+ enum reset_type type);
+
+void *mpt3sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void *mpt3sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+__le32 mpt3sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc,
+ u16 smid);
+void *mpt3sas_base_get_pcie_sgl(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+dma_addr_t mpt3sas_base_get_pcie_sgl_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc, u8 poll);
+void mpt3sas_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc);
+
+void mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle);
+void mpt3sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 msix_task);
+void mpt3sas_base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+/* hi-priority queue */
+u16 mpt3sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx);
+u16 mpt3sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx,
+ struct scsi_cmnd *scmd);
+void mpt3sas_base_clear_st(struct MPT3SAS_ADAPTER *ioc,
+ struct scsiio_tracker *st);
+
+u16 mpt3sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx);
+void mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void mpt3sas_base_initialize_callback_handler(void);
+u8 mpt3sas_base_register_callback_handler(MPT_CALLBACK cb_func);
+void mpt3sas_base_release_callback_handler(u8 cb_idx);
+
+u8 mpt3sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+u8 mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 msix_index, u32 reply);
+void *mpt3sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc,
+ u32 phys_addr);
+
+u32 mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked);
+
+void mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code);
+#define mpt3sas_print_fault_code(ioc, fault_code) \
+do { pr_err("%s fault info from func: %s\n", ioc->name, __func__); \
+ mpt3sas_base_fault_info(ioc, fault_code); } while (0)
+
+void mpt3sas_base_coredump_info(struct MPT3SAS_ADAPTER *ioc, u16 fault_code);
+#define mpt3sas_print_coredump_info(ioc, fault_code) \
+do { pr_err("%s fault info from func: %s\n", ioc->name, __func__); \
+ mpt3sas_base_coredump_info(ioc, fault_code); } while (0)
+
+int mpt3sas_base_wait_for_coredump_completion(struct MPT3SAS_ADAPTER *ioc,
+ const char *caller);
+int mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SasIoUnitControlReply_t *mpi_reply,
+ Mpi2SasIoUnitControlRequest_t *mpi_request);
+int mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request);
+
+void mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc,
+ u32 *event_type);
+
+void mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc);
+
+void mpt3sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc,
+ u16 device_missing_delay, u8 io_missing_delay);
+
+int mpt3sas_base_check_for_fault_and_issue_reset(
+ struct MPT3SAS_ADAPTER *ioc);
+
+int mpt3sas_port_enable(struct MPT3SAS_ADAPTER *ioc);
+
+void
+mpt3sas_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc);
+
+u8 mpt3sas_base_check_cmd_timeout(struct MPT3SAS_ADAPTER *ioc,
+ u8 status, void *mpi_request, int sz);
+#define mpt3sas_check_cmd_timeout(ioc, status, mpi_request, sz, issue_reset) \
+do { ioc_err(ioc, "In func: %s\n", __func__); \
+ issue_reset = mpt3sas_base_check_cmd_timeout(ioc, \
+ status, mpi_request, sz); } while (0)
+
+int mpt3sas_wait_for_ioc(struct MPT3SAS_ADAPTER *ioc, int wait_count);
+int mpt3sas_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type);
+void mpt3sas_base_free_irq(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_disable_msix(struct MPT3SAS_ADAPTER *ioc);
+int mpt3sas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num);
+void mpt3sas_base_pause_mq_polling(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_resume_mq_polling(struct MPT3SAS_ADAPTER *ioc);
+int mpt3sas_base_unlock_and_get_host_diagnostic(struct MPT3SAS_ADAPTER *ioc,
+ u32 *host_diagnostic);
+void mpt3sas_base_lock_host_diagnostic(struct MPT3SAS_ADAPTER *ioc);
+
+/* scsih shared API */
+struct scsi_cmnd *mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc,
+ u16 smid);
+u8 mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
+ u32 reply);
+void mpt3sas_scsih_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_scsih_clear_outstanding_scsi_tm_commands(
+ struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_scsih_reset_done_handler(struct MPT3SAS_ADAPTER *ioc);
+
+int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ uint channel, uint id, u64 lun, u8 type, u16 smid_task,
+ u16 msix_task, u8 timeout, u8 tr_method);
+int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ uint channel, uint id, u64 lun, u8 type, u16 smid_task,
+ u16 msix_task, u8 timeout, u8 tr_method);
+
+void mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle);
+void mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle);
+void mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ struct hba_port *port);
+void mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct hba_port *port);
+u8 mpt3sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc,
+ u16 smid);
+struct hba_port *
+mpt3sas_get_port_by_id(struct MPT3SAS_ADAPTER *ioc, u8 port,
+ u8 bypass_dirty_port_flag);
+
+struct _sas_node *mpt3sas_scsih_expander_find_by_handle(
+ struct MPT3SAS_ADAPTER *ioc, u16 handle);
+struct _sas_node *mpt3sas_scsih_expander_find_by_sas_address(
+ struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ struct hba_port *port);
+struct _sas_device *mpt3sas_get_sdev_by_addr(
+ struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ struct hba_port *port);
+struct _sas_device *__mpt3sas_get_sdev_by_addr(
+ struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ struct hba_port *port);
+struct _sas_device *mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc,
+ u16 handle);
+struct _pcie_device *mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc,
+ u16 handle);
+
+void mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc);
+struct _raid_device *
+mpt3sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle);
+void mpt3sas_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth);
+struct _sas_device *
+__mpt3sas_get_sdev_by_rphy(struct MPT3SAS_ADAPTER *ioc, struct sas_rphy *rphy);
+struct virtual_phy *
+mpt3sas_get_vphy_by_phy(struct MPT3SAS_ADAPTER *ioc,
+ struct hba_port *port, u32 phy);
+
+/* config shared API */
+u8 mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+int mpt3sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc,
+ u8 *num_phys);
+int mpt3sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page);
+int mpt3sas_config_get_manufacturing_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage1_t *config_page);
+
+int mpt3sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage10_t *config_page);
+
+int mpt3sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page);
+int mpt3sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page);
+
+int mpt3sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage2_t *config_page);
+int mpt3sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage3_t *config_page);
+int mpt3sas_config_set_bios_pg4(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page,
+ int sz_config_page);
+int mpt3sas_config_get_bios_pg4(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page,
+ int sz_config_page);
+int mpt3sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage0_t *config_page);
+int mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_pcie_device_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeDevicePage0_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_pcie_device_pg2(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeDevicePage2_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_pcie_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeIOUnitPage1_t *config_page,
+ u16 sz);
+int mpt3sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page,
+ u16 sz);
+int mpt3sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage1_t *config_page);
+int mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz);
+int mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage1_t *config_page);
+int mpt3sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage8_t *config_page);
+int mpt3sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz);
+int mpt3sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz);
+int mpt3sas_config_get_ioc_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOCPage1_t *config_page);
+int mpt3sas_config_set_ioc_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOCPage1_t *config_page);
+int mpt3sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOCPage8_t *config_page);
+int mpt3sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage0_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage1_t *config_page,
+ u32 phy_number, u16 handle);
+int mpt3sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasEnclosurePage0_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number);
+int mpt3sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number);
+int mpt3sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
+ u32 handle);
+int mpt3sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u8 *num_pds);
+int mpt3sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form,
+ u32 handle, u16 sz);
+int mpt3sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page,
+ u32 form, u32 form_specific);
+int mpt3sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle,
+ u16 *volume_handle);
+int mpt3sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc,
+ u16 volume_handle, u64 *wwid);
+int
+mpt3sas_config_get_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage0_t *config_page);
+int
+mpt3sas_config_get_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage1_t *config_page);
+int
+mpt3sas_config_get_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage2_t *config_page);
+int
+mpt3sas_config_get_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage3_t *config_page);
+int
+mpt3sas_config_get_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage4_t *config_page);
+int
+mpt3sas_config_update_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_MASTER_TRIGGER_T *master_tg, bool set);
+int
+mpt3sas_config_update_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_EVENT_TRIGGERS_T *event_tg, bool set);
+int
+mpt3sas_config_update_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_SCSI_TRIGGERS_T *scsi_tg, bool set);
+int
+mpt3sas_config_update_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_MPI_TRIGGERS_T *mpi_tg, bool set);
+
+/* ctl shared API */
+extern const struct attribute_group *mpt3sas_host_groups[];
+extern const struct attribute_group *mpt3sas_dev_groups[];
+void mpt3sas_ctl_init(ushort hbas_to_enumerate);
+void mpt3sas_ctl_exit(ushort hbas_to_enumerate);
+void mpt3sas_ctl_release(struct MPT3SAS_ADAPTER *ioc);
+u8 mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+void mpt3sas_ctl_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_ctl_clear_outstanding_ioctls(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_ctl_reset_done_handler(struct MPT3SAS_ADAPTER *ioc);
+u8 mpt3sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc,
+ u8 msix_index, u32 reply);
+void mpt3sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply);
+
+void mpt3sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc,
+ u8 bits_to_register);
+int mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type,
+ u8 *issue_reset);
+
+/* transport shared API */
+extern struct scsi_transport_template *mpt3sas_transport_template;
+u8 mpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+struct _sas_port *mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc,
+ u16 handle, u64 sas_address, struct hba_port *port);
+void mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u64 sas_address_parent, struct hba_port *port);
+int mpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
+int mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_phy *mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1,
+ struct device *parent_dev);
+void mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate,
+ struct hba_port *port);
+extern struct sas_function_template mpt3sas_transport_functions;
+extern struct scsi_transport_template *mpt3sas_transport_template;
+void
+mpt3sas_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy);
+void
+mpt3sas_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy,
+ u64 sas_address, struct hba_port *port);
+/* trigger data externs */
+void mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data);
+void mpt3sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data);
+void mpt3sas_trigger_master(struct MPT3SAS_ADAPTER *ioc,
+ u32 trigger_bitmask);
+void mpt3sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event,
+ u16 log_entry_qualifier);
+void mpt3sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key,
+ u8 asc, u8 ascq);
+void mpt3sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status,
+ u32 loginfo);
+
+/* warpdrive APIs */
+u8 mpt3sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device);
+void
+mpt3sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ struct _raid_device *raid_device, Mpi25SCSIIORequest_t *mpi_request);
+
+void mpt3sas_setup_debugfs(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_destroy_debugfs(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_init_debugfs(void);
+void mpt3sas_exit_debugfs(void);
+
+/**
+ * _scsih_is_pcie_scsi_device - determines if device is an pcie scsi device
+ * @device_info: bitfield providing information about the device.
+ * Context: none
+ *
+ * Returns 1 if scsi device.
+ */
+static inline int
+mpt3sas_scsih_is_pcie_scsi_device(u32 device_info)
+{
+ if ((device_info &
+ MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE) == MPI26_PCIE_DEVINFO_SCSI)
+ return 1;
+ else
+ return 0;
+}
+#endif /* MPT3SAS_BASE_H_INCLUDED */
diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c
new file mode 100644
index 000000000000..45ac853e1289
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_config.c
@@ -0,0 +1,2715 @@
+/*
+ * This module provides common API for accessing firmware configuration pages
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include "mpt3sas_base.h"
+
+/* local definitions */
+
+/* Timeout for config page request (in seconds) */
+#define MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT 15
+
+/* Common sgl flags for READING a config page. */
+#define MPT3_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
+ | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT)
+
+/* Common sgl flags for WRITING a config page. */
+#define MPT3_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
+ | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \
+ << MPI2_SGE_FLAGS_SHIFT)
+
+/**
+ * struct config_request - obtain dma memory via routine
+ * @sz: size
+ * @page: virt pointer
+ * @page_dma: phys pointer
+ *
+ */
+struct config_request {
+ u16 sz;
+ void *page;
+ dma_addr_t page_dma;
+};
+
+/**
+ * _config_display_some_debug - debug routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @calling_function_name: string pass from calling function
+ * @mpi_reply: reply message frame
+ * Context: none.
+ *
+ * Function for displaying debug info helpful when debugging issues
+ * in this module.
+ */
+static void
+_config_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
+{
+ Mpi2ConfigRequest_t *mpi_request;
+ char *desc = NULL;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) {
+ case MPI2_CONFIG_PAGETYPE_IO_UNIT:
+ desc = "io_unit";
+ break;
+ case MPI2_CONFIG_PAGETYPE_IOC:
+ desc = "ioc";
+ break;
+ case MPI2_CONFIG_PAGETYPE_BIOS:
+ desc = "bios";
+ break;
+ case MPI2_CONFIG_PAGETYPE_RAID_VOLUME:
+ desc = "raid_volume";
+ break;
+ case MPI2_CONFIG_PAGETYPE_MANUFACTURING:
+ desc = "manufacturing";
+ break;
+ case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK:
+ desc = "physdisk";
+ break;
+ case MPI2_CONFIG_PAGETYPE_EXTENDED:
+ switch (mpi_request->ExtPageType) {
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT:
+ desc = "sas_io_unit";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER:
+ desc = "sas_expander";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE:
+ desc = "sas_device";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY:
+ desc = "sas_phy";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_LOG:
+ desc = "log";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE:
+ desc = "enclosure";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG:
+ desc = "raid_config";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING:
+ desc = "driver_mapping";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_PORT:
+ desc = "sas_port";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING:
+ desc = "ext_manufacturing";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_PCIE_IO_UNIT:
+ desc = "pcie_io_unit";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_PCIE_SWITCH:
+ desc = "pcie_switch";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE:
+ desc = "pcie_device";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_PCIE_LINK:
+ desc = "pcie_link";
+ break;
+ }
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ ioc_info(ioc, "%s: %s(%d), action(%d), form(0x%08x), smid(%d)\n",
+ calling_function_name, desc,
+ mpi_request->Header.PageNumber, mpi_request->Action,
+ le32_to_cpu(mpi_request->PageAddress), smid);
+
+ if (!mpi_reply)
+ return;
+
+ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
+ ioc_info(ioc, "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+}
+
+/**
+ * _config_alloc_config_dma_memory - obtain physical memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper for obtaining dma-able memory for config page request.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_config_alloc_config_dma_memory(struct MPT3SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ int r = 0;
+
+ if (mem->sz > ioc->config_page_sz) {
+ mem->page = dma_alloc_coherent(&ioc->pdev->dev, mem->sz,
+ &mem->page_dma, GFP_KERNEL);
+ if (!mem->page) {
+ ioc_err(ioc, "%s: dma_alloc_coherent failed asking for (%d) bytes!!\n",
+ __func__, mem->sz);
+ r = -ENOMEM;
+ }
+ } else { /* use tmp buffer if less than 512 bytes */
+ mem->page = ioc->config_page;
+ mem->page_dma = ioc->config_page_dma;
+ }
+ ioc->config_vaddr = mem->page;
+ return r;
+}
+
+/**
+ * _config_free_config_dma_memory - wrapper to free the memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static void
+_config_free_config_dma_memory(struct MPT3SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ if (mem->sz > ioc->config_page_sz)
+ dma_free_coherent(&ioc->pdev->dev, mem->sz, mem->page,
+ mem->page_dma);
+}
+
+/**
+ * mpt3sas_config_done - config page completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using _config_request.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ if (ioc->config_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->config_cmds.smid != smid)
+ return 1;
+ ioc->config_cmds.status |= MPT3_CMD_COMPLETE;
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ ioc->config_cmds.status |= MPT3_CMD_REPLY_VALID;
+ memcpy(ioc->config_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ }
+ ioc->config_cmds.status &= ~MPT3_CMD_PENDING;
+ if (ioc->logging_level & MPT_DEBUG_CONFIG)
+ _config_display_some_debug(ioc, smid, "config_done", mpi_reply);
+ ioc->config_cmds.smid = USHRT_MAX;
+ complete(&ioc->config_cmds.done);
+ return 1;
+}
+
+/**
+ * _config_request - main routine for sending config page requests
+ * @ioc: per adapter object
+ * @mpi_request: request message frame
+ * @mpi_reply: reply mf payload returned from firmware
+ * @timeout: timeout in seconds
+ * @config_page: contents of the config page
+ * @config_page_sz: size of config page
+ * Context: sleep
+ *
+ * A generic API for config page requests to firmware.
+ *
+ * The ioc->config_cmds.status flag should be MPT3_CMD_NOT_USED before calling
+ * this API.
+ *
+ * The callback index is set inside `ioc->config_cb_idx.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
+ *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout,
+ void *config_page, u16 config_page_sz)
+{
+ u16 smid;
+ Mpi2ConfigRequest_t *config_request;
+ int r;
+ u8 retry_count, issue_host_reset = 0;
+ struct config_request mem;
+ u32 ioc_status = UINT_MAX;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ if (ioc->config_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: config_cmd in use\n", __func__);
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return -EAGAIN;
+ }
+
+ retry_count = 0;
+ memset(&mem, 0, sizeof(struct config_request));
+
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ if (config_page) {
+ mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request->Header.PageType = mpi_reply->Header.PageType;
+ mpi_request->Header.PageLength = mpi_reply->Header.PageLength;
+ mpi_request->ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request->ExtPageType = mpi_reply->ExtPageType;
+ if (mpi_request->Header.PageLength)
+ mem.sz = mpi_request->Header.PageLength * 4;
+ else
+ mem.sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r != 0)
+ goto out;
+ if (mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
+ mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
+ ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
+ MPT3_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz,
+ mem.page_dma);
+ memcpy(mem.page, config_page, min_t(u16, mem.sz,
+ config_page_sz));
+ } else {
+ memset(config_page, 0, config_page_sz);
+ ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
+ MPT3_CONFIG_COMMON_SGLFLAGS | mem.sz, mem.page_dma);
+ memset(mem.page, 0, min_t(u16, mem.sz, config_page_sz));
+ }
+ }
+
+ retry_config:
+ if (retry_count) {
+ if (retry_count > 2) { /* attempt only 2 retries */
+ r = -EFAULT;
+ goto free_mem;
+ }
+ ioc_info(ioc, "%s: attempting retry (%d)\n",
+ __func__, retry_count);
+ }
+
+ r = mpt3sas_wait_for_ioc(ioc, MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ if (r) {
+ if (r == -ETIME)
+ issue_host_reset = 1;
+ goto free_mem;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->config_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ ioc->config_cmds.status = MPT3_CMD_NOT_USED;
+ r = -EAGAIN;
+ goto free_mem;
+ }
+
+ r = 0;
+ memset(ioc->config_cmds.reply, 0, sizeof(Mpi2ConfigReply_t));
+ ioc->config_cmds.status = MPT3_CMD_PENDING;
+ config_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->config_cmds.smid = smid;
+ memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t));
+ if (ioc->logging_level & MPT_DEBUG_CONFIG)
+ _config_display_some_debug(ioc, smid, "config_request", NULL);
+ init_completion(&ioc->config_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->config_cmds.done, timeout*HZ);
+ if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) {
+ if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
+ _config_display_some_debug(ioc,
+ smid, "config_request", NULL);
+ ioc_err(ioc, "%s: command timeout\n", __func__);
+ mpt3sas_base_check_cmd_timeout(ioc, ioc->config_cmds.status,
+ mpi_request, sizeof(Mpi2ConfigRequest_t) / 4);
+ retry_count++;
+ if (ioc->config_cmds.smid == smid)
+ mpt3sas_base_free_smid(ioc, smid);
+ if (ioc->config_cmds.status & MPT3_CMD_RESET)
+ goto retry_config;
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ issue_host_reset = 0;
+ r = -EFAULT;
+ } else
+ issue_host_reset = 1;
+ goto free_mem;
+ }
+
+ if (ioc->config_cmds.status & MPT3_CMD_REPLY_VALID) {
+ memcpy(mpi_reply, ioc->config_cmds.reply,
+ sizeof(Mpi2ConfigReply_t));
+
+ /* Reply Frame Sanity Checks to workaround FW issues */
+ if ((mpi_request->Header.PageType & 0xF) !=
+ (mpi_reply->Header.PageType & 0xF)) {
+ if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
+ _config_display_some_debug(ioc,
+ smid, "config_request", NULL);
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->reply_sz/4);
+ panic("%s: %s: Firmware BUG: mpi_reply mismatch: Requested PageType(0x%02x) Reply PageType(0x%02x)\n",
+ ioc->name, __func__,
+ mpi_request->Header.PageType & 0xF,
+ mpi_reply->Header.PageType & 0xF);
+ }
+
+ if (((mpi_request->Header.PageType & 0xF) ==
+ MPI2_CONFIG_PAGETYPE_EXTENDED) &&
+ mpi_request->ExtPageType != mpi_reply->ExtPageType) {
+ if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
+ _config_display_some_debug(ioc,
+ smid, "config_request", NULL);
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->reply_sz/4);
+ panic("%s: %s: Firmware BUG: mpi_reply mismatch: Requested ExtPageType(0x%02x) Reply ExtPageType(0x%02x)\n",
+ ioc->name, __func__,
+ mpi_request->ExtPageType,
+ mpi_reply->ExtPageType);
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ }
+
+ if (retry_count)
+ ioc_info(ioc, "%s: retry (%d) completed!!\n",
+ __func__, retry_count);
+
+ if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) &&
+ config_page && mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_READ_CURRENT) {
+ u8 *p = (u8 *)mem.page;
+
+ /* Config Page Sanity Checks to workaround FW issues */
+ if (p) {
+ if ((mpi_request->Header.PageType & 0xF) !=
+ (p[3] & 0xF)) {
+ if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
+ _config_display_some_debug(ioc,
+ smid, "config_request", NULL);
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->reply_sz/4);
+ _debug_dump_config(p, min_t(u16, mem.sz,
+ config_page_sz)/4);
+ panic("%s: %s: Firmware BUG: config page mismatch: Requested PageType(0x%02x) Reply PageType(0x%02x)\n",
+ ioc->name, __func__,
+ mpi_request->Header.PageType & 0xF,
+ p[3] & 0xF);
+ }
+
+ if (((mpi_request->Header.PageType & 0xF) ==
+ MPI2_CONFIG_PAGETYPE_EXTENDED) &&
+ (mpi_request->ExtPageType != p[6])) {
+ if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
+ _config_display_some_debug(ioc,
+ smid, "config_request", NULL);
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->reply_sz/4);
+ _debug_dump_config(p, min_t(u16, mem.sz,
+ config_page_sz)/4);
+ panic("%s: %s: Firmware BUG: config page mismatch: Requested ExtPageType(0x%02x) Reply ExtPageType(0x%02x)\n",
+ ioc->name, __func__,
+ mpi_request->ExtPageType, p[6]);
+ }
+ }
+ memcpy(config_page, mem.page, min_t(u16, mem.sz,
+ config_page_sz));
+ }
+
+ free_mem:
+ if (config_page)
+ _config_free_config_dma_memory(ioc, &mem);
+ out:
+ ioc->config_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->config_cmds.mutex);
+
+ if (issue_host_reset) {
+ if (ioc->drv_internal_flags & MPT_DRV_INTERNAL_FIRST_PE_ISSUED) {
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ r = -EFAULT;
+ } else {
+ if (mpt3sas_base_check_for_fault_and_issue_reset(ioc))
+ return -EFAULT;
+ r = -EAGAIN;
+ }
+ }
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg0 - obtain manufacturing page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg1 - obtain manufacturing page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg10 - obtain manufacturing page 10
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage10_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 10;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg11 - obtain manufacturing page 11
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 11;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_manufacturing_pg11 - set manufacturing page 11
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 11;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_bios_pg2 - obtain bios page 2
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 2;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_bios_pg3 - obtain bios page 3
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage3_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 3;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_bios_pg4 - write out bios page 4
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz_config_pg: sizeof the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_bios_pg4(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page,
+ int sz_config_pg)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 4;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE4_PAGEVERSION;
+
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sz_config_pg);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_bios_pg4 - read bios page 4
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz_config_pg: sizeof the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_bios_pg4(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage4_t *config_page,
+ int sz_config_pg)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 4;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE4_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ /*
+ * The sizeof the page is variable. Allow for just the
+ * size to be returned
+ */
+ if (config_page && sz_config_pg) {
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sz_config_pg);
+ }
+
+out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_iounit_pg0 - obtain iounit page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_iounit_pg1 - obtain iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_iounit_pg1 - set iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_iounit_pg3 - obtain iounit page 3
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 3;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE3_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_iounit_pg8 - obtain iounit page 8
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage8_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 8;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE8_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_ioc_pg8 - obtain ioc page 8
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+ mpi_request.Header.PageNumber = 8;
+ mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+/**
+ * mpt3sas_config_get_ioc_pg1 - obtain ioc page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_ioc_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_ioc_pg1 - modify ioc page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_ioc_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_sas_device_pg0 - obtain sas device page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page,
+ u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+ mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
+ mpi_request.Header.PageNumber = 0;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_pcie_device_pg0 - obtain pcie device page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_pcie_device_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeDevicePage0_t *config_page,
+ u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE;
+ mpi_request.Header.PageVersion = MPI26_PCIEDEVICE0_PAGEVERSION;
+ mpi_request.Header.PageNumber = 0;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_pcie_iounit_pg1 - obtain pcie iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_pcie_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeIOUnitPage1_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_IO_UNIT;
+ mpi_request.Header.PageVersion = MPI26_PCIEIOUNITPAGE1_PAGEVERSION;
+ mpi_request.Header.PageNumber = 1;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_pcie_device_pg2 - obtain pcie device page 2
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_pcie_device_pg2(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26PCIeDevicePage2_t *config_page,
+ u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_PCIE_DEVICE;
+ mpi_request.Header.PageVersion = MPI26_PCIEDEVICE2_PAGEVERSION;
+ mpi_request.Header.PageNumber = 2;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_number_hba_phys - obtain number of phys on the host
+ * @ioc: per adapter object
+ * @num_phys: pointer returned with the number of phys
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc, u8 *num_phys)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ u16 ioc_status;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t config_page;
+
+ *num_phys = 0;
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
+ sizeof(Mpi2SasIOUnitPage0_t));
+ if (!r) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
+ *num_phys = config_page.NumPhys;
+ }
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_sas_iounit_pg1 - send sas iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_expander_pg0 - obtain expander page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_expander_pg1 - obtain expander page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number,
+ u16 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
+ (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_enclosure_pg0 - obtain enclosure page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_phy_pg0 - obtain phy page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_phy_pg1 - obtain phy page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_raid_volume_pg1 - obtain raid volume page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
+ u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_number_pds - obtain number of phys disk assigned to volume
+ * @ioc: per adapter object
+ * @handle: volume handle
+ * @num_pds: returns pds count
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u8 *num_pds)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ Mpi2RaidVolPage0_t config_page;
+ Mpi2ConfigReply_t mpi_reply;
+ int r;
+ u16 ioc_status;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ *num_pds = 0;
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
+ sizeof(Mpi2RaidVolPage0_t));
+ if (!r) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
+ *num_pds = config_page.NumPhysDisks;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_raid_volume_pg0 - obtain raid volume page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form,
+ u32 handle, u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_phys_disk_pg0 - obtain phys disk page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE
+ * @form_specific: specific to the form
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form,
+ u32 form_specific)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | form_specific);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_driver_trigger_pg0 - obtain driver trigger page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * _config_set_driver_trigger_pg0 - write driver trigger page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_set_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_update_driver_trigger_pg0 - update driver trigger page 0
+ * @ioc: per adapter object
+ * @trigger_flag: trigger type bit map
+ * @set: set ot clear trigger values
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+mpt3sas_config_update_driver_trigger_pg0(struct MPT3SAS_ADAPTER *ioc,
+ u16 trigger_flag, bool set)
+{
+ Mpi26DriverTriggerPage0_t tg_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ int rc;
+ u16 flags, ioc_status;
+
+ rc = mpt3sas_config_get_driver_trigger_pg0(ioc, &mpi_reply, &tg_pg0);
+ if (rc)
+ return rc;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg0, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return -EFAULT;
+ }
+
+ if (set)
+ flags = le16_to_cpu(tg_pg0.TriggerFlags) | trigger_flag;
+ else
+ flags = le16_to_cpu(tg_pg0.TriggerFlags) & ~trigger_flag;
+
+ tg_pg0.TriggerFlags = cpu_to_le16(flags);
+
+ rc = _config_set_driver_trigger_pg0(ioc, &mpi_reply, &tg_pg0);
+ if (rc)
+ return rc;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to update trigger pg0, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/**
+ * mpt3sas_config_get_driver_trigger_pg1 - obtain driver trigger page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * _config_set_driver_trigger_pg1 - write driver trigger page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_set_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_update_driver_trigger_pg1 - update driver trigger page 1
+ * @ioc: per adapter object
+ * @master_tg: Master trigger bit map
+ * @set: set ot clear trigger values
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_update_driver_trigger_pg1(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_MASTER_TRIGGER_T *master_tg, bool set)
+{
+ Mpi26DriverTriggerPage1_t tg_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ int rc;
+ u16 ioc_status;
+
+ rc = mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID, set);
+ if (rc)
+ return rc;
+
+ rc = mpt3sas_config_get_driver_trigger_pg1(ioc, &mpi_reply, &tg_pg1);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (set) {
+ tg_pg1.NumMasterTrigger = cpu_to_le16(1);
+ tg_pg1.MasterTriggers[0].MasterTriggerFlags = cpu_to_le32(
+ master_tg->MasterData);
+ } else {
+ tg_pg1.NumMasterTrigger = 0;
+ tg_pg1.MasterTriggers[0].MasterTriggerFlags = 0;
+ }
+
+ rc = _config_set_driver_trigger_pg1(ioc, &mpi_reply, &tg_pg1);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ rc = -EFAULT;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID, !set);
+
+ return rc;
+}
+
+/**
+ * mpt3sas_config_get_driver_trigger_pg2 - obtain driver trigger page 2
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage2_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 2;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE2_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * _config_set_driver_trigger_pg2 - write driver trigger page 2
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_set_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage2_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 2;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE2_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_update_driver_trigger_pg2 - update driver trigger page 2
+ * @ioc: per adapter object
+ * @event_tg: list of Event Triggers
+ * @set: set ot clear trigger values
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_update_driver_trigger_pg2(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_EVENT_TRIGGERS_T *event_tg, bool set)
+{
+ Mpi26DriverTriggerPage2_t tg_pg2;
+ Mpi2ConfigReply_t mpi_reply;
+ int rc, i, count;
+ u16 ioc_status;
+
+ rc = mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID, set);
+ if (rc)
+ return rc;
+
+ rc = mpt3sas_config_get_driver_trigger_pg2(ioc, &mpi_reply, &tg_pg2);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (set) {
+ count = event_tg->ValidEntries;
+ tg_pg2.NumMPIEventTrigger = cpu_to_le16(count);
+ for (i = 0; i < count; i++) {
+ tg_pg2.MPIEventTriggers[i].MPIEventCode =
+ cpu_to_le16(
+ event_tg->EventTriggerEntry[i].EventValue);
+ tg_pg2.MPIEventTriggers[i].MPIEventCodeSpecific =
+ cpu_to_le16(
+ event_tg->EventTriggerEntry[i].LogEntryQualifier);
+ }
+ } else {
+ tg_pg2.NumMPIEventTrigger = 0;
+ memset(&tg_pg2.MPIEventTriggers[0], 0,
+ NUM_VALID_ENTRIES * sizeof(
+ MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY));
+ }
+
+ rc = _config_set_driver_trigger_pg2(ioc, &mpi_reply, &tg_pg2);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ rc = -EFAULT;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID, !set);
+
+ return rc;
+}
+
+/**
+ * mpt3sas_config_get_driver_trigger_pg3 - obtain driver trigger page 3
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage3_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 3;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE3_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * _config_set_driver_trigger_pg3 - write driver trigger page 3
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_set_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage3_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 3;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE3_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_update_driver_trigger_pg3 - update driver trigger page 3
+ * @ioc: per adapter object
+ * @scsi_tg: scsi trigger list
+ * @set: set ot clear trigger values
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_update_driver_trigger_pg3(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_SCSI_TRIGGERS_T *scsi_tg, bool set)
+{
+ Mpi26DriverTriggerPage3_t tg_pg3;
+ Mpi2ConfigReply_t mpi_reply;
+ int rc, i, count;
+ u16 ioc_status;
+
+ rc = mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID, set);
+ if (rc)
+ return rc;
+
+ rc = mpt3sas_config_get_driver_trigger_pg3(ioc, &mpi_reply, &tg_pg3);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg3, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return -EFAULT;
+ }
+
+ if (set) {
+ count = scsi_tg->ValidEntries;
+ tg_pg3.NumSCSISenseTrigger = cpu_to_le16(count);
+ for (i = 0; i < count; i++) {
+ tg_pg3.SCSISenseTriggers[i].ASCQ =
+ scsi_tg->SCSITriggerEntry[i].ASCQ;
+ tg_pg3.SCSISenseTriggers[i].ASC =
+ scsi_tg->SCSITriggerEntry[i].ASC;
+ tg_pg3.SCSISenseTriggers[i].SenseKey =
+ scsi_tg->SCSITriggerEntry[i].SenseKey;
+ }
+ } else {
+ tg_pg3.NumSCSISenseTrigger = 0;
+ memset(&tg_pg3.SCSISenseTriggers[0], 0,
+ NUM_VALID_ENTRIES * sizeof(
+ MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY));
+ }
+
+ rc = _config_set_driver_trigger_pg3(ioc, &mpi_reply, &tg_pg3);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg3, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return -EFAULT;
+ }
+
+ return 0;
+out:
+ mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID, !set);
+
+ return rc;
+}
+
+/**
+ * mpt3sas_config_get_driver_trigger_pg4 - obtain driver trigger page 4
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage4_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 4;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE4_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * _config_set_driver_trigger_pg4 - write driver trigger page 4
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_set_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi26DriverTriggerPage4_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType =
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER;
+ mpi_request.Header.PageNumber = 4;
+ mpi_request.Header.PageVersion = MPI26_DRIVER_TRIGGER_PAGE4_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_update_driver_trigger_pg4 - update driver trigger page 4
+ * @ioc: per adapter object
+ * @mpi_tg: mpi trigger list
+ * @set: set ot clear trigger values
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_update_driver_trigger_pg4(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_MPI_TRIGGERS_T *mpi_tg, bool set)
+{
+ Mpi26DriverTriggerPage4_t tg_pg4;
+ Mpi2ConfigReply_t mpi_reply;
+ int rc, i, count;
+ u16 ioc_status;
+
+ rc = mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID, set);
+ if (rc)
+ return rc;
+
+ rc = mpt3sas_config_get_driver_trigger_pg4(ioc, &mpi_reply, &tg_pg4);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg4, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (set) {
+ count = mpi_tg->ValidEntries;
+ tg_pg4.NumIOCStatusLogInfoTrigger = cpu_to_le16(count);
+ for (i = 0; i < count; i++) {
+ tg_pg4.IOCStatusLoginfoTriggers[i].IOCStatus =
+ cpu_to_le16(mpi_tg->MPITriggerEntry[i].IOCStatus);
+ tg_pg4.IOCStatusLoginfoTriggers[i].LogInfo =
+ cpu_to_le32(mpi_tg->MPITriggerEntry[i].IocLogInfo);
+ }
+ } else {
+ tg_pg4.NumIOCStatusLogInfoTrigger = 0;
+ memset(&tg_pg4.IOCStatusLoginfoTriggers[0], 0,
+ NUM_VALID_ENTRIES * sizeof(
+ MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY));
+ }
+
+ rc = _config_set_driver_trigger_pg4(ioc, &mpi_reply, &tg_pg4);
+ if (rc)
+ goto out;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dcprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg4, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ rc = -EFAULT;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ mpt3sas_config_update_driver_trigger_pg0(ioc,
+ MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID, !set);
+
+ return rc;
+}
+
+/**
+ * mpt3sas_config_get_volume_handle - returns volume handle for give hidden
+ * raid components
+ * @ioc: per adapter object
+ * @pd_handle: phys disk handle
+ * @volume_handle: volume handle
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle,
+ u16 *volume_handle)
+{
+ Mpi2RaidConfigurationPage0_t *config_page = NULL;
+ Mpi2ConfigRequest_t mpi_request;
+ Mpi2ConfigReply_t mpi_reply;
+ int r, i, config_page_sz;
+ u16 ioc_status;
+ int config_num;
+ u16 element_type;
+ u16 phys_disk_dev_handle;
+
+ *volume_handle = 0;
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG;
+ mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION;
+ mpi_request.Header.PageNumber = 0;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4);
+ config_page = kmalloc(config_page_sz, GFP_KERNEL);
+ if (!config_page) {
+ r = -1;
+ goto out;
+ }
+
+ config_num = 0xff;
+ while (1) {
+ mpi_request.PageAddress = cpu_to_le32(config_num +
+ MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ config_page_sz);
+ if (r)
+ goto out;
+ r = -1;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ for (i = 0; i < config_page->NumElements; i++) {
+ element_type = le16_to_cpu(config_page->
+ ConfigElement[i].ElementFlags) &
+ MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE;
+ if (element_type ==
+ MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT ||
+ element_type ==
+ MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT) {
+ phys_disk_dev_handle =
+ le16_to_cpu(config_page->ConfigElement[i].
+ PhysDiskDevHandle);
+ if (phys_disk_dev_handle == pd_handle) {
+ *volume_handle =
+ le16_to_cpu(config_page->
+ ConfigElement[i].VolDevHandle);
+ r = 0;
+ goto out;
+ }
+ } else if (element_type ==
+ MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT) {
+ *volume_handle = 0;
+ r = 0;
+ goto out;
+ }
+ }
+ config_num = config_page->ConfigNum;
+ }
+ out:
+ kfree(config_page);
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_volume_wwid - returns wwid given the volume handle
+ * @ioc: per adapter object
+ * @volume_handle: volume handle
+ * @wwid: volume wwid
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, u16 volume_handle,
+ u64 *wwid)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2RaidVolPage1_t raid_vol_pg1;
+
+ *wwid = 0;
+ if (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE,
+ volume_handle))) {
+ *wwid = le64_to_cpu(raid_vol_pg1.WWID);
+ return 0;
+ } else
+ return -1;
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
new file mode 100644
index 000000000000..3b951589feeb
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
@@ -0,0 +1,4522 @@
+/*
+ * Management Module Support for MPT (Message Passing Technology) based
+ * controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.c
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/compat.h>
+#include <linux/poll.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include "mpt3sas_base.h"
+#include "mpt3sas_ctl.h"
+
+
+static struct fasync_struct *async_queue;
+static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait);
+
+
+/**
+ * enum block_state - blocking state
+ * @NON_BLOCKING: non blocking
+ * @BLOCKING: blocking
+ *
+ * These states are for ioctls that need to wait for a response
+ * from firmware, so they probably require sleep.
+ */
+enum block_state {
+ NON_BLOCKING,
+ BLOCKING,
+};
+
+/**
+ * _ctl_display_some_debug - debug routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @calling_function_name: string pass from calling function
+ * @mpi_reply: reply message frame
+ * Context: none.
+ *
+ * Function for displaying debug info helpful when debugging issues
+ * in this module.
+ */
+static void
+_ctl_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
+{
+ Mpi2ConfigRequest_t *mpi_request;
+ char *desc = NULL;
+
+ if (!(ioc->logging_level & MPT_DEBUG_IOCTL))
+ return;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ switch (mpi_request->Function) {
+ case MPI2_FUNCTION_SCSI_IO_REQUEST:
+ {
+ Mpi2SCSIIORequest_t *scsi_request =
+ (Mpi2SCSIIORequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "scsi_io, cmd(0x%02x), cdb_len(%d)",
+ scsi_request->CDB.CDB32[0],
+ le16_to_cpu(scsi_request->IoFlags) & 0xF);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ desc = "task_mgmt";
+ break;
+ case MPI2_FUNCTION_IOC_INIT:
+ desc = "ioc_init";
+ break;
+ case MPI2_FUNCTION_IOC_FACTS:
+ desc = "ioc_facts";
+ break;
+ case MPI2_FUNCTION_CONFIG:
+ {
+ Mpi2ConfigRequest_t *config_request =
+ (Mpi2ConfigRequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "config, type(0x%02x), ext_type(0x%02x), number(%d)",
+ (config_request->Header.PageType &
+ MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType,
+ config_request->Header.PageNumber);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_PORT_FACTS:
+ desc = "port_facts";
+ break;
+ case MPI2_FUNCTION_PORT_ENABLE:
+ desc = "port_enable";
+ break;
+ case MPI2_FUNCTION_EVENT_NOTIFICATION:
+ desc = "event_notification";
+ break;
+ case MPI2_FUNCTION_FW_DOWNLOAD:
+ desc = "fw_download";
+ break;
+ case MPI2_FUNCTION_FW_UPLOAD:
+ desc = "fw_upload";
+ break;
+ case MPI2_FUNCTION_RAID_ACTION:
+ desc = "raid_action";
+ break;
+ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
+ {
+ Mpi2SCSIIORequest_t *scsi_request =
+ (Mpi2SCSIIORequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "raid_pass, cmd(0x%02x), cdb_len(%d)",
+ scsi_request->CDB.CDB32[0],
+ le16_to_cpu(scsi_request->IoFlags) & 0xF);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ desc = "sas_iounit_cntl";
+ break;
+ case MPI2_FUNCTION_SATA_PASSTHROUGH:
+ desc = "sata_pass";
+ break;
+ case MPI2_FUNCTION_DIAG_BUFFER_POST:
+ desc = "diag_buffer_post";
+ break;
+ case MPI2_FUNCTION_DIAG_RELEASE:
+ desc = "diag_release";
+ break;
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ desc = "smp_passthrough";
+ break;
+ case MPI2_FUNCTION_TOOLBOX:
+ desc = "toolbox";
+ break;
+ case MPI2_FUNCTION_NVME_ENCAPSULATED:
+ desc = "nvme_encapsulated";
+ break;
+ case MPI2_FUNCTION_MCTP_PASSTHROUGH:
+ desc = "mctp_passthrough";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ ioc_info(ioc, "%s: %s, smid(%d)\n", calling_function_name, desc, smid);
+
+ if (!mpi_reply)
+ return;
+
+ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
+ ioc_info(ioc, "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
+ Mpi2SCSIIOReply_t *scsi_reply =
+ (Mpi2SCSIIOReply_t *)mpi_reply;
+ struct _sas_device *sas_device = NULL;
+ struct _pcie_device *pcie_device = NULL;
+
+ sas_device = mpt3sas_get_sdev_by_handle(ioc,
+ le16_to_cpu(scsi_reply->DevHandle));
+ if (sas_device) {
+ ioc_warn(ioc, "\tsas_address(0x%016llx), phy(%d)\n",
+ (u64)sas_device->sas_address,
+ sas_device->phy);
+ ioc_warn(ioc, "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
+ (u64)sas_device->enclosure_logical_id,
+ sas_device->slot);
+ sas_device_put(sas_device);
+ }
+ if (!sas_device) {
+ pcie_device = mpt3sas_get_pdev_by_handle(ioc,
+ le16_to_cpu(scsi_reply->DevHandle));
+ if (pcie_device) {
+ ioc_warn(ioc, "\tWWID(0x%016llx), port(%d)\n",
+ (unsigned long long)pcie_device->wwid,
+ pcie_device->port_num);
+ if (pcie_device->enclosure_handle != 0)
+ ioc_warn(ioc, "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot);
+ pcie_device_put(pcie_device);
+ }
+ }
+ if (scsi_reply->SCSIState || scsi_reply->SCSIStatus)
+ ioc_info(ioc, "\tscsi_state(0x%02x), scsi_status(0x%02x)\n",
+ scsi_reply->SCSIState,
+ scsi_reply->SCSIStatus);
+ }
+}
+
+/**
+ * mpt3sas_ctl_done - ctl module completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using ioc->ctl_cb_idx.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+ Mpi2SCSIIOReply_t *scsiio_reply;
+ Mpi26NVMeEncapsulatedErrorReply_t *nvme_error_reply;
+ const void *sense_data;
+ u32 sz;
+
+ if (ioc->ctl_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->ctl_cmds.smid != smid)
+ return 1;
+ ioc->ctl_cmds.status |= MPT3_CMD_COMPLETE;
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc->ctl_cmds.status |= MPT3_CMD_REPLY_VALID;
+ /* get sense data */
+ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_reply->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
+ scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply;
+ if (scsiio_reply->SCSIState &
+ MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
+ le32_to_cpu(scsiio_reply->SenseCount));
+ sense_data = mpt3sas_base_get_sense_buffer(ioc,
+ smid);
+ memcpy(ioc->ctl_cmds.sense, sense_data, sz);
+ }
+ }
+ /*
+ * Get Error Response data for NVMe device. The ctl_cmds.sense
+ * buffer is used to store the Error Response data.
+ */
+ if (mpi_reply->Function == MPI2_FUNCTION_NVME_ENCAPSULATED) {
+ nvme_error_reply =
+ (Mpi26NVMeEncapsulatedErrorReply_t *)mpi_reply;
+ sz = min_t(u32, NVME_ERROR_RESPONSE_SIZE,
+ le16_to_cpu(nvme_error_reply->ErrorResponseCount));
+ sense_data = mpt3sas_base_get_sense_buffer(ioc, smid);
+ memcpy(ioc->ctl_cmds.sense, sense_data, sz);
+ }
+ }
+
+ _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply);
+ ioc->ctl_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->ctl_cmds.done);
+ return 1;
+}
+
+/**
+ * _ctl_check_event_type - determines when an event needs logging
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * The bitmask in ioc->event_type[] indicates which events should be
+ * be saved in the driver event_log. This bitmask is set by application.
+ *
+ * Return: 1 when event should be captured, or zero means no match.
+ */
+static int
+_ctl_check_event_type(struct MPT3SAS_ADAPTER *ioc, u16 event)
+{
+ u16 i;
+ u32 desired_event;
+
+ if (event >= 128 || !event || !ioc->event_log)
+ return 0;
+
+ desired_event = (1 << (event % 32));
+ if (!desired_event)
+ desired_event = 1;
+ i = event / 32;
+ return desired_event & ioc->event_type[i];
+}
+
+/**
+ * mpt3sas_ctl_add_to_event_log - add event
+ * @ioc: per adapter object
+ * @mpi_reply: reply message frame
+ */
+void
+mpt3sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply)
+{
+ struct MPT3_IOCTL_EVENTS *event_log;
+ u16 event;
+ int i;
+ u32 sz, event_data_sz;
+ u8 send_aen = 0;
+
+ if (!ioc->event_log)
+ return;
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ if (_ctl_check_event_type(ioc, event)) {
+
+ /* insert entry into circular event_log */
+ i = ioc->event_context % MPT3SAS_CTL_EVENT_LOG_SIZE;
+ event_log = ioc->event_log;
+ event_log[i].event = event;
+ event_log[i].context = ioc->event_context++;
+
+ event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4;
+ sz = min_t(u32, event_data_sz, MPT3_EVENT_DATA_SIZE);
+ memset(event_log[i].data, 0, MPT3_EVENT_DATA_SIZE);
+ memcpy(event_log[i].data, mpi_reply->EventData, sz);
+ send_aen = 1;
+ }
+
+ /* This aen_event_read_flag flag is set until the
+ * application has read the event log.
+ * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify.
+ */
+ if (event == MPI2_EVENT_LOG_ENTRY_ADDED ||
+ (send_aen && !ioc->aen_event_read_flag)) {
+ ioc->aen_event_read_flag = 1;
+ wake_up_interruptible(&ctl_poll_wait);
+ if (async_queue)
+ kill_fasync(&async_queue, SIGIO, POLL_IN);
+ }
+}
+
+/**
+ * mpt3sas_ctl_event_callback - firmware event handler (called at ISR time)
+ * @ioc: per adapter object
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt.
+ *
+ * This function merely adds a new work task into ioc->firmware_event_thread.
+ * The tasks are worked from _firmware_event_work in user context.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
+ u32 reply)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply)
+ mpt3sas_ctl_add_to_event_log(ioc, mpi_reply);
+ return 1;
+}
+
+/**
+ * _ctl_verify_adapter - validates ioc_number passed from application
+ * @ioc_number: ?
+ * @iocpp: The ioc pointer is returned in this.
+ * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device &
+ * MPI25_VERSION | MPI26_VERSION for mpt3ctl ioctl device.
+ *
+ * Return: (-1) means error, else ioc_number.
+ */
+static int
+_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp,
+ int mpi_version)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+ int version = 0;
+ /* global ioc lock to protect controller on list operations */
+ spin_lock(&gioc_lock);
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
+ if (ioc->id != ioc_number)
+ continue;
+ /* Check whether this ioctl command is from right
+ * ioctl device or not, if not continue the search.
+ */
+ version = ioc->hba_mpi_version_belonged;
+ /* MPI25_VERSION and MPI26_VERSION uses same ioctl
+ * device.
+ */
+ if (mpi_version == (MPI25_VERSION | MPI26_VERSION)) {
+ if ((version == MPI25_VERSION) ||
+ (version == MPI26_VERSION))
+ goto out;
+ else
+ continue;
+ } else {
+ if (version != mpi_version)
+ continue;
+ }
+out:
+ spin_unlock(&gioc_lock);
+ *iocpp = ioc;
+ return ioc_number;
+ }
+ spin_unlock(&gioc_lock);
+ *iocpp = NULL;
+ return -1;
+}
+
+/**
+ * mpt3sas_ctl_pre_reset_handler - reset callback handler (for ctl)
+ * @ioc: per adapter object
+ *
+ * The handler for doing any required cleanup or initialization.
+ */
+void mpt3sas_ctl_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i;
+ u8 issue_reset;
+
+ dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_PRE_RESET\n", __func__));
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!(ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED))
+ continue;
+ if ((ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ continue;
+
+ /*
+ * add a log message to indicate the release
+ */
+ ioc_info(ioc,
+ "%s: Releasing the trace buffer due to adapter reset.",
+ __func__);
+ ioc->htb_rel.buffer_rel_condition =
+ MPT3_DIAG_BUFFER_REL_TRIGGER;
+ mpt3sas_send_diag_release(ioc, i, &issue_reset);
+ }
+}
+
+/**
+ * mpt3sas_ctl_clear_outstanding_ioctls - clears outstanding ioctl cmd.
+ * @ioc: per adapter object
+ *
+ * The handler for doing any required cleanup or initialization.
+ */
+void mpt3sas_ctl_clear_outstanding_ioctls(struct MPT3SAS_ADAPTER *ioc)
+{
+ dtmprintk(ioc,
+ ioc_info(ioc, "%s: clear outstanding ioctl cmd\n", __func__));
+ if (ioc->ctl_cmds.status & MPT3_CMD_PENDING) {
+ ioc->ctl_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->ctl_cmds.smid);
+ complete(&ioc->ctl_cmds.done);
+ }
+}
+
+/**
+ * mpt3sas_ctl_reset_done_handler - reset callback handler (for ctl)
+ * @ioc: per adapter object
+ *
+ * The handler for doing any required cleanup or initialization.
+ */
+void mpt3sas_ctl_reset_done_handler(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i;
+
+ dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_DONE_RESET\n", __func__));
+
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!(ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED))
+ continue;
+ if ((ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ continue;
+ ioc->diag_buffer_status[i] |=
+ MPT3_DIAG_BUFFER_IS_DIAG_RESET;
+ }
+}
+
+/**
+ * _ctl_fasync -
+ * @fd: ?
+ * @filep: ?
+ * @mode: ?
+ *
+ * Called when application request fasyn callback handler.
+ */
+static int
+_ctl_fasync(int fd, struct file *filep, int mode)
+{
+ return fasync_helper(fd, filep, mode, &async_queue);
+}
+
+/**
+ * _ctl_poll -
+ * @filep: ?
+ * @wait: ?
+ *
+ */
+static __poll_t
+_ctl_poll(struct file *filep, poll_table *wait)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+
+ poll_wait(filep, &ctl_poll_wait, wait);
+
+ /* global ioc lock to protect controller on list operations */
+ spin_lock(&gioc_lock);
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
+ if (ioc->aen_event_read_flag) {
+ spin_unlock(&gioc_lock);
+ return EPOLLIN | EPOLLRDNORM;
+ }
+ }
+ spin_unlock(&gioc_lock);
+ return 0;
+}
+
+/**
+ * _ctl_set_task_mid - assign an active smid to tm request
+ * @ioc: per adapter object
+ * @karg: (struct mpt3_ioctl_command)
+ * @tm_request: pointer to mf from user space
+ *
+ * Return: 0 when an smid if found, else fail.
+ * during failure, the reply frame is filled.
+ */
+static int
+_ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg,
+ Mpi2SCSITaskManagementRequest_t *tm_request)
+{
+ bool found = false;
+ u16 smid;
+ u16 handle;
+ struct scsi_cmnd *scmd;
+ struct MPT3SAS_DEVICE *priv_data;
+ Mpi2SCSITaskManagementReply_t *tm_reply;
+ u32 sz;
+ u32 lun;
+ char *desc = NULL;
+
+ if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
+ desc = "abort_task";
+ else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
+ desc = "query_task";
+ else
+ return 0;
+
+ lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN);
+
+ handle = le16_to_cpu(tm_request->DevHandle);
+ for (smid = ioc->scsiio_depth; smid && !found; smid--) {
+ struct scsiio_tracker *st;
+ __le16 task_mid;
+
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (!scmd)
+ continue;
+ if (lun != scmd->device->lun)
+ continue;
+ priv_data = scmd->device->hostdata;
+ if (priv_data->sas_target == NULL)
+ continue;
+ if (priv_data->sas_target->handle != handle)
+ continue;
+ st = scsi_cmd_priv(scmd);
+
+ /*
+ * If the given TaskMID from the user space is zero, then the
+ * first outstanding smid will be picked up. Otherwise,
+ * targeted smid will be the one.
+ */
+ task_mid = cpu_to_le16(st->smid);
+ if (!tm_request->TaskMID)
+ tm_request->TaskMID = task_mid;
+ found = tm_request->TaskMID == task_mid;
+ }
+
+ if (!found) {
+ dctlprintk(ioc,
+ ioc_info(ioc, "%s: handle(0x%04x), lun(%d), no active mid!!\n",
+ desc, le16_to_cpu(tm_request->DevHandle),
+ lun));
+ tm_reply = ioc->ctl_cmds.reply;
+ tm_reply->DevHandle = tm_request->DevHandle;
+ tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ tm_reply->TaskType = tm_request->TaskType;
+ tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4;
+ tm_reply->VP_ID = tm_request->VP_ID;
+ tm_reply->VF_ID = tm_request->VF_ID;
+ sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz);
+ if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply,
+ sz))
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ return 1;
+ }
+
+ dctlprintk(ioc,
+ ioc_info(ioc, "%s: handle(0x%04x), lun(%d), task_mid(%d)\n",
+ desc, le16_to_cpu(tm_request->DevHandle), lun,
+ le16_to_cpu(tm_request->TaskMID)));
+ return 0;
+}
+
+/**
+ * _ctl_send_mctp_passthru_req - Send an MCTP passthru request
+ * @ioc: per adapter object
+ * @mctp_passthru_req: MPI mctp passhthru request from caller
+ * @psge: pointer to the H2DSGL
+ * @data_out_dma: DMA buffer for H2D SGL
+ * @data_out_sz: H2D length
+ * @data_in_dma: DMA buffer for D2H SGL
+ * @data_in_sz: D2H length
+ * @smid: SMID to submit the request
+ *
+ */
+static void
+_ctl_send_mctp_passthru_req(
+ struct MPT3SAS_ADAPTER *ioc,
+ Mpi26MctpPassthroughRequest_t *mctp_passthru_req, void *psge,
+ dma_addr_t data_out_dma, int data_out_sz,
+ dma_addr_t data_in_dma, int data_in_sz,
+ u16 smid)
+{
+ mctp_passthru_req->H2DLength = data_out_sz;
+ mctp_passthru_req->D2HLength = data_in_sz;
+
+ /* Build the H2D SGL from the data out buffer */
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, 0, 0);
+
+ psge += ioc->sge_size_ieee;
+
+ /* Build the D2H SGL for the data in buffer */
+ ioc->build_sg(ioc, psge, 0, 0, data_in_dma, data_in_sz);
+
+ ioc->put_smid_default(ioc, smid);
+}
+
+/**
+ * _ctl_do_mpt_command - main handler for MPT3COMMAND opcode
+ * @ioc: per adapter object
+ * @karg: (struct mpt3_ioctl_command)
+ * @mf: pointer to mf in user space
+ */
+static long
+_ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
+ void __user *mf)
+{
+ MPI2RequestHeader_t *mpi_request = NULL, *request;
+ MPI2DefaultReply_t *mpi_reply;
+ Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL;
+ struct _pcie_device *pcie_device = NULL;
+ u16 smid;
+ unsigned long timeout;
+ u8 issue_reset;
+ u32 sz, sz_arg;
+ void *psge;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma = 0;
+ size_t data_out_sz = 0;
+ void *data_in = NULL;
+ dma_addr_t data_in_dma = 0;
+ size_t data_in_sz = 0;
+ long ret;
+ u16 device_handle = MPT3SAS_INVALID_DEVICE_HANDLE;
+ int tm_ret;
+
+ issue_reset = 0;
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ ret = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (ret)
+ goto out;
+
+ mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL);
+ if (!mpi_request) {
+ ioc_err(ioc, "%s: failed obtaining a memory for mpi_request\n",
+ __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Check for overflow and wraparound */
+ if (karg.data_sge_offset * 4 > ioc->request_sz ||
+ karg.data_sge_offset > (UINT_MAX / 4)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* copy in request message frame from user */
+ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__, __LINE__,
+ __func__);
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ ret = -EAGAIN;
+ goto out;
+ }
+ } else {
+ /* Use first reserved smid for passthrough ioctls */
+ smid = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT + 1;
+ }
+
+ ret = 0;
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(request, 0, ioc->request_sz);
+ memcpy(request, mpi_request, karg.data_sge_offset*4);
+ ioc->ctl_cmds.smid = smid;
+ data_out_sz = karg.data_out_size;
+ data_in_sz = karg.data_in_size;
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
+ mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT ||
+ mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH ||
+ mpi_request->Function == MPI2_FUNCTION_NVME_ENCAPSULATED) {
+
+ device_handle = le16_to_cpu(mpi_request->FunctionDependent1);
+ if (!device_handle || (device_handle >
+ ioc->facts.MaxDevHandle)) {
+ ret = -EINVAL;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ /* obtain dma-able memory for data transfer */
+ if (data_out_sz) /* WRITE */ {
+ data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz,
+ &data_out_dma, GFP_KERNEL);
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ if (copy_from_user(data_out, karg.data_out_buf_ptr,
+ data_out_sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -EFAULT;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ if (data_in_sz) /* READ */ {
+ data_in = dma_alloc_coherent(&ioc->pdev->dev, data_in_sz,
+ &data_in_dma, GFP_KERNEL);
+ if (!data_in) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ psge = (void *)request + (karg.data_sge_offset*4);
+
+ /* send command to firmware */
+ _ctl_display_some_debug(ioc, smid, "ctl_request", NULL);
+
+ init_completion(&ioc->ctl_cmds.done);
+ switch (mpi_request->Function) {
+ case MPI2_FUNCTION_MCTP_PASSTHROUGH:
+ {
+ Mpi26MctpPassthroughRequest_t *mctp_passthru_req =
+ (Mpi26MctpPassthroughRequest_t *)request;
+
+ if (!(ioc->facts.IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU)) {
+ ioc_err(ioc, "%s: MCTP Passthrough request not supported\n",
+ __func__);
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ _ctl_send_mctp_passthru_req(ioc, mctp_passthru_req, psge, data_out_dma,
+ data_out_sz, data_in_dma, data_in_sz, smid);
+ break;
+ }
+ case MPI2_FUNCTION_NVME_ENCAPSULATED:
+ {
+ nvme_encap_request = (Mpi26NVMeEncapsulatedRequest_t *)request;
+ if (!ioc->pcie_sg_lookup) {
+ dtmprintk(ioc, ioc_info(ioc,
+ "HBA doesn't support NVMe. Rejecting NVMe Encapsulated request.\n"
+ ));
+
+ if (ioc->logging_level & MPT_DEBUG_TM)
+ _debug_dump_mf(nvme_encap_request,
+ ioc->request_sz/4);
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+ /*
+ * Get the Physical Address of the sense buffer.
+ * Use Error Response buffer address field to hold the sense
+ * buffer address.
+ * Clear the internal sense buffer, which will potentially hold
+ * the Completion Queue Entry on return, or 0 if no Entry.
+ * Build the PRPs and set direction bits.
+ * Send the request.
+ */
+ nvme_encap_request->ErrorResponseBaseAddress =
+ cpu_to_le64(ioc->sense_dma & 0xFFFFFFFF00000000UL);
+ nvme_encap_request->ErrorResponseBaseAddress |=
+ cpu_to_le64(le32_to_cpu(
+ mpt3sas_base_get_sense_buffer_dma(ioc, smid)));
+ nvme_encap_request->ErrorResponseAllocationLength =
+ cpu_to_le16(NVME_ERROR_RESPONSE_SIZE);
+ memset(ioc->ctl_cmds.sense, 0, NVME_ERROR_RESPONSE_SIZE);
+ ioc->build_nvme_prp(ioc, smid, nvme_encap_request,
+ data_out_dma, data_out_sz, data_in_dma, data_in_sz);
+ if (test_bit(device_handle, ioc->device_remove_in_progress)) {
+ dtmprintk(ioc,
+ ioc_info(ioc, "handle(0x%04x): ioctl failed due to device removal in progress\n",
+ device_handle));
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+ mpt3sas_base_put_smid_nvme_encap(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SCSI_IO_REQUEST:
+ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
+ {
+ Mpi2SCSIIORequest_t *scsiio_request =
+ (Mpi2SCSIIORequest_t *)request;
+ scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
+ scsiio_request->SenseBufferLowAddress =
+ mpt3sas_base_get_sense_buffer_dma(ioc, smid);
+ memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE);
+ if (test_bit(device_handle, ioc->device_remove_in_progress)) {
+ dtmprintk(ioc,
+ ioc_info(ioc, "handle(0x%04x) :ioctl failed due to device removal in progress\n",
+ device_handle));
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)
+ ioc->put_smid_scsi_io(ioc, smid, device_handle);
+ else
+ ioc->put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ {
+ Mpi2SCSITaskManagementRequest_t *tm_request =
+ (Mpi2SCSITaskManagementRequest_t *)request;
+
+ dtmprintk(ioc,
+ ioc_info(ioc, "TASK_MGMT: handle(0x%04x), task_type(0x%02x)\n",
+ le16_to_cpu(tm_request->DevHandle),
+ tm_request->TaskType));
+ ioc->got_task_abort_from_ioctl = 1;
+ if (tm_request->TaskType ==
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK ||
+ tm_request->TaskType ==
+ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
+ if (_ctl_set_task_mid(ioc, &karg, tm_request)) {
+ mpt3sas_base_free_smid(ioc, smid);
+ ioc->got_task_abort_from_ioctl = 0;
+ goto out;
+ }
+ }
+ ioc->got_task_abort_from_ioctl = 0;
+
+ if (test_bit(device_handle, ioc->device_remove_in_progress)) {
+ dtmprintk(ioc,
+ ioc_info(ioc, "handle(0x%04x) :ioctl failed due to device removal in progress\n",
+ device_handle));
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+ mpt3sas_scsih_set_tm_flag(ioc, le16_to_cpu(
+ tm_request->DevHandle));
+ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ ioc->put_smid_hi_priority(ioc, smid, 0);
+ break;
+ }
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ {
+ Mpi2SmpPassthroughRequest_t *smp_request =
+ (Mpi2SmpPassthroughRequest_t *)mpi_request;
+ u8 *data;
+
+ if (!ioc->multipath_on_hba) {
+ /* ioc determines which port to use */
+ smp_request->PhysicalPort = 0xFF;
+ }
+ if (smp_request->PassthroughFlags &
+ MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE)
+ data = (u8 *)&smp_request->SGL;
+ else {
+ if (unlikely(data_out == NULL)) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+ data = data_out;
+ }
+
+ if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) {
+ ioc->ioc_link_reset_in_progress = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
+ data_in_sz);
+ ioc->put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SATA_PASSTHROUGH:
+ {
+ if (test_bit(device_handle, ioc->device_remove_in_progress)) {
+ dtmprintk(ioc,
+ ioc_info(ioc, "handle(0x%04x) :ioctl failed due to device removal in progress\n",
+ device_handle));
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
+ data_in_sz);
+ ioc->put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_FW_DOWNLOAD:
+ {
+ if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO) {
+ ioc_info(ioc, "Firmware download not supported for ATTO HBA.\n");
+ ret = -EPERM;
+ break;
+ }
+ fallthrough;
+ }
+ case MPI2_FUNCTION_FW_UPLOAD:
+ {
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
+ data_in_sz);
+ ioc->put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_TOOLBOX:
+ {
+ Mpi2ToolboxCleanRequest_t *toolbox_request =
+ (Mpi2ToolboxCleanRequest_t *)mpi_request;
+
+ if ((toolbox_request->Tool == MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL)
+ || (toolbox_request->Tool ==
+ MPI26_TOOLBOX_BACKEND_PCIE_LANE_MARGIN))
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ else if (toolbox_request->Tool ==
+ MPI2_TOOLBOX_MEMORY_MOVE_TOOL) {
+ Mpi2ToolboxMemMoveRequest_t *mem_move_request =
+ (Mpi2ToolboxMemMoveRequest_t *)request;
+ Mpi2SGESimple64_t tmp, *src = NULL, *dst = NULL;
+
+ ioc->build_sg_mpi(ioc, psge, data_out_dma,
+ data_out_sz, data_in_dma, data_in_sz);
+ if (data_out_sz && !data_in_sz) {
+ dst =
+ (Mpi2SGESimple64_t *)&mem_move_request->SGL;
+ src = (void *)dst + ioc->sge_size;
+
+ memcpy(&tmp, src, ioc->sge_size);
+ memcpy(src, dst, ioc->sge_size);
+ memcpy(dst, &tmp, ioc->sge_size);
+ }
+ if (ioc->logging_level & MPT_DEBUG_TM) {
+ ioc_info(ioc,
+ "Mpi2ToolboxMemMoveRequest_t request msg\n");
+ _debug_dump_mf(mem_move_request,
+ ioc->request_sz/4);
+ }
+ } else
+ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ ioc->put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ {
+ Mpi2SasIoUnitControlRequest_t *sasiounit_request =
+ (Mpi2SasIoUnitControlRequest_t *)mpi_request;
+
+ if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET
+ || sasiounit_request->Operation ==
+ MPI2_SAS_OP_PHY_LINK_RESET) {
+ ioc->ioc_link_reset_in_progress = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ /* drop to default case for posting the request */
+ }
+ fallthrough;
+ default:
+ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ ioc->put_smid_default(ioc, smid);
+ break;
+ }
+
+ if (karg.timeout < MPT3_IOCTL_DEFAULT_TIMEOUT)
+ timeout = MPT3_IOCTL_DEFAULT_TIMEOUT;
+ else
+ timeout = karg.timeout;
+ wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ);
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
+ Mpi2SCSITaskManagementRequest_t *tm_request =
+ (Mpi2SCSITaskManagementRequest_t *)mpi_request;
+ mpt3sas_scsih_clear_tm_flag(ioc, le16_to_cpu(
+ tm_request->DevHandle));
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT);
+ } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH ||
+ mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) &&
+ ioc->ioc_link_reset_in_progress) {
+ ioc->ioc_link_reset_in_progress = 0;
+ ioc->ignore_loginfos = 0;
+ }
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->ctl_cmds.status, mpi_request,
+ karg.data_sge_offset, issue_reset);
+ goto issue_host_reset;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+
+ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT &&
+ (ioc->logging_level & MPT_DEBUG_TM)) {
+ Mpi2SCSITaskManagementReply_t *tm_reply =
+ (Mpi2SCSITaskManagementReply_t *)mpi_reply;
+
+ ioc_info(ioc, "TASK_MGMT: IOCStatus(0x%04x), IOCLogInfo(0x%08x), TerminationCount(0x%08x)\n",
+ le16_to_cpu(tm_reply->IOCStatus),
+ le32_to_cpu(tm_reply->IOCLogInfo),
+ le32_to_cpu(tm_reply->TerminationCount));
+ }
+
+ /* copy out xdata to user */
+ if (data_in_sz) {
+ if (copy_to_user(karg.data_in_buf_ptr, data_in,
+ data_in_sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ /* copy out reply message frame to user */
+ if (karg.max_reply_bytes) {
+ sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz);
+ if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply,
+ sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ /* copy out sense/NVMe Error Response to user */
+ if (karg.max_sense_bytes && (mpi_request->Function ==
+ MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || mpi_request->Function ==
+ MPI2_FUNCTION_NVME_ENCAPSULATED)) {
+ if (karg.sense_data_ptr == NULL) {
+ ioc_info(ioc, "Response buffer provided by application is NULL; Response data will not be returned\n");
+ goto out;
+ }
+ sz_arg = (mpi_request->Function ==
+ MPI2_FUNCTION_NVME_ENCAPSULATED) ? NVME_ERROR_RESPONSE_SIZE :
+ SCSI_SENSE_BUFFERSIZE;
+ sz = min_t(u32, karg.max_sense_bytes, sz_arg);
+ if (copy_to_user(karg.sense_data_ptr, ioc->ctl_cmds.sense,
+ sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ issue_host_reset:
+ if (issue_reset) {
+ ret = -ENODATA;
+ if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
+ mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) {
+ ioc_info(ioc, "issue target reset: handle = (0x%04x)\n",
+ le16_to_cpu(mpi_request->FunctionDependent1));
+ mpt3sas_halt_firmware(ioc);
+ pcie_device = mpt3sas_get_pdev_by_handle(ioc,
+ le16_to_cpu(mpi_request->FunctionDependent1));
+ if (pcie_device && (!ioc->tm_custom_handling) &&
+ (!(mpt3sas_scsih_is_pcie_scsi_device(
+ pcie_device->device_info))))
+ tm_ret = mpt3sas_scsih_issue_locked_tm(ioc,
+ le16_to_cpu(mpi_request->FunctionDependent1),
+ 0, 0, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0,
+ 0, pcie_device->reset_timeout,
+ MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE);
+ else
+ tm_ret = mpt3sas_scsih_issue_locked_tm(ioc,
+ le16_to_cpu(mpi_request->FunctionDependent1),
+ 0, 0, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0,
+ 0, 30, MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET);
+
+ if (tm_ret != SUCCESS) {
+ ioc_info(ioc,
+ "target reset failed, issue hard reset: handle (0x%04x)\n",
+ le16_to_cpu(mpi_request->FunctionDependent1));
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ }
+ } else
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ }
+
+ out:
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+
+ /* free memory associated with sg buffers */
+ if (data_in)
+ dma_free_coherent(&ioc->pdev->dev, data_in_sz, data_in,
+ data_in_dma);
+
+ if (data_out)
+ dma_free_coherent(&ioc->pdev->dev, data_out_sz, data_out,
+ data_out_dma);
+
+ kfree(mpi_request);
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return ret;
+}
+
+/**
+ * _ctl_getiocinfo - main handler for MPT3IOCINFO opcode
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ */
+static long
+_ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_iocinfo karg;
+
+ dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
+ __func__));
+
+ memset(&karg, 0 , sizeof(karg));
+ if (ioc->pfacts)
+ karg.port_number = ioc->pfacts[0].PortNumber;
+ karg.hw_rev = ioc->pdev->revision;
+ karg.pci_id = ioc->pdev->device;
+ karg.subsystem_device = ioc->pdev->subsystem_device;
+ karg.subsystem_vendor = ioc->pdev->subsystem_vendor;
+ karg.pci_information.u.bits.bus = ioc->pdev->bus->number;
+ karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn);
+ karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn);
+ karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus);
+ karg.firmware_version = ioc->facts.FWVersion.Word;
+ strcpy(karg.driver_version, ioc->driver_name);
+ strcat(karg.driver_version, "-");
+ switch (ioc->hba_mpi_version_belonged) {
+ case MPI2_VERSION:
+ if (ioc->is_warpdrive)
+ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200;
+ else
+ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
+ strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION);
+ break;
+ case MPI25_VERSION:
+ case MPI26_VERSION:
+ if (ioc->is_gen35_ioc)
+ karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS35;
+ else
+ karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS3;
+ strcat(karg.driver_version, MPT3SAS_DRIVER_VERSION);
+ break;
+ }
+ karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
+
+ karg.driver_capability |= MPT3_IOCTL_IOCINFO_DRIVER_CAP_MCTP_PASSTHRU;
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventquery - main handler for MPT3EVENTQUERY opcode
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ */
+static long
+_ctl_eventquery(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_eventquery karg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
+ __func__));
+
+ karg.event_entries = MPT3SAS_CTL_EVENT_LOG_SIZE;
+ memcpy(karg.event_types, ioc->event_type,
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventenable - main handler for MPT3EVENTENABLE opcode
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ */
+static long
+_ctl_eventenable(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_eventenable karg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
+ __func__));
+
+ memcpy(ioc->event_type, karg.event_types,
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
+ mpt3sas_base_validate_event_type(ioc, ioc->event_type);
+
+ if (ioc->event_log)
+ return 0;
+ /* initialize event_log */
+ ioc->event_context = 0;
+ ioc->aen_event_read_flag = 0;
+ ioc->event_log = kcalloc(MPT3SAS_CTL_EVENT_LOG_SIZE,
+ sizeof(struct MPT3_IOCTL_EVENTS), GFP_KERNEL);
+ if (!ioc->event_log) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventreport - main handler for MPT3EVENTREPORT opcode
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ */
+static long
+_ctl_eventreport(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_eventreport karg;
+ u32 number_bytes, max_events, max;
+ struct mpt3_ioctl_eventreport __user *uarg = arg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
+ __func__));
+
+ number_bytes = karg.hdr.max_data_size -
+ sizeof(struct mpt3_ioctl_header);
+ max_events = number_bytes/sizeof(struct MPT3_IOCTL_EVENTS);
+ max = min_t(u32, MPT3SAS_CTL_EVENT_LOG_SIZE, max_events);
+
+ /* If fewer than 1 event is requested, there must have
+ * been some type of error.
+ */
+ if (!max || !ioc->event_log)
+ return -ENODATA;
+
+ number_bytes = max * sizeof(struct MPT3_IOCTL_EVENTS);
+ if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ /* reset flag so SIGIO can restart */
+ ioc->aen_event_read_flag = 0;
+ return 0;
+}
+
+/**
+ * _ctl_do_reset - main handler for MPT3HARDRESET opcode
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ */
+static long
+_ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_diag_reset karg;
+ int retval;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery ||
+ ioc->is_driver_loading)
+ return -EAGAIN;
+
+ dctlprintk(ioc, ioc_info(ioc, "%s: enter\n",
+ __func__));
+
+ ioc->reset_from_user = 1;
+ retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ ioc_info(ioc,
+ "Ioctl: host reset: %s\n", ((!retval) ? "SUCCESS" : "FAILED"));
+ return 0;
+}
+
+/**
+ * _ctl_btdh_search_sas_device - searching for sas device
+ * @ioc: per adapter object
+ * @btdh: btdh ioctl payload
+ */
+static int
+_ctl_btdh_search_sas_device(struct MPT3SAS_ADAPTER *ioc,
+ struct mpt3_ioctl_btdh_mapping *btdh)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ int rc = 0;
+
+ if (list_empty(&ioc->sas_device_list))
+ return rc;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
+ btdh->handle == sas_device->handle) {
+ btdh->bus = sas_device->channel;
+ btdh->id = sas_device->id;
+ rc = 1;
+ goto out;
+ } else if (btdh->bus == sas_device->channel && btdh->id ==
+ sas_device->id && btdh->handle == 0xFFFF) {
+ btdh->handle = sas_device->handle;
+ rc = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_btdh_search_pcie_device - searching for pcie device
+ * @ioc: per adapter object
+ * @btdh: btdh ioctl payload
+ */
+static int
+_ctl_btdh_search_pcie_device(struct MPT3SAS_ADAPTER *ioc,
+ struct mpt3_ioctl_btdh_mapping *btdh)
+{
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+ int rc = 0;
+
+ if (list_empty(&ioc->pcie_device_list))
+ return rc;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) {
+ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
+ btdh->handle == pcie_device->handle) {
+ btdh->bus = pcie_device->channel;
+ btdh->id = pcie_device->id;
+ rc = 1;
+ goto out;
+ } else if (btdh->bus == pcie_device->channel && btdh->id ==
+ pcie_device->id && btdh->handle == 0xFFFF) {
+ btdh->handle = pcie_device->handle;
+ rc = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_btdh_search_raid_device - searching for raid device
+ * @ioc: per adapter object
+ * @btdh: btdh ioctl payload
+ */
+static int
+_ctl_btdh_search_raid_device(struct MPT3SAS_ADAPTER *ioc,
+ struct mpt3_ioctl_btdh_mapping *btdh)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ int rc = 0;
+
+ if (list_empty(&ioc->raid_device_list))
+ return rc;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
+ btdh->handle == raid_device->handle) {
+ btdh->bus = raid_device->channel;
+ btdh->id = raid_device->id;
+ rc = 1;
+ goto out;
+ } else if (btdh->bus == raid_device->channel && btdh->id ==
+ raid_device->id && btdh->handle == 0xFFFF) {
+ btdh->handle = raid_device->handle;
+ rc = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_btdh_mapping - main handler for MPT3BTDHMAPPING opcode
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ */
+static long
+_ctl_btdh_mapping(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_btdh_mapping karg;
+ int rc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s\n",
+ __func__));
+
+ rc = _ctl_btdh_search_sas_device(ioc, &karg);
+ if (!rc)
+ rc = _ctl_btdh_search_pcie_device(ioc, &karg);
+ if (!rc)
+ _ctl_btdh_search_raid_device(ioc, &karg);
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_diag_capability - return diag buffer capability
+ * @ioc: per adapter object
+ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
+ *
+ * returns 1 when diag buffer support is enabled in firmware
+ */
+static u8
+_ctl_diag_capability(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type)
+{
+ u8 rc = 0;
+
+ switch (buffer_type) {
+ case MPI2_DIAG_BUF_TYPE_TRACE:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER)
+ rc = 1;
+ break;
+ case MPI2_DIAG_BUF_TYPE_SNAPSHOT:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
+ rc = 1;
+ break;
+ case MPI2_DIAG_BUF_TYPE_EXTENDED:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
+ rc = 1;
+ }
+
+ return rc;
+}
+
+/**
+ * _ctl_diag_get_bufftype - return diag buffer type
+ * either TRACE, SNAPSHOT, or EXTENDED
+ * @ioc: per adapter object
+ * @unique_id: specifies the unique_id for the buffer
+ *
+ * returns MPT3_DIAG_UID_NOT_FOUND if the id not found
+ */
+static u8
+_ctl_diag_get_bufftype(struct MPT3SAS_ADAPTER *ioc, u32 unique_id)
+{
+ u8 index;
+
+ for (index = 0; index < MPI2_DIAG_BUF_TYPE_COUNT; index++) {
+ if (ioc->unique_id[index] == unique_id)
+ return index;
+ }
+
+ return MPT3_DIAG_UID_NOT_FOUND;
+}
+
+/**
+ * _ctl_diag_register_2 - wrapper for registering diag buffer support
+ * @ioc: per adapter object
+ * @diag_register: the diag_register struct passed in from user space
+ *
+ */
+static long
+_ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc,
+ struct mpt3_diag_register *diag_register)
+{
+ int rc, i;
+ void *request_data = NULL;
+ dma_addr_t request_data_dma;
+ u32 request_data_sz = 0;
+ Mpi2DiagBufferPostRequest_t *mpi_request;
+ Mpi2DiagBufferPostReply_t *mpi_reply;
+ u8 buffer_type;
+ u16 smid;
+ u16 ioc_status;
+ u32 ioc_state;
+ u8 issue_reset = 0;
+
+ dctlprintk(ioc, ioc_info(ioc, "%s\n",
+ __func__));
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ ioc_err(ioc, "%s: failed due to ioc not operational\n",
+ __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ buffer_type = diag_register->buffer_type;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if (diag_register->unique_id == 0) {
+ ioc_err(ioc,
+ "%s: Invalid UID(0x%08x), buffer_type(0x%02x)\n", __func__,
+ diag_register->unique_id, buffer_type);
+ return -EINVAL;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_APP_OWNED) &&
+ !(ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED)) {
+ ioc_err(ioc,
+ "%s: buffer_type(0x%02x) is already registered by application with UID(0x%08x)\n",
+ __func__, buffer_type, ioc->unique_id[buffer_type]);
+ return -EINVAL;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) {
+ /*
+ * If driver posts buffer initially, then an application wants
+ * to Register that buffer (own it) without Releasing first,
+ * the application Register command MUST have the same buffer
+ * type and size in the Register command (obtained from the
+ * Query command). Otherwise that Register command will be
+ * failed. If the application has released the buffer but wants
+ * to re-register it, it should be allowed as long as the
+ * Unique-Id/Size match.
+ */
+
+ if (ioc->unique_id[buffer_type] == MPT3DIAGBUFFUNIQUEID &&
+ ioc->diag_buffer_sz[buffer_type] ==
+ diag_register->requested_buffer_size) {
+
+ if (!(ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED)) {
+ dctlprintk(ioc, ioc_info(ioc,
+ "%s: diag_buffer (%d) ownership changed. old-ID(0x%08x), new-ID(0x%08x)\n",
+ __func__, buffer_type,
+ ioc->unique_id[buffer_type],
+ diag_register->unique_id));
+
+ /*
+ * Application wants to own the buffer with
+ * the same size.
+ */
+ ioc->unique_id[buffer_type] =
+ diag_register->unique_id;
+ rc = 0; /* success */
+ goto out;
+ }
+ } else if (ioc->unique_id[buffer_type] !=
+ MPT3DIAGBUFFUNIQUEID) {
+ if (ioc->unique_id[buffer_type] !=
+ diag_register->unique_id ||
+ ioc->diag_buffer_sz[buffer_type] !=
+ diag_register->requested_buffer_size ||
+ !(ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED)) {
+ ioc_err(ioc,
+ "%s: already has a registered buffer for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -EINVAL;
+ }
+ } else {
+ ioc_err(ioc, "%s: already has a registered buffer for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -EINVAL;
+ }
+ } else if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED) {
+
+ if (ioc->unique_id[buffer_type] != MPT3DIAGBUFFUNIQUEID ||
+ ioc->diag_buffer_sz[buffer_type] !=
+ diag_register->requested_buffer_size) {
+
+ ioc_err(ioc,
+ "%s: already a buffer is allocated for buffer_type(0x%02x) of size %d bytes, so please try registering again with same size\n",
+ __func__, buffer_type,
+ ioc->diag_buffer_sz[buffer_type]);
+ return -EINVAL;
+ }
+ }
+
+ if (diag_register->requested_buffer_size % 4) {
+ ioc_err(ioc, "%s: the requested_buffer_size is not 4 byte aligned\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, ioc->request_sz);
+ ioc->ctl_cmds.smid = smid;
+
+ request_data = ioc->diag_buffer[buffer_type];
+ request_data_sz = diag_register->requested_buffer_size;
+ ioc->unique_id[buffer_type] = diag_register->unique_id;
+ /* Reset ioc variables used for additional query commands */
+ ioc->reset_from_user = 0;
+ memset(&ioc->htb_rel, 0, sizeof(struct htb_rel_query));
+ ioc->diag_buffer_status[buffer_type] &=
+ MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED;
+ memcpy(ioc->product_specific[buffer_type],
+ diag_register->product_specific, MPT3_PRODUCT_SPECIFIC_DWORDS);
+ ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags;
+
+ if (request_data) {
+ request_data_dma = ioc->diag_buffer_dma[buffer_type];
+ if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) {
+ dma_free_coherent(&ioc->pdev->dev,
+ ioc->diag_buffer_sz[buffer_type],
+ request_data, request_data_dma);
+ request_data = NULL;
+ }
+ }
+
+ if (request_data == NULL) {
+ ioc->diag_buffer_sz[buffer_type] = 0;
+ ioc->diag_buffer_dma[buffer_type] = 0;
+ request_data = dma_alloc_coherent(&ioc->pdev->dev,
+ request_data_sz, &request_data_dma, GFP_KERNEL);
+ if (request_data == NULL) {
+ ioc_err(ioc, "%s: failed allocating memory for diag buffers, requested size(%d)\n",
+ __func__, request_data_sz);
+ mpt3sas_base_free_smid(ioc, smid);
+ rc = -ENOMEM;
+ goto out;
+ }
+ ioc->diag_buffer[buffer_type] = request_data;
+ ioc->diag_buffer_sz[buffer_type] = request_data_sz;
+ ioc->diag_buffer_dma[buffer_type] = request_data_dma;
+ }
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+ mpi_request->BufferType = diag_register->buffer_type;
+ mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags);
+ mpi_request->BufferAddress = cpu_to_le64(request_data_dma);
+ mpi_request->BufferLength = cpu_to_le32(request_data_sz);
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ dctlprintk(ioc,
+ ioc_info(ioc, "%s: diag_buffer(0x%p), dma(0x%llx), sz(%d)\n",
+ __func__, request_data,
+ (unsigned long long)request_data_dma,
+ le32_to_cpu(mpi_request->BufferLength)));
+
+ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
+ mpi_request->ProductSpecific[i] =
+ cpu_to_le32(ioc->product_specific[buffer_type][i]);
+
+ init_completion(&ioc->ctl_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->ctl_cmds.status, mpi_request,
+ sizeof(Mpi2DiagBufferPostRequest_t)/4, issue_reset);
+ goto issue_host_reset;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) {
+ ioc_err(ioc, "%s: no reply message\n", __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_REGISTERED;
+ dctlprintk(ioc, ioc_info(ioc, "%s: success\n", __func__));
+ } else {
+ ioc_info(ioc, "%s: ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__,
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
+ rc = -EFAULT;
+ }
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+
+ out:
+
+ if (rc && request_data) {
+ dma_free_coherent(&ioc->pdev->dev, request_data_sz,
+ request_data, request_data_dma);
+ ioc->diag_buffer[buffer_type] = NULL;
+ ioc->diag_buffer_status[buffer_type] &=
+ ~MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED;
+ }
+
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * mpt3sas_enable_diag_buffer - enabling diag_buffers support driver load time
+ * @ioc: per adapter object
+ * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1
+ *
+ * This is called when command line option diag_buffer_enable is enabled
+ * at driver load time.
+ */
+void
+mpt3sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, u8 bits_to_register)
+{
+ struct mpt3_diag_register diag_register;
+ u32 ret_val;
+ u32 trace_buff_size = ioc->manu_pg11.HostTraceBufferMaxSizeKB<<10;
+ u32 min_trace_buff_size = 0;
+ u32 decr_trace_buff_size = 0;
+
+ memset(&diag_register, 0, sizeof(struct mpt3_diag_register));
+
+ if (bits_to_register & 1) {
+ ioc_info(ioc, "registering trace buffer support\n");
+ ioc->diag_trigger_master.MasterData =
+ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
+ diag_register.unique_id =
+ (ioc->hba_mpi_version_belonged == MPI2_VERSION) ?
+ (MPT2DIAGBUFFUNIQUEID):(MPT3DIAGBUFFUNIQUEID);
+
+ if (trace_buff_size != 0) {
+ diag_register.requested_buffer_size = trace_buff_size;
+ min_trace_buff_size =
+ ioc->manu_pg11.HostTraceBufferMinSizeKB<<10;
+ decr_trace_buff_size =
+ ioc->manu_pg11.HostTraceBufferDecrementSizeKB<<10;
+
+ if (min_trace_buff_size > trace_buff_size) {
+ /* The buff size is not set correctly */
+ ioc_err(ioc,
+ "Min Trace Buff size (%d KB) greater than Max Trace Buff size (%d KB)\n",
+ min_trace_buff_size>>10,
+ trace_buff_size>>10);
+ ioc_err(ioc,
+ "Using zero Min Trace Buff Size\n");
+ min_trace_buff_size = 0;
+ }
+
+ if (decr_trace_buff_size == 0) {
+ /*
+ * retry the min size if decrement
+ * is not available.
+ */
+ decr_trace_buff_size =
+ trace_buff_size - min_trace_buff_size;
+ }
+ } else {
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ }
+
+ do {
+ ret_val = _ctl_diag_register_2(ioc, &diag_register);
+
+ if (ret_val == -ENOMEM && min_trace_buff_size &&
+ (trace_buff_size - decr_trace_buff_size) >=
+ min_trace_buff_size) {
+ /* adjust the buffer size */
+ trace_buff_size -= decr_trace_buff_size;
+ diag_register.requested_buffer_size =
+ trace_buff_size;
+ } else
+ break;
+ } while (true);
+
+ if (ret_val == -ENOMEM)
+ ioc_err(ioc,
+ "Cannot allocate trace buffer memory. Last memory tried = %d KB\n",
+ diag_register.requested_buffer_size>>10);
+ else if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE]
+ & MPT3_DIAG_BUFFER_IS_REGISTERED) {
+ ioc_info(ioc, "Trace buffer memory %d KB allocated\n",
+ diag_register.requested_buffer_size>>10);
+ if (ioc->hba_mpi_version_belonged != MPI2_VERSION)
+ ioc->diag_buffer_status[
+ MPI2_DIAG_BUF_TYPE_TRACE] |=
+ MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED;
+ }
+ }
+
+ if (bits_to_register & 2) {
+ ioc_info(ioc, "registering snapshot buffer support\n");
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT;
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ diag_register.unique_id = 0x7075901;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+
+ if (bits_to_register & 4) {
+ ioc_info(ioc, "registering extended buffer support\n");
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED;
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ diag_register.unique_id = 0x7075901;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+}
+
+/**
+ * _ctl_diag_register - application register with driver
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ *
+ * This will allow the driver to setup any required buffers that will be
+ * needed by firmware to communicate with the driver.
+ */
+static long
+_ctl_diag_register(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_register karg;
+ long rc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ rc = _ctl_diag_register_2(ioc, &karg);
+
+ if (!rc && (ioc->diag_buffer_status[karg.buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED))
+ ioc->diag_buffer_status[karg.buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_APP_OWNED;
+
+ return rc;
+}
+
+/**
+ * _ctl_diag_unregister - application unregister with driver
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ *
+ * This will allow the driver to cleanup any memory allocated for diag
+ * messages and to free up any resources.
+ */
+static long
+_ctl_diag_unregister(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_unregister karg;
+ void *request_data;
+ dma_addr_t request_data_dma;
+ u32 request_data_sz;
+ u8 buffer_type;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s\n",
+ __func__));
+
+ buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id);
+ if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) {
+ ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n",
+ __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ ioc_err(ioc, "%s: buffer_type(0x%02x) is not registered\n",
+ __func__, buffer_type);
+ return -EINVAL;
+ }
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
+ ioc_err(ioc, "%s: buffer_type(0x%02x) has not been released\n",
+ __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ ioc_err(ioc, "%s: unique_id(0x%08x) is not registered\n",
+ __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ ioc_err(ioc, "%s: doesn't have memory allocated for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED) {
+ ioc->unique_id[buffer_type] = MPT3DIAGBUFFUNIQUEID;
+ ioc->diag_buffer_status[buffer_type] &=
+ ~MPT3_DIAG_BUFFER_IS_APP_OWNED;
+ ioc->diag_buffer_status[buffer_type] &=
+ ~MPT3_DIAG_BUFFER_IS_REGISTERED;
+ } else {
+ request_data_sz = ioc->diag_buffer_sz[buffer_type];
+ request_data_dma = ioc->diag_buffer_dma[buffer_type];
+ dma_free_coherent(&ioc->pdev->dev, request_data_sz,
+ request_data, request_data_dma);
+ ioc->diag_buffer[buffer_type] = NULL;
+ ioc->diag_buffer_status[buffer_type] = 0;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_diag_query - query relevant info associated with diag buffers
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ *
+ * The application will send only buffer_type and unique_id. Driver will
+ * inspect unique_id first, if valid, fill in all the info. If unique_id is
+ * 0x00, the driver will return info specified by Buffer Type.
+ */
+static long
+_ctl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_query karg;
+ void *request_data;
+ int i;
+ u8 buffer_type;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s\n",
+ __func__));
+
+ karg.application_flags = 0;
+ buffer_type = karg.buffer_type;
+
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if (!(ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED)) {
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ ioc_err(ioc, "%s: buffer_type(0x%02x) is not registered\n",
+ __func__, buffer_type);
+ return -EINVAL;
+ }
+ }
+
+ if (karg.unique_id) {
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ ioc_err(ioc, "%s: unique_id(0x%08x) is not registered\n",
+ __func__, karg.unique_id);
+ return -EINVAL;
+ }
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ ioc_err(ioc, "%s: doesn't have buffer for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED))
+ karg.application_flags |= MPT3_APP_FLAGS_BUFFER_VALID;
+
+ if (!(ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ karg.application_flags |= MPT3_APP_FLAGS_FW_BUFFER_ACCESS;
+
+ if (!(ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED))
+ karg.application_flags |= MPT3_APP_FLAGS_DYNAMIC_BUFFER_ALLOC;
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_APP_OWNED))
+ karg.application_flags |= MPT3_APP_FLAGS_APP_OWNED;
+
+ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
+ karg.product_specific[i] =
+ ioc->product_specific[buffer_type][i];
+
+ karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type];
+ karg.driver_added_buffer_size = 0;
+ karg.unique_id = ioc->unique_id[buffer_type];
+ karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type];
+
+ if (copy_to_user(arg, &karg, sizeof(struct mpt3_diag_query))) {
+ ioc_err(ioc, "%s: unable to write mpt3_diag_query data @ %p\n",
+ __func__, arg);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * mpt3sas_send_diag_release - Diag Release Message
+ * @ioc: per adapter object
+ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
+ * @issue_reset: specifies whether host reset is required.
+ *
+ */
+int
+mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type,
+ u8 *issue_reset)
+{
+ Mpi2DiagReleaseRequest_t *mpi_request;
+ Mpi2DiagReleaseReply_t *mpi_reply;
+ u16 smid;
+ u16 ioc_status;
+ u32 ioc_state;
+ int rc;
+ u8 reset_needed = 0;
+
+ dctlprintk(ioc, ioc_info(ioc, "%s\n",
+ __func__));
+
+ rc = 0;
+ *issue_reset = 0;
+
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED)
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_RELEASED;
+ dctlprintk(ioc,
+ ioc_info(ioc, "%s: skipping due to FAULT state\n",
+ __func__));
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, ioc->request_sz);
+ ioc->ctl_cmds.smid = smid;
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE;
+ mpi_request->BufferType = buffer_type;
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ init_completion(&ioc->ctl_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->ctl_cmds.status, mpi_request,
+ sizeof(Mpi2DiagReleaseRequest_t)/4, reset_needed);
+ *issue_reset = reset_needed;
+ rc = -EFAULT;
+ goto out;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) {
+ ioc_err(ioc, "%s: no reply message\n", __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_RELEASED;
+ dctlprintk(ioc, ioc_info(ioc, "%s: success\n", __func__));
+ } else {
+ ioc_info(ioc, "%s: ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__,
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
+ rc = -EFAULT;
+ }
+
+ out:
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * _ctl_diag_release - request to send Diag Release Message to firmware
+ * @ioc: ?
+ * @arg: user space buffer containing ioctl content
+ *
+ * This allows ownership of the specified buffer to returned to the driver,
+ * allowing an application to read the buffer without fear that firmware is
+ * overwriting information in the buffer.
+ */
+static long
+_ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_release karg;
+ void *request_data;
+ int rc;
+ u8 buffer_type;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s\n",
+ __func__));
+
+ buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id);
+ if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) {
+ ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n",
+ __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ ioc_err(ioc, "%s: buffer_type(0x%02x) is not registered\n",
+ __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ ioc_err(ioc, "%s: unique_id(0x%08x) is not registered\n",
+ __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ ioc_err(ioc, "%s: buffer_type(0x%02x) is already released\n",
+ __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+
+ if (!request_data) {
+ ioc_err(ioc, "%s: doesn't have memory allocated for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ /* buffers were released by due to host reset */
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_DIAG_RESET)) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_RELEASED;
+ ioc->diag_buffer_status[buffer_type] &=
+ ~MPT3_DIAG_BUFFER_IS_DIAG_RESET;
+ ioc_err(ioc, "%s: buffer_type(0x%02x) was released due to host reset\n",
+ __func__, buffer_type);
+ return 0;
+ }
+
+ rc = mpt3sas_send_diag_release(ioc, buffer_type, &issue_reset);
+
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+
+ return rc;
+}
+
+/**
+ * _ctl_diag_read_buffer - request for copy of the diag buffer
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ */
+static long
+_ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_read_buffer karg;
+ struct mpt3_diag_read_buffer __user *uarg = arg;
+ void *request_data, *diag_data;
+ Mpi2DiagBufferPostRequest_t *mpi_request;
+ Mpi2DiagBufferPostReply_t *mpi_reply;
+ int rc, i;
+ u8 buffer_type;
+ unsigned long request_size, copy_size;
+ u16 smid;
+ u16 ioc_status;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, ioc_info(ioc, "%s\n",
+ __func__));
+
+ buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id);
+ if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) {
+ ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n",
+ __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ ioc_err(ioc, "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ ioc_err(ioc, "%s: unique_id(0x%08x) is not registered\n",
+ __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ ioc_err(ioc, "%s: doesn't have buffer for buffer_type(0x%02x)\n",
+ __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ request_size = ioc->diag_buffer_sz[buffer_type];
+
+ if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) {
+ ioc_err(ioc, "%s: either the starting_offset or bytes_to_read are not 4 byte aligned\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (karg.starting_offset > request_size)
+ return -EINVAL;
+
+ diag_data = (void *)(request_data + karg.starting_offset);
+ dctlprintk(ioc,
+ ioc_info(ioc, "%s: diag_buffer(%p), offset(%d), sz(%d)\n",
+ __func__, diag_data, karg.starting_offset,
+ karg.bytes_to_read));
+
+ /* Truncate data on requests that are too large */
+ if ((diag_data + karg.bytes_to_read < diag_data) ||
+ (diag_data + karg.bytes_to_read > request_data + request_size))
+ copy_size = request_size - karg.starting_offset;
+ else
+ copy_size = karg.bytes_to_read;
+
+ if (copy_to_user((void __user *)uarg->diagnostic_data,
+ diag_data, copy_size)) {
+ ioc_err(ioc, "%s: Unable to write mpt_diag_read_buffer_t data @ %p\n",
+ __func__, diag_data);
+ return -EFAULT;
+ }
+
+ if ((karg.flags & MPT3_FLAGS_REREGISTER) == 0)
+ return 0;
+
+ dctlprintk(ioc,
+ ioc_info(ioc, "%s: Reregister buffer_type(0x%02x)\n",
+ __func__, buffer_type));
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
+ dctlprintk(ioc,
+ ioc_info(ioc, "%s: buffer_type(0x%02x) is still registered\n",
+ __func__, buffer_type));
+ return 0;
+ }
+ /* Get a free request frame and save the message context.
+ */
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, ioc->request_sz);
+ ioc->ctl_cmds.smid = smid;
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+ mpi_request->BufferType = buffer_type;
+ mpi_request->BufferLength =
+ cpu_to_le32(ioc->diag_buffer_sz[buffer_type]);
+ mpi_request->BufferAddress =
+ cpu_to_le64(ioc->diag_buffer_dma[buffer_type]);
+ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
+ mpi_request->ProductSpecific[i] =
+ cpu_to_le32(ioc->product_specific[buffer_type][i]);
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ init_completion(&ioc->ctl_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->ctl_cmds.status, mpi_request,
+ sizeof(Mpi2DiagBufferPostRequest_t)/4, issue_reset);
+ goto issue_host_reset;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) {
+ ioc_err(ioc, "%s: no reply message\n", __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_REGISTERED;
+ ioc->diag_buffer_status[buffer_type] &=
+ ~MPT3_DIAG_BUFFER_IS_RELEASED;
+ dctlprintk(ioc, ioc_info(ioc, "%s: success\n", __func__));
+ } else {
+ ioc_info(ioc, "%s: ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__, ioc_status,
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+ rc = -EFAULT;
+ }
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+
+ out:
+
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * _ctl_addnl_diag_query - query relevant info associated with diag buffers
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ *
+ * The application will send only unique_id. Driver will
+ * inspect unique_id first, if valid, fill the details related to cause
+ * for diag buffer release.
+ */
+static long
+_ctl_addnl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_addnl_diag_query karg;
+ u32 buffer_type = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("%s: failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ dctlprintk(ioc, ioc_info(ioc, "%s\n", __func__));
+ if (karg.unique_id == 0) {
+ ioc_err(ioc, "%s: unique_id is(0x%08x)\n",
+ __func__, karg.unique_id);
+ return -EPERM;
+ }
+ buffer_type = _ctl_diag_get_bufftype(ioc, karg.unique_id);
+ if (buffer_type == MPT3_DIAG_UID_NOT_FOUND) {
+ ioc_err(ioc, "%s: buffer with unique_id(0x%08x) not found\n",
+ __func__, karg.unique_id);
+ return -EPERM;
+ }
+ memset(&karg.rel_query, 0, sizeof(karg.rel_query));
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ ioc_info(ioc, "%s: buffer_type(0x%02x) is not registered\n",
+ __func__, buffer_type);
+ goto out;
+ }
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
+ ioc_err(ioc, "%s: buffer_type(0x%02x) is not released\n",
+ __func__, buffer_type);
+ return -EPERM;
+ }
+ memcpy(&karg.rel_query, &ioc->htb_rel, sizeof(karg.rel_query));
+out:
+ if (copy_to_user(arg, &karg, sizeof(struct mpt3_addnl_diag_query))) {
+ ioc_err(ioc, "%s: unable to write mpt3_addnl_diag_query data @ %p\n",
+ __func__, arg);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_enable_diag_sbr_reload - enable sbr reload bit
+ * @ioc: per adapter object
+ * @arg: user space buffer containing ioctl content
+ *
+ * Enable the SBR reload bit
+ */
+static int
+_ctl_enable_diag_sbr_reload(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ u32 ioc_state, host_diagnostic;
+
+ if (ioc->shost_recovery ||
+ ioc->pci_error_recovery || ioc->is_driver_loading ||
+ ioc->remove_host)
+ return -EAGAIN;
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL)
+ return -EFAULT;
+
+ host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic);
+
+ if (host_diagnostic & MPI2_DIAG_SBR_RELOAD)
+ return 0;
+
+ if (mutex_trylock(&ioc->hostdiag_unlock_mutex)) {
+ if (mpt3sas_base_unlock_and_get_host_diagnostic(ioc, &host_diagnostic)) {
+ mutex_unlock(&ioc->hostdiag_unlock_mutex);
+ return -EFAULT;
+ }
+ } else
+ return -EAGAIN;
+
+ host_diagnostic |= MPI2_DIAG_SBR_RELOAD;
+ writel(host_diagnostic, &ioc->chip->HostDiagnostic);
+ host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic);
+ mpt3sas_base_lock_host_diagnostic(ioc);
+ mutex_unlock(&ioc->hostdiag_unlock_mutex);
+
+ if (!(host_diagnostic & MPI2_DIAG_SBR_RELOAD)) {
+ ioc_err(ioc, "%s: Failed to set Diag SBR Reload Bit\n", __func__);
+ return -EFAULT;
+ }
+
+ ioc_info(ioc, "%s: Successfully set the Diag SBR Reload Bit\n", __func__);
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+/**
+ * _ctl_compat_mpt_command - convert 32bit pointers to 64bit.
+ * @ioc: per adapter object
+ * @cmd: ioctl opcode
+ * @arg: (struct mpt3_ioctl_command32)
+ *
+ * MPT3COMMAND32 - Handle 32bit applications running on 64bit os.
+ */
+static long
+_ctl_compat_mpt_command(struct MPT3SAS_ADAPTER *ioc, unsigned cmd,
+ void __user *arg)
+{
+ struct mpt3_ioctl_command32 karg32;
+ struct mpt3_ioctl_command32 __user *uarg;
+ struct mpt3_ioctl_command karg;
+
+ if (_IOC_SIZE(cmd) != sizeof(struct mpt3_ioctl_command32))
+ return -EINVAL;
+
+ uarg = (struct mpt3_ioctl_command32 __user *) arg;
+
+ if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ memset(&karg, 0, sizeof(struct mpt3_ioctl_command));
+ karg.hdr.ioc_number = karg32.hdr.ioc_number;
+ karg.hdr.port_number = karg32.hdr.port_number;
+ karg.hdr.max_data_size = karg32.hdr.max_data_size;
+ karg.timeout = karg32.timeout;
+ karg.max_reply_bytes = karg32.max_reply_bytes;
+ karg.data_in_size = karg32.data_in_size;
+ karg.data_out_size = karg32.data_out_size;
+ karg.max_sense_bytes = karg32.max_sense_bytes;
+ karg.data_sge_offset = karg32.data_sge_offset;
+ karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr);
+ karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr);
+ karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr);
+ karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr);
+ return _ctl_do_mpt_command(ioc, karg, &uarg->mf);
+}
+#endif
+
+/**
+ * _ctl_ioctl_main - main ioctl entry point
+ * @file: (struct file)
+ * @cmd: ioctl opcode
+ * @arg: user space data buffer
+ * @compat: handles 32 bit applications in 64bit os
+ * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device &
+ * MPI25_VERSION | MPI26_VERSION for mpt3ctl ioctl device.
+ */
+static long
+_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg,
+ u8 compat, u16 mpi_version)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+ struct mpt3_ioctl_header ioctl_header;
+ enum block_state state;
+ long ret = -ENOIOCTLCMD;
+
+ /* get IOCTL header */
+ if (copy_from_user(&ioctl_header, (char __user *)arg,
+ sizeof(struct mpt3_ioctl_header))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ if (_ctl_verify_adapter(ioctl_header.ioc_number,
+ &ioc, mpi_version) == -1 || !ioc)
+ return -ENODEV;
+
+ /* pci_access_mutex lock acquired by ioctl path */
+ mutex_lock(&ioc->pci_access_mutex);
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery ||
+ ioc->is_driver_loading || ioc->remove_host) {
+ ret = -EAGAIN;
+ goto out_unlock_pciaccess;
+ }
+
+ state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING;
+ if (state == NON_BLOCKING) {
+ if (!mutex_trylock(&ioc->ctl_cmds.mutex)) {
+ ret = -EAGAIN;
+ goto out_unlock_pciaccess;
+ }
+ } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) {
+ ret = -ERESTARTSYS;
+ goto out_unlock_pciaccess;
+ }
+
+
+ switch (cmd) {
+ case MPT3IOCINFO:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_iocinfo))
+ ret = _ctl_getiocinfo(ioc, arg);
+ break;
+#ifdef CONFIG_COMPAT
+ case MPT3COMMAND32:
+#endif
+ case MPT3COMMAND:
+ {
+ struct mpt3_ioctl_command __user *uarg;
+ struct mpt3_ioctl_command karg;
+
+#ifdef CONFIG_COMPAT
+ if (compat) {
+ ret = _ctl_compat_mpt_command(ioc, cmd, arg);
+ break;
+ }
+#endif
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ ret = -EFAULT;
+ break;
+ }
+
+ if (karg.hdr.ioc_number != ioctl_header.ioc_number) {
+ ret = -EINVAL;
+ break;
+ }
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_command)) {
+ uarg = arg;
+ ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf);
+ }
+ break;
+ }
+ case MPT3EVENTQUERY:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventquery))
+ ret = _ctl_eventquery(ioc, arg);
+ break;
+ case MPT3EVENTENABLE:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventenable))
+ ret = _ctl_eventenable(ioc, arg);
+ break;
+ case MPT3EVENTREPORT:
+ ret = _ctl_eventreport(ioc, arg);
+ break;
+ case MPT3HARDRESET:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_diag_reset))
+ ret = _ctl_do_reset(ioc, arg);
+ break;
+ case MPT3BTDHMAPPING:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_btdh_mapping))
+ ret = _ctl_btdh_mapping(ioc, arg);
+ break;
+ case MPT3DIAGREGISTER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_register))
+ ret = _ctl_diag_register(ioc, arg);
+ break;
+ case MPT3DIAGUNREGISTER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_unregister))
+ ret = _ctl_diag_unregister(ioc, arg);
+ break;
+ case MPT3DIAGQUERY:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_query))
+ ret = _ctl_diag_query(ioc, arg);
+ break;
+ case MPT3DIAGRELEASE:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_release))
+ ret = _ctl_diag_release(ioc, arg);
+ break;
+ case MPT3DIAGREADBUFFER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_read_buffer))
+ ret = _ctl_diag_read_buffer(ioc, arg);
+ break;
+ case MPT3ADDNLDIAGQUERY:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_addnl_diag_query))
+ ret = _ctl_addnl_diag_query(ioc, arg);
+ break;
+ case MPT3ENABLEDIAGSBRRELOAD:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_enable_diag_sbr_reload))
+ ret = _ctl_enable_diag_sbr_reload(ioc, arg);
+ break;
+ default:
+ dctlprintk(ioc,
+ ioc_info(ioc, "unsupported ioctl opcode(0x%08x)\n",
+ cmd));
+ break;
+ }
+
+ mutex_unlock(&ioc->ctl_cmds.mutex);
+out_unlock_pciaccess:
+ mutex_unlock(&ioc->pci_access_mutex);
+ return ret;
+}
+
+/**
+ * _ctl_get_mpt_mctp_passthru_adapter - Traverse the IOC list and return the IOC at
+ * dev_index positionthat support MCTP passhtru
+ * @dev_index: position in the mpt3sas_ioc_list to search for
+ * Return pointer to the IOC on success
+ * NULL if device not found error
+ */
+static struct MPT3SAS_ADAPTER *
+_ctl_get_mpt_mctp_passthru_adapter(int dev_index)
+{
+ struct MPT3SAS_ADAPTER *ioc = NULL;
+ int count = 0;
+
+ spin_lock(&gioc_lock);
+ /* Traverse ioc list and return number of IOC that support MCTP passthru */
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
+ if (ioc->facts.IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU) {
+ if (count == dev_index) {
+ spin_unlock(&gioc_lock);
+ return ioc;
+ }
+ count++;
+ }
+ }
+ spin_unlock(&gioc_lock);
+
+ return NULL;
+}
+
+/**
+ * mpt3sas_get_device_count - Retrieve the count of MCTP passthrough
+ * capable devices managed by the driver.
+ *
+ * Returns number of devices that support MCTP passthrough.
+ */
+int
+mpt3sas_get_device_count(void)
+{
+ int count = 0;
+ struct MPT3SAS_ADAPTER *ioc = NULL;
+
+ spin_lock(&gioc_lock);
+ /* Traverse ioc list and return number of IOC that support MCTP passthru */
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
+ if (ioc->facts.IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU)
+ count++;
+
+ spin_unlock(&gioc_lock);
+
+ return count;
+}
+EXPORT_SYMBOL(mpt3sas_get_device_count);
+
+/**
+ * mpt3sas_send_passthru_cmd - Send an MPI MCTP passthrough command to
+ * firmware
+ * @command: The MPI MCTP passthrough command to send to firmware
+ *
+ * Returns 0 on success, anything else is error.
+ */
+int mpt3sas_send_mctp_passthru_req(struct mpt3_passthru_command *command)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+ MPI2RequestHeader_t *mpi_request = NULL, *request;
+ Mpi26MctpPassthroughRequest_t *mctp_passthru_req;
+ u16 smid;
+ unsigned long timeout;
+ u8 issue_reset = 0;
+ u32 sz;
+ void *psge;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma = 0;
+ size_t data_out_sz = 0;
+ void *data_in = NULL;
+ dma_addr_t data_in_dma = 0;
+ size_t data_in_sz = 0;
+ long ret;
+
+ /* Retrieve ioc from dev_index */
+ ioc = _ctl_get_mpt_mctp_passthru_adapter(command->dev_index);
+ if (!ioc)
+ return -ENODEV;
+
+ mutex_lock(&ioc->pci_access_mutex);
+ if (ioc->shost_recovery ||
+ ioc->pci_error_recovery || ioc->is_driver_loading ||
+ ioc->remove_host) {
+ ret = -EAGAIN;
+ goto unlock_pci_access;
+ }
+
+ /* Lock the ctl_cmds mutex to ensure a single ctl cmd is pending */
+ if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) {
+ ret = -ERESTARTSYS;
+ goto unlock_pci_access;
+ }
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: ctl_cmd in use\n", __func__);
+ ret = -EAGAIN;
+ goto unlock_ctl_cmds;
+ }
+
+ ret = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (ret)
+ goto unlock_ctl_cmds;
+
+ mpi_request = (MPI2RequestHeader_t *)command->mpi_request;
+ if (mpi_request->Function != MPI2_FUNCTION_MCTP_PASSTHROUGH) {
+ ioc_err(ioc, "%s: Invalid request received, Function 0x%x\n",
+ __func__, mpi_request->Function);
+ ret = -EINVAL;
+ goto unlock_ctl_cmds;
+ }
+
+ /* Use first reserved smid for passthrough commands */
+ smid = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT + 1;
+ ret = 0;
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(request, 0, ioc->request_sz);
+ memcpy(request, command->mpi_request, sizeof(Mpi26MctpPassthroughRequest_t));
+ ioc->ctl_cmds.smid = smid;
+ data_out_sz = command->data_out_size;
+ data_in_sz = command->data_in_size;
+
+ /* obtain dma-able memory for data transfer */
+ if (data_out_sz) /* WRITE */ {
+ data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz,
+ &data_out_dma, GFP_ATOMIC);
+ if (!data_out) {
+ ret = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ memcpy(data_out, command->data_out_buf_ptr, data_out_sz);
+
+ }
+
+ if (data_in_sz) /* READ */ {
+ data_in = dma_alloc_coherent(&ioc->pdev->dev, data_in_sz,
+ &data_in_dma, GFP_ATOMIC);
+ if (!data_in) {
+ ret = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ psge = &((Mpi26MctpPassthroughRequest_t *)request)->H2DSGL;
+
+ init_completion(&ioc->ctl_cmds.done);
+
+ mctp_passthru_req = (Mpi26MctpPassthroughRequest_t *)request;
+
+ _ctl_send_mctp_passthru_req(ioc, mctp_passthru_req, psge, data_out_dma,
+ data_out_sz, data_in_dma, data_in_sz, smid);
+
+ timeout = command->timeout;
+ if (timeout < MPT3_IOCTL_DEFAULT_TIMEOUT)
+ timeout = MPT3_IOCTL_DEFAULT_TIMEOUT;
+
+ wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ);
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->ctl_cmds.status, mpi_request,
+ sizeof(Mpi26MctpPassthroughRequest_t) / 4, issue_reset);
+ goto issue_host_reset;
+ }
+
+ /* copy out xdata to user */
+ if (data_in_sz)
+ memcpy(command->data_in_buf_ptr, data_in, data_in_sz);
+
+ /* copy out reply message frame to user */
+ if (command->max_reply_bytes) {
+ sz = min_t(u32, command->max_reply_bytes, ioc->reply_sz);
+ memcpy(command->reply_frame_buf_ptr, ioc->ctl_cmds.reply, sz);
+ }
+
+issue_host_reset:
+ if (issue_reset) {
+ ret = -ENODATA;
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ }
+
+out:
+ /* free memory associated with sg buffers */
+ if (data_in)
+ dma_free_coherent(&ioc->pdev->dev, data_in_sz, data_in,
+ data_in_dma);
+
+ if (data_out)
+ dma_free_coherent(&ioc->pdev->dev, data_out_sz, data_out,
+ data_out_dma);
+
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+
+unlock_ctl_cmds:
+ mutex_unlock(&ioc->ctl_cmds.mutex);
+
+unlock_pci_access:
+ mutex_unlock(&ioc->pci_access_mutex);
+ return ret;
+
+}
+EXPORT_SYMBOL(mpt3sas_send_mctp_passthru_req);
+
+/**
+ * _ctl_ioctl - mpt3ctl main ioctl entry point (unlocked)
+ * @file: (struct file)
+ * @cmd: ioctl opcode
+ * @arg: ?
+ */
+static long
+_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ /* pass MPI25_VERSION | MPI26_VERSION value,
+ * to indicate that this ioctl cmd
+ * came from mpt3ctl ioctl device.
+ */
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0,
+ MPI25_VERSION | MPI26_VERSION);
+ return ret;
+}
+
+/**
+ * _ctl_mpt2_ioctl - mpt2ctl main ioctl entry point (unlocked)
+ * @file: (struct file)
+ * @cmd: ioctl opcode
+ * @arg: ?
+ */
+static long
+_ctl_mpt2_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ /* pass MPI2_VERSION value, to indicate that this ioctl cmd
+ * came from mpt2ctl ioctl device.
+ */
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0, MPI2_VERSION);
+ return ret;
+}
+#ifdef CONFIG_COMPAT
+/**
+ * _ctl_ioctl_compat - main ioctl entry point (compat)
+ * @file: ?
+ * @cmd: ?
+ * @arg: ?
+ *
+ * This routine handles 32 bit applications in 64bit os.
+ */
+static long
+_ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg)
+{
+ long ret;
+
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1,
+ MPI25_VERSION | MPI26_VERSION);
+ return ret;
+}
+
+/**
+ * _ctl_mpt2_ioctl_compat - main ioctl entry point (compat)
+ * @file: ?
+ * @cmd: ?
+ * @arg: ?
+ *
+ * This routine handles 32 bit applications in 64bit os.
+ */
+static long
+_ctl_mpt2_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg)
+{
+ long ret;
+
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1, MPI2_VERSION);
+ return ret;
+}
+#endif
+
+/* scsi host attributes */
+/**
+ * version_fw_show - firmware version
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+version_fw_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
+ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
+ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
+ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
+ ioc->facts.FWVersion.Word & 0x000000FF);
+}
+static DEVICE_ATTR_RO(version_fw);
+
+/**
+ * version_bios_show - bios version
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+version_bios_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
+
+ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
+ (version & 0xFF000000) >> 24,
+ (version & 0x00FF0000) >> 16,
+ (version & 0x0000FF00) >> 8,
+ version & 0x000000FF);
+}
+static DEVICE_ATTR_RO(version_bios);
+
+/**
+ * version_mpi_show - MPI (message passing interface) version
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+version_mpi_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%03x.%02x\n",
+ ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8);
+}
+static DEVICE_ATTR_RO(version_mpi);
+
+/**
+ * version_product_show - product name
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+version_product_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName);
+}
+static DEVICE_ATTR_RO(version_product);
+
+/**
+ * version_nvdata_persistent_show - ndvata persistent version
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+version_nvdata_persistent_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n",
+ le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word));
+}
+static DEVICE_ATTR_RO(version_nvdata_persistent);
+
+/**
+ * version_nvdata_default_show - nvdata default version
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+version_nvdata_default_show(struct device *cdev, struct device_attribute
+ *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n",
+ le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word));
+}
+static DEVICE_ATTR_RO(version_nvdata_default);
+
+/**
+ * board_name_show - board name
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+board_name_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName);
+}
+static DEVICE_ATTR_RO(board_name);
+
+/**
+ * board_assembly_show - board assembly name
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+board_assembly_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly);
+}
+static DEVICE_ATTR_RO(board_assembly);
+
+/**
+ * board_tracer_show - board tracer number
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+board_tracer_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber);
+}
+static DEVICE_ATTR_RO(board_tracer);
+
+/**
+ * io_delay_show - io missing delay
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is for firmware implemention for deboucing device
+ * removal events.
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+io_delay_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay);
+}
+static DEVICE_ATTR_RO(io_delay);
+
+/**
+ * device_delay_show - device missing delay
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is for firmware implemention for deboucing device
+ * removal events.
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+device_delay_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay);
+}
+static DEVICE_ATTR_RO(device_delay);
+
+/**
+ * fw_queue_depth_show - global credits
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is firmware queue depth limit
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+fw_queue_depth_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit);
+}
+static DEVICE_ATTR_RO(fw_queue_depth);
+
+/**
+ * host_sas_address_show - sas address
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is the controller sas address
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+host_sas_address_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ (unsigned long long)ioc->sas_hba.sas_address);
+}
+static DEVICE_ATTR_RO(host_sas_address);
+
+/**
+ * logging_level_show - logging level
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+logging_level_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level);
+}
+static ssize_t
+logging_level_store(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%x", &val) != 1)
+ return -EINVAL;
+
+ ioc->logging_level = val;
+ ioc_info(ioc, "logging_level=%08xh\n",
+ ioc->logging_level);
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(logging_level);
+
+/**
+ * fwfault_debug_show - show/store fwfault_debug
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * mpt3sas_fwfault_debug is command line option
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+fwfault_debug_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug);
+}
+static ssize_t
+fwfault_debug_store(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ ioc->fwfault_debug = val;
+ ioc_info(ioc, "fwfault_debug=%d\n",
+ ioc->fwfault_debug);
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(fwfault_debug);
+
+/**
+ * ioc_reset_count_show - ioc reset count
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is firmware queue depth limit
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+ioc_reset_count_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->ioc_reset_count);
+}
+static DEVICE_ATTR_RO(ioc_reset_count);
+
+/**
+ * reply_queue_count_show - number of reply queues
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is number of reply queues
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+reply_queue_count_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 reply_queue_count;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ if ((ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable)
+ reply_queue_count = ioc->reply_queue_count;
+ else
+ reply_queue_count = 1;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count);
+}
+static DEVICE_ATTR_RO(reply_queue_count);
+
+/**
+ * BRM_status_show - Backup Rail Monitor Status
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is number of reply queues
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+BRM_status_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ Mpi2IOUnitPage3_t io_unit_pg3;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 backup_rail_monitor_status = 0;
+ u16 ioc_status;
+ int sz;
+ ssize_t rc = 0;
+
+ if (!ioc->is_warpdrive) {
+ ioc_err(ioc, "%s: BRM attribute is only for warpdrive\n",
+ __func__);
+ return 0;
+ }
+ /* pci_access_mutex lock acquired by sysfs show path */
+ mutex_lock(&ioc->pci_access_mutex);
+ if (ioc->pci_error_recovery || ioc->remove_host)
+ goto out;
+
+ sz = sizeof(io_unit_pg3);
+ memset(&io_unit_pg3, 0, sz);
+
+ if (mpt3sas_config_get_iounit_pg3(ioc, &mpi_reply, &io_unit_pg3, sz) !=
+ 0) {
+ ioc_err(ioc, "%s: failed reading iounit_pg3\n",
+ __func__);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "%s: iounit_pg3 failed with ioc_status(0x%04x)\n",
+ __func__, ioc_status);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (io_unit_pg3.GPIOCount < 25) {
+ ioc_err(ioc, "%s: iounit_pg3.GPIOCount less than 25 entries, detected (%d) entries\n",
+ __func__, io_unit_pg3.GPIOCount);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* BRM status is in bit zero of GPIOVal[24] */
+ backup_rail_monitor_status = le16_to_cpu(io_unit_pg3.GPIOVal[24]);
+ rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1));
+
+ out:
+ mutex_unlock(&ioc->pci_access_mutex);
+ return rc;
+}
+static DEVICE_ATTR_RO(BRM_status);
+
+struct DIAG_BUFFER_START {
+ __le32 Size;
+ __le32 DiagVersion;
+ u8 BufferType;
+ u8 Reserved[3];
+ __le32 Reserved1;
+ __le32 Reserved2;
+ __le32 Reserved3;
+};
+
+/**
+ * host_trace_buffer_size_show - host buffer size (trace only)
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+host_trace_buffer_size_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ u32 size = 0;
+ struct DIAG_BUFFER_START *request_data;
+
+ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) {
+ ioc_err(ioc, "%s: host_trace_buffer is not registered\n",
+ __func__);
+ return 0;
+ }
+
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ ioc_err(ioc, "%s: host_trace_buffer is not registered\n",
+ __func__);
+ return 0;
+ }
+
+ request_data = (struct DIAG_BUFFER_START *)
+ ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE];
+ if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 ||
+ le32_to_cpu(request_data->DiagVersion) == 0x01000000 ||
+ le32_to_cpu(request_data->DiagVersion) == 0x01010000) &&
+ le32_to_cpu(request_data->Reserved3) == 0x4742444c)
+ size = le32_to_cpu(request_data->Size);
+
+ ioc->ring_buffer_sz = size;
+ return snprintf(buf, PAGE_SIZE, "%d\n", size);
+}
+static DEVICE_ATTR_RO(host_trace_buffer_size);
+
+/**
+ * host_trace_buffer_show - firmware ring buffer (trace only)
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ *
+ * You will only be able to read 4k bytes of ring buffer at a time.
+ * In order to read beyond 4k bytes, you will have to write out the
+ * offset to the same attribute, it will move the pointer.
+ */
+static ssize_t
+host_trace_buffer_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ void *request_data;
+ u32 size;
+
+ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) {
+ ioc_err(ioc, "%s: host_trace_buffer is not registered\n",
+ __func__);
+ return 0;
+ }
+
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ ioc_err(ioc, "%s: host_trace_buffer is not registered\n",
+ __func__);
+ return 0;
+ }
+
+ if (ioc->ring_buffer_offset > ioc->ring_buffer_sz)
+ return 0;
+
+ size = ioc->ring_buffer_sz - ioc->ring_buffer_offset;
+ size = (size >= PAGE_SIZE) ? (PAGE_SIZE - 1) : size;
+ request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset;
+ memcpy(buf, request_data, size);
+ return size;
+}
+
+static ssize_t
+host_trace_buffer_store(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ ioc->ring_buffer_offset = val;
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(host_trace_buffer);
+
+
+/*****************************************/
+
+/**
+ * host_trace_buffer_enable_show - firmware ring buffer (trace only)
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ *
+ * This is a mechnism to post/release host_trace_buffers
+ */
+static ssize_t
+host_trace_buffer_enable_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) ||
+ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0))
+ return snprintf(buf, PAGE_SIZE, "off\n");
+ else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ return snprintf(buf, PAGE_SIZE, "release\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "post\n");
+}
+
+static ssize_t
+host_trace_buffer_enable_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ char str[10] = "";
+ struct mpt3_diag_register diag_register;
+ u8 issue_reset = 0;
+
+ /* don't allow post/release occurr while recovery is active */
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery || ioc->is_driver_loading)
+ return -EBUSY;
+
+ if (sscanf(buf, "%9s", str) != 1)
+ return -EINVAL;
+
+ if (!strcmp(str, "post")) {
+ /* exit out if host buffers are already posted */
+ if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) &&
+ (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) &&
+ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0))
+ goto out;
+ memset(&diag_register, 0, sizeof(struct mpt3_diag_register));
+ ioc_info(ioc, "posting host trace buffers\n");
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
+
+ if (ioc->manu_pg11.HostTraceBufferMaxSizeKB != 0 &&
+ ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE] != 0) {
+ /* post the same buffer allocated previously */
+ diag_register.requested_buffer_size =
+ ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE];
+ } else {
+ /*
+ * Free the diag buffer memory which was previously
+ * allocated by an application.
+ */
+ if ((ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE] != 0)
+ &&
+ (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_APP_OWNED)) {
+ dma_free_coherent(&ioc->pdev->dev,
+ ioc->diag_buffer_sz[MPI2_DIAG_BUF_TYPE_TRACE],
+ ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE],
+ ioc->diag_buffer_dma[MPI2_DIAG_BUF_TYPE_TRACE]);
+ ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE] =
+ NULL;
+ }
+
+ diag_register.requested_buffer_size = (1024 * 1024);
+ }
+
+ diag_register.unique_id =
+ (ioc->hba_mpi_version_belonged == MPI2_VERSION) ?
+ (MPT2DIAGBUFFUNIQUEID):(MPT3DIAGBUFFUNIQUEID);
+ ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0;
+ _ctl_diag_register_2(ioc, &diag_register);
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) {
+ ioc_info(ioc,
+ "Trace buffer %d KB allocated through sysfs\n",
+ diag_register.requested_buffer_size>>10);
+ if (ioc->hba_mpi_version_belonged != MPI2_VERSION)
+ ioc->diag_buffer_status[
+ MPI2_DIAG_BUF_TYPE_TRACE] |=
+ MPT3_DIAG_BUFFER_IS_DRIVER_ALLOCATED;
+ }
+ } else if (!strcmp(str, "release")) {
+ /* exit out if host buffers are already released */
+ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE])
+ goto out;
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0)
+ goto out;
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ goto out;
+ ioc_info(ioc, "releasing host trace buffer\n");
+ ioc->htb_rel.buffer_rel_condition = MPT3_DIAG_BUFFER_REL_SYSFS;
+ mpt3sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE,
+ &issue_reset);
+ }
+
+ out:
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(host_trace_buffer_enable);
+
+/*********** diagnostic trigger suppport *********************************/
+
+/**
+ * diag_trigger_master_show - show the diag_trigger_master attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_master_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_MASTER_TRIGGER_T);
+ memcpy(buf, &ioc->diag_trigger_master, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * diag_trigger_master_store - store the diag_trigger_master attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ * @count: ?
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_master_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct SL_WH_MASTER_TRIGGER_T *master_tg;
+ unsigned long flags;
+ ssize_t rc;
+ bool set = 1;
+
+ rc = min(sizeof(struct SL_WH_MASTER_TRIGGER_T), count);
+
+ if (ioc->supports_trigger_pages) {
+ master_tg = kzalloc(sizeof(struct SL_WH_MASTER_TRIGGER_T),
+ GFP_KERNEL);
+ if (!master_tg)
+ return -ENOMEM;
+
+ memcpy(master_tg, buf, rc);
+ if (!master_tg->MasterData)
+ set = 0;
+ if (mpt3sas_config_update_driver_trigger_pg1(ioc, master_tg,
+ set)) {
+ kfree(master_tg);
+ return -EFAULT;
+ }
+ kfree(master_tg);
+ }
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ memset(&ioc->diag_trigger_master, 0,
+ sizeof(struct SL_WH_MASTER_TRIGGER_T));
+ memcpy(&ioc->diag_trigger_master, buf, rc);
+ ioc->diag_trigger_master.MasterData |=
+ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+static DEVICE_ATTR_RW(diag_trigger_master);
+
+
+/**
+ * diag_trigger_event_show - show the diag_trigger_event attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_event_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_EVENT_TRIGGERS_T);
+ memcpy(buf, &ioc->diag_trigger_event, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * diag_trigger_event_store - store the diag_trigger_event attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ * @count: ?
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_event_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct SL_WH_EVENT_TRIGGERS_T *event_tg;
+ unsigned long flags;
+ ssize_t sz;
+ bool set = 1;
+
+ sz = min(sizeof(struct SL_WH_EVENT_TRIGGERS_T), count);
+ if (ioc->supports_trigger_pages) {
+ event_tg = kzalloc(sizeof(struct SL_WH_EVENT_TRIGGERS_T),
+ GFP_KERNEL);
+ if (!event_tg)
+ return -ENOMEM;
+
+ memcpy(event_tg, buf, sz);
+ if (!event_tg->ValidEntries)
+ set = 0;
+ if (mpt3sas_config_update_driver_trigger_pg2(ioc, event_tg,
+ set)) {
+ kfree(event_tg);
+ return -EFAULT;
+ }
+ kfree(event_tg);
+ }
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ memset(&ioc->diag_trigger_event, 0,
+ sizeof(struct SL_WH_EVENT_TRIGGERS_T));
+ memcpy(&ioc->diag_trigger_event, buf, sz);
+ if (ioc->diag_trigger_event.ValidEntries > NUM_VALID_ENTRIES)
+ ioc->diag_trigger_event.ValidEntries = NUM_VALID_ENTRIES;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return sz;
+}
+static DEVICE_ATTR_RW(diag_trigger_event);
+
+
+/**
+ * diag_trigger_scsi_show - show the diag_trigger_scsi attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_scsi_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_SCSI_TRIGGERS_T);
+ memcpy(buf, &ioc->diag_trigger_scsi, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * diag_trigger_scsi_store - store the diag_trigger_scsi attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ * @count: ?
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_scsi_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct SL_WH_SCSI_TRIGGERS_T *scsi_tg;
+ unsigned long flags;
+ ssize_t sz;
+ bool set = 1;
+
+ sz = min(sizeof(struct SL_WH_SCSI_TRIGGERS_T), count);
+ if (ioc->supports_trigger_pages) {
+ scsi_tg = kzalloc(sizeof(struct SL_WH_SCSI_TRIGGERS_T),
+ GFP_KERNEL);
+ if (!scsi_tg)
+ return -ENOMEM;
+
+ memcpy(scsi_tg, buf, sz);
+ if (!scsi_tg->ValidEntries)
+ set = 0;
+ if (mpt3sas_config_update_driver_trigger_pg3(ioc, scsi_tg,
+ set)) {
+ kfree(scsi_tg);
+ return -EFAULT;
+ }
+ kfree(scsi_tg);
+ }
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ memset(&ioc->diag_trigger_scsi, 0, sizeof(ioc->diag_trigger_scsi));
+ memcpy(&ioc->diag_trigger_scsi, buf, sz);
+ if (ioc->diag_trigger_scsi.ValidEntries > NUM_VALID_ENTRIES)
+ ioc->diag_trigger_scsi.ValidEntries = NUM_VALID_ENTRIES;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return sz;
+}
+static DEVICE_ATTR_RW(diag_trigger_scsi);
+
+
+/**
+ * diag_trigger_mpi_show - show the diag_trigger_mpi attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_mpi_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_MPI_TRIGGERS_T);
+ memcpy(buf, &ioc->diag_trigger_mpi, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * diag_trigger_mpi_store - store the diag_trigger_mpi attribute
+ * @cdev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ * @count: ?
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+diag_trigger_mpi_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct SL_WH_MPI_TRIGGERS_T *mpi_tg;
+ unsigned long flags;
+ ssize_t sz;
+ bool set = 1;
+
+ sz = min(sizeof(struct SL_WH_MPI_TRIGGERS_T), count);
+ if (ioc->supports_trigger_pages) {
+ mpi_tg = kzalloc(sizeof(struct SL_WH_MPI_TRIGGERS_T),
+ GFP_KERNEL);
+ if (!mpi_tg)
+ return -ENOMEM;
+
+ memcpy(mpi_tg, buf, sz);
+ if (!mpi_tg->ValidEntries)
+ set = 0;
+ if (mpt3sas_config_update_driver_trigger_pg4(ioc, mpi_tg,
+ set)) {
+ kfree(mpi_tg);
+ return -EFAULT;
+ }
+ kfree(mpi_tg);
+ }
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ memset(&ioc->diag_trigger_mpi, 0,
+ sizeof(ioc->diag_trigger_mpi));
+ memcpy(&ioc->diag_trigger_mpi, buf, sz);
+ if (ioc->diag_trigger_mpi.ValidEntries > NUM_VALID_ENTRIES)
+ ioc->diag_trigger_mpi.ValidEntries = NUM_VALID_ENTRIES;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return sz;
+}
+
+static DEVICE_ATTR_RW(diag_trigger_mpi);
+
+/*********** diagnostic trigger suppport *** END ****************************/
+
+/*****************************************/
+
+/**
+ * drv_support_bitmap_show - driver supported feature bitmap
+ * @cdev: pointer to embedded class device
+ * @attr: unused
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+drv_support_bitmap_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "0x%08x\n", ioc->drv_support_bitmap);
+}
+static DEVICE_ATTR_RO(drv_support_bitmap);
+
+/**
+ * enable_sdev_max_qd_show - display whether sdev max qd is enabled/disabled
+ * @cdev: pointer to embedded class device
+ * @attr: unused
+ * @buf: the buffer returned
+ *
+ * A sysfs read/write shost attribute. This attribute is used to set the
+ * targets queue depth to HBA IO queue depth if this attribute is enabled.
+ */
+static ssize_t
+enable_sdev_max_qd_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->enable_sdev_max_qd);
+}
+
+/**
+ * enable_sdev_max_qd_store - Enable/disable sdev max qd
+ * @cdev: pointer to embedded class device
+ * @attr: unused
+ * @buf: the buffer returned
+ * @count: unused
+ *
+ * A sysfs read/write shost attribute. This attribute is used to set the
+ * targets queue depth to HBA IO queue depth if this attribute is enabled.
+ * If this attribute is disabled then targets will have corresponding default
+ * queue depth.
+ */
+static ssize_t
+enable_sdev_max_qd_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ int val = 0;
+ struct scsi_device *sdev;
+ struct _raid_device *raid_device;
+ int qdepth;
+
+ if (kstrtoint(buf, 0, &val) != 0)
+ return -EINVAL;
+
+ switch (val) {
+ case 0:
+ ioc->enable_sdev_max_qd = 0;
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ if (!sas_target_priv_data)
+ continue;
+
+ if (sas_target_priv_data->flags &
+ MPT_TARGET_FLAGS_VOLUME) {
+ raid_device =
+ mpt3sas_raid_device_find_by_handle(ioc,
+ sas_target_priv_data->handle);
+
+ switch (raid_device->volume_type) {
+ case MPI2_RAID_VOL_TYPE_RAID0:
+ if (raid_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SSP_TARGET)
+ qdepth =
+ MPT3SAS_SAS_QUEUE_DEPTH;
+ else
+ qdepth =
+ MPT3SAS_SATA_QUEUE_DEPTH;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1E:
+ case MPI2_RAID_VOL_TYPE_RAID1:
+ case MPI2_RAID_VOL_TYPE_RAID10:
+ case MPI2_RAID_VOL_TYPE_UNKNOWN:
+ default:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ }
+ } else if (sas_target_priv_data->flags &
+ MPT_TARGET_FLAGS_PCIE_DEVICE)
+ qdepth = ioc->max_nvme_qd;
+ else
+ qdepth = (sas_target_priv_data->sas_dev->port_type > 1) ?
+ ioc->max_wideport_qd : ioc->max_narrowport_qd;
+
+ mpt3sas_scsih_change_queue_depth(sdev, qdepth);
+ }
+ break;
+ case 1:
+ ioc->enable_sdev_max_qd = 1;
+ shost_for_each_device(sdev, ioc->shost)
+ mpt3sas_scsih_change_queue_depth(sdev,
+ shost->can_queue);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(enable_sdev_max_qd);
+
+static struct attribute *mpt3sas_host_attrs[] = {
+ &dev_attr_version_fw.attr,
+ &dev_attr_version_bios.attr,
+ &dev_attr_version_mpi.attr,
+ &dev_attr_version_product.attr,
+ &dev_attr_version_nvdata_persistent.attr,
+ &dev_attr_version_nvdata_default.attr,
+ &dev_attr_board_name.attr,
+ &dev_attr_board_assembly.attr,
+ &dev_attr_board_tracer.attr,
+ &dev_attr_io_delay.attr,
+ &dev_attr_device_delay.attr,
+ &dev_attr_logging_level.attr,
+ &dev_attr_fwfault_debug.attr,
+ &dev_attr_fw_queue_depth.attr,
+ &dev_attr_host_sas_address.attr,
+ &dev_attr_ioc_reset_count.attr,
+ &dev_attr_host_trace_buffer_size.attr,
+ &dev_attr_host_trace_buffer.attr,
+ &dev_attr_host_trace_buffer_enable.attr,
+ &dev_attr_reply_queue_count.attr,
+ &dev_attr_diag_trigger_master.attr,
+ &dev_attr_diag_trigger_event.attr,
+ &dev_attr_diag_trigger_scsi.attr,
+ &dev_attr_diag_trigger_mpi.attr,
+ &dev_attr_drv_support_bitmap.attr,
+ &dev_attr_BRM_status.attr,
+ &dev_attr_enable_sdev_max_qd.attr,
+ NULL,
+};
+
+static const struct attribute_group mpt3sas_host_attr_group = {
+ .attrs = mpt3sas_host_attrs
+};
+
+const struct attribute_group *mpt3sas_host_groups[] = {
+ &mpt3sas_host_attr_group,
+ NULL
+};
+
+/* device attributes */
+
+/**
+ * sas_address_show - sas address
+ * @dev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is the sas address for the target
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+sas_address_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ (unsigned long long)sas_device_priv_data->sas_target->sas_address);
+}
+static DEVICE_ATTR_RO(sas_address);
+
+/**
+ * sas_device_handle_show - device handle
+ * @dev: pointer to embedded class device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * This is the firmware assigned device handle
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+sas_device_handle_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n",
+ sas_device_priv_data->sas_target->handle);
+}
+static DEVICE_ATTR_RO(sas_device_handle);
+
+/**
+ * sas_ncq_prio_supported_show - Indicate if device supports NCQ priority
+ * @dev: pointer to embedded device
+ * @attr: sas_ncq_prio_supported attribute descriptor
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' sdev attribute, only works with SATA
+ */
+static ssize_t
+sas_ncq_prio_supported_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return sysfs_emit(buf, "%d\n", sas_ata_ncq_prio_supported(sdev));
+}
+static DEVICE_ATTR_RO(sas_ncq_prio_supported);
+
+/**
+ * sas_ncq_prio_enable_show - send prioritized io commands to device
+ * @dev: pointer to embedded device
+ * @attr: ?
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' sdev attribute, only works with SATA
+ */
+static ssize_t
+sas_ncq_prio_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ sas_device_priv_data->ncq_prio_enable);
+}
+
+static ssize_t
+sas_ncq_prio_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+ bool ncq_prio_enable = 0;
+
+ if (kstrtobool(buf, &ncq_prio_enable))
+ return -EINVAL;
+
+ if (!sas_ata_ncq_prio_supported(sdev))
+ return -EINVAL;
+
+ sas_device_priv_data->ncq_prio_enable = ncq_prio_enable;
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(sas_ncq_prio_enable);
+
+static struct attribute *mpt3sas_dev_attrs[] = {
+ &dev_attr_sas_address.attr,
+ &dev_attr_sas_device_handle.attr,
+ &dev_attr_sas_ncq_prio_supported.attr,
+ &dev_attr_sas_ncq_prio_enable.attr,
+ NULL,
+};
+
+static const struct attribute_group mpt3sas_dev_attr_group = {
+ .attrs = mpt3sas_dev_attrs
+};
+
+const struct attribute_group *mpt3sas_dev_groups[] = {
+ &mpt3sas_dev_attr_group,
+ NULL
+};
+
+/* file operations table for mpt3ctl device */
+static const struct file_operations ctl_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = _ctl_ioctl,
+ .poll = _ctl_poll,
+ .fasync = _ctl_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = _ctl_ioctl_compat,
+#endif
+};
+
+/* file operations table for mpt2ctl device */
+static const struct file_operations ctl_gen2_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = _ctl_mpt2_ioctl,
+ .poll = _ctl_poll,
+ .fasync = _ctl_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = _ctl_mpt2_ioctl_compat,
+#endif
+};
+
+static struct miscdevice ctl_dev = {
+ .minor = MPT3SAS_MINOR,
+ .name = MPT3SAS_DEV_NAME,
+ .fops = &ctl_fops,
+};
+
+static struct miscdevice gen2_ctl_dev = {
+ .minor = MPT2SAS_MINOR,
+ .name = MPT2SAS_DEV_NAME,
+ .fops = &ctl_gen2_fops,
+};
+
+/**
+ * mpt3sas_ctl_init - main entry point for ctl.
+ * @hbas_to_enumerate: ?
+ */
+void
+mpt3sas_ctl_init(ushort hbas_to_enumerate)
+{
+ async_queue = NULL;
+
+ /* Don't register mpt3ctl ioctl device if
+ * hbas_to_enumarate is one.
+ */
+ if (hbas_to_enumerate != 1)
+ if (misc_register(&ctl_dev) < 0)
+ pr_err("%s can't register misc device [minor=%d]\n",
+ MPT3SAS_DRIVER_NAME, MPT3SAS_MINOR);
+
+ /* Don't register mpt3ctl ioctl device if
+ * hbas_to_enumarate is two.
+ */
+ if (hbas_to_enumerate != 2)
+ if (misc_register(&gen2_ctl_dev) < 0)
+ pr_err("%s can't register misc device [minor=%d]\n",
+ MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR);
+
+ init_waitqueue_head(&ctl_poll_wait);
+}
+
+/**
+ * mpt3sas_ctl_release - release dma for ctl
+ * @ioc: per adapter object
+ */
+void
+mpt3sas_ctl_release(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i;
+
+ /* free memory associated to diag buffers */
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!ioc->diag_buffer[i])
+ continue;
+ dma_free_coherent(&ioc->pdev->dev,
+ ioc->diag_buffer_sz[i],
+ ioc->diag_buffer[i],
+ ioc->diag_buffer_dma[i]);
+ ioc->diag_buffer[i] = NULL;
+ ioc->diag_buffer_status[i] = 0;
+ }
+
+ kfree(ioc->event_log);
+}
+
+/**
+ * mpt3sas_ctl_exit - exit point for ctl
+ * @hbas_to_enumerate: ?
+ */
+void
+mpt3sas_ctl_exit(ushort hbas_to_enumerate)
+{
+
+ if (hbas_to_enumerate != 1)
+ misc_deregister(&ctl_dev);
+ if (hbas_to_enumerate != 2)
+ misc_deregister(&gen2_ctl_dev);
+}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt3sas/mpt3sas_ctl.h
index 11ff1d5fb8f0..483e0549c02f 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.h
@@ -2,9 +2,10 @@
* Management Module Support for MPT (Message Passing Technology) based
* controllers
*
- * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.h
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -42,80 +43,101 @@
* USA.
*/
-#ifndef MPT2SAS_CTL_H_INCLUDED
-#define MPT2SAS_CTL_H_INCLUDED
+#ifndef MPT3SAS_CTL_H_INCLUDED
+#define MPT3SAS_CTL_H_INCLUDED
#ifdef __KERNEL__
#include <linux/miscdevice.h>
#endif
+#include "mpt3sas_base.h"
+
+#ifndef MPT2SAS_MINOR
+#define MPT2SAS_MINOR (MPT_MINOR + 1)
+#endif
+#ifndef MPT3SAS_MINOR
+#define MPT3SAS_MINOR (MPT_MINOR + 2)
+#endif
#define MPT2SAS_DEV_NAME "mpt2ctl"
-#define MPT2_MAGIC_NUMBER 'L'
-#define MPT2_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */
+#define MPT3SAS_DEV_NAME "mpt3ctl"
+#define MPT3_MAGIC_NUMBER 'L'
+#define MPT3_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */
/**
* IOCTL opcodes
*/
-#define MPT2IOCINFO _IOWR(MPT2_MAGIC_NUMBER, 17, \
- struct mpt2_ioctl_iocinfo)
-#define MPT2COMMAND _IOWR(MPT2_MAGIC_NUMBER, 20, \
- struct mpt2_ioctl_command)
+#define MPT3IOCINFO _IOWR(MPT3_MAGIC_NUMBER, 17, \
+ struct mpt3_ioctl_iocinfo)
+#define MPT3COMMAND _IOWR(MPT3_MAGIC_NUMBER, 20, \
+ struct mpt3_ioctl_command)
#ifdef CONFIG_COMPAT
-#define MPT2COMMAND32 _IOWR(MPT2_MAGIC_NUMBER, 20, \
- struct mpt2_ioctl_command32)
+#define MPT3COMMAND32 _IOWR(MPT3_MAGIC_NUMBER, 20, \
+ struct mpt3_ioctl_command32)
#endif
-#define MPT2EVENTQUERY _IOWR(MPT2_MAGIC_NUMBER, 21, \
- struct mpt2_ioctl_eventquery)
-#define MPT2EVENTENABLE _IOWR(MPT2_MAGIC_NUMBER, 22, \
- struct mpt2_ioctl_eventenable)
-#define MPT2EVENTREPORT _IOWR(MPT2_MAGIC_NUMBER, 23, \
- struct mpt2_ioctl_eventreport)
-#define MPT2HARDRESET _IOWR(MPT2_MAGIC_NUMBER, 24, \
- struct mpt2_ioctl_diag_reset)
-#define MPT2BTDHMAPPING _IOWR(MPT2_MAGIC_NUMBER, 31, \
- struct mpt2_ioctl_btdh_mapping)
+#define MPT3EVENTQUERY _IOWR(MPT3_MAGIC_NUMBER, 21, \
+ struct mpt3_ioctl_eventquery)
+#define MPT3EVENTENABLE _IOWR(MPT3_MAGIC_NUMBER, 22, \
+ struct mpt3_ioctl_eventenable)
+#define MPT3EVENTREPORT _IOWR(MPT3_MAGIC_NUMBER, 23, \
+ struct mpt3_ioctl_eventreport)
+#define MPT3HARDRESET _IOWR(MPT3_MAGIC_NUMBER, 24, \
+ struct mpt3_ioctl_diag_reset)
+#define MPT3BTDHMAPPING _IOWR(MPT3_MAGIC_NUMBER, 31, \
+ struct mpt3_ioctl_btdh_mapping)
/* diag buffer support */
-#define MPT2DIAGREGISTER _IOWR(MPT2_MAGIC_NUMBER, 26, \
- struct mpt2_diag_register)
-#define MPT2DIAGRELEASE _IOWR(MPT2_MAGIC_NUMBER, 27, \
- struct mpt2_diag_release)
-#define MPT2DIAGUNREGISTER _IOWR(MPT2_MAGIC_NUMBER, 28, \
- struct mpt2_diag_unregister)
-#define MPT2DIAGQUERY _IOWR(MPT2_MAGIC_NUMBER, 29, \
- struct mpt2_diag_query)
-#define MPT2DIAGREADBUFFER _IOWR(MPT2_MAGIC_NUMBER, 30, \
- struct mpt2_diag_read_buffer)
+#define MPT3DIAGREGISTER _IOWR(MPT3_MAGIC_NUMBER, 26, \
+ struct mpt3_diag_register)
+#define MPT3DIAGRELEASE _IOWR(MPT3_MAGIC_NUMBER, 27, \
+ struct mpt3_diag_release)
+#define MPT3DIAGUNREGISTER _IOWR(MPT3_MAGIC_NUMBER, 28, \
+ struct mpt3_diag_unregister)
+#define MPT3DIAGQUERY _IOWR(MPT3_MAGIC_NUMBER, 29, \
+ struct mpt3_diag_query)
+#define MPT3DIAGREADBUFFER _IOWR(MPT3_MAGIC_NUMBER, 30, \
+ struct mpt3_diag_read_buffer)
+#define MPT3ADDNLDIAGQUERY _IOWR(MPT3_MAGIC_NUMBER, 32, \
+ struct mpt3_addnl_diag_query)
+#define MPT3ENABLEDIAGSBRRELOAD _IOWR(MPT3_MAGIC_NUMBER, 33, \
+ struct mpt3_enable_diag_sbr_reload)
+
+/* Trace Buffer default UniqueId */
+#define MPT2DIAGBUFFUNIQUEID (0x07075900)
+#define MPT3DIAGBUFFUNIQUEID (0x4252434D)
+
+/* UID not found */
+#define MPT3_DIAG_UID_NOT_FOUND (0xFF)
+
/**
- * struct mpt2_ioctl_header - main header structure
+ * struct mpt3_ioctl_header - main header structure
* @ioc_number - IOC unit number
* @port_number - IOC port number
* @max_data_size - maximum number bytes to transfer on read
*/
-struct mpt2_ioctl_header {
+struct mpt3_ioctl_header {
uint32_t ioc_number;
uint32_t port_number;
uint32_t max_data_size;
};
/**
- * struct mpt2_ioctl_diag_reset - diagnostic reset
+ * struct mpt3_ioctl_diag_reset - diagnostic reset
* @hdr - generic header
*/
-struct mpt2_ioctl_diag_reset {
- struct mpt2_ioctl_header hdr;
+struct mpt3_ioctl_diag_reset {
+ struct mpt3_ioctl_header hdr;
};
/**
- * struct mpt2_ioctl_pci_info - pci device info
+ * struct mpt3_ioctl_pci_info - pci device info
* @device - pci device id
* @function - pci function id
* @bus - pci bus id
* @segment_id - pci segment id
*/
-struct mpt2_ioctl_pci_info {
+struct mpt3_ioctl_pci_info {
union {
struct {
uint32_t device:5;
@@ -134,10 +156,15 @@ struct mpt2_ioctl_pci_info {
#define MPT2_IOCTL_INTERFACE_SAS (0x03)
#define MPT2_IOCTL_INTERFACE_SAS2 (0x04)
#define MPT2_IOCTL_INTERFACE_SAS2_SSS6200 (0x05)
+#define MPT3_IOCTL_INTERFACE_SAS3 (0x06)
+#define MPT3_IOCTL_INTERFACE_SAS35 (0x07)
#define MPT2_IOCTL_VERSION_LENGTH (32)
+/* Bits set for mpt3_ioctl_iocinfo.driver_cap */
+#define MPT3_IOCTL_IOCINFO_DRIVER_CAP_MCTP_PASSTHRU 0x1
+
/**
- * struct mpt2_ioctl_iocinfo - generic controller info
+ * struct mpt3_ioctl_iocinfo - generic controller info
* @hdr - generic header
* @adapter_type - type of adapter (spi, fc, sas)
* @port_number - port number
@@ -151,11 +178,12 @@ struct mpt2_ioctl_pci_info {
* @driver_version - driver version - 32 ASCII characters
* @rsvd1 - reserved
* @scsi_id - scsi id of adapter 0
+ * @driver_capability - driver capabilities
* @rsvd2 - reserved
* @pci_information - pci info (2nd revision)
*/
-struct mpt2_ioctl_iocinfo {
- struct mpt2_ioctl_header hdr;
+struct mpt3_ioctl_iocinfo {
+ struct mpt3_ioctl_header hdr;
uint32_t adapter_type;
uint32_t port_number;
uint32_t pci_id;
@@ -168,63 +196,64 @@ struct mpt2_ioctl_iocinfo {
uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH];
uint8_t rsvd1;
uint8_t scsi_id;
- uint16_t rsvd2;
- struct mpt2_ioctl_pci_info pci_information;
+ uint8_t driver_capability;
+ uint8_t rsvd2;
+ struct mpt3_ioctl_pci_info pci_information;
};
/* number of event log entries */
-#define MPT2SAS_CTL_EVENT_LOG_SIZE (50)
+#define MPT3SAS_CTL_EVENT_LOG_SIZE (200)
/**
- * struct mpt2_ioctl_eventquery - query event count and type
+ * struct mpt3_ioctl_eventquery - query event count and type
* @hdr - generic header
* @event_entries - number of events returned by get_event_report
* @rsvd - reserved
* @event_types - type of events currently being captured
*/
-struct mpt2_ioctl_eventquery {
- struct mpt2_ioctl_header hdr;
+struct mpt3_ioctl_eventquery {
+ struct mpt3_ioctl_header hdr;
uint16_t event_entries;
uint16_t rsvd;
uint32_t event_types[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
};
/**
- * struct mpt2_ioctl_eventenable - enable/disable event capturing
+ * struct mpt3_ioctl_eventenable - enable/disable event capturing
* @hdr - generic header
* @event_types - toggle off/on type of events to be captured
*/
-struct mpt2_ioctl_eventenable {
- struct mpt2_ioctl_header hdr;
+struct mpt3_ioctl_eventenable {
+ struct mpt3_ioctl_header hdr;
uint32_t event_types[4];
};
-#define MPT2_EVENT_DATA_SIZE (192)
+#define MPT3_EVENT_DATA_SIZE (192)
/**
- * struct MPT2_IOCTL_EVENTS -
+ * struct MPT3_IOCTL_EVENTS -
* @event - the event that was reported
* @context - unique value for each event assigned by driver
* @data - event data returned in fw reply message
*/
-struct MPT2_IOCTL_EVENTS {
+struct MPT3_IOCTL_EVENTS {
uint32_t event;
uint32_t context;
- uint8_t data[MPT2_EVENT_DATA_SIZE];
+ uint8_t data[MPT3_EVENT_DATA_SIZE];
};
/**
- * struct mpt2_ioctl_eventreport - returing event log
+ * struct mpt3_ioctl_eventreport - returing event log
* @hdr - generic header
- * @event_data - (see struct MPT2_IOCTL_EVENTS)
+ * @event_data - (see struct MPT3_IOCTL_EVENTS)
*/
-struct mpt2_ioctl_eventreport {
- struct mpt2_ioctl_header hdr;
- struct MPT2_IOCTL_EVENTS event_data[1];
+struct mpt3_ioctl_eventreport {
+ struct mpt3_ioctl_header hdr;
+ struct MPT3_IOCTL_EVENTS event_data[1];
};
/**
- * struct mpt2_ioctl_command - generic mpt firmware passthru ioclt
+ * struct mpt3_ioctl_command - generic mpt firmware passthru ioctl
* @hdr - generic header
* @timeout - command timeout in seconds. (if zero then use driver default
* value).
@@ -240,8 +269,8 @@ struct mpt2_ioctl_eventreport {
* the first SGL
* @mf[1];
*/
-struct mpt2_ioctl_command {
- struct mpt2_ioctl_header hdr;
+struct mpt3_ioctl_command {
+ struct mpt3_ioctl_header hdr;
uint32_t timeout;
void __user *reply_frame_buf_ptr;
void __user *data_in_buf_ptr;
@@ -256,8 +285,8 @@ struct mpt2_ioctl_command {
};
#ifdef CONFIG_COMPAT
-struct mpt2_ioctl_command32 {
- struct mpt2_ioctl_header hdr;
+struct mpt3_ioctl_command32 {
+ struct mpt3_ioctl_header hdr;
uint32_t timeout;
uint32_t reply_frame_buf_ptr;
uint32_t data_in_buf_ptr;
@@ -273,7 +302,7 @@ struct mpt2_ioctl_command32 {
#endif
/**
- * struct mpt2_ioctl_btdh_mapping - mapping info
+ * struct mpt3_ioctl_btdh_mapping - mapping info
* @hdr - generic header
* @id - target device identification number
* @bus - SCSI bus number that the target device exists on
@@ -286,8 +315,8 @@ struct mpt2_ioctl_command32 {
* To obtain the device handle the application sets
* bus/id valid value, and the handle to 0xFFFF.
*/
-struct mpt2_ioctl_btdh_mapping {
- struct mpt2_ioctl_header hdr;
+struct mpt3_ioctl_btdh_mapping {
+ struct mpt3_ioctl_header hdr;
uint32_t id;
uint32_t bus;
uint16_t handle;
@@ -295,23 +324,20 @@ struct mpt2_ioctl_btdh_mapping {
};
-/* status bits for ioc->diag_buffer_status */
-#define MPT2_DIAG_BUFFER_IS_REGISTERED (0x01)
-#define MPT2_DIAG_BUFFER_IS_RELEASED (0x02)
-#define MPT2_DIAG_BUFFER_IS_DIAG_RESET (0x04)
-/* application flags for mpt2_diag_register, mpt2_diag_query */
-#define MPT2_APP_FLAGS_APP_OWNED (0x0001)
-#define MPT2_APP_FLAGS_BUFFER_VALID (0x0002)
-#define MPT2_APP_FLAGS_FW_BUFFER_ACCESS (0x0004)
+/* application flags for mpt3_diag_register, mpt3_diag_query */
+#define MPT3_APP_FLAGS_APP_OWNED (0x0001)
+#define MPT3_APP_FLAGS_BUFFER_VALID (0x0002)
+#define MPT3_APP_FLAGS_FW_BUFFER_ACCESS (0x0004)
+#define MPT3_APP_FLAGS_DYNAMIC_BUFFER_ALLOC (0x0008)
-/* flags for mpt2_diag_read_buffer */
-#define MPT2_FLAGS_REREGISTER (0x0001)
+/* flags for mpt3_diag_read_buffer */
+#define MPT3_FLAGS_REREGISTER (0x0001)
-#define MPT2_PRODUCT_SPECIFIC_DWORDS 23
+#define MPT3_PRODUCT_SPECIFIC_DWORDS 23
/**
- * struct mpt2_diag_register - application register with driver
+ * struct mpt3_diag_register - application register with driver
* @hdr - generic header
* @reserved -
* @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
@@ -325,32 +351,32 @@ struct mpt2_ioctl_btdh_mapping {
* This will allow the driver to setup any required buffers that will be
* needed by firmware to communicate with the driver.
*/
-struct mpt2_diag_register {
- struct mpt2_ioctl_header hdr;
+struct mpt3_diag_register {
+ struct mpt3_ioctl_header hdr;
uint8_t reserved;
uint8_t buffer_type;
uint16_t application_flags;
uint32_t diagnostic_flags;
- uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS];
+ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS];
uint32_t requested_buffer_size;
uint32_t unique_id;
};
/**
- * struct mpt2_diag_unregister - application unregister with driver
+ * struct mpt3_diag_unregister - application unregister with driver
* @hdr - generic header
* @unique_id - tag uniquely identifies the buffer to be unregistered
*
* This will allow the driver to cleanup any memory allocated for diag
* messages and to free up any resources.
*/
-struct mpt2_diag_unregister {
- struct mpt2_ioctl_header hdr;
+struct mpt3_diag_unregister {
+ struct mpt3_ioctl_header hdr;
uint32_t unique_id;
};
/**
- * struct mpt2_diag_query - query relevant info associated with diag buffers
+ * struct mpt3_diag_query - query relevant info associated with diag buffers
* @hdr - generic header
* @reserved -
* @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
@@ -365,34 +391,34 @@ struct mpt2_diag_unregister {
* inspect unique_id first, if valid, fill in all the info. If unique_id is
* 0x00, the driver will return info specified by Buffer Type.
*/
-struct mpt2_diag_query {
- struct mpt2_ioctl_header hdr;
+struct mpt3_diag_query {
+ struct mpt3_ioctl_header hdr;
uint8_t reserved;
uint8_t buffer_type;
uint16_t application_flags;
uint32_t diagnostic_flags;
- uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS];
+ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS];
uint32_t total_buffer_size;
uint32_t driver_added_buffer_size;
uint32_t unique_id;
};
/**
- * struct mpt2_diag_release - request to send Diag Release Message to firmware
+ * struct mpt3_diag_release - request to send Diag Release Message to firmware
* @hdr - generic header
* @unique_id - tag uniquely identifies the buffer to be released
*
* This allows ownership of the specified buffer to returned to the driver,
* allowing an application to read the buffer without fear that firmware is
- * overwritting information in the buffer.
+ * overwriting information in the buffer.
*/
-struct mpt2_diag_release {
- struct mpt2_ioctl_header hdr;
+struct mpt3_diag_release {
+ struct mpt3_ioctl_header hdr;
uint32_t unique_id;
};
/**
- * struct mpt2_diag_read_buffer - request for copy of the diag buffer
+ * struct mpt3_diag_read_buffer - request for copy of the diag buffer
* @hdr - generic header
* @status -
* @reserved -
@@ -404,8 +430,8 @@ struct mpt2_diag_release {
* @unique_id - unique id associated with this buffer.
* @diagnostic_data - data payload
*/
-struct mpt2_diag_read_buffer {
- struct mpt2_ioctl_header hdr;
+struct mpt3_diag_read_buffer {
+ struct mpt3_ioctl_header hdr;
uint8_t status;
uint8_t reserved;
uint16_t flags;
@@ -415,4 +441,68 @@ struct mpt2_diag_read_buffer {
uint32_t diagnostic_data[1];
};
-#endif /* MPT2SAS_CTL_H_INCLUDED */
+/**
+ * struct mpt3_addnl_diag_query - diagnostic buffer release reason
+ * @hdr - generic header
+ * @unique_id - unique id associated with this buffer.
+ * @rel_query - release query.
+ * @reserved2
+ */
+struct mpt3_addnl_diag_query {
+ struct mpt3_ioctl_header hdr;
+ uint32_t unique_id;
+ struct htb_rel_query rel_query;
+ uint32_t reserved2[2];
+};
+
+/**
+ * struct mpt3_enable_diag_sbr_reload - enable sbr reload
+ * @hdr - generic header
+ */
+struct mpt3_enable_diag_sbr_reload {
+ struct mpt3_ioctl_header hdr;
+};
+
+/**
+ * struct mpt3_passthru_command - generic mpt firmware passthru command
+ * @dev_index - device index
+ * @timeout - command timeout in seconds. (if zero then use driver default
+ * value).
+ * @reply_frame_buf_ptr - MPI reply location
+ * @data_in_buf_ptr - destination for read
+ * @data_out_buf_ptr - data source for write
+ * @max_reply_bytes - maximum number of reply bytes to be sent to app.
+ * @data_in_size - number bytes for data transfer in (read)
+ * @data_out_size - number bytes for data transfer out (write)
+ * @mpi_request - request frame
+ */
+struct mpt3_passthru_command {
+ u8 dev_index;
+ uint32_t timeout;
+ void *reply_frame_buf_ptr;
+ void *data_in_buf_ptr;
+ void *data_out_buf_ptr;
+ uint32_t max_reply_bytes;
+ uint32_t data_in_size;
+ uint32_t data_out_size;
+ Mpi26MctpPassthroughRequest_t *mpi_request;
+};
+
+/*
+ * mpt3sas_get_device_count - Retrieve the count of MCTP passthrough
+ * capable devices managed by the driver.
+ *
+ * Returns number of devices that support MCTP passthrough.
+ */
+int mpt3sas_get_device_count(void);
+
+/*
+ * mpt3sas_send_passthru_cmd - Send an MPI MCTP passthrough command to
+ * firmware
+ * @command: The MPI MCTP passthrough command to send to firmware
+ *
+ * Returns 0 on success, anything else is error .
+ */
+int mpt3sas_send_mctp_passthru_req(struct mpt3_passthru_command *command);
+
+#endif /* MPT3SAS_CTL_H_INCLUDED */
diff --git a/drivers/scsi/mpt2sas/mpt2sas_debug.h b/drivers/scsi/mpt3sas/mpt3sas_debug.h
index 3dcddfeb6f4c..cceeb2c16e64 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_debug.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_debug.h
@@ -1,9 +1,10 @@
/*
* Logging Support for MPT (Message Passing Technology) based controllers
*
- * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c
- * Copyright (C) 2007-2010 LSI Corporation
- * (mailto:DL-MPTFusionLinux@lsi.com)
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_debug.c
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -41,8 +42,8 @@
* USA.
*/
-#ifndef MPT2SAS_DEBUG_H_INCLUDED
-#define MPT2SAS_DEBUG_H_INCLUDED
+#ifndef MPT3SAS_DEBUG_H_INCLUDED
+#define MPT3SAS_DEBUG_H_INCLUDED
#define MPT_DEBUG 0x00000001
#define MPT_DEBUG_MSG_FRAME 0x00000002
@@ -53,35 +54,25 @@
#define MPT_DEBUG_EXIT 0x00000040
#define MPT_DEBUG_FAIL 0x00000080
#define MPT_DEBUG_TM 0x00000100
-#define MPT_DEBUG_REPLY 0x00000200
+#define MPT_DEBUG_REPLY 0x00000200
#define MPT_DEBUG_HANDSHAKE 0x00000400
#define MPT_DEBUG_CONFIG 0x00000800
#define MPT_DEBUG_DL 0x00001000
-#define MPT_DEBUG_RESET 0x00002000
+#define MPT_DEBUG_RESET 0x00002000
#define MPT_DEBUG_SCSI 0x00004000
-#define MPT_DEBUG_IOCTL 0x00008000
-#define MPT_DEBUG_CSMISAS 0x00010000
+#define MPT_DEBUG_IOCTL 0x00008000
#define MPT_DEBUG_SAS 0x00020000
#define MPT_DEBUG_TRANSPORT 0x00040000
-#define MPT_DEBUG_TASK_SET_FULL 0x00080000
+#define MPT_DEBUG_TASK_SET_FULL 0x00080000
-#define MPT_DEBUG_TARGET_MODE 0x00100000
+#define MPT_DEBUG_TRIGGER_DIAG 0x00200000
-/*
- * CONFIG_SCSI_MPT2SAS_LOGGING - enabled in Kconfig
- */
-
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
#define MPT_CHECK_LOGGING(IOC, CMD, BITS) \
{ \
if (IOC->logging_level & BITS) \
CMD; \
}
-#else
-#define MPT_CHECK_LOGGING(IOC, CMD, BITS)
-#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */
-
/*
* debug macros
@@ -132,9 +123,6 @@
#define dctlprintk(IOC, CMD) \
MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL)
-#define dcsmisasprintk(IOC, CMD) \
- MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CSMISAS)
-
#define dsasprintk(IOC, CMD) \
MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS)
@@ -150,11 +138,13 @@
#define dtransportprintk(IOC, CMD) \
MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRANSPORT)
-#define dTMprintk(IOC, CMD) \
- MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TARGET_MODE)
+#define dTriggerDiagPrintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRIGGER_DIAG)
+
+
/* inline functions for dumping debug data*/
-#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+
/**
* _debug_dump_mf - print message frame contents
* @mpi_request: pointer to message frame
@@ -164,18 +154,53 @@ static inline void
_debug_dump_mf(void *mpi_request, int sz)
{
int i;
- u32 *mfp = (u32 *)mpi_request;
+ __le32 *mfp = (__le32 *)mpi_request;
+
+ pr_info("mf:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
+ }
+ pr_info("\n");
+}
+/**
+ * _debug_dump_reply - print message frame contents
+ * @mpi_request: pointer to message frame
+ * @sz: number of dwords
+ */
+static inline void
+_debug_dump_reply(void *mpi_request, int sz)
+{
+ int i;
+ __le32 *mfp = (__le32 *)mpi_request;
+
+ pr_info("reply:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
+ }
+ pr_info("\n");
+}
+/**
+ * _debug_dump_config - print config page contents
+ * @mpi_request: pointer to message frame
+ * @sz: number of dwords
+ */
+static inline void
+_debug_dump_config(void *mpi_request, int sz)
+{
+ int i;
+ __le32 *mfp = (__le32 *)mpi_request;
- printk(KERN_INFO "mf:\n\t");
+ pr_info("config:\n\t");
for (i = 0; i < sz; i++) {
if (i && ((i % 8) == 0))
- printk("\n\t");
- printk("%08x ", le32_to_cpu(mfp[i]));
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
}
- printk("\n");
+ pr_info("\n");
}
-#else
-#define _debug_dump_mf(mpi_request, sz)
-#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */
-#endif /* MPT2SAS_DEBUG_H_INCLUDED */
+#endif /* MPT3SAS_DEBUG_H_INCLUDED */
diff --git a/drivers/scsi/mpt3sas/mpt3sas_debugfs.c b/drivers/scsi/mpt3sas/mpt3sas_debugfs.c
new file mode 100644
index 000000000000..a6ab1db81167
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_debugfs.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Debugfs interface Support for MPT (Message Passing Technology) based
+ * controllers.
+ *
+ * Copyright (C) 2020 Broadcom Inc.
+ *
+ * Authors: Broadcom Inc.
+ * Sreekanth Reddy <sreekanth.reddy@broadcom.com>
+ * Suganath Prabu <suganath-prabu.subramani@broadcom.com>
+ *
+ * Send feedback to : MPT-FusionLinux.pdl@broadcom.com)
+ *
+ **/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/compat.h>
+#include <linux/uio.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include "mpt3sas_base.h"
+#include <linux/debugfs.h>
+
+static struct dentry *mpt3sas_debugfs_root;
+
+/*
+ * _debugfs_iocdump_read - copy ioc dump from debugfs buffer
+ * @filep: File Pointer
+ * @ubuf: Buffer to fill data
+ * @cnt: Length of the buffer
+ * @ppos: Offset in the file
+ */
+
+static ssize_t
+_debugfs_iocdump_read(struct file *filp, char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+
+{
+ struct mpt3sas_debugfs_buffer *debug = filp->private_data;
+
+ if (!debug || !debug->buf)
+ return 0;
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, debug->buf, debug->len);
+}
+
+/*
+ * _debugfs_iocdump_open : open the ioc_dump debugfs attribute file
+ */
+static int
+_debugfs_iocdump_open(struct inode *inode, struct file *file)
+{
+ struct MPT3SAS_ADAPTER *ioc = inode->i_private;
+ struct mpt3sas_debugfs_buffer *debug;
+
+ debug = kzalloc(sizeof(struct mpt3sas_debugfs_buffer), GFP_KERNEL);
+ if (!debug)
+ return -ENOMEM;
+
+ debug->buf = (void *)ioc;
+ debug->len = sizeof(struct MPT3SAS_ADAPTER);
+ file->private_data = debug;
+ return 0;
+}
+
+/*
+ * _debugfs_iocdump_release : release the ioc_dump debugfs attribute
+ * @inode: inode structure to the corresponds device
+ * @file: File pointer
+ */
+static int
+_debugfs_iocdump_release(struct inode *inode, struct file *file)
+{
+ struct mpt3sas_debugfs_buffer *debug = file->private_data;
+
+ if (!debug)
+ return 0;
+
+ file->private_data = NULL;
+ kfree(debug);
+ return 0;
+}
+
+static const struct file_operations mpt3sas_debugfs_iocdump_fops = {
+ .owner = THIS_MODULE,
+ .open = _debugfs_iocdump_open,
+ .read = _debugfs_iocdump_read,
+ .release = _debugfs_iocdump_release,
+};
+
+/*
+ * mpt3sas_init_debugfs : Create debugfs root for mpt3sas driver
+ */
+void mpt3sas_init_debugfs(void)
+{
+ mpt3sas_debugfs_root = debugfs_create_dir("mpt3sas", NULL);
+ if (!mpt3sas_debugfs_root)
+ pr_info("mpt3sas: Cannot create debugfs root\n");
+}
+
+/*
+ * mpt3sas_exit_debugfs : Remove debugfs root for mpt3sas driver
+ */
+void mpt3sas_exit_debugfs(void)
+{
+ debugfs_remove_recursive(mpt3sas_debugfs_root);
+}
+
+/*
+ * mpt3sas_setup_debugfs : Setup debugfs per HBA adapter
+ * ioc: MPT3SAS_ADAPTER object
+ */
+void
+mpt3sas_setup_debugfs(struct MPT3SAS_ADAPTER *ioc)
+{
+ char name[64];
+
+ snprintf(name, sizeof(name), "scsi_host%d", ioc->shost->host_no);
+ if (!ioc->debugfs_root) {
+ ioc->debugfs_root =
+ debugfs_create_dir(name, mpt3sas_debugfs_root);
+ if (!ioc->debugfs_root) {
+ dev_err(&ioc->pdev->dev,
+ "Cannot create per adapter debugfs directory\n");
+ return;
+ }
+ }
+
+ snprintf(name, sizeof(name), "ioc_dump");
+ ioc->ioc_dump = debugfs_create_file(name, 0444,
+ ioc->debugfs_root, ioc, &mpt3sas_debugfs_iocdump_fops);
+ if (!ioc->ioc_dump) {
+ dev_err(&ioc->pdev->dev,
+ "Cannot create ioc_dump debugfs file\n");
+ debugfs_remove(ioc->debugfs_root);
+ return;
+ }
+
+ snprintf(name, sizeof(name), "host_recovery");
+ debugfs_create_u8(name, 0444, ioc->debugfs_root, &ioc->shost_recovery);
+
+}
+
+/*
+ * mpt3sas_destroy_debugfs : Destroy debugfs per HBA adapter
+ * @ioc: MPT3SAS_ADAPTER object
+ */
+void mpt3sas_destroy_debugfs(struct MPT3SAS_ADAPTER *ioc)
+{
+ debugfs_remove_recursive(ioc->debugfs_root);
+}
+
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
new file mode 100644
index 000000000000..7092d0debef3
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -0,0 +1,12909 @@
+/*
+ * Scsi Host Layer for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_scsih.c
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/raid_class.h>
+#include <linux/unaligned.h>
+
+#include "mpt3sas_base.h"
+
+#define RAID_CHANNEL 1
+
+#define PCIE_CHANNEL 2
+
+/* forward proto's */
+static void _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander);
+static void _firmware_event_work(struct work_struct *work);
+
+static void _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device);
+static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u8 retry_count, u8 is_pd);
+static int _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle);
+static void _scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc,
+ struct _pcie_device *pcie_device);
+static void
+_scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle);
+static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+static void _scsih_complete_devices_scanning(struct MPT3SAS_ADAPTER *ioc);
+
+/* global parameters */
+LIST_HEAD(mpt3sas_ioc_list);
+/* global ioc lock for list operations */
+DEFINE_SPINLOCK(gioc_lock);
+
+MODULE_AUTHOR(MPT3SAS_AUTHOR);
+MODULE_DESCRIPTION(MPT3SAS_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MPT3SAS_DRIVER_VERSION);
+MODULE_ALIAS("mpt2sas");
+
+/* local parameters */
+static u8 scsi_io_cb_idx = -1;
+static u8 tm_cb_idx = -1;
+static u8 ctl_cb_idx = -1;
+static u8 base_cb_idx = -1;
+static u8 port_enable_cb_idx = -1;
+static u8 transport_cb_idx = -1;
+static u8 scsih_cb_idx = -1;
+static u8 config_cb_idx = -1;
+static int mpt2_ids;
+static int mpt3_ids;
+
+static u8 tm_tr_cb_idx = -1 ;
+static u8 tm_tr_volume_cb_idx = -1 ;
+static u8 tm_sas_control_cb_idx = -1;
+
+/* command line options */
+static u32 logging_level;
+MODULE_PARM_DESC(logging_level,
+ " bits for enabling additional logging info (default=0)");
+
+
+static ushort max_sectors = 0xFFFF;
+module_param(max_sectors, ushort, 0444);
+MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767");
+
+
+static int missing_delay[2] = {-1, -1};
+module_param_array(missing_delay, int, NULL, 0444);
+MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay");
+
+/* scsi-mid layer global parmeter is max_report_luns, which is 511 */
+#define MPT3SAS_MAX_LUN (16895)
+static u64 max_lun = MPT3SAS_MAX_LUN;
+module_param(max_lun, ullong, 0444);
+MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
+
+static ushort hbas_to_enumerate;
+module_param(hbas_to_enumerate, ushort, 0444);
+MODULE_PARM_DESC(hbas_to_enumerate,
+ " 0 - enumerates both SAS 2.0 & SAS 3.0 generation HBAs\n \
+ 1 - enumerates only SAS 2.0 generation HBAs\n \
+ 2 - enumerates only SAS 3.0 generation HBAs (default=0)");
+
+/* diag_buffer_enable is bitwise
+ * bit 0 set = TRACE
+ * bit 1 set = SNAPSHOT
+ * bit 2 set = EXTENDED
+ *
+ * Either bit can be set, or both
+ */
+static int diag_buffer_enable = -1;
+module_param(diag_buffer_enable, int, 0444);
+MODULE_PARM_DESC(diag_buffer_enable,
+ " post diag buffers (TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
+static int disable_discovery = -1;
+module_param(disable_discovery, int, 0444);
+MODULE_PARM_DESC(disable_discovery, " disable discovery ");
+
+
+/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */
+static int prot_mask = -1;
+module_param(prot_mask, int, 0444);
+MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 ");
+
+static bool enable_sdev_max_qd;
+module_param(enable_sdev_max_qd, bool, 0444);
+MODULE_PARM_DESC(enable_sdev_max_qd,
+ "Enable sdev max qd as can_queue, def=disabled(0)");
+
+static int multipath_on_hba = -1;
+module_param(multipath_on_hba, int, 0);
+MODULE_PARM_DESC(multipath_on_hba,
+ "Multipath support to add same target device\n\t\t"
+ "as many times as it is visible to HBA from various paths\n\t\t"
+ "(by default:\n\t\t"
+ "\t SAS 2.0 & SAS 3.0 HBA - This will be disabled,\n\t\t"
+ "\t SAS 3.5 HBA - This will be enabled)");
+
+static int host_tagset_enable = 1;
+module_param(host_tagset_enable, int, 0444);
+MODULE_PARM_DESC(host_tagset_enable,
+ "Shared host tagset enable/disable Default: enable(1)");
+
+/* raid transport support */
+static struct raid_template *mpt3sas_raid_template;
+static struct raid_template *mpt2sas_raid_template;
+
+
+/**
+ * struct sense_info - common structure for obtaining sense keys
+ * @skey: sense key
+ * @asc: additional sense code
+ * @ascq: additional sense code qualifier
+ */
+struct sense_info {
+ u8 skey;
+ u8 asc;
+ u8 ascq;
+};
+
+#define MPT3SAS_PROCESS_TRIGGER_DIAG (0xFFFB)
+#define MPT3SAS_TURN_ON_PFA_LED (0xFFFC)
+#define MPT3SAS_PORT_ENABLE_COMPLETE (0xFFFD)
+#define MPT3SAS_ABRT_TASK_SET (0xFFFE)
+#define MPT3SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF)
+
+/*
+ * SAS Log info code for a NCQ collateral abort after an NCQ error:
+ * IOC_LOGINFO_PREFIX_PL | PL_LOGINFO_CODE_SATA_NCQ_FAIL_ALL_CMDS_AFTR_ERR
+ * See: drivers/message/fusion/lsi/mpi_log_sas.h
+ */
+#define IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR 0x31080000
+
+/**
+ * struct fw_event_work - firmware event struct
+ * @list: link list framework
+ * @work: work object (ioc->fault_reset_work_q)
+ * @ioc: per adapter object
+ * @device_handle: device handle
+ * @VF_ID: virtual function id
+ * @VP_ID: virtual port id
+ * @ignore: flag meaning this event has been marked to ignore
+ * @event: firmware event MPI2_EVENT_XXX defined in mpi2_ioc.h
+ * @refcount: kref for this event
+ * @event_data: reply event data payload follows
+ *
+ * This object stored on ioc->fw_event_list.
+ */
+struct fw_event_work {
+ struct list_head list;
+ struct work_struct work;
+
+ struct MPT3SAS_ADAPTER *ioc;
+ u16 device_handle;
+ u8 VF_ID;
+ u8 VP_ID;
+ u8 ignore;
+ u16 event;
+ struct kref refcount;
+ char event_data[] __aligned(4);
+};
+
+static void fw_event_work_free(struct kref *r)
+{
+ kfree(container_of(r, struct fw_event_work, refcount));
+}
+
+static void fw_event_work_get(struct fw_event_work *fw_work)
+{
+ kref_get(&fw_work->refcount);
+}
+
+static void fw_event_work_put(struct fw_event_work *fw_work)
+{
+ kref_put(&fw_work->refcount, fw_event_work_free);
+}
+
+static struct fw_event_work *alloc_fw_event_work(int len)
+{
+ struct fw_event_work *fw_event;
+
+ fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC);
+ if (!fw_event)
+ return NULL;
+
+ kref_init(&fw_event->refcount);
+ return fw_event;
+}
+
+/**
+ * struct _scsi_io_transfer - scsi io transfer
+ * @handle: sas device handle (assigned by firmware)
+ * @is_raid: flag set for hidden raid components
+ * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE,
+ * @data_length: data transfer length
+ * @data_dma: dma pointer to data
+ * @sense: sense data
+ * @lun: lun number
+ * @cdb_length: cdb length
+ * @cdb: cdb contents
+ * @timeout: timeout for this command
+ * @VF_ID: virtual function id
+ * @VP_ID: virtual port id
+ * @valid_reply: flag set for reply message
+ * @sense_length: sense length
+ * @ioc_status: ioc status
+ * @scsi_state: scsi state
+ * @scsi_status: scsi staus
+ * @log_info: log information
+ * @transfer_length: data length transfer when there is a reply message
+ *
+ * Used for sending internal scsi commands to devices within this module.
+ * Refer to _scsi_send_scsi_io().
+ */
+struct _scsi_io_transfer {
+ u16 handle;
+ u8 is_raid;
+ enum dma_data_direction dir;
+ u32 data_length;
+ dma_addr_t data_dma;
+ u8 sense[SCSI_SENSE_BUFFERSIZE];
+ u32 lun;
+ u8 cdb_length;
+ u8 cdb[32];
+ u8 timeout;
+ u8 VF_ID;
+ u8 VP_ID;
+ u8 valid_reply;
+ /* the following bits are only valid when 'valid_reply = 1' */
+ u32 sense_length;
+ u16 ioc_status;
+ u8 scsi_state;
+ u8 scsi_status;
+ u32 log_info;
+ u32 transfer_length;
+};
+
+/**
+ * _scsih_set_debug_level - global setting of ioc->logging_level.
+ * @val: value of the parameter to be set
+ * @kp: pointer to kernel_param structure
+ *
+ * Note: The logging levels are defined in mpt3sas_debug.h.
+ */
+static int
+_scsih_set_debug_level(const char *val, const struct kernel_param *kp)
+{
+ int ret = param_set_int(val, kp);
+ struct MPT3SAS_ADAPTER *ioc;
+
+ if (ret)
+ return ret;
+
+ pr_info("setting logging_level(0x%08x)\n", logging_level);
+ spin_lock(&gioc_lock);
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
+ ioc->logging_level = logging_level;
+ spin_unlock(&gioc_lock);
+ return 0;
+}
+module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
+ &logging_level, 0644);
+
+/**
+ * _scsih_srch_boot_sas_address - search based on sas_address
+ * @sas_address: sas address
+ * @boot_device: boot device object from bios page 2
+ *
+ * Return: 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_sas_address(u64 sas_address,
+ Mpi2BootDeviceSasWwid_t *boot_device)
+{
+ return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0;
+}
+
+/**
+ * _scsih_srch_boot_device_name - search based on device name
+ * @device_name: device name specified in INDENTIFY fram
+ * @boot_device: boot device object from bios page 2
+ *
+ * Return: 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_device_name(u64 device_name,
+ Mpi2BootDeviceDeviceName_t *boot_device)
+{
+ return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0;
+}
+
+/**
+ * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot
+ * @enclosure_logical_id: enclosure logical id
+ * @slot_number: slot number
+ * @boot_device: boot device object from bios page 2
+ *
+ * Return: 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number,
+ Mpi2BootDeviceEnclosureSlot_t *boot_device)
+{
+ return (enclosure_logical_id == le64_to_cpu(boot_device->
+ EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device->
+ SlotNumber)) ? 1 : 0;
+}
+
+/**
+ * mpt3sas_get_port_by_id - get hba port entry corresponding to provided
+ * port number from port list
+ * @ioc: per adapter object
+ * @port_id: port number
+ * @bypass_dirty_port_flag: when set look the matching hba port entry even
+ * if hba port entry is marked as dirty.
+ *
+ * Search for hba port entry corresponding to provided port number,
+ * if available return port object otherwise return NULL.
+ */
+struct hba_port *
+mpt3sas_get_port_by_id(struct MPT3SAS_ADAPTER *ioc,
+ u8 port_id, u8 bypass_dirty_port_flag)
+{
+ struct hba_port *port, *port_next;
+
+ /*
+ * When multipath_on_hba is disabled then
+ * search the hba_port entry using default
+ * port id i.e. 255
+ */
+ if (!ioc->multipath_on_hba)
+ port_id = MULTIPATH_DISABLED_PORT_ID;
+
+ list_for_each_entry_safe(port, port_next,
+ &ioc->port_table_list, list) {
+ if (port->port_id != port_id)
+ continue;
+ if (bypass_dirty_port_flag)
+ return port;
+ if (port->flags & HBA_PORT_FLAG_DIRTY_PORT)
+ continue;
+ return port;
+ }
+
+ /*
+ * Allocate hba_port object for default port id (i.e. 255)
+ * when multipath_on_hba is disabled for the HBA.
+ * And add this object to port_table_list.
+ */
+ if (!ioc->multipath_on_hba) {
+ port = kzalloc(sizeof(struct hba_port), GFP_ATOMIC);
+ if (!port)
+ return NULL;
+
+ port->port_id = port_id;
+ ioc_info(ioc,
+ "hba_port entry: %p, port: %d is added to hba_port list\n",
+ port, port->port_id);
+ list_add_tail(&port->list,
+ &ioc->port_table_list);
+ return port;
+ }
+ return NULL;
+}
+
+/**
+ * mpt3sas_get_vphy_by_phy - get virtual_phy object corresponding to phy number
+ * @ioc: per adapter object
+ * @port: hba_port object
+ * @phy: phy number
+ *
+ * Return virtual_phy object corresponding to phy number.
+ */
+struct virtual_phy *
+mpt3sas_get_vphy_by_phy(struct MPT3SAS_ADAPTER *ioc,
+ struct hba_port *port, u32 phy)
+{
+ struct virtual_phy *vphy, *vphy_next;
+
+ if (!port->vphys_mask)
+ return NULL;
+
+ list_for_each_entry_safe(vphy, vphy_next, &port->vphys_list, list) {
+ if (vphy->phy_mask & (1 << phy))
+ return vphy;
+ }
+ return NULL;
+}
+
+/**
+ * _scsih_is_boot_device - search for matching boot device.
+ * @sas_address: sas address
+ * @device_name: device name specified in INDENTIFY fram
+ * @enclosure_logical_id: enclosure logical id
+ * @slot: slot number
+ * @form: specifies boot device form
+ * @boot_device: boot device object from bios page 2
+ *
+ * Return: 1 when there's a match, 0 means no match.
+ */
+static int
+_scsih_is_boot_device(u64 sas_address, u64 device_name,
+ u64 enclosure_logical_id, u16 slot, u8 form,
+ Mpi2BiosPage2BootDevice_t *boot_device)
+{
+ int rc = 0;
+
+ switch (form) {
+ case MPI2_BIOSPAGE2_FORM_SAS_WWID:
+ if (!sas_address)
+ break;
+ rc = _scsih_srch_boot_sas_address(
+ sas_address, &boot_device->SasWwid);
+ break;
+ case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT:
+ if (!enclosure_logical_id)
+ break;
+ rc = _scsih_srch_boot_encl_slot(
+ enclosure_logical_id,
+ slot, &boot_device->EnclosureSlot);
+ break;
+ case MPI2_BIOSPAGE2_FORM_DEVICE_NAME:
+ if (!device_name)
+ break;
+ rc = _scsih_srch_boot_device_name(
+ device_name, &boot_device->DeviceName);
+ break;
+ case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED:
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * _scsih_get_sas_address - set the sas_address for given device handle
+ * @ioc: ?
+ * @handle: device handle
+ * @sas_address: sas address
+ *
+ * Return: 0 success, non-zero when failure
+ */
+static int
+_scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u64 *sas_address)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 ioc_status;
+
+ *sas_address = 0;
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ /* For HBA, vSES doesn't return HBA SAS address. Instead return
+ * vSES's sas address.
+ */
+ if ((handle <= ioc->sas_hba.num_phys) &&
+ (!(le32_to_cpu(sas_device_pg0.DeviceInfo) &
+ MPI2_SAS_DEVICE_INFO_SEP)))
+ *sas_address = ioc->sas_hba.sas_address;
+ else
+ *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ return 0;
+ }
+
+ /* we hit this because the given parent handle doesn't exist */
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ return -ENXIO;
+
+ /* else error case */
+ ioc_err(ioc, "handle(0x%04x), ioc_status(0x%04x), failure at %s:%d/%s()!\n",
+ handle, ioc_status, __FILE__, __LINE__, __func__);
+ return -EIO;
+}
+
+/**
+ * _scsih_determine_boot_device - determine boot device.
+ * @ioc: per adapter object
+ * @device: sas_device or pcie_device object
+ * @channel: SAS or PCIe channel
+ *
+ * Determines whether this device should be first reported device to
+ * to scsi-ml or sas transport, this purpose is for persistent boot device.
+ * There are primary, alternate, and current entries in bios page 2. The order
+ * priority is primary, alternate, then current. This routine saves
+ * the corresponding device object.
+ * The saved data to be used later in _scsih_probe_boot_devices().
+ */
+static void
+_scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, void *device,
+ u32 channel)
+{
+ struct _sas_device *sas_device;
+ struct _pcie_device *pcie_device;
+ struct _raid_device *raid_device;
+ u64 sas_address;
+ u64 device_name;
+ u64 enclosure_logical_id;
+ u16 slot;
+
+ /* only process this function when driver loads */
+ if (!ioc->is_driver_loading)
+ return;
+
+ /* no Bios, return immediately */
+ if (!ioc->bios_pg3.BiosVersion)
+ return;
+
+ if (channel == RAID_CHANNEL) {
+ raid_device = device;
+ sas_address = raid_device->wwid;
+ device_name = 0;
+ enclosure_logical_id = 0;
+ slot = 0;
+ } else if (channel == PCIE_CHANNEL) {
+ pcie_device = device;
+ sas_address = pcie_device->wwid;
+ device_name = 0;
+ enclosure_logical_id = 0;
+ slot = 0;
+ } else {
+ sas_device = device;
+ sas_address = sas_device->sas_address;
+ device_name = sas_device->device_name;
+ enclosure_logical_id = sas_device->enclosure_logical_id;
+ slot = sas_device->slot;
+ }
+
+ if (!ioc->req_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.ReqBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.RequestedBootDevice)) {
+ dinitprintk(ioc,
+ ioc_info(ioc, "%s: req_boot_device(0x%016llx)\n",
+ __func__, (u64)sas_address));
+ ioc->req_boot_device.device = device;
+ ioc->req_boot_device.channel = channel;
+ }
+ }
+
+ if (!ioc->req_alt_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.ReqAltBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.RequestedAltBootDevice)) {
+ dinitprintk(ioc,
+ ioc_info(ioc, "%s: req_alt_boot_device(0x%016llx)\n",
+ __func__, (u64)sas_address));
+ ioc->req_alt_boot_device.device = device;
+ ioc->req_alt_boot_device.channel = channel;
+ }
+ }
+
+ if (!ioc->current_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.CurrentBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.CurrentBootDevice)) {
+ dinitprintk(ioc,
+ ioc_info(ioc, "%s: current_boot_device(0x%016llx)\n",
+ __func__, (u64)sas_address));
+ ioc->current_boot_device.device = device;
+ ioc->current_boot_device.channel = channel;
+ }
+ }
+}
+
+static struct _sas_device *
+__mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc,
+ struct MPT3SAS_TARGET *tgt_priv)
+{
+ struct _sas_device *ret;
+
+ assert_spin_locked(&ioc->sas_device_lock);
+
+ ret = tgt_priv->sas_dev;
+ if (ret)
+ sas_device_get(ret);
+
+ return ret;
+}
+
+static struct _sas_device *
+mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc,
+ struct MPT3SAS_TARGET *tgt_priv)
+{
+ struct _sas_device *ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ ret = __mpt3sas_get_sdev_from_target(ioc, tgt_priv);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ return ret;
+}
+
+static struct _pcie_device *
+__mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc,
+ struct MPT3SAS_TARGET *tgt_priv)
+{
+ struct _pcie_device *ret;
+
+ assert_spin_locked(&ioc->pcie_device_lock);
+
+ ret = tgt_priv->pcie_dev;
+ if (ret)
+ pcie_device_get(ret);
+
+ return ret;
+}
+
+/**
+ * mpt3sas_get_pdev_from_target - pcie device search
+ * @ioc: per adapter object
+ * @tgt_priv: starget private object
+ *
+ * Context: This function will acquire ioc->pcie_device_lock and will release
+ * before returning the pcie_device object.
+ *
+ * This searches for pcie_device from target, then return pcie_device object.
+ */
+static struct _pcie_device *
+mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc,
+ struct MPT3SAS_TARGET *tgt_priv)
+{
+ struct _pcie_device *ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ ret = __mpt3sas_get_pdev_from_target(ioc, tgt_priv);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ return ret;
+}
+
+
+/**
+ * __mpt3sas_get_sdev_by_rphy - sas device search
+ * @ioc: per adapter object
+ * @rphy: sas_rphy pointer
+ *
+ * Context: This function will acquire ioc->sas_device_lock and will release
+ * before returning the sas_device object.
+ *
+ * This searches for sas_device from rphy object
+ * then return sas_device object.
+ */
+struct _sas_device *
+__mpt3sas_get_sdev_by_rphy(struct MPT3SAS_ADAPTER *ioc,
+ struct sas_rphy *rphy)
+{
+ struct _sas_device *sas_device;
+
+ assert_spin_locked(&ioc->sas_device_lock);
+
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (sas_device->rphy != rphy)
+ continue;
+ sas_device_get(sas_device);
+ return sas_device;
+ }
+
+ sas_device = NULL;
+ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) {
+ if (sas_device->rphy != rphy)
+ continue;
+ sas_device_get(sas_device);
+ return sas_device;
+ }
+
+ return NULL;
+}
+
+/**
+ * __mpt3sas_get_sdev_by_addr - get _sas_device object corresponding to provided
+ * sas address from sas_device_list list
+ * @ioc: per adapter object
+ * @sas_address: device sas address
+ * @port: port number
+ *
+ * Search for _sas_device object corresponding to provided sas address,
+ * if available return _sas_device object address otherwise return NULL.
+ */
+struct _sas_device *
+__mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct hba_port *port)
+{
+ struct _sas_device *sas_device;
+
+ if (!port)
+ return NULL;
+
+ assert_spin_locked(&ioc->sas_device_lock);
+
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (sas_device->sas_address != sas_address)
+ continue;
+ if (sas_device->port != port)
+ continue;
+ sas_device_get(sas_device);
+ return sas_device;
+ }
+
+ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) {
+ if (sas_device->sas_address != sas_address)
+ continue;
+ if (sas_device->port != port)
+ continue;
+ sas_device_get(sas_device);
+ return sas_device;
+ }
+
+ return NULL;
+}
+
+/**
+ * mpt3sas_get_sdev_by_addr - sas device search
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @port: hba port entry
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for sas_device based on sas_address & port number,
+ * then return sas_device object.
+ */
+struct _sas_device *
+mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct hba_port *port)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+ sas_address, port);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ return sas_device;
+}
+
+static struct _sas_device *
+__mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_device *sas_device;
+
+ assert_spin_locked(&ioc->sas_device_lock);
+
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list)
+ if (sas_device->handle == handle)
+ goto found_device;
+
+ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
+ if (sas_device->handle == handle)
+ goto found_device;
+
+ return NULL;
+
+found_device:
+ sas_device_get(sas_device);
+ return sas_device;
+}
+
+/**
+ * mpt3sas_get_sdev_by_handle - sas device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for sas_device based on sas_address, then return sas_device
+ * object.
+ */
+struct _sas_device *
+mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ return sas_device;
+}
+
+/**
+ * _scsih_display_enclosure_chassis_info - display device location info
+ * @ioc: per adapter object
+ * @sas_device: per sas device object
+ * @sdev: scsi device struct
+ * @starget: scsi target struct
+ */
+static void
+_scsih_display_enclosure_chassis_info(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device, struct scsi_device *sdev,
+ struct scsi_target *starget)
+{
+ if (sdev) {
+ if (sas_device->enclosure_handle != 0)
+ sdev_printk(KERN_INFO, sdev,
+ "enclosure logical id (0x%016llx), slot(%d) \n",
+ (unsigned long long)
+ sas_device->enclosure_logical_id,
+ sas_device->slot);
+ if (sas_device->connector_name[0] != '\0')
+ sdev_printk(KERN_INFO, sdev,
+ "enclosure level(0x%04x), connector name( %s)\n",
+ sas_device->enclosure_level,
+ sas_device->connector_name);
+ if (sas_device->is_chassis_slot_valid)
+ sdev_printk(KERN_INFO, sdev, "chassis slot(0x%04x)\n",
+ sas_device->chassis_slot);
+ } else if (starget) {
+ if (sas_device->enclosure_handle != 0)
+ starget_printk(KERN_INFO, starget,
+ "enclosure logical id(0x%016llx), slot(%d) \n",
+ (unsigned long long)
+ sas_device->enclosure_logical_id,
+ sas_device->slot);
+ if (sas_device->connector_name[0] != '\0')
+ starget_printk(KERN_INFO, starget,
+ "enclosure level(0x%04x), connector name( %s)\n",
+ sas_device->enclosure_level,
+ sas_device->connector_name);
+ if (sas_device->is_chassis_slot_valid)
+ starget_printk(KERN_INFO, starget,
+ "chassis slot(0x%04x)\n",
+ sas_device->chassis_slot);
+ } else {
+ if (sas_device->enclosure_handle != 0)
+ ioc_info(ioc, "enclosure logical id(0x%016llx), slot(%d)\n",
+ (u64)sas_device->enclosure_logical_id,
+ sas_device->slot);
+ if (sas_device->connector_name[0] != '\0')
+ ioc_info(ioc, "enclosure level(0x%04x), connector name( %s)\n",
+ sas_device->enclosure_level,
+ sas_device->connector_name);
+ if (sas_device->is_chassis_slot_valid)
+ ioc_info(ioc, "chassis slot(0x%04x)\n",
+ sas_device->chassis_slot);
+ }
+}
+
+/**
+ * _scsih_sas_device_remove - remove sas_device from list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * If sas_device is on the list, remove it and decrement its reference count.
+ */
+static void
+_scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ if (!sas_device)
+ return;
+ ioc_info(ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n",
+ sas_device->handle, (u64)sas_device->sas_address);
+
+ _scsih_display_enclosure_chassis_info(ioc, sas_device, NULL, NULL);
+
+ /*
+ * The lock serializes access to the list, but we still need to verify
+ * that nobody removed the entry while we were waiting on the lock.
+ */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ if (!list_empty(&sas_device->list)) {
+ list_del_init(&sas_device->list);
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_device_remove_by_handle - removing device object by handle
+ * @ioc: per adapter object
+ * @handle: device handle
+ */
+static void
+_scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (ioc->shost_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (sas_device) {
+ list_del_init(&sas_device->list);
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device) {
+ _scsih_remove_device(ioc, sas_device);
+ sas_device_put(sas_device);
+ }
+}
+
+/**
+ * mpt3sas_device_remove_by_sas_address - removing device object by
+ * sas address & port number
+ * @ioc: per adapter object
+ * @sas_address: device sas_address
+ * @port: hba port entry
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct hba_port *port)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (ioc->shost_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address, port);
+ if (sas_device) {
+ list_del_init(&sas_device->list);
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device) {
+ _scsih_remove_device(ioc, sas_device);
+ sas_device_put(sas_device);
+ }
+}
+
+/**
+ * _scsih_sas_device_add - insert sas_device to the list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Adding new object to the ioc->sas_device_list.
+ */
+static void
+_scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: handle(0x%04x), sas_addr(0x%016llx)\n",
+ __func__, sas_device->handle,
+ (u64)sas_device->sas_address));
+
+ dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device,
+ NULL, NULL));
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device_get(sas_device);
+ list_add_tail(&sas_device->list, &ioc->sas_device_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (ioc->hide_drives) {
+ clear_bit(sas_device->handle, ioc->pend_os_device_add);
+ return;
+ }
+
+ if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent, sas_device->port)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ /*
+ * When asyn scanning is enabled, its not possible to remove
+ * devices while scanning is turned on due to an oops in
+ * scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start()
+ */
+ if (!ioc->is_driver_loading) {
+ mpt3sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent,
+ sas_device->port);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+ } else
+ clear_bit(sas_device->handle, ioc->pend_os_device_add);
+}
+
+/**
+ * _scsih_sas_device_init_add - insert sas_device to the list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Adding new object at driver load time to the ioc->sas_device_init_list.
+ */
+static void
+_scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: handle(0x%04x), sas_addr(0x%016llx)\n",
+ __func__, sas_device->handle,
+ (u64)sas_device->sas_address));
+
+ dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device,
+ NULL, NULL));
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device_get(sas_device);
+ list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
+ _scsih_determine_boot_device(ioc, sas_device, 0);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+
+static struct _pcie_device *
+__mpt3sas_get_pdev_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
+{
+ struct _pcie_device *pcie_device;
+
+ assert_spin_locked(&ioc->pcie_device_lock);
+
+ list_for_each_entry(pcie_device, &ioc->pcie_device_list, list)
+ if (pcie_device->wwid == wwid)
+ goto found_device;
+
+ list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list)
+ if (pcie_device->wwid == wwid)
+ goto found_device;
+
+ return NULL;
+
+found_device:
+ pcie_device_get(pcie_device);
+ return pcie_device;
+}
+
+
+/**
+ * mpt3sas_get_pdev_by_wwid - pcie device search
+ * @ioc: per adapter object
+ * @wwid: wwid
+ *
+ * Context: This function will acquire ioc->pcie_device_lock and will release
+ * before returning the pcie_device object.
+ *
+ * This searches for pcie_device based on wwid, then return pcie_device object.
+ */
+static struct _pcie_device *
+mpt3sas_get_pdev_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
+{
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ return pcie_device;
+}
+
+
+static struct _pcie_device *
+__mpt3sas_get_pdev_by_idchannel(struct MPT3SAS_ADAPTER *ioc, int id,
+ int channel)
+{
+ struct _pcie_device *pcie_device;
+
+ assert_spin_locked(&ioc->pcie_device_lock);
+
+ list_for_each_entry(pcie_device, &ioc->pcie_device_list, list)
+ if (pcie_device->id == id && pcie_device->channel == channel)
+ goto found_device;
+
+ list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list)
+ if (pcie_device->id == id && pcie_device->channel == channel)
+ goto found_device;
+
+ return NULL;
+
+found_device:
+ pcie_device_get(pcie_device);
+ return pcie_device;
+}
+
+static struct _pcie_device *
+__mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _pcie_device *pcie_device;
+
+ assert_spin_locked(&ioc->pcie_device_lock);
+
+ list_for_each_entry(pcie_device, &ioc->pcie_device_list, list)
+ if (pcie_device->handle == handle)
+ goto found_device;
+
+ list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list)
+ if (pcie_device->handle == handle)
+ goto found_device;
+
+ return NULL;
+
+found_device:
+ pcie_device_get(pcie_device);
+ return pcie_device;
+}
+
+
+/**
+ * mpt3sas_get_pdev_by_handle - pcie device search
+ * @ioc: per adapter object
+ * @handle: Firmware device handle
+ *
+ * Context: This function will acquire ioc->pcie_device_lock and will release
+ * before returning the pcie_device object.
+ *
+ * This searches for pcie_device based on handle, then return pcie_device
+ * object.
+ */
+struct _pcie_device *
+mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ return pcie_device;
+}
+
+/**
+ * _scsih_set_nvme_max_shutdown_latency - Update max_shutdown_latency.
+ * @ioc: per adapter object
+ * Context: This function will acquire ioc->pcie_device_lock
+ *
+ * Update ioc->max_shutdown_latency to that NVMe drives RTD3 Entry Latency
+ * which has reported maximum among all available NVMe drives.
+ * Minimum max_shutdown_latency will be six seconds.
+ */
+static void
+_scsih_set_nvme_max_shutdown_latency(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+ u16 shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) {
+ if (pcie_device->shutdown_latency) {
+ if (shutdown_latency < pcie_device->shutdown_latency)
+ shutdown_latency =
+ pcie_device->shutdown_latency;
+ }
+ }
+ ioc->max_shutdown_latency = shutdown_latency;
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+}
+
+/**
+ * _scsih_pcie_device_remove - remove pcie_device from list.
+ * @ioc: per adapter object
+ * @pcie_device: the pcie_device object
+ * Context: This function will acquire ioc->pcie_device_lock.
+ *
+ * If pcie_device is on the list, remove it and decrement its reference count.
+ */
+static void
+_scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _pcie_device *pcie_device)
+{
+ unsigned long flags;
+ int was_on_pcie_device_list = 0;
+ u8 update_latency = 0;
+
+ if (!pcie_device)
+ return;
+ ioc_info(ioc, "removing handle(0x%04x), wwid(0x%016llx)\n",
+ pcie_device->handle, (u64)pcie_device->wwid);
+ if (pcie_device->enclosure_handle != 0)
+ ioc_info(ioc, "removing enclosure logical id(0x%016llx), slot(%d)\n",
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot);
+ if (pcie_device->connector_name[0] != '\0')
+ ioc_info(ioc, "removing enclosure level(0x%04x), connector name( %s)\n",
+ pcie_device->enclosure_level,
+ pcie_device->connector_name);
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ if (!list_empty(&pcie_device->list)) {
+ list_del_init(&pcie_device->list);
+ was_on_pcie_device_list = 1;
+ }
+ if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
+ update_latency = 1;
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ if (was_on_pcie_device_list) {
+ kfree(pcie_device->serial_number);
+ pcie_device_put(pcie_device);
+ }
+
+ /*
+ * This device's RTD3 Entry Latency matches IOC's
+ * max_shutdown_latency. Recalculate IOC's max_shutdown_latency
+ * from the available drives as current drive is getting removed.
+ */
+ if (update_latency)
+ _scsih_set_nvme_max_shutdown_latency(ioc);
+}
+
+
+/**
+ * _scsih_pcie_device_remove_by_handle - removing pcie device object by handle
+ * @ioc: per adapter object
+ * @handle: device handle
+ */
+static void
+_scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+ int was_on_pcie_device_list = 0;
+ u8 update_latency = 0;
+
+ if (ioc->shost_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle);
+ if (pcie_device) {
+ if (!list_empty(&pcie_device->list)) {
+ list_del_init(&pcie_device->list);
+ was_on_pcie_device_list = 1;
+ pcie_device_put(pcie_device);
+ }
+ if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
+ update_latency = 1;
+ }
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ if (was_on_pcie_device_list) {
+ _scsih_pcie_device_remove_from_sml(ioc, pcie_device);
+ pcie_device_put(pcie_device);
+ }
+
+ /*
+ * This device's RTD3 Entry Latency matches IOC's
+ * max_shutdown_latency. Recalculate IOC's max_shutdown_latency
+ * from the available drives as current drive is getting removed.
+ */
+ if (update_latency)
+ _scsih_set_nvme_max_shutdown_latency(ioc);
+}
+
+/**
+ * _scsih_pcie_device_add - add pcie_device object
+ * @ioc: per adapter object
+ * @pcie_device: pcie_device object
+ *
+ * This is added to the pcie_device_list link list.
+ */
+static void
+_scsih_pcie_device_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _pcie_device *pcie_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: handle (0x%04x), wwid(0x%016llx)\n",
+ __func__,
+ pcie_device->handle, (u64)pcie_device->wwid));
+ if (pcie_device->enclosure_handle != 0)
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enclosure logical id(0x%016llx), slot( %d)\n",
+ __func__,
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot));
+ if (pcie_device->connector_name[0] != '\0')
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enclosure level(0x%04x), connector name( %s)\n",
+ __func__, pcie_device->enclosure_level,
+ pcie_device->connector_name));
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device_get(pcie_device);
+ list_add_tail(&pcie_device->list, &ioc->pcie_device_list);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ if (pcie_device->access_status ==
+ MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) {
+ clear_bit(pcie_device->handle, ioc->pend_os_device_add);
+ return;
+ }
+ if (scsi_add_device(ioc->shost, PCIE_CHANNEL, pcie_device->id, 0)) {
+ _scsih_pcie_device_remove(ioc, pcie_device);
+ } else if (!pcie_device->starget) {
+ if (!ioc->is_driver_loading) {
+/*TODO-- Need to find out whether this condition will occur or not*/
+ clear_bit(pcie_device->handle, ioc->pend_os_device_add);
+ }
+ } else
+ clear_bit(pcie_device->handle, ioc->pend_os_device_add);
+}
+
+/*
+ * _scsih_pcie_device_init_add - insert pcie_device to the init list.
+ * @ioc: per adapter object
+ * @pcie_device: the pcie_device object
+ * Context: This function will acquire ioc->pcie_device_lock.
+ *
+ * Adding new object at driver load time to the ioc->pcie_device_init_list.
+ */
+static void
+_scsih_pcie_device_init_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _pcie_device *pcie_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: handle (0x%04x), wwid(0x%016llx)\n",
+ __func__,
+ pcie_device->handle, (u64)pcie_device->wwid));
+ if (pcie_device->enclosure_handle != 0)
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enclosure logical id(0x%016llx), slot( %d)\n",
+ __func__,
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot));
+ if (pcie_device->connector_name[0] != '\0')
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enclosure level(0x%04x), connector name( %s)\n",
+ __func__, pcie_device->enclosure_level,
+ pcie_device->connector_name));
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device_get(pcie_device);
+ list_add_tail(&pcie_device->list, &ioc->pcie_device_init_list);
+ if (pcie_device->access_status !=
+ MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED)
+ _scsih_determine_boot_device(ioc, pcie_device, PCIE_CHANNEL);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+}
+/**
+ * _scsih_raid_device_find_by_id - raid device search
+ * @ioc: per adapter object
+ * @id: sas device target id
+ * @channel: sas device channel
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on target id, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_id(struct MPT3SAS_ADAPTER *ioc, int id, int channel)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->id == id && raid_device->channel == channel) {
+ r = raid_device;
+ goto out;
+ }
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_raid_device_find_by_handle - raid device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on handle, then return raid_device
+ * object.
+ */
+struct _raid_device *
+mpt3sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->handle != handle)
+ continue;
+ r = raid_device;
+ goto out;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_find_by_wwid - raid device search
+ * @ioc: per adapter object
+ * @wwid: ?
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on wwid, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->wwid != wwid)
+ continue;
+ r = raid_device;
+ goto out;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_add - add raid_device object
+ * @ioc: per adapter object
+ * @raid_device: raid_device object
+ *
+ * This is added to the raid_device_list link list.
+ */
+static void
+_scsih_raid_device_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: handle(0x%04x), wwid(0x%016llx)\n",
+ __func__,
+ raid_device->handle, (u64)raid_device->wwid));
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_add_tail(&raid_device->list, &ioc->raid_device_list);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_raid_device_remove - delete raid_device object
+ * @ioc: per adapter object
+ * @raid_device: raid_device object
+ *
+ */
+static void
+_scsih_raid_device_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_del(&raid_device->list);
+ kfree(raid_device);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * mpt3sas_scsih_expander_find_by_handle - expander device search
+ * @ioc: per adapter object
+ * @handle: expander handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for expander device based on handle, then returns the
+ * sas_node object.
+ */
+struct _sas_node *
+mpt3sas_scsih_expander_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->handle != handle)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_scsih_enclosure_find_by_handle - exclosure device search
+ * @ioc: per adapter object
+ * @handle: enclosure handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for enclosure device based on handle, then returns the
+ * enclosure object.
+ */
+static struct _enclosure_node *
+mpt3sas_scsih_enclosure_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _enclosure_node *enclosure_dev, *r;
+
+ r = NULL;
+ list_for_each_entry(enclosure_dev, &ioc->enclosure_list, list) {
+ if (le16_to_cpu(enclosure_dev->pg0.EnclosureHandle) != handle)
+ continue;
+ r = enclosure_dev;
+ goto out;
+ }
+out:
+ return r;
+}
+/**
+ * mpt3sas_scsih_expander_find_by_sas_address - expander device search
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @port: hba port entry
+ * Context: Calling function should acquire ioc->sas_node_lock.
+ *
+ * This searches for expander device based on sas_address & port number,
+ * then returns the sas_node object.
+ */
+struct _sas_node *
+mpt3sas_scsih_expander_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct hba_port *port)
+{
+ struct _sas_node *sas_expander, *r = NULL;
+
+ if (!port)
+ return r;
+
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->sas_address != sas_address)
+ continue;
+ if (sas_expander->port != port)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * _scsih_expander_node_add - insert expander device to the list.
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ * Context: This function will acquire ioc->sas_node_lock.
+ *
+ * Adding new object to the ioc->sas_expander_list.
+ */
+static void
+_scsih_expander_node_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_add_tail(&sas_expander->list, &ioc->sas_expander_list);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+}
+
+/**
+ * _scsih_is_end_device - determines if device is an end device
+ * @device_info: bitfield providing information about the device.
+ * Context: none
+ *
+ * Return: 1 if end device.
+ */
+static int
+_scsih_is_end_device(u32 device_info)
+{
+ if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE &&
+ ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) |
+ (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) |
+ (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * _scsih_is_nvme_pciescsi_device - determines if
+ * device is an pcie nvme/scsi device
+ * @device_info: bitfield providing information about the device.
+ * Context: none
+ *
+ * Returns 1 if device is pcie device type nvme/scsi.
+ */
+static int
+_scsih_is_nvme_pciescsi_device(u32 device_info)
+{
+ if (((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE)
+ == MPI26_PCIE_DEVINFO_NVME) ||
+ ((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE)
+ == MPI26_PCIE_DEVINFO_SCSI))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * _scsih_scsi_lookup_find_by_target - search for matching channel:id
+ * @ioc: per adapter object
+ * @id: target id
+ * @channel: channel
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a matching channel:id in the scsi_lookup array,
+ * returning 1 if found.
+ */
+static u8
+_scsih_scsi_lookup_find_by_target(struct MPT3SAS_ADAPTER *ioc, int id,
+ int channel)
+{
+ int smid;
+ struct scsi_cmnd *scmd;
+
+ for (smid = 1;
+ smid <= ioc->shost->can_queue; smid++) {
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (!scmd)
+ continue;
+ if (scmd->device->id == id &&
+ scmd->device->channel == channel)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun
+ * @ioc: per adapter object
+ * @id: target id
+ * @lun: lun number
+ * @channel: channel
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a matching channel:id:lun in the scsi_lookup array,
+ * returning 1 if found.
+ */
+static u8
+_scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id,
+ unsigned int lun, int channel)
+{
+ int smid;
+ struct scsi_cmnd *scmd;
+
+ for (smid = 1; smid <= ioc->shost->can_queue; smid++) {
+
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (!scmd)
+ continue;
+ if (scmd->device->id == id &&
+ scmd->device->channel == channel &&
+ scmd->device->lun == lun)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * mpt3sas_scsih_scsi_lookup_get - returns scmd entry
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return: the smid stored scmd pointer.
+ * Then will dereference the stored scmd pointer.
+ */
+struct scsi_cmnd *
+mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ struct scsi_cmnd *scmd = NULL;
+ struct scsiio_tracker *st;
+ Mpi25SCSIIORequest_t *mpi_request;
+ u16 tag = smid - 1;
+
+ if (smid > 0 &&
+ smid <= ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT) {
+ u32 unique_tag =
+ ioc->io_queue_num[tag] << BLK_MQ_UNIQUE_TAG_BITS | tag;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+
+ /*
+ * If SCSI IO request is outstanding at driver level then
+ * DevHandle filed must be non-zero. If DevHandle is zero
+ * then it means that this smid is free at driver level,
+ * so return NULL.
+ */
+ if (!mpi_request->DevHandle)
+ return scmd;
+
+ scmd = scsi_host_find_tag(ioc->shost, unique_tag);
+ if (scmd) {
+ st = scsi_cmd_priv(scmd);
+ if (st->cb_idx == 0xFF || st->smid == 0)
+ scmd = NULL;
+ }
+ }
+ return scmd;
+}
+
+/**
+ * scsih_change_queue_depth - setting device queue depth
+ * @sdev: scsi device struct
+ * @qdepth: requested queue depth
+ *
+ * Return: queue depth.
+ */
+static int
+scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ struct Scsi_Host *shost = sdev->host;
+ int max_depth;
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ max_depth = shost->can_queue;
+
+ /*
+ * limit max device queue for SATA to 32 if enable_sdev_max_qd
+ * is disabled.
+ */
+ if (ioc->enable_sdev_max_qd || ioc->is_gen35_ioc)
+ goto not_sata;
+
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ goto not_sata;
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ if (!sas_target_priv_data)
+ goto not_sata;
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
+ goto not_sata;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data);
+ if (sas_device) {
+ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ max_depth = MPT3SAS_SATA_QUEUE_DEPTH;
+
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ not_sata:
+
+ if (!sdev->tagged_supported)
+ max_depth = 1;
+ if (qdepth > max_depth)
+ qdepth = max_depth;
+ scsi_change_queue_depth(sdev, qdepth);
+ sdev_printk(KERN_INFO, sdev,
+ "qdepth(%d), tagged(%d), scsi_level(%d), cmd_que(%d)\n",
+ sdev->queue_depth, sdev->tagged_supported,
+ sdev->scsi_level, ((sdev->inquiry[7] & 2) >> 1));
+ return sdev->queue_depth;
+}
+
+/**
+ * mpt3sas_scsih_change_queue_depth - setting device queue depth
+ * @sdev: scsi device struct
+ * @qdepth: requested queue depth
+ *
+ * Returns nothing.
+ */
+void
+mpt3sas_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ struct Scsi_Host *shost = sdev->host;
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ if (ioc->enable_sdev_max_qd)
+ qdepth = shost->can_queue;
+
+ scsih_change_queue_depth(sdev, qdepth);
+}
+
+/**
+ * scsih_target_alloc - target add routine
+ * @starget: scsi target struct
+ *
+ * Return: 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+scsih_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+ struct sas_rphy *rphy;
+
+ sas_target_priv_data = kzalloc(sizeof(*sas_target_priv_data),
+ GFP_KERNEL);
+ if (!sas_target_priv_data)
+ return -ENOMEM;
+
+ starget->hostdata = sas_target_priv_data;
+ sas_target_priv_data->starget = starget;
+ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;
+
+ /* RAID volumes */
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
+ starget->channel);
+ if (raid_device) {
+ sas_target_priv_data->handle = raid_device->handle;
+ sas_target_priv_data->sas_address = raid_device->wwid;
+ sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME;
+ if (ioc->is_warpdrive)
+ sas_target_priv_data->raid_device = raid_device;
+ raid_device->starget = starget;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return 0;
+ }
+
+ /* PCIe devices */
+ if (starget->channel == PCIE_CHANNEL) {
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_idchannel(ioc, starget->id,
+ starget->channel);
+ if (pcie_device) {
+ sas_target_priv_data->handle = pcie_device->handle;
+ sas_target_priv_data->sas_address = pcie_device->wwid;
+ sas_target_priv_data->port = NULL;
+ sas_target_priv_data->pcie_dev = pcie_device;
+ pcie_device->starget = starget;
+ pcie_device->id = starget->id;
+ pcie_device->channel = starget->channel;
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FLAGS_PCIE_DEVICE;
+ if (pcie_device->fast_path)
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FASTPATH_IO;
+ }
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ return 0;
+ }
+
+ /* sas/sata devices */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ rphy = dev_to_rphy(starget->dev.parent);
+ sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);
+
+ if (sas_device) {
+ sas_target_priv_data->handle = sas_device->handle;
+ sas_target_priv_data->sas_address = sas_device->sas_address;
+ sas_target_priv_data->port = sas_device->port;
+ sas_target_priv_data->sas_dev = sas_device;
+ sas_device->starget = starget;
+ sas_device->id = starget->id;
+ sas_device->channel = starget->channel;
+ if (test_bit(sas_device->handle, ioc->pd_handles))
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FLAGS_RAID_COMPONENT;
+ if (sas_device->fast_path)
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FASTPATH_IO;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ return 0;
+}
+
+/**
+ * scsih_target_destroy - target destroy routine
+ * @starget: scsi target struct
+ */
+static void
+scsih_target_destroy(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+
+ sas_target_priv_data = starget->hostdata;
+ if (!sas_target_priv_data)
+ return;
+
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
+ starget->channel);
+ if (raid_device) {
+ raid_device->starget = NULL;
+ raid_device->sdev = NULL;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ goto out;
+ }
+
+ if (starget->channel == PCIE_CHANNEL) {
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_from_target(ioc,
+ sas_target_priv_data);
+ if (pcie_device && (pcie_device->starget == starget) &&
+ (pcie_device->id == starget->id) &&
+ (pcie_device->channel == starget->channel))
+ pcie_device->starget = NULL;
+
+ if (pcie_device) {
+ /*
+ * Corresponding get() is in _scsih_target_alloc()
+ */
+ sas_target_priv_data->pcie_dev = NULL;
+ pcie_device_put(pcie_device);
+ pcie_device_put(pcie_device);
+ }
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ goto out;
+ }
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data);
+ if (sas_device && (sas_device->starget == starget) &&
+ (sas_device->id == starget->id) &&
+ (sas_device->channel == starget->channel))
+ sas_device->starget = NULL;
+
+ if (sas_device) {
+ /*
+ * Corresponding get() is in _scsih_target_alloc()
+ */
+ sas_target_priv_data->sas_dev = NULL;
+ sas_device_put(sas_device);
+
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ out:
+ kfree(sas_target_priv_data);
+ starget->hostdata = NULL;
+}
+
+/**
+ * scsih_sdev_init - device add routine
+ * @sdev: scsi device struct
+ *
+ * Return: 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+scsih_sdev_init(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_target *starget;
+ struct _raid_device *raid_device;
+ struct _sas_device *sas_device;
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+
+ sas_device_priv_data = kzalloc(sizeof(*sas_device_priv_data),
+ GFP_KERNEL);
+ if (!sas_device_priv_data)
+ return -ENOMEM;
+
+ sas_device_priv_data->lun = sdev->lun;
+ sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT;
+
+ starget = scsi_target(sdev);
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->num_luns++;
+ sas_device_priv_data->sas_target = sas_target_priv_data;
+ sdev->hostdata = sas_device_priv_data;
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT))
+ sdev->no_uld_attach = 1;
+
+ shost = dev_to_shost(&starget->dev);
+ ioc = shost_priv(shost);
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc,
+ starget->id, starget->channel);
+ if (raid_device)
+ raid_device->sdev = sdev; /* raid is single lun */
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ }
+ if (starget->channel == PCIE_CHANNEL) {
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_wwid(ioc,
+ sas_target_priv_data->sas_address);
+ if (pcie_device && (pcie_device->starget == NULL)) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s : pcie_device->starget set to starget @ %d\n",
+ __func__, __LINE__);
+ pcie_device->starget = starget;
+ }
+
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ } else if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+ sas_target_priv_data->sas_address,
+ sas_target_priv_data->port);
+ if (sas_device && (sas_device->starget == NULL)) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s : sas_device->starget set to starget @ %d\n",
+ __func__, __LINE__);
+ sas_device->starget = starget;
+ }
+
+ if (sas_device)
+ sas_device_put(sas_device);
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+
+ return 0;
+}
+
+/**
+ * scsih_sdev_destroy - device destroy routine
+ * @sdev: scsi device struct
+ */
+static void
+scsih_sdev_destroy(struct scsi_device *sdev)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget;
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ struct _sas_device *sas_device;
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+
+ if (!sdev->hostdata)
+ return;
+
+ starget = scsi_target(sdev);
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->num_luns--;
+
+ shost = dev_to_shost(&starget->dev);
+ ioc = shost_priv(shost);
+
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) {
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_from_target(ioc,
+ sas_target_priv_data);
+ if (pcie_device && !sas_target_priv_data->num_luns)
+ pcie_device->starget = NULL;
+
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ } else if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_from_target(ioc,
+ sas_target_priv_data);
+ if (sas_device && !sas_target_priv_data->num_luns)
+ sas_device->starget = NULL;
+
+ if (sas_device)
+ sas_device_put(sas_device);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+
+ kfree(sdev->hostdata);
+ sdev->hostdata = NULL;
+}
+
+/**
+ * _scsih_display_sata_capabilities - sata capabilities
+ * @ioc: per adapter object
+ * @handle: device handle
+ * @sdev: scsi device struct
+ */
+static void
+_scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc,
+ u16 handle, struct scsi_device *sdev)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
+ u16 flags;
+ u32 device_info;
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ flags = le16_to_cpu(sas_device_pg0.Flags);
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+
+ sdev_printk(KERN_INFO, sdev,
+ "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), "
+ "sw_preserve(%s)\n",
+ (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" :
+ "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n");
+}
+
+/*
+ * raid transport support -
+ * Enabled for SLES11 and newer, in older kernels the driver will panic when
+ * unloading the driver followed by a load - I believe that the subroutine
+ * raid_class_release() is not cleaning up properly.
+ */
+
+/**
+ * scsih_is_raid - return boolean indicating device is raid volume
+ * @dev: the device struct object
+ */
+static int
+scsih_is_raid(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host);
+
+ if (ioc->is_warpdrive)
+ return 0;
+ return (sdev->channel == RAID_CHANNEL) ? 1 : 0;
+}
+
+static int
+scsih_is_nvme(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return (sdev->channel == PCIE_CHANNEL) ? 1 : 0;
+}
+
+/**
+ * scsih_get_resync - get raid volume resync percent complete
+ * @dev: the device struct object
+ */
+static void
+scsih_get_resync(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host);
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ Mpi2RaidVolPage0_t vol_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 volume_status_flags;
+ u8 percent_complete;
+ u16 handle;
+
+ percent_complete = 0;
+ handle = 0;
+ if (ioc->is_warpdrive)
+ goto out;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
+ sdev->channel);
+ if (raid_device) {
+ handle = raid_device->handle;
+ percent_complete = raid_device->percent_complete;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (!handle)
+ goto out;
+
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ percent_complete = 0;
+ goto out;
+ }
+
+ volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags);
+ if (!(volume_status_flags &
+ MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS))
+ percent_complete = 0;
+
+ out:
+
+ switch (ioc->hba_mpi_version_belonged) {
+ case MPI2_VERSION:
+ raid_set_resync(mpt2sas_raid_template, dev, percent_complete);
+ break;
+ case MPI25_VERSION:
+ case MPI26_VERSION:
+ raid_set_resync(mpt3sas_raid_template, dev, percent_complete);
+ break;
+ }
+}
+
+/**
+ * scsih_get_state - get raid volume level
+ * @dev: the device struct object
+ */
+static void
+scsih_get_state(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host);
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ Mpi2RaidVolPage0_t vol_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 volstate;
+ enum raid_state state = RAID_STATE_UNKNOWN;
+ u16 handle = 0;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
+ sdev->channel);
+ if (raid_device)
+ handle = raid_device->handle;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (!raid_device)
+ goto out;
+
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags);
+ if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) {
+ state = RAID_STATE_RESYNCING;
+ goto out;
+ }
+
+ switch (vol_pg0.VolumeState) {
+ case MPI2_RAID_VOL_STATE_OPTIMAL:
+ case MPI2_RAID_VOL_STATE_ONLINE:
+ state = RAID_STATE_ACTIVE;
+ break;
+ case MPI2_RAID_VOL_STATE_DEGRADED:
+ state = RAID_STATE_DEGRADED;
+ break;
+ case MPI2_RAID_VOL_STATE_FAILED:
+ case MPI2_RAID_VOL_STATE_MISSING:
+ state = RAID_STATE_OFFLINE;
+ break;
+ }
+ out:
+ switch (ioc->hba_mpi_version_belonged) {
+ case MPI2_VERSION:
+ raid_set_state(mpt2sas_raid_template, dev, state);
+ break;
+ case MPI25_VERSION:
+ case MPI26_VERSION:
+ raid_set_state(mpt3sas_raid_template, dev, state);
+ break;
+ }
+}
+
+/**
+ * _scsih_set_level - set raid level
+ * @ioc: ?
+ * @sdev: scsi device struct
+ * @volume_type: volume type
+ */
+static void
+_scsih_set_level(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_device *sdev, u8 volume_type)
+{
+ enum raid_level level = RAID_LEVEL_UNKNOWN;
+
+ switch (volume_type) {
+ case MPI2_RAID_VOL_TYPE_RAID0:
+ level = RAID_LEVEL_0;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID10:
+ level = RAID_LEVEL_10;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1E:
+ level = RAID_LEVEL_1E;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1:
+ level = RAID_LEVEL_1;
+ break;
+ }
+
+ switch (ioc->hba_mpi_version_belonged) {
+ case MPI2_VERSION:
+ raid_set_level(mpt2sas_raid_template,
+ &sdev->sdev_gendev, level);
+ break;
+ case MPI25_VERSION:
+ case MPI26_VERSION:
+ raid_set_level(mpt3sas_raid_template,
+ &sdev->sdev_gendev, level);
+ break;
+ }
+}
+
+
+/**
+ * _scsih_get_volume_capabilities - volume capabilities
+ * @ioc: per adapter object
+ * @raid_device: the raid_device object
+ *
+ * Return: 0 for success, else 1
+ */
+static int
+_scsih_get_volume_capabilities(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ Mpi2RaidVolPage0_t *vol_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 sz;
+ u8 num_pds;
+
+ if ((mpt3sas_config_get_number_pds(ioc, raid_device->handle,
+ &num_pds)) || !num_pds) {
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+
+ raid_device->num_pds = num_pds;
+ sz = struct_size(vol_pg0, PhysDisk, num_pds);
+ vol_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!vol_pg0) {
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+
+ if ((mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ kfree(vol_pg0);
+ return 1;
+ }
+
+ raid_device->volume_type = vol_pg0->VolumeType;
+
+ /* figure out what the underlying devices are by
+ * obtaining the device_info bits for the 1st device
+ */
+ if (!(mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
+ vol_pg0->PhysDisk[0].PhysDiskNum))) {
+ if (!(mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ le16_to_cpu(pd_pg0.DevHandle)))) {
+ raid_device->device_info =
+ le32_to_cpu(sas_device_pg0.DeviceInfo);
+ }
+ }
+
+ kfree(vol_pg0);
+ return 0;
+}
+
+/**
+ * _scsih_enable_tlr - setting TLR flags
+ * @ioc: per adapter object
+ * @sdev: scsi device struct
+ *
+ * Enabling Transaction Layer Retries for tape devices when
+ * vpd page 0x90 is present
+ *
+ */
+static void
+_scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev)
+{
+
+ /* only for TAPE */
+ if (sdev->type != TYPE_TAPE)
+ return;
+
+ if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR))
+ return;
+
+ sas_enable_tlr(sdev);
+ sdev_printk(KERN_INFO, sdev, "TLR %s\n",
+ sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled");
+ return;
+
+}
+
+/**
+ * scsih_sdev_configure - device configure routine.
+ * @sdev: scsi device struct
+ * @lim: queue limits
+ *
+ * Return: 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+scsih_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
+{
+ struct Scsi_Host *shost = sdev->host;
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _pcie_device *pcie_device;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ int qdepth;
+ u8 ssp_target = 0;
+ char *ds = "";
+ char *r_level = "";
+ u16 handle, volume_handle = 0;
+ u64 volume_wwid = 0;
+
+ qdepth = 1;
+ sas_device_priv_data = sdev->hostdata;
+ sas_device_priv_data->configured_lun = 1;
+ sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT;
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ handle = sas_target_priv_data->handle;
+
+ /* raid volume handling */
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) {
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (!raid_device) {
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+
+ if (_scsih_get_volume_capabilities(ioc, raid_device)) {
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+
+ /*
+ * WARPDRIVE: Initialize the required data for Direct IO
+ */
+ mpt3sas_init_warpdrive_properties(ioc, raid_device);
+
+ /* RAID Queue Depth Support
+ * IS volume = underlying qdepth of drive type, either
+ * MPT3SAS_SAS_QUEUE_DEPTH or MPT3SAS_SATA_QUEUE_DEPTH
+ * IM/IME/R10 = 128 (MPT3SAS_RAID_QUEUE_DEPTH)
+ */
+ if (raid_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
+ qdepth = MPT3SAS_SAS_QUEUE_DEPTH;
+ ds = "SSP";
+ } else {
+ qdepth = MPT3SAS_SATA_QUEUE_DEPTH;
+ if (raid_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "SATA";
+ else
+ ds = "STP";
+ }
+
+ switch (raid_device->volume_type) {
+ case MPI2_RAID_VOL_TYPE_RAID0:
+ r_level = "RAID0";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1E:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ if (ioc->manu_pg10.OEMIdentifier &&
+ (le32_to_cpu(ioc->manu_pg10.GenericFlags0) &
+ MFG10_GF0_R10_DISPLAY) &&
+ !(raid_device->num_pds % 2))
+ r_level = "RAID10";
+ else
+ r_level = "RAID1E";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAID1";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID10:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAID10";
+ break;
+ case MPI2_RAID_VOL_TYPE_UNKNOWN:
+ default:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAIDX";
+ break;
+ }
+
+ if (!ioc->hide_ir_msg)
+ sdev_printk(KERN_INFO, sdev,
+ "%s: handle(0x%04x), wwid(0x%016llx),"
+ " pd_count(%d), type(%s)\n",
+ r_level, raid_device->handle,
+ (unsigned long long)raid_device->wwid,
+ raid_device->num_pds, ds);
+
+ if (shost->max_sectors > MPT3SAS_RAID_MAX_SECTORS) {
+ lim->max_hw_sectors = MPT3SAS_RAID_MAX_SECTORS;
+ sdev_printk(KERN_INFO, sdev,
+ "Set queue's max_sector to: %u\n",
+ MPT3SAS_RAID_MAX_SECTORS);
+ }
+
+ mpt3sas_scsih_change_queue_depth(sdev, qdepth);
+
+ /* raid transport support */
+ if (!ioc->is_warpdrive)
+ _scsih_set_level(ioc, sdev, raid_device->volume_type);
+ return 0;
+ }
+
+ /* non-raid handling */
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ if (mpt3sas_config_get_volume_handle(ioc, handle,
+ &volume_handle)) {
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+ if (volume_handle && mpt3sas_config_get_volume_wwid(ioc,
+ volume_handle, &volume_wwid)) {
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+ }
+
+ /* PCIe handling */
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) {
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_wwid(ioc,
+ sas_device_priv_data->sas_target->sas_address);
+ if (!pcie_device) {
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+
+ qdepth = ioc->max_nvme_qd;
+ ds = "NVMe";
+ sdev_printk(KERN_INFO, sdev,
+ "%s: handle(0x%04x), wwid(0x%016llx), port(%d)\n",
+ ds, handle, (unsigned long long)pcie_device->wwid,
+ pcie_device->port_num);
+ if (pcie_device->enclosure_handle != 0)
+ sdev_printk(KERN_INFO, sdev,
+ "%s: enclosure logical id(0x%016llx), slot(%d)\n",
+ ds,
+ (unsigned long long)pcie_device->enclosure_logical_id,
+ pcie_device->slot);
+ if (pcie_device->connector_name[0] != '\0')
+ sdev_printk(KERN_INFO, sdev,
+ "%s: enclosure level(0x%04x),"
+ "connector name( %s)\n", ds,
+ pcie_device->enclosure_level,
+ pcie_device->connector_name);
+
+ if (pcie_device->nvme_mdts)
+ lim->max_hw_sectors = pcie_device->nvme_mdts / 512;
+
+ pcie_device_put(pcie_device);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ mpt3sas_scsih_change_queue_depth(sdev, qdepth);
+ lim->virt_boundary_mask = ioc->page_size - 1;
+ return 0;
+ }
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+ sas_device_priv_data->sas_target->sas_address,
+ sas_device_priv_data->sas_target->port);
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ dfailprintk(ioc,
+ ioc_warn(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+
+ sas_device->volume_handle = volume_handle;
+ sas_device->volume_wwid = volume_wwid;
+ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
+ qdepth = (sas_device->port_type > 1) ?
+ ioc->max_wideport_qd : ioc->max_narrowport_qd;
+ ssp_target = 1;
+ if (sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SEP) {
+ sdev_printk(KERN_INFO, sdev,
+ "set ignore_delay_remove for handle(0x%04x)\n",
+ sas_device_priv_data->sas_target->handle);
+ sas_device_priv_data->ignore_delay_remove = 1;
+ ds = "SES";
+ } else
+ ds = "SSP";
+ } else {
+ qdepth = ioc->max_sata_qd;
+ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
+ ds = "STP";
+ else if (sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "SATA";
+ }
+
+ sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " \
+ "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n",
+ ds, handle, (unsigned long long)sas_device->sas_address,
+ sas_device->phy, (unsigned long long)sas_device->device_name);
+
+ _scsih_display_enclosure_chassis_info(NULL, sas_device, sdev, NULL);
+
+ sas_device_put(sas_device);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (!ssp_target)
+ _scsih_display_sata_capabilities(ioc, handle, sdev);
+
+
+ mpt3sas_scsih_change_queue_depth(sdev, qdepth);
+
+ if (ssp_target) {
+ sas_read_port_mode_page(sdev);
+ _scsih_enable_tlr(ioc, sdev);
+ }
+
+ return 0;
+}
+
+/**
+ * scsih_bios_param - fetch head, sector, cylinder info for a disk
+ * @sdev: scsi device struct
+ * @unused: pointer to gendisk
+ * @capacity: device size (in 512 byte sectors)
+ * @params: three element array to place output:
+ * params[0] number of heads (max 255)
+ * params[1] number of sectors (max 63)
+ * params[2] number of cylinders
+ */
+static int
+scsih_bios_param(struct scsi_device *sdev, struct gendisk *unused,
+ sector_t capacity, int params[])
+{
+ int heads;
+ int sectors;
+ sector_t cylinders;
+ ulong dummy;
+
+ heads = 64;
+ sectors = 32;
+
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+
+ /*
+ * Handle extended translation size for logical drives
+ * > 1Gb
+ */
+ if ((ulong)capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+ }
+
+ /* return result */
+ params[0] = heads;
+ params[1] = sectors;
+ params[2] = cylinders;
+
+ return 0;
+}
+
+/**
+ * _scsih_response_code - translation of device response code
+ * @ioc: per adapter object
+ * @response_code: response code returned by the device
+ */
+static void
+_scsih_response_code(struct MPT3SAS_ADAPTER *ioc, u8 response_code)
+{
+ char *desc;
+
+ switch (response_code) {
+ case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE:
+ desc = "task management request completed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME:
+ desc = "invalid frame";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
+ desc = "task management request not supported";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_FAILED:
+ desc = "task management request failed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED:
+ desc = "task management request succeeded";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN:
+ desc = "invalid lun";
+ break;
+ case 0xA:
+ desc = "overlapped tag attempted";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
+ desc = "task queued, however not sent to target";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+ ioc_warn(ioc, "response_code(0x%01x): %s\n", response_code, desc);
+}
+
+/**
+ * _scsih_tm_done - tm completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using scsih_issue_tm.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_tm_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ if (ioc->tm_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->tm_cmds.smid != smid)
+ return 1;
+ ioc->tm_cmds.status |= MPT3_CMD_COMPLETE;
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc->tm_cmds.status |= MPT3_CMD_REPLY_VALID;
+ }
+ ioc->tm_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->tm_cmds.done);
+ return 1;
+}
+
+/**
+ * mpt3sas_scsih_set_tm_flag - set per target tm_busy
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During taskmangement request, we need to freeze the device queue.
+ */
+void
+mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+ u8 skip = 0;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ if (skip)
+ continue;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ sas_device_priv_data->sas_target->tm_busy = 1;
+ skip = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ }
+}
+
+/**
+ * mpt3sas_scsih_clear_tm_flag - clear per target tm_busy
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During taskmangement request, we need to freeze the device queue.
+ */
+void
+mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+ u8 skip = 0;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ if (skip)
+ continue;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ sas_device_priv_data->sas_target->tm_busy = 0;
+ skip = 1;
+ ioc->ignore_loginfos = 0;
+ }
+ }
+}
+
+/**
+ * scsih_tm_cmd_map_status - map the target reset & LUN reset TM status
+ * @ioc: per adapter object
+ * @channel: the channel assigned by the OS
+ * @id: the id assigned by the OS
+ * @lun: lun number
+ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h)
+ * @smid_task: smid assigned to the task
+ *
+ * Look whether TM has aborted the timed out SCSI command, if
+ * TM has aborted the IO then return SUCCESS else return FAILED.
+ */
+static int
+scsih_tm_cmd_map_status(struct MPT3SAS_ADAPTER *ioc, uint channel,
+ uint id, uint lun, u8 type, u16 smid_task)
+{
+
+ if (smid_task <= ioc->shost->can_queue) {
+ switch (type) {
+ case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ if (!(_scsih_scsi_lookup_find_by_target(ioc,
+ id, channel)))
+ return SUCCESS;
+ break;
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET:
+ case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET:
+ if (!(_scsih_scsi_lookup_find_by_lun(ioc, id,
+ lun, channel)))
+ return SUCCESS;
+ break;
+ default:
+ return SUCCESS;
+ }
+ } else if (smid_task == ioc->scsih_cmds.smid) {
+ if ((ioc->scsih_cmds.status & MPT3_CMD_COMPLETE) ||
+ (ioc->scsih_cmds.status & MPT3_CMD_NOT_USED))
+ return SUCCESS;
+ } else if (smid_task == ioc->ctl_cmds.smid) {
+ if ((ioc->ctl_cmds.status & MPT3_CMD_COMPLETE) ||
+ (ioc->ctl_cmds.status & MPT3_CMD_NOT_USED))
+ return SUCCESS;
+ }
+
+ return FAILED;
+}
+
+/**
+ * scsih_tm_post_processing - post processing of target & LUN reset
+ * @ioc: per adapter object
+ * @handle: device handle
+ * @channel: the channel assigned by the OS
+ * @id: the id assigned by the OS
+ * @lun: lun number
+ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h)
+ * @smid_task: smid assigned to the task
+ *
+ * Post processing of target & LUN reset. Due to interrupt latency
+ * issue it possible that interrupt for aborted IO might not be
+ * received yet. So before returning failure status, poll the
+ * reply descriptor pools for the reply of timed out SCSI command.
+ * Return FAILED status if reply for timed out is not received
+ * otherwise return SUCCESS.
+ */
+static int
+scsih_tm_post_processing(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ uint channel, uint id, uint lun, u8 type, u16 smid_task)
+{
+ int rc;
+
+ rc = scsih_tm_cmd_map_status(ioc, channel, id, lun, type, smid_task);
+ if (rc == SUCCESS)
+ return rc;
+
+ ioc_info(ioc,
+ "Poll ReplyDescriptor queues for completion of"
+ " smid(%d), task_type(0x%02x), handle(0x%04x)\n",
+ smid_task, type, handle);
+
+ /*
+ * Due to interrupt latency issues, driver may receive interrupt for
+ * TM first and then for aborted SCSI IO command. So, poll all the
+ * ReplyDescriptor pools before returning the FAILED status to SML.
+ */
+ mpt3sas_base_mask_interrupts(ioc);
+ mpt3sas_base_sync_reply_irqs(ioc, 1);
+ mpt3sas_base_unmask_interrupts(ioc);
+
+ return scsih_tm_cmd_map_status(ioc, channel, id, lun, type, smid_task);
+}
+
+/**
+ * mpt3sas_scsih_issue_tm - main routine for sending tm requests
+ * @ioc: per adapter struct
+ * @handle: device handle
+ * @channel: the channel assigned by the OS
+ * @id: the id assigned by the OS
+ * @lun: lun number
+ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h)
+ * @smid_task: smid assigned to the task
+ * @msix_task: MSIX table index supplied by the OS
+ * @timeout: timeout in seconds
+ * @tr_method: Target Reset Method
+ * Context: user
+ *
+ * A generic API for sending task management requests to firmware.
+ *
+ * The callback index is set inside `ioc->tm_cb_idx`.
+ * The caller is responsible to check for outstanding commands.
+ *
+ * Return: SUCCESS or FAILED.
+ */
+int
+mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel,
+ uint id, u64 lun, u8 type, u16 smid_task, u16 msix_task,
+ u8 timeout, u8 tr_method)
+{
+ Mpi2SCSITaskManagementRequest_t *mpi_request;
+ Mpi2SCSITaskManagementReply_t *mpi_reply;
+ Mpi25SCSIIORequest_t *request;
+ u16 smid = 0;
+ u32 ioc_state;
+ int rc;
+ u8 issue_reset = 0;
+
+ lockdep_assert_held(&ioc->tm_cmds.mutex);
+
+ if (ioc->tm_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_info(ioc, "%s: tm_cmd busy!!!\n", __func__);
+ return FAILED;
+ }
+
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery) {
+ ioc_info(ioc, "%s: host reset in progress!\n", __func__);
+ return FAILED;
+ }
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ if (ioc_state & MPI2_DOORBELL_USED) {
+ dhsprintk(ioc, ioc_info(ioc, "unexpected doorbell active!\n"));
+ rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ return (!rc) ? SUCCESS : FAILED;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ return (!rc) ? SUCCESS : FAILED;
+ } else if ((ioc_state & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ mpt3sas_print_coredump_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ return (!rc) ? SUCCESS : FAILED;
+ }
+
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ return FAILED;
+ }
+
+ dtmprintk(ioc,
+ ioc_info(ioc, "sending tm: handle(0x%04x), task_type(0x%02x), smid(%d), timeout(%d), tr_method(0x%x)\n",
+ handle, type, smid_task, timeout, tr_method));
+ ioc->tm_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->tm_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+ memset(ioc->tm_cmds.reply, 0, sizeof(Mpi2SCSITaskManagementReply_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->TaskType = type;
+ if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK ||
+ type == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
+ mpi_request->MsgFlags = tr_method;
+ mpi_request->TaskMID = cpu_to_le16(smid_task);
+ int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);
+ mpt3sas_scsih_set_tm_flag(ioc, handle);
+ init_completion(&ioc->tm_cmds.done);
+ ioc->put_smid_hi_priority(ioc, smid, msix_task);
+ wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ);
+ if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->tm_cmds.status, mpi_request,
+ sizeof(Mpi2SCSITaskManagementRequest_t)/4, issue_reset);
+ if (issue_reset) {
+ rc = mpt3sas_base_hard_reset_handler(ioc,
+ FORCE_BIG_HAMMER);
+ rc = (!rc) ? SUCCESS : FAILED;
+ goto out;
+ }
+ }
+
+ /* sync IRQs in case those were busy during flush. */
+ mpt3sas_base_sync_reply_irqs(ioc, 0);
+
+ if (ioc->tm_cmds.status & MPT3_CMD_REPLY_VALID) {
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT);
+ mpi_reply = ioc->tm_cmds.reply;
+ dtmprintk(ioc,
+ ioc_info(ioc, "complete tm: ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+ if (ioc->logging_level & MPT_DEBUG_TM) {
+ _scsih_response_code(ioc, mpi_reply->ResponseCode);
+ if (mpi_reply->IOCStatus)
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SCSITaskManagementRequest_t)/4);
+ }
+ }
+
+ switch (type) {
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
+ rc = SUCCESS;
+ /*
+ * If DevHandle filed in smid_task's entry of request pool
+ * doesn't match with device handle on which this task abort
+ * TM is received then it means that TM has successfully
+ * aborted the timed out command. Since smid_task's entry in
+ * request pool will be memset to zero once the timed out
+ * command is returned to the SML. If the command is not
+ * aborted then smid_task’s entry won’t be cleared and it
+ * will have same DevHandle value on which this task abort TM
+ * is received and driver will return the TM status as FAILED.
+ */
+ request = mpt3sas_base_get_msg_frame(ioc, smid_task);
+ if (le16_to_cpu(request->DevHandle) != handle)
+ break;
+
+ ioc_info(ioc, "Task abort tm failed: handle(0x%04x),"
+ "timeout(%d) tr_method(0x%x) smid(%d) msix_index(%d)\n",
+ handle, timeout, tr_method, smid_task, msix_task);
+ rc = FAILED;
+ break;
+
+ case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET:
+ case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET:
+ rc = scsih_tm_post_processing(ioc, handle, channel, id, lun,
+ type, smid_task);
+ break;
+ case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK:
+ rc = SUCCESS;
+ break;
+ default:
+ rc = FAILED;
+ break;
+ }
+
+out:
+ mpt3sas_scsih_clear_tm_flag(ioc, handle);
+ ioc->tm_cmds.status = MPT3_CMD_NOT_USED;
+ return rc;
+}
+
+int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ uint channel, uint id, u64 lun, u8 type, u16 smid_task,
+ u16 msix_task, u8 timeout, u8 tr_method)
+{
+ int ret;
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ ret = mpt3sas_scsih_issue_tm(ioc, handle, channel, id, lun, type,
+ smid_task, msix_task, timeout, tr_method);
+ mutex_unlock(&ioc->tm_cmds.mutex);
+
+ return ret;
+}
+
+/**
+ * _scsih_tm_display_info - displays info about the device
+ * @ioc: per adapter struct
+ * @scmd: pointer to scsi command object
+ *
+ * Called by task management callback handlers.
+ */
+static void
+_scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
+{
+ struct scsi_target *starget = scmd->device->sdev_target;
+ struct MPT3SAS_TARGET *priv_target = starget->hostdata;
+ struct _sas_device *sas_device = NULL;
+ struct _pcie_device *pcie_device = NULL;
+ unsigned long flags;
+ char *device_str = NULL;
+
+ if (!priv_target)
+ return;
+ if (ioc->hide_ir_msg)
+ device_str = "WarpDrive";
+ else
+ device_str = "volume";
+
+ scsi_print_command(scmd);
+ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
+ starget_printk(KERN_INFO, starget,
+ "%s handle(0x%04x), %s wwid(0x%016llx)\n",
+ device_str, priv_target->handle,
+ device_str, (unsigned long long)priv_target->sas_address);
+
+ } else if (priv_target->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) {
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_from_target(ioc, priv_target);
+ if (pcie_device) {
+ starget_printk(KERN_INFO, starget,
+ "handle(0x%04x), wwid(0x%016llx), port(%d)\n",
+ pcie_device->handle,
+ (unsigned long long)pcie_device->wwid,
+ pcie_device->port_num);
+ if (pcie_device->enclosure_handle != 0)
+ starget_printk(KERN_INFO, starget,
+ "enclosure logical id(0x%016llx), slot(%d)\n",
+ (unsigned long long)
+ pcie_device->enclosure_logical_id,
+ pcie_device->slot);
+ if (pcie_device->connector_name[0] != '\0')
+ starget_printk(KERN_INFO, starget,
+ "enclosure level(0x%04x), connector name( %s)\n",
+ pcie_device->enclosure_level,
+ pcie_device->connector_name);
+ pcie_device_put(pcie_device);
+ }
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ } else {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_from_target(ioc, priv_target);
+ if (sas_device) {
+ if (priv_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ starget_printk(KERN_INFO, starget,
+ "volume handle(0x%04x), "
+ "volume wwid(0x%016llx)\n",
+ sas_device->volume_handle,
+ (unsigned long long)sas_device->volume_wwid);
+ }
+ starget_printk(KERN_INFO, starget,
+ "handle(0x%04x), sas_address(0x%016llx), phy(%d)\n",
+ sas_device->handle,
+ (unsigned long long)sas_device->sas_address,
+ sas_device->phy);
+
+ _scsih_display_enclosure_chassis_info(NULL, sas_device,
+ NULL, starget);
+
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+}
+
+/**
+ * scsih_abort - eh threads main abort routine
+ * @scmd: pointer to scsi command object
+ *
+ * Return: SUCCESS if command aborted else FAILED
+ */
+static int
+scsih_abort(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsiio_tracker *st = scsi_cmd_priv(scmd);
+ u16 handle;
+ int r;
+
+ u8 timeout = 30;
+ struct _pcie_device *pcie_device = NULL;
+ sdev_printk(KERN_INFO, scmd->device, "attempting task abort!"
+ "scmd(0x%p), outstanding for %u ms & timeout %u ms\n",
+ scmd, jiffies_to_msecs(jiffies - scmd->jiffies_at_alloc),
+ (scsi_cmd_to_rq(scmd)->timeout / HZ) * 1000);
+ _scsih_tm_display_info(ioc, scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
+ ioc->remove_host) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "device been deleted! scmd(0x%p)\n", scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* check for completed command */
+ if (st == NULL || st->cb_idx == 0xFF) {
+ sdev_printk(KERN_INFO, scmd->device, "No reference found at "
+ "driver, assuming scmd(0x%p) might have completed\n", scmd);
+ scmd->result = DID_RESET << 16;
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components and volumes this is not supported */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT ||
+ sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ mpt3sas_halt_firmware(ioc);
+
+ handle = sas_device_priv_data->sas_target->handle;
+ pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle);
+ if (pcie_device && (!ioc->tm_custom_handling) &&
+ (!(mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info))))
+ timeout = ioc->nvme_abort_timeout;
+ r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel,
+ scmd->device->id, scmd->device->lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
+ st->smid, st->msix_io, timeout, 0);
+ /* Command must be cleared after abort */
+ if (r == SUCCESS && st->cb_idx != 0xFF)
+ r = FAILED;
+ out:
+ sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(0x%p)\n",
+ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+ return r;
+}
+
+/**
+ * scsih_dev_reset - eh threads main device reset routine
+ * @scmd: pointer to scsi command object
+ *
+ * Return: SUCCESS if command aborted else FAILED
+ */
+static int
+scsih_dev_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct _sas_device *sas_device = NULL;
+ struct _pcie_device *pcie_device = NULL;
+ u16 handle;
+ u8 tr_method = 0;
+ u8 tr_timeout = 30;
+ int r;
+
+ struct scsi_target *starget = scmd->device->sdev_target;
+ struct MPT3SAS_TARGET *target_priv_data = starget->hostdata;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "attempting device reset! scmd(0x%p)\n", scmd);
+ _scsih_tm_display_info(ioc, scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
+ ioc->remove_host) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "device been deleted! scmd(0x%p)\n", scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components obtain the volume_handle */
+ handle = 0;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ sas_device = mpt3sas_get_sdev_from_target(ioc,
+ target_priv_data);
+ if (sas_device)
+ handle = sas_device->volume_handle;
+ } else
+ handle = sas_device_priv_data->sas_target->handle;
+
+ if (!handle) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle);
+
+ if (pcie_device && (!ioc->tm_custom_handling) &&
+ (!(mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info)))) {
+ tr_timeout = pcie_device->reset_timeout;
+ tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE;
+ } else
+ tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
+
+ r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel,
+ scmd->device->id, scmd->device->lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 0,
+ tr_timeout, tr_method);
+ /* Check for busy commands after reset */
+ if (r == SUCCESS && scsi_device_busy(scmd->device))
+ r = FAILED;
+ out:
+ sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(0x%p)\n",
+ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ if (sas_device)
+ sas_device_put(sas_device);
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+
+ return r;
+}
+
+/**
+ * scsih_target_reset - eh threads main target reset routine
+ * @scmd: pointer to scsi command object
+ *
+ * Return: SUCCESS if command aborted else FAILED
+ */
+static int
+scsih_target_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct _sas_device *sas_device = NULL;
+ struct _pcie_device *pcie_device = NULL;
+ u16 handle;
+ u8 tr_method = 0;
+ u8 tr_timeout = 30;
+ int r;
+ struct scsi_target *starget = scmd->device->sdev_target;
+ struct MPT3SAS_TARGET *target_priv_data = starget->hostdata;
+
+ starget_printk(KERN_INFO, starget,
+ "attempting target reset! scmd(0x%p)\n", scmd);
+ _scsih_tm_display_info(ioc, scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
+ ioc->remove_host) {
+ starget_printk(KERN_INFO, starget,
+ "target been deleted! scmd(0x%p)\n", scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components obtain the volume_handle */
+ handle = 0;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ sas_device = mpt3sas_get_sdev_from_target(ioc,
+ target_priv_data);
+ if (sas_device)
+ handle = sas_device->volume_handle;
+ } else
+ handle = sas_device_priv_data->sas_target->handle;
+
+ if (!handle) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle);
+
+ if (pcie_device && (!ioc->tm_custom_handling) &&
+ (!(mpt3sas_scsih_is_pcie_scsi_device(pcie_device->device_info)))) {
+ tr_timeout = pcie_device->reset_timeout;
+ tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE;
+ } else
+ tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
+ r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel,
+ scmd->device->id, 0,
+ MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 0,
+ tr_timeout, tr_method);
+ /* Check for busy commands after reset */
+ if (r == SUCCESS && atomic_read(&starget->target_busy))
+ r = FAILED;
+ out:
+ starget_printk(KERN_INFO, starget, "target reset: %s scmd(0x%p)\n",
+ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ if (sas_device)
+ sas_device_put(sas_device);
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+ return r;
+}
+
+
+/**
+ * scsih_host_reset - eh threads main host reset routine
+ * @scmd: pointer to scsi command object
+ *
+ * Return: SUCCESS if command aborted else FAILED
+ */
+static int
+scsih_host_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ int r, retval;
+
+ ioc_info(ioc, "attempting host reset! scmd(0x%p)\n", scmd);
+ scsi_print_command(scmd);
+
+ if (ioc->is_driver_loading || ioc->remove_host) {
+ ioc_info(ioc, "Blocking the host reset\n");
+ r = FAILED;
+ goto out;
+ }
+
+ retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ r = (retval < 0) ? FAILED : SUCCESS;
+out:
+ ioc_info(ioc, "host reset: %s scmd(0x%p)\n",
+ r == SUCCESS ? "SUCCESS" : "FAILED", scmd);
+
+ return r;
+}
+
+/**
+ * _scsih_fw_event_add - insert and queue up fw_event
+ * @ioc: per adapter object
+ * @fw_event: object describing the event
+ * Context: This function will acquire ioc->fw_event_lock.
+ *
+ * This adds the firmware event object into link list, then queues it up to
+ * be processed from user context.
+ */
+static void
+_scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
+{
+ unsigned long flags;
+
+ if (ioc->firmware_event_thread == NULL)
+ return;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ fw_event_work_get(fw_event);
+ INIT_LIST_HEAD(&fw_event->list);
+ list_add_tail(&fw_event->list, &ioc->fw_event_list);
+ INIT_WORK(&fw_event->work, _firmware_event_work);
+ fw_event_work_get(fw_event);
+ queue_work(ioc->firmware_event_thread, &fw_event->work);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_fw_event_del_from_list - delete fw_event from the list
+ * @ioc: per adapter object
+ * @fw_event: object describing the event
+ * Context: This function will acquire ioc->fw_event_lock.
+ *
+ * If the fw_event is on the fw_event_list, remove it and do a put.
+ */
+static void
+_scsih_fw_event_del_from_list(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work
+ *fw_event)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ if (!list_empty(&fw_event->list)) {
+ list_del_init(&fw_event->list);
+ fw_event_work_put(fw_event);
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+
+ /**
+ * mpt3sas_send_trigger_data_event - send event for processing trigger data
+ * @ioc: per adapter object
+ * @event_data: trigger event data
+ */
+void
+mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data)
+{
+ struct fw_event_work *fw_event;
+ u16 sz;
+
+ if (ioc->is_driver_loading)
+ return;
+ sz = sizeof(*event_data);
+ fw_event = alloc_fw_event_work(sz);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG;
+ fw_event->ioc = ioc;
+ memcpy(fw_event->event_data, event_data, sizeof(*event_data));
+ _scsih_fw_event_add(ioc, fw_event);
+ fw_event_work_put(fw_event);
+}
+
+/**
+ * _scsih_error_recovery_delete_devices - remove devices not responding
+ * @ioc: per adapter object
+ */
+static void
+_scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event;
+
+ fw_event = alloc_fw_event_work(0);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES;
+ fw_event->ioc = ioc;
+ _scsih_fw_event_add(ioc, fw_event);
+ fw_event_work_put(fw_event);
+}
+
+/**
+ * mpt3sas_port_enable_complete - port enable completed (fake event)
+ * @ioc: per adapter object
+ */
+void
+mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event;
+
+ fw_event = alloc_fw_event_work(0);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE;
+ fw_event->ioc = ioc;
+ _scsih_fw_event_add(ioc, fw_event);
+ fw_event_work_put(fw_event);
+}
+
+static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+ struct fw_event_work *fw_event = NULL;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ if (!list_empty(&ioc->fw_event_list)) {
+ fw_event = list_first_entry(&ioc->fw_event_list,
+ struct fw_event_work, list);
+ list_del_init(&fw_event->list);
+ fw_event_work_put(fw_event);
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
+ return fw_event;
+}
+
+/**
+ * _scsih_fw_event_cleanup_queue - cleanup event queue
+ * @ioc: per adapter object
+ *
+ * Walk the firmware event queue, either killing timers, or waiting
+ * for outstanding events to complete
+ *
+ * Context: task, can sleep
+ */
+static void
+_scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event;
+
+ if ((list_empty(&ioc->fw_event_list) && !ioc->current_event) ||
+ !ioc->firmware_event_thread)
+ return;
+ /*
+ * Set current running event as ignore, so that
+ * current running event will exit quickly.
+ * As diag reset has occurred it is of no use
+ * to process remaining stale event data entries.
+ */
+ if (ioc->shost_recovery && ioc->current_event)
+ ioc->current_event->ignore = 1;
+
+ ioc->fw_events_cleanup = 1;
+ while ((fw_event = dequeue_next_fw_event(ioc)) ||
+ (fw_event = ioc->current_event)) {
+
+ /*
+ * Don't call cancel_work_sync() for current_event
+ * other than MPT3SAS_REMOVE_UNRESPONDING_DEVICES;
+ * otherwise we may observe deadlock if current
+ * hard reset issued as part of processing the current_event.
+ *
+ * Orginal logic of cleaning the current_event is added
+ * for handling the back to back host reset issued by the user.
+ * i.e. during back to back host reset, driver use to process
+ * the two instances of MPT3SAS_REMOVE_UNRESPONDING_DEVICES
+ * event back to back and this made the drives to unregister
+ * the devices from SML.
+ */
+
+ if (fw_event == ioc->current_event &&
+ ioc->current_event->event !=
+ MPT3SAS_REMOVE_UNRESPONDING_DEVICES) {
+ ioc->current_event = NULL;
+ continue;
+ }
+
+ /*
+ * Driver has to clear ioc->start_scan flag when
+ * it is cleaning up MPT3SAS_PORT_ENABLE_COMPLETE,
+ * otherwise scsi_scan_host() API waits for the
+ * 5 minute timer to expire. If we exit from
+ * scsi_scan_host() early then we can issue the
+ * new port enable request as part of current diag reset.
+ */
+ if (fw_event->event == MPT3SAS_PORT_ENABLE_COMPLETE) {
+ ioc->port_enable_cmds.status |= MPT3_CMD_RESET;
+ ioc->start_scan = 0;
+ }
+
+ /*
+ * Wait on the fw_event to complete. If this returns 1, then
+ * the event was never executed, and we need a put for the
+ * reference the work had on the fw_event.
+ *
+ * If it did execute, we wait for it to finish, and the put will
+ * happen from _firmware_event_work()
+ */
+ if (cancel_work_sync(&fw_event->work))
+ fw_event_work_put(fw_event);
+
+ }
+ ioc->fw_events_cleanup = 0;
+}
+
+/**
+ * _scsih_internal_device_block - block the sdev device
+ * @sdev: per device object
+ * @sas_device_priv_data : per device driver private data
+ *
+ * make sure device is blocked without error, if not
+ * print an error
+ */
+static void
+_scsih_internal_device_block(struct scsi_device *sdev,
+ struct MPT3SAS_DEVICE *sas_device_priv_data)
+{
+ int r = 0;
+
+ sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n",
+ sas_device_priv_data->sas_target->handle);
+ sas_device_priv_data->block = 1;
+
+ r = scsi_internal_device_block_nowait(sdev);
+ if (r == -EINVAL)
+ sdev_printk(KERN_WARNING, sdev,
+ "device_block failed with return(%d) for handle(0x%04x)\n",
+ r, sas_device_priv_data->sas_target->handle);
+}
+
+/**
+ * _scsih_internal_device_unblock - unblock the sdev device
+ * @sdev: per device object
+ * @sas_device_priv_data : per device driver private data
+ * make sure device is unblocked without error, if not retry
+ * by blocking and then unblocking
+ */
+
+static void
+_scsih_internal_device_unblock(struct scsi_device *sdev,
+ struct MPT3SAS_DEVICE *sas_device_priv_data)
+{
+ int r = 0;
+
+ sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, "
+ "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle);
+ sas_device_priv_data->block = 0;
+ r = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
+ if (r == -EINVAL) {
+ /* The device has been set to SDEV_RUNNING by SD layer during
+ * device addition but the request queue is still stopped by
+ * our earlier block call. We need to perform a block again
+ * to get the device to SDEV_BLOCK and then to SDEV_RUNNING */
+
+ sdev_printk(KERN_WARNING, sdev,
+ "device_unblock failed with return(%d) for handle(0x%04x) "
+ "performing a block followed by an unblock\n",
+ r, sas_device_priv_data->sas_target->handle);
+ sas_device_priv_data->block = 1;
+ r = scsi_internal_device_block_nowait(sdev);
+ if (r)
+ sdev_printk(KERN_WARNING, sdev, "retried device_block "
+ "failed with return(%d) for handle(0x%04x)\n",
+ r, sas_device_priv_data->sas_target->handle);
+
+ sas_device_priv_data->block = 0;
+ r = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
+ if (r)
+ sdev_printk(KERN_WARNING, sdev, "retried device_unblock"
+ " failed with return(%d) for handle(0x%04x)\n",
+ r, sas_device_priv_data->sas_target->handle);
+ }
+}
+
+/**
+ * _scsih_ublock_io_all_device - unblock every device
+ * @ioc: per adapter object
+ *
+ * change the device state from block to running
+ */
+static void
+_scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (!sas_device_priv_data->block)
+ continue;
+
+ dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
+ "device_running, handle(0x%04x)\n",
+ sas_device_priv_data->sas_target->handle));
+ _scsih_internal_device_unblock(sdev, sas_device_priv_data);
+ }
+}
+
+
+/**
+ * _scsih_ublock_io_device - prepare device to be deleted
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @port: hba port entry
+ *
+ * unblock then put device in offline state
+ */
+static void
+_scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct hba_port *port)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target)
+ continue;
+ if (sas_device_priv_data->sas_target->sas_address
+ != sas_address)
+ continue;
+ if (sas_device_priv_data->sas_target->port != port)
+ continue;
+ if (sas_device_priv_data->block)
+ _scsih_internal_device_unblock(sdev,
+ sas_device_priv_data);
+ }
+}
+
+/**
+ * _scsih_block_io_all_device - set the device state to SDEV_BLOCK
+ * @ioc: per adapter object
+ *
+ * During device pull we need to appropriately set the sdev state.
+ */
+static void
+_scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->block)
+ continue;
+ if (sas_device_priv_data->ignore_delay_remove) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s skip device_block for SES handle(0x%04x)\n",
+ __func__, sas_device_priv_data->sas_target->handle);
+ continue;
+ }
+ _scsih_internal_device_block(sdev, sas_device_priv_data);
+ }
+}
+
+/**
+ * _scsih_block_io_device - set the device state to SDEV_BLOCK
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During device pull we need to appropriately set the sdev state.
+ */
+static void
+_scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+ struct _sas_device *sas_device;
+
+ sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle != handle)
+ continue;
+ if (sas_device_priv_data->block)
+ continue;
+ if (sas_device && sas_device->pend_sas_rphy_add)
+ continue;
+ if (sas_device_priv_data->ignore_delay_remove) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s skip device_block for SES handle(0x%04x)\n",
+ __func__, sas_device_priv_data->sas_target->handle);
+ continue;
+ }
+ _scsih_internal_device_block(sdev, sas_device_priv_data);
+ }
+
+ if (sas_device)
+ sas_device_put(sas_device);
+}
+
+/**
+ * _scsih_block_io_to_children_attached_to_ex
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ *
+ * This routine set sdev state to SDEV_BLOCK for all devices
+ * attached to this expander. This function called when expander is
+ * pulled.
+ */
+static void
+_scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ struct _sas_port *mpt3sas_port;
+ struct _sas_device *sas_device;
+ struct _sas_node *expander_sibling;
+ unsigned long flags;
+
+ if (!sas_expander)
+ return;
+
+ list_for_each_entry(mpt3sas_port,
+ &sas_expander->sas_port_list, port_list) {
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_port->hba_port);
+ if (sas_device) {
+ set_bit(sas_device->handle,
+ ioc->blocking_handles);
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+ }
+
+ list_for_each_entry(mpt3sas_port,
+ &sas_expander->sas_port_list, port_list) {
+
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mpt3sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE) {
+ expander_sibling =
+ mpt3sas_scsih_expander_find_by_sas_address(
+ ioc, mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_port->hba_port);
+ _scsih_block_io_to_children_attached_to_ex(ioc,
+ expander_sibling);
+ }
+ }
+}
+
+/**
+ * _scsih_block_io_to_children_attached_directly
+ * @ioc: per adapter object
+ * @event_data: topology change event data
+ *
+ * This routine set sdev state to SDEV_BLOCK for all devices
+ * direct attached during device pull.
+ */
+static void
+_scsih_block_io_to_children_attached_directly(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
+ _scsih_block_io_device(ioc, handle);
+ }
+}
+
+/**
+ * _scsih_block_io_to_pcie_children_attached_directly
+ * @ioc: per adapter object
+ * @event_data: topology change event data
+ *
+ * This routine set sdev state to SDEV_BLOCK for all devices
+ * direct attached during device pull/reconnect.
+ */
+static void
+_scsih_block_io_to_pcie_children_attached_directly(struct MPT3SAS_ADAPTER *ioc,
+ Mpi26EventDataPCIeTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle =
+ le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ reason_code = event_data->PortEntry[i].PortStatus;
+ if (reason_code ==
+ MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING)
+ _scsih_block_io_device(ioc, handle);
+ }
+}
+/**
+ * _scsih_tm_tr_send - send task management request
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt time.
+ *
+ * This code is to initiate the device removal handshake protocol
+ * with controller firmware. This function will issue target reset
+ * using high priority request queue. It will send a sas iounit
+ * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion.
+ *
+ * This is designed to send muliple task management request at the same
+ * time to the fifo. If the fifo is full, we will append the request,
+ * and process it in a future completion.
+ */
+static void
+_scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2SCSITaskManagementRequest_t *mpi_request;
+ u16 smid;
+ struct _sas_device *sas_device = NULL;
+ struct _pcie_device *pcie_device = NULL;
+ struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
+ u64 sas_address = 0;
+ unsigned long flags;
+ struct _tr_list *delayed_tr;
+ u32 ioc_state;
+ u8 tr_method = 0;
+ struct hba_port *port = NULL;
+
+ if (ioc->pci_error_recovery) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host in pci error recovery: handle(0x%04x)\n",
+ __func__, handle));
+ return;
+ }
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host is not operational: handle(0x%04x)\n",
+ __func__, handle));
+ return;
+ }
+
+ /* if PD, then return */
+ if (test_bit(handle, ioc->pd_handles))
+ return;
+
+ clear_bit(handle, ioc->pend_os_device_add);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (sas_device && sas_device->starget &&
+ sas_device->starget->hostdata) {
+ sas_target_priv_data = sas_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ sas_address = sas_device->sas_address;
+ port = sas_device->port;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device) {
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle);
+ if (pcie_device && pcie_device->starget &&
+ pcie_device->starget->hostdata) {
+ sas_target_priv_data = pcie_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ sas_address = pcie_device->wwid;
+ }
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ if (pcie_device && (!ioc->tm_custom_handling) &&
+ (!(mpt3sas_scsih_is_pcie_scsi_device(
+ pcie_device->device_info))))
+ tr_method =
+ MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE;
+ else
+ tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
+ }
+ if (sas_target_priv_data) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n",
+ handle, (u64)sas_address));
+ if (sas_device) {
+ if (sas_device->enclosure_handle != 0)
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting delete flag:enclosure logical id(0x%016llx), slot(%d)\n",
+ (u64)sas_device->enclosure_logical_id,
+ sas_device->slot));
+ if (sas_device->connector_name[0] != '\0')
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting delete flag: enclosure level(0x%04x), connector name( %s)\n",
+ sas_device->enclosure_level,
+ sas_device->connector_name));
+ } else if (pcie_device) {
+ if (pcie_device->enclosure_handle != 0)
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting delete flag: logical id(0x%016llx), slot(%d)\n",
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot));
+ if (pcie_device->connector_name[0] != '\0')
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting delete flag:, enclosure level(0x%04x), connector name( %s)\n",
+ pcie_device->enclosure_level,
+ pcie_device->connector_name));
+ }
+ _scsih_ublock_io_device(ioc, sas_address, port);
+ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;
+ }
+
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
+ if (!smid) {
+ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
+ if (!delayed_tr)
+ goto out;
+ INIT_LIST_HEAD(&delayed_tr->list);
+ delayed_tr->handle = handle;
+ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
+ dewtprintk(ioc,
+ ioc_info(ioc, "DELAYED:tr:handle(0x%04x), (open)\n",
+ handle));
+ goto out;
+ }
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n",
+ handle, smid, ioc->tm_tr_cb_idx));
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+ mpi_request->MsgFlags = tr_method;
+ set_bit(handle, ioc->device_remove_in_progress);
+ ioc->put_smid_hi_priority(ioc, smid, 0);
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL);
+
+out:
+ if (sas_device)
+ sas_device_put(sas_device);
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+}
+
+/**
+ * _scsih_tm_tr_complete -
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
+ *
+ * This is the target reset completion routine.
+ * This code is part of the code to initiate the device removal
+ * handshake protocol with controller firmware.
+ * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE)
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ u16 handle;
+ Mpi2SCSITaskManagementRequest_t *mpi_request_tm;
+ Mpi2SCSITaskManagementReply_t *mpi_reply =
+ mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ Mpi2SasIoUnitControlRequest_t *mpi_request;
+ u16 smid_sas_ctrl;
+ u32 ioc_state;
+ struct _sc_list *delayed_sc;
+
+ if (ioc->pci_error_recovery) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host in pci error recovery\n",
+ __func__));
+ return 1;
+ }
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host is not operational\n",
+ __func__));
+ return 1;
+ }
+ if (unlikely(!mpi_reply)) {
+ ioc_err(ioc, "mpi_reply not valid at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 1;
+ }
+ mpi_request_tm = mpt3sas_base_get_msg_frame(ioc, smid);
+ handle = le16_to_cpu(mpi_request_tm->DevHandle);
+ if (handle != le16_to_cpu(mpi_reply->DevHandle)) {
+ dewtprintk(ioc,
+ ioc_err(ioc, "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n",
+ handle,
+ le16_to_cpu(mpi_reply->DevHandle), smid));
+ return 0;
+ }
+
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT);
+ dewtprintk(ioc,
+ ioc_info(ioc, "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), loginfo(0x%08x), completed(%d)\n",
+ handle, smid, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+
+ smid_sas_ctrl = mpt3sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx);
+ if (!smid_sas_ctrl) {
+ delayed_sc = kzalloc(sizeof(*delayed_sc), GFP_ATOMIC);
+ if (!delayed_sc)
+ return _scsih_check_for_pending_tm(ioc, smid);
+ INIT_LIST_HEAD(&delayed_sc->list);
+ delayed_sc->handle = le16_to_cpu(mpi_request_tm->DevHandle);
+ list_add_tail(&delayed_sc->list, &ioc->delayed_sc_list);
+ dewtprintk(ioc,
+ ioc_info(ioc, "DELAYED:sc:handle(0x%04x), (open)\n",
+ handle));
+ return _scsih_check_for_pending_tm(ioc, smid);
+ }
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n",
+ handle, smid_sas_ctrl, ioc->tm_sas_control_cb_idx));
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid_sas_ctrl);
+ memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
+ mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
+ mpi_request->DevHandle = mpi_request_tm->DevHandle;
+ ioc->put_smid_default(ioc, smid_sas_ctrl);
+
+ return _scsih_check_for_pending_tm(ioc, smid);
+}
+
+/** _scsih_allow_scmd_to_device - check whether scmd needs to
+ * issue to IOC or not.
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ *
+ * Returns true if scmd can be issued to IOC otherwise returns false.
+ */
+inline bool _scsih_allow_scmd_to_device(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd)
+{
+
+ if (ioc->pci_error_recovery)
+ return false;
+
+ if (ioc->hba_mpi_version_belonged == MPI2_VERSION) {
+ if (ioc->remove_host)
+ return false;
+
+ return true;
+ }
+
+ if (ioc->remove_host) {
+
+ switch (scmd->cmnd[0]) {
+ case SYNCHRONIZE_CACHE:
+ case START_STOP:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * _scsih_sas_control_complete - completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
+ *
+ * This is the sas iounit control completion routine.
+ * This code is part of the code to initiate the device removal
+ * handshake protocol with controller firmware.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_sas_control_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 msix_index, u32 reply)
+{
+ Mpi2SasIoUnitControlReply_t *mpi_reply =
+ mpt3sas_base_get_reply_virt_addr(ioc, reply);
+
+ if (likely(mpi_reply)) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "sc_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply->DevHandle), smid,
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo)));
+ if (le16_to_cpu(mpi_reply->IOCStatus) ==
+ MPI2_IOCSTATUS_SUCCESS) {
+ clear_bit(le16_to_cpu(mpi_reply->DevHandle),
+ ioc->device_remove_in_progress);
+ }
+ } else {
+ ioc_err(ioc, "mpi_reply not valid at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ }
+ return mpt3sas_check_for_pending_internal_cmds(ioc, smid);
+}
+
+/**
+ * _scsih_tm_tr_volume_send - send target reset request for volumes
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt time.
+ *
+ * This is designed to send muliple task management request at the same
+ * time to the fifo. If the fifo is full, we will append the request,
+ * and process it in a future completion.
+ */
+static void
+_scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2SCSITaskManagementRequest_t *mpi_request;
+ u16 smid;
+ struct _tr_list *delayed_tr;
+
+ if (ioc->pci_error_recovery) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host reset in progress!\n",
+ __func__));
+ return;
+ }
+
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_tr_volume_cb_idx);
+ if (!smid) {
+ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
+ if (!delayed_tr)
+ return;
+ INIT_LIST_HEAD(&delayed_tr->list);
+ delayed_tr->handle = handle;
+ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_volume_list);
+ dewtprintk(ioc,
+ ioc_info(ioc, "DELAYED:tr:handle(0x%04x), (open)\n",
+ handle));
+ return;
+ }
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n",
+ handle, smid, ioc->tm_tr_volume_cb_idx));
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+ ioc->put_smid_hi_priority(ioc, smid, 0);
+}
+
+/**
+ * _scsih_tm_volume_tr_complete - target reset completion
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 msix_index, u32 reply)
+{
+ u16 handle;
+ Mpi2SCSITaskManagementRequest_t *mpi_request_tm;
+ Mpi2SCSITaskManagementReply_t *mpi_reply =
+ mpt3sas_base_get_reply_virt_addr(ioc, reply);
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host reset in progress!\n",
+ __func__));
+ return 1;
+ }
+ if (unlikely(!mpi_reply)) {
+ ioc_err(ioc, "mpi_reply not valid at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 1;
+ }
+
+ mpi_request_tm = mpt3sas_base_get_msg_frame(ioc, smid);
+ handle = le16_to_cpu(mpi_request_tm->DevHandle);
+ if (handle != le16_to_cpu(mpi_reply->DevHandle)) {
+ dewtprintk(ioc,
+ ioc_err(ioc, "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n",
+ handle, le16_to_cpu(mpi_reply->DevHandle),
+ smid));
+ return 0;
+ }
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), loginfo(0x%08x), completed(%d)\n",
+ handle, smid, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+
+ return _scsih_check_for_pending_tm(ioc, smid);
+}
+
+/**
+ * _scsih_issue_delayed_event_ack - issue delayed Event ACK messages
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @event: Event ID
+ * @event_context: used to track events uniquely
+ *
+ * Context - processed in interrupt context.
+ */
+static void
+_scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, U16 event,
+ U32 event_context)
+{
+ Mpi2EventAckRequest_t *ack_request;
+ int i = smid - ioc->internal_smid;
+ unsigned long flags;
+
+ /* Without releasing the smid just update the
+ * call back index and reuse the same smid for
+ * processing this delayed request
+ */
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ ioc->internal_lookup[i].cb_idx = ioc->base_cb_idx;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "EVENT ACK: event(0x%04x), smid(%d), cb(%d)\n",
+ le16_to_cpu(event), smid, ioc->base_cb_idx));
+ ack_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t));
+ ack_request->Function = MPI2_FUNCTION_EVENT_ACK;
+ ack_request->Event = event;
+ ack_request->EventContext = event_context;
+ ack_request->VF_ID = 0; /* TODO */
+ ack_request->VP_ID = 0;
+ ioc->put_smid_default(ioc, smid);
+}
+
+/**
+ * _scsih_issue_delayed_sas_io_unit_ctrl - issue delayed
+ * sas_io_unit_ctrl messages
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle
+ *
+ * Context - processed in interrupt context.
+ */
+static void
+_scsih_issue_delayed_sas_io_unit_ctrl(struct MPT3SAS_ADAPTER *ioc,
+ u16 smid, u16 handle)
+{
+ Mpi2SasIoUnitControlRequest_t *mpi_request;
+ u32 ioc_state;
+ int i = smid - ioc->internal_smid;
+ unsigned long flags;
+
+ if (ioc->remove_host) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host has been removed\n",
+ __func__));
+ return;
+ } else if (ioc->pci_error_recovery) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host in pci error recovery\n",
+ __func__));
+ return;
+ }
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: host is not operational\n",
+ __func__));
+ return;
+ }
+
+ /* Without releasing the smid just update the
+ * call back index and reuse the same smid for
+ * processing this delayed request
+ */
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ ioc->internal_lookup[i].cb_idx = ioc->tm_sas_control_cb_idx;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n",
+ handle, smid, ioc->tm_sas_control_cb_idx));
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
+ mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ ioc->put_smid_default(ioc, smid);
+}
+
+/**
+ * mpt3sas_check_for_pending_internal_cmds - check for pending internal messages
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Context: Executed in interrupt context
+ *
+ * This will check delayed internal messages list, and process the
+ * next request.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_check_for_pending_internal_cmds(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ struct _sc_list *delayed_sc;
+ struct _event_ack_list *delayed_event_ack;
+
+ if (!list_empty(&ioc->delayed_event_ack_list)) {
+ delayed_event_ack = list_entry(ioc->delayed_event_ack_list.next,
+ struct _event_ack_list, list);
+ _scsih_issue_delayed_event_ack(ioc, smid,
+ delayed_event_ack->Event, delayed_event_ack->EventContext);
+ list_del(&delayed_event_ack->list);
+ kfree(delayed_event_ack);
+ return 0;
+ }
+
+ if (!list_empty(&ioc->delayed_sc_list)) {
+ delayed_sc = list_entry(ioc->delayed_sc_list.next,
+ struct _sc_list, list);
+ _scsih_issue_delayed_sas_io_unit_ctrl(ioc, smid,
+ delayed_sc->handle);
+ list_del(&delayed_sc->list);
+ kfree(delayed_sc);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * _scsih_check_for_pending_tm - check for pending task management
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * This will check delayed target reset list, and feed the
+ * next reqeust.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ struct _tr_list *delayed_tr;
+
+ if (!list_empty(&ioc->delayed_tr_volume_list)) {
+ delayed_tr = list_entry(ioc->delayed_tr_volume_list.next,
+ struct _tr_list, list);
+ mpt3sas_base_free_smid(ioc, smid);
+ _scsih_tm_tr_volume_send(ioc, delayed_tr->handle);
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ return 0;
+ }
+
+ if (!list_empty(&ioc->delayed_tr_list)) {
+ delayed_tr = list_entry(ioc->delayed_tr_list.next,
+ struct _tr_list, list);
+ mpt3sas_base_free_smid(ioc, smid);
+ _scsih_tm_tr_send(ioc, delayed_tr->handle);
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * _scsih_check_topo_delete_events - sanity check on topo events
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ *
+ * This routine added to better handle cable breaker.
+ *
+ * This handles the case where driver receives multiple expander
+ * add and delete events in a single shot. When there is a delete event
+ * the routine will void any pending add events waiting in the event queue.
+ */
+static void
+_scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ struct fw_event_work *fw_event;
+ Mpi2EventDataSasTopologyChangeList_t *local_event_data;
+ u16 expander_handle;
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+ int i, reason_code;
+ u16 handle;
+
+ for (i = 0 ; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)
+ _scsih_tm_tr_send(ioc, handle);
+ }
+
+ expander_handle = le16_to_cpu(event_data->ExpanderDevHandle);
+ if (expander_handle < ioc->sas_hba.num_phys) {
+ _scsih_block_io_to_children_attached_directly(ioc, event_data);
+ return;
+ }
+ if (event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING) {
+ /* put expander attached devices into blocking state */
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_handle(ioc,
+ expander_handle);
+ _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ do {
+ handle = find_first_bit(ioc->blocking_handles,
+ ioc->facts.MaxDevHandle);
+ if (handle < ioc->facts.MaxDevHandle)
+ _scsih_block_io_device(ioc, handle);
+ } while (test_and_clear_bit(handle, ioc->blocking_handles));
+ } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING)
+ _scsih_block_io_to_children_attached_directly(ioc, event_data);
+
+ if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
+ return;
+
+ /* mark ignore flag for pending events */
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
+ if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+ fw_event->ignore)
+ continue;
+ local_event_data = (Mpi2EventDataSasTopologyChangeList_t *)
+ fw_event->event_data;
+ if (local_event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_ADDED ||
+ local_event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
+ if (le16_to_cpu(local_event_data->ExpanderDevHandle) ==
+ expander_handle) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting ignoring flag\n"));
+ fw_event->ignore = 1;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_check_pcie_topo_remove_events - sanity check on topo
+ * events
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ *
+ * This handles the case where driver receives multiple switch
+ * or device add and delete events in a single shot. When there
+ * is a delete event the routine will void any pending add
+ * events waiting in the event queue.
+ */
+static void
+_scsih_check_pcie_topo_remove_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi26EventDataPCIeTopologyChangeList_t *event_data)
+{
+ struct fw_event_work *fw_event;
+ Mpi26EventDataPCIeTopologyChangeList_t *local_event_data;
+ unsigned long flags;
+ int i, reason_code;
+ u16 handle, switch_handle;
+
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle =
+ le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ reason_code = event_data->PortEntry[i].PortStatus;
+ if (reason_code == MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING)
+ _scsih_tm_tr_send(ioc, handle);
+ }
+
+ switch_handle = le16_to_cpu(event_data->SwitchDevHandle);
+ if (!switch_handle) {
+ _scsih_block_io_to_pcie_children_attached_directly(
+ ioc, event_data);
+ return;
+ }
+ /* TODO We are not supporting cascaded PCIe Switch removal yet*/
+ if ((event_data->SwitchStatus
+ == MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING) ||
+ (event_data->SwitchStatus ==
+ MPI26_EVENT_PCIE_TOPO_SS_RESPONDING))
+ _scsih_block_io_to_pcie_children_attached_directly(
+ ioc, event_data);
+
+ if (event_data->SwitchStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
+ return;
+
+ /* mark ignore flag for pending events */
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
+ if (fw_event->event != MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST ||
+ fw_event->ignore)
+ continue;
+ local_event_data =
+ (Mpi26EventDataPCIeTopologyChangeList_t *)
+ fw_event->event_data;
+ if (local_event_data->SwitchStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_ADDED ||
+ local_event_data->SwitchStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
+ if (le16_to_cpu(local_event_data->SwitchDevHandle) ==
+ switch_handle) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting ignoring flag for switch event\n"));
+ fw_event->ignore = 1;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_set_volume_delete_flag - setting volume delete flag
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * This returns nothing.
+ */
+static void
+_scsih_set_volume_delete_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _raid_device *raid_device;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle);
+ if (raid_device && raid_device->starget &&
+ raid_device->starget->hostdata) {
+ sas_target_priv_data =
+ raid_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ dewtprintk(ioc,
+ ioc_info(ioc, "setting delete flag: handle(0x%04x), wwid(0x%016llx)\n",
+ handle, (u64)raid_device->wwid));
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_set_volume_handle_for_tr - set handle for target reset to volume
+ * @handle: input handle
+ * @a: handle for volume a
+ * @b: handle for volume b
+ *
+ * IR firmware only supports two raid volumes. The purpose of this
+ * routine is to set the volume handle in either a or b. When the given
+ * input handle is non-zero, or when a and b have not been set before.
+ */
+static void
+_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b)
+{
+ if (!handle || handle == *a || handle == *b)
+ return;
+ if (!*a)
+ *a = handle;
+ else if (!*b)
+ *b = handle;
+}
+
+/**
+ * _scsih_check_ir_config_unhide_events - check for UNHIDE events
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ * Context: interrupt time.
+ *
+ * This routine will send target reset to volume, followed by target
+ * resets to the PDs. This is called when a PD has been removed, or
+ * volume has been deleted or removed. When the target reset is sent
+ * to volume, the PD target resets need to be queued to start upon
+ * completion of the volume target reset.
+ */
+static void
+_scsih_check_ir_config_unhide_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+ Mpi2EventIrConfigElement_t *element;
+ int i;
+ u16 handle, volume_handle, a, b;
+ struct _tr_list *delayed_tr;
+
+ a = 0;
+ b = 0;
+
+ if (ioc->is_warpdrive)
+ return;
+
+ /* Volume Resets for Deleted or Removed */
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+ continue;
+ if (element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED ||
+ element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_REMOVED) {
+ volume_handle = le16_to_cpu(element->VolDevHandle);
+ _scsih_set_volume_delete_flag(ioc, volume_handle);
+ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
+ }
+ }
+
+ /* Volume Resets for UNHIDE events */
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+ continue;
+ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) {
+ volume_handle = le16_to_cpu(element->VolDevHandle);
+ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
+ }
+ }
+
+ if (a)
+ _scsih_tm_tr_volume_send(ioc, a);
+ if (b)
+ _scsih_tm_tr_volume_send(ioc, b);
+
+ /* PD target resets */
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE)
+ continue;
+ handle = le16_to_cpu(element->PhysDiskDevHandle);
+ volume_handle = le16_to_cpu(element->VolDevHandle);
+ clear_bit(handle, ioc->pd_handles);
+ if (!volume_handle)
+ _scsih_tm_tr_send(ioc, handle);
+ else if (volume_handle == a || volume_handle == b) {
+ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
+ BUG_ON(!delayed_tr);
+ INIT_LIST_HEAD(&delayed_tr->list);
+ delayed_tr->handle = handle;
+ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
+ dewtprintk(ioc,
+ ioc_info(ioc, "DELAYED:tr:handle(0x%04x), (open)\n",
+ handle));
+ } else
+ _scsih_tm_tr_send(ioc, handle);
+ }
+}
+
+
+/**
+ * _scsih_check_volume_delete_events - set delete flag for volumes
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ * Context: interrupt time.
+ *
+ * This will handle the case when the cable connected to entire volume is
+ * pulled. We will take care of setting the deleted flag so normal IO will
+ * not be sent.
+ */
+static void
+_scsih_check_volume_delete_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrVolume_t *event_data)
+{
+ u32 state;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
+ return;
+ state = le32_to_cpu(event_data->NewValue);
+ if (state == MPI2_RAID_VOL_STATE_MISSING || state ==
+ MPI2_RAID_VOL_STATE_FAILED)
+ _scsih_set_volume_delete_flag(ioc,
+ le16_to_cpu(event_data->VolDevHandle));
+}
+
+/**
+ * _scsih_temp_threshold_events - display temperature threshold exceeded events
+ * @ioc: per adapter object
+ * @event_data: the temp threshold event data
+ * Context: interrupt time.
+ */
+static void
+_scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataTemperature_t *event_data)
+{
+ u32 doorbell;
+ if (ioc->temp_sensors_count >= event_data->SensorNum) {
+ ioc_err(ioc, "Temperature Threshold flags %s%s%s%s exceeded for Sensor: %d !!!\n",
+ le16_to_cpu(event_data->Status) & 0x1 ? "0 " : " ",
+ le16_to_cpu(event_data->Status) & 0x2 ? "1 " : " ",
+ le16_to_cpu(event_data->Status) & 0x4 ? "2 " : " ",
+ le16_to_cpu(event_data->Status) & 0x8 ? "3 " : " ",
+ event_data->SensorNum);
+ ioc_err(ioc, "Current Temp In Celsius: %d\n",
+ event_data->CurrentTemperature);
+ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) {
+ doorbell = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc,
+ doorbell & MPI2_DOORBELL_DATA_MASK);
+ } else if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ mpt3sas_print_coredump_info(ioc,
+ doorbell & MPI2_DOORBELL_DATA_MASK);
+ }
+ }
+ }
+}
+
+static int _scsih_set_satl_pending(struct scsi_cmnd *scmd, bool pending)
+{
+ struct MPT3SAS_DEVICE *priv = scmd->device->hostdata;
+
+ if (scmd->cmnd[0] != ATA_12 && scmd->cmnd[0] != ATA_16)
+ return 0;
+
+ if (pending)
+ return test_and_set_bit(0, &priv->ata_command_pending);
+
+ clear_bit(0, &priv->ata_command_pending);
+ return 0;
+}
+
+/**
+ * _scsih_flush_running_cmds - completing outstanding commands.
+ * @ioc: per adapter object
+ *
+ * The flushing out of all pending scmd commands following host reset,
+ * where all IO is dropped to the floor.
+ */
+static void
+_scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct scsi_cmnd *scmd;
+ struct scsiio_tracker *st;
+ u16 smid;
+ int count = 0;
+
+ for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (!scmd)
+ continue;
+ count++;
+ _scsih_set_satl_pending(scmd, false);
+ st = scsi_cmd_priv(scmd);
+ mpt3sas_base_clear_st(ioc, st);
+ scsi_dma_unmap(scmd);
+ if (ioc->pci_error_recovery || ioc->remove_host)
+ scmd->result = DID_NO_CONNECT << 16;
+ else
+ scmd->result = DID_RESET << 16;
+ scsi_done(scmd);
+ }
+ dtmprintk(ioc, ioc_info(ioc, "completing %d cmds\n", count));
+}
+
+/**
+ * _scsih_setup_eedp - setup MPI request for EEDP transfer
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ * @mpi_request: pointer to the SCSI_IO request message frame
+ *
+ * Supporting protection 1 and 3.
+ */
+static void
+_scsih_setup_eedp(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ Mpi25SCSIIORequest_t *mpi_request)
+{
+ u16 eedp_flags;
+ Mpi25SCSIIORequest_t *mpi_request_3v =
+ (Mpi25SCSIIORequest_t *)mpi_request;
+
+ switch (scsi_get_prot_op(scmd)) {
+ case SCSI_PROT_READ_STRIP:
+ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP;
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP;
+ break;
+ default:
+ return;
+ }
+
+ if (scmd->prot_flags & SCSI_PROT_GUARD_CHECK)
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+
+ if (scmd->prot_flags & SCSI_PROT_REF_CHECK)
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG;
+
+ if (scmd->prot_flags & SCSI_PROT_REF_INCREMENT) {
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG;
+
+ mpi_request->CDB.EEDP32.PrimaryReferenceTag =
+ cpu_to_be32(scsi_prot_ref_tag(scmd));
+ }
+
+ mpi_request_3v->EEDPBlockSize = cpu_to_le16(scsi_prot_interval(scmd));
+
+ if (ioc->is_gen35_ioc)
+ eedp_flags |= MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE;
+ mpi_request->EEDPFlags = cpu_to_le16(eedp_flags);
+}
+
+/**
+ * _scsih_eedp_error_handling - return sense code for EEDP errors
+ * @scmd: pointer to scsi command object
+ * @ioc_status: ioc status
+ */
+static void
+_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
+{
+ u8 ascq;
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ ascq = 0x01;
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ ascq = 0x02;
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ ascq = 0x03;
+ break;
+ default:
+ ascq = 0x00;
+ break;
+ }
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x10, ascq);
+ set_host_byte(scmd, DID_ABORT);
+}
+
+/**
+ * scsih_qcmd - main scsi request entry point
+ * @shost: SCSI host pointer
+ * @scmd: pointer to scsi command object
+ *
+ * The callback index is set inside `ioc->scsi_io_cb_idx`.
+ *
+ * Return: 0 on success. If there's a failure, return either:
+ * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or
+ * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full
+ */
+static int
+scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _raid_device *raid_device;
+ struct request *rq = scsi_cmd_to_rq(scmd);
+ int class;
+ Mpi25SCSIIORequest_t *mpi_request;
+ struct _pcie_device *pcie_device = NULL;
+ u32 mpi_control;
+ u16 smid;
+ u16 handle;
+
+ if (ioc->logging_level & MPT_DEBUG_SCSI)
+ scsi_print_command(scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ }
+
+ if (!(_scsih_allow_scmd_to_device(ioc, scmd))) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ }
+
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+
+ /* invalid device handle */
+ handle = sas_target_priv_data->handle;
+
+ /*
+ * Avoid error handling escallation when device is disconnected
+ */
+ if (handle == MPT3SAS_INVALID_DEVICE_HANDLE || sas_device_priv_data->block) {
+ if (scmd->device->host->shost_state == SHOST_RECOVERY &&
+ scmd->cmnd[0] == TEST_UNIT_READY) {
+ scsi_build_sense(scmd, 0, UNIT_ATTENTION, 0x29, 0x07);
+ scsi_done(scmd);
+ return 0;
+ }
+ }
+
+ if (handle == MPT3SAS_INVALID_DEVICE_HANDLE) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ }
+
+
+ if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) {
+ /* host recovery or link resets sent via IOCTLs */
+ return SCSI_MLQUEUE_HOST_BUSY;
+ } else if (sas_target_priv_data->deleted) {
+ /* device has been deleted */
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_done(scmd);
+ return 0;
+ } else if (sas_target_priv_data->tm_busy ||
+ sas_device_priv_data->block) {
+ /* device busy with task management */
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+
+ /*
+ * Bug work around for firmware SATL handling. The loop
+ * is based on atomic operations and ensures consistency
+ * since we're lockless at this point
+ */
+ do {
+ if (test_bit(0, &sas_device_priv_data->ata_command_pending))
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ } while (_scsih_set_satl_pending(scmd, true));
+
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ mpi_control = MPI2_SCSIIO_CONTROL_READ;
+ else if (scmd->sc_data_direction == DMA_TO_DEVICE)
+ mpi_control = MPI2_SCSIIO_CONTROL_WRITE;
+ else
+ mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
+
+ /* set tags */
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+ /* NCQ Prio supported, make sure control indicated high priority */
+ if (sas_device_priv_data->ncq_prio_enable) {
+ class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
+ if (class == IOPRIO_CLASS_RT)
+ mpi_control |= 1 << MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT;
+ }
+ /* Make sure Device is not raid volume.
+ * We do not expose raid functionality to upper layer for warpdrive.
+ */
+ if (((!ioc->is_warpdrive && !scsih_is_raid(&scmd->device->sdev_gendev))
+ && !scsih_is_nvme(&scmd->device->sdev_gendev))
+ && sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32)
+ mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
+
+ smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ _scsih_set_satl_pending(scmd, false);
+ goto out;
+ }
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, ioc->request_sz);
+ _scsih_setup_eedp(ioc, scmd, mpi_request);
+
+ if (scmd->cmd_len == 32)
+ mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT;
+ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT)
+ mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
+ else
+ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+ mpi_request->Control = cpu_to_le32(mpi_control);
+ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len);
+ mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
+ mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
+ mpi_request->SenseBufferLowAddress =
+ mpt3sas_base_get_sense_buffer_dma(ioc, smid);
+ mpi_request->SGLOffset0 = offsetof(Mpi25SCSIIORequest_t, SGL) / 4;
+ int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *)
+ mpi_request->LUN);
+ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
+
+ if (mpi_request->DataLength) {
+ pcie_device = sas_target_priv_data->pcie_dev;
+ if (ioc->build_sg_scmd(ioc, scmd, smid, pcie_device)) {
+ mpt3sas_base_free_smid(ioc, smid);
+ _scsih_set_satl_pending(scmd, false);
+ goto out;
+ }
+ } else
+ ioc->build_zero_len_sge(ioc, &mpi_request->SGL);
+
+ raid_device = sas_target_priv_data->raid_device;
+ if (raid_device && raid_device->direct_io_enabled)
+ mpt3sas_setup_direct_io(ioc, scmd,
+ raid_device, mpi_request);
+
+ if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) {
+ if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) {
+ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len |
+ MPI25_SCSIIO_IOFLAGS_FAST_PATH);
+ ioc->put_smid_fast_path(ioc, smid, handle);
+ } else
+ ioc->put_smid_scsi_io(ioc, smid,
+ le16_to_cpu(mpi_request->DevHandle));
+ } else
+ ioc->put_smid_default(ioc, smid);
+ return 0;
+
+ out:
+ return SCSI_MLQUEUE_HOST_BUSY;
+}
+
+/**
+ * _scsih_normalize_sense - normalize descriptor and fixed format sense data
+ * @sense_buffer: sense data returned by target
+ * @data: normalized skey/asc/ascq
+ */
+static void
+_scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
+{
+ if ((sense_buffer[0] & 0x7F) >= 0x72) {
+ /* descriptor format */
+ data->skey = sense_buffer[1] & 0x0F;
+ data->asc = sense_buffer[2];
+ data->ascq = sense_buffer[3];
+ } else {
+ /* fixed format */
+ data->skey = sense_buffer[2] & 0x0F;
+ data->asc = sense_buffer[12];
+ data->ascq = sense_buffer[13];
+ }
+}
+
+/**
+ * _scsih_scsi_ioc_info - translated non-successful SCSI_IO request
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @smid: ?
+ *
+ * scsi_status - SCSI Status code returned from target device
+ * scsi_state - state info associated with SCSI_IO determined by ioc
+ * ioc_status - ioc supplied status info
+ */
+static void
+_scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ Mpi2SCSIIOReply_t *mpi_reply, u16 smid)
+{
+ u32 response_info;
+ u8 *response_bytes;
+ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ u8 scsi_state = mpi_reply->SCSIState;
+ u8 scsi_status = mpi_reply->SCSIStatus;
+ char *desc_ioc_state = NULL;
+ char *desc_scsi_status = NULL;
+ char *desc_scsi_state = ioc->tmp_string;
+ u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+ struct _sas_device *sas_device = NULL;
+ struct _pcie_device *pcie_device = NULL;
+ struct scsi_target *starget = scmd->device->sdev_target;
+ struct MPT3SAS_TARGET *priv_target = starget->hostdata;
+ char *device_str = NULL;
+
+ if (!priv_target)
+ return;
+ if (ioc->hide_ir_msg)
+ device_str = "WarpDrive";
+ else
+ device_str = "volume";
+
+ if (log_info == 0x31170000)
+ return;
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_SUCCESS:
+ desc_ioc_state = "success";
+ break;
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ desc_ioc_state = "invalid function";
+ break;
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ desc_ioc_state = "scsi recovered error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
+ desc_ioc_state = "scsi invalid dev handle";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ desc_ioc_state = "scsi device not there";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ desc_ioc_state = "scsi data overrun";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ desc_ioc_state = "scsi data underrun";
+ break;
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ desc_ioc_state = "scsi io data error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ desc_ioc_state = "scsi protocol error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ desc_ioc_state = "scsi task terminated";
+ break;
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ desc_ioc_state = "scsi residual mismatch";
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ desc_ioc_state = "scsi task mgmt failed";
+ break;
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ desc_ioc_state = "scsi ioc terminated";
+ break;
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ desc_ioc_state = "scsi ext terminated";
+ break;
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ desc_ioc_state = "eedp guard error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ desc_ioc_state = "eedp ref tag error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ desc_ioc_state = "eedp app tag error";
+ break;
+ case MPI2_IOCSTATUS_INSUFFICIENT_POWER:
+ desc_ioc_state = "insufficient power";
+ break;
+ default:
+ desc_ioc_state = "unknown";
+ break;
+ }
+
+ switch (scsi_status) {
+ case MPI2_SCSI_STATUS_GOOD:
+ desc_scsi_status = "good";
+ break;
+ case MPI2_SCSI_STATUS_CHECK_CONDITION:
+ desc_scsi_status = "check condition";
+ break;
+ case MPI2_SCSI_STATUS_CONDITION_MET:
+ desc_scsi_status = "condition met";
+ break;
+ case MPI2_SCSI_STATUS_BUSY:
+ desc_scsi_status = "busy";
+ break;
+ case MPI2_SCSI_STATUS_INTERMEDIATE:
+ desc_scsi_status = "intermediate";
+ break;
+ case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET:
+ desc_scsi_status = "intermediate condmet";
+ break;
+ case MPI2_SCSI_STATUS_RESERVATION_CONFLICT:
+ desc_scsi_status = "reservation conflict";
+ break;
+ case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
+ desc_scsi_status = "command terminated";
+ break;
+ case MPI2_SCSI_STATUS_TASK_SET_FULL:
+ desc_scsi_status = "task set full";
+ break;
+ case MPI2_SCSI_STATUS_ACA_ACTIVE:
+ desc_scsi_status = "aca active";
+ break;
+ case MPI2_SCSI_STATUS_TASK_ABORTED:
+ desc_scsi_status = "task aborted";
+ break;
+ default:
+ desc_scsi_status = "unknown";
+ break;
+ }
+
+ desc_scsi_state[0] = '\0';
+ if (!scsi_state)
+ desc_scsi_state = " ";
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+ strcat(desc_scsi_state, "response info ");
+ if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ strcat(desc_scsi_state, "state terminated ");
+ if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS)
+ strcat(desc_scsi_state, "no status ");
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
+ strcat(desc_scsi_state, "autosense failed ");
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)
+ strcat(desc_scsi_state, "autosense valid ");
+
+ scsi_print_command(scmd);
+
+ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
+ ioc_warn(ioc, "\t%s wwid(0x%016llx)\n",
+ device_str, (u64)priv_target->sas_address);
+ } else if (priv_target->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) {
+ pcie_device = mpt3sas_get_pdev_from_target(ioc, priv_target);
+ if (pcie_device) {
+ ioc_info(ioc, "\twwid(0x%016llx), port(%d)\n",
+ (u64)pcie_device->wwid, pcie_device->port_num);
+ if (pcie_device->enclosure_handle != 0)
+ ioc_info(ioc, "\tenclosure logical id(0x%016llx), slot(%d)\n",
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot);
+ if (pcie_device->connector_name[0])
+ ioc_info(ioc, "\tenclosure level(0x%04x), connector name( %s)\n",
+ pcie_device->enclosure_level,
+ pcie_device->connector_name);
+ pcie_device_put(pcie_device);
+ }
+ } else {
+ sas_device = mpt3sas_get_sdev_from_target(ioc, priv_target);
+ if (sas_device) {
+ ioc_warn(ioc, "\tsas_address(0x%016llx), phy(%d)\n",
+ (u64)sas_device->sas_address, sas_device->phy);
+
+ _scsih_display_enclosure_chassis_info(ioc, sas_device,
+ NULL, NULL);
+
+ sas_device_put(sas_device);
+ }
+ }
+
+ ioc_warn(ioc, "\thandle(0x%04x), ioc_status(%s)(0x%04x), smid(%d)\n",
+ le16_to_cpu(mpi_reply->DevHandle),
+ desc_ioc_state, ioc_status, smid);
+ ioc_warn(ioc, "\trequest_len(%d), underflow(%d), resid(%d)\n",
+ scsi_bufflen(scmd), scmd->underflow, scsi_get_resid(scmd));
+ ioc_warn(ioc, "\ttag(%d), transfer_count(%d), sc->result(0x%08x)\n",
+ le16_to_cpu(mpi_reply->TaskTag),
+ le32_to_cpu(mpi_reply->TransferCount), scmd->result);
+ ioc_warn(ioc, "\tscsi_status(%s)(0x%02x), scsi_state(%s)(0x%02x)\n",
+ desc_scsi_status, scsi_status, desc_scsi_state, scsi_state);
+
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ struct sense_info data;
+ _scsih_normalize_sense(scmd->sense_buffer, &data);
+ ioc_warn(ioc, "\t[sense_key,asc,ascq]: [0x%02x,0x%02x,0x%02x], count(%d)\n",
+ data.skey, data.asc, data.ascq,
+ le32_to_cpu(mpi_reply->SenseCount));
+ }
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
+ response_info = le32_to_cpu(mpi_reply->ResponseInfo);
+ response_bytes = (u8 *)&response_info;
+ _scsih_response_code(ioc, response_bytes[0]);
+ }
+}
+
+/**
+ * _scsih_turn_on_pfa_led - illuminate PFA LED
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: process
+ */
+static void
+_scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2SepReply_t mpi_reply;
+ Mpi2SepRequest_t mpi_request;
+ struct _sas_device *sas_device;
+
+ sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (!sas_device)
+ return;
+
+ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
+ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
+ mpi_request.SlotStatus =
+ cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT);
+ mpi_request.DevHandle = cpu_to_le16(handle);
+ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS;
+ if ((mpt3sas_base_scsi_enclosure_processor(ioc, &mpi_reply,
+ &mpi_request)) != 0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ sas_device->pfa_led_on = 1;
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo)));
+ goto out;
+ }
+out:
+ sas_device_put(sas_device);
+}
+
+/**
+ * _scsih_turn_off_pfa_led - turn off Fault LED
+ * @ioc: per adapter object
+ * @sas_device: sas device whose PFA LED has to turned off
+ * Context: process
+ */
+static void
+_scsih_turn_off_pfa_led(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ Mpi2SepReply_t mpi_reply;
+ Mpi2SepRequest_t mpi_request;
+
+ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
+ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
+ mpi_request.SlotStatus = 0;
+ mpi_request.Slot = cpu_to_le16(sas_device->slot);
+ mpi_request.DevHandle = 0;
+ mpi_request.EnclosureHandle = cpu_to_le16(sas_device->enclosure_handle);
+ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS;
+ if ((mpt3sas_base_scsi_enclosure_processor(ioc, &mpi_reply,
+ &mpi_request)) != 0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo)));
+ return;
+ }
+}
+
+/**
+ * _scsih_send_event_to_turn_on_pfa_led - fire delayed event
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt.
+ */
+static void
+_scsih_send_event_to_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct fw_event_work *fw_event;
+
+ fw_event = alloc_fw_event_work(0);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT3SAS_TURN_ON_PFA_LED;
+ fw_event->device_handle = handle;
+ fw_event->ioc = ioc;
+ _scsih_fw_event_add(ioc, fw_event);
+ fw_event_work_put(fw_event);
+}
+
+/**
+ * _scsih_smart_predicted_fault - process smart errors
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt.
+ */
+static void
+_scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct scsi_target *starget;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ Mpi2EventNotificationReply_t *event_reply;
+ Mpi2EventDataSasDeviceStatusChange_t *event_data;
+ struct _sas_device *sas_device;
+ ssize_t sz;
+ unsigned long flags;
+
+ /* only handle non-raid devices */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (!sas_device)
+ goto out_unlock;
+
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) ||
+ ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)))
+ goto out_unlock;
+
+ _scsih_display_enclosure_chassis_info(NULL, sas_device, NULL, starget);
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
+ _scsih_send_event_to_turn_on_pfa_led(ioc, handle);
+
+ /* insert into event log */
+ sz = offsetof(Mpi2EventNotificationReply_t, EventData) +
+ sizeof(Mpi2EventDataSasDeviceStatusChange_t);
+ event_reply = kzalloc(sz, GFP_ATOMIC);
+ if (!event_reply) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+ event_reply->Event =
+ cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
+ event_reply->MsgLength = sz/4;
+ event_reply->EventDataLength =
+ cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4);
+ event_data = (Mpi2EventDataSasDeviceStatusChange_t *)
+ event_reply->EventData;
+ event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA;
+ event_data->ASC = 0x5D;
+ event_data->DevHandle = cpu_to_le16(handle);
+ event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address);
+ mpt3sas_ctl_add_to_event_log(ioc, event_reply);
+ kfree(event_reply);
+out:
+ if (sas_device)
+ sas_device_put(sas_device);
+ return;
+
+out_unlock:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ goto out;
+}
+
+/**
+ * _scsih_io_done - scsi request callback
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when using _scsih_qcmd.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ Mpi25SCSIIORequest_t *mpi_request;
+ Mpi2SCSIIOReply_t *mpi_reply;
+ struct scsi_cmnd *scmd;
+ struct scsiio_tracker *st;
+ u16 ioc_status;
+ u32 xfer_cnt;
+ u8 scsi_state;
+ u8 scsi_status;
+ u32 log_info;
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ u32 response_code = 0;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (scmd == NULL)
+ return 1;
+
+ _scsih_set_satl_pending(scmd, false);
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+
+ if (mpi_reply == NULL) {
+ scmd->result = DID_OK << 16;
+ goto out;
+ }
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
+ sas_device_priv_data->sas_target->deleted) {
+ scmd->result = DID_NO_CONNECT << 16;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+
+ /*
+ * WARPDRIVE: If direct_io is set then it is directIO,
+ * the failed direct I/O should be redirected to volume
+ */
+ st = scsi_cmd_priv(scmd);
+ if (st->direct_io &&
+ ((ioc_status & MPI2_IOCSTATUS_MASK)
+ != MPI2_IOCSTATUS_SCSI_TASK_TERMINATED)) {
+ st->direct_io = 0;
+ st->scmd = scmd;
+ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
+ mpi_request->DevHandle =
+ cpu_to_le16(sas_device_priv_data->sas_target->handle);
+ ioc->put_smid_scsi_io(ioc, smid,
+ sas_device_priv_data->sas_target->handle);
+ return 0;
+ }
+ /* turning off TLR */
+ scsi_state = mpi_reply->SCSIState;
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+ response_code =
+ le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
+ if (!sas_device_priv_data->tlr_snoop_check) {
+ sas_device_priv_data->tlr_snoop_check++;
+ if ((!ioc->is_warpdrive &&
+ !scsih_is_raid(&scmd->device->sdev_gendev) &&
+ !scsih_is_nvme(&scmd->device->sdev_gendev))
+ && sas_is_tlr_enabled(scmd->device) &&
+ response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) {
+ sas_disable_tlr(scmd->device);
+ sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n");
+ }
+ }
+
+ xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
+ scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
+ log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+ else
+ log_info = 0;
+ ioc_status &= MPI2_IOCSTATUS_MASK;
+ scsi_status = mpi_reply->SCSIStatus;
+
+ if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
+ (scsi_status == MPI2_SCSI_STATUS_BUSY ||
+ scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT ||
+ scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) {
+ ioc_status = MPI2_IOCSTATUS_SUCCESS;
+ }
+
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ struct sense_info data;
+ const void *sense_data = mpt3sas_base_get_sense_buffer(ioc,
+ smid);
+ u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
+ le32_to_cpu(mpi_reply->SenseCount));
+ memcpy(scmd->sense_buffer, sense_data, sz);
+ _scsih_normalize_sense(scmd->sense_buffer, &data);
+ /* failure prediction threshold exceeded */
+ if (data.asc == 0x5D)
+ _scsih_smart_predicted_fault(ioc,
+ le16_to_cpu(mpi_reply->DevHandle));
+ mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq);
+
+ if ((ioc->logging_level & MPT_DEBUG_REPLY) &&
+ ((scmd->sense_buffer[2] == UNIT_ATTENTION) ||
+ (scmd->sense_buffer[2] == MEDIUM_ERROR) ||
+ (scmd->sense_buffer[2] == HARDWARE_ERROR)))
+ _scsih_scsi_ioc_info(ioc, scmd, mpi_reply, smid);
+ }
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_BUSY:
+ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ scmd->result = SAM_STAT_BUSY;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ scmd->result = DID_NO_CONNECT << 16;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ if (sas_device_priv_data->block) {
+ scmd->result = DID_TRANSPORT_DISRUPTED << 16;
+ goto out;
+ }
+ if (log_info == IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR) {
+ /*
+ * This is a ATA NCQ command aborted due to another NCQ
+ * command failure. We must retry this command
+ * immediately but without incrementing its retry
+ * counter.
+ */
+ WARN_ON_ONCE(xfer_cnt != 0);
+ scmd->result = DID_IMM_RETRY << 16;
+ break;
+ }
+ if (log_info == 0x31110630) {
+ if (scmd->retries > 2) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_device_set_state(scmd->device,
+ SDEV_OFFLINE);
+ } else {
+ scmd->result = DID_SOFT_ERROR << 16;
+ scmd->device->expecting_cc_ua = 1;
+ }
+ break;
+ } else if (log_info == VIRTUAL_IO_FAILED_RETRY) {
+ scmd->result = DID_RESET << 16;
+ break;
+ } else if ((scmd->device->channel == RAID_CHANNEL) &&
+ (scsi_state == (MPI2_SCSI_STATE_TERMINATED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS))) {
+ scmd->result = DID_RESET << 16;
+ break;
+ }
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ scmd->result = DID_RESET << 16;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else
+ scmd->result = (DID_OK << 16) | scsi_status;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ scmd->result = (DID_OK << 16) | scsi_status;
+
+ if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID))
+ break;
+
+ if (xfer_cnt < scmd->underflow) {
+ if (scsi_status == SAM_STAT_BUSY)
+ scmd->result = SAM_STAT_BUSY;
+ else
+ scmd->result = DID_SOFT_ERROR << 16;
+ } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) {
+ mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID;
+ mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST,
+ 0x20, 0);
+ }
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ scsi_set_resid(scmd, 0);
+ fallthrough;
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI2_IOCSTATUS_SUCCESS:
+ scmd->result = (DID_OK << 16) | scsi_status;
+ if (response_code ==
+ MPI2_SCSITASKMGMT_RSP_INVALID_FRAME ||
+ (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS)))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ break;
+
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ _scsih_eedp_error_handling(scmd, ioc_status);
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ case MPI2_IOCSTATUS_INVALID_SGL:
+ case MPI2_IOCSTATUS_INTERNAL_ERROR:
+ case MPI2_IOCSTATUS_INVALID_FIELD:
+ case MPI2_IOCSTATUS_INVALID_STATE:
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ case MPI2_IOCSTATUS_INSUFFICIENT_POWER:
+ default:
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+
+ }
+
+ if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY))
+ _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid);
+
+ out:
+
+ scsi_dma_unmap(scmd);
+ mpt3sas_base_free_smid(ioc, smid);
+ scsi_done(scmd);
+ return 0;
+}
+
+/**
+ * _scsih_update_vphys_after_reset - update the Port's
+ * vphys_list after reset
+ * @ioc: per adapter object
+ *
+ * Returns nothing.
+ */
+static void
+_scsih_update_vphys_after_reset(struct MPT3SAS_ADAPTER *ioc)
+{
+ u16 sz, ioc_status;
+ int i;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ u16 attached_handle;
+ u64 attached_sas_addr;
+ u8 found = 0, port_id;
+ Mpi2SasPhyPage0_t phy_pg0;
+ struct hba_port *port, *port_next, *mport;
+ struct virtual_phy *vphy, *vphy_next;
+ struct _sas_device *sas_device;
+
+ /*
+ * Mark all the vphys objects as dirty.
+ */
+ list_for_each_entry_safe(port, port_next,
+ &ioc->port_table_list, list) {
+ if (!port->vphys_mask)
+ continue;
+ list_for_each_entry_safe(vphy, vphy_next,
+ &port->vphys_list, list) {
+ vphy->flags |= MPT_VPHY_FLAG_DIRTY_PHY;
+ }
+ }
+
+ /*
+ * Read SASIOUnitPage0 to get each HBA Phy's data.
+ */
+ sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz)) != 0)
+ goto out;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ /*
+ * Loop over each HBA Phy.
+ */
+ for (i = 0; i < ioc->sas_hba.num_phys; i++) {
+ /*
+ * Check whether Phy's Negotiation Link Rate is > 1.5G or not.
+ */
+ if ((sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4) <
+ MPI2_SAS_NEG_LINK_RATE_1_5)
+ continue;
+ /*
+ * Check whether Phy is connected to SEP device or not,
+ * if it is SEP device then read the Phy's SASPHYPage0 data to
+ * determine whether Phy is a virtual Phy or not. if it is
+ * virtual phy then it is conformed that the attached remote
+ * device is a HBA's vSES device.
+ */
+ if (!(le32_to_cpu(
+ sas_iounit_pg0->PhyData[i].ControllerPhyDeviceInfo) &
+ MPI2_SAS_DEVICE_INFO_SEP))
+ continue;
+
+ if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
+ i))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ continue;
+ }
+
+ if (!(le32_to_cpu(phy_pg0.PhyInfo) &
+ MPI2_SAS_PHYINFO_VIRTUAL_PHY))
+ continue;
+ /*
+ * Get the vSES device's SAS Address.
+ */
+ attached_handle = le16_to_cpu(
+ sas_iounit_pg0->PhyData[i].AttachedDevHandle);
+ if (_scsih_get_sas_address(ioc, attached_handle,
+ &attached_sas_addr) != 0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ continue;
+ }
+
+ found = 0;
+ port = port_next = NULL;
+ /*
+ * Loop over each virtual_phy object from
+ * each port's vphys_list.
+ */
+ list_for_each_entry_safe(port,
+ port_next, &ioc->port_table_list, list) {
+ if (!port->vphys_mask)
+ continue;
+ list_for_each_entry_safe(vphy, vphy_next,
+ &port->vphys_list, list) {
+ /*
+ * Continue with next virtual_phy object
+ * if the object is not marked as dirty.
+ */
+ if (!(vphy->flags & MPT_VPHY_FLAG_DIRTY_PHY))
+ continue;
+
+ /*
+ * Continue with next virtual_phy object
+ * if the object's SAS Address is not equals
+ * to current Phy's vSES device SAS Address.
+ */
+ if (vphy->sas_address != attached_sas_addr)
+ continue;
+ /*
+ * Enable current Phy number bit in object's
+ * phy_mask field.
+ */
+ if (!(vphy->phy_mask & (1 << i)))
+ vphy->phy_mask = (1 << i);
+ /*
+ * Get hba_port object from hba_port table
+ * corresponding to current phy's Port ID.
+ * if there is no hba_port object corresponding
+ * to Phy's Port ID then create a new hba_port
+ * object & add to hba_port table.
+ */
+ port_id = sas_iounit_pg0->PhyData[i].Port;
+ mport = mpt3sas_get_port_by_id(ioc, port_id, 1);
+ if (!mport) {
+ mport = kzalloc(
+ sizeof(struct hba_port), GFP_KERNEL);
+ if (!mport)
+ break;
+ mport->port_id = port_id;
+ ioc_info(ioc,
+ "%s: hba_port entry: %p, port: %d is added to hba_port list\n",
+ __func__, mport, mport->port_id);
+ list_add_tail(&mport->list,
+ &ioc->port_table_list);
+ }
+ /*
+ * If mport & port pointers are not pointing to
+ * same hba_port object then it means that vSES
+ * device's Port ID got changed after reset and
+ * hence move current virtual_phy object from
+ * port's vphys_list to mport's vphys_list.
+ */
+ if (port != mport) {
+ if (!mport->vphys_mask)
+ INIT_LIST_HEAD(
+ &mport->vphys_list);
+ mport->vphys_mask |= (1 << i);
+ port->vphys_mask &= ~(1 << i);
+ list_move(&vphy->list,
+ &mport->vphys_list);
+ sas_device = mpt3sas_get_sdev_by_addr(
+ ioc, attached_sas_addr, port);
+ if (sas_device)
+ sas_device->port = mport;
+ }
+ /*
+ * Earlier while updating the hba_port table,
+ * it is determined that there is no other
+ * direct attached device with mport's Port ID,
+ * Hence mport was marked as dirty. Only vSES
+ * device has this Port ID, so unmark the mport
+ * as dirt.
+ */
+ if (mport->flags & HBA_PORT_FLAG_DIRTY_PORT) {
+ mport->sas_address = 0;
+ mport->phy_mask = 0;
+ mport->flags &=
+ ~HBA_PORT_FLAG_DIRTY_PORT;
+ }
+ /*
+ * Unmark current virtual_phy object as dirty.
+ */
+ vphy->flags &= ~MPT_VPHY_FLAG_DIRTY_PHY;
+ found = 1;
+ break;
+ }
+ if (found)
+ break;
+ }
+ }
+out:
+ kfree(sas_iounit_pg0);
+}
+
+/**
+ * _scsih_get_port_table_after_reset - Construct temporary port table
+ * @ioc: per adapter object
+ * @port_table: address where port table needs to be constructed
+ *
+ * return number of HBA port entries available after reset.
+ */
+static int
+_scsih_get_port_table_after_reset(struct MPT3SAS_ADAPTER *ioc,
+ struct hba_port *port_table)
+{
+ u16 sz, ioc_status;
+ int i, j;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ u16 attached_handle;
+ u64 attached_sas_addr;
+ u8 found = 0, port_count = 0, port_id;
+
+ sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return port_count;
+ }
+
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz)) != 0)
+ goto out;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ for (i = 0; i < ioc->sas_hba.num_phys; i++) {
+ found = 0;
+ if ((sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4) <
+ MPI2_SAS_NEG_LINK_RATE_1_5)
+ continue;
+ attached_handle =
+ le16_to_cpu(sas_iounit_pg0->PhyData[i].AttachedDevHandle);
+ if (_scsih_get_sas_address(
+ ioc, attached_handle, &attached_sas_addr) != 0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ continue;
+ }
+
+ for (j = 0; j < port_count; j++) {
+ port_id = sas_iounit_pg0->PhyData[i].Port;
+ if (port_table[j].port_id == port_id &&
+ port_table[j].sas_address == attached_sas_addr) {
+ port_table[j].phy_mask |= (1 << i);
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ port_id = sas_iounit_pg0->PhyData[i].Port;
+ port_table[port_count].port_id = port_id;
+ port_table[port_count].phy_mask = (1 << i);
+ port_table[port_count].sas_address = attached_sas_addr;
+ port_count++;
+ }
+out:
+ kfree(sas_iounit_pg0);
+ return port_count;
+}
+
+enum hba_port_matched_codes {
+ NOT_MATCHED = 0,
+ MATCHED_WITH_ADDR_AND_PHYMASK,
+ MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT,
+ MATCHED_WITH_ADDR_AND_SUBPHYMASK,
+ MATCHED_WITH_ADDR,
+};
+
+/**
+ * _scsih_look_and_get_matched_port_entry - Get matched hba port entry
+ * from HBA port table
+ * @ioc: per adapter object
+ * @port_entry: hba port entry from temporary port table which needs to be
+ * searched for matched entry in the HBA port table
+ * @matched_port_entry: save matched hba port entry here
+ * @count: count of matched entries
+ *
+ * return type of matched entry found.
+ */
+static enum hba_port_matched_codes
+_scsih_look_and_get_matched_port_entry(struct MPT3SAS_ADAPTER *ioc,
+ struct hba_port *port_entry,
+ struct hba_port **matched_port_entry, int *count)
+{
+ struct hba_port *port_table_entry, *matched_port = NULL;
+ enum hba_port_matched_codes matched_code = NOT_MATCHED;
+ int lcount = 0;
+ *matched_port_entry = NULL;
+
+ list_for_each_entry(port_table_entry, &ioc->port_table_list, list) {
+ if (!(port_table_entry->flags & HBA_PORT_FLAG_DIRTY_PORT))
+ continue;
+
+ if ((port_table_entry->sas_address == port_entry->sas_address)
+ && (port_table_entry->phy_mask == port_entry->phy_mask)) {
+ matched_code = MATCHED_WITH_ADDR_AND_PHYMASK;
+ matched_port = port_table_entry;
+ break;
+ }
+
+ if ((port_table_entry->sas_address == port_entry->sas_address)
+ && (port_table_entry->phy_mask & port_entry->phy_mask)
+ && (port_table_entry->port_id == port_entry->port_id)) {
+ matched_code = MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT;
+ matched_port = port_table_entry;
+ continue;
+ }
+
+ if ((port_table_entry->sas_address == port_entry->sas_address)
+ && (port_table_entry->phy_mask & port_entry->phy_mask)) {
+ if (matched_code ==
+ MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT)
+ continue;
+ matched_code = MATCHED_WITH_ADDR_AND_SUBPHYMASK;
+ matched_port = port_table_entry;
+ continue;
+ }
+
+ if (port_table_entry->sas_address == port_entry->sas_address) {
+ if (matched_code ==
+ MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT)
+ continue;
+ if (matched_code == MATCHED_WITH_ADDR_AND_SUBPHYMASK)
+ continue;
+ matched_code = MATCHED_WITH_ADDR;
+ matched_port = port_table_entry;
+ lcount++;
+ }
+ }
+
+ *matched_port_entry = matched_port;
+ if (matched_code == MATCHED_WITH_ADDR)
+ *count = lcount;
+ return matched_code;
+}
+
+/**
+ * _scsih_del_phy_part_of_anther_port - remove phy if it
+ * is a part of anther port
+ *@ioc: per adapter object
+ *@port_table: port table after reset
+ *@index: hba port entry index
+ *@port_count: number of ports available after host reset
+ *@offset: HBA phy bit offset
+ *
+ */
+static void
+_scsih_del_phy_part_of_anther_port(struct MPT3SAS_ADAPTER *ioc,
+ struct hba_port *port_table,
+ int index, u8 port_count, int offset)
+{
+ struct _sas_node *sas_node = &ioc->sas_hba;
+ u32 i, found = 0;
+
+ for (i = 0; i < port_count; i++) {
+ if (i == index)
+ continue;
+
+ if (port_table[i].phy_mask & (1 << offset)) {
+ mpt3sas_transport_del_phy_from_an_existing_port(
+ ioc, sas_node, &sas_node->phy[offset]);
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ port_table[index].phy_mask |= (1 << offset);
+}
+
+/**
+ * _scsih_add_or_del_phys_from_existing_port - add/remove phy to/from
+ * right port
+ *@ioc: per adapter object
+ *@hba_port_entry: hba port table entry
+ *@port_table: temporary port table
+ *@index: hba port entry index
+ *@port_count: number of ports available after host reset
+ *
+ */
+static void
+_scsih_add_or_del_phys_from_existing_port(struct MPT3SAS_ADAPTER *ioc,
+ struct hba_port *hba_port_entry, struct hba_port *port_table,
+ int index, int port_count)
+{
+ u32 phy_mask, offset = 0;
+ struct _sas_node *sas_node = &ioc->sas_hba;
+
+ phy_mask = hba_port_entry->phy_mask ^ port_table[index].phy_mask;
+
+ for (offset = 0; offset < ioc->sas_hba.num_phys; offset++) {
+ if (phy_mask & (1 << offset)) {
+ if (!(port_table[index].phy_mask & (1 << offset))) {
+ _scsih_del_phy_part_of_anther_port(
+ ioc, port_table, index, port_count,
+ offset);
+ continue;
+ }
+ if (sas_node->phy[offset].phy_belongs_to_port)
+ mpt3sas_transport_del_phy_from_an_existing_port(
+ ioc, sas_node, &sas_node->phy[offset]);
+ mpt3sas_transport_add_phy_to_an_existing_port(
+ ioc, sas_node, &sas_node->phy[offset],
+ hba_port_entry->sas_address,
+ hba_port_entry);
+ }
+ }
+}
+
+/**
+ * _scsih_del_dirty_vphy - delete virtual_phy objects marked as dirty.
+ * @ioc: per adapter object
+ *
+ * Returns nothing.
+ */
+static void
+_scsih_del_dirty_vphy(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct hba_port *port, *port_next;
+ struct virtual_phy *vphy, *vphy_next;
+
+ list_for_each_entry_safe(port, port_next,
+ &ioc->port_table_list, list) {
+ if (!port->vphys_mask)
+ continue;
+ list_for_each_entry_safe(vphy, vphy_next,
+ &port->vphys_list, list) {
+ if (vphy->flags & MPT_VPHY_FLAG_DIRTY_PHY) {
+ drsprintk(ioc, ioc_info(ioc,
+ "Deleting vphy %p entry from port id: %d\t, Phy_mask 0x%08x\n",
+ vphy, port->port_id,
+ vphy->phy_mask));
+ port->vphys_mask &= ~vphy->phy_mask;
+ list_del(&vphy->list);
+ kfree(vphy);
+ }
+ }
+ if (!port->vphys_mask && !port->sas_address)
+ port->flags |= HBA_PORT_FLAG_DIRTY_PORT;
+ }
+}
+
+/**
+ * _scsih_del_dirty_port_entries - delete dirty port entries from port list
+ * after host reset
+ *@ioc: per adapter object
+ *
+ */
+static void
+_scsih_del_dirty_port_entries(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct hba_port *port, *port_next;
+
+ list_for_each_entry_safe(port, port_next,
+ &ioc->port_table_list, list) {
+ if (!(port->flags & HBA_PORT_FLAG_DIRTY_PORT) ||
+ port->flags & HBA_PORT_FLAG_NEW_PORT)
+ continue;
+
+ drsprintk(ioc, ioc_info(ioc,
+ "Deleting port table entry %p having Port: %d\t Phy_mask 0x%08x\n",
+ port, port->port_id, port->phy_mask));
+ list_del(&port->list);
+ kfree(port);
+ }
+}
+
+/**
+ * _scsih_sas_port_refresh - Update HBA port table after host reset
+ * @ioc: per adapter object
+ */
+static void
+_scsih_sas_port_refresh(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 port_count = 0;
+ struct hba_port *port_table;
+ struct hba_port *port_table_entry;
+ struct hba_port *port_entry = NULL;
+ int i, j, count = 0, lcount = 0;
+ int ret;
+ u64 sas_addr;
+ u8 num_phys;
+
+ drsprintk(ioc, ioc_info(ioc,
+ "updating ports for sas_host(0x%016llx)\n",
+ (unsigned long long)ioc->sas_hba.sas_address));
+
+ mpt3sas_config_get_number_hba_phys(ioc, &num_phys);
+ if (!num_phys) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ if (num_phys > ioc->sas_hba.nr_phys_allocated) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+ ioc->sas_hba.num_phys = num_phys;
+
+ port_table = kcalloc(ioc->sas_hba.num_phys,
+ sizeof(struct hba_port), GFP_KERNEL);
+ if (!port_table)
+ return;
+
+ port_count = _scsih_get_port_table_after_reset(ioc, port_table);
+ if (!port_count)
+ return;
+
+ drsprintk(ioc, ioc_info(ioc, "New Port table\n"));
+ for (j = 0; j < port_count; j++)
+ drsprintk(ioc, ioc_info(ioc,
+ "Port: %d\t Phy_mask 0x%08x\t sas_addr(0x%016llx)\n",
+ port_table[j].port_id,
+ port_table[j].phy_mask, port_table[j].sas_address));
+
+ list_for_each_entry(port_table_entry, &ioc->port_table_list, list)
+ port_table_entry->flags |= HBA_PORT_FLAG_DIRTY_PORT;
+
+ drsprintk(ioc, ioc_info(ioc, "Old Port table\n"));
+ port_table_entry = NULL;
+ list_for_each_entry(port_table_entry, &ioc->port_table_list, list) {
+ drsprintk(ioc, ioc_info(ioc,
+ "Port: %d\t Phy_mask 0x%08x\t sas_addr(0x%016llx)\n",
+ port_table_entry->port_id,
+ port_table_entry->phy_mask,
+ port_table_entry->sas_address));
+ }
+
+ for (j = 0; j < port_count; j++) {
+ ret = _scsih_look_and_get_matched_port_entry(ioc,
+ &port_table[j], &port_entry, &count);
+ if (!port_entry) {
+ drsprintk(ioc, ioc_info(ioc,
+ "No Matched entry for sas_addr(0x%16llx), Port:%d\n",
+ port_table[j].sas_address,
+ port_table[j].port_id));
+ continue;
+ }
+
+ switch (ret) {
+ case MATCHED_WITH_ADDR_SUBPHYMASK_AND_PORT:
+ case MATCHED_WITH_ADDR_AND_SUBPHYMASK:
+ _scsih_add_or_del_phys_from_existing_port(ioc,
+ port_entry, port_table, j, port_count);
+ break;
+ case MATCHED_WITH_ADDR:
+ sas_addr = port_table[j].sas_address;
+ for (i = 0; i < port_count; i++) {
+ if (port_table[i].sas_address == sas_addr)
+ lcount++;
+ }
+
+ if (count > 1 || lcount > 1)
+ port_entry = NULL;
+ else
+ _scsih_add_or_del_phys_from_existing_port(ioc,
+ port_entry, port_table, j, port_count);
+ }
+
+ if (!port_entry)
+ continue;
+
+ if (port_entry->port_id != port_table[j].port_id)
+ port_entry->port_id = port_table[j].port_id;
+ port_entry->flags &= ~HBA_PORT_FLAG_DIRTY_PORT;
+ port_entry->phy_mask = port_table[j].phy_mask;
+ }
+
+ port_table_entry = NULL;
+}
+
+/**
+ * _scsih_alloc_vphy - allocate virtual_phy object
+ * @ioc: per adapter object
+ * @port_id: Port ID number
+ * @phy_num: HBA Phy number
+ *
+ * Returns allocated virtual_phy object.
+ */
+static struct virtual_phy *
+_scsih_alloc_vphy(struct MPT3SAS_ADAPTER *ioc, u8 port_id, u8 phy_num)
+{
+ struct virtual_phy *vphy;
+ struct hba_port *port;
+
+ port = mpt3sas_get_port_by_id(ioc, port_id, 0);
+ if (!port)
+ return NULL;
+
+ vphy = mpt3sas_get_vphy_by_phy(ioc, port, phy_num);
+ if (!vphy) {
+ vphy = kzalloc(sizeof(struct virtual_phy), GFP_KERNEL);
+ if (!vphy)
+ return NULL;
+
+ if (!port->vphys_mask)
+ INIT_LIST_HEAD(&port->vphys_list);
+
+ /*
+ * Enable bit corresponding to HBA phy number on its
+ * parent hba_port object's vphys_mask field.
+ */
+ port->vphys_mask |= (1 << phy_num);
+ vphy->phy_mask |= (1 << phy_num);
+
+ list_add_tail(&vphy->list, &port->vphys_list);
+
+ ioc_info(ioc,
+ "vphy entry: %p, port id: %d, phy:%d is added to port's vphys_list\n",
+ vphy, port->port_id, phy_num);
+ }
+ return vphy;
+}
+
+/**
+ * _scsih_sas_host_refresh - refreshing sas host object contents
+ * @ioc: per adapter object
+ * Context: user
+ *
+ * During port enable, fw will send topology events for every device. Its
+ * possible that the handles may change from the previous setting, so this
+ * code keeping handles updating if changed.
+ */
+static void
+_scsih_sas_host_refresh(struct MPT3SAS_ADAPTER *ioc)
+{
+ u16 sz;
+ u16 ioc_status;
+ int i;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ u16 attached_handle;
+ u8 link_rate, port_id;
+ struct hba_port *port;
+ Mpi2SasPhyPage0_t phy_pg0;
+
+ dtmprintk(ioc,
+ ioc_info(ioc, "updating handles for sas_host(0x%016llx)\n",
+ (u64)ioc->sas_hba.sas_address));
+
+ sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz)) != 0)
+ goto out;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4;
+ if (i == 0)
+ ioc->sas_hba.handle = le16_to_cpu(
+ sas_iounit_pg0->PhyData[0].ControllerDevHandle);
+ port_id = sas_iounit_pg0->PhyData[i].Port;
+ if (!(mpt3sas_get_port_by_id(ioc, port_id, 0))) {
+ port = kzalloc(sizeof(struct hba_port), GFP_KERNEL);
+ if (!port)
+ goto out;
+
+ port->port_id = port_id;
+ ioc_info(ioc,
+ "hba_port entry: %p, port: %d is added to hba_port list\n",
+ port, port->port_id);
+ if (ioc->shost_recovery)
+ port->flags = HBA_PORT_FLAG_NEW_PORT;
+ list_add_tail(&port->list, &ioc->port_table_list);
+ }
+ /*
+ * Check whether current Phy belongs to HBA vSES device or not.
+ */
+ if (le32_to_cpu(sas_iounit_pg0->PhyData[i].ControllerPhyDeviceInfo) &
+ MPI2_SAS_DEVICE_INFO_SEP &&
+ (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) {
+ if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply,
+ &phy_pg0, i))) {
+ ioc_err(ioc,
+ "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if (!(le32_to_cpu(phy_pg0.PhyInfo) &
+ MPI2_SAS_PHYINFO_VIRTUAL_PHY))
+ continue;
+ /*
+ * Allocate a virtual_phy object for vSES device, if
+ * this vSES device is hot added.
+ */
+ if (!_scsih_alloc_vphy(ioc, port_id, i))
+ goto out;
+ ioc->sas_hba.phy[i].hba_vphy = 1;
+ }
+
+ /*
+ * Add new HBA phys to STL if these new phys got added as part
+ * of HBA Firmware upgrade/downgrade operation.
+ */
+ if (!ioc->sas_hba.phy[i].phy) {
+ if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply,
+ &phy_pg0, i))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ continue;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ continue;
+ }
+ ioc->sas_hba.phy[i].phy_id = i;
+ mpt3sas_transport_add_host_phy(ioc,
+ &ioc->sas_hba.phy[i], phy_pg0,
+ ioc->sas_hba.parent_dev);
+ continue;
+ }
+ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
+ attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i].
+ AttachedDevHandle);
+ if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5)
+ link_rate = MPI2_SAS_NEG_LINK_RATE_1_5;
+ ioc->sas_hba.phy[i].port =
+ mpt3sas_get_port_by_id(ioc, port_id, 0);
+ mpt3sas_transport_update_links(ioc, ioc->sas_hba.sas_address,
+ attached_handle, i, link_rate,
+ ioc->sas_hba.phy[i].port);
+ }
+ /*
+ * Clear the phy details if this phy got disabled as part of
+ * HBA Firmware upgrade/downgrade operation.
+ */
+ for (i = ioc->sas_hba.num_phys;
+ i < ioc->sas_hba.nr_phys_allocated; i++) {
+ if (ioc->sas_hba.phy[i].phy &&
+ ioc->sas_hba.phy[i].phy->negotiated_linkrate >=
+ SAS_LINK_RATE_1_5_GBPS)
+ mpt3sas_transport_update_links(ioc,
+ ioc->sas_hba.sas_address, 0, i,
+ MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED, NULL);
+ }
+ out:
+ kfree(sas_iounit_pg0);
+}
+
+/**
+ * _scsih_sas_host_add - create sas host object
+ * @ioc: per adapter object
+ *
+ * Creating host side data object, stored in ioc->sas_hba
+ */
+static void
+_scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasPhyPage0_t phy_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2SasEnclosurePage0_t enclosure_pg0;
+ u16 ioc_status;
+ u16 sz;
+ u8 device_missing_delay;
+ u8 num_phys, port_id;
+ struct hba_port *port;
+
+ mpt3sas_config_get_number_hba_phys(ioc, &num_phys);
+ if (!num_phys) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc->sas_hba.nr_phys_allocated = max_t(u8,
+ MPT_MAX_HBA_NUM_PHYS, num_phys);
+ ioc->sas_hba.phy = kcalloc(ioc->sas_hba.nr_phys_allocated,
+ sizeof(struct _sas_phy), GFP_KERNEL);
+ if (!ioc->sas_hba.phy) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc->sas_hba.num_phys = num_phys;
+
+ /* sas_iounit page 0 */
+ sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ /* sas_iounit page 1 */
+ sz = struct_size(sas_iounit_pg1, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ ioc->io_missing_delay =
+ sas_iounit_pg1->IODeviceMissingDelay;
+ device_missing_delay =
+ sas_iounit_pg1->ReportDeviceMissingDelay;
+ if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
+ ioc->device_missing_delay = (device_missing_delay &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
+ else
+ ioc->device_missing_delay = device_missing_delay &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
+
+ ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev;
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
+ i))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ if (i == 0)
+ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+ PhyData[0].ControllerDevHandle);
+
+ port_id = sas_iounit_pg0->PhyData[i].Port;
+ if (!(mpt3sas_get_port_by_id(ioc, port_id, 0))) {
+ port = kzalloc(sizeof(struct hba_port), GFP_KERNEL);
+ if (!port)
+ goto out;
+
+ port->port_id = port_id;
+ ioc_info(ioc,
+ "hba_port entry: %p, port: %d is added to hba_port list\n",
+ port, port->port_id);
+ list_add_tail(&port->list,
+ &ioc->port_table_list);
+ }
+
+ /*
+ * Check whether current Phy belongs to HBA vSES device or not.
+ */
+ if ((le32_to_cpu(phy_pg0.PhyInfo) &
+ MPI2_SAS_PHYINFO_VIRTUAL_PHY) &&
+ (phy_pg0.NegotiatedLinkRate >> 4) >=
+ MPI2_SAS_NEG_LINK_RATE_1_5) {
+ /*
+ * Allocate a virtual_phy object for vSES device.
+ */
+ if (!_scsih_alloc_vphy(ioc, port_id, i))
+ goto out;
+ ioc->sas_hba.phy[i].hba_vphy = 1;
+ }
+
+ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
+ ioc->sas_hba.phy[i].phy_id = i;
+ ioc->sas_hba.phy[i].port =
+ mpt3sas_get_port_by_id(ioc, port_id, 0);
+ mpt3sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
+ phy_pg0, ioc->sas_hba.parent_dev);
+ }
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc->sas_hba.enclosure_handle =
+ le16_to_cpu(sas_device_pg0.EnclosureHandle);
+ ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ ioc_info(ioc, "host_add: handle(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
+ ioc->sas_hba.handle,
+ (u64)ioc->sas_hba.sas_address,
+ ioc->sas_hba.num_phys);
+
+ if (ioc->sas_hba.enclosure_handle) {
+ if (!(mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply,
+ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ ioc->sas_hba.enclosure_handle)))
+ ioc->sas_hba.enclosure_logical_id =
+ le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
+ }
+
+ out:
+ kfree(sas_iounit_pg1);
+ kfree(sas_iounit_pg0);
+}
+
+/**
+ * _scsih_expander_add - creating expander object
+ * @ioc: per adapter object
+ * @handle: expander handle
+ *
+ * Creating expander object, stored in ioc->sas_expander_list.
+ *
+ * Return: 0 for success, else error.
+ */
+static int
+_scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander;
+ struct _enclosure_node *enclosure_dev;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2ExpanderPage1_t expander_pg1;
+ u32 ioc_status;
+ u16 parent_handle;
+ u64 sas_address, sas_address_parent = 0;
+ int i;
+ unsigned long flags;
+ struct _sas_port *mpt3sas_port = NULL;
+ u8 port_id;
+
+ int rc = 0;
+
+ if (!handle)
+ return -1;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery)
+ return -1;
+
+ if ((mpt3sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ /* handle out of order topology events */
+ parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
+ if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent)
+ != 0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ port_id = expander_pg0.PhysicalPort;
+ if (sas_address_parent != ioc->sas_hba.sas_address) {
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address_parent,
+ mpt3sas_get_port_by_id(ioc, port_id, 0));
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (!sas_expander) {
+ rc = _scsih_expander_add(ioc, parent_handle);
+ if (rc != 0)
+ return rc;
+ }
+ }
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_address = le64_to_cpu(expander_pg0.SASAddress);
+ sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address, mpt3sas_get_port_by_id(ioc, port_id, 0));
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (sas_expander)
+ return 0;
+
+ sas_expander = kzalloc(sizeof(struct _sas_node),
+ GFP_KERNEL);
+ if (!sas_expander) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ sas_expander->handle = handle;
+ sas_expander->num_phys = expander_pg0.NumPhys;
+ sas_expander->sas_address_parent = sas_address_parent;
+ sas_expander->sas_address = sas_address;
+ sas_expander->port = mpt3sas_get_port_by_id(ioc, port_id, 0);
+ if (!sas_expander->port) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+
+ ioc_info(ioc, "expander_add: handle(0x%04x), parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
+ handle, parent_handle,
+ (u64)sas_expander->sas_address, sas_expander->num_phys);
+
+ if (!sas_expander->num_phys) {
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->phy = kcalloc(sas_expander->num_phys,
+ sizeof(struct _sas_phy), GFP_KERNEL);
+ if (!sas_expander->phy) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+
+ INIT_LIST_HEAD(&sas_expander->sas_port_list);
+ mpt3sas_port = mpt3sas_transport_port_add(ioc, handle,
+ sas_address_parent, sas_expander->port);
+ if (!mpt3sas_port) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->parent_dev = &mpt3sas_port->rphy->dev;
+ sas_expander->rphy = mpt3sas_port->rphy;
+
+ for (i = 0 ; i < sas_expander->num_phys ; i++) {
+ if ((mpt3sas_config_get_expander_pg1(ioc, &mpi_reply,
+ &expander_pg1, i, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->phy[i].handle = handle;
+ sas_expander->phy[i].phy_id = i;
+ sas_expander->phy[i].port =
+ mpt3sas_get_port_by_id(ioc, port_id, 0);
+
+ if ((mpt3sas_transport_add_expander_phy(ioc,
+ &sas_expander->phy[i], expander_pg1,
+ sas_expander->parent_dev))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ }
+
+ if (sas_expander->enclosure_handle) {
+ enclosure_dev =
+ mpt3sas_scsih_enclosure_find_by_handle(ioc,
+ sas_expander->enclosure_handle);
+ if (enclosure_dev)
+ sas_expander->enclosure_logical_id =
+ le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID);
+ }
+
+ _scsih_expander_node_add(ioc, sas_expander);
+ return 0;
+
+ out_fail:
+
+ if (mpt3sas_port)
+ mpt3sas_transport_port_remove(ioc, sas_expander->sas_address,
+ sas_address_parent, sas_expander->port);
+ kfree(sas_expander);
+ return rc;
+}
+
+/**
+ * mpt3sas_expander_remove - removing expander object
+ * @ioc: per adapter object
+ * @sas_address: expander sas_address
+ * @port: hba port entry
+ */
+void
+mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ struct hba_port *port)
+{
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+
+ if (ioc->shost_recovery)
+ return;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address, port);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (sas_expander)
+ _scsih_expander_node_remove(ioc, sas_expander);
+}
+
+/**
+ * _scsih_done - internal SCSI_IO callback handler.
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when sending internal generated SCSI_IO.
+ * The callback index passed is `ioc->scsih_cb_idx`
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (ioc->scsih_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->scsih_cmds.smid != smid)
+ return 1;
+ ioc->scsih_cmds.status |= MPT3_CMD_COMPLETE;
+ if (mpi_reply) {
+ memcpy(ioc->scsih_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ ioc->scsih_cmds.status |= MPT3_CMD_REPLY_VALID;
+ }
+ ioc->scsih_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->scsih_cmds.done);
+ return 1;
+}
+
+
+
+
+#define MPT3_MAX_LUNS (255)
+
+
+/**
+ * _scsih_check_access_status - check access flags
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @handle: sas device handle
+ * @access_status: errors returned during discovery of the device
+ *
+ * Return: 0 for success, else failure
+ */
+static u8
+_scsih_check_access_status(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u16 handle, u8 access_status)
+{
+ u8 rc = 1;
+ char *desc = NULL;
+
+ switch (access_status) {
+ case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS:
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION:
+ rc = 0;
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED:
+ desc = "sata capability failed";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT:
+ desc = "sata affiliation conflict";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE:
+ desc = "route not addressable";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE:
+ desc = "smp error not addressable";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED:
+ desc = "device blocked";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX:
+ desc = "sata initialization failed";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+
+ if (!rc)
+ return 0;
+
+ ioc_err(ioc, "discovery errors(%s): sas_address(0x%016llx), handle(0x%04x)\n",
+ desc, (u64)sas_address, handle);
+ return rc;
+}
+
+/**
+ * _scsih_check_device - checking device responsiveness
+ * @ioc: per adapter object
+ * @parent_sas_address: sas address of parent expander or sas host
+ * @handle: attached device handle
+ * @phy_number: phy number
+ * @link_rate: new link rate
+ */
+static void
+_scsih_check_device(struct MPT3SAS_ADAPTER *ioc,
+ u64 parent_sas_address, u16 handle, u8 phy_number, u8 link_rate)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ struct _sas_device *sas_device = NULL;
+ struct _enclosure_node *enclosure_dev = NULL;
+ u32 ioc_status;
+ unsigned long flags;
+ u64 sas_address;
+ struct scsi_target *starget;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ u32 device_info;
+ struct hba_port *port;
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle)))
+ return;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ return;
+
+ /* wide port handling ~ we need only handle device once for the phy that
+ * is matched in sas device page zero
+ */
+ if (phy_number != sas_device_pg0.PhyNum)
+ return;
+
+ /* check if this is end device */
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info)))
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ port = mpt3sas_get_port_by_id(ioc, sas_device_pg0.PhysicalPort, 0);
+ if (!port)
+ goto out_unlock;
+ sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+ sas_address, port);
+
+ if (!sas_device)
+ goto out_unlock;
+
+ if (unlikely(sas_device->handle != handle)) {
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ starget_printk(KERN_INFO, starget,
+ "handle changed from(0x%04x) to (0x%04x)!!!\n",
+ sas_device->handle, handle);
+ sas_target_priv_data->handle = handle;
+ sas_device->handle = handle;
+ if (le16_to_cpu(sas_device_pg0.Flags) &
+ MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+ sas_device->enclosure_level =
+ sas_device_pg0.EnclosureLevel;
+ memcpy(sas_device->connector_name,
+ sas_device_pg0.ConnectorName, 4);
+ sas_device->connector_name[4] = '\0';
+ } else {
+ sas_device->enclosure_level = 0;
+ sas_device->connector_name[0] = '\0';
+ }
+
+ sas_device->enclosure_handle =
+ le16_to_cpu(sas_device_pg0.EnclosureHandle);
+ sas_device->is_chassis_slot_valid = 0;
+ enclosure_dev = mpt3sas_scsih_enclosure_find_by_handle(ioc,
+ sas_device->enclosure_handle);
+ if (enclosure_dev) {
+ sas_device->enclosure_logical_id =
+ le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID);
+ if (le16_to_cpu(enclosure_dev->pg0.Flags) &
+ MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) {
+ sas_device->is_chassis_slot_valid = 1;
+ sas_device->chassis_slot =
+ enclosure_dev->pg0.ChassisSlot;
+ }
+ }
+ }
+
+ /* check if device is present */
+ if (!(le16_to_cpu(sas_device_pg0.Flags) &
+ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
+ ioc_err(ioc, "device is not present handle(0x%04x), flags!!!\n",
+ handle);
+ goto out_unlock;
+ }
+
+ /* check if there were any issues with discovery */
+ if (_scsih_check_access_status(ioc, sas_address, handle,
+ sas_device_pg0.AccessStatus))
+ goto out_unlock;
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ _scsih_ublock_io_device(ioc, sas_address, port);
+
+ if (sas_device)
+ sas_device_put(sas_device);
+ return;
+
+out_unlock:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device)
+ sas_device_put(sas_device);
+}
+
+/**
+ * _scsih_add_device - creating sas device object
+ * @ioc: per adapter object
+ * @handle: sas device handle
+ * @phy_num: phy number end device attached to
+ * @is_pd: is this hidden raid component
+ *
+ * Creating end device object, stored in ioc->sas_device_list.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
+ u8 is_pd)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ struct _sas_device *sas_device;
+ struct _enclosure_node *enclosure_dev = NULL;
+ u32 ioc_status;
+ u64 sas_address;
+ u32 device_info;
+ u8 port_id;
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ /* check if this is end device */
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info)))
+ return -1;
+ set_bit(handle, ioc->pend_os_device_add);
+ sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+
+ /* check if device is present */
+ if (!(le16_to_cpu(sas_device_pg0.Flags) &
+ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
+ ioc_err(ioc, "device is not present handle(0x04%x)!!!\n",
+ handle);
+ return -1;
+ }
+
+ /* check if there were any issues with discovery */
+ if (_scsih_check_access_status(ioc, sas_address, handle,
+ sas_device_pg0.AccessStatus))
+ return -1;
+
+ port_id = sas_device_pg0.PhysicalPort;
+ sas_device = mpt3sas_get_sdev_by_addr(ioc,
+ sas_address, mpt3sas_get_port_by_id(ioc, port_id, 0));
+ if (sas_device) {
+ clear_bit(handle, ioc->pend_os_device_add);
+ sas_device_put(sas_device);
+ return -1;
+ }
+
+ if (sas_device_pg0.EnclosureHandle) {
+ enclosure_dev =
+ mpt3sas_scsih_enclosure_find_by_handle(ioc,
+ le16_to_cpu(sas_device_pg0.EnclosureHandle));
+ if (enclosure_dev == NULL)
+ ioc_info(ioc, "Enclosure handle(0x%04x) doesn't match with enclosure device!\n",
+ sas_device_pg0.EnclosureHandle);
+ }
+
+ sas_device = kzalloc(sizeof(struct _sas_device),
+ GFP_KERNEL);
+ if (!sas_device) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 0;
+ }
+
+ kref_init(&sas_device->refcount);
+ sas_device->handle = handle;
+ if (_scsih_get_sas_address(ioc,
+ le16_to_cpu(sas_device_pg0.ParentDevHandle),
+ &sas_device->sas_address_parent) != 0)
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_device->enclosure_handle =
+ le16_to_cpu(sas_device_pg0.EnclosureHandle);
+ if (sas_device->enclosure_handle != 0)
+ sas_device->slot =
+ le16_to_cpu(sas_device_pg0.Slot);
+ sas_device->device_info = device_info;
+ sas_device->sas_address = sas_address;
+ sas_device->phy = sas_device_pg0.PhyNum;
+ sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) &
+ MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0;
+ sas_device->port = mpt3sas_get_port_by_id(ioc, port_id, 0);
+ if (!sas_device->port) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ if (le16_to_cpu(sas_device_pg0.Flags)
+ & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+ sas_device->enclosure_level =
+ sas_device_pg0.EnclosureLevel;
+ memcpy(sas_device->connector_name,
+ sas_device_pg0.ConnectorName, 4);
+ sas_device->connector_name[4] = '\0';
+ } else {
+ sas_device->enclosure_level = 0;
+ sas_device->connector_name[0] = '\0';
+ }
+ /* get enclosure_logical_id & chassis_slot*/
+ sas_device->is_chassis_slot_valid = 0;
+ if (enclosure_dev) {
+ sas_device->enclosure_logical_id =
+ le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID);
+ if (le16_to_cpu(enclosure_dev->pg0.Flags) &
+ MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) {
+ sas_device->is_chassis_slot_valid = 1;
+ sas_device->chassis_slot =
+ enclosure_dev->pg0.ChassisSlot;
+ }
+ }
+
+ /* get device name */
+ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName);
+ sas_device->port_type = sas_device_pg0.MaxPortConnections;
+ ioc_info(ioc,
+ "handle(0x%0x) sas_address(0x%016llx) port_type(0x%0x)\n",
+ handle, sas_device->sas_address, sas_device->port_type);
+
+ if (ioc->wait_for_discovery_to_complete)
+ _scsih_sas_device_init_add(ioc, sas_device);
+ else
+ _scsih_sas_device_add(ioc, sas_device);
+
+out:
+ sas_device_put(sas_device);
+ return 0;
+}
+
+/**
+ * _scsih_remove_device - removing sas device object
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ */
+static void
+_scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+
+ if ((ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) &&
+ (sas_device->pfa_led_on)) {
+ _scsih_turn_off_pfa_led(ioc, sas_device);
+ sas_device->pfa_led_on = 0;
+ }
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enter: handle(0x%04x), sas_addr(0x%016llx)\n",
+ __func__,
+ sas_device->handle, (u64)sas_device->sas_address));
+
+ dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device,
+ NULL, NULL));
+
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ sas_target_priv_data = sas_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ _scsih_ublock_io_device(ioc, sas_device->sas_address,
+ sas_device->port);
+ sas_target_priv_data->handle =
+ MPT3SAS_INVALID_DEVICE_HANDLE;
+ }
+
+ if (!ioc->hide_drives)
+ mpt3sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent,
+ sas_device->port);
+
+ ioc_info(ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n",
+ sas_device->handle, (u64)sas_device->sas_address);
+
+ _scsih_display_enclosure_chassis_info(ioc, sas_device, NULL, NULL);
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n",
+ __func__,
+ sas_device->handle, (u64)sas_device->sas_address));
+ dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device,
+ NULL, NULL));
+}
+
+/**
+ * _scsih_sas_topology_change_event_debug - debug for topology event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_sas_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 phy_number;
+ char *status_str = NULL;
+ u8 link_rate, prev_link_rate;
+
+ switch (event_data->ExpStatus) {
+ case MPI2_EVENT_SAS_TOPO_ES_ADDED:
+ status_str = "add";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_RESPONDING:
+ case 0:
+ status_str = "responding";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ ioc_info(ioc, "sas topology change: (%s)\n", status_str);
+ pr_info("\thandle(0x%04x), enclosure_handle(0x%04x) " \
+ "start_phy(%02d), count(%d)\n",
+ le16_to_cpu(event_data->ExpanderDevHandle),
+ le16_to_cpu(event_data->EnclosureHandle),
+ event_data->StartPhyNum, event_data->NumEntries);
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ phy_number = event_data->StartPhyNum + i;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ switch (reason_code) {
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+ status_str = "target add";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+ status_str = "target remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
+ status_str = "delay target remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+ status_str = "link rate change";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
+ status_str = "target responding";
+ break;
+ default:
+ status_str = "unknown";
+ break;
+ }
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+ pr_info("\tphy(%02d), attached_handle(0x%04x): %s:" \
+ " link rate: new(0x%02x), old(0x%02x)\n", phy_number,
+ handle, status_str, link_rate, prev_link_rate);
+
+ }
+}
+
+/**
+ * _scsih_sas_topology_change_event - handle topology changes
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ */
+static int
+_scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ int i;
+ u16 parent_handle, handle;
+ u16 reason_code;
+ u8 phy_number, max_phys;
+ struct _sas_node *sas_expander;
+ u64 sas_address;
+ unsigned long flags;
+ u8 link_rate, prev_link_rate;
+ struct hba_port *port;
+ Mpi2EventDataSasTopologyChangeList_t *event_data =
+ (Mpi2EventDataSasTopologyChangeList_t *)
+ fw_event->event_data;
+
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_topology_change_event_debug(ioc, event_data);
+
+ if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery)
+ return 0;
+
+ if (!ioc->sas_hba.num_phys)
+ _scsih_sas_host_add(ioc);
+ else
+ _scsih_sas_host_refresh(ioc);
+
+ if (fw_event->ignore) {
+ dewtprintk(ioc, ioc_info(ioc, "ignoring expander event\n"));
+ return 0;
+ }
+
+ parent_handle = le16_to_cpu(event_data->ExpanderDevHandle);
+ port = mpt3sas_get_port_by_id(ioc, event_data->PhysicalPort, 0);
+
+ /* handle expander add */
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED)
+ if (_scsih_expander_add(ioc, parent_handle) != 0)
+ return 0;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_handle(ioc,
+ parent_handle);
+ if (sas_expander) {
+ sas_address = sas_expander->sas_address;
+ max_phys = sas_expander->num_phys;
+ port = sas_expander->port;
+ } else if (parent_handle < ioc->sas_hba.num_phys) {
+ sas_address = ioc->sas_hba.sas_address;
+ max_phys = ioc->sas_hba.num_phys;
+ } else {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* handle siblings events */
+ for (i = 0; i < event_data->NumEntries; i++) {
+ if (fw_event->ignore) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "ignoring expander event\n"));
+ return 0;
+ }
+ if (ioc->remove_host || ioc->pci_error_recovery)
+ return 0;
+ phy_number = event_data->StartPhyNum + i;
+ if (phy_number >= max_phys)
+ continue;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ if ((event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code !=
+ MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING))
+ continue;
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+ switch (reason_code) {
+ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+
+ if (ioc->shost_recovery)
+ break;
+
+ if (link_rate == prev_link_rate)
+ break;
+
+ mpt3sas_transport_update_links(ioc, sas_address,
+ handle, phy_number, link_rate, port);
+
+ if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5)
+ break;
+
+ _scsih_check_device(ioc, sas_address, handle,
+ phy_number, link_rate);
+
+ if (!test_bit(handle, ioc->pend_os_device_add))
+ break;
+
+ fallthrough;
+
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+
+ if (ioc->shost_recovery)
+ break;
+
+ mpt3sas_transport_update_links(ioc, sas_address,
+ handle, phy_number, link_rate, port);
+
+ _scsih_add_device(ioc, handle, phy_number, 0);
+
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+
+ _scsih_device_remove_by_handle(ioc, handle);
+ break;
+ }
+ }
+
+ /* handle expander removal */
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING &&
+ sas_expander)
+ mpt3sas_expander_remove(ioc, sas_address, port);
+
+ return 0;
+}
+
+/**
+ * _scsih_sas_device_status_change_event_debug - debug for device event
+ * @ioc: ?
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_sas_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasDeviceStatusChange_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
+ reason_str = "smart data";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED:
+ reason_str = "unsupported device discovered";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
+ reason_str = "internal device reset";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
+ reason_str = "internal task abort set";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
+ reason_str = "internal clear task set";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL:
+ reason_str = "internal query task";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE:
+ reason_str = "sata init failure";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
+ reason_str = "internal device reset complete";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort complete";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION:
+ reason_str = "internal async notification";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY:
+ reason_str = "expander reduced functionality";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY:
+ reason_str = "expander reduced functionality complete";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+ ioc_info(ioc, "device status change: (%s)\thandle(0x%04x), sas address(0x%016llx), tag(%d)",
+ reason_str, le16_to_cpu(event_data->DevHandle),
+ (u64)le64_to_cpu(event_data->SASAddress),
+ le16_to_cpu(event_data->TaskTag));
+ if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA)
+ pr_cont(", ASC(0x%x), ASCQ(0x%x)\n",
+ event_data->ASC, event_data->ASCQ);
+ pr_cont("\n");
+}
+
+/**
+ * _scsih_sas_device_status_change_event - handle device status change
+ * @ioc: per adapter object
+ * @event_data: The fw event
+ * Context: user.
+ */
+static void
+_scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasDeviceStatusChange_t *event_data)
+{
+ struct MPT3SAS_TARGET *target_priv_data;
+ struct _sas_device *sas_device;
+ u64 sas_address;
+ unsigned long flags;
+
+ /* In MPI Revision K (0xC), the internal device reset complete was
+ * implemented, so avoid setting tm_busy flag for older firmware.
+ */
+ if ((ioc->facts.HeaderVersion >> 8) < 0xC)
+ return;
+
+ if (event_data->ReasonCode !=
+ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
+ event_data->ReasonCode !=
+ MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_address = le64_to_cpu(event_data->SASAddress);
+ sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+ sas_address,
+ mpt3sas_get_port_by_id(ioc, event_data->PhysicalPort, 0));
+
+ if (!sas_device || !sas_device->starget)
+ goto out;
+
+ target_priv_data = sas_device->starget->hostdata;
+ if (!target_priv_data)
+ goto out;
+
+ if (event_data->ReasonCode ==
+ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET)
+ target_priv_data->tm_busy = 1;
+ else
+ target_priv_data->tm_busy = 0;
+
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ ioc_info(ioc,
+ "%s tm_busy flag for handle(0x%04x)\n",
+ (target_priv_data->tm_busy == 1) ? "Enable" : "Disable",
+ target_priv_data->handle);
+
+out:
+ if (sas_device)
+ sas_device_put(sas_device);
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+
+/**
+ * _scsih_check_pcie_access_status - check access flags
+ * @ioc: per adapter object
+ * @wwid: wwid
+ * @handle: sas device handle
+ * @access_status: errors returned during discovery of the device
+ *
+ * Return: 0 for success, else failure
+ */
+static u8
+_scsih_check_pcie_access_status(struct MPT3SAS_ADAPTER *ioc, u64 wwid,
+ u16 handle, u8 access_status)
+{
+ u8 rc = 1;
+ char *desc = NULL;
+
+ switch (access_status) {
+ case MPI26_PCIEDEV0_ASTATUS_NO_ERRORS:
+ case MPI26_PCIEDEV0_ASTATUS_NEEDS_INITIALIZATION:
+ rc = 0;
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_CAPABILITY_FAILED:
+ desc = "PCIe device capability failed";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED:
+ desc = "PCIe device blocked";
+ ioc_info(ioc,
+ "Device with Access Status (%s): wwid(0x%016llx), "
+ "handle(0x%04x)\n ll only be added to the internal list",
+ desc, (u64)wwid, handle);
+ rc = 0;
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_MEMORY_SPACE_ACCESS_FAILED:
+ desc = "PCIe device mem space access failed";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_UNSUPPORTED_DEVICE:
+ desc = "PCIe device unsupported";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_MSIX_REQUIRED:
+ desc = "PCIe device MSIx Required";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_INIT_FAIL_MAX:
+ desc = "PCIe device init fail max";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_UNKNOWN:
+ desc = "PCIe device status unknown";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_READY_TIMEOUT:
+ desc = "nvme ready timeout";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_DEVCFG_UNSUPPORTED:
+ desc = "nvme device configuration unsupported";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_IDENTIFY_FAILED:
+ desc = "nvme identify failed";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_QCONFIG_FAILED:
+ desc = "nvme qconfig failed";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_QCREATION_FAILED:
+ desc = "nvme qcreation failed";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_EVENTCFG_FAILED:
+ desc = "nvme eventcfg failed";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_GET_FEATURE_STAT_FAILED:
+ desc = "nvme get feature stat failed";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_IDLE_TIMEOUT:
+ desc = "nvme idle timeout";
+ break;
+ case MPI26_PCIEDEV0_ASTATUS_NVME_FAILURE_STATUS:
+ desc = "nvme failure status";
+ break;
+ default:
+ ioc_err(ioc, "NVMe discovery error(0x%02x): wwid(0x%016llx), handle(0x%04x)\n",
+ access_status, (u64)wwid, handle);
+ return rc;
+ }
+
+ if (!rc)
+ return rc;
+
+ ioc_info(ioc, "NVMe discovery error(%s): wwid(0x%016llx), handle(0x%04x)\n",
+ desc, (u64)wwid, handle);
+ return rc;
+}
+
+/**
+ * _scsih_pcie_device_remove_from_sml - removing pcie device
+ * from SML and free up associated memory
+ * @ioc: per adapter object
+ * @pcie_device: the pcie_device object
+ */
+static void
+_scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc,
+ struct _pcie_device *pcie_device)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enter: handle(0x%04x), wwid(0x%016llx)\n",
+ __func__,
+ pcie_device->handle, (u64)pcie_device->wwid));
+ if (pcie_device->enclosure_handle != 0)
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n",
+ __func__,
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot));
+ if (pcie_device->connector_name[0] != '\0')
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: enter: enclosure level(0x%04x), connector name(%s)\n",
+ __func__,
+ pcie_device->enclosure_level,
+ pcie_device->connector_name));
+
+ if (pcie_device->starget && pcie_device->starget->hostdata) {
+ sas_target_priv_data = pcie_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ _scsih_ublock_io_device(ioc, pcie_device->wwid, NULL);
+ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;
+ }
+
+ ioc_info(ioc, "removing handle(0x%04x), wwid(0x%016llx)\n",
+ pcie_device->handle, (u64)pcie_device->wwid);
+ if (pcie_device->enclosure_handle != 0)
+ ioc_info(ioc, "removing : enclosure logical id(0x%016llx), slot(%d)\n",
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot);
+ if (pcie_device->connector_name[0] != '\0')
+ ioc_info(ioc, "removing: enclosure level(0x%04x), connector name( %s)\n",
+ pcie_device->enclosure_level,
+ pcie_device->connector_name);
+
+ if (pcie_device->starget && (pcie_device->access_status !=
+ MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED))
+ scsi_remove_target(&pcie_device->starget->dev);
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: exit: handle(0x%04x), wwid(0x%016llx)\n",
+ __func__,
+ pcie_device->handle, (u64)pcie_device->wwid));
+ if (pcie_device->enclosure_handle != 0)
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n",
+ __func__,
+ (u64)pcie_device->enclosure_logical_id,
+ pcie_device->slot));
+ if (pcie_device->connector_name[0] != '\0')
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: exit: enclosure level(0x%04x), connector name( %s)\n",
+ __func__,
+ pcie_device->enclosure_level,
+ pcie_device->connector_name));
+
+ kfree(pcie_device->serial_number);
+}
+
+
+/**
+ * _scsih_pcie_check_device - checking device responsiveness
+ * @ioc: per adapter object
+ * @handle: attached device handle
+ */
+static void
+_scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi26PCIeDevicePage0_t pcie_device_pg0;
+ u32 ioc_status;
+ struct _pcie_device *pcie_device;
+ u64 wwid;
+ unsigned long flags;
+ struct scsi_target *starget;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ u32 device_info;
+
+ if ((mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply,
+ &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, handle)))
+ return;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ return;
+
+ /* check if this is end device */
+ device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo);
+ if (!(_scsih_is_nvme_pciescsi_device(device_info)))
+ return;
+
+ wwid = le64_to_cpu(pcie_device_pg0.WWID);
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid);
+
+ if (!pcie_device) {
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ return;
+ }
+
+ if (unlikely(pcie_device->handle != handle)) {
+ starget = pcie_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ pcie_device->access_status = pcie_device_pg0.AccessStatus;
+ starget_printk(KERN_INFO, starget,
+ "handle changed from(0x%04x) to (0x%04x)!!!\n",
+ pcie_device->handle, handle);
+ sas_target_priv_data->handle = handle;
+ pcie_device->handle = handle;
+
+ if (le32_to_cpu(pcie_device_pg0.Flags) &
+ MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID) {
+ pcie_device->enclosure_level =
+ pcie_device_pg0.EnclosureLevel;
+ memcpy(&pcie_device->connector_name[0],
+ &pcie_device_pg0.ConnectorName[0], 4);
+ } else {
+ pcie_device->enclosure_level = 0;
+ pcie_device->connector_name[0] = '\0';
+ }
+ }
+
+ /* check if device is present */
+ if (!(le32_to_cpu(pcie_device_pg0.Flags) &
+ MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT)) {
+ ioc_info(ioc, "device is not present handle(0x%04x), flags!!!\n",
+ handle);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ pcie_device_put(pcie_device);
+ return;
+ }
+
+ /* check if there were any issues with discovery */
+ if (_scsih_check_pcie_access_status(ioc, wwid, handle,
+ pcie_device_pg0.AccessStatus)) {
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ pcie_device_put(pcie_device);
+ return;
+ }
+
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ pcie_device_put(pcie_device);
+
+ _scsih_ublock_io_device(ioc, wwid, NULL);
+
+ return;
+}
+
+/**
+ * _scsih_pcie_add_device - creating pcie device object
+ * @ioc: per adapter object
+ * @handle: pcie device handle
+ *
+ * Creating end device object, stored in ioc->pcie_device_list.
+ *
+ * Return: 1 means queue the event later, 0 means complete the event
+ */
+static int
+_scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi26PCIeDevicePage0_t pcie_device_pg0;
+ Mpi26PCIeDevicePage2_t pcie_device_pg2;
+ Mpi2ConfigReply_t mpi_reply;
+ struct _pcie_device *pcie_device;
+ struct _enclosure_node *enclosure_dev;
+ u32 ioc_status;
+ u64 wwid;
+
+ if ((mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply,
+ &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 0;
+ }
+
+ set_bit(handle, ioc->pend_os_device_add);
+ wwid = le64_to_cpu(pcie_device_pg0.WWID);
+
+ /* check if device is present */
+ if (!(le32_to_cpu(pcie_device_pg0.Flags) &
+ MPI26_PCIEDEV0_FLAGS_DEVICE_PRESENT)) {
+ ioc_err(ioc, "device is not present handle(0x04%x)!!!\n",
+ handle);
+ return 0;
+ }
+
+ /* check if there were any issues with discovery */
+ if (_scsih_check_pcie_access_status(ioc, wwid, handle,
+ pcie_device_pg0.AccessStatus))
+ return 0;
+
+ if (!(_scsih_is_nvme_pciescsi_device(le32_to_cpu
+ (pcie_device_pg0.DeviceInfo))))
+ return 0;
+
+ pcie_device = mpt3sas_get_pdev_by_wwid(ioc, wwid);
+ if (pcie_device) {
+ clear_bit(handle, ioc->pend_os_device_add);
+ pcie_device_put(pcie_device);
+ return 0;
+ }
+
+ /* PCIe Device Page 2 contains read-only information about a
+ * specific NVMe device; therefore, this page is only
+ * valid for NVMe devices and skip for pcie devices of type scsi.
+ */
+ if (!(mpt3sas_scsih_is_pcie_scsi_device(
+ le32_to_cpu(pcie_device_pg0.DeviceInfo)))) {
+ if (mpt3sas_config_get_pcie_device_pg2(ioc, &mpi_reply,
+ &pcie_device_pg2, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ handle)) {
+ ioc_err(ioc,
+ "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ return 0;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc,
+ "failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ return 0;
+ }
+ }
+
+ pcie_device = kzalloc(sizeof(struct _pcie_device), GFP_KERNEL);
+ if (!pcie_device) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 0;
+ }
+
+ kref_init(&pcie_device->refcount);
+ pcie_device->id = ioc->pcie_target_id++;
+ pcie_device->channel = PCIE_CHANNEL;
+ pcie_device->handle = handle;
+ pcie_device->access_status = pcie_device_pg0.AccessStatus;
+ pcie_device->device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo);
+ pcie_device->wwid = wwid;
+ pcie_device->port_num = pcie_device_pg0.PortNum;
+ pcie_device->fast_path = (le32_to_cpu(pcie_device_pg0.Flags) &
+ MPI26_PCIEDEV0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0;
+
+ pcie_device->enclosure_handle =
+ le16_to_cpu(pcie_device_pg0.EnclosureHandle);
+ if (pcie_device->enclosure_handle != 0)
+ pcie_device->slot = le16_to_cpu(pcie_device_pg0.Slot);
+
+ if (le32_to_cpu(pcie_device_pg0.Flags) &
+ MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID) {
+ pcie_device->enclosure_level = pcie_device_pg0.EnclosureLevel;
+ memcpy(&pcie_device->connector_name[0],
+ &pcie_device_pg0.ConnectorName[0], 4);
+ } else {
+ pcie_device->enclosure_level = 0;
+ pcie_device->connector_name[0] = '\0';
+ }
+
+ /* get enclosure_logical_id */
+ if (pcie_device->enclosure_handle) {
+ enclosure_dev =
+ mpt3sas_scsih_enclosure_find_by_handle(ioc,
+ pcie_device->enclosure_handle);
+ if (enclosure_dev)
+ pcie_device->enclosure_logical_id =
+ le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID);
+ }
+ /* TODO -- Add device name once FW supports it */
+ if (!(mpt3sas_scsih_is_pcie_scsi_device(
+ le32_to_cpu(pcie_device_pg0.DeviceInfo)))) {
+ pcie_device->nvme_mdts =
+ le32_to_cpu(pcie_device_pg2.MaximumDataTransferSize);
+ pcie_device->shutdown_latency =
+ le16_to_cpu(pcie_device_pg2.ShutdownLatency);
+ /*
+ * Set IOC's max_shutdown_latency to drive's RTD3 Entry Latency
+ * if drive's RTD3 Entry Latency is greater then IOC's
+ * max_shutdown_latency.
+ */
+ if (pcie_device->shutdown_latency > ioc->max_shutdown_latency)
+ ioc->max_shutdown_latency =
+ pcie_device->shutdown_latency;
+ if (pcie_device_pg2.ControllerResetTO)
+ pcie_device->reset_timeout =
+ pcie_device_pg2.ControllerResetTO;
+ else
+ pcie_device->reset_timeout = 30;
+ } else
+ pcie_device->reset_timeout = 30;
+
+ if (ioc->wait_for_discovery_to_complete)
+ _scsih_pcie_device_init_add(ioc, pcie_device);
+ else
+ _scsih_pcie_device_add(ioc, pcie_device);
+
+ pcie_device_put(pcie_device);
+ return 0;
+}
+
+/**
+ * _scsih_pcie_topology_change_event_debug - debug for topology
+ * event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_pcie_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi26EventDataPCIeTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 port_number;
+ char *status_str = NULL;
+ u8 link_rate, prev_link_rate;
+
+ switch (event_data->SwitchStatus) {
+ case MPI26_EVENT_PCIE_TOPO_SS_ADDED:
+ status_str = "add";
+ break;
+ case MPI26_EVENT_PCIE_TOPO_SS_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI26_EVENT_PCIE_TOPO_SS_RESPONDING:
+ case 0:
+ status_str = "responding";
+ break;
+ case MPI26_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ ioc_info(ioc, "pcie topology change: (%s)\n", status_str);
+ pr_info("\tswitch_handle(0x%04x), enclosure_handle(0x%04x)"
+ "start_port(%02d), count(%d)\n",
+ le16_to_cpu(event_data->SwitchDevHandle),
+ le16_to_cpu(event_data->EnclosureHandle),
+ event_data->StartPortNum, event_data->NumEntries);
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle =
+ le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ port_number = event_data->StartPortNum + i;
+ reason_code = event_data->PortEntry[i].PortStatus;
+ switch (reason_code) {
+ case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED:
+ status_str = "target add";
+ break;
+ case MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ status_str = "target remove";
+ break;
+ case MPI26_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING:
+ status_str = "delay target remove";
+ break;
+ case MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
+ status_str = "link rate change";
+ break;
+ case MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE:
+ status_str = "target responding";
+ break;
+ default:
+ status_str = "unknown";
+ break;
+ }
+ link_rate = event_data->PortEntry[i].CurrentPortInfo &
+ MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
+ prev_link_rate = event_data->PortEntry[i].PreviousPortInfo &
+ MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
+ pr_info("\tport(%02d), attached_handle(0x%04x): %s:"
+ " link rate: new(0x%02x), old(0x%02x)\n", port_number,
+ handle, status_str, link_rate, prev_link_rate);
+ }
+}
+
+/**
+ * _scsih_pcie_topology_change_event - handle PCIe topology
+ * changes
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ */
+static void
+_scsih_pcie_topology_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 link_rate, prev_link_rate;
+ unsigned long flags;
+ int rc;
+ Mpi26EventDataPCIeTopologyChangeList_t *event_data =
+ (Mpi26EventDataPCIeTopologyChangeList_t *) fw_event->event_data;
+ struct _pcie_device *pcie_device;
+
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_pcie_topology_change_event_debug(ioc, event_data);
+
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery)
+ return;
+
+ if (fw_event->ignore) {
+ dewtprintk(ioc, ioc_info(ioc, "ignoring switch event\n"));
+ return;
+ }
+
+ /* handle siblings events */
+ for (i = 0; i < event_data->NumEntries; i++) {
+ if (fw_event->ignore) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "ignoring switch event\n"));
+ return;
+ }
+ if (ioc->remove_host || ioc->pci_error_recovery)
+ return;
+ reason_code = event_data->PortEntry[i].PortStatus;
+ handle =
+ le16_to_cpu(event_data->PortEntry[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+
+ link_rate = event_data->PortEntry[i].CurrentPortInfo
+ & MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
+ prev_link_rate = event_data->PortEntry[i].PreviousPortInfo
+ & MPI26_EVENT_PCIE_TOPO_PI_RATE_MASK;
+
+ switch (reason_code) {
+ case MPI26_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
+ if (ioc->shost_recovery)
+ break;
+ if (link_rate == prev_link_rate)
+ break;
+ if (link_rate < MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5)
+ break;
+
+ _scsih_pcie_check_device(ioc, handle);
+
+ /* This code after this point handles the test case
+ * where a device has been added, however its returning
+ * BUSY for sometime. Then before the Device Missing
+ * Delay expires and the device becomes READY, the
+ * device is removed and added back.
+ */
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ if (pcie_device) {
+ pcie_device_put(pcie_device);
+ break;
+ }
+
+ if (!test_bit(handle, ioc->pend_os_device_add))
+ break;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "handle(0x%04x) device not found: convert event to a device add\n",
+ handle));
+ event_data->PortEntry[i].PortStatus &= 0xF0;
+ event_data->PortEntry[i].PortStatus |=
+ MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED;
+ fallthrough;
+ case MPI26_EVENT_PCIE_TOPO_PS_DEV_ADDED:
+ if (ioc->shost_recovery)
+ break;
+ if (link_rate < MPI26_EVENT_PCIE_TOPO_PI_RATE_2_5)
+ break;
+
+ rc = _scsih_pcie_add_device(ioc, handle);
+ if (!rc) {
+ /* mark entry vacant */
+ /* TODO This needs to be reviewed and fixed,
+ * we dont have an entry
+ * to make an event void like vacant
+ */
+ event_data->PortEntry[i].PortStatus |=
+ MPI26_EVENT_PCIE_TOPO_PS_NO_CHANGE;
+ }
+ break;
+ case MPI26_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ _scsih_pcie_device_remove_by_handle(ioc, handle);
+ break;
+ }
+ }
+}
+
+/**
+ * _scsih_pcie_device_status_change_event_debug - debug for device event
+ * @ioc: ?
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_pcie_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi26EventDataPCIeDeviceStatusChange_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->ReasonCode) {
+ case MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA:
+ reason_str = "smart data";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_UNSUPPORTED:
+ reason_str = "unsupported device discovered";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET:
+ reason_str = "internal device reset";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
+ reason_str = "internal task abort set";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
+ reason_str = "internal clear task set";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_QUERY_TASK_INTERNAL:
+ reason_str = "internal query task";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_DEV_INIT_FAILURE:
+ reason_str = "device init failure";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
+ reason_str = "internal device reset complete";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_CMP_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort complete";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_ASYNC_NOTIFICATION:
+ reason_str = "internal async notification";
+ break;
+ case MPI26_EVENT_PCIDEV_STAT_RC_PCIE_HOT_RESET_FAILED:
+ reason_str = "pcie hot reset failed";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+
+ ioc_info(ioc, "PCIE device status change: (%s)\n"
+ "\thandle(0x%04x), WWID(0x%016llx), tag(%d)",
+ reason_str, le16_to_cpu(event_data->DevHandle),
+ (u64)le64_to_cpu(event_data->WWID),
+ le16_to_cpu(event_data->TaskTag));
+ if (event_data->ReasonCode == MPI26_EVENT_PCIDEV_STAT_RC_SMART_DATA)
+ pr_cont(", ASC(0x%x), ASCQ(0x%x)\n",
+ event_data->ASC, event_data->ASCQ);
+ pr_cont("\n");
+}
+
+/**
+ * _scsih_pcie_device_status_change_event - handle device status
+ * change
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_pcie_device_status_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ struct MPT3SAS_TARGET *target_priv_data;
+ struct _pcie_device *pcie_device;
+ u64 wwid;
+ unsigned long flags;
+ Mpi26EventDataPCIeDeviceStatusChange_t *event_data =
+ (Mpi26EventDataPCIeDeviceStatusChange_t *)fw_event->event_data;
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_pcie_device_status_change_event_debug(ioc,
+ event_data);
+
+ if (event_data->ReasonCode !=
+ MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET &&
+ event_data->ReasonCode !=
+ MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET)
+ return;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ wwid = le64_to_cpu(event_data->WWID);
+ pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid);
+
+ if (!pcie_device || !pcie_device->starget)
+ goto out;
+
+ target_priv_data = pcie_device->starget->hostdata;
+ if (!target_priv_data)
+ goto out;
+
+ if (event_data->ReasonCode ==
+ MPI26_EVENT_PCIDEV_STAT_RC_INTERNAL_DEVICE_RESET)
+ target_priv_data->tm_busy = 1;
+ else
+ target_priv_data->tm_busy = 0;
+out:
+ if (pcie_device)
+ pcie_device_put(pcie_device);
+
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+}
+
+/**
+ * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure
+ * event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasEnclDevStatusChange_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_SAS_ENCL_RC_ADDED:
+ reason_str = "enclosure add";
+ break;
+ case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING:
+ reason_str = "enclosure remove";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+
+ ioc_info(ioc, "enclosure status change: (%s)\n"
+ "\thandle(0x%04x), enclosure logical id(0x%016llx) number slots(%d)\n",
+ reason_str,
+ le16_to_cpu(event_data->EnclosureHandle),
+ (u64)le64_to_cpu(event_data->EnclosureLogicalID),
+ le16_to_cpu(event_data->StartSlot));
+}
+
+/**
+ * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_enclosure_dev_status_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ struct _enclosure_node *enclosure_dev = NULL;
+ Mpi2EventDataSasEnclDevStatusChange_t *event_data =
+ (Mpi2EventDataSasEnclDevStatusChange_t *)fw_event->event_data;
+ int rc;
+ u16 enclosure_handle = le16_to_cpu(event_data->EnclosureHandle);
+
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_enclosure_dev_status_change_event_debug(ioc,
+ (Mpi2EventDataSasEnclDevStatusChange_t *)
+ fw_event->event_data);
+ if (ioc->shost_recovery)
+ return;
+
+ if (enclosure_handle)
+ enclosure_dev =
+ mpt3sas_scsih_enclosure_find_by_handle(ioc,
+ enclosure_handle);
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_SAS_ENCL_RC_ADDED:
+ if (!enclosure_dev) {
+ enclosure_dev =
+ kzalloc(sizeof(struct _enclosure_node),
+ GFP_KERNEL);
+ if (!enclosure_dev) {
+ ioc_info(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+ rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply,
+ &enclosure_dev->pg0,
+ MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ enclosure_handle);
+
+ if (rc || (le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK)) {
+ kfree(enclosure_dev);
+ return;
+ }
+
+ list_add_tail(&enclosure_dev->list,
+ &ioc->enclosure_list);
+ }
+ break;
+ case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING:
+ if (enclosure_dev) {
+ list_del(&enclosure_dev->list);
+ kfree(enclosure_dev);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * _scsih_sas_broadcast_primitive_event - handle broadcast events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ struct scsi_cmnd *scmd;
+ struct scsi_device *sdev;
+ struct scsiio_tracker *st;
+ u16 smid, handle;
+ u32 lun;
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ u32 termination_count;
+ u32 query_count;
+ Mpi2SCSITaskManagementReply_t *mpi_reply;
+ Mpi2EventDataSasBroadcastPrimitive_t *event_data =
+ (Mpi2EventDataSasBroadcastPrimitive_t *)
+ fw_event->event_data;
+ u16 ioc_status;
+ unsigned long flags;
+ int r;
+ u8 max_retries = 0;
+ u8 task_abort_retries;
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ ioc_info(ioc, "%s: enter: phy number(%d), width(%d)\n",
+ __func__, event_data->PhyNum, event_data->PortWidth);
+
+ _scsih_block_io_all_device(ioc);
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ mpi_reply = ioc->tm_cmds.reply;
+ broadcast_aen_retry:
+
+ /* sanity checks for retrying this loop */
+ if (max_retries++ == 5) {
+ dewtprintk(ioc, ioc_info(ioc, "%s: giving up\n", __func__));
+ goto out;
+ } else if (max_retries > 1)
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: %d retry\n",
+ __func__, max_retries - 1));
+
+ termination_count = 0;
+ query_count = 0;
+ for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
+ if (ioc->shost_recovery)
+ goto out;
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (!scmd)
+ continue;
+ st = scsi_cmd_priv(scmd);
+ sdev = scmd->device;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target)
+ continue;
+ /* skip hidden raid components */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT)
+ continue;
+ /* skip volumes */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_VOLUME)
+ continue;
+ /* skip PCIe devices */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_PCIE_DEVICE)
+ continue;
+
+ handle = sas_device_priv_data->sas_target->handle;
+ lun = sas_device_priv_data->lun;
+ query_count++;
+
+ if (ioc->shost_recovery)
+ goto out;
+
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ r = mpt3sas_scsih_issue_tm(ioc, handle, 0, 0, lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, st->smid,
+ st->msix_io, 30, 0);
+ if (r == FAILED) {
+ sdev_printk(KERN_WARNING, sdev,
+ "mpt3sas_scsih_issue_tm: FAILED when sending "
+ "QUERY_TASK: scmd(%p)\n", scmd);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ goto broadcast_aen_retry;
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ sdev_printk(KERN_WARNING, sdev,
+ "query task: FAILED with IOCSTATUS(0x%04x), scmd(%p)\n",
+ ioc_status, scmd);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ goto broadcast_aen_retry;
+ }
+
+ /* see if IO is still owned by IOC and target */
+ if (mpi_reply->ResponseCode ==
+ MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
+ mpi_reply->ResponseCode ==
+ MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC) {
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ continue;
+ }
+ task_abort_retries = 0;
+ tm_retry:
+ if (task_abort_retries++ == 60) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: ABORT_TASK: giving up\n",
+ __func__));
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ goto broadcast_aen_retry;
+ }
+
+ if (ioc->shost_recovery)
+ goto out_no_lock;
+
+ r = mpt3sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id,
+ sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
+ st->smid, st->msix_io, 30, 0);
+ if (r == FAILED || st->cb_idx != 0xFF) {
+ sdev_printk(KERN_WARNING, sdev,
+ "mpt3sas_scsih_issue_tm: ABORT_TASK: FAILED : "
+ "scmd(%p)\n", scmd);
+ goto tm_retry;
+ }
+
+ if (task_abort_retries > 1)
+ sdev_printk(KERN_WARNING, sdev,
+ "mpt3sas_scsih_issue_tm: ABORT_TASK: RETRIES (%d):"
+ " scmd(%p)\n",
+ task_abort_retries - 1, scmd);
+
+ termination_count += le32_to_cpu(mpi_reply->TerminationCount);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ }
+
+ if (ioc->broadcast_aen_pending) {
+ dewtprintk(ioc,
+ ioc_info(ioc,
+ "%s: loop back due to pending AEN\n",
+ __func__));
+ ioc->broadcast_aen_pending = 0;
+ goto broadcast_aen_retry;
+ }
+
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ out_no_lock:
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s - exit, query_count = %d termination_count = %d\n",
+ __func__, query_count, termination_count));
+
+ ioc->broadcast_aen_busy = 0;
+ if (!ioc->shost_recovery)
+ _scsih_ublock_io_all_device(ioc);
+ mutex_unlock(&ioc->tm_cmds.mutex);
+}
+
+/**
+ * _scsih_sas_discovery_event - handle discovery events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi2EventDataSasDiscovery_t *event_data =
+ (Mpi2EventDataSasDiscovery_t *) fw_event->event_data;
+
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) {
+ ioc_info(ioc, "discovery event: (%s)",
+ event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED ?
+ "start" : "stop");
+ if (event_data->DiscoveryStatus)
+ pr_cont("discovery_status(0x%08x)",
+ le32_to_cpu(event_data->DiscoveryStatus));
+ pr_cont("\n");
+ }
+
+ if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED &&
+ !ioc->sas_hba.num_phys) {
+ if (disable_discovery > 0 && ioc->shost_recovery) {
+ /* Wait for the reset to complete */
+ while (ioc->shost_recovery)
+ ssleep(1);
+ }
+ _scsih_sas_host_add(ioc);
+ }
+}
+
+/**
+ * _scsih_sas_device_discovery_error_event - display SAS device discovery error
+ * events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_device_discovery_error_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi25EventDataSasDeviceDiscoveryError_t *event_data =
+ (Mpi25EventDataSasDeviceDiscoveryError_t *)fw_event->event_data;
+
+ switch (event_data->ReasonCode) {
+ case MPI25_EVENT_SAS_DISC_ERR_SMP_FAILED:
+ ioc_warn(ioc, "SMP command sent to the expander (handle:0x%04x, sas_address:0x%016llx, physical_port:0x%02x) has failed\n",
+ le16_to_cpu(event_data->DevHandle),
+ (u64)le64_to_cpu(event_data->SASAddress),
+ event_data->PhysicalPort);
+ break;
+ case MPI25_EVENT_SAS_DISC_ERR_SMP_TIMEOUT:
+ ioc_warn(ioc, "SMP command sent to the expander (handle:0x%04x, sas_address:0x%016llx, physical_port:0x%02x) has timed out\n",
+ le16_to_cpu(event_data->DevHandle),
+ (u64)le64_to_cpu(event_data->SASAddress),
+ event_data->PhysicalPort);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * _scsih_pcie_enumeration_event - handle enumeration events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_pcie_enumeration_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi26EventDataPCIeEnumeration_t *event_data =
+ (Mpi26EventDataPCIeEnumeration_t *)fw_event->event_data;
+
+ if (!(ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK))
+ return;
+
+ ioc_info(ioc, "pcie enumeration event: (%s) Flag 0x%02x",
+ (event_data->ReasonCode == MPI26_EVENT_PCIE_ENUM_RC_STARTED) ?
+ "started" : "completed",
+ event_data->Flags);
+ if (event_data->EnumerationStatus)
+ pr_cont("enumeration_status(0x%08x)",
+ le32_to_cpu(event_data->EnumerationStatus));
+ pr_cont("\n");
+}
+
+/**
+ * _scsih_ir_fastpath - turn on fastpath for IR physdisk
+ * @ioc: per adapter object
+ * @handle: device handle for physical disk
+ * @phys_disk_num: physical disk number
+ *
+ * Return: 0 for success, else failure.
+ */
+static int
+_scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num)
+{
+ Mpi2RaidActionRequest_t *mpi_request;
+ Mpi2RaidActionReply_t *mpi_reply;
+ u16 smid;
+ u8 issue_reset = 0;
+ int rc = 0;
+ u16 ioc_status;
+ u32 log_info;
+
+ if (ioc->hba_mpi_version_belonged == MPI2_VERSION)
+ return rc;
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+
+ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: scsih_cmd in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT3_CMD_PENDING;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
+
+ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
+ mpi_request->Action = MPI2_RAID_ACTION_PHYSDISK_HIDDEN;
+ mpi_request->PhysDiskNum = phys_disk_num;
+
+ dewtprintk(ioc,
+ ioc_info(ioc, "IR RAID_ACTION: turning fast path on for handle(0x%04x), phys_disk_num (0x%02x)\n",
+ handle, phys_disk_num));
+
+ init_completion(&ioc->scsih_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->scsih_cmds.status, mpi_request,
+ sizeof(Mpi2RaidActionRequest_t)/4, issue_reset);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
+
+ mpi_reply = ioc->scsih_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
+ log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+ else
+ log_info = 0;
+ ioc_status &= MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dewtprintk(ioc,
+ ioc_info(ioc, "IR RAID_ACTION: failed: ioc_status(0x%04x), loginfo(0x%08x)!!!\n",
+ ioc_status, log_info));
+ rc = -EFAULT;
+ } else
+ dewtprintk(ioc,
+ ioc_info(ioc, "IR RAID_ACTION: completed successfully\n"));
+ }
+
+ out:
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ return rc;
+}
+
+/**
+ * _scsih_reprobe_lun - reprobing lun
+ * @sdev: scsi device struct
+ * @no_uld_attach: sdev->no_uld_attach flag setting
+ *
+ **/
+static void
+_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach)
+{
+ sdev->no_uld_attach = no_uld_attach ? 1 : 0;
+ sdev_printk(KERN_INFO, sdev, "%s raid component\n",
+ sdev->no_uld_attach ? "hiding" : "exposing");
+ WARN_ON(scsi_device_reprobe(sdev));
+}
+
+/**
+ * _scsih_sas_volume_add - add new volume
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ */
+static void
+_scsih_sas_volume_add(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ u64 wwid;
+ u16 handle = le16_to_cpu(element->VolDevHandle);
+ int rc;
+
+ mpt3sas_config_get_volume_wwid(ioc, handle, &wwid);
+ if (!wwid) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (raid_device)
+ return;
+
+ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
+ if (!raid_device) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ raid_device->id = ioc->sas_id++;
+ raid_device->channel = RAID_CHANNEL;
+ raid_device->handle = handle;
+ raid_device->wwid = wwid;
+ _scsih_raid_device_add(ioc, raid_device);
+ if (!ioc->wait_for_discovery_to_complete) {
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ } else {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ _scsih_determine_boot_device(ioc, raid_device, 1);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ }
+}
+
+/**
+ * _scsih_sas_volume_delete - delete volume
+ * @ioc: per adapter object
+ * @handle: volume device handle
+ * Context: user.
+ */
+static void
+_scsih_sas_volume_delete(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget = NULL;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle);
+ if (raid_device) {
+ if (raid_device->starget) {
+ starget = raid_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ }
+ ioc_info(ioc, "removing handle(0x%04x), wwid(0x%016llx)\n",
+ raid_device->handle, (u64)raid_device->wwid);
+ list_del(&raid_device->list);
+ kfree(raid_device);
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (starget)
+ scsi_remove_target(&starget->dev);
+}
+
+/**
+ * _scsih_sas_pd_expose - expose pd component to /dev/sdX
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ */
+static void
+_scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ struct scsi_target *starget = NULL;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (sas_device) {
+ sas_device->volume_handle = 0;
+ sas_device->volume_wwid = 0;
+ clear_bit(handle, ioc->pd_handles);
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->flags &=
+ ~MPT_TARGET_FLAGS_RAID_COMPONENT;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ return;
+
+ /* exposing raid component */
+ if (starget)
+ starget_for_each_device(starget, NULL, _scsih_reprobe_lun);
+
+ sas_device_put(sas_device);
+}
+
+/**
+ * _scsih_sas_pd_hide - hide pd component from /dev/sdX
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ */
+static void
+_scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ struct scsi_target *starget = NULL;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+ u16 volume_handle = 0;
+ u64 volume_wwid = 0;
+
+ mpt3sas_config_get_volume_handle(ioc, handle, &volume_handle);
+ if (volume_handle)
+ mpt3sas_config_get_volume_wwid(ioc, volume_handle,
+ &volume_wwid);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (sas_device) {
+ set_bit(handle, ioc->pd_handles);
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FLAGS_RAID_COMPONENT;
+ sas_device->volume_handle = volume_handle;
+ sas_device->volume_wwid = volume_wwid;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ return;
+
+ /* hiding raid component */
+ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
+
+ if (starget)
+ starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun);
+
+ sas_device_put(sas_device);
+}
+
+/**
+ * _scsih_sas_pd_delete - delete pd component
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ */
+static void
+_scsih_sas_pd_delete(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ _scsih_device_remove_by_handle(ioc, handle);
+}
+
+/**
+ * _scsih_sas_pd_add - remove pd component
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ */
+static void
+_scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
+ u64 sas_address;
+ u16 parent_handle;
+
+ set_bit(handle, ioc->pd_handles);
+
+ sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (sas_device) {
+ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
+ sas_device_put(sas_device);
+ return;
+ }
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt3sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5,
+ mpt3sas_get_port_by_id(ioc,
+ sas_device_pg0.PhysicalPort, 0));
+
+ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
+ _scsih_add_device(ioc, handle, 0, 1);
+}
+
+/**
+ * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_sas_ir_config_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+ Mpi2EventIrConfigElement_t *element;
+ u8 element_type;
+ int i;
+ char *reason_str = NULL, *element_str = NULL;
+
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+
+ ioc_info(ioc, "raid config change: (%s), elements(%d)\n",
+ le32_to_cpu(event_data->Flags) & MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG ?
+ "foreign" : "native",
+ event_data->NumElements);
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ switch (element->ReasonCode) {
+ case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+ reason_str = "add";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+ reason_str = "remove";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE:
+ reason_str = "no change";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_HIDE:
+ reason_str = "hide";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
+ reason_str = "unhide";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+ reason_str = "volume_created";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+ reason_str = "volume_deleted";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+ reason_str = "pd_created";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+ reason_str = "pd_deleted";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+ element_type = le16_to_cpu(element->ElementFlags) &
+ MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK;
+ switch (element_type) {
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT:
+ element_str = "volume";
+ break;
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT:
+ element_str = "phys disk";
+ break;
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT:
+ element_str = "hot spare";
+ break;
+ default:
+ element_str = "unknown element";
+ break;
+ }
+ pr_info("\t(%s:%s), vol handle(0x%04x), " \
+ "pd handle(0x%04x), pd num(0x%02x)\n", element_str,
+ reason_str, le16_to_cpu(element->VolDevHandle),
+ le16_to_cpu(element->PhysDiskDevHandle),
+ element->PhysDiskNum);
+ }
+}
+
+/**
+ * _scsih_sas_ir_config_change_event - handle ir configuration change events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_ir_config_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi2EventIrConfigElement_t *element;
+ int i;
+ u8 foreign_config;
+ Mpi2EventDataIrConfigChangeList_t *event_data =
+ (Mpi2EventDataIrConfigChangeList_t *)
+ fw_event->event_data;
+
+ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) &&
+ (!ioc->hide_ir_msg))
+ _scsih_sas_ir_config_change_event_debug(ioc, event_data);
+
+ foreign_config = (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
+
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ if (ioc->shost_recovery &&
+ ioc->hba_mpi_version_belonged != MPI2_VERSION) {
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_HIDE)
+ _scsih_ir_fastpath(ioc,
+ le16_to_cpu(element->PhysDiskDevHandle),
+ element->PhysDiskNum);
+ }
+ return;
+ }
+
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+
+ switch (element->ReasonCode) {
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+ case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+ if (!foreign_config)
+ _scsih_sas_volume_add(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+ case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+ if (!foreign_config)
+ _scsih_sas_volume_delete(ioc,
+ le16_to_cpu(element->VolDevHandle));
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_hide(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_expose(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_HIDE:
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_add(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_delete(ioc, element);
+ break;
+ }
+ }
+}
+
+/**
+ * _scsih_sas_ir_volume_event - IR volume event
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_ir_volume_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ u64 wwid;
+ unsigned long flags;
+ struct _raid_device *raid_device;
+ u16 handle;
+ u32 state;
+ int rc;
+ Mpi2EventDataIrVolume_t *event_data =
+ (Mpi2EventDataIrVolume_t *) fw_event->event_data;
+
+ if (ioc->shost_recovery)
+ return;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
+ return;
+
+ handle = le16_to_cpu(event_data->VolDevHandle);
+ state = le32_to_cpu(event_data->NewValue);
+ if (!ioc->hide_ir_msg)
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n",
+ __func__, handle,
+ le32_to_cpu(event_data->PreviousValue),
+ state));
+ switch (state) {
+ case MPI2_RAID_VOL_STATE_MISSING:
+ case MPI2_RAID_VOL_STATE_FAILED:
+ _scsih_sas_volume_delete(ioc, handle);
+ break;
+
+ case MPI2_RAID_VOL_STATE_ONLINE:
+ case MPI2_RAID_VOL_STATE_DEGRADED:
+ case MPI2_RAID_VOL_STATE_OPTIMAL:
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (raid_device)
+ break;
+
+ mpt3sas_config_get_volume_wwid(ioc, handle, &wwid);
+ if (!wwid) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
+ if (!raid_device) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ raid_device->id = ioc->sas_id++;
+ raid_device->channel = RAID_CHANNEL;
+ raid_device->handle = handle;
+ raid_device->wwid = wwid;
+ _scsih_raid_device_add(ioc, raid_device);
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ break;
+
+ case MPI2_RAID_VOL_STATE_INITIALIZING:
+ default:
+ break;
+ }
+}
+
+/**
+ * _scsih_sas_ir_physical_disk_event - PD event
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ u16 handle, parent_handle;
+ u32 state;
+ struct _sas_device *sas_device;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
+ Mpi2EventDataIrPhysicalDisk_t *event_data =
+ (Mpi2EventDataIrPhysicalDisk_t *) fw_event->event_data;
+ u64 sas_address;
+
+ if (ioc->shost_recovery)
+ return;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
+ return;
+
+ handle = le16_to_cpu(event_data->PhysDiskDevHandle);
+ state = le32_to_cpu(event_data->NewValue);
+
+ if (!ioc->hide_ir_msg)
+ dewtprintk(ioc,
+ ioc_info(ioc, "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n",
+ __func__, handle,
+ le32_to_cpu(event_data->PreviousValue),
+ state));
+
+ switch (state) {
+ case MPI2_RAID_PD_STATE_ONLINE:
+ case MPI2_RAID_PD_STATE_DEGRADED:
+ case MPI2_RAID_PD_STATE_REBUILDING:
+ case MPI2_RAID_PD_STATE_OPTIMAL:
+ case MPI2_RAID_PD_STATE_HOT_SPARE:
+
+ if (!ioc->is_warpdrive)
+ set_bit(handle, ioc->pd_handles);
+
+ sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (sas_device) {
+ sas_device_put(sas_device);
+ return;
+ }
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt3sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5,
+ mpt3sas_get_port_by_id(ioc,
+ sas_device_pg0.PhysicalPort, 0));
+
+ _scsih_add_device(ioc, handle, 0, 1);
+
+ break;
+
+ case MPI2_RAID_PD_STATE_OFFLINE:
+ case MPI2_RAID_PD_STATE_NOT_CONFIGURED:
+ case MPI2_RAID_PD_STATE_NOT_COMPATIBLE:
+ default:
+ break;
+ }
+}
+
+/**
+ * _scsih_sas_ir_operation_status_event_debug - debug for IR op event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_sas_ir_operation_status_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrOperationStatus_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->RAIDOperation) {
+ case MPI2_EVENT_IR_RAIDOP_RESYNC:
+ reason_str = "resync";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION:
+ reason_str = "online capacity expansion";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
+ reason_str = "consistency check";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT:
+ reason_str = "background init";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT:
+ reason_str = "make data consistent";
+ break;
+ }
+
+ if (!reason_str)
+ return;
+
+ ioc_info(ioc, "raid operational status: (%s)\thandle(0x%04x), percent complete(%d)\n",
+ reason_str,
+ le16_to_cpu(event_data->VolDevHandle),
+ event_data->PercentComplete);
+}
+
+/**
+ * _scsih_sas_ir_operation_status_event - handle RAID operation events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_scsih_sas_ir_operation_status_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi2EventDataIrOperationStatus_t *event_data =
+ (Mpi2EventDataIrOperationStatus_t *)
+ fw_event->event_data;
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ u16 handle;
+
+ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) &&
+ (!ioc->hide_ir_msg))
+ _scsih_sas_ir_operation_status_event_debug(ioc,
+ event_data);
+
+ /* code added for raid transport support */
+ if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) {
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ handle = le16_to_cpu(event_data->VolDevHandle);
+ raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle);
+ if (raid_device)
+ raid_device->percent_complete =
+ event_data->PercentComplete;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ }
+}
+
+/**
+ * _scsih_prep_device_scan - initialize parameters prior to device scan
+ * @ioc: per adapter object
+ *
+ * Set the deleted flag prior to device scan. If the device is found during
+ * the scan, then we clear the deleted flag.
+ */
+static void
+_scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (sas_device_priv_data && sas_device_priv_data->sas_target)
+ sas_device_priv_data->sas_target->deleted = 1;
+ }
+}
+
+/**
+ * _scsih_update_device_qdepth - Update QD during Reset.
+ * @ioc: per adapter object
+ *
+ */
+static void
+_scsih_update_device_qdepth(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct scsi_device *sdev;
+ u16 qdepth;
+
+ ioc_info(ioc, "Update devices with firmware reported queue depth\n");
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (sas_device_priv_data && sas_device_priv_data->sas_target) {
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ sas_device = sas_device_priv_data->sas_target->sas_dev;
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_PCIE_DEVICE)
+ qdepth = ioc->max_nvme_qd;
+ else if (sas_device &&
+ sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET)
+ qdepth = (sas_device->port_type > 1) ?
+ ioc->max_wideport_qd : ioc->max_narrowport_qd;
+ else if (sas_device &&
+ sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ qdepth = ioc->max_sata_qd;
+ else
+ continue;
+ mpt3sas_scsih_change_queue_depth(sdev, qdepth);
+ }
+ }
+}
+
+/**
+ * _scsih_mark_responding_sas_device - mark a sas_devices as responding
+ * @ioc: per adapter object
+ * @sas_device_pg0: SAS Device page 0
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsih_remove_unresponsive_sas_devices.
+ */
+static void
+_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc,
+Mpi2SasDevicePage0_t *sas_device_pg0)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
+ struct scsi_target *starget;
+ struct _sas_device *sas_device = NULL;
+ struct _enclosure_node *enclosure_dev = NULL;
+ unsigned long flags;
+ struct hba_port *port = mpt3sas_get_port_by_id(
+ ioc, sas_device_pg0->PhysicalPort, 0);
+
+ if (sas_device_pg0->EnclosureHandle) {
+ enclosure_dev =
+ mpt3sas_scsih_enclosure_find_by_handle(ioc,
+ le16_to_cpu(sas_device_pg0->EnclosureHandle));
+ if (enclosure_dev == NULL)
+ ioc_info(ioc, "Enclosure handle(0x%04x) doesn't match with enclosure device!\n",
+ sas_device_pg0->EnclosureHandle);
+ }
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (sas_device->sas_address != le64_to_cpu(
+ sas_device_pg0->SASAddress))
+ continue;
+ if (sas_device->slot != le16_to_cpu(sas_device_pg0->Slot))
+ continue;
+ if (sas_device->port != port)
+ continue;
+ sas_device->responding = 1;
+ starget = sas_device->starget;
+ if (starget && starget->hostdata) {
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->tm_busy = 0;
+ sas_target_priv_data->deleted = 0;
+ } else
+ sas_target_priv_data = NULL;
+ if (starget) {
+ starget_printk(KERN_INFO, starget,
+ "handle(0x%04x), sas_addr(0x%016llx)\n",
+ le16_to_cpu(sas_device_pg0->DevHandle),
+ (unsigned long long)
+ sas_device->sas_address);
+
+ if (sas_device->enclosure_handle != 0)
+ starget_printk(KERN_INFO, starget,
+ "enclosure logical id(0x%016llx), slot(%d)\n",
+ (unsigned long long)
+ sas_device->enclosure_logical_id,
+ sas_device->slot);
+ }
+ if (le16_to_cpu(sas_device_pg0->Flags) &
+ MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+ sas_device->enclosure_level =
+ sas_device_pg0->EnclosureLevel;
+ memcpy(&sas_device->connector_name[0],
+ &sas_device_pg0->ConnectorName[0], 4);
+ } else {
+ sas_device->enclosure_level = 0;
+ sas_device->connector_name[0] = '\0';
+ }
+
+ sas_device->enclosure_handle =
+ le16_to_cpu(sas_device_pg0->EnclosureHandle);
+ sas_device->is_chassis_slot_valid = 0;
+ if (enclosure_dev) {
+ sas_device->enclosure_logical_id = le64_to_cpu(
+ enclosure_dev->pg0.EnclosureLogicalID);
+ if (le16_to_cpu(enclosure_dev->pg0.Flags) &
+ MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) {
+ sas_device->is_chassis_slot_valid = 1;
+ sas_device->chassis_slot =
+ enclosure_dev->pg0.ChassisSlot;
+ }
+ }
+
+ if (sas_device->handle == le16_to_cpu(
+ sas_device_pg0->DevHandle))
+ goto out;
+ pr_info("\thandle changed from(0x%04x)!!!\n",
+ sas_device->handle);
+ sas_device->handle = le16_to_cpu(
+ sas_device_pg0->DevHandle);
+ if (sas_target_priv_data)
+ sas_target_priv_data->handle =
+ le16_to_cpu(sas_device_pg0->DevHandle);
+ goto out;
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_create_enclosure_list_after_reset - Free Existing list,
+ * And create enclosure list by scanning all Enclosure Page(0)s
+ * @ioc: per adapter object
+ */
+static void
+_scsih_create_enclosure_list_after_reset(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _enclosure_node *enclosure_dev;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 enclosure_handle;
+ int rc;
+
+ /* Free existing enclosure list */
+ mpt3sas_free_enclosure_list(ioc);
+
+ /* Re constructing enclosure list after reset*/
+ enclosure_handle = 0xFFFF;
+ do {
+ enclosure_dev =
+ kzalloc(sizeof(struct _enclosure_node), GFP_KERNEL);
+ if (!enclosure_dev) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+ rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply,
+ &enclosure_dev->pg0,
+ MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE,
+ enclosure_handle);
+
+ if (rc || (le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK)) {
+ kfree(enclosure_dev);
+ return;
+ }
+ list_add_tail(&enclosure_dev->list,
+ &ioc->enclosure_list);
+ enclosure_handle =
+ le16_to_cpu(enclosure_dev->pg0.EnclosureHandle);
+ } while (1);
+}
+
+/**
+ * _scsih_search_responding_sas_devices -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ */
+static void
+_scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 handle;
+ u32 device_info;
+
+ ioc_info(ioc, "search for end-devices: start\n");
+
+ if (list_empty(&ioc->sas_device_list))
+ goto out;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE,
+ handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ break;
+ handle = le16_to_cpu(sas_device_pg0.DevHandle);
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info)))
+ continue;
+ _scsih_mark_responding_sas_device(ioc, &sas_device_pg0);
+ }
+
+ out:
+ ioc_info(ioc, "search for end-devices: complete\n");
+}
+
+/**
+ * _scsih_mark_responding_pcie_device - mark a pcie_device as responding
+ * @ioc: per adapter object
+ * @pcie_device_pg0: PCIe Device page 0
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsih_remove_unresponding_devices.
+ */
+static void
+_scsih_mark_responding_pcie_device(struct MPT3SAS_ADAPTER *ioc,
+ Mpi26PCIeDevicePage0_t *pcie_device_pg0)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
+ struct scsi_target *starget;
+ struct _pcie_device *pcie_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) {
+ if ((pcie_device->wwid == le64_to_cpu(pcie_device_pg0->WWID))
+ && (pcie_device->slot == le16_to_cpu(
+ pcie_device_pg0->Slot))) {
+ pcie_device->access_status =
+ pcie_device_pg0->AccessStatus;
+ pcie_device->responding = 1;
+ starget = pcie_device->starget;
+ if (starget && starget->hostdata) {
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->tm_busy = 0;
+ sas_target_priv_data->deleted = 0;
+ } else
+ sas_target_priv_data = NULL;
+ if (starget) {
+ starget_printk(KERN_INFO, starget,
+ "handle(0x%04x), wwid(0x%016llx) ",
+ pcie_device->handle,
+ (unsigned long long)pcie_device->wwid);
+ if (pcie_device->enclosure_handle != 0)
+ starget_printk(KERN_INFO, starget,
+ "enclosure logical id(0x%016llx), "
+ "slot(%d)\n",
+ (unsigned long long)
+ pcie_device->enclosure_logical_id,
+ pcie_device->slot);
+ }
+
+ if (((le32_to_cpu(pcie_device_pg0->Flags)) &
+ MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID) &&
+ (ioc->hba_mpi_version_belonged != MPI2_VERSION)) {
+ pcie_device->enclosure_level =
+ pcie_device_pg0->EnclosureLevel;
+ memcpy(&pcie_device->connector_name[0],
+ &pcie_device_pg0->ConnectorName[0], 4);
+ } else {
+ pcie_device->enclosure_level = 0;
+ pcie_device->connector_name[0] = '\0';
+ }
+
+ if (pcie_device->handle == le16_to_cpu(
+ pcie_device_pg0->DevHandle))
+ goto out;
+ pr_info("\thandle changed from(0x%04x)!!!\n",
+ pcie_device->handle);
+ pcie_device->handle = le16_to_cpu(
+ pcie_device_pg0->DevHandle);
+ if (sas_target_priv_data)
+ sas_target_priv_data->handle =
+ le16_to_cpu(pcie_device_pg0->DevHandle);
+ goto out;
+ }
+ }
+
+ out:
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_pcie_devices -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ */
+static void
+_scsih_search_responding_pcie_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi26PCIeDevicePage0_t pcie_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 handle;
+ u32 device_info;
+
+ ioc_info(ioc, "search for end-devices: start\n");
+
+ if (list_empty(&ioc->pcie_device_list))
+ goto out;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply,
+ &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_GET_NEXT_HANDLE,
+ handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from %s: ioc_status(0x%04x), loginfo(0x%08x)\n",
+ __func__, ioc_status,
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(pcie_device_pg0.DevHandle);
+ device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo);
+ if (!(_scsih_is_nvme_pciescsi_device(device_info)))
+ continue;
+ _scsih_mark_responding_pcie_device(ioc, &pcie_device_pg0);
+ }
+out:
+ ioc_info(ioc, "search for PCIe end-devices: complete\n");
+}
+
+/**
+ * _scsih_mark_responding_raid_device - mark a raid_device as responding
+ * @ioc: per adapter object
+ * @wwid: world wide identifier for raid volume
+ * @handle: device handle
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsih_remove_unresponsive_raid_devices.
+ */
+static void
+_scsih_mark_responding_raid_device(struct MPT3SAS_ADAPTER *ioc, u64 wwid,
+ u16 handle)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
+ struct scsi_target *starget;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->wwid == wwid && raid_device->starget) {
+ starget = raid_device->starget;
+ if (starget && starget->hostdata) {
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->deleted = 0;
+ } else
+ sas_target_priv_data = NULL;
+ raid_device->responding = 1;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ starget_printk(KERN_INFO, raid_device->starget,
+ "handle(0x%04x), wwid(0x%016llx)\n", handle,
+ (unsigned long long)raid_device->wwid);
+
+ /*
+ * WARPDRIVE: The handles of the PDs might have changed
+ * across the host reset so re-initialize the
+ * required data for Direct IO
+ */
+ mpt3sas_init_warpdrive_properties(ioc, raid_device);
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ if (raid_device->handle == handle) {
+ spin_unlock_irqrestore(&ioc->raid_device_lock,
+ flags);
+ return;
+ }
+ pr_info("\thandle changed from(0x%04x)!!!\n",
+ raid_device->handle);
+ raid_device->handle = handle;
+ if (sas_target_priv_data)
+ sas_target_priv_data->handle = handle;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_raid_devices -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ */
+static void
+_scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2RaidVolPage1_t volume_pg1;
+ Mpi2RaidVolPage0_t volume_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 handle;
+ u8 phys_disk_num;
+
+ if (!ioc->ir_firmware)
+ return;
+
+ ioc_info(ioc, "search for raid volumes: start\n");
+
+ if (list_empty(&ioc->raid_device_list))
+ goto out;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ break;
+ handle = le16_to_cpu(volume_pg1.DevHandle);
+
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply,
+ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t)))
+ continue;
+
+ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED)
+ _scsih_mark_responding_raid_device(ioc,
+ le64_to_cpu(volume_pg1.WWID), handle);
+ }
+
+ /* refresh the pd_handles */
+ if (!ioc->is_warpdrive) {
+ phys_disk_num = 0xFF;
+ memset(ioc->pd_handles, 0, ioc->pd_handles_sz);
+ while (!(mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM,
+ phys_disk_num))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ break;
+ phys_disk_num = pd_pg0.PhysDiskNum;
+ handle = le16_to_cpu(pd_pg0.DevHandle);
+ set_bit(handle, ioc->pd_handles);
+ }
+ }
+ out:
+ ioc_info(ioc, "search for responding raid volumes: complete\n");
+}
+
+/**
+ * _scsih_mark_responding_expander - mark a expander as responding
+ * @ioc: per adapter object
+ * @expander_pg0:SAS Expander Config Page0
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsih_remove_unresponsive_expanders.
+ */
+static void
+_scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ExpanderPage0_t *expander_pg0)
+{
+ struct _sas_node *sas_expander = NULL;
+ unsigned long flags;
+ int i;
+ struct _enclosure_node *enclosure_dev = NULL;
+ u16 handle = le16_to_cpu(expander_pg0->DevHandle);
+ u16 enclosure_handle = le16_to_cpu(expander_pg0->EnclosureHandle);
+ u64 sas_address = le64_to_cpu(expander_pg0->SASAddress);
+ struct hba_port *port = mpt3sas_get_port_by_id(
+ ioc, expander_pg0->PhysicalPort, 0);
+
+ if (enclosure_handle)
+ enclosure_dev =
+ mpt3sas_scsih_enclosure_find_by_handle(ioc,
+ enclosure_handle);
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->sas_address != sas_address)
+ continue;
+ if (sas_expander->port != port)
+ continue;
+ sas_expander->responding = 1;
+
+ if (enclosure_dev) {
+ sas_expander->enclosure_logical_id =
+ le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID);
+ sas_expander->enclosure_handle =
+ le16_to_cpu(expander_pg0->EnclosureHandle);
+ }
+
+ if (sas_expander->handle == handle)
+ goto out;
+ pr_info("\texpander(0x%016llx): handle changed" \
+ " from(0x%04x) to (0x%04x)!!!\n",
+ (unsigned long long)sas_expander->sas_address,
+ sas_expander->handle, handle);
+ sas_expander->handle = handle;
+ for (i = 0 ; i < sas_expander->num_phys ; i++)
+ sas_expander->phy[i].handle = handle;
+ goto out;
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_expanders -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ */
+static void
+_scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u64 sas_address;
+ u16 handle;
+ u8 port;
+
+ ioc_info(ioc, "search for expanders: start\n");
+
+ if (list_empty(&ioc->sas_expander_list))
+ goto out;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) {
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ break;
+
+ handle = le16_to_cpu(expander_pg0.DevHandle);
+ sas_address = le64_to_cpu(expander_pg0.SASAddress);
+ port = expander_pg0.PhysicalPort;
+ pr_info(
+ "\texpander present: handle(0x%04x), sas_addr(0x%016llx), port:%d\n",
+ handle, (unsigned long long)sas_address,
+ (ioc->multipath_on_hba ?
+ port : MULTIPATH_DISABLED_PORT_ID));
+ _scsih_mark_responding_expander(ioc, &expander_pg0);
+ }
+
+ out:
+ ioc_info(ioc, "search for expanders: complete\n");
+}
+
+/**
+ * _scsih_remove_unresponding_devices - removing unresponding devices
+ * @ioc: per adapter object
+ */
+static void
+_scsih_remove_unresponding_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device, *sas_device_next;
+ struct _sas_node *sas_expander, *sas_expander_next;
+ struct _raid_device *raid_device, *raid_device_next;
+ struct _pcie_device *pcie_device, *pcie_device_next;
+ struct list_head tmp_list;
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ ioc_info(ioc, "removing unresponding devices: start\n");
+
+ /* removing unresponding end devices */
+ ioc_info(ioc, "removing unresponding devices: end-devices\n");
+ /*
+ * Iterate, pulling off devices marked as non-responding. We become the
+ * owner for the reference the list had on any object we prune.
+ */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+
+ /*
+ * Clean up the sas_device_init_list list as
+ * driver goes for fresh scan as part of diag reset.
+ */
+ list_for_each_entry_safe(sas_device, sas_device_next,
+ &ioc->sas_device_init_list, list) {
+ list_del_init(&sas_device->list);
+ sas_device_put(sas_device);
+ }
+
+ list_for_each_entry_safe(sas_device, sas_device_next,
+ &ioc->sas_device_list, list) {
+ if (!sas_device->responding)
+ list_move_tail(&sas_device->list, &head);
+ else
+ sas_device->responding = 0;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ /*
+ * Now, uninitialize and remove the unresponding devices we pruned.
+ */
+ list_for_each_entry_safe(sas_device, sas_device_next, &head, list) {
+ _scsih_remove_device(ioc, sas_device);
+ list_del_init(&sas_device->list);
+ sas_device_put(sas_device);
+ }
+
+ ioc_info(ioc, "Removing unresponding devices: pcie end-devices\n");
+ INIT_LIST_HEAD(&head);
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ /*
+ * Clean up the pcie_device_init_list list as
+ * driver goes for fresh scan as part of diag reset.
+ */
+ list_for_each_entry_safe(pcie_device, pcie_device_next,
+ &ioc->pcie_device_init_list, list) {
+ list_del_init(&pcie_device->list);
+ pcie_device_put(pcie_device);
+ }
+
+ list_for_each_entry_safe(pcie_device, pcie_device_next,
+ &ioc->pcie_device_list, list) {
+ if (!pcie_device->responding)
+ list_move_tail(&pcie_device->list, &head);
+ else
+ pcie_device->responding = 0;
+ }
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ list_for_each_entry_safe(pcie_device, pcie_device_next, &head, list) {
+ _scsih_pcie_device_remove_from_sml(ioc, pcie_device);
+ list_del_init(&pcie_device->list);
+ pcie_device_put(pcie_device);
+ }
+
+ /* removing unresponding volumes */
+ if (ioc->ir_firmware) {
+ ioc_info(ioc, "removing unresponding devices: volumes\n");
+ list_for_each_entry_safe(raid_device, raid_device_next,
+ &ioc->raid_device_list, list) {
+ if (!raid_device->responding)
+ _scsih_sas_volume_delete(ioc,
+ raid_device->handle);
+ else
+ raid_device->responding = 0;
+ }
+ }
+
+ /* removing unresponding expanders */
+ ioc_info(ioc, "removing unresponding devices: expanders\n");
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ INIT_LIST_HEAD(&tmp_list);
+ list_for_each_entry_safe(sas_expander, sas_expander_next,
+ &ioc->sas_expander_list, list) {
+ if (!sas_expander->responding)
+ list_move_tail(&sas_expander->list, &tmp_list);
+ else
+ sas_expander->responding = 0;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ list_for_each_entry_safe(sas_expander, sas_expander_next, &tmp_list,
+ list) {
+ _scsih_expander_node_remove(ioc, sas_expander);
+ }
+
+ ioc_info(ioc, "removing unresponding devices: complete\n");
+
+ /* unblock devices */
+ _scsih_ublock_io_all_device(ioc);
+}
+
+static void
+_scsih_refresh_expander_links(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander, u16 handle)
+{
+ Mpi2ExpanderPage1_t expander_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ int i;
+
+ for (i = 0 ; i < sas_expander->num_phys ; i++) {
+ if ((mpt3sas_config_get_expander_pg1(ioc, &mpi_reply,
+ &expander_pg1, i, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ mpt3sas_transport_update_links(ioc, sas_expander->sas_address,
+ le16_to_cpu(expander_pg1.AttachedDevHandle), i,
+ expander_pg1.NegotiatedLinkRate >> 4,
+ sas_expander->port);
+ }
+}
+
+/**
+ * _scsih_scan_for_devices_after_reset - scan for devices after host reset
+ * @ioc: per adapter object
+ */
+static void
+_scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi26PCIeDevicePage0_t pcie_device_pg0;
+ Mpi2RaidVolPage1_t volume_pg1;
+ Mpi2RaidVolPage0_t volume_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2EventIrConfigElement_t element;
+ Mpi2ConfigReply_t mpi_reply;
+ u8 phys_disk_num, port_id;
+ u16 ioc_status;
+ u16 handle, parent_handle;
+ u64 sas_address;
+ struct _sas_device *sas_device;
+ struct _pcie_device *pcie_device;
+ struct _sas_node *expander_device;
+ static struct _raid_device *raid_device;
+ u8 retry_count;
+ unsigned long flags;
+
+ ioc_info(ioc, "scan devices: start\n");
+
+ _scsih_sas_host_refresh(ioc);
+
+ ioc_info(ioc, "\tscan devices: expanders start\n");
+
+ /* expanders */
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from expander scan: ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc_status, le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(expander_pg0.DevHandle);
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ port_id = expander_pg0.PhysicalPort;
+ expander_device = mpt3sas_scsih_expander_find_by_sas_address(
+ ioc, le64_to_cpu(expander_pg0.SASAddress),
+ mpt3sas_get_port_by_id(ioc, port_id, 0));
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (expander_device)
+ _scsih_refresh_expander_links(ioc, expander_device,
+ handle);
+ else {
+ ioc_info(ioc, "\tBEFORE adding expander: handle (0x%04x), sas_addr(0x%016llx)\n",
+ handle,
+ (u64)le64_to_cpu(expander_pg0.SASAddress));
+ _scsih_expander_add(ioc, handle);
+ ioc_info(ioc, "\tAFTER adding expander: handle (0x%04x), sas_addr(0x%016llx)\n",
+ handle,
+ (u64)le64_to_cpu(expander_pg0.SASAddress));
+ }
+ }
+
+ ioc_info(ioc, "\tscan devices: expanders complete\n");
+
+ if (!ioc->ir_firmware)
+ goto skip_to_sas;
+
+ ioc_info(ioc, "\tscan devices: phys disk start\n");
+
+ /* phys disk */
+ phys_disk_num = 0xFF;
+ while (!(mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM,
+ phys_disk_num))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from phys disk scan: ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc_status, le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ phys_disk_num = pd_pg0.PhysDiskNum;
+ handle = le16_to_cpu(pd_pg0.DevHandle);
+ sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
+ if (sas_device) {
+ sas_device_put(sas_device);
+ continue;
+ }
+ if (mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ handle) != 0)
+ continue;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from phys disk scan ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc_status, le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle,
+ &sas_address)) {
+ ioc_info(ioc, "\tBEFORE adding phys disk: handle (0x%04x), sas_addr(0x%016llx)\n",
+ handle,
+ (u64)le64_to_cpu(sas_device_pg0.SASAddress));
+ port_id = sas_device_pg0.PhysicalPort;
+ mpt3sas_transport_update_links(ioc, sas_address,
+ handle, sas_device_pg0.PhyNum,
+ MPI2_SAS_NEG_LINK_RATE_1_5,
+ mpt3sas_get_port_by_id(ioc, port_id, 0));
+ set_bit(handle, ioc->pd_handles);
+ retry_count = 0;
+ /* This will retry adding the end device.
+ * _scsih_add_device() will decide on retries and
+ * return "1" when it should be retried
+ */
+ while (_scsih_add_device(ioc, handle, retry_count++,
+ 1)) {
+ ssleep(1);
+ }
+ ioc_info(ioc, "\tAFTER adding phys disk: handle (0x%04x), sas_addr(0x%016llx)\n",
+ handle,
+ (u64)le64_to_cpu(sas_device_pg0.SASAddress));
+ }
+ }
+
+ ioc_info(ioc, "\tscan devices: phys disk complete\n");
+
+ ioc_info(ioc, "\tscan devices: volumes start\n");
+
+ /* volumes */
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from volume scan: ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc_status, le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(volume_pg1.DevHandle);
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_wwid(ioc,
+ le64_to_cpu(volume_pg1.WWID));
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (raid_device)
+ continue;
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply,
+ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t)))
+ continue;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from volume scan: ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc_status, le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) {
+ memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t));
+ element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED;
+ element.VolDevHandle = volume_pg1.DevHandle;
+ ioc_info(ioc, "\tBEFORE adding volume: handle (0x%04x)\n",
+ volume_pg1.DevHandle);
+ _scsih_sas_volume_add(ioc, &element);
+ ioc_info(ioc, "\tAFTER adding volume: handle (0x%04x)\n",
+ volume_pg1.DevHandle);
+ }
+ }
+
+ ioc_info(ioc, "\tscan devices: volumes complete\n");
+
+ skip_to_sas:
+
+ ioc_info(ioc, "\tscan devices: end devices start\n");
+
+ /* sas devices */
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE,
+ handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from end device scan: ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc_status, le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(sas_device_pg0.DevHandle);
+ if (!(_scsih_is_end_device(
+ le32_to_cpu(sas_device_pg0.DeviceInfo))))
+ continue;
+ port_id = sas_device_pg0.PhysicalPort;
+ sas_device = mpt3sas_get_sdev_by_addr(ioc,
+ le64_to_cpu(sas_device_pg0.SASAddress),
+ mpt3sas_get_port_by_id(ioc, port_id, 0));
+ if (sas_device) {
+ sas_device_put(sas_device);
+ continue;
+ }
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) {
+ ioc_info(ioc, "\tBEFORE adding end device: handle (0x%04x), sas_addr(0x%016llx)\n",
+ handle,
+ (u64)le64_to_cpu(sas_device_pg0.SASAddress));
+ mpt3sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5,
+ mpt3sas_get_port_by_id(ioc, port_id, 0));
+ retry_count = 0;
+ /* This will retry adding the end device.
+ * _scsih_add_device() will decide on retries and
+ * return "1" when it should be retried
+ */
+ while (_scsih_add_device(ioc, handle, retry_count++,
+ 0)) {
+ ssleep(1);
+ }
+ ioc_info(ioc, "\tAFTER adding end device: handle (0x%04x), sas_addr(0x%016llx)\n",
+ handle,
+ (u64)le64_to_cpu(sas_device_pg0.SASAddress));
+ }
+ }
+ ioc_info(ioc, "\tscan devices: end devices complete\n");
+ ioc_info(ioc, "\tscan devices: pcie end devices start\n");
+
+ /* pcie devices */
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_pcie_device_pg0(ioc, &mpi_reply,
+ &pcie_device_pg0, MPI26_PCIE_DEVICE_PGAD_FORM_GET_NEXT_HANDLE,
+ handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_info(ioc, "\tbreak from pcie end device scan: ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc_status, le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(pcie_device_pg0.DevHandle);
+ if (!(_scsih_is_nvme_pciescsi_device(
+ le32_to_cpu(pcie_device_pg0.DeviceInfo))))
+ continue;
+ pcie_device = mpt3sas_get_pdev_by_wwid(ioc,
+ le64_to_cpu(pcie_device_pg0.WWID));
+ if (pcie_device) {
+ pcie_device_put(pcie_device);
+ continue;
+ }
+ retry_count = 0;
+ parent_handle = le16_to_cpu(pcie_device_pg0.ParentDevHandle);
+ _scsih_pcie_add_device(ioc, handle);
+
+ ioc_info(ioc, "\tAFTER adding pcie end device: handle (0x%04x), wwid(0x%016llx)\n",
+ handle, (u64)le64_to_cpu(pcie_device_pg0.WWID));
+ }
+
+ ioc_info(ioc, "\tpcie devices: pcie end devices complete\n");
+ ioc_info(ioc, "scan devices: complete\n");
+}
+
+/**
+ * mpt3sas_scsih_pre_reset_handler - reset callback handler (for scsih)
+ * @ioc: per adapter object
+ *
+ * The handler for doing any required cleanup or initialization.
+ */
+void mpt3sas_scsih_pre_reset_handler(struct MPT3SAS_ADAPTER *ioc)
+{
+ dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_PRE_RESET\n", __func__));
+}
+
+/**
+ * mpt3sas_scsih_clear_outstanding_scsi_tm_commands - clears outstanding
+ * scsi & tm cmds.
+ * @ioc: per adapter object
+ *
+ * The handler for doing any required cleanup or initialization.
+ */
+void
+mpt3sas_scsih_clear_outstanding_scsi_tm_commands(struct MPT3SAS_ADAPTER *ioc)
+{
+ dtmprintk(ioc,
+ ioc_info(ioc, "%s: clear outstanding scsi & tm cmds\n", __func__));
+ if (ioc->scsih_cmds.status & MPT3_CMD_PENDING) {
+ ioc->scsih_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->scsih_cmds.smid);
+ complete(&ioc->scsih_cmds.done);
+ }
+ if (ioc->tm_cmds.status & MPT3_CMD_PENDING) {
+ ioc->tm_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->tm_cmds.smid);
+ complete(&ioc->tm_cmds.done);
+ }
+
+ memset(ioc->pend_os_device_add, 0, ioc->pend_os_device_add_sz);
+ memset(ioc->device_remove_in_progress, 0,
+ ioc->device_remove_in_progress_sz);
+ _scsih_fw_event_cleanup_queue(ioc);
+ _scsih_flush_running_cmds(ioc);
+}
+
+/**
+ * mpt3sas_scsih_reset_done_handler - reset callback handler (for scsih)
+ * @ioc: per adapter object
+ *
+ * The handler for doing any required cleanup or initialization.
+ */
+void
+mpt3sas_scsih_reset_done_handler(struct MPT3SAS_ADAPTER *ioc)
+{
+ dtmprintk(ioc, ioc_info(ioc, "%s: MPT3_IOC_DONE_RESET\n", __func__));
+ if (!(disable_discovery > 0 && !ioc->sas_hba.num_phys)) {
+ if (ioc->multipath_on_hba) {
+ _scsih_sas_port_refresh(ioc);
+ _scsih_update_vphys_after_reset(ioc);
+ }
+ _scsih_prep_device_scan(ioc);
+ _scsih_create_enclosure_list_after_reset(ioc);
+ _scsih_search_responding_sas_devices(ioc);
+ _scsih_search_responding_pcie_devices(ioc);
+ _scsih_search_responding_raid_devices(ioc);
+ _scsih_search_responding_expanders(ioc);
+ _scsih_error_recovery_delete_devices(ioc);
+ }
+}
+
+/**
+ * _mpt3sas_fw_work - delayed task for processing firmware events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ */
+static void
+_mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
+{
+ ioc->current_event = fw_event;
+ _scsih_fw_event_del_from_list(ioc, fw_event);
+
+ /* the queue is being flushed so ignore this event */
+ if (ioc->remove_host || ioc->pci_error_recovery) {
+ fw_event_work_put(fw_event);
+ ioc->current_event = NULL;
+ return;
+ }
+
+ switch (fw_event->event) {
+ case MPT3SAS_PROCESS_TRIGGER_DIAG:
+ mpt3sas_process_trigger_data(ioc,
+ (struct SL_WH_TRIGGERS_EVENT_DATA_T *)
+ fw_event->event_data);
+ break;
+ case MPT3SAS_REMOVE_UNRESPONDING_DEVICES:
+ while (scsi_host_in_recovery(ioc->shost) ||
+ ioc->shost_recovery) {
+ /*
+ * If we're unloading or cancelling the work, bail.
+ * Otherwise, this can become an infinite loop.
+ */
+ if (ioc->remove_host || ioc->fw_events_cleanup)
+ goto out;
+ ssleep(1);
+ }
+ _scsih_remove_unresponding_devices(ioc);
+ _scsih_del_dirty_vphy(ioc);
+ _scsih_del_dirty_port_entries(ioc);
+ if (ioc->is_gen35_ioc)
+ _scsih_update_device_qdepth(ioc);
+ _scsih_scan_for_devices_after_reset(ioc);
+ /*
+ * If diag reset has occurred during the driver load
+ * then driver has to complete the driver load operation
+ * by executing the following items:
+ *- Register the devices from sas_device_init_list to SML
+ *- clear is_driver_loading flag,
+ *- start the watchdog thread.
+ * In happy driver load path, above things are taken care of when
+ * driver executes scsih_scan_finished().
+ */
+ if (ioc->is_driver_loading)
+ _scsih_complete_devices_scanning(ioc);
+ _scsih_set_nvme_max_shutdown_latency(ioc);
+ break;
+ case MPT3SAS_PORT_ENABLE_COMPLETE:
+ ioc->start_scan = 0;
+ if (missing_delay[0] != -1 && missing_delay[1] != -1)
+ mpt3sas_base_update_missing_delay(ioc, missing_delay[0],
+ missing_delay[1]);
+ dewtprintk(ioc,
+ ioc_info(ioc, "port enable: complete from worker thread\n"));
+ break;
+ case MPT3SAS_TURN_ON_PFA_LED:
+ _scsih_turn_on_pfa_led(ioc, fw_event->device_handle);
+ break;
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ _scsih_sas_topology_change_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_device_status_change_event_debug(ioc,
+ (Mpi2EventDataSasDeviceStatusChange_t *)
+ fw_event->event_data);
+ break;
+ case MPI2_EVENT_SAS_DISCOVERY:
+ _scsih_sas_discovery_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
+ _scsih_sas_device_discovery_error_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ _scsih_sas_broadcast_primitive_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ _scsih_sas_enclosure_dev_status_change_event(ioc,
+ fw_event);
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ _scsih_sas_ir_config_change_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ _scsih_sas_ir_volume_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ _scsih_sas_ir_physical_disk_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ _scsih_sas_ir_operation_status_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE:
+ _scsih_pcie_device_status_change_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_PCIE_ENUMERATION:
+ _scsih_pcie_enumeration_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ _scsih_pcie_topology_change_event(ioc, fw_event);
+ break;
+ }
+out:
+ fw_event_work_put(fw_event);
+ ioc->current_event = NULL;
+}
+
+/**
+ * _firmware_event_work
+ * @work: The fw_event_work object
+ * Context: user.
+ *
+ * wrappers for the work thread handling firmware events
+ */
+
+static void
+_firmware_event_work(struct work_struct *work)
+{
+ struct fw_event_work *fw_event = container_of(work,
+ struct fw_event_work, work);
+
+ _mpt3sas_fw_work(fw_event->ioc, fw_event);
+}
+
+/**
+ * mpt3sas_scsih_event_callback - firmware event handler (called at ISR time)
+ * @ioc: per adapter object
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt.
+ *
+ * This function merely adds a new work task into ioc->firmware_event_thread.
+ * The tasks are worked from _firmware_event_work in user context.
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
+ u32 reply)
+{
+ struct fw_event_work *fw_event;
+ Mpi2EventNotificationReply_t *mpi_reply;
+ u16 event;
+ u16 sz;
+ Mpi26EventDataActiveCableExcept_t *ActiveCableEventData;
+
+ /* events turned off due to host reset */
+ if (ioc->pci_error_recovery)
+ return 1;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+
+ if (unlikely(!mpi_reply)) {
+ ioc_err(ioc, "mpi_reply not valid at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 1;
+ }
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ if (event != MPI2_EVENT_LOG_ENTRY_ADDED)
+ mpt3sas_trigger_event(ioc, event, 0);
+
+ switch (event) {
+ /* handle these */
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ {
+ Mpi2EventDataSasBroadcastPrimitive_t *baen_data =
+ (Mpi2EventDataSasBroadcastPrimitive_t *)
+ mpi_reply->EventData;
+
+ if (baen_data->Primitive !=
+ MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT)
+ return 1;
+
+ if (ioc->broadcast_aen_busy) {
+ ioc->broadcast_aen_pending++;
+ return 1;
+ } else
+ ioc->broadcast_aen_busy = 1;
+ break;
+ }
+
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ _scsih_check_topo_delete_events(ioc,
+ (Mpi2EventDataSasTopologyChangeList_t *)
+ mpi_reply->EventData);
+ /*
+ * No need to add the topology change list
+ * event to fw event work queue when
+ * diag reset is going on. Since during diag
+ * reset driver scan the devices by reading
+ * sas device page0's not by processing the
+ * events.
+ */
+ if (ioc->shost_recovery)
+ return 1;
+ break;
+ case MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ _scsih_check_pcie_topo_remove_events(ioc,
+ (Mpi26EventDataPCIeTopologyChangeList_t *)
+ mpi_reply->EventData);
+ if (ioc->shost_recovery)
+ return 1;
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ _scsih_check_ir_config_unhide_events(ioc,
+ (Mpi2EventDataIrConfigChangeList_t *)
+ mpi_reply->EventData);
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ _scsih_check_volume_delete_events(ioc,
+ (Mpi2EventDataIrVolume_t *)
+ mpi_reply->EventData);
+ break;
+ case MPI2_EVENT_LOG_ENTRY_ADDED:
+ {
+ Mpi2EventDataLogEntryAdded_t *log_entry;
+ u32 log_code;
+
+ if (!ioc->is_warpdrive)
+ break;
+
+ log_entry = (Mpi2EventDataLogEntryAdded_t *)
+ mpi_reply->EventData;
+ log_code = le32_to_cpu(*(__le32 *)log_entry->LogData);
+
+ if (le16_to_cpu(log_entry->LogEntryQualifier)
+ != MPT2_WARPDRIVE_LOGENTRY)
+ break;
+
+ switch (log_code) {
+ case MPT2_WARPDRIVE_LC_SSDT:
+ ioc_warn(ioc, "WarpDrive Warning: IO Throttling has occurred in the WarpDrive subsystem. Check WarpDrive documentation for additional details.\n");
+ break;
+ case MPT2_WARPDRIVE_LC_SSDLW:
+ ioc_warn(ioc, "WarpDrive Warning: Program/Erase Cycles for the WarpDrive subsystem in degraded range. Check WarpDrive documentation for additional details.\n");
+ break;
+ case MPT2_WARPDRIVE_LC_SSDLF:
+ ioc_err(ioc, "WarpDrive Fatal Error: There are no Program/Erase Cycles for the WarpDrive subsystem. The storage device will be in read-only mode. Check WarpDrive documentation for additional details.\n");
+ break;
+ case MPT2_WARPDRIVE_LC_BRMF:
+ ioc_err(ioc, "WarpDrive Fatal Error: The Backup Rail Monitor has failed on the WarpDrive subsystem. Check WarpDrive documentation for additional details.\n");
+ break;
+ }
+
+ break;
+ }
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ _scsih_sas_device_status_change_event(ioc,
+ (Mpi2EventDataSasDeviceStatusChange_t *)
+ mpi_reply->EventData);
+ break;
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ case MPI2_EVENT_SAS_DISCOVERY:
+ case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ case MPI2_EVENT_PCIE_ENUMERATION:
+ case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE:
+ break;
+
+ case MPI2_EVENT_TEMP_THRESHOLD:
+ _scsih_temp_threshold_events(ioc,
+ (Mpi2EventDataTemperature_t *)
+ mpi_reply->EventData);
+ break;
+ case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION:
+ ActiveCableEventData =
+ (Mpi26EventDataActiveCableExcept_t *) mpi_reply->EventData;
+ switch (ActiveCableEventData->ReasonCode) {
+ case MPI26_EVENT_ACTIVE_CABLE_INSUFFICIENT_POWER:
+ ioc_notice(ioc, "Currently an active cable with ReceptacleID %d\n",
+ ActiveCableEventData->ReceptacleID);
+ pr_notice("cannot be powered and devices connected\n");
+ pr_notice("to this active cable will not be seen\n");
+ pr_notice("This active cable requires %d mW of power\n",
+ le32_to_cpu(
+ ActiveCableEventData->ActiveCablePowerRequirement));
+ break;
+
+ case MPI26_EVENT_ACTIVE_CABLE_DEGRADED:
+ ioc_notice(ioc, "Currently a cable with ReceptacleID %d\n",
+ ActiveCableEventData->ReceptacleID);
+ pr_notice(
+ "is not running at optimal speed(12 Gb/s rate)\n");
+ break;
+ }
+
+ break;
+
+ default: /* ignore the rest */
+ return 1;
+ }
+
+ sz = le16_to_cpu(mpi_reply->EventDataLength) * 4;
+ fw_event = alloc_fw_event_work(sz);
+ if (!fw_event) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return 1;
+ }
+
+ memcpy(fw_event->event_data, mpi_reply->EventData, sz);
+ fw_event->ioc = ioc;
+ fw_event->VF_ID = mpi_reply->VF_ID;
+ fw_event->VP_ID = mpi_reply->VP_ID;
+ fw_event->event = event;
+ _scsih_fw_event_add(ioc, fw_event);
+ fw_event_work_put(fw_event);
+ return 1;
+}
+
+/**
+ * _scsih_expander_node_remove - removing expander device from list.
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ *
+ * Removing object and freeing associated memory from the
+ * ioc->sas_expander_list.
+ */
+static void
+_scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ struct _sas_port *mpt3sas_port, *next;
+ unsigned long flags;
+ int port_id;
+
+ /* remove sibling ports attached to this expander */
+ list_for_each_entry_safe(mpt3sas_port, next,
+ &sas_expander->sas_port_list, port_list) {
+ if (ioc->shost_recovery)
+ return;
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE)
+ mpt3sas_device_remove_by_sas_address(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_port->hba_port);
+ else if (mpt3sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mpt3sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE)
+ mpt3sas_expander_remove(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_port->hba_port);
+ }
+
+ port_id = sas_expander->port->port_id;
+
+ mpt3sas_transport_port_remove(ioc, sas_expander->sas_address,
+ sas_expander->sas_address_parent, sas_expander->port);
+
+ ioc_info(ioc,
+ "expander_remove: handle(0x%04x), sas_addr(0x%016llx), port:%d\n",
+ sas_expander->handle, (unsigned long long)
+ sas_expander->sas_address,
+ port_id);
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_del(&sas_expander->list);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ kfree(sas_expander->phy);
+ kfree(sas_expander);
+}
+
+/**
+ * _scsih_nvme_shutdown - NVMe shutdown notification
+ * @ioc: per adapter object
+ *
+ * Sending IoUnitControl request with shutdown operation code to alert IOC that
+ * the host system is shutting down so that IOC can issue NVMe shutdown to
+ * NVMe drives attached to it.
+ */
+static void
+_scsih_nvme_shutdown(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi26IoUnitControlRequest_t *mpi_request;
+ Mpi26IoUnitControlReply_t *mpi_reply;
+ u16 smid;
+
+ /* are there any NVMe devices ? */
+ if (list_empty(&ioc->pcie_device_list))
+ return;
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+
+ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: scsih_cmd in use\n", __func__);
+ goto out;
+ }
+
+ ioc->scsih_cmds.status = MPT3_CMD_PENDING;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ ioc_err(ioc,
+ "%s: failed obtaining a smid\n", __func__);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi26IoUnitControlRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_IO_UNIT_CONTROL;
+ mpi_request->Operation = MPI26_CTRL_OP_SHUTDOWN;
+
+ init_completion(&ioc->scsih_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ /* Wait for max_shutdown_latency seconds */
+ ioc_info(ioc,
+ "Io Unit Control shutdown (sending), Shutdown latency %d sec\n",
+ ioc->max_shutdown_latency);
+ wait_for_completion_timeout(&ioc->scsih_cmds.done,
+ ioc->max_shutdown_latency*HZ);
+
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ goto out;
+ }
+
+ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
+ mpi_reply = ioc->scsih_cmds.reply;
+ ioc_info(ioc, "Io Unit Control shutdown (complete):"
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+ }
+ out:
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+
+/**
+ * _scsih_ir_shutdown - IR shutdown notification
+ * @ioc: per adapter object
+ *
+ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
+ * the host system is shutting down.
+ */
+static void
+_scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2RaidActionRequest_t *mpi_request;
+ Mpi2RaidActionReply_t *mpi_reply;
+ u16 smid;
+
+ /* is IR firmware build loaded ? */
+ if (!ioc->ir_firmware)
+ return;
+
+ /* are there any volumes ? */
+ if (list_empty(&ioc->raid_device_list))
+ return;
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+
+ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: scsih_cmd in use\n", __func__);
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT3_CMD_PENDING;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
+
+ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
+ mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
+
+ if (!ioc->hide_ir_msg)
+ ioc_info(ioc, "IR shutdown (sending)\n");
+ init_completion(&ioc->scsih_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ goto out;
+ }
+
+ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
+ mpi_reply = ioc->scsih_cmds.reply;
+ if (!ioc->hide_ir_msg)
+ ioc_info(ioc, "IR shutdown (complete): ioc_status(0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+ }
+
+ out:
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+/**
+ * _scsih_get_shost_and_ioc - get shost and ioc
+ * and verify whether they are NULL or not
+ * @pdev: PCI device struct
+ * @shost: address of scsi host pointer
+ * @ioc: address of HBA adapter pointer
+ *
+ * Return zero if *shost and *ioc are not NULL otherwise return error number.
+ */
+static int
+_scsih_get_shost_and_ioc(struct pci_dev *pdev,
+ struct Scsi_Host **shost, struct MPT3SAS_ADAPTER **ioc)
+{
+ *shost = pci_get_drvdata(pdev);
+ if (*shost == NULL) {
+ dev_err(&pdev->dev, "pdev's driver data is null\n");
+ return -ENXIO;
+ }
+
+ *ioc = shost_priv(*shost);
+ if (*ioc == NULL) {
+ dev_err(&pdev->dev, "shost's private data is null\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+/**
+ * scsih_remove - detach and remove add host
+ * @pdev: PCI device struct
+ *
+ * Routine called when unloading the driver.
+ */
+static void scsih_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ struct _sas_port *mpt3sas_port, *next_port;
+ struct _raid_device *raid_device, *next;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _pcie_device *pcie_device, *pcienext;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+ Mpi2ConfigReply_t mpi_reply;
+ struct hba_port *port, *port_next;
+
+ if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc))
+ return;
+
+ ioc->remove_host = 1;
+
+ if (!pci_device_is_present(pdev)) {
+ mpt3sas_base_pause_mq_polling(ioc);
+ _scsih_flush_running_cmds(ioc);
+ }
+
+ _scsih_fw_event_cleanup_queue(ioc);
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ wq = ioc->firmware_event_thread;
+ ioc->firmware_event_thread = NULL;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+ /*
+ * Copy back the unmodified ioc page1. so that on next driver load,
+ * current modified changes on ioc page1 won't take effect.
+ */
+ if (ioc->is_aero_ioc)
+ mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply,
+ &ioc->ioc_pg1_copy);
+ /* release all the volumes */
+ _scsih_ir_shutdown(ioc);
+ mpt3sas_destroy_debugfs(ioc);
+ sas_remove_host(shost);
+ list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list,
+ list) {
+ if (raid_device->starget) {
+ sas_target_priv_data =
+ raid_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ scsi_remove_target(&raid_device->starget->dev);
+ }
+ ioc_info(ioc, "removing handle(0x%04x), wwid(0x%016llx)\n",
+ raid_device->handle, (u64)raid_device->wwid);
+ _scsih_raid_device_remove(ioc, raid_device);
+ }
+ list_for_each_entry_safe(pcie_device, pcienext, &ioc->pcie_device_list,
+ list) {
+ _scsih_pcie_device_remove_from_sml(ioc, pcie_device);
+ list_del_init(&pcie_device->list);
+ pcie_device_put(pcie_device);
+ }
+
+ /* free ports attached to the sas_host */
+ list_for_each_entry_safe(mpt3sas_port, next_port,
+ &ioc->sas_hba.sas_port_list, port_list) {
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE)
+ mpt3sas_device_remove_by_sas_address(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_port->hba_port);
+ else if (mpt3sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mpt3sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE)
+ mpt3sas_expander_remove(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_port->hba_port);
+ }
+
+ list_for_each_entry_safe(port, port_next,
+ &ioc->port_table_list, list) {
+ list_del(&port->list);
+ kfree(port);
+ }
+
+ /* free phys attached to the sas_host */
+ if (ioc->sas_hba.num_phys) {
+ kfree(ioc->sas_hba.phy);
+ ioc->sas_hba.phy = NULL;
+ ioc->sas_hba.num_phys = 0;
+ }
+
+ mpt3sas_base_detach(ioc);
+ mpt3sas_ctl_release(ioc);
+ spin_lock(&gioc_lock);
+ list_del(&ioc->list);
+ spin_unlock(&gioc_lock);
+ scsi_host_put(shost);
+}
+
+/**
+ * scsih_shutdown - routine call during system shutdown
+ * @pdev: PCI device struct
+ */
+static void
+scsih_shutdown(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+ Mpi2ConfigReply_t mpi_reply;
+
+ if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc))
+ return;
+
+ ioc->remove_host = 1;
+
+ if (!pci_device_is_present(pdev)) {
+ mpt3sas_base_pause_mq_polling(ioc);
+ _scsih_flush_running_cmds(ioc);
+ }
+
+ _scsih_fw_event_cleanup_queue(ioc);
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ wq = ioc->firmware_event_thread;
+ ioc->firmware_event_thread = NULL;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+ /*
+ * Copy back the unmodified ioc page1 so that on next driver load,
+ * current modified changes on ioc page1 won't take effect.
+ */
+ if (ioc->is_aero_ioc)
+ mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply,
+ &ioc->ioc_pg1_copy);
+
+ _scsih_ir_shutdown(ioc);
+ _scsih_nvme_shutdown(ioc);
+ mpt3sas_base_mask_interrupts(ioc);
+ mpt3sas_base_stop_watchdog(ioc);
+ ioc->shost_recovery = 1;
+ mpt3sas_base_make_ioc_ready(ioc, SOFT_RESET);
+ ioc->shost_recovery = 0;
+ mpt3sas_base_free_irq(ioc);
+ mpt3sas_base_disable_msix(ioc);
+}
+
+
+/**
+ * _scsih_probe_boot_devices - reports 1st device
+ * @ioc: per adapter object
+ *
+ * If specified in bios page 2, this routine reports the 1st
+ * device scsi-ml or sas transport for persistent boot device
+ * purposes. Please refer to function _scsih_determine_boot_device()
+ */
+static void
+_scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 channel;
+ void *device;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ struct _pcie_device *pcie_device;
+ u16 handle;
+ u64 sas_address_parent;
+ u64 sas_address;
+ unsigned long flags;
+ int rc;
+ int tid;
+ struct hba_port *port;
+
+ /* no Bios, return immediately */
+ if (!ioc->bios_pg3.BiosVersion)
+ return;
+
+ device = NULL;
+ if (ioc->req_boot_device.device) {
+ device = ioc->req_boot_device.device;
+ channel = ioc->req_boot_device.channel;
+ } else if (ioc->req_alt_boot_device.device) {
+ device = ioc->req_alt_boot_device.device;
+ channel = ioc->req_alt_boot_device.channel;
+ } else if (ioc->current_boot_device.device) {
+ device = ioc->current_boot_device.device;
+ channel = ioc->current_boot_device.channel;
+ }
+
+ if (!device)
+ return;
+
+ if (channel == RAID_CHANNEL) {
+ raid_device = device;
+ /*
+ * If this boot vd is already registered with SML then
+ * no need to register it again as part of device scanning
+ * after diag reset during driver load operation.
+ */
+ if (raid_device->starget)
+ return;
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ } else if (channel == PCIE_CHANNEL) {
+ pcie_device = device;
+ /*
+ * If this boot NVMe device is already registered with SML then
+ * no need to register it again as part of device scanning
+ * after diag reset during driver load operation.
+ */
+ if (pcie_device->starget)
+ return;
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ tid = pcie_device->id;
+ list_move_tail(&pcie_device->list, &ioc->pcie_device_list);
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+ rc = scsi_add_device(ioc->shost, PCIE_CHANNEL, tid, 0);
+ if (rc)
+ _scsih_pcie_device_remove(ioc, pcie_device);
+ } else {
+ sas_device = device;
+ /*
+ * If this boot sas/sata device is already registered with SML
+ * then no need to register it again as part of device scanning
+ * after diag reset during driver load operation.
+ */
+ if (sas_device->starget)
+ return;
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ handle = sas_device->handle;
+ sas_address_parent = sas_device->sas_address_parent;
+ sas_address = sas_device->sas_address;
+ port = sas_device->port;
+ list_move_tail(&sas_device->list, &ioc->sas_device_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (ioc->hide_drives)
+ return;
+
+ if (!port)
+ return;
+
+ if (!mpt3sas_transport_port_add(ioc, handle,
+ sas_address_parent, port)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ if (!ioc->is_driver_loading) {
+ mpt3sas_transport_port_remove(ioc,
+ sas_address,
+ sas_address_parent, port);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+ }
+ }
+}
+
+/**
+ * _scsih_probe_raid - reporting raid volumes to scsi-ml
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _raid_device *raid_device, *raid_next;
+ int rc;
+
+ list_for_each_entry_safe(raid_device, raid_next,
+ &ioc->raid_device_list, list) {
+ if (raid_device->starget)
+ continue;
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ }
+}
+
+static struct _sas_device *get_next_sas_device(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ if (!list_empty(&ioc->sas_device_init_list)) {
+ sas_device = list_first_entry(&ioc->sas_device_init_list,
+ struct _sas_device, list);
+ sas_device_get(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ return sas_device;
+}
+
+static void sas_device_make_active(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+
+ /*
+ * Since we dropped the lock during the call to port_add(), we need to
+ * be careful here that somebody else didn't move or delete this item
+ * while we were busy with other things.
+ *
+ * If it was on the list, we need a put() for the reference the list
+ * had. Either way, we need a get() for the destination list.
+ */
+ if (!list_empty(&sas_device->list)) {
+ list_del_init(&sas_device->list);
+ sas_device_put(sas_device);
+ }
+
+ sas_device_get(sas_device);
+ list_add_tail(&sas_device->list, &ioc->sas_device_list);
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_probe_sas - reporting sas devices to sas transport
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device;
+
+ if (ioc->hide_drives)
+ return;
+
+ while ((sas_device = get_next_sas_device(ioc))) {
+ if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent, sas_device->port)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ sas_device_put(sas_device);
+ continue;
+ } else if (!sas_device->starget) {
+ /*
+ * When asyn scanning is enabled, its not possible to
+ * remove devices while scanning is turned on due to an
+ * oops in scsi_sysfs_add_sdev()->add_device()->
+ * sysfs_addrm_start()
+ */
+ if (!ioc->is_driver_loading) {
+ mpt3sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent,
+ sas_device->port);
+ _scsih_sas_device_remove(ioc, sas_device);
+ sas_device_put(sas_device);
+ continue;
+ }
+ }
+ sas_device_make_active(ioc, sas_device);
+ sas_device_put(sas_device);
+ }
+}
+
+/**
+ * get_next_pcie_device - Get the next pcie device
+ * @ioc: per adapter object
+ *
+ * Get the next pcie device from pcie_device_init_list list.
+ *
+ * Return: pcie device structure if pcie_device_init_list list is not empty
+ * otherwise returns NULL
+ */
+static struct _pcie_device *get_next_pcie_device(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _pcie_device *pcie_device = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+ if (!list_empty(&ioc->pcie_device_init_list)) {
+ pcie_device = list_first_entry(&ioc->pcie_device_init_list,
+ struct _pcie_device, list);
+ pcie_device_get(pcie_device);
+ }
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+
+ return pcie_device;
+}
+
+/**
+ * pcie_device_make_active - Add pcie device to pcie_device_list list
+ * @ioc: per adapter object
+ * @pcie_device: pcie device object
+ *
+ * Add the pcie device which has registered with SCSI Transport Later to
+ * pcie_device_list list
+ */
+static void pcie_device_make_active(struct MPT3SAS_ADAPTER *ioc,
+ struct _pcie_device *pcie_device)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->pcie_device_lock, flags);
+
+ if (!list_empty(&pcie_device->list)) {
+ list_del_init(&pcie_device->list);
+ pcie_device_put(pcie_device);
+ }
+ pcie_device_get(pcie_device);
+ list_add_tail(&pcie_device->list, &ioc->pcie_device_list);
+
+ spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
+}
+
+/**
+ * _scsih_probe_pcie - reporting PCIe devices to scsi-ml
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_pcie(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _pcie_device *pcie_device;
+ int rc;
+
+ /* PCIe Device List */
+ while ((pcie_device = get_next_pcie_device(ioc))) {
+ if (pcie_device->starget) {
+ pcie_device_put(pcie_device);
+ continue;
+ }
+ if (pcie_device->access_status ==
+ MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) {
+ pcie_device_make_active(ioc, pcie_device);
+ pcie_device_put(pcie_device);
+ continue;
+ }
+ rc = scsi_add_device(ioc->shost, PCIE_CHANNEL,
+ pcie_device->id, 0);
+ if (rc) {
+ _scsih_pcie_device_remove(ioc, pcie_device);
+ pcie_device_put(pcie_device);
+ continue;
+ } else if (!pcie_device->starget) {
+ /*
+ * When async scanning is enabled, its not possible to
+ * remove devices while scanning is turned on due to an
+ * oops in scsi_sysfs_add_sdev()->add_device()->
+ * sysfs_addrm_start()
+ */
+ if (!ioc->is_driver_loading) {
+ /* TODO-- Need to find out whether this condition will
+ * occur or not
+ */
+ _scsih_pcie_device_remove(ioc, pcie_device);
+ pcie_device_put(pcie_device);
+ continue;
+ }
+ }
+ pcie_device_make_active(ioc, pcie_device);
+ pcie_device_put(pcie_device);
+ }
+}
+
+/**
+ * _scsih_probe_devices - probing for devices
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ u16 volume_mapping_flags;
+
+ if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR))
+ return; /* return when IOC doesn't support initiator mode */
+
+ _scsih_probe_boot_devices(ioc);
+
+ if (ioc->ir_firmware) {
+ volume_mapping_flags =
+ le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+ if (volume_mapping_flags ==
+ MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {
+ _scsih_probe_raid(ioc);
+ _scsih_probe_sas(ioc);
+ } else {
+ _scsih_probe_sas(ioc);
+ _scsih_probe_raid(ioc);
+ }
+ } else {
+ _scsih_probe_sas(ioc);
+ _scsih_probe_pcie(ioc);
+ }
+}
+
+/**
+ * scsih_scan_start - scsi lld callback for .scan_start
+ * @shost: SCSI host pointer
+ *
+ * The shost has the ability to discover targets on its own instead
+ * of scanning the entire bus. In our implemention, we will kick off
+ * firmware discovery.
+ */
+static void
+scsih_scan_start(struct Scsi_Host *shost)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int rc;
+ if (diag_buffer_enable != -1 && diag_buffer_enable != 0)
+ mpt3sas_enable_diag_buffer(ioc, diag_buffer_enable);
+ else if (ioc->manu_pg11.HostTraceBufferMaxSizeKB != 0)
+ mpt3sas_enable_diag_buffer(ioc, 1);
+
+ if (disable_discovery > 0)
+ return;
+
+ ioc->start_scan = 1;
+ rc = mpt3sas_port_enable(ioc);
+
+ if (rc != 0)
+ ioc_info(ioc, "port enable: FAILED\n");
+}
+
+/**
+ * _scsih_complete_devices_scanning - add the devices to sml and
+ * complete ioc initialization.
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void _scsih_complete_devices_scanning(struct MPT3SAS_ADAPTER *ioc)
+{
+
+ if (ioc->wait_for_discovery_to_complete) {
+ ioc->wait_for_discovery_to_complete = 0;
+ _scsih_probe_devices(ioc);
+ }
+
+ mpt3sas_base_start_watchdog(ioc);
+ ioc->is_driver_loading = 0;
+}
+
+/**
+ * scsih_scan_finished - scsi lld callback for .scan_finished
+ * @shost: SCSI host pointer
+ * @time: elapsed time of the scan in jiffies
+ *
+ * This function will be called periodicallyn until it returns 1 with the
+ * scsi_host and the elapsed time of the scan in jiffies. In our implemention,
+ * we wait for firmware discovery to complete, then return 1.
+ */
+static int
+scsih_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ u32 ioc_state;
+ int issue_hard_reset = 0;
+
+ if (disable_discovery > 0) {
+ ioc->is_driver_loading = 0;
+ ioc->wait_for_discovery_to_complete = 0;
+ return 1;
+ }
+
+ if (time >= (300 * HZ)) {
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+ ioc_info(ioc, "port enable: FAILED with timeout (timeout=300s)\n");
+ ioc->is_driver_loading = 0;
+ return 1;
+ }
+
+ if (ioc->start_scan) {
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_print_fault_code(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ issue_hard_reset = 1;
+ goto out;
+ } else if ((ioc_state & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_COREDUMP) {
+ mpt3sas_base_coredump_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ mpt3sas_base_wait_for_coredump_completion(ioc, __func__);
+ issue_hard_reset = 1;
+ goto out;
+ }
+ return 0;
+ }
+
+ if (ioc->port_enable_cmds.status & MPT3_CMD_RESET) {
+ ioc_info(ioc,
+ "port enable: aborted due to diag reset\n");
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+ }
+ if (ioc->start_scan_failed) {
+ ioc_info(ioc, "port enable: FAILED with (ioc_status=0x%08x)\n",
+ ioc->start_scan_failed);
+ ioc->is_driver_loading = 0;
+ ioc->wait_for_discovery_to_complete = 0;
+ ioc->remove_host = 1;
+ return 1;
+ }
+
+ ioc_info(ioc, "port enable: SUCCESS\n");
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+ _scsih_complete_devices_scanning(ioc);
+
+out:
+ if (issue_hard_reset) {
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+ if (mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET))
+ ioc->is_driver_loading = 0;
+ }
+ return 1;
+}
+
+/**
+ * scsih_map_queues - map reply queues with request queues
+ * @shost: SCSI host pointer
+ */
+static void scsih_map_queues(struct Scsi_Host *shost)
+{
+ struct MPT3SAS_ADAPTER *ioc =
+ (struct MPT3SAS_ADAPTER *)shost->hostdata;
+ struct blk_mq_queue_map *map;
+ int i, qoff, offset;
+ int nr_msix_vectors = ioc->iopoll_q_start_index;
+ int iopoll_q_count = ioc->reply_queue_count - nr_msix_vectors;
+
+ if (shost->nr_hw_queues == 1)
+ return;
+
+ for (i = 0, qoff = 0; i < shost->nr_maps; i++) {
+ map = &shost->tag_set.map[i];
+ map->nr_queues = 0;
+ offset = 0;
+ if (i == HCTX_TYPE_DEFAULT) {
+ map->nr_queues =
+ nr_msix_vectors - ioc->high_iops_queues;
+ offset = ioc->high_iops_queues;
+ } else if (i == HCTX_TYPE_POLL)
+ map->nr_queues = iopoll_q_count;
+
+ if (!map->nr_queues)
+ BUG_ON(i == HCTX_TYPE_DEFAULT);
+
+ /*
+ * The poll queue(s) doesn't have an IRQ (and hence IRQ
+ * affinity), so use the regular blk-mq cpu mapping
+ */
+ map->queue_offset = qoff;
+ if (i != HCTX_TYPE_POLL)
+ blk_mq_map_hw_queues(map, &ioc->pdev->dev, offset);
+ else
+ blk_mq_map_queues(map);
+
+ qoff += map->nr_queues;
+ }
+}
+
+/* shost template for SAS 2.0 HBA devices */
+static const struct scsi_host_template mpt2sas_driver_template = {
+ .module = THIS_MODULE,
+ .name = "Fusion MPT SAS Host",
+ .proc_name = MPT2SAS_DRIVER_NAME,
+ .queuecommand = scsih_qcmd,
+ .target_alloc = scsih_target_alloc,
+ .sdev_init = scsih_sdev_init,
+ .sdev_configure = scsih_sdev_configure,
+ .target_destroy = scsih_target_destroy,
+ .sdev_destroy = scsih_sdev_destroy,
+ .scan_finished = scsih_scan_finished,
+ .scan_start = scsih_scan_start,
+ .change_queue_depth = scsih_change_queue_depth,
+ .eh_abort_handler = scsih_abort,
+ .eh_device_reset_handler = scsih_dev_reset,
+ .eh_target_reset_handler = scsih_target_reset,
+ .eh_host_reset_handler = scsih_host_reset,
+ .bios_param = scsih_bios_param,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = MPT2SAS_SG_DEPTH,
+ .max_sectors = 32767,
+ .cmd_per_lun = 7,
+ .shost_groups = mpt3sas_host_groups,
+ .sdev_groups = mpt3sas_dev_groups,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct scsiio_tracker),
+};
+
+/* raid transport support for SAS 2.0 HBA devices */
+static struct raid_function_template mpt2sas_raid_functions = {
+ .cookie = &mpt2sas_driver_template,
+ .is_raid = scsih_is_raid,
+ .get_resync = scsih_get_resync,
+ .get_state = scsih_get_state,
+};
+
+/* shost template for SAS 3.0 HBA devices */
+static const struct scsi_host_template mpt3sas_driver_template = {
+ .module = THIS_MODULE,
+ .name = "Fusion MPT SAS Host",
+ .proc_name = MPT3SAS_DRIVER_NAME,
+ .queuecommand = scsih_qcmd,
+ .target_alloc = scsih_target_alloc,
+ .sdev_init = scsih_sdev_init,
+ .sdev_configure = scsih_sdev_configure,
+ .target_destroy = scsih_target_destroy,
+ .sdev_destroy = scsih_sdev_destroy,
+ .scan_finished = scsih_scan_finished,
+ .scan_start = scsih_scan_start,
+ .change_queue_depth = scsih_change_queue_depth,
+ .eh_abort_handler = scsih_abort,
+ .eh_device_reset_handler = scsih_dev_reset,
+ .eh_target_reset_handler = scsih_target_reset,
+ .eh_host_reset_handler = scsih_host_reset,
+ .bios_param = scsih_bios_param,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = MPT3SAS_SG_DEPTH,
+ .max_sectors = 32767,
+ .max_segment_size = 0xffffffff,
+ .cmd_per_lun = 128,
+ .shost_groups = mpt3sas_host_groups,
+ .sdev_groups = mpt3sas_dev_groups,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct scsiio_tracker),
+ .map_queues = scsih_map_queues,
+ .mq_poll = mpt3sas_blk_mq_poll,
+};
+
+/* raid transport support for SAS 3.0 HBA devices */
+static struct raid_function_template mpt3sas_raid_functions = {
+ .cookie = &mpt3sas_driver_template,
+ .is_raid = scsih_is_raid,
+ .get_resync = scsih_get_resync,
+ .get_state = scsih_get_state,
+};
+
+/**
+ * _scsih_determine_hba_mpi_version - determine in which MPI version class
+ * this device belongs to.
+ * @pdev: PCI device struct
+ *
+ * return MPI2_VERSION for SAS 2.0 HBA devices,
+ * MPI25_VERSION for SAS 3.0 HBA devices, and
+ * MPI26 VERSION for Cutlass & Invader SAS 3.0 HBA devices
+ */
+static u16
+_scsih_determine_hba_mpi_version(struct pci_dev *pdev)
+{
+
+ switch (pdev->device) {
+ case MPI2_MFGPAGE_DEVID_SSS6200:
+ case MPI2_MFGPAGE_DEVID_SAS2004:
+ case MPI2_MFGPAGE_DEVID_SAS2008:
+ case MPI2_MFGPAGE_DEVID_SAS2108_1:
+ case MPI2_MFGPAGE_DEVID_SAS2108_2:
+ case MPI2_MFGPAGE_DEVID_SAS2108_3:
+ case MPI2_MFGPAGE_DEVID_SAS2116_1:
+ case MPI2_MFGPAGE_DEVID_SAS2116_2:
+ case MPI2_MFGPAGE_DEVID_SAS2208_1:
+ case MPI2_MFGPAGE_DEVID_SAS2208_2:
+ case MPI2_MFGPAGE_DEVID_SAS2208_3:
+ case MPI2_MFGPAGE_DEVID_SAS2208_4:
+ case MPI2_MFGPAGE_DEVID_SAS2208_5:
+ case MPI2_MFGPAGE_DEVID_SAS2208_6:
+ case MPI2_MFGPAGE_DEVID_SAS2308_1:
+ case MPI2_MFGPAGE_DEVID_SAS2308_2:
+ case MPI2_MFGPAGE_DEVID_SAS2308_3:
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP:
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1:
+ return MPI2_VERSION;
+ case MPI25_MFGPAGE_DEVID_SAS3004:
+ case MPI25_MFGPAGE_DEVID_SAS3008:
+ case MPI25_MFGPAGE_DEVID_SAS3108_1:
+ case MPI25_MFGPAGE_DEVID_SAS3108_2:
+ case MPI25_MFGPAGE_DEVID_SAS3108_5:
+ case MPI25_MFGPAGE_DEVID_SAS3108_6:
+ return MPI25_VERSION;
+ case MPI26_MFGPAGE_DEVID_SAS3216:
+ case MPI26_MFGPAGE_DEVID_SAS3224:
+ case MPI26_MFGPAGE_DEVID_SAS3316_1:
+ case MPI26_MFGPAGE_DEVID_SAS3316_2:
+ case MPI26_MFGPAGE_DEVID_SAS3316_3:
+ case MPI26_MFGPAGE_DEVID_SAS3316_4:
+ case MPI26_MFGPAGE_DEVID_SAS3324_1:
+ case MPI26_MFGPAGE_DEVID_SAS3324_2:
+ case MPI26_MFGPAGE_DEVID_SAS3324_3:
+ case MPI26_MFGPAGE_DEVID_SAS3324_4:
+ case MPI26_MFGPAGE_DEVID_SAS3508:
+ case MPI26_MFGPAGE_DEVID_SAS3508_1:
+ case MPI26_MFGPAGE_DEVID_SAS3408:
+ case MPI26_MFGPAGE_DEVID_SAS3516:
+ case MPI26_MFGPAGE_DEVID_SAS3516_1:
+ case MPI26_MFGPAGE_DEVID_SAS3416:
+ case MPI26_MFGPAGE_DEVID_SAS3616:
+ case MPI26_ATLAS_PCIe_SWITCH_DEVID:
+ case MPI26_MFGPAGE_DEVID_CFG_SEC_3916:
+ case MPI26_MFGPAGE_DEVID_HARD_SEC_3916:
+ case MPI26_MFGPAGE_DEVID_CFG_SEC_3816:
+ case MPI26_MFGPAGE_DEVID_HARD_SEC_3816:
+ case MPI26_MFGPAGE_DEVID_INVALID0_3916:
+ case MPI26_MFGPAGE_DEVID_INVALID1_3916:
+ case MPI26_MFGPAGE_DEVID_INVALID0_3816:
+ case MPI26_MFGPAGE_DEVID_INVALID1_3816:
+ return MPI26_VERSION;
+ }
+ return 0;
+}
+
+/**
+ * _scsih_probe - attach and add scsi host
+ * @pdev: PCI device struct
+ * @id: pci device id
+ *
+ * Return: 0 success, anything else error.
+ */
+static int
+_scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+ struct Scsi_Host *shost = NULL;
+ int rv;
+ u16 hba_mpi_version;
+ int iopoll_q_count = 0;
+
+ /* Determine in which MPI version class this pci device belongs */
+ hba_mpi_version = _scsih_determine_hba_mpi_version(pdev);
+ if (hba_mpi_version == 0)
+ return -ENODEV;
+
+ /* Enumerate only SAS 2.0 HBA's if hbas_to_enumerate is one,
+ * for other generation HBA's return with -ENODEV
+ */
+ if ((hbas_to_enumerate == 1) && (hba_mpi_version != MPI2_VERSION))
+ return -ENODEV;
+
+ /* Enumerate only SAS 3.0 HBA's if hbas_to_enumerate is two,
+ * for other generation HBA's return with -ENODEV
+ */
+ if ((hbas_to_enumerate == 2) && (!(hba_mpi_version == MPI25_VERSION
+ || hba_mpi_version == MPI26_VERSION)))
+ return -ENODEV;
+
+ switch (hba_mpi_version) {
+ case MPI2_VERSION:
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
+ /* Use mpt2sas driver host template for SAS 2.0 HBA's */
+ shost = scsi_host_alloc(&mpt2sas_driver_template,
+ sizeof(struct MPT3SAS_ADAPTER));
+ if (!shost)
+ return -ENODEV;
+ ioc = shost_priv(shost);
+ memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER));
+ ioc->hba_mpi_version_belonged = hba_mpi_version;
+ ioc->id = mpt2_ids++;
+ sprintf(ioc->driver_name, "%s", MPT2SAS_DRIVER_NAME);
+ switch (pdev->device) {
+ case MPI2_MFGPAGE_DEVID_SSS6200:
+ ioc->is_warpdrive = 1;
+ ioc->hide_ir_msg = 1;
+ break;
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP:
+ case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1:
+ ioc->is_mcpu_endpoint = 1;
+ break;
+ default:
+ ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS;
+ break;
+ }
+
+ if (multipath_on_hba == -1 || multipath_on_hba == 0)
+ ioc->multipath_on_hba = 0;
+ else
+ ioc->multipath_on_hba = 1;
+
+ break;
+ case MPI25_VERSION:
+ case MPI26_VERSION:
+ /* Use mpt3sas driver host template for SAS 3.0 HBA's */
+ shost = scsi_host_alloc(&mpt3sas_driver_template,
+ sizeof(struct MPT3SAS_ADAPTER));
+ if (!shost)
+ return -ENODEV;
+ ioc = shost_priv(shost);
+ memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER));
+ ioc->hba_mpi_version_belonged = hba_mpi_version;
+ ioc->id = mpt3_ids++;
+ sprintf(ioc->driver_name, "%s", MPT3SAS_DRIVER_NAME);
+ switch (pdev->device) {
+ case MPI26_MFGPAGE_DEVID_SAS3508:
+ case MPI26_MFGPAGE_DEVID_SAS3508_1:
+ case MPI26_MFGPAGE_DEVID_SAS3408:
+ case MPI26_MFGPAGE_DEVID_SAS3516:
+ case MPI26_MFGPAGE_DEVID_SAS3516_1:
+ case MPI26_MFGPAGE_DEVID_SAS3416:
+ case MPI26_MFGPAGE_DEVID_SAS3616:
+ case MPI26_ATLAS_PCIe_SWITCH_DEVID:
+ ioc->is_gen35_ioc = 1;
+ break;
+ case MPI26_MFGPAGE_DEVID_INVALID0_3816:
+ case MPI26_MFGPAGE_DEVID_INVALID0_3916:
+ dev_err(&pdev->dev,
+ "HBA with DeviceId 0x%04x, sub VendorId 0x%04x, sub DeviceId 0x%04x is Invalid",
+ pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ return 1;
+ case MPI26_MFGPAGE_DEVID_INVALID1_3816:
+ case MPI26_MFGPAGE_DEVID_INVALID1_3916:
+ dev_err(&pdev->dev,
+ "HBA with DeviceId 0x%04x, sub VendorId 0x%04x, sub DeviceId 0x%04x is Tampered",
+ pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ return 1;
+ case MPI26_MFGPAGE_DEVID_CFG_SEC_3816:
+ case MPI26_MFGPAGE_DEVID_CFG_SEC_3916:
+ dev_info(&pdev->dev,
+ "HBA is in Configurable Secure mode\n");
+ fallthrough;
+ case MPI26_MFGPAGE_DEVID_HARD_SEC_3816:
+ case MPI26_MFGPAGE_DEVID_HARD_SEC_3916:
+ ioc->is_aero_ioc = ioc->is_gen35_ioc = 1;
+ break;
+ default:
+ ioc->is_gen35_ioc = ioc->is_aero_ioc = 0;
+ }
+ if ((ioc->hba_mpi_version_belonged == MPI25_VERSION &&
+ pdev->revision >= SAS3_PCI_DEVICE_C0_REVISION) ||
+ (ioc->hba_mpi_version_belonged == MPI26_VERSION)) {
+ ioc->combined_reply_queue = 1;
+ if (ioc->is_gen35_ioc)
+ ioc->combined_reply_index_count =
+ MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G35;
+ else
+ ioc->combined_reply_index_count =
+ MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT_G3;
+ }
+
+ switch (ioc->is_gen35_ioc) {
+ case 0:
+ if (multipath_on_hba == -1 || multipath_on_hba == 0)
+ ioc->multipath_on_hba = 0;
+ else
+ ioc->multipath_on_hba = 1;
+ break;
+ case 1:
+ if (multipath_on_hba == -1 || multipath_on_hba > 0)
+ ioc->multipath_on_hba = 1;
+ else
+ ioc->multipath_on_hba = 0;
+ break;
+ default:
+ break;
+ }
+
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ INIT_LIST_HEAD(&ioc->list);
+ spin_lock(&gioc_lock);
+ list_add_tail(&ioc->list, &mpt3sas_ioc_list);
+ spin_unlock(&gioc_lock);
+ ioc->shost = shost;
+ ioc->pdev = pdev;
+ ioc->scsi_io_cb_idx = scsi_io_cb_idx;
+ ioc->tm_cb_idx = tm_cb_idx;
+ ioc->ctl_cb_idx = ctl_cb_idx;
+ ioc->base_cb_idx = base_cb_idx;
+ ioc->port_enable_cb_idx = port_enable_cb_idx;
+ ioc->transport_cb_idx = transport_cb_idx;
+ ioc->scsih_cb_idx = scsih_cb_idx;
+ ioc->config_cb_idx = config_cb_idx;
+ ioc->tm_tr_cb_idx = tm_tr_cb_idx;
+ ioc->tm_tr_volume_cb_idx = tm_tr_volume_cb_idx;
+ ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx;
+ ioc->logging_level = logging_level;
+ ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds;
+ /* Host waits for minimum of six seconds */
+ ioc->max_shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT;
+ /*
+ * Enable MEMORY MOVE support flag.
+ */
+ ioc->drv_support_bitmap |= MPT_DRV_SUPPORT_BITMAP_MEMMOVE;
+ /* Enable ADDITIONAL QUERY support flag. */
+ ioc->drv_support_bitmap |= MPT_DRV_SUPPORT_BITMAP_ADDNLQUERY;
+
+ ioc->enable_sdev_max_qd = enable_sdev_max_qd;
+
+ /* misc semaphores and spin locks */
+ mutex_init(&ioc->reset_in_progress_mutex);
+ mutex_init(&ioc->hostdiag_unlock_mutex);
+ /* initializing pci_access_mutex lock */
+ mutex_init(&ioc->pci_access_mutex);
+ spin_lock_init(&ioc->ioc_reset_in_progress_lock);
+ spin_lock_init(&ioc->scsi_lookup_lock);
+ spin_lock_init(&ioc->sas_device_lock);
+ spin_lock_init(&ioc->sas_node_lock);
+ spin_lock_init(&ioc->fw_event_lock);
+ spin_lock_init(&ioc->raid_device_lock);
+ spin_lock_init(&ioc->pcie_device_lock);
+ spin_lock_init(&ioc->diag_trigger_lock);
+
+ INIT_LIST_HEAD(&ioc->sas_device_list);
+ INIT_LIST_HEAD(&ioc->sas_device_init_list);
+ INIT_LIST_HEAD(&ioc->sas_expander_list);
+ INIT_LIST_HEAD(&ioc->enclosure_list);
+ INIT_LIST_HEAD(&ioc->pcie_device_list);
+ INIT_LIST_HEAD(&ioc->pcie_device_init_list);
+ INIT_LIST_HEAD(&ioc->fw_event_list);
+ INIT_LIST_HEAD(&ioc->raid_device_list);
+ INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list);
+ INIT_LIST_HEAD(&ioc->delayed_tr_list);
+ INIT_LIST_HEAD(&ioc->delayed_sc_list);
+ INIT_LIST_HEAD(&ioc->delayed_event_ack_list);
+ INIT_LIST_HEAD(&ioc->delayed_tr_volume_list);
+ INIT_LIST_HEAD(&ioc->reply_queue_list);
+ INIT_LIST_HEAD(&ioc->port_table_list);
+
+ sprintf(ioc->name, "%s_cm%d", ioc->driver_name, ioc->id);
+
+ /* init shost parameters */
+ shost->max_cmd_len = 32;
+ shost->max_lun = max_lun;
+ shost->transportt = mpt3sas_transport_template;
+ shost->unique_id = ioc->id;
+
+ if (ioc->is_mcpu_endpoint) {
+ /* mCPU MPI support 64K max IO */
+ shost->max_sectors = 128;
+ ioc_info(ioc, "The max_sectors value is set to %d\n",
+ shost->max_sectors);
+ } else {
+ if (max_sectors != 0xFFFF) {
+ if (max_sectors < 64) {
+ shost->max_sectors = 64;
+ ioc_warn(ioc, "Invalid value %d passed for max_sectors, range is 64 to 32767. Assigning value of 64.\n",
+ max_sectors);
+ } else if (max_sectors > 32767) {
+ shost->max_sectors = 32767;
+ ioc_warn(ioc, "Invalid value %d passed for max_sectors, range is 64 to 32767.Assigning default value of 32767.\n",
+ max_sectors);
+ } else {
+ shost->max_sectors = max_sectors & 0xFFFE;
+ ioc_info(ioc, "The max_sectors value is set to %d\n",
+ shost->max_sectors);
+ }
+ }
+ }
+ /* register EEDP capabilities with SCSI layer */
+ if (prot_mask >= 0)
+ scsi_host_set_prot(shost, (prot_mask & 0x07));
+ else
+ scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION);
+
+ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+
+ /* event thread */
+ ioc->firmware_event_thread = alloc_ordered_workqueue(
+ "fw_event_%s%d", 0, ioc->driver_name, ioc->id);
+ if (!ioc->firmware_event_thread) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rv = -ENODEV;
+ goto out_thread_fail;
+ }
+
+ shost->host_tagset = 0;
+
+ if (ioc->is_gen35_ioc && host_tagset_enable)
+ shost->host_tagset = 1;
+
+ ioc->is_driver_loading = 1;
+ if ((mpt3sas_base_attach(ioc))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rv = -ENODEV;
+ goto out_attach_fail;
+ }
+
+ if (ioc->is_warpdrive) {
+ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS)
+ ioc->hide_drives = 0;
+ else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS)
+ ioc->hide_drives = 1;
+ else {
+ if (mpt3sas_get_num_volumes(ioc))
+ ioc->hide_drives = 1;
+ else
+ ioc->hide_drives = 0;
+ }
+ } else
+ ioc->hide_drives = 0;
+
+ shost->nr_hw_queues = 1;
+
+ if (shost->host_tagset) {
+ shost->nr_hw_queues =
+ ioc->reply_queue_count - ioc->high_iops_queues;
+
+ iopoll_q_count =
+ ioc->reply_queue_count - ioc->iopoll_q_start_index;
+
+ shost->nr_maps = iopoll_q_count ? 3 : 1;
+
+ dev_info(&ioc->pdev->dev,
+ "Max SCSIIO MPT commands: %d shared with nr_hw_queues = %d\n",
+ shost->can_queue, shost->nr_hw_queues);
+ }
+
+ rv = scsi_add_host(shost, &pdev->dev);
+ if (rv) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_add_shost_fail;
+ }
+
+ scsi_scan_host(shost);
+ mpt3sas_setup_debugfs(ioc);
+ return 0;
+out_add_shost_fail:
+ mpt3sas_base_detach(ioc);
+ out_attach_fail:
+ destroy_workqueue(ioc->firmware_event_thread);
+ out_thread_fail:
+ spin_lock(&gioc_lock);
+ list_del(&ioc->list);
+ spin_unlock(&gioc_lock);
+ scsi_host_put(shost);
+ return rv;
+}
+
+/**
+ * scsih_suspend - power management suspend main entry point
+ * @dev: Device struct
+ *
+ * Return: 0 success, anything else error.
+ */
+static int __maybe_unused
+scsih_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ int rc;
+
+ rc = _scsih_get_shost_and_ioc(pdev, &shost, &ioc);
+ if (rc)
+ return rc;
+
+ mpt3sas_base_stop_watchdog(ioc);
+ scsi_block_requests(shost);
+ _scsih_nvme_shutdown(ioc);
+ ioc_info(ioc, "pdev=0x%p, slot=%s, entering operating state\n",
+ pdev, pci_name(pdev));
+
+ mpt3sas_base_free_resources(ioc);
+ return 0;
+}
+
+/**
+ * scsih_resume - power management resume main entry point
+ * @dev: Device struct
+ *
+ * Return: 0 success, anything else error.
+ */
+static int __maybe_unused
+scsih_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ pci_power_t device_state = pdev->current_state;
+ int r;
+
+ r = _scsih_get_shost_and_ioc(pdev, &shost, &ioc);
+ if (r)
+ return r;
+
+ ioc_info(ioc, "pdev=0x%p, slot=%s, previous operating state [D%d]\n",
+ pdev, pci_name(pdev), device_state);
+
+ ioc->pdev = pdev;
+ r = mpt3sas_base_map_resources(ioc);
+ if (r)
+ return r;
+ ioc_info(ioc, "Issuing Hard Reset as part of OS Resume\n");
+ mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET);
+ scsi_unblock_requests(shost);
+ mpt3sas_base_start_watchdog(ioc);
+ return 0;
+}
+
+/**
+ * scsih_pci_error_detected - Called when a PCI error is detected.
+ * @pdev: PCI device struct
+ * @state: PCI channel state
+ *
+ * Description: Called when a PCI error is detected.
+ *
+ * Return: PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT.
+ */
+static pci_ers_result_t
+scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+
+ if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc))
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ ioc_info(ioc, "PCI error: detected callback, state(%d)!!\n", state);
+
+ switch (state) {
+ case pci_channel_io_normal:
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ /* Fatal error, prepare for slot reset */
+ ioc->pci_error_recovery = 1;
+ scsi_block_requests(ioc->shost);
+ mpt3sas_base_stop_watchdog(ioc);
+ mpt3sas_base_free_resources(ioc);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ /* Permanent error, prepare for device removal */
+ ioc->pci_error_recovery = 1;
+ mpt3sas_base_stop_watchdog(ioc);
+ mpt3sas_base_pause_mq_polling(ioc);
+ _scsih_flush_running_cmds(ioc);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * scsih_pci_slot_reset - Called when PCI slot has been reset.
+ * @pdev: PCI device struct
+ *
+ * Description: This routine is called by the pci error recovery
+ * code after the PCI slot has been reset, just before we
+ * should resume normal operations.
+ */
+static pci_ers_result_t
+scsih_pci_slot_reset(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ int rc;
+
+ if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc))
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ ioc_info(ioc, "PCI error: slot reset callback!!\n");
+
+ ioc->pci_error_recovery = 0;
+ ioc->pdev = pdev;
+ pci_restore_state(pdev);
+ rc = mpt3sas_base_map_resources(ioc);
+ if (rc)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ ioc_info(ioc, "Issuing Hard Reset as part of PCI Slot Reset\n");
+ rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+
+ ioc_warn(ioc, "hard reset: %s\n",
+ (rc == 0) ? "success" : "failed");
+
+ if (!rc)
+ return PCI_ERS_RESULT_RECOVERED;
+ else
+ return PCI_ERS_RESULT_DISCONNECT;
+}
+
+/**
+ * scsih_pci_resume() - resume normal ops after PCI reset
+ * @pdev: pointer to PCI device
+ *
+ * Called when the error recovery driver tells us that its
+ * OK to resume normal operation. Use completion to allow
+ * halted scsi ops to resume.
+ */
+static void
+scsih_pci_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+
+ if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc))
+ return;
+
+ ioc_info(ioc, "PCI error: resume callback!!\n");
+
+ mpt3sas_base_start_watchdog(ioc);
+ scsi_unblock_requests(ioc->shost);
+}
+
+/**
+ * scsih_pci_mmio_enabled - Enable MMIO and dump debug registers
+ * @pdev: pointer to PCI device
+ */
+static pci_ers_result_t
+scsih_pci_mmio_enabled(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+
+ if (_scsih_get_shost_and_ioc(pdev, &shost, &ioc))
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ ioc_info(ioc, "PCI error: mmio enabled callback!!\n");
+
+ /* TODO - dump whatever for debugging purposes */
+
+ /* This called only if scsih_pci_error_detected returns
+ * PCI_ERS_RESULT_CAN_RECOVER. Read/write to the device still
+ * works, no need to reset slot.
+ */
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/*
+ * The pci device ids are defined in mpi/mpi2_cnfg.h.
+ */
+static const struct pci_device_id mpt3sas_pci_table[] = {
+ /* Spitfire ~ 2004 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Falcon ~ 2008 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Liberator ~ 2108 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Meteor ~ 2116 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Thunderbolt ~ 2208 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Mustang ~ 2308 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* SSS6200 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Fury ~ 3004 and 3008 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Invader ~ 3108 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Cutlass ~ 3216 and 3224 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3216,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3224,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Intruder ~ 3316 and 3324 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3316_4,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3324_4,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Ventura, Crusader, Harpoon & Tomcat ~ 3516, 3416, 3508 & 3408*/
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3508,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3508_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3408,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3516,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3516_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3416,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Mercator ~ 3616*/
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_SAS3616,
+ PCI_ANY_ID, PCI_ANY_ID },
+
+ /* Aero SI 0x00E1 Configurable Secure
+ * 0x00E2 Hard Secure
+ */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_CFG_SEC_3916,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_3916,
+ PCI_ANY_ID, PCI_ANY_ID },
+
+ /*
+ * Aero SI –> 0x00E0 Invalid, 0x00E3 Tampered
+ */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID0_3916,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID1_3916,
+ PCI_ANY_ID, PCI_ANY_ID },
+
+ /* Atlas PCIe Switch Management Port */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_ATLAS_PCIe_SWITCH_DEVID,
+ PCI_ANY_ID, PCI_ANY_ID },
+
+ /* Sea SI 0x00E5 Configurable Secure
+ * 0x00E6 Hard Secure
+ */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_CFG_SEC_3816,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_3816,
+ PCI_ANY_ID, PCI_ANY_ID },
+
+ /*
+ * ATTO Branded ExpressSAS H12xx GT
+ */
+ { MPI2_MFGPAGE_VENDORID_ATTO, MPI26_MFGPAGE_DEVID_HARD_SEC_3816,
+ PCI_ANY_ID, PCI_ANY_ID },
+
+ /*
+ * Sea SI –> 0x00E4 Invalid, 0x00E7 Tampered
+ */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID0_3816,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_INVALID1_3816,
+ PCI_ANY_ID, PCI_ANY_ID },
+
+ {0} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, mpt3sas_pci_table);
+
+static const struct pci_error_handlers _mpt3sas_err_handler = {
+ .error_detected = scsih_pci_error_detected,
+ .mmio_enabled = scsih_pci_mmio_enabled,
+ .slot_reset = scsih_pci_slot_reset,
+ .resume = scsih_pci_resume,
+};
+
+static SIMPLE_DEV_PM_OPS(scsih_pm_ops, scsih_suspend, scsih_resume);
+
+static struct pci_driver mpt3sas_driver = {
+ .name = MPT3SAS_DRIVER_NAME,
+ .id_table = mpt3sas_pci_table,
+ .probe = _scsih_probe,
+ .remove = scsih_remove,
+ .shutdown = scsih_shutdown,
+ .err_handler = &_mpt3sas_err_handler,
+ .driver.pm = &scsih_pm_ops,
+};
+
+/**
+ * scsih_init - main entry point for this driver.
+ *
+ * Return: 0 success, anything else error.
+ */
+static int
+scsih_init(void)
+{
+ mpt2_ids = 0;
+ mpt3_ids = 0;
+
+ mpt3sas_base_initialize_callback_handler();
+
+ /* queuecommand callback hander */
+ scsi_io_cb_idx = mpt3sas_base_register_callback_handler(_scsih_io_done);
+
+ /* task management callback handler */
+ tm_cb_idx = mpt3sas_base_register_callback_handler(_scsih_tm_done);
+
+ /* base internal commands callback handler */
+ base_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_base_done);
+ port_enable_cb_idx = mpt3sas_base_register_callback_handler(
+ mpt3sas_port_enable_done);
+
+ /* transport internal commands callback handler */
+ transport_cb_idx = mpt3sas_base_register_callback_handler(
+ mpt3sas_transport_done);
+
+ /* scsih internal commands callback handler */
+ scsih_cb_idx = mpt3sas_base_register_callback_handler(_scsih_done);
+
+ /* configuration page API internal commands callback handler */
+ config_cb_idx = mpt3sas_base_register_callback_handler(
+ mpt3sas_config_done);
+
+ /* ctl module callback handler */
+ ctl_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_ctl_done);
+
+ tm_tr_cb_idx = mpt3sas_base_register_callback_handler(
+ _scsih_tm_tr_complete);
+
+ tm_tr_volume_cb_idx = mpt3sas_base_register_callback_handler(
+ _scsih_tm_volume_tr_complete);
+
+ tm_sas_control_cb_idx = mpt3sas_base_register_callback_handler(
+ _scsih_sas_control_complete);
+
+ mpt3sas_init_debugfs();
+ return 0;
+}
+
+/**
+ * scsih_exit - exit point for this driver (when it is a module).
+ *
+ * Return: 0 success, anything else error.
+ */
+static void
+scsih_exit(void)
+{
+
+ mpt3sas_base_release_callback_handler(scsi_io_cb_idx);
+ mpt3sas_base_release_callback_handler(tm_cb_idx);
+ mpt3sas_base_release_callback_handler(base_cb_idx);
+ mpt3sas_base_release_callback_handler(port_enable_cb_idx);
+ mpt3sas_base_release_callback_handler(transport_cb_idx);
+ mpt3sas_base_release_callback_handler(scsih_cb_idx);
+ mpt3sas_base_release_callback_handler(config_cb_idx);
+ mpt3sas_base_release_callback_handler(ctl_cb_idx);
+
+ mpt3sas_base_release_callback_handler(tm_tr_cb_idx);
+ mpt3sas_base_release_callback_handler(tm_tr_volume_cb_idx);
+ mpt3sas_base_release_callback_handler(tm_sas_control_cb_idx);
+
+/* raid transport support */
+ if (hbas_to_enumerate != 1)
+ raid_class_release(mpt3sas_raid_template);
+ if (hbas_to_enumerate != 2)
+ raid_class_release(mpt2sas_raid_template);
+ sas_release_transport(mpt3sas_transport_template);
+ mpt3sas_exit_debugfs();
+}
+
+/**
+ * _mpt3sas_init - main entry point for this driver.
+ *
+ * Return: 0 success, anything else error.
+ */
+static int __init
+_mpt3sas_init(void)
+{
+ int error;
+
+ pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME,
+ MPT3SAS_DRIVER_VERSION);
+
+ mpt3sas_transport_template =
+ sas_attach_transport(&mpt3sas_transport_functions);
+ if (!mpt3sas_transport_template)
+ return -ENODEV;
+
+ /* No need attach mpt3sas raid functions template
+ * if hbas_to_enumarate value is one.
+ */
+ if (hbas_to_enumerate != 1) {
+ mpt3sas_raid_template =
+ raid_class_attach(&mpt3sas_raid_functions);
+ if (!mpt3sas_raid_template) {
+ sas_release_transport(mpt3sas_transport_template);
+ return -ENODEV;
+ }
+ }
+
+ /* No need to attach mpt2sas raid functions template
+ * if hbas_to_enumarate value is two
+ */
+ if (hbas_to_enumerate != 2) {
+ mpt2sas_raid_template =
+ raid_class_attach(&mpt2sas_raid_functions);
+ if (!mpt2sas_raid_template) {
+ sas_release_transport(mpt3sas_transport_template);
+ return -ENODEV;
+ }
+ }
+
+ error = scsih_init();
+ if (error) {
+ scsih_exit();
+ return error;
+ }
+
+ mpt3sas_ctl_init(hbas_to_enumerate);
+
+ error = pci_register_driver(&mpt3sas_driver);
+ if (error) {
+ mpt3sas_ctl_exit(hbas_to_enumerate);
+ scsih_exit();
+ }
+
+ return error;
+}
+
+/**
+ * _mpt3sas_exit - exit point for this driver (when it is a module).
+ *
+ */
+static void __exit
+_mpt3sas_exit(void)
+{
+ pr_info("mpt3sas version %s unloading\n",
+ MPT3SAS_DRIVER_VERSION);
+
+ pci_unregister_driver(&mpt3sas_driver);
+
+ mpt3sas_ctl_exit(hbas_to_enumerate);
+
+ scsih_exit();
+}
+
+module_init(_mpt3sas_init);
+module_exit(_mpt3sas_exit);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c
new file mode 100644
index 000000000000..f3400d01cc2a
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c
@@ -0,0 +1,2194 @@
+/*
+ * SAS Transport Layer for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_sas.h>
+#include <scsi/scsi_dbg.h>
+
+#include "mpt3sas_base.h"
+
+/**
+ * _transport_get_port_id_by_sas_phy - get zone's port id that Phy belong to
+ * @phy: sas_phy object
+ *
+ * Return Port number
+ */
+static inline u8
+_transport_get_port_id_by_sas_phy(struct sas_phy *phy)
+{
+ u8 port_id = 0xFF;
+ struct hba_port *port = phy->hostdata;
+
+ if (port)
+ port_id = port->port_id;
+
+ return port_id;
+}
+
+/**
+ * _transport_sas_node_find_by_sas_address - sas node search
+ * @ioc: per adapter object
+ * @sas_address: sas address of expander or sas host
+ * @port: hba port entry
+ * Context: Calling function should acquire ioc->sas_node_lock.
+ *
+ * Search for either hba phys or expander device based on handle, then returns
+ * the sas_node object.
+ */
+static struct _sas_node *
+_transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct hba_port *port)
+{
+ if (ioc->sas_hba.sas_address == sas_address)
+ return &ioc->sas_hba;
+ else
+ return mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address, port);
+}
+
+/**
+ * _transport_get_port_id_by_rphy - Get Port number from rphy object
+ * @ioc: per adapter object
+ * @rphy: sas_rphy object
+ *
+ * Returns Port number.
+ */
+static u8
+_transport_get_port_id_by_rphy(struct MPT3SAS_ADAPTER *ioc,
+ struct sas_rphy *rphy)
+{
+ struct _sas_node *sas_expander;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u8 port_id = 0xFF;
+
+ if (!rphy)
+ return port_id;
+
+ if (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
+ rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) {
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_for_each_entry(sas_expander,
+ &ioc->sas_expander_list, list) {
+ if (sas_expander->rphy == rphy) {
+ port_id = sas_expander->port->port_id;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ } else if (rphy->identify.device_type == SAS_END_DEVICE) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);
+ if (sas_device) {
+ port_id = sas_device->port->port_id;
+ sas_device_put(sas_device);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+
+ return port_id;
+}
+
+/**
+ * _transport_convert_phy_link_rate -
+ * @link_rate: link rate returned from mpt firmware
+ *
+ * Convert link_rate from mpi fusion into sas_transport form.
+ */
+static enum sas_linkrate
+_transport_convert_phy_link_rate(u8 link_rate)
+{
+ enum sas_linkrate rc;
+
+ switch (link_rate) {
+ case MPI2_SAS_NEG_LINK_RATE_1_5:
+ rc = SAS_LINK_RATE_1_5_GBPS;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_3_0:
+ rc = SAS_LINK_RATE_3_0_GBPS;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_6_0:
+ rc = SAS_LINK_RATE_6_0_GBPS;
+ break;
+ case MPI25_SAS_NEG_LINK_RATE_12_0:
+ rc = SAS_LINK_RATE_12_0_GBPS;
+ break;
+ case MPI26_SAS_NEG_LINK_RATE_22_5:
+ rc = SAS_LINK_RATE_22_5_GBPS;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED:
+ rc = SAS_PHY_DISABLED;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
+ rc = SAS_LINK_RATE_FAILED;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR:
+ rc = SAS_SATA_PORT_SELECTOR;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
+ rc = SAS_PHY_RESET_IN_PROGRESS;
+ break;
+
+ default:
+ case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
+ case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
+ rc = SAS_LINK_RATE_UNKNOWN;
+ break;
+ }
+ return rc;
+}
+
+/**
+ * _transport_set_identify - set identify for phys and end devices
+ * @ioc: per adapter object
+ * @handle: device handle
+ * @identify: sas identify info
+ *
+ * Populates sas identify info.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_transport_set_identify(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ struct sas_identify *identify)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 device_info;
+ u32 ioc_status;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ ioc_info(ioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "handle(0x%04x), ioc_status(0x%04x) failure at %s:%d/%s()!\n",
+ handle, ioc_status, __FILE__, __LINE__, __func__);
+ return -EIO;
+ }
+
+ memset(identify, 0, sizeof(struct sas_identify));
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+
+ /* sas_address */
+ identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+
+ /* phy number of the parent device this device is linked to */
+ identify->phy_identifier = sas_device_pg0.PhyNum;
+
+ /* device_type */
+ switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
+ case MPI2_SAS_DEVICE_INFO_NO_DEVICE:
+ identify->device_type = SAS_PHY_UNUSED;
+ break;
+ case MPI2_SAS_DEVICE_INFO_END_DEVICE:
+ identify->device_type = SAS_END_DEVICE;
+ break;
+ case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER:
+ identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
+ break;
+ case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER:
+ identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
+ break;
+ }
+
+ /* initiator_port_protocols */
+ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
+
+ /* target_port_protocols */
+ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_SSP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_STP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_SMP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ identify->target_port_protocols |= SAS_PROTOCOL_SATA;
+
+ return 0;
+}
+
+/**
+ * mpt3sas_transport_done - internal transport layer callback handler.
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when sending internal generated transport cmds.
+ * The callback index passed is `ioc->transport_cb_idx`
+ *
+ * Return: 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (ioc->transport_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->transport_cmds.smid != smid)
+ return 1;
+ ioc->transport_cmds.status |= MPT3_CMD_COMPLETE;
+ if (mpi_reply) {
+ memcpy(ioc->transport_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ ioc->transport_cmds.status |= MPT3_CMD_REPLY_VALID;
+ }
+ ioc->transport_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->transport_cmds.done);
+ return 1;
+}
+
+/* report manufacture request structure */
+struct rep_manu_request {
+ u8 smp_frame_type;
+ u8 function;
+ u8 reserved;
+ u8 request_length;
+};
+
+/* report manufacture reply structure */
+struct rep_manu_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x01 */
+ u8 function_result;
+ u8 response_length;
+ u16 expander_change_count;
+ u8 reserved0[2];
+ u8 sas_format;
+ u8 reserved2[3];
+ u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] __nonstring;
+ u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN] __nonstring;
+ u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] __nonstring;
+ u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] __nonstring;
+ u16 component_id;
+ u8 component_revision_id;
+ u8 reserved3;
+ u8 vendor_specific[8];
+};
+
+/**
+ * _transport_expander_report_manufacture - obtain SMP report_manufacture
+ * @ioc: per adapter object
+ * @sas_address: expander sas address
+ * @edev: the sas_expander_device object
+ * @port_id: Port ID number
+ *
+ * Fills in the sas_expander_device object when SMP port is created.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct sas_expander_device *edev, u8 port_id)
+{
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ struct rep_manu_reply *manufacture_reply;
+ struct rep_manu_request *manufacture_request;
+ int rc;
+ u16 smid;
+ void *psge;
+ u8 issue_reset = 0;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ dma_addr_t data_in_dma;
+ size_t data_in_sz;
+ size_t data_out_sz;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ ioc_info(ioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ioc->transport_cmds.mutex);
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (rc)
+ goto out;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ data_out_sz = sizeof(struct rep_manu_request);
+ data_in_sz = sizeof(struct rep_manu_reply);
+ data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz,
+ &data_out_dma, GFP_KERNEL);
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ rc = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+
+ data_in_dma = data_out_dma + sizeof(struct rep_manu_request);
+
+ manufacture_request = data_out;
+ manufacture_request->smp_frame_type = 0x40;
+ manufacture_request->function = 1;
+ manufacture_request->reserved = 0;
+ manufacture_request->request_length = 0;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = port_id;
+ mpi_request->SASAddress = cpu_to_le64(sas_address);
+ mpi_request->RequestDataLength = cpu_to_le16(data_out_sz);
+ psge = &mpi_request->SGL;
+
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
+ data_in_sz);
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "report_manufacture - send to sas_addr(0x%016llx)\n",
+ (u64)sas_address));
+ init_completion(&ioc->transport_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ dtransportprintk(ioc, ioc_info(ioc, "report_manufacture - complete\n"));
+
+ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
+ u8 *tmp;
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "report_manufacture - reply data transfer size(%d)\n",
+ le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
+ sizeof(struct rep_manu_reply))
+ goto out;
+
+ manufacture_reply = data_out + sizeof(struct rep_manu_request);
+ memtostr(edev->vendor_id, manufacture_reply->vendor_id);
+ memtostr(edev->product_id, manufacture_reply->product_id);
+ memtostr(edev->product_rev, manufacture_reply->product_rev);
+ edev->level = manufacture_reply->sas_format & 1;
+ if (edev->level) {
+ memtostr(edev->component_vendor_id,
+ manufacture_reply->component_vendor_id);
+ tmp = (u8 *)&manufacture_reply->component_id;
+ edev->component_id = tmp[0] << 8 | tmp[1];
+ edev->component_revision_id =
+ manufacture_reply->component_revision_id;
+ }
+ } else
+ dtransportprintk(ioc,
+ ioc_info(ioc, "report_manufacture - no reply\n"));
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ if (data_out)
+ dma_free_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz,
+ data_out, data_out_dma);
+
+ mutex_unlock(&ioc->transport_cmds.mutex);
+ return rc;
+}
+
+
+/**
+ * _transport_delete_port - helper function to removing a port
+ * @ioc: per adapter object
+ * @mpt3sas_port: mpt3sas per port object
+ */
+static void
+_transport_delete_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_port *mpt3sas_port)
+{
+ u64 sas_address = mpt3sas_port->remote_identify.sas_address;
+ struct hba_port *port = mpt3sas_port->hba_port;
+ enum sas_device_type device_type =
+ mpt3sas_port->remote_identify.device_type;
+
+ dev_printk(KERN_INFO, &mpt3sas_port->port->dev,
+ "remove: sas_addr(0x%016llx)\n",
+ (unsigned long long) sas_address);
+
+ ioc->logging_level |= MPT_DEBUG_TRANSPORT;
+ if (device_type == SAS_END_DEVICE)
+ mpt3sas_device_remove_by_sas_address(ioc,
+ sas_address, port);
+ else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
+ device_type == SAS_FANOUT_EXPANDER_DEVICE)
+ mpt3sas_expander_remove(ioc, sas_address, port);
+ ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
+}
+
+/**
+ * _transport_delete_phy - helper function to removing single phy from port
+ * @ioc: per adapter object
+ * @mpt3sas_port: mpt3sas per port object
+ * @mpt3sas_phy: mpt3sas per phy object
+ */
+static void
+_transport_delete_phy(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_port *mpt3sas_port, struct _sas_phy *mpt3sas_phy)
+{
+ u64 sas_address = mpt3sas_port->remote_identify.sas_address;
+
+ dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
+ "remove: sas_addr(0x%016llx), phy(%d)\n",
+ (unsigned long long) sas_address, mpt3sas_phy->phy_id);
+
+ list_del(&mpt3sas_phy->port_siblings);
+ mpt3sas_port->num_phys--;
+ sas_port_delete_phy(mpt3sas_port->port, mpt3sas_phy->phy);
+ mpt3sas_phy->phy_belongs_to_port = 0;
+}
+
+/**
+ * _transport_add_phy - helper function to adding single phy to port
+ * @ioc: per adapter object
+ * @mpt3sas_port: mpt3sas per port object
+ * @mpt3sas_phy: mpt3sas per phy object
+ */
+static void
+_transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt3sas_port,
+ struct _sas_phy *mpt3sas_phy)
+{
+ u64 sas_address = mpt3sas_port->remote_identify.sas_address;
+
+ dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
+ "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long)
+ sas_address, mpt3sas_phy->phy_id);
+
+ list_add_tail(&mpt3sas_phy->port_siblings, &mpt3sas_port->phy_list);
+ mpt3sas_port->num_phys++;
+ sas_port_add_phy(mpt3sas_port->port, mpt3sas_phy->phy);
+ mpt3sas_phy->phy_belongs_to_port = 1;
+}
+
+/**
+ * mpt3sas_transport_add_phy_to_an_existing_port - adding new phy to existing port
+ * @ioc: per adapter object
+ * @sas_node: sas node object (either expander or sas host)
+ * @mpt3sas_phy: mpt3sas per phy object
+ * @sas_address: sas address of device/expander were phy needs to be added to
+ * @port: hba port entry
+ */
+void
+mpt3sas_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy,
+ u64 sas_address, struct hba_port *port)
+{
+ struct _sas_port *mpt3sas_port;
+ struct _sas_phy *phy_srch;
+
+ if (mpt3sas_phy->phy_belongs_to_port == 1)
+ return;
+
+ if (!port)
+ return;
+
+ list_for_each_entry(mpt3sas_port, &sas_node->sas_port_list,
+ port_list) {
+ if (mpt3sas_port->remote_identify.sas_address !=
+ sas_address)
+ continue;
+ if (mpt3sas_port->hba_port != port)
+ continue;
+ list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
+ port_siblings) {
+ if (phy_srch == mpt3sas_phy)
+ return;
+ }
+ _transport_add_phy(ioc, mpt3sas_port, mpt3sas_phy);
+ return;
+ }
+
+}
+
+/**
+ * mpt3sas_transport_del_phy_from_an_existing_port - delete phy from existing port
+ * @ioc: per adapter object
+ * @sas_node: sas node object (either expander or sas host)
+ * @mpt3sas_phy: mpt3sas per phy object
+ */
+void
+mpt3sas_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy)
+{
+ struct _sas_port *mpt3sas_port, *next;
+ struct _sas_phy *phy_srch;
+
+ if (mpt3sas_phy->phy_belongs_to_port == 0)
+ return;
+
+ list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
+ port_list) {
+ list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
+ port_siblings) {
+ if (phy_srch != mpt3sas_phy)
+ continue;
+
+ /*
+ * Don't delete port during host reset,
+ * just delete phy.
+ */
+ if (mpt3sas_port->num_phys == 1 && !ioc->shost_recovery)
+ _transport_delete_port(ioc, mpt3sas_port);
+ else
+ _transport_delete_phy(ioc, mpt3sas_port,
+ mpt3sas_phy);
+ return;
+ }
+ }
+}
+
+/**
+ * _transport_sanity_check - sanity check when adding a new port
+ * @ioc: per adapter object
+ * @sas_node: sas node object (either expander or sas host)
+ * @sas_address: sas address of device being added
+ * @port: hba port entry
+ *
+ * See the explanation above from _transport_delete_duplicate_port
+ */
+static void
+_transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node,
+ u64 sas_address, struct hba_port *port)
+{
+ int i;
+
+ for (i = 0; i < sas_node->num_phys; i++) {
+ if (sas_node->phy[i].remote_identify.sas_address != sas_address)
+ continue;
+ if (sas_node->phy[i].port != port)
+ continue;
+ if (sas_node->phy[i].phy_belongs_to_port == 1)
+ mpt3sas_transport_del_phy_from_an_existing_port(ioc,
+ sas_node, &sas_node->phy[i]);
+ }
+}
+
+/**
+ * mpt3sas_transport_port_add - insert port to the list
+ * @ioc: per adapter object
+ * @handle: handle of attached device
+ * @sas_address: sas address of parent expander or sas host
+ * @hba_port: hba port entry
+ * Context: This function will acquire ioc->sas_node_lock.
+ *
+ * Adding new port object to the sas_node->sas_port_list.
+ *
+ * Return: mpt3sas_port.
+ */
+struct _sas_port *
+mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u64 sas_address, struct hba_port *hba_port)
+{
+ struct _sas_phy *mpt3sas_phy, *next;
+ struct _sas_port *mpt3sas_port;
+ unsigned long flags;
+ struct _sas_node *sas_node;
+ struct sas_rphy *rphy;
+ struct _sas_device *sas_device = NULL;
+ int i;
+ struct sas_port *port;
+ struct virtual_phy *vphy = NULL;
+
+ if (!hba_port) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ mpt3sas_port = kzalloc(sizeof(struct _sas_port),
+ GFP_KERNEL);
+ if (!mpt3sas_port) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&mpt3sas_port->port_list);
+ INIT_LIST_HEAD(&mpt3sas_port->phy_list);
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc,
+ sas_address, hba_port);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (!sas_node) {
+ ioc_err(ioc, "%s: Could not find parent sas_address(0x%016llx)!\n",
+ __func__, (u64)sas_address);
+ goto out_fail;
+ }
+
+ if ((_transport_set_identify(ioc, handle,
+ &mpt3sas_port->remote_identify))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ if (mpt3sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ mpt3sas_port->hba_port = hba_port;
+ _transport_sanity_check(ioc, sas_node,
+ mpt3sas_port->remote_identify.sas_address, hba_port);
+
+ for (i = 0; i < sas_node->num_phys; i++) {
+ if (sas_node->phy[i].remote_identify.sas_address !=
+ mpt3sas_port->remote_identify.sas_address)
+ continue;
+ if (sas_node->phy[i].port != hba_port)
+ continue;
+ list_add_tail(&sas_node->phy[i].port_siblings,
+ &mpt3sas_port->phy_list);
+ mpt3sas_port->num_phys++;
+ if (sas_node->handle <= ioc->sas_hba.num_phys) {
+ if (!sas_node->phy[i].hba_vphy) {
+ hba_port->phy_mask |= (1 << i);
+ continue;
+ }
+
+ vphy = mpt3sas_get_vphy_by_phy(ioc, hba_port, i);
+ if (!vphy) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+ }
+ }
+
+ if (!mpt3sas_port->num_phys) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+ sas_device = mpt3sas_get_sdev_by_addr(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_port->hba_port);
+ if (!sas_device) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+ sas_device->pend_sas_rphy_add = 1;
+ }
+
+ if (!sas_node->parent_dev) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+ port = sas_port_alloc_num(sas_node->parent_dev);
+ if (!port || (sas_port_add(port))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ list_for_each_entry(mpt3sas_phy, &mpt3sas_port->phy_list,
+ port_siblings) {
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &port->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx), phy(%d)\n",
+ handle, (unsigned long long)
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_phy->phy_id);
+ sas_port_add_phy(port, mpt3sas_phy->phy);
+ mpt3sas_phy->phy_belongs_to_port = 1;
+ mpt3sas_phy->port = hba_port;
+ }
+
+ mpt3sas_port->port = port;
+ if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+ rphy = sas_end_device_alloc(port);
+ sas_device->rphy = rphy;
+ if (sas_node->handle <= ioc->sas_hba.num_phys) {
+ if (!vphy)
+ hba_port->sas_address =
+ sas_device->sas_address;
+ else
+ vphy->sas_address =
+ sas_device->sas_address;
+ }
+ } else {
+ rphy = sas_expander_alloc(port,
+ mpt3sas_port->remote_identify.device_type);
+ if (sas_node->handle <= ioc->sas_hba.num_phys)
+ hba_port->sas_address =
+ mpt3sas_port->remote_identify.sas_address;
+ }
+
+ if (!rphy) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto out_delete_port;
+ }
+
+ rphy->identify = mpt3sas_port->remote_identify;
+
+ if ((sas_rphy_add(rphy))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_rphy_free(rphy);
+ rphy = NULL;
+ goto out_delete_port;
+ }
+
+ if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+ sas_device->pend_sas_rphy_add = 0;
+ sas_device_put(sas_device);
+ }
+
+ dev_info(&rphy->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx)\n", handle,
+ (unsigned long long)mpt3sas_port->remote_identify.sas_address);
+
+ mpt3sas_port->rphy = rphy;
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_add_tail(&mpt3sas_port->port_list, &sas_node->sas_port_list);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* fill in report manufacture */
+ if (mpt3sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
+ mpt3sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
+ _transport_expander_report_manufacture(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ rphy_to_expander_device(rphy), hba_port->port_id);
+ return mpt3sas_port;
+
+out_delete_port:
+ sas_port_delete(port);
+
+out_fail:
+ list_for_each_entry_safe(mpt3sas_phy, next, &mpt3sas_port->phy_list,
+ port_siblings)
+ list_del(&mpt3sas_phy->port_siblings);
+ kfree(mpt3sas_port);
+ return NULL;
+}
+
+/**
+ * mpt3sas_transport_port_remove - remove port from the list
+ * @ioc: per adapter object
+ * @sas_address: sas address of attached device
+ * @sas_address_parent: sas address of parent expander or sas host
+ * @port: hba port entry
+ * Context: This function will acquire ioc->sas_node_lock.
+ *
+ * Removing object and freeing associated memory from the
+ * ioc->sas_port_list.
+ */
+void
+mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u64 sas_address_parent, struct hba_port *port)
+{
+ int i;
+ unsigned long flags;
+ struct _sas_port *mpt3sas_port, *next;
+ struct _sas_node *sas_node;
+ u8 found = 0;
+ struct _sas_phy *mpt3sas_phy, *next_phy;
+ struct hba_port *hba_port_next, *hba_port = NULL;
+ struct virtual_phy *vphy, *vphy_next = NULL;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc,
+ sas_address_parent, port);
+ if (!sas_node) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return;
+ }
+ list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
+ port_list) {
+ if (mpt3sas_port->remote_identify.sas_address != sas_address)
+ continue;
+ if (mpt3sas_port->hba_port != port)
+ continue;
+ found = 1;
+ list_del(&mpt3sas_port->port_list);
+ goto out;
+ }
+ out:
+ if (!found) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return;
+ }
+
+ if (sas_node->handle <= ioc->sas_hba.num_phys &&
+ (ioc->multipath_on_hba)) {
+ if (port->vphys_mask) {
+ list_for_each_entry_safe(vphy, vphy_next,
+ &port->vphys_list, list) {
+ if (vphy->sas_address != sas_address)
+ continue;
+ ioc_info(ioc,
+ "remove vphy entry: %p of port:%p,from %d port's vphys list\n",
+ vphy, port, port->port_id);
+ port->vphys_mask &= ~vphy->phy_mask;
+ list_del(&vphy->list);
+ kfree(vphy);
+ }
+ }
+
+ list_for_each_entry_safe(hba_port, hba_port_next,
+ &ioc->port_table_list, list) {
+ if (hba_port != port)
+ continue;
+ /*
+ * Delete hba_port object if
+ * - hba_port object's sas address matches with current
+ * removed device's sas address and no vphy's
+ * associated with it.
+ * - Current removed device is a vSES device and
+ * none of the other direct attached device have
+ * this vSES device's port number (hence hba_port
+ * object sas_address field will be zero).
+ */
+ if ((hba_port->sas_address == sas_address ||
+ !hba_port->sas_address) && !hba_port->vphys_mask) {
+ ioc_info(ioc,
+ "remove hba_port entry: %p port: %d from hba_port list\n",
+ hba_port, hba_port->port_id);
+ list_del(&hba_port->list);
+ kfree(hba_port);
+ } else if (hba_port->sas_address == sas_address &&
+ hba_port->vphys_mask) {
+ /*
+ * Current removed device is a non vSES device
+ * and a vSES device has the same port number
+ * as of current device's port number. Hence
+ * only clear the sas_address filed, don't
+ * delete the hba_port object.
+ */
+ ioc_info(ioc,
+ "clearing sas_address from hba_port entry: %p port: %d from hba_port list\n",
+ hba_port, hba_port->port_id);
+ port->sas_address = 0;
+ }
+ break;
+ }
+ }
+
+ for (i = 0; i < sas_node->num_phys; i++) {
+ if (sas_node->phy[i].remote_identify.sas_address == sas_address)
+ memset(&sas_node->phy[i].remote_identify, 0 ,
+ sizeof(struct sas_identify));
+ }
+
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ list_for_each_entry_safe(mpt3sas_phy, next_phy,
+ &mpt3sas_port->phy_list, port_siblings) {
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ ioc_info(ioc, "remove: sas_addr(0x%016llx), phy(%d)\n",
+ (unsigned long long) mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_phy->phy_id);
+ mpt3sas_phy->phy_belongs_to_port = 0;
+ if (!ioc->remove_host)
+ sas_port_delete_phy(mpt3sas_port->port,
+ mpt3sas_phy->phy);
+ list_del(&mpt3sas_phy->port_siblings);
+ }
+ if (!ioc->remove_host)
+ sas_port_delete(mpt3sas_port->port);
+ ioc_info(ioc, "%s: removed: sas_addr(0x%016llx)\n",
+ __func__, (unsigned long long)sas_address);
+ kfree(mpt3sas_port);
+}
+
+/**
+ * mpt3sas_transport_add_host_phy - report sas_host phy to transport
+ * @ioc: per adapter object
+ * @mpt3sas_phy: mpt3sas per phy object
+ * @phy_pg0: sas phy page 0
+ * @parent_dev: parent device class object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev)
+{
+ struct sas_phy *phy;
+ int phy_index = mpt3sas_phy->phy_id;
+
+
+ INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
+ phy = sas_phy_alloc(parent_dev, phy_index);
+ if (!phy) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
+ &mpt3sas_phy->identify))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ phy->identify = mpt3sas_phy->identify;
+ mpt3sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle);
+ if (mpt3sas_phy->attached_handle)
+ _transport_set_identify(ioc, mpt3sas_phy->attached_handle,
+ &mpt3sas_phy->remote_identify);
+ phy->identify.phy_identifier = mpt3sas_phy->phy_id;
+ phy->negotiated_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
+ phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
+ phy_pg0.HwLinkRate >> 4);
+ phy->minimum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate >> 4);
+ phy->hostdata = mpt3sas_phy->port;
+
+ if ((sas_phy_add(phy))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &phy->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx)\n"
+ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
+ mpt3sas_phy->handle, (unsigned long long)
+ mpt3sas_phy->identify.sas_address,
+ mpt3sas_phy->attached_handle,
+ (unsigned long long)
+ mpt3sas_phy->remote_identify.sas_address);
+ mpt3sas_phy->phy = phy;
+ return 0;
+}
+
+
+/**
+ * mpt3sas_transport_add_expander_phy - report expander phy to transport
+ * @ioc: per adapter object
+ * @mpt3sas_phy: mpt3sas per phy object
+ * @expander_pg1: expander page 1
+ * @parent_dev: parent device class object
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1,
+ struct device *parent_dev)
+{
+ struct sas_phy *phy;
+ int phy_index = mpt3sas_phy->phy_id;
+
+ INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
+ phy = sas_phy_alloc(parent_dev, phy_index);
+ if (!phy) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
+ &mpt3sas_phy->identify))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ phy->identify = mpt3sas_phy->identify;
+ mpt3sas_phy->attached_handle =
+ le16_to_cpu(expander_pg1.AttachedDevHandle);
+ if (mpt3sas_phy->attached_handle)
+ _transport_set_identify(ioc, mpt3sas_phy->attached_handle,
+ &mpt3sas_phy->remote_identify);
+ phy->identify.phy_identifier = mpt3sas_phy->phy_id;
+ phy->negotiated_linkrate = _transport_convert_phy_link_rate(
+ expander_pg1.NegotiatedLinkRate &
+ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
+ expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
+ expander_pg1.HwLinkRate >> 4);
+ phy->minimum_linkrate = _transport_convert_phy_link_rate(
+ expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = _transport_convert_phy_link_rate(
+ expander_pg1.ProgrammedLinkRate >> 4);
+ phy->hostdata = mpt3sas_phy->port;
+
+ if ((sas_phy_add(phy))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &phy->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx)\n"
+ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
+ mpt3sas_phy->handle, (unsigned long long)
+ mpt3sas_phy->identify.sas_address,
+ mpt3sas_phy->attached_handle,
+ (unsigned long long)
+ mpt3sas_phy->remote_identify.sas_address);
+ mpt3sas_phy->phy = phy;
+ return 0;
+}
+
+/**
+ * mpt3sas_transport_update_links - refreshing phy link changes
+ * @ioc: per adapter object
+ * @sas_address: sas address of parent expander or sas host
+ * @handle: attached device handle
+ * @phy_number: phy number
+ * @link_rate: new link rate
+ * @port: hba port entry
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate,
+ struct hba_port *port)
+{
+ unsigned long flags;
+ struct _sas_node *sas_node;
+ struct _sas_phy *mpt3sas_phy;
+ struct hba_port *hba_port = NULL;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc,
+ sas_address, port);
+ if (!sas_node) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return;
+ }
+
+ mpt3sas_phy = &sas_node->phy[phy_number];
+ mpt3sas_phy->attached_handle = handle;
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) {
+ _transport_set_identify(ioc, handle,
+ &mpt3sas_phy->remote_identify);
+ if ((sas_node->handle <= ioc->sas_hba.num_phys) &&
+ (ioc->multipath_on_hba)) {
+ list_for_each_entry(hba_port,
+ &ioc->port_table_list, list) {
+ if (hba_port->sas_address == sas_address &&
+ hba_port == port)
+ hba_port->phy_mask |=
+ (1 << mpt3sas_phy->phy_id);
+ }
+ }
+ mpt3sas_transport_add_phy_to_an_existing_port(ioc, sas_node,
+ mpt3sas_phy, mpt3sas_phy->remote_identify.sas_address,
+ port);
+ } else
+ memset(&mpt3sas_phy->remote_identify, 0 , sizeof(struct
+ sas_identify));
+
+ if (mpt3sas_phy->phy)
+ mpt3sas_phy->phy->negotiated_linkrate =
+ _transport_convert_phy_link_rate(link_rate);
+
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
+ "refresh: parent sas_addr(0x%016llx),\n"
+ "\tlink_rate(0x%02x), phy(%d)\n"
+ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
+ (unsigned long long)sas_address,
+ link_rate, phy_number, handle, (unsigned long long)
+ mpt3sas_phy->remote_identify.sas_address);
+}
+
+static inline void *
+phy_to_ioc(struct sas_phy *phy)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ return shost_priv(shost);
+}
+
+static inline void *
+rphy_to_ioc(struct sas_rphy *rphy)
+{
+ struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
+ return shost_priv(shost);
+}
+
+/* report phy error log structure */
+struct phy_error_log_request {
+ u8 smp_frame_type; /* 0x40 */
+ u8 function; /* 0x11 */
+ u8 allocated_response_length;
+ u8 request_length; /* 02 */
+ u8 reserved_1[5];
+ u8 phy_identifier;
+ u8 reserved_2[2];
+};
+
+/* report phy error log reply structure */
+struct phy_error_log_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x11 */
+ u8 function_result;
+ u8 response_length;
+ __be16 expander_change_count;
+ u8 reserved_1[3];
+ u8 phy_identifier;
+ u8 reserved_2[2];
+ __be32 invalid_dword;
+ __be32 running_disparity_error;
+ __be32 loss_of_dword_sync;
+ __be32 phy_reset_problem;
+};
+
+/**
+ * _transport_get_expander_phy_error_log - return expander counters
+ * @ioc: per adapter object
+ * @phy: The sas phy object
+ *
+ * Return: 0 for success, non-zero for failure.
+ *
+ */
+static int
+_transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc,
+ struct sas_phy *phy)
+{
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ struct phy_error_log_request *phy_error_log_request;
+ struct phy_error_log_reply *phy_error_log_reply;
+ int rc;
+ u16 smid;
+ void *psge;
+ u8 issue_reset = 0;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ u32 sz;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ ioc_info(ioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ioc->transport_cmds.mutex);
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (rc)
+ goto out;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ sz = sizeof(struct phy_error_log_request) +
+ sizeof(struct phy_error_log_reply);
+ data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma,
+ GFP_KERNEL);
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ rc = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+
+ rc = -EINVAL;
+ memset(data_out, 0, sz);
+ phy_error_log_request = data_out;
+ phy_error_log_request->smp_frame_type = 0x40;
+ phy_error_log_request->function = 0x11;
+ phy_error_log_request->request_length = 2;
+ phy_error_log_request->allocated_response_length = 0;
+ phy_error_log_request->phy_identifier = phy->number;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy);
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
+ mpi_request->RequestDataLength =
+ cpu_to_le16(sizeof(struct phy_error_log_request));
+ psge = &mpi_request->SGL;
+
+ ioc->build_sg(ioc, psge, data_out_dma,
+ sizeof(struct phy_error_log_request),
+ data_out_dma + sizeof(struct phy_error_log_request),
+ sizeof(struct phy_error_log_reply));
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_error_log - send to sas_addr(0x%016llx), phy(%d)\n",
+ (u64)phy->identify.sas_address,
+ phy->number));
+ init_completion(&ioc->transport_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ dtransportprintk(ioc, ioc_info(ioc, "phy_error_log - complete\n"));
+
+ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_error_log - reply data transfer size(%d)\n",
+ le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
+ sizeof(struct phy_error_log_reply))
+ goto out;
+
+ phy_error_log_reply = data_out +
+ sizeof(struct phy_error_log_request);
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_error_log - function_result(%d)\n",
+ phy_error_log_reply->function_result));
+
+ phy->invalid_dword_count =
+ be32_to_cpu(phy_error_log_reply->invalid_dword);
+ phy->running_disparity_error_count =
+ be32_to_cpu(phy_error_log_reply->running_disparity_error);
+ phy->loss_of_dword_sync_count =
+ be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
+ phy->phy_reset_problem_count =
+ be32_to_cpu(phy_error_log_reply->phy_reset_problem);
+ rc = 0;
+ } else
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_error_log - no reply\n"));
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ if (data_out)
+ dma_free_coherent(&ioc->pdev->dev, sz, data_out, data_out_dma);
+
+ mutex_unlock(&ioc->transport_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _transport_get_linkerrors - return phy counters for both hba and expanders
+ * @phy: The sas phy object
+ *
+ * Return: 0 for success, non-zero for failure.
+ *
+ */
+static int
+_transport_get_linkerrors(struct sas_phy *phy)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ unsigned long flags;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasPhyPage1_t phy_pg1;
+ struct hba_port *port = phy->hostdata;
+ int port_id = port->port_id;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address,
+ mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address)
+ return _transport_get_expander_phy_error_log(ioc, phy);
+
+ /* get hba phy error logs */
+ if ((mpt3sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1,
+ phy->number))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
+ ioc_info(ioc, "phy(%d), ioc_status (0x%04x), loginfo(0x%08x)\n",
+ phy->number,
+ le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+
+ phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount);
+ phy->running_disparity_error_count =
+ le32_to_cpu(phy_pg1.RunningDisparityErrorCount);
+ phy->loss_of_dword_sync_count =
+ le32_to_cpu(phy_pg1.LossDwordSynchCount);
+ phy->phy_reset_problem_count =
+ le32_to_cpu(phy_pg1.PhyResetProblemCount);
+ return 0;
+}
+
+/**
+ * _transport_get_enclosure_identifier -
+ * @rphy: The sas phy object
+ * @identifier: ?
+ *
+ * Obtain the enclosure logical id for an expander.
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
+{
+ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);
+ if (sas_device) {
+ *identifier = sas_device->enclosure_logical_id;
+ rc = 0;
+ sas_device_put(sas_device);
+ } else {
+ *identifier = 0;
+ rc = -ENXIO;
+ }
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _transport_get_bay_identifier -
+ * @rphy: The sas phy object
+ *
+ * Return: the slot id for a device that resides inside an enclosure.
+ */
+static int
+_transport_get_bay_identifier(struct sas_rphy *rphy)
+{
+ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);
+ if (sas_device) {
+ rc = sas_device->slot;
+ sas_device_put(sas_device);
+ } else {
+ rc = -ENXIO;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return rc;
+}
+
+/* phy control request structure */
+struct phy_control_request {
+ u8 smp_frame_type; /* 0x40 */
+ u8 function; /* 0x91 */
+ u8 allocated_response_length;
+ u8 request_length; /* 0x09 */
+ u16 expander_change_count;
+ u8 reserved_1[3];
+ u8 phy_identifier;
+ u8 phy_operation;
+ u8 reserved_2[13];
+ u64 attached_device_name;
+ u8 programmed_min_physical_link_rate;
+ u8 programmed_max_physical_link_rate;
+ u8 reserved_3[6];
+};
+
+/* phy control reply structure */
+struct phy_control_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x11 */
+ u8 function_result;
+ u8 response_length;
+};
+
+#define SMP_PHY_CONTROL_LINK_RESET (0x01)
+#define SMP_PHY_CONTROL_HARD_RESET (0x02)
+#define SMP_PHY_CONTROL_DISABLE (0x03)
+
+/**
+ * _transport_expander_phy_control - expander phy control
+ * @ioc: per adapter object
+ * @phy: The sas phy object
+ * @phy_operation: ?
+ *
+ * Return: 0 for success, non-zero for failure.
+ *
+ */
+static int
+_transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc,
+ struct sas_phy *phy, u8 phy_operation)
+{
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ struct phy_control_request *phy_control_request;
+ struct phy_control_reply *phy_control_reply;
+ int rc;
+ u16 smid;
+ void *psge;
+ u8 issue_reset = 0;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ u32 sz;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ ioc_info(ioc, "%s: host reset in progress!\n", __func__);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ioc->transport_cmds.mutex);
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (rc)
+ goto out;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ sz = sizeof(struct phy_control_request) +
+ sizeof(struct phy_control_reply);
+ data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma,
+ GFP_KERNEL);
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ rc = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+
+ rc = -EINVAL;
+ memset(data_out, 0, sz);
+ phy_control_request = data_out;
+ phy_control_request->smp_frame_type = 0x40;
+ phy_control_request->function = 0x91;
+ phy_control_request->request_length = 9;
+ phy_control_request->allocated_response_length = 0;
+ phy_control_request->phy_identifier = phy->number;
+ phy_control_request->phy_operation = phy_operation;
+ phy_control_request->programmed_min_physical_link_rate =
+ phy->minimum_linkrate << 4;
+ phy_control_request->programmed_max_physical_link_rate =
+ phy->maximum_linkrate << 4;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy);
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
+ mpi_request->RequestDataLength =
+ cpu_to_le16(sizeof(struct phy_error_log_request));
+ psge = &mpi_request->SGL;
+
+ ioc->build_sg(ioc, psge, data_out_dma,
+ sizeof(struct phy_control_request),
+ data_out_dma + sizeof(struct phy_control_request),
+ sizeof(struct phy_control_reply));
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_control - send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n",
+ (u64)phy->identify.sas_address,
+ phy->number, phy_operation));
+ init_completion(&ioc->transport_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ dtransportprintk(ioc, ioc_info(ioc, "phy_control - complete\n"));
+
+ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_control - reply data transfer size(%d)\n",
+ le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
+ sizeof(struct phy_control_reply))
+ goto out;
+
+ phy_control_reply = data_out +
+ sizeof(struct phy_control_request);
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_control - function_result(%d)\n",
+ phy_control_reply->function_result));
+
+ rc = 0;
+ } else
+ dtransportprintk(ioc,
+ ioc_info(ioc, "phy_control - no reply\n"));
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ if (data_out)
+ dma_free_coherent(&ioc->pdev->dev, sz, data_out,
+ data_out_dma);
+
+ mutex_unlock(&ioc->transport_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _transport_phy_reset -
+ * @phy: The sas phy object
+ * @hard_reset:
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ Mpi2SasIoUnitControlReply_t mpi_reply;
+ Mpi2SasIoUnitControlRequest_t mpi_request;
+ struct hba_port *port = phy->hostdata;
+ int port_id = port->port_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address,
+ mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address)
+ return _transport_expander_phy_control(ioc, phy,
+ (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
+ SMP_PHY_CONTROL_LINK_RESET);
+
+ /* handle hba phys */
+ memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
+ mpi_request.Operation = hard_reset ?
+ MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET;
+ mpi_request.PhyNum = phy->number;
+
+ if ((mpt3sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
+ ioc_info(ioc, "phy(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
+ phy->number, le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+
+ return 0;
+}
+
+/**
+ * _transport_phy_enable - enable/disable phys
+ * @phy: The sas phy object
+ * @enable: enable phy when true
+ *
+ * Only support sas_host direct attached phys.
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_enable(struct sas_phy *phy, int enable)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 sz;
+ int rc = 0;
+ unsigned long flags;
+ int i, discovery_active;
+ struct hba_port *port = phy->hostdata;
+ int port_id = port->port_id;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address,
+ mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address)
+ return _transport_expander_phy_control(ioc, phy,
+ (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
+ SMP_PHY_CONTROL_DISABLE);
+
+ /* handle hba phys */
+
+ /* read sas_iounit page 0 */
+ sz = struct_size(sas_iounit_pg0, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ /* unable to enable/disable phys when when discovery is active */
+ for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) {
+ if (sas_iounit_pg0->PhyData[i].PortFlags &
+ MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
+ ioc_err(ioc, "discovery is active on port = %d, phy = %d: unable to enable/disable phys, try again later!\n",
+ sas_iounit_pg0->PhyData[i].Port, i);
+ discovery_active = 1;
+ }
+ }
+
+ if (discovery_active) {
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ /* read sas_iounit page 1 */
+ sz = struct_size(sas_iounit_pg1, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ /* copy Port/PortFlags/PhyFlags from page 0 */
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ sas_iounit_pg1->PhyData[i].Port =
+ sas_iounit_pg0->PhyData[i].Port;
+ sas_iounit_pg1->PhyData[i].PortFlags =
+ (sas_iounit_pg0->PhyData[i].PortFlags &
+ MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG);
+ sas_iounit_pg1->PhyData[i].PhyFlags =
+ (sas_iounit_pg0->PhyData[i].PhyFlags &
+ (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED +
+ MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED));
+ }
+
+ if (enable)
+ sas_iounit_pg1->PhyData[phy->number].PhyFlags
+ &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+ else
+ sas_iounit_pg1->PhyData[phy->number].PhyFlags
+ |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+
+ mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);
+
+ /* link reset */
+ if (enable)
+ _transport_phy_reset(phy, 0);
+
+ out:
+ kfree(sas_iounit_pg1);
+ kfree(sas_iounit_pg0);
+ return rc;
+}
+
+/**
+ * _transport_phy_speed - set phy min/max link rates
+ * @phy: The sas phy object
+ * @rates: rates defined in sas_phy_linkrates
+ *
+ * Only support sas_host direct attached phys.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasPhyPage0_t phy_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 sz;
+ int i;
+ int rc = 0;
+ unsigned long flags;
+ struct hba_port *port = phy->hostdata;
+ int port_id = port->port_id;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address,
+ mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (!rates->minimum_linkrate)
+ rates->minimum_linkrate = phy->minimum_linkrate;
+ else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
+ rates->minimum_linkrate = phy->minimum_linkrate_hw;
+
+ if (!rates->maximum_linkrate)
+ rates->maximum_linkrate = phy->maximum_linkrate;
+ else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
+ rates->maximum_linkrate = phy->maximum_linkrate_hw;
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address) {
+ phy->minimum_linkrate = rates->minimum_linkrate;
+ phy->maximum_linkrate = rates->maximum_linkrate;
+ return _transport_expander_phy_control(ioc, phy,
+ SMP_PHY_CONTROL_LINK_RESET);
+ }
+
+ /* handle hba phys */
+
+ /* sas_iounit page 1 */
+ sz = struct_size(sas_iounit_pg1, PhyData, ioc->sas_hba.num_phys);
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ for (i = 0; i < ioc->sas_hba.num_phys; i++) {
+ if (phy->number != i) {
+ sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
+ (ioc->sas_hba.phy[i].phy->minimum_linkrate +
+ (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
+ } else {
+ sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
+ (rates->minimum_linkrate +
+ (rates->maximum_linkrate << 4));
+ }
+ }
+
+ if (mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
+ sz)) {
+ ioc_err(ioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ /* link reset */
+ _transport_phy_reset(phy, 0);
+
+ /* read phy page 0, then update the rates in the sas transport phy */
+ if (!mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
+ phy->number)) {
+ phy->minimum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate >> 4);
+ phy->negotiated_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.NegotiatedLinkRate &
+ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+ }
+
+ out:
+ kfree(sas_iounit_pg1);
+ return rc;
+}
+
+static int
+_transport_map_smp_buffer(struct device *dev, struct bsg_buffer *buf,
+ dma_addr_t *dma_addr, size_t *dma_len, void **p)
+{
+ /* Check if the request is split across multiple segments */
+ if (buf->sg_cnt > 1) {
+ *p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
+ GFP_KERNEL);
+ if (!*p)
+ return -ENOMEM;
+ *dma_len = buf->payload_len;
+ } else {
+ if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL))
+ return -ENOMEM;
+ *dma_addr = sg_dma_address(buf->sg_list);
+ *dma_len = sg_dma_len(buf->sg_list);
+ *p = NULL;
+ }
+
+ return 0;
+}
+
+static void
+_transport_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf,
+ dma_addr_t dma_addr, void *p)
+{
+ if (p)
+ dma_free_coherent(dev, buf->payload_len, p, dma_addr);
+ else
+ dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
+}
+
+/**
+ * _transport_smp_handler - transport portal for smp passthru
+ * @job: ?
+ * @shost: shost object
+ * @rphy: sas transport rphy object
+ *
+ * This used primarily for smp_utils.
+ * Example:
+ * smp_rep_general /sys/class/bsg/expander-5:0
+ */
+static void
+_transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
+ struct sas_rphy *rphy)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ int rc;
+ u16 smid;
+ void *psge;
+ dma_addr_t dma_addr_in;
+ dma_addr_t dma_addr_out;
+ void *addr_in = NULL;
+ void *addr_out = NULL;
+ size_t dma_len_in;
+ size_t dma_len_out;
+ unsigned int reslen = 0;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ ioc_info(ioc, "%s: host reset in progress!\n", __func__);
+ rc = -EFAULT;
+ goto job_done;
+ }
+
+ rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
+ if (rc)
+ goto job_done;
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "%s: transport_cmds in use\n",
+ __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->request_payload,
+ &dma_addr_out, &dma_len_out, &addr_out);
+ if (rc)
+ goto out;
+ if (addr_out) {
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt, addr_out,
+ job->request_payload.payload_len);
+ }
+
+ rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
+ &dma_addr_in, &dma_len_in, &addr_in);
+ if (rc)
+ goto unmap_out;
+
+ rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
+ if (rc)
+ goto unmap_in;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
+ rc = -EAGAIN;
+ goto unmap_in;
+ }
+
+ rc = 0;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = _transport_get_port_id_by_rphy(ioc, rphy);
+ mpi_request->SASAddress = (rphy) ?
+ cpu_to_le64(rphy->identify.sas_address) :
+ cpu_to_le64(ioc->sas_hba.sas_address);
+ mpi_request->RequestDataLength = cpu_to_le16(dma_len_out - 4);
+ psge = &mpi_request->SGL;
+
+ ioc->build_sg(ioc, psge, dma_addr_out, dma_len_out - 4, dma_addr_in,
+ dma_len_in - 4);
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "%s: sending smp request\n", __func__));
+
+ init_completion(&ioc->transport_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ ioc_err(ioc, "%s: timeout\n", __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) {
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ rc = -ETIMEDOUT;
+ goto unmap_in;
+ }
+ }
+
+ dtransportprintk(ioc, ioc_info(ioc, "%s - complete\n", __func__));
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID)) {
+ dtransportprintk(ioc,
+ ioc_info(ioc, "%s: no reply\n", __func__));
+ rc = -ENXIO;
+ goto unmap_in;
+ }
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc,
+ ioc_info(ioc, "%s: reply data transfer size(%d)\n",
+ __func__,
+ le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ memcpy(job->reply, mpi_reply, sizeof(*mpi_reply));
+ job->reply_len = sizeof(*mpi_reply);
+ reslen = le16_to_cpu(mpi_reply->ResponseDataLength);
+
+ if (addr_in) {
+ sg_copy_to_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, addr_in,
+ job->reply_payload.payload_len);
+ }
+
+ rc = 0;
+ unmap_in:
+ _transport_unmap_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
+ dma_addr_in, addr_in);
+ unmap_out:
+ _transport_unmap_smp_buffer(&ioc->pdev->dev, &job->request_payload,
+ dma_addr_out, addr_out);
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->transport_cmds.mutex);
+job_done:
+ bsg_job_done(job, rc, reslen);
+}
+
+struct sas_function_template mpt3sas_transport_functions = {
+ .get_linkerrors = _transport_get_linkerrors,
+ .get_enclosure_identifier = _transport_get_enclosure_identifier,
+ .get_bay_identifier = _transport_get_bay_identifier,
+ .phy_reset = _transport_phy_reset,
+ .phy_enable = _transport_phy_enable,
+ .set_phy_speed = _transport_phy_speed,
+ .smp_handler = _transport_smp_handler,
+};
+
+struct scsi_transport_template *mpt3sas_transport_template;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
new file mode 100644
index 000000000000..d9b7d0ee25b0
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
@@ -0,0 +1,473 @@
+/*
+ * This module provides common API to set Diagnostic trigger for MPT
+ * (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/compat.h>
+#include <linux/poll.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include "mpt3sas_base.h"
+
+/**
+ * _mpt3sas_raise_sigio - notifiy app
+ * @ioc: per adapter object
+ * @event_data: ?
+ */
+static void
+_mpt3sas_raise_sigio(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+ u16 sz, event_data_sz;
+ unsigned long flags;
+
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: enter\n", __func__));
+
+ sz = offsetof(Mpi2EventNotificationReply_t, EventData) +
+ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4;
+ mpi_reply = kzalloc(sz, GFP_KERNEL);
+ if (!mpi_reply)
+ goto out;
+ mpi_reply->Event = cpu_to_le16(MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED);
+ event_data_sz = (sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4) / 4;
+ mpi_reply->EventDataLength = cpu_to_le16(event_data_sz);
+ memcpy(&mpi_reply->EventData, event_data,
+ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: add to driver event log\n",
+ __func__));
+ mpt3sas_ctl_add_to_event_log(ioc, mpi_reply);
+ kfree(mpi_reply);
+ out:
+
+ /* clearing the diag_trigger_active flag */
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: clearing diag_trigger_active flag\n",
+ __func__));
+ ioc->diag_trigger_active = 0;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: exit\n",
+ __func__));
+}
+
+/**
+ * mpt3sas_process_trigger_data - process the event data for the trigger
+ * @ioc: per adapter object
+ * @event_data: ?
+ */
+void
+mpt3sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data)
+{
+ u8 issue_reset = 0;
+ u32 *trig_data = (u32 *)&event_data->u.master;
+
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: enter\n", __func__));
+
+ /* release the diag buffer trace */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
+ /*
+ * add a log message so that user knows which event caused
+ * the release
+ */
+ ioc_info(ioc,
+ "%s: Releasing the trace buffer. Trigger_Type 0x%08x, Data[0] 0x%08x, Data[1] 0x%08x\n",
+ __func__, event_data->trigger_type,
+ trig_data[0], trig_data[1]);
+ mpt3sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE,
+ &issue_reset);
+ }
+
+ ioc->htb_rel.buffer_rel_condition = MPT3_DIAG_BUFFER_REL_TRIGGER;
+ if (event_data) {
+ ioc->htb_rel.trigger_type = event_data->trigger_type;
+ switch (event_data->trigger_type) {
+ case MPT3SAS_TRIGGER_SCSI:
+ memcpy(&ioc->htb_rel.trigger_info_dwords,
+ &event_data->u.scsi,
+ sizeof(struct SL_WH_SCSI_TRIGGER_T));
+ break;
+ case MPT3SAS_TRIGGER_MPI:
+ memcpy(&ioc->htb_rel.trigger_info_dwords,
+ &event_data->u.mpi,
+ sizeof(struct SL_WH_MPI_TRIGGER_T));
+ break;
+ case MPT3SAS_TRIGGER_MASTER:
+ ioc->htb_rel.trigger_info_dwords[0] =
+ event_data->u.master.MasterData;
+ break;
+ case MPT3SAS_TRIGGER_EVENT:
+ memcpy(&ioc->htb_rel.trigger_info_dwords,
+ &event_data->u.event,
+ sizeof(struct SL_WH_EVENT_TRIGGER_T));
+ break;
+ default:
+ ioc_err(ioc, "%d - Is not a valid Trigger type\n",
+ event_data->trigger_type);
+ break;
+ }
+ }
+ _mpt3sas_raise_sigio(ioc, event_data);
+
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: exit\n",
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_master - Master trigger handler
+ * @ioc: per adapter object
+ * @trigger_bitmask:
+ *
+ */
+void
+mpt3sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, u32 trigger_bitmask)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ unsigned long flags;
+ u8 found_match = 0;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT ||
+ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET)
+ goto by_pass_checks;
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ by_pass_checks:
+
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: enter - trigger_bitmask = 0x%08x\n",
+ __func__, trigger_bitmask));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ if (ioc->diag_trigger_master.MasterData & trigger_bitmask) {
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: setting diag_trigger_active flag\n",
+ __func__));
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_MASTER;
+ event_data.u.master.MasterData = trigger_bitmask;
+
+ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT ||
+ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET) {
+ ioc->htb_rel.trigger_type = MPT3SAS_TRIGGER_MASTER;
+ ioc->htb_rel.trigger_info_dwords[0] = trigger_bitmask;
+ if (ioc->reset_from_user)
+ ioc->htb_rel.trigger_info_dwords[1] =
+ MPT_DIAG_RESET_ISSUED_BY_USER;
+ _mpt3sas_raise_sigio(ioc, &event_data);
+ } else
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+
+ out:
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: exit\n",
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_event - Event trigger handler
+ * @ioc: per adapter object
+ * @event: ?
+ * @log_entry_qualifier: ?
+ *
+ */
+void
+mpt3sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event,
+ u16 log_entry_qualifier)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ struct SL_WH_EVENT_TRIGGER_T *event_trigger;
+ int i;
+ unsigned long flags;
+ u8 found_match;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: enter - event = 0x%04x, log_entry_qualifier = 0x%04x\n",
+ __func__, event, log_entry_qualifier));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ event_trigger = ioc->diag_trigger_event.EventTriggerEntry;
+ for (i = 0 , found_match = 0; i < ioc->diag_trigger_event.ValidEntries
+ && !found_match; i++, event_trigger++) {
+ if (event_trigger->EventValue != event)
+ continue;
+ if (event == MPI2_EVENT_LOG_ENTRY_ADDED) {
+ if (event_trigger->LogEntryQualifier ==
+ log_entry_qualifier)
+ found_match = 1;
+ continue;
+ }
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: setting diag_trigger_active flag\n",
+ __func__));
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: setting diag_trigger_active flag\n",
+ __func__));
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_EVENT;
+ event_data.u.event.EventValue = event;
+ event_data.u.event.LogEntryQualifier = log_entry_qualifier;
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+ out:
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: exit\n",
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_scsi - SCSI trigger handler
+ * @ioc: per adapter object
+ * @sense_key: ?
+ * @asc: ?
+ * @ascq: ?
+ *
+ */
+void
+mpt3sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, u8 asc,
+ u8 ascq)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ struct SL_WH_SCSI_TRIGGER_T *scsi_trigger;
+ int i;
+ unsigned long flags;
+ u8 found_match;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: enter - sense_key = 0x%02x, asc = 0x%02x, ascq = 0x%02x\n",
+ __func__, sense_key, asc, ascq));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ scsi_trigger = ioc->diag_trigger_scsi.SCSITriggerEntry;
+ for (i = 0 , found_match = 0; i < ioc->diag_trigger_scsi.ValidEntries
+ && !found_match; i++, scsi_trigger++) {
+ if (scsi_trigger->SenseKey != sense_key)
+ continue;
+ if (!(scsi_trigger->ASC == 0xFF || scsi_trigger->ASC == asc))
+ continue;
+ if (!(scsi_trigger->ASCQ == 0xFF || scsi_trigger->ASCQ == ascq))
+ continue;
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: setting diag_trigger_active flag\n",
+ __func__));
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_SCSI;
+ event_data.u.scsi.SenseKey = sense_key;
+ event_data.u.scsi.ASC = asc;
+ event_data.u.scsi.ASCQ = ascq;
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+ out:
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: exit\n",
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_mpi - MPI trigger handler
+ * @ioc: per adapter object
+ * @ioc_status: ?
+ * @loginfo: ?
+ *
+ */
+void
+mpt3sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status, u32 loginfo)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ struct SL_WH_MPI_TRIGGER_T *mpi_trigger;
+ int i;
+ unsigned long flags;
+ u8 found_match;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: enter - ioc_status = 0x%04x, loginfo = 0x%08x\n",
+ __func__, ioc_status, loginfo));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ mpi_trigger = ioc->diag_trigger_mpi.MPITriggerEntry;
+ for (i = 0 , found_match = 0; i < ioc->diag_trigger_mpi.ValidEntries
+ && !found_match; i++, mpi_trigger++) {
+ if (mpi_trigger->IOCStatus != ioc_status)
+ continue;
+ if (!(mpi_trigger->IocLogInfo == 0xFFFFFFFF ||
+ mpi_trigger->IocLogInfo == loginfo))
+ continue;
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ dTriggerDiagPrintk(ioc,
+ ioc_info(ioc, "%s: setting diag_trigger_active flag\n",
+ __func__));
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_MPI;
+ event_data.u.mpi.IOCStatus = ioc_status;
+ event_data.u.mpi.IocLogInfo = loginfo;
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+ out:
+ dTriggerDiagPrintk(ioc, ioc_info(ioc, "%s: exit\n",
+ __func__));
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h
new file mode 100644
index 000000000000..405eada2669d
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h
@@ -0,0 +1,194 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * to set Diagnostic triggers for MPT (Message Passing Technology) based
+ * controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2014 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+ /* Diagnostic Trigger Configuration Data Structures */
+
+#ifndef MPT3SAS_TRIGGER_DIAG_H_INCLUDED
+#define MPT3SAS_TRIGGER_DIAG_H_INCLUDED
+
+/* limitation on number of entries */
+#define NUM_VALID_ENTRIES (20)
+
+/* trigger types */
+#define MPT3SAS_TRIGGER_MASTER (1)
+#define MPT3SAS_TRIGGER_EVENT (2)
+#define MPT3SAS_TRIGGER_SCSI (3)
+#define MPT3SAS_TRIGGER_MPI (4)
+
+/* trigger names */
+#define MASTER_TRIGGER_FILE_NAME "diag_trigger_master"
+#define EVENT_TRIGGERS_FILE_NAME "diag_trigger_event"
+#define SCSI_TRIGGERS_FILE_NAME "diag_trigger_scsi"
+#define MPI_TRIGGER_FILE_NAME "diag_trigger_mpi"
+
+/* master trigger bitmask */
+#define MASTER_TRIGGER_FW_FAULT (0x00000001)
+#define MASTER_TRIGGER_ADAPTER_RESET (0x00000002)
+#define MASTER_TRIGGER_TASK_MANAGMENT (0x00000004)
+#define MASTER_TRIGGER_DEVICE_REMOVAL (0x00000008)
+
+/* fake firmware event for trigger */
+#define MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED (0x6E)
+
+/**
+ * MasterTrigger is a single U32 passed to/from sysfs.
+ *
+ * Bit Flags (enables) include:
+ * 1. FW Faults
+ * 2. Adapter Reset issued by driver
+ * 3. TMs
+ * 4. Device Remove Event sent by FW
+ */
+
+struct SL_WH_MASTER_TRIGGER_T {
+ uint32_t MasterData;
+};
+
+/**
+ * struct SL_WH_EVENT_TRIGGER_T - Definition of an event trigger element
+ * @EventValue: Event Code to trigger on
+ * @LogEntryQualifier: Type of FW event that logged (Log Entry Added Event only)
+ *
+ * Defines an event that should induce a DIAG_TRIGGER driver event if observed.
+ */
+struct SL_WH_EVENT_TRIGGER_T {
+ uint16_t EventValue;
+ uint16_t LogEntryQualifier;
+};
+
+/**
+ * struct SL_WH_EVENT_TRIGGERS_T - Structure passed to/from sysfs containing a
+ * list of Event Triggers to be monitored for.
+ * @ValidEntries: Number of _SL_WH_EVENT_TRIGGER_T structures contained in this
+ * structure.
+ * @EventTriggerEntry: List of Event trigger elements.
+ *
+ * This binary structure is transferred via sysfs to get/set Event Triggers
+ * in the Linux Driver.
+ */
+
+struct SL_WH_EVENT_TRIGGERS_T {
+ uint32_t ValidEntries;
+ struct SL_WH_EVENT_TRIGGER_T EventTriggerEntry[NUM_VALID_ENTRIES];
+};
+
+/**
+ * struct SL_WH_SCSI_TRIGGER_T - Definition of a SCSI trigger element
+ * @ASCQ: Additional Sense Code Qualifier. Can be specific or 0xFF for
+ * wildcard.
+ * @ASC: Additional Sense Code. Can be specific or 0xFF for wildcard
+ * @SenseKey: SCSI Sense Key
+ *
+ * Defines a sense key (single or many variants) that should induce a
+ * DIAG_TRIGGER driver event if observed.
+ */
+struct SL_WH_SCSI_TRIGGER_T {
+ U8 ASCQ;
+ U8 ASC;
+ U8 SenseKey;
+ U8 Reserved;
+};
+
+/**
+ * struct SL_WH_SCSI_TRIGGERS_T - Structure passed to/from sysfs containing a
+ * list of SCSI sense codes that should trigger a DIAG_SERVICE event when
+ * observed.
+ * @ValidEntries: Number of _SL_WH_SCSI_TRIGGER_T structures contained in this
+ * structure.
+ * @SCSITriggerEntry: List of SCSI Sense Code trigger elements.
+ *
+ * This binary structure is transferred via sysfs to get/set SCSI Sense Code
+ * Triggers in the Linux Driver.
+ */
+struct SL_WH_SCSI_TRIGGERS_T {
+ uint32_t ValidEntries;
+ struct SL_WH_SCSI_TRIGGER_T SCSITriggerEntry[NUM_VALID_ENTRIES];
+};
+
+/**
+ * struct SL_WH_MPI_TRIGGER_T - Definition of an MPI trigger element
+ * @IOCStatus: MPI IOCStatus
+ * @IocLogInfo: MPI IocLogInfo. Can be specific or 0xFFFFFFFF for wildcard
+ *
+ * Defines a MPI IOCStatus/IocLogInfo pair that should induce a DIAG_TRIGGER
+ * driver event if observed.
+ */
+struct SL_WH_MPI_TRIGGER_T {
+ uint16_t IOCStatus;
+ uint16_t Reserved;
+ uint32_t IocLogInfo;
+};
+
+/**
+ * struct SL_WH_MPI_TRIGGERS_T - Structure passed to/from sysfs containing a
+ * list of MPI IOCStatus/IocLogInfo pairs that should trigger a DIAG_SERVICE
+ * event when observed.
+ * @ValidEntries: Number of _SL_WH_MPI_TRIGGER_T structures contained in this
+ * structure.
+ * @MPITriggerEntry: List of MPI IOCStatus/IocLogInfo trigger elements.
+ *
+ * This binary structure is transferred via sysfs to get/set MPI Error Triggers
+ * in the Linux Driver.
+ */
+struct SL_WH_MPI_TRIGGERS_T {
+ uint32_t ValidEntries;
+ struct SL_WH_MPI_TRIGGER_T MPITriggerEntry[NUM_VALID_ENTRIES];
+};
+
+/**
+ * struct SL_WH_TRIGGERS_EVENT_DATA_T - event data for trigger
+ * @trigger_type: trigger type (see MPT3SAS_TRIGGER_XXXX)
+ * @u: trigger condition that caused trigger to be sent
+ */
+struct SL_WH_TRIGGERS_EVENT_DATA_T {
+ uint32_t trigger_type;
+ union {
+ struct SL_WH_MASTER_TRIGGER_T master;
+ struct SL_WH_EVENT_TRIGGER_T event;
+ struct SL_WH_SCSI_TRIGGER_T scsi;
+ struct SL_WH_MPI_TRIGGER_T mpi;
+ } u;
+};
+#endif /* MPT3SAS_TRIGGER_DIAG_H_INCLUDED */
diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h b/drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h
new file mode 100644
index 000000000000..edb8fe709089
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * to store diag trigger values into persistent driver triggers pages
+ * for MPT (Message Passing Technology) based controllers.
+ *
+ * Copyright (C) 2020 Broadcom Inc.
+ *
+ * Authors: Broadcom Inc.
+ * Sreekanth Reddy <sreekanth.reddy@broadcom.com>
+ *
+ * Send feedback to : MPT-FusionLinux.pdl@broadcom.com)
+ */
+
+#include "mpi/mpi2_cnfg.h"
+
+#ifndef MPI2_TRIGGER_PAGES_H
+#define MPI2_TRIGGER_PAGES_H
+
+#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER (0xE0)
+#define MPI26_DRIVER_TRIGGER_PAGE0_PAGEVERSION (0x01)
+typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 TriggerFlags; /* 0x08 */
+ U16 Reserved0xA; /* 0x0A */
+ U32 Reserved0xC[61]; /* 0x0C */
+} _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_0, Mpi26DriverTriggerPage0_t;
+
+/* Trigger Flags */
+#define MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID (0x0001)
+#define MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID (0x0002)
+#define MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID (0x0004)
+#define MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID (0x0008)
+
+#define MPI26_DRIVER_TRIGGER_PAGE1_PAGEVERSION (0x01)
+typedef struct _MPI26_DRIVER_MASTER_TRIGGER_ENTRY {
+ U32 MasterTriggerFlags;
+} MPI26_DRIVER_MASTER_TRIGGER_ENTRY;
+
+#define MPI26_MAX_MASTER_TRIGGERS (1)
+typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 NumMasterTrigger; /* 0x08 */
+ U16 Reserved0xA; /* 0x0A */
+ MPI26_DRIVER_MASTER_TRIGGER_ENTRY MasterTriggers[MPI26_MAX_MASTER_TRIGGERS]; /* 0x0C */
+} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_1, Mpi26DriverTriggerPage1_t;
+
+#define MPI26_DRIVER_TRIGGER_PAGE2_PAGEVERSION (0x01)
+typedef struct _MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY {
+ U16 MPIEventCode; /* 0x00 */
+ U16 MPIEventCodeSpecific; /* 0x02 */
+} MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY;
+
+#define MPI26_MAX_MPI_EVENT_TRIGGERS (20)
+typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_2 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 NumMPIEventTrigger; /* 0x08 */
+ U16 Reserved0xA; /* 0x0A */
+ MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY MPIEventTriggers[MPI26_MAX_MPI_EVENT_TRIGGERS]; /* 0x0C */
+} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_2, Mpi26DriverTriggerPage2_t;
+
+#define MPI26_DRIVER_TRIGGER_PAGE3_PAGEVERSION (0x01)
+typedef struct _MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY {
+ U8 ASCQ; /* 0x00 */
+ U8 ASC; /* 0x01 */
+ U8 SenseKey; /* 0x02 */
+ U8 Reserved; /* 0x03 */
+} MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY;
+
+#define MPI26_MAX_SCSI_SENSE_TRIGGERS (20)
+typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_3 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 NumSCSISenseTrigger; /* 0x08 */
+ U16 Reserved0xA; /* 0x0A */
+ MPI26_DRIVER_SCSI_SENSE_TRIGGER_ENTRY SCSISenseTriggers[MPI26_MAX_SCSI_SENSE_TRIGGERS]; /* 0x0C */
+} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_3, Mpi26DriverTriggerPage3_t;
+
+#define MPI26_DRIVER_TRIGGER_PAGE4_PAGEVERSION (0x01)
+typedef struct _MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY {
+ U16 IOCStatus; /* 0x00 */
+ U16 Reserved; /* 0x02 */
+ U32 LogInfo; /* 0x04 */
+} MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY;
+
+#define MPI26_MAX_LOGINFO_TRIGGERS (20)
+typedef struct _MPI26_CONFIG_PAGE_DRIVER_TRIGGER_4 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U16 NumIOCStatusLogInfoTrigger; /* 0x08 */
+ U16 Reserved0xA; /* 0x0A */
+ MPI26_DRIVER_IOCSTATUS_LOGINFO_TRIGGER_ENTRY IOCStatusLoginfoTriggers[MPI26_MAX_LOGINFO_TRIGGERS]; /* 0x0C */
+} MPI26_CONFIG_PAGE_DRIVER_TRIGGER_4, Mpi26DriverTriggerPage4_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c b/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c
new file mode 100644
index 000000000000..2b04f0852dec
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c
@@ -0,0 +1,298 @@
+/*
+ * Scsi Host Layer for MPT (Message Passing Technology) based controllers
+ *
+ * Copyright (C) 2012-2014 LSI Corporation
+ * Copyright (C) 2013-2015 Avago Technologies
+ * (mailto: MPT-FusionLinux.pdl@avagotech.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "mpt3sas_base.h"
+
+/**
+ * _warpdrive_disable_ddio - Disable direct I/O for all the volumes
+ * @ioc: per adapter object
+ */
+static void
+_warpdrive_disable_ddio(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2RaidVolPage1_t vol_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ struct _raid_device *raid_device;
+ u16 handle;
+ u16 ioc_status;
+ unsigned long flags;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ handle = le16_to_cpu(vol_pg1.DevHandle);
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle);
+ if (raid_device)
+ raid_device->direct_io_enabled = 0;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ }
+ return;
+}
+
+
+/**
+ * mpt3sas_get_num_volumes - Get number of volumes in the ioc
+ * @ioc: per adapter object
+ */
+u8
+mpt3sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2RaidVolPage1_t vol_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 handle;
+ u8 vol_cnt = 0;
+ u16 ioc_status;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ vol_cnt++;
+ handle = le16_to_cpu(vol_pg1.DevHandle);
+ }
+ return vol_cnt;
+}
+
+
+/**
+ * mpt3sas_init_warpdrive_properties - Set properties for warpdrive direct I/O.
+ * @ioc: per adapter object
+ * @raid_device: the raid_device object
+ */
+void
+mpt3sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ Mpi2RaidVolPage0_t *vol_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 sz;
+ u8 num_pds, count;
+ unsigned long stripe_sz, block_sz;
+ u8 stripe_exp, block_exp;
+ u64 dev_max_lba;
+
+ if (!ioc->is_warpdrive)
+ return;
+
+ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled globally as drives are exposed\n");
+ return;
+ }
+ if (mpt3sas_get_num_volumes(ioc) > 1) {
+ _warpdrive_disable_ddio(ioc);
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled globally as number of drives > 1\n");
+ return;
+ }
+ if ((mpt3sas_config_get_number_pds(ioc, raid_device->handle,
+ &num_pds)) || !num_pds) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled Failure in computing number of drives\n");
+ return;
+ }
+
+ sz = struct_size(vol_pg0, PhysDisk, num_pds);
+ vol_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!vol_pg0) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled Memory allocation failure for RVPG0\n");
+ return;
+ }
+
+ if ((mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled Failure in retrieving RVPG0\n");
+ kfree(vol_pg0);
+ return;
+ }
+
+ /*
+ * WARPDRIVE:If number of physical disks in a volume exceeds the max pds
+ * assumed for WARPDRIVE, disable direct I/O
+ */
+ if (num_pds > MPT_MAX_WARPDRIVE_PDS) {
+ ioc_warn(ioc, "WarpDrive : Direct IO is disabled for the drive with handle(0x%04x): num_mem=%d, max_mem_allowed=%d\n",
+ raid_device->handle, num_pds, MPT_MAX_WARPDRIVE_PDS);
+ kfree(vol_pg0);
+ return;
+ }
+ for (count = 0; count < num_pds; count++) {
+ if (mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
+ vol_pg0->PhysDisk[count].PhysDiskNum) ||
+ le16_to_cpu(pd_pg0.DevHandle) ==
+ MPT3SAS_INVALID_DEVICE_HANDLE) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled for the drive with handle(0x%04x) member handle retrieval failed for member number=%d\n",
+ raid_device->handle,
+ vol_pg0->PhysDisk[count].PhysDiskNum);
+ goto out_error;
+ }
+ /* Disable direct I/O if member drive lba exceeds 4 bytes */
+ dev_max_lba = le64_to_cpu(pd_pg0.DeviceMaxLBA);
+ if (dev_max_lba >> 32) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled for the drive with handle(0x%04x) member handle (0x%04x) unsupported max lba 0x%016llx\n",
+ raid_device->handle,
+ le16_to_cpu(pd_pg0.DevHandle),
+ (u64)dev_max_lba);
+ goto out_error;
+ }
+
+ raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle);
+ }
+
+ /*
+ * Assumption for WD: Direct I/O is not supported if the volume is
+ * not RAID0
+ */
+ if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled for the drive with handle(0x%04x): type=%d, s_sz=%uK, blk_size=%u\n",
+ raid_device->handle, raid_device->volume_type,
+ (le32_to_cpu(vol_pg0->StripeSize) *
+ le16_to_cpu(vol_pg0->BlockSize)) / 1024,
+ le16_to_cpu(vol_pg0->BlockSize));
+ goto out_error;
+ }
+
+ stripe_sz = le32_to_cpu(vol_pg0->StripeSize);
+ stripe_exp = find_first_bit(&stripe_sz, 32);
+ if (stripe_exp == 32) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled for the drive with handle(0x%04x) invalid stripe sz %uK\n",
+ raid_device->handle,
+ (le32_to_cpu(vol_pg0->StripeSize) *
+ le16_to_cpu(vol_pg0->BlockSize)) / 1024);
+ goto out_error;
+ }
+ raid_device->stripe_exponent = stripe_exp;
+ block_sz = le16_to_cpu(vol_pg0->BlockSize);
+ block_exp = find_first_bit(&block_sz, 16);
+ if (block_exp == 16) {
+ ioc_info(ioc, "WarpDrive : Direct IO is disabled for the drive with handle(0x%04x) invalid block sz %u\n",
+ raid_device->handle, le16_to_cpu(vol_pg0->BlockSize));
+ goto out_error;
+ }
+ raid_device->block_exponent = block_exp;
+ raid_device->direct_io_enabled = 1;
+
+ ioc_info(ioc, "WarpDrive : Direct IO is Enabled for the drive with handle(0x%04x)\n",
+ raid_device->handle);
+ /*
+ * WARPDRIVE: Though the following fields are not used for direct IO,
+ * stored for future purpose:
+ */
+ raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA);
+ raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize);
+ raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize);
+
+
+ kfree(vol_pg0);
+ return;
+
+out_error:
+ raid_device->direct_io_enabled = 0;
+ for (count = 0; count < num_pds; count++)
+ raid_device->pd_handle[count] = 0;
+ kfree(vol_pg0);
+ return;
+}
+
+/**
+ * mpt3sas_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ * @raid_device: pointer to raid device data structure
+ * @mpi_request: pointer to the SCSI_IO reqest message frame
+ */
+void
+mpt3sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ struct _raid_device *raid_device, Mpi25SCSIIORequest_t *mpi_request)
+{
+ sector_t v_lba, p_lba, stripe_off, column, io_size;
+ u32 stripe_sz, stripe_exp;
+ u8 num_pds, cmd = scmd->cmnd[0];
+ struct scsiio_tracker *st = scsi_cmd_priv(scmd);
+
+ if (cmd != READ_10 && cmd != WRITE_10 &&
+ cmd != READ_16 && cmd != WRITE_16)
+ return;
+
+ if (cmd == READ_10 || cmd == WRITE_10)
+ v_lba = get_unaligned_be32(&mpi_request->CDB.CDB32[2]);
+ else
+ v_lba = get_unaligned_be64(&mpi_request->CDB.CDB32[2]);
+
+ io_size = scsi_bufflen(scmd) >> raid_device->block_exponent;
+
+ if (v_lba + io_size - 1 > raid_device->max_lba)
+ return;
+
+ stripe_sz = raid_device->stripe_sz;
+ stripe_exp = raid_device->stripe_exponent;
+ stripe_off = v_lba & (stripe_sz - 1);
+
+ /* Return unless IO falls within a stripe */
+ if (stripe_off + io_size > stripe_sz)
+ return;
+
+ num_pds = raid_device->num_pds;
+ p_lba = v_lba >> stripe_exp;
+ column = sector_div(p_lba, num_pds);
+ p_lba = (p_lba << stripe_exp) + stripe_off;
+ mpi_request->DevHandle = cpu_to_le16(raid_device->pd_handle[column]);
+
+ if (cmd == READ_10 || cmd == WRITE_10)
+ put_unaligned_be32(lower_32_bits(p_lba),
+ &mpi_request->CDB.CDB32[2]);
+ else
+ put_unaligned_be64(p_lba, &mpi_request->CDB.CDB32[2]);
+
+ st->direct_io = 1;
+}
diff --git a/drivers/scsi/mvme147.c b/drivers/scsi/mvme147.c
index c29d0dbb9660..98b99c0f5bc7 100644
--- a/drivers/scsi/mvme147.c
+++ b/drivers/scsi/mvme147.c
@@ -1,21 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <asm/mvme147hw.h>
#include <asm/irq.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include "wd33c93.h"
#include "mvme147.h"
-#include <linux/stat.h>
-
-
static irqreturn_t mvme147_intr(int irq, void *data)
{
struct Scsi_Host *instance = data;
@@ -29,10 +33,11 @@ static irqreturn_t mvme147_intr(int irq, void *data)
static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
{
+ struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd);
struct Scsi_Host *instance = cmd->device->host;
struct WD33C93_hostdata *hdata = shost_priv(instance);
unsigned char flags = 0x01;
- unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+ unsigned long addr = virt_to_bus(scsi_pointer->ptr);
/* setup dma direction */
if (!dir_in)
@@ -43,14 +48,14 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
if (dir_in) {
/* invalidate any cache */
- cache_clear(addr, cmd->SCp.this_residual);
+ cache_clear(addr, scsi_pointer->this_residual);
} else {
/* push any dirty cache */
- cache_push(addr, cmd->SCp.this_residual);
+ cache_push(addr, scsi_pointer->this_residual);
}
/* start DMA */
- m147_pcc->dma_bcr = cmd->SCp.this_residual | (1 << 24);
+ m147_pcc->dma_bcr = scsi_pointer->this_residual | (1 << 24);
m147_pcc->dma_dadr = addr;
m147_pcc->dma_cntrl = flags;
@@ -64,39 +69,57 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
m147_pcc->dma_cntrl = 0;
}
-int mvme147_detect(struct scsi_host_template *tpnt)
+static const struct scsi_host_template mvme147_host_template = {
+ .module = THIS_MODULE,
+ .proc_name = "MVME147",
+ .name = "MVME147 built-in SCSI",
+ .queuecommand = wd33c93_queuecommand,
+ .eh_abort_handler = wd33c93_abort,
+ .eh_host_reset_handler = wd33c93_host_reset,
+ .show_info = wd33c93_show_info,
+ .write_info = wd33c93_write_info,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .cmd_size = sizeof(struct scsi_pointer),
+};
+
+static struct Scsi_Host *mvme147_shost;
+
+static int __init mvme147_init(void)
{
- static unsigned char called = 0;
- struct Scsi_Host *instance;
wd33c93_regs regs;
struct WD33C93_hostdata *hdata;
+ int error = -ENOMEM;
- if (!MACH_IS_MVME147 || called)
+ if (!MACH_IS_MVME147)
return 0;
- called++;
-
- tpnt->proc_name = "MVME147";
- tpnt->proc_info = &wd33c93_proc_info;
- instance = scsi_register(tpnt, sizeof(struct WD33C93_hostdata));
- if (!instance)
+ mvme147_shost = scsi_host_alloc(&mvme147_host_template,
+ sizeof(struct WD33C93_hostdata));
+ if (!mvme147_shost)
goto err_out;
+ mvme147_shost->base = 0xfffe4000;
+ mvme147_shost->irq = MVME147_IRQ_SCSI_PORT;
- instance->base = 0xfffe4000;
- instance->irq = MVME147_IRQ_SCSI_PORT;
regs.SASR = (volatile unsigned char *)0xfffe4000;
regs.SCMD = (volatile unsigned char *)0xfffe4001;
- hdata = shost_priv(instance);
+
+ hdata = shost_priv(mvme147_shost);
hdata->no_sync = 0xff;
hdata->fast = 0;
hdata->dma_mode = CTRL_DMA;
- wd33c93_init(instance, regs, dma_setup, dma_stop, WD33C93_FS_8_10);
- if (request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0,
- "MVME147 SCSI PORT", instance))
+ wd33c93_init(mvme147_shost, regs, dma_setup, dma_stop, WD33C93_FS_8_10);
+
+ error = request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0,
+ "MVME147 SCSI PORT", mvme147_shost);
+ if (error)
goto err_unregister;
- if (request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0,
- "MVME147 SCSI DMA", instance))
+ error = request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0,
+ "MVME147 SCSI DMA", mvme147_shost);
+ if (error)
goto err_free_irq;
#if 0 /* Disabled; causes problems booting */
m147_pcc->scsi_interrupt = 0x10; /* Assert SCSI bus reset */
@@ -110,56 +133,30 @@ int mvme147_detect(struct scsi_host_template *tpnt)
m147_pcc->dma_cntrl = 0x00; /* ensure DMA is stopped */
m147_pcc->dma_intr = 0x89; /* Ack and enable ints */
- return 1;
+ error = scsi_add_host(mvme147_shost, NULL);
+ if (error)
+ goto err_free_irq;
+ scsi_scan_host(mvme147_shost);
+ return 0;
err_free_irq:
- free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr);
+ free_irq(MVME147_IRQ_SCSI_PORT, mvme147_shost);
err_unregister:
- scsi_unregister(instance);
+ scsi_host_put(mvme147_shost);
err_out:
- return 0;
+ return error;
}
-static int mvme147_bus_reset(struct scsi_cmnd *cmd)
+static void __exit mvme147_exit(void)
{
- /* FIXME perform bus-specific reset */
+ scsi_remove_host(mvme147_shost);
- /* FIXME 2: kill this function, and let midlayer fallback to
- the same result, calling wd33c93_host_reset() */
-
- spin_lock_irq(cmd->device->host->host_lock);
- wd33c93_host_reset(cmd);
- spin_unlock_irq(cmd->device->host->host_lock);
+ /* XXX Make sure DMA is stopped! */
+ free_irq(MVME147_IRQ_SCSI_PORT, mvme147_shost);
+ free_irq(MVME147_IRQ_SCSI_DMA, mvme147_shost);
- return SUCCESS;
+ scsi_host_put(mvme147_shost);
}
-
-static struct scsi_host_template driver_template = {
- .proc_name = "MVME147",
- .name = "MVME147 built-in SCSI",
- .detect = mvme147_detect,
- .release = mvme147_release,
- .queuecommand = wd33c93_queuecommand,
- .eh_abort_handler = wd33c93_abort,
- .eh_bus_reset_handler = mvme147_bus_reset,
- .eh_host_reset_handler = wd33c93_host_reset,
- .can_queue = CAN_QUEUE,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING
-};
-
-
-#include "scsi_module.c"
-
-int mvme147_release(struct Scsi_Host *instance)
-{
-#ifdef MODULE
- /* XXX Make sure DMA is stopped! */
- free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr);
- free_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr);
-#endif
- return 1;
-}
+module_init(mvme147_init);
+module_exit(mvme147_exit);
diff --git a/drivers/scsi/mvme147.h b/drivers/scsi/mvme147.h
index bfd4566ef050..f75ff58ddcd0 100644
--- a/drivers/scsi/mvme147.h
+++ b/drivers/scsi/mvme147.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef MVME147_H
/* $Id: mvme147.h,v 1.4 1997/01/19 23:07:10 davem Exp $
diff --git a/drivers/scsi/mvme16x_scsi.c b/drivers/scsi/mvme16x_scsi.c
index 39f554f5f261..9b19d5205e50 100644
--- a/drivers/scsi/mvme16x_scsi.c
+++ b/drivers/scsi/mvme16x_scsi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Detection routine for the NCR53c710 based MVME16x SCSI Controllers for Linux.
*
@@ -34,8 +35,7 @@ static struct scsi_host_template mvme16x_scsi_driver_template = {
static struct platform_device *mvme16x_scsi_device;
-static __devinit int
-mvme16x_probe(struct platform_device *dev)
+static int mvme16x_probe(struct platform_device *dev)
{
struct Scsi_Host * host = NULL;
struct NCR_700_Host_Parameters *hostdata;
@@ -103,8 +103,7 @@ mvme16x_probe(struct platform_device *dev)
return -ENODEV;
}
-static __devexit int
-mvme16x_device_remove(struct platform_device *dev)
+static void mvme16x_device_remove(struct platform_device *dev)
{
struct Scsi_Host *host = platform_get_drvdata(dev);
struct NCR_700_Host_Parameters *hostdata = shost_priv(host);
@@ -121,17 +120,14 @@ mvme16x_device_remove(struct platform_device *dev)
NCR_700_release(host);
kfree(hostdata);
free_irq(host->irq, host);
-
- return 0;
}
static struct platform_driver mvme16x_scsi_driver = {
.driver = {
.name = "mvme16x-scsi",
- .owner = THIS_MODULE,
},
.probe = mvme16x_probe,
- .remove = __devexit_p(mvme16x_device_remove),
+ .remove = mvme16x_device_remove,
};
static int __init mvme16x_scsi_init(void)
diff --git a/drivers/scsi/mvsas/Kconfig b/drivers/scsi/mvsas/Kconfig
index c82b012aba37..5ac7fd593b17 100644
--- a/drivers/scsi/mvsas/Kconfig
+++ b/drivers/scsi/mvsas/Kconfig
@@ -1,33 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Kernel configuration file for 88SE64XX/88SE94XX SAS/SATA driver.
#
# Copyright 2007 Red Hat, Inc.
# Copyright 2008 Marvell. <kewei@marvell.com>
-# Copyright 2009-20011 Marvell. <yuxiangl@marvell.com>
-#
-# This file is licensed under GPLv2.
-#
-# This file is part of the 88SE64XX/88SE94XX driver.
-#
-# The 88SE64XX/88SE94XX driver is free software; you can redistribute
-# it and/or modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2 of the
-# License.
-#
-# The 88SE64XX/88SE94XX driver is distributed in the hope that it will be
-# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with 88SE64XX/88SE94XX Driver; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-#
+# Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
#
config SCSI_MVSAS
tristate "Marvell 88SE64XX/88SE94XX SAS/SATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select SCSI_SAS_LIBSAS
select FW_LOADER
help
@@ -41,3 +23,10 @@ config SCSI_MVSAS_DEBUG
help
Compiles the 88SE64XX/88SE94XX driver in debug mode. In debug mode,
the driver prints some messages to the console.
+config SCSI_MVSAS_TASKLET
+ bool "Support for interrupt tasklet"
+ default n
+ depends on SCSI_MVSAS
+ help
+ Compiles the 88SE64xx/88SE94xx driver in interrupt tasklet mode.In this mode,
+ the interrupt will schedule a tasklet.
diff --git a/drivers/scsi/mvsas/Makefile b/drivers/scsi/mvsas/Makefile
index 87b231a5bd5e..75849258e898 100644
--- a/drivers/scsi/mvsas/Makefile
+++ b/drivers/scsi/mvsas/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Marvell 88SE64xx/88SE84xx SAS/SATA driver.
#
@@ -5,22 +6,6 @@
# Copyright 2008 Marvell. <kewei@marvell.com>
# Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
#
-# This file is licensed under GPLv2.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; version 2 of the
-# License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
ccflags-$(CONFIG_SCSI_MVSAS_DEBUG) := -DMV_DEBUG
diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c
index 13c960481391..1f2b61de8c63 100644
--- a/drivers/scsi/mvsas/mv_64xx.c
+++ b/drivers/scsi/mvsas/mv_64xx.c
@@ -1,26 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Marvell 88SE64xx hardware specific
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#include "mv_sas.h"
@@ -33,7 +17,6 @@ static void mvs_64xx_detect_porttype(struct mvs_info *mvi, int i)
u32 reg;
struct mvs_phy *phy = &mvi->phy[i];
- /* TODO check & save device type */
reg = mr32(MVS_GBL_PORT_TYPE);
phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
if (reg & MODE_SAS_SATA & (1 << i))
@@ -42,40 +25,32 @@ static void mvs_64xx_detect_porttype(struct mvs_info *mvi, int i)
phy->phy_type |= PORT_TYPE_SATA;
}
-static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id)
+static void mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id)
{
void __iomem *regs = mvi->regs;
u32 tmp;
tmp = mr32(MVS_PCS);
- if (mvi->chip->n_phy <= 4)
+ if (mvi->chip->n_phy <= MVS_SOC_PORTS)
tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT);
else
tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
mw32(MVS_PCS, tmp);
}
-static void __devinit mvs_64xx_phy_hacks(struct mvs_info *mvi)
+static void mvs_64xx_phy_hacks(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs;
+ int i;
mvs_phy_hacks(mvi);
if (!(mvi->flags & MVF_FLAG_SOC)) {
- /* TEST - for phy decoding error, adjust voltage levels */
- mw32(MVS_P0_VSR_ADDR + 0, 0x8);
- mw32(MVS_P0_VSR_DATA + 0, 0x2F0);
-
- mw32(MVS_P0_VSR_ADDR + 8, 0x8);
- mw32(MVS_P0_VSR_DATA + 8, 0x2F0);
-
- mw32(MVS_P0_VSR_ADDR + 16, 0x8);
- mw32(MVS_P0_VSR_DATA + 16, 0x2F0);
-
- mw32(MVS_P0_VSR_ADDR + 24, 0x8);
- mw32(MVS_P0_VSR_DATA + 24, 0x2F0);
+ for (i = 0; i < MVS_SOC_PORTS; i++) {
+ mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE8);
+ mvs_write_port_vsr_data(mvi, i, 0x2F0);
+ }
} else {
- int i;
/* disable auto port detection */
mw32(MVS_GBL_PORT_TYPE, 0);
for (i = 0; i < mvi->chip->n_phy; i++) {
@@ -95,7 +70,7 @@ static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id)
u32 reg, tmp;
if (!(mvi->flags & MVF_FLAG_SOC)) {
- if (phy_id < 4)
+ if (phy_id < MVS_SOC_PORTS)
pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &reg);
else
pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &reg);
@@ -104,13 +79,13 @@ static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id)
reg = mr32(MVS_PHY_CTL);
tmp = reg;
- if (phy_id < 4)
+ if (phy_id < MVS_SOC_PORTS)
tmp |= (1U << phy_id) << PCTL_LINK_OFFS;
else
- tmp |= (1U << (phy_id - 4)) << PCTL_LINK_OFFS;
+ tmp |= (1U << (phy_id - MVS_SOC_PORTS)) << PCTL_LINK_OFFS;
if (!(mvi->flags & MVF_FLAG_SOC)) {
- if (phy_id < 4) {
+ if (phy_id < MVS_SOC_PORTS) {
pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
mdelay(10);
pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, reg);
@@ -133,9 +108,9 @@ static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
tmp &= ~PHYEV_RDY_CH;
mvs_write_port_irq_stat(mvi, phy_id, tmp);
tmp = mvs_read_phy_ctl(mvi, phy_id);
- if (hard == 1)
+ if (hard == MVS_HARD_RESET)
tmp |= PHY_RST_HARD;
- else if (hard == 0)
+ else if (hard == MVS_SOFT_RESET)
tmp |= PHY_RST;
mvs_write_phy_ctl(mvi, phy_id, tmp);
if (hard) {
@@ -145,7 +120,8 @@ static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
}
}
-void mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
+static void
+mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
{
void __iomem *regs = mvi->regs;
u32 tmp;
@@ -165,7 +141,7 @@ void mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
}
}
-static int __devinit mvs_64xx_chip_reset(struct mvs_info *mvi)
+static int mvs_64xx_chip_reset(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs;
u32 tmp;
@@ -259,7 +235,7 @@ static void mvs_64xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
}
}
-static int __devinit mvs_64xx_init(struct mvs_info *mvi)
+static int mvs_64xx_init(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs;
int i;
@@ -321,6 +297,11 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi)
/* init phys */
mvs_64xx_phy_hacks(mvi);
+ tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
+ tmp &= 0x0000ffff;
+ tmp |= 0x00fa0000;
+ mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
+
/* enable auto port detection */
mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN);
@@ -346,7 +327,7 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi)
mvs_64xx_enable_xmt(mvi, i);
- mvs_64xx_phy_reset(mvi, i, 1);
+ mvs_64xx_phy_reset(mvi, i, MVS_HARD_RESET);
msleep(500);
mvs_64xx_detect_porttype(mvi, i);
}
@@ -377,13 +358,7 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi)
mvs_update_phyinfo(mvi, i, 1);
}
- /* FIXME: update wide port bitmaps */
-
/* little endian for open address and command table, etc. */
- /*
- * it seems that ( from the spec ) turning on big-endian won't
- * do us any good on big-endian machines, need further confirmation
- */
cctl = mr32(MVS_CTL);
cctl |= CCTL_ENDIAN_CMD;
cctl |= CCTL_ENDIAN_DATA;
@@ -394,15 +369,19 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi)
/* reset CMD queue */
tmp = mr32(MVS_PCS);
tmp |= PCS_CMD_RST;
+ tmp &= ~PCS_SELF_CLEAR;
mw32(MVS_PCS, tmp);
- /* interrupt coalescing may cause missing HW interrput in some case,
- * and the max count is 0x1ff, while our max slot is 0x200,
+ /*
+ * the max count is 0x1ff, while our max slot is 0x200,
* it will make count 0.
*/
tmp = 0;
- mw32(MVS_INT_COAL, tmp);
+ if (MVS_CHIP_SLOT_SZ > 0x1ff)
+ mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
+ else
+ mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
- tmp = 0x100;
+ tmp = 0x10000 | interrupt_coalescing;
mw32(MVS_INT_COAL_TMOUT, tmp);
/* ladies and gentlemen, start your engines */
@@ -477,13 +456,11 @@ static irqreturn_t mvs_64xx_isr(struct mvs_info *mvi, int irq, u32 stat)
/* clear CMD_CMPLT ASAP */
mw32_f(MVS_INT_STAT, CINT_DONE);
-#ifndef MVS_USE_TASKLET
+
spin_lock(&mvi->lock);
-#endif
mvs_int_full(mvi);
-#ifndef MVS_USE_TASKLET
spin_unlock(&mvi->lock);
-#endif
+
return IRQ_HANDLED;
}
@@ -571,7 +548,7 @@ static u8 mvs_64xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
return MVS_ID_NOT_MAPPED;
}
-void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
+static void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
{
int i;
struct scatterlist *sg;
@@ -630,7 +607,6 @@ static void mvs_64xx_phy_work_around(struct mvs_info *mvi, int i)
{
u32 tmp;
struct mvs_phy *phy = &mvi->phy[i];
- /* workaround for HW phy decoding error on 1.5g disk drive */
mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6);
tmp = mvs_read_port_vsr_data(mvi, i);
if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
@@ -642,7 +618,7 @@ static void mvs_64xx_phy_work_around(struct mvs_info *mvi, int i)
mvs_write_port_vsr_data(mvi, i, tmp);
}
-void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
+static void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
struct sas_phy_linkrates *rates)
{
u32 lrmin = 0, lrmax = 0;
@@ -661,7 +637,7 @@ void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
tmp |= lrmax;
}
mvs_write_phy_ctl(mvi, phy_id, tmp);
- mvs_64xx_phy_reset(mvi, phy_id, 1);
+ mvs_64xx_phy_reset(mvi, phy_id, MVS_HARD_RESET);
}
static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi)
@@ -677,20 +653,21 @@ static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi)
}
-u32 mvs_64xx_spi_read_data(struct mvs_info *mvi)
+static u32 mvs_64xx_spi_read_data(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs_ex;
return ior32(SPI_DATA_REG_64XX);
}
-void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data)
+static void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data)
{
void __iomem *regs = mvi->regs_ex;
- iow32(SPI_DATA_REG_64XX, data);
+
+ iow32(SPI_DATA_REG_64XX, data);
}
-int mvs_64xx_spi_buildcmd(struct mvs_info *mvi,
+static int mvs_64xx_spi_buildcmd(struct mvs_info *mvi,
u32 *dwCmd,
u8 cmd,
u8 read,
@@ -714,7 +691,7 @@ int mvs_64xx_spi_buildcmd(struct mvs_info *mvi,
}
-int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
+static int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
{
void __iomem *regs = mvi->regs_ex;
int retry;
@@ -729,7 +706,7 @@ int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
return 0;
}
-int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
+static int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
{
void __iomem *regs = mvi->regs_ex;
u32 i, dwTmp;
@@ -744,11 +721,13 @@ int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
return -1;
}
-#ifndef DISABLE_HOTPLUG_DMA_FIX
-void mvs_64xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+static void mvs_64xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
+ int buf_len, int from, void *prd)
{
int i;
struct mvs_prd *buf_prd = prd;
+ dma_addr_t buf_dma = mvi->bulk_buffer_dma;
+
buf_prd += from;
for (i = 0; i < MAX_SG_ENTRY - from; i++) {
buf_prd->addr = cpu_to_le64(buf_dma);
@@ -756,7 +735,28 @@ void mvs_64xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
++buf_prd;
}
}
-#endif
+
+static void mvs_64xx_tune_interrupt(struct mvs_info *mvi, u32 time)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp = 0;
+ /*
+ * the max count is 0x1ff, while our max slot is 0x200,
+ * it will make count 0.
+ */
+ if (time == 0) {
+ mw32(MVS_INT_COAL, 0);
+ mw32(MVS_INT_COAL_TMOUT, 0x10000);
+ } else {
+ if (MVS_CHIP_SLOT_SZ > 0x1ff)
+ mw32(MVS_INT_COAL, 0x1ff|COAL_EN);
+ else
+ mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN);
+
+ tmp = 0x10000 | time;
+ mw32(MVS_INT_COAL_TMOUT, tmp);
+ }
+}
const struct mvs_dispatch mvs_64xx_dispatch = {
"mv64xx",
@@ -780,7 +780,6 @@ const struct mvs_dispatch mvs_64xx_dispatch = {
mvs_write_port_irq_stat,
mvs_read_port_irq_mask,
mvs_write_port_irq_mask,
- mvs_get_sas_addr,
mvs_64xx_command_active,
mvs_64xx_clear_srs_irq,
mvs_64xx_issue_stop,
@@ -808,8 +807,8 @@ const struct mvs_dispatch mvs_64xx_dispatch = {
mvs_64xx_spi_buildcmd,
mvs_64xx_spi_issuecmd,
mvs_64xx_spi_waitdataready,
-#ifndef DISABLE_HOTPLUG_DMA_FIX
mvs_64xx_fix_dma,
-#endif
+ mvs_64xx_tune_interrupt,
+ NULL,
};
diff --git a/drivers/scsi/mvsas/mv_64xx.h b/drivers/scsi/mvsas/mv_64xx.h
index 545889bd9753..749f616b21af 100644
--- a/drivers/scsi/mvsas/mv_64xx.h
+++ b/drivers/scsi/mvsas/mv_64xx.h
@@ -1,26 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Marvell 88SE64xx hardware specific head file
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#ifndef _MVS64XX_REG_H_
@@ -117,8 +101,8 @@ enum sas_sata_vsp_regs {
VSR_PHY_MODE9 = 0x09, /* Test */
VSR_PHY_MODE10 = 0x0A, /* Power */
VSR_PHY_MODE11 = 0x0B, /* Phy Mode */
- VSR_PHY_VS0 = 0x0C, /* Vednor Specific 0 */
- VSR_PHY_VS1 = 0x0D, /* Vednor Specific 1 */
+ VSR_PHY_VS0 = 0x0C, /* Vendor Specific 0 */
+ VSR_PHY_VS1 = 0x0D, /* Vendor Specific 1 */
};
enum chip_register_bits {
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 78162c3c36e6..fc0b8eb68204 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -1,26 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Marvell 88SE94xx hardware specific
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#include "mv_sas.h"
@@ -48,7 +32,216 @@ static void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i)
}
}
-static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
+static void set_phy_tuning(struct mvs_info *mvi, int phy_id,
+ struct phy_tuning phy_tuning)
+{
+ u32 tmp, setting_0 = 0, setting_1 = 0;
+ u8 i;
+
+ /* Remap information for B0 chip:
+ *
+ * R0Ch -> R118h[15:0] (Adapted DFE F3 - F5 coefficient)
+ * R0Dh -> R118h[31:16] (Generation 1 Setting 0)
+ * R0Eh -> R11Ch[15:0] (Generation 1 Setting 1)
+ * R0Fh -> R11Ch[31:16] (Generation 2 Setting 0)
+ * R10h -> R120h[15:0] (Generation 2 Setting 1)
+ * R11h -> R120h[31:16] (Generation 3 Setting 0)
+ * R12h -> R124h[15:0] (Generation 3 Setting 1)
+ * R13h -> R124h[31:16] (Generation 4 Setting 0 (Reserved))
+ */
+
+ /* A0 has a different set of registers */
+ if (mvi->pdev->revision == VANIR_A0_REV)
+ return;
+
+ for (i = 0; i < 3; i++) {
+ /* loop 3 times, set Gen 1, Gen 2, Gen 3 */
+ switch (i) {
+ case 0:
+ setting_0 = GENERATION_1_SETTING;
+ setting_1 = GENERATION_1_2_SETTING;
+ break;
+ case 1:
+ setting_0 = GENERATION_1_2_SETTING;
+ setting_1 = GENERATION_2_3_SETTING;
+ break;
+ case 2:
+ setting_0 = GENERATION_2_3_SETTING;
+ setting_1 = GENERATION_3_4_SETTING;
+ break;
+ }
+
+ /* Set:
+ *
+ * Transmitter Emphasis Enable
+ * Transmitter Emphasis Amplitude
+ * Transmitter Amplitude
+ */
+ mvs_write_port_vsr_addr(mvi, phy_id, setting_0);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp &= ~(0xFBE << 16);
+ tmp |= (((phy_tuning.trans_emp_en << 11) |
+ (phy_tuning.trans_emp_amp << 7) |
+ (phy_tuning.trans_amp << 1)) << 16);
+ mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+ /* Set Transmitter Amplitude Adjust */
+ mvs_write_port_vsr_addr(mvi, phy_id, setting_1);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp &= ~(0xC000);
+ tmp |= (phy_tuning.trans_amp_adj << 14);
+ mvs_write_port_vsr_data(mvi, phy_id, tmp);
+ }
+}
+
+static void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id,
+ struct ffe_control ffe)
+{
+ u32 tmp;
+
+ /* Don't run this if A0/B0 */
+ if ((mvi->pdev->revision == VANIR_A0_REV)
+ || (mvi->pdev->revision == VANIR_B0_REV))
+ return;
+
+ /* FFE Resistor and Capacitor */
+ /* R10Ch DFE Resolution Control/Squelch and FFE Setting
+ *
+ * FFE_FORCE [7]
+ * FFE_RES_SEL [6:4]
+ * FFE_CAP_SEL [3:0]
+ */
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_FFE_CONTROL);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp &= ~0xFF;
+
+ /* Read from HBA_Info_Page */
+ tmp |= ((0x1 << 7) |
+ (ffe.ffe_rss_sel << 4) |
+ (ffe.ffe_cap_sel << 0));
+
+ mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+ /* R064h PHY Mode Register 1
+ *
+ * DFE_DIS 18
+ */
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp &= ~0x40001;
+ /* Hard coding */
+ /* No defines in HBA_Info_Page */
+ tmp |= (0 << 18);
+ mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+ /* R110h DFE F0-F1 Coefficient Control/DFE Update Control
+ *
+ * DFE_UPDATE_EN [11:6]
+ * DFE_FX_FORCE [5:0]
+ */
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_DFE_UPDATE_CRTL);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp &= ~0xFFF;
+ /* Hard coding */
+ /* No defines in HBA_Info_Page */
+ tmp |= ((0x3F << 6) | (0x0 << 0));
+ mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+ /* R1A0h Interface and Digital Reference Clock Control/Reserved_50h
+ *
+ * FFE_TRAIN_EN 3
+ */
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp &= ~0x8;
+ /* Hard coding */
+ /* No defines in HBA_Info_Page */
+ tmp |= (0 << 3);
+ mvs_write_port_vsr_data(mvi, phy_id, tmp);
+}
+
+/*Notice: this function must be called when phy is disabled*/
+static void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate)
+{
+ union reg_phy_cfg phy_cfg, phy_cfg_tmp;
+ mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
+ phy_cfg_tmp.v = mvs_read_port_vsr_data(mvi, phy_id);
+ phy_cfg.v = 0;
+ phy_cfg.u.disable_phy = phy_cfg_tmp.u.disable_phy;
+ phy_cfg.u.sas_support = 1;
+ phy_cfg.u.sata_support = 1;
+ phy_cfg.u.sata_host_mode = 1;
+
+ switch (rate) {
+ case 0x0:
+ /* support 1.5 Gbps */
+ phy_cfg.u.speed_support = 1;
+ phy_cfg.u.snw_3_support = 0;
+ phy_cfg.u.tx_lnk_parity = 1;
+ phy_cfg.u.tx_spt_phs_lnk_rate = 0x30;
+ break;
+ case 0x1:
+
+ /* support 1.5, 3.0 Gbps */
+ phy_cfg.u.speed_support = 3;
+ phy_cfg.u.tx_spt_phs_lnk_rate = 0x3c;
+ phy_cfg.u.tx_lgcl_lnk_rate = 0x08;
+ break;
+ case 0x2:
+ default:
+ /* support 1.5, 3.0, 6.0 Gbps */
+ phy_cfg.u.speed_support = 7;
+ phy_cfg.u.snw_3_support = 1;
+ phy_cfg.u.tx_lnk_parity = 1;
+ phy_cfg.u.tx_spt_phs_lnk_rate = 0x3f;
+ phy_cfg.u.tx_lgcl_lnk_rate = 0x09;
+ break;
+ }
+ mvs_write_port_vsr_data(mvi, phy_id, phy_cfg.v);
+}
+
+static void mvs_94xx_config_reg_from_hba(struct mvs_info *mvi, int phy_id)
+{
+ u32 temp;
+ temp = (u32)(*(u32 *)&mvi->hba_info_param.phy_tuning[phy_id]);
+ if (temp == 0xFFFFFFFFL) {
+ mvi->hba_info_param.phy_tuning[phy_id].trans_emp_amp = 0x6;
+ mvi->hba_info_param.phy_tuning[phy_id].trans_amp = 0x1A;
+ mvi->hba_info_param.phy_tuning[phy_id].trans_amp_adj = 0x3;
+ }
+
+ temp = (u8)(*(u8 *)&mvi->hba_info_param.ffe_ctl[phy_id]);
+ if (temp == 0xFFL) {
+ switch (mvi->pdev->revision) {
+ case VANIR_A0_REV:
+ case VANIR_B0_REV:
+ mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
+ mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0x7;
+ break;
+ case VANIR_C0_REV:
+ case VANIR_C1_REV:
+ case VANIR_C2_REV:
+ default:
+ mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
+ mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0xC;
+ break;
+ }
+ }
+
+ temp = (u8)(*(u8 *)&mvi->hba_info_param.phy_rate[phy_id]);
+ if (temp == 0xFFL)
+ /*set default phy_rate = 6Gbps*/
+ mvi->hba_info_param.phy_rate[phy_id] = 0x2;
+
+ set_phy_tuning(mvi, phy_id,
+ mvi->hba_info_param.phy_tuning[phy_id]);
+ set_phy_ffe_tuning(mvi, phy_id,
+ mvi->hba_info_param.ffe_ctl[phy_id]);
+ set_phy_rate(mvi, phy_id,
+ mvi->hba_info_param.phy_rate[phy_id]);
+}
+
+static void mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
{
void __iomem *regs = mvi->regs;
u32 tmp;
@@ -61,7 +254,14 @@ static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
{
u32 tmp;
-
+ u32 delay = 5000;
+ if (hard == MVS_PHY_TUNE) {
+ mvs_write_port_cfg_addr(mvi, phy_id, PHYR_SATA_CTL);
+ tmp = mvs_read_port_cfg_data(mvi, phy_id);
+ mvs_write_port_cfg_data(mvi, phy_id, tmp|0x20000000);
+ mvs_write_port_cfg_data(mvi, phy_id, tmp|0x100000);
+ return;
+ }
tmp = mvs_read_port_irq_stat(mvi, phy_id);
tmp &= ~PHYEV_RDY_CH;
mvs_write_port_irq_stat(mvi, phy_id, tmp);
@@ -71,12 +271,15 @@ static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
mvs_write_phy_ctl(mvi, phy_id, tmp);
do {
tmp = mvs_read_phy_ctl(mvi, phy_id);
- } while (tmp & PHY_RST_HARD);
+ udelay(10);
+ delay--;
+ } while ((tmp & PHY_RST_HARD) && delay);
+ if (!delay)
+ mv_dprintk("phy hard reset failed.\n");
} else {
- mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_STAT);
- tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp = mvs_read_phy_ctl(mvi, phy_id);
tmp |= PHY_RST;
- mvs_write_port_vsr_data(mvi, phy_id, tmp);
+ mvs_write_phy_ctl(mvi, phy_id, tmp);
}
}
@@ -90,20 +293,80 @@ static void mvs_94xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
{
- mvs_write_port_vsr_addr(mvi, phy_id, 0x1B4);
- mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
- mvs_write_port_vsr_addr(mvi, phy_id, 0x104);
- mvs_write_port_vsr_data(mvi, phy_id, 0x00018080);
+ u32 tmp;
+ u8 revision = 0;
+
+ revision = mvi->pdev->revision;
+ if (revision == VANIR_A0_REV) {
+ mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
+ mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
+ }
+ if (revision == VANIR_B0_REV) {
+ mvs_write_port_vsr_addr(mvi, phy_id, CMD_APP_MEM_CTL);
+ mvs_write_port_vsr_data(mvi, phy_id, 0x08001006);
+ mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
+ mvs_write_port_vsr_data(mvi, phy_id, 0x0000705f);
+ }
+
mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
- mvs_write_port_vsr_data(mvi, phy_id, 0x00207fff);
+ tmp = mvs_read_port_vsr_data(mvi, phy_id);
+ tmp |= bit(0);
+ mvs_write_port_vsr_data(mvi, phy_id, tmp & 0xfd7fffff);
+}
+
+static void mvs_94xx_sgpio_init(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+ u32 tmp;
+
+ tmp = mr32(MVS_HST_CHIP_CONFIG);
+ tmp |= 0x100;
+ mw32(MVS_HST_CHIP_CONFIG, tmp);
+
+ mw32(MVS_SGPIO_CTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ MVS_SGPIO_CTRL_SDOUT_AUTO << MVS_SGPIO_CTRL_SDOUT_SHIFT);
+
+ mw32(MVS_SGPIO_CFG1 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ 8 << MVS_SGPIO_CFG1_LOWA_SHIFT |
+ 8 << MVS_SGPIO_CFG1_HIA_SHIFT |
+ 4 << MVS_SGPIO_CFG1_LOWB_SHIFT |
+ 4 << MVS_SGPIO_CFG1_HIB_SHIFT |
+ 2 << MVS_SGPIO_CFG1_MAXACTON_SHIFT |
+ 1 << MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT
+ );
+
+ mw32(MVS_SGPIO_CFG2 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ (300000 / 100) << MVS_SGPIO_CFG2_CLK_SHIFT | /* 100kHz clock */
+ 66 << MVS_SGPIO_CFG2_BLINK_SHIFT /* (66 * 0,121 Hz?)*/
+ );
+
+ mw32(MVS_SGPIO_CFG0 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ MVS_SGPIO_CFG0_ENABLE |
+ MVS_SGPIO_CFG0_BLINKA |
+ MVS_SGPIO_CFG0_BLINKB |
+ /* 3*4 data bits / PDU */
+ (12 - 1) << MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT
+ );
+
+ mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ DEFAULT_SGPIO_BITS);
+
+ mw32(MVS_SGPIO_DSRC + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ ((mvi->id * 4) + 3) << (8 * 3) |
+ ((mvi->id * 4) + 2) << (8 * 2) |
+ ((mvi->id * 4) + 1) << (8 * 1) |
+ ((mvi->id * 4) + 0) << (8 * 0));
+
}
-static int __devinit mvs_94xx_init(struct mvs_info *mvi)
+static int mvs_94xx_init(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs;
int i;
u32 tmp, cctl;
+ u8 revision;
+ revision = mvi->pdev->revision;
mvs_show_pcie_usage(mvi);
if (mvi->flags & MVF_FLAG_SOC) {
tmp = mr32(MVS_PHY_CTL);
@@ -133,6 +396,28 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
msleep(100);
}
+ /* disable Multiplexing, enable phy implemented */
+ mw32(MVS_PORTS_IMP, 0xFF);
+
+ if (revision == VANIR_A0_REV) {
+ mw32(MVS_PA_VSR_ADDR, CMD_CMWK_OOB_DET);
+ mw32(MVS_PA_VSR_PORT, 0x00018080);
+ }
+ mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE2);
+ if (revision == VANIR_A0_REV || revision == VANIR_B0_REV)
+ /* set 6G/3G/1.5G, multiplexing, without SSC */
+ mw32(MVS_PA_VSR_PORT, 0x0084d4fe);
+ else
+ /* set 6G/3G/1.5G, multiplexing, with and without SSC */
+ mw32(MVS_PA_VSR_PORT, 0x0084fffe);
+
+ if (revision == VANIR_B0_REV) {
+ mw32(MVS_PA_VSR_ADDR, CMD_APP_MEM_CTL);
+ mw32(MVS_PA_VSR_PORT, 0x08001006);
+ mw32(MVS_PA_VSR_ADDR, CMD_HOST_RD_DATA);
+ mw32(MVS_PA_VSR_PORT, 0x0000705f);
+ }
+
/* reset control */
mw32(MVS_PCS, 0); /* MVS_PCS */
mw32(MVS_STP_REG_SET_0, 0);
@@ -141,17 +426,18 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
/* init phys */
mvs_phy_hacks(mvi);
- /* disable Multiplexing, enable phy implemented */
- mw32(MVS_PORTS_IMP, 0xFF);
-
-
- mw32(MVS_PA_VSR_ADDR, 0x00000104);
- mw32(MVS_PA_VSR_PORT, 0x00018080);
- mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE8);
- mw32(MVS_PA_VSR_PORT, 0x0084ffff);
+ /* disable non data frame retry */
+ tmp = mvs_cr32(mvi, CMD_SAS_CTL1);
+ if ((revision == VANIR_A0_REV) ||
+ (revision == VANIR_B0_REV) ||
+ (revision == VANIR_C0_REV)) {
+ tmp &= ~0xffff;
+ tmp |= 0x007f;
+ mvs_cw32(mvi, CMD_SAS_CTL1, tmp);
+ }
/* set LED blink when IO*/
- mw32(MVS_PA_VSR_ADDR, 0x00000030);
+ mw32(MVS_PA_VSR_ADDR, VSR_PHY_ACT_LED);
tmp = mr32(MVS_PA_VSR_PORT);
tmp &= 0xFFFF00FF;
tmp |= 0x00003300;
@@ -175,12 +461,13 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
mvs_94xx_phy_disable(mvi, i);
/* set phy local SAS address */
mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4,
- (mvi->phy[i].dev_sas_addr));
+ cpu_to_le64(mvi->phy[i].dev_sas_addr));
mvs_94xx_enable_xmt(mvi, i);
+ mvs_94xx_config_reg_from_hba(mvi, i);
mvs_94xx_phy_enable(mvi, i);
- mvs_94xx_phy_reset(mvi, i, 1);
+ mvs_94xx_phy_reset(mvi, i, PHY_RST_HARD);
msleep(500);
mvs_94xx_detect_porttype(mvi, i);
}
@@ -211,16 +498,9 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
mvs_update_phyinfo(mvi, i, 1);
}
- /* FIXME: update wide port bitmaps */
-
/* little endian for open address and command table, etc. */
- /*
- * it seems that ( from the spec ) turning on big-endian won't
- * do us any good on big-endian machines, need further confirmation
- */
cctl = mr32(MVS_CTL);
cctl |= CCTL_ENDIAN_CMD;
- cctl |= CCTL_ENDIAN_DATA;
cctl &= ~CCTL_ENDIAN_OPEN;
cctl |= CCTL_ENDIAN_RSP;
mw32_f(MVS_CTL, cctl);
@@ -228,15 +508,20 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
/* reset CMD queue */
tmp = mr32(MVS_PCS);
tmp |= PCS_CMD_RST;
+ tmp &= ~PCS_SELF_CLEAR;
mw32(MVS_PCS, tmp);
- /* interrupt coalescing may cause missing HW interrput in some case,
- * and the max count is 0x1ff, while our max slot is 0x200,
+ /*
+ * the max count is 0x1ff, while our max slot is 0x200,
* it will make count 0.
*/
tmp = 0;
- mw32(MVS_INT_COAL, tmp);
+ if (MVS_CHIP_SLOT_SZ > 0x1ff)
+ mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
+ else
+ mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
- tmp = 0x100;
+ /* default interrupt coalescing time is 128us */
+ tmp = 0x10000 | interrupt_coalescing;
mw32(MVS_INT_COAL_TMOUT, tmp);
/* ladies and gentlemen, start your engines */
@@ -249,13 +534,36 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
/* enable completion queue interrupt */
tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
- CINT_DMA_PCIE);
+ CINT_DMA_PCIE | CINT_NON_SPEC_NCQ_ERROR);
tmp |= CINT_PHY_MASK;
mw32(MVS_INT_MASK, tmp);
+ tmp = mvs_cr32(mvi, CMD_LINK_TIMER);
+ tmp |= 0xFFFF0000;
+ mvs_cw32(mvi, CMD_LINK_TIMER, tmp);
+
+ /* tune STP performance */
+ tmp = 0x003F003F;
+ mvs_cw32(mvi, CMD_PL_TIMER, tmp);
+
+ /* This can improve expander large block size seq write performance */
+ tmp = mvs_cr32(mvi, CMD_PORT_LAYER_TIMER1);
+ tmp |= 0xFFFF007F;
+ mvs_cw32(mvi, CMD_PORT_LAYER_TIMER1, tmp);
+
+ /* change the connection open-close behavior (bit 9)
+ * set bit8 to 1 for performance tuning */
+ tmp = mvs_cr32(mvi, CMD_SL_MODE0);
+ tmp |= 0x00000300;
+ /* set bit0 to 0 to enable retry for no_dest reject case */
+ tmp &= 0xFFFFFFFE;
+ mvs_cw32(mvi, CMD_SL_MODE0, tmp);
+
/* Enable SRS interrupt */
mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
+ mvs_94xx_sgpio_init(mvi);
+
return 0;
}
@@ -287,7 +595,7 @@ static void mvs_94xx_interrupt_enable(struct mvs_info *mvi)
u32 tmp;
tmp = mr32(MVS_GBL_CTL);
- tmp |= (IRQ_SAS_A | IRQ_SAS_B);
+ tmp |= (MVS_IRQ_SAS_A | MVS_IRQ_SAS_B);
mw32(MVS_GBL_INT_STAT, tmp);
writel(tmp, regs + 0x0C);
writel(tmp, regs + 0x10);
@@ -303,7 +611,7 @@ static void mvs_94xx_interrupt_disable(struct mvs_info *mvi)
tmp = mr32(MVS_GBL_CTL);
- tmp &= ~(IRQ_SAS_A | IRQ_SAS_B);
+ tmp &= ~(MVS_IRQ_SAS_A | MVS_IRQ_SAS_B);
mw32(MVS_GBL_INT_STAT, tmp);
writel(tmp, regs + 0x0C);
writel(tmp, regs + 0x10);
@@ -319,7 +627,7 @@ static u32 mvs_94xx_isr_status(struct mvs_info *mvi, int irq)
if (!(mvi->flags & MVF_FLAG_SOC)) {
stat = mr32(MVS_GBL_INT_STAT);
- if (!(stat & (IRQ_SAS_A | IRQ_SAS_B)))
+ if (!(stat & (MVS_IRQ_SAS_A | MVS_IRQ_SAS_B)))
return 0;
}
return stat;
@@ -329,16 +637,13 @@ static irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat)
{
void __iomem *regs = mvi->regs;
- if (((stat & IRQ_SAS_A) && mvi->id == 0) ||
- ((stat & IRQ_SAS_B) && mvi->id == 1)) {
+ if (((stat & MVS_IRQ_SAS_A) && mvi->id == 0) ||
+ ((stat & MVS_IRQ_SAS_B) && mvi->id == 1)) {
mw32_f(MVS_INT_STAT, CINT_DONE);
- #ifndef MVS_USE_TASKLET
+
spin_lock(&mvi->lock);
- #endif
mvs_int_full(mvi);
- #ifndef MVS_USE_TASKLET
spin_unlock(&mvi->lock);
- #endif
}
return IRQ_HANDLED;
}
@@ -346,10 +651,49 @@ static irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat)
static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
{
u32 tmp;
- mvs_cw32(mvi, 0x300 + (slot_idx >> 3), 1 << (slot_idx % 32));
- do {
- tmp = mvs_cr32(mvi, 0x300 + (slot_idx >> 3));
- } while (tmp & 1 << (slot_idx % 32));
+ tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
+ if (tmp & 1 << (slot_idx % 32)) {
+ mv_printk("command active %08X, slot [%x].\n", tmp, slot_idx);
+ mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
+ 1 << (slot_idx % 32));
+ do {
+ tmp = mvs_cr32(mvi,
+ MVS_COMMAND_ACTIVE + (slot_idx >> 3));
+ } while (tmp & 1 << (slot_idx % 32));
+ }
+}
+
+static void
+mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
+{
+ void __iomem *regs = mvi->regs;
+ u32 tmp;
+
+ if (clear_all) {
+ tmp = mr32(MVS_INT_STAT_SRS_0);
+ if (tmp) {
+ mv_dprintk("check SRS 0 %08X.\n", tmp);
+ mw32(MVS_INT_STAT_SRS_0, tmp);
+ }
+ tmp = mr32(MVS_INT_STAT_SRS_1);
+ if (tmp) {
+ mv_dprintk("check SRS 1 %08X.\n", tmp);
+ mw32(MVS_INT_STAT_SRS_1, tmp);
+ }
+ } else {
+ if (reg_set > 31)
+ tmp = mr32(MVS_INT_STAT_SRS_1);
+ else
+ tmp = mr32(MVS_INT_STAT_SRS_0);
+
+ if (tmp & (1 << (reg_set % 32))) {
+ mv_dprintk("register set 0x%x was stopped.\n", reg_set);
+ if (reg_set > 31)
+ mw32(MVS_INT_STAT_SRS_1, 1 << (reg_set % 32));
+ else
+ mw32(MVS_INT_STAT_SRS_0, 1 << (reg_set % 32));
+ }
+ }
}
static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
@@ -357,37 +701,56 @@ static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
{
void __iomem *regs = mvi->regs;
u32 tmp;
+ mvs_94xx_clear_srs_irq(mvi, 0, 1);
- if (type == PORT_TYPE_SATA) {
- tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs);
- mw32(MVS_INT_STAT_SRS_0, tmp);
- }
- mw32(MVS_INT_STAT, CINT_CI_STOP);
+ tmp = mr32(MVS_INT_STAT);
+ mw32(MVS_INT_STAT, tmp | CINT_CI_STOP);
tmp = mr32(MVS_PCS) | 0xFF00;
mw32(MVS_PCS, tmp);
}
+static void mvs_94xx_non_spec_ncq_error(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs;
+ u32 err_0, err_1;
+ u8 i;
+ struct mvs_device *device;
+
+ err_0 = mr32(MVS_NON_NCQ_ERR_0);
+ err_1 = mr32(MVS_NON_NCQ_ERR_1);
+
+ mv_dprintk("non specific ncq error err_0:%x,err_1:%x.\n",
+ err_0, err_1);
+ for (i = 0; i < 32; i++) {
+ if (err_0 & bit(i)) {
+ device = mvs_find_dev_by_reg_set(mvi, i);
+ if (device)
+ mvs_release_task(mvi, device->sas_device);
+ }
+ if (err_1 & bit(i)) {
+ device = mvs_find_dev_by_reg_set(mvi, i+32);
+ if (device)
+ mvs_release_task(mvi, device->sas_device);
+ }
+ }
+
+ mw32(MVS_NON_NCQ_ERR_0, err_0);
+ mw32(MVS_NON_NCQ_ERR_1, err_1);
+}
+
static void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
{
void __iomem *regs = mvi->regs;
- u32 tmp;
u8 reg_set = *tfs;
if (*tfs == MVS_ID_NOT_MAPPED)
return;
mvi->sata_reg_set &= ~bit(reg_set);
- if (reg_set < 32) {
+ if (reg_set < 32)
w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set);
- tmp = mr32(MVS_INT_STAT_SRS_0) & (u32)mvi->sata_reg_set;
- if (tmp)
- mw32(MVS_INT_STAT_SRS_0, tmp);
- } else {
- w_reg_set_enable(reg_set, mvi->sata_reg_set);
- tmp = mr32(MVS_INT_STAT_SRS_1) & mvi->sata_reg_set;
- if (tmp)
- mw32(MVS_INT_STAT_SRS_1, tmp);
- }
+ else
+ w_reg_set_enable(reg_set, (u32)(mvi->sata_reg_set >> 32));
*tfs = MVS_ID_NOT_MAPPED;
@@ -403,7 +766,7 @@ static u8 mvs_94xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
return 0;
i = mv_ffc64(mvi->sata_reg_set);
- if (i > 32) {
+ if (i >= 32) {
mvi->sata_reg_set |= bit(i);
w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32));
*tfs = i;
@@ -422,9 +785,12 @@ static void mvs_94xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
int i;
struct scatterlist *sg;
struct mvs_prd *buf_prd = prd;
+ struct mvs_prd_imt im_len;
+ *(u32 *)&im_len = 0;
for_each_sg(scatter, sg, nr, i) {
buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
- buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg));
+ im_len.len = sg_dma_len(sg);
+ buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
buf_prd++;
}
}
@@ -433,7 +799,7 @@ static int mvs_94xx_oob_done(struct mvs_info *mvi, int i)
{
u32 phy_st;
phy_st = mvs_read_phy_ctl(mvi, i);
- if (phy_st & PHY_READY_MASK) /* phy ready */
+ if (phy_st & PHY_READY_MASK)
return 1;
return 0;
}
@@ -447,7 +813,7 @@ static void mvs_94xx_get_dev_identify_frame(struct mvs_info *mvi, int port_id,
for (i = 0; i < 7; i++) {
mvs_write_port_cfg_addr(mvi, port_id,
CONFIG_ID_FRAME0 + i * 4);
- id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+ id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
}
memcpy(id, id_frame, 28);
}
@@ -458,15 +824,13 @@ static void mvs_94xx_get_att_identify_frame(struct mvs_info *mvi, int port_id,
int i;
u32 id_frame[7];
- /* mvs_hexdump(28, (u8 *)id_frame, 0); */
for (i = 0; i < 7; i++) {
mvs_write_port_cfg_addr(mvi, port_id,
CONFIG_ATT_ID_FRAME0 + i * 4);
- id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+ id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
mv_dprintk("94xx phy %d atta frame %d %x.\n",
port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]);
}
- /* mvs_hexdump(28, (u8 *)id_frame, 0); */
memcpy(id, id_frame, 28);
}
@@ -521,12 +885,27 @@ static void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i,
phy->att_dev_info = PORT_DEV_STP_TRGT | 1;
}
+ /* enable spin up bit */
+ mvs_write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
+ mvs_write_port_cfg_data(mvi, i, 0x04);
+
}
-void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
- struct sas_phy_linkrates *rates)
+static void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
+ struct sas_phy_linkrates *rates)
{
- /* TODO */
+ u32 lrmax = 0;
+ u32 tmp;
+
+ tmp = mvs_read_phy_ctl(mvi, phy_id);
+ lrmax = (rates->maximum_linkrate - SAS_LINK_RATE_1_5_GBPS) << 12;
+
+ if (lrmax) {
+ tmp &= ~(0x3 << 12);
+ tmp |= lrmax;
+ }
+ mvs_write_phy_ctl(mvi, phy_id, tmp);
+ mvs_94xx_phy_reset(mvi, phy_id, PHY_RST_HARD);
}
static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
@@ -542,25 +921,26 @@ static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
}
-u32 mvs_94xx_spi_read_data(struct mvs_info *mvi)
+static u32 mvs_94xx_spi_read_data(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs_ex - 0x10200;
return mr32(SPI_RD_DATA_REG_94XX);
}
-void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data)
+static void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data)
{
void __iomem *regs = mvi->regs_ex - 0x10200;
- mw32(SPI_RD_DATA_REG_94XX, data);
+
+ mw32(SPI_RD_DATA_REG_94XX, data);
}
-int mvs_94xx_spi_buildcmd(struct mvs_info *mvi,
- u32 *dwCmd,
- u8 cmd,
- u8 read,
- u8 length,
- u32 addr
+static int mvs_94xx_spi_buildcmd(struct mvs_info *mvi,
+ u32 *dwCmd,
+ u8 cmd,
+ u8 read,
+ u8 length,
+ u32 addr
)
{
void __iomem *regs = mvi->regs_ex - 0x10200;
@@ -580,7 +960,7 @@ int mvs_94xx_spi_buildcmd(struct mvs_info *mvi,
}
-int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
+static int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
{
void __iomem *regs = mvi->regs_ex - 0x10200;
mw32(SPI_CTRL_REG_94XX, cmd | SPI_CTRL_SpiStart_94XX);
@@ -588,7 +968,7 @@ int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
return 0;
}
-int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
+static int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
{
void __iomem *regs = mvi->regs_ex - 0x10200;
u32 i, dwTmp;
@@ -603,27 +983,146 @@ int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
return -1;
}
-#ifndef DISABLE_HOTPLUG_DMA_FIX
-void mvs_94xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+static void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
+ int buf_len, int from, void *prd)
{
int i;
struct mvs_prd *buf_prd = prd;
+ dma_addr_t buf_dma;
+ struct mvs_prd_imt im_len;
+
+ *(u32 *)&im_len = 0;
buf_prd += from;
- for (i = 0; i < MAX_SG_ENTRY - from; i++) {
- buf_prd->addr = cpu_to_le64(buf_dma);
- buf_prd->im_len.len = cpu_to_le32(buf_len);
- ++buf_prd;
+
+#define PRD_CHAINED_ENTRY 0x01
+ if ((mvi->pdev->revision == VANIR_A0_REV) ||
+ (mvi->pdev->revision == VANIR_B0_REV))
+ buf_dma = (phy_mask <= 0x08) ?
+ mvi->bulk_buffer_dma : mvi->bulk_buffer_dma1;
+ else
+ return;
+
+ for (i = from; i < MAX_SG_ENTRY; i++, ++buf_prd) {
+ if (i == MAX_SG_ENTRY - 1) {
+ buf_prd->addr = cpu_to_le64(virt_to_phys(buf_prd - 1));
+ im_len.len = 2;
+ im_len.misc_ctl = PRD_CHAINED_ENTRY;
+ } else {
+ buf_prd->addr = cpu_to_le64(buf_dma);
+ im_len.len = buf_len;
+ }
+ buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
}
}
-#endif
-/*
- * FIXME JEJB: temporary nop clear_srs_irq to make 94xx still work
- * with 64xx fixes
- */
-static void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set,
- u8 clear_all)
+static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time)
{
+ void __iomem *regs = mvi->regs;
+ u32 tmp = 0;
+ /*
+ * the max count is 0x1ff, while our max slot is 0x200,
+ * it will make count 0.
+ */
+ if (time == 0) {
+ mw32(MVS_INT_COAL, 0);
+ mw32(MVS_INT_COAL_TMOUT, 0x10000);
+ } else {
+ if (MVS_CHIP_SLOT_SZ > 0x1ff)
+ mw32(MVS_INT_COAL, 0x1ff|COAL_EN);
+ else
+ mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN);
+
+ tmp = 0x10000 | time;
+ mw32(MVS_INT_COAL_TMOUT, tmp);
+ }
+
+}
+
+static int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
+ u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data)
+{
+ int i;
+
+ switch (reg_type) {
+
+ case SAS_GPIO_REG_TX_GP:
+ if (reg_index == 0)
+ return -EINVAL;
+
+ if (reg_count > 1)
+ return -EINVAL;
+
+ if (reg_count == 0)
+ return 0;
+
+ /* maximum supported bits = hosts * 4 drives * 3 bits */
+ for (i = 0; i < mvs_prv->n_host * 4 * 3; i++) {
+
+ /* select host */
+ struct mvs_info *mvi = mvs_prv->mvi[i/(4*3)];
+
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+
+ int drive = (i/3) & (4-1); /* drive number on host */
+ int driveshift = drive * 8; /* bit offset of drive */
+ u32 block = ioread32be(regs + MVS_SGPIO_DCTRL +
+ MVS_SGPIO_HOST_OFFSET * mvi->id);
+
+ /*
+ * if bit is set then create a mask with the first
+ * bit of the drive set in the mask ...
+ */
+ u32 bit = get_unaligned_be32(write_data) & (1 << i) ?
+ 1 << driveshift : 0;
+
+ /*
+ * ... and then shift it to the right position based
+ * on the led type (activity/id/fail)
+ */
+ switch (i%3) {
+ case 0: /* activity */
+ block &= ~((0x7 << MVS_SGPIO_DCTRL_ACT_SHIFT)
+ << driveshift);
+ /* hardwire activity bit to SOF */
+ block |= LED_BLINKA_SOF << (
+ MVS_SGPIO_DCTRL_ACT_SHIFT +
+ driveshift);
+ break;
+ case 1: /* id */
+ block &= ~((0x3 << MVS_SGPIO_DCTRL_LOC_SHIFT)
+ << driveshift);
+ block |= bit << MVS_SGPIO_DCTRL_LOC_SHIFT;
+ break;
+ case 2: /* fail */
+ block &= ~((0x7 << MVS_SGPIO_DCTRL_ERR_SHIFT)
+ << driveshift);
+ block |= bit << MVS_SGPIO_DCTRL_ERR_SHIFT;
+ break;
+ }
+
+ iowrite32be(block,
+ regs + MVS_SGPIO_DCTRL +
+ MVS_SGPIO_HOST_OFFSET * mvi->id);
+
+ }
+
+ return reg_count;
+
+ case SAS_GPIO_REG_TX:
+ if (reg_index + reg_count > mvs_prv->n_host)
+ return -EINVAL;
+
+ for (i = 0; i < reg_count; i++) {
+ struct mvs_info *mvi = mvs_prv->mvi[i+reg_index];
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+
+ mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ ((u32 *) write_data)[i]);
+ }
+ return reg_count;
+ }
+ return -ENOSYS;
}
const struct mvs_dispatch mvs_94xx_dispatch = {
@@ -648,7 +1147,6 @@ const struct mvs_dispatch mvs_94xx_dispatch = {
mvs_write_port_irq_stat,
mvs_read_port_irq_mask,
mvs_write_port_irq_mask,
- mvs_get_sas_addr,
mvs_94xx_command_active,
mvs_94xx_clear_srs_irq,
mvs_94xx_issue_stop,
@@ -676,8 +1174,9 @@ const struct mvs_dispatch mvs_94xx_dispatch = {
mvs_94xx_spi_buildcmd,
mvs_94xx_spi_issuecmd,
mvs_94xx_spi_waitdataready,
-#ifndef DISABLE_HOTPLUG_DMA_FIX
mvs_94xx_fix_dma,
-#endif
+ mvs_94xx_tune_interrupt,
+ mvs_94xx_non_spec_ncq_error,
+ mvs_94xx_gpio_write,
};
diff --git a/drivers/scsi/mvsas/mv_94xx.h b/drivers/scsi/mvsas/mv_94xx.h
index 8835befe2c0e..a243182c1345 100644
--- a/drivers/scsi/mvsas/mv_94xx.h
+++ b/drivers/scsi/mvsas/mv_94xx.h
@@ -1,26 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Marvell 88SE94xx hardware specific head file
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#ifndef _MVS94XX_REG_H_
@@ -30,6 +14,18 @@
#define MAX_LINK_RATE SAS_LINK_RATE_6_0_GBPS
+enum VANIR_REVISION_ID {
+ VANIR_A0_REV = 0xA0,
+ VANIR_B0_REV = 0x01,
+ VANIR_C0_REV = 0x02,
+ VANIR_C1_REV = 0x03,
+ VANIR_C2_REV = 0xC2,
+};
+
+enum host_registers {
+ MVS_HST_CHIP_CONFIG = 0x10104, /* chip configuration */
+};
+
enum hw_registers {
MVS_GBL_CTL = 0x04, /* global control */
MVS_GBL_INT_STAT = 0x00, /* global irq status */
@@ -101,6 +97,7 @@ enum hw_registers {
MVS_P4_VSR_DATA = 0x254, /* phy4 VSR data */
MVS_PA_VSR_ADDR = 0x290, /* All port VSR addr */
MVS_PA_VSR_PORT = 0x294, /* All port VSR data */
+ MVS_COMMAND_ACTIVE = 0x300,
};
enum pci_cfg_registers {
@@ -112,79 +109,202 @@ enum pci_cfg_registers {
/* SAS/SATA Vendor Specific Port Registers */
enum sas_sata_vsp_regs {
- VSR_PHY_STAT = 0x00 * 4, /* Phy Status */
- VSR_PHY_MODE1 = 0x01 * 4, /* phy tx */
- VSR_PHY_MODE2 = 0x02 * 4, /* tx scc */
- VSR_PHY_MODE3 = 0x03 * 4, /* pll */
- VSR_PHY_MODE4 = 0x04 * 4, /* VCO */
- VSR_PHY_MODE5 = 0x05 * 4, /* Rx */
- VSR_PHY_MODE6 = 0x06 * 4, /* CDR */
- VSR_PHY_MODE7 = 0x07 * 4, /* Impedance */
- VSR_PHY_MODE8 = 0x08 * 4, /* Voltage */
- VSR_PHY_MODE9 = 0x09 * 4, /* Test */
- VSR_PHY_MODE10 = 0x0A * 4, /* Power */
- VSR_PHY_MODE11 = 0x0B * 4, /* Phy Mode */
- VSR_PHY_VS0 = 0x0C * 4, /* Vednor Specific 0 */
- VSR_PHY_VS1 = 0x0D * 4, /* Vednor Specific 1 */
+ VSR_PHY_STAT = 0x00 * 4, /* Phy Interrupt Status */
+ VSR_PHY_MODE1 = 0x01 * 4, /* phy Interrupt Enable */
+ VSR_PHY_MODE2 = 0x02 * 4, /* Phy Configuration */
+ VSR_PHY_MODE3 = 0x03 * 4, /* Phy Status */
+ VSR_PHY_MODE4 = 0x04 * 4, /* Phy Counter 0 */
+ VSR_PHY_MODE5 = 0x05 * 4, /* Phy Counter 1 */
+ VSR_PHY_MODE6 = 0x06 * 4, /* Event Counter Control */
+ VSR_PHY_MODE7 = 0x07 * 4, /* Event Counter Select */
+ VSR_PHY_MODE8 = 0x08 * 4, /* Event Counter 0 */
+ VSR_PHY_MODE9 = 0x09 * 4, /* Event Counter 1 */
+ VSR_PHY_MODE10 = 0x0A * 4, /* Event Counter 2 */
+ VSR_PHY_MODE11 = 0x0B * 4, /* Event Counter 3 */
+ VSR_PHY_ACT_LED = 0x0C * 4, /* Activity LED control */
+
+ VSR_PHY_FFE_CONTROL = 0x10C,
+ VSR_PHY_DFE_UPDATE_CRTL = 0x110,
+ VSR_REF_CLOCK_CRTL = 0x1A0,
};
enum chip_register_bits {
PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
- PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
- PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (12),
+ PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 12),
+ PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16),
PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
(0x3 << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
};
enum pci_interrupt_cause {
/* MAIN_IRQ_CAUSE (R10200) Bits*/
- IRQ_COM_IN_I2O_IOP0 = (1 << 0),
- IRQ_COM_IN_I2O_IOP1 = (1 << 1),
- IRQ_COM_IN_I2O_IOP2 = (1 << 2),
- IRQ_COM_IN_I2O_IOP3 = (1 << 3),
- IRQ_COM_OUT_I2O_HOS0 = (1 << 4),
- IRQ_COM_OUT_I2O_HOS1 = (1 << 5),
- IRQ_COM_OUT_I2O_HOS2 = (1 << 6),
- IRQ_COM_OUT_I2O_HOS3 = (1 << 7),
- IRQ_PCIF_TO_CPU_DRBL0 = (1 << 8),
- IRQ_PCIF_TO_CPU_DRBL1 = (1 << 9),
- IRQ_PCIF_TO_CPU_DRBL2 = (1 << 10),
- IRQ_PCIF_TO_CPU_DRBL3 = (1 << 11),
- IRQ_PCIF_DRBL0 = (1 << 12),
- IRQ_PCIF_DRBL1 = (1 << 13),
- IRQ_PCIF_DRBL2 = (1 << 14),
- IRQ_PCIF_DRBL3 = (1 << 15),
- IRQ_XOR_A = (1 << 16),
- IRQ_XOR_B = (1 << 17),
- IRQ_SAS_A = (1 << 18),
- IRQ_SAS_B = (1 << 19),
- IRQ_CPU_CNTRL = (1 << 20),
- IRQ_GPIO = (1 << 21),
- IRQ_UART = (1 << 22),
- IRQ_SPI = (1 << 23),
- IRQ_I2C = (1 << 24),
- IRQ_SGPIO = (1 << 25),
- IRQ_COM_ERR = (1 << 29),
- IRQ_I2O_ERR = (1 << 30),
- IRQ_PCIE_ERR = (1 << 31),
+ MVS_IRQ_COM_IN_I2O_IOP0 = (1 << 0),
+ MVS_IRQ_COM_IN_I2O_IOP1 = (1 << 1),
+ MVS_IRQ_COM_IN_I2O_IOP2 = (1 << 2),
+ MVS_IRQ_COM_IN_I2O_IOP3 = (1 << 3),
+ MVS_IRQ_COM_OUT_I2O_HOS0 = (1 << 4),
+ MVS_IRQ_COM_OUT_I2O_HOS1 = (1 << 5),
+ MVS_IRQ_COM_OUT_I2O_HOS2 = (1 << 6),
+ MVS_IRQ_COM_OUT_I2O_HOS3 = (1 << 7),
+ MVS_IRQ_PCIF_TO_CPU_DRBL0 = (1 << 8),
+ MVS_IRQ_PCIF_TO_CPU_DRBL1 = (1 << 9),
+ MVS_IRQ_PCIF_TO_CPU_DRBL2 = (1 << 10),
+ MVS_IRQ_PCIF_TO_CPU_DRBL3 = (1 << 11),
+ MVS_IRQ_PCIF_DRBL0 = (1 << 12),
+ MVS_IRQ_PCIF_DRBL1 = (1 << 13),
+ MVS_IRQ_PCIF_DRBL2 = (1 << 14),
+ MVS_IRQ_PCIF_DRBL3 = (1 << 15),
+ MVS_IRQ_XOR_A = (1 << 16),
+ MVS_IRQ_XOR_B = (1 << 17),
+ MVS_IRQ_SAS_A = (1 << 18),
+ MVS_IRQ_SAS_B = (1 << 19),
+ MVS_IRQ_CPU_CNTRL = (1 << 20),
+ MVS_IRQ_GPIO = (1 << 21),
+ MVS_IRQ_UART = (1 << 22),
+ MVS_IRQ_SPI = (1 << 23),
+ MVS_IRQ_I2C = (1 << 24),
+ MVS_IRQ_SGPIO = (1 << 25),
+ MVS_IRQ_COM_ERR = (1 << 29),
+ MVS_IRQ_I2O_ERR = (1 << 30),
+ MVS_IRQ_PCIE_ERR = (1 << 31),
+};
+
+union reg_phy_cfg {
+ u32 v;
+ struct {
+ u32 phy_reset:1;
+ u32 sas_support:1;
+ u32 sata_support:1;
+ u32 sata_host_mode:1;
+ /*
+ * bit 2: 6Gbps support
+ * bit 1: 3Gbps support
+ * bit 0: 1.5Gbps support
+ */
+ u32 speed_support:3;
+ u32 snw_3_support:1;
+ u32 tx_lnk_parity:1;
+ /*
+ * bit 5: G1 (1.5Gbps) Without SSC
+ * bit 4: G1 (1.5Gbps) with SSC
+ * bit 3: G2 (3.0Gbps) Without SSC
+ * bit 2: G2 (3.0Gbps) with SSC
+ * bit 1: G3 (6.0Gbps) without SSC
+ * bit 0: G3 (6.0Gbps) with SSC
+ */
+ u32 tx_spt_phs_lnk_rate:6;
+ /* 8h: 1.5Gbps 9h: 3Gbps Ah: 6Gbps */
+ u32 tx_lgcl_lnk_rate:4;
+ u32 tx_ssc_type:1;
+ u32 sata_spin_up_spt:1;
+ u32 sata_spin_up_en:1;
+ u32 bypass_oob:1;
+ u32 disable_phy:1;
+ u32 rsvd:8;
+ } u;
};
#define MAX_SG_ENTRY 255
struct mvs_prd_imt {
+#ifndef __BIG_ENDIAN
__le32 len:22;
u8 _r_a:2;
u8 misc_ctl:4;
u8 inter_sel:4;
+#else
+ u32 inter_sel:4;
+ u32 misc_ctl:4;
+ u32 _r_a:2;
+ u32 len:22;
+#endif
};
struct mvs_prd {
/* 64-bit buffer address */
__le64 addr;
/* 22-bit length */
- struct mvs_prd_imt im_len;
+ __le32 im_len;
} __attribute__ ((packed));
+enum sgpio_registers {
+ MVS_SGPIO_HOST_OFFSET = 0x100, /* offset between hosts */
+
+ MVS_SGPIO_CFG0 = 0xc200,
+ MVS_SGPIO_CFG0_ENABLE = (1 << 0), /* enable pins */
+ MVS_SGPIO_CFG0_BLINKB = (1 << 1), /* blink generators */
+ MVS_SGPIO_CFG0_BLINKA = (1 << 2),
+ MVS_SGPIO_CFG0_INVSCLK = (1 << 3), /* invert signal? */
+ MVS_SGPIO_CFG0_INVSLOAD = (1 << 4),
+ MVS_SGPIO_CFG0_INVSDOUT = (1 << 5),
+ MVS_SGPIO_CFG0_SLOAD_FALLEDGE = (1 << 6), /* rise/fall edge? */
+ MVS_SGPIO_CFG0_SDOUT_FALLEDGE = (1 << 7),
+ MVS_SGPIO_CFG0_SDIN_RISEEDGE = (1 << 8),
+ MVS_SGPIO_CFG0_MAN_BITLEN_SHIFT = 18, /* bits/frame manual mode */
+ MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT = 24, /* bits/frame auto mode */
+
+ MVS_SGPIO_CFG1 = 0xc204, /* blink timing register */
+ MVS_SGPIO_CFG1_LOWA_SHIFT = 0, /* A off time */
+ MVS_SGPIO_CFG1_HIA_SHIFT = 4, /* A on time */
+ MVS_SGPIO_CFG1_LOWB_SHIFT = 8, /* B off time */
+ MVS_SGPIO_CFG1_HIB_SHIFT = 12, /* B on time */
+ MVS_SGPIO_CFG1_MAXACTON_SHIFT = 16, /* max activity on time */
+
+ /* force activity off time */
+ MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT = 20,
+ /* stretch activity on time */
+ MVS_SGPIO_CFG1_STRCHACTON_SHIFT = 24,
+ /* stretch activiity off time */
+ MVS_SGPIO_CFG1_STRCHACTOFF_SHIFT = 28,
+
+
+ MVS_SGPIO_CFG2 = 0xc208, /* clock speed register */
+ MVS_SGPIO_CFG2_CLK_SHIFT = 0,
+ MVS_SGPIO_CFG2_BLINK_SHIFT = 20,
+
+ MVS_SGPIO_CTRL = 0xc20c, /* SDOUT/SDIN mode control */
+ MVS_SGPIO_CTRL_SDOUT_AUTO = 2,
+ MVS_SGPIO_CTRL_SDOUT_SHIFT = 2,
+
+ MVS_SGPIO_DSRC = 0xc220, /* map ODn bits to drives */
+
+ MVS_SGPIO_DCTRL = 0xc238,
+ MVS_SGPIO_DCTRL_ERR_SHIFT = 0,
+ MVS_SGPIO_DCTRL_LOC_SHIFT = 3,
+ MVS_SGPIO_DCTRL_ACT_SHIFT = 5,
+};
+
+enum sgpio_led_status {
+ LED_OFF = 0,
+ LED_ON = 1,
+ LED_BLINKA = 2,
+ LED_BLINKA_INV = 3,
+ LED_BLINKA_SOF = 4,
+ LED_BLINKA_EOF = 5,
+ LED_BLINKB = 6,
+ LED_BLINKB_INV = 7,
+};
+
+#define DEFAULT_SGPIO_BITS ((LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 3) | \
+ (LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 2) | \
+ (LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 1) | \
+ (LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 0))
+
+/*
+ * these registers are accessed through port vendor
+ * specific address/data registers
+ */
+enum sas_sata_phy_regs {
+ GENERATION_1_SETTING = 0x118,
+ GENERATION_1_2_SETTING = 0x11C,
+ GENERATION_2_3_SETTING = 0x120,
+ GENERATION_3_4_SETTING = 0x124,
+};
+
#define SPI_CTRL_REG_94XX 0xc800
#define SPI_ADDR_REG_94XX 0xc804
#define SPI_WR_DATA_REG_94XX 0xc808
@@ -193,21 +313,11 @@ struct mvs_prd {
#define SPI_ADDR_VLD_94XX (1U << 1)
#define SPI_CTRL_SpiStart_94XX (1U << 0)
-#define mv_ffc(x) ffz(x)
-
static inline int
mv_ffc64(u64 v)
{
- int i;
- i = mv_ffc((u32)v);
- if (i >= 0)
- return i;
- i = mv_ffc((u32)(v>>32));
-
- if (i != 0)
- return 32 + i;
-
- return -1;
+ u64 x = ~v;
+ return x ? __ffs64(x) : -1;
}
#define r_reg_set_enable(i) \
diff --git a/drivers/scsi/mvsas/mv_chips.h b/drivers/scsi/mvsas/mv_chips.h
index 1753a6fc42d0..0e7366fafca9 100644
--- a/drivers/scsi/mvsas/mv_chips.h
+++ b/drivers/scsi/mvsas/mv_chips.h
@@ -1,26 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Marvell 88SE64xx/88SE94xx register IO interface
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
@@ -160,11 +144,10 @@ static inline void mvs_write_port_irq_mask(struct mvs_info *mvi,
MVS_P4_INT_MASK, port, val);
}
-static inline void __devinit mvs_phy_hacks(struct mvs_info *mvi)
+static inline void mvs_phy_hacks(struct mvs_info *mvi)
{
u32 tmp;
- /* workaround for SATA R-ERR, to ignore phy glitch */
tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
tmp &= ~(1 << 9);
tmp |= (1 << 10);
@@ -179,23 +162,10 @@ static inline void __devinit mvs_phy_hacks(struct mvs_info *mvi)
tmp |= 0x3fff;
mvs_cw32(mvi, CMD_SAS_CTL0, tmp);
- /* workaround for WDTIMEOUT , set to 550 ms */
mvs_cw32(mvi, CMD_WD_TIMER, 0x7a0000);
/* not to halt for different port op during wideport link change */
mvs_cw32(mvi, CMD_APP_ERR_CONFIG, 0xffefbf7d);
-
- /* workaround for Seagate disk not-found OOB sequence, recv
- * COMINIT before sending out COMWAKE */
- tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
- tmp &= 0x0000ffff;
- tmp |= 0x00fa0000;
- mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
-
- tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
- tmp &= 0x1fffffff;
- tmp |= (2U << 29); /* 8 ms retry */
- mvs_cw32(mvi, CMD_PHY_TIMER, tmp);
}
static inline void mvs_int_sata(struct mvs_info *mvi)
@@ -223,6 +193,9 @@ static inline void mvs_int_full(struct mvs_info *mvi)
mvs_int_port(mvi, i, tmp);
}
+ if (stat & CINT_NON_SPEC_NCQ_ERROR)
+ MVS_CHIP_DISP->non_spec_ncq_error(mvi);
+
if (stat & CINT_SRS)
mvs_int_sata(mvi);
diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h
index bc00c940743c..3e4124177b2a 100644
--- a/drivers/scsi/mvsas/mv_defs.h
+++ b/drivers/scsi/mvsas/mv_defs.h
@@ -1,26 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Marvell 88SE64xx/88SE94xx const head file
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#ifndef _MV_DEFS_H_
@@ -43,7 +27,6 @@ enum chip_flavors {
/* driver compile-time configuration */
enum driver_configuration {
- MVS_SLOTS = 512, /* command slots */
MVS_TX_RING_SZ = 1024, /* TX ring size (12-bit) */
MVS_RX_RING_SZ = 1024, /* RX ring size (12-bit) */
/* software requires power-of-2
@@ -56,8 +39,8 @@ enum driver_configuration {
MVS_SSP_CMD_SZ = 64, /* SSP command table buffer size */
MVS_ATA_CMD_SZ = 96, /* SATA command table buffer size */
MVS_OAF_SZ = 64, /* Open address frame buffer size */
- MVS_QUEUE_SIZE = 32, /* Support Queue depth */
- MVS_CAN_QUEUE = MVS_SLOTS - 2, /* SCSI Queue depth */
+ MVS_QUEUE_SIZE = 64, /* Support Queue depth */
+ MVS_RSVD_SLOTS = 4,
MVS_SOC_CAN_QUEUE = MVS_SOC_SLOTS - 2,
};
@@ -144,6 +127,7 @@ enum hw_register_bits {
CINT_DMA_PCIE = (1U << 27), /* DMA to PCIE timeout */
CINT_MEM = (1U << 26), /* int mem parity err */
CINT_I2C_SLAVE = (1U << 25), /* slave I2C event */
+ CINT_NON_SPEC_NCQ_ERROR = (1U << 25), /* Non specific NCQ error */
CINT_SRS = (1U << 3), /* SRS event */
CINT_CI_STOP = (1U << 1), /* cmd issue stopped */
CINT_DONE = (1U << 0), /* cmd completion */
@@ -161,7 +145,7 @@ enum hw_register_bits {
TXQ_CMD_SSP = 1, /* SSP protocol */
TXQ_CMD_SMP = 2, /* SMP protocol */
TXQ_CMD_STP = 3, /* STP/SATA protocol */
- TXQ_CMD_SSP_FREE_LIST = 4, /* add to SSP targ free list */
+ TXQ_CMD_SSP_FREE_LIST = 4, /* add to SSP target free list */
TXQ_CMD_SLOT_RESET = 7, /* reset command slot */
TXQ_MODE_I = (1U << 28), /* mode: 0=target,1=initiator */
TXQ_MODE_TARGET = 0,
@@ -231,7 +215,7 @@ enum hw_register_bits {
/* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */
PHYEV_DEC_ERR = (1U << 24), /* Phy Decoding Error */
- PHYEV_DCDR_ERR = (1U << 23), /* STP Deocder Error */
+ PHYEV_DCDR_ERR = (1U << 23), /* STP Decoder Error */
PHYEV_CRC_ERR = (1U << 22), /* STP CRC Error */
PHYEV_UNASSOC_FIS = (1U << 19), /* unassociated FIS rx'd */
PHYEV_AN = (1U << 18), /* SATA async notification */
@@ -363,7 +347,7 @@ enum sas_cmd_port_registers {
CMD_SATA_PORT_MEM_CTL0 = 0x158, /* SATA Port Memory Control 0 */
CMD_SATA_PORT_MEM_CTL1 = 0x15c, /* SATA Port Memory Control 1 */
CMD_XOR_MEM_BIST_CTL = 0x160, /* XOR Memory BIST Control */
- CMD_XOR_MEM_BIST_STAT = 0x164, /* XOR Memroy BIST Status */
+ CMD_XOR_MEM_BIST_STAT = 0x164, /* XOR Memory BIST Status */
CMD_DMA_MEM_BIST_CTL = 0x168, /* DMA Memory BIST Control */
CMD_DMA_MEM_BIST_STAT = 0x16c, /* DMA Memory BIST Status */
CMD_PORT_MEM_BIST_CTL = 0x170, /* Port Memory BIST Control */
@@ -388,18 +372,20 @@ enum sas_cmd_port_registers {
CMD_SL_MODE0 = 0x1BC, /* SL Mode 0 */
CMD_SL_MODE1 = 0x1C0, /* SL Mode 1 */
CMD_PND_FIFO_CTL1 = 0x1C4, /* Pending FIFO Control 1 */
+ CMD_PORT_LAYER_TIMER1 = 0x1E0, /* Port Layer Timer 1 */
+ CMD_LINK_TIMER = 0x1E4, /* Link Timer */
};
enum mvs_info_flags {
- MVF_MSI = (1U << 0), /* MSI is enabled */
MVF_PHY_PWR_FIX = (1U << 1), /* bug workaround */
MVF_FLAG_SOC = (1U << 2), /* SoC integrated controllers */
};
enum mvs_event_flags {
- PHY_PLUG_EVENT = (3U),
+ PHY_PLUG_EVENT = (3U),
PHY_PLUG_IN = (1U << 0), /* phy plug in */
PHY_PLUG_OUT = (1U << 1), /* phy plug out */
+ EXP_BRCT_CHG = (1U << 2), /* broadcast change */
};
enum mvs_port_type {
@@ -501,9 +487,4 @@ enum datapres_field {
SENSE_DATA = 2,
};
-/* define task management IU */
-struct mvs_tmf_task{
- u8 tmf;
- u16 tag_of_task_to_be_managed;
-};
#endif
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 90b636611cde..7f1ad305eee6 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -1,79 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Marvell 88SE64xx/88SE94xx pci init
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#include "mv_sas.h"
-static int lldd_max_execute_num = 1;
-module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
-MODULE_PARM_DESC(collector, "\n"
- "\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
- "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
- "\tThe mvsas SAS LLDD supports both modes.\n"
- "\tDefault: 1 (Direct Mode).\n");
+int interrupt_coalescing = 0x80;
static struct scsi_transport_template *mvs_stt;
-struct kmem_cache *mvs_task_list_cache;
static const struct mvs_chip_info mvs_chips[] = {
- [chip_6320] = { 1, 2, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
- [chip_6440] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
- [chip_6485] = { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, },
- [chip_9180] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
- [chip_9480] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
- [chip_9445] = { 1, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
- [chip_9485] = { 2, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
- [chip_1300] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
- [chip_1320] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
+ [chip_6320] = { 1, 2, 0x400, 17, 16, 6, 9, &mvs_64xx_dispatch, },
+ [chip_6440] = { 1, 4, 0x400, 17, 16, 6, 9, &mvs_64xx_dispatch, },
+ [chip_6485] = { 1, 8, 0x800, 33, 32, 6, 10, &mvs_64xx_dispatch, },
+ [chip_9180] = { 2, 4, 0x800, 17, 64, 8, 9, &mvs_94xx_dispatch, },
+ [chip_9480] = { 2, 4, 0x800, 17, 64, 8, 9, &mvs_94xx_dispatch, },
+ [chip_9445] = { 1, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
+ [chip_9485] = { 2, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
+ [chip_1300] = { 1, 4, 0x400, 17, 16, 6, 9, &mvs_64xx_dispatch, },
+ [chip_1320] = { 2, 4, 0x800, 17, 64, 8, 9, &mvs_94xx_dispatch, },
};
+static const struct attribute_group *mvst_host_groups[];
+static const struct attribute_group *mvst_sdev_groups[];
+
#define SOC_SAS_NUM 2
-#define SG_MX 64
-
-static struct scsi_host_template mvs_sht = {
- .module = THIS_MODULE,
- .name = DRV_NAME,
- .queuecommand = sas_queuecommand,
- .target_alloc = sas_target_alloc,
- .slave_configure = mvs_slave_configure,
- .slave_destroy = sas_slave_destroy,
+
+static const struct scsi_host_template mvs_sht = {
+ LIBSAS_SHT_BASE
.scan_finished = mvs_scan_finished,
.scan_start = mvs_scan_start,
- .change_queue_depth = sas_change_queue_depth,
- .change_queue_type = sas_change_queue_type,
- .bios_param = sas_bios_param,
.can_queue = 1,
- .cmd_per_lun = 1,
- .this_id = -1,
- .sg_tablesize = SG_MX,
- .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
- .use_clustering = ENABLE_CLUSTERING,
- .eh_device_reset_handler = sas_eh_device_reset_handler,
- .eh_bus_reset_handler = sas_eh_bus_reset_handler,
- .slave_alloc = mvs_slave_alloc,
- .target_destroy = sas_target_destroy,
- .ioctl = sas_ioctl,
+ .sg_tablesize = SG_ALL,
+ .shost_groups = mvst_host_groups,
+ .sdev_groups = mvst_sdev_groups,
+ .track_queue_depth = 1,
};
static struct sas_domain_function_template mvs_transport_ops = {
@@ -83,29 +48,29 @@ static struct sas_domain_function_template mvs_transport_ops = {
.lldd_control_phy = mvs_phy_control,
.lldd_abort_task = mvs_abort_task,
- .lldd_abort_task_set = mvs_abort_task_set,
- .lldd_clear_aca = mvs_clear_aca,
- .lldd_clear_task_set = mvs_clear_task_set,
+ .lldd_abort_task_set = sas_abort_task_set,
+ .lldd_clear_task_set = sas_clear_task_set,
.lldd_I_T_nexus_reset = mvs_I_T_nexus_reset,
.lldd_lu_reset = mvs_lu_reset,
.lldd_query_task = mvs_query_task,
.lldd_port_formed = mvs_port_formed,
.lldd_port_deformed = mvs_port_deformed,
+ .lldd_write_gpio = mvs_gpio_write,
+
};
-static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
+static void mvs_phy_init(struct mvs_info *mvi, int phy_id)
{
struct mvs_phy *phy = &mvi->phy[phy_id];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
phy->mvi = mvi;
- init_timer(&phy->timer);
+ phy->port = NULL;
+ timer_setup(&phy->timer, NULL, 0);
sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
- sas_phy->class = SAS;
sas_phy->iproto = SAS_PROTOCOL_ALL;
sas_phy->tproto = 0;
- sas_phy->type = PHY_TYPE_PHYSICAL;
sas_phy->role = PHY_ROLE_INITIATOR;
sas_phy->oob_mode = OOB_NOT_CONNECTED;
sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
@@ -128,10 +93,9 @@ static void mvs_free(struct mvs_info *mvi)
if (mvi->flags & MVF_FLAG_SOC)
slot_nr = MVS_SOC_SLOTS;
else
- slot_nr = MVS_SLOTS;
+ slot_nr = MVS_CHIP_SLOT_SZ;
- if (mvi->dma_pool)
- pci_pool_destroy(mvi->dma_pool);
+ dma_pool_destroy(mvi->dma_pool);
if (mvi->tx)
dma_free_coherent(mvi->dev,
@@ -148,25 +112,26 @@ static void mvs_free(struct mvs_info *mvi)
dma_free_coherent(mvi->dev,
sizeof(*mvi->slot) * slot_nr,
mvi->slot, mvi->slot_dma);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
+
if (mvi->bulk_buffer)
dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
mvi->bulk_buffer, mvi->bulk_buffer_dma);
-#endif
+ if (mvi->bulk_buffer1)
+ dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
+ mvi->bulk_buffer1, mvi->bulk_buffer_dma1);
MVS_CHIP_DISP->chip_iounmap(mvi);
if (mvi->shost)
scsi_host_put(mvi->shost);
list_for_each_entry(mwq, &mvi->wq_list, entry)
- cancel_delayed_work(&mwq->work_q);
+ cancel_delayed_work_sync(&mwq->work_q);
+ kfree(mvi->rsvd_tags);
kfree(mvi);
}
-#ifdef MVS_USE_TASKLET
-struct tasklet_struct mv_tasklet;
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
static void mvs_tasklet(unsigned long opaque)
{
- unsigned long flags;
u32 stat;
u16 core_nr, i = 0;
@@ -179,35 +144,50 @@ static void mvs_tasklet(unsigned long opaque)
if (unlikely(!mvi))
BUG_ON(1);
+ stat = MVS_CHIP_DISP->isr_status(mvi, mvi->pdev->irq);
+ if (!stat)
+ goto out;
+
for (i = 0; i < core_nr; i++) {
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
- stat = MVS_CHIP_DISP->isr_status(mvi, mvi->irq);
- if (stat)
- MVS_CHIP_DISP->isr(mvi, mvi->irq, stat);
+ MVS_CHIP_DISP->isr(mvi, mvi->pdev->irq, stat);
}
+out:
+ MVS_CHIP_DISP->interrupt_enable(mvi);
}
#endif
static irqreturn_t mvs_interrupt(int irq, void *opaque)
{
- u32 core_nr, i = 0;
u32 stat;
struct mvs_info *mvi;
struct sas_ha_struct *sha = opaque;
+#ifndef CONFIG_SCSI_MVSAS_TASKLET
+ u32 i;
+ u32 core_nr;
core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+#endif
+
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
if (unlikely(!mvi))
return IRQ_NONE;
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+ MVS_CHIP_DISP->interrupt_disable(mvi);
+#endif
stat = MVS_CHIP_DISP->isr_status(mvi, irq);
- if (!stat)
+ if (!stat) {
+ #ifdef CONFIG_SCSI_MVSAS_TASKLET
+ MVS_CHIP_DISP->interrupt_enable(mvi);
+ #endif
return IRQ_NONE;
+ }
-#ifdef MVS_USE_TASKLET
- tasklet_schedule(&mv_tasklet);
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+ tasklet_schedule(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
#else
for (i = 0; i < core_nr; i++) {
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
@@ -217,7 +197,7 @@ static irqreturn_t mvs_interrupt(int irq, void *opaque)
return IRQ_HANDLED;
}
-static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
+static int mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
{
int i = 0, slot_nr;
char pool_name[32];
@@ -225,7 +205,7 @@ static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
if (mvi->flags & MVF_FLAG_SOC)
slot_nr = MVS_SOC_SLOTS;
else
- slot_nr = MVS_SLOTS;
+ slot_nr = MVS_CHIP_SLOT_SZ;
spin_lock_init(&mvi->lock);
for (i = 0; i < mvi->chip->n_phy; i++) {
@@ -236,10 +216,9 @@ static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
}
for (i = 0; i < MVS_MAX_DEVICES; i++) {
mvi->devices[i].taskfileset = MVS_ID_NOT_MAPPED;
- mvi->devices[i].dev_type = NO_DEVICE;
+ mvi->devices[i].dev_type = SAS_PHY_UNUSED;
mvi->devices[i].device_id = i;
mvi->devices[i].dev_status = MVS_DEV_NORMAL;
- init_timer(&mvi->devices[i].timer);
}
/*
@@ -250,19 +229,16 @@ static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
&mvi->tx_dma, GFP_KERNEL);
if (!mvi->tx)
goto err_out;
- memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ);
mvi->rx_fis = dma_alloc_coherent(mvi->dev, MVS_RX_FISL_SZ,
&mvi->rx_fis_dma, GFP_KERNEL);
if (!mvi->rx_fis)
goto err_out;
- memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ);
mvi->rx = dma_alloc_coherent(mvi->dev,
sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
&mvi->rx_dma, GFP_KERNEL);
if (!mvi->rx)
goto err_out;
- memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1));
mvi->rx[0] = cpu_to_le32(0xfff);
mvi->rx_cons = 0xfff;
@@ -271,25 +247,27 @@ static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
&mvi->slot_dma, GFP_KERNEL);
if (!mvi->slot)
goto err_out;
- memset(mvi->slot, 0, sizeof(*mvi->slot) * slot_nr);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
mvi->bulk_buffer = dma_alloc_coherent(mvi->dev,
TRASH_BUCKET_SIZE,
&mvi->bulk_buffer_dma, GFP_KERNEL);
if (!mvi->bulk_buffer)
goto err_out;
-#endif
+
+ mvi->bulk_buffer1 = dma_alloc_coherent(mvi->dev,
+ TRASH_BUCKET_SIZE,
+ &mvi->bulk_buffer_dma1, GFP_KERNEL);
+ if (!mvi->bulk_buffer1)
+ goto err_out;
+
sprintf(pool_name, "%s%d", "mvs_dma_pool", mvi->id);
- mvi->dma_pool = pci_pool_create(pool_name, mvi->pdev, MVS_SLOT_BUF_SZ, 16, 0);
+ mvi->dma_pool = dma_pool_create(pool_name, &mvi->pdev->dev,
+ MVS_SLOT_BUF_SZ, 16, 0);
if (!mvi->dma_pool) {
printk(KERN_DEBUG "failed to create dma pool %s.\n", pool_name);
goto err_out;
}
- mvi->tags_num = slot_nr;
- /* Initialize tags */
- mvs_tag_init(mvi);
return 0;
err_out:
return 1;
@@ -298,7 +276,7 @@ err_out:
int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex)
{
- unsigned long res_start, res_len, res_flag, res_flag_ex = 0;
+ unsigned long res_start, res_len, res_flag_ex = 0;
struct pci_dev *pdev = mvi->pdev;
if (bar_ex != -1) {
/*
@@ -310,13 +288,9 @@ int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex)
goto err_out;
res_flag_ex = pci_resource_flags(pdev, bar_ex);
- if (res_flag_ex & IORESOURCE_MEM) {
- if (res_flag_ex & IORESOURCE_CACHEABLE)
- mvi->regs_ex = ioremap(res_start, res_len);
- else
- mvi->regs_ex = ioremap_nocache(res_start,
- res_len);
- } else
+ if (res_flag_ex & IORESOURCE_MEM)
+ mvi->regs_ex = ioremap(res_start, res_len);
+ else
mvi->regs_ex = (void *)res_start;
if (!mvi->regs_ex)
goto err_out;
@@ -324,14 +298,13 @@ int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex)
res_start = pci_resource_start(pdev, bar);
res_len = pci_resource_len(pdev, bar);
- if (!res_start || !res_len)
+ if (!res_start || !res_len) {
+ iounmap(mvi->regs_ex);
+ mvi->regs_ex = NULL;
goto err_out;
+ }
- res_flag = pci_resource_flags(pdev, bar);
- if (res_flag & IORESOURCE_CACHEABLE)
- mvi->regs = ioremap(res_start, res_len);
- else
- mvi->regs = ioremap_nocache(res_start, res_len);
+ mvi->regs = ioremap(res_start, res_len);
if (!mvi->regs) {
if (mvi->regs_ex && (res_flag_ex & IORESOURCE_MEM))
@@ -350,15 +323,16 @@ void mvs_iounmap(void __iomem *regs)
iounmap(regs);
}
-static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev,
+static struct mvs_info *mvs_pci_alloc(struct pci_dev *pdev,
const struct pci_device_id *ent,
struct Scsi_Host *shost, unsigned int id)
{
- struct mvs_info *mvi;
+ struct mvs_info *mvi = NULL;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
- mvi = kzalloc(sizeof(*mvi) + MVS_SLOTS * sizeof(struct mvs_slot_info),
- GFP_KERNEL);
+ mvi = kzalloc(sizeof(*mvi) +
+ (1L << mvs_chips[ent->driver_data].slot_width) *
+ sizeof(struct mvs_slot_info), GFP_KERNEL);
if (!mvi)
return NULL;
@@ -367,7 +341,6 @@ static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev,
mvi->chip_id = ent->driver_data;
mvi->chip = &mvs_chips[mvi->chip_id];
INIT_LIST_HEAD(&mvi->wq_list);
- mvi->irq = pdev->irq;
((struct mvs_prv_info *)sha->lldd_ha)->mvi[id] = mvi;
((struct mvs_prv_info *)sha->lldd_ha)->n_phy = mvi->chip->n_phy;
@@ -375,9 +348,10 @@ static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev,
mvi->id = id;
mvi->sas = sha;
mvi->shost = shost;
-#ifdef MVS_USE_TASKLET
- tasklet_init(&mv_tasklet, mvs_tasklet, (unsigned long)sha);
-#endif
+
+ mvi->rsvd_tags = bitmap_zalloc(MVS_RSVD_SLOTS, GFP_KERNEL);
+ if (!mvi->rsvd_tags)
+ goto err_out;
if (MVS_CHIP_DISP->chip_ioremap(mvi))
goto err_out;
@@ -388,40 +362,24 @@ err_out:
return NULL;
}
-/* move to PCI layer or libata core? */
static int pci_go_64(struct pci_dev *pdev)
{
int rc;
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
- if (rc) {
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "64-bit DMA enable failed\n");
- return rc;
- }
- }
- } else {
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (rc) {
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (rc) {
dev_printk(KERN_ERR, &pdev->dev,
"32-bit DMA enable failed\n");
return rc;
}
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
- return rc;
- }
}
return rc;
}
-static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost,
+static int mvs_prep_sas_ha_init(struct Scsi_Host *shost,
const struct mvs_chip_info *chip_info)
{
int phy_nr, port_nr; unsigned short core_nr;
@@ -441,7 +399,7 @@ static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost,
sha->sas_phy = arr_phy;
sha->sas_port = arr_port;
- sha->core.shost = shost;
+ sha->shost = shost;
sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL);
if (!sha->lldd_ha)
@@ -450,7 +408,7 @@ static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost,
((struct mvs_prv_info *)sha->lldd_ha)->n_host = core_nr;
shost->transportt = mvs_stt;
- shost->max_id = 128;
+ shost->max_id = MVS_MAX_DEVICES;
shost->max_lun = ~0;
shost->max_channel = 1;
shost->max_cmd_len = 16;
@@ -463,7 +421,7 @@ exit_free:
}
-static void __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost,
+static void mvs_post_sas_ha_init(struct Scsi_Host *shost,
const struct mvs_chip_info *chip_info)
{
int can_queue, i = 0, j = 0;
@@ -483,22 +441,21 @@ static void __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost,
sha->sas_ha_name = DRV_NAME;
sha->dev = mvi->dev;
- sha->lldd_module = THIS_MODULE;
sha->sas_addr = &mvi->sas_addr[0];
sha->num_phys = nr_core * chip_info->n_phy;
- sha->lldd_max_execute_num = lldd_max_execute_num;
-
if (mvi->flags & MVF_FLAG_SOC)
can_queue = MVS_SOC_CAN_QUEUE;
else
- can_queue = MVS_CAN_QUEUE;
+ can_queue = MVS_CHIP_SLOT_SZ;
- sha->lldd_queue_size = can_queue;
+ can_queue -= MVS_RSVD_SLOTS;
+
+ shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG);
shost->can_queue = can_queue;
- mvi->shost->cmd_per_lun = MVS_SLOTS/sha->num_phys;
- sha->core.shost = mvi->shost;
+ mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE;
+ sha->shost = mvi->shost;
}
static void mvs_init_sas_add(struct mvs_info *mvi)
@@ -513,8 +470,7 @@ static void mvs_init_sas_add(struct mvs_info *mvi)
memcpy(mvi->sas_addr, &mvi->phy[0].dev_sas_addr, SAS_ADDR_SIZE);
}
-static int __devinit mvs_pci_init(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static int mvs_pci_init(struct pci_dev *pdev, const struct pci_device_id *ent)
{
unsigned int rc, nhost = 0;
struct mvs_info *mvi;
@@ -548,14 +504,14 @@ static int __devinit mvs_pci_init(struct pci_dev *pdev,
SHOST_TO_SAS_HA(shost) =
kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
if (!SHOST_TO_SAS_HA(shost)) {
- kfree(shost);
+ scsi_host_put(shost);
rc = -ENOMEM;
goto err_out_regions;
}
rc = mvs_prep_sas_ha_init(shost, chip);
if (rc) {
- kfree(shost);
+ scsi_host_put(shost);
rc = -ENOMEM;
goto err_out_regions;
}
@@ -569,6 +525,9 @@ static int __devinit mvs_pci_init(struct pci_dev *pdev,
goto err_out_regions;
}
+ memset(&mvi->hba_info_param, 0xFF,
+ sizeof(struct hba_info_page));
+
mvs_init_sas_add(mvi);
mvi->instance = nhost;
@@ -579,9 +538,13 @@ static int __devinit mvs_pci_init(struct pci_dev *pdev,
}
nhost++;
} while (nhost < chip->n_host);
-#ifdef MVS_USE_TASKLET
- tasklet_init(&mv_tasklet, mvs_tasklet,
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+ {
+ struct mvs_prv_info *mpi = SHOST_TO_SAS_HA(shost)->lldd_ha;
+
+ tasklet_init(&(mpi->mv_tasklet), mvs_tasklet,
(unsigned long)SHOST_TO_SAS_HA(shost));
+ }
#endif
mvs_post_sas_ha_init(shost, chip);
@@ -616,7 +579,7 @@ err_out_enable:
return rc;
}
-static void __devexit mvs_pci_remove(struct pci_dev *pdev)
+static void mvs_pci_remove(struct pci_dev *pdev)
{
unsigned short core_nr, i = 0;
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
@@ -625,17 +588,15 @@ static void __devexit mvs_pci_remove(struct pci_dev *pdev)
core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
-#ifdef MVS_USE_TASKLET
- tasklet_kill(&mv_tasklet);
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+ tasklet_kill(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet);
#endif
- pci_set_drvdata(pdev, NULL);
sas_unregister_ha(sha);
sas_remove_host(mvi->shost);
- scsi_remove_host(mvi->shost);
MVS_CHIP_DISP->interrupt_disable(mvi);
- free_irq(mvi->irq, sha);
+ free_irq(mvi->pdev->irq, sha);
for (i = 0; i < core_nr; i++) {
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
mvs_free(mvi);
@@ -648,7 +609,7 @@ static void __devexit mvs_pci_remove(struct pci_dev *pdev)
return;
}
-static struct pci_device_id __devinitdata mvs_pci_table[] = {
+static const struct pci_device_id mvs_pci_table[] = {
{ PCI_VDEVICE(MARVELL, 0x6320), chip_6320 },
{ PCI_VDEVICE(MARVELL, 0x6340), chip_6440 },
{
@@ -667,6 +628,7 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = {
{ PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1300), chip_1300 },
{ PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1320), chip_1320 },
{ PCI_VDEVICE(ADAPTEC2, 0x0450), chip_6440 },
+ { PCI_VDEVICE(TTI, 0x2640), chip_6440 },
{ PCI_VDEVICE(TTI, 0x2710), chip_9480 },
{ PCI_VDEVICE(TTI, 0x2720), chip_9480 },
{ PCI_VDEVICE(TTI, 0x2721), chip_9480 },
@@ -675,23 +637,34 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = {
{ PCI_VDEVICE(TTI, 0x2744), chip_9480 },
{ PCI_VDEVICE(TTI, 0x2760), chip_9480 },
{
- .vendor = 0x1b4b,
- .device = 0x9445,
+ .vendor = PCI_VENDOR_ID_MARVELL_EXT,
+ .device = 0x9480,
.subvendor = PCI_ANY_ID,
.subdevice = 0x9480,
.class = 0,
.class_mask = 0,
- .driver_data = chip_9445,
+ .driver_data = chip_9480,
},
{
- .vendor = 0x1b4b,
- .device = 0x9485,
+ .vendor = PCI_VENDOR_ID_MARVELL_EXT,
+ .device = 0x9445,
.subvendor = PCI_ANY_ID,
.subdevice = 0x9480,
.class = 0,
.class_mask = 0,
- .driver_data = chip_9485,
+ .driver_data = chip_9445,
},
+ { PCI_VDEVICE(MARVELL_EXT, 0x9485), chip_9485 }, /* Marvell 9480/9485 (any vendor/model) */
+ { PCI_VDEVICE(OCZ, 0x1021), chip_9485}, /* OCZ RevoDrive3 */
+ { PCI_VDEVICE(OCZ, 0x1022), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1040), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1041), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1042), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1043), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1044), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1080), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1083), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
+ { PCI_VDEVICE(OCZ, 0x1084), chip_9485}, /* OCZ RevoDrive3/zDriveR4 (exact model unknown) */
{ } /* terminate list */
};
@@ -700,11 +673,59 @@ static struct pci_driver mvs_pci_driver = {
.name = DRV_NAME,
.id_table = mvs_pci_table,
.probe = mvs_pci_init,
- .remove = __devexit_p(mvs_pci_remove),
+ .remove = mvs_pci_remove,
};
-/* task handler */
-struct task_struct *mvs_th;
+static DEVICE_STRING_ATTR_RO(driver_version, 0444, DRV_VERSION);
+
+static ssize_t interrupt_coalescing_store(struct device *cdev,
+ struct device_attribute *attr,
+ const char *buffer, size_t size)
+{
+ unsigned int val = 0;
+ struct mvs_info *mvi = NULL;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ u8 i, core_nr;
+ if (buffer == NULL)
+ return size;
+
+ if (sscanf(buffer, "%u", &val) != 1)
+ return -EINVAL;
+
+ if (val >= 0x10000) {
+ mv_dprintk("interrupt coalescing timer %d us is"
+ "too long\n", val);
+ return strlen(buffer);
+ }
+
+ interrupt_coalescing = val;
+
+ core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+ if (unlikely(!mvi))
+ return -EINVAL;
+
+ for (i = 0; i < core_nr; i++) {
+ mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+ if (MVS_CHIP_DISP->tune_interrupt)
+ MVS_CHIP_DISP->tune_interrupt(mvi,
+ interrupt_coalescing);
+ }
+ mv_dprintk("set interrupt coalescing time to %d us\n",
+ interrupt_coalescing);
+ return strlen(buffer);
+}
+
+static ssize_t interrupt_coalescing_show(struct device *cdev,
+ struct device_attribute *attr, char *buffer)
+{
+ return sysfs_emit(buffer, "%d\n", interrupt_coalescing);
+}
+
+static DEVICE_ATTR_RW(interrupt_coalescing);
+
static int __init mvs_init(void)
{
int rc;
@@ -712,16 +733,7 @@ static int __init mvs_init(void)
if (!mvs_stt)
return -ENOMEM;
- mvs_task_list_cache = kmem_cache_create("mvs_task_list", sizeof(struct mvs_task_list),
- 0, SLAB_HWCACHE_ALIGN, NULL);
- if (!mvs_task_list_cache) {
- rc = -ENOMEM;
- mv_printk("%s: mvs_task_list_cache alloc failed! \n", __func__);
- goto err_out;
- }
-
rc = pci_register_driver(&mvs_pci_driver);
-
if (rc)
goto err_out;
@@ -736,9 +748,21 @@ static void __exit mvs_exit(void)
{
pci_unregister_driver(&mvs_pci_driver);
sas_release_transport(mvs_stt);
- kmem_cache_destroy(mvs_task_list_cache);
}
+static struct attribute *mvst_host_attrs[] = {
+ &dev_attr_driver_version.attr.attr,
+ &dev_attr_interrupt_coalescing.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(mvst_host);
+
+static const struct attribute_group *mvst_sdev_groups[] = {
+ &sas_ata_sdev_attr_group,
+ NULL
+};
+
module_init(mvs_init);
module_exit(mvs_exit);
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 0ef27425c447..f2e7997d5b9d 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -1,26 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Marvell 88SE64xx/88SE94xx main function
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#include "mv_sas.h"
@@ -36,165 +20,41 @@ static int mvs_find_tag(struct mvs_info *mvi, struct sas_task *task, u32 *tag)
return 0;
}
-void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
+static void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
{
- void *bitmap = &mvi->tags;
+ void *bitmap = mvi->rsvd_tags;
clear_bit(tag, bitmap);
}
-void mvs_tag_free(struct mvs_info *mvi, u32 tag)
+static void mvs_tag_free(struct mvs_info *mvi, u32 tag)
{
+ if (tag >= MVS_RSVD_SLOTS)
+ return;
+
mvs_tag_clear(mvi, tag);
}
-void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
+static void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
{
- void *bitmap = &mvi->tags;
+ void *bitmap = mvi->rsvd_tags;
set_bit(tag, bitmap);
}
-inline int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
+static int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
{
unsigned int index, tag;
- void *bitmap = &mvi->tags;
+ void *bitmap = mvi->rsvd_tags;
- index = find_first_zero_bit(bitmap, mvi->tags_num);
+ index = find_first_zero_bit(bitmap, MVS_RSVD_SLOTS);
tag = index;
- if (tag >= mvi->tags_num)
+ if (tag >= MVS_RSVD_SLOTS)
return -SAS_QUEUE_FULL;
mvs_tag_set(mvi, tag);
*tag_out = tag;
return 0;
}
-void mvs_tag_init(struct mvs_info *mvi)
-{
- int i;
- for (i = 0; i < mvi->tags_num; ++i)
- mvs_tag_clear(mvi, i);
-}
-
-void mvs_hexdump(u32 size, u8 *data, u32 baseaddr)
-{
- u32 i;
- u32 run;
- u32 offset;
-
- offset = 0;
- while (size) {
- printk(KERN_DEBUG"%08X : ", baseaddr + offset);
- if (size >= 16)
- run = 16;
- else
- run = size;
- size -= run;
- for (i = 0; i < 16; i++) {
- if (i < run)
- printk(KERN_DEBUG"%02X ", (u32)data[i]);
- else
- printk(KERN_DEBUG" ");
- }
- printk(KERN_DEBUG": ");
- for (i = 0; i < run; i++)
- printk(KERN_DEBUG"%c",
- isalnum(data[i]) ? data[i] : '.');
- printk(KERN_DEBUG"\n");
- data = &data[16];
- offset += run;
- }
- printk(KERN_DEBUG"\n");
-}
-
-#if (_MV_DUMP > 1)
-static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag,
- enum sas_protocol proto)
-{
- u32 offset;
- struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
- offset = slot->cmd_size + MVS_OAF_SZ +
- MVS_CHIP_DISP->prd_size() * slot->n_elem;
- dev_printk(KERN_DEBUG, mvi->dev, "+---->Status buffer[%d] :\n",
- tag);
- mvs_hexdump(32, (u8 *) slot->response,
- (u32) slot->buf_dma + offset);
-}
-#endif
-
-static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag,
- enum sas_protocol proto)
-{
-#if (_MV_DUMP > 1)
- u32 sz, w_ptr;
- u64 addr;
- struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
- /*Delivery Queue */
- sz = MVS_CHIP_SLOT_SZ;
- w_ptr = slot->tx;
- addr = mvi->tx_dma;
- dev_printk(KERN_DEBUG, mvi->dev,
- "Delivery Queue Size=%04d , WRT_PTR=%04X\n", sz, w_ptr);
- dev_printk(KERN_DEBUG, mvi->dev,
- "Delivery Queue Base Address=0x%llX (PA)"
- "(tx_dma=0x%llX), Entry=%04d\n",
- addr, (unsigned long long)mvi->tx_dma, w_ptr);
- mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]),
- (u32) mvi->tx_dma + sizeof(u32) * w_ptr);
- /*Command List */
- addr = mvi->slot_dma;
- dev_printk(KERN_DEBUG, mvi->dev,
- "Command List Base Address=0x%llX (PA)"
- "(slot_dma=0x%llX), Header=%03d\n",
- addr, (unsigned long long)slot->buf_dma, tag);
- dev_printk(KERN_DEBUG, mvi->dev, "Command Header[%03d]:\n", tag);
- /*mvs_cmd_hdr */
- mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]),
- (u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr));
- /*1.command table area */
- dev_printk(KERN_DEBUG, mvi->dev, "+---->Command Table :\n");
- mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma);
- /*2.open address frame area */
- dev_printk(KERN_DEBUG, mvi->dev, "+---->Open Address Frame :\n");
- mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size,
- (u32) slot->buf_dma + slot->cmd_size);
- /*3.status buffer */
- mvs_hba_sb_dump(mvi, tag, proto);
- /*4.PRD table */
- dev_printk(KERN_DEBUG, mvi->dev, "+---->PRD table :\n");
- mvs_hexdump(MVS_CHIP_DISP->prd_size() * slot->n_elem,
- (u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ,
- (u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ);
-#endif
-}
-
-static void mvs_hba_cq_dump(struct mvs_info *mvi)
-{
-#if (_MV_DUMP > 2)
- u64 addr;
- void __iomem *regs = mvi->regs;
- u32 entry = mvi->rx_cons + 1;
- u32 rx_desc = le32_to_cpu(mvi->rx[entry]);
-
- /*Completion Queue */
- addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO);
- dev_printk(KERN_DEBUG, mvi->dev, "Completion Task = 0x%p\n",
- mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task);
- dev_printk(KERN_DEBUG, mvi->dev,
- "Completion List Base Address=0x%llX (PA), "
- "CQ_Entry=%04d, CQ_WP=0x%08X\n",
- addr, entry - 1, mvi->rx[0]);
- mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc),
- mvi->rx_dma + sizeof(u32) * entry);
-#endif
-}
-
-void mvs_get_sas_addr(void *buf, u32 buflen)
-{
- /*memcpy(buf, "\x50\x05\x04\x30\x11\xab\x64\x40", 8);*/
-}
-
-struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
+static struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
{
unsigned long i = 0, j = 0, hi = 0;
struct sas_ha_struct *sha = dev->port->ha;
@@ -203,8 +63,10 @@ struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
while (sha->sas_port[i]) {
if (sha->sas_port[i] == dev->port) {
+ spin_lock(&sha->sas_port[i]->phy_list_lock);
phy = container_of(sha->sas_port[i]->phy_list.next,
struct asd_sas_phy, port_phy_el);
+ spin_unlock(&sha->sas_port[i]->phy_list_lock);
j = 0;
while (sha->sas_phy[j]) {
if (sha->sas_phy[j] == phy)
@@ -222,8 +84,7 @@ struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
}
-/* FIXME */
-int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
+static int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
{
unsigned long i = 0, j = 0, n = 0, num = 0;
struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
@@ -233,6 +94,8 @@ int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
while (sha->sas_port[i]) {
if (sha->sas_port[i] == dev->port) {
struct asd_sas_phy *phy;
+
+ spin_lock(&sha->sas_port[i]->phy_list_lock);
list_for_each_entry(phy,
&sha->sas_port[i]->phy_list, port_phy_el) {
j = 0;
@@ -246,6 +109,7 @@ int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
num++;
n++;
}
+ spin_unlock(&sha->sas_port[i]->phy_list_lock);
break;
}
i++;
@@ -253,6 +117,20 @@ int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
return num;
}
+struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi,
+ u8 reg_set)
+{
+ u32 dev_no;
+ for (dev_no = 0; dev_no < MVS_MAX_DEVICES; dev_no++) {
+ if (mvi->devices[dev_no].taskfileset == MVS_ID_NOT_MAPPED)
+ continue;
+
+ if (mvi->devices[dev_no].taskfileset == reg_set)
+ return &mvi->devices[dev_no];
+ }
+ return NULL;
+}
+
static inline void mvs_free_reg_set(struct mvs_info *mvi,
struct mvs_device *dev)
{
@@ -273,17 +151,6 @@ static inline u8 mvs_assign_reg_set(struct mvs_info *mvi,
return MVS_CHIP_DISP->assign_reg_set(mvi, &dev->taskfileset);
}
-void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard)
-{
- u32 no;
- for_each_phy(phy_mask, phy_mask, no) {
- if (!(phy_mask & 1))
- continue;
- MVS_CHIP_DISP->phy_reset(mvi, no, hard);
- }
-}
-
-/* FIXME: locking? */
int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
void *funcdata)
{
@@ -309,12 +176,12 @@ int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_id);
if (tmp & PHY_RST_HARD)
break;
- MVS_CHIP_DISP->phy_reset(mvi, phy_id, 1);
+ MVS_CHIP_DISP->phy_reset(mvi, phy_id, MVS_HARD_RESET);
break;
case PHY_FUNC_LINK_RESET:
MVS_CHIP_DISP->phy_enable(mvi, phy_id);
- MVS_CHIP_DISP->phy_reset(mvi, phy_id, 0);
+ MVS_CHIP_DISP->phy_reset(mvi, phy_id, MVS_SOFT_RESET);
break;
case PHY_FUNC_DISABLE:
@@ -322,14 +189,14 @@ int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
break;
case PHY_FUNC_RELEASE_SPINUP_HOLD:
default:
- rc = -EOPNOTSUPP;
+ rc = -ENOSYS;
}
msleep(200);
return rc;
}
-void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
- u32 off_lo, u32 off_hi, u64 sas_addr)
+void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo,
+ u32 off_hi, u64 sas_addr)
{
u32 lo = (u32)sas_addr;
u32 hi = (u32)(sas_addr>>32);
@@ -340,11 +207,11 @@ void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
MVS_CHIP_DISP->write_port_cfg_data(mvi, port_id, hi);
}
-static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
+static void mvs_bytes_dmaed(struct mvs_info *mvi, int i, gfp_t gfp_flags)
{
struct mvs_phy *phy = &mvi->phy[i];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
- struct sas_ha_struct *sas_ha;
+
if (!phy->phy_attached)
return;
@@ -353,8 +220,7 @@ static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
return;
}
- sas_ha = mvi->sas;
- sas_ha->notify_phy_event(sas_phy, PHYE_OOB_DONE);
+ sas_notify_phy_event(sas_phy, PHYE_OOB_DONE, gfp_flags);
if (sas_phy->phy) {
struct sas_phy *sphy = sas_phy->phy;
@@ -373,6 +239,12 @@ static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
id->dev_type = phy->identify.device_type;
id->initiator_bits = SAS_PROTOCOL_ALL;
id->target_bits = phy->identify.target_port_protocols;
+
+ /* direct attached SAS device */
+ if (phy->att_dev_info & PORT_SSP_TRGT_MASK) {
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
+ MVS_CHIP_DISP->write_port_cfg_data(mvi, i, 0x00);
+ }
} else if (phy->phy_type & PORT_TYPE_SATA) {
/*Nothing*/
}
@@ -380,42 +252,7 @@ static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
- mvi->sas->notify_port_event(sas_phy,
- PORTE_BYTES_DMAED);
-}
-
-int mvs_slave_alloc(struct scsi_device *scsi_dev)
-{
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
- if (dev_is_sata(dev)) {
- /* We don't need to rescan targets
- * if REPORT_LUNS request is failed
- */
- if (scsi_dev->lun > 0)
- return -ENXIO;
- scsi_dev->tagged_supported = 1;
- }
-
- return sas_slave_alloc(scsi_dev);
-}
-
-int mvs_slave_configure(struct scsi_device *sdev)
-{
- struct domain_device *dev = sdev_to_domain_dev(sdev);
- int ret = sas_slave_configure(sdev);
-
- if (ret)
- return ret;
- if (dev_is_sata(dev)) {
- /* may set PIO mode */
- #if MV_DISABLE_NCQ
- struct ata_port *ap = dev->sata_dev.ap;
- struct ata_device *adev = ap->link.device;
- adev->flags |= ATA_DFLAG_NCQ_OFF;
- scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
- #endif
- }
- return 0;
+ sas_notify_port_event(sas_phy, PORTE_BYTES_DMAED, gfp_flags);
}
void mvs_scan_start(struct Scsi_Host *shost)
@@ -424,24 +261,27 @@ void mvs_scan_start(struct Scsi_Host *shost)
unsigned short core_nr;
struct mvs_info *mvi;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct mvs_prv_info *mvs_prv = sha->lldd_ha;
core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
for (j = 0; j < core_nr; j++) {
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j];
for (i = 0; i < mvi->chip->n_phy; ++i)
- mvs_bytes_dmaed(mvi, i);
+ mvs_bytes_dmaed(mvi, i, GFP_KERNEL);
}
+ mvs_prv->scan_finished = 1;
}
int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
- /* give the phy enabling interrupt event time to come in (1s
- * is empirically about all it takes) */
- if (time < HZ)
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct mvs_prv_info *mvs_prv = sha->lldd_ha;
+
+ if (mvs_prv->scan_finished == 0)
return 0;
- /* Wait for discovery to finish */
- scsi_flush_work(shost);
+
+ sas_drain_work(sha);
return 1;
}
@@ -449,10 +289,13 @@ static int mvs_task_prep_smp(struct mvs_info *mvi,
struct mvs_task_exec_info *tei)
{
int elem, rc, i;
+ struct sas_ha_struct *sha = mvi->sas;
struct sas_task *task = tei->task;
struct mvs_cmd_hdr *hdr = tei->hdr;
struct domain_device *dev = task->dev;
struct asd_sas_port *sas_port = dev->port;
+ struct sas_phy *sphy = dev->phy;
+ struct asd_sas_phy *sas_phy = sha->sas_phy[sphy->number];
struct scatterlist *sg_req, *sg_resp;
u32 req_len, resp_len, tag = tei->tag;
void *buf_tmp;
@@ -461,21 +304,18 @@ static int mvs_task_prep_smp(struct mvs_info *mvi,
void *buf_prd;
struct mvs_slot_info *slot = &mvi->slot_info[tag];
u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#if _MV_DUMP
- u8 *buf_cmd;
- void *from;
-#endif
+
/*
* DMA-map SMP request, response buffers
*/
sg_req = &task->smp_task.smp_req;
- elem = dma_map_sg(mvi->dev, sg_req, 1, PCI_DMA_TODEVICE);
+ elem = dma_map_sg(mvi->dev, sg_req, 1, DMA_TO_DEVICE);
if (!elem)
return -ENOMEM;
req_len = sg_dma_len(sg_req);
sg_resp = &task->smp_task.smp_resp;
- elem = dma_map_sg(mvi->dev, sg_resp, 1, PCI_DMA_FROMDEVICE);
+ elem = dma_map_sg(mvi->dev, sg_resp, 1, DMA_FROM_DEVICE);
if (!elem) {
rc = -ENOMEM;
goto err_out;
@@ -496,15 +336,7 @@ static int mvs_task_prep_smp(struct mvs_info *mvi,
buf_tmp = slot->buf;
buf_tmp_dma = slot->buf_dma;
-#if _MV_DUMP
- buf_cmd = buf_tmp;
- hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
- buf_tmp += req_len;
- buf_tmp_dma += req_len;
- slot->cmd_size = req_len;
-#else
hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req));
-#endif
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
buf_oaf = buf_tmp;
@@ -536,7 +368,7 @@ static int mvs_task_prep_smp(struct mvs_info *mvi,
slot->tx = mvi->tx_prod;
mvi->tx[mvi->tx_prod] = cpu_to_le32((TXQ_CMD_SMP << TXQ_CMD_SHIFT) |
TXQ_MODE_I | tag |
- (sas_port->phy_mask << TXQ_PHY_SHIFT));
+ (MVS_PHY_ID << TXQ_PHY_SHIFT));
hdr->flags |= flags;
hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | ((req_len - 4) / 4));
@@ -546,27 +378,21 @@ static int mvs_task_prep_smp(struct mvs_info *mvi,
/* generate open address frame hdr (first 12 bytes) */
/* initiator, SMP, ftype 1h */
buf_oaf[0] = (1 << 7) | (PROTOCOL_SMP << 4) | 0x01;
- buf_oaf[1] = dev->linkrate & 0xf;
+ buf_oaf[1] = min(sas_port->linkrate, dev->linkrate) & 0xf;
*(u16 *)(buf_oaf + 2) = 0xFFFF; /* SAS SPEC */
memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
/* fill in PRD (scatter/gather) table, if any */
MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
-#if _MV_DUMP
- /* copy cmd table */
- from = kmap_atomic(sg_page(sg_req), KM_IRQ0);
- memcpy(buf_cmd, from + sg_req->offset, req_len);
- kunmap_atomic(from, KM_IRQ0);
-#endif
return 0;
err_out_2:
dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
+ DMA_FROM_DEVICE);
err_out:
dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
+ DMA_TO_DEVICE);
return rc;
}
@@ -576,7 +402,10 @@ static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag)
if (qc) {
if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
- qc->tf.command == ATA_CMD_FPDMA_READ) {
+ qc->tf.command == ATA_CMD_FPDMA_READ ||
+ qc->tf.command == ATA_CMD_FPDMA_RECV ||
+ qc->tf.command == ATA_CMD_FPDMA_SEND ||
+ qc->tf.command == ATA_CMD_NCQ_NON_DATA) {
*tag = qc->tag;
return 1;
}
@@ -612,30 +441,24 @@ static int mvs_task_prep_ata(struct mvs_info *mvi,
slot->tx = mvi->tx_prod;
del_q = TXQ_MODE_I | tag |
(TXQ_CMD_STP << TXQ_CMD_SHIFT) |
- (sas_port->phy_mask << TXQ_PHY_SHIFT) |
+ ((sas_port->phy_mask & TXQ_PHY_MASK) << TXQ_PHY_SHIFT) |
(mvi_dev->taskfileset << TXQ_SRS_SHIFT);
mvi->tx[mvi->tx_prod] = cpu_to_le32(del_q);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
if (task->data_dir == DMA_FROM_DEVICE)
flags = (MVS_CHIP_DISP->prd_count() << MCH_PRD_LEN_SHIFT);
else
flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#else
- flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#endif
+
if (task->ata_task.use_ncq)
flags |= MCH_FPDMA;
- if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
+ if (dev->sata_dev.class == ATA_DEV_ATAPI) {
if (task->ata_task.fis.command != ATA_CMD_ID_ATAPI)
flags |= MCH_ATAPI;
}
- /* FIXME: fill in port multiplier number */
-
hdr->flags = cpu_to_le32(flags);
- /* FIXME: the low order order 5 bits for the TAG if enable NCQ */
if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr_tag))
task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
else
@@ -657,9 +480,6 @@ static int mvs_task_prep_ata(struct mvs_info *mvi,
buf_tmp += MVS_ATA_CMD_SZ;
buf_tmp_dma += MVS_ATA_CMD_SZ;
-#if _MV_DUMP
- slot->cmd_size = MVS_ATA_CMD_SZ;
-#endif
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
/* used for STP. unused for SATA? */
@@ -682,9 +502,6 @@ static int mvs_task_prep_ata(struct mvs_info *mvi,
buf_tmp_dma += i;
/* region 4: status buffer (larger the PRD, smaller this buf) ****** */
- /* FIXME: probably unused, for SATA. kept here just in case
- * we get a STP/SATA error information record
- */
slot->response = buf_tmp;
hdr->status_buf = cpu_to_le64(buf_tmp_dma);
if (mvi->flags & MVF_FLAG_SOC)
@@ -702,30 +519,30 @@ static int mvs_task_prep_ata(struct mvs_info *mvi,
task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
/* fill in command FIS and ATAPI CDB */
memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
- if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
+ if (dev->sata_dev.class == ATA_DEV_ATAPI)
memcpy(buf_cmd + STP_ATAPI_CMD,
task->ata_task.atapi_packet, 16);
/* generate open address frame hdr (first 12 bytes) */
/* initiator, STP, ftype 1h */
buf_oaf[0] = (1 << 7) | (PROTOCOL_STP << 4) | 0x1;
- buf_oaf[1] = dev->linkrate & 0xf;
+ buf_oaf[1] = min(sas_port->linkrate, dev->linkrate) & 0xf;
*(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
/* fill in PRD (scatter/gather) table, if any */
MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
+
if (task->data_dir == DMA_FROM_DEVICE)
- MVS_CHIP_DISP->dma_fix(mvi->bulk_buffer_dma,
+ MVS_CHIP_DISP->dma_fix(mvi, sas_port->phy_mask,
TRASH_BUCKET_SIZE, tei->n_elem, buf_prd);
-#endif
+
return 0;
}
static int mvs_task_prep_ssp(struct mvs_info *mvi,
struct mvs_task_exec_info *tei, int is_tmf,
- struct mvs_tmf_task *tmf)
+ struct sas_tmf_task *tmf)
{
struct sas_task *task = tei->task;
struct mvs_cmd_hdr *hdr = tei->hdr;
@@ -737,7 +554,7 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
void *buf_prd;
struct ssp_frame_hdr *ssp_hdr;
void *buf_tmp;
- u8 *buf_cmd, *buf_oaf, fburst = 0;
+ u8 *buf_cmd, *buf_oaf;
dma_addr_t buf_tmp_dma;
u32 flags;
u32 resp_len, req_len, i, tag = tei->tag;
@@ -755,12 +572,11 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
(phy_mask << TXQ_PHY_SHIFT));
flags = MCH_RETRY;
- if (task->ssp_task.enable_first_burst) {
- flags |= MCH_FBURST;
- fburst = (1 << 7);
- }
if (is_tmf)
flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT);
+ else
+ flags |= (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT);
+
hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT));
hdr->tags = cpu_to_le32(tag);
hdr->data_len = cpu_to_le32(task->total_xfer_len);
@@ -777,9 +593,6 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
buf_tmp += MVS_SSP_CMD_SZ;
buf_tmp_dma += MVS_SSP_CMD_SZ;
-#if _MV_DUMP
- slot->cmd_size = MVS_SSP_CMD_SZ;
-#endif
/* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
buf_oaf = buf_tmp;
@@ -817,7 +630,7 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
/* generate open address frame hdr (first 12 bytes) */
/* initiator, SSP, ftype 1h */
buf_oaf[0] = (1 << 7) | (PROTOCOL_SSP << 4) | 0x1;
- buf_oaf[1] = dev->linkrate & 0xf;
+ buf_oaf[1] = min(sas_port->linkrate, dev->linkrate) & 0xf;
*(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
@@ -840,9 +653,9 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
memcpy(buf_cmd, &task->ssp_task.LUN, 8);
if (ssp_hdr->frame_type != SSP_TASK) {
- buf_cmd[9] = fburst | task->ssp_task.task_attr |
- (task->ssp_task.task_prio << 3);
- memcpy(buf_cmd + 12, &task->ssp_task.cdb, 16);
+ buf_cmd[9] = task->ssp_task.task_attr;
+ memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
} else{
buf_cmd[10] = tmf->tmf;
switch (tmf->tmf) {
@@ -862,15 +675,16 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
return 0;
}
-#define DEV_IS_GONE(mvi_dev) ((!mvi_dev || (mvi_dev->dev_type == NO_DEVICE)))
+#define DEV_IS_GONE(mvi_dev) ((!mvi_dev || (mvi_dev->dev_type == SAS_PHY_UNUSED)))
static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf,
- struct mvs_tmf_task *tmf, int *pass)
+ struct sas_tmf_task *tmf, int *pass)
{
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_task_exec_info tei;
struct mvs_slot_info *slot;
u32 tag = 0xdeadbeef, n_elem = 0;
+ struct request *rq;
int rc = 0;
if (!dev->port) {
@@ -882,7 +696,7 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
* libsas will use dev->port, should
* not call task_done for sata
*/
- if (dev->dev_type != SATA_DEV)
+ if (dev->dev_type != SAS_SATA_DEV)
task->task_done(task);
return rc;
}
@@ -895,8 +709,8 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
- rc = SAS_PHY_DOWN;
- return rc;
+ rc = SAS_PHY_DOWN;
+ return rc;
}
tei.port = dev->port->lldd_port;
if (tei.port && !tei.port->port_attached && !tmf) {
@@ -935,9 +749,14 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
n_elem = task->num_scatter;
}
- rc = mvs_tag_alloc(mvi, &tag);
- if (rc)
- goto err_out;
+ rq = sas_task_find_rq(task);
+ if (rq) {
+ tag = rq->tag + MVS_RSVD_SLOTS;
+ } else {
+ rc = mvs_tag_alloc(mvi, &tag);
+ if (rc)
+ goto err_out;
+ }
slot = &mvi->slot_info[tag];
@@ -945,10 +764,11 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
slot->n_elem = n_elem;
slot->slot_tag = tag;
- slot->buf = pci_pool_alloc(mvi->dma_pool, GFP_ATOMIC, &slot->buf_dma);
- if (!slot->buf)
+ slot->buf = dma_pool_zalloc(mvi->dma_pool, GFP_ATOMIC, &slot->buf_dma);
+ if (!slot->buf) {
+ rc = -ENOMEM;
goto err_out_tag;
- memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
+ }
tei.task = task;
tei.hdr = &mvi->slot[tag];
@@ -982,11 +802,7 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
slot->port = tei.port;
task->lldd_task = slot;
list_add_tail(&slot->entry, &tei.port->list);
- spin_lock(&task->task_state_lock);
- task->task_state_flags |= SAS_TASK_AT_INITIATOR;
- spin_unlock(&task->task_state_lock);
- mvs_hba_memory_dump(mvi, tag, task->task_proto);
mvi_dev->running_req++;
++(*pass);
mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
@@ -994,7 +810,7 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
return rc;
err_out_slot_buf:
- pci_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
+ dma_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
err_out_tag:
mvs_tag_free(mvi, tag);
err_out:
@@ -1002,63 +818,23 @@ err_out:
dev_printk(KERN_ERR, mvi->dev, "mvsas prep failed[%d]!\n", rc);
if (!sas_protocol_ata(task->task_proto))
if (n_elem)
- dma_unmap_sg(mvi->dev, task->scatter, n_elem,
+ dma_unmap_sg(mvi->dev, task->scatter, task->num_scatter,
task->data_dir);
prep_out:
return rc;
}
-static struct mvs_task_list *mvs_task_alloc_list(int *num, gfp_t gfp_flags)
-{
- struct mvs_task_list *first = NULL;
-
- for (; *num > 0; --*num) {
- struct mvs_task_list *mvs_list = kmem_cache_zalloc(mvs_task_list_cache, gfp_flags);
-
- if (!mvs_list)
- break;
-
- INIT_LIST_HEAD(&mvs_list->list);
- if (!first)
- first = mvs_list;
- else
- list_add_tail(&mvs_list->list, &first->list);
-
- }
-
- return first;
-}
-
-static inline void mvs_task_free_list(struct mvs_task_list *mvs_list)
-{
- LIST_HEAD(list);
- struct list_head *pos, *a;
- struct mvs_task_list *mlist = NULL;
-
- __list_add(&list, mvs_list->list.prev, &mvs_list->list);
-
- list_for_each_safe(pos, a, &list) {
- list_del_init(pos);
- mlist = list_entry(pos, struct mvs_task_list, list);
- kmem_cache_free(mvs_task_list_cache, mlist);
- }
-}
-
-static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
- struct completion *completion, int is_tmf,
- struct mvs_tmf_task *tmf)
+int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags)
{
- struct domain_device *dev = task->dev;
struct mvs_info *mvi = NULL;
u32 rc = 0;
u32 pass = 0;
unsigned long flags = 0;
+ struct sas_tmf_task *tmf = task->tmf;
+ int is_tmf = !!task->tmf;
mvi = ((struct mvs_device *)task->dev->lldd_dev)->mvi_info;
- if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
- spin_unlock_irq(dev->sata_dev.ap->lock);
-
spin_lock_irqsave(&mvi->lock, flags);
rc = mvs_task_prep(task, mvi, is_tmf, tmf, &pass);
if (rc)
@@ -1069,104 +845,33 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
(MVS_CHIP_SLOT_SZ - 1));
spin_unlock_irqrestore(&mvi->lock, flags);
- if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
- spin_lock_irq(dev->sata_dev.ap->lock);
-
- return rc;
-}
-
-static int mvs_collector_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
- struct completion *completion, int is_tmf,
- struct mvs_tmf_task *tmf)
-{
- struct domain_device *dev = task->dev;
- struct mvs_prv_info *mpi = dev->port->ha->lldd_ha;
- struct mvs_info *mvi = NULL;
- struct sas_task *t = task;
- struct mvs_task_list *mvs_list = NULL, *a;
- LIST_HEAD(q);
- int pass[2] = {0};
- u32 rc = 0;
- u32 n = num;
- unsigned long flags = 0;
-
- mvs_list = mvs_task_alloc_list(&n, gfp_flags);
- if (n) {
- printk(KERN_ERR "%s: mvs alloc list failed.\n", __func__);
- rc = -ENOMEM;
- goto free_list;
- }
-
- __list_add(&q, mvs_list->list.prev, &mvs_list->list);
-
- list_for_each_entry(a, &q, list) {
- a->task = t;
- t = list_entry(t->list.next, struct sas_task, list);
- }
-
- list_for_each_entry(a, &q , list) {
-
- t = a->task;
- mvi = ((struct mvs_device *)t->dev->lldd_dev)->mvi_info;
-
- spin_lock_irqsave(&mvi->lock, flags);
- rc = mvs_task_prep(t, mvi, is_tmf, tmf, &pass[mvi->id]);
- if (rc)
- dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
- spin_unlock_irqrestore(&mvi->lock, flags);
- }
-
- if (likely(pass[0]))
- MVS_CHIP_DISP->start_delivery(mpi->mvi[0],
- (mpi->mvi[0]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
-
- if (likely(pass[1]))
- MVS_CHIP_DISP->start_delivery(mpi->mvi[1],
- (mpi->mvi[1]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
-
- list_del_init(&q);
-
-free_list:
- if (mvs_list)
- mvs_task_free_list(mvs_list);
-
return rc;
}
-int mvs_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags)
-{
- struct mvs_device *mvi_dev = task->dev->lldd_dev;
- struct sas_ha_struct *sas = mvi_dev->mvi_info->sas;
-
- if (sas->lldd_max_execute_num < 2)
- return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
- else
- return mvs_collector_task_exec(task, num, gfp_flags, NULL, 0, NULL);
-}
-
static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
{
u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
- mvs_tag_clear(mvi, slot_idx);
+ mvs_tag_free(mvi, slot_idx);
}
static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
struct mvs_slot_info *slot, u32 slot_idx)
{
+ if (!slot)
+ return;
if (!slot->task)
return;
if (!sas_protocol_ata(task->task_proto))
if (slot->n_elem)
dma_unmap_sg(mvi->dev, task->scatter,
- slot->n_elem, task->data_dir);
+ task->num_scatter, task->data_dir);
switch (task->task_proto) {
case SAS_PROTOCOL_SMP:
dma_unmap_sg(mvi->dev, &task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
+ DMA_FROM_DEVICE);
dma_unmap_sg(mvi->dev, &task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
+ DMA_TO_DEVICE);
break;
case SAS_PROTOCOL_SATA:
@@ -1178,7 +883,7 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
}
if (slot->buf) {
- pci_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
+ dma_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
slot->buf = NULL;
}
list_del_init(&slot->entry);
@@ -1189,9 +894,9 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
mvs_slot_free(mvi, slot_idx);
}
-static void mvs_update_wideport(struct mvs_info *mvi, int i)
+static void mvs_update_wideport(struct mvs_info *mvi, int phy_no)
{
- struct mvs_phy *phy = &mvi->phy[i];
+ struct mvs_phy *phy = &mvi->phy[phy_no];
struct mvs_port *port = phy->port;
int j, no;
@@ -1246,18 +951,17 @@ static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf)
return NULL;
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
- s[3] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+ s[3] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
- s[2] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+ s[2] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
- s[1] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+ s[1] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
- s[0] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+ s[0] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
- /* Workaround: take some ATAPI devices for ATA */
if (((s[1] & 0x00FFFFFF) == 0x00EB1401) && (*(u8 *)&s[3] == 0x01))
s[1] = 0x00EB1401 | (*((u8 *)&s[1] + 3) & 0x10);
@@ -1269,6 +973,13 @@ static u32 mvs_is_sig_fis_received(u32 irq_status)
return irq_status & PHYEV_SIG_FIS;
}
+static void mvs_sig_remove_timer(struct mvs_phy *phy)
+{
+ if (phy->timer.function)
+ timer_delete(&phy->timer);
+ phy->timer.function = NULL;
+}
+
void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
{
struct mvs_phy *phy = &mvi->phy[i];
@@ -1291,6 +1002,7 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
if (phy->phy_type & PORT_TYPE_SATA) {
phy->identify.target_port_protocols = SAS_PROTOCOL_STP;
if (mvs_is_sig_fis_received(phy->irq_status)) {
+ mvs_sig_remove_timer(phy);
phy->phy_attached = 1;
phy->att_dev_sas_addr =
i + mvi->id * mvi->chip->n_phy;
@@ -1308,7 +1020,6 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
tmp | PHYEV_SIG_FIS);
phy->phy_attached = 0;
phy->phy_type &= ~PORT_TYPE_SATA;
- MVS_CHIP_DISP->phy_reset(mvi, i, 0);
goto out_done;
}
} else if (phy->phy_type & PORT_TYPE_SAS
@@ -1317,10 +1028,10 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
phy->identify.device_type =
phy->att_dev_info & PORT_DEV_TYPE_MASK;
- if (phy->identify.device_type == SAS_END_DEV)
+ if (phy->identify.device_type == SAS_END_DEVICE)
phy->identify.target_port_protocols =
SAS_PROTOCOL_SSP;
- else if (phy->identify.device_type != NO_DEVICE)
+ else if (phy->identify.device_type != SAS_PHY_UNUSED)
phy->identify.target_port_protocols =
SAS_PROTOCOL_SMP;
if (oob_done)
@@ -1334,9 +1045,9 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
if (MVS_CHIP_DISP->phy_work_around)
MVS_CHIP_DISP->phy_work_around(mvi, i);
}
- mv_dprintk("port %d attach dev info is %x\n",
+ mv_dprintk("phy %d attach dev info is %x\n",
i + mvi->id * mvi->chip->n_phy, phy->att_dev_info);
- mv_dprintk("port %d attach sas addr is %llx\n",
+ mv_dprintk("phy %d attach sas addr is %llx\n",
i + mvi->id * mvi->chip->n_phy, phy->att_dev_sas_addr);
out_done:
if (get_st)
@@ -1361,10 +1072,10 @@ static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
}
hi = i/((struct mvs_prv_info *)sas_ha->lldd_ha)->n_phy;
mvi = ((struct mvs_prv_info *)sas_ha->lldd_ha)->mvi[hi];
- if (sas_port->id >= mvi->chip->n_phy)
- port = &mvi->port[sas_port->id - mvi->chip->n_phy];
+ if (i >= mvi->chip->n_phy)
+ port = &mvi->port[i - mvi->chip->n_phy];
else
- port = &mvi->port[sas_port->id];
+ port = &mvi->port[i];
if (lock)
spin_lock_irqsave(&mvi->lock, flags);
port->port_attached = 1;
@@ -1374,6 +1085,12 @@ static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
port->wide_port_phymap = sas_port->phy_mask;
mv_printk("set wide port phy map %x\n", sas_port->phy_mask);
mvs_update_wideport(mvi, sas_phy->id);
+
+ /* direct attached SAS device */
+ if (phy->att_dev_info & PORT_SSP_TRGT_MASK) {
+ MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
+ MVS_CHIP_DISP->write_port_cfg_data(mvi, i, 0x04);
+ }
}
if (lock)
spin_unlock_irqrestore(&mvi->lock, flags);
@@ -1393,7 +1110,7 @@ static void mvs_port_notify_deformed(struct asd_sas_phy *sas_phy, int lock)
return;
}
list_for_each_entry(dev, &port->dev_list, dev_list_node)
- mvs_do_release_task(phy->mvi, phy_no, NULL);
+ mvs_do_release_task(phy->mvi, phy_no, dev);
}
@@ -1408,11 +1125,11 @@ void mvs_port_deformed(struct asd_sas_phy *sas_phy)
mvs_port_notify_deformed(sas_phy, 1);
}
-struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi)
+static struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi)
{
u32 dev;
for (dev = 0; dev < MVS_MAX_DEVICES; dev++) {
- if (mvi->devices[dev].dev_type == NO_DEVICE) {
+ if (mvi->devices[dev].dev_type == SAS_PHY_UNUSED) {
mvi->devices[dev].device_id = dev;
return &mvi->devices[dev];
}
@@ -1425,17 +1142,17 @@ struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi)
return NULL;
}
-void mvs_free_dev(struct mvs_device *mvi_dev)
+static void mvs_free_dev(struct mvs_device *mvi_dev)
{
u32 id = mvi_dev->device_id;
memset(mvi_dev, 0, sizeof(*mvi_dev));
mvi_dev->device_id = id;
- mvi_dev->dev_type = NO_DEVICE;
+ mvi_dev->dev_type = SAS_PHY_UNUSED;
mvi_dev->dev_status = MVS_DEV_NORMAL;
mvi_dev->taskfileset = MVS_ID_NOT_MAPPED;
}
-int mvs_dev_found_notify(struct domain_device *dev, int lock)
+static int mvs_dev_found_notify(struct domain_device *dev, int lock)
{
unsigned long flags = 0;
int res = 0;
@@ -1457,25 +1174,19 @@ int mvs_dev_found_notify(struct domain_device *dev, int lock)
mvi_device->dev_status = MVS_DEV_NORMAL;
mvi_device->dev_type = dev->dev_type;
mvi_device->mvi_info = mvi;
- if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+ mvi_device->sas_device = dev;
+ if (dev_parent_is_expander(dev)) {
int phy_id;
- u8 phy_num = parent_dev->ex_dev.num_phys;
- struct ex_phy *phy;
- for (phy_id = 0; phy_id < phy_num; phy_id++) {
- phy = &parent_dev->ex_dev.ex_phy[phy_id];
- if (SAS_ADDR(phy->attached_sas_addr) ==
- SAS_ADDR(dev->sas_addr)) {
- mvi_device->attached_phy = phy_id;
- break;
- }
- }
- if (phy_id == phy_num) {
+ phy_id = sas_find_attached_phy_id(&parent_dev->ex_dev, dev);
+ if (phy_id < 0) {
mv_printk("Error: no attached dev:%016llx"
"at ex:%016llx.\n",
SAS_ADDR(dev->sas_addr),
SAS_ADDR(parent_dev->sas_addr));
- res = -1;
+ res = phy_id;
+ } else {
+ mvi_device->attached_phy = phy_id;
}
}
@@ -1490,174 +1201,49 @@ int mvs_dev_found(struct domain_device *dev)
return mvs_dev_found_notify(dev, 1);
}
-void mvs_dev_gone_notify(struct domain_device *dev)
+static void mvs_dev_gone_notify(struct domain_device *dev)
{
unsigned long flags = 0;
struct mvs_device *mvi_dev = dev->lldd_dev;
- struct mvs_info *mvi = mvi_dev->mvi_info;
-
- spin_lock_irqsave(&mvi->lock, flags);
+ struct mvs_info *mvi;
- if (mvi_dev) {
- mv_dprintk("found dev[%d:%x] is gone.\n",
- mvi_dev->device_id, mvi_dev->dev_type);
- mvs_release_task(mvi, dev);
- mvs_free_reg_set(mvi, mvi_dev);
- mvs_free_dev(mvi_dev);
- } else {
+ if (!mvi_dev) {
mv_dprintk("found dev has gone.\n");
+ return;
}
- dev->lldd_dev = NULL;
-
- spin_unlock_irqrestore(&mvi->lock, flags);
-}
-
-void mvs_dev_gone(struct domain_device *dev)
-{
- mvs_dev_gone_notify(dev);
-}
-
-static struct sas_task *mvs_alloc_task(void)
-{
- struct sas_task *task = kzalloc(sizeof(struct sas_task), GFP_KERNEL);
-
- if (task) {
- INIT_LIST_HEAD(&task->list);
- spin_lock_init(&task->task_state_lock);
- task->task_state_flags = SAS_TASK_STATE_PENDING;
- init_timer(&task->timer);
- init_completion(&task->completion);
- }
- return task;
-}
+ mvi = mvi_dev->mvi_info;
-static void mvs_free_task(struct sas_task *task)
-{
- if (task) {
- BUG_ON(!list_empty(&task->list));
- kfree(task);
- }
-}
+ spin_lock_irqsave(&mvi->lock, flags);
-static void mvs_task_done(struct sas_task *task)
-{
- if (!del_timer(&task->timer))
- return;
- complete(&task->completion);
-}
+ mv_dprintk("found dev[%d:%x] is gone.\n",
+ mvi_dev->device_id, mvi_dev->dev_type);
+ mvs_release_task(mvi, dev);
+ mvs_free_reg_set(mvi, mvi_dev);
+ mvs_free_dev(mvi_dev);
-static void mvs_tmf_timedout(unsigned long data)
-{
- struct sas_task *task = (struct sas_task *)data;
+ dev->lldd_dev = NULL;
+ mvi_dev->sas_device = NULL;
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- complete(&task->completion);
+ spin_unlock_irqrestore(&mvi->lock, flags);
}
-/* XXX */
-#define MVS_TASK_TIMEOUT 20
-static int mvs_exec_internal_tmf_task(struct domain_device *dev,
- void *parameter, u32 para_len, struct mvs_tmf_task *tmf)
-{
- int res, retry;
- struct sas_task *task = NULL;
-
- for (retry = 0; retry < 3; retry++) {
- task = mvs_alloc_task();
- if (!task)
- return -ENOMEM;
-
- task->dev = dev;
- task->task_proto = dev->tproto;
-
- memcpy(&task->ssp_task, parameter, para_len);
- task->task_done = mvs_task_done;
-
- task->timer.data = (unsigned long) task;
- task->timer.function = mvs_tmf_timedout;
- task->timer.expires = jiffies + MVS_TASK_TIMEOUT*HZ;
- add_timer(&task->timer);
-
- res = mvs_task_exec(task, 1, GFP_KERNEL, NULL, 1, tmf);
-
- if (res) {
- del_timer(&task->timer);
- mv_printk("executing internel task failed:%d\n", res);
- goto ex_err;
- }
-
- wait_for_completion(&task->completion);
- res = -TMF_RESP_FUNC_FAILED;
- /* Even TMF timed out, return direct. */
- if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- mv_printk("TMF task[%x] timeout.\n", tmf->tmf);
- goto ex_err;
- }
- }
-
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAM_STAT_GOOD) {
- res = TMF_RESP_FUNC_COMPLETE;
- break;
- }
-
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_UNDERRUN) {
- /* no error, but return the number of bytes of
- * underrun */
- res = task->task_status.residual;
- break;
- }
-
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_OVERRUN) {
- mv_dprintk("blocked task error.\n");
- res = -EMSGSIZE;
- break;
- } else {
- mv_dprintk(" task to dev %016llx response: 0x%x "
- "status 0x%x\n",
- SAS_ADDR(dev->sas_addr),
- task->task_status.resp,
- task->task_status.stat);
- mvs_free_task(task);
- task = NULL;
-
- }
- }
-ex_err:
- BUG_ON(retry == 3 && task != NULL);
- if (task != NULL)
- mvs_free_task(task);
- return res;
-}
-static int mvs_debug_issue_ssp_tmf(struct domain_device *dev,
- u8 *lun, struct mvs_tmf_task *tmf)
+void mvs_dev_gone(struct domain_device *dev)
{
- struct sas_ssp_task ssp_task;
- DECLARE_COMPLETION_ONSTACK(completion);
- if (!(dev->tproto & SAS_PROTOCOL_SSP))
- return TMF_RESP_FUNC_ESUPP;
-
- strncpy((u8 *)&ssp_task.LUN, lun, 8);
-
- return mvs_exec_internal_tmf_task(dev, &ssp_task,
- sizeof(ssp_task), tmf);
+ mvs_dev_gone_notify(dev);
}
-
/* Standard mandates link reset for ATA (type 0)
and hard reset for SSP (type 1) , only for RECOVERY */
static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
{
int rc;
- struct sas_phy *phy = sas_find_local_phy(dev);
- int reset_type = (dev->dev_type == SATA_DEV ||
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ int reset_type = (dev->dev_type == SAS_SATA_DEV ||
(dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
rc = sas_phy_reset(phy, reset_type);
+ sas_put_local_phy(phy);
msleep(2000);
return rc;
}
@@ -1666,19 +1252,15 @@ static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
int mvs_lu_reset(struct domain_device *dev, u8 *lun)
{
unsigned long flags;
- int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED;
- struct mvs_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
struct mvs_device * mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
- tmf_task.tmf = TMF_LU_RESET;
mvi_dev->dev_status = MVS_DEV_EH;
- rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+ rc = sas_lu_reset(dev, lun);
if (rc == TMF_RESP_FUNC_COMPLETE) {
- num = mvs_find_dev_phyno(dev, phyno);
spin_lock_irqsave(&mvi->lock, flags);
- for (i = 0; i < num; i++)
- mvs_release_task(mvi, dev);
+ mvs_release_task(mvi, dev);
spin_unlock_irqrestore(&mvi->lock, flags);
}
/* If failed, fall-through I_T_Nexus reset */
@@ -1691,16 +1273,17 @@ int mvs_I_T_nexus_reset(struct domain_device *dev)
{
unsigned long flags;
int rc = TMF_RESP_FUNC_FAILED;
- struct mvs_device * mvi_dev = (struct mvs_device *)dev->lldd_dev;
+ struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
if (mvi_dev->dev_status != MVS_DEV_EH)
return TMF_RESP_FUNC_COMPLETE;
+ else
+ mvi_dev->dev_status = MVS_DEV_NORMAL;
rc = mvs_debug_I_T_nexus_reset(dev);
mv_printk("%s for device[%x]:rc= %d\n",
__func__, mvi_dev->device_id, rc);
- /* housekeeper */
spin_lock_irqsave(&mvi->lock, flags);
mvs_release_task(mvi, dev);
spin_unlock_irqrestore(&mvi->lock, flags);
@@ -1711,27 +1294,20 @@ int mvs_I_T_nexus_reset(struct domain_device *dev)
int mvs_query_task(struct sas_task *task)
{
u32 tag;
- struct scsi_lun lun;
- struct mvs_tmf_task tmf_task;
int rc = TMF_RESP_FUNC_FAILED;
if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
- struct scsi_cmnd * cmnd = (struct scsi_cmnd *)task->uldd_task;
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
- int_to_scsilun(cmnd->device->lun, &lun);
rc = mvs_find_tag(mvi, task, &tag);
if (rc == 0) {
rc = TMF_RESP_FUNC_FAILED;
return rc;
}
- tmf_task.tmf = TMF_QUERY_TASK;
- tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
-
- rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ rc = sas_query_task(task, tag);
switch (rc) {
/* The task is still in Lun, release it then */
case TMF_RESP_FUNC_SUCC:
@@ -1739,9 +1315,6 @@ int mvs_query_task(struct sas_task *task)
case TMF_RESP_FUNC_FAILED:
case TMF_RESP_FUNC_COMPLETE:
break;
- default:
- rc = TMF_RESP_FUNC_COMPLETE;
- break;
}
}
mv_printk("%s:rc= %d\n", __func__, rc);
@@ -1751,8 +1324,6 @@ int mvs_query_task(struct sas_task *task)
/* mandatory SAM-3, still need free task/slot info */
int mvs_abort_task(struct sas_task *task)
{
- struct scsi_lun lun;
- struct mvs_tmf_task tmf_task;
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
struct mvs_info *mvi;
@@ -1761,8 +1332,8 @@ int mvs_abort_task(struct sas_task *task)
u32 tag;
if (!mvi_dev) {
- mv_printk("%s:%d TMF_RESP_FUNC_FAILED\n", __func__, __LINE__);
- rc = TMF_RESP_FUNC_FAILED;
+ mv_printk("Device has removed\n");
+ return TMF_RESP_FUNC_FAILED;
}
mvi = mvi_dev->mvi_info;
@@ -1776,9 +1347,6 @@ int mvs_abort_task(struct sas_task *task)
spin_unlock_irqrestore(&task->task_state_lock, flags);
mvi_dev->dev_status = MVS_DEV_EH;
if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
- struct scsi_cmnd * cmnd = (struct scsi_cmnd *)task->uldd_task;
-
- int_to_scsilun(cmnd->device->lun, &lun);
rc = mvs_find_tag(mvi, task, &tag);
if (rc == 0) {
mv_printk("No such tag in %s\n", __func__);
@@ -1786,10 +1354,7 @@ int mvs_abort_task(struct sas_task *task)
return rc;
}
- tmf_task.tmf = TMF_ABORT_TASK;
- tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
-
- rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ rc = sas_abort_task(task, tag);
/* if successful, clear the task and callback forwards.*/
if (rc == TMF_RESP_FUNC_COMPLETE) {
@@ -1807,25 +1372,17 @@ int mvs_abort_task(struct sas_task *task)
} else if (task->task_proto & SAS_PROTOCOL_SATA ||
task->task_proto & SAS_PROTOCOL_STP) {
- /* to do free register_set */
- if (SATA_DEV == dev->dev_type) {
+ if (SAS_SATA_DEV == dev->dev_type) {
struct mvs_slot_info *slot = task->lldd_task;
- struct task_status_struct *tstat;
u32 slot_idx = (u32)(slot - mvi->slot_info);
- tstat = &task->task_status;
- mv_dprintk(KERN_DEBUG "mv_abort_task() mvi=%p task=%p "
+ mv_dprintk("mvs_abort_task() mvi=%p task=%p "
"slot=%p slot_idx=x%x\n",
mvi, task, slot, slot_idx);
- tstat->stat = SAS_ABORTED_TASK;
- if (mvi_dev && mvi_dev->running_req)
- mvi_dev->running_req--;
- if (sas_protocol_ata(task->task_proto))
- mvs_free_reg_set(mvi, mvi_dev);
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
mvs_slot_task_free(mvi, task, slot, slot_idx);
- return -1;
+ rc = TMF_RESP_FUNC_COMPLETE;
+ goto out;
}
- } else {
- /* SMP */
}
out:
@@ -1834,39 +1391,6 @@ out:
return rc;
}
-int mvs_abort_task_set(struct domain_device *dev, u8 *lun)
-{
- int rc = TMF_RESP_FUNC_FAILED;
- struct mvs_tmf_task tmf_task;
-
- tmf_task.tmf = TMF_ABORT_TASK_SET;
- rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
-
- return rc;
-}
-
-int mvs_clear_aca(struct domain_device *dev, u8 *lun)
-{
- int rc = TMF_RESP_FUNC_FAILED;
- struct mvs_tmf_task tmf_task;
-
- tmf_task.tmf = TMF_CLEAR_ACA;
- rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
-
- return rc;
-}
-
-int mvs_clear_task_set(struct domain_device *dev, u8 *lun)
-{
- int rc = TMF_RESP_FUNC_FAILED;
- struct mvs_tmf_task tmf_task;
-
- tmf_task.tmf = TMF_CLEAR_TASK_SET;
- rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
-
- return rc;
-}
-
static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
u32 slot_idx, int err)
{
@@ -1891,12 +1415,63 @@ static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
return stat;
}
+static void mvs_set_sense(u8 *buffer, int len, int d_sense,
+ int key, int asc, int ascq)
+{
+ memset(buffer, 0, len);
+
+ if (d_sense) {
+ /* Descriptor format */
+ if (len < 4) {
+ mv_printk("Length %d of sense buffer too small to "
+ "fit sense %x:%x:%x", len, key, asc, ascq);
+ }
+
+ buffer[0] = 0x72; /* Response Code */
+ if (len > 1)
+ buffer[1] = key; /* Sense Key */
+ if (len > 2)
+ buffer[2] = asc; /* ASC */
+ if (len > 3)
+ buffer[3] = ascq; /* ASCQ */
+ } else {
+ if (len < 14) {
+ mv_printk("Length %d of sense buffer too small to "
+ "fit sense %x:%x:%x", len, key, asc, ascq);
+ }
+
+ buffer[0] = 0x70; /* Response Code */
+ if (len > 2)
+ buffer[2] = key; /* Sense Key */
+ if (len > 7)
+ buffer[7] = 0x0a; /* Additional Sense Length */
+ if (len > 12)
+ buffer[12] = asc; /* ASC */
+ if (len > 13)
+ buffer[13] = ascq; /* ASCQ */
+ }
+
+ return;
+}
+
+static void mvs_fill_ssp_resp_iu(struct ssp_response_iu *iu,
+ u8 key, u8 asc, u8 asc_q)
+{
+ iu->datapres = SAS_DATAPRES_SENSE_DATA;
+ iu->response_data_len = 0;
+ iu->sense_data_len = 17;
+ iu->status = 02;
+ mvs_set_sense(iu->sense_data, 17, 0,
+ key, asc, asc_q);
+}
+
static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
u32 slot_idx)
{
struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
int stat;
- u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
+ u32 err_dw0 = le32_to_cpu(*(u32 *)slot->response);
+ u32 err_dw1 = le32_to_cpu(*((u32 *)slot->response + 1));
u32 tfs = 0;
enum mvs_port_type type = PORT_TYPE_SAS;
@@ -1908,8 +1483,19 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
stat = SAM_STAT_CHECK_CONDITION;
switch (task->task_proto) {
case SAS_PROTOCOL_SSP:
+ {
stat = SAS_ABORTED_TASK;
+ if ((err_dw0 & NO_DEST) || err_dw1 & bit(31)) {
+ struct ssp_response_iu *iu = slot->response +
+ sizeof(struct mvs_err_info);
+ mvs_fill_ssp_resp_iu(iu, NOT_READY, 0x04, 01);
+ sas_ssp_task_response(mvi->dev, task, iu);
+ stat = SAM_STAT_CHECK_CONDITION;
+ }
+ if (err_dw1 & bit(31))
+ mv_printk("reuse same slot, retry command.\n");
break;
+ }
case SAS_PROTOCOL_SMP:
stat = SAM_STAT_CHECK_CONDITION;
break;
@@ -1918,10 +1504,8 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
{
- if (err_dw0 == 0x80400002)
- mv_printk("find reserved error, why?\n");
-
task->ata_task.use_ncq = 0;
+ stat = SAS_PROTO_RESPONSE;
mvs_sata_done(mvi, task, slot_idx, err_dw0);
}
break;
@@ -1945,8 +1529,6 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
void *to;
enum exec_status sts;
- if (mvi->exp_req)
- mvi->exp_req--;
if (unlikely(!task || !task->lldd_task || !task->dev))
return -1;
@@ -1954,11 +1536,8 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
dev = task->dev;
mvi_dev = dev->lldd_dev;
- mvs_hba_cq_dump(mvi);
-
spin_lock(&task->task_state_lock);
- task->task_state_flags &=
- ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
task->task_state_flags |= SAS_TASK_STATE_DONE;
/* race condition*/
aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED;
@@ -1978,6 +1557,7 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
return -1;
}
+ /* when no device attaching, go ahead and complete by error handling*/
if (unlikely(!mvi_dev || flags)) {
if (!mvi_dev)
mv_dprintk("port has not device.\n");
@@ -1985,8 +1565,16 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
goto out;
}
- /* error info record present */
- if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
+ /*
+ * error info record present; slot->response is 32 bit aligned but may
+ * not be 64 bit aligned, so check for zero in two 32 bit reads
+ */
+ if (unlikely((rx_desc & RXQ_ERR)
+ && (*((u32 *)slot->response)
+ || *(((u32 *)slot->response) + 1)))) {
+ mv_dprintk("port %d slot %d rx_desc %X has error info"
+ "%016llX.\n", slot->port->sas_port.id, slot_idx,
+ rx_desc, get_unaligned_le64(slot->response));
tstat->stat = mvs_slot_err(mvi, task, slot_idx);
tstat->resp = SAS_TASK_COMPLETE;
goto out;
@@ -1996,7 +1584,7 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
case SAS_PROTOCOL_SSP:
/* hw says status == 0, datapres == 0 */
if (rx_desc & RXQ_GOOD) {
- tstat->stat = SAM_STAT_GOOD;
+ tstat->stat = SAS_SAM_STAT_GOOD;
tstat->resp = SAS_TASK_COMPLETE;
}
/* response frame present */
@@ -2005,17 +1593,17 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
sizeof(struct mvs_err_info);
sas_ssp_task_response(mvi->dev, task, iu);
} else
- tstat->stat = SAM_STAT_CHECK_CONDITION;
+ tstat->stat = SAS_SAM_STAT_CHECK_CONDITION;
break;
case SAS_PROTOCOL_SMP: {
struct scatterlist *sg_resp = &task->smp_task.smp_resp;
- tstat->stat = SAM_STAT_GOOD;
- to = kmap_atomic(sg_page(sg_resp), KM_IRQ0);
+ tstat->stat = SAS_SAM_STAT_GOOD;
+ to = kmap_atomic(sg_page(sg_resp));
memcpy(to + sg_resp->offset,
slot->response + sizeof(struct mvs_err_info),
sg_dma_len(sg_resp));
- kunmap_atomic(to, KM_IRQ0);
+ kunmap_atomic(to);
break;
}
@@ -2027,7 +1615,7 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
}
default:
- tstat->stat = SAM_STAT_CHECK_CONDITION;
+ tstat->stat = SAS_SAM_STAT_CHECK_CONDITION;
break;
}
if (!slot->port->port_attached) {
@@ -2048,8 +1636,7 @@ out:
spin_unlock(&mvi->lock);
if (task->task_done)
task->task_done(task);
- else
- mv_dprintk("why has not task_done.\n");
+
spin_lock(&mvi->lock);
return sts;
@@ -2092,7 +1679,6 @@ void mvs_release_task(struct mvs_info *mvi,
struct domain_device *dev)
{
int i, phyno[WIDE_PORT_MAX_PHY], num;
- /* housekeeper */
num = mvs_find_dev_phyno(dev, phyno);
for (i = 0; i < num; i++)
mvs_do_release_task(mvi, phyno[i], dev);
@@ -2111,34 +1697,37 @@ static void mvs_work_queue(struct work_struct *work)
struct mvs_wq *mwq = container_of(dw, struct mvs_wq, work_q);
struct mvs_info *mvi = mwq->mvi;
unsigned long flags;
+ u32 phy_no = (unsigned long) mwq->data;
+ struct mvs_phy *phy = &mvi->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
spin_lock_irqsave(&mvi->lock, flags);
if (mwq->handler & PHY_PLUG_EVENT) {
- u32 phy_no = (unsigned long) mwq->data;
- struct sas_ha_struct *sas_ha = mvi->sas;
- struct mvs_phy *phy = &mvi->phy[phy_no];
- struct asd_sas_phy *sas_phy = &phy->sas_phy;
if (phy->phy_event & PHY_PLUG_OUT) {
u32 tmp;
- struct sas_identify_frame *id;
- id = (struct sas_identify_frame *)phy->frame_rcvd;
+
tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no);
phy->phy_event &= ~PHY_PLUG_OUT;
if (!(tmp & PHY_READY_MASK)) {
sas_phy_disconnected(sas_phy);
mvs_phy_disconnected(phy);
- sas_ha->notify_phy_event(sas_phy,
- PHYE_LOSS_OF_SIGNAL);
+ sas_notify_phy_event(sas_phy,
+ PHYE_LOSS_OF_SIGNAL, GFP_ATOMIC);
mv_dprintk("phy%d Removed Device\n", phy_no);
} else {
MVS_CHIP_DISP->detect_porttype(mvi, phy_no);
mvs_update_phyinfo(mvi, phy_no, 1);
- mvs_bytes_dmaed(mvi, phy_no);
+ mvs_bytes_dmaed(mvi, phy_no, GFP_ATOMIC);
mvs_port_notify_formed(sas_phy, 0);
mv_dprintk("phy%d Attached Device\n", phy_no);
}
}
+ } else if (mwq->handler & EXP_BRCT_CHG) {
+ phy->phy_event &= ~EXP_BRCT_CHG;
+ sas_notify_port_event(sas_phy,
+ PORTE_BROADCAST_RCVD, GFP_ATOMIC);
+ mv_dprintk("phy%d Got Broadcast Change\n", phy_no);
}
list_del(&mwq->entry);
spin_unlock_irqrestore(&mvi->lock, flags);
@@ -2164,9 +1753,9 @@ static int mvs_handle_event(struct mvs_info *mvi, void *data, int handler)
return ret;
}
-static void mvs_sig_time_out(unsigned long tphy)
+static void mvs_sig_time_out(struct timer_list *t)
{
- struct mvs_phy *phy = (struct mvs_phy *)tphy;
+ struct mvs_phy *phy = timer_container_of(phy, t, timer);
struct mvs_info *mvi = phy->mvi;
u8 phy_no;
@@ -2174,29 +1763,21 @@ static void mvs_sig_time_out(unsigned long tphy)
if (&mvi->phy[phy_no] == phy) {
mv_dprintk("Get signature time out, reset phy %d\n",
phy_no+mvi->id*mvi->chip->n_phy);
- MVS_CHIP_DISP->phy_reset(mvi, phy_no, 1);
+ MVS_CHIP_DISP->phy_reset(mvi, phy_no, MVS_HARD_RESET);
}
}
}
-static void mvs_sig_remove_timer(struct mvs_phy *phy)
-{
- if (phy->timer.function)
- del_timer(&phy->timer);
- phy->timer.function = NULL;
-}
-
void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
{
u32 tmp;
- struct sas_ha_struct *sas_ha = mvi->sas;
struct mvs_phy *phy = &mvi->phy[phy_no];
- struct asd_sas_phy *sas_phy = &phy->sas_phy;
phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no);
- mv_dprintk("port %d ctrl sts=0x%X.\n", phy_no+mvi->id*mvi->chip->n_phy,
+ MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
+ mv_dprintk("phy %d ctrl sts=0x%08X.\n", phy_no+mvi->id*mvi->chip->n_phy,
MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no));
- mv_dprintk("Port %d irq sts = 0x%X\n", phy_no+mvi->id*mvi->chip->n_phy,
+ mv_dprintk("phy %d irq sts = 0x%08X\n", phy_no+mvi->id*mvi->chip->n_phy,
phy->irq_status);
/*
@@ -2205,11 +1786,12 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
*/
if (phy->irq_status & PHYEV_DCDR_ERR) {
- mv_dprintk("port %d STP decoding error.\n",
+ mv_dprintk("phy %d STP decoding error.\n",
phy_no + mvi->id*mvi->chip->n_phy);
}
if (phy->irq_status & PHYEV_POOF) {
+ mdelay(500);
if (!(phy->phy_event & PHY_PLUG_OUT)) {
int dev_sata = phy->phy_type & PORT_TYPE_SATA;
int ready;
@@ -2220,17 +1802,13 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
(void *)(unsigned long)phy_no,
PHY_PLUG_EVENT);
ready = mvs_is_phy_ready(mvi, phy_no);
- if (!ready)
- mv_dprintk("phy%d Unplug Notice\n",
- phy_no +
- mvi->id * mvi->chip->n_phy);
if (ready || dev_sata) {
if (MVS_CHIP_DISP->stp_reset)
MVS_CHIP_DISP->stp_reset(mvi,
phy_no);
else
MVS_CHIP_DISP->phy_reset(mvi,
- phy_no, 0);
+ phy_no, MVS_SOFT_RESET);
return;
}
}
@@ -2241,15 +1819,13 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
MVS_CHIP_DISP->write_port_irq_mask(mvi, phy_no,
tmp | PHYEV_SIG_FIS);
if (phy->timer.function == NULL) {
- phy->timer.data = (unsigned long)phy;
phy->timer.function = mvs_sig_time_out;
- phy->timer.expires = jiffies + 10*HZ;
+ phy->timer.expires = jiffies + 5*HZ;
add_timer(&phy->timer);
}
}
if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) {
phy->phy_status = mvs_is_phy_ready(mvi, phy_no);
- mvs_sig_remove_timer(phy);
mv_dprintk("notify plug in on phy[%d]\n", phy_no);
if (phy->phy_status) {
mdelay(10);
@@ -2263,14 +1839,14 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
}
mvs_update_phyinfo(mvi, phy_no, 0);
if (phy->phy_type & PORT_TYPE_SAS) {
- MVS_CHIP_DISP->phy_reset(mvi, phy_no, 2);
+ MVS_CHIP_DISP->phy_reset(mvi, phy_no, MVS_PHY_TUNE);
mdelay(10);
}
- mvs_bytes_dmaed(mvi, phy_no);
+ mvs_bytes_dmaed(mvi, phy_no, GFP_ATOMIC);
/* whether driver is going to handle hot plug */
if (phy->phy_event & PHY_PLUG_OUT) {
- mvs_port_notify_formed(sas_phy, 0);
+ mvs_port_notify_formed(&phy->sas_phy, 0);
phy->phy_event &= ~PHY_PLUG_OUT;
}
} else {
@@ -2278,13 +1854,11 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
phy_no + mvi->id*mvi->chip->n_phy);
}
} else if (phy->irq_status & PHYEV_BROAD_CH) {
- mv_dprintk("port %d broadcast change.\n",
+ mv_dprintk("phy %d broadcast change.\n",
phy_no + mvi->id*mvi->chip->n_phy);
- /* exception for Samsung disk drive*/
- mdelay(1000);
- sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ mvs_handle_event(mvi, (void *)(unsigned long)phy_no,
+ EXP_BRCT_CHG);
}
- MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
}
int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
@@ -2334,3 +1908,16 @@ int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
return 0;
}
+int mvs_gpio_write(struct sas_ha_struct *sha, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data)
+{
+ struct mvs_prv_info *mvs_prv = sha->lldd_ha;
+ struct mvs_info *mvi = mvs_prv->mvi[0];
+
+ if (MVS_CHIP_DISP->gpio_write) {
+ return MVS_CHIP_DISP->gpio_write(mvs_prv, reg_type,
+ reg_index, reg_count, write_data);
+ }
+
+ return -ENOSYS;
+}
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index 1367d8b9350d..09ce3f2241f2 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -1,26 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Marvell 88SE64xx/88SE94xx main function head file
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
- *
- * This file is licensed under GPLv2.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
#ifndef _MV_SAS_H_
@@ -39,47 +23,41 @@
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/unaligned.h>
#include <scsi/libsas.h>
#include <scsi/scsi.h>
#include <scsi/scsi_tcq.h>
#include <scsi/sas_ata.h>
-#include <linux/version.h>
#include "mv_defs.h"
#define DRV_NAME "mvsas"
-#define DRV_VERSION "0.8.2"
-#define _MV_DUMP 0
+#define DRV_VERSION "0.8.16"
#define MVS_ID_NOT_MAPPED 0x7f
-/* #define DISABLE_HOTPLUG_DMA_FIX */
-// #define MAX_EXP_RUNNING_REQ 2
#define WIDE_PORT_MAX_PHY 4
-#define MV_DISABLE_NCQ 0
#define mv_printk(fmt, arg ...) \
printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg)
#ifdef MV_DEBUG
#define mv_dprintk(format, arg...) \
printk(KERN_DEBUG"%s %d:" format, __FILE__, __LINE__, ## arg)
#else
-#define mv_dprintk(format, arg...)
+#define mv_dprintk(format, arg...) no_printk(format, ## arg)
#endif
#define MV_MAX_U32 0xffffffff
+extern int interrupt_coalescing;
extern struct mvs_tgt_initiator mvs_tgt;
extern struct mvs_info *tgt_mvi;
extern const struct mvs_dispatch mvs_64xx_dispatch;
extern const struct mvs_dispatch mvs_94xx_dispatch;
-extern struct kmem_cache *mvs_task_list_cache;
-#define DEV_IS_EXPANDER(type) \
- ((type == EDGE_DEV) || (type == FANOUT_DEV))
-
-#define bit(n) ((u32)1 << n)
+#define bit(n) ((u64)1 << n)
#define for_each_phy(__lseq_mask, __mc, __lseq) \
for ((__mc) = (__lseq_mask), (__lseq) = 0; \
(__mc) != 0 ; \
(++__lseq), (__mc) >>= 1)
+#define MVS_PHY_ID (1U << sas_phy->id)
#define MV_INIT_DELAYED_WORK(w, f, d) INIT_DELAYED_WORK(w, f)
#define UNASSOC_D2H_FIS(id) \
((void *) mvi->rx_fis + 0x100 * id)
@@ -99,8 +77,14 @@ enum dev_status {
MVS_DEV_EH = 0x1,
};
+enum dev_reset {
+ MVS_SOFT_RESET = 0,
+ MVS_HARD_RESET = 1,
+ MVS_PHY_TUNE = 2,
+};
struct mvs_info;
+struct mvs_prv_info;
struct mvs_dispatch {
char *name;
@@ -130,7 +114,6 @@ struct mvs_dispatch {
u32 (*read_port_irq_mask)(struct mvs_info *mvi, u32 port);
void (*write_port_irq_mask)(struct mvs_info *mvi, u32 port, u32 val);
- void (*get_sas_addr)(void *buf, u32 buflen);
void (*command_active)(struct mvs_info *mvi, u32 slot_idx);
void (*clear_srs_irq)(struct mvs_info *mvi, u8 reg_set, u8 clear_all);
void (*issue_stop)(struct mvs_info *mvi, enum mvs_port_type type,
@@ -167,9 +150,12 @@ struct mvs_dispatch {
);
int (*spi_issuecmd)(struct mvs_info *mvi, u32 cmd);
int (*spi_waitdataready)(struct mvs_info *mvi, u32 timeout);
-#ifndef DISABLE_HOTPLUG_DMA_FIX
- void (*dma_fix)(dma_addr_t buf_dma, int buf_len, int from, void *prd);
-#endif
+ void (*dma_fix)(struct mvs_info *mvi, u32 phy_mask,
+ int buf_len, int from, void *prd);
+ void (*tune_interrupt)(struct mvs_info *mvi, u32 time);
+ void (*non_spec_ncq_error)(struct mvs_info *mvi);
+ int (*gpio_write)(struct mvs_prv_info *mvs_prv, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data);
};
@@ -179,9 +165,11 @@ struct mvs_chip_info {
u32 fis_offs;
u32 fis_count;
u32 srs_sz;
+ u32 sg_width;
u32 slot_width;
const struct mvs_dispatch *dispatch;
};
+#define MVS_MAX_SG (1U << mvi->chip->sg_width)
#define MVS_CHIP_SLOT_SZ (1U << mvi->chip->slot_width)
#define MVS_RX_FISL_SZ \
(mvi->chip->fis_offs + (mvi->chip->fis_count * 0x100))
@@ -237,10 +225,9 @@ struct mvs_phy {
struct mvs_device {
struct list_head dev_entry;
- enum sas_dev_type dev_type;
+ enum sas_device_type dev_type;
struct mvs_info *mvi_info;
struct domain_device *sas_device;
- struct timer_list timer;
u32 attached_phy;
u32 device_id;
u32 running_req;
@@ -249,6 +236,73 @@ struct mvs_device {
u16 reserved;
};
+/* Generate PHY tunning parameters */
+struct phy_tuning {
+ /* 1 bit, transmitter emphasis enable */
+ u8 trans_emp_en:1;
+ /* 4 bits, transmitter emphasis amplitude */
+ u8 trans_emp_amp:4;
+ /* 3 bits, reserved space */
+ u8 Reserved_2bit_1:3;
+ /* 5 bits, transmitter amplitude */
+ u8 trans_amp:5;
+ /* 2 bits, transmitter amplitude adjust */
+ u8 trans_amp_adj:2;
+ /* 1 bit, reserved space */
+ u8 resv_2bit_2:1;
+ /* 2 bytes, reserved space */
+ u8 reserved[2];
+};
+
+struct ffe_control {
+ /* 4 bits, FFE Capacitor Select (value range 0~F) */
+ u8 ffe_cap_sel:4;
+ /* 3 bits, FFE Resistor Select (value range 0~7) */
+ u8 ffe_rss_sel:3;
+ /* 1 bit reserve*/
+ u8 reserved:1;
+};
+
+/*
+ * HBA_Info_Page is saved in Flash/NVRAM, total 256 bytes.
+ * The data area is valid only Signature="MRVL".
+ * If any member fills with 0xFF, the member is invalid.
+ */
+struct hba_info_page {
+ /* Dword 0 */
+ /* 4 bytes, structure signature,should be "MRVL" at first initial */
+ u8 signature[4];
+
+ /* Dword 1-13 */
+ u32 reserved1[13];
+
+ /* Dword 14-29 */
+ /* 64 bytes, SAS address for each port */
+ u64 sas_addr[8];
+
+ /* Dword 30-31 */
+ /* 8 bytes for vanir 8 port PHY FFE seeting
+ * BIT 0~3 : FFE Capacitor select(value range 0~F)
+ * BIT 4~6 : FFE Resistor select(value range 0~7)
+ * BIT 7: reserve.
+ */
+
+ struct ffe_control ffe_ctl[8];
+ /* Dword 32 -43 */
+ u32 reserved2[12];
+
+ /* Dword 44-45 */
+ /* 8 bytes, 0: 1.5G, 1: 3.0G, should be 0x01 at first initial */
+ u8 phy_rate[8];
+
+ /* Dword 46-53 */
+ /* 32 bytes, PHY tuning parameters for each PHY*/
+ struct phy_tuning phy_tuning[8];
+
+ /* Dword 54-63 */
+ u32 reserved3[10];
+}; /* total 256 bytes */
+
struct mvs_slot_info {
struct list_head entry;
union {
@@ -264,9 +318,6 @@ struct mvs_slot_info {
*/
void *buf;
dma_addr_t buf_dma;
-#if _MV_DUMP
- u32 cmd_size;
-#endif
void *response;
struct mvs_port *port;
struct mvs_device *device;
@@ -319,13 +370,10 @@ struct mvs_info {
u32 chip_id;
const struct mvs_chip_info *chip;
- int tags_num;
- DECLARE_BITMAP(tags, MVS_SLOTS);
+ unsigned long *rsvd_tags;
/* further per-slot information */
struct mvs_phy phy[MVS_MAX_PHYS];
struct mvs_port port[MVS_MAX_PHYS];
- u32 irq;
- u32 exp_req;
u32 id;
u64 sata_reg_set;
struct list_head *hba_list;
@@ -337,21 +385,24 @@ struct mvs_info {
u32 flashsectSize;
void *addon;
+ struct hba_info_page hba_info_param;
struct mvs_device devices[MVS_MAX_DEVICES];
-#ifndef DISABLE_HOTPLUG_DMA_FIX
void *bulk_buffer;
dma_addr_t bulk_buffer_dma;
+ void *bulk_buffer1;
+ dma_addr_t bulk_buffer_dma1;
#define TRASH_BUCKET_SIZE 0x20000
-#endif
void *dma_pool;
- struct mvs_slot_info slot_info[0];
+ struct mvs_slot_info slot_info[];
};
struct mvs_prv_info{
u8 n_host;
u8 n_phy;
- u16 reserve;
+ u8 scan_finished;
+ u8 reserve;
struct mvs_info *mvi[2];
+ struct tasklet_struct mv_tasklet;
};
struct mvs_wq {
@@ -370,36 +421,18 @@ struct mvs_task_exec_info {
int n_elem;
};
-struct mvs_task_list {
- struct sas_task *task;
- struct list_head list;
-};
-
-
/******************** function prototype *********************/
void mvs_get_sas_addr(void *buf, u32 buflen);
-void mvs_tag_clear(struct mvs_info *mvi, u32 tag);
-void mvs_tag_free(struct mvs_info *mvi, u32 tag);
-void mvs_tag_set(struct mvs_info *mvi, unsigned int tag);
-int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out);
-void mvs_tag_init(struct mvs_info *mvi);
void mvs_iounmap(void __iomem *regs);
int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex);
-void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard);
int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
void *funcdata);
-void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
- u32 off_lo, u32 off_hi, u64 sas_addr);
-int mvs_slave_alloc(struct scsi_device *scsi_dev);
-int mvs_slave_configure(struct scsi_device *sdev);
+void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo,
+ u32 off_hi, u64 sas_addr);
void mvs_scan_start(struct Scsi_Host *shost);
int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time);
-int mvs_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags);
+int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags);
int mvs_abort_task(struct sas_task *task);
-int mvs_abort_task_set(struct domain_device *dev, u8 *lun);
-int mvs_clear_aca(struct domain_device *dev, u8 *lun);
-int mvs_clear_task_set(struct domain_device *dev, u8 * lun);
void mvs_port_formed(struct asd_sas_phy *sas_phy);
void mvs_port_deformed(struct asd_sas_phy *sas_phy);
int mvs_dev_found(struct domain_device *dev);
@@ -415,6 +448,8 @@ void mvs_do_release_task(struct mvs_info *mvi, int phy_no,
void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
-void mvs_hexdump(u32 size, u8 *data, u32 baseaddr);
+struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi, u8 reg_set);
+int mvs_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data);
#endif
diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c
new file mode 100644
index 000000000000..bdc2f2f17753
--- /dev/null
+++ b/drivers/scsi/mvumi.c
@@ -0,0 +1,2633 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Marvell UMI driver
+ *
+ * Copyright 2011 Marvell. <jyli@marvell.com>
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#include <linux/blkdev.h>
+#include <linux/io.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_eh.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+
+#include "mvumi.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("jyli@marvell.com");
+MODULE_DESCRIPTION("Marvell UMI Driver");
+
+static const struct pci_device_id mvumi_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, PCI_DEVICE_ID_MARVELL_MV9143) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, PCI_DEVICE_ID_MARVELL_MV9580) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, mvumi_pci_table);
+
+static void tag_init(struct mvumi_tag *st, unsigned short size)
+{
+ unsigned short i;
+ BUG_ON(size != st->size);
+ st->top = size;
+ for (i = 0; i < size; i++)
+ st->stack[i] = size - 1 - i;
+}
+
+static unsigned short tag_get_one(struct mvumi_hba *mhba, struct mvumi_tag *st)
+{
+ BUG_ON(st->top <= 0);
+ return st->stack[--st->top];
+}
+
+static void tag_release_one(struct mvumi_hba *mhba, struct mvumi_tag *st,
+ unsigned short tag)
+{
+ BUG_ON(st->top >= st->size);
+ st->stack[st->top++] = tag;
+}
+
+static bool tag_is_empty(struct mvumi_tag *st)
+{
+ if (st->top == 0)
+ return true;
+ else
+ return false;
+}
+
+static void mvumi_unmap_pci_addr(struct pci_dev *dev, void **addr_array)
+{
+ int i;
+
+ for (i = 0; i < MAX_BASE_ADDRESS; i++)
+ if ((pci_resource_flags(dev, i) & IORESOURCE_MEM) &&
+ addr_array[i])
+ pci_iounmap(dev, addr_array[i]);
+}
+
+static int mvumi_map_pci_addr(struct pci_dev *dev, void **addr_array)
+{
+ int i;
+
+ for (i = 0; i < MAX_BASE_ADDRESS; i++) {
+ if (pci_resource_flags(dev, i) & IORESOURCE_MEM) {
+ addr_array[i] = pci_iomap(dev, i, 0);
+ if (!addr_array[i]) {
+ dev_err(&dev->dev, "failed to map Bar[%d]\n",
+ i);
+ mvumi_unmap_pci_addr(dev, addr_array);
+ return -ENOMEM;
+ }
+ } else
+ addr_array[i] = NULL;
+
+ dev_dbg(&dev->dev, "Bar %d : %p.\n", i, addr_array[i]);
+ }
+
+ return 0;
+}
+
+static struct mvumi_res *mvumi_alloc_mem_resource(struct mvumi_hba *mhba,
+ enum resource_type type, unsigned int size)
+{
+ struct mvumi_res *res = kzalloc(sizeof(*res), GFP_ATOMIC);
+
+ if (!res) {
+ dev_err(&mhba->pdev->dev,
+ "Failed to allocate memory for resource manager.\n");
+ return NULL;
+ }
+
+ switch (type) {
+ case RESOURCE_CACHED_MEMORY:
+ res->virt_addr = kzalloc(size, GFP_ATOMIC);
+ if (!res->virt_addr) {
+ dev_err(&mhba->pdev->dev,
+ "unable to allocate memory,size = %d.\n", size);
+ kfree(res);
+ return NULL;
+ }
+ break;
+
+ case RESOURCE_UNCACHED_MEMORY:
+ size = round_up(size, 8);
+ res->virt_addr = dma_alloc_coherent(&mhba->pdev->dev, size,
+ &res->bus_addr,
+ GFP_KERNEL);
+ if (!res->virt_addr) {
+ dev_err(&mhba->pdev->dev,
+ "unable to allocate consistent mem,"
+ "size = %d.\n", size);
+ kfree(res);
+ return NULL;
+ }
+ break;
+
+ default:
+ dev_err(&mhba->pdev->dev, "unknown resource type %d.\n", type);
+ kfree(res);
+ return NULL;
+ }
+
+ res->type = type;
+ res->size = size;
+ INIT_LIST_HEAD(&res->entry);
+ list_add_tail(&res->entry, &mhba->res_list);
+
+ return res;
+}
+
+static void mvumi_release_mem_resource(struct mvumi_hba *mhba)
+{
+ struct mvumi_res *res, *tmp;
+
+ list_for_each_entry_safe(res, tmp, &mhba->res_list, entry) {
+ switch (res->type) {
+ case RESOURCE_UNCACHED_MEMORY:
+ dma_free_coherent(&mhba->pdev->dev, res->size,
+ res->virt_addr, res->bus_addr);
+ break;
+ case RESOURCE_CACHED_MEMORY:
+ kfree(res->virt_addr);
+ break;
+ default:
+ dev_err(&mhba->pdev->dev,
+ "unknown resource type %d\n", res->type);
+ break;
+ }
+ list_del(&res->entry);
+ kfree(res);
+ }
+ mhba->fw_flag &= ~MVUMI_FW_ALLOC;
+}
+
+/**
+ * mvumi_make_sgl - Prepares SGL
+ * @mhba: Adapter soft state
+ * @scmd: SCSI command from the mid-layer
+ * @sgl_p: SGL to be filled in
+ * @sg_count: return the number of SG elements
+ *
+ * If successful, this function returns 0. otherwise, it returns -1.
+ */
+static int mvumi_make_sgl(struct mvumi_hba *mhba, struct scsi_cmnd *scmd,
+ void *sgl_p, unsigned char *sg_count)
+{
+ struct scatterlist *sg;
+ struct mvumi_sgl *m_sg = (struct mvumi_sgl *) sgl_p;
+ unsigned int i;
+ unsigned int sgnum = scsi_sg_count(scmd);
+ dma_addr_t busaddr;
+
+ *sg_count = dma_map_sg(&mhba->pdev->dev, scsi_sglist(scmd), sgnum,
+ scmd->sc_data_direction);
+ if (*sg_count > mhba->max_sge) {
+ dev_err(&mhba->pdev->dev,
+ "sg count[0x%x] is bigger than max sg[0x%x].\n",
+ *sg_count, mhba->max_sge);
+ dma_unmap_sg(&mhba->pdev->dev, scsi_sglist(scmd), sgnum,
+ scmd->sc_data_direction);
+ return -1;
+ }
+ scsi_for_each_sg(scmd, sg, *sg_count, i) {
+ busaddr = sg_dma_address(sg);
+ m_sg->baseaddr_l = cpu_to_le32(lower_32_bits(busaddr));
+ m_sg->baseaddr_h = cpu_to_le32(upper_32_bits(busaddr));
+ m_sg->flags = 0;
+ sgd_setsz(mhba, m_sg, cpu_to_le32(sg_dma_len(sg)));
+ if ((i + 1) == *sg_count)
+ m_sg->flags |= 1U << mhba->eot_flag;
+
+ sgd_inc(mhba, m_sg);
+ }
+
+ return 0;
+}
+
+static int mvumi_internal_cmd_sgl(struct mvumi_hba *mhba, struct mvumi_cmd *cmd,
+ unsigned int size)
+{
+ struct mvumi_sgl *m_sg;
+ void *virt_addr;
+ dma_addr_t phy_addr;
+
+ if (size == 0)
+ return 0;
+
+ virt_addr = dma_alloc_coherent(&mhba->pdev->dev, size, &phy_addr,
+ GFP_KERNEL);
+ if (!virt_addr)
+ return -1;
+
+ m_sg = (struct mvumi_sgl *) &cmd->frame->payload[0];
+ cmd->frame->sg_counts = 1;
+ cmd->data_buf = virt_addr;
+
+ m_sg->baseaddr_l = cpu_to_le32(lower_32_bits(phy_addr));
+ m_sg->baseaddr_h = cpu_to_le32(upper_32_bits(phy_addr));
+ m_sg->flags = 1U << mhba->eot_flag;
+ sgd_setsz(mhba, m_sg, cpu_to_le32(size));
+
+ return 0;
+}
+
+static struct mvumi_cmd *mvumi_create_internal_cmd(struct mvumi_hba *mhba,
+ unsigned int buf_size)
+{
+ struct mvumi_cmd *cmd;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ dev_err(&mhba->pdev->dev, "failed to create a internal cmd\n");
+ return NULL;
+ }
+ INIT_LIST_HEAD(&cmd->queue_pointer);
+
+ cmd->frame = dma_alloc_coherent(&mhba->pdev->dev, mhba->ib_max_size,
+ &cmd->frame_phys, GFP_KERNEL);
+ if (!cmd->frame) {
+ dev_err(&mhba->pdev->dev, "failed to allocate memory for FW"
+ " frame,size = %d.\n", mhba->ib_max_size);
+ kfree(cmd);
+ return NULL;
+ }
+
+ if (buf_size) {
+ if (mvumi_internal_cmd_sgl(mhba, cmd, buf_size)) {
+ dev_err(&mhba->pdev->dev, "failed to allocate memory"
+ " for internal frame\n");
+ dma_free_coherent(&mhba->pdev->dev, mhba->ib_max_size,
+ cmd->frame, cmd->frame_phys);
+ kfree(cmd);
+ return NULL;
+ }
+ } else
+ cmd->frame->sg_counts = 0;
+
+ return cmd;
+}
+
+static void mvumi_delete_internal_cmd(struct mvumi_hba *mhba,
+ struct mvumi_cmd *cmd)
+{
+ struct mvumi_sgl *m_sg;
+ unsigned int size;
+ dma_addr_t phy_addr;
+
+ if (cmd && cmd->frame) {
+ if (cmd->frame->sg_counts) {
+ m_sg = (struct mvumi_sgl *) &cmd->frame->payload[0];
+ sgd_getsz(mhba, m_sg, size);
+
+ phy_addr = (dma_addr_t) m_sg->baseaddr_l |
+ (dma_addr_t) ((m_sg->baseaddr_h << 16) << 16);
+
+ dma_free_coherent(&mhba->pdev->dev, size, cmd->data_buf,
+ phy_addr);
+ }
+ dma_free_coherent(&mhba->pdev->dev, mhba->ib_max_size,
+ cmd->frame, cmd->frame_phys);
+ kfree(cmd);
+ }
+}
+
+/**
+ * mvumi_get_cmd - Get a command from the free pool
+ * @mhba: Adapter soft state
+ *
+ * Returns a free command from the pool
+ */
+static struct mvumi_cmd *mvumi_get_cmd(struct mvumi_hba *mhba)
+{
+ struct mvumi_cmd *cmd = NULL;
+
+ if (likely(!list_empty(&mhba->cmd_pool))) {
+ cmd = list_entry((&mhba->cmd_pool)->next,
+ struct mvumi_cmd, queue_pointer);
+ list_del_init(&cmd->queue_pointer);
+ } else
+ dev_warn(&mhba->pdev->dev, "command pool is empty!\n");
+
+ return cmd;
+}
+
+/**
+ * mvumi_return_cmd - Return a cmd to free command pool
+ * @mhba: Adapter soft state
+ * @cmd: Command packet to be returned to free command pool
+ */
+static inline void mvumi_return_cmd(struct mvumi_hba *mhba,
+ struct mvumi_cmd *cmd)
+{
+ cmd->scmd = NULL;
+ list_add_tail(&cmd->queue_pointer, &mhba->cmd_pool);
+}
+
+/**
+ * mvumi_free_cmds - Free all the cmds in the free cmd pool
+ * @mhba: Adapter soft state
+ */
+static void mvumi_free_cmds(struct mvumi_hba *mhba)
+{
+ struct mvumi_cmd *cmd;
+
+ while (!list_empty(&mhba->cmd_pool)) {
+ cmd = list_first_entry(&mhba->cmd_pool, struct mvumi_cmd,
+ queue_pointer);
+ list_del(&cmd->queue_pointer);
+ if (!(mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC))
+ kfree(cmd->frame);
+ kfree(cmd);
+ }
+}
+
+/**
+ * mvumi_alloc_cmds - Allocates the command packets
+ * @mhba: Adapter soft state
+ *
+ */
+static int mvumi_alloc_cmds(struct mvumi_hba *mhba)
+{
+ int i;
+ struct mvumi_cmd *cmd;
+
+ for (i = 0; i < mhba->max_io; i++) {
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ goto err_exit;
+
+ INIT_LIST_HEAD(&cmd->queue_pointer);
+ list_add_tail(&cmd->queue_pointer, &mhba->cmd_pool);
+ if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
+ cmd->frame = mhba->ib_frame + i * mhba->ib_max_size;
+ cmd->frame_phys = mhba->ib_frame_phys
+ + i * mhba->ib_max_size;
+ } else
+ cmd->frame = kzalloc(mhba->ib_max_size, GFP_KERNEL);
+ if (!cmd->frame)
+ goto err_exit;
+ }
+ return 0;
+
+err_exit:
+ dev_err(&mhba->pdev->dev,
+ "failed to allocate memory for cmd[0x%x].\n", i);
+ while (!list_empty(&mhba->cmd_pool)) {
+ cmd = list_first_entry(&mhba->cmd_pool, struct mvumi_cmd,
+ queue_pointer);
+ list_del(&cmd->queue_pointer);
+ if (!(mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC))
+ kfree(cmd->frame);
+ kfree(cmd);
+ }
+ return -ENOMEM;
+}
+
+static unsigned int mvumi_check_ib_list_9143(struct mvumi_hba *mhba)
+{
+ unsigned int ib_rp_reg;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ ib_rp_reg = ioread32(mhba->regs->inb_read_pointer);
+
+ if (unlikely(((ib_rp_reg & regs->cl_slot_num_mask) ==
+ (mhba->ib_cur_slot & regs->cl_slot_num_mask)) &&
+ ((ib_rp_reg & regs->cl_pointer_toggle)
+ != (mhba->ib_cur_slot & regs->cl_pointer_toggle)))) {
+ dev_warn(&mhba->pdev->dev, "no free slot to use.\n");
+ return 0;
+ }
+ if (atomic_read(&mhba->fw_outstanding) >= mhba->max_io) {
+ dev_warn(&mhba->pdev->dev, "firmware io overflow.\n");
+ return 0;
+ } else {
+ return mhba->max_io - atomic_read(&mhba->fw_outstanding);
+ }
+}
+
+static unsigned int mvumi_check_ib_list_9580(struct mvumi_hba *mhba)
+{
+ unsigned int count;
+ if (atomic_read(&mhba->fw_outstanding) >= (mhba->max_io - 1))
+ return 0;
+ count = ioread32(mhba->ib_shadow);
+ if (count == 0xffff)
+ return 0;
+ return count;
+}
+
+static void mvumi_get_ib_list_entry(struct mvumi_hba *mhba, void **ib_entry)
+{
+ unsigned int cur_ib_entry;
+
+ cur_ib_entry = mhba->ib_cur_slot & mhba->regs->cl_slot_num_mask;
+ cur_ib_entry++;
+ if (cur_ib_entry >= mhba->list_num_io) {
+ cur_ib_entry -= mhba->list_num_io;
+ mhba->ib_cur_slot ^= mhba->regs->cl_pointer_toggle;
+ }
+ mhba->ib_cur_slot &= ~mhba->regs->cl_slot_num_mask;
+ mhba->ib_cur_slot |= (cur_ib_entry & mhba->regs->cl_slot_num_mask);
+ if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
+ *ib_entry = mhba->ib_list + cur_ib_entry *
+ sizeof(struct mvumi_dyn_list_entry);
+ } else {
+ *ib_entry = mhba->ib_list + cur_ib_entry * mhba->ib_max_size;
+ }
+ atomic_inc(&mhba->fw_outstanding);
+}
+
+static void mvumi_send_ib_list_entry(struct mvumi_hba *mhba)
+{
+ iowrite32(0xffff, mhba->ib_shadow);
+ iowrite32(mhba->ib_cur_slot, mhba->regs->inb_write_pointer);
+}
+
+static char mvumi_check_ob_frame(struct mvumi_hba *mhba,
+ unsigned int cur_obf, struct mvumi_rsp_frame *p_outb_frame)
+{
+ unsigned short tag, request_id;
+
+ udelay(1);
+ p_outb_frame = mhba->ob_list + cur_obf * mhba->ob_max_size;
+ request_id = p_outb_frame->request_id;
+ tag = p_outb_frame->tag;
+ if (tag > mhba->tag_pool.size) {
+ dev_err(&mhba->pdev->dev, "ob frame data error\n");
+ return -1;
+ }
+ if (mhba->tag_cmd[tag] == NULL) {
+ dev_err(&mhba->pdev->dev, "tag[0x%x] with NO command\n", tag);
+ return -1;
+ } else if (mhba->tag_cmd[tag]->request_id != request_id &&
+ mhba->request_id_enabled) {
+ dev_err(&mhba->pdev->dev, "request ID from FW:0x%x,"
+ "cmd request ID:0x%x\n", request_id,
+ mhba->tag_cmd[tag]->request_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mvumi_check_ob_list_9143(struct mvumi_hba *mhba,
+ unsigned int *cur_obf, unsigned int *assign_obf_end)
+{
+ unsigned int ob_write, ob_write_shadow;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ do {
+ ob_write = ioread32(regs->outb_copy_pointer);
+ ob_write_shadow = ioread32(mhba->ob_shadow);
+ } while ((ob_write & regs->cl_slot_num_mask) != ob_write_shadow);
+
+ *cur_obf = mhba->ob_cur_slot & mhba->regs->cl_slot_num_mask;
+ *assign_obf_end = ob_write & mhba->regs->cl_slot_num_mask;
+
+ if ((ob_write & regs->cl_pointer_toggle) !=
+ (mhba->ob_cur_slot & regs->cl_pointer_toggle)) {
+ *assign_obf_end += mhba->list_num_io;
+ }
+ return 0;
+}
+
+static int mvumi_check_ob_list_9580(struct mvumi_hba *mhba,
+ unsigned int *cur_obf, unsigned int *assign_obf_end)
+{
+ unsigned int ob_write;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ ob_write = ioread32(regs->outb_read_pointer);
+ ob_write = ioread32(regs->outb_copy_pointer);
+ *cur_obf = mhba->ob_cur_slot & mhba->regs->cl_slot_num_mask;
+ *assign_obf_end = ob_write & mhba->regs->cl_slot_num_mask;
+ if (*assign_obf_end < *cur_obf)
+ *assign_obf_end += mhba->list_num_io;
+ else if (*assign_obf_end == *cur_obf)
+ return -1;
+ return 0;
+}
+
+static void mvumi_receive_ob_list_entry(struct mvumi_hba *mhba)
+{
+ unsigned int cur_obf, assign_obf_end, i;
+ struct mvumi_ob_data *ob_data;
+ struct mvumi_rsp_frame *p_outb_frame;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ if (mhba->instancet->check_ob_list(mhba, &cur_obf, &assign_obf_end))
+ return;
+
+ for (i = (assign_obf_end - cur_obf); i != 0; i--) {
+ cur_obf++;
+ if (cur_obf >= mhba->list_num_io) {
+ cur_obf -= mhba->list_num_io;
+ mhba->ob_cur_slot ^= regs->cl_pointer_toggle;
+ }
+
+ p_outb_frame = mhba->ob_list + cur_obf * mhba->ob_max_size;
+
+ /* Copy pointer may point to entry in outbound list
+ * before entry has valid data
+ */
+ if (unlikely(p_outb_frame->tag > mhba->tag_pool.size ||
+ mhba->tag_cmd[p_outb_frame->tag] == NULL ||
+ p_outb_frame->request_id !=
+ mhba->tag_cmd[p_outb_frame->tag]->request_id))
+ if (mvumi_check_ob_frame(mhba, cur_obf, p_outb_frame))
+ continue;
+
+ if (!list_empty(&mhba->ob_data_list)) {
+ ob_data = (struct mvumi_ob_data *)
+ list_first_entry(&mhba->ob_data_list,
+ struct mvumi_ob_data, list);
+ list_del_init(&ob_data->list);
+ } else {
+ ob_data = NULL;
+ if (cur_obf == 0) {
+ cur_obf = mhba->list_num_io - 1;
+ mhba->ob_cur_slot ^= regs->cl_pointer_toggle;
+ } else
+ cur_obf -= 1;
+ break;
+ }
+
+ memcpy(ob_data->data, p_outb_frame, mhba->ob_max_size);
+ p_outb_frame->tag = 0xff;
+
+ list_add_tail(&ob_data->list, &mhba->free_ob_list);
+ }
+ mhba->ob_cur_slot &= ~regs->cl_slot_num_mask;
+ mhba->ob_cur_slot |= (cur_obf & regs->cl_slot_num_mask);
+ iowrite32(mhba->ob_cur_slot, regs->outb_read_pointer);
+}
+
+static void mvumi_reset(struct mvumi_hba *mhba)
+{
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ iowrite32(0, regs->enpointa_mask_reg);
+ if (ioread32(regs->arm_to_pciea_msg1) != HANDSHAKE_DONESTATE)
+ return;
+
+ iowrite32(DRBL_SOFT_RESET, regs->pciea_to_arm_drbl_reg);
+}
+
+static unsigned char mvumi_start(struct mvumi_hba *mhba);
+
+static int mvumi_wait_for_outstanding(struct mvumi_hba *mhba)
+{
+ mhba->fw_state = FW_STATE_ABORT;
+ mvumi_reset(mhba);
+
+ if (mvumi_start(mhba))
+ return FAILED;
+ else
+ return SUCCESS;
+}
+
+static int mvumi_wait_for_fw(struct mvumi_hba *mhba)
+{
+ struct mvumi_hw_regs *regs = mhba->regs;
+ u32 tmp;
+ unsigned long before;
+ before = jiffies;
+
+ iowrite32(0, regs->enpointa_mask_reg);
+ tmp = ioread32(regs->arm_to_pciea_msg1);
+ while (tmp != HANDSHAKE_READYSTATE) {
+ iowrite32(DRBL_MU_RESET, regs->pciea_to_arm_drbl_reg);
+ if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) {
+ dev_err(&mhba->pdev->dev,
+ "FW reset failed [0x%x].\n", tmp);
+ return FAILED;
+ }
+
+ msleep(500);
+ rmb();
+ tmp = ioread32(regs->arm_to_pciea_msg1);
+ }
+
+ return SUCCESS;
+}
+
+static void mvumi_backup_bar_addr(struct mvumi_hba *mhba)
+{
+ unsigned char i;
+
+ for (i = 0; i < MAX_BASE_ADDRESS; i++) {
+ pci_read_config_dword(mhba->pdev, 0x10 + i * 4,
+ &mhba->pci_base[i]);
+ }
+}
+
+static void mvumi_restore_bar_addr(struct mvumi_hba *mhba)
+{
+ unsigned char i;
+
+ for (i = 0; i < MAX_BASE_ADDRESS; i++) {
+ if (mhba->pci_base[i])
+ pci_write_config_dword(mhba->pdev, 0x10 + i * 4,
+ mhba->pci_base[i]);
+ }
+}
+
+static int mvumi_pci_set_master(struct pci_dev *pdev)
+{
+ int ret = 0;
+
+ pci_set_master(pdev);
+
+ if (IS_DMA64) {
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ } else
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+ return ret;
+}
+
+static int mvumi_reset_host_9580(struct mvumi_hba *mhba)
+{
+ mhba->fw_state = FW_STATE_ABORT;
+
+ iowrite32(0, mhba->regs->reset_enable);
+ iowrite32(0xf, mhba->regs->reset_request);
+
+ iowrite32(0x10, mhba->regs->reset_enable);
+ iowrite32(0x10, mhba->regs->reset_request);
+ msleep(100);
+ pci_disable_device(mhba->pdev);
+
+ if (pci_enable_device(mhba->pdev)) {
+ dev_err(&mhba->pdev->dev, "enable device failed\n");
+ return FAILED;
+ }
+ if (mvumi_pci_set_master(mhba->pdev)) {
+ dev_err(&mhba->pdev->dev, "set master failed\n");
+ return FAILED;
+ }
+ mvumi_restore_bar_addr(mhba);
+ if (mvumi_wait_for_fw(mhba) == FAILED)
+ return FAILED;
+
+ return mvumi_wait_for_outstanding(mhba);
+}
+
+static int mvumi_reset_host_9143(struct mvumi_hba *mhba)
+{
+ return mvumi_wait_for_outstanding(mhba);
+}
+
+static int mvumi_host_reset(struct scsi_cmnd *scmd)
+{
+ struct mvumi_hba *mhba;
+
+ mhba = (struct mvumi_hba *) scmd->device->host->hostdata;
+
+ scmd_printk(KERN_NOTICE, scmd, "RESET -%u cmd=%x retries=%x\n",
+ scsi_cmd_to_rq(scmd)->tag, scmd->cmnd[0], scmd->retries);
+
+ return mhba->instancet->reset_host(mhba);
+}
+
+static int mvumi_issue_blocked_cmd(struct mvumi_hba *mhba,
+ struct mvumi_cmd *cmd)
+{
+ unsigned long flags;
+
+ cmd->cmd_status = REQ_STATUS_PENDING;
+
+ if (atomic_read(&cmd->sync_cmd)) {
+ dev_err(&mhba->pdev->dev,
+ "last blocked cmd not finished, sync_cmd = %d\n",
+ atomic_read(&cmd->sync_cmd));
+ BUG_ON(1);
+ return -1;
+ }
+ atomic_inc(&cmd->sync_cmd);
+ spin_lock_irqsave(mhba->shost->host_lock, flags);
+ mhba->instancet->fire_cmd(mhba, cmd);
+ spin_unlock_irqrestore(mhba->shost->host_lock, flags);
+
+ wait_event_timeout(mhba->int_cmd_wait_q,
+ (cmd->cmd_status != REQ_STATUS_PENDING),
+ MVUMI_INTERNAL_CMD_WAIT_TIME * HZ);
+
+ /* command timeout */
+ if (atomic_read(&cmd->sync_cmd)) {
+ spin_lock_irqsave(mhba->shost->host_lock, flags);
+ atomic_dec(&cmd->sync_cmd);
+ if (mhba->tag_cmd[cmd->frame->tag]) {
+ mhba->tag_cmd[cmd->frame->tag] = NULL;
+ dev_warn(&mhba->pdev->dev, "TIMEOUT:release tag [%d]\n",
+ cmd->frame->tag);
+ tag_release_one(mhba, &mhba->tag_pool, cmd->frame->tag);
+ }
+ if (!list_empty(&cmd->queue_pointer)) {
+ dev_warn(&mhba->pdev->dev,
+ "TIMEOUT:A internal command doesn't send!\n");
+ list_del_init(&cmd->queue_pointer);
+ } else
+ atomic_dec(&mhba->fw_outstanding);
+
+ spin_unlock_irqrestore(mhba->shost->host_lock, flags);
+ }
+ return 0;
+}
+
+static void mvumi_release_fw(struct mvumi_hba *mhba)
+{
+ mvumi_free_cmds(mhba);
+ mvumi_release_mem_resource(mhba);
+ mvumi_unmap_pci_addr(mhba->pdev, mhba->base_addr);
+ dma_free_coherent(&mhba->pdev->dev, HSP_MAX_SIZE,
+ mhba->handshake_page, mhba->handshake_page_phys);
+ kfree(mhba->regs);
+ pci_release_regions(mhba->pdev);
+}
+
+static unsigned char mvumi_flush_cache(struct mvumi_hba *mhba)
+{
+ struct mvumi_cmd *cmd;
+ struct mvumi_msg_frame *frame;
+ unsigned char device_id, retry = 0;
+ unsigned char bitcount = sizeof(unsigned char) * 8;
+
+ for (device_id = 0; device_id < mhba->max_target_id; device_id++) {
+ if (!(mhba->target_map[device_id / bitcount] &
+ (1 << (device_id % bitcount))))
+ continue;
+get_cmd: cmd = mvumi_create_internal_cmd(mhba, 0);
+ if (!cmd) {
+ if (retry++ >= 5) {
+ dev_err(&mhba->pdev->dev, "failed to get memory"
+ " for internal flush cache cmd for "
+ "device %d", device_id);
+ retry = 0;
+ continue;
+ } else
+ goto get_cmd;
+ }
+ cmd->scmd = NULL;
+ cmd->cmd_status = REQ_STATUS_PENDING;
+ atomic_set(&cmd->sync_cmd, 0);
+ frame = cmd->frame;
+ frame->req_function = CL_FUN_SCSI_CMD;
+ frame->device_id = device_id;
+ frame->cmd_flag = CMD_FLAG_NON_DATA;
+ frame->data_transfer_length = 0;
+ frame->cdb_length = MAX_COMMAND_SIZE;
+ memset(frame->cdb, 0, MAX_COMMAND_SIZE);
+ frame->cdb[0] = SCSI_CMD_MARVELL_SPECIFIC;
+ frame->cdb[1] = CDB_CORE_MODULE;
+ frame->cdb[2] = CDB_CORE_SHUTDOWN;
+
+ mvumi_issue_blocked_cmd(mhba, cmd);
+ if (cmd->cmd_status != SAM_STAT_GOOD) {
+ dev_err(&mhba->pdev->dev,
+ "device %d flush cache failed, status=0x%x.\n",
+ device_id, cmd->cmd_status);
+ }
+
+ mvumi_delete_internal_cmd(mhba, cmd);
+ }
+ return 0;
+}
+
+static unsigned char
+mvumi_calculate_checksum(struct mvumi_hs_header *p_header,
+ unsigned short len)
+{
+ unsigned char *ptr;
+ unsigned char ret = 0, i;
+
+ ptr = (unsigned char *) p_header->frame_content;
+ for (i = 0; i < len; i++) {
+ ret ^= *ptr;
+ ptr++;
+ }
+
+ return ret;
+}
+
+static void mvumi_hs_build_page(struct mvumi_hba *mhba,
+ struct mvumi_hs_header *hs_header)
+{
+ struct mvumi_hs_page2 *hs_page2;
+ struct mvumi_hs_page4 *hs_page4;
+ struct mvumi_hs_page3 *hs_page3;
+ u64 time;
+ u64 local_time;
+
+ switch (hs_header->page_code) {
+ case HS_PAGE_HOST_INFO:
+ hs_page2 = (struct mvumi_hs_page2 *) hs_header;
+ hs_header->frame_length = sizeof(*hs_page2) - 4;
+ memset(hs_header->frame_content, 0, hs_header->frame_length);
+ hs_page2->host_type = 3; /* 3 mean linux*/
+ if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC)
+ hs_page2->host_cap = 0x08;/* host dynamic source mode */
+ hs_page2->host_ver.ver_major = VER_MAJOR;
+ hs_page2->host_ver.ver_minor = VER_MINOR;
+ hs_page2->host_ver.ver_oem = VER_OEM;
+ hs_page2->host_ver.ver_build = VER_BUILD;
+ hs_page2->system_io_bus = 0;
+ hs_page2->slot_number = 0;
+ hs_page2->intr_level = 0;
+ hs_page2->intr_vector = 0;
+ time = ktime_get_real_seconds();
+ local_time = (time - (sys_tz.tz_minuteswest * 60));
+ hs_page2->seconds_since1970 = local_time;
+ hs_header->checksum = mvumi_calculate_checksum(hs_header,
+ hs_header->frame_length);
+ break;
+
+ case HS_PAGE_FIRM_CTL:
+ hs_page3 = (struct mvumi_hs_page3 *) hs_header;
+ hs_header->frame_length = sizeof(*hs_page3) - 4;
+ memset(hs_header->frame_content, 0, hs_header->frame_length);
+ hs_header->checksum = mvumi_calculate_checksum(hs_header,
+ hs_header->frame_length);
+ break;
+
+ case HS_PAGE_CL_INFO:
+ hs_page4 = (struct mvumi_hs_page4 *) hs_header;
+ hs_header->frame_length = sizeof(*hs_page4) - 4;
+ memset(hs_header->frame_content, 0, hs_header->frame_length);
+ hs_page4->ib_baseaddr_l = lower_32_bits(mhba->ib_list_phys);
+ hs_page4->ib_baseaddr_h = upper_32_bits(mhba->ib_list_phys);
+
+ hs_page4->ob_baseaddr_l = lower_32_bits(mhba->ob_list_phys);
+ hs_page4->ob_baseaddr_h = upper_32_bits(mhba->ob_list_phys);
+ hs_page4->ib_entry_size = mhba->ib_max_size_setting;
+ hs_page4->ob_entry_size = mhba->ob_max_size_setting;
+ if (mhba->hba_capability
+ & HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF) {
+ hs_page4->ob_depth = find_first_bit((unsigned long *)
+ &mhba->list_num_io,
+ BITS_PER_LONG);
+ hs_page4->ib_depth = find_first_bit((unsigned long *)
+ &mhba->list_num_io,
+ BITS_PER_LONG);
+ } else {
+ hs_page4->ob_depth = (u8) mhba->list_num_io;
+ hs_page4->ib_depth = (u8) mhba->list_num_io;
+ }
+ hs_header->checksum = mvumi_calculate_checksum(hs_header,
+ hs_header->frame_length);
+ break;
+
+ default:
+ dev_err(&mhba->pdev->dev, "cannot build page, code[0x%x]\n",
+ hs_header->page_code);
+ break;
+ }
+}
+
+/**
+ * mvumi_init_data - Initialize requested date for FW
+ * @mhba: Adapter soft state
+ */
+static int mvumi_init_data(struct mvumi_hba *mhba)
+{
+ struct mvumi_ob_data *ob_pool;
+ struct mvumi_res *res_mgnt;
+ unsigned int tmp_size, offset, i;
+ void *virmem, *v;
+ dma_addr_t p;
+
+ if (mhba->fw_flag & MVUMI_FW_ALLOC)
+ return 0;
+
+ tmp_size = mhba->ib_max_size * mhba->max_io;
+ if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC)
+ tmp_size += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io;
+
+ tmp_size += 128 + mhba->ob_max_size * mhba->max_io;
+ tmp_size += 8 + sizeof(u32)*2 + 16;
+
+ res_mgnt = mvumi_alloc_mem_resource(mhba,
+ RESOURCE_UNCACHED_MEMORY, tmp_size);
+ if (!res_mgnt) {
+ dev_err(&mhba->pdev->dev,
+ "failed to allocate memory for inbound list\n");
+ goto fail_alloc_dma_buf;
+ }
+
+ p = res_mgnt->bus_addr;
+ v = res_mgnt->virt_addr;
+ /* ib_list */
+ offset = round_up(p, 128) - p;
+ p += offset;
+ v += offset;
+ mhba->ib_list = v;
+ mhba->ib_list_phys = p;
+ if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
+ v += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io;
+ p += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io;
+ mhba->ib_frame = v;
+ mhba->ib_frame_phys = p;
+ }
+ v += mhba->ib_max_size * mhba->max_io;
+ p += mhba->ib_max_size * mhba->max_io;
+
+ /* ib shadow */
+ offset = round_up(p, 8) - p;
+ p += offset;
+ v += offset;
+ mhba->ib_shadow = v;
+ mhba->ib_shadow_phys = p;
+ p += sizeof(u32)*2;
+ v += sizeof(u32)*2;
+ /* ob shadow */
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) {
+ offset = round_up(p, 8) - p;
+ p += offset;
+ v += offset;
+ mhba->ob_shadow = v;
+ mhba->ob_shadow_phys = p;
+ p += 8;
+ v += 8;
+ } else {
+ offset = round_up(p, 4) - p;
+ p += offset;
+ v += offset;
+ mhba->ob_shadow = v;
+ mhba->ob_shadow_phys = p;
+ p += 4;
+ v += 4;
+ }
+
+ /* ob list */
+ offset = round_up(p, 128) - p;
+ p += offset;
+ v += offset;
+
+ mhba->ob_list = v;
+ mhba->ob_list_phys = p;
+
+ /* ob data pool */
+ tmp_size = mhba->max_io * (mhba->ob_max_size + sizeof(*ob_pool));
+ tmp_size = round_up(tmp_size, 8);
+
+ res_mgnt = mvumi_alloc_mem_resource(mhba,
+ RESOURCE_CACHED_MEMORY, tmp_size);
+ if (!res_mgnt) {
+ dev_err(&mhba->pdev->dev,
+ "failed to allocate memory for outbound data buffer\n");
+ goto fail_alloc_dma_buf;
+ }
+ virmem = res_mgnt->virt_addr;
+
+ for (i = mhba->max_io; i != 0; i--) {
+ ob_pool = (struct mvumi_ob_data *) virmem;
+ list_add_tail(&ob_pool->list, &mhba->ob_data_list);
+ virmem += mhba->ob_max_size + sizeof(*ob_pool);
+ }
+
+ tmp_size = sizeof(unsigned short) * mhba->max_io +
+ sizeof(struct mvumi_cmd *) * mhba->max_io;
+ tmp_size += round_up(mhba->max_target_id, sizeof(unsigned char) * 8) /
+ (sizeof(unsigned char) * 8);
+
+ res_mgnt = mvumi_alloc_mem_resource(mhba,
+ RESOURCE_CACHED_MEMORY, tmp_size);
+ if (!res_mgnt) {
+ dev_err(&mhba->pdev->dev,
+ "failed to allocate memory for tag and target map\n");
+ goto fail_alloc_dma_buf;
+ }
+
+ virmem = res_mgnt->virt_addr;
+ mhba->tag_pool.stack = virmem;
+ mhba->tag_pool.size = mhba->max_io;
+ tag_init(&mhba->tag_pool, mhba->max_io);
+ virmem += sizeof(unsigned short) * mhba->max_io;
+
+ mhba->tag_cmd = virmem;
+ virmem += sizeof(struct mvumi_cmd *) * mhba->max_io;
+
+ mhba->target_map = virmem;
+
+ mhba->fw_flag |= MVUMI_FW_ALLOC;
+ return 0;
+
+fail_alloc_dma_buf:
+ mvumi_release_mem_resource(mhba);
+ return -1;
+}
+
+static int mvumi_hs_process_page(struct mvumi_hba *mhba,
+ struct mvumi_hs_header *hs_header)
+{
+ struct mvumi_hs_page1 *hs_page1;
+ unsigned char page_checksum;
+
+ page_checksum = mvumi_calculate_checksum(hs_header,
+ hs_header->frame_length);
+ if (page_checksum != hs_header->checksum) {
+ dev_err(&mhba->pdev->dev, "checksum error\n");
+ return -1;
+ }
+
+ switch (hs_header->page_code) {
+ case HS_PAGE_FIRM_CAP:
+ hs_page1 = (struct mvumi_hs_page1 *) hs_header;
+
+ mhba->max_io = hs_page1->max_io_support;
+ mhba->list_num_io = hs_page1->cl_inout_list_depth;
+ mhba->max_transfer_size = hs_page1->max_transfer_size;
+ mhba->max_target_id = hs_page1->max_devices_support;
+ mhba->hba_capability = hs_page1->capability;
+ mhba->ib_max_size_setting = hs_page1->cl_in_max_entry_size;
+ mhba->ib_max_size = (1 << hs_page1->cl_in_max_entry_size) << 2;
+
+ mhba->ob_max_size_setting = hs_page1->cl_out_max_entry_size;
+ mhba->ob_max_size = (1 << hs_page1->cl_out_max_entry_size) << 2;
+
+ dev_dbg(&mhba->pdev->dev, "FW version:%d\n",
+ hs_page1->fw_ver.ver_build);
+
+ if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG)
+ mhba->eot_flag = 22;
+ else
+ mhba->eot_flag = 27;
+ if (mhba->hba_capability & HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF)
+ mhba->list_num_io = 1 << hs_page1->cl_inout_list_depth;
+ break;
+ default:
+ dev_err(&mhba->pdev->dev, "handshake: page code error\n");
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * mvumi_handshake - Move the FW to READY state
+ * @mhba: Adapter soft state
+ *
+ * During the initialization, FW passes can potentially be in any one of
+ * several possible states. If the FW in operational, waiting-for-handshake
+ * states, driver must take steps to bring it to ready state. Otherwise, it
+ * has to wait for the ready state.
+ */
+static int mvumi_handshake(struct mvumi_hba *mhba)
+{
+ unsigned int hs_state, tmp, hs_fun;
+ struct mvumi_hs_header *hs_header;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ if (mhba->fw_state == FW_STATE_STARTING)
+ hs_state = HS_S_START;
+ else {
+ tmp = ioread32(regs->arm_to_pciea_msg0);
+ hs_state = HS_GET_STATE(tmp);
+ dev_dbg(&mhba->pdev->dev, "handshake state[0x%x].\n", hs_state);
+ if (HS_GET_STATUS(tmp) != HS_STATUS_OK) {
+ mhba->fw_state = FW_STATE_STARTING;
+ return -1;
+ }
+ }
+
+ hs_fun = 0;
+ switch (hs_state) {
+ case HS_S_START:
+ mhba->fw_state = FW_STATE_HANDSHAKING;
+ HS_SET_STATUS(hs_fun, HS_STATUS_OK);
+ HS_SET_STATE(hs_fun, HS_S_RESET);
+ iowrite32(HANDSHAKE_SIGNATURE, regs->pciea_to_arm_msg1);
+ iowrite32(hs_fun, regs->pciea_to_arm_msg0);
+ iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg);
+ break;
+
+ case HS_S_RESET:
+ iowrite32(lower_32_bits(mhba->handshake_page_phys),
+ regs->pciea_to_arm_msg1);
+ iowrite32(upper_32_bits(mhba->handshake_page_phys),
+ regs->arm_to_pciea_msg1);
+ HS_SET_STATUS(hs_fun, HS_STATUS_OK);
+ HS_SET_STATE(hs_fun, HS_S_PAGE_ADDR);
+ iowrite32(hs_fun, regs->pciea_to_arm_msg0);
+ iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg);
+ break;
+
+ case HS_S_PAGE_ADDR:
+ case HS_S_QUERY_PAGE:
+ case HS_S_SEND_PAGE:
+ hs_header = (struct mvumi_hs_header *) mhba->handshake_page;
+ if (hs_header->page_code == HS_PAGE_FIRM_CAP) {
+ mhba->hba_total_pages =
+ ((struct mvumi_hs_page1 *) hs_header)->total_pages;
+
+ if (mhba->hba_total_pages == 0)
+ mhba->hba_total_pages = HS_PAGE_TOTAL-1;
+ }
+
+ if (hs_state == HS_S_QUERY_PAGE) {
+ if (mvumi_hs_process_page(mhba, hs_header)) {
+ HS_SET_STATE(hs_fun, HS_S_ABORT);
+ return -1;
+ }
+ if (mvumi_init_data(mhba)) {
+ HS_SET_STATE(hs_fun, HS_S_ABORT);
+ return -1;
+ }
+ } else if (hs_state == HS_S_PAGE_ADDR) {
+ hs_header->page_code = 0;
+ mhba->hba_total_pages = HS_PAGE_TOTAL-1;
+ }
+
+ if ((hs_header->page_code + 1) <= mhba->hba_total_pages) {
+ hs_header->page_code++;
+ if (hs_header->page_code != HS_PAGE_FIRM_CAP) {
+ mvumi_hs_build_page(mhba, hs_header);
+ HS_SET_STATE(hs_fun, HS_S_SEND_PAGE);
+ } else
+ HS_SET_STATE(hs_fun, HS_S_QUERY_PAGE);
+ } else
+ HS_SET_STATE(hs_fun, HS_S_END);
+
+ HS_SET_STATUS(hs_fun, HS_STATUS_OK);
+ iowrite32(hs_fun, regs->pciea_to_arm_msg0);
+ iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg);
+ break;
+
+ case HS_S_END:
+ /* Set communication list ISR */
+ tmp = ioread32(regs->enpointa_mask_reg);
+ tmp |= regs->int_comaout | regs->int_comaerr;
+ iowrite32(tmp, regs->enpointa_mask_reg);
+ iowrite32(mhba->list_num_io, mhba->ib_shadow);
+ /* Set InBound List Available count shadow */
+ iowrite32(lower_32_bits(mhba->ib_shadow_phys),
+ regs->inb_aval_count_basel);
+ iowrite32(upper_32_bits(mhba->ib_shadow_phys),
+ regs->inb_aval_count_baseh);
+
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143) {
+ /* Set OutBound List Available count shadow */
+ iowrite32((mhba->list_num_io-1) |
+ regs->cl_pointer_toggle,
+ mhba->ob_shadow);
+ iowrite32(lower_32_bits(mhba->ob_shadow_phys),
+ regs->outb_copy_basel);
+ iowrite32(upper_32_bits(mhba->ob_shadow_phys),
+ regs->outb_copy_baseh);
+ }
+
+ mhba->ib_cur_slot = (mhba->list_num_io - 1) |
+ regs->cl_pointer_toggle;
+ mhba->ob_cur_slot = (mhba->list_num_io - 1) |
+ regs->cl_pointer_toggle;
+ mhba->fw_state = FW_STATE_STARTED;
+
+ break;
+ default:
+ dev_err(&mhba->pdev->dev, "unknown handshake state [0x%x].\n",
+ hs_state);
+ return -1;
+ }
+ return 0;
+}
+
+static unsigned char mvumi_handshake_event(struct mvumi_hba *mhba)
+{
+ unsigned int isr_status;
+ unsigned long before;
+
+ before = jiffies;
+ mvumi_handshake(mhba);
+ do {
+ isr_status = mhba->instancet->read_fw_status_reg(mhba);
+
+ if (mhba->fw_state == FW_STATE_STARTED)
+ return 0;
+ if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) {
+ dev_err(&mhba->pdev->dev,
+ "no handshake response at state 0x%x.\n",
+ mhba->fw_state);
+ dev_err(&mhba->pdev->dev,
+ "isr : global=0x%x,status=0x%x.\n",
+ mhba->global_isr, isr_status);
+ return -1;
+ }
+ rmb();
+ usleep_range(1000, 2000);
+ } while (!(isr_status & DRBL_HANDSHAKE_ISR));
+
+ return 0;
+}
+
+static unsigned char mvumi_check_handshake(struct mvumi_hba *mhba)
+{
+ unsigned int tmp;
+ unsigned long before;
+
+ before = jiffies;
+ tmp = ioread32(mhba->regs->arm_to_pciea_msg1);
+ while ((tmp != HANDSHAKE_READYSTATE) && (tmp != HANDSHAKE_DONESTATE)) {
+ if (tmp != HANDSHAKE_READYSTATE)
+ iowrite32(DRBL_MU_RESET,
+ mhba->regs->pciea_to_arm_drbl_reg);
+ if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) {
+ dev_err(&mhba->pdev->dev,
+ "invalid signature [0x%x].\n", tmp);
+ return -1;
+ }
+ usleep_range(1000, 2000);
+ rmb();
+ tmp = ioread32(mhba->regs->arm_to_pciea_msg1);
+ }
+
+ mhba->fw_state = FW_STATE_STARTING;
+ dev_dbg(&mhba->pdev->dev, "start firmware handshake...\n");
+ do {
+ if (mvumi_handshake_event(mhba)) {
+ dev_err(&mhba->pdev->dev,
+ "handshake failed at state 0x%x.\n",
+ mhba->fw_state);
+ return -1;
+ }
+ } while (mhba->fw_state != FW_STATE_STARTED);
+
+ dev_dbg(&mhba->pdev->dev, "firmware handshake done\n");
+
+ return 0;
+}
+
+static unsigned char mvumi_start(struct mvumi_hba *mhba)
+{
+ unsigned int tmp;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ /* clear Door bell */
+ tmp = ioread32(regs->arm_to_pciea_drbl_reg);
+ iowrite32(tmp, regs->arm_to_pciea_drbl_reg);
+
+ iowrite32(regs->int_drbl_int_mask, regs->arm_to_pciea_mask_reg);
+ tmp = ioread32(regs->enpointa_mask_reg) | regs->int_dl_cpu2pciea;
+ iowrite32(tmp, regs->enpointa_mask_reg);
+ msleep(100);
+ if (mvumi_check_handshake(mhba))
+ return -1;
+
+ return 0;
+}
+
+/**
+ * mvumi_complete_cmd - Completes a command
+ * @mhba: Adapter soft state
+ * @cmd: Command to be completed
+ * @ob_frame: Command response
+ */
+static void mvumi_complete_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd,
+ struct mvumi_rsp_frame *ob_frame)
+{
+ struct scsi_cmnd *scmd = cmd->scmd;
+
+ mvumi_priv(cmd->scmd)->cmd_priv = NULL;
+ scmd->result = ob_frame->req_status;
+
+ switch (ob_frame->req_status) {
+ case SAM_STAT_GOOD:
+ scmd->result |= DID_OK << 16;
+ break;
+ case SAM_STAT_BUSY:
+ scmd->result |= DID_BUS_BUSY << 16;
+ break;
+ case SAM_STAT_CHECK_CONDITION:
+ scmd->result |= (DID_OK << 16);
+ if (ob_frame->rsp_flag & CL_RSP_FLAG_SENSEDATA) {
+ memcpy(cmd->scmd->sense_buffer, ob_frame->payload,
+ sizeof(struct mvumi_sense_data));
+ }
+ break;
+ default:
+ scmd->result |= (DID_ABORT << 16);
+ break;
+ }
+
+ if (scsi_bufflen(scmd))
+ dma_unmap_sg(&mhba->pdev->dev, scsi_sglist(scmd),
+ scsi_sg_count(scmd),
+ scmd->sc_data_direction);
+ scsi_done(scmd);
+ mvumi_return_cmd(mhba, cmd);
+}
+
+static void mvumi_complete_internal_cmd(struct mvumi_hba *mhba,
+ struct mvumi_cmd *cmd,
+ struct mvumi_rsp_frame *ob_frame)
+{
+ if (atomic_read(&cmd->sync_cmd)) {
+ cmd->cmd_status = ob_frame->req_status;
+
+ if ((ob_frame->req_status == SAM_STAT_CHECK_CONDITION) &&
+ (ob_frame->rsp_flag & CL_RSP_FLAG_SENSEDATA) &&
+ cmd->data_buf) {
+ memcpy(cmd->data_buf, ob_frame->payload,
+ sizeof(struct mvumi_sense_data));
+ }
+ atomic_dec(&cmd->sync_cmd);
+ wake_up(&mhba->int_cmd_wait_q);
+ }
+}
+
+static void mvumi_show_event(struct mvumi_hba *mhba,
+ struct mvumi_driver_event *ptr)
+{
+ unsigned int i;
+
+ dev_warn(&mhba->pdev->dev,
+ "Event[0x%x] id[0x%x] severity[0x%x] device id[0x%x]\n",
+ ptr->sequence_no, ptr->event_id, ptr->severity, ptr->device_id);
+ if (ptr->param_count) {
+ printk(KERN_WARNING "Event param(len 0x%x): ",
+ ptr->param_count);
+ for (i = 0; i < ptr->param_count; i++)
+ printk(KERN_WARNING "0x%x ", ptr->params[i]);
+
+ printk(KERN_WARNING "\n");
+ }
+
+ if (ptr->sense_data_length) {
+ printk(KERN_WARNING "Event sense data(len 0x%x): ",
+ ptr->sense_data_length);
+ for (i = 0; i < ptr->sense_data_length; i++)
+ printk(KERN_WARNING "0x%x ", ptr->sense_data[i]);
+ printk(KERN_WARNING "\n");
+ }
+}
+
+static int mvumi_handle_hotplug(struct mvumi_hba *mhba, u16 devid, int status)
+{
+ struct scsi_device *sdev;
+ int ret = -1;
+
+ if (status == DEVICE_OFFLINE) {
+ sdev = scsi_device_lookup(mhba->shost, 0, devid, 0);
+ if (sdev) {
+ dev_dbg(&mhba->pdev->dev, "remove disk %d-%d-%d.\n", 0,
+ sdev->id, 0);
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ ret = 0;
+ } else
+ dev_err(&mhba->pdev->dev, " no disk[%d] to remove\n",
+ devid);
+ } else if (status == DEVICE_ONLINE) {
+ sdev = scsi_device_lookup(mhba->shost, 0, devid, 0);
+ if (!sdev) {
+ scsi_add_device(mhba->shost, 0, devid, 0);
+ dev_dbg(&mhba->pdev->dev, " add disk %d-%d-%d.\n", 0,
+ devid, 0);
+ ret = 0;
+ } else {
+ dev_err(&mhba->pdev->dev, " don't add disk %d-%d-%d.\n",
+ 0, devid, 0);
+ scsi_device_put(sdev);
+ }
+ }
+ return ret;
+}
+
+static u64 mvumi_inquiry(struct mvumi_hba *mhba,
+ unsigned int id, struct mvumi_cmd *cmd)
+{
+ struct mvumi_msg_frame *frame;
+ u64 wwid = 0;
+ int cmd_alloc = 0;
+ int data_buf_len = 64;
+
+ if (!cmd) {
+ cmd = mvumi_create_internal_cmd(mhba, data_buf_len);
+ if (cmd)
+ cmd_alloc = 1;
+ else
+ return 0;
+ } else {
+ memset(cmd->data_buf, 0, data_buf_len);
+ }
+ cmd->scmd = NULL;
+ cmd->cmd_status = REQ_STATUS_PENDING;
+ atomic_set(&cmd->sync_cmd, 0);
+ frame = cmd->frame;
+ frame->device_id = (u16) id;
+ frame->cmd_flag = CMD_FLAG_DATA_IN;
+ frame->req_function = CL_FUN_SCSI_CMD;
+ frame->cdb_length = 6;
+ frame->data_transfer_length = MVUMI_INQUIRY_LENGTH;
+ memset(frame->cdb, 0, frame->cdb_length);
+ frame->cdb[0] = INQUIRY;
+ frame->cdb[4] = frame->data_transfer_length;
+
+ mvumi_issue_blocked_cmd(mhba, cmd);
+
+ if (cmd->cmd_status == SAM_STAT_GOOD) {
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143)
+ wwid = id + 1;
+ else
+ memcpy((void *)&wwid,
+ (cmd->data_buf + MVUMI_INQUIRY_UUID_OFF),
+ MVUMI_INQUIRY_UUID_LEN);
+ dev_dbg(&mhba->pdev->dev,
+ "inquiry device(0:%d:0) wwid(%llx)\n", id, wwid);
+ } else {
+ wwid = 0;
+ }
+ if (cmd_alloc)
+ mvumi_delete_internal_cmd(mhba, cmd);
+
+ return wwid;
+}
+
+static void mvumi_detach_devices(struct mvumi_hba *mhba)
+{
+ struct mvumi_device *mv_dev = NULL , *dev_next;
+ struct scsi_device *sdev = NULL;
+
+ mutex_lock(&mhba->device_lock);
+
+ /* detach Hard Disk */
+ list_for_each_entry_safe(mv_dev, dev_next,
+ &mhba->shost_dev_list, list) {
+ mvumi_handle_hotplug(mhba, mv_dev->id, DEVICE_OFFLINE);
+ list_del_init(&mv_dev->list);
+ dev_dbg(&mhba->pdev->dev, "release device(0:%d:0) wwid(%llx)\n",
+ mv_dev->id, mv_dev->wwid);
+ kfree(mv_dev);
+ }
+ list_for_each_entry_safe(mv_dev, dev_next, &mhba->mhba_dev_list, list) {
+ list_del_init(&mv_dev->list);
+ dev_dbg(&mhba->pdev->dev, "release device(0:%d:0) wwid(%llx)\n",
+ mv_dev->id, mv_dev->wwid);
+ kfree(mv_dev);
+ }
+
+ /* detach virtual device */
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580)
+ sdev = scsi_device_lookup(mhba->shost, 0,
+ mhba->max_target_id - 1, 0);
+
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ }
+
+ mutex_unlock(&mhba->device_lock);
+}
+
+static void mvumi_rescan_devices(struct mvumi_hba *mhba, int id)
+{
+ struct scsi_device *sdev;
+
+ sdev = scsi_device_lookup(mhba->shost, 0, id, 0);
+ if (sdev) {
+ scsi_rescan_device(sdev);
+ scsi_device_put(sdev);
+ }
+}
+
+static int mvumi_match_devices(struct mvumi_hba *mhba, int id, u64 wwid)
+{
+ struct mvumi_device *mv_dev = NULL;
+
+ list_for_each_entry(mv_dev, &mhba->shost_dev_list, list) {
+ if (mv_dev->wwid == wwid) {
+ if (mv_dev->id != id) {
+ dev_err(&mhba->pdev->dev,
+ "%s has same wwid[%llx] ,"
+ " but different id[%d %d]\n",
+ __func__, mv_dev->wwid, mv_dev->id, id);
+ return -1;
+ } else {
+ if (mhba->pdev->device ==
+ PCI_DEVICE_ID_MARVELL_MV9143)
+ mvumi_rescan_devices(mhba, id);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void mvumi_remove_devices(struct mvumi_hba *mhba, int id)
+{
+ struct mvumi_device *mv_dev = NULL, *dev_next;
+
+ list_for_each_entry_safe(mv_dev, dev_next,
+ &mhba->shost_dev_list, list) {
+ if (mv_dev->id == id) {
+ dev_dbg(&mhba->pdev->dev,
+ "detach device(0:%d:0) wwid(%llx) from HOST\n",
+ mv_dev->id, mv_dev->wwid);
+ mvumi_handle_hotplug(mhba, mv_dev->id, DEVICE_OFFLINE);
+ list_del_init(&mv_dev->list);
+ kfree(mv_dev);
+ }
+ }
+}
+
+static int mvumi_probe_devices(struct mvumi_hba *mhba)
+{
+ int id, maxid;
+ u64 wwid = 0;
+ struct mvumi_device *mv_dev = NULL;
+ struct mvumi_cmd *cmd = NULL;
+ int found = 0;
+
+ cmd = mvumi_create_internal_cmd(mhba, 64);
+ if (!cmd)
+ return -1;
+
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143)
+ maxid = mhba->max_target_id;
+ else
+ maxid = mhba->max_target_id - 1;
+
+ for (id = 0; id < maxid; id++) {
+ wwid = mvumi_inquiry(mhba, id, cmd);
+ if (!wwid) {
+ /* device no response, remove it */
+ mvumi_remove_devices(mhba, id);
+ } else {
+ /* device response, add it */
+ found = mvumi_match_devices(mhba, id, wwid);
+ if (!found) {
+ mvumi_remove_devices(mhba, id);
+ mv_dev = kzalloc(sizeof(struct mvumi_device),
+ GFP_KERNEL);
+ if (!mv_dev) {
+ dev_err(&mhba->pdev->dev,
+ "%s alloc mv_dev failed\n",
+ __func__);
+ continue;
+ }
+ mv_dev->id = id;
+ mv_dev->wwid = wwid;
+ mv_dev->sdev = NULL;
+ INIT_LIST_HEAD(&mv_dev->list);
+ list_add_tail(&mv_dev->list,
+ &mhba->mhba_dev_list);
+ dev_dbg(&mhba->pdev->dev,
+ "probe a new device(0:%d:0)"
+ " wwid(%llx)\n", id, mv_dev->wwid);
+ } else if (found == -1)
+ return -1;
+ else
+ continue;
+ }
+ }
+
+ if (cmd)
+ mvumi_delete_internal_cmd(mhba, cmd);
+
+ return 0;
+}
+
+static int mvumi_rescan_bus(void *data)
+{
+ int ret = 0;
+ struct mvumi_hba *mhba = (struct mvumi_hba *) data;
+ struct mvumi_device *mv_dev = NULL , *dev_next;
+
+ while (!kthread_should_stop()) {
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!atomic_read(&mhba->pnp_count))
+ schedule();
+ msleep(1000);
+ atomic_set(&mhba->pnp_count, 0);
+ __set_current_state(TASK_RUNNING);
+
+ mutex_lock(&mhba->device_lock);
+ ret = mvumi_probe_devices(mhba);
+ if (!ret) {
+ list_for_each_entry_safe(mv_dev, dev_next,
+ &mhba->mhba_dev_list, list) {
+ if (mvumi_handle_hotplug(mhba, mv_dev->id,
+ DEVICE_ONLINE)) {
+ dev_err(&mhba->pdev->dev,
+ "%s add device(0:%d:0) failed"
+ "wwid(%llx) has exist\n",
+ __func__,
+ mv_dev->id, mv_dev->wwid);
+ list_del_init(&mv_dev->list);
+ kfree(mv_dev);
+ } else {
+ list_move_tail(&mv_dev->list,
+ &mhba->shost_dev_list);
+ }
+ }
+ }
+ mutex_unlock(&mhba->device_lock);
+ }
+ return 0;
+}
+
+static void mvumi_proc_msg(struct mvumi_hba *mhba,
+ struct mvumi_hotplug_event *param)
+{
+ u16 size = param->size;
+ const unsigned long *ar_bitmap;
+ const unsigned long *re_bitmap;
+ int index;
+
+ if (mhba->fw_flag & MVUMI_FW_ATTACH) {
+ index = -1;
+ ar_bitmap = (const unsigned long *) param->bitmap;
+ re_bitmap = (const unsigned long *) &param->bitmap[size >> 3];
+
+ mutex_lock(&mhba->sas_discovery_mutex);
+ do {
+ index = find_next_zero_bit(ar_bitmap, size, index + 1);
+ if (index >= size)
+ break;
+ mvumi_handle_hotplug(mhba, index, DEVICE_ONLINE);
+ } while (1);
+
+ index = -1;
+ do {
+ index = find_next_zero_bit(re_bitmap, size, index + 1);
+ if (index >= size)
+ break;
+ mvumi_handle_hotplug(mhba, index, DEVICE_OFFLINE);
+ } while (1);
+ mutex_unlock(&mhba->sas_discovery_mutex);
+ }
+}
+
+static void mvumi_notification(struct mvumi_hba *mhba, u8 msg, void *buffer)
+{
+ if (msg == APICDB1_EVENT_GETEVENT) {
+ int i, count;
+ struct mvumi_driver_event *param = NULL;
+ struct mvumi_event_req *er = buffer;
+ count = er->count;
+ if (count > MAX_EVENTS_RETURNED) {
+ dev_err(&mhba->pdev->dev, "event count[0x%x] is bigger"
+ " than max event count[0x%x].\n",
+ count, MAX_EVENTS_RETURNED);
+ return;
+ }
+ for (i = 0; i < count; i++) {
+ param = &er->events[i];
+ mvumi_show_event(mhba, param);
+ }
+ } else if (msg == APICDB1_HOST_GETEVENT) {
+ mvumi_proc_msg(mhba, buffer);
+ }
+}
+
+static int mvumi_get_event(struct mvumi_hba *mhba, unsigned char msg)
+{
+ struct mvumi_cmd *cmd;
+ struct mvumi_msg_frame *frame;
+
+ cmd = mvumi_create_internal_cmd(mhba, 512);
+ if (!cmd)
+ return -1;
+ cmd->scmd = NULL;
+ cmd->cmd_status = REQ_STATUS_PENDING;
+ atomic_set(&cmd->sync_cmd, 0);
+ frame = cmd->frame;
+ frame->device_id = 0;
+ frame->cmd_flag = CMD_FLAG_DATA_IN;
+ frame->req_function = CL_FUN_SCSI_CMD;
+ frame->cdb_length = MAX_COMMAND_SIZE;
+ frame->data_transfer_length = sizeof(struct mvumi_event_req);
+ memset(frame->cdb, 0, MAX_COMMAND_SIZE);
+ frame->cdb[0] = APICDB0_EVENT;
+ frame->cdb[1] = msg;
+ mvumi_issue_blocked_cmd(mhba, cmd);
+
+ if (cmd->cmd_status != SAM_STAT_GOOD)
+ dev_err(&mhba->pdev->dev, "get event failed, status=0x%x.\n",
+ cmd->cmd_status);
+ else
+ mvumi_notification(mhba, cmd->frame->cdb[1], cmd->data_buf);
+
+ mvumi_delete_internal_cmd(mhba, cmd);
+ return 0;
+}
+
+static void mvumi_scan_events(struct work_struct *work)
+{
+ struct mvumi_events_wq *mu_ev =
+ container_of(work, struct mvumi_events_wq, work_q);
+
+ mvumi_get_event(mu_ev->mhba, mu_ev->event);
+ kfree(mu_ev);
+}
+
+static void mvumi_launch_events(struct mvumi_hba *mhba, u32 isr_status)
+{
+ struct mvumi_events_wq *mu_ev;
+
+ while (isr_status & (DRBL_BUS_CHANGE | DRBL_EVENT_NOTIFY)) {
+ if (isr_status & DRBL_BUS_CHANGE) {
+ atomic_inc(&mhba->pnp_count);
+ wake_up_process(mhba->dm_thread);
+ isr_status &= ~(DRBL_BUS_CHANGE);
+ continue;
+ }
+
+ mu_ev = kzalloc(sizeof(*mu_ev), GFP_ATOMIC);
+ if (mu_ev) {
+ INIT_WORK(&mu_ev->work_q, mvumi_scan_events);
+ mu_ev->mhba = mhba;
+ mu_ev->event = APICDB1_EVENT_GETEVENT;
+ isr_status &= ~(DRBL_EVENT_NOTIFY);
+ mu_ev->param = NULL;
+ schedule_work(&mu_ev->work_q);
+ }
+ }
+}
+
+static void mvumi_handle_clob(struct mvumi_hba *mhba)
+{
+ struct mvumi_rsp_frame *ob_frame;
+ struct mvumi_cmd *cmd;
+ struct mvumi_ob_data *pool;
+
+ while (!list_empty(&mhba->free_ob_list)) {
+ pool = list_first_entry(&mhba->free_ob_list,
+ struct mvumi_ob_data, list);
+ list_del_init(&pool->list);
+ list_add_tail(&pool->list, &mhba->ob_data_list);
+
+ ob_frame = (struct mvumi_rsp_frame *) &pool->data[0];
+ cmd = mhba->tag_cmd[ob_frame->tag];
+
+ atomic_dec(&mhba->fw_outstanding);
+ mhba->tag_cmd[ob_frame->tag] = NULL;
+ tag_release_one(mhba, &mhba->tag_pool, ob_frame->tag);
+ if (cmd->scmd)
+ mvumi_complete_cmd(mhba, cmd, ob_frame);
+ else
+ mvumi_complete_internal_cmd(mhba, cmd, ob_frame);
+ }
+ mhba->instancet->fire_cmd(mhba, NULL);
+}
+
+static irqreturn_t mvumi_isr_handler(int irq, void *devp)
+{
+ struct mvumi_hba *mhba = (struct mvumi_hba *) devp;
+ unsigned long flags;
+
+ spin_lock_irqsave(mhba->shost->host_lock, flags);
+ if (unlikely(mhba->instancet->clear_intr(mhba) || !mhba->global_isr)) {
+ spin_unlock_irqrestore(mhba->shost->host_lock, flags);
+ return IRQ_NONE;
+ }
+
+ if (mhba->global_isr & mhba->regs->int_dl_cpu2pciea) {
+ if (mhba->isr_status & (DRBL_BUS_CHANGE | DRBL_EVENT_NOTIFY))
+ mvumi_launch_events(mhba, mhba->isr_status);
+ if (mhba->isr_status & DRBL_HANDSHAKE_ISR) {
+ dev_warn(&mhba->pdev->dev, "enter handshake again!\n");
+ mvumi_handshake(mhba);
+ }
+
+ }
+
+ if (mhba->global_isr & mhba->regs->int_comaout)
+ mvumi_receive_ob_list_entry(mhba);
+
+ mhba->global_isr = 0;
+ mhba->isr_status = 0;
+ if (mhba->fw_state == FW_STATE_STARTED)
+ mvumi_handle_clob(mhba);
+ spin_unlock_irqrestore(mhba->shost->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static enum mvumi_qc_result mvumi_send_command(struct mvumi_hba *mhba,
+ struct mvumi_cmd *cmd)
+{
+ void *ib_entry;
+ struct mvumi_msg_frame *ib_frame;
+ unsigned int frame_len;
+
+ ib_frame = cmd->frame;
+ if (unlikely(mhba->fw_state != FW_STATE_STARTED)) {
+ dev_dbg(&mhba->pdev->dev, "firmware not ready.\n");
+ return MV_QUEUE_COMMAND_RESULT_NO_RESOURCE;
+ }
+ if (tag_is_empty(&mhba->tag_pool)) {
+ dev_dbg(&mhba->pdev->dev, "no free tag.\n");
+ return MV_QUEUE_COMMAND_RESULT_NO_RESOURCE;
+ }
+ mvumi_get_ib_list_entry(mhba, &ib_entry);
+
+ cmd->frame->tag = tag_get_one(mhba, &mhba->tag_pool);
+ cmd->frame->request_id = mhba->io_seq++;
+ cmd->request_id = cmd->frame->request_id;
+ mhba->tag_cmd[cmd->frame->tag] = cmd;
+ frame_len = sizeof(*ib_frame) +
+ ib_frame->sg_counts * sizeof(struct mvumi_sgl);
+ if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
+ struct mvumi_dyn_list_entry *dle;
+ dle = ib_entry;
+ dle->src_low_addr =
+ cpu_to_le32(lower_32_bits(cmd->frame_phys));
+ dle->src_high_addr =
+ cpu_to_le32(upper_32_bits(cmd->frame_phys));
+ dle->if_length = (frame_len >> 2) & 0xFFF;
+ } else {
+ memcpy(ib_entry, ib_frame, frame_len);
+ }
+ return MV_QUEUE_COMMAND_RESULT_SENT;
+}
+
+static void mvumi_fire_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd)
+{
+ unsigned short num_of_cl_sent = 0;
+ unsigned int count;
+ enum mvumi_qc_result result;
+
+ if (cmd)
+ list_add_tail(&cmd->queue_pointer, &mhba->waiting_req_list);
+ count = mhba->instancet->check_ib_list(mhba);
+ if (list_empty(&mhba->waiting_req_list) || !count)
+ return;
+
+ do {
+ cmd = list_first_entry(&mhba->waiting_req_list,
+ struct mvumi_cmd, queue_pointer);
+ list_del_init(&cmd->queue_pointer);
+ result = mvumi_send_command(mhba, cmd);
+ switch (result) {
+ case MV_QUEUE_COMMAND_RESULT_SENT:
+ num_of_cl_sent++;
+ break;
+ case MV_QUEUE_COMMAND_RESULT_NO_RESOURCE:
+ list_add(&cmd->queue_pointer, &mhba->waiting_req_list);
+ if (num_of_cl_sent > 0)
+ mvumi_send_ib_list_entry(mhba);
+
+ return;
+ }
+ } while (!list_empty(&mhba->waiting_req_list) && count--);
+
+ if (num_of_cl_sent > 0)
+ mvumi_send_ib_list_entry(mhba);
+}
+
+/**
+ * mvumi_enable_intr - Enables interrupts
+ * @mhba: Adapter soft state
+ */
+static void mvumi_enable_intr(struct mvumi_hba *mhba)
+{
+ unsigned int mask;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ iowrite32(regs->int_drbl_int_mask, regs->arm_to_pciea_mask_reg);
+ mask = ioread32(regs->enpointa_mask_reg);
+ mask |= regs->int_dl_cpu2pciea | regs->int_comaout | regs->int_comaerr;
+ iowrite32(mask, regs->enpointa_mask_reg);
+}
+
+/**
+ * mvumi_disable_intr -Disables interrupt
+ * @mhba: Adapter soft state
+ */
+static void mvumi_disable_intr(struct mvumi_hba *mhba)
+{
+ unsigned int mask;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ iowrite32(0, regs->arm_to_pciea_mask_reg);
+ mask = ioread32(regs->enpointa_mask_reg);
+ mask &= ~(regs->int_dl_cpu2pciea | regs->int_comaout |
+ regs->int_comaerr);
+ iowrite32(mask, regs->enpointa_mask_reg);
+}
+
+static int mvumi_clear_intr(void *extend)
+{
+ struct mvumi_hba *mhba = (struct mvumi_hba *) extend;
+ unsigned int status, isr_status = 0, tmp = 0;
+ struct mvumi_hw_regs *regs = mhba->regs;
+
+ status = ioread32(regs->main_int_cause_reg);
+ if (!(status & regs->int_mu) || status == 0xFFFFFFFF)
+ return 1;
+ if (unlikely(status & regs->int_comaerr)) {
+ tmp = ioread32(regs->outb_isr_cause);
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) {
+ if (tmp & regs->clic_out_err) {
+ iowrite32(tmp & regs->clic_out_err,
+ regs->outb_isr_cause);
+ }
+ } else {
+ if (tmp & (regs->clic_in_err | regs->clic_out_err))
+ iowrite32(tmp & (regs->clic_in_err |
+ regs->clic_out_err),
+ regs->outb_isr_cause);
+ }
+ status ^= mhba->regs->int_comaerr;
+ /* inbound or outbound parity error, command will timeout */
+ }
+ if (status & regs->int_comaout) {
+ tmp = ioread32(regs->outb_isr_cause);
+ if (tmp & regs->clic_irq)
+ iowrite32(tmp & regs->clic_irq, regs->outb_isr_cause);
+ }
+ if (status & regs->int_dl_cpu2pciea) {
+ isr_status = ioread32(regs->arm_to_pciea_drbl_reg);
+ if (isr_status)
+ iowrite32(isr_status, regs->arm_to_pciea_drbl_reg);
+ }
+
+ mhba->global_isr = status;
+ mhba->isr_status = isr_status;
+
+ return 0;
+}
+
+/**
+ * mvumi_read_fw_status_reg - returns the current FW status value
+ * @mhba: Adapter soft state
+ */
+static unsigned int mvumi_read_fw_status_reg(struct mvumi_hba *mhba)
+{
+ unsigned int status;
+
+ status = ioread32(mhba->regs->arm_to_pciea_drbl_reg);
+ if (status)
+ iowrite32(status, mhba->regs->arm_to_pciea_drbl_reg);
+ return status;
+}
+
+static struct mvumi_instance_template mvumi_instance_9143 = {
+ .fire_cmd = mvumi_fire_cmd,
+ .enable_intr = mvumi_enable_intr,
+ .disable_intr = mvumi_disable_intr,
+ .clear_intr = mvumi_clear_intr,
+ .read_fw_status_reg = mvumi_read_fw_status_reg,
+ .check_ib_list = mvumi_check_ib_list_9143,
+ .check_ob_list = mvumi_check_ob_list_9143,
+ .reset_host = mvumi_reset_host_9143,
+};
+
+static struct mvumi_instance_template mvumi_instance_9580 = {
+ .fire_cmd = mvumi_fire_cmd,
+ .enable_intr = mvumi_enable_intr,
+ .disable_intr = mvumi_disable_intr,
+ .clear_intr = mvumi_clear_intr,
+ .read_fw_status_reg = mvumi_read_fw_status_reg,
+ .check_ib_list = mvumi_check_ib_list_9580,
+ .check_ob_list = mvumi_check_ob_list_9580,
+ .reset_host = mvumi_reset_host_9580,
+};
+
+static int mvumi_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ struct mvumi_hba *mhba;
+ unsigned char bitcount = sizeof(unsigned char) * 8;
+
+ mhba = (struct mvumi_hba *) sdev->host->hostdata;
+ if (sdev->id >= mhba->max_target_id)
+ return -EINVAL;
+
+ mhba->target_map[sdev->id / bitcount] |= (1 << (sdev->id % bitcount));
+ return 0;
+}
+
+/**
+ * mvumi_build_frame - Prepares a direct cdb (DCDB) command
+ * @mhba: Adapter soft state
+ * @scmd: SCSI command
+ * @cmd: Command to be prepared in
+ *
+ * This function prepares CDB commands. These are typcially pass-through
+ * commands to the devices.
+ */
+static unsigned char mvumi_build_frame(struct mvumi_hba *mhba,
+ struct scsi_cmnd *scmd, struct mvumi_cmd *cmd)
+{
+ struct mvumi_msg_frame *pframe;
+
+ cmd->scmd = scmd;
+ cmd->cmd_status = REQ_STATUS_PENDING;
+ pframe = cmd->frame;
+ pframe->device_id = ((unsigned short) scmd->device->id) |
+ (((unsigned short) scmd->device->lun) << 8);
+ pframe->cmd_flag = 0;
+
+ switch (scmd->sc_data_direction) {
+ case DMA_NONE:
+ pframe->cmd_flag |= CMD_FLAG_NON_DATA;
+ break;
+ case DMA_FROM_DEVICE:
+ pframe->cmd_flag |= CMD_FLAG_DATA_IN;
+ break;
+ case DMA_TO_DEVICE:
+ pframe->cmd_flag |= CMD_FLAG_DATA_OUT;
+ break;
+ case DMA_BIDIRECTIONAL:
+ default:
+ dev_warn(&mhba->pdev->dev, "unexpected data direction[%d] "
+ "cmd[0x%x]\n", scmd->sc_data_direction, scmd->cmnd[0]);
+ goto error;
+ }
+
+ pframe->cdb_length = scmd->cmd_len;
+ memcpy(pframe->cdb, scmd->cmnd, pframe->cdb_length);
+ pframe->req_function = CL_FUN_SCSI_CMD;
+ if (scsi_bufflen(scmd)) {
+ if (mvumi_make_sgl(mhba, scmd, &pframe->payload[0],
+ &pframe->sg_counts))
+ goto error;
+
+ pframe->data_transfer_length = scsi_bufflen(scmd);
+ } else {
+ pframe->sg_counts = 0;
+ pframe->data_transfer_length = 0;
+ }
+ return 0;
+
+error:
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
+ return -1;
+}
+
+/**
+ * mvumi_queue_command - Queue entry point
+ * @shost: Scsi host to queue command on
+ * @scmd: SCSI command to be queued
+ */
+static int mvumi_queue_command(struct Scsi_Host *shost,
+ struct scsi_cmnd *scmd)
+{
+ struct mvumi_cmd *cmd;
+ struct mvumi_hba *mhba;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(shost->host_lock, irq_flags);
+
+ mhba = (struct mvumi_hba *) shost->hostdata;
+ scmd->result = 0;
+ cmd = mvumi_get_cmd(mhba);
+ if (unlikely(!cmd)) {
+ spin_unlock_irqrestore(shost->host_lock, irq_flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ if (unlikely(mvumi_build_frame(mhba, scmd, cmd)))
+ goto out_return_cmd;
+
+ cmd->scmd = scmd;
+ mvumi_priv(scmd)->cmd_priv = cmd;
+ mhba->instancet->fire_cmd(mhba, cmd);
+ spin_unlock_irqrestore(shost->host_lock, irq_flags);
+ return 0;
+
+out_return_cmd:
+ mvumi_return_cmd(mhba, cmd);
+ scsi_done(scmd);
+ spin_unlock_irqrestore(shost->host_lock, irq_flags);
+ return 0;
+}
+
+static enum scsi_timeout_action mvumi_timed_out(struct scsi_cmnd *scmd)
+{
+ struct mvumi_cmd *cmd = mvumi_priv(scmd)->cmd_priv;
+ struct Scsi_Host *host = scmd->device->host;
+ struct mvumi_hba *mhba = shost_priv(host);
+ unsigned long flags;
+
+ spin_lock_irqsave(mhba->shost->host_lock, flags);
+
+ if (mhba->tag_cmd[cmd->frame->tag]) {
+ mhba->tag_cmd[cmd->frame->tag] = NULL;
+ tag_release_one(mhba, &mhba->tag_pool, cmd->frame->tag);
+ }
+ if (!list_empty(&cmd->queue_pointer))
+ list_del_init(&cmd->queue_pointer);
+ else
+ atomic_dec(&mhba->fw_outstanding);
+
+ scmd->result = (DID_ABORT << 16);
+ mvumi_priv(scmd)->cmd_priv = NULL;
+ if (scsi_bufflen(scmd)) {
+ dma_unmap_sg(&mhba->pdev->dev, scsi_sglist(scmd),
+ scsi_sg_count(scmd),
+ scmd->sc_data_direction);
+ }
+ mvumi_return_cmd(mhba, cmd);
+ spin_unlock_irqrestore(mhba->shost->host_lock, flags);
+
+ return SCSI_EH_NOT_HANDLED;
+}
+
+static int
+mvumi_bios_param(struct scsi_device *sdev, struct gendisk *unused,
+ sector_t capacity, int geom[])
+{
+ int heads, sectors;
+ sector_t cylinders;
+ unsigned long tmp;
+
+ heads = 64;
+ sectors = 32;
+ tmp = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, tmp);
+
+ if (capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ tmp = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, tmp);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return 0;
+}
+
+static const struct scsi_host_template mvumi_template = {
+
+ .module = THIS_MODULE,
+ .name = "Marvell Storage Controller",
+ .sdev_configure = mvumi_sdev_configure,
+ .queuecommand = mvumi_queue_command,
+ .eh_timed_out = mvumi_timed_out,
+ .eh_host_reset_handler = mvumi_host_reset,
+ .bios_param = mvumi_bios_param,
+ .dma_boundary = PAGE_SIZE - 1,
+ .this_id = -1,
+ .cmd_size = sizeof(struct mvumi_cmd_priv),
+};
+
+static int mvumi_cfg_hw_reg(struct mvumi_hba *mhba)
+{
+ void *base = NULL;
+ struct mvumi_hw_regs *regs;
+
+ switch (mhba->pdev->device) {
+ case PCI_DEVICE_ID_MARVELL_MV9143:
+ mhba->mmio = mhba->base_addr[0];
+ base = mhba->mmio;
+ if (!mhba->regs) {
+ mhba->regs = kzalloc(sizeof(*regs), GFP_KERNEL);
+ if (mhba->regs == NULL)
+ return -ENOMEM;
+ }
+ regs = mhba->regs;
+
+ /* For Arm */
+ regs->ctrl_sts_reg = base + 0x20104;
+ regs->rstoutn_mask_reg = base + 0x20108;
+ regs->sys_soft_rst_reg = base + 0x2010C;
+ regs->main_int_cause_reg = base + 0x20200;
+ regs->enpointa_mask_reg = base + 0x2020C;
+ regs->rstoutn_en_reg = base + 0xF1400;
+ /* For Doorbell */
+ regs->pciea_to_arm_drbl_reg = base + 0x20400;
+ regs->arm_to_pciea_drbl_reg = base + 0x20408;
+ regs->arm_to_pciea_mask_reg = base + 0x2040C;
+ regs->pciea_to_arm_msg0 = base + 0x20430;
+ regs->pciea_to_arm_msg1 = base + 0x20434;
+ regs->arm_to_pciea_msg0 = base + 0x20438;
+ regs->arm_to_pciea_msg1 = base + 0x2043C;
+
+ /* For Message Unit */
+
+ regs->inb_aval_count_basel = base + 0x508;
+ regs->inb_aval_count_baseh = base + 0x50C;
+ regs->inb_write_pointer = base + 0x518;
+ regs->inb_read_pointer = base + 0x51C;
+ regs->outb_coal_cfg = base + 0x568;
+ regs->outb_copy_basel = base + 0x5B0;
+ regs->outb_copy_baseh = base + 0x5B4;
+ regs->outb_copy_pointer = base + 0x544;
+ regs->outb_read_pointer = base + 0x548;
+ regs->outb_isr_cause = base + 0x560;
+ regs->outb_coal_cfg = base + 0x568;
+ /* Bit setting for HW */
+ regs->int_comaout = 1 << 8;
+ regs->int_comaerr = 1 << 6;
+ regs->int_dl_cpu2pciea = 1 << 1;
+ regs->cl_pointer_toggle = 1 << 12;
+ regs->clic_irq = 1 << 1;
+ regs->clic_in_err = 1 << 8;
+ regs->clic_out_err = 1 << 12;
+ regs->cl_slot_num_mask = 0xFFF;
+ regs->int_drbl_int_mask = 0x3FFFFFFF;
+ regs->int_mu = regs->int_dl_cpu2pciea | regs->int_comaout |
+ regs->int_comaerr;
+ break;
+ case PCI_DEVICE_ID_MARVELL_MV9580:
+ mhba->mmio = mhba->base_addr[2];
+ base = mhba->mmio;
+ if (!mhba->regs) {
+ mhba->regs = kzalloc(sizeof(*regs), GFP_KERNEL);
+ if (mhba->regs == NULL)
+ return -ENOMEM;
+ }
+ regs = mhba->regs;
+ /* For Arm */
+ regs->ctrl_sts_reg = base + 0x20104;
+ regs->rstoutn_mask_reg = base + 0x1010C;
+ regs->sys_soft_rst_reg = base + 0x10108;
+ regs->main_int_cause_reg = base + 0x10200;
+ regs->enpointa_mask_reg = base + 0x1020C;
+ regs->rstoutn_en_reg = base + 0xF1400;
+
+ /* For Doorbell */
+ regs->pciea_to_arm_drbl_reg = base + 0x10460;
+ regs->arm_to_pciea_drbl_reg = base + 0x10480;
+ regs->arm_to_pciea_mask_reg = base + 0x10484;
+ regs->pciea_to_arm_msg0 = base + 0x10400;
+ regs->pciea_to_arm_msg1 = base + 0x10404;
+ regs->arm_to_pciea_msg0 = base + 0x10420;
+ regs->arm_to_pciea_msg1 = base + 0x10424;
+
+ /* For reset*/
+ regs->reset_request = base + 0x10108;
+ regs->reset_enable = base + 0x1010c;
+
+ /* For Message Unit */
+ regs->inb_aval_count_basel = base + 0x4008;
+ regs->inb_aval_count_baseh = base + 0x400C;
+ regs->inb_write_pointer = base + 0x4018;
+ regs->inb_read_pointer = base + 0x401C;
+ regs->outb_copy_basel = base + 0x4058;
+ regs->outb_copy_baseh = base + 0x405C;
+ regs->outb_copy_pointer = base + 0x406C;
+ regs->outb_read_pointer = base + 0x4070;
+ regs->outb_coal_cfg = base + 0x4080;
+ regs->outb_isr_cause = base + 0x4088;
+ /* Bit setting for HW */
+ regs->int_comaout = 1 << 4;
+ regs->int_dl_cpu2pciea = 1 << 12;
+ regs->int_comaerr = 1 << 29;
+ regs->cl_pointer_toggle = 1 << 14;
+ regs->cl_slot_num_mask = 0x3FFF;
+ regs->clic_irq = 1 << 0;
+ regs->clic_out_err = 1 << 1;
+ regs->int_drbl_int_mask = 0x3FFFFFFF;
+ regs->int_mu = regs->int_dl_cpu2pciea | regs->int_comaout;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * mvumi_init_fw - Initializes the FW
+ * @mhba: Adapter soft state
+ *
+ * This is the main function for initializing firmware.
+ */
+static int mvumi_init_fw(struct mvumi_hba *mhba)
+{
+ int ret = 0;
+
+ if (pci_request_regions(mhba->pdev, MV_DRIVER_NAME)) {
+ dev_err(&mhba->pdev->dev, "IO memory region busy!\n");
+ return -EBUSY;
+ }
+ ret = mvumi_map_pci_addr(mhba->pdev, mhba->base_addr);
+ if (ret)
+ goto fail_ioremap;
+
+ switch (mhba->pdev->device) {
+ case PCI_DEVICE_ID_MARVELL_MV9143:
+ mhba->instancet = &mvumi_instance_9143;
+ mhba->io_seq = 0;
+ mhba->max_sge = MVUMI_MAX_SG_ENTRY;
+ mhba->request_id_enabled = 1;
+ break;
+ case PCI_DEVICE_ID_MARVELL_MV9580:
+ mhba->instancet = &mvumi_instance_9580;
+ mhba->io_seq = 0;
+ mhba->max_sge = MVUMI_MAX_SG_ENTRY;
+ break;
+ default:
+ dev_err(&mhba->pdev->dev, "device 0x%x not supported!\n",
+ mhba->pdev->device);
+ mhba->instancet = NULL;
+ ret = -EINVAL;
+ goto fail_alloc_mem;
+ }
+ dev_dbg(&mhba->pdev->dev, "device id : %04X is found.\n",
+ mhba->pdev->device);
+ ret = mvumi_cfg_hw_reg(mhba);
+ if (ret) {
+ dev_err(&mhba->pdev->dev,
+ "failed to allocate memory for reg\n");
+ ret = -ENOMEM;
+ goto fail_alloc_mem;
+ }
+ mhba->handshake_page = dma_alloc_coherent(&mhba->pdev->dev,
+ HSP_MAX_SIZE, &mhba->handshake_page_phys, GFP_KERNEL);
+ if (!mhba->handshake_page) {
+ dev_err(&mhba->pdev->dev,
+ "failed to allocate memory for handshake\n");
+ ret = -ENOMEM;
+ goto fail_alloc_page;
+ }
+
+ if (mvumi_start(mhba)) {
+ ret = -EINVAL;
+ goto fail_ready_state;
+ }
+ ret = mvumi_alloc_cmds(mhba);
+ if (ret)
+ goto fail_ready_state;
+
+ return 0;
+
+fail_ready_state:
+ mvumi_release_mem_resource(mhba);
+ dma_free_coherent(&mhba->pdev->dev, HSP_MAX_SIZE,
+ mhba->handshake_page, mhba->handshake_page_phys);
+fail_alloc_page:
+ kfree(mhba->regs);
+fail_alloc_mem:
+ mvumi_unmap_pci_addr(mhba->pdev, mhba->base_addr);
+fail_ioremap:
+ pci_release_regions(mhba->pdev);
+
+ return ret;
+}
+
+/**
+ * mvumi_io_attach - Attaches this driver to SCSI mid-layer
+ * @mhba: Adapter soft state
+ */
+static int mvumi_io_attach(struct mvumi_hba *mhba)
+{
+ struct Scsi_Host *host = mhba->shost;
+ struct scsi_device *sdev = NULL;
+ int ret;
+ unsigned int max_sg = (mhba->ib_max_size -
+ sizeof(struct mvumi_msg_frame)) / sizeof(struct mvumi_sgl);
+
+ host->irq = mhba->pdev->irq;
+ host->unique_id = mhba->unique_id;
+ host->can_queue = (mhba->max_io - 1) ? (mhba->max_io - 1) : 1;
+ host->sg_tablesize = mhba->max_sge > max_sg ? max_sg : mhba->max_sge;
+ host->max_sectors = mhba->max_transfer_size / 512;
+ host->cmd_per_lun = (mhba->max_io - 1) ? (mhba->max_io - 1) : 1;
+ host->max_id = mhba->max_target_id;
+ host->max_cmd_len = MAX_COMMAND_SIZE;
+
+ ret = scsi_add_host(host, &mhba->pdev->dev);
+ if (ret) {
+ dev_err(&mhba->pdev->dev, "scsi_add_host failed\n");
+ return ret;
+ }
+ mhba->fw_flag |= MVUMI_FW_ATTACH;
+
+ mutex_lock(&mhba->sas_discovery_mutex);
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580)
+ ret = scsi_add_device(host, 0, mhba->max_target_id - 1, 0);
+ else
+ ret = 0;
+ if (ret) {
+ dev_err(&mhba->pdev->dev, "add virtual device failed\n");
+ mutex_unlock(&mhba->sas_discovery_mutex);
+ goto fail_add_device;
+ }
+
+ mhba->dm_thread = kthread_create(mvumi_rescan_bus,
+ mhba, "mvumi_scanthread");
+ if (IS_ERR(mhba->dm_thread)) {
+ dev_err(&mhba->pdev->dev,
+ "failed to create device scan thread\n");
+ ret = PTR_ERR(mhba->dm_thread);
+ mutex_unlock(&mhba->sas_discovery_mutex);
+ goto fail_create_thread;
+ }
+ atomic_set(&mhba->pnp_count, 1);
+ wake_up_process(mhba->dm_thread);
+
+ mutex_unlock(&mhba->sas_discovery_mutex);
+ return 0;
+
+fail_create_thread:
+ if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580)
+ sdev = scsi_device_lookup(mhba->shost, 0,
+ mhba->max_target_id - 1, 0);
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ }
+fail_add_device:
+ scsi_remove_host(mhba->shost);
+ return ret;
+}
+
+/**
+ * mvumi_probe_one - PCI hotplug entry point
+ * @pdev: PCI device structure
+ * @id: PCI ids of supported hotplugged adapter
+ */
+static int mvumi_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct Scsi_Host *host;
+ struct mvumi_hba *mhba;
+ int ret;
+
+ dev_dbg(&pdev->dev, " %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ ret = mvumi_pci_set_master(pdev);
+ if (ret)
+ goto fail_set_dma_mask;
+
+ host = scsi_host_alloc(&mvumi_template, sizeof(*mhba));
+ if (!host) {
+ dev_err(&pdev->dev, "scsi_host_alloc failed\n");
+ ret = -ENOMEM;
+ goto fail_alloc_instance;
+ }
+ mhba = shost_priv(host);
+
+ INIT_LIST_HEAD(&mhba->cmd_pool);
+ INIT_LIST_HEAD(&mhba->ob_data_list);
+ INIT_LIST_HEAD(&mhba->free_ob_list);
+ INIT_LIST_HEAD(&mhba->res_list);
+ INIT_LIST_HEAD(&mhba->waiting_req_list);
+ mutex_init(&mhba->device_lock);
+ INIT_LIST_HEAD(&mhba->mhba_dev_list);
+ INIT_LIST_HEAD(&mhba->shost_dev_list);
+ atomic_set(&mhba->fw_outstanding, 0);
+ init_waitqueue_head(&mhba->int_cmd_wait_q);
+ mutex_init(&mhba->sas_discovery_mutex);
+
+ mhba->pdev = pdev;
+ mhba->shost = host;
+ mhba->unique_id = pci_dev_id(pdev);
+
+ ret = mvumi_init_fw(mhba);
+ if (ret)
+ goto fail_init_fw;
+
+ ret = request_irq(mhba->pdev->irq, mvumi_isr_handler, IRQF_SHARED,
+ "mvumi", mhba);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register IRQ\n");
+ goto fail_init_irq;
+ }
+
+ mhba->instancet->enable_intr(mhba);
+ pci_set_drvdata(pdev, mhba);
+
+ ret = mvumi_io_attach(mhba);
+ if (ret)
+ goto fail_io_attach;
+
+ mvumi_backup_bar_addr(mhba);
+ dev_dbg(&pdev->dev, "probe mvumi driver successfully.\n");
+
+ return 0;
+
+fail_io_attach:
+ mhba->instancet->disable_intr(mhba);
+ free_irq(mhba->pdev->irq, mhba);
+fail_init_irq:
+ mvumi_release_fw(mhba);
+fail_init_fw:
+ scsi_host_put(host);
+
+fail_alloc_instance:
+fail_set_dma_mask:
+ pci_disable_device(pdev);
+
+ return ret;
+}
+
+static void mvumi_detach_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host;
+ struct mvumi_hba *mhba;
+
+ mhba = pci_get_drvdata(pdev);
+ if (mhba->dm_thread) {
+ kthread_stop(mhba->dm_thread);
+ mhba->dm_thread = NULL;
+ }
+
+ mvumi_detach_devices(mhba);
+ host = mhba->shost;
+ scsi_remove_host(mhba->shost);
+ mvumi_flush_cache(mhba);
+
+ mhba->instancet->disable_intr(mhba);
+ free_irq(mhba->pdev->irq, mhba);
+ mvumi_release_fw(mhba);
+ scsi_host_put(host);
+ pci_disable_device(pdev);
+ dev_dbg(&pdev->dev, "driver is removed!\n");
+}
+
+/**
+ * mvumi_shutdown - Shutdown entry point
+ * @pdev: PCI device structure
+ */
+static void mvumi_shutdown(struct pci_dev *pdev)
+{
+ struct mvumi_hba *mhba = pci_get_drvdata(pdev);
+
+ mvumi_flush_cache(mhba);
+}
+
+static int __maybe_unused mvumi_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct mvumi_hba *mhba = pci_get_drvdata(pdev);
+
+ mvumi_flush_cache(mhba);
+
+ mhba->instancet->disable_intr(mhba);
+ mvumi_unmap_pci_addr(pdev, mhba->base_addr);
+
+ return 0;
+}
+
+static int __maybe_unused mvumi_resume(struct device *dev)
+{
+ int ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct mvumi_hba *mhba = pci_get_drvdata(pdev);
+
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto fail;
+ ret = mvumi_map_pci_addr(mhba->pdev, mhba->base_addr);
+ if (ret)
+ goto release_regions;
+
+ if (mvumi_cfg_hw_reg(mhba)) {
+ ret = -EINVAL;
+ goto unmap_pci_addr;
+ }
+
+ mhba->mmio = mhba->base_addr[0];
+ mvumi_reset(mhba);
+
+ if (mvumi_start(mhba)) {
+ ret = -EINVAL;
+ goto unmap_pci_addr;
+ }
+
+ mhba->instancet->enable_intr(mhba);
+
+ return 0;
+
+unmap_pci_addr:
+ mvumi_unmap_pci_addr(pdev, mhba->base_addr);
+release_regions:
+ pci_release_regions(pdev);
+fail:
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(mvumi_pm_ops, mvumi_suspend, mvumi_resume);
+
+static struct pci_driver mvumi_pci_driver = {
+
+ .name = MV_DRIVER_NAME,
+ .id_table = mvumi_pci_table,
+ .probe = mvumi_probe_one,
+ .remove = mvumi_detach_one,
+ .shutdown = mvumi_shutdown,
+ .driver.pm = &mvumi_pm_ops,
+};
+
+module_pci_driver(mvumi_pci_driver);
diff --git a/drivers/scsi/mvumi.h b/drivers/scsi/mvumi.h
new file mode 100644
index 000000000000..1306a4abf19a
--- /dev/null
+++ b/drivers/scsi/mvumi.h
@@ -0,0 +1,566 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Marvell UMI head file
+ *
+ * Copyright 2011 Marvell. <jyli@marvell.com>
+ */
+
+#ifndef MVUMI_H
+#define MVUMI_H
+
+#define MAX_BASE_ADDRESS 6
+
+#define VER_MAJOR 1
+#define VER_MINOR 1
+#define VER_OEM 0
+#define VER_BUILD 1500
+
+#define MV_DRIVER_NAME "mvumi"
+#define PCI_DEVICE_ID_MARVELL_MV9143 0x9143
+#define PCI_DEVICE_ID_MARVELL_MV9580 0x9580
+
+#define MVUMI_INTERNAL_CMD_WAIT_TIME 45
+#define MVUMI_INQUIRY_LENGTH 44
+#define MVUMI_INQUIRY_UUID_OFF 36
+#define MVUMI_INQUIRY_UUID_LEN 8
+
+#define IS_DMA64 (sizeof(dma_addr_t) == 8)
+
+enum mvumi_qc_result {
+ MV_QUEUE_COMMAND_RESULT_SENT = 0,
+ MV_QUEUE_COMMAND_RESULT_NO_RESOURCE,
+};
+
+struct mvumi_hw_regs {
+ /* For CPU */
+ void *main_int_cause_reg;
+ void *enpointa_mask_reg;
+ void *enpointb_mask_reg;
+ void *rstoutn_en_reg;
+ void *ctrl_sts_reg;
+ void *rstoutn_mask_reg;
+ void *sys_soft_rst_reg;
+
+ /* For Doorbell */
+ void *pciea_to_arm_drbl_reg;
+ void *arm_to_pciea_drbl_reg;
+ void *arm_to_pciea_mask_reg;
+ void *pciea_to_arm_msg0;
+ void *pciea_to_arm_msg1;
+ void *arm_to_pciea_msg0;
+ void *arm_to_pciea_msg1;
+
+ /* reset register */
+ void *reset_request;
+ void *reset_enable;
+
+ /* For Message Unit */
+ void *inb_list_basel;
+ void *inb_list_baseh;
+ void *inb_aval_count_basel;
+ void *inb_aval_count_baseh;
+ void *inb_write_pointer;
+ void *inb_read_pointer;
+ void *outb_list_basel;
+ void *outb_list_baseh;
+ void *outb_copy_basel;
+ void *outb_copy_baseh;
+ void *outb_copy_pointer;
+ void *outb_read_pointer;
+ void *inb_isr_cause;
+ void *outb_isr_cause;
+ void *outb_coal_cfg;
+ void *outb_coal_timeout;
+
+ /* Bit setting for HW */
+ u32 int_comaout;
+ u32 int_comaerr;
+ u32 int_dl_cpu2pciea;
+ u32 int_mu;
+ u32 int_drbl_int_mask;
+ u32 int_main_int_mask;
+ u32 cl_pointer_toggle;
+ u32 cl_slot_num_mask;
+ u32 clic_irq;
+ u32 clic_in_err;
+ u32 clic_out_err;
+};
+
+struct mvumi_dyn_list_entry {
+ u32 src_low_addr;
+ u32 src_high_addr;
+ u32 if_length;
+ u32 reserve;
+};
+
+#define SCSI_CMD_MARVELL_SPECIFIC 0xE1
+#define CDB_CORE_MODULE 0x1
+#define CDB_CORE_SHUTDOWN 0xB
+
+enum {
+ DRBL_HANDSHAKE = 1 << 0,
+ DRBL_SOFT_RESET = 1 << 1,
+ DRBL_BUS_CHANGE = 1 << 2,
+ DRBL_EVENT_NOTIFY = 1 << 3,
+ DRBL_MU_RESET = 1 << 4,
+ DRBL_HANDSHAKE_ISR = DRBL_HANDSHAKE,
+
+ /*
+ * Command flag is the flag for the CDB command itself
+ */
+ /* 1-non data; 0-data command */
+ CMD_FLAG_NON_DATA = 1 << 0,
+ CMD_FLAG_DMA = 1 << 1,
+ CMD_FLAG_PIO = 1 << 2,
+ /* 1-host read data */
+ CMD_FLAG_DATA_IN = 1 << 3,
+ /* 1-host write data */
+ CMD_FLAG_DATA_OUT = 1 << 4,
+ CMD_FLAG_PRDT_IN_HOST = 1 << 5,
+};
+
+#define APICDB0_EVENT 0xF4
+#define APICDB1_EVENT_GETEVENT 0
+#define APICDB1_HOST_GETEVENT 1
+#define MAX_EVENTS_RETURNED 6
+
+#define DEVICE_OFFLINE 0
+#define DEVICE_ONLINE 1
+
+struct mvumi_hotplug_event {
+ u16 size;
+ u8 dummy[2];
+ u8 bitmap[];
+};
+
+struct mvumi_driver_event {
+ u32 time_stamp;
+ u32 sequence_no;
+ u32 event_id;
+ u8 severity;
+ u8 param_count;
+ u16 device_id;
+ u32 params[4];
+ u8 sense_data_length;
+ u8 Reserved1;
+ u8 sense_data[30];
+};
+
+struct mvumi_event_req {
+ unsigned char count;
+ unsigned char reserved[3];
+ struct mvumi_driver_event events[MAX_EVENTS_RETURNED];
+};
+
+struct mvumi_events_wq {
+ struct work_struct work_q;
+ struct mvumi_hba *mhba;
+ unsigned int event;
+ void *param;
+};
+
+#define HS_CAPABILITY_SUPPORT_COMPACT_SG (1U << 4)
+#define HS_CAPABILITY_SUPPORT_PRD_HOST (1U << 5)
+#define HS_CAPABILITY_SUPPORT_DYN_SRC (1U << 6)
+#define HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF (1U << 14)
+
+#define MVUMI_MAX_SG_ENTRY 32
+#define SGD_EOT (1L << 27)
+#define SGD_EOT_CP (1L << 22)
+
+struct mvumi_sgl {
+ u32 baseaddr_l;
+ u32 baseaddr_h;
+ u32 flags;
+ u32 size;
+};
+struct mvumi_compact_sgl {
+ u32 baseaddr_l;
+ u32 baseaddr_h;
+ u32 flags;
+};
+
+#define GET_COMPACT_SGD_SIZE(sgd) \
+ ((((struct mvumi_compact_sgl *)(sgd))->flags) & 0x3FFFFFL)
+
+#define SET_COMPACT_SGD_SIZE(sgd, sz) do { \
+ (((struct mvumi_compact_sgl *)(sgd))->flags) &= ~0x3FFFFFL; \
+ (((struct mvumi_compact_sgl *)(sgd))->flags) |= (sz); \
+} while (0)
+#define sgd_getsz(_mhba, sgd, sz) do { \
+ if (_mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG) \
+ (sz) = GET_COMPACT_SGD_SIZE(sgd); \
+ else \
+ (sz) = (sgd)->size; \
+} while (0)
+
+#define sgd_setsz(_mhba, sgd, sz) do { \
+ if (_mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG) \
+ SET_COMPACT_SGD_SIZE(sgd, sz); \
+ else \
+ (sgd)->size = (sz); \
+} while (0)
+
+#define sgd_inc(_mhba, sgd) do { \
+ if (_mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG) \
+ sgd = (struct mvumi_sgl *)(((unsigned char *) (sgd)) + 12); \
+ else \
+ sgd = (struct mvumi_sgl *)(((unsigned char *) (sgd)) + 16); \
+} while (0)
+
+struct mvumi_res {
+ struct list_head entry;
+ dma_addr_t bus_addr;
+ void *virt_addr;
+ unsigned int size;
+ unsigned short type; /* enum Resource_Type */
+};
+
+/* Resource type */
+enum resource_type {
+ RESOURCE_CACHED_MEMORY = 0,
+ RESOURCE_UNCACHED_MEMORY
+};
+
+struct mvumi_sense_data {
+ u8 error_code:7;
+ u8 valid:1;
+ u8 segment_number;
+ u8 sense_key:4;
+ u8 reserved:1;
+ u8 incorrect_length:1;
+ u8 end_of_media:1;
+ u8 file_mark:1;
+ u8 information[4];
+ u8 additional_sense_length;
+ u8 command_specific_information[4];
+ u8 additional_sense_code;
+ u8 additional_sense_code_qualifier;
+ u8 field_replaceable_unit_code;
+ u8 sense_key_specific[3];
+};
+
+/* Request initiator must set the status to REQ_STATUS_PENDING. */
+#define REQ_STATUS_PENDING 0x80
+
+struct mvumi_cmd {
+ struct list_head queue_pointer;
+ struct mvumi_msg_frame *frame;
+ dma_addr_t frame_phys;
+ struct scsi_cmnd *scmd;
+ atomic_t sync_cmd;
+ void *data_buf;
+ unsigned short request_id;
+ unsigned char cmd_status;
+};
+
+struct mvumi_cmd_priv {
+ struct mvumi_cmd *cmd_priv;
+};
+
+static inline struct mvumi_cmd_priv *mvumi_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
+/*
+ * the function type of the in bound frame
+ */
+#define CL_FUN_SCSI_CMD 0x1
+
+struct mvumi_msg_frame {
+ u16 device_id;
+ u16 tag;
+ u8 cmd_flag;
+ u8 req_function;
+ u8 cdb_length;
+ u8 sg_counts;
+ u32 data_transfer_length;
+ u16 request_id;
+ u16 reserved1;
+ u8 cdb[MAX_COMMAND_SIZE];
+ u32 payload[];
+};
+
+/*
+ * the respond flag for data_payload of the out bound frame
+ */
+#define CL_RSP_FLAG_NODATA 0x0
+#define CL_RSP_FLAG_SENSEDATA 0x1
+
+struct mvumi_rsp_frame {
+ u16 device_id;
+ u16 tag;
+ u8 req_status;
+ u8 rsp_flag; /* Indicates the type of Data_Payload.*/
+ u16 request_id;
+ u32 payload[];
+};
+
+struct mvumi_ob_data {
+ struct list_head list;
+ unsigned char data[];
+};
+
+struct version_info {
+ u32 ver_major;
+ u32 ver_minor;
+ u32 ver_oem;
+ u32 ver_build;
+};
+
+#define FW_MAX_DELAY 30
+#define MVUMI_FW_BUSY (1U << 0)
+#define MVUMI_FW_ATTACH (1U << 1)
+#define MVUMI_FW_ALLOC (1U << 2)
+
+/*
+ * State is the state of the MU
+ */
+#define FW_STATE_IDLE 0
+#define FW_STATE_STARTING 1
+#define FW_STATE_HANDSHAKING 2
+#define FW_STATE_STARTED 3
+#define FW_STATE_ABORT 4
+
+#define HANDSHAKE_SIGNATURE 0x5A5A5A5AL
+#define HANDSHAKE_READYSTATE 0x55AA5AA5L
+#define HANDSHAKE_DONESTATE 0x55AAA55AL
+
+/* HandShake Status definition */
+#define HS_STATUS_OK 1
+#define HS_STATUS_ERR 2
+#define HS_STATUS_INVALID 3
+
+/* HandShake State/Cmd definition */
+#define HS_S_START 1
+#define HS_S_RESET 2
+#define HS_S_PAGE_ADDR 3
+#define HS_S_QUERY_PAGE 4
+#define HS_S_SEND_PAGE 5
+#define HS_S_END 6
+#define HS_S_ABORT 7
+#define HS_PAGE_VERIFY_SIZE 128
+
+#define HS_GET_STATE(a) (a & 0xFFFF)
+#define HS_GET_STATUS(a) ((a & 0xFFFF0000) >> 16)
+#define HS_SET_STATE(a, b) (a |= (b & 0xFFFF))
+#define HS_SET_STATUS(a, b) (a |= ((b & 0xFFFF) << 16))
+
+/* handshake frame */
+struct mvumi_hs_frame {
+ u16 size;
+ /* host information */
+ u8 host_type;
+ u8 reserved_1[1];
+ struct version_info host_ver; /* bios or driver version */
+
+ /* controller information */
+ u32 system_io_bus;
+ u32 slot_number;
+ u32 intr_level;
+ u32 intr_vector;
+
+ /* communication list configuration */
+ u32 ib_baseaddr_l;
+ u32 ib_baseaddr_h;
+ u32 ob_baseaddr_l;
+ u32 ob_baseaddr_h;
+
+ u8 ib_entry_size;
+ u8 ob_entry_size;
+ u8 ob_depth;
+ u8 ib_depth;
+
+ /* system time */
+ u64 seconds_since1970;
+};
+
+struct mvumi_hs_header {
+ u8 page_code;
+ u8 checksum;
+ u16 frame_length;
+ u32 frame_content[];
+};
+
+/*
+ * the page code type of the handshake header
+ */
+#define HS_PAGE_FIRM_CAP 0x1
+#define HS_PAGE_HOST_INFO 0x2
+#define HS_PAGE_FIRM_CTL 0x3
+#define HS_PAGE_CL_INFO 0x4
+#define HS_PAGE_TOTAL 0x5
+
+#define HSP_SIZE(i) sizeof(struct mvumi_hs_page##i)
+
+#define HSP_MAX_SIZE ({ \
+ int size, m1, m2; \
+ m1 = max(HSP_SIZE(1), HSP_SIZE(3)); \
+ m2 = max(HSP_SIZE(2), HSP_SIZE(4)); \
+ size = max(m1, m2); \
+ size; \
+})
+
+/* The format of the page code for Firmware capability */
+struct mvumi_hs_page1 {
+ u8 pagecode;
+ u8 checksum;
+ u16 frame_length;
+
+ u16 number_of_ports;
+ u16 max_devices_support;
+ u16 max_io_support;
+ u16 umi_ver;
+ u32 max_transfer_size;
+ struct version_info fw_ver;
+ u8 cl_in_max_entry_size;
+ u8 cl_out_max_entry_size;
+ u8 cl_inout_list_depth;
+ u8 total_pages;
+ u16 capability;
+ u16 reserved1;
+};
+
+/* The format of the page code for Host information */
+struct mvumi_hs_page2 {
+ u8 pagecode;
+ u8 checksum;
+ u16 frame_length;
+
+ u8 host_type;
+ u8 host_cap;
+ u8 reserved[2];
+ struct version_info host_ver;
+ u32 system_io_bus;
+ u32 slot_number;
+ u32 intr_level;
+ u32 intr_vector;
+ u64 seconds_since1970;
+};
+
+/* The format of the page code for firmware control */
+struct mvumi_hs_page3 {
+ u8 pagecode;
+ u8 checksum;
+ u16 frame_length;
+ u16 control;
+ u8 reserved[2];
+ u32 host_bufferaddr_l;
+ u32 host_bufferaddr_h;
+ u32 host_eventaddr_l;
+ u32 host_eventaddr_h;
+};
+
+struct mvumi_hs_page4 {
+ u8 pagecode;
+ u8 checksum;
+ u16 frame_length;
+ u32 ib_baseaddr_l;
+ u32 ib_baseaddr_h;
+ u32 ob_baseaddr_l;
+ u32 ob_baseaddr_h;
+ u8 ib_entry_size;
+ u8 ob_entry_size;
+ u8 ob_depth;
+ u8 ib_depth;
+};
+
+struct mvumi_tag {
+ unsigned short *stack;
+ unsigned short top;
+ unsigned short size;
+};
+
+struct mvumi_device {
+ struct list_head list;
+ struct scsi_device *sdev;
+ u64 wwid;
+ u8 dev_type;
+ int id;
+};
+
+struct mvumi_hba {
+ void *base_addr[MAX_BASE_ADDRESS];
+ u32 pci_base[MAX_BASE_ADDRESS];
+ void *mmio;
+ struct list_head cmd_pool;
+ struct Scsi_Host *shost;
+ wait_queue_head_t int_cmd_wait_q;
+ struct pci_dev *pdev;
+ unsigned int unique_id;
+ atomic_t fw_outstanding;
+ struct mvumi_instance_template *instancet;
+
+ void *ib_list;
+ dma_addr_t ib_list_phys;
+
+ void *ib_frame;
+ dma_addr_t ib_frame_phys;
+
+ void *ob_list;
+ dma_addr_t ob_list_phys;
+
+ void *ib_shadow;
+ dma_addr_t ib_shadow_phys;
+
+ void *ob_shadow;
+ dma_addr_t ob_shadow_phys;
+
+ void *handshake_page;
+ dma_addr_t handshake_page_phys;
+
+ unsigned int global_isr;
+ unsigned int isr_status;
+
+ unsigned short max_sge;
+ unsigned short max_target_id;
+ unsigned char *target_map;
+ unsigned int max_io;
+ unsigned int list_num_io;
+ unsigned int ib_max_size;
+ unsigned int ob_max_size;
+ unsigned int ib_max_size_setting;
+ unsigned int ob_max_size_setting;
+ unsigned int max_transfer_size;
+ unsigned char hba_total_pages;
+ unsigned char fw_flag;
+ unsigned char request_id_enabled;
+ unsigned char eot_flag;
+ unsigned short hba_capability;
+ unsigned short io_seq;
+
+ unsigned int ib_cur_slot;
+ unsigned int ob_cur_slot;
+ unsigned int fw_state;
+ struct mutex sas_discovery_mutex;
+
+ struct list_head ob_data_list;
+ struct list_head free_ob_list;
+ struct list_head res_list;
+ struct list_head waiting_req_list;
+
+ struct mvumi_tag tag_pool;
+ struct mvumi_cmd **tag_cmd;
+ struct mvumi_hw_regs *regs;
+ struct mutex device_lock;
+ struct list_head mhba_dev_list;
+ struct list_head shost_dev_list;
+ struct task_struct *dm_thread;
+ atomic_t pnp_count;
+};
+
+struct mvumi_instance_template {
+ void (*fire_cmd) (struct mvumi_hba *, struct mvumi_cmd *);
+ void (*enable_intr) (struct mvumi_hba *);
+ void (*disable_intr) (struct mvumi_hba *);
+ int (*clear_intr) (void *);
+ unsigned int (*read_fw_status_reg) (struct mvumi_hba *);
+ unsigned int (*check_ib_list) (struct mvumi_hba *);
+ int (*check_ob_list) (struct mvumi_hba *, unsigned int *,
+ unsigned int *);
+ int (*reset_host) (struct mvumi_hba *);
+};
+
+extern struct timezone sys_tz;
+#endif
diff --git a/drivers/scsi/myrb.c b/drivers/scsi/myrb.c
new file mode 100644
index 000000000000..b8453c0333dc
--- /dev/null
+++ b/drivers/scsi/myrb.c
@@ -0,0 +1,3560 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
+ *
+ * Copyright 2017 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com>
+ *
+ * Based on the original DAC960 driver,
+ * Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com>
+ * Portions Copyright 2002 by Mylex (An IBM Business Unit)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/raid_class.h>
+#include <linux/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include "myrb.h"
+
+static struct raid_template *myrb_raid_template;
+
+static void myrb_monitor(struct work_struct *work);
+static inline void myrb_translate_devstate(void *DeviceState);
+
+static inline int myrb_logical_channel(struct Scsi_Host *shost)
+{
+ return shost->max_channel - 1;
+}
+
+static struct myrb_devstate_name_entry {
+ enum myrb_devstate state;
+ const char *name;
+} myrb_devstate_name_list[] = {
+ { MYRB_DEVICE_DEAD, "Dead" },
+ { MYRB_DEVICE_WO, "WriteOnly" },
+ { MYRB_DEVICE_ONLINE, "Online" },
+ { MYRB_DEVICE_CRITICAL, "Critical" },
+ { MYRB_DEVICE_STANDBY, "Standby" },
+ { MYRB_DEVICE_OFFLINE, "Offline" },
+};
+
+static const char *myrb_devstate_name(enum myrb_devstate state)
+{
+ struct myrb_devstate_name_entry *entry = myrb_devstate_name_list;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(myrb_devstate_name_list); i++) {
+ if (entry[i].state == state)
+ return entry[i].name;
+ }
+ return "Unknown";
+}
+
+static struct myrb_raidlevel_name_entry {
+ enum myrb_raidlevel level;
+ const char *name;
+} myrb_raidlevel_name_list[] = {
+ { MYRB_RAID_LEVEL0, "RAID0" },
+ { MYRB_RAID_LEVEL1, "RAID1" },
+ { MYRB_RAID_LEVEL3, "RAID3" },
+ { MYRB_RAID_LEVEL5, "RAID5" },
+ { MYRB_RAID_LEVEL6, "RAID6" },
+ { MYRB_RAID_JBOD, "JBOD" },
+};
+
+static const char *myrb_raidlevel_name(enum myrb_raidlevel level)
+{
+ struct myrb_raidlevel_name_entry *entry = myrb_raidlevel_name_list;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(myrb_raidlevel_name_list); i++) {
+ if (entry[i].level == level)
+ return entry[i].name;
+ }
+ return NULL;
+}
+
+/*
+ * myrb_create_mempools - allocates auxiliary data structures
+ *
+ * Return: true on success, false otherwise.
+ */
+static bool myrb_create_mempools(struct pci_dev *pdev, struct myrb_hba *cb)
+{
+ size_t elem_size, elem_align;
+
+ elem_align = sizeof(struct myrb_sge);
+ elem_size = cb->host->sg_tablesize * elem_align;
+ cb->sg_pool = dma_pool_create("myrb_sg", &pdev->dev,
+ elem_size, elem_align, 0);
+ if (cb->sg_pool == NULL) {
+ shost_printk(KERN_ERR, cb->host,
+ "Failed to allocate SG pool\n");
+ return false;
+ }
+
+ cb->dcdb_pool = dma_pool_create("myrb_dcdb", &pdev->dev,
+ sizeof(struct myrb_dcdb),
+ sizeof(unsigned int), 0);
+ if (!cb->dcdb_pool) {
+ dma_pool_destroy(cb->sg_pool);
+ cb->sg_pool = NULL;
+ shost_printk(KERN_ERR, cb->host,
+ "Failed to allocate DCDB pool\n");
+ return false;
+ }
+
+ cb->work_q = alloc_ordered_workqueue("myrb_wq_%d", WQ_MEM_RECLAIM,
+ cb->host->host_no);
+ if (!cb->work_q) {
+ dma_pool_destroy(cb->dcdb_pool);
+ cb->dcdb_pool = NULL;
+ dma_pool_destroy(cb->sg_pool);
+ cb->sg_pool = NULL;
+ shost_printk(KERN_ERR, cb->host,
+ "Failed to create workqueue\n");
+ return false;
+ }
+
+ /*
+ * Initialize the Monitoring Timer.
+ */
+ INIT_DELAYED_WORK(&cb->monitor_work, myrb_monitor);
+ queue_delayed_work(cb->work_q, &cb->monitor_work, 1);
+
+ return true;
+}
+
+/*
+ * myrb_destroy_mempools - tears down the memory pools for the controller
+ */
+static void myrb_destroy_mempools(struct myrb_hba *cb)
+{
+ cancel_delayed_work_sync(&cb->monitor_work);
+ destroy_workqueue(cb->work_q);
+
+ dma_pool_destroy(cb->sg_pool);
+ dma_pool_destroy(cb->dcdb_pool);
+}
+
+/*
+ * myrb_reset_cmd - reset command block
+ */
+static inline void myrb_reset_cmd(struct myrb_cmdblk *cmd_blk)
+{
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+
+ memset(mbox, 0, sizeof(union myrb_cmd_mbox));
+ cmd_blk->status = 0;
+}
+
+/*
+ * myrb_qcmd - queues command block for execution
+ */
+static void myrb_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk)
+{
+ void __iomem *base = cb->io_base;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ union myrb_cmd_mbox *next_mbox = cb->next_cmd_mbox;
+
+ cb->write_cmd_mbox(next_mbox, mbox);
+ if (cb->prev_cmd_mbox1->words[0] == 0 ||
+ cb->prev_cmd_mbox2->words[0] == 0)
+ cb->get_cmd_mbox(base);
+ cb->prev_cmd_mbox2 = cb->prev_cmd_mbox1;
+ cb->prev_cmd_mbox1 = next_mbox;
+ if (++next_mbox > cb->last_cmd_mbox)
+ next_mbox = cb->first_cmd_mbox;
+ cb->next_cmd_mbox = next_mbox;
+}
+
+/*
+ * myrb_exec_cmd - executes command block and waits for completion.
+ *
+ * Return: command status
+ */
+static unsigned short myrb_exec_cmd(struct myrb_hba *cb,
+ struct myrb_cmdblk *cmd_blk)
+{
+ DECLARE_COMPLETION_ONSTACK(cmpl);
+ unsigned long flags;
+
+ cmd_blk->completion = &cmpl;
+
+ spin_lock_irqsave(&cb->queue_lock, flags);
+ cb->qcmd(cb, cmd_blk);
+ spin_unlock_irqrestore(&cb->queue_lock, flags);
+
+ wait_for_completion(&cmpl);
+ return cmd_blk->status;
+}
+
+/*
+ * myrb_exec_type3 - executes a type 3 command and waits for completion.
+ *
+ * Return: command status
+ */
+static unsigned short myrb_exec_type3(struct myrb_hba *cb,
+ enum myrb_cmd_opcode op, dma_addr_t addr)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ unsigned short status;
+
+ mutex_lock(&cb->dcmd_mutex);
+ myrb_reset_cmd(cmd_blk);
+ mbox->type3.id = MYRB_DCMD_TAG;
+ mbox->type3.opcode = op;
+ mbox->type3.addr = addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ mutex_unlock(&cb->dcmd_mutex);
+ return status;
+}
+
+/*
+ * myrb_exec_type3D - executes a type 3D command and waits for completion.
+ *
+ * Return: command status
+ */
+static unsigned short myrb_exec_type3D(struct myrb_hba *cb,
+ enum myrb_cmd_opcode op, struct scsi_device *sdev,
+ struct myrb_pdev_state *pdev_info)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ unsigned short status;
+ dma_addr_t pdev_info_addr;
+
+ pdev_info_addr = dma_map_single(&cb->pdev->dev, pdev_info,
+ sizeof(struct myrb_pdev_state),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&cb->pdev->dev, pdev_info_addr))
+ return MYRB_STATUS_SUBSYS_FAILED;
+
+ mutex_lock(&cb->dcmd_mutex);
+ myrb_reset_cmd(cmd_blk);
+ mbox->type3D.id = MYRB_DCMD_TAG;
+ mbox->type3D.opcode = op;
+ mbox->type3D.channel = sdev->channel;
+ mbox->type3D.target = sdev->id;
+ mbox->type3D.addr = pdev_info_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ mutex_unlock(&cb->dcmd_mutex);
+ dma_unmap_single(&cb->pdev->dev, pdev_info_addr,
+ sizeof(struct myrb_pdev_state), DMA_FROM_DEVICE);
+ if (status == MYRB_STATUS_SUCCESS &&
+ mbox->type3D.opcode == MYRB_CMD_GET_DEVICE_STATE_OLD)
+ myrb_translate_devstate(pdev_info);
+
+ return status;
+}
+
+static char *myrb_event_msg[] = {
+ "killed because write recovery failed",
+ "killed because of SCSI bus reset failure",
+ "killed because of double check condition",
+ "killed because it was removed",
+ "killed because of gross error on SCSI chip",
+ "killed because of bad tag returned from drive",
+ "killed because of timeout on SCSI command",
+ "killed because of reset SCSI command issued from system",
+ "killed because busy or parity error count exceeded limit",
+ "killed because of 'kill drive' command from system",
+ "killed because of selection timeout",
+ "killed due to SCSI phase sequence error",
+ "killed due to unknown status",
+};
+
+/**
+ * myrb_get_event - get event log from HBA
+ * @cb: pointer to the hba structure
+ * @event: number of the event
+ *
+ * Execute a type 3E command and logs the event message
+ */
+static void myrb_get_event(struct myrb_hba *cb, unsigned int event)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ struct myrb_log_entry *ev_buf;
+ dma_addr_t ev_addr;
+ unsigned short status;
+
+ ev_buf = dma_alloc_coherent(&cb->pdev->dev,
+ sizeof(struct myrb_log_entry),
+ &ev_addr, GFP_KERNEL);
+ if (!ev_buf)
+ return;
+
+ myrb_reset_cmd(cmd_blk);
+ mbox->type3E.id = MYRB_MCMD_TAG;
+ mbox->type3E.opcode = MYRB_CMD_EVENT_LOG_OPERATION;
+ mbox->type3E.optype = DAC960_V1_GetEventLogEntry;
+ mbox->type3E.opqual = 1;
+ mbox->type3E.ev_seq = event;
+ mbox->type3E.addr = ev_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ if (status != MYRB_STATUS_SUCCESS)
+ shost_printk(KERN_INFO, cb->host,
+ "Failed to get event log %d, status %04x\n",
+ event, status);
+
+ else if (ev_buf->seq_num == event) {
+ struct scsi_sense_hdr sshdr;
+
+ memset(&sshdr, 0, sizeof(sshdr));
+ scsi_normalize_sense(ev_buf->sense, 32, &sshdr);
+
+ if (sshdr.sense_key == VENDOR_SPECIFIC &&
+ sshdr.asc == 0x80 &&
+ sshdr.ascq < ARRAY_SIZE(myrb_event_msg))
+ shost_printk(KERN_CRIT, cb->host,
+ "Physical drive %d:%d: %s\n",
+ ev_buf->channel, ev_buf->target,
+ myrb_event_msg[sshdr.ascq]);
+ else
+ shost_printk(KERN_CRIT, cb->host,
+ "Physical drive %d:%d: Sense: %X/%02X/%02X\n",
+ ev_buf->channel, ev_buf->target,
+ sshdr.sense_key, sshdr.asc, sshdr.ascq);
+ }
+
+ dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_log_entry),
+ ev_buf, ev_addr);
+}
+
+/*
+ * myrb_get_errtable - retrieves the error table from the controller
+ *
+ * Executes a type 3 command and logs the error table from the controller.
+ */
+static void myrb_get_errtable(struct myrb_hba *cb)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ unsigned short status;
+ struct myrb_error_entry old_table[MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS];
+
+ memcpy(&old_table, cb->err_table, sizeof(old_table));
+
+ myrb_reset_cmd(cmd_blk);
+ mbox->type3.id = MYRB_MCMD_TAG;
+ mbox->type3.opcode = MYRB_CMD_GET_ERROR_TABLE;
+ mbox->type3.addr = cb->err_table_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ if (status == MYRB_STATUS_SUCCESS) {
+ struct myrb_error_entry *table = cb->err_table;
+ struct myrb_error_entry *new, *old;
+ size_t err_table_offset;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, cb->host) {
+ if (sdev->channel >= myrb_logical_channel(cb->host))
+ continue;
+ err_table_offset = sdev->channel * MYRB_MAX_TARGETS
+ + sdev->id;
+ new = table + err_table_offset;
+ old = &old_table[err_table_offset];
+ if (new->parity_err == old->parity_err &&
+ new->soft_err == old->soft_err &&
+ new->hard_err == old->hard_err &&
+ new->misc_err == old->misc_err)
+ continue;
+ sdev_printk(KERN_CRIT, sdev,
+ "Errors: Parity = %d, Soft = %d, Hard = %d, Misc = %d\n",
+ new->parity_err, new->soft_err,
+ new->hard_err, new->misc_err);
+ }
+ }
+}
+
+/*
+ * myrb_get_ldev_info - retrieves the logical device table from the controller
+ *
+ * Executes a type 3 command and updates the logical device table.
+ *
+ * Return: command status
+ */
+static unsigned short myrb_get_ldev_info(struct myrb_hba *cb)
+{
+ unsigned short status;
+ int ldev_num, ldev_cnt = cb->enquiry->ldev_count;
+ struct Scsi_Host *shost = cb->host;
+
+ status = myrb_exec_type3(cb, MYRB_CMD_GET_LDEV_INFO,
+ cb->ldev_info_addr);
+ if (status != MYRB_STATUS_SUCCESS)
+ return status;
+
+ for (ldev_num = 0; ldev_num < ldev_cnt; ldev_num++) {
+ struct myrb_ldev_info *old = NULL;
+ struct myrb_ldev_info *new = cb->ldev_info_buf + ldev_num;
+ struct scsi_device *sdev;
+
+ sdev = scsi_device_lookup(shost, myrb_logical_channel(shost),
+ ldev_num, 0);
+ if (!sdev) {
+ if (new->state == MYRB_DEVICE_OFFLINE)
+ continue;
+ shost_printk(KERN_INFO, shost,
+ "Adding Logical Drive %d in state %s\n",
+ ldev_num, myrb_devstate_name(new->state));
+ scsi_add_device(shost, myrb_logical_channel(shost),
+ ldev_num, 0);
+ continue;
+ }
+ old = sdev->hostdata;
+ if (new->state != old->state)
+ shost_printk(KERN_INFO, shost,
+ "Logical Drive %d is now %s\n",
+ ldev_num, myrb_devstate_name(new->state));
+ if (new->wb_enabled != old->wb_enabled)
+ sdev_printk(KERN_INFO, sdev,
+ "Logical Drive is now WRITE %s\n",
+ (new->wb_enabled ? "BACK" : "THRU"));
+ memcpy(old, new, sizeof(*new));
+ scsi_device_put(sdev);
+ }
+ return status;
+}
+
+/*
+ * myrb_get_rbld_progress - get rebuild progress information
+ *
+ * Executes a type 3 command and returns the rebuild progress
+ * information.
+ *
+ * Return: command status
+ */
+static unsigned short myrb_get_rbld_progress(struct myrb_hba *cb,
+ struct myrb_rbld_progress *rbld)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ struct myrb_rbld_progress *rbld_buf;
+ dma_addr_t rbld_addr;
+ unsigned short status;
+
+ rbld_buf = dma_alloc_coherent(&cb->pdev->dev,
+ sizeof(struct myrb_rbld_progress),
+ &rbld_addr, GFP_KERNEL);
+ if (!rbld_buf)
+ return MYRB_STATUS_RBLD_NOT_CHECKED;
+
+ myrb_reset_cmd(cmd_blk);
+ mbox->type3.id = MYRB_MCMD_TAG;
+ mbox->type3.opcode = MYRB_CMD_GET_REBUILD_PROGRESS;
+ mbox->type3.addr = rbld_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ if (rbld)
+ memcpy(rbld, rbld_buf, sizeof(struct myrb_rbld_progress));
+ dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress),
+ rbld_buf, rbld_addr);
+ return status;
+}
+
+/*
+ * myrb_update_rbld_progress - updates the rebuild status
+ *
+ * Updates the rebuild status for the attached logical devices.
+ */
+static void myrb_update_rbld_progress(struct myrb_hba *cb)
+{
+ struct myrb_rbld_progress rbld_buf;
+ unsigned short status;
+
+ status = myrb_get_rbld_progress(cb, &rbld_buf);
+ if (status == MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS &&
+ cb->last_rbld_status == MYRB_STATUS_SUCCESS)
+ status = MYRB_STATUS_RBLD_SUCCESS;
+ if (status != MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS) {
+ unsigned int blocks_done =
+ rbld_buf.ldev_size - rbld_buf.blocks_left;
+ struct scsi_device *sdev;
+
+ sdev = scsi_device_lookup(cb->host,
+ myrb_logical_channel(cb->host),
+ rbld_buf.ldev_num, 0);
+ if (!sdev)
+ return;
+
+ switch (status) {
+ case MYRB_STATUS_SUCCESS:
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild in Progress, %d%% completed\n",
+ (100 * (blocks_done >> 7))
+ / (rbld_buf.ldev_size >> 7));
+ break;
+ case MYRB_STATUS_RBLD_FAILED_LDEV_FAILURE:
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Failed due to Logical Drive Failure\n");
+ break;
+ case MYRB_STATUS_RBLD_FAILED_BADBLOCKS:
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Failed due to Bad Blocks on Other Drives\n");
+ break;
+ case MYRB_STATUS_RBLD_FAILED_NEW_DRIVE_FAILED:
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Failed due to Failure of Drive Being Rebuilt\n");
+ break;
+ case MYRB_STATUS_RBLD_SUCCESS:
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Completed Successfully\n");
+ break;
+ case MYRB_STATUS_RBLD_SUCCESS_TERMINATED:
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Successfully Terminated\n");
+ break;
+ default:
+ break;
+ }
+ scsi_device_put(sdev);
+ }
+ cb->last_rbld_status = status;
+}
+
+/*
+ * myrb_get_cc_progress - retrieve the rebuild status
+ *
+ * Execute a type 3 Command and fetch the rebuild / consistency check
+ * status.
+ */
+static void myrb_get_cc_progress(struct myrb_hba *cb)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ struct myrb_rbld_progress *rbld_buf;
+ dma_addr_t rbld_addr;
+ unsigned short status;
+
+ rbld_buf = dma_alloc_coherent(&cb->pdev->dev,
+ sizeof(struct myrb_rbld_progress),
+ &rbld_addr, GFP_KERNEL);
+ if (!rbld_buf) {
+ cb->need_cc_status = true;
+ return;
+ }
+ myrb_reset_cmd(cmd_blk);
+ mbox->type3.id = MYRB_MCMD_TAG;
+ mbox->type3.opcode = MYRB_CMD_REBUILD_STAT;
+ mbox->type3.addr = rbld_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ if (status == MYRB_STATUS_SUCCESS) {
+ unsigned int ldev_num = rbld_buf->ldev_num;
+ unsigned int ldev_size = rbld_buf->ldev_size;
+ unsigned int blocks_done =
+ ldev_size - rbld_buf->blocks_left;
+ struct scsi_device *sdev;
+
+ sdev = scsi_device_lookup(cb->host,
+ myrb_logical_channel(cb->host),
+ ldev_num, 0);
+ if (sdev) {
+ sdev_printk(KERN_INFO, sdev,
+ "Consistency Check in Progress: %d%% completed\n",
+ (100 * (blocks_done >> 7))
+ / (ldev_size >> 7));
+ scsi_device_put(sdev);
+ }
+ }
+ dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress),
+ rbld_buf, rbld_addr);
+}
+
+/*
+ * myrb_bgi_control - updates background initialisation status
+ *
+ * Executes a type 3B command and updates the background initialisation status
+ */
+static void myrb_bgi_control(struct myrb_hba *cb)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ struct myrb_bgi_status *bgi, *last_bgi;
+ dma_addr_t bgi_addr;
+ struct scsi_device *sdev = NULL;
+ unsigned short status;
+
+ bgi = dma_alloc_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status),
+ &bgi_addr, GFP_KERNEL);
+ if (!bgi) {
+ shost_printk(KERN_ERR, cb->host,
+ "Failed to allocate bgi memory\n");
+ return;
+ }
+ myrb_reset_cmd(cmd_blk);
+ mbox->type3B.id = MYRB_DCMD_TAG;
+ mbox->type3B.opcode = MYRB_CMD_BGI_CONTROL;
+ mbox->type3B.optype = 0x20;
+ mbox->type3B.addr = bgi_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ last_bgi = &cb->bgi_status;
+ sdev = scsi_device_lookup(cb->host,
+ myrb_logical_channel(cb->host),
+ bgi->ldev_num, 0);
+ switch (status) {
+ case MYRB_STATUS_SUCCESS:
+ switch (bgi->status) {
+ case MYRB_BGI_INVALID:
+ break;
+ case MYRB_BGI_STARTED:
+ if (!sdev)
+ break;
+ sdev_printk(KERN_INFO, sdev,
+ "Background Initialization Started\n");
+ break;
+ case MYRB_BGI_INPROGRESS:
+ if (!sdev)
+ break;
+ if (bgi->blocks_done == last_bgi->blocks_done &&
+ bgi->ldev_num == last_bgi->ldev_num)
+ break;
+ sdev_printk(KERN_INFO, sdev,
+ "Background Initialization in Progress: %d%% completed\n",
+ (100 * (bgi->blocks_done >> 7))
+ / (bgi->ldev_size >> 7));
+ break;
+ case MYRB_BGI_SUSPENDED:
+ if (!sdev)
+ break;
+ sdev_printk(KERN_INFO, sdev,
+ "Background Initialization Suspended\n");
+ break;
+ case MYRB_BGI_CANCELLED:
+ if (!sdev)
+ break;
+ sdev_printk(KERN_INFO, sdev,
+ "Background Initialization Cancelled\n");
+ break;
+ }
+ memcpy(&cb->bgi_status, bgi, sizeof(struct myrb_bgi_status));
+ break;
+ case MYRB_STATUS_BGI_SUCCESS:
+ if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS)
+ sdev_printk(KERN_INFO, sdev,
+ "Background Initialization Completed Successfully\n");
+ cb->bgi_status.status = MYRB_BGI_INVALID;
+ break;
+ case MYRB_STATUS_BGI_ABORTED:
+ if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS)
+ sdev_printk(KERN_INFO, sdev,
+ "Background Initialization Aborted\n");
+ fallthrough;
+ case MYRB_STATUS_NO_BGI_INPROGRESS:
+ cb->bgi_status.status = MYRB_BGI_INVALID;
+ break;
+ }
+ if (sdev)
+ scsi_device_put(sdev);
+ dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status),
+ bgi, bgi_addr);
+}
+
+/*
+ * myrb_hba_enquiry - updates the controller status
+ *
+ * Executes a DAC_V1_Enquiry command and updates the controller status.
+ *
+ * Return: command status
+ */
+static unsigned short myrb_hba_enquiry(struct myrb_hba *cb)
+{
+ struct myrb_enquiry old, *new;
+ unsigned short status;
+
+ memcpy(&old, cb->enquiry, sizeof(struct myrb_enquiry));
+
+ status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY, cb->enquiry_addr);
+ if (status != MYRB_STATUS_SUCCESS)
+ return status;
+
+ new = cb->enquiry;
+ if (new->ldev_count > old.ldev_count) {
+ int ldev_num = old.ldev_count - 1;
+
+ while (++ldev_num < new->ldev_count)
+ shost_printk(KERN_CRIT, cb->host,
+ "Logical Drive %d Now Exists\n",
+ ldev_num);
+ }
+ if (new->ldev_count < old.ldev_count) {
+ int ldev_num = new->ldev_count - 1;
+
+ while (++ldev_num < old.ldev_count)
+ shost_printk(KERN_CRIT, cb->host,
+ "Logical Drive %d No Longer Exists\n",
+ ldev_num);
+ }
+ if (new->status.deferred != old.status.deferred)
+ shost_printk(KERN_CRIT, cb->host,
+ "Deferred Write Error Flag is now %s\n",
+ (new->status.deferred ? "TRUE" : "FALSE"));
+ if (new->ev_seq != old.ev_seq) {
+ cb->new_ev_seq = new->ev_seq;
+ cb->need_err_info = true;
+ shost_printk(KERN_INFO, cb->host,
+ "Event log %d/%d (%d/%d) available\n",
+ cb->old_ev_seq, cb->new_ev_seq,
+ old.ev_seq, new->ev_seq);
+ }
+ if ((new->ldev_critical > 0 &&
+ new->ldev_critical != old.ldev_critical) ||
+ (new->ldev_offline > 0 &&
+ new->ldev_offline != old.ldev_offline) ||
+ (new->ldev_count != old.ldev_count)) {
+ shost_printk(KERN_INFO, cb->host,
+ "Logical drive count changed (%d/%d/%d)\n",
+ new->ldev_critical,
+ new->ldev_offline,
+ new->ldev_count);
+ cb->need_ldev_info = true;
+ }
+ if (new->pdev_dead > 0 ||
+ new->pdev_dead != old.pdev_dead ||
+ time_after_eq(jiffies, cb->secondary_monitor_time
+ + MYRB_SECONDARY_MONITOR_INTERVAL)) {
+ cb->need_bgi_status = cb->bgi_status_supported;
+ cb->secondary_monitor_time = jiffies;
+ }
+ if (new->rbld == MYRB_STDBY_RBLD_IN_PROGRESS ||
+ new->rbld == MYRB_BG_RBLD_IN_PROGRESS ||
+ old.rbld == MYRB_STDBY_RBLD_IN_PROGRESS ||
+ old.rbld == MYRB_BG_RBLD_IN_PROGRESS) {
+ cb->need_rbld = true;
+ cb->rbld_first = (new->ldev_critical < old.ldev_critical);
+ }
+ if (old.rbld == MYRB_BG_CHECK_IN_PROGRESS)
+ switch (new->rbld) {
+ case MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS:
+ shost_printk(KERN_INFO, cb->host,
+ "Consistency Check Completed Successfully\n");
+ break;
+ case MYRB_STDBY_RBLD_IN_PROGRESS:
+ case MYRB_BG_RBLD_IN_PROGRESS:
+ break;
+ case MYRB_BG_CHECK_IN_PROGRESS:
+ cb->need_cc_status = true;
+ break;
+ case MYRB_STDBY_RBLD_COMPLETED_WITH_ERROR:
+ shost_printk(KERN_INFO, cb->host,
+ "Consistency Check Completed with Error\n");
+ break;
+ case MYRB_BG_RBLD_OR_CHECK_FAILED_DRIVE_FAILED:
+ shost_printk(KERN_INFO, cb->host,
+ "Consistency Check Failed - Physical Device Failed\n");
+ break;
+ case MYRB_BG_RBLD_OR_CHECK_FAILED_LDEV_FAILED:
+ shost_printk(KERN_INFO, cb->host,
+ "Consistency Check Failed - Logical Drive Failed\n");
+ break;
+ case MYRB_BG_RBLD_OR_CHECK_FAILED_OTHER:
+ shost_printk(KERN_INFO, cb->host,
+ "Consistency Check Failed - Other Causes\n");
+ break;
+ case MYRB_BG_RBLD_OR_CHECK_SUCCESS_TERMINATED:
+ shost_printk(KERN_INFO, cb->host,
+ "Consistency Check Successfully Terminated\n");
+ break;
+ }
+ else if (new->rbld == MYRB_BG_CHECK_IN_PROGRESS)
+ cb->need_cc_status = true;
+
+ return MYRB_STATUS_SUCCESS;
+}
+
+/*
+ * myrb_set_pdev_state - sets the device state for a physical device
+ *
+ * Return: command status
+ */
+static unsigned short myrb_set_pdev_state(struct myrb_hba *cb,
+ struct scsi_device *sdev, enum myrb_devstate state)
+{
+ struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ unsigned short status;
+
+ mutex_lock(&cb->dcmd_mutex);
+ mbox->type3D.opcode = MYRB_CMD_START_DEVICE;
+ mbox->type3D.id = MYRB_DCMD_TAG;
+ mbox->type3D.channel = sdev->channel;
+ mbox->type3D.target = sdev->id;
+ mbox->type3D.state = state & 0x1F;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ mutex_unlock(&cb->dcmd_mutex);
+
+ return status;
+}
+
+/*
+ * myrb_enable_mmio - enables the Memory Mailbox Interface
+ *
+ * PD and P controller types have no memory mailbox, but still need the
+ * other dma mapped memory.
+ *
+ * Return: true on success, false otherwise.
+ */
+static bool myrb_enable_mmio(struct myrb_hba *cb, mbox_mmio_init_t mmio_init_fn)
+{
+ void __iomem *base = cb->io_base;
+ struct pci_dev *pdev = cb->pdev;
+ size_t err_table_size;
+ size_t ldev_info_size;
+ union myrb_cmd_mbox *cmd_mbox_mem;
+ struct myrb_stat_mbox *stat_mbox_mem;
+ union myrb_cmd_mbox mbox;
+ unsigned short status;
+
+ memset(&mbox, 0, sizeof(union myrb_cmd_mbox));
+
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
+ dev_err(&pdev->dev, "DMA mask out of range\n");
+ return false;
+ }
+
+ cb->enquiry = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct myrb_enquiry),
+ &cb->enquiry_addr, GFP_KERNEL);
+ if (!cb->enquiry)
+ return false;
+
+ err_table_size = sizeof(struct myrb_error_entry) *
+ MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS;
+ cb->err_table = dma_alloc_coherent(&pdev->dev, err_table_size,
+ &cb->err_table_addr, GFP_KERNEL);
+ if (!cb->err_table)
+ return false;
+
+ ldev_info_size = sizeof(struct myrb_ldev_info) * MYRB_MAX_LDEVS;
+ cb->ldev_info_buf = dma_alloc_coherent(&pdev->dev, ldev_info_size,
+ &cb->ldev_info_addr, GFP_KERNEL);
+ if (!cb->ldev_info_buf)
+ return false;
+
+ /*
+ * Skip mailbox initialisation for PD and P Controllers
+ */
+ if (!mmio_init_fn)
+ return true;
+
+ /* These are the base addresses for the command memory mailbox array */
+ cb->cmd_mbox_size = MYRB_CMD_MBOX_COUNT * sizeof(union myrb_cmd_mbox);
+ cb->first_cmd_mbox = dma_alloc_coherent(&pdev->dev,
+ cb->cmd_mbox_size,
+ &cb->cmd_mbox_addr,
+ GFP_KERNEL);
+ if (!cb->first_cmd_mbox)
+ return false;
+
+ cmd_mbox_mem = cb->first_cmd_mbox;
+ cmd_mbox_mem += MYRB_CMD_MBOX_COUNT - 1;
+ cb->last_cmd_mbox = cmd_mbox_mem;
+ cb->next_cmd_mbox = cb->first_cmd_mbox;
+ cb->prev_cmd_mbox1 = cb->last_cmd_mbox;
+ cb->prev_cmd_mbox2 = cb->last_cmd_mbox - 1;
+
+ /* These are the base addresses for the status memory mailbox array */
+ cb->stat_mbox_size = MYRB_STAT_MBOX_COUNT *
+ sizeof(struct myrb_stat_mbox);
+ cb->first_stat_mbox = dma_alloc_coherent(&pdev->dev,
+ cb->stat_mbox_size,
+ &cb->stat_mbox_addr,
+ GFP_KERNEL);
+ if (!cb->first_stat_mbox)
+ return false;
+
+ stat_mbox_mem = cb->first_stat_mbox;
+ stat_mbox_mem += MYRB_STAT_MBOX_COUNT - 1;
+ cb->last_stat_mbox = stat_mbox_mem;
+ cb->next_stat_mbox = cb->first_stat_mbox;
+
+ /* Enable the Memory Mailbox Interface. */
+ cb->dual_mode_interface = true;
+ mbox.typeX.opcode = 0x2B;
+ mbox.typeX.id = 0;
+ mbox.typeX.opcode2 = 0x14;
+ mbox.typeX.cmd_mbox_addr = cb->cmd_mbox_addr;
+ mbox.typeX.stat_mbox_addr = cb->stat_mbox_addr;
+
+ status = mmio_init_fn(pdev, base, &mbox);
+ if (status != MYRB_STATUS_SUCCESS) {
+ cb->dual_mode_interface = false;
+ mbox.typeX.opcode2 = 0x10;
+ status = mmio_init_fn(pdev, base, &mbox);
+ if (status != MYRB_STATUS_SUCCESS) {
+ dev_err(&pdev->dev,
+ "Failed to enable mailbox, status %02X\n",
+ status);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * myrb_get_hba_config - reads the configuration information
+ *
+ * Reads the configuration information from the controller and
+ * initializes the controller structure.
+ *
+ * Return: 0 on success, errno otherwise
+ */
+static int myrb_get_hba_config(struct myrb_hba *cb)
+{
+ struct myrb_enquiry2 *enquiry2;
+ dma_addr_t enquiry2_addr;
+ struct myrb_config2 *config2;
+ dma_addr_t config2_addr;
+ struct Scsi_Host *shost = cb->host;
+ struct pci_dev *pdev = cb->pdev;
+ int pchan_max = 0, pchan_cur = 0;
+ unsigned short status;
+ int ret = -ENODEV, memsize = 0;
+
+ enquiry2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_enquiry2),
+ &enquiry2_addr, GFP_KERNEL);
+ if (!enquiry2) {
+ shost_printk(KERN_ERR, cb->host,
+ "Failed to allocate V1 enquiry2 memory\n");
+ return -ENOMEM;
+ }
+ config2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_config2),
+ &config2_addr, GFP_KERNEL);
+ if (!config2) {
+ shost_printk(KERN_ERR, cb->host,
+ "Failed to allocate V1 config2 memory\n");
+ dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2),
+ enquiry2, enquiry2_addr);
+ return -ENOMEM;
+ }
+ mutex_lock(&cb->dma_mutex);
+ status = myrb_hba_enquiry(cb);
+ mutex_unlock(&cb->dma_mutex);
+ if (status != MYRB_STATUS_SUCCESS) {
+ shost_printk(KERN_WARNING, cb->host,
+ "Failed it issue V1 Enquiry\n");
+ goto out_free;
+ }
+
+ status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY2, enquiry2_addr);
+ if (status != MYRB_STATUS_SUCCESS) {
+ shost_printk(KERN_WARNING, cb->host,
+ "Failed to issue V1 Enquiry2\n");
+ goto out_free;
+ }
+
+ status = myrb_exec_type3(cb, MYRB_CMD_READ_CONFIG2, config2_addr);
+ if (status != MYRB_STATUS_SUCCESS) {
+ shost_printk(KERN_WARNING, cb->host,
+ "Failed to issue ReadConfig2\n");
+ goto out_free;
+ }
+
+ status = myrb_get_ldev_info(cb);
+ if (status != MYRB_STATUS_SUCCESS) {
+ shost_printk(KERN_WARNING, cb->host,
+ "Failed to get logical drive information\n");
+ goto out_free;
+ }
+
+ /*
+ * Initialize the Controller Model Name and Full Model Name fields.
+ */
+ switch (enquiry2->hw.sub_model) {
+ case DAC960_V1_P_PD_PU:
+ if (enquiry2->scsi_cap.bus_speed == MYRB_SCSI_SPEED_ULTRA)
+ strcpy(cb->model_name, "DAC960PU");
+ else
+ strcpy(cb->model_name, "DAC960PD");
+ break;
+ case DAC960_V1_PL:
+ strcpy(cb->model_name, "DAC960PL");
+ break;
+ case DAC960_V1_PG:
+ strcpy(cb->model_name, "DAC960PG");
+ break;
+ case DAC960_V1_PJ:
+ strcpy(cb->model_name, "DAC960PJ");
+ break;
+ case DAC960_V1_PR:
+ strcpy(cb->model_name, "DAC960PR");
+ break;
+ case DAC960_V1_PT:
+ strcpy(cb->model_name, "DAC960PT");
+ break;
+ case DAC960_V1_PTL0:
+ strcpy(cb->model_name, "DAC960PTL0");
+ break;
+ case DAC960_V1_PRL:
+ strcpy(cb->model_name, "DAC960PRL");
+ break;
+ case DAC960_V1_PTL1:
+ strcpy(cb->model_name, "DAC960PTL1");
+ break;
+ case DAC960_V1_1164P:
+ strcpy(cb->model_name, "eXtremeRAID 1100");
+ break;
+ default:
+ shost_printk(KERN_WARNING, cb->host,
+ "Unknown Model %X\n",
+ enquiry2->hw.sub_model);
+ goto out;
+ }
+ /*
+ * Initialize the Controller Firmware Version field and verify that it
+ * is a supported firmware version.
+ * The supported firmware versions are:
+ *
+ * DAC1164P 5.06 and above
+ * DAC960PTL/PRL/PJ/PG 4.06 and above
+ * DAC960PU/PD/PL 3.51 and above
+ * DAC960PU/PD/PL/P 2.73 and above
+ */
+#if defined(CONFIG_ALPHA)
+ /*
+ * DEC Alpha machines were often equipped with DAC960 cards that were
+ * OEMed from Mylex, and had their own custom firmware. Version 2.70,
+ * the last custom FW revision to be released by DEC for these older
+ * controllers, appears to work quite well with this driver.
+ *
+ * Cards tested successfully were several versions each of the PD and
+ * PU, called by DEC the KZPSC and KZPAC, respectively, and having
+ * the Manufacturer Numbers (from Mylex), usually on a sticker on the
+ * back of the board, of:
+ *
+ * KZPSC: D040347 (1-channel) or D040348 (2-channel)
+ * or D040349 (3-channel)
+ * KZPAC: D040395 (1-channel) or D040396 (2-channel)
+ * or D040397 (3-channel)
+ */
+# define FIRMWARE_27X "2.70"
+#else
+# define FIRMWARE_27X "2.73"
+#endif
+
+ if (enquiry2->fw.major_version == 0) {
+ enquiry2->fw.major_version = cb->enquiry->fw_major_version;
+ enquiry2->fw.minor_version = cb->enquiry->fw_minor_version;
+ enquiry2->fw.firmware_type = '0';
+ enquiry2->fw.turn_id = 0;
+ }
+ snprintf(cb->fw_version, sizeof(cb->fw_version),
+ "%u.%02u-%c-%02u",
+ enquiry2->fw.major_version,
+ enquiry2->fw.minor_version,
+ enquiry2->fw.firmware_type,
+ enquiry2->fw.turn_id);
+ if (!((enquiry2->fw.major_version == 5 &&
+ enquiry2->fw.minor_version >= 6) ||
+ (enquiry2->fw.major_version == 4 &&
+ enquiry2->fw.minor_version >= 6) ||
+ (enquiry2->fw.major_version == 3 &&
+ enquiry2->fw.minor_version >= 51) ||
+ (enquiry2->fw.major_version == 2 &&
+ strcmp(cb->fw_version, FIRMWARE_27X) >= 0))) {
+ shost_printk(KERN_WARNING, cb->host,
+ "Firmware Version '%s' unsupported\n",
+ cb->fw_version);
+ goto out;
+ }
+ /*
+ * Initialize the Channels, Targets, Memory Size, and SAF-TE
+ * Enclosure Management Enabled fields.
+ */
+ switch (enquiry2->hw.model) {
+ case MYRB_5_CHANNEL_BOARD:
+ pchan_max = 5;
+ break;
+ case MYRB_3_CHANNEL_BOARD:
+ case MYRB_3_CHANNEL_ASIC_DAC:
+ pchan_max = 3;
+ break;
+ case MYRB_2_CHANNEL_BOARD:
+ pchan_max = 2;
+ break;
+ default:
+ pchan_max = enquiry2->cfg_chan;
+ break;
+ }
+ pchan_cur = enquiry2->cur_chan;
+ if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_32BIT)
+ cb->bus_width = 32;
+ else if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_16BIT)
+ cb->bus_width = 16;
+ else
+ cb->bus_width = 8;
+ cb->ldev_block_size = enquiry2->ldev_block_size;
+ shost->max_channel = pchan_cur;
+ shost->max_id = enquiry2->max_targets;
+ memsize = enquiry2->mem_size >> 20;
+ cb->safte_enabled = (enquiry2->fault_mgmt == MYRB_FAULT_SAFTE);
+ /*
+ * Initialize the Controller Queue Depth, Driver Queue Depth,
+ * Logical Drive Count, Maximum Blocks per Command, Controller
+ * Scatter/Gather Limit, and Driver Scatter/Gather Limit.
+ * The Driver Queue Depth must be at most one less than the
+ * Controller Queue Depth to allow for an automatic drive
+ * rebuild operation.
+ */
+ shost->can_queue = cb->enquiry->max_tcq;
+ if (shost->can_queue < 3)
+ shost->can_queue = enquiry2->max_cmds;
+ if (shost->can_queue < 3)
+ /* Play safe and disable TCQ */
+ shost->can_queue = 1;
+
+ if (shost->can_queue > MYRB_CMD_MBOX_COUNT - 2)
+ shost->can_queue = MYRB_CMD_MBOX_COUNT - 2;
+ shost->max_sectors = enquiry2->max_sectors;
+ shost->sg_tablesize = enquiry2->max_sge;
+ if (shost->sg_tablesize > MYRB_SCATTER_GATHER_LIMIT)
+ shost->sg_tablesize = MYRB_SCATTER_GATHER_LIMIT;
+ /*
+ * Initialize the Stripe Size, Segment Size, and Geometry Translation.
+ */
+ cb->stripe_size = config2->blocks_per_stripe * config2->block_factor
+ >> (10 - MYRB_BLKSIZE_BITS);
+ cb->segment_size = config2->blocks_per_cacheline * config2->block_factor
+ >> (10 - MYRB_BLKSIZE_BITS);
+ /* Assume 255/63 translation */
+ cb->ldev_geom_heads = 255;
+ cb->ldev_geom_sectors = 63;
+ if (config2->drive_geometry) {
+ cb->ldev_geom_heads = 128;
+ cb->ldev_geom_sectors = 32;
+ }
+
+ /*
+ * Initialize the Background Initialization Status.
+ */
+ if ((cb->fw_version[0] == '4' &&
+ strcmp(cb->fw_version, "4.08") >= 0) ||
+ (cb->fw_version[0] == '5' &&
+ strcmp(cb->fw_version, "5.08") >= 0)) {
+ cb->bgi_status_supported = true;
+ myrb_bgi_control(cb);
+ }
+ cb->last_rbld_status = MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS;
+ ret = 0;
+
+out:
+ shost_printk(KERN_INFO, cb->host,
+ "Configuring %s PCI RAID Controller\n", cb->model_name);
+ shost_printk(KERN_INFO, cb->host,
+ " Firmware Version: %s, Memory Size: %dMB\n",
+ cb->fw_version, memsize);
+ if (cb->io_addr == 0)
+ shost_printk(KERN_INFO, cb->host,
+ " I/O Address: n/a, PCI Address: 0x%lX, IRQ Channel: %d\n",
+ (unsigned long)cb->pci_addr, cb->irq);
+ else
+ shost_printk(KERN_INFO, cb->host,
+ " I/O Address: 0x%lX, PCI Address: 0x%lX, IRQ Channel: %d\n",
+ (unsigned long)cb->io_addr, (unsigned long)cb->pci_addr,
+ cb->irq);
+ shost_printk(KERN_INFO, cb->host,
+ " Controller Queue Depth: %d, Maximum Blocks per Command: %d\n",
+ cb->host->can_queue, cb->host->max_sectors);
+ shost_printk(KERN_INFO, cb->host,
+ " Driver Queue Depth: %d, Scatter/Gather Limit: %d of %d Segments\n",
+ cb->host->can_queue, cb->host->sg_tablesize,
+ MYRB_SCATTER_GATHER_LIMIT);
+ shost_printk(KERN_INFO, cb->host,
+ " Stripe Size: %dKB, Segment Size: %dKB, BIOS Geometry: %d/%d%s\n",
+ cb->stripe_size, cb->segment_size,
+ cb->ldev_geom_heads, cb->ldev_geom_sectors,
+ cb->safte_enabled ?
+ " SAF-TE Enclosure Management Enabled" : "");
+ shost_printk(KERN_INFO, cb->host,
+ " Physical: %d/%d channels %d/%d/%d devices\n",
+ pchan_cur, pchan_max, 0, cb->enquiry->pdev_dead,
+ cb->host->max_id);
+
+ shost_printk(KERN_INFO, cb->host,
+ " Logical: 1/1 channels, %d/%d disks\n",
+ cb->enquiry->ldev_count, MYRB_MAX_LDEVS);
+
+out_free:
+ dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2),
+ enquiry2, enquiry2_addr);
+ dma_free_coherent(&pdev->dev, sizeof(struct myrb_config2),
+ config2, config2_addr);
+
+ return ret;
+}
+
+/*
+ * myrb_unmap - unmaps controller structures
+ */
+static void myrb_unmap(struct myrb_hba *cb)
+{
+ if (cb->ldev_info_buf) {
+ size_t ldev_info_size = sizeof(struct myrb_ldev_info) *
+ MYRB_MAX_LDEVS;
+ dma_free_coherent(&cb->pdev->dev, ldev_info_size,
+ cb->ldev_info_buf, cb->ldev_info_addr);
+ cb->ldev_info_buf = NULL;
+ }
+ if (cb->err_table) {
+ size_t err_table_size = sizeof(struct myrb_error_entry) *
+ MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS;
+ dma_free_coherent(&cb->pdev->dev, err_table_size,
+ cb->err_table, cb->err_table_addr);
+ cb->err_table = NULL;
+ }
+ if (cb->enquiry) {
+ dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_enquiry),
+ cb->enquiry, cb->enquiry_addr);
+ cb->enquiry = NULL;
+ }
+ if (cb->first_stat_mbox) {
+ dma_free_coherent(&cb->pdev->dev, cb->stat_mbox_size,
+ cb->first_stat_mbox, cb->stat_mbox_addr);
+ cb->first_stat_mbox = NULL;
+ }
+ if (cb->first_cmd_mbox) {
+ dma_free_coherent(&cb->pdev->dev, cb->cmd_mbox_size,
+ cb->first_cmd_mbox, cb->cmd_mbox_addr);
+ cb->first_cmd_mbox = NULL;
+ }
+}
+
+/*
+ * myrb_cleanup - cleanup controller structures
+ */
+static void myrb_cleanup(struct myrb_hba *cb)
+{
+ struct pci_dev *pdev = cb->pdev;
+
+ /* Free the memory mailbox, status, and related structures */
+ myrb_unmap(cb);
+
+ if (cb->mmio_base) {
+ if (cb->disable_intr)
+ cb->disable_intr(cb->io_base);
+ iounmap(cb->mmio_base);
+ }
+ if (cb->irq)
+ free_irq(cb->irq, cb);
+ if (cb->io_addr)
+ release_region(cb->io_addr, 0x80);
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+ scsi_host_put(cb->host);
+}
+
+static int myrb_host_reset(struct scsi_cmnd *scmd)
+{
+ struct Scsi_Host *shost = scmd->device->host;
+ struct myrb_hba *cb = shost_priv(shost);
+
+ cb->reset(cb->io_base);
+ return SUCCESS;
+}
+
+static int myrb_pthru_queuecommand(struct Scsi_Host *shost,
+ struct scsi_cmnd *scmd)
+{
+ struct request *rq = scsi_cmd_to_rq(scmd);
+ struct myrb_hba *cb = shost_priv(shost);
+ struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd);
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ struct myrb_dcdb *dcdb;
+ dma_addr_t dcdb_addr;
+ struct scsi_device *sdev = scmd->device;
+ struct scatterlist *sgl;
+ unsigned long flags;
+ int nsge;
+
+ myrb_reset_cmd(cmd_blk);
+ dcdb = dma_pool_alloc(cb->dcdb_pool, GFP_ATOMIC, &dcdb_addr);
+ if (!dcdb)
+ return SCSI_MLQUEUE_HOST_BUSY;
+ nsge = scsi_dma_map(scmd);
+ if (nsge > 1) {
+ dma_pool_free(cb->dcdb_pool, dcdb, dcdb_addr);
+ scmd->result = (DID_ERROR << 16);
+ scsi_done(scmd);
+ return 0;
+ }
+
+ mbox->type3.opcode = MYRB_CMD_DCDB;
+ mbox->type3.id = rq->tag + 3;
+ mbox->type3.addr = dcdb_addr;
+ dcdb->channel = sdev->channel;
+ dcdb->target = sdev->id;
+ switch (scmd->sc_data_direction) {
+ case DMA_NONE:
+ dcdb->data_xfer = MYRB_DCDB_XFER_NONE;
+ break;
+ case DMA_TO_DEVICE:
+ dcdb->data_xfer = MYRB_DCDB_XFER_SYSTEM_TO_DEVICE;
+ break;
+ case DMA_FROM_DEVICE:
+ dcdb->data_xfer = MYRB_DCDB_XFER_DEVICE_TO_SYSTEM;
+ break;
+ default:
+ dcdb->data_xfer = MYRB_DCDB_XFER_ILLEGAL;
+ break;
+ }
+ dcdb->early_status = false;
+ if (rq->timeout <= 10)
+ dcdb->timeout = MYRB_DCDB_TMO_10_SECS;
+ else if (rq->timeout <= 60)
+ dcdb->timeout = MYRB_DCDB_TMO_60_SECS;
+ else if (rq->timeout <= 600)
+ dcdb->timeout = MYRB_DCDB_TMO_10_MINS;
+ else
+ dcdb->timeout = MYRB_DCDB_TMO_24_HRS;
+ dcdb->no_autosense = false;
+ dcdb->allow_disconnect = true;
+ sgl = scsi_sglist(scmd);
+ dcdb->dma_addr = sg_dma_address(sgl);
+ if (sg_dma_len(sgl) > USHRT_MAX) {
+ dcdb->xfer_len_lo = sg_dma_len(sgl) & 0xffff;
+ dcdb->xfer_len_hi4 = sg_dma_len(sgl) >> 16;
+ } else {
+ dcdb->xfer_len_lo = sg_dma_len(sgl);
+ dcdb->xfer_len_hi4 = 0;
+ }
+ dcdb->cdb_len = scmd->cmd_len;
+ dcdb->sense_len = sizeof(dcdb->sense);
+ memcpy(&dcdb->cdb, scmd->cmnd, scmd->cmd_len);
+
+ spin_lock_irqsave(&cb->queue_lock, flags);
+ cb->qcmd(cb, cmd_blk);
+ spin_unlock_irqrestore(&cb->queue_lock, flags);
+ return 0;
+}
+
+static void myrb_inquiry(struct myrb_hba *cb,
+ struct scsi_cmnd *scmd)
+{
+ unsigned char inq[36] = {
+ 0x00, 0x00, 0x03, 0x02, 0x20, 0x00, 0x01, 0x00,
+ 0x4d, 0x59, 0x4c, 0x45, 0x58, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20,
+ };
+
+ if (cb->bus_width > 16)
+ inq[7] |= 1 << 6;
+ if (cb->bus_width > 8)
+ inq[7] |= 1 << 5;
+ memcpy(&inq[16], cb->model_name, 16);
+ memcpy(&inq[32], cb->fw_version, 1);
+ memcpy(&inq[33], &cb->fw_version[2], 2);
+ memcpy(&inq[35], &cb->fw_version[7], 1);
+
+ scsi_sg_copy_from_buffer(scmd, (void *)inq, 36);
+}
+
+static void
+myrb_mode_sense(struct myrb_hba *cb, struct scsi_cmnd *scmd,
+ struct myrb_ldev_info *ldev_info)
+{
+ unsigned char modes[32], *mode_pg;
+ bool dbd;
+ size_t mode_len;
+
+ dbd = (scmd->cmnd[1] & 0x08) == 0x08;
+ if (dbd) {
+ mode_len = 24;
+ mode_pg = &modes[4];
+ } else {
+ mode_len = 32;
+ mode_pg = &modes[12];
+ }
+ memset(modes, 0, sizeof(modes));
+ modes[0] = mode_len - 1;
+ if (!dbd) {
+ unsigned char *block_desc = &modes[4];
+
+ modes[3] = 8;
+ put_unaligned_be32(ldev_info->size, &block_desc[0]);
+ put_unaligned_be32(cb->ldev_block_size, &block_desc[5]);
+ }
+ mode_pg[0] = 0x08;
+ mode_pg[1] = 0x12;
+ if (ldev_info->wb_enabled)
+ mode_pg[2] |= 0x04;
+ if (cb->segment_size) {
+ mode_pg[2] |= 0x08;
+ put_unaligned_be16(cb->segment_size, &mode_pg[14]);
+ }
+
+ scsi_sg_copy_from_buffer(scmd, modes, mode_len);
+}
+
+static void myrb_request_sense(struct myrb_hba *cb,
+ struct scsi_cmnd *scmd)
+{
+ scsi_build_sense(scmd, 0, NO_SENSE, 0, 0);
+ scsi_sg_copy_from_buffer(scmd, scmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE);
+}
+
+static void myrb_read_capacity(struct myrb_hba *cb, struct scsi_cmnd *scmd,
+ struct myrb_ldev_info *ldev_info)
+{
+ unsigned char data[8];
+
+ dev_dbg(&scmd->device->sdev_gendev,
+ "Capacity %u, blocksize %u\n",
+ ldev_info->size, cb->ldev_block_size);
+ put_unaligned_be32(ldev_info->size - 1, &data[0]);
+ put_unaligned_be32(cb->ldev_block_size, &data[4]);
+ scsi_sg_copy_from_buffer(scmd, data, 8);
+}
+
+static int myrb_ldev_queuecommand(struct Scsi_Host *shost,
+ struct scsi_cmnd *scmd)
+{
+ struct myrb_hba *cb = shost_priv(shost);
+ struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd);
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ struct myrb_ldev_info *ldev_info;
+ struct scsi_device *sdev = scmd->device;
+ struct scatterlist *sgl;
+ unsigned long flags;
+ u64 lba;
+ u32 block_cnt;
+ int nsge;
+
+ ldev_info = sdev->hostdata;
+ if (ldev_info->state != MYRB_DEVICE_ONLINE &&
+ ldev_info->state != MYRB_DEVICE_WO) {
+ dev_dbg(&shost->shost_gendev, "ldev %u in state %x, skip\n",
+ sdev->id, ldev_info ? ldev_info->state : 0xff);
+ scmd->result = (DID_BAD_TARGET << 16);
+ scsi_done(scmd);
+ return 0;
+ }
+ switch (scmd->cmnd[0]) {
+ case TEST_UNIT_READY:
+ scmd->result = (DID_OK << 16);
+ scsi_done(scmd);
+ return 0;
+ case INQUIRY:
+ if (scmd->cmnd[1] & 1) {
+ /* Illegal request, invalid field in CDB */
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
+ } else {
+ myrb_inquiry(cb, scmd);
+ scmd->result = (DID_OK << 16);
+ }
+ scsi_done(scmd);
+ return 0;
+ case SYNCHRONIZE_CACHE:
+ scmd->result = (DID_OK << 16);
+ scsi_done(scmd);
+ return 0;
+ case MODE_SENSE:
+ if ((scmd->cmnd[2] & 0x3F) != 0x3F &&
+ (scmd->cmnd[2] & 0x3F) != 0x08) {
+ /* Illegal request, invalid field in CDB */
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
+ } else {
+ myrb_mode_sense(cb, scmd, ldev_info);
+ scmd->result = (DID_OK << 16);
+ }
+ scsi_done(scmd);
+ return 0;
+ case READ_CAPACITY:
+ if ((scmd->cmnd[1] & 1) ||
+ (scmd->cmnd[8] & 1)) {
+ /* Illegal request, invalid field in CDB */
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
+ scsi_done(scmd);
+ return 0;
+ }
+ lba = get_unaligned_be32(&scmd->cmnd[2]);
+ if (lba) {
+ /* Illegal request, invalid field in CDB */
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
+ scsi_done(scmd);
+ return 0;
+ }
+ myrb_read_capacity(cb, scmd, ldev_info);
+ scsi_done(scmd);
+ return 0;
+ case REQUEST_SENSE:
+ myrb_request_sense(cb, scmd);
+ scmd->result = (DID_OK << 16);
+ return 0;
+ case SEND_DIAGNOSTIC:
+ if (scmd->cmnd[1] != 0x04) {
+ /* Illegal request, invalid field in CDB */
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
+ } else {
+ /* Assume good status */
+ scmd->result = (DID_OK << 16);
+ }
+ scsi_done(scmd);
+ return 0;
+ case READ_6:
+ if (ldev_info->state == MYRB_DEVICE_WO) {
+ /* Data protect, attempt to read invalid data */
+ scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06);
+ scsi_done(scmd);
+ return 0;
+ }
+ fallthrough;
+ case WRITE_6:
+ lba = (((scmd->cmnd[1] & 0x1F) << 16) |
+ (scmd->cmnd[2] << 8) |
+ scmd->cmnd[3]);
+ block_cnt = scmd->cmnd[4];
+ break;
+ case READ_10:
+ if (ldev_info->state == MYRB_DEVICE_WO) {
+ /* Data protect, attempt to read invalid data */
+ scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06);
+ scsi_done(scmd);
+ return 0;
+ }
+ fallthrough;
+ case WRITE_10:
+ case VERIFY: /* 0x2F */
+ case WRITE_VERIFY: /* 0x2E */
+ lba = get_unaligned_be32(&scmd->cmnd[2]);
+ block_cnt = get_unaligned_be16(&scmd->cmnd[7]);
+ break;
+ case READ_12:
+ if (ldev_info->state == MYRB_DEVICE_WO) {
+ /* Data protect, attempt to read invalid data */
+ scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06);
+ scsi_done(scmd);
+ return 0;
+ }
+ fallthrough;
+ case WRITE_12:
+ case VERIFY_12: /* 0xAF */
+ case WRITE_VERIFY_12: /* 0xAE */
+ lba = get_unaligned_be32(&scmd->cmnd[2]);
+ block_cnt = get_unaligned_be32(&scmd->cmnd[6]);
+ break;
+ default:
+ /* Illegal request, invalid opcode */
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x20, 0);
+ scsi_done(scmd);
+ return 0;
+ }
+
+ myrb_reset_cmd(cmd_blk);
+ mbox->type5.id = scsi_cmd_to_rq(scmd)->tag + 3;
+ if (scmd->sc_data_direction == DMA_NONE)
+ goto submit;
+ nsge = scsi_dma_map(scmd);
+ if (nsge == 1) {
+ sgl = scsi_sglist(scmd);
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ mbox->type5.opcode = MYRB_CMD_READ;
+ else
+ mbox->type5.opcode = MYRB_CMD_WRITE;
+
+ mbox->type5.ld.xfer_len = block_cnt;
+ mbox->type5.ld.ldev_num = sdev->id;
+ mbox->type5.lba = lba;
+ mbox->type5.addr = (u32)sg_dma_address(sgl);
+ } else {
+ struct myrb_sge *hw_sgl;
+ dma_addr_t hw_sgl_addr;
+ int i;
+
+ hw_sgl = dma_pool_alloc(cb->sg_pool, GFP_ATOMIC, &hw_sgl_addr);
+ if (!hw_sgl)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ cmd_blk->sgl = hw_sgl;
+ cmd_blk->sgl_addr = hw_sgl_addr;
+
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ mbox->type5.opcode = MYRB_CMD_READ_SG;
+ else
+ mbox->type5.opcode = MYRB_CMD_WRITE_SG;
+
+ mbox->type5.ld.xfer_len = block_cnt;
+ mbox->type5.ld.ldev_num = sdev->id;
+ mbox->type5.lba = lba;
+ mbox->type5.addr = hw_sgl_addr;
+ mbox->type5.sg_count = nsge;
+
+ scsi_for_each_sg(scmd, sgl, nsge, i) {
+ hw_sgl->sge_addr = (u32)sg_dma_address(sgl);
+ hw_sgl->sge_count = (u32)sg_dma_len(sgl);
+ hw_sgl++;
+ }
+ }
+submit:
+ spin_lock_irqsave(&cb->queue_lock, flags);
+ cb->qcmd(cb, cmd_blk);
+ spin_unlock_irqrestore(&cb->queue_lock, flags);
+
+ return 0;
+}
+
+static int myrb_queuecommand(struct Scsi_Host *shost,
+ struct scsi_cmnd *scmd)
+{
+ struct scsi_device *sdev = scmd->device;
+
+ if (sdev->channel > myrb_logical_channel(shost)) {
+ scmd->result = (DID_BAD_TARGET << 16);
+ scsi_done(scmd);
+ return 0;
+ }
+ if (sdev->channel == myrb_logical_channel(shost))
+ return myrb_ldev_queuecommand(shost, scmd);
+
+ return myrb_pthru_queuecommand(shost, scmd);
+}
+
+static int myrb_ldev_sdev_init(struct scsi_device *sdev)
+{
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_ldev_info *ldev_info;
+ unsigned short ldev_num = sdev->id;
+ enum raid_level level;
+
+ ldev_info = cb->ldev_info_buf + ldev_num;
+
+ sdev->hostdata = kzalloc(sizeof(*ldev_info), GFP_KERNEL);
+ if (!sdev->hostdata)
+ return -ENOMEM;
+ dev_dbg(&sdev->sdev_gendev,
+ "slave alloc ldev %d state %x\n",
+ ldev_num, ldev_info->state);
+ memcpy(sdev->hostdata, ldev_info,
+ sizeof(*ldev_info));
+ switch (ldev_info->raid_level) {
+ case MYRB_RAID_LEVEL0:
+ level = RAID_LEVEL_LINEAR;
+ break;
+ case MYRB_RAID_LEVEL1:
+ level = RAID_LEVEL_1;
+ break;
+ case MYRB_RAID_LEVEL3:
+ level = RAID_LEVEL_3;
+ break;
+ case MYRB_RAID_LEVEL5:
+ level = RAID_LEVEL_5;
+ break;
+ case MYRB_RAID_LEVEL6:
+ level = RAID_LEVEL_6;
+ break;
+ case MYRB_RAID_JBOD:
+ level = RAID_LEVEL_JBOD;
+ break;
+ default:
+ level = RAID_LEVEL_UNKNOWN;
+ break;
+ }
+ raid_set_level(myrb_raid_template, &sdev->sdev_gendev, level);
+ return 0;
+}
+
+static int myrb_pdev_sdev_init(struct scsi_device *sdev)
+{
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_pdev_state *pdev_info;
+ unsigned short status;
+
+ if (sdev->id > MYRB_MAX_TARGETS)
+ return -ENXIO;
+
+ pdev_info = kzalloc(sizeof(*pdev_info), GFP_KERNEL);
+ if (!pdev_info)
+ return -ENOMEM;
+
+ status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE,
+ sdev, pdev_info);
+ if (status != MYRB_STATUS_SUCCESS) {
+ dev_dbg(&sdev->sdev_gendev,
+ "Failed to get device state, status %x\n",
+ status);
+ kfree(pdev_info);
+ return -ENXIO;
+ }
+ if (!pdev_info->present) {
+ dev_dbg(&sdev->sdev_gendev,
+ "device not present, skip\n");
+ kfree(pdev_info);
+ return -ENXIO;
+ }
+ dev_dbg(&sdev->sdev_gendev,
+ "slave alloc pdev %d:%d state %x\n",
+ sdev->channel, sdev->id, pdev_info->state);
+ sdev->hostdata = pdev_info;
+
+ return 0;
+}
+
+static int myrb_sdev_init(struct scsi_device *sdev)
+{
+ if (sdev->channel > myrb_logical_channel(sdev->host))
+ return -ENXIO;
+
+ if (sdev->lun > 0)
+ return -ENXIO;
+
+ if (sdev->channel == myrb_logical_channel(sdev->host))
+ return myrb_ldev_sdev_init(sdev);
+
+ return myrb_pdev_sdev_init(sdev);
+}
+
+static int myrb_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ struct myrb_ldev_info *ldev_info;
+
+ if (sdev->channel > myrb_logical_channel(sdev->host))
+ return -ENXIO;
+
+ if (sdev->channel < myrb_logical_channel(sdev->host)) {
+ sdev->no_uld_attach = 1;
+ return 0;
+ }
+ if (sdev->lun != 0)
+ return -ENXIO;
+
+ ldev_info = sdev->hostdata;
+ if (!ldev_info)
+ return -ENXIO;
+ if (ldev_info->state != MYRB_DEVICE_ONLINE)
+ sdev_printk(KERN_INFO, sdev,
+ "Logical drive is %s\n",
+ myrb_devstate_name(ldev_info->state));
+
+ sdev->tagged_supported = 1;
+ return 0;
+}
+
+static void myrb_sdev_destroy(struct scsi_device *sdev)
+{
+ kfree(sdev->hostdata);
+}
+
+static int myrb_biosparam(struct scsi_device *sdev, struct gendisk *unused,
+ sector_t capacity, int geom[])
+{
+ struct myrb_hba *cb = shost_priv(sdev->host);
+
+ geom[0] = cb->ldev_geom_heads;
+ geom[1] = cb->ldev_geom_sectors;
+ geom[2] = sector_div(capacity, geom[0] * geom[1]);
+
+ return 0;
+}
+
+static ssize_t raid_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ int ret;
+
+ if (!sdev->hostdata)
+ return snprintf(buf, 16, "Unknown\n");
+
+ if (sdev->channel == myrb_logical_channel(sdev->host)) {
+ struct myrb_ldev_info *ldev_info = sdev->hostdata;
+ const char *name;
+
+ name = myrb_devstate_name(ldev_info->state);
+ if (name)
+ ret = snprintf(buf, 64, "%s\n", name);
+ else
+ ret = snprintf(buf, 64, "Invalid (%02X)\n",
+ ldev_info->state);
+ } else {
+ struct myrb_pdev_state *pdev_info = sdev->hostdata;
+ unsigned short status;
+ const char *name;
+
+ status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE,
+ sdev, pdev_info);
+ if (status != MYRB_STATUS_SUCCESS)
+ sdev_printk(KERN_INFO, sdev,
+ "Failed to get device state, status %x\n",
+ status);
+
+ if (!pdev_info->present)
+ name = "Removed";
+ else
+ name = myrb_devstate_name(pdev_info->state);
+ if (name)
+ ret = snprintf(buf, 64, "%s\n", name);
+ else
+ ret = snprintf(buf, 64, "Invalid (%02X)\n",
+ pdev_info->state);
+ }
+ return ret;
+}
+
+static ssize_t raid_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_pdev_state *pdev_info;
+ enum myrb_devstate new_state;
+ unsigned short status;
+
+ if (!strncmp(buf, "kill", 4) ||
+ !strncmp(buf, "offline", 7))
+ new_state = MYRB_DEVICE_DEAD;
+ else if (!strncmp(buf, "online", 6))
+ new_state = MYRB_DEVICE_ONLINE;
+ else if (!strncmp(buf, "standby", 7))
+ new_state = MYRB_DEVICE_STANDBY;
+ else
+ return -EINVAL;
+
+ pdev_info = sdev->hostdata;
+ if (!pdev_info) {
+ sdev_printk(KERN_INFO, sdev,
+ "Failed - no physical device information\n");
+ return -ENXIO;
+ }
+ if (!pdev_info->present) {
+ sdev_printk(KERN_INFO, sdev,
+ "Failed - device not present\n");
+ return -ENXIO;
+ }
+
+ if (pdev_info->state == new_state)
+ return count;
+
+ status = myrb_set_pdev_state(cb, sdev, new_state);
+ switch (status) {
+ case MYRB_STATUS_SUCCESS:
+ break;
+ case MYRB_STATUS_START_DEVICE_FAILED:
+ sdev_printk(KERN_INFO, sdev,
+ "Failed - Unable to Start Device\n");
+ count = -EAGAIN;
+ break;
+ case MYRB_STATUS_NO_DEVICE:
+ sdev_printk(KERN_INFO, sdev,
+ "Failed - No Device at Address\n");
+ count = -ENODEV;
+ break;
+ case MYRB_STATUS_INVALID_CHANNEL_OR_TARGET:
+ sdev_printk(KERN_INFO, sdev,
+ "Failed - Invalid Channel or Target or Modifier\n");
+ count = -EINVAL;
+ break;
+ case MYRB_STATUS_CHANNEL_BUSY:
+ sdev_printk(KERN_INFO, sdev,
+ "Failed - Channel Busy\n");
+ count = -EBUSY;
+ break;
+ default:
+ sdev_printk(KERN_INFO, sdev,
+ "Failed - Unexpected Status %04X\n", status);
+ count = -EIO;
+ break;
+ }
+ return count;
+}
+static DEVICE_ATTR_RW(raid_state);
+
+static ssize_t raid_level_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ if (sdev->channel == myrb_logical_channel(sdev->host)) {
+ struct myrb_ldev_info *ldev_info = sdev->hostdata;
+ const char *name;
+
+ if (!ldev_info)
+ return -ENXIO;
+
+ name = myrb_raidlevel_name(ldev_info->raid_level);
+ if (!name)
+ return snprintf(buf, 64, "Invalid (%02X)\n",
+ ldev_info->state);
+ return snprintf(buf, 64, "%s\n", name);
+ }
+ return snprintf(buf, 64, "Physical Drive\n");
+}
+static DEVICE_ATTR_RO(raid_level);
+
+static ssize_t rebuild_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_rbld_progress rbld_buf;
+ unsigned char status;
+
+ if (sdev->channel < myrb_logical_channel(sdev->host))
+ return snprintf(buf, 64, "physical device - not rebuilding\n");
+
+ status = myrb_get_rbld_progress(cb, &rbld_buf);
+
+ if (rbld_buf.ldev_num != sdev->id ||
+ status != MYRB_STATUS_SUCCESS)
+ return snprintf(buf, 64, "not rebuilding\n");
+
+ return snprintf(buf, 64, "rebuilding block %u of %u\n",
+ rbld_buf.ldev_size - rbld_buf.blocks_left,
+ rbld_buf.ldev_size);
+}
+
+static ssize_t rebuild_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_cmdblk *cmd_blk;
+ union myrb_cmd_mbox *mbox;
+ unsigned short status;
+ int rc, start;
+ const char *msg;
+
+ rc = kstrtoint(buf, 0, &start);
+ if (rc)
+ return rc;
+
+ if (sdev->channel >= myrb_logical_channel(sdev->host))
+ return -ENXIO;
+
+ status = myrb_get_rbld_progress(cb, NULL);
+ if (start) {
+ if (status == MYRB_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Not Initiated; already in progress\n");
+ return -EALREADY;
+ }
+ mutex_lock(&cb->dcmd_mutex);
+ cmd_blk = &cb->dcmd_blk;
+ myrb_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->type3D.opcode = MYRB_CMD_REBUILD_ASYNC;
+ mbox->type3D.id = MYRB_DCMD_TAG;
+ mbox->type3D.channel = sdev->channel;
+ mbox->type3D.target = sdev->id;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ mutex_unlock(&cb->dcmd_mutex);
+ } else {
+ struct pci_dev *pdev = cb->pdev;
+ unsigned char *rate;
+ dma_addr_t rate_addr;
+
+ if (status != MYRB_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Not Cancelled; not in progress\n");
+ return 0;
+ }
+
+ rate = dma_alloc_coherent(&pdev->dev, sizeof(char),
+ &rate_addr, GFP_KERNEL);
+ if (rate == NULL) {
+ sdev_printk(KERN_INFO, sdev,
+ "Cancellation of Rebuild Failed - Out of Memory\n");
+ return -ENOMEM;
+ }
+ mutex_lock(&cb->dcmd_mutex);
+ cmd_blk = &cb->dcmd_blk;
+ myrb_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL;
+ mbox->type3R.id = MYRB_DCMD_TAG;
+ mbox->type3R.rbld_rate = 0xFF;
+ mbox->type3R.addr = rate_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr);
+ mutex_unlock(&cb->dcmd_mutex);
+ }
+ if (status == MYRB_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev, "Rebuild %s\n",
+ start ? "Initiated" : "Cancelled");
+ return count;
+ }
+ if (!start) {
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Not Cancelled, status 0x%x\n",
+ status);
+ return -EIO;
+ }
+
+ switch (status) {
+ case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE:
+ msg = "Attempt to Rebuild Online or Unresponsive Drive";
+ break;
+ case MYRB_STATUS_RBLD_NEW_DISK_FAILED:
+ msg = "New Disk Failed During Rebuild";
+ break;
+ case MYRB_STATUS_INVALID_ADDRESS:
+ msg = "Invalid Device Address";
+ break;
+ case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS:
+ msg = "Already in Progress";
+ break;
+ default:
+ msg = NULL;
+ break;
+ }
+ if (msg)
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Failed - %s\n", msg);
+ else
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Failed, status 0x%x\n", status);
+
+ return -EIO;
+}
+static DEVICE_ATTR_RW(rebuild);
+
+static ssize_t consistency_check_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_rbld_progress rbld_buf;
+ struct myrb_cmdblk *cmd_blk;
+ union myrb_cmd_mbox *mbox;
+ unsigned short ldev_num = 0xFFFF;
+ unsigned short status;
+ int rc, start;
+ const char *msg;
+
+ rc = kstrtoint(buf, 0, &start);
+ if (rc)
+ return rc;
+
+ if (sdev->channel < myrb_logical_channel(sdev->host))
+ return -ENXIO;
+
+ status = myrb_get_rbld_progress(cb, &rbld_buf);
+ if (start) {
+ if (status == MYRB_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev,
+ "Check Consistency Not Initiated; already in progress\n");
+ return -EALREADY;
+ }
+ mutex_lock(&cb->dcmd_mutex);
+ cmd_blk = &cb->dcmd_blk;
+ myrb_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->type3C.opcode = MYRB_CMD_CHECK_CONSISTENCY_ASYNC;
+ mbox->type3C.id = MYRB_DCMD_TAG;
+ mbox->type3C.ldev_num = sdev->id;
+ mbox->type3C.auto_restore = true;
+
+ status = myrb_exec_cmd(cb, cmd_blk);
+ mutex_unlock(&cb->dcmd_mutex);
+ } else {
+ struct pci_dev *pdev = cb->pdev;
+ unsigned char *rate;
+ dma_addr_t rate_addr;
+
+ if (ldev_num != sdev->id) {
+ sdev_printk(KERN_INFO, sdev,
+ "Check Consistency Not Cancelled; not in progress\n");
+ return 0;
+ }
+ rate = dma_alloc_coherent(&pdev->dev, sizeof(char),
+ &rate_addr, GFP_KERNEL);
+ if (rate == NULL) {
+ sdev_printk(KERN_INFO, sdev,
+ "Cancellation of Check Consistency Failed - Out of Memory\n");
+ return -ENOMEM;
+ }
+ mutex_lock(&cb->dcmd_mutex);
+ cmd_blk = &cb->dcmd_blk;
+ myrb_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL;
+ mbox->type3R.id = MYRB_DCMD_TAG;
+ mbox->type3R.rbld_rate = 0xFF;
+ mbox->type3R.addr = rate_addr;
+ status = myrb_exec_cmd(cb, cmd_blk);
+ dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr);
+ mutex_unlock(&cb->dcmd_mutex);
+ }
+ if (status == MYRB_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev, "Check Consistency %s\n",
+ start ? "Initiated" : "Cancelled");
+ return count;
+ }
+ if (!start) {
+ sdev_printk(KERN_INFO, sdev,
+ "Check Consistency Not Cancelled, status 0x%x\n",
+ status);
+ return -EIO;
+ }
+
+ switch (status) {
+ case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE:
+ msg = "Dependent Physical Device is DEAD";
+ break;
+ case MYRB_STATUS_RBLD_NEW_DISK_FAILED:
+ msg = "New Disk Failed During Rebuild";
+ break;
+ case MYRB_STATUS_INVALID_ADDRESS:
+ msg = "Invalid or Nonredundant Logical Drive";
+ break;
+ case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS:
+ msg = "Already in Progress";
+ break;
+ default:
+ msg = NULL;
+ break;
+ }
+ if (msg)
+ sdev_printk(KERN_INFO, sdev,
+ "Check Consistency Failed - %s\n", msg);
+ else
+ sdev_printk(KERN_INFO, sdev,
+ "Check Consistency Failed, status 0x%x\n", status);
+
+ return -EIO;
+}
+
+static ssize_t consistency_check_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return rebuild_show(dev, attr, buf);
+}
+static DEVICE_ATTR_RW(consistency_check);
+
+static ssize_t ctlr_num_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrb_hba *cb = shost_priv(shost);
+
+ return snprintf(buf, 20, "%u\n", cb->ctlr_num);
+}
+static DEVICE_ATTR_RO(ctlr_num);
+
+static ssize_t firmware_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrb_hba *cb = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", cb->fw_version);
+}
+static DEVICE_ATTR_RO(firmware);
+
+static ssize_t model_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrb_hba *cb = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", cb->model_name);
+}
+static DEVICE_ATTR_RO(model);
+
+static ssize_t flush_cache_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrb_hba *cb = shost_priv(shost);
+ unsigned short status;
+
+ status = myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0);
+ if (status == MYRB_STATUS_SUCCESS) {
+ shost_printk(KERN_INFO, shost,
+ "Cache Flush Completed\n");
+ return count;
+ }
+ shost_printk(KERN_INFO, shost,
+ "Cache Flush Failed, status %x\n", status);
+ return -EIO;
+}
+static DEVICE_ATTR_WO(flush_cache);
+
+static struct attribute *myrb_sdev_attrs[] = {
+ &dev_attr_rebuild.attr,
+ &dev_attr_consistency_check.attr,
+ &dev_attr_raid_state.attr,
+ &dev_attr_raid_level.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(myrb_sdev);
+
+static struct attribute *myrb_shost_attrs[] = {
+ &dev_attr_ctlr_num.attr,
+ &dev_attr_model.attr,
+ &dev_attr_firmware.attr,
+ &dev_attr_flush_cache.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(myrb_shost);
+
+static const struct scsi_host_template myrb_template = {
+ .module = THIS_MODULE,
+ .name = "DAC960",
+ .proc_name = "myrb",
+ .queuecommand = myrb_queuecommand,
+ .eh_host_reset_handler = myrb_host_reset,
+ .sdev_init = myrb_sdev_init,
+ .sdev_configure = myrb_sdev_configure,
+ .sdev_destroy = myrb_sdev_destroy,
+ .bios_param = myrb_biosparam,
+ .cmd_size = sizeof(struct myrb_cmdblk),
+ .shost_groups = myrb_shost_groups,
+ .sdev_groups = myrb_sdev_groups,
+ .this_id = -1,
+};
+
+/**
+ * myrb_is_raid - return boolean indicating device is raid volume
+ * @dev: the device struct object
+ */
+static int myrb_is_raid(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return sdev->channel == myrb_logical_channel(sdev->host);
+}
+
+/**
+ * myrb_get_resync - get raid volume resync percent complete
+ * @dev: the device struct object
+ */
+static void myrb_get_resync(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_rbld_progress rbld_buf;
+ unsigned int percent_complete = 0;
+ unsigned short status;
+ unsigned int ldev_size = 0, remaining = 0;
+
+ if (sdev->channel < myrb_logical_channel(sdev->host))
+ return;
+ status = myrb_get_rbld_progress(cb, &rbld_buf);
+ if (status == MYRB_STATUS_SUCCESS) {
+ if (rbld_buf.ldev_num == sdev->id) {
+ ldev_size = rbld_buf.ldev_size;
+ remaining = rbld_buf.blocks_left;
+ }
+ }
+ if (remaining && ldev_size)
+ percent_complete = (ldev_size - remaining) * 100 / ldev_size;
+ raid_set_resync(myrb_raid_template, dev, percent_complete);
+}
+
+/**
+ * myrb_get_state - get raid volume status
+ * @dev: the device struct object
+ */
+static void myrb_get_state(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrb_hba *cb = shost_priv(sdev->host);
+ struct myrb_ldev_info *ldev_info = sdev->hostdata;
+ enum raid_state state = RAID_STATE_UNKNOWN;
+ unsigned short status;
+
+ if (sdev->channel < myrb_logical_channel(sdev->host) || !ldev_info)
+ state = RAID_STATE_UNKNOWN;
+ else {
+ status = myrb_get_rbld_progress(cb, NULL);
+ if (status == MYRB_STATUS_SUCCESS)
+ state = RAID_STATE_RESYNCING;
+ else {
+ switch (ldev_info->state) {
+ case MYRB_DEVICE_ONLINE:
+ state = RAID_STATE_ACTIVE;
+ break;
+ case MYRB_DEVICE_WO:
+ case MYRB_DEVICE_CRITICAL:
+ state = RAID_STATE_DEGRADED;
+ break;
+ default:
+ state = RAID_STATE_OFFLINE;
+ }
+ }
+ }
+ raid_set_state(myrb_raid_template, dev, state);
+}
+
+static struct raid_function_template myrb_raid_functions = {
+ .cookie = &myrb_template,
+ .is_raid = myrb_is_raid,
+ .get_resync = myrb_get_resync,
+ .get_state = myrb_get_state,
+};
+
+static void myrb_handle_scsi(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk,
+ struct scsi_cmnd *scmd)
+{
+ unsigned short status;
+
+ if (!cmd_blk)
+ return;
+
+ scsi_dma_unmap(scmd);
+
+ if (cmd_blk->dcdb) {
+ memcpy(scmd->sense_buffer, &cmd_blk->dcdb->sense, 64);
+ dma_pool_free(cb->dcdb_pool, cmd_blk->dcdb,
+ cmd_blk->dcdb_addr);
+ cmd_blk->dcdb = NULL;
+ }
+ if (cmd_blk->sgl) {
+ dma_pool_free(cb->sg_pool, cmd_blk->sgl, cmd_blk->sgl_addr);
+ cmd_blk->sgl = NULL;
+ cmd_blk->sgl_addr = 0;
+ }
+ status = cmd_blk->status;
+ switch (status) {
+ case MYRB_STATUS_SUCCESS:
+ case MYRB_STATUS_DEVICE_BUSY:
+ scmd->result = (DID_OK << 16) | status;
+ break;
+ case MYRB_STATUS_BAD_DATA:
+ dev_dbg(&scmd->device->sdev_gendev,
+ "Bad Data Encountered\n");
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ /* Unrecovered read error */
+ scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0);
+ else
+ /* Write error */
+ scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0);
+ break;
+ case MYRB_STATUS_IRRECOVERABLE_DATA_ERROR:
+ scmd_printk(KERN_ERR, scmd, "Irrecoverable Data Error\n");
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ /* Unrecovered read error, auto-reallocation failed */
+ scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0x04);
+ else
+ /* Write error, auto-reallocation failed */
+ scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0x02);
+ break;
+ case MYRB_STATUS_LDRV_NONEXISTENT_OR_OFFLINE:
+ dev_dbg(&scmd->device->sdev_gendev,
+ "Logical Drive Nonexistent or Offline");
+ scmd->result = (DID_BAD_TARGET << 16);
+ break;
+ case MYRB_STATUS_ACCESS_BEYOND_END_OF_LDRV:
+ dev_dbg(&scmd->device->sdev_gendev,
+ "Attempt to Access Beyond End of Logical Drive");
+ /* Logical block address out of range */
+ scsi_build_sense(scmd, 0, NOT_READY, 0x21, 0);
+ break;
+ case MYRB_STATUS_DEVICE_NONRESPONSIVE:
+ dev_dbg(&scmd->device->sdev_gendev, "Device nonresponsive\n");
+ scmd->result = (DID_BAD_TARGET << 16);
+ break;
+ default:
+ scmd_printk(KERN_ERR, scmd,
+ "Unexpected Error Status %04X", status);
+ scmd->result = (DID_ERROR << 16);
+ break;
+ }
+ scsi_done(scmd);
+}
+
+static void myrb_handle_cmdblk(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk)
+{
+ if (!cmd_blk)
+ return;
+
+ if (cmd_blk->completion) {
+ complete(cmd_blk->completion);
+ cmd_blk->completion = NULL;
+ }
+}
+
+static void myrb_monitor(struct work_struct *work)
+{
+ struct myrb_hba *cb = container_of(work,
+ struct myrb_hba, monitor_work.work);
+ struct Scsi_Host *shost = cb->host;
+ unsigned long interval = MYRB_PRIMARY_MONITOR_INTERVAL;
+
+ dev_dbg(&shost->shost_gendev, "monitor tick\n");
+
+ if (cb->new_ev_seq > cb->old_ev_seq) {
+ int event = cb->old_ev_seq;
+
+ dev_dbg(&shost->shost_gendev,
+ "get event log no %d/%d\n",
+ cb->new_ev_seq, event);
+ myrb_get_event(cb, event);
+ cb->old_ev_seq = event + 1;
+ interval = 10;
+ } else if (cb->need_err_info) {
+ cb->need_err_info = false;
+ dev_dbg(&shost->shost_gendev, "get error table\n");
+ myrb_get_errtable(cb);
+ interval = 10;
+ } else if (cb->need_rbld && cb->rbld_first) {
+ cb->need_rbld = false;
+ dev_dbg(&shost->shost_gendev,
+ "get rebuild progress\n");
+ myrb_update_rbld_progress(cb);
+ interval = 10;
+ } else if (cb->need_ldev_info) {
+ cb->need_ldev_info = false;
+ dev_dbg(&shost->shost_gendev,
+ "get logical drive info\n");
+ myrb_get_ldev_info(cb);
+ interval = 10;
+ } else if (cb->need_rbld) {
+ cb->need_rbld = false;
+ dev_dbg(&shost->shost_gendev,
+ "get rebuild progress\n");
+ myrb_update_rbld_progress(cb);
+ interval = 10;
+ } else if (cb->need_cc_status) {
+ cb->need_cc_status = false;
+ dev_dbg(&shost->shost_gendev,
+ "get consistency check progress\n");
+ myrb_get_cc_progress(cb);
+ interval = 10;
+ } else if (cb->need_bgi_status) {
+ cb->need_bgi_status = false;
+ dev_dbg(&shost->shost_gendev, "get background init status\n");
+ myrb_bgi_control(cb);
+ interval = 10;
+ } else {
+ dev_dbg(&shost->shost_gendev, "new enquiry\n");
+ mutex_lock(&cb->dma_mutex);
+ myrb_hba_enquiry(cb);
+ mutex_unlock(&cb->dma_mutex);
+ if ((cb->new_ev_seq - cb->old_ev_seq > 0) ||
+ cb->need_err_info || cb->need_rbld ||
+ cb->need_ldev_info || cb->need_cc_status ||
+ cb->need_bgi_status) {
+ dev_dbg(&shost->shost_gendev,
+ "reschedule monitor\n");
+ interval = 0;
+ }
+ }
+ if (interval > 1)
+ cb->primary_monitor_time = jiffies;
+ queue_delayed_work(cb->work_q, &cb->monitor_work, interval);
+}
+
+/*
+ * myrb_err_status - reports controller BIOS messages
+ *
+ * Controller BIOS messages are passed through the Error Status Register
+ * when the driver performs the BIOS handshaking.
+ *
+ * Return: true for fatal errors and false otherwise.
+ */
+static bool myrb_err_status(struct myrb_hba *cb, unsigned char error,
+ unsigned char parm0, unsigned char parm1)
+{
+ struct pci_dev *pdev = cb->pdev;
+
+ switch (error) {
+ case 0x00:
+ dev_info(&pdev->dev,
+ "Physical Device %d:%d Not Responding\n",
+ parm1, parm0);
+ break;
+ case 0x08:
+ dev_notice(&pdev->dev, "Spinning Up Drives\n");
+ break;
+ case 0x30:
+ dev_notice(&pdev->dev, "Configuration Checksum Error\n");
+ break;
+ case 0x60:
+ dev_notice(&pdev->dev, "Mirror Race Recovery Failed\n");
+ break;
+ case 0x70:
+ dev_notice(&pdev->dev, "Mirror Race Recovery In Progress\n");
+ break;
+ case 0x90:
+ dev_notice(&pdev->dev, "Physical Device %d:%d COD Mismatch\n",
+ parm1, parm0);
+ break;
+ case 0xA0:
+ dev_notice(&pdev->dev, "Logical Drive Installation Aborted\n");
+ break;
+ case 0xB0:
+ dev_notice(&pdev->dev, "Mirror Race On A Critical Logical Drive\n");
+ break;
+ case 0xD0:
+ dev_notice(&pdev->dev, "New Controller Configuration Found\n");
+ break;
+ case 0xF0:
+ dev_err(&pdev->dev, "Fatal Memory Parity Error\n");
+ return true;
+ default:
+ dev_err(&pdev->dev, "Unknown Initialization Error %02X\n",
+ error);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Hardware-specific functions
+ */
+
+/*
+ * DAC960 LA Series Controllers
+ */
+
+static inline void DAC960_LA_hw_mbox_new_cmd(void __iomem *base)
+{
+ writeb(DAC960_LA_IDB_HWMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET);
+}
+
+static inline void DAC960_LA_ack_hw_mbox_status(void __iomem *base)
+{
+ writeb(DAC960_LA_IDB_HWMBOX_ACK_STS, base + DAC960_LA_IDB_OFFSET);
+}
+
+static inline void DAC960_LA_reset_ctrl(void __iomem *base)
+{
+ writeb(DAC960_LA_IDB_CTRL_RESET, base + DAC960_LA_IDB_OFFSET);
+}
+
+static inline void DAC960_LA_mem_mbox_new_cmd(void __iomem *base)
+{
+ writeb(DAC960_LA_IDB_MMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET);
+}
+
+static inline bool DAC960_LA_hw_mbox_is_full(void __iomem *base)
+{
+ unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET);
+
+ return !(idb & DAC960_LA_IDB_HWMBOX_EMPTY);
+}
+
+static inline bool DAC960_LA_init_in_progress(void __iomem *base)
+{
+ unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET);
+
+ return !(idb & DAC960_LA_IDB_INIT_DONE);
+}
+
+static inline void DAC960_LA_ack_hw_mbox_intr(void __iomem *base)
+{
+ writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ, base + DAC960_LA_ODB_OFFSET);
+}
+
+static inline void DAC960_LA_ack_intr(void __iomem *base)
+{
+ writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ | DAC960_LA_ODB_MMBOX_ACK_IRQ,
+ base + DAC960_LA_ODB_OFFSET);
+}
+
+static inline bool DAC960_LA_hw_mbox_status_available(void __iomem *base)
+{
+ unsigned char odb = readb(base + DAC960_LA_ODB_OFFSET);
+
+ return odb & DAC960_LA_ODB_HWMBOX_STS_AVAIL;
+}
+
+static inline void DAC960_LA_enable_intr(void __iomem *base)
+{
+ unsigned char odb = 0xFF;
+
+ odb &= ~DAC960_LA_IRQMASK_DISABLE_IRQ;
+ writeb(odb, base + DAC960_LA_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_LA_disable_intr(void __iomem *base)
+{
+ unsigned char odb = 0xFF;
+
+ odb |= DAC960_LA_IRQMASK_DISABLE_IRQ;
+ writeb(odb, base + DAC960_LA_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_LA_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox,
+ union myrb_cmd_mbox *mbox)
+{
+ mem_mbox->words[1] = mbox->words[1];
+ mem_mbox->words[2] = mbox->words[2];
+ mem_mbox->words[3] = mbox->words[3];
+ /* Memory barrier to prevent reordering */
+ wmb();
+ mem_mbox->words[0] = mbox->words[0];
+ /* Memory barrier to force PCI access */
+ mb();
+}
+
+static inline void DAC960_LA_write_hw_mbox(void __iomem *base,
+ union myrb_cmd_mbox *mbox)
+{
+ writel(mbox->words[0], base + DAC960_LA_CMDOP_OFFSET);
+ writel(mbox->words[1], base + DAC960_LA_MBOX4_OFFSET);
+ writel(mbox->words[2], base + DAC960_LA_MBOX8_OFFSET);
+ writeb(mbox->bytes[12], base + DAC960_LA_MBOX12_OFFSET);
+}
+
+static inline unsigned short DAC960_LA_read_status(void __iomem *base)
+{
+ return readw(base + DAC960_LA_STS_OFFSET);
+}
+
+static inline bool
+DAC960_LA_read_error_status(void __iomem *base, unsigned char *error,
+ unsigned char *param0, unsigned char *param1)
+{
+ unsigned char errsts = readb(base + DAC960_LA_ERRSTS_OFFSET);
+
+ if (!(errsts & DAC960_LA_ERRSTS_PENDING))
+ return false;
+ errsts &= ~DAC960_LA_ERRSTS_PENDING;
+
+ *error = errsts;
+ *param0 = readb(base + DAC960_LA_CMDOP_OFFSET);
+ *param1 = readb(base + DAC960_LA_CMDID_OFFSET);
+ writeb(0xFF, base + DAC960_LA_ERRSTS_OFFSET);
+ return true;
+}
+
+static inline unsigned short
+DAC960_LA_mbox_init(struct pci_dev *pdev, void __iomem *base,
+ union myrb_cmd_mbox *mbox)
+{
+ unsigned short status;
+ int timeout = 0;
+
+ while (timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (!DAC960_LA_hw_mbox_is_full(base))
+ break;
+ udelay(10);
+ timeout++;
+ }
+ if (DAC960_LA_hw_mbox_is_full(base)) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for empty mailbox\n");
+ return MYRB_STATUS_SUBSYS_TIMEOUT;
+ }
+ DAC960_LA_write_hw_mbox(base, mbox);
+ DAC960_LA_hw_mbox_new_cmd(base);
+ timeout = 0;
+ while (timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (DAC960_LA_hw_mbox_status_available(base))
+ break;
+ udelay(10);
+ timeout++;
+ }
+ if (!DAC960_LA_hw_mbox_status_available(base)) {
+ dev_err(&pdev->dev, "Timeout waiting for mailbox status\n");
+ return MYRB_STATUS_SUBSYS_TIMEOUT;
+ }
+ status = DAC960_LA_read_status(base);
+ DAC960_LA_ack_hw_mbox_intr(base);
+ DAC960_LA_ack_hw_mbox_status(base);
+
+ return status;
+}
+
+static int DAC960_LA_hw_init(struct pci_dev *pdev,
+ struct myrb_hba *cb, void __iomem *base)
+{
+ int timeout = 0;
+ unsigned char error, parm0, parm1;
+
+ DAC960_LA_disable_intr(base);
+ DAC960_LA_ack_hw_mbox_status(base);
+ udelay(1000);
+ while (DAC960_LA_init_in_progress(base) &&
+ timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (DAC960_LA_read_error_status(base, &error,
+ &parm0, &parm1) &&
+ myrb_err_status(cb, error, parm0, parm1))
+ return -ENODEV;
+ udelay(10);
+ timeout++;
+ }
+ if (timeout == MYRB_MAILBOX_TIMEOUT) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for Controller Initialisation\n");
+ return -ETIMEDOUT;
+ }
+ if (!myrb_enable_mmio(cb, DAC960_LA_mbox_init)) {
+ dev_err(&pdev->dev,
+ "Unable to Enable Memory Mailbox Interface\n");
+ DAC960_LA_reset_ctrl(base);
+ return -ENODEV;
+ }
+ DAC960_LA_enable_intr(base);
+ cb->qcmd = myrb_qcmd;
+ cb->write_cmd_mbox = DAC960_LA_write_cmd_mbox;
+ if (cb->dual_mode_interface)
+ cb->get_cmd_mbox = DAC960_LA_mem_mbox_new_cmd;
+ else
+ cb->get_cmd_mbox = DAC960_LA_hw_mbox_new_cmd;
+ cb->disable_intr = DAC960_LA_disable_intr;
+ cb->reset = DAC960_LA_reset_ctrl;
+
+ return 0;
+}
+
+static irqreturn_t DAC960_LA_intr_handler(int irq, void *arg)
+{
+ struct myrb_hba *cb = arg;
+ void __iomem *base = cb->io_base;
+ struct myrb_stat_mbox *next_stat_mbox;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cb->queue_lock, flags);
+ DAC960_LA_ack_intr(base);
+ next_stat_mbox = cb->next_stat_mbox;
+ while (next_stat_mbox->valid) {
+ unsigned char id = next_stat_mbox->id;
+ struct scsi_cmnd *scmd = NULL;
+ struct myrb_cmdblk *cmd_blk = NULL;
+
+ if (id == MYRB_DCMD_TAG)
+ cmd_blk = &cb->dcmd_blk;
+ else if (id == MYRB_MCMD_TAG)
+ cmd_blk = &cb->mcmd_blk;
+ else {
+ scmd = scsi_host_find_tag(cb->host, id - 3);
+ if (scmd)
+ cmd_blk = scsi_cmd_priv(scmd);
+ }
+ if (cmd_blk)
+ cmd_blk->status = next_stat_mbox->status;
+ else
+ dev_err(&cb->pdev->dev,
+ "Unhandled command completion %d\n", id);
+
+ memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox));
+ if (++next_stat_mbox > cb->last_stat_mbox)
+ next_stat_mbox = cb->first_stat_mbox;
+
+ if (cmd_blk) {
+ if (id < 3)
+ myrb_handle_cmdblk(cb, cmd_blk);
+ else
+ myrb_handle_scsi(cb, cmd_blk, scmd);
+ }
+ }
+ cb->next_stat_mbox = next_stat_mbox;
+ spin_unlock_irqrestore(&cb->queue_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct myrb_privdata DAC960_LA_privdata = {
+ .hw_init = DAC960_LA_hw_init,
+ .irq_handler = DAC960_LA_intr_handler,
+ .mmio_size = DAC960_LA_mmio_size,
+};
+
+/*
+ * DAC960 PG Series Controllers
+ */
+static inline void DAC960_PG_hw_mbox_new_cmd(void __iomem *base)
+{
+ writel(DAC960_PG_IDB_HWMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET);
+}
+
+static inline void DAC960_PG_ack_hw_mbox_status(void __iomem *base)
+{
+ writel(DAC960_PG_IDB_HWMBOX_ACK_STS, base + DAC960_PG_IDB_OFFSET);
+}
+
+static inline void DAC960_PG_reset_ctrl(void __iomem *base)
+{
+ writel(DAC960_PG_IDB_CTRL_RESET, base + DAC960_PG_IDB_OFFSET);
+}
+
+static inline void DAC960_PG_mem_mbox_new_cmd(void __iomem *base)
+{
+ writel(DAC960_PG_IDB_MMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET);
+}
+
+static inline bool DAC960_PG_hw_mbox_is_full(void __iomem *base)
+{
+ unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET);
+
+ return idb & DAC960_PG_IDB_HWMBOX_FULL;
+}
+
+static inline bool DAC960_PG_init_in_progress(void __iomem *base)
+{
+ unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET);
+
+ return idb & DAC960_PG_IDB_INIT_IN_PROGRESS;
+}
+
+static inline void DAC960_PG_ack_hw_mbox_intr(void __iomem *base)
+{
+ writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ, base + DAC960_PG_ODB_OFFSET);
+}
+
+static inline void DAC960_PG_ack_intr(void __iomem *base)
+{
+ writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ | DAC960_PG_ODB_MMBOX_ACK_IRQ,
+ base + DAC960_PG_ODB_OFFSET);
+}
+
+static inline bool DAC960_PG_hw_mbox_status_available(void __iomem *base)
+{
+ unsigned char odb = readl(base + DAC960_PG_ODB_OFFSET);
+
+ return odb & DAC960_PG_ODB_HWMBOX_STS_AVAIL;
+}
+
+static inline void DAC960_PG_enable_intr(void __iomem *base)
+{
+ unsigned int imask = (unsigned int)-1;
+
+ imask &= ~DAC960_PG_IRQMASK_DISABLE_IRQ;
+ writel(imask, base + DAC960_PG_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_PG_disable_intr(void __iomem *base)
+{
+ unsigned int imask = (unsigned int)-1;
+
+ writel(imask, base + DAC960_PG_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_PG_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox,
+ union myrb_cmd_mbox *mbox)
+{
+ mem_mbox->words[1] = mbox->words[1];
+ mem_mbox->words[2] = mbox->words[2];
+ mem_mbox->words[3] = mbox->words[3];
+ /* Memory barrier to prevent reordering */
+ wmb();
+ mem_mbox->words[0] = mbox->words[0];
+ /* Memory barrier to force PCI access */
+ mb();
+}
+
+static inline void DAC960_PG_write_hw_mbox(void __iomem *base,
+ union myrb_cmd_mbox *mbox)
+{
+ writel(mbox->words[0], base + DAC960_PG_CMDOP_OFFSET);
+ writel(mbox->words[1], base + DAC960_PG_MBOX4_OFFSET);
+ writel(mbox->words[2], base + DAC960_PG_MBOX8_OFFSET);
+ writeb(mbox->bytes[12], base + DAC960_PG_MBOX12_OFFSET);
+}
+
+static inline unsigned short
+DAC960_PG_read_status(void __iomem *base)
+{
+ return readw(base + DAC960_PG_STS_OFFSET);
+}
+
+static inline bool
+DAC960_PG_read_error_status(void __iomem *base, unsigned char *error,
+ unsigned char *param0, unsigned char *param1)
+{
+ unsigned char errsts = readb(base + DAC960_PG_ERRSTS_OFFSET);
+
+ if (!(errsts & DAC960_PG_ERRSTS_PENDING))
+ return false;
+ errsts &= ~DAC960_PG_ERRSTS_PENDING;
+ *error = errsts;
+ *param0 = readb(base + DAC960_PG_CMDOP_OFFSET);
+ *param1 = readb(base + DAC960_PG_CMDID_OFFSET);
+ writeb(0, base + DAC960_PG_ERRSTS_OFFSET);
+ return true;
+}
+
+static inline unsigned short
+DAC960_PG_mbox_init(struct pci_dev *pdev, void __iomem *base,
+ union myrb_cmd_mbox *mbox)
+{
+ unsigned short status;
+ int timeout = 0;
+
+ while (timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (!DAC960_PG_hw_mbox_is_full(base))
+ break;
+ udelay(10);
+ timeout++;
+ }
+ if (DAC960_PG_hw_mbox_is_full(base)) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for empty mailbox\n");
+ return MYRB_STATUS_SUBSYS_TIMEOUT;
+ }
+ DAC960_PG_write_hw_mbox(base, mbox);
+ DAC960_PG_hw_mbox_new_cmd(base);
+
+ timeout = 0;
+ while (timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (DAC960_PG_hw_mbox_status_available(base))
+ break;
+ udelay(10);
+ timeout++;
+ }
+ if (!DAC960_PG_hw_mbox_status_available(base)) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for mailbox status\n");
+ return MYRB_STATUS_SUBSYS_TIMEOUT;
+ }
+ status = DAC960_PG_read_status(base);
+ DAC960_PG_ack_hw_mbox_intr(base);
+ DAC960_PG_ack_hw_mbox_status(base);
+
+ return status;
+}
+
+static int DAC960_PG_hw_init(struct pci_dev *pdev,
+ struct myrb_hba *cb, void __iomem *base)
+{
+ int timeout = 0;
+ unsigned char error, parm0, parm1;
+
+ DAC960_PG_disable_intr(base);
+ DAC960_PG_ack_hw_mbox_status(base);
+ udelay(1000);
+ while (DAC960_PG_init_in_progress(base) &&
+ timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (DAC960_PG_read_error_status(base, &error,
+ &parm0, &parm1) &&
+ myrb_err_status(cb, error, parm0, parm1))
+ return -EIO;
+ udelay(10);
+ timeout++;
+ }
+ if (timeout == MYRB_MAILBOX_TIMEOUT) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for Controller Initialisation\n");
+ return -ETIMEDOUT;
+ }
+ if (!myrb_enable_mmio(cb, DAC960_PG_mbox_init)) {
+ dev_err(&pdev->dev,
+ "Unable to Enable Memory Mailbox Interface\n");
+ DAC960_PG_reset_ctrl(base);
+ return -ENODEV;
+ }
+ DAC960_PG_enable_intr(base);
+ cb->qcmd = myrb_qcmd;
+ cb->write_cmd_mbox = DAC960_PG_write_cmd_mbox;
+ if (cb->dual_mode_interface)
+ cb->get_cmd_mbox = DAC960_PG_mem_mbox_new_cmd;
+ else
+ cb->get_cmd_mbox = DAC960_PG_hw_mbox_new_cmd;
+ cb->disable_intr = DAC960_PG_disable_intr;
+ cb->reset = DAC960_PG_reset_ctrl;
+
+ return 0;
+}
+
+static irqreturn_t DAC960_PG_intr_handler(int irq, void *arg)
+{
+ struct myrb_hba *cb = arg;
+ void __iomem *base = cb->io_base;
+ struct myrb_stat_mbox *next_stat_mbox;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cb->queue_lock, flags);
+ DAC960_PG_ack_intr(base);
+ next_stat_mbox = cb->next_stat_mbox;
+ while (next_stat_mbox->valid) {
+ unsigned char id = next_stat_mbox->id;
+ struct scsi_cmnd *scmd = NULL;
+ struct myrb_cmdblk *cmd_blk = NULL;
+
+ if (id == MYRB_DCMD_TAG)
+ cmd_blk = &cb->dcmd_blk;
+ else if (id == MYRB_MCMD_TAG)
+ cmd_blk = &cb->mcmd_blk;
+ else {
+ scmd = scsi_host_find_tag(cb->host, id - 3);
+ if (scmd)
+ cmd_blk = scsi_cmd_priv(scmd);
+ }
+ if (cmd_blk)
+ cmd_blk->status = next_stat_mbox->status;
+ else
+ dev_err(&cb->pdev->dev,
+ "Unhandled command completion %d\n", id);
+
+ memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox));
+ if (++next_stat_mbox > cb->last_stat_mbox)
+ next_stat_mbox = cb->first_stat_mbox;
+
+ if (id < 3)
+ myrb_handle_cmdblk(cb, cmd_blk);
+ else
+ myrb_handle_scsi(cb, cmd_blk, scmd);
+ }
+ cb->next_stat_mbox = next_stat_mbox;
+ spin_unlock_irqrestore(&cb->queue_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct myrb_privdata DAC960_PG_privdata = {
+ .hw_init = DAC960_PG_hw_init,
+ .irq_handler = DAC960_PG_intr_handler,
+ .mmio_size = DAC960_PG_mmio_size,
+};
+
+
+/*
+ * DAC960 PD Series Controllers
+ */
+
+static inline void DAC960_PD_hw_mbox_new_cmd(void __iomem *base)
+{
+ writeb(DAC960_PD_IDB_HWMBOX_NEW_CMD, base + DAC960_PD_IDB_OFFSET);
+}
+
+static inline void DAC960_PD_ack_hw_mbox_status(void __iomem *base)
+{
+ writeb(DAC960_PD_IDB_HWMBOX_ACK_STS, base + DAC960_PD_IDB_OFFSET);
+}
+
+static inline void DAC960_PD_reset_ctrl(void __iomem *base)
+{
+ writeb(DAC960_PD_IDB_CTRL_RESET, base + DAC960_PD_IDB_OFFSET);
+}
+
+static inline bool DAC960_PD_hw_mbox_is_full(void __iomem *base)
+{
+ unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET);
+
+ return idb & DAC960_PD_IDB_HWMBOX_FULL;
+}
+
+static inline bool DAC960_PD_init_in_progress(void __iomem *base)
+{
+ unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET);
+
+ return idb & DAC960_PD_IDB_INIT_IN_PROGRESS;
+}
+
+static inline void DAC960_PD_ack_intr(void __iomem *base)
+{
+ writeb(DAC960_PD_ODB_HWMBOX_ACK_IRQ, base + DAC960_PD_ODB_OFFSET);
+}
+
+static inline bool DAC960_PD_hw_mbox_status_available(void __iomem *base)
+{
+ unsigned char odb = readb(base + DAC960_PD_ODB_OFFSET);
+
+ return odb & DAC960_PD_ODB_HWMBOX_STS_AVAIL;
+}
+
+static inline void DAC960_PD_enable_intr(void __iomem *base)
+{
+ writeb(DAC960_PD_IRQMASK_ENABLE_IRQ, base + DAC960_PD_IRQEN_OFFSET);
+}
+
+static inline void DAC960_PD_disable_intr(void __iomem *base)
+{
+ writeb(0, base + DAC960_PD_IRQEN_OFFSET);
+}
+
+static inline void DAC960_PD_write_cmd_mbox(void __iomem *base,
+ union myrb_cmd_mbox *mbox)
+{
+ writel(mbox->words[0], base + DAC960_PD_CMDOP_OFFSET);
+ writel(mbox->words[1], base + DAC960_PD_MBOX4_OFFSET);
+ writel(mbox->words[2], base + DAC960_PD_MBOX8_OFFSET);
+ writeb(mbox->bytes[12], base + DAC960_PD_MBOX12_OFFSET);
+}
+
+static inline unsigned char
+DAC960_PD_read_status_cmd_ident(void __iomem *base)
+{
+ return readb(base + DAC960_PD_STSID_OFFSET);
+}
+
+static inline unsigned short
+DAC960_PD_read_status(void __iomem *base)
+{
+ return readw(base + DAC960_PD_STS_OFFSET);
+}
+
+static inline bool
+DAC960_PD_read_error_status(void __iomem *base, unsigned char *error,
+ unsigned char *param0, unsigned char *param1)
+{
+ unsigned char errsts = readb(base + DAC960_PD_ERRSTS_OFFSET);
+
+ if (!(errsts & DAC960_PD_ERRSTS_PENDING))
+ return false;
+ errsts &= ~DAC960_PD_ERRSTS_PENDING;
+ *error = errsts;
+ *param0 = readb(base + DAC960_PD_CMDOP_OFFSET);
+ *param1 = readb(base + DAC960_PD_CMDID_OFFSET);
+ writeb(0, base + DAC960_PD_ERRSTS_OFFSET);
+ return true;
+}
+
+static void DAC960_PD_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk)
+{
+ void __iomem *base = cb->io_base;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+
+ while (DAC960_PD_hw_mbox_is_full(base))
+ udelay(1);
+ DAC960_PD_write_cmd_mbox(base, mbox);
+ DAC960_PD_hw_mbox_new_cmd(base);
+}
+
+static int DAC960_PD_hw_init(struct pci_dev *pdev,
+ struct myrb_hba *cb, void __iomem *base)
+{
+ int timeout = 0;
+ unsigned char error, parm0, parm1;
+
+ if (!request_region(cb->io_addr, 0x80, "myrb")) {
+ dev_err(&pdev->dev, "IO port 0x%lx busy\n",
+ (unsigned long)cb->io_addr);
+ return -EBUSY;
+ }
+ DAC960_PD_disable_intr(base);
+ DAC960_PD_ack_hw_mbox_status(base);
+ udelay(1000);
+ while (DAC960_PD_init_in_progress(base) &&
+ timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (DAC960_PD_read_error_status(base, &error,
+ &parm0, &parm1) &&
+ myrb_err_status(cb, error, parm0, parm1))
+ return -EIO;
+ udelay(10);
+ timeout++;
+ }
+ if (timeout == MYRB_MAILBOX_TIMEOUT) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for Controller Initialisation\n");
+ return -ETIMEDOUT;
+ }
+ if (!myrb_enable_mmio(cb, NULL)) {
+ dev_err(&pdev->dev,
+ "Unable to Enable Memory Mailbox Interface\n");
+ DAC960_PD_reset_ctrl(base);
+ return -ENODEV;
+ }
+ DAC960_PD_enable_intr(base);
+ cb->qcmd = DAC960_PD_qcmd;
+ cb->disable_intr = DAC960_PD_disable_intr;
+ cb->reset = DAC960_PD_reset_ctrl;
+
+ return 0;
+}
+
+static irqreturn_t DAC960_PD_intr_handler(int irq, void *arg)
+{
+ struct myrb_hba *cb = arg;
+ void __iomem *base = cb->io_base;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cb->queue_lock, flags);
+ while (DAC960_PD_hw_mbox_status_available(base)) {
+ unsigned char id = DAC960_PD_read_status_cmd_ident(base);
+ struct scsi_cmnd *scmd = NULL;
+ struct myrb_cmdblk *cmd_blk = NULL;
+
+ if (id == MYRB_DCMD_TAG)
+ cmd_blk = &cb->dcmd_blk;
+ else if (id == MYRB_MCMD_TAG)
+ cmd_blk = &cb->mcmd_blk;
+ else {
+ scmd = scsi_host_find_tag(cb->host, id - 3);
+ if (scmd)
+ cmd_blk = scsi_cmd_priv(scmd);
+ }
+ if (cmd_blk)
+ cmd_blk->status = DAC960_PD_read_status(base);
+ else
+ dev_err(&cb->pdev->dev,
+ "Unhandled command completion %d\n", id);
+
+ DAC960_PD_ack_intr(base);
+ DAC960_PD_ack_hw_mbox_status(base);
+
+ if (id < 3)
+ myrb_handle_cmdblk(cb, cmd_blk);
+ else
+ myrb_handle_scsi(cb, cmd_blk, scmd);
+ }
+ spin_unlock_irqrestore(&cb->queue_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct myrb_privdata DAC960_PD_privdata = {
+ .hw_init = DAC960_PD_hw_init,
+ .irq_handler = DAC960_PD_intr_handler,
+ .mmio_size = DAC960_PD_mmio_size,
+};
+
+
+/*
+ * DAC960 P Series Controllers
+ *
+ * Similar to the DAC960 PD Series Controllers, but some commands have
+ * to be translated.
+ */
+
+static inline void myrb_translate_enquiry(void *enq)
+{
+ memcpy(enq + 132, enq + 36, 64);
+ memset(enq + 36, 0, 96);
+}
+
+static inline void myrb_translate_devstate(void *state)
+{
+ memcpy(state + 2, state + 3, 1);
+ memmove(state + 4, state + 5, 2);
+ memmove(state + 6, state + 8, 4);
+}
+
+static inline void myrb_translate_to_rw_command(struct myrb_cmdblk *cmd_blk)
+{
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ int ldev_num = mbox->type5.ld.ldev_num;
+
+ mbox->bytes[3] &= 0x7;
+ mbox->bytes[3] |= mbox->bytes[7] << 6;
+ mbox->bytes[7] = ldev_num;
+}
+
+static inline void myrb_translate_from_rw_command(struct myrb_cmdblk *cmd_blk)
+{
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+ int ldev_num = mbox->bytes[7];
+
+ mbox->bytes[7] = mbox->bytes[3] >> 6;
+ mbox->bytes[3] &= 0x7;
+ mbox->bytes[3] |= ldev_num << 3;
+}
+
+static void DAC960_P_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk)
+{
+ void __iomem *base = cb->io_base;
+ union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
+
+ switch (mbox->common.opcode) {
+ case MYRB_CMD_ENQUIRY:
+ mbox->common.opcode = MYRB_CMD_ENQUIRY_OLD;
+ break;
+ case MYRB_CMD_GET_DEVICE_STATE:
+ mbox->common.opcode = MYRB_CMD_GET_DEVICE_STATE_OLD;
+ break;
+ case MYRB_CMD_READ:
+ mbox->common.opcode = MYRB_CMD_READ_OLD;
+ myrb_translate_to_rw_command(cmd_blk);
+ break;
+ case MYRB_CMD_WRITE:
+ mbox->common.opcode = MYRB_CMD_WRITE_OLD;
+ myrb_translate_to_rw_command(cmd_blk);
+ break;
+ case MYRB_CMD_READ_SG:
+ mbox->common.opcode = MYRB_CMD_READ_SG_OLD;
+ myrb_translate_to_rw_command(cmd_blk);
+ break;
+ case MYRB_CMD_WRITE_SG:
+ mbox->common.opcode = MYRB_CMD_WRITE_SG_OLD;
+ myrb_translate_to_rw_command(cmd_blk);
+ break;
+ default:
+ break;
+ }
+ while (DAC960_PD_hw_mbox_is_full(base))
+ udelay(1);
+ DAC960_PD_write_cmd_mbox(base, mbox);
+ DAC960_PD_hw_mbox_new_cmd(base);
+}
+
+
+static int DAC960_P_hw_init(struct pci_dev *pdev,
+ struct myrb_hba *cb, void __iomem *base)
+{
+ int timeout = 0;
+ unsigned char error, parm0, parm1;
+
+ if (!request_region(cb->io_addr, 0x80, "myrb")) {
+ dev_err(&pdev->dev, "IO port 0x%lx busy\n",
+ (unsigned long)cb->io_addr);
+ return -EBUSY;
+ }
+ DAC960_PD_disable_intr(base);
+ DAC960_PD_ack_hw_mbox_status(base);
+ udelay(1000);
+ while (DAC960_PD_init_in_progress(base) &&
+ timeout < MYRB_MAILBOX_TIMEOUT) {
+ if (DAC960_PD_read_error_status(base, &error,
+ &parm0, &parm1) &&
+ myrb_err_status(cb, error, parm0, parm1))
+ return -EAGAIN;
+ udelay(10);
+ timeout++;
+ }
+ if (timeout == MYRB_MAILBOX_TIMEOUT) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for Controller Initialisation\n");
+ return -ETIMEDOUT;
+ }
+ if (!myrb_enable_mmio(cb, NULL)) {
+ dev_err(&pdev->dev,
+ "Unable to allocate DMA mapped memory\n");
+ DAC960_PD_reset_ctrl(base);
+ return -ETIMEDOUT;
+ }
+ DAC960_PD_enable_intr(base);
+ cb->qcmd = DAC960_P_qcmd;
+ cb->disable_intr = DAC960_PD_disable_intr;
+ cb->reset = DAC960_PD_reset_ctrl;
+
+ return 0;
+}
+
+static irqreturn_t DAC960_P_intr_handler(int irq, void *arg)
+{
+ struct myrb_hba *cb = arg;
+ void __iomem *base = cb->io_base;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cb->queue_lock, flags);
+ while (DAC960_PD_hw_mbox_status_available(base)) {
+ unsigned char id = DAC960_PD_read_status_cmd_ident(base);
+ struct scsi_cmnd *scmd = NULL;
+ struct myrb_cmdblk *cmd_blk = NULL;
+ union myrb_cmd_mbox *mbox;
+ enum myrb_cmd_opcode op;
+
+
+ if (id == MYRB_DCMD_TAG)
+ cmd_blk = &cb->dcmd_blk;
+ else if (id == MYRB_MCMD_TAG)
+ cmd_blk = &cb->mcmd_blk;
+ else {
+ scmd = scsi_host_find_tag(cb->host, id - 3);
+ if (scmd)
+ cmd_blk = scsi_cmd_priv(scmd);
+ }
+ if (cmd_blk)
+ cmd_blk->status = DAC960_PD_read_status(base);
+ else
+ dev_err(&cb->pdev->dev,
+ "Unhandled command completion %d\n", id);
+
+ DAC960_PD_ack_intr(base);
+ DAC960_PD_ack_hw_mbox_status(base);
+
+ if (!cmd_blk)
+ continue;
+
+ mbox = &cmd_blk->mbox;
+ op = mbox->common.opcode;
+ switch (op) {
+ case MYRB_CMD_ENQUIRY_OLD:
+ mbox->common.opcode = MYRB_CMD_ENQUIRY;
+ myrb_translate_enquiry(cb->enquiry);
+ break;
+ case MYRB_CMD_READ_OLD:
+ mbox->common.opcode = MYRB_CMD_READ;
+ myrb_translate_from_rw_command(cmd_blk);
+ break;
+ case MYRB_CMD_WRITE_OLD:
+ mbox->common.opcode = MYRB_CMD_WRITE;
+ myrb_translate_from_rw_command(cmd_blk);
+ break;
+ case MYRB_CMD_READ_SG_OLD:
+ mbox->common.opcode = MYRB_CMD_READ_SG;
+ myrb_translate_from_rw_command(cmd_blk);
+ break;
+ case MYRB_CMD_WRITE_SG_OLD:
+ mbox->common.opcode = MYRB_CMD_WRITE_SG;
+ myrb_translate_from_rw_command(cmd_blk);
+ break;
+ default:
+ break;
+ }
+ if (id < 3)
+ myrb_handle_cmdblk(cb, cmd_blk);
+ else
+ myrb_handle_scsi(cb, cmd_blk, scmd);
+ }
+ spin_unlock_irqrestore(&cb->queue_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct myrb_privdata DAC960_P_privdata = {
+ .hw_init = DAC960_P_hw_init,
+ .irq_handler = DAC960_P_intr_handler,
+ .mmio_size = DAC960_PD_mmio_size,
+};
+
+static struct myrb_hba *myrb_detect(struct pci_dev *pdev,
+ const struct pci_device_id *entry)
+{
+ struct myrb_privdata *privdata =
+ (struct myrb_privdata *)entry->driver_data;
+ irq_handler_t irq_handler = privdata->irq_handler;
+ unsigned int mmio_size = privdata->mmio_size;
+ struct Scsi_Host *shost;
+ struct myrb_hba *cb = NULL;
+
+ shost = scsi_host_alloc(&myrb_template, sizeof(struct myrb_hba));
+ if (!shost) {
+ dev_err(&pdev->dev, "Unable to allocate Controller\n");
+ return NULL;
+ }
+ shost->max_cmd_len = 12;
+ shost->max_lun = 256;
+ cb = shost_priv(shost);
+ mutex_init(&cb->dcmd_mutex);
+ mutex_init(&cb->dma_mutex);
+ cb->pdev = pdev;
+ cb->host = shost;
+
+ if (pci_enable_device(pdev)) {
+ dev_err(&pdev->dev, "Failed to enable PCI device\n");
+ scsi_host_put(shost);
+ return NULL;
+ }
+
+ if (privdata->hw_init == DAC960_PD_hw_init ||
+ privdata->hw_init == DAC960_P_hw_init) {
+ cb->io_addr = pci_resource_start(pdev, 0);
+ cb->pci_addr = pci_resource_start(pdev, 1);
+ } else
+ cb->pci_addr = pci_resource_start(pdev, 0);
+
+ pci_set_drvdata(pdev, cb);
+ spin_lock_init(&cb->queue_lock);
+ if (mmio_size < PAGE_SIZE)
+ mmio_size = PAGE_SIZE;
+ cb->mmio_base = ioremap(cb->pci_addr & PAGE_MASK, mmio_size);
+ if (cb->mmio_base == NULL) {
+ dev_err(&pdev->dev,
+ "Unable to map Controller Register Window\n");
+ goto failure;
+ }
+
+ cb->io_base = cb->mmio_base + (cb->pci_addr & ~PAGE_MASK);
+ if (privdata->hw_init(pdev, cb, cb->io_base))
+ goto failure;
+
+ if (request_irq(pdev->irq, irq_handler, IRQF_SHARED, "myrb", cb) < 0) {
+ dev_err(&pdev->dev,
+ "Unable to acquire IRQ Channel %d\n", pdev->irq);
+ goto failure;
+ }
+ cb->irq = pdev->irq;
+ return cb;
+
+failure:
+ dev_err(&pdev->dev,
+ "Failed to initialize Controller\n");
+ myrb_cleanup(cb);
+ return NULL;
+}
+
+static int myrb_probe(struct pci_dev *dev, const struct pci_device_id *entry)
+{
+ struct myrb_hba *cb;
+ int ret;
+
+ cb = myrb_detect(dev, entry);
+ if (!cb)
+ return -ENODEV;
+
+ ret = myrb_get_hba_config(cb);
+ if (ret < 0) {
+ myrb_cleanup(cb);
+ return ret;
+ }
+
+ if (!myrb_create_mempools(dev, cb)) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ ret = scsi_add_host(cb->host, &dev->dev);
+ if (ret) {
+ dev_err(&dev->dev, "scsi_add_host failed with %d\n", ret);
+ myrb_destroy_mempools(cb);
+ goto failed;
+ }
+ scsi_scan_host(cb->host);
+ return 0;
+failed:
+ myrb_cleanup(cb);
+ return ret;
+}
+
+
+static void myrb_remove(struct pci_dev *pdev)
+{
+ struct myrb_hba *cb = pci_get_drvdata(pdev);
+
+ shost_printk(KERN_NOTICE, cb->host, "Flushing Cache...");
+ myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0);
+ myrb_cleanup(cb);
+ myrb_destroy_mempools(cb);
+}
+
+
+static const struct pci_device_id myrb_id_table[] = {
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_DEC,
+ PCI_DEVICE_ID_DEC_21285,
+ PCI_VENDOR_ID_MYLEX,
+ PCI_DEVICE_ID_MYLEX_DAC960_LA),
+ .driver_data = (unsigned long) &DAC960_LA_privdata,
+ },
+ {
+ PCI_DEVICE_DATA(MYLEX, DAC960_PG, &DAC960_PG_privdata),
+ },
+ {
+ PCI_DEVICE_DATA(MYLEX, DAC960_PD, &DAC960_PD_privdata),
+ },
+ {
+ PCI_DEVICE_DATA(MYLEX, DAC960_P, &DAC960_P_privdata),
+ },
+ {0, },
+};
+
+MODULE_DEVICE_TABLE(pci, myrb_id_table);
+
+static struct pci_driver myrb_pci_driver = {
+ .name = "myrb",
+ .id_table = myrb_id_table,
+ .probe = myrb_probe,
+ .remove = myrb_remove,
+};
+
+static int __init myrb_init_module(void)
+{
+ int ret;
+
+ myrb_raid_template = raid_class_attach(&myrb_raid_functions);
+ if (!myrb_raid_template)
+ return -ENODEV;
+
+ ret = pci_register_driver(&myrb_pci_driver);
+ if (ret)
+ raid_class_release(myrb_raid_template);
+
+ return ret;
+}
+
+static void __exit myrb_cleanup_module(void)
+{
+ pci_unregister_driver(&myrb_pci_driver);
+ raid_class_release(myrb_raid_template);
+}
+
+module_init(myrb_init_module);
+module_exit(myrb_cleanup_module);
+
+MODULE_DESCRIPTION("Mylex DAC960/AcceleRAID/eXtremeRAID driver (Block interface)");
+MODULE_AUTHOR("Hannes Reinecke <hare@suse.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/myrb.h b/drivers/scsi/myrb.h
new file mode 100644
index 000000000000..78dc4136fb10
--- /dev/null
+++ b/drivers/scsi/myrb.h
@@ -0,0 +1,957 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
+ *
+ * Copyright 2017 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com>
+ *
+ * Based on the original DAC960 driver,
+ * Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com>
+ * Portions Copyright 2002 by Mylex (An IBM Business Unit)
+ *
+ */
+
+#ifndef MYRB_H
+#define MYRB_H
+
+#define MYRB_MAX_LDEVS 32
+#define MYRB_MAX_CHANNELS 3
+#define MYRB_MAX_TARGETS 16
+#define MYRB_MAX_PHYSICAL_DEVICES 45
+#define MYRB_SCATTER_GATHER_LIMIT 32
+#define MYRB_CMD_MBOX_COUNT 256
+#define MYRB_STAT_MBOX_COUNT 1024
+
+#define MYRB_BLKSIZE_BITS 9
+#define MYRB_MAILBOX_TIMEOUT 1000000
+
+#define MYRB_DCMD_TAG 1
+#define MYRB_MCMD_TAG 2
+
+#define MYRB_PRIMARY_MONITOR_INTERVAL (10 * HZ)
+#define MYRB_SECONDARY_MONITOR_INTERVAL (60 * HZ)
+
+/*
+ * DAC960 V1 Firmware Command Opcodes.
+ */
+enum myrb_cmd_opcode {
+ /* I/O Commands */
+ MYRB_CMD_READ_EXTENDED = 0x33,
+ MYRB_CMD_WRITE_EXTENDED = 0x34,
+ MYRB_CMD_READAHEAD_EXTENDED = 0x35,
+ MYRB_CMD_READ_EXTENDED_SG = 0xB3,
+ MYRB_CMD_WRITE_EXTENDED_SG = 0xB4,
+ MYRB_CMD_READ = 0x36,
+ MYRB_CMD_READ_SG = 0xB6,
+ MYRB_CMD_WRITE = 0x37,
+ MYRB_CMD_WRITE_SG = 0xB7,
+ MYRB_CMD_DCDB = 0x04,
+ MYRB_CMD_DCDB_SG = 0x84,
+ MYRB_CMD_FLUSH = 0x0A,
+ /* Controller Status Related Commands */
+ MYRB_CMD_ENQUIRY = 0x53,
+ MYRB_CMD_ENQUIRY2 = 0x1C,
+ MYRB_CMD_GET_LDRV_ELEMENT = 0x55,
+ MYRB_CMD_GET_LDEV_INFO = 0x19,
+ MYRB_CMD_IOPORTREAD = 0x39,
+ MYRB_CMD_IOPORTWRITE = 0x3A,
+ MYRB_CMD_GET_SD_STATS = 0x3E,
+ MYRB_CMD_GET_PD_STATS = 0x3F,
+ MYRB_CMD_EVENT_LOG_OPERATION = 0x72,
+ /* Device Related Commands */
+ MYRB_CMD_START_DEVICE = 0x10,
+ MYRB_CMD_GET_DEVICE_STATE = 0x50,
+ MYRB_CMD_STOP_CHANNEL = 0x13,
+ MYRB_CMD_START_CHANNEL = 0x12,
+ MYRB_CMD_RESET_CHANNEL = 0x1A,
+ /* Commands Associated with Data Consistency and Errors */
+ MYRB_CMD_REBUILD = 0x09,
+ MYRB_CMD_REBUILD_ASYNC = 0x16,
+ MYRB_CMD_CHECK_CONSISTENCY = 0x0F,
+ MYRB_CMD_CHECK_CONSISTENCY_ASYNC = 0x1E,
+ MYRB_CMD_REBUILD_STAT = 0x0C,
+ MYRB_CMD_GET_REBUILD_PROGRESS = 0x27,
+ MYRB_CMD_REBUILD_CONTROL = 0x1F,
+ MYRB_CMD_READ_BADBLOCK_TABLE = 0x0B,
+ MYRB_CMD_READ_BADDATA_TABLE = 0x25,
+ MYRB_CMD_CLEAR_BADDATA_TABLE = 0x26,
+ MYRB_CMD_GET_ERROR_TABLE = 0x17,
+ MYRB_CMD_ADD_CAPACITY_ASYNC = 0x2A,
+ MYRB_CMD_BGI_CONTROL = 0x2B,
+ /* Configuration Related Commands */
+ MYRB_CMD_READ_CONFIG2 = 0x3D,
+ MYRB_CMD_WRITE_CONFIG2 = 0x3C,
+ MYRB_CMD_READ_CONFIG_ONDISK = 0x4A,
+ MYRB_CMD_WRITE_CONFIG_ONDISK = 0x4B,
+ MYRB_CMD_READ_CONFIG = 0x4E,
+ MYRB_CMD_READ_BACKUP_CONFIG = 0x4D,
+ MYRB_CMD_WRITE_CONFIG = 0x4F,
+ MYRB_CMD_ADD_CONFIG = 0x4C,
+ MYRB_CMD_READ_CONFIG_LABEL = 0x48,
+ MYRB_CMD_WRITE_CONFIG_LABEL = 0x49,
+ /* Firmware Upgrade Related Commands */
+ MYRB_CMD_LOAD_IMAGE = 0x20,
+ MYRB_CMD_STORE_IMAGE = 0x21,
+ MYRB_CMD_PROGRAM_IMAGE = 0x22,
+ /* Diagnostic Commands */
+ MYRB_CMD_SET_DIAGNOSTIC_MODE = 0x31,
+ MYRB_CMD_RUN_DIAGNOSTIC = 0x32,
+ /* Subsystem Service Commands */
+ MYRB_CMD_GET_SUBSYS_DATA = 0x70,
+ MYRB_CMD_SET_SUBSYS_PARAM = 0x71,
+ /* Version 2.xx Firmware Commands */
+ MYRB_CMD_ENQUIRY_OLD = 0x05,
+ MYRB_CMD_GET_DEVICE_STATE_OLD = 0x14,
+ MYRB_CMD_READ_OLD = 0x02,
+ MYRB_CMD_WRITE_OLD = 0x03,
+ MYRB_CMD_READ_SG_OLD = 0x82,
+ MYRB_CMD_WRITE_SG_OLD = 0x83
+} __packed;
+
+/*
+ * DAC960 V1 Firmware Command Status Codes.
+ */
+#define MYRB_STATUS_SUCCESS 0x0000 /* Common */
+#define MYRB_STATUS_CHECK_CONDITION 0x0002 /* Common */
+#define MYRB_STATUS_NO_DEVICE 0x0102 /* Common */
+#define MYRB_STATUS_INVALID_ADDRESS 0x0105 /* Common */
+#define MYRB_STATUS_INVALID_PARAM 0x0105 /* Common */
+#define MYRB_STATUS_IRRECOVERABLE_DATA_ERROR 0x0001 /* I/O */
+#define MYRB_STATUS_LDRV_NONEXISTENT_OR_OFFLINE 0x0002 /* I/O */
+#define MYRB_STATUS_ACCESS_BEYOND_END_OF_LDRV 0x0105 /* I/O */
+#define MYRB_STATUS_BAD_DATA 0x010C /* I/O */
+#define MYRB_STATUS_DEVICE_BUSY 0x0008 /* DCDB */
+#define MYRB_STATUS_DEVICE_NONRESPONSIVE 0x000E /* DCDB */
+#define MYRB_STATUS_COMMAND_TERMINATED 0x000F /* DCDB */
+#define MYRB_STATUS_START_DEVICE_FAILED 0x0002 /* Device */
+#define MYRB_STATUS_INVALID_CHANNEL_OR_TARGET 0x0105 /* Device */
+#define MYRB_STATUS_CHANNEL_BUSY 0x0106 /* Device */
+#define MYRB_STATUS_OUT_OF_MEMORY 0x0107 /* Device */
+#define MYRB_STATUS_CHANNEL_NOT_STOPPED 0x0002 /* Device */
+#define MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE 0x0002 /* Consistency */
+#define MYRB_STATUS_RBLD_BADBLOCKS 0x0003 /* Consistency */
+#define MYRB_STATUS_RBLD_NEW_DISK_FAILED 0x0004 /* Consistency */
+#define MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS 0x0106 /* Consistency */
+#define MYRB_STATUS_DEPENDENT_DISK_DEAD 0x0002 /* Consistency */
+#define MYRB_STATUS_INCONSISTENT_BLOCKS 0x0003 /* Consistency */
+#define MYRB_STATUS_INVALID_OR_NONREDUNDANT_LDRV 0x0105 /* Consistency */
+#define MYRB_STATUS_NO_RBLD_OR_CHECK_INPROGRESS 0x0105 /* Consistency */
+#define MYRB_STATUS_RBLD_IN_PROGRESS_DATA_VALID 0x0000 /* Consistency */
+#define MYRB_STATUS_RBLD_FAILED_LDEV_FAILURE 0x0002 /* Consistency */
+#define MYRB_STATUS_RBLD_FAILED_BADBLOCKS 0x0003 /* Consistency */
+#define MYRB_STATUS_RBLD_FAILED_NEW_DRIVE_FAILED 0x0004 /* Consistency */
+#define MYRB_STATUS_RBLD_SUCCESS 0x0100 /* Consistency */
+#define MYRB_STATUS_RBLD_SUCCESS_TERMINATED 0x0107 /* Consistency */
+#define MYRB_STATUS_RBLD_NOT_CHECKED 0x0108 /* Consistency */
+#define MYRB_STATUS_BGI_SUCCESS 0x0100 /* Consistency */
+#define MYRB_STATUS_BGI_ABORTED 0x0005 /* Consistency */
+#define MYRB_STATUS_NO_BGI_INPROGRESS 0x0105 /* Consistency */
+#define MYRB_STATUS_ADD_CAPACITY_INPROGRESS 0x0004 /* Consistency */
+#define MYRB_STATUS_ADD_CAPACITY_FAILED_OR_SUSPENDED 0x00F4 /* Consistency */
+#define MYRB_STATUS_CONFIG2_CSUM_ERROR 0x0002 /* Configuration */
+#define MYRB_STATUS_CONFIGURATION_SUSPENDED 0x0106 /* Configuration */
+#define MYRB_STATUS_FAILED_TO_CONFIGURE_NVRAM 0x0105 /* Configuration */
+#define MYRB_STATUS_CONFIGURATION_NOT_SAVED 0x0106 /* Configuration */
+#define MYRB_STATUS_SUBSYS_NOTINSTALLED 0x0001 /* Subsystem */
+#define MYRB_STATUS_SUBSYS_FAILED 0x0002 /* Subsystem */
+#define MYRB_STATUS_SUBSYS_BUSY 0x0106 /* Subsystem */
+#define MYRB_STATUS_SUBSYS_TIMEOUT 0x0108 /* Subsystem */
+
+/*
+ * DAC960 V1 Firmware Enquiry Command reply structure.
+ */
+struct myrb_enquiry {
+ unsigned char ldev_count; /* Byte 0 */
+ unsigned int rsvd1:24; /* Bytes 1-3 */
+ unsigned int ldev_sizes[32]; /* Bytes 4-131 */
+ unsigned short flash_age; /* Bytes 132-133 */
+ struct {
+ unsigned char deferred:1; /* Byte 134 Bit 0 */
+ unsigned char low_bat:1; /* Byte 134 Bit 1 */
+ unsigned char rsvd2:6; /* Byte 134 Bits 2-7 */
+ } status;
+ unsigned char rsvd3:8; /* Byte 135 */
+ unsigned char fw_minor_version; /* Byte 136 */
+ unsigned char fw_major_version; /* Byte 137 */
+ enum {
+ MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS = 0x00,
+ MYRB_STDBY_RBLD_IN_PROGRESS = 0x01,
+ MYRB_BG_RBLD_IN_PROGRESS = 0x02,
+ MYRB_BG_CHECK_IN_PROGRESS = 0x03,
+ MYRB_STDBY_RBLD_COMPLETED_WITH_ERROR = 0xFF,
+ MYRB_BG_RBLD_OR_CHECK_FAILED_DRIVE_FAILED = 0xF0,
+ MYRB_BG_RBLD_OR_CHECK_FAILED_LDEV_FAILED = 0xF1,
+ MYRB_BG_RBLD_OR_CHECK_FAILED_OTHER = 0xF2,
+ MYRB_BG_RBLD_OR_CHECK_SUCCESS_TERMINATED = 0xF3
+ } __packed rbld; /* Byte 138 */
+ unsigned char max_tcq; /* Byte 139 */
+ unsigned char ldev_offline; /* Byte 140 */
+ unsigned char rsvd4:8; /* Byte 141 */
+ unsigned short ev_seq; /* Bytes 142-143 */
+ unsigned char ldev_critical; /* Byte 144 */
+ unsigned int rsvd5:24; /* Bytes 145-147 */
+ unsigned char pdev_dead; /* Byte 148 */
+ unsigned char rsvd6:8; /* Byte 149 */
+ unsigned char rbld_count; /* Byte 150 */
+ struct {
+ unsigned char rsvd7:3; /* Byte 151 Bits 0-2 */
+ unsigned char bbu_present:1; /* Byte 151 Bit 3 */
+ unsigned char rsvd8:4; /* Byte 151 Bits 4-7 */
+ } misc;
+ struct {
+ unsigned char target;
+ unsigned char channel;
+ } dead_drives[21]; /* Bytes 152-194 */
+ unsigned char rsvd9[62]; /* Bytes 195-255 */
+} __packed;
+
+/*
+ * DAC960 V1 Firmware Enquiry2 Command reply structure.
+ */
+struct myrb_enquiry2 {
+ struct {
+ enum {
+ DAC960_V1_P_PD_PU = 0x01,
+ DAC960_V1_PL = 0x02,
+ DAC960_V1_PG = 0x10,
+ DAC960_V1_PJ = 0x11,
+ DAC960_V1_PR = 0x12,
+ DAC960_V1_PT = 0x13,
+ DAC960_V1_PTL0 = 0x14,
+ DAC960_V1_PRL = 0x15,
+ DAC960_V1_PTL1 = 0x16,
+ DAC960_V1_1164P = 0x20
+ } __packed sub_model; /* Byte 0 */
+ unsigned char actual_channels; /* Byte 1 */
+ enum {
+ MYRB_5_CHANNEL_BOARD = 0x01,
+ MYRB_3_CHANNEL_BOARD = 0x02,
+ MYRB_2_CHANNEL_BOARD = 0x03,
+ MYRB_3_CHANNEL_ASIC_DAC = 0x04
+ } __packed model; /* Byte 2 */
+ enum {
+ MYRB_EISA_CONTROLLER = 0x01,
+ MYRB_MCA_CONTROLLER = 0x02,
+ MYRB_PCI_CONTROLLER = 0x03,
+ MYRB_SCSI_TO_SCSI = 0x08
+ } __packed controller; /* Byte 3 */
+ } hw; /* Bytes 0-3 */
+ /* MajorVersion.MinorVersion-FirmwareType-TurnID */
+ struct {
+ unsigned char major_version; /* Byte 4 */
+ unsigned char minor_version; /* Byte 5 */
+ unsigned char turn_id; /* Byte 6 */
+ char firmware_type; /* Byte 7 */
+ } fw; /* Bytes 4-7 */
+ unsigned int rsvd1; /* Byte 8-11 */
+ unsigned char cfg_chan; /* Byte 12 */
+ unsigned char cur_chan; /* Byte 13 */
+ unsigned char max_targets; /* Byte 14 */
+ unsigned char max_tcq; /* Byte 15 */
+ unsigned char max_ldev; /* Byte 16 */
+ unsigned char max_arms; /* Byte 17 */
+ unsigned char max_spans; /* Byte 18 */
+ unsigned char rsvd2; /* Byte 19 */
+ unsigned int rsvd3; /* Bytes 20-23 */
+ unsigned int mem_size; /* Bytes 24-27 */
+ unsigned int cache_size; /* Bytes 28-31 */
+ unsigned int flash_size; /* Bytes 32-35 */
+ unsigned int nvram_size; /* Bytes 36-39 */
+ struct {
+ enum {
+ MYRB_RAM_TYPE_DRAM = 0x0,
+ MYRB_RAM_TYPE_EDO = 0x1,
+ MYRB_RAM_TYPE_SDRAM = 0x2,
+ MYRB_RAM_TYPE_Last = 0x7
+ } __packed ram:3; /* Byte 40 Bits 0-2 */
+ enum {
+ MYRB_ERR_CORR_None = 0x0,
+ MYRB_ERR_CORR_Parity = 0x1,
+ MYRB_ERR_CORR_ECC = 0x2,
+ MYRB_ERR_CORR_Last = 0x7
+ } __packed ec:3; /* Byte 40 Bits 3-5 */
+ unsigned char fast_page:1; /* Byte 40 Bit 6 */
+ unsigned char low_power:1; /* Byte 40 Bit 7 */
+ unsigned char rsvd4; /* Bytes 41 */
+ } mem_type;
+ unsigned short clock_speed; /* Bytes 42-43 */
+ unsigned short mem_speed; /* Bytes 44-45 */
+ unsigned short hw_speed; /* Bytes 46-47 */
+ unsigned char rsvd5[12]; /* Bytes 48-59 */
+ unsigned short max_cmds; /* Bytes 60-61 */
+ unsigned short max_sge; /* Bytes 62-63 */
+ unsigned short max_drv_cmds; /* Bytes 64-65 */
+ unsigned short max_io_desc; /* Bytes 66-67 */
+ unsigned short max_sectors; /* Bytes 68-69 */
+ unsigned char latency; /* Byte 70 */
+ unsigned char rsvd6; /* Byte 71 */
+ unsigned char scsi_tmo; /* Byte 72 */
+ unsigned char rsvd7; /* Byte 73 */
+ unsigned short min_freelines; /* Bytes 74-75 */
+ unsigned char rsvd8[8]; /* Bytes 76-83 */
+ unsigned char rbld_rate_const; /* Byte 84 */
+ unsigned char rsvd9[11]; /* Byte 85-95 */
+ unsigned short pdrv_block_size; /* Bytes 96-97 */
+ unsigned short ldev_block_size; /* Bytes 98-99 */
+ unsigned short max_blocks_per_cmd; /* Bytes 100-101 */
+ unsigned short block_factor; /* Bytes 102-103 */
+ unsigned short cacheline_size; /* Bytes 104-105 */
+ struct {
+ enum {
+ MYRB_WIDTH_NARROW_8BIT = 0x0,
+ MYRB_WIDTH_WIDE_16BIT = 0x1,
+ MYRB_WIDTH_WIDE_32BIT = 0x2
+ } __packed bus_width:2; /* Byte 106 Bits 0-1 */
+ enum {
+ MYRB_SCSI_SPEED_FAST = 0x0,
+ MYRB_SCSI_SPEED_ULTRA = 0x1,
+ MYRB_SCSI_SPEED_ULTRA2 = 0x2
+ } __packed bus_speed:2; /* Byte 106 Bits 2-3 */
+ unsigned char differential:1; /* Byte 106 Bit 4 */
+ unsigned char rsvd10:3; /* Byte 106 Bits 5-7 */
+ } scsi_cap;
+ unsigned char rsvd11[5]; /* Byte 107-111 */
+ unsigned short fw_build; /* Bytes 112-113 */
+ enum {
+ MYRB_FAULT_AEMI = 0x01,
+ MYRB_FAULT_OEM1 = 0x02,
+ MYRB_FAULT_OEM2 = 0x04,
+ MYRB_FAULT_OEM3 = 0x08,
+ MYRB_FAULT_CONNER = 0x10,
+ MYRB_FAULT_SAFTE = 0x20
+ } __packed fault_mgmt; /* Byte 114 */
+ unsigned char rsvd12; /* Byte 115 */
+ struct {
+ unsigned int clustering:1; /* Byte 116 Bit 0 */
+ unsigned int online_RAID_expansion:1; /* Byte 116 Bit 1 */
+ unsigned int readahead:1; /* Byte 116 Bit 2 */
+ unsigned int bgi:1; /* Byte 116 Bit 3 */
+ unsigned int rsvd13:28; /* Bytes 116-119 */
+ } fw_features;
+ unsigned char rsvd14[8]; /* Bytes 120-127 */
+} __packed;
+
+/*
+ * DAC960 V1 Firmware Logical Drive State type.
+ */
+enum myrb_devstate {
+ MYRB_DEVICE_DEAD = 0x00,
+ MYRB_DEVICE_WO = 0x02,
+ MYRB_DEVICE_ONLINE = 0x03,
+ MYRB_DEVICE_CRITICAL = 0x04,
+ MYRB_DEVICE_STANDBY = 0x10,
+ MYRB_DEVICE_OFFLINE = 0xFF
+} __packed;
+
+/*
+ * DAC960 V1 RAID Levels
+ */
+enum myrb_raidlevel {
+ MYRB_RAID_LEVEL0 = 0x0, /* RAID 0 */
+ MYRB_RAID_LEVEL1 = 0x1, /* RAID 1 */
+ MYRB_RAID_LEVEL3 = 0x3, /* RAID 3 */
+ MYRB_RAID_LEVEL5 = 0x5, /* RAID 5 */
+ MYRB_RAID_LEVEL6 = 0x6, /* RAID 6 */
+ MYRB_RAID_JBOD = 0x7, /* RAID 7 (JBOD) */
+} __packed;
+
+/*
+ * DAC960 V1 Firmware Logical Drive Information structure.
+ */
+struct myrb_ldev_info {
+ unsigned int size; /* Bytes 0-3 */
+ enum myrb_devstate state; /* Byte 4 */
+ unsigned int raid_level:7; /* Byte 5 Bits 0-6 */
+ unsigned int wb_enabled:1; /* Byte 5 Bit 7 */
+ unsigned int rsvd:16; /* Bytes 6-7 */
+};
+
+/*
+ * DAC960 V1 Firmware Perform Event Log Operation Types.
+ */
+#define DAC960_V1_GetEventLogEntry 0x00
+
+/*
+ * DAC960 V1 Firmware Get Event Log Entry Command reply structure.
+ */
+struct myrb_log_entry {
+ unsigned char msg_type; /* Byte 0 */
+ unsigned char msg_len; /* Byte 1 */
+ unsigned char target:5; /* Byte 2 Bits 0-4 */
+ unsigned char channel:3; /* Byte 2 Bits 5-7 */
+ unsigned char lun:6; /* Byte 3 Bits 0-5 */
+ unsigned char rsvd1:2; /* Byte 3 Bits 6-7 */
+ unsigned short seq_num; /* Bytes 4-5 */
+ unsigned char sense[26]; /* Bytes 6-31 */
+};
+
+/*
+ * DAC960 V1 Firmware Get Device State Command reply structure.
+ * The structure is padded by 2 bytes for compatibility with Version 2.xx
+ * Firmware.
+ */
+struct myrb_pdev_state {
+ unsigned int present:1; /* Byte 0 Bit 0 */
+ unsigned int :7; /* Byte 0 Bits 1-7 */
+ enum {
+ MYRB_TYPE_OTHER = 0x0,
+ MYRB_TYPE_DISK = 0x1,
+ MYRB_TYPE_TAPE = 0x2,
+ MYRB_TYPE_CDROM_OR_WORM = 0x3
+ } __packed devtype:2; /* Byte 1 Bits 0-1 */
+ unsigned int rsvd1:1; /* Byte 1 Bit 2 */
+ unsigned int fast20:1; /* Byte 1 Bit 3 */
+ unsigned int sync:1; /* Byte 1 Bit 4 */
+ unsigned int fast:1; /* Byte 1 Bit 5 */
+ unsigned int wide:1; /* Byte 1 Bit 6 */
+ unsigned int tcq_supported:1; /* Byte 1 Bit 7 */
+ enum myrb_devstate state; /* Byte 2 */
+ unsigned int rsvd2:8; /* Byte 3 */
+ unsigned int sync_multiplier; /* Byte 4 */
+ unsigned int sync_offset:5; /* Byte 5 Bits 0-4 */
+ unsigned int rsvd3:3; /* Byte 5 Bits 5-7 */
+ unsigned int size; /* Bytes 6-9 */
+ unsigned int rsvd4:16; /* Bytes 10-11 */
+} __packed;
+
+/*
+ * DAC960 V1 Firmware Get Rebuild Progress Command reply structure.
+ */
+struct myrb_rbld_progress {
+ unsigned int ldev_num; /* Bytes 0-3 */
+ unsigned int ldev_size; /* Bytes 4-7 */
+ unsigned int blocks_left; /* Bytes 8-11 */
+};
+
+/*
+ * DAC960 V1 Firmware Background Initialization Status Command reply structure.
+ */
+struct myrb_bgi_status {
+ unsigned int ldev_size; /* Bytes 0-3 */
+ unsigned int blocks_done; /* Bytes 4-7 */
+ unsigned char rsvd1[12]; /* Bytes 8-19 */
+ unsigned int ldev_num; /* Bytes 20-23 */
+ unsigned char raid_level; /* Byte 24 */
+ enum {
+ MYRB_BGI_INVALID = 0x00,
+ MYRB_BGI_STARTED = 0x02,
+ MYRB_BGI_INPROGRESS = 0x04,
+ MYRB_BGI_SUSPENDED = 0x05,
+ MYRB_BGI_CANCELLED = 0x06
+ } __packed status; /* Byte 25 */
+ unsigned char rsvd2[6]; /* Bytes 26-31 */
+};
+
+/*
+ * DAC960 V1 Firmware Error Table Entry structure.
+ */
+struct myrb_error_entry {
+ unsigned char parity_err; /* Byte 0 */
+ unsigned char soft_err; /* Byte 1 */
+ unsigned char hard_err; /* Byte 2 */
+ unsigned char misc_err; /* Byte 3 */
+};
+
+/*
+ * DAC960 V1 Firmware Read Config2 Command reply structure.
+ */
+struct myrb_config2 {
+ unsigned rsvd1:1; /* Byte 0 Bit 0 */
+ unsigned active_negation:1; /* Byte 0 Bit 1 */
+ unsigned rsvd2:5; /* Byte 0 Bits 2-6 */
+ unsigned no_rescan_on_reset_during_scan:1; /* Byte 0 Bit 7 */
+ unsigned StorageWorks_support:1; /* Byte 1 Bit 0 */
+ unsigned HewlettPackard_support:1; /* Byte 1 Bit 1 */
+ unsigned no_disconnect_on_first_command:1; /* Byte 1 Bit 2 */
+ unsigned rsvd3:2; /* Byte 1 Bits 3-4 */
+ unsigned AEMI_ARM:1; /* Byte 1 Bit 5 */
+ unsigned AEMI_OFM:1; /* Byte 1 Bit 6 */
+ unsigned rsvd4:1; /* Byte 1 Bit 7 */
+ enum {
+ MYRB_OEMID_MYLEX = 0x00,
+ MYRB_OEMID_IBM = 0x08,
+ MYRB_OEMID_HP = 0x0A,
+ MYRB_OEMID_DEC = 0x0C,
+ MYRB_OEMID_SIEMENS = 0x10,
+ MYRB_OEMID_INTEL = 0x12
+ } __packed OEMID; /* Byte 2 */
+ unsigned char oem_model_number; /* Byte 3 */
+ unsigned char physical_sector; /* Byte 4 */
+ unsigned char logical_sector; /* Byte 5 */
+ unsigned char block_factor; /* Byte 6 */
+ unsigned readahead_enabled:1; /* Byte 7 Bit 0 */
+ unsigned low_BIOS_delay:1; /* Byte 7 Bit 1 */
+ unsigned rsvd5:2; /* Byte 7 Bits 2-3 */
+ unsigned restrict_reassign_to_one_sector:1; /* Byte 7 Bit 4 */
+ unsigned rsvd6:1; /* Byte 7 Bit 5 */
+ unsigned FUA_during_write_recovery:1; /* Byte 7 Bit 6 */
+ unsigned enable_LeftSymmetricRAID5Algorithm:1; /* Byte 7 Bit 7 */
+ unsigned char default_rebuild_rate; /* Byte 8 */
+ unsigned char rsvd7; /* Byte 9 */
+ unsigned char blocks_per_cacheline; /* Byte 10 */
+ unsigned char blocks_per_stripe; /* Byte 11 */
+ struct {
+ enum {
+ MYRB_SPEED_ASYNC = 0x0,
+ MYRB_SPEED_SYNC_8MHz = 0x1,
+ MYRB_SPEED_SYNC_5MHz = 0x2,
+ MYRB_SPEED_SYNC_10_OR_20MHz = 0x3
+ } __packed speed:2; /* Byte 11 Bits 0-1 */
+ unsigned force_8bit:1; /* Byte 11 Bit 2 */
+ unsigned disable_fast20:1; /* Byte 11 Bit 3 */
+ unsigned rsvd8:3; /* Byte 11 Bits 4-6 */
+ unsigned enable_tcq:1; /* Byte 11 Bit 7 */
+ } __packed channelparam[6]; /* Bytes 12-17 */
+ unsigned char SCSIInitiatorID; /* Byte 18 */
+ unsigned char rsvd9; /* Byte 19 */
+ enum {
+ MYRB_STARTUP_CONTROLLER_SPINUP = 0x00,
+ MYRB_STARTUP_POWERON_SPINUP = 0x01
+ } __packed startup; /* Byte 20 */
+ unsigned char simultaneous_device_spinup_count; /* Byte 21 */
+ unsigned char seconds_delay_between_spinups; /* Byte 22 */
+ unsigned char rsvd10[29]; /* Bytes 23-51 */
+ unsigned BIOS_disabled:1; /* Byte 52 Bit 0 */
+ unsigned CDROM_boot_enabled:1; /* Byte 52 Bit 1 */
+ unsigned rsvd11:3; /* Byte 52 Bits 2-4 */
+ enum {
+ MYRB_GEOM_128_32 = 0x0,
+ MYRB_GEOM_255_63 = 0x1,
+ MYRB_GEOM_RESERVED1 = 0x2,
+ MYRB_GEOM_RESERVED2 = 0x3
+ } __packed drive_geometry:2; /* Byte 52 Bits 5-6 */
+ unsigned rsvd12:1; /* Byte 52 Bit 7 */
+ unsigned char rsvd13[9]; /* Bytes 53-61 */
+ unsigned short csum; /* Bytes 62-63 */
+};
+
+/*
+ * DAC960 V1 Firmware DCDB request structure.
+ */
+struct myrb_dcdb {
+ unsigned target:4; /* Byte 0 Bits 0-3 */
+ unsigned channel:4; /* Byte 0 Bits 4-7 */
+ enum {
+ MYRB_DCDB_XFER_NONE = 0,
+ MYRB_DCDB_XFER_DEVICE_TO_SYSTEM = 1,
+ MYRB_DCDB_XFER_SYSTEM_TO_DEVICE = 2,
+ MYRB_DCDB_XFER_ILLEGAL = 3
+ } __packed data_xfer:2; /* Byte 1 Bits 0-1 */
+ unsigned early_status:1; /* Byte 1 Bit 2 */
+ unsigned rsvd1:1; /* Byte 1 Bit 3 */
+ enum {
+ MYRB_DCDB_TMO_24_HRS = 0,
+ MYRB_DCDB_TMO_10_SECS = 1,
+ MYRB_DCDB_TMO_60_SECS = 2,
+ MYRB_DCDB_TMO_10_MINS = 3
+ } __packed timeout:2; /* Byte 1 Bits 4-5 */
+ unsigned no_autosense:1; /* Byte 1 Bit 6 */
+ unsigned allow_disconnect:1; /* Byte 1 Bit 7 */
+ unsigned short xfer_len_lo; /* Bytes 2-3 */
+ u32 dma_addr; /* Bytes 4-7 */
+ unsigned char cdb_len:4; /* Byte 8 Bits 0-3 */
+ unsigned char xfer_len_hi4:4; /* Byte 8 Bits 4-7 */
+ unsigned char sense_len; /* Byte 9 */
+ unsigned char cdb[12]; /* Bytes 10-21 */
+ unsigned char sense[64]; /* Bytes 22-85 */
+ unsigned char status; /* Byte 86 */
+ unsigned char rsvd2; /* Byte 87 */
+};
+
+/*
+ * DAC960 V1 Firmware Scatter/Gather List Type 1 32 Bit Address
+ *32 Bit Byte Count structure.
+ */
+struct myrb_sge {
+ u32 sge_addr; /* Bytes 0-3 */
+ u32 sge_count; /* Bytes 4-7 */
+};
+
+/*
+ * 13 Byte DAC960 V1 Firmware Command Mailbox structure.
+ * Bytes 13-15 are not used. The structure is padded to 16 bytes for
+ * efficient access.
+ */
+union myrb_cmd_mbox {
+ unsigned int words[4]; /* Words 0-3 */
+ unsigned char bytes[16]; /* Bytes 0-15 */
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char rsvd[14]; /* Bytes 2-15 */
+ } __packed common;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char rsvd1[6]; /* Bytes 2-7 */
+ u32 addr; /* Bytes 8-11 */
+ unsigned char rsvd2[4]; /* Bytes 12-15 */
+ } __packed type3;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char optype; /* Byte 2 */
+ unsigned char rsvd1[5]; /* Bytes 3-7 */
+ u32 addr; /* Bytes 8-11 */
+ unsigned char rsvd2[4]; /* Bytes 12-15 */
+ } __packed type3B;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char rsvd1[5]; /* Bytes 2-6 */
+ unsigned char ldev_num:6; /* Byte 7 Bits 0-6 */
+ unsigned char auto_restore:1; /* Byte 7 Bit 7 */
+ unsigned char rsvd2[8]; /* Bytes 8-15 */
+ } __packed type3C;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char channel; /* Byte 2 */
+ unsigned char target; /* Byte 3 */
+ enum myrb_devstate state; /* Byte 4 */
+ unsigned char rsvd1[3]; /* Bytes 5-7 */
+ u32 addr; /* Bytes 8-11 */
+ unsigned char rsvd2[4]; /* Bytes 12-15 */
+ } __packed type3D;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char optype; /* Byte 2 */
+ unsigned char opqual; /* Byte 3 */
+ unsigned short ev_seq; /* Bytes 4-5 */
+ unsigned char rsvd1[2]; /* Bytes 6-7 */
+ u32 addr; /* Bytes 8-11 */
+ unsigned char rsvd2[4]; /* Bytes 12-15 */
+ } __packed type3E;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char rsvd1[2]; /* Bytes 2-3 */
+ unsigned char rbld_rate; /* Byte 4 */
+ unsigned char rsvd2[3]; /* Bytes 5-7 */
+ u32 addr; /* Bytes 8-11 */
+ unsigned char rsvd3[4]; /* Bytes 12-15 */
+ } __packed type3R;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned short xfer_len; /* Bytes 2-3 */
+ unsigned int lba; /* Bytes 4-7 */
+ u32 addr; /* Bytes 8-11 */
+ unsigned char ldev_num; /* Byte 12 */
+ unsigned char rsvd[3]; /* Bytes 13-15 */
+ } __packed type4;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ struct {
+ unsigned short xfer_len:11; /* Bytes 2-3 */
+ unsigned char ldev_num:5; /* Byte 3 Bits 3-7 */
+ } __packed ld;
+ unsigned int lba; /* Bytes 4-7 */
+ u32 addr; /* Bytes 8-11 */
+ unsigned char sg_count:6; /* Byte 12 Bits 0-5 */
+ enum {
+ MYRB_SGL_ADDR32_COUNT32 = 0x0,
+ MYRB_SGL_ADDR32_COUNT16 = 0x1,
+ MYRB_SGL_COUNT32_ADDR32 = 0x2,
+ MYRB_SGL_COUNT16_ADDR32 = 0x3
+ } __packed sg_type:2; /* Byte 12 Bits 6-7 */
+ unsigned char rsvd[3]; /* Bytes 13-15 */
+ } __packed type5;
+ struct {
+ enum myrb_cmd_opcode opcode; /* Byte 0 */
+ unsigned char id; /* Byte 1 */
+ unsigned char opcode2; /* Byte 2 */
+ unsigned char rsvd1:8; /* Byte 3 */
+ u32 cmd_mbox_addr; /* Bytes 4-7 */
+ u32 stat_mbox_addr; /* Bytes 8-11 */
+ unsigned char rsvd2[4]; /* Bytes 12-15 */
+ } __packed typeX;
+};
+
+/*
+ * DAC960 V1 Firmware Controller Status Mailbox structure.
+ */
+struct myrb_stat_mbox {
+ unsigned char id; /* Byte 0 */
+ unsigned char rsvd:7; /* Byte 1 Bits 0-6 */
+ unsigned char valid:1; /* Byte 1 Bit 7 */
+ unsigned short status; /* Bytes 2-3 */
+};
+
+struct myrb_cmdblk {
+ union myrb_cmd_mbox mbox;
+ unsigned short status;
+ struct completion *completion;
+ struct myrb_dcdb *dcdb;
+ dma_addr_t dcdb_addr;
+ struct myrb_sge *sgl;
+ dma_addr_t sgl_addr;
+};
+
+struct myrb_hba {
+ unsigned int ldev_block_size;
+ unsigned char ldev_geom_heads;
+ unsigned char ldev_geom_sectors;
+ unsigned char bus_width;
+ unsigned short stripe_size;
+ unsigned short segment_size;
+ unsigned short new_ev_seq;
+ unsigned short old_ev_seq;
+ bool dual_mode_interface;
+ bool bgi_status_supported;
+ bool safte_enabled;
+ bool need_ldev_info;
+ bool need_err_info;
+ bool need_rbld;
+ bool need_cc_status;
+ bool need_bgi_status;
+ bool rbld_first;
+
+ struct pci_dev *pdev;
+ struct Scsi_Host *host;
+
+ struct workqueue_struct *work_q;
+ struct delayed_work monitor_work;
+ unsigned long primary_monitor_time;
+ unsigned long secondary_monitor_time;
+
+ struct dma_pool *sg_pool;
+ struct dma_pool *dcdb_pool;
+
+ spinlock_t queue_lock;
+
+ void (*qcmd)(struct myrb_hba *cs, struct myrb_cmdblk *cmd_blk);
+ void (*write_cmd_mbox)(union myrb_cmd_mbox *next_mbox,
+ union myrb_cmd_mbox *cmd_mbox);
+ void (*get_cmd_mbox)(void __iomem *base);
+ void (*disable_intr)(void __iomem *base);
+ void (*reset)(void __iomem *base);
+
+ unsigned int ctlr_num;
+ unsigned char model_name[20];
+ unsigned char fw_version[12];
+
+ unsigned int irq;
+ phys_addr_t io_addr;
+ phys_addr_t pci_addr;
+ void __iomem *io_base;
+ void __iomem *mmio_base;
+
+ size_t cmd_mbox_size;
+ dma_addr_t cmd_mbox_addr;
+ union myrb_cmd_mbox *first_cmd_mbox;
+ union myrb_cmd_mbox *last_cmd_mbox;
+ union myrb_cmd_mbox *next_cmd_mbox;
+ union myrb_cmd_mbox *prev_cmd_mbox1;
+ union myrb_cmd_mbox *prev_cmd_mbox2;
+
+ size_t stat_mbox_size;
+ dma_addr_t stat_mbox_addr;
+ struct myrb_stat_mbox *first_stat_mbox;
+ struct myrb_stat_mbox *last_stat_mbox;
+ struct myrb_stat_mbox *next_stat_mbox;
+
+ struct myrb_cmdblk dcmd_blk;
+ struct myrb_cmdblk mcmd_blk;
+ struct mutex dcmd_mutex;
+
+ struct myrb_enquiry *enquiry;
+ dma_addr_t enquiry_addr;
+
+ struct myrb_error_entry *err_table;
+ dma_addr_t err_table_addr;
+
+ unsigned short last_rbld_status;
+
+ struct myrb_ldev_info *ldev_info_buf;
+ dma_addr_t ldev_info_addr;
+
+ struct myrb_bgi_status bgi_status;
+
+ struct mutex dma_mutex;
+};
+
+/*
+ * DAC960 LA Series Controller Interface Register Offsets.
+ */
+#define DAC960_LA_mmio_size 0x80
+
+enum DAC960_LA_reg_offset {
+ DAC960_LA_IRQMASK_OFFSET = 0x34,
+ DAC960_LA_CMDOP_OFFSET = 0x50,
+ DAC960_LA_CMDID_OFFSET = 0x51,
+ DAC960_LA_MBOX2_OFFSET = 0x52,
+ DAC960_LA_MBOX3_OFFSET = 0x53,
+ DAC960_LA_MBOX4_OFFSET = 0x54,
+ DAC960_LA_MBOX5_OFFSET = 0x55,
+ DAC960_LA_MBOX6_OFFSET = 0x56,
+ DAC960_LA_MBOX7_OFFSET = 0x57,
+ DAC960_LA_MBOX8_OFFSET = 0x58,
+ DAC960_LA_MBOX9_OFFSET = 0x59,
+ DAC960_LA_MBOX10_OFFSET = 0x5A,
+ DAC960_LA_MBOX11_OFFSET = 0x5B,
+ DAC960_LA_MBOX12_OFFSET = 0x5C,
+ DAC960_LA_STSID_OFFSET = 0x5D,
+ DAC960_LA_STS_OFFSET = 0x5E,
+ DAC960_LA_IDB_OFFSET = 0x60,
+ DAC960_LA_ODB_OFFSET = 0x61,
+ DAC960_LA_ERRSTS_OFFSET = 0x63,
+};
+
+/*
+ * DAC960 LA Series Inbound Door Bell Register.
+ */
+#define DAC960_LA_IDB_HWMBOX_NEW_CMD 0x01
+#define DAC960_LA_IDB_HWMBOX_ACK_STS 0x02
+#define DAC960_LA_IDB_GEN_IRQ 0x04
+#define DAC960_LA_IDB_CTRL_RESET 0x08
+#define DAC960_LA_IDB_MMBOX_NEW_CMD 0x10
+
+#define DAC960_LA_IDB_HWMBOX_EMPTY 0x01
+#define DAC960_LA_IDB_INIT_DONE 0x02
+
+/*
+ * DAC960 LA Series Outbound Door Bell Register.
+ */
+#define DAC960_LA_ODB_HWMBOX_ACK_IRQ 0x01
+#define DAC960_LA_ODB_MMBOX_ACK_IRQ 0x02
+#define DAC960_LA_ODB_HWMBOX_STS_AVAIL 0x01
+#define DAC960_LA_ODB_MMBOX_STS_AVAIL 0x02
+
+/*
+ * DAC960 LA Series Interrupt Mask Register.
+ */
+#define DAC960_LA_IRQMASK_DISABLE_IRQ 0x04
+
+/*
+ * DAC960 LA Series Error Status Register.
+ */
+#define DAC960_LA_ERRSTS_PENDING 0x02
+
+/*
+ * DAC960 PG Series Controller Interface Register Offsets.
+ */
+#define DAC960_PG_mmio_size 0x2000
+
+enum DAC960_PG_reg_offset {
+ DAC960_PG_IDB_OFFSET = 0x0020,
+ DAC960_PG_ODB_OFFSET = 0x002C,
+ DAC960_PG_IRQMASK_OFFSET = 0x0034,
+ DAC960_PG_CMDOP_OFFSET = 0x1000,
+ DAC960_PG_CMDID_OFFSET = 0x1001,
+ DAC960_PG_MBOX2_OFFSET = 0x1002,
+ DAC960_PG_MBOX3_OFFSET = 0x1003,
+ DAC960_PG_MBOX4_OFFSET = 0x1004,
+ DAC960_PG_MBOX5_OFFSET = 0x1005,
+ DAC960_PG_MBOX6_OFFSET = 0x1006,
+ DAC960_PG_MBOX7_OFFSET = 0x1007,
+ DAC960_PG_MBOX8_OFFSET = 0x1008,
+ DAC960_PG_MBOX9_OFFSET = 0x1009,
+ DAC960_PG_MBOX10_OFFSET = 0x100A,
+ DAC960_PG_MBOX11_OFFSET = 0x100B,
+ DAC960_PG_MBOX12_OFFSET = 0x100C,
+ DAC960_PG_STSID_OFFSET = 0x1018,
+ DAC960_PG_STS_OFFSET = 0x101A,
+ DAC960_PG_ERRSTS_OFFSET = 0x103F,
+};
+
+/*
+ * DAC960 PG Series Inbound Door Bell Register.
+ */
+#define DAC960_PG_IDB_HWMBOX_NEW_CMD 0x01
+#define DAC960_PG_IDB_HWMBOX_ACK_STS 0x02
+#define DAC960_PG_IDB_GEN_IRQ 0x04
+#define DAC960_PG_IDB_CTRL_RESET 0x08
+#define DAC960_PG_IDB_MMBOX_NEW_CMD 0x10
+
+#define DAC960_PG_IDB_HWMBOX_FULL 0x01
+#define DAC960_PG_IDB_INIT_IN_PROGRESS 0x02
+
+/*
+ * DAC960 PG Series Outbound Door Bell Register.
+ */
+#define DAC960_PG_ODB_HWMBOX_ACK_IRQ 0x01
+#define DAC960_PG_ODB_MMBOX_ACK_IRQ 0x02
+#define DAC960_PG_ODB_HWMBOX_STS_AVAIL 0x01
+#define DAC960_PG_ODB_MMBOX_STS_AVAIL 0x02
+
+/*
+ * DAC960 PG Series Interrupt Mask Register.
+ */
+#define DAC960_PG_IRQMASK_MSI_MASK1 0x03
+#define DAC960_PG_IRQMASK_DISABLE_IRQ 0x04
+#define DAC960_PG_IRQMASK_MSI_MASK2 0xF8
+
+/*
+ * DAC960 PG Series Error Status Register.
+ */
+#define DAC960_PG_ERRSTS_PENDING 0x04
+
+/*
+ * DAC960 PD Series Controller Interface Register Offsets.
+ */
+#define DAC960_PD_mmio_size 0x80
+
+enum DAC960_PD_reg_offset {
+ DAC960_PD_CMDOP_OFFSET = 0x00,
+ DAC960_PD_CMDID_OFFSET = 0x01,
+ DAC960_PD_MBOX2_OFFSET = 0x02,
+ DAC960_PD_MBOX3_OFFSET = 0x03,
+ DAC960_PD_MBOX4_OFFSET = 0x04,
+ DAC960_PD_MBOX5_OFFSET = 0x05,
+ DAC960_PD_MBOX6_OFFSET = 0x06,
+ DAC960_PD_MBOX7_OFFSET = 0x07,
+ DAC960_PD_MBOX8_OFFSET = 0x08,
+ DAC960_PD_MBOX9_OFFSET = 0x09,
+ DAC960_PD_MBOX10_OFFSET = 0x0A,
+ DAC960_PD_MBOX11_OFFSET = 0x0B,
+ DAC960_PD_MBOX12_OFFSET = 0x0C,
+ DAC960_PD_STSID_OFFSET = 0x0D,
+ DAC960_PD_STS_OFFSET = 0x0E,
+ DAC960_PD_ERRSTS_OFFSET = 0x3F,
+ DAC960_PD_IDB_OFFSET = 0x40,
+ DAC960_PD_ODB_OFFSET = 0x41,
+ DAC960_PD_IRQEN_OFFSET = 0x43,
+};
+
+/*
+ * DAC960 PD Series Inbound Door Bell Register.
+ */
+#define DAC960_PD_IDB_HWMBOX_NEW_CMD 0x01
+#define DAC960_PD_IDB_HWMBOX_ACK_STS 0x02
+#define DAC960_PD_IDB_GEN_IRQ 0x04
+#define DAC960_PD_IDB_CTRL_RESET 0x08
+
+#define DAC960_PD_IDB_HWMBOX_FULL 0x01
+#define DAC960_PD_IDB_INIT_IN_PROGRESS 0x02
+
+/*
+ * DAC960 PD Series Outbound Door Bell Register.
+ */
+#define DAC960_PD_ODB_HWMBOX_ACK_IRQ 0x01
+#define DAC960_PD_ODB_HWMBOX_STS_AVAIL 0x01
+
+/*
+ * DAC960 PD Series Interrupt Enable Register.
+ */
+#define DAC960_PD_IRQMASK_ENABLE_IRQ 0x01
+
+/*
+ * DAC960 PD Series Error Status Register.
+ */
+#define DAC960_PD_ERRSTS_PENDING 0x04
+
+typedef int (*myrb_hw_init_t)(struct pci_dev *pdev,
+ struct myrb_hba *cb, void __iomem *base);
+typedef unsigned short (*mbox_mmio_init_t)(struct pci_dev *pdev,
+ void __iomem *base,
+ union myrb_cmd_mbox *mbox);
+
+struct myrb_privdata {
+ myrb_hw_init_t hw_init;
+ irq_handler_t irq_handler;
+ unsigned int mmio_size;
+};
+
+#endif /* MYRB_H */
diff --git a/drivers/scsi/myrs.c b/drivers/scsi/myrs.c
new file mode 100644
index 000000000000..a58abd796603
--- /dev/null
+++ b/drivers/scsi/myrs.c
@@ -0,0 +1,3168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
+ *
+ * This driver supports the newer, SCSI-based firmware interface only.
+ *
+ * Copyright 2017 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com>
+ *
+ * Based on the original DAC960 driver, which has
+ * Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com>
+ * Portions Copyright 2002 by Mylex (An IBM Business Unit)
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/raid_class.h>
+#include <linux/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include "myrs.h"
+
+static struct raid_template *myrs_raid_template;
+
+static struct myrs_devstate_name_entry {
+ enum myrs_devstate state;
+ char *name;
+} myrs_devstate_name_list[] = {
+ { MYRS_DEVICE_UNCONFIGURED, "Unconfigured" },
+ { MYRS_DEVICE_ONLINE, "Online" },
+ { MYRS_DEVICE_REBUILD, "Rebuild" },
+ { MYRS_DEVICE_MISSING, "Missing" },
+ { MYRS_DEVICE_SUSPECTED_CRITICAL, "SuspectedCritical" },
+ { MYRS_DEVICE_OFFLINE, "Offline" },
+ { MYRS_DEVICE_CRITICAL, "Critical" },
+ { MYRS_DEVICE_SUSPECTED_DEAD, "SuspectedDead" },
+ { MYRS_DEVICE_COMMANDED_OFFLINE, "CommandedOffline" },
+ { MYRS_DEVICE_STANDBY, "Standby" },
+ { MYRS_DEVICE_INVALID_STATE, "Invalid" },
+};
+
+static char *myrs_devstate_name(enum myrs_devstate state)
+{
+ struct myrs_devstate_name_entry *entry = myrs_devstate_name_list;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(myrs_devstate_name_list); i++) {
+ if (entry[i].state == state)
+ return entry[i].name;
+ }
+ return NULL;
+}
+
+static struct myrs_raid_level_name_entry {
+ enum myrs_raid_level level;
+ char *name;
+} myrs_raid_level_name_list[] = {
+ { MYRS_RAID_LEVEL0, "RAID0" },
+ { MYRS_RAID_LEVEL1, "RAID1" },
+ { MYRS_RAID_LEVEL3, "RAID3 right asymmetric parity" },
+ { MYRS_RAID_LEVEL5, "RAID5 right asymmetric parity" },
+ { MYRS_RAID_LEVEL6, "RAID6" },
+ { MYRS_RAID_JBOD, "JBOD" },
+ { MYRS_RAID_NEWSPAN, "New Mylex SPAN" },
+ { MYRS_RAID_LEVEL3F, "RAID3 fixed parity" },
+ { MYRS_RAID_LEVEL3L, "RAID3 left symmetric parity" },
+ { MYRS_RAID_SPAN, "Mylex SPAN" },
+ { MYRS_RAID_LEVEL5L, "RAID5 left symmetric parity" },
+ { MYRS_RAID_LEVELE, "RAIDE (concatenation)" },
+ { MYRS_RAID_PHYSICAL, "Physical device" },
+};
+
+static char *myrs_raid_level_name(enum myrs_raid_level level)
+{
+ struct myrs_raid_level_name_entry *entry = myrs_raid_level_name_list;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(myrs_raid_level_name_list); i++) {
+ if (entry[i].level == level)
+ return entry[i].name;
+ }
+ return NULL;
+}
+
+/*
+ * myrs_reset_cmd - clears critical fields in struct myrs_cmdblk
+ */
+static inline void myrs_reset_cmd(struct myrs_cmdblk *cmd_blk)
+{
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+
+ memset(mbox, 0, sizeof(union myrs_cmd_mbox));
+ cmd_blk->status = 0;
+}
+
+/*
+ * myrs_qcmd - queues Command for DAC960 V2 Series Controllers.
+ */
+static void myrs_qcmd(struct myrs_hba *cs, struct myrs_cmdblk *cmd_blk)
+{
+ void __iomem *base = cs->io_base;
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ union myrs_cmd_mbox *next_mbox = cs->next_cmd_mbox;
+
+ cs->write_cmd_mbox(next_mbox, mbox);
+
+ if (cs->prev_cmd_mbox1->words[0] == 0 ||
+ cs->prev_cmd_mbox2->words[0] == 0)
+ cs->get_cmd_mbox(base);
+
+ cs->prev_cmd_mbox2 = cs->prev_cmd_mbox1;
+ cs->prev_cmd_mbox1 = next_mbox;
+
+ if (++next_mbox > cs->last_cmd_mbox)
+ next_mbox = cs->first_cmd_mbox;
+
+ cs->next_cmd_mbox = next_mbox;
+}
+
+/*
+ * myrs_exec_cmd - executes V2 Command and waits for completion.
+ */
+static void myrs_exec_cmd(struct myrs_hba *cs,
+ struct myrs_cmdblk *cmd_blk)
+{
+ DECLARE_COMPLETION_ONSTACK(complete);
+ unsigned long flags;
+
+ cmd_blk->complete = &complete;
+ spin_lock_irqsave(&cs->queue_lock, flags);
+ myrs_qcmd(cs, cmd_blk);
+ spin_unlock_irqrestore(&cs->queue_lock, flags);
+
+ wait_for_completion(&complete);
+}
+
+/*
+ * myrs_report_progress - prints progress message
+ */
+static void myrs_report_progress(struct myrs_hba *cs, unsigned short ldev_num,
+ unsigned char *msg, unsigned long blocks,
+ unsigned long size)
+{
+ shost_printk(KERN_INFO, cs->host,
+ "Logical Drive %d: %s in Progress: %d%% completed\n",
+ ldev_num, msg,
+ (100 * (int)(blocks >> 7)) / (int)(size >> 7));
+}
+
+/*
+ * myrs_get_ctlr_info - executes a Controller Information IOCTL Command
+ */
+static unsigned char myrs_get_ctlr_info(struct myrs_hba *cs)
+{
+ struct myrs_cmdblk *cmd_blk = &cs->dcmd_blk;
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ dma_addr_t ctlr_info_addr;
+ union myrs_sgl *sgl;
+ unsigned char status;
+ unsigned short ldev_present, ldev_critical, ldev_offline;
+
+ ldev_present = cs->ctlr_info->ldev_present;
+ ldev_critical = cs->ctlr_info->ldev_critical;
+ ldev_offline = cs->ctlr_info->ldev_offline;
+
+ ctlr_info_addr = dma_map_single(&cs->pdev->dev, cs->ctlr_info,
+ sizeof(struct myrs_ctlr_info),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&cs->pdev->dev, ctlr_info_addr))
+ return MYRS_STATUS_FAILED;
+
+ mutex_lock(&cs->dcmd_mutex);
+ myrs_reset_cmd(cmd_blk);
+ mbox->ctlr_info.id = MYRS_DCMD_TAG;
+ mbox->ctlr_info.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->ctlr_info.control.dma_ctrl_to_host = true;
+ mbox->ctlr_info.control.no_autosense = true;
+ mbox->ctlr_info.dma_size = sizeof(struct myrs_ctlr_info);
+ mbox->ctlr_info.ctlr_num = 0;
+ mbox->ctlr_info.ioctl_opcode = MYRS_IOCTL_GET_CTLR_INFO;
+ sgl = &mbox->ctlr_info.dma_addr;
+ sgl->sge[0].sge_addr = ctlr_info_addr;
+ sgl->sge[0].sge_count = mbox->ctlr_info.dma_size;
+ dev_dbg(&cs->host->shost_gendev, "Sending GetControllerInfo\n");
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ dma_unmap_single(&cs->pdev->dev, ctlr_info_addr,
+ sizeof(struct myrs_ctlr_info), DMA_FROM_DEVICE);
+ if (status == MYRS_STATUS_SUCCESS) {
+ if (cs->ctlr_info->bg_init_active +
+ cs->ctlr_info->ldev_init_active +
+ cs->ctlr_info->pdev_init_active +
+ cs->ctlr_info->cc_active +
+ cs->ctlr_info->rbld_active +
+ cs->ctlr_info->exp_active != 0)
+ cs->needs_update = true;
+ if (cs->ctlr_info->ldev_present != ldev_present ||
+ cs->ctlr_info->ldev_critical != ldev_critical ||
+ cs->ctlr_info->ldev_offline != ldev_offline)
+ shost_printk(KERN_INFO, cs->host,
+ "Logical drive count changes (%d/%d/%d)\n",
+ cs->ctlr_info->ldev_critical,
+ cs->ctlr_info->ldev_offline,
+ cs->ctlr_info->ldev_present);
+ }
+
+ return status;
+}
+
+/*
+ * myrs_get_ldev_info - executes a Logical Device Information IOCTL Command
+ */
+static unsigned char myrs_get_ldev_info(struct myrs_hba *cs,
+ unsigned short ldev_num, struct myrs_ldev_info *ldev_info)
+{
+ struct myrs_cmdblk *cmd_blk = &cs->dcmd_blk;
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ dma_addr_t ldev_info_addr;
+ struct myrs_ldev_info ldev_info_orig;
+ union myrs_sgl *sgl;
+ unsigned char status;
+
+ memcpy(&ldev_info_orig, ldev_info, sizeof(struct myrs_ldev_info));
+ ldev_info_addr = dma_map_single(&cs->pdev->dev, ldev_info,
+ sizeof(struct myrs_ldev_info),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&cs->pdev->dev, ldev_info_addr))
+ return MYRS_STATUS_FAILED;
+
+ mutex_lock(&cs->dcmd_mutex);
+ myrs_reset_cmd(cmd_blk);
+ mbox->ldev_info.id = MYRS_DCMD_TAG;
+ mbox->ldev_info.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->ldev_info.control.dma_ctrl_to_host = true;
+ mbox->ldev_info.control.no_autosense = true;
+ mbox->ldev_info.dma_size = sizeof(struct myrs_ldev_info);
+ mbox->ldev_info.ldev.ldev_num = ldev_num;
+ mbox->ldev_info.ioctl_opcode = MYRS_IOCTL_GET_LDEV_INFO_VALID;
+ sgl = &mbox->ldev_info.dma_addr;
+ sgl->sge[0].sge_addr = ldev_info_addr;
+ sgl->sge[0].sge_count = mbox->ldev_info.dma_size;
+ dev_dbg(&cs->host->shost_gendev,
+ "Sending GetLogicalDeviceInfoValid for ldev %d\n", ldev_num);
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ dma_unmap_single(&cs->pdev->dev, ldev_info_addr,
+ sizeof(struct myrs_ldev_info), DMA_FROM_DEVICE);
+ if (status == MYRS_STATUS_SUCCESS) {
+ unsigned short ldev_num = ldev_info->ldev_num;
+ struct myrs_ldev_info *new = ldev_info;
+ struct myrs_ldev_info *old = &ldev_info_orig;
+ unsigned long ldev_size = new->cfg_devsize;
+
+ if (new->dev_state != old->dev_state) {
+ const char *name;
+
+ name = myrs_devstate_name(new->dev_state);
+ shost_printk(KERN_INFO, cs->host,
+ "Logical Drive %d is now %s\n",
+ ldev_num, name ? name : "Invalid");
+ }
+ if ((new->soft_errs != old->soft_errs) ||
+ (new->cmds_failed != old->cmds_failed) ||
+ (new->deferred_write_errs != old->deferred_write_errs))
+ shost_printk(KERN_INFO, cs->host,
+ "Logical Drive %d Errors: Soft = %d, Failed = %d, Deferred Write = %d\n",
+ ldev_num, new->soft_errs,
+ new->cmds_failed,
+ new->deferred_write_errs);
+ if (new->bg_init_active)
+ myrs_report_progress(cs, ldev_num,
+ "Background Initialization",
+ new->bg_init_lba, ldev_size);
+ else if (new->fg_init_active)
+ myrs_report_progress(cs, ldev_num,
+ "Foreground Initialization",
+ new->fg_init_lba, ldev_size);
+ else if (new->migration_active)
+ myrs_report_progress(cs, ldev_num,
+ "Data Migration",
+ new->migration_lba, ldev_size);
+ else if (new->patrol_active)
+ myrs_report_progress(cs, ldev_num,
+ "Patrol Operation",
+ new->patrol_lba, ldev_size);
+ if (old->bg_init_active && !new->bg_init_active)
+ shost_printk(KERN_INFO, cs->host,
+ "Logical Drive %d: Background Initialization %s\n",
+ ldev_num,
+ (new->ldev_control.ldev_init_done ?
+ "Completed" : "Failed"));
+ }
+ return status;
+}
+
+/*
+ * myrs_get_pdev_info - executes a "Read Physical Device Information" Command
+ */
+static unsigned char myrs_get_pdev_info(struct myrs_hba *cs,
+ unsigned char channel, unsigned char target, unsigned char lun,
+ struct myrs_pdev_info *pdev_info)
+{
+ struct myrs_cmdblk *cmd_blk = &cs->dcmd_blk;
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ dma_addr_t pdev_info_addr;
+ union myrs_sgl *sgl;
+ unsigned char status;
+
+ pdev_info_addr = dma_map_single(&cs->pdev->dev, pdev_info,
+ sizeof(struct myrs_pdev_info),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&cs->pdev->dev, pdev_info_addr))
+ return MYRS_STATUS_FAILED;
+
+ mutex_lock(&cs->dcmd_mutex);
+ myrs_reset_cmd(cmd_blk);
+ mbox->pdev_info.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->pdev_info.id = MYRS_DCMD_TAG;
+ mbox->pdev_info.control.dma_ctrl_to_host = true;
+ mbox->pdev_info.control.no_autosense = true;
+ mbox->pdev_info.dma_size = sizeof(struct myrs_pdev_info);
+ mbox->pdev_info.pdev.lun = lun;
+ mbox->pdev_info.pdev.target = target;
+ mbox->pdev_info.pdev.channel = channel;
+ mbox->pdev_info.ioctl_opcode = MYRS_IOCTL_GET_PDEV_INFO_VALID;
+ sgl = &mbox->pdev_info.dma_addr;
+ sgl->sge[0].sge_addr = pdev_info_addr;
+ sgl->sge[0].sge_count = mbox->pdev_info.dma_size;
+ dev_dbg(&cs->host->shost_gendev,
+ "Sending GetPhysicalDeviceInfoValid for pdev %d:%d:%d\n",
+ channel, target, lun);
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ dma_unmap_single(&cs->pdev->dev, pdev_info_addr,
+ sizeof(struct myrs_pdev_info), DMA_FROM_DEVICE);
+ return status;
+}
+
+/*
+ * myrs_dev_op - executes a "Device Operation" Command
+ */
+static unsigned char myrs_dev_op(struct myrs_hba *cs,
+ enum myrs_ioctl_opcode opcode, enum myrs_opdev opdev)
+{
+ struct myrs_cmdblk *cmd_blk = &cs->dcmd_blk;
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ unsigned char status;
+
+ mutex_lock(&cs->dcmd_mutex);
+ myrs_reset_cmd(cmd_blk);
+ mbox->dev_op.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->dev_op.id = MYRS_DCMD_TAG;
+ mbox->dev_op.control.dma_ctrl_to_host = true;
+ mbox->dev_op.control.no_autosense = true;
+ mbox->dev_op.ioctl_opcode = opcode;
+ mbox->dev_op.opdev = opdev;
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ return status;
+}
+
+/*
+ * myrs_translate_pdev - translates a Physical Device Channel and
+ * TargetID into a Logical Device.
+ */
+static unsigned char myrs_translate_pdev(struct myrs_hba *cs,
+ unsigned char channel, unsigned char target, unsigned char lun,
+ struct myrs_devmap *devmap)
+{
+ struct pci_dev *pdev = cs->pdev;
+ dma_addr_t devmap_addr;
+ struct myrs_cmdblk *cmd_blk;
+ union myrs_cmd_mbox *mbox;
+ union myrs_sgl *sgl;
+ unsigned char status;
+
+ memset(devmap, 0x0, sizeof(struct myrs_devmap));
+ devmap_addr = dma_map_single(&pdev->dev, devmap,
+ sizeof(struct myrs_devmap),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&pdev->dev, devmap_addr))
+ return MYRS_STATUS_FAILED;
+
+ mutex_lock(&cs->dcmd_mutex);
+ cmd_blk = &cs->dcmd_blk;
+ mbox = &cmd_blk->mbox;
+ mbox->pdev_info.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->pdev_info.control.dma_ctrl_to_host = true;
+ mbox->pdev_info.control.no_autosense = true;
+ mbox->pdev_info.dma_size = sizeof(struct myrs_devmap);
+ mbox->pdev_info.pdev.target = target;
+ mbox->pdev_info.pdev.channel = channel;
+ mbox->pdev_info.pdev.lun = lun;
+ mbox->pdev_info.ioctl_opcode = MYRS_IOCTL_XLATE_PDEV_TO_LDEV;
+ sgl = &mbox->pdev_info.dma_addr;
+ sgl->sge[0].sge_addr = devmap_addr;
+ sgl->sge[0].sge_count = mbox->pdev_info.dma_size;
+
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ dma_unmap_single(&pdev->dev, devmap_addr,
+ sizeof(struct myrs_devmap), DMA_FROM_DEVICE);
+ return status;
+}
+
+/*
+ * myrs_get_event - executes a Get Event Command
+ */
+static unsigned char myrs_get_event(struct myrs_hba *cs,
+ unsigned int event_num, struct myrs_event *event_buf)
+{
+ struct pci_dev *pdev = cs->pdev;
+ dma_addr_t event_addr;
+ struct myrs_cmdblk *cmd_blk = &cs->mcmd_blk;
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ union myrs_sgl *sgl;
+ unsigned char status;
+
+ event_addr = dma_map_single(&pdev->dev, event_buf,
+ sizeof(struct myrs_event), DMA_FROM_DEVICE);
+ if (dma_mapping_error(&pdev->dev, event_addr))
+ return MYRS_STATUS_FAILED;
+
+ mbox->get_event.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->get_event.dma_size = sizeof(struct myrs_event);
+ mbox->get_event.evnum_upper = event_num >> 16;
+ mbox->get_event.ctlr_num = 0;
+ mbox->get_event.ioctl_opcode = MYRS_IOCTL_GET_EVENT;
+ mbox->get_event.evnum_lower = event_num & 0xFFFF;
+ sgl = &mbox->get_event.dma_addr;
+ sgl->sge[0].sge_addr = event_addr;
+ sgl->sge[0].sge_count = mbox->get_event.dma_size;
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ dma_unmap_single(&pdev->dev, event_addr,
+ sizeof(struct myrs_event), DMA_FROM_DEVICE);
+
+ return status;
+}
+
+/*
+ * myrs_get_fwstatus - executes a Get Health Status Command
+ */
+static unsigned char myrs_get_fwstatus(struct myrs_hba *cs)
+{
+ struct myrs_cmdblk *cmd_blk = &cs->mcmd_blk;
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ union myrs_sgl *sgl;
+ unsigned char status = cmd_blk->status;
+
+ myrs_reset_cmd(cmd_blk);
+ mbox->common.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->common.id = MYRS_MCMD_TAG;
+ mbox->common.control.dma_ctrl_to_host = true;
+ mbox->common.control.no_autosense = true;
+ mbox->common.dma_size = sizeof(struct myrs_fwstat);
+ mbox->common.ioctl_opcode = MYRS_IOCTL_GET_HEALTH_STATUS;
+ sgl = &mbox->common.dma_addr;
+ sgl->sge[0].sge_addr = cs->fwstat_addr;
+ sgl->sge[0].sge_count = mbox->ctlr_info.dma_size;
+ dev_dbg(&cs->host->shost_gendev, "Sending GetHealthStatus\n");
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+
+ return status;
+}
+
+/*
+ * myrs_enable_mmio_mbox - enables the Memory Mailbox Interface
+ */
+static bool myrs_enable_mmio_mbox(struct myrs_hba *cs,
+ enable_mbox_t enable_mbox_fn)
+{
+ void __iomem *base = cs->io_base;
+ struct pci_dev *pdev = cs->pdev;
+ union myrs_cmd_mbox *cmd_mbox;
+ struct myrs_stat_mbox *stat_mbox;
+ union myrs_cmd_mbox *mbox;
+ dma_addr_t mbox_addr;
+ unsigned char status = MYRS_STATUS_FAILED;
+
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
+ dev_err(&pdev->dev, "DMA mask out of range\n");
+ return false;
+ }
+
+ /* Temporary dma mapping, used only in the scope of this function */
+ mbox = dma_alloc_coherent(&pdev->dev, sizeof(union myrs_cmd_mbox),
+ &mbox_addr, GFP_KERNEL);
+ if (!mbox)
+ return false;
+
+ /* These are the base addresses for the command memory mailbox array */
+ cs->cmd_mbox_size = MYRS_MAX_CMD_MBOX * sizeof(union myrs_cmd_mbox);
+ cmd_mbox = dma_alloc_coherent(&pdev->dev, cs->cmd_mbox_size,
+ &cs->cmd_mbox_addr, GFP_KERNEL);
+ if (!cmd_mbox) {
+ dev_err(&pdev->dev, "Failed to map command mailbox\n");
+ goto out_free;
+ }
+ cs->first_cmd_mbox = cmd_mbox;
+ cmd_mbox += MYRS_MAX_CMD_MBOX - 1;
+ cs->last_cmd_mbox = cmd_mbox;
+ cs->next_cmd_mbox = cs->first_cmd_mbox;
+ cs->prev_cmd_mbox1 = cs->last_cmd_mbox;
+ cs->prev_cmd_mbox2 = cs->last_cmd_mbox - 1;
+
+ /* These are the base addresses for the status memory mailbox array */
+ cs->stat_mbox_size = MYRS_MAX_STAT_MBOX * sizeof(struct myrs_stat_mbox);
+ stat_mbox = dma_alloc_coherent(&pdev->dev, cs->stat_mbox_size,
+ &cs->stat_mbox_addr, GFP_KERNEL);
+ if (!stat_mbox) {
+ dev_err(&pdev->dev, "Failed to map status mailbox\n");
+ goto out_free;
+ }
+
+ cs->first_stat_mbox = stat_mbox;
+ stat_mbox += MYRS_MAX_STAT_MBOX - 1;
+ cs->last_stat_mbox = stat_mbox;
+ cs->next_stat_mbox = cs->first_stat_mbox;
+
+ cs->fwstat_buf = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct myrs_fwstat),
+ &cs->fwstat_addr, GFP_KERNEL);
+ if (!cs->fwstat_buf) {
+ dev_err(&pdev->dev, "Failed to map firmware health buffer\n");
+ cs->fwstat_buf = NULL;
+ goto out_free;
+ }
+ cs->ctlr_info = kzalloc(sizeof(struct myrs_ctlr_info), GFP_KERNEL);
+ if (!cs->ctlr_info)
+ goto out_free;
+
+ cs->event_buf = kzalloc(sizeof(struct myrs_event), GFP_KERNEL);
+ if (!cs->event_buf)
+ goto out_free;
+
+ /* Enable the Memory Mailbox Interface. */
+ memset(mbox, 0, sizeof(union myrs_cmd_mbox));
+ mbox->set_mbox.id = 1;
+ mbox->set_mbox.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->set_mbox.control.no_autosense = true;
+ mbox->set_mbox.first_cmd_mbox_size_kb =
+ (MYRS_MAX_CMD_MBOX * sizeof(union myrs_cmd_mbox)) >> 10;
+ mbox->set_mbox.first_stat_mbox_size_kb =
+ (MYRS_MAX_STAT_MBOX * sizeof(struct myrs_stat_mbox)) >> 10;
+ mbox->set_mbox.second_cmd_mbox_size_kb = 0;
+ mbox->set_mbox.second_stat_mbox_size_kb = 0;
+ mbox->set_mbox.sense_len = 0;
+ mbox->set_mbox.ioctl_opcode = MYRS_IOCTL_SET_MEM_MBOX;
+ mbox->set_mbox.fwstat_buf_size_kb = 1;
+ mbox->set_mbox.fwstat_buf_addr = cs->fwstat_addr;
+ mbox->set_mbox.first_cmd_mbox_addr = cs->cmd_mbox_addr;
+ mbox->set_mbox.first_stat_mbox_addr = cs->stat_mbox_addr;
+ status = enable_mbox_fn(base, mbox_addr);
+
+out_free:
+ dma_free_coherent(&pdev->dev, sizeof(union myrs_cmd_mbox),
+ mbox, mbox_addr);
+ if (status != MYRS_STATUS_SUCCESS)
+ dev_err(&pdev->dev, "Failed to enable mailbox, status %X\n",
+ status);
+ return (status == MYRS_STATUS_SUCCESS);
+}
+
+/*
+ * myrs_get_config - reads the Configuration Information
+ */
+static int myrs_get_config(struct myrs_hba *cs)
+{
+ struct myrs_ctlr_info *info = cs->ctlr_info;
+ struct Scsi_Host *shost = cs->host;
+ unsigned char status;
+ unsigned char model[20];
+ unsigned char fw_version[12];
+ int i, model_len;
+
+ /* Get data into dma-able area, then copy into permanent location */
+ mutex_lock(&cs->cinfo_mutex);
+ status = myrs_get_ctlr_info(cs);
+ mutex_unlock(&cs->cinfo_mutex);
+ if (status != MYRS_STATUS_SUCCESS) {
+ shost_printk(KERN_ERR, shost,
+ "Failed to get controller information\n");
+ return -ENODEV;
+ }
+
+ /* Initialize the Controller Model Name and Full Model Name fields. */
+ model_len = sizeof(info->ctlr_name);
+ if (model_len > sizeof(model)-1)
+ model_len = sizeof(model)-1;
+ memcpy(model, info->ctlr_name, model_len);
+ model_len--;
+ while (model[model_len] == ' ' || model[model_len] == '\0')
+ model_len--;
+ model[++model_len] = '\0';
+ strcpy(cs->model_name, "DAC960 ");
+ strcat(cs->model_name, model);
+ /* Initialize the Controller Firmware Version field. */
+ sprintf(fw_version, "%d.%02d-%02d",
+ info->fw_major_version, info->fw_minor_version,
+ info->fw_turn_number);
+ if (info->fw_major_version == 6 &&
+ info->fw_minor_version == 0 &&
+ info->fw_turn_number < 1) {
+ shost_printk(KERN_WARNING, shost,
+ "FIRMWARE VERSION %s DOES NOT PROVIDE THE CONTROLLER\n"
+ "STATUS MONITORING FUNCTIONALITY NEEDED BY THIS DRIVER.\n"
+ "PLEASE UPGRADE TO VERSION 6.00-01 OR ABOVE.\n",
+ fw_version);
+ return -ENODEV;
+ }
+ /* Initialize the Controller Channels and Targets. */
+ shost->max_channel = info->physchan_present + info->virtchan_present;
+ shost->max_id = info->max_targets[0];
+ for (i = 1; i < 16; i++) {
+ if (!info->max_targets[i])
+ continue;
+ if (shost->max_id < info->max_targets[i])
+ shost->max_id = info->max_targets[i];
+ }
+
+ /*
+ * Initialize the Controller Queue Depth, Driver Queue Depth,
+ * Logical Drive Count, Maximum Blocks per Command, Controller
+ * Scatter/Gather Limit, and Driver Scatter/Gather Limit.
+ * The Driver Queue Depth must be at most three less than
+ * the Controller Queue Depth; tag '1' is reserved for
+ * direct commands, and tag '2' for monitoring commands.
+ */
+ shost->can_queue = info->max_tcq - 3;
+ if (shost->can_queue > MYRS_MAX_CMD_MBOX - 3)
+ shost->can_queue = MYRS_MAX_CMD_MBOX - 3;
+ shost->max_sectors = info->max_transfer_size;
+ shost->sg_tablesize = info->max_sge;
+ if (shost->sg_tablesize > MYRS_SG_LIMIT)
+ shost->sg_tablesize = MYRS_SG_LIMIT;
+
+ shost_printk(KERN_INFO, shost,
+ "Configuring %s PCI RAID Controller\n", model);
+ shost_printk(KERN_INFO, shost,
+ " Firmware Version: %s, Channels: %d, Memory Size: %dMB\n",
+ fw_version, info->physchan_present, info->mem_size_mb);
+
+ shost_printk(KERN_INFO, shost,
+ " Controller Queue Depth: %d, Maximum Blocks per Command: %d\n",
+ shost->can_queue, shost->max_sectors);
+
+ shost_printk(KERN_INFO, shost,
+ " Driver Queue Depth: %d, Scatter/Gather Limit: %d of %d Segments\n",
+ shost->can_queue, shost->sg_tablesize, MYRS_SG_LIMIT);
+ for (i = 0; i < info->physchan_max; i++) {
+ if (!info->max_targets[i])
+ continue;
+ shost_printk(KERN_INFO, shost,
+ " Device Channel %d: max %d devices\n",
+ i, info->max_targets[i]);
+ }
+ shost_printk(KERN_INFO, shost,
+ " Physical: %d/%d channels, %d disks, %d devices\n",
+ info->physchan_present, info->physchan_max,
+ info->pdisk_present, info->pdev_present);
+
+ shost_printk(KERN_INFO, shost,
+ " Logical: %d/%d channels, %d disks\n",
+ info->virtchan_present, info->virtchan_max,
+ info->ldev_present);
+ return 0;
+}
+
+/*
+ * myrs_log_event - prints a Controller Event message
+ */
+static struct {
+ int ev_code;
+ unsigned char *ev_msg;
+} myrs_ev_list[] = {
+ /* Physical Device Events (0x0000 - 0x007F) */
+ { 0x0001, "P Online" },
+ { 0x0002, "P Standby" },
+ { 0x0005, "P Automatic Rebuild Started" },
+ { 0x0006, "P Manual Rebuild Started" },
+ { 0x0007, "P Rebuild Completed" },
+ { 0x0008, "P Rebuild Cancelled" },
+ { 0x0009, "P Rebuild Failed for Unknown Reasons" },
+ { 0x000A, "P Rebuild Failed due to New Physical Device" },
+ { 0x000B, "P Rebuild Failed due to Logical Drive Failure" },
+ { 0x000C, "S Offline" },
+ { 0x000D, "P Found" },
+ { 0x000E, "P Removed" },
+ { 0x000F, "P Unconfigured" },
+ { 0x0010, "P Expand Capacity Started" },
+ { 0x0011, "P Expand Capacity Completed" },
+ { 0x0012, "P Expand Capacity Failed" },
+ { 0x0013, "P Command Timed Out" },
+ { 0x0014, "P Command Aborted" },
+ { 0x0015, "P Command Retried" },
+ { 0x0016, "P Parity Error" },
+ { 0x0017, "P Soft Error" },
+ { 0x0018, "P Miscellaneous Error" },
+ { 0x0019, "P Reset" },
+ { 0x001A, "P Active Spare Found" },
+ { 0x001B, "P Warm Spare Found" },
+ { 0x001C, "S Sense Data Received" },
+ { 0x001D, "P Initialization Started" },
+ { 0x001E, "P Initialization Completed" },
+ { 0x001F, "P Initialization Failed" },
+ { 0x0020, "P Initialization Cancelled" },
+ { 0x0021, "P Failed because Write Recovery Failed" },
+ { 0x0022, "P Failed because SCSI Bus Reset Failed" },
+ { 0x0023, "P Failed because of Double Check Condition" },
+ { 0x0024, "P Failed because Device Cannot Be Accessed" },
+ { 0x0025, "P Failed because of Gross Error on SCSI Processor" },
+ { 0x0026, "P Failed because of Bad Tag from Device" },
+ { 0x0027, "P Failed because of Command Timeout" },
+ { 0x0028, "P Failed because of System Reset" },
+ { 0x0029, "P Failed because of Busy Status or Parity Error" },
+ { 0x002A, "P Failed because Host Set Device to Failed State" },
+ { 0x002B, "P Failed because of Selection Timeout" },
+ { 0x002C, "P Failed because of SCSI Bus Phase Error" },
+ { 0x002D, "P Failed because Device Returned Unknown Status" },
+ { 0x002E, "P Failed because Device Not Ready" },
+ { 0x002F, "P Failed because Device Not Found at Startup" },
+ { 0x0030, "P Failed because COD Write Operation Failed" },
+ { 0x0031, "P Failed because BDT Write Operation Failed" },
+ { 0x0039, "P Missing at Startup" },
+ { 0x003A, "P Start Rebuild Failed due to Physical Drive Too Small" },
+ { 0x003C, "P Temporarily Offline Device Automatically Made Online" },
+ { 0x003D, "P Standby Rebuild Started" },
+ /* Logical Device Events (0x0080 - 0x00FF) */
+ { 0x0080, "M Consistency Check Started" },
+ { 0x0081, "M Consistency Check Completed" },
+ { 0x0082, "M Consistency Check Cancelled" },
+ { 0x0083, "M Consistency Check Completed With Errors" },
+ { 0x0084, "M Consistency Check Failed due to Logical Drive Failure" },
+ { 0x0085, "M Consistency Check Failed due to Physical Device Failure" },
+ { 0x0086, "L Offline" },
+ { 0x0087, "L Critical" },
+ { 0x0088, "L Online" },
+ { 0x0089, "M Automatic Rebuild Started" },
+ { 0x008A, "M Manual Rebuild Started" },
+ { 0x008B, "M Rebuild Completed" },
+ { 0x008C, "M Rebuild Cancelled" },
+ { 0x008D, "M Rebuild Failed for Unknown Reasons" },
+ { 0x008E, "M Rebuild Failed due to New Physical Device" },
+ { 0x008F, "M Rebuild Failed due to Logical Drive Failure" },
+ { 0x0090, "M Initialization Started" },
+ { 0x0091, "M Initialization Completed" },
+ { 0x0092, "M Initialization Cancelled" },
+ { 0x0093, "M Initialization Failed" },
+ { 0x0094, "L Found" },
+ { 0x0095, "L Deleted" },
+ { 0x0096, "M Expand Capacity Started" },
+ { 0x0097, "M Expand Capacity Completed" },
+ { 0x0098, "M Expand Capacity Failed" },
+ { 0x0099, "L Bad Block Found" },
+ { 0x009A, "L Size Changed" },
+ { 0x009B, "L Type Changed" },
+ { 0x009C, "L Bad Data Block Found" },
+ { 0x009E, "L Read of Data Block in BDT" },
+ { 0x009F, "L Write Back Data for Disk Block Lost" },
+ { 0x00A0, "L Temporarily Offline RAID-5/3 Drive Made Online" },
+ { 0x00A1, "L Temporarily Offline RAID-6/1/0/7 Drive Made Online" },
+ { 0x00A2, "L Standby Rebuild Started" },
+ /* Fault Management Events (0x0100 - 0x017F) */
+ { 0x0140, "E Fan %d Failed" },
+ { 0x0141, "E Fan %d OK" },
+ { 0x0142, "E Fan %d Not Present" },
+ { 0x0143, "E Power Supply %d Failed" },
+ { 0x0144, "E Power Supply %d OK" },
+ { 0x0145, "E Power Supply %d Not Present" },
+ { 0x0146, "E Temperature Sensor %d Temperature Exceeds Safe Limit" },
+ { 0x0147, "E Temperature Sensor %d Temperature Exceeds Working Limit" },
+ { 0x0148, "E Temperature Sensor %d Temperature Normal" },
+ { 0x0149, "E Temperature Sensor %d Not Present" },
+ { 0x014A, "E Enclosure Management Unit %d Access Critical" },
+ { 0x014B, "E Enclosure Management Unit %d Access OK" },
+ { 0x014C, "E Enclosure Management Unit %d Access Offline" },
+ /* Controller Events (0x0180 - 0x01FF) */
+ { 0x0181, "C Cache Write Back Error" },
+ { 0x0188, "C Battery Backup Unit Found" },
+ { 0x0189, "C Battery Backup Unit Charge Level Low" },
+ { 0x018A, "C Battery Backup Unit Charge Level OK" },
+ { 0x0193, "C Installation Aborted" },
+ { 0x0195, "C Battery Backup Unit Physically Removed" },
+ { 0x0196, "C Memory Error During Warm Boot" },
+ { 0x019E, "C Memory Soft ECC Error Corrected" },
+ { 0x019F, "C Memory Hard ECC Error Corrected" },
+ { 0x01A2, "C Battery Backup Unit Failed" },
+ { 0x01AB, "C Mirror Race Recovery Failed" },
+ { 0x01AC, "C Mirror Race on Critical Drive" },
+ /* Controller Internal Processor Events */
+ { 0x0380, "C Internal Controller Hung" },
+ { 0x0381, "C Internal Controller Firmware Breakpoint" },
+ { 0x0390, "C Internal Controller i960 Processor Specific Error" },
+ { 0x03A0, "C Internal Controller StrongARM Processor Specific Error" },
+ { 0, "" }
+};
+
+static void myrs_log_event(struct myrs_hba *cs, struct myrs_event *ev)
+{
+ unsigned char msg_buf[MYRS_LINE_BUFFER_SIZE];
+ int ev_idx = 0, ev_code;
+ unsigned char ev_type, *ev_msg;
+ struct Scsi_Host *shost = cs->host;
+ struct scsi_device *sdev;
+ struct scsi_sense_hdr sshdr = {0};
+ unsigned char sense_info[4];
+ unsigned char cmd_specific[4];
+
+ if (ev->ev_code == 0x1C) {
+ if (!scsi_normalize_sense(ev->sense_data, 40, &sshdr)) {
+ memset(&sshdr, 0x0, sizeof(sshdr));
+ memset(sense_info, 0x0, sizeof(sense_info));
+ memset(cmd_specific, 0x0, sizeof(cmd_specific));
+ } else {
+ memcpy(sense_info, &ev->sense_data[3], 4);
+ memcpy(cmd_specific, &ev->sense_data[7], 4);
+ }
+ }
+ if (sshdr.sense_key == VENDOR_SPECIFIC &&
+ (sshdr.asc == 0x80 || sshdr.asc == 0x81))
+ ev->ev_code = ((sshdr.asc - 0x80) << 8 | sshdr.ascq);
+ while (true) {
+ ev_code = myrs_ev_list[ev_idx].ev_code;
+ if (ev_code == ev->ev_code || ev_code == 0)
+ break;
+ ev_idx++;
+ }
+ ev_type = myrs_ev_list[ev_idx].ev_msg[0];
+ ev_msg = &myrs_ev_list[ev_idx].ev_msg[2];
+ if (ev_code == 0) {
+ shost_printk(KERN_WARNING, shost,
+ "Unknown Controller Event Code %04X\n",
+ ev->ev_code);
+ return;
+ }
+ switch (ev_type) {
+ case 'P':
+ sdev = scsi_device_lookup(shost, ev->channel,
+ ev->target, 0);
+ sdev_printk(KERN_INFO, sdev, "event %d: Physical Device %s\n",
+ ev->ev_seq, ev_msg);
+ if (sdev && sdev->hostdata &&
+ sdev->channel < cs->ctlr_info->physchan_present) {
+ struct myrs_pdev_info *pdev_info = sdev->hostdata;
+
+ switch (ev->ev_code) {
+ case 0x0001:
+ case 0x0007:
+ pdev_info->dev_state = MYRS_DEVICE_ONLINE;
+ break;
+ case 0x0002:
+ pdev_info->dev_state = MYRS_DEVICE_STANDBY;
+ break;
+ case 0x000C:
+ pdev_info->dev_state = MYRS_DEVICE_OFFLINE;
+ break;
+ case 0x000E:
+ pdev_info->dev_state = MYRS_DEVICE_MISSING;
+ break;
+ case 0x000F:
+ pdev_info->dev_state = MYRS_DEVICE_UNCONFIGURED;
+ break;
+ }
+ }
+ break;
+ case 'L':
+ shost_printk(KERN_INFO, shost,
+ "event %d: Logical Drive %d %s\n",
+ ev->ev_seq, ev->lun, ev_msg);
+ cs->needs_update = true;
+ break;
+ case 'M':
+ shost_printk(KERN_INFO, shost,
+ "event %d: Logical Drive %d %s\n",
+ ev->ev_seq, ev->lun, ev_msg);
+ cs->needs_update = true;
+ break;
+ case 'S':
+ if (sshdr.sense_key == NO_SENSE ||
+ (sshdr.sense_key == NOT_READY &&
+ sshdr.asc == 0x04 && (sshdr.ascq == 0x01 ||
+ sshdr.ascq == 0x02)))
+ break;
+ shost_printk(KERN_INFO, shost,
+ "event %d: Physical Device %d:%d %s\n",
+ ev->ev_seq, ev->channel, ev->target, ev_msg);
+ shost_printk(KERN_INFO, shost,
+ "Physical Device %d:%d Sense Key = %X, ASC = %02X, ASCQ = %02X\n",
+ ev->channel, ev->target,
+ sshdr.sense_key, sshdr.asc, sshdr.ascq);
+ shost_printk(KERN_INFO, shost,
+ "Physical Device %d:%d Sense Information = %02X%02X%02X%02X %02X%02X%02X%02X\n",
+ ev->channel, ev->target,
+ sense_info[0], sense_info[1],
+ sense_info[2], sense_info[3],
+ cmd_specific[0], cmd_specific[1],
+ cmd_specific[2], cmd_specific[3]);
+ break;
+ case 'E':
+ if (cs->disable_enc_msg)
+ break;
+ sprintf(msg_buf, ev_msg, ev->lun);
+ shost_printk(KERN_INFO, shost, "event %d: Enclosure %d %s\n",
+ ev->ev_seq, ev->target, msg_buf);
+ break;
+ case 'C':
+ shost_printk(KERN_INFO, shost, "event %d: Controller %s\n",
+ ev->ev_seq, ev_msg);
+ break;
+ default:
+ shost_printk(KERN_INFO, shost,
+ "event %d: Unknown Event Code %04X\n",
+ ev->ev_seq, ev->ev_code);
+ break;
+ }
+}
+
+/*
+ * SCSI sysfs interface functions
+ */
+static ssize_t raid_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ int ret;
+
+ if (!sdev->hostdata)
+ return snprintf(buf, 16, "Unknown\n");
+
+ if (sdev->channel >= cs->ctlr_info->physchan_present) {
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+ const char *name;
+
+ name = myrs_devstate_name(ldev_info->dev_state);
+ if (name)
+ ret = snprintf(buf, 64, "%s\n", name);
+ else
+ ret = snprintf(buf, 64, "Invalid (%02X)\n",
+ ldev_info->dev_state);
+ } else {
+ struct myrs_pdev_info *pdev_info;
+ const char *name;
+
+ pdev_info = sdev->hostdata;
+ name = myrs_devstate_name(pdev_info->dev_state);
+ if (name)
+ ret = snprintf(buf, 64, "%s\n", name);
+ else
+ ret = snprintf(buf, 64, "Invalid (%02X)\n",
+ pdev_info->dev_state);
+ }
+ return ret;
+}
+
+static ssize_t raid_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_cmdblk *cmd_blk;
+ union myrs_cmd_mbox *mbox;
+ enum myrs_devstate new_state;
+ unsigned short ldev_num;
+ unsigned char status;
+
+ if (!strncmp(buf, "offline", 7) ||
+ !strncmp(buf, "kill", 4))
+ new_state = MYRS_DEVICE_OFFLINE;
+ else if (!strncmp(buf, "online", 6))
+ new_state = MYRS_DEVICE_ONLINE;
+ else if (!strncmp(buf, "standby", 7))
+ new_state = MYRS_DEVICE_STANDBY;
+ else
+ return -EINVAL;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present) {
+ struct myrs_pdev_info *pdev_info = sdev->hostdata;
+ struct myrs_devmap *pdev_devmap =
+ (struct myrs_devmap *)&pdev_info->rsvd13;
+
+ if (pdev_info->dev_state == new_state) {
+ sdev_printk(KERN_INFO, sdev,
+ "Device already in %s\n",
+ myrs_devstate_name(new_state));
+ return count;
+ }
+ status = myrs_translate_pdev(cs, sdev->channel, sdev->id,
+ sdev->lun, pdev_devmap);
+ if (status != MYRS_STATUS_SUCCESS)
+ return -ENXIO;
+ ldev_num = pdev_devmap->ldev_num;
+ } else {
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+
+ if (ldev_info->dev_state == new_state) {
+ sdev_printk(KERN_INFO, sdev,
+ "Device already in %s\n",
+ myrs_devstate_name(new_state));
+ return count;
+ }
+ ldev_num = ldev_info->ldev_num;
+ }
+ mutex_lock(&cs->dcmd_mutex);
+ cmd_blk = &cs->dcmd_blk;
+ myrs_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->common.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->common.id = MYRS_DCMD_TAG;
+ mbox->common.control.dma_ctrl_to_host = true;
+ mbox->common.control.no_autosense = true;
+ mbox->set_devstate.ioctl_opcode = MYRS_IOCTL_SET_DEVICE_STATE;
+ mbox->set_devstate.state = new_state;
+ mbox->set_devstate.ldev.ldev_num = ldev_num;
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ if (status == MYRS_STATUS_SUCCESS) {
+ if (sdev->channel < cs->ctlr_info->physchan_present) {
+ struct myrs_pdev_info *pdev_info = sdev->hostdata;
+
+ pdev_info->dev_state = new_state;
+ } else {
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+
+ ldev_info->dev_state = new_state;
+ }
+ sdev_printk(KERN_INFO, sdev,
+ "Set device state to %s\n",
+ myrs_devstate_name(new_state));
+ return count;
+ }
+ sdev_printk(KERN_INFO, sdev,
+ "Failed to set device state to %s, status 0x%02x\n",
+ myrs_devstate_name(new_state), status);
+ return -EINVAL;
+}
+static DEVICE_ATTR_RW(raid_state);
+
+static ssize_t raid_level_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ const char *name = NULL;
+
+ if (!sdev->hostdata)
+ return snprintf(buf, 16, "Unknown\n");
+
+ if (sdev->channel >= cs->ctlr_info->physchan_present) {
+ struct myrs_ldev_info *ldev_info;
+
+ ldev_info = sdev->hostdata;
+ name = myrs_raid_level_name(ldev_info->raid_level);
+ if (!name)
+ return snprintf(buf, 64, "Invalid (%02X)\n",
+ ldev_info->dev_state);
+
+ } else
+ name = myrs_raid_level_name(MYRS_RAID_PHYSICAL);
+
+ return snprintf(buf, 64, "%s\n", name);
+}
+static DEVICE_ATTR_RO(raid_level);
+
+static ssize_t rebuild_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_ldev_info *ldev_info;
+ unsigned short ldev_num;
+ unsigned char status;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present)
+ return snprintf(buf, 64, "physical device - not rebuilding\n");
+
+ ldev_info = sdev->hostdata;
+ ldev_num = ldev_info->ldev_num;
+ status = myrs_get_ldev_info(cs, ldev_num, ldev_info);
+ if (status != MYRS_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev,
+ "Failed to get device information, status 0x%02x\n",
+ status);
+ return -EIO;
+ }
+ if (ldev_info->rbld_active) {
+ return snprintf(buf, 64, "rebuilding block %zu of %zu\n",
+ (size_t)ldev_info->rbld_lba,
+ (size_t)ldev_info->cfg_devsize);
+ } else
+ return snprintf(buf, 64, "not rebuilding\n");
+}
+
+static ssize_t rebuild_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_ldev_info *ldev_info;
+ struct myrs_cmdblk *cmd_blk;
+ union myrs_cmd_mbox *mbox;
+ unsigned short ldev_num;
+ unsigned char status;
+ int rebuild, ret;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present)
+ return -EINVAL;
+
+ ldev_info = sdev->hostdata;
+ if (!ldev_info)
+ return -ENXIO;
+ ldev_num = ldev_info->ldev_num;
+
+ ret = kstrtoint(buf, 0, &rebuild);
+ if (ret)
+ return ret;
+
+ status = myrs_get_ldev_info(cs, ldev_num, ldev_info);
+ if (status != MYRS_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev,
+ "Failed to get device information, status 0x%02x\n",
+ status);
+ return -EIO;
+ }
+
+ if (rebuild && ldev_info->rbld_active) {
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Not Initiated; already in progress\n");
+ return -EALREADY;
+ }
+ if (!rebuild && !ldev_info->rbld_active) {
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Not Cancelled; no rebuild in progress\n");
+ return count;
+ }
+
+ mutex_lock(&cs->dcmd_mutex);
+ cmd_blk = &cs->dcmd_blk;
+ myrs_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->common.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->common.id = MYRS_DCMD_TAG;
+ mbox->common.control.dma_ctrl_to_host = true;
+ mbox->common.control.no_autosense = true;
+ if (rebuild) {
+ mbox->ldev_info.ldev.ldev_num = ldev_num;
+ mbox->ldev_info.ioctl_opcode = MYRS_IOCTL_RBLD_DEVICE_START;
+ } else {
+ mbox->ldev_info.ldev.ldev_num = ldev_num;
+ mbox->ldev_info.ioctl_opcode = MYRS_IOCTL_RBLD_DEVICE_STOP;
+ }
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ if (status) {
+ sdev_printk(KERN_INFO, sdev,
+ "Rebuild Not %s, status 0x%02x\n",
+ rebuild ? "Initiated" : "Cancelled", status);
+ ret = -EIO;
+ } else {
+ sdev_printk(KERN_INFO, sdev, "Rebuild %s\n",
+ rebuild ? "Initiated" : "Cancelled");
+ ret = count;
+ }
+
+ return ret;
+}
+static DEVICE_ATTR_RW(rebuild);
+
+static ssize_t consistency_check_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_ldev_info *ldev_info;
+ unsigned short ldev_num;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present)
+ return snprintf(buf, 64, "physical device - not checking\n");
+
+ ldev_info = sdev->hostdata;
+ if (!ldev_info)
+ return -ENXIO;
+ ldev_num = ldev_info->ldev_num;
+ myrs_get_ldev_info(cs, ldev_num, ldev_info);
+ if (ldev_info->cc_active)
+ return snprintf(buf, 64, "checking block %zu of %zu\n",
+ (size_t)ldev_info->cc_lba,
+ (size_t)ldev_info->cfg_devsize);
+ else
+ return snprintf(buf, 64, "not checking\n");
+}
+
+static ssize_t consistency_check_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_ldev_info *ldev_info;
+ struct myrs_cmdblk *cmd_blk;
+ union myrs_cmd_mbox *mbox;
+ unsigned short ldev_num;
+ unsigned char status;
+ int check, ret;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present)
+ return -EINVAL;
+
+ ldev_info = sdev->hostdata;
+ if (!ldev_info)
+ return -ENXIO;
+ ldev_num = ldev_info->ldev_num;
+
+ ret = kstrtoint(buf, 0, &check);
+ if (ret)
+ return ret;
+
+ status = myrs_get_ldev_info(cs, ldev_num, ldev_info);
+ if (status != MYRS_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev,
+ "Failed to get device information, status 0x%02x\n",
+ status);
+ return -EIO;
+ }
+ if (check && ldev_info->cc_active) {
+ sdev_printk(KERN_INFO, sdev,
+ "Consistency Check Not Initiated; "
+ "already in progress\n");
+ return -EALREADY;
+ }
+ if (!check && !ldev_info->cc_active) {
+ sdev_printk(KERN_INFO, sdev,
+ "Consistency Check Not Cancelled; "
+ "check not in progress\n");
+ return count;
+ }
+
+ mutex_lock(&cs->dcmd_mutex);
+ cmd_blk = &cs->dcmd_blk;
+ myrs_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->common.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->common.id = MYRS_DCMD_TAG;
+ mbox->common.control.dma_ctrl_to_host = true;
+ mbox->common.control.no_autosense = true;
+ if (check) {
+ mbox->cc.ldev.ldev_num = ldev_num;
+ mbox->cc.ioctl_opcode = MYRS_IOCTL_CC_START;
+ mbox->cc.restore_consistency = true;
+ mbox->cc.initialized_area_only = false;
+ } else {
+ mbox->cc.ldev.ldev_num = ldev_num;
+ mbox->cc.ioctl_opcode = MYRS_IOCTL_CC_STOP;
+ }
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ if (status != MYRS_STATUS_SUCCESS) {
+ sdev_printk(KERN_INFO, sdev,
+ "Consistency Check Not %s, status 0x%02x\n",
+ check ? "Initiated" : "Cancelled", status);
+ ret = -EIO;
+ } else {
+ sdev_printk(KERN_INFO, sdev, "Consistency Check %s\n",
+ check ? "Initiated" : "Cancelled");
+ ret = count;
+ }
+
+ return ret;
+}
+static DEVICE_ATTR_RW(consistency_check);
+
+static struct attribute *myrs_sdev_attrs[] = {
+ &dev_attr_consistency_check.attr,
+ &dev_attr_rebuild.attr,
+ &dev_attr_raid_state.attr,
+ &dev_attr_raid_level.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(myrs_sdev);
+
+static ssize_t serial_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+ char serial[17];
+
+ memcpy(serial, cs->ctlr_info->serial_number, 16);
+ serial[16] = '\0';
+ return snprintf(buf, 16, "%s\n", serial);
+}
+static DEVICE_ATTR_RO(serial);
+
+static ssize_t ctlr_num_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+
+ return snprintf(buf, 20, "%d\n", cs->host->host_no);
+}
+static DEVICE_ATTR_RO(ctlr_num);
+
+static struct myrs_cpu_type_tbl {
+ enum myrs_cpu_type type;
+ char *name;
+} myrs_cpu_type_names[] = {
+ { MYRS_CPUTYPE_i960CA, "i960CA" },
+ { MYRS_CPUTYPE_i960RD, "i960RD" },
+ { MYRS_CPUTYPE_i960RN, "i960RN" },
+ { MYRS_CPUTYPE_i960RP, "i960RP" },
+ { MYRS_CPUTYPE_NorthBay, "NorthBay" },
+ { MYRS_CPUTYPE_StrongArm, "StrongARM" },
+ { MYRS_CPUTYPE_i960RM, "i960RM" },
+};
+
+static ssize_t processor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+ struct myrs_cpu_type_tbl *tbl;
+ const char *first_processor = NULL;
+ const char *second_processor = NULL;
+ struct myrs_ctlr_info *info = cs->ctlr_info;
+ ssize_t ret;
+ int i;
+
+ if (info->cpu[0].cpu_count) {
+ tbl = myrs_cpu_type_names;
+ for (i = 0; i < ARRAY_SIZE(myrs_cpu_type_names); i++) {
+ if (tbl[i].type == info->cpu[0].cpu_type) {
+ first_processor = tbl[i].name;
+ break;
+ }
+ }
+ }
+ if (info->cpu[1].cpu_count) {
+ tbl = myrs_cpu_type_names;
+ for (i = 0; i < ARRAY_SIZE(myrs_cpu_type_names); i++) {
+ if (tbl[i].type == info->cpu[1].cpu_type) {
+ second_processor = tbl[i].name;
+ break;
+ }
+ }
+ }
+ if (first_processor && second_processor)
+ ret = snprintf(buf, 64, "1: %s (%s, %d cpus)\n"
+ "2: %s (%s, %d cpus)\n",
+ info->cpu[0].cpu_name,
+ first_processor, info->cpu[0].cpu_count,
+ info->cpu[1].cpu_name,
+ second_processor, info->cpu[1].cpu_count);
+ else if (first_processor && !second_processor)
+ ret = snprintf(buf, 64, "1: %s (%s, %d cpus)\n2: absent\n",
+ info->cpu[0].cpu_name,
+ first_processor, info->cpu[0].cpu_count);
+ else if (!first_processor && second_processor)
+ ret = snprintf(buf, 64, "1: absent\n2: %s (%s, %d cpus)\n",
+ info->cpu[1].cpu_name,
+ second_processor, info->cpu[1].cpu_count);
+ else
+ ret = snprintf(buf, 64, "1: absent\n2: absent\n");
+
+ return ret;
+}
+static DEVICE_ATTR_RO(processor);
+
+static ssize_t model_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+
+ return snprintf(buf, 28, "%s\n", cs->model_name);
+}
+static DEVICE_ATTR_RO(model);
+
+static ssize_t ctlr_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+
+ return snprintf(buf, 4, "%d\n", cs->ctlr_info->ctlr_type);
+}
+static DEVICE_ATTR_RO(ctlr_type);
+
+static ssize_t cache_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+
+ return snprintf(buf, 8, "%d MB\n", cs->ctlr_info->cache_size_mb);
+}
+static DEVICE_ATTR_RO(cache_size);
+
+static ssize_t firmware_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+
+ return snprintf(buf, 16, "%d.%02d-%02d\n",
+ cs->ctlr_info->fw_major_version,
+ cs->ctlr_info->fw_minor_version,
+ cs->ctlr_info->fw_turn_number);
+}
+static DEVICE_ATTR_RO(firmware);
+
+static ssize_t discovery_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+ struct myrs_cmdblk *cmd_blk;
+ union myrs_cmd_mbox *mbox;
+ unsigned char status;
+
+ mutex_lock(&cs->dcmd_mutex);
+ cmd_blk = &cs->dcmd_blk;
+ myrs_reset_cmd(cmd_blk);
+ mbox = &cmd_blk->mbox;
+ mbox->common.opcode = MYRS_CMD_OP_IOCTL;
+ mbox->common.id = MYRS_DCMD_TAG;
+ mbox->common.control.dma_ctrl_to_host = true;
+ mbox->common.control.no_autosense = true;
+ mbox->common.ioctl_opcode = MYRS_IOCTL_START_DISCOVERY;
+ myrs_exec_cmd(cs, cmd_blk);
+ status = cmd_blk->status;
+ mutex_unlock(&cs->dcmd_mutex);
+ if (status != MYRS_STATUS_SUCCESS) {
+ shost_printk(KERN_INFO, shost,
+ "Discovery Not Initiated, status %02X\n",
+ status);
+ return -EINVAL;
+ }
+ shost_printk(KERN_INFO, shost, "Discovery Initiated\n");
+ cs->next_evseq = 0;
+ cs->needs_update = true;
+ queue_delayed_work(cs->work_q, &cs->monitor_work, 1);
+ flush_delayed_work(&cs->monitor_work);
+ shost_printk(KERN_INFO, shost, "Discovery Completed\n");
+
+ return count;
+}
+static DEVICE_ATTR_WO(discovery);
+
+static ssize_t flush_cache_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+ unsigned char status;
+
+ status = myrs_dev_op(cs, MYRS_IOCTL_FLUSH_DEVICE_DATA,
+ MYRS_RAID_CONTROLLER);
+ if (status == MYRS_STATUS_SUCCESS) {
+ shost_printk(KERN_INFO, shost, "Cache Flush Completed\n");
+ return count;
+ }
+ shost_printk(KERN_INFO, shost,
+ "Cache Flush failed, status 0x%02x\n", status);
+ return -EIO;
+}
+static DEVICE_ATTR_WO(flush_cache);
+
+static ssize_t disable_enclosure_messages_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct myrs_hba *cs = shost_priv(shost);
+
+ return snprintf(buf, 3, "%d\n", cs->disable_enc_msg);
+}
+
+static ssize_t disable_enclosure_messages_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ int value, ret;
+
+ ret = kstrtoint(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value > 2)
+ return -EINVAL;
+
+ cs->disable_enc_msg = value;
+ return count;
+}
+static DEVICE_ATTR_RW(disable_enclosure_messages);
+
+static struct attribute *myrs_shost_attrs[] = {
+ &dev_attr_serial.attr,
+ &dev_attr_ctlr_num.attr,
+ &dev_attr_processor.attr,
+ &dev_attr_model.attr,
+ &dev_attr_ctlr_type.attr,
+ &dev_attr_cache_size.attr,
+ &dev_attr_firmware.attr,
+ &dev_attr_discovery.attr,
+ &dev_attr_flush_cache.attr,
+ &dev_attr_disable_enclosure_messages.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(myrs_shost);
+
+/*
+ * SCSI midlayer interface
+ */
+static int myrs_host_reset(struct scsi_cmnd *scmd)
+{
+ struct Scsi_Host *shost = scmd->device->host;
+ struct myrs_hba *cs = shost_priv(shost);
+
+ cs->reset(cs->io_base);
+ return SUCCESS;
+}
+
+static void myrs_mode_sense(struct myrs_hba *cs, struct scsi_cmnd *scmd,
+ struct myrs_ldev_info *ldev_info)
+{
+ unsigned char modes[32], *mode_pg;
+ bool dbd;
+ size_t mode_len;
+
+ dbd = (scmd->cmnd[1] & 0x08) == 0x08;
+ if (dbd) {
+ mode_len = 24;
+ mode_pg = &modes[4];
+ } else {
+ mode_len = 32;
+ mode_pg = &modes[12];
+ }
+ memset(modes, 0, sizeof(modes));
+ modes[0] = mode_len - 1;
+ modes[2] = 0x10; /* Enable FUA */
+ if (ldev_info->ldev_control.wce == MYRS_LOGICALDEVICE_RO)
+ modes[2] |= 0x80;
+ if (!dbd) {
+ unsigned char *block_desc = &modes[4];
+
+ modes[3] = 8;
+ put_unaligned_be32(ldev_info->cfg_devsize, &block_desc[0]);
+ put_unaligned_be32(ldev_info->devsize_bytes, &block_desc[5]);
+ }
+ mode_pg[0] = 0x08;
+ mode_pg[1] = 0x12;
+ if (ldev_info->ldev_control.rce == MYRS_READCACHE_DISABLED)
+ mode_pg[2] |= 0x01;
+ if (ldev_info->ldev_control.wce == MYRS_WRITECACHE_ENABLED ||
+ ldev_info->ldev_control.wce == MYRS_INTELLIGENT_WRITECACHE_ENABLED)
+ mode_pg[2] |= 0x04;
+ if (ldev_info->cacheline_size) {
+ mode_pg[2] |= 0x08;
+ put_unaligned_be16(1 << ldev_info->cacheline_size,
+ &mode_pg[14]);
+ }
+
+ scsi_sg_copy_from_buffer(scmd, modes, mode_len);
+}
+
+static int myrs_queuecommand(struct Scsi_Host *shost,
+ struct scsi_cmnd *scmd)
+{
+ struct request *rq = scsi_cmd_to_rq(scmd);
+ struct myrs_hba *cs = shost_priv(shost);
+ struct myrs_cmdblk *cmd_blk = scsi_cmd_priv(scmd);
+ union myrs_cmd_mbox *mbox = &cmd_blk->mbox;
+ struct scsi_device *sdev = scmd->device;
+ union myrs_sgl *hw_sge;
+ dma_addr_t sense_addr;
+ struct scatterlist *sgl;
+ unsigned long flags, timeout;
+ int nsge;
+
+ if (!scmd->device->hostdata) {
+ scmd->result = (DID_NO_CONNECT << 16);
+ scsi_done(scmd);
+ return 0;
+ }
+
+ switch (scmd->cmnd[0]) {
+ case REPORT_LUNS:
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x20, 0x0);
+ scsi_done(scmd);
+ return 0;
+ case MODE_SENSE:
+ if (scmd->device->channel >= cs->ctlr_info->physchan_present) {
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+
+ if ((scmd->cmnd[2] & 0x3F) != 0x3F &&
+ (scmd->cmnd[2] & 0x3F) != 0x08) {
+ /* Illegal request, invalid field in CDB */
+ scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
+ } else {
+ myrs_mode_sense(cs, scmd, ldev_info);
+ scmd->result = (DID_OK << 16);
+ }
+ scsi_done(scmd);
+ return 0;
+ }
+ break;
+ }
+
+ myrs_reset_cmd(cmd_blk);
+ cmd_blk->sense = dma_pool_alloc(cs->sense_pool, GFP_ATOMIC,
+ &sense_addr);
+ if (!cmd_blk->sense)
+ return SCSI_MLQUEUE_HOST_BUSY;
+ cmd_blk->sense_addr = sense_addr;
+
+ timeout = rq->timeout;
+ if (scmd->cmd_len <= 10) {
+ if (scmd->device->channel >= cs->ctlr_info->physchan_present) {
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+
+ mbox->SCSI_10.opcode = MYRS_CMD_OP_SCSI_10;
+ mbox->SCSI_10.pdev.lun = ldev_info->lun;
+ mbox->SCSI_10.pdev.target = ldev_info->target;
+ mbox->SCSI_10.pdev.channel = ldev_info->channel;
+ mbox->SCSI_10.pdev.ctlr = 0;
+ } else {
+ mbox->SCSI_10.opcode = MYRS_CMD_OP_SCSI_10_PASSTHRU;
+ mbox->SCSI_10.pdev.lun = sdev->lun;
+ mbox->SCSI_10.pdev.target = sdev->id;
+ mbox->SCSI_10.pdev.channel = sdev->channel;
+ }
+ mbox->SCSI_10.id = rq->tag + 3;
+ mbox->SCSI_10.control.dma_ctrl_to_host =
+ (scmd->sc_data_direction == DMA_FROM_DEVICE);
+ if (rq->cmd_flags & REQ_FUA)
+ mbox->SCSI_10.control.fua = true;
+ mbox->SCSI_10.dma_size = scsi_bufflen(scmd);
+ mbox->SCSI_10.sense_addr = cmd_blk->sense_addr;
+ mbox->SCSI_10.sense_len = MYRS_SENSE_SIZE;
+ mbox->SCSI_10.cdb_len = scmd->cmd_len;
+ if (timeout > 60) {
+ mbox->SCSI_10.tmo.tmo_scale = MYRS_TMO_SCALE_MINUTES;
+ mbox->SCSI_10.tmo.tmo_val = timeout / 60;
+ } else {
+ mbox->SCSI_10.tmo.tmo_scale = MYRS_TMO_SCALE_SECONDS;
+ mbox->SCSI_10.tmo.tmo_val = timeout;
+ }
+ memcpy(&mbox->SCSI_10.cdb, scmd->cmnd, scmd->cmd_len);
+ hw_sge = &mbox->SCSI_10.dma_addr;
+ cmd_blk->dcdb = NULL;
+ } else {
+ dma_addr_t dcdb_dma;
+
+ cmd_blk->dcdb = dma_pool_alloc(cs->dcdb_pool, GFP_ATOMIC,
+ &dcdb_dma);
+ if (!cmd_blk->dcdb) {
+ dma_pool_free(cs->sense_pool, cmd_blk->sense,
+ cmd_blk->sense_addr);
+ cmd_blk->sense = NULL;
+ cmd_blk->sense_addr = 0;
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ cmd_blk->dcdb_dma = dcdb_dma;
+ if (scmd->device->channel >= cs->ctlr_info->physchan_present) {
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+
+ mbox->SCSI_255.opcode = MYRS_CMD_OP_SCSI_256;
+ mbox->SCSI_255.pdev.lun = ldev_info->lun;
+ mbox->SCSI_255.pdev.target = ldev_info->target;
+ mbox->SCSI_255.pdev.channel = ldev_info->channel;
+ mbox->SCSI_255.pdev.ctlr = 0;
+ } else {
+ mbox->SCSI_255.opcode = MYRS_CMD_OP_SCSI_255_PASSTHRU;
+ mbox->SCSI_255.pdev.lun = sdev->lun;
+ mbox->SCSI_255.pdev.target = sdev->id;
+ mbox->SCSI_255.pdev.channel = sdev->channel;
+ }
+ mbox->SCSI_255.id = rq->tag + 3;
+ mbox->SCSI_255.control.dma_ctrl_to_host =
+ (scmd->sc_data_direction == DMA_FROM_DEVICE);
+ if (rq->cmd_flags & REQ_FUA)
+ mbox->SCSI_255.control.fua = true;
+ mbox->SCSI_255.dma_size = scsi_bufflen(scmd);
+ mbox->SCSI_255.sense_addr = cmd_blk->sense_addr;
+ mbox->SCSI_255.sense_len = MYRS_SENSE_SIZE;
+ mbox->SCSI_255.cdb_len = scmd->cmd_len;
+ mbox->SCSI_255.cdb_addr = cmd_blk->dcdb_dma;
+ if (timeout > 60) {
+ mbox->SCSI_255.tmo.tmo_scale = MYRS_TMO_SCALE_MINUTES;
+ mbox->SCSI_255.tmo.tmo_val = timeout / 60;
+ } else {
+ mbox->SCSI_255.tmo.tmo_scale = MYRS_TMO_SCALE_SECONDS;
+ mbox->SCSI_255.tmo.tmo_val = timeout;
+ }
+ memcpy(cmd_blk->dcdb, scmd->cmnd, scmd->cmd_len);
+ hw_sge = &mbox->SCSI_255.dma_addr;
+ }
+ if (scmd->sc_data_direction == DMA_NONE)
+ goto submit;
+ nsge = scsi_dma_map(scmd);
+ if (nsge == 1) {
+ sgl = scsi_sglist(scmd);
+ hw_sge->sge[0].sge_addr = (u64)sg_dma_address(sgl);
+ hw_sge->sge[0].sge_count = (u64)sg_dma_len(sgl);
+ } else {
+ struct myrs_sge *hw_sgl;
+ dma_addr_t hw_sgl_addr;
+ int i;
+
+ if (nsge > 2) {
+ hw_sgl = dma_pool_alloc(cs->sg_pool, GFP_ATOMIC,
+ &hw_sgl_addr);
+ if (WARN_ON(!hw_sgl)) {
+ if (cmd_blk->dcdb) {
+ dma_pool_free(cs->dcdb_pool,
+ cmd_blk->dcdb,
+ cmd_blk->dcdb_dma);
+ cmd_blk->dcdb = NULL;
+ cmd_blk->dcdb_dma = 0;
+ }
+ dma_pool_free(cs->sense_pool,
+ cmd_blk->sense,
+ cmd_blk->sense_addr);
+ cmd_blk->sense = NULL;
+ cmd_blk->sense_addr = 0;
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ cmd_blk->sgl = hw_sgl;
+ cmd_blk->sgl_addr = hw_sgl_addr;
+ if (scmd->cmd_len <= 10)
+ mbox->SCSI_10.control.add_sge_mem = true;
+ else
+ mbox->SCSI_255.control.add_sge_mem = true;
+ hw_sge->ext.sge0_len = nsge;
+ hw_sge->ext.sge0_addr = cmd_blk->sgl_addr;
+ } else
+ hw_sgl = hw_sge->sge;
+
+ scsi_for_each_sg(scmd, sgl, nsge, i) {
+ if (WARN_ON(!hw_sgl)) {
+ scsi_dma_unmap(scmd);
+ scmd->result = (DID_ERROR << 16);
+ scsi_done(scmd);
+ return 0;
+ }
+ hw_sgl->sge_addr = (u64)sg_dma_address(sgl);
+ hw_sgl->sge_count = (u64)sg_dma_len(sgl);
+ hw_sgl++;
+ }
+ }
+submit:
+ spin_lock_irqsave(&cs->queue_lock, flags);
+ myrs_qcmd(cs, cmd_blk);
+ spin_unlock_irqrestore(&cs->queue_lock, flags);
+
+ return 0;
+}
+
+static unsigned short myrs_translate_ldev(struct myrs_hba *cs,
+ struct scsi_device *sdev)
+{
+ unsigned short ldev_num;
+ unsigned int chan_offset =
+ sdev->channel - cs->ctlr_info->physchan_present;
+
+ ldev_num = sdev->id + chan_offset * sdev->host->max_id;
+
+ return ldev_num;
+}
+
+static int myrs_sdev_init(struct scsi_device *sdev)
+{
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ unsigned char status;
+
+ if (sdev->channel > sdev->host->max_channel)
+ return 0;
+
+ if (sdev->channel >= cs->ctlr_info->physchan_present) {
+ struct myrs_ldev_info *ldev_info;
+ unsigned short ldev_num;
+
+ if (sdev->lun > 0)
+ return -ENXIO;
+
+ ldev_num = myrs_translate_ldev(cs, sdev);
+
+ ldev_info = kzalloc(sizeof(*ldev_info), GFP_KERNEL);
+ if (!ldev_info)
+ return -ENOMEM;
+
+ status = myrs_get_ldev_info(cs, ldev_num, ldev_info);
+ if (status != MYRS_STATUS_SUCCESS) {
+ sdev->hostdata = NULL;
+ kfree(ldev_info);
+ } else {
+ enum raid_level level;
+
+ dev_dbg(&sdev->sdev_gendev,
+ "Logical device mapping %d:%d:%d -> %d\n",
+ ldev_info->channel, ldev_info->target,
+ ldev_info->lun, ldev_info->ldev_num);
+
+ sdev->hostdata = ldev_info;
+ switch (ldev_info->raid_level) {
+ case MYRS_RAID_LEVEL0:
+ level = RAID_LEVEL_LINEAR;
+ break;
+ case MYRS_RAID_LEVEL1:
+ level = RAID_LEVEL_1;
+ break;
+ case MYRS_RAID_LEVEL3:
+ case MYRS_RAID_LEVEL3F:
+ case MYRS_RAID_LEVEL3L:
+ level = RAID_LEVEL_3;
+ break;
+ case MYRS_RAID_LEVEL5:
+ case MYRS_RAID_LEVEL5L:
+ level = RAID_LEVEL_5;
+ break;
+ case MYRS_RAID_LEVEL6:
+ level = RAID_LEVEL_6;
+ break;
+ case MYRS_RAID_LEVELE:
+ case MYRS_RAID_NEWSPAN:
+ case MYRS_RAID_SPAN:
+ level = RAID_LEVEL_LINEAR;
+ break;
+ case MYRS_RAID_JBOD:
+ level = RAID_LEVEL_JBOD;
+ break;
+ default:
+ level = RAID_LEVEL_UNKNOWN;
+ break;
+ }
+ raid_set_level(myrs_raid_template,
+ &sdev->sdev_gendev, level);
+ if (ldev_info->dev_state != MYRS_DEVICE_ONLINE) {
+ const char *name;
+
+ name = myrs_devstate_name(ldev_info->dev_state);
+ sdev_printk(KERN_DEBUG, sdev,
+ "logical device in state %s\n",
+ name ? name : "Invalid");
+ }
+ }
+ } else {
+ struct myrs_pdev_info *pdev_info;
+
+ pdev_info = kzalloc(sizeof(*pdev_info), GFP_KERNEL);
+ if (!pdev_info)
+ return -ENOMEM;
+
+ status = myrs_get_pdev_info(cs, sdev->channel,
+ sdev->id, sdev->lun,
+ pdev_info);
+ if (status != MYRS_STATUS_SUCCESS) {
+ sdev->hostdata = NULL;
+ kfree(pdev_info);
+ return -ENXIO;
+ }
+ sdev->hostdata = pdev_info;
+ }
+ return 0;
+}
+
+static int myrs_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_ldev_info *ldev_info;
+
+ if (sdev->channel > sdev->host->max_channel)
+ return -ENXIO;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present) {
+ /* Skip HBA device */
+ if (sdev->type == TYPE_RAID)
+ return -ENXIO;
+ sdev->no_uld_attach = 1;
+ return 0;
+ }
+ if (sdev->lun != 0)
+ return -ENXIO;
+
+ ldev_info = sdev->hostdata;
+ if (!ldev_info)
+ return -ENXIO;
+ if (ldev_info->ldev_control.wce == MYRS_WRITECACHE_ENABLED ||
+ ldev_info->ldev_control.wce == MYRS_INTELLIGENT_WRITECACHE_ENABLED)
+ sdev->wce_default_on = 1;
+ sdev->tagged_supported = 1;
+ return 0;
+}
+
+static void myrs_sdev_destroy(struct scsi_device *sdev)
+{
+ kfree(sdev->hostdata);
+}
+
+static const struct scsi_host_template myrs_template = {
+ .module = THIS_MODULE,
+ .name = "DAC960",
+ .proc_name = "myrs",
+ .queuecommand = myrs_queuecommand,
+ .eh_host_reset_handler = myrs_host_reset,
+ .sdev_init = myrs_sdev_init,
+ .sdev_configure = myrs_sdev_configure,
+ .sdev_destroy = myrs_sdev_destroy,
+ .cmd_size = sizeof(struct myrs_cmdblk),
+ .shost_groups = myrs_shost_groups,
+ .sdev_groups = myrs_sdev_groups,
+ .this_id = -1,
+};
+
+static struct myrs_hba *myrs_alloc_host(struct pci_dev *pdev,
+ const struct pci_device_id *entry)
+{
+ struct Scsi_Host *shost;
+ struct myrs_hba *cs;
+
+ shost = scsi_host_alloc(&myrs_template, sizeof(struct myrs_hba));
+ if (!shost)
+ return NULL;
+
+ shost->max_cmd_len = 16;
+ shost->max_lun = 256;
+ cs = shost_priv(shost);
+ mutex_init(&cs->dcmd_mutex);
+ mutex_init(&cs->cinfo_mutex);
+ cs->host = shost;
+
+ return cs;
+}
+
+/*
+ * RAID template functions
+ */
+
+/**
+ * myrs_is_raid - return boolean indicating device is raid volume
+ * @dev: the device struct object
+ */
+static int
+myrs_is_raid(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+
+ return (sdev->channel >= cs->ctlr_info->physchan_present) ? 1 : 0;
+}
+
+/**
+ * myrs_get_resync - get raid volume resync percent complete
+ * @dev: the device struct object
+ */
+static void
+myrs_get_resync(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+ u64 percent_complete = 0;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present || !ldev_info)
+ return;
+ if (ldev_info->rbld_active) {
+ unsigned short ldev_num = ldev_info->ldev_num;
+
+ myrs_get_ldev_info(cs, ldev_num, ldev_info);
+ percent_complete = ldev_info->rbld_lba * 100;
+ do_div(percent_complete, ldev_info->cfg_devsize);
+ }
+ raid_set_resync(myrs_raid_template, dev, percent_complete);
+}
+
+/**
+ * myrs_get_state - get raid volume status
+ * @dev: the device struct object
+ */
+static void
+myrs_get_state(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct myrs_hba *cs = shost_priv(sdev->host);
+ struct myrs_ldev_info *ldev_info = sdev->hostdata;
+ enum raid_state state = RAID_STATE_UNKNOWN;
+
+ if (sdev->channel < cs->ctlr_info->physchan_present || !ldev_info)
+ state = RAID_STATE_UNKNOWN;
+ else {
+ switch (ldev_info->dev_state) {
+ case MYRS_DEVICE_ONLINE:
+ state = RAID_STATE_ACTIVE;
+ break;
+ case MYRS_DEVICE_SUSPECTED_CRITICAL:
+ case MYRS_DEVICE_CRITICAL:
+ state = RAID_STATE_DEGRADED;
+ break;
+ case MYRS_DEVICE_REBUILD:
+ state = RAID_STATE_RESYNCING;
+ break;
+ case MYRS_DEVICE_UNCONFIGURED:
+ case MYRS_DEVICE_INVALID_STATE:
+ state = RAID_STATE_UNKNOWN;
+ break;
+ default:
+ state = RAID_STATE_OFFLINE;
+ }
+ }
+ raid_set_state(myrs_raid_template, dev, state);
+}
+
+static struct raid_function_template myrs_raid_functions = {
+ .cookie = &myrs_template,
+ .is_raid = myrs_is_raid,
+ .get_resync = myrs_get_resync,
+ .get_state = myrs_get_state,
+};
+
+/*
+ * PCI interface functions
+ */
+static void myrs_flush_cache(struct myrs_hba *cs)
+{
+ myrs_dev_op(cs, MYRS_IOCTL_FLUSH_DEVICE_DATA, MYRS_RAID_CONTROLLER);
+}
+
+static void myrs_handle_scsi(struct myrs_hba *cs, struct myrs_cmdblk *cmd_blk,
+ struct scsi_cmnd *scmd)
+{
+ unsigned char status;
+
+ if (!cmd_blk)
+ return;
+
+ scsi_dma_unmap(scmd);
+ status = cmd_blk->status;
+ if (cmd_blk->sense) {
+ if (status == MYRS_STATUS_FAILED && cmd_blk->sense_len) {
+ unsigned int sense_len = SCSI_SENSE_BUFFERSIZE;
+
+ if (sense_len > cmd_blk->sense_len)
+ sense_len = cmd_blk->sense_len;
+ memcpy(scmd->sense_buffer, cmd_blk->sense, sense_len);
+ }
+ dma_pool_free(cs->sense_pool, cmd_blk->sense,
+ cmd_blk->sense_addr);
+ cmd_blk->sense = NULL;
+ cmd_blk->sense_addr = 0;
+ }
+ if (cmd_blk->dcdb) {
+ dma_pool_free(cs->dcdb_pool, cmd_blk->dcdb,
+ cmd_blk->dcdb_dma);
+ cmd_blk->dcdb = NULL;
+ cmd_blk->dcdb_dma = 0;
+ }
+ if (cmd_blk->sgl) {
+ dma_pool_free(cs->sg_pool, cmd_blk->sgl,
+ cmd_blk->sgl_addr);
+ cmd_blk->sgl = NULL;
+ cmd_blk->sgl_addr = 0;
+ }
+ if (cmd_blk->residual)
+ scsi_set_resid(scmd, cmd_blk->residual);
+ if (status == MYRS_STATUS_DEVICE_NON_RESPONSIVE ||
+ status == MYRS_STATUS_DEVICE_NON_RESPONSIVE2)
+ scmd->result = (DID_BAD_TARGET << 16);
+ else
+ scmd->result = (DID_OK << 16) | status;
+ scsi_done(scmd);
+}
+
+static void myrs_handle_cmdblk(struct myrs_hba *cs, struct myrs_cmdblk *cmd_blk)
+{
+ if (!cmd_blk)
+ return;
+
+ if (cmd_blk->complete) {
+ complete(cmd_blk->complete);
+ cmd_blk->complete = NULL;
+ }
+}
+
+static void myrs_monitor(struct work_struct *work)
+{
+ struct myrs_hba *cs = container_of(work, struct myrs_hba,
+ monitor_work.work);
+ struct Scsi_Host *shost = cs->host;
+ struct myrs_ctlr_info *info = cs->ctlr_info;
+ unsigned int epoch = cs->fwstat_buf->epoch;
+ unsigned long interval = MYRS_PRIMARY_MONITOR_INTERVAL;
+ unsigned char status;
+
+ dev_dbg(&shost->shost_gendev, "monitor tick\n");
+
+ status = myrs_get_fwstatus(cs);
+
+ if (cs->needs_update) {
+ cs->needs_update = false;
+ mutex_lock(&cs->cinfo_mutex);
+ status = myrs_get_ctlr_info(cs);
+ mutex_unlock(&cs->cinfo_mutex);
+ }
+ if (cs->fwstat_buf->next_evseq - cs->next_evseq > 0) {
+ status = myrs_get_event(cs, cs->next_evseq,
+ cs->event_buf);
+ if (status == MYRS_STATUS_SUCCESS) {
+ myrs_log_event(cs, cs->event_buf);
+ cs->next_evseq++;
+ interval = 1;
+ }
+ }
+
+ if (time_after(jiffies, cs->secondary_monitor_time
+ + MYRS_SECONDARY_MONITOR_INTERVAL))
+ cs->secondary_monitor_time = jiffies;
+
+ if (info->bg_init_active +
+ info->ldev_init_active +
+ info->pdev_init_active +
+ info->cc_active +
+ info->rbld_active +
+ info->exp_active != 0) {
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, shost) {
+ struct myrs_ldev_info *ldev_info;
+ int ldev_num;
+
+ if (sdev->channel < info->physchan_present)
+ continue;
+ ldev_info = sdev->hostdata;
+ if (!ldev_info)
+ continue;
+ ldev_num = ldev_info->ldev_num;
+ myrs_get_ldev_info(cs, ldev_num, ldev_info);
+ }
+ cs->needs_update = true;
+ }
+ if (epoch == cs->epoch &&
+ cs->fwstat_buf->next_evseq == cs->next_evseq &&
+ (cs->needs_update == false ||
+ time_before(jiffies, cs->primary_monitor_time
+ + MYRS_PRIMARY_MONITOR_INTERVAL))) {
+ interval = MYRS_SECONDARY_MONITOR_INTERVAL;
+ }
+
+ if (interval > 1)
+ cs->primary_monitor_time = jiffies;
+ queue_delayed_work(cs->work_q, &cs->monitor_work, interval);
+}
+
+static bool myrs_create_mempools(struct pci_dev *pdev, struct myrs_hba *cs)
+{
+ struct Scsi_Host *shost = cs->host;
+ size_t elem_size, elem_align;
+
+ elem_align = sizeof(struct myrs_sge);
+ elem_size = shost->sg_tablesize * elem_align;
+ cs->sg_pool = dma_pool_create("myrs_sg", &pdev->dev,
+ elem_size, elem_align, 0);
+ if (cs->sg_pool == NULL) {
+ shost_printk(KERN_ERR, shost,
+ "Failed to allocate SG pool\n");
+ return false;
+ }
+
+ cs->sense_pool = dma_pool_create("myrs_sense", &pdev->dev,
+ MYRS_SENSE_SIZE, sizeof(int), 0);
+ if (cs->sense_pool == NULL) {
+ dma_pool_destroy(cs->sg_pool);
+ cs->sg_pool = NULL;
+ shost_printk(KERN_ERR, shost,
+ "Failed to allocate sense data pool\n");
+ return false;
+ }
+
+ cs->dcdb_pool = dma_pool_create("myrs_dcdb", &pdev->dev,
+ MYRS_DCDB_SIZE,
+ sizeof(unsigned char), 0);
+ if (!cs->dcdb_pool) {
+ dma_pool_destroy(cs->sg_pool);
+ cs->sg_pool = NULL;
+ dma_pool_destroy(cs->sense_pool);
+ cs->sense_pool = NULL;
+ shost_printk(KERN_ERR, shost,
+ "Failed to allocate DCDB pool\n");
+ return false;
+ }
+
+ cs->work_q = alloc_ordered_workqueue("myrs_wq_%d", WQ_MEM_RECLAIM,
+ shost->host_no);
+ if (!cs->work_q) {
+ dma_pool_destroy(cs->dcdb_pool);
+ cs->dcdb_pool = NULL;
+ dma_pool_destroy(cs->sg_pool);
+ cs->sg_pool = NULL;
+ dma_pool_destroy(cs->sense_pool);
+ cs->sense_pool = NULL;
+ shost_printk(KERN_ERR, shost,
+ "Failed to create workqueue\n");
+ return false;
+ }
+
+ /* Initialize the Monitoring Timer. */
+ INIT_DELAYED_WORK(&cs->monitor_work, myrs_monitor);
+ queue_delayed_work(cs->work_q, &cs->monitor_work, 1);
+
+ return true;
+}
+
+static void myrs_destroy_mempools(struct myrs_hba *cs)
+{
+ cancel_delayed_work_sync(&cs->monitor_work);
+ destroy_workqueue(cs->work_q);
+
+ dma_pool_destroy(cs->sg_pool);
+ dma_pool_destroy(cs->dcdb_pool);
+ dma_pool_destroy(cs->sense_pool);
+}
+
+static void myrs_unmap(struct myrs_hba *cs)
+{
+ kfree(cs->event_buf);
+ kfree(cs->ctlr_info);
+ if (cs->fwstat_buf) {
+ dma_free_coherent(&cs->pdev->dev, sizeof(struct myrs_fwstat),
+ cs->fwstat_buf, cs->fwstat_addr);
+ cs->fwstat_buf = NULL;
+ }
+ if (cs->first_stat_mbox) {
+ dma_free_coherent(&cs->pdev->dev, cs->stat_mbox_size,
+ cs->first_stat_mbox, cs->stat_mbox_addr);
+ cs->first_stat_mbox = NULL;
+ }
+ if (cs->first_cmd_mbox) {
+ dma_free_coherent(&cs->pdev->dev, cs->cmd_mbox_size,
+ cs->first_cmd_mbox, cs->cmd_mbox_addr);
+ cs->first_cmd_mbox = NULL;
+ }
+}
+
+static void myrs_cleanup(struct myrs_hba *cs)
+{
+ struct pci_dev *pdev = cs->pdev;
+
+ /* Free the memory mailbox, status, and related structures */
+ myrs_unmap(cs);
+
+ if (cs->mmio_base) {
+ if (cs->disable_intr)
+ cs->disable_intr(cs);
+ iounmap(cs->mmio_base);
+ cs->mmio_base = NULL;
+ }
+ if (cs->irq)
+ free_irq(cs->irq, cs);
+ if (cs->io_addr)
+ release_region(cs->io_addr, 0x80);
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+ scsi_host_put(cs->host);
+}
+
+static struct myrs_hba *myrs_detect(struct pci_dev *pdev,
+ const struct pci_device_id *entry)
+{
+ struct myrs_privdata *privdata =
+ (struct myrs_privdata *)entry->driver_data;
+ irq_handler_t irq_handler = privdata->irq_handler;
+ unsigned int mmio_size = privdata->mmio_size;
+ struct myrs_hba *cs = NULL;
+
+ cs = myrs_alloc_host(pdev, entry);
+ if (!cs) {
+ dev_err(&pdev->dev, "Unable to allocate Controller\n");
+ return NULL;
+ }
+ cs->pdev = pdev;
+
+ if (pci_enable_device(pdev))
+ goto Failure;
+
+ cs->pci_addr = pci_resource_start(pdev, 0);
+
+ pci_set_drvdata(pdev, cs);
+ spin_lock_init(&cs->queue_lock);
+ /* Map the Controller Register Window. */
+ if (mmio_size < PAGE_SIZE)
+ mmio_size = PAGE_SIZE;
+ cs->mmio_base = ioremap(cs->pci_addr & PAGE_MASK, mmio_size);
+ if (cs->mmio_base == NULL) {
+ dev_err(&pdev->dev,
+ "Unable to map Controller Register Window\n");
+ goto Failure;
+ }
+
+ cs->io_base = cs->mmio_base + (cs->pci_addr & ~PAGE_MASK);
+ if (privdata->hw_init(pdev, cs, cs->io_base))
+ goto Failure;
+
+ /* Acquire shared access to the IRQ Channel. */
+ if (request_irq(pdev->irq, irq_handler, IRQF_SHARED, "myrs", cs) < 0) {
+ dev_err(&pdev->dev,
+ "Unable to acquire IRQ Channel %d\n", pdev->irq);
+ goto Failure;
+ }
+ cs->irq = pdev->irq;
+ return cs;
+
+Failure:
+ dev_err(&pdev->dev,
+ "Failed to initialize Controller\n");
+ myrs_cleanup(cs);
+ return NULL;
+}
+
+/*
+ * myrs_err_status reports Controller BIOS Messages passed through
+ * the Error Status Register when the driver performs the BIOS handshaking.
+ * It returns true for fatal errors and false otherwise.
+ */
+
+static bool myrs_err_status(struct myrs_hba *cs, unsigned char status,
+ unsigned char parm0, unsigned char parm1)
+{
+ struct pci_dev *pdev = cs->pdev;
+
+ switch (status) {
+ case 0x00:
+ dev_info(&pdev->dev,
+ "Physical Device %d:%d Not Responding\n",
+ parm1, parm0);
+ break;
+ case 0x08:
+ dev_notice(&pdev->dev, "Spinning Up Drives\n");
+ break;
+ case 0x30:
+ dev_notice(&pdev->dev, "Configuration Checksum Error\n");
+ break;
+ case 0x60:
+ dev_notice(&pdev->dev, "Mirror Race Recovery Failed\n");
+ break;
+ case 0x70:
+ dev_notice(&pdev->dev, "Mirror Race Recovery In Progress\n");
+ break;
+ case 0x90:
+ dev_notice(&pdev->dev, "Physical Device %d:%d COD Mismatch\n",
+ parm1, parm0);
+ break;
+ case 0xA0:
+ dev_notice(&pdev->dev, "Logical Drive Installation Aborted\n");
+ break;
+ case 0xB0:
+ dev_notice(&pdev->dev, "Mirror Race On A Critical Logical Drive\n");
+ break;
+ case 0xD0:
+ dev_notice(&pdev->dev, "New Controller Configuration Found\n");
+ break;
+ case 0xF0:
+ dev_err(&pdev->dev, "Fatal Memory Parity Error\n");
+ return true;
+ default:
+ dev_err(&pdev->dev, "Unknown Initialization Error %02X\n",
+ status);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Hardware-specific functions
+ */
+
+/*
+ * DAC960 GEM Series Controllers.
+ */
+
+static inline void DAC960_GEM_hw_mbox_new_cmd(void __iomem *base)
+{
+ __le32 val = cpu_to_le32(DAC960_GEM_IDB_HWMBOX_NEW_CMD << 24);
+
+ writel(val, base + DAC960_GEM_IDB_READ_OFFSET);
+}
+
+static inline void DAC960_GEM_ack_hw_mbox_status(void __iomem *base)
+{
+ __le32 val = cpu_to_le32(DAC960_GEM_IDB_HWMBOX_ACK_STS << 24);
+
+ writel(val, base + DAC960_GEM_IDB_CLEAR_OFFSET);
+}
+
+static inline void DAC960_GEM_reset_ctrl(void __iomem *base)
+{
+ __le32 val = cpu_to_le32(DAC960_GEM_IDB_CTRL_RESET << 24);
+
+ writel(val, base + DAC960_GEM_IDB_READ_OFFSET);
+}
+
+static inline void DAC960_GEM_mem_mbox_new_cmd(void __iomem *base)
+{
+ __le32 val = cpu_to_le32(DAC960_GEM_IDB_HWMBOX_NEW_CMD << 24);
+
+ writel(val, base + DAC960_GEM_IDB_READ_OFFSET);
+}
+
+static inline bool DAC960_GEM_hw_mbox_is_full(void __iomem *base)
+{
+ __le32 val;
+
+ val = readl(base + DAC960_GEM_IDB_READ_OFFSET);
+ return (le32_to_cpu(val) >> 24) & DAC960_GEM_IDB_HWMBOX_FULL;
+}
+
+static inline bool DAC960_GEM_init_in_progress(void __iomem *base)
+{
+ __le32 val;
+
+ val = readl(base + DAC960_GEM_IDB_READ_OFFSET);
+ return (le32_to_cpu(val) >> 24) & DAC960_GEM_IDB_INIT_IN_PROGRESS;
+}
+
+static inline void DAC960_GEM_ack_hw_mbox_intr(void __iomem *base)
+{
+ __le32 val = cpu_to_le32(DAC960_GEM_ODB_HWMBOX_ACK_IRQ << 24);
+
+ writel(val, base + DAC960_GEM_ODB_CLEAR_OFFSET);
+}
+
+static inline void DAC960_GEM_ack_intr(void __iomem *base)
+{
+ __le32 val = cpu_to_le32((DAC960_GEM_ODB_HWMBOX_ACK_IRQ |
+ DAC960_GEM_ODB_MMBOX_ACK_IRQ) << 24);
+
+ writel(val, base + DAC960_GEM_ODB_CLEAR_OFFSET);
+}
+
+static inline bool DAC960_GEM_hw_mbox_status_available(void __iomem *base)
+{
+ __le32 val;
+
+ val = readl(base + DAC960_GEM_ODB_READ_OFFSET);
+ return (le32_to_cpu(val) >> 24) & DAC960_GEM_ODB_HWMBOX_STS_AVAIL;
+}
+
+static inline void DAC960_GEM_enable_intr(void __iomem *base)
+{
+ __le32 val = cpu_to_le32((DAC960_GEM_IRQMASK_HWMBOX_IRQ |
+ DAC960_GEM_IRQMASK_MMBOX_IRQ) << 24);
+ writel(val, base + DAC960_GEM_IRQMASK_CLEAR_OFFSET);
+}
+
+static inline void DAC960_GEM_disable_intr(void __iomem *base)
+{
+ __le32 val = 0;
+
+ writel(val, base + DAC960_GEM_IRQMASK_READ_OFFSET);
+}
+
+static inline void DAC960_GEM_write_cmd_mbox(union myrs_cmd_mbox *mem_mbox,
+ union myrs_cmd_mbox *mbox)
+{
+ memcpy(&mem_mbox->words[1], &mbox->words[1],
+ sizeof(union myrs_cmd_mbox) - sizeof(unsigned int));
+ /* Barrier to avoid reordering */
+ wmb();
+ mem_mbox->words[0] = mbox->words[0];
+ /* Barrier to force PCI access */
+ mb();
+}
+
+static inline void DAC960_GEM_write_hw_mbox(void __iomem *base,
+ dma_addr_t cmd_mbox_addr)
+{
+ dma_addr_writeql(cmd_mbox_addr, base + DAC960_GEM_CMDMBX_OFFSET);
+}
+
+static inline unsigned char DAC960_GEM_read_cmd_status(void __iomem *base)
+{
+ return readw(base + DAC960_GEM_CMDSTS_OFFSET + 2);
+}
+
+static inline bool
+DAC960_GEM_read_error_status(void __iomem *base, unsigned char *error,
+ unsigned char *param0, unsigned char *param1)
+{
+ __le32 val;
+
+ val = readl(base + DAC960_GEM_ERRSTS_READ_OFFSET);
+ if (!((le32_to_cpu(val) >> 24) & DAC960_GEM_ERRSTS_PENDING))
+ return false;
+ *error = val & ~(DAC960_GEM_ERRSTS_PENDING << 24);
+ *param0 = readb(base + DAC960_GEM_CMDMBX_OFFSET + 0);
+ *param1 = readb(base + DAC960_GEM_CMDMBX_OFFSET + 1);
+ writel(0x03000000, base + DAC960_GEM_ERRSTS_CLEAR_OFFSET);
+ return true;
+}
+
+static inline unsigned char
+DAC960_GEM_mbox_init(void __iomem *base, dma_addr_t mbox_addr)
+{
+ unsigned char status;
+
+ while (DAC960_GEM_hw_mbox_is_full(base))
+ udelay(1);
+ DAC960_GEM_write_hw_mbox(base, mbox_addr);
+ DAC960_GEM_hw_mbox_new_cmd(base);
+ while (!DAC960_GEM_hw_mbox_status_available(base))
+ udelay(1);
+ status = DAC960_GEM_read_cmd_status(base);
+ DAC960_GEM_ack_hw_mbox_intr(base);
+ DAC960_GEM_ack_hw_mbox_status(base);
+
+ return status;
+}
+
+static int DAC960_GEM_hw_init(struct pci_dev *pdev,
+ struct myrs_hba *cs, void __iomem *base)
+{
+ int timeout = 0;
+ unsigned char status, parm0, parm1;
+
+ DAC960_GEM_disable_intr(base);
+ DAC960_GEM_ack_hw_mbox_status(base);
+ udelay(1000);
+ while (DAC960_GEM_init_in_progress(base) &&
+ timeout < MYRS_MAILBOX_TIMEOUT) {
+ if (DAC960_GEM_read_error_status(base, &status,
+ &parm0, &parm1) &&
+ myrs_err_status(cs, status, parm0, parm1))
+ return -EIO;
+ udelay(10);
+ timeout++;
+ }
+ if (timeout == MYRS_MAILBOX_TIMEOUT) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for Controller Initialisation\n");
+ return -ETIMEDOUT;
+ }
+ if (!myrs_enable_mmio_mbox(cs, DAC960_GEM_mbox_init)) {
+ dev_err(&pdev->dev,
+ "Unable to Enable Memory Mailbox Interface\n");
+ DAC960_GEM_reset_ctrl(base);
+ return -EAGAIN;
+ }
+ DAC960_GEM_enable_intr(base);
+ cs->write_cmd_mbox = DAC960_GEM_write_cmd_mbox;
+ cs->get_cmd_mbox = DAC960_GEM_mem_mbox_new_cmd;
+ cs->disable_intr = DAC960_GEM_disable_intr;
+ cs->reset = DAC960_GEM_reset_ctrl;
+ return 0;
+}
+
+static irqreturn_t DAC960_GEM_intr_handler(int irq, void *arg)
+{
+ struct myrs_hba *cs = arg;
+ void __iomem *base = cs->io_base;
+ struct myrs_stat_mbox *next_stat_mbox;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->queue_lock, flags);
+ DAC960_GEM_ack_intr(base);
+ next_stat_mbox = cs->next_stat_mbox;
+ while (next_stat_mbox->id > 0) {
+ unsigned short id = next_stat_mbox->id;
+ struct scsi_cmnd *scmd = NULL;
+ struct myrs_cmdblk *cmd_blk = NULL;
+
+ if (id == MYRS_DCMD_TAG)
+ cmd_blk = &cs->dcmd_blk;
+ else if (id == MYRS_MCMD_TAG)
+ cmd_blk = &cs->mcmd_blk;
+ else {
+ scmd = scsi_host_find_tag(cs->host, id - 3);
+ if (scmd)
+ cmd_blk = scsi_cmd_priv(scmd);
+ }
+ if (cmd_blk) {
+ cmd_blk->status = next_stat_mbox->status;
+ cmd_blk->sense_len = next_stat_mbox->sense_len;
+ cmd_blk->residual = next_stat_mbox->residual;
+ } else
+ dev_err(&cs->pdev->dev,
+ "Unhandled command completion %d\n", id);
+
+ memset(next_stat_mbox, 0, sizeof(struct myrs_stat_mbox));
+ if (++next_stat_mbox > cs->last_stat_mbox)
+ next_stat_mbox = cs->first_stat_mbox;
+
+ if (cmd_blk) {
+ if (id < 3)
+ myrs_handle_cmdblk(cs, cmd_blk);
+ else
+ myrs_handle_scsi(cs, cmd_blk, scmd);
+ }
+ }
+ cs->next_stat_mbox = next_stat_mbox;
+ spin_unlock_irqrestore(&cs->queue_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct myrs_privdata DAC960_GEM_privdata = {
+ .hw_init = DAC960_GEM_hw_init,
+ .irq_handler = DAC960_GEM_intr_handler,
+ .mmio_size = DAC960_GEM_mmio_size,
+};
+
+/*
+ * DAC960 BA Series Controllers.
+ */
+
+static inline void DAC960_BA_hw_mbox_new_cmd(void __iomem *base)
+{
+ writeb(DAC960_BA_IDB_HWMBOX_NEW_CMD, base + DAC960_BA_IDB_OFFSET);
+}
+
+static inline void DAC960_BA_ack_hw_mbox_status(void __iomem *base)
+{
+ writeb(DAC960_BA_IDB_HWMBOX_ACK_STS, base + DAC960_BA_IDB_OFFSET);
+}
+
+static inline void DAC960_BA_reset_ctrl(void __iomem *base)
+{
+ writeb(DAC960_BA_IDB_CTRL_RESET, base + DAC960_BA_IDB_OFFSET);
+}
+
+static inline void DAC960_BA_mem_mbox_new_cmd(void __iomem *base)
+{
+ writeb(DAC960_BA_IDB_MMBOX_NEW_CMD, base + DAC960_BA_IDB_OFFSET);
+}
+
+static inline bool DAC960_BA_hw_mbox_is_full(void __iomem *base)
+{
+ u8 val;
+
+ val = readb(base + DAC960_BA_IDB_OFFSET);
+ return !(val & DAC960_BA_IDB_HWMBOX_EMPTY);
+}
+
+static inline bool DAC960_BA_init_in_progress(void __iomem *base)
+{
+ u8 val;
+
+ val = readb(base + DAC960_BA_IDB_OFFSET);
+ return !(val & DAC960_BA_IDB_INIT_DONE);
+}
+
+static inline void DAC960_BA_ack_hw_mbox_intr(void __iomem *base)
+{
+ writeb(DAC960_BA_ODB_HWMBOX_ACK_IRQ, base + DAC960_BA_ODB_OFFSET);
+}
+
+static inline void DAC960_BA_ack_intr(void __iomem *base)
+{
+ writeb(DAC960_BA_ODB_HWMBOX_ACK_IRQ | DAC960_BA_ODB_MMBOX_ACK_IRQ,
+ base + DAC960_BA_ODB_OFFSET);
+}
+
+static inline bool DAC960_BA_hw_mbox_status_available(void __iomem *base)
+{
+ u8 val;
+
+ val = readb(base + DAC960_BA_ODB_OFFSET);
+ return val & DAC960_BA_ODB_HWMBOX_STS_AVAIL;
+}
+
+static inline void DAC960_BA_enable_intr(void __iomem *base)
+{
+ writeb(~DAC960_BA_IRQMASK_DISABLE_IRQ, base + DAC960_BA_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_BA_disable_intr(void __iomem *base)
+{
+ writeb(0xFF, base + DAC960_BA_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_BA_write_cmd_mbox(union myrs_cmd_mbox *mem_mbox,
+ union myrs_cmd_mbox *mbox)
+{
+ memcpy(&mem_mbox->words[1], &mbox->words[1],
+ sizeof(union myrs_cmd_mbox) - sizeof(unsigned int));
+ /* Barrier to avoid reordering */
+ wmb();
+ mem_mbox->words[0] = mbox->words[0];
+ /* Barrier to force PCI access */
+ mb();
+}
+
+
+static inline void DAC960_BA_write_hw_mbox(void __iomem *base,
+ dma_addr_t cmd_mbox_addr)
+{
+ dma_addr_writeql(cmd_mbox_addr, base + DAC960_BA_CMDMBX_OFFSET);
+}
+
+static inline unsigned char DAC960_BA_read_cmd_status(void __iomem *base)
+{
+ return readw(base + DAC960_BA_CMDSTS_OFFSET + 2);
+}
+
+static inline bool
+DAC960_BA_read_error_status(void __iomem *base, unsigned char *error,
+ unsigned char *param0, unsigned char *param1)
+{
+ u8 val;
+
+ val = readb(base + DAC960_BA_ERRSTS_OFFSET);
+ if (!(val & DAC960_BA_ERRSTS_PENDING))
+ return false;
+ val &= ~DAC960_BA_ERRSTS_PENDING;
+ *error = val;
+ *param0 = readb(base + DAC960_BA_CMDMBX_OFFSET + 0);
+ *param1 = readb(base + DAC960_BA_CMDMBX_OFFSET + 1);
+ writeb(0xFF, base + DAC960_BA_ERRSTS_OFFSET);
+ return true;
+}
+
+static inline unsigned char
+DAC960_BA_mbox_init(void __iomem *base, dma_addr_t mbox_addr)
+{
+ unsigned char status;
+
+ while (DAC960_BA_hw_mbox_is_full(base))
+ udelay(1);
+ DAC960_BA_write_hw_mbox(base, mbox_addr);
+ DAC960_BA_hw_mbox_new_cmd(base);
+ while (!DAC960_BA_hw_mbox_status_available(base))
+ udelay(1);
+ status = DAC960_BA_read_cmd_status(base);
+ DAC960_BA_ack_hw_mbox_intr(base);
+ DAC960_BA_ack_hw_mbox_status(base);
+
+ return status;
+}
+
+static int DAC960_BA_hw_init(struct pci_dev *pdev,
+ struct myrs_hba *cs, void __iomem *base)
+{
+ int timeout = 0;
+ unsigned char status, parm0, parm1;
+
+ DAC960_BA_disable_intr(base);
+ DAC960_BA_ack_hw_mbox_status(base);
+ udelay(1000);
+ while (DAC960_BA_init_in_progress(base) &&
+ timeout < MYRS_MAILBOX_TIMEOUT) {
+ if (DAC960_BA_read_error_status(base, &status,
+ &parm0, &parm1) &&
+ myrs_err_status(cs, status, parm0, parm1))
+ return -EIO;
+ udelay(10);
+ timeout++;
+ }
+ if (timeout == MYRS_MAILBOX_TIMEOUT) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for Controller Initialisation\n");
+ return -ETIMEDOUT;
+ }
+ if (!myrs_enable_mmio_mbox(cs, DAC960_BA_mbox_init)) {
+ dev_err(&pdev->dev,
+ "Unable to Enable Memory Mailbox Interface\n");
+ DAC960_BA_reset_ctrl(base);
+ return -EAGAIN;
+ }
+ DAC960_BA_enable_intr(base);
+ cs->write_cmd_mbox = DAC960_BA_write_cmd_mbox;
+ cs->get_cmd_mbox = DAC960_BA_mem_mbox_new_cmd;
+ cs->disable_intr = DAC960_BA_disable_intr;
+ cs->reset = DAC960_BA_reset_ctrl;
+ return 0;
+}
+
+static irqreturn_t DAC960_BA_intr_handler(int irq, void *arg)
+{
+ struct myrs_hba *cs = arg;
+ void __iomem *base = cs->io_base;
+ struct myrs_stat_mbox *next_stat_mbox;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->queue_lock, flags);
+ DAC960_BA_ack_intr(base);
+ next_stat_mbox = cs->next_stat_mbox;
+ while (next_stat_mbox->id > 0) {
+ unsigned short id = next_stat_mbox->id;
+ struct scsi_cmnd *scmd = NULL;
+ struct myrs_cmdblk *cmd_blk = NULL;
+
+ if (id == MYRS_DCMD_TAG)
+ cmd_blk = &cs->dcmd_blk;
+ else if (id == MYRS_MCMD_TAG)
+ cmd_blk = &cs->mcmd_blk;
+ else {
+ scmd = scsi_host_find_tag(cs->host, id - 3);
+ if (scmd)
+ cmd_blk = scsi_cmd_priv(scmd);
+ }
+ if (cmd_blk) {
+ cmd_blk->status = next_stat_mbox->status;
+ cmd_blk->sense_len = next_stat_mbox->sense_len;
+ cmd_blk->residual = next_stat_mbox->residual;
+ } else
+ dev_err(&cs->pdev->dev,
+ "Unhandled command completion %d\n", id);
+
+ memset(next_stat_mbox, 0, sizeof(struct myrs_stat_mbox));
+ if (++next_stat_mbox > cs->last_stat_mbox)
+ next_stat_mbox = cs->first_stat_mbox;
+
+ if (cmd_blk) {
+ if (id < 3)
+ myrs_handle_cmdblk(cs, cmd_blk);
+ else
+ myrs_handle_scsi(cs, cmd_blk, scmd);
+ }
+ }
+ cs->next_stat_mbox = next_stat_mbox;
+ spin_unlock_irqrestore(&cs->queue_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct myrs_privdata DAC960_BA_privdata = {
+ .hw_init = DAC960_BA_hw_init,
+ .irq_handler = DAC960_BA_intr_handler,
+ .mmio_size = DAC960_BA_mmio_size,
+};
+
+/*
+ * DAC960 LP Series Controllers.
+ */
+
+static inline void DAC960_LP_hw_mbox_new_cmd(void __iomem *base)
+{
+ writeb(DAC960_LP_IDB_HWMBOX_NEW_CMD, base + DAC960_LP_IDB_OFFSET);
+}
+
+static inline void DAC960_LP_ack_hw_mbox_status(void __iomem *base)
+{
+ writeb(DAC960_LP_IDB_HWMBOX_ACK_STS, base + DAC960_LP_IDB_OFFSET);
+}
+
+static inline void DAC960_LP_reset_ctrl(void __iomem *base)
+{
+ writeb(DAC960_LP_IDB_CTRL_RESET, base + DAC960_LP_IDB_OFFSET);
+}
+
+static inline void DAC960_LP_mem_mbox_new_cmd(void __iomem *base)
+{
+ writeb(DAC960_LP_IDB_MMBOX_NEW_CMD, base + DAC960_LP_IDB_OFFSET);
+}
+
+static inline bool DAC960_LP_hw_mbox_is_full(void __iomem *base)
+{
+ u8 val;
+
+ val = readb(base + DAC960_LP_IDB_OFFSET);
+ return val & DAC960_LP_IDB_HWMBOX_FULL;
+}
+
+static inline bool DAC960_LP_init_in_progress(void __iomem *base)
+{
+ u8 val;
+
+ val = readb(base + DAC960_LP_IDB_OFFSET);
+ return val & DAC960_LP_IDB_INIT_IN_PROGRESS;
+}
+
+static inline void DAC960_LP_ack_hw_mbox_intr(void __iomem *base)
+{
+ writeb(DAC960_LP_ODB_HWMBOX_ACK_IRQ, base + DAC960_LP_ODB_OFFSET);
+}
+
+static inline void DAC960_LP_ack_intr(void __iomem *base)
+{
+ writeb(DAC960_LP_ODB_HWMBOX_ACK_IRQ | DAC960_LP_ODB_MMBOX_ACK_IRQ,
+ base + DAC960_LP_ODB_OFFSET);
+}
+
+static inline bool DAC960_LP_hw_mbox_status_available(void __iomem *base)
+{
+ u8 val;
+
+ val = readb(base + DAC960_LP_ODB_OFFSET);
+ return val & DAC960_LP_ODB_HWMBOX_STS_AVAIL;
+}
+
+static inline void DAC960_LP_enable_intr(void __iomem *base)
+{
+ writeb(~DAC960_LP_IRQMASK_DISABLE_IRQ, base + DAC960_LP_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_LP_disable_intr(void __iomem *base)
+{
+ writeb(0xFF, base + DAC960_LP_IRQMASK_OFFSET);
+}
+
+static inline void DAC960_LP_write_cmd_mbox(union myrs_cmd_mbox *mem_mbox,
+ union myrs_cmd_mbox *mbox)
+{
+ memcpy(&mem_mbox->words[1], &mbox->words[1],
+ sizeof(union myrs_cmd_mbox) - sizeof(unsigned int));
+ /* Barrier to avoid reordering */
+ wmb();
+ mem_mbox->words[0] = mbox->words[0];
+ /* Barrier to force PCI access */
+ mb();
+}
+
+static inline void DAC960_LP_write_hw_mbox(void __iomem *base,
+ dma_addr_t cmd_mbox_addr)
+{
+ dma_addr_writeql(cmd_mbox_addr, base + DAC960_LP_CMDMBX_OFFSET);
+}
+
+static inline unsigned char DAC960_LP_read_cmd_status(void __iomem *base)
+{
+ return readw(base + DAC960_LP_CMDSTS_OFFSET + 2);
+}
+
+static inline bool
+DAC960_LP_read_error_status(void __iomem *base, unsigned char *error,
+ unsigned char *param0, unsigned char *param1)
+{
+ u8 val;
+
+ val = readb(base + DAC960_LP_ERRSTS_OFFSET);
+ if (!(val & DAC960_LP_ERRSTS_PENDING))
+ return false;
+ val &= ~DAC960_LP_ERRSTS_PENDING;
+ *error = val;
+ *param0 = readb(base + DAC960_LP_CMDMBX_OFFSET + 0);
+ *param1 = readb(base + DAC960_LP_CMDMBX_OFFSET + 1);
+ writeb(0xFF, base + DAC960_LP_ERRSTS_OFFSET);
+ return true;
+}
+
+static inline unsigned char
+DAC960_LP_mbox_init(void __iomem *base, dma_addr_t mbox_addr)
+{
+ unsigned char status;
+
+ while (DAC960_LP_hw_mbox_is_full(base))
+ udelay(1);
+ DAC960_LP_write_hw_mbox(base, mbox_addr);
+ DAC960_LP_hw_mbox_new_cmd(base);
+ while (!DAC960_LP_hw_mbox_status_available(base))
+ udelay(1);
+ status = DAC960_LP_read_cmd_status(base);
+ DAC960_LP_ack_hw_mbox_intr(base);
+ DAC960_LP_ack_hw_mbox_status(base);
+
+ return status;
+}
+
+static int DAC960_LP_hw_init(struct pci_dev *pdev,
+ struct myrs_hba *cs, void __iomem *base)
+{
+ int timeout = 0;
+ unsigned char status, parm0, parm1;
+
+ DAC960_LP_disable_intr(base);
+ DAC960_LP_ack_hw_mbox_status(base);
+ udelay(1000);
+ while (DAC960_LP_init_in_progress(base) &&
+ timeout < MYRS_MAILBOX_TIMEOUT) {
+ if (DAC960_LP_read_error_status(base, &status,
+ &parm0, &parm1) &&
+ myrs_err_status(cs, status, parm0, parm1))
+ return -EIO;
+ udelay(10);
+ timeout++;
+ }
+ if (timeout == MYRS_MAILBOX_TIMEOUT) {
+ dev_err(&pdev->dev,
+ "Timeout waiting for Controller Initialisation\n");
+ return -ETIMEDOUT;
+ }
+ if (!myrs_enable_mmio_mbox(cs, DAC960_LP_mbox_init)) {
+ dev_err(&pdev->dev,
+ "Unable to Enable Memory Mailbox Interface\n");
+ DAC960_LP_reset_ctrl(base);
+ return -ENODEV;
+ }
+ DAC960_LP_enable_intr(base);
+ cs->write_cmd_mbox = DAC960_LP_write_cmd_mbox;
+ cs->get_cmd_mbox = DAC960_LP_mem_mbox_new_cmd;
+ cs->disable_intr = DAC960_LP_disable_intr;
+ cs->reset = DAC960_LP_reset_ctrl;
+
+ return 0;
+}
+
+static irqreturn_t DAC960_LP_intr_handler(int irq, void *arg)
+{
+ struct myrs_hba *cs = arg;
+ void __iomem *base = cs->io_base;
+ struct myrs_stat_mbox *next_stat_mbox;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->queue_lock, flags);
+ DAC960_LP_ack_intr(base);
+ next_stat_mbox = cs->next_stat_mbox;
+ while (next_stat_mbox->id > 0) {
+ unsigned short id = next_stat_mbox->id;
+ struct scsi_cmnd *scmd = NULL;
+ struct myrs_cmdblk *cmd_blk = NULL;
+
+ if (id == MYRS_DCMD_TAG)
+ cmd_blk = &cs->dcmd_blk;
+ else if (id == MYRS_MCMD_TAG)
+ cmd_blk = &cs->mcmd_blk;
+ else {
+ scmd = scsi_host_find_tag(cs->host, id - 3);
+ if (scmd)
+ cmd_blk = scsi_cmd_priv(scmd);
+ }
+ if (cmd_blk) {
+ cmd_blk->status = next_stat_mbox->status;
+ cmd_blk->sense_len = next_stat_mbox->sense_len;
+ cmd_blk->residual = next_stat_mbox->residual;
+ } else
+ dev_err(&cs->pdev->dev,
+ "Unhandled command completion %d\n", id);
+
+ memset(next_stat_mbox, 0, sizeof(struct myrs_stat_mbox));
+ if (++next_stat_mbox > cs->last_stat_mbox)
+ next_stat_mbox = cs->first_stat_mbox;
+
+ if (cmd_blk) {
+ if (id < 3)
+ myrs_handle_cmdblk(cs, cmd_blk);
+ else
+ myrs_handle_scsi(cs, cmd_blk, scmd);
+ }
+ }
+ cs->next_stat_mbox = next_stat_mbox;
+ spin_unlock_irqrestore(&cs->queue_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct myrs_privdata DAC960_LP_privdata = {
+ .hw_init = DAC960_LP_hw_init,
+ .irq_handler = DAC960_LP_intr_handler,
+ .mmio_size = DAC960_LP_mmio_size,
+};
+
+/*
+ * Module functions
+ */
+static int
+myrs_probe(struct pci_dev *dev, const struct pci_device_id *entry)
+{
+ struct myrs_hba *cs;
+ int ret;
+
+ cs = myrs_detect(dev, entry);
+ if (!cs)
+ return -ENODEV;
+
+ ret = myrs_get_config(cs);
+ if (ret < 0) {
+ myrs_cleanup(cs);
+ return ret;
+ }
+
+ if (!myrs_create_mempools(dev, cs)) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ ret = scsi_add_host(cs->host, &dev->dev);
+ if (ret) {
+ dev_err(&dev->dev, "scsi_add_host failed with %d\n", ret);
+ myrs_destroy_mempools(cs);
+ goto failed;
+ }
+ scsi_scan_host(cs->host);
+ return 0;
+failed:
+ myrs_cleanup(cs);
+ return ret;
+}
+
+
+static void myrs_remove(struct pci_dev *pdev)
+{
+ struct myrs_hba *cs = pci_get_drvdata(pdev);
+
+ if (cs == NULL)
+ return;
+
+ shost_printk(KERN_NOTICE, cs->host, "Flushing Cache...");
+ myrs_flush_cache(cs);
+ myrs_destroy_mempools(cs);
+ myrs_cleanup(cs);
+}
+
+
+static const struct pci_device_id myrs_id_table[] = {
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_MYLEX,
+ PCI_DEVICE_ID_MYLEX_DAC960_GEM,
+ PCI_VENDOR_ID_MYLEX, PCI_ANY_ID),
+ .driver_data = (unsigned long) &DAC960_GEM_privdata,
+ },
+ {
+ PCI_DEVICE_DATA(MYLEX, DAC960_BA, &DAC960_BA_privdata),
+ },
+ {
+ PCI_DEVICE_DATA(MYLEX, DAC960_LP, &DAC960_LP_privdata),
+ },
+ {0, },
+};
+
+MODULE_DEVICE_TABLE(pci, myrs_id_table);
+
+static struct pci_driver myrs_pci_driver = {
+ .name = "myrs",
+ .id_table = myrs_id_table,
+ .probe = myrs_probe,
+ .remove = myrs_remove,
+};
+
+static int __init myrs_init_module(void)
+{
+ int ret;
+
+ myrs_raid_template = raid_class_attach(&myrs_raid_functions);
+ if (!myrs_raid_template)
+ return -ENODEV;
+
+ ret = pci_register_driver(&myrs_pci_driver);
+ if (ret)
+ raid_class_release(myrs_raid_template);
+
+ return ret;
+}
+
+static void __exit myrs_cleanup_module(void)
+{
+ pci_unregister_driver(&myrs_pci_driver);
+ raid_class_release(myrs_raid_template);
+}
+
+module_init(myrs_init_module);
+module_exit(myrs_cleanup_module);
+
+MODULE_DESCRIPTION("Mylex DAC960/AcceleRAID/eXtremeRAID driver (SCSI Interface)");
+MODULE_AUTHOR("Hannes Reinecke <hare@suse.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/myrs.h b/drivers/scsi/myrs.h
new file mode 100644
index 000000000000..e1d6b123de7b
--- /dev/null
+++ b/drivers/scsi/myrs.h
@@ -0,0 +1,1133 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
+ *
+ * This driver supports the newer, SCSI-based firmware interface only.
+ *
+ * Copyright 2018 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com>
+ *
+ * Based on the original DAC960 driver, which has
+ * Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com>
+ * Portions Copyright 2002 by Mylex (An IBM Business Unit)
+ */
+
+#ifndef _MYRS_H
+#define _MYRS_H
+
+#define MYRS_MAILBOX_TIMEOUT 1000000
+
+#define MYRS_DCMD_TAG 1
+#define MYRS_MCMD_TAG 2
+
+#define MYRS_LINE_BUFFER_SIZE 128
+
+#define MYRS_PRIMARY_MONITOR_INTERVAL (10 * HZ)
+#define MYRS_SECONDARY_MONITOR_INTERVAL (60 * HZ)
+
+/* Maximum number of Scatter/Gather Segments supported */
+#define MYRS_SG_LIMIT 128
+
+/*
+ * Number of Command and Status Mailboxes used by the
+ * DAC960 V2 Firmware Memory Mailbox Interface.
+ */
+#define MYRS_MAX_CMD_MBOX 512
+#define MYRS_MAX_STAT_MBOX 512
+
+#define MYRS_DCDB_SIZE 16
+#define MYRS_SENSE_SIZE 14
+
+/*
+ * DAC960 V2 Firmware Command Opcodes.
+ */
+enum myrs_cmd_opcode {
+ MYRS_CMD_OP_MEMCOPY = 0x01,
+ MYRS_CMD_OP_SCSI_10_PASSTHRU = 0x02,
+ MYRS_CMD_OP_SCSI_255_PASSTHRU = 0x03,
+ MYRS_CMD_OP_SCSI_10 = 0x04,
+ MYRS_CMD_OP_SCSI_256 = 0x05,
+ MYRS_CMD_OP_IOCTL = 0x20,
+} __packed;
+
+/*
+ * DAC960 V2 Firmware IOCTL Opcodes.
+ */
+enum myrs_ioctl_opcode {
+ MYRS_IOCTL_GET_CTLR_INFO = 0x01,
+ MYRS_IOCTL_GET_LDEV_INFO_VALID = 0x03,
+ MYRS_IOCTL_GET_PDEV_INFO_VALID = 0x05,
+ MYRS_IOCTL_GET_HEALTH_STATUS = 0x11,
+ MYRS_IOCTL_GET_EVENT = 0x15,
+ MYRS_IOCTL_START_DISCOVERY = 0x81,
+ MYRS_IOCTL_SET_DEVICE_STATE = 0x82,
+ MYRS_IOCTL_INIT_PDEV_START = 0x84,
+ MYRS_IOCTL_INIT_PDEV_STOP = 0x85,
+ MYRS_IOCTL_INIT_LDEV_START = 0x86,
+ MYRS_IOCTL_INIT_LDEV_STOP = 0x87,
+ MYRS_IOCTL_RBLD_DEVICE_START = 0x88,
+ MYRS_IOCTL_RBLD_DEVICE_STOP = 0x89,
+ MYRS_IOCTL_MAKE_CONSISTENT_START = 0x8A,
+ MYRS_IOCTL_MAKE_CONSISTENT_STOP = 0x8B,
+ MYRS_IOCTL_CC_START = 0x8C,
+ MYRS_IOCTL_CC_STOP = 0x8D,
+ MYRS_IOCTL_SET_MEM_MBOX = 0x8E,
+ MYRS_IOCTL_RESET_DEVICE = 0x90,
+ MYRS_IOCTL_FLUSH_DEVICE_DATA = 0x91,
+ MYRS_IOCTL_PAUSE_DEVICE = 0x92,
+ MYRS_IOCTL_UNPAUS_EDEVICE = 0x93,
+ MYRS_IOCTL_LOCATE_DEVICE = 0x94,
+ MYRS_IOCTL_CREATE_CONFIGURATION = 0xC0,
+ MYRS_IOCTL_DELETE_LDEV = 0xC1,
+ MYRS_IOCTL_REPLACE_INTERNALDEVICE = 0xC2,
+ MYRS_IOCTL_RENAME_LDEV = 0xC3,
+ MYRS_IOCTL_ADD_CONFIGURATION = 0xC4,
+ MYRS_IOCTL_XLATE_PDEV_TO_LDEV = 0xC5,
+ MYRS_IOCTL_CLEAR_CONFIGURATION = 0xCA,
+} __packed;
+
+/*
+ * DAC960 V2 Firmware Command Status Codes.
+ */
+#define MYRS_STATUS_SUCCESS 0x00
+#define MYRS_STATUS_FAILED 0x02
+#define MYRS_STATUS_DEVICE_BUSY 0x08
+#define MYRS_STATUS_DEVICE_NON_RESPONSIVE 0x0E
+#define MYRS_STATUS_DEVICE_NON_RESPONSIVE2 0x0F
+#define MYRS_STATUS_RESERVATION_CONFLICT 0x18
+
+/*
+ * DAC960 V2 Firmware Memory Type structure.
+ */
+struct myrs_mem_type {
+ enum {
+ MYRS_MEMTYPE_RESERVED = 0x00,
+ MYRS_MEMTYPE_DRAM = 0x01,
+ MYRS_MEMTYPE_EDRAM = 0x02,
+ MYRS_MEMTYPE_EDO = 0x03,
+ MYRS_MEMTYPE_SDRAM = 0x04,
+ MYRS_MEMTYPE_LAST = 0x1F,
+ } __packed mem_type:5; /* Byte 0 Bits 0-4 */
+ unsigned rsvd:1; /* Byte 0 Bit 5 */
+ unsigned mem_parity:1; /* Byte 0 Bit 6 */
+ unsigned mem_ecc:1; /* Byte 0 Bit 7 */
+};
+
+/*
+ * DAC960 V2 Firmware Processor Type structure.
+ */
+enum myrs_cpu_type {
+ MYRS_CPUTYPE_i960CA = 0x01,
+ MYRS_CPUTYPE_i960RD = 0x02,
+ MYRS_CPUTYPE_i960RN = 0x03,
+ MYRS_CPUTYPE_i960RP = 0x04,
+ MYRS_CPUTYPE_NorthBay = 0x05,
+ MYRS_CPUTYPE_StrongArm = 0x06,
+ MYRS_CPUTYPE_i960RM = 0x07,
+} __packed;
+
+/*
+ * DAC960 V2 Firmware Get Controller Info reply structure.
+ */
+struct myrs_ctlr_info {
+ unsigned char rsvd1; /* Byte 0 */
+ enum {
+ MYRS_SCSI_BUS = 0x00,
+ MYRS_Fibre_BUS = 0x01,
+ MYRS_PCI_BUS = 0x03
+ } __packed bus; /* Byte 1 */
+ enum {
+ MYRS_CTLR_DAC960E = 0x01,
+ MYRS_CTLR_DAC960M = 0x08,
+ MYRS_CTLR_DAC960PD = 0x10,
+ MYRS_CTLR_DAC960PL = 0x11,
+ MYRS_CTLR_DAC960PU = 0x12,
+ MYRS_CTLR_DAC960PE = 0x13,
+ MYRS_CTLR_DAC960PG = 0x14,
+ MYRS_CTLR_DAC960PJ = 0x15,
+ MYRS_CTLR_DAC960PTL0 = 0x16,
+ MYRS_CTLR_DAC960PR = 0x17,
+ MYRS_CTLR_DAC960PRL = 0x18,
+ MYRS_CTLR_DAC960PT = 0x19,
+ MYRS_CTLR_DAC1164P = 0x1A,
+ MYRS_CTLR_DAC960PTL1 = 0x1B,
+ MYRS_CTLR_EXR2000P = 0x1C,
+ MYRS_CTLR_EXR3000P = 0x1D,
+ MYRS_CTLR_ACCELERAID352 = 0x1E,
+ MYRS_CTLR_ACCELERAID170 = 0x1F,
+ MYRS_CTLR_ACCELERAID160 = 0x20,
+ MYRS_CTLR_DAC960S = 0x60,
+ MYRS_CTLR_DAC960SU = 0x61,
+ MYRS_CTLR_DAC960SX = 0x62,
+ MYRS_CTLR_DAC960SF = 0x63,
+ MYRS_CTLR_DAC960SS = 0x64,
+ MYRS_CTLR_DAC960FL = 0x65,
+ MYRS_CTLR_DAC960LL = 0x66,
+ MYRS_CTLR_DAC960FF = 0x67,
+ MYRS_CTLR_DAC960HP = 0x68,
+ MYRS_CTLR_RAIDBRICK = 0x69,
+ MYRS_CTLR_METEOR_FL = 0x6A,
+ MYRS_CTLR_METEOR_FF = 0x6B
+ } __packed ctlr_type; /* Byte 2 */
+ unsigned char rsvd2; /* Byte 3 */
+ unsigned short bus_speed_mhz; /* Bytes 4-5 */
+ unsigned char bus_width; /* Byte 6 */
+ unsigned char flash_code; /* Byte 7 */
+ unsigned char ports_present; /* Byte 8 */
+ unsigned char rsvd3[7]; /* Bytes 9-15 */
+ unsigned char bus_name[16]; /* Bytes 16-31 */
+ unsigned char ctlr_name[16]; /* Bytes 32-47 */
+ unsigned char rsvd4[16]; /* Bytes 48-63 */
+ /* Firmware Release Information */
+ unsigned char fw_major_version; /* Byte 64 */
+ unsigned char fw_minor_version; /* Byte 65 */
+ unsigned char fw_turn_number; /* Byte 66 */
+ unsigned char fw_build_number; /* Byte 67 */
+ unsigned char fw_release_day; /* Byte 68 */
+ unsigned char fw_release_month; /* Byte 69 */
+ unsigned char fw_release_year_hi; /* Byte 70 */
+ unsigned char fw_release_year_lo; /* Byte 71 */
+ /* Hardware Release Information */
+ unsigned char hw_rev; /* Byte 72 */
+ unsigned char rsvd5[3]; /* Bytes 73-75 */
+ unsigned char hw_release_day; /* Byte 76 */
+ unsigned char hw_release_month; /* Byte 77 */
+ unsigned char hw_release_year_hi; /* Byte 78 */
+ unsigned char hw_release_year_lo; /* Byte 79 */
+ /* Hardware Manufacturing Information */
+ unsigned char manuf_batch_num; /* Byte 80 */
+ unsigned char rsvd6; /* Byte 81 */
+ unsigned char manuf_plant_num; /* Byte 82 */
+ unsigned char rsvd7; /* Byte 83 */
+ unsigned char hw_manuf_day; /* Byte 84 */
+ unsigned char hw_manuf_month; /* Byte 85 */
+ unsigned char hw_manuf_year_hi; /* Byte 86 */
+ unsigned char hw_manuf_year_lo; /* Byte 87 */
+ unsigned char max_pd_per_xld; /* Byte 88 */
+ unsigned char max_ild_per_xld; /* Byte 89 */
+ unsigned short nvram_size_kb; /* Bytes 90-91 */
+ unsigned char max_xld; /* Byte 92 */
+ unsigned char rsvd8[3]; /* Bytes 93-95 */
+ /* Unique Information per Controller */
+ unsigned char serial_number[16]; /* Bytes 96-111 */
+ unsigned char rsvd9[16]; /* Bytes 112-127 */
+ /* Vendor Information */
+ unsigned char rsvd10[3]; /* Bytes 128-130 */
+ unsigned char oem_code; /* Byte 131 */
+ unsigned char vendor[16]; /* Bytes 132-147 */
+ /* Other Physical/Controller/Operation Information */
+ unsigned char bbu_present:1; /* Byte 148 Bit 0 */
+ unsigned char cluster_mode:1; /* Byte 148 Bit 1 */
+ unsigned char rsvd11:6; /* Byte 148 Bits 2-7 */
+ unsigned char rsvd12[3]; /* Bytes 149-151 */
+ /* Physical Device Scan Information */
+ unsigned char pscan_active:1; /* Byte 152 Bit 0 */
+ unsigned char rsvd13:7; /* Byte 152 Bits 1-7 */
+ unsigned char pscan_chan; /* Byte 153 */
+ unsigned char pscan_target; /* Byte 154 */
+ unsigned char pscan_lun; /* Byte 155 */
+ /* Maximum Command Data Transfer Sizes */
+ unsigned short max_transfer_size; /* Bytes 156-157 */
+ unsigned short max_sge; /* Bytes 158-159 */
+ /* Logical/Physical Device Counts */
+ unsigned short ldev_present; /* Bytes 160-161 */
+ unsigned short ldev_critical; /* Bytes 162-163 */
+ unsigned short ldev_offline; /* Bytes 164-165 */
+ unsigned short pdev_present; /* Bytes 166-167 */
+ unsigned short pdisk_present; /* Bytes 168-169 */
+ unsigned short pdisk_critical; /* Bytes 170-171 */
+ unsigned short pdisk_offline; /* Bytes 172-173 */
+ unsigned short max_tcq; /* Bytes 174-175 */
+ /* Channel and Target ID Information */
+ unsigned char physchan_present; /* Byte 176 */
+ unsigned char virtchan_present; /* Byte 177 */
+ unsigned char physchan_max; /* Byte 178 */
+ unsigned char virtchan_max; /* Byte 179 */
+ unsigned char max_targets[16]; /* Bytes 180-195 */
+ unsigned char rsvd14[12]; /* Bytes 196-207 */
+ /* Memory/Cache Information */
+ unsigned short mem_size_mb; /* Bytes 208-209 */
+ unsigned short cache_size_mb; /* Bytes 210-211 */
+ unsigned int valid_cache_bytes; /* Bytes 212-215 */
+ unsigned int dirty_cache_bytes; /* Bytes 216-219 */
+ unsigned short mem_speed_mhz; /* Bytes 220-221 */
+ unsigned char mem_data_width; /* Byte 222 */
+ struct myrs_mem_type mem_type; /* Byte 223 */
+ unsigned char cache_mem_type_name[16]; /* Bytes 224-239 */
+ /* Execution Memory Information */
+ unsigned short exec_mem_size_mb; /* Bytes 240-241 */
+ unsigned short exec_l2_cache_size_mb; /* Bytes 242-243 */
+ unsigned char rsvd15[8]; /* Bytes 244-251 */
+ unsigned short exec_mem_speed_mhz; /* Bytes 252-253 */
+ unsigned char exec_mem_data_width; /* Byte 254 */
+ struct myrs_mem_type exec_mem_type; /* Byte 255 */
+ unsigned char exec_mem_type_name[16]; /* Bytes 256-271 */
+ /* CPU Type Information */
+ struct { /* Bytes 272-335 */
+ unsigned short cpu_speed_mhz;
+ enum myrs_cpu_type cpu_type;
+ unsigned char cpu_count;
+ unsigned char rsvd16[12];
+ unsigned char cpu_name[16];
+ } __packed cpu[2];
+ /* Debugging/Profiling/Command Time Tracing Information */
+ unsigned short cur_prof_page_num; /* Bytes 336-337 */
+ unsigned short num_prof_waiters; /* Bytes 338-339 */
+ unsigned short cur_trace_page_num; /* Bytes 340-341 */
+ unsigned short num_trace_waiters; /* Bytes 342-343 */
+ unsigned char rsvd18[8]; /* Bytes 344-351 */
+ /* Error Counters on Physical Devices */
+ unsigned short pdev_bus_resets; /* Bytes 352-353 */
+ unsigned short pdev_parity_errors; /* Bytes 355-355 */
+ unsigned short pdev_soft_errors; /* Bytes 356-357 */
+ unsigned short pdev_cmds_failed; /* Bytes 358-359 */
+ unsigned short pdev_misc_errors; /* Bytes 360-361 */
+ unsigned short pdev_cmd_timeouts; /* Bytes 362-363 */
+ unsigned short pdev_sel_timeouts; /* Bytes 364-365 */
+ unsigned short pdev_retries_done; /* Bytes 366-367 */
+ unsigned short pdev_aborts_done; /* Bytes 368-369 */
+ unsigned short pdev_host_aborts_done; /* Bytes 370-371 */
+ unsigned short pdev_predicted_failures; /* Bytes 372-373 */
+ unsigned short pdev_host_cmds_failed; /* Bytes 374-375 */
+ unsigned short pdev_hard_errors; /* Bytes 376-377 */
+ unsigned char rsvd19[6]; /* Bytes 378-383 */
+ /* Error Counters on Logical Devices */
+ unsigned short ldev_soft_errors; /* Bytes 384-385 */
+ unsigned short ldev_cmds_failed; /* Bytes 386-387 */
+ unsigned short ldev_host_aborts_done; /* Bytes 388-389 */
+ unsigned char rsvd20[2]; /* Bytes 390-391 */
+ /* Error Counters on Controller */
+ unsigned short ctlr_mem_errors; /* Bytes 392-393 */
+ unsigned short ctlr_host_aborts_done; /* Bytes 394-395 */
+ unsigned char rsvd21[4]; /* Bytes 396-399 */
+ /* Long Duration Activity Information */
+ unsigned short bg_init_active; /* Bytes 400-401 */
+ unsigned short ldev_init_active; /* Bytes 402-403 */
+ unsigned short pdev_init_active; /* Bytes 404-405 */
+ unsigned short cc_active; /* Bytes 406-407 */
+ unsigned short rbld_active; /* Bytes 408-409 */
+ unsigned short exp_active; /* Bytes 410-411 */
+ unsigned short patrol_active; /* Bytes 412-413 */
+ unsigned char rsvd22[2]; /* Bytes 414-415 */
+ /* Flash ROM Information */
+ unsigned char flash_type; /* Byte 416 */
+ unsigned char rsvd23; /* Byte 417 */
+ unsigned short flash_size_MB; /* Bytes 418-419 */
+ unsigned int flash_limit; /* Bytes 420-423 */
+ unsigned int flash_count; /* Bytes 424-427 */
+ unsigned char rsvd24[4]; /* Bytes 428-431 */
+ unsigned char flash_type_name[16]; /* Bytes 432-447 */
+ /* Firmware Run Time Information */
+ unsigned char rbld_rate; /* Byte 448 */
+ unsigned char bg_init_rate; /* Byte 449 */
+ unsigned char fg_init_rate; /* Byte 450 */
+ unsigned char cc_rate; /* Byte 451 */
+ unsigned char rsvd25[4]; /* Bytes 452-455 */
+ unsigned int max_dp; /* Bytes 456-459 */
+ unsigned int free_dp; /* Bytes 460-463 */
+ unsigned int max_iop; /* Bytes 464-467 */
+ unsigned int free_iop; /* Bytes 468-471 */
+ unsigned short max_combined_len; /* Bytes 472-473 */
+ unsigned short num_cfg_groups; /* Bytes 474-475 */
+ unsigned installation_abort_status:1; /* Byte 476 Bit 0 */
+ unsigned maint_mode_status:1; /* Byte 476 Bit 1 */
+ unsigned rsvd26:6; /* Byte 476 Bits 2-7 */
+ unsigned char rsvd27[6]; /* Bytes 477-511 */
+ unsigned char rsvd28[512]; /* Bytes 512-1023 */
+};
+
+/*
+ * DAC960 V2 Firmware Device State type.
+ */
+enum myrs_devstate {
+ MYRS_DEVICE_UNCONFIGURED = 0x00,
+ MYRS_DEVICE_ONLINE = 0x01,
+ MYRS_DEVICE_REBUILD = 0x03,
+ MYRS_DEVICE_MISSING = 0x04,
+ MYRS_DEVICE_SUSPECTED_CRITICAL = 0x05,
+ MYRS_DEVICE_OFFLINE = 0x08,
+ MYRS_DEVICE_CRITICAL = 0x09,
+ MYRS_DEVICE_SUSPECTED_DEAD = 0x0C,
+ MYRS_DEVICE_COMMANDED_OFFLINE = 0x10,
+ MYRS_DEVICE_STANDBY = 0x21,
+ MYRS_DEVICE_INVALID_STATE = 0xFF,
+} __packed;
+
+/*
+ * DAC960 V2 RAID Levels
+ */
+enum myrs_raid_level {
+ MYRS_RAID_LEVEL0 = 0x0, /* RAID 0 */
+ MYRS_RAID_LEVEL1 = 0x1, /* RAID 1 */
+ MYRS_RAID_LEVEL3 = 0x3, /* RAID 3 right asymmetric parity */
+ MYRS_RAID_LEVEL5 = 0x5, /* RAID 5 right asymmetric parity */
+ MYRS_RAID_LEVEL6 = 0x6, /* RAID 6 (Mylex RAID 6) */
+ MYRS_RAID_JBOD = 0x7, /* RAID 7 (JBOD) */
+ MYRS_RAID_NEWSPAN = 0x8, /* New Mylex SPAN */
+ MYRS_RAID_LEVEL3F = 0x9, /* RAID 3 fixed parity */
+ MYRS_RAID_LEVEL3L = 0xb, /* RAID 3 left symmetric parity */
+ MYRS_RAID_SPAN = 0xc, /* current spanning implementation */
+ MYRS_RAID_LEVEL5L = 0xd, /* RAID 5 left symmetric parity */
+ MYRS_RAID_LEVELE = 0xe, /* RAID E (concatenation) */
+ MYRS_RAID_PHYSICAL = 0xf, /* physical device */
+} __packed;
+
+enum myrs_stripe_size {
+ MYRS_STRIPE_SIZE_0 = 0x0, /* no stripe (RAID 1, RAID 7, etc) */
+ MYRS_STRIPE_SIZE_512B = 0x1,
+ MYRS_STRIPE_SIZE_1K = 0x2,
+ MYRS_STRIPE_SIZE_2K = 0x3,
+ MYRS_STRIPE_SIZE_4K = 0x4,
+ MYRS_STRIPE_SIZE_8K = 0x5,
+ MYRS_STRIPE_SIZE_16K = 0x6,
+ MYRS_STRIPE_SIZE_32K = 0x7,
+ MYRS_STRIPE_SIZE_64K = 0x8,
+ MYRS_STRIPE_SIZE_128K = 0x9,
+ MYRS_STRIPE_SIZE_256K = 0xa,
+ MYRS_STRIPE_SIZE_512K = 0xb,
+ MYRS_STRIPE_SIZE_1M = 0xc,
+} __packed;
+
+enum myrs_cacheline_size {
+ MYRS_CACHELINE_ZERO = 0x0, /* caching cannot be enabled */
+ MYRS_CACHELINE_512B = 0x1,
+ MYRS_CACHELINE_1K = 0x2,
+ MYRS_CACHELINE_2K = 0x3,
+ MYRS_CACHELINE_4K = 0x4,
+ MYRS_CACHELINE_8K = 0x5,
+ MYRS_CACHELINE_16K = 0x6,
+ MYRS_CACHELINE_32K = 0x7,
+ MYRS_CACHELINE_64K = 0x8,
+} __packed;
+
+/*
+ * DAC960 V2 Firmware Get Logical Device Info reply structure.
+ */
+struct myrs_ldev_info {
+ unsigned char ctlr; /* Byte 0 */
+ unsigned char channel; /* Byte 1 */
+ unsigned char target; /* Byte 2 */
+ unsigned char lun; /* Byte 3 */
+ enum myrs_devstate dev_state; /* Byte 4 */
+ unsigned char raid_level; /* Byte 5 */
+ enum myrs_stripe_size stripe_size; /* Byte 6 */
+ enum myrs_cacheline_size cacheline_size; /* Byte 7 */
+ struct {
+ enum {
+ MYRS_READCACHE_DISABLED = 0x0,
+ MYRS_READCACHE_ENABLED = 0x1,
+ MYRS_READAHEAD_ENABLED = 0x2,
+ MYRS_INTELLIGENT_READAHEAD_ENABLED = 0x3,
+ MYRS_READCACHE_LAST = 0x7,
+ } __packed rce:3; /* Byte 8 Bits 0-2 */
+ enum {
+ MYRS_WRITECACHE_DISABLED = 0x0,
+ MYRS_LOGICALDEVICE_RO = 0x1,
+ MYRS_WRITECACHE_ENABLED = 0x2,
+ MYRS_INTELLIGENT_WRITECACHE_ENABLED = 0x3,
+ MYRS_WRITECACHE_LAST = 0x7,
+ } __packed wce:3; /* Byte 8 Bits 3-5 */
+ unsigned rsvd1:1; /* Byte 8 Bit 6 */
+ unsigned ldev_init_done:1; /* Byte 8 Bit 7 */
+ } ldev_control; /* Byte 8 */
+ /* Logical Device Operations Status */
+ unsigned char cc_active:1; /* Byte 9 Bit 0 */
+ unsigned char rbld_active:1; /* Byte 9 Bit 1 */
+ unsigned char bg_init_active:1; /* Byte 9 Bit 2 */
+ unsigned char fg_init_active:1; /* Byte 9 Bit 3 */
+ unsigned char migration_active:1; /* Byte 9 Bit 4 */
+ unsigned char patrol_active:1; /* Byte 9 Bit 5 */
+ unsigned char rsvd2:2; /* Byte 9 Bits 6-7 */
+ unsigned char raid5_writeupdate; /* Byte 10 */
+ unsigned char raid5_algo; /* Byte 11 */
+ unsigned short ldev_num; /* Bytes 12-13 */
+ /* BIOS Info */
+ unsigned char bios_disabled:1; /* Byte 14 Bit 0 */
+ unsigned char cdrom_boot:1; /* Byte 14 Bit 1 */
+ unsigned char drv_coercion:1; /* Byte 14 Bit 2 */
+ unsigned char write_same_disabled:1; /* Byte 14 Bit 3 */
+ unsigned char hba_mode:1; /* Byte 14 Bit 4 */
+ enum {
+ MYRS_GEOMETRY_128_32 = 0x0,
+ MYRS_GEOMETRY_255_63 = 0x1,
+ MYRS_GEOMETRY_RSVD1 = 0x2,
+ MYRS_GEOMETRY_RSVD2 = 0x3
+ } __packed drv_geom:2; /* Byte 14 Bits 5-6 */
+ unsigned char super_ra_enabled:1; /* Byte 14 Bit 7 */
+ unsigned char rsvd3; /* Byte 15 */
+ /* Error Counters */
+ unsigned short soft_errs; /* Bytes 16-17 */
+ unsigned short cmds_failed; /* Bytes 18-19 */
+ unsigned short cmds_aborted; /* Bytes 20-21 */
+ unsigned short deferred_write_errs; /* Bytes 22-23 */
+ unsigned int rsvd4; /* Bytes 24-27 */
+ unsigned int rsvd5; /* Bytes 28-31 */
+ /* Device Size Information */
+ unsigned short rsvd6; /* Bytes 32-33 */
+ unsigned short devsize_bytes; /* Bytes 34-35 */
+ unsigned int orig_devsize; /* Bytes 36-39 */
+ unsigned int cfg_devsize; /* Bytes 40-43 */
+ unsigned int rsvd7; /* Bytes 44-47 */
+ unsigned char ldev_name[32]; /* Bytes 48-79 */
+ unsigned char inquiry[36]; /* Bytes 80-115 */
+ unsigned char rsvd8[12]; /* Bytes 116-127 */
+ u64 last_read_lba; /* Bytes 128-135 */
+ u64 last_write_lba; /* Bytes 136-143 */
+ u64 cc_lba; /* Bytes 144-151 */
+ u64 rbld_lba; /* Bytes 152-159 */
+ u64 bg_init_lba; /* Bytes 160-167 */
+ u64 fg_init_lba; /* Bytes 168-175 */
+ u64 migration_lba; /* Bytes 176-183 */
+ u64 patrol_lba; /* Bytes 184-191 */
+ unsigned char rsvd9[64]; /* Bytes 192-255 */
+};
+
+/*
+ * DAC960 V2 Firmware Get Physical Device Info reply structure.
+ */
+struct myrs_pdev_info {
+ unsigned char rsvd1; /* Byte 0 */
+ unsigned char channel; /* Byte 1 */
+ unsigned char target; /* Byte 2 */
+ unsigned char lun; /* Byte 3 */
+ /* Configuration Status Bits */
+ unsigned char pdev_fault_tolerant:1; /* Byte 4 Bit 0 */
+ unsigned char pdev_connected:1; /* Byte 4 Bit 1 */
+ unsigned char pdev_local_to_ctlr:1; /* Byte 4 Bit 2 */
+ unsigned char rsvd2:5; /* Byte 4 Bits 3-7 */
+ /* Multiple Host/Controller Status Bits */
+ unsigned char remote_host_dead:1; /* Byte 5 Bit 0 */
+ unsigned char remove_ctlr_dead:1; /* Byte 5 Bit 1 */
+ unsigned char rsvd3:6; /* Byte 5 Bits 2-7 */
+ enum myrs_devstate dev_state; /* Byte 6 */
+ unsigned char nego_data_width; /* Byte 7 */
+ unsigned short nego_sync_rate; /* Bytes 8-9 */
+ /* Multiported Physical Device Information */
+ unsigned char num_ports; /* Byte 10 */
+ unsigned char drv_access_bitmap; /* Byte 11 */
+ unsigned int rsvd4; /* Bytes 12-15 */
+ unsigned char ip_address[16]; /* Bytes 16-31 */
+ unsigned short max_tags; /* Bytes 32-33 */
+ /* Physical Device Operations Status */
+ unsigned char cc_in_progress:1; /* Byte 34 Bit 0 */
+ unsigned char rbld_in_progress:1; /* Byte 34 Bit 1 */
+ unsigned char makecc_in_progress:1; /* Byte 34 Bit 2 */
+ unsigned char pdevinit_in_progress:1; /* Byte 34 Bit 3 */
+ unsigned char migration_in_progress:1; /* Byte 34 Bit 4 */
+ unsigned char patrol_in_progress:1; /* Byte 34 Bit 5 */
+ unsigned char rsvd5:2; /* Byte 34 Bits 6-7 */
+ unsigned char long_op_status; /* Byte 35 */
+ unsigned char parity_errs; /* Byte 36 */
+ unsigned char soft_errs; /* Byte 37 */
+ unsigned char hard_errs; /* Byte 38 */
+ unsigned char misc_errs; /* Byte 39 */
+ unsigned char cmd_timeouts; /* Byte 40 */
+ unsigned char retries; /* Byte 41 */
+ unsigned char aborts; /* Byte 42 */
+ unsigned char pred_failures; /* Byte 43 */
+ unsigned int rsvd6; /* Bytes 44-47 */
+ unsigned short rsvd7; /* Bytes 48-49 */
+ unsigned short devsize_bytes; /* Bytes 50-51 */
+ unsigned int orig_devsize; /* Bytes 52-55 */
+ unsigned int cfg_devsize; /* Bytes 56-59 */
+ unsigned int rsvd8; /* Bytes 60-63 */
+ unsigned char pdev_name[16]; /* Bytes 64-79 */
+ unsigned char rsvd9[16]; /* Bytes 80-95 */
+ unsigned char rsvd10[32]; /* Bytes 96-127 */
+ unsigned char inquiry[36]; /* Bytes 128-163 */
+ unsigned char rsvd11[20]; /* Bytes 164-183 */
+ unsigned char rsvd12[8]; /* Bytes 184-191 */
+ u64 last_read_lba; /* Bytes 192-199 */
+ u64 last_write_lba; /* Bytes 200-207 */
+ u64 cc_lba; /* Bytes 208-215 */
+ u64 rbld_lba; /* Bytes 216-223 */
+ u64 makecc_lba; /* Bytes 224-231 */
+ u64 devinit_lba; /* Bytes 232-239 */
+ u64 migration_lba; /* Bytes 240-247 */
+ u64 patrol_lba; /* Bytes 248-255 */
+ unsigned char rsvd13[256]; /* Bytes 256-511 */
+};
+
+/*
+ * DAC960 V2 Firmware Health Status Buffer structure.
+ */
+struct myrs_fwstat {
+ unsigned int uptime_usecs; /* Bytes 0-3 */
+ unsigned int uptime_msecs; /* Bytes 4-7 */
+ unsigned int seconds; /* Bytes 8-11 */
+ unsigned char rsvd1[4]; /* Bytes 12-15 */
+ unsigned int epoch; /* Bytes 16-19 */
+ unsigned char rsvd2[4]; /* Bytes 20-23 */
+ unsigned int dbg_msgbuf_idx; /* Bytes 24-27 */
+ unsigned int coded_msgbuf_idx; /* Bytes 28-31 */
+ unsigned int cur_timetrace_page; /* Bytes 32-35 */
+ unsigned int cur_prof_page; /* Bytes 36-39 */
+ unsigned int next_evseq; /* Bytes 40-43 */
+ unsigned char rsvd3[4]; /* Bytes 44-47 */
+ unsigned char rsvd4[16]; /* Bytes 48-63 */
+ unsigned char rsvd5[64]; /* Bytes 64-127 */
+};
+
+/*
+ * DAC960 V2 Firmware Get Event reply structure.
+ */
+struct myrs_event {
+ unsigned int ev_seq; /* Bytes 0-3 */
+ unsigned int ev_time; /* Bytes 4-7 */
+ unsigned int ev_code; /* Bytes 8-11 */
+ unsigned char rsvd1; /* Byte 12 */
+ unsigned char channel; /* Byte 13 */
+ unsigned char target; /* Byte 14 */
+ unsigned char lun; /* Byte 15 */
+ unsigned int rsvd2; /* Bytes 16-19 */
+ unsigned int ev_parm; /* Bytes 20-23 */
+ unsigned char sense_data[40]; /* Bytes 24-63 */
+};
+
+/*
+ * DAC960 V2 Firmware Command Control Bits structure.
+ */
+struct myrs_cmd_ctrl {
+ unsigned char fua:1; /* Byte 0 Bit 0 */
+ unsigned char disable_pgout:1; /* Byte 0 Bit 1 */
+ unsigned char rsvd1:1; /* Byte 0 Bit 2 */
+ unsigned char add_sge_mem:1; /* Byte 0 Bit 3 */
+ unsigned char dma_ctrl_to_host:1; /* Byte 0 Bit 4 */
+ unsigned char rsvd2:1; /* Byte 0 Bit 5 */
+ unsigned char no_autosense:1; /* Byte 0 Bit 6 */
+ unsigned char disc_prohibited:1; /* Byte 0 Bit 7 */
+};
+
+/*
+ * DAC960 V2 Firmware Command Timeout structure.
+ */
+struct myrs_cmd_tmo {
+ unsigned char tmo_val:6; /* Byte 0 Bits 0-5 */
+ enum {
+ MYRS_TMO_SCALE_SECONDS = 0,
+ MYRS_TMO_SCALE_MINUTES = 1,
+ MYRS_TMO_SCALE_HOURS = 2,
+ MYRS_TMO_SCALE_RESERVED = 3
+ } __packed tmo_scale:2; /* Byte 0 Bits 6-7 */
+};
+
+/*
+ * DAC960 V2 Firmware Physical Device structure.
+ */
+struct myrs_pdev {
+ unsigned char lun; /* Byte 0 */
+ unsigned char target; /* Byte 1 */
+ unsigned char channel:3; /* Byte 2 Bits 0-2 */
+ unsigned char ctlr:5; /* Byte 2 Bits 3-7 */
+} __packed;
+
+/*
+ * DAC960 V2 Firmware Logical Device structure.
+ */
+struct myrs_ldev {
+ unsigned short ldev_num; /* Bytes 0-1 */
+ unsigned char rsvd:3; /* Byte 2 Bits 0-2 */
+ unsigned char ctlr:5; /* Byte 2 Bits 3-7 */
+} __packed;
+
+/*
+ * DAC960 V2 Firmware Operation Device type.
+ */
+enum myrs_opdev {
+ MYRS_PHYSICAL_DEVICE = 0x00,
+ MYRS_RAID_DEVICE = 0x01,
+ MYRS_PHYSICAL_CHANNEL = 0x02,
+ MYRS_RAID_CHANNEL = 0x03,
+ MYRS_PHYSICAL_CONTROLLER = 0x04,
+ MYRS_RAID_CONTROLLER = 0x05,
+ MYRS_CONFIGURATION_GROUP = 0x10,
+ MYRS_ENCLOSURE = 0x11,
+} __packed;
+
+/*
+ * DAC960 V2 Firmware Translate Physical To Logical Device structure.
+ */
+struct myrs_devmap {
+ unsigned short ldev_num; /* Bytes 0-1 */
+ unsigned short rsvd; /* Bytes 2-3 */
+ unsigned char prev_boot_ctlr; /* Byte 4 */
+ unsigned char prev_boot_channel; /* Byte 5 */
+ unsigned char prev_boot_target; /* Byte 6 */
+ unsigned char prev_boot_lun; /* Byte 7 */
+};
+
+/*
+ * DAC960 V2 Firmware Scatter/Gather List Entry structure.
+ */
+struct myrs_sge {
+ u64 sge_addr; /* Bytes 0-7 */
+ u64 sge_count; /* Bytes 8-15 */
+};
+
+/*
+ * DAC960 V2 Firmware Data Transfer Memory Address structure.
+ */
+union myrs_sgl {
+ struct myrs_sge sge[2]; /* Bytes 0-31 */
+ struct {
+ unsigned short sge0_len; /* Bytes 0-1 */
+ unsigned short sge1_len; /* Bytes 2-3 */
+ unsigned short sge2_len; /* Bytes 4-5 */
+ unsigned short rsvd; /* Bytes 6-7 */
+ u64 sge0_addr; /* Bytes 8-15 */
+ u64 sge1_addr; /* Bytes 16-23 */
+ u64 sge2_addr; /* Bytes 24-31 */
+ } ext;
+};
+
+/*
+ * 64 Byte DAC960 V2 Firmware Command Mailbox structure.
+ */
+union myrs_cmd_mbox {
+ unsigned int words[16]; /* Words 0-15 */
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ unsigned int rsvd1:24; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ unsigned char rsvd2[10]; /* Bytes 22-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } common;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size; /* Bytes 4-7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ struct myrs_pdev pdev; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ unsigned char cdb_len; /* Byte 21 */
+ unsigned char cdb[10]; /* Bytes 22-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } SCSI_10;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size; /* Bytes 4-7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ struct myrs_pdev pdev; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ unsigned char cdb_len; /* Byte 21 */
+ unsigned short rsvd; /* Bytes 22-23 */
+ u64 cdb_addr; /* Bytes 24-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } SCSI_255;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ unsigned short rsvd1; /* Bytes 16-17 */
+ unsigned char ctlr_num; /* Byte 18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ unsigned char rsvd2[10]; /* Bytes 22-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } ctlr_info;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ struct myrs_ldev ldev; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ unsigned char rsvd[10]; /* Bytes 22-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } ldev_info;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ struct myrs_pdev pdev; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ unsigned char rsvd[10]; /* Bytes 22-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } pdev_info;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ unsigned short evnum_upper; /* Bytes 16-17 */
+ unsigned char ctlr_num; /* Byte 18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ unsigned short evnum_lower; /* Bytes 22-23 */
+ unsigned char rsvd[8]; /* Bytes 24-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } get_event;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ union {
+ struct myrs_ldev ldev; /* Bytes 16-18 */
+ struct myrs_pdev pdev; /* Bytes 16-18 */
+ };
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ enum myrs_devstate state; /* Byte 22 */
+ unsigned char rsvd[9]; /* Bytes 23-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } set_devstate;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ struct myrs_ldev ldev; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ unsigned char restore_consistency:1; /* Byte 22 Bit 0 */
+ unsigned char initialized_area_only:1; /* Byte 22 Bit 1 */
+ unsigned char rsvd1:6; /* Byte 22 Bits 2-7 */
+ unsigned char rsvd2[9]; /* Bytes 23-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } cc;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ unsigned char first_cmd_mbox_size_kb; /* Byte 4 */
+ unsigned char first_stat_mbox_size_kb; /* Byte 5 */
+ unsigned char second_cmd_mbox_size_kb; /* Byte 6 */
+ unsigned char second_stat_mbox_size_kb; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ unsigned int rsvd1:24; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ unsigned char fwstat_buf_size_kb; /* Byte 22 */
+ unsigned char rsvd2; /* Byte 23 */
+ u64 fwstat_buf_addr; /* Bytes 24-31 */
+ u64 first_cmd_mbox_addr; /* Bytes 32-39 */
+ u64 first_stat_mbox_addr; /* Bytes 40-47 */
+ u64 second_cmd_mbox_addr; /* Bytes 48-55 */
+ u64 second_stat_mbox_addr; /* Bytes 56-63 */
+ } set_mbox;
+ struct {
+ unsigned short id; /* Bytes 0-1 */
+ enum myrs_cmd_opcode opcode; /* Byte 2 */
+ struct myrs_cmd_ctrl control; /* Byte 3 */
+ u32 dma_size:24; /* Bytes 4-6 */
+ unsigned char dma_num; /* Byte 7 */
+ u64 sense_addr; /* Bytes 8-15 */
+ struct myrs_pdev pdev; /* Bytes 16-18 */
+ struct myrs_cmd_tmo tmo; /* Byte 19 */
+ unsigned char sense_len; /* Byte 20 */
+ enum myrs_ioctl_opcode ioctl_opcode; /* Byte 21 */
+ enum myrs_opdev opdev; /* Byte 22 */
+ unsigned char rsvd[9]; /* Bytes 23-31 */
+ union myrs_sgl dma_addr; /* Bytes 32-63 */
+ } dev_op;
+};
+
+/*
+ * DAC960 V2 Firmware Controller Status Mailbox structure.
+ */
+struct myrs_stat_mbox {
+ unsigned short id; /* Bytes 0-1 */
+ unsigned char status; /* Byte 2 */
+ unsigned char sense_len; /* Byte 3 */
+ int residual; /* Bytes 4-7 */
+};
+
+struct myrs_cmdblk {
+ union myrs_cmd_mbox mbox;
+ unsigned char status;
+ unsigned char sense_len;
+ int residual;
+ struct completion *complete;
+ struct myrs_sge *sgl;
+ dma_addr_t sgl_addr;
+ unsigned char *dcdb;
+ dma_addr_t dcdb_dma;
+ unsigned char *sense;
+ dma_addr_t sense_addr;
+};
+
+/*
+ * DAC960 Driver Controller structure.
+ */
+struct myrs_hba {
+ void __iomem *io_base;
+ void __iomem *mmio_base;
+ phys_addr_t io_addr;
+ phys_addr_t pci_addr;
+ unsigned int irq;
+
+ unsigned char model_name[28];
+ unsigned char fw_version[12];
+
+ struct Scsi_Host *host;
+ struct pci_dev *pdev;
+
+ unsigned int epoch;
+ unsigned int next_evseq;
+ /* Monitor flags */
+ bool needs_update;
+ bool disable_enc_msg;
+
+ struct workqueue_struct *work_q;
+ struct delayed_work monitor_work;
+ unsigned long primary_monitor_time;
+ unsigned long secondary_monitor_time;
+
+ spinlock_t queue_lock;
+
+ struct dma_pool *sg_pool;
+ struct dma_pool *sense_pool;
+ struct dma_pool *dcdb_pool;
+
+ void (*write_cmd_mbox)(union myrs_cmd_mbox *next_mbox,
+ union myrs_cmd_mbox *cmd_mbox);
+ void (*get_cmd_mbox)(void __iomem *base);
+ void (*disable_intr)(void __iomem *base);
+ void (*reset)(void __iomem *base);
+
+ dma_addr_t cmd_mbox_addr;
+ size_t cmd_mbox_size;
+ union myrs_cmd_mbox *first_cmd_mbox;
+ union myrs_cmd_mbox *last_cmd_mbox;
+ union myrs_cmd_mbox *next_cmd_mbox;
+ union myrs_cmd_mbox *prev_cmd_mbox1;
+ union myrs_cmd_mbox *prev_cmd_mbox2;
+
+ dma_addr_t stat_mbox_addr;
+ size_t stat_mbox_size;
+ struct myrs_stat_mbox *first_stat_mbox;
+ struct myrs_stat_mbox *last_stat_mbox;
+ struct myrs_stat_mbox *next_stat_mbox;
+
+ struct myrs_cmdblk dcmd_blk;
+ struct myrs_cmdblk mcmd_blk;
+ struct mutex dcmd_mutex;
+
+ struct myrs_fwstat *fwstat_buf;
+ dma_addr_t fwstat_addr;
+
+ struct myrs_ctlr_info *ctlr_info;
+ struct mutex cinfo_mutex;
+
+ struct myrs_event *event_buf;
+};
+
+typedef unsigned char (*enable_mbox_t)(void __iomem *base, dma_addr_t addr);
+typedef int (*myrs_hwinit_t)(struct pci_dev *pdev,
+ struct myrs_hba *c, void __iomem *base);
+
+struct myrs_privdata {
+ myrs_hwinit_t hw_init;
+ irq_handler_t irq_handler;
+ unsigned int mmio_size;
+};
+
+/*
+ * DAC960 GEM Series Controller Interface Register Offsets.
+ */
+
+#define DAC960_GEM_mmio_size 0x600
+
+enum DAC960_GEM_reg_offset {
+ DAC960_GEM_IDB_READ_OFFSET = 0x214,
+ DAC960_GEM_IDB_CLEAR_OFFSET = 0x218,
+ DAC960_GEM_ODB_READ_OFFSET = 0x224,
+ DAC960_GEM_ODB_CLEAR_OFFSET = 0x228,
+ DAC960_GEM_IRQSTS_OFFSET = 0x208,
+ DAC960_GEM_IRQMASK_READ_OFFSET = 0x22C,
+ DAC960_GEM_IRQMASK_CLEAR_OFFSET = 0x230,
+ DAC960_GEM_CMDMBX_OFFSET = 0x510,
+ DAC960_GEM_CMDSTS_OFFSET = 0x518,
+ DAC960_GEM_ERRSTS_READ_OFFSET = 0x224,
+ DAC960_GEM_ERRSTS_CLEAR_OFFSET = 0x228,
+};
+
+/*
+ * DAC960 GEM Series Inbound Door Bell Register.
+ */
+#define DAC960_GEM_IDB_HWMBOX_NEW_CMD 0x01
+#define DAC960_GEM_IDB_HWMBOX_ACK_STS 0x02
+#define DAC960_GEM_IDB_GEN_IRQ 0x04
+#define DAC960_GEM_IDB_CTRL_RESET 0x08
+#define DAC960_GEM_IDB_MMBOX_NEW_CMD 0x10
+
+#define DAC960_GEM_IDB_HWMBOX_FULL 0x01
+#define DAC960_GEM_IDB_INIT_IN_PROGRESS 0x02
+
+/*
+ * DAC960 GEM Series Outbound Door Bell Register.
+ */
+#define DAC960_GEM_ODB_HWMBOX_ACK_IRQ 0x01
+#define DAC960_GEM_ODB_MMBOX_ACK_IRQ 0x02
+#define DAC960_GEM_ODB_HWMBOX_STS_AVAIL 0x01
+#define DAC960_GEM_ODB_MMBOX_STS_AVAIL 0x02
+
+/*
+ * DAC960 GEM Series Interrupt Mask Register.
+ */
+#define DAC960_GEM_IRQMASK_HWMBOX_IRQ 0x01
+#define DAC960_GEM_IRQMASK_MMBOX_IRQ 0x02
+
+/*
+ * DAC960 GEM Series Error Status Register.
+ */
+#define DAC960_GEM_ERRSTS_PENDING 0x20
+
+/*
+ * dma_addr_writeql is provided to write dma_addr_t types
+ * to a 64-bit pci address space register. The controller
+ * will accept having the register written as two 32-bit
+ * values.
+ *
+ * In HIGHMEM kernels, dma_addr_t is a 64-bit value.
+ * without HIGHMEM, dma_addr_t is a 32-bit value.
+ *
+ * The compiler should always fix up the assignment
+ * to u.wq appropriately, depending upon the size of
+ * dma_addr_t.
+ */
+static inline
+void dma_addr_writeql(dma_addr_t addr, void __iomem *write_address)
+{
+ union {
+ u64 wq;
+ uint wl[2];
+ } u;
+
+ u.wq = addr;
+
+ writel(u.wl[0], write_address);
+ writel(u.wl[1], write_address + 4);
+}
+
+/*
+ * DAC960 BA Series Controller Interface Register Offsets.
+ */
+
+#define DAC960_BA_mmio_size 0x80
+
+enum DAC960_BA_reg_offset {
+ DAC960_BA_IRQSTS_OFFSET = 0x30,
+ DAC960_BA_IRQMASK_OFFSET = 0x34,
+ DAC960_BA_CMDMBX_OFFSET = 0x50,
+ DAC960_BA_CMDSTS_OFFSET = 0x58,
+ DAC960_BA_IDB_OFFSET = 0x60,
+ DAC960_BA_ODB_OFFSET = 0x61,
+ DAC960_BA_ERRSTS_OFFSET = 0x63,
+};
+
+/*
+ * DAC960 BA Series Inbound Door Bell Register.
+ */
+#define DAC960_BA_IDB_HWMBOX_NEW_CMD 0x01
+#define DAC960_BA_IDB_HWMBOX_ACK_STS 0x02
+#define DAC960_BA_IDB_GEN_IRQ 0x04
+#define DAC960_BA_IDB_CTRL_RESET 0x08
+#define DAC960_BA_IDB_MMBOX_NEW_CMD 0x10
+
+#define DAC960_BA_IDB_HWMBOX_EMPTY 0x01
+#define DAC960_BA_IDB_INIT_DONE 0x02
+
+/*
+ * DAC960 BA Series Outbound Door Bell Register.
+ */
+#define DAC960_BA_ODB_HWMBOX_ACK_IRQ 0x01
+#define DAC960_BA_ODB_MMBOX_ACK_IRQ 0x02
+
+#define DAC960_BA_ODB_HWMBOX_STS_AVAIL 0x01
+#define DAC960_BA_ODB_MMBOX_STS_AVAIL 0x02
+
+/*
+ * DAC960 BA Series Interrupt Mask Register.
+ */
+#define DAC960_BA_IRQMASK_DISABLE_IRQ 0x04
+#define DAC960_BA_IRQMASK_DISABLEW_I2O 0x08
+
+/*
+ * DAC960 BA Series Error Status Register.
+ */
+#define DAC960_BA_ERRSTS_PENDING 0x04
+
+/*
+ * DAC960 LP Series Controller Interface Register Offsets.
+ */
+
+#define DAC960_LP_mmio_size 0x80
+
+enum DAC960_LP_reg_offset {
+ DAC960_LP_CMDMBX_OFFSET = 0x10,
+ DAC960_LP_CMDSTS_OFFSET = 0x18,
+ DAC960_LP_IDB_OFFSET = 0x20,
+ DAC960_LP_ODB_OFFSET = 0x2C,
+ DAC960_LP_ERRSTS_OFFSET = 0x2E,
+ DAC960_LP_IRQSTS_OFFSET = 0x30,
+ DAC960_LP_IRQMASK_OFFSET = 0x34,
+};
+
+/*
+ * DAC960 LP Series Inbound Door Bell Register.
+ */
+#define DAC960_LP_IDB_HWMBOX_NEW_CMD 0x01
+#define DAC960_LP_IDB_HWMBOX_ACK_STS 0x02
+#define DAC960_LP_IDB_GEN_IRQ 0x04
+#define DAC960_LP_IDB_CTRL_RESET 0x08
+#define DAC960_LP_IDB_MMBOX_NEW_CMD 0x10
+
+#define DAC960_LP_IDB_HWMBOX_FULL 0x01
+#define DAC960_LP_IDB_INIT_IN_PROGRESS 0x02
+
+/*
+ * DAC960 LP Series Outbound Door Bell Register.
+ */
+#define DAC960_LP_ODB_HWMBOX_ACK_IRQ 0x01
+#define DAC960_LP_ODB_MMBOX_ACK_IRQ 0x02
+
+#define DAC960_LP_ODB_HWMBOX_STS_AVAIL 0x01
+#define DAC960_LP_ODB_MMBOX_STS_AVAIL 0x02
+
+/*
+ * DAC960 LP Series Interrupt Mask Register.
+ */
+#define DAC960_LP_IRQMASK_DISABLE_IRQ 0x04
+
+/*
+ * DAC960 LP Series Error Status Register.
+ */
+#define DAC960_LP_ERRSTS_PENDING 0x04
+
+#endif /* _MYRS_H */
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 4b3b4755945c..34ba9b137789 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
** Device driver for the PCI-SCSI NCR538XX controller family.
**
** Copyright (C) 1994 Wolfgang Stanglmeier
**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
**
@@ -115,7 +103,6 @@
#include <asm/dma.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -161,6 +148,11 @@ static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
#define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
#endif
+/*
+ * Locally used status flag
+ */
+#define SAM_STAT_ILLEGAL 0xff
+
static inline struct list_head *ncr_list_pop(struct list_head *head)
{
if (!list_empty(head)) {
@@ -522,30 +514,29 @@ static m_addr_t __vtobus(m_bush_t bush, void *m)
* Deal with DMA mapping/unmapping.
*/
-/* To keep track of the dma mapping (sg/single) that has been set */
-#define __data_mapped SCp.phase
-#define __data_mapping SCp.have_data_in
-
static void __unmap_scsi_data(struct device *dev, struct scsi_cmnd *cmd)
{
- switch(cmd->__data_mapped) {
+ struct ncr_cmd_priv *cmd_priv = scsi_cmd_priv(cmd);
+
+ switch(cmd_priv->data_mapped) {
case 2:
scsi_dma_unmap(cmd);
break;
}
- cmd->__data_mapped = 0;
+ cmd_priv->data_mapped = 0;
}
static int __map_scsi_sg_data(struct device *dev, struct scsi_cmnd *cmd)
{
+ struct ncr_cmd_priv *cmd_priv = scsi_cmd_priv(cmd);
int use_sg;
use_sg = scsi_dma_map(cmd);
if (!use_sg)
return 0;
- cmd->__data_mapped = 2;
- cmd->__data_mapping = use_sg;
+ cmd_priv->data_mapped = 2;
+ cmd_priv->data_mapping = use_sg;
return use_sg;
}
@@ -1011,8 +1002,6 @@ typedef u32 tagmap_t;
** Other definitions
*/
-#define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
-
#define initverbose (driver_setup.verbose)
#define bootverbose (np->verbose)
@@ -1463,11 +1452,6 @@ struct head {
#define xerr_status phys.xerr_st
#define nego_status phys.nego_st
-#if 0
-#define sync_status phys.sync_st
-#define wide_status phys.wide_st
-#endif
-
/*==========================================================
**
** Declaration of structs: Data structure block
@@ -1616,7 +1600,7 @@ struct ncb {
spinlock_t smp_lock; /* Lock for SMP threading */
/*----------------------------------------------------------------
- ** Chip and controller indentification.
+ ** Chip and controller identification.
**----------------------------------------------------------------
*/
int unit; /* Unit number */
@@ -1735,7 +1719,7 @@ struct ncb {
** Miscellaneous configuration and status parameters.
**----------------------------------------------------------------
*/
- u_char disc; /* Diconnection allowed */
+ u_char disc; /* Disconnection allowed */
u_char scsi_mode; /* Current SCSI BUS mode */
u_char order; /* Tag order to use */
u_char verbose; /* Verbosity for this controller*/
@@ -1954,11 +1938,8 @@ static void ncr_start_next_ccb (struct ncb *np, struct lcb * lp, int maxn);
static void ncr_put_start_queue(struct ncb *np, struct ccb *cp);
static void insert_into_waiting_list(struct ncb *np, struct scsi_cmnd *cmd);
-static struct scsi_cmnd *retrieve_from_waiting_list(int to_remove, struct ncb *np, struct scsi_cmnd *cmd);
static void process_waiting_list(struct ncb *np, int sts);
-#define remove_from_waiting_list(np, cmd) \
- retrieve_from_waiting_list(1, (np), (cmd))
#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
@@ -1990,9 +1971,6 @@ static inline char *ncr_name (struct ncb *np)
#define RELOC_SOFTC 0x40000000
#define RELOC_LABEL 0x50000000
#define RELOC_REGISTER 0x60000000
-#if 0
-#define RELOC_KVAR 0x70000000
-#endif
#define RELOC_LABELH 0x80000000
#define RELOC_MASK 0xf0000000
@@ -2001,21 +1979,7 @@ static inline char *ncr_name (struct ncb *np)
#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label))
#define RADDR(label) (RELOC_REGISTER | REG(label))
#define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
-#if 0
-#define KVAR(which) (RELOC_KVAR | (which))
-#endif
-#if 0
-#define SCRIPT_KVAR_JIFFIES (0)
-#define SCRIPT_KVAR_FIRST SCRIPT_KVAR_JIFFIES
-#define SCRIPT_KVAR_LAST SCRIPT_KVAR_JIFFIES
-/*
- * Kernel variables referenced in the scripts.
- * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
- */
-static void *script_kvars[] __initdata =
- { (void *)&jiffies };
-#endif
static struct script script0 __initdata = {
/*--------------------------< START >-----------------------*/ {
@@ -2172,11 +2136,6 @@ static struct script script0 __initdata = {
SCR_COPY (1),
RADDR (scratcha),
NADDR (msgout),
-#if 0
- SCR_COPY (1),
- RADDR (scratcha),
- NADDR (msgin),
-#endif
/*
** Anticipate the COMMAND phase.
** This is the normal case for initial selection.
@@ -2216,7 +2175,7 @@ static struct script script0 __initdata = {
** Possible data corruption during Memory Write and Invalidate.
** This work-around resets the addressing logic prior to the
** start of the first MOVE of a DATA IN phase.
- ** (See Documentation/scsi/ncr53c8xx.txt for more information)
+ ** (See Documentation/scsi/ncr53c8xx.rst for more information)
*/
SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
20,
@@ -2443,7 +2402,7 @@ static struct script script0 __initdata = {
*/
SCR_FROM_REG (SS_REG),
0,
- SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
+ SCR_CALL ^ IFFALSE (DATA (SAM_STAT_GOOD)),
PADDRH (bad_status),
#ifndef SCSI_NCR_CCB_DONE_SUPPORT
@@ -2892,7 +2851,7 @@ static struct scripth scripth0 __initdata = {
8,
SCR_TO_REG (HS_REG),
0,
- SCR_LOAD_REG (SS_REG, S_GOOD),
+ SCR_LOAD_REG (SS_REG, SAM_STAT_GOOD),
0,
SCR_JUMP,
PADDR (cleanup_ok),
@@ -3354,15 +3313,15 @@ static struct scripth scripth0 __initdata = {
PADDRH (reset),
}/*-------------------------< BAD_STATUS >-----------------*/,{
/*
- ** If command resulted in either QUEUE FULL,
+ ** If command resulted in either TASK_SET FULL,
** CHECK CONDITION or COMMAND TERMINATED,
** call the C code.
*/
- SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+ SCR_INT ^ IFTRUE (DATA (SAM_STAT_TASK_SET_FULL)),
SIR_BAD_STATUS,
- SCR_INT ^ IFTRUE (DATA (S_CHECK_COND)),
+ SCR_INT ^ IFTRUE (DATA (SAM_STAT_CHECK_CONDITION)),
SIR_BAD_STATUS,
- SCR_INT ^ IFTRUE (DATA (S_TERMINATED)),
+ SCR_INT ^ IFTRUE (DATA (SAM_STAT_COMMAND_TERMINATED)),
SIR_BAD_STATUS,
SCR_RETURN,
0,
@@ -3653,7 +3612,7 @@ ncr_script_copy_and_bind (struct ncb *np, ncrcmd *src, ncrcmd *dst, int len)
new = old;
break;
}
- /* fall through */
+ fallthrough;
default:
panic("ncr_script_copy_and_bind: weird relocation %x\n", old);
break;
@@ -3923,11 +3882,14 @@ static void __init ncr_prepare_setting(struct ncb *np)
np->scsi_mode = SMODE_HVD;
break;
}
+ fallthrough;
case 3: /* SYMBIOS controllers report HVD through GPIO3 */
if (INB(nc_gpreg) & 0x08)
break;
+ fallthrough;
case 2: /* Set HVD unconditionally */
np->scsi_mode = SMODE_HVD;
+ fallthrough;
case 1: /* Trust previous settings for HVD */
if (np->sv_stest2 & 0x20)
np->scsi_mode = SMODE_HVD;
@@ -4040,7 +4002,7 @@ static inline void ncr_flush_done_cmds(struct scsi_cmnd *lcmd)
while (lcmd) {
cmd = lcmd;
lcmd = (struct scsi_cmnd *) cmd->host_scribble;
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
}
@@ -4171,8 +4133,8 @@ static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd)
**
**----------------------------------------------------
*/
- if (np->settle_time && cmd->request->timeout >= HZ) {
- u_long tlimit = jiffies + cmd->request->timeout - HZ;
+ if (np->settle_time && scsi_cmd_to_rq(cmd)->timeout >= HZ) {
+ u_long tlimit = jiffies + scsi_cmd_to_rq(cmd)->timeout - HZ;
if (time_after(np->settle_time, tlimit))
np->settle_time = tlimit;
}
@@ -4306,7 +4268,7 @@ static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd)
break;
cp->phys.header.wgoalp = cpu_to_scr(goalp);
cp->phys.header.wlastp = cpu_to_scr(lastp);
- /* fall through */
+ fallthrough;
case DMA_FROM_DEVICE:
goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
if (segments <= MAX_SCATTERL)
@@ -4381,14 +4343,10 @@ static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd)
*/
cp->actualquirks = 0;
cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
- cp->scsi_status = S_ILLEGAL;
+ cp->scsi_status = SAM_STAT_ILLEGAL;
cp->parity_status = 0;
cp->xerr_status = XE_OK;
-#if 0
- cp->sync_status = tp->sval;
- cp->wide_status = tp->wval;
-#endif
/*----------------------------------------------------
**
@@ -4560,12 +4518,8 @@ static void ncr_start_reset(struct ncb *np)
**
**==========================================================
*/
-static int ncr_reset_bus (struct ncb *np, struct scsi_cmnd *cmd, int sync_reset)
+static int ncr_reset_bus (struct ncb *np)
{
-/* struct scsi_device *device = cmd->device; */
- struct ccb *cp;
- int found;
-
/*
* Return immediately if reset is in progress.
*/
@@ -4580,24 +4534,6 @@ static int ncr_reset_bus (struct ncb *np, struct scsi_cmnd *cmd, int sync_reset)
*/
ncr_start_reset(np);
/*
- * First, look in the wakeup list
- */
- for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
- /*
- ** look for the ccb of this command.
- */
- if (cp->host_status == HS_IDLE) continue;
- if (cp->cmd == cmd) {
- found = 1;
- break;
- }
- }
-/*
- * Then, look in the waiting list
- */
- if (!found && retrieve_from_waiting_list(0, np, cmd))
- found = 1;
-/*
* Wake-up all awaiting commands with DID_RESET.
*/
reset_waiting_list(np);
@@ -4605,103 +4541,10 @@ static int ncr_reset_bus (struct ncb *np, struct scsi_cmnd *cmd, int sync_reset)
* Wake-up all pending commands with HS_RESET -> DID_RESET.
*/
ncr_wakeup(np, HS_RESET);
-/*
- * If the involved command was not in a driver queue, and the
- * scsi driver told us reset is synchronous, and the command is not
- * currently in the waiting list, complete it with DID_RESET status,
- * in order to keep it alive.
- */
- if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) {
- cmd->result = ScsiResult(DID_RESET, 0);
- ncr_queue_done_cmd(np, cmd);
- }
return SUCCESS;
}
-#if 0 /* unused and broken.. */
-/*==========================================================
-**
-**
-** Abort an SCSI command.
-** This is called from the generic SCSI driver.
-**
-**
-**==========================================================
-*/
-static int ncr_abort_command (struct ncb *np, struct scsi_cmnd *cmd)
-{
-/* struct scsi_device *device = cmd->device; */
- struct ccb *cp;
- int found;
- int retv;
-
-/*
- * First, look for the scsi command in the waiting list
- */
- if (remove_from_waiting_list(np, cmd)) {
- cmd->result = ScsiResult(DID_ABORT, 0);
- ncr_queue_done_cmd(np, cmd);
- return SCSI_ABORT_SUCCESS;
- }
-
-/*
- * Then, look in the wakeup list
- */
- for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
- /*
- ** look for the ccb of this command.
- */
- if (cp->host_status == HS_IDLE) continue;
- if (cp->cmd == cmd) {
- found = 1;
- break;
- }
- }
-
- if (!found) {
- return SCSI_ABORT_NOT_RUNNING;
- }
-
- if (np->settle_time) {
- return SCSI_ABORT_SNOOZE;
- }
-
- /*
- ** If the CCB is active, patch schedule jumps for the
- ** script to abort the command.
- */
-
- switch(cp->host_status) {
- case HS_BUSY:
- case HS_NEGOTIATE:
- printk ("%s: abort ccb=%p (cancel)\n", ncr_name (np), cp);
- cp->start.schedule.l_paddr =
- cpu_to_scr(NCB_SCRIPTH_PHYS (np, cancel));
- retv = SCSI_ABORT_PENDING;
- break;
- case HS_DISCONNECT:
- cp->restart.schedule.l_paddr =
- cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
- retv = SCSI_ABORT_PENDING;
- break;
- default:
- retv = SCSI_ABORT_NOT_RUNNING;
- break;
-
- }
-
- /*
- ** If there are no requests, the script
- ** processor will sleep on SEL_WAIT_RESEL.
- ** Let's wake it up, since it may have to work.
- */
- OUTB (nc_istat, SIGP);
-
- return retv;
-}
-#endif
-
static void ncr_detach(struct ncb *np)
{
struct ccb *cp;
@@ -4712,7 +4555,7 @@ static void ncr_detach(struct ncb *np)
char inst_name[16];
/* Local copy so we don't access np after freeing it! */
- strlcpy(inst_name, ncr_name(np), sizeof(inst_name));
+ strscpy(inst_name, ncr_name(np), sizeof(inst_name));
printk("%s: releasing host resources\n", ncr_name(np));
@@ -4905,7 +4748,8 @@ void ncr_complete (struct ncb *np, struct ccb *cp)
** Print out any error for debugging purpose.
*/
if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
- if (cp->host_status!=HS_COMPLETE || cp->scsi_status!=S_GOOD) {
+ if (cp->host_status != HS_COMPLETE ||
+ cp->scsi_status != SAM_STAT_GOOD) {
PRINT_ADDR(cmd, "ERROR: cmd=%x host_status=%x "
"scsi_status=%x\n", cmd->cmnd[0],
cp->host_status, cp->scsi_status);
@@ -4915,15 +4759,16 @@ void ncr_complete (struct ncb *np, struct ccb *cp)
/*
** Check the status.
*/
+ cmd->result = 0;
if ( (cp->host_status == HS_COMPLETE)
- && (cp->scsi_status == S_GOOD ||
- cp->scsi_status == S_COND_MET)) {
+ && (cp->scsi_status == SAM_STAT_GOOD ||
+ cp->scsi_status == SAM_STAT_CONDITION_MET)) {
/*
* All went well (GOOD status).
- * CONDITION MET status is returned on
+ * CONDITION MET status is returned on
* `Pre-Fetch' or `Search data' success.
*/
- cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+ set_status_byte(cmd, cp->scsi_status);
/*
** @RESID@
@@ -4954,11 +4799,11 @@ void ncr_complete (struct ncb *np, struct ccb *cp)
}
}
} else if ((cp->host_status == HS_COMPLETE)
- && (cp->scsi_status == S_CHECK_COND)) {
+ && (cp->scsi_status == SAM_STAT_CHECK_CONDITION)) {
/*
** Check condition code
*/
- cmd->result = ScsiResult(DID_OK, S_CHECK_COND);
+ set_status_byte(cmd, SAM_STAT_CHECK_CONDITION);
/*
** Copy back sense data to caller's buffer.
@@ -4975,20 +4820,20 @@ void ncr_complete (struct ncb *np, struct ccb *cp)
printk (".\n");
}
} else if ((cp->host_status == HS_COMPLETE)
- && (cp->scsi_status == S_CONFLICT)) {
+ && (cp->scsi_status == SAM_STAT_RESERVATION_CONFLICT)) {
/*
** Reservation Conflict condition code
*/
- cmd->result = ScsiResult(DID_OK, S_CONFLICT);
-
+ set_status_byte(cmd, SAM_STAT_RESERVATION_CONFLICT);
+
} else if ((cp->host_status == HS_COMPLETE)
- && (cp->scsi_status == S_BUSY ||
- cp->scsi_status == S_QUEUE_FULL)) {
+ && (cp->scsi_status == SAM_STAT_BUSY ||
+ cp->scsi_status == SAM_STAT_TASK_SET_FULL)) {
/*
** Target is busy.
*/
- cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+ set_status_byte(cmd, cp->scsi_status);
} else if ((cp->host_status == HS_SEL_TIMEOUT)
|| (cp->host_status == HS_TIMEOUT)) {
@@ -4996,21 +4841,24 @@ void ncr_complete (struct ncb *np, struct ccb *cp)
/*
** No response
*/
- cmd->result = ScsiResult(DID_TIME_OUT, cp->scsi_status);
+ set_status_byte(cmd, cp->scsi_status);
+ set_host_byte(cmd, DID_TIME_OUT);
} else if (cp->host_status == HS_RESET) {
/*
** SCSI bus reset
*/
- cmd->result = ScsiResult(DID_RESET, cp->scsi_status);
+ set_status_byte(cmd, cp->scsi_status);
+ set_host_byte(cmd, DID_RESET);
} else if (cp->host_status == HS_ABORTED) {
/*
** Transfer aborted
*/
- cmd->result = ScsiResult(DID_ABORT, cp->scsi_status);
+ set_status_byte(cmd, cp->scsi_status);
+ set_host_byte(cmd, DID_ABORT);
} else {
@@ -5020,7 +4868,8 @@ void ncr_complete (struct ncb *np, struct ccb *cp)
PRINT_ADDR(cmd, "COMMAND FAILED (%x %x) @%p.\n",
cp->host_status, cp->scsi_status, cp);
- cmd->result = ScsiResult(DID_ERROR, cp->scsi_status);
+ set_status_byte(cmd, cp->scsi_status);
+ set_host_byte(cmd, DID_ERROR);
}
/*
@@ -5036,10 +4885,10 @@ void ncr_complete (struct ncb *np, struct ccb *cp)
if (cp->host_status==HS_COMPLETE) {
switch (cp->scsi_status) {
- case S_GOOD:
+ case SAM_STAT_GOOD:
printk (" GOOD");
break;
- case S_CHECK_COND:
+ case SAM_STAT_CHECK_CONDITION:
printk (" SENSE:");
p = (u_char*) &cmd->sense_buffer;
for (i=0; i<14; i++)
@@ -5454,27 +5303,6 @@ static void ncr_getsync(struct ncb *np, u_char sfac, u_char *fakp, u_char *scntl
*/
fak = (kpc - 1) / div_10M[div] + 1;
-#if 0 /* This optimization does not seem very useful */
-
- per = (fak * div_10M[div]) / clk;
-
- /*
- ** Why not to try the immediate lower divisor and to choose
- ** the one that allows the fastest output speed ?
- ** We don't want input speed too much greater than output speed.
- */
- if (div >= 1 && fak < 8) {
- u_long fak2, per2;
- fak2 = (kpc - 1) / div_10M[div-1] + 1;
- per2 = (fak2 * div_10M[div-1]) / clk;
- if (per2 < per && fak2 <= 8) {
- fak = fak2;
- per = per2;
- --div;
- }
- }
-#endif
-
if (fak < 4) fak = 4; /* Should never happen, too bad ... */
/*
@@ -5512,10 +5340,6 @@ static void ncr_set_sync_wide_status (struct ncb *np, u_char target)
for (cp = np->ccb; cp; cp = cp->link_ccb) {
if (!cp->cmd) continue;
if (scmd_id(cp->cmd) != target) continue;
-#if 0
- cp->sync_status = tp->sval;
- cp->wide_status = tp->wval;
-#endif
cp->phys.select.sel_scntl3 = tp->wval;
cp->phys.select.sel_sxfer = tp->sval;
}
@@ -6574,7 +6398,7 @@ static void ncr_sir_to_redo(struct ncb *np, int num, struct ccb *cp)
switch(s_status) {
default: /* Just for safety, should never happen */
- case S_QUEUE_FULL:
+ case SAM_STAT_TASK_SET_FULL:
/*
** Decrease number of tags to the number of
** disconnected commands.
@@ -6598,15 +6422,15 @@ static void ncr_sir_to_redo(struct ncb *np, int num, struct ccb *cp)
*/
cp->phys.header.savep = cp->startp;
cp->host_status = HS_BUSY;
- cp->scsi_status = S_ILLEGAL;
+ cp->scsi_status = SAM_STAT_ILLEGAL;
ncr_put_start_queue(np, cp);
if (disc_cnt)
INB (nc_ctest2); /* Clear SIGP */
OUTL_DSP (NCB_SCRIPT_PHYS (np, reselect));
return;
- case S_TERMINATED:
- case S_CHECK_COND:
+ case SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
/*
** If we were requesting sense, give up.
*/
@@ -6634,7 +6458,7 @@ static void ncr_sir_to_redo(struct ncb *np, int num, struct ccb *cp)
** patch requested size into sense command
*/
cp->sensecmd[0] = 0x03;
- cp->sensecmd[1] = cmd->device->lun << 5;
+ cp->sensecmd[1] = (cmd->device->lun & 0x7) << 5;
cp->sensecmd[4] = sizeof(cp->sense_buf);
/*
@@ -6656,7 +6480,7 @@ static void ncr_sir_to_redo(struct ncb *np, int num, struct ccb *cp)
cp->phys.header.wlastp = startp;
cp->host_status = HS_BUSY;
- cp->scsi_status = S_ILLEGAL;
+ cp->scsi_status = SAM_STAT_ILLEGAL;
cp->auto_sense = s_status;
cp->start.schedule.l_paddr =
@@ -6727,6 +6551,7 @@ void ncr_int_sir (struct ncb *np)
OUTL_DSP (scr_to_cpu(tp->lp[0]->jump_ccb[0]));
return;
}
+ fallthrough;
case SIR_RESEL_BAD_TARGET: /* Will send a TARGET RESET message */
case SIR_RESEL_BAD_LUN: /* Will send a TARGET RESET message */
case SIR_RESEL_BAD_I_T_L_Q: /* Will send an ABORT TAG message */
@@ -6834,7 +6659,7 @@ void ncr_int_sir (struct ncb *np)
*/
OUTB (HS_PRT, HS_BUSY);
- /* fall through */
+ fallthrough;
case SIR_NEGO_PROTO:
/*-------------------------------------------------------
@@ -7961,7 +7786,7 @@ static void __init ncr_getclock (struct ncb *np, int mult)
/*===================== LINUX ENTRY POINTS SECTION ==========================*/
-static int ncr53c8xx_slave_alloc(struct scsi_device *device)
+static int ncr53c8xx_sdev_init(struct scsi_device *device)
{
struct Scsi_Host *host = device->host;
struct ncb *np = ((struct host_data *) host->hostdata)->ncb;
@@ -7971,7 +7796,8 @@ static int ncr53c8xx_slave_alloc(struct scsi_device *device)
return 0;
}
-static int ncr53c8xx_slave_configure(struct scsi_device *device)
+static int ncr53c8xx_sdev_configure(struct scsi_device *device,
+ struct queue_limits *lim)
{
struct Scsi_Host *host = device->host;
struct ncb *np = ((struct host_data *) host->hostdata)->ncb;
@@ -7998,10 +7824,7 @@ static int ncr53c8xx_slave_configure(struct scsi_device *device)
if (depth_to_use > MAX_TAGS)
depth_to_use = MAX_TAGS;
- scsi_adjust_queue_depth(device,
- (device->tagged_supported ?
- MSG_SIMPLE_TAG : 0),
- depth_to_use);
+ scsi_change_queue_depth(device, depth_to_use);
/*
** Since the queue depth is not tunable under Linux,
@@ -8029,8 +7852,10 @@ static int ncr53c8xx_slave_configure(struct scsi_device *device)
return 0;
}
-static int ncr53c8xx_queue_command_lck (struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+static int ncr53c8xx_queue_command_lck(struct scsi_cmnd *cmd)
{
+ struct ncr_cmd_priv *cmd_priv = scsi_cmd_priv(cmd);
+ void (*done)(struct scsi_cmnd *) = scsi_done;
struct ncb *np = ((struct host_data *) cmd->device->host->hostdata)->ncb;
unsigned long flags;
int sts;
@@ -8039,15 +7864,14 @@ static int ncr53c8xx_queue_command_lck (struct scsi_cmnd *cmd, void (*done)(stru
printk("ncr53c8xx_queue_command\n");
#endif
- cmd->scsi_done = done;
cmd->host_scribble = NULL;
- cmd->__data_mapped = 0;
- cmd->__data_mapping = 0;
+ cmd_priv->data_mapped = 0;
+ cmd_priv->data_mapping = 0;
spin_lock_irqsave(&np->smp_lock, flags);
if ((sts = ncr_queue_command(np, cmd)) != DID_OK) {
- cmd->result = ScsiResult(sts, 0);
+ set_host_byte(cmd, sts);
#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : command not queued - result=%d\n", sts);
#endif
@@ -8097,9 +7921,9 @@ irqreturn_t ncr53c8xx_intr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void ncr53c8xx_timeout(unsigned long npref)
+static void ncr53c8xx_timeout(struct timer_list *t)
{
- struct ncb *np = (struct ncb *) npref;
+ struct ncb *np = timer_container_of(np, t, timer);
unsigned long flags;
struct scsi_cmnd *done_list;
@@ -8128,7 +7952,7 @@ static int ncr53c8xx_bus_reset(struct scsi_cmnd *cmd)
*/
spin_lock_irqsave(&np->smp_lock, flags);
- sts = ncr_reset_bus(np, cmd, 1);
+ sts = ncr_reset_bus(np);
done_list = np->done_list;
np->done_list = NULL;
@@ -8139,30 +7963,6 @@ static int ncr53c8xx_bus_reset(struct scsi_cmnd *cmd)
return sts;
}
-#if 0 /* unused and broken */
-static int ncr53c8xx_abort(struct scsi_cmnd *cmd)
-{
- struct ncb *np = ((struct host_data *) cmd->device->host->hostdata)->ncb;
- int sts;
- unsigned long flags;
- struct scsi_cmnd *done_list;
-
- printk("ncr53c8xx_abort\n");
-
- NCR_LOCK_NCB(np, flags);
-
- sts = ncr_abort_command(np, cmd);
-out:
- done_list = np->done_list;
- np->done_list = NULL;
- NCR_UNLOCK_NCB(np, flags);
-
- ncr_flush_done_cmds(done_list);
-
- return sts;
-}
-#endif
-
/*
** Scsi command waiting list management.
@@ -8195,26 +7995,6 @@ static void insert_into_waiting_list(struct ncb *np, struct scsi_cmnd *cmd)
}
}
-static struct scsi_cmnd *retrieve_from_waiting_list(int to_remove, struct ncb *np, struct scsi_cmnd *cmd)
-{
- struct scsi_cmnd **pcmd = &np->waiting_list;
-
- while (*pcmd) {
- if (cmd == *pcmd) {
- if (to_remove) {
- *pcmd = (struct scsi_cmnd *) cmd->next_wcmd;
- cmd->next_wcmd = NULL;
- }
-#ifdef DEBUG_WAITING_LIST
- printk("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
-#endif
- return cmd;
- }
- pcmd = (struct scsi_cmnd **) &(*pcmd)->next_wcmd;
- }
- return NULL;
-}
-
static void process_waiting_list(struct ncb *np, int sts)
{
struct scsi_cmnd *waiting_list, *wcmd;
@@ -8238,7 +8018,7 @@ static void process_waiting_list(struct ncb *np, int sts)
#ifdef DEBUG_WAITING_LIST
printk("%s: cmd %lx done forced sts=%d\n", ncr_name(np), (u_long) wcmd, sts);
#endif
- wcmd->result = ScsiResult(sts, 0);
+ set_host_byte(wcmd, sts);
ncr_queue_done_cmd(np, wcmd);
}
}
@@ -8260,11 +8040,13 @@ static struct device_attribute ncr53c8xx_revision_attr = {
.show = show_ncr53c8xx_revision,
};
-static struct device_attribute *ncr53c8xx_host_attrs[] = {
- &ncr53c8xx_revision_attr,
+static struct attribute *ncr53c8xx_host_attrs[] = {
+ &ncr53c8xx_revision_attr.attr,
NULL
};
+ATTRIBUTE_GROUPS(ncr53c8xx_host);
+
/*==========================================================
**
** Boot command line.
@@ -8304,20 +8086,21 @@ struct Scsi_Host * __init ncr_attach(struct scsi_host_template *tpnt,
u_long flags = 0;
int i;
+ WARN_ON_ONCE(tpnt->cmd_size < sizeof(struct ncr_cmd_priv));
+
if (!tpnt->name)
tpnt->name = SCSI_NCR_DRIVER_NAME;
- if (!tpnt->shost_attrs)
- tpnt->shost_attrs = ncr53c8xx_host_attrs;
+ if (!tpnt->shost_groups)
+ tpnt->shost_groups = ncr53c8xx_host_groups;
tpnt->queuecommand = ncr53c8xx_queue_command;
- tpnt->slave_configure = ncr53c8xx_slave_configure;
- tpnt->slave_alloc = ncr53c8xx_slave_alloc;
+ tpnt->sdev_configure = ncr53c8xx_sdev_configure;
+ tpnt->sdev_init = ncr53c8xx_sdev_init;
tpnt->eh_bus_reset_handler = ncr53c8xx_bus_reset;
tpnt->can_queue = SCSI_NCR_CAN_QUEUE;
tpnt->this_id = 7;
tpnt->sg_tablesize = SCSI_NCR_SG_TABLESIZE;
tpnt->cmd_per_lun = SCSI_NCR_CMD_PER_LUN;
- tpnt->use_clustering = ENABLE_CLUSTERING;
if (device->differential)
driver_setup.diff_support = device->differential;
@@ -8361,9 +8144,7 @@ struct Scsi_Host * __init ncr_attach(struct scsi_host_template *tpnt,
if (!np->scripth0)
goto attach_error;
- init_timer(&np->timer);
- np->timer.data = (unsigned long) np;
- np->timer.function = ncr53c8xx_timeout;
+ timer_setup(&np->timer, ncr53c8xx_timeout, 0);
/* Try to map the controller chip to virtual and physical memory. */
diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h
index 0e008dacf679..be38c902859e 100644
--- a/drivers/scsi/ncr53c8xx.h
+++ b/drivers/scsi/ncr53c8xx.h
@@ -1,22 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/******************************************************************************
** Device driver for the PCI-SCSI NCR538XX controller family.
**
** Copyright (C) 1994 Wolfgang Stanglmeier
** Copyright (C) 1998-2001 Gerard Roudier <groudier@free.fr>
**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
**
@@ -264,11 +252,7 @@
#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER)
#define SCSI_NCR_TIMER_INTERVAL (HZ)
-#if 1 /* defined CONFIG_SCSI_MULTI_LUN */
#define SCSI_NCR_MAX_LUN (16)
-#else
-#define SCSI_NCR_MAX_LUN (1)
-#endif
/*
* IO functions definition for big/little endian CPU support.
@@ -1255,22 +1239,6 @@ struct scr_tblsel {
*/
/*
-** Status
-*/
-
-#define S_GOOD (0x00)
-#define S_CHECK_COND (0x02)
-#define S_COND_MET (0x04)
-#define S_BUSY (0x08)
-#define S_INT (0x10)
-#define S_INT_COND_MET (0x14)
-#define S_CONFLICT (0x18)
-#define S_TERMINATED (0x20)
-#define S_QUEUE_FULL (0x28)
-#define S_ILLEGAL (0xff)
-#define S_SENSE (0x80)
-
-/*
* End of ncrreg from FreeBSD
*/
@@ -1320,6 +1288,12 @@ struct ncr_device {
u8 differential;
};
+/* To keep track of the dma mapping (sg/single) that has been set */
+struct ncr_cmd_priv {
+ int data_mapped;
+ int data_mapping;
+};
+
extern struct Scsi_Host *ncr_attach(struct scsi_host_template *tpnt, int unit, struct ncr_device *device);
extern void ncr53c8xx_release(struct Scsi_Host *host);
irqreturn_t ncr53c8xx_intr(int irq, void *dev_id);
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index f6a50c98c36f..abc4ce9eae74 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -1,20 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* NinjaSCSI-32Bi Cardbus, NinjaSCSI-32UDE PCI/CardBus SCSI driver
* Copyright (C) 2001, 2002, 2003
* YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
* GOTO Masanori <gotom@debian.or.jp>, <gotom@debian.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
* Revision History:
* 1.0: Initial Release.
* 1.1: Add /proc SDTR status.
@@ -38,7 +28,6 @@
#include <linux/dma-mapping.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <scsi/scsi.h>
@@ -59,11 +48,11 @@ MODULE_PARM_DESC(trans_mode, "transfer mode (0: BIOS(default) 1: Async 2: Ultra2
#define ASYNC_MODE 1
#define ULTRA20M_MODE 2
-static int auto_param = 0; /* default: ON */
+static bool auto_param = 0; /* default: ON */
module_param (auto_param, bool, 0);
MODULE_PARM_DESC(auto_param, "AutoParameter mode (0: ON(default) 1: OFF)");
-static int disc_priv = 1; /* default: OFF */
+static bool disc_priv = 1; /* default: OFF */
module_param (disc_priv, bool, 0);
MODULE_PARM_DESC(disc_priv, "disconnection privilege mode (0: ON 1: OFF(default))");
@@ -77,7 +66,7 @@ static const char *nsp32_release_version = "1.2";
/****************************************************************************
* Supported hardware
*/
-static struct pci_device_id nsp32_pci_table[] __devinitdata = {
+static const struct pci_device_id nsp32_pci_table[] = {
{
.vendor = PCI_VENDOR_ID_IODATA,
.device = PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II,
@@ -187,39 +176,40 @@ static nsp32_sync_table nsp32_sync_table_pci[] = {
* function declaration
*/
/* module entry point */
-static int __devinit nsp32_probe (struct pci_dev *, const struct pci_device_id *);
-static void __devexit nsp32_remove(struct pci_dev *);
-static int __init init_nsp32 (void);
-static void __exit exit_nsp32 (void);
+static int nsp32_probe (struct pci_dev *, const struct pci_device_id *);
+static void nsp32_remove(struct pci_dev *);
+static int __init init_nsp32 (void);
+static void __exit exit_nsp32 (void);
/* struct struct scsi_host_template */
-static int nsp32_proc_info (struct Scsi_Host *, char *, char **, off_t, int, int);
+static int nsp32_show_info (struct seq_file *, struct Scsi_Host *);
-static int nsp32_detect (struct pci_dev *pdev);
-static int nsp32_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
-static const char *nsp32_info (struct Scsi_Host *);
-static int nsp32_release (struct Scsi_Host *);
+static int nsp32_detect (struct pci_dev *pdev);
+static int nsp32_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
+static const char *nsp32_info (struct Scsi_Host *);
+static int nsp32_release (struct Scsi_Host *);
/* SCSI error handler */
-static int nsp32_eh_abort (struct scsi_cmnd *);
-static int nsp32_eh_bus_reset (struct scsi_cmnd *);
-static int nsp32_eh_host_reset(struct scsi_cmnd *);
+static int nsp32_eh_abort (struct scsi_cmnd *);
+static int nsp32_eh_host_reset(struct scsi_cmnd *);
/* generate SCSI message */
static void nsp32_build_identify(struct scsi_cmnd *);
static void nsp32_build_nop (struct scsi_cmnd *);
static void nsp32_build_reject (struct scsi_cmnd *);
-static void nsp32_build_sdtr (struct scsi_cmnd *, unsigned char, unsigned char);
+static void nsp32_build_sdtr (struct scsi_cmnd *, unsigned char,
+ unsigned char);
/* SCSI message handler */
static int nsp32_busfree_occur(struct scsi_cmnd *, unsigned short);
static void nsp32_msgout_occur (struct scsi_cmnd *);
-static void nsp32_msgin_occur (struct scsi_cmnd *, unsigned long, unsigned short);
+static void nsp32_msgin_occur (struct scsi_cmnd *, unsigned long,
+ unsigned short);
static int nsp32_setup_sg_table (struct scsi_cmnd *);
static int nsp32_selection_autopara(struct scsi_cmnd *);
static int nsp32_selection_autoscsi(struct scsi_cmnd *);
-static void nsp32_scsi_done (struct scsi_cmnd *);
+static void nsp32_scsi_done (struct scsi_cmnd *);
static int nsp32_arbitration (struct scsi_cmnd *, unsigned int);
static int nsp32_reselection (struct scsi_cmnd *, unsigned char);
static void nsp32_adjust_busfree (struct scsi_cmnd *, unsigned int);
@@ -227,10 +217,13 @@ static void nsp32_restart_autoscsi (struct scsi_cmnd *, unsigned short);
/* SCSI SDTR */
static void nsp32_analyze_sdtr (struct scsi_cmnd *);
-static int nsp32_search_period_entry(nsp32_hw_data *, nsp32_target *, unsigned char);
-static void nsp32_set_async (nsp32_hw_data *, nsp32_target *);
-static void nsp32_set_max_sync (nsp32_hw_data *, nsp32_target *, unsigned char *, unsigned char *);
-static void nsp32_set_sync_entry (nsp32_hw_data *, nsp32_target *, int, unsigned char);
+static int nsp32_search_period_entry(nsp32_hw_data *, nsp32_target *,
+ unsigned char);
+static void nsp32_set_async (nsp32_hw_data *, nsp32_target *);
+static void nsp32_set_max_sync (nsp32_hw_data *, nsp32_target *,
+ unsigned char *, unsigned char *);
+static void nsp32_set_sync_entry (nsp32_hw_data *, nsp32_target *,
+ int, unsigned char);
/* SCSI bus status handler */
static void nsp32_wait_req (nsp32_hw_data *, int);
@@ -246,16 +239,16 @@ static irqreturn_t do_nsp32_isr(int, void *);
static int nsp32hw_init(nsp32_hw_data *);
/* EEPROM handler */
-static int nsp32_getprom_param (nsp32_hw_data *);
-static int nsp32_getprom_at24 (nsp32_hw_data *);
-static int nsp32_getprom_c16 (nsp32_hw_data *);
-static void nsp32_prom_start (nsp32_hw_data *);
-static void nsp32_prom_stop (nsp32_hw_data *);
-static int nsp32_prom_read (nsp32_hw_data *, int);
-static int nsp32_prom_read_bit (nsp32_hw_data *);
-static void nsp32_prom_write_bit(nsp32_hw_data *, int);
-static void nsp32_prom_set (nsp32_hw_data *, int, int);
-static int nsp32_prom_get (nsp32_hw_data *, int);
+static int nsp32_getprom_param (nsp32_hw_data *);
+static int nsp32_getprom_at24 (nsp32_hw_data *);
+static int nsp32_getprom_c16 (nsp32_hw_data *);
+static void nsp32_prom_start (nsp32_hw_data *);
+static void nsp32_prom_stop (nsp32_hw_data *);
+static int nsp32_prom_read (nsp32_hw_data *, int);
+static int nsp32_prom_read_bit (nsp32_hw_data *);
+static void nsp32_prom_write_bit(nsp32_hw_data *, int);
+static void nsp32_prom_set (nsp32_hw_data *, int, int);
+static int nsp32_prom_get (nsp32_hw_data *, int);
/* debug/warning/info message */
static void nsp32_message (const char *, int, char *, char *, ...);
@@ -266,22 +259,21 @@ static void nsp32_dmessage(const char *, int, int, char *, ...);
/*
* max_sectors is currently limited up to 128.
*/
-static struct scsi_host_template nsp32_template = {
+static const struct scsi_host_template nsp32_template = {
.proc_name = "nsp32",
.name = "Workbit NinjaSCSI-32Bi/UDE",
- .proc_info = nsp32_proc_info,
+ .show_info = nsp32_show_info,
.info = nsp32_info,
.queuecommand = nsp32_queuecommand,
.can_queue = 1,
.sg_tablesize = NSP32_SG_SIZE,
.max_sectors = 128,
- .cmd_per_lun = 1,
.this_id = NSP32_HOST_SCSIID,
- .use_clustering = DISABLE_CLUSTERING,
- .eh_abort_handler = nsp32_eh_abort,
- .eh_bus_reset_handler = nsp32_eh_bus_reset,
+ .dma_boundary = PAGE_SIZE - 1,
+ .eh_abort_handler = nsp32_eh_abort,
.eh_host_reset_handler = nsp32_eh_host_reset,
/* .highmem_io = 1, */
+ .cmd_size = sizeof(struct nsp32_cmd_priv),
};
#include "nsp32_io.h"
@@ -323,6 +315,7 @@ static struct scsi_host_template nsp32_template = {
#define NSP32_DEBUG_BUF_LEN 100
+__printf(4, 5)
static void nsp32_message(const char *func, int line, char *type, char *fmt, ...)
{
va_list args;
@@ -369,8 +362,8 @@ static void nsp32_dmessage(const char *func, int line, int mask, char *fmt, ...)
static void nsp32_build_identify(struct scsi_cmnd *SCpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
- int pos = data->msgout_len;
- int mode = FALSE;
+ int pos = data->msgout_len;
+ int mode = FALSE;
/* XXX: Auto DiscPriv detection is progressing... */
if (disc_priv == 0) {
@@ -390,13 +383,13 @@ static void nsp32_build_sdtr(struct scsi_cmnd *SCpnt,
unsigned char offset)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
- int pos = data->msgout_len;
+ int pos = data->msgout_len;
data->msgoutbuf[pos] = EXTENDED_MESSAGE; pos++;
data->msgoutbuf[pos] = EXTENDED_SDTR_LEN; pos++;
data->msgoutbuf[pos] = EXTENDED_SDTR; pos++;
- data->msgoutbuf[pos] = period; pos++;
- data->msgoutbuf[pos] = offset; pos++;
+ data->msgoutbuf[pos] = period; pos++;
+ data->msgoutbuf[pos] = offset; pos++;
data->msgout_len = pos;
}
@@ -407,7 +400,7 @@ static void nsp32_build_sdtr(struct scsi_cmnd *SCpnt,
static void nsp32_build_nop(struct scsi_cmnd *SCpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
- int pos = data->msgout_len;
+ int pos = data->msgout_len;
if (pos != 0) {
nsp32_msg(KERN_WARNING,
@@ -425,12 +418,12 @@ static void nsp32_build_nop(struct scsi_cmnd *SCpnt)
static void nsp32_build_reject(struct scsi_cmnd *SCpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
- int pos = data->msgout_len;
+ int pos = data->msgout_len;
data->msgoutbuf[pos] = MESSAGE_REJECT; pos++;
data->msgout_len = pos;
}
-
+
/*
* timer
*/
@@ -463,7 +456,7 @@ static int nsp32_selection_autopara(struct scsi_cmnd *SCpnt)
unsigned char phase;
int i, ret;
unsigned int msgout;
- u16_le s;
+ u16_le s;
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");
@@ -495,7 +488,7 @@ static int nsp32_selection_autopara(struct scsi_cmnd *SCpnt)
* the sending order of the message is:
* MCNT 3: MSG#0 -> MSG#1 -> MSG#2
* MCNT 2: MSG#1 -> MSG#2
- * MCNT 1: MSG#2
+ * MCNT 1: MSG#2
*/
msgout >>= 8;
msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);
@@ -507,7 +500,8 @@ static int nsp32_selection_autopara(struct scsi_cmnd *SCpnt)
msgout = 0;
}
- // nsp_dbg(NSP32_DEBUG_AUTOSCSI, "sel time out=0x%x\n", nsp32_read2(base, SEL_TIME_OUT));
+ // nsp_dbg(NSP32_DEBUG_AUTOSCSI, "sel time out=0x%x\n",
+ // nsp32_read2(base, SEL_TIME_OUT));
// nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
/*
@@ -533,10 +527,10 @@ static int nsp32_selection_autopara(struct scsi_cmnd *SCpnt)
/* command control */
param->command_control = cpu_to_le16(CLEAR_CDB_FIFO_POINTER |
- AUTOSCSI_START |
- AUTO_MSGIN_00_OR_04 |
- AUTO_MSGIN_02 |
- AUTO_ATN );
+ AUTOSCSI_START |
+ AUTO_MSGIN_00_OR_04 |
+ AUTO_MSGIN_02 |
+ AUTO_ATN );
/* transfer control */
@@ -568,9 +562,9 @@ static int nsp32_selection_autopara(struct scsi_cmnd *SCpnt)
/*
* transfer parameter to ASIC
*/
- nsp32_write4(base, SGT_ADR, data->auto_paddr);
- nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER |
- AUTO_PARAMETER );
+ nsp32_write4(base, SGT_ADR, data->auto_paddr);
+ nsp32_write2(base, COMMAND_CONTROL,
+ CLEAR_CDB_FIFO_POINTER | AUTO_PARAMETER );
/*
* Check arbitration
@@ -594,7 +588,6 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
int status;
unsigned short command = 0;
unsigned int msgout = 0;
- unsigned short execph;
int i;
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");
@@ -608,17 +601,17 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
* check bus line
*/
phase = nsp32_read1(base, SCSI_BUS_MONITOR);
- if(((phase & BUSMON_BSY) == 1) || (phase & BUSMON_SEL) == 1) {
+ if ((phase & BUSMON_BSY) || (phase & BUSMON_SEL)) {
nsp32_msg(KERN_WARNING, "bus busy");
SCpnt->result = DID_BUS_BUSY << 16;
status = 1;
goto out;
- }
+ }
/*
* clear execph
*/
- execph = nsp32_read2(base, SCSI_EXECUTE_PHASE);
+ nsp32_read2(base, SCSI_EXECUTE_PHASE);
/*
* clear FIFO counter to set CDBs
@@ -630,13 +623,14 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
*/
for (i = 0; i < SCpnt->cmd_len; i++) {
nsp32_write1(base, COMMAND_DATA, SCpnt->cmnd[i]);
- }
+ }
nsp32_dbg(NSP32_DEBUG_CDB_CONTENTS, "CDB[0]=[0x%x]", SCpnt->cmnd[0]);
/*
* set SCSIOUT LATCH(initiator)/TARGET(target) (OR-ed) ID
*/
- nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID, BIT(host_id) | BIT(target));
+ nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID,
+ BIT(host_id) | BIT(target));
/*
* set SCSI MSGOUT REG
@@ -656,7 +650,7 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
* the sending order of the message is:
* MCNT 3: MSG#0 -> MSG#1 -> MSG#2
* MCNT 2: MSG#1 -> MSG#2
- * MCNT 1: MSG#2
+ * MCNT 1: MSG#2
*/
msgout >>= 8;
msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);
@@ -676,7 +670,7 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
/*
* set SREQ hazard killer sampling rate
- *
+ *
* TODO: sample_rate (BASE+0F) is 0 when internal clock = 40MHz.
* check other internal clock!
*/
@@ -701,7 +695,8 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
"syncreg=0x%x, ackwidth=0x%x, sgtpaddr=0x%x, id=0x%x",
nsp32_read1(base, SYNC_REG), nsp32_read1(base, ACK_WIDTH),
- nsp32_read4(base, SGT_ADR), nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
+ nsp32_read4(base, SGT_ADR),
+ nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "msgout_len=%d, msgout=0x%x",
data->msgout_len, msgout);
@@ -730,10 +725,10 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
* start AUTO SCSI, kick off arbitration
*/
command = (CLEAR_CDB_FIFO_POINTER |
- AUTOSCSI_START |
+ AUTOSCSI_START |
AUTO_MSGIN_00_OR_04 |
- AUTO_MSGIN_02 |
- AUTO_ATN );
+ AUTO_MSGIN_02 |
+ AUTO_ATN);
nsp32_write2(base, COMMAND_CONTROL, command);
/*
@@ -753,9 +748,9 @@ static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
/*
* Arbitration Status Check
- *
+ *
* Note: Arbitration counter is waited during ARBIT_GO is not lifting.
- * Using udelay(1) consumes CPU time and system time, but
+ * Using udelay(1) consumes CPU time and system time, but
* arbitration delay time is defined minimal 2.4us in SCSI
* specification, thus udelay works as coarse grained wait timer.
*/
@@ -790,7 +785,7 @@ static int nsp32_arbitration(struct scsi_cmnd *SCpnt, unsigned int base)
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit timeout");
SCpnt->result = DID_NO_CONNECT << 16;
status = FALSE;
- }
+ }
/*
* clear Arbit
@@ -836,7 +831,8 @@ static int nsp32_reselection(struct scsi_cmnd *SCpnt, unsigned char newlun)
* or current nexus is not existed, unexpected
* reselection is occurred. Send reject message.
*/
- if (newid >= ARRAY_SIZE(data->lunt) || newlun >= ARRAY_SIZE(data->lunt[0])) {
+ if (newid >= ARRAY_SIZE(data->lunt) ||
+ newlun >= ARRAY_SIZE(data->lunt[0])) {
nsp32_msg(KERN_WARNING, "unknown id/lun");
return FALSE;
} else if(data->lunt[newid][newlun].SCpnt == NULL) {
@@ -890,7 +886,8 @@ static int nsp32_setup_sg_table(struct scsi_cmnd *SCpnt)
if (le32_to_cpu(sgt[i].len) > 0x10000) {
nsp32_msg(KERN_ERR,
- "can't transfer over 64KB at a time, size=0x%lx", le32_to_cpu(sgt[i].len));
+ "can't transfer over 64KB at a time, "
+ "size=0x%x", le32_to_cpu(sgt[i].len));
return FALSE;
}
nsp32_dbg(NSP32_DEBUG_SGLIST,
@@ -908,18 +905,20 @@ static int nsp32_setup_sg_table(struct scsi_cmnd *SCpnt)
return TRUE;
}
-static int nsp32_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+static int nsp32_queuecommand_lck(struct scsi_cmnd *SCpnt)
{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
nsp32_target *target;
nsp32_lunt *cur_lunt;
int ret;
nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
- "enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x "
+ "enter. target: 0x%x LUN: 0x%llx cmnd: 0x%x cmndlen: 0x%x "
"use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x",
- SCpnt->device->id, SCpnt->device->lun, SCpnt->cmnd[0], SCpnt->cmd_len,
- scsi_sg_count(SCpnt), scsi_sglist(SCpnt), scsi_bufflen(SCpnt));
+ SCpnt->device->id, SCpnt->device->lun, SCpnt->cmnd[0],
+ SCpnt->cmd_len, scsi_sg_count(SCpnt), scsi_sglist(SCpnt),
+ scsi_bufflen(SCpnt));
if (data->CurrentSC != NULL) {
nsp32_msg(KERN_ERR, "Currentsc != NULL. Cancel this command request");
@@ -931,7 +930,7 @@ static int nsp32_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct s
/* check target ID is not same as this initiator ID */
if (scmd_id(SCpnt) == SCpnt->device->host->this_id) {
- nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "terget==host???");
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "target==host???");
SCpnt->result = DID_BAD_TARGET << 16;
done(SCpnt);
return 0;
@@ -947,17 +946,10 @@ static int nsp32_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct s
show_command(SCpnt);
- SCpnt->scsi_done = done;
data->CurrentSC = SCpnt;
- SCpnt->SCp.Status = CHECK_CONDITION;
- SCpnt->SCp.Message = 0;
+ nsp32_priv(SCpnt)->status = SAM_STAT_CHECK_CONDITION;
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
- SCpnt->SCp.ptr = (char *)scsi_sglist(SCpnt);
- SCpnt->SCp.this_residual = scsi_bufflen(SCpnt);
- SCpnt->SCp.buffer = NULL;
- SCpnt->SCp.buffers_residual = 0;
-
/* initialize data */
data->msgout_len = 0;
data->msgin_len = 0;
@@ -980,7 +972,7 @@ static int nsp32_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct s
/* Build IDENTIFY */
nsp32_build_identify(SCpnt);
- /*
+ /*
* If target is the first time to transfer after the reset
* (target don't have SDTR_DONE and SDTR_INITIATOR), sync
* message SDTR is needed to do synchronous transfer.
@@ -1065,9 +1057,9 @@ static int nsp32hw_init(nsp32_hw_data *data)
nsp32_index_write2(base, CFG_LATE_CACHE, lc_reg & 0xffff);
}
- nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
- nsp32_write2(base, TRANSFER_CONTROL, 0);
- nsp32_write4(base, BM_CNT, 0);
+ nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+ nsp32_write2(base, TRANSFER_CONTROL, 0);
+ nsp32_write4(base, BM_CNT, 0);
nsp32_write2(base, SCSI_EXECUTE_PHASE, 0);
do {
@@ -1095,12 +1087,13 @@ static int nsp32hw_init(nsp32_hw_data *data)
nsp32_index_read1(base, FIFO_EMPTY_SHLD_COUNT));
nsp32_index_write1(base, CLOCK_DIV, data->clock);
- nsp32_index_write1(base, BM_CYCLE, MEMRD_CMD1 | SGT_AUTO_PARA_MEMED_CMD);
+ nsp32_index_write1(base, BM_CYCLE,
+ MEMRD_CMD1 | SGT_AUTO_PARA_MEMED_CMD);
nsp32_write1(base, PARITY_CONTROL, 0); /* parity check is disable */
/*
* initialize MISC_WRRD register
- *
+ *
* Note: Designated parameters is obeyed as following:
* MISC_SCSI_DIRECTION_DETECTOR_SELECT: It must be set.
* MISC_MASTER_TERMINATION_SELECT: It must be set.
@@ -1115,10 +1108,10 @@ static int nsp32hw_init(nsp32_hw_data *data)
*/
nsp32_index_write2(base, MISC_WR,
(SCSI_DIRECTION_DETECTOR_SELECT |
- DELAYED_BMSTART |
- MASTER_TERMINATION_SELECT |
- BMREQ_NEGATE_TIMING_SEL |
- AUTOSEL_TIMING_SEL |
+ DELAYED_BMSTART |
+ MASTER_TERMINATION_SELECT |
+ BMREQ_NEGATE_TIMING_SEL |
+ AUTOSEL_TIMING_SEL |
BMSTOP_CHANGE2_NONDATA_PHASE));
nsp32_index_write1(base, TERM_PWR_CONTROL, 0);
@@ -1139,15 +1132,16 @@ static int nsp32hw_init(nsp32_hw_data *data)
* enable to select designated IRQ (except for
* IRQSELECT_SERR, IRQSELECT_PERR, IRQSELECT_BMCNTERR)
*/
- nsp32_index_write2(base, IRQ_SELECT, IRQSELECT_TIMER_IRQ |
- IRQSELECT_SCSIRESET_IRQ |
- IRQSELECT_FIFO_SHLD_IRQ |
- IRQSELECT_RESELECT_IRQ |
- IRQSELECT_PHASE_CHANGE_IRQ |
- IRQSELECT_AUTO_SCSI_SEQ_IRQ |
- // IRQSELECT_BMCNTERR_IRQ |
- IRQSELECT_TARGET_ABORT_IRQ |
- IRQSELECT_MASTER_ABORT_IRQ );
+ nsp32_index_write2(base, IRQ_SELECT,
+ IRQSELECT_TIMER_IRQ |
+ IRQSELECT_SCSIRESET_IRQ |
+ IRQSELECT_FIFO_SHLD_IRQ |
+ IRQSELECT_RESELECT_IRQ |
+ IRQSELECT_PHASE_CHANGE_IRQ |
+ IRQSELECT_AUTO_SCSI_SEQ_IRQ |
+ // IRQSELECT_BMCNTERR_IRQ |
+ IRQSELECT_TARGET_ABORT_IRQ |
+ IRQSELECT_MASTER_ABORT_IRQ );
nsp32_write2(base, IRQ_CONTROL, 0);
/* PCI LED off */
@@ -1177,11 +1171,12 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
* IRQ check, then enable IRQ mask
*/
irq_stat = nsp32_read2(base, IRQ_STATUS);
- nsp32_dbg(NSP32_DEBUG_INTR,
+ nsp32_dbg(NSP32_DEBUG_INTR,
"enter IRQ: %d, IRQstatus: 0x%x", irq, irq_stat);
/* is this interrupt comes from Ninja asic? */
if ((irq_stat & IRQSTATUS_ANY_IRQ) == 0) {
- nsp32_dbg(NSP32_DEBUG_INTR, "shared interrupt: irq other 0x%x", irq_stat);
+ nsp32_dbg(NSP32_DEBUG_INTR,
+ "shared interrupt: irq other 0x%x", irq_stat);
goto out2;
}
handled = 1;
@@ -1221,7 +1216,8 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
if (SCpnt == NULL) {
nsp32_msg(KERN_WARNING, "SCpnt==NULL this can't be happened");
- nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x", irq_stat, trans_stat);
+ nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x",
+ irq_stat, trans_stat);
goto out;
}
@@ -1261,7 +1257,7 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
* ---> AutoSCSI with MSGOUTreg is processed.
*/
data->msgout_len = 0;
- };
+ }
nsp32_dbg(NSP32_DEBUG_INTR, "MsgOut phase processed");
}
@@ -1279,13 +1275,13 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
"Data in/out phase processed");
/* read BMCNT, SGT pointer addr */
- nsp32_dbg(NSP32_DEBUG_INTR, "BMCNT=0x%lx",
+ nsp32_dbg(NSP32_DEBUG_INTR, "BMCNT=0x%lx",
nsp32_read4(base, BM_CNT));
- nsp32_dbg(NSP32_DEBUG_INTR, "addr=0x%lx",
+ nsp32_dbg(NSP32_DEBUG_INTR, "addr=0x%lx",
nsp32_read4(base, SGT_ADR));
- nsp32_dbg(NSP32_DEBUG_INTR, "SACK=0x%lx",
+ nsp32_dbg(NSP32_DEBUG_INTR, "SACK=0x%lx",
nsp32_read4(base, SACK_CNT));
- nsp32_dbg(NSP32_DEBUG_INTR, "SSACK=0x%lx",
+ nsp32_dbg(NSP32_DEBUG_INTR, "SSACK=0x%lx",
nsp32_read4(base, SAVED_SACK_CNT));
scsi_set_resid(SCpnt, 0); /* all data transferred! */
@@ -1320,7 +1316,7 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
* Read CSB and substitute CSB for SCpnt->result
* to save status phase stutas byte.
* scsi error handler checks host_byte (DID_*:
- * low level driver to indicate status), then checks
+ * low level driver to indicate status), then checks
* status_byte (SCSI status byte).
*/
SCpnt->result = (int)nsp32_read1(base, SCSI_CSB_IN);
@@ -1328,7 +1324,7 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
if (auto_stat & ILLEGAL_PHASE) {
/* Illegal phase is detected. SACK is not back. */
- nsp32_msg(KERN_WARNING,
+ nsp32_msg(KERN_WARNING,
"AUTO SCSI ILLEGAL PHASE OCCUR!!!!");
/* TODO: currently we don't have any action... bus reset? */
@@ -1376,12 +1372,13 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
case BUSPHASE_STATUS:
nsp32_dbg(NSP32_DEBUG_INTR, "fifo/status");
- SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
+ nsp32_priv(SCpnt)->status = nsp32_read1(base, SCSI_CSB_IN);
break;
default:
nsp32_dbg(NSP32_DEBUG_INTR, "fifo/other phase");
- nsp32_dbg(NSP32_DEBUG_INTR, "irq_stat=0x%x trans_stat=0x%x", irq_stat, trans_stat);
+ nsp32_dbg(NSP32_DEBUG_INTR, "irq_stat=0x%x trans_stat=0x%x",
+ irq_stat, trans_stat);
show_busphase(busphase);
break;
}
@@ -1442,108 +1439,85 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-#undef SPRINTF
-#define SPRINTF(args...) \
- do { \
- if(length > (pos - buffer)) { \
- pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
- nsp32_dbg(NSP32_DEBUG_PROC, "buffer=0x%p pos=0x%p length=%d %d\n", buffer, pos, length, length - (pos - buffer));\
- } \
- } while(0)
-
-static int nsp32_proc_info(struct Scsi_Host *host, char *buffer, char **start,
- off_t offset, int length, int inout)
+
+static int nsp32_show_info(struct seq_file *m, struct Scsi_Host *host)
{
- char *pos = buffer;
- int thislength;
unsigned long flags;
nsp32_hw_data *data;
- int hostno;
+ int hostno;
unsigned int base;
unsigned char mode_reg;
- int id, speed;
- long model;
-
- /* Write is not supported, just return. */
- if (inout == TRUE) {
- return -EINVAL;
- }
+ int id, speed;
+ long model;
hostno = host->host_no;
data = (nsp32_hw_data *)host->hostdata;
base = host->io_port;
- SPRINTF("NinjaSCSI-32 status\n\n");
- SPRINTF("Driver version: %s, $Revision: 1.33 $\n", nsp32_release_version);
- SPRINTF("SCSI host No.: %d\n", hostno);
- SPRINTF("IRQ: %d\n", host->irq);
- SPRINTF("IO: 0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
- SPRINTF("MMIO(virtual address): 0x%lx-0x%lx\n", host->base, host->base + data->MmioLength - 1);
- SPRINTF("sg_tablesize: %d\n", host->sg_tablesize);
- SPRINTF("Chip revision: 0x%x\n", (nsp32_read2(base, INDEX_REG) >> 8) & 0xff);
+ seq_puts(m, "NinjaSCSI-32 status\n\n");
+ seq_printf(m, "Driver version: %s, $Revision: 1.33 $\n",
+ nsp32_release_version);
+ seq_printf(m, "SCSI host No.: %d\n", hostno);
+ seq_printf(m, "IRQ: %d\n", host->irq);
+ seq_printf(m, "IO: 0x%lx-0x%lx\n",
+ host->io_port, host->io_port + host->n_io_port - 1);
+ seq_printf(m, "MMIO(virtual address): 0x%lx-0x%lx\n",
+ host->base, host->base + data->MmioLength - 1);
+ seq_printf(m, "sg_tablesize: %d\n",
+ host->sg_tablesize);
+ seq_printf(m, "Chip revision: 0x%x\n",
+ (nsp32_read2(base, INDEX_REG) >> 8) & 0xff);
mode_reg = nsp32_index_read1(base, CHIP_MODE);
model = data->pci_devid->driver_data;
#ifdef CONFIG_PM
- SPRINTF("Power Management: %s\n", (mode_reg & OPTF) ? "yes" : "no");
+ seq_printf(m, "Power Management: %s\n",
+ (mode_reg & OPTF) ? "yes" : "no");
#endif
- SPRINTF("OEM: %ld, %s\n", (mode_reg & (OEM0|OEM1)), nsp32_model[model]);
+ seq_printf(m, "OEM: %ld, %s\n",
+ (mode_reg & (OEM0|OEM1)), nsp32_model[model]);
spin_lock_irqsave(&(data->Lock), flags);
- SPRINTF("CurrentSC: 0x%p\n\n", data->CurrentSC);
+ seq_printf(m, "CurrentSC: 0x%p\n\n", data->CurrentSC);
spin_unlock_irqrestore(&(data->Lock), flags);
- SPRINTF("SDTR status\n");
+ seq_puts(m, "SDTR status\n");
for (id = 0; id < ARRAY_SIZE(data->target); id++) {
- SPRINTF("id %d: ", id);
+ seq_printf(m, "id %d: ", id);
if (id == host->this_id) {
- SPRINTF("----- NinjaSCSI-32 host adapter\n");
+ seq_puts(m, "----- NinjaSCSI-32 host adapter\n");
continue;
}
if (data->target[id].sync_flag == SDTR_DONE) {
- if (data->target[id].period == 0 &&
+ if (data->target[id].period == 0 &&
data->target[id].offset == ASYNC_OFFSET ) {
- SPRINTF("async");
+ seq_puts(m, "async");
} else {
- SPRINTF(" sync");
+ seq_puts(m, " sync");
}
} else {
- SPRINTF(" none");
+ seq_puts(m, " none");
}
if (data->target[id].period != 0) {
speed = 1000000 / (data->target[id].period * 4);
- SPRINTF(" transfer %d.%dMB/s, offset %d",
+ seq_printf(m, " transfer %d.%dMB/s, offset %d",
speed / 1000,
speed % 1000,
data->target[id].offset
);
}
- SPRINTF("\n");
+ seq_putc(m, '\n');
}
-
-
- thislength = pos - (buffer + offset);
-
- if(thislength < 0) {
- *start = NULL;
- return 0;
- }
-
-
- thislength = min(thislength, length);
- *start = buffer + offset;
-
- return thislength;
+ return 0;
}
-#undef SPRINTF
@@ -1562,20 +1536,20 @@ static void nsp32_scsi_done(struct scsi_cmnd *SCpnt)
* clear TRANSFERCONTROL_BM_START
*/
nsp32_write2(base, TRANSFER_CONTROL, 0);
- nsp32_write4(base, BM_CNT, 0);
+ nsp32_write4(base, BM_CNT, 0);
/*
* call scsi_done
*/
- (*SCpnt->scsi_done)(SCpnt);
+ scsi_done(SCpnt);
/*
* reset parameters
*/
- data->cur_lunt->SCpnt = NULL;
- data->cur_lunt = NULL;
- data->cur_target = NULL;
- data->CurrentSC = NULL;
+ data->cur_lunt->SCpnt = NULL;
+ data->cur_lunt = NULL;
+ data->cur_target = NULL;
+ data->CurrentSC = NULL;
}
@@ -1586,7 +1560,7 @@ static void nsp32_scsi_done(struct scsi_cmnd *SCpnt)
* with ACK reply when below condition is matched:
* MsgIn 00: Command Complete.
* MsgIn 02: Save Data Pointer.
- * MsgIn 04: Diconnect.
+ * MsgIn 04: Disconnect.
* In other case, unexpected BUSFREE is detected.
*/
static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
@@ -1597,7 +1571,7 @@ static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
nsp32_dbg(NSP32_DEBUG_BUSFREE, "enter execph=0x%x", execph);
show_autophase(execph);
- nsp32_write4(base, BM_CNT, 0);
+ nsp32_write4(base, BM_CNT, 0);
nsp32_write2(base, TRANSFER_CONTROL, 0);
/*
@@ -1605,7 +1579,7 @@ static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
*
* VALID:
* Save Data Pointer is received. Adjust pointer.
- *
+ *
* NO-VALID:
* SCSI-3 says if Save Data Pointer is not received, then we restart
* processing and we can't adjust any SCSI data pointer in next data
@@ -1618,7 +1592,7 @@ static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
* Check sack_cnt/saved_sack_cnt, then adjust sg table if
* needed.
*/
- if (!(execph & MSGIN_00_VALID) &&
+ if (!(execph & MSGIN_00_VALID) &&
((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE))) {
unsigned int sacklen, s_sacklen;
@@ -1661,7 +1635,7 @@ static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
* no processing.
*/
}
-
+
if (execph & MSGIN_03_VALID) {
/* MsgIn03 was valid to be processed. No need processing. */
}
@@ -1683,7 +1657,7 @@ static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
* negotiating.
*/
if (execph & (MSGIN_00_VALID | MSGIN_04_VALID)) {
- /*
+ /*
* If valid message is received, then
* negotiation is succeeded.
*/
@@ -1709,30 +1683,25 @@ static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
/* MsgIn 00: Command Complete */
nsp32_dbg(NSP32_DEBUG_BUSFREE, "command complete");
- SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
- SCpnt->SCp.Message = 0;
- nsp32_dbg(NSP32_DEBUG_BUSFREE,
+ nsp32_priv(SCpnt)->status = nsp32_read1(base, SCSI_CSB_IN);
+ nsp32_dbg(NSP32_DEBUG_BUSFREE,
"normal end stat=0x%x resid=0x%x\n",
- SCpnt->SCp.Status, scsi_get_resid(SCpnt));
- SCpnt->result = (DID_OK << 16) |
- (SCpnt->SCp.Message << 8) |
- (SCpnt->SCp.Status << 0);
+ nsp32_priv(SCpnt)->status, scsi_get_resid(SCpnt));
+ SCpnt->result = (DID_OK << 16) |
+ (nsp32_priv(SCpnt)->status << 0);
nsp32_scsi_done(SCpnt);
/* All operation is done */
return TRUE;
} else if (execph & MSGIN_04_VALID) {
/* MsgIn 04: Disconnect */
- SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
- SCpnt->SCp.Message = 4;
-
+ nsp32_priv(SCpnt)->status = nsp32_read1(base, SCSI_CSB_IN);
+
nsp32_dbg(NSP32_DEBUG_BUSFREE, "disconnect");
return TRUE;
} else {
/* Unexpected bus free */
nsp32_msg(KERN_WARNING, "unexpected bus free occurred");
- /* DID_ERROR? */
- //SCpnt->result = (DID_OK << 16) | (SCpnt->SCp.Message << 8) | (SCpnt->SCp.Status << 0);
SCpnt->result = DID_ERROR << 16;
nsp32_scsi_done(SCpnt);
return TRUE;
@@ -1750,12 +1719,12 @@ static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
static void nsp32_adjust_busfree(struct scsi_cmnd *SCpnt, unsigned int s_sacklen)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
- int old_entry = data->cur_entry;
- int new_entry;
- int sg_num = data->cur_lunt->sg_num;
- nsp32_sgtable *sgt = data->cur_lunt->sglun->sgt;
- unsigned int restlen, sentlen;
- u32_le len, addr;
+ int old_entry = data->cur_entry;
+ int new_entry;
+ int sg_num = data->cur_lunt->sg_num;
+ nsp32_sgtable *sgt = data->cur_lunt->sglun->sgt;
+ unsigned int restlen, sentlen;
+ u32_le len, addr;
nsp32_dbg(NSP32_DEBUG_SGLIST, "old resid=0x%x", scsi_get_resid(SCpnt));
@@ -1763,7 +1732,7 @@ static void nsp32_adjust_busfree(struct scsi_cmnd *SCpnt, unsigned int s_sacklen
s_sacklen -= le32_to_cpu(sgt[old_entry].addr) & 3;
/*
- * calculate new_entry from sack count and each sgt[].len
+ * calculate new_entry from sack count and each sgt[].len
* calculate the byte which is intent to send
*/
sentlen = 0;
@@ -1781,8 +1750,10 @@ static void nsp32_adjust_busfree(struct scsi_cmnd *SCpnt, unsigned int s_sacklen
if (sentlen == s_sacklen) {
/* XXX: confirm it's ok or not */
- /* In this case, it's ok because we are at
- the head element of the sg. restlen is correctly calculated. */
+ /* In this case, it's ok because we are at
+ * the head element of the sg. restlen is correctly
+ * calculated.
+ */
}
/* calculate the rest length for transferring */
@@ -1797,7 +1768,7 @@ static void nsp32_adjust_busfree(struct scsi_cmnd *SCpnt, unsigned int s_sacklen
/* set cur_entry with new_entry */
data->cur_entry = new_entry;
-
+
return;
last:
@@ -1824,10 +1795,8 @@ static void nsp32_msgout_occur(struct scsi_cmnd *SCpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
unsigned int base = SCpnt->device->host->io_port;
- //unsigned short command;
- long new_sgtp;
int i;
-
+
nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR,
"enter: msgout_len: 0x%x", data->msgout_len);
@@ -1840,14 +1809,6 @@ static void nsp32_msgout_occur(struct scsi_cmnd *SCpnt)
}
/*
- * Set SGTP ADDR current entry for restarting AUTOSCSI,
- * because SGTP is incremented next point.
- * There is few statement in the specification...
- */
- new_sgtp = data->cur_lunt->sglun_paddr +
- (data->cur_lunt->cur_entry * sizeof(nsp32_sgtable));
-
- /*
* send messages
*/
for (i = 0; i < data->msgout_len; i++) {
@@ -1869,10 +1830,10 @@ static void nsp32_msgout_occur(struct scsi_cmnd *SCpnt)
//nsp32_restart_autoscsi(SCpnt, command);
nsp32_write2(base, COMMAND_CONTROL,
(CLEAR_CDB_FIFO_POINTER |
- AUTO_COMMAND_PHASE |
- AUTOSCSI_RESTART |
- AUTO_MSGIN_00_OR_04 |
- AUTO_MSGIN_02 ));
+ AUTO_COMMAND_PHASE |
+ AUTOSCSI_RESTART |
+ AUTO_MSGIN_00_OR_04 |
+ AUTO_MSGIN_02 ));
}
/*
* Write data with SACK, then wait sack is
@@ -1883,7 +1844,7 @@ static void nsp32_msgout_occur(struct scsi_cmnd *SCpnt)
nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR, "bus: 0x%x\n",
nsp32_read1(base, SCSI_BUS_MONITOR));
- };
+ }
data->msgout_len = 0;
@@ -1972,9 +1933,9 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
unsigned char msgtype;
unsigned char newlun;
unsigned short command = 0;
- int msgclear = TRUE;
- long new_sgtp;
- int ret;
+ int msgclear = TRUE;
+ long new_sgtp;
+ int ret;
/*
* read first message
@@ -2014,7 +1975,7 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
goto reject;
}
}
-
+
/*
* processing messages except for IDENTIFY
*
@@ -2030,10 +1991,10 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
* These messages should not be occurred.
* They should be processed on AutoSCSI sequencer.
*/
- nsp32_msg(KERN_WARNING,
+ nsp32_msg(KERN_WARNING,
"unexpected message of AutoSCSI MsgIn: 0x%x", msg);
break;
-
+
case RESTORE_POINTERS:
/*
* AutoMsgIn03 is disabled, and HBA gets this message.
@@ -2059,7 +2020,7 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
/*
* set new sg pointer
*/
- new_sgtp = data->cur_lunt->sglun_paddr +
+ new_sgtp = data->cur_lunt->sglun_paddr +
(data->cur_lunt->cur_entry * sizeof(nsp32_sgtable));
nsp32_write4(base, SGT_ADR, new_sgtp);
@@ -2070,13 +2031,13 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
* These messages should not be occurred.
* They should be processed on AutoSCSI sequencer.
*/
- nsp32_msg (KERN_WARNING,
+ nsp32_msg (KERN_WARNING,
"unexpected message of AutoSCSI MsgIn: SAVE_POINTERS");
-
+
break;
-
+
case MESSAGE_REJECT:
- /* If previous message_out is sending SDTR, and get
+ /* If previous message_out is sending SDTR, and get
message_reject from target, SDTR negotiation is failed */
if (data->cur_target->sync_flag &
(SDTR_INITIATOR | SDTR_TARGET)) {
@@ -2095,7 +2056,7 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
case LINKED_CMD_COMPLETE:
case LINKED_FLG_CMD_COMPLETE:
/* queue tag is not supported currently */
- nsp32_msg (KERN_WARNING,
+ nsp32_msg (KERN_WARNING,
"unsupported message: 0x%x", msgtype);
break;
@@ -2148,7 +2109,7 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
}
/*
- * Reach here means regular length of each type of
+ * Reach here means regular length of each type of
* extended messages.
*/
switch (data->msginbuf[2]) {
@@ -2183,12 +2144,12 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
goto reject; /* not implemented yet */
break;
-
+
default:
goto reject;
}
break;
-
+
default:
goto reject;
}
@@ -2204,7 +2165,7 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
* AutoSCSI restart, at the same time MsgOutOccur should be
* happened (however, such situation is really possible...?).
*/
- if (data->msgout_len > 0) {
+ if (data->msgout_len > 0) {
nsp32_write4(base, SCSI_MSG_OUT, 0);
command |= AUTO_ATN;
}
@@ -2246,7 +2207,7 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
return;
reject:
- nsp32_msg(KERN_WARNING,
+ nsp32_msg(KERN_WARNING,
"invalid or unsupported MessageIn, rejected. "
"current msg: 0x%x (len: 0x%x), processing msg: 0x%x",
msg, data->msgin_len, msgtype);
@@ -2257,37 +2218,32 @@ static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
}
/*
- *
+ *
*/
static void nsp32_analyze_sdtr(struct scsi_cmnd *SCpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
- nsp32_target *target = data->cur_target;
- nsp32_sync_table *synct;
- unsigned char get_period = data->msginbuf[3];
- unsigned char get_offset = data->msginbuf[4];
- int entry;
- int syncnum;
+ nsp32_target *target = data->cur_target;
+ unsigned char get_period = data->msginbuf[3];
+ unsigned char get_offset = data->msginbuf[4];
+ int entry;
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "enter");
- synct = data->synct;
- syncnum = data->syncnum;
-
/*
* If this inititor sent the SDTR message, then target responds SDTR,
* initiator SYNCREG, ACKWIDTH from SDTR parameter.
* Messages are not appropriate, then send back reject message.
- * If initiator did not send the SDTR, but target sends SDTR,
+ * If initiator did not send the SDTR, but target sends SDTR,
* initiator calculator the appropriate parameter and send back SDTR.
- */
+ */
if (target->sync_flag & SDTR_INITIATOR) {
/*
* Initiator sent SDTR, the target responds and
* send back negotiation SDTR.
*/
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target responds SDTR");
-
+
target->sync_flag &= ~SDTR_INITIATOR;
target->sync_flag |= SDTR_DONE;
@@ -2301,7 +2257,7 @@ static void nsp32_analyze_sdtr(struct scsi_cmnd *SCpnt)
*/
goto reject;
}
-
+
if (get_offset == ASYNC_OFFSET) {
/*
* Negotiation is succeeded, the target want
@@ -2332,7 +2288,7 @@ static void nsp32_analyze_sdtr(struct scsi_cmnd *SCpnt)
if (entry < 0) {
/*
- * Target want to use long period which is not
+ * Target want to use long period which is not
* acceptable NinjaSCSI-32Bi/UDE.
*/
goto reject;
@@ -2345,7 +2301,7 @@ static void nsp32_analyze_sdtr(struct scsi_cmnd *SCpnt)
} else {
/* Target send SDTR to initiator. */
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target send SDTR");
-
+
target->sync_flag |= SDTR_INITIATOR;
/* offset: */
@@ -2468,14 +2424,13 @@ static void nsp32_set_max_sync(nsp32_hw_data *data,
*/
static void nsp32_set_sync_entry(nsp32_hw_data *data,
nsp32_target *target,
- int entry,
+ int entry,
unsigned char offset)
{
unsigned char period, ackwidth, sample_rate;
period = data->synct[entry].period_num;
ackwidth = data->synct[entry].ackwidth;
- offset = offset;
sample_rate = data->synct[entry].sample_rate;
target->syncreg = TO_SYNCREG(period, offset);
@@ -2498,7 +2453,7 @@ static void nsp32_set_sync_entry(nsp32_hw_data *data,
static void nsp32_wait_req(nsp32_hw_data *data, int state)
{
unsigned int base = data->BaseAddress;
- int wait_time = 0;
+ int wait_time = 0;
unsigned char bus, req_bit;
if (!((state == ASSERT) || (state == NEGATE))) {
@@ -2510,7 +2465,7 @@ static void nsp32_wait_req(nsp32_hw_data *data, int state)
do {
bus = nsp32_read1(base, SCSI_BUS_MONITOR);
if ((bus & BUSMON_REQ) == req_bit) {
- nsp32_dbg(NSP32_DEBUG_WAIT,
+ nsp32_dbg(NSP32_DEBUG_WAIT,
"wait_time: %d", wait_time);
return;
}
@@ -2527,7 +2482,7 @@ static void nsp32_wait_req(nsp32_hw_data *data, int state)
static void nsp32_wait_sack(nsp32_hw_data *data, int state)
{
unsigned int base = data->BaseAddress;
- int wait_time = 0;
+ int wait_time = 0;
unsigned char bus, ack_bit;
if (!((state == ASSERT) || (state == NEGATE))) {
@@ -2592,8 +2547,8 @@ static int nsp32_detect(struct pci_dev *pdev)
struct Scsi_Host *host; /* registered host structure */
struct resource *res;
nsp32_hw_data *data;
- int ret;
- int i, j;
+ int ret;
+ int i, j;
nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
@@ -2670,9 +2625,9 @@ static int nsp32_detect(struct pci_dev *pdev)
*/
/*
- * setup DMA
+ * setup DMA
*/
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
nsp32_msg (KERN_ERR, "failed to set PCI DMA mask");
goto scsi_unregister;
}
@@ -2680,7 +2635,9 @@ static int nsp32_detect(struct pci_dev *pdev)
/*
* allocate autoparam DMA resource.
*/
- data->autoparam = pci_alloc_consistent(pdev, sizeof(nsp32_autoparam), &(data->auto_paddr));
+ data->autoparam = dma_alloc_coherent(&pdev->dev,
+ sizeof(nsp32_autoparam), &(data->auto_paddr),
+ GFP_KERNEL);
if (data->autoparam == NULL) {
nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
goto scsi_unregister;
@@ -2689,8 +2646,8 @@ static int nsp32_detect(struct pci_dev *pdev)
/*
* allocate scatter-gather DMA resource.
*/
- data->sg_list = pci_alloc_consistent(pdev, NSP32_SG_TABLE_SIZE,
- &(data->sg_paddr));
+ data->sg_list = dma_alloc_coherent(&pdev->dev, NSP32_SG_TABLE_SIZE,
+ &data->sg_paddr, GFP_KERNEL);
if (data->sg_list == NULL) {
nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
goto free_autoparam;
@@ -2768,16 +2725,16 @@ static int nsp32_detect(struct pci_dev *pdev)
goto free_sg_list;
}
- /*
+ /*
* PCI IO register
*/
res = request_region(host->io_port, host->n_io_port, "nsp32");
if (res == NULL) {
- nsp32_msg(KERN_ERR,
- "I/O region 0x%lx+0x%lx is already used",
+ nsp32_msg(KERN_ERR,
+ "I/O region 0x%x+0x%x is already used",
data->BaseAddress, data->NumAddress);
goto free_irq;
- }
+ }
ret = scsi_add_host(host, &pdev->dev);
if (ret) {
@@ -2795,13 +2752,13 @@ static int nsp32_detect(struct pci_dev *pdev)
free_irq(host->irq, data);
free_sg_list:
- pci_free_consistent(pdev, NSP32_SG_TABLE_SIZE,
+ dma_free_coherent(&pdev->dev, NSP32_SG_TABLE_SIZE,
data->sg_list, data->sg_paddr);
free_autoparam:
- pci_free_consistent(pdev, sizeof(nsp32_autoparam),
+ dma_free_coherent(&pdev->dev, sizeof(nsp32_autoparam),
data->autoparam, data->auto_paddr);
-
+
scsi_unregister:
scsi_host_put(host);
@@ -2814,12 +2771,12 @@ static int nsp32_release(struct Scsi_Host *host)
nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
if (data->autoparam) {
- pci_free_consistent(data->Pci, sizeof(nsp32_autoparam),
+ dma_free_coherent(&data->Pci->dev, sizeof(nsp32_autoparam),
data->autoparam, data->auto_paddr);
}
if (data->sg_list) {
- pci_free_consistent(data->Pci, NSP32_SG_TABLE_SIZE,
+ dma_free_coherent(&data->Pci->dev, NSP32_SG_TABLE_SIZE,
data->sg_list, data->sg_paddr);
}
@@ -2868,7 +2825,7 @@ static int nsp32_eh_abort(struct scsi_cmnd *SCpnt)
}
nsp32_write2(base, TRANSFER_CONTROL, 0);
- nsp32_write2(base, BM_CNT, 0);
+ nsp32_write2(base, BM_CNT, 0);
SCpnt->result = DID_ABORT << 16;
nsp32_scsi_done(SCpnt);
@@ -2877,29 +2834,11 @@ static int nsp32_eh_abort(struct scsi_cmnd *SCpnt)
return SUCCESS;
}
-static int nsp32_eh_bus_reset(struct scsi_cmnd *SCpnt)
-{
- nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
- unsigned int base = SCpnt->device->host->io_port;
-
- spin_lock_irq(SCpnt->device->host->host_lock);
-
- nsp32_msg(KERN_INFO, "Bus Reset");
- nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
-
- nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
- nsp32_do_bus_reset(data);
- nsp32_write2(base, IRQ_CONTROL, 0);
-
- spin_unlock_irq(SCpnt->device->host->host_lock);
- return SUCCESS; /* SCSI bus reset is succeeded at any time. */
-}
-
static void nsp32_do_bus_reset(nsp32_hw_data *data)
{
unsigned int base = data->BaseAddress;
- unsigned short intrdat;
int i;
+ unsigned short __maybe_unused intrdat;
nsp32_dbg(NSP32_DEBUG_BUSRESET, "in");
@@ -2909,8 +2848,8 @@ static void nsp32_do_bus_reset(nsp32_hw_data *data)
* clear counter
*/
nsp32_write2(base, TRANSFER_CONTROL, 0);
- nsp32_write4(base, BM_CNT, 0);
- nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+ nsp32_write4(base, BM_CNT, 0);
+ nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
/*
* fall back to asynchronous transfer mode
@@ -2927,12 +2866,12 @@ static void nsp32_do_bus_reset(nsp32_hw_data *data)
* reset SCSI bus
*/
nsp32_write1(base, SCSI_BUS_CONTROL, BUSCTL_RST);
- udelay(RESET_HOLD_TIME);
+ mdelay(RESET_HOLD_TIME / 1000);
nsp32_write1(base, SCSI_BUS_CONTROL, 0);
for(i = 0; i < 5; i++) {
intrdat = nsp32_read2(base, IRQ_STATUS); /* dummy read */
nsp32_dbg(NSP32_DEBUG_BUSRESET, "irq:1: 0x%x", intrdat);
- }
+ }
data->CurrentSC = NULL;
}
@@ -2943,7 +2882,7 @@ static int nsp32_eh_host_reset(struct scsi_cmnd *SCpnt)
unsigned int base = SCpnt->device->host->io_port;
nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
- nsp32_msg(KERN_INFO, "Host Reset");
+ nsp32_msg(KERN_INFO, "Host Reset");
nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
spin_lock_irq(SCpnt->device->host->host_lock);
@@ -2969,7 +2908,8 @@ static int nsp32_getprom_param(nsp32_hw_data *data)
{
int vendor = data->pci_devid->vendor;
int device = data->pci_devid->device;
- int ret, val, i;
+ int ret, i;
+ int __maybe_unused val;
/*
* EEPROM checking.
@@ -3017,13 +2957,13 @@ static int nsp32_getprom_param(nsp32_hw_data *data)
* AT24C01A (Logitec: LHA-600S), AT24C02 (Melco Buffalo: IFC-USLP) data map:
*
* ROMADDR
- * 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
+ * 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
* Value 0x0: ASYNC, 0x0c: Ultra-20M, 0x19: Fast-10M
* 0x07 : HBA Synchronous Transfer Period
* Value 0: AutoSync, 1: Manual Setting
* 0x08 - 0x0f : Not Used? (0x0)
* 0x10 : Bus Termination
- * Value 0: Auto[ON], 1: ON, 2: OFF
+ * Value 0: Auto[ON], 1: ON, 2: OFF
* 0x11 : Not Used? (0)
* 0x12 : Bus Reset Delay Time (0x03)
* 0x13 : Bootable CD Support
@@ -3031,7 +2971,7 @@ static int nsp32_getprom_param(nsp32_hw_data *data)
* 0x14 : Device Scan
* Bit 7 6 5 4 3 2 1 0
* | <----------------->
- * | SCSI ID: Value 0: Skip, 1: YES
+ * | SCSI ID: Value 0: Skip, 1: YES
* |-> Value 0: ALL scan, Value 1: Manual
* 0x15 - 0x1b : Not Used? (0)
* 0x1c : Constant? (0x01) (clock div?)
@@ -3042,10 +2982,10 @@ static int nsp32_getprom_param(nsp32_hw_data *data)
*/
static int nsp32_getprom_at24(nsp32_hw_data *data)
{
- int ret, i;
- int auto_sync;
+ int ret, i;
+ int auto_sync;
nsp32_target *target;
- int entry;
+ int entry;
/*
* Reset time which is designated by EEPROM.
@@ -3111,7 +3051,7 @@ static int nsp32_getprom_at24(nsp32_hw_data *data)
* C16 110 (I-O Data: SC-NBD) data map:
*
* ROMADDR
- * 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
+ * 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
* Value 0x0: 20MB/S, 0x1: 10MB/S, 0x2: 5MB/S, 0x3: ASYNC
* 0x07 : 0 (HBA Synchronous Transfer Period: Auto Sync)
* 0x08 - 0x0f : Not Used? (0x0)
@@ -3119,7 +3059,7 @@ static int nsp32_getprom_at24(nsp32_hw_data *data)
* Value 0: PIO, 1: Busmater
* 0x11 : Bus Reset Delay Time (0x00-0x20)
* 0x12 : Bus Termination
- * Value 0: Disable, 1: Enable
+ * Value 0: Disable, 1: Enable
* 0x13 - 0x19 : Disconnection
* Value 0: Disable, 1: Enable
* 0x1a - 0x7c : Not Used? (0)
@@ -3129,9 +3069,9 @@ static int nsp32_getprom_at24(nsp32_hw_data *data)
*/
static int nsp32_getprom_c16(nsp32_hw_data *data)
{
- int ret, i;
+ int ret, i;
nsp32_target *target;
- int entry, val;
+ int entry, val;
/*
* Reset time which is designated by EEPROM.
@@ -3231,7 +3171,7 @@ static int nsp32_prom_read(nsp32_hw_data *data, int romaddr)
for (i = 7; i >= 0; i--) {
val += (nsp32_prom_read_bit(data) << i);
}
-
+
/* no ack */
nsp32_prom_write_bit(data, 1);
@@ -3339,7 +3279,8 @@ static int nsp32_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct Scsi_Host *host = pci_get_drvdata(pdev);
- nsp32_msg(KERN_INFO, "pci-suspend: pdev=0x%p, state=%ld, slot=%s, host=0x%p", pdev, state, pci_name(pdev), host);
+ nsp32_msg(KERN_INFO, "pci-suspend: pdev=0x%p, state.event=%x, slot=%s, host=0x%p",
+ pdev, state.event, pci_name(pdev), host);
pci_save_state (pdev);
pci_disable_device (pdev);
@@ -3355,7 +3296,8 @@ static int nsp32_resume(struct pci_dev *pdev)
nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
unsigned short reg;
- nsp32_msg(KERN_INFO, "pci-resume: pdev=0x%p, slot=%s, host=0x%p", pdev, pci_name(pdev), host);
+ nsp32_msg(KERN_INFO, "pci-resume: pdev=0x%p, slot=%s, host=0x%p",
+ pdev, pci_name(pdev), host);
pci_set_power_state(pdev, PCI_D0);
pci_enable_wake (pdev, PCI_D0, 0);
@@ -3383,20 +3325,20 @@ static int nsp32_resume(struct pci_dev *pdev)
/************************************************************************
* PCI/Cardbus probe/remove routine
*/
-static int __devinit nsp32_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int nsp32_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret;
nsp32_hw_data *data = &nsp32_data_base;
nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
- ret = pci_enable_device(pdev);
+ ret = pci_enable_device(pdev);
if (ret) {
nsp32_msg(KERN_ERR, "failed to enable pci device");
return ret;
}
- data->Pci = pdev;
+ data->Pci = pdev;
data->pci_devid = id;
data->IrqNumber = pdev->irq;
data->BaseAddress = pci_resource_start(pdev, 0);
@@ -3419,13 +3361,13 @@ static int __devinit nsp32_probe(struct pci_dev *pdev, const struct pci_device_i
return ret;
}
-static void __devexit nsp32_remove(struct pci_dev *pdev)
+static void nsp32_remove(struct pci_dev *pdev)
{
struct Scsi_Host *host = pci_get_drvdata(pdev);
nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
- scsi_remove_host(host);
+ scsi_remove_host(host);
nsp32_release(host);
@@ -3436,10 +3378,10 @@ static struct pci_driver nsp32_driver = {
.name = "nsp32",
.id_table = nsp32_pci_table,
.probe = nsp32_probe,
- .remove = __devexit_p(nsp32_remove),
+ .remove = nsp32_remove,
#ifdef CONFIG_PM
- .suspend = nsp32_suspend,
- .resume = nsp32_resume,
+ .suspend = nsp32_suspend,
+ .resume = nsp32_resume,
#endif
};
diff --git a/drivers/scsi/nsp32.h b/drivers/scsi/nsp32.h
index c0221829069c..924889f8bd37 100644
--- a/drivers/scsi/nsp32.h
+++ b/drivers/scsi/nsp32.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Workbit NinjaSCSI-32Bi/UDE PCI/CardBus SCSI Host Bus Adapter driver
* Basic data header
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef _NSP32_H
@@ -543,6 +534,15 @@ typedef struct _nsp32_sync_table {
---PERIOD-- ---OFFSET-- */
#define TO_SYNCREG(period, offset) (((period) & 0x0f) << 4 | ((offset) & 0x0f))
+struct nsp32_cmd_priv {
+ enum sam_status status;
+};
+
+static inline struct nsp32_cmd_priv *nsp32_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
typedef struct _nsp32_target {
unsigned char syncreg; /* value for SYNCREG */
unsigned char ackwidth; /* value for ACKWIDTH */
diff --git a/drivers/scsi/nsp32_debug.c b/drivers/scsi/nsp32_debug.c
index 58806f432a16..4f1d4bf9c775 100644
--- a/drivers/scsi/nsp32_debug.c
+++ b/drivers/scsi/nsp32_debug.c
@@ -137,7 +137,7 @@ static void print_commandk (unsigned char *command)
printk("\n");
}
-static void show_command(Scsi_Cmnd *SCpnt)
+static void show_command(struct scsi_cmnd *SCpnt)
{
print_commandk(SCpnt->cmnd);
}
diff --git a/drivers/scsi/osd/Kbuild b/drivers/scsi/osd/Kbuild
deleted file mode 100644
index 5fd73d77c3af..000000000000
--- a/drivers/scsi/osd/Kbuild
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Kbuild for the OSD modules
-#
-# Copyright (C) 2008 Panasas Inc. All rights reserved.
-#
-# Authors:
-# Boaz Harrosh <bharrosh@panasas.com>
-# Benny Halevy <bhalevy@panasas.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2
-#
-
-# libosd.ko - osd-initiator library
-libosd-y := osd_initiator.o
-obj-$(CONFIG_SCSI_OSD_INITIATOR) += libosd.o
-
-# osd.ko - SCSI ULD and char-device
-osd-y := osd_uld.o
-obj-$(CONFIG_SCSI_OSD_ULD) += osd.o
diff --git a/drivers/scsi/osd/Kconfig b/drivers/scsi/osd/Kconfig
deleted file mode 100644
index 861b5cebaeae..000000000000
--- a/drivers/scsi/osd/Kconfig
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Kernel configuration file for the OSD scsi protocol
-#
-# Copyright (C) 2008 Panasas Inc. All rights reserved.
-#
-# Authors:
-# Boaz Harrosh <bharrosh@panasas.com>
-# Benny Halevy <bhalevy@panasas.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public version 2 License as
-# published by the Free Software Foundation
-#
-# FIXME: SCSI_OSD_INITIATOR should select CONFIG (HMAC) SHA1 somehow.
-# How is it done properly?
-#
-
-config SCSI_OSD_INITIATOR
- tristate "OSD-Initiator library"
- depends on SCSI
- help
- Enable the OSD-Initiator library (libosd.ko).
- NOTE: You must also select CRYPTO_SHA1 + CRYPTO_HMAC and their
- dependencies
-
-config SCSI_OSD_ULD
- tristate "OSD Upper Level driver"
- depends on SCSI_OSD_INITIATOR
- help
- Build a SCSI upper layer driver that exports /dev/osdX devices
- to user-mode for testing and controlling OSD devices. It is also
- needed by exofs, for mounting an OSD based file system.
-
-config SCSI_OSD_DPRINT_SENSE
- int "(0-2) When sense is returned, DEBUG print all sense descriptors"
- default 1
- depends on SCSI_OSD_INITIATOR
- help
- When a CHECK_CONDITION status is returned from a target, and a
- sense-buffer is retrieved, turning this on will dump a full
- sense-decoding message. Setting to 2 will also print recoverable
- errors that might be regularly returned for some filesystem
- operations.
-
-config SCSI_OSD_DEBUG
- bool "Compile All OSD modules with lots of DEBUG prints"
- default n
- depends on SCSI_OSD_INITIATOR
- help
- OSD Code is populated with lots of OSD_DEBUG(..) printouts to
- dmesg. Enable this if you found a bug and you want to help us
- track the problem (see also MAINTAINERS). Setting this will also
- force SCSI_OSD_DPRINT_SENSE=2.
diff --git a/drivers/scsi/osd/osd_debug.h b/drivers/scsi/osd/osd_debug.h
deleted file mode 100644
index 579e491f11df..000000000000
--- a/drivers/scsi/osd/osd_debug.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * osd_debug.h - Some kprintf macros
- *
- * Copyright (C) 2008 Panasas Inc. All rights reserved.
- *
- * Authors:
- * Boaz Harrosh <bharrosh@panasas.com>
- * Benny Halevy <bhalevy@panasas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- *
- */
-#ifndef __OSD_DEBUG_H__
-#define __OSD_DEBUG_H__
-
-#define OSD_ERR(fmt, a...) printk(KERN_ERR "osd: " fmt, ##a)
-#define OSD_INFO(fmt, a...) printk(KERN_NOTICE "osd: " fmt, ##a)
-
-#ifdef CONFIG_SCSI_OSD_DEBUG
-#define OSD_DEBUG(fmt, a...) \
- printk(KERN_NOTICE "osd @%s:%d: " fmt, __func__, __LINE__, ##a)
-#else
-#define OSD_DEBUG(fmt, a...) do {} while (0)
-#endif
-
-/* u64 has problems with printk this will cast it to unsigned long long */
-#define _LLU(x) (unsigned long long)(x)
-
-#endif /* ndef __OSD_DEBUG_H__ */
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
deleted file mode 100644
index 86afb13f1e79..000000000000
--- a/drivers/scsi/osd/osd_initiator.c
+++ /dev/null
@@ -1,2066 +0,0 @@
-/*
- * osd_initiator - Main body of the osd initiator library.
- *
- * Note: The file does not contain the advanced security functionality which
- * is only needed by the security_manager's initiators.
- *
- * Copyright (C) 2008 Panasas Inc. All rights reserved.
- *
- * Authors:
- * Boaz Harrosh <bharrosh@panasas.com>
- * Benny Halevy <bhalevy@panasas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the Panasas company nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <linux/slab.h>
-
-#include <scsi/osd_initiator.h>
-#include <scsi/osd_sec.h>
-#include <scsi/osd_attributes.h>
-#include <scsi/osd_sense.h>
-
-#include <scsi/scsi_device.h>
-
-#include "osd_debug.h"
-
-#ifndef __unused
-# define __unused __attribute__((unused))
-#endif
-
-enum { OSD_REQ_RETRIES = 1 };
-
-MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>");
-MODULE_DESCRIPTION("open-osd initiator library libosd.ko");
-MODULE_LICENSE("GPL");
-
-static inline void build_test(void)
-{
- /* structures were not packed */
- BUILD_BUG_ON(sizeof(struct osd_capability) != OSD_CAP_LEN);
- BUILD_BUG_ON(sizeof(struct osdv2_cdb) != OSD_TOTAL_CDB_LEN);
- BUILD_BUG_ON(sizeof(struct osdv1_cdb) != OSDv1_TOTAL_CDB_LEN);
-}
-
-static const char *_osd_ver_desc(struct osd_request *or)
-{
- return osd_req_is_ver1(or) ? "OSD1" : "OSD2";
-}
-
-#define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
-
-static int _osd_get_print_system_info(struct osd_dev *od,
- void *caps, struct osd_dev_info *odi)
-{
- struct osd_request *or;
- struct osd_attr get_attrs[] = {
- ATTR_DEF_RI(OSD_ATTR_RI_VENDOR_IDENTIFICATION, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_IDENTIFICATION, 16),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_MODEL, 32),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_REVISION_LEVEL, 4),
- ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER, 64 /*variable*/),
- ATTR_DEF_RI(OSD_ATTR_RI_OSD_NAME, 64 /*variable*/),
- ATTR_DEF_RI(OSD_ATTR_RI_TOTAL_CAPACITY, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_USED_CAPACITY, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_NUMBER_OF_PARTITIONS, 8),
- ATTR_DEF_RI(OSD_ATTR_RI_CLOCK, 6),
- /* IBM-OSD-SIM Has a bug with this one put it last */
- ATTR_DEF_RI(OSD_ATTR_RI_OSD_SYSTEM_ID, 20),
- };
- void *iter = NULL, *pFirst;
- int nelem = ARRAY_SIZE(get_attrs), a = 0;
- int ret;
-
- or = osd_start_request(od, GFP_KERNEL);
- if (!or)
- return -ENOMEM;
-
- /* get attrs */
- osd_req_get_attributes(or, &osd_root_object);
- osd_req_add_get_attr_list(or, get_attrs, ARRAY_SIZE(get_attrs));
-
- ret = osd_finalize_request(or, 0, caps, NULL);
- if (ret)
- goto out;
-
- ret = osd_execute_request(or);
- if (ret) {
- OSD_ERR("Failed to detect %s => %d\n", _osd_ver_desc(or), ret);
- goto out;
- }
-
- osd_req_decode_get_attr_list(or, get_attrs, &nelem, &iter);
-
- OSD_INFO("Detected %s device\n",
- _osd_ver_desc(or));
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("VENDOR_IDENTIFICATION [%s]\n",
- (char *)pFirst);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_IDENTIFICATION [%s]\n",
- (char *)pFirst);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_MODEL [%s]\n",
- (char *)pFirst);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_REVISION_LEVEL [%u]\n",
- pFirst ? get_unaligned_be32(pFirst) : ~0U);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n",
- (char *)pFirst);
-
- odi->osdname_len = get_attrs[a].len;
- /* Avoid NULL for memcmp optimization 0-length is good enough */
- odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
- if (odi->osdname_len)
- memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
- OSD_INFO("OSD_NAME [%s]\n", odi->osdname);
- a++;
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("TOTAL_CAPACITY [0x%llx]\n",
- pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("USED_CAPACITY [0x%llx]\n",
- pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
-
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("NUMBER_OF_PARTITIONS [%llu]\n",
- pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
-
- if (a >= nelem)
- goto out;
-
- /* FIXME: Where are the time utilities */
- pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("CLOCK [0x%02x%02x%02x%02x%02x%02x]\n",
- ((char *)pFirst)[0], ((char *)pFirst)[1],
- ((char *)pFirst)[2], ((char *)pFirst)[3],
- ((char *)pFirst)[4], ((char *)pFirst)[5]);
-
- if (a < nelem) { /* IBM-OSD-SIM bug, Might not have it */
- unsigned len = get_attrs[a].len;
- char sid_dump[32*4 + 2]; /* 2nibbles+space+ASCII */
-
- hex_dump_to_buffer(get_attrs[a].val_ptr, len, 32, 1,
- sid_dump, sizeof(sid_dump), true);
- OSD_INFO("OSD_SYSTEM_ID(%d)\n"
- " [%s]\n", len, sid_dump);
-
- if (unlikely(len > sizeof(odi->systemid))) {
- OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
- "device idetification might not work\n", len);
- len = sizeof(odi->systemid);
- }
- odi->systemid_len = len;
- memcpy(odi->systemid, get_attrs[a].val_ptr, len);
- a++;
- }
-out:
- osd_end_request(or);
- return ret;
-}
-
-int osd_auto_detect_ver(struct osd_dev *od,
- void *caps, struct osd_dev_info *odi)
-{
- int ret;
-
- /* Auto-detect the osd version */
- ret = _osd_get_print_system_info(od, caps, odi);
- if (ret) {
- osd_dev_set_ver(od, OSD_VER1);
- OSD_DEBUG("converting to OSD1\n");
- ret = _osd_get_print_system_info(od, caps, odi);
- }
-
- return ret;
-}
-EXPORT_SYMBOL(osd_auto_detect_ver);
-
-static unsigned _osd_req_cdb_len(struct osd_request *or)
-{
- return osd_req_is_ver1(or) ? OSDv1_TOTAL_CDB_LEN : OSD_TOTAL_CDB_LEN;
-}
-
-static unsigned _osd_req_alist_elem_size(struct osd_request *or, unsigned len)
-{
- return osd_req_is_ver1(or) ?
- osdv1_attr_list_elem_size(len) :
- osdv2_attr_list_elem_size(len);
-}
-
-static void _osd_req_alist_elem_encode(struct osd_request *or,
- void *attr_last, const struct osd_attr *oa)
-{
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_element *attr = attr_last;
-
- attr->attr_page = cpu_to_be32(oa->attr_page);
- attr->attr_id = cpu_to_be32(oa->attr_id);
- attr->attr_bytes = cpu_to_be16(oa->len);
- memcpy(attr->attr_val, oa->val_ptr, oa->len);
- } else {
- struct osdv2_attributes_list_element *attr = attr_last;
-
- attr->attr_page = cpu_to_be32(oa->attr_page);
- attr->attr_id = cpu_to_be32(oa->attr_id);
- attr->attr_bytes = cpu_to_be16(oa->len);
- memcpy(attr->attr_val, oa->val_ptr, oa->len);
- }
-}
-
-static int _osd_req_alist_elem_decode(struct osd_request *or,
- void *cur_p, struct osd_attr *oa, unsigned max_bytes)
-{
- unsigned inc;
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_element *attr = cur_p;
-
- if (max_bytes < sizeof(*attr))
- return -1;
-
- oa->len = be16_to_cpu(attr->attr_bytes);
- inc = _osd_req_alist_elem_size(or, oa->len);
- if (inc > max_bytes)
- return -1;
-
- oa->attr_page = be32_to_cpu(attr->attr_page);
- oa->attr_id = be32_to_cpu(attr->attr_id);
-
- /* OSD1: On empty attributes we return a pointer to 2 bytes
- * of zeros. This keeps similar behaviour with OSD2.
- * (See below)
- */
- oa->val_ptr = likely(oa->len) ? attr->attr_val :
- (u8 *)&attr->attr_bytes;
- } else {
- struct osdv2_attributes_list_element *attr = cur_p;
-
- if (max_bytes < sizeof(*attr))
- return -1;
-
- oa->len = be16_to_cpu(attr->attr_bytes);
- inc = _osd_req_alist_elem_size(or, oa->len);
- if (inc > max_bytes)
- return -1;
-
- oa->attr_page = be32_to_cpu(attr->attr_page);
- oa->attr_id = be32_to_cpu(attr->attr_id);
-
- /* OSD2: For convenience, on empty attributes, we return 8 bytes
- * of zeros here. This keeps the same behaviour with OSD2r04,
- * and is nice with null terminating ASCII fields.
- * oa->val_ptr == NULL marks the end-of-list, or error.
- */
- oa->val_ptr = likely(oa->len) ? attr->attr_val : attr->reserved;
- }
- return inc;
-}
-
-static unsigned _osd_req_alist_size(struct osd_request *or, void *list_head)
-{
- return osd_req_is_ver1(or) ?
- osdv1_list_size(list_head) :
- osdv2_list_size(list_head);
-}
-
-static unsigned _osd_req_sizeof_alist_header(struct osd_request *or)
-{
- return osd_req_is_ver1(or) ?
- sizeof(struct osdv1_attributes_list_header) :
- sizeof(struct osdv2_attributes_list_header);
-}
-
-static void _osd_req_set_alist_type(struct osd_request *or,
- void *list, int list_type)
-{
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_header *attr_list = list;
-
- memset(attr_list, 0, sizeof(*attr_list));
- attr_list->type = list_type;
- } else {
- struct osdv2_attributes_list_header *attr_list = list;
-
- memset(attr_list, 0, sizeof(*attr_list));
- attr_list->type = list_type;
- }
-}
-
-static bool _osd_req_is_alist_type(struct osd_request *or,
- void *list, int list_type)
-{
- if (!list)
- return false;
-
- if (osd_req_is_ver1(or)) {
- struct osdv1_attributes_list_header *attr_list = list;
-
- return attr_list->type == list_type;
- } else {
- struct osdv2_attributes_list_header *attr_list = list;
-
- return attr_list->type == list_type;
- }
-}
-
-/* This is for List-objects not Attributes-Lists */
-static void _osd_req_encode_olist(struct osd_request *or,
- struct osd_obj_id_list *list)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
-
- if (osd_req_is_ver1(or)) {
- cdbh->v1.list_identifier = list->list_identifier;
- cdbh->v1.start_address = list->continuation_id;
- } else {
- cdbh->v2.list_identifier = list->list_identifier;
- cdbh->v2.start_address = list->continuation_id;
- }
-}
-
-static osd_cdb_offset osd_req_encode_offset(struct osd_request *or,
- u64 offset, unsigned *padding)
-{
- return __osd_encode_offset(offset, padding,
- osd_req_is_ver1(or) ?
- OSDv1_OFFSET_MIN_SHIFT : OSD_OFFSET_MIN_SHIFT,
- OSD_OFFSET_MAX_SHIFT);
-}
-
-static struct osd_security_parameters *
-_osd_req_sec_params(struct osd_request *or)
-{
- struct osd_cdb *ocdb = &or->cdb;
-
- if (osd_req_is_ver1(or))
- return (struct osd_security_parameters *)&ocdb->v1.sec_params;
- else
- return (struct osd_security_parameters *)&ocdb->v2.sec_params;
-}
-
-void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device)
-{
- memset(osdd, 0, sizeof(*osdd));
- osdd->scsi_device = scsi_device;
- osdd->def_timeout = BLK_DEFAULT_SG_TIMEOUT;
-#ifdef OSD_VER1_SUPPORT
- osdd->version = OSD_VER2;
-#endif
- /* TODO: Allocate pools for osd_request attributes ... */
-}
-EXPORT_SYMBOL(osd_dev_init);
-
-void osd_dev_fini(struct osd_dev *osdd)
-{
- /* TODO: De-allocate pools */
-
- osdd->scsi_device = NULL;
-}
-EXPORT_SYMBOL(osd_dev_fini);
-
-static struct osd_request *_osd_request_alloc(gfp_t gfp)
-{
- struct osd_request *or;
-
- /* TODO: Use mempool with one saved request */
- or = kzalloc(sizeof(*or), gfp);
- return or;
-}
-
-static void _osd_request_free(struct osd_request *or)
-{
- kfree(or);
-}
-
-struct osd_request *osd_start_request(struct osd_dev *dev, gfp_t gfp)
-{
- struct osd_request *or;
-
- or = _osd_request_alloc(gfp);
- if (!or)
- return NULL;
-
- or->osd_dev = dev;
- or->alloc_flags = gfp;
- or->timeout = dev->def_timeout;
- or->retries = OSD_REQ_RETRIES;
-
- return or;
-}
-EXPORT_SYMBOL(osd_start_request);
-
-static void _osd_free_seg(struct osd_request *or __unused,
- struct _osd_req_data_segment *seg)
-{
- if (!seg->buff || !seg->alloc_size)
- return;
-
- kfree(seg->buff);
- seg->buff = NULL;
- seg->alloc_size = 0;
-}
-
-static void _put_request(struct request *rq)
-{
- /*
- * If osd_finalize_request() was called but the request was not
- * executed through the block layer, then we must release BIOs.
- * TODO: Keep error code in or->async_error. Need to audit all
- * code paths.
- */
- if (unlikely(rq->bio))
- blk_end_request(rq, -ENOMEM, blk_rq_bytes(rq));
- else
- blk_put_request(rq);
-}
-
-void osd_end_request(struct osd_request *or)
-{
- struct request *rq = or->request;
-
- if (rq) {
- if (rq->next_rq) {
- _put_request(rq->next_rq);
- rq->next_rq = NULL;
- }
-
- _put_request(rq);
- }
-
- _osd_free_seg(or, &or->get_attr);
- _osd_free_seg(or, &or->enc_get_attr);
- _osd_free_seg(or, &or->set_attr);
- _osd_free_seg(or, &or->cdb_cont);
-
- _osd_request_free(or);
-}
-EXPORT_SYMBOL(osd_end_request);
-
-static void _set_error_resid(struct osd_request *or, struct request *req,
- int error)
-{
- or->async_error = error;
- or->req_errors = req->errors ? : error;
- or->sense_len = req->sense_len;
- if (or->out.req)
- or->out.residual = or->out.req->resid_len;
- if (or->in.req)
- or->in.residual = or->in.req->resid_len;
-}
-
-int osd_execute_request(struct osd_request *or)
-{
- int error = blk_execute_rq(or->request->q, NULL, or->request, 0);
-
- _set_error_resid(or, or->request, error);
- return error;
-}
-EXPORT_SYMBOL(osd_execute_request);
-
-static void osd_request_async_done(struct request *req, int error)
-{
- struct osd_request *or = req->end_io_data;
-
- _set_error_resid(or, req, error);
- if (req->next_rq) {
- __blk_put_request(req->q, req->next_rq);
- req->next_rq = NULL;
- }
-
- __blk_put_request(req->q, req);
- or->request = NULL;
- or->in.req = NULL;
- or->out.req = NULL;
-
- if (or->async_done)
- or->async_done(or, or->async_private);
- else
- osd_end_request(or);
-}
-
-int osd_execute_request_async(struct osd_request *or,
- osd_req_done_fn *done, void *private)
-{
- or->request->end_io_data = or;
- or->async_private = private;
- or->async_done = done;
-
- blk_execute_rq_nowait(or->request->q, NULL, or->request, 0,
- osd_request_async_done);
- return 0;
-}
-EXPORT_SYMBOL(osd_execute_request_async);
-
-u8 sg_out_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT];
-u8 sg_in_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT];
-
-static int _osd_realloc_seg(struct osd_request *or,
- struct _osd_req_data_segment *seg, unsigned max_bytes)
-{
- void *buff;
-
- if (seg->alloc_size >= max_bytes)
- return 0;
-
- buff = krealloc(seg->buff, max_bytes, or->alloc_flags);
- if (!buff) {
- OSD_ERR("Failed to Realloc %d-bytes was-%d\n", max_bytes,
- seg->alloc_size);
- return -ENOMEM;
- }
-
- memset(buff + seg->alloc_size, 0, max_bytes - seg->alloc_size);
- seg->buff = buff;
- seg->alloc_size = max_bytes;
- return 0;
-}
-
-static int _alloc_cdb_cont(struct osd_request *or, unsigned total_bytes)
-{
- OSD_DEBUG("total_bytes=%d\n", total_bytes);
- return _osd_realloc_seg(or, &or->cdb_cont, total_bytes);
-}
-
-static int _alloc_set_attr_list(struct osd_request *or,
- const struct osd_attr *oa, unsigned nelem, unsigned add_bytes)
-{
- unsigned total_bytes = add_bytes;
-
- for (; nelem; --nelem, ++oa)
- total_bytes += _osd_req_alist_elem_size(or, oa->len);
-
- OSD_DEBUG("total_bytes=%d\n", total_bytes);
- return _osd_realloc_seg(or, &or->set_attr, total_bytes);
-}
-
-static int _alloc_get_attr_desc(struct osd_request *or, unsigned max_bytes)
-{
- OSD_DEBUG("total_bytes=%d\n", max_bytes);
- return _osd_realloc_seg(or, &or->enc_get_attr, max_bytes);
-}
-
-static int _alloc_get_attr_list(struct osd_request *or)
-{
- OSD_DEBUG("total_bytes=%d\n", or->get_attr.total_bytes);
- return _osd_realloc_seg(or, &or->get_attr, or->get_attr.total_bytes);
-}
-
-/*
- * Common to all OSD commands
- */
-
-static void _osdv1_req_encode_common(struct osd_request *or,
- __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
-{
- struct osdv1_cdb *ocdb = &or->cdb.v1;
-
- /*
- * For speed, the commands
- * OSD_ACT_PERFORM_SCSI_COMMAND , V1 0x8F7E, V2 0x8F7C
- * OSD_ACT_SCSI_TASK_MANAGEMENT , V1 0x8F7F, V2 0x8F7D
- * are not supported here. Should pass zero and set after the call
- */
- act &= cpu_to_be16(~0x0080); /* V1 action code */
-
- OSD_DEBUG("OSDv1 execute opcode 0x%x\n", be16_to_cpu(act));
-
- ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD;
- ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH;
- ocdb->h.varlen_cdb.service_action = act;
-
- ocdb->h.partition = cpu_to_be64(obj->partition);
- ocdb->h.object = cpu_to_be64(obj->id);
- ocdb->h.v1.length = cpu_to_be64(len);
- ocdb->h.v1.start_address = cpu_to_be64(offset);
-}
-
-static void _osdv2_req_encode_common(struct osd_request *or,
- __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
-{
- struct osdv2_cdb *ocdb = &or->cdb.v2;
-
- OSD_DEBUG("OSDv2 execute opcode 0x%x\n", be16_to_cpu(act));
-
- ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD;
- ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH;
- ocdb->h.varlen_cdb.service_action = act;
-
- ocdb->h.partition = cpu_to_be64(obj->partition);
- ocdb->h.object = cpu_to_be64(obj->id);
- ocdb->h.v2.length = cpu_to_be64(len);
- ocdb->h.v2.start_address = cpu_to_be64(offset);
-}
-
-static void _osd_req_encode_common(struct osd_request *or,
- __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
-{
- if (osd_req_is_ver1(or))
- _osdv1_req_encode_common(or, act, obj, offset, len);
- else
- _osdv2_req_encode_common(or, act, obj, offset, len);
-}
-
-/*
- * Device commands
- */
-/*TODO: void osd_req_set_master_seed_xchg(struct osd_request *, ...); */
-/*TODO: void osd_req_set_master_key(struct osd_request *, ...); */
-
-void osd_req_format(struct osd_request *or, u64 tot_capacity)
-{
- _osd_req_encode_common(or, OSD_ACT_FORMAT_OSD, &osd_root_object, 0,
- tot_capacity);
-}
-EXPORT_SYMBOL(osd_req_format);
-
-int osd_req_list_dev_partitions(struct osd_request *or,
- osd_id initial_id, struct osd_obj_id_list *list, unsigned nelem)
-{
- return osd_req_list_partition_objects(or, 0, initial_id, list, nelem);
-}
-EXPORT_SYMBOL(osd_req_list_dev_partitions);
-
-static void _osd_req_encode_flush(struct osd_request *or,
- enum osd_options_flush_scope_values op)
-{
- struct osd_cdb_head *ocdb = osd_cdb_head(&or->cdb);
-
- ocdb->command_specific_options = op;
-}
-
-void osd_req_flush_obsd(struct osd_request *or,
- enum osd_options_flush_scope_values op)
-{
- _osd_req_encode_common(or, OSD_ACT_FLUSH_OSD, &osd_root_object, 0, 0);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_obsd);
-
-/*TODO: void osd_req_perform_scsi_command(struct osd_request *,
- const u8 *cdb, ...); */
-/*TODO: void osd_req_task_management(struct osd_request *, ...); */
-
-/*
- * Partition commands
- */
-static void _osd_req_encode_partition(struct osd_request *or,
- __be16 act, osd_id partition)
-{
- struct osd_obj_id par = {
- .partition = partition,
- .id = 0,
- };
-
- _osd_req_encode_common(or, act, &par, 0, 0);
-}
-
-void osd_req_create_partition(struct osd_request *or, osd_id partition)
-{
- _osd_req_encode_partition(or, OSD_ACT_CREATE_PARTITION, partition);
-}
-EXPORT_SYMBOL(osd_req_create_partition);
-
-void osd_req_remove_partition(struct osd_request *or, osd_id partition)
-{
- _osd_req_encode_partition(or, OSD_ACT_REMOVE_PARTITION, partition);
-}
-EXPORT_SYMBOL(osd_req_remove_partition);
-
-/*TODO: void osd_req_set_partition_key(struct osd_request *,
- osd_id partition, u8 new_key_id[OSD_CRYPTO_KEYID_SIZE],
- u8 seed[OSD_CRYPTO_SEED_SIZE]); */
-
-static int _osd_req_list_objects(struct osd_request *or,
- __be16 action, const struct osd_obj_id *obj, osd_id initial_id,
- struct osd_obj_id_list *list, unsigned nelem)
-{
- struct request_queue *q = osd_request_queue(or->osd_dev);
- u64 len = nelem * sizeof(osd_id) + sizeof(*list);
- struct bio *bio;
-
- _osd_req_encode_common(or, action, obj, (u64)initial_id, len);
-
- if (list->list_identifier)
- _osd_req_encode_olist(or, list);
-
- WARN_ON(or->in.bio);
- bio = bio_map_kern(q, list, len, or->alloc_flags);
- if (IS_ERR(bio)) {
- OSD_ERR("!!! Failed to allocate list_objects BIO\n");
- return PTR_ERR(bio);
- }
-
- bio->bi_rw &= ~REQ_WRITE;
- or->in.bio = bio;
- or->in.total_bytes = bio->bi_size;
- return 0;
-}
-
-int osd_req_list_partition_collections(struct osd_request *or,
- osd_id partition, osd_id initial_id, struct osd_obj_id_list *list,
- unsigned nelem)
-{
- struct osd_obj_id par = {
- .partition = partition,
- .id = 0,
- };
-
- return osd_req_list_collection_objects(or, &par, initial_id, list,
- nelem);
-}
-EXPORT_SYMBOL(osd_req_list_partition_collections);
-
-int osd_req_list_partition_objects(struct osd_request *or,
- osd_id partition, osd_id initial_id, struct osd_obj_id_list *list,
- unsigned nelem)
-{
- struct osd_obj_id par = {
- .partition = partition,
- .id = 0,
- };
-
- return _osd_req_list_objects(or, OSD_ACT_LIST, &par, initial_id, list,
- nelem);
-}
-EXPORT_SYMBOL(osd_req_list_partition_objects);
-
-void osd_req_flush_partition(struct osd_request *or,
- osd_id partition, enum osd_options_flush_scope_values op)
-{
- _osd_req_encode_partition(or, OSD_ACT_FLUSH_PARTITION, partition);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_partition);
-
-/*
- * Collection commands
- */
-/*TODO: void osd_req_create_collection(struct osd_request *,
- const struct osd_obj_id *); */
-/*TODO: void osd_req_remove_collection(struct osd_request *,
- const struct osd_obj_id *); */
-
-int osd_req_list_collection_objects(struct osd_request *or,
- const struct osd_obj_id *obj, osd_id initial_id,
- struct osd_obj_id_list *list, unsigned nelem)
-{
- return _osd_req_list_objects(or, OSD_ACT_LIST_COLLECTION, obj,
- initial_id, list, nelem);
-}
-EXPORT_SYMBOL(osd_req_list_collection_objects);
-
-/*TODO: void query(struct osd_request *, ...); V2 */
-
-void osd_req_flush_collection(struct osd_request *or,
- const struct osd_obj_id *obj, enum osd_options_flush_scope_values op)
-{
- _osd_req_encode_common(or, OSD_ACT_FLUSH_PARTITION, obj, 0, 0);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_collection);
-
-/*TODO: void get_member_attrs(struct osd_request *, ...); V2 */
-/*TODO: void set_member_attrs(struct osd_request *, ...); V2 */
-
-/*
- * Object commands
- */
-void osd_req_create_object(struct osd_request *or, struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_CREATE, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_create_object);
-
-void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_REMOVE, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_remove_object);
-
-
-/*TODO: void osd_req_create_multi(struct osd_request *or,
- struct osd_obj_id *first, struct osd_obj_id_list *list, unsigned nelem);
-*/
-
-void osd_req_write(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset,
- struct bio *bio, u64 len)
-{
- _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len);
- WARN_ON(or->out.bio || or->out.total_bytes);
- WARN_ON(0 == (bio->bi_rw & REQ_WRITE));
- or->out.bio = bio;
- or->out.total_bytes = len;
-}
-EXPORT_SYMBOL(osd_req_write);
-
-int osd_req_write_kern(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
-{
- struct request_queue *req_q = osd_request_queue(or->osd_dev);
- struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
-
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- bio->bi_rw |= REQ_WRITE; /* FIXME: bio_set_dir() */
- osd_req_write(or, obj, offset, bio, len);
- return 0;
-}
-EXPORT_SYMBOL(osd_req_write_kern);
-
-/*TODO: void osd_req_append(struct osd_request *,
- const struct osd_obj_id *, struct bio *data_out); */
-/*TODO: void osd_req_create_write(struct osd_request *,
- const struct osd_obj_id *, struct bio *data_out, u64 offset); */
-/*TODO: void osd_req_clear(struct osd_request *,
- const struct osd_obj_id *, u64 offset, u64 len); */
-/*TODO: void osd_req_punch(struct osd_request *,
- const struct osd_obj_id *, u64 offset, u64 len); V2 */
-
-void osd_req_flush_object(struct osd_request *or,
- const struct osd_obj_id *obj, enum osd_options_flush_scope_values op,
- /*V2*/ u64 offset, /*V2*/ u64 len)
-{
- if (unlikely(osd_req_is_ver1(or) && (offset || len))) {
- OSD_DEBUG("OSD Ver1 flush on specific range ignored\n");
- offset = 0;
- len = 0;
- }
-
- _osd_req_encode_common(or, OSD_ACT_FLUSH, obj, offset, len);
- _osd_req_encode_flush(or, op);
-}
-EXPORT_SYMBOL(osd_req_flush_object);
-
-void osd_req_read(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset,
- struct bio *bio, u64 len)
-{
- _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len);
- WARN_ON(or->in.bio || or->in.total_bytes);
- WARN_ON(bio->bi_rw & REQ_WRITE);
- or->in.bio = bio;
- or->in.total_bytes = len;
-}
-EXPORT_SYMBOL(osd_req_read);
-
-int osd_req_read_kern(struct osd_request *or,
- const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
-{
- struct request_queue *req_q = osd_request_queue(or->osd_dev);
- struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
-
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- osd_req_read(or, obj, offset, bio, len);
- return 0;
-}
-EXPORT_SYMBOL(osd_req_read_kern);
-
-static int _add_sg_continuation_descriptor(struct osd_request *or,
- const struct osd_sg_entry *sglist, unsigned numentries, u64 *len)
-{
- struct osd_sg_continuation_descriptor *oscd;
- u32 oscd_size;
- unsigned i;
- int ret;
-
- oscd_size = sizeof(*oscd) + numentries * sizeof(oscd->entries[0]);
-
- if (!or->cdb_cont.total_bytes) {
- /* First time, jump over the header, we will write to:
- * cdb_cont.buff + cdb_cont.total_bytes
- */
- or->cdb_cont.total_bytes =
- sizeof(struct osd_continuation_segment_header);
- }
-
- ret = _alloc_cdb_cont(or, or->cdb_cont.total_bytes + oscd_size);
- if (unlikely(ret))
- return ret;
-
- oscd = or->cdb_cont.buff + or->cdb_cont.total_bytes;
- oscd->hdr.type = cpu_to_be16(SCATTER_GATHER_LIST);
- oscd->hdr.pad_length = 0;
- oscd->hdr.length = cpu_to_be32(oscd_size - sizeof(*oscd));
-
- *len = 0;
- /* copy the sg entries and convert to network byte order */
- for (i = 0; i < numentries; i++) {
- oscd->entries[i].offset = cpu_to_be64(sglist[i].offset);
- oscd->entries[i].len = cpu_to_be64(sglist[i].len);
- *len += sglist[i].len;
- }
-
- or->cdb_cont.total_bytes += oscd_size;
- OSD_DEBUG("total_bytes=%d oscd_size=%d numentries=%d\n",
- or->cdb_cont.total_bytes, oscd_size, numentries);
- return 0;
-}
-
-static int _osd_req_finalize_cdb_cont(struct osd_request *or, const u8 *cap_key)
-{
- struct request_queue *req_q = osd_request_queue(or->osd_dev);
- struct bio *bio;
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- struct osd_continuation_segment_header *cont_seg_hdr;
-
- if (!or->cdb_cont.total_bytes)
- return 0;
-
- cont_seg_hdr = or->cdb_cont.buff;
- cont_seg_hdr->format = CDB_CONTINUATION_FORMAT_V2;
- cont_seg_hdr->service_action = cdbh->varlen_cdb.service_action;
-
- /* create a bio for continuation segment */
- bio = bio_map_kern(req_q, or->cdb_cont.buff, or->cdb_cont.total_bytes,
- GFP_KERNEL);
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- bio->bi_rw |= REQ_WRITE;
-
- /* integrity check the continuation before the bio is linked
- * with the other data segments since the continuation
- * integrity is separate from the other data segments.
- */
- osd_sec_sign_data(cont_seg_hdr->integrity_check, bio, cap_key);
-
- cdbh->v2.cdb_continuation_length = cpu_to_be32(or->cdb_cont.total_bytes);
-
- /* we can't use _req_append_segment, because we need to link in the
- * continuation bio to the head of the bio list - the
- * continuation segment (if it exists) is always the first segment in
- * the out data buffer.
- */
- bio->bi_next = or->out.bio;
- or->out.bio = bio;
- or->out.total_bytes += or->cdb_cont.total_bytes;
-
- return 0;
-}
-
-/* osd_req_write_sg: Takes a @bio that points to the data out buffer and an
- * @sglist that has the scatter gather entries. Scatter-gather enables a write
- * of multiple none-contiguous areas of an object, in a single call. The extents
- * may overlap and/or be in any order. The only constrain is that:
- * total_bytes(sglist) >= total_bytes(bio)
- */
-int osd_req_write_sg(struct osd_request *or,
- const struct osd_obj_id *obj, struct bio *bio,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- u64 len;
- int ret = _add_sg_continuation_descriptor(or, sglist, numentries, &len);
-
- if (ret)
- return ret;
- osd_req_write(or, obj, 0, bio, len);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_write_sg);
-
-/* osd_req_read_sg: Read multiple extents of an object into @bio
- * See osd_req_write_sg
- */
-int osd_req_read_sg(struct osd_request *or,
- const struct osd_obj_id *obj, struct bio *bio,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- u64 len;
- u64 off;
- int ret;
-
- if (numentries > 1) {
- off = 0;
- ret = _add_sg_continuation_descriptor(or, sglist, numentries,
- &len);
- if (ret)
- return ret;
- } else {
- /* Optimize the case of single segment, read_sg is a
- * bidi operation.
- */
- len = sglist->len;
- off = sglist->offset;
- }
- osd_req_read(or, obj, off, bio, len);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_read_sg);
-
-/* SG-list write/read Kern API
- *
- * osd_req_{write,read}_sg_kern takes an array of @buff pointers and an array
- * of sg_entries. @numentries indicates how many pointers and sg_entries there
- * are. By requiring an array of buff pointers. This allows a caller to do a
- * single write/read and scatter into multiple buffers.
- * NOTE: Each buffer + len should not cross a page boundary.
- */
-static struct bio *_create_sg_bios(struct osd_request *or,
- void **buff, const struct osd_sg_entry *sglist, unsigned numentries)
-{
- struct request_queue *q = osd_request_queue(or->osd_dev);
- struct bio *bio;
- unsigned i;
-
- bio = bio_kmalloc(GFP_KERNEL, numentries);
- if (unlikely(!bio)) {
- OSD_DEBUG("Faild to allocate BIO size=%u\n", numentries);
- return ERR_PTR(-ENOMEM);
- }
-
- for (i = 0; i < numentries; i++) {
- unsigned offset = offset_in_page(buff[i]);
- struct page *page = virt_to_page(buff[i]);
- unsigned len = sglist[i].len;
- unsigned added_len;
-
- BUG_ON(offset + len > PAGE_SIZE);
- added_len = bio_add_pc_page(q, bio, page, len, offset);
- if (unlikely(len != added_len)) {
- OSD_DEBUG("bio_add_pc_page len(%d) != added_len(%d)\n",
- len, added_len);
- bio_put(bio);
- return ERR_PTR(-ENOMEM);
- }
- }
-
- return bio;
-}
-
-int osd_req_write_sg_kern(struct osd_request *or,
- const struct osd_obj_id *obj, void **buff,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- struct bio *bio = _create_sg_bios(or, buff, sglist, numentries);
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- bio->bi_rw |= REQ_WRITE;
- osd_req_write_sg(or, obj, bio, sglist, numentries);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_write_sg_kern);
-
-int osd_req_read_sg_kern(struct osd_request *or,
- const struct osd_obj_id *obj, void **buff,
- const struct osd_sg_entry *sglist, unsigned numentries)
-{
- struct bio *bio = _create_sg_bios(or, buff, sglist, numentries);
- if (IS_ERR(bio))
- return PTR_ERR(bio);
-
- osd_req_read_sg(or, obj, bio, sglist, numentries);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_read_sg_kern);
-
-
-
-void osd_req_get_attributes(struct osd_request *or,
- const struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_GET_ATTRIBUTES, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_get_attributes);
-
-void osd_req_set_attributes(struct osd_request *or,
- const struct osd_obj_id *obj)
-{
- _osd_req_encode_common(or, OSD_ACT_SET_ATTRIBUTES, obj, 0, 0);
-}
-EXPORT_SYMBOL(osd_req_set_attributes);
-
-/*
- * Attributes List-mode
- */
-
-int osd_req_add_set_attr_list(struct osd_request *or,
- const struct osd_attr *oa, unsigned nelem)
-{
- unsigned total_bytes = or->set_attr.total_bytes;
- void *attr_last;
- int ret;
-
- if (or->attributes_mode &&
- or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) {
- WARN_ON(1);
- return -EINVAL;
- }
- or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
-
- if (!total_bytes) { /* first-time: allocate and put list header */
- total_bytes = _osd_req_sizeof_alist_header(or);
- ret = _alloc_set_attr_list(or, oa, nelem, total_bytes);
- if (ret)
- return ret;
- _osd_req_set_alist_type(or, or->set_attr.buff,
- OSD_ATTR_LIST_SET_RETRIEVE);
- }
- attr_last = or->set_attr.buff + total_bytes;
-
- for (; nelem; --nelem) {
- unsigned elem_size = _osd_req_alist_elem_size(or, oa->len);
-
- total_bytes += elem_size;
- if (unlikely(or->set_attr.alloc_size < total_bytes)) {
- or->set_attr.total_bytes = total_bytes - elem_size;
- ret = _alloc_set_attr_list(or, oa, nelem, total_bytes);
- if (ret)
- return ret;
- attr_last =
- or->set_attr.buff + or->set_attr.total_bytes;
- }
-
- _osd_req_alist_elem_encode(or, attr_last, oa);
-
- attr_last += elem_size;
- ++oa;
- }
-
- or->set_attr.total_bytes = total_bytes;
- return 0;
-}
-EXPORT_SYMBOL(osd_req_add_set_attr_list);
-
-static int _req_append_segment(struct osd_request *or,
- unsigned padding, struct _osd_req_data_segment *seg,
- struct _osd_req_data_segment *last_seg, struct _osd_io_info *io)
-{
- void *pad_buff;
- int ret;
-
- if (padding) {
- /* check if we can just add it to last buffer */
- if (last_seg &&
- (padding <= last_seg->alloc_size - last_seg->total_bytes))
- pad_buff = last_seg->buff + last_seg->total_bytes;
- else
- pad_buff = io->pad_buff;
-
- ret = blk_rq_map_kern(io->req->q, io->req, pad_buff, padding,
- or->alloc_flags);
- if (ret)
- return ret;
- io->total_bytes += padding;
- }
-
- ret = blk_rq_map_kern(io->req->q, io->req, seg->buff, seg->total_bytes,
- or->alloc_flags);
- if (ret)
- return ret;
-
- io->total_bytes += seg->total_bytes;
- OSD_DEBUG("padding=%d buff=%p total_bytes=%d\n", padding, seg->buff,
- seg->total_bytes);
- return 0;
-}
-
-static int _osd_req_finalize_set_attr_list(struct osd_request *or)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- unsigned padding;
- int ret;
-
- if (!or->set_attr.total_bytes) {
- cdbh->attrs_list.set_attr_offset = OSD_OFFSET_UNUSED;
- return 0;
- }
-
- cdbh->attrs_list.set_attr_bytes = cpu_to_be32(or->set_attr.total_bytes);
- cdbh->attrs_list.set_attr_offset =
- osd_req_encode_offset(or, or->out.total_bytes, &padding);
-
- ret = _req_append_segment(or, padding, &or->set_attr,
- or->out.last_seg, &or->out);
- if (ret)
- return ret;
-
- or->out.last_seg = &or->set_attr;
- return 0;
-}
-
-int osd_req_add_get_attr_list(struct osd_request *or,
- const struct osd_attr *oa, unsigned nelem)
-{
- unsigned total_bytes = or->enc_get_attr.total_bytes;
- void *attr_last;
- int ret;
-
- if (or->attributes_mode &&
- or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) {
- WARN_ON(1);
- return -EINVAL;
- }
- or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
-
- /* first time calc data-in list header size */
- if (!or->get_attr.total_bytes)
- or->get_attr.total_bytes = _osd_req_sizeof_alist_header(or);
-
- /* calc data-out info */
- if (!total_bytes) { /* first-time: allocate and put list header */
- unsigned max_bytes;
-
- total_bytes = _osd_req_sizeof_alist_header(or);
- max_bytes = total_bytes +
- nelem * sizeof(struct osd_attributes_list_attrid);
- ret = _alloc_get_attr_desc(or, max_bytes);
- if (ret)
- return ret;
-
- _osd_req_set_alist_type(or, or->enc_get_attr.buff,
- OSD_ATTR_LIST_GET);
- }
- attr_last = or->enc_get_attr.buff + total_bytes;
-
- for (; nelem; --nelem) {
- struct osd_attributes_list_attrid *attrid;
- const unsigned cur_size = sizeof(*attrid);
-
- total_bytes += cur_size;
- if (unlikely(or->enc_get_attr.alloc_size < total_bytes)) {
- or->enc_get_attr.total_bytes = total_bytes - cur_size;
- ret = _alloc_get_attr_desc(or,
- total_bytes + nelem * sizeof(*attrid));
- if (ret)
- return ret;
- attr_last = or->enc_get_attr.buff +
- or->enc_get_attr.total_bytes;
- }
-
- attrid = attr_last;
- attrid->attr_page = cpu_to_be32(oa->attr_page);
- attrid->attr_id = cpu_to_be32(oa->attr_id);
-
- attr_last += cur_size;
-
- /* calc data-in size */
- or->get_attr.total_bytes +=
- _osd_req_alist_elem_size(or, oa->len);
- ++oa;
- }
-
- or->enc_get_attr.total_bytes = total_bytes;
-
- OSD_DEBUG(
- "get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%Zu)\n",
- or->get_attr.total_bytes,
- or->get_attr.total_bytes - _osd_req_sizeof_alist_header(or),
- or->enc_get_attr.total_bytes,
- (or->enc_get_attr.total_bytes - _osd_req_sizeof_alist_header(or))
- / sizeof(struct osd_attributes_list_attrid));
-
- return 0;
-}
-EXPORT_SYMBOL(osd_req_add_get_attr_list);
-
-static int _osd_req_finalize_get_attr_list(struct osd_request *or)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- unsigned out_padding;
- unsigned in_padding;
- int ret;
-
- if (!or->enc_get_attr.total_bytes) {
- cdbh->attrs_list.get_attr_desc_offset = OSD_OFFSET_UNUSED;
- cdbh->attrs_list.get_attr_offset = OSD_OFFSET_UNUSED;
- return 0;
- }
-
- ret = _alloc_get_attr_list(or);
- if (ret)
- return ret;
-
- /* The out-going buffer info update */
- OSD_DEBUG("out-going\n");
- cdbh->attrs_list.get_attr_desc_bytes =
- cpu_to_be32(or->enc_get_attr.total_bytes);
-
- cdbh->attrs_list.get_attr_desc_offset =
- osd_req_encode_offset(or, or->out.total_bytes, &out_padding);
-
- ret = _req_append_segment(or, out_padding, &or->enc_get_attr,
- or->out.last_seg, &or->out);
- if (ret)
- return ret;
- or->out.last_seg = &or->enc_get_attr;
-
- /* The incoming buffer info update */
- OSD_DEBUG("in-coming\n");
- cdbh->attrs_list.get_attr_alloc_length =
- cpu_to_be32(or->get_attr.total_bytes);
-
- cdbh->attrs_list.get_attr_offset =
- osd_req_encode_offset(or, or->in.total_bytes, &in_padding);
-
- ret = _req_append_segment(or, in_padding, &or->get_attr, NULL,
- &or->in);
- if (ret)
- return ret;
- or->in.last_seg = &or->get_attr;
-
- return 0;
-}
-
-int osd_req_decode_get_attr_list(struct osd_request *or,
- struct osd_attr *oa, int *nelem, void **iterator)
-{
- unsigned cur_bytes, returned_bytes;
- int n;
- const unsigned sizeof_attr_list = _osd_req_sizeof_alist_header(or);
- void *cur_p;
-
- if (!_osd_req_is_alist_type(or, or->get_attr.buff,
- OSD_ATTR_LIST_SET_RETRIEVE)) {
- oa->attr_page = 0;
- oa->attr_id = 0;
- oa->val_ptr = NULL;
- oa->len = 0;
- *iterator = NULL;
- return 0;
- }
-
- if (*iterator) {
- BUG_ON((*iterator < or->get_attr.buff) ||
- (or->get_attr.buff + or->get_attr.alloc_size < *iterator));
- cur_p = *iterator;
- cur_bytes = (*iterator - or->get_attr.buff) - sizeof_attr_list;
- returned_bytes = or->get_attr.total_bytes;
- } else { /* first time decode the list header */
- cur_bytes = sizeof_attr_list;
- returned_bytes = _osd_req_alist_size(or, or->get_attr.buff) +
- sizeof_attr_list;
-
- cur_p = or->get_attr.buff + sizeof_attr_list;
-
- if (returned_bytes > or->get_attr.alloc_size) {
- OSD_DEBUG("target report: space was not big enough! "
- "Allocate=%u Needed=%u\n",
- or->get_attr.alloc_size,
- returned_bytes + sizeof_attr_list);
-
- returned_bytes =
- or->get_attr.alloc_size - sizeof_attr_list;
- }
- or->get_attr.total_bytes = returned_bytes;
- }
-
- for (n = 0; (n < *nelem) && (cur_bytes < returned_bytes); ++n) {
- int inc = _osd_req_alist_elem_decode(or, cur_p, oa,
- returned_bytes - cur_bytes);
-
- if (inc < 0) {
- OSD_ERR("BAD FOOD from target. list not valid!"
- "c=%d r=%d n=%d\n",
- cur_bytes, returned_bytes, n);
- oa->val_ptr = NULL;
- cur_bytes = returned_bytes; /* break the caller loop */
- break;
- }
-
- cur_bytes += inc;
- cur_p += inc;
- ++oa;
- }
-
- *iterator = (returned_bytes - cur_bytes) ? cur_p : NULL;
- *nelem = n;
- return returned_bytes - cur_bytes;
-}
-EXPORT_SYMBOL(osd_req_decode_get_attr_list);
-
-/*
- * Attributes Page-mode
- */
-
-int osd_req_add_get_attr_page(struct osd_request *or,
- u32 page_id, void *attar_page, unsigned max_page_len,
- const struct osd_attr *set_one_attr)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
-
- if (or->attributes_mode &&
- or->attributes_mode != OSD_CDB_GET_ATTR_PAGE_SET_ONE) {
- WARN_ON(1);
- return -EINVAL;
- }
- or->attributes_mode = OSD_CDB_GET_ATTR_PAGE_SET_ONE;
-
- or->get_attr.buff = attar_page;
- or->get_attr.total_bytes = max_page_len;
-
- cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id);
- cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len);
-
- if (!set_one_attr || !set_one_attr->attr_page)
- return 0; /* The set is optional */
-
- or->set_attr.buff = set_one_attr->val_ptr;
- or->set_attr.total_bytes = set_one_attr->len;
-
- cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page);
- cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id);
- cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len);
- return 0;
-}
-EXPORT_SYMBOL(osd_req_add_get_attr_page);
-
-static int _osd_req_finalize_attr_page(struct osd_request *or)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- unsigned in_padding, out_padding;
- int ret;
-
- /* returned page */
- cdbh->attrs_page.get_attr_offset =
- osd_req_encode_offset(or, or->in.total_bytes, &in_padding);
-
- ret = _req_append_segment(or, in_padding, &or->get_attr, NULL,
- &or->in);
- if (ret)
- return ret;
-
- if (or->set_attr.total_bytes == 0)
- return 0;
-
- /* set one value */
- cdbh->attrs_page.set_attr_offset =
- osd_req_encode_offset(or, or->out.total_bytes, &out_padding);
-
- ret = _req_append_segment(or, out_padding, &or->set_attr, NULL,
- &or->out);
- return ret;
-}
-
-static inline void osd_sec_parms_set_out_offset(bool is_v1,
- struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
-{
- if (is_v1)
- sec_parms->v1.data_out_integrity_check_offset = offset;
- else
- sec_parms->v2.data_out_integrity_check_offset = offset;
-}
-
-static inline void osd_sec_parms_set_in_offset(bool is_v1,
- struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
-{
- if (is_v1)
- sec_parms->v1.data_in_integrity_check_offset = offset;
- else
- sec_parms->v2.data_in_integrity_check_offset = offset;
-}
-
-static int _osd_req_finalize_data_integrity(struct osd_request *or,
- bool has_in, bool has_out, struct bio *out_data_bio, u64 out_data_bytes,
- const u8 *cap_key)
-{
- struct osd_security_parameters *sec_parms = _osd_req_sec_params(or);
- int ret;
-
- if (!osd_is_sec_alldata(sec_parms))
- return 0;
-
- if (has_out) {
- struct _osd_req_data_segment seg = {
- .buff = &or->out_data_integ,
- .total_bytes = sizeof(or->out_data_integ),
- };
- unsigned pad;
-
- or->out_data_integ.data_bytes = cpu_to_be64(out_data_bytes);
- or->out_data_integ.set_attributes_bytes = cpu_to_be64(
- or->set_attr.total_bytes);
- or->out_data_integ.get_attributes_bytes = cpu_to_be64(
- or->enc_get_attr.total_bytes);
-
- osd_sec_parms_set_out_offset(osd_req_is_ver1(or), sec_parms,
- osd_req_encode_offset(or, or->out.total_bytes, &pad));
-
- ret = _req_append_segment(or, pad, &seg, or->out.last_seg,
- &or->out);
- if (ret)
- return ret;
- or->out.last_seg = NULL;
-
- /* they are now all chained to request sign them all together */
- osd_sec_sign_data(&or->out_data_integ, out_data_bio,
- cap_key);
- }
-
- if (has_in) {
- struct _osd_req_data_segment seg = {
- .buff = &or->in_data_integ,
- .total_bytes = sizeof(or->in_data_integ),
- };
- unsigned pad;
-
- osd_sec_parms_set_in_offset(osd_req_is_ver1(or), sec_parms,
- osd_req_encode_offset(or, or->in.total_bytes, &pad));
-
- ret = _req_append_segment(or, pad, &seg, or->in.last_seg,
- &or->in);
- if (ret)
- return ret;
-
- or->in.last_seg = NULL;
- }
-
- return 0;
-}
-
-/*
- * osd_finalize_request and helpers
- */
-static struct request *_make_request(struct request_queue *q, bool has_write,
- struct _osd_io_info *oii, gfp_t flags)
-{
- if (oii->bio)
- return blk_make_request(q, oii->bio, flags);
- else {
- struct request *req;
-
- req = blk_get_request(q, has_write ? WRITE : READ, flags);
- if (unlikely(!req))
- return ERR_PTR(-ENOMEM);
-
- return req;
- }
-}
-
-static int _init_blk_request(struct osd_request *or,
- bool has_in, bool has_out)
-{
- gfp_t flags = or->alloc_flags;
- struct scsi_device *scsi_device = or->osd_dev->scsi_device;
- struct request_queue *q = scsi_device->request_queue;
- struct request *req;
- int ret;
-
- req = _make_request(q, has_out, has_out ? &or->out : &or->in, flags);
- if (IS_ERR(req)) {
- ret = PTR_ERR(req);
- goto out;
- }
-
- or->request = req;
- req->cmd_type = REQ_TYPE_BLOCK_PC;
- req->cmd_flags |= REQ_QUIET;
-
- req->timeout = or->timeout;
- req->retries = or->retries;
- req->sense = or->sense;
- req->sense_len = 0;
-
- if (has_out) {
- or->out.req = req;
- if (has_in) {
- /* allocate bidi request */
- req = _make_request(q, false, &or->in, flags);
- if (IS_ERR(req)) {
- OSD_DEBUG("blk_get_request for bidi failed\n");
- ret = PTR_ERR(req);
- goto out;
- }
- req->cmd_type = REQ_TYPE_BLOCK_PC;
- or->in.req = or->request->next_rq = req;
- }
- } else if (has_in)
- or->in.req = req;
-
- ret = 0;
-out:
- OSD_DEBUG("or=%p has_in=%d has_out=%d => %d, %p\n",
- or, has_in, has_out, ret, or->request);
- return ret;
-}
-
-int osd_finalize_request(struct osd_request *or,
- u8 options, const void *cap, const u8 *cap_key)
-{
- struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
- bool has_in, has_out;
- /* Save for data_integrity without the cdb_continuation */
- struct bio *out_data_bio = or->out.bio;
- u64 out_data_bytes = or->out.total_bytes;
- int ret;
-
- if (options & OSD_REQ_FUA)
- cdbh->options |= OSD_CDB_FUA;
-
- if (options & OSD_REQ_DPO)
- cdbh->options |= OSD_CDB_DPO;
-
- if (options & OSD_REQ_BYPASS_TIMESTAMPS)
- cdbh->timestamp_control = OSD_CDB_BYPASS_TIMESTAMPS;
-
- osd_set_caps(&or->cdb, cap);
-
- has_in = or->in.bio || or->get_attr.total_bytes;
- has_out = or->out.bio || or->cdb_cont.total_bytes ||
- or->set_attr.total_bytes || or->enc_get_attr.total_bytes;
-
- ret = _osd_req_finalize_cdb_cont(or, cap_key);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_cdb_cont failed\n");
- return ret;
- }
- ret = _init_blk_request(or, has_in, has_out);
- if (ret) {
- OSD_DEBUG("_init_blk_request failed\n");
- return ret;
- }
-
- or->out.pad_buff = sg_out_pad_buffer;
- or->in.pad_buff = sg_in_pad_buffer;
-
- if (!or->attributes_mode)
- or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
- cdbh->command_specific_options |= or->attributes_mode;
- if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) {
- ret = _osd_req_finalize_attr_page(or);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_attr_page failed\n");
- return ret;
- }
- } else {
- /* TODO: I think that for the GET_ATTR command these 2 should
- * be reversed to keep them in execution order (for embeded
- * targets with low memory footprint)
- */
- ret = _osd_req_finalize_set_attr_list(or);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_set_attr_list failed\n");
- return ret;
- }
-
- ret = _osd_req_finalize_get_attr_list(or);
- if (ret) {
- OSD_DEBUG("_osd_req_finalize_get_attr_list failed\n");
- return ret;
- }
- }
-
- ret = _osd_req_finalize_data_integrity(or, has_in, has_out,
- out_data_bio, out_data_bytes,
- cap_key);
- if (ret)
- return ret;
-
- osd_sec_sign_cdb(&or->cdb, cap_key);
-
- or->request->cmd = or->cdb.buff;
- or->request->cmd_len = _osd_req_cdb_len(or);
-
- return 0;
-}
-EXPORT_SYMBOL(osd_finalize_request);
-
-static bool _is_osd_security_code(int code)
-{
- return (code == osd_security_audit_value_frozen) ||
- (code == osd_security_working_key_frozen) ||
- (code == osd_nonce_not_unique) ||
- (code == osd_nonce_timestamp_out_of_range) ||
- (code == osd_invalid_dataout_buffer_integrity_check_value);
-}
-
-#define OSD_SENSE_PRINT1(fmt, a...) \
- do { \
- if (__cur_sense_need_output) \
- OSD_ERR(fmt, ##a); \
- } while (0)
-
-#define OSD_SENSE_PRINT2(fmt, a...) OSD_SENSE_PRINT1(" " fmt, ##a)
-
-int osd_req_decode_sense_full(struct osd_request *or,
- struct osd_sense_info *osi, bool silent,
- struct osd_obj_id *bad_obj_list __unused, int max_obj __unused,
- struct osd_attr *bad_attr_list, int max_attr)
-{
- int sense_len, original_sense_len;
- struct osd_sense_info local_osi;
- struct scsi_sense_descriptor_based *ssdb;
- void *cur_descriptor;
-#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 0)
- const bool __cur_sense_need_output = false;
-#else
- bool __cur_sense_need_output = !silent;
-#endif
- int ret;
-
- if (likely(!or->req_errors))
- return 0;
-
- osi = osi ? : &local_osi;
- memset(osi, 0, sizeof(*osi));
-
- ssdb = (typeof(ssdb))or->sense;
- sense_len = or->sense_len;
- if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) {
- OSD_ERR("Block-layer returned error(0x%x) but "
- "sense_len(%u) || key(%d) is empty\n",
- or->req_errors, sense_len, ssdb->sense_key);
- goto analyze;
- }
-
- if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) {
- OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n",
- ssdb->response_code, sense_len);
- goto analyze;
- }
-
- osi->key = ssdb->sense_key;
- osi->additional_code = be16_to_cpu(ssdb->additional_sense_code);
- original_sense_len = ssdb->additional_sense_length + 8;
-
-#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 1)
- if (__cur_sense_need_output)
- __cur_sense_need_output = (osi->key > scsi_sk_recovered_error);
-#endif
- OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) "
- "additional_code=0x%x async_error=%d errors=0x%x\n",
- osi->key, original_sense_len, sense_len,
- osi->additional_code, or->async_error,
- or->req_errors);
-
- if (original_sense_len < sense_len)
- sense_len = original_sense_len;
-
- cur_descriptor = ssdb->ssd;
- sense_len -= sizeof(*ssdb);
- while (sense_len > 0) {
- struct scsi_sense_descriptor *ssd = cur_descriptor;
- int cur_len = ssd->additional_length + 2;
-
- sense_len -= cur_len;
-
- if (sense_len < 0)
- break; /* sense was truncated */
-
- switch (ssd->descriptor_type) {
- case scsi_sense_information:
- case scsi_sense_command_specific_information:
- {
- struct scsi_sense_command_specific_data_descriptor
- *sscd = cur_descriptor;
-
- osi->command_info =
- get_unaligned_be64(&sscd->information) ;
- OSD_SENSE_PRINT2(
- "command_specific_information 0x%llx \n",
- _LLU(osi->command_info));
- break;
- }
- case scsi_sense_key_specific:
- {
- struct scsi_sense_key_specific_data_descriptor
- *ssks = cur_descriptor;
-
- osi->sense_info = get_unaligned_be16(&ssks->value);
- OSD_SENSE_PRINT2(
- "sense_key_specific_information %u"
- "sksv_cd_bpv_bp (0x%x)\n",
- osi->sense_info, ssks->sksv_cd_bpv_bp);
- break;
- }
- case osd_sense_object_identification:
- { /*FIXME: Keep first not last, Store in array*/
- struct osd_sense_identification_data_descriptor
- *osidd = cur_descriptor;
-
- osi->not_initiated_command_functions =
- le32_to_cpu(osidd->not_initiated_functions);
- osi->completed_command_functions =
- le32_to_cpu(osidd->completed_functions);
- osi->obj.partition = be64_to_cpu(osidd->partition_id);
- osi->obj.id = be64_to_cpu(osidd->object_id);
- OSD_SENSE_PRINT2(
- "object_identification pid=0x%llx oid=0x%llx\n",
- _LLU(osi->obj.partition), _LLU(osi->obj.id));
- OSD_SENSE_PRINT2(
- "not_initiated_bits(%x) "
- "completed_command_bits(%x)\n",
- osi->not_initiated_command_functions,
- osi->completed_command_functions);
- break;
- }
- case osd_sense_response_integrity_check:
- {
- struct osd_sense_response_integrity_check_descriptor
- *osricd = cur_descriptor;
- const unsigned len =
- sizeof(osricd->integrity_check_value);
- char key_dump[len*4 + 2]; /* 2nibbles+space+ASCII */
-
- hex_dump_to_buffer(osricd->integrity_check_value, len,
- 32, 1, key_dump, sizeof(key_dump), true);
- OSD_SENSE_PRINT2("response_integrity [%s]\n", key_dump);
- }
- case osd_sense_attribute_identification:
- {
- struct osd_sense_attributes_data_descriptor
- *osadd = cur_descriptor;
- unsigned len = min(cur_len, sense_len);
- struct osd_sense_attr *pattr = osadd->sense_attrs;
-
- while (len >= sizeof(*pattr)) {
- u32 attr_page = be32_to_cpu(pattr->attr_page);
- u32 attr_id = be32_to_cpu(pattr->attr_id);
-
- if (!osi->attr.attr_page) {
- osi->attr.attr_page = attr_page;
- osi->attr.attr_id = attr_id;
- }
-
- if (bad_attr_list && max_attr) {
- bad_attr_list->attr_page = attr_page;
- bad_attr_list->attr_id = attr_id;
- bad_attr_list++;
- max_attr--;
- }
-
- len -= sizeof(*pattr);
- OSD_SENSE_PRINT2(
- "osd_sense_attribute_identification"
- "attr_page=0x%x attr_id=0x%x\n",
- attr_page, attr_id);
- }
- }
- /*These are not legal for OSD*/
- case scsi_sense_field_replaceable_unit:
- OSD_SENSE_PRINT2("scsi_sense_field_replaceable_unit\n");
- break;
- case scsi_sense_stream_commands:
- OSD_SENSE_PRINT2("scsi_sense_stream_commands\n");
- break;
- case scsi_sense_block_commands:
- OSD_SENSE_PRINT2("scsi_sense_block_commands\n");
- break;
- case scsi_sense_ata_return:
- OSD_SENSE_PRINT2("scsi_sense_ata_return\n");
- break;
- default:
- if (ssd->descriptor_type <= scsi_sense_Reserved_last)
- OSD_SENSE_PRINT2(
- "scsi_sense Reserved descriptor (0x%x)",
- ssd->descriptor_type);
- else
- OSD_SENSE_PRINT2(
- "scsi_sense Vendor descriptor (0x%x)",
- ssd->descriptor_type);
- }
-
- cur_descriptor += cur_len;
- }
-
-analyze:
- if (!osi->key) {
- /* scsi sense is Empty, the request was never issued to target
- * linux return code might tell us what happened.
- */
- if (or->async_error == -ENOMEM)
- osi->osd_err_pri = OSD_ERR_PRI_RESOURCE;
- else
- osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE;
- ret = or->async_error;
- } else if (osi->key <= scsi_sk_recovered_error) {
- osi->osd_err_pri = 0;
- ret = 0;
- } else if (osi->additional_code == scsi_invalid_field_in_cdb) {
- if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) {
- osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES;
- ret = -EFAULT; /* caller should recover from this */
- } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) {
- osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND;
- ret = -ENOENT;
- } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) {
- osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS;
- ret = -EACCES;
- } else {
- osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
- ret = -EINVAL;
- }
- } else if (osi->additional_code == osd_quota_error) {
- osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE;
- ret = -ENOSPC;
- } else if (_is_osd_security_code(osi->additional_code)) {
- osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
- ret = -EINVAL;
- } else {
- osi->osd_err_pri = OSD_ERR_PRI_EIO;
- ret = -EIO;
- }
-
- if (!or->out.residual)
- or->out.residual = or->out.total_bytes;
- if (!or->in.residual)
- or->in.residual = or->in.total_bytes;
-
- return ret;
-}
-EXPORT_SYMBOL(osd_req_decode_sense_full);
-
-/*
- * Implementation of osd_sec.h API
- * TODO: Move to a separate osd_sec.c file at a later stage.
- */
-
-enum { OSD_SEC_CAP_V1_ALL_CAPS =
- OSD_SEC_CAP_APPEND | OSD_SEC_CAP_OBJ_MGMT | OSD_SEC_CAP_REMOVE |
- OSD_SEC_CAP_CREATE | OSD_SEC_CAP_SET_ATTR | OSD_SEC_CAP_GET_ATTR |
- OSD_SEC_CAP_WRITE | OSD_SEC_CAP_READ | OSD_SEC_CAP_POL_SEC |
- OSD_SEC_CAP_GLOBAL | OSD_SEC_CAP_DEV_MGMT
-};
-
-enum { OSD_SEC_CAP_V2_ALL_CAPS =
- OSD_SEC_CAP_V1_ALL_CAPS | OSD_SEC_CAP_QUERY | OSD_SEC_CAP_M_OBJECT
-};
-
-void osd_sec_init_nosec_doall_caps(void *caps,
- const struct osd_obj_id *obj, bool is_collection, const bool is_v1)
-{
- struct osd_capability *cap = caps;
- u8 type;
- u8 descriptor_type;
-
- if (likely(obj->id)) {
- if (unlikely(is_collection)) {
- type = OSD_SEC_OBJ_COLLECTION;
- descriptor_type = is_v1 ? OSD_SEC_OBJ_DESC_OBJ :
- OSD_SEC_OBJ_DESC_COL;
- } else {
- type = OSD_SEC_OBJ_USER;
- descriptor_type = OSD_SEC_OBJ_DESC_OBJ;
- }
- WARN_ON(!obj->partition);
- } else {
- type = obj->partition ? OSD_SEC_OBJ_PARTITION :
- OSD_SEC_OBJ_ROOT;
- descriptor_type = OSD_SEC_OBJ_DESC_PAR;
- }
-
- memset(cap, 0, sizeof(*cap));
-
- cap->h.format = OSD_SEC_CAP_FORMAT_VER1;
- cap->h.integrity_algorithm__key_version = 0; /* MAKE_BYTE(0, 0); */
- cap->h.security_method = OSD_SEC_NOSEC;
-/* cap->expiration_time;
- cap->AUDIT[30-10];
- cap->discriminator[42-30];
- cap->object_created_time; */
- cap->h.object_type = type;
- osd_sec_set_caps(&cap->h, OSD_SEC_CAP_V1_ALL_CAPS);
- cap->h.object_descriptor_type = descriptor_type;
- cap->od.obj_desc.policy_access_tag = 0;
- cap->od.obj_desc.allowed_partition_id = cpu_to_be64(obj->partition);
- cap->od.obj_desc.allowed_object_id = cpu_to_be64(obj->id);
-}
-EXPORT_SYMBOL(osd_sec_init_nosec_doall_caps);
-
-/* FIXME: Extract version from caps pointer.
- * Also Pete's target only supports caps from OSDv1 for now
- */
-void osd_set_caps(struct osd_cdb *cdb, const void *caps)
-{
- bool is_ver1 = true;
- /* NOTE: They start at same address */
- memcpy(&cdb->v1.caps, caps, is_ver1 ? OSDv1_CAP_LEN : OSD_CAP_LEN);
-}
-
-bool osd_is_sec_alldata(struct osd_security_parameters *sec_parms __unused)
-{
- return false;
-}
-
-void osd_sec_sign_cdb(struct osd_cdb *ocdb __unused, const u8 *cap_key __unused)
-{
-}
-
-void osd_sec_sign_data(void *data_integ __unused,
- struct bio *bio __unused, const u8 *cap_key __unused)
-{
-}
-
-/*
- * Declared in osd_protocol.h
- * 4.12.5 Data-In and Data-Out buffer offsets
- * byte offset = mantissa * (2^(exponent+8))
- * Returns the smallest allowed encoded offset that contains given @offset
- * The actual encoded offset returned is @offset + *@padding.
- */
-osd_cdb_offset __osd_encode_offset(
- u64 offset, unsigned *padding, int min_shift, int max_shift)
-{
- u64 try_offset = -1, mod, align;
- osd_cdb_offset be32_offset;
- int shift;
-
- *padding = 0;
- if (!offset)
- return 0;
-
- for (shift = min_shift; shift < max_shift; ++shift) {
- try_offset = offset >> shift;
- if (try_offset < (1 << OSD_OFFSET_MAX_BITS))
- break;
- }
-
- BUG_ON(shift == max_shift);
-
- align = 1 << shift;
- mod = offset & (align - 1);
- if (mod) {
- *padding = align - mod;
- try_offset += 1;
- }
-
- try_offset |= ((shift - 8) & 0xf) << 28;
- be32_offset = cpu_to_be32((u32)try_offset);
-
- OSD_DEBUG("offset=%llu mantissa=%llu exp=%d encoded=%x pad=%d\n",
- _LLU(offset), _LLU(try_offset & 0x0FFFFFFF), shift,
- be32_offset, *padding);
- return be32_offset;
-}
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c
deleted file mode 100644
index b31a8e3841d7..000000000000
--- a/drivers/scsi/osd/osd_uld.c
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * osd_uld.c - OSD Upper Layer Driver
- *
- * A Linux driver module that registers as a SCSI ULD and probes
- * for OSD type SCSI devices.
- * It's main function is to export osd devices to in-kernel users like
- * osdfs and pNFS-objects-LD. It also provides one ioctl for running
- * in Kernel tests.
- *
- * Copyright (C) 2008 Panasas Inc. All rights reserved.
- *
- * Authors:
- * Boaz Harrosh <bharrosh@panasas.com>
- * Benny Halevy <bhalevy@panasas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the Panasas company nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <linux/namei.h>
-#include <linux/cdev.h>
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/idr.h>
-#include <linux/major.h>
-#include <linux/file.h>
-#include <linux/slab.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_driver.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_ioctl.h>
-
-#include <scsi/osd_initiator.h>
-#include <scsi/osd_sec.h>
-
-#include "osd_debug.h"
-
-#ifndef TYPE_OSD
-# define TYPE_OSD 0x11
-#endif
-
-#ifndef SCSI_OSD_MAJOR
-# define SCSI_OSD_MAJOR 260
-#endif
-#define SCSI_OSD_MAX_MINOR 64
-
-static const char osd_name[] = "osd";
-static const char *osd_version_string = "open-osd 0.2.0";
-
-MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>");
-MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR);
-MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD);
-
-struct osd_uld_device {
- int minor;
- struct device class_dev;
- struct cdev cdev;
- struct osd_dev od;
- struct osd_dev_info odi;
- struct gendisk *disk;
-};
-
-struct osd_dev_handle {
- struct osd_dev od;
- struct file *file;
- struct osd_uld_device *oud;
-} ;
-
-static DEFINE_IDA(osd_minor_ida);
-
-static struct class osd_uld_class = {
- .owner = THIS_MODULE,
- .name = "scsi_osd",
-};
-
-/*
- * Char Device operations
- */
-
-static int osd_uld_open(struct inode *inode, struct file *file)
-{
- struct osd_uld_device *oud = container_of(inode->i_cdev,
- struct osd_uld_device, cdev);
-
- get_device(&oud->class_dev);
- /* cache osd_uld_device on file handle */
- file->private_data = oud;
- OSD_DEBUG("osd_uld_open %p\n", oud);
- return 0;
-}
-
-static int osd_uld_release(struct inode *inode, struct file *file)
-{
- struct osd_uld_device *oud = file->private_data;
-
- OSD_DEBUG("osd_uld_release %p\n", file->private_data);
- file->private_data = NULL;
- put_device(&oud->class_dev);
- return 0;
-}
-
-/* FIXME: Only one vector for now */
-unsigned g_test_ioctl;
-do_test_fn *g_do_test;
-
-int osduld_register_test(unsigned ioctl, do_test_fn *do_test)
-{
- if (g_test_ioctl)
- return -EINVAL;
-
- g_test_ioctl = ioctl;
- g_do_test = do_test;
- return 0;
-}
-EXPORT_SYMBOL(osduld_register_test);
-
-void osduld_unregister_test(unsigned ioctl)
-{
- if (ioctl == g_test_ioctl) {
- g_test_ioctl = 0;
- g_do_test = NULL;
- }
-}
-EXPORT_SYMBOL(osduld_unregister_test);
-
-static do_test_fn *_find_ioctl(unsigned cmd)
-{
- if (g_test_ioctl == cmd)
- return g_do_test;
- else
- return NULL;
-}
-
-static long osd_uld_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct osd_uld_device *oud = file->private_data;
- int ret;
- do_test_fn *do_test;
-
- do_test = _find_ioctl(cmd);
- if (do_test)
- ret = do_test(&oud->od, cmd, arg);
- else {
- OSD_ERR("Unknown ioctl %d: osd_uld_device=%p\n", cmd, oud);
- ret = -ENOIOCTLCMD;
- }
- return ret;
-}
-
-static const struct file_operations osd_fops = {
- .owner = THIS_MODULE,
- .open = osd_uld_open,
- .release = osd_uld_release,
- .unlocked_ioctl = osd_uld_ioctl,
- .llseek = noop_llseek,
-};
-
-struct osd_dev *osduld_path_lookup(const char *name)
-{
- struct osd_uld_device *oud;
- struct osd_dev_handle *odh;
- struct file *file;
- int error;
-
- if (!name || !*name) {
- OSD_ERR("Mount with !path || !*path\n");
- return ERR_PTR(-EINVAL);
- }
-
- odh = kzalloc(sizeof(*odh), GFP_KERNEL);
- if (unlikely(!odh))
- return ERR_PTR(-ENOMEM);
-
- file = filp_open(name, O_RDWR, 0);
- if (IS_ERR(file)) {
- error = PTR_ERR(file);
- goto free_od;
- }
-
- if (file->f_op != &osd_fops){
- error = -EINVAL;
- goto close_file;
- }
-
- oud = file->private_data;
-
- odh->od = oud->od;
- odh->file = file;
- odh->oud = oud;
-
- return &odh->od;
-
-close_file:
- fput(file);
-free_od:
- kfree(odh);
- return ERR_PTR(error);
-}
-EXPORT_SYMBOL(osduld_path_lookup);
-
-static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len,
- const u8 *a2, unsigned a2_len)
-{
- if (!a2_len) /* User string is Empty means don't care */
- return true;
-
- if (a1_len != a2_len)
- return false;
-
- return 0 == memcmp(a1, a2, a1_len);
-}
-
-struct find_oud_t {
- const struct osd_dev_info *odi;
- struct device *dev;
- struct osd_uld_device *oud;
-} ;
-
-int _mach_odi(struct device *dev, void *find_data)
-{
- struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
- class_dev);
- struct find_oud_t *fot = find_data;
- const struct osd_dev_info *odi = fot->odi;
-
- if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
- odi->systemid, odi->systemid_len) &&
- _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
- odi->osdname, odi->osdname_len)) {
- OSD_DEBUG("found device sysid_len=%d osdname=%d\n",
- odi->systemid_len, odi->osdname_len);
- fot->oud = oud;
- return 1;
- } else {
- return 0;
- }
-}
-
-/* osduld_info_lookup - Loop through all devices, return the requested osd_dev.
- *
- * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't
- * care. .e.g if they're both zero /dev/osd0 is returned.
- */
-struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi)
-{
- struct find_oud_t find = {.odi = odi};
-
- find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi);
- if (likely(find.dev)) {
- struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL);
-
- if (unlikely(!odh)) {
- put_device(find.dev);
- return ERR_PTR(-ENOMEM);
- }
-
- odh->od = find.oud->od;
- odh->oud = find.oud;
-
- return &odh->od;
- }
-
- return ERR_PTR(-ENODEV);
-}
-EXPORT_SYMBOL(osduld_info_lookup);
-
-void osduld_put_device(struct osd_dev *od)
-{
- if (od && !IS_ERR(od)) {
- struct osd_dev_handle *odh =
- container_of(od, struct osd_dev_handle, od);
- struct osd_uld_device *oud = odh->oud;
-
- BUG_ON(od->scsi_device != oud->od.scsi_device);
-
- /* If scsi has released the device (logout), and exofs has last
- * reference on oud it will be freed by above osd_uld_release
- * within fput below. But this will oops in cdev_release which
- * is called after the fops->release. A get_/put_ pair makes
- * sure we have a cdev for the duration of fput
- */
- if (odh->file) {
- get_device(&oud->class_dev);
- fput(odh->file);
- }
- put_device(&oud->class_dev);
- kfree(odh);
- }
-}
-EXPORT_SYMBOL(osduld_put_device);
-
-const struct osd_dev_info *osduld_device_info(struct osd_dev *od)
-{
- struct osd_dev_handle *odh =
- container_of(od, struct osd_dev_handle, od);
- return &odh->oud->odi;
-}
-EXPORT_SYMBOL(osduld_device_info);
-
-bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi)
-{
- struct osd_dev_handle *odh =
- container_of(od, struct osd_dev_handle, od);
- struct osd_uld_device *oud = odh->oud;
-
- return (oud->odi.systemid_len == odi->systemid_len) &&
- _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
- odi->systemid, odi->systemid_len) &&
- (oud->odi.osdname_len == odi->osdname_len) &&
- _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
- odi->osdname, odi->osdname_len);
-}
-EXPORT_SYMBOL(osduld_device_same);
-
-/*
- * Scsi Device operations
- */
-
-static int __detect_osd(struct osd_uld_device *oud)
-{
- struct scsi_device *scsi_device = oud->od.scsi_device;
- char caps[OSD_CAP_LEN];
- int error;
-
- /* sending a test_unit_ready as first command seems to be needed
- * by some targets
- */
- OSD_DEBUG("start scsi_test_unit_ready %p %p %p\n",
- oud, scsi_device, scsi_device->request_queue);
- error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, NULL);
- if (error)
- OSD_ERR("warning: scsi_test_unit_ready failed\n");
-
- osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
- if (osd_auto_detect_ver(&oud->od, caps, &oud->odi))
- return -ENODEV;
-
- return 0;
-}
-
-static void __remove(struct device *dev)
-{
- struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
- class_dev);
- struct scsi_device *scsi_device = oud->od.scsi_device;
-
- kfree(oud->odi.osdname);
-
- if (oud->cdev.owner)
- cdev_del(&oud->cdev);
-
- osd_dev_fini(&oud->od);
- scsi_device_put(scsi_device);
-
- OSD_INFO("osd_remove %s\n",
- oud->disk ? oud->disk->disk_name : NULL);
-
- if (oud->disk)
- put_disk(oud->disk);
- ida_remove(&osd_minor_ida, oud->minor);
-
- kfree(oud);
-}
-
-static int osd_probe(struct device *dev)
-{
- struct scsi_device *scsi_device = to_scsi_device(dev);
- struct gendisk *disk;
- struct osd_uld_device *oud;
- int minor;
- int error;
-
- if (scsi_device->type != TYPE_OSD)
- return -ENODEV;
-
- do {
- if (!ida_pre_get(&osd_minor_ida, GFP_KERNEL))
- return -ENODEV;
-
- error = ida_get_new(&osd_minor_ida, &minor);
- } while (error == -EAGAIN);
-
- if (error)
- return error;
- if (minor >= SCSI_OSD_MAX_MINOR) {
- error = -EBUSY;
- goto err_retract_minor;
- }
-
- error = -ENOMEM;
- oud = kzalloc(sizeof(*oud), GFP_KERNEL);
- if (NULL == oud)
- goto err_retract_minor;
-
- dev_set_drvdata(dev, oud);
- oud->minor = minor;
-
- /* allocate a disk and set it up */
- /* FIXME: do we need this since sg has already done that */
- disk = alloc_disk(1);
- if (!disk) {
- OSD_ERR("alloc_disk failed\n");
- goto err_free_osd;
- }
- disk->major = SCSI_OSD_MAJOR;
- disk->first_minor = oud->minor;
- sprintf(disk->disk_name, "osd%d", oud->minor);
- oud->disk = disk;
-
- /* hold one more reference to the scsi_device that will get released
- * in __release, in case a logout is happening while fs is mounted
- */
- scsi_device_get(scsi_device);
- osd_dev_init(&oud->od, scsi_device);
-
- /* Detect the OSD Version */
- error = __detect_osd(oud);
- if (error) {
- OSD_ERR("osd detection failed, non-compatible OSD device\n");
- goto err_put_disk;
- }
-
- /* init the char-device for communication with user-mode */
- cdev_init(&oud->cdev, &osd_fops);
- oud->cdev.owner = THIS_MODULE;
- error = cdev_add(&oud->cdev,
- MKDEV(SCSI_OSD_MAJOR, oud->minor), 1);
- if (error) {
- OSD_ERR("cdev_add failed\n");
- goto err_put_disk;
- }
-
- /* class device member */
- oud->class_dev.devt = oud->cdev.dev;
- oud->class_dev.class = &osd_uld_class;
- oud->class_dev.parent = dev;
- oud->class_dev.release = __remove;
- error = dev_set_name(&oud->class_dev, disk->disk_name);
- if (error) {
- OSD_ERR("dev_set_name failed => %d\n", error);
- goto err_put_cdev;
- }
-
- error = device_register(&oud->class_dev);
- if (error) {
- OSD_ERR("device_register failed => %d\n", error);
- goto err_put_cdev;
- }
-
- get_device(&oud->class_dev);
-
- OSD_INFO("osd_probe %s\n", disk->disk_name);
- return 0;
-
-err_put_cdev:
- cdev_del(&oud->cdev);
-err_put_disk:
- scsi_device_put(scsi_device);
- put_disk(disk);
-err_free_osd:
- dev_set_drvdata(dev, NULL);
- kfree(oud);
-err_retract_minor:
- ida_remove(&osd_minor_ida, minor);
- return error;
-}
-
-static int osd_remove(struct device *dev)
-{
- struct scsi_device *scsi_device = to_scsi_device(dev);
- struct osd_uld_device *oud = dev_get_drvdata(dev);
-
- if (!oud || (oud->od.scsi_device != scsi_device)) {
- OSD_ERR("Half cooked osd-device %p,%p || %p!=%p",
- dev, oud, oud ? oud->od.scsi_device : NULL,
- scsi_device);
- }
-
- device_unregister(&oud->class_dev);
-
- put_device(&oud->class_dev);
- return 0;
-}
-
-/*
- * Global driver and scsi registration
- */
-
-static struct scsi_driver osd_driver = {
- .owner = THIS_MODULE,
- .gendrv = {
- .name = osd_name,
- .probe = osd_probe,
- .remove = osd_remove,
- }
-};
-
-static int __init osd_uld_init(void)
-{
- int err;
-
- err = class_register(&osd_uld_class);
- if (err) {
- OSD_ERR("Unable to register sysfs class => %d\n", err);
- return err;
- }
-
- err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0),
- SCSI_OSD_MAX_MINOR, osd_name);
- if (err) {
- OSD_ERR("Unable to register major %d for osd ULD => %d\n",
- SCSI_OSD_MAJOR, err);
- goto err_out;
- }
-
- err = scsi_register_driver(&osd_driver.gendrv);
- if (err) {
- OSD_ERR("scsi_register_driver failed => %d\n", err);
- goto err_out_chrdev;
- }
-
- OSD_INFO("LOADED %s\n", osd_version_string);
- return 0;
-
-err_out_chrdev:
- unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
-err_out:
- class_unregister(&osd_uld_class);
- return err;
-}
-
-static void __exit osd_uld_exit(void)
-{
- scsi_unregister_driver(&osd_driver.gendrv);
- unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
- class_unregister(&osd_uld_class);
- OSD_INFO("UNLOADED %s\n", osd_version_string);
-}
-
-module_init(osd_uld_init);
-module_exit(osd_uld_exit);
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
deleted file mode 100644
index de0b1a704fb5..000000000000
--- a/drivers/scsi/osst.c
+++ /dev/null
@@ -1,6096 +0,0 @@
-/*
- SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
- file Documentation/scsi/st.txt for more information.
-
- History:
-
- OnStream SCSI Tape support (osst) cloned from st.c by
- Willem Riede (osst@riede.org) Feb 2000
- Fixes ... Kurt Garloff <garloff@suse.de> Mar 2000
-
- Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
- Contribution and ideas from several people including (in alphabetical
- order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
- Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
-
- Copyright 1992 - 2002 Kai Makisara / 2000 - 2006 Willem Riede
- email osst@riede.org
-
- $Header: /cvsroot/osst/Driver/osst.c,v 1.73 2005/01/01 21:13:34 wriede Exp $
-
- Microscopic alterations - Rik Ling, 2000/12/21
- Last st.c sync: Tue Oct 15 22:01:04 2002 by makisara
- Some small formal changes - aeb, 950809
-*/
-
-static const char * cvsid = "$Id: osst.c,v 1.73 2005/01/01 21:13:34 wriede Exp $";
-static const char * osst_version = "0.99.4";
-
-/* The "failure to reconnect" firmware bug */
-#define OSST_FW_NEED_POLL_MIN 10601 /*(107A)*/
-#define OSST_FW_NEED_POLL_MAX 10704 /*(108D)*/
-#define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7)
-
-#include <linux/module.h>
-
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/proc_fs.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mtio.h>
-#include <linux/ioctl.h>
-#include <linux/fcntl.h>
-#include <linux/spinlock.h>
-#include <linux/vmalloc.h>
-#include <linux/blkdev.h>
-#include <linux/moduleparam.h>
-#include <linux/delay.h>
-#include <linux/jiffies.h>
-#include <linux/mutex.h>
-#include <asm/uaccess.h>
-#include <asm/dma.h>
-#include <asm/system.h>
-
-/* The driver prints some debugging information on the console if DEBUG
- is defined and non-zero. */
-#define DEBUG 0
-
-/* The message level for the debug messages is currently set to KERN_NOTICE
- so that people can easily see the messages. Later when the debugging messages
- in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
-#define OSST_DEB_MSG KERN_NOTICE
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_driver.h>
-#include <scsi/scsi_eh.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_ioctl.h>
-
-#define ST_KILOBYTE 1024
-
-#include "st.h"
-#include "osst.h"
-#include "osst_options.h"
-#include "osst_detect.h"
-
-static DEFINE_MUTEX(osst_int_mutex);
-static int max_dev = 0;
-static int write_threshold_kbs = 0;
-static int max_sg_segs = 0;
-
-#ifdef MODULE
-MODULE_AUTHOR("Willem Riede");
-MODULE_DESCRIPTION("OnStream {DI-|FW-|SC-|USB}{30|50} Tape Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(OSST_MAJOR);
-MODULE_ALIAS_SCSI_DEVICE(TYPE_TAPE);
-
-module_param(max_dev, int, 0444);
-MODULE_PARM_DESC(max_dev, "Maximum number of OnStream Tape Drives to attach (4)");
-
-module_param(write_threshold_kbs, int, 0644);
-MODULE_PARM_DESC(write_threshold_kbs, "Asynchronous write threshold (KB; 32)");
-
-module_param(max_sg_segs, int, 0644);
-MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (9)");
-#else
-static struct osst_dev_parm {
- char *name;
- int *val;
-} parms[] __initdata = {
- { "max_dev", &max_dev },
- { "write_threshold_kbs", &write_threshold_kbs },
- { "max_sg_segs", &max_sg_segs }
-};
-#endif
-
-/* Some default definitions have been moved to osst_options.h */
-#define OSST_BUFFER_SIZE (OSST_BUFFER_BLOCKS * ST_KILOBYTE)
-#define OSST_WRITE_THRESHOLD (OSST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE)
-
-/* The buffer size should fit into the 24 bits for length in the
- 6-byte SCSI read and write commands. */
-#if OSST_BUFFER_SIZE >= (2 << 24 - 1)
-#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
-#endif
-
-#if DEBUG
-static int debugging = 1;
-/* uncomment define below to test error recovery */
-// #define OSST_INJECT_ERRORS 1
-#endif
-
-/* Do not retry! The drive firmware already retries when appropriate,
- and when it tries to tell us something, we had better listen... */
-#define MAX_RETRIES 0
-
-#define NO_TAPE NOT_READY
-
-#define OSST_WAIT_POSITION_COMPLETE (HZ > 200 ? HZ / 200 : 1)
-#define OSST_WAIT_WRITE_COMPLETE (HZ / 12)
-#define OSST_WAIT_LONG_WRITE_COMPLETE (HZ / 2)
-
-#define OSST_TIMEOUT (200 * HZ)
-#define OSST_LONG_TIMEOUT (1800 * HZ)
-
-#define TAPE_NR(x) (iminor(x) & ~(-1 << ST_MODE_SHIFT))
-#define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
-#define TAPE_REWIND(x) ((iminor(x) & 0x80) == 0)
-#define TAPE_IS_RAW(x) (TAPE_MODE(x) & (ST_NBR_MODES >> 1))
-
-/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
- 24 bits) */
-#define SET_DENS_AND_BLK 0x10001
-
-static int osst_buffer_size = OSST_BUFFER_SIZE;
-static int osst_write_threshold = OSST_WRITE_THRESHOLD;
-static int osst_max_sg_segs = OSST_MAX_SG;
-static int osst_max_dev = OSST_MAX_TAPES;
-static int osst_nr_dev;
-
-static struct osst_tape **os_scsi_tapes = NULL;
-static DEFINE_RWLOCK(os_scsi_tapes_lock);
-
-static int modes_defined = 0;
-
-static struct osst_buffer *new_tape_buffer(int, int, int);
-static int enlarge_buffer(struct osst_buffer *, int);
-static void normalize_buffer(struct osst_buffer *);
-static int append_to_buffer(const char __user *, struct osst_buffer *, int);
-static int from_buffer(struct osst_buffer *, char __user *, int);
-static int osst_zero_buffer_tail(struct osst_buffer *);
-static int osst_copy_to_buffer(struct osst_buffer *, unsigned char *);
-static int osst_copy_from_buffer(struct osst_buffer *, unsigned char *);
-
-static int osst_probe(struct device *);
-static int osst_remove(struct device *);
-
-static struct scsi_driver osst_template = {
- .owner = THIS_MODULE,
- .gendrv = {
- .name = "osst",
- .probe = osst_probe,
- .remove = osst_remove,
- }
-};
-
-static int osst_int_ioctl(struct osst_tape *STp, struct osst_request ** aSRpnt,
- unsigned int cmd_in, unsigned long arg);
-
-static int osst_set_frame_position(struct osst_tape *STp, struct osst_request ** aSRpnt, int frame, int skip);
-
-static int osst_get_frame_position(struct osst_tape *STp, struct osst_request ** aSRpnt);
-
-static int osst_flush_write_buffer(struct osst_tape *STp, struct osst_request ** aSRpnt);
-
-static int osst_write_error_recovery(struct osst_tape * STp, struct osst_request ** aSRpnt, int pending);
-
-static inline char *tape_name(struct osst_tape *tape)
-{
- return tape->drive->disk_name;
-}
-
-/* Routines that handle the interaction with mid-layer SCSI routines */
-
-
-/* Normalize Sense */
-static void osst_analyze_sense(struct osst_request *SRpnt, struct st_cmdstatus *s)
-{
- const u8 *ucp;
- const u8 *sense = SRpnt->sense;
-
- s->have_sense = scsi_normalize_sense(SRpnt->sense,
- SCSI_SENSE_BUFFERSIZE, &s->sense_hdr);
- s->flags = 0;
-
- if (s->have_sense) {
- s->deferred = 0;
- s->remainder_valid =
- scsi_get_sense_info_fld(sense, SCSI_SENSE_BUFFERSIZE, &s->uremainder64);
- switch (sense[0] & 0x7f) {
- case 0x71:
- s->deferred = 1;
- case 0x70:
- s->fixed_format = 1;
- s->flags = sense[2] & 0xe0;
- break;
- case 0x73:
- s->deferred = 1;
- case 0x72:
- s->fixed_format = 0;
- ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4);
- s->flags = ucp ? (ucp[3] & 0xe0) : 0;
- break;
- }
- }
-}
-
-/* Convert the result to success code */
-static int osst_chk_result(struct osst_tape * STp, struct osst_request * SRpnt)
-{
- char *name = tape_name(STp);
- int result = SRpnt->result;
- u8 * sense = SRpnt->sense, scode;
-#if DEBUG
- const char *stp;
-#endif
- struct st_cmdstatus *cmdstatp;
-
- if (!result)
- return 0;
-
- cmdstatp = &STp->buffer->cmdstat;
- osst_analyze_sense(SRpnt, cmdstatp);
-
- if (cmdstatp->have_sense)
- scode = STp->buffer->cmdstat.sense_hdr.sense_key;
- else
- scode = 0;
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG "%s:D: Error: %x, cmd: %x %x %x %x %x %x\n",
- name, result,
- SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2],
- SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]);
- if (scode) printk(OSST_DEB_MSG "%s:D: Sense: %02x, ASC: %02x, ASCQ: %02x\n",
- name, scode, sense[12], sense[13]);
- if (cmdstatp->have_sense)
- __scsi_print_sense("osst ", SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
- }
- else
-#endif
- if (cmdstatp->have_sense && (
- scode != NO_SENSE &&
- scode != RECOVERED_ERROR &&
-/* scode != UNIT_ATTENTION && */
- scode != BLANK_CHECK &&
- scode != VOLUME_OVERFLOW &&
- SRpnt->cmd[0] != MODE_SENSE &&
- SRpnt->cmd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
- if (cmdstatp->have_sense) {
- printk(KERN_WARNING "%s:W: Command with sense data:\n", name);
- __scsi_print_sense("osst ", SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
- }
- else {
- static int notyetprinted = 1;
-
- printk(KERN_WARNING
- "%s:W: Warning %x (driver bt 0x%x, host bt 0x%x).\n",
- name, result, driver_byte(result),
- host_byte(result));
- if (notyetprinted) {
- notyetprinted = 0;
- printk(KERN_INFO
- "%s:I: This warning may be caused by your scsi controller,\n", name);
- printk(KERN_INFO
- "%s:I: it has been reported with some Buslogic cards.\n", name);
- }
- }
- }
- STp->pos_unknown |= STp->device->was_reset;
-
- if (cmdstatp->have_sense && scode == RECOVERED_ERROR) {
- STp->recover_count++;
- STp->recover_erreg++;
-#if DEBUG
- if (debugging) {
- if (SRpnt->cmd[0] == READ_6)
- stp = "read";
- else if (SRpnt->cmd[0] == WRITE_6)
- stp = "write";
- else
- stp = "ioctl";
- printk(OSST_DEB_MSG "%s:D: Recovered %s error (%d).\n", name, stp,
- STp->recover_count);
- }
-#endif
- if ((sense[2] & 0xe0) == 0)
- return 0;
- }
- return (-EIO);
-}
-
-
-/* Wakeup from interrupt */
-static void osst_end_async(struct request *req, int update)
-{
- struct osst_request *SRpnt = req->end_io_data;
- struct osst_tape *STp = SRpnt->stp;
- struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
-
- STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors;
-#if DEBUG
- STp->write_pending = 0;
-#endif
- if (SRpnt->waiting)
- complete(SRpnt->waiting);
-
- if (SRpnt->bio) {
- kfree(mdata->pages);
- blk_rq_unmap_user(SRpnt->bio);
- }
-
- __blk_put_request(req->q, req);
-}
-
-/* osst_request memory management */
-static struct osst_request *osst_allocate_request(void)
-{
- return kzalloc(sizeof(struct osst_request), GFP_KERNEL);
-}
-
-static void osst_release_request(struct osst_request *streq)
-{
- kfree(streq);
-}
-
-static int osst_execute(struct osst_request *SRpnt, const unsigned char *cmd,
- int cmd_len, int data_direction, void *buffer, unsigned bufflen,
- int use_sg, int timeout, int retries)
-{
- struct request *req;
- struct page **pages = NULL;
- struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
-
- int err = 0;
- int write = (data_direction == DMA_TO_DEVICE);
-
- req = blk_get_request(SRpnt->stp->device->request_queue, write, GFP_KERNEL);
- if (!req)
- return DRIVER_ERROR << 24;
-
- req->cmd_type = REQ_TYPE_BLOCK_PC;
- req->cmd_flags |= REQ_QUIET;
-
- SRpnt->bio = NULL;
-
- if (use_sg) {
- struct scatterlist *sg, *sgl = (struct scatterlist *)buffer;
- int i;
-
- pages = kzalloc(use_sg * sizeof(struct page *), GFP_KERNEL);
- if (!pages)
- goto free_req;
-
- for_each_sg(sgl, sg, use_sg, i)
- pages[i] = sg_page(sg);
-
- mdata->null_mapped = 1;
-
- mdata->page_order = get_order(sgl[0].length);
- mdata->nr_entries =
- DIV_ROUND_UP(bufflen, PAGE_SIZE << mdata->page_order);
- mdata->offset = 0;
-
- err = blk_rq_map_user(req->q, req, mdata, NULL, bufflen, GFP_KERNEL);
- if (err) {
- kfree(pages);
- goto free_req;
- }
- SRpnt->bio = req->bio;
- mdata->pages = pages;
-
- } else if (bufflen) {
- err = blk_rq_map_kern(req->q, req, buffer, bufflen, GFP_KERNEL);
- if (err)
- goto free_req;
- }
-
- req->cmd_len = cmd_len;
- memset(req->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */
- memcpy(req->cmd, cmd, req->cmd_len);
- req->sense = SRpnt->sense;
- req->sense_len = 0;
- req->timeout = timeout;
- req->retries = retries;
- req->end_io_data = SRpnt;
-
- blk_execute_rq_nowait(req->q, NULL, req, 1, osst_end_async);
- return 0;
-free_req:
- blk_put_request(req);
- return DRIVER_ERROR << 24;
-}
-
-/* Do the scsi command. Waits until command performed if do_wait is true.
- Otherwise osst_write_behind_check() is used to check that the command
- has finished. */
-static struct osst_request * osst_do_scsi(struct osst_request *SRpnt, struct osst_tape *STp,
- unsigned char *cmd, int bytes, int direction, int timeout, int retries, int do_wait)
-{
- unsigned char *bp;
- unsigned short use_sg;
-#ifdef OSST_INJECT_ERRORS
- static int inject = 0;
- static int repeat = 0;
-#endif
- struct completion *waiting;
-
- /* if async, make sure there's no command outstanding */
- if (!do_wait && ((STp->buffer)->last_SRpnt)) {
- printk(KERN_ERR "%s: Async command already active.\n",
- tape_name(STp));
- if (signal_pending(current))
- (STp->buffer)->syscall_result = (-EINTR);
- else
- (STp->buffer)->syscall_result = (-EBUSY);
- return NULL;
- }
-
- if (SRpnt == NULL) {
- SRpnt = osst_allocate_request();
- if (SRpnt == NULL) {
- printk(KERN_ERR "%s: Can't allocate SCSI request.\n",
- tape_name(STp));
- if (signal_pending(current))
- (STp->buffer)->syscall_result = (-EINTR);
- else
- (STp->buffer)->syscall_result = (-EBUSY);
- return NULL;
- }
- SRpnt->stp = STp;
- }
-
- /* If async IO, set last_SRpnt. This ptr tells write_behind_check
- which IO is outstanding. It's nulled out when the IO completes. */
- if (!do_wait)
- (STp->buffer)->last_SRpnt = SRpnt;
-
- waiting = &STp->wait;
- init_completion(waiting);
- SRpnt->waiting = waiting;
-
- use_sg = (bytes > STp->buffer->sg[0].length) ? STp->buffer->use_sg : 0;
- if (use_sg) {
- bp = (char *)&(STp->buffer->sg[0]);
- if (STp->buffer->sg_segs < use_sg)
- use_sg = STp->buffer->sg_segs;
- }
- else
- bp = (STp->buffer)->b_data;
-
- memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd));
- STp->buffer->cmdstat.have_sense = 0;
- STp->buffer->syscall_result = 0;
-
- if (osst_execute(SRpnt, cmd, COMMAND_SIZE(cmd[0]), direction, bp, bytes,
- use_sg, timeout, retries))
- /* could not allocate the buffer or request was too large */
- (STp->buffer)->syscall_result = (-EBUSY);
- else if (do_wait) {
- wait_for_completion(waiting);
- SRpnt->waiting = NULL;
- STp->buffer->syscall_result = osst_chk_result(STp, SRpnt);
-#ifdef OSST_INJECT_ERRORS
- if (STp->buffer->syscall_result == 0 &&
- cmd[0] == READ_6 &&
- cmd[4] &&
- ( (++ inject % 83) == 29 ||
- (STp->first_frame_position == 240
- /* or STp->read_error_frame to fail again on the block calculated above */ &&
- ++repeat < 3))) {
- printk(OSST_DEB_MSG "%s:D: Injecting read error\n", tape_name(STp));
- STp->buffer->last_result_fatal = 1;
- }
-#endif
- }
- return SRpnt;
-}
-
-
-/* Handle the write-behind checking (downs the semaphore) */
-static void osst_write_behind_check(struct osst_tape *STp)
-{
- struct osst_buffer * STbuffer;
-
- STbuffer = STp->buffer;
-
-#if DEBUG
- if (STp->write_pending)
- STp->nbr_waits++;
- else
- STp->nbr_finished++;
-#endif
- wait_for_completion(&(STp->wait));
- STp->buffer->last_SRpnt->waiting = NULL;
-
- STp->buffer->syscall_result = osst_chk_result(STp, STp->buffer->last_SRpnt);
-
- if (STp->buffer->syscall_result)
- STp->buffer->syscall_result =
- osst_write_error_recovery(STp, &(STp->buffer->last_SRpnt), 1);
- else
- STp->first_frame_position++;
-
- osst_release_request(STp->buffer->last_SRpnt);
-
- if (STbuffer->writing < STbuffer->buffer_bytes)
- printk(KERN_WARNING "osst :A: write_behind_check: something left in buffer!\n");
-
- STbuffer->last_SRpnt = NULL;
- STbuffer->buffer_bytes -= STbuffer->writing;
- STbuffer->writing = 0;
-
- return;
-}
-
-
-
-/* Onstream specific Routines */
-/*
- * Initialize the OnStream AUX
- */
-static void osst_init_aux(struct osst_tape * STp, int frame_type, int frame_seq_number,
- int logical_blk_num, int blk_sz, int blk_cnt)
-{
- os_aux_t *aux = STp->buffer->aux;
- os_partition_t *par = &aux->partition;
- os_dat_t *dat = &aux->dat;
-
- if (STp->raw) return;
-
- memset(aux, 0, sizeof(*aux));
- aux->format_id = htonl(0);
- memcpy(aux->application_sig, "LIN4", 4);
- aux->hdwr = htonl(0);
- aux->frame_type = frame_type;
-
- switch (frame_type) {
- case OS_FRAME_TYPE_HEADER:
- aux->update_frame_cntr = htonl(STp->update_frame_cntr);
- par->partition_num = OS_CONFIG_PARTITION;
- par->par_desc_ver = OS_PARTITION_VERSION;
- par->wrt_pass_cntr = htons(0xffff);
- /* 0-4 = reserved, 5-9 = header, 2990-2994 = header, 2995-2999 = reserved */
- par->first_frame_ppos = htonl(0);
- par->last_frame_ppos = htonl(0xbb7);
- aux->frame_seq_num = htonl(0);
- aux->logical_blk_num_high = htonl(0);
- aux->logical_blk_num = htonl(0);
- aux->next_mark_ppos = htonl(STp->first_mark_ppos);
- break;
- case OS_FRAME_TYPE_DATA:
- case OS_FRAME_TYPE_MARKER:
- dat->dat_sz = 8;
- dat->reserved1 = 0;
- dat->entry_cnt = 1;
- dat->reserved3 = 0;
- dat->dat_list[0].blk_sz = htonl(blk_sz);
- dat->dat_list[0].blk_cnt = htons(blk_cnt);
- dat->dat_list[0].flags = frame_type==OS_FRAME_TYPE_MARKER?
- OS_DAT_FLAGS_MARK:OS_DAT_FLAGS_DATA;
- dat->dat_list[0].reserved = 0;
- case OS_FRAME_TYPE_EOD:
- aux->update_frame_cntr = htonl(0);
- par->partition_num = OS_DATA_PARTITION;
- par->par_desc_ver = OS_PARTITION_VERSION;
- par->wrt_pass_cntr = htons(STp->wrt_pass_cntr);
- par->first_frame_ppos = htonl(STp->first_data_ppos);
- par->last_frame_ppos = htonl(STp->capacity);
- aux->frame_seq_num = htonl(frame_seq_number);
- aux->logical_blk_num_high = htonl(0);
- aux->logical_blk_num = htonl(logical_blk_num);
- break;
- default: ; /* probably FILL */
- }
- aux->filemark_cnt = htonl(STp->filemark_cnt);
- aux->phys_fm = htonl(0xffffffff);
- aux->last_mark_ppos = htonl(STp->last_mark_ppos);
- aux->last_mark_lbn = htonl(STp->last_mark_lbn);
-}
-
-/*
- * Verify that we have the correct tape frame
- */
-static int osst_verify_frame(struct osst_tape * STp, int frame_seq_number, int quiet)
-{
- char * name = tape_name(STp);
- os_aux_t * aux = STp->buffer->aux;
- os_partition_t * par = &(aux->partition);
- struct st_partstat * STps = &(STp->ps[STp->partition]);
- int blk_cnt, blk_sz, i;
-
- if (STp->raw) {
- if (STp->buffer->syscall_result) {
- for (i=0; i < STp->buffer->sg_segs; i++)
- memset(page_address(sg_page(&STp->buffer->sg[i])),
- 0, STp->buffer->sg[i].length);
- strcpy(STp->buffer->b_data, "READ ERROR ON FRAME");
- } else
- STp->buffer->buffer_bytes = OS_FRAME_SIZE;
- return 1;
- }
- if (STp->buffer->syscall_result) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, read error\n", name);
-#endif
- return 0;
- }
- if (ntohl(aux->format_id) != 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, format_id %u\n", name, ntohl(aux->format_id));
-#endif
- goto err_out;
- }
- if (memcmp(aux->application_sig, STp->application_sig, 4) != 0 &&
- (memcmp(aux->application_sig, "LIN3", 4) != 0 || STp->linux_media_version != 4)) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, incorrect application signature\n", name);
-#endif
- goto err_out;
- }
- if (par->partition_num != OS_DATA_PARTITION) {
- if (!STp->linux_media || STp->linux_media_version != 2) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, partition num %d\n",
- name, par->partition_num);
-#endif
- goto err_out;
- }
- }
- if (par->par_desc_ver != OS_PARTITION_VERSION) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, partition version %d\n", name, par->par_desc_ver);
-#endif
- goto err_out;
- }
- if (ntohs(par->wrt_pass_cntr) != STp->wrt_pass_cntr) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, wrt_pass_cntr %d (expected %d)\n",
- name, ntohs(par->wrt_pass_cntr), STp->wrt_pass_cntr);
-#endif
- goto err_out;
- }
- if (aux->frame_type != OS_FRAME_TYPE_DATA &&
- aux->frame_type != OS_FRAME_TYPE_EOD &&
- aux->frame_type != OS_FRAME_TYPE_MARKER) {
- if (!quiet) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, frame type %x\n", name, aux->frame_type);
-#endif
- }
- goto err_out;
- }
- if (aux->frame_type == OS_FRAME_TYPE_EOD &&
- STp->first_frame_position < STp->eod_frame_ppos) {
- printk(KERN_INFO "%s:I: Skipping premature EOD frame %d\n", name,
- STp->first_frame_position);
- goto err_out;
- }
- if (frame_seq_number != -1 && ntohl(aux->frame_seq_num) != frame_seq_number) {
- if (!quiet) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame, sequence number %u (expected %d)\n",
- name, ntohl(aux->frame_seq_num), frame_seq_number);
-#endif
- }
- goto err_out;
- }
- if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
- STps->eof = ST_FM_HIT;
-
- i = ntohl(aux->filemark_cnt);
- if (STp->header_cache != NULL && i < OS_FM_TAB_MAX && (i > STp->filemark_cnt ||
- STp->first_frame_position - 1 != ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i]))) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: %s filemark %d at frame pos %d\n", name,
- STp->header_cache->dat_fm_tab.fm_tab_ent[i] == 0?"Learned":"Corrected",
- i, STp->first_frame_position - 1);
-#endif
- STp->header_cache->dat_fm_tab.fm_tab_ent[i] = htonl(STp->first_frame_position - 1);
- if (i >= STp->filemark_cnt)
- STp->filemark_cnt = i+1;
- }
- }
- if (aux->frame_type == OS_FRAME_TYPE_EOD) {
- STps->eof = ST_EOD_1;
- STp->frame_in_buffer = 1;
- }
- if (aux->frame_type == OS_FRAME_TYPE_DATA) {
- blk_cnt = ntohs(aux->dat.dat_list[0].blk_cnt);
- blk_sz = ntohl(aux->dat.dat_list[0].blk_sz);
- STp->buffer->buffer_bytes = blk_cnt * blk_sz;
- STp->buffer->read_pointer = 0;
- STp->frame_in_buffer = 1;
-
- /* See what block size was used to write file */
- if (STp->block_size != blk_sz && blk_sz > 0) {
- printk(KERN_INFO
- "%s:I: File was written with block size %d%c, currently %d%c, adjusted to match.\n",
- name, blk_sz<1024?blk_sz:blk_sz/1024,blk_sz<1024?'b':'k',
- STp->block_size<1024?STp->block_size:STp->block_size/1024,
- STp->block_size<1024?'b':'k');
- STp->block_size = blk_sz;
- STp->buffer->buffer_blocks = OS_DATA_SIZE / blk_sz;
- }
- STps->eof = ST_NOEOF;
- }
- STp->frame_seq_number = ntohl(aux->frame_seq_num);
- STp->logical_blk_num = ntohl(aux->logical_blk_num);
- return 1;
-
-err_out:
- if (STp->read_error_frame == 0)
- STp->read_error_frame = STp->first_frame_position - 1;
- return 0;
-}
-
-/*
- * Wait for the unit to become Ready
- */
-static int osst_wait_ready(struct osst_tape * STp, struct osst_request ** aSRpnt,
- unsigned timeout, int initial_delay)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- unsigned long startwait = jiffies;
-#if DEBUG
- int dbg = debugging;
- char * name = tape_name(STp);
-
- printk(OSST_DEB_MSG "%s:D: Reached onstream wait ready\n", name);
-#endif
-
- if (initial_delay > 0)
- msleep(jiffies_to_msecs(initial_delay));
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
- *aSRpnt = SRpnt;
- if (!SRpnt) return (-EBUSY);
-
- while ( STp->buffer->syscall_result && time_before(jiffies, startwait + timeout*HZ) &&
- (( SRpnt->sense[2] == 2 && SRpnt->sense[12] == 4 &&
- (SRpnt->sense[13] == 1 || SRpnt->sense[13] == 8) ) ||
- ( SRpnt->sense[2] == 6 && SRpnt->sense[12] == 0x28 &&
- SRpnt->sense[13] == 0 ) )) {
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG "%s:D: Sleeping in onstream wait ready\n", name);
- printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
- debugging = 0;
- }
-#endif
- msleep(100);
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
- }
- *aSRpnt = SRpnt;
-#if DEBUG
- debugging = dbg;
-#endif
- if ( STp->buffer->syscall_result &&
- osst_write_error_recovery(STp, aSRpnt, 0) ) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Abnormal exit from onstream wait ready\n", name);
- printk(OSST_DEB_MSG "%s:D: Result = %d, Sense: 0=%02x, 2=%02x, 12=%02x, 13=%02x\n", name,
- STp->buffer->syscall_result, SRpnt->sense[0], SRpnt->sense[2],
- SRpnt->sense[12], SRpnt->sense[13]);
-#endif
- return (-EIO);
- }
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Normal exit from onstream wait ready\n", name);
-#endif
- return 0;
-}
-
-/*
- * Wait for a tape to be inserted in the unit
- */
-static int osst_wait_for_medium(struct osst_tape * STp, struct osst_request ** aSRpnt, unsigned timeout)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- unsigned long startwait = jiffies;
-#if DEBUG
- int dbg = debugging;
- char * name = tape_name(STp);
-
- printk(OSST_DEB_MSG "%s:D: Reached onstream wait for medium\n", name);
-#endif
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
- *aSRpnt = SRpnt;
- if (!SRpnt) return (-EBUSY);
-
- while ( STp->buffer->syscall_result && time_before(jiffies, startwait + timeout*HZ) &&
- SRpnt->sense[2] == 2 && SRpnt->sense[12] == 0x3a && SRpnt->sense[13] == 0 ) {
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG "%s:D: Sleeping in onstream wait medium\n", name);
- printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
- debugging = 0;
- }
-#endif
- msleep(100);
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
- }
- *aSRpnt = SRpnt;
-#if DEBUG
- debugging = dbg;
-#endif
- if ( STp->buffer->syscall_result && SRpnt->sense[2] != 2 &&
- SRpnt->sense[12] != 4 && SRpnt->sense[13] == 1) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Abnormal exit from onstream wait medium\n", name);
- printk(OSST_DEB_MSG "%s:D: Result = %d, Sense: 0=%02x, 2=%02x, 12=%02x, 13=%02x\n", name,
- STp->buffer->syscall_result, SRpnt->sense[0], SRpnt->sense[2],
- SRpnt->sense[12], SRpnt->sense[13]);
-#endif
- return 0;
- }
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Normal exit from onstream wait medium\n", name);
-#endif
- return 1;
-}
-
-static int osst_position_tape_and_confirm(struct osst_tape * STp, struct osst_request ** aSRpnt, int frame)
-{
- int retval;
-
- osst_wait_ready(STp, aSRpnt, 15 * 60, 0); /* TODO - can this catch a write error? */
- retval = osst_set_frame_position(STp, aSRpnt, frame, 0);
- if (retval) return (retval);
- osst_wait_ready(STp, aSRpnt, 15 * 60, OSST_WAIT_POSITION_COMPLETE);
- return (osst_get_frame_position(STp, aSRpnt));
-}
-
-/*
- * Wait for write(s) to complete
- */
-static int osst_flush_drive_buffer(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- int result = 0;
- int delay = OSST_WAIT_WRITE_COMPLETE;
-#if DEBUG
- char * name = tape_name(STp);
-
- printk(OSST_DEB_MSG "%s:D: Reached onstream flush drive buffer (write filemark)\n", name);
-#endif
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = WRITE_FILEMARKS;
- cmd[1] = 1;
-
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
- *aSRpnt = SRpnt;
- if (!SRpnt) return (-EBUSY);
- if (STp->buffer->syscall_result) {
- if ((SRpnt->sense[2] & 0x0f) == 2 && SRpnt->sense[12] == 4) {
- if (SRpnt->sense[13] == 8) {
- delay = OSST_WAIT_LONG_WRITE_COMPLETE;
- }
- } else
- result = osst_write_error_recovery(STp, aSRpnt, 0);
- }
- result |= osst_wait_ready(STp, aSRpnt, 5 * 60, delay);
- STp->ps[STp->partition].rw = OS_WRITING_COMPLETE;
-
- return (result);
-}
-
-#define OSST_POLL_PER_SEC 10
-static int osst_wait_frame(struct osst_tape * STp, struct osst_request ** aSRpnt, int curr, int minlast, int to)
-{
- unsigned long startwait = jiffies;
- char * name = tape_name(STp);
-#if DEBUG
- char notyetprinted = 1;
-#endif
- if (minlast >= 0 && STp->ps[STp->partition].rw != ST_READING)
- printk(KERN_ERR "%s:A: Waiting for frame without having initialized read!\n", name);
-
- while (time_before (jiffies, startwait + to*HZ))
- {
- int result;
- result = osst_get_frame_position(STp, aSRpnt);
- if (result == -EIO)
- if ((result = osst_write_error_recovery(STp, aSRpnt, 0)) == 0)
- return 0; /* successful recovery leaves drive ready for frame */
- if (result < 0) break;
- if (STp->first_frame_position == curr &&
- ((minlast < 0 &&
- (signed)STp->last_frame_position > (signed)curr + minlast) ||
- (minlast >= 0 && STp->cur_frames > minlast)
- ) && result >= 0)
- {
-#if DEBUG
- if (debugging || time_after_eq(jiffies, startwait + 2*HZ/OSST_POLL_PER_SEC))
- printk (OSST_DEB_MSG
- "%s:D: Succ wait f fr %i (>%i): %i-%i %i (%i): %3li.%li s\n",
- name, curr, curr+minlast, STp->first_frame_position,
- STp->last_frame_position, STp->cur_frames,
- result, (jiffies-startwait)/HZ,
- (((jiffies-startwait)%HZ)*10)/HZ);
-#endif
- return 0;
- }
-#if DEBUG
- if (time_after_eq(jiffies, startwait + 2*HZ/OSST_POLL_PER_SEC) && notyetprinted)
- {
- printk (OSST_DEB_MSG "%s:D: Wait for frame %i (>%i): %i-%i %i (%i)\n",
- name, curr, curr+minlast, STp->first_frame_position,
- STp->last_frame_position, STp->cur_frames, result);
- notyetprinted--;
- }
-#endif
- msleep(1000 / OSST_POLL_PER_SEC);
- }
-#if DEBUG
- printk (OSST_DEB_MSG "%s:D: Fail wait f fr %i (>%i): %i-%i %i: %3li.%li s\n",
- name, curr, curr+minlast, STp->first_frame_position,
- STp->last_frame_position, STp->cur_frames,
- (jiffies-startwait)/HZ, (((jiffies-startwait)%HZ)*10)/HZ);
-#endif
- return -EBUSY;
-}
-
-static int osst_recover_wait_frame(struct osst_tape * STp, struct osst_request ** aSRpnt, int writing)
-{
- struct osst_request * SRpnt;
- unsigned char cmd[MAX_COMMAND_SIZE];
- unsigned long startwait = jiffies;
- int retval = 1;
- char * name = tape_name(STp);
-
- if (writing) {
- char mybuf[24];
- char * olddata = STp->buffer->b_data;
- int oldsize = STp->buffer->buffer_size;
-
- /* write zero fm then read pos - if shows write error, try to recover - if no progress, wait */
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = WRITE_FILEMARKS;
- cmd[1] = 1;
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout,
- MAX_RETRIES, 1);
-
- while (retval && time_before (jiffies, startwait + 5*60*HZ)) {
-
- if (STp->buffer->syscall_result && (SRpnt->sense[2] & 0x0f) != 2) {
-
- /* some failure - not just not-ready */
- retval = osst_write_error_recovery(STp, aSRpnt, 0);
- break;
- }
- schedule_timeout_interruptible(HZ / OSST_POLL_PER_SEC);
-
- STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = READ_POSITION;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 20, DMA_FROM_DEVICE, STp->timeout,
- MAX_RETRIES, 1);
-
- retval = ( STp->buffer->syscall_result || (STp->buffer)->b_data[15] > 25 );
- STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
- }
- if (retval)
- printk(KERN_ERR "%s:E: Device did not succeed to write buffered data\n", name);
- } else
- /* TODO - figure out which error conditions can be handled */
- if (STp->buffer->syscall_result)
- printk(KERN_WARNING
- "%s:W: Recover_wait_frame(read) cannot handle %02x:%02x:%02x\n", name,
- (*aSRpnt)->sense[ 2] & 0x0f,
- (*aSRpnt)->sense[12],
- (*aSRpnt)->sense[13]);
-
- return retval;
-}
-
-/*
- * Read the next OnStream tape frame at the current location
- */
-static int osst_read_frame(struct osst_tape * STp, struct osst_request ** aSRpnt, int timeout)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- int retval = 0;
-#if DEBUG
- os_aux_t * aux = STp->buffer->aux;
- char * name = tape_name(STp);
-#endif
-
- if (STp->poll)
- if (osst_wait_frame (STp, aSRpnt, STp->first_frame_position, 0, timeout))
- retval = osst_recover_wait_frame(STp, aSRpnt, 0);
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = READ_6;
- cmd[1] = 1;
- cmd[4] = 1;
-
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Reading frame from OnStream tape\n", name);
-#endif
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, DMA_FROM_DEVICE,
- STp->timeout, MAX_RETRIES, 1);
- *aSRpnt = SRpnt;
- if (!SRpnt)
- return (-EBUSY);
-
- if ((STp->buffer)->syscall_result) {
- retval = 1;
- if (STp->read_error_frame == 0) {
- STp->read_error_frame = STp->first_frame_position;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Recording read error at %d\n", name, STp->read_error_frame);
-#endif
- }
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
- name,
- SRpnt->sense[0], SRpnt->sense[1],
- SRpnt->sense[2], SRpnt->sense[3],
- SRpnt->sense[4], SRpnt->sense[5],
- SRpnt->sense[6], SRpnt->sense[7]);
-#endif
- }
- else
- STp->first_frame_position++;
-#if DEBUG
- if (debugging) {
- char sig[8]; int i;
- for (i=0;i<4;i++)
- sig[i] = aux->application_sig[i]<32?'^':aux->application_sig[i];
- sig[4] = '\0';
- printk(OSST_DEB_MSG
- "%s:D: AUX: %s UpdFrCt#%d Wpass#%d %s FrSeq#%d LogBlk#%d Qty=%d Sz=%d\n", name, sig,
- ntohl(aux->update_frame_cntr), ntohs(aux->partition.wrt_pass_cntr),
- aux->frame_type==1?"EOD":aux->frame_type==2?"MARK":
- aux->frame_type==8?"HEADR":aux->frame_type==0x80?"DATA":"FILL",
- ntohl(aux->frame_seq_num), ntohl(aux->logical_blk_num),
- ntohs(aux->dat.dat_list[0].blk_cnt), ntohl(aux->dat.dat_list[0].blk_sz) );
- if (aux->frame_type==2)
- printk(OSST_DEB_MSG "%s:D: mark_cnt=%d, last_mark_ppos=%d, last_mark_lbn=%d\n", name,
- ntohl(aux->filemark_cnt), ntohl(aux->last_mark_ppos), ntohl(aux->last_mark_lbn));
- printk(OSST_DEB_MSG "%s:D: Exit read frame from OnStream tape with code %d\n", name, retval);
- }
-#endif
- return (retval);
-}
-
-static int osst_initiate_read(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- struct st_partstat * STps = &(STp->ps[STp->partition]);
- struct osst_request * SRpnt ;
- unsigned char cmd[MAX_COMMAND_SIZE];
- int retval = 0;
- char * name = tape_name(STp);
-
- if (STps->rw != ST_READING) { /* Initialize read operation */
- if (STps->rw == ST_WRITING || STp->dirty) {
- STp->write_type = OS_WRITE_DATA;
- osst_flush_write_buffer(STp, aSRpnt);
- osst_flush_drive_buffer(STp, aSRpnt);
- }
- STps->rw = ST_READING;
- STp->frame_in_buffer = 0;
-
- /*
- * Issue a read 0 command to get the OnStream drive
- * read frames into its buffer.
- */
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = READ_6;
- cmd[1] = 1;
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Start Read Ahead on OnStream tape\n", name);
-#endif
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
- *aSRpnt = SRpnt;
- if ((retval = STp->buffer->syscall_result))
- printk(KERN_WARNING "%s:W: Error starting read ahead\n", name);
- }
-
- return retval;
-}
-
-static int osst_get_logical_frame(struct osst_tape * STp, struct osst_request ** aSRpnt,
- int frame_seq_number, int quiet)
-{
- struct st_partstat * STps = &(STp->ps[STp->partition]);
- char * name = tape_name(STp);
- int cnt = 0,
- bad = 0,
- past = 0,
- x,
- position;
-
- /*
- * If we want just any frame (-1) and there is a frame in the buffer, return it
- */
- if (frame_seq_number == -1 && STp->frame_in_buffer) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Frame %d still in buffer\n", name, STp->frame_seq_number);
-#endif
- return (STps->eof);
- }
- /*
- * Search and wait for the next logical tape frame
- */
- while (1) {
- if (cnt++ > 400) {
- printk(KERN_ERR "%s:E: Couldn't find logical frame %d, aborting\n",
- name, frame_seq_number);
- if (STp->read_error_frame) {
- osst_set_frame_position(STp, aSRpnt, STp->read_error_frame, 0);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Repositioning tape to bad frame %d\n",
- name, STp->read_error_frame);
-#endif
- STp->read_error_frame = 0;
- STp->abort_count++;
- }
- return (-EIO);
- }
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Looking for frame %d, attempt %d\n",
- name, frame_seq_number, cnt);
-#endif
- if ( osst_initiate_read(STp, aSRpnt)
- || ( (!STp->frame_in_buffer) && osst_read_frame(STp, aSRpnt, 30) ) ) {
- if (STp->raw)
- return (-EIO);
- position = osst_get_frame_position(STp, aSRpnt);
- if (position >= 0xbae && position < 0xbb8)
- position = 0xbb8;
- else if (position > STp->eod_frame_ppos || ++bad == 10) {
- position = STp->read_error_frame - 1;
- bad = 0;
- }
- else {
- position += 29;
- cnt += 19;
- }
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Bad frame detected, positioning tape to block %d\n",
- name, position);
-#endif
- osst_set_frame_position(STp, aSRpnt, position, 0);
- continue;
- }
- if (osst_verify_frame(STp, frame_seq_number, quiet))
- break;
- if (osst_verify_frame(STp, -1, quiet)) {
- x = ntohl(STp->buffer->aux->frame_seq_num);
- if (STp->fast_open) {
- printk(KERN_WARNING
- "%s:W: Found logical frame %d instead of %d after fast open\n",
- name, x, frame_seq_number);
- STp->header_ok = 0;
- STp->read_error_frame = 0;
- return (-EIO);
- }
- if (x > frame_seq_number) {
- if (++past > 3) {
- /* positioning backwards did not bring us to the desired frame */
- position = STp->read_error_frame - 1;
- }
- else {
- position = osst_get_frame_position(STp, aSRpnt)
- + frame_seq_number - x - 1;
-
- if (STp->first_frame_position >= 3000 && position < 3000)
- position -= 10;
- }
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: Found logical frame %d while looking for %d: back up %d\n",
- name, x, frame_seq_number,
- STp->first_frame_position - position);
-#endif
- osst_set_frame_position(STp, aSRpnt, position, 0);
- cnt += 10;
- }
- else
- past = 0;
- }
- if (osst_get_frame_position(STp, aSRpnt) == 0xbaf) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping config partition\n", name);
-#endif
- osst_set_frame_position(STp, aSRpnt, 0xbb8, 0);
- cnt--;
- }
- STp->frame_in_buffer = 0;
- }
- if (cnt > 1) {
- STp->recover_count++;
- STp->recover_erreg++;
- printk(KERN_WARNING "%s:I: Don't worry, Read error at position %d recovered\n",
- name, STp->read_error_frame);
- }
- STp->read_count++;
-
-#if DEBUG
- if (debugging || STps->eof)
- printk(OSST_DEB_MSG
- "%s:D: Exit get logical frame (%d=>%d) from OnStream tape with code %d\n",
- name, frame_seq_number, STp->frame_seq_number, STps->eof);
-#endif
- STp->fast_open = 0;
- STp->read_error_frame = 0;
- return (STps->eof);
-}
-
-static int osst_seek_logical_blk(struct osst_tape * STp, struct osst_request ** aSRpnt, int logical_blk_num)
-{
- struct st_partstat * STps = &(STp->ps[STp->partition]);
- char * name = tape_name(STp);
- int retries = 0;
- int frame_seq_estimate, ppos_estimate, move;
-
- if (logical_blk_num < 0) logical_blk_num = 0;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Seeking logical block %d (now at %d, size %d%c)\n",
- name, logical_blk_num, STp->logical_blk_num,
- STp->block_size<1024?STp->block_size:STp->block_size/1024,
- STp->block_size<1024?'b':'k');
-#endif
- /* Do we know where we are? */
- if (STps->drv_block >= 0) {
- move = logical_blk_num - STp->logical_blk_num;
- if (move < 0) move -= (OS_DATA_SIZE / STp->block_size) - 1;
- move /= (OS_DATA_SIZE / STp->block_size);
- frame_seq_estimate = STp->frame_seq_number + move;
- } else
- frame_seq_estimate = logical_blk_num * STp->block_size / OS_DATA_SIZE;
-
- if (frame_seq_estimate < 2980) ppos_estimate = frame_seq_estimate + 10;
- else ppos_estimate = frame_seq_estimate + 20;
- while (++retries < 10) {
- if (ppos_estimate > STp->eod_frame_ppos-2) {
- frame_seq_estimate += STp->eod_frame_ppos - 2 - ppos_estimate;
- ppos_estimate = STp->eod_frame_ppos - 2;
- }
- if (frame_seq_estimate < 0) {
- frame_seq_estimate = 0;
- ppos_estimate = 10;
- }
- osst_set_frame_position(STp, aSRpnt, ppos_estimate, 0);
- if (osst_get_logical_frame(STp, aSRpnt, frame_seq_estimate, 1) >= 0) {
- /* we've located the estimated frame, now does it have our block? */
- if (logical_blk_num < STp->logical_blk_num ||
- logical_blk_num >= STp->logical_blk_num + ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt)) {
- if (STps->eof == ST_FM_HIT)
- move = logical_blk_num < STp->logical_blk_num? -2 : 1;
- else {
- move = logical_blk_num - STp->logical_blk_num;
- if (move < 0) move -= (OS_DATA_SIZE / STp->block_size) - 1;
- move /= (OS_DATA_SIZE / STp->block_size);
- }
- if (!move) move = logical_blk_num > STp->logical_blk_num ? 1 : -1;
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: Seek retry %d at ppos %d fsq %d (est %d) lbn %d (need %d) move %d\n",
- name, retries, ppos_estimate, STp->frame_seq_number, frame_seq_estimate,
- STp->logical_blk_num, logical_blk_num, move);
-#endif
- frame_seq_estimate += move;
- ppos_estimate += move;
- continue;
- } else {
- STp->buffer->read_pointer = (logical_blk_num - STp->logical_blk_num) * STp->block_size;
- STp->buffer->buffer_bytes -= STp->buffer->read_pointer;
- STp->logical_blk_num = logical_blk_num;
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: Seek success at ppos %d fsq %d in_buf %d, bytes %d, ptr %d*%d\n",
- name, ppos_estimate, STp->frame_seq_number, STp->frame_in_buffer,
- STp->buffer->buffer_bytes, STp->buffer->read_pointer / STp->block_size,
- STp->block_size);
-#endif
- STps->drv_file = ntohl(STp->buffer->aux->filemark_cnt);
- if (STps->eof == ST_FM_HIT) {
- STps->drv_file++;
- STps->drv_block = 0;
- } else {
- STps->drv_block = ntohl(STp->buffer->aux->last_mark_lbn)?
- STp->logical_blk_num -
- (STps->drv_file ? ntohl(STp->buffer->aux->last_mark_lbn) + 1 : 0):
- -1;
- }
- STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_NOEOF;
- return 0;
- }
- }
- if (osst_get_logical_frame(STp, aSRpnt, -1, 1) < 0)
- goto error;
- /* we are not yet at the estimated frame, adjust our estimate of its physical position */
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Seek retry %d at ppos %d fsq %d (est %d) lbn %d (need %d)\n",
- name, retries, ppos_estimate, STp->frame_seq_number, frame_seq_estimate,
- STp->logical_blk_num, logical_blk_num);
-#endif
- if (frame_seq_estimate != STp->frame_seq_number)
- ppos_estimate += frame_seq_estimate - STp->frame_seq_number;
- else
- break;
- }
-error:
- printk(KERN_ERR "%s:E: Couldn't seek to logical block %d (at %d), %d retries\n",
- name, logical_blk_num, STp->logical_blk_num, retries);
- return (-EIO);
-}
-
-/* The values below are based on the OnStream frame payload size of 32K == 2**15,
- * that is, OSST_FRAME_SHIFT + OSST_SECTOR_SHIFT must be 15. With a minimum block
- * size of 512 bytes, we need to be able to resolve 32K/512 == 64 == 2**6 positions
- * inside each frame. Finally, OSST_SECTOR_MASK == 2**OSST_FRAME_SHIFT - 1.
- */
-#define OSST_FRAME_SHIFT 6
-#define OSST_SECTOR_SHIFT 9
-#define OSST_SECTOR_MASK 0x03F
-
-static int osst_get_sector(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- int sector;
-#if DEBUG
- char * name = tape_name(STp);
-
- printk(OSST_DEB_MSG
- "%s:D: Positioned at ppos %d, frame %d, lbn %d, file %d, blk %d, %cptr %d, eof %d\n",
- name, STp->first_frame_position, STp->frame_seq_number, STp->logical_blk_num,
- STp->ps[STp->partition].drv_file, STp->ps[STp->partition].drv_block,
- STp->ps[STp->partition].rw == ST_WRITING?'w':'r',
- STp->ps[STp->partition].rw == ST_WRITING?STp->buffer->buffer_bytes:
- STp->buffer->read_pointer, STp->ps[STp->partition].eof);
-#endif
- /* do we know where we are inside a file? */
- if (STp->ps[STp->partition].drv_block >= 0) {
- sector = (STp->frame_in_buffer ? STp->first_frame_position-1 :
- STp->first_frame_position) << OSST_FRAME_SHIFT;
- if (STp->ps[STp->partition].rw == ST_WRITING)
- sector |= (STp->buffer->buffer_bytes >> OSST_SECTOR_SHIFT) & OSST_SECTOR_MASK;
- else
- sector |= (STp->buffer->read_pointer >> OSST_SECTOR_SHIFT) & OSST_SECTOR_MASK;
- } else {
- sector = osst_get_frame_position(STp, aSRpnt);
- if (sector > 0)
- sector <<= OSST_FRAME_SHIFT;
- }
- return sector;
-}
-
-static int osst_seek_sector(struct osst_tape * STp, struct osst_request ** aSRpnt, int sector)
-{
- struct st_partstat * STps = &(STp->ps[STp->partition]);
- int frame = sector >> OSST_FRAME_SHIFT,
- offset = (sector & OSST_SECTOR_MASK) << OSST_SECTOR_SHIFT,
- r;
-#if DEBUG
- char * name = tape_name(STp);
-
- printk(OSST_DEB_MSG "%s:D: Seeking sector %d in frame %d at offset %d\n",
- name, sector, frame, offset);
-#endif
- if (frame < 0 || frame >= STp->capacity) return (-ENXIO);
-
- if (frame <= STp->first_data_ppos) {
- STp->frame_seq_number = STp->logical_blk_num = STps->drv_file = STps->drv_block = 0;
- return (osst_set_frame_position(STp, aSRpnt, frame, 0));
- }
- r = osst_set_frame_position(STp, aSRpnt, offset?frame:frame-1, 0);
- if (r < 0) return r;
-
- r = osst_get_logical_frame(STp, aSRpnt, -1, 1);
- if (r < 0) return r;
-
- if (osst_get_frame_position(STp, aSRpnt) != (offset?frame+1:frame)) return (-EIO);
-
- if (offset) {
- STp->logical_blk_num += offset / STp->block_size;
- STp->buffer->read_pointer = offset;
- STp->buffer->buffer_bytes -= offset;
- } else {
- STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
- STp->buffer->buffer_bytes = STp->buffer->read_pointer = 0;
- }
- STps->drv_file = ntohl(STp->buffer->aux->filemark_cnt);
- if (STps->eof == ST_FM_HIT) {
- STps->drv_file++;
- STps->drv_block = 0;
- } else {
- STps->drv_block = ntohl(STp->buffer->aux->last_mark_lbn)?
- STp->logical_blk_num -
- (STps->drv_file ? ntohl(STp->buffer->aux->last_mark_lbn) + 1 : 0):
- -1;
- }
- STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_NOEOF;
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: Now positioned at ppos %d, frame %d, lbn %d, file %d, blk %d, rptr %d, eof %d\n",
- name, STp->first_frame_position, STp->frame_seq_number, STp->logical_blk_num,
- STps->drv_file, STps->drv_block, STp->buffer->read_pointer, STps->eof);
-#endif
- return 0;
-}
-
-/*
- * Read back the drive's internal buffer contents, as a part
- * of the write error recovery mechanism for old OnStream
- * firmware revisions.
- * Precondition for this function to work: all frames in the
- * drive's buffer must be of one type (DATA, MARK or EOD)!
- */
-static int osst_read_back_buffer_and_rewrite(struct osst_tape * STp, struct osst_request ** aSRpnt,
- unsigned int frame, unsigned int skip, int pending)
-{
- struct osst_request * SRpnt = * aSRpnt;
- unsigned char * buffer, * p;
- unsigned char cmd[MAX_COMMAND_SIZE];
- int flag, new_frame, i;
- int nframes = STp->cur_frames;
- int blks_per_frame = ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
- int frame_seq_number = ntohl(STp->buffer->aux->frame_seq_num)
- - (nframes + pending - 1);
- int logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num)
- - (nframes + pending - 1) * blks_per_frame;
- char * name = tape_name(STp);
- unsigned long startwait = jiffies;
-#if DEBUG
- int dbg = debugging;
-#endif
-
- if ((buffer = vmalloc((nframes + 1) * OS_DATA_SIZE)) == NULL)
- return (-EIO);
-
- printk(KERN_INFO "%s:I: Reading back %d frames from drive buffer%s\n",
- name, nframes, pending?" and one that was pending":"");
-
- osst_copy_from_buffer(STp->buffer, (p = &buffer[nframes * OS_DATA_SIZE]));
-#if DEBUG
- if (pending && debugging)
- printk(OSST_DEB_MSG "%s:D: Pending frame %d (lblk %d), data %02x %02x %02x %02x\n",
- name, frame_seq_number + nframes,
- logical_blk_num + nframes * blks_per_frame,
- p[0], p[1], p[2], p[3]);
-#endif
- for (i = 0, p = buffer; i < nframes; i++, p += OS_DATA_SIZE) {
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = 0x3C; /* Buffer Read */
- cmd[1] = 6; /* Retrieve Faulty Block */
- cmd[7] = 32768 >> 8;
- cmd[8] = 32768 & 0xff;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, DMA_FROM_DEVICE,
- STp->timeout, MAX_RETRIES, 1);
-
- if ((STp->buffer)->syscall_result || !SRpnt) {
- printk(KERN_ERR "%s:E: Failed to read frame back from OnStream buffer\n", name);
- vfree(buffer);
- *aSRpnt = SRpnt;
- return (-EIO);
- }
- osst_copy_from_buffer(STp->buffer, p);
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Read back logical frame %d, data %02x %02x %02x %02x\n",
- name, frame_seq_number + i, p[0], p[1], p[2], p[3]);
-#endif
- }
- *aSRpnt = SRpnt;
- osst_get_frame_position(STp, aSRpnt);
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Frames left in buffer: %d\n", name, STp->cur_frames);
-#endif
- /* Write synchronously so we can be sure we're OK again and don't have to recover recursively */
- /* In the header we don't actually re-write the frames that fail, just the ones after them */
-
- for (flag=1, new_frame=frame, p=buffer, i=0; i < nframes + pending; ) {
-
- if (flag) {
- if (STp->write_type == OS_WRITE_HEADER) {
- i += skip;
- p += skip * OS_DATA_SIZE;
- }
- else if (new_frame < 2990 && new_frame+skip+nframes+pending >= 2990)
- new_frame = 3000-i;
- else
- new_frame += skip;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Position to frame %d, write fseq %d\n",
- name, new_frame+i, frame_seq_number+i);
-#endif
- osst_set_frame_position(STp, aSRpnt, new_frame + i, 0);
- osst_wait_ready(STp, aSRpnt, 60, OSST_WAIT_POSITION_COMPLETE);
- osst_get_frame_position(STp, aSRpnt);
- SRpnt = * aSRpnt;
-
- if (new_frame > frame + 1000) {
- printk(KERN_ERR "%s:E: Failed to find writable tape media\n", name);
- vfree(buffer);
- return (-EIO);
- }
- if ( i >= nframes + pending ) break;
- flag = 0;
- }
- osst_copy_to_buffer(STp->buffer, p);
- /*
- * IMPORTANT: for error recovery to work, _never_ queue frames with mixed frame type!
- */
- osst_init_aux(STp, STp->buffer->aux->frame_type, frame_seq_number+i,
- logical_blk_num + i*blks_per_frame,
- ntohl(STp->buffer->aux->dat.dat_list[0].blk_sz), blks_per_frame);
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = WRITE_6;
- cmd[1] = 1;
- cmd[4] = 1;
-
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG
- "%s:D: About to write frame %d, seq %d, lbn %d, data %02x %02x %02x %02x\n",
- name, new_frame+i, frame_seq_number+i, logical_blk_num + i*blks_per_frame,
- p[0], p[1], p[2], p[3]);
-#endif
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, DMA_TO_DEVICE,
- STp->timeout, MAX_RETRIES, 1);
-
- if (STp->buffer->syscall_result)
- flag = 1;
- else {
- p += OS_DATA_SIZE; i++;
-
- /* if we just sent the last frame, wait till all successfully written */
- if ( i == nframes + pending ) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Check re-write successful\n", name);
-#endif
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = WRITE_FILEMARKS;
- cmd[1] = 1;
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
- STp->timeout, MAX_RETRIES, 1);
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG "%s:D: Sleeping in re-write wait ready\n", name);
- printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
- debugging = 0;
- }
-#endif
- flag = STp->buffer->syscall_result;
- while ( !flag && time_before(jiffies, startwait + 60*HZ) ) {
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout,
- MAX_RETRIES, 1);
-
- if (SRpnt->sense[2] == 2 && SRpnt->sense[12] == 4 &&
- (SRpnt->sense[13] == 1 || SRpnt->sense[13] == 8)) {
- /* in the process of becoming ready */
- msleep(100);
- continue;
- }
- if (STp->buffer->syscall_result)
- flag = 1;
- break;
- }
-#if DEBUG
- debugging = dbg;
- printk(OSST_DEB_MSG "%s:D: Wait re-write finished\n", name);
-#endif
- }
- }
- *aSRpnt = SRpnt;
- if (flag) {
- if ((SRpnt->sense[ 2] & 0x0f) == 13 &&
- SRpnt->sense[12] == 0 &&
- SRpnt->sense[13] == 2) {
- printk(KERN_ERR "%s:E: Volume overflow in write error recovery\n", name);
- vfree(buffer);
- return (-EIO); /* hit end of tape = fail */
- }
- i = ((SRpnt->sense[3] << 24) |
- (SRpnt->sense[4] << 16) |
- (SRpnt->sense[5] << 8) |
- SRpnt->sense[6] ) - new_frame;
- p = &buffer[i * OS_DATA_SIZE];
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Additional write error at %d\n", name, new_frame+i);
-#endif
- osst_get_frame_position(STp, aSRpnt);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: reported frame positions: host = %d, tape = %d, buffer = %d\n",
- name, STp->first_frame_position, STp->last_frame_position, STp->cur_frames);
-#endif
- }
- }
- if (flag) {
- /* error recovery did not successfully complete */
- printk(KERN_ERR "%s:D: Write error recovery failed in %s\n", name,
- STp->write_type == OS_WRITE_HEADER?"header":"body");
- }
- if (!pending)
- osst_copy_to_buffer(STp->buffer, p); /* so buffer content == at entry in all cases */
- vfree(buffer);
- return 0;
-}
-
-static int osst_reposition_and_retry(struct osst_tape * STp, struct osst_request ** aSRpnt,
- unsigned int frame, unsigned int skip, int pending)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- char * name = tape_name(STp);
- int expected = 0;
- int attempts = 1000 / skip;
- int flag = 1;
- unsigned long startwait = jiffies;
-#if DEBUG
- int dbg = debugging;
-#endif
-
- while (attempts && time_before(jiffies, startwait + 60*HZ)) {
- if (flag) {
-#if DEBUG
- debugging = dbg;
-#endif
- if (frame < 2990 && frame+skip+STp->cur_frames+pending >= 2990)
- frame = 3000-skip;
- expected = frame+skip+STp->cur_frames+pending;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Position to fppos %d, re-write from fseq %d\n",
- name, frame+skip, STp->frame_seq_number-STp->cur_frames-pending);
-#endif
- osst_set_frame_position(STp, aSRpnt, frame + skip, 1);
- flag = 0;
- attempts--;
- schedule_timeout_interruptible(msecs_to_jiffies(100));
- }
- if (osst_get_frame_position(STp, aSRpnt) < 0) { /* additional write error */
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Addl error, host %d, tape %d, buffer %d\n",
- name, STp->first_frame_position,
- STp->last_frame_position, STp->cur_frames);
-#endif
- frame = STp->last_frame_position;
- flag = 1;
- continue;
- }
- if (pending && STp->cur_frames < 50) {
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = WRITE_6;
- cmd[1] = 1;
- cmd[4] = 1;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: About to write pending fseq %d at fppos %d\n",
- name, STp->frame_seq_number-1, STp->first_frame_position);
-#endif
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, DMA_TO_DEVICE,
- STp->timeout, MAX_RETRIES, 1);
- *aSRpnt = SRpnt;
-
- if (STp->buffer->syscall_result) { /* additional write error */
- if ((SRpnt->sense[ 2] & 0x0f) == 13 &&
- SRpnt->sense[12] == 0 &&
- SRpnt->sense[13] == 2) {
- printk(KERN_ERR
- "%s:E: Volume overflow in write error recovery\n",
- name);
- break; /* hit end of tape = fail */
- }
- flag = 1;
- }
- else
- pending = 0;
-
- continue;
- }
- if (STp->cur_frames == 0) {
-#if DEBUG
- debugging = dbg;
- printk(OSST_DEB_MSG "%s:D: Wait re-write finished\n", name);
-#endif
- if (STp->first_frame_position != expected) {
- printk(KERN_ERR "%s:A: Actual position %d - expected %d\n",
- name, STp->first_frame_position, expected);
- return (-EIO);
- }
- return 0;
- }
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG "%s:D: Sleeping in re-write wait ready\n", name);
- printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
- debugging = 0;
- }
-#endif
- schedule_timeout_interruptible(msecs_to_jiffies(100));
- }
- printk(KERN_ERR "%s:E: Failed to find valid tape media\n", name);
-#if DEBUG
- debugging = dbg;
-#endif
- return (-EIO);
-}
-
-/*
- * Error recovery algorithm for the OnStream tape.
- */
-
-static int osst_write_error_recovery(struct osst_tape * STp, struct osst_request ** aSRpnt, int pending)
-{
- struct osst_request * SRpnt = * aSRpnt;
- struct st_partstat * STps = & STp->ps[STp->partition];
- char * name = tape_name(STp);
- int retval = 0;
- int rw_state;
- unsigned int frame, skip;
-
- rw_state = STps->rw;
-
- if ((SRpnt->sense[ 2] & 0x0f) != 3
- || SRpnt->sense[12] != 12
- || SRpnt->sense[13] != 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Write error recovery cannot handle %02x:%02x:%02x\n", name,
- SRpnt->sense[2], SRpnt->sense[12], SRpnt->sense[13]);
-#endif
- return (-EIO);
- }
- frame = (SRpnt->sense[3] << 24) |
- (SRpnt->sense[4] << 16) |
- (SRpnt->sense[5] << 8) |
- SRpnt->sense[6];
- skip = SRpnt->sense[9];
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Detected physical bad frame at %u, advised to skip %d\n", name, frame, skip);
-#endif
- osst_get_frame_position(STp, aSRpnt);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: reported frame positions: host = %d, tape = %d\n",
- name, STp->first_frame_position, STp->last_frame_position);
-#endif
- switch (STp->write_type) {
- case OS_WRITE_DATA:
- case OS_WRITE_EOD:
- case OS_WRITE_NEW_MARK:
- printk(KERN_WARNING
- "%s:I: Relocating %d buffered logical frames from position %u to %u\n",
- name, STp->cur_frames, frame, (frame + skip > 3000 && frame < 3000)?3000:frame + skip);
- if (STp->os_fw_rev >= 10600)
- retval = osst_reposition_and_retry(STp, aSRpnt, frame, skip, pending);
- else
- retval = osst_read_back_buffer_and_rewrite(STp, aSRpnt, frame, skip, pending);
- printk(KERN_WARNING "%s:%s: %sWrite error%srecovered\n", name,
- retval?"E" :"I",
- retval?"" :"Don't worry, ",
- retval?" not ":" ");
- break;
- case OS_WRITE_LAST_MARK:
- printk(KERN_ERR "%s:E: Bad frame in update last marker, fatal\n", name);
- osst_set_frame_position(STp, aSRpnt, frame + STp->cur_frames + pending, 0);
- retval = -EIO;
- break;
- case OS_WRITE_HEADER:
- printk(KERN_WARNING "%s:I: Bad frame in header partition, skipped\n", name);
- retval = osst_read_back_buffer_and_rewrite(STp, aSRpnt, frame, 1, pending);
- break;
- default:
- printk(KERN_INFO "%s:I: Bad frame in filler, ignored\n", name);
- osst_set_frame_position(STp, aSRpnt, frame + STp->cur_frames + pending, 0);
- }
- osst_get_frame_position(STp, aSRpnt);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Positioning complete, cur_frames %d, pos %d, tape pos %d\n",
- name, STp->cur_frames, STp->first_frame_position, STp->last_frame_position);
- printk(OSST_DEB_MSG "%s:D: next logical frame to write: %d\n", name, STp->logical_blk_num);
-#endif
- if (retval == 0) {
- STp->recover_count++;
- STp->recover_erreg++;
- } else
- STp->abort_count++;
-
- STps->rw = rw_state;
- return retval;
-}
-
-static int osst_space_over_filemarks_backward(struct osst_tape * STp, struct osst_request ** aSRpnt,
- int mt_op, int mt_count)
-{
- char * name = tape_name(STp);
- int cnt;
- int last_mark_ppos = -1;
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reached space_over_filemarks_backwards %d %d\n", name, mt_op, mt_count);
-#endif
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks_bwd\n", name);
-#endif
- return -EIO;
- }
- if (STp->linux_media_version >= 4) {
- /*
- * direct lookup in header filemark list
- */
- cnt = ntohl(STp->buffer->aux->filemark_cnt);
- if (STp->header_ok &&
- STp->header_cache != NULL &&
- (cnt - mt_count) >= 0 &&
- (cnt - mt_count) < OS_FM_TAB_MAX &&
- (cnt - mt_count) < STp->filemark_cnt &&
- STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] == STp->buffer->aux->last_mark_ppos)
-
- last_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt - mt_count]);
-#if DEBUG
- if (STp->header_cache == NULL || (cnt - mt_count) < 0 || (cnt - mt_count) >= OS_FM_TAB_MAX)
- printk(OSST_DEB_MSG "%s:D: Filemark lookup fail due to %s\n", name,
- STp->header_cache == NULL?"lack of header cache":"count out of range");
- else
- printk(OSST_DEB_MSG "%s:D: Filemark lookup: prev mark %d (%s), skip %d to %d\n",
- name, cnt,
- ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
- (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] ==
- STp->buffer->aux->last_mark_ppos))?"match":"error",
- mt_count, last_mark_ppos);
-#endif
- if (last_mark_ppos > 10 && last_mark_ppos < STp->eod_frame_ppos) {
- osst_position_tape_and_confirm(STp, aSRpnt, last_mark_ppos);
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: Couldn't get logical blk num in space_filemarks\n", name);
-#endif
- return (-EIO);
- }
- if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
- printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
- name, last_mark_ppos);
- return (-EIO);
- }
- goto found;
- }
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reverting to scan filemark backwards\n", name);
-#endif
- }
- cnt = 0;
- while (cnt != mt_count) {
- last_mark_ppos = ntohl(STp->buffer->aux->last_mark_ppos);
- if (last_mark_ppos == -1)
- return (-EIO);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Positioning to last mark at %d\n", name, last_mark_ppos);
-#endif
- osst_position_tape_and_confirm(STp, aSRpnt, last_mark_ppos);
- cnt++;
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n", name);
-#endif
- return (-EIO);
- }
- if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
- printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
- name, last_mark_ppos);
- return (-EIO);
- }
- }
-found:
- if (mt_op == MTBSFM) {
- STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->buffer->buffer_bytes = 0;
- STp->buffer->read_pointer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
- }
- return 0;
-}
-
-/*
- * ADRL 1.1 compatible "slow" space filemarks fwd version
- *
- * Just scans for the filemark sequentially.
- */
-static int osst_space_over_filemarks_forward_slow(struct osst_tape * STp, struct osst_request ** aSRpnt,
- int mt_op, int mt_count)
-{
- int cnt = 0;
-#if DEBUG
- char * name = tape_name(STp);
-
- printk(OSST_DEB_MSG "%s:D: Reached space_over_filemarks_forward_slow %d %d\n", name, mt_op, mt_count);
-#endif
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks_fwd\n", name);
-#endif
- return (-EIO);
- }
- while (1) {
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n", name);
-#endif
- return (-EIO);
- }
- if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
- cnt++;
- if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: space_fwd: EOD reached\n", name);
-#endif
- if (STp->first_frame_position > STp->eod_frame_ppos+1) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: EOD position corrected (%d=>%d)\n",
- name, STp->eod_frame_ppos, STp->first_frame_position-1);
-#endif
- STp->eod_frame_ppos = STp->first_frame_position-1;
- }
- return (-EIO);
- }
- if (cnt == mt_count)
- break;
- STp->frame_in_buffer = 0;
- }
- if (mt_op == MTFSF) {
- STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->buffer->buffer_bytes = 0;
- STp->buffer->read_pointer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
- }
- return 0;
-}
-
-/*
- * Fast linux specific version of OnStream FSF
- */
-static int osst_space_over_filemarks_forward_fast(struct osst_tape * STp, struct osst_request ** aSRpnt,
- int mt_op, int mt_count)
-{
- char * name = tape_name(STp);
- int cnt = 0,
- next_mark_ppos = -1;
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reached space_over_filemarks_forward_fast %d %d\n", name, mt_op, mt_count);
-#endif
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks_fwd\n", name);
-#endif
- return (-EIO);
- }
-
- if (STp->linux_media_version >= 4) {
- /*
- * direct lookup in header filemark list
- */
- cnt = ntohl(STp->buffer->aux->filemark_cnt) - 1;
- if (STp->header_ok &&
- STp->header_cache != NULL &&
- (cnt + mt_count) < OS_FM_TAB_MAX &&
- (cnt + mt_count) < STp->filemark_cnt &&
- ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
- (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] == STp->buffer->aux->last_mark_ppos)))
-
- next_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt + mt_count]);
-#if DEBUG
- if (STp->header_cache == NULL || (cnt + mt_count) >= OS_FM_TAB_MAX)
- printk(OSST_DEB_MSG "%s:D: Filemark lookup fail due to %s\n", name,
- STp->header_cache == NULL?"lack of header cache":"count out of range");
- else
- printk(OSST_DEB_MSG "%s:D: Filemark lookup: prev mark %d (%s), skip %d to %d\n",
- name, cnt,
- ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
- (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] ==
- STp->buffer->aux->last_mark_ppos))?"match":"error",
- mt_count, next_mark_ppos);
-#endif
- if (next_mark_ppos <= 10 || next_mark_ppos > STp->eod_frame_ppos) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reverting to slow filemark space\n", name);
-#endif
- return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count);
- } else {
- osst_position_tape_and_confirm(STp, aSRpnt, next_mark_ppos);
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n",
- name);
-#endif
- return (-EIO);
- }
- if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
- printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
- name, next_mark_ppos);
- return (-EIO);
- }
- if (ntohl(STp->buffer->aux->filemark_cnt) != cnt + mt_count) {
- printk(KERN_WARNING "%s:W: Expected to find marker %d at ppos %d, not %d\n",
- name, cnt+mt_count, next_mark_ppos,
- ntohl(STp->buffer->aux->filemark_cnt));
- return (-EIO);
- }
- }
- } else {
- /*
- * Find nearest (usually previous) marker, then jump from marker to marker
- */
- while (1) {
- if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
- break;
- if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: space_fwd: EOD reached\n", name);
-#endif
- return (-EIO);
- }
- if (ntohl(STp->buffer->aux->filemark_cnt) == 0) {
- if (STp->first_mark_ppos == -1) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reverting to slow filemark space\n", name);
-#endif
- return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count);
- }
- osst_position_tape_and_confirm(STp, aSRpnt, STp->first_mark_ppos);
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: Couldn't get logical blk num in space_filemarks_fwd_fast\n",
- name);
-#endif
- return (-EIO);
- }
- if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
- printk(KERN_WARNING "%s:W: Expected to find filemark at %d\n",
- name, STp->first_mark_ppos);
- return (-EIO);
- }
- } else {
- if (osst_space_over_filemarks_backward(STp, aSRpnt, MTBSF, 1) < 0)
- return (-EIO);
- mt_count++;
- }
- }
- cnt++;
- while (cnt != mt_count) {
- next_mark_ppos = ntohl(STp->buffer->aux->next_mark_ppos);
- if (!next_mark_ppos || next_mark_ppos > STp->eod_frame_ppos) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reverting to slow filemark space\n", name);
-#endif
- return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count - cnt);
- }
-#if DEBUG
- else printk(OSST_DEB_MSG "%s:D: Positioning to next mark at %d\n", name, next_mark_ppos);
-#endif
- osst_position_tape_and_confirm(STp, aSRpnt, next_mark_ppos);
- cnt++;
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n",
- name);
-#endif
- return (-EIO);
- }
- if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
- printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
- name, next_mark_ppos);
- return (-EIO);
- }
- }
- }
- if (mt_op == MTFSF) {
- STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->buffer->buffer_bytes = 0;
- STp->buffer->read_pointer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
- }
- return 0;
-}
-
-/*
- * In debug mode, we want to see as many errors as possible
- * to test the error recovery mechanism.
- */
-#if DEBUG
-static void osst_set_retries(struct osst_tape * STp, struct osst_request ** aSRpnt, int retries)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt = * aSRpnt;
- char * name = tape_name(STp);
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = NUMBER_RETRIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- (STp->buffer)->b_data[0] = cmd[4] - 1;
- (STp->buffer)->b_data[1] = 0; /* Medium Type - ignoring */
- (STp->buffer)->b_data[2] = 0; /* Reserved */
- (STp->buffer)->b_data[3] = 0; /* Block Descriptor Length */
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = NUMBER_RETRIES_PAGE | (1 << 7);
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 2;
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 4;
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = retries;
-
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Setting number of retries on OnStream tape to %d\n", name, retries);
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
- *aSRpnt = SRpnt;
-
- if ((STp->buffer)->syscall_result)
- printk (KERN_ERR "%s:D: Couldn't set retries to %d\n", name, retries);
-}
-#endif
-
-
-static int osst_write_filemark(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- int result;
- int this_mark_ppos = STp->first_frame_position;
- int this_mark_lbn = STp->logical_blk_num;
-#if DEBUG
- char * name = tape_name(STp);
-#endif
-
- if (STp->raw) return 0;
-
- STp->write_type = OS_WRITE_NEW_MARK;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Writing Filemark %i at fppos %d (fseq %d, lblk %d)\n",
- name, STp->filemark_cnt, this_mark_ppos, STp->frame_seq_number, this_mark_lbn);
-#endif
- STp->dirty = 1;
- result = osst_flush_write_buffer(STp, aSRpnt);
- result |= osst_flush_drive_buffer(STp, aSRpnt);
- STp->last_mark_ppos = this_mark_ppos;
- STp->last_mark_lbn = this_mark_lbn;
- if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX)
- STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos);
- if (STp->filemark_cnt++ == 0)
- STp->first_mark_ppos = this_mark_ppos;
- return result;
-}
-
-static int osst_write_eod(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- int result;
-#if DEBUG
- char * name = tape_name(STp);
-#endif
-
- if (STp->raw) return 0;
-
- STp->write_type = OS_WRITE_EOD;
- STp->eod_frame_ppos = STp->first_frame_position;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Writing EOD at fppos %d (fseq %d, lblk %d)\n", name,
- STp->eod_frame_ppos, STp->frame_seq_number, STp->logical_blk_num);
-#endif
- STp->dirty = 1;
-
- result = osst_flush_write_buffer(STp, aSRpnt);
- result |= osst_flush_drive_buffer(STp, aSRpnt);
- STp->eod_frame_lfa = --(STp->frame_seq_number);
- return result;
-}
-
-static int osst_write_filler(struct osst_tape * STp, struct osst_request ** aSRpnt, int where, int count)
-{
- char * name = tape_name(STp);
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reached onstream write filler group %d\n", name, where);
-#endif
- osst_wait_ready(STp, aSRpnt, 60 * 5, 0);
- osst_set_frame_position(STp, aSRpnt, where, 0);
- STp->write_type = OS_WRITE_FILLER;
- while (count--) {
- memcpy(STp->buffer->b_data, "Filler", 6);
- STp->buffer->buffer_bytes = 6;
- STp->dirty = 1;
- if (osst_flush_write_buffer(STp, aSRpnt)) {
- printk(KERN_INFO "%s:I: Couldn't write filler frame\n", name);
- return (-EIO);
- }
- }
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Exiting onstream write filler group\n", name);
-#endif
- return osst_flush_drive_buffer(STp, aSRpnt);
-}
-
-static int __osst_write_header(struct osst_tape * STp, struct osst_request ** aSRpnt, int where, int count)
-{
- char * name = tape_name(STp);
- int result;
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reached onstream write header group %d\n", name, where);
-#endif
- osst_wait_ready(STp, aSRpnt, 60 * 5, 0);
- osst_set_frame_position(STp, aSRpnt, where, 0);
- STp->write_type = OS_WRITE_HEADER;
- while (count--) {
- osst_copy_to_buffer(STp->buffer, (unsigned char *)STp->header_cache);
- STp->buffer->buffer_bytes = sizeof(os_header_t);
- STp->dirty = 1;
- if (osst_flush_write_buffer(STp, aSRpnt)) {
- printk(KERN_INFO "%s:I: Couldn't write header frame\n", name);
- return (-EIO);
- }
- }
- result = osst_flush_drive_buffer(STp, aSRpnt);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Write onstream header group %s\n", name, result?"failed":"done");
-#endif
- return result;
-}
-
-static int osst_write_header(struct osst_tape * STp, struct osst_request ** aSRpnt, int locate_eod)
-{
- os_header_t * header;
- int result;
- char * name = tape_name(STp);
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Writing tape header\n", name);
-#endif
- if (STp->raw) return 0;
-
- if (STp->header_cache == NULL) {
- if ((STp->header_cache = vmalloc(sizeof(os_header_t))) == NULL) {
- printk(KERN_ERR "%s:E: Failed to allocate header cache\n", name);
- return (-ENOMEM);
- }
- memset(STp->header_cache, 0, sizeof(os_header_t));
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Allocated and cleared memory for header cache\n", name);
-#endif
- }
- if (STp->header_ok) STp->update_frame_cntr++;
- else STp->update_frame_cntr = 0;
-
- header = STp->header_cache;
- strcpy(header->ident_str, "ADR_SEQ");
- header->major_rev = 1;
- header->minor_rev = 4;
- header->ext_trk_tb_off = htons(17192);
- header->pt_par_num = 1;
- header->partition[0].partition_num = OS_DATA_PARTITION;
- header->partition[0].par_desc_ver = OS_PARTITION_VERSION;
- header->partition[0].wrt_pass_cntr = htons(STp->wrt_pass_cntr);
- header->partition[0].first_frame_ppos = htonl(STp->first_data_ppos);
- header->partition[0].last_frame_ppos = htonl(STp->capacity);
- header->partition[0].eod_frame_ppos = htonl(STp->eod_frame_ppos);
- header->cfg_col_width = htonl(20);
- header->dat_col_width = htonl(1500);
- header->qfa_col_width = htonl(0);
- header->ext_track_tb.nr_stream_part = 1;
- header->ext_track_tb.et_ent_sz = 32;
- header->ext_track_tb.dat_ext_trk_ey.et_part_num = 0;
- header->ext_track_tb.dat_ext_trk_ey.fmt = 1;
- header->ext_track_tb.dat_ext_trk_ey.fm_tab_off = htons(17736);
- header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi = 0;
- header->ext_track_tb.dat_ext_trk_ey.last_hlb = htonl(STp->eod_frame_lfa);
- header->ext_track_tb.dat_ext_trk_ey.last_pp = htonl(STp->eod_frame_ppos);
- header->dat_fm_tab.fm_part_num = 0;
- header->dat_fm_tab.fm_tab_ent_sz = 4;
- header->dat_fm_tab.fm_tab_ent_cnt = htons(STp->filemark_cnt<OS_FM_TAB_MAX?
- STp->filemark_cnt:OS_FM_TAB_MAX);
-
- result = __osst_write_header(STp, aSRpnt, 0xbae, 5);
- if (STp->update_frame_cntr == 0)
- osst_write_filler(STp, aSRpnt, 0xbb3, 5);
- result &= __osst_write_header(STp, aSRpnt, 5, 5);
-
- if (locate_eod) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Locating back to eod frame addr %d\n", name, STp->eod_frame_ppos);
-#endif
- osst_set_frame_position(STp, aSRpnt, STp->eod_frame_ppos, 0);
- }
- if (result)
- printk(KERN_ERR "%s:E: Write header failed\n", name);
- else {
- memcpy(STp->application_sig, "LIN4", 4);
- STp->linux_media = 1;
- STp->linux_media_version = 4;
- STp->header_ok = 1;
- }
- return result;
-}
-
-static int osst_reset_header(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- if (STp->header_cache != NULL)
- memset(STp->header_cache, 0, sizeof(os_header_t));
-
- STp->logical_blk_num = STp->frame_seq_number = 0;
- STp->frame_in_buffer = 0;
- STp->eod_frame_ppos = STp->first_data_ppos = 0x0000000A;
- STp->filemark_cnt = 0;
- STp->first_mark_ppos = STp->last_mark_ppos = STp->last_mark_lbn = -1;
- return osst_write_header(STp, aSRpnt, 1);
-}
-
-static int __osst_analyze_headers(struct osst_tape * STp, struct osst_request ** aSRpnt, int ppos)
-{
- char * name = tape_name(STp);
- os_header_t * header;
- os_aux_t * aux;
- char id_string[8];
- int linux_media_version,
- update_frame_cntr;
-
- if (STp->raw)
- return 1;
-
- if (ppos == 5 || ppos == 0xbae || STp->buffer->syscall_result) {
- if (osst_set_frame_position(STp, aSRpnt, ppos, 0))
- printk(KERN_WARNING "%s:W: Couldn't position tape\n", name);
- osst_wait_ready(STp, aSRpnt, 60 * 15, 0);
- if (osst_initiate_read (STp, aSRpnt)) {
- printk(KERN_WARNING "%s:W: Couldn't initiate read\n", name);
- return 0;
- }
- }
- if (osst_read_frame(STp, aSRpnt, 180)) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't read header frame\n", name);
-#endif
- return 0;
- }
- header = (os_header_t *) STp->buffer->b_data; /* warning: only first segment addressable */
- aux = STp->buffer->aux;
- if (aux->frame_type != OS_FRAME_TYPE_HEADER) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping non-header frame (%d)\n", name, ppos);
-#endif
- return 0;
- }
- if (ntohl(aux->frame_seq_num) != 0 ||
- ntohl(aux->logical_blk_num) != 0 ||
- aux->partition.partition_num != OS_CONFIG_PARTITION ||
- ntohl(aux->partition.first_frame_ppos) != 0 ||
- ntohl(aux->partition.last_frame_ppos) != 0xbb7 ) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Invalid header frame (%d,%d,%d,%d,%d)\n", name,
- ntohl(aux->frame_seq_num), ntohl(aux->logical_blk_num),
- aux->partition.partition_num, ntohl(aux->partition.first_frame_ppos),
- ntohl(aux->partition.last_frame_ppos));
-#endif
- return 0;
- }
- if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0 &&
- strncmp(header->ident_str, "ADR-SEQ", 7) != 0) {
- strlcpy(id_string, header->ident_str, 8);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Invalid header identification string %s\n", name, id_string);
-#endif
- return 0;
- }
- update_frame_cntr = ntohl(aux->update_frame_cntr);
- if (update_frame_cntr < STp->update_frame_cntr) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame %d with update_frame_counter %d<%d\n",
- name, ppos, update_frame_cntr, STp->update_frame_cntr);
-#endif
- return 0;
- }
- if (header->major_rev != 1 || header->minor_rev != 4 ) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: %s revision %d.%d detected (1.4 supported)\n",
- name, (header->major_rev != 1 || header->minor_rev < 2 ||
- header->minor_rev > 4 )? "Invalid" : "Warning:",
- header->major_rev, header->minor_rev);
-#endif
- if (header->major_rev != 1 || header->minor_rev < 2 || header->minor_rev > 4)
- return 0;
- }
-#if DEBUG
- if (header->pt_par_num != 1)
- printk(KERN_INFO "%s:W: %d partitions defined, only one supported\n",
- name, header->pt_par_num);
-#endif
- memcpy(id_string, aux->application_sig, 4);
- id_string[4] = 0;
- if (memcmp(id_string, "LIN", 3) == 0) {
- STp->linux_media = 1;
- linux_media_version = id_string[3] - '0';
- if (linux_media_version != 4)
- printk(KERN_INFO "%s:I: Linux media version %d detected (current 4)\n",
- name, linux_media_version);
- } else {
- printk(KERN_WARNING "%s:W: Non Linux media detected (%s)\n", name, id_string);
- return 0;
- }
- if (linux_media_version < STp->linux_media_version) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping frame %d with linux_media_version %d\n",
- name, ppos, linux_media_version);
-#endif
- return 0;
- }
- if (linux_media_version > STp->linux_media_version) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Frame %d sets linux_media_version to %d\n",
- name, ppos, linux_media_version);
-#endif
- memcpy(STp->application_sig, id_string, 5);
- STp->linux_media_version = linux_media_version;
- STp->update_frame_cntr = -1;
- }
- if (update_frame_cntr > STp->update_frame_cntr) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Frame %d sets update_frame_counter to %d\n",
- name, ppos, update_frame_cntr);
-#endif
- if (STp->header_cache == NULL) {
- if ((STp->header_cache = vmalloc(sizeof(os_header_t))) == NULL) {
- printk(KERN_ERR "%s:E: Failed to allocate header cache\n", name);
- return 0;
- }
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Allocated memory for header cache\n", name);
-#endif
- }
- osst_copy_from_buffer(STp->buffer, (unsigned char *)STp->header_cache);
- header = STp->header_cache; /* further accesses from cached (full) copy */
-
- STp->wrt_pass_cntr = ntohs(header->partition[0].wrt_pass_cntr);
- STp->first_data_ppos = ntohl(header->partition[0].first_frame_ppos);
- STp->eod_frame_ppos = ntohl(header->partition[0].eod_frame_ppos);
- STp->eod_frame_lfa = ntohl(header->ext_track_tb.dat_ext_trk_ey.last_hlb);
- STp->filemark_cnt = ntohl(aux->filemark_cnt);
- STp->first_mark_ppos = ntohl(aux->next_mark_ppos);
- STp->last_mark_ppos = ntohl(aux->last_mark_ppos);
- STp->last_mark_lbn = ntohl(aux->last_mark_lbn);
- STp->update_frame_cntr = update_frame_cntr;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Detected write pass %d, update frame counter %d, filemark counter %d\n",
- name, STp->wrt_pass_cntr, STp->update_frame_cntr, STp->filemark_cnt);
- printk(OSST_DEB_MSG "%s:D: first data frame on tape = %d, last = %d, eod frame = %d\n", name,
- STp->first_data_ppos,
- ntohl(header->partition[0].last_frame_ppos),
- ntohl(header->partition[0].eod_frame_ppos));
- printk(OSST_DEB_MSG "%s:D: first mark on tape = %d, last = %d, eod frame = %d\n",
- name, STp->first_mark_ppos, STp->last_mark_ppos, STp->eod_frame_ppos);
-#endif
- if (header->minor_rev < 4 && STp->linux_media_version == 4) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Moving filemark list to ADR 1.4 location\n", name);
-#endif
- memcpy((void *)header->dat_fm_tab.fm_tab_ent,
- (void *)header->old_filemark_list, sizeof(header->dat_fm_tab.fm_tab_ent));
- memset((void *)header->old_filemark_list, 0, sizeof(header->old_filemark_list));
- }
- if (header->minor_rev == 4 &&
- (header->ext_trk_tb_off != htons(17192) ||
- header->partition[0].partition_num != OS_DATA_PARTITION ||
- header->partition[0].par_desc_ver != OS_PARTITION_VERSION ||
- header->partition[0].last_frame_ppos != htonl(STp->capacity) ||
- header->cfg_col_width != htonl(20) ||
- header->dat_col_width != htonl(1500) ||
- header->qfa_col_width != htonl(0) ||
- header->ext_track_tb.nr_stream_part != 1 ||
- header->ext_track_tb.et_ent_sz != 32 ||
- header->ext_track_tb.dat_ext_trk_ey.et_part_num != OS_DATA_PARTITION ||
- header->ext_track_tb.dat_ext_trk_ey.fmt != 1 ||
- header->ext_track_tb.dat_ext_trk_ey.fm_tab_off != htons(17736) ||
- header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi != 0 ||
- header->ext_track_tb.dat_ext_trk_ey.last_pp != htonl(STp->eod_frame_ppos) ||
- header->dat_fm_tab.fm_part_num != OS_DATA_PARTITION ||
- header->dat_fm_tab.fm_tab_ent_sz != 4 ||
- header->dat_fm_tab.fm_tab_ent_cnt !=
- htons(STp->filemark_cnt<OS_FM_TAB_MAX?STp->filemark_cnt:OS_FM_TAB_MAX)))
- printk(KERN_WARNING "%s:W: Failed consistency check ADR 1.4 format\n", name);
-
- }
-
- return 1;
-}
-
-static int osst_analyze_headers(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- int position, ppos;
- int first, last;
- int valid = 0;
- char * name = tape_name(STp);
-
- position = osst_get_frame_position(STp, aSRpnt);
-
- if (STp->raw) {
- STp->header_ok = STp->linux_media = 1;
- STp->linux_media_version = 0;
- return 1;
- }
- STp->header_ok = STp->linux_media = STp->linux_media_version = 0;
- STp->wrt_pass_cntr = STp->update_frame_cntr = -1;
- STp->eod_frame_ppos = STp->first_data_ppos = -1;
- STp->first_mark_ppos = STp->last_mark_ppos = STp->last_mark_lbn = -1;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reading header\n", name);
-#endif
-
- /* optimization for speed - if we are positioned at ppos 10, read second group first */
- /* TODO try the ADR 1.1 locations for the second group if we have no valid one yet... */
-
- first = position==10?0xbae: 5;
- last = position==10?0xbb3:10;
-
- for (ppos = first; ppos < last; ppos++)
- if (__osst_analyze_headers(STp, aSRpnt, ppos))
- valid = 1;
-
- first = position==10? 5:0xbae;
- last = position==10?10:0xbb3;
-
- for (ppos = first; ppos < last; ppos++)
- if (__osst_analyze_headers(STp, aSRpnt, ppos))
- valid = 1;
-
- if (!valid) {
- printk(KERN_ERR "%s:E: Failed to find valid ADRL header, new media?\n", name);
- STp->eod_frame_ppos = STp->first_data_ppos = 0;
- osst_set_frame_position(STp, aSRpnt, 10, 0);
- return 0;
- }
- if (position <= STp->first_data_ppos) {
- position = STp->first_data_ppos;
- STp->ps[0].drv_file = STp->ps[0].drv_block = STp->frame_seq_number = STp->logical_blk_num = 0;
- }
- osst_set_frame_position(STp, aSRpnt, position, 0);
- STp->header_ok = 1;
-
- return 1;
-}
-
-static int osst_verify_position(struct osst_tape * STp, struct osst_request ** aSRpnt)
-{
- int frame_position = STp->first_frame_position;
- int frame_seq_numbr = STp->frame_seq_number;
- int logical_blk_num = STp->logical_blk_num;
- int halfway_frame = STp->frame_in_buffer;
- int read_pointer = STp->buffer->read_pointer;
- int prev_mark_ppos = -1;
- int actual_mark_ppos, i, n;
-#if DEBUG
- char * name = tape_name(STp);
-
- printk(OSST_DEB_MSG "%s:D: Verify that the tape is really the one we think before writing\n", name);
-#endif
- osst_set_frame_position(STp, aSRpnt, frame_position - 1, 0);
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in verify_position\n", name);
-#endif
- return (-EIO);
- }
- if (STp->linux_media_version >= 4) {
- for (i=0; i<STp->filemark_cnt; i++)
- if ((n=ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i])) < frame_position)
- prev_mark_ppos = n;
- } else
- prev_mark_ppos = frame_position - 1; /* usually - we don't really know */
- actual_mark_ppos = STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER ?
- frame_position - 1 : ntohl(STp->buffer->aux->last_mark_ppos);
- if (frame_position != STp->first_frame_position ||
- frame_seq_numbr != STp->frame_seq_number + (halfway_frame?0:1) ||
- prev_mark_ppos != actual_mark_ppos ) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Block mismatch: fppos %d-%d, fseq %d-%d, mark %d-%d\n", name,
- STp->first_frame_position, frame_position,
- STp->frame_seq_number + (halfway_frame?0:1),
- frame_seq_numbr, actual_mark_ppos, prev_mark_ppos);
-#endif
- return (-EIO);
- }
- if (halfway_frame) {
- /* prepare buffer for append and rewrite on top of original */
- osst_set_frame_position(STp, aSRpnt, frame_position - 1, 0);
- STp->buffer->buffer_bytes = read_pointer;
- STp->ps[STp->partition].rw = ST_WRITING;
- STp->dirty = 1;
- }
- STp->frame_in_buffer = halfway_frame;
- STp->frame_seq_number = frame_seq_numbr;
- STp->logical_blk_num = logical_blk_num;
- return 0;
-}
-
-/* Acc. to OnStream, the vers. numbering is the following:
- * X.XX for released versions (X=digit),
- * XXXY for unreleased versions (Y=letter)
- * Ordering 1.05 < 106A < 106B < ... < 106a < ... < 1.06
- * This fn makes monoton numbers out of this scheme ...
- */
-static unsigned int osst_parse_firmware_rev (const char * str)
-{
- if (str[1] == '.') {
- return (str[0]-'0')*10000
- +(str[2]-'0')*1000
- +(str[3]-'0')*100;
- } else {
- return (str[0]-'0')*10000
- +(str[1]-'0')*1000
- +(str[2]-'0')*100 - 100
- +(str[3]-'@');
- }
-}
-
-/*
- * Configure the OnStream SCII tape drive for default operation
- */
-static int osst_configure_onstream(struct osst_tape *STp, struct osst_request ** aSRpnt)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- char * name = tape_name(STp);
- struct osst_request * SRpnt = * aSRpnt;
- osst_mode_parameter_header_t * header;
- osst_block_size_page_t * bs;
- osst_capabilities_page_t * cp;
- osst_tape_paramtr_page_t * prm;
- int drive_buffer_size;
-
- if (STp->ready != ST_READY) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Not Ready\n", name);
-#endif
- return (-EIO);
- }
-
- if (STp->os_fw_rev < 10600) {
- printk(KERN_INFO "%s:I: Old OnStream firmware revision detected (%s),\n", name, STp->device->rev);
- printk(KERN_INFO "%s:I: an upgrade to version 1.06 or above is recommended\n", name);
- }
-
- /*
- * Configure 32.5KB (data+aux) frame size.
- * Get the current frame size from the block size mode page
- */
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8;
- cmd[2] = BLOCK_SIZE_PAGE;
- cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
- if (SRpnt == NULL) {
-#if DEBUG
- printk(OSST_DEB_MSG "osst :D: Busy\n");
-#endif
- return (-EBUSY);
- }
- *aSRpnt = SRpnt;
- if ((STp->buffer)->syscall_result != 0) {
- printk (KERN_ERR "%s:E: Can't get tape block size mode page\n", name);
- return (-EIO);
- }
-
- header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
- bs = (osst_block_size_page_t *) ((STp->buffer)->b_data + sizeof(osst_mode_parameter_header_t) + header->bdl);
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: 32KB play back: %s\n", name, bs->play32 ? "Yes" : "No");
- printk(OSST_DEB_MSG "%s:D: 32.5KB play back: %s\n", name, bs->play32_5 ? "Yes" : "No");
- printk(OSST_DEB_MSG "%s:D: 32KB record: %s\n", name, bs->record32 ? "Yes" : "No");
- printk(OSST_DEB_MSG "%s:D: 32.5KB record: %s\n", name, bs->record32_5 ? "Yes" : "No");
-#endif
-
- /*
- * Configure default auto columns mode, 32.5KB transfer mode
- */
- bs->one = 1;
- bs->play32 = 0;
- bs->play32_5 = 1;
- bs->record32 = 0;
- bs->record32_5 = 1;
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
- *aSRpnt = SRpnt;
- if ((STp->buffer)->syscall_result != 0) {
- printk (KERN_ERR "%s:E: Couldn't set tape block size mode page\n", name);
- return (-EIO);
- }
-
-#if DEBUG
- printk(KERN_INFO "%s:D: Drive Block Size changed to 32.5K\n", name);
- /*
- * In debug mode, we want to see as many errors as possible
- * to test the error recovery mechanism.
- */
- osst_set_retries(STp, aSRpnt, 0);
- SRpnt = * aSRpnt;
-#endif
-
- /*
- * Set vendor name to 'LIN4' for "Linux support version 4".
- */
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- header->mode_data_length = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH - 1;
- header->medium_type = 0; /* Medium Type - ignoring */
- header->dsp = 0; /* Reserved */
- header->bdl = 0; /* Block Descriptor Length */
-
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = VENDOR_IDENT_PAGE | (1 << 7);
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 6;
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 'L';
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 'I';
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 4] = 'N';
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 5] = '4';
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 6] = 0;
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 7] = 0;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
- *aSRpnt = SRpnt;
-
- if ((STp->buffer)->syscall_result != 0) {
- printk (KERN_ERR "%s:E: Couldn't set vendor name to %s\n", name,
- (char *) ((STp->buffer)->b_data + MODE_HEADER_LENGTH + 2));
- return (-EIO);
- }
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8;
- cmd[2] = CAPABILITIES_PAGE;
- cmd[4] = CAPABILITIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
- *aSRpnt = SRpnt;
-
- if ((STp->buffer)->syscall_result != 0) {
- printk (KERN_ERR "%s:E: Can't get capabilities page\n", name);
- return (-EIO);
- }
-
- header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
- cp = (osst_capabilities_page_t *) ((STp->buffer)->b_data +
- sizeof(osst_mode_parameter_header_t) + header->bdl);
-
- drive_buffer_size = ntohs(cp->buffer_size) / 2;
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8;
- cmd[2] = TAPE_PARAMTR_PAGE;
- cmd[4] = TAPE_PARAMTR_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
- *aSRpnt = SRpnt;
-
- if ((STp->buffer)->syscall_result != 0) {
- printk (KERN_ERR "%s:E: Can't get tape parameter page\n", name);
- return (-EIO);
- }
-
- header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
- prm = (osst_tape_paramtr_page_t *) ((STp->buffer)->b_data +
- sizeof(osst_mode_parameter_header_t) + header->bdl);
-
- STp->density = prm->density;
- STp->capacity = ntohs(prm->segtrk) * ntohs(prm->trks);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Density %d, tape length: %dMB, drive buffer size: %dKB\n",
- name, STp->density, STp->capacity / 32, drive_buffer_size);
-#endif
-
- return 0;
-
-}
-
-
-/* Step over EOF if it has been inadvertently crossed (ioctl not used because
- it messes up the block number). */
-static int cross_eof(struct osst_tape *STp, struct osst_request ** aSRpnt, int forward)
-{
- int result;
- char * name = tape_name(STp);
-
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Stepping over filemark %s.\n",
- name, forward ? "forward" : "backward");
-#endif
-
- if (forward) {
- /* assumes that the filemark is already read by the drive, so this is low cost */
- result = osst_space_over_filemarks_forward_slow(STp, aSRpnt, MTFSF, 1);
- }
- else
- /* assumes this is only called if we just read the filemark! */
- result = osst_seek_logical_blk(STp, aSRpnt, STp->logical_blk_num - 1);
-
- if (result < 0)
- printk(KERN_WARNING "%s:W: Stepping over filemark %s failed.\n",
- name, forward ? "forward" : "backward");
-
- return result;
-}
-
-
-/* Get the tape position. */
-
-static int osst_get_frame_position(struct osst_tape *STp, struct osst_request ** aSRpnt)
-{
- unsigned char scmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- int result = 0;
- char * name = tape_name(STp);
-
- /* KG: We want to be able to use it for checking Write Buffer availability
- * and thus don't want to risk to overwrite anything. Exchange buffers ... */
- char mybuf[24];
- char * olddata = STp->buffer->b_data;
- int oldsize = STp->buffer->buffer_size;
-
- if (STp->ready != ST_READY) return (-EIO);
-
- memset (scmd, 0, MAX_COMMAND_SIZE);
- scmd[0] = READ_POSITION;
-
- STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
- SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 20, DMA_FROM_DEVICE,
- STp->timeout, MAX_RETRIES, 1);
- if (!SRpnt) {
- STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
- return (-EBUSY);
- }
- *aSRpnt = SRpnt;
-
- if (STp->buffer->syscall_result)
- result = ((SRpnt->sense[2] & 0x0f) == 3) ? -EIO : -EINVAL; /* 3: Write Error */
-
- if (result == -EINVAL)
- printk(KERN_ERR "%s:E: Can't read tape position.\n", name);
- else {
- if (result == -EIO) { /* re-read position - this needs to preserve media errors */
- unsigned char mysense[16];
- memcpy (mysense, SRpnt->sense, 16);
- memset (scmd, 0, MAX_COMMAND_SIZE);
- scmd[0] = READ_POSITION;
- STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
- SRpnt = osst_do_scsi(SRpnt, STp, scmd, 20, DMA_FROM_DEVICE,
- STp->timeout, MAX_RETRIES, 1);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reread position, reason=[%02x:%02x:%02x], result=[%s%02x:%02x:%02x]\n",
- name, mysense[2], mysense[12], mysense[13], STp->buffer->syscall_result?"":"ok:",
- SRpnt->sense[2],SRpnt->sense[12],SRpnt->sense[13]);
-#endif
- if (!STp->buffer->syscall_result)
- memcpy (SRpnt->sense, mysense, 16);
- else
- printk(KERN_WARNING "%s:W: Double error in get position\n", name);
- }
- STp->first_frame_position = ((STp->buffer)->b_data[4] << 24)
- + ((STp->buffer)->b_data[5] << 16)
- + ((STp->buffer)->b_data[6] << 8)
- + (STp->buffer)->b_data[7];
- STp->last_frame_position = ((STp->buffer)->b_data[ 8] << 24)
- + ((STp->buffer)->b_data[ 9] << 16)
- + ((STp->buffer)->b_data[10] << 8)
- + (STp->buffer)->b_data[11];
- STp->cur_frames = (STp->buffer)->b_data[15];
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG "%s:D: Drive Positions: host %d, tape %d%s, buffer %d\n", name,
- STp->first_frame_position, STp->last_frame_position,
- ((STp->buffer)->b_data[0]&0x80)?" (BOP)":
- ((STp->buffer)->b_data[0]&0x40)?" (EOP)":"",
- STp->cur_frames);
- }
-#endif
- if (STp->cur_frames == 0 && STp->first_frame_position != STp->last_frame_position) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Correcting read position %d, %d, %d\n", name,
- STp->first_frame_position, STp->last_frame_position, STp->cur_frames);
-#endif
- STp->first_frame_position = STp->last_frame_position;
- }
- }
- STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
-
- return (result == 0 ? STp->first_frame_position : result);
-}
-
-
-/* Set the tape block */
-static int osst_set_frame_position(struct osst_tape *STp, struct osst_request ** aSRpnt, int ppos, int skip)
-{
- unsigned char scmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- struct st_partstat * STps;
- int result = 0;
- int pp = (ppos == 3000 && !skip)? 0 : ppos;
- char * name = tape_name(STp);
-
- if (STp->ready != ST_READY) return (-EIO);
-
- STps = &(STp->ps[STp->partition]);
-
- if (ppos < 0 || ppos > STp->capacity) {
- printk(KERN_WARNING "%s:W: Reposition request %d out of range\n", name, ppos);
- pp = ppos = ppos < 0 ? 0 : (STp->capacity - 1);
- result = (-EINVAL);
- }
-
- do {
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Setting ppos to %d.\n", name, pp);
-#endif
- memset (scmd, 0, MAX_COMMAND_SIZE);
- scmd[0] = SEEK_10;
- scmd[1] = 1;
- scmd[3] = (pp >> 24);
- scmd[4] = (pp >> 16);
- scmd[5] = (pp >> 8);
- scmd[6] = pp;
- if (skip)
- scmd[9] = 0x80;
-
- SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 0, DMA_NONE, STp->long_timeout,
- MAX_RETRIES, 1);
- if (!SRpnt)
- return (-EBUSY);
- *aSRpnt = SRpnt;
-
- if ((STp->buffer)->syscall_result != 0) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: SEEK command from %d to %d failed.\n",
- name, STp->first_frame_position, pp);
-#endif
- result = (-EIO);
- }
- if (pp != ppos)
- osst_wait_ready(STp, aSRpnt, 5 * 60, OSST_WAIT_POSITION_COMPLETE);
- } while ((pp != ppos) && (pp = ppos));
- STp->first_frame_position = STp->last_frame_position = ppos;
- STps->eof = ST_NOEOF;
- STps->at_sm = 0;
- STps->rw = ST_IDLE;
- STp->frame_in_buffer = 0;
- return result;
-}
-
-static int osst_write_trailer(struct osst_tape *STp, struct osst_request ** aSRpnt, int leave_at_EOT)
-{
- struct st_partstat * STps = &(STp->ps[STp->partition]);
- int result = 0;
-
- if (STp->write_type != OS_WRITE_NEW_MARK) {
- /* true unless the user wrote the filemark for us */
- result = osst_flush_drive_buffer(STp, aSRpnt);
- if (result < 0) goto out;
- result = osst_write_filemark(STp, aSRpnt);
- if (result < 0) goto out;
-
- if (STps->drv_file >= 0)
- STps->drv_file++ ;
- STps->drv_block = 0;
- }
- result = osst_write_eod(STp, aSRpnt);
- osst_write_header(STp, aSRpnt, leave_at_EOT);
-
- STps->eof = ST_FM;
-out:
- return result;
-}
-
-/* osst versions of st functions - augmented and stripped to suit OnStream only */
-
-/* Flush the write buffer (never need to write if variable blocksize). */
-static int osst_flush_write_buffer(struct osst_tape *STp, struct osst_request ** aSRpnt)
-{
- int offset, transfer, blks = 0;
- int result = 0;
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt = *aSRpnt;
- struct st_partstat * STps;
- char * name = tape_name(STp);
-
- if ((STp->buffer)->writing) {
- if (SRpnt == (STp->buffer)->last_SRpnt)
-#if DEBUG
- { printk(OSST_DEB_MSG
- "%s:D: aSRpnt points to osst_request that write_behind_check will release -- cleared\n", name);
-#endif
- *aSRpnt = SRpnt = NULL;
-#if DEBUG
- } else if (SRpnt)
- printk(OSST_DEB_MSG
- "%s:D: aSRpnt does not point to osst_request that write_behind_check will release -- strange\n", name);
-#endif
- osst_write_behind_check(STp);
- if ((STp->buffer)->syscall_result) {
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Async write error (flush) %x.\n",
- name, (STp->buffer)->midlevel_result);
-#endif
- if ((STp->buffer)->midlevel_result == INT_MAX)
- return (-ENOSPC);
- return (-EIO);
- }
- }
-
- result = 0;
- if (STp->dirty == 1) {
-
- STp->write_count++;
- STps = &(STp->ps[STp->partition]);
- STps->rw = ST_WRITING;
- offset = STp->buffer->buffer_bytes;
- blks = (offset + STp->block_size - 1) / STp->block_size;
- transfer = OS_FRAME_SIZE;
-
- if (offset < OS_DATA_SIZE)
- osst_zero_buffer_tail(STp->buffer);
-
- if (STp->poll)
- if (osst_wait_frame (STp, aSRpnt, STp->first_frame_position, -50, 120))
- result = osst_recover_wait_frame(STp, aSRpnt, 1);
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = WRITE_6;
- cmd[1] = 1;
- cmd[4] = 1;
-
- switch (STp->write_type) {
- case OS_WRITE_DATA:
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Writing %d blocks to frame %d, lblks %d-%d\n",
- name, blks, STp->frame_seq_number,
- STp->logical_blk_num - blks, STp->logical_blk_num - 1);
-#endif
- osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->frame_seq_number++,
- STp->logical_blk_num - blks, STp->block_size, blks);
- break;
- case OS_WRITE_EOD:
- osst_init_aux(STp, OS_FRAME_TYPE_EOD, STp->frame_seq_number++,
- STp->logical_blk_num, 0, 0);
- break;
- case OS_WRITE_NEW_MARK:
- osst_init_aux(STp, OS_FRAME_TYPE_MARKER, STp->frame_seq_number++,
- STp->logical_blk_num++, 0, blks=1);
- break;
- case OS_WRITE_HEADER:
- osst_init_aux(STp, OS_FRAME_TYPE_HEADER, 0, 0, 0, blks=0);
- break;
- default: /* probably FILLER */
- osst_init_aux(STp, OS_FRAME_TYPE_FILL, 0, 0, 0, 0);
- }
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Flushing %d bytes, Transferring %d bytes in %d lblocks.\n",
- name, offset, transfer, blks);
-#endif
-
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
- STp->timeout, MAX_RETRIES, 1);
- *aSRpnt = SRpnt;
- if (!SRpnt)
- return (-EBUSY);
-
- if ((STp->buffer)->syscall_result != 0) {
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: write sense [0]=0x%02x [2]=%02x [12]=%02x [13]=%02x\n",
- name, SRpnt->sense[0], SRpnt->sense[2],
- SRpnt->sense[12], SRpnt->sense[13]);
-#endif
- if ((SRpnt->sense[0] & 0x70) == 0x70 &&
- (SRpnt->sense[2] & 0x40) && /* FIXME - SC-30 drive doesn't assert EOM bit */
- (SRpnt->sense[2] & 0x0f) == NO_SENSE) {
- STp->dirty = 0;
- (STp->buffer)->buffer_bytes = 0;
- result = (-ENOSPC);
- }
- else {
- if (osst_write_error_recovery(STp, aSRpnt, 1)) {
- printk(KERN_ERR "%s:E: Error on flush write.\n", name);
- result = (-EIO);
- }
- }
- STps->drv_block = (-1); /* FIXME - even if write recovery succeeds? */
- }
- else {
- STp->first_frame_position++;
- STp->dirty = 0;
- (STp->buffer)->buffer_bytes = 0;
- }
- }
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Exit flush write buffer with code %d\n", name, result);
-#endif
- return result;
-}
-
-
-/* Flush the tape buffer. The tape will be positioned correctly unless
- seek_next is true. */
-static int osst_flush_buffer(struct osst_tape * STp, struct osst_request ** aSRpnt, int seek_next)
-{
- struct st_partstat * STps;
- int backspace = 0, result = 0;
-#if DEBUG
- char * name = tape_name(STp);
-#endif
-
- /*
- * If there was a bus reset, block further access
- * to this device.
- */
- if( STp->pos_unknown)
- return (-EIO);
-
- if (STp->ready != ST_READY)
- return 0;
-
- STps = &(STp->ps[STp->partition]);
- if (STps->rw == ST_WRITING || STp->dirty) { /* Writing */
- STp->write_type = OS_WRITE_DATA;
- return osst_flush_write_buffer(STp, aSRpnt);
- }
- if (STp->block_size == 0)
- return 0;
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reached flush (read) buffer\n", name);
-#endif
-
- if (!STp->can_bsr) {
- backspace = ((STp->buffer)->buffer_bytes + (STp->buffer)->read_pointer) / STp->block_size -
- ((STp->buffer)->read_pointer + STp->block_size - 1 ) / STp->block_size ;
- (STp->buffer)->buffer_bytes = 0;
- (STp->buffer)->read_pointer = 0;
- STp->frame_in_buffer = 0; /* FIXME is this relevant w. OSST? */
- }
-
- if (!seek_next) {
- if (STps->eof == ST_FM_HIT) {
- result = cross_eof(STp, aSRpnt, 0); /* Back over the EOF hit */
- if (!result)
- STps->eof = ST_NOEOF;
- else {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- }
- }
- if (!result && backspace > 0) /* TODO -- design and run a test case for this */
- result = osst_seek_logical_blk(STp, aSRpnt, STp->logical_blk_num - backspace);
- }
- else if (STps->eof == ST_FM_HIT) {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- STps->eof = ST_NOEOF;
- }
-
- return result;
-}
-
-static int osst_write_frame(struct osst_tape * STp, struct osst_request ** aSRpnt, int synchronous)
-{
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt;
- int blks;
-#if DEBUG
- char * name = tape_name(STp);
-#endif
-
- if ((!STp-> raw) && (STp->first_frame_position == 0xbae)) { /* _must_ preserve buffer! */
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Reaching config partition.\n", name);
-#endif
- if (osst_flush_drive_buffer(STp, aSRpnt) < 0) {
- return (-EIO);
- }
- /* error recovery may have bumped us past the header partition */
- if (osst_get_frame_position(STp, aSRpnt) < 0xbb8) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Skipping over config partition.\n", name);
-#endif
- osst_position_tape_and_confirm(STp, aSRpnt, 0xbb8);
- }
- }
-
- if (STp->poll)
- if (osst_wait_frame (STp, aSRpnt, STp->first_frame_position, -48, 120))
- if (osst_recover_wait_frame(STp, aSRpnt, 1))
- return (-EIO);
-
-// osst_build_stats(STp, &SRpnt);
-
- STp->ps[STp->partition].rw = ST_WRITING;
- STp->write_type = OS_WRITE_DATA;
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = WRITE_6;
- cmd[1] = 1;
- cmd[4] = 1; /* one frame at a time... */
- blks = STp->buffer->buffer_bytes / STp->block_size;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Writing %d blocks to frame %d, lblks %d-%d\n", name, blks,
- STp->frame_seq_number, STp->logical_blk_num - blks, STp->logical_blk_num - 1);
-#endif
- osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->frame_seq_number++,
- STp->logical_blk_num - blks, STp->block_size, blks);
-
-#if DEBUG
- if (!synchronous)
- STp->write_pending = 1;
-#endif
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, DMA_TO_DEVICE, STp->timeout,
- MAX_RETRIES, synchronous);
- if (!SRpnt)
- return (-EBUSY);
- *aSRpnt = SRpnt;
-
- if (synchronous) {
- if (STp->buffer->syscall_result != 0) {
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Error on write:\n", name);
-#endif
- if ((SRpnt->sense[0] & 0x70) == 0x70 &&
- (SRpnt->sense[2] & 0x40)) {
- if ((SRpnt->sense[2] & 0x0f) == VOLUME_OVERFLOW)
- return (-ENOSPC);
- }
- else {
- if (osst_write_error_recovery(STp, aSRpnt, 1))
- return (-EIO);
- }
- }
- else
- STp->first_frame_position++;
- }
-
- STp->write_count++;
-
- return 0;
-}
-
-/* Lock or unlock the drive door. Don't use when struct osst_request allocated. */
-static int do_door_lock(struct osst_tape * STp, int do_lock)
-{
- int retval, cmd;
-
- cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: %socking drive door.\n", tape_name(STp), do_lock ? "L" : "Unl");
-#endif
- retval = scsi_ioctl(STp->device, cmd, NULL);
- if (!retval) {
- STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
- }
- else {
- STp->door_locked = ST_LOCK_FAILS;
- }
- return retval;
-}
-
-/* Set the internal state after reset */
-static void reset_state(struct osst_tape *STp)
-{
- int i;
- struct st_partstat *STps;
-
- STp->pos_unknown = 0;
- for (i = 0; i < ST_NBR_PARTITIONS; i++) {
- STps = &(STp->ps[i]);
- STps->rw = ST_IDLE;
- STps->eof = ST_NOEOF;
- STps->at_sm = 0;
- STps->last_block_valid = 0;
- STps->drv_block = -1;
- STps->drv_file = -1;
- }
-}
-
-
-/* Entry points to osst */
-
-/* Write command */
-static ssize_t osst_write(struct file * filp, const char __user * buf, size_t count, loff_t *ppos)
-{
- ssize_t total, retval = 0;
- ssize_t i, do_count, blks, transfer;
- int write_threshold;
- int doing_write = 0;
- const char __user * b_point;
- struct osst_request * SRpnt = NULL;
- struct st_modedef * STm;
- struct st_partstat * STps;
- struct osst_tape * STp = filp->private_data;
- char * name = tape_name(STp);
-
-
- if (mutex_lock_interruptible(&STp->lock))
- return (-ERESTARTSYS);
-
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if( !scsi_block_when_processing_errors(STp->device) ) {
- retval = (-ENXIO);
- goto out;
- }
-
- if (STp->ready != ST_READY) {
- if (STp->ready == ST_NO_TAPE)
- retval = (-ENOMEDIUM);
- else
- retval = (-EIO);
- goto out;
- }
- STm = &(STp->modes[STp->current_mode]);
- if (!STm->defined) {
- retval = (-ENXIO);
- goto out;
- }
- if (count == 0)
- goto out;
-
- /*
- * If there was a bus reset, block further access
- * to this device.
- */
- if (STp->pos_unknown) {
- retval = (-EIO);
- goto out;
- }
-
-#if DEBUG
- if (!STp->in_use) {
- printk(OSST_DEB_MSG "%s:D: Incorrect device.\n", name);
- retval = (-EIO);
- goto out;
- }
-#endif
-
- if (STp->write_prot) {
- retval = (-EACCES);
- goto out;
- }
-
- /* Write must be integral number of blocks */
- if (STp->block_size != 0 && (count % STp->block_size) != 0) {
- printk(KERN_ERR "%s:E: Write (%Zd bytes) not multiple of tape block size (%d%c).\n",
- name, count, STp->block_size<1024?
- STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k');
- retval = (-EINVAL);
- goto out;
- }
-
- if (STp->first_frame_position >= STp->capacity - OSST_EOM_RESERVE) {
- printk(KERN_ERR "%s:E: Write truncated at EOM early warning (frame %d).\n",
- name, STp->first_frame_position);
- retval = (-ENOSPC);
- goto out;
- }
-
- if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED && !do_door_lock(STp, 1))
- STp->door_locked = ST_LOCKED_AUTO;
-
- STps = &(STp->ps[STp->partition]);
-
- if (STps->rw == ST_READING) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Switching from read to write at file %d, block %d\n", name,
- STps->drv_file, STps->drv_block);
-#endif
- retval = osst_flush_buffer(STp, &SRpnt, 0);
- if (retval)
- goto out;
- STps->rw = ST_IDLE;
- }
- if (STps->rw != ST_WRITING) {
- /* Are we totally rewriting this tape? */
- if (!STp->header_ok ||
- (STp->first_frame_position == STp->first_data_ppos && STps->drv_block < 0) ||
- (STps->drv_file == 0 && STps->drv_block == 0)) {
- STp->wrt_pass_cntr++;
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Allocating next write pass counter: %d\n",
- name, STp->wrt_pass_cntr);
-#endif
- osst_reset_header(STp, &SRpnt);
- STps->drv_file = STps->drv_block = 0;
- }
- /* Do we know where we'll be writing on the tape? */
- else {
- if ((STp->fast_open && osst_verify_position(STp, &SRpnt)) ||
- STps->drv_file < 0 || STps->drv_block < 0) {
- if (STp->first_frame_position == STp->eod_frame_ppos) { /* at EOD */
- STps->drv_file = STp->filemark_cnt;
- STps->drv_block = 0;
- }
- else {
- /* We have no idea where the tape is positioned - give up */
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: Cannot write at indeterminate position.\n", name);
-#endif
- retval = (-EIO);
- goto out;
- }
- }
- if ((STps->drv_file + STps->drv_block) > 0 && STps->drv_file < STp->filemark_cnt) {
- STp->filemark_cnt = STps->drv_file;
- STp->last_mark_ppos =
- ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt-1]);
- printk(KERN_WARNING
- "%s:W: Overwriting file %d with old write pass counter %d\n",
- name, STps->drv_file, STp->wrt_pass_cntr);
- printk(KERN_WARNING
- "%s:W: may lead to stale data being accepted on reading back!\n",
- name);
-#if DEBUG
- printk(OSST_DEB_MSG
- "%s:D: resetting filemark count to %d and last mark ppos,lbn to %d,%d\n",
- name, STp->filemark_cnt, STp->last_mark_ppos, STp->last_mark_lbn);
-#endif
- }
- }
- STp->fast_open = 0;
- }
- if (!STp->header_ok) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Write cannot proceed without valid headers\n", name);
-#endif
- retval = (-EIO);
- goto out;
- }
-
- if ((STp->buffer)->writing) {
-if (SRpnt) printk(KERN_ERR "%s:A: Not supposed to have SRpnt at line %d\n", name, __LINE__);
- osst_write_behind_check(STp);
- if ((STp->buffer)->syscall_result) {
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Async write error (write) %x.\n", name,
- (STp->buffer)->midlevel_result);
-#endif
- if ((STp->buffer)->midlevel_result == INT_MAX)
- STps->eof = ST_EOM_OK;
- else
- STps->eof = ST_EOM_ERROR;
- }
- }
- if (STps->eof == ST_EOM_OK) {
- retval = (-ENOSPC);
- goto out;
- }
- else if (STps->eof == ST_EOM_ERROR) {
- retval = (-EIO);
- goto out;
- }
-
- /* Check the buffer readability in cases where copy_user might catch
- the problems after some tape movement. */
- if ((copy_from_user(&i, buf, 1) != 0 ||
- copy_from_user(&i, buf + count - 1, 1) != 0)) {
- retval = (-EFAULT);
- goto out;
- }
-
- if (!STm->do_buffer_writes) {
- write_threshold = 1;
- }
- else
- write_threshold = (STp->buffer)->buffer_blocks * STp->block_size;
- if (!STm->do_async_writes)
- write_threshold--;
-
- total = count;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Writing %d bytes to file %d block %d lblk %d fseq %d fppos %d\n",
- name, (int) count, STps->drv_file, STps->drv_block,
- STp->logical_blk_num, STp->frame_seq_number, STp->first_frame_position);
-#endif
- b_point = buf;
- while ((STp->buffer)->buffer_bytes + count > write_threshold)
- {
- doing_write = 1;
- do_count = (STp->buffer)->buffer_blocks * STp->block_size -
- (STp->buffer)->buffer_bytes;
- if (do_count > count)
- do_count = count;
-
- i = append_to_buffer(b_point, STp->buffer, do_count);
- if (i) {
- retval = i;
- goto out;
- }
-
- blks = do_count / STp->block_size;
- STp->logical_blk_num += blks; /* logical_blk_num is incremented as data is moved from user */
-
- i = osst_write_frame(STp, &SRpnt, 1);
-
- if (i == (-ENOSPC)) {
- transfer = STp->buffer->writing; /* FIXME -- check this logic */
- if (transfer <= do_count) {
- *ppos += do_count - transfer;
- count -= do_count - transfer;
- if (STps->drv_block >= 0) {
- STps->drv_block += (do_count - transfer) / STp->block_size;
- }
- STps->eof = ST_EOM_OK;
- retval = (-ENOSPC); /* EOM within current request */
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: EOM with %d bytes unwritten.\n",
- name, (int) transfer);
-#endif
- }
- else {
- STps->eof = ST_EOM_ERROR;
- STps->drv_block = (-1); /* Too cautious? */
- retval = (-EIO); /* EOM for old data */
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: EOM with lost data.\n", name);
-#endif
- }
- }
- else
- retval = i;
-
- if (retval < 0) {
- if (SRpnt != NULL) {
- osst_release_request(SRpnt);
- SRpnt = NULL;
- }
- STp->buffer->buffer_bytes = 0;
- STp->dirty = 0;
- if (count < total)
- retval = total - count;
- goto out;
- }
-
- *ppos += do_count;
- b_point += do_count;
- count -= do_count;
- if (STps->drv_block >= 0) {
- STps->drv_block += blks;
- }
- STp->buffer->buffer_bytes = 0;
- STp->dirty = 0;
- } /* end while write threshold exceeded */
-
- if (count != 0) {
- STp->dirty = 1;
- i = append_to_buffer(b_point, STp->buffer, count);
- if (i) {
- retval = i;
- goto out;
- }
- blks = count / STp->block_size;
- STp->logical_blk_num += blks;
- if (STps->drv_block >= 0) {
- STps->drv_block += blks;
- }
- *ppos += count;
- count = 0;
- }
-
- if (doing_write && (STp->buffer)->syscall_result != 0) {
- retval = (STp->buffer)->syscall_result;
- goto out;
- }
-
- if (STm->do_async_writes && ((STp->buffer)->buffer_bytes >= STp->write_threshold)) {
- /* Schedule an asynchronous write */
- (STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
- STp->block_size) * STp->block_size;
- STp->dirty = !((STp->buffer)->writing ==
- (STp->buffer)->buffer_bytes);
-
- i = osst_write_frame(STp, &SRpnt, 0);
- if (i < 0) {
- retval = (-EIO);
- goto out;
- }
- SRpnt = NULL; /* Prevent releasing this request! */
- }
- STps->at_sm &= (total == 0);
- if (total > 0)
- STps->eof = ST_NOEOF;
-
- retval = total;
-
-out:
- if (SRpnt != NULL) osst_release_request(SRpnt);
-
- mutex_unlock(&STp->lock);
-
- return retval;
-}
-
-
-/* Read command */
-static ssize_t osst_read(struct file * filp, char __user * buf, size_t count, loff_t *ppos)
-{
- ssize_t total, retval = 0;
- ssize_t i, transfer;
- int special;
- struct st_modedef * STm;
- struct st_partstat * STps;
- struct osst_request * SRpnt = NULL;
- struct osst_tape * STp = filp->private_data;
- char * name = tape_name(STp);
-
-
- if (mutex_lock_interruptible(&STp->lock))
- return (-ERESTARTSYS);
-
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if( !scsi_block_when_processing_errors(STp->device) ) {
- retval = (-ENXIO);
- goto out;
- }
-
- if (STp->ready != ST_READY) {
- if (STp->ready == ST_NO_TAPE)
- retval = (-ENOMEDIUM);
- else
- retval = (-EIO);
- goto out;
- }
- STm = &(STp->modes[STp->current_mode]);
- if (!STm->defined) {
- retval = (-ENXIO);
- goto out;
- }
-#if DEBUG
- if (!STp->in_use) {
- printk(OSST_DEB_MSG "%s:D: Incorrect device.\n", name);
- retval = (-EIO);
- goto out;
- }
-#endif
- /* Must have initialized medium */
- if (!STp->header_ok) {
- retval = (-EIO);
- goto out;
- }
-
- if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED && !do_door_lock(STp, 1))
- STp->door_locked = ST_LOCKED_AUTO;
-
- STps = &(STp->ps[STp->partition]);
- if (STps->rw == ST_WRITING) {
- retval = osst_flush_buffer(STp, &SRpnt, 0);
- if (retval)
- goto out;
- STps->rw = ST_IDLE;
- /* FIXME -- this may leave the tape without EOD and up2date headers */
- }
-
- if ((count % STp->block_size) != 0) {
- printk(KERN_WARNING
- "%s:W: Read (%Zd bytes) not multiple of tape block size (%d%c).\n", name, count,
- STp->block_size<1024?STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k');
- }
-
-#if DEBUG
- if (debugging && STps->eof != ST_NOEOF)
- printk(OSST_DEB_MSG "%s:D: EOF/EOM flag up (%d). Bytes %d\n", name,
- STps->eof, (STp->buffer)->buffer_bytes);
-#endif
- if ((STp->buffer)->buffer_bytes == 0 &&
- STps->eof >= ST_EOD_1) {
- if (STps->eof < ST_EOD) {
- STps->eof += 1;
- retval = 0;
- goto out;
- }
- retval = (-EIO); /* EOM or Blank Check */
- goto out;
- }
-
- /* Check the buffer writability before any tape movement. Don't alter
- buffer data. */
- if (copy_from_user(&i, buf, 1) != 0 ||
- copy_to_user (buf, &i, 1) != 0 ||
- copy_from_user(&i, buf + count - 1, 1) != 0 ||
- copy_to_user (buf + count - 1, &i, 1) != 0) {
- retval = (-EFAULT);
- goto out;
- }
-
- /* Loop until enough data in buffer or a special condition found */
- for (total = 0, special = 0; total < count - STp->block_size + 1 && !special; ) {
-
- /* Get new data if the buffer is empty */
- if ((STp->buffer)->buffer_bytes == 0) {
- if (STps->eof == ST_FM_HIT)
- break;
- special = osst_get_logical_frame(STp, &SRpnt, STp->frame_seq_number, 0);
- if (special < 0) { /* No need to continue read */
- STp->frame_in_buffer = 0;
- retval = special;
- goto out;
- }
- }
-
- /* Move the data from driver buffer to user buffer */
- if ((STp->buffer)->buffer_bytes > 0) {
-#if DEBUG
- if (debugging && STps->eof != ST_NOEOF)
- printk(OSST_DEB_MSG "%s:D: EOF up (%d). Left %d, needed %d.\n", name,
- STps->eof, (STp->buffer)->buffer_bytes, (int) (count - total));
-#endif
- /* force multiple of block size, note block_size may have been adjusted */
- transfer = (((STp->buffer)->buffer_bytes < count - total ?
- (STp->buffer)->buffer_bytes : count - total)/
- STp->block_size) * STp->block_size;
-
- if (transfer == 0) {
- printk(KERN_WARNING
- "%s:W: Nothing can be transferred, requested %Zd, tape block size (%d%c).\n",
- name, count, STp->block_size < 1024?
- STp->block_size:STp->block_size/1024,
- STp->block_size<1024?'b':'k');
- break;
- }
- i = from_buffer(STp->buffer, buf, transfer);
- if (i) {
- retval = i;
- goto out;
- }
- STp->logical_blk_num += transfer / STp->block_size;
- STps->drv_block += transfer / STp->block_size;
- *ppos += transfer;
- buf += transfer;
- total += transfer;
- }
-
- if ((STp->buffer)->buffer_bytes == 0) {
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Finished with frame %d\n",
- name, STp->frame_seq_number);
-#endif
- STp->frame_in_buffer = 0;
- STp->frame_seq_number++; /* frame to look for next time */
- }
- } /* for (total = 0, special = 0; total < count && !special; ) */
-
- /* Change the eof state if no data from tape or buffer */
- if (total == 0) {
- if (STps->eof == ST_FM_HIT) {
- STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD_2:ST_FM;
- STps->drv_block = 0;
- if (STps->drv_file >= 0)
- STps->drv_file++;
- }
- else if (STps->eof == ST_EOD_1) {
- STps->eof = ST_EOD_2;
- if (STps->drv_block > 0 && STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- }
- else if (STps->eof == ST_EOD_2)
- STps->eof = ST_EOD;
- }
- else if (STps->eof == ST_FM)
- STps->eof = ST_NOEOF;
-
- retval = total;
-
-out:
- if (SRpnt != NULL) osst_release_request(SRpnt);
-
- mutex_unlock(&STp->lock);
-
- return retval;
-}
-
-
-/* Set the driver options */
-static void osst_log_options(struct osst_tape *STp, struct st_modedef *STm, char *name)
-{
- printk(KERN_INFO
-"%s:I: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
- name, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
- STm->do_read_ahead);
- printk(KERN_INFO
-"%s:I: can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
- name, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
- printk(KERN_INFO
-"%s:I: defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
- name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
- STp->scsi2_logical);
- printk(KERN_INFO
-"%s:I: sysv: %d\n", name, STm->sysv);
-#if DEBUG
- printk(KERN_INFO
- "%s:D: debugging: %d\n",
- name, debugging);
-#endif
-}
-
-
-static int osst_set_options(struct osst_tape *STp, long options)
-{
- int value;
- long code;
- struct st_modedef * STm;
- char * name = tape_name(STp);
-
- STm = &(STp->modes[STp->current_mode]);
- if (!STm->defined) {
- memcpy(STm, &(STp->modes[0]), sizeof(*STm));
- modes_defined = 1;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Initialized mode %d definition from mode 0\n",
- name, STp->current_mode);
-#endif
- }
-
- code = options & MT_ST_OPTIONS;
- if (code == MT_ST_BOOLEANS) {
- STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
- STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
- STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
- STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
- STp->two_fm = (options & MT_ST_TWO_FM) != 0;
- STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
- STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0;
- STp->can_bsr = (options & MT_ST_CAN_BSR) != 0;
- STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0;
- if ((STp->device)->scsi_level >= SCSI_2)
- STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
- STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
- STm->sysv = (options & MT_ST_SYSV) != 0;
-#if DEBUG
- debugging = (options & MT_ST_DEBUGGING) != 0;
-#endif
- osst_log_options(STp, STm, name);
- }
- else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
- value = (code == MT_ST_SETBOOLEANS);
- if ((options & MT_ST_BUFFER_WRITES) != 0)
- STm->do_buffer_writes = value;
- if ((options & MT_ST_ASYNC_WRITES) != 0)
- STm->do_async_writes = value;
- if ((options & MT_ST_DEF_WRITES) != 0)
- STm->defaults_for_writes = value;
- if ((options & MT_ST_READ_AHEAD) != 0)
- STm->do_read_ahead = value;
- if ((options & MT_ST_TWO_FM) != 0)
- STp->two_fm = value;
- if ((options & MT_ST_FAST_MTEOM) != 0)
- STp->fast_mteom = value;
- if ((options & MT_ST_AUTO_LOCK) != 0)
- STp->do_auto_lock = value;
- if ((options & MT_ST_CAN_BSR) != 0)
- STp->can_bsr = value;
- if ((options & MT_ST_NO_BLKLIMS) != 0)
- STp->omit_blklims = value;
- if ((STp->device)->scsi_level >= SCSI_2 &&
- (options & MT_ST_CAN_PARTITIONS) != 0)
- STp->can_partitions = value;
- if ((options & MT_ST_SCSI2LOGICAL) != 0)
- STp->scsi2_logical = value;
- if ((options & MT_ST_SYSV) != 0)
- STm->sysv = value;
-#if DEBUG
- if ((options & MT_ST_DEBUGGING) != 0)
- debugging = value;
-#endif
- osst_log_options(STp, STm, name);
- }
- else if (code == MT_ST_WRITE_THRESHOLD) {
- value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE;
- if (value < 1 || value > osst_buffer_size) {
- printk(KERN_WARNING "%s:W: Write threshold %d too small or too large.\n",
- name, value);
- return (-EIO);
- }
- STp->write_threshold = value;
- printk(KERN_INFO "%s:I: Write threshold set to %d bytes.\n",
- name, value);
- }
- else if (code == MT_ST_DEF_BLKSIZE) {
- value = (options & ~MT_ST_OPTIONS);
- if (value == ~MT_ST_OPTIONS) {
- STm->default_blksize = (-1);
- printk(KERN_INFO "%s:I: Default block size disabled.\n", name);
- }
- else {
- if (value < 512 || value > OS_DATA_SIZE || OS_DATA_SIZE % value) {
- printk(KERN_WARNING "%s:W: Default block size cannot be set to %d.\n",
- name, value);
- return (-EINVAL);
- }
- STm->default_blksize = value;
- printk(KERN_INFO "%s:I: Default block size set to %d bytes.\n",
- name, STm->default_blksize);
- }
- }
- else if (code == MT_ST_TIMEOUTS) {
- value = (options & ~MT_ST_OPTIONS);
- if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
- STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
- printk(KERN_INFO "%s:I: Long timeout set to %d seconds.\n", name,
- (value & ~MT_ST_SET_LONG_TIMEOUT));
- }
- else {
- STp->timeout = value * HZ;
- printk(KERN_INFO "%s:I: Normal timeout set to %d seconds.\n", name, value);
- }
- }
- else if (code == MT_ST_DEF_OPTIONS) {
- code = (options & ~MT_ST_CLEAR_DEFAULT);
- value = (options & MT_ST_CLEAR_DEFAULT);
- if (code == MT_ST_DEF_DENSITY) {
- if (value == MT_ST_CLEAR_DEFAULT) {
- STm->default_density = (-1);
- printk(KERN_INFO "%s:I: Density default disabled.\n", name);
- }
- else {
- STm->default_density = value & 0xff;
- printk(KERN_INFO "%s:I: Density default set to %x\n",
- name, STm->default_density);
- }
- }
- else if (code == MT_ST_DEF_DRVBUFFER) {
- if (value == MT_ST_CLEAR_DEFAULT) {
- STp->default_drvbuffer = 0xff;
- printk(KERN_INFO "%s:I: Drive buffer default disabled.\n", name);
- }
- else {
- STp->default_drvbuffer = value & 7;
- printk(KERN_INFO "%s:I: Drive buffer default set to %x\n",
- name, STp->default_drvbuffer);
- }
- }
- else if (code == MT_ST_DEF_COMPRESSION) {
- if (value == MT_ST_CLEAR_DEFAULT) {
- STm->default_compression = ST_DONT_TOUCH;
- printk(KERN_INFO "%s:I: Compression default disabled.\n", name);
- }
- else {
- STm->default_compression = (value & 1 ? ST_YES : ST_NO);
- printk(KERN_INFO "%s:I: Compression default set to %x\n",
- name, (value & 1));
- }
- }
- }
- else
- return (-EIO);
-
- return 0;
-}
-
-
-/* Internal ioctl function */
-static int osst_int_ioctl(struct osst_tape * STp, struct osst_request ** aSRpnt,
- unsigned int cmd_in, unsigned long arg)
-{
- int timeout;
- long ltmp;
- int i, ioctl_result;
- int chg_eof = 1;
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt = * aSRpnt;
- struct st_partstat * STps;
- int fileno, blkno, at_sm, frame_seq_numbr, logical_blk_num;
- int datalen = 0, direction = DMA_NONE;
- char * name = tape_name(STp);
-
- if (STp->ready != ST_READY && cmd_in != MTLOAD) {
- if (STp->ready == ST_NO_TAPE)
- return (-ENOMEDIUM);
- else
- return (-EIO);
- }
- timeout = STp->long_timeout;
- STps = &(STp->ps[STp->partition]);
- fileno = STps->drv_file;
- blkno = STps->drv_block;
- at_sm = STps->at_sm;
- frame_seq_numbr = STp->frame_seq_number;
- logical_blk_num = STp->logical_blk_num;
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- switch (cmd_in) {
- case MTFSFM:
- chg_eof = 0; /* Changed from the FSF after this */
- case MTFSF:
- if (STp->raw)
- return (-EIO);
- if (STp->linux_media)
- ioctl_result = osst_space_over_filemarks_forward_fast(STp, &SRpnt, cmd_in, arg);
- else
- ioctl_result = osst_space_over_filemarks_forward_slow(STp, &SRpnt, cmd_in, arg);
- if (fileno >= 0)
- fileno += arg;
- blkno = 0;
- at_sm &= (arg == 0);
- goto os_bypass;
-
- case MTBSF:
- chg_eof = 0; /* Changed from the FSF after this */
- case MTBSFM:
- if (STp->raw)
- return (-EIO);
- ioctl_result = osst_space_over_filemarks_backward(STp, &SRpnt, cmd_in, arg);
- if (fileno >= 0)
- fileno -= arg;
- blkno = (-1); /* We can't know the block number */
- at_sm &= (arg == 0);
- goto os_bypass;
-
- case MTFSR:
- case MTBSR:
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Skipping %lu blocks %s from logical block %d\n",
- name, arg, cmd_in==MTFSR?"forward":"backward", logical_blk_num);
-#endif
- if (cmd_in == MTFSR) {
- logical_blk_num += arg;
- if (blkno >= 0) blkno += arg;
- }
- else {
- logical_blk_num -= arg;
- if (blkno >= 0) blkno -= arg;
- }
- ioctl_result = osst_seek_logical_blk(STp, &SRpnt, logical_blk_num);
- fileno = STps->drv_file;
- blkno = STps->drv_block;
- at_sm &= (arg == 0);
- goto os_bypass;
-
- case MTFSS:
- cmd[0] = SPACE;
- cmd[1] = 0x04; /* Space Setmarks */ /* FIXME -- OS can't do this? */
- cmd[2] = (arg >> 16);
- cmd[3] = (arg >> 8);
- cmd[4] = arg;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Spacing tape forward %d setmarks.\n", name,
- cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
-#endif
- if (arg != 0) {
- blkno = fileno = (-1);
- at_sm = 1;
- }
- break;
- case MTBSS:
- cmd[0] = SPACE;
- cmd[1] = 0x04; /* Space Setmarks */ /* FIXME -- OS can't do this? */
- ltmp = (-arg);
- cmd[2] = (ltmp >> 16);
- cmd[3] = (ltmp >> 8);
- cmd[4] = ltmp;
-#if DEBUG
- if (debugging) {
- if (cmd[2] & 0x80)
- ltmp = 0xff000000;
- ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
- printk(OSST_DEB_MSG "%s:D: Spacing tape backward %ld setmarks.\n",
- name, (-ltmp));
- }
-#endif
- if (arg != 0) {
- blkno = fileno = (-1);
- at_sm = 1;
- }
- break;
- case MTWEOF:
- if ((STps->rw == ST_WRITING || STp->dirty) && !STp->pos_unknown) {
- STp->write_type = OS_WRITE_DATA;
- ioctl_result = osst_flush_write_buffer(STp, &SRpnt);
- } else
- ioctl_result = 0;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Writing %ld filemark(s).\n", name, arg);
-#endif
- for (i=0; i<arg; i++)
- ioctl_result |= osst_write_filemark(STp, &SRpnt);
- if (fileno >= 0) fileno += arg;
- if (blkno >= 0) blkno = 0;
- goto os_bypass;
-
- case MTWSM:
- if (STp->write_prot)
- return (-EACCES);
- if (!STp->raw)
- return 0;
- cmd[0] = WRITE_FILEMARKS; /* FIXME -- need OS version */
- if (cmd_in == MTWSM)
- cmd[1] = 2;
- cmd[2] = (arg >> 16);
- cmd[3] = (arg >> 8);
- cmd[4] = arg;
- timeout = STp->timeout;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Writing %d setmark(s).\n", name,
- cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
-#endif
- if (fileno >= 0)
- fileno += arg;
- blkno = 0;
- at_sm = (cmd_in == MTWSM);
- break;
- case MTOFFL:
- case MTLOAD:
- case MTUNLOAD:
- case MTRETEN:
- cmd[0] = START_STOP;
- cmd[1] = 1; /* Don't wait for completion */
- if (cmd_in == MTLOAD) {
- if (STp->ready == ST_NO_TAPE)
- cmd[4] = 4; /* open tray */
- else
- cmd[4] = 1; /* load */
- }
- if (cmd_in == MTRETEN)
- cmd[4] = 3; /* retension then mount */
- if (cmd_in == MTOFFL)
- cmd[4] = 4; /* rewind then eject */
- timeout = STp->timeout;
-#if DEBUG
- if (debugging) {
- switch (cmd_in) {
- case MTUNLOAD:
- printk(OSST_DEB_MSG "%s:D: Unloading tape.\n", name);
- break;
- case MTLOAD:
- printk(OSST_DEB_MSG "%s:D: Loading tape.\n", name);
- break;
- case MTRETEN:
- printk(OSST_DEB_MSG "%s:D: Retensioning tape.\n", name);
- break;
- case MTOFFL:
- printk(OSST_DEB_MSG "%s:D: Ejecting tape.\n", name);
- break;
- }
- }
-#endif
- fileno = blkno = at_sm = frame_seq_numbr = logical_blk_num = 0 ;
- break;
- case MTNOP:
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: No-op on tape.\n", name);
-#endif
- return 0; /* Should do something ? */
- break;
- case MTEOM:
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Spacing to end of recorded medium.\n", name);
-#endif
- if ((osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos) < 0) ||
- (osst_get_logical_frame(STp, &SRpnt, -1, 0) < 0)) {
- ioctl_result = -EIO;
- goto os_bypass;
- }
- if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_EOD) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: No EOD frame found where expected.\n", name);
-#endif
- ioctl_result = -EIO;
- goto os_bypass;
- }
- ioctl_result = osst_set_frame_position(STp, &SRpnt, STp->eod_frame_ppos, 0);
- fileno = STp->filemark_cnt;
- blkno = at_sm = 0;
- goto os_bypass;
-
- case MTERASE:
- if (STp->write_prot)
- return (-EACCES);
- ioctl_result = osst_reset_header(STp, &SRpnt);
- i = osst_write_eod(STp, &SRpnt);
- if (i < ioctl_result) ioctl_result = i;
- i = osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos);
- if (i < ioctl_result) ioctl_result = i;
- fileno = blkno = at_sm = 0 ;
- goto os_bypass;
-
- case MTREW:
- cmd[0] = REZERO_UNIT; /* rewind */
- cmd[1] = 1;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Rewinding tape, Immed=%d.\n", name, cmd[1]);
-#endif
- fileno = blkno = at_sm = frame_seq_numbr = logical_blk_num = 0 ;
- break;
-
- case MTSETBLK: /* Set block length */
- if ((STps->drv_block == 0 ) &&
- !STp->dirty &&
- ((STp->buffer)->buffer_bytes == 0) &&
- ((arg & MT_ST_BLKSIZE_MASK) >= 512 ) &&
- ((arg & MT_ST_BLKSIZE_MASK) <= OS_DATA_SIZE) &&
- !(OS_DATA_SIZE % (arg & MT_ST_BLKSIZE_MASK)) ) {
- /*
- * Only allowed to change the block size if you opened the
- * device at the beginning of a file before writing anything.
- * Note, that when reading, changing block_size is futile,
- * as the size used when writing overrides it.
- */
- STp->block_size = (arg & MT_ST_BLKSIZE_MASK);
- printk(KERN_INFO "%s:I: Block size set to %d bytes.\n",
- name, STp->block_size);
- return 0;
- }
- case MTSETDENSITY: /* Set tape density */
- case MTSETDRVBUFFER: /* Set drive buffering */
- case SET_DENS_AND_BLK: /* Set density and block size */
- chg_eof = 0;
- if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
- return (-EIO); /* Not allowed if data in buffer */
- if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
- (arg & MT_ST_BLKSIZE_MASK) != 0 &&
- (arg & MT_ST_BLKSIZE_MASK) != STp->block_size ) {
- printk(KERN_WARNING "%s:W: Illegal to set block size to %d%s.\n",
- name, (int)(arg & MT_ST_BLKSIZE_MASK),
- (OS_DATA_SIZE % (arg & MT_ST_BLKSIZE_MASK))?"":" now");
- return (-EINVAL);
- }
- return 0; /* FIXME silently ignore if block size didn't change */
-
- default:
- return (-ENOSYS);
- }
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, datalen, direction, timeout, MAX_RETRIES, 1);
-
- ioctl_result = (STp->buffer)->syscall_result;
-
- if (!SRpnt) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Couldn't exec scsi cmd for IOCTL\n", name);
-#endif
- return ioctl_result;
- }
-
- if (!ioctl_result) { /* SCSI command successful */
- STp->frame_seq_number = frame_seq_numbr;
- STp->logical_blk_num = logical_blk_num;
- }
-
-os_bypass:
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: IOCTL (%d) Result=%d\n", name, cmd_in, ioctl_result);
-#endif
-
- if (!ioctl_result) { /* success */
-
- if (cmd_in == MTFSFM) {
- fileno--;
- blkno--;
- }
- if (cmd_in == MTBSFM) {
- fileno++;
- blkno++;
- }
- STps->drv_block = blkno;
- STps->drv_file = fileno;
- STps->at_sm = at_sm;
-
- if (cmd_in == MTEOM)
- STps->eof = ST_EOD;
- else if ((cmd_in == MTFSFM || cmd_in == MTBSF) && STps->eof == ST_FM_HIT) {
- ioctl_result = osst_seek_logical_blk(STp, &SRpnt, STp->logical_blk_num-1);
- STps->drv_block++;
- STp->logical_blk_num++;
- STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->buffer->read_pointer = 0;
- }
- else if (cmd_in == MTFSF)
- STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_FM;
- else if (chg_eof)
- STps->eof = ST_NOEOF;
-
- if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
- STp->rew_at_close = 0;
- else if (cmd_in == MTLOAD) {
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STp->ps[i].rw = ST_IDLE;
- STp->ps[i].last_block_valid = 0;/* FIXME - where else is this field maintained? */
- }
- STp->partition = 0;
- }
-
- if (cmd_in == MTREW) {
- ioctl_result = osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos);
- if (ioctl_result > 0)
- ioctl_result = 0;
- }
-
- } else if (cmd_in == MTBSF || cmd_in == MTBSFM ) {
- if (osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos) < 0)
- STps->drv_file = STps->drv_block = -1;
- else
- STps->drv_file = STps->drv_block = 0;
- STps->eof = ST_NOEOF;
- } else if (cmd_in == MTFSF || cmd_in == MTFSFM) {
- if (osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos) < 0)
- STps->drv_file = STps->drv_block = -1;
- else {
- STps->drv_file = STp->filemark_cnt;
- STps->drv_block = 0;
- }
- STps->eof = ST_EOD;
- } else if (cmd_in == MTBSR || cmd_in == MTFSR || cmd_in == MTWEOF || cmd_in == MTEOM) {
- STps->drv_file = STps->drv_block = (-1);
- STps->eof = ST_NOEOF;
- STp->header_ok = 0;
- } else if (cmd_in == MTERASE) {
- STp->header_ok = 0;
- } else if (SRpnt) { /* SCSI command was not completely successful. */
- if (SRpnt->sense[2] & 0x40) {
- STps->eof = ST_EOM_OK;
- STps->drv_block = 0;
- }
- if (chg_eof)
- STps->eof = ST_NOEOF;
-
- if ((SRpnt->sense[2] & 0x0f) == BLANK_CHECK)
- STps->eof = ST_EOD;
-
- if (cmd_in == MTLOAD && osst_wait_for_medium(STp, &SRpnt, 60))
- ioctl_result = osst_wait_ready(STp, &SRpnt, 5 * 60, OSST_WAIT_POSITION_COMPLETE);
- }
- *aSRpnt = SRpnt;
-
- return ioctl_result;
-}
-
-
-/* Open the device */
-static int __os_scsi_tape_open(struct inode * inode, struct file * filp)
-{
- unsigned short flags;
- int i, b_size, new_session = 0, retval = 0;
- unsigned char cmd[MAX_COMMAND_SIZE];
- struct osst_request * SRpnt = NULL;
- struct osst_tape * STp;
- struct st_modedef * STm;
- struct st_partstat * STps;
- char * name;
- int dev = TAPE_NR(inode);
- int mode = TAPE_MODE(inode);
-
- /*
- * We really want to do nonseekable_open(inode, filp); here, but some
- * versions of tar incorrectly call lseek on tapes and bail out if that
- * fails. So we disallow pread() and pwrite(), but permit lseeks.
- */
- filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
-
- write_lock(&os_scsi_tapes_lock);
- if (dev >= osst_max_dev || os_scsi_tapes == NULL ||
- (STp = os_scsi_tapes[dev]) == NULL || !STp->device) {
- write_unlock(&os_scsi_tapes_lock);
- return (-ENXIO);
- }
-
- name = tape_name(STp);
-
- if (STp->in_use) {
- write_unlock(&os_scsi_tapes_lock);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Device already in use.\n", name);
-#endif
- return (-EBUSY);
- }
- if (scsi_device_get(STp->device)) {
- write_unlock(&os_scsi_tapes_lock);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Failed scsi_device_get.\n", name);
-#endif
- return (-ENXIO);
- }
- filp->private_data = STp;
- STp->in_use = 1;
- write_unlock(&os_scsi_tapes_lock);
- STp->rew_at_close = TAPE_REWIND(inode);
-
- if( !scsi_block_when_processing_errors(STp->device) ) {
- return -ENXIO;
- }
-
- if (mode != STp->current_mode) {
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Mode change from %d to %d.\n",
- name, STp->current_mode, mode);
-#endif
- new_session = 1;
- STp->current_mode = mode;
- }
- STm = &(STp->modes[STp->current_mode]);
-
- flags = filp->f_flags;
- STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
-
- STp->raw = TAPE_IS_RAW(inode);
- if (STp->raw)
- STp->header_ok = 0;
-
- /* Allocate data segments for this device's tape buffer */
- if (!enlarge_buffer(STp->buffer, STp->restr_dma)) {
- printk(KERN_ERR "%s:E: Unable to allocate memory segments for tape buffer.\n", name);
- retval = (-EOVERFLOW);
- goto err_out;
- }
- if (STp->buffer->buffer_size >= OS_FRAME_SIZE) {
- for (i = 0, b_size = 0;
- (i < STp->buffer->sg_segs) && ((b_size + STp->buffer->sg[i].length) <= OS_DATA_SIZE);
- b_size += STp->buffer->sg[i++].length);
- STp->buffer->aux = (os_aux_t *) (page_address(sg_page(&STp->buffer->sg[i])) + OS_DATA_SIZE - b_size);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: b_data points to %p in segment 0 at %p\n", name,
- STp->buffer->b_data, page_address(STp->buffer->sg[0].page));
- printk(OSST_DEB_MSG "%s:D: AUX points to %p in segment %d at %p\n", name,
- STp->buffer->aux, i, page_address(STp->buffer->sg[i].page));
-#endif
- } else {
- STp->buffer->aux = NULL; /* this had better never happen! */
- printk(KERN_NOTICE "%s:A: Framesize %d too large for buffer.\n", name, OS_FRAME_SIZE);
- retval = (-EIO);
- goto err_out;
- }
- STp->buffer->writing = 0;
- STp->buffer->syscall_result = 0;
- STp->dirty = 0;
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STps = &(STp->ps[i]);
- STps->rw = ST_IDLE;
- }
- STp->ready = ST_READY;
-#if DEBUG
- STp->nbr_waits = STp->nbr_finished = 0;
-#endif
-
- memset (cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(NULL, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result; /* FIXME - valid? */
- goto err_out;
- }
- if ((SRpnt->sense[0] & 0x70) == 0x70 &&
- (SRpnt->sense[2] & 0x0f) == NOT_READY &&
- SRpnt->sense[12] == 4 ) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Unit not ready, cause %x\n", name, SRpnt->sense[13]);
-#endif
- if (filp->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- goto err_out;
- }
- if (SRpnt->sense[13] == 2) { /* initialize command required (LOAD) */
- memset (cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = START_STOP;
- cmd[1] = 1;
- cmd[4] = 1;
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
- STp->timeout, MAX_RETRIES, 1);
- }
- osst_wait_ready(STp, &SRpnt, (SRpnt->sense[13]==1?15:3) * 60, 0);
- }
- if ((SRpnt->sense[0] & 0x70) == 0x70 &&
- (SRpnt->sense[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Unit wants attention\n", name);
-#endif
- STp->header_ok = 0;
-
- for (i=0; i < 10; i++) {
-
- memset (cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
- STp->timeout, MAX_RETRIES, 1);
- if ((SRpnt->sense[0] & 0x70) != 0x70 ||
- (SRpnt->sense[2] & 0x0f) != UNIT_ATTENTION)
- break;
- }
-
- STp->pos_unknown = 0;
- STp->partition = STp->new_partition = 0;
- if (STp->can_partitions)
- STp->nbr_partitions = 1; /* This guess will be updated later if necessary */
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STps = &(STp->ps[i]);
- STps->rw = ST_IDLE; /* FIXME - seems to be redundant... */
- STps->eof = ST_NOEOF;
- STps->at_sm = 0;
- STps->last_block_valid = 0;
- STps->drv_block = 0;
- STps->drv_file = 0 ;
- }
- new_session = 1;
- STp->recover_count = 0;
- STp->abort_count = 0;
- }
- /*
- * if we have valid headers from before, and the drive/tape seem untouched,
- * open without reconfiguring and re-reading the headers
- */
- if (!STp->buffer->syscall_result && STp->header_ok &&
- !SRpnt->result && SRpnt->sense[0] == 0) {
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8;
- cmd[2] = VENDOR_IDENT_PAGE;
- cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
-
- if (STp->buffer->syscall_result ||
- STp->buffer->b_data[MODE_HEADER_LENGTH + 2] != 'L' ||
- STp->buffer->b_data[MODE_HEADER_LENGTH + 3] != 'I' ||
- STp->buffer->b_data[MODE_HEADER_LENGTH + 4] != 'N' ||
- STp->buffer->b_data[MODE_HEADER_LENGTH + 5] != '4' ) {
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Signature was changed to %c%c%c%c\n", name,
- STp->buffer->b_data[MODE_HEADER_LENGTH + 2],
- STp->buffer->b_data[MODE_HEADER_LENGTH + 3],
- STp->buffer->b_data[MODE_HEADER_LENGTH + 4],
- STp->buffer->b_data[MODE_HEADER_LENGTH + 5]);
-#endif
- STp->header_ok = 0;
- }
- i = STp->first_frame_position;
- if (STp->header_ok && i == osst_get_frame_position(STp, &SRpnt)) {
- if (STp->door_locked == ST_UNLOCKED) {
- if (do_door_lock(STp, 1))
- printk(KERN_INFO "%s:I: Can't lock drive door\n", name);
- else
- STp->door_locked = ST_LOCKED_AUTO;
- }
- if (!STp->frame_in_buffer) {
- STp->block_size = (STm->default_blksize > 0) ?
- STm->default_blksize : OS_DATA_SIZE;
- STp->buffer->buffer_bytes = STp->buffer->read_pointer = 0;
- }
- STp->buffer->buffer_blocks = OS_DATA_SIZE / STp->block_size;
- STp->fast_open = 1;
- osst_release_request(SRpnt);
- return 0;
- }
-#if DEBUG
- if (i != STp->first_frame_position)
- printk(OSST_DEB_MSG "%s:D: Tape position changed from %d to %d\n",
- name, i, STp->first_frame_position);
-#endif
- STp->header_ok = 0;
- }
- STp->fast_open = 0;
-
- if ((STp->buffer)->syscall_result != 0 && /* in all error conditions except no medium */
- (SRpnt->sense[2] != 2 || SRpnt->sense[12] != 0x3A) ) {
-
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = 4 + MODE_HEADER_LENGTH;
-
- (STp->buffer)->b_data[0] = cmd[4] - 1;
- (STp->buffer)->b_data[1] = 0; /* Medium Type - ignoring */
- (STp->buffer)->b_data[2] = 0; /* Reserved */
- (STp->buffer)->b_data[3] = 0; /* Block Descriptor Length */
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = 0x3f;
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 1;
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 2;
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 3;
-
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Applying soft reset\n", name);
-#endif
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
-
- STp->header_ok = 0;
-
- for (i=0; i < 10; i++) {
-
- memset (cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
- SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
- STp->timeout, MAX_RETRIES, 1);
- if ((SRpnt->sense[0] & 0x70) != 0x70 ||
- (SRpnt->sense[2] & 0x0f) == NOT_READY)
- break;
-
- if ((SRpnt->sense[2] & 0x0f) == UNIT_ATTENTION) {
- int j;
-
- STp->pos_unknown = 0;
- STp->partition = STp->new_partition = 0;
- if (STp->can_partitions)
- STp->nbr_partitions = 1; /* This guess will be updated later if necessary */
- for (j = 0; j < ST_NBR_PARTITIONS; j++) {
- STps = &(STp->ps[j]);
- STps->rw = ST_IDLE;
- STps->eof = ST_NOEOF;
- STps->at_sm = 0;
- STps->last_block_valid = 0;
- STps->drv_block = 0;
- STps->drv_file = 0 ;
- }
- new_session = 1;
- }
- }
- }
-
- if (osst_wait_ready(STp, &SRpnt, 15 * 60, 0)) /* FIXME - not allowed with NOBLOCK */
- printk(KERN_INFO "%s:I: Device did not become Ready in open\n", name);
-
- if ((STp->buffer)->syscall_result != 0) {
- if ((STp->device)->scsi_level >= SCSI_2 &&
- (SRpnt->sense[0] & 0x70) == 0x70 &&
- (SRpnt->sense[2] & 0x0f) == NOT_READY &&
- SRpnt->sense[12] == 0x3a) { /* Check ASC */
- STp->ready = ST_NO_TAPE;
- } else
- STp->ready = ST_NOT_READY;
- osst_release_request(SRpnt);
- SRpnt = NULL;
- STp->density = 0; /* Clear the erroneous "residue" */
- STp->write_prot = 0;
- STp->block_size = 0;
- STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
- STp->partition = STp->new_partition = 0;
- STp->door_locked = ST_UNLOCKED;
- return 0;
- }
-
- osst_configure_onstream(STp, &SRpnt);
-
- STp->block_size = STp->raw ? OS_FRAME_SIZE : (
- (STm->default_blksize > 0) ? STm->default_blksize : OS_DATA_SIZE);
- STp->buffer->buffer_blocks = STp->raw ? 1 : OS_DATA_SIZE / STp->block_size;
- STp->buffer->buffer_bytes =
- STp->buffer->read_pointer =
- STp->frame_in_buffer = 0;
-
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Block size: %d, frame size: %d, buffer size: %d (%d blocks).\n",
- name, STp->block_size, OS_FRAME_SIZE, (STp->buffer)->buffer_size,
- (STp->buffer)->buffer_blocks);
-#endif
-
- if (STp->drv_write_prot) {
- STp->write_prot = 1;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Write protected\n", name);
-#endif
- if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
- retval = (-EROFS);
- goto err_out;
- }
- }
-
- if (new_session) { /* Change the drive parameters for the new mode */
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: New Session\n", name);
-#endif
- STp->density_changed = STp->blksize_changed = 0;
- STp->compression_changed = 0;
- }
-
- /*
- * properly position the tape and check the ADR headers
- */
- if (STp->door_locked == ST_UNLOCKED) {
- if (do_door_lock(STp, 1))
- printk(KERN_INFO "%s:I: Can't lock drive door\n", name);
- else
- STp->door_locked = ST_LOCKED_AUTO;
- }
-
- osst_analyze_headers(STp, &SRpnt);
-
- osst_release_request(SRpnt);
- SRpnt = NULL;
-
- return 0;
-
-err_out:
- if (SRpnt != NULL)
- osst_release_request(SRpnt);
- normalize_buffer(STp->buffer);
- STp->header_ok = 0;
- STp->in_use = 0;
- scsi_device_put(STp->device);
-
- return retval;
-}
-
-/* BKL pushdown: spaghetti avoidance wrapper */
-static int os_scsi_tape_open(struct inode * inode, struct file * filp)
-{
- int ret;
-
- mutex_lock(&osst_int_mutex);
- ret = __os_scsi_tape_open(inode, filp);
- mutex_unlock(&osst_int_mutex);
- return ret;
-}
-
-
-
-/* Flush the tape buffer before close */
-static int os_scsi_tape_flush(struct file * filp, fl_owner_t id)
-{
- int result = 0, result2;
- struct osst_tape * STp = filp->private_data;
- struct st_modedef * STm = &(STp->modes[STp->current_mode]);
- struct st_partstat * STps = &(STp->ps[STp->partition]);
- struct osst_request * SRpnt = NULL;
- char * name = tape_name(STp);
-
- if (file_count(filp) > 1)
- return 0;
-
- if ((STps->rw == ST_WRITING || STp->dirty) && !STp->pos_unknown) {
- STp->write_type = OS_WRITE_DATA;
- result = osst_flush_write_buffer(STp, &SRpnt);
- if (result != 0 && result != (-ENOSPC))
- goto out;
- }
- if ( STps->rw >= ST_WRITING && !STp->pos_unknown) {
-
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG "%s:D: File length %ld bytes.\n",
- name, (long)(filp->f_pos));
- printk(OSST_DEB_MSG "%s:D: Async write waits %d, finished %d.\n",
- name, STp->nbr_waits, STp->nbr_finished);
- }
-#endif
- result = osst_write_trailer(STp, &SRpnt, !(STp->rew_at_close));
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG "%s:D: Buffer flushed, %d EOF(s) written\n",
- name, 1+STp->two_fm);
-#endif
- }
- else if (!STp->rew_at_close) {
- STps = &(STp->ps[STp->partition]);
- if (!STm->sysv || STps->rw != ST_READING) {
- if (STp->can_bsr)
- result = osst_flush_buffer(STp, &SRpnt, 0); /* this is the default path */
- else if (STps->eof == ST_FM_HIT) {
- result = cross_eof(STp, &SRpnt, 0);
- if (result) {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- STps->eof = ST_FM;
- }
- else
- STps->eof = ST_NOEOF;
- }
- }
- else if ((STps->eof == ST_NOEOF &&
- !(result = cross_eof(STp, &SRpnt, 1))) ||
- STps->eof == ST_FM_HIT) {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- STps->eof = ST_FM;
- }
- }
-
-out:
- if (STp->rew_at_close) {
- result2 = osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos);
- STps->drv_file = STps->drv_block = STp->frame_seq_number = STp->logical_blk_num = 0;
- if (result == 0 && result2 < 0)
- result = result2;
- }
- if (SRpnt) osst_release_request(SRpnt);
-
- if (STp->abort_count || STp->recover_count) {
- printk(KERN_INFO "%s:I:", name);
- if (STp->abort_count)
- printk(" %d unrecovered errors", STp->abort_count);
- if (STp->recover_count)
- printk(" %d recovered errors", STp->recover_count);
- if (STp->write_count)
- printk(" in %d frames written", STp->write_count);
- if (STp->read_count)
- printk(" in %d frames read", STp->read_count);
- printk("\n");
- STp->recover_count = 0;
- STp->abort_count = 0;
- }
- STp->write_count = 0;
- STp->read_count = 0;
-
- return result;
-}
-
-
-/* Close the device and release it */
-static int os_scsi_tape_close(struct inode * inode, struct file * filp)
-{
- int result = 0;
- struct osst_tape * STp = filp->private_data;
-
- if (STp->door_locked == ST_LOCKED_AUTO)
- do_door_lock(STp, 0);
-
- if (STp->raw)
- STp->header_ok = 0;
-
- normalize_buffer(STp->buffer);
- write_lock(&os_scsi_tapes_lock);
- STp->in_use = 0;
- write_unlock(&os_scsi_tapes_lock);
-
- scsi_device_put(STp->device);
-
- return result;
-}
-
-
-/* The ioctl command */
-static long osst_ioctl(struct file * file,
- unsigned int cmd_in, unsigned long arg)
-{
- int i, cmd_nr, cmd_type, blk, retval = 0;
- struct st_modedef * STm;
- struct st_partstat * STps;
- struct osst_request * SRpnt = NULL;
- struct osst_tape * STp = file->private_data;
- char * name = tape_name(STp);
- void __user * p = (void __user *)arg;
-
- mutex_lock(&osst_int_mutex);
- if (mutex_lock_interruptible(&STp->lock)) {
- mutex_unlock(&osst_int_mutex);
- return -ERESTARTSYS;
- }
-
-#if DEBUG
- if (debugging && !STp->in_use) {
- printk(OSST_DEB_MSG "%s:D: Incorrect device.\n", name);
- retval = (-EIO);
- goto out;
- }
-#endif
- STm = &(STp->modes[STp->current_mode]);
- STps = &(STp->ps[STp->partition]);
-
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if( !scsi_block_when_processing_errors(STp->device) ) {
- retval = (-ENXIO);
- goto out;
- }
-
- cmd_type = _IOC_TYPE(cmd_in);
- cmd_nr = _IOC_NR(cmd_in);
-#if DEBUG
- printk(OSST_DEB_MSG "%s:D: Ioctl %d,%d in %s mode\n", name,
- cmd_type, cmd_nr, STp->raw?"raw":"normal");
-#endif
- if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
- struct mtop mtc;
- int auto_weof = 0;
-
- if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
- retval = (-EINVAL);
- goto out;
- }
-
- i = copy_from_user((char *) &mtc, p, sizeof(struct mtop));
- if (i) {
- retval = (-EFAULT);
- goto out;
- }
-
- if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
- printk(KERN_WARNING "%s:W: MTSETDRVBUFFER only allowed for root.\n", name);
- retval = (-EPERM);
- goto out;
- }
-
- if (!STm->defined && (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0)) {
- retval = (-ENXIO);
- goto out;
- }
-
- if (!STp->pos_unknown) {
-
- if (STps->eof == ST_FM_HIT) {
- if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) {
- mtc.mt_count -= 1;
- if (STps->drv_file >= 0)
- STps->drv_file += 1;
- }
- else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
- mtc.mt_count += 1;
- if (STps->drv_file >= 0)
- STps->drv_file += 1;
- }
- }
-
- if (mtc.mt_op == MTSEEK) {
- /* Old position must be restored if partition will be changed */
- i = !STp->can_partitions || (STp->new_partition != STp->partition);
- }
- else {
- i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
- mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
- mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
- mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
- mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM ||
- mtc.mt_op == MTCOMPRESSION;
- }
- i = osst_flush_buffer(STp, &SRpnt, i);
- if (i < 0) {
- retval = i;
- goto out;
- }
- }
- else {
- /*
- * If there was a bus reset, block further access
- * to this device. If the user wants to rewind the tape,
- * then reset the flag and allow access again.
- */
- if(mtc.mt_op != MTREW &&
- mtc.mt_op != MTOFFL &&
- mtc.mt_op != MTRETEN &&
- mtc.mt_op != MTERASE &&
- mtc.mt_op != MTSEEK &&
- mtc.mt_op != MTEOM) {
- retval = (-EIO);
- goto out;
- }
- reset_state(STp);
- /* remove this when the midlevel properly clears was_reset */
- STp->device->was_reset = 0;
- }
-
- if (mtc.mt_op != MTCOMPRESSION && mtc.mt_op != MTLOCK &&
- mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
- mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTSETDRVBUFFER &&
- mtc.mt_op != MTMKPART && mtc.mt_op != MTSETPART &&
- mtc.mt_op != MTWEOF && mtc.mt_op != MTWSM ) {
-
- /*
- * The user tells us to move to another position on the tape.
- * If we were appending to the tape content, that would leave
- * the tape without proper end, in that case write EOD and
- * update the header to reflect its position.
- */
-#if DEBUG
- printk(KERN_WARNING "%s:D: auto_weod %s at ffp=%d,efp=%d,fsn=%d,lbn=%d,fn=%d,bn=%d\n", name,
- STps->rw >= ST_WRITING ? "write" : STps->rw == ST_READING ? "read" : "idle",
- STp->first_frame_position, STp->eod_frame_ppos, STp->frame_seq_number,
- STp->logical_blk_num, STps->drv_file, STps->drv_block );
-#endif
- if (STps->rw >= ST_WRITING && STp->first_frame_position >= STp->eod_frame_ppos) {
- auto_weof = ((STp->write_type != OS_WRITE_NEW_MARK) &&
- !(mtc.mt_op == MTREW || mtc.mt_op == MTOFFL));
- i = osst_write_trailer(STp, &SRpnt,
- !(mtc.mt_op == MTREW || mtc.mt_op == MTOFFL));
-#if DEBUG
- printk(KERN_WARNING "%s:D: post trailer xeof=%d,ffp=%d,efp=%d,fsn=%d,lbn=%d,fn=%d,bn=%d\n",
- name, auto_weof, STp->first_frame_position, STp->eod_frame_ppos,
- STp->frame_seq_number, STp->logical_blk_num, STps->drv_file, STps->drv_block );
-#endif
- if (i < 0) {
- retval = i;
- goto out;
- }
- }
- STps->rw = ST_IDLE;
- }
-
- if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
- do_door_lock(STp, 0); /* Ignore result! */
-
- if (mtc.mt_op == MTSETDRVBUFFER &&
- (mtc.mt_count & MT_ST_OPTIONS) != 0) {
- retval = osst_set_options(STp, mtc.mt_count);
- goto out;
- }
-
- if (mtc.mt_op == MTSETPART) {
- if (mtc.mt_count >= STp->nbr_partitions)
- retval = -EINVAL;
- else {
- STp->new_partition = mtc.mt_count;
- retval = 0;
- }
- goto out;
- }
-
- if (mtc.mt_op == MTMKPART) {
- if (!STp->can_partitions) {
- retval = (-EINVAL);
- goto out;
- }
- if ((i = osst_int_ioctl(STp, &SRpnt, MTREW, 0)) < 0 /*||
- (i = partition_tape(inode, mtc.mt_count)) < 0*/) {
- retval = i;
- goto out;
- }
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STp->ps[i].rw = ST_IDLE;
- STp->ps[i].at_sm = 0;
- STp->ps[i].last_block_valid = 0;
- }
- STp->partition = STp->new_partition = 0;
- STp->nbr_partitions = 1; /* Bad guess ?-) */
- STps->drv_block = STps->drv_file = 0;
- retval = 0;
- goto out;
- }
-
- if (mtc.mt_op == MTSEEK) {
- if (STp->raw)
- i = osst_set_frame_position(STp, &SRpnt, mtc.mt_count, 0);
- else
- i = osst_seek_sector(STp, &SRpnt, mtc.mt_count);
- if (!STp->can_partitions)
- STp->ps[0].rw = ST_IDLE;
- retval = i;
- goto out;
- }
-
- if (mtc.mt_op == MTLOCK || mtc.mt_op == MTUNLOCK) {
- retval = do_door_lock(STp, (mtc.mt_op == MTLOCK));
- goto out;
- }
-
- if (auto_weof)
- cross_eof(STp, &SRpnt, 0);
-
- if (mtc.mt_op == MTCOMPRESSION)
- retval = -EINVAL; /* OnStream drives don't have compression hardware */
- else
- /* MTBSF MTBSFM MTBSR MTBSS MTEOM MTERASE MTFSF MTFSFB MTFSR MTFSS
- * MTLOAD MTOFFL MTRESET MTRETEN MTREW MTUNLOAD MTWEOF MTWSM */
- retval = osst_int_ioctl(STp, &SRpnt, mtc.mt_op, mtc.mt_count);
- goto out;
- }
-
- if (!STm->defined) {
- retval = (-ENXIO);
- goto out;
- }
-
- if ((i = osst_flush_buffer(STp, &SRpnt, 0)) < 0) {
- retval = i;
- goto out;
- }
-
- if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
- struct mtget mt_status;
-
- if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) {
- retval = (-EINVAL);
- goto out;
- }
-
- mt_status.mt_type = MT_ISONSTREAM_SC;
- mt_status.mt_erreg = STp->recover_erreg << MT_ST_SOFTERR_SHIFT;
- mt_status.mt_dsreg =
- ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
- ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
- mt_status.mt_blkno = STps->drv_block;
- mt_status.mt_fileno = STps->drv_file;
- if (STp->block_size != 0) {
- if (STps->rw == ST_WRITING)
- mt_status.mt_blkno += (STp->buffer)->buffer_bytes / STp->block_size;
- else if (STps->rw == ST_READING)
- mt_status.mt_blkno -= ((STp->buffer)->buffer_bytes +
- STp->block_size - 1) / STp->block_size;
- }
-
- mt_status.mt_gstat = 0;
- if (STp->drv_write_prot)
- mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
- if (mt_status.mt_blkno == 0) {
- if (mt_status.mt_fileno == 0)
- mt_status.mt_gstat |= GMT_BOT(0xffffffff);
- else
- mt_status.mt_gstat |= GMT_EOF(0xffffffff);
- }
- mt_status.mt_resid = STp->partition;
- if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
- mt_status.mt_gstat |= GMT_EOT(0xffffffff);
- else if (STps->eof >= ST_EOM_OK)
- mt_status.mt_gstat |= GMT_EOD(0xffffffff);
- if (STp->density == 1)
- mt_status.mt_gstat |= GMT_D_800(0xffffffff);
- else if (STp->density == 2)
- mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
- else if (STp->density == 3)
- mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
- if (STp->ready == ST_READY)
- mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
- if (STp->ready == ST_NO_TAPE)
- mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
- if (STps->at_sm)
- mt_status.mt_gstat |= GMT_SM(0xffffffff);
- if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) ||
- STp->drv_buffer != 0)
- mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
-
- i = copy_to_user(p, &mt_status, sizeof(struct mtget));
- if (i) {
- retval = (-EFAULT);
- goto out;
- }
-
- STp->recover_erreg = 0; /* Clear after read */
- retval = 0;
- goto out;
- } /* End of MTIOCGET */
-
- if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
- struct mtpos mt_pos;
-
- if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) {
- retval = (-EINVAL);
- goto out;
- }
- if (STp->raw)
- blk = osst_get_frame_position(STp, &SRpnt);
- else
- blk = osst_get_sector(STp, &SRpnt);
- if (blk < 0) {
- retval = blk;
- goto out;
- }
- mt_pos.mt_blkno = blk;
- i = copy_to_user(p, &mt_pos, sizeof(struct mtpos));
- if (i)
- retval = -EFAULT;
- goto out;
- }
- if (SRpnt) osst_release_request(SRpnt);
-
- mutex_unlock(&STp->lock);
-
- retval = scsi_ioctl(STp->device, cmd_in, p);
- mutex_unlock(&osst_int_mutex);
- return retval;
-
-out:
- if (SRpnt) osst_release_request(SRpnt);
-
- mutex_unlock(&STp->lock);
- mutex_unlock(&osst_int_mutex);
-
- return retval;
-}
-
-#ifdef CONFIG_COMPAT
-static long osst_compat_ioctl(struct file * file, unsigned int cmd_in, unsigned long arg)
-{
- struct osst_tape *STp = file->private_data;
- struct scsi_device *sdev = STp->device;
- int ret = -ENOIOCTLCMD;
- if (sdev->host->hostt->compat_ioctl) {
-
- ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
-
- }
- return ret;
-}
-#endif
-
-
-
-/* Memory handling routines */
-
-/* Try to allocate a new tape buffer skeleton. Caller must not hold os_scsi_tapes_lock */
-static struct osst_buffer * new_tape_buffer( int from_initialization, int need_dma, int max_sg )
-{
- int i;
- gfp_t priority;
- struct osst_buffer *tb;
-
- if (from_initialization)
- priority = GFP_ATOMIC;
- else
- priority = GFP_KERNEL;
-
- i = sizeof(struct osst_buffer) + (osst_max_sg_segs - 1) * sizeof(struct scatterlist);
- tb = kzalloc(i, priority);
- if (!tb) {
- printk(KERN_NOTICE "osst :I: Can't allocate new tape buffer.\n");
- return NULL;
- }
-
- tb->sg_segs = tb->orig_sg_segs = 0;
- tb->use_sg = max_sg;
- tb->in_use = 1;
- tb->dma = need_dma;
- tb->buffer_size = 0;
-#if DEBUG
- if (debugging)
- printk(OSST_DEB_MSG
- "osst :D: Allocated tape buffer skeleton (%d bytes, %d segments, dma: %d).\n",
- i, max_sg, need_dma);
-#endif
- return tb;
-}
-
-/* Try to allocate a temporary (while a user has the device open) enlarged tape buffer */
-static int enlarge_buffer(struct osst_buffer *STbuffer, int need_dma)
-{
- int segs, nbr, max_segs, b_size, order, got;
- gfp_t priority;
-
- if (STbuffer->buffer_size >= OS_FRAME_SIZE)
- return 1;
-
- if (STbuffer->sg_segs) {
- printk(KERN_WARNING "osst :A: Buffer not previously normalized.\n");
- normalize_buffer(STbuffer);
- }
- /* See how many segments we can use -- need at least two */
- nbr = max_segs = STbuffer->use_sg;
- if (nbr <= 2)
- return 0;
-
- priority = GFP_KERNEL /* | __GFP_NOWARN */;
- if (need_dma)
- priority |= GFP_DMA;
-
- /* Try to allocate the first segment up to OS_DATA_SIZE and the others
- big enough to reach the goal (code assumes no segments in place) */
- for (b_size = OS_DATA_SIZE, order = OSST_FIRST_ORDER; b_size >= PAGE_SIZE; order--, b_size /= 2) {
- struct page *page = alloc_pages(priority, order);
-
- STbuffer->sg[0].offset = 0;
- if (page != NULL) {
- sg_set_page(&STbuffer->sg[0], page, b_size, 0);
- STbuffer->b_data = page_address(page);
- break;
- }
- }
- if (sg_page(&STbuffer->sg[0]) == NULL) {
- printk(KERN_NOTICE "osst :I: Can't allocate tape buffer main segment.\n");
- return 0;
- }
- /* Got initial segment of 'bsize,order', continue with same size if possible, except for AUX */
- for (segs=STbuffer->sg_segs=1, got=b_size;
- segs < max_segs && got < OS_FRAME_SIZE; ) {
- struct page *page = alloc_pages(priority, (OS_FRAME_SIZE - got <= PAGE_SIZE) ? 0 : order);
- STbuffer->sg[segs].offset = 0;
- if (page == NULL) {
- printk(KERN_WARNING "osst :W: Failed to enlarge buffer to %d bytes.\n",
- OS_FRAME_SIZE);
-#if DEBUG
- STbuffer->buffer_size = got;
-#endif
- normalize_buffer(STbuffer);
- return 0;
- }
- sg_set_page(&STbuffer->sg[segs], page, (OS_FRAME_SIZE - got <= PAGE_SIZE / 2) ? (OS_FRAME_SIZE - got) : b_size, 0);
- got += STbuffer->sg[segs].length;
- STbuffer->buffer_size = got;
- STbuffer->sg_segs = ++segs;
- }
-#if DEBUG
- if (debugging) {
- printk(OSST_DEB_MSG
- "osst :D: Expanded tape buffer (%d bytes, %d->%d segments, dma: %d, at: %p).\n",
- got, STbuffer->orig_sg_segs, STbuffer->sg_segs, need_dma, STbuffer->b_data);
- printk(OSST_DEB_MSG
- "osst :D: segment sizes: first %d at %p, last %d bytes at %p.\n",
- STbuffer->sg[0].length, page_address(STbuffer->sg[0].page),
- STbuffer->sg[segs-1].length, page_address(STbuffer->sg[segs-1].page));
- }
-#endif
-
- return 1;
-}
-
-
-/* Release the segments */
-static void normalize_buffer(struct osst_buffer *STbuffer)
-{
- int i, order, b_size;
-
- for (i=0; i < STbuffer->sg_segs; i++) {
-
- for (b_size = PAGE_SIZE, order = 0;
- b_size < STbuffer->sg[i].length;
- b_size *= 2, order++);
-
- __free_pages(sg_page(&STbuffer->sg[i]), order);
- STbuffer->buffer_size -= STbuffer->sg[i].length;
- }
-#if DEBUG
- if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
- printk(OSST_DEB_MSG "osst :D: Buffer at %p normalized to %d bytes (segs %d).\n",
- STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs);
-#endif
- STbuffer->sg_segs = STbuffer->orig_sg_segs = 0;
-}
-
-
-/* Move data from the user buffer to the tape buffer. Returns zero (success) or
- negative error code. */
-static int append_to_buffer(const char __user *ubp, struct osst_buffer *st_bp, int do_count)
-{
- int i, cnt, res, offset;
-
- for (i=0, offset=st_bp->buffer_bytes;
- i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
- offset -= st_bp->sg[i].length;
- if (i == st_bp->sg_segs) { /* Should never happen */
- printk(KERN_WARNING "osst :A: Append_to_buffer offset overflow.\n");
- return (-EIO);
- }
- for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
- cnt = st_bp->sg[i].length - offset < do_count ?
- st_bp->sg[i].length - offset : do_count;
- res = copy_from_user(page_address(sg_page(&st_bp->sg[i])) + offset, ubp, cnt);
- if (res)
- return (-EFAULT);
- do_count -= cnt;
- st_bp->buffer_bytes += cnt;
- ubp += cnt;
- offset = 0;
- }
- if (do_count) { /* Should never happen */
- printk(KERN_WARNING "osst :A: Append_to_buffer overflow (left %d).\n",
- do_count);
- return (-EIO);
- }
- return 0;
-}
-
-
-/* Move data from the tape buffer to the user buffer. Returns zero (success) or
- negative error code. */
-static int from_buffer(struct osst_buffer *st_bp, char __user *ubp, int do_count)
-{
- int i, cnt, res, offset;
-
- for (i=0, offset=st_bp->read_pointer;
- i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
- offset -= st_bp->sg[i].length;
- if (i == st_bp->sg_segs) { /* Should never happen */
- printk(KERN_WARNING "osst :A: From_buffer offset overflow.\n");
- return (-EIO);
- }
- for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
- cnt = st_bp->sg[i].length - offset < do_count ?
- st_bp->sg[i].length - offset : do_count;
- res = copy_to_user(ubp, page_address(sg_page(&st_bp->sg[i])) + offset, cnt);
- if (res)
- return (-EFAULT);
- do_count -= cnt;
- st_bp->buffer_bytes -= cnt;
- st_bp->read_pointer += cnt;
- ubp += cnt;
- offset = 0;
- }
- if (do_count) { /* Should never happen */
- printk(KERN_WARNING "osst :A: From_buffer overflow (left %d).\n", do_count);
- return (-EIO);
- }
- return 0;
-}
-
-/* Sets the tail of the buffer after fill point to zero.
- Returns zero (success) or negative error code. */
-static int osst_zero_buffer_tail(struct osst_buffer *st_bp)
-{
- int i, offset, do_count, cnt;
-
- for (i = 0, offset = st_bp->buffer_bytes;
- i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
- offset -= st_bp->sg[i].length;
- if (i == st_bp->sg_segs) { /* Should never happen */
- printk(KERN_WARNING "osst :A: Zero_buffer offset overflow.\n");
- return (-EIO);
- }
- for (do_count = OS_DATA_SIZE - st_bp->buffer_bytes;
- i < st_bp->sg_segs && do_count > 0; i++) {
- cnt = st_bp->sg[i].length - offset < do_count ?
- st_bp->sg[i].length - offset : do_count ;
- memset(page_address(sg_page(&st_bp->sg[i])) + offset, 0, cnt);
- do_count -= cnt;
- offset = 0;
- }
- if (do_count) { /* Should never happen */
- printk(KERN_WARNING "osst :A: Zero_buffer overflow (left %d).\n", do_count);
- return (-EIO);
- }
- return 0;
-}
-
-/* Copy a osst 32K chunk of memory into the buffer.
- Returns zero (success) or negative error code. */
-static int osst_copy_to_buffer(struct osst_buffer *st_bp, unsigned char *ptr)
-{
- int i, cnt, do_count = OS_DATA_SIZE;
-
- for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
- cnt = st_bp->sg[i].length < do_count ?
- st_bp->sg[i].length : do_count ;
- memcpy(page_address(sg_page(&st_bp->sg[i])), ptr, cnt);
- do_count -= cnt;
- ptr += cnt;
- }
- if (do_count || i != st_bp->sg_segs-1) { /* Should never happen */
- printk(KERN_WARNING "osst :A: Copy_to_buffer overflow (left %d at sg %d).\n",
- do_count, i);
- return (-EIO);
- }
- return 0;
-}
-
-/* Copy a osst 32K chunk of memory from the buffer.
- Returns zero (success) or negative error code. */
-static int osst_copy_from_buffer(struct osst_buffer *st_bp, unsigned char *ptr)
-{
- int i, cnt, do_count = OS_DATA_SIZE;
-
- for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
- cnt = st_bp->sg[i].length < do_count ?
- st_bp->sg[i].length : do_count ;
- memcpy(ptr, page_address(sg_page(&st_bp->sg[i])), cnt);
- do_count -= cnt;
- ptr += cnt;
- }
- if (do_count || i != st_bp->sg_segs-1) { /* Should never happen */
- printk(KERN_WARNING "osst :A: Copy_from_buffer overflow (left %d at sg %d).\n",
- do_count, i);
- return (-EIO);
- }
- return 0;
-}
-
-
-/* Module housekeeping */
-
-static void validate_options (void)
-{
- if (max_dev > 0)
- osst_max_dev = max_dev;
- if (write_threshold_kbs > 0)
- osst_write_threshold = write_threshold_kbs * ST_KILOBYTE;
- if (osst_write_threshold > osst_buffer_size)
- osst_write_threshold = osst_buffer_size;
- if (max_sg_segs >= OSST_FIRST_SG)
- osst_max_sg_segs = max_sg_segs;
-#if DEBUG
- printk(OSST_DEB_MSG "osst :D: max tapes %d, write threshold %d, max s/g segs %d.\n",
- osst_max_dev, osst_write_threshold, osst_max_sg_segs);
-#endif
-}
-
-#ifndef MODULE
-/* Set the boot options. Syntax: osst=xxx,yyy,...
- where xxx is write threshold in 1024 byte blocks,
- and yyy is number of s/g segments to use. */
-static int __init osst_setup (char *str)
-{
- int i, ints[5];
- char *stp;
-
- stp = get_options(str, ARRAY_SIZE(ints), ints);
-
- if (ints[0] > 0) {
- for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++)
- *parms[i].val = ints[i + 1];
- } else {
- while (stp != NULL) {
- for (i = 0; i < ARRAY_SIZE(parms); i++) {
- int len = strlen(parms[i].name);
- if (!strncmp(stp, parms[i].name, len) &&
- (*(stp + len) == ':' || *(stp + len) == '=')) {
- *parms[i].val =
- simple_strtoul(stp + len + 1, NULL, 0);
- break;
- }
- }
- if (i >= ARRAY_SIZE(parms))
- printk(KERN_INFO "osst :I: Illegal parameter in '%s'\n",
- stp);
- stp = strchr(stp, ',');
- if (stp)
- stp++;
- }
- }
-
- return 1;
-}
-
-__setup("osst=", osst_setup);
-
-#endif
-
-static const struct file_operations osst_fops = {
- .owner = THIS_MODULE,
- .read = osst_read,
- .write = osst_write,
- .unlocked_ioctl = osst_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = osst_compat_ioctl,
-#endif
- .open = os_scsi_tape_open,
- .flush = os_scsi_tape_flush,
- .release = os_scsi_tape_close,
- .llseek = noop_llseek,
-};
-
-static int osst_supports(struct scsi_device * SDp)
-{
- struct osst_support_data {
- char *vendor;
- char *model;
- char *rev;
- char *driver_hint; /* Name of the correct driver, NULL if unknown */
- };
-
-static struct osst_support_data support_list[] = {
- /* {"XXX", "Yy-", "", NULL}, example */
- SIGS_FROM_OSST,
- {NULL, }};
-
- struct osst_support_data *rp;
-
- /* We are willing to drive OnStream SC-x0 as well as the
- * * IDE, ParPort, FireWire, USB variants, if accessible by
- * * emulation layer (ide-scsi, usb-storage, ...) */
-
- for (rp=&(support_list[0]); rp->vendor != NULL; rp++)
- if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
- !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
- !strncmp(rp->rev, SDp->rev, strlen(rp->rev)))
- return 1;
- return 0;
-}
-
-/*
- * sysfs support for osst driver parameter information
- */
-
-static ssize_t osst_version_show(struct device_driver *ddd, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%s\n", osst_version);
-}
-
-static DRIVER_ATTR(version, S_IRUGO, osst_version_show, NULL);
-
-static int osst_create_sysfs_files(struct device_driver *sysfs)
-{
- return driver_create_file(sysfs, &driver_attr_version);
-}
-
-static void osst_remove_sysfs_files(struct device_driver *sysfs)
-{
- driver_remove_file(sysfs, &driver_attr_version);
-}
-
-/*
- * sysfs support for accessing ADR header information
- */
-
-static ssize_t osst_adr_rev_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
- ssize_t l = 0;
-
- if (STp && STp->header_ok && STp->linux_media)
- l = snprintf(buf, PAGE_SIZE, "%d.%d\n", STp->header_cache->major_rev, STp->header_cache->minor_rev);
- return l;
-}
-
-DEVICE_ATTR(ADR_rev, S_IRUGO, osst_adr_rev_show, NULL);
-
-static ssize_t osst_linux_media_version_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
- ssize_t l = 0;
-
- if (STp && STp->header_ok && STp->linux_media)
- l = snprintf(buf, PAGE_SIZE, "LIN%d\n", STp->linux_media_version);
- return l;
-}
-
-DEVICE_ATTR(media_version, S_IRUGO, osst_linux_media_version_show, NULL);
-
-static ssize_t osst_capacity_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
- ssize_t l = 0;
-
- if (STp && STp->header_ok && STp->linux_media)
- l = snprintf(buf, PAGE_SIZE, "%d\n", STp->capacity);
- return l;
-}
-
-DEVICE_ATTR(capacity, S_IRUGO, osst_capacity_show, NULL);
-
-static ssize_t osst_first_data_ppos_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
- ssize_t l = 0;
-
- if (STp && STp->header_ok && STp->linux_media)
- l = snprintf(buf, PAGE_SIZE, "%d\n", STp->first_data_ppos);
- return l;
-}
-
-DEVICE_ATTR(BOT_frame, S_IRUGO, osst_first_data_ppos_show, NULL);
-
-static ssize_t osst_eod_frame_ppos_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
- ssize_t l = 0;
-
- if (STp && STp->header_ok && STp->linux_media)
- l = snprintf(buf, PAGE_SIZE, "%d\n", STp->eod_frame_ppos);
- return l;
-}
-
-DEVICE_ATTR(EOD_frame, S_IRUGO, osst_eod_frame_ppos_show, NULL);
-
-static ssize_t osst_filemark_cnt_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct osst_tape * STp = (struct osst_tape *) dev_get_drvdata (dev);
- ssize_t l = 0;
-
- if (STp && STp->header_ok && STp->linux_media)
- l = snprintf(buf, PAGE_SIZE, "%d\n", STp->filemark_cnt);
- return l;
-}
-
-DEVICE_ATTR(file_count, S_IRUGO, osst_filemark_cnt_show, NULL);
-
-static struct class *osst_sysfs_class;
-
-static int osst_sysfs_init(void)
-{
- osst_sysfs_class = class_create(THIS_MODULE, "onstream_tape");
- if (IS_ERR(osst_sysfs_class)) {
- printk(KERN_ERR "osst :W: Unable to register sysfs class\n");
- return PTR_ERR(osst_sysfs_class);
- }
-
- return 0;
-}
-
-static void osst_sysfs_destroy(dev_t dev)
-{
- device_destroy(osst_sysfs_class, dev);
-}
-
-static int osst_sysfs_add(dev_t dev, struct device *device, struct osst_tape * STp, char * name)
-{
- struct device *osst_member;
- int err;
-
- osst_member = device_create(osst_sysfs_class, device, dev, STp,
- "%s", name);
- if (IS_ERR(osst_member)) {
- printk(KERN_WARNING "osst :W: Unable to add sysfs class member %s\n", name);
- return PTR_ERR(osst_member);
- }
-
- err = device_create_file(osst_member, &dev_attr_ADR_rev);
- if (err)
- goto err_out;
- err = device_create_file(osst_member, &dev_attr_media_version);
- if (err)
- goto err_out;
- err = device_create_file(osst_member, &dev_attr_capacity);
- if (err)
- goto err_out;
- err = device_create_file(osst_member, &dev_attr_BOT_frame);
- if (err)
- goto err_out;
- err = device_create_file(osst_member, &dev_attr_EOD_frame);
- if (err)
- goto err_out;
- err = device_create_file(osst_member, &dev_attr_file_count);
- if (err)
- goto err_out;
-
- return 0;
-
-err_out:
- osst_sysfs_destroy(dev);
- return err;
-}
-
-static void osst_sysfs_cleanup(void)
-{
- class_destroy(osst_sysfs_class);
-}
-
-/*
- * osst startup / cleanup code
- */
-
-static int osst_probe(struct device *dev)
-{
- struct scsi_device * SDp = to_scsi_device(dev);
- struct osst_tape * tpnt;
- struct st_modedef * STm;
- struct st_partstat * STps;
- struct osst_buffer * buffer;
- struct gendisk * drive;
- int i, dev_num, err = -ENODEV;
-
- if (SDp->type != TYPE_TAPE || !osst_supports(SDp))
- return -ENODEV;
-
- drive = alloc_disk(1);
- if (!drive) {
- printk(KERN_ERR "osst :E: Out of memory. Device not attached.\n");
- return -ENODEV;
- }
-
- /* if this is the first attach, build the infrastructure */
- write_lock(&os_scsi_tapes_lock);
- if (os_scsi_tapes == NULL) {
- os_scsi_tapes = kmalloc(osst_max_dev * sizeof(struct osst_tape *), GFP_ATOMIC);
- if (os_scsi_tapes == NULL) {
- write_unlock(&os_scsi_tapes_lock);
- printk(KERN_ERR "osst :E: Unable to allocate array for OnStream SCSI tapes.\n");
- goto out_put_disk;
- }
- for (i=0; i < osst_max_dev; ++i) os_scsi_tapes[i] = NULL;
- }
-
- if (osst_nr_dev >= osst_max_dev) {
- write_unlock(&os_scsi_tapes_lock);
- printk(KERN_ERR "osst :E: Too many tape devices (max. %d).\n", osst_max_dev);
- goto out_put_disk;
- }
-
- /* find a free minor number */
- for (i = 0; i < osst_max_dev && os_scsi_tapes[i]; i++)
- ;
- if(i >= osst_max_dev) panic ("Scsi_devices corrupt (osst)");
- dev_num = i;
-
- /* allocate a struct osst_tape for this device */
- tpnt = kzalloc(sizeof(struct osst_tape), GFP_ATOMIC);
- if (!tpnt) {
- write_unlock(&os_scsi_tapes_lock);
- printk(KERN_ERR "osst :E: Can't allocate device descriptor, device not attached.\n");
- goto out_put_disk;
- }
-
- /* allocate a buffer for this device */
- i = SDp->host->sg_tablesize;
- if (osst_max_sg_segs < i)
- i = osst_max_sg_segs;
- buffer = new_tape_buffer(1, SDp->host->unchecked_isa_dma, i);
- if (buffer == NULL) {
- write_unlock(&os_scsi_tapes_lock);
- printk(KERN_ERR "osst :E: Unable to allocate a tape buffer, device not attached.\n");
- kfree(tpnt);
- goto out_put_disk;
- }
- os_scsi_tapes[dev_num] = tpnt;
- tpnt->buffer = buffer;
- tpnt->device = SDp;
- drive->private_data = &tpnt->driver;
- sprintf(drive->disk_name, "osst%d", dev_num);
- tpnt->driver = &osst_template;
- tpnt->drive = drive;
- tpnt->in_use = 0;
- tpnt->capacity = 0xfffff;
- tpnt->dirty = 0;
- tpnt->drv_buffer = 1; /* Try buffering if no mode sense */
- tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
- tpnt->density = 0;
- tpnt->do_auto_lock = OSST_AUTO_LOCK;
- tpnt->can_bsr = OSST_IN_FILE_POS;
- tpnt->can_partitions = 0;
- tpnt->two_fm = OSST_TWO_FM;
- tpnt->fast_mteom = OSST_FAST_MTEOM;
- tpnt->scsi2_logical = OSST_SCSI2LOGICAL; /* FIXME */
- tpnt->write_threshold = osst_write_threshold;
- tpnt->default_drvbuffer = 0xff; /* No forced buffering */
- tpnt->partition = 0;
- tpnt->new_partition = 0;
- tpnt->nbr_partitions = 0;
- tpnt->min_block = 512;
- tpnt->max_block = OS_DATA_SIZE;
- tpnt->timeout = OSST_TIMEOUT;
- tpnt->long_timeout = OSST_LONG_TIMEOUT;
-
- /* Recognize OnStream tapes */
- /* We don't need to test for OnStream, as this has been done in detect () */
- tpnt->os_fw_rev = osst_parse_firmware_rev (SDp->rev);
- tpnt->omit_blklims = 1;
-
- tpnt->poll = (strncmp(SDp->model, "DI-", 3) == 0) ||
- (strncmp(SDp->model, "FW-", 3) == 0) || OSST_FW_NEED_POLL(tpnt->os_fw_rev,SDp);
- tpnt->frame_in_buffer = 0;
- tpnt->header_ok = 0;
- tpnt->linux_media = 0;
- tpnt->header_cache = NULL;
-
- for (i=0; i < ST_NBR_MODES; i++) {
- STm = &(tpnt->modes[i]);
- STm->defined = 0;
- STm->sysv = OSST_SYSV;
- STm->defaults_for_writes = 0;
- STm->do_async_writes = OSST_ASYNC_WRITES;
- STm->do_buffer_writes = OSST_BUFFER_WRITES;
- STm->do_read_ahead = OSST_READ_AHEAD;
- STm->default_compression = ST_DONT_TOUCH;
- STm->default_blksize = 512;
- STm->default_density = (-1); /* No forced density */
- }
-
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STps = &(tpnt->ps[i]);
- STps->rw = ST_IDLE;
- STps->eof = ST_NOEOF;
- STps->at_sm = 0;
- STps->last_block_valid = 0;
- STps->drv_block = (-1);
- STps->drv_file = (-1);
- }
-
- tpnt->current_mode = 0;
- tpnt->modes[0].defined = 1;
- tpnt->modes[2].defined = 1;
- tpnt->density_changed = tpnt->compression_changed = tpnt->blksize_changed = 0;
-
- mutex_init(&tpnt->lock);
- osst_nr_dev++;
- write_unlock(&os_scsi_tapes_lock);
-
- {
- char name[8];
-
- /* Rewind entry */
- err = osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num), dev, tpnt, tape_name(tpnt));
- if (err)
- goto out_free_buffer;
-
- /* No-rewind entry */
- snprintf(name, 8, "%s%s", "n", tape_name(tpnt));
- err = osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num + 128), dev, tpnt, name);
- if (err)
- goto out_free_sysfs1;
- }
-
- sdev_printk(KERN_INFO, SDp,
- "osst :I: Attached OnStream %.5s tape as %s\n",
- SDp->model, tape_name(tpnt));
-
- return 0;
-
-out_free_sysfs1:
- osst_sysfs_destroy(MKDEV(OSST_MAJOR, dev_num));
-out_free_buffer:
- kfree(buffer);
-out_put_disk:
- put_disk(drive);
- return err;
-};
-
-static int osst_remove(struct device *dev)
-{
- struct scsi_device * SDp = to_scsi_device(dev);
- struct osst_tape * tpnt;
- int i;
-
- if ((SDp->type != TYPE_TAPE) || (osst_nr_dev <= 0))
- return 0;
-
- write_lock(&os_scsi_tapes_lock);
- for(i=0; i < osst_max_dev; i++) {
- if((tpnt = os_scsi_tapes[i]) && (tpnt->device == SDp)) {
- osst_sysfs_destroy(MKDEV(OSST_MAJOR, i));
- osst_sysfs_destroy(MKDEV(OSST_MAJOR, i+128));
- tpnt->device = NULL;
- put_disk(tpnt->drive);
- os_scsi_tapes[i] = NULL;
- osst_nr_dev--;
- write_unlock(&os_scsi_tapes_lock);
- vfree(tpnt->header_cache);
- if (tpnt->buffer) {
- normalize_buffer(tpnt->buffer);
- kfree(tpnt->buffer);
- }
- kfree(tpnt);
- return 0;
- }
- }
- write_unlock(&os_scsi_tapes_lock);
- return 0;
-}
-
-static int __init init_osst(void)
-{
- int err;
-
- printk(KERN_INFO "osst :I: Tape driver with OnStream support version %s\nosst :I: %s\n", osst_version, cvsid);
-
- validate_options();
-
- err = osst_sysfs_init();
- if (err)
- return err;
-
- err = register_chrdev(OSST_MAJOR, "osst", &osst_fops);
- if (err < 0) {
- printk(KERN_ERR "osst :E: Unable to register major %d for OnStream tapes\n", OSST_MAJOR);
- goto err_out;
- }
-
- err = scsi_register_driver(&osst_template.gendrv);
- if (err)
- goto err_out_chrdev;
-
- err = osst_create_sysfs_files(&osst_template.gendrv);
- if (err)
- goto err_out_scsidrv;
-
- return 0;
-
-err_out_scsidrv:
- scsi_unregister_driver(&osst_template.gendrv);
-err_out_chrdev:
- unregister_chrdev(OSST_MAJOR, "osst");
-err_out:
- osst_sysfs_cleanup();
- return err;
-}
-
-static void __exit exit_osst (void)
-{
- int i;
- struct osst_tape * STp;
-
- osst_remove_sysfs_files(&osst_template.gendrv);
- scsi_unregister_driver(&osst_template.gendrv);
- unregister_chrdev(OSST_MAJOR, "osst");
- osst_sysfs_cleanup();
-
- if (os_scsi_tapes) {
- for (i=0; i < osst_max_dev; ++i) {
- if (!(STp = os_scsi_tapes[i])) continue;
- /* This is defensive, supposed to happen during detach */
- vfree(STp->header_cache);
- if (STp->buffer) {
- normalize_buffer(STp->buffer);
- kfree(STp->buffer);
- }
- put_disk(STp->drive);
- kfree(STp);
- }
- kfree(os_scsi_tapes);
- }
- printk(KERN_INFO "osst :I: Unloaded.\n");
-}
-
-module_init(init_osst);
-module_exit(exit_osst);
diff --git a/drivers/scsi/osst.h b/drivers/scsi/osst.h
deleted file mode 100644
index b4fea98ba276..000000000000
--- a/drivers/scsi/osst.h
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * $Header: /cvsroot/osst/Driver/osst.h,v 1.16 2005/01/01 21:13:35 wriede Exp $
- */
-
-#include <asm/byteorder.h>
-#include <linux/completion.h>
-#include <linux/mutex.h>
-
-/* FIXME - rename and use the following two types or delete them!
- * and the types really should go to st.h anyway...
- * INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
- */
-typedef struct {
- unsigned device_type :5; /* Peripheral Device Type */
- unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
- unsigned reserved1_6t0 :7; /* Reserved */
- unsigned rmb :1; /* Removable Medium Bit */
- unsigned ansi_version :3; /* ANSI Version */
- unsigned ecma_version :3; /* ECMA Version */
- unsigned iso_version :2; /* ISO Version */
- unsigned response_format :4; /* Response Data Format */
- unsigned reserved3_45 :2; /* Reserved */
- unsigned reserved3_6 :1; /* TrmIOP - Reserved */
- unsigned reserved3_7 :1; /* AENC - Reserved */
- u8 additional_length; /* Additional Length (total_length-4) */
- u8 rsv5, rsv6, rsv7; /* Reserved */
- u8 vendor_id[8]; /* Vendor Identification */
- u8 product_id[16]; /* Product Identification */
- u8 revision_level[4]; /* Revision Level */
- u8 vendor_specific[20]; /* Vendor Specific - Optional */
- u8 reserved56t95[40]; /* Reserved - Optional */
- /* Additional information may be returned */
-} idetape_inquiry_result_t;
-
-/*
- * READ POSITION packet command - Data Format (From Table 6-57)
- */
-typedef struct {
- unsigned reserved0_10 :2; /* Reserved */
- unsigned bpu :1; /* Block Position Unknown */
- unsigned reserved0_543 :3; /* Reserved */
- unsigned eop :1; /* End Of Partition */
- unsigned bop :1; /* Beginning Of Partition */
- u8 partition; /* Partition Number */
- u8 reserved2, reserved3; /* Reserved */
- u32 first_block; /* First Block Location */
- u32 last_block; /* Last Block Location (Optional) */
- u8 reserved12; /* Reserved */
- u8 blocks_in_buffer[3]; /* Blocks In Buffer - (Optional) */
- u32 bytes_in_buffer; /* Bytes In Buffer (Optional) */
-} idetape_read_position_result_t;
-
-/*
- * Follows structures which are related to the SELECT SENSE / MODE SENSE
- * packet commands.
- */
-#define COMPRESSION_PAGE 0x0f
-#define COMPRESSION_PAGE_LENGTH 16
-
-#define CAPABILITIES_PAGE 0x2a
-#define CAPABILITIES_PAGE_LENGTH 20
-
-#define TAPE_PARAMTR_PAGE 0x2b
-#define TAPE_PARAMTR_PAGE_LENGTH 16
-
-#define NUMBER_RETRIES_PAGE 0x2f
-#define NUMBER_RETRIES_PAGE_LENGTH 4
-
-#define BLOCK_SIZE_PAGE 0x30
-#define BLOCK_SIZE_PAGE_LENGTH 4
-
-#define BUFFER_FILLING_PAGE 0x33
-#define BUFFER_FILLING_PAGE_LENGTH 4
-
-#define VENDOR_IDENT_PAGE 0x36
-#define VENDOR_IDENT_PAGE_LENGTH 8
-
-#define LOCATE_STATUS_PAGE 0x37
-#define LOCATE_STATUS_PAGE_LENGTH 0
-
-#define MODE_HEADER_LENGTH 4
-
-
-/*
- * REQUEST SENSE packet command result - Data Format.
- */
-typedef struct {
- unsigned error_code :7; /* Current of deferred errors */
- unsigned valid :1; /* The information field conforms to QIC-157C */
- u8 reserved1 :8; /* Segment Number - Reserved */
- unsigned sense_key :4; /* Sense Key */
- unsigned reserved2_4 :1; /* Reserved */
- unsigned ili :1; /* Incorrect Length Indicator */
- unsigned eom :1; /* End Of Medium */
- unsigned filemark :1; /* Filemark */
- u32 information __attribute__ ((packed));
- u8 asl; /* Additional sense length (n-7) */
- u32 command_specific; /* Additional command specific information */
- u8 asc; /* Additional Sense Code */
- u8 ascq; /* Additional Sense Code Qualifier */
- u8 replaceable_unit_code; /* Field Replaceable Unit Code */
- unsigned sk_specific1 :7; /* Sense Key Specific */
- unsigned sksv :1; /* Sense Key Specific information is valid */
- u8 sk_specific2; /* Sense Key Specific */
- u8 sk_specific3; /* Sense Key Specific */
- u8 pad[2]; /* Padding to 20 bytes */
-} idetape_request_sense_result_t;
-
-/*
- * Mode Parameter Header for the MODE SENSE packet command
- */
-typedef struct {
- u8 mode_data_length; /* Length of the following data transfer */
- u8 medium_type; /* Medium Type */
- u8 dsp; /* Device Specific Parameter */
- u8 bdl; /* Block Descriptor Length */
-} osst_mode_parameter_header_t;
-
-/*
- * Mode Parameter Block Descriptor the MODE SENSE packet command
- *
- * Support for block descriptors is optional.
- */
-typedef struct {
- u8 density_code; /* Medium density code */
- u8 blocks[3]; /* Number of blocks */
- u8 reserved4; /* Reserved */
- u8 length[3]; /* Block Length */
-} osst_parameter_block_descriptor_t;
-
-/*
- * The Data Compression Page, as returned by the MODE SENSE packet command.
- */
-typedef struct {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned ps :1;
- unsigned reserved0 :1; /* Reserved */
- unsigned page_code :6; /* Page Code - Should be 0xf */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned page_code :6; /* Page Code - Should be 0xf */
- unsigned reserved0 :1; /* Reserved */
- unsigned ps :1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- u8 page_length; /* Page Length - Should be 14 */
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned dce :1; /* Data Compression Enable */
- unsigned dcc :1; /* Data Compression Capable */
- unsigned reserved2 :6; /* Reserved */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned reserved2 :6; /* Reserved */
- unsigned dcc :1; /* Data Compression Capable */
- unsigned dce :1; /* Data Compression Enable */
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned dde :1; /* Data Decompression Enable */
- unsigned red :2; /* Report Exception on Decompression */
- unsigned reserved3 :5; /* Reserved */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned reserved3 :5; /* Reserved */
- unsigned red :2; /* Report Exception on Decompression */
- unsigned dde :1; /* Data Decompression Enable */
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- u32 ca; /* Compression Algorithm */
- u32 da; /* Decompression Algorithm */
- u8 reserved[4]; /* Reserved */
-} osst_data_compression_page_t;
-
-/*
- * The Medium Partition Page, as returned by the MODE SENSE packet command.
- */
-typedef struct {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned ps :1;
- unsigned reserved1_6 :1; /* Reserved */
- unsigned page_code :6; /* Page Code - Should be 0x11 */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned page_code :6; /* Page Code - Should be 0x11 */
- unsigned reserved1_6 :1; /* Reserved */
- unsigned ps :1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- u8 page_length; /* Page Length - Should be 6 */
- u8 map; /* Maximum Additional Partitions - Should be 0 */
- u8 apd; /* Additional Partitions Defined - Should be 0 */
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned fdp :1; /* Fixed Data Partitions */
- unsigned sdp :1; /* Should be 0 */
- unsigned idp :1; /* Should be 0 */
- unsigned psum :2; /* Should be 0 */
- unsigned reserved4_012 :3; /* Reserved */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned reserved4_012 :3; /* Reserved */
- unsigned psum :2; /* Should be 0 */
- unsigned idp :1; /* Should be 0 */
- unsigned sdp :1; /* Should be 0 */
- unsigned fdp :1; /* Fixed Data Partitions */
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- u8 mfr; /* Medium Format Recognition */
- u8 reserved[2]; /* Reserved */
-} osst_medium_partition_page_t;
-
-/*
- * Capabilities and Mechanical Status Page
- */
-typedef struct {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned reserved1_67 :2;
- unsigned page_code :6; /* Page code - Should be 0x2a */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned page_code :6; /* Page code - Should be 0x2a */
- unsigned reserved1_67 :2;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- u8 page_length; /* Page Length - Should be 0x12 */
- u8 reserved2, reserved3;
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned reserved4_67 :2;
- unsigned sprev :1; /* Supports SPACE in the reverse direction */
- unsigned reserved4_1234 :4;
- unsigned ro :1; /* Read Only Mode */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned ro :1; /* Read Only Mode */
- unsigned reserved4_1234 :4;
- unsigned sprev :1; /* Supports SPACE in the reverse direction */
- unsigned reserved4_67 :2;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned reserved5_67 :2;
- unsigned qfa :1; /* Supports the QFA two partition formats */
- unsigned reserved5_4 :1;
- unsigned efmt :1; /* Supports ERASE command initiated formatting */
- unsigned reserved5_012 :3;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned reserved5_012 :3;
- unsigned efmt :1; /* Supports ERASE command initiated formatting */
- unsigned reserved5_4 :1;
- unsigned qfa :1; /* Supports the QFA two partition formats */
- unsigned reserved5_67 :2;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned cmprs :1; /* Supports data compression */
- unsigned ecc :1; /* Supports error correction */
- unsigned reserved6_45 :2; /* Reserved */
- unsigned eject :1; /* The device can eject the volume */
- unsigned prevent :1; /* The device defaults in the prevent state after power up */
- unsigned locked :1; /* The volume is locked */
- unsigned lock :1; /* Supports locking the volume */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned lock :1; /* Supports locking the volume */
- unsigned locked :1; /* The volume is locked */
- unsigned prevent :1; /* The device defaults in the prevent state after power up */
- unsigned eject :1; /* The device can eject the volume */
- unsigned reserved6_45 :2; /* Reserved */
- unsigned ecc :1; /* Supports error correction */
- unsigned cmprs :1; /* Supports data compression */
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */
- /* transfers for slow buffer memory ??? */
- /* Also 32768 block size in some cases */
- unsigned reserved7_3_6 :4;
- unsigned blk1024 :1; /* Supports 1024 bytes block size */
- unsigned blk512 :1; /* Supports 512 bytes block size */
- unsigned reserved7_0 :1;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned reserved7_0 :1;
- unsigned blk512 :1; /* Supports 512 bytes block size */
- unsigned blk1024 :1; /* Supports 1024 bytes block size */
- unsigned reserved7_3_6 :4;
- unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */
- /* transfers for slow buffer memory ??? */
- /* Also 32768 block size in some cases */
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- __be16 max_speed; /* Maximum speed supported in KBps */
- u8 reserved10, reserved11;
- __be16 ctl; /* Continuous Transfer Limit in blocks */
- __be16 speed; /* Current Speed, in KBps */
- __be16 buffer_size; /* Buffer Size, in 512 bytes */
- u8 reserved18, reserved19;
-} osst_capabilities_page_t;
-
-/*
- * Block Size Page
- */
-typedef struct {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned ps :1;
- unsigned reserved1_6 :1;
- unsigned page_code :6; /* Page code - Should be 0x30 */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned page_code :6; /* Page code - Should be 0x30 */
- unsigned reserved1_6 :1;
- unsigned ps :1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- u8 page_length; /* Page Length - Should be 2 */
- u8 reserved2;
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned one :1;
- unsigned reserved2_6 :1;
- unsigned record32_5 :1;
- unsigned record32 :1;
- unsigned reserved2_23 :2;
- unsigned play32_5 :1;
- unsigned play32 :1;
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned play32 :1;
- unsigned play32_5 :1;
- unsigned reserved2_23 :2;
- unsigned record32 :1;
- unsigned record32_5 :1;
- unsigned reserved2_6 :1;
- unsigned one :1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
-} osst_block_size_page_t;
-
-/*
- * Tape Parameters Page
- */
-typedef struct {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unsigned ps :1;
- unsigned reserved1_6 :1;
- unsigned page_code :6; /* Page code - Should be 0x2b */
-#elif defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned page_code :6; /* Page code - Should be 0x2b */
- unsigned reserved1_6 :1;
- unsigned ps :1;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- u8 reserved2;
- u8 density;
- u8 reserved3,reserved4;
- __be16 segtrk;
- __be16 trks;
- u8 reserved5,reserved6,reserved7,reserved8,reserved9,reserved10;
-} osst_tape_paramtr_page_t;
-
-/* OnStream definitions */
-
-#define OS_CONFIG_PARTITION (0xff)
-#define OS_DATA_PARTITION (0)
-#define OS_PARTITION_VERSION (1)
-
-/*
- * partition
- */
-typedef struct os_partition_s {
- __u8 partition_num;
- __u8 par_desc_ver;
- __be16 wrt_pass_cntr;
- __be32 first_frame_ppos;
- __be32 last_frame_ppos;
- __be32 eod_frame_ppos;
-} os_partition_t;
-
-/*
- * DAT entry
- */
-typedef struct os_dat_entry_s {
- __be32 blk_sz;
- __be16 blk_cnt;
- __u8 flags;
- __u8 reserved;
-} os_dat_entry_t;
-
-/*
- * DAT
- */
-#define OS_DAT_FLAGS_DATA (0xc)
-#define OS_DAT_FLAGS_MARK (0x1)
-
-typedef struct os_dat_s {
- __u8 dat_sz;
- __u8 reserved1;
- __u8 entry_cnt;
- __u8 reserved3;
- os_dat_entry_t dat_list[16];
-} os_dat_t;
-
-/*
- * Frame types
- */
-#define OS_FRAME_TYPE_FILL (0)
-#define OS_FRAME_TYPE_EOD (1 << 0)
-#define OS_FRAME_TYPE_MARKER (1 << 1)
-#define OS_FRAME_TYPE_HEADER (1 << 3)
-#define OS_FRAME_TYPE_DATA (1 << 7)
-
-/*
- * AUX
- */
-typedef struct os_aux_s {
- __be32 format_id; /* hardware compatibility AUX is based on */
- char application_sig[4]; /* driver used to write this media */
- __be32 hdwr; /* reserved */
- __be32 update_frame_cntr; /* for configuration frame */
- __u8 frame_type;
- __u8 frame_type_reserved;
- __u8 reserved_18_19[2];
- os_partition_t partition;
- __u8 reserved_36_43[8];
- __be32 frame_seq_num;
- __be32 logical_blk_num_high;
- __be32 logical_blk_num;
- os_dat_t dat;
- __u8 reserved188_191[4];
- __be32 filemark_cnt;
- __be32 phys_fm;
- __be32 last_mark_ppos;
- __u8 reserved204_223[20];
-
- /*
- * __u8 app_specific[32];
- *
- * Linux specific fields:
- */
- __be32 next_mark_ppos; /* when known, points to next marker */
- __be32 last_mark_lbn; /* storing log_blk_num of last mark is extends ADR spec */
- __u8 linux_specific[24];
-
- __u8 reserved_256_511[256];
-} os_aux_t;
-
-#define OS_FM_TAB_MAX 1024
-
-typedef struct os_fm_tab_s {
- __u8 fm_part_num;
- __u8 reserved_1;
- __u8 fm_tab_ent_sz;
- __u8 reserved_3;
- __be16 fm_tab_ent_cnt;
- __u8 reserved6_15[10];
- __be32 fm_tab_ent[OS_FM_TAB_MAX];
-} os_fm_tab_t;
-
-typedef struct os_ext_trk_ey_s {
- __u8 et_part_num;
- __u8 fmt;
- __be16 fm_tab_off;
- __u8 reserved4_7[4];
- __be32 last_hlb_hi;
- __be32 last_hlb;
- __be32 last_pp;
- __u8 reserved20_31[12];
-} os_ext_trk_ey_t;
-
-typedef struct os_ext_trk_tb_s {
- __u8 nr_stream_part;
- __u8 reserved_1;
- __u8 et_ent_sz;
- __u8 reserved3_15[13];
- os_ext_trk_ey_t dat_ext_trk_ey;
- os_ext_trk_ey_t qfa_ext_trk_ey;
-} os_ext_trk_tb_t;
-
-typedef struct os_header_s {
- char ident_str[8];
- __u8 major_rev;
- __u8 minor_rev;
- __be16 ext_trk_tb_off;
- __u8 reserved12_15[4];
- __u8 pt_par_num;
- __u8 pt_reserved1_3[3];
- os_partition_t partition[16];
- __be32 cfg_col_width;
- __be32 dat_col_width;
- __be32 qfa_col_width;
- __u8 cartridge[16];
- __u8 reserved304_511[208];
- __be32 old_filemark_list[16680/4]; /* in ADR 1.4 __u8 track_table[16680] */
- os_ext_trk_tb_t ext_track_tb;
- __u8 reserved17272_17735[464];
- os_fm_tab_t dat_fm_tab;
- os_fm_tab_t qfa_fm_tab;
- __u8 reserved25960_32767[6808];
-} os_header_t;
-
-
-/*
- * OnStream ADRL frame
- */
-#define OS_FRAME_SIZE (32 * 1024 + 512)
-#define OS_DATA_SIZE (32 * 1024)
-#define OS_AUX_SIZE (512)
-//#define OSST_MAX_SG 2
-
-/* The OnStream tape buffer descriptor. */
-struct osst_buffer {
- unsigned char in_use;
- unsigned char dma; /* DMA-able buffer */
- int buffer_size;
- int buffer_blocks;
- int buffer_bytes;
- int read_pointer;
- int writing;
- int midlevel_result;
- int syscall_result;
- struct osst_request *last_SRpnt;
- struct st_cmdstatus cmdstat;
- struct rq_map_data map_data;
- unsigned char *b_data;
- os_aux_t *aux; /* onstream AUX structure at end of each block */
- unsigned short use_sg; /* zero or number of s/g segments for this adapter */
- unsigned short sg_segs; /* number of segments in s/g list */
- unsigned short orig_sg_segs; /* number of segments allocated at first try */
- struct scatterlist sg[1]; /* MUST BE last item */
-} ;
-
-/* The OnStream tape drive descriptor */
-struct osst_tape {
- struct scsi_driver *driver;
- unsigned capacity;
- struct scsi_device *device;
- struct mutex lock; /* for serialization */
- struct completion wait; /* for SCSI commands */
- struct osst_buffer * buffer;
-
- /* Drive characteristics */
- unsigned char omit_blklims;
- unsigned char do_auto_lock;
- unsigned char can_bsr;
- unsigned char can_partitions;
- unsigned char two_fm;
- unsigned char fast_mteom;
- unsigned char restr_dma;
- unsigned char scsi2_logical;
- unsigned char default_drvbuffer; /* 0xff = don't touch, value 3 bits */
- unsigned char pos_unknown; /* after reset position unknown */
- int write_threshold;
- int timeout; /* timeout for normal commands */
- int long_timeout; /* timeout for commands known to take long time*/
-
- /* Mode characteristics */
- struct st_modedef modes[ST_NBR_MODES];
- int current_mode;
-
- /* Status variables */
- int partition;
- int new_partition;
- int nbr_partitions; /* zero until partition support enabled */
- struct st_partstat ps[ST_NBR_PARTITIONS];
- unsigned char dirty;
- unsigned char ready;
- unsigned char write_prot;
- unsigned char drv_write_prot;
- unsigned char in_use;
- unsigned char blksize_changed;
- unsigned char density_changed;
- unsigned char compression_changed;
- unsigned char drv_buffer;
- unsigned char density;
- unsigned char door_locked;
- unsigned char rew_at_close;
- unsigned char inited;
- int block_size;
- int min_block;
- int max_block;
- int recover_count; /* from tape opening */
- int abort_count;
- int write_count;
- int read_count;
- int recover_erreg; /* from last status call */
- /*
- * OnStream specific data
- */
- int os_fw_rev; /* the firmware revision * 10000 */
- unsigned char raw; /* flag OnStream raw access (32.5KB block size) */
- unsigned char poll; /* flag that this drive needs polling (IDE|firmware) */
- unsigned char frame_in_buffer; /* flag that the frame as per frame_seq_number
- * has been read into STp->buffer and is valid */
- int frame_seq_number; /* logical frame number */
- int logical_blk_num; /* logical block number */
- unsigned first_frame_position; /* physical frame to be transferred to/from host */
- unsigned last_frame_position; /* physical frame to be transferd to/from tape */
- int cur_frames; /* current number of frames in internal buffer */
- int max_frames; /* max number of frames in internal buffer */
- char application_sig[5]; /* application signature */
- unsigned char fast_open; /* flag that reminds us we didn't check headers at open */
- unsigned short wrt_pass_cntr; /* write pass counter */
- int update_frame_cntr; /* update frame counter */
- int onstream_write_error; /* write error recovery active */
- int header_ok; /* header frame verified ok */
- int linux_media; /* reading linux-specifc media */
- int linux_media_version;
- os_header_t * header_cache; /* cache is kept for filemark positions */
- int filemark_cnt;
- int first_mark_ppos;
- int last_mark_ppos;
- int last_mark_lbn; /* storing log_blk_num of last mark is extends ADR spec */
- int first_data_ppos;
- int eod_frame_ppos;
- int eod_frame_lfa;
- int write_type; /* used in write error recovery */
- int read_error_frame; /* used in read error recovery */
- unsigned long cmd_start_time;
- unsigned long max_cmd_time;
-
-#if DEBUG
- unsigned char write_pending;
- int nbr_finished;
- int nbr_waits;
- unsigned char last_cmnd[6];
- unsigned char last_sense[16];
-#endif
- struct gendisk *drive;
-} ;
-
-/* scsi tape command */
-struct osst_request {
- unsigned char cmd[MAX_COMMAND_SIZE];
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
- int result;
- struct osst_tape *stp;
- struct completion *waiting;
- struct bio *bio;
-};
-
-/* Values of write_type */
-#define OS_WRITE_DATA 0
-#define OS_WRITE_EOD 1
-#define OS_WRITE_NEW_MARK 2
-#define OS_WRITE_LAST_MARK 3
-#define OS_WRITE_HEADER 4
-#define OS_WRITE_FILLER 5
-
-/* Additional rw state */
-#define OS_WRITING_COMPLETE 3
diff --git a/drivers/scsi/osst_detect.h b/drivers/scsi/osst_detect.h
deleted file mode 100644
index 21717d0e6974..000000000000
--- a/drivers/scsi/osst_detect.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SIGS_FROM_OSST \
- {"OnStream", "SC-", "", "osst"}, \
- {"OnStream", "DI-", "", "osst"}, \
- {"OnStream", "DP-", "", "osst"}, \
- {"OnStream", "FW-", "", "osst"}, \
- {"OnStream", "USB", "", "osst"}
diff --git a/drivers/scsi/osst_options.h b/drivers/scsi/osst_options.h
deleted file mode 100644
index ff1e610946ed..000000000000
--- a/drivers/scsi/osst_options.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- The compile-time configurable defaults for the Linux SCSI tape driver.
-
- Copyright 1995 Kai Makisara.
-
- Last modified: Wed Sep 2 21:24:07 1998 by root@home
-
- Changed (and renamed) for OnStream SCSI drives garloff@suse.de
- 2000-06-21
-
- $Header: /cvsroot/osst/Driver/osst_options.h,v 1.6 2003/12/23 14:22:12 wriede Exp $
-*/
-
-#ifndef _OSST_OPTIONS_H
-#define _OSST_OPTIONS_H
-
-/* The minimum limit for the number of SCSI tape devices is determined by
- OSST_MAX_TAPES. If the number of tape devices and the "slack" defined by
- OSST_EXTRA_DEVS exceeds OSST_MAX_TAPES, the large number is used. */
-#define OSST_MAX_TAPES 4
-
-/* If OSST_IN_FILE_POS is nonzero, the driver positions the tape after the
- record been read by the user program even if the tape has moved further
- because of buffered reads. Should be set to zero to support also drives
- that can't space backwards over records. NOTE: The tape will be
- spaced backwards over an "accidentally" crossed filemark in any case. */
-#define OSST_IN_FILE_POS 1
-
-/* The tape driver buffer size in kilobytes. */
-/* Don't change, as this is the HW blocksize */
-#define OSST_BUFFER_BLOCKS 32
-
-/* The number of kilobytes of data in the buffer that triggers an
- asynchronous write in fixed block mode. See also OSST_ASYNC_WRITES
- below. */
-#define OSST_WRITE_THRESHOLD_BLOCKS 32
-
-/* OSST_EOM_RESERVE defines the number of frames are kept in reserve for
- * * write error recovery when writing near end of medium. ENOSPC is returned
- * * when write() is called and the tape write position is within this number
- * * of blocks from the tape capacity. */
-#define OSST_EOM_RESERVE 300
-
-/* The maximum number of tape buffers the driver allocates. The number
- is also constrained by the number of drives detected. Determines the
- maximum number of concurrently active tape drives. */
-#define OSST_MAX_BUFFERS OSST_MAX_TAPES
-
-/* Maximum number of scatter/gather segments */
-/* Fit one buffer in pages and add one for the AUX header */
-#define OSST_MAX_SG (((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + 1)
-
-/* The number of scatter/gather segments to allocate at first try (must be
- smaller or equal to the maximum). */
-#define OSST_FIRST_SG ((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE)
-
-/* The size of the first scatter/gather segments (determines the maximum block
- size for SCSI adapters not supporting scatter/gather). The default is set
- to try to allocate the buffer as one chunk. */
-#define OSST_FIRST_ORDER (15-PAGE_SHIFT)
-
-
-/* The following lines define defaults for properties that can be set
- separately for each drive using the MTSTOPTIONS ioctl. */
-
-/* If OSST_TWO_FM is non-zero, the driver writes two filemarks after a
- file being written. Some drives can't handle two filemarks at the
- end of data. */
-#define OSST_TWO_FM 0
-
-/* If OSST_BUFFER_WRITES is non-zero, writes in fixed block mode are
- buffered until the driver buffer is full or asynchronous write is
- triggered. */
-#define OSST_BUFFER_WRITES 1
-
-/* If OSST_ASYNC_WRITES is non-zero, the SCSI write command may be started
- without waiting for it to finish. May cause problems in multiple
- tape backups. */
-#define OSST_ASYNC_WRITES 1
-
-/* If OSST_READ_AHEAD is non-zero, blocks are read ahead in fixed block
- mode. */
-#define OSST_READ_AHEAD 1
-
-/* If OSST_AUTO_LOCK is non-zero, the drive door is locked at the first
- read or write command after the device is opened. The door is opened
- when the device is closed. */
-#define OSST_AUTO_LOCK 0
-
-/* If OSST_FAST_MTEOM is non-zero, the MTEOM ioctl is done using the
- direct SCSI command. The file number status is lost but this method
- is fast with some drives. Otherwise MTEOM is done by spacing over
- files and the file number status is retained. */
-#define OSST_FAST_MTEOM 0
-
-/* If OSST_SCSI2LOGICAL is nonzero, the logical block addresses are used for
- MTIOCPOS and MTSEEK by default. Vendor addresses are used if OSST_SCSI2LOGICAL
- is zero. */
-#define OSST_SCSI2LOGICAL 0
-
-/* If OSST_SYSV is non-zero, the tape behaves according to the SYS V semantics.
- The default is BSD semantics. */
-#define OSST_SYSV 0
-
-
-#endif
diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c
deleted file mode 100644
index f2018b46f494..000000000000
--- a/drivers/scsi/pas16.c
+++ /dev/null
@@ -1,638 +0,0 @@
-#define AUTOSENSE
-#define PSEUDO_DMA
-#define FOO
-#define UNSAFE /* Not unsafe for PAS16 -- use it */
-#define PDEBUG 0
-
-/*
- * This driver adapted from Drew Eckhardt's Trantor T128 driver
- *
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 666-5836
- *
- * ( Based on T128 - DISTRIBUTION RELEASE 3. )
- *
- * Modified to work with the Pro Audio Spectrum/Studio 16
- * by John Weidman.
- *
- *
- * For more information, please consult
- *
- * Media Vision
- * (510) 770-8600
- * (800) 348-7116
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * Options :
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
- * bytes at a time. Since interrupts are disabled by default during
- * these transfers, we might need this to give reasonable interrupt
- * service time if the transfer size gets too large.
- *
- * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
- * increase compared to polled I/O.
- *
- * PARITY - enable parity checking. Not supported.
- *
- * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- *
- * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This
- * parameter comes from the NCR5380 code. It is NOT unsafe with
- * the PAS16 and you should use it. If you don't you will have
- * a problem with dropped characters during high speed
- * communications during SCSI transfers. If you really don't
- * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or
- * twiddle with the transfer size in the high level code.
- *
- * USLEEP - enable support for devices that don't disconnect. Untested.
- *
- * The card is detected and initialized in one of several ways :
- * 1. Autoprobe (default) - There are many different models of
- * the Pro Audio Spectrum/Studio 16, and I only have one of
- * them, so this may require a little tweaking. An interrupt
- * is triggered to autoprobe for the interrupt line. Note:
- * with the newer model boards, the interrupt is set via
- * software after reset using the default_irq for the
- * current board number.
- *
- * 2. With command line overrides - pas16=port,irq may be
- * used on the LILO command line to override the defaults.
- *
- * 3. With the PAS16_OVERRIDE compile time define. This is
- * specified as an array of address, irq tuples. Ie, for
- * one board at the default 0x388 address, IRQ10, I could say
- * -DPAS16_OVERRIDE={{0x388, 10}}
- * NOTE: Untested.
- *
- * 4. When included as a module, with arguments passed on the command line:
- * pas16_irq=xx the interrupt
- * pas16_addr=xx the port
- * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5"
- *
- * Note that if the override methods are used, place holders must
- * be specified for other boards in the system.
- *
- *
- * Configuration notes :
- * The current driver does not support interrupt sharing with the
- * sound portion of the card. If you use the same irq for the
- * scsi port and sound you will have problems. Either use
- * a different irq for the scsi port or don't use interrupts
- * for the scsi port.
- *
- * If you have problems with your card not being recognized, use
- * the LILO command line override. Try to get it recognized without
- * interrupts. Ie, for a board at the default 0x388 base port,
- * boot: linux pas16=0x388,255
- *
- * SCSI_IRQ_NONE (255) should be specified for no interrupt,
- * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden
- * on the command line.
- *
- * (IRQ_AUTO == 254, SCSI_IRQ_NONE == 255 in NCR5380.h)
- */
-
-#include <linux/module.h>
-
-#include <asm/system.h>
-#include <linux/signal.h>
-#include <linux/proc_fs.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "pas16.h"
-#define AUTOPROBE_IRQ
-#include "NCR5380.h"
-
-
-static int pas_maxi = 0;
-static int pas_wmaxi = 0;
-static unsigned short pas16_addr = 0;
-static int pas16_irq = 0;
-
-
-static const int scsi_irq_translate[] =
- { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
-
-/* The default_irqs array contains values used to set the irq into the
- * board via software (as must be done on newer model boards without
- * irq jumpers on the board). The first value in the array will be
- * assigned to logical board 0, the next to board 1, etc.
- */
-static int default_irqs[] __initdata =
- { PAS16_DEFAULT_BOARD_1_IRQ,
- PAS16_DEFAULT_BOARD_2_IRQ,
- PAS16_DEFAULT_BOARD_3_IRQ,
- PAS16_DEFAULT_BOARD_4_IRQ
- };
-
-static struct override {
- unsigned short io_port;
- int irq;
-} overrides
-#ifdef PAS16_OVERRIDE
- [] __initdata = PAS16_OVERRIDE;
-#else
- [4] __initdata = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO},
- {0,IRQ_AUTO}};
-#endif
-
-#define NO_OVERRIDES ARRAY_SIZE(overrides)
-
-static struct base {
- unsigned short io_port;
- int noauto;
-} bases[] __initdata =
- { {PAS16_DEFAULT_BASE_1, 0},
- {PAS16_DEFAULT_BASE_2, 0},
- {PAS16_DEFAULT_BASE_3, 0},
- {PAS16_DEFAULT_BASE_4, 0}
- };
-
-#define NO_BASES ARRAY_SIZE(bases)
-
-static const unsigned short pas16_offset[ 8 ] =
- {
- 0x1c00, /* OUTPUT_DATA_REG */
- 0x1c01, /* INITIATOR_COMMAND_REG */
- 0x1c02, /* MODE_REG */
- 0x1c03, /* TARGET_COMMAND_REG */
- 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */
- 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */
- 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?)
- * START_DMA_TARGET_RECEIVE_REG wo
- */
- 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro,
- * START_DMA_INITIATOR_RECEIVE_REG wo
- */
- };
-/*----------------------------------------------------------------*/
-/* the following will set the monitor border color (useful to find
- where something crashed or gets stuck at */
-/* 1 = blue
- 2 = green
- 3 = cyan
- 4 = red
- 5 = magenta
- 6 = yellow
- 7 = white
-*/
-#if 1
-#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
-#else
-#define rtrc(i) {}
-#endif
-
-
-/*
- * Function : enable_board( int board_num, unsigned short port )
- *
- * Purpose : set address in new model board
- *
- * Inputs : board_num - logical board number 0-3, port - base address
- *
- */
-
-static void __init
- enable_board( int board_num, unsigned short port )
-{
- outb( 0xbc + board_num, MASTER_ADDRESS_PTR );
- outb( port >> 2, MASTER_ADDRESS_PTR );
-}
-
-
-
-/*
- * Function : init_board( unsigned short port, int irq )
- *
- * Purpose : Set the board up to handle the SCSI interface
- *
- * Inputs : port - base address of the board,
- * irq - irq to assign to the SCSI port
- * force_irq - set it even if it conflicts with sound driver
- *
- */
-
-static void __init
- init_board( unsigned short io_port, int irq, int force_irq )
-{
- unsigned int tmp;
- unsigned int pas_irq_code;
-
- /* Initialize the SCSI part of the board */
-
- outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */
- outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */
- outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
-
- NCR5380_read( RESET_PARITY_INTERRUPT_REG );
-
- /* Set the SCSI interrupt pointer without mucking up the sound
- * interrupt pointer in the same byte.
- */
- pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0;
- tmp = inb( io_port + IO_CONFIG_3 );
-
- if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0
- && !force_irq )
- {
- printk( "pas16: WARNING: Can't use same irq as sound "
- "driver -- interrupts disabled\n" );
- /* Set up the drive parameters, disable 5380 interrupts */
- outb( 0x4d, io_port + SYS_CONFIG_4 );
- }
- else
- {
- tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 );
- outb( tmp, io_port + IO_CONFIG_3 );
-
- /* Set up the drive parameters and enable 5380 interrupts */
- outb( 0x6d, io_port + SYS_CONFIG_4 );
- }
-}
-
-
-/*
- * Function : pas16_hw_detect( unsigned short board_num )
- *
- * Purpose : determine if a pas16 board is present
- *
- * Inputs : board_num - logical board number ( 0 - 3 )
- *
- * Returns : 0 if board not found, 1 if found.
- */
-
-static int __init
- pas16_hw_detect( unsigned short board_num )
-{
- unsigned char board_rev, tmp;
- unsigned short io_port = bases[ board_num ].io_port;
-
- /* See if we can find a PAS16 board at the address associated
- * with this logical board number.
- */
-
- /* First, attempt to take a newer model board out of reset and
- * give it a base address. This shouldn't affect older boards.
- */
- enable_board( board_num, io_port );
-
- /* Now see if it looks like a PAS16 board */
- board_rev = inb( io_port + PCB_CONFIG );
-
- if( board_rev == 0xff )
- return 0;
-
- tmp = board_rev ^ 0xe0;
-
- outb( tmp, io_port + PCB_CONFIG );
- tmp = inb( io_port + PCB_CONFIG );
- outb( board_rev, io_port + PCB_CONFIG );
-
- if( board_rev != tmp ) /* Not a PAS-16 */
- return 0;
-
- if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 )
- return 0; /* return if no SCSI interface found */
-
- /* Mediavision has some new model boards that return ID bits
- * that indicate a SCSI interface, but they're not (LMS). We'll
- * put in an additional test to try to weed them out.
- */
-
- outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
- NCR5380_write( MODE_REG, 0x20 ); /* Is it really SCSI? */
- if( NCR5380_read( MODE_REG ) != 0x20 ) /* Write to a reg. */
- return 0; /* and try to read */
- NCR5380_write( MODE_REG, 0x00 ); /* it back. */
- if( NCR5380_read( MODE_REG ) != 0x00 )
- return 0;
-
- return 1;
-}
-
-
-/*
- * Function : pas16_setup(char *str, int *ints)
- *
- * Purpose : LILO command line initialization of the overrides array,
- *
- * Inputs : str - unused, ints - array of integer parameters with ints[0]
- * equal to the number of ints.
- *
- */
-
-void __init pas16_setup(char *str, int *ints)
-{
- static int commandline_current = 0;
- int i;
- if (ints[0] != 2)
- printk("pas16_setup : usage pas16=io_port,irq\n");
- else
- if (commandline_current < NO_OVERRIDES) {
- overrides[commandline_current].io_port = (unsigned short) ints[1];
- overrides[commandline_current].irq = ints[2];
- for (i = 0; i < NO_BASES; ++i)
- if (bases[i].io_port == (unsigned short) ints[1]) {
- bases[i].noauto = 1;
- break;
- }
- ++commandline_current;
- }
-}
-
-/*
- * Function : int pas16_detect(struct scsi_host_template * tpnt)
- *
- * Purpose : detects and initializes PAS16 controllers
- * that were autoprobed, overridden on the LILO command line,
- * or specified at compile time.
- *
- * Inputs : tpnt - template for this SCSI adapter.
- *
- * Returns : 1 if a host adapter was found, 0 if not.
- *
- */
-
-int __init pas16_detect(struct scsi_host_template * tpnt)
-{
- static int current_override = 0;
- static unsigned short current_base = 0;
- struct Scsi_Host *instance;
- unsigned short io_port;
- int count;
-
- tpnt->proc_name = "pas16";
- tpnt->proc_info = &pas16_proc_info;
-
- if (pas16_addr != 0) {
- overrides[0].io_port = pas16_addr;
- /*
- * This is how we avoid seeing more than
- * one host adapter at the same I/O port.
- * Cribbed shamelessly from pas16_setup().
- */
- for (count = 0; count < NO_BASES; ++count)
- if (bases[count].io_port == pas16_addr) {
- bases[count].noauto = 1;
- break;
- }
- }
- if (pas16_irq != 0)
- overrides[0].irq = pas16_irq;
-
- for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
- io_port = 0;
-
- if (overrides[current_override].io_port)
- {
- io_port = overrides[current_override].io_port;
- enable_board( current_override, io_port );
- init_board( io_port, overrides[current_override].irq, 1 );
- }
- else
- for (; !io_port && (current_base < NO_BASES); ++current_base) {
-#if (PDEBUG & PDEBUG_INIT)
- printk("scsi-pas16 : probing io_port %04x\n", (unsigned int) bases[current_base].io_port);
-#endif
- if ( !bases[current_base].noauto &&
- pas16_hw_detect( current_base ) ){
- io_port = bases[current_base].io_port;
- init_board( io_port, default_irqs[ current_base ], 0 );
-#if (PDEBUG & PDEBUG_INIT)
- printk("scsi-pas16 : detected board.\n");
-#endif
- }
- }
-
-
-#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
- printk("scsi-pas16 : io_port = %04x\n", (unsigned int) io_port);
-#endif
-
- if (!io_port)
- break;
-
- instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
- if(instance == NULL)
- break;
-
- instance->io_port = io_port;
-
- NCR5380_init(instance, 0);
-
- if (overrides[current_override].irq != IRQ_AUTO)
- instance->irq = overrides[current_override].irq;
- else
- instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS);
-
- if (instance->irq != SCSI_IRQ_NONE)
- if (request_irq(instance->irq, pas16_intr, IRQF_DISABLED,
- "pas16", instance)) {
- printk("scsi%d : IRQ%d not free, interrupts disabled\n",
- instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
- }
-
- if (instance->irq == SCSI_IRQ_NONE) {
- printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
- printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
- /* Disable 5380 interrupts, leave drive params the same */
- outb( 0x4d, io_port + SYS_CONFIG_4 );
- outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 );
- }
-
-#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
- printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
-#endif
-
- printk("scsi%d : at 0x%04x", instance->host_no, (int)
- instance->io_port);
- if (instance->irq == SCSI_IRQ_NONE)
- printk (" interrupts disabled");
- else
- printk (" irq %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- CAN_QUEUE, CMD_PER_LUN, PAS16_PUBLIC_RELEASE);
- NCR5380_print_options(instance);
- printk("\n");
-
- ++current_override;
- ++count;
- }
- return count;
-}
-
-/*
- * Function : int pas16_biosparam(Disk *disk, struct block_device *dev, int *ip)
- *
- * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
- * the specified device / size.
- *
- * Inputs : size = size of device in sectors (512 bytes), dev = block device
- * major / minor, ip[] = {heads, sectors, cylinders}
- *
- * Returns : always 0 (success), initializes ip
- *
- */
-
-/*
- * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
- * using hard disks on a trantor should verify that this mapping corresponds
- * to that used by the BIOS / ASPI driver by running the linux fdisk program
- * and matching the H_C_S coordinates to what DOS uses.
- */
-
-int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev,
- sector_t capacity, int * ip)
-{
- int size = capacity;
- ip[0] = 64;
- ip[1] = 32;
- ip[2] = size >> 11; /* I think I have it as /(32*64) */
- if( ip[2] > 1024 ) { /* yes, >, not >= */
- ip[0]=255;
- ip[1]=63;
- ip[2]=size/(63*255);
- if( ip[2] > 1023 ) /* yes >1023... */
- ip[2] = 1023;
- }
-
- return 0;
-}
-
-/*
- * Function : int NCR5380_pread (struct Scsi_Host *instance,
- * unsigned char *dst, int len)
- *
- * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to
- * dst
- *
- * Inputs : dst = destination, len = length in bytes
- *
- * Returns : 0 on success, non zero on a failure such as a watchdog
- * timeout.
- */
-
-static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
- int len) {
- register unsigned char *d = dst;
- register unsigned short reg = (unsigned short) (instance->io_port +
- P_DATA_REG_OFFSET);
- register int i = len;
- int ii = 0;
-
- while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
- ++ii;
-
- insb( reg, d, i );
-
- if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
- outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
- printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
- instance->host_no);
- return -1;
- }
- if (ii > pas_maxi)
- pas_maxi = ii;
- return 0;
-}
-
-/*
- * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
- * unsigned char *src, int len)
- *
- * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
- * src
- *
- * Inputs : src = source, len = length in bytes
- *
- * Returns : 0 on success, non zero on a failure such as a watchdog
- * timeout.
- */
-
-static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,
- int len) {
- register unsigned char *s = src;
- register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
- register int i = len;
- int ii = 0;
-
- while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
- ++ii;
-
- outsb( reg, s, i );
-
- if (inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
- outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
- printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
- instance->host_no);
- return -1;
- }
- if (ii > pas_maxi)
- pas_wmaxi = ii;
- return 0;
-}
-
-#include "NCR5380.c"
-
-static int pas16_release(struct Scsi_Host *shost)
-{
- if (shost->irq)
- free_irq(shost->irq, shost);
- NCR5380_exit(shost);
- if (shost->dma_channel != 0xff)
- free_dma(shost->dma_channel);
- if (shost->io_port && shost->n_io_port)
- release_region(shost->io_port, shost->n_io_port);
- scsi_unregister(shost);
- return 0;
-}
-
-static struct scsi_host_template driver_template = {
- .name = "Pro Audio Spectrum-16 SCSI",
- .detect = pas16_detect,
- .release = pas16_release,
- .queuecommand = pas16_queue_command,
- .eh_abort_handler = pas16_abort,
- .eh_bus_reset_handler = pas16_bus_reset,
- .bios_param = pas16_biosparam,
- .can_queue = CAN_QUEUE,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = CMD_PER_LUN,
- .use_clustering = DISABLE_CLUSTERING,
-};
-#include "scsi_module.c"
-
-#ifdef MODULE
-module_param(pas16_addr, ushort, 0);
-module_param(pas16_irq, int, 0);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h
deleted file mode 100644
index a04281cace2e..000000000000
--- a/drivers/scsi/pas16.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * This driver adapted from Drew Eckhardt's Trantor T128 driver
- *
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 666-5836
- *
- * ( Based on T128 - DISTRIBUTION RELEASE 3. )
- *
- * Modified to work with the Pro Audio Spectrum/Studio 16
- * by John Weidman.
- *
- *
- * For more information, please consult
- *
- * Media Vision
- * (510) 770-8600
- * (800) 348-7116
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-
-#ifndef PAS16_H
-#define PAS16_H
-
-#define PAS16_PUBLIC_RELEASE 3
-
-#define PDEBUG_INIT 0x1
-#define PDEBUG_TRANSFER 0x2
-
-#define PAS16_DEFAULT_BASE_1 0x388
-#define PAS16_DEFAULT_BASE_2 0x384
-#define PAS16_DEFAULT_BASE_3 0x38c
-#define PAS16_DEFAULT_BASE_4 0x288
-
-#define PAS16_DEFAULT_BOARD_1_IRQ 10
-#define PAS16_DEFAULT_BOARD_2_IRQ 12
-#define PAS16_DEFAULT_BOARD_3_IRQ 14
-#define PAS16_DEFAULT_BOARD_4_IRQ 15
-
-
-/*
- * The Pro Audio Spectrum boards are I/O mapped. They use a Zilog 5380
- * SCSI controller, which is the equivalent of NCR's 5380. "Pseudo-DMA"
- * architecture is used, where a PAL drives the DMA signals on the 5380
- * allowing fast, blind transfers with proper handshaking.
- */
-
-
-/* The Time-out Counter register is used to safe-guard against a stuck
- * bus (in the case of RDY driven handshake) or a stuck byte (if 16-Bit
- * DMA conversion is used). The counter uses a 28.224MHz clock
- * divided by 14 as its clock source. In the case of a stuck byte in
- * the holding register, an interrupt is generated (and mixed with the
- * one with the drive) using the CD-ROM interrupt pointer.
- */
-
-#define P_TIMEOUT_COUNTER_REG 0x4000
-#define P_TC_DISABLE 0x80 /* Set to 0 to enable timeout int. */
- /* Bits D6-D0 contain timeout count */
-
-
-#define P_TIMEOUT_STATUS_REG_OFFSET 0x4001
-#define P_TS_TIM 0x80 /* check timeout status */
- /* Bits D6-D4 N/U */
-#define P_TS_ARM_DRQ_INT 0x08 /* Arm DRQ Int. When set high,
- * the next rising edge will
- * cause a CD-ROM interrupt.
- * When set low, the interrupt
- * will be cleared. There is
- * no status available for
- * this interrupt.
- */
-#define P_TS_ENABLE_TO_ERR_INTERRUPT /* Enable timeout error int. */
-#define P_TS_ENABLE_WAIT /* Enable Wait */
-
-#define P_TS_CT 0x01 /* clear timeout. Note: writing
- * to this register clears the
- * timeout error int. or status
- */
-
-
-/*
- * The data register reads/writes to/from the 5380 in pseudo-DMA mode
- */
-
-#define P_DATA_REG_OFFSET 0x5c00 /* rw */
-
-#define P_STATUS_REG_OFFSET 0x5c01 /* ro */
-#define P_ST_RDY 0x80 /* 5380 DDRQ Status */
-
-#define P_IRQ_STATUS 0x5c03
-#define P_IS_IRQ 0x80 /* DIRQ status */
-
-#define PCB_CONFIG 0x803
-#define MASTER_ADDRESS_PTR 0x9a01 /* Fixed position - no relo */
-#define SYS_CONFIG_4 0x8003
-#define WAIT_STATE 0xbc00
-#define OPERATION_MODE_1 0xec03
-#define IO_CONFIG_3 0xf002
-
-
-#ifndef ASM
-static int pas16_abort(Scsi_Cmnd *);
-static int pas16_biosparam(struct scsi_device *, struct block_device *,
- sector_t, int*);
-static int pas16_detect(struct scsi_host_template *);
-static int pas16_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int pas16_bus_reset(Scsi_Cmnd *);
-
-#ifndef CMD_PER_LUN
-#define CMD_PER_LUN 2
-#endif
-
-#ifndef CAN_QUEUE
-#define CAN_QUEUE 32
-#endif
-
-#ifndef HOSTS_C
-
-#define NCR5380_implementation_fields \
- volatile unsigned short io_port
-
-#define NCR5380_local_declare() \
- volatile unsigned short io_port
-
-#define NCR5380_setup(instance) \
- io_port = (instance)->io_port
-
-#define PAS16_io_port(reg) ( io_port + pas16_offset[(reg)] )
-
-#if !(PDEBUG & PDEBUG_TRANSFER)
-#define NCR5380_read(reg) ( inb(PAS16_io_port(reg)) )
-#define NCR5380_write(reg, value) ( outb((value),PAS16_io_port(reg)) )
-#else
-#define NCR5380_read(reg) \
- (((unsigned char) printk("scsi%d : read register %d at io_port %04x\n"\
- , instance->hostno, (reg), PAS16_io_port(reg))), inb( PAS16_io_port(reg)) )
-
-#define NCR5380_write(reg, value) \
- (printk("scsi%d : write %02x to register %d at io_port %04x\n", \
- instance->hostno, (value), (reg), PAS16_io_port(reg)), \
- outb( (value),PAS16_io_port(reg) ) )
-
-#endif
-
-
-#define NCR5380_intr pas16_intr
-#define do_NCR5380_intr do_pas16_intr
-#define NCR5380_queue_command pas16_queue_command
-#define NCR5380_abort pas16_abort
-#define NCR5380_bus_reset pas16_bus_reset
-#define NCR5380_proc_info pas16_proc_info
-
-/* 15 14 12 10 7 5 3
- 1101 0100 1010 1000 */
-
-#define PAS16_IRQS 0xd4a8
-
-#endif /* else def HOSTS_C */
-#endif /* ndef ASM */
-#endif /* PAS16_H */
diff --git a/drivers/scsi/pcmcia/Kconfig b/drivers/scsi/pcmcia/Kconfig
index ecc855c550aa..449bd85db7bb 100644
--- a/drivers/scsi/pcmcia/Kconfig
+++ b/drivers/scsi/pcmcia/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# PCMCIA SCSI adapter configuration
#
@@ -11,6 +12,7 @@ if SCSI_LOWLEVEL_PCMCIA && SCSI && PCMCIA && m
config PCMCIA_AHA152X
tristate "Adaptec AHA152X PCMCIA support"
+ depends on HAS_IOPORT
select SCSI_SPI_ATTRS
help
Say Y here if you intend to attach this type of PCMCIA SCSI host
@@ -21,6 +23,8 @@ config PCMCIA_AHA152X
config PCMCIA_FDOMAIN
tristate "Future Domain PCMCIA support"
+ depends on HAS_IOPORT
+ select SCSI_FDOMAIN
help
Say Y here if you intend to attach this type of PCMCIA SCSI host
adapter to your computer.
@@ -30,11 +34,11 @@ config PCMCIA_FDOMAIN
config PCMCIA_NINJA_SCSI
tristate "NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support"
- depends on !64BIT
+ depends on (!64BIT || COMPILE_TEST) && HAS_IOPORT
help
If you intend to attach this type of PCMCIA SCSI host adapter to
your computer, say Y here and read
- <file:Documentation/scsi/NinjaSCSI.txt>.
+ <file:Documentation/scsi/NinjaSCSI.rst>.
Supported cards:
@@ -64,6 +68,7 @@ config PCMCIA_NINJA_SCSI
config PCMCIA_QLOGIC
tristate "Qlogic PCMCIA support"
+ depends on HAS_IOPORT
help
Say Y here if you intend to attach this type of PCMCIA SCSI host
adapter to your computer.
@@ -73,6 +78,7 @@ config PCMCIA_QLOGIC
config PCMCIA_SYM53C500
tristate "Symbios 53c500 PCMCIA support"
+ depends on HAS_IOPORT
help
Say Y here if you have a New Media Bus Toaster or other PCMCIA
SCSI adapter based on the Symbios 53c500 controller.
diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile
index 683bf148b5b7..02f5b44a2685 100644
--- a/drivers/scsi/pcmcia/Makefile
+++ b/drivers/scsi/pcmcia/Makefile
@@ -1,5 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
-ccflags-y := -Idrivers/scsi
+ccflags-y := -I $(srctree)/drivers/scsi
# 16-bit client drivers
obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o
@@ -9,5 +10,4 @@ obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o
obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o
aha152x_cs-objs := aha152x_stub.o aha152x_core.o
-fdomain_cs-objs := fdomain_stub.o fdomain_core.o
qlogic_cs-objs := qlogic_stub.o
diff --git a/drivers/scsi/pcmcia/aha152x_core.c b/drivers/scsi/pcmcia/aha152x_core.c
index dba3716511c5..24b89228b241 100644
--- a/drivers/scsi/pcmcia/aha152x_core.c
+++ b/drivers/scsi/pcmcia/aha152x_core.c
@@ -1,3 +1,3 @@
-#define PCMCIA 1
+#define AHA152X_PCMCIA 1
#define AHA152X_STAT 1
#include "aha152x.c"
diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
index 7d1609fa233c..1b54ba51a485 100644
--- a/drivers/scsi/pcmcia/aha152x_stub.c
+++ b/drivers/scsi/pcmcia/aha152x_stub.c
@@ -40,13 +40,16 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/ioport.h>
-#include <scsi/scsi.h>
#include <linux/major.h>
#include <linux/blkdev.h>
-#include <scsi/scsi_ioctl.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_tcq.h>
#include "aha152x.h"
#include <pcmcia/cistpl.h>
@@ -72,6 +75,7 @@ module_param(synchronous, int, 0);
module_param(reset_delay, int, 0);
module_param(ext_trans, int, 0);
+MODULE_DESCRIPTION("Adaptec AHA152X-compatible PCMCIA SCSI card driver");
MODULE_LICENSE("Dual MPL/GPL");
/*====================================================================*/
@@ -220,16 +224,4 @@ static struct pcmcia_driver aha152x_cs_driver = {
.id_table = aha152x_ids,
.resume = aha152x_resume,
};
-
-static int __init init_aha152x_cs(void)
-{
- return pcmcia_register_driver(&aha152x_cs_driver);
-}
-
-static void __exit exit_aha152x_cs(void)
-{
- pcmcia_unregister_driver(&aha152x_cs_driver);
-}
-
-module_init(init_aha152x_cs);
-module_exit(exit_aha152x_cs);
+module_pcmcia_driver(aha152x_cs_driver);
diff --git a/drivers/scsi/pcmcia/fdomain_core.c b/drivers/scsi/pcmcia/fdomain_core.c
deleted file mode 100644
index a48913791868..000000000000
--- a/drivers/scsi/pcmcia/fdomain_core.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#define PCMCIA 1
-#include "fdomain.c"
diff --git a/drivers/scsi/pcmcia/fdomain_cs.c b/drivers/scsi/pcmcia/fdomain_cs.c
new file mode 100644
index 000000000000..33df6a9ba9b5
--- /dev/null
+++ b/drivers/scsi/pcmcia/fdomain_cs.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1)
+/*
+ * Driver for Future Domain-compatible PCMCIA SCSI cards
+ * Copyright 2019 Ondrej Zary
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <scsi/scsi_host.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include "fdomain.h"
+
+MODULE_AUTHOR("Ondrej Zary, David Hinds");
+MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
+MODULE_LICENSE("Dual MPL/GPL");
+
+static int fdomain_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+ p_dev->io_lines = 10;
+ p_dev->resource[0]->end = FDOMAIN_REGION_SIZE;
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+ return pcmcia_request_io(p_dev);
+}
+
+static int fdomain_probe(struct pcmcia_device *link)
+{
+ int ret;
+ struct Scsi_Host *sh;
+
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+ link->config_regs = PRESENT_OPTION;
+
+ ret = pcmcia_loop_config(link, fdomain_config_check, NULL);
+ if (ret)
+ return ret;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto fail_disable;
+
+ if (!request_region(link->resource[0]->start, FDOMAIN_REGION_SIZE,
+ "fdomain_cs")) {
+ ret = -EBUSY;
+ goto fail_disable;
+ }
+
+ sh = fdomain_create(link->resource[0]->start, link->irq, 7, &link->dev);
+ if (!sh) {
+ dev_err(&link->dev, "Controller initialization failed");
+ ret = -ENODEV;
+ goto fail_release;
+ }
+
+ link->priv = sh;
+
+ return 0;
+
+fail_release:
+ release_region(link->resource[0]->start, FDOMAIN_REGION_SIZE);
+fail_disable:
+ pcmcia_disable_device(link);
+ return ret;
+}
+
+static void fdomain_remove(struct pcmcia_device *link)
+{
+ fdomain_destroy(link->priv);
+ release_region(link->resource[0]->start, FDOMAIN_REGION_SIZE);
+ pcmcia_disable_device(link);
+}
+
+static const struct pcmcia_device_id fdomain_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88,
+ 0x859cad20),
+ PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e),
+ PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation",
+ "SCSI PCMCIA Credit Card Controller",
+ 0x182bdafe, 0xc80d106f),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, fdomain_ids);
+
+static struct pcmcia_driver fdomain_cs_driver = {
+ .owner = THIS_MODULE,
+ .name = "fdomain_cs",
+ .probe = fdomain_probe,
+ .remove = fdomain_remove,
+ .id_table = fdomain_ids,
+};
+
+module_pcmcia_driver(fdomain_cs_driver);
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
deleted file mode 100644
index 714b248f5d5e..000000000000
--- a/drivers/scsi/pcmcia/fdomain_stub.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*======================================================================
-
- A driver for Future Domain-compatible PCMCIA SCSI cards
-
- fdomain_cs.c 1.47 2001/10/13 00:08:52
-
- The contents of this file are subject to the Mozilla Public
- License Version 1.1 (the "License"); you may not use this file
- except in compliance with the License. You may obtain a copy of
- the License at http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS
- IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- implied. See the License for the specific language governing
- rights and limitations under the License.
-
- The initial developer of the original code is David A. Hinds
- <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
- are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
-
- Alternatively, the contents of this file may be used under the
- terms of the GNU General Public License version 2 (the "GPL"), in
- which case the provisions of the GPL are applicable instead of the
- above. If you wish to allow the use of your version of this file
- only under the terms of the GPL and not to allow others to use
- your version of this file under the MPL, indicate your decision
- by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL. If you do not delete
- the provisions above, a recipient may use your version of this
- file under either the MPL or the GPL.
-
-======================================================================*/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <scsi/scsi.h>
-#include <linux/major.h>
-#include <linux/blkdev.h>
-#include <scsi/scsi_ioctl.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "fdomain.h"
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
-MODULE_LICENSE("Dual MPL/GPL");
-
-/*====================================================================*/
-
-typedef struct scsi_info_t {
- struct pcmcia_device *p_dev;
- struct Scsi_Host *host;
-} scsi_info_t;
-
-
-static void fdomain_release(struct pcmcia_device *link);
-static void fdomain_detach(struct pcmcia_device *p_dev);
-static int fdomain_config(struct pcmcia_device *link);
-
-static int fdomain_probe(struct pcmcia_device *link)
-{
- scsi_info_t *info;
-
- dev_dbg(&link->dev, "fdomain_attach()\n");
-
- /* Create new SCSI device */
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->p_dev = link;
- link->priv = info;
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
- link->config_regs = PRESENT_OPTION;
-
- return fdomain_config(link);
-} /* fdomain_attach */
-
-/*====================================================================*/
-
-static void fdomain_detach(struct pcmcia_device *link)
-{
- dev_dbg(&link->dev, "fdomain_detach\n");
-
- fdomain_release(link);
-
- kfree(link->priv);
-} /* fdomain_detach */
-
-/*====================================================================*/
-
-static int fdomain_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- p_dev->io_lines = 10;
- p_dev->resource[0]->end = 0x10;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- return pcmcia_request_io(p_dev);
-}
-
-
-static int fdomain_config(struct pcmcia_device *link)
-{
- scsi_info_t *info = link->priv;
- int ret;
- char str[22];
- struct Scsi_Host *host;
-
- dev_dbg(&link->dev, "fdomain_config\n");
-
- ret = pcmcia_loop_config(link, fdomain_config_check, NULL);
- if (ret)
- goto failed;
-
- if (!link->irq)
- goto failed;
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- /* A bad hack... */
- release_region(link->resource[0]->start, resource_size(link->resource[0]));
-
- /* Set configuration options for the fdomain driver */
- sprintf(str, "%d,%d", (unsigned int) link->resource[0]->start, link->irq);
- fdomain_setup(str);
-
- host = __fdomain_16x0_detect(&fdomain_driver_template);
- if (!host) {
- printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
- goto failed;
- }
-
- if (scsi_add_host(host, NULL))
- goto failed;
- scsi_scan_host(host);
-
- info->host = host;
-
- return 0;
-
-failed:
- fdomain_release(link);
- return -ENODEV;
-} /* fdomain_config */
-
-/*====================================================================*/
-
-static void fdomain_release(struct pcmcia_device *link)
-{
- scsi_info_t *info = link->priv;
-
- dev_dbg(&link->dev, "fdomain_release\n");
-
- scsi_remove_host(info->host);
- pcmcia_disable_device(link);
- scsi_unregister(info->host);
-}
-
-/*====================================================================*/
-
-static int fdomain_resume(struct pcmcia_device *link)
-{
- fdomain_16x0_bus_reset(NULL);
-
- return 0;
-}
-
-static const struct pcmcia_device_id fdomain_ids[] = {
- PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20),
- PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e),
- PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, fdomain_ids);
-
-static struct pcmcia_driver fdomain_cs_driver = {
- .owner = THIS_MODULE,
- .name = "fdomain_cs",
- .probe = fdomain_probe,
- .remove = fdomain_detach,
- .id_table = fdomain_ids,
- .resume = fdomain_resume,
-};
-
-static int __init init_fdomain_cs(void)
-{
- return pcmcia_register_driver(&fdomain_cs_driver);
-}
-
-static void __exit exit_fdomain_cs(void)
-{
- pcmcia_unregister_driver(&fdomain_cs_driver);
-}
-
-module_init(init_fdomain_cs);
-module_exit(exit_fdomain_cs);
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
index ca86721a71b9..a5a1406a2bde 100644
--- a/drivers/scsi/pcmcia/nsp_cs.c
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -41,10 +41,9 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <../drivers/scsi/scsi.h>
-#include <scsi/scsi_host.h>
-
#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h>
#include <pcmcia/cistpl.h>
@@ -55,10 +54,7 @@
MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>");
MODULE_DESCRIPTION("WorkBit NinjaSCSI-3 / NinjaSCSI-32Bi(16bit) PCMCIA SCSI host adapter module");
-MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");
-#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
-#endif
#include "nsp_io.h"
@@ -70,13 +66,18 @@ module_param(nsp_burst_mode, int, 0);
MODULE_PARM_DESC(nsp_burst_mode, "Burst transfer mode (0=io8, 1=io32, 2=mem32(default))");
/* Release IO ports after configuration? */
-static int free_ports = 0;
+static bool free_ports = 0;
module_param(free_ports, bool, 0);
MODULE_PARM_DESC(free_ports, "Release IO ports after configuration? (default: 0 (=no))");
+static struct scsi_pointer *nsp_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
static struct scsi_host_template nsp_driver_template = {
.proc_name = "nsp_cs",
- .proc_info = nsp_proc_info,
+ .show_info = nsp_show_info,
.name = "WorkBit NinjaSCSI-3/32Bi(16bit)",
.info = nsp_info,
.queuecommand = nsp_queuecommand,
@@ -86,8 +87,8 @@ static struct scsi_host_template nsp_driver_template = {
.can_queue = 1,
.this_id = NSP_INITIATOR_ID,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
+ .cmd_size = sizeof(struct scsi_pointer),
};
static nsp_hw_data nsp_data_base; /* attach <-> detect glue */
@@ -137,6 +138,7 @@ static inline void nsp_inc_resid(struct scsi_cmnd *SCpnt, int residInc)
scsi_set_resid(SCpnt, scsi_get_resid(SCpnt) + residInc);
}
+__printf(4, 5)
static void nsp_cs_message(const char *func, int line, char *type, char *fmt, ...)
{
va_list args;
@@ -181,12 +183,12 @@ static void nsp_scsi_done(struct scsi_cmnd *SCpnt)
data->CurrentSC = NULL;
- SCpnt->scsi_done(SCpnt);
+ scsi_done(SCpnt);
}
-static int nsp_queuecommand_lck(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
+static int nsp_queuecommand_lck(struct scsi_cmnd *const SCpnt)
{
+ struct scsi_pointer *scsi_pointer = nsp_priv(SCpnt);
#ifdef NSP_DEBUG
/*unsigned int host_id = SCpnt->device->host->this_id;*/
/*unsigned int base = SCpnt->device->host->io_port;*/
@@ -195,13 +197,11 @@ static int nsp_queuecommand_lck(struct scsi_cmnd *SCpnt,
nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
nsp_dbg(NSP_DEBUG_QUEUECOMMAND,
- "SCpnt=0x%p target=%d lun=%d sglist=0x%p bufflen=%d sg_count=%d",
+ "SCpnt=0x%p target=%d lun=%llu sglist=0x%p bufflen=%d sg_count=%d",
SCpnt, target, SCpnt->device->lun, scsi_sglist(SCpnt),
scsi_bufflen(SCpnt), scsi_sg_count(SCpnt));
//nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "before CurrentSC=0x%p", data->CurrentSC);
- SCpnt->scsi_done = done;
-
if (data->CurrentSC != NULL) {
nsp_msg(KERN_DEBUG, "CurrentSC!=NULL this can't be happen");
SCpnt->result = DID_BAD_TARGET << 16;
@@ -224,11 +224,11 @@ static int nsp_queuecommand_lck(struct scsi_cmnd *SCpnt,
data->CurrentSC = SCpnt;
- SCpnt->SCp.Status = CHECK_CONDITION;
- SCpnt->SCp.Message = 0;
- SCpnt->SCp.have_data_in = IO_UNKNOWN;
- SCpnt->SCp.sent_command = 0;
- SCpnt->SCp.phase = PH_UNDETERMINED;
+ scsi_pointer->Status = SAM_STAT_CHECK_CONDITION;
+ scsi_pointer->Message = 0;
+ scsi_pointer->have_data_in = IO_UNKNOWN;
+ scsi_pointer->sent_command = 0;
+ scsi_pointer->phase = PH_UNDETERMINED;
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
/* setup scratch area
@@ -238,18 +238,18 @@ static int nsp_queuecommand_lck(struct scsi_cmnd *SCpnt,
SCp.buffers_residual : left buffers in list
SCp.phase : current state of the command */
if (scsi_bufflen(SCpnt)) {
- SCpnt->SCp.buffer = scsi_sglist(SCpnt);
- SCpnt->SCp.ptr = BUFFER_ADDR;
- SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
- SCpnt->SCp.buffers_residual = scsi_sg_count(SCpnt) - 1;
+ scsi_pointer->buffer = scsi_sglist(SCpnt);
+ scsi_pointer->ptr = BUFFER_ADDR(SCpnt);
+ scsi_pointer->this_residual = scsi_pointer->buffer->length;
+ scsi_pointer->buffers_residual = scsi_sg_count(SCpnt) - 1;
} else {
- SCpnt->SCp.ptr = NULL;
- SCpnt->SCp.this_residual = 0;
- SCpnt->SCp.buffer = NULL;
- SCpnt->SCp.buffers_residual = 0;
+ scsi_pointer->ptr = NULL;
+ scsi_pointer->this_residual = 0;
+ scsi_pointer->buffer = NULL;
+ scsi_pointer->buffers_residual = 0;
}
- if (nsphw_start_selection(SCpnt) == FALSE) {
+ if (!nsphw_start_selection(SCpnt)) {
nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "selection fail");
SCpnt->result = DID_BUS_BUSY << 16;
nsp_scsi_done(SCpnt);
@@ -269,14 +269,14 @@ static DEF_SCSI_QCMD(nsp_queuecommand)
/*
* setup PIO FIFO transfer mode and enable/disable to data out
*/
-static void nsp_setup_fifo(nsp_hw_data *data, int enabled)
+static void nsp_setup_fifo(nsp_hw_data *data, bool enabled)
{
unsigned int base = data->BaseAddress;
unsigned char transfer_mode_reg;
//nsp_dbg(NSP_DEBUG_DATA_IO, "enabled=%d", enabled);
- if (enabled != FALSE) {
+ if (enabled) {
transfer_mode_reg = TRANSFER_GO | BRAIND;
} else {
transfer_mode_reg = 0;
@@ -304,7 +304,7 @@ static void nsphw_init_sync(nsp_hw_data *data)
/*
* Initialize Ninja hardware
*/
-static int nsphw_init(nsp_hw_data *data)
+static void nsphw_init(nsp_hw_data *data)
{
unsigned int base = data->BaseAddress;
@@ -354,16 +354,15 @@ static int nsphw_init(nsp_hw_data *data)
SCSI_RESET_IRQ_EI );
nsp_write(base, IRQCONTROL, IRQCONTROL_ALLCLEAR);
- nsp_setup_fifo(data, FALSE);
-
- return TRUE;
+ nsp_setup_fifo(data, false);
}
/*
* Start selection phase
*/
-static int nsphw_start_selection(struct scsi_cmnd *SCpnt)
+static bool nsphw_start_selection(struct scsi_cmnd *const SCpnt)
{
+ struct scsi_pointer *scsi_pointer = nsp_priv(SCpnt);
unsigned int host_id = SCpnt->device->host->this_id;
unsigned int base = SCpnt->device->host->io_port;
unsigned char target = scmd_id(SCpnt);
@@ -376,12 +375,12 @@ static int nsphw_start_selection(struct scsi_cmnd *SCpnt)
phase = nsp_index_read(base, SCSIBUSMON);
if(phase != BUSMON_BUS_FREE) {
//nsp_dbg(NSP_DEBUG_RESELECTION, "bus busy");
- return FALSE;
+ return false;
}
/* start arbitration */
//nsp_dbg(NSP_DEBUG_RESELECTION, "start arbit");
- SCpnt->SCp.phase = PH_ARBSTART;
+ scsi_pointer->phase = PH_ARBSTART;
nsp_index_write(base, SETARBIT, ARBIT_GO);
time_out = 1000;
@@ -396,12 +395,12 @@ static int nsphw_start_selection(struct scsi_cmnd *SCpnt)
if (!(arbit & ARBIT_WIN)) {
//nsp_dbg(NSP_DEBUG_RESELECTION, "arbit fail");
nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR);
- return FALSE;
+ return false;
}
/* assert select line */
//nsp_dbg(NSP_DEBUG_RESELECTION, "assert SEL line");
- SCpnt->SCp.phase = PH_SELSTART;
+ scsi_pointer->phase = PH_SELSTART;
udelay(3); /* wait 2.4us */
nsp_index_write(base, SCSIDATALATCH, BIT(host_id) | BIT(target));
nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_ATN);
@@ -415,7 +414,7 @@ static int nsphw_start_selection(struct scsi_cmnd *SCpnt)
nsp_start_timer(SCpnt, 1000/51);
data->SelectionTimeOut = 1;
- return TRUE;
+ return true;
}
struct nsp_sync_table {
@@ -451,8 +450,6 @@ static int nsp_analyze_sdtr(struct scsi_cmnd *SCpnt)
sync_data *sync = &(data->Sync[target]);
struct nsp_sync_table *sync_table;
unsigned int period, offset;
- int i;
-
nsp_dbg(NSP_DEBUG_SYNC, "in");
@@ -467,7 +464,7 @@ static int nsp_analyze_sdtr(struct scsi_cmnd *SCpnt)
sync_table = nsp_sync_table_40M;
}
- for ( i = 0; sync_table->max_period != 0; i++, sync_table++) {
+ for (; sync_table->max_period != 0; sync_table++) {
if ( period >= sync_table->min_period &&
period <= sync_table->max_period ) {
break;
@@ -485,7 +482,7 @@ static int nsp_analyze_sdtr(struct scsi_cmnd *SCpnt)
sync->SyncRegister = 0;
sync->AckWidth = 0;
- return FALSE;
+ return false;
}
sync->SyncRegister = (sync_table->chip_period << SYNCREG_PERIOD_SHIFT) |
@@ -494,7 +491,7 @@ static int nsp_analyze_sdtr(struct scsi_cmnd *SCpnt)
nsp_dbg(NSP_DEBUG_SYNC, "sync_reg=0x%x, ack_width=0x%x", sync->SyncRegister, sync->AckWidth);
- return TRUE;
+ return true;
}
@@ -533,7 +530,7 @@ static int nsp_negate_signal(struct scsi_cmnd *SCpnt, unsigned char mask,
} while ((--time_out != 0) && (reg & mask) != 0);
if (time_out == 0) {
- nsp_msg(KERN_DEBUG, " %s signal off timeut", str);
+ nsp_msg(KERN_DEBUG, " %s signal off timeout", str);
}
return 0;
@@ -577,8 +574,9 @@ static int nsp_expect_signal(struct scsi_cmnd *SCpnt,
/*
* transfer SCSI message
*/
-static int nsp_xfer(struct scsi_cmnd *SCpnt, int phase)
+static int nsp_xfer(struct scsi_cmnd *const SCpnt, int phase)
{
+ struct scsi_pointer *scsi_pointer = nsp_priv(SCpnt);
unsigned int base = SCpnt->device->host->io_port;
nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
char *buf = data->MsgBuffer;
@@ -596,7 +594,7 @@ static int nsp_xfer(struct scsi_cmnd *SCpnt, int phase)
}
/* if last byte, negate ATN */
- if (len == 1 && SCpnt->SCp.phase == PH_MSG_OUT) {
+ if (len == 1 && scsi_pointer->phase == PH_MSG_OUT) {
nsp_index_write(base, SCSIBUSCTRL, AUTODIRECTION | ACKENB);
}
@@ -617,14 +615,15 @@ static int nsp_xfer(struct scsi_cmnd *SCpnt, int phase)
/*
* get extra SCSI data from fifo
*/
-static int nsp_dataphase_bypass(struct scsi_cmnd *SCpnt)
+static int nsp_dataphase_bypass(struct scsi_cmnd *const SCpnt)
{
+ struct scsi_pointer *scsi_pointer = nsp_priv(SCpnt);
nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
unsigned int count;
//nsp_dbg(NSP_DEBUG_DATA_IO, "in");
- if (SCpnt->SCp.have_data_in != IO_IN) {
+ if (scsi_pointer->have_data_in != IO_IN) {
return 0;
}
@@ -639,9 +638,9 @@ static int nsp_dataphase_bypass(struct scsi_cmnd *SCpnt)
* data phase skip only occures in case of SCSI_LOW_READ
*/
nsp_dbg(NSP_DEBUG_DATA_IO, "use bypass quirk");
- SCpnt->SCp.phase = PH_DATA;
+ scsi_pointer->phase = PH_DATA;
nsp_pio_read(SCpnt);
- nsp_setup_fifo(data, FALSE);
+ nsp_setup_fifo(data, false);
return 0;
}
@@ -649,7 +648,7 @@ static int nsp_dataphase_bypass(struct scsi_cmnd *SCpnt)
/*
* accept reselection
*/
-static int nsp_reselected(struct scsi_cmnd *SCpnt)
+static void nsp_reselected(struct scsi_cmnd *SCpnt)
{
unsigned int base = SCpnt->device->host->io_port;
unsigned int host_id = SCpnt->device->host->this_id;
@@ -681,8 +680,6 @@ static int nsp_reselected(struct scsi_cmnd *SCpnt)
bus_reg = nsp_index_read(base, SCSIBUSCTRL) & ~(SCSI_BSY | SCSI_ATN);
nsp_index_write(base, SCSIBUSCTRL, bus_reg);
nsp_index_write(base, SCSIBUSCTRL, bus_reg | AUTODIRECTION | ACKENB);
-
- return TRUE;
}
/*
@@ -692,14 +689,14 @@ static int nsp_fifo_count(struct scsi_cmnd *SCpnt)
{
unsigned int base = SCpnt->device->host->io_port;
unsigned int count;
- unsigned int l, m, h, dummy;
+ unsigned int l, m, h;
nsp_index_write(base, POINTERCLR, POINTER_CLEAR | ACK_COUNTER);
l = nsp_index_read(base, TRANSFERCOUNT);
m = nsp_index_read(base, TRANSFERCOUNT);
h = nsp_index_read(base, TRANSFERCOUNT);
- dummy = nsp_index_read(base, TRANSFERCOUNT); /* required this! */
+ nsp_index_read(base, TRANSFERCOUNT); /* required this! */
count = (h << 16) | (m << 8) | (l << 0);
@@ -715,8 +712,9 @@ static int nsp_fifo_count(struct scsi_cmnd *SCpnt)
/*
* read data in DATA IN phase
*/
-static void nsp_pio_read(struct scsi_cmnd *SCpnt)
+static void nsp_pio_read(struct scsi_cmnd *const SCpnt)
{
+ struct scsi_pointer *scsi_pointer = nsp_priv(SCpnt);
unsigned int base = SCpnt->device->host->io_port;
unsigned long mmio_base = SCpnt->device->host->base;
nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
@@ -727,24 +725,25 @@ static void nsp_pio_read(struct scsi_cmnd *SCpnt)
ocount = data->FifoCount;
nsp_dbg(NSP_DEBUG_DATA_IO, "in SCpnt=0x%p resid=%d ocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d",
- SCpnt, scsi_get_resid(SCpnt), ocount, SCpnt->SCp.ptr,
- SCpnt->SCp.this_residual, SCpnt->SCp.buffer,
- SCpnt->SCp.buffers_residual);
+ SCpnt, scsi_get_resid(SCpnt), ocount, scsi_pointer->ptr,
+ scsi_pointer->this_residual, scsi_pointer->buffer,
+ scsi_pointer->buffers_residual);
time_out = 1000;
while ((time_out-- != 0) &&
- (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0 ) ) {
+ (scsi_pointer->this_residual > 0 ||
+ scsi_pointer->buffers_residual > 0)) {
stat = nsp_index_read(base, SCSIBUSMON);
stat &= BUSMON_PHASE_MASK;
res = nsp_fifo_count(SCpnt) - ocount;
- //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x ocount=0x%x res=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount, res);
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x ocount=0x%x res=0x%x", scsi_pointer->ptr, scsi_pointer->this_residual, ocount, res);
if (res == 0) { /* if some data available ? */
if (stat == BUSPHASE_DATA_IN) { /* phase changed? */
- //nsp_dbg(NSP_DEBUG_DATA_IO, " wait for data this=%d", SCpnt->SCp.this_residual);
+ //nsp_dbg(NSP_DEBUG_DATA_IO, " wait for data this=%d", scsi_pointer->this_residual);
continue;
} else {
nsp_dbg(NSP_DEBUG_DATA_IO, "phase changed stat=0x%x", stat);
@@ -758,20 +757,21 @@ static void nsp_pio_read(struct scsi_cmnd *SCpnt)
continue;
}
- res = min(res, SCpnt->SCp.this_residual);
+ res = min(res, scsi_pointer->this_residual);
switch (data->TransferMode) {
case MODE_IO32:
res &= ~(BIT(1)|BIT(0)); /* align 4 */
- nsp_fifo32_read(base, SCpnt->SCp.ptr, res >> 2);
+ nsp_fifo32_read(base, scsi_pointer->ptr, res >> 2);
break;
case MODE_IO8:
- nsp_fifo8_read (base, SCpnt->SCp.ptr, res );
+ nsp_fifo8_read(base, scsi_pointer->ptr, res);
break;
case MODE_MEM32:
res &= ~(BIT(1)|BIT(0)); /* align 4 */
- nsp_mmio_fifo32_read(mmio_base, SCpnt->SCp.ptr, res >> 2);
+ nsp_mmio_fifo32_read(mmio_base, scsi_pointer->ptr,
+ res >> 2);
break;
default:
@@ -780,22 +780,23 @@ static void nsp_pio_read(struct scsi_cmnd *SCpnt)
}
nsp_inc_resid(SCpnt, -res);
- SCpnt->SCp.ptr += res;
- SCpnt->SCp.this_residual -= res;
+ scsi_pointer->ptr += res;
+ scsi_pointer->this_residual -= res;
ocount += res;
- //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this_residual=0x%x ocount=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount);
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this_residual=0x%x ocount=0x%x", scsi_pointer->ptr, scsi_pointer->this_residual, ocount);
/* go to next scatter list if available */
- if (SCpnt->SCp.this_residual == 0 &&
- SCpnt->SCp.buffers_residual != 0 ) {
+ if (scsi_pointer->this_residual == 0 &&
+ scsi_pointer->buffers_residual != 0 ) {
//nsp_dbg(NSP_DEBUG_DATA_IO, "scatterlist next timeout=%d", time_out);
- SCpnt->SCp.buffers_residual--;
- SCpnt->SCp.buffer++;
- SCpnt->SCp.ptr = BUFFER_ADDR;
- SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ scsi_pointer->buffers_residual--;
+ scsi_pointer->buffer = sg_next(scsi_pointer->buffer);
+ scsi_pointer->ptr = BUFFER_ADDR(SCpnt);
+ scsi_pointer->this_residual =
+ scsi_pointer->buffer->length;
time_out = 1000;
- //nsp_dbg(NSP_DEBUG_DATA_IO, "page: 0x%p, off: 0x%x", SCpnt->SCp.buffer->page, SCpnt->SCp.buffer->offset);
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "page: 0x%p, off: 0x%x", scsi_pointer->buffer->page, scsi_pointer->buffer->offset);
}
}
@@ -803,8 +804,8 @@ static void nsp_pio_read(struct scsi_cmnd *SCpnt)
if (time_out < 0) {
nsp_msg(KERN_DEBUG, "pio read timeout resid=%d this_residual=%d buffers_residual=%d",
- scsi_get_resid(SCpnt), SCpnt->SCp.this_residual,
- SCpnt->SCp.buffers_residual);
+ scsi_get_resid(SCpnt), scsi_pointer->this_residual,
+ scsi_pointer->buffers_residual);
}
nsp_dbg(NSP_DEBUG_DATA_IO, "read ocount=0x%x", ocount);
nsp_dbg(NSP_DEBUG_DATA_IO, "r cmd=%d resid=0x%x\n", data->CmdId,
@@ -816,6 +817,7 @@ static void nsp_pio_read(struct scsi_cmnd *SCpnt)
*/
static void nsp_pio_write(struct scsi_cmnd *SCpnt)
{
+ struct scsi_pointer *scsi_pointer = nsp_priv(SCpnt);
unsigned int base = SCpnt->device->host->io_port;
unsigned long mmio_base = SCpnt->device->host->base;
nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
@@ -826,14 +828,15 @@ static void nsp_pio_write(struct scsi_cmnd *SCpnt)
ocount = data->FifoCount;
nsp_dbg(NSP_DEBUG_DATA_IO, "in fifocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d resid=0x%x",
- data->FifoCount, SCpnt->SCp.ptr, SCpnt->SCp.this_residual,
- SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual,
+ data->FifoCount, scsi_pointer->ptr, scsi_pointer->this_residual,
+ scsi_pointer->buffer, scsi_pointer->buffers_residual,
scsi_get_resid(SCpnt));
time_out = 1000;
while ((time_out-- != 0) &&
- (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0)) {
+ (scsi_pointer->this_residual > 0 ||
+ scsi_pointer->buffers_residual > 0)) {
stat = nsp_index_read(base, SCSIBUSMON);
stat &= BUSMON_PHASE_MASK;
@@ -843,9 +846,9 @@ static void nsp_pio_write(struct scsi_cmnd *SCpnt)
nsp_dbg(NSP_DEBUG_DATA_IO, "phase changed stat=0x%x, res=%d\n", stat, res);
/* Put back pointer */
nsp_inc_resid(SCpnt, res);
- SCpnt->SCp.ptr -= res;
- SCpnt->SCp.this_residual += res;
- ocount -= res;
+ scsi_pointer->ptr -= res;
+ scsi_pointer->this_residual += res;
+ ocount -= res;
break;
}
@@ -856,21 +859,22 @@ static void nsp_pio_write(struct scsi_cmnd *SCpnt)
continue;
}
- res = min(SCpnt->SCp.this_residual, WFIFO_CRIT);
+ res = min(scsi_pointer->this_residual, WFIFO_CRIT);
- //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x res=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, res);
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x res=0x%x", scsi_pointer->ptr, scsi_pointer->this_residual, res);
switch (data->TransferMode) {
case MODE_IO32:
res &= ~(BIT(1)|BIT(0)); /* align 4 */
- nsp_fifo32_write(base, SCpnt->SCp.ptr, res >> 2);
+ nsp_fifo32_write(base, scsi_pointer->ptr, res >> 2);
break;
case MODE_IO8:
- nsp_fifo8_write (base, SCpnt->SCp.ptr, res );
+ nsp_fifo8_write(base, scsi_pointer->ptr, res);
break;
case MODE_MEM32:
res &= ~(BIT(1)|BIT(0)); /* align 4 */
- nsp_mmio_fifo32_write(mmio_base, SCpnt->SCp.ptr, res >> 2);
+ nsp_mmio_fifo32_write(mmio_base, scsi_pointer->ptr,
+ res >> 2);
break;
default:
@@ -879,18 +883,19 @@ static void nsp_pio_write(struct scsi_cmnd *SCpnt)
}
nsp_inc_resid(SCpnt, -res);
- SCpnt->SCp.ptr += res;
- SCpnt->SCp.this_residual -= res;
- ocount += res;
+ scsi_pointer->ptr += res;
+ scsi_pointer->this_residual -= res;
+ ocount += res;
/* go to next scatter list if available */
- if (SCpnt->SCp.this_residual == 0 &&
- SCpnt->SCp.buffers_residual != 0 ) {
+ if (scsi_pointer->this_residual == 0 &&
+ scsi_pointer->buffers_residual != 0 ) {
//nsp_dbg(NSP_DEBUG_DATA_IO, "scatterlist next");
- SCpnt->SCp.buffers_residual--;
- SCpnt->SCp.buffer++;
- SCpnt->SCp.ptr = BUFFER_ADDR;
- SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ scsi_pointer->buffers_residual--;
+ scsi_pointer->buffer = sg_next(scsi_pointer->buffer);
+ scsi_pointer->ptr = BUFFER_ADDR(SCpnt);
+ scsi_pointer->this_residual =
+ scsi_pointer->buffer->length;
time_out = 1000;
}
}
@@ -937,7 +942,7 @@ static int nsp_nexus(struct scsi_cmnd *SCpnt)
}
/* setup pdma fifo */
- nsp_setup_fifo(data, TRUE);
+ nsp_setup_fifo(data, true);
/* clear ack counter */
data->FifoCount = 0;
@@ -958,6 +963,7 @@ static irqreturn_t nspintr(int irq, void *dev_id)
unsigned int base;
unsigned char irq_status, irq_phase, phase;
struct scsi_cmnd *tmpSC;
+ struct scsi_pointer *scsi_pointer;
unsigned char target, lun;
unsigned int *sync_neg;
int i, tmp;
@@ -1036,9 +1042,10 @@ static irqreturn_t nspintr(int irq, void *dev_id)
if(data->CurrentSC != NULL) {
tmpSC = data->CurrentSC;
- tmpSC->result = (DID_RESET << 16) |
- ((tmpSC->SCp.Message & 0xff) << 8) |
- ((tmpSC->SCp.Status & 0xff) << 0);
+ scsi_pointer = nsp_priv(tmpSC);
+ tmpSC->result = (DID_RESET << 16) |
+ ((scsi_pointer->Message & 0xff) << 8) |
+ ((scsi_pointer->Status & 0xff) << 0);
nsp_scsi_done(tmpSC);
}
return IRQ_HANDLED;
@@ -1052,6 +1059,7 @@ static irqreturn_t nspintr(int irq, void *dev_id)
}
tmpSC = data->CurrentSC;
+ scsi_pointer = nsp_priv(tmpSC);
target = tmpSC->device->id;
lun = tmpSC->device->lun;
sync_neg = &(data->Sync[target].SyncNegotiation);
@@ -1063,9 +1071,8 @@ static irqreturn_t nspintr(int irq, void *dev_id)
if (irq_phase & RESELECT_IRQ) {
nsp_dbg(NSP_DEBUG_INTR, "reselect");
nsp_write(base, IRQCONTROL, IRQCONTROL_RESELECT_CLEAR);
- if (nsp_reselected(tmpSC) != FALSE) {
- return IRQ_HANDLED;
- }
+ nsp_reselected(tmpSC);
+ return IRQ_HANDLED;
}
if ((irq_phase & (PHASE_CHANGE_IRQ | LATCHED_BUS_FREE)) == 0) {
@@ -1075,7 +1082,7 @@ static irqreturn_t nspintr(int irq, void *dev_id)
//show_phase(tmpSC);
- switch(tmpSC->SCp.phase) {
+ switch (scsi_pointer->phase) {
case PH_SELSTART:
// *sync_neg = SYNC_NOT_YET;
if ((phase & BUSMON_BSY) == 0) {
@@ -1098,14 +1105,12 @@ static irqreturn_t nspintr(int irq, void *dev_id)
/* attention assert */
//nsp_dbg(NSP_DEBUG_INTR, "attention assert");
data->SelectionTimeOut = 0;
- tmpSC->SCp.phase = PH_SELECTED;
+ scsi_pointer->phase = PH_SELECTED;
nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN);
udelay(1);
nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN | AUTODIRECTION | ACKENB);
return IRQ_HANDLED;
- break;
-
case PH_RESELECT:
//nsp_dbg(NSP_DEBUG_INTR, "phase reselect");
// *sync_neg = SYNC_NOT_YET;
@@ -1115,7 +1120,7 @@ static irqreturn_t nspintr(int irq, void *dev_id)
nsp_scsi_done(tmpSC);
return IRQ_HANDLED;
}
- /* fall thru */
+ fallthrough;
default:
if ((irq_status & (IRQSTATUS_SCSI | IRQSTATUS_FIFO)) == 0) {
return IRQ_HANDLED;
@@ -1129,16 +1134,18 @@ static irqreturn_t nspintr(int irq, void *dev_id)
//nsp_dbg(NSP_DEBUG_INTR, "start scsi seq");
/* normal disconnect */
- if (((tmpSC->SCp.phase == PH_MSG_IN) || (tmpSC->SCp.phase == PH_MSG_OUT)) &&
- (irq_phase & LATCHED_BUS_FREE) != 0 ) {
+ if ((scsi_pointer->phase == PH_MSG_IN ||
+ scsi_pointer->phase == PH_MSG_OUT) &&
+ (irq_phase & LATCHED_BUS_FREE) != 0) {
nsp_dbg(NSP_DEBUG_INTR, "normal disconnect irq_status=0x%x, phase=0x%x, irq_phase=0x%x", irq_status, phase, irq_phase);
//*sync_neg = SYNC_NOT_YET;
- if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) { /* all command complete and return status */
- tmpSC->result = (DID_OK << 16) |
- ((tmpSC->SCp.Message & 0xff) << 8) |
- ((tmpSC->SCp.Status & 0xff) << 0);
+ /* all command complete and return status */
+ if (scsi_pointer->Message == COMMAND_COMPLETE) {
+ tmpSC->result = (DID_OK << 16) |
+ ((scsi_pointer->Message & 0xff) << 8) |
+ ((scsi_pointer->Status & 0xff) << 0);
nsp_dbg(NSP_DEBUG_INTR, "command complete result=0x%x", tmpSC->result);
nsp_scsi_done(tmpSC);
@@ -1167,7 +1174,7 @@ static irqreturn_t nspintr(int irq, void *dev_id)
return IRQ_HANDLED;
}
- tmpSC->SCp.phase = PH_COMMAND;
+ scsi_pointer->phase = PH_COMMAND;
nsp_nexus(tmpSC);
@@ -1183,8 +1190,8 @@ static irqreturn_t nspintr(int irq, void *dev_id)
case BUSPHASE_DATA_OUT:
nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_DATA_OUT");
- tmpSC->SCp.phase = PH_DATA;
- tmpSC->SCp.have_data_in = IO_OUT;
+ scsi_pointer->phase = PH_DATA;
+ scsi_pointer->have_data_in = IO_OUT;
nsp_pio_write(tmpSC);
@@ -1193,8 +1200,8 @@ static irqreturn_t nspintr(int irq, void *dev_id)
case BUSPHASE_DATA_IN:
nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_DATA_IN");
- tmpSC->SCp.phase = PH_DATA;
- tmpSC->SCp.have_data_in = IO_IN;
+ scsi_pointer->phase = PH_DATA;
+ scsi_pointer->have_data_in = IO_IN;
nsp_pio_read(tmpSC);
@@ -1204,10 +1211,11 @@ static irqreturn_t nspintr(int irq, void *dev_id)
nsp_dataphase_bypass(tmpSC);
nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_STATUS");
- tmpSC->SCp.phase = PH_STATUS;
+ scsi_pointer->phase = PH_STATUS;
- tmpSC->SCp.Status = nsp_index_read(base, SCSIDATAWITHACK);
- nsp_dbg(NSP_DEBUG_INTR, "message=0x%x status=0x%x", tmpSC->SCp.Message, tmpSC->SCp.Status);
+ scsi_pointer->Status = nsp_index_read(base, SCSIDATAWITHACK);
+ nsp_dbg(NSP_DEBUG_INTR, "message=0x%x status=0x%x",
+ scsi_pointer->Message, scsi_pointer->Status);
break;
@@ -1217,21 +1225,21 @@ static irqreturn_t nspintr(int irq, void *dev_id)
goto timer_out;
}
- tmpSC->SCp.phase = PH_MSG_OUT;
+ scsi_pointer->phase = PH_MSG_OUT;
//*sync_neg = SYNC_NOT_YET;
data->MsgLen = i = 0;
- data->MsgBuffer[i] = IDENTIFY(TRUE, lun); i++;
+ data->MsgBuffer[i] = IDENTIFY(true, lun); i++;
if (*sync_neg == SYNC_NOT_YET) {
data->Sync[target].SyncPeriod = 0;
data->Sync[target].SyncOffset = 0;
/**/
- data->MsgBuffer[i] = MSG_EXTENDED; i++;
+ data->MsgBuffer[i] = EXTENDED_MESSAGE; i++;
data->MsgBuffer[i] = 3; i++;
- data->MsgBuffer[i] = MSG_EXT_SDTR; i++;
+ data->MsgBuffer[i] = EXTENDED_SDTR; i++;
data->MsgBuffer[i] = 0x0c; i++;
data->MsgBuffer[i] = 15; i++;
/**/
@@ -1250,7 +1258,7 @@ static irqreturn_t nspintr(int irq, void *dev_id)
goto timer_out;
}
- tmpSC->SCp.phase = PH_MSG_IN;
+ scsi_pointer->phase = PH_MSG_IN;
nsp_message_in(tmpSC);
/**/
@@ -1258,9 +1266,9 @@ static irqreturn_t nspintr(int irq, void *dev_id)
//nsp_dbg(NSP_DEBUG_INTR, "sync target=%d,lun=%d",target,lun);
if (data->MsgLen >= 5 &&
- data->MsgBuffer[0] == MSG_EXTENDED &&
+ data->MsgBuffer[0] == EXTENDED_MESSAGE &&
data->MsgBuffer[1] == 3 &&
- data->MsgBuffer[2] == MSG_EXT_SDTR ) {
+ data->MsgBuffer[2] == EXTENDED_SDTR ) {
data->Sync[target].SyncPeriod = data->MsgBuffer[3];
data->Sync[target].SyncOffset = data->MsgBuffer[4];
//nsp_dbg(NSP_DEBUG_INTR, "sync ok, %d %d", data->MsgBuffer[3], data->MsgBuffer[4]);
@@ -1278,13 +1286,14 @@ static irqreturn_t nspintr(int irq, void *dev_id)
tmp = -1;
for (i = 0; i < data->MsgLen; i++) {
tmp = data->MsgBuffer[i];
- if (data->MsgBuffer[i] == MSG_EXTENDED) {
+ if (data->MsgBuffer[i] == EXTENDED_MESSAGE) {
i += (1 + data->MsgBuffer[i+1]);
}
}
- tmpSC->SCp.Message = tmp;
+ scsi_pointer->Message = tmp;
- nsp_dbg(NSP_DEBUG_INTR, "message=0x%x len=%d", tmpSC->SCp.Message, data->MsgLen);
+ nsp_dbg(NSP_DEBUG_INTR, "message=0x%x len=%d",
+ scsi_pointer->Message, data->MsgLen);
show_message(data);
break;
@@ -1364,115 +1373,85 @@ static const char *nsp_info(struct Scsi_Host *shpnt)
return data->nspinfo;
}
-#undef SPRINTF
-#define SPRINTF(args...) \
- do { \
- if(length > (pos - buffer)) { \
- pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
- nsp_dbg(NSP_DEBUG_PROC, "buffer=0x%p pos=0x%p length=%d %d\n", buffer, pos, length, length - (pos - buffer));\
- } \
- } while(0)
-
-static int nsp_proc_info(struct Scsi_Host *host, char *buffer, char **start,
- off_t offset, int length, int inout)
+static int nsp_show_info(struct seq_file *m, struct Scsi_Host *host)
{
int id;
- char *pos = buffer;
- int thislength;
int speed;
unsigned long flags;
nsp_hw_data *data;
int hostno;
- if (inout) {
- return -EINVAL;
- }
-
hostno = host->host_no;
data = (nsp_hw_data *)host->hostdata;
+ seq_puts(m, "NinjaSCSI status\n\n"
+ "Driver version: $Revision: 1.23 $\n");
+ seq_printf(m, "SCSI host No.: %d\n", hostno);
+ seq_printf(m, "IRQ: %d\n", host->irq);
+ seq_printf(m, "IO: 0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
+ seq_printf(m, "MMIO(virtual address): 0x%lx-0x%lx\n", host->base, host->base + data->MmioLength - 1);
+ seq_printf(m, "sg_tablesize: %d\n", host->sg_tablesize);
- SPRINTF("NinjaSCSI status\n\n");
- SPRINTF("Driver version: $Revision: 1.23 $\n");
- SPRINTF("SCSI host No.: %d\n", hostno);
- SPRINTF("IRQ: %d\n", host->irq);
- SPRINTF("IO: 0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
- SPRINTF("MMIO(virtual address): 0x%lx-0x%lx\n", host->base, host->base + data->MmioLength - 1);
- SPRINTF("sg_tablesize: %d\n", host->sg_tablesize);
-
- SPRINTF("burst transfer mode: ");
+ seq_puts(m, "burst transfer mode: ");
switch (nsp_burst_mode) {
case BURST_IO8:
- SPRINTF("io8");
+ seq_puts(m, "io8");
break;
case BURST_IO32:
- SPRINTF("io32");
+ seq_puts(m, "io32");
break;
case BURST_MEM32:
- SPRINTF("mem32");
+ seq_puts(m, "mem32");
break;
default:
- SPRINTF("???");
+ seq_puts(m, "???");
break;
}
- SPRINTF("\n");
+ seq_putc(m, '\n');
spin_lock_irqsave(&(data->Lock), flags);
- SPRINTF("CurrentSC: 0x%p\n\n", data->CurrentSC);
+ seq_printf(m, "CurrentSC: 0x%p\n\n", data->CurrentSC);
spin_unlock_irqrestore(&(data->Lock), flags);
- SPRINTF("SDTR status\n");
+ seq_puts(m, "SDTR status\n");
for(id = 0; id < ARRAY_SIZE(data->Sync); id++) {
- SPRINTF("id %d: ", id);
+ seq_printf(m, "id %d: ", id);
if (id == host->this_id) {
- SPRINTF("----- NinjaSCSI-3 host adapter\n");
+ seq_puts(m, "----- NinjaSCSI-3 host adapter\n");
continue;
}
switch(data->Sync[id].SyncNegotiation) {
case SYNC_OK:
- SPRINTF(" sync");
+ seq_puts(m, " sync");
break;
case SYNC_NG:
- SPRINTF("async");
+ seq_puts(m, "async");
break;
case SYNC_NOT_YET:
- SPRINTF(" none");
+ seq_puts(m, " none");
break;
default:
- SPRINTF("?????");
+ seq_puts(m, "?????");
break;
}
if (data->Sync[id].SyncPeriod != 0) {
speed = 1000000 / (data->Sync[id].SyncPeriod * 4);
- SPRINTF(" transfer %d.%dMB/s, offset %d",
+ seq_printf(m, " transfer %d.%dMB/s, offset %d",
speed / 1000,
speed % 1000,
data->Sync[id].SyncOffset
);
}
- SPRINTF("\n");
+ seq_putc(m, '\n');
}
-
- thislength = pos - (buffer + offset);
-
- if(thislength < 0) {
- *start = NULL;
- return 0;
- }
-
-
- thislength = min(thislength, length);
- *start = buffer + offset;
-
- return thislength;
+ return 0;
}
-#undef SPRINTF
/*---------------------------------------------------------------*/
/* error handler */
@@ -1592,8 +1571,11 @@ static int nsp_cs_config_check(struct pcmcia_device *p_dev, void *priv_data)
goto next_entry;
data->MmioAddress = (unsigned long)
- ioremap_nocache(p_dev->resource[2]->start,
+ ioremap(p_dev->resource[2]->start,
resource_size(p_dev->resource[2]));
+ if (!data->MmioAddress)
+ goto next_entry;
+
data->MmioLength = resource_size(p_dev->resource[2]);
}
/* If we got this far, we're cool! */
@@ -1648,9 +1630,7 @@ static int nsp_cs_config(struct pcmcia_device *link)
nsp_dbg(NSP_DEBUG_INIT, "I/O[0x%x+0x%x] IRQ %d",
data->BaseAddress, data->NumAddress, data->IrqNumber);
- if(nsphw_init(data) == FALSE) {
- goto cs_failed;
- }
+ nsphw_init(data);
host = nsp_detect(&nsp_driver_template);
@@ -1773,19 +1753,6 @@ static struct pcmcia_driver nsp_driver = {
.suspend = nsp_cs_suspend,
.resume = nsp_cs_resume,
};
-
-static int __init nsp_cs_init(void)
-{
- return pcmcia_register_driver(&nsp_driver);
-}
-
-static void __exit nsp_cs_exit(void)
-{
- pcmcia_unregister_driver(&nsp_driver);
-}
-
-
-module_init(nsp_cs_init)
-module_exit(nsp_cs_exit)
+module_pcmcia_driver(nsp_driver);
/* end */
diff --git a/drivers/scsi/pcmcia/nsp_cs.h b/drivers/scsi/pcmcia/nsp_cs.h
index 7fc9a9d0a448..e1ee8ef90ad3 100644
--- a/drivers/scsi/pcmcia/nsp_cs.h
+++ b/drivers/scsi/pcmcia/nsp_cs.h
@@ -292,13 +292,8 @@ static int nsp_cs_config (struct pcmcia_device *link);
/* Linux SCSI subsystem specific functions */
static struct Scsi_Host *nsp_detect (struct scsi_host_template *sht);
static const char *nsp_info (struct Scsi_Host *shpnt);
-static int nsp_proc_info (
- struct Scsi_Host *host,
- char *buffer,
- char **start,
- off_t offset,
- int length,
- int inout);
+static int nsp_show_info (struct seq_file *m,
+ struct Scsi_Host *host);
static int nsp_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *SCpnt);
/* Error handler */
@@ -309,8 +304,8 @@ static int nsp_eh_host_reset (struct scsi_cmnd *SCpnt);
static int nsp_bus_reset (nsp_hw_data *data);
/* */
-static int nsphw_init (nsp_hw_data *data);
-static int nsphw_start_selection(struct scsi_cmnd *SCpnt);
+static void nsphw_init (nsp_hw_data *data);
+static bool nsphw_start_selection(struct scsi_cmnd *SCpnt);
static void nsp_start_timer (struct scsi_cmnd *SCpnt, int time);
static int nsp_fifo_count (struct scsi_cmnd *SCpnt);
static void nsp_pio_read (struct scsi_cmnd *SCpnt);
@@ -325,16 +320,12 @@ static int nsp_expect_signal (struct scsi_cmnd *SCpnt,
unsigned char mask);
static int nsp_xfer (struct scsi_cmnd *SCpnt, int phase);
static int nsp_dataphase_bypass (struct scsi_cmnd *SCpnt);
-static int nsp_reselected (struct scsi_cmnd *SCpnt);
+static void nsp_reselected (struct scsi_cmnd *SCpnt);
static struct Scsi_Host *nsp_detect(struct scsi_host_template *sht);
/* Interrupt handler */
//static irqreturn_t nspintr(int irq, void *dev_id);
-/* Module entry point*/
-static int __init nsp_cs_init(void);
-static void __exit nsp_cs_exit(void);
-
/* Debug */
#ifdef NSP_DEBUG
static void show_command (struct scsi_cmnd *SCpnt);
@@ -379,19 +370,8 @@ enum _burst_mode {
BURST_MEM32 = 2,
};
-/**************************************************************************
- * SCSI messaage
- */
-#define MSG_COMMAND_COMPLETE 0x00
-#define MSG_EXTENDED 0x01
-#define MSG_ABORT 0x06
-#define MSG_NO_OPERATION 0x08
-#define MSG_BUS_DEVICE_RESET 0x0c
-
-#define MSG_EXT_SDTR 0x01
-
/* scatter-gather table */
-# define BUFFER_ADDR ((char *)((sg_virt(SCpnt->SCp.buffer))))
+#define BUFFER_ADDR(SCpnt) ((char *)(sg_virt(nsp_priv(SCpnt)->buffer)))
#endif /*__nsp_cs__*/
/* end */
diff --git a/drivers/scsi/pcmcia/nsp_debug.c b/drivers/scsi/pcmcia/nsp_debug.c
index 6aa7d269d3b3..23b68dd26f74 100644
--- a/drivers/scsi/pcmcia/nsp_debug.c
+++ b/drivers/scsi/pcmcia/nsp_debug.c
@@ -145,7 +145,7 @@ static void show_command(struct scsi_cmnd *SCpnt)
static void show_phase(struct scsi_cmnd *SCpnt)
{
- int i = SCpnt->SCp.phase;
+ int i = nsp_scsi_pointer(SCpnt)->phase;
char *ph[] = {
"PH_UNDETERMINED",
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
index bcaf89fe0c9e..310d0b6586a6 100644
--- a/drivers/scsi/pcmcia/qlogic_stub.c
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -38,14 +38,17 @@
#include <linux/string.h>
#include <linux/ioport.h>
#include <asm/io.h>
-#include <scsi/scsi.h>
#include <linux/major.h>
#include <linux/blkdev.h>
-#include <scsi/scsi_ioctl.h>
#include <linux/interrupt.h>
-#include "scsi.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_tcq.h>
#include "../qlogicfas408.h"
#include <pcmcia/cistpl.h>
@@ -67,13 +70,12 @@ static struct scsi_host_template qlogicfas_driver_template = {
.info = qlogicfas408_info,
.queuecommand = qlogicfas408_queuecommand,
.eh_abort_handler = qlogicfas408_abort,
- .eh_bus_reset_handler = qlogicfas408_bus_reset,
+ .eh_host_reset_handler = qlogicfas408_host_reset,
.bios_param = qlogicfas408_biosparam,
.can_queue = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = DISABLE_CLUSTERING,
+ .dma_boundary = PAGE_SIZE - 1,
};
/*====================================================================*/
@@ -255,8 +257,12 @@ static void qlogic_release(struct pcmcia_device *link)
static int qlogic_resume(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
+ int ret;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ return ret;
- pcmcia_enable_device(link);
if ((info->manf_id == MANFID_MACNICA) ||
(info->manf_id == MANFID_PIONEER) ||
(info->manf_id == 0x0098)) {
@@ -265,7 +271,7 @@ static int qlogic_resume(struct pcmcia_device *link)
outb(0x04, link->resource[0]->start + 0xd);
}
/* Ugggglllyyyy!!! */
- qlogicfas408_bus_reset(NULL);
+ qlogicfas408_host_reset(NULL);
return 0;
}
@@ -301,18 +307,7 @@ static struct pcmcia_driver qlogic_cs_driver = {
.resume = qlogic_resume,
};
-static int __init init_qlogic_cs(void)
-{
- return pcmcia_register_driver(&qlogic_cs_driver);
-}
-
-static void __exit exit_qlogic_cs(void)
-{
- pcmcia_unregister_driver(&qlogic_cs_driver);
-}
-
MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
MODULE_LICENSE("GPL");
-module_init(init_qlogic_cs);
-module_exit(exit_qlogic_cs);
+module_pcmcia_driver(qlogic_cs_driver);
diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c
index f5b52731abd9..a3b505240351 100644
--- a/drivers/scsi/pcmcia/sym53c500_cs.c
+++ b/drivers/scsi/pcmcia/sym53c500_cs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sym53c500_cs.c Bob Tracy (rct@frus.com)
*
@@ -25,16 +26,6 @@
* Original by Tom Corner (tcorner@via.at) was adapted from a
* driver for the Qlogic SCSI card written by
* David Hinds (dhinds@allegro.stanford.edu).
-*
-* This program is free software; you can redistribute it and/or modify it
-* under the terms of the GNU General Public License as published by the
-* Free Software Foundation; either version 2, or (at your option) any
-* later version.
-*
-* This program is distributed in the hope that it will be useful, but
-* WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
*/
#define SYM53C500_DEBUG 0
@@ -201,6 +192,12 @@ struct sym53c500_data {
int fast_pio;
};
+struct sym53c500_cmd_priv {
+ int status;
+ int message;
+ int phase;
+};
+
enum Phase {
idle,
data_out,
@@ -360,6 +357,7 @@ SYM53C500_intr(int irq, void *dev_id)
struct sym53c500_data *data =
(struct sym53c500_data *)dev->hostdata;
struct scsi_cmnd *curSC = data->current_SC;
+ struct sym53c500_cmd_priv *scp = scsi_cmd_priv(curSC);
int fast_pio = data->fast_pio;
spin_lock_irqsave(dev->host_lock, flags);
@@ -406,11 +404,11 @@ SYM53C500_intr(int irq, void *dev_id)
if (int_reg & 0x20) { /* Disconnect */
DEB(printk("SYM53C500: disconnect intr received\n"));
- if (curSC->SCp.phase != message_in) { /* Unexpected disconnect */
+ if (scp->phase != message_in) { /* Unexpected disconnect */
curSC->result = DID_NO_CONNECT << 16;
} else { /* Command complete, return status and message */
- curSC->result = (curSC->SCp.Status & 0xff)
- | ((curSC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ curSC->result = (scp->status & 0xff) |
+ ((scp->message & 0xff) << 8) | (DID_OK << 16);
}
goto idle_out;
}
@@ -421,7 +419,7 @@ SYM53C500_intr(int irq, void *dev_id)
struct scatterlist *sg;
int i;
- curSC->SCp.phase = data_out;
+ scp->phase = data_out;
VDEB(printk("SYM53C500: Data-Out phase\n"));
outb(FLUSH_FIFO, port_base + CMD_REG);
LOAD_DMA_COUNT(port_base, scsi_bufflen(curSC)); /* Max transfer size */
@@ -440,7 +438,7 @@ SYM53C500_intr(int irq, void *dev_id)
struct scatterlist *sg;
int i;
- curSC->SCp.phase = data_in;
+ scp->phase = data_in;
VDEB(printk("SYM53C500: Data-In phase\n"));
outb(FLUSH_FIFO, port_base + CMD_REG);
LOAD_DMA_COUNT(port_base, scsi_bufflen(curSC)); /* Max transfer size */
@@ -455,12 +453,12 @@ SYM53C500_intr(int irq, void *dev_id)
break;
case 0x02: /* COMMAND */
- curSC->SCp.phase = command_ph;
+ scp->phase = command_ph;
printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n");
break;
case 0x03: /* STATUS */
- curSC->SCp.phase = status_ph;
+ scp->phase = status_ph;
VDEB(printk("SYM53C500: Status phase\n"));
outb(FLUSH_FIFO, port_base + CMD_REG);
outb(INIT_CMD_COMPLETE, port_base + CMD_REG);
@@ -473,22 +471,22 @@ SYM53C500_intr(int irq, void *dev_id)
case 0x06: /* MESSAGE-OUT */
DEB(printk("SYM53C500: Message-Out phase\n"));
- curSC->SCp.phase = message_out;
+ scp->phase = message_out;
outb(SET_ATN, port_base + CMD_REG); /* Reject the message */
outb(MSG_ACCEPT, port_base + CMD_REG);
break;
case 0x07: /* MESSAGE-IN */
VDEB(printk("SYM53C500: Message-In phase\n"));
- curSC->SCp.phase = message_in;
+ scp->phase = message_in;
- curSC->SCp.Status = inb(port_base + SCSI_FIFO);
- curSC->SCp.Message = inb(port_base + SCSI_FIFO);
+ scp->status = inb(port_base + SCSI_FIFO);
+ scp->message = inb(port_base + SCSI_FIFO);
VDEB(printk("SCSI FIFO size=%d\n", inb(port_base + FIFO_FLAGS) & 0x1f));
- DEB(printk("Status = %02x Message = %02x\n", curSC->SCp.Status, curSC->SCp.Message));
+ DEB(printk("Status = %02x Message = %02x\n", scp->status, scp->message));
- if (curSC->SCp.Message == SAVE_POINTERS || curSC->SCp.Message == DISCONNECT) {
+ if (scp->message == SAVE_POINTERS || scp->message == DISCONNECT) {
outb(SET_ATN, port_base + CMD_REG); /* Reject message */
DEB(printk("Discarding SAVE_POINTERS message\n"));
}
@@ -500,8 +498,8 @@ out:
return IRQ_HANDLED;
idle_out:
- curSC->SCp.phase = idle;
- curSC->scsi_done(curSC);
+ scp->phase = idle;
+ scsi_done(curSC);
goto out;
}
@@ -546,9 +544,9 @@ SYM53C500_info(struct Scsi_Host *SChost)
return (info_msg);
}
-static int
-SYM53C500_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+static int SYM53C500_queue_lck(struct scsi_cmnd *SCpnt)
{
+ struct sym53c500_cmd_priv *scp = scsi_cmd_priv(SCpnt);
int i;
int port_base = SCpnt->device->host->io_port;
struct sym53c500_data *data =
@@ -558,17 +556,16 @@ SYM53C500_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id,
- SCpnt->device->lun, scsi_bufflen(SCpnt)));
+ (u8)SCpnt->device->lun, scsi_bufflen(SCpnt)));
VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
VDEB(printk("\n"));
data->current_SC = SCpnt;
- data->current_SC->scsi_done = done;
- data->current_SC->SCp.phase = command_ph;
- data->current_SC->SCp.Status = 0;
- data->current_SC->SCp.Message = 0;
+ scp->phase = command_ph;
+ scp->status = 0;
+ scp->message = 0;
/* We are locked here already by the mid layer */
REG0(port_base);
@@ -600,7 +597,7 @@ SYM53C500_host_reset(struct scsi_cmnd *SCpnt)
static int
SYM53C500_biosparm(struct scsi_device *disk,
- struct block_device *dev,
+ struct gendisk *unused,
sector_t capacity, int *info_array)
{
int size;
@@ -661,15 +658,17 @@ static struct device_attribute SYM53C500_pio_attr = {
.store = SYM53C500_store_pio,
};
-static struct device_attribute *SYM53C500_shost_attrs[] = {
- &SYM53C500_pio_attr,
+static struct attribute *SYM53C500_shost_attrs[] = {
+ &SYM53C500_pio_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(SYM53C500_shost);
+
/*
* scsi_host_template initializer
*/
-static struct scsi_host_template sym53c500_driver_template = {
+static const struct scsi_host_template sym53c500_driver_template = {
.module = THIS_MODULE,
.name = "SYM53C500",
.info = SYM53C500_info,
@@ -680,9 +679,8 @@ static struct scsi_host_template sym53c500_driver_template = {
.can_queue = 1,
.this_id = 7,
.sg_tablesize = 32,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = SYM53C500_shost_attrs
+ .shost_groups = SYM53C500_shost_groups,
+ .cmd_size = sizeof(struct sym53c500_cmd_priv),
};
static int SYM53C500_config_check(struct pcmcia_device *p_dev, void *priv_data)
@@ -704,7 +702,7 @@ SYM53C500_config(struct pcmcia_device *link)
int ret;
int irq_level, port_base;
struct Scsi_Host *host;
- struct scsi_host_template *tpnt = &sym53c500_driver_template;
+ const struct scsi_host_template *tpnt = &sym53c500_driver_template;
struct sym53c500_data *data;
dev_dbg(&link->dev, "SYM53C500_config\n");
@@ -881,18 +879,4 @@ static struct pcmcia_driver sym53c500_cs_driver = {
.id_table = sym53c500_ids,
.resume = sym53c500_resume,
};
-
-static int __init
-init_sym53c500_cs(void)
-{
- return pcmcia_register_driver(&sym53c500_cs_driver);
-}
-
-static void __exit
-exit_sym53c500_cs(void)
-{
- pcmcia_unregister_driver(&sym53c500_cs_driver);
-}
-
-module_init(init_sym53c500_cs);
-module_exit(exit_sym53c500_cs);
+module_pcmcia_driver(sym53c500_cs_driver);
diff --git a/drivers/scsi/pm8001/Makefile b/drivers/scsi/pm8001/Makefile
index 52f04296171c..bbb51b7312f1 100644
--- a/drivers/scsi/pm8001/Makefile
+++ b/drivers/scsi/pm8001/Makefile
@@ -1,12 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Kernel configuration file for the PM8001 SAS/SATA 8x6G based HBA driver
#
# Copyright (C) 2008-2009 USI Co., Ltd.
-obj-$(CONFIG_SCSI_PM8001) += pm8001.o
-pm8001-y += pm8001_init.o \
+obj-$(CONFIG_SCSI_PM8001) += pm80xx.o
+
+CFLAGS_pm80xx_tracepoints.o := -I$(src)
+
+pm80xx-y += pm8001_init.o \
pm8001_sas.o \
pm8001_ctl.o \
- pm8001_hwi.o
-
+ pm8001_hwi.o \
+ pm80xx_hwi.o \
+ pm80xx_tracepoints.o
diff --git a/drivers/scsi/pm8001/pm8001_chips.h b/drivers/scsi/pm8001/pm8001_chips.h
index 4efa4d0950e5..9241c7826034 100644
--- a/drivers/scsi/pm8001/pm8001_chips.h
+++ b/drivers/scsi/pm8001/pm8001_chips.h
@@ -46,9 +46,9 @@ static inline u32 pm8001_read_32(void *virt_addr)
return *((u32 *)virt_addr);
}
-static inline void pm8001_write_32(void *addr, u32 offset, u32 val)
+static inline void pm8001_write_32(void *addr, u32 offset, __le32 val)
{
- *((u32 *)(addr + offset)) = val;
+ *((__le32 *)(addr + offset)) = val;
}
static inline u32 pm8001_cr32(struct pm8001_hba_info *pm8001_ha, u32 bar,
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index 45bc197bc22f..cbfda8c04e95 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -1,5 +1,5 @@
/*
- * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ * PMC-Sierra 8001/8081/8088/8089 SAS/SATA based host adapters driver
*
* Copyright (c) 2008-2009 USI Co., Ltd.
* All rights reserved.
@@ -41,12 +41,14 @@
#include <linux/slab.h>
#include "pm8001_sas.h"
#include "pm8001_ctl.h"
+#include "pm8001_chips.h"
/* scsi host attributes */
/**
* pm8001_ctl_mpi_interface_rev_show - MPI interface revision number
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -58,15 +60,41 @@ static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev,
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return snprintf(buf, PAGE_SIZE, "%d\n",
- pm8001_ha->main_cfg_tbl.interface_rev);
+ if (pm8001_ha->chip_id == chip_8001) {
+ return sysfs_emit(buf, "%d\n",
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.interface_rev);
+ } else {
+ return sysfs_emit(buf, "%d\n",
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.interface_rev);
+ }
}
static
DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL);
/**
+ * controller_fatal_error_show - check controller is under fatal err
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t controller_fatal_error_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return sysfs_emit(buf, "%d\n",
+ pm8001_ha->controller_fatal_error);
+}
+static DEVICE_ATTR_RO(controller_fatal_error);
+
+/**
* pm8001_ctl_fw_version_show - firmware version
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -78,16 +106,79 @@ static ssize_t pm8001_ctl_fw_version_show(struct device *cdev,
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n",
- (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 24),
- (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 16),
- (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 8),
- (u8)(pm8001_ha->main_cfg_tbl.firmware_rev));
+ if (pm8001_ha->chip_id == chip_8001) {
+ return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n",
+ (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 24),
+ (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 16),
+ (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 8),
+ (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev));
+ } else {
+ return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n",
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 24),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 16),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 8),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev));
+ }
}
static DEVICE_ATTR(fw_version, S_IRUGO, pm8001_ctl_fw_version_show, NULL);
+
+/**
+ * pm8001_ctl_ila_version_show - ila version
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_ila_version_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ if (pm8001_ha->chip_id != chip_8001) {
+ return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n",
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 24),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 16),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 8),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version));
+ }
+ return 0;
+}
+static DEVICE_ATTR(ila_version, 0444, pm8001_ctl_ila_version_show, NULL);
+
+/**
+ * pm8001_ctl_inactive_fw_version_show - Inactive firmware version number
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_inactive_fw_version_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ if (pm8001_ha->chip_id != chip_8001) {
+ return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n",
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 24),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 16),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 8),
+ (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version));
+ }
+ return 0;
+}
+static
+DEVICE_ATTR(inc_fw_ver, 0444, pm8001_ctl_inactive_fw_version_show, NULL);
+
/**
* pm8001_ctl_max_out_io_show - max outstanding io supported
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -99,13 +190,19 @@ static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev,
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return snprintf(buf, PAGE_SIZE, "%d\n",
- pm8001_ha->main_cfg_tbl.max_out_io);
+ if (pm8001_ha->chip_id == chip_8001) {
+ return sysfs_emit(buf, "%d\n",
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.max_out_io);
+ } else {
+ return sysfs_emit(buf, "%d\n",
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_out_io);
+ }
}
static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL);
/**
* pm8001_ctl_max_devices_show - max devices support
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -117,14 +214,20 @@ static ssize_t pm8001_ctl_max_devices_show(struct device *cdev,
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return snprintf(buf, PAGE_SIZE, "%04d\n",
- (u16)(pm8001_ha->main_cfg_tbl.max_sgl >> 16));
+ if (pm8001_ha->chip_id == chip_8001) {
+ return sysfs_emit(buf, "%04d\n",
+ (u16)(pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl >> 16));
+ } else {
+ return sysfs_emit(buf, "%04d\n",
+ (u16)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl >> 16));
+ }
}
static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL);
/**
* pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no
* hardware limitation
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -136,8 +239,13 @@ static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev,
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return snprintf(buf, PAGE_SIZE, "%04d\n",
- pm8001_ha->main_cfg_tbl.max_sgl & 0x0000FFFF);
+ if (pm8001_ha->chip_id == chip_8001) {
+ return sysfs_emit(buf, "%04d\n",
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl & 0x0000FFFF);
+ } else {
+ return sysfs_emit(buf, "%04d\n",
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl & 0x0000FFFF);
+ }
}
static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL);
@@ -162,6 +270,7 @@ show_sas_spec_support_status(unsigned int mode, char *buf)
/**
* pm8001_ctl_sas_spec_support_show - sas spec supported
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -173,15 +282,23 @@ static ssize_t pm8001_ctl_sas_spec_support_show(struct device *cdev,
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- mode = (pm8001_ha->main_cfg_tbl.ctrl_cap_flag & 0xfe000000)>>25;
+ /* fe000000 means supports SAS2.1 */
+ if (pm8001_ha->chip_id == chip_8001)
+ mode = (pm8001_ha->main_cfg_tbl.pm8001_tbl.ctrl_cap_flag &
+ 0xfe000000)>>25;
+ else
+ /* fe000000 means supports SAS2.1 */
+ mode = (pm8001_ha->main_cfg_tbl.pm80xx_tbl.ctrl_cap_flag &
+ 0xfe000000)>>25;
return show_sas_spec_support_status(mode, buf);
}
static DEVICE_ATTR(sas_spec_support, S_IRUGO,
pm8001_ctl_sas_spec_support_show, NULL);
/**
- * pm8001_ctl_sas_address_show - sas address
+ * pm8001_ctl_host_sas_address_show - sas address
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* This is the controller sas address
@@ -194,7 +311,7 @@ static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev,
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ return sysfs_emit(buf, "0x%016llx\n",
be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr));
}
static DEVICE_ATTR(host_sas_address, S_IRUGO,
@@ -203,6 +320,7 @@ static DEVICE_ATTR(host_sas_address, S_IRUGO,
/**
* pm8001_ctl_logging_level_show - logging level
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read/write' shost attribute.
@@ -214,8 +332,9 @@ static ssize_t pm8001_ctl_logging_level_show(struct device *cdev,
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return snprintf(buf, PAGE_SIZE, "%08xh\n", pm8001_ha->logging_level);
+ return sysfs_emit(buf, "%08xh\n", pm8001_ha->logging_level);
}
+
static ssize_t pm8001_ctl_logging_level_store(struct device *cdev,
struct device_attribute *attr, const char *buf, size_t count)
{
@@ -236,6 +355,7 @@ static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
/**
* pm8001_ctl_aap_log_show - aap1 event log
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -246,32 +366,161 @@ static ssize_t pm8001_ctl_aap_log_show(struct device *cdev,
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ u8 *ptr = (u8 *)pm8001_ha->memoryMap.region[AAP1].virt_ptr;
int i;
-#define AAP1_MEMMAP(r, c) \
- (*(u32 *)((u8*)pm8001_ha->memoryMap.region[AAP1].virt_ptr + (r) * 32 \
- + (c)))
char *str = buf;
int max = 2;
for (i = 0; i < max; i++) {
str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
"0x%08x 0x%08x\n",
- AAP1_MEMMAP(i, 0),
- AAP1_MEMMAP(i, 4),
- AAP1_MEMMAP(i, 8),
- AAP1_MEMMAP(i, 12),
- AAP1_MEMMAP(i, 16),
- AAP1_MEMMAP(i, 20),
- AAP1_MEMMAP(i, 24),
- AAP1_MEMMAP(i, 28));
+ pm8001_ctl_aap1_memmap(ptr, i, 0),
+ pm8001_ctl_aap1_memmap(ptr, i, 4),
+ pm8001_ctl_aap1_memmap(ptr, i, 8),
+ pm8001_ctl_aap1_memmap(ptr, i, 12),
+ pm8001_ctl_aap1_memmap(ptr, i, 16),
+ pm8001_ctl_aap1_memmap(ptr, i, 20),
+ pm8001_ctl_aap1_memmap(ptr, i, 24),
+ pm8001_ctl_aap1_memmap(ptr, i, 28));
}
return str - buf;
}
static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL);
/**
- * pm8001_ctl_aap_log_show - IOP event log
+ * pm8001_ctl_ib_queue_log_show - Out bound Queue log
+ * @cdev:pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_ib_queue_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ int offset;
+ char *str = buf;
+ int start = 0;
+ u32 ib_offset = pm8001_ha->ib_offset;
+ u32 queue_size = pm8001_ha->max_q_num * PM8001_MPI_QUEUE * 128;
+#define IB_MEMMAP(c) \
+ (*(u32 *)((u8 *)pm8001_ha-> \
+ memoryMap.region[ib_offset].virt_ptr + \
+ pm8001_ha->evtlog_ib_offset + (c)))
+
+ for (offset = 0; offset < IB_OB_READ_TIMES; offset++) {
+ str += sprintf(str, "0x%08x\n", IB_MEMMAP(start));
+ start = start + 4;
+ }
+ pm8001_ha->evtlog_ib_offset += SYSFS_OFFSET;
+ if (((pm8001_ha->evtlog_ib_offset) % queue_size) == 0)
+ pm8001_ha->evtlog_ib_offset = 0;
+
+ return str - buf;
+}
+
+static DEVICE_ATTR(ib_log, S_IRUGO, pm8001_ctl_ib_queue_log_show, NULL);
+/**
+ * pm8001_ctl_ob_queue_log_show - Out bound Queue log
+ * @cdev:pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+
+static ssize_t pm8001_ctl_ob_queue_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ int offset;
+ char *str = buf;
+ int start = 0;
+ u32 ob_offset = pm8001_ha->ob_offset;
+ u32 queue_size = pm8001_ha->max_q_num * PM8001_MPI_QUEUE * 128;
+#define OB_MEMMAP(c) \
+ (*(u32 *)((u8 *)pm8001_ha-> \
+ memoryMap.region[ob_offset].virt_ptr + \
+ pm8001_ha->evtlog_ob_offset + (c)))
+
+ for (offset = 0; offset < IB_OB_READ_TIMES; offset++) {
+ str += sprintf(str, "0x%08x\n", OB_MEMMAP(start));
+ start = start + 4;
+ }
+ pm8001_ha->evtlog_ob_offset += SYSFS_OFFSET;
+ if (((pm8001_ha->evtlog_ob_offset) % queue_size) == 0)
+ pm8001_ha->evtlog_ob_offset = 0;
+
+ return str - buf;
+}
+static DEVICE_ATTR(ob_log, S_IRUGO, pm8001_ctl_ob_queue_log_show, NULL);
+/**
+ * pm8001_ctl_bios_version_show - Bios version Display
+ * @cdev:pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf:the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_bios_version_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ char *str = buf;
+ int bios_index;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct pm8001_ioctl_payload payload;
+
+ pm8001_ha->nvmd_completion = &completion;
+ payload.minor_function = 7;
+ payload.offset = 0;
+ payload.rd_length = 4096;
+ payload.func_specific = kzalloc(4096, GFP_KERNEL);
+ if (!payload.func_specific)
+ return -ENOMEM;
+ if (PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload)) {
+ kfree(payload.func_specific);
+ return -ENOMEM;
+ }
+ wait_for_completion(&completion);
+ for (bios_index = BIOSOFFSET; bios_index < BIOS_OFFSET_LIMIT;
+ bios_index++)
+ str += sprintf(str, "%c",
+ *(payload.func_specific+bios_index));
+ kfree(payload.func_specific);
+ return str - buf;
+}
+static DEVICE_ATTR(bios_version, S_IRUGO, pm8001_ctl_bios_version_show, NULL);
+/**
+ * event_log_size_show - event log size
* @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs read shost attribute.
+ */
+static ssize_t event_log_size_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return sysfs_emit(buf, "%d\n",
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size);
+}
+static DEVICE_ATTR_RO(event_log_size);
+/**
+ * pm8001_ctl_iop_log_show - IOP event log
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
* @buf: the buffer returned
*
* A sysfs 'read-only' shost attribute.
@@ -282,40 +531,126 @@ static ssize_t pm8001_ctl_iop_log_show(struct device *cdev,
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
-#define IOP_MEMMAP(r, c) \
- (*(u32 *)((u8*)pm8001_ha->memoryMap.region[IOP].virt_ptr + (r) * 32 \
- + (c)))
- int i;
char *str = buf;
- int max = 2;
- for (i = 0; i < max; i++) {
- str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
- "0x%08x 0x%08x\n",
- IOP_MEMMAP(i, 0),
- IOP_MEMMAP(i, 4),
- IOP_MEMMAP(i, 8),
- IOP_MEMMAP(i, 12),
- IOP_MEMMAP(i, 16),
- IOP_MEMMAP(i, 20),
- IOP_MEMMAP(i, 24),
- IOP_MEMMAP(i, 28));
+ u32 read_size =
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size / 1024;
+ u32 max_read_times = 32;
+ u32 max_count = (read_size * 1024) / (max_read_times * 4);
+ u32 *temp = (u32 *)pm8001_ha->memoryMap.region[IOP].virt_ptr;
+
+ mutex_lock(&pm8001_ha->iop_log_lock);
+
+ if ((pm8001_ha->iop_log_count % max_count) == 0) {
+ pm8001_ha->iop_log_start = 0;
+ pm8001_ha->iop_log_end = max_read_times;
+ pm8001_ha->iop_log_count = 0;
+ } else {
+ pm8001_ha->iop_log_start = pm8001_ha->iop_log_end;
+ pm8001_ha->iop_log_end = pm8001_ha->iop_log_end + max_read_times;
}
+ for (; pm8001_ha->iop_log_start < pm8001_ha->iop_log_end; pm8001_ha->iop_log_start++)
+ str += sprintf(str, "%08x ", *(temp+pm8001_ha->iop_log_start));
+ pm8001_ha->iop_log_count++;
+ mutex_unlock(&pm8001_ha->iop_log_lock);
return str - buf;
}
static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL);
+/**
+ * pm8001_ctl_fatal_log_show - fatal error logging
+ * @cdev:pointer to embedded class device
+ * @attr: device attribute
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+
+static ssize_t pm8001_ctl_fatal_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t count;
+
+ count = pm80xx_get_fatal_dump(cdev, attr, buf);
+ return count;
+}
+
+static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL);
+
+/**
+ * non_fatal_log_show - non fatal error logging
+ * @cdev:pointer to embedded class device
+ * @attr: device attribute
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t non_fatal_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 count;
+
+ count = pm80xx_get_non_fatal_dump(cdev, attr, buf);
+ return count;
+}
+static DEVICE_ATTR_RO(non_fatal_log);
+
+static ssize_t non_fatal_count_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return sysfs_emit(buf, "%08x\n",
+ pm8001_ha->non_fatal_count);
+}
+
+static ssize_t non_fatal_count_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ int val = 0;
+
+ if (kstrtoint(buf, 16, &val) != 0)
+ return -EINVAL;
+
+ pm8001_ha->non_fatal_count = val;
+ return strlen(buf);
+}
+static DEVICE_ATTR_RW(non_fatal_count);
+
+/**
+ * pm8001_ctl_gsm_log_show - gsm dump collection
+ * @cdev:pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_gsm_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t count;
+
+ count = pm8001_get_gsm_dump(cdev, SYSFS_OFFSET, buf);
+ return count;
+}
+
+static DEVICE_ATTR(gsm_log, S_IRUGO, pm8001_ctl_gsm_log_show, NULL);
+
#define FLASH_CMD_NONE 0x00
#define FLASH_CMD_UPDATE 0x01
#define FLASH_CMD_SET_NVMD 0x02
struct flash_command {
- u8 command[8];
+ u8 command[8] __nonstring;
int code;
};
-static struct flash_command flash_command_table[] =
-{
+static const struct flash_command flash_command_table[] = {
{"set_nvmd", FLASH_CMD_SET_NVMD},
{"update", FLASH_CMD_UPDATE},
{"", FLASH_CMD_NONE} /* Last entry should be NULL. */
@@ -326,8 +661,7 @@ struct error_fw {
int err_code;
};
-static struct error_fw flash_error_table[] =
-{
+static const struct error_fw flash_error_table[] = {
{"Failed to open fw image file", FAIL_OPEN_BIOS_FILE},
{"image header mismatch", FLASH_UPDATE_HDR_ERR},
{"image offset mismatch", FLASH_UPDATE_OFFSET_ERR},
@@ -347,26 +681,32 @@ static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha)
{
struct pm8001_ioctl_payload *payload;
DECLARE_COMPLETION_ONSTACK(completion);
- u8 *ioctlbuffer = NULL;
- u32 length = 0;
- u32 ret = 0;
+ u8 *ioctlbuffer;
+ int ret;
+ u32 length = 1024 * 5 + sizeof(*payload) - 1;
+
+ if (pm8001_ha->fw_image->size > 4096) {
+ pm8001_ha->fw_status = FAIL_FILE_SIZE;
+ return -EFAULT;
+ }
- length = 1024 * 5 + sizeof(*payload) - 1;
ioctlbuffer = kzalloc(length, GFP_KERNEL);
- if (!ioctlbuffer)
+ if (!ioctlbuffer) {
+ pm8001_ha->fw_status = FAIL_OUT_MEMORY;
return -ENOMEM;
- if ((pm8001_ha->fw_image->size <= 0) ||
- (pm8001_ha->fw_image->size > 4096)) {
- ret = FAIL_FILE_SIZE;
- goto out;
}
payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
- memcpy((u8 *)payload->func_specific, (u8 *)pm8001_ha->fw_image->data,
+ memcpy((u8 *)&payload->func_specific, (u8 *)pm8001_ha->fw_image->data,
pm8001_ha->fw_image->size);
- payload->length = pm8001_ha->fw_image->size;
+ payload->wr_length = pm8001_ha->fw_image->size;
payload->id = 0;
+ payload->minor_function = 0x1;
pm8001_ha->nvmd_completion = &completion;
ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload);
+ if (ret) {
+ pm8001_ha->fw_status = FAIL_OUT_MEMORY;
+ goto out;
+ }
wait_for_completion(&completion);
out:
kfree(ioctlbuffer);
@@ -377,78 +717,76 @@ static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha)
{
struct pm8001_ioctl_payload *payload;
DECLARE_COMPLETION_ONSTACK(completion);
- u8 *ioctlbuffer = NULL;
- u32 length = 0;
+ u8 *ioctlbuffer;
struct fw_control_info *fwControl;
- u32 loopNumber, loopcount = 0;
+ __be32 partitionSizeTmp;
+ u32 partitionSize;
+ u32 loopNumber, loopcount;
+ struct pm8001_fw_image_header *image_hdr;
u32 sizeRead = 0;
- u32 partitionSize, partitionSizeTmp;
u32 ret = 0;
- u32 partitionNumber = 0;
- struct pm8001_fw_image_header *image_hdr;
+ u32 length = 1024 * 16 + sizeof(*payload) - 1;
+ u32 fc_len;
+ u8 *read_buf;
- length = 1024 * 16 + sizeof(*payload) - 1;
+ if (pm8001_ha->fw_image->size < 28) {
+ pm8001_ha->fw_status = FAIL_FILE_SIZE;
+ return -EFAULT;
+ }
ioctlbuffer = kzalloc(length, GFP_KERNEL);
- image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data;
- if (!ioctlbuffer)
+ if (!ioctlbuffer) {
+ pm8001_ha->fw_status = FAIL_OUT_MEMORY;
return -ENOMEM;
- if (pm8001_ha->fw_image->size < 28) {
- ret = FAIL_FILE_SIZE;
- goto out;
}
-
+ image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data;
while (sizeRead < pm8001_ha->fw_image->size) {
partitionSizeTmp =
- *(u32 *)((u8 *)&image_hdr->image_length + sizeRead);
+ *(__be32 *)((u8 *)&image_hdr->image_length + sizeRead);
partitionSize = be32_to_cpu(partitionSizeTmp);
- loopcount = (partitionSize + HEADER_LEN)/IOCTL_BUF_SIZE;
- if (loopcount % IOCTL_BUF_SIZE)
- loopcount++;
- if (loopcount == 0)
- loopcount++;
+ loopcount = DIV_ROUND_UP(partitionSize + HEADER_LEN,
+ IOCTL_BUF_SIZE);
for (loopNumber = 0; loopNumber < loopcount; loopNumber++) {
payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
- payload->length = 1024*16;
+ payload->wr_length = 1024*16;
payload->id = 0;
fwControl =
- (struct fw_control_info *)payload->func_specific;
+ (struct fw_control_info *)&payload->func_specific;
fwControl->len = IOCTL_BUF_SIZE; /* IN */
fwControl->size = partitionSize + HEADER_LEN;/* IN */
fwControl->retcode = 0;/* OUT */
fwControl->offset = loopNumber * IOCTL_BUF_SIZE;/*OUT */
- /* for the last chunk of data in case file size is not even with
- 4k, load only the rest*/
- if (((loopcount-loopNumber) == 1) &&
- ((partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE)) {
- fwControl->len =
- (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
- memcpy((u8 *)fwControl->buffer,
- (u8 *)pm8001_ha->fw_image->data + sizeRead,
- (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE);
- sizeRead +=
- (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
- } else {
- memcpy((u8 *)fwControl->buffer,
- (u8 *)pm8001_ha->fw_image->data + sizeRead,
- IOCTL_BUF_SIZE);
- sizeRead += IOCTL_BUF_SIZE;
- }
+ /*
+ * for the last chunk of data in case file size is
+ * not even with 4k, load only the rest
+ */
- pm8001_ha->nvmd_completion = &completion;
- ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload);
- wait_for_completion(&completion);
- if (ret || (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS)) {
- ret = fwControl->retcode;
- kfree(ioctlbuffer);
- ioctlbuffer = NULL;
- break;
+ read_buf = (u8 *)pm8001_ha->fw_image->data + sizeRead;
+ fc_len = (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
+
+ if (loopcount - loopNumber == 1 && fc_len) {
+ fwControl->len = fc_len;
+ memcpy((u8 *)fwControl->buffer, read_buf, fc_len);
+ sizeRead += fc_len;
+ } else {
+ memcpy((u8 *)fwControl->buffer, read_buf, IOCTL_BUF_SIZE);
+ sizeRead += IOCTL_BUF_SIZE;
+ }
+
+ pm8001_ha->nvmd_completion = &completion;
+ ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload);
+ if (ret) {
+ pm8001_ha->fw_status = FAIL_OUT_MEMORY;
+ goto out;
+ }
+ wait_for_completion(&completion);
+ if (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS) {
+ pm8001_ha->fw_status = fwControl->retcode;
+ ret = -EFAULT;
+ goto out;
+ }
}
}
- if (ret)
- break;
- partitionNumber++;
-}
out:
kfree(ioctlbuffer);
return ret;
@@ -463,22 +801,29 @@ static ssize_t pm8001_store_update_fw(struct device *cdev,
char *cmd_ptr, *filename_ptr;
int res, i;
int flash_command = FLASH_CMD_NONE;
- int err = 0;
+ int ret;
+
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+ /* this test protects us from running two flash processes at once,
+ * so we should start with this test */
+ if (pm8001_ha->fw_status == FLASH_IN_PROGRESS)
+ return -EINPROGRESS;
+ pm8001_ha->fw_status = FLASH_IN_PROGRESS;
+ cmd_ptr = kcalloc(count, 2, GFP_KERNEL);
if (!cmd_ptr) {
- err = FAIL_OUT_MEMORY;
- goto out;
+ pm8001_ha->fw_status = FAIL_OUT_MEMORY;
+ return -ENOMEM;
}
filename_ptr = cmd_ptr + count;
res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr);
if (res != 2) {
- err = FAIL_PARAMETERS;
- goto out1;
+ pm8001_ha->fw_status = FAIL_PARAMETERS;
+ ret = -EINVAL;
+ goto out;
}
for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
@@ -489,50 +834,37 @@ static ssize_t pm8001_store_update_fw(struct device *cdev,
}
}
if (flash_command == FLASH_CMD_NONE) {
- err = FAIL_PARAMETERS;
- goto out1;
+ pm8001_ha->fw_status = FAIL_PARAMETERS;
+ ret = -EINVAL;
+ goto out;
}
- if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) {
- err = FLASH_IN_PROGRESS;
- goto out1;
- }
- err = request_firmware(&pm8001_ha->fw_image,
+ ret = request_firmware(&pm8001_ha->fw_image,
filename_ptr,
pm8001_ha->dev);
- if (err) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Failed to load firmware image file %s,"
- " error %d\n", filename_ptr, err));
- err = FAIL_OPEN_BIOS_FILE;
- goto out1;
+ if (ret) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Failed to load firmware image file %s, error %d\n",
+ filename_ptr, ret);
+ pm8001_ha->fw_status = FAIL_OPEN_BIOS_FILE;
+ goto out;
}
- switch (flash_command) {
- case FLASH_CMD_UPDATE:
- pm8001_ha->fw_status = FLASH_IN_PROGRESS;
- err = pm8001_update_flash(pm8001_ha);
- break;
- case FLASH_CMD_SET_NVMD:
- pm8001_ha->fw_status = FLASH_IN_PROGRESS;
- err = pm8001_set_nvmd(pm8001_ha);
- break;
- default:
- pm8001_ha->fw_status = FAIL_PARAMETERS;
- err = FAIL_PARAMETERS;
- break;
- }
+ if (FLASH_CMD_UPDATE == flash_command)
+ ret = pm8001_update_flash(pm8001_ha);
+ else
+ ret = pm8001_set_nvmd(pm8001_ha);
+
release_firmware(pm8001_ha->fw_image);
-out1:
- kfree(cmd_ptr);
out:
- pm8001_ha->fw_status = err;
+ kfree(cmd_ptr);
- if (!err)
- return count;
- else
- return -err;
+ if (ret)
+ return ret;
+
+ pm8001_ha->fw_status = FLASH_OK;
+ return count;
}
static ssize_t pm8001_show_update_fw(struct device *cdev,
@@ -550,25 +882,167 @@ static ssize_t pm8001_show_update_fw(struct device *cdev,
if (pm8001_ha->fw_status != FLASH_IN_PROGRESS)
pm8001_ha->fw_status = FLASH_OK;
- return snprintf(buf, PAGE_SIZE, "status=%x %s\n",
- flash_error_table[i].err_code,
- flash_error_table[i].reason);
+ return sysfs_emit(buf, "status=%x %s\n",
+ flash_error_table[i].err_code,
+ flash_error_table[i].reason);
}
-
-static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUGO,
+static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUSR|S_IWGRP,
pm8001_show_update_fw, pm8001_store_update_fw);
-struct device_attribute *pm8001_host_attrs[] = {
- &dev_attr_interface_rev,
- &dev_attr_fw_version,
- &dev_attr_update_fw,
- &dev_attr_aap_log,
- &dev_attr_iop_log,
- &dev_attr_max_out_io,
- &dev_attr_max_devices,
- &dev_attr_max_sg_list,
- &dev_attr_sas_spec_support,
- &dev_attr_logging_level,
- &dev_attr_host_sas_address,
+
+static const char *const mpiStateText[] = {
+ "MPI is not initialized",
+ "MPI is successfully initialized",
+ "MPI termination is in progress",
+ "MPI initialization failed with error in [31:16]"
+};
+
+/**
+ * ctl_mpi_state_show - controller MPI state check
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t ctl_mpi_state_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ unsigned int mpidw0;
+
+ mpidw0 = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 0);
+ return sysfs_emit(buf, "%s\n", mpiStateText[mpidw0 & 0x0003]);
+}
+static DEVICE_ATTR_RO(ctl_mpi_state);
+
+/**
+ * ctl_hmi_error_show - controller MPI initialization fails
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t ctl_hmi_error_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ unsigned int mpidw0;
+
+ mpidw0 = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 0);
+ return sysfs_emit(buf, "0x%08x\n", (mpidw0 >> 16));
+}
+static DEVICE_ATTR_RO(ctl_hmi_error);
+
+/**
+ * ctl_raae_count_show - controller raae count check
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t ctl_raae_count_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ unsigned int raaecnt;
+
+ raaecnt = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 12);
+ return sysfs_emit(buf, "0x%08x\n", raaecnt);
+}
+static DEVICE_ATTR_RO(ctl_raae_count);
+
+/**
+ * ctl_iop0_count_show - controller iop0 count check
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t ctl_iop0_count_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ unsigned int iop0cnt;
+
+ iop0cnt = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 16);
+ return sysfs_emit(buf, "0x%08x\n", iop0cnt);
+}
+static DEVICE_ATTR_RO(ctl_iop0_count);
+
+/**
+ * ctl_iop1_count_show - controller iop1 count check
+ * @cdev: pointer to embedded class device
+ * @attr: device attribute (unused)
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t ctl_iop1_count_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ unsigned int iop1cnt;
+
+ iop1cnt = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 20);
+ return sysfs_emit(buf, "0x%08x\n", iop1cnt);
+
+}
+static DEVICE_ATTR_RO(ctl_iop1_count);
+
+static struct attribute *pm8001_host_attrs[] = {
+ &dev_attr_interface_rev.attr,
+ &dev_attr_controller_fatal_error.attr,
+ &dev_attr_fw_version.attr,
+ &dev_attr_update_fw.attr,
+ &dev_attr_aap_log.attr,
+ &dev_attr_iop_log.attr,
+ &dev_attr_fatal_log.attr,
+ &dev_attr_non_fatal_log.attr,
+ &dev_attr_non_fatal_count.attr,
+ &dev_attr_gsm_log.attr,
+ &dev_attr_max_out_io.attr,
+ &dev_attr_max_devices.attr,
+ &dev_attr_max_sg_list.attr,
+ &dev_attr_sas_spec_support.attr,
+ &dev_attr_logging_level.attr,
+ &dev_attr_event_log_size.attr,
+ &dev_attr_host_sas_address.attr,
+ &dev_attr_bios_version.attr,
+ &dev_attr_ib_log.attr,
+ &dev_attr_ob_log.attr,
+ &dev_attr_ila_version.attr,
+ &dev_attr_inc_fw_ver.attr,
+ &dev_attr_ctl_mpi_state.attr,
+ &dev_attr_ctl_hmi_error.attr,
+ &dev_attr_ctl_raae_count.attr,
+ &dev_attr_ctl_iop0_count.attr,
+ &dev_attr_ctl_iop1_count.attr,
NULL,
};
+static const struct attribute_group pm8001_host_attr_group = {
+ .attrs = pm8001_host_attrs
+};
+
+const struct attribute_group *pm8001_host_groups[] = {
+ &pm8001_host_attr_group,
+ NULL
+};
+
+const struct attribute_group *pm8001_sdev_groups[] = {
+ &sas_ata_sdev_attr_group,
+ NULL
+};
diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h
index 63ad4aa0c422..4743f0de223e 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.h
+++ b/drivers/scsi/pm8001/pm8001_ctl.h
@@ -45,6 +45,8 @@
#define HEADER_LEN 28
#define SIZE_OFFSET 16
+#define BIOSOFFSET 56
+#define BIOS_OFFSET_LIMIT 61
#define FLASH_OK 0x000000
#define FAIL_OPEN_BIOS_FILE 0x000100
@@ -53,5 +55,14 @@
#define FAIL_OUT_MEMORY 0x000c00
#define FLASH_IN_PROGRESS 0x001000
+#define IB_OB_READ_TIMES 256
+#define SYSFS_OFFSET 1024
+#define PM80XX_IB_OB_QUEUE_SIZE (32 * 1024)
+#define PM8001_IB_OB_QUEUE_SIZE (16 * 1024)
+
+static inline u32 pm8001_ctl_aap1_memmap(u8 *ptr, int idx, int off)
+{
+ return *(u32 *)(ptr + idx * 32 + off);
+}
#endif /* PM8001_CTL_H_INCLUDED */
diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h
index 944afada61ee..4e19d61dffbb 100644
--- a/drivers/scsi/pm8001/pm8001_defs.h
+++ b/drivers/scsi/pm8001/pm8001_defs.h
@@ -1,5 +1,5 @@
/*
- * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ * PMC-Sierra 8001/8081/8088/8089 SAS/SATA based host adapters driver
*
* Copyright (c) 2008-2009 USI Co., Ltd.
* All rights reserved.
@@ -43,13 +43,23 @@
enum chip_flavors {
chip_8001,
+ chip_8008,
+ chip_8009,
+ chip_8018,
+ chip_8019,
+ chip_8074,
+ chip_8076,
+ chip_8077,
+ chip_8006,
+ chip_8070,
+ chip_8072
};
-#define USI_MAX_MEMCNT 9
-#define PM8001_MAX_DMA_SG SG_ALL
+
enum phy_speed {
PHY_SPEED_15 = 0x01,
PHY_SPEED_30 = 0x02,
PHY_SPEED_60 = 0x04,
+ PHY_SPEED_120 = 0x08,
};
enum data_direction {
@@ -65,29 +75,45 @@ enum port_type {
};
/* driver compile-time configuration */
-#define PM8001_MAX_CCB 512 /* max ccbs supported */
-#define PM8001_MAX_INB_NUM 1
-#define PM8001_MAX_OUTB_NUM 1
-#define PM8001_CAN_QUEUE 128 /* SCSI Queue depth */
+#define PM8001_MAX_CCB 1024 /* max ccbs supported */
+#define PM8001_MPI_QUEUE 1024 /* maximum mpi queue entries */
+#define PM8001_MAX_INB_NUM 64
+#define PM8001_MAX_OUTB_NUM 64
+#define PM8001_CAN_QUEUE 508 /* SCSI Queue depth */
+
+/* Inbound/Outbound queue size */
+#define IOMB_SIZE_SPC 64
+#define IOMB_SIZE_SPCV 128
/* unchangeable hardware details */
-#define PM8001_MAX_PHYS 8 /* max. possible phys */
-#define PM8001_MAX_PORTS 8 /* max. possible ports */
-#define PM8001_MAX_DEVICES 1024 /* max supported device */
+#define PM8001_MAX_PHYS 16 /* max. possible phys */
+#define PM8001_MAX_PORTS 16 /* max. possible ports */
+#define PM8001_MAX_DEVICES 2048 /* max supported device */
+#define PM8001_MAX_MSIX_VEC 64 /* max msi-x int for spcv/ve */
+#define PM8001_RESERVE_SLOT 128
+
+#define PM8001_SECTOR_SIZE 512
+#define PM8001_PAGE_SIZE_4K 4096
+#define PM8001_MAX_IO_SIZE (4 * 1024 * 1024)
+#define PM8001_MAX_DMA_SG (PM8001_MAX_IO_SIZE / PM8001_PAGE_SIZE_4K)
+#define PM8001_MAX_SECTORS (PM8001_MAX_IO_SIZE / PM8001_SECTOR_SIZE)
enum memory_region_num {
AAP1 = 0x0, /* application acceleration processor */
IOP, /* IO processor */
- CI, /* consumer index */
- PI, /* producer index */
- IB, /* inbound queue */
- OB, /* outbound queue */
NVMD, /* NVM device */
- DEV_MEM, /* memory for devices */
- CCB_MEM, /* memory for command control block */
+ FW_FLASH, /* memory for fw flash update */
+ FORENSIC_MEM, /* memory for fw forensic data */
+ USI_MAX_MEMCNT_BASE
};
#define PM8001_EVENT_LOG_SIZE (128 * 1024)
+/**
+ * maximum DMA memory regions(number of IBQ + number of IBQ CI
+ * + number of OBQ + number of OBQ PI)
+ */
+#define USI_MAX_MEMCNT (USI_MAX_MEMCNT_BASE + ((2 * PM8001_MAX_INB_NUM) \
+ + (2 * PM8001_MAX_OUTB_NUM)))
/*error code*/
enum mpi_err {
MPI_IO_STATUS_SUCCESS = 0x0,
@@ -109,4 +135,12 @@ enum pm8001_hba_info_flags {
PM8001F_RUN_TIME = (1U << 1),
};
+/**
+ * Phy Status
+ */
+#define PHY_LINK_DISABLE 0x00
+#define PHY_LINK_DOWN 0x01
+#define PHY_STATE_LINK_UP_SPCV 0x2
+#define PHY_STATE_LINK_UP_SPC 0x1
+
#endif
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index 8b7db1e53c10..8005995a317c 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -42,40 +42,48 @@
#include "pm8001_hwi.h"
#include "pm8001_chips.h"
#include "pm8001_ctl.h"
+ #include "pm80xx_tracepoints.h"
/**
* read_main_config_table - read the configure table and save it.
* @pm8001_ha: our hba card information
*/
-static void __devinit read_main_config_table(struct pm8001_hba_info *pm8001_ha)
+static void read_main_config_table(struct pm8001_hba_info *pm8001_ha)
{
void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
- pm8001_ha->main_cfg_tbl.signature = pm8001_mr32(address, 0x00);
- pm8001_ha->main_cfg_tbl.interface_rev = pm8001_mr32(address, 0x04);
- pm8001_ha->main_cfg_tbl.firmware_rev = pm8001_mr32(address, 0x08);
- pm8001_ha->main_cfg_tbl.max_out_io = pm8001_mr32(address, 0x0C);
- pm8001_ha->main_cfg_tbl.max_sgl = pm8001_mr32(address, 0x10);
- pm8001_ha->main_cfg_tbl.ctrl_cap_flag = pm8001_mr32(address, 0x14);
- pm8001_ha->main_cfg_tbl.gst_offset = pm8001_mr32(address, 0x18);
- pm8001_ha->main_cfg_tbl.inbound_queue_offset =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.signature =
+ pm8001_mr32(address, 0x00);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.interface_rev =
+ pm8001_mr32(address, 0x04);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev =
+ pm8001_mr32(address, 0x08);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.max_out_io =
+ pm8001_mr32(address, 0x0C);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl =
+ pm8001_mr32(address, 0x10);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.ctrl_cap_flag =
+ pm8001_mr32(address, 0x14);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.gst_offset =
+ pm8001_mr32(address, 0x18);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.inbound_queue_offset =
pm8001_mr32(address, MAIN_IBQ_OFFSET);
- pm8001_ha->main_cfg_tbl.outbound_queue_offset =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_queue_offset =
pm8001_mr32(address, MAIN_OBQ_OFFSET);
- pm8001_ha->main_cfg_tbl.hda_mode_flag =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.hda_mode_flag =
pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET);
/* read analog Setting offset from the configuration table */
- pm8001_ha->main_cfg_tbl.anolog_setup_table_offset =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.anolog_setup_table_offset =
pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET);
/* read Error Dump Offset and Length */
- pm8001_ha->main_cfg_tbl.fatal_err_dump_offset0 =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_offset0 =
pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET);
- pm8001_ha->main_cfg_tbl.fatal_err_dump_length0 =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_length0 =
pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH);
- pm8001_ha->main_cfg_tbl.fatal_err_dump_offset1 =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_offset1 =
pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET);
- pm8001_ha->main_cfg_tbl.fatal_err_dump_length1 =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_length1 =
pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH);
}
@@ -83,48 +91,70 @@ static void __devinit read_main_config_table(struct pm8001_hba_info *pm8001_ha)
* read_general_status_table - read the general status table and save it.
* @pm8001_ha: our hba card information
*/
-static void __devinit
-read_general_status_table(struct pm8001_hba_info *pm8001_ha)
+static void read_general_status_table(struct pm8001_hba_info *pm8001_ha)
{
void __iomem *address = pm8001_ha->general_stat_tbl_addr;
- pm8001_ha->gs_tbl.gst_len_mpistate = pm8001_mr32(address, 0x00);
- pm8001_ha->gs_tbl.iq_freeze_state0 = pm8001_mr32(address, 0x04);
- pm8001_ha->gs_tbl.iq_freeze_state1 = pm8001_mr32(address, 0x08);
- pm8001_ha->gs_tbl.msgu_tcnt = pm8001_mr32(address, 0x0C);
- pm8001_ha->gs_tbl.iop_tcnt = pm8001_mr32(address, 0x10);
- pm8001_ha->gs_tbl.reserved = pm8001_mr32(address, 0x14);
- pm8001_ha->gs_tbl.phy_state[0] = pm8001_mr32(address, 0x18);
- pm8001_ha->gs_tbl.phy_state[1] = pm8001_mr32(address, 0x1C);
- pm8001_ha->gs_tbl.phy_state[2] = pm8001_mr32(address, 0x20);
- pm8001_ha->gs_tbl.phy_state[3] = pm8001_mr32(address, 0x24);
- pm8001_ha->gs_tbl.phy_state[4] = pm8001_mr32(address, 0x28);
- pm8001_ha->gs_tbl.phy_state[5] = pm8001_mr32(address, 0x2C);
- pm8001_ha->gs_tbl.phy_state[6] = pm8001_mr32(address, 0x30);
- pm8001_ha->gs_tbl.phy_state[7] = pm8001_mr32(address, 0x34);
- pm8001_ha->gs_tbl.reserved1 = pm8001_mr32(address, 0x38);
- pm8001_ha->gs_tbl.reserved2 = pm8001_mr32(address, 0x3C);
- pm8001_ha->gs_tbl.reserved3 = pm8001_mr32(address, 0x40);
- pm8001_ha->gs_tbl.recover_err_info[0] = pm8001_mr32(address, 0x44);
- pm8001_ha->gs_tbl.recover_err_info[1] = pm8001_mr32(address, 0x48);
- pm8001_ha->gs_tbl.recover_err_info[2] = pm8001_mr32(address, 0x4C);
- pm8001_ha->gs_tbl.recover_err_info[3] = pm8001_mr32(address, 0x50);
- pm8001_ha->gs_tbl.recover_err_info[4] = pm8001_mr32(address, 0x54);
- pm8001_ha->gs_tbl.recover_err_info[5] = pm8001_mr32(address, 0x58);
- pm8001_ha->gs_tbl.recover_err_info[6] = pm8001_mr32(address, 0x5C);
- pm8001_ha->gs_tbl.recover_err_info[7] = pm8001_mr32(address, 0x60);
+ pm8001_ha->gs_tbl.pm8001_tbl.gst_len_mpistate =
+ pm8001_mr32(address, 0x00);
+ pm8001_ha->gs_tbl.pm8001_tbl.iq_freeze_state0 =
+ pm8001_mr32(address, 0x04);
+ pm8001_ha->gs_tbl.pm8001_tbl.iq_freeze_state1 =
+ pm8001_mr32(address, 0x08);
+ pm8001_ha->gs_tbl.pm8001_tbl.msgu_tcnt =
+ pm8001_mr32(address, 0x0C);
+ pm8001_ha->gs_tbl.pm8001_tbl.iop_tcnt =
+ pm8001_mr32(address, 0x10);
+ pm8001_ha->gs_tbl.pm8001_tbl.rsvd =
+ pm8001_mr32(address, 0x14);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[0] =
+ pm8001_mr32(address, 0x18);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[1] =
+ pm8001_mr32(address, 0x1C);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[2] =
+ pm8001_mr32(address, 0x20);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[3] =
+ pm8001_mr32(address, 0x24);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[4] =
+ pm8001_mr32(address, 0x28);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[5] =
+ pm8001_mr32(address, 0x2C);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[6] =
+ pm8001_mr32(address, 0x30);
+ pm8001_ha->gs_tbl.pm8001_tbl.phy_state[7] =
+ pm8001_mr32(address, 0x34);
+ pm8001_ha->gs_tbl.pm8001_tbl.gpio_input_val =
+ pm8001_mr32(address, 0x38);
+ pm8001_ha->gs_tbl.pm8001_tbl.rsvd1[0] =
+ pm8001_mr32(address, 0x3C);
+ pm8001_ha->gs_tbl.pm8001_tbl.rsvd1[1] =
+ pm8001_mr32(address, 0x40);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[0] =
+ pm8001_mr32(address, 0x44);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[1] =
+ pm8001_mr32(address, 0x48);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[2] =
+ pm8001_mr32(address, 0x4C);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[3] =
+ pm8001_mr32(address, 0x50);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[4] =
+ pm8001_mr32(address, 0x54);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[5] =
+ pm8001_mr32(address, 0x58);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[6] =
+ pm8001_mr32(address, 0x5C);
+ pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[7] =
+ pm8001_mr32(address, 0x60);
}
/**
* read_inbnd_queue_table - read the inbound queue table and save it.
* @pm8001_ha: our hba card information
*/
-static void __devinit
-read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+static void read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
{
- int inbQ_num = 1;
int i;
void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
- for (i = 0; i < inbQ_num; i++) {
+ for (i = 0; i < PM8001_MAX_INB_NUM; i++) {
u32 offset = i * 0x20;
pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
@@ -137,13 +167,11 @@ read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
* read_outbnd_queue_table - read the outbound queue table and save it.
* @pm8001_ha: our hba card information
*/
-static void __devinit
-read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+static void read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
{
- int outbQ_num = 1;
int i;
void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
- for (i = 0; i < outbQ_num; i++) {
+ for (i = 0; i < PM8001_MAX_OUTB_NUM; i++) {
u32 offset = i * 0x24;
pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
@@ -156,57 +184,64 @@ read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
* init_default_table_values - init the default table.
* @pm8001_ha: our hba card information
*/
-static void __devinit
-init_default_table_values(struct pm8001_hba_info *pm8001_ha)
+static void init_default_table_values(struct pm8001_hba_info *pm8001_ha)
{
- int qn = 1;
int i;
u32 offsetib, offsetob;
void __iomem *addressib = pm8001_ha->inbnd_q_tbl_addr;
void __iomem *addressob = pm8001_ha->outbnd_q_tbl_addr;
-
- pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd = 0;
- pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3 = 0;
- pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7 = 0;
- pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3 = 0;
- pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7 = 0;
- pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3 = 0;
- pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7 = 0;
- pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3 = 0;
- pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7 = 0;
- pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3 = 0;
- pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7 = 0;
-
- pm8001_ha->main_cfg_tbl.upper_event_log_addr =
+ u32 ib_offset = pm8001_ha->ib_offset;
+ u32 ob_offset = pm8001_ha->ob_offset;
+ u32 ci_offset = pm8001_ha->ci_offset;
+ u32 pi_offset = pm8001_ha->pi_offset;
+
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.inbound_q_nppd_hppd = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ITNexus_event_pid0_3 =
+ 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ITNexus_event_pid4_7 =
+ 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ssp_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ssp_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_smp_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_smp_event_pid4_7 = 0;
+
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_event_log_addr =
pm8001_ha->memoryMap.region[AAP1].phys_addr_hi;
- pm8001_ha->main_cfg_tbl.lower_event_log_addr =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_event_log_addr =
pm8001_ha->memoryMap.region[AAP1].phys_addr_lo;
- pm8001_ha->main_cfg_tbl.event_log_size = PM8001_EVENT_LOG_SIZE;
- pm8001_ha->main_cfg_tbl.event_log_option = 0x01;
- pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_size =
+ PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_option = 0x01;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_iop_event_log_addr =
pm8001_ha->memoryMap.region[IOP].phys_addr_hi;
- pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr =
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_iop_event_log_addr =
pm8001_ha->memoryMap.region[IOP].phys_addr_lo;
- pm8001_ha->main_cfg_tbl.iop_event_log_size = PM8001_EVENT_LOG_SIZE;
- pm8001_ha->main_cfg_tbl.iop_event_log_option = 0x01;
- pm8001_ha->main_cfg_tbl.fatal_err_interrupt = 0x01;
- for (i = 0; i < qn; i++) {
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_size =
+ PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_option = 0x01;
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_interrupt = 0x01;
+ for (i = 0; i < pm8001_ha->max_q_num; i++) {
pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
- 0x00000100 | (0x00000040 << 16) | (0x00<<30);
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30);
pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
- pm8001_ha->memoryMap.region[IB].phys_addr_hi;
+ pm8001_ha->memoryMap.region[ib_offset + i].phys_addr_hi;
pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
- pm8001_ha->memoryMap.region[IB].phys_addr_lo;
+ pm8001_ha->memoryMap.region[ib_offset + i].phys_addr_lo;
pm8001_ha->inbnd_q_tbl[i].base_virt =
- (u8 *)pm8001_ha->memoryMap.region[IB].virt_ptr;
+ (u8 *)pm8001_ha->memoryMap.region[ib_offset + i].virt_ptr;
pm8001_ha->inbnd_q_tbl[i].total_length =
- pm8001_ha->memoryMap.region[IB].total_len;
+ pm8001_ha->memoryMap.region[ib_offset + i].total_len;
pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr =
- pm8001_ha->memoryMap.region[CI].phys_addr_hi;
+ pm8001_ha->memoryMap.region[ci_offset + i].phys_addr_hi;
pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr =
- pm8001_ha->memoryMap.region[CI].phys_addr_lo;
+ pm8001_ha->memoryMap.region[ci_offset + i].phys_addr_lo;
pm8001_ha->inbnd_q_tbl[i].ci_virt =
- pm8001_ha->memoryMap.region[CI].virt_ptr;
+ pm8001_ha->memoryMap.region[ci_offset + i].virt_ptr;
+ pm8001_write_32(pm8001_ha->inbnd_q_tbl[i].ci_virt, 0, 0);
offsetib = i * 0x20;
pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
get_pci_bar_index(pm8001_mr32(addressib,
@@ -216,25 +251,26 @@ init_default_table_values(struct pm8001_hba_info *pm8001_ha)
pm8001_ha->inbnd_q_tbl[i].producer_idx = 0;
pm8001_ha->inbnd_q_tbl[i].consumer_index = 0;
}
- for (i = 0; i < qn; i++) {
+ for (i = 0; i < pm8001_ha->max_q_num; i++) {
pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
- 256 | (64 << 16) | (1<<30);
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30);
pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
- pm8001_ha->memoryMap.region[OB].phys_addr_hi;
+ pm8001_ha->memoryMap.region[ob_offset + i].phys_addr_hi;
pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
- pm8001_ha->memoryMap.region[OB].phys_addr_lo;
+ pm8001_ha->memoryMap.region[ob_offset + i].phys_addr_lo;
pm8001_ha->outbnd_q_tbl[i].base_virt =
- (u8 *)pm8001_ha->memoryMap.region[OB].virt_ptr;
+ (u8 *)pm8001_ha->memoryMap.region[ob_offset + i].virt_ptr;
pm8001_ha->outbnd_q_tbl[i].total_length =
- pm8001_ha->memoryMap.region[OB].total_len;
+ pm8001_ha->memoryMap.region[ob_offset + i].total_len;
pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr =
- pm8001_ha->memoryMap.region[PI].phys_addr_hi;
+ pm8001_ha->memoryMap.region[pi_offset + i].phys_addr_hi;
pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr =
- pm8001_ha->memoryMap.region[PI].phys_addr_lo;
+ pm8001_ha->memoryMap.region[pi_offset + i].phys_addr_lo;
pm8001_ha->outbnd_q_tbl[i].interrup_vec_cnt_delay =
- 0 | (10 << 16) | (0 << 24);
+ 0 | (10 << 16) | (i << 24);
pm8001_ha->outbnd_q_tbl[i].pi_virt =
- pm8001_ha->memoryMap.region[PI].virt_ptr;
+ pm8001_ha->memoryMap.region[pi_offset + i].virt_ptr;
+ pm8001_write_32(pm8001_ha->outbnd_q_tbl[i].pi_virt, 0, 0);
offsetob = i * 0x24;
pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
get_pci_bar_index(pm8001_mr32(addressob,
@@ -250,55 +286,64 @@ init_default_table_values(struct pm8001_hba_info *pm8001_ha)
* update_main_config_table - update the main default table to the HBA.
* @pm8001_ha: our hba card information
*/
-static void __devinit
-update_main_config_table(struct pm8001_hba_info *pm8001_ha)
+static void update_main_config_table(struct pm8001_hba_info *pm8001_ha)
{
void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
pm8001_mw32(address, 0x24,
- pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.inbound_q_nppd_hppd);
pm8001_mw32(address, 0x28,
- pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid0_3);
pm8001_mw32(address, 0x2C,
- pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid4_7);
pm8001_mw32(address, 0x30,
- pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid0_3);
pm8001_mw32(address, 0x34,
- pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid4_7);
pm8001_mw32(address, 0x38,
- pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.
+ outbound_tgt_ITNexus_event_pid0_3);
pm8001_mw32(address, 0x3C,
- pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.
+ outbound_tgt_ITNexus_event_pid4_7);
pm8001_mw32(address, 0x40,
- pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.
+ outbound_tgt_ssp_event_pid0_3);
pm8001_mw32(address, 0x44,
- pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.
+ outbound_tgt_ssp_event_pid4_7);
pm8001_mw32(address, 0x48,
- pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.
+ outbound_tgt_smp_event_pid0_3);
pm8001_mw32(address, 0x4C,
- pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.
+ outbound_tgt_smp_event_pid4_7);
pm8001_mw32(address, 0x50,
- pm8001_ha->main_cfg_tbl.upper_event_log_addr);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_event_log_addr);
pm8001_mw32(address, 0x54,
- pm8001_ha->main_cfg_tbl.lower_event_log_addr);
- pm8001_mw32(address, 0x58, pm8001_ha->main_cfg_tbl.event_log_size);
- pm8001_mw32(address, 0x5C, pm8001_ha->main_cfg_tbl.event_log_option);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_event_log_addr);
+ pm8001_mw32(address, 0x58,
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_size);
+ pm8001_mw32(address, 0x5C,
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_option);
pm8001_mw32(address, 0x60,
- pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_iop_event_log_addr);
pm8001_mw32(address, 0x64,
- pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr);
- pm8001_mw32(address, 0x68, pm8001_ha->main_cfg_tbl.iop_event_log_size);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_iop_event_log_addr);
+ pm8001_mw32(address, 0x68,
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_size);
pm8001_mw32(address, 0x6C,
- pm8001_ha->main_cfg_tbl.iop_event_log_option);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_option);
pm8001_mw32(address, 0x70,
- pm8001_ha->main_cfg_tbl.fatal_err_interrupt);
+ pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_interrupt);
}
/**
* update_inbnd_queue_table - update the inbound queue table to the HBA.
* @pm8001_ha: our hba card information
+ * @number: entry in the queue
*/
-static void __devinit
-update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+static void update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha,
+ int number)
{
void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
u16 offset = number * 0x20;
@@ -317,9 +362,10 @@ update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
/**
* update_outbnd_queue_table - update the outbound queue table to the HBA.
* @pm8001_ha: our hba card information
+ * @number: entry in the queue
*/
-static void __devinit
-update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+static void update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha,
+ int number)
{
void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
u16 offset = number * 0x24;
@@ -338,29 +384,28 @@ update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
}
/**
- * bar4_shift - function is called to shift BAR base address
+ * pm8001_bar4_shift - function is called to shift BAR base address
* @pm8001_ha : our hba card information
* @shiftValue : shifting value in memory bar.
*/
-static int bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
+int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
{
u32 regVal;
- u32 max_wait_count;
+ unsigned long start;
/* program the inbound AXI translation Lower Address */
pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue);
/* confirm the setting is written */
- max_wait_count = 1 * 1000 * 1000; /* 1 sec */
+ start = jiffies + HZ; /* 1 sec */
do {
- udelay(1);
regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW);
- } while ((regVal != shiftValue) && (--max_wait_count));
+ } while ((regVal != shiftValue) && time_before(jiffies, start));
- if (!max_wait_count) {
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW"
- " = 0x%x\n", regVal));
+ if (regVal != shiftValue) {
+ pm8001_dbg(pm8001_ha, INIT,
+ "TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW = 0x%x\n",
+ regVal);
return -1;
}
return 0;
@@ -371,10 +416,11 @@ static int bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
* @pm8001_ha: our hba card information
* @SSCbit: set SSCbit to 0 to disable all phys ssc; 1 to enable all phys ssc.
*/
-static void __devinit
-mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
+static void mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha,
+ u32 SSCbit)
{
- u32 value, offset, i;
+ u32 offset, i;
+ unsigned long flags;
#define SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR 0x00030000
#define SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR 0x00040000
@@ -388,16 +434,23 @@ mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
* Using shifted destination address 0x3_0000:0x1074 + 0x4000*N (N=0:3)
* Using shifted destination address 0x4_0000:0x1074 + 0x4000*(N-4) (N=4:7)
*/
- if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR))
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 0; i < 4; i++) {
offset = SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET + 0x4000 * i;
pm8001_cw32(pm8001_ha, 2, offset, 0x80001501);
}
/* shift membase 3 for SAS2_SETTINGS_LOCAL_PHY 4 - 7 */
- if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR))
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 4; i < 8; i++) {
offset = SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
pm8001_cw32(pm8001_ha, 2, offset, 0x80001501);
@@ -417,26 +470,27 @@ mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
so that the written value will be 0x8090c016.
This will ensure only down-spreading SSC is enabled on the SPC.
*************************************************************/
- value = pm8001_cr32(pm8001_ha, 2, 0xd8);
+ pm8001_cr32(pm8001_ha, 2, 0xd8);
pm8001_cw32(pm8001_ha, 2, 0xd8, 0x8000C016);
/*set the shifted destination address to 0x0 to avoid error operation */
- bar4_shift(pm8001_ha, 0x0);
+ pm8001_bar4_shift(pm8001_ha, 0x0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
}
/**
* mpi_set_open_retry_interval_reg
* @pm8001_ha: our hba card information
- * @interval - interval time for each OPEN_REJECT (RETRY). The units are in 1us.
+ * @interval: interval time for each OPEN_REJECT (RETRY). The units are in 1us.
*/
-static void __devinit
-mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
- u32 interval)
+static void mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
+ u32 interval)
{
u32 offset;
u32 value;
u32 i;
+ unsigned long flags;
#define OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR 0x00030000
#define OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR 0x00040000
@@ -445,24 +499,30 @@ mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
#define OPEN_RETRY_INTERVAL_REG_MASK 0x0000FFFF
value = interval & OPEN_RETRY_INTERVAL_REG_MASK;
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
/* shift bar and set the OPEN_REJECT(RETRY) interval time of PHY 0 -3.*/
- if (-1 == bar4_shift(pm8001_ha,
- OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR))
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 0; i < 4; i++) {
offset = OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET + 0x4000 * i;
pm8001_cw32(pm8001_ha, 2, offset, value);
}
- if (-1 == bar4_shift(pm8001_ha,
- OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR))
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 4; i < 8; i++) {
offset = OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
pm8001_cw32(pm8001_ha, 2, offset, value);
}
/*set the shifted destination address to 0x0 to avoid error operation */
- bar4_shift(pm8001_ha, 0x0);
+ pm8001_bar4_shift(pm8001_ha, 0x0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
}
@@ -566,12 +626,10 @@ static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
value = pm8001_cr32(pm8001_ha, 0, 0x44);
offset = value & 0x03FFFFFF;
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("Scratchpad 0 Offset: %x \n", offset));
+ pm8001_dbg(pm8001_ha, INIT, "Scratchpad 0 Offset: %x\n", offset);
pcilogic = (value & 0xFC000000) >> 26;
pcibar = get_pci_bar_index(pcilogic);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("Scratchpad 0 PCI BAR: %d \n", pcibar));
+ pm8001_dbg(pm8001_ha, INIT, "Scratchpad 0 PCI BAR: %d\n", pcibar);
pm8001_ha->main_cfg_tbl_addr = base_addr =
pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
pm8001_ha->general_stat_tbl_addr =
@@ -586,12 +644,24 @@ static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
* pm8001_chip_init - the main init function that initialize whole PM8001 chip.
* @pm8001_ha: our hba card information
*/
-static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
+static int pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
{
+ u32 i = 0;
+ u16 deviceid;
+ pci_read_config_word(pm8001_ha->pdev, PCI_DEVICE_ID, &deviceid);
+ /* 8081 controllers need BAR shift to access MPI space
+ * as this is shared with BIOS data */
+ if (deviceid == 0x8081 || deviceid == 0x0042) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_SM_BASE)) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Shift Bar4 to 0x%x failed\n",
+ GSM_SM_BASE);
+ return -1;
+ }
+ }
/* check the firmware status */
if (-1 == check_fw_ready(pm8001_ha)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Firmware is not ready!\n"));
+ pm8001_dbg(pm8001_ha, FAIL, "Firmware is not ready!\n");
return -EBUSY;
}
@@ -604,14 +674,19 @@ static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
read_outbnd_queue_table(pm8001_ha);
/* update main config table ,inbound table and outbound table */
update_main_config_table(pm8001_ha);
- update_inbnd_queue_table(pm8001_ha, 0);
- update_outbnd_queue_table(pm8001_ha, 0);
- mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
- mpi_set_open_retry_interval_reg(pm8001_ha, 7);
+ for (i = 0; i < pm8001_ha->max_q_num; i++)
+ update_inbnd_queue_table(pm8001_ha, i);
+ for (i = 0; i < pm8001_ha->max_q_num; i++)
+ update_outbnd_queue_table(pm8001_ha, i);
+ /* 8081 controller donot require these operations */
+ if (deviceid != 0x8081 && deviceid != 0x0042) {
+ mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
+ /* 7->130ms, 34->500ms, 119->1.5s */
+ mpi_set_open_retry_interval_reg(pm8001_ha, 119);
+ }
/* notify firmware update finished and check initialization status */
if (0 == mpi_init_check(pm8001_ha)) {
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("MPI initialize successful!\n"));
+ pm8001_dbg(pm8001_ha, INIT, "MPI initialize successful!\n");
} else
return -EBUSY;
/*This register is a 16-bit timer with a resolution of 1us. This is the
@@ -624,11 +699,25 @@ static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
return 0;
}
+static void pm8001_chip_post_init(struct pm8001_hba_info *pm8001_ha)
+{
+}
+
static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
{
u32 max_wait_count;
u32 value;
u32 gst_len_mpistate;
+ u16 deviceid;
+ pci_read_config_word(pm8001_ha->pdev, PCI_DEVICE_ID, &deviceid);
+ if (deviceid == 0x8081 || deviceid == 0x0042) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_SM_BASE)) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Shift Bar4 to 0x%x failed\n",
+ GSM_SM_BASE);
+ return -1;
+ }
+ }
init_pci_device_addresses(pm8001_ha);
/* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the
table is stop */
@@ -643,8 +732,8 @@ static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
} while ((value != 0) && (--max_wait_count));
if (!max_wait_count) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("TIMEOUT:IBDB value/=0x%x\n", value));
+ pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT:IBDB value/=0x%x\n",
+ value);
return -1;
}
@@ -661,9 +750,8 @@ static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
break;
} while (--max_wait_count);
if (!max_wait_count) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk(" TIME OUT MPI State = 0x%x\n",
- gst_len_mpistate & GST_MPI_STATE_MASK));
+ pm8001_dbg(pm8001_ha, FAIL, " TIME OUT MPI State = 0x%x\n",
+ gst_len_mpistate & GST_MPI_STATE_MASK);
return -1;
}
return 0;
@@ -677,22 +765,23 @@ static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
{
u32 regVal, regVal1, regVal2;
if (mpi_uninit_check(pm8001_ha) != 0) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("MPI state is not ready\n"));
+ pm8001_dbg(pm8001_ha, FAIL, "MPI state is not ready\n");
return -1;
}
/* read the scratch pad 2 register bit 2 */
regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
& SCRATCH_PAD2_FWRDY_RST;
if (regVal == SCRATCH_PAD2_FWRDY_RST) {
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("Firmware is ready for reset .\n"));
+ pm8001_dbg(pm8001_ha, INIT, "Firmware is ready for reset.\n");
} else {
- /* Trigger NMI twice via RB6 */
- if (-1 == bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Shift Bar4 to 0x%x failed\n",
- RB6_ACCESS_REG));
+ unsigned long flags;
+ /* Trigger NMI twice via RB6 */
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (-1 == pm8001_bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Shift Bar4 to 0x%x failed\n",
+ RB6_ACCESS_REG);
return -1;
}
pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET,
@@ -705,18 +794,18 @@ static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
if (regVal != SCRATCH_PAD2_FWRDY_RST) {
regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("TIMEOUT:MSGU_SCRATCH_PAD1"
- "=0x%x, MSGU_SCRATCH_PAD2=0x%x\n",
- regVal1, regVal2));
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
- pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0)));
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
- pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3)));
+ pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT:MSGU_SCRATCH_PAD1=0x%x, MSGU_SCRATCH_PAD2=0x%x\n",
+ regVal1, regVal2);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0));
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3));
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return -1;
}
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
}
return 0;
}
@@ -725,64 +814,65 @@ static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
* pm8001_chip_soft_rst - soft reset the PM8001 chip, so that the clear all
* the FW register status to the originated status.
* @pm8001_ha: our hba card information
- * @signature: signature in host scratch pad0 register.
*/
static int
-pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
+pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha)
{
u32 regVal, toggleVal;
u32 max_wait_count;
u32 regVal1, regVal2, regVal3;
+ u32 signature = 0x252acbcd; /* for host scratch pad0 */
+ unsigned long flags;
/* step1: Check FW is ready for soft reset */
if (soft_reset_ready_check(pm8001_ha) != 0) {
- PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("FW is not ready\n"));
+ pm8001_dbg(pm8001_ha, FAIL, "FW is not ready\n");
return -1;
}
/* step 2: clear NMI status register on AAP1 and IOP, write the same
value to clear */
/* map 0x60000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Shift Bar4 to 0x%x failed\n",
- MBIC_AAP1_ADDR_BASE));
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (-1 == pm8001_bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
+ MBIC_AAP1_ADDR_BASE);
return -1;
}
regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("MBIC - NMI Enable VPE0 (IOP)= 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "MBIC - NMI Enable VPE0 (IOP)= 0x%x\n",
+ regVal);
pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0);
/* map 0x70000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Shift Bar4 to 0x%x failed\n",
- MBIC_IOP_ADDR_BASE));
+ if (-1 == pm8001_bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
+ MBIC_IOP_ADDR_BASE);
return -1;
}
regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("MBIC - NMI Enable VPE0 (AAP1)= 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "MBIC - NMI Enable VPE0 (AAP1)= 0x%x\n",
+ regVal);
pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1, 0x0);
regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("PCIE -Event Interrupt Enable = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "PCIE -Event Interrupt Enable = 0x%x\n",
+ regVal);
pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE, 0x0);
regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("PCIE - Event Interrupt = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "PCIE - Event Interrupt = 0x%x\n",
+ regVal);
pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT, regVal);
regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("PCIE -Error Interrupt Enable = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "PCIE -Error Interrupt Enable = 0x%x\n",
+ regVal);
pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE, 0x0);
regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("PCIE - Error Interrupt = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "PCIE - Error Interrupt = 0x%x\n", regVal);
pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT, regVal);
/* read the scratch pad 1 register bit 2 */
@@ -796,16 +886,15 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* read required registers for confirmming */
/* map 0x0700000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Shift Bar4 to 0x%x failed\n",
- GSM_ADDR_BASE));
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
+ GSM_ADDR_BASE);
return -1;
}
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x0(0x00007b88)-GSM Configuration and"
- " Reset = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x0(0x00007b88)-GSM Configuration and Reset = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));
/* step 3: host read GSM Configuration and Reset register */
regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
@@ -820,82 +909,74 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
regVal &= ~(0x00003b00);
/* host write GSM Configuration and Reset register */
pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM "
- "Configuration and Reset is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM Configuration and Reset is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));
/* step 4: */
/* disable GSM - Read Address Parity Check */
regVal1 = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700038 - Read Address Parity Check "
- "Enable = 0x%x\n", regVal1));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x700038 - Read Address Parity Check Enable = 0x%x\n",
+ regVal1);
pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, 0x0);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
- "is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x700038 - Read Address Parity Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK));
/* disable GSM - Write Address Parity Check */
regVal2 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700040 - Write Address Parity Check"
- " Enable = 0x%x\n", regVal2));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x700040 - Write Address Parity Check Enable = 0x%x\n",
+ regVal2);
pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, 0x0);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700040 - Write Address Parity Check "
- "Enable is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x700040 - Write Address Parity Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK));
/* disable GSM - Write Data Parity Check */
regVal3 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x300048 - Write Data Parity Check"
- " Enable = 0x%x\n", regVal3));
+ pm8001_dbg(pm8001_ha, INIT, "GSM 0x300048 - Write Data Parity Check Enable = 0x%x\n",
+ regVal3);
pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, 0x0);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x300048 - Write Data Parity Check Enable"
- "is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x300048 - Write Data Parity Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK));
/* step 5: delay 10 usec */
udelay(10);
/* step 5-b: set GPIO-0 output control to tristate anyway */
- if (-1 == bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("Shift Bar4 to 0x%x failed\n",
- GPIO_ADDR_BASE));
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, INIT, "Shift Bar4 to 0x%x failed\n",
+ GPIO_ADDR_BASE);
return -1;
}
regVal = pm8001_cr32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GPIO Output Control Register:"
- " = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "GPIO Output Control Register: = 0x%x\n",
+ regVal);
/* set GPIO-0 output control to tri-state */
regVal &= 0xFFFFFFFC;
pm8001_cw32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET, regVal);
/* Step 6: Reset the IOP and AAP1 */
/* map 0x00000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
- SPC_TOP_LEVEL_ADDR_BASE));
+ if (-1 == pm8001_bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL, "SPC Shift Bar4 to 0x%x failed\n",
+ SPC_TOP_LEVEL_ADDR_BASE);
return -1;
}
regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("Top Register before resetting IOP/AAP1"
- ":= 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "Top Register before resetting IOP/AAP1:= 0x%x\n",
+ regVal);
regVal &= ~(SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
/* step 7: Reset the BDMA/OSSP */
regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("Top Register before resetting BDMA/OSSP"
- ": = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT, "Top Register before resetting BDMA/OSSP: = 0x%x\n",
+ regVal);
regVal &= ~(SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
@@ -904,9 +985,9 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* step 9: bring the BDMA and OSSP out of reset */
regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("Top Register before bringing up BDMA/OSSP"
- ":= 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT,
+ "Top Register before bringing up BDMA/OSSP:= 0x%x\n",
+ regVal);
regVal |= (SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
@@ -915,15 +996,15 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* step 11: reads and sets the GSM Configuration and Reset Register */
/* map 0x0700000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
- GSM_ADDR_BASE));
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL, "SPC Shift Bar4 to 0x%x failed\n",
+ GSM_ADDR_BASE);
return -1;
}
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x0 (0x00007b88)-GSM Configuration and "
- "Reset = 0x%x\n", pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x0 (0x00007b88)-GSM Configuration and Reset = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));
regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
/* Put those bits to high */
/* GSM XCBI offset = 0x70 0000
@@ -935,43 +1016,37 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
*/
regVal |= (GSM_CONFIG_RESET_VALUE);
pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM (0x00004088 ==> 0x00007b88) - GSM"
- " Configuration and Reset is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+ pm8001_dbg(pm8001_ha, INIT, "GSM (0x00004088 ==> 0x00007b88) - GSM Configuration and Reset is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));
/* step 12: Restore GSM - Read Address Parity Check */
regVal = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
/* just for debugging */
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
- " = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x700038 - Read Address Parity Check Enable = 0x%x\n",
+ regVal);
pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, regVal1);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700038 - Read Address Parity"
- " Check Enable is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+ pm8001_dbg(pm8001_ha, INIT, "GSM 0x700038 - Read Address Parity Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK));
/* Restore GSM - Write Address Parity Check */
regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, regVal2);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700040 - Write Address Parity Check"
- " Enable is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x700040 - Write Address Parity Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK));
/* Restore GSM - Write Data Parity Check */
regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, regVal3);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("GSM 0x700048 - Write Data Parity Check Enable"
- "is set to = 0x%x\n",
- pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "GSM 0x700048 - Write Data Parity Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK));
/* step 13: bring the IOP and AAP1 out of reset */
/* map 0x00000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Shift Bar4 to 0x%x failed\n",
- SPC_TOP_LEVEL_ADDR_BASE));
+ if (-1 == pm8001_bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
+ SPC_TOP_LEVEL_ADDR_BASE);
return -1;
}
regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
@@ -994,22 +1069,21 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
if (!max_wait_count) {
regVal = pm8001_cr32(pm8001_ha, 0,
MSGU_SCRATCH_PAD_1);
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("TIMEOUT : ToggleVal 0x%x,"
- "MSGU_SCRATCH_PAD1 = 0x%x\n",
- toggleVal, regVal));
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
- pm8001_cr32(pm8001_ha, 0,
- MSGU_SCRATCH_PAD_0)));
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("SCRATCH_PAD2 value = 0x%x\n",
- pm8001_cr32(pm8001_ha, 0,
- MSGU_SCRATCH_PAD_2)));
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
- pm8001_cr32(pm8001_ha, 0,
- MSGU_SCRATCH_PAD_3)));
+ pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT : ToggleVal 0x%x,MSGU_SCRATCH_PAD1 = 0x%x\n",
+ toggleVal, regVal);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_0));
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SCRATCH_PAD2 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_2));
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_3));
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return -1;
}
@@ -1023,28 +1097,30 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
if (check_fw_ready(pm8001_ha) == -1) {
regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
/* return error if MPI Configuration Table not ready */
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("FW not ready SCRATCH_PAD1"
- " = 0x%x\n", regVal));
+ pm8001_dbg(pm8001_ha, INIT,
+ "FW not ready SCRATCH_PAD1 = 0x%x\n",
+ regVal);
regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
/* return error if MPI Configuration Table not ready */
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("FW not ready SCRATCH_PAD2"
- " = 0x%x\n", regVal));
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
- pm8001_cr32(pm8001_ha, 0,
- MSGU_SCRATCH_PAD_0)));
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
- pm8001_cr32(pm8001_ha, 0,
- MSGU_SCRATCH_PAD_3)));
+ pm8001_dbg(pm8001_ha, INIT,
+ "FW not ready SCRATCH_PAD2 = 0x%x\n",
+ regVal);
+ pm8001_dbg(pm8001_ha, INIT,
+ "SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_0));
+ pm8001_dbg(pm8001_ha, INIT,
+ "SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_3));
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return -1;
}
}
+ pm8001_bar4_shift(pm8001_ha, 0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("SPC soft reset Complete\n"));
+ pm8001_dbg(pm8001_ha, INIT, "SPC soft reset Complete\n");
return 0;
}
@@ -1052,8 +1128,7 @@ static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
{
u32 i;
u32 regVal;
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("chip reset start\n"));
+ pm8001_dbg(pm8001_ha, INIT, "chip reset start\n");
/* do SPC chip reset. */
regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
@@ -1077,18 +1152,17 @@ static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
mdelay(1);
} while ((--i) != 0);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("chip reset finished\n"));
+ pm8001_dbg(pm8001_ha, INIT, "chip reset finished\n");
}
/**
- * pm8001_chip_iounmap - which maped when initialized.
+ * pm8001_chip_iounmap - which mapped when initialized.
* @pm8001_ha: our hba card information
*/
-static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha)
+void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha)
{
s8 bar, logical = 0;
- for (bar = 0; bar < 6; bar++) {
+ for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
/*
** logical BARs for SPC:
** bar 0 and 1 - logical BAR0
@@ -1109,93 +1183,44 @@ static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha)
/**
* pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
* @pm8001_ha: our hba card information
+ * @vec: unused
*/
static void
-pm8001_chip_intx_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
-{
- pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
- pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
-}
-
- /**
- * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
- * @pm8001_ha: our hba card information
- */
-static void
-pm8001_chip_intx_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
-{
- pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL);
-}
-
-/**
- * pm8001_chip_msix_interrupt_enable - enable PM8001 chip interrupt
- * @pm8001_ha: our hba card information
- */
-static void
-pm8001_chip_msix_interrupt_enable(struct pm8001_hba_info *pm8001_ha,
- u32 int_vec_idx)
-{
- u32 msi_index;
- u32 value;
- msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
- msi_index += MSIX_TABLE_BASE;
- pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_ENABLE);
- value = (1 << int_vec_idx);
- pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, value);
-
-}
-
-/**
- * pm8001_chip_msix_interrupt_disable - disable PM8001 chip interrupt
- * @pm8001_ha: our hba card information
- */
-static void
-pm8001_chip_msix_interrupt_disable(struct pm8001_hba_info *pm8001_ha,
- u32 int_vec_idx)
-{
- u32 msi_index;
- msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
- msi_index += MSIX_TABLE_BASE;
- pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_DISABLE);
-
-}
-/**
- * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
- * @pm8001_ha: our hba card information
- */
-static void
-pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
+pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
-#ifdef PM8001_USE_MSIX
- pm8001_chip_msix_interrupt_enable(pm8001_ha, 0);
- return;
-#endif
- pm8001_chip_intx_interrupt_enable(pm8001_ha);
-
+ if (pm8001_ha->use_msix) {
+ pm8001_cw32(pm8001_ha, 0, MSIX_TABLE_BASE,
+ MSIX_INTERRUPT_ENABLE);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, 1);
+ } else {
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+ }
}
/**
- * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
+ * pm8001_chip_interrupt_disable - disable PM8001 chip interrupt
* @pm8001_ha: our hba card information
+ * @vec: unused
*/
static void
-pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
-#ifdef PM8001_USE_MSIX
- pm8001_chip_msix_interrupt_disable(pm8001_ha, 0);
- return;
-#endif
- pm8001_chip_intx_interrupt_disable(pm8001_ha);
-
+ if (pm8001_ha->use_msix)
+ pm8001_cw32(pm8001_ha, 0, MSIX_TABLE_BASE,
+ MSIX_INTERRUPT_DISABLE);
+ else
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL);
}
/**
- * mpi_msg_free_get- get the free message buffer for transfer inbound queue.
+ * pm8001_mpi_msg_free_get - get the free message buffer for transfer
+ * inbound queue.
* @circularQ: the inbound queue we want to transfer to HBA.
* @messageSize: the message size of this transfer, normally it is 64 bytes
* @messagePtr: the pointer to message.
*/
-static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
+int pm8001_mpi_msg_free_get(struct inbound_queue_table *circularQ,
u16 messageSize, void **messagePtr)
{
u32 offset, consumer_index;
@@ -1203,7 +1228,7 @@ static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
u8 bcCount = 1; /* only support single buffer */
/* Checks is the requested message size can be allocated in this queue*/
- if (messageSize > 64) {
+ if (messageSize > IOMB_SIZE_SPCV) {
*messagePtr = NULL;
return -1;
}
@@ -1211,15 +1236,16 @@ static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
/* Stores the new consumer index */
consumer_index = pm8001_read_32(circularQ->ci_virt);
circularQ->consumer_index = cpu_to_le32(consumer_index);
- if (((circularQ->producer_idx + bcCount) % 256) ==
- circularQ->consumer_index) {
+ if (((circularQ->producer_idx + bcCount) % PM8001_MPI_QUEUE) ==
+ le32_to_cpu(circularQ->consumer_index)) {
*messagePtr = NULL;
return -1;
}
/* get memory IOMB buffer address */
- offset = circularQ->producer_idx * 64;
+ offset = circularQ->producer_idx * messageSize;
/* increment to next bcCount element */
- circularQ->producer_idx = (circularQ->producer_idx + bcCount) % 256;
+ circularQ->producer_idx = (circularQ->producer_idx + bcCount)
+ % PM8001_MPI_QUEUE;
/* Adds that distance to the base of the region virtual address plus
the message header size*/
msgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt + offset);
@@ -1228,29 +1254,47 @@ static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
}
/**
- * mpi_build_cmd- build the message queue for transfer, update the PI to FW
- * to tell the fw to get this message from IOMB.
+ * pm8001_mpi_build_cmd- build the message queue for transfer, update the PI to
+ * FW to tell the fw to get this message from IOMB.
* @pm8001_ha: our hba card information
- * @circularQ: the inbound queue we want to transfer to HBA.
+ * @q_index: the index in the inbound queue we want to transfer to HBA.
* @opCode: the operation code represents commands which LLDD and fw recognized.
* @payload: the command payload of each operation command.
+ * @nb: size in bytes of the command payload
+ * @responseQueue: queue to interrupt on w/ command response (if any)
*/
-static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
- struct inbound_queue_table *circularQ,
- u32 opCode, void *payload)
+int pm8001_mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
+ u32 q_index, u32 opCode, void *payload, size_t nb,
+ u32 responseQueue)
{
u32 Header = 0, hpriority = 0, bc = 1, category = 0x02;
- u32 responseQueue = 0;
void *pMessage;
-
- if (mpi_msg_free_get(circularQ, 64, &pMessage) < 0) {
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("No free mpi buffer \n"));
- return -1;
+ unsigned long flags;
+ struct inbound_queue_table *circularQ = &pm8001_ha->inbnd_q_tbl[q_index];
+ int rv;
+ u32 htag = le32_to_cpu(*(__le32 *)payload);
+
+ trace_pm80xx_mpi_build_cmd(pm8001_ha->id, opCode, htag, q_index,
+ circularQ->producer_idx, le32_to_cpu(circularQ->consumer_index));
+
+ if (WARN_ON(q_index >= pm8001_ha->max_q_num))
+ return -EINVAL;
+
+ spin_lock_irqsave(&circularQ->iq_lock, flags);
+ rv = pm8001_mpi_msg_free_get(circularQ, pm8001_ha->iomb_size,
+ &pMessage);
+ if (rv < 0) {
+ pm8001_dbg(pm8001_ha, IO, "No free mpi buffer\n");
+ rv = -ENOMEM;
+ goto done;
}
- BUG_ON(!payload);
- /*Copy to the payload*/
- memcpy(pMessage, payload, (64 - sizeof(struct mpi_msg_hdr)));
+
+ if (nb > (pm8001_ha->iomb_size - sizeof(struct mpi_msg_hdr)))
+ nb = pm8001_ha->iomb_size - sizeof(struct mpi_msg_hdr);
+ memcpy(pMessage, payload, nb);
+ if (nb + sizeof(struct mpi_msg_hdr) < pm8001_ha->iomb_size)
+ memset(pMessage + nb, 0, pm8001_ha->iomb_size -
+ (nb + sizeof(struct mpi_msg_hdr)));
/*Build the header*/
Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24)
@@ -1261,13 +1305,16 @@ static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
/*Update the PI to the firmware*/
pm8001_cw32(pm8001_ha, circularQ->pi_pci_bar,
circularQ->pi_offset, circularQ->producer_idx);
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("after PI= %d CI= %d \n", circularQ->producer_idx,
- circularQ->consumer_index));
- return 0;
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "INB Q %x OPCODE:%x , UPDATED PI=%d CI=%d\n",
+ responseQueue, opCode, circularQ->producer_idx,
+ circularQ->consumer_index);
+done:
+ spin_unlock_irqrestore(&circularQ->iq_lock, flags);
+ return rv;
}
-static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
+u32 pm8001_mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
struct outbound_queue_table *circularQ, u8 bc)
{
u32 producer_index;
@@ -1276,43 +1323,44 @@ static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
msgHeader = (struct mpi_msg_hdr *)(pMsg - sizeof(struct mpi_msg_hdr));
pOutBoundMsgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt +
- circularQ->consumer_idx * 64);
+ circularQ->consumer_idx * pm8001_ha->iomb_size);
if (pOutBoundMsgHeader != msgHeader) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("consumer_idx = %d msgHeader = %p\n",
- circularQ->consumer_idx, msgHeader));
+ pm8001_dbg(pm8001_ha, FAIL,
+ "consumer_idx = %d msgHeader = %p\n",
+ circularQ->consumer_idx, msgHeader);
/* Update the producer index from SPC */
producer_index = pm8001_read_32(circularQ->pi_virt);
circularQ->producer_index = cpu_to_le32(producer_index);
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("consumer_idx = %d producer_index = %d"
- "msgHeader = %p\n", circularQ->consumer_idx,
- circularQ->producer_index, msgHeader));
+ pm8001_dbg(pm8001_ha, FAIL,
+ "consumer_idx = %d producer_index = %dmsgHeader = %p\n",
+ circularQ->consumer_idx,
+ circularQ->producer_index, msgHeader);
return 0;
}
/* free the circular queue buffer elements associated with the message*/
- circularQ->consumer_idx = (circularQ->consumer_idx + bc) % 256;
+ circularQ->consumer_idx = (circularQ->consumer_idx + bc)
+ % PM8001_MPI_QUEUE;
/* update the CI of outbound queue */
pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, circularQ->ci_offset,
circularQ->consumer_idx);
/* Update the producer index from SPC*/
producer_index = pm8001_read_32(circularQ->pi_virt);
circularQ->producer_index = cpu_to_le32(producer_index);
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk(" CI=%d PI=%d\n", circularQ->consumer_idx,
- circularQ->producer_index));
+ pm8001_dbg(pm8001_ha, IO, " CI=%d PI=%d\n",
+ circularQ->consumer_idx, circularQ->producer_index);
return 0;
}
/**
- * mpi_msg_consume- get the MPI message from outbound queue message table.
+ * pm8001_mpi_msg_consume- get the MPI message from outbound queue
+ * message table.
* @pm8001_ha: our hba card information
* @circularQ: the outbound queue table.
* @messagePtr1: the message contents of this outbound message.
* @pBC: the message size.
*/
-static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
+u32 pm8001_mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
struct outbound_queue_table *circularQ,
void **messagePtr1, u8 *pBC)
{
@@ -1321,34 +1369,39 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
u32 header_tmp;
do {
/* If there are not-yet-delivered messages ... */
- if (circularQ->producer_index != circularQ->consumer_idx) {
+ if (le32_to_cpu(circularQ->producer_index)
+ != circularQ->consumer_idx) {
/*Get the pointer to the circular queue buffer element*/
msgHeader = (struct mpi_msg_hdr *)
(circularQ->base_virt +
- circularQ->consumer_idx * 64);
+ circularQ->consumer_idx * pm8001_ha->iomb_size);
/* read header */
header_tmp = pm8001_read_32(msgHeader);
msgHeader_tmp = cpu_to_le32(header_tmp);
- if (0 != (msgHeader_tmp & 0x80000000)) {
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "outbound opcode msgheader:%x ci=%d pi=%d\n",
+ msgHeader_tmp, circularQ->consumer_idx,
+ circularQ->producer_index);
+ if (0 != (le32_to_cpu(msgHeader_tmp) & 0x80000000)) {
if (OPC_OUB_SKIP_ENTRY !=
- (msgHeader_tmp & 0xfff)) {
+ (le32_to_cpu(msgHeader_tmp) & 0xfff)) {
*messagePtr1 =
((u8 *)msgHeader) +
sizeof(struct mpi_msg_hdr);
- *pBC = (u8)((msgHeader_tmp >> 24) &
- 0x1f);
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk(": CI=%d PI=%d "
- "msgHeader=%x\n",
- circularQ->consumer_idx,
- circularQ->producer_index,
- msgHeader_tmp));
+ *pBC = (u8)((le32_to_cpu(msgHeader_tmp)
+ >> 24) & 0x1f);
+ pm8001_dbg(pm8001_ha, IO,
+ ": CI=%d PI=%d msgHeader=%x\n",
+ circularQ->consumer_idx,
+ circularQ->producer_index,
+ msgHeader_tmp);
return MPI_IO_STATUS_SUCCESS;
} else {
circularQ->consumer_idx =
(circularQ->consumer_idx +
- ((msgHeader_tmp >> 24) & 0x1f))
- % 256;
+ ((le32_to_cpu(msgHeader_tmp)
+ >> 24) & 0x1f))
+ % PM8001_MPI_QUEUE;
msgHeader_tmp = 0;
pm8001_write_32(msgHeader, 0, 0);
/* update the CI of outbound queue */
@@ -1360,7 +1413,8 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
} else {
circularQ->consumer_idx =
(circularQ->consumer_idx +
- ((msgHeader_tmp >> 24) & 0x1f)) % 256;
+ ((le32_to_cpu(msgHeader_tmp) >> 24) &
+ 0x1f)) % PM8001_MPI_QUEUE;
msgHeader_tmp = 0;
pm8001_write_32(msgHeader, 0, 0);
/* update the CI of outbound queue */
@@ -1372,48 +1426,261 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
} else {
u32 producer_index;
void *pi_virt = circularQ->pi_virt;
+ /* spurious interrupt during setup if
+ * kexec-ing and driver doing a doorbell access
+ * with the pre-kexec oq interrupt setup
+ */
+ if (!pi_virt)
+ break;
/* Update the producer index from SPC */
producer_index = pm8001_read_32(pi_virt);
circularQ->producer_index = cpu_to_le32(producer_index);
}
- } while (circularQ->producer_index != circularQ->consumer_idx);
+ } while (le32_to_cpu(circularQ->producer_index) !=
+ circularQ->consumer_idx);
/* while we don't have any more not-yet-delivered message */
/* report empty */
return MPI_IO_STATUS_BUSY;
}
-static void pm8001_work_fn(struct work_struct *work)
+void pm8001_work_fn(struct work_struct *work)
{
struct pm8001_work *pw = container_of(work, struct pm8001_work, work);
struct pm8001_device *pm8001_dev;
struct domain_device *dev;
+ /*
+ * So far, all users of this stash an associated structure here.
+ * If we get here, and this pointer is null, then the action
+ * was cancelled. This nullification happens when the device
+ * goes away.
+ */
+ if (pw->handler != IO_FATAL_ERROR) {
+ pm8001_dev = pw->data; /* Most stash device structure */
+ if ((pm8001_dev == NULL)
+ || ((pw->handler != IO_XFER_ERROR_BREAK)
+ && (pm8001_dev->dev_type == SAS_PHY_UNUSED))) {
+ kfree(pw);
+ return;
+ }
+ }
+
switch (pw->handler) {
+ case IO_XFER_ERROR_BREAK:
+ { /* This one stashes the sas_task instead */
+ struct sas_task *t = (struct sas_task *)pm8001_dev;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
+ unsigned long flags, flags1;
+ struct task_status_struct *ts;
+ int i;
+
+ if (pm8001_query_task(t) == TMF_RESP_FUNC_SUCC)
+ break; /* Task still on lu */
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ spin_lock_irqsave(&t->task_state_lock, flags1);
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ break; /* Task got completed by another */
+ }
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+
+ /* Search for a possible ccb that matches the task */
+ for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
+ ccb = &pm8001_ha->ccb_info[i];
+ if ((ccb->ccb_tag != PM8001_INVALID_TAG) &&
+ (ccb->task == t))
+ break;
+ }
+ if (!ccb) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ break; /* Task got freed by another */
+ }
+ ts = &t->task_status;
+ ts->resp = SAS_TASK_COMPLETE;
+ /* Force the midlayer to retry */
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_dev = ccb->device;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ spin_lock_irqsave(&t->task_state_lock, flags1);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ pm8001_dbg(pm8001_ha, FAIL, "task 0x%p done with event 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, pw->handler, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ mb();/* in order to force CPU ordering */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ t->task_done(t);
+ }
+ } break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ { /* This one stashes the sas_task instead */
+ struct sas_task *t = (struct sas_task *)pm8001_dev;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
+ unsigned long flags, flags1;
+ int i, ret = 0;
+
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
+
+ ret = pm8001_query_task(t);
+
+ if (ret == TMF_RESP_FUNC_SUCC)
+ pm8001_dbg(pm8001_ha, IO, "...Task on lu\n");
+ else if (ret == TMF_RESP_FUNC_COMPLETE)
+ pm8001_dbg(pm8001_ha, IO, "...Task NOT on lu\n");
+ else
+ pm8001_dbg(pm8001_ha, DEVIO, "...query task failed!!!\n");
+
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ spin_lock_irqsave(&t->task_state_lock, flags1);
+
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
+ (void)pm8001_abort_task(t);
+ break; /* Task got completed by another */
+ }
+
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+
+ /* Search for a possible ccb that matches the task */
+ for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
+ ccb = &pm8001_ha->ccb_info[i];
+ if ((ccb->ccb_tag != PM8001_INVALID_TAG) &&
+ (ccb->task == t))
+ break;
+ }
+ if (!ccb) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
+ (void)pm8001_abort_task(t);
+ break; /* Task got freed by another */
+ }
+
+ pm8001_dev = ccb->device;
+ dev = pm8001_dev->sas_device;
+
+ switch (ret) {
+ case TMF_RESP_FUNC_SUCC: /* task on lu */
+ ccb->open_retry = 1; /* Snub completion */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ ret = pm8001_abort_task(t);
+ ccb->open_retry = 0;
+ switch (ret) {
+ case TMF_RESP_FUNC_SUCC:
+ case TMF_RESP_FUNC_COMPLETE:
+ break;
+ default: /* device misbehavior */
+ ret = TMF_RESP_FUNC_FAILED;
+ pm8001_dbg(pm8001_ha, IO, "...Reset phy\n");
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ }
+ break;
+
+ case TMF_RESP_FUNC_COMPLETE: /* task not on lu */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ /* Do we need to abort the task locally? */
+ break;
+
+ default: /* device misbehavior */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ ret = TMF_RESP_FUNC_FAILED;
+ pm8001_dbg(pm8001_ha, IO, "...Reset phy\n");
+ pm8001_I_T_nexus_reset(dev);
+ }
+
+ if (ret == TMF_RESP_FUNC_FAILED)
+ t = NULL;
+ pm8001_open_reject_retry(pm8001_ha, t, pm8001_dev);
+ pm8001_dbg(pm8001_ha, IO, "...Complete\n");
+ } break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
- pm8001_I_T_nexus_reset(dev);
+ pm8001_I_T_nexus_event_handler(dev);
break;
case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
pm8001_I_T_nexus_reset(dev);
break;
case IO_DS_IN_ERROR:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
pm8001_I_T_nexus_reset(dev);
break;
case IO_DS_NON_OPERATIONAL:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
pm8001_I_T_nexus_reset(dev);
break;
+ case IO_FATAL_ERROR:
+ {
+ struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
+ struct pm8001_ccb_info *ccb;
+ struct task_status_struct *ts;
+ struct sas_task *task;
+ int i;
+ u32 device_id;
+
+ for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
+ ccb = &pm8001_ha->ccb_info[i];
+ task = ccb->task;
+ ts = &task->task_status;
+
+ if (task != NULL) {
+ dev = task->dev;
+ if (!dev) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "dev is NULL\n");
+ continue;
+ }
+ /*complete sas task and update to top layer */
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ ts->resp = SAS_TASK_COMPLETE;
+ task->task_done(task);
+ } else if (ccb->ccb_tag != PM8001_INVALID_TAG) {
+ /* complete the internal commands/non-sas task */
+ pm8001_dev = ccb->device;
+ if (pm8001_dev->dcompletion) {
+ complete(pm8001_dev->dcompletion);
+ pm8001_dev->dcompletion = NULL;
+ }
+ complete(pm8001_ha->nvmd_completion);
+ pm8001_ccb_free(pm8001_ha, ccb);
+ }
+ }
+ /* Deregister all the device ids */
+ for (i = 0; i < PM8001_MAX_DEVICES; i++) {
+ pm8001_dev = &pm8001_ha->devices[i];
+ device_id = pm8001_dev->device_id;
+ if (device_id) {
+ PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id);
+ pm8001_free_dev(pm8001_dev);
+ }
+ }
+ }
+ break;
+ case IO_XFER_ERROR_ABORTED_NCQ_MODE:
+ {
+ dev = pm8001_dev->sas_device;
+ sas_ata_device_link_abort(dev, false);
+ }
+ break;
}
kfree(pw);
}
-static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
+int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
int handler)
{
struct pm8001_work *pw;
@@ -1438,13 +1705,13 @@ static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
* @piomb: the message contents of this outbound message.
*
* When FW has completed a ssp request for example a IO request, after it has
- * filled the SG data with the data, it will trigger this event represent
- * that he has finished the job,please check the coresponding buffer.
+ * filled the SG data with the data, it will trigger this event representing
+ * that he has finished the job; please check the corresponding buffer.
* So we will tell the caller who maybe waiting the result to tell upper layer
* that the task has been finished.
*/
static void
-mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
+mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
struct sas_task *t;
struct pm8001_ccb_info *ccb;
@@ -1460,24 +1727,39 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
status = le32_to_cpu(psspPayload->status);
tag = le32_to_cpu(psspPayload->tag);
ccb = &pm8001_ha->ccb_info[tag];
+ if ((status == IO_ABORTED) && ccb->open_retry) {
+ /* Being completed by another */
+ ccb->open_retry = 0;
+ return;
+ }
pm8001_dev = ccb->device;
param = le32_to_cpu(psspPayload->param);
t = ccb->task;
if (status && status != IO_UNDERFLOW)
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("sas IO status 0x%x\n", status));
+ pm8001_dbg(pm8001_ha, FAIL, "sas IO status 0x%x\n", status);
if (unlikely(!t || !t->lldd_task || !t->dev))
return;
ts = &t->task_status;
+ /* Print sas address of IO failed device */
+ if ((status != IO_SUCCESS) && (status != IO_OVERFLOW) &&
+ (status != IO_UNDERFLOW))
+ pm8001_dbg(pm8001_ha, FAIL, "SAS Address of IO Failure Drive:%016llx\n",
+ SAS_ADDR(t->dev->sas_addr));
+
+ if (status)
+ pm8001_dbg(pm8001_ha, IOERR,
+ "status:0x%x, tag:0x%x, task:0x%p\n",
+ status, tag, t);
+
switch (status) {
case IO_SUCCESS:
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS"
- ",param = %d \n", param));
+ pm8001_dbg(pm8001_ha, IO, "IO_SUCCESS,param = %d\n",
+ param);
if (param == 0) {
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_GOOD;
+ ts->stat = SAS_SAM_STAT_GOOD;
} else {
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_PROTO_RESPONSE;
@@ -1486,67 +1768,63 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
sas_ssp_task_response(pm8001_ha->dev, t, iu);
}
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_ABORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_ABORTED IOMB Tag \n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_ABORTED IOMB Tag\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_ABORTED_TASK;
break;
case IO_UNDERFLOW:
/* SSP Completion with error */
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW"
- ",param = %d \n", param));
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW,param = %d\n",
+ param);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_UNDERRUN;
ts->residual = param;
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_NO_DEVICE:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_NO_DEVICE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_NO_DEVICE\n");
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_PHY_DOWN;
break;
case IO_XFER_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
+ /* Force the midlayer to retry */
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_XFER_ERROR_PHY_NOT_READY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_EPROTO;
break;
case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
break;
case IO_OPEN_CNX_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
@@ -1556,68 +1834,59 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
break;
case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_BAD_DEST;
break;
case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
- "NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_CONN_RATE;
break;
case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
break;
case IO_XFER_ERROR_NAK_RECEIVED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_ACK_NAK_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_NAK_R_ERR;
break;
case IO_XFER_ERROR_DMA:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_DMA\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_DMA\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
break;
case IO_XFER_OPEN_RETRY_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_XFER_ERROR_OFFSET_MISMATCH:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_OFFSET_MISMATCH\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
break;
case IO_PORT_IN_RESET:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_PORT_IN_RESET\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_PORT_IN_RESET\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
break;
case IO_DS_NON_OPERATIONAL:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_NON_OPERATIONAL\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
if (!t->uldd_task)
@@ -1626,61 +1895,54 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
IO_DS_NON_OPERATIONAL);
break;
case IO_DS_IN_RECOVERY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_DS_IN_RECOVERY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_IN_RECOVERY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
break;
case IO_TM_TAG_NOT_FOUND:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_TM_TAG_NOT_FOUND\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_TM_TAG_NOT_FOUND\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
break;
case IO_SSP_EXT_IU_ZERO_LEN_ERROR:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_SSP_EXT_IU_ZERO_LEN_ERROR\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_SSP_EXT_IU_ZERO_LEN_ERROR\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
break;
case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
default:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("Unknown status 0x%x\n", status));
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", status);
/* not allowed case. Therefore, return failed status */
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
break;
}
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("scsi_status = %x \n ",
- psspPayload->ssp_resp_iu.status));
+ pm8001_dbg(pm8001_ha, IO, "scsi_status = %x\n",
+ psspPayload->ssp_resp_iu.status);
spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
- t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
- " io_status 0x%x resp 0x%x "
- "stat 0x%x but aborted by upper layer!\n",
- t, status, ts->resp, ts->stat));
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ pm8001_dbg(pm8001_ha, FAIL, "task 0x%p done with io_status 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
} else {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
mb();/* in order to force CPU ordering */
t->task_done(t);
}
}
/*See the comments for mpi_ssp_completion */
-static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
struct sas_task *t;
unsigned long flags;
@@ -1698,61 +1960,52 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
t = ccb->task;
pm8001_dev = ccb->device;
if (event)
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("sas IO status 0x%x\n", event));
+ pm8001_dbg(pm8001_ha, FAIL, "sas IO status 0x%x\n", event);
if (unlikely(!t || !t->lldd_task || !t->dev))
return;
ts = &t->task_status;
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("port_id = %x,device_id = %x\n",
- port_id, dev_id));
+ pm8001_dbg(pm8001_ha, DEVIO, "port_id = %x,device_id = %x\n",
+ port_id, dev_id);
switch (event) {
case IO_OVERFLOW:
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n");)
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
ts->residual = 0;
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_BREAK\n"));
- ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAS_INTERRUPTED;
- break;
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
+ pm8001_handle_event(pm8001_ha, t, IO_XFER_ERROR_BREAK);
+ return;
case IO_XFER_ERROR_PHY_NOT_READY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
- "_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_EPROTO;
break;
case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
break;
case IO_OPEN_CNX_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
@@ -1762,90 +2015,78 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
break;
case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_BAD_DEST;
break;
case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
- "NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_CONN_RATE;
break;
case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
break;
case IO_XFER_ERROR_NAK_RECEIVED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_ACK_NAK_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_NAK_R_ERR;
break;
case IO_XFER_OPEN_RETRY_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
- ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAS_OPEN_REJECT;
- ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
- break;
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
+ pm8001_handle_event(pm8001_ha, t, IO_XFER_OPEN_RETRY_TIMEOUT);
+ return;
case IO_XFER_ERROR_UNEXPECTED_PHASE:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_UNEXPECTED_PHASE\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
break;
case IO_XFER_ERROR_XFER_RDY_OVERRUN:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_XFER_RDY_OVERRUN\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
break;
case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
break;
case IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
break;
case IO_XFER_ERROR_OFFSET_MISMATCH:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_OFFSET_MISMATCH\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
break;
case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
break;
case IO_XFER_CMD_FRAME_ISSUED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk(" IO_XFER_CMD_FRAME_ISSUED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_CMD_FRAME_ISSUED\n");
return;
default:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("Unknown status 0x%x\n", event));
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", event);
/* not allowed case. Therefore, return failed status */
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
@@ -1853,18 +2094,15 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
}
spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
- t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
- " event 0x%x resp 0x%x "
- "stat 0x%x but aborted by upper layer!\n",
- t, event, ts->resp, ts->stat));
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ pm8001_dbg(pm8001_ha, FAIL, "task 0x%p done with event 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, event, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
} else {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
mb();/* in order to force CPU ordering */
t->task_done(t);
}
@@ -1876,135 +2114,199 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
struct sas_task *t;
struct pm8001_ccb_info *ccb;
- unsigned long flags = 0;
u32 param;
u32 status;
u32 tag;
+ int i, j;
+ u8 sata_addr_low[4];
+ u32 temp_sata_addr_low;
+ u8 sata_addr_hi[4];
+ u32 temp_sata_addr_hi;
struct sata_completion_resp *psataPayload;
struct task_status_struct *ts;
struct ata_task_resp *resp ;
u32 *sata_resp;
struct pm8001_device *pm8001_dev;
+ unsigned long flags;
psataPayload = (struct sata_completion_resp *)(piomb + 4);
status = le32_to_cpu(psataPayload->status);
+ param = le32_to_cpu(psataPayload->param);
tag = le32_to_cpu(psataPayload->tag);
ccb = &pm8001_ha->ccb_info[tag];
- param = le32_to_cpu(psataPayload->param);
t = ccb->task;
- ts = &t->task_status;
pm8001_dev = ccb->device;
- if (status)
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("sata IO status 0x%x\n", status));
- if (unlikely(!t || !t->lldd_task || !t->dev))
+
+ if (t) {
+ if (t->dev && (t->dev->lldd_dev))
+ pm8001_dev = t->dev->lldd_dev;
+ } else {
+ pm8001_dbg(pm8001_ha, FAIL, "task null, freeing CCB tag %d\n",
+ ccb->ccb_tag);
+ pm8001_ccb_free(pm8001_ha, ccb);
return;
+ }
+
+ if (pm8001_dev && unlikely(!t || !t->lldd_task || !t->dev)) {
+ pm8001_dbg(pm8001_ha, FAIL, "task or dev null\n");
+ return;
+ }
+ ts = &t->task_status;
+
+ if (status)
+ pm8001_dbg(pm8001_ha, IOERR,
+ "status:0x%x, tag:0x%x, task::0x%p\n",
+ status, tag, t);
+
+ /* Print sas address of IO failed device */
+ if ((status != IO_SUCCESS) && (status != IO_OVERFLOW) &&
+ (status != IO_UNDERFLOW)) {
+ if (!dev_parent_is_expander(t->dev)) {
+ for (i = 0, j = 4; j <= 7 && i <= 3; i++, j++)
+ sata_addr_low[i] = pm8001_ha->sas_addr[j];
+ for (i = 0, j = 0; j <= 3 && i <= 3; i++, j++)
+ sata_addr_hi[i] = pm8001_ha->sas_addr[j];
+ memcpy(&temp_sata_addr_low, sata_addr_low,
+ sizeof(sata_addr_low));
+ memcpy(&temp_sata_addr_hi, sata_addr_hi,
+ sizeof(sata_addr_hi));
+ temp_sata_addr_hi = (((temp_sata_addr_hi >> 24) & 0xff)
+ |((temp_sata_addr_hi << 8) &
+ 0xff0000) |
+ ((temp_sata_addr_hi >> 8)
+ & 0xff00) |
+ ((temp_sata_addr_hi << 24) &
+ 0xff000000));
+ temp_sata_addr_low = ((((temp_sata_addr_low >> 24)
+ & 0xff) |
+ ((temp_sata_addr_low << 8)
+ & 0xff0000) |
+ ((temp_sata_addr_low >> 8)
+ & 0xff00) |
+ ((temp_sata_addr_low << 24)
+ & 0xff000000)) +
+ pm8001_dev->attached_phy +
+ 0x10);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SAS Address of IO Failure Drive:%08x%08x\n",
+ temp_sata_addr_hi,
+ temp_sata_addr_low);
+ } else {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SAS Address of IO Failure Drive:%016llx\n",
+ SAS_ADDR(t->dev->sas_addr));
+ }
+ }
switch (status) {
case IO_SUCCESS:
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_SUCCESS\n");
if (param == 0) {
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_GOOD;
+ ts->stat = SAS_SAM_STAT_GOOD;
} else {
u8 len;
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_PROTO_RESPONSE;
ts->residual = param;
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("SAS_PROTO_RESPONSE len = %d\n",
- param));
+ pm8001_dbg(pm8001_ha, IO,
+ "SAS_PROTO_RESPONSE len = %d\n",
+ param);
sata_resp = &psataPayload->sata_resp[0];
resp = (struct ata_task_resp *)ts->buf;
if (t->ata_task.dma_xfer == 0 &&
- t->data_dir == PCI_DMA_FROMDEVICE) {
+ t->data_dir == DMA_FROM_DEVICE) {
len = sizeof(struct pio_setup_fis);
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("PIO read len = %d\n", len));
- } else if (t->ata_task.use_ncq) {
+ pm8001_dbg(pm8001_ha, IO,
+ "PIO read len = %d\n", len);
+ } else if (t->ata_task.use_ncq &&
+ t->data_dir != DMA_NONE) {
len = sizeof(struct set_dev_bits_fis);
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("FPDMA len = %d\n", len));
+ pm8001_dbg(pm8001_ha, IO, "FPDMA len = %d\n",
+ len);
} else {
len = sizeof(struct dev_to_host_fis);
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("other len = %d\n", len));
+ pm8001_dbg(pm8001_ha, IO, "other len = %d\n",
+ len);
}
if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
resp->frame_len = len;
memcpy(&resp->ending_fis[0], sata_resp, len);
ts->buf_valid_size = sizeof(*resp);
} else
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("response to large \n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "response too large\n");
}
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_ABORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_ABORTED IOMB Tag \n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_ABORTED IOMB Tag\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_ABORTED_TASK;
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
/* following cases are to do cases */
case IO_UNDERFLOW:
/* SATA Completion with error */
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_UNDERFLOW param = %d\n", param));
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW param = %d\n", param);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_UNDERRUN;
ts->residual = param;
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_NO_DEVICE:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_NO_DEVICE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_NO_DEVICE\n");
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_PHY_DOWN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_INTERRUPTED;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_PHY_NOT_READY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
- "_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_EPROTO;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_OPEN_CNX_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
if (!t->uldd_task) {
@@ -2013,17 +2315,13 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_QUEUE_FULL;
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/*in order to force CPU ordering*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
return;
}
break;
case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_BAD_DEST;
@@ -2033,26 +2331,20 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_QUEUE_FULL;
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
return;
}
break;
case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
- "NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES"
- "_BUSY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
if (!t->uldd_task) {
@@ -2061,66 +2353,70 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY);
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_QUEUE_FULL;
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/* ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
return;
}
break;
case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_NAK_RECEIVED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_NAK_R_ERR;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_ACK_NAK_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_NAK_R_ERR;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_DMA:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_DMA\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_DMA\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_SATA_LINK_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_SATA_LINK_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_SATA_LINK_TIMEOUT\n");
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_ERROR_REJECTED_NCQ_MODE:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_REJECTED_NCQ_MODE\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_UNDERRUN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_XFER_OPEN_RETRY_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_PORT_IN_RESET:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_PORT_IN_RESET\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_PORT_IN_RESET\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_DS_NON_OPERATIONAL:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_NON_OPERATIONAL\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
if (!t->uldd_task) {
@@ -2128,23 +2424,19 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
IO_DS_NON_OPERATIONAL);
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_QUEUE_FULL;
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
return;
}
break;
case IO_DS_IN_RECOVERY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk(" IO_DS_IN_RECOVERY\n"));
+ pm8001_dbg(pm8001_ha, IO, " IO_DS_IN_RECOVERY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_DS_IN_ERROR:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_DS_IN_ERROR\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_IN_ERROR\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
if (!t->uldd_task) {
@@ -2152,61 +2444,47 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
IO_DS_IN_ERROR);
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_QUEUE_FULL;
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
return;
}
break;
case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
default:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("Unknown status 0x%x\n", status));
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", status);
/* not allowed case. Therefore, return failed status */
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
}
spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
- t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("task 0x%p done with io_status 0x%x"
- " resp 0x%x stat 0x%x but aborted by upper layer!\n",
- t, status, ts->resp, ts->stat));
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- } else if (t->uldd_task) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/* ditto */
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
- } else if (!t->uldd_task) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "task 0x%p done with io_status 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ } else {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
}
}
/*See the comments for mpi_ssp_completion */
-static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
struct sas_task *t;
- unsigned long flags = 0;
struct task_status_struct *ts;
struct pm8001_ccb_info *ccb;
struct pm8001_device *pm8001_dev;
@@ -2217,65 +2495,79 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
u32 port_id = le32_to_cpu(psataPayload->port_id);
u32 dev_id = le32_to_cpu(psataPayload->device_id);
+ if (event)
+ pm8001_dbg(pm8001_ha, FAIL, "SATA EVENT 0x%x\n", event);
+
+ /* Check if this is NCQ error */
+ if (event == IO_XFER_ERROR_ABORTED_NCQ_MODE) {
+ /* find device using device id */
+ pm8001_dev = pm8001_find_dev(pm8001_ha, dev_id);
+ if (pm8001_dev)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_XFER_ERROR_ABORTED_NCQ_MODE);
+ return;
+ }
+
ccb = &pm8001_ha->ccb_info[tag];
t = ccb->task;
pm8001_dev = ccb->device;
if (event)
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("sata IO status 0x%x\n", event));
- if (unlikely(!t || !t->lldd_task || !t->dev))
+ pm8001_dbg(pm8001_ha, FAIL, "sata IO status 0x%x\n", event);
+
+ if (unlikely(!t)) {
+ pm8001_dbg(pm8001_ha, FAIL, "task null, freeing CCB tag %d\n",
+ ccb->ccb_tag);
+ pm8001_ccb_free(pm8001_ha, ccb);
+ return;
+ }
+
+ if (unlikely(!t->lldd_task || !t->dev))
return;
+
ts = &t->task_status;
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("port_id = %x,device_id = %x\n",
- port_id, dev_id));
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "port_id:0x%x, device_id:0x%x, tag:0x%x, event:0x%x\n",
+ port_id, dev_id, tag, event);
switch (event) {
case IO_OVERFLOW:
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
ts->residual = 0;
- if (pm8001_dev)
- pm8001_dev->running_req--;
break;
case IO_XFER_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_INTERRUPTED;
break;
case IO_XFER_ERROR_PHY_NOT_READY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
- "_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_EPROTO;
break;
case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
break;
case IO_OPEN_CNX_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_DEV_NO_RESPONSE;
if (!t->uldd_task) {
@@ -2284,141 +2576,97 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_QUEUE_FULL;
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
return;
}
break;
case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_BAD_DEST;
break;
case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
- "NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_CONN_RATE;
break;
case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
break;
case IO_XFER_ERROR_NAK_RECEIVED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_NAK_R_ERR;
break;
case IO_XFER_ERROR_PEER_ABORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_PEER_ABORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PEER_ABORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_NAK_R_ERR;
break;
case IO_XFER_ERROR_REJECTED_NCQ_MODE:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_REJECTED_NCQ_MODE\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_UNDERRUN;
break;
case IO_XFER_OPEN_RETRY_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
case IO_XFER_ERROR_UNEXPECTED_PHASE:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_UNEXPECTED_PHASE\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
case IO_XFER_ERROR_XFER_RDY_OVERRUN:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_XFER_RDY_OVERRUN\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
case IO_XFER_ERROR_OFFSET_MISMATCH:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_OFFSET_MISMATCH\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
case IO_XFER_CMD_FRAME_ISSUED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_CMD_FRAME_ISSUED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_CMD_FRAME_ISSUED\n");
break;
case IO_XFER_PIO_SETUP_ERROR:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_PIO_SETUP_ERROR\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_PIO_SETUP_ERROR\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
default:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("Unknown status 0x%x\n", event));
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", event);
/* not allowed case. Therefore, return failed status */
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_TO;
break;
}
- spin_lock_irqsave(&t->task_state_lock, flags);
- t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
- t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
- t->task_state_flags |= SAS_TASK_STATE_DONE;
- if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("task 0x%p done with io_status 0x%x"
- " resp 0x%x stat 0x%x but aborted by upper layer!\n",
- t, event, ts->resp, ts->stat));
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- } else if (t->uldd_task) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/* ditto */
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
- } else if (!t->uldd_task) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
- }
}
/*See the comments for mpi_ssp_completion */
static void
mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
- u32 param;
struct sas_task *t;
struct pm8001_ccb_info *ccb;
unsigned long flags;
@@ -2433,87 +2681,83 @@ mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
tag = le32_to_cpu(psmpPayload->tag);
ccb = &pm8001_ha->ccb_info[tag];
- param = le32_to_cpu(psmpPayload->param);
t = ccb->task;
ts = &t->task_status;
pm8001_dev = ccb->device;
- if (status)
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("smp IO status 0x%x\n", status));
+ if (status) {
+ pm8001_dbg(pm8001_ha, FAIL, "smp IO status 0x%x\n", status);
+ pm8001_dbg(pm8001_ha, IOERR,
+ "status:0x%x, tag:0x%x, task:0x%p\n",
+ status, tag, t);
+ }
if (unlikely(!t || !t->lldd_task || !t->dev))
return;
switch (status) {
case IO_SUCCESS:
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_SUCCESS\n");
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_GOOD;
- if (pm8001_dev)
- pm8001_dev->running_req--;
+ ts->stat = SAS_SAM_STAT_GOOD;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_ABORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_ABORTED IOMB\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_ABORTED IOMB\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_ABORTED_TASK;
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_OVERFLOW:
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DATA_OVERRUN;
ts->residual = 0;
if (pm8001_dev)
- pm8001_dev->running_req--;
+ atomic_dec(&pm8001_dev->running_req);
break;
case IO_NO_DEVICE:
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_NO_DEVICE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_NO_DEVICE\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_PHY_DOWN;
break;
case IO_ERROR_HW_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_ERROR_HW_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_ERROR_HW_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_BUSY;
+ ts->stat = SAS_SAM_STAT_BUSY;
break;
case IO_XFER_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_BUSY;
+ ts->stat = SAS_SAM_STAT_BUSY;
break;
case IO_XFER_ERROR_PHY_NOT_READY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_BUSY;
+ ts->stat = SAS_SAM_STAT_BUSY;
break;
case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
break;
case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
break;
case IO_OPEN_CNX_ERROR_BREAK:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
@@ -2522,76 +2766,67 @@ mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
break;
case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_BAD_DEST;
break;
case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
- "NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_CONN_RATE;
break;
case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
break;
case IO_XFER_ERROR_RX_FRAME:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_ERROR_RX_FRAME\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_RX_FRAME\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
break;
case IO_XFER_OPEN_RETRY_TIMEOUT:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_ERROR_INTERNAL_SMP_RESOURCE:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_ERROR_INTERNAL_SMP_RESOURCE\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_ERROR_INTERNAL_SMP_RESOURCE\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_QUEUE_FULL;
break;
case IO_PORT_IN_RESET:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_PORT_IN_RESET\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_PORT_IN_RESET\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_DS_NON_OPERATIONAL:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_NON_OPERATIONAL\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
break;
case IO_DS_IN_RECOVERY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_DS_IN_RECOVERY\n"));
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_IN_RECOVERY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n");
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
default:
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("Unknown status 0x%x\n", status));
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", status);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_DEV_NO_RESPONSE;
/* not allowed case. Therefore, return failed status */
@@ -2599,25 +2834,20 @@ mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
}
spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
- t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
- " io_status 0x%x resp 0x%x "
- "stat 0x%x but aborted by upper layer!\n",
- t, status, ts->resp, ts->stat));
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ pm8001_dbg(pm8001_ha, FAIL, "task 0x%p done with io_status 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
} else {
spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
- mb();/* in order to force CPU ordering */
- t->task_done(t);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
}
}
-static void
-mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+void pm8001_mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
{
struct set_dev_state_resp *pPayload =
(struct set_dev_state_resp *)(piomb + 4);
@@ -2626,41 +2856,37 @@ mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
struct pm8001_device *pm8001_dev = ccb->device;
u32 status = le32_to_cpu(pPayload->status);
u32 device_id = le32_to_cpu(pPayload->device_id);
- u8 pds = le32_to_cpu(pPayload->pds_nds) | PDS_BITS;
- u8 nds = le32_to_cpu(pPayload->pds_nds) | NDS_BITS;
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set device id = 0x%x state "
- "from 0x%x to 0x%x status = 0x%x!\n",
- device_id, pds, nds, status));
+ u8 pds = le32_to_cpu(pPayload->pds_nds) & PDS_BITS;
+ u8 nds = le32_to_cpu(pPayload->pds_nds) & NDS_BITS;
+
+ pm8001_dbg(pm8001_ha, MSG,
+ "Set device id = 0x%x state from 0x%x to 0x%x status = 0x%x!\n",
+ device_id, pds, nds, status);
complete(pm8001_dev->setds_completion);
- ccb->task = NULL;
- ccb->ccb_tag = 0xFFFFFFFF;
- pm8001_ccb_free(pm8001_ha, tag);
+ pm8001_ccb_free(pm8001_ha, ccb);
}
-static void
-mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+void pm8001_mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
struct get_nvm_data_resp *pPayload =
(struct get_nvm_data_resp *)(piomb + 4);
u32 tag = le32_to_cpu(pPayload->tag);
struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
u32 dlen_status = le32_to_cpu(pPayload->dlen_status);
+
complete(pm8001_ha->nvmd_completion);
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n"));
+ pm8001_dbg(pm8001_ha, MSG, "Set nvm data complete!\n");
if ((dlen_status & NVMD_STAT) != 0) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Set nvm data error!\n"));
- return;
+ pm8001_dbg(pm8001_ha, FAIL, "Set nvm data error %x\n",
+ dlen_status);
}
- ccb->task = NULL;
- ccb->ccb_tag = 0xFFFFFFFF;
- pm8001_ccb_free(pm8001_ha, tag);
+ pm8001_ccb_free(pm8001_ha, ccb);
}
-static void
-mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+void
+pm8001_mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
- struct fw_control_ex *fw_control_context;
+ struct fw_control_ex *fw_control_context;
struct get_nvm_data_resp *pPayload =
(struct get_nvm_data_resp *)(piomb + 4);
u32 tag = le32_to_cpu(pPayload->tag);
@@ -2671,26 +2897,26 @@ mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
void *virt_addr = pm8001_ha->memoryMap.region[NVMD].virt_ptr;
fw_control_context = ccb->fw_control_context;
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Get nvm data complete!\n"));
+ pm8001_dbg(pm8001_ha, MSG, "Get nvm data complete!\n");
if ((dlen_status & NVMD_STAT) != 0) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Get nvm data error!\n"));
+ pm8001_dbg(pm8001_ha, FAIL, "Get nvm data error %x\n",
+ dlen_status);
complete(pm8001_ha->nvmd_completion);
+ /* We should free tag during failure also, the tag is not being
+ * freed by requesting path anywhere.
+ */
+ pm8001_ccb_free(pm8001_ha, ccb);
return;
}
-
if (ir_tds_bn_dps_das_nvm & IPMode) {
/* indirect mode - IR bit set */
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("Get NVMD success, IR=1\n"));
+ pm8001_dbg(pm8001_ha, MSG, "Get NVMD success, IR=1\n");
if ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == TWI_DEVICE) {
if (ir_tds_bn_dps_das_nvm == 0x80a80200) {
memcpy(pm8001_ha->sas_addr,
((u8 *)virt_addr + 4),
SAS_ADDR_SIZE);
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("Get SAS address"
- " from VPD successfully!\n"));
+ pm8001_dbg(pm8001_ha, MSG, "Get SAS address from VPD successfully!\n");
}
} else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == C_SEEPROM)
|| ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == VPD_FLASH) ||
@@ -2701,39 +2927,55 @@ mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
;
} else {
/* Should not be happened*/
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("(IR=1)Wrong Device type 0x%x\n",
- ir_tds_bn_dps_das_nvm));
+ pm8001_dbg(pm8001_ha, MSG,
+ "(IR=1)Wrong Device type 0x%x\n",
+ ir_tds_bn_dps_das_nvm);
}
} else /* direct mode */{
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("Get NVMD success, IR=0, dataLen=%d\n",
- (dlen_status & NVMD_LEN) >> 24));
+ pm8001_dbg(pm8001_ha, MSG,
+ "Get NVMD success, IR=0, dataLen=%d\n",
+ (dlen_status & NVMD_LEN) >> 24);
}
+ /* Though fw_control_context is freed below, usrAddr still needs
+ * to be updated as this holds the response to the request function
+ */
memcpy(fw_control_context->usrAddr,
pm8001_ha->memoryMap.region[NVMD].virt_ptr,
fw_control_context->len);
+ kfree(ccb->fw_control_context);
+ /* To avoid race condition, complete should be
+ * called after the message is copied to
+ * fw_control_context->usrAddr
+ */
complete(pm8001_ha->nvmd_completion);
- ccb->task = NULL;
- ccb->ccb_tag = 0xFFFFFFFF;
- pm8001_ccb_free(pm8001_ha, tag);
+ pm8001_dbg(pm8001_ha, MSG, "Get nvmd data complete!\n");
+ pm8001_ccb_free(pm8001_ha, ccb);
}
-static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb)
+int pm8001_mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
+ u32 tag;
struct local_phy_ctl_resp *pPayload =
(struct local_phy_ctl_resp *)(piomb + 4);
u32 status = le32_to_cpu(pPayload->status);
u32 phy_id = le32_to_cpu(pPayload->phyop_phyid) & ID_BITS;
u32 phy_op = le32_to_cpu(pPayload->phyop_phyid) & OP_BITS;
+ tag = le32_to_cpu(pPayload->tag);
if (status != 0) {
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("%x phy execute %x phy op failed! \n",
- phy_id, phy_op));
- } else
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("%x phy execute %x phy op success! \n",
- phy_id, phy_op));
+ pm8001_dbg(pm8001_ha, MSG,
+ "%x phy execute %x phy op failed!\n",
+ phy_id, phy_op);
+ } else {
+ pm8001_dbg(pm8001_ha, MSG,
+ "%x phy execute %x phy op success!\n",
+ phy_id, phy_op);
+ pm8001_ha->phy[phy_id].reset_success = true;
+ }
+ if (pm8001_ha->phy[phy_id].enable_completion) {
+ complete(pm8001_ha->phy[phy_id].enable_completion);
+ pm8001_ha->phy[phy_id].enable_completion = NULL;
+ }
+ pm8001_tag_free(pm8001_ha, tag);
return 0;
}
@@ -2744,28 +2986,17 @@ static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb)
*
* when HBA driver received the identify done event or initiate FIS received
* event(for SATA), it will invoke this function to notify the sas layer that
- * the sas toplogy has formed, please discover the the whole sas domain,
+ * the sas toplogy has formed, please discover the whole sas domain,
* while receive a broadcast(change) primitive just tell the sas
* layer to discover the changed domain rather than the whole domain.
*/
-static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i)
+void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i)
{
struct pm8001_phy *phy = &pm8001_ha->phy[i];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
- struct sas_ha_struct *sas_ha;
if (!phy->phy_attached)
return;
- sas_ha = pm8001_ha->sas;
- if (sas_phy->phy) {
- struct sas_phy *sphy = sas_phy->phy;
- sphy->negotiated_linkrate = sas_phy->linkrate;
- sphy->minimum_linkrate = phy->minimum_linkrate;
- sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
- sphy->maximum_linkrate = phy->maximum_linkrate;
- sphy->maximum_linkrate_hw = phy->maximum_linkrate;
- }
-
if (phy->phy_type & PORT_TYPE_SAS) {
struct sas_identify_frame *id;
id = (struct sas_identify_frame *)phy->frame_rcvd;
@@ -2775,40 +3006,40 @@ static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i)
} else if (phy->phy_type & PORT_TYPE_SATA) {
/*Nothing*/
}
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("phy %d byte dmaded.\n", i));
+ pm8001_dbg(pm8001_ha, MSG, "phy %d byte dmaded.\n", i);
sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
- pm8001_ha->sas->notify_port_event(sas_phy, PORTE_BYTES_DMAED);
+ sas_notify_port_event(sas_phy, PORTE_BYTES_DMAED, GFP_ATOMIC);
}
/* Get the link rate speed */
-static void get_lrate_mode(struct pm8001_phy *phy, u8 link_rate)
+void pm8001_get_lrate_mode(struct pm8001_phy *phy, u8 link_rate)
{
struct sas_phy *sas_phy = phy->sas_phy.phy;
switch (link_rate) {
+ case PHY_SPEED_120:
+ phy->sas_phy.linkrate = SAS_LINK_RATE_12_0_GBPS;
+ break;
case PHY_SPEED_60:
phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS;
- phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
break;
case PHY_SPEED_30:
phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS;
- phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
break;
case PHY_SPEED_15:
phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS;
- phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
break;
}
sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
- sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_6_0_GBPS;
+ sas_phy->maximum_linkrate_hw = phy->maximum_linkrate;
sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
- sas_phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
- sas_phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ sas_phy->maximum_linkrate = phy->maximum_linkrate;
+ sas_phy->minimum_linkrate = phy->minimum_linkrate;
}
/**
- * asd_get_attached_sas_addr -- extract/generate attached SAS address
+ * pm8001_get_attached_sas_addr - extract/generate attached SAS address
* @phy: pointer to asd_phy
* @sas_addr: pointer to buffer where the SAS address is to be written
*
@@ -2819,7 +3050,7 @@ static void get_lrate_mode(struct pm8001_phy *phy, u8 link_rate)
* LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
* buffer.
*/
-static void pm8001_get_attached_sas_addr(struct pm8001_phy *phy,
+void pm8001_get_attached_sas_addr(struct pm8001_phy *phy,
u8 *sas_addr)
{
if (phy->sas_phy.frame_rcvd[0] == 0x34
@@ -2852,16 +3083,14 @@ static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
struct hw_event_ack_req payload;
u32 opc = OPC_INB_SAS_HW_EVENT_ACK;
- struct inbound_queue_table *circularQ;
-
memset((u8 *)&payload, 0, sizeof(payload));
- circularQ = &pm8001_ha->inbnd_q_tbl[Qnum];
- payload.tag = 1;
+ payload.tag = cpu_to_le32(1);
payload.sea_phyid_portid = cpu_to_le32(((SEA & 0xFFFF) << 8) |
((phyId & 0x0F) << 4) | (port_id & 0x0F));
payload.param0 = cpu_to_le32(param0);
payload.param1 = cpu_to_le32(param1);
- mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+
+ pm8001_mpi_build_cmd(pm8001_ha, Qnum, opc, &payload, sizeof(payload), 0);
}
static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
@@ -2887,53 +3116,52 @@ hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
u32 npip_portstate = le32_to_cpu(pPayload->npip_portstate);
u8 portstate = (u8)(npip_portstate & 0x0000000F);
struct pm8001_port *port = &pm8001_ha->port[port_id];
- struct sas_ha_struct *sas_ha = pm8001_ha->sas;
struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
unsigned long flags;
u8 deviceType = pPayload->sas_identify.dev_type;
+ phy->port = port;
+ port->port_id = port_id;
port->port_state = portstate;
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_SAS_PHY_UP port id = %d, phy id = %d\n",
- port_id, phy_id));
+ phy->phy_state = PHY_STATE_LINK_UP_SPC;
+ pm8001_dbg(pm8001_ha, MSG,
+ "HW_EVENT_SAS_PHY_UP port id = %d, phy id = %d\n",
+ port_id, phy_id);
switch (deviceType) {
case SAS_PHY_UNUSED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("device type no device.\n"));
+ pm8001_dbg(pm8001_ha, MSG, "device type no device.\n");
break;
case SAS_END_DEVICE:
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("end device.\n"));
+ pm8001_dbg(pm8001_ha, MSG, "end device.\n");
pm8001_chip_phy_ctl_req(pm8001_ha, phy_id,
PHY_NOTIFY_ENABLE_SPINUP);
port->port_attached = 1;
- get_lrate_mode(phy, link_rate);
+ pm8001_get_lrate_mode(phy, link_rate);
break;
case SAS_EDGE_EXPANDER_DEVICE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("expander device.\n"));
+ pm8001_dbg(pm8001_ha, MSG, "expander device.\n");
port->port_attached = 1;
- get_lrate_mode(phy, link_rate);
+ pm8001_get_lrate_mode(phy, link_rate);
break;
case SAS_FANOUT_EXPANDER_DEVICE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("fanout expander device.\n"));
+ pm8001_dbg(pm8001_ha, MSG, "fanout expander device.\n");
port->port_attached = 1;
- get_lrate_mode(phy, link_rate);
+ pm8001_get_lrate_mode(phy, link_rate);
break;
default:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("unknown device type(%x)\n", deviceType));
+ pm8001_dbg(pm8001_ha, DEVIO, "unknown device type(%x)\n",
+ deviceType);
break;
}
phy->phy_type |= PORT_TYPE_SAS;
phy->identify.device_type = deviceType;
phy->phy_attached = 1;
- if (phy->identify.device_type == SAS_END_DEV)
+ if (phy->identify.device_type == SAS_END_DEVICE)
phy->identify.target_port_protocols = SAS_PROTOCOL_SSP;
- else if (phy->identify.device_type != NO_DEVICE)
+ else if (phy->identify.device_type != SAS_PHY_UNUSED)
phy->identify.target_port_protocols = SAS_PROTOCOL_SMP;
phy->sas_phy.oob_mode = SAS_OOB_MODE;
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE, GFP_ATOMIC);
spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
memcpy(phy->frame_rcvd, &pPayload->sas_identify,
sizeof(struct sas_identify_frame)-4);
@@ -2965,25 +3193,26 @@ hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
u32 npip_portstate = le32_to_cpu(pPayload->npip_portstate);
u8 portstate = (u8)(npip_portstate & 0x0000000F);
struct pm8001_port *port = &pm8001_ha->port[port_id];
- struct sas_ha_struct *sas_ha = pm8001_ha->sas;
struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
unsigned long flags;
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_SATA_PHY_UP port id = %d,"
- " phy id = %d\n", port_id, phy_id));
+ pm8001_dbg(pm8001_ha, DEVIO, "HW_EVENT_SATA_PHY_UP port id = %d, phy id = %d\n",
+ port_id, phy_id);
+ phy->port = port;
+ port->port_id = port_id;
port->port_state = portstate;
+ phy->phy_state = PHY_STATE_LINK_UP_SPC;
port->port_attached = 1;
- get_lrate_mode(phy, link_rate);
+ pm8001_get_lrate_mode(phy, link_rate);
phy->phy_type |= PORT_TYPE_SATA;
phy->phy_attached = 1;
phy->sas_phy.oob_mode = SATA_OOB_MODE;
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE, GFP_ATOMIC);
spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
memcpy(phy->frame_rcvd, ((u8 *)&pPayload->sata_fis - 4),
sizeof(struct dev_to_host_fis));
phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
- phy->identify.device_type = SATA_DEV;
+ phy->identify.device_type = SAS_SATA_DEV;
pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
pm8001_bytes_dmaed(pm8001_ha, phy_id);
@@ -3017,53 +3246,51 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
case PORT_VALID:
break;
case PORT_INVALID:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" PortInvalid portID %d \n", port_id));
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" Last phy Down and port invalid\n"));
+ pm8001_dbg(pm8001_ha, MSG, " PortInvalid portID %d\n",
+ port_id);
+ pm8001_dbg(pm8001_ha, MSG,
+ " Last phy Down and port invalid\n");
port->port_attached = 0;
pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
port_id, phy_id, 0, 0);
break;
case PORT_IN_RESET:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" Port In Reset portID %d \n", port_id));
+ pm8001_dbg(pm8001_ha, MSG, " Port In Reset portID %d\n",
+ port_id);
break;
case PORT_NOT_ESTABLISHED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ " phy Down and PORT_NOT_ESTABLISHED\n");
port->port_attached = 0;
break;
case PORT_LOSTCOMM:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" phy Down and PORT_LOSTCOMM\n"));
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" Last phy Down and port invalid\n"));
+ pm8001_dbg(pm8001_ha, MSG, " phy Down and PORT_LOSTCOMM\n");
+ pm8001_dbg(pm8001_ha, MSG,
+ " Last phy Down and port invalid\n");
port->port_attached = 0;
pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
port_id, phy_id, 0, 0);
break;
default:
port->port_attached = 0;
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" phy Down and(default) = %x\n",
- portstate));
+ pm8001_dbg(pm8001_ha, DEVIO, " phy Down and(default) = %x\n",
+ portstate);
break;
}
}
/**
- * mpi_reg_resp -process register device ID response.
+ * pm8001_mpi_reg_resp -process register device ID response.
* @pm8001_ha: our hba card information
* @piomb: IO message buffer
*
* when sas layer find a device it will notify LLDD, then the driver register
* the domain device to FW, this event is the return device ID which the FW
- * has assigned, from now,inter-communication with FW is no longer using the
+ * has assigned, from now, inter-communication with FW is no longer using the
* SAS address, use device ID which FW assigned.
*/
-static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+int pm8001_mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
u32 status;
u32 device_id;
@@ -3074,58 +3301,55 @@ static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
(struct dev_reg_resp *)(piomb + 4);
htag = le32_to_cpu(registerRespPayload->tag);
- ccb = &pm8001_ha->ccb_info[registerRespPayload->tag];
+ ccb = &pm8001_ha->ccb_info[htag];
pm8001_dev = ccb->device;
status = le32_to_cpu(registerRespPayload->status);
device_id = le32_to_cpu(registerRespPayload->device_id);
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" register device is status = %d\n", status));
+ pm8001_dbg(pm8001_ha, INIT,
+ "register device status %d phy_id 0x%x device_id %d\n",
+ status, pm8001_dev->attached_phy, device_id);
switch (status) {
case DEVREG_SUCCESS:
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("DEVREG_SUCCESS\n"));
+ pm8001_dbg(pm8001_ha, MSG, "DEVREG_SUCCESS\n");
pm8001_dev->device_id = device_id;
break;
case DEVREG_FAILURE_OUT_OF_RESOURCE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_OUT_OF_RESOURCE\n"));
+ pm8001_dbg(pm8001_ha, MSG, "DEVREG_FAILURE_OUT_OF_RESOURCE\n");
break;
case DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED\n");
break;
case DEVREG_FAILURE_INVALID_PHY_ID:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_INVALID_PHY_ID\n"));
+ pm8001_dbg(pm8001_ha, MSG, "DEVREG_FAILURE_INVALID_PHY_ID\n");
break;
case DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED\n");
break;
case DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE\n");
break;
case DEVREG_FAILURE_PORT_NOT_VALID_STATE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_PORT_NOT_VALID_STATE\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "DEVREG_FAILURE_PORT_NOT_VALID_STATE\n");
break;
case DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID\n");
break;
default:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_UNSORPORTED\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "DEVREG_FAILURE_DEVICE_TYPE_NOT_SUPPORTED\n");
break;
}
complete(pm8001_dev->dcompletion);
- ccb->task = NULL;
- ccb->ccb_tag = 0xFFFFFFFF;
- pm8001_ccb_free(pm8001_ha, htag);
+ pm8001_ccb_free(pm8001_ha, ccb);
return 0;
}
-static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+int pm8001_mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
u32 status;
u32 device_id;
@@ -3135,98 +3359,84 @@ static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
status = le32_to_cpu(registerRespPayload->status);
device_id = le32_to_cpu(registerRespPayload->device_id);
if (status != 0)
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" deregister device failed ,status = %x"
- ", device_id = %x\n", status, device_id));
+ pm8001_dbg(pm8001_ha, MSG,
+ " deregister device failed ,status = %x, device_id = %x\n",
+ status, device_id);
return 0;
}
-static int
-mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+/**
+ * pm8001_mpi_fw_flash_update_resp - Response from FW for flash update command.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+int pm8001_mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
{
u32 status;
- struct fw_control_ex fw_control_context;
struct fw_flash_Update_resp *ppayload =
(struct fw_flash_Update_resp *)(piomb + 4);
u32 tag = le32_to_cpu(ppayload->tag);
struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+
status = le32_to_cpu(ppayload->status);
- memcpy(&fw_control_context,
- ccb->fw_control_context,
- sizeof(fw_control_context));
switch (status) {
case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_COMPLETE_PENDING_REBOOT\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ ": FLASH_UPDATE_COMPLETE_PENDING_REBOOT\n");
break;
case FLASH_UPDATE_IN_PROGRESS:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_IN_PROGRESS\n"));
+ pm8001_dbg(pm8001_ha, MSG, ": FLASH_UPDATE_IN_PROGRESS\n");
break;
case FLASH_UPDATE_HDR_ERR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_HDR_ERR\n"));
+ pm8001_dbg(pm8001_ha, MSG, ": FLASH_UPDATE_HDR_ERR\n");
break;
case FLASH_UPDATE_OFFSET_ERR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_OFFSET_ERR\n"));
+ pm8001_dbg(pm8001_ha, MSG, ": FLASH_UPDATE_OFFSET_ERR\n");
break;
case FLASH_UPDATE_CRC_ERR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_CRC_ERR\n"));
+ pm8001_dbg(pm8001_ha, MSG, ": FLASH_UPDATE_CRC_ERR\n");
break;
case FLASH_UPDATE_LENGTH_ERR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_LENGTH_ERR\n"));
+ pm8001_dbg(pm8001_ha, MSG, ": FLASH_UPDATE_LENGTH_ERR\n");
break;
case FLASH_UPDATE_HW_ERR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_HW_ERR\n"));
+ pm8001_dbg(pm8001_ha, MSG, ": FLASH_UPDATE_HW_ERR\n");
break;
case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_DNLD_NOT_SUPPORTED\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ ": FLASH_UPDATE_DNLD_NOT_SUPPORTED\n");
break;
case FLASH_UPDATE_DISABLED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(": FLASH_UPDATE_DISABLED\n"));
+ pm8001_dbg(pm8001_ha, MSG, ": FLASH_UPDATE_DISABLED\n");
break;
default:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("No matched status = %d\n", status));
+ pm8001_dbg(pm8001_ha, DEVIO, "No matched status = %d\n",
+ status);
break;
}
- ccb->fw_control_context->fw_control->retcode = status;
- pci_free_consistent(pm8001_ha->pdev,
- fw_control_context.len,
- fw_control_context.virtAddr,
- fw_control_context.phys_addr);
+ kfree(ccb->fw_control_context);
+ pm8001_ccb_free(pm8001_ha, ccb);
complete(pm8001_ha->nvmd_completion);
- ccb->task = NULL;
- ccb->ccb_tag = 0xFFFFFFFF;
- pm8001_ccb_free(pm8001_ha, tag);
return 0;
}
-static int
-mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+int pm8001_mpi_general_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
u32 status;
int i;
struct general_event_resp *pPayload =
(struct general_event_resp *)(piomb + 4);
status = le32_to_cpu(pPayload->status);
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk(" status = 0x%x\n", status));
+ pm8001_dbg(pm8001_ha, MSG, " status = 0x%x\n", status);
for (i = 0; i < GENERAL_EVENT_PAYLOAD; i++)
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("inb_IOMB_payload[0x%x] 0x%x, \n", i,
- pPayload->inb_IOMB_payload[i]));
+ pm8001_dbg(pm8001_ha, MSG, "inb_IOMB_payload[0x%x] 0x%x,\n",
+ i,
+ pPayload->inb_IOMB_payload[i]);
return 0;
}
-static int
-mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
struct sas_task *t;
struct pm8001_ccb_info *ccb;
@@ -3234,44 +3444,52 @@ mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
u32 status ;
u32 tag, scp;
struct task_status_struct *ts;
+ struct pm8001_device *pm8001_dev;
struct task_abort_resp *pPayload =
(struct task_abort_resp *)(piomb + 4);
- ccb = &pm8001_ha->ccb_info[pPayload->tag];
- t = ccb->task;
-
status = le32_to_cpu(pPayload->status);
tag = le32_to_cpu(pPayload->tag);
+
scp = le32_to_cpu(pPayload->scp);
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk(" status = 0x%x\n", status));
- if (t == NULL)
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device; /* retrieve device */
+
+ if (!t) {
+ pm8001_dbg(pm8001_ha, FAIL, " TASK NULL. RETURNING !!!\n");
return -1;
+ }
+
+ if (t->task_proto == SAS_PROTOCOL_INTERNAL_ABORT)
+ atomic_dec(&pm8001_dev->running_req);
+
ts = &t->task_status;
if (status != 0)
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("task abort failed status 0x%x ,"
- "tag = 0x%x, scp= 0x%x\n", status, tag, scp));
+ pm8001_dbg(pm8001_ha, FAIL, "task abort failed status 0x%x ,tag = 0x%x, scp= 0x%x\n",
+ status, tag, scp);
switch (status) {
case IO_SUCCESS:
- PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ pm8001_dbg(pm8001_ha, FAIL, "ABORT IO_SUCCESS for tag %#x\n",
+ tag);
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_STAT_GOOD;
+ ts->stat = SAS_SAM_STAT_GOOD;
break;
case IO_NOT_VALID:
- PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_NOT_VALID\n"));
+ pm8001_dbg(pm8001_ha, FAIL, "IO_NOT_VALID for tag %#x\n", tag);
ts->resp = TMF_RESP_FUNC_FAILED;
break;
}
spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
- t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, pPayload->tag);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
mb();
+
t->task_done(t);
+
return 0;
}
@@ -3280,7 +3498,7 @@ mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
* @pm8001_ha: our hba card information
* @piomb: IO message buffer
*/
-static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
+static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
unsigned long flags;
struct hw_event_resp *pPayload =
@@ -3297,192 +3515,191 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
struct sas_ha_struct *sas_ha = pm8001_ha->sas;
struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("outbound queue HW event & event type : "));
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "SPC HW event for portid:%d, phyid:%d, event:%x, status:%x\n",
+ port_id, phy_id, eventType, status);
switch (eventType) {
case HW_EVENT_PHY_START_STATUS:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PHY_START_STATUS"
- " status = %x\n", status));
- if (status == 0) {
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PHY_START_STATUS status = %x\n",
+ status);
+ if (status == 0)
phy->phy_state = 1;
- if (pm8001_ha->flags == PM8001F_RUN_TIME)
- complete(phy->enable_completion);
+
+ if (pm8001_ha->flags == PM8001F_RUN_TIME &&
+ phy->enable_completion != NULL) {
+ complete(phy->enable_completion);
+ phy->enable_completion = NULL;
}
break;
case HW_EVENT_SAS_PHY_UP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PHY_START_STATUS \n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PHY_START_STATUS\n");
hw_event_sas_phy_up(pm8001_ha, piomb);
break;
case HW_EVENT_SATA_PHY_UP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_SATA_PHY_UP \n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_SATA_PHY_UP\n");
hw_event_sata_phy_up(pm8001_ha, piomb);
break;
case HW_EVENT_PHY_STOP_STATUS:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PHY_STOP_STATUS "
- "status = %x\n", status));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PHY_STOP_STATUS status = %x\n",
+ status);
if (status == 0)
phy->phy_state = 0;
break;
case HW_EVENT_SATA_SPINUP_HOLD:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_SATA_SPINUP_HOLD \n"));
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_SATA_SPINUP_HOLD\n");
+ sas_notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD,
+ GFP_ATOMIC);
break;
case HW_EVENT_PHY_DOWN:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PHY_DOWN \n"));
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PHY_DOWN\n");
+ sas_notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL,
+ GFP_ATOMIC);
phy->phy_attached = 0;
phy->phy_state = 0;
hw_event_phy_down(pm8001_ha, piomb);
break;
case HW_EVENT_PORT_INVALID:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PORT_INVALID\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PORT_INVALID\n");
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
/* the broadcast change primitive received, tell the LIBSAS this event
to revalidate the sas domain*/
case HW_EVENT_BROADCAST_CHANGE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_BROADCAST_CHANGE\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_BROADCAST_CHANGE\n");
pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_BROADCAST_CHANGE,
port_id, phy_id, 1, 0);
spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
sas_phy->sas_prim = HW_EVENT_BROADCAST_CHANGE;
spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
- sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
+ GFP_ATOMIC);
break;
case HW_EVENT_PHY_ERROR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PHY_ERROR\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PHY_ERROR\n");
sas_phy_disconnected(&phy->sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR, GFP_ATOMIC);
break;
case HW_EVENT_BROADCAST_EXP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_BROADCAST_EXP\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_BROADCAST_EXP\n");
spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
sas_phy->sas_prim = HW_EVENT_BROADCAST_EXP;
spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
- sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
+ GFP_ATOMIC);
break;
case HW_EVENT_LINK_ERR_INVALID_DWORD:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "HW_EVENT_LINK_ERR_INVALID_DWORD\n");
pm8001_hw_event_ack_req(pm8001_ha, 0,
HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0);
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_LINK_ERR_DISPARITY_ERROR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_LINK_ERR_DISPARITY_ERROR\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "HW_EVENT_LINK_ERR_DISPARITY_ERROR\n");
pm8001_hw_event_ack_req(pm8001_ha, 0,
HW_EVENT_LINK_ERR_DISPARITY_ERROR,
port_id, phy_id, 0, 0);
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_LINK_ERR_CODE_VIOLATION:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_LINK_ERR_CODE_VIOLATION\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "HW_EVENT_LINK_ERR_CODE_VIOLATION\n");
pm8001_hw_event_ack_req(pm8001_ha, 0,
HW_EVENT_LINK_ERR_CODE_VIOLATION,
port_id, phy_id, 0, 0);
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH\n");
pm8001_hw_event_ack_req(pm8001_ha, 0,
HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH,
port_id, phy_id, 0, 0);
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_MALFUNCTION:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_MALFUNCTION\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_MALFUNCTION\n");
break;
case HW_EVENT_BROADCAST_SES:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_BROADCAST_SES\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_BROADCAST_SES\n");
spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
sas_phy->sas_prim = HW_EVENT_BROADCAST_SES;
spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
- sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
+ GFP_ATOMIC);
break;
case HW_EVENT_INBOUND_CRC_ERROR:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_INBOUND_CRC_ERROR\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_INBOUND_CRC_ERROR\n");
pm8001_hw_event_ack_req(pm8001_ha, 0,
HW_EVENT_INBOUND_CRC_ERROR,
port_id, phy_id, 0, 0);
break;
case HW_EVENT_HARD_RESET_RECEIVED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_HARD_RESET_RECEIVED\n"));
- sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_HARD_RESET_RECEIVED\n");
+ sas_notify_port_event(sas_phy, PORTE_HARD_RESET, GFP_ATOMIC);
break;
case HW_EVENT_ID_FRAME_TIMEOUT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_ID_FRAME_TIMEOUT\n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_ID_FRAME_TIMEOUT\n");
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_LINK_ERR_PHY_RESET_FAILED:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_LINK_ERR_PHY_RESET_FAILED \n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "HW_EVENT_LINK_ERR_PHY_RESET_FAILED\n");
pm8001_hw_event_ack_req(pm8001_ha, 0,
HW_EVENT_LINK_ERR_PHY_RESET_FAILED,
port_id, phy_id, 0, 0);
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_PORT_RESET_TIMER_TMO:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PORT_RESET_TIMER_TMO \n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PORT_RESET_TIMER_TMO\n");
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_PORT_RECOVERY_TIMER_TMO:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PORT_RECOVERY_TIMER_TMO \n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "HW_EVENT_PORT_RECOVERY_TIMER_TMO\n");
sas_phy_disconnected(sas_phy);
phy->phy_attached = 0;
- sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
break;
case HW_EVENT_PORT_RECOVER:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PORT_RECOVER \n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PORT_RECOVER\n");
break;
case HW_EVENT_PORT_RESET_COMPLETE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("HW_EVENT_PORT_RESET_COMPLETE \n"));
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_PORT_RESET_COMPLETE\n");
break;
case EVENT_BROADCAST_ASYNCH_EVENT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("EVENT_BROADCAST_ASYNCH_EVENT\n"));
+ pm8001_dbg(pm8001_ha, MSG, "EVENT_BROADCAST_ASYNCH_EVENT\n");
break;
default:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("Unknown event type = %x\n", eventType));
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown event type = %x\n",
+ eventType);
break;
}
return 0;
@@ -3495,208 +3712,180 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
*/
static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
- u32 pHeader = (u32)*(u32 *)piomb;
+ __le32 pHeader = *(__le32 *)piomb;
u8 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF);
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:"));
+ pm8001_dbg(pm8001_ha, MSG, "process_one_iomb:\n");
switch (opc) {
case OPC_OUB_ECHO:
- PM8001_MSG_DBG(pm8001_ha, pm8001_printk("OPC_OUB_ECHO \n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_ECHO\n");
break;
case OPC_OUB_HW_EVENT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_HW_EVENT \n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_HW_EVENT\n");
mpi_hw_event(pm8001_ha, piomb);
break;
case OPC_OUB_SSP_COMP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SSP_COMP \n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_COMP\n");
mpi_ssp_completion(pm8001_ha, piomb);
break;
case OPC_OUB_SMP_COMP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SMP_COMP \n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SMP_COMP\n");
mpi_smp_completion(pm8001_ha, piomb);
break;
case OPC_OUB_LOCAL_PHY_CNTRL:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_LOCAL_PHY_CNTRL\n"));
- mpi_local_phy_ctl(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_LOCAL_PHY_CNTRL\n");
+ pm8001_mpi_local_phy_ctl(pm8001_ha, piomb);
break;
case OPC_OUB_DEV_REGIST:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_DEV_REGIST \n"));
- mpi_reg_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_DEV_REGIST\n");
+ pm8001_mpi_reg_resp(pm8001_ha, piomb);
break;
case OPC_OUB_DEREG_DEV:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("unresgister the deviece \n"));
- mpi_dereg_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "unregister the device\n");
+ pm8001_mpi_dereg_resp(pm8001_ha, piomb);
break;
case OPC_OUB_GET_DEV_HANDLE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_GET_DEV_HANDLE \n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_DEV_HANDLE\n");
break;
case OPC_OUB_SATA_COMP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SATA_COMP \n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SATA_COMP\n");
mpi_sata_completion(pm8001_ha, piomb);
break;
case OPC_OUB_SATA_EVENT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SATA_EVENT \n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SATA_EVENT\n");
mpi_sata_event(pm8001_ha, piomb);
break;
case OPC_OUB_SSP_EVENT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SSP_EVENT\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_EVENT\n");
mpi_ssp_event(pm8001_ha, piomb);
break;
case OPC_OUB_DEV_HANDLE_ARRIV:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_DEV_HANDLE_ARRIV\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_DEV_HANDLE_ARRIV\n");
/*This is for target*/
break;
case OPC_OUB_SSP_RECV_EVENT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SSP_RECV_EVENT\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_RECV_EVENT\n");
/*This is for target*/
break;
case OPC_OUB_DEV_INFO:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_DEV_INFO\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_DEV_INFO\n");
break;
case OPC_OUB_FW_FLASH_UPDATE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_FW_FLASH_UPDATE\n"));
- mpi_fw_flash_update_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_FW_FLASH_UPDATE\n");
+ pm8001_mpi_fw_flash_update_resp(pm8001_ha, piomb);
break;
case OPC_OUB_GPIO_RESPONSE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GPIO_RESPONSE\n");
break;
case OPC_OUB_GPIO_EVENT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GPIO_EVENT\n");
break;
case OPC_OUB_GENERAL_EVENT:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_GENERAL_EVENT\n"));
- mpi_general_event(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GENERAL_EVENT\n");
+ pm8001_mpi_general_event(pm8001_ha, piomb);
break;
case OPC_OUB_SSP_ABORT_RSP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SSP_ABORT_RSP\n"));
- mpi_task_abort_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_ABORT_RSP\n");
+ pm8001_mpi_task_abort_resp(pm8001_ha, piomb);
break;
case OPC_OUB_SATA_ABORT_RSP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SATA_ABORT_RSP\n"));
- mpi_task_abort_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SATA_ABORT_RSP\n");
+ pm8001_mpi_task_abort_resp(pm8001_ha, piomb);
break;
case OPC_OUB_SAS_DIAG_MODE_START_END:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SAS_DIAG_MODE_START_END\n"));
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_SAS_DIAG_MODE_START_END\n");
break;
case OPC_OUB_SAS_DIAG_EXECUTE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SAS_DIAG_EXECUTE\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SAS_DIAG_EXECUTE\n");
break;
case OPC_OUB_GET_TIME_STAMP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_GET_TIME_STAMP\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_TIME_STAMP\n");
break;
case OPC_OUB_SAS_HW_EVENT_ACK:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SAS_HW_EVENT_ACK\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SAS_HW_EVENT_ACK\n");
break;
case OPC_OUB_PORT_CONTROL:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_PORT_CONTROL\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_PORT_CONTROL\n");
break;
case OPC_OUB_SMP_ABORT_RSP:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SMP_ABORT_RSP\n"));
- mpi_task_abort_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SMP_ABORT_RSP\n");
+ pm8001_mpi_task_abort_resp(pm8001_ha, piomb);
break;
case OPC_OUB_GET_NVMD_DATA:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_GET_NVMD_DATA\n"));
- mpi_get_nvmd_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_NVMD_DATA\n");
+ pm8001_mpi_get_nvmd_resp(pm8001_ha, piomb);
break;
case OPC_OUB_SET_NVMD_DATA:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SET_NVMD_DATA\n"));
- mpi_set_nvmd_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SET_NVMD_DATA\n");
+ pm8001_mpi_set_nvmd_resp(pm8001_ha, piomb);
break;
case OPC_OUB_DEVICE_HANDLE_REMOVAL:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_DEVICE_HANDLE_REMOVAL\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_DEVICE_HANDLE_REMOVAL\n");
break;
case OPC_OUB_SET_DEVICE_STATE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SET_DEVICE_STATE\n"));
- mpi_set_dev_state_resp(pm8001_ha, piomb);
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SET_DEVICE_STATE\n");
+ pm8001_mpi_set_dev_state_resp(pm8001_ha, piomb);
break;
case OPC_OUB_GET_DEVICE_STATE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_GET_DEVICE_STATE\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_DEVICE_STATE\n");
break;
case OPC_OUB_SET_DEV_INFO:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SET_DEV_INFO\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SET_DEV_INFO\n");
break;
case OPC_OUB_SAS_RE_INITIALIZE:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("OPC_OUB_SAS_RE_INITIALIZE\n"));
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SAS_RE_INITIALIZE\n");
break;
default:
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("Unknown outbound Queue IOMB OPC = %x\n",
- opc));
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "Unknown outbound Queue IOMB OPC = %x\n",
+ opc);
break;
}
}
-static int process_oq(struct pm8001_hba_info *pm8001_ha)
+static int process_oq(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
struct outbound_queue_table *circularQ;
void *pMsg1 = NULL;
- u8 bc = 0;
+ u8 bc;
u32 ret = MPI_IO_STATUS_FAIL;
+ unsigned long flags;
- circularQ = &pm8001_ha->outbnd_q_tbl[0];
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ circularQ = &pm8001_ha->outbnd_q_tbl[vec];
do {
- ret = mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
+ ret = pm8001_mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
if (MPI_IO_STATUS_SUCCESS == ret) {
/* process the outbound message */
process_one_iomb(pm8001_ha, (void *)(pMsg1 - 4));
/* free the message from the outbound circular buffer */
- mpi_msg_free_set(pm8001_ha, pMsg1, circularQ, bc);
+ pm8001_mpi_msg_free_set(pm8001_ha, pMsg1,
+ circularQ, bc);
}
if (MPI_IO_STATUS_BUSY == ret) {
- u32 producer_idx;
/* Update the producer index from SPC */
- producer_idx = pm8001_read_32(circularQ->pi_virt);
- circularQ->producer_index = cpu_to_le32(producer_idx);
- if (circularQ->producer_index ==
+ circularQ->producer_index =
+ cpu_to_le32(pm8001_read_32(circularQ->pi_virt));
+ if (le32_to_cpu(circularQ->producer_index) ==
circularQ->consumer_idx)
/* OQ is empty */
break;
}
} while (1);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return ret;
}
-/* PCI_DMA_... to our direction translation. */
+/* DMA_... to our direction translation. */
static const u8 data_dir_flags[] = {
- [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT,/* UNSPECIFIED */
- [PCI_DMA_TODEVICE] = DATA_DIR_OUT,/* OUTBOUND */
- [PCI_DMA_FROMDEVICE] = DATA_DIR_IN,/* INBOUND */
- [PCI_DMA_NONE] = DATA_DIR_NONE,/* NO TRANSFER */
+ [DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT, /* UNSPECIFIED */
+ [DMA_TO_DEVICE] = DATA_DIR_OUT, /* OUTBOUND */
+ [DMA_FROM_DEVICE] = DATA_DIR_IN, /* INBOUND */
+ [DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */
};
-static void
+void
pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd)
{
int i;
@@ -3711,9 +3900,9 @@ pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd)
}
}
-static void build_smp_cmd(u32 deviceID, u32 hTag, struct smp_req *psmp_cmd)
+static void build_smp_cmd(u32 deviceID, __le32 hTag, struct smp_req *psmp_cmd)
{
- psmp_cmd->tag = cpu_to_le32(hTag);
+ psmp_cmd->tag = hTag;
psmp_cmd->device_id = cpu_to_le32(deviceID);
psmp_cmd->len_ip_ir = cpu_to_le32(1|(1 << 1));
}
@@ -3734,20 +3923,19 @@ static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha,
u32 req_len, resp_len;
struct smp_req smp_cmd;
u32 opc;
- struct inbound_queue_table *circularQ;
memset(&smp_cmd, 0, sizeof(smp_cmd));
/*
* DMA-map SMP request, response buffers
*/
sg_req = &task->smp_task.smp_req;
- elem = dma_map_sg(pm8001_ha->dev, sg_req, 1, PCI_DMA_TODEVICE);
+ elem = dma_map_sg(pm8001_ha->dev, sg_req, 1, DMA_TO_DEVICE);
if (!elem)
return -ENOMEM;
req_len = sg_dma_len(sg_req);
sg_resp = &task->smp_task.smp_resp;
- elem = dma_map_sg(pm8001_ha->dev, sg_resp, 1, PCI_DMA_FROMDEVICE);
+ elem = dma_map_sg(pm8001_ha->dev, sg_resp, 1, DMA_FROM_DEVICE);
if (!elem) {
rc = -ENOMEM;
goto err_out;
@@ -3760,7 +3948,6 @@ static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha,
}
opc = OPC_INB_SMP_REQUEST;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
smp_cmd.tag = cpu_to_le32(ccb->ccb_tag);
smp_cmd.long_smp_req.long_req_addr =
cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
@@ -3771,15 +3958,19 @@ static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha,
smp_cmd.long_smp_req.long_resp_size =
cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag, &smp_cmd);
- mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd);
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc,
+ &smp_cmd, sizeof(smp_cmd), 0);
+ if (rc)
+ goto err_out_2;
+
return 0;
err_out_2:
dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
+ DMA_FROM_DEVICE);
err_out:
dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
+ DMA_TO_DEVICE);
return rc;
}
@@ -3796,9 +3987,7 @@ static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_dev = dev->lldd_dev;
struct ssp_ini_io_start_req ssp_cmd;
u32 tag = ccb->ccb_tag;
- int ret;
- __le64 phys_addr;
- struct inbound_queue_table *circularQ;
+ u64 phys_addr;
u32 opc = OPC_INB_SSPINIIOSTART;
memset(&ssp_cmd, 0, sizeof(ssp_cmd));
memcpy(ssp_cmd.ssp_iu.lun, task->ssp_task.LUN, 8);
@@ -3808,25 +3997,21 @@ static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len);
ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id);
ssp_cmd.tag = cpu_to_le32(tag);
- if (task->ssp_task.enable_first_burst)
- ssp_cmd.ssp_iu.efb_prio_attr |= 0x80;
- ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3);
ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7);
- memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cdb, 16);
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
/* fill in PRD (scatter/gather) table, if any */
if (task->num_scatter > 1) {
pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
- phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
- offsetof(struct pm8001_ccb_info, buf_prd[0]));
- ssp_cmd.addr_low = lower_32_bits(phys_addr);
- ssp_cmd.addr_high = upper_32_bits(phys_addr);
+ phys_addr = ccb->ccb_dma_handle;
+ ssp_cmd.addr_low = cpu_to_le32(lower_32_bits(phys_addr));
+ ssp_cmd.addr_high = cpu_to_le32(upper_32_bits(phys_addr));
ssp_cmd.esgl = cpu_to_le32(1<<31);
} else if (task->num_scatter == 1) {
- __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
- ssp_cmd.addr_low = lower_32_bits(dma_addr);
- ssp_cmd.addr_high = upper_32_bits(dma_addr);
+ u64 dma_addr = sg_dma_address(task->scatter);
+ ssp_cmd.addr_low = cpu_to_le32(lower_32_bits(dma_addr));
+ ssp_cmd.addr_high = cpu_to_le32(upper_32_bits(dma_addr));
ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
ssp_cmd.esgl = 0;
} else if (task->num_scatter == 0) {
@@ -3835,8 +4020,9 @@ static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
ssp_cmd.esgl = 0;
}
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &ssp_cmd);
- return ret;
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &ssp_cmd,
+ sizeof(ssp_cmd), 0);
}
static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
@@ -3846,41 +4032,44 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
struct domain_device *dev = task->dev;
struct pm8001_device *pm8001_ha_dev = dev->lldd_dev;
u32 tag = ccb->ccb_tag;
- int ret;
struct sata_start_req sata_cmd;
u32 hdr_tag, ncg_tag = 0;
- __le64 phys_addr;
+ u64 phys_addr;
u32 ATAP = 0x0;
- u32 dir;
- struct inbound_queue_table *circularQ;
+ u32 dir, retfis = 0;
u32 opc = OPC_INB_SATA_HOST_OPSTART;
+
memset(&sata_cmd, 0, sizeof(sata_cmd));
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
- if (task->data_dir == PCI_DMA_NONE) {
+
+ if (task->data_dir == DMA_NONE && !task->ata_task.use_ncq) {
ATAP = 0x04; /* no data*/
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("no data \n"));
+ pm8001_dbg(pm8001_ha, IO, "no data\n");
} else if (likely(!task->ata_task.device_control_reg_update)) {
- if (task->ata_task.dma_xfer) {
+ if (task->ata_task.use_ncq &&
+ dev->sata_dev.class != ATA_DEV_ATAPI) {
+ ATAP = 0x07; /* FPDMA */
+ pm8001_dbg(pm8001_ha, IO, "FPDMA\n");
+ } else if (task->ata_task.dma_xfer) {
ATAP = 0x06; /* DMA */
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("DMA \n"));
+ pm8001_dbg(pm8001_ha, IO, "DMA\n");
} else {
ATAP = 0x05; /* PIO*/
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("PIO \n"));
- }
- if (task->ata_task.use_ncq &&
- dev->sata_dev.command_set != ATAPI_COMMAND_SET) {
- ATAP = 0x07; /* FPDMA */
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA \n"));
+ pm8001_dbg(pm8001_ha, IO, "PIO\n");
}
}
- if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag))
+ if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag)) {
+ task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
ncg_tag = hdr_tag;
+ }
dir = data_dir_flags[task->data_dir] << 8;
sata_cmd.tag = cpu_to_le32(tag);
sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
sata_cmd.data_len = cpu_to_le32(task->total_xfer_len);
- sata_cmd.ncqtag_atap_dir_m =
- cpu_to_le32(((ncg_tag & 0xff)<<16)|((ATAP & 0x3f) << 10) | dir);
+ if (task->ata_task.return_fis_on_success)
+ retfis = 1;
+ sata_cmd.retfis_ncqtag_atap_dir_m =
+ cpu_to_le32((retfis << 24) | ((ncg_tag & 0xff) << 16) |
+ ((ATAP & 0x3f) << 10) | dir);
sata_cmd.sata_fis = task->ata_task.fis;
if (likely(!task->ata_task.device_control_reg_update))
sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */
@@ -3888,13 +4077,12 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
/* fill in PRD (scatter/gather) table, if any */
if (task->num_scatter > 1) {
pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
- phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
- offsetof(struct pm8001_ccb_info, buf_prd[0]));
+ phys_addr = ccb->ccb_dma_handle;
sata_cmd.addr_low = lower_32_bits(phys_addr);
sata_cmd.addr_high = upper_32_bits(phys_addr);
sata_cmd.esgl = cpu_to_le32(1 << 31);
} else if (task->num_scatter == 1) {
- __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+ u64 dma_addr = sg_dma_address(task->scatter);
sata_cmd.addr_low = lower_32_bits(dma_addr);
sata_cmd.addr_high = upper_32_bits(dma_addr);
sata_cmd.len = cpu_to_le32(task->total_xfer_len);
@@ -3905,25 +4093,23 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
sata_cmd.len = cpu_to_le32(task->total_xfer_len);
sata_cmd.esgl = 0;
}
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd);
- return ret;
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &sata_cmd,
+ sizeof(sata_cmd), 0);
}
/**
* pm8001_chip_phy_start_req - start phy via PHY_START COMMAND
* @pm8001_ha: our hba card information.
- * @num: the inbound queue number
* @phy_id: the phy id which we wanted to start up.
*/
static int
pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id)
{
struct phy_start_req payload;
- struct inbound_queue_table *circularQ;
- int ret;
u32 tag = 0x01;
u32 opcode = OPC_INB_PHYSTART;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
memset(&payload, 0, sizeof(payload));
payload.tag = cpu_to_le32(tag);
/*
@@ -3935,39 +4121,38 @@ pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id)
payload.ase_sh_lm_slr_phyid = cpu_to_le32(SPINHOLD_DISABLE |
LINKMODE_AUTO | LINKRATE_15 |
LINKRATE_30 | LINKRATE_60 | phy_id);
- payload.sas_identify.dev_type = SAS_END_DEV;
+ payload.sas_identify.dev_type = SAS_END_DEVICE;
payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL;
memcpy(payload.sas_identify.sas_addr,
- pm8001_ha->sas_addr, SAS_ADDR_SIZE);
+ &pm8001_ha->phy[phy_id].dev_sas_addr, SAS_ADDR_SIZE);
payload.sas_identify.phy_id = phy_id;
- ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
- return ret;
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload,
+ sizeof(payload), 0);
}
/**
* pm8001_chip_phy_stop_req - start phy via PHY_STOP COMMAND
* @pm8001_ha: our hba card information.
- * @num: the inbound queue number
* @phy_id: the phy id which we wanted to start up.
*/
static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
- u8 phy_id)
+ u8 phy_id)
{
struct phy_stop_req payload;
- struct inbound_queue_table *circularQ;
- int ret;
u32 tag = 0x01;
u32 opcode = OPC_INB_PHYSTOP;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
memset(&payload, 0, sizeof(payload));
payload.tag = cpu_to_le32(tag);
payload.phy_id = cpu_to_le32(phy_id);
- ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
- return ret;
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload,
+ sizeof(payload), 0);
}
-/**
- * see comments on mpi_reg_resp.
+/*
+ * see comments on pm8001_mpi_reg_resp.
*/
static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_dev, u32 flag)
@@ -3975,44 +4160,38 @@ static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
struct reg_dev_req payload;
u32 opc;
u32 stp_sspsmp_sata = 0x4;
- struct inbound_queue_table *circularQ;
u32 linkrate, phy_id;
- int rc, tag = 0xdeadbeef;
+ int rc;
struct pm8001_ccb_info *ccb;
u8 retryFlag = 0x1;
u16 firstBurstSize = 0;
u16 ITNT = 2000;
struct domain_device *dev = pm8001_dev->sas_device;
- struct domain_device *parent_dev = dev->parent;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ struct pm8001_port *port = dev->port->lldd_port;
memset(&payload, 0, sizeof(payload));
- rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc)
- return rc;
- ccb = &pm8001_ha->ccb_info[tag];
- ccb->device = pm8001_dev;
- ccb->ccb_tag = tag;
- payload.tag = cpu_to_le32(tag);
+ ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_dev, NULL);
+ if (!ccb)
+ return -SAS_QUEUE_FULL;
+
+ payload.tag = cpu_to_le32(ccb->ccb_tag);
if (flag == 1)
stp_sspsmp_sata = 0x02; /*direct attached sata */
else {
- if (pm8001_dev->dev_type == SATA_DEV)
+ if (pm8001_dev->dev_type == SAS_SATA_DEV)
stp_sspsmp_sata = 0x00; /* stp*/
- else if (pm8001_dev->dev_type == SAS_END_DEV ||
- pm8001_dev->dev_type == EDGE_DEV ||
- pm8001_dev->dev_type == FANOUT_DEV)
+ else if (pm8001_dev->dev_type == SAS_END_DEVICE ||
+ dev_is_expander(pm8001_dev->dev_type))
stp_sspsmp_sata = 0x01; /*ssp or smp*/
}
- if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
- phy_id = parent_dev->ex_dev.ex_phy->phy_id;
- else
- phy_id = pm8001_dev->attached_phy;
+
+ phy_id = pm80xx_get_local_phy_id(dev);
+
opc = OPC_INB_REG_DEV;
linkrate = (pm8001_dev->sas_device->linkrate < dev->port->linkrate) ?
pm8001_dev->sas_device->linkrate : dev->port->linkrate;
payload.phyid_portid =
- cpu_to_le32(((pm8001_dev->sas_device->port->id) & 0x0F) |
+ cpu_to_le32(((port->port_id) & 0x0F) |
((phy_id & 0x0F) << 4));
payload.dtype_dlr_retry = cpu_to_le32((retryFlag & 0x01) |
((linkrate & 0x0F) * 0x1000000) |
@@ -4021,130 +4200,134 @@ static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
cpu_to_le32(ITNT | (firstBurstSize * 0x10000));
memcpy(payload.sas_addr, pm8001_dev->sas_device->sas_addr,
SAS_ADDR_SIZE);
- rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_ccb_free(pm8001_ha, ccb);
+
return rc;
}
-/**
- * see comments on mpi_reg_resp.
+/*
+ * see comments on pm8001_mpi_reg_resp.
*/
-static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha,
+int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha,
u32 device_id)
{
struct dereg_dev_req payload;
u32 opc = OPC_INB_DEREG_DEV_HANDLE;
- int ret;
- struct inbound_queue_table *circularQ;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
memset(&payload, 0, sizeof(payload));
- payload.tag = 1;
+ payload.tag = cpu_to_le32(1);
payload.device_id = cpu_to_le32(device_id);
- PM8001_MSG_DBG(pm8001_ha,
- pm8001_printk("unregister device device_id = %d\n", device_id));
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
- return ret;
+ pm8001_dbg(pm8001_ha, INIT, "unregister device device_id %d\n",
+ device_id);
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
}
/**
* pm8001_chip_phy_ctl_req - support the local phy operation
* @pm8001_ha: our hba card information.
- * @num: the inbound queue number
- * @phy_id: the phy id which we wanted to operate
- * @phy_op:
+ * @phyId: the phy id which we wanted to operate
+ * @phy_op: the phy operation to request
*/
static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
u32 phyId, u32 phy_op)
{
struct local_phy_ctl_req payload;
- struct inbound_queue_table *circularQ;
- int ret;
u32 opc = OPC_INB_LOCAL_PHY_CONTROL;
+
memset(&payload, 0, sizeof(payload));
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
- payload.tag = 1;
+ payload.tag = cpu_to_le32(1);
payload.phyop_phyid =
cpu_to_le32(((phy_op & 0xff) << 8) | (phyId & 0x0F));
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
- return ret;
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
}
-static u32 pm8001_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
+static u32 pm8001_chip_is_our_interrupt(struct pm8001_hba_info *pm8001_ha)
{
u32 value;
-#ifdef PM8001_USE_MSIX
- return 1;
-#endif
+
+ if (pm8001_ha->use_msix)
+ return 1;
+
value = pm8001_cr32(pm8001_ha, 0, MSGU_ODR);
if (value)
return 1;
return 0;
-
}
/**
* pm8001_chip_isr - PM8001 isr handler.
* @pm8001_ha: our hba card information.
- * @irq: irq number.
- * @stat: stat.
+ * @vec: IRQ number
*/
static irqreturn_t
-pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha)
+pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
- unsigned long flags;
- spin_lock_irqsave(&pm8001_ha->lock, flags);
- pm8001_chip_interrupt_disable(pm8001_ha);
- process_oq(pm8001_ha);
- pm8001_chip_interrupt_enable(pm8001_ha);
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_chip_interrupt_disable(pm8001_ha, vec);
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "irq vec %d, ODMR:0x%x\n",
+ vec, pm8001_cr32(pm8001_ha, 0, 0x30));
+ process_oq(pm8001_ha, vec);
+ pm8001_chip_interrupt_enable(pm8001_ha, vec);
return IRQ_HANDLED;
}
static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc,
- u32 dev_id, u8 flag, u32 task_tag, u32 cmd_tag)
+ u32 dev_id, enum sas_internal_abort type, u32 task_tag, u32 cmd_tag)
{
struct task_abort_req task_abort;
- struct inbound_queue_table *circularQ;
- int ret;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
memset(&task_abort, 0, sizeof(task_abort));
- if (ABORT_SINGLE == (flag & ABORT_MASK)) {
+ if (type == SAS_INTERNAL_ABORT_SINGLE) {
task_abort.abort_all = 0;
task_abort.device_id = cpu_to_le32(dev_id);
task_abort.tag_to_abort = cpu_to_le32(task_tag);
- task_abort.tag = cpu_to_le32(cmd_tag);
- } else if (ABORT_ALL == (flag & ABORT_MASK)) {
+ } else if (type == SAS_INTERNAL_ABORT_DEV) {
task_abort.abort_all = cpu_to_le32(1);
task_abort.device_id = cpu_to_le32(dev_id);
- task_abort.tag = cpu_to_le32(cmd_tag);
+ } else {
+ pm8001_dbg(pm8001_ha, EH, "unknown type (%d)\n", type);
+ return -EIO;
}
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort);
- return ret;
+
+ task_abort.tag = cpu_to_le32(cmd_tag);
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &task_abort,
+ sizeof(task_abort), 0);
}
-/**
+/*
* pm8001_chip_abort_task - SAS abort task when error or exception happened.
- * @task: the task we wanted to aborted.
- * @flag: the abort flag.
*/
-static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
- struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, u32 cmd_tag)
+int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
{
- u32 opc, device_id;
+ struct sas_task *task = ccb->task;
+ struct sas_internal_abort_task *abort = &task->abort_task;
+ struct pm8001_device *pm8001_dev = ccb->device;
int rc = TMF_RESP_FUNC_FAILED;
- PM8001_EH_DBG(pm8001_ha, pm8001_printk("cmd_tag = %x, abort task tag"
- " = %x", cmd_tag, task_tag));
- if (pm8001_dev->dev_type == SAS_END_DEV)
+ u32 opc, device_id;
+
+ pm8001_dbg(pm8001_ha, EH, "cmd_tag = %x, abort task tag = 0x%x\n",
+ ccb->ccb_tag, abort->tag);
+ if (pm8001_dev->dev_type == SAS_END_DEVICE)
opc = OPC_INB_SSP_ABORT;
- else if (pm8001_dev->dev_type == SATA_DEV)
+ else if (pm8001_dev->dev_type == SAS_SATA_DEV)
opc = OPC_INB_SATA_ABORT;
else
opc = OPC_INB_SMP_ABORT;/* SMP */
device_id = pm8001_dev->device_id;
- rc = send_task_abort(pm8001_ha, opc, device_id, flag,
- task_tag, cmd_tag);
+ rc = send_task_abort(pm8001_ha, opc, device_id, abort->type,
+ abort->tag, ccb->ccb_tag);
if (rc != TMF_RESP_FUNC_COMPLETE)
- PM8001_EH_DBG(pm8001_ha, pm8001_printk("rc= %d\n", rc));
+ pm8001_dbg(pm8001_ha, EH, "rc= %d\n", rc);
return rc;
}
@@ -4154,37 +4337,35 @@ static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
* @ccb: the ccb information.
* @tmf: task management function.
*/
-static int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
- struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb, struct sas_tmf_task *tmf)
{
struct sas_task *task = ccb->task;
struct domain_device *dev = task->dev;
struct pm8001_device *pm8001_dev = dev->lldd_dev;
u32 opc = OPC_INB_SSPINITMSTART;
- struct inbound_queue_table *circularQ;
struct ssp_ini_tm_start_req sspTMCmd;
- int ret;
memset(&sspTMCmd, 0, sizeof(sspTMCmd));
sspTMCmd.device_id = cpu_to_le32(pm8001_dev->device_id);
- sspTMCmd.relate_tag = cpu_to_le32(tmf->tag_of_task_to_be_managed);
+ sspTMCmd.relate_tag = cpu_to_le32((u32)tmf->tag_of_task_to_be_managed);
sspTMCmd.tmf = cpu_to_le32(tmf->tmf);
memcpy(sspTMCmd.lun, task->ssp_task.LUN, 8);
sspTMCmd.tag = cpu_to_le32(ccb->ccb_tag);
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sspTMCmd);
- return ret;
+ if (pm8001_ha->chip_id != chip_8001)
+ sspTMCmd.ds_ads_m = cpu_to_le32(0x08);
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &sspTMCmd,
+ sizeof(sspTMCmd), 0);
}
-static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
void *payload)
{
u32 opc = OPC_INB_GET_NVMD_DATA;
u32 nvmd_type;
int rc;
- u32 tag;
struct pm8001_ccb_info *ccb;
- struct inbound_queue_table *circularQ;
struct get_nvm_data_req nvmd_req;
struct fw_control_ex *fw_control_context;
struct pm8001_ioctl_payload *ioctl_payload = payload;
@@ -4193,19 +4374,18 @@ static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
if (!fw_control_context)
return -ENOMEM;
- fw_control_context->usrAddr = (u8 *)&ioctl_payload->func_specific[0];
- fw_control_context->len = ioctl_payload->length;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ fw_control_context->usrAddr = (u8 *)ioctl_payload->func_specific;
+ fw_control_context->len = ioctl_payload->rd_length;
memset(&nvmd_req, 0, sizeof(nvmd_req));
- rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc) {
+
+ ccb = pm8001_ccb_alloc(pm8001_ha, NULL, NULL);
+ if (!ccb) {
kfree(fw_control_context);
- return rc;
+ return -SAS_QUEUE_FULL;
}
- ccb = &pm8001_ha->ccb_info[tag];
- ccb->ccb_tag = tag;
ccb->fw_control_context = fw_control_context;
- nvmd_req.tag = cpu_to_le32(tag);
+
+ nvmd_req.tag = cpu_to_le32(ccb->ccb_tag);
switch (nvmd_type) {
case TWI_DEVICE: {
@@ -4215,7 +4395,7 @@ static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
twi_page_size << 8 | TWI_DEVICE);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->rd_length);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
nvmd_req.resp_addr_lo =
@@ -4224,7 +4404,7 @@ static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
}
case C_SEEPROM: {
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->rd_length);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
nvmd_req.resp_addr_lo =
@@ -4233,7 +4413,7 @@ static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
}
case VPD_FLASH: {
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->rd_length);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
nvmd_req.resp_addr_lo =
@@ -4242,29 +4422,43 @@ static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
}
case EXPAN_ROM: {
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->rd_length);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
nvmd_req.resp_addr_lo =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
break;
}
+ case IOP_RDUMP: {
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | IOP_RDUMP);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->rd_length);
+ nvmd_req.vpd_offset = cpu_to_le32(ioctl_payload->offset);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
default:
break;
}
- rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &nvmd_req,
+ sizeof(nvmd_req), 0);
+ if (rc) {
+ kfree(fw_control_context);
+ pm8001_ccb_free(pm8001_ha, ccb);
+ }
return rc;
}
-static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
void *payload)
{
u32 opc = OPC_INB_SET_NVMD_DATA;
u32 nvmd_type;
int rc;
- u32 tag;
struct pm8001_ccb_info *ccb;
- struct inbound_queue_table *circularQ;
struct set_nvm_data_req nvmd_req;
struct fw_control_ex *fw_control_context;
struct pm8001_ioctl_payload *ioctl_payload = payload;
@@ -4273,20 +4467,20 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
if (!fw_control_context)
return -ENOMEM;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr,
- ioctl_payload->func_specific,
- ioctl_payload->length);
+ &ioctl_payload->func_specific,
+ ioctl_payload->wr_length);
memset(&nvmd_req, 0, sizeof(nvmd_req));
- rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc) {
+
+ ccb = pm8001_ccb_alloc(pm8001_ha, NULL, NULL);
+ if (!ccb) {
kfree(fw_control_context);
- return rc;
+ return -SAS_QUEUE_FULL;
}
- ccb = &pm8001_ha->ccb_info[tag];
ccb->fw_control_context = fw_control_context;
- ccb->ccb_tag = tag;
- nvmd_req.tag = cpu_to_le32(tag);
+
+ nvmd_req.tag = cpu_to_le32(ccb->ccb_tag);
switch (nvmd_type) {
case TWI_DEVICE: {
u32 twi_addr, twi_page_size;
@@ -4295,7 +4489,7 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
twi_page_size << 8 | TWI_DEVICE);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->wr_length);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
nvmd_req.resp_addr_lo =
@@ -4304,7 +4498,7 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
}
case C_SEEPROM:
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->wr_length);
nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
@@ -4313,7 +4507,7 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
break;
case VPD_FLASH:
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->wr_length);
nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
@@ -4322,7 +4516,7 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
break;
case EXPAN_ROM:
nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
- nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->wr_length);
nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
nvmd_req.resp_addr_hi =
cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
@@ -4332,7 +4526,13 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
default:
break;
}
- rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &nvmd_req,
+ sizeof(nvmd_req), 0);
+ if (rc) {
+ kfree(fw_control_context);
+ pm8001_ccb_free(pm8001_ha, ccb);
+ }
return rc;
}
@@ -4340,32 +4540,33 @@ static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
* pm8001_chip_fw_flash_update_build - support the firmware update operation
* @pm8001_ha: our hba card information.
* @fw_flash_updata_info: firmware flash update param
+ * @tag: Tag to apply to the payload
*/
-static int
+int
pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
void *fw_flash_updata_info, u32 tag)
{
struct fw_flash_Update_req payload;
struct fw_flash_updata_info *info;
- struct inbound_queue_table *circularQ;
- int ret;
u32 opc = OPC_INB_FW_FLASH_UPDATE;
memset(&payload, 0, sizeof(struct fw_flash_Update_req));
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
info = fw_flash_updata_info;
payload.tag = cpu_to_le32(tag);
payload.cur_image_len = cpu_to_le32(info->cur_image_len);
payload.cur_image_offset = cpu_to_le32(info->cur_image_offset);
payload.total_image_len = cpu_to_le32(info->total_image_len);
payload.len = info->sgl.im_len.len ;
- payload.sgl_addr_lo = lower_32_bits(info->sgl.addr);
- payload.sgl_addr_hi = upper_32_bits(info->sgl.addr);
- ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
- return ret;
+ payload.sgl_addr_lo =
+ cpu_to_le32(lower_32_bits(le64_to_cpu(info->sgl.addr)));
+ payload.sgl_addr_hi =
+ cpu_to_le32(upper_32_bits(le64_to_cpu(info->sgl.addr)));
+
+ return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
}
-static int
+int
pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
void *payload)
{
@@ -4373,31 +4574,18 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
struct fw_control_info *fw_control;
struct fw_control_ex *fw_control_context;
int rc;
- u32 tag;
struct pm8001_ccb_info *ccb;
- void *buffer = NULL;
- dma_addr_t phys_addr;
- u32 phys_addr_hi;
- u32 phys_addr_lo;
+ void *buffer = pm8001_ha->memoryMap.region[FW_FLASH].virt_ptr;
+ dma_addr_t phys_addr = pm8001_ha->memoryMap.region[FW_FLASH].phys_addr;
struct pm8001_ioctl_payload *ioctl_payload = payload;
fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
if (!fw_control_context)
return -ENOMEM;
- fw_control = (struct fw_control_info *)&ioctl_payload->func_specific[0];
- if (fw_control->len != 0) {
- if (pm8001_mem_alloc(pm8001_ha->pdev,
- (void **)&buffer,
- &phys_addr,
- &phys_addr_hi,
- &phys_addr_lo,
- fw_control->len, 0) != 0) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Mem alloc failure\n"));
- kfree(fw_control_context);
- return -ENOMEM;
- }
- }
+ fw_control = (struct fw_control_info *)&ioctl_payload->func_specific;
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "dma fw_control context input length :%x\n",
+ fw_control->len);
memcpy(buffer, fw_control->buffer, fw_control->len);
flash_update_info.sgl.addr = cpu_to_le64(phys_addr);
flash_update_info.sgl.im_len.len = cpu_to_le32(fw_control->len);
@@ -4407,79 +4595,167 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
flash_update_info.total_image_len = fw_control->size;
fw_control_context->fw_control = fw_control;
fw_control_context->virtAddr = buffer;
+ fw_control_context->phys_addr = phys_addr;
fw_control_context->len = fw_control->len;
- rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc) {
+
+ ccb = pm8001_ccb_alloc(pm8001_ha, NULL, NULL);
+ if (!ccb) {
kfree(fw_control_context);
- return rc;
+ return -SAS_QUEUE_FULL;
}
- ccb = &pm8001_ha->ccb_info[tag];
ccb->fw_control_context = fw_control_context;
- ccb->ccb_tag = tag;
+
rc = pm8001_chip_fw_flash_update_build(pm8001_ha, &flash_update_info,
- tag);
+ ccb->ccb_tag);
+ if (rc) {
+ kfree(fw_control_context);
+ pm8001_ccb_free(pm8001_ha, ccb);
+ }
+
return rc;
}
-static int
+ssize_t
+pm8001_get_gsm_dump(struct device *cdev, u32 length, char *buf)
+{
+ u32 value, rem, offset = 0, bar = 0;
+ u32 index, work_offset, dw_length;
+ u32 shift_value, gsm_base, gsm_dump_offset;
+ char *direct_data;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ direct_data = buf;
+ gsm_dump_offset = pm8001_ha->fatal_forensic_shift_offset;
+
+ /* check max is 1 Mbytes */
+ if ((length > 0x100000) || (gsm_dump_offset & 3) ||
+ ((gsm_dump_offset + length) > 0x1000000))
+ return -EINVAL;
+
+ if (pm8001_ha->chip_id == chip_8001)
+ bar = 2;
+ else
+ bar = 1;
+
+ work_offset = gsm_dump_offset & 0xFFFF0000;
+ offset = gsm_dump_offset & 0x0000FFFF;
+ gsm_dump_offset = work_offset;
+ /* adjust length to dword boundary */
+ rem = length & 3;
+ dw_length = length >> 2;
+
+ for (index = 0; index < dw_length; index++) {
+ if ((work_offset + offset) & 0xFFFF0000) {
+ if (pm8001_ha->chip_id == chip_8001)
+ shift_value = ((gsm_dump_offset + offset) &
+ SHIFT_REG_64K_MASK);
+ else
+ shift_value = (((gsm_dump_offset + offset) &
+ SHIFT_REG_64K_MASK) >>
+ SHIFT_REG_BIT_SHIFT);
+
+ if (pm8001_ha->chip_id == chip_8001) {
+ gsm_base = GSM_BASE;
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ (gsm_base + shift_value)))
+ return -EIO;
+ } else {
+ gsm_base = 0;
+ if (-1 == pm80xx_bar4_shift(pm8001_ha,
+ (gsm_base + shift_value)))
+ return -EIO;
+ }
+ gsm_dump_offset = (gsm_dump_offset + offset) &
+ 0xFFFF0000;
+ work_offset = 0;
+ offset = offset & 0x0000FFFF;
+ }
+ value = pm8001_cr32(pm8001_ha, bar, (work_offset + offset) &
+ 0x0000FFFF);
+ direct_data += sprintf(direct_data, "%08x ", value);
+ offset += 4;
+ }
+ if (rem != 0) {
+ value = pm8001_cr32(pm8001_ha, bar, (work_offset + offset) &
+ 0x0000FFFF);
+ /* xfr for non_dw */
+ direct_data += sprintf(direct_data, "%08x ", value);
+ }
+ /* Shift back to BAR4 original address */
+ if (-1 == pm8001_bar4_shift(pm8001_ha, 0))
+ return -EIO;
+ pm8001_ha->fatal_forensic_shift_offset += 1024;
+
+ if (pm8001_ha->fatal_forensic_shift_offset >= 0x100000)
+ pm8001_ha->fatal_forensic_shift_offset = 0;
+ return direct_data - buf;
+}
+
+int
pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_dev, u32 state)
{
struct set_dev_state_req payload;
- struct inbound_queue_table *circularQ;
struct pm8001_ccb_info *ccb;
int rc;
- u32 tag;
u32 opc = OPC_INB_SET_DEVICE_STATE;
+
memset(&payload, 0, sizeof(payload));
- rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc)
- return -1;
- ccb = &pm8001_ha->ccb_info[tag];
- ccb->ccb_tag = tag;
- ccb->device = pm8001_dev;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
- payload.tag = cpu_to_le32(tag);
+
+ ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_dev, NULL);
+ if (!ccb)
+ return -SAS_QUEUE_FULL;
+
+ payload.tag = cpu_to_le32(ccb->ccb_tag);
payload.device_id = cpu_to_le32(pm8001_dev->device_id);
payload.nds = cpu_to_le32(state);
- rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
- return rc;
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_ccb_free(pm8001_ha, ccb);
+
+ return rc;
}
static int
pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha)
{
struct sas_re_initialization_req payload;
- struct inbound_queue_table *circularQ;
struct pm8001_ccb_info *ccb;
int rc;
- u32 tag;
u32 opc = OPC_INB_SAS_RE_INITIALIZE;
+
memset(&payload, 0, sizeof(payload));
- rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc)
- return -1;
- ccb = &pm8001_ha->ccb_info[tag];
- ccb->ccb_tag = tag;
- circularQ = &pm8001_ha->inbnd_q_tbl[0];
- payload.tag = cpu_to_le32(tag);
+
+ ccb = pm8001_ccb_alloc(pm8001_ha, NULL, NULL);
+ if (!ccb)
+ return -SAS_QUEUE_FULL;
+
+ payload.tag = cpu_to_le32(ccb->ccb_tag);
payload.SSAHOLT = cpu_to_le32(0xd << 25);
payload.sata_hol_tmo = cpu_to_le32(80);
payload.open_reject_cmdretries_data_retries = cpu_to_le32(0xff00ff);
- rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
- return rc;
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_ccb_free(pm8001_ha, ccb);
+
+ return rc;
}
const struct pm8001_dispatch pm8001_8001_dispatch = {
.name = "pmc8001",
.chip_init = pm8001_chip_init,
+ .chip_post_init = pm8001_chip_post_init,
.chip_soft_rst = pm8001_chip_soft_rst,
.chip_rst = pm8001_hw_chip_rst,
.chip_iounmap = pm8001_chip_iounmap,
.isr = pm8001_chip_isr,
- .is_our_interupt = pm8001_chip_is_our_interupt,
+ .is_our_interrupt = pm8001_chip_is_our_interrupt,
.isr_process_oq = process_oq,
.interrupt_enable = pm8001_chip_interrupt_enable,
.interrupt_disable = pm8001_chip_interrupt_disable,
@@ -4499,5 +4775,5 @@ const struct pm8001_dispatch pm8001_8001_dispatch = {
.fw_flash_update_req = pm8001_chip_fw_flash_update_req,
.set_dev_state_req = pm8001_chip_set_dev_state_req,
.sas_re_init_req = pm8001_chip_sas_re_initialization,
+ .fatal_errors = pm80xx_fatal_errors,
};
-
diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h
index 909132041c07..f1ce8df082b0 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.h
+++ b/drivers/scsi/pm8001/pm8001_hwi.h
@@ -131,6 +131,8 @@
#define LINKRATE_30 (0x02 << 8)
#define LINKRATE_60 (0x04 << 8)
+/* for new SPC controllers MEMBASE III is shared between BIOS and DATA */
+#define GSM_SM_BASE 0x4F0000
struct mpi_msg_hdr{
__le32 header; /* Bits [11:0] - Message operation code */
/* Bits [15:12] - Message Category */
@@ -298,7 +300,7 @@ struct local_phy_ctl_resp {
#define OP_BITS 0x0000FF00
-#define ID_BITS 0x0000000F
+#define ID_BITS 0x000000FF
/*
* brief the data structure of PORT Control Command
@@ -337,8 +339,10 @@ struct ssp_completion_resp {
__le32 status;
__le32 param;
__le32 ssptag_rescv_rescpad;
+
+ /* Must be last --ends in a flexible-array member. */
struct ssp_response_iu ssp_resp_iu;
- __le32 residual_count;
+ /* __le32 residual_count; */
} __attribute__((packed, aligned(4)));
@@ -432,11 +436,6 @@ struct task_abort_req {
u32 reserved[11];
} __attribute__((packed, aligned(4)));
-/* These flags used for SSP SMP & SATA Abort */
-#define ABORT_MASK 0x3
-#define ABORT_SINGLE 0x0
-#define ABORT_ALL 0x1
-
/**
* brief the data structure of SSP SATA SMP Abort Response
* use to describe SSP SMP & SATA Abort Response ( 64 bytes)
@@ -518,7 +517,7 @@ struct sata_start_req {
__le32 tag;
__le32 device_id;
__le32 data_len;
- __le32 ncqtag_atap_dir_m;
+ __le32 retfis_ncqtag_atap_dir_m;
struct host_to_dev_fis sata_fis;
u32 reserved1;
u32 reserved2;
@@ -599,7 +598,7 @@ struct fw_flash_Update_req {
*
*/
struct fw_flash_Update_resp {
- dma_addr_t tag;
+ __le32 tag;
__le32 status;
u32 reserved[13];
} __attribute__((packed, aligned(4)));
@@ -625,7 +624,7 @@ struct set_nvm_data_req {
__le32 tag;
__le32 len_ir_vpdd;
__le32 vpd_offset;
- u32 reserved[8];
+ __le32 reserved[8];
__le32 resp_addr_lo;
__le32 resp_addr_hi;
__le32 resp_len;
@@ -803,6 +802,7 @@ struct set_dev_state_resp {
#define IO_ABORT_IN_PROGRESS 0x40
#define IO_ABORT_DELAYED 0x41
#define IO_INVALID_LENGTH 0x42
+#define IO_FATAL_ERROR 0x51
/* WARNING: This error code must always be the last number.
* If you add error code, modify this code also
@@ -1025,5 +1025,8 @@ struct set_dev_state_resp {
#define DEVREG_FAILURE_PORT_NOT_VALID_STATE 0x06
#define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID 0x07
+#define GSM_BASE 0x4F0000
+#define SHIFT_REG_64K_MASK 0xffff0000
+#define SHIFT_REG_BIT_SHIFT 8
#endif
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index 172cefb6deb9..9acca83d6958 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -1,5 +1,5 @@
/*
- * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ * PMC-Sierra PM8001/8081/8088/8089 SAS/SATA based host adapters driver
*
* Copyright (c) 2008-2009 USI Co., Ltd.
* All rights reserved.
@@ -41,11 +41,56 @@
#include <linux/slab.h>
#include "pm8001_sas.h"
#include "pm8001_chips.h"
+#include "pm80xx_hwi.h"
+
+static ulong logging_level = PM8001_FAIL_LOGGING | PM8001_IOERR_LOGGING |
+ PM8001_EVENT_LOGGING | PM8001_INIT_LOGGING;
+module_param(logging_level, ulong, 0644);
+MODULE_PARM_DESC(logging_level, " bits for enabling logging info.");
+
+static ulong link_rate = LINKRATE_15 | LINKRATE_30 | LINKRATE_60 | LINKRATE_120;
+module_param(link_rate, ulong, 0644);
+MODULE_PARM_DESC(link_rate, "Enable link rate.\n"
+ " 1: Link rate 1.5G\n"
+ " 2: Link rate 3.0G\n"
+ " 4: Link rate 6.0G\n"
+ " 8: Link rate 12.0G\n");
+
+bool pm8001_use_msix = true;
+module_param_named(use_msix, pm8001_use_msix, bool, 0444);
+MODULE_PARM_DESC(zoned, "Use MSIX interrupts. Default: true");
+
+static bool pm8001_use_tasklet = true;
+module_param_named(use_tasklet, pm8001_use_tasklet, bool, 0444);
+MODULE_PARM_DESC(zoned, "Use MSIX interrupts. Default: true");
+
+static bool pm8001_read_wwn = true;
+module_param_named(read_wwn, pm8001_read_wwn, bool, 0444);
+MODULE_PARM_DESC(zoned, "Get WWN from the controller. Default: true");
+
+uint pcs_event_log_severity = 0x03;
+module_param(pcs_event_log_severity, int, 0644);
+MODULE_PARM_DESC(pcs_event_log_severity, "PCS event log severity level");
static struct scsi_transport_template *pm8001_stt;
+static int pm8001_init_ccb_tag(struct pm8001_hba_info *);
+/*
+ * chip info structure to identify chip key functionality as
+ * encryption available/not, no of ports, hw specific function ref
+ */
static const struct pm8001_chip_info pm8001_chips[] = {
- [chip_8001] = { 8, &pm8001_8001_dispatch,},
+ [chip_8001] = {0, 8, &pm8001_8001_dispatch,},
+ [chip_8008] = {0, 8, &pm8001_80xx_dispatch,},
+ [chip_8009] = {1, 8, &pm8001_80xx_dispatch,},
+ [chip_8018] = {0, 16, &pm8001_80xx_dispatch,},
+ [chip_8019] = {1, 16, &pm8001_80xx_dispatch,},
+ [chip_8074] = {0, 8, &pm8001_80xx_dispatch,},
+ [chip_8076] = {0, 16, &pm8001_80xx_dispatch,},
+ [chip_8077] = {0, 16, &pm8001_80xx_dispatch,},
+ [chip_8006] = {0, 16, &pm8001_80xx_dispatch,},
+ [chip_8070] = {0, 8, &pm8001_80xx_dispatch,},
+ [chip_8072] = {0, 16, &pm8001_80xx_dispatch,},
};
static int pm8001_id;
@@ -53,36 +98,38 @@ LIST_HEAD(hba_list);
struct workqueue_struct *pm8001_wq;
-/**
+static void pm8001_map_queues(struct Scsi_Host *shost)
+{
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
+
+ if (pm8001_ha->number_of_intr > 1) {
+ blk_mq_map_hw_queues(qmap, &pm8001_ha->pdev->dev, 1);
+ return;
+ }
+
+ blk_mq_map_queues(qmap);
+}
+
+/*
* The main structure which LLDD must register for scsi core.
*/
-static struct scsi_host_template pm8001_sht = {
- .module = THIS_MODULE,
- .name = DRV_NAME,
- .queuecommand = sas_queuecommand,
- .target_alloc = sas_target_alloc,
- .slave_configure = pm8001_slave_configure,
- .slave_destroy = sas_slave_destroy,
+static const struct scsi_host_template pm8001_sht = {
+ LIBSAS_SHT_BASE
.scan_finished = pm8001_scan_finished,
.scan_start = pm8001_scan_start,
- .change_queue_depth = sas_change_queue_depth,
- .change_queue_type = sas_change_queue_type,
- .bios_param = sas_bios_param,
.can_queue = 1,
- .cmd_per_lun = 1,
- .this_id = -1,
- .sg_tablesize = SG_ALL,
- .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
- .use_clustering = ENABLE_CLUSTERING,
- .eh_device_reset_handler = sas_eh_device_reset_handler,
- .eh_bus_reset_handler = sas_eh_bus_reset_handler,
- .slave_alloc = pm8001_slave_alloc,
- .target_destroy = sas_target_destroy,
- .ioctl = sas_ioctl,
- .shost_attrs = pm8001_host_attrs,
+ .sg_tablesize = PM8001_MAX_DMA_SG,
+ .max_sectors = PM8001_MAX_SECTORS,
+ .shost_groups = pm8001_host_groups,
+ .sdev_groups = pm8001_sdev_groups,
+ .track_queue_depth = 1,
+ .cmd_per_lun = 32,
+ .map_queues = pm8001_map_queues,
};
-/**
+/*
* Sas layer call this function to execute specific task.
*/
static struct sas_domain_function_template pm8001_transport_ops = {
@@ -93,45 +140,45 @@ static struct sas_domain_function_template pm8001_transport_ops = {
.lldd_control_phy = pm8001_phy_control,
.lldd_abort_task = pm8001_abort_task,
- .lldd_abort_task_set = pm8001_abort_task_set,
- .lldd_clear_aca = pm8001_clear_aca,
+ .lldd_abort_task_set = sas_abort_task_set,
.lldd_clear_task_set = pm8001_clear_task_set,
.lldd_I_T_nexus_reset = pm8001_I_T_nexus_reset,
.lldd_lu_reset = pm8001_lu_reset,
.lldd_query_task = pm8001_query_task,
+ .lldd_port_formed = pm8001_port_formed,
+ .lldd_tmf_exec_complete = pm8001_setds_completion,
+ .lldd_tmf_aborted = pm8001_tmf_aborted,
};
/**
- *pm8001_phy_init - initiate our adapter phys
- *@pm8001_ha: our hba structure.
- *@phy_id: phy id.
+ * pm8001_phy_init - initiate our adapter phys
+ * @pm8001_ha: our hba structure.
+ * @phy_id: phy id.
*/
-static void __devinit pm8001_phy_init(struct pm8001_hba_info *pm8001_ha,
- int phy_id)
+static void pm8001_phy_init(struct pm8001_hba_info *pm8001_ha, int phy_id)
{
struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
- phy->phy_state = 0;
+ phy->phy_state = PHY_LINK_DISABLE;
phy->pm8001_ha = pm8001_ha;
+ phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
sas_phy->enabled = (phy_id < pm8001_ha->chip->n_phy) ? 1 : 0;
- sas_phy->class = SAS;
sas_phy->iproto = SAS_PROTOCOL_ALL;
sas_phy->tproto = 0;
- sas_phy->type = PHY_TYPE_PHYSICAL;
sas_phy->role = PHY_ROLE_INITIATOR;
sas_phy->oob_mode = OOB_NOT_CONNECTED;
sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
sas_phy->id = phy_id;
- sas_phy->sas_addr = &pm8001_ha->sas_addr[0];
+ sas_phy->sas_addr = (u8 *)&phy->dev_sas_addr;
sas_phy->frame_rcvd = &phy->frame_rcvd[0];
sas_phy->ha = (struct sas_ha_struct *)pm8001_ha->shost->hostdata;
sas_phy->lldd_phy = phy;
}
/**
- *pm8001_free - free hba
- *@pm8001_ha: our hba structure.
- *
+ * pm8001_free - free hba
+ * @pm8001_ha: our hba structure.
*/
static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
{
@@ -142,65 +189,152 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
for (i = 0; i < USI_MAX_MEMCNT; i++) {
if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
- pci_free_consistent(pm8001_ha->pdev,
- pm8001_ha->memoryMap.region[i].element_size,
+ dma_free_coherent(&pm8001_ha->pdev->dev,
+ (pm8001_ha->memoryMap.region[i].total_len +
+ pm8001_ha->memoryMap.region[i].alignment),
pm8001_ha->memoryMap.region[i].virt_ptr,
pm8001_ha->memoryMap.region[i].phys_addr);
}
}
PM8001_CHIP_DISP->chip_iounmap(pm8001_ha);
- if (pm8001_ha->shost)
- scsi_host_put(pm8001_ha->shost);
flush_workqueue(pm8001_wq);
- kfree(pm8001_ha->tags);
+ bitmap_free(pm8001_ha->rsvd_tags);
kfree(pm8001_ha);
}
-#ifdef PM8001_USE_TASKLET
+/**
+ * pm8001_tasklet() - tasklet for 64 msi-x interrupt handler
+ * @opaque: the passed general host adapter struct
+ * Note: pm8001_tasklet is common for pm8001 & pm80xx
+ */
static void pm8001_tasklet(unsigned long opaque)
{
- struct pm8001_hba_info *pm8001_ha;
- pm8001_ha = (struct pm8001_hba_info *)opaque;
- if (unlikely(!pm8001_ha))
- BUG_ON(1);
- PM8001_CHIP_DISP->isr(pm8001_ha);
+ struct isr_param *irq_vector = (struct isr_param *)opaque;
+ struct pm8001_hba_info *pm8001_ha = irq_vector->drv_inst;
+
+ if (WARN_ON_ONCE(!pm8001_ha))
+ return;
+
+ PM8001_CHIP_DISP->isr(pm8001_ha, irq_vector->irq_id);
}
-#endif
+static void pm8001_init_tasklet(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+
+ if (!pm8001_use_tasklet)
+ return;
+
+ /* Tasklet for non msi-x interrupt handler */
+ if ((!pm8001_ha->pdev->msix_cap || !pci_msi_enabled()) ||
+ (pm8001_ha->chip_id == chip_8001)) {
+ tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet,
+ (unsigned long)&(pm8001_ha->irq_vector[0]));
+ return;
+ }
+ for (i = 0; i < PM8001_MAX_MSIX_VEC; i++)
+ tasklet_init(&pm8001_ha->tasklet[i], pm8001_tasklet,
+ (unsigned long)&(pm8001_ha->irq_vector[i]));
+}
- /**
- * pm8001_interrupt - when HBA originate a interrupt,we should invoke this
- * dispatcher to handle each case.
- * @irq: irq number.
- * @opaque: the passed general host adapter struct
- */
-static irqreturn_t pm8001_interrupt(int irq, void *opaque)
+static void pm8001_kill_tasklet(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+
+ if (!pm8001_use_tasklet)
+ return;
+
+ /* For non-msix and msix interrupts */
+ if ((!pm8001_ha->pdev->msix_cap || !pci_msi_enabled()) ||
+ (pm8001_ha->chip_id == chip_8001)) {
+ tasklet_kill(&pm8001_ha->tasklet[0]);
+ return;
+ }
+
+ for (i = 0; i < PM8001_MAX_MSIX_VEC; i++)
+ tasklet_kill(&pm8001_ha->tasklet[i]);
+}
+
+static irqreturn_t pm8001_handle_irq(struct pm8001_hba_info *pm8001_ha,
+ int irq)
{
- struct pm8001_hba_info *pm8001_ha;
- irqreturn_t ret = IRQ_HANDLED;
- struct sas_ha_struct *sha = opaque;
- pm8001_ha = sha->lldd_ha;
if (unlikely(!pm8001_ha))
return IRQ_NONE;
- if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
+
+ if (!PM8001_CHIP_DISP->is_our_interrupt(pm8001_ha))
return IRQ_NONE;
-#ifdef PM8001_USE_TASKLET
- tasklet_schedule(&pm8001_ha->tasklet);
-#else
- ret = PM8001_CHIP_DISP->isr(pm8001_ha);
-#endif
- return ret;
+
+ if (!pm8001_use_tasklet)
+ return PM8001_CHIP_DISP->isr(pm8001_ha, irq);
+
+ tasklet_schedule(&pm8001_ha->tasklet[irq]);
+ return IRQ_HANDLED;
+}
+
+/**
+ * pm8001_interrupt_handler_msix - main MSIX interrupt handler.
+ * It obtains the vector number and calls the equivalent bottom
+ * half or services directly.
+ * @irq: interrupt number
+ * @opaque: the passed outbound queue/vector. Host structure is
+ * retrieved from the same.
+ */
+static irqreturn_t pm8001_interrupt_handler_msix(int irq, void *opaque)
+{
+ struct isr_param *irq_vector = (struct isr_param *)opaque;
+ struct pm8001_hba_info *pm8001_ha = irq_vector->drv_inst;
+
+ return pm8001_handle_irq(pm8001_ha, irq_vector->irq_id);
+}
+
+/**
+ * pm8001_interrupt_handler_intx - main INTx interrupt handler.
+ * @irq: interrupt number
+ * @dev_id: sas_ha structure. The HBA is retrieved from sas_ha structure.
+ */
+
+static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id)
+{
+ struct sas_ha_struct *sha = dev_id;
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return pm8001_handle_irq(pm8001_ha, 0);
}
+static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha);
+static void pm8001_free_irq(struct pm8001_hba_info *pm8001_ha);
+
/**
* pm8001_alloc - initiate our hba structure and 6 DMAs area.
- * @pm8001_ha:our hba structure.
- *
+ * @pm8001_ha: our hba structure.
+ * @ent: PCI device ID structure to match on
*/
-static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
+static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha,
+ const struct pci_device_id *ent)
{
- int i;
+ int i, count = 0, rc = 0;
+ u32 ci_offset, ib_offset, ob_offset, pi_offset;
+ struct inbound_queue_table *ibq;
+ struct outbound_queue_table *obq;
+
spin_lock_init(&pm8001_ha->lock);
+ spin_lock_init(&pm8001_ha->bitmap_lock);
+ pm8001_dbg(pm8001_ha, INIT, "pm8001_alloc: PHY:%x\n",
+ pm8001_ha->chip->n_phy);
+
+ /* Request Interrupt */
+ rc = pm8001_request_irq(pm8001_ha);
+ if (rc)
+ goto err_out;
+
+ count = pm8001_ha->max_q_num;
+ /* Queues are chosen based on the number of cores/msix availability */
+ ib_offset = pm8001_ha->ib_offset = USI_MAX_MEMCNT_BASE;
+ ci_offset = pm8001_ha->ci_offset = ib_offset + count;
+ ob_offset = pm8001_ha->ob_offset = ci_offset + count;
+ pi_offset = pm8001_ha->pi_offset = ob_offset + count;
+ pm8001_ha->max_memcnt = pi_offset + count;
+
for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
pm8001_phy_init(pm8001_ha, i);
pm8001_ha->port[i].wide_port_phymap = 0;
@@ -209,9 +343,6 @@ static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
INIT_LIST_HEAD(&pm8001_ha->port[i].list);
}
- pm8001_ha->tags = kzalloc(PM8001_MAX_CCB, GFP_KERNEL);
- if (!pm8001_ha->tags)
- goto err_out;
/* MPI Memory region 1 for AAP Event Log for fw */
pm8001_ha->memoryMap.region[AAP1].num_elements = 1;
pm8001_ha->memoryMap.region[AAP1].element_size = PM8001_EVENT_LOG_SIZE;
@@ -224,92 +355,125 @@ static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
pm8001_ha->memoryMap.region[IOP].total_len = PM8001_EVENT_LOG_SIZE;
pm8001_ha->memoryMap.region[IOP].alignment = 32;
- /* MPI Memory region 3 for consumer Index of inbound queues */
- pm8001_ha->memoryMap.region[CI].num_elements = 1;
- pm8001_ha->memoryMap.region[CI].element_size = 4;
- pm8001_ha->memoryMap.region[CI].total_len = 4;
- pm8001_ha->memoryMap.region[CI].alignment = 4;
-
- /* MPI Memory region 4 for producer Index of outbound queues */
- pm8001_ha->memoryMap.region[PI].num_elements = 1;
- pm8001_ha->memoryMap.region[PI].element_size = 4;
- pm8001_ha->memoryMap.region[PI].total_len = 4;
- pm8001_ha->memoryMap.region[PI].alignment = 4;
-
- /* MPI Memory region 5 inbound queues */
- pm8001_ha->memoryMap.region[IB].num_elements = 256;
- pm8001_ha->memoryMap.region[IB].element_size = 64;
- pm8001_ha->memoryMap.region[IB].total_len = 256 * 64;
- pm8001_ha->memoryMap.region[IB].alignment = 64;
-
- /* MPI Memory region 6 inbound queues */
- pm8001_ha->memoryMap.region[OB].num_elements = 256;
- pm8001_ha->memoryMap.region[OB].element_size = 64;
- pm8001_ha->memoryMap.region[OB].total_len = 256 * 64;
- pm8001_ha->memoryMap.region[OB].alignment = 64;
+ for (i = 0; i < count; i++) {
+ ibq = &pm8001_ha->inbnd_q_tbl[i];
+ spin_lock_init(&ibq->iq_lock);
+ /* MPI Memory region 3 for consumer Index of inbound queues */
+ pm8001_ha->memoryMap.region[ci_offset+i].num_elements = 1;
+ pm8001_ha->memoryMap.region[ci_offset+i].element_size = 4;
+ pm8001_ha->memoryMap.region[ci_offset+i].total_len = 4;
+ pm8001_ha->memoryMap.region[ci_offset+i].alignment = 4;
+
+ if ((ent->driver_data) != chip_8001) {
+ /* MPI Memory region 5 inbound queues */
+ pm8001_ha->memoryMap.region[ib_offset+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[ib_offset+i].element_size
+ = 128;
+ pm8001_ha->memoryMap.region[ib_offset+i].total_len =
+ PM8001_MPI_QUEUE * 128;
+ pm8001_ha->memoryMap.region[ib_offset+i].alignment
+ = 128;
+ } else {
+ pm8001_ha->memoryMap.region[ib_offset+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[ib_offset+i].element_size
+ = 64;
+ pm8001_ha->memoryMap.region[ib_offset+i].total_len =
+ PM8001_MPI_QUEUE * 64;
+ pm8001_ha->memoryMap.region[ib_offset+i].alignment = 64;
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ obq = &pm8001_ha->outbnd_q_tbl[i];
+ spin_lock_init(&obq->oq_lock);
+ /* MPI Memory region 4 for producer Index of outbound queues */
+ pm8001_ha->memoryMap.region[pi_offset+i].num_elements = 1;
+ pm8001_ha->memoryMap.region[pi_offset+i].element_size = 4;
+ pm8001_ha->memoryMap.region[pi_offset+i].total_len = 4;
+ pm8001_ha->memoryMap.region[pi_offset+i].alignment = 4;
+
+ if (ent->driver_data != chip_8001) {
+ /* MPI Memory region 6 Outbound queues */
+ pm8001_ha->memoryMap.region[ob_offset+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[ob_offset+i].element_size
+ = 128;
+ pm8001_ha->memoryMap.region[ob_offset+i].total_len =
+ PM8001_MPI_QUEUE * 128;
+ pm8001_ha->memoryMap.region[ob_offset+i].alignment
+ = 128;
+ } else {
+ /* MPI Memory region 6 Outbound queues */
+ pm8001_ha->memoryMap.region[ob_offset+i].num_elements =
+ PM8001_MPI_QUEUE;
+ pm8001_ha->memoryMap.region[ob_offset+i].element_size
+ = 64;
+ pm8001_ha->memoryMap.region[ob_offset+i].total_len =
+ PM8001_MPI_QUEUE * 64;
+ pm8001_ha->memoryMap.region[ob_offset+i].alignment = 64;
+ }
+ }
/* Memory region write DMA*/
pm8001_ha->memoryMap.region[NVMD].num_elements = 1;
pm8001_ha->memoryMap.region[NVMD].element_size = 4096;
pm8001_ha->memoryMap.region[NVMD].total_len = 4096;
- /* Memory region for devices*/
- pm8001_ha->memoryMap.region[DEV_MEM].num_elements = 1;
- pm8001_ha->memoryMap.region[DEV_MEM].element_size = PM8001_MAX_DEVICES *
- sizeof(struct pm8001_device);
- pm8001_ha->memoryMap.region[DEV_MEM].total_len = PM8001_MAX_DEVICES *
- sizeof(struct pm8001_device);
- /* Memory region for ccb_info*/
- pm8001_ha->memoryMap.region[CCB_MEM].num_elements = 1;
- pm8001_ha->memoryMap.region[CCB_MEM].element_size = PM8001_MAX_CCB *
- sizeof(struct pm8001_ccb_info);
- pm8001_ha->memoryMap.region[CCB_MEM].total_len = PM8001_MAX_CCB *
- sizeof(struct pm8001_ccb_info);
+ /* Memory region for fw flash */
+ pm8001_ha->memoryMap.region[FW_FLASH].total_len = 4096;
+
+ pm8001_ha->memoryMap.region[FORENSIC_MEM].num_elements = 1;
+ pm8001_ha->memoryMap.region[FORENSIC_MEM].total_len = 0x10000;
+ pm8001_ha->memoryMap.region[FORENSIC_MEM].element_size = 0x10000;
+ pm8001_ha->memoryMap.region[FORENSIC_MEM].alignment = 0x10000;
+ for (i = 0; i < pm8001_ha->max_memcnt; i++) {
+ struct mpi_mem *region = &pm8001_ha->memoryMap.region[i];
- for (i = 0; i < USI_MAX_MEMCNT; i++) {
if (pm8001_mem_alloc(pm8001_ha->pdev,
- &pm8001_ha->memoryMap.region[i].virt_ptr,
- &pm8001_ha->memoryMap.region[i].phys_addr,
- &pm8001_ha->memoryMap.region[i].phys_addr_hi,
- &pm8001_ha->memoryMap.region[i].phys_addr_lo,
- pm8001_ha->memoryMap.region[i].total_len,
- pm8001_ha->memoryMap.region[i].alignment) != 0) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Mem%d alloc failed\n",
- i));
- goto err_out;
+ &region->virt_ptr,
+ &region->phys_addr,
+ &region->phys_addr_hi,
+ &region->phys_addr_lo,
+ region->total_len,
+ region->alignment) != 0) {
+ pm8001_dbg(pm8001_ha, FAIL, "Mem%d alloc failed\n", i);
+ goto err_out;
}
}
- pm8001_ha->devices = pm8001_ha->memoryMap.region[DEV_MEM].virt_ptr;
+ /* Memory region for devices*/
+ pm8001_ha->devices = kzalloc(PM8001_MAX_DEVICES
+ * sizeof(struct pm8001_device), GFP_KERNEL);
+ if (!pm8001_ha->devices) {
+ rc = -ENOMEM;
+ goto err_out_nodev;
+ }
for (i = 0; i < PM8001_MAX_DEVICES; i++) {
- pm8001_ha->devices[i].dev_type = NO_DEVICE;
- pm8001_ha->devices[i].id = i;
- pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES;
- pm8001_ha->devices[i].running_req = 0;
- }
- pm8001_ha->ccb_info = pm8001_ha->memoryMap.region[CCB_MEM].virt_ptr;
- for (i = 0; i < PM8001_MAX_CCB; i++) {
- pm8001_ha->ccb_info[i].ccb_dma_handle =
- pm8001_ha->memoryMap.region[CCB_MEM].phys_addr +
- i * sizeof(struct pm8001_ccb_info);
- pm8001_ha->ccb_info[i].task = NULL;
- pm8001_ha->ccb_info[i].ccb_tag = 0xffffffff;
- pm8001_ha->ccb_info[i].device = NULL;
- ++pm8001_ha->tags_num;
+ pm8001_ha->devices[i].dev_type = SAS_PHY_UNUSED;
}
pm8001_ha->flags = PM8001F_INIT_TIME;
- /* Initialize tags */
- pm8001_tag_init(pm8001_ha);
return 0;
+
+err_out_nodev:
+ for (i = 0; i < pm8001_ha->max_memcnt; i++) {
+ if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
+ dma_free_coherent(&pm8001_ha->pdev->dev,
+ (pm8001_ha->memoryMap.region[i].total_len +
+ pm8001_ha->memoryMap.region[i].alignment),
+ pm8001_ha->memoryMap.region[i].virt_ptr,
+ pm8001_ha->memoryMap.region[i].phys_addr);
+ }
+ }
err_out:
return 1;
}
/**
- * pm8001_ioremap - remap the pci high physical address to kernal virtual
+ * pm8001_ioremap - remap the pci high physical address to kernel virtual
* address so that we can access them.
- * @pm8001_ha:our hba structure.
+ * @pm8001_ha: our hba structure.
*/
static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
{
@@ -319,7 +483,7 @@ static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
pdev = pm8001_ha->pdev;
/* map pci mem (PMC pci base 0-3)*/
- for (bar = 0; bar < 6; bar++) {
+ for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
/*
** logical BARs for SPC:
** bar 0 and 1 - logical BAR0
@@ -333,23 +497,27 @@ static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
pm8001_ha->io_mem[logicalBar].membase =
pci_resource_start(pdev, bar);
- pm8001_ha->io_mem[logicalBar].membase &=
- (u32)PCI_BASE_ADDRESS_MEM_MASK;
pm8001_ha->io_mem[logicalBar].memsize =
pci_resource_len(pdev, bar);
pm8001_ha->io_mem[logicalBar].memvirtaddr =
ioremap(pm8001_ha->io_mem[logicalBar].membase,
pm8001_ha->io_mem[logicalBar].memsize);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("PCI: bar %d, logicalBar %d "
- "virt_addr=%lx,len=%d\n", bar, logicalBar,
- (unsigned long)
- pm8001_ha->io_mem[logicalBar].memvirtaddr,
- pm8001_ha->io_mem[logicalBar].memsize));
+ if (!pm8001_ha->io_mem[logicalBar].memvirtaddr) {
+ pm8001_dbg(pm8001_ha, INIT,
+ "Failed to ioremap bar %d, logicalBar %d",
+ bar, logicalBar);
+ return -ENOMEM;
+ }
+ pm8001_dbg(pm8001_ha, INIT,
+ "base addr %llx virt_addr=%llx len=%d\n",
+ (u64)pm8001_ha->io_mem[logicalBar].membase,
+ (u64)(unsigned long)
+ pm8001_ha->io_mem[logicalBar].memvirtaddr,
+ pm8001_ha->io_mem[logicalBar].memsize);
} else {
pm8001_ha->io_mem[logicalBar].membase = 0;
pm8001_ha->io_mem[logicalBar].memsize = 0;
- pm8001_ha->io_mem[logicalBar].memvirtaddr = 0;
+ pm8001_ha->io_mem[logicalBar].memvirtaddr = NULL;
}
logicalBar++;
}
@@ -362,34 +530,51 @@ static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
* @ent: ent
* @shost: scsi host struct which has been initialized before.
*/
-static struct pm8001_hba_info *__devinit
-pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost)
+static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
+ const struct pci_device_id *ent,
+ struct Scsi_Host *shost)
+
{
struct pm8001_hba_info *pm8001_ha;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
-
pm8001_ha = sha->lldd_ha;
if (!pm8001_ha)
return NULL;
pm8001_ha->pdev = pdev;
pm8001_ha->dev = &pdev->dev;
- pm8001_ha->chip_id = chip_id;
+ pm8001_ha->chip_id = ent->driver_data;
pm8001_ha->chip = &pm8001_chips[pm8001_ha->chip_id];
pm8001_ha->irq = pdev->irq;
pm8001_ha->sas = sha;
pm8001_ha->shost = shost;
pm8001_ha->id = pm8001_id++;
- pm8001_ha->logging_level = 0x01;
+ pm8001_ha->logging_level = logging_level;
+ pm8001_ha->non_fatal_count = 0;
+ mutex_init(&pm8001_ha->iop_log_lock);
+ if (link_rate >= 1 && link_rate <= 15)
+ pm8001_ha->link_rate = (link_rate << 8);
+ else {
+ pm8001_ha->link_rate = LINKRATE_15 | LINKRATE_30 |
+ LINKRATE_60 | LINKRATE_120;
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Setting link rate to default value\n");
+ }
sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id);
-#ifdef PM8001_USE_TASKLET
- tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
- (unsigned long)pm8001_ha);
-#endif
- pm8001_ioremap(pm8001_ha);
- if (!pm8001_alloc(pm8001_ha))
+ /* IOMB size is 128 for 8088/89 controllers */
+ if (pm8001_ha->chip_id != chip_8001)
+ pm8001_ha->iomb_size = IOMB_SIZE_SPCV;
+ else
+ pm8001_ha->iomb_size = IOMB_SIZE_SPC;
+
+ pm8001_init_tasklet(pm8001_ha);
+
+ if (pm8001_ioremap(pm8001_ha))
+ goto failed_pci_alloc;
+ if (!pm8001_alloc(pm8001_ha, ent))
return pm8001_ha;
+failed_pci_alloc:
pm8001_free(pm8001_ha);
return NULL;
}
@@ -402,30 +587,12 @@ static int pci_go_44(struct pci_dev *pdev)
{
int rc;
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(44))) {
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(44));
- if (rc) {
- rc = pci_set_consistent_dma_mask(pdev,
- DMA_BIT_MASK(32));
- if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "44-bit DMA enable failed\n");
- return rc;
- }
- }
- } else {
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc) {
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(44));
+ if (rc) {
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (rc)
dev_printk(KERN_ERR, &pdev->dev,
"32-bit DMA enable failed\n");
- return rc;
- }
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (rc) {
- dev_printk(KERN_ERR, &pdev->dev,
- "32-bit consistent DMA enable failed\n");
- return rc;
- }
}
return rc;
}
@@ -435,8 +602,8 @@ static int pci_go_44(struct pci_dev *pdev)
* @shost: scsi host which has been allocated outside.
* @chip_info: our ha struct.
*/
-static int __devinit pm8001_prep_sas_ha_init(struct Scsi_Host * shost,
- const struct pm8001_chip_info *chip_info)
+static int pm8001_prep_sas_ha_init(struct Scsi_Host *shost,
+ const struct pm8001_chip_info *chip_info)
{
int phy_nr, port_nr;
struct asd_sas_phy **arr_phy;
@@ -461,12 +628,8 @@ static int __devinit pm8001_prep_sas_ha_init(struct Scsi_Host * shost,
shost->transportt = pm8001_stt;
shost->max_id = PM8001_MAX_DEVICES;
- shost->max_lun = 8;
- shost->max_channel = 0;
shost->unique_id = pm8001_id;
shost->max_cmd_len = 16;
- shost->can_queue = PM8001_CAN_QUEUE;
- shost->cmd_per_lun = 32;
return 0;
exit_free1:
kfree(arr_port);
@@ -481,8 +644,8 @@ exit:
* @shost: scsi host which has been allocated outside
* @chip_info: our ha struct.
*/
-static void __devinit pm8001_post_sas_ha_init(struct Scsi_Host *shost,
- const struct pm8001_chip_info *chip_info)
+static void pm8001_post_sas_ha_init(struct Scsi_Host *shost,
+ const struct pm8001_chip_info *chip_info)
{
int i = 0;
struct pm8001_hba_info *pm8001_ha;
@@ -492,120 +655,450 @@ static void __devinit pm8001_post_sas_ha_init(struct Scsi_Host *shost,
for (i = 0; i < chip_info->n_phy; i++) {
sha->sas_phy[i] = &pm8001_ha->phy[i].sas_phy;
sha->sas_port[i] = &pm8001_ha->port[i].sas_port;
+ sha->sas_phy[i]->sas_addr =
+ (u8 *)&pm8001_ha->phy[i].dev_sas_addr;
}
sha->sas_ha_name = DRV_NAME;
sha->dev = pm8001_ha->dev;
-
- sha->lldd_module = THIS_MODULE;
+ sha->strict_wide_ports = 1;
sha->sas_addr = &pm8001_ha->sas_addr[0];
sha->num_phys = chip_info->n_phy;
- sha->lldd_max_execute_num = 1;
- sha->lldd_queue_size = PM8001_CAN_QUEUE;
- sha->core.shost = shost;
+ sha->shost = shost;
}
/**
* pm8001_init_sas_add - initialize sas address
- * @chip_info: our ha struct.
+ * @pm8001_ha: our ha struct.
*
- * Currently we just set the fixed SAS address to our HBA,for manufacture,
+ * Currently we just set the fixed SAS address to our HBA, for manufacture,
* it should read from the EEPROM
*/
-static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
+static int pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
+{
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct pm8001_ioctl_payload payload;
+ unsigned long time_remaining;
+ u8 sas_add[8];
+ u16 deviceid;
+ int rc;
+ u8 i, j;
+
+ if (!pm8001_read_wwn) {
+ __be64 dev_sas_addr = cpu_to_be64(0x50010c600047f9d0ULL);
+
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++)
+ memcpy(&pm8001_ha->phy[i].dev_sas_addr, &dev_sas_addr,
+ SAS_ADDR_SIZE);
+ memcpy(pm8001_ha->sas_addr, &pm8001_ha->phy[0].dev_sas_addr,
+ SAS_ADDR_SIZE);
+ return 0;
+ }
+
+ /*
+ * For new SPC controllers WWN is stored in flash vpd. For SPC/SPCve
+ * controllers WWN is stored in EEPROM. And for Older SPC WWN is stored
+ * in NVMD.
+ */
+ if (PM8001_CHIP_DISP->fatal_errors(pm8001_ha)) {
+ pm8001_dbg(pm8001_ha, FAIL, "controller is in fatal error state\n");
+ return -EIO;
+ }
+
+ pci_read_config_word(pm8001_ha->pdev, PCI_DEVICE_ID, &deviceid);
+ pm8001_ha->nvmd_completion = &completion;
+
+ if (pm8001_ha->chip_id == chip_8001) {
+ if (deviceid == 0x8081 || deviceid == 0x0042) {
+ payload.minor_function = 4;
+ payload.rd_length = 4096;
+ } else {
+ payload.minor_function = 0;
+ payload.rd_length = 128;
+ }
+ } else if ((pm8001_ha->chip_id == chip_8070 ||
+ pm8001_ha->chip_id == chip_8072) &&
+ pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ATTO) {
+ payload.minor_function = 4;
+ payload.rd_length = 4096;
+ } else {
+ payload.minor_function = 1;
+ payload.rd_length = 4096;
+ }
+ payload.offset = 0;
+ payload.func_specific = kzalloc(payload.rd_length, GFP_KERNEL);
+ if (!payload.func_specific) {
+ pm8001_dbg(pm8001_ha, FAIL, "mem alloc fail\n");
+ return -ENOMEM;
+ }
+ rc = PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload);
+ if (rc) {
+ kfree(payload.func_specific);
+ pm8001_dbg(pm8001_ha, FAIL, "nvmd failed\n");
+ return -EIO;
+ }
+ time_remaining = wait_for_completion_timeout(&completion,
+ secs_to_jiffies(60)); // 1 min
+ if (!time_remaining) {
+ kfree(payload.func_specific);
+ pm8001_dbg(pm8001_ha, FAIL, "get_nvmd_req timeout\n");
+ return -EIO;
+ }
+
+
+ for (i = 0, j = 0; i <= 7; i++, j++) {
+ if (pm8001_ha->chip_id == chip_8001) {
+ if (deviceid == 0x8081)
+ pm8001_ha->sas_addr[j] =
+ payload.func_specific[0x704 + i];
+ else if (deviceid == 0x0042)
+ pm8001_ha->sas_addr[j] =
+ payload.func_specific[0x010 + i];
+ } else if ((pm8001_ha->chip_id == chip_8070 ||
+ pm8001_ha->chip_id == chip_8072) &&
+ pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ATTO) {
+ pm8001_ha->sas_addr[j] =
+ payload.func_specific[0x010 + i];
+ } else
+ pm8001_ha->sas_addr[j] =
+ payload.func_specific[0x804 + i];
+ }
+ memcpy(sas_add, pm8001_ha->sas_addr, SAS_ADDR_SIZE);
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ if (i && ((i % 4) == 0))
+ sas_add[7] = sas_add[7] + 4;
+ memcpy(&pm8001_ha->phy[i].dev_sas_addr,
+ sas_add, SAS_ADDR_SIZE);
+ pm8001_dbg(pm8001_ha, INIT, "phy %d sas_addr = %016llx\n", i,
+ pm8001_ha->phy[i].dev_sas_addr);
+ }
+ kfree(payload.func_specific);
+
+ return 0;
+}
+
+/*
+ * pm8001_get_phy_settings_info : Read phy setting values.
+ * @pm8001_ha : our hba.
+ */
+static int pm8001_get_phy_settings_info(struct pm8001_hba_info *pm8001_ha)
{
- u8 i;
-#ifdef PM8001_READ_VPD
DECLARE_COMPLETION_ONSTACK(completion);
struct pm8001_ioctl_payload payload;
+ int rc;
+
+ if (!pm8001_read_wwn)
+ return 0;
+
pm8001_ha->nvmd_completion = &completion;
- payload.minor_function = 0;
- payload.length = 128;
- payload.func_specific = kzalloc(128, GFP_KERNEL);
- PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload);
+ /* SAS ADDRESS read from flash / EEPROM */
+ payload.minor_function = 6;
+ payload.offset = 0;
+ payload.rd_length = 4096;
+ payload.func_specific = kzalloc(4096, GFP_KERNEL);
+ if (!payload.func_specific)
+ return -ENOMEM;
+ /* Read phy setting values from flash */
+ rc = PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload);
+ if (rc) {
+ kfree(payload.func_specific);
+ pm8001_dbg(pm8001_ha, INIT, "nvmd failed\n");
+ return -ENOMEM;
+ }
wait_for_completion(&completion);
- for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
- memcpy(&pm8001_ha->phy[i].dev_sas_addr, pm8001_ha->sas_addr,
- SAS_ADDR_SIZE);
- PM8001_INIT_DBG(pm8001_ha,
- pm8001_printk("phy %d sas_addr = %016llx \n", i,
- pm8001_ha->phy[i].dev_sas_addr));
+ pm8001_set_phy_profile(pm8001_ha, sizeof(u8), payload.func_specific);
+ kfree(payload.func_specific);
+
+ return 0;
+}
+
+struct pm8001_mpi3_phy_pg_trx_config {
+ u32 LaneLosCfg;
+ u32 LanePgaCfg1;
+ u32 LanePisoCfg1;
+ u32 LanePisoCfg2;
+ u32 LanePisoCfg3;
+ u32 LanePisoCfg4;
+ u32 LanePisoCfg5;
+ u32 LanePisoCfg6;
+ u32 LaneBctCtrl;
+};
+
+/**
+ * pm8001_get_internal_phy_settings - Retrieves the internal PHY settings
+ * @pm8001_ha : our adapter
+ * @phycfg : PHY config page to populate
+ */
+static
+void pm8001_get_internal_phy_settings(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_mpi3_phy_pg_trx_config *phycfg)
+{
+ phycfg->LaneLosCfg = 0x00000132;
+ phycfg->LanePgaCfg1 = 0x00203949;
+ phycfg->LanePisoCfg1 = 0x000000FF;
+ phycfg->LanePisoCfg2 = 0xFF000001;
+ phycfg->LanePisoCfg3 = 0xE7011300;
+ phycfg->LanePisoCfg4 = 0x631C40C0;
+ phycfg->LanePisoCfg5 = 0xF8102036;
+ phycfg->LanePisoCfg6 = 0xF74A1000;
+ phycfg->LaneBctCtrl = 0x00FB33F8;
+}
+
+/**
+ * pm8001_get_external_phy_settings - Retrieves the external PHY settings
+ * @pm8001_ha : our adapter
+ * @phycfg : PHY config page to populate
+ */
+static
+void pm8001_get_external_phy_settings(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_mpi3_phy_pg_trx_config *phycfg)
+{
+ phycfg->LaneLosCfg = 0x00000132;
+ phycfg->LanePgaCfg1 = 0x00203949;
+ phycfg->LanePisoCfg1 = 0x000000FF;
+ phycfg->LanePisoCfg2 = 0xFF000001;
+ phycfg->LanePisoCfg3 = 0xE7011300;
+ phycfg->LanePisoCfg4 = 0x63349140;
+ phycfg->LanePisoCfg5 = 0xF8102036;
+ phycfg->LanePisoCfg6 = 0xF80D9300;
+ phycfg->LaneBctCtrl = 0x00FB33F8;
+}
+
+/**
+ * pm8001_get_phy_mask - Retrieves the mask that denotes if a PHY is int/ext
+ * @pm8001_ha : our adapter
+ * @phymask : The PHY mask
+ */
+static
+void pm8001_get_phy_mask(struct pm8001_hba_info *pm8001_ha, int *phymask)
+{
+ switch (pm8001_ha->pdev->subsystem_device) {
+ case 0x0070: /* H1280 - 8 external 0 internal */
+ case 0x0072: /* H12F0 - 16 external 0 internal */
+ *phymask = 0x0000;
+ break;
+
+ case 0x0071: /* H1208 - 0 external 8 internal */
+ case 0x0073: /* H120F - 0 external 16 internal */
+ *phymask = 0xFFFF;
+ break;
+
+ case 0x0080: /* H1244 - 4 external 4 internal */
+ *phymask = 0x00F0;
+ break;
+
+ case 0x0081: /* H1248 - 4 external 8 internal */
+ *phymask = 0x0FF0;
+ break;
+
+ case 0x0082: /* H1288 - 8 external 8 internal */
+ *phymask = 0xFF00;
+ break;
+
+ default:
+ pm8001_dbg(pm8001_ha, INIT,
+ "Unknown subsystem device=0x%.04x\n",
+ pm8001_ha->pdev->subsystem_device);
}
-#else
+}
+
+/**
+ * pm8001_set_phy_settings_ven_117c_12G() - Configure ATTO 12Gb PHY settings
+ * @pm8001_ha : our adapter
+ */
+static
+int pm8001_set_phy_settings_ven_117c_12G(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pm8001_mpi3_phy_pg_trx_config phycfg_int;
+ struct pm8001_mpi3_phy_pg_trx_config phycfg_ext;
+ int phymask = 0;
+ int i = 0;
+
+ memset(&phycfg_int, 0, sizeof(phycfg_int));
+ memset(&phycfg_ext, 0, sizeof(phycfg_ext));
+
+ pm8001_get_internal_phy_settings(pm8001_ha, &phycfg_int);
+ pm8001_get_external_phy_settings(pm8001_ha, &phycfg_ext);
+ pm8001_get_phy_mask(pm8001_ha, &phymask);
+
for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
- pm8001_ha->phy[i].dev_sas_addr = 0x50010c600047f9d0ULL;
- pm8001_ha->phy[i].dev_sas_addr =
- cpu_to_be64((u64)
- (*(u64 *)&pm8001_ha->phy[i].dev_sas_addr));
+ if (phymask & (1 << i)) {/* Internal PHY */
+ pm8001_set_phy_profile_single(pm8001_ha, i,
+ sizeof(phycfg_int) / sizeof(u32),
+ (u32 *)&phycfg_int);
+
+ } else { /* External PHY */
+ pm8001_set_phy_profile_single(pm8001_ha, i,
+ sizeof(phycfg_ext) / sizeof(u32),
+ (u32 *)&phycfg_ext);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * pm8001_configure_phy_settings - Configures PHY settings based on vendor ID.
+ * @pm8001_ha : our hba.
+ */
+static int pm8001_configure_phy_settings(struct pm8001_hba_info *pm8001_ha)
+{
+ switch (pm8001_ha->pdev->subsystem_vendor) {
+ case PCI_VENDOR_ID_ATTO:
+ if (pm8001_ha->pdev->device == 0x0042) /* 6Gb */
+ return 0;
+ else
+ return pm8001_set_phy_settings_ven_117c_12G(pm8001_ha);
+
+ case PCI_VENDOR_ID_ADAPTEC2:
+ case 0:
+ return 0;
+
+ default:
+ return pm8001_get_phy_settings_info(pm8001_ha);
}
- memcpy(pm8001_ha->sas_addr, &pm8001_ha->phy[0].dev_sas_addr,
- SAS_ADDR_SIZE);
-#endif
}
-#ifdef PM8001_USE_MSIX
/**
* pm8001_setup_msix - enable MSI-X interrupt
- * @chip_info: our ha struct.
- * @irq_handler: irq_handler
+ * @pm8001_ha: our ha struct.
*/
-static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha,
- irq_handler_t irq_handler)
+static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha)
{
- u32 i = 0, j = 0;
- u32 number_of_intr = 1;
- int flag = 0;
- u32 max_entry;
+ unsigned int allocated_irq_vectors;
int rc;
- max_entry = sizeof(pm8001_ha->msix_entries) /
- sizeof(pm8001_ha->msix_entries[0]);
- flag |= IRQF_DISABLED;
- for (i = 0; i < max_entry ; i++)
- pm8001_ha->msix_entries[i].entry = i;
- rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries,
- number_of_intr);
- pm8001_ha->number_of_intr = number_of_intr;
- if (!rc) {
- for (i = 0; i < number_of_intr; i++) {
- if (request_irq(pm8001_ha->msix_entries[i].vector,
- irq_handler, flag, DRV_NAME,
- SHOST_TO_SAS_HA(pm8001_ha->shost))) {
- for (j = 0; j < i; j++)
- free_irq(
- pm8001_ha->msix_entries[j].vector,
- SHOST_TO_SAS_HA(pm8001_ha->shost));
- pci_disable_msix(pm8001_ha->pdev);
- break;
+
+ /* SPCv controllers supports 64 msi-x */
+ if (pm8001_ha->chip_id == chip_8001) {
+ rc = pci_alloc_irq_vectors(pm8001_ha->pdev, 1, 1,
+ PCI_IRQ_MSIX);
+ } else {
+ /*
+ * Queue index #0 is used always for housekeeping, so don't
+ * include in the affinity spreading.
+ */
+ struct irq_affinity desc = {
+ .pre_vectors = 1,
+ };
+ rc = pci_alloc_irq_vectors_affinity(
+ pm8001_ha->pdev, 2, PM8001_MAX_MSIX_VEC,
+ PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, &desc);
+ }
+
+ allocated_irq_vectors = rc;
+ if (rc < 0)
+ return rc;
+
+ /* Assigns the number of interrupts */
+ pm8001_ha->number_of_intr = allocated_irq_vectors;
+
+ /* Maximum queue number updating in HBA structure */
+ pm8001_ha->max_q_num = allocated_irq_vectors;
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "pci_alloc_irq_vectors request ret:%d no of intr %d\n",
+ rc, pm8001_ha->number_of_intr);
+ return 0;
+}
+
+static u32 pm8001_request_msix(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 i = 0, j = 0;
+ int flag = 0, rc = 0;
+ int nr_irqs = pm8001_ha->number_of_intr;
+
+ if (pm8001_ha->chip_id != chip_8001)
+ flag &= ~IRQF_SHARED;
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "pci_enable_msix request number of intr %d\n",
+ pm8001_ha->number_of_intr);
+
+ if (nr_irqs > ARRAY_SIZE(pm8001_ha->intr_drvname))
+ nr_irqs = ARRAY_SIZE(pm8001_ha->intr_drvname);
+
+ for (i = 0; i < nr_irqs; i++) {
+ snprintf(pm8001_ha->intr_drvname[i],
+ sizeof(pm8001_ha->intr_drvname[0]),
+ "%s-%d", pm8001_ha->name, i);
+ pm8001_ha->irq_vector[i].irq_id = i;
+ pm8001_ha->irq_vector[i].drv_inst = pm8001_ha;
+
+ rc = request_irq(pci_irq_vector(pm8001_ha->pdev, i),
+ pm8001_interrupt_handler_msix, flag,
+ pm8001_ha->intr_drvname[i],
+ &(pm8001_ha->irq_vector[i]));
+ if (rc) {
+ for (j = 0; j < i; j++) {
+ free_irq(pci_irq_vector(pm8001_ha->pdev, i),
+ &(pm8001_ha->irq_vector[i]));
}
+ pci_free_irq_vectors(pm8001_ha->pdev);
+ break;
}
}
+
return rc;
}
-#endif
/**
* pm8001_request_irq - register interrupt
- * @chip_info: our ha struct.
+ * @pm8001_ha: our ha struct.
*/
static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha)
{
- struct pci_dev *pdev;
- irq_handler_t irq_handler = pm8001_interrupt;
+ struct pci_dev *pdev = pm8001_ha->pdev;
int rc;
- pdev = pm8001_ha->pdev;
+ if (pm8001_use_msix && pci_find_capability(pdev, PCI_CAP_ID_MSIX)) {
+ rc = pm8001_setup_msix(pm8001_ha);
+ if (rc) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "pm8001_setup_irq failed [ret: %d]\n", rc);
+ return rc;
+ }
-#ifdef PM8001_USE_MSIX
- if (pci_find_capability(pdev, PCI_CAP_ID_MSIX))
- return pm8001_setup_msix(pm8001_ha, irq_handler);
- else
- goto intx;
-#endif
+ if (!pdev->msix_cap || !pci_msi_enabled())
+ goto use_intx;
-intx:
- /* initialize the INT-X interrupt */
- rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME,
- SHOST_TO_SAS_HA(pm8001_ha->shost));
- return rc;
+ rc = pm8001_request_msix(pm8001_ha);
+ if (rc)
+ return rc;
+
+ pm8001_ha->use_msix = true;
+
+ return 0;
+ }
+
+use_intx:
+ /* Initialize the INT-X interrupt */
+ pm8001_dbg(pm8001_ha, INIT, "MSIX not supported!!!\n");
+ pm8001_ha->use_msix = false;
+ pm8001_ha->irq_vector[0].irq_id = 0;
+ pm8001_ha->irq_vector[0].drv_inst = pm8001_ha;
+
+ return request_irq(pdev->irq, pm8001_interrupt_handler_intx,
+ IRQF_SHARED, pm8001_ha->name,
+ SHOST_TO_SAS_HA(pm8001_ha->shost));
+}
+
+static void pm8001_free_irq(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pci_dev *pdev = pm8001_ha->pdev;
+ int i;
+
+ if (pm8001_ha->use_msix) {
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ synchronize_irq(pci_irq_vector(pdev, i));
+
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ free_irq(pci_irq_vector(pdev, i), &pm8001_ha->irq_vector[i]);
+
+ pci_free_irq_vectors(pdev);
+ return;
+ }
+
+ /* INT-X */
+ free_irq(pm8001_ha->irq, pm8001_ha->sas);
}
/**
@@ -614,20 +1107,22 @@ intx:
* @ent: pci device id
*
* This function is the main initialization function, when register a new
- * pci driver it is invoked, all struct an hardware initilization should be done
- * here, also, register interrupt
+ * pci driver it is invoked, all struct and hardware initialization should be
+ * done here, also, register interrupt.
*/
-static int __devinit pm8001_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static int pm8001_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
unsigned int rc;
u32 pci_reg;
+ u8 i = 0;
struct pm8001_hba_info *pm8001_ha;
struct Scsi_Host *shost = NULL;
const struct pm8001_chip_info *chip;
+ struct sas_ha_struct *sha;
dev_printk(KERN_INFO, &pdev->dev,
- "pm8001: driver version %s\n", DRV_VERSION);
+ "pm80xx: driver version %s\n", DRV_VERSION);
rc = pci_enable_device(pdev);
if (rc)
goto err_out_enable;
@@ -653,12 +1148,12 @@ static int __devinit pm8001_pci_probe(struct pci_dev *pdev,
goto err_out_regions;
}
chip = &pm8001_chips[ent->driver_data];
- SHOST_TO_SAS_HA(shost) =
- kzalloc(sizeof(struct sas_ha_struct), GFP_KERNEL);
- if (!SHOST_TO_SAS_HA(shost)) {
+ sha = kzalloc(sizeof(struct sas_ha_struct), GFP_KERNEL);
+ if (!sha) {
rc = -ENOMEM;
goto err_out_free_host;
}
+ SHOST_TO_SAS_HA(shost) = sha;
rc = pm8001_prep_sas_ha_init(shost, chip);
if (rc) {
@@ -666,30 +1161,67 @@ static int __devinit pm8001_pci_probe(struct pci_dev *pdev,
goto err_out_free;
}
pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost));
- pm8001_ha = pm8001_pci_alloc(pdev, chip_8001, shost);
+ /* ent->driver variable is used to differentiate between controllers */
+ pm8001_ha = pm8001_pci_alloc(pdev, ent, shost);
if (!pm8001_ha) {
rc = -ENOMEM;
goto err_out_free;
}
- list_add_tail(&pm8001_ha->list, &hba_list);
- PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
- if (rc)
+ if (rc) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "chip_init failed [ret: %d]\n", rc);
goto err_out_ha_free;
+ }
+
+ rc = pm8001_init_ccb_tag(pm8001_ha);
+ if (rc)
+ goto err_out_enable;
+
+
+ PM8001_CHIP_DISP->chip_post_init(pm8001_ha);
+
+ if (pm8001_ha->number_of_intr > 1) {
+ shost->nr_hw_queues = pm8001_ha->number_of_intr - 1;
+ /*
+ * For now, ensure we're not sent too many commands by setting
+ * host_tagset. This is also required if we start using request
+ * tag.
+ */
+ shost->host_tagset = 1;
+ }
rc = scsi_add_host(shost, &pdev->dev);
if (rc)
goto err_out_ha_free;
- rc = pm8001_request_irq(pm8001_ha);
+
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0);
+ if (pm8001_ha->chip_id != chip_8001) {
+ for (i = 1; i < pm8001_ha->number_of_intr; i++)
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i);
+ /* setup thermal configuration. */
+ pm80xx_set_thermal_config(pm8001_ha);
+ }
+
+ rc = pm8001_init_sas_add(pm8001_ha);
+ if (rc)
+ goto err_out_shost;
+ /* phy setting support for motherboard controller */
+ rc = pm8001_configure_phy_settings(pm8001_ha);
if (rc)
goto err_out_shost;
- PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
- pm8001_init_sas_add(pm8001_ha);
pm8001_post_sas_ha_init(shost, chip);
rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
- if (rc)
+ if (rc) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "sas_register_ha failed [ret: %d]\n", rc);
goto err_out_shost;
+ }
+ list_add_tail(&pm8001_ha->list, &hba_list);
+ pm8001_ha->flags = PM8001F_RUN_TIME;
scsi_scan_host(pm8001_ha->shost);
return 0;
@@ -698,9 +1230,9 @@ err_out_shost:
err_out_ha_free:
pm8001_free(pm8001_ha);
err_out_free:
- kfree(SHOST_TO_SAS_HA(shost));
+ kfree(sha);
err_out_free_host:
- kfree(shost);
+ scsi_host_put(shost);
err_out_regions:
pci_release_regions(pdev);
err_out_disable:
@@ -709,32 +1241,83 @@ err_out_enable:
return rc;
}
-static void __devexit pm8001_pci_remove(struct pci_dev *pdev)
+/**
+ * pm8001_init_ccb_tag - allocate memory to CCB and tag.
+ * @pm8001_ha: our hba card information.
+ */
+static int pm8001_init_ccb_tag(struct pm8001_hba_info *pm8001_ha)
+{
+ struct Scsi_Host *shost = pm8001_ha->shost;
+ struct device *dev = pm8001_ha->dev;
+ u32 max_out_io, ccb_count;
+ int i;
+
+ max_out_io = pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_out_io;
+ ccb_count = min_t(int, PM8001_MAX_CCB, max_out_io);
+
+ shost->can_queue = ccb_count - PM8001_RESERVE_SLOT;
+
+ pm8001_ha->rsvd_tags = bitmap_zalloc(PM8001_RESERVE_SLOT, GFP_KERNEL);
+ if (!pm8001_ha->rsvd_tags)
+ goto err_out;
+
+ /* Memory region for ccb_info*/
+ pm8001_ha->ccb_count = ccb_count;
+ pm8001_ha->ccb_info =
+ kcalloc(ccb_count, sizeof(struct pm8001_ccb_info), GFP_KERNEL);
+ if (!pm8001_ha->ccb_info) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Unable to allocate memory for ccb\n");
+ goto err_out_noccb;
+ }
+ for (i = 0; i < ccb_count; i++) {
+ pm8001_ha->ccb_info[i].buf_prd = dma_alloc_coherent(dev,
+ sizeof(struct pm8001_prd) * PM8001_MAX_DMA_SG,
+ &pm8001_ha->ccb_info[i].ccb_dma_handle,
+ GFP_KERNEL);
+ if (!pm8001_ha->ccb_info[i].buf_prd) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "ccb prd memory allocation error\n");
+ goto err_out;
+ }
+ pm8001_ha->ccb_info[i].task = NULL;
+ pm8001_ha->ccb_info[i].ccb_tag = PM8001_INVALID_TAG;
+ pm8001_ha->ccb_info[i].device = NULL;
+ }
+
+ return 0;
+
+err_out_noccb:
+ kfree(pm8001_ha->devices);
+err_out:
+ return -ENOMEM;
+}
+
+static void pm8001_pci_remove(struct pci_dev *pdev)
{
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
- struct pm8001_hba_info *pm8001_ha;
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
int i;
- pm8001_ha = sha->lldd_ha;
- pci_set_drvdata(pdev, NULL);
+
sas_unregister_ha(sha);
sas_remove_host(pm8001_ha->shost);
list_del(&pm8001_ha->list);
- scsi_remove_host(pm8001_ha->shost);
- PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
- PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
-
-#ifdef PM8001_USE_MSIX
- for (i = 0; i < pm8001_ha->number_of_intr; i++)
- synchronize_irq(pm8001_ha->msix_entries[i].vector);
- for (i = 0; i < pm8001_ha->number_of_intr; i++)
- free_irq(pm8001_ha->msix_entries[i].vector, sha);
- pci_disable_msix(pdev);
-#else
- free_irq(pm8001_ha->irq, sha);
-#endif
-#ifdef PM8001_USE_TASKLET
- tasklet_kill(&pm8001_ha->tasklet);
-#endif
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
+
+ pm8001_free_irq(pm8001_ha);
+ pm8001_kill_tasklet(pm8001_ha);
+ scsi_host_put(pm8001_ha->shost);
+
+ for (i = 0; i < pm8001_ha->ccb_count; i++) {
+ dma_free_coherent(&pm8001_ha->pdev->dev,
+ sizeof(struct pm8001_prd) * PM8001_MAX_DMA_SG,
+ pm8001_ha->ccb_info[i].buf_prd,
+ pm8001_ha->ccb_info[i].ccb_dma_handle);
+ }
+ kfree(pm8001_ha->ccb_info);
+ kfree(pm8001_ha->devices);
+
pm8001_free(pm8001_ha);
kfree(sha->sas_phy);
kfree(sha->sas_port);
@@ -745,123 +1328,200 @@ static void __devexit pm8001_pci_remove(struct pci_dev *pdev)
/**
* pm8001_pci_suspend - power management suspend main entry point
- * @pdev: PCI device struct
- * @state: PM state change to (usually PCI_D3)
+ * @dev: Device struct
*
- * Returns 0 success, anything else error.
+ * Return: 0 on success, anything else on error.
*/
-static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused pm8001_pci_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
- struct pm8001_hba_info *pm8001_ha;
- int i , pos;
- u32 device_state;
- pm8001_ha = sha->lldd_ha;
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ sas_suspend_ha(sha);
flush_workqueue(pm8001_wq);
scsi_block_requests(pm8001_ha->shost);
- pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pos == 0) {
- printk(KERN_ERR " PCI PM not supported\n");
+ if (!pdev->pm_cap) {
+ dev_err(dev, " PCI PM not supported\n");
return -ENODEV;
}
- PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
- PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
-#ifdef PM8001_USE_MSIX
- for (i = 0; i < pm8001_ha->number_of_intr; i++)
- synchronize_irq(pm8001_ha->msix_entries[i].vector);
- for (i = 0; i < pm8001_ha->number_of_intr; i++)
- free_irq(pm8001_ha->msix_entries[i].vector, sha);
- pci_disable_msix(pdev);
-#else
- free_irq(pm8001_ha->irq, sha);
-#endif
-#ifdef PM8001_USE_TASKLET
- tasklet_kill(&pm8001_ha->tasklet);
-#endif
- device_state = pci_choose_state(pdev, state);
- pm8001_printk("pdev=0x%p, slot=%s, entering "
- "operating state [D%d]\n", pdev,
- pm8001_ha->name, device_state);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, device_state);
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
+
+ pm8001_free_irq(pm8001_ha);
+ pm8001_kill_tasklet(pm8001_ha);
+
+ pm8001_info(pm8001_ha, "pdev=0x%p, slot=%s, entering "
+ "suspended state\n", pdev,
+ pm8001_ha->name);
return 0;
}
/**
* pm8001_pci_resume - power management resume main entry point
- * @pdev: PCI device struct
+ * @dev: Device struct
*
- * Returns 0 success, anything else error.
+ * Return: 0 on success, anything else on error.
*/
-static int pm8001_pci_resume(struct pci_dev *pdev)
+static int __maybe_unused pm8001_pci_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha;
int rc;
- u32 device_state;
- pm8001_ha = sha->lldd_ha;
- device_state = pdev->current_state;
+ u8 i = 0;
+ DECLARE_COMPLETION_ONSTACK(completion);
- pm8001_printk("pdev=0x%p, slot=%s, resuming from previous "
- "operating state [D%d]\n", pdev, pm8001_ha->name, device_state);
+ pm8001_ha = sha->lldd_ha;
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
- rc = pci_enable_device(pdev);
- if (rc) {
- pm8001_printk("slot=%s Enable device failed during resume\n",
- pm8001_ha->name);
- goto err_out_enable;
- }
+ pm8001_info(pm8001_ha,
+ "pdev=0x%p, slot=%s, resuming from previous operating state [D%d]\n",
+ pdev, pm8001_ha->name, pdev->current_state);
- pci_set_master(pdev);
rc = pci_go_44(pdev);
if (rc)
goto err_out_disable;
-
- PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+ sas_prep_resume_ha(sha);
+ /* chip soft rst only for spc */
+ if (pm8001_ha->chip_id == chip_8001) {
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha);
+ pm8001_dbg(pm8001_ha, INIT, "chip soft reset successful\n");
+ }
rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
if (rc)
goto err_out_disable;
- PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+
+ /* disable all the interrupt bits */
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
+
rc = pm8001_request_irq(pm8001_ha);
if (rc)
goto err_out_disable;
- #ifdef PM8001_USE_TASKLET
- tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
- (unsigned long)pm8001_ha);
- #endif
- PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
- scsi_unblock_requests(pm8001_ha->shost);
+
+ pm8001_init_tasklet(pm8001_ha);
+
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0);
+ if (pm8001_ha->chip_id != chip_8001) {
+ for (i = 1; i < pm8001_ha->number_of_intr; i++)
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i);
+ }
+
+ /* Chip documentation for the 8070 and 8072 SPCv */
+ /* states that a 500ms minimum delay is required */
+ /* before issuing commands. Otherwise, the firmware */
+ /* will enter an unrecoverable state. */
+
+ if (pm8001_ha->chip_id == chip_8070 ||
+ pm8001_ha->chip_id == chip_8072) {
+ mdelay(500);
+ }
+
+ /* Spin up the PHYs */
+
+ pm8001_ha->flags = PM8001F_RUN_TIME;
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ pm8001_ha->phy[i].enable_completion = &completion;
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i);
+ wait_for_completion(&completion);
+ }
+ sas_resume_ha(sha);
return 0;
err_out_disable:
scsi_remove_host(pm8001_ha->shost);
- pci_disable_device(pdev);
-err_out_enable:
+
return rc;
}
-static struct pci_device_id __devinitdata pm8001_pci_table[] = {
- {
- PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001
- },
- {
- PCI_DEVICE(0x117c, 0x0042),
- .driver_data = chip_8001
- },
+/* update of pci device, vendor id and driver data with
+ * unique value for each of the controller
+ */
+static const struct pci_device_id pm8001_pci_table[] = {
+ { PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8006), chip_8006 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8006), chip_8006 },
+ { PCI_VDEVICE(ATTO, 0x0042), chip_8001 },
+ /* Support for SPC/SPCv/SPCve controllers */
+ { PCI_VDEVICE(ADAPTEC2, 0x8001), chip_8001 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8008), chip_8008 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8008), chip_8008 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8018), chip_8018 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8018), chip_8018 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8009), chip_8009 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8009), chip_8009 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8019), chip_8019 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8019), chip_8019 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8074), chip_8074 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8074), chip_8074 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8076), chip_8076 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8076), chip_8076 },
+ { PCI_VDEVICE(PMC_Sierra, 0x8077), chip_8077 },
+ { PCI_VDEVICE(ADAPTEC2, 0x8077), chip_8077 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8081,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0400, 0, 0, chip_8001 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8081,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0800, 0, 0, chip_8001 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8088,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0008, 0, 0, chip_8008 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8088,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0800, 0, 0, chip_8008 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8089,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0008, 0, 0, chip_8009 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8089,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0800, 0, 0, chip_8009 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8088,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0016, 0, 0, chip_8018 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8088,
+ PCI_VENDOR_ID_ADAPTEC2, 0x1600, 0, 0, chip_8018 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8089,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0016, 0, 0, chip_8019 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8089,
+ PCI_VENDOR_ID_ADAPTEC2, 0x1600, 0, 0, chip_8019 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8074,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0800, 0, 0, chip_8074 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8076,
+ PCI_VENDOR_ID_ADAPTEC2, 0x1600, 0, 0, chip_8076 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8077,
+ PCI_VENDOR_ID_ADAPTEC2, 0x1600, 0, 0, chip_8077 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8074,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0008, 0, 0, chip_8074 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8076,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0016, 0, 0, chip_8076 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8077,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0016, 0, 0, chip_8077 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8076,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0808, 0, 0, chip_8076 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8077,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0808, 0, 0, chip_8077 },
+ { PCI_VENDOR_ID_ADAPTEC2, 0x8074,
+ PCI_VENDOR_ID_ADAPTEC2, 0x0404, 0, 0, chip_8074 },
+ { PCI_VENDOR_ID_ATTO, 0x8070,
+ PCI_VENDOR_ID_ATTO, 0x0070, 0, 0, chip_8070 },
+ { PCI_VENDOR_ID_ATTO, 0x8070,
+ PCI_VENDOR_ID_ATTO, 0x0071, 0, 0, chip_8070 },
+ { PCI_VENDOR_ID_ATTO, 0x8072,
+ PCI_VENDOR_ID_ATTO, 0x0072, 0, 0, chip_8072 },
+ { PCI_VENDOR_ID_ATTO, 0x8072,
+ PCI_VENDOR_ID_ATTO, 0x0073, 0, 0, chip_8072 },
+ { PCI_VENDOR_ID_ATTO, 0x8070,
+ PCI_VENDOR_ID_ATTO, 0x0080, 0, 0, chip_8070 },
+ { PCI_VENDOR_ID_ATTO, 0x8072,
+ PCI_VENDOR_ID_ATTO, 0x0081, 0, 0, chip_8072 },
+ { PCI_VENDOR_ID_ATTO, 0x8072,
+ PCI_VENDOR_ID_ATTO, 0x0082, 0, 0, chip_8072 },
{} /* terminate list */
};
+static SIMPLE_DEV_PM_OPS(pm8001_pci_pm_ops,
+ pm8001_pci_suspend,
+ pm8001_pci_resume);
+
static struct pci_driver pm8001_pci_driver = {
.name = DRV_NAME,
.id_table = pm8001_pci_table,
.probe = pm8001_pci_probe,
- .remove = __devexit_p(pm8001_pci_remove),
- .suspend = pm8001_pci_suspend,
- .resume = pm8001_pci_resume,
+ .remove = pm8001_pci_remove,
+ .driver.pm = &pm8001_pci_pm_ops,
};
/**
@@ -871,7 +1531,10 @@ static int __init pm8001_init(void)
{
int rc = -ENOMEM;
- pm8001_wq = alloc_workqueue("pm8001", 0, 0);
+ if (pm8001_use_tasklet && !pm8001_use_msix)
+ pm8001_use_tasklet = false;
+
+ pm8001_wq = alloc_workqueue("pm80xx", WQ_PERCPU, 0);
if (!pm8001_wq)
goto err;
@@ -903,7 +1566,12 @@ module_init(pm8001_init);
module_exit(pm8001_exit);
MODULE_AUTHOR("Jack Wang <jack_wang@usish.com>");
-MODULE_DESCRIPTION("PMC-Sierra PM8001 SAS/SATA controller driver");
+MODULE_AUTHOR("Anand Kumar Santhanam <AnandKumar.Santhanam@pmcs.com>");
+MODULE_AUTHOR("Sangeetha Gnanasekaran <Sangeetha.Gnanasekaran@pmcs.com>");
+MODULE_AUTHOR("Nikith Ganigarakoppal <Nikith.Ganigarakoppal@pmcs.com>");
+MODULE_DESCRIPTION(
+ "PMC-Sierra PM8001/8006/8081/8088/8089/8074/8076/8077/8070/8072 "
+ "SAS/SATA controller driver");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, pm8001_pci_table);
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c
index 6ae059ebb4bb..6a8d35aea93a 100644
--- a/drivers/scsi/pm8001/pm8001_sas.c
+++ b/drivers/scsi/pm8001/pm8001_sas.c
@@ -1,5 +1,5 @@
/*
- * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ * PMC-Sierra PM8001/8081/8088/8089 SAS/SATA based host adapters driver
*
* Copyright (c) 2008-2009 USI Co., Ltd.
* All rights reserved.
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include "pm8001_sas.h"
+#include "pm80xx_tracepoints.h"
/**
* pm8001_find_tag - from sas task to find out tag that belongs to this task
@@ -58,25 +59,21 @@ static int pm8001_find_tag(struct sas_task *task, u32 *tag)
}
/**
- * pm8001_tag_clear - clear the tags bitmap
+ * pm8001_tag_free - free the no more needed tag
* @pm8001_ha: our hba struct
* @tag: the found tag associated with the task
*/
-static void pm8001_tag_clear(struct pm8001_hba_info *pm8001_ha, u32 tag)
+void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
{
- void *bitmap = pm8001_ha->tags;
- clear_bit(tag, bitmap);
-}
+ void *bitmap = pm8001_ha->rsvd_tags;
+ unsigned long flags;
-static void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
-{
- pm8001_tag_clear(pm8001_ha, tag);
-}
+ if (tag >= PM8001_RESERVE_SLOT)
+ return;
-static void pm8001_tag_set(struct pm8001_hba_info *pm8001_ha, u32 tag)
-{
- void *bitmap = pm8001_ha->tags;
- set_bit(tag, bitmap);
+ spin_lock_irqsave(&pm8001_ha->bitmap_lock, flags);
+ __clear_bit(tag, bitmap);
+ spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags);
}
/**
@@ -84,35 +81,103 @@ static void pm8001_tag_set(struct pm8001_hba_info *pm8001_ha, u32 tag)
* @pm8001_ha: our hba struct
* @tag_out: the found empty tag .
*/
-inline int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out)
+int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out)
{
- unsigned int index, tag;
- void *bitmap = pm8001_ha->tags;
+ void *bitmap = pm8001_ha->rsvd_tags;
+ unsigned long flags;
+ unsigned int tag;
- index = find_first_zero_bit(bitmap, pm8001_ha->tags_num);
- tag = index;
- if (tag >= pm8001_ha->tags_num)
+ spin_lock_irqsave(&pm8001_ha->bitmap_lock, flags);
+ tag = find_first_zero_bit(bitmap, PM8001_RESERVE_SLOT);
+ if (tag >= PM8001_RESERVE_SLOT) {
+ spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags);
return -SAS_QUEUE_FULL;
- pm8001_tag_set(pm8001_ha, tag);
+ }
+ __set_bit(tag, bitmap);
+ spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags);
+
+ /* reserved tags are in the lower region of the tagset */
*tag_out = tag;
return 0;
}
-void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha)
+static void pm80xx_get_tag_opcodes(struct sas_task *task, int *ata_op,
+ int *ata_tag, bool *task_aborted)
{
- int i;
- for (i = 0; i < pm8001_ha->tags_num; ++i)
- pm8001_tag_clear(pm8001_ha, i);
+ unsigned long flags;
+ struct ata_queued_cmd *qc = NULL;
+
+ *ata_op = 0;
+ *ata_tag = -1;
+ *task_aborted = false;
+
+ if (!task)
+ return;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED)))
+ *task_aborted = true;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ if (task->task_proto == SAS_PROTOCOL_STP) {
+ // sas_ata_qc_issue path uses SAS_PROTOCOL_STP.
+ // This only works for scsi + libsas + libata users.
+ qc = task->uldd_task;
+ if (qc) {
+ *ata_op = qc->tf.command;
+ *ata_tag = qc->tag;
+ }
+ }
}
- /**
- * pm8001_mem_alloc - allocate memory for pm8001.
- * @pdev: pci device.
- * @virt_addr: the allocated virtual address
- * @pphys_addr_hi: the physical address high byte address.
- * @pphys_addr_lo: the physical address low byte address.
- * @mem_size: memory size.
- */
+u32 pm80xx_get_local_phy_id(struct domain_device *dev)
+{
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+
+ if (dev_parent_is_expander(dev))
+ return dev->parent->ex_dev.ex_phy->phy_id;
+
+ return pm8001_dev->attached_phy;
+}
+
+void pm80xx_show_pending_commands(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *target_pm8001_dev)
+{
+ int i = 0, ata_op = 0, ata_tag = -1;
+ struct pm8001_ccb_info *ccb = NULL;
+ struct sas_task *task = NULL;
+ struct pm8001_device *pm8001_dev = NULL;
+ bool task_aborted;
+
+ for (i = 0; i < pm8001_ha->ccb_count; i++) {
+ ccb = &pm8001_ha->ccb_info[i];
+ if (ccb->ccb_tag == PM8001_INVALID_TAG)
+ continue;
+ pm8001_dev = ccb->device;
+ if (target_pm8001_dev && pm8001_dev &&
+ target_pm8001_dev != pm8001_dev)
+ continue;
+ task = ccb->task;
+ pm80xx_get_tag_opcodes(task, &ata_op, &ata_tag, &task_aborted);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "tag %#x, device %#x task %p task aborted %d ata opcode %#x ata tag %d\n",
+ ccb->ccb_tag,
+ (pm8001_dev ? pm8001_dev->device_id : 0),
+ task, task_aborted,
+ ata_op, ata_tag);
+ }
+}
+
+/**
+ * pm8001_mem_alloc - allocate memory for pm8001.
+ * @pdev: pci device.
+ * @virt_addr: the allocated virtual address
+ * @pphys_addr: DMA address for this device
+ * @pphys_addr_hi: the physical address high byte address.
+ * @pphys_addr_lo: the physical address low byte address.
+ * @mem_size: memory size.
+ * @align: requested byte alignment
+ */
int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
dma_addr_t *pphys_addr, u32 *pphys_addr_hi,
u32 *pphys_addr_lo, u32 mem_size, u32 align)
@@ -123,13 +188,10 @@ int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
u64 align_offset = 0;
if (align)
align_offset = (dma_addr_t)align - 1;
- mem_virt_alloc =
- pci_alloc_consistent(pdev, mem_size + align, &mem_dma_handle);
- if (!mem_virt_alloc) {
- pm8001_printk("memory allocation error\n");
- return -1;
- }
- memset((void *)mem_virt_alloc, 0, mem_size+align);
+ mem_virt_alloc = dma_alloc_coherent(&pdev->dev, mem_size + align,
+ &mem_dma_handle, GFP_KERNEL);
+ if (!mem_virt_alloc)
+ return -ENOMEM;
*pphys_addr = mem_dma_handle;
phys_align = (*pphys_addr + align_offset) & ~align_offset;
*virt_addr = (void *)mem_virt_alloc + phys_align - *pphys_addr;
@@ -137,6 +199,7 @@ int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
*pphys_addr_lo = lower_32_bits(phys_align);
return 0;
}
+
/**
* pm8001_find_ha_by_dev - from domain device which come from sas layer to
* find out our hba struct.
@@ -165,9 +228,22 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
int rc = 0, phy_id = sas_phy->id;
struct pm8001_hba_info *pm8001_ha = NULL;
struct sas_phy_linkrates *rates;
+ struct pm8001_phy *phy;
DECLARE_COMPLETION_ONSTACK(completion);
+ unsigned long flags;
pm8001_ha = sas_phy->ha->lldd_ha;
- pm8001_ha->phy[phy_id].enable_completion = &completion;
+ phy = &pm8001_ha->phy[phy_id];
+
+ if (PM8001_CHIP_DISP->fatal_errors(pm8001_ha)) {
+ /*
+ * If the controller is in fatal error state,
+ * we will not get a response from the controller
+ */
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Phy control failed due to fatal errors\n");
+ return -EFAULT;
+ }
+
switch (func) {
case PHY_FUNC_SET_LINK_RATE:
rates = funcdata;
@@ -179,7 +255,8 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
pm8001_ha->phy[phy_id].maximum_linkrate =
rates->maximum_linkrate;
}
- if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ if (pm8001_ha->phy[phy_id].phy_state == PHY_LINK_DISABLE) {
+ pm8001_ha->phy[phy_id].enable_completion = &completion;
PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
wait_for_completion(&completion);
}
@@ -187,7 +264,8 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
PHY_LINK_RESET);
break;
case PHY_FUNC_HARD_RESET:
- if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ if (pm8001_ha->phy[phy_id].phy_state == PHY_LINK_DISABLE) {
+ pm8001_ha->phy[phy_id].enable_completion = &completion;
PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
wait_for_completion(&completion);
}
@@ -195,7 +273,8 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
PHY_HARD_RESET);
break;
case PHY_FUNC_LINK_RESET:
- if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ if (pm8001_ha->phy[phy_id].phy_state == PHY_LINK_DISABLE) {
+ pm8001_ha->phy[phy_id].enable_completion = &completion;
PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
wait_for_completion(&completion);
}
@@ -207,29 +286,56 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
PHY_LINK_RESET);
break;
case PHY_FUNC_DISABLE:
+ if (pm8001_ha->chip_id != chip_8001) {
+ if (pm8001_ha->phy[phy_id].phy_state ==
+ PHY_STATE_LINK_UP_SPCV) {
+ sas_phy_disconnected(&phy->sas_phy);
+ sas_notify_phy_event(&phy->sas_phy,
+ PHYE_LOSS_OF_SIGNAL, GFP_KERNEL);
+ phy->phy_attached = 0;
+ }
+ } else {
+ if (pm8001_ha->phy[phy_id].phy_state ==
+ PHY_STATE_LINK_UP_SPC) {
+ sas_phy_disconnected(&phy->sas_phy);
+ sas_notify_phy_event(&phy->sas_phy,
+ PHYE_LOSS_OF_SIGNAL, GFP_KERNEL);
+ phy->phy_attached = 0;
+ }
+ }
PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, phy_id);
break;
+ case PHY_FUNC_GET_EVENTS:
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (pm8001_ha->chip_id == chip_8001) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ (phy_id < 4) ? 0x30000 : 0x40000)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return -EINVAL;
+ }
+ }
+ {
+ struct sas_phy *phy = sas_phy->phy;
+ u32 __iomem *qp = pm8001_ha->io_mem[2].memvirtaddr
+ + 0x1034 + (0x4000 * (phy_id & 3));
+
+ phy->invalid_dword_count = readl(qp);
+ phy->running_disparity_error_count = readl(&qp[1]);
+ phy->loss_of_dword_sync_count = readl(&qp[3]);
+ phy->phy_reset_problem_count = readl(&qp[4]);
+ }
+ if (pm8001_ha->chip_id == chip_8001)
+ pm8001_bar4_shift(pm8001_ha, 0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return 0;
default:
+ pm8001_dbg(pm8001_ha, DEVIO, "func 0x%x\n", func);
rc = -EOPNOTSUPP;
}
msleep(300);
return rc;
}
-int pm8001_slave_alloc(struct scsi_device *scsi_dev)
-{
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
- if (dev_is_sata(dev)) {
- /* We don't need to rescan targets
- * if REPORT_LUNS request is failed
- */
- if (scsi_dev->lun > 0)
- return -ENXIO;
- scsi_dev->tagged_supported = 1;
- }
- return sas_slave_alloc(scsi_dev);
-}
-
/**
* pm8001_scan_start - we should enable all HBA phys by sending the phy_start
* command to HBA.
@@ -240,20 +346,29 @@ void pm8001_scan_start(struct Scsi_Host *shost)
int i;
struct pm8001_hba_info *pm8001_ha;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ DECLARE_COMPLETION_ONSTACK(completion);
pm8001_ha = sha->lldd_ha;
- PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha);
- for (i = 0; i < pm8001_ha->chip->n_phy; ++i)
+ /* SAS_RE_INITIALIZATION not available in SPCv/ve */
+ if (pm8001_ha->chip_id == chip_8001)
+ PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha);
+ for (i = 0; i < pm8001_ha->chip->n_phy; ++i) {
+ pm8001_ha->phy[i].enable_completion = &completion;
PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i);
+ wait_for_completion(&completion);
+ msleep(300);
+ }
}
int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+
/* give the phy enabling interrupt event time to come in (1s
* is empirically about all it takes) */
if (time < HZ)
return 0;
/* Wait for discovery to finish */
- scsi_flush_work(shost);
+ sas_drain_work(ha);
return 1;
}
@@ -271,13 +386,12 @@ static int pm8001_task_prep_smp(struct pm8001_hba_info *pm8001_ha,
u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag)
{
struct ata_queued_cmd *qc = task->uldd_task;
- if (qc) {
- if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
- qc->tf.command == ATA_CMD_FPDMA_READ) {
- *tag = qc->tag;
- return 1;
- }
+
+ if (qc && ata_is_ncq(qc->tf.protocol)) {
+ *tag = qc->tag;
+ return 1;
}
+
return 0;
}
@@ -293,19 +407,31 @@ static int pm8001_task_prep_ata(struct pm8001_hba_info *pm8001_ha,
}
/**
+ * pm8001_task_prep_internal_abort - the dispatcher function, prepare data
+ * for internal abort task
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to sata task
+ */
+static int pm8001_task_prep_internal_abort(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ return PM8001_CHIP_DISP->task_abort(pm8001_ha, ccb);
+}
+
+/**
* pm8001_task_prep_ssp_tm - the dispatcher function, prepare task management data
* @pm8001_ha: our hba card information
* @ccb: the ccb which attached to TM
* @tmf: the task management IU
*/
static int pm8001_task_prep_ssp_tm(struct pm8001_hba_info *pm8001_ha,
- struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+ struct pm8001_ccb_info *ccb, struct sas_tmf_task *tmf)
{
return PM8001_CHIP_DISP->ssp_tm_req(pm8001_ha, ccb, tmf);
}
/**
- * pm8001_task_prep_ssp - the dispatcher function,prepare ssp data for ssp task
+ * pm8001_task_prep_ssp - the dispatcher function, prepare ssp data for ssp task
* @pm8001_ha: our hba card information
* @ccb: the ccb which attached to ssp task
*/
@@ -314,238 +440,162 @@ static int pm8001_task_prep_ssp(struct pm8001_hba_info *pm8001_ha,
{
return PM8001_CHIP_DISP->ssp_io_req(pm8001_ha, ccb);
}
-int pm8001_slave_configure(struct scsi_device *sdev)
-{
- struct domain_device *dev = sdev_to_domain_dev(sdev);
- int ret = sas_slave_configure(sdev);
- if (ret)
- return ret;
- if (dev_is_sata(dev)) {
- #ifdef PM8001_DISABLE_NCQ
- struct ata_port *ap = dev->sata_dev.ap;
- struct ata_device *adev = ap->link.device;
- adev->flags |= ATA_DFLAG_NCQ_OFF;
- scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
- #endif
- }
- return 0;
-}
- /* Find the local port id that's attached to this device */
-static int sas_find_local_port_id(struct domain_device *dev)
+
+#define DEV_IS_GONE(pm8001_dev) \
+ ((!pm8001_dev || (pm8001_dev->dev_type == SAS_PHY_UNUSED)))
+
+
+static int pm8001_deliver_command(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
{
- struct domain_device *pdev = dev->parent;
-
- /* Directly attached device */
- if (!pdev)
- return dev->port->id;
- while (pdev) {
- struct domain_device *pdev_p = pdev->parent;
- if (!pdev_p)
- return pdev->port->id;
- pdev = pdev->parent;
+ struct sas_task *task = ccb->task;
+ enum sas_protocol task_proto = task->task_proto;
+ struct sas_tmf_task *tmf = task->tmf;
+ int is_tmf = !!tmf;
+
+ switch (task_proto) {
+ case SAS_PROTOCOL_SMP:
+ return pm8001_task_prep_smp(pm8001_ha, ccb);
+ case SAS_PROTOCOL_SSP:
+ if (is_tmf)
+ return pm8001_task_prep_ssp_tm(pm8001_ha, ccb, tmf);
+ return pm8001_task_prep_ssp(pm8001_ha, ccb);
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ return pm8001_task_prep_ata(pm8001_ha, ccb);
+ case SAS_PROTOCOL_INTERNAL_ABORT:
+ return pm8001_task_prep_internal_abort(pm8001_ha, ccb);
+ default:
+ dev_err(pm8001_ha->dev, "unknown sas_task proto: 0x%x\n",
+ task_proto);
}
- return 0;
+
+ return -EINVAL;
}
/**
- * pm8001_task_exec - queue the task(ssp, smp && ata) to the hardware.
+ * pm8001_queue_command - register for upper layer used, all IO commands sent
+ * to HBA are from this interface.
* @task: the task to be execute.
- * @num: if can_queue great than 1, the task can be queued up. for SMP task,
- * we always execute one one time.
- * @gfp_flags: gfp_flags.
- * @is_tmf: if it is task management task.
- * @tmf: the task management IU
+ * @gfp_flags: gfp_flags
*/
-#define DEV_IS_GONE(pm8001_dev) \
- ((!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE)))
-static int pm8001_task_exec(struct sas_task *task, const int num,
- gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf)
+int pm8001_queue_command(struct sas_task *task, gfp_t gfp_flags)
{
+ struct task_status_struct *ts = &task->task_status;
+ enum sas_protocol task_proto = task->task_proto;
struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ bool internal_abort = sas_is_internal_abort(task);
struct pm8001_hba_info *pm8001_ha;
- struct pm8001_device *pm8001_dev;
- struct pm8001_port *port = NULL;
- struct sas_task *t = task;
+ struct pm8001_port *port;
struct pm8001_ccb_info *ccb;
- u32 tag = 0xdeadbeef, rc, n_elem = 0;
- u32 n = num;
- unsigned long flags = 0, flags_libsas = 0;
-
- if (!dev->port) {
- struct task_status_struct *tsm = &t->task_status;
- tsm->resp = SAS_TASK_UNDELIVERED;
- tsm->stat = SAS_PHY_DOWN;
- if (dev->dev_type != SATA_DEV)
- t->task_done(t);
+ unsigned long flags;
+ u32 n_elem = 0;
+ int rc = 0;
+
+ if (!internal_abort && !dev->port) {
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ if (dev->dev_type != SAS_SATA_DEV)
+ task->task_done(task);
return 0;
}
- pm8001_ha = pm8001_find_ha_by_dev(task->dev);
- PM8001_IO_DBG(pm8001_ha, pm8001_printk("pm8001_task_exec device \n "));
+
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ if (pm8001_ha->controller_fatal_error) {
+ ts->resp = SAS_TASK_UNDELIVERED;
+ task->task_done(task);
+ return 0;
+ }
+
+ pm8001_dbg(pm8001_ha, IO, "pm8001_task_exec device\n");
+
spin_lock_irqsave(&pm8001_ha->lock, flags);
- do {
- dev = t->dev;
- pm8001_dev = dev->lldd_dev;
- if (DEV_IS_GONE(pm8001_dev)) {
- if (pm8001_dev) {
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("device %d not ready.\n",
- pm8001_dev->device_id));
- } else {
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("device %016llx not "
- "ready.\n", SAS_ADDR(dev->sas_addr)));
- }
- rc = SAS_PHY_DOWN;
- goto out_done;
- }
- port = &pm8001_ha->port[sas_find_local_port_id(dev)];
- if (!port->port_attached) {
- if (sas_protocol_ata(t->task_proto)) {
- struct task_status_struct *ts = &t->task_status;
- ts->resp = SAS_TASK_UNDELIVERED;
- ts->stat = SAS_PHY_DOWN;
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- spin_unlock_irqrestore(dev->sata_dev.ap->lock,
- flags_libsas);
- t->task_done(t);
- spin_lock_irqsave(dev->sata_dev.ap->lock,
- flags_libsas);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
- if (n > 1)
- t = list_entry(t->list.next,
- struct sas_task, list);
- continue;
- } else {
- struct task_status_struct *ts = &t->task_status;
- ts->resp = SAS_TASK_UNDELIVERED;
- ts->stat = SAS_PHY_DOWN;
- t->task_done(t);
- if (n > 1)
- t = list_entry(t->list.next,
- struct sas_task, list);
- continue;
- }
- }
- rc = pm8001_tag_alloc(pm8001_ha, &tag);
- if (rc)
- goto err_out;
- ccb = &pm8001_ha->ccb_info[tag];
-
- if (!sas_protocol_ata(t->task_proto)) {
- if (t->num_scatter) {
- n_elem = dma_map_sg(pm8001_ha->dev,
- t->scatter,
- t->num_scatter,
- t->data_dir);
- if (!n_elem) {
- rc = -ENOMEM;
- goto err_out_tag;
- }
- }
+ port = dev->port->lldd_port;
+
+ if (!internal_abort &&
+ (DEV_IS_GONE(pm8001_dev) || !port || !port->port_attached)) {
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ if (sas_protocol_ata(task_proto)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ task->task_done(task);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
} else {
- n_elem = t->num_scatter;
+ task->task_done(task);
}
+ rc = -ENODEV;
+ goto err_out;
+ }
- t->lldd_task = ccb;
- ccb->n_elem = n_elem;
- ccb->ccb_tag = tag;
- ccb->task = t;
- switch (t->task_proto) {
- case SAS_PROTOCOL_SMP:
- rc = pm8001_task_prep_smp(pm8001_ha, ccb);
- break;
- case SAS_PROTOCOL_SSP:
- if (is_tmf)
- rc = pm8001_task_prep_ssp_tm(pm8001_ha,
- ccb, tmf);
- else
- rc = pm8001_task_prep_ssp(pm8001_ha, ccb);
- break;
- case SAS_PROTOCOL_SATA:
- case SAS_PROTOCOL_STP:
- case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
- rc = pm8001_task_prep_ata(pm8001_ha, ccb);
- break;
- default:
- dev_printk(KERN_ERR, pm8001_ha->dev,
- "unknown sas_task proto: 0x%x\n",
- t->task_proto);
- rc = -EINVAL;
- break;
- }
+ ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_dev, task);
+ if (!ccb) {
+ rc = -SAS_QUEUE_FULL;
+ goto err_out;
+ }
- if (rc) {
- PM8001_IO_DBG(pm8001_ha,
- pm8001_printk("rc is %x\n", rc));
- goto err_out_tag;
+ if (!sas_protocol_ata(task_proto)) {
+ if (task->num_scatter) {
+ n_elem = dma_map_sg(pm8001_ha->dev, task->scatter,
+ task->num_scatter, task->data_dir);
+ if (!n_elem) {
+ rc = -ENOMEM;
+ goto err_out_ccb;
+ }
}
- /* TODO: select normal or high priority */
- spin_lock(&t->task_state_lock);
- t->task_state_flags |= SAS_TASK_AT_INITIATOR;
- spin_unlock(&t->task_state_lock);
- pm8001_dev->running_req++;
- if (n > 1)
- t = list_entry(t->list.next, struct sas_task, list);
- } while (--n);
- rc = 0;
- goto out_done;
-
-err_out_tag:
- pm8001_tag_free(pm8001_ha, tag);
+ } else {
+ n_elem = task->num_scatter;
+ }
+
+ task->lldd_task = ccb;
+ ccb->n_elem = n_elem;
+
+ atomic_inc(&pm8001_dev->running_req);
+
+ rc = pm8001_deliver_command(pm8001_ha, ccb);
+ if (rc) {
+ atomic_dec(&pm8001_dev->running_req);
+ if (!sas_protocol_ata(task_proto) && n_elem)
+ dma_unmap_sg(pm8001_ha->dev, task->scatter,
+ task->num_scatter, task->data_dir);
+err_out_ccb:
+ pm8001_ccb_free(pm8001_ha, ccb);
+
err_out:
- dev_printk(KERN_ERR, pm8001_ha->dev, "pm8001 exec failed[%d]!\n", rc);
- if (!sas_protocol_ata(t->task_proto))
- if (n_elem)
- dma_unmap_sg(pm8001_ha->dev, t->scatter, n_elem,
- t->data_dir);
-out_done:
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- return rc;
-}
+ pm8001_dbg(pm8001_ha, IO, "pm8001_task_exec failed[%d]!\n", rc);
+ }
-/**
- * pm8001_queue_command - register for upper layer used, all IO commands sent
- * to HBA are from this interface.
- * @task: the task to be execute.
- * @num: if can_queue great than 1, the task can be queued up. for SMP task,
- * we always execute one one time
- * @gfp_flags: gfp_flags
- */
-int pm8001_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags)
-{
- return pm8001_task_exec(task, num, gfp_flags, 0, NULL);
-}
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
-void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx)
-{
- pm8001_tag_clear(pm8001_ha, ccb_idx);
+ return rc;
}
/**
* pm8001_ccb_task_free - free the sg for ssp and smp command, free the ccb.
* @pm8001_ha: our hba card information
- * @ccb: the ccb which attached to ssp task
- * @task: the task to be free.
- * @ccb_idx: ccb index.
+ * @ccb: the ccb which attached to ssp task to free
*/
void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
- struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx)
+ struct pm8001_ccb_info *ccb)
{
- if (!ccb->task)
+ struct sas_task *task = ccb->task;
+ struct ata_queued_cmd *qc;
+ struct pm8001_device *pm8001_dev;
+
+ if (!task)
return;
- if (!sas_protocol_ata(task->task_proto))
- if (ccb->n_elem)
- dma_unmap_sg(pm8001_ha->dev, task->scatter,
- task->num_scatter, task->data_dir);
+
+ if (!sas_protocol_ata(task->task_proto) && ccb->n_elem)
+ dma_unmap_sg(pm8001_ha->dev, task->scatter,
+ task->num_scatter, task->data_dir);
switch (task->task_proto) {
case SAS_PROTOCOL_SMP:
dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_resp, 1,
- PCI_DMA_FROMDEVICE);
+ DMA_FROM_DEVICE);
dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_req, 1,
- PCI_DMA_TODEVICE);
+ DMA_TO_DEVICE);
break;
case SAS_PROTOCOL_SATA:
@@ -555,39 +605,74 @@ void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
/* do nothing */
break;
}
+
+ if (sas_protocol_ata(task->task_proto)) {
+ /* For SCSI/ATA commands uldd_task points to ata_queued_cmd */
+ qc = task->uldd_task;
+ pm8001_dev = ccb->device;
+ trace_pm80xx_request_complete(pm8001_ha->id,
+ pm8001_dev ? pm8001_dev->attached_phy : PM8001_MAX_PHYS,
+ ccb->ccb_tag, 0 /* ctlr_opcode not known */,
+ qc ? qc->tf.command : 0, // ata opcode
+ pm8001_dev ? atomic_read(&pm8001_dev->running_req) : -1);
+ }
+
task->lldd_task = NULL;
- ccb->task = NULL;
- ccb->ccb_tag = 0xFFFFFFFF;
- pm8001_ccb_free(pm8001_ha, ccb_idx);
+ pm8001_ccb_free(pm8001_ha, ccb);
+}
+
+static void pm8001_init_dev(struct pm8001_device *pm8001_dev, int id)
+{
+ pm8001_dev->id = id;
+ pm8001_dev->device_id = PM8001_MAX_DEVICES;
+ atomic_set(&pm8001_dev->running_req, 0);
}
- /**
- * pm8001_alloc_dev - find a empty pm8001_device
+/**
+ * pm8001_alloc_dev - find a empty pm8001_device
+ * @pm8001_ha: our hba card information
+ */
+static struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 dev;
+ for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) {
+ struct pm8001_device *pm8001_dev = &pm8001_ha->devices[dev];
+
+ if (pm8001_dev->dev_type == SAS_PHY_UNUSED) {
+ pm8001_init_dev(pm8001_dev, dev);
+ return pm8001_dev;
+ }
+ }
+ if (dev == PM8001_MAX_DEVICES) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "max support %d devices, ignore ..\n",
+ PM8001_MAX_DEVICES);
+ }
+ return NULL;
+}
+/**
+ * pm8001_find_dev - find a matching pm8001_device
* @pm8001_ha: our hba card information
+ * @device_id: device ID to match against
*/
-struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha)
+struct pm8001_device *pm8001_find_dev(struct pm8001_hba_info *pm8001_ha,
+ u32 device_id)
{
u32 dev;
for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) {
- if (pm8001_ha->devices[dev].dev_type == NO_DEVICE) {
- pm8001_ha->devices[dev].id = dev;
+ if (pm8001_ha->devices[dev].device_id == device_id)
return &pm8001_ha->devices[dev];
- }
}
if (dev == PM8001_MAX_DEVICES) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("max support %d devices, ignore ..\n",
- PM8001_MAX_DEVICES));
+ pm8001_dbg(pm8001_ha, FAIL, "NO MATCHING DEVICE FOUND !!!\n");
}
return NULL;
}
-static void pm8001_free_dev(struct pm8001_device *pm8001_dev)
+void pm8001_free_dev(struct pm8001_device *pm8001_dev)
{
- u32 id = pm8001_dev->id;
memset(pm8001_dev, 0, sizeof(*pm8001_dev));
- pm8001_dev->id = id;
- pm8001_dev->dev_type = NO_DEVICE;
+ pm8001_dev->dev_type = SAS_PHY_UNUSED;
pm8001_dev->device_id = PM8001_MAX_DEVICES;
pm8001_dev->sas_device = NULL;
}
@@ -625,39 +710,33 @@ static int pm8001_dev_found_notify(struct domain_device *dev)
dev->lldd_dev = pm8001_device;
pm8001_device->dev_type = dev->dev_type;
pm8001_device->dcompletion = &completion;
- if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+ if (dev_parent_is_expander(dev)) {
int phy_id;
- struct ex_phy *phy;
- for (phy_id = 0; phy_id < parent_dev->ex_dev.num_phys;
- phy_id++) {
- phy = &parent_dev->ex_dev.ex_phy[phy_id];
- if (SAS_ADDR(phy->attached_sas_addr)
- == SAS_ADDR(dev->sas_addr)) {
- pm8001_device->attached_phy = phy_id;
- break;
- }
- }
- if (phy_id == parent_dev->ex_dev.num_phys) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Error: no attached dev:%016llx"
- " at ex:%016llx.\n", SAS_ADDR(dev->sas_addr),
- SAS_ADDR(parent_dev->sas_addr)));
- res = -1;
+
+ phy_id = sas_find_attached_phy_id(&parent_dev->ex_dev, dev);
+ if (phy_id < 0) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Error: no attached dev:%016llx at ex:%016llx.\n",
+ SAS_ADDR(dev->sas_addr),
+ SAS_ADDR(parent_dev->sas_addr));
+ res = phy_id;
+ } else {
+ pm8001_device->attached_phy = phy_id;
}
} else {
- if (dev->dev_type == SATA_DEV) {
+ if (dev->dev_type == SAS_SATA_DEV) {
pm8001_device->attached_phy =
dev->rphy->identify.phy_identifier;
- flag = 1; /* directly sata*/
+ flag = 1; /* directly sata */
}
} /*register this device to HBA*/
- PM8001_DISC_DBG(pm8001_ha, pm8001_printk("Found device \n"));
+ pm8001_dbg(pm8001_ha, DISC, "Found device\n");
PM8001_CHIP_DISP->reg_dev_req(pm8001_ha, pm8001_device, flag);
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
wait_for_completion(&completion);
- if (dev->dev_type == SAS_END_DEV)
+ if (dev->dev_type == SAS_END_DEVICE)
msleep(50);
- pm8001_ha->flags |= PM8001F_RUN_TIME ;
+ pm8001_ha->flags = PM8001F_RUN_TIME;
return 0;
found_out:
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
@@ -669,211 +748,7 @@ int pm8001_dev_found(struct domain_device *dev)
return pm8001_dev_found_notify(dev);
}
-/**
- * pm8001_alloc_task - allocate a task structure for TMF
- */
-static struct sas_task *pm8001_alloc_task(void)
-{
- struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL);
- if (task) {
- INIT_LIST_HEAD(&task->list);
- spin_lock_init(&task->task_state_lock);
- task->task_state_flags = SAS_TASK_STATE_PENDING;
- init_timer(&task->timer);
- init_completion(&task->completion);
- }
- return task;
-}
-
-static void pm8001_free_task(struct sas_task *task)
-{
- if (task) {
- BUG_ON(!list_empty(&task->list));
- kfree(task);
- }
-}
-
-static void pm8001_task_done(struct sas_task *task)
-{
- if (!del_timer(&task->timer))
- return;
- complete(&task->completion);
-}
-
-static void pm8001_tmf_timedout(unsigned long data)
-{
- struct sas_task *task = (struct sas_task *)data;
-
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- complete(&task->completion);
-}
-
#define PM8001_TASK_TIMEOUT 20
-/**
- * pm8001_exec_internal_tmf_task - execute some task management commands.
- * @dev: the wanted device.
- * @tmf: which task management wanted to be take.
- * @para_len: para_len.
- * @parameter: ssp task parameter.
- *
- * when errors or exception happened, we may want to do something, for example
- * abort the issued task which result in this execption, it is done by calling
- * this function, note it is also with the task execute interface.
- */
-static int pm8001_exec_internal_tmf_task(struct domain_device *dev,
- void *parameter, u32 para_len, struct pm8001_tmf_task *tmf)
-{
- int res, retry;
- struct sas_task *task = NULL;
- struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
-
- for (retry = 0; retry < 3; retry++) {
- task = pm8001_alloc_task();
- if (!task)
- return -ENOMEM;
-
- task->dev = dev;
- task->task_proto = dev->tproto;
- memcpy(&task->ssp_task, parameter, para_len);
- task->task_done = pm8001_task_done;
- task->timer.data = (unsigned long)task;
- task->timer.function = pm8001_tmf_timedout;
- task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
- add_timer(&task->timer);
-
- res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf);
-
- if (res) {
- del_timer(&task->timer);
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Executing internal task "
- "failed\n"));
- goto ex_err;
- }
- wait_for_completion(&task->completion);
- res = -TMF_RESP_FUNC_FAILED;
- /* Even TMF timed out, return direct. */
- if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("TMF task[%x]timeout.\n",
- tmf->tmf));
- goto ex_err;
- }
- }
-
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAM_STAT_GOOD) {
- res = TMF_RESP_FUNC_COMPLETE;
- break;
- }
-
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_UNDERRUN) {
- /* no error, but return the number of bytes of
- * underrun */
- res = task->task_status.residual;
- break;
- }
-
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_OVERRUN) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Blocked task error.\n"));
- res = -EMSGSIZE;
- break;
- } else {
- PM8001_EH_DBG(pm8001_ha,
- pm8001_printk(" Task to dev %016llx response:"
- "0x%x status 0x%x\n",
- SAS_ADDR(dev->sas_addr),
- task->task_status.resp,
- task->task_status.stat));
- pm8001_free_task(task);
- task = NULL;
- }
- }
-ex_err:
- BUG_ON(retry == 3 && task != NULL);
- if (task != NULL)
- pm8001_free_task(task);
- return res;
-}
-
-static int
-pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha,
- struct pm8001_device *pm8001_dev, struct domain_device *dev, u32 flag,
- u32 task_tag)
-{
- int res, retry;
- u32 ccb_tag;
- struct pm8001_ccb_info *ccb;
- struct sas_task *task = NULL;
-
- for (retry = 0; retry < 3; retry++) {
- task = pm8001_alloc_task();
- if (!task)
- return -ENOMEM;
-
- task->dev = dev;
- task->task_proto = dev->tproto;
- task->task_done = pm8001_task_done;
- task->timer.data = (unsigned long)task;
- task->timer.function = pm8001_tmf_timedout;
- task->timer.expires = jiffies + PM8001_TASK_TIMEOUT * HZ;
- add_timer(&task->timer);
-
- res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
- if (res)
- return res;
- ccb = &pm8001_ha->ccb_info[ccb_tag];
- ccb->device = pm8001_dev;
- ccb->ccb_tag = ccb_tag;
- ccb->task = task;
-
- res = PM8001_CHIP_DISP->task_abort(pm8001_ha,
- pm8001_dev, flag, task_tag, ccb_tag);
-
- if (res) {
- del_timer(&task->timer);
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("Executing internal task "
- "failed\n"));
- goto ex_err;
- }
- wait_for_completion(&task->completion);
- res = TMF_RESP_FUNC_FAILED;
- /* Even TMF timed out, return direct. */
- if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- PM8001_FAIL_DBG(pm8001_ha,
- pm8001_printk("TMF task timeout.\n"));
- goto ex_err;
- }
- }
-
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAM_STAT_GOOD) {
- res = TMF_RESP_FUNC_COMPLETE;
- break;
-
- } else {
- PM8001_EH_DBG(pm8001_ha,
- pm8001_printk(" Task to dev %016llx response: "
- "0x%x status 0x%x\n",
- SAS_ADDR(dev->sas_addr),
- task->task_status.resp,
- task->task_status.stat));
- pm8001_free_task(task);
- task = NULL;
- }
- }
-ex_err:
- BUG_ON(retry == 3 && task != NULL);
- if (task != NULL)
- pm8001_free_task(task);
- return res;
-}
/**
* pm8001_dev_gone_notify - see the comments for "pm8001_dev_found_notify"
@@ -882,30 +757,37 @@ ex_err:
static void pm8001_dev_gone_notify(struct domain_device *dev)
{
unsigned long flags = 0;
- u32 tag;
struct pm8001_hba_info *pm8001_ha;
struct pm8001_device *pm8001_dev = dev->lldd_dev;
pm8001_ha = pm8001_find_ha_by_dev(dev);
spin_lock_irqsave(&pm8001_ha->lock, flags);
- pm8001_tag_alloc(pm8001_ha, &tag);
if (pm8001_dev) {
u32 device_id = pm8001_dev->device_id;
- PM8001_DISC_DBG(pm8001_ha,
- pm8001_printk("found dev[%d:%x] is gone.\n",
- pm8001_dev->device_id, pm8001_dev->dev_type));
- if (pm8001_dev->running_req) {
+ pm8001_dbg(pm8001_ha, DISC, "found dev[%d:%x] is gone.\n",
+ pm8001_dev->device_id, pm8001_dev->dev_type);
+ if (atomic_read(&pm8001_dev->running_req)) {
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
- dev, 1, 0);
+ sas_execute_internal_abort_dev(dev, 0, NULL);
+ while (atomic_read(&pm8001_dev->running_req))
+ msleep(20);
spin_lock_irqsave(&pm8001_ha->lock, flags);
}
PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id);
+
+ /*
+ * The phy array only contains local phys. Thus, we cannot clear
+ * phy_attached for a device behind an expander.
+ */
+ if (!dev_parent_is_expander(dev)) {
+ u32 phy_id = pm80xx_get_local_phy_id(dev);
+
+ pm8001_ha->phy[phy_id].phy_attached = 0;
+ }
pm8001_free_dev(pm8001_dev);
} else {
- PM8001_DISC_DBG(pm8001_ha,
- pm8001_printk("Found dev has gone.\n"));
+ pm8001_dbg(pm8001_ha, DISC, "Found dev has gone.\n");
}
dev->lldd_dev = NULL;
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
@@ -916,78 +798,221 @@ void pm8001_dev_gone(struct domain_device *dev)
pm8001_dev_gone_notify(dev);
}
-static int pm8001_issue_ssp_tmf(struct domain_device *dev,
- u8 *lun, struct pm8001_tmf_task *tmf)
+/* retry commands by ha, by task and/or by device */
+void pm8001_open_reject_retry(
+ struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task_to_close,
+ struct pm8001_device *device_to_close)
{
- struct sas_ssp_task ssp_task;
- if (!(dev->tproto & SAS_PROTOCOL_SSP))
- return TMF_RESP_FUNC_ESUPP;
+ int i;
+ unsigned long flags;
+
+ if (pm8001_ha == NULL)
+ return;
+
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ for (i = 0; i < PM8001_MAX_CCB; i++) {
+ struct sas_task *task;
+ struct task_status_struct *ts;
+ struct pm8001_device *pm8001_dev;
+ unsigned long flags1;
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[i];
+
+ if (ccb->ccb_tag == PM8001_INVALID_TAG)
+ continue;
+
+ pm8001_dev = ccb->device;
+ if (!pm8001_dev || (pm8001_dev->dev_type == SAS_PHY_UNUSED))
+ continue;
+ if (!device_to_close) {
+ uintptr_t d = (uintptr_t)pm8001_dev
+ - (uintptr_t)&pm8001_ha->devices;
+ if (((d % sizeof(*pm8001_dev)) != 0)
+ || ((d / sizeof(*pm8001_dev)) >= PM8001_MAX_DEVICES))
+ continue;
+ } else if (pm8001_dev != device_to_close)
+ continue;
+ task = ccb->task;
+ if (!task || !task->task_done)
+ continue;
+ if (task_to_close && (task != task_to_close))
+ continue;
+ ts = &task->task_status;
+ ts->resp = SAS_TASK_COMPLETE;
+ /* Force the midlayer to retry */
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ spin_lock_irqsave(&task->task_state_lock, flags1);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((task->task_state_flags
+ & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags1);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ } else {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags1);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ mb();/* in order to force CPU ordering */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ task->task_done(task);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ }
+ }
- strncpy((u8 *)&ssp_task.LUN, lun, 8);
- return pm8001_exec_internal_tmf_task(dev, &ssp_task, sizeof(ssp_task),
- tmf);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
}
/**
- * Standard mandates link reset for ATA (type 0) and hard reset for
- * SSP (type 1) , only for RECOVERY
- */
+ * pm8001_I_T_nexus_reset() - reset the initiator/target connection
+ * @dev: the device structure for the device to reset.
+ *
+ * Standard mandates link reset for ATA (type 0) and hard reset for
+ * SSP (type 1), only for RECOVERY
+ */
int pm8001_I_T_nexus_reset(struct domain_device *dev)
{
int rc = TMF_RESP_FUNC_FAILED;
struct pm8001_device *pm8001_dev;
struct pm8001_hba_info *pm8001_ha;
struct sas_phy *phy;
+
+ if (!dev || !dev->lldd_dev)
+ return -ENODEV;
+
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ phy = sas_get_local_phy(dev);
+
+ if (dev_is_sata(dev)) {
+ if (scsi_is_sas_phy_local(phy)) {
+ rc = 0;
+ goto out;
+ }
+ rc = sas_phy_reset(phy, 1);
+ if (rc) {
+ pm8001_dbg(pm8001_ha, EH,
+ "phy reset failed for device %x\n"
+ "with rc %d\n", pm8001_dev->device_id, rc);
+ rc = TMF_RESP_FUNC_FAILED;
+ goto out;
+ }
+ msleep(2000);
+ rc = sas_execute_internal_abort_dev(dev, 0, NULL);
+ if (rc) {
+ pm8001_dbg(pm8001_ha, EH, "task abort failed %x\n"
+ "with rc %d\n", pm8001_dev->device_id, rc);
+ rc = TMF_RESP_FUNC_FAILED;
+ }
+ } else {
+ rc = sas_phy_reset(phy, 1);
+ msleep(2000);
+ }
+ pm8001_dbg(pm8001_ha, EH, " for device[%x]:rc=%d\n",
+ pm8001_dev->device_id, rc);
+ out:
+ sas_put_local_phy(phy);
+ return rc;
+}
+
+/*
+* This function handle the IT_NEXUS_XXX event or completion
+* status code for SSP/SATA/SMP I/O request.
+*/
+int pm8001_I_T_nexus_event_handler(struct domain_device *dev)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_device *pm8001_dev;
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_phy *phy;
+
if (!dev || !dev->lldd_dev)
return -1;
pm8001_dev = dev->lldd_dev;
pm8001_ha = pm8001_find_ha_by_dev(dev);
- phy = sas_find_local_phy(dev);
+
+ pm8001_dbg(pm8001_ha, EH, "I_T_Nexus handler invoked !!\n");
+
+ phy = sas_get_local_phy(dev);
if (dev_is_sata(dev)) {
DECLARE_COMPLETION_ONSTACK(completion_setstate);
- if (scsi_is_sas_phy_local(phy))
- return 0;
+ if (scsi_is_sas_phy_local(phy)) {
+ rc = 0;
+ goto out;
+ }
+ /* send internal ssp/sata/smp abort command to FW */
+ sas_execute_internal_abort_dev(dev, 0, NULL);
+ msleep(100);
+
+ /* deregister the target device */
+ pm8001_dev_gone_notify(dev);
+ msleep(200);
+
+ /*send phy reset to hard reset target */
rc = sas_phy_reset(phy, 1);
msleep(2000);
- rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
- dev, 1, 0);
pm8001_dev->setds_completion = &completion_setstate;
- rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
- pm8001_dev, 0x01);
+
wait_for_completion(&completion_setstate);
- } else{
- rc = sas_phy_reset(phy, 1);
- msleep(2000);
+ } else {
+ /* send internal ssp/sata/smp abort command to FW */
+ sas_execute_internal_abort_dev(dev, 0, NULL);
+ msleep(100);
+
+ /* deregister the target device */
+ pm8001_dev_gone_notify(dev);
+ msleep(200);
+
+ /*send phy reset to hard reset target */
+ rc = sas_phy_reset(phy, 1);
+ msleep(2000);
}
- PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n",
- pm8001_dev->device_id, rc));
+ pm8001_dbg(pm8001_ha, EH, " for device[%x]:rc=%d\n",
+ pm8001_dev->device_id, rc);
+out:
+ sas_put_local_phy(phy);
+
return rc;
}
-
/* mandatory SAM-3, the task reset the specified LUN*/
int pm8001_lu_reset(struct domain_device *dev, u8 *lun)
{
int rc = TMF_RESP_FUNC_FAILED;
- struct pm8001_tmf_task tmf_task;
struct pm8001_device *pm8001_dev = dev->lldd_dev;
struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+ DECLARE_COMPLETION_ONSTACK(completion_setstate);
+
+ if (PM8001_CHIP_DISP->fatal_errors(pm8001_ha)) {
+ /*
+ * If the controller is in fatal error state,
+ * we will not get a response from the controller
+ */
+ pm8001_dbg(pm8001_ha, FAIL,
+ "LUN reset failed due to fatal errors\n");
+ return rc;
+ }
+
if (dev_is_sata(dev)) {
- struct sas_phy *phy = sas_find_local_phy(dev);
- rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
- dev, 1, 0);
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ sas_execute_internal_abort_dev(dev, 0, NULL);
rc = sas_phy_reset(phy, 1);
+ sas_put_local_phy(phy);
+ pm8001_dev->setds_completion = &completion_setstate;
rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
- pm8001_dev, 0x01);
- msleep(2000);
+ pm8001_dev, DS_OPERATIONAL);
+ wait_for_completion(&completion_setstate);
} else {
- tmf_task.tmf = TMF_LU_RESET;
- rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+ rc = sas_lu_reset(dev, lun);
}
/* If failed, fall-through I_T_Nexus reset */
- PM8001_EH_DBG(pm8001_ha, pm8001_printk("for device[%x]:rc=%d\n",
- pm8001_dev->device_id, rc));
+ pm8001_dbg(pm8001_ha, EH, "for device[%x]:rc=%d\n",
+ pm8001_dev->device_id, rc);
return rc;
}
@@ -995,9 +1020,6 @@ int pm8001_lu_reset(struct domain_device *dev, u8 *lun)
int pm8001_query_task(struct sas_task *task)
{
u32 tag = 0xdeadbeef;
- int i = 0;
- struct scsi_lun lun;
- struct pm8001_tmf_task tmf_task;
int rc = TMF_RESP_FUNC_FAILED;
if (unlikely(!task || !task->lldd_task || !task->dev))
return rc;
@@ -1008,148 +1030,234 @@ int pm8001_query_task(struct sas_task *task)
struct pm8001_hba_info *pm8001_ha =
pm8001_find_ha_by_dev(dev);
- int_to_scsilun(cmnd->device->lun, &lun);
rc = pm8001_find_tag(task, &tag);
if (rc == 0) {
rc = TMF_RESP_FUNC_FAILED;
return rc;
}
- PM8001_EH_DBG(pm8001_ha, pm8001_printk("Query:["));
- for (i = 0; i < 16; i++)
- printk(KERN_INFO "%02x ", cmnd->cmnd[i]);
- printk(KERN_INFO "]\n");
- tmf_task.tmf = TMF_QUERY_TASK;
- tmf_task.tag_of_task_to_be_managed = tag;
-
- rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ pm8001_dbg(pm8001_ha, EH, "Query:[%16ph]\n", cmnd->cmnd);
+
+ rc = sas_query_task(task, tag);
switch (rc) {
/* The task is still in Lun, release it then */
case TMF_RESP_FUNC_SUCC:
- PM8001_EH_DBG(pm8001_ha,
- pm8001_printk("The task is still in Lun \n"));
+ pm8001_dbg(pm8001_ha, EH,
+ "The task is still in Lun\n");
+ break;
/* The task is not in Lun or failed, reset the phy */
case TMF_RESP_FUNC_FAILED:
case TMF_RESP_FUNC_COMPLETE:
- PM8001_EH_DBG(pm8001_ha,
- pm8001_printk("The task is not in Lun or failed,"
- " reset the phy \n"));
+ pm8001_dbg(pm8001_ha, EH,
+ "The task is not in Lun or failed, reset the phy\n");
break;
}
}
- pm8001_printk(":rc= %d\n", rc);
+ pr_err("pm80xx: rc= %d\n", rc);
return rc;
}
-/* mandatory SAM-3, still need free task/ccb info, abord the specified task */
+/* mandatory SAM-3, still need free task/ccb info, abort the specified task */
int pm8001_abort_task(struct sas_task *task)
{
+ struct pm8001_ccb_info *ccb = task->lldd_task;
unsigned long flags;
- u32 tag = 0xdeadbeef;
- u32 device_id;
+ u32 tag;
struct domain_device *dev ;
- struct pm8001_hba_info *pm8001_ha = NULL;
- struct pm8001_ccb_info *ccb;
- struct scsi_lun lun;
+ struct pm8001_hba_info *pm8001_ha;
struct pm8001_device *pm8001_dev;
- struct pm8001_tmf_task tmf_task;
- int rc = TMF_RESP_FUNC_FAILED;
- if (unlikely(!task || !task->lldd_task || !task->dev))
- return rc;
+ int rc = TMF_RESP_FUNC_FAILED, ret;
+ u32 port_id;
+ struct sas_task_slow slow_task;
+
+ if (!task->lldd_task || !task->dev)
+ return TMF_RESP_FUNC_FAILED;
+
+ dev = task->dev;
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+ if (PM8001_CHIP_DISP->fatal_errors(pm8001_ha)) {
+ // If the controller is seeing fatal errors
+ // abort task will not get a response from the controller
+ return TMF_RESP_FUNC_FAILED;
+ }
+
+ ret = pm8001_find_tag(task, &tag);
+ if (ret == 0) {
+ pm8001_info(pm8001_ha, "no tag for task:%p\n", task);
+ return TMF_RESP_FUNC_FAILED;
+ }
spin_lock_irqsave(&task->task_state_lock, flags);
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
spin_unlock_irqrestore(&task->task_state_lock, flags);
- rc = TMF_RESP_FUNC_COMPLETE;
- goto out;
+ return TMF_RESP_FUNC_COMPLETE;
+ }
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ if (task->slow_task == NULL) {
+ init_completion(&slow_task.completion);
+ task->slow_task = &slow_task;
}
spin_unlock_irqrestore(&task->task_state_lock, flags);
if (task->task_proto & SAS_PROTOCOL_SSP) {
- struct scsi_cmnd *cmnd = task->uldd_task;
- dev = task->dev;
- ccb = task->lldd_task;
- pm8001_dev = dev->lldd_dev;
- pm8001_ha = pm8001_find_ha_by_dev(dev);
- int_to_scsilun(cmnd->device->lun, &lun);
- rc = pm8001_find_tag(task, &tag);
- if (rc == 0) {
- printk(KERN_INFO "No such tag in %s\n", __func__);
- rc = TMF_RESP_FUNC_FAILED;
- return rc;
- }
- device_id = pm8001_dev->device_id;
- PM8001_EH_DBG(pm8001_ha,
- pm8001_printk("abort io to deviceid= %d\n", device_id));
- tmf_task.tmf = TMF_ABORT_TASK;
- tmf_task.tag_of_task_to_be_managed = tag;
- rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
- pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
- pm8001_dev->sas_device, 0, tag);
+ rc = sas_abort_task(task, tag);
+ sas_execute_internal_abort_single(dev, tag, 0, NULL);
} else if (task->task_proto & SAS_PROTOCOL_SATA ||
task->task_proto & SAS_PROTOCOL_STP) {
- dev = task->dev;
- pm8001_dev = dev->lldd_dev;
- pm8001_ha = pm8001_find_ha_by_dev(dev);
- rc = pm8001_find_tag(task, &tag);
- if (rc == 0) {
- printk(KERN_INFO "No such tag in %s\n", __func__);
- rc = TMF_RESP_FUNC_FAILED;
- return rc;
+ if (pm8001_ha->chip_id == chip_8006) {
+ DECLARE_COMPLETION_ONSTACK(completion_reset);
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u32 phy_id = pm80xx_get_local_phy_id(dev);
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ port_id = phy->port->port_id;
+
+ /* 1. Set Device state as Recovery */
+ pm8001_dev->setds_completion = &completion;
+ PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+ pm8001_dev, DS_IN_RECOVERY);
+ wait_for_completion(&completion);
+
+ /* 2. Send Phy Control Hard Reset */
+ reinit_completion(&completion);
+ phy->port_reset_status = PORT_RESET_TMO;
+ phy->reset_success = false;
+ phy->enable_completion = &completion;
+ phy->reset_completion = &completion_reset;
+ ret = PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_HARD_RESET);
+ if (ret) {
+ phy->enable_completion = NULL;
+ phy->reset_completion = NULL;
+ goto out;
+ }
+
+ /* In the case of the reset timeout/fail we still
+ * abort the command at the firmware. The assumption
+ * here is that the drive is off doing something so
+ * that it's not processing requests, and we want to
+ * avoid getting a completion for this and either
+ * leaking the task in libsas or losing the race and
+ * getting a double free.
+ */
+ pm8001_dbg(pm8001_ha, MSG,
+ "Waiting for local phy ctl\n");
+ ret = wait_for_completion_timeout(&completion,
+ PM8001_TASK_TIMEOUT * HZ);
+ if (!ret || !phy->reset_success) {
+ phy->enable_completion = NULL;
+ phy->reset_completion = NULL;
+ } else {
+ /* 3. Wait for Port Reset complete or
+ * Port reset TMO
+ */
+ pm8001_dbg(pm8001_ha, MSG,
+ "Waiting for Port reset\n");
+ ret = wait_for_completion_timeout(
+ &completion_reset,
+ PM8001_TASK_TIMEOUT * HZ);
+ if (!ret)
+ phy->reset_completion = NULL;
+ WARN_ON(phy->port_reset_status ==
+ PORT_RESET_TMO);
+ if (phy->port_reset_status == PORT_RESET_TMO) {
+ pm8001_dev_gone_notify(dev);
+ PM8001_CHIP_DISP->hw_event_ack_req(
+ pm8001_ha, 0,
+ 0x07, /*HW_EVENT_PHY_DOWN ack*/
+ port_id, phy_id, 0, 0);
+ goto out;
+ }
+ }
+
+ /*
+ * 4. SATA Abort ALL
+ * we wait for the task to be aborted so that the task
+ * is removed from the ccb. on success the caller is
+ * going to free the task.
+ */
+ ret = sas_execute_internal_abort_dev(dev, 0, NULL);
+ if (ret)
+ goto out;
+ ret = wait_for_completion_timeout(
+ &task->slow_task->completion,
+ PM8001_TASK_TIMEOUT * HZ);
+ if (!ret)
+ goto out;
+
+ /* 5. Set Device State as Operational */
+ reinit_completion(&completion);
+ pm8001_dev->setds_completion = &completion;
+ PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+ pm8001_dev, DS_OPERATIONAL);
+ wait_for_completion(&completion);
+ } else {
+ /*
+ * Ensure that if we see a completion for the ccb
+ * associated with the task which we are trying to
+ * abort then we should not touch the sas_task as it
+ * may race with libsas freeing it when return here.
+ */
+ ccb->task = NULL;
+ ret = sas_execute_internal_abort_single(dev, tag, 0, NULL);
}
- rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
- pm8001_dev->sas_device, 0, tag);
+ rc = TMF_RESP_FUNC_COMPLETE;
} else if (task->task_proto & SAS_PROTOCOL_SMP) {
/* SMP */
- dev = task->dev;
- pm8001_dev = dev->lldd_dev;
- pm8001_ha = pm8001_find_ha_by_dev(dev);
- rc = pm8001_find_tag(task, &tag);
- if (rc == 0) {
- printk(KERN_INFO "No such tag in %s\n", __func__);
- rc = TMF_RESP_FUNC_FAILED;
- return rc;
- }
- rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
- pm8001_dev->sas_device, 0, tag);
+ rc = sas_execute_internal_abort_single(dev, tag, 0, NULL);
}
out:
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->slow_task == &slow_task)
+ task->slow_task = NULL;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
if (rc != TMF_RESP_FUNC_COMPLETE)
- pm8001_printk("rc= %d\n", rc);
+ pm8001_info(pm8001_ha, "rc= %d\n", rc);
return rc;
}
-int pm8001_abort_task_set(struct domain_device *dev, u8 *lun)
+int pm8001_clear_task_set(struct domain_device *dev, u8 *lun)
{
- int rc = TMF_RESP_FUNC_FAILED;
- struct pm8001_tmf_task tmf_task;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
- tmf_task.tmf = TMF_ABORT_TASK_SET;
- rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
- return rc;
+ pm8001_dbg(pm8001_ha, EH, "I_T_L_Q clear task set[%x]\n",
+ pm8001_dev->device_id);
+ return sas_clear_task_set(dev, lun);
}
-int pm8001_clear_aca(struct domain_device *dev, u8 *lun)
+void pm8001_port_formed(struct asd_sas_phy *sas_phy)
{
- int rc = TMF_RESP_FUNC_FAILED;
- struct pm8001_tmf_task tmf_task;
-
- tmf_task.tmf = TMF_CLEAR_ACA;
- rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
-
- return rc;
+ struct sas_ha_struct *sas_ha = sas_phy->ha;
+ struct pm8001_hba_info *pm8001_ha = sas_ha->lldd_ha;
+ struct pm8001_phy *phy = sas_phy->lldd_phy;
+ struct asd_sas_port *sas_port = sas_phy->port;
+ struct pm8001_port *port = phy->port;
+
+ if (!sas_port) {
+ pm8001_dbg(pm8001_ha, FAIL, "Received null port\n");
+ return;
+ }
+ sas_port->lldd_port = port;
}
-int pm8001_clear_task_set(struct domain_device *dev, u8 *lun)
+void pm8001_setds_completion(struct domain_device *dev)
{
- int rc = TMF_RESP_FUNC_FAILED;
- struct pm8001_tmf_task tmf_task;
- struct pm8001_device *pm8001_dev = dev->lldd_dev;
struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ DECLARE_COMPLETION_ONSTACK(completion_setstate);
- PM8001_EH_DBG(pm8001_ha,
- pm8001_printk("I_T_L_Q clear task set[%x]\n",
- pm8001_dev->device_id));
- tmf_task.tmf = TMF_CLEAR_TASK_SET;
- rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
- return rc;
+ if (pm8001_ha->chip_id != chip_8001) {
+ pm8001_dev->setds_completion = &completion_setstate;
+ PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+ pm8001_dev, DS_OPERATIONAL);
+ wait_for_completion(&completion_setstate);
+ }
}
+void pm8001_tmf_aborted(struct sas_task *task)
+{
+ struct pm8001_ccb_info *ccb = task->lldd_task;
+
+ if (ccb)
+ ccb->task = NULL;
+}
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index aa05e661d113..b63b6ffcaaf5 100644
--- a/drivers/scsi/pm8001/pm8001_sas.h
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -1,5 +1,5 @@
/*
- * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ * PMC-Sierra PM8001/8081/8088/8089 SAS/SATA based host adapters driver
*
* Copyright (c) 2008-2009 USI Co., Ltd.
* All rights reserved.
@@ -54,11 +54,12 @@
#include <scsi/libsas.h>
#include <scsi/scsi_tcq.h>
#include <scsi/sas_ata.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
+#include <linux/blk-mq.h>
#include "pm8001_defs.h"
-#define DRV_NAME "pm8001"
-#define DRV_VERSION "0.1.36"
+#define DRV_NAME "pm80xx"
+#define DRV_VERSION "0.1.40"
#define PM8001_FAIL_LOGGING 0x01 /* Error message logging */
#define PM8001_INIT_LOGGING 0x02 /* driver init logging */
#define PM8001_DISC_LOGGING 0x04 /* discovery layer logging */
@@ -66,80 +67,130 @@
#define PM8001_EH_LOGGING 0x10 /* libsas EH function logging*/
#define PM8001_IOCTL_LOGGING 0x20 /* IOCTL message logging */
#define PM8001_MSG_LOGGING 0x40 /* misc message logging */
-#define pm8001_printk(format, arg...) printk(KERN_INFO "%s %d:" format,\
- __func__, __LINE__, ## arg)
-#define PM8001_CHECK_LOGGING(HBA, LEVEL, CMD) \
-do { \
- if (unlikely(HBA->logging_level & LEVEL)) \
- do { \
- CMD; \
- } while (0); \
-} while (0);
+#define PM8001_DEV_LOGGING 0x80 /* development message logging */
+#define PM8001_DEVIO_LOGGING 0x100 /* development io message logging */
+#define PM8001_IOERR_LOGGING 0x200 /* development io err message logging */
+#define PM8001_EVENT_LOGGING 0x400 /* HW event logging */
-#define PM8001_EH_DBG(HBA, CMD) \
- PM8001_CHECK_LOGGING(HBA, PM8001_EH_LOGGING, CMD)
+#define pm8001_info(HBA, fmt, ...) \
+ pr_info("%s:: %s %d: " fmt, \
+ (HBA)->name, __func__, __LINE__, ##__VA_ARGS__)
-#define PM8001_INIT_DBG(HBA, CMD) \
- PM8001_CHECK_LOGGING(HBA, PM8001_INIT_LOGGING, CMD)
+#define pm8001_dbg(HBA, level, fmt, ...) \
+do { \
+ if (unlikely((HBA)->logging_level & PM8001_##level##_LOGGING)) \
+ pm8001_info(HBA, fmt, ##__VA_ARGS__); \
+} while (0)
-#define PM8001_DISC_DBG(HBA, CMD) \
- PM8001_CHECK_LOGGING(HBA, PM8001_DISC_LOGGING, CMD)
+extern bool pm8001_use_msix;
-#define PM8001_IO_DBG(HBA, CMD) \
- PM8001_CHECK_LOGGING(HBA, PM8001_IO_LOGGING, CMD)
-
-#define PM8001_FAIL_DBG(HBA, CMD) \
- PM8001_CHECK_LOGGING(HBA, PM8001_FAIL_LOGGING, CMD)
-
-#define PM8001_IOCTL_DBG(HBA, CMD) \
- PM8001_CHECK_LOGGING(HBA, PM8001_IOCTL_LOGGING, CMD)
-
-#define PM8001_MSG_DBG(HBA, CMD) \
- PM8001_CHECK_LOGGING(HBA, PM8001_MSG_LOGGING, CMD)
-
-
-#define PM8001_USE_TASKLET
-#define PM8001_USE_MSIX
-#define PM8001_READ_VPD
-
-
-#define DEV_IS_EXPANDER(type) ((type == EDGE_DEV) || (type == FANOUT_DEV))
+#define IS_SPCV_12G(dev) ((dev->device == 0X8074) \
+ || (dev->device == 0X8076) \
+ || (dev->device == 0X8077) \
+ || (dev->device == 0X8070) \
+ || (dev->device == 0X8072))
#define PM8001_NAME_LENGTH 32/* generic length of strings */
extern struct list_head hba_list;
extern const struct pm8001_dispatch pm8001_8001_dispatch;
+extern const struct pm8001_dispatch pm8001_80xx_dispatch;
+
+extern uint pcs_event_log_severity;
struct pm8001_hba_info;
struct pm8001_ccb_info;
struct pm8001_device;
-/* define task management IU */
-struct pm8001_tmf_task {
- u8 tmf;
- u32 tag_of_task_to_be_managed;
-};
+
struct pm8001_ioctl_payload {
u32 signature;
u16 major_function;
u16 minor_function;
- u16 length;
u16 status;
u16 offset;
u16 id;
+ u32 wr_length;
+ u32 rd_length;
u8 *func_specific;
};
+#define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF
+#define MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
+#define MPI_FATAL_EDUMP_TABLE_LO_OFFSET 0x00 /* HNFBUFL */
+#define MPI_FATAL_EDUMP_TABLE_HI_OFFSET 0x04 /* HNFBUFH */
+#define MPI_FATAL_EDUMP_TABLE_LENGTH 0x08 /* HNFBLEN */
+#define MPI_FATAL_EDUMP_TABLE_HANDSHAKE 0x0C /* FDDHSHK */
+#define MPI_FATAL_EDUMP_TABLE_STATUS 0x10 /* FDDTSTAT */
+#define MPI_FATAL_EDUMP_TABLE_ACCUM_LEN 0x14 /* ACCDDLEN */
+#define MPI_FATAL_EDUMP_TABLE_TOTAL_LEN 0x18 /* TOTALLEN */
+#define MPI_FATAL_EDUMP_TABLE_SIGNATURE 0x1C /* SIGNITURE */
+#define MPI_FATAL_EDUMP_HANDSHAKE_RDY 0x1
+#define MPI_FATAL_EDUMP_HANDSHAKE_BUSY 0x0
+#define MPI_FATAL_EDUMP_TABLE_STAT_RSVD 0x0
+#define MPI_FATAL_EDUMP_TABLE_STAT_DMA_FAILED 0x1
+#define MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_MORE_DATA 0x2
+#define MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE 0x3
+#define TYPE_GSM_SPACE 1
+#define TYPE_QUEUE 2
+#define TYPE_FATAL 3
+#define TYPE_NON_FATAL 4
+#define TYPE_INBOUND 1
+#define TYPE_OUTBOUND 2
+struct forensic_data {
+ u32 data_type;
+ union {
+ struct {
+ u32 direct_len;
+ u32 direct_offset;
+ void *direct_data;
+ } gsm_buf;
+ struct {
+ u16 queue_type;
+ u16 queue_index;
+ u32 direct_len;
+ void *direct_data;
+ } queue_buf;
+ struct {
+ u32 direct_len;
+ u32 direct_offset;
+ u32 read_len;
+ void *direct_data;
+ } data_buf;
+ };
+};
+
+/* bit31-26 - mask bar */
+#define SCRATCH_PAD0_BAR_MASK 0xFC000000
+/* bit25-0 - offset mask */
+#define SCRATCH_PAD0_OFFSET_MASK 0x03FFFFFF
+/* if AAP error state */
+#define SCRATCH_PAD0_AAPERR_MASK 0xFFFFFFFF
+/* Inbound doorbell bit7 */
+#define SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP 0x80
+/* Inbound doorbell bit7 SPCV */
+#define SPCV_MSGU_CFG_TABLE_TRANSFER_DEBUG_INFO 0x80
+#define MAIN_MERRDCTO_MERRDCES 0xA0/* DWORD 0x28) */
+
+/**
+ * enum fatal_error_reporter: Indicates the originator of the fatal error
+ */
+enum fatal_error_reporter {
+ REPORTER_DRIVER,
+ REPORTER_FIRMWARE,
+};
+
struct pm8001_dispatch {
char *name;
int (*chip_init)(struct pm8001_hba_info *pm8001_ha);
- int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha, u32 signature);
+ void (*chip_post_init)(struct pm8001_hba_info *pm8001_ha);
+ int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha);
void (*chip_rst)(struct pm8001_hba_info *pm8001_ha);
int (*chip_ioremap)(struct pm8001_hba_info *pm8001_ha);
void (*chip_iounmap)(struct pm8001_hba_info *pm8001_ha);
- irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha);
- u32 (*is_our_interupt)(struct pm8001_hba_info *pm8001_ha);
- int (*isr_process_oq)(struct pm8001_hba_info *pm8001_ha);
- void (*interrupt_enable)(struct pm8001_hba_info *pm8001_ha);
- void (*interrupt_disable)(struct pm8001_hba_info *pm8001_ha);
+ irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha, u8 vec);
+ u32 (*is_our_interrupt)(struct pm8001_hba_info *pm8001_ha);
+ int (*isr_process_oq)(struct pm8001_hba_info *pm8001_ha, u8 vec);
+ void (*interrupt_enable)(struct pm8001_hba_info *pm8001_ha, u8 vec);
+ void (*interrupt_disable)(struct pm8001_hba_info *pm8001_ha, u8 vec);
void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
int (*smp_req)(struct pm8001_hba_info *pm8001_ha,
struct pm8001_ccb_info *ccb);
@@ -155,10 +206,9 @@ struct pm8001_dispatch {
int (*phy_ctl_req)(struct pm8001_hba_info *pm8001_ha,
u32 phy_id, u32 phy_op);
int (*task_abort)(struct pm8001_hba_info *pm8001_ha,
- struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag,
- u32 cmd_tag);
+ struct pm8001_ccb_info *ccb);
int (*ssp_tm_req)(struct pm8001_hba_info *pm8001_ha,
- struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf);
+ struct pm8001_ccb_info *ccb, struct sas_tmf_task *tmf);
int (*get_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
int (*set_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
int (*fw_flash_update_req)(struct pm8001_hba_info *pm8001_ha,
@@ -170,9 +220,14 @@ struct pm8001_dispatch {
int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
u32 state);
int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
+ int (*fatal_errors)(struct pm8001_hba_info *pm8001_ha);
+ void (*hw_event_ack_req)(struct pm8001_hba_info *pm8001_ha,
+ u32 Qnum, u32 SEA, u32 port_id, u32 phyId, u32 param0,
+ u32 param1);
};
struct pm8001_chip_info {
+ u32 encrypt;
u32 n_phy;
const struct pm8001_dispatch *dispatch;
};
@@ -181,8 +236,9 @@ struct pm8001_chip_info {
struct pm8001_port {
struct asd_sas_port sas_port;
u8 port_attached;
- u8 wide_port_phymap;
+ u16 wide_port_phymap;
u8 port_state;
+ u8 port_id;
struct list_head list;
};
@@ -201,17 +257,24 @@ struct pm8001_phy {
u8 phy_state;
enum sas_linkrate minimum_linkrate;
enum sas_linkrate maximum_linkrate;
+ struct completion *reset_completion;
+ bool port_reset_status;
+ bool reset_success;
};
+/* port reset status */
+#define PORT_RESET_SUCCESS 0x00
+#define PORT_RESET_TMO 0x01
+
struct pm8001_device {
- enum sas_dev_type dev_type;
+ enum sas_device_type dev_type;
struct domain_device *sas_device;
u32 attached_phy;
u32 id;
struct completion *dcompletion;
struct completion *setds_completion;
u32 device_id;
- u32 running_req;
+ atomic_t running_req;
};
struct pm8001_prd_imt {
@@ -227,14 +290,14 @@ struct pm8001_prd {
* CCB(Command Control Block)
*/
struct pm8001_ccb_info {
- struct list_head entry;
struct sas_task *task;
u32 n_elem;
u32 ccb_tag;
dma_addr_t ccb_dma_handle;
struct pm8001_device *device;
- struct pm8001_prd buf_prd[PM8001_MAX_DMA_SG];
+ struct pm8001_prd *buf_prd;
struct fw_control_ex *fw_control_context;
+ u8 open_retry;
};
struct mpi_mem {
@@ -255,7 +318,20 @@ struct mpi_mem_req {
struct mpi_mem region[USI_MAX_MEMCNT];
};
-struct main_cfg_table {
+struct encrypt {
+ u32 cipher_mode;
+ u32 sec_mode;
+ u32 status;
+ u32 flag;
+};
+
+struct sas_phy_attribute_table {
+ u32 phystart1_16[16];
+ u32 outbound_hw_event_pid1_16[16];
+};
+
+union main_cfg_table {
+ struct {
u32 signature;
u32 interface_rev;
u32 firmware_rev;
@@ -291,19 +367,72 @@ struct main_cfg_table {
u32 fatal_err_dump_length1;
u32 hda_mode_flag;
u32 anolog_setup_table_offset;
+ u32 rsvd[4];
+ } pm8001_tbl;
+
+ struct {
+ u32 signature;
+ u32 interface_rev;
+ u32 firmware_rev;
+ u32 max_out_io;
+ u32 max_sgl;
+ u32 ctrl_cap_flag;
+ u32 gst_offset;
+ u32 inbound_queue_offset;
+ u32 outbound_queue_offset;
+ u32 inbound_q_nppd_hppd;
+ u32 rsvd[8];
+ u32 crc_core_dump;
+ u32 rsvd1;
+ u32 upper_event_log_addr;
+ u32 lower_event_log_addr;
+ u32 event_log_size;
+ u32 event_log_severity;
+ u32 upper_pcs_event_log_addr;
+ u32 lower_pcs_event_log_addr;
+ u32 pcs_event_log_size;
+ u32 pcs_event_log_severity;
+ u32 fatal_err_interrupt;
+ u32 fatal_err_dump_offset0;
+ u32 fatal_err_dump_length0;
+ u32 fatal_err_dump_offset1;
+ u32 fatal_err_dump_length1;
+ u32 gpio_led_mapping;
+ u32 analog_setup_table_offset;
+ u32 int_vec_table_offset;
+ u32 phy_attr_table_offset;
+ u32 port_recovery_timer;
+ u32 interrupt_reassertion_delay;
+ u32 fatal_n_non_fatal_dump; /* 0x28 */
+ u32 ila_version;
+ u32 inc_fw_version;
+ } pm80xx_tbl;
};
-struct general_status_table {
+
+union general_status_table {
+ struct {
u32 gst_len_mpistate;
u32 iq_freeze_state0;
u32 iq_freeze_state1;
u32 msgu_tcnt;
u32 iop_tcnt;
- u32 reserved;
+ u32 rsvd;
u32 phy_state[8];
- u32 reserved1;
- u32 reserved2;
- u32 reserved3;
+ u32 gpio_input_val;
+ u32 rsvd1[2];
u32 recover_err_info[8];
+ } pm8001_tbl;
+ struct {
+ u32 gst_len_mpistate;
+ u32 iq_freeze_state0;
+ u32 iq_freeze_state1;
+ u32 msgu_tcnt;
+ u32 iop_tcnt;
+ u32 rsvd[9];
+ u32 gpio_input_val;
+ u32 rsvd1[2];
+ u32 recover_err_info[8];
+ } pm80xx_tbl;
};
struct inbound_queue_table {
u32 element_pri_size_cnt;
@@ -319,6 +448,7 @@ struct inbound_queue_table {
u32 reserved;
__le32 consumer_index;
u32 producer_idx;
+ spinlock_t iq_lock;
};
struct outbound_queue_table {
u32 element_size_cnt;
@@ -335,54 +465,92 @@ struct outbound_queue_table {
u32 dinterrup_to_pci_offset;
__le32 producer_index;
u32 consumer_idx;
+ spinlock_t oq_lock;
+ unsigned long lock_flags;
};
struct pm8001_hba_memspace {
void __iomem *memvirtaddr;
u64 membase;
u32 memsize;
};
+struct isr_param {
+ struct pm8001_hba_info *drv_inst;
+ u32 irq_id;
+};
struct pm8001_hba_info {
char name[PM8001_NAME_LENGTH];
struct list_head list;
unsigned long flags;
spinlock_t lock;/* host-wide lock */
+ spinlock_t bitmap_lock;
struct pci_dev *pdev;/* our device */
struct device *dev;
struct pm8001_hba_memspace io_mem[6];
struct mpi_mem_req memoryMap;
+ struct encrypt encrypt_info; /* support encryption */
+ struct forensic_data forensic_info;
+ u32 fatal_bar_loc;
+ u32 forensic_last_offset;
+ u32 fatal_forensic_shift_offset;
+ u32 forensic_fatal_step;
+ u32 forensic_preserved_accumulated_transfer;
+ u32 evtlog_ib_offset;
+ u32 evtlog_ob_offset;
void __iomem *msg_unit_tbl_addr;/*Message Unit Table Addr*/
void __iomem *main_cfg_tbl_addr;/*Main Config Table Addr*/
void __iomem *general_stat_tbl_addr;/*General Status Table Addr*/
void __iomem *inbnd_q_tbl_addr;/*Inbound Queue Config Table Addr*/
void __iomem *outbnd_q_tbl_addr;/*Outbound Queue Config Table Addr*/
- struct main_cfg_table main_cfg_tbl;
- struct general_status_table gs_tbl;
+ void __iomem *pspa_q_tbl_addr;
+ /*MPI SAS PHY attributes Queue Config Table Addr*/
+ void __iomem *ivt_tbl_addr; /*MPI IVT Table Addr */
+ void __iomem *fatal_tbl_addr; /*MPI IVT Table Addr */
+ union main_cfg_table main_cfg_tbl;
+ union general_status_table gs_tbl;
struct inbound_queue_table inbnd_q_tbl[PM8001_MAX_INB_NUM];
struct outbound_queue_table outbnd_q_tbl[PM8001_MAX_OUTB_NUM];
+ struct sas_phy_attribute_table phy_attr_table;
+ /* MPI SAS PHY attributes */
u8 sas_addr[SAS_ADDR_SIZE];
struct sas_ha_struct *sas;/* SCSI/SAS glue */
struct Scsi_Host *shost;
u32 chip_id;
const struct pm8001_chip_info *chip;
struct completion *nvmd_completion;
- int tags_num;
- unsigned long *tags;
+ unsigned long *rsvd_tags;
struct pm8001_phy phy[PM8001_MAX_PHYS];
struct pm8001_port port[PM8001_MAX_PHYS];
u32 id;
u32 irq;
+ u32 iomb_size; /* SPC and SPCV IOMB size */
struct pm8001_device *devices;
struct pm8001_ccb_info *ccb_info;
-#ifdef PM8001_USE_MSIX
- struct msix_entry msix_entries[16];/*for msi-x interrupt*/
+ u32 ccb_count;
+
+ bool use_msix;
int number_of_intr;/*will be used in remove()*/
-#endif
-#ifdef PM8001_USE_TASKLET
- struct tasklet_struct tasklet;
-#endif
+ char intr_drvname[PM8001_MAX_MSIX_VEC]
+ [PM8001_NAME_LENGTH+1+3+1];
+ struct tasklet_struct tasklet[PM8001_MAX_MSIX_VEC];
u32 logging_level;
+ u32 link_rate;
u32 fw_status;
+ u32 smp_exp_mode;
+ bool controller_fatal_error;
const struct firmware *fw_image;
+ struct isr_param irq_vector[PM8001_MAX_MSIX_VEC];
+ u32 non_fatal_count;
+ u32 non_fatal_read_length;
+ u32 max_q_num;
+ u32 ib_offset;
+ u32 ob_offset;
+ u32 ci_offset;
+ u32 pi_offset;
+ u32 max_memcnt;
+ u32 iop_log_start;
+ u32 iop_log_end;
+ u32 iop_log_count;
+ struct mutex iop_log_lock;
};
struct pm8001_work {
@@ -418,6 +586,13 @@ struct pm8001_fw_image_header {
#define FLASH_UPDATE_DNLD_NOT_SUPPORTED 0x10
#define FLASH_UPDATE_DISABLED 0x11
+/* Device states */
+#define DS_OPERATIONAL 0x01
+#define DS_PORT_IN_RESET 0x02
+#define DS_IN_RECOVERY 0x03
+#define DS_IN_ERROR 0x04
+#define DS_NON_OPERATIONAL 0x07
+
/**
* brief param structure for firmware flash update.
*/
@@ -440,7 +615,7 @@ struct fw_control_info {
operations.*/
u32 reserved;/* padding required for 64 bit
alignment */
- u8 buffer[1];/* Start of buffer */
+ u8 buffer[];/* Start of buffer */
};
struct fw_control_ex {
struct fw_control_info *fw_control;
@@ -464,35 +639,170 @@ extern struct workqueue_struct *pm8001_wq;
/******************** function prototype *********************/
int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out);
-void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha);
u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag);
-void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx);
void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
- struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx);
+ struct pm8001_ccb_info *ccb);
int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
void *funcdata);
-int pm8001_slave_alloc(struct scsi_device *scsi_dev);
-int pm8001_slave_configure(struct scsi_device *sdev);
void pm8001_scan_start(struct Scsi_Host *shost);
int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time);
-int pm8001_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags);
+int pm8001_queue_command(struct sas_task *task, gfp_t gfp_flags);
int pm8001_abort_task(struct sas_task *task);
-int pm8001_abort_task_set(struct domain_device *dev, u8 *lun);
-int pm8001_clear_aca(struct domain_device *dev, u8 *lun);
int pm8001_clear_task_set(struct domain_device *dev, u8 *lun);
int pm8001_dev_found(struct domain_device *dev);
void pm8001_dev_gone(struct domain_device *dev);
int pm8001_lu_reset(struct domain_device *dev, u8 *lun);
int pm8001_I_T_nexus_reset(struct domain_device *dev);
+int pm8001_I_T_nexus_event_handler(struct domain_device *dev);
int pm8001_query_task(struct sas_task *task);
+void pm8001_port_formed(struct asd_sas_phy *sas_phy);
+void pm8001_open_reject_retry(
+ struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task_to_close,
+ struct pm8001_device *device_to_close);
int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo,
u32 mem_size, u32 align);
-
+void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha);
+int pm8001_mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
+ u32 q_index, u32 opCode, void *payload, size_t nb,
+ u32 responseQueue);
+int pm8001_mpi_msg_free_get(struct inbound_queue_table *circularQ,
+ u16 messageSize, void **messagePtr);
+u32 pm8001_mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
+ struct outbound_queue_table *circularQ, u8 bc);
+u32 pm8001_mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
+ struct outbound_queue_table *circularQ,
+ void **messagePtr1, u8 *pBC);
+int pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 state);
+int pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
+ void *payload);
+int pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
+ void *fw_flash_updata_info, u32 tag);
+int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha, void *payload);
+int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha, void *payload);
+int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb,
+ struct sas_tmf_task *tmf);
+int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb);
+int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha, u32 device_id);
+void pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd);
+void pm8001_work_fn(struct work_struct *work);
+int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha,
+ void *data, int handler);
+void pm8001_mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb);
+void pm8001_mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb);
+void pm8001_mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb);
+int pm8001_mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha,
+ void *piomb);
+void pm8001_get_lrate_mode(struct pm8001_phy *phy, u8 link_rate);
+void pm8001_get_attached_sas_addr(struct pm8001_phy *phy, u8 *sas_addr);
+void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i);
+int pm8001_mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb);
+int pm8001_mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb);
+int pm8001_mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb);
+int pm8001_mpi_general_event(struct pm8001_hba_info *pm8001_ha, void *piomb);
+int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb);
+void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag);
+struct pm8001_device *pm8001_find_dev(struct pm8001_hba_info *pm8001_ha,
+ u32 device_id);
+int pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha);
+
+int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue);
+void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha,
+ u32 length, u8 *buf);
+void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha,
+ u32 phy, u32 length, u32 *buf);
+int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue);
+ssize_t pm80xx_get_fatal_dump(struct device *cdev,
+ struct device_attribute *attr, char *buf);
+ssize_t pm80xx_get_non_fatal_dump(struct device *cdev,
+ struct device_attribute *attr, char *buf);
+ssize_t pm8001_get_gsm_dump(struct device *cdev, u32, char *buf);
+int pm80xx_fatal_errors(struct pm8001_hba_info *pm8001_ha);
+void pm80xx_fatal_error_uevent_emit(struct pm8001_hba_info *pm8001_ha,
+ enum fatal_error_reporter error_reporter);
+void pm8001_free_dev(struct pm8001_device *pm8001_dev);
/* ctl shared API */
-extern struct device_attribute *pm8001_host_attrs[];
+extern const struct attribute_group *pm8001_host_groups[];
+extern const struct attribute_group *pm8001_sdev_groups[];
+
+#define PM8001_INVALID_TAG ((u32)-1)
+
+/*
+ * Allocate a new tag and return the corresponding ccb after initializing it.
+ */
+static inline struct pm8001_ccb_info *
+pm8001_ccb_alloc(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *dev, struct sas_task *task)
+{
+ struct pm8001_ccb_info *ccb;
+ struct request *rq = NULL;
+ u32 tag;
+
+ if (task)
+ rq = sas_task_find_rq(task);
+
+ if (rq) {
+ tag = rq->tag + PM8001_RESERVE_SLOT;
+ } else if (pm8001_tag_alloc(pm8001_ha, &tag)) {
+ pm8001_dbg(pm8001_ha, FAIL, "Failed to allocate a tag\n");
+ return NULL;
+ }
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->task = task;
+ ccb->n_elem = 0;
+ ccb->ccb_tag = tag;
+ ccb->device = dev;
+ ccb->fw_control_context = NULL;
+ ccb->open_retry = 0;
+
+ return ccb;
+}
+
+/*
+ * Free the tag of an initialized ccb.
+ */
+static inline void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ u32 tag = ccb->ccb_tag;
+
+ /*
+ * Cleanup the ccb to make sure that a manual scan of the adapter
+ * ccb_info array can detect ccb's that are in use.
+ * C.f. pm8001_open_reject_retry()
+ */
+ ccb->task = NULL;
+ ccb->ccb_tag = PM8001_INVALID_TAG;
+ ccb->device = NULL;
+ ccb->fw_control_context = NULL;
+
+ pm8001_tag_free(pm8001_ha, tag);
+}
+
+static inline void pm8001_ccb_task_free_done(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ struct sas_task *task = ccb->task;
+
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ smp_mb(); /*in order to force CPU ordering*/
+ task->task_done(task);
+}
+void pm8001_setds_completion(struct domain_device *dev);
+void pm8001_tmf_aborted(struct sas_task *task);
+void pm80xx_show_pending_commands(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *dev);
+u32 pm80xx_get_local_phy_id(struct domain_device *dev);
#endif
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c
new file mode 100644
index 000000000000..31960b72c1e9
--- /dev/null
+++ b/drivers/scsi/pm8001/pm80xx_hwi.c
@@ -0,0 +1,4999 @@
+/*
+ * PMC-Sierra SPCv/ve 8088/8089 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 PMC-Sierra, Inc.,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+ #include <linux/slab.h>
+ #include "pm8001_sas.h"
+ #include "pm80xx_hwi.h"
+ #include "pm8001_chips.h"
+ #include "pm8001_ctl.h"
+#include "pm80xx_tracepoints.h"
+
+#define SMP_DIRECT 1
+#define SMP_INDIRECT 2
+
+
+int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shift_value)
+{
+ u32 reg_val;
+ unsigned long start;
+ pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, shift_value);
+ /* confirm the setting is written */
+ start = jiffies + HZ; /* 1 sec */
+ do {
+ reg_val = pm8001_cr32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER);
+ } while ((reg_val != shift_value) && time_before(jiffies, start));
+ if (reg_val != shift_value) {
+ pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT:MEMBASE_II_SHIFT_REGISTER = 0x%x\n",
+ reg_val);
+ return -1;
+ }
+ return 0;
+}
+
+static void pm80xx_pci_mem_copy(struct pm8001_hba_info *pm8001_ha, u32 soffset,
+ __le32 *destination,
+ u32 dw_count, u32 bus_base_number)
+{
+ u32 index, value, offset;
+
+ for (index = 0; index < dw_count; index += 4, destination++) {
+ offset = (soffset + index);
+ if (offset < (64 * 1024)) {
+ value = pm8001_cr32(pm8001_ha, bus_base_number, offset);
+ *destination = cpu_to_le32(value);
+ }
+ }
+ return;
+}
+
+ssize_t pm80xx_get_fatal_dump(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ void __iomem *fatal_table_address = pm8001_ha->fatal_tbl_addr;
+ u32 accum_len, reg_val, index, *temp;
+ u32 status = 1;
+ unsigned long start;
+ u8 *direct_data;
+ char *fatal_error_data = buf;
+ u32 length_to_read;
+ u32 offset;
+
+ pm8001_ha->forensic_info.data_buf.direct_data = buf;
+ if (pm8001_ha->chip_id == chip_8001) {
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+ "Not supported for SPC controller");
+ return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
+ (char *)buf;
+ }
+ /* initialize variables for very first call from host application */
+ if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) {
+ pm8001_dbg(pm8001_ha, IO,
+ "forensic_info TYPE_NON_FATAL..............\n");
+ direct_data = (u8 *)fatal_error_data;
+ pm8001_ha->forensic_info.data_type = TYPE_NON_FATAL;
+ pm8001_ha->forensic_info.data_buf.direct_len = SYSFS_OFFSET;
+ pm8001_ha->forensic_info.data_buf.direct_offset = 0;
+ pm8001_ha->forensic_info.data_buf.read_len = 0;
+ pm8001_ha->forensic_preserved_accumulated_transfer = 0;
+
+ /* Write signature to fatal dump table */
+ pm8001_mw32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_SIGNATURE, 0x1234abcd);
+
+ pm8001_ha->forensic_info.data_buf.direct_data = direct_data;
+ pm8001_dbg(pm8001_ha, IO, "ossaHwCB: status1 %d\n", status);
+ pm8001_dbg(pm8001_ha, IO, "ossaHwCB: read_len 0x%x\n",
+ pm8001_ha->forensic_info.data_buf.read_len);
+ pm8001_dbg(pm8001_ha, IO, "ossaHwCB: direct_len 0x%x\n",
+ pm8001_ha->forensic_info.data_buf.direct_len);
+ pm8001_dbg(pm8001_ha, IO, "ossaHwCB: direct_offset 0x%x\n",
+ pm8001_ha->forensic_info.data_buf.direct_offset);
+ }
+ if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) {
+ /* start to get data */
+ /* Program the MEMBASE II Shifting Register with 0x00.*/
+ pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER,
+ pm8001_ha->fatal_forensic_shift_offset);
+ pm8001_ha->forensic_last_offset = 0;
+ pm8001_ha->forensic_fatal_step = 0;
+ pm8001_ha->fatal_bar_loc = 0;
+ }
+
+ /* Read until accum_len is retrieved */
+ accum_len = pm8001_mr32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_ACCUM_LEN);
+ /* Determine length of data between previously stored transfer length
+ * and current accumulated transfer length
+ */
+ length_to_read =
+ accum_len - pm8001_ha->forensic_preserved_accumulated_transfer;
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: accum_len 0x%x\n",
+ accum_len);
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: length_to_read 0x%x\n",
+ length_to_read);
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: last_offset 0x%x\n",
+ pm8001_ha->forensic_last_offset);
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: read_len 0x%x\n",
+ pm8001_ha->forensic_info.data_buf.read_len);
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv:: direct_len 0x%x\n",
+ pm8001_ha->forensic_info.data_buf.direct_len);
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv:: direct_offset 0x%x\n",
+ pm8001_ha->forensic_info.data_buf.direct_offset);
+
+ /* If accumulated length failed to read correctly fail the attempt.*/
+ if (accum_len == 0xFFFFFFFF) {
+ pm8001_dbg(pm8001_ha, IO,
+ "Possible PCI issue 0x%x not expected\n",
+ accum_len);
+ return status;
+ }
+ /* If accumulated length is zero fail the attempt */
+ if (accum_len == 0) {
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+ "%08x ", 0xFFFFFFFF);
+ return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
+ (char *)buf;
+ }
+ /* Accumulated length is good so start capturing the first data */
+ temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr;
+ if (pm8001_ha->forensic_fatal_step == 0) {
+moreData:
+ /* If data to read is less than SYSFS_OFFSET then reduce the
+ * length of dataLen
+ */
+ if (pm8001_ha->forensic_last_offset + SYSFS_OFFSET
+ > length_to_read) {
+ pm8001_ha->forensic_info.data_buf.direct_len =
+ length_to_read -
+ pm8001_ha->forensic_last_offset;
+ } else {
+ pm8001_ha->forensic_info.data_buf.direct_len =
+ SYSFS_OFFSET;
+ }
+ if (pm8001_ha->forensic_info.data_buf.direct_data) {
+ /* Data is in bar, copy to host memory */
+ pm80xx_pci_mem_copy(pm8001_ha,
+ pm8001_ha->fatal_bar_loc,
+ pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr,
+ pm8001_ha->forensic_info.data_buf.direct_len, 1);
+ }
+ pm8001_ha->fatal_bar_loc +=
+ pm8001_ha->forensic_info.data_buf.direct_len;
+ pm8001_ha->forensic_info.data_buf.direct_offset +=
+ pm8001_ha->forensic_info.data_buf.direct_len;
+ pm8001_ha->forensic_last_offset +=
+ pm8001_ha->forensic_info.data_buf.direct_len;
+ pm8001_ha->forensic_info.data_buf.read_len =
+ pm8001_ha->forensic_info.data_buf.direct_len;
+
+ if (pm8001_ha->forensic_last_offset >= length_to_read) {
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+ "%08x ", 3);
+ for (index = 0; index <
+ (pm8001_ha->forensic_info.data_buf.direct_len
+ / 4); index++) {
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(
+ pm8001_ha->forensic_info.data_buf.direct_data,
+ "%08x ", *(temp + index));
+ }
+
+ pm8001_ha->fatal_bar_loc = 0;
+ pm8001_ha->forensic_fatal_step = 1;
+ pm8001_ha->fatal_forensic_shift_offset = 0;
+ pm8001_ha->forensic_last_offset = 0;
+ status = 0;
+ offset = (int)
+ ((char *)pm8001_ha->forensic_info.data_buf.direct_data
+ - (char *)buf);
+ pm8001_dbg(pm8001_ha, IO,
+ "get_fatal_spcv:return1 0x%x\n", offset);
+ return (char *)pm8001_ha->
+ forensic_info.data_buf.direct_data -
+ (char *)buf;
+ }
+ if (pm8001_ha->fatal_bar_loc < (64 * 1024)) {
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(pm8001_ha->
+ forensic_info.data_buf.direct_data,
+ "%08x ", 2);
+ for (index = 0; index <
+ (pm8001_ha->forensic_info.data_buf.direct_len
+ / 4); index++) {
+ pm8001_ha->forensic_info.data_buf.direct_data
+ += sprintf(pm8001_ha->
+ forensic_info.data_buf.direct_data,
+ "%08x ", *(temp + index));
+ }
+ status = 0;
+ offset = (int)
+ ((char *)pm8001_ha->forensic_info.data_buf.direct_data
+ - (char *)buf);
+ pm8001_dbg(pm8001_ha, IO,
+ "get_fatal_spcv:return2 0x%x\n", offset);
+ return (char *)pm8001_ha->
+ forensic_info.data_buf.direct_data -
+ (char *)buf;
+ }
+
+ /* Increment the MEMBASE II Shifting Register value by 0x100.*/
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+ "%08x ", 2);
+ for (index = 0; index <
+ (pm8001_ha->forensic_info.data_buf.direct_len
+ / 4) ; index++) {
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(pm8001_ha->
+ forensic_info.data_buf.direct_data,
+ "%08x ", *(temp + index));
+ }
+ pm8001_ha->fatal_forensic_shift_offset += 0x100;
+ pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER,
+ pm8001_ha->fatal_forensic_shift_offset);
+ pm8001_ha->fatal_bar_loc = 0;
+ status = 0;
+ offset = (int)
+ ((char *)pm8001_ha->forensic_info.data_buf.direct_data
+ - (char *)buf);
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: return3 0x%x\n",
+ offset);
+ return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
+ (char *)buf;
+ }
+ if (pm8001_ha->forensic_fatal_step == 1) {
+ /* store previous accumulated length before triggering next
+ * accumulated length update
+ */
+ pm8001_ha->forensic_preserved_accumulated_transfer =
+ pm8001_mr32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_ACCUM_LEN);
+
+ /* continue capturing the fatal log until Dump status is 0x3 */
+ if (pm8001_mr32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_STATUS) <
+ MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE) {
+
+ /* reset fddstat bit by writing to zero*/
+ pm8001_mw32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_STATUS, 0x0);
+
+ /* set dump control value to '1' so that new data will
+ * be transferred to shared memory
+ */
+ pm8001_mw32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_HANDSHAKE,
+ MPI_FATAL_EDUMP_HANDSHAKE_RDY);
+
+ /*Poll FDDHSHK until clear */
+ start = jiffies + (2 * HZ); /* 2 sec */
+
+ do {
+ reg_val = pm8001_mr32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_HANDSHAKE);
+ } while ((reg_val) && time_before(jiffies, start));
+
+ if (reg_val != 0) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "TIMEOUT:MPI_FATAL_EDUMP_TABLE_HDSHAKE 0x%x\n",
+ reg_val);
+ /* Fail the dump if a timeout occurs */
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(
+ pm8001_ha->forensic_info.data_buf.direct_data,
+ "%08x ", 0xFFFFFFFF);
+ return((char *)
+ pm8001_ha->forensic_info.data_buf.direct_data
+ - (char *)buf);
+ }
+ /* Poll status register until set to 2 or
+ * 3 for up to 2 seconds
+ */
+ start = jiffies + (2 * HZ); /* 2 sec */
+
+ do {
+ reg_val = pm8001_mr32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_STATUS);
+ } while (((reg_val != 2) && (reg_val != 3)) &&
+ time_before(jiffies, start));
+
+ if (reg_val < 2) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "TIMEOUT:MPI_FATAL_EDUMP_TABLE_STATUS = 0x%x\n",
+ reg_val);
+ /* Fail the dump if a timeout occurs */
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(
+ pm8001_ha->forensic_info.data_buf.direct_data,
+ "%08x ", 0xFFFFFFFF);
+ return((char *)pm8001_ha->forensic_info.data_buf.direct_data -
+ (char *)buf);
+ }
+ /* reset fatal_forensic_shift_offset back to zero and reset MEMBASE 2 register to zero */
+ pm8001_ha->fatal_forensic_shift_offset = 0; /* location in 64k region */
+ pm8001_cw32(pm8001_ha, 0,
+ MEMBASE_II_SHIFT_REGISTER,
+ pm8001_ha->fatal_forensic_shift_offset);
+ }
+ /* Read the next block of the debug data.*/
+ length_to_read = pm8001_mr32(fatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_ACCUM_LEN) -
+ pm8001_ha->forensic_preserved_accumulated_transfer;
+ if (length_to_read != 0x0) {
+ pm8001_ha->forensic_fatal_step = 0;
+ goto moreData;
+ } else {
+ pm8001_ha->forensic_info.data_buf.direct_data +=
+ sprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+ "%08x ", 4);
+ pm8001_ha->forensic_info.data_buf.read_len = 0xFFFFFFFF;
+ pm8001_ha->forensic_info.data_buf.direct_len = 0;
+ pm8001_ha->forensic_info.data_buf.direct_offset = 0;
+ pm8001_ha->forensic_info.data_buf.read_len = 0;
+ }
+ }
+ offset = (int)((char *)pm8001_ha->forensic_info.data_buf.direct_data
+ - (char *)buf);
+ pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: return4 0x%x\n", offset);
+ return ((char *)pm8001_ha->forensic_info.data_buf.direct_data -
+ (char *)buf);
+}
+
+/* pm80xx_get_non_fatal_dump - dump the nonfatal data from the dma
+ * location by the firmware.
+ */
+ssize_t pm80xx_get_non_fatal_dump(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ void __iomem *nonfatal_table_address = pm8001_ha->fatal_tbl_addr;
+ u32 accum_len = 0;
+ u32 total_len = 0;
+ u32 reg_val = 0;
+ u32 *temp = NULL;
+ u32 index = 0;
+ u32 output_length;
+ unsigned long start = 0;
+ char *buf_copy = buf;
+
+ temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr;
+ if (++pm8001_ha->non_fatal_count == 1) {
+ if (pm8001_ha->chip_id == chip_8001) {
+ snprintf(pm8001_ha->forensic_info.data_buf.direct_data,
+ PAGE_SIZE, "Not supported for SPC controller");
+ return 0;
+ }
+ pm8001_dbg(pm8001_ha, IO, "forensic_info TYPE_NON_FATAL...\n");
+ /*
+ * Step 1: Write the host buffer parameters in the MPI Fatal and
+ * Non-Fatal Error Dump Capture Table.This is the buffer
+ * where debug data will be DMAed to.
+ */
+ pm8001_mw32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_LO_OFFSET,
+ pm8001_ha->memoryMap.region[FORENSIC_MEM].phys_addr_lo);
+
+ pm8001_mw32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_HI_OFFSET,
+ pm8001_ha->memoryMap.region[FORENSIC_MEM].phys_addr_hi);
+
+ pm8001_mw32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_LENGTH, SYSFS_OFFSET);
+
+ /* Optionally, set the DUMPCTRL bit to 1 if the host
+ * keeps sending active I/Os while capturing the non-fatal
+ * debug data. Otherwise, leave this bit set to zero
+ */
+ pm8001_mw32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_HANDSHAKE, MPI_FATAL_EDUMP_HANDSHAKE_RDY);
+
+ /*
+ * Step 2: Clear Accumulative Length of Debug Data Transferred
+ * [ACCDDLEN] field in the MPI Fatal and Non-Fatal Error Dump
+ * Capture Table to zero.
+ */
+ pm8001_mw32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_ACCUM_LEN, 0);
+
+ /* initiallize previous accumulated length to 0 */
+ pm8001_ha->forensic_preserved_accumulated_transfer = 0;
+ pm8001_ha->non_fatal_read_length = 0;
+ }
+
+ total_len = pm8001_mr32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_TOTAL_LEN);
+ /*
+ * Step 3:Clear Fatal/Non-Fatal Debug Data Transfer Status [FDDTSTAT]
+ * field and then request that the SPCv controller transfer the debug
+ * data by setting bit 7 of the Inbound Doorbell Set Register.
+ */
+ pm8001_mw32(nonfatal_table_address, MPI_FATAL_EDUMP_TABLE_STATUS, 0);
+ pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET,
+ SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP);
+
+ /*
+ * Step 4.1: Read back the Inbound Doorbell Set Register (by polling for
+ * 2 seconds) until register bit 7 is cleared.
+ * This step only indicates the request is accepted by the controller.
+ */
+ start = jiffies + (2 * HZ); /* 2 sec */
+ do {
+ reg_val = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET) &
+ SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP;
+ } while ((reg_val != 0) && time_before(jiffies, start));
+
+ /* Step 4.2: To check the completion of the transfer, poll the Fatal/Non
+ * Fatal Debug Data Transfer Status [FDDTSTAT] field for 2 seconds in
+ * the MPI Fatal and Non-Fatal Error Dump Capture Table.
+ */
+ start = jiffies + (2 * HZ); /* 2 sec */
+ do {
+ reg_val = pm8001_mr32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_STATUS);
+ } while ((!reg_val) && time_before(jiffies, start));
+
+ if ((reg_val == 0x00) ||
+ (reg_val == MPI_FATAL_EDUMP_TABLE_STAT_DMA_FAILED) ||
+ (reg_val > MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE)) {
+ pm8001_ha->non_fatal_read_length = 0;
+ buf_copy += snprintf(buf_copy, PAGE_SIZE, "%08x ", 0xFFFFFFFF);
+ pm8001_ha->non_fatal_count = 0;
+ return (buf_copy - buf);
+ } else if (reg_val ==
+ MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_MORE_DATA) {
+ buf_copy += snprintf(buf_copy, PAGE_SIZE, "%08x ", 2);
+ } else if ((reg_val == MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE) ||
+ (pm8001_ha->non_fatal_read_length >= total_len)) {
+ pm8001_ha->non_fatal_read_length = 0;
+ buf_copy += snprintf(buf_copy, PAGE_SIZE, "%08x ", 4);
+ pm8001_ha->non_fatal_count = 0;
+ }
+ accum_len = pm8001_mr32(nonfatal_table_address,
+ MPI_FATAL_EDUMP_TABLE_ACCUM_LEN);
+ output_length = accum_len -
+ pm8001_ha->forensic_preserved_accumulated_transfer;
+
+ for (index = 0; index < output_length/4; index++)
+ buf_copy += snprintf(buf_copy, PAGE_SIZE,
+ "%08x ", *(temp+index));
+
+ pm8001_ha->non_fatal_read_length += output_length;
+
+ /* store current accumulated length to use in next iteration as
+ * the previous accumulated length
+ */
+ pm8001_ha->forensic_preserved_accumulated_transfer = accum_len;
+ return (buf_copy - buf);
+}
+
+/**
+ * read_main_config_table - read the configure table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void read_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.signature =
+ pm8001_mr32(address, MAIN_SIGNATURE_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.interface_rev =
+ pm8001_mr32(address, MAIN_INTERFACE_REVISION);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev =
+ pm8001_mr32(address, MAIN_FW_REVISION);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_out_io =
+ pm8001_mr32(address, MAIN_MAX_OUTSTANDING_IO_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl =
+ pm8001_mr32(address, MAIN_MAX_SGL_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.ctrl_cap_flag =
+ pm8001_mr32(address, MAIN_CNTRL_CAP_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.gst_offset =
+ pm8001_mr32(address, MAIN_GST_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.inbound_queue_offset =
+ pm8001_mr32(address, MAIN_IBQ_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.outbound_queue_offset =
+ pm8001_mr32(address, MAIN_OBQ_OFFSET);
+
+ /* read Error Dump Offset and Length */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_dump_offset0 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_dump_length0 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_dump_offset1 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_dump_length1 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH);
+
+ /* read GPIO LED settings from the configuration table */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping =
+ pm8001_mr32(address, MAIN_GPIO_LED_FLAGS_OFFSET);
+
+ /* read analog Setting offset from the configuration table */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.analog_setup_table_offset =
+ pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET);
+
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.int_vec_table_offset =
+ pm8001_mr32(address, MAIN_INT_VECTOR_TABLE_OFFSET);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.phy_attr_table_offset =
+ pm8001_mr32(address, MAIN_SAS_PHY_ATTR_TABLE_OFFSET);
+ /* read port recover and reset timeout */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer =
+ pm8001_mr32(address, MAIN_PORT_RECOVERY_TIMER);
+ /* read ILA and inactive firmware version */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version =
+ pm8001_mr32(address, MAIN_MPI_ILA_RELEASE_TYPE);
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version =
+ pm8001_mr32(address, MAIN_MPI_INACTIVE_FW_VERSION);
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "Main cfg table: sign:%x interface rev:%x fw_rev:%x\n",
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.signature,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.interface_rev,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev);
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "table offset: gst:%x iq:%x oq:%x int vec:%x phy attr:%x\n",
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.gst_offset,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.inbound_queue_offset,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.outbound_queue_offset,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.int_vec_table_offset,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.phy_attr_table_offset);
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "Main cfg table; ila rev:%x Inactive fw rev:%x\n",
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version);
+}
+
+/**
+ * read_general_status_table - read the general status table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void read_general_status_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->general_stat_tbl_addr;
+ pm8001_ha->gs_tbl.pm80xx_tbl.gst_len_mpistate =
+ pm8001_mr32(address, GST_GSTLEN_MPIS_OFFSET);
+ pm8001_ha->gs_tbl.pm80xx_tbl.iq_freeze_state0 =
+ pm8001_mr32(address, GST_IQ_FREEZE_STATE0_OFFSET);
+ pm8001_ha->gs_tbl.pm80xx_tbl.iq_freeze_state1 =
+ pm8001_mr32(address, GST_IQ_FREEZE_STATE1_OFFSET);
+ pm8001_ha->gs_tbl.pm80xx_tbl.msgu_tcnt =
+ pm8001_mr32(address, GST_MSGUTCNT_OFFSET);
+ pm8001_ha->gs_tbl.pm80xx_tbl.iop_tcnt =
+ pm8001_mr32(address, GST_IOPTCNT_OFFSET);
+ pm8001_ha->gs_tbl.pm80xx_tbl.gpio_input_val =
+ pm8001_mr32(address, GST_GPIO_INPUT_VAL);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[0] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET0);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[1] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET1);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[2] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET2);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[3] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET3);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[4] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET4);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[5] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET5);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[6] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET6);
+ pm8001_ha->gs_tbl.pm80xx_tbl.recover_err_info[7] =
+ pm8001_mr32(address, GST_RERRINFO_OFFSET7);
+}
+/**
+ * read_phy_attr_table - read the phy attribute table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void read_phy_attr_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->pspa_q_tbl_addr;
+ pm8001_ha->phy_attr_table.phystart1_16[0] =
+ pm8001_mr32(address, PSPA_PHYSTATE0_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[1] =
+ pm8001_mr32(address, PSPA_PHYSTATE1_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[2] =
+ pm8001_mr32(address, PSPA_PHYSTATE2_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[3] =
+ pm8001_mr32(address, PSPA_PHYSTATE3_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[4] =
+ pm8001_mr32(address, PSPA_PHYSTATE4_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[5] =
+ pm8001_mr32(address, PSPA_PHYSTATE5_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[6] =
+ pm8001_mr32(address, PSPA_PHYSTATE6_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[7] =
+ pm8001_mr32(address, PSPA_PHYSTATE7_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[8] =
+ pm8001_mr32(address, PSPA_PHYSTATE8_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[9] =
+ pm8001_mr32(address, PSPA_PHYSTATE9_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[10] =
+ pm8001_mr32(address, PSPA_PHYSTATE10_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[11] =
+ pm8001_mr32(address, PSPA_PHYSTATE11_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[12] =
+ pm8001_mr32(address, PSPA_PHYSTATE12_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[13] =
+ pm8001_mr32(address, PSPA_PHYSTATE13_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[14] =
+ pm8001_mr32(address, PSPA_PHYSTATE14_OFFSET);
+ pm8001_ha->phy_attr_table.phystart1_16[15] =
+ pm8001_mr32(address, PSPA_PHYSTATE15_OFFSET);
+
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[0] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID0_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[1] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID1_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[2] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID2_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[3] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID3_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[4] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID4_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[5] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID5_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[6] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID6_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[7] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID7_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[8] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID8_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[9] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID9_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[10] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID10_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[11] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID11_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[12] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID12_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[13] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID13_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[14] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID14_OFFSET);
+ pm8001_ha->phy_attr_table.outbound_hw_event_pid1_16[15] =
+ pm8001_mr32(address, PSPA_OB_HW_EVENT_PID15_OFFSET);
+
+}
+
+/**
+ * read_inbnd_queue_table - read the inbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+ for (i = 0; i < PM8001_MAX_INB_NUM; i++) {
+ u32 offset = i * 0x20;
+ pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
+ get_pci_bar_index(pm8001_mr32(address,
+ (offset + IB_PIPCI_BAR)));
+ pm8001_ha->inbnd_q_tbl[i].pi_offset =
+ pm8001_mr32(address, (offset + IB_PIPCI_BAR_OFFSET));
+ }
+}
+
+/**
+ * read_outbnd_queue_table - read the outbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+ for (i = 0; i < PM8001_MAX_OUTB_NUM; i++) {
+ u32 offset = i * 0x24;
+ pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
+ get_pci_bar_index(pm8001_mr32(address,
+ (offset + OB_CIPCI_BAR)));
+ pm8001_ha->outbnd_q_tbl[i].ci_offset =
+ pm8001_mr32(address, (offset + OB_CIPCI_BAR_OFFSET));
+ }
+}
+
+/**
+ * init_default_table_values - init the default table.
+ * @pm8001_ha: our hba card information
+ */
+static void init_default_table_values(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ u32 offsetib, offsetob;
+ void __iomem *addressib = pm8001_ha->inbnd_q_tbl_addr;
+ void __iomem *addressob = pm8001_ha->outbnd_q_tbl_addr;
+ u32 ib_offset = pm8001_ha->ib_offset;
+ u32 ob_offset = pm8001_ha->ob_offset;
+ u32 ci_offset = pm8001_ha->ci_offset;
+ u32 pi_offset = pm8001_ha->pi_offset;
+
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.upper_event_log_addr =
+ pm8001_ha->memoryMap.region[AAP1].phys_addr_hi;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.lower_event_log_addr =
+ pm8001_ha->memoryMap.region[AAP1].phys_addr_lo;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size =
+ PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_severity = 0x01;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.upper_pcs_event_log_addr =
+ pm8001_ha->memoryMap.region[IOP].phys_addr_hi;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.lower_pcs_event_log_addr =
+ pm8001_ha->memoryMap.region[IOP].phys_addr_lo;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_size =
+ PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity =
+ pcs_event_log_severity;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt = 0x01;
+
+ /* Enable higher IQs and OQs, 32 to 63, bit 16 */
+ if (pm8001_ha->max_q_num > 32)
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt |=
+ 1 << 16;
+ /* Disable end to end CRC checking */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.crc_core_dump = (0x1 << 16);
+
+ for (i = 0; i < pm8001_ha->max_q_num; i++) {
+ pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30);
+ pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[ib_offset + i].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[ib_offset + i].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[ib_offset + i].virt_ptr;
+ pm8001_ha->inbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[ib_offset + i].total_len;
+ pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr =
+ pm8001_ha->memoryMap.region[ci_offset + i].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr =
+ pm8001_ha->memoryMap.region[ci_offset + i].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].ci_virt =
+ pm8001_ha->memoryMap.region[ci_offset + i].virt_ptr;
+ pm8001_write_32(pm8001_ha->inbnd_q_tbl[i].ci_virt, 0, 0);
+ offsetib = i * 0x20;
+ pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
+ get_pci_bar_index(pm8001_mr32(addressib,
+ (offsetib + 0x14)));
+ pm8001_ha->inbnd_q_tbl[i].pi_offset =
+ pm8001_mr32(addressib, (offsetib + 0x18));
+ pm8001_ha->inbnd_q_tbl[i].producer_idx = 0;
+ pm8001_ha->inbnd_q_tbl[i].consumer_index = 0;
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "IQ %d pi_bar 0x%x pi_offset 0x%x\n", i,
+ pm8001_ha->inbnd_q_tbl[i].pi_pci_bar,
+ pm8001_ha->inbnd_q_tbl[i].pi_offset);
+ }
+ for (i = 0; i < pm8001_ha->max_q_num; i++) {
+ pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30);
+ pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[ob_offset + i].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[ob_offset + i].phys_addr_lo;
+ pm8001_ha->outbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[ob_offset + i].virt_ptr;
+ pm8001_ha->outbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[ob_offset + i].total_len;
+ pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr =
+ pm8001_ha->memoryMap.region[pi_offset + i].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr =
+ pm8001_ha->memoryMap.region[pi_offset + i].phys_addr_lo;
+ /* interrupt vector based on oq */
+ pm8001_ha->outbnd_q_tbl[i].interrup_vec_cnt_delay = (i << 24);
+ pm8001_ha->outbnd_q_tbl[i].pi_virt =
+ pm8001_ha->memoryMap.region[pi_offset + i].virt_ptr;
+ pm8001_write_32(pm8001_ha->outbnd_q_tbl[i].pi_virt, 0, 0);
+ offsetob = i * 0x24;
+ pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
+ get_pci_bar_index(pm8001_mr32(addressob,
+ offsetob + 0x14));
+ pm8001_ha->outbnd_q_tbl[i].ci_offset =
+ pm8001_mr32(addressob, (offsetob + 0x18));
+ pm8001_ha->outbnd_q_tbl[i].consumer_idx = 0;
+ pm8001_ha->outbnd_q_tbl[i].producer_index = 0;
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "OQ %d ci_bar 0x%x ci_offset 0x%x\n", i,
+ pm8001_ha->outbnd_q_tbl[i].ci_pci_bar,
+ pm8001_ha->outbnd_q_tbl[i].ci_offset);
+ }
+}
+
+/**
+ * update_main_config_table - update the main default table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void update_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+ pm8001_mw32(address, MAIN_IQNPPD_HPPD_OFFSET,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.inbound_q_nppd_hppd);
+ pm8001_mw32(address, MAIN_EVENT_LOG_ADDR_HI,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.upper_event_log_addr);
+ pm8001_mw32(address, MAIN_EVENT_LOG_ADDR_LO,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.lower_event_log_addr);
+ pm8001_mw32(address, MAIN_EVENT_LOG_BUFF_SIZE,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size);
+ pm8001_mw32(address, MAIN_EVENT_LOG_OPTION,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_severity);
+ pm8001_mw32(address, MAIN_PCS_EVENT_LOG_ADDR_HI,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.upper_pcs_event_log_addr);
+ pm8001_mw32(address, MAIN_PCS_EVENT_LOG_ADDR_LO,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.lower_pcs_event_log_addr);
+ pm8001_mw32(address, MAIN_PCS_EVENT_LOG_BUFF_SIZE,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_size);
+ pm8001_mw32(address, MAIN_PCS_EVENT_LOG_OPTION,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.pcs_event_log_severity);
+ /* Update Fatal error interrupt vector */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt |=
+ ((pm8001_ha->max_q_num - 1) << 8);
+ pm8001_mw32(address, MAIN_FATAL_ERROR_INTERRUPT,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt);
+ pm8001_dbg(pm8001_ha, DEV,
+ "Updated Fatal error interrupt vector 0x%x\n",
+ pm8001_mr32(address, MAIN_FATAL_ERROR_INTERRUPT));
+
+ pm8001_mw32(address, MAIN_EVENT_CRC_CHECK,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.crc_core_dump);
+
+ /* SPCv specific */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping &= 0xCFFFFFFF;
+ /* Set GPIOLED to 0x2 for LED indicator */
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping |= 0x20000000;
+ pm8001_mw32(address, MAIN_GPIO_LED_FLAGS_OFFSET,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping);
+ pm8001_dbg(pm8001_ha, DEV,
+ "Programming DW 0x21 in main cfg table with 0x%x\n",
+ pm8001_mr32(address, MAIN_GPIO_LED_FLAGS_OFFSET));
+
+ pm8001_mw32(address, MAIN_PORT_RECOVERY_TIMER,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer);
+ pm8001_mw32(address, MAIN_INT_REASSERTION_DELAY,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.interrupt_reassertion_delay);
+
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer &= 0xffff0000;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer |=
+ PORT_RECOVERY_TIMEOUT;
+ if (pm8001_ha->chip_id == chip_8006) {
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer &=
+ 0x0000ffff;
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer |=
+ CHIP_8006_PORT_RECOVERY_TIMEOUT;
+ }
+ pm8001_mw32(address, MAIN_PORT_RECOVERY_TIMER,
+ pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer);
+}
+
+/**
+ * update_inbnd_queue_table - update the inbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ * @number: entry in the queue
+ */
+static void update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha,
+ int number)
+{
+ void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+ u16 offset = number * 0x20;
+ pm8001_mw32(address, offset + IB_PROPERITY_OFFSET,
+ pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
+ pm8001_mw32(address, offset + IB_BASE_ADDR_HI_OFFSET,
+ pm8001_ha->inbnd_q_tbl[number].upper_base_addr);
+ pm8001_mw32(address, offset + IB_BASE_ADDR_LO_OFFSET,
+ pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
+ pm8001_mw32(address, offset + IB_CI_BASE_ADDR_HI_OFFSET,
+ pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr);
+ pm8001_mw32(address, offset + IB_CI_BASE_ADDR_LO_OFFSET,
+ pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "IQ %d: Element pri size 0x%x\n",
+ number,
+ pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "IQ upr base addr 0x%x IQ lwr base addr 0x%x\n",
+ pm8001_ha->inbnd_q_tbl[number].upper_base_addr,
+ pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "CI upper base addr 0x%x CI lower base addr 0x%x\n",
+ pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr,
+ pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
+}
+
+/**
+ * update_outbnd_queue_table - update the outbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ * @number: entry in the queue
+ */
+static void update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha,
+ int number)
+{
+ void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+ u16 offset = number * 0x24;
+ pm8001_mw32(address, offset + OB_PROPERITY_OFFSET,
+ pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
+ pm8001_mw32(address, offset + OB_BASE_ADDR_HI_OFFSET,
+ pm8001_ha->outbnd_q_tbl[number].upper_base_addr);
+ pm8001_mw32(address, offset + OB_BASE_ADDR_LO_OFFSET,
+ pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
+ pm8001_mw32(address, offset + OB_PI_BASE_ADDR_HI_OFFSET,
+ pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr);
+ pm8001_mw32(address, offset + OB_PI_BASE_ADDR_LO_OFFSET,
+ pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
+ pm8001_mw32(address, offset + OB_INTERRUPT_COALES_OFFSET,
+ pm8001_ha->outbnd_q_tbl[number].interrup_vec_cnt_delay);
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "OQ %d: Element pri size 0x%x\n",
+ number,
+ pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "OQ upr base addr 0x%x OQ lwr base addr 0x%x\n",
+ pm8001_ha->outbnd_q_tbl[number].upper_base_addr,
+ pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "PI upper base addr 0x%x PI lower base addr 0x%x\n",
+ pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr,
+ pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
+}
+
+/**
+ * mpi_init_check - check firmware initialization status.
+ * @pm8001_ha: our hba card information
+ */
+static int mpi_init_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 max_wait_count;
+ u32 value;
+ u32 gst_len_mpistate;
+
+ /* Write bit0=1 to Inbound DoorBell Register to tell the SPC FW the
+ table is updated */
+ pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPCv_MSGU_CFG_TABLE_UPDATE);
+ /* wait until Inbound DoorBell Clear Register toggled */
+ if (IS_SPCV_12G(pm8001_ha->pdev)) {
+ max_wait_count = SPCV_DOORBELL_CLEAR_TIMEOUT;
+ } else {
+ max_wait_count = SPC_DOORBELL_CLEAR_TIMEOUT;
+ }
+ do {
+ msleep(FW_READY_INTERVAL);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+ value &= SPCv_MSGU_CFG_TABLE_UPDATE;
+ } while ((value != 0) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ /* additional check */
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Inb doorbell clear not toggled[value:%x]\n",
+ value);
+ return -EBUSY;
+ }
+ /* check the MPI-State for initialization up to 100ms*/
+ max_wait_count = 5;/* 100 msec */
+ do {
+ msleep(FW_READY_INTERVAL);
+ gst_len_mpistate =
+ pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+ GST_GSTLEN_MPIS_OFFSET);
+ } while ((GST_MPI_STATE_INIT !=
+ (gst_len_mpistate & GST_MPI_STATE_MASK)) && (--max_wait_count));
+ if (!max_wait_count)
+ return -EBUSY;
+
+ /* check MPI Initialization error */
+ gst_len_mpistate = gst_len_mpistate >> 16;
+ if (0x0000 != gst_len_mpistate)
+ return -EBUSY;
+
+ /*
+ * As per controller datasheet, after successful MPI
+ * initialization minimum 500ms delay is required before
+ * issuing commands.
+ */
+ msleep(500);
+
+ return 0;
+}
+
+/**
+ * check_fw_ready - The LLDD check if the FW is ready, if not, return error.
+ * This function sleeps hence it must not be used in atomic context.
+ * @pm8001_ha: our hba card information
+ */
+static int check_fw_ready(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 value;
+ u32 max_wait_count;
+ u32 max_wait_time;
+ u32 expected_mask;
+ int ret = 0;
+
+ /* reset / PCIe ready */
+ max_wait_time = max_wait_count = 5; /* 100 milli sec */
+ do {
+ msleep(FW_READY_INTERVAL);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ } while ((value == 0xFFFFFFFF) && (--max_wait_count));
+
+ /* check ila, RAAE and iops status */
+ if ((pm8001_ha->chip_id != chip_8008) &&
+ (pm8001_ha->chip_id != chip_8009)) {
+ max_wait_time = max_wait_count = 180; /* 3600 milli sec */
+ expected_mask = SCRATCH_PAD_ILA_READY |
+ SCRATCH_PAD_RAAE_READY |
+ SCRATCH_PAD_IOP0_READY |
+ SCRATCH_PAD_IOP1_READY;
+ } else {
+ max_wait_time = max_wait_count = 170; /* 3400 milli sec */
+ expected_mask = SCRATCH_PAD_ILA_READY |
+ SCRATCH_PAD_RAAE_READY |
+ SCRATCH_PAD_IOP0_READY;
+ }
+ do {
+ msleep(FW_READY_INTERVAL);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ } while (((value & expected_mask) !=
+ expected_mask) && (--max_wait_count));
+ if (!max_wait_count) {
+ pm8001_dbg(pm8001_ha, INIT,
+ "At least one FW component failed to load within %d millisec: Scratchpad1: 0x%x\n",
+ max_wait_time * FW_READY_INTERVAL, value);
+ ret = -1;
+ } else {
+ pm8001_dbg(pm8001_ha, MSG,
+ "All FW components ready by %d ms\n",
+ (max_wait_time - max_wait_count) * FW_READY_INTERVAL);
+ }
+ return ret;
+}
+
+static int init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *base_addr;
+ u32 value;
+ u32 offset;
+ u32 pcibar;
+ u32 pcilogic;
+
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+
+ /*
+ * lower 26 bits of SCRATCHPAD0 register describes offset within the
+ * PCIe BAR where the MPI configuration table is present
+ */
+ offset = value & 0x03FFFFFF; /* scratch pad 0 TBL address */
+
+ pm8001_dbg(pm8001_ha, DEV, "Scratchpad 0 Offset: 0x%x value 0x%x\n",
+ offset, value);
+ /*
+ * Upper 6 bits describe the offset within PCI config space where BAR
+ * is located.
+ */
+ pcilogic = (value & 0xFC000000) >> 26;
+ pcibar = get_pci_bar_index(pcilogic);
+ pm8001_dbg(pm8001_ha, INIT, "Scratchpad 0 PCI BAR: %d\n", pcibar);
+
+ /*
+ * Make sure the offset falls inside the ioremapped PCI BAR
+ */
+ if (offset > pm8001_ha->io_mem[pcibar].memsize) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Main cfg tbl offset outside %u > %u\n",
+ offset, pm8001_ha->io_mem[pcibar].memsize);
+ return -EBUSY;
+ }
+ pm8001_ha->main_cfg_tbl_addr = base_addr =
+ pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
+
+ /*
+ * Validate main configuration table address: first DWord should read
+ * "PMCS"
+ */
+ value = pm8001_mr32(pm8001_ha->main_cfg_tbl_addr, 0);
+ if (memcmp(&value, "PMCS", 4) != 0) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "BAD main config signature 0x%x\n",
+ value);
+ return -EBUSY;
+ }
+ pm8001_dbg(pm8001_ha, INIT,
+ "VALID main config signature 0x%x\n", value);
+ pm8001_ha->general_stat_tbl_addr =
+ base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x18) &
+ 0xFFFFFF);
+ pm8001_ha->inbnd_q_tbl_addr =
+ base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C) &
+ 0xFFFFFF);
+ pm8001_ha->outbnd_q_tbl_addr =
+ base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x20) &
+ 0xFFFFFF);
+ pm8001_ha->ivt_tbl_addr =
+ base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x8C) &
+ 0xFFFFFF);
+ pm8001_ha->pspa_q_tbl_addr =
+ base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x90) &
+ 0xFFFFFF);
+ pm8001_ha->fatal_tbl_addr =
+ base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0xA0) &
+ 0xFFFFFF);
+
+ pm8001_dbg(pm8001_ha, INIT, "GST OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x18));
+ pm8001_dbg(pm8001_ha, INIT, "INBND OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C));
+ pm8001_dbg(pm8001_ha, INIT, "OBND OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x20));
+ pm8001_dbg(pm8001_ha, INIT, "IVT OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x8C));
+ pm8001_dbg(pm8001_ha, INIT, "PSPA OFFSET 0x%x\n",
+ pm8001_cr32(pm8001_ha, pcibar, offset + 0x90));
+ pm8001_dbg(pm8001_ha, INIT, "addr - main cfg %p general status %p\n",
+ pm8001_ha->main_cfg_tbl_addr,
+ pm8001_ha->general_stat_tbl_addr);
+ pm8001_dbg(pm8001_ha, INIT, "addr - inbnd %p obnd %p\n",
+ pm8001_ha->inbnd_q_tbl_addr,
+ pm8001_ha->outbnd_q_tbl_addr);
+ pm8001_dbg(pm8001_ha, INIT, "addr - pspa %p ivt %p\n",
+ pm8001_ha->pspa_q_tbl_addr,
+ pm8001_ha->ivt_tbl_addr);
+ return 0;
+}
+
+/**
+ * pm80xx_set_thermal_config - support the thermal configuration
+ * @pm8001_ha: our hba card information.
+ */
+int
+pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha)
+{
+ struct set_ctrl_cfg_req payload;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_SET_CONTROLLER_CONFIG;
+ u32 page_code;
+
+ memset(&payload, 0, sizeof(struct set_ctrl_cfg_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+
+ payload.tag = cpu_to_le32(tag);
+
+ if (IS_SPCV_12G(pm8001_ha->pdev))
+ page_code = THERMAL_PAGE_CODE_7H;
+ else
+ page_code = THERMAL_PAGE_CODE_8H;
+
+ payload.cfg_pg[0] =
+ cpu_to_le32((THERMAL_LOG_ENABLE << 9) |
+ (THERMAL_ENABLE << 8) | page_code);
+ payload.cfg_pg[1] =
+ cpu_to_le32((LTEMPHIL << 24) | (RTEMPHIL << 8));
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "Setting up thermal config. cfg_pg 0 0x%x cfg_pg 1 0x%x\n",
+ payload.cfg_pg[0], payload.cfg_pg[1]);
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_tag_free(pm8001_ha, tag);
+ return rc;
+
+}
+
+/**
+* pm80xx_set_sas_protocol_timer_config - support the SAS Protocol
+* Timer configuration page
+* @pm8001_ha: our hba card information.
+*/
+static int
+pm80xx_set_sas_protocol_timer_config(struct pm8001_hba_info *pm8001_ha)
+{
+ struct set_ctrl_cfg_req payload;
+ SASProtocolTimerConfig_t SASConfigPage;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_SET_CONTROLLER_CONFIG;
+
+ memset(&payload, 0, sizeof(struct set_ctrl_cfg_req));
+ memset(&SASConfigPage, 0, sizeof(SASProtocolTimerConfig_t));
+
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+
+ payload.tag = cpu_to_le32(tag);
+
+ SASConfigPage.pageCode = cpu_to_le32(SAS_PROTOCOL_TIMER_CONFIG_PAGE);
+ SASConfigPage.MST_MSI = cpu_to_le32(3 << 15);
+ SASConfigPage.STP_SSP_MCT_TMO =
+ cpu_to_le32((STP_MCT_TMO << 16) | SSP_MCT_TMO);
+ SASConfigPage.STP_FRM_TMO =
+ cpu_to_le32((SAS_MAX_OPEN_TIME << 24) |
+ (SMP_MAX_CONN_TIMER << 16) | STP_FRM_TIMER);
+ SASConfigPage.STP_IDLE_TMO = cpu_to_le32(STP_IDLE_TIME);
+
+ SASConfigPage.OPNRJT_RTRY_INTVL =
+ cpu_to_le32((SAS_MFD << 16) | SAS_OPNRJT_RTRY_INTVL);
+ SASConfigPage.Data_Cmd_OPNRJT_RTRY_TMO =
+ cpu_to_le32((SAS_DOPNRJT_RTRY_TMO << 16) | SAS_COPNRJT_RTRY_TMO);
+ SASConfigPage.Data_Cmd_OPNRJT_RTRY_THR =
+ cpu_to_le32((SAS_DOPNRJT_RTRY_THR << 16) | SAS_COPNRJT_RTRY_THR);
+ SASConfigPage.MAX_AIP = cpu_to_le32(SAS_MAX_AIP);
+
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.pageCode 0x%08x\n",
+ le32_to_cpu(SASConfigPage.pageCode));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.MST_MSI 0x%08x\n",
+ le32_to_cpu(SASConfigPage.MST_MSI));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.STP_SSP_MCT_TMO 0x%08x\n",
+ le32_to_cpu(SASConfigPage.STP_SSP_MCT_TMO));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.STP_FRM_TMO 0x%08x\n",
+ le32_to_cpu(SASConfigPage.STP_FRM_TMO));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.STP_IDLE_TMO 0x%08x\n",
+ le32_to_cpu(SASConfigPage.STP_IDLE_TMO));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.OPNRJT_RTRY_INTVL 0x%08x\n",
+ le32_to_cpu(SASConfigPage.OPNRJT_RTRY_INTVL));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.Data_Cmd_OPNRJT_RTRY_TMO 0x%08x\n",
+ le32_to_cpu(SASConfigPage.Data_Cmd_OPNRJT_RTRY_TMO));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.Data_Cmd_OPNRJT_RTRY_THR 0x%08x\n",
+ le32_to_cpu(SASConfigPage.Data_Cmd_OPNRJT_RTRY_THR));
+ pm8001_dbg(pm8001_ha, INIT, "SASConfigPage.MAX_AIP 0x%08x\n",
+ le32_to_cpu(SASConfigPage.MAX_AIP));
+
+ memcpy(&payload.cfg_pg, &SASConfigPage,
+ sizeof(SASProtocolTimerConfig_t));
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_tag_free(pm8001_ha, tag);
+
+ return rc;
+}
+
+/**
+ * pm80xx_get_encrypt_info - Check for encryption
+ * @pm8001_ha: our hba card information.
+ */
+static int
+pm80xx_get_encrypt_info(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 scratch3_value;
+ int ret = -1;
+
+ /* Read encryption status from SCRATCH PAD 3 */
+ scratch3_value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
+
+ if ((scratch3_value & SCRATCH_PAD3_ENC_MASK) ==
+ SCRATCH_PAD3_ENC_READY) {
+ if (scratch3_value & SCRATCH_PAD3_XTS_ENABLED)
+ pm8001_ha->encrypt_info.cipher_mode = CIPHER_MODE_XTS;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMF_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMF;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMA_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMA;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMB_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMB;
+ pm8001_ha->encrypt_info.status = 0;
+ pm8001_dbg(pm8001_ha, INIT,
+ "Encryption: SCRATCH_PAD3_ENC_READY 0x%08X.Cipher mode 0x%x Sec mode 0x%x status 0x%x\n",
+ scratch3_value,
+ pm8001_ha->encrypt_info.cipher_mode,
+ pm8001_ha->encrypt_info.sec_mode,
+ pm8001_ha->encrypt_info.status);
+ ret = 0;
+ } else if ((scratch3_value & SCRATCH_PAD3_ENC_READY) ==
+ SCRATCH_PAD3_ENC_DISABLED) {
+ pm8001_dbg(pm8001_ha, INIT,
+ "Encryption: SCRATCH_PAD3_ENC_DISABLED 0x%08X\n",
+ scratch3_value);
+ pm8001_ha->encrypt_info.status = 0xFFFFFFFF;
+ pm8001_ha->encrypt_info.cipher_mode = 0;
+ pm8001_ha->encrypt_info.sec_mode = 0;
+ ret = 0;
+ } else if ((scratch3_value & SCRATCH_PAD3_ENC_MASK) ==
+ SCRATCH_PAD3_ENC_DIS_ERR) {
+ pm8001_ha->encrypt_info.status =
+ (scratch3_value & SCRATCH_PAD3_ERR_CODE) >> 16;
+ if (scratch3_value & SCRATCH_PAD3_XTS_ENABLED)
+ pm8001_ha->encrypt_info.cipher_mode = CIPHER_MODE_XTS;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMF_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMF;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMA_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMA;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMB_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMB;
+ pm8001_dbg(pm8001_ha, INIT,
+ "Encryption: SCRATCH_PAD3_DIS_ERR 0x%08X.Cipher mode 0x%x sec mode 0x%x status 0x%x\n",
+ scratch3_value,
+ pm8001_ha->encrypt_info.cipher_mode,
+ pm8001_ha->encrypt_info.sec_mode,
+ pm8001_ha->encrypt_info.status);
+ } else if ((scratch3_value & SCRATCH_PAD3_ENC_MASK) ==
+ SCRATCH_PAD3_ENC_ENA_ERR) {
+
+ pm8001_ha->encrypt_info.status =
+ (scratch3_value & SCRATCH_PAD3_ERR_CODE) >> 16;
+ if (scratch3_value & SCRATCH_PAD3_XTS_ENABLED)
+ pm8001_ha->encrypt_info.cipher_mode = CIPHER_MODE_XTS;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMF_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMF;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMA_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMA;
+ if ((scratch3_value & SCRATCH_PAD3_SM_MASK) ==
+ SCRATCH_PAD3_SMB_ENABLED)
+ pm8001_ha->encrypt_info.sec_mode = SEC_MODE_SMB;
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "Encryption: SCRATCH_PAD3_ENA_ERR 0x%08X.Cipher mode 0x%x sec mode 0x%x status 0x%x\n",
+ scratch3_value,
+ pm8001_ha->encrypt_info.cipher_mode,
+ pm8001_ha->encrypt_info.sec_mode,
+ pm8001_ha->encrypt_info.status);
+ }
+ return ret;
+}
+
+/**
+ * pm80xx_encrypt_update - update flash with encryption information
+ * @pm8001_ha: our hba card information.
+ */
+static int pm80xx_encrypt_update(struct pm8001_hba_info *pm8001_ha)
+{
+ struct kek_mgmt_req payload;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_KEK_MANAGEMENT;
+
+ memset(&payload, 0, sizeof(struct kek_mgmt_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+
+ payload.tag = cpu_to_le32(tag);
+ /* Currently only one key is used. New KEK index is 1.
+ * Current KEK index is 1. Store KEK to NVRAM is 1.
+ */
+ payload.new_curidx_ksop =
+ cpu_to_le32(((1 << 24) | (1 << 16) | (1 << 8) |
+ KEK_MGMT_SUBOP_KEYCARDUPDATE));
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "Saving Encryption info to flash. payload 0x%x\n",
+ le32_to_cpu(payload.new_curidx_ksop));
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_tag_free(pm8001_ha, tag);
+
+ return rc;
+}
+
+/**
+ * pm80xx_chip_init - the main init function that initializes whole PM8001 chip.
+ * @pm8001_ha: our hba card information
+ */
+static int pm80xx_chip_init(struct pm8001_hba_info *pm8001_ha)
+{
+ int ret;
+ u8 i = 0;
+
+ /* check the firmware status */
+ if (-1 == check_fw_ready(pm8001_ha)) {
+ pm8001_dbg(pm8001_ha, FAIL, "Firmware is not ready!\n");
+ return -EBUSY;
+ }
+
+ /* Initialize the controller fatal error flag */
+ pm8001_ha->controller_fatal_error = false;
+
+ /* Initialize pci space address eg: mpi offset */
+ ret = init_pci_device_addresses(pm8001_ha);
+ if (ret) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Failed to init pci addresses");
+ return ret;
+ }
+ init_default_table_values(pm8001_ha);
+ read_main_config_table(pm8001_ha);
+ read_general_status_table(pm8001_ha);
+ read_inbnd_queue_table(pm8001_ha);
+ read_outbnd_queue_table(pm8001_ha);
+ read_phy_attr_table(pm8001_ha);
+
+ /* update main config table ,inbound table and outbound table */
+ update_main_config_table(pm8001_ha);
+ for (i = 0; i < pm8001_ha->max_q_num; i++) {
+ update_inbnd_queue_table(pm8001_ha, i);
+ update_outbnd_queue_table(pm8001_ha, i);
+ }
+ /* notify firmware update finished and check initialization status */
+ if (0 == mpi_init_check(pm8001_ha)) {
+ pm8001_dbg(pm8001_ha, INIT, "MPI initialize successful!\n");
+ } else
+ return -EBUSY;
+
+ return 0;
+}
+
+static void pm80xx_chip_post_init(struct pm8001_hba_info *pm8001_ha)
+{
+ /* send SAS protocol timer configuration page to FW */
+ pm80xx_set_sas_protocol_timer_config(pm8001_ha);
+
+ /* Check for encryption */
+ if (pm8001_ha->chip->encrypt) {
+ int ret;
+
+ pm8001_dbg(pm8001_ha, INIT, "Checking for encryption\n");
+ ret = pm80xx_get_encrypt_info(pm8001_ha);
+ if (ret == -1) {
+ pm8001_dbg(pm8001_ha, INIT, "Encryption error !!\n");
+ if (pm8001_ha->encrypt_info.status == 0x81) {
+ pm8001_dbg(pm8001_ha, INIT,
+ "Encryption enabled with error.Saving encryption key to flash\n");
+ pm80xx_encrypt_update(pm8001_ha);
+ }
+ }
+ }
+}
+
+static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 max_wait_count;
+ u32 value;
+ u32 gst_len_mpistate;
+ int ret;
+
+ ret = init_pci_device_addresses(pm8001_ha);
+ if (ret) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Failed to init pci addresses");
+ return ret;
+ }
+
+ /* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the
+ table is stop */
+ pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPCv_MSGU_CFG_TABLE_RESET);
+
+ /* wait until Inbound DoorBell Clear Register toggled */
+ if (IS_SPCV_12G(pm8001_ha->pdev)) {
+ max_wait_count = SPCV_DOORBELL_CLEAR_TIMEOUT;
+ } else {
+ max_wait_count = SPC_DOORBELL_CLEAR_TIMEOUT;
+ }
+ do {
+ msleep(FW_READY_INTERVAL);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+ value &= SPCv_MSGU_CFG_TABLE_RESET;
+ } while ((value != 0) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT:IBDB value/=%x\n", value);
+ return -1;
+ }
+
+ /* check the MPI-State for termination in progress */
+ /* wait until Inbound DoorBell Clear Register toggled */
+ max_wait_count = 100; /* 2 sec for spcv/ve */
+ do {
+ msleep(FW_READY_INTERVAL);
+ gst_len_mpistate =
+ pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+ GST_GSTLEN_MPIS_OFFSET);
+ if (GST_MPI_STATE_UNINIT ==
+ (gst_len_mpistate & GST_MPI_STATE_MASK))
+ break;
+ } while (--max_wait_count);
+ if (!max_wait_count) {
+ pm8001_dbg(pm8001_ha, FAIL, " TIME OUT MPI State = 0x%x\n",
+ gst_len_mpistate & GST_MPI_STATE_MASK);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * pm80xx_fatal_error_uevent_emit - emits a single fatal error uevent
+ * @pm8001_ha: our hba card information
+ * @error_reporter: reporter of fatal error
+ */
+void pm80xx_fatal_error_uevent_emit(struct pm8001_hba_info *pm8001_ha,
+ enum fatal_error_reporter error_reporter)
+{
+ struct kobj_uevent_env *env;
+
+ pm8001_dbg(pm8001_ha, FAIL, "emitting fatal error uevent");
+
+ env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
+ if (!env)
+ return;
+
+ if (add_uevent_var(env, "DRIVER=%s", DRV_NAME))
+ goto exit;
+
+ if (add_uevent_var(env, "HBA_NUM=%u", pm8001_ha->id))
+ goto exit;
+
+ if (add_uevent_var(env, "EVENT_TYPE=FATAL_ERROR"))
+ goto exit;
+
+ switch (error_reporter) {
+ case REPORTER_DRIVER:
+ if (add_uevent_var(env, "REPORTED_BY=DRIVER"))
+ goto exit;
+ break;
+ case REPORTER_FIRMWARE:
+ if (add_uevent_var(env, "REPORTED_BY=FIRMWARE"))
+ goto exit;
+ break;
+ default:
+ if (add_uevent_var(env, "REPORTED_BY=OTHER"))
+ goto exit;
+ break;
+ }
+
+ kobject_uevent_env(&pm8001_ha->shost->shost_dev.kobj, KOBJ_CHANGE, env->envp);
+
+exit:
+ kfree(env);
+}
+
+/**
+ * pm80xx_fatal_errors - returns non-zero *ONLY* when fatal errors
+ * @pm8001_ha: our hba card information
+ *
+ * Fatal errors are recoverable only after a host reboot.
+ */
+int
+pm80xx_fatal_errors(struct pm8001_hba_info *pm8001_ha)
+{
+ int ret = 0;
+ u32 scratch_pad_rsvd0 = pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_RSVD_0);
+ u32 scratch_pad_rsvd1 = pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_RSVD_1);
+ u32 scratch_pad1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ u32 scratch_pad2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ u32 scratch_pad3 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
+
+ if (pm8001_ha->chip_id != chip_8006 &&
+ pm8001_ha->chip_id != chip_8074 &&
+ pm8001_ha->chip_id != chip_8076) {
+ return 0;
+ }
+
+ if (MSGU_SCRATCHPAD1_STATE_FATAL_ERROR(scratch_pad1)) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Fatal error SCRATCHPAD1 = 0x%x SCRATCHPAD2 = 0x%x SCRATCHPAD3 = 0x%x SCRATCHPAD_RSVD0 = 0x%x SCRATCHPAD_RSVD1 = 0x%x\n",
+ scratch_pad1, scratch_pad2, scratch_pad3,
+ scratch_pad_rsvd0, scratch_pad_rsvd1);
+ pm80xx_fatal_error_uevent_emit(pm8001_ha, REPORTER_DRIVER);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * pm80xx_chip_soft_rst - soft reset the PM8001 chip, so that all
+ * FW register status are reset to the originated status.
+ * @pm8001_ha: our hba card information
+ */
+
+static int
+pm80xx_chip_soft_rst(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 regval;
+ u32 bootloader_state;
+ u32 ibutton0, ibutton1;
+
+ /* Process MPI table uninitialization only if FW is ready */
+ if (!pm8001_ha->controller_fatal_error) {
+ /* Check if MPI is in ready state to reset */
+ if (mpi_uninit_check(pm8001_ha) != 0) {
+ u32 r0 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+ u32 r1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ u32 r2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ u32 r3 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "MPI state is not ready scratch: %x:%x:%x:%x\n",
+ r0, r1, r2, r3);
+ /* if things aren't ready but the bootloader is ok then
+ * try the reset anyway.
+ */
+ if (r1 & SCRATCH_PAD1_BOOTSTATE_MASK)
+ return -1;
+ }
+ }
+ /* checked for reset register normal state; 0x0 */
+ regval = pm8001_cr32(pm8001_ha, 0, SPC_REG_SOFT_RESET);
+ pm8001_dbg(pm8001_ha, INIT, "reset register before write : 0x%x\n",
+ regval);
+
+ pm8001_cw32(pm8001_ha, 0, SPC_REG_SOFT_RESET, SPCv_NORMAL_RESET_VALUE);
+ msleep(500);
+
+ regval = pm8001_cr32(pm8001_ha, 0, SPC_REG_SOFT_RESET);
+ pm8001_dbg(pm8001_ha, INIT, "reset register after write 0x%x\n",
+ regval);
+
+ if ((regval & SPCv_SOFT_RESET_READ_MASK) ==
+ SPCv_SOFT_RESET_NORMAL_RESET_OCCURED) {
+ pm8001_dbg(pm8001_ha, MSG,
+ " soft reset successful [regval: 0x%x]\n",
+ regval);
+ } else {
+ pm8001_dbg(pm8001_ha, MSG,
+ " soft reset failed [regval: 0x%x]\n",
+ regval);
+
+ /* check bootloader is successfully executed or in HDA mode */
+ bootloader_state =
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) &
+ SCRATCH_PAD1_BOOTSTATE_MASK;
+
+ if (bootloader_state == SCRATCH_PAD1_BOOTSTATE_HDA_SEEPROM) {
+ pm8001_dbg(pm8001_ha, MSG,
+ "Bootloader state - HDA mode SEEPROM\n");
+ } else if (bootloader_state ==
+ SCRATCH_PAD1_BOOTSTATE_HDA_BOOTSTRAP) {
+ pm8001_dbg(pm8001_ha, MSG,
+ "Bootloader state - HDA mode Bootstrap Pin\n");
+ } else if (bootloader_state ==
+ SCRATCH_PAD1_BOOTSTATE_HDA_SOFTRESET) {
+ pm8001_dbg(pm8001_ha, MSG,
+ "Bootloader state - HDA mode soft reset\n");
+ } else if (bootloader_state ==
+ SCRATCH_PAD1_BOOTSTATE_CRIT_ERROR) {
+ pm8001_dbg(pm8001_ha, MSG,
+ "Bootloader state-HDA mode critical error\n");
+ }
+ return -EBUSY;
+ }
+
+ /* check the firmware status after reset */
+ if (-1 == check_fw_ready(pm8001_ha)) {
+ pm8001_dbg(pm8001_ha, FAIL, "Firmware is not ready!\n");
+ /* check iButton feature support for motherboard controller */
+ if (pm8001_ha->pdev->subsystem_vendor !=
+ PCI_VENDOR_ID_ADAPTEC2 &&
+ pm8001_ha->pdev->subsystem_vendor !=
+ PCI_VENDOR_ID_ATTO &&
+ pm8001_ha->pdev->subsystem_vendor != 0) {
+ ibutton0 = pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_RSVD_0);
+ ibutton1 = pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_RSVD_1);
+ if (!ibutton0 && !ibutton1) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "iButton Feature is not Available!!!\n");
+ return -EBUSY;
+ }
+ if (ibutton0 == 0xdeadbeef && ibutton1 == 0xdeadbeef) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "CRC Check for iButton Feature Failed!!!\n");
+ return -EBUSY;
+ }
+ }
+ }
+ pm8001_dbg(pm8001_ha, INIT, "SPCv soft reset Complete\n");
+ return 0;
+}
+
+static void pm80xx_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 i;
+
+ pm8001_dbg(pm8001_ha, INIT, "chip reset start\n");
+
+ /* do SPCv chip reset. */
+ pm8001_cw32(pm8001_ha, 0, SPC_REG_SOFT_RESET, 0x11);
+ pm8001_dbg(pm8001_ha, INIT, "SPC soft reset Complete\n");
+
+ /* Check this ..whether delay is required or no */
+ /* delay 10 usec */
+ udelay(10);
+
+ /* wait for 20 msec until the firmware gets reloaded */
+ i = 20;
+ do {
+ mdelay(1);
+ } while ((--i) != 0);
+
+ pm8001_dbg(pm8001_ha, INIT, "chip reset finished\n");
+}
+
+/**
+ * pm80xx_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ * @vec: interrupt number to enable
+ */
+static void
+pm80xx_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+ if (!pm8001_ha->use_msix) {
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+ return;
+ }
+
+ if (vec < 32)
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR_CLR, 1U << vec);
+ else
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR_CLR_U, 1U << (vec - 32));
+}
+
+/**
+ * pm80xx_chip_interrupt_disable - disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ * @vec: interrupt number to disable
+ */
+static void
+pm80xx_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+ if (!pm8001_ha->use_msix) {
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR_CLR, ODMR_MASK_ALL);
+ return;
+ }
+
+ if (vec == 0xFF) {
+ /* disable all vectors 0-31, 32-63 */
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, 0xFFFFFFFF);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR_U, 0xFFFFFFFF);
+ } else if (vec < 32) {
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, 1U << vec);
+ } else {
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR_U, 1U << (vec - 32));
+ }
+}
+
+/**
+ * mpi_ssp_completion - process the event that FW response to the SSP request.
+ * @pm8001_ha: our hba card information
+ * @piomb: the message contents of this outbound message.
+ *
+ * When FW has completed a ssp request for example a IO request, after it has
+ * filled the SG data with the data, it will trigger this event representing
+ * that he has finished the job; please check the corresponding buffer.
+ * So we will tell the caller who maybe waiting the result to tell upper layer
+ * that the task has been finished.
+ */
+static void
+mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status;
+ u32 param;
+ u32 tag;
+ struct ssp_completion_resp *psspPayload;
+ struct task_status_struct *ts;
+ struct ssp_response_iu *iu;
+ struct pm8001_device *pm8001_dev;
+ psspPayload = (struct ssp_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psspPayload->status);
+ tag = le32_to_cpu(psspPayload->tag);
+ ccb = &pm8001_ha->ccb_info[tag];
+ if ((status == IO_ABORTED) && ccb->open_retry) {
+ /* Being completed by another */
+ ccb->open_retry = 0;
+ return;
+ }
+ pm8001_dev = ccb->device;
+ param = le32_to_cpu(psspPayload->param);
+ t = ccb->task;
+
+ if (status && status != IO_UNDERFLOW)
+ pm8001_dbg(pm8001_ha, FAIL, "sas IO status 0x%x\n", status);
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+
+ pm8001_dbg(pm8001_ha, DEV,
+ "tag::0x%x, status::0x%x task::0x%p\n", tag, status, t);
+
+ /* Print sas address of IO failed device */
+ if ((status != IO_SUCCESS) && (status != IO_OVERFLOW) &&
+ (status != IO_UNDERFLOW))
+ pm8001_dbg(pm8001_ha, FAIL, "SAS Address of IO Failure Drive:%016llx\n",
+ SAS_ADDR(t->dev->sas_addr));
+
+ switch (status) {
+ case IO_SUCCESS:
+ pm8001_dbg(pm8001_ha, IO, "IO_SUCCESS ,param = 0x%x\n",
+ param);
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_SAM_STAT_GOOD;
+ } else {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PROTO_RESPONSE;
+ ts->residual = param;
+ iu = &psspPayload->ssp_resp_iu;
+ sas_ssp_task_response(pm8001_ha->dev, t, iu);
+ }
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_ABORTED:
+ pm8001_dbg(pm8001_ha, IO, "IO_ABORTED IOMB Tag\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_UNDERFLOW:
+ /* SSP Completion with error */
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW ,param = 0x%x\n",
+ param);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_NO_DEVICE:
+ pm8001_dbg(pm8001_ha, IO, "IO_NO_DEVICE\n");
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ /* Force the midlayer to retry */
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_INVALID_SSP_RSP_FRAME:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_INVALID_SSP_RSP_FRAME\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ case IO_XFER_OPEN_RETRY_BACKOFF_THRESHOLD_REACHED:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_TMO:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_NO_DEST:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_COLLIDE:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_PATHWAY_BLOCKED:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_ACK_NAK_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_DMA:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_DMA\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_OFFSET_MISMATCH\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_PORT_IN_RESET:
+ pm8001_dbg(pm8001_ha, IO, "IO_PORT_IN_RESET\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_NON_OPERATIONAL\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_DS_NON_OPERATIONAL);
+ break;
+ case IO_DS_IN_RECOVERY:
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_IN_RECOVERY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_TM_TAG_NOT_FOUND:
+ pm8001_dbg(pm8001_ha, IO, "IO_TM_TAG_NOT_FOUND\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_SSP_EXT_IU_ZERO_LEN_ERROR:
+ pm8001_dbg(pm8001_ha, IO, "IO_SSP_EXT_IU_ZERO_LEN_ERROR\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ default:
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", status);
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ }
+ pm8001_dbg(pm8001_ha, IO, "scsi_status = 0x%x\n",
+ psspPayload->ssp_resp_iu.status);
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "task 0x%p done with io_status 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ if (t->slow_task)
+ complete(&t->slow_task->completion);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct sas_task *t;
+ unsigned long flags;
+ struct task_status_struct *ts;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct ssp_event_resp *psspPayload =
+ (struct ssp_event_resp *)(piomb + 4);
+ u32 event = le32_to_cpu(psspPayload->event);
+ u32 tag = le32_to_cpu(psspPayload->tag);
+ u32 port_id = le32_to_cpu(psspPayload->port_id);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ if (event)
+ pm8001_dbg(pm8001_ha, FAIL, "sas IO status 0x%x\n", event);
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ pm8001_dbg(pm8001_ha, IOERR, "port_id:0x%x, tag:0x%x, event:0x%x\n",
+ port_id, tag, event);
+ switch (event) {
+ case IO_OVERFLOW:
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
+ pm8001_handle_event(pm8001_ha, t, IO_XFER_ERROR_BREAK);
+ return;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ case IO_XFER_OPEN_RETRY_BACKOFF_THRESHOLD_REACHED:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_TMO:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_NO_DEST:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_COLLIDE:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_PATHWAY_BLOCKED:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_ACK_NAK_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
+ pm8001_handle_event(pm8001_ha, t, IO_XFER_OPEN_RETRY_TIMEOUT);
+ return;
+ case IO_XFER_ERROR_UNEXPECTED_PHASE:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_UNEXPECTED_PHASE\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_XFER_RDY_OVERRUN\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_OFFSET_MISMATCH\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_INTERNAL_CRC_ERROR:
+ pm8001_dbg(pm8001_ha, IOERR,
+ "IO_XFR_ERROR_INTERNAL_CRC_ERROR\n");
+ /* TBC: used default set values */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_CMD_FRAME_ISSUED:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_CMD_FRAME_ISSUED\n");
+ return;
+ default:
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", event);
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "task 0x%p done with event 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, event, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_sata_completion(struct pm8001_hba_info *pm8001_ha,
+ struct outbound_queue_table *circularQ, void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ u32 param;
+ u32 status;
+ u32 tag;
+ int i, j, ata_tag = -1;
+ u8 sata_addr_low[4];
+ u32 temp_sata_addr_low, temp_sata_addr_hi;
+ u8 sata_addr_hi[4];
+ struct sata_completion_resp *psataPayload;
+ struct task_status_struct *ts;
+ struct ata_task_resp *resp ;
+ u32 *sata_resp;
+ struct pm8001_device *pm8001_dev;
+ unsigned long flags;
+ struct ata_queued_cmd *qc;
+
+ psataPayload = (struct sata_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psataPayload->status);
+ param = le32_to_cpu(psataPayload->param);
+ tag = le32_to_cpu(psataPayload->tag);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+
+ if (t) {
+ if (t->dev && (t->dev->lldd_dev)) {
+ pm8001_dev = t->dev->lldd_dev;
+ qc = t->uldd_task;
+ ata_tag = qc ? qc->tag : -1;
+ }
+ } else {
+ pm8001_dbg(pm8001_ha, FAIL, "task null, freeing CCB tag %d\n",
+ ccb->ccb_tag);
+ pm8001_ccb_free(pm8001_ha, ccb);
+ return;
+ }
+
+ if (pm8001_dev && unlikely(!t->lldd_task || !t->dev))
+ return;
+
+ ts = &t->task_status;
+ if (status != IO_SUCCESS) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "IO failed status %#x pm80xx tag %#x ata tag %d\n",
+ status, tag, ata_tag);
+ }
+
+ /* Print sas address of IO failed device */
+ if ((status != IO_SUCCESS) && (status != IO_OVERFLOW) &&
+ (status != IO_UNDERFLOW)) {
+ if (!dev_parent_is_expander(t->dev)) {
+ for (i = 0, j = 4; i <= 3 && j <= 7; i++, j++)
+ sata_addr_low[i] = pm8001_ha->sas_addr[j];
+ for (i = 0, j = 0; i <= 3 && j <= 3; i++, j++)
+ sata_addr_hi[i] = pm8001_ha->sas_addr[j];
+ memcpy(&temp_sata_addr_low, sata_addr_low,
+ sizeof(sata_addr_low));
+ memcpy(&temp_sata_addr_hi, sata_addr_hi,
+ sizeof(sata_addr_hi));
+ temp_sata_addr_hi = (((temp_sata_addr_hi >> 24) & 0xff)
+ |((temp_sata_addr_hi << 8) &
+ 0xff0000) |
+ ((temp_sata_addr_hi >> 8)
+ & 0xff00) |
+ ((temp_sata_addr_hi << 24) &
+ 0xff000000));
+ temp_sata_addr_low = ((((temp_sata_addr_low >> 24)
+ & 0xff) |
+ ((temp_sata_addr_low << 8)
+ & 0xff0000) |
+ ((temp_sata_addr_low >> 8)
+ & 0xff00) |
+ ((temp_sata_addr_low << 24)
+ & 0xff000000)) +
+ pm8001_dev->attached_phy +
+ 0x10);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SAS Address of IO Failure Drive:%08x%08x\n",
+ temp_sata_addr_hi,
+ temp_sata_addr_low);
+
+ } else {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "SAS Address of IO Failure Drive:%016llx\n",
+ SAS_ADDR(t->dev->sas_addr));
+ }
+ }
+ switch (status) {
+ case IO_SUCCESS:
+ pm8001_dbg(pm8001_ha, IO, "IO_SUCCESS\n");
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_SAM_STAT_GOOD;
+ } else {
+ u8 len;
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PROTO_RESPONSE;
+ ts->residual = param;
+ pm8001_dbg(pm8001_ha, IO,
+ "SAS_PROTO_RESPONSE len = %d\n",
+ param);
+ sata_resp = &psataPayload->sata_resp[0];
+ resp = (struct ata_task_resp *)ts->buf;
+ if (t->ata_task.dma_xfer == 0 &&
+ t->data_dir == DMA_FROM_DEVICE) {
+ len = sizeof(struct pio_setup_fis);
+ pm8001_dbg(pm8001_ha, IO,
+ "PIO read len = %d\n", len);
+ } else if (t->ata_task.use_ncq &&
+ t->data_dir != DMA_NONE) {
+ len = sizeof(struct set_dev_bits_fis);
+ pm8001_dbg(pm8001_ha, IO, "FPDMA len = %d\n",
+ len);
+ } else {
+ len = sizeof(struct dev_to_host_fis);
+ pm8001_dbg(pm8001_ha, IO, "other len = %d\n",
+ len);
+ }
+ if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
+ resp->frame_len = len;
+ memcpy(&resp->ending_fis[0], sata_resp, len);
+ ts->buf_valid_size = sizeof(*resp);
+ } else
+ pm8001_dbg(pm8001_ha, IO,
+ "response too large\n");
+ }
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_ABORTED:
+ pm8001_dbg(pm8001_ha, IO, "IO_ABORTED IOMB Tag\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ /* following cases are to do cases */
+ case IO_UNDERFLOW:
+ /* SATA Completion with error */
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW param = %d\n", param);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_NO_DEVICE:
+ pm8001_dbg(pm8001_ha, IO, "IO_NO_DEVICE\n");
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ case IO_XFER_OPEN_RETRY_BACKOFF_THRESHOLD_REACHED:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_TMO:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_NO_DEST:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_COLLIDE:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_PATHWAY_BLOCKED:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ spin_unlock_irqrestore(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ spin_lock_irqsave(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ spin_unlock_irqrestore(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ spin_lock_irqsave(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ spin_unlock_irqrestore(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ spin_lock_irqsave(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_ACK_NAK_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_DMA:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_DMA\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_SATA_LINK_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_SATA_LINK_TIMEOUT\n");
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_REJECTED_NCQ_MODE\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_PORT_IN_RESET:
+ pm8001_dbg(pm8001_ha, IO, "IO_PORT_IN_RESET\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_NON_OPERATIONAL\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha, pm8001_dev,
+ IO_DS_NON_OPERATIONAL);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ spin_unlock_irqrestore(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ spin_lock_irqsave(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ return;
+ }
+ break;
+ case IO_DS_IN_RECOVERY:
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_IN_RECOVERY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_DS_IN_ERROR:
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_IN_ERROR\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha, pm8001_dev,
+ IO_DS_IN_ERROR);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ spin_unlock_irqrestore(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ spin_lock_irqsave(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ default:
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "Unknown status device_id %u status 0x%x tag %d\n",
+ pm8001_dev->device_id, status, tag);
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "task 0x%p done with io_status 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ if (t->slow_task)
+ complete(&t->slow_task->completion);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ spin_unlock_irqrestore(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ pm8001_ccb_task_free_done(pm8001_ha, ccb);
+ spin_lock_irqsave(&circularQ->oq_lock,
+ circularQ->lock_flags);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha,
+ struct outbound_queue_table *circularQ, void *piomb)
+{
+ struct sas_task *t;
+ struct task_status_struct *ts;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct sata_event_resp *psataPayload =
+ (struct sata_event_resp *)(piomb + 4);
+ u32 event = le32_to_cpu(psataPayload->event);
+ u32 tag = le32_to_cpu(psataPayload->tag);
+ u32 port_id = le32_to_cpu(psataPayload->port_id);
+ u32 dev_id = le32_to_cpu(psataPayload->device_id);
+
+ if (event)
+ pm8001_dbg(pm8001_ha, FAIL, "SATA EVENT 0x%x\n", event);
+
+ /* Check if this is NCQ error */
+ if (event == IO_XFER_ERROR_ABORTED_NCQ_MODE) {
+ /* tag value is invalid with this event */
+ pm8001_dbg(pm8001_ha, FAIL, "NCQ ERROR for device %#x tag %#x\n",
+ dev_id, tag);
+
+ /* find device using device id */
+ pm8001_dev = pm8001_find_dev(pm8001_ha, dev_id);
+ /* send read log extension by aborting the link - libata does what we want */
+ if (pm8001_dev) {
+ pm80xx_show_pending_commands(pm8001_ha, pm8001_dev);
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_XFER_ERROR_ABORTED_NCQ_MODE);
+ }
+ return;
+ }
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ if (unlikely(!t)) {
+ pm8001_dbg(pm8001_ha, FAIL, "task null, freeing CCB tag %d\n",
+ ccb->ccb_tag);
+ pm8001_ccb_free(pm8001_ha, ccb);
+ return;
+ }
+
+ if (unlikely(!t->lldd_task || !t->dev))
+ return;
+
+ ts = &t->task_status;
+ pm8001_dbg(pm8001_ha, IOERR, "port_id:0x%x, tag:0x%x, event:0x%x\n",
+ port_id, tag, event);
+ switch (event) {
+ case IO_OVERFLOW:
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ case IO_XFER_OPEN_RETRY_BACKOFF_THRESHOLD_REACHED:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_TMO:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_NO_DEST:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_COLLIDE:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_PATHWAY_BLOCKED:
+ pm8001_dbg(pm8001_ha, FAIL,
+ "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_QUEUE_FULL;
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_PEER_ABORTED:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PEER_ABORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_REJECTED_NCQ_MODE\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_UNEXPECTED_PHASE:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_UNEXPECTED_PHASE\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_XFER_RDY_OVERRUN\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_OFFSET_MISMATCH\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_CMD_FRAME_ISSUED:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_CMD_FRAME_ISSUED\n");
+ break;
+ case IO_XFER_PIO_SETUP_ERROR:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_PIO_SETUP_ERROR\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_INTERNAL_CRC_ERROR:
+ pm8001_dbg(pm8001_ha, FAIL,
+ "IO_XFR_ERROR_INTERNAL_CRC_ERROR\n");
+ /* TBC: used default set values */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_DMA_ACTIVATE_TIMEOUT:
+ pm8001_dbg(pm8001_ha, FAIL, "IO_XFR_DMA_ACTIVATE_TIMEOUT\n");
+ /* TBC: used default set values */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ default:
+ pm8001_dbg(pm8001_ha, IO, "Unknown status 0x%x\n", event);
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 param, i;
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status;
+ u32 tag;
+ struct smp_completion_resp *psmpPayload;
+ struct task_status_struct *ts;
+ struct pm8001_device *pm8001_dev;
+
+ psmpPayload = (struct smp_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psmpPayload->status);
+ tag = le32_to_cpu(psmpPayload->tag);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ param = le32_to_cpu(psmpPayload->param);
+ t = ccb->task;
+ ts = &t->task_status;
+ pm8001_dev = ccb->device;
+ if (status)
+ pm8001_dbg(pm8001_ha, FAIL, "smp IO status 0x%x\n", status);
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+
+ pm8001_dbg(pm8001_ha, DEV, "tag::0x%x status::0x%x\n", tag, status);
+
+ switch (status) {
+
+ case IO_SUCCESS:
+ pm8001_dbg(pm8001_ha, IO, "IO_SUCCESS\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_SAM_STAT_GOOD;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ if (pm8001_ha->smp_exp_mode == SMP_DIRECT) {
+ struct scatterlist *sg_resp = &t->smp_task.smp_resp;
+ u8 *payload;
+ void *to;
+
+ pm8001_dbg(pm8001_ha, IO,
+ "DIRECT RESPONSE Length:%d\n",
+ param);
+ to = kmap_atomic(sg_page(sg_resp));
+ payload = to + sg_resp->offset;
+ for (i = 0; i < param; i++) {
+ *(payload + i) = psmpPayload->_r_a[i];
+ pm8001_dbg(pm8001_ha, IO,
+ "SMP Byte%d DMA data 0x%x psmp 0x%x\n",
+ i, *(payload + i),
+ psmpPayload->_r_a[i]);
+ }
+ kunmap_atomic(to);
+ }
+ break;
+ case IO_ABORTED:
+ pm8001_dbg(pm8001_ha, IO, "IO_ABORTED IOMB\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_OVERFLOW:
+ pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ atomic_dec(&pm8001_dev->running_req);
+ break;
+ case IO_NO_DEVICE:
+ pm8001_dbg(pm8001_ha, IO, "IO_NO_DEVICE\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ case IO_ERROR_HW_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_ERROR_HW_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_SAM_STAT_BUSY;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_SAM_STAT_BUSY;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_SAM_STAT_BUSY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ case IO_XFER_OPEN_RETRY_BACKOFF_THRESHOLD_REACHED:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_TMO:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_NO_DEST:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_COLLIDE:
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_PATHWAY_BLOCKED:
+ pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_RX_FRAME:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_RX_FRAME\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_ERROR_INTERNAL_SMP_RESOURCE:
+ pm8001_dbg(pm8001_ha, IO, "IO_ERROR_INTERNAL_SMP_RESOURCE\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_QUEUE_FULL;
+ break;
+ case IO_PORT_IN_RESET:
+ pm8001_dbg(pm8001_ha, IO, "IO_PORT_IN_RESET\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_NON_OPERATIONAL\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_IN_RECOVERY:
+ pm8001_dbg(pm8001_ha, IO, "IO_DS_IN_RECOVERY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ pm8001_dbg(pm8001_ha, IO,
+ "IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n");
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ default:
+ pm8001_dbg(pm8001_ha, DEVIO, "Unknown status 0x%x\n", status);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ /* not allowed case. Therefore, return failed status */
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_dbg(pm8001_ha, FAIL,
+ "task 0x%p done with io_status 0x%x resp 0x%xstat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, ccb);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+/**
+ * pm80xx_hw_event_ack_req- For PM8001, some events need to acknowledge to FW.
+ * @pm8001_ha: our hba card information
+ * @Qnum: the outbound queue message number.
+ * @SEA: source of event to ack
+ * @port_id: port id.
+ * @phyId: phy id.
+ * @param0: parameter 0.
+ * @param1: parameter 1.
+ */
+static void pm80xx_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
+ u32 Qnum, u32 SEA, u32 port_id, u32 phyId, u32 param0, u32 param1)
+{
+ struct hw_event_ack_req payload;
+ u32 opc = OPC_INB_SAS_HW_EVENT_ACK;
+
+ memset((u8 *)&payload, 0, sizeof(payload));
+ payload.tag = cpu_to_le32(1);
+ payload.phyid_sea_portid = cpu_to_le32(((SEA & 0xFFFF) << 8) |
+ ((phyId & 0xFF) << 24) | (port_id & 0xFF));
+ payload.param0 = cpu_to_le32(param0);
+ payload.param1 = cpu_to_le32(param1);
+
+ pm8001_mpi_build_cmd(pm8001_ha, Qnum, opc, &payload,
+ sizeof(payload), 0);
+}
+
+static int pm80xx_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+ u32 phyId, u32 phy_op);
+
+static void hw_event_port_recover(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ struct hw_event_resp *pPayload = (struct hw_event_resp *)(piomb + 4);
+ u32 phyid_npip_portstate = le32_to_cpu(pPayload->phyid_npip_portstate);
+ u8 phy_id = (u8)((phyid_npip_portstate & 0xFF0000) >> 16);
+ u32 lr_status_evt_portid =
+ le32_to_cpu(pPayload->lr_status_evt_portid);
+ u8 deviceType = pPayload->sas_identify.dev_type;
+ u8 link_rate = (u8)((lr_status_evt_portid & 0xF0000000) >> 28);
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ u8 port_id = (u8)(lr_status_evt_portid & 0x000000FF);
+ struct pm8001_port *port = &pm8001_ha->port[port_id];
+
+ if (deviceType == SAS_END_DEVICE) {
+ pm80xx_chip_phy_ctl_req(pm8001_ha, phy_id,
+ PHY_NOTIFY_ENABLE_SPINUP);
+ }
+
+ port->wide_port_phymap |= (1U << phy_id);
+ pm8001_get_lrate_mode(phy, link_rate);
+ phy->sas_phy.oob_mode = SAS_OOB_MODE;
+ phy->phy_state = PHY_STATE_LINK_UP_SPCV;
+ phy->phy_attached = 1;
+}
+
+/**
+ * hw_event_sas_phy_up - FW tells me a SAS phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_status_evt_portid =
+ le32_to_cpu(pPayload->lr_status_evt_portid);
+ u32 phyid_npip_portstate = le32_to_cpu(pPayload->phyid_npip_portstate);
+
+ u8 link_rate =
+ (u8)((lr_status_evt_portid & 0xF0000000) >> 28);
+ u8 port_id = (u8)(lr_status_evt_portid & 0x000000FF);
+ u8 phy_id =
+ (u8)((phyid_npip_portstate & 0xFF0000) >> 16);
+ u8 portstate = (u8)(phyid_npip_portstate & 0x0000000F);
+
+ struct pm8001_port *port = &pm8001_ha->port[port_id];
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ unsigned long flags;
+ u8 deviceType = pPayload->sas_identify.dev_type;
+ phy->port = port;
+ port->port_id = port_id;
+ port->port_state = portstate;
+ port->wide_port_phymap |= (1U << phy_id);
+ phy->phy_state = PHY_STATE_LINK_UP_SPCV;
+ pm8001_dbg(pm8001_ha, MSG,
+ "portid:%d; phyid:%d; linkrate:%d; portstate:%x; devicetype:%x\n",
+ port_id, phy_id, link_rate, portstate, deviceType);
+
+ switch (deviceType) {
+ case SAS_PHY_UNUSED:
+ pm8001_dbg(pm8001_ha, MSG, "device type no device.\n");
+ break;
+ case SAS_END_DEVICE:
+ pm8001_dbg(pm8001_ha, MSG, "end device.\n");
+ pm80xx_chip_phy_ctl_req(pm8001_ha, phy_id,
+ PHY_NOTIFY_ENABLE_SPINUP);
+ port->port_attached = 1;
+ pm8001_get_lrate_mode(phy, link_rate);
+ break;
+ case SAS_EDGE_EXPANDER_DEVICE:
+ pm8001_dbg(pm8001_ha, MSG, "expander device.\n");
+ port->port_attached = 1;
+ pm8001_get_lrate_mode(phy, link_rate);
+ break;
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ pm8001_dbg(pm8001_ha, MSG, "fanout expander device.\n");
+ port->port_attached = 1;
+ pm8001_get_lrate_mode(phy, link_rate);
+ break;
+ default:
+ pm8001_dbg(pm8001_ha, DEVIO, "unknown device type(%x)\n",
+ deviceType);
+ break;
+ }
+ phy->phy_type |= PORT_TYPE_SAS;
+ phy->identify.device_type = deviceType;
+ phy->phy_attached = 1;
+ if (phy->identify.device_type == SAS_END_DEVICE)
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != SAS_PHY_UNUSED)
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SMP;
+ phy->sas_phy.oob_mode = SAS_OOB_MODE;
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE, GFP_ATOMIC);
+ spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+ memcpy(phy->frame_rcvd, &pPayload->sas_identify,
+ sizeof(struct sas_identify_frame)-4);
+ phy->frame_rcvd_size = sizeof(struct sas_identify_frame) - 4;
+ pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+ spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+ if (pm8001_ha->flags == PM8001F_RUN_TIME)
+ mdelay(200); /* delay a moment to wait for disk to spin up */
+ pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_sata_phy_up - FW tells me a SATA phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 phyid_npip_portstate = le32_to_cpu(pPayload->phyid_npip_portstate);
+ u32 lr_status_evt_portid =
+ le32_to_cpu(pPayload->lr_status_evt_portid);
+ u8 link_rate =
+ (u8)((lr_status_evt_portid & 0xF0000000) >> 28);
+ u8 port_id = (u8)(lr_status_evt_portid & 0x000000FF);
+ u8 phy_id =
+ (u8)((phyid_npip_portstate & 0xFF0000) >> 16);
+
+ u8 portstate = (u8)(phyid_npip_portstate & 0x0000000F);
+
+ struct pm8001_port *port = &pm8001_ha->port[port_id];
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ unsigned long flags;
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_SATA_PHY_UP phyid:%#x port_id:%#x link_rate:%d portstate:%#x\n",
+ phy_id, port_id, link_rate, portstate);
+
+ phy->port = port;
+ port->port_id = port_id;
+ port->port_state = portstate;
+ phy->phy_state = PHY_STATE_LINK_UP_SPCV;
+ port->port_attached = 1;
+ pm8001_get_lrate_mode(phy, link_rate);
+ phy->phy_type |= PORT_TYPE_SATA;
+ phy->phy_attached = 1;
+ phy->sas_phy.oob_mode = SATA_OOB_MODE;
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE, GFP_ATOMIC);
+ spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+ memcpy(phy->frame_rcvd, ((u8 *)&pPayload->sata_fis - 4),
+ sizeof(struct dev_to_host_fis));
+ phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+ phy->identify.device_type = SAS_SATA_DEV;
+ pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+ spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+ pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_phy_down - we should notify the libsas the phy is down.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+
+ u32 lr_status_evt_portid =
+ le32_to_cpu(pPayload->lr_status_evt_portid);
+ u8 port_id = (u8)(lr_status_evt_portid & 0x000000FF);
+ u32 phyid_npip_portstate = le32_to_cpu(pPayload->phyid_npip_portstate);
+ u8 phy_id =
+ (u8)((phyid_npip_portstate & 0xFF0000) >> 16);
+ u8 portstate = (u8)(phyid_npip_portstate & 0x0000000F);
+
+ struct pm8001_port *port = &pm8001_ha->port[port_id];
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ u32 port_sata = (phy->phy_type & PORT_TYPE_SATA);
+ port->port_state = portstate;
+ phy->identify.device_type = 0;
+ phy->phy_attached = 0;
+ switch (portstate) {
+ case PORT_VALID:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PHY_DOWN phyid:%#x port_id:%#x portstate: PORT_VALID\n",
+ phy_id, port_id);
+ break;
+ case PORT_INVALID:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PHY_DOWN phyid:%#x port_id:%#x portstate: PORT_INVALID\n",
+ phy_id, port_id);
+ pm8001_dbg(pm8001_ha, MSG,
+ " Last phy Down and port invalid\n");
+ if (port_sata) {
+ phy->phy_type = 0;
+ port->port_attached = 0;
+ pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+ port_id, phy_id, 0, 0);
+ }
+ sas_phy_disconnected(&phy->sas_phy);
+ break;
+ case PORT_IN_RESET:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PHY_DOWN phyid:%#x port_id:%#x portstate: PORT_IN_RESET\n",
+ phy_id, port_id);
+ break;
+ case PORT_NOT_ESTABLISHED:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PHY_DOWN phyid:%#x port_id:%#x portstate: PORT_NOT_ESTABLISHED\n",
+ phy_id, port_id);
+ port->port_attached = 0;
+ break;
+ case PORT_LOSTCOMM:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PHY_DOWN phyid:%#x port_id:%#x portstate: PORT_LOSTCOMM\n",
+ phy_id, port_id);
+ pm8001_dbg(pm8001_ha, MSG, " Last phy Down and port invalid\n");
+ if (port_sata) {
+ port->port_attached = 0;
+ phy->phy_type = 0;
+ pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+ port_id, phy_id, 0, 0);
+ }
+ sas_phy_disconnected(&phy->sas_phy);
+ break;
+ default:
+ port->port_attached = 0;
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PHY_DOWN phyid:%#x port_id:%#x portstate:%#x\n",
+ phy_id, port_id, portstate);
+ break;
+
+ }
+ if (port_sata && (portstate != PORT_IN_RESET))
+ sas_notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL,
+ GFP_ATOMIC);
+}
+
+static int mpi_phy_start_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct phy_start_resp *pPayload =
+ (struct phy_start_resp *)(piomb + 4);
+ u32 status =
+ le32_to_cpu(pPayload->status);
+ u32 phy_id =
+ le32_to_cpu(pPayload->phyid) & 0xFF;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ u32 tag = le32_to_cpu(pPayload->tag);
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "phy start resp status:0x%x, phyid:0x%x, tag 0x%x\n",
+ status, phy_id, tag);
+ if (status == 0)
+ phy->phy_state = PHY_LINK_DOWN;
+
+ if (pm8001_ha->flags == PM8001F_RUN_TIME &&
+ phy->enable_completion != NULL) {
+ complete(phy->enable_completion);
+ phy->enable_completion = NULL;
+ }
+
+ pm8001_tag_free(pm8001_ha, tag);
+ return 0;
+
+}
+
+/**
+ * mpi_thermal_hw_event - a thermal hw event has come.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_thermal_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct thermal_hw_event *pPayload =
+ (struct thermal_hw_event *)(piomb + 4);
+
+ u32 thermal_event = le32_to_cpu(pPayload->thermal_event);
+ u32 rht_lht = le32_to_cpu(pPayload->rht_lht);
+
+ if (thermal_event & 0x40) {
+ pm8001_dbg(pm8001_ha, IO,
+ "Thermal Event: Local high temperature violated!\n");
+ pm8001_dbg(pm8001_ha, IO,
+ "Thermal Event: Measured local high temperature %d\n",
+ ((rht_lht & 0xFF00) >> 8));
+ }
+ if (thermal_event & 0x10) {
+ pm8001_dbg(pm8001_ha, IO,
+ "Thermal Event: Remote high temperature violated!\n");
+ pm8001_dbg(pm8001_ha, IO,
+ "Thermal Event: Measured remote high temperature %d\n",
+ ((rht_lht & 0xFF000000) >> 24));
+ }
+ return 0;
+}
+
+/**
+ * mpi_hw_event - The hw event has come.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ unsigned long flags, i;
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_status_evt_portid =
+ le32_to_cpu(pPayload->lr_status_evt_portid);
+ u32 phyid_npip_portstate = le32_to_cpu(pPayload->phyid_npip_portstate);
+ u8 port_id = (u8)(lr_status_evt_portid & 0x000000FF);
+ u8 phy_id =
+ (u8)((phyid_npip_portstate & 0xFF0000) >> 16);
+ u8 portstate = (u8)(phyid_npip_portstate & 0x0000000F);
+ u16 eventType =
+ (u16)((lr_status_evt_portid & 0x00FFFF00) >> 8);
+ u8 status =
+ (u8)((lr_status_evt_portid & 0x0F000000) >> 24);
+ struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ struct pm8001_port *port = &pm8001_ha->port[port_id];
+ struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+ pm8001_dbg(pm8001_ha, DEV,
+ "portid:%d phyid:%d event:0x%x status:0x%x\n",
+ port_id, phy_id, eventType, status);
+
+ switch (eventType) {
+
+ case HW_EVENT_SAS_PHY_UP:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_SAS_PHY_UP phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ hw_event_sas_phy_up(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_SATA_PHY_UP:
+ hw_event_sata_phy_up(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_SATA_SPINUP_HOLD:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_SATA_SPINUP_HOLD phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ sas_notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD,
+ GFP_ATOMIC);
+ break;
+ case HW_EVENT_PHY_DOWN:
+ hw_event_phy_down(pm8001_ha, piomb);
+ phy->phy_state = PHY_LINK_DISABLE;
+ break;
+ case HW_EVENT_PORT_INVALID:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PORT_INVALID phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
+ break;
+ /* the broadcast change primitive received, tell the LIBSAS this event
+ to revalidate the sas domain*/
+ case HW_EVENT_BROADCAST_CHANGE:
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_BROADCAST_CHANGE\n");
+ pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_BROADCAST_CHANGE,
+ port_id, phy_id, 1, 0);
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_CHANGE;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
+ GFP_ATOMIC);
+ break;
+ case HW_EVENT_PHY_ERROR:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PHY_ERROR phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ sas_phy_disconnected(&phy->sas_phy);
+ phy->phy_attached = 0;
+ sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR, GFP_ATOMIC);
+ break;
+ case HW_EVENT_BROADCAST_EXP:
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_BROADCAST_EXP\n");
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_EXP;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
+ GFP_ATOMIC);
+ break;
+ case HW_EVENT_LINK_ERR_INVALID_DWORD:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_LINK_ERR_INVALID_DWORD phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0);
+ break;
+ case HW_EVENT_LINK_ERR_DISPARITY_ERROR:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_LINK_ERR_DISPARITY_ERROR phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_DISPARITY_ERROR,
+ port_id, phy_id, 0, 0);
+ break;
+ case HW_EVENT_LINK_ERR_CODE_VIOLATION:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_LINK_ERR_CODE_VIOLATION phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_CODE_VIOLATION,
+ port_id, phy_id, 0, 0);
+ break;
+ case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH,
+ port_id, phy_id, 0, 0);
+ break;
+ case HW_EVENT_MALFUNCTION:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_MALFUNCTION phyid:%#x\n", phy_id);
+ break;
+ case HW_EVENT_BROADCAST_SES:
+ pm8001_dbg(pm8001_ha, MSG, "HW_EVENT_BROADCAST_SES\n");
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_SES;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
+ GFP_ATOMIC);
+ break;
+ case HW_EVENT_INBOUND_CRC_ERROR:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_INBOUND_CRC_ERROR phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_INBOUND_CRC_ERROR,
+ port_id, phy_id, 0, 0);
+ break;
+ case HW_EVENT_HARD_RESET_RECEIVED:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_HARD_RESET_RECEIVED phyid:%#x\n", phy_id);
+ sas_notify_port_event(sas_phy, PORTE_HARD_RESET, GFP_ATOMIC);
+ break;
+ case HW_EVENT_ID_FRAME_TIMEOUT:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_ID_FRAME_TIMEOUT phyid:%#x\n", phy_id);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
+ break;
+ case HW_EVENT_LINK_ERR_PHY_RESET_FAILED:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_LINK_ERR_PHY_RESET_FAILED phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_PHY_RESET_FAILED,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
+ break;
+ case HW_EVENT_PORT_RESET_TIMER_TMO:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PORT_RESET_TIMER_TMO phyid:%#x port_id:%#x portstate:%#x\n",
+ phy_id, port_id, portstate);
+ if (!pm8001_ha->phy[phy_id].reset_completion) {
+ pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+ port_id, phy_id, 0, 0);
+ }
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ port->port_state = portstate;
+ sas_notify_port_event(sas_phy, PORTE_LINK_RESET_ERR,
+ GFP_ATOMIC);
+ if (pm8001_ha->phy[phy_id].reset_completion) {
+ pm8001_ha->phy[phy_id].port_reset_status =
+ PORT_RESET_TMO;
+ complete(pm8001_ha->phy[phy_id].reset_completion);
+ pm8001_ha->phy[phy_id].reset_completion = NULL;
+ }
+ break;
+ case HW_EVENT_PORT_RECOVERY_TIMER_TMO:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PORT_RECOVERY_TIMER_TMO phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ pm80xx_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_PORT_RECOVERY_TIMER_TMO,
+ port_id, phy_id, 0, 0);
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ if (port->wide_port_phymap & (1 << i)) {
+ phy = &pm8001_ha->phy[i];
+ sas_notify_phy_event(&phy->sas_phy,
+ PHYE_LOSS_OF_SIGNAL, GFP_ATOMIC);
+ port->wide_port_phymap &= ~(1 << i);
+ }
+ }
+ break;
+ case HW_EVENT_PORT_RECOVER:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PORT_RECOVER phyid:%#x port_id:%#x\n",
+ phy_id, port_id);
+ hw_event_port_recover(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_PORT_RESET_COMPLETE:
+ pm8001_dbg(pm8001_ha, EVENT,
+ "HW_EVENT_PORT_RESET_COMPLETE phyid:%#x port_id:%#x portstate:%#x\n",
+ phy_id, port_id, portstate);
+ if (pm8001_ha->phy[phy_id].reset_completion) {
+ pm8001_ha->phy[phy_id].port_reset_status =
+ PORT_RESET_SUCCESS;
+ complete(pm8001_ha->phy[phy_id].reset_completion);
+ pm8001_ha->phy[phy_id].reset_completion = NULL;
+ }
+ phy->phy_attached = 1;
+ phy->phy_state = PHY_STATE_LINK_UP_SPCV;
+ port->port_state = portstate;
+ break;
+ case EVENT_BROADCAST_ASYNCH_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "EVENT_BROADCAST_ASYNCH_EVENT\n");
+ break;
+ default:
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "Unknown event portid:%d phyid:%d event:0x%x status:0x%x\n",
+ port_id, phy_id, eventType, status);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * mpi_phy_stop_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_phy_stop_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct phy_stop_resp *pPayload =
+ (struct phy_stop_resp *)(piomb + 4);
+ u32 status =
+ le32_to_cpu(pPayload->status);
+ u32 phyid =
+ le32_to_cpu(pPayload->phyid) & 0xFF;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phyid];
+ u32 tag = le32_to_cpu(pPayload->tag);
+
+ pm8001_dbg(pm8001_ha, MSG, "phy:0x%x status:0x%x tag 0x%x\n", phyid,
+ status, tag);
+ if (status == PHY_STOP_SUCCESS ||
+ status == PHY_STOP_ERR_DEVICE_ATTACHED) {
+ phy->phy_state = PHY_LINK_DISABLE;
+ phy->sas_phy.phy->negotiated_linkrate = SAS_PHY_DISABLED;
+ phy->sas_phy.linkrate = SAS_PHY_DISABLED;
+ }
+
+ pm8001_tag_free(pm8001_ha, tag);
+ return 0;
+}
+
+/**
+ * mpi_set_controller_config_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_set_controller_config_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ struct set_ctrl_cfg_resp *pPayload =
+ (struct set_ctrl_cfg_resp *)(piomb + 4);
+ u32 status = le32_to_cpu(pPayload->status);
+ u32 err_qlfr_pgcd = le32_to_cpu(pPayload->err_qlfr_pgcd);
+ u32 tag = le32_to_cpu(pPayload->tag);
+
+ pm8001_dbg(pm8001_ha, MSG,
+ "SET CONTROLLER RESP: status 0x%x qlfr_pgcd 0x%x tag 0x%x\n",
+ status, err_qlfr_pgcd, tag);
+ pm8001_tag_free(pm8001_ha, tag);
+ return 0;
+}
+
+/**
+ * mpi_get_controller_config_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_get_controller_config_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ pm8001_dbg(pm8001_ha, MSG, " pm80xx_addition_functionality\n");
+
+ return 0;
+}
+
+/**
+ * mpi_get_phy_profile_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_get_phy_profile_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ pm8001_dbg(pm8001_ha, MSG, " pm80xx_addition_functionality\n");
+
+ return 0;
+}
+
+/**
+ * mpi_flash_op_ext_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_flash_op_ext_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ pm8001_dbg(pm8001_ha, MSG, " pm80xx_addition_functionality\n");
+
+ return 0;
+}
+
+/**
+ * mpi_set_phy_profile_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_set_phy_profile_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ u32 tag;
+ u8 page_code;
+ int rc = 0;
+ struct set_phy_profile_resp *pPayload =
+ (struct set_phy_profile_resp *)(piomb + 4);
+ u32 ppc_phyid = le32_to_cpu(pPayload->ppc_phyid);
+ u32 status = le32_to_cpu(pPayload->status);
+
+ tag = le32_to_cpu(pPayload->tag);
+ page_code = (u8)((ppc_phyid & 0xFF00) >> 8);
+ if (status) {
+ /* status is FAILED */
+ pm8001_dbg(pm8001_ha, FAIL,
+ "PhyProfile command failed with status 0x%08X\n",
+ status);
+ rc = -1;
+ } else {
+ if (page_code != SAS_PHY_ANALOG_SETTINGS_PAGE) {
+ pm8001_dbg(pm8001_ha, FAIL, "Invalid page code 0x%X\n",
+ page_code);
+ rc = -1;
+ }
+ }
+ pm8001_tag_free(pm8001_ha, tag);
+ return rc;
+}
+
+/**
+ * mpi_kek_management_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_kek_management_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ struct kek_mgmt_resp *pPayload = (struct kek_mgmt_resp *)(piomb + 4);
+
+ u32 status = le32_to_cpu(pPayload->status);
+ u32 kidx_new_curr_ksop = le32_to_cpu(pPayload->kidx_new_curr_ksop);
+ u32 err_qlfr = le32_to_cpu(pPayload->err_qlfr);
+
+ pm8001_dbg(pm8001_ha, MSG,
+ "KEK MGMT RESP. Status 0x%x idx_ksop 0x%x err_qlfr 0x%x\n",
+ status, kidx_new_curr_ksop, err_qlfr);
+
+ return 0;
+}
+
+/**
+ * mpi_dek_management_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_dek_management_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ pm8001_dbg(pm8001_ha, MSG, " pm80xx_addition_functionality\n");
+
+ return 0;
+}
+
+/**
+ * ssp_coalesced_comp_resp - SPCv specific
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int ssp_coalesced_comp_resp(struct pm8001_hba_info *pm8001_ha,
+ void *piomb)
+{
+ pm8001_dbg(pm8001_ha, MSG, " pm80xx_addition_functionality\n");
+
+ return 0;
+}
+
+/**
+ * process_one_iomb - process one outbound Queue memory block
+ * @pm8001_ha: our hba card information
+ * @circularQ: outbound circular queue
+ * @piomb: IO message buffer
+ */
+static void process_one_iomb(struct pm8001_hba_info *pm8001_ha,
+ struct outbound_queue_table *circularQ, void *piomb)
+{
+ __le32 pHeader = *(__le32 *)piomb;
+ u32 opc = (u32)((le32_to_cpu(pHeader)) & 0xFFF);
+
+ switch (opc) {
+ case OPC_OUB_ECHO:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_ECHO\n");
+ break;
+ case OPC_OUB_HW_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_HW_EVENT\n");
+ mpi_hw_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_THERM_HW_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_THERMAL_EVENT\n");
+ mpi_thermal_hw_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_COMP:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_COMP\n");
+ mpi_ssp_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SMP_COMP:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SMP_COMP\n");
+ mpi_smp_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_LOCAL_PHY_CNTRL:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_LOCAL_PHY_CNTRL\n");
+ pm8001_mpi_local_phy_ctl(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEV_REGIST:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_DEV_REGIST\n");
+ pm8001_mpi_reg_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEREG_DEV:
+ pm8001_dbg(pm8001_ha, MSG, "unregister the device\n");
+ pm8001_mpi_dereg_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_DEV_HANDLE:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_DEV_HANDLE\n");
+ break;
+ case OPC_OUB_SATA_COMP:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SATA_COMP\n");
+ mpi_sata_completion(pm8001_ha, circularQ, piomb);
+ break;
+ case OPC_OUB_SATA_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SATA_EVENT\n");
+ mpi_sata_event(pm8001_ha, circularQ, piomb);
+ break;
+ case OPC_OUB_SSP_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_EVENT\n");
+ mpi_ssp_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEV_HANDLE_ARRIV:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_DEV_HANDLE_ARRIV\n");
+ /*This is for target*/
+ break;
+ case OPC_OUB_SSP_RECV_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_RECV_EVENT\n");
+ /*This is for target*/
+ break;
+ case OPC_OUB_FW_FLASH_UPDATE:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_FW_FLASH_UPDATE\n");
+ pm8001_mpi_fw_flash_update_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GPIO_RESPONSE:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GPIO_RESPONSE\n");
+ break;
+ case OPC_OUB_GPIO_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GPIO_EVENT\n");
+ break;
+ case OPC_OUB_GENERAL_EVENT:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GENERAL_EVENT\n");
+ pm8001_mpi_general_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_ABORT_RSP:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SSP_ABORT_RSP\n");
+ pm8001_mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SATA_ABORT_RSP:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SATA_ABORT_RSP\n");
+ pm8001_mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SAS_DIAG_MODE_START_END:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_SAS_DIAG_MODE_START_END\n");
+ break;
+ case OPC_OUB_SAS_DIAG_EXECUTE:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SAS_DIAG_EXECUTE\n");
+ break;
+ case OPC_OUB_GET_TIME_STAMP:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_TIME_STAMP\n");
+ break;
+ case OPC_OUB_SAS_HW_EVENT_ACK:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SAS_HW_EVENT_ACK\n");
+ break;
+ case OPC_OUB_PORT_CONTROL:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_PORT_CONTROL\n");
+ break;
+ case OPC_OUB_SMP_ABORT_RSP:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SMP_ABORT_RSP\n");
+ pm8001_mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_NVMD_DATA:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_NVMD_DATA\n");
+ pm8001_mpi_get_nvmd_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SET_NVMD_DATA:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SET_NVMD_DATA\n");
+ pm8001_mpi_set_nvmd_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEVICE_HANDLE_REMOVAL:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_DEVICE_HANDLE_REMOVAL\n");
+ break;
+ case OPC_OUB_SET_DEVICE_STATE:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SET_DEVICE_STATE\n");
+ pm8001_mpi_set_dev_state_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_DEVICE_STATE:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_GET_DEVICE_STATE\n");
+ break;
+ case OPC_OUB_SET_DEV_INFO:
+ pm8001_dbg(pm8001_ha, MSG, "OPC_OUB_SET_DEV_INFO\n");
+ break;
+ /* spcv specific commands */
+ case OPC_OUB_PHY_START_RESP:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_PHY_START_RESP opcode:%x\n", opc);
+ mpi_phy_start_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_PHY_STOP_RESP:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_PHY_STOP_RESP opcode:%x\n", opc);
+ mpi_phy_stop_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SET_CONTROLLER_CONFIG:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_SET_CONTROLLER_CONFIG opcode:%x\n", opc);
+ mpi_set_controller_config_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_CONTROLLER_CONFIG:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_GET_CONTROLLER_CONFIG opcode:%x\n", opc);
+ mpi_get_controller_config_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_PHY_PROFILE:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_GET_PHY_PROFILE opcode:%x\n", opc);
+ mpi_get_phy_profile_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_FLASH_OP_EXT:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_FLASH_OP_EXT opcode:%x\n", opc);
+ mpi_flash_op_ext_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SET_PHY_PROFILE:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_SET_PHY_PROFILE opcode:%x\n", opc);
+ mpi_set_phy_profile_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_KEK_MANAGEMENT_RESP:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_KEK_MANAGEMENT_RESP opcode:%x\n", opc);
+ mpi_kek_management_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEK_MANAGEMENT_RESP:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_DEK_MANAGEMENT_RESP opcode:%x\n", opc);
+ mpi_dek_management_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_COALESCED_COMP_RESP:
+ pm8001_dbg(pm8001_ha, MSG,
+ "OPC_OUB_SSP_COALESCED_COMP_RESP opcode:%x\n", opc);
+ ssp_coalesced_comp_resp(pm8001_ha, piomb);
+ break;
+ default:
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "Unknown outbound Queue IOMB OPC = 0x%x\n", opc);
+ break;
+ }
+}
+
+static void print_scratchpad_registers(struct pm8001_hba_info *pm8001_ha)
+{
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_SCRATCH_PAD_0: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_SCRATCH_PAD_1:0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_SCRATCH_PAD_2: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_SCRATCH_PAD_3: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_HOST_SCRATCH_PAD_0: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_HOST_SCRATCH_PAD_1: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_1));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_HOST_SCRATCH_PAD_2: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_2));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_HOST_SCRATCH_PAD_3: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_3));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_HOST_SCRATCH_PAD_4: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_4));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_HOST_SCRATCH_PAD_5: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_5));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_RSVD_SCRATCH_PAD_0: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_RSVD_0));
+ pm8001_dbg(pm8001_ha, FAIL, "MSGU_RSVD_SCRATCH_PAD_1: 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_RSVD_1));
+}
+
+static int process_oq(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+ struct outbound_queue_table *circularQ;
+ void *pMsg1 = NULL;
+ u8 bc;
+ u32 ret = MPI_IO_STATUS_FAIL;
+ u32 regval;
+
+ /*
+ * Fatal errors are programmed to be signalled in irq vector
+ * pm8001_ha->max_q_num - 1 through pm8001_ha->main_cfg_tbl.pm80xx_tbl.
+ * fatal_err_interrupt
+ */
+ if (vec == (pm8001_ha->max_q_num - 1)) {
+ u32 mipsall_ready;
+
+ if (pm8001_ha->chip_id == chip_8008 ||
+ pm8001_ha->chip_id == chip_8009)
+ mipsall_ready = SCRATCH_PAD_MIPSALL_READY_8PORT;
+ else
+ mipsall_ready = SCRATCH_PAD_MIPSALL_READY_16PORT;
+
+ regval = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ if ((regval & mipsall_ready) != mipsall_ready) {
+ pm8001_ha->controller_fatal_error = true;
+ pm8001_dbg(pm8001_ha, FAIL,
+ "Firmware Fatal error! Regval:0x%x\n",
+ regval);
+ pm80xx_fatal_error_uevent_emit(pm8001_ha, REPORTER_FIRMWARE);
+ pm8001_handle_event(pm8001_ha, NULL, IO_FATAL_ERROR);
+ print_scratchpad_registers(pm8001_ha);
+ return ret;
+ } else {
+ /*read scratchpad rsvd 0 register*/
+ regval = pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_RSVD_0);
+ switch (regval) {
+ case NON_FATAL_SPBC_LBUS_ECC_ERR:
+ case NON_FATAL_BDMA_ERR:
+ case NON_FATAL_THERM_OVERTEMP_ERR:
+ /*Clear the register*/
+ pm8001_cw32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_RSVD_0,
+ 0x00000000);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ circularQ = &pm8001_ha->outbnd_q_tbl[vec];
+ spin_lock_irqsave(&circularQ->oq_lock, circularQ->lock_flags);
+ do {
+ /* spurious interrupt during setup if kexec-ing and
+ * driver doing a doorbell access w/ the pre-kexec oq
+ * interrupt setup.
+ */
+ if (!circularQ->pi_virt)
+ break;
+ ret = pm8001_mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
+ if (MPI_IO_STATUS_SUCCESS == ret) {
+ /* process the outbound message */
+ process_one_iomb(pm8001_ha, circularQ,
+ (void *)(pMsg1 - 4));
+ /* free the message from the outbound circular buffer */
+ pm8001_mpi_msg_free_set(pm8001_ha, pMsg1,
+ circularQ, bc);
+ }
+ if (MPI_IO_STATUS_BUSY == ret) {
+ /* Update the producer index from SPC */
+ circularQ->producer_index =
+ cpu_to_le32(pm8001_read_32(circularQ->pi_virt));
+ if (le32_to_cpu(circularQ->producer_index) ==
+ circularQ->consumer_idx)
+ /* OQ is empty */
+ break;
+ }
+ } while (1);
+ spin_unlock_irqrestore(&circularQ->oq_lock, circularQ->lock_flags);
+ return ret;
+}
+
+/* DMA_... to our direction translation. */
+static const u8 data_dir_flags[] = {
+ [DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT, /* UNSPECIFIED */
+ [DMA_TO_DEVICE] = DATA_DIR_OUT, /* OUTBOUND */
+ [DMA_FROM_DEVICE] = DATA_DIR_IN, /* INBOUND */
+ [DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */
+};
+
+static void build_smp_cmd(u32 deviceID, __le32 hTag,
+ struct smp_req *psmp_cmd, int mode, int length)
+{
+ psmp_cmd->tag = hTag;
+ psmp_cmd->device_id = cpu_to_le32(deviceID);
+ if (mode == SMP_DIRECT) {
+ length = length - 4; /* subtract crc */
+ psmp_cmd->len_ip_ir = cpu_to_le32(length << 16);
+ } else {
+ psmp_cmd->len_ip_ir = cpu_to_le32(1|(1 << 1));
+ }
+}
+
+/**
+ * pm80xx_chip_smp_req - send an SMP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm80xx_chip_smp_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ int elem, rc;
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct scatterlist *sg_req, *sg_resp, *smp_req;
+ u32 req_len, resp_len;
+ struct smp_req smp_cmd;
+ u32 opc;
+ u32 i, length;
+ u8 *payload;
+ u8 *to;
+
+ memset(&smp_cmd, 0, sizeof(smp_cmd));
+ /*
+ * DMA-map SMP request, response buffers
+ */
+ sg_req = &task->smp_task.smp_req;
+ elem = dma_map_sg(pm8001_ha->dev, sg_req, 1, DMA_TO_DEVICE);
+ if (!elem)
+ return -ENOMEM;
+ req_len = sg_dma_len(sg_req);
+
+ sg_resp = &task->smp_task.smp_resp;
+ elem = dma_map_sg(pm8001_ha->dev, sg_resp, 1, DMA_FROM_DEVICE);
+ if (!elem) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ resp_len = sg_dma_len(sg_resp);
+ /* must be in dwords */
+ if ((req_len & 0x3) || (resp_len & 0x3)) {
+ rc = -EINVAL;
+ goto err_out_2;
+ }
+
+ opc = OPC_INB_SMP_REQUEST;
+ smp_cmd.tag = cpu_to_le32(ccb->ccb_tag);
+
+ length = sg_req->length;
+ pm8001_dbg(pm8001_ha, IO, "SMP Frame Length %d\n", sg_req->length);
+ if (!(length - 8))
+ pm8001_ha->smp_exp_mode = SMP_DIRECT;
+ else
+ pm8001_ha->smp_exp_mode = SMP_INDIRECT;
+
+
+ smp_req = &task->smp_task.smp_req;
+ to = kmap_atomic(sg_page(smp_req));
+ payload = to + smp_req->offset;
+
+ /* INDIRECT MODE command settings. Use DMA */
+ if (pm8001_ha->smp_exp_mode == SMP_INDIRECT) {
+ pm8001_dbg(pm8001_ha, IO, "SMP REQUEST INDIRECT MODE\n");
+ /* for SPCv indirect mode. Place the top 4 bytes of
+ * SMP Request header here. */
+ for (i = 0; i < 4; i++)
+ smp_cmd.smp_req16[i] = *(payload + i);
+ /* exclude top 4 bytes for SMP req header */
+ smp_cmd.long_smp_req.long_req_addr =
+ cpu_to_le64((u64)sg_dma_address
+ (&task->smp_task.smp_req) + 4);
+ /* exclude 4 bytes for SMP req header and CRC */
+ smp_cmd.long_smp_req.long_req_size =
+ cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-8);
+ smp_cmd.long_smp_req.long_resp_addr =
+ cpu_to_le64((u64)sg_dma_address
+ (&task->smp_task.smp_resp));
+ smp_cmd.long_smp_req.long_resp_size =
+ cpu_to_le32((u32)sg_dma_len
+ (&task->smp_task.smp_resp)-4);
+ } else { /* DIRECT MODE */
+ smp_cmd.long_smp_req.long_req_addr =
+ cpu_to_le64((u64)sg_dma_address
+ (&task->smp_task.smp_req));
+ smp_cmd.long_smp_req.long_req_size =
+ cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4);
+ smp_cmd.long_smp_req.long_resp_addr =
+ cpu_to_le64((u64)sg_dma_address
+ (&task->smp_task.smp_resp));
+ smp_cmd.long_smp_req.long_resp_size =
+ cpu_to_le32
+ ((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
+ }
+ if (pm8001_ha->smp_exp_mode == SMP_DIRECT) {
+ pm8001_dbg(pm8001_ha, IO, "SMP REQUEST DIRECT MODE\n");
+ for (i = 0; i < length; i++)
+ if (i < 16) {
+ smp_cmd.smp_req16[i] = *(payload + i);
+ pm8001_dbg(pm8001_ha, IO,
+ "Byte[%d]:%x (DMA data:%x)\n",
+ i, smp_cmd.smp_req16[i],
+ *(payload));
+ } else {
+ smp_cmd.smp_req[i] = *(payload + i);
+ pm8001_dbg(pm8001_ha, IO,
+ "Byte[%d]:%x (DMA data:%x)\n",
+ i, smp_cmd.smp_req[i],
+ *(payload));
+ }
+ }
+ kunmap_atomic(to);
+ build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag,
+ &smp_cmd, pm8001_ha->smp_exp_mode, length);
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &smp_cmd,
+ sizeof(smp_cmd), 0);
+ if (rc)
+ goto err_out_2;
+ return 0;
+
+err_out_2:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1,
+ DMA_FROM_DEVICE);
+err_out:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1,
+ DMA_TO_DEVICE);
+ return rc;
+}
+
+static int check_enc_sas_cmd(struct sas_task *task)
+{
+ u8 cmd = task->ssp_task.cmd->cmnd[0];
+
+ if (cmd == READ_10 || cmd == WRITE_10 || cmd == WRITE_VERIFY)
+ return 1;
+ else
+ return 0;
+}
+
+static int check_enc_sat_cmd(struct sas_task *task)
+{
+ int ret = 0;
+ switch (task->ata_task.fis.command) {
+ case ATA_CMD_FPDMA_READ:
+ case ATA_CMD_READ_EXT:
+ case ATA_CMD_READ:
+ case ATA_CMD_FPDMA_WRITE:
+ case ATA_CMD_WRITE_EXT:
+ case ATA_CMD_WRITE:
+ case ATA_CMD_PIO_READ:
+ case ATA_CMD_PIO_READ_EXT:
+ case ATA_CMD_PIO_WRITE:
+ case ATA_CMD_PIO_WRITE_EXT:
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static u32 pm80xx_chip_get_q_index(struct sas_task *task)
+{
+ struct request *rq = sas_task_find_rq(task);
+
+ if (!rq)
+ return 0;
+
+ return blk_mq_unique_tag_to_hwq(blk_mq_unique_tag(rq));
+}
+
+/**
+ * pm80xx_chip_ssp_io_req - send an SSP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct ssp_ini_io_start_req ssp_cmd;
+ u32 tag = ccb->ccb_tag;
+ u64 phys_addr, end_addr;
+ u32 end_addr_high, end_addr_low;
+ u32 q_index;
+ u32 opc = OPC_INB_SSPINIIOSTART;
+
+ memset(&ssp_cmd, 0, sizeof(ssp_cmd));
+ memcpy(ssp_cmd.ssp_iu.lun, task->ssp_task.LUN, 8);
+
+ /* data address domain added for spcv; set to 0 by host,
+ * used internally by controller
+ * 0 for SAS 1.1 and SAS 2.0 compatible TLR
+ */
+ ssp_cmd.dad_dir_m_tlr =
+ cpu_to_le32(data_dir_flags[task->data_dir] << 8 | 0x0);
+ ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id);
+ ssp_cmd.tag = cpu_to_le32(tag);
+ ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7);
+ memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
+ q_index = pm80xx_chip_get_q_index(task);
+
+ /* Check if encryption is set */
+ if (pm8001_ha->chip->encrypt &&
+ !(pm8001_ha->encrypt_info.status) && check_enc_sas_cmd(task)) {
+ pm8001_dbg(pm8001_ha, IO,
+ "Encryption enabled.Sending Encrypt SAS command 0x%x\n",
+ task->ssp_task.cmd->cmnd[0]);
+ opc = OPC_INB_SSP_INI_DIF_ENC_IO;
+ /* enable encryption. 0 for SAS 1.1 and SAS 2.0 compatible TLR*/
+ ssp_cmd.dad_dir_m_tlr = cpu_to_le32
+ ((data_dir_flags[task->data_dir] << 8) | 0x20 | 0x0);
+
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter,
+ ccb->n_elem, ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ ssp_cmd.enc_addr_low =
+ cpu_to_le32(lower_32_bits(phys_addr));
+ ssp_cmd.enc_addr_high =
+ cpu_to_le32(upper_32_bits(phys_addr));
+ ssp_cmd.enc_esgl = cpu_to_le32(1<<31);
+ } else if (task->num_scatter == 1) {
+ u64 dma_addr = sg_dma_address(task->scatter);
+
+ ssp_cmd.enc_addr_low =
+ cpu_to_le32(lower_32_bits(dma_addr));
+ ssp_cmd.enc_addr_high =
+ cpu_to_le32(upper_32_bits(dma_addr));
+ ssp_cmd.enc_len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.enc_esgl = 0;
+
+ /* Check 4G Boundary */
+ end_addr = dma_addr + le32_to_cpu(ssp_cmd.enc_len) - 1;
+ end_addr_low = lower_32_bits(end_addr);
+ end_addr_high = upper_32_bits(end_addr);
+
+ if (end_addr_high != le32_to_cpu(ssp_cmd.enc_addr_high)) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "The sg list address start_addr=0x%016llx data_len=0x%x end_addr_high=0x%08x end_addr_low=0x%08x has crossed 4G boundary\n",
+ dma_addr,
+ le32_to_cpu(ssp_cmd.enc_len),
+ end_addr_high, end_addr_low);
+ pm8001_chip_make_sg(task->scatter, 1,
+ ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ ssp_cmd.enc_addr_low =
+ cpu_to_le32(lower_32_bits(phys_addr));
+ ssp_cmd.enc_addr_high =
+ cpu_to_le32(upper_32_bits(phys_addr));
+ ssp_cmd.enc_esgl = cpu_to_le32(1U<<31);
+ }
+ } else if (task->num_scatter == 0) {
+ ssp_cmd.enc_addr_low = 0;
+ ssp_cmd.enc_addr_high = 0;
+ ssp_cmd.enc_len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.enc_esgl = 0;
+ }
+
+ /* XTS mode. All other fields are 0 */
+ ssp_cmd.key_cmode = cpu_to_le32(0x6 << 4);
+
+ /* set tweak values. Should be the start lba */
+ ssp_cmd.twk_val0 = cpu_to_le32((task->ssp_task.cmd->cmnd[2] << 24) |
+ (task->ssp_task.cmd->cmnd[3] << 16) |
+ (task->ssp_task.cmd->cmnd[4] << 8) |
+ (task->ssp_task.cmd->cmnd[5]));
+ } else {
+ pm8001_dbg(pm8001_ha, IO,
+ "Sending Normal SAS command 0x%x inb q %x\n",
+ task->ssp_task.cmd->cmnd[0], q_index);
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter, ccb->n_elem,
+ ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ ssp_cmd.addr_low =
+ cpu_to_le32(lower_32_bits(phys_addr));
+ ssp_cmd.addr_high =
+ cpu_to_le32(upper_32_bits(phys_addr));
+ ssp_cmd.esgl = cpu_to_le32(1<<31);
+ } else if (task->num_scatter == 1) {
+ u64 dma_addr = sg_dma_address(task->scatter);
+
+ ssp_cmd.addr_low = cpu_to_le32(lower_32_bits(dma_addr));
+ ssp_cmd.addr_high =
+ cpu_to_le32(upper_32_bits(dma_addr));
+ ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.esgl = 0;
+
+ /* Check 4G Boundary */
+ end_addr = dma_addr + le32_to_cpu(ssp_cmd.len) - 1;
+ end_addr_low = lower_32_bits(end_addr);
+ end_addr_high = upper_32_bits(end_addr);
+ if (end_addr_high != le32_to_cpu(ssp_cmd.addr_high)) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "The sg list address start_addr=0x%016llx data_len=0x%x end_addr_high=0x%08x end_addr_low=0x%08x has crossed 4G boundary\n",
+ dma_addr,
+ le32_to_cpu(ssp_cmd.len),
+ end_addr_high, end_addr_low);
+ pm8001_chip_make_sg(task->scatter, 1,
+ ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ ssp_cmd.addr_low =
+ cpu_to_le32(lower_32_bits(phys_addr));
+ ssp_cmd.addr_high =
+ cpu_to_le32(upper_32_bits(phys_addr));
+ ssp_cmd.esgl = cpu_to_le32(1<<31);
+ }
+ } else if (task->num_scatter == 0) {
+ ssp_cmd.addr_low = 0;
+ ssp_cmd.addr_high = 0;
+ ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.esgl = 0;
+ }
+ }
+
+ return pm8001_mpi_build_cmd(pm8001_ha, q_index, opc, &ssp_cmd,
+ sizeof(ssp_cmd), q_index);
+}
+
+static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_ha_dev = dev->lldd_dev;
+ struct ata_queued_cmd *qc = task->uldd_task;
+ u32 tag = ccb->ccb_tag, q_index;
+ struct sata_start_req sata_cmd;
+ u32 hdr_tag, ncg_tag = 0;
+ u64 phys_addr, end_addr;
+ u32 end_addr_high, end_addr_low;
+ u32 ATAP = 0x0;
+ u32 dir, retfis = 0;
+ u32 opc = OPC_INB_SATA_HOST_OPSTART;
+ memset(&sata_cmd, 0, sizeof(sata_cmd));
+
+ q_index = pm80xx_chip_get_q_index(task);
+
+ if (task->data_dir == DMA_NONE && !task->ata_task.use_ncq) {
+ ATAP = 0x04; /* no data*/
+ pm8001_dbg(pm8001_ha, IO, "no data\n");
+ } else if (likely(!task->ata_task.device_control_reg_update)) {
+ if (task->ata_task.use_ncq &&
+ dev->sata_dev.class != ATA_DEV_ATAPI) {
+ ATAP = 0x07; /* FPDMA */
+ pm8001_dbg(pm8001_ha, IO, "FPDMA\n");
+ } else if (task->ata_task.dma_xfer) {
+ ATAP = 0x06; /* DMA */
+ pm8001_dbg(pm8001_ha, IO, "DMA\n");
+ } else {
+ ATAP = 0x05; /* PIO*/
+ pm8001_dbg(pm8001_ha, IO, "PIO\n");
+ }
+ }
+ if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag)) {
+ task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
+ ncg_tag = hdr_tag;
+ }
+ dir = data_dir_flags[task->data_dir] << 8;
+ sata_cmd.tag = cpu_to_le32(tag);
+ sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+ sata_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+ if (task->ata_task.return_fis_on_success)
+ retfis = 1;
+ sata_cmd.sata_fis = task->ata_task.fis;
+ if (likely(!task->ata_task.device_control_reg_update))
+ sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */
+ sata_cmd.sata_fis.flags &= 0xF0;/* PM_PORT field shall be 0 */
+
+ /* Check if encryption is set */
+ if (pm8001_ha->chip->encrypt &&
+ !(pm8001_ha->encrypt_info.status) && check_enc_sat_cmd(task)) {
+ pm8001_dbg(pm8001_ha, IO,
+ "Encryption enabled.Sending Encrypt SATA cmd 0x%x\n",
+ sata_cmd.sata_fis.command);
+ opc = OPC_INB_SATA_DIF_ENC_IO;
+ /* set encryption bit; dad (bits 0-1) is 0 */
+ sata_cmd.retfis_ncqtag_atap_dir_m_dad =
+ cpu_to_le32((retfis << 24) | ((ncg_tag & 0xff) << 16) |
+ ((ATAP & 0x3f) << 10) | 0x20 | dir);
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter,
+ ccb->n_elem, ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ sata_cmd.enc_addr_low =
+ cpu_to_le32(lower_32_bits(phys_addr));
+ sata_cmd.enc_addr_high =
+ cpu_to_le32(upper_32_bits(phys_addr));
+ sata_cmd.enc_esgl = cpu_to_le32(1 << 31);
+ } else if (task->num_scatter == 1) {
+ u64 dma_addr = sg_dma_address(task->scatter);
+
+ sata_cmd.enc_addr_low =
+ cpu_to_le32(lower_32_bits(dma_addr));
+ sata_cmd.enc_addr_high =
+ cpu_to_le32(upper_32_bits(dma_addr));
+ sata_cmd.enc_len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.enc_esgl = 0;
+
+ /* Check 4G Boundary */
+ end_addr = dma_addr + le32_to_cpu(sata_cmd.enc_len) - 1;
+ end_addr_low = lower_32_bits(end_addr);
+ end_addr_high = upper_32_bits(end_addr);
+ if (end_addr_high != le32_to_cpu(sata_cmd.enc_addr_high)) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "The sg list address start_addr=0x%016llx data_len=0x%x end_addr_high=0x%08x end_addr_low=0x%08x has crossed 4G boundary\n",
+ dma_addr,
+ le32_to_cpu(sata_cmd.enc_len),
+ end_addr_high, end_addr_low);
+ pm8001_chip_make_sg(task->scatter, 1,
+ ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ sata_cmd.enc_addr_low =
+ cpu_to_le32(lower_32_bits(phys_addr));
+ sata_cmd.enc_addr_high =
+ cpu_to_le32(upper_32_bits(phys_addr));
+ sata_cmd.enc_esgl =
+ cpu_to_le32(1 << 31);
+ }
+ } else if (task->num_scatter == 0) {
+ sata_cmd.enc_addr_low = 0;
+ sata_cmd.enc_addr_high = 0;
+ sata_cmd.enc_len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.enc_esgl = 0;
+ }
+ /* XTS mode. All other fields are 0 */
+ sata_cmd.key_index_mode = cpu_to_le32(0x6 << 4);
+
+ /* set tweak values. Should be the start lba */
+ sata_cmd.twk_val0 =
+ cpu_to_le32((sata_cmd.sata_fis.lbal_exp << 24) |
+ (sata_cmd.sata_fis.lbah << 16) |
+ (sata_cmd.sata_fis.lbam << 8) |
+ (sata_cmd.sata_fis.lbal));
+ sata_cmd.twk_val1 =
+ cpu_to_le32((sata_cmd.sata_fis.lbah_exp << 8) |
+ (sata_cmd.sata_fis.lbam_exp));
+ } else {
+ pm8001_dbg(pm8001_ha, IO,
+ "Sending Normal SATA command 0x%x inb %x\n",
+ sata_cmd.sata_fis.command, q_index);
+ /* dad (bits 0-1) is 0 */
+ sata_cmd.retfis_ncqtag_atap_dir_m_dad =
+ cpu_to_le32((retfis << 24) | ((ncg_tag & 0xff) << 16) |
+ ((ATAP & 0x3f) << 10) | dir);
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter,
+ ccb->n_elem, ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ sata_cmd.addr_low = lower_32_bits(phys_addr);
+ sata_cmd.addr_high = upper_32_bits(phys_addr);
+ sata_cmd.esgl = cpu_to_le32(1U << 31);
+ } else if (task->num_scatter == 1) {
+ u64 dma_addr = sg_dma_address(task->scatter);
+
+ sata_cmd.addr_low = lower_32_bits(dma_addr);
+ sata_cmd.addr_high = upper_32_bits(dma_addr);
+ sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.esgl = 0;
+
+ /* Check 4G Boundary */
+ end_addr = dma_addr + le32_to_cpu(sata_cmd.len) - 1;
+ end_addr_low = lower_32_bits(end_addr);
+ end_addr_high = upper_32_bits(end_addr);
+ if (end_addr_high != sata_cmd.addr_high) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "The sg list address start_addr=0x%016llx data_len=0x%xend_addr_high=0x%08x end_addr_low=0x%08x has crossed 4G boundary\n",
+ dma_addr,
+ le32_to_cpu(sata_cmd.len),
+ end_addr_high, end_addr_low);
+ pm8001_chip_make_sg(task->scatter, 1,
+ ccb->buf_prd);
+ phys_addr = ccb->ccb_dma_handle;
+ sata_cmd.addr_low = lower_32_bits(phys_addr);
+ sata_cmd.addr_high = upper_32_bits(phys_addr);
+ sata_cmd.esgl = cpu_to_le32(1U << 31);
+ }
+ } else if (task->num_scatter == 0) {
+ sata_cmd.addr_low = 0;
+ sata_cmd.addr_high = 0;
+ sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.esgl = 0;
+ }
+
+ /* scsi cdb */
+ sata_cmd.atapi_scsi_cdb[0] =
+ cpu_to_le32(((task->ata_task.atapi_packet[0]) |
+ (task->ata_task.atapi_packet[1] << 8) |
+ (task->ata_task.atapi_packet[2] << 16) |
+ (task->ata_task.atapi_packet[3] << 24)));
+ sata_cmd.atapi_scsi_cdb[1] =
+ cpu_to_le32(((task->ata_task.atapi_packet[4]) |
+ (task->ata_task.atapi_packet[5] << 8) |
+ (task->ata_task.atapi_packet[6] << 16) |
+ (task->ata_task.atapi_packet[7] << 24)));
+ sata_cmd.atapi_scsi_cdb[2] =
+ cpu_to_le32(((task->ata_task.atapi_packet[8]) |
+ (task->ata_task.atapi_packet[9] << 8) |
+ (task->ata_task.atapi_packet[10] << 16) |
+ (task->ata_task.atapi_packet[11] << 24)));
+ sata_cmd.atapi_scsi_cdb[3] =
+ cpu_to_le32(((task->ata_task.atapi_packet[12]) |
+ (task->ata_task.atapi_packet[13] << 8) |
+ (task->ata_task.atapi_packet[14] << 16) |
+ (task->ata_task.atapi_packet[15] << 24)));
+ }
+
+ trace_pm80xx_request_issue(pm8001_ha->id,
+ ccb->device ? ccb->device->attached_phy : PM8001_MAX_PHYS,
+ ccb->ccb_tag, opc,
+ qc ? qc->tf.command : 0, // ata opcode
+ ccb->device ? atomic_read(&ccb->device->running_req) : 0);
+ return pm8001_mpi_build_cmd(pm8001_ha, q_index, opc, &sata_cmd,
+ sizeof(sata_cmd), q_index);
+}
+
+/**
+ * pm80xx_chip_phy_start_req - start phy via PHY_START COMMAND
+ * @pm8001_ha: our hba card information.
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int
+pm80xx_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id)
+{
+ struct phy_start_req payload;
+ int ret;
+ u32 tag;
+ u32 opcode = OPC_INB_PHYSTART;
+
+ ret = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (ret) {
+ pm8001_dbg(pm8001_ha, FAIL, "Tag allocation failed\n");
+ return ret;
+ }
+
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = cpu_to_le32(tag);
+
+ pm8001_dbg(pm8001_ha, INIT, "PHY START REQ for phy_id %d\n", phy_id);
+
+ payload.ase_sh_lm_slr_phyid = cpu_to_le32(SPINHOLD_DISABLE |
+ LINKMODE_AUTO | pm8001_ha->link_rate | phy_id);
+ /* SSC Disable and SAS Analog ST configuration */
+ /*
+ payload.ase_sh_lm_slr_phyid =
+ cpu_to_le32(SSC_DISABLE_30 | SAS_ASE | SPINHOLD_DISABLE |
+ LINKMODE_AUTO | LINKRATE_15 | LINKRATE_30 | LINKRATE_60 |
+ phy_id);
+ Have to add "SAS PHY Analog Setup SPASTI 1 Byte" Based on need
+ */
+
+ payload.sas_identify.dev_type = SAS_END_DEVICE;
+ payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL;
+ memcpy(payload.sas_identify.sas_addr,
+ &pm8001_ha->phy[phy_id].dev_sas_addr, SAS_ADDR_SIZE);
+ payload.sas_identify.phy_id = phy_id;
+
+ ret = pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload,
+ sizeof(payload), 0);
+ if (ret < 0)
+ pm8001_tag_free(pm8001_ha, tag);
+
+ return ret;
+}
+
+/**
+ * pm80xx_chip_phy_stop_req - start phy via PHY_STOP COMMAND
+ * @pm8001_ha: our hba card information.
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int pm80xx_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
+ u8 phy_id)
+{
+ struct phy_stop_req payload;
+ int ret;
+ u32 tag;
+ u32 opcode = OPC_INB_PHYSTOP;
+
+ ret = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (ret) {
+ pm8001_dbg(pm8001_ha, FAIL, "Tag allocation failed\n");
+ return ret;
+ }
+
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = cpu_to_le32(tag);
+ payload.phy_id = cpu_to_le32(phy_id);
+
+ ret = pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload,
+ sizeof(payload), 0);
+ if (ret < 0)
+ pm8001_tag_free(pm8001_ha, tag);
+
+ return ret;
+}
+
+/*
+ * see comments on pm8001_mpi_reg_resp.
+ */
+static int pm80xx_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 flag)
+{
+ struct reg_dev_req payload;
+ u32 opc;
+ u32 stp_sspsmp_sata = 0x4;
+ u32 linkrate, phy_id;
+ int rc;
+ struct pm8001_ccb_info *ccb;
+ u8 retryFlag = 0x1;
+ u16 firstBurstSize = 0;
+ u16 ITNT = 2000;
+ struct domain_device *dev = pm8001_dev->sas_device;
+ struct pm8001_port *port = dev->port->lldd_port;
+
+ memset(&payload, 0, sizeof(payload));
+ ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_dev, NULL);
+ if (!ccb)
+ return -SAS_QUEUE_FULL;
+
+ payload.tag = cpu_to_le32(ccb->ccb_tag);
+
+ if (flag == 1) {
+ stp_sspsmp_sata = 0x02; /*direct attached sata */
+ } else {
+ if (pm8001_dev->dev_type == SAS_SATA_DEV)
+ stp_sspsmp_sata = 0x00; /* stp*/
+ else if (pm8001_dev->dev_type == SAS_END_DEVICE ||
+ dev_is_expander(pm8001_dev->dev_type))
+ stp_sspsmp_sata = 0x01; /*ssp or smp*/
+ }
+
+ phy_id = pm80xx_get_local_phy_id(dev);
+
+ opc = OPC_INB_REG_DEV;
+
+ linkrate = (pm8001_dev->sas_device->linkrate < dev->port->linkrate) ?
+ pm8001_dev->sas_device->linkrate : dev->port->linkrate;
+
+ payload.phyid_portid =
+ cpu_to_le32(((port->port_id) & 0xFF) |
+ ((phy_id & 0xFF) << 8));
+
+ payload.dtype_dlr_mcn_ir_retry = cpu_to_le32((retryFlag & 0x01) |
+ ((linkrate & 0x0F) << 24) |
+ ((stp_sspsmp_sata & 0x03) << 28));
+ payload.firstburstsize_ITNexustimeout =
+ cpu_to_le32(ITNT | (firstBurstSize * 0x10000));
+
+ memcpy(payload.sas_addr, pm8001_dev->sas_device->sas_addr,
+ SAS_ADDR_SIZE);
+
+ pm8001_dbg(pm8001_ha, INIT,
+ "register device req phy_id 0x%x port_id 0x%x\n", phy_id,
+ (port->port_id & 0xFF));
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_ccb_free(pm8001_ha, ccb);
+
+ return rc;
+}
+
+/**
+ * pm80xx_chip_phy_ctl_req - support the local phy operation
+ * @pm8001_ha: our hba card information.
+ * @phyId: the phy id which we wanted to operate
+ * @phy_op: phy operation to request
+ */
+static int pm80xx_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+ u32 phyId, u32 phy_op)
+{
+ u32 tag;
+ int rc;
+ struct local_phy_ctl_req payload;
+ u32 opc = OPC_INB_LOCAL_PHY_CONTROL;
+
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+
+ payload.tag = cpu_to_le32(tag);
+ payload.phyop_phyid =
+ cpu_to_le32(((phy_op & 0xFF) << 8) | (phyId & 0xFF));
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_tag_free(pm8001_ha, tag);
+
+ return rc;
+}
+
+static u32 pm80xx_chip_is_our_interrupt(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 value;
+
+ if (pm8001_ha->use_msix)
+ return 1;
+
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_ODR);
+ if (value)
+ return 1;
+ return 0;
+}
+
+/**
+ * pm80xx_chip_isr - PM8001 isr handler.
+ * @pm8001_ha: our hba card information.
+ * @vec: irq number.
+ */
+static irqreturn_t
+pm80xx_chip_isr(struct pm8001_hba_info *pm8001_ha, u8 vec)
+{
+ pm80xx_chip_interrupt_disable(pm8001_ha, vec);
+ pm8001_dbg(pm8001_ha, DEVIO,
+ "irq vec %d, ODMR:0x%x\n",
+ vec, pm8001_cr32(pm8001_ha, 0, 0x30));
+ process_oq(pm8001_ha, vec);
+ pm80xx_chip_interrupt_enable(pm8001_ha, vec);
+ return IRQ_HANDLED;
+}
+
+static void mpi_set_phy_profile_req(struct pm8001_hba_info *pm8001_ha,
+ u32 operation, u32 phyid,
+ u32 length, u32 *buf)
+{
+ u32 tag, i, j = 0;
+ int rc;
+ struct set_phy_profile_req payload;
+ u32 opc = OPC_INB_SET_PHY_PROFILE;
+
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc) {
+ pm8001_dbg(pm8001_ha, FAIL, "Invalid tag\n");
+ return;
+ }
+
+ payload.tag = cpu_to_le32(tag);
+ payload.ppc_phyid =
+ cpu_to_le32(((operation & 0xF) << 8) | (phyid & 0xFF));
+ pm8001_dbg(pm8001_ha, DISC,
+ " phy profile command for phy %x ,length is %d\n",
+ le32_to_cpu(payload.ppc_phyid), length);
+ for (i = length; i < (length + PHY_DWORD_LENGTH - 1); i++) {
+ payload.reserved[j] = cpu_to_le32(*((u32 *)buf + i));
+ j++;
+ }
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_tag_free(pm8001_ha, tag);
+}
+
+void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha,
+ u32 length, u8 *buf)
+{
+ u32 i;
+
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ mpi_set_phy_profile_req(pm8001_ha,
+ SAS_PHY_ANALOG_SETTINGS_PAGE, i, length, (u32 *)buf);
+ length = length + PHY_DWORD_LENGTH;
+ }
+ pm8001_dbg(pm8001_ha, INIT, "phy settings completed\n");
+}
+
+void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha,
+ u32 phy, u32 length, u32 *buf)
+{
+ u32 tag, opc;
+ int rc, i;
+ struct set_phy_profile_req payload;
+
+ memset(&payload, 0, sizeof(payload));
+
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc) {
+ pm8001_dbg(pm8001_ha, INIT, "Invalid tag\n");
+ return;
+ }
+
+ opc = OPC_INB_SET_PHY_PROFILE;
+
+ payload.tag = cpu_to_le32(tag);
+ payload.ppc_phyid =
+ cpu_to_le32(((SAS_PHY_ANALOG_SETTINGS_PAGE & 0xF) << 8)
+ | (phy & 0xFF));
+
+ for (i = 0; i < length; i++)
+ payload.reserved[i] = cpu_to_le32(*(buf + i));
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &payload,
+ sizeof(payload), 0);
+ if (rc)
+ pm8001_tag_free(pm8001_ha, tag);
+
+ pm8001_dbg(pm8001_ha, INIT, "PHY %d settings applied\n", phy);
+}
+const struct pm8001_dispatch pm8001_80xx_dispatch = {
+ .name = "pmc80xx",
+ .chip_init = pm80xx_chip_init,
+ .chip_post_init = pm80xx_chip_post_init,
+ .chip_soft_rst = pm80xx_chip_soft_rst,
+ .chip_rst = pm80xx_hw_chip_rst,
+ .chip_iounmap = pm8001_chip_iounmap,
+ .isr = pm80xx_chip_isr,
+ .is_our_interrupt = pm80xx_chip_is_our_interrupt,
+ .isr_process_oq = process_oq,
+ .interrupt_enable = pm80xx_chip_interrupt_enable,
+ .interrupt_disable = pm80xx_chip_interrupt_disable,
+ .make_prd = pm8001_chip_make_sg,
+ .smp_req = pm80xx_chip_smp_req,
+ .ssp_io_req = pm80xx_chip_ssp_io_req,
+ .sata_req = pm80xx_chip_sata_req,
+ .phy_start_req = pm80xx_chip_phy_start_req,
+ .phy_stop_req = pm80xx_chip_phy_stop_req,
+ .reg_dev_req = pm80xx_chip_reg_dev_req,
+ .dereg_dev_req = pm8001_chip_dereg_dev_req,
+ .phy_ctl_req = pm80xx_chip_phy_ctl_req,
+ .task_abort = pm8001_chip_abort_task,
+ .ssp_tm_req = pm8001_chip_ssp_tm_req,
+ .get_nvmd_req = pm8001_chip_get_nvmd_req,
+ .set_nvmd_req = pm8001_chip_set_nvmd_req,
+ .fw_flash_update_req = pm8001_chip_fw_flash_update_req,
+ .set_dev_state_req = pm8001_chip_set_dev_state_req,
+ .fatal_errors = pm80xx_fatal_errors,
+ .hw_event_ack_req = pm80xx_hw_event_ack_req,
+};
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h
new file mode 100644
index 000000000000..d8a63b7fed6a
--- /dev/null
+++ b/drivers/scsi/pm8001/pm80xx_hwi.h
@@ -0,0 +1,1667 @@
+/*
+ * PMC-Sierra SPCv/ve 8088/8089 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PMC8001_REG_H_
+#define _PMC8001_REG_H_
+
+#include <linux/types.h>
+#include <scsi/libsas.h>
+
+/* for Request Opcode of IOMB */
+#define OPC_INB_ECHO 1 /* 0x000 */
+#define OPC_INB_PHYSTART 4 /* 0x004 */
+#define OPC_INB_PHYSTOP 5 /* 0x005 */
+#define OPC_INB_SSPINIIOSTART 6 /* 0x006 */
+#define OPC_INB_SSPINITMSTART 7 /* 0x007 */
+/* 0x8 RESV IN SPCv */
+#define OPC_INB_RSVD 8 /* 0x008 */
+#define OPC_INB_DEV_HANDLE_ACCEPT 9 /* 0x009 */
+#define OPC_INB_SSPTGTIOSTART 10 /* 0x00A */
+#define OPC_INB_SSPTGTRSPSTART 11 /* 0x00B */
+/* 0xC, 0xD, 0xE removed in SPCv */
+#define OPC_INB_SSP_ABORT 15 /* 0x00F */
+#define OPC_INB_DEREG_DEV_HANDLE 16 /* 0x010 */
+#define OPC_INB_GET_DEV_HANDLE 17 /* 0x011 */
+#define OPC_INB_SMP_REQUEST 18 /* 0x012 */
+/* 0x13 SMP_RESPONSE is removed in SPCv */
+#define OPC_INB_SMP_ABORT 20 /* 0x014 */
+/* 0x16 RESV IN SPCv */
+#define OPC_INB_RSVD1 22 /* 0x016 */
+#define OPC_INB_SATA_HOST_OPSTART 23 /* 0x017 */
+#define OPC_INB_SATA_ABORT 24 /* 0x018 */
+#define OPC_INB_LOCAL_PHY_CONTROL 25 /* 0x019 */
+/* 0x1A RESV IN SPCv */
+#define OPC_INB_RSVD2 26 /* 0x01A */
+#define OPC_INB_FW_FLASH_UPDATE 32 /* 0x020 */
+#define OPC_INB_GPIO 34 /* 0x022 */
+#define OPC_INB_SAS_DIAG_MODE_START_END 35 /* 0x023 */
+#define OPC_INB_SAS_DIAG_EXECUTE 36 /* 0x024 */
+/* 0x25 RESV IN SPCv */
+#define OPC_INB_RSVD3 37 /* 0x025 */
+#define OPC_INB_GET_TIME_STAMP 38 /* 0x026 */
+#define OPC_INB_PORT_CONTROL 39 /* 0x027 */
+#define OPC_INB_GET_NVMD_DATA 40 /* 0x028 */
+#define OPC_INB_SET_NVMD_DATA 41 /* 0x029 */
+#define OPC_INB_SET_DEVICE_STATE 42 /* 0x02A */
+#define OPC_INB_GET_DEVICE_STATE 43 /* 0x02B */
+#define OPC_INB_SET_DEV_INFO 44 /* 0x02C */
+/* 0x2D RESV IN SPCv */
+#define OPC_INB_RSVD4 45 /* 0x02D */
+#define OPC_INB_SGPIO_REGISTER 46 /* 0x02E */
+#define OPC_INB_PCIE_DIAG_EXEC 47 /* 0x02F */
+#define OPC_INB_SET_CONTROLLER_CONFIG 48 /* 0x030 */
+#define OPC_INB_GET_CONTROLLER_CONFIG 49 /* 0x031 */
+#define OPC_INB_REG_DEV 50 /* 0x032 */
+#define OPC_INB_SAS_HW_EVENT_ACK 51 /* 0x033 */
+#define OPC_INB_GET_DEVICE_INFO 52 /* 0x034 */
+#define OPC_INB_GET_PHY_PROFILE 53 /* 0x035 */
+#define OPC_INB_FLASH_OP_EXT 54 /* 0x036 */
+#define OPC_INB_SET_PHY_PROFILE 55 /* 0x037 */
+#define OPC_INB_KEK_MANAGEMENT 256 /* 0x100 */
+#define OPC_INB_DEK_MANAGEMENT 257 /* 0x101 */
+#define OPC_INB_SSP_INI_DIF_ENC_IO 258 /* 0x102 */
+#define OPC_INB_SATA_DIF_ENC_IO 259 /* 0x103 */
+
+/* for Response Opcode of IOMB */
+#define OPC_OUB_ECHO 1 /* 0x001 */
+#define OPC_OUB_RSVD 4 /* 0x004 */
+#define OPC_OUB_SSP_COMP 5 /* 0x005 */
+#define OPC_OUB_SMP_COMP 6 /* 0x006 */
+#define OPC_OUB_LOCAL_PHY_CNTRL 7 /* 0x007 */
+#define OPC_OUB_RSVD1 10 /* 0x00A */
+#define OPC_OUB_DEREG_DEV 11 /* 0x00B */
+#define OPC_OUB_GET_DEV_HANDLE 12 /* 0x00C */
+#define OPC_OUB_SATA_COMP 13 /* 0x00D */
+#define OPC_OUB_SATA_EVENT 14 /* 0x00E */
+#define OPC_OUB_SSP_EVENT 15 /* 0x00F */
+#define OPC_OUB_RSVD2 16 /* 0x010 */
+/* 0x11 - SMP_RECEIVED Notification removed in SPCv*/
+#define OPC_OUB_SSP_RECV_EVENT 18 /* 0x012 */
+#define OPC_OUB_RSVD3 19 /* 0x013 */
+#define OPC_OUB_FW_FLASH_UPDATE 20 /* 0x014 */
+#define OPC_OUB_GPIO_RESPONSE 22 /* 0x016 */
+#define OPC_OUB_GPIO_EVENT 23 /* 0x017 */
+#define OPC_OUB_GENERAL_EVENT 24 /* 0x018 */
+#define OPC_OUB_SSP_ABORT_RSP 26 /* 0x01A */
+#define OPC_OUB_SATA_ABORT_RSP 27 /* 0x01B */
+#define OPC_OUB_SAS_DIAG_MODE_START_END 28 /* 0x01C */
+#define OPC_OUB_SAS_DIAG_EXECUTE 29 /* 0x01D */
+#define OPC_OUB_GET_TIME_STAMP 30 /* 0x01E */
+#define OPC_OUB_RSVD4 31 /* 0x01F */
+#define OPC_OUB_PORT_CONTROL 32 /* 0x020 */
+#define OPC_OUB_SKIP_ENTRY 33 /* 0x021 */
+#define OPC_OUB_SMP_ABORT_RSP 34 /* 0x022 */
+#define OPC_OUB_GET_NVMD_DATA 35 /* 0x023 */
+#define OPC_OUB_SET_NVMD_DATA 36 /* 0x024 */
+#define OPC_OUB_DEVICE_HANDLE_REMOVAL 37 /* 0x025 */
+#define OPC_OUB_SET_DEVICE_STATE 38 /* 0x026 */
+#define OPC_OUB_GET_DEVICE_STATE 39 /* 0x027 */
+#define OPC_OUB_SET_DEV_INFO 40 /* 0x028 */
+#define OPC_OUB_RSVD5 41 /* 0x029 */
+#define OPC_OUB_HW_EVENT 1792 /* 0x700 */
+#define OPC_OUB_DEV_HANDLE_ARRIV 1824 /* 0x720 */
+#define OPC_OUB_THERM_HW_EVENT 1840 /* 0x730 */
+#define OPC_OUB_SGPIO_RESP 2094 /* 0x82E */
+#define OPC_OUB_PCIE_DIAG_EXECUTE 2095 /* 0x82F */
+#define OPC_OUB_DEV_REGIST 2098 /* 0x832 */
+#define OPC_OUB_SAS_HW_EVENT_ACK 2099 /* 0x833 */
+#define OPC_OUB_GET_DEVICE_INFO 2100 /* 0x834 */
+/* spcv specific commands */
+#define OPC_OUB_PHY_START_RESP 2052 /* 0x804 */
+#define OPC_OUB_PHY_STOP_RESP 2053 /* 0x805 */
+#define OPC_OUB_SET_CONTROLLER_CONFIG 2096 /* 0x830 */
+#define OPC_OUB_GET_CONTROLLER_CONFIG 2097 /* 0x831 */
+#define OPC_OUB_GET_PHY_PROFILE 2101 /* 0x835 */
+#define OPC_OUB_FLASH_OP_EXT 2102 /* 0x836 */
+#define OPC_OUB_SET_PHY_PROFILE 2103 /* 0x837 */
+#define OPC_OUB_KEK_MANAGEMENT_RESP 2304 /* 0x900 */
+#define OPC_OUB_DEK_MANAGEMENT_RESP 2305 /* 0x901 */
+#define OPC_OUB_SSP_COALESCED_COMP_RESP 2306 /* 0x902 */
+
+/* for phy start*/
+#define SSC_DISABLE_15 (0x01 << 16)
+#define SSC_DISABLE_30 (0x02 << 16)
+#define SSC_DISABLE_60 (0x04 << 16)
+#define SAS_ASE (0x01 << 15)
+#define SPINHOLD_DISABLE (0x00 << 14)
+#define SPINHOLD_ENABLE (0x01 << 14)
+#define LINKMODE_SAS (0x01 << 12)
+#define LINKMODE_DSATA (0x02 << 12)
+#define LINKMODE_AUTO (0x03 << 12)
+#define LINKRATE_15 (0x01 << 8)
+#define LINKRATE_30 (0x02 << 8)
+#define LINKRATE_60 (0x04 << 8)
+#define LINKRATE_120 (0x08 << 8)
+
+/*phy_stop*/
+#define PHY_STOP_SUCCESS 0x00
+#define PHY_STOP_ERR_DEVICE_ATTACHED 0x1046
+
+/* phy_profile */
+#define SAS_PHY_ANALOG_SETTINGS_PAGE 0x04
+#define PHY_DWORD_LENGTH 0xC
+
+/* Thermal related */
+#define THERMAL_ENABLE 0x1
+#define THERMAL_LOG_ENABLE 0x1
+#define THERMAL_PAGE_CODE_7H 0x6
+#define THERMAL_PAGE_CODE_8H 0x7
+#define LTEMPHIL 70
+#define RTEMPHIL 100
+
+/* Encryption info */
+#define SCRATCH_PAD3_ENC_DISABLED 0x00000000
+#define SCRATCH_PAD3_ENC_DIS_ERR 0x00000001
+#define SCRATCH_PAD3_ENC_ENA_ERR 0x00000002
+#define SCRATCH_PAD3_ENC_READY 0x00000003
+#define SCRATCH_PAD3_ENC_MASK SCRATCH_PAD3_ENC_READY
+
+#define SCRATCH_PAD3_XTS_ENABLED (1 << 14)
+#define SCRATCH_PAD3_SMA_ENABLED (1 << 4)
+#define SCRATCH_PAD3_SMB_ENABLED (1 << 5)
+#define SCRATCH_PAD3_SMF_ENABLED 0
+#define SCRATCH_PAD3_SM_MASK 0x000000F0
+#define SCRATCH_PAD3_ERR_CODE 0x00FF0000
+
+#define SEC_MODE_SMF 0x0
+#define SEC_MODE_SMA 0x100
+#define SEC_MODE_SMB 0x200
+#define CIPHER_MODE_ECB 0x00000001
+#define CIPHER_MODE_XTS 0x00000002
+#define KEK_MGMT_SUBOP_KEYCARDUPDATE 0x4
+
+/* SAS protocol timer configuration page */
+#define SAS_PROTOCOL_TIMER_CONFIG_PAGE 0x04
+#define STP_MCT_TMO 32
+#define SSP_MCT_TMO 32
+#define SAS_MAX_OPEN_TIME 5
+#define SMP_MAX_CONN_TIMER 0xFF
+#define STP_FRM_TIMER 0
+#define STP_IDLE_TIME 5 /* 5 us; controller default */
+#define SAS_MFD 0
+#define SAS_OPNRJT_RTRY_INTVL 2
+#define SAS_DOPNRJT_RTRY_TMO 128
+#define SAS_COPNRJT_RTRY_TMO 128
+
+#define SPCV_DOORBELL_CLEAR_TIMEOUT (30 * 50) /* 30 sec */
+#define SPC_DOORBELL_CLEAR_TIMEOUT (15 * 50) /* 15 sec */
+
+/*
+ Making ORR bigger than IT NEXUS LOSS which is 2000000us = 2 second.
+ Assuming a bigger value 3 second, 3000000/128 = 23437.5 where 128
+ is DOPNRJT_RTRY_TMO
+*/
+#define SAS_DOPNRJT_RTRY_THR 23438
+#define SAS_COPNRJT_RTRY_THR 23438
+#define SAS_MAX_AIP 0x200000
+#define IT_NEXUS_TIMEOUT 0x7D0
+#define PORT_RECOVERY_TIMEOUT ((IT_NEXUS_TIMEOUT/100) + 30)
+/* Port recovery timeout, 10000 ms for PM8006 controller */
+#define CHIP_8006_PORT_RECOVERY_TIMEOUT 0x640000
+
+#ifdef __LITTLE_ENDIAN_BITFIELD
+struct sas_identify_frame_local {
+ /* Byte 0 */
+ u8 frame_type:4;
+ u8 dev_type:3;
+ u8 _un0:1;
+
+ /* Byte 1 */
+ u8 _un1;
+
+ /* Byte 2 */
+ union {
+ struct {
+ u8 _un20:1;
+ u8 smp_iport:1;
+ u8 stp_iport:1;
+ u8 ssp_iport:1;
+ u8 _un247:4;
+ };
+ u8 initiator_bits;
+ };
+
+ /* Byte 3 */
+ union {
+ struct {
+ u8 _un30:1;
+ u8 smp_tport:1;
+ u8 stp_tport:1;
+ u8 ssp_tport:1;
+ u8 _un347:4;
+ };
+ u8 target_bits;
+ };
+
+ /* Byte 4 - 11 */
+ u8 _un4_11[8];
+
+ /* Byte 12 - 19 */
+ u8 sas_addr[SAS_ADDR_SIZE];
+
+ /* Byte 20 */
+ u8 phy_id;
+
+ u8 _un21_27[7];
+
+} __packed;
+
+#elif defined(__BIG_ENDIAN_BITFIELD)
+struct sas_identify_frame_local {
+ /* Byte 0 */
+ u8 _un0:1;
+ u8 dev_type:3;
+ u8 frame_type:4;
+
+ /* Byte 1 */
+ u8 _un1;
+
+ /* Byte 2 */
+ union {
+ struct {
+ u8 _un247:4;
+ u8 ssp_iport:1;
+ u8 stp_iport:1;
+ u8 smp_iport:1;
+ u8 _un20:1;
+ };
+ u8 initiator_bits;
+ };
+
+ /* Byte 3 */
+ union {
+ struct {
+ u8 _un347:4;
+ u8 ssp_tport:1;
+ u8 stp_tport:1;
+ u8 smp_tport:1;
+ u8 _un30:1;
+ };
+ u8 target_bits;
+ };
+
+ /* Byte 4 - 11 */
+ u8 _un4_11[8];
+
+ /* Byte 12 - 19 */
+ u8 sas_addr[SAS_ADDR_SIZE];
+
+ /* Byte 20 */
+ u8 phy_id;
+
+ u8 _un21_27[7];
+} __packed;
+#else
+#error "Bitfield order not defined!"
+#endif
+
+struct mpi_msg_hdr {
+ __le32 header; /* Bits [11:0] - Message operation code */
+ /* Bits [15:12] - Message Category */
+ /* Bits [21:16] - Outboundqueue ID for the
+ operation completion message */
+ /* Bits [23:22] - Reserved */
+ /* Bits [28:24] - Buffer Count, indicates how
+ many buffer are allocated for the massage */
+ /* Bits [30:29] - Reserved */
+ /* Bits [31] - Message Valid bit */
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to describe enable the phy (128 bytes)
+ */
+struct phy_start_req {
+ __le32 tag;
+ __le32 ase_sh_lm_slr_phyid;
+ struct sas_identify_frame_local sas_identify; /* 28 Bytes */
+ __le32 spasti;
+ u32 reserved[21];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to disable the phy (128 bytes)
+ */
+struct phy_stop_req {
+ __le32 tag;
+ __le32 phy_id;
+ u32 reserved[29];
+} __attribute__((packed, aligned(4)));
+
+/* set device bits fis - device to host */
+struct set_dev_bits_fis {
+ u8 fis_type; /* 0xA1*/
+ u8 n_i_pmport;
+ /* b7 : n Bit. Notification bit. If set device needs attention. */
+ /* b6 : i Bit. Interrupt Bit */
+ /* b5-b4: reserved2 */
+ /* b3-b0: PM Port */
+ u8 status;
+ u8 error;
+ u32 _r_a;
+} __attribute__ ((packed));
+/* PIO setup FIS - device to host */
+struct pio_setup_fis {
+ u8 fis_type; /* 0x5f */
+ u8 i_d_pmPort;
+ /* b7 : reserved */
+ /* b6 : i bit. Interrupt bit */
+ /* b5 : d bit. data transfer direction. set to 1 for device to host
+ xfer */
+ /* b4 : reserved */
+ /* b3-b0: PM Port */
+ u8 status;
+ u8 error;
+ u8 lbal;
+ u8 lbam;
+ u8 lbah;
+ u8 device;
+ u8 lbal_exp;
+ u8 lbam_exp;
+ u8 lbah_exp;
+ u8 _r_a;
+ u8 sector_count;
+ u8 sector_count_exp;
+ u8 _r_b;
+ u8 e_status;
+ u8 _r_c[2];
+ u8 transfer_count;
+} __attribute__ ((packed));
+
+/*
+ * brief the data structure of SATA Completion Response
+ * use to describe the sata task response (64 bytes)
+ */
+struct sata_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ u32 sata_resp[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SAS HW Event Notification
+ * use to alert the host about the hardware event(64 bytes)
+ */
+/* updated outbound struct for spcv */
+
+struct hw_event_resp {
+ __le32 lr_status_evt_portid;
+ __le32 evt_param;
+ __le32 phyid_npip_portstate;
+ struct sas_identify_frame sas_identify;
+ struct dev_to_host_fis sata_fis;
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure for thermal event notification
+ */
+
+struct thermal_hw_event {
+ __le32 thermal_event;
+ __le32 rht_lht;
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of REGISTER DEVICE Command
+ * use to describe MPI REGISTER DEVICE Command (64 bytes)
+ */
+
+struct reg_dev_req {
+ __le32 tag;
+ __le32 phyid_portid;
+ __le32 dtype_dlr_mcn_ir_retry;
+ __le32 firstburstsize_ITNexustimeout;
+ u8 sas_addr[SAS_ADDR_SIZE];
+ __le32 upper_device_id;
+ u32 reserved[24];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of DEREGISTER DEVICE Command
+ * use to request spc to remove all internal resources associated
+ * with the device id (64 bytes)
+ */
+
+struct dereg_dev_req {
+ __le32 tag;
+ __le32 device_id;
+ u32 reserved[29];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of DEVICE_REGISTRATION Response
+ * use to notify the completion of the device registration (64 bytes)
+ */
+struct dev_reg_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 device_id;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of Local PHY Control Command
+ * use to issue PHY CONTROL to local phy (64 bytes)
+ */
+struct local_phy_ctl_req {
+ __le32 tag;
+ __le32 phyop_phyid;
+ u32 reserved1[29];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of Local Phy Control Response
+ * use to describe MPI Local Phy Control Response (64 bytes)
+ */
+ struct local_phy_ctl_resp {
+ __le32 tag;
+ __le32 phyop_phyid;
+ __le32 status;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+#define OP_BITS 0x0000FF00
+#define ID_BITS 0x000000FF
+
+/*
+ * brief the data structure of PORT Control Command
+ * use to control port properties (64 bytes)
+ */
+
+struct port_ctl_req {
+ __le32 tag;
+ __le32 portop_portid;
+ __le32 param0;
+ __le32 param1;
+ u32 reserved1[27];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of HW Event Ack Command
+ * use to acknowledge receive HW event (64 bytes)
+ */
+struct hw_event_ack_req {
+ __le32 tag;
+ __le32 phyid_sea_portid;
+ __le32 param0;
+ __le32 param1;
+ u32 reserved1[27];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of PHY_START Response Command
+ * indicates the completion of PHY_START command (64 bytes)
+ */
+struct phy_start_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 phyid;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of PHY_STOP Response Command
+ * indicates the completion of PHY_STOP command (64 bytes)
+ */
+struct phy_stop_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 phyid;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SSP Completion Response
+ * use to indicate a SSP Completion (n bytes)
+ */
+struct ssp_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ __le32 ssptag_rescv_rescpad;
+
+ /* Must be last --ends in a flexible-array member. */
+ struct ssp_response_iu ssp_resp_iu;
+ /* __le32 residual_count; */
+} __attribute__((packed, aligned(4)));
+
+#define SSP_RESCV_BIT 0x00010000
+
+/*
+ * brief the data structure of SATA EVNET response
+ * use to indicate a SATA Completion (64 bytes)
+ */
+struct sata_event_resp {
+ __le32 tag;
+ __le32 event;
+ __le32 port_id;
+ __le32 device_id;
+ u32 reserved;
+ __le32 event_param0;
+ __le32 event_param1;
+ __le32 sata_addr_h32;
+ __le32 sata_addr_l32;
+ __le32 e_udt1_udt0_crc;
+ __le32 e_udt5_udt4_udt3_udt2;
+ __le32 a_udt1_udt0_crc;
+ __le32 a_udt5_udt4_udt3_udt2;
+ __le32 hwdevid_diferr;
+ __le32 err_framelen_byteoffset;
+ __le32 err_dataframe;
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SSP EVNET esponse
+ * use to indicate a SSP Completion (64 bytes)
+ */
+struct ssp_event_resp {
+ __le32 tag;
+ __le32 event;
+ __le32 port_id;
+ __le32 device_id;
+ __le32 ssp_tag;
+ __le32 event_param0;
+ __le32 event_param1;
+ __le32 sas_addr_h32;
+ __le32 sas_addr_l32;
+ __le32 e_udt1_udt0_crc;
+ __le32 e_udt5_udt4_udt3_udt2;
+ __le32 a_udt1_udt0_crc;
+ __le32 a_udt5_udt4_udt3_udt2;
+ __le32 hwdevid_diferr;
+ __le32 err_framelen_byteoffset;
+ __le32 err_dataframe;
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of General Event Notification Response
+ * use to describe MPI General Event Notification Response (64 bytes)
+ */
+struct general_event_resp {
+ __le32 status;
+ __le32 inb_IOMB_payload[14];
+} __attribute__((packed, aligned(4)));
+
+#define GENERAL_EVENT_PAYLOAD 14
+#define OPCODE_BITS 0x00000fff
+
+/*
+ * brief the data structure of SMP Request Command
+ * use to describe MPI SMP REQUEST Command (64 bytes)
+ */
+struct smp_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 len_ip_ir;
+ /* Bits [0] - Indirect response */
+ /* Bits [1] - Indirect Payload */
+ /* Bits [15:2] - Reserved */
+ /* Bits [23:16] - direct payload Len */
+ /* Bits [31:24] - Reserved */
+ u8 smp_req16[16];
+ union {
+ u8 smp_req[32];
+ struct {
+ __le64 long_req_addr;/* sg dma address, LE */
+ __le32 long_req_size;/* LE */
+ u32 _r_a;
+ __le64 long_resp_addr;/* sg dma address, LE */
+ __le32 long_resp_size;/* LE */
+ u32 _r_b;
+ } long_smp_req;/* sequencer extension */
+ };
+ __le32 rsvd[16];
+} __attribute__((packed, aligned(4)));
+/*
+ * brief the data structure of SMP Completion Response
+ * use to describe MPI SMP Completion Response (64 bytes)
+ */
+struct smp_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ u8 _r_a[252];
+} __attribute__((packed, aligned(4)));
+
+/*
+ *brief the data structure of SSP SMP SATA Abort Command
+ * use to describe MPI SSP SMP & SATA Abort Command (64 bytes)
+ */
+struct task_abort_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 tag_to_abort;
+ __le32 abort_all;
+ u32 reserved[27];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SSP SATA SMP Abort Response
+ * use to describe SSP SMP & SATA Abort Response ( 64 bytes)
+ */
+struct task_abort_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 scp;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Command
+ * use to describe MPI SAS Diagnostic Start/End Command (64 bytes)
+ */
+struct sas_diag_start_end_req {
+ __le32 tag;
+ __le32 operation_phyid;
+ u32 reserved[29];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Command
+ * use to describe MPI SAS Diagnostic Execute Command (64 bytes)
+ */
+struct sas_diag_execute_req {
+ __le32 tag;
+ __le32 cmdtype_cmddesc_phyid;
+ __le32 pat1_pat2;
+ __le32 threshold;
+ __le32 codepat_errmsk;
+ __le32 pmon;
+ __le32 pERF1CTL;
+ u32 reserved[24];
+} __attribute__((packed, aligned(4)));
+
+#define SAS_DIAG_PARAM_BYTES 24
+
+/*
+ * brief the data structure of Set Device State Command
+ * use to describe MPI Set Device State Command (64 bytes)
+ */
+struct set_dev_state_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 nds;
+ u32 reserved[28];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SATA Start Command
+ * use to describe MPI SATA IO Start Command (64 bytes)
+ * Note: This structure is common for normal / encryption I/O
+ */
+
+struct sata_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 data_len;
+ __le32 retfis_ncqtag_atap_dir_m_dad;
+ struct host_to_dev_fis sata_fis;
+ u32 reserved1;
+ u32 reserved2; /* dword 11. rsvd for normal I/O. */
+ /* EPLE Descl for enc I/O */
+ u32 addr_low; /* dword 12. rsvd for enc I/O */
+ u32 addr_high; /* dword 13. reserved for enc I/O */
+ __le32 len; /* dword 14: length for normal I/O. */
+ /* EPLE Desch for enc I/O */
+ __le32 esgl; /* dword 15. rsvd for enc I/O */
+ __le32 atapi_scsi_cdb[4]; /* dword 16-19. rsvd for enc I/O */
+ /* The below fields are reserved for normal I/O */
+ __le32 key_index_mode; /* dword 20 */
+ __le32 sector_cnt_enss;/* dword 21 */
+ __le32 keytagl; /* dword 22 */
+ __le32 keytagh; /* dword 23 */
+ __le32 twk_val0; /* dword 24 */
+ __le32 twk_val1; /* dword 25 */
+ __le32 twk_val2; /* dword 26 */
+ __le32 twk_val3; /* dword 27 */
+ __le32 enc_addr_low; /* dword 28. Encryption SGL address high */
+ __le32 enc_addr_high; /* dword 29. Encryption SGL address low */
+ __le32 enc_len; /* dword 30. Encryption length */
+ __le32 enc_esgl; /* dword 31. Encryption esgl bit */
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SSP INI TM Start Command
+ * use to describe MPI SSP INI TM Start Command (64 bytes)
+ */
+struct ssp_ini_tm_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 relate_tag;
+ __le32 tmf;
+ u8 lun[8];
+ __le32 ds_ads_m;
+ u32 reserved[24];
+} __attribute__((packed, aligned(4)));
+
+struct ssp_info_unit {
+ u8 lun[8];/* SCSI Logical Unit Number */
+ u8 reserved1;/* reserved */
+ u8 efb_prio_attr;
+ /* B7 : enabledFirstBurst */
+ /* B6-3 : taskPriority */
+ /* B2-0 : taskAttribute */
+ u8 reserved2; /* reserved */
+ u8 additional_cdb_len;
+ /* B7-2 : additional_cdb_len */
+ /* B1-0 : reserved */
+ u8 cdb[16];/* The SCSI CDB up to 16 bytes length */
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SSP INI IO Start Command
+ * use to describe MPI SSP INI IO Start Command (64 bytes)
+ * Note: This structure is common for normal / encryption I/O
+ */
+struct ssp_ini_io_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 data_len;
+ __le32 dad_dir_m_tlr;
+ struct ssp_info_unit ssp_iu;
+ __le32 addr_low; /* dword 12: sgl low for normal I/O. */
+ /* epl_descl for encryption I/O */
+ __le32 addr_high; /* dword 13: sgl hi for normal I/O */
+ /* dpl_descl for encryption I/O */
+ __le32 len; /* dword 14: len for normal I/O. */
+ /* edpl_desch for encryption I/O */
+ __le32 esgl; /* dword 15: ESGL bit for normal I/O. */
+ /* user defined tag mask for enc I/O */
+ /* The below fields are reserved for normal I/O */
+ u8 udt[12]; /* dword 16-18 */
+ __le32 sectcnt_ios; /* dword 19 */
+ __le32 key_cmode; /* dword 20 */
+ __le32 ks_enss; /* dword 21 */
+ __le32 keytagl; /* dword 22 */
+ __le32 keytagh; /* dword 23 */
+ __le32 twk_val0; /* dword 24 */
+ __le32 twk_val1; /* dword 25 */
+ __le32 twk_val2; /* dword 26 */
+ __le32 twk_val3; /* dword 27 */
+ __le32 enc_addr_low; /* dword 28: Encryption sgl addr low */
+ __le32 enc_addr_high; /* dword 29: Encryption sgl addr hi */
+ __le32 enc_len; /* dword 30: Encryption length */
+ __le32 enc_esgl; /* dword 31: ESGL bit for encryption */
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for SSP_INI_DIF_ENC_IO COMMAND
+ * use to initiate SSP I/O operation with optional DIF/ENC
+ */
+struct ssp_dif_enc_io_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 data_len;
+ __le32 dirMTlr;
+ __le32 sspiu0;
+ __le32 sspiu1;
+ __le32 sspiu2;
+ __le32 sspiu3;
+ __le32 sspiu4;
+ __le32 sspiu5;
+ __le32 sspiu6;
+ __le32 epl_des;
+ __le32 dpl_desl_ndplr;
+ __le32 dpl_desh;
+ __le32 uum_uuv_bss_difbits;
+ u8 udt[12];
+ __le32 sectcnt_ios;
+ __le32 key_cmode;
+ __le32 ks_enss;
+ __le32 keytagl;
+ __le32 keytagh;
+ __le32 twk_val0;
+ __le32 twk_val1;
+ __le32 twk_val2;
+ __le32 twk_val3;
+ __le32 addr_low;
+ __le32 addr_high;
+ __le32 len;
+ __le32 esgl;
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of Firmware download
+ * use to describe MPI FW DOWNLOAD Command (64 bytes)
+ */
+struct fw_flash_Update_req {
+ __le32 tag;
+ __le32 cur_image_offset;
+ __le32 cur_image_len;
+ __le32 total_image_len;
+ u32 reserved0[7];
+ __le32 sgl_addr_lo;
+ __le32 sgl_addr_hi;
+ __le32 len;
+ __le32 ext_reserved;
+ u32 reserved1[16];
+} __attribute__((packed, aligned(4)));
+
+#define FWFLASH_IOMB_RESERVED_LEN 0x07
+/**
+ * brief the data structure of FW_FLASH_UPDATE Response
+ * use to describe MPI FW_FLASH_UPDATE Response (64 bytes)
+ *
+ */
+ struct fw_flash_Update_resp {
+ __le32 tag;
+ __le32 status;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of Get NVM Data Command
+ * use to get data from NVM in HBA(64 bytes)
+ */
+struct get_nvm_data_req {
+ __le32 tag;
+ __le32 len_ir_vpdd;
+ __le32 vpd_offset;
+ u32 reserved[8];
+ __le32 resp_addr_lo;
+ __le32 resp_addr_hi;
+ __le32 resp_len;
+ u32 reserved1[17];
+} __attribute__((packed, aligned(4)));
+
+struct set_nvm_data_req {
+ __le32 tag;
+ __le32 len_ir_vpdd;
+ __le32 vpd_offset;
+ u32 reserved[8];
+ __le32 resp_addr_lo;
+ __le32 resp_addr_hi;
+ __le32 resp_len;
+ u32 reserved1[17];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for SET CONTROLLER CONFIG COMMAND
+ * use to modify controller configuration
+ */
+struct set_ctrl_cfg_req {
+ __le32 tag;
+ __le32 cfg_pg[14];
+ u32 reserved[16];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for GET CONTROLLER CONFIG COMMAND
+ * use to get controller configuration page
+ */
+struct get_ctrl_cfg_req {
+ __le32 tag;
+ __le32 pgcd;
+ __le32 int_vec;
+ u32 reserved[28];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for KEK_MANAGEMENT COMMAND
+ * use for KEK management
+ */
+struct kek_mgmt_req {
+ __le32 tag;
+ __le32 new_curidx_ksop;
+ u32 reserved;
+ __le32 kblob[12];
+ u32 reserved1[16];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for DEK_MANAGEMENT COMMAND
+ * use for DEK management
+ */
+struct dek_mgmt_req {
+ __le32 tag;
+ __le32 kidx_dsop;
+ __le32 dekidx;
+ __le32 addr_l;
+ __le32 addr_h;
+ __le32 nent;
+ __le32 dbf_tblsize;
+ u32 reserved[24];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for SET PHY PROFILE COMMAND
+ * use to retrive phy specific information
+ */
+struct set_phy_profile_req {
+ __le32 tag;
+ __le32 ppc_phyid;
+ __le32 reserved[29];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for GET PHY PROFILE COMMAND
+ * use to retrive phy specific information
+ */
+struct get_phy_profile_req {
+ __le32 tag;
+ __le32 ppc_phyid;
+ __le32 profile[29];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure for EXT FLASH PARTITION
+ * use to manage ext flash partition
+ */
+struct ext_flash_partition_req {
+ __le32 tag;
+ __le32 cmd;
+ __le32 offset;
+ __le32 len;
+ u32 reserved[7];
+ __le32 addr_low;
+ __le32 addr_high;
+ __le32 len1;
+ __le32 ext;
+ u32 reserved1[16];
+} __attribute__((packed, aligned(4)));
+
+#define TWI_DEVICE 0x0
+#define C_SEEPROM 0x1
+#define VPD_FLASH 0x4
+#define AAP1_RDUMP 0x5
+#define IOP_RDUMP 0x6
+#define EXPAN_ROM 0x7
+
+#define IPMode 0x80000000
+#define NVMD_TYPE 0x0000000F
+#define NVMD_STAT 0x0000FFFF
+#define NVMD_LEN 0xFF000000
+/**
+ * brief the data structure of Get NVMD Data Response
+ * use to describe MPI Get NVMD Data Response (64 bytes)
+ */
+struct get_nvm_data_resp {
+ __le32 tag;
+ __le32 ir_tda_bn_dps_das_nvm;
+ __le32 dlen_status;
+ __le32 nvm_data[12];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Response
+ * use to describe MPI SAS Diagnostic Start/End Response (64 bytes)
+ *
+ */
+struct sas_diag_start_end_resp {
+ __le32 tag;
+ __le32 status;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Response
+ * use to describe MPI SAS Diagnostic Execute Response (64 bytes)
+ *
+ */
+struct sas_diag_execute_resp {
+ __le32 tag;
+ __le32 cmdtype_cmddesc_phyid;
+ __le32 Status;
+ __le32 ReportData;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of Set Device State Response
+ * use to describe MPI Set Device State Response (64 bytes)
+ *
+ */
+struct set_dev_state_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 device_id;
+ __le32 pds_nds;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/* new outbound structure for spcv - begins */
+/**
+ * brief the data structure for SET CONTROLLER CONFIG COMMAND
+ * use to modify controller configuration
+ */
+struct set_ctrl_cfg_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 err_qlfr_pgcd;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+struct get_ctrl_cfg_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 err_qlfr;
+ __le32 confg_page[12];
+} __attribute__((packed, aligned(4)));
+
+struct kek_mgmt_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 kidx_new_curr_ksop;
+ __le32 err_qlfr;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+struct dek_mgmt_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 kekidx_tbls_dsop;
+ __le32 dekidx;
+ __le32 err_qlfr;
+ u32 reserved[10];
+} __attribute__((packed, aligned(4)));
+
+struct get_phy_profile_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 ppc_phyid;
+ __le32 ppc_specific_rsp[12];
+} __attribute__((packed, aligned(4)));
+
+struct flash_op_ext_resp {
+ __le32 tag;
+ __le32 cmd;
+ __le32 status;
+ __le32 epart_size;
+ __le32 epart_sect_size;
+ u32 reserved[10];
+} __attribute__((packed, aligned(4)));
+
+struct set_phy_profile_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 ppc_phyid;
+ __le32 ppc_specific_rsp[12];
+} __attribute__((packed, aligned(4)));
+
+struct ssp_coalesced_comp_resp {
+ __le32 coal_cnt;
+ __le32 tag0;
+ __le32 ssp_tag0;
+ __le32 tag1;
+ __le32 ssp_tag1;
+ __le32 add_tag_ssp_tag[10];
+} __attribute__((packed, aligned(4)));
+
+/* new outbound structure for spcv - ends */
+
+/* brief data structure for SAS protocol timer configuration page.
+ *
+ */
+struct SASProtocolTimerConfig {
+ __le32 pageCode; /* 0 */
+ __le32 MST_MSI; /* 1 */
+ __le32 STP_SSP_MCT_TMO; /* 2 */
+ __le32 STP_FRM_TMO; /* 3 */
+ __le32 STP_IDLE_TMO; /* 4 */
+ __le32 OPNRJT_RTRY_INTVL; /* 5 */
+ __le32 Data_Cmd_OPNRJT_RTRY_TMO; /* 6 */
+ __le32 Data_Cmd_OPNRJT_RTRY_THR; /* 7 */
+ __le32 MAX_AIP; /* 8 */
+} __attribute__((packed, aligned(4)));
+
+typedef struct SASProtocolTimerConfig SASProtocolTimerConfig_t;
+
+#define NDS_BITS 0x0F
+#define PDS_BITS 0xF0
+
+/*
+ * HW Events type
+ */
+
+#define HW_EVENT_RESET_START 0x01
+#define HW_EVENT_CHIP_RESET_COMPLETE 0x02
+#define HW_EVENT_PHY_STOP_STATUS 0x03
+#define HW_EVENT_SAS_PHY_UP 0x04
+#define HW_EVENT_SATA_PHY_UP 0x05
+#define HW_EVENT_SATA_SPINUP_HOLD 0x06
+#define HW_EVENT_PHY_DOWN 0x07
+#define HW_EVENT_PORT_INVALID 0x08
+#define HW_EVENT_BROADCAST_CHANGE 0x09
+#define HW_EVENT_PHY_ERROR 0x0A
+#define HW_EVENT_BROADCAST_SES 0x0B
+#define HW_EVENT_INBOUND_CRC_ERROR 0x0C
+#define HW_EVENT_HARD_RESET_RECEIVED 0x0D
+#define HW_EVENT_MALFUNCTION 0x0E
+#define HW_EVENT_ID_FRAME_TIMEOUT 0x0F
+#define HW_EVENT_BROADCAST_EXP 0x10
+#define HW_EVENT_PHY_START_STATUS 0x11
+#define HW_EVENT_LINK_ERR_INVALID_DWORD 0x12
+#define HW_EVENT_LINK_ERR_DISPARITY_ERROR 0x13
+#define HW_EVENT_LINK_ERR_CODE_VIOLATION 0x14
+#define HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH 0x15
+#define HW_EVENT_LINK_ERR_PHY_RESET_FAILED 0x16
+#define HW_EVENT_PORT_RECOVERY_TIMER_TMO 0x17
+#define HW_EVENT_PORT_RECOVER 0x18
+#define HW_EVENT_PORT_RESET_TIMER_TMO 0x19
+#define HW_EVENT_PORT_RESET_COMPLETE 0x20
+#define EVENT_BROADCAST_ASYNCH_EVENT 0x21
+
+/* port state */
+#define PORT_NOT_ESTABLISHED 0x00
+#define PORT_VALID 0x01
+#define PORT_LOSTCOMM 0x02
+#define PORT_IN_RESET 0x04
+#define PORT_3RD_PARTY_RESET 0x07
+#define PORT_INVALID 0x08
+
+/*
+ * SSP/SMP/SATA IO Completion Status values
+ */
+
+#define IO_SUCCESS 0x00
+#define IO_ABORTED 0x01
+#define IO_OVERFLOW 0x02
+#define IO_UNDERFLOW 0x03
+#define IO_FAILED 0x04
+#define IO_ABORT_RESET 0x05
+#define IO_NOT_VALID 0x06
+#define IO_NO_DEVICE 0x07
+#define IO_ILLEGAL_PARAMETER 0x08
+#define IO_LINK_FAILURE 0x09
+#define IO_PROG_ERROR 0x0A
+
+#define IO_EDC_IN_ERROR 0x0B
+#define IO_EDC_OUT_ERROR 0x0C
+#define IO_ERROR_HW_TIMEOUT 0x0D
+#define IO_XFER_ERROR_BREAK 0x0E
+#define IO_XFER_ERROR_PHY_NOT_READY 0x0F
+#define IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED 0x10
+#define IO_OPEN_CNX_ERROR_ZONE_VIOLATION 0x11
+#define IO_OPEN_CNX_ERROR_BREAK 0x12
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS 0x13
+#define IO_OPEN_CNX_ERROR_BAD_DESTINATION 0x14
+#define IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED 0x15
+#define IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY 0x16
+#define IO_OPEN_CNX_ERROR_WRONG_DESTINATION 0x17
+/* This error code 0x18 is not used on SPCv */
+#define IO_OPEN_CNX_ERROR_UNKNOWN_ERROR 0x18
+#define IO_XFER_ERROR_NAK_RECEIVED 0x19
+#define IO_XFER_ERROR_ACK_NAK_TIMEOUT 0x1A
+#define IO_XFER_ERROR_PEER_ABORTED 0x1B
+#define IO_XFER_ERROR_RX_FRAME 0x1C
+#define IO_XFER_ERROR_DMA 0x1D
+#define IO_XFER_ERROR_CREDIT_TIMEOUT 0x1E
+#define IO_XFER_ERROR_SATA_LINK_TIMEOUT 0x1F
+#define IO_XFER_ERROR_SATA 0x20
+
+/* This error code 0x22 is not used on SPCv */
+#define IO_XFER_ERROR_ABORTED_DUE_TO_SRST 0x22
+#define IO_XFER_ERROR_REJECTED_NCQ_MODE 0x21
+#define IO_XFER_ERROR_ABORTED_NCQ_MODE 0x23
+#define IO_XFER_OPEN_RETRY_TIMEOUT 0x24
+/* This error code 0x25 is not used on SPCv */
+#define IO_XFER_SMP_RESP_CONNECTION_ERROR 0x25
+#define IO_XFER_ERROR_UNEXPECTED_PHASE 0x26
+#define IO_XFER_ERROR_XFER_RDY_OVERRUN 0x27
+#define IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED 0x28
+#define IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT 0x30
+
+/* The following error code 0x31 and 0x32 are not using (obsolete) */
+#define IO_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NAK 0x31
+#define IO_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK 0x32
+
+#define IO_XFER_ERROR_OFFSET_MISMATCH 0x34
+#define IO_XFER_ERROR_XFER_ZERO_DATA_LEN 0x35
+#define IO_XFER_CMD_FRAME_ISSUED 0x36
+#define IO_ERROR_INTERNAL_SMP_RESOURCE 0x37
+#define IO_PORT_IN_RESET 0x38
+#define IO_DS_NON_OPERATIONAL 0x39
+#define IO_DS_IN_RECOVERY 0x3A
+#define IO_TM_TAG_NOT_FOUND 0x3B
+#define IO_XFER_PIO_SETUP_ERROR 0x3C
+#define IO_SSP_EXT_IU_ZERO_LEN_ERROR 0x3D
+#define IO_DS_IN_ERROR 0x3E
+#define IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY 0x3F
+#define IO_ABORT_IN_PROGRESS 0x40
+#define IO_ABORT_DELAYED 0x41
+#define IO_INVALID_LENGTH 0x42
+
+/********** additional response event values *****************/
+
+#define IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY_ALT 0x43
+#define IO_XFER_OPEN_RETRY_BACKOFF_THRESHOLD_REACHED 0x44
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_TMO 0x45
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_NO_DEST 0x46
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_OPEN_COLLIDE 0x47
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS_PATHWAY_BLOCKED 0x48
+#define IO_DS_INVALID 0x49
+#define IO_FATAL_ERROR 0x51
+/* WARNING: the value is not contiguous from here */
+#define IO_XFER_ERR_LAST_PIO_DATAIN_CRC_ERR 0x52
+#define IO_XFER_DMA_ACTIVATE_TIMEOUT 0x53
+#define IO_XFER_ERROR_INTERNAL_CRC_ERROR 0x54
+#define MPI_IO_RQE_BUSY_FULL 0x55
+#define IO_XFER_ERR_EOB_DATA_OVERRUN 0x56
+#define IO_XFER_ERROR_INVALID_SSP_RSP_FRAME 0x57
+#define IO_OPEN_CNX_ERROR_OPEN_PREEMPTED 0x58
+
+#define MPI_ERR_IO_RESOURCE_UNAVAILABLE 0x1004
+#define MPI_ERR_ATAPI_DEVICE_BUSY 0x1024
+
+#define IO_XFR_ERROR_DEK_KEY_CACHE_MISS 0x2040
+/*
+ * An encryption IO request failed due to DEK Key Tag mismatch.
+ * The key tag supplied in the encryption IOMB does not match with
+ * the Key Tag in the referenced DEK Entry.
+ */
+#define IO_XFR_ERROR_DEK_KEY_TAG_MISMATCH 0x2041
+#define IO_XFR_ERROR_CIPHER_MODE_INVALID 0x2042
+/*
+ * An encryption I/O request failed because the initial value (IV)
+ * in the unwrapped DEK blob didn't match the IV used to unwrap it.
+ */
+#define IO_XFR_ERROR_DEK_IV_MISMATCH 0x2043
+/* An encryption I/O request failed due to an internal RAM ECC or
+ * interface error while unwrapping the DEK. */
+#define IO_XFR_ERROR_DEK_RAM_INTERFACE_ERROR 0x2044
+/* An encryption I/O request failed due to an internal RAM ECC or
+ * interface error while unwrapping the DEK. */
+#define IO_XFR_ERROR_INTERNAL_RAM 0x2045
+/*
+ * An encryption I/O request failed
+ * because the DEK index specified in the I/O was outside the bounds of
+ * the total number of entries in the host DEK table.
+ */
+#define IO_XFR_ERROR_DEK_INDEX_OUT_OF_BOUNDS0x2046
+
+/* define DIF IO response error status code */
+#define IO_XFR_ERROR_DIF_MISMATCH 0x3000
+#define IO_XFR_ERROR_DIF_APPLICATION_TAG_MISMATCH 0x3001
+#define IO_XFR_ERROR_DIF_REFERENCE_TAG_MISMATCH 0x3002
+#define IO_XFR_ERROR_DIF_CRC_MISMATCH 0x3003
+
+/* define operator management response status and error qualifier code */
+#define OPR_MGMT_OP_NOT_SUPPORTED 0x2060
+#define OPR_MGMT_MPI_ENC_ERR_OPR_PARAM_ILLEGAL 0x2061
+#define OPR_MGMT_MPI_ENC_ERR_OPR_ID_NOT_FOUND 0x2062
+#define OPR_MGMT_MPI_ENC_ERR_OPR_ROLE_NOT_MATCH 0x2063
+#define OPR_MGMT_MPI_ENC_ERR_OPR_MAX_NUM_EXCEEDED 0x2064
+#define OPR_MGMT_MPI_ENC_ERR_KEK_UNWRAP_FAIL 0x2022
+#define OPR_MGMT_MPI_ENC_ERR_NVRAM_OPERATION_FAILURE 0x2023
+/***************** additional response event values ***************/
+
+/* WARNING: This error code must always be the last number.
+ * If you add error code, modify this code also
+ * It is used as an index
+ */
+#define IO_ERROR_UNKNOWN_GENERIC 0x2023
+
+/* MSGU CONFIGURATION TABLE*/
+
+#define SPCv_MSGU_CFG_TABLE_UPDATE 0x001
+#define SPCv_MSGU_CFG_TABLE_RESET 0x002
+#define SPCv_MSGU_CFG_TABLE_FREEZE 0x004
+#define SPCv_MSGU_CFG_TABLE_UNFREEZE 0x008
+#define MSGU_IBDB_SET 0x00
+#define MSGU_HOST_INT_STATUS 0x08
+#define MSGU_HOST_INT_MASK 0x0C
+#define MSGU_IOPIB_INT_STATUS 0x18
+#define MSGU_IOPIB_INT_MASK 0x1C
+#define MSGU_IBDB_CLEAR 0x20
+
+#define MSGU_MSGU_CONTROL 0x24
+#define MSGU_ODR 0x20
+#define MSGU_ODCR 0x28
+
+#define MSGU_ODMR 0x30
+#define MSGU_ODMR_U 0x34
+#define MSGU_ODMR_CLR 0x38
+#define MSGU_ODMR_CLR_U 0x3C
+#define MSGU_OD_RSVD 0x40
+
+#define MSGU_SCRATCH_PAD_0 0x44
+#define MSGU_SCRATCH_PAD_1 0x48
+#define MSGU_SCRATCH_PAD_2 0x4C
+#define MSGU_SCRATCH_PAD_3 0x50
+#define MSGU_HOST_SCRATCH_PAD_0 0x54
+#define MSGU_HOST_SCRATCH_PAD_1 0x58
+#define MSGU_HOST_SCRATCH_PAD_2 0x5C
+#define MSGU_HOST_SCRATCH_PAD_3 0x60
+#define MSGU_HOST_SCRATCH_PAD_4 0x64
+#define MSGU_HOST_SCRATCH_PAD_5 0x68
+#define MSGU_SCRATCH_PAD_RSVD_0 0x6C
+#define MSGU_SCRATCH_PAD_RSVD_1 0x70
+
+#define MSGU_SCRATCHPAD1_RAAE_STATE_ERR(x) ((x & 0x3) == 0x2)
+#define MSGU_SCRATCHPAD1_ILA_STATE_ERR(x) (((x >> 2) & 0x3) == 0x2)
+#define MSGU_SCRATCHPAD1_BOOTLDR_STATE_ERR(x) ((((x >> 4) & 0x7) == 0x7) || \
+ (((x >> 4) & 0x7) == 0x4))
+#define MSGU_SCRATCHPAD1_IOP0_STATE_ERR(x) (((x >> 10) & 0x3) == 0x2)
+#define MSGU_SCRATCHPAD1_IOP1_STATE_ERR(x) (((x >> 12) & 0x3) == 0x2)
+#define MSGU_SCRATCHPAD1_STATE_FATAL_ERROR(x) \
+ (MSGU_SCRATCHPAD1_RAAE_STATE_ERR(x) || \
+ MSGU_SCRATCHPAD1_ILA_STATE_ERR(x) || \
+ MSGU_SCRATCHPAD1_BOOTLDR_STATE_ERR(x) || \
+ MSGU_SCRATCHPAD1_IOP0_STATE_ERR(x) || \
+ MSGU_SCRATCHPAD1_IOP1_STATE_ERR(x))
+
+/* bit definition for ODMR register */
+#define ODMR_MASK_ALL 0xFFFFFFFF/* mask all
+ interrupt vector */
+#define ODMR_CLEAR_ALL 0 /* clear all
+ interrupt vector */
+/* bit definition for ODCR register */
+#define ODCR_CLEAR_ALL 0xFFFFFFFF /* mask all
+ interrupt vector*/
+/* MSIX Interupts */
+#define MSIX_TABLE_OFFSET 0x2000
+#define MSIX_TABLE_ELEMENT_SIZE 0x10
+#define MSIX_INTERRUPT_CONTROL_OFFSET 0xC
+#define MSIX_TABLE_BASE (MSIX_TABLE_OFFSET + \
+ MSIX_INTERRUPT_CONTROL_OFFSET)
+#define MSIX_INTERRUPT_DISABLE 0x1
+#define MSIX_INTERRUPT_ENABLE 0x0
+
+/* state definition for Scratch Pad1 register */
+#define SCRATCH_PAD_RAAE_READY 0x3
+#define SCRATCH_PAD_ILA_READY 0xC
+#define SCRATCH_PAD_BOOT_LOAD_SUCCESS 0x0
+#define SCRATCH_PAD_IOP0_READY 0xC00
+#define SCRATCH_PAD_IOP1_READY 0x3000
+#define SCRATCH_PAD_MIPSALL_READY_16PORT (SCRATCH_PAD_IOP1_READY | \
+ SCRATCH_PAD_IOP0_READY | \
+ SCRATCH_PAD_ILA_READY | \
+ SCRATCH_PAD_RAAE_READY)
+#define SCRATCH_PAD_MIPSALL_READY_8PORT (SCRATCH_PAD_IOP0_READY | \
+ SCRATCH_PAD_ILA_READY | \
+ SCRATCH_PAD_RAAE_READY)
+
+/* boot loader state */
+#define SCRATCH_PAD1_BOOTSTATE_MASK 0x70 /* Bit 4-6 */
+#define SCRATCH_PAD1_BOOTSTATE_SUCESS 0x0 /* Load successful */
+#define SCRATCH_PAD1_BOOTSTATE_HDA_SEEPROM 0x10 /* HDA SEEPROM */
+#define SCRATCH_PAD1_BOOTSTATE_HDA_BOOTSTRAP 0x20 /* HDA BootStrap Pins */
+#define SCRATCH_PAD1_BOOTSTATE_HDA_SOFTRESET 0x30 /* HDA Soft Reset */
+#define SCRATCH_PAD1_BOOTSTATE_CRIT_ERROR 0x40 /* HDA critical error */
+#define SCRATCH_PAD1_BOOTSTATE_R1 0x50 /* Reserved */
+#define SCRATCH_PAD1_BOOTSTATE_R2 0x60 /* Reserved */
+#define SCRATCH_PAD1_BOOTSTATE_FATAL 0x70 /* Fatal Error */
+
+ /* state definition for Scratch Pad2 register */
+#define SCRATCH_PAD2_POR 0x00 /* power on state */
+#define SCRATCH_PAD2_SFR 0x01 /* soft reset state */
+#define SCRATCH_PAD2_ERR 0x02 /* error state */
+#define SCRATCH_PAD2_RDY 0x03 /* ready state */
+#define SCRATCH_PAD2_FWRDY_RST 0x04 /* FW rdy for soft reset flag */
+#define SCRATCH_PAD2_IOPRDY_RST 0x08 /* IOP ready for soft reset */
+#define SCRATCH_PAD2_STATE_MASK 0xFFFFFFF4 /* ScratchPad 2
+ Mask, bit1-0 State */
+#define SCRATCH_PAD2_RESERVED 0x000003FC/* Scratch Pad1
+ Reserved bit 2 to 9 */
+
+#define SCRATCH_PAD_ERROR_MASK 0xFFFFFC00 /* Error mask bits */
+#define SCRATCH_PAD_STATE_MASK 0x00000003 /* State Mask bits */
+
+/*state definition for Scratchpad Rsvd 0, Offset 0x6C, Non-fatal*/
+#define NON_FATAL_SPBC_LBUS_ECC_ERR 0x70000001
+#define NON_FATAL_BDMA_ERR 0xE0000001
+#define NON_FATAL_THERM_OVERTEMP_ERR 0x80000001
+
+/* main configuration offset - byte offset */
+#define MAIN_SIGNATURE_OFFSET 0x00 /* DWORD 0x00 */
+#define MAIN_INTERFACE_REVISION 0x04 /* DWORD 0x01 */
+#define MAIN_FW_REVISION 0x08 /* DWORD 0x02 */
+#define MAIN_MAX_OUTSTANDING_IO_OFFSET 0x0C /* DWORD 0x03 */
+#define MAIN_MAX_SGL_OFFSET 0x10 /* DWORD 0x04 */
+#define MAIN_CNTRL_CAP_OFFSET 0x14 /* DWORD 0x05 */
+#define MAIN_GST_OFFSET 0x18 /* DWORD 0x06 */
+#define MAIN_IBQ_OFFSET 0x1C /* DWORD 0x07 */
+#define MAIN_OBQ_OFFSET 0x20 /* DWORD 0x08 */
+#define MAIN_IQNPPD_HPPD_OFFSET 0x24 /* DWORD 0x09 */
+
+/* 0x28 - 0x4C - RSVD */
+#define MAIN_EVENT_CRC_CHECK 0x48 /* DWORD 0x12 */
+#define MAIN_EVENT_LOG_ADDR_HI 0x50 /* DWORD 0x14 */
+#define MAIN_EVENT_LOG_ADDR_LO 0x54 /* DWORD 0x15 */
+#define MAIN_EVENT_LOG_BUFF_SIZE 0x58 /* DWORD 0x16 */
+#define MAIN_EVENT_LOG_OPTION 0x5C /* DWORD 0x17 */
+#define MAIN_PCS_EVENT_LOG_ADDR_HI 0x60 /* DWORD 0x18 */
+#define MAIN_PCS_EVENT_LOG_ADDR_LO 0x64 /* DWORD 0x19 */
+#define MAIN_PCS_EVENT_LOG_BUFF_SIZE 0x68 /* DWORD 0x1A */
+#define MAIN_PCS_EVENT_LOG_OPTION 0x6C /* DWORD 0x1B */
+#define MAIN_FATAL_ERROR_INTERRUPT 0x70 /* DWORD 0x1C */
+#define MAIN_FATAL_ERROR_RDUMP0_OFFSET 0x74 /* DWORD 0x1D */
+#define MAIN_FATAL_ERROR_RDUMP0_LENGTH 0x78 /* DWORD 0x1E */
+#define MAIN_FATAL_ERROR_RDUMP1_OFFSET 0x7C /* DWORD 0x1F */
+#define MAIN_FATAL_ERROR_RDUMP1_LENGTH 0x80 /* DWORD 0x20 */
+#define MAIN_GPIO_LED_FLAGS_OFFSET 0x84 /* DWORD 0x21 */
+#define MAIN_ANALOG_SETUP_OFFSET 0x88 /* DWORD 0x22 */
+
+#define MAIN_INT_VECTOR_TABLE_OFFSET 0x8C /* DWORD 0x23 */
+#define MAIN_SAS_PHY_ATTR_TABLE_OFFSET 0x90 /* DWORD 0x24 */
+#define MAIN_PORT_RECOVERY_TIMER 0x94 /* DWORD 0x25 */
+#define MAIN_INT_REASSERTION_DELAY 0x98 /* DWORD 0x26 */
+#define MAIN_MPI_ILA_RELEASE_TYPE 0xA4 /* DWORD 0x29 */
+#define MAIN_MPI_INACTIVE_FW_VERSION 0XB0 /* DWORD 0x2C */
+
+/* Gereral Status Table offset - byte offset */
+#define GST_GSTLEN_MPIS_OFFSET 0x00
+#define GST_IQ_FREEZE_STATE0_OFFSET 0x04
+#define GST_IQ_FREEZE_STATE1_OFFSET 0x08
+#define GST_MSGUTCNT_OFFSET 0x0C
+#define GST_IOPTCNT_OFFSET 0x10
+/* 0x14 - 0x34 - RSVD */
+#define GST_GPIO_INPUT_VAL 0x38
+/* 0x3c - 0x40 - RSVD */
+#define GST_RERRINFO_OFFSET0 0x44
+#define GST_RERRINFO_OFFSET1 0x48
+#define GST_RERRINFO_OFFSET2 0x4c
+#define GST_RERRINFO_OFFSET3 0x50
+#define GST_RERRINFO_OFFSET4 0x54
+#define GST_RERRINFO_OFFSET5 0x58
+#define GST_RERRINFO_OFFSET6 0x5c
+#define GST_RERRINFO_OFFSET7 0x60
+
+/* General Status Table - MPI state */
+#define GST_MPI_STATE_UNINIT 0x00
+#define GST_MPI_STATE_INIT 0x01
+#define GST_MPI_STATE_TERMINATION 0x02
+#define GST_MPI_STATE_ERROR 0x03
+#define GST_MPI_STATE_MASK 0x07
+
+/* Per SAS PHY Attributes */
+
+#define PSPA_PHYSTATE0_OFFSET 0x00 /* Dword V */
+#define PSPA_OB_HW_EVENT_PID0_OFFSET 0x04 /* DWORD V+1 */
+#define PSPA_PHYSTATE1_OFFSET 0x08 /* Dword V+2 */
+#define PSPA_OB_HW_EVENT_PID1_OFFSET 0x0C /* DWORD V+3 */
+#define PSPA_PHYSTATE2_OFFSET 0x10 /* Dword V+4 */
+#define PSPA_OB_HW_EVENT_PID2_OFFSET 0x14 /* DWORD V+5 */
+#define PSPA_PHYSTATE3_OFFSET 0x18 /* Dword V+6 */
+#define PSPA_OB_HW_EVENT_PID3_OFFSET 0x1C /* DWORD V+7 */
+#define PSPA_PHYSTATE4_OFFSET 0x20 /* Dword V+8 */
+#define PSPA_OB_HW_EVENT_PID4_OFFSET 0x24 /* DWORD V+9 */
+#define PSPA_PHYSTATE5_OFFSET 0x28 /* Dword V+10 */
+#define PSPA_OB_HW_EVENT_PID5_OFFSET 0x2C /* DWORD V+11 */
+#define PSPA_PHYSTATE6_OFFSET 0x30 /* Dword V+12 */
+#define PSPA_OB_HW_EVENT_PID6_OFFSET 0x34 /* DWORD V+13 */
+#define PSPA_PHYSTATE7_OFFSET 0x38 /* Dword V+14 */
+#define PSPA_OB_HW_EVENT_PID7_OFFSET 0x3C /* DWORD V+15 */
+#define PSPA_PHYSTATE8_OFFSET 0x40 /* DWORD V+16 */
+#define PSPA_OB_HW_EVENT_PID8_OFFSET 0x44 /* DWORD V+17 */
+#define PSPA_PHYSTATE9_OFFSET 0x48 /* DWORD V+18 */
+#define PSPA_OB_HW_EVENT_PID9_OFFSET 0x4C /* DWORD V+19 */
+#define PSPA_PHYSTATE10_OFFSET 0x50 /* DWORD V+20 */
+#define PSPA_OB_HW_EVENT_PID10_OFFSET 0x54 /* DWORD V+21 */
+#define PSPA_PHYSTATE11_OFFSET 0x58 /* DWORD V+22 */
+#define PSPA_OB_HW_EVENT_PID11_OFFSET 0x5C /* DWORD V+23 */
+#define PSPA_PHYSTATE12_OFFSET 0x60 /* DWORD V+24 */
+#define PSPA_OB_HW_EVENT_PID12_OFFSET 0x64 /* DWORD V+25 */
+#define PSPA_PHYSTATE13_OFFSET 0x68 /* DWORD V+26 */
+#define PSPA_OB_HW_EVENT_PID13_OFFSET 0x6c /* DWORD V+27 */
+#define PSPA_PHYSTATE14_OFFSET 0x70 /* DWORD V+28 */
+#define PSPA_OB_HW_EVENT_PID14_OFFSET 0x74 /* DWORD V+29 */
+#define PSPA_PHYSTATE15_OFFSET 0x78 /* DWORD V+30 */
+#define PSPA_OB_HW_EVENT_PID15_OFFSET 0x7c /* DWORD V+31 */
+/* end PSPA */
+
+/* inbound queue configuration offset - byte offset */
+#define IB_PROPERITY_OFFSET 0x00
+#define IB_BASE_ADDR_HI_OFFSET 0x04
+#define IB_BASE_ADDR_LO_OFFSET 0x08
+#define IB_CI_BASE_ADDR_HI_OFFSET 0x0C
+#define IB_CI_BASE_ADDR_LO_OFFSET 0x10
+#define IB_PIPCI_BAR 0x14
+#define IB_PIPCI_BAR_OFFSET 0x18
+#define IB_RESERVED_OFFSET 0x1C
+
+/* outbound queue configuration offset - byte offset */
+#define OB_PROPERITY_OFFSET 0x00
+#define OB_BASE_ADDR_HI_OFFSET 0x04
+#define OB_BASE_ADDR_LO_OFFSET 0x08
+#define OB_PI_BASE_ADDR_HI_OFFSET 0x0C
+#define OB_PI_BASE_ADDR_LO_OFFSET 0x10
+#define OB_CIPCI_BAR 0x14
+#define OB_CIPCI_BAR_OFFSET 0x18
+#define OB_INTERRUPT_COALES_OFFSET 0x1C
+#define OB_DYNAMIC_COALES_OFFSET 0x20
+#define OB_PROPERTY_INT_ENABLE 0x40000000
+
+#define MBIC_NMI_ENABLE_VPE0_IOP 0x000418
+#define MBIC_NMI_ENABLE_VPE0_AAP1 0x000418
+/* PCIE registers - BAR2(0x18), BAR1(win) 0x010000 */
+#define PCIE_EVENT_INTERRUPT_ENABLE 0x003040
+#define PCIE_EVENT_INTERRUPT 0x003044
+#define PCIE_ERROR_INTERRUPT_ENABLE 0x003048
+#define PCIE_ERROR_INTERRUPT 0x00304C
+
+/* SPCV soft reset */
+#define SPC_REG_SOFT_RESET 0x00001000
+#define SPCv_NORMAL_RESET_VALUE 0x1
+
+#define SPCv_SOFT_RESET_READ_MASK 0xC0
+#define SPCv_SOFT_RESET_NO_RESET 0x0
+#define SPCv_SOFT_RESET_NORMAL_RESET_OCCURED 0x40
+#define SPCv_SOFT_RESET_HDA_MODE_OCCURED 0x80
+#define SPCv_SOFT_RESET_CHIP_RESET_OCCURED 0xC0
+
+/* signature definition for host scratch pad0 register */
+#define SPC_SOFT_RESET_SIGNATURE 0x252acbcd
+/* Signature for Soft Reset */
+
+/* SPC Reset register - BAR4(0x20), BAR2(win) (need dynamic mapping) */
+#define SPC_REG_RESET 0x000000/* reset register */
+
+/* bit definition for SPC_RESET register */
+#define SPC_REG_RESET_OSSP 0x00000001
+#define SPC_REG_RESET_RAAE 0x00000002
+#define SPC_REG_RESET_PCS_SPBC 0x00000004
+#define SPC_REG_RESET_PCS_IOP_SS 0x00000008
+#define SPC_REG_RESET_PCS_AAP1_SS 0x00000010
+#define SPC_REG_RESET_PCS_AAP2_SS 0x00000020
+#define SPC_REG_RESET_PCS_LM 0x00000040
+#define SPC_REG_RESET_PCS 0x00000080
+#define SPC_REG_RESET_GSM 0x00000100
+#define SPC_REG_RESET_DDR2 0x00010000
+#define SPC_REG_RESET_BDMA_CORE 0x00020000
+#define SPC_REG_RESET_BDMA_SXCBI 0x00040000
+#define SPC_REG_RESET_PCIE_AL_SXCBI 0x00080000
+#define SPC_REG_RESET_PCIE_PWR 0x00100000
+#define SPC_REG_RESET_PCIE_SFT 0x00200000
+#define SPC_REG_RESET_PCS_SXCBI 0x00400000
+#define SPC_REG_RESET_LMS_SXCBI 0x00800000
+#define SPC_REG_RESET_PMIC_SXCBI 0x01000000
+#define SPC_REG_RESET_PMIC_CORE 0x02000000
+#define SPC_REG_RESET_PCIE_PC_SXCBI 0x04000000
+#define SPC_REG_RESET_DEVICE 0x80000000
+
+/* registers for BAR Shifting - BAR2(0x18), BAR1(win) */
+#define SPCV_IBW_AXI_TRANSLATION_LOW 0x001010
+
+#define MBIC_AAP1_ADDR_BASE 0x060000
+#define MBIC_IOP_ADDR_BASE 0x070000
+#define GSM_ADDR_BASE 0x0700000
+/* Dynamic map through Bar4 - 0x00700000 */
+#define GSM_CONFIG_RESET 0x00000000
+#define RAM_ECC_DB_ERR 0x00000018
+#define GSM_READ_ADDR_PARITY_INDIC 0x00000058
+#define GSM_WRITE_ADDR_PARITY_INDIC 0x00000060
+#define GSM_WRITE_DATA_PARITY_INDIC 0x00000068
+#define GSM_READ_ADDR_PARITY_CHECK 0x00000038
+#define GSM_WRITE_ADDR_PARITY_CHECK 0x00000040
+#define GSM_WRITE_DATA_PARITY_CHECK 0x00000048
+
+#define RB6_ACCESS_REG 0x6A0000
+#define HDAC_EXEC_CMD 0x0002
+#define HDA_C_PA 0xcb
+#define HDA_SEQ_ID_BITS 0x00ff0000
+#define HDA_GSM_OFFSET_BITS 0x00FFFFFF
+#define HDA_GSM_CMD_OFFSET_BITS 0x42C0
+#define HDA_GSM_RSP_OFFSET_BITS 0x42E0
+
+#define MBIC_AAP1_ADDR_BASE 0x060000
+#define MBIC_IOP_ADDR_BASE 0x070000
+#define GSM_ADDR_BASE 0x0700000
+#define SPC_TOP_LEVEL_ADDR_BASE 0x000000
+#define GSM_CONFIG_RESET_VALUE 0x00003b00
+#define GPIO_ADDR_BASE 0x00090000
+#define GPIO_GPIO_0_0UTPUT_CTL_OFFSET 0x0000010c
+
+/* RB6 offset */
+#define SPC_RB6_OFFSET 0x80C0
+/* Magic number of soft reset for RB6 */
+#define RB6_MAGIC_NUMBER_RST 0x1234
+
+/* Device Register status */
+#define DEVREG_SUCCESS 0x00
+#define DEVREG_FAILURE_OUT_OF_RESOURCE 0x01
+#define DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED 0x02
+#define DEVREG_FAILURE_INVALID_PHY_ID 0x03
+#define DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED 0x04
+#define DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE 0x05
+#define DEVREG_FAILURE_PORT_NOT_VALID_STATE 0x06
+#define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID 0x07
+
+
+#define MEMBASE_II_SHIFT_REGISTER 0x1010
+#endif
+
+/**
+ * As we know sleep (1~20) ms may result in sleep longer than ~20 ms, hence we
+ * choose 20 ms interval.
+ */
+#define FW_READY_INTERVAL 20
diff --git a/drivers/scsi/pm8001/pm80xx_tracepoints.c b/drivers/scsi/pm8001/pm80xx_tracepoints.c
new file mode 100644
index 000000000000..344aface9cdb
--- /dev/null
+++ b/drivers/scsi/pm8001/pm80xx_tracepoints.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Trace events in pm8001 driver.
+ *
+ * Copyright 2020 Google LLC
+ * Author: Akshat Jain <akshatzen@google.com>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "pm80xx_tracepoints.h"
diff --git a/drivers/scsi/pm8001/pm80xx_tracepoints.h b/drivers/scsi/pm8001/pm80xx_tracepoints.h
new file mode 100644
index 000000000000..5e669a8a9344
--- /dev/null
+++ b/drivers/scsi/pm8001/pm80xx_tracepoints.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Trace events in pm8001 driver.
+ *
+ * Copyright 2020 Google LLC
+ * Author: Akshat Jain <akshatzen@google.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM pm80xx
+
+#if !defined(_TRACE_PM80XX_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_PM80XX_H
+
+#include <linux/tracepoint.h>
+#include "pm8001_sas.h"
+
+TRACE_EVENT(pm80xx_request_issue,
+ TP_PROTO(u32 id, u32 phy_id, u32 htag, u32 ctlr_opcode,
+ u16 ata_opcode, int running_req),
+
+ TP_ARGS(id, phy_id, htag, ctlr_opcode, ata_opcode, running_req),
+
+ TP_STRUCT__entry(
+ __field(u32, id)
+ __field(u32, phy_id)
+ __field(u32, htag)
+ __field(u32, ctlr_opcode)
+ __field(u16, ata_opcode)
+ __field(int, running_req)
+ ),
+
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->phy_id = phy_id;
+ __entry->htag = htag;
+ __entry->ctlr_opcode = ctlr_opcode;
+ __entry->ata_opcode = ata_opcode;
+ __entry->running_req = running_req;
+ ),
+
+ TP_printk("ctlr_id = %u phy_id = %u htag = %#x, ctlr_opcode = %#x ata_opcode = %#x running_req = %d",
+ __entry->id, __entry->phy_id, __entry->htag,
+ __entry->ctlr_opcode, __entry->ata_opcode,
+ __entry->running_req)
+);
+
+TRACE_EVENT(pm80xx_request_complete,
+ TP_PROTO(u32 id, u32 phy_id, u32 htag, u32 ctlr_opcode,
+ u16 ata_opcode, int running_req),
+
+ TP_ARGS(id, phy_id, htag, ctlr_opcode, ata_opcode, running_req),
+
+ TP_STRUCT__entry(
+ __field(u32, id)
+ __field(u32, phy_id)
+ __field(u32, htag)
+ __field(u32, ctlr_opcode)
+ __field(u16, ata_opcode)
+ __field(int, running_req)
+ ),
+
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->phy_id = phy_id;
+ __entry->htag = htag;
+ __entry->ctlr_opcode = ctlr_opcode;
+ __entry->ata_opcode = ata_opcode;
+ __entry->running_req = running_req;
+ ),
+
+ TP_printk("ctlr_id = %u phy_id = %u htag = %#x, ctlr_opcode = %#x ata_opcode = %#x running_req = %d",
+ __entry->id, __entry->phy_id, __entry->htag,
+ __entry->ctlr_opcode, __entry->ata_opcode,
+ __entry->running_req)
+);
+
+TRACE_EVENT(pm80xx_mpi_build_cmd,
+ TP_PROTO(u32 id, u32 opc, u32 htag, u32 qi, u32 pi, u32 ci),
+
+ TP_ARGS(id, opc, htag, qi, pi, ci),
+
+ TP_STRUCT__entry(
+ __field(u32, id)
+ __field(u32, opc)
+ __field(u32, htag)
+ __field(u32, qi)
+ __field(u32, pi)
+ __field(u32, ci)
+ ),
+
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->opc = opc;
+ __entry->htag = htag;
+ __entry->qi = qi;
+ __entry->pi = pi;
+ __entry->ci = ci;
+ ),
+
+ TP_printk("ctlr_id = %u opc = %#x htag = %#x QI = %u PI = %u CI = %u",
+ __entry->id, __entry->opc, __entry->htag, __entry->qi,
+ __entry->pi, __entry->ci)
+);
+
+#endif /* _TRACE_PM80XX_H_ */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE pm80xx_tracepoints
+
+#include <trace/define_trace.h>
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index fca6a8953070..33f403e307eb 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters
*
@@ -5,22 +6,6 @@
* PMC-Sierra Inc
*
* Copyright (C) 2008, 2009 PMC Sierra Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
- * USA
- *
*/
#include <linux/fs.h>
#include <linux/init.h>
@@ -39,13 +24,13 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/hdreg.h>
-#include <linux/version.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <asm/irq.h>
#include <asm/processor.h>
#include <linux/libata.h>
#include <linux/mutex.h>
+#include <linux/ktime.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
@@ -76,8 +61,10 @@ static atomic_t pmcraid_adapter_count = ATOMIC_INIT(0);
* pmcraid_minor - minor number(s) to use
*/
static unsigned int pmcraid_major;
-static struct class *pmcraid_class;
-DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
+static const struct class pmcraid_class = {
+ .name = PMCRAID_DEVFILE,
+};
+static DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
/*
* Module parameters
@@ -126,7 +113,7 @@ static struct pmcraid_chip_details pmcraid_chip_cfg[] = {
/*
* PCI device ids supported by pmcraid driver
*/
-static struct pci_device_id pmcraid_pci_table[] __devinitdata = {
+static const struct pci_device_id pmcraid_pci_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_PMC, PCI_DEVICE_ID_PMC_MAXRAID),
0, 0, (kernel_ulong_t)&pmcraid_chip_cfg[0]
},
@@ -138,7 +125,7 @@ MODULE_DEVICE_TABLE(pci, pmcraid_pci_table);
/**
- * pmcraid_slave_alloc - Prepare for commands to a device
+ * pmcraid_sdev_init - Prepare for commands to a device
* @scsi_dev: scsi device struct
*
* This function is called by mid-layer prior to sending any command to the new
@@ -149,7 +136,7 @@ MODULE_DEVICE_TABLE(pci, pmcraid_pci_table);
* Return value:
* 0 on success / -ENXIO if device does not exist
*/
-static int pmcraid_slave_alloc(struct scsi_device *scsi_dev)
+static int pmcraid_sdev_init(struct scsi_device *scsi_dev)
{
struct pmcraid_resource_entry *temp, *res = NULL;
struct pmcraid_instance *pinstance;
@@ -175,7 +162,7 @@ static int pmcraid_slave_alloc(struct scsi_device *scsi_dev)
if (fw_version <= PMCRAID_FW_VERSION_1)
target = temp->cfg_entry.unique_flags1;
else
- target = temp->cfg_entry.array_id & 0xFF;
+ target = le16_to_cpu(temp->cfg_entry.array_id) & 0xFF;
if (target > PMCRAID_MAX_VSET_TARGETS)
continue;
@@ -210,8 +197,9 @@ static int pmcraid_slave_alloc(struct scsi_device *scsi_dev)
}
/**
- * pmcraid_slave_configure - Configures a SCSI device
+ * pmcraid_sdev_configure - Configures a SCSI device
* @scsi_dev: scsi device struct
+ * @lim: queue limits
*
* This function is executed by SCSI mid layer just after a device is first
* scanned (i.e. it has responded to an INQUIRY). For VSET resources, the
@@ -222,7 +210,8 @@ static int pmcraid_slave_alloc(struct scsi_device *scsi_dev)
* Return value:
* 0 on success
*/
-static int pmcraid_slave_configure(struct scsi_device *scsi_dev)
+static int pmcraid_sdev_configure(struct scsi_device *scsi_dev,
+ struct queue_limits *lim)
{
struct pmcraid_resource_entry *res = scsi_dev->hostdata;
@@ -238,7 +227,7 @@ static int pmcraid_slave_configure(struct scsi_device *scsi_dev)
scsi_dev->host->unique_id,
scsi_dev->channel,
scsi_dev->id,
- scsi_dev->lun);
+ (u8)scsi_dev->lun);
if (RES_IS_GSCSI(res->cfg_entry)) {
scsi_dev->allow_restart = 1;
@@ -246,35 +235,30 @@ static int pmcraid_slave_configure(struct scsi_device *scsi_dev)
scsi_dev->allow_restart = 1;
blk_queue_rq_timeout(scsi_dev->request_queue,
PMCRAID_VSET_IO_TIMEOUT);
- blk_queue_max_hw_sectors(scsi_dev->request_queue,
- PMCRAID_VSET_MAX_SECTORS);
+ lim->max_hw_sectors = PMCRAID_VSET_MAX_SECTORS;
}
- if (scsi_dev->tagged_supported &&
- (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) {
- scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
- scsi_adjust_queue_depth(scsi_dev, MSG_SIMPLE_TAG,
- scsi_dev->host->cmd_per_lun);
- } else {
- scsi_adjust_queue_depth(scsi_dev, 0,
- scsi_dev->host->cmd_per_lun);
- }
+ /*
+ * We never want to report TCQ support for these types of devices.
+ */
+ if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry))
+ scsi_dev->tagged_supported = 0;
return 0;
}
/**
- * pmcraid_slave_destroy - Unconfigure a SCSI device before removing it
+ * pmcraid_sdev_destroy - Unconfigure a SCSI device before removing it
*
* @scsi_dev: scsi device struct
*
* This is called by mid-layer before removing a device. Pointer assignments
- * done in pmcraid_slave_alloc will be reset to NULL here.
+ * done in pmcraid_sdev_init will be reset to NULL here.
*
* Return value
* none
*/
-static void pmcraid_slave_destroy(struct scsi_device *scsi_dev)
+static void pmcraid_sdev_destroy(struct scsi_device *scsi_dev)
{
struct pmcraid_resource_entry *res;
@@ -290,55 +274,18 @@ static void pmcraid_slave_destroy(struct scsi_device *scsi_dev)
* pmcraid_change_queue_depth - Change the device's queue depth
* @scsi_dev: scsi device struct
* @depth: depth to set
- * @reason: calling context
*
* Return value
* actual depth set
*/
-static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth,
- int reason)
+static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (depth > PMCRAID_MAX_CMD_PER_LUN)
depth = PMCRAID_MAX_CMD_PER_LUN;
-
- scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), depth);
-
- return scsi_dev->queue_depth;
+ return scsi_change_queue_depth(scsi_dev, depth);
}
/**
- * pmcraid_change_queue_type - Change the device's queue type
- * @scsi_dev: scsi device struct
- * @tag: type of tags to use
- *
- * Return value:
- * actual queue type set
- */
-static int pmcraid_change_queue_type(struct scsi_device *scsi_dev, int tag)
-{
- struct pmcraid_resource_entry *res;
-
- res = (struct pmcraid_resource_entry *)scsi_dev->hostdata;
-
- if ((res) && scsi_dev->tagged_supported &&
- (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) {
- scsi_set_tag_type(scsi_dev, tag);
-
- if (tag)
- scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
- else
- scsi_deactivate_tcq(scsi_dev, scsi_dev->queue_depth);
- } else
- tag = 0;
-
- return tag;
-}
-
-
-/**
* pmcraid_init_cmdblk - initializes a command block
*
* @cmd: pointer to struct pmcraid_cmd to be initialized
@@ -347,7 +294,7 @@ static int pmcraid_change_queue_type(struct scsi_device *scsi_dev, int tag)
* Return Value
* None
*/
-void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index)
+static void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index)
{
struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
dma_addr_t dma_addr = cmd->ioa_cb_bus_addr;
@@ -371,7 +318,7 @@ void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index)
ioarcb->request_flags0 = 0;
ioarcb->request_flags1 = 0;
ioarcb->cmd_timeout = 0;
- ioarcb->ioarcb_bus_addr &= (~0x1FULL);
+ ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL);
ioarcb->ioadl_bus_addr = 0;
ioarcb->ioadl_length = 0;
ioarcb->data_transfer_length = 0;
@@ -386,10 +333,10 @@ void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index)
cmd->scsi_cmd = NULL;
cmd->release = 0;
cmd->completion_req = 0;
- cmd->sense_buffer = 0;
+ cmd->sense_buffer = NULL;
cmd->sense_buffer_dma = 0;
cmd->dma_handle = 0;
- init_timer(&cmd->timer);
+ timer_setup(&cmd->timer, NULL, 0);
}
/**
@@ -442,7 +389,7 @@ static struct pmcraid_cmd *pmcraid_get_free_cmd(
* Return Value:
* nothing
*/
-void pmcraid_return_cmd(struct pmcraid_cmd *cmd)
+static void pmcraid_return_cmd(struct pmcraid_cmd *cmd)
{
struct pmcraid_instance *pinstance = cmd->drv_inst;
unsigned long lock_flags;
@@ -499,15 +446,14 @@ static void pmcraid_disable_interrupts(
* pmcraid_enable_interrupts - Enables specified interrupts
*
* @pinstance: pointer to per adapter instance structure
- * @intr: interrupts to enable
+ * @intrs: interrupts to enable
*
* Return Value
* None
*/
static void pmcraid_enable_interrupts(
struct pmcraid_instance *pinstance,
- u32 intrs
-)
+ u32 intrs)
{
u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg);
u32 nmask = gmask & (~GLOBAL_INTERRUPT_MASK);
@@ -549,7 +495,7 @@ static void pmcraid_clr_trans_op(
}
if (pinstance->reset_cmd != NULL) {
- del_timer(&pinstance->reset_cmd->timer);
+ timer_delete(&pinstance->reset_cmd->timer);
spin_lock_irqsave(
pinstance->host->host_lock, lock_flags);
pinstance->reset_cmd->cmd_done(pinstance->reset_cmd);
@@ -589,17 +535,16 @@ static void pmcraid_reset_type(struct pmcraid_instance *pinstance)
pinstance->ioa_unit_check = 1;
}
+static void pmcraid_ioa_reset(struct pmcraid_cmd *);
/**
* pmcraid_bist_done - completion function for PCI BIST
- * @cmd: pointer to reset command
+ * @t: pointer to reset command
* Return Value
* none
*/
-
-static void pmcraid_ioa_reset(struct pmcraid_cmd *);
-
-static void pmcraid_bist_done(struct pmcraid_cmd *cmd)
+static void pmcraid_bist_done(struct timer_list *t)
{
+ struct pmcraid_cmd *cmd = timer_container_of(cmd, t, timer);
struct pmcraid_instance *pinstance = cmd->drv_inst;
unsigned long lock_flags;
int rc;
@@ -613,9 +558,6 @@ static void pmcraid_bist_done(struct pmcraid_cmd *cmd)
pmcraid_info("BIST not complete, waiting another 2 secs\n");
cmd->timer.expires = jiffies + cmd->time_left;
cmd->time_left = 0;
- cmd->timer.data = (unsigned long)cmd;
- cmd->timer.function =
- (void (*)(unsigned long))pmcraid_bist_done;
add_timer(&cmd->timer);
} else {
cmd->time_left = 0;
@@ -646,20 +588,20 @@ static void pmcraid_start_bist(struct pmcraid_cmd *cmd)
doorbells, intrs);
cmd->time_left = msecs_to_jiffies(PMCRAID_BIST_TIMEOUT);
- cmd->timer.data = (unsigned long)cmd;
cmd->timer.expires = jiffies + msecs_to_jiffies(PMCRAID_BIST_TIMEOUT);
- cmd->timer.function = (void (*)(unsigned long))pmcraid_bist_done;
+ cmd->timer.function = pmcraid_bist_done;
add_timer(&cmd->timer);
}
/**
* pmcraid_reset_alert_done - completion routine for reset_alert
- * @cmd: pointer to command block used in reset sequence
+ * @t: pointer to command block used in reset sequence
* Return value
* None
*/
-static void pmcraid_reset_alert_done(struct pmcraid_cmd *cmd)
+static void pmcraid_reset_alert_done(struct timer_list *t)
{
+ struct pmcraid_cmd *cmd = timer_container_of(cmd, t, timer);
struct pmcraid_instance *pinstance = cmd->drv_inst;
u32 status = ioread32(pinstance->ioa_status);
unsigned long lock_flags;
@@ -678,24 +620,22 @@ static void pmcraid_reset_alert_done(struct pmcraid_cmd *cmd)
pmcraid_info("critical op is not yet reset waiting again\n");
/* restart timer if some more time is available to wait */
cmd->time_left -= PMCRAID_CHECK_FOR_RESET_TIMEOUT;
- cmd->timer.data = (unsigned long)cmd;
cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
- cmd->timer.function =
- (void (*)(unsigned long))pmcraid_reset_alert_done;
+ cmd->timer.function = pmcraid_reset_alert_done;
add_timer(&cmd->timer);
}
}
+static void pmcraid_notify_ioastate(struct pmcraid_instance *, u32);
/**
* pmcraid_reset_alert - alerts IOA for a possible reset
- * @cmd : command block to be used for reset sequence.
+ * @cmd: command block to be used for reset sequence.
*
* Return Value
* returns 0 if pci config-space is accessible and RESET_DOORBELL is
* successfully written to IOA. Returns non-zero in case pci_config_space
* is not accessible
*/
-static void pmcraid_notify_ioastate(struct pmcraid_instance *, u32);
static void pmcraid_reset_alert(struct pmcraid_cmd *cmd)
{
struct pmcraid_instance *pinstance = cmd->drv_inst;
@@ -717,10 +657,8 @@ static void pmcraid_reset_alert(struct pmcraid_cmd *cmd)
* bit to be reset.
*/
cmd->time_left = PMCRAID_RESET_TIMEOUT;
- cmd->timer.data = (unsigned long)cmd;
cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
- cmd->timer.function =
- (void (*)(unsigned long))pmcraid_reset_alert_done;
+ cmd->timer.function = pmcraid_reset_alert_done;
add_timer(&cmd->timer);
iowrite32(DOORBELL_IOA_RESET_ALERT,
@@ -738,15 +676,16 @@ static void pmcraid_reset_alert(struct pmcraid_cmd *cmd)
/**
* pmcraid_timeout_handler - Timeout handler for internally generated ops
*
- * @cmd : pointer to command structure, that got timedout
+ * @t: pointer to command structure, that got timedout
*
* This function blocks host requests and initiates an adapter reset.
*
* Return value:
* None
*/
-static void pmcraid_timeout_handler(struct pmcraid_cmd *cmd)
+static void pmcraid_timeout_handler(struct timer_list *t)
{
+ struct pmcraid_cmd *cmd = timer_container_of(cmd, t, timer);
struct pmcraid_instance *pinstance = cmd->drv_inst;
unsigned long lock_flags;
@@ -892,27 +831,20 @@ static void pmcraid_erp_done(struct pmcraid_cmd *cmd)
cmd->ioa_cb->ioarcb.cdb[0], ioasc);
}
- /* if we had allocated sense buffers for request sense, copy the sense
- * release the buffers
- */
- if (cmd->sense_buffer != NULL) {
- memcpy(scsi_cmd->sense_buffer,
- cmd->sense_buffer,
- SCSI_SENSE_BUFFERSIZE);
- pci_free_consistent(pinstance->pdev,
- SCSI_SENSE_BUFFERSIZE,
- cmd->sense_buffer, cmd->sense_buffer_dma);
+ if (cmd->sense_buffer) {
+ dma_unmap_single(&pinstance->pdev->dev, cmd->sense_buffer_dma,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
cmd->sense_buffer = NULL;
cmd->sense_buffer_dma = 0;
}
scsi_dma_unmap(scsi_cmd);
pmcraid_return_cmd(cmd);
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
}
/**
- * pmcraid_fire_command - sends an IOA command to adapter
+ * _pmcraid_fire_command - sends an IOA command to adapter
*
* This function adds the given block into pending command list
* and returns without waiting
@@ -939,8 +871,7 @@ static void _pmcraid_fire_command(struct pmcraid_cmd *cmd)
/* driver writes lower 32-bit value of IOARCB address only */
mb();
- iowrite32(le32_to_cpu(cmd->ioa_cb->ioarcb.ioarcb_bus_addr),
- pinstance->ioarrin);
+ iowrite32(le64_to_cpu(cmd->ioa_cb->ioarcb.ioarcb_bus_addr), pinstance->ioarrin);
}
/**
@@ -961,7 +892,7 @@ static void pmcraid_send_cmd(
struct pmcraid_cmd *cmd,
void (*cmd_done) (struct pmcraid_cmd *),
unsigned long timeout,
- void (*timeout_func) (struct pmcraid_cmd *)
+ void (*timeout_func) (struct timer_list *)
)
{
/* initialize done function */
@@ -969,9 +900,8 @@ static void pmcraid_send_cmd(
if (timeout_func) {
/* setup timeout handler */
- cmd->timer.data = (unsigned long)cmd;
cmd->timer.expires = jiffies + timeout;
- cmd->timer.function = (void (*)(unsigned long))timeout_func;
+ cmd->timer.function = timeout_func;
add_timer(&cmd->timer);
}
@@ -1031,6 +961,7 @@ static void pmcraid_ioa_shutdown(struct pmcraid_cmd *cmd)
pmcraid_timeout_handler);
}
+static void pmcraid_querycfg(struct pmcraid_cmd *);
/**
* pmcraid_get_fwversion_done - completion function for get_fwversion
*
@@ -1039,8 +970,6 @@ static void pmcraid_ioa_shutdown(struct pmcraid_cmd *cmd)
* Return Value
* none
*/
-static void pmcraid_querycfg(struct pmcraid_cmd *);
-
static void pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd)
{
struct pmcraid_instance *pinstance = cmd->drv_inst;
@@ -1073,7 +1002,7 @@ static void pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd)
static void pmcraid_get_fwversion(struct pmcraid_cmd *cmd)
{
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_ioadl_desc *ioadl;
struct pmcraid_instance *pinstance = cmd->drv_inst;
u16 data_size = sizeof(struct pmcraid_inquiry_data);
@@ -1092,7 +1021,7 @@ static void pmcraid_get_fwversion(struct pmcraid_cmd *cmd)
offsetof(struct pmcraid_ioarcb,
add_data.u.ioadl[0]));
ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
- ioarcb->ioarcb_bus_addr &= ~(0x1FULL);
+ ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
ioarcb->request_flags0 |= NO_LINK_DESCS;
ioarcb->data_transfer_length = cpu_to_le32(data_size);
@@ -1118,7 +1047,7 @@ static void pmcraid_identify_hrrq(struct pmcraid_cmd *cmd)
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
int index = cmd->hrrq_index;
__be64 hrrq_addr = cpu_to_be64(pinstance->hrrq_start_bus_addr[index]);
- u32 hrrq_size = cpu_to_be32(sizeof(u32) * PMCRAID_MAX_CMD);
+ __be32 hrrq_size = cpu_to_be32(sizeof(u32) * PMCRAID_MAX_CMD);
void (*done_function)(struct pmcraid_cmd *);
pmcraid_reinit_cmdblk(cmd);
@@ -1243,7 +1172,7 @@ static struct pmcraid_cmd *pmcraid_init_hcam
ioadl[0].flags |= IOADL_FLAGS_READ_LAST;
ioadl[0].data_len = cpu_to_le32(rcb_size);
- ioadl[0].address = cpu_to_le32(dma);
+ ioadl[0].address = cpu_to_le64(dma);
cmd->cmd_done = cmd_done;
return cmd;
@@ -1278,7 +1207,13 @@ static void pmcraid_prepare_cancel_cmd(
)
{
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
- __be64 ioarcb_addr = cmd_to_cancel->ioa_cb->ioarcb.ioarcb_bus_addr;
+ __be64 ioarcb_addr;
+
+ /* IOARCB address of the command to be cancelled is given in
+ * cdb[2]..cdb[9] is Big-Endian format. Note that length bits in
+ * IOARCB address are not masked.
+ */
+ ioarcb_addr = cpu_to_be64(le64_to_cpu(cmd_to_cancel->ioa_cb->ioarcb.ioarcb_bus_addr));
/* Get the resource handle to where the command to be aborted has been
* sent.
@@ -1288,11 +1223,6 @@ static void pmcraid_prepare_cancel_cmd(
memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
ioarcb->cdb[0] = PMCRAID_ABORT_CMD;
- /* IOARCB address of the command to be cancelled is given in
- * cdb[2]..cdb[9] is Big-Endian format. Note that length bits in
- * IOARCB address are not masked.
- */
- ioarcb_addr = cpu_to_be64(ioarcb_addr);
memcpy(&(ioarcb->cdb[2]), &ioarcb_addr, sizeof(ioarcb_addr));
}
@@ -1405,11 +1335,17 @@ enum {
};
#define PMCRAID_AEN_CMD_MAX (__PMCRAID_AEN_CMD_MAX - 1)
-static struct genl_family pmcraid_event_family = {
- .id = GENL_ID_GENERATE,
+static struct genl_multicast_group pmcraid_mcgrps[] = {
+ { .name = "events", /* not really used - see ID discussion below */ },
+};
+
+static struct genl_family pmcraid_event_family __ro_after_init = {
+ .module = THIS_MODULE,
.name = "pmcraid",
.version = 1,
- .maxattr = PMCRAID_AEN_ATTR_MAX
+ .maxattr = PMCRAID_AEN_ATTR_MAX,
+ .mcgrps = pmcraid_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(pmcraid_mcgrps),
};
/**
@@ -1419,7 +1355,7 @@ static struct genl_family pmcraid_event_family = {
* 0 if the pmcraid_event_family is successfully registered
* with netlink generic, non-zero otherwise
*/
-static int pmcraid_netlink_init(void)
+static int __init pmcraid_netlink_init(void)
{
int result;
@@ -1445,10 +1381,9 @@ static void pmcraid_netlink_release(void)
genl_unregister_family(&pmcraid_event_family);
}
-/**
+/*
* pmcraid_notify_aen - sends event msg to user space application
* @pinstance: pointer to adapter instance structure
- * @type: HCAM type
*
* Return value:
* 0 if success, error value in case of any failure.
@@ -1456,8 +1391,7 @@ static void pmcraid_netlink_release(void)
static int pmcraid_notify_aen(
struct pmcraid_instance *pinstance,
struct pmcraid_aen_msg *aen_msg,
- u32 data_size
-)
+ u32 data_size)
{
struct sk_buff *skb;
void *msg_header;
@@ -1503,17 +1437,11 @@ static int pmcraid_notify_aen(
return -EINVAL;
}
- /* send genetlink multicast message to notify appplications */
- result = genlmsg_end(skb, msg_header);
-
- if (result < 0) {
- pmcraid_err("genlmsg_end failed\n");
- nlmsg_free(skb);
- return result;
- }
+ /* send genetlink multicast message to notify applications */
+ genlmsg_end(skb, msg_header);
- result =
- genlmsg_multicast(skb, 0, pmcraid_event_family.id, GFP_ATOMIC);
+ result = genlmsg_multicast(&pmcraid_event_family, skb,
+ 0, 0, GFP_ATOMIC);
/* If there are no listeners, genlmsg_multicast may return non-zero
* value.
@@ -1534,7 +1462,7 @@ static int pmcraid_notify_ccn(struct pmcraid_instance *pinstance)
{
return pmcraid_notify_aen(pinstance,
pinstance->ccn.msg,
- pinstance->ccn.hcam->data_len +
+ le32_to_cpu(pinstance->ccn.hcam->data_len) +
sizeof(struct pmcraid_hcam_hdr));
}
@@ -1549,7 +1477,7 @@ static int pmcraid_notify_ldn(struct pmcraid_instance *pinstance)
{
return pmcraid_notify_aen(pinstance,
pinstance->ldn.msg,
- pinstance->ldn.hcam->data_len +
+ le32_to_cpu(pinstance->ldn.hcam->data_len) +
sizeof(struct pmcraid_hcam_hdr));
}
@@ -1597,10 +1525,10 @@ static void pmcraid_handle_config_change(struct pmcraid_instance *pinstance)
pmcraid_info("CCN(%x): %x timestamp: %llx type: %x lost: %x flags: %x \
res: %x:%x:%x:%x\n",
- pinstance->ccn.hcam->ilid,
+ le32_to_cpu(pinstance->ccn.hcam->ilid),
pinstance->ccn.hcam->op_code,
- ((pinstance->ccn.hcam->timestamp1) |
- ((pinstance->ccn.hcam->timestamp2 & 0xffffffffLL) << 32)),
+ (le32_to_cpu(pinstance->ccn.hcam->timestamp1) |
+ ((le32_to_cpu(pinstance->ccn.hcam->timestamp2) & 0xffffffffLL) << 32)),
pinstance->ccn.hcam->notification_type,
pinstance->ccn.hcam->notification_lost,
pinstance->ccn.hcam->flags,
@@ -1611,7 +1539,7 @@ static void pmcraid_handle_config_change(struct pmcraid_instance *pinstance)
RES_IS_VSET(*cfg_entry) ?
(fw_version <= PMCRAID_FW_VERSION_1 ?
cfg_entry->unique_flags1 :
- cfg_entry->array_id & 0xFF) :
+ le16_to_cpu(cfg_entry->array_id) & 0xFF) :
RES_TARGET(cfg_entry->resource_address),
RES_LUN(cfg_entry->resource_address));
@@ -1636,12 +1564,7 @@ static void pmcraid_handle_config_change(struct pmcraid_instance *pinstance)
if (pinstance->ccn.hcam->notification_type ==
NOTIFICATION_TYPE_ENTRY_CHANGED &&
cfg_entry->resource_type == RES_TYPE_VSET) {
-
- if (fw_version <= PMCRAID_FW_VERSION_1)
- hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0;
- else
- hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0;
-
+ hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0;
} else if (!pmcraid_expose_resource(fw_version, cfg_entry)) {
goto out_notify_apps;
}
@@ -1699,7 +1622,7 @@ static void pmcraid_handle_config_change(struct pmcraid_instance *pinstance)
if (fw_version <= PMCRAID_FW_VERSION_1)
res->cfg_entry.unique_flags1 &= 0x7F;
else
- res->cfg_entry.array_id &= 0xFF;
+ res->cfg_entry.array_id &= cpu_to_le16(0xFF);
res->change_detected = RES_CHANGE_DEL;
res->cfg_entry.resource_handle =
PMCRAID_INVALID_RES_HANDLE;
@@ -1746,7 +1669,7 @@ static struct pmcraid_ioasc_error *pmcraid_get_error_info(u32 ioasc)
* @ioasc: ioasc code
* @cmd: pointer to command that resulted in 'ioasc'
*/
-void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd)
+static void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd)
{
struct pmcraid_ioasc_error *error_info = pmcraid_get_error_info(ioasc);
@@ -1757,8 +1680,8 @@ void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd)
/* log the error string */
pmcraid_err("cmd [%x] for resource %x failed with %x(%s)\n",
cmd->ioa_cb->ioarcb.cdb[0],
- cmd->ioa_cb->ioarcb.resource_handle,
- le32_to_cpu(ioasc), error_info->error_string);
+ le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle),
+ ioasc, error_info->error_string);
}
/**
@@ -1845,6 +1768,8 @@ static void pmcraid_process_ccn(struct pmcraid_cmd *cmd)
}
}
+static void pmcraid_initiate_reset(struct pmcraid_instance *);
+static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd);
/**
* pmcraid_process_ldn - op done function for an LDN
* @cmd: pointer to command block
@@ -1852,9 +1777,6 @@ static void pmcraid_process_ccn(struct pmcraid_cmd *cmd)
* Return value
* none
*/
-static void pmcraid_initiate_reset(struct pmcraid_instance *);
-static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd);
-
static void pmcraid_process_ldn(struct pmcraid_cmd *cmd)
{
struct pmcraid_instance *pinstance = cmd->drv_inst;
@@ -1952,14 +1874,14 @@ static void pmcraid_unregister_hcams(struct pmcraid_cmd *cmd)
pmcraid_cancel_ldn(cmd);
}
+static void pmcraid_reinit_buffers(struct pmcraid_instance *);
+
/**
* pmcraid_reset_enable_ioa - re-enable IOA after a hard reset
* @pinstance: pointer to adapter instance structure
* Return Value
* 1 if TRANSITION_TO_OPERATIONAL is active, otherwise 0
*/
-static void pmcraid_reinit_buffers(struct pmcraid_instance *);
-
static int pmcraid_reset_enable_ioa(struct pmcraid_instance *pinstance)
{
u32 intrs;
@@ -2001,10 +1923,9 @@ static void pmcraid_soft_reset(struct pmcraid_cmd *cmd)
* would re-initiate a reset
*/
cmd->cmd_done = pmcraid_ioa_reset;
- cmd->timer.data = (unsigned long)cmd;
cmd->timer.expires = jiffies +
msecs_to_jiffies(PMCRAID_TRANSOP_TIMEOUT);
- cmd->timer.function = (void (*)(unsigned long))pmcraid_timeout_handler;
+ cmd->timer.function = pmcraid_timeout_handler;
if (!timer_pending(&cmd->timer))
add_timer(&cmd->timer);
@@ -2025,7 +1946,7 @@ static void pmcraid_soft_reset(struct pmcraid_cmd *cmd)
}
iowrite32(doorbell, pinstance->int_regs.host_ioa_interrupt_reg);
- ioread32(pinstance->int_regs.host_ioa_interrupt_reg),
+ ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
int_reg = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
pmcraid_info("Waiting for IOA to become operational %x:%x\n",
@@ -2075,10 +1996,10 @@ static void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance)
cmd->ioa_cb->ioasa.ioasc =
cpu_to_le32(PMCRAID_IOASC_IOA_WAS_RESET);
cmd->ioa_cb->ioasa.ilid =
- cpu_to_be32(PMCRAID_DRIVER_ILID);
+ cpu_to_le32(PMCRAID_DRIVER_ILID);
/* In case the command timer is still running */
- del_timer(&cmd->timer);
+ timer_delete(&cmd->timer);
/* If this is an IO command, complete it by invoking scsi_done
* function. If this is one of the internal commands other
@@ -2099,7 +2020,7 @@ static void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance)
le32_to_cpu(resp) >> 2,
cmd->ioa_cb->ioarcb.cdb[0],
scsi_cmd->result);
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
} else if (cmd->cmd_done == pmcraid_internal_done ||
cmd->cmd_done == pmcraid_erp_done) {
cmd->cmd_done(cmd);
@@ -2414,46 +2335,43 @@ static int pmcraid_reset_reload(
spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
if (pinstance->ioa_state == IOA_STATE_DEAD) {
- spin_unlock_irqrestore(pinstance->host->host_lock,
- lock_flags);
pmcraid_info("reset_reload: IOA is dead\n");
- return reset;
- } else if (pinstance->ioa_state == target_state) {
+ goto out_unlock;
+ }
+
+ if (pinstance->ioa_state == target_state) {
reset = 0;
+ goto out_unlock;
}
}
- if (reset) {
- pmcraid_info("reset_reload: proceeding with reset\n");
- scsi_block_requests(pinstance->host);
- reset_cmd = pmcraid_get_free_cmd(pinstance);
-
- if (reset_cmd == NULL) {
- pmcraid_err("no free cmnd for reset_reload\n");
- spin_unlock_irqrestore(pinstance->host->host_lock,
- lock_flags);
- return reset;
- }
+ pmcraid_info("reset_reload: proceeding with reset\n");
+ scsi_block_requests(pinstance->host);
+ reset_cmd = pmcraid_get_free_cmd(pinstance);
+ if (reset_cmd == NULL) {
+ pmcraid_err("no free cmnd for reset_reload\n");
+ goto out_unlock;
+ }
- if (shutdown_type == SHUTDOWN_NORMAL)
- pinstance->ioa_bringdown = 1;
+ if (shutdown_type == SHUTDOWN_NORMAL)
+ pinstance->ioa_bringdown = 1;
- pinstance->ioa_shutdown_type = shutdown_type;
- pinstance->reset_cmd = reset_cmd;
- pinstance->force_ioa_reset = reset;
- pmcraid_info("reset_reload: initiating reset\n");
- pmcraid_ioa_reset(reset_cmd);
- spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
- pmcraid_info("reset_reload: waiting for reset to complete\n");
- wait_event(pinstance->reset_wait_q,
- !pinstance->ioa_reset_in_progress);
+ pinstance->ioa_shutdown_type = shutdown_type;
+ pinstance->reset_cmd = reset_cmd;
+ pinstance->force_ioa_reset = reset;
+ pmcraid_info("reset_reload: initiating reset\n");
+ pmcraid_ioa_reset(reset_cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ pmcraid_info("reset_reload: waiting for reset to complete\n");
+ wait_event(pinstance->reset_wait_q,
+ !pinstance->ioa_reset_in_progress);
- pmcraid_info("reset_reload: reset is complete !!\n");
- scsi_unblock_requests(pinstance->host);
- if (pinstance->ioa_state == target_state)
- reset = 0;
- }
+ pmcraid_info("reset_reload: reset is complete !!\n");
+ scsi_unblock_requests(pinstance->host);
+ return pinstance->ioa_state != target_state;
+out_unlock:
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
return reset;
}
@@ -2500,13 +2418,12 @@ static void pmcraid_request_sense(struct pmcraid_cmd *cmd)
{
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct device *dev = &cmd->drv_inst->pdev->dev;
- /* allocate DMAable memory for sense buffers */
- cmd->sense_buffer = pci_alloc_consistent(cmd->drv_inst->pdev,
- SCSI_SENSE_BUFFERSIZE,
- &cmd->sense_buffer_dma);
-
- if (cmd->sense_buffer == NULL) {
+ cmd->sense_buffer = cmd->scsi_cmd->sense_buffer;
+ cmd->sense_buffer_dma = dma_map_single(dev, cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, cmd->sense_buffer_dma)) {
pmcraid_err
("couldn't allocate sense buffer for request sense\n");
pmcraid_erp_done(cmd);
@@ -2547,17 +2464,15 @@ static void pmcraid_request_sense(struct pmcraid_cmd *cmd)
/**
* pmcraid_cancel_all - cancel all outstanding IOARCBs as part of error recovery
* @cmd: command that failed
- * @sense: true if request_sense is required after cancel all
+ * @need_sense: true if request_sense is required after cancel all
*
* This function sends a cancel all to a device to clear the queue.
*/
-static void pmcraid_cancel_all(struct pmcraid_cmd *cmd, u32 sense)
+static void pmcraid_cancel_all(struct pmcraid_cmd *cmd, bool need_sense)
{
struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata;
- void (*cmd_done) (struct pmcraid_cmd *) = sense ? pmcraid_erp_done
- : pmcraid_request_sense;
memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
ioarcb->request_flags0 = SYNC_OVERRIDE;
@@ -2570,12 +2485,13 @@ static void pmcraid_cancel_all(struct pmcraid_cmd *cmd, u32 sense)
ioarcb->ioadl_bus_addr = 0;
ioarcb->ioadl_length = 0;
ioarcb->data_transfer_length = 0;
- ioarcb->ioarcb_bus_addr &= (~0x1FULL);
+ ioarcb->ioarcb_bus_addr &= cpu_to_le64((~0x1FULL));
/* writing to IOARRIN must be protected by host_lock, as mid-layer
* schedule queuecommand while we are doing this
*/
- pmcraid_send_cmd(cmd, cmd_done,
+ pmcraid_send_cmd(cmd, need_sense ?
+ pmcraid_erp_done : pmcraid_request_sense,
PMCRAID_REQUEST_SENSE_TIMEOUT,
pmcraid_timeout_handler);
}
@@ -2668,7 +2584,7 @@ static int pmcraid_error_handler(struct pmcraid_cmd *cmd)
struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa;
u32 ioasc = le32_to_cpu(ioasa->ioasc);
u32 masked_ioasc = ioasc & PMCRAID_IOASC_SENSE_MASK;
- u32 sense_copied = 0;
+ bool sense_copied = false;
if (!res) {
pmcraid_info("resource pointer is NULL\n");
@@ -2733,14 +2649,14 @@ static int pmcraid_error_handler(struct pmcraid_cmd *cmd)
* mid-layer
*/
if (ioasa->auto_sense_length != 0) {
- short sense_len = ioasa->auto_sense_length;
- int data_size = min_t(u16, le16_to_cpu(sense_len),
+ short sense_len = le16_to_cpu(ioasa->auto_sense_length);
+ int data_size = min_t(u16, sense_len,
SCSI_SENSE_BUFFERSIZE);
memcpy(scsi_cmd->sense_buffer,
ioasa->sense_data,
data_size);
- sense_copied = 1;
+ sense_copied = true;
}
if (RES_IS_GSCSI(res->cfg_entry))
@@ -2766,7 +2682,8 @@ static int pmcraid_error_handler(struct pmcraid_cmd *cmd)
/**
* pmcraid_reset_device - device reset handler functions
*
- * @scsi_cmd: scsi command struct
+ * @scsi_dev: scsi device struct
+ * @timeout: command timeout
* @modifier: reset modifier indicating the reset sequence to be performed
*
* This function issues a device reset to the affected device.
@@ -2777,10 +2694,9 @@ static int pmcraid_error_handler(struct pmcraid_cmd *cmd)
* SUCCESS / FAILED
*/
static int pmcraid_reset_device(
- struct scsi_cmnd *scsi_cmd,
+ struct scsi_device *scsi_dev,
unsigned long timeout,
- u8 modifier
-)
+ u8 modifier)
{
struct pmcraid_cmd *cmd;
struct pmcraid_instance *pinstance;
@@ -2790,11 +2706,11 @@ static int pmcraid_reset_device(
u32 ioasc;
pinstance =
- (struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
- res = scsi_cmd->device->hostdata;
+ (struct pmcraid_instance *)scsi_dev->host->hostdata;
+ res = scsi_dev->hostdata;
if (!res) {
- sdev_printk(KERN_ERR, scsi_cmd->device,
+ sdev_printk(KERN_ERR, scsi_dev,
"reset_device: NULL resource pointer\n");
return FAILED;
}
@@ -2901,7 +2817,7 @@ static int _pmcraid_io_done(struct pmcraid_cmd *cmd, int reslen, int ioasc)
if (rc == 0) {
scsi_dma_unmap(scsi_cmd);
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
}
return rc;
@@ -2940,10 +2856,8 @@ static struct pmcraid_cmd *pmcraid_abort_cmd(struct pmcraid_cmd *cmd)
{
struct pmcraid_cmd *cancel_cmd;
struct pmcraid_instance *pinstance;
- struct pmcraid_resource_entry *res;
pinstance = (struct pmcraid_instance *)cmd->drv_inst;
- res = cmd->scsi_cmd->device->hostdata;
cancel_cmd = pmcraid_get_free_cmd(pinstance);
@@ -2956,7 +2870,7 @@ static struct pmcraid_cmd *pmcraid_abort_cmd(struct pmcraid_cmd *cmd)
pmcraid_info("aborting command CDB[0]= %x with index = %d\n",
cmd->ioa_cb->ioarcb.cdb[0],
- cmd->ioa_cb->ioarcb.response_handle >> 2);
+ le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2);
init_completion(&cancel_cmd->wait_for_completion);
cancel_cmd->completion_req = 1;
@@ -3090,7 +3004,7 @@ static int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd)
}
/**
- * pmcraid_eh_xxxx_reset_handler - bus/target/device reset handler callbacks
+ * pmcraid_eh_device_reset_handler - bus/target/device reset handler callbacks
*
* @scmd: pointer to scsi_cmd that was sent to the resource to be reset.
*
@@ -3107,27 +3021,72 @@ static int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd)
{
scmd_printk(KERN_INFO, scmd,
"resetting device due to an I/O command timeout.\n");
- return pmcraid_reset_device(scmd,
+ return pmcraid_reset_device(scmd->device,
PMCRAID_INTERNAL_TIMEOUT,
RESET_DEVICE_LUN);
}
static int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd)
{
- scmd_printk(KERN_INFO, scmd,
+ struct Scsi_Host *host = scmd->device->host;
+ struct pmcraid_instance *pinstance =
+ (struct pmcraid_instance *)host->hostdata;
+ struct pmcraid_resource_entry *res = NULL;
+ struct pmcraid_resource_entry *temp;
+ struct scsi_device *sdev = NULL;
+ unsigned long lock_flags;
+
+ /*
+ * The reset device code insists on us passing down
+ * a device, so grab the first device on the bus.
+ */
+ spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
+ list_for_each_entry(temp, &pinstance->used_res_q, queue) {
+ if (scmd->device->channel == PMCRAID_VSET_BUS_ID &&
+ RES_IS_VSET(temp->cfg_entry)) {
+ res = temp;
+ break;
+ } else if (scmd->device->channel == PMCRAID_PHYS_BUS_ID &&
+ RES_IS_GSCSI(temp->cfg_entry)) {
+ res = temp;
+ break;
+ }
+ }
+ if (res)
+ sdev = res->scsi_dev;
+ spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
+ if (!sdev)
+ return FAILED;
+
+ sdev_printk(KERN_INFO, sdev,
"Doing bus reset due to an I/O command timeout.\n");
- return pmcraid_reset_device(scmd,
+ return pmcraid_reset_device(sdev,
PMCRAID_RESET_BUS_TIMEOUT,
RESET_DEVICE_BUS);
}
static int pmcraid_eh_target_reset_handler(struct scsi_cmnd *scmd)
{
- scmd_printk(KERN_INFO, scmd,
+ struct Scsi_Host *shost = scmd->device->host;
+ struct scsi_device *scsi_dev = NULL, *tmp;
+ int ret;
+
+ shost_for_each_device(tmp, shost) {
+ if ((tmp->channel == scmd->device->channel) &&
+ (tmp->id == scmd->device->id)) {
+ scsi_dev = tmp;
+ break;
+ }
+ }
+ if (!scsi_dev)
+ return FAILED;
+ sdev_printk(KERN_INFO, scsi_dev,
"Doing target reset due to an I/O command timeout.\n");
- return pmcraid_reset_device(scmd,
- PMCRAID_INTERNAL_TIMEOUT,
- RESET_DEVICE_TARGET);
+ ret = pmcraid_reset_device(scsi_dev,
+ PMCRAID_INTERNAL_TIMEOUT,
+ RESET_DEVICE_TARGET);
+ scsi_device_put(scsi_dev);
+ return ret;
}
/**
@@ -3165,36 +3124,6 @@ static int pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd)
}
/**
- * pmcraid_task_attributes - Translate SPI Q-Tags to task attributes
- * @scsi_cmd: scsi command struct
- *
- * Return value
- * number of tags or 0 if the task is not tagged
- */
-static u8 pmcraid_task_attributes(struct scsi_cmnd *scsi_cmd)
-{
- char tag[2];
- u8 rc = 0;
-
- if (scsi_populate_tag_msg(scsi_cmd, tag)) {
- switch (tag[0]) {
- case MSG_SIMPLE_TAG:
- rc = TASK_TAG_SIMPLE;
- break;
- case MSG_HEAD_TAG:
- rc = TASK_TAG_QUEUE_HEAD;
- break;
- case MSG_ORDERED_TAG:
- rc = TASK_TAG_ORDERED;
- break;
- };
- }
-
- return rc;
-}
-
-
-/**
* pmcraid_init_ioadls - initializes IOADL related fields in IOARCB
* @cmd: pmcraid command struct
* @sgcount: count of scatter-gather elements
@@ -3203,7 +3132,7 @@ static u8 pmcraid_task_attributes(struct scsi_cmnd *scsi_cmd)
* returns pointer pmcraid_ioadl_desc, initialized to point to internal
* or external IOADLs
*/
-struct pmcraid_ioadl_desc *
+static struct pmcraid_ioadl_desc *
pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount)
{
struct pmcraid_ioadl_desc *ioadl;
@@ -3211,9 +3140,8 @@ pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount)
int ioadl_count = 0;
if (ioarcb->add_cmd_param_length)
- ioadl_count = DIV_ROUND_UP(ioarcb->add_cmd_param_length, 16);
- ioarcb->ioadl_length =
- sizeof(struct pmcraid_ioadl_desc) * sgcount;
+ ioadl_count = DIV_ROUND_UP(le16_to_cpu(ioarcb->add_cmd_param_length), 16);
+ ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc) * sgcount);
if ((sgcount + ioadl_count) > (ARRAY_SIZE(ioarcb->add_data.u.ioadl))) {
/* external ioadls start at offset 0x80 from control_block
@@ -3221,7 +3149,7 @@ pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount)
* It is necessary to indicate to firmware that driver is
* using ioadls to be treated as external to IOARCB.
*/
- ioarcb->ioarcb_bus_addr &= ~(0x1FULL);
+ ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
ioarcb->ioadl_bus_addr =
cpu_to_le64((cmd->ioa_cb_bus_addr) +
offsetof(struct pmcraid_ioarcb,
@@ -3235,7 +3163,7 @@ pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount)
ioadl = &ioarcb->add_data.u.ioadl[ioadl_count];
ioarcb->ioarcb_bus_addr |=
- DIV_ROUND_CLOSEST(sgcount + ioadl_count, 8);
+ cpu_to_le64(DIV_ROUND_CLOSEST(sgcount + ioadl_count, 8));
}
return ioadl;
@@ -3262,7 +3190,7 @@ static int pmcraid_build_ioadl(
struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_ioadl_desc *ioadl;
u32 length = scsi_bufflen(scsi_cmd);
@@ -3302,168 +3230,8 @@ static int pmcraid_build_ioadl(
}
/**
- * pmcraid_free_sglist - Frees an allocated SG buffer list
- * @sglist: scatter/gather list pointer
- *
- * Free a DMA'able memory previously allocated with pmcraid_alloc_sglist
- *
- * Return value:
- * none
- */
-static void pmcraid_free_sglist(struct pmcraid_sglist *sglist)
-{
- int i;
-
- for (i = 0; i < sglist->num_sg; i++)
- __free_pages(sg_page(&(sglist->scatterlist[i])),
- sglist->order);
-
- kfree(sglist);
-}
-
-/**
- * pmcraid_alloc_sglist - Allocates memory for a SG list
- * @buflen: buffer length
- *
- * Allocates a DMA'able buffer in chunks and assembles a scatter/gather
- * list.
- *
- * Return value
- * pointer to sglist / NULL on failure
- */
-static struct pmcraid_sglist *pmcraid_alloc_sglist(int buflen)
-{
- struct pmcraid_sglist *sglist;
- struct scatterlist *scatterlist;
- struct page *page;
- int num_elem, i, j;
- int sg_size;
- int order;
- int bsize_elem;
-
- sg_size = buflen / (PMCRAID_MAX_IOADLS - 1);
- order = (sg_size > 0) ? get_order(sg_size) : 0;
- bsize_elem = PAGE_SIZE * (1 << order);
-
- /* Determine the actual number of sg entries needed */
- if (buflen % bsize_elem)
- num_elem = (buflen / bsize_elem) + 1;
- else
- num_elem = buflen / bsize_elem;
-
- /* Allocate a scatter/gather list for the DMA */
- sglist = kzalloc(sizeof(struct pmcraid_sglist) +
- (sizeof(struct scatterlist) * (num_elem - 1)),
- GFP_KERNEL);
-
- if (sglist == NULL)
- return NULL;
-
- scatterlist = sglist->scatterlist;
- sg_init_table(scatterlist, num_elem);
- sglist->order = order;
- sglist->num_sg = num_elem;
- sg_size = buflen;
-
- for (i = 0; i < num_elem; i++) {
- page = alloc_pages(GFP_KERNEL|GFP_DMA|__GFP_ZERO, order);
- if (!page) {
- for (j = i - 1; j >= 0; j--)
- __free_pages(sg_page(&scatterlist[j]), order);
- kfree(sglist);
- return NULL;
- }
-
- sg_set_page(&scatterlist[i], page,
- sg_size < bsize_elem ? sg_size : bsize_elem, 0);
- sg_size -= bsize_elem;
- }
-
- return sglist;
-}
-
-/**
- * pmcraid_copy_sglist - Copy user buffer to kernel buffer's SG list
- * @sglist: scatter/gather list pointer
- * @buffer: buffer pointer
- * @len: buffer length
- * @direction: data transfer direction
- *
- * Copy a user buffer into a buffer allocated by pmcraid_alloc_sglist
- *
- * Return value:
- * 0 on success / other on failure
- */
-static int pmcraid_copy_sglist(
- struct pmcraid_sglist *sglist,
- unsigned long buffer,
- u32 len,
- int direction
-)
-{
- struct scatterlist *scatterlist;
- void *kaddr;
- int bsize_elem;
- int i;
- int rc = 0;
-
- /* Determine the actual number of bytes per element */
- bsize_elem = PAGE_SIZE * (1 << sglist->order);
-
- scatterlist = sglist->scatterlist;
-
- for (i = 0; i < (len / bsize_elem); i++, buffer += bsize_elem) {
- struct page *page = sg_page(&scatterlist[i]);
-
- kaddr = kmap(page);
- if (direction == DMA_TO_DEVICE)
- rc = __copy_from_user(kaddr,
- (void *)buffer,
- bsize_elem);
- else
- rc = __copy_to_user((void *)buffer, kaddr, bsize_elem);
-
- kunmap(page);
-
- if (rc) {
- pmcraid_err("failed to copy user data into sg list\n");
- return -EFAULT;
- }
-
- scatterlist[i].length = bsize_elem;
- }
-
- if (len % bsize_elem) {
- struct page *page = sg_page(&scatterlist[i]);
-
- kaddr = kmap(page);
-
- if (direction == DMA_TO_DEVICE)
- rc = __copy_from_user(kaddr,
- (void *)buffer,
- len % bsize_elem);
- else
- rc = __copy_to_user((void *)buffer,
- kaddr,
- len % bsize_elem);
-
- kunmap(page);
-
- scatterlist[i].length = len % bsize_elem;
- }
-
- if (rc) {
- pmcraid_err("failed to copy user data into sg list\n");
- rc = -EFAULT;
- }
-
- return rc;
-}
-
-/**
- * pmcraid_queuecommand - Queue a mid-layer request
+ * pmcraid_queuecommand_lck - Queue a mid-layer request
* @scsi_cmd: scsi command struct
- * @done: done function
*
* This function queues a request generated by the mid-layer. Midlayer calls
* this routine within host->lock. Some of the functions called by queuecommand
@@ -3474,10 +3242,7 @@ static int pmcraid_copy_sglist(
* SCSI_MLQUEUE_DEVICE_BUSY if device is busy
* SCSI_MLQUEUE_HOST_BUSY if host is busy
*/
-static int pmcraid_queuecommand_lck(
- struct scsi_cmnd *scsi_cmd,
- void (*done) (struct scsi_cmnd *)
-)
+static int pmcraid_queuecommand_lck(struct scsi_cmnd *scsi_cmd)
{
struct pmcraid_instance *pinstance;
struct pmcraid_resource_entry *res;
@@ -3489,7 +3254,6 @@ static int pmcraid_queuecommand_lck(
pinstance =
(struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
fw_version = be16_to_cpu(pinstance->inq_data->fw_version);
- scsi_cmd->scsi_done = done;
res = scsi_cmd->device->hostdata;
scsi_cmd->result = (DID_OK << 16);
@@ -3499,7 +3263,7 @@ static int pmcraid_queuecommand_lck(
if (pinstance->ioa_state == IOA_STATE_DEAD) {
pmcraid_info("IOA is dead, but queuecommand is scheduled\n");
scsi_cmd->result = (DID_NO_CONNECT << 16);
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
return 0;
}
@@ -3512,7 +3276,7 @@ static int pmcraid_queuecommand_lck(
*/
if (scsi_cmd->cmnd[0] == SYNCHRONIZE_CACHE) {
pmcraid_info("SYNC_CACHE(0x35), completing in driver itself\n");
- scsi_cmd->scsi_done(scsi_cmd);
+ scsi_done(scsi_cmd);
return 0;
}
@@ -3549,7 +3313,9 @@ static int pmcraid_queuecommand_lck(
}
ioarcb->request_flags0 |= NO_LINK_DESCS;
- ioarcb->request_flags1 |= pmcraid_task_attributes(scsi_cmd);
+
+ if (scsi_cmd->flags & SCMD_TAGGED)
+ ioarcb->request_flags1 |= TASK_TAG_SIMPLE;
if (RES_IS_GSCSI(res->cfg_entry))
ioarcb->request_flags1 |= DELAY_AFTER_RESET;
@@ -3565,7 +3331,7 @@ static int pmcraid_queuecommand_lck(
RES_IS_VSET(res->cfg_entry) ?
(fw_version <= PMCRAID_FW_VERSION_1 ?
res->cfg_entry.unique_flags1 :
- res->cfg_entry.array_id & 0xFF) :
+ le16_to_cpu(res->cfg_entry.array_id) & 0xFF) :
RES_TARGET(res->cfg_entry.resource_address),
RES_LUN(res->cfg_entry.resource_address));
@@ -3582,7 +3348,7 @@ static int pmcraid_queuecommand_lck(
static DEF_SCSI_QCMD(pmcraid_queuecommand)
-/**
+/*
* pmcraid_open -char node "open" entry, allowed only users with admin access
*/
static int pmcraid_chr_open(struct inode *inode, struct file *filep)
@@ -3599,20 +3365,7 @@ static int pmcraid_chr_open(struct inode *inode, struct file *filep)
return 0;
}
-/**
- * pmcraid_release - char node "release" entry point
- */
-static int pmcraid_chr_release(struct inode *inode, struct file *filep)
-{
- struct pmcraid_instance *pinstance = filep->private_data;
-
- filep->private_data = NULL;
- fasync_helper(-1, filep, 0, &pinstance->aen_queue);
-
- return 0;
-}
-
-/**
+/*
* pmcraid_fasync - Async notifier registration from applications
*
* This function adds the calling process to a driver global queue. When an
@@ -3631,373 +3384,6 @@ static int pmcraid_chr_fasync(int fd, struct file *filep, int mode)
return rc;
}
-
-/**
- * pmcraid_build_passthrough_ioadls - builds SG elements for passthrough
- * commands sent over IOCTL interface
- *
- * @cmd : pointer to struct pmcraid_cmd
- * @buflen : length of the request buffer
- * @direction : data transfer direction
- *
- * Return value
- * 0 on success, non-zero error code on failure
- */
-static int pmcraid_build_passthrough_ioadls(
- struct pmcraid_cmd *cmd,
- int buflen,
- int direction
-)
-{
- struct pmcraid_sglist *sglist = NULL;
- struct scatterlist *sg = NULL;
- struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
- struct pmcraid_ioadl_desc *ioadl;
- int i;
-
- sglist = pmcraid_alloc_sglist(buflen);
-
- if (!sglist) {
- pmcraid_err("can't allocate memory for passthrough SGls\n");
- return -ENOMEM;
- }
-
- sglist->num_dma_sg = pci_map_sg(cmd->drv_inst->pdev,
- sglist->scatterlist,
- sglist->num_sg, direction);
-
- if (!sglist->num_dma_sg || sglist->num_dma_sg > PMCRAID_MAX_IOADLS) {
- dev_err(&cmd->drv_inst->pdev->dev,
- "Failed to map passthrough buffer!\n");
- pmcraid_free_sglist(sglist);
- return -EIO;
- }
-
- cmd->sglist = sglist;
- ioarcb->request_flags0 |= NO_LINK_DESCS;
-
- ioadl = pmcraid_init_ioadls(cmd, sglist->num_dma_sg);
-
- /* Initialize IOADL descriptor addresses */
- for_each_sg(sglist->scatterlist, sg, sglist->num_dma_sg, i) {
- ioadl[i].data_len = cpu_to_le32(sg_dma_len(sg));
- ioadl[i].address = cpu_to_le64(sg_dma_address(sg));
- ioadl[i].flags = 0;
- }
-
- /* setup the last descriptor */
- ioadl[i - 1].flags = IOADL_FLAGS_LAST_DESC;
-
- return 0;
-}
-
-
-/**
- * pmcraid_release_passthrough_ioadls - release passthrough ioadls
- *
- * @cmd: pointer to struct pmcraid_cmd for which ioadls were allocated
- * @buflen: size of the request buffer
- * @direction: data transfer direction
- *
- * Return value
- * 0 on success, non-zero error code on failure
- */
-static void pmcraid_release_passthrough_ioadls(
- struct pmcraid_cmd *cmd,
- int buflen,
- int direction
-)
-{
- struct pmcraid_sglist *sglist = cmd->sglist;
-
- if (buflen > 0) {
- pci_unmap_sg(cmd->drv_inst->pdev,
- sglist->scatterlist,
- sglist->num_sg,
- direction);
- pmcraid_free_sglist(sglist);
- cmd->sglist = NULL;
- }
-}
-
-/**
- * pmcraid_ioctl_passthrough - handling passthrough IOCTL commands
- *
- * @pinstance: pointer to adapter instance structure
- * @cmd: ioctl code
- * @arg: pointer to pmcraid_passthrough_buffer user buffer
- *
- * Return value
- * 0 on success, non-zero error code on failure
- */
-static long pmcraid_ioctl_passthrough(
- struct pmcraid_instance *pinstance,
- unsigned int ioctl_cmd,
- unsigned int buflen,
- unsigned long arg
-)
-{
- struct pmcraid_passthrough_ioctl_buffer *buffer;
- struct pmcraid_ioarcb *ioarcb;
- struct pmcraid_cmd *cmd;
- struct pmcraid_cmd *cancel_cmd;
- unsigned long request_buffer;
- unsigned long request_offset;
- unsigned long lock_flags;
- void *ioasa;
- u32 ioasc;
- int request_size;
- int buffer_size;
- u8 access, direction;
- int rc = 0;
-
- /* If IOA reset is in progress, wait 10 secs for reset to complete */
- if (pinstance->ioa_reset_in_progress) {
- rc = wait_event_interruptible_timeout(
- pinstance->reset_wait_q,
- !pinstance->ioa_reset_in_progress,
- msecs_to_jiffies(10000));
-
- if (!rc)
- return -ETIMEDOUT;
- else if (rc < 0)
- return -ERESTARTSYS;
- }
-
- /* If adapter is not in operational state, return error */
- if (pinstance->ioa_state != IOA_STATE_OPERATIONAL) {
- pmcraid_err("IOA is not operational\n");
- return -ENOTTY;
- }
-
- buffer_size = sizeof(struct pmcraid_passthrough_ioctl_buffer);
- buffer = kmalloc(buffer_size, GFP_KERNEL);
-
- if (!buffer) {
- pmcraid_err("no memory for passthrough buffer\n");
- return -ENOMEM;
- }
-
- request_offset =
- offsetof(struct pmcraid_passthrough_ioctl_buffer, request_buffer);
-
- request_buffer = arg + request_offset;
-
- rc = __copy_from_user(buffer,
- (struct pmcraid_passthrough_ioctl_buffer *) arg,
- sizeof(struct pmcraid_passthrough_ioctl_buffer));
-
- ioasa =
- (void *)(arg +
- offsetof(struct pmcraid_passthrough_ioctl_buffer, ioasa));
-
- if (rc) {
- pmcraid_err("ioctl: can't copy passthrough buffer\n");
- rc = -EFAULT;
- goto out_free_buffer;
- }
-
- request_size = buffer->ioarcb.data_transfer_length;
-
- if (buffer->ioarcb.request_flags0 & TRANSFER_DIR_WRITE) {
- access = VERIFY_READ;
- direction = DMA_TO_DEVICE;
- } else {
- access = VERIFY_WRITE;
- direction = DMA_FROM_DEVICE;
- }
-
- if (request_size > 0) {
- rc = access_ok(access, arg, request_offset + request_size);
-
- if (!rc) {
- rc = -EFAULT;
- goto out_free_buffer;
- }
- } else if (request_size < 0) {
- rc = -EINVAL;
- goto out_free_buffer;
- }
-
- /* check if we have any additional command parameters */
- if (buffer->ioarcb.add_cmd_param_length > PMCRAID_ADD_CMD_PARAM_LEN) {
- rc = -EINVAL;
- goto out_free_buffer;
- }
-
- cmd = pmcraid_get_free_cmd(pinstance);
-
- if (!cmd) {
- pmcraid_err("free command block is not available\n");
- rc = -ENOMEM;
- goto out_free_buffer;
- }
-
- cmd->scsi_cmd = NULL;
- ioarcb = &(cmd->ioa_cb->ioarcb);
-
- /* Copy the user-provided IOARCB stuff field by field */
- ioarcb->resource_handle = buffer->ioarcb.resource_handle;
- ioarcb->data_transfer_length = buffer->ioarcb.data_transfer_length;
- ioarcb->cmd_timeout = buffer->ioarcb.cmd_timeout;
- ioarcb->request_type = buffer->ioarcb.request_type;
- ioarcb->request_flags0 = buffer->ioarcb.request_flags0;
- ioarcb->request_flags1 = buffer->ioarcb.request_flags1;
- memcpy(ioarcb->cdb, buffer->ioarcb.cdb, PMCRAID_MAX_CDB_LEN);
-
- if (buffer->ioarcb.add_cmd_param_length) {
- ioarcb->add_cmd_param_length =
- buffer->ioarcb.add_cmd_param_length;
- ioarcb->add_cmd_param_offset =
- buffer->ioarcb.add_cmd_param_offset;
- memcpy(ioarcb->add_data.u.add_cmd_params,
- buffer->ioarcb.add_data.u.add_cmd_params,
- buffer->ioarcb.add_cmd_param_length);
- }
-
- /* set hrrq number where the IOA should respond to. Note that all cmds
- * generated internally uses hrrq_id 0, exception to this is the cmd
- * block of scsi_cmd which is re-used (e.g. cancel/abort), which uses
- * hrrq_id assigned here in queuecommand
- */
- ioarcb->hrrq_id = atomic_add_return(1, &(pinstance->last_message_id)) %
- pinstance->num_hrrq;
-
- if (request_size) {
- rc = pmcraid_build_passthrough_ioadls(cmd,
- request_size,
- direction);
- if (rc) {
- pmcraid_err("couldn't build passthrough ioadls\n");
- goto out_free_buffer;
- }
- }
-
- /* If data is being written into the device, copy the data from user
- * buffers
- */
- if (direction == DMA_TO_DEVICE && request_size > 0) {
- rc = pmcraid_copy_sglist(cmd->sglist,
- request_buffer,
- request_size,
- direction);
- if (rc) {
- pmcraid_err("failed to copy user buffer\n");
- goto out_free_sglist;
- }
- }
-
- /* passthrough ioctl is a blocking command so, put the user to sleep
- * until timeout. Note that a timeout value of 0 means, do timeout.
- */
- cmd->cmd_done = pmcraid_internal_done;
- init_completion(&cmd->wait_for_completion);
- cmd->completion_req = 1;
-
- pmcraid_info("command(%d) (CDB[0] = %x) for %x\n",
- le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2,
- cmd->ioa_cb->ioarcb.cdb[0],
- le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle));
-
- spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
- _pmcraid_fire_command(cmd);
- spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
-
- /* NOTE ! Remove the below line once abort_task is implemented
- * in firmware. This line disables ioctl command timeout handling logic
- * similar to IO command timeout handling, making ioctl commands to wait
- * until the command completion regardless of timeout value specified in
- * ioarcb
- */
- buffer->ioarcb.cmd_timeout = 0;
-
- /* If command timeout is specified put caller to wait till that time,
- * otherwise it would be blocking wait. If command gets timed out, it
- * will be aborted.
- */
- if (buffer->ioarcb.cmd_timeout == 0) {
- wait_for_completion(&cmd->wait_for_completion);
- } else if (!wait_for_completion_timeout(
- &cmd->wait_for_completion,
- msecs_to_jiffies(buffer->ioarcb.cmd_timeout * 1000))) {
-
- pmcraid_info("aborting cmd %d (CDB[0] = %x) due to timeout\n",
- le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle >> 2),
- cmd->ioa_cb->ioarcb.cdb[0]);
-
- spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
- cancel_cmd = pmcraid_abort_cmd(cmd);
- spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
-
- if (cancel_cmd) {
- wait_for_completion(&cancel_cmd->wait_for_completion);
- ioasc = cancel_cmd->ioa_cb->ioasa.ioasc;
- pmcraid_return_cmd(cancel_cmd);
-
- /* if abort task couldn't find the command i.e it got
- * completed prior to aborting, return good completion.
- * if command got aborted successfully or there was IOA
- * reset due to abort task itself getting timedout then
- * return -ETIMEDOUT
- */
- if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET ||
- PMCRAID_IOASC_SENSE_KEY(ioasc) == 0x00) {
- if (ioasc != PMCRAID_IOASC_GC_IOARCB_NOTFOUND)
- rc = -ETIMEDOUT;
- goto out_handle_response;
- }
- }
-
- /* no command block for abort task or abort task failed to abort
- * the IOARCB, then wait for 150 more seconds and initiate reset
- * sequence after timeout
- */
- if (!wait_for_completion_timeout(
- &cmd->wait_for_completion,
- msecs_to_jiffies(150 * 1000))) {
- pmcraid_reset_bringup(cmd->drv_inst);
- rc = -ETIMEDOUT;
- }
- }
-
-out_handle_response:
- /* copy entire IOASA buffer and return IOCTL success.
- * If copying IOASA to user-buffer fails, return
- * EFAULT
- */
- if (copy_to_user(ioasa, &cmd->ioa_cb->ioasa,
- sizeof(struct pmcraid_ioasa))) {
- pmcraid_err("failed to copy ioasa buffer to user\n");
- rc = -EFAULT;
- }
-
- /* If the data transfer was from device, copy the data onto user
- * buffers
- */
- else if (direction == DMA_FROM_DEVICE && request_size > 0) {
- rc = pmcraid_copy_sglist(cmd->sglist,
- request_buffer,
- request_size,
- direction);
- if (rc) {
- pmcraid_err("failed to copy user buffer\n");
- rc = -EFAULT;
- }
- }
-
-out_free_sglist:
- pmcraid_release_passthrough_ioadls(cmd, request_size, direction);
- pmcraid_return_cmd(cmd);
-
-out_free_buffer:
- kfree(buffer);
-
- return rc;
-}
-
-
-
-
/**
* pmcraid_ioctl_driver - ioctl handler for commands handled by driver itself
*
@@ -4018,11 +3404,6 @@ static long pmcraid_ioctl_driver(
{
int rc = -ENOSYS;
- if (!access_ok(VERIFY_READ, user_buffer, _IOC_SIZE(cmd))) {
- pmcraid_err("ioctl_driver: access fault in request buffer\n");
- return -EFAULT;
- }
-
switch (cmd) {
case PMCRAID_IOCTL_RESET_ADAPTER:
pmcraid_reset_bringup(pinstance);
@@ -4054,8 +3435,7 @@ static int pmcraid_check_ioctl_buffer(
struct pmcraid_ioctl_header *hdr
)
{
- int rc = 0;
- int access = VERIFY_READ;
+ int rc;
if (copy_from_user(hdr, arg, sizeof(struct pmcraid_ioctl_header))) {
pmcraid_err("couldn't copy ioctl header from user buffer\n");
@@ -4071,23 +3451,10 @@ static int pmcraid_check_ioctl_buffer(
return -EINVAL;
}
- /* check for appropriate buffer access */
- if ((_IOC_DIR(cmd) & _IOC_READ) == _IOC_READ)
- access = VERIFY_WRITE;
-
- rc = access_ok(access,
- (arg + sizeof(struct pmcraid_ioctl_header)),
- hdr->buffer_length);
- if (!rc) {
- pmcraid_err("access failed for user buffer of size %d\n",
- hdr->buffer_length);
- return -EFAULT;
- }
-
return 0;
}
-/**
+/*
* pmcraid_ioctl - char node ioctl entry point
*/
static long pmcraid_chr_ioctl(
@@ -4098,16 +3465,17 @@ static long pmcraid_chr_ioctl(
{
struct pmcraid_instance *pinstance = NULL;
struct pmcraid_ioctl_header *hdr = NULL;
+ void __user *argp = (void __user *)arg;
int retval = -ENOTTY;
- hdr = kmalloc(GFP_KERNEL, sizeof(struct pmcraid_ioctl_header));
+ hdr = kmalloc(sizeof(struct pmcraid_ioctl_header), GFP_KERNEL);
if (!hdr) {
- pmcraid_err("faile to allocate memory for ioctl header\n");
+ pmcraid_err("failed to allocate memory for ioctl header\n");
return -ENOMEM;
}
- retval = pmcraid_check_ioctl_buffer(cmd, (void *)arg, hdr);
+ retval = pmcraid_check_ioctl_buffer(cmd, argp, hdr);
if (retval) {
pmcraid_info("chr_ioctl: header check failed\n");
@@ -4125,28 +3493,10 @@ static long pmcraid_chr_ioctl(
switch (_IOC_TYPE(cmd)) {
- case PMCRAID_PASSTHROUGH_IOCTL:
- /* If ioctl code is to download microcode, we need to block
- * mid-layer requests.
- */
- if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE)
- scsi_block_requests(pinstance->host);
-
- retval = pmcraid_ioctl_passthrough(pinstance,
- cmd,
- hdr->buffer_length,
- arg);
-
- if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE)
- scsi_unblock_requests(pinstance->host);
- break;
-
case PMCRAID_DRIVER_IOCTL:
arg += sizeof(struct pmcraid_ioctl_header);
- retval = pmcraid_ioctl_driver(pinstance,
- cmd,
- hdr->buffer_length,
- (void __user *)arg);
+ retval = pmcraid_ioctl_driver(pinstance, cmd,
+ hdr->buffer_length, argp);
break;
default:
@@ -4159,18 +3509,15 @@ static long pmcraid_chr_ioctl(
return retval;
}
-/**
+/*
* File operations structure for management interface
*/
static const struct file_operations pmcraid_fops = {
.owner = THIS_MODULE,
.open = pmcraid_chr_open,
- .release = pmcraid_chr_release,
.fasync = pmcraid_chr_fasync,
.unlocked_ioctl = pmcraid_chr_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = pmcraid_chr_ioctl,
-#endif
+ .compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};
@@ -4180,6 +3527,7 @@ static const struct file_operations pmcraid_fops = {
/**
* pmcraid_show_log_level - Display adapter's error logging level
* @dev: class device struct
+ * @attr: unused
* @buf: buffer
*
* Return value:
@@ -4199,6 +3547,7 @@ static ssize_t pmcraid_show_log_level(
/**
* pmcraid_store_log_level - Change the adapter's error logging level
* @dev: class device struct
+ * @attr: unused
* @buf: buffer
* @count: not used
*
@@ -4214,9 +3563,9 @@ static ssize_t pmcraid_store_log_level(
{
struct Scsi_Host *shost;
struct pmcraid_instance *pinstance;
- unsigned long val;
+ u8 val;
- if (strict_strtoul(buf, 10, &val))
+ if (kstrtou8(buf, 10, &val))
return -EINVAL;
/* log-level should be from 0 to 2 */
if (val > 2)
@@ -4241,6 +3590,7 @@ static struct device_attribute pmcraid_log_level_attr = {
/**
* pmcraid_show_drv_version - Display driver version
* @dev: class device struct
+ * @attr: unused
* @buf: buffer
*
* Return value:
@@ -4265,8 +3615,9 @@ static struct device_attribute pmcraid_driver_version_attr = {
};
/**
- * pmcraid_show_io_adapter_id - Display driver assigned adapter id
+ * pmcraid_show_adapter_id - Display driver assigned adapter id
* @dev: class device struct
+ * @attr: unused
* @buf: buffer
*
* Return value:
@@ -4281,8 +3632,7 @@ static ssize_t pmcraid_show_adapter_id(
struct Scsi_Host *shost = class_to_shost(dev);
struct pmcraid_instance *pinstance =
(struct pmcraid_instance *)shost->hostdata;
- u32 adapter_id = (pinstance->pdev->bus->number << 8) |
- pinstance->pdev->devfn;
+ u32 adapter_id = pci_dev_id(pinstance->pdev);
u32 aen_group = pmcraid_event_family.id;
return snprintf(buf, PAGE_SIZE,
@@ -4293,21 +3643,22 @@ static ssize_t pmcraid_show_adapter_id(
static struct device_attribute pmcraid_adapter_id_attr = {
.attr = {
.name = "adapter_id",
- .mode = S_IRUGO | S_IWUSR,
+ .mode = S_IRUGO,
},
.show = pmcraid_show_adapter_id,
};
-static struct device_attribute *pmcraid_host_attrs[] = {
- &pmcraid_log_level_attr,
- &pmcraid_driver_version_attr,
- &pmcraid_adapter_id_attr,
+static struct attribute *pmcraid_host_attrs[] = {
+ &pmcraid_log_level_attr.attr,
+ &pmcraid_driver_version_attr.attr,
+ &pmcraid_adapter_id_attr.attr,
NULL,
};
+ATTRIBUTE_GROUPS(pmcraid_host);
/* host template structure for pmcraid driver */
-static struct scsi_host_template pmcraid_host_template = {
+static const struct scsi_host_template pmcraid_host_template = {
.module = THIS_MODULE,
.name = PMCRAID_DRIVER_NAME,
.queuecommand = pmcraid_queuecommand,
@@ -4317,19 +3668,18 @@ static struct scsi_host_template pmcraid_host_template = {
.eh_device_reset_handler = pmcraid_eh_device_reset_handler,
.eh_host_reset_handler = pmcraid_eh_host_reset_handler,
- .slave_alloc = pmcraid_slave_alloc,
- .slave_configure = pmcraid_slave_configure,
- .slave_destroy = pmcraid_slave_destroy,
+ .sdev_init = pmcraid_sdev_init,
+ .sdev_configure = pmcraid_sdev_configure,
+ .sdev_destroy = pmcraid_sdev_destroy,
.change_queue_depth = pmcraid_change_queue_depth,
- .change_queue_type = pmcraid_change_queue_type,
.can_queue = PMCRAID_MAX_IO_CMD,
.this_id = -1,
.sg_tablesize = PMCRAID_MAX_IOADLS,
.max_sectors = PMCRAID_IOA_MAX_SECTORS,
+ .no_write_same = 1,
.cmd_per_lun = PMCRAID_MAX_CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = pmcraid_host_attrs,
- .proc_name = PMCRAID_DRIVER_NAME
+ .shost_groups = pmcraid_host_groups,
+ .proc_name = PMCRAID_DRIVER_NAME,
};
/*
@@ -4548,7 +3898,7 @@ static void pmcraid_worker_function(struct work_struct *workp)
if (fw_version <= PMCRAID_FW_VERSION_1)
target = res->cfg_entry.unique_flags1;
else
- target = res->cfg_entry.array_id & 0xFF;
+ target = le16_to_cpu(res->cfg_entry.array_id) & 0xFF;
lun = PMCRAID_VSET_LUN_ID;
} else {
bus = PMCRAID_PHYS_BUS_ID;
@@ -4587,7 +3937,7 @@ static void pmcraid_tasklet_function(unsigned long instance)
unsigned long host_lock_flags;
spinlock_t *lockp; /* hrrq buffer lock */
int id;
- __le32 resp;
+ u32 resp;
hrrq_vector = (struct pmcraid_isr_param *)instance;
pinstance = hrrq_vector->drv_inst;
@@ -4632,7 +3982,7 @@ static void pmcraid_tasklet_function(unsigned long instance)
list_del(&cmd->free_list);
spin_unlock_irqrestore(&pinstance->pending_pool_lock,
pending_lock_flags);
- del_timer(&cmd->timer);
+ timer_delete(&cmd->timer);
atomic_dec(&pinstance->outstanding_cmds);
if (cmd->cmd_done == pmcraid_ioa_reset) {
@@ -4659,22 +4009,20 @@ static void pmcraid_tasklet_function(unsigned long instance)
* This routine un-registers registered interrupt handler and
* also frees irqs/vectors.
*
- * Retun Value
+ * Return Value
* None
*/
static
void pmcraid_unregister_interrupt_handler(struct pmcraid_instance *pinstance)
{
+ struct pci_dev *pdev = pinstance->pdev;
int i;
for (i = 0; i < pinstance->num_hrrq; i++)
- free_irq(pinstance->hrrq_vector[i].vector,
- &(pinstance->hrrq_vector[i]));
+ free_irq(pci_irq_vector(pdev, i), &pinstance->hrrq_vector[i]);
- if (pinstance->interrupt_mode) {
- pci_disable_msix(pinstance->pdev);
- pinstance->interrupt_mode = 0;
- }
+ pinstance->interrupt_mode = 0;
+ pci_free_irq_vectors(pdev);
}
/**
@@ -4687,70 +4035,52 @@ void pmcraid_unregister_interrupt_handler(struct pmcraid_instance *pinstance)
static int
pmcraid_register_interrupt_handler(struct pmcraid_instance *pinstance)
{
- int rc;
struct pci_dev *pdev = pinstance->pdev;
+ unsigned int irq_flag = PCI_IRQ_INTX, flag;
+ int num_hrrq, rc, i;
+ irq_handler_t isr;
- if ((pmcraid_enable_msix) &&
- (pci_find_capability(pdev, PCI_CAP_ID_MSIX))) {
- int num_hrrq = PMCRAID_NUM_MSIX_VECTORS;
- struct msix_entry entries[PMCRAID_NUM_MSIX_VECTORS];
- int i;
- for (i = 0; i < PMCRAID_NUM_MSIX_VECTORS; i++)
- entries[i].entry = i;
+ if (pmcraid_enable_msix)
+ irq_flag |= PCI_IRQ_MSIX;
- rc = pci_enable_msix(pdev, entries, num_hrrq);
- if (rc < 0)
- goto pmcraid_isr_legacy;
+ num_hrrq = pci_alloc_irq_vectors(pdev, 1, PMCRAID_NUM_MSIX_VECTORS,
+ irq_flag);
+ if (num_hrrq < 0)
+ return num_hrrq;
- /* Check how many MSIX vectors are allocated and register
- * msi-x handlers for each of them giving appropriate buffer
- */
- if (rc > 0) {
- num_hrrq = rc;
- if (pci_enable_msix(pdev, entries, num_hrrq))
- goto pmcraid_isr_legacy;
- }
+ if (pdev->msix_enabled) {
+ flag = 0;
+ isr = pmcraid_isr_msix;
+ } else {
+ flag = IRQF_SHARED;
+ isr = pmcraid_isr;
+ }
- for (i = 0; i < num_hrrq; i++) {
- pinstance->hrrq_vector[i].hrrq_id = i;
- pinstance->hrrq_vector[i].drv_inst = pinstance;
- pinstance->hrrq_vector[i].vector = entries[i].vector;
- rc = request_irq(pinstance->hrrq_vector[i].vector,
- pmcraid_isr_msix, 0,
- PMCRAID_DRIVER_NAME,
- &(pinstance->hrrq_vector[i]));
-
- if (rc) {
- int j;
- for (j = 0; j < i; j++)
- free_irq(entries[j].vector,
- &(pinstance->hrrq_vector[j]));
- pci_disable_msix(pdev);
- goto pmcraid_isr_legacy;
- }
- }
+ for (i = 0; i < num_hrrq; i++) {
+ struct pmcraid_isr_param *vec = &pinstance->hrrq_vector[i];
- pinstance->num_hrrq = num_hrrq;
+ vec->hrrq_id = i;
+ vec->drv_inst = pinstance;
+ rc = request_irq(pci_irq_vector(pdev, i), isr, flag,
+ PMCRAID_DRIVER_NAME, vec);
+ if (rc)
+ goto out_unwind;
+ }
+
+ pinstance->num_hrrq = num_hrrq;
+ if (pdev->msix_enabled) {
pinstance->interrupt_mode = 1;
iowrite32(DOORBELL_INTR_MODE_MSIX,
pinstance->int_regs.host_ioa_interrupt_reg);
ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
- goto pmcraid_isr_out;
}
-pmcraid_isr_legacy:
- /* If MSI-X registration failed fallback to legacy mode, where
- * only one hrrq entry will be used
- */
- pinstance->hrrq_vector[0].hrrq_id = 0;
- pinstance->hrrq_vector[0].drv_inst = pinstance;
- pinstance->hrrq_vector[0].vector = pdev->irq;
- pinstance->num_hrrq = 1;
- rc = 0;
-
- rc = request_irq(pdev->irq, pmcraid_isr, IRQF_SHARED,
- PMCRAID_DRIVER_NAME, &pinstance->hrrq_vector[0]);
-pmcraid_isr_out:
+ return 0;
+
+out_unwind:
+ while (--i >= 0)
+ free_irq(pci_irq_vector(pdev, i), &pinstance->hrrq_vector[i]);
+ pci_free_irq_vectors(pdev);
return rc;
}
@@ -4797,27 +4127,26 @@ pmcraid_release_control_blocks(
return;
for (i = 0; i < max_index; i++) {
- pci_pool_free(pinstance->control_pool,
+ dma_pool_free(pinstance->control_pool,
pinstance->cmd_list[i]->ioa_cb,
pinstance->cmd_list[i]->ioa_cb_bus_addr);
pinstance->cmd_list[i]->ioa_cb = NULL;
pinstance->cmd_list[i]->ioa_cb_bus_addr = 0;
}
- pci_pool_destroy(pinstance->control_pool);
+ dma_pool_destroy(pinstance->control_pool);
pinstance->control_pool = NULL;
}
/**
* pmcraid_allocate_cmd_blocks - allocate memory for cmd block structures
- * @pinstance - pointer to per adapter instance structure
+ * @pinstance: pointer to per adapter instance structure
*
* Allocates memory for command blocks using kernel slab allocator.
*
* Return Value
* 0 in case of success; -ENOMEM in case of failure
*/
-static int __devinit
-pmcraid_allocate_cmd_blocks(struct pmcraid_instance *pinstance)
+static int pmcraid_allocate_cmd_blocks(struct pmcraid_instance *pinstance)
{
int i;
@@ -4853,8 +4182,7 @@ pmcraid_allocate_cmd_blocks(struct pmcraid_instance *pinstance)
* Return Value
* 0 in case it can allocate all control blocks, otherwise -ENOMEM
*/
-static int __devinit
-pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
+static int pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
{
int i;
@@ -4862,8 +4190,8 @@ pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
pinstance->host->unique_id);
pinstance->control_pool =
- pci_pool_create(pinstance->ctl_pool_name,
- pinstance->pdev,
+ dma_pool_create(pinstance->ctl_pool_name,
+ &pinstance->pdev->dev,
sizeof(struct pmcraid_control_block),
PMCRAID_IOARCB_ALIGNMENT, 0);
@@ -4872,7 +4200,7 @@ pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
for (i = 0; i < PMCRAID_MAX_CMD; i++) {
pinstance->cmd_list[i]->ioa_cb =
- pci_pool_alloc(
+ dma_pool_zalloc(
pinstance->control_pool,
GFP_KERNEL,
&(pinstance->cmd_list[i]->ioa_cb_bus_addr));
@@ -4881,8 +4209,6 @@ pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
pmcraid_release_control_blocks(pinstance, i);
return -ENOMEM;
}
- memset(pinstance->cmd_list[i]->ioa_cb, 0,
- sizeof(struct pmcraid_control_block));
}
return 0;
}
@@ -4899,9 +4225,9 @@ static void
pmcraid_release_host_rrqs(struct pmcraid_instance *pinstance, int maxindex)
{
int i;
- for (i = 0; i < maxindex; i++) {
- pci_free_consistent(pinstance->pdev,
+ for (i = 0; i < maxindex; i++) {
+ dma_free_coherent(&pinstance->pdev->dev,
HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD,
pinstance->hrrq_start[i],
pinstance->hrrq_start_bus_addr[i]);
@@ -4920,8 +4246,7 @@ pmcraid_release_host_rrqs(struct pmcraid_instance *pinstance, int maxindex)
* Return value
* 0 hrrq buffers are allocated, -ENOMEM otherwise.
*/
-static int __devinit
-pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance)
+static int pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance)
{
int i, buffer_size;
@@ -4929,19 +4254,16 @@ pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance)
for (i = 0; i < pinstance->num_hrrq; i++) {
pinstance->hrrq_start[i] =
- pci_alloc_consistent(
- pinstance->pdev,
- buffer_size,
- &(pinstance->hrrq_start_bus_addr[i]));
-
- if (pinstance->hrrq_start[i] == 0) {
+ dma_alloc_coherent(&pinstance->pdev->dev, buffer_size,
+ &pinstance->hrrq_start_bus_addr[i],
+ GFP_KERNEL);
+ if (!pinstance->hrrq_start[i]) {
pmcraid_err("pci_alloc failed for hrrq vector : %d\n",
i);
pmcraid_release_host_rrqs(pinstance, i);
return -ENOMEM;
}
- memset(pinstance->hrrq_start[i], 0, buffer_size);
pinstance->hrrq_curr[i] = pinstance->hrrq_start[i];
pinstance->hrrq_end[i] =
pinstance->hrrq_start[i] + PMCRAID_MAX_CMD - 1;
@@ -4962,7 +4284,7 @@ pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance)
static void pmcraid_release_hcams(struct pmcraid_instance *pinstance)
{
if (pinstance->ccn.msg != NULL) {
- pci_free_consistent(pinstance->pdev,
+ dma_free_coherent(&pinstance->pdev->dev,
PMCRAID_AEN_HDR_SIZE +
sizeof(struct pmcraid_hcam_ccn_ext),
pinstance->ccn.msg,
@@ -4974,7 +4296,7 @@ static void pmcraid_release_hcams(struct pmcraid_instance *pinstance)
}
if (pinstance->ldn.msg != NULL) {
- pci_free_consistent(pinstance->pdev,
+ dma_free_coherent(&pinstance->pdev->dev,
PMCRAID_AEN_HDR_SIZE +
sizeof(struct pmcraid_hcam_ldn),
pinstance->ldn.msg,
@@ -4995,17 +4317,15 @@ static void pmcraid_release_hcams(struct pmcraid_instance *pinstance)
*/
static int pmcraid_allocate_hcams(struct pmcraid_instance *pinstance)
{
- pinstance->ccn.msg = pci_alloc_consistent(
- pinstance->pdev,
+ pinstance->ccn.msg = dma_alloc_coherent(&pinstance->pdev->dev,
PMCRAID_AEN_HDR_SIZE +
sizeof(struct pmcraid_hcam_ccn_ext),
- &(pinstance->ccn.baddr));
+ &pinstance->ccn.baddr, GFP_KERNEL);
- pinstance->ldn.msg = pci_alloc_consistent(
- pinstance->pdev,
+ pinstance->ldn.msg = dma_alloc_coherent(&pinstance->pdev->dev,
PMCRAID_AEN_HDR_SIZE +
sizeof(struct pmcraid_hcam_ldn),
- &(pinstance->ldn.baddr));
+ &pinstance->ldn.baddr, GFP_KERNEL);
if (pinstance->ldn.msg == NULL || pinstance->ccn.msg == NULL) {
pmcraid_release_hcams(pinstance);
@@ -5033,7 +4353,7 @@ static void pmcraid_release_config_buffers(struct pmcraid_instance *pinstance)
{
if (pinstance->cfg_table != NULL &&
pinstance->cfg_table_bus_addr != 0) {
- pci_free_consistent(pinstance->pdev,
+ dma_free_coherent(&pinstance->pdev->dev,
sizeof(struct pmcraid_config_table),
pinstance->cfg_table,
pinstance->cfg_table_bus_addr);
@@ -5060,14 +4380,14 @@ static void pmcraid_release_config_buffers(struct pmcraid_instance *pinstance)
* Return Value
* 0 for successful allocation, -ENOMEM for any failure
*/
-static int __devinit
-pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance)
+static int pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance)
{
int i;
pinstance->res_entries =
- kzalloc(sizeof(struct pmcraid_resource_entry) *
- PMCRAID_MAX_RESOURCES, GFP_KERNEL);
+ kcalloc(PMCRAID_MAX_RESOURCES,
+ sizeof(struct pmcraid_resource_entry),
+ GFP_KERNEL);
if (NULL == pinstance->res_entries) {
pmcraid_err("failed to allocate memory for resource table\n");
@@ -5078,10 +4398,10 @@ pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance)
list_add_tail(&pinstance->res_entries[i].queue,
&pinstance->free_res_q);
- pinstance->cfg_table =
- pci_alloc_consistent(pinstance->pdev,
+ pinstance->cfg_table = dma_alloc_coherent(&pinstance->pdev->dev,
sizeof(struct pmcraid_config_table),
- &pinstance->cfg_table_bus_addr);
+ &pinstance->cfg_table_bus_addr,
+ GFP_KERNEL);
if (NULL == pinstance->cfg_table) {
pmcraid_err("couldn't alloc DMA memory for config table\n");
@@ -5146,7 +4466,7 @@ static void pmcraid_release_buffers(struct pmcraid_instance *pinstance)
pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
if (pinstance->inq_data != NULL) {
- pci_free_consistent(pinstance->pdev,
+ dma_free_coherent(&pinstance->pdev->dev,
sizeof(struct pmcraid_inquiry_data),
pinstance->inq_data,
pinstance->inq_data_baddr);
@@ -5156,7 +4476,7 @@ static void pmcraid_release_buffers(struct pmcraid_instance *pinstance)
}
if (pinstance->timestamp_data != NULL) {
- pci_free_consistent(pinstance->pdev,
+ dma_free_coherent(&pinstance->pdev->dev,
sizeof(struct pmcraid_timestamp_data),
pinstance->timestamp_data,
pinstance->timestamp_data_baddr);
@@ -5173,13 +4493,13 @@ static void pmcraid_release_buffers(struct pmcraid_instance *pinstance)
* This routine pre-allocates memory based on the type of block as below:
* cmdblocks(PMCRAID_MAX_CMD): kernel memory using kernel's slab_allocator,
* IOARCBs(PMCRAID_MAX_CMD) : DMAable memory, using pci pool allocator
- * config-table entries : DMAable memory using pci_alloc_consistent
- * HostRRQs : DMAable memory, using pci_alloc_consistent
+ * config-table entries : DMAable memory using dma_alloc_coherent
+ * HostRRQs : DMAable memory, using dma_alloc_coherent
*
* Return Value
* 0 in case all of the blocks are allocated, -ENOMEM otherwise.
*/
-static int __devinit pmcraid_init_buffers(struct pmcraid_instance *pinstance)
+static int pmcraid_init_buffers(struct pmcraid_instance *pinstance)
{
int i;
@@ -5211,11 +4531,9 @@ static int __devinit pmcraid_init_buffers(struct pmcraid_instance *pinstance)
}
/* allocate DMAable memory for page D0 INQUIRY buffer */
- pinstance->inq_data = pci_alloc_consistent(
- pinstance->pdev,
+ pinstance->inq_data = dma_alloc_coherent(&pinstance->pdev->dev,
sizeof(struct pmcraid_inquiry_data),
- &pinstance->inq_data_baddr);
-
+ &pinstance->inq_data_baddr, GFP_KERNEL);
if (pinstance->inq_data == NULL) {
pmcraid_err("couldn't allocate DMA memory for INQUIRY\n");
pmcraid_release_buffers(pinstance);
@@ -5223,11 +4541,10 @@ static int __devinit pmcraid_init_buffers(struct pmcraid_instance *pinstance)
}
/* allocate DMAable memory for set timestamp data buffer */
- pinstance->timestamp_data = pci_alloc_consistent(
- pinstance->pdev,
+ pinstance->timestamp_data = dma_alloc_coherent(&pinstance->pdev->dev,
sizeof(struct pmcraid_timestamp_data),
- &pinstance->timestamp_data_baddr);
-
+ &pinstance->timestamp_data_baddr,
+ GFP_KERNEL);
if (pinstance->timestamp_data == NULL) {
pmcraid_err("couldn't allocate DMA memory for \
set time_stamp \n");
@@ -5279,11 +4596,8 @@ static void pmcraid_reinit_buffers(struct pmcraid_instance *pinstance)
* Return Value
* 0 on success, non-zero in case of any failure
*/
-static int __devinit pmcraid_init_instance(
- struct pci_dev *pdev,
- struct Scsi_Host *host,
- void __iomem *mapped_pci_addr
-)
+static int pmcraid_init_instance(struct pci_dev *pdev, struct Scsi_Host *host,
+ void __iomem *mapped_pci_addr)
{
struct pmcraid_instance *pinstance =
(struct pmcraid_instance *)host->hostdata;
@@ -5323,7 +4637,7 @@ static int __devinit pmcraid_init_instance(
mapped_pci_addr + chip_cfg->ioa_host_mask_clr;
pint_regs->global_interrupt_mask_reg =
mapped_pci_addr + chip_cfg->global_intr_mask;
- };
+ }
pinstance->ioa_reset_attempts = 0;
init_waitqueue_head(&pinstance->reset_wait_q);
@@ -5370,19 +4684,19 @@ static void pmcraid_shutdown(struct pci_dev *pdev)
}
-/**
+/*
* pmcraid_get_minor - returns unused minor number from minor number bitmap
*/
static unsigned short pmcraid_get_minor(void)
{
int minor;
- minor = find_first_zero_bit(pmcraid_minor, sizeof(pmcraid_minor));
+ minor = find_first_zero_bit(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
__set_bit(minor, pmcraid_minor);
return minor;
}
-/**
+/*
* pmcraid_release_minor - releases given minor back to minor number bitmap
*/
static void pmcraid_release_minor(unsigned short minor)
@@ -5412,7 +4726,7 @@ static int pmcraid_setup_chrdev(struct pmcraid_instance *pinstance)
if (error)
pmcraid_release_minor(minor);
else
- device_create(pmcraid_class, NULL, MKDEV(pmcraid_major, minor),
+ device_create(&pmcraid_class, NULL, MKDEV(pmcraid_major, minor),
NULL, "%s%u", PMCRAID_DEVFILE, minor);
return error;
}
@@ -5428,7 +4742,7 @@ static int pmcraid_setup_chrdev(struct pmcraid_instance *pinstance)
static void pmcraid_release_chrdev(struct pmcraid_instance *pinstance)
{
pmcraid_release_minor(MINOR(pinstance->cdev.dev));
- device_destroy(pmcraid_class,
+ device_destroy(&pmcraid_class,
MKDEV(pmcraid_major, MINOR(pinstance->cdev.dev)));
cdev_del(&pinstance->cdev);
}
@@ -5440,7 +4754,7 @@ static void pmcraid_release_chrdev(struct pmcraid_instance *pinstance)
* Return value
* none
*/
-static void __devexit pmcraid_remove(struct pci_dev *pdev)
+static void pmcraid_remove(struct pci_dev *pdev)
{
struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
@@ -5457,7 +4771,7 @@ static void __devexit pmcraid_remove(struct pci_dev *pdev)
pmcraid_shutdown(pdev);
pmcraid_disable_interrupts(pinstance, ~0);
- flush_work_sync(&pinstance->worker_q);
+ flush_work(&pinstance->worker_q);
pmcraid_kill_tasklets(pinstance);
pmcraid_unregister_interrupt_handler(pinstance);
@@ -5470,61 +4784,44 @@ static void __devexit pmcraid_remove(struct pci_dev *pdev)
return;
}
-#ifdef CONFIG_PM
/**
* pmcraid_suspend - driver suspend entry point for power management
- * @pdev: PCI device structure
- * @state: PCI power state to suspend routine
+ * @dev: Device structure
*
* Return Value - 0 always
*/
-static int pmcraid_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused pmcraid_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
pmcraid_shutdown(pdev);
pmcraid_disable_interrupts(pinstance, ~0);
pmcraid_kill_tasklets(pinstance);
- pci_set_drvdata(pinstance->pdev, pinstance);
pmcraid_unregister_interrupt_handler(pinstance);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
/**
* pmcraid_resume - driver resume entry point PCI power management
- * @pdev: PCI device structure
+ * @dev: Device structure
*
* Return Value - 0 in case of success. Error code in case of any failure
*/
-static int pmcraid_resume(struct pci_dev *pdev)
+static int __maybe_unused pmcraid_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
struct Scsi_Host *host = pinstance->host;
- int rc;
-
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
-
- rc = pci_enable_device(pdev);
-
- if (rc) {
- dev_err(&pdev->dev, "resume: Enable device failed\n");
- return rc;
- }
-
- pci_set_master(pdev);
+ int rc = 0;
- if ((sizeof(dma_addr_t) == 4) ||
- pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (sizeof(dma_addr_t) == 4 ||
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (rc == 0)
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (rc != 0) {
dev_err(&pdev->dev, "resume: Failed to set PCI DMA mask\n");
@@ -5570,18 +4867,10 @@ release_host:
scsi_host_put(host);
disable_device:
- pci_disable_device(pdev);
return rc;
}
-#else
-
-#define pmcraid_suspend NULL
-#define pmcraid_resume NULL
-
-#endif /* CONFIG_PM */
-
/**
* pmcraid_complete_ioa_reset - Called by either timer or tasklet during
* completion of the ioa reset
@@ -5653,13 +4942,10 @@ static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd)
struct pmcraid_instance *pinstance = cmd->drv_inst;
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
__be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN);
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
-
- struct timeval tv;
- __le64 timestamp;
+ struct pmcraid_ioadl_desc *ioadl;
+ u64 timestamp;
- do_gettimeofday(&tv);
- timestamp = tv.tv_sec * 1000;
+ timestamp = ktime_get_real_seconds() * 1000;
pinstance->timestamp_data->timestamp[0] = (__u8)(timestamp);
pinstance->timestamp_data->timestamp[1] = (__u8)((timestamp) >> 8);
@@ -5679,7 +4965,7 @@ static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd)
offsetof(struct pmcraid_ioarcb,
add_data.u.ioadl[0]));
ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
- ioarcb->ioarcb_bus_addr &= ~(0x1FULL);
+ ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
ioarcb->request_flags0 |= NO_LINK_DESCS;
ioarcb->request_flags0 |= TRANSFER_DIR_WRITE;
@@ -5738,7 +5024,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd)
list_for_each_entry_safe(res, temp, &pinstance->used_res_q, queue)
list_move_tail(&res->queue, &old_res);
- for (i = 0; i < pinstance->cfg_table->num_entries; i++) {
+ for (i = 0; i < le16_to_cpu(pinstance->cfg_table->num_entries); i++) {
if (be16_to_cpu(pinstance->inq_data->fw_version) <=
PMCRAID_FW_VERSION_1)
cfgte = &pinstance->cfg_table->entries[i];
@@ -5793,7 +5079,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd)
res->cfg_entry.resource_type,
(fw_version <= PMCRAID_FW_VERSION_1 ?
res->cfg_entry.unique_flags1 :
- res->cfg_entry.array_id & 0xFF),
+ le16_to_cpu(res->cfg_entry.array_id) & 0xFF),
le32_to_cpu(res->cfg_entry.resource_address));
}
}
@@ -5829,9 +5115,9 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd)
static void pmcraid_querycfg(struct pmcraid_cmd *cmd)
{
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_ioadl_desc *ioadl;
struct pmcraid_instance *pinstance = cmd->drv_inst;
- int cfg_table_size = cpu_to_be32(sizeof(struct pmcraid_config_table));
+ __be32 cfg_table_size = cpu_to_be32(sizeof(struct pmcraid_config_table));
if (be16_to_cpu(pinstance->inq_data->fw_version) <=
PMCRAID_FW_VERSION_1)
@@ -5856,7 +5142,7 @@ static void pmcraid_querycfg(struct pmcraid_cmd *cmd)
offsetof(struct pmcraid_ioarcb,
add_data.u.ioadl[0]));
ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
- ioarcb->ioarcb_bus_addr &= ~(0x1FULL);
+ ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL);
ioarcb->request_flags0 |= NO_LINK_DESCS;
ioarcb->data_transfer_length =
@@ -5881,10 +5167,8 @@ static void pmcraid_querycfg(struct pmcraid_cmd *cmd)
* returns 0 if the device is claimed and successfully configured.
* returns non-zero error code in case of any failure
*/
-static int __devinit pmcraid_probe(
- struct pci_dev *pdev,
- const struct pci_device_id *dev_id
-)
+static int pmcraid_probe(struct pci_dev *pdev,
+ const struct pci_device_id *dev_id)
{
struct pmcraid_instance *pinstance;
struct Scsi_Host *host;
@@ -5933,19 +5217,19 @@ static int __devinit pmcraid_probe(
/* Firmware requires the system bus address of IOARCB to be within
* 32-bit addressable range though it has 64-bit IOARRIN register.
* However, firmware supports 64-bit streaming DMA buffers, whereas
- * coherent buffers are to be 32-bit. Since pci_alloc_consistent always
+ * coherent buffers are to be 32-bit. Since dma_alloc_coherent always
* returns memory within 4GB (if not, change this logic), coherent
* buffers are within firmware acceptable address ranges.
*/
- if ((sizeof(dma_addr_t) == 4) ||
- pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (sizeof(dma_addr_t) == 4 ||
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
/* firmware expects 32-bit DMA addresses for IOARRIN register; set 32
- * bit mask for pci_alloc_consistent to return addresses within 4GB
+ * bit mask for dma_alloc_coherent to return addresses within 4GB
*/
if (rc == 0)
- rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (rc != 0) {
dev_err(&pdev->dev, "Failed to set PCI DMA mask\n");
@@ -6070,21 +5354,21 @@ out_release_regions:
out_disable_device:
atomic_dec(&pmcraid_adapter_count);
- pci_set_drvdata(pdev, NULL);
pci_disable_device(pdev);
return -ENODEV;
}
+static SIMPLE_DEV_PM_OPS(pmcraid_pm_ops, pmcraid_suspend, pmcraid_resume);
+
/*
- * PCI driver structure of pcmraid driver
+ * PCI driver structure of pmcraid driver
*/
static struct pci_driver pmcraid_driver = {
.name = PMCRAID_DRIVER_NAME,
.id_table = pmcraid_pci_table,
.probe = pmcraid_probe,
.remove = pmcraid_remove,
- .suspend = pmcraid_suspend,
- .resume = pmcraid_resume,
+ .driver.pm = &pmcraid_pm_ops,
.shutdown = pmcraid_shutdown
};
@@ -6109,19 +5393,21 @@ static int __init pmcraid_init(void)
}
pmcraid_major = MAJOR(dev);
- pmcraid_class = class_create(THIS_MODULE, PMCRAID_DEVFILE);
- if (IS_ERR(pmcraid_class)) {
- error = PTR_ERR(pmcraid_class);
- pmcraid_err("failed to register with with sysfs, error = %x\n",
+ error = class_register(&pmcraid_class);
+
+ if (error) {
+ pmcraid_err("failed to register with sysfs, error = %x\n",
error);
goto out_unreg_chrdev;
}
error = pmcraid_netlink_init();
- if (error)
+ if (error) {
+ class_unregister(&pmcraid_class);
goto out_unreg_chrdev;
+ }
error = pci_register_driver(&pmcraid_driver);
@@ -6130,7 +5416,7 @@ static int __init pmcraid_init(void)
pmcraid_err("failed to register pmcraid driver, error = %x\n",
error);
- class_destroy(pmcraid_class);
+ class_unregister(&pmcraid_class);
pmcraid_netlink_release();
out_unreg_chrdev:
@@ -6149,7 +5435,7 @@ static void __exit pmcraid_exit(void)
unregister_chrdev_region(MKDEV(pmcraid_major, 0),
PMCRAID_MAX_ADAPTERS);
pci_unregister_driver(&pmcraid_driver);
- class_destroy(pmcraid_class);
+ class_unregister(&pmcraid_class);
}
module_init(pmcraid_init);
diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h
index f920baf3ff24..9f59930e8b4f 100644
--- a/drivers/scsi/pmcraid.h
+++ b/drivers/scsi/pmcraid.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* pmcraid.h -- PMC Sierra MaxRAID controller driver header file
*
@@ -5,26 +6,11 @@
* PMC-Sierra Inc
*
* Copyright (C) 2008, 2009 PMC Sierra Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PMCRAID_H
#define _PMCRAID_H
-#include <linux/version.h>
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/list.h>
@@ -61,8 +47,8 @@
/*
* MAX_CMD : maximum commands that can be outstanding with IOA
* MAX_IO_CMD : command blocks available for IO commands
- * MAX_HCAM_CMD : command blocks avaibale for HCAMS
- * MAX_INTERNAL_CMD : command blocks avaible for internal commands like reset
+ * MAX_HCAM_CMD : command blocks available for HCAMS
+ * MAX_INTERNAL_CMD : command blocks available for internal commands like reset
*/
#define PMCRAID_MAX_CMD 1024
#define PMCRAID_MAX_IO_CMD 1020
@@ -258,7 +244,7 @@ struct pmcraid_ioarcb {
__u8 hrrq_id;
__u8 cdb[PMCRAID_MAX_CDB_LEN];
struct pmcraid_ioarcb_add_data add_data;
-} __attribute__((packed, aligned(PMCRAID_IOARCB_ALIGNMENT)));
+};
/* well known resource handle values */
#define PMCRAID_IOA_RES_HANDLE 0xffffffff
@@ -543,8 +529,7 @@ struct pmcraid_sglist {
u32 order;
u32 num_sg;
u32 num_dma_sg;
- u32 buffer_len;
- struct scatterlist scatterlist[1];
+ struct scatterlist *scatterlist;
};
/* page D0 inquiry data of focal point resource */
@@ -555,7 +540,7 @@ struct pmcraid_inquiry_data {
__u8 add_page_len;
__u8 length;
__u8 reserved2;
- __le16 fw_version;
+ __be16 fw_version;
__u8 reserved3[16];
};
@@ -629,7 +614,6 @@ struct pmcraid_interrupts {
/* ISR parameters LLD allocates (one for each MSI-X if enabled) vectors */
struct pmcraid_isr_param {
struct pmcraid_instance *drv_inst;
- u16 vector; /* allocated msi-x vector */
u8 hrrq_id; /* hrrq entry index */
};
@@ -639,7 +623,7 @@ struct pmcraid_aen_msg {
u32 hostno;
u32 length;
u8 reserved[8];
- u8 data[0];
+ u8 data[];
};
/* Controller state event message type */
@@ -699,13 +683,13 @@ struct pmcraid_instance {
dma_addr_t hrrq_start_bus_addr[PMCRAID_NUM_MSIX_VECTORS];
/* Pointer to 1st entry of HRRQ */
- __be32 *hrrq_start[PMCRAID_NUM_MSIX_VECTORS];
+ __le32 *hrrq_start[PMCRAID_NUM_MSIX_VECTORS];
/* Pointer to last entry of HRRQ */
- __be32 *hrrq_end[PMCRAID_NUM_MSIX_VECTORS];
+ __le32 *hrrq_end[PMCRAID_NUM_MSIX_VECTORS];
/* Pointer to current pointer of hrrq */
- __be32 *hrrq_curr[PMCRAID_NUM_MSIX_VECTORS];
+ __le32 *hrrq_curr[PMCRAID_NUM_MSIX_VECTORS];
/* Lock for HRRQ access */
spinlock_t hrrq_lock[PMCRAID_NUM_MSIX_VECTORS];
@@ -757,7 +741,7 @@ struct pmcraid_instance {
/* structures related to command blocks */
struct kmem_cache *cmd_cachep; /* cache for cmd blocks */
- struct pci_pool *control_pool; /* pool for control blocks */
+ struct dma_pool *control_pool; /* pool for control blocks */
char cmd_pool_name[64]; /* name of cmd cache */
char ctl_pool_name[64]; /* name of control cache */
@@ -858,11 +842,11 @@ static struct pmcraid_ioasc_error pmcraid_ioasc_error_table[] = {
{0x01180600, IOASC_LOG_LEVEL_HARD,
"Recovered Error, soft media error, sector reassignment suggested"},
{0x015D0000, IOASC_LOG_LEVEL_HARD,
- "Recovered Error, failure prediction thresold exceeded"},
+ "Recovered Error, failure prediction threshold exceeded"},
{0x015D9200, IOASC_LOG_LEVEL_HARD,
- "Recovered Error, soft Cache Card Battery error thresold"},
+ "Recovered Error, soft Cache Card Battery error threshold"},
{0x015D9200, IOASC_LOG_LEVEL_HARD,
- "Recovered Error, soft Cache Card Battery error thresold"},
+ "Recovered Error, soft Cache Card Battery error threshold"},
{0x02048000, IOASC_LOG_LEVEL_HARD,
"Not Ready, IOA Reset Required"},
{0x02408500, IOASC_LOG_LEVEL_HARD,
@@ -1039,40 +1023,15 @@ struct pmcraid_ioctl_header {
#define PMCRAID_IOCTL_SIGNATURE "PMCRAID"
/*
- * pmcraid_passthrough_ioctl_buffer - structure given as argument to
- * passthrough(or firmware handled) IOCTL commands. Note that ioarcb requires
- * 32-byte alignment so, it is necessary to pack this structure to avoid any
- * holes between ioctl_header and passthrough buffer
- *
- * .ioactl_header : ioctl header
- * .ioarcb : filled-up ioarcb buffer, driver always reads this buffer
- * .ioasa : buffer for ioasa, driver fills this with IOASA from firmware
- * .request_buffer: The I/O buffer (flat), driver reads/writes to this based on
- * the transfer directions passed in ioarcb.flags0. Contents
- * of this buffer are valid only when ioarcb.data_transfer_len
- * is not zero.
- */
-struct pmcraid_passthrough_ioctl_buffer {
- struct pmcraid_ioctl_header ioctl_header;
- struct pmcraid_ioarcb ioarcb;
- struct pmcraid_ioasa ioasa;
- u8 request_buffer[1];
-} __attribute__ ((packed));
-
-/*
* keys to differentiate between driver handled IOCTLs and passthrough
* IOCTLs passed to IOA. driver determines the ioctl type using macro
* _IOC_TYPE
*/
#define PMCRAID_DRIVER_IOCTL 'D'
-#define PMCRAID_PASSTHROUGH_IOCTL 'F'
#define DRV_IOCTL(n, size) \
_IOC(_IOC_READ|_IOC_WRITE, PMCRAID_DRIVER_IOCTL, (n), (size))
-#define FMW_IOCTL(n, size) \
- _IOC(_IOC_READ|_IOC_WRITE, PMCRAID_PASSTHROUGH_IOCTL, (n), (size))
-
/*
* _ARGSIZE: macro that gives size of the argument type passed to an IOCTL cmd.
* This is to facilitate applications avoiding un-necessary memory allocations.
@@ -1085,12 +1044,4 @@ struct pmcraid_passthrough_ioctl_buffer {
#define PMCRAID_IOCTL_RESET_ADAPTER \
DRV_IOCTL(5, sizeof(struct pmcraid_ioctl_header))
-/* passthrough/firmware handled commands */
-#define PMCRAID_IOCTL_PASSTHROUGH_COMMAND \
- FMW_IOCTL(1, sizeof(struct pmcraid_passthrough_ioctl_buffer))
-
-#define PMCRAID_IOCTL_DOWNLOAD_MICROCODE \
- FMW_IOCTL(2, sizeof(struct pmcraid_passthrough_ioctl_buffer))
-
-
#endif /* _PMCRAID_H */
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index d164c9639361..ea682f3044b6 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -37,6 +37,7 @@ typedef struct {
unsigned long recon_tmo; /* How many usecs to wait for reconnection (6th bit) */
unsigned int failed:1; /* Failure flag */
unsigned wanted:1; /* Parport sharing busy flag */
+ unsigned int dev_no; /* Device number */
wait_queue_head_t *waiting;
struct Scsi_Host *host;
struct list_head list;
@@ -44,6 +45,16 @@ typedef struct {
#include "ppa.h"
+static unsigned int mode = PPA_AUTODETECT;
+module_param(mode, uint, 0644);
+MODULE_PARM_DESC(mode, "Transfer mode (0 = Autodetect, 1 = SPP 4-bit, "
+ "2 = SPP 8-bit, 3 = EPP 8-bit, 4 = EPP 16-bit, 5 = EPP 32-bit");
+
+static struct scsi_pointer *ppa_scsi_pointer(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
static inline ppa_struct *ppa_dev(struct Scsi_Host *host)
{
return *(ppa_struct **)&host->hostdata;
@@ -55,7 +66,7 @@ static void got_it(ppa_struct *dev)
{
dev->base = dev->dev->port->base;
if (dev->cur_cmd)
- dev->cur_cmd->SCp.phase = 1;
+ ppa_scsi_pointer(dev->cur_cmd)->phase = 1;
else
wake_up(dev->waiting);
}
@@ -118,8 +129,9 @@ static inline void ppa_pb_release(ppa_struct *dev)
* Also gives a method to use a script to obtain optimum timings (TODO)
*/
-static inline int ppa_proc_write(ppa_struct *dev, char *buffer, int length)
+static inline int ppa_write_info(struct Scsi_Host *host, char *buffer, int length)
{
+ ppa_struct *dev = ppa_dev(host);
unsigned long x;
if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {
@@ -137,38 +149,20 @@ static inline int ppa_proc_write(ppa_struct *dev, char *buffer, int length)
return -EINVAL;
}
-static int ppa_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout)
+static int ppa_show_info(struct seq_file *m, struct Scsi_Host *host)
{
- int len = 0;
ppa_struct *dev = ppa_dev(host);
- if (inout)
- return ppa_proc_write(dev, buffer, length);
-
- len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
- len +=
- sprintf(buffer + len, "Parport : %s\n",
- dev->dev->port->name);
- len +=
- sprintf(buffer + len, "Mode : %s\n",
- PPA_MODE_STRING[dev->mode]);
+ seq_printf(m, "Version : %s\n", PPA_VERSION);
+ seq_printf(m, "Parport : %s\n", dev->dev->port->name);
+ seq_printf(m, "Mode : %s\n", PPA_MODE_STRING[dev->mode]);
#if PPA_DEBUG > 0
- len +=
- sprintf(buffer + len, "recon_tmo : %lu\n", dev->recon_tmo);
+ seq_printf(m, "recon_tmo : %lu\n", dev->recon_tmo);
#endif
-
- /* Request for beyond end of buffer */
- if (offset > length)
- return 0;
-
- *start = buffer + offset;
- len -= offset;
- if (len > length)
- len = length;
- return len;
+ return 0;
}
-static int device_check(ppa_struct *dev);
+static int device_check(ppa_struct *dev, bool autodetect);
#if PPA_DEBUG > 0
#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\
@@ -313,13 +307,10 @@ static int ppa_out(ppa_struct *dev, char *buffer, int len)
case PPA_EPP_8:
epp_reset(ppb);
w_ctr(ppb, 0x4);
-#ifdef CONFIG_SCSI_IZIP_EPP16
- if (!(((long) buffer | len) & 0x01))
- outsw(ppb + 4, buffer, len >> 1);
-#else
- if (!(((long) buffer | len) & 0x03))
+ if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x03))
outsl(ppb + 4, buffer, len >> 2);
-#endif
+ else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x01))
+ outsw(ppb + 4, buffer, len >> 1);
else
outsb(ppb + 4, buffer, len);
w_ctr(ppb, 0xc);
@@ -366,13 +357,10 @@ static int ppa_in(ppa_struct *dev, char *buffer, int len)
case PPA_EPP_8:
epp_reset(ppb);
w_ctr(ppb, 0x24);
-#ifdef CONFIG_SCSI_IZIP_EPP16
- if (!(((long) buffer | len) & 0x01))
- insw(ppb + 4, buffer, len >> 1);
-#else
- if (!(((long) buffer | len) & 0x03))
+ if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x03))
insl(ppb + 4, buffer, len >> 2);
-#endif
+ else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x01))
+ insw(ppb + 4, buffer, len >> 1);
else
insb(ppb + 4, buffer, len);
w_ctr(ppb, 0x2c);
@@ -480,6 +468,27 @@ static int ppa_init(ppa_struct *dev)
{
int retv;
unsigned short ppb = dev->base;
+ bool autodetect = dev->mode == PPA_AUTODETECT;
+
+ if (autodetect) {
+ int modes = dev->dev->port->modes;
+ int ppb_hi = dev->dev->port->base_hi;
+
+ /* Mode detection works up the chain of speed
+ * This avoids a nasty if-then-else-if-... tree
+ */
+ dev->mode = PPA_NIBBLE;
+
+ if (modes & PARPORT_MODE_TRISTATE)
+ dev->mode = PPA_PS2;
+
+ if (modes & PARPORT_MODE_ECP) {
+ w_ecr(ppb_hi, 0x20);
+ dev->mode = PPA_PS2;
+ }
+ if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP))
+ w_ecr(ppb_hi, 0x80);
+ }
ppa_disconnect(dev);
ppa_connect(dev, CONNECT_NORMAL);
@@ -503,7 +512,7 @@ static int ppa_init(ppa_struct *dev)
if (retv)
return -EIO;
- return device_check(dev);
+ return device_check(dev, autodetect);
}
static inline int ppa_send_command(struct scsi_cmnd *cmd)
@@ -527,13 +536,14 @@ static inline int ppa_send_command(struct scsi_cmnd *cmd)
* The driver appears to remain stable if we speed up the parallel port
* i/o in this function, but not elsewhere.
*/
-static int ppa_completion(struct scsi_cmnd *cmd)
+static int ppa_completion(struct scsi_cmnd *const cmd)
{
/* Return codes:
* -1 Error
* 0 Told to schedule
* 1 Finished data transfer
*/
+ struct scsi_pointer *scsi_pointer = ppa_scsi_pointer(cmd);
ppa_struct *dev = ppa_dev(cmd->device->host);
unsigned short ppb = dev->base;
unsigned long start_jiffies = jiffies;
@@ -559,7 +569,7 @@ static int ppa_completion(struct scsi_cmnd *cmd)
if (time_after(jiffies, start_jiffies + 1))
return 0;
- if ((cmd->SCp.this_residual <= 0)) {
+ if (scsi_pointer->this_residual <= 0) {
ppa_fail(dev, DID_ERROR);
return -1; /* ERROR_RETURN */
}
@@ -588,28 +598,30 @@ static int ppa_completion(struct scsi_cmnd *cmd)
}
/* determine if we should use burst I/O */
- fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE))
- ? PPA_BURST_SIZE : 1;
+ fast = bulk && scsi_pointer->this_residual >= PPA_BURST_SIZE ?
+ PPA_BURST_SIZE : 1;
if (r == (unsigned char) 0xc0)
- status = ppa_out(dev, cmd->SCp.ptr, fast);
+ status = ppa_out(dev, scsi_pointer->ptr, fast);
else
- status = ppa_in(dev, cmd->SCp.ptr, fast);
+ status = ppa_in(dev, scsi_pointer->ptr, fast);
- cmd->SCp.ptr += fast;
- cmd->SCp.this_residual -= fast;
+ scsi_pointer->ptr += fast;
+ scsi_pointer->this_residual -= fast;
if (!status) {
ppa_fail(dev, DID_BUS_BUSY);
return -1; /* ERROR_RETURN */
}
- if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+ if (scsi_pointer->buffer && !scsi_pointer->this_residual) {
/* if scatter/gather, advance to the next segment */
- if (cmd->SCp.buffers_residual--) {
- cmd->SCp.buffer++;
- cmd->SCp.this_residual =
- cmd->SCp.buffer->length;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
+ if (scsi_pointer->buffers_residual--) {
+ scsi_pointer->buffer =
+ sg_next(scsi_pointer->buffer);
+ scsi_pointer->this_residual =
+ scsi_pointer->buffer->length;
+ scsi_pointer->ptr =
+ sg_virt(scsi_pointer->buffer);
}
}
/* Now check to see if the drive is ready to comunicate */
@@ -645,7 +657,7 @@ static void ppa_interrupt(struct work_struct *work)
case DID_OK:
break;
case DID_NO_CONNECT:
- printk(KERN_DEBUG "ppa: no device at SCSI ID %i\n", cmd->device->target);
+ printk(KERN_DEBUG "ppa: no device at SCSI ID %i\n", scmd_id(cmd));
break;
case DID_BUS_BUSY:
printk(KERN_DEBUG "ppa: BUS BUSY - EPP timeout detected\n");
@@ -674,18 +686,19 @@ static void ppa_interrupt(struct work_struct *work)
}
#endif
- if (cmd->SCp.phase > 1)
+ if (ppa_scsi_pointer(cmd)->phase > 1)
ppa_disconnect(dev);
ppa_pb_dismiss(dev);
dev->cur_cmd = NULL;
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
{
+ struct scsi_pointer *scsi_pointer = ppa_scsi_pointer(cmd);
unsigned short ppb = dev->base;
unsigned char l = 0, h = 0;
int retv;
@@ -696,7 +709,7 @@ static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
if (dev->failed)
return 0;
- switch (cmd->SCp.phase) {
+ switch (scsi_pointer->phase) {
case 0: /* Phase 0 - Waiting for parport */
if (time_after(jiffies, dev->jstart + HZ)) {
/*
@@ -731,15 +744,17 @@ static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
return 1; /* Try again in a jiffy */
}
}
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
}
+ fallthrough;
case 2: /* Phase 2 - We are now talking to the scsi bus */
if (!ppa_select(dev, scmd_id(cmd))) {
ppa_fail(dev, DID_NO_CONNECT);
return 0;
}
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
case 3: /* Phase 3 - Ready to accept a command */
w_ctr(ppb, 0x0c);
@@ -748,20 +763,23 @@ static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
if (!ppa_send_command(cmd))
return 0;
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
case 4: /* Phase 4 - Setup scatter/gather buffers */
if (scsi_bufflen(cmd)) {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
+ scsi_pointer->buffer = scsi_sglist(cmd);
+ scsi_pointer->this_residual =
+ scsi_pointer->buffer->length;
+ scsi_pointer->ptr = sg_virt(scsi_pointer->buffer);
} else {
- cmd->SCp.buffer = NULL;
- cmd->SCp.this_residual = 0;
- cmd->SCp.ptr = NULL;
+ scsi_pointer->buffer = NULL;
+ scsi_pointer->this_residual = 0;
+ scsi_pointer->ptr = NULL;
}
- cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
- cmd->SCp.phase++;
+ scsi_pointer->buffers_residual = scsi_sg_count(cmd) - 1;
+ scsi_pointer->phase++;
+ fallthrough;
case 5: /* Phase 5 - Data transfer stage */
w_ctr(ppb, 0x0c);
@@ -773,7 +791,8 @@ static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
return 0;
if (retv == 0)
return 1;
- cmd->SCp.phase++;
+ scsi_pointer->phase++;
+ fallthrough;
case 6: /* Phase 6 - Read status/message */
cmd->result = DID_OK << 16;
@@ -790,7 +809,6 @@ static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
(DID_OK << 16) + (h << 8) + (l & STATUS_MASK);
}
return 0; /* Finished */
- break;
default:
printk(KERN_ERR "ppa: Invalid scsi phase\n");
@@ -798,8 +816,7 @@ static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
return 0;
}
-static int ppa_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*done) (struct scsi_cmnd *))
+static int ppa_queuecommand_lck(struct scsi_cmnd *cmd)
{
ppa_struct *dev = ppa_dev(cmd->device->host);
@@ -810,9 +827,8 @@ static int ppa_queuecommand_lck(struct scsi_cmnd *cmd,
dev->failed = 0;
dev->jstart = jiffies;
dev->cur_cmd = cmd;
- cmd->scsi_done = done;
cmd->result = DID_ERROR << 16; /* default return code */
- cmd->SCp.phase = 0; /* bus free */
+ ppa_scsi_pointer(cmd)->phase = 0; /* bus free */
schedule_delayed_work(&dev->ppa_tq, 0);
@@ -829,7 +845,7 @@ static DEF_SCSI_QCMD(ppa_queuecommand)
* be done in sd.c. Even if it gets fixed there, this will still
* work.
*/
-static int ppa_biosparam(struct scsi_device *sdev, struct block_device *dev,
+static int ppa_biosparam(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int ip[])
{
ip[0] = 0x40;
@@ -853,15 +869,13 @@ static int ppa_abort(struct scsi_cmnd *cmd)
* have tied the SCSI_MESSAGE line high in the interface
*/
- switch (cmd->SCp.phase) {
+ switch (ppa_scsi_pointer(cmd)->phase) {
case 0: /* Do not have access to parport */
case 1: /* Have not connected to interface */
dev->cur_cmd = NULL; /* Forget the problem */
return SUCCESS;
- break;
default: /* SCSI command sent, can not abort */
return FAILED;
- break;
}
}
@@ -877,7 +891,7 @@ static int ppa_reset(struct scsi_cmnd *cmd)
{
ppa_struct *dev = ppa_dev(cmd->device->host);
- if (cmd->SCp.phase)
+ if (ppa_scsi_pointer(cmd)->phase)
ppa_disconnect(dev);
dev->cur_cmd = NULL; /* Forget the problem */
@@ -889,7 +903,7 @@ static int ppa_reset(struct scsi_cmnd *cmd)
return SUCCESS;
}
-static int device_check(ppa_struct *dev)
+static int device_check(ppa_struct *dev, bool autodetect)
{
/* This routine looks for a device and then attempts to use EPP
to send a command. If all goes as planned then EPP is available. */
@@ -901,8 +915,8 @@ static int device_check(ppa_struct *dev)
old_mode = dev->mode;
for (loop = 0; loop < 8; loop++) {
/* Attempt to use EPP for Test Unit Ready */
- if ((ppb & 0x0007) == 0x0000)
- dev->mode = PPA_EPP_32;
+ if (autodetect && (ppb & 0x0007) == 0x0000)
+ dev->mode = PPA_EPP_8;
second_pass:
ppa_connect(dev, CONNECT_EPP_MAYBE);
@@ -930,7 +944,7 @@ second_pass:
udelay(1000);
ppa_disconnect(dev);
udelay(1000);
- if (dev->mode == PPA_EPP_32) {
+ if (dev->mode != old_mode) {
dev->mode = old_mode;
goto second_pass;
}
@@ -953,7 +967,7 @@ second_pass:
udelay(1000);
ppa_disconnect(dev);
udelay(1000);
- if (dev->mode == PPA_EPP_32) {
+ if (dev->mode != old_mode) {
dev->mode = old_mode;
goto second_pass;
}
@@ -972,28 +986,20 @@ second_pass:
return -ENODEV;
}
-static int ppa_adjust_queue(struct scsi_device *device)
-{
- blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH);
- return 0;
-}
-
-static struct scsi_host_template ppa_template = {
+static const struct scsi_host_template ppa_template = {
.module = THIS_MODULE,
.proc_name = "ppa",
- .proc_info = ppa_proc_info,
+ .show_info = ppa_show_info,
+ .write_info = ppa_write_info,
.name = "Iomega VPI0 (ppa) interface",
.queuecommand = ppa_queuecommand,
.eh_abort_handler = ppa_abort,
- .eh_bus_reset_handler = ppa_reset,
.eh_host_reset_handler = ppa_reset,
.bios_param = ppa_biosparam,
.this_id = -1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
.can_queue = 1,
- .slave_alloc = ppa_adjust_queue,
+ .cmd_size = sizeof(struct scsi_pointer),
};
/***************************************************************************
@@ -1002,25 +1008,56 @@ static struct scsi_host_template ppa_template = {
static LIST_HEAD(ppa_hosts);
+/*
+ * Finds the first available device number that can be alloted to the
+ * new ppa device and returns the address of the previous node so that
+ * we can add to the tail and have a list in the ascending order.
+ */
+
+static inline ppa_struct *find_parent(void)
+{
+ ppa_struct *dev, *par = NULL;
+ unsigned int cnt = 0;
+
+ if (list_empty(&ppa_hosts))
+ return NULL;
+
+ list_for_each_entry(dev, &ppa_hosts, list) {
+ if (dev->dev_no != cnt)
+ return par;
+ cnt++;
+ par = dev;
+ }
+
+ return par;
+}
+
static int __ppa_attach(struct parport *pb)
{
struct Scsi_Host *host;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waiting);
DEFINE_WAIT(wait);
- ppa_struct *dev;
+ ppa_struct *dev, *temp;
int ports;
- int modes, ppb, ppb_hi;
int err = -ENOMEM;
+ struct pardev_cb ppa_cb;
dev = kzalloc(sizeof(ppa_struct), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->base = -1;
- dev->mode = PPA_AUTODETECT;
+ dev->mode = mode < PPA_UNKNOWN ? mode : PPA_AUTODETECT;
dev->recon_tmo = PPA_RECON_TMO;
init_waitqueue_head(&waiting);
- dev->dev = parport_register_device(pb, "ppa", NULL, ppa_wakeup,
- NULL, 0, dev);
+ temp = find_parent();
+ if (temp)
+ dev->dev_no = temp->dev_no + 1;
+
+ memset(&ppa_cb, 0, sizeof(ppa_cb));
+ ppa_cb.private = dev;
+ ppa_cb.wakeup = ppa_wakeup;
+
+ dev->dev = parport_register_dev_model(pb, "ppa", &ppa_cb, dev->dev_no);
if (!dev->dev)
goto out;
@@ -1044,25 +1081,8 @@ static int __ppa_attach(struct parport *pb)
}
dev->waiting = NULL;
finish_wait(&waiting, &wait);
- ppb = dev->base = dev->dev->port->base;
- ppb_hi = dev->dev->port->base_hi;
- w_ctr(ppb, 0x0c);
- modes = dev->dev->port->modes;
-
- /* Mode detection works up the chain of speed
- * This avoids a nasty if-then-else-if-... tree
- */
- dev->mode = PPA_NIBBLE;
-
- if (modes & PARPORT_MODE_TRISTATE)
- dev->mode = PPA_PS2;
-
- if (modes & PARPORT_MODE_ECP) {
- w_ecr(ppb_hi, 0x20);
- dev->mode = PPA_PS2;
- }
- if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP))
- w_ecr(ppb_hi, 0x80);
+ dev->base = dev->dev->port->base;
+ w_ctr(dev->base, 0x0c);
/* Done configuration */
@@ -1127,22 +1147,11 @@ static void ppa_detach(struct parport *pb)
}
static struct parport_driver ppa_driver = {
- .name = "ppa",
- .attach = ppa_attach,
- .detach = ppa_detach,
+ .name = "ppa",
+ .match_port = ppa_attach,
+ .detach = ppa_detach,
};
+module_parport_driver(ppa_driver);
-static int __init ppa_driver_init(void)
-{
- printk(KERN_INFO "ppa: Version %s\n", PPA_VERSION);
- return parport_register_driver(&ppa_driver);
-}
-
-static void __exit ppa_driver_exit(void)
-{
- parport_unregister_driver(&ppa_driver);
-}
-
-module_init(ppa_driver_init);
-module_exit(ppa_driver_exit);
+MODULE_DESCRIPTION("IOMEGA PPA3 parallel port SCSI host adapter driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h
index ba8021427b88..098bcf7b9eb4 100644
--- a/drivers/scsi/ppa.h
+++ b/drivers/scsi/ppa.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Driver for the PPA3 parallel port SCSI HBA embedded in
* the Iomega ZIP drive
*
@@ -106,11 +107,7 @@ static char *PPA_MODE_STRING[] =
"PS/2",
"EPP 8 bit",
"EPP 16 bit",
-#ifdef CONFIG_SCSI_IZIP_EPP16
- "EPP 16 bit",
-#else
"EPP 32 bit",
-#endif
"Unknown"};
/* other options */
diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c
index cd178b9e40cd..92fe5c5c5bb0 100644
--- a/drivers/scsi/ps3rom.c
+++ b/drivers/scsi/ps3rom.c
@@ -1,25 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PS3 BD/DVD/CD-ROM Storage Driver
*
* Copyright (C) 2007 Sony Computer Entertainment Inc.
* Copyright 2007 Sony Corp.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published
- * by the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/cdrom.h>
#include <linux/highmem.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <scsi/scsi.h>
@@ -72,12 +61,13 @@ enum lv1_atapi_in_out {
};
-static int ps3rom_slave_configure(struct scsi_device *scsi_dev)
+static int ps3rom_sdev_configure(struct scsi_device *scsi_dev,
+ struct queue_limits *lim)
{
struct ps3rom_private *priv = shost_priv(scsi_dev->host);
struct ps3_storage_device *dev = priv->dev;
- dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %u, channel %u\n", __func__,
+ dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %llu, channel %u\n", __func__,
__LINE__, scsi_dev->id, scsi_dev->lun, scsi_dev->channel);
/*
@@ -211,20 +201,14 @@ static int ps3rom_write_request(struct ps3_storage_device *dev,
return 0;
}
-static int ps3rom_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
+static int ps3rom_queuecommand_lck(struct scsi_cmnd *cmd)
{
struct ps3rom_private *priv = shost_priv(cmd->device->host);
struct ps3_storage_device *dev = priv->dev;
unsigned char opcode;
int res;
-#ifdef DEBUG
- scsi_print_command(cmd);
-#endif
-
priv->curr_cmd = cmd;
- cmd->scsi_done = done;
opcode = cmd->cmnd[0];
/*
@@ -249,12 +233,10 @@ static int ps3rom_queuecommand_lck(struct scsi_cmnd *cmd,
}
if (res) {
- memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ scsi_build_sense(cmd, 0, ILLEGAL_REQUEST, 0, 0);
cmd->result = res;
- cmd->sense_buffer[0] = 0x70;
- cmd->sense_buffer[2] = ILLEGAL_REQUEST;
priv->curr_cmd = NULL;
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
}
return 0;
@@ -334,31 +316,28 @@ static irqreturn_t ps3rom_interrupt(int irq, void *data)
goto done;
}
- scsi_build_sense_buffer(0, cmd->sense_buffer, sense_key, asc, ascq);
- cmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense(cmd, 0, sense_key, asc, ascq);
done:
priv->curr_cmd = NULL;
- cmd->scsi_done(cmd);
+ scsi_done(cmd);
return IRQ_HANDLED;
}
-static struct scsi_host_template ps3rom_host_template = {
+static const struct scsi_host_template ps3rom_host_template = {
.name = DEVICE_NAME,
- .slave_configure = ps3rom_slave_configure,
+ .sdev_configure = ps3rom_sdev_configure,
.queuecommand = ps3rom_queuecommand,
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.emulated = 1, /* only sg driver uses this */
.max_sectors = PS3ROM_MAX_SECTORS,
- .use_clustering = ENABLE_CLUSTERING,
.module = THIS_MODULE,
};
-static int __devinit ps3rom_probe(struct ps3_system_bus_device *_dev)
+static int ps3rom_probe(struct ps3_system_bus_device *_dev)
{
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
int error;
@@ -386,6 +365,7 @@ static int __devinit ps3rom_probe(struct ps3_system_bus_device *_dev)
if (!host) {
dev_err(&dev->sbd.core, "%s:%u: scsi_host_alloc failed\n",
__func__, __LINE__);
+ error = -ENOMEM;
goto fail_teardown;
}
@@ -418,7 +398,7 @@ fail_free_bounce:
return error;
}
-static int ps3rom_remove(struct ps3_system_bus_device *_dev)
+static void ps3rom_remove(struct ps3_system_bus_device *_dev)
{
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
struct Scsi_Host *host = ps3_system_bus_get_drvdata(&dev->sbd);
@@ -428,7 +408,6 @@ static int ps3rom_remove(struct ps3_system_bus_device *_dev)
scsi_host_put(host);
ps3_system_bus_set_drvdata(&dev->sbd, NULL);
kfree(dev->bounce_buf);
- return 0;
}
static struct ps3_system_bus_driver ps3rom = {
diff --git a/drivers/scsi/qedf/Kconfig b/drivers/scsi/qedf/Kconfig
new file mode 100644
index 000000000000..eb81a1b0374a
--- /dev/null
+++ b/drivers/scsi/qedf/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config QEDF
+ tristate "QLogic QEDF 25/40/100Gb FCoE Initiator Driver Support"
+ depends on PCI && SCSI
+ depends on QED
+ depends on LIBFC
+ depends on LIBFCOE
+ select QED_LL2
+ select QED_FCOE
+ help
+ This driver supports FCoE offload for the QLogic FastLinQ
+ 41000 Series Converged Network Adapters.
diff --git a/drivers/scsi/qedf/Makefile b/drivers/scsi/qedf/Makefile
new file mode 100644
index 000000000000..c46287826fb8
--- /dev/null
+++ b/drivers/scsi/qedf/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_QEDF) := qedf.o
+qedf-y = qedf_dbg.o qedf_main.o qedf_io.o qedf_fip.o \
+ qedf_attr.o qedf_els.o drv_scsi_fw_funcs.o drv_fcoe_fw_funcs.o
+
+qedf-$(CONFIG_DEBUG_FS) += qedf_debugfs.o
diff --git a/drivers/scsi/qedf/drv_fcoe_fw_funcs.c b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c
new file mode 100644
index 000000000000..e8bc8d9e4583
--- /dev/null
+++ b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include "drv_fcoe_fw_funcs.h"
+#include "drv_scsi_fw_funcs.h"
+
+#define FCOE_RX_ID (0xFFFFu)
+
+static inline void init_common_sqe(struct fcoe_task_params *task_params,
+ enum fcoe_sqe_request_type request_type)
+{
+ memset(task_params->sqe, 0, sizeof(*(task_params->sqe)));
+ SET_FIELD(task_params->sqe->flags, FCOE_WQE_REQ_TYPE,
+ request_type);
+ task_params->sqe->task_id = task_params->itid;
+}
+
+int init_initiator_rw_fcoe_task(struct fcoe_task_params *task_params,
+ struct scsi_sgl_task_params *sgl_task_params,
+ struct regpair sense_data_buffer_phys_addr,
+ u32 task_retry_id,
+ u8 fcp_cmd_payload[32])
+{
+ struct fcoe_task_context *ctx = task_params->context;
+ const u8 val_byte = ctx->ystorm_ag_context.byte0;
+ struct ustorm_fcoe_task_ag_ctx *u_ag_ctx;
+ struct ystorm_fcoe_task_st_ctx *y_st_ctx;
+ struct tstorm_fcoe_task_st_ctx *t_st_ctx;
+ struct mstorm_fcoe_task_st_ctx *m_st_ctx;
+ u32 io_size, val;
+ bool slow_sgl;
+
+ memset(ctx, 0, sizeof(*(ctx)));
+ ctx->ystorm_ag_context.byte0 = val_byte;
+ slow_sgl = scsi_is_slow_sgl(sgl_task_params->num_sges,
+ sgl_task_params->small_mid_sge);
+ io_size = (task_params->task_type == FCOE_TASK_TYPE_WRITE_INITIATOR ?
+ task_params->tx_io_size : task_params->rx_io_size);
+
+ /* Ystorm ctx */
+ y_st_ctx = &ctx->ystorm_st_context;
+ y_st_ctx->data_2_trns_rem = cpu_to_le32(io_size);
+ y_st_ctx->task_rety_identifier = cpu_to_le32(task_retry_id);
+ y_st_ctx->task_type = (u8)task_params->task_type;
+ memcpy(&y_st_ctx->tx_info_union.fcp_cmd_payload,
+ fcp_cmd_payload, sizeof(struct fcoe_fcp_cmd_payload));
+
+ /* Tstorm ctx */
+ t_st_ctx = &ctx->tstorm_st_context;
+ t_st_ctx->read_only.dev_type = (u8)(task_params->is_tape_device == 1 ?
+ FCOE_TASK_DEV_TYPE_TAPE :
+ FCOE_TASK_DEV_TYPE_DISK);
+ t_st_ctx->read_only.cid = cpu_to_le32(task_params->conn_cid);
+ val = cpu_to_le32(task_params->cq_rss_number);
+ t_st_ctx->read_only.glbl_q_num = val;
+ t_st_ctx->read_only.fcp_cmd_trns_size = cpu_to_le32(io_size);
+ t_st_ctx->read_only.task_type = (u8)task_params->task_type;
+ SET_FIELD(t_st_ctx->read_write.flags,
+ FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, 1);
+ t_st_ctx->read_write.rx_id = cpu_to_le16(FCOE_RX_ID);
+
+ /* Ustorm ctx */
+ u_ag_ctx = &ctx->ustorm_ag_context;
+ u_ag_ctx->global_cq_num = cpu_to_le32(task_params->cq_rss_number);
+
+ /* Mstorm buffer for sense/rsp data placement */
+ m_st_ctx = &ctx->mstorm_st_context;
+ val = cpu_to_le32(sense_data_buffer_phys_addr.hi);
+ m_st_ctx->rsp_buf_addr.hi = val;
+ val = cpu_to_le32(sense_data_buffer_phys_addr.lo);
+ m_st_ctx->rsp_buf_addr.lo = val;
+
+ if (task_params->task_type == FCOE_TASK_TYPE_WRITE_INITIATOR) {
+ /* Ystorm ctx */
+ y_st_ctx->expect_first_xfer = 1;
+
+ /* Set the amount of super SGEs. Can be up to 4. */
+ SET_FIELD(y_st_ctx->sgl_mode,
+ YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE,
+ (slow_sgl ? SCSI_TX_SLOW_SGL : SCSI_FAST_SGL));
+ init_scsi_sgl_context(&y_st_ctx->sgl_params,
+ &y_st_ctx->data_desc,
+ sgl_task_params);
+
+ /* Mstorm ctx */
+ SET_FIELD(m_st_ctx->flags,
+ MSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE,
+ (slow_sgl ? SCSI_TX_SLOW_SGL : SCSI_FAST_SGL));
+ m_st_ctx->sgl_params.sgl_num_sges =
+ cpu_to_le16(sgl_task_params->num_sges);
+ } else {
+ /* Tstorm ctx */
+ SET_FIELD(t_st_ctx->read_write.flags,
+ FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RX_SGL_MODE,
+ (slow_sgl ? SCSI_TX_SLOW_SGL : SCSI_FAST_SGL));
+
+ /* Mstorm ctx */
+ m_st_ctx->data_2_trns_rem = cpu_to_le32(io_size);
+ init_scsi_sgl_context(&m_st_ctx->sgl_params,
+ &m_st_ctx->data_desc,
+ sgl_task_params);
+ }
+
+ /* Init Sqe */
+ init_common_sqe(task_params, SEND_FCOE_CMD);
+
+ return 0;
+}
+
+int init_initiator_midpath_unsolicited_fcoe_task(
+ struct fcoe_task_params *task_params,
+ struct fcoe_tx_mid_path_params *mid_path_fc_header,
+ struct scsi_sgl_task_params *tx_sgl_task_params,
+ struct scsi_sgl_task_params *rx_sgl_task_params,
+ u8 fw_to_place_fc_header)
+{
+ struct fcoe_task_context *ctx = task_params->context;
+ const u8 val_byte = ctx->ystorm_ag_context.byte0;
+ struct ustorm_fcoe_task_ag_ctx *u_ag_ctx;
+ struct ystorm_fcoe_task_st_ctx *y_st_ctx;
+ struct tstorm_fcoe_task_st_ctx *t_st_ctx;
+ struct mstorm_fcoe_task_st_ctx *m_st_ctx;
+ u32 val;
+
+ memset(ctx, 0, sizeof(*(ctx)));
+ ctx->ystorm_ag_context.byte0 = val_byte;
+
+ /* Init Ystorm */
+ y_st_ctx = &ctx->ystorm_st_context;
+ init_scsi_sgl_context(&y_st_ctx->sgl_params,
+ &y_st_ctx->data_desc,
+ tx_sgl_task_params);
+ SET_FIELD(y_st_ctx->sgl_mode,
+ YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, SCSI_FAST_SGL);
+ y_st_ctx->data_2_trns_rem = cpu_to_le32(task_params->tx_io_size);
+ y_st_ctx->task_type = (u8)task_params->task_type;
+ memcpy(&y_st_ctx->tx_info_union.tx_params.mid_path,
+ mid_path_fc_header, sizeof(struct fcoe_tx_mid_path_params));
+
+ /* Init Mstorm */
+ m_st_ctx = &ctx->mstorm_st_context;
+ init_scsi_sgl_context(&m_st_ctx->sgl_params,
+ &m_st_ctx->data_desc,
+ rx_sgl_task_params);
+ SET_FIELD(m_st_ctx->flags,
+ MSTORM_FCOE_TASK_ST_CTX_MP_INCLUDE_FC_HEADER,
+ fw_to_place_fc_header);
+ m_st_ctx->data_2_trns_rem = cpu_to_le32(task_params->rx_io_size);
+
+ /* Init Tstorm */
+ t_st_ctx = &ctx->tstorm_st_context;
+ t_st_ctx->read_only.cid = cpu_to_le32(task_params->conn_cid);
+ val = cpu_to_le32(task_params->cq_rss_number);
+ t_st_ctx->read_only.glbl_q_num = val;
+ t_st_ctx->read_only.task_type = (u8)task_params->task_type;
+ SET_FIELD(t_st_ctx->read_write.flags,
+ FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, 1);
+ t_st_ctx->read_write.rx_id = cpu_to_le16(FCOE_RX_ID);
+
+ /* Init Ustorm */
+ u_ag_ctx = &ctx->ustorm_ag_context;
+ u_ag_ctx->global_cq_num = cpu_to_le32(task_params->cq_rss_number);
+
+ /* Init SQE */
+ init_common_sqe(task_params, SEND_FCOE_MIDPATH);
+ task_params->sqe->additional_info_union.burst_length =
+ tx_sgl_task_params->total_buffer_size;
+ SET_FIELD(task_params->sqe->flags,
+ FCOE_WQE_NUM_SGES, tx_sgl_task_params->num_sges);
+ SET_FIELD(task_params->sqe->flags, FCOE_WQE_SGL_MODE,
+ SCSI_FAST_SGL);
+
+ return 0;
+}
+
+int init_initiator_abort_fcoe_task(struct fcoe_task_params *task_params)
+{
+ init_common_sqe(task_params, SEND_FCOE_ABTS_REQUEST);
+ return 0;
+}
+
+int init_initiator_cleanup_fcoe_task(struct fcoe_task_params *task_params)
+{
+ init_common_sqe(task_params, FCOE_EXCHANGE_CLEANUP);
+ return 0;
+}
+
+int init_initiator_sequence_recovery_fcoe_task(
+ struct fcoe_task_params *task_params, u32 desired_offset)
+{
+ init_common_sqe(task_params, FCOE_SEQUENCE_RECOVERY);
+ task_params->sqe->additional_info_union.seq_rec_updated_offset =
+ desired_offset;
+ return 0;
+}
diff --git a/drivers/scsi/qedf/drv_fcoe_fw_funcs.h b/drivers/scsi/qedf/drv_fcoe_fw_funcs.h
new file mode 100644
index 000000000000..7125e484bf93
--- /dev/null
+++ b/drivers/scsi/qedf/drv_fcoe_fw_funcs.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#ifndef _FCOE_FW_FUNCS_H
+#define _FCOE_FW_FUNCS_H
+#include "drv_scsi_fw_funcs.h"
+#include "qedf_hsi.h"
+#include <linux/qed/qed_if.h>
+
+struct fcoe_task_params {
+ /* Output parameter [set/filled by the HSI function] */
+ struct fcoe_task_context *context;
+
+ /* Output parameter [set/filled by the HSI function] */
+ struct fcoe_wqe *sqe;
+ enum fcoe_task_type task_type;
+ u32 tx_io_size; /* in bytes */
+ u32 rx_io_size; /* in bytes */
+ u32 conn_cid;
+ u16 itid;
+ u8 cq_rss_number;
+
+ /* Whether it's Tape device or not (0=Disk, 1=Tape) */
+ u8 is_tape_device;
+};
+
+/**
+ * @brief init_initiator_rw_fcoe_task - Initializes FCoE task context for
+ * read/write task types and init fcoe_sqe
+ *
+ * @param task_params - Pointer to task parameters struct
+ * @param sgl_task_params - Pointer to SGL task params
+ * @param sense_data_buffer_phys_addr - Pointer to sense data buffer
+ * @param task_retry_id - retry identification - Used only for Tape device
+ * @param fcp_cmnd_payload - FCP CMD Payload
+ */
+int init_initiator_rw_fcoe_task(struct fcoe_task_params *task_params,
+ struct scsi_sgl_task_params *sgl_task_params,
+ struct regpair sense_data_buffer_phys_addr,
+ u32 task_retry_id,
+ u8 fcp_cmd_payload[32]);
+
+/**
+ * @brief init_initiator_midpath_fcoe_task - Initializes FCoE task context for
+ * midpath/unsolicited task types and init fcoe_sqe
+ *
+ * @param task_params - Pointer to task parameters struct
+ * @param mid_path_fc_header - FC header
+ * @param tx_sgl_task_params - Pointer to Tx SGL task params
+ * @param rx_sgl_task_params - Pointer to Rx SGL task params
+ * @param fw_to_place_fc_header - Indication if the FW will place the FC header
+ * in addition to the data arrives.
+ */
+int init_initiator_midpath_unsolicited_fcoe_task(
+ struct fcoe_task_params *task_params,
+ struct fcoe_tx_mid_path_params *mid_path_fc_header,
+ struct scsi_sgl_task_params *tx_sgl_task_params,
+ struct scsi_sgl_task_params *rx_sgl_task_params,
+ u8 fw_to_place_fc_header);
+
+/**
+ * @brief init_initiator_abort_fcoe_task - Initializes FCoE task context for
+ * abort task types and init fcoe_sqe
+ *
+ * @param task_params - Pointer to task parameters struct
+ */
+int init_initiator_abort_fcoe_task(struct fcoe_task_params *task_params);
+
+/**
+ * @brief init_initiator_cleanup_fcoe_task - Initializes FCoE task context for
+ * cleanup task types and init fcoe_sqe
+ *
+ *
+ * @param task_params - Pointer to task parameters struct
+ */
+int init_initiator_cleanup_fcoe_task(struct fcoe_task_params *task_params);
+
+/**
+ * @brief init_initiator_cleanup_fcoe_task - Initializes FCoE task context for
+ * sequence recovery task types and init fcoe_sqe
+ *
+ *
+ * @param task_params - Pointer to task parameters struct
+ * @param desired_offset - The desired offest the task will be re-sent from
+ */
+int init_initiator_sequence_recovery_fcoe_task(
+ struct fcoe_task_params *task_params,
+ u32 desired_offset);
+#endif
diff --git a/drivers/scsi/qedf/drv_scsi_fw_funcs.c b/drivers/scsi/qedf/drv_scsi_fw_funcs.c
new file mode 100644
index 000000000000..3289b71031d6
--- /dev/null
+++ b/drivers/scsi/qedf/drv_scsi_fw_funcs.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include "drv_scsi_fw_funcs.h"
+
+#define SCSI_NUM_SGES_IN_CACHE 0x4
+
+bool scsi_is_slow_sgl(u16 num_sges, bool small_mid_sge)
+{
+ return (num_sges > SCSI_NUM_SGES_SLOW_SGL_THR && small_mid_sge);
+}
+
+void init_scsi_sgl_context(struct scsi_sgl_params *ctx_sgl_params,
+ struct scsi_cached_sges *ctx_data_desc,
+ struct scsi_sgl_task_params *sgl_task_params)
+{
+ /* no need to check for sgl_task_params->sgl validity */
+ u8 num_sges_to_init = sgl_task_params->num_sges >
+ SCSI_NUM_SGES_IN_CACHE ? SCSI_NUM_SGES_IN_CACHE :
+ sgl_task_params->num_sges;
+ u8 sge_index;
+ u32 val;
+
+ val = cpu_to_le32(sgl_task_params->sgl_phys_addr.lo);
+ ctx_sgl_params->sgl_addr.lo = val;
+ val = cpu_to_le32(sgl_task_params->sgl_phys_addr.hi);
+ ctx_sgl_params->sgl_addr.hi = val;
+ val = cpu_to_le32(sgl_task_params->total_buffer_size);
+ ctx_sgl_params->sgl_total_length = val;
+ ctx_sgl_params->sgl_num_sges = cpu_to_le16(sgl_task_params->num_sges);
+
+ for (sge_index = 0; sge_index < num_sges_to_init; sge_index++) {
+ val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_addr.lo);
+ ctx_data_desc->sge[sge_index].sge_addr.lo = val;
+ val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_addr.hi);
+ ctx_data_desc->sge[sge_index].sge_addr.hi = val;
+ val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_len);
+ ctx_data_desc->sge[sge_index].sge_len = val;
+ }
+}
diff --git a/drivers/scsi/qedf/drv_scsi_fw_funcs.h b/drivers/scsi/qedf/drv_scsi_fw_funcs.h
new file mode 100644
index 000000000000..6195f13dece8
--- /dev/null
+++ b/drivers/scsi/qedf/drv_scsi_fw_funcs.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#ifndef _SCSI_FW_FUNCS_H
+#define _SCSI_FW_FUNCS_H
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/storage_common.h>
+#include <linux/qed/fcoe_common.h>
+
+struct scsi_sgl_task_params {
+ struct scsi_sge *sgl;
+ struct regpair sgl_phys_addr;
+ u32 total_buffer_size;
+ u16 num_sges;
+
+ /* true if SGL contains a small (< 4KB) SGE in middle(not 1st or last)
+ * -> relevant for tx only
+ */
+ bool small_mid_sge;
+};
+
+struct scsi_dif_task_params {
+ u32 initial_ref_tag;
+ bool initial_ref_tag_is_valid;
+ u16 application_tag;
+ u16 application_tag_mask;
+ u16 dif_block_size_log;
+ bool dif_on_network;
+ bool dif_on_host;
+ u8 host_guard_type;
+ u8 protection_type;
+ u8 ref_tag_mask;
+ bool crc_seed;
+
+ /* Enable Connection error upon DIF error (segments with DIF errors are
+ * dropped)
+ */
+ bool tx_dif_conn_err_en;
+ bool ignore_app_tag;
+ bool keep_ref_tag_const;
+ bool validate_guard;
+ bool validate_app_tag;
+ bool validate_ref_tag;
+ bool forward_guard;
+ bool forward_app_tag;
+ bool forward_ref_tag;
+ bool forward_app_tag_with_mask;
+ bool forward_ref_tag_with_mask;
+};
+
+struct scsi_initiator_cmd_params {
+ /* for cdb_size > default CDB size (extended CDB > 16 bytes) ->
+ * pointer to the CDB buffer SGE
+ */
+ struct scsi_sge extended_cdb_sge;
+
+ /* Physical address of sense data buffer for sense data - 256B buffer */
+ struct regpair sense_data_buffer_phys_addr;
+};
+
+/**
+ * @brief scsi_is_slow_sgl - checks for slow SGL
+ *
+ * @param num_sges - number of sges in SGL
+ * @param small_mid_sge - True is the SGL contains an SGE which is smaller than
+ * 4KB and its not the 1st or last SGE in the SGL
+ */
+bool scsi_is_slow_sgl(u16 num_sges, bool small_mid_sge);
+
+/**
+ * @brief init_scsi_sgl_context - initializes SGL task context
+ *
+ * @param sgl_params - SGL context parameters to initialize (output parameter)
+ * @param data_desc - context struct containing SGEs array to set (output
+ * parameter)
+ * @param sgl_task_params - SGL parameters (input)
+ */
+void init_scsi_sgl_context(struct scsi_sgl_params *sgl_params,
+ struct scsi_cached_sges *ctx_data_desc,
+ struct scsi_sgl_task_params *sgl_task_params);
+#endif
diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h
new file mode 100644
index 000000000000..98afdfe63600
--- /dev/null
+++ b/drivers/scsi/qedf/qedf.h
@@ -0,0 +1,605 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#ifndef _QEDFC_H_
+#define _QEDFC_H_
+
+#include <scsi/libfcoe.h>
+#include <scsi/libfc.h>
+#include <scsi/fc/fc_fip.h>
+#include <scsi/fc/fc_fc2.h>
+#include <scsi/scsi_tcq.h>
+
+/* qedf_hsi.h needs to before included any qed includes */
+#include "qedf_hsi.h"
+
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_fcoe_if.h>
+#include <linux/qed/qed_ll2_if.h>
+#include "qedf_version.h"
+#include "qedf_dbg.h"
+#include "drv_fcoe_fw_funcs.h"
+
+/* Helpers to extract upper and lower 32-bits of pointer */
+#define U64_HI(val) ((u32)(((u64)(val)) >> 32))
+#define U64_LO(val) ((u32)(((u64)(val)) & 0xffffffff))
+
+#define QEDF_DESCR "QLogic FCoE Offload Driver"
+#define QEDF_MODULE_NAME "qedf"
+
+#define QEDF_FLOGI_RETRY_CNT 3
+#define QEDF_RPORT_RETRY_CNT 255
+#define QEDF_MAX_SESSIONS 1024
+#define QEDF_MAX_PAYLOAD 2048
+#define QEDF_MAX_BDS_PER_CMD 256
+#define QEDF_MAX_BD_LEN 0xffff
+#define QEDF_BD_SPLIT_SZ 0x1000
+#define QEDF_PAGE_SIZE 4096
+#define QED_HW_DMA_BOUNDARY 0xfff
+#define QEDF_MAX_SGLEN_FOR_CACHESGL ((1U << 16) - 1)
+#define QEDF_MFS (QEDF_MAX_PAYLOAD + \
+ sizeof(struct fc_frame_header))
+#define QEDF_MAX_NPIV 64
+#define QEDF_TM_TIMEOUT 10
+#define QEDF_ABORT_TIMEOUT (10 * 1000)
+#define QEDF_CLEANUP_TIMEOUT 1
+#define QEDF_MAX_CDB_LEN 16
+#define QEDF_LL2_BUF_SIZE 2500 /* Buffer size required for LL2 Rx */
+
+#define UPSTREAM_REMOVE 1
+#define UPSTREAM_KEEP 1
+
+struct qedf_mp_req {
+ uint32_t req_len;
+ void *req_buf;
+ dma_addr_t req_buf_dma;
+ struct scsi_sge *mp_req_bd;
+ dma_addr_t mp_req_bd_dma;
+ struct fc_frame_header req_fc_hdr;
+
+ uint32_t resp_len;
+ void *resp_buf;
+ dma_addr_t resp_buf_dma;
+ struct scsi_sge *mp_resp_bd;
+ dma_addr_t mp_resp_bd_dma;
+ struct fc_frame_header resp_fc_hdr;
+};
+
+struct qedf_els_cb_arg {
+ struct qedf_ioreq *aborted_io_req;
+ struct qedf_ioreq *io_req;
+ u8 op; /* Used to keep track of ELS op */
+ uint16_t l2_oxid;
+ u32 offset; /* Used for sequence cleanup */
+ u8 r_ctl; /* Used for sequence cleanup */
+};
+
+enum qedf_ioreq_event {
+ QEDF_IOREQ_EV_NONE,
+ QEDF_IOREQ_EV_ABORT_SUCCESS,
+ QEDF_IOREQ_EV_ABORT_FAILED,
+ QEDF_IOREQ_EV_SEND_RRQ,
+ QEDF_IOREQ_EV_ELS_TMO,
+ QEDF_IOREQ_EV_ELS_ERR_DETECT,
+ QEDF_IOREQ_EV_ELS_FLUSH,
+ QEDF_IOREQ_EV_CLEANUP_SUCCESS,
+ QEDF_IOREQ_EV_CLEANUP_FAILED,
+};
+
+#define FC_GOOD 0
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER (0x1<<2)
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER (0x1<<3)
+#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID (0x1<<0)
+#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID (0x1<<1)
+struct qedf_ioreq {
+ struct list_head link;
+ uint16_t xid;
+ struct scsi_cmnd *sc_cmd;
+#define QEDF_SCSI_CMD 1
+#define QEDF_TASK_MGMT_CMD 2
+#define QEDF_ABTS 3
+#define QEDF_ELS 4
+#define QEDF_CLEANUP 5
+#define QEDF_SEQ_CLEANUP 6
+ u8 cmd_type;
+#define QEDF_CMD_OUTSTANDING 0x0
+#define QEDF_CMD_IN_ABORT 0x1
+#define QEDF_CMD_IN_CLEANUP 0x2
+#define QEDF_CMD_SRR_SENT 0x3
+#define QEDF_CMD_DIRTY 0x4
+#define QEDF_CMD_ERR_SCSI_DONE 0x5
+ u8 io_req_flags;
+ uint8_t tm_flags;
+ u64 tm_lun;
+ struct qedf_rport *fcport;
+#define QEDF_CMD_ST_INACTIVE 0
+#define QEDFC_CMD_ST_IO_ACTIVE 1
+#define QEDFC_CMD_ST_ABORT_ACTIVE 2
+#define QEDFC_CMD_ST_ABORT_ACTIVE_EH 3
+#define QEDFC_CMD_ST_CLEANUP_ACTIVE 4
+#define QEDFC_CMD_ST_CLEANUP_ACTIVE_EH 5
+#define QEDFC_CMD_ST_RRQ_ACTIVE 6
+#define QEDFC_CMD_ST_RRQ_WAIT 7
+#define QEDFC_CMD_ST_OXID_RETIRE_WAIT 8
+#define QEDFC_CMD_ST_TMF_ACTIVE 9
+#define QEDFC_CMD_ST_DRAIN_ACTIVE 10
+#define QEDFC_CMD_ST_CLEANED 11
+#define QEDFC_CMD_ST_ELS_ACTIVE 12
+ atomic_t state;
+ unsigned long flags;
+ enum qedf_ioreq_event event;
+ size_t data_xfer_len;
+ /* ID: 001: Alloc cmd (qedf_alloc_cmd) */
+ /* ID: 002: Initiate ABTS (qedf_initiate_abts) */
+ /* ID: 003: For RRQ (qedf_process_abts_compl) */
+ struct kref refcount;
+ struct qedf_cmd_mgr *cmd_mgr;
+ struct io_bdt *bd_tbl;
+ struct delayed_work timeout_work;
+ struct completion tm_done;
+ struct completion abts_done;
+ struct completion cleanup_done;
+ struct fcoe_task_context *task;
+ struct fcoe_task_params *task_params;
+ struct scsi_sgl_task_params *sgl_task_params;
+ int idx;
+ int lun;
+/*
+ * Need to allocate enough room for both sense data and FCP response data
+ * which has a max length of 8 bytes according to spec.
+ */
+#define QEDF_SCSI_SENSE_BUFFERSIZE (SCSI_SENSE_BUFFERSIZE + 8)
+ uint8_t *sense_buffer;
+ dma_addr_t sense_buffer_dma;
+ u32 fcp_resid;
+ u32 fcp_rsp_len;
+ u32 fcp_sns_len;
+ u8 cdb_status;
+ u8 fcp_status;
+ u8 fcp_rsp_code;
+ u8 scsi_comp_flags;
+#define QEDF_MAX_REUSE 0xfff
+ u16 reuse_count;
+ struct qedf_mp_req mp_req;
+ void (*cb_func)(struct qedf_els_cb_arg *cb_arg);
+ struct qedf_els_cb_arg *cb_arg;
+ int fp_idx;
+ unsigned int cpu;
+ unsigned int int_cpu;
+#define QEDF_IOREQ_UNKNOWN_SGE 1
+#define QEDF_IOREQ_SLOW_SGE 2
+#define QEDF_IOREQ_FAST_SGE 3
+ u8 sge_type;
+ struct delayed_work rrq_work;
+
+ /* Used for sequence level recovery; i.e. REC/SRR */
+ uint32_t rx_buf_off;
+ uint32_t tx_buf_off;
+ uint32_t rx_id;
+ uint32_t task_retry_identifier;
+
+ /*
+ * Used to tell if we need to return a SCSI command
+ * during some form of error processing.
+ */
+ bool return_scsi_cmd_on_abts;
+
+ unsigned int alloc;
+};
+
+struct qedf_cmd_priv {
+ struct qedf_ioreq *io_req;
+};
+
+static inline struct qedf_cmd_priv *qedf_priv(struct scsi_cmnd *cmd)
+{
+ return scsi_cmd_priv(cmd);
+}
+
+extern struct workqueue_struct *qedf_io_wq;
+
+struct qedf_rport {
+ spinlock_t rport_lock;
+#define QEDF_RPORT_SESSION_READY 1
+#define QEDF_RPORT_UPLOADING_CONNECTION 2
+#define QEDF_RPORT_IN_RESET 3
+#define QEDF_RPORT_IN_LUN_RESET 4
+#define QEDF_RPORT_IN_TARGET_RESET 5
+ unsigned long flags;
+ int lun_reset_lun;
+ unsigned long retry_delay_timestamp;
+ struct fc_rport *rport;
+ struct fc_rport_priv *rdata;
+ struct qedf_ctx *qedf;
+ u32 handle; /* Handle from qed */
+ u32 fw_cid; /* fw_cid from qed */
+ void __iomem *p_doorbell;
+ /* Send queue management */
+ atomic_t free_sqes;
+ atomic_t ios_to_queue;
+ atomic_t num_active_ios;
+ struct fcoe_wqe *sq;
+ dma_addr_t sq_dma;
+ u16 sq_prod_idx;
+ u16 fw_sq_prod_idx;
+ u16 sq_con_idx;
+ u32 sq_mem_size;
+ void *sq_pbl;
+ dma_addr_t sq_pbl_dma;
+ u32 sq_pbl_size;
+ u32 sid;
+#define QEDF_RPORT_TYPE_DISK 0
+#define QEDF_RPORT_TYPE_TAPE 1
+ uint dev_type; /* Disk or tape */
+ struct list_head peers;
+};
+
+/* Used to contain LL2 skb's in ll2_skb_list */
+struct qedf_skb_work {
+ struct work_struct work;
+ struct sk_buff *skb;
+ struct qedf_ctx *qedf;
+};
+
+struct qedf_fastpath {
+#define QEDF_SB_ID_NULL 0xffff
+ u16 sb_id;
+ struct qed_sb_info *sb_info;
+ struct qedf_ctx *qedf;
+ /* Keep track of number of completions on this fastpath */
+ unsigned long completions;
+ uint32_t cq_num_entries;
+};
+
+/* Used to pass fastpath information needed to process CQEs */
+struct qedf_io_work {
+ struct work_struct work;
+ struct fcoe_cqe cqe;
+ struct qedf_ctx *qedf;
+ struct fc_frame *fp;
+};
+
+struct qedf_glbl_q_params {
+ u64 hw_p_cq; /* Completion queue PBL */
+ u64 hw_p_rq; /* Request queue PBL */
+ u64 hw_p_cmdq; /* Command queue PBL */
+};
+
+struct global_queue {
+ struct fcoe_cqe *cq;
+ dma_addr_t cq_dma;
+ u32 cq_mem_size;
+ u32 cq_cons_idx; /* Completion queue consumer index */
+ u32 cq_prod_idx;
+
+ void *cq_pbl;
+ dma_addr_t cq_pbl_dma;
+ u32 cq_pbl_size;
+};
+
+/* I/O tracing entry */
+#define QEDF_IO_TRACE_SIZE 2048
+struct qedf_io_log {
+#define QEDF_IO_TRACE_REQ 0
+#define QEDF_IO_TRACE_RSP 1
+ uint8_t direction;
+ uint16_t task_id;
+ uint32_t port_id; /* Remote port fabric ID */
+ int lun;
+ unsigned char op; /* SCSI CDB */
+ uint8_t lba[4];
+ unsigned int bufflen; /* SCSI buffer length */
+ unsigned int sg_count; /* Number of SG elements */
+ int result; /* Result passed back to mid-layer */
+ unsigned long jiffies; /* Time stamp when I/O logged */
+ int refcount; /* Reference count for task id */
+ unsigned int req_cpu; /* CPU that the task is queued on */
+ unsigned int int_cpu; /* Interrupt CPU that the task is received on */
+ unsigned int rsp_cpu; /* CPU that task is returned on */
+ u8 sge_type; /* Did we take the slow, single or fast SGE path */
+};
+
+/* Number of entries in BDQ */
+#define QEDF_BDQ_SIZE 256
+#define QEDF_BDQ_BUF_SIZE 2072
+
+/* DMA coherent buffers for BDQ */
+struct qedf_bdq_buf {
+ void *buf_addr;
+ dma_addr_t buf_dma;
+};
+
+/* Main adapter struct */
+struct qedf_ctx {
+ struct qedf_dbg_ctx dbg_ctx;
+ struct fcoe_ctlr ctlr;
+ struct fc_lport *lport;
+ u8 data_src_addr[ETH_ALEN];
+#define QEDF_LINK_DOWN 0
+#define QEDF_LINK_UP 1
+ atomic_t link_state;
+#define QEDF_DCBX_PENDING 0
+#define QEDF_DCBX_DONE 1
+ atomic_t dcbx;
+#define QEDF_NULL_VLAN_ID -1
+#define QEDF_FALLBACK_VLAN 1002
+#define QEDF_DEFAULT_PRIO 3
+ int vlan_id;
+ u8 prio;
+ struct qed_dev *cdev;
+ struct qed_dev_fcoe_info dev_info;
+ struct qed_int_info int_info;
+ uint16_t last_command;
+ spinlock_t hba_lock;
+ struct pci_dev *pdev;
+ u64 wwnn;
+ u64 wwpn;
+ u8 __aligned(16) mac[ETH_ALEN];
+ struct list_head fcports;
+ atomic_t num_offloads;
+ unsigned int curr_conn_id;
+ struct workqueue_struct *ll2_recv_wq;
+ struct workqueue_struct *link_update_wq;
+ struct devlink *devlink;
+ struct delayed_work link_update;
+ struct delayed_work link_recovery;
+ struct completion flogi_compl;
+ struct completion fipvlan_compl;
+
+ /*
+ * Used to tell if we're in the window where we are waiting for
+ * the link to come back up before informting fcoe that the link is
+ * done.
+ */
+ atomic_t link_down_tmo_valid;
+#define QEDF_TIMER_INTERVAL (1 * HZ)
+ struct timer_list timer; /* One second book keeping timer */
+#define QEDF_DRAIN_ACTIVE 1
+#define QEDF_LL2_STARTED 2
+#define QEDF_UNLOADING 3
+#define QEDF_GRCDUMP_CAPTURE 4
+#define QEDF_IN_RECOVERY 5
+#define QEDF_DBG_STOP_IO 6
+#define QEDF_PROBING 8
+#define QEDF_STAG_IN_PROGRESS 9
+ unsigned long flags; /* Miscellaneous state flags */
+ int fipvlan_retries;
+ u8 num_queues;
+ struct global_queue **global_queues;
+ /* Pointer to array of queue structures */
+ struct qedf_glbl_q_params *p_cpuq;
+ /* Physical address of array of queue structures */
+ dma_addr_t hw_p_cpuq;
+
+ struct qedf_bdq_buf bdq[QEDF_BDQ_SIZE];
+ void *bdq_pbl;
+ dma_addr_t bdq_pbl_dma;
+ size_t bdq_pbl_mem_size;
+ void *bdq_pbl_list;
+ dma_addr_t bdq_pbl_list_dma;
+ u8 bdq_pbl_list_num_entries;
+ void __iomem *bdq_primary_prod;
+ void __iomem *bdq_secondary_prod;
+ uint16_t bdq_prod_idx;
+
+ /* Structure for holding all the fastpath for this qedf_ctx */
+ struct qedf_fastpath *fp_array;
+ struct qed_fcoe_tid tasks;
+ struct qedf_cmd_mgr *cmd_mgr;
+ /* Holds the PF parameters we pass to qed to start he FCoE function */
+ struct qed_pf_params pf_params;
+ /* Used to time middle path ELS and TM commands */
+ struct workqueue_struct *timer_work_queue;
+
+#define QEDF_IO_WORK_MIN 64
+ mempool_t *io_mempool;
+ struct workqueue_struct *dpc_wq;
+ struct delayed_work recovery_work;
+ struct delayed_work board_disable_work;
+ struct delayed_work grcdump_work;
+ struct delayed_work stag_work;
+
+ u32 slow_sge_ios;
+ u32 fast_sge_ios;
+
+ uint8_t *grcdump;
+ uint32_t grcdump_size;
+
+ struct qedf_io_log io_trace_buf[QEDF_IO_TRACE_SIZE];
+ spinlock_t io_trace_lock;
+ uint16_t io_trace_idx;
+
+ bool stop_io_on_error;
+
+ u32 flogi_cnt;
+ u32 flogi_failed;
+ u32 flogi_pending;
+
+ /* Used for fc statistics */
+ struct mutex stats_mutex;
+ u64 input_requests;
+ u64 output_requests;
+ u64 control_requests;
+ u64 packet_aborts;
+ u64 alloc_failures;
+ u8 lun_resets;
+ u8 target_resets;
+ u8 task_set_fulls;
+ u8 busy;
+ /* Used for flush routine */
+ struct mutex flush_mutex;
+};
+
+struct io_bdt {
+ struct qedf_ioreq *io_req;
+ struct scsi_sge *bd_tbl;
+ dma_addr_t bd_tbl_dma;
+ u16 bd_valid;
+};
+
+struct qedf_cmd_mgr {
+ struct qedf_ctx *qedf;
+ u16 idx;
+ struct io_bdt **io_bdt_pool;
+#define FCOE_PARAMS_NUM_TASKS 2048
+ struct qedf_ioreq cmds[FCOE_PARAMS_NUM_TASKS];
+ spinlock_t lock;
+ atomic_t free_list_cnt;
+};
+
+/* Stolen from qed_cxt_api.h and adapted for qed_fcoe_info
+ * Usage:
+ *
+ * void *ptr;
+ * ptr = qedf_get_task_mem(&qedf->tasks, 128);
+ */
+static inline void *qedf_get_task_mem(struct qed_fcoe_tid *info, u32 tid)
+{
+ return (void *)(info->blocks[tid / info->num_tids_per_block] +
+ (tid % info->num_tids_per_block) * info->size);
+}
+
+static inline void qedf_stop_all_io(struct qedf_ctx *qedf)
+{
+ set_bit(QEDF_DBG_STOP_IO, &qedf->flags);
+}
+
+/*
+ * Externs
+ */
+
+/*
+ * (QEDF_LOG_NPIV | QEDF_LOG_SESS | QEDF_LOG_LPORT | QEDF_LOG_ELS | QEDF_LOG_MQ
+ * | QEDF_LOG_IO | QEDF_LOG_UNSOL | QEDF_LOG_SCSI_TM | QEDF_LOG_MP_REQ |
+ * QEDF_LOG_EVT | QEDF_LOG_CONN | QEDF_LOG_DISC | QEDF_LOG_INFO)
+ */
+#define QEDF_DEFAULT_LOG_MASK 0x3CFB6
+extern const struct qed_fcoe_ops *qed_ops;
+extern uint qedf_dump_frames;
+extern uint qedf_io_tracing;
+extern uint qedf_stop_io_on_error;
+extern uint qedf_link_down_tmo;
+#define QEDF_RETRY_DELAY_MAX 600 /* 60 seconds */
+extern bool qedf_retry_delay;
+extern uint qedf_debug;
+
+extern struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf);
+extern void qedf_cmd_mgr_free(struct qedf_cmd_mgr *cmgr);
+extern int qedf_queuecommand(struct Scsi_Host *host,
+ struct scsi_cmnd *sc_cmd);
+extern void qedf_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb);
+extern u8 *qedf_get_src_mac(struct fc_lport *lport);
+extern void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb);
+extern void qedf_fcoe_send_vlan_req(struct qedf_ctx *qedf);
+extern void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req);
+extern void qedf_process_warning_compl(struct qedf_ctx *qedf,
+ struct fcoe_cqe *cqe, struct qedf_ioreq *io_req);
+extern void qedf_process_error_detect(struct qedf_ctx *qedf,
+ struct fcoe_cqe *cqe, struct qedf_ioreq *io_req);
+extern void qedf_flush_active_ios(struct qedf_rport *fcport, u64 lun);
+extern void qedf_release_cmd(struct kref *ref);
+extern int qedf_initiate_abts(struct qedf_ioreq *io_req,
+ bool return_scsi_cmd_on_abts);
+extern void qedf_process_abts_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req);
+extern struct qedf_ioreq *qedf_alloc_cmd(struct qedf_rport *fcport,
+ u8 cmd_type);
+
+extern const struct attribute_group *qedf_host_groups[];
+extern void qedf_cmd_timer_set(struct qedf_ctx *qedf, struct qedf_ioreq *io_req,
+ unsigned int timer_msec);
+extern int qedf_init_mp_req(struct qedf_ioreq *io_req);
+extern void qedf_init_mp_task(struct qedf_ioreq *io_req,
+ struct fcoe_task_context *task_ctx, struct fcoe_wqe *sqe);
+extern u16 qedf_get_sqe_idx(struct qedf_rport *fcport);
+extern void qedf_ring_doorbell(struct qedf_rport *fcport);
+extern void qedf_process_els_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *els_req);
+extern int qedf_send_rrq(struct qedf_ioreq *aborted_io_req);
+extern int qedf_send_adisc(struct qedf_rport *fcport, struct fc_frame *fp);
+extern int qedf_initiate_cleanup(struct qedf_ioreq *io_req,
+ bool return_scsi_cmd_on_abts);
+extern void qedf_process_cleanup_compl(struct qedf_ctx *qedf,
+ struct fcoe_cqe *cqe, struct qedf_ioreq *io_req);
+extern int qedf_initiate_tmf(struct fc_rport *rport, u64 lun, u8 tm_flags);
+extern void qedf_process_tmf_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req);
+extern void qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe);
+extern void qedf_scsi_done(struct qedf_ctx *qedf, struct qedf_ioreq *io_req,
+ int result);
+extern void qedf_set_vlan_id(struct qedf_ctx *qedf, int vlan_id);
+extern void qedf_create_sysfs_ctx_attr(struct qedf_ctx *qedf);
+extern void qedf_remove_sysfs_ctx_attr(struct qedf_ctx *qedf);
+extern void qedf_capture_grc_dump(struct qedf_ctx *qedf);
+bool qedf_wait_for_upload(struct qedf_ctx *qedf);
+extern void qedf_process_unsol_compl(struct qedf_ctx *qedf, uint16_t que_idx,
+ struct fcoe_cqe *cqe);
+extern void qedf_restart_rport(struct qedf_rport *fcport);
+extern int qedf_send_rec(struct qedf_ioreq *orig_io_req);
+extern int qedf_post_io_req(struct qedf_rport *fcport,
+ struct qedf_ioreq *io_req);
+extern void qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf,
+ struct fcoe_cqe *cqe, struct qedf_ioreq *io_req);
+extern int qedf_send_flogi(struct qedf_ctx *qedf);
+extern void qedf_get_protocol_tlv_data(void *dev, void *data);
+extern void qedf_fp_io_handler(struct work_struct *work);
+extern void qedf_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data);
+extern void qedf_wq_grcdump(struct work_struct *work);
+void qedf_stag_change_work(struct work_struct *work);
+void qedf_ctx_soft_reset(struct fc_lport *lport);
+extern void qedf_schedule_hw_err_handler(void *dev,
+ enum qed_hw_err_type err_type);
+
+#define FCOE_WORD_TO_BYTE 4
+#define QEDF_MAX_TASK_NUM 0xFFFF
+#define QL45xxx 0x165C
+#define QL41xxx 0x8080
+#define MAX_CT_PAYLOAD 2048
+#define DISCOVERED_PORTS 4
+#define NUMBER_OF_PORTS 1
+
+struct fip_vlan {
+ struct ethhdr eth;
+ struct fip_header fip;
+ struct {
+ struct fip_mac_desc mac;
+ struct fip_wwn_desc wwnn;
+ } desc;
+};
+
+/* SQ/CQ Sizes */
+#define GBL_RSVD_TASKS 16
+#define NUM_TASKS_PER_CONNECTION 1024
+#define NUM_RW_TASKS_PER_CONNECTION 512
+#define FCOE_PARAMS_CQ_NUM_ENTRIES FCOE_PARAMS_NUM_TASKS
+
+#define FCOE_PARAMS_CMDQ_NUM_ENTRIES FCOE_PARAMS_NUM_TASKS
+#define SQ_NUM_ENTRIES NUM_TASKS_PER_CONNECTION
+
+#define QEDF_FCOE_PARAMS_GL_RQ_PI 0
+#define QEDF_FCOE_PARAMS_GL_CMD_PI 1
+
+#define QEDF_READ (1 << 1)
+#define QEDF_WRITE (1 << 0)
+#define MAX_FIBRE_LUNS 0xffffffff
+
+#define MIN_NUM_CPUS_MSIX(x) min_t(u32, x->dev_info.num_cqs, \
+ num_online_cpus())
+
+/*
+ * PCI function probe defines
+ */
+/* Probe/remove called during normal PCI probe */
+#define QEDF_MODE_NORMAL 0
+/* Probe/remove called from qed error recovery */
+#define QEDF_MODE_RECOVERY 1
+
+#define SUPPORTED_25000baseKR_Full (1<<27)
+#define SUPPORTED_50000baseKR2_Full (1<<28)
+#define SUPPORTED_100000baseKR4_Full (1<<29)
+#define SUPPORTED_100000baseCR4_Full (1<<30)
+
+#endif
diff --git a/drivers/scsi/qedf/qedf_attr.c b/drivers/scsi/qedf/qedf_attr.c
new file mode 100644
index 000000000000..7ebb46689f97
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_attr.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include "qedf.h"
+
+inline bool qedf_is_vport(struct qedf_ctx *qedf)
+{
+ return qedf->lport->vport != NULL;
+}
+
+/* Get base qedf for physical port from vport */
+static struct qedf_ctx *qedf_get_base_qedf(struct qedf_ctx *qedf)
+{
+ struct fc_lport *lport;
+ struct fc_lport *base_lport;
+
+ if (!(qedf_is_vport(qedf)))
+ return NULL;
+
+ lport = qedf->lport;
+ base_lport = shost_priv(vport_to_shost(lport->vport));
+ return lport_priv(base_lport);
+}
+
+static ssize_t fcoe_mac_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fc_lport *lport = shost_priv(class_to_shost(dev));
+ u32 port_id;
+ u8 lport_src_id[3];
+ u8 fcoe_mac[6];
+
+ port_id = fc_host_port_id(lport->host);
+ lport_src_id[2] = (port_id & 0x000000FF);
+ lport_src_id[1] = (port_id & 0x0000FF00) >> 8;
+ lport_src_id[0] = (port_id & 0x00FF0000) >> 16;
+ fc_fcoe_set_mac(fcoe_mac, lport_src_id);
+
+ return scnprintf(buf, PAGE_SIZE, "%pM\n", fcoe_mac);
+}
+
+static ssize_t fka_period_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fc_lport *lport = shost_priv(class_to_shost(dev));
+ struct qedf_ctx *qedf = lport_priv(lport);
+ int fka_period = -1;
+
+ if (qedf_is_vport(qedf))
+ qedf = qedf_get_base_qedf(qedf);
+
+ if (qedf->ctlr.sel_fcf)
+ fka_period = qedf->ctlr.sel_fcf->fka_period;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", fka_period);
+}
+
+static DEVICE_ATTR_RO(fcoe_mac);
+static DEVICE_ATTR_RO(fka_period);
+
+static struct attribute *qedf_host_attrs[] = {
+ &dev_attr_fcoe_mac.attr,
+ &dev_attr_fka_period.attr,
+ NULL,
+};
+
+static const struct attribute_group qedf_host_attr_group = {
+ .attrs = qedf_host_attrs
+};
+
+const struct attribute_group *qedf_host_groups[] = {
+ &qedf_host_attr_group,
+ NULL
+};
+
+extern const struct qed_fcoe_ops *qed_ops;
+
+void qedf_capture_grc_dump(struct qedf_ctx *qedf)
+{
+ struct qedf_ctx *base_qedf;
+
+ /* Make sure we use the base qedf to take the GRC dump */
+ if (qedf_is_vport(qedf))
+ base_qedf = qedf_get_base_qedf(qedf);
+ else
+ base_qedf = qedf;
+
+ if (test_bit(QEDF_GRCDUMP_CAPTURE, &base_qedf->flags)) {
+ QEDF_INFO(&(base_qedf->dbg_ctx), QEDF_LOG_INFO,
+ "GRC Dump already captured.\n");
+ return;
+ }
+
+
+ qedf_get_grc_dump(base_qedf->cdev, qed_ops->common,
+ &base_qedf->grcdump, &base_qedf->grcdump_size);
+ QEDF_ERR(&(base_qedf->dbg_ctx), "GRC Dump captured.\n");
+ set_bit(QEDF_GRCDUMP_CAPTURE, &base_qedf->flags);
+ qedf_uevent_emit(base_qedf->lport->host, QEDF_UEVENT_CODE_GRCDUMP,
+ NULL);
+}
+
+static ssize_t
+qedf_sysfs_read_grcdump(struct file *filep, struct kobject *kobj,
+ const struct bin_attribute *ba, char *buf, loff_t off,
+ size_t count)
+{
+ ssize_t ret = 0;
+ struct fc_lport *lport = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qedf_ctx *qedf = lport_priv(lport);
+
+ if (test_bit(QEDF_GRCDUMP_CAPTURE, &qedf->flags)) {
+ ret = memory_read_from_buffer(buf, count, &off,
+ qedf->grcdump, qedf->grcdump_size);
+ } else {
+ QEDF_ERR(&(qedf->dbg_ctx), "GRC Dump not captured!\n");
+ }
+
+ return ret;
+}
+
+static ssize_t
+qedf_sysfs_write_grcdump(struct file *filep, struct kobject *kobj,
+ const struct bin_attribute *ba, char *buf, loff_t off,
+ size_t count)
+{
+ struct fc_lport *lport = NULL;
+ struct qedf_ctx *qedf = NULL;
+ long reading;
+ int ret = 0;
+
+ if (off != 0)
+ return ret;
+
+
+ lport = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ qedf = lport_priv(lport);
+
+ buf[1] = 0;
+ ret = kstrtol(buf, 10, &reading);
+ if (ret) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Invalid input, err(%d)\n", ret);
+ return ret;
+ }
+
+ switch (reading) {
+ case 0:
+ memset(qedf->grcdump, 0, qedf->grcdump_size);
+ clear_bit(QEDF_GRCDUMP_CAPTURE, &qedf->flags);
+ break;
+ case 1:
+ qedf_capture_grc_dump(qedf);
+ break;
+ }
+
+ return count;
+}
+
+static const struct bin_attribute sysfs_grcdump_attr = {
+ .attr = {
+ .name = "grcdump",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .size = 0,
+ .read = qedf_sysfs_read_grcdump,
+ .write = qedf_sysfs_write_grcdump,
+};
+
+static struct sysfs_bin_attrs bin_file_entries[] = {
+ {"grcdump", &sysfs_grcdump_attr},
+ {NULL},
+};
+
+void qedf_create_sysfs_ctx_attr(struct qedf_ctx *qedf)
+{
+ qedf_create_sysfs_attr(qedf->lport->host, bin_file_entries);
+}
+
+void qedf_remove_sysfs_ctx_attr(struct qedf_ctx *qedf)
+{
+ qedf_remove_sysfs_attr(qedf->lport->host, bin_file_entries);
+}
diff --git a/drivers/scsi/qedf/qedf_dbg.c b/drivers/scsi/qedf/qedf_dbg.c
new file mode 100644
index 000000000000..0d2aed82882a
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_dbg.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include "qedf_dbg.h"
+#include <linux/vmalloc.h>
+
+void
+qedf_dbg_err(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
+ const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ if (likely(qedf) && likely(qedf->pdev))
+ pr_err("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)),
+ func, line, qedf->host_no, &vaf);
+ else
+ pr_err("[0000:00:00.0]:[%s:%d]: %pV", func, line, &vaf);
+
+ va_end(va);
+}
+
+void
+qedf_dbg_warn(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
+ const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ if (!(qedf_debug & QEDF_LOG_WARN))
+ goto ret;
+
+ if (likely(qedf) && likely(qedf->pdev))
+ pr_warn("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)),
+ func, line, qedf->host_no, &vaf);
+ else
+ pr_warn("[0000:00:00.0]:[%s:%d]: %pV", func, line, &vaf);
+
+ret:
+ va_end(va);
+}
+
+void
+qedf_dbg_notice(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
+ const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ if (!(qedf_debug & QEDF_LOG_NOTICE))
+ goto ret;
+
+ if (likely(qedf) && likely(qedf->pdev))
+ pr_notice("[%s]:[%s:%d]:%d: %pV",
+ dev_name(&(qedf->pdev->dev)), func, line,
+ qedf->host_no, &vaf);
+ else
+ pr_notice("[0000:00:00.0]:[%s:%d]: %pV", func, line, &vaf);
+
+ret:
+ va_end(va);
+}
+
+void
+qedf_dbg_info(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
+ u32 level, const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ if (!(qedf_debug & level))
+ goto ret;
+
+ if (likely(qedf) && likely(qedf->pdev))
+ pr_info("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)),
+ func, line, qedf->host_no, &vaf);
+ else
+ pr_info("[0000:00:00.0]:[%s:%d]: %pV", func, line, &vaf);
+
+ret:
+ va_end(va);
+}
+
+int
+qedf_alloc_grc_dump_buf(u8 **buf, uint32_t len)
+{
+ *buf = vzalloc(len);
+ if (!(*buf))
+ return -ENOMEM;
+
+ return 0;
+}
+
+void
+qedf_free_grc_dump_buf(uint8_t **buf)
+{
+ vfree(*buf);
+ *buf = NULL;
+}
+
+int
+qedf_get_grc_dump(struct qed_dev *cdev, const struct qed_common_ops *common,
+ u8 **buf, uint32_t *grcsize)
+{
+ if (!*buf)
+ return -EINVAL;
+
+ return common->dbg_all_data(cdev, *buf);
+}
+
+void
+qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg)
+{
+ char event_string[40];
+ char *envp[] = {event_string, NULL};
+
+ memset(event_string, 0, sizeof(event_string));
+ switch (code) {
+ case QEDF_UEVENT_CODE_GRCDUMP:
+ if (msg)
+ strscpy(event_string, msg, sizeof(event_string));
+ else
+ sprintf(event_string, "GRCDUMP=%u", shost->host_no);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ kobject_uevent_env(&shost->shost_gendev.kobj, KOBJ_CHANGE, envp);
+}
+
+int
+qedf_create_sysfs_attr(struct Scsi_Host *shost, struct sysfs_bin_attrs *iter)
+{
+ int ret = 0;
+
+ for (; iter->name; iter++) {
+ ret = sysfs_create_bin_file(&shost->shost_gendev.kobj,
+ iter->attr);
+ if (ret)
+ pr_err("Unable to create sysfs %s attr, err(%d).\n",
+ iter->name, ret);
+ }
+ return ret;
+}
+
+void
+qedf_remove_sysfs_attr(struct Scsi_Host *shost, struct sysfs_bin_attrs *iter)
+{
+ for (; iter->name; iter++)
+ sysfs_remove_bin_file(&shost->shost_gendev.kobj, iter->attr);
+}
diff --git a/drivers/scsi/qedf/qedf_dbg.h b/drivers/scsi/qedf/qedf_dbg.h
new file mode 100644
index 000000000000..eeb6c841dacb
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_dbg.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#ifndef _QEDF_DBG_H_
+#define _QEDF_DBG_H_
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <scsi/scsi_transport.h>
+#include <linux/fs.h>
+
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/qed_if.h>
+
+extern uint qedf_debug;
+
+/* Debug print level definitions */
+#define QEDF_LOG_DEFAULT 0x1 /* Set default logging mask */
+#define QEDF_LOG_INFO 0x2 /*
+ * Informational logs,
+ * MAC address, WWPN, WWNN
+ */
+#define QEDF_LOG_DISC 0x4 /* Init, discovery, rport */
+#define QEDF_LOG_LL2 0x8 /* LL2, VLAN logs */
+#define QEDF_LOG_CONN 0x10 /* Connection setup, cleanup */
+#define QEDF_LOG_EVT 0x20 /* Events, link, mtu */
+#define QEDF_LOG_TIMER 0x40 /* Timer events */
+#define QEDF_LOG_MP_REQ 0x80 /* Middle Path (MP) logs */
+#define QEDF_LOG_SCSI_TM 0x100 /* SCSI Aborts, Task Mgmt */
+#define QEDF_LOG_UNSOL 0x200 /* unsolicited event logs */
+#define QEDF_LOG_IO 0x400 /* scsi cmd, completion */
+#define QEDF_LOG_MQ 0x800 /* Multi Queue logs */
+#define QEDF_LOG_BSG 0x1000 /* BSG logs */
+#define QEDF_LOG_DEBUGFS 0x2000 /* debugFS logs */
+#define QEDF_LOG_LPORT 0x4000 /* lport logs */
+#define QEDF_LOG_ELS 0x8000 /* ELS logs */
+#define QEDF_LOG_NPIV 0x10000 /* NPIV logs */
+#define QEDF_LOG_SESS 0x20000 /* Connection setup, cleanup */
+#define QEDF_LOG_TID 0x80000 /*
+ * FW TID context acquire
+ * free
+ */
+#define QEDF_TRACK_TID 0x100000 /*
+ * Track TID state. To be
+ * enabled only at module load
+ * and not run-time.
+ */
+#define QEDF_TRACK_CMD_LIST 0x300000 /*
+ * Track active cmd list nodes,
+ * done with reference to TID,
+ * hence TRACK_TID also enabled.
+ */
+#define QEDF_LOG_NOTICE 0x40000000 /* Notice logs */
+#define QEDF_LOG_WARN 0x80000000 /* Warning logs */
+
+#define QEDF_DEBUGFS_LOG_LEN (2 * PAGE_SIZE)
+
+/* Debug context structure */
+struct qedf_dbg_ctx {
+ unsigned int host_no;
+ struct pci_dev *pdev;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *bdf_dentry;
+#endif
+};
+
+#define QEDF_ERR(pdev, fmt, ...) \
+ qedf_dbg_err(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__)
+#define QEDF_WARN(pdev, fmt, ...) \
+ qedf_dbg_warn(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__)
+#define QEDF_NOTICE(pdev, fmt, ...) \
+ qedf_dbg_notice(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__)
+#define QEDF_INFO(pdev, level, fmt, ...) \
+ qedf_dbg_info(pdev, __func__, __LINE__, level, fmt, \
+ ## __VA_ARGS__)
+__printf(4, 5)
+void qedf_dbg_err(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
+ const char *fmt, ...);
+__printf(4, 5)
+void qedf_dbg_warn(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
+ const char *, ...);
+__printf(4, 5)
+void qedf_dbg_notice(struct qedf_dbg_ctx *qedf, const char *func,
+ u32 line, const char *, ...);
+__printf(5, 6)
+void qedf_dbg_info(struct qedf_dbg_ctx *qedf, const char *func, u32 line,
+ u32 info, const char *fmt, ...);
+
+/* GRC Dump related defines */
+
+struct Scsi_Host;
+
+#define QEDF_UEVENT_CODE_GRCDUMP 0
+
+struct sysfs_bin_attrs {
+ char *name;
+ const struct bin_attribute *attr;
+};
+
+extern int qedf_alloc_grc_dump_buf(uint8_t **buf, uint32_t len);
+extern void qedf_free_grc_dump_buf(uint8_t **buf);
+extern int qedf_get_grc_dump(struct qed_dev *cdev,
+ const struct qed_common_ops *common, uint8_t **buf,
+ uint32_t *grcsize);
+extern void qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg);
+extern int qedf_create_sysfs_attr(struct Scsi_Host *shost,
+ struct sysfs_bin_attrs *iter);
+extern void qedf_remove_sysfs_attr(struct Scsi_Host *shost,
+ struct sysfs_bin_attrs *iter);
+
+struct qedf_debugfs_ops {
+ char *name;
+ struct qedf_list_of_funcs *qedf_funcs;
+};
+
+extern const struct qedf_debugfs_ops qedf_debugfs_ops[];
+extern const struct file_operations qedf_dbg_fops[];
+
+#ifdef CONFIG_DEBUG_FS
+/* DebugFS related code */
+struct qedf_list_of_funcs {
+ char *oper_str;
+ ssize_t (*oper_func)(struct qedf_dbg_ctx *qedf);
+};
+
+#define qedf_dbg_fileops(drv, ops) \
+{ \
+ .owner = THIS_MODULE, \
+ .open = simple_open, \
+ .read = drv##_dbg_##ops##_cmd_read, \
+ .write = drv##_dbg_##ops##_cmd_write \
+}
+
+/* Used for debugfs sequential files */
+#define qedf_dbg_fileops_seq(drv, ops) \
+{ \
+ .owner = THIS_MODULE, \
+ .open = drv##_dbg_##ops##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+}
+
+extern void qedf_dbg_host_init(struct qedf_dbg_ctx *qedf,
+ const struct qedf_debugfs_ops *dops,
+ const struct file_operations *fops);
+extern void qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf);
+extern void qedf_dbg_init(char *drv_name);
+extern void qedf_dbg_exit(void);
+#endif /* CONFIG_DEBUG_FS */
+
+#endif /* _QEDF_DBG_H_ */
diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c
new file mode 100644
index 000000000000..96174353e389
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_debugfs.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 QLogic Corporation
+ */
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "qedf.h"
+#include "qedf_dbg.h"
+
+static struct dentry *qedf_dbg_root;
+
+/*
+ * qedf_dbg_host_init - setup the debugfs file for the pf
+ */
+void
+qedf_dbg_host_init(struct qedf_dbg_ctx *qedf,
+ const struct qedf_debugfs_ops *dops,
+ const struct file_operations *fops)
+{
+ char host_dirname[32];
+
+ QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "Creating debugfs host node\n");
+ /* create pf dir */
+ sprintf(host_dirname, "host%u", qedf->host_no);
+ qedf->bdf_dentry = debugfs_create_dir(host_dirname, qedf_dbg_root);
+
+ /* create debugfs files */
+ while (dops) {
+ if (!(dops->name))
+ break;
+
+ debugfs_create_file(dops->name, 0600, qedf->bdf_dentry, qedf,
+ fops);
+ dops++;
+ fops++;
+ }
+}
+
+/*
+ * qedf_dbg_host_exit - clear out the pf's debugfs entries
+ */
+void
+qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf_dbg)
+{
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "Destroying debugfs host "
+ "entry\n");
+ /* remove debugfs entries of this PF */
+ debugfs_remove_recursive(qedf_dbg->bdf_dentry);
+ qedf_dbg->bdf_dentry = NULL;
+}
+
+/*
+ * qedf_dbg_init - start up debugfs for the driver
+ */
+void
+qedf_dbg_init(char *drv_name)
+{
+ QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Creating debugfs root node\n");
+
+ /* create qed dir in root of debugfs. NULL means debugfs root */
+ qedf_dbg_root = debugfs_create_dir(drv_name, NULL);
+}
+
+/*
+ * qedf_dbg_exit - clean out the driver's debugfs entries
+ */
+void
+qedf_dbg_exit(void)
+{
+ QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Destroying debugfs root "
+ "entry\n");
+
+ /* remove qed dir in root of debugfs */
+ debugfs_remove_recursive(qedf_dbg_root);
+ qedf_dbg_root = NULL;
+}
+
+const struct qedf_debugfs_ops qedf_debugfs_ops[] = {
+ { "fp_int", NULL },
+ { "io_trace", NULL },
+ { "debug", NULL },
+ { "stop_io_on_error", NULL},
+ { "driver_stats", NULL},
+ { "clear_stats", NULL},
+ { "offload_stats", NULL},
+ /* This must be last */
+ { NULL, NULL }
+};
+
+DECLARE_PER_CPU(struct qedf_percpu_iothread_s, qedf_percpu_iothreads);
+
+static ssize_t
+qedf_dbg_fp_int_cmd_read(struct file *filp, char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ ssize_t ret;
+ size_t cnt = 0;
+ char *cbuf;
+ int id;
+ struct qedf_fastpath *fp = NULL;
+ struct qedf_dbg_ctx *qedf_dbg =
+ (struct qedf_dbg_ctx *)filp->private_data;
+ struct qedf_ctx *qedf = container_of(qedf_dbg,
+ struct qedf_ctx, dbg_ctx);
+
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n");
+
+ cbuf = vmalloc(QEDF_DEBUGFS_LOG_LEN);
+ if (!cbuf)
+ return 0;
+
+ cnt += scnprintf(cbuf + cnt, QEDF_DEBUGFS_LOG_LEN - cnt, "\nFastpath I/O completions\n\n");
+
+ for (id = 0; id < qedf->num_queues; id++) {
+ fp = &(qedf->fp_array[id]);
+ if (fp->sb_id == QEDF_SB_ID_NULL)
+ continue;
+ cnt += scnprintf(cbuf + cnt, QEDF_DEBUGFS_LOG_LEN - cnt,
+ "#%d: %lu\n", id, fp->completions);
+ }
+
+ ret = simple_read_from_buffer(buffer, count, ppos, cbuf, cnt);
+
+ vfree(cbuf);
+
+ return ret;
+}
+
+static ssize_t
+qedf_dbg_fp_int_cmd_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ if (!count || *ppos)
+ return 0;
+
+ return count;
+}
+
+static ssize_t
+qedf_dbg_debug_cmd_read(struct file *filp, char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ int cnt;
+ char cbuf[32];
+ struct qedf_dbg_ctx *qedf_dbg =
+ (struct qedf_dbg_ctx *)filp->private_data;
+
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "debug mask=0x%x\n", qedf_debug);
+ cnt = scnprintf(cbuf, sizeof(cbuf), "debug mask = 0x%x\n", qedf_debug);
+
+ return simple_read_from_buffer(buffer, count, ppos, cbuf, cnt);
+}
+
+static ssize_t
+qedf_dbg_debug_cmd_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ uint32_t val;
+ void *kern_buf;
+ int rval;
+ struct qedf_dbg_ctx *qedf_dbg =
+ (struct qedf_dbg_ctx *)filp->private_data;
+
+ if (!count || *ppos)
+ return 0;
+
+ kern_buf = memdup_user_nul(buffer, count);
+ if (IS_ERR(kern_buf))
+ return PTR_ERR(kern_buf);
+
+ rval = kstrtouint(kern_buf, 10, &val);
+ kfree(kern_buf);
+ if (rval)
+ return rval;
+
+ if (val == 1)
+ qedf_debug = QEDF_DEFAULT_LOG_MASK;
+ else
+ qedf_debug = val;
+
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "Setting debug=0x%x.\n", val);
+ return count;
+}
+
+static ssize_t
+qedf_dbg_stop_io_on_error_cmd_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ int cnt;
+ char cbuf[7];
+ struct qedf_dbg_ctx *qedf_dbg =
+ (struct qedf_dbg_ctx *)filp->private_data;
+ struct qedf_ctx *qedf = container_of(qedf_dbg,
+ struct qedf_ctx, dbg_ctx);
+
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n");
+ cnt = scnprintf(cbuf, sizeof(cbuf), "%s\n",
+ qedf->stop_io_on_error ? "true" : "false");
+
+ return simple_read_from_buffer(buffer, count, ppos, cbuf, cnt);
+}
+
+static ssize_t
+qedf_dbg_stop_io_on_error_cmd_write(struct file *filp,
+ const char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ void *kern_buf;
+ struct qedf_dbg_ctx *qedf_dbg =
+ (struct qedf_dbg_ctx *)filp->private_data;
+ struct qedf_ctx *qedf = container_of(qedf_dbg, struct qedf_ctx,
+ dbg_ctx);
+
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n");
+
+ if (!count || *ppos)
+ return 0;
+
+ kern_buf = memdup_user(buffer, 6);
+ if (IS_ERR(kern_buf))
+ return PTR_ERR(kern_buf);
+
+ if (strncmp(kern_buf, "false", 5) == 0)
+ qedf->stop_io_on_error = false;
+ else if (strncmp(kern_buf, "true", 4) == 0)
+ qedf->stop_io_on_error = true;
+ else if (strncmp(kern_buf, "now", 3) == 0)
+ /* Trigger from user to stop all I/O on this host */
+ set_bit(QEDF_DBG_STOP_IO, &qedf->flags);
+
+ kfree(kern_buf);
+ return count;
+}
+
+static int
+qedf_io_trace_show(struct seq_file *s, void *unused)
+{
+ int i, idx = 0;
+ struct qedf_ctx *qedf = s->private;
+ struct qedf_dbg_ctx *qedf_dbg = &qedf->dbg_ctx;
+ struct qedf_io_log *io_log;
+ unsigned long flags;
+
+ if (!qedf_io_tracing) {
+ seq_puts(s, "I/O tracing not enabled.\n");
+ goto out;
+ }
+
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n");
+
+ spin_lock_irqsave(&qedf->io_trace_lock, flags);
+ idx = qedf->io_trace_idx;
+ for (i = 0; i < QEDF_IO_TRACE_SIZE; i++) {
+ io_log = &qedf->io_trace_buf[idx];
+ seq_printf(s, "%d:", io_log->direction);
+ seq_printf(s, "0x%x:", io_log->task_id);
+ seq_printf(s, "0x%06x:", io_log->port_id);
+ seq_printf(s, "%d:", io_log->lun);
+ seq_printf(s, "0x%02x:", io_log->op);
+ seq_printf(s, "0x%02x%02x%02x%02x:", io_log->lba[0],
+ io_log->lba[1], io_log->lba[2], io_log->lba[3]);
+ seq_printf(s, "%d:", io_log->bufflen);
+ seq_printf(s, "%d:", io_log->sg_count);
+ seq_printf(s, "0x%08x:", io_log->result);
+ seq_printf(s, "%lu:", io_log->jiffies);
+ seq_printf(s, "%d:", io_log->refcount);
+ seq_printf(s, "%d:", io_log->req_cpu);
+ seq_printf(s, "%d:", io_log->int_cpu);
+ seq_printf(s, "%d:", io_log->rsp_cpu);
+ seq_printf(s, "%d\n", io_log->sge_type);
+
+ idx++;
+ if (idx == QEDF_IO_TRACE_SIZE)
+ idx = 0;
+ }
+ spin_unlock_irqrestore(&qedf->io_trace_lock, flags);
+
+out:
+ return 0;
+}
+
+static int
+qedf_dbg_io_trace_open(struct inode *inode, struct file *file)
+{
+ struct qedf_dbg_ctx *qedf_dbg = inode->i_private;
+ struct qedf_ctx *qedf = container_of(qedf_dbg,
+ struct qedf_ctx, dbg_ctx);
+
+ return single_open(file, qedf_io_trace_show, qedf);
+}
+
+/* Based on fip_state enum from libfcoe.h */
+static char *fip_state_names[] = {
+ "FIP_ST_DISABLED",
+ "FIP_ST_LINK_WAIT",
+ "FIP_ST_AUTO",
+ "FIP_ST_NON_FIP",
+ "FIP_ST_ENABLED",
+ "FIP_ST_VNMP_START",
+ "FIP_ST_VNMP_PROBE1",
+ "FIP_ST_VNMP_PROBE2",
+ "FIP_ST_VNMP_CLAIM",
+ "FIP_ST_VNMP_UP",
+};
+
+/* Based on fc_rport_state enum from libfc.h */
+static char *fc_rport_state_names[] = {
+ "RPORT_ST_INIT",
+ "RPORT_ST_FLOGI",
+ "RPORT_ST_PLOGI_WAIT",
+ "RPORT_ST_PLOGI",
+ "RPORT_ST_PRLI",
+ "RPORT_ST_RTV",
+ "RPORT_ST_READY",
+ "RPORT_ST_ADISC",
+ "RPORT_ST_DELETE",
+};
+
+static int
+qedf_driver_stats_show(struct seq_file *s, void *unused)
+{
+ struct qedf_ctx *qedf = s->private;
+ struct qedf_rport *fcport;
+ struct fc_rport_priv *rdata;
+
+ seq_printf(s, "Host WWNN/WWPN: %016llx/%016llx\n",
+ qedf->wwnn, qedf->wwpn);
+ seq_printf(s, "Host NPortID: %06x\n", qedf->lport->port_id);
+ seq_printf(s, "Link State: %s\n", atomic_read(&qedf->link_state) ?
+ "Up" : "Down");
+ seq_printf(s, "Logical Link State: %s\n", qedf->lport->link_up ?
+ "Up" : "Down");
+ seq_printf(s, "FIP state: %s\n", fip_state_names[qedf->ctlr.state]);
+ seq_printf(s, "FIP VLAN ID: %d\n", qedf->vlan_id & 0xfff);
+ seq_printf(s, "FIP 802.1Q Priority: %d\n", qedf->prio);
+ if (qedf->ctlr.sel_fcf) {
+ seq_printf(s, "FCF WWPN: %016llx\n",
+ qedf->ctlr.sel_fcf->switch_name);
+ seq_printf(s, "FCF MAC: %pM\n", qedf->ctlr.sel_fcf->fcf_mac);
+ } else {
+ seq_puts(s, "FCF not selected\n");
+ }
+
+ seq_puts(s, "\nSGE stats:\n\n");
+ seq_printf(s, "cmg_mgr free io_reqs: %d\n",
+ atomic_read(&qedf->cmd_mgr->free_list_cnt));
+ seq_printf(s, "slow SGEs: %d\n", qedf->slow_sge_ios);
+ seq_printf(s, "fast SGEs: %d\n\n", qedf->fast_sge_ios);
+
+ seq_puts(s, "Offloaded ports:\n\n");
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(fcport, &qedf->fcports, peers) {
+ rdata = fcport->rdata;
+ if (rdata == NULL)
+ continue;
+ seq_printf(s, "%016llx/%016llx/%06x: state=%s, free_sqes=%d, num_active_ios=%d\n",
+ rdata->rport->node_name, rdata->rport->port_name,
+ rdata->ids.port_id,
+ fc_rport_state_names[rdata->rp_state],
+ atomic_read(&fcport->free_sqes),
+ atomic_read(&fcport->num_active_ios));
+ }
+ rcu_read_unlock();
+
+ return 0;
+}
+
+static int
+qedf_dbg_driver_stats_open(struct inode *inode, struct file *file)
+{
+ struct qedf_dbg_ctx *qedf_dbg = inode->i_private;
+ struct qedf_ctx *qedf = container_of(qedf_dbg,
+ struct qedf_ctx, dbg_ctx);
+
+ return single_open(file, qedf_driver_stats_show, qedf);
+}
+
+static ssize_t
+qedf_dbg_clear_stats_cmd_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ int cnt = 0;
+
+ /* Essentially a read stub */
+ cnt = min_t(int, count, cnt - *ppos);
+ *ppos += cnt;
+ return cnt;
+}
+
+static ssize_t
+qedf_dbg_clear_stats_cmd_write(struct file *filp,
+ const char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct qedf_dbg_ctx *qedf_dbg =
+ (struct qedf_dbg_ctx *)filp->private_data;
+ struct qedf_ctx *qedf = container_of(qedf_dbg, struct qedf_ctx,
+ dbg_ctx);
+
+ QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "Clearing stat counters.\n");
+
+ if (!count || *ppos)
+ return 0;
+
+ /* Clear stat counters exposed by 'stats' node */
+ qedf->slow_sge_ios = 0;
+ qedf->fast_sge_ios = 0;
+
+ return count;
+}
+
+static int
+qedf_offload_stats_show(struct seq_file *s, void *unused)
+{
+ struct qedf_ctx *qedf = s->private;
+ struct qed_fcoe_stats *fw_fcoe_stats;
+
+ fw_fcoe_stats = kmalloc(sizeof(struct qed_fcoe_stats), GFP_KERNEL);
+ if (!fw_fcoe_stats) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate memory for "
+ "fw_fcoe_stats.\n");
+ goto out;
+ }
+
+ /* Query firmware for offload stats */
+ qed_ops->get_stats(qedf->cdev, fw_fcoe_stats);
+
+ seq_printf(s, "fcoe_rx_byte_cnt=%llu\n"
+ "fcoe_rx_data_pkt_cnt=%llu\n"
+ "fcoe_rx_xfer_pkt_cnt=%llu\n"
+ "fcoe_rx_other_pkt_cnt=%llu\n"
+ "fcoe_silent_drop_pkt_cmdq_full_cnt=%u\n"
+ "fcoe_silent_drop_pkt_crc_error_cnt=%u\n"
+ "fcoe_silent_drop_pkt_task_invalid_cnt=%u\n"
+ "fcoe_silent_drop_total_pkt_cnt=%u\n"
+ "fcoe_silent_drop_pkt_rq_full_cnt=%u\n"
+ "fcoe_tx_byte_cnt=%llu\n"
+ "fcoe_tx_data_pkt_cnt=%llu\n"
+ "fcoe_tx_xfer_pkt_cnt=%llu\n"
+ "fcoe_tx_other_pkt_cnt=%llu\n",
+ fw_fcoe_stats->fcoe_rx_byte_cnt,
+ fw_fcoe_stats->fcoe_rx_data_pkt_cnt,
+ fw_fcoe_stats->fcoe_rx_xfer_pkt_cnt,
+ fw_fcoe_stats->fcoe_rx_other_pkt_cnt,
+ fw_fcoe_stats->fcoe_silent_drop_pkt_cmdq_full_cnt,
+ fw_fcoe_stats->fcoe_silent_drop_pkt_crc_error_cnt,
+ fw_fcoe_stats->fcoe_silent_drop_pkt_task_invalid_cnt,
+ fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt,
+ fw_fcoe_stats->fcoe_silent_drop_pkt_rq_full_cnt,
+ fw_fcoe_stats->fcoe_tx_byte_cnt,
+ fw_fcoe_stats->fcoe_tx_data_pkt_cnt,
+ fw_fcoe_stats->fcoe_tx_xfer_pkt_cnt,
+ fw_fcoe_stats->fcoe_tx_other_pkt_cnt);
+
+ kfree(fw_fcoe_stats);
+out:
+ return 0;
+}
+
+static int
+qedf_dbg_offload_stats_open(struct inode *inode, struct file *file)
+{
+ struct qedf_dbg_ctx *qedf_dbg = inode->i_private;
+ struct qedf_ctx *qedf = container_of(qedf_dbg,
+ struct qedf_ctx, dbg_ctx);
+
+ return single_open(file, qedf_offload_stats_show, qedf);
+}
+
+const struct file_operations qedf_dbg_fops[] = {
+ qedf_dbg_fileops(qedf, fp_int),
+ qedf_dbg_fileops_seq(qedf, io_trace),
+ qedf_dbg_fileops(qedf, debug),
+ qedf_dbg_fileops(qedf, stop_io_on_error),
+ qedf_dbg_fileops_seq(qedf, driver_stats),
+ qedf_dbg_fileops(qedf, clear_stats),
+ qedf_dbg_fileops_seq(qedf, offload_stats),
+ /* This must be last */
+ { },
+};
+
+#else /* CONFIG_DEBUG_FS */
+void qedf_dbg_host_init(struct qedf_dbg_ctx *);
+void qedf_dbg_host_exit(struct qedf_dbg_ctx *);
+void qedf_dbg_init(char *);
+void qedf_dbg_exit(void);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c
new file mode 100644
index 000000000000..1ff5bc314fc0
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_els.c
@@ -0,0 +1,1066 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include "qedf.h"
+
+/* It's assumed that the lock is held when calling this function. */
+static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op,
+ void *data, uint32_t data_len,
+ void (*cb_func)(struct qedf_els_cb_arg *cb_arg),
+ struct qedf_els_cb_arg *cb_arg, uint32_t timer_msec)
+{
+ struct qedf_ctx *qedf;
+ struct fc_lport *lport;
+ struct qedf_ioreq *els_req;
+ struct qedf_mp_req *mp_req;
+ struct fc_frame_header *fc_hdr;
+ struct fcoe_task_context *task;
+ int rc = 0;
+ uint32_t did, sid;
+ uint16_t xid;
+ struct fcoe_wqe *sqe;
+ unsigned long flags;
+ u16 sqe_idx;
+
+ if (!fcport) {
+ QEDF_ERR(NULL, "fcport is NULL");
+ rc = -EINVAL;
+ goto els_err;
+ }
+
+ qedf = fcport->qedf;
+ lport = qedf->lport;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending ELS\n");
+
+ rc = fc_remote_port_chkready(fcport->rport);
+ if (rc) {
+ QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: rport not ready\n", op);
+ rc = -EAGAIN;
+ goto els_err;
+ }
+ if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: link is not ready\n",
+ op);
+ rc = -EAGAIN;
+ goto els_err;
+ }
+
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: fcport not ready\n", op);
+ rc = -EINVAL;
+ goto els_err;
+ }
+
+ els_req = qedf_alloc_cmd(fcport, QEDF_ELS);
+ if (!els_req) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS,
+ "Failed to alloc ELS request 0x%x\n", op);
+ rc = -ENOMEM;
+ goto els_err;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "initiate_els els_req = "
+ "0x%p cb_arg = %p xid = %x\n", els_req, cb_arg,
+ els_req->xid);
+ els_req->sc_cmd = NULL;
+ els_req->cmd_type = QEDF_ELS;
+ els_req->fcport = fcport;
+ els_req->cb_func = cb_func;
+ cb_arg->io_req = els_req;
+ cb_arg->op = op;
+ els_req->cb_arg = cb_arg;
+ els_req->data_xfer_len = data_len;
+
+ /* Record which cpu this request is associated with */
+ els_req->cpu = smp_processor_id();
+
+ mp_req = (struct qedf_mp_req *)&(els_req->mp_req);
+ rc = qedf_init_mp_req(els_req);
+ if (rc) {
+ QEDF_ERR(&(qedf->dbg_ctx), "ELS MP request init failed\n");
+ kref_put(&els_req->refcount, qedf_release_cmd);
+ goto els_err;
+ } else {
+ rc = 0;
+ }
+
+ /* Fill ELS Payload */
+ if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) {
+ memcpy(mp_req->req_buf, data, data_len);
+ } else {
+ QEDF_ERR(&(qedf->dbg_ctx), "Invalid ELS op 0x%x\n", op);
+ els_req->cb_func = NULL;
+ els_req->cb_arg = NULL;
+ kref_put(&els_req->refcount, qedf_release_cmd);
+ rc = -EINVAL;
+ }
+
+ if (rc)
+ goto els_err;
+
+ /* Fill FC header */
+ fc_hdr = &(mp_req->req_fc_hdr);
+
+ did = fcport->rdata->ids.port_id;
+ sid = fcport->sid;
+
+ __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid,
+ FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ |
+ FC_FC_SEQ_INIT, 0);
+
+ /* Obtain exchange id */
+ xid = els_req->xid;
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+
+ sqe_idx = qedf_get_sqe_idx(fcport);
+ sqe = &fcport->sq[sqe_idx];
+ memset(sqe, 0, sizeof(struct fcoe_wqe));
+
+ /* Initialize task context for this IO request */
+ task = qedf_get_task_mem(&qedf->tasks, xid);
+ qedf_init_mp_task(els_req, task, sqe);
+
+ /* Put timer on els request */
+ if (timer_msec)
+ qedf_cmd_timer_set(qedf, els_req, timer_msec);
+
+ /* Ring doorbell */
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Ringing doorbell for ELS "
+ "req\n");
+ qedf_ring_doorbell(fcport);
+ set_bit(QEDF_CMD_OUTSTANDING, &els_req->flags);
+
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+els_err:
+ return rc;
+}
+
+void qedf_process_els_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *els_req)
+{
+ struct fcoe_cqe_midpath_info *mp_info;
+ struct qedf_rport *fcport;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered with xid = 0x%x"
+ " cmd_type = %d.\n", els_req->xid, els_req->cmd_type);
+
+ if ((els_req->event == QEDF_IOREQ_EV_ELS_FLUSH)
+ || (els_req->event == QEDF_IOREQ_EV_CLEANUP_SUCCESS)
+ || (els_req->event == QEDF_IOREQ_EV_CLEANUP_FAILED)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "ELS completion xid=0x%x after flush event=0x%x",
+ els_req->xid, els_req->event);
+ return;
+ }
+
+ fcport = els_req->fcport;
+
+ /* When flush is active,
+ * let the cmds be completed from the cleanup context
+ */
+ if (test_bit(QEDF_RPORT_IN_TARGET_RESET, &fcport->flags) ||
+ test_bit(QEDF_RPORT_IN_LUN_RESET, &fcport->flags)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Dropping ELS completion xid=0x%x as fcport is flushing",
+ els_req->xid);
+ return;
+ }
+
+ clear_bit(QEDF_CMD_OUTSTANDING, &els_req->flags);
+
+ /* Kill the ELS timer */
+ cancel_delayed_work(&els_req->timeout_work);
+
+ /* Get ELS response length from CQE */
+ mp_info = &cqe->cqe_info.midpath_info;
+ els_req->mp_req.resp_len = mp_info->data_placement_size;
+
+ /* Parse ELS response */
+ if ((els_req->cb_func) && (els_req->cb_arg)) {
+ els_req->cb_func(els_req->cb_arg);
+ els_req->cb_arg = NULL;
+ }
+
+ kref_put(&els_req->refcount, qedf_release_cmd);
+}
+
+static void qedf_rrq_compl(struct qedf_els_cb_arg *cb_arg)
+{
+ struct qedf_ioreq *orig_io_req;
+ struct qedf_ioreq *rrq_req;
+ struct qedf_ctx *qedf;
+ int refcount;
+
+ rrq_req = cb_arg->io_req;
+ qedf = rrq_req->fcport->qedf;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered.\n");
+
+ orig_io_req = cb_arg->aborted_io_req;
+
+ if (!orig_io_req) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Original io_req is NULL, rrq_req = %p.\n", rrq_req);
+ goto out_free;
+ }
+
+ refcount = kref_read(&orig_io_req->refcount);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "rrq_compl: orig io = %p,"
+ " orig xid = 0x%x, rrq_xid = 0x%x, refcount=%d\n",
+ orig_io_req, orig_io_req->xid, rrq_req->xid, refcount);
+
+ /*
+ * This should return the aborted io_req to the command pool. Note that
+ * we need to check the refcound in case the original request was
+ * flushed but we get a completion on this xid.
+ */
+ if (orig_io_req && refcount > 0)
+ kref_put(&orig_io_req->refcount, qedf_release_cmd);
+
+out_free:
+ /*
+ * Release a reference to the rrq request if we timed out as the
+ * rrq completion handler is called directly from the timeout handler
+ * and not from els_compl where the reference would have normally been
+ * released.
+ */
+ if (rrq_req->event == QEDF_IOREQ_EV_ELS_TMO)
+ kref_put(&rrq_req->refcount, qedf_release_cmd);
+ kfree(cb_arg);
+}
+
+/* Assumes kref is already held by caller */
+int qedf_send_rrq(struct qedf_ioreq *aborted_io_req)
+{
+
+ struct fc_els_rrq rrq;
+ struct qedf_rport *fcport;
+ struct fc_lport *lport;
+ struct qedf_els_cb_arg *cb_arg = NULL;
+ struct qedf_ctx *qedf;
+ uint32_t sid;
+ uint32_t r_a_tov;
+ int rc;
+ int refcount;
+
+ if (!aborted_io_req) {
+ QEDF_ERR(NULL, "abort_io_req is NULL.\n");
+ return -EINVAL;
+ }
+
+ fcport = aborted_io_req->fcport;
+
+ if (!fcport) {
+ refcount = kref_read(&aborted_io_req->refcount);
+ QEDF_ERR(NULL,
+ "RRQ work was queued prior to a flush xid=0x%x, refcount=%d.\n",
+ aborted_io_req->xid, refcount);
+ kref_put(&aborted_io_req->refcount, qedf_release_cmd);
+ return -EINVAL;
+ }
+
+ /* Check that fcport is still offloaded */
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(NULL, "fcport is no longer offloaded.\n");
+ return -EINVAL;
+ }
+
+ if (!fcport->qedf) {
+ QEDF_ERR(NULL, "fcport->qedf is NULL.\n");
+ return -EINVAL;
+ }
+
+ qedf = fcport->qedf;
+
+ /*
+ * Sanity check that we can send a RRQ to make sure that refcount isn't
+ * 0
+ */
+ refcount = kref_read(&aborted_io_req->refcount);
+ if (refcount != 1) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS,
+ "refcount for xid=%x io_req=%p refcount=%d is not 1.\n",
+ aborted_io_req->xid, aborted_io_req, refcount);
+ return -EINVAL;
+ }
+
+ lport = qedf->lport;
+ sid = fcport->sid;
+ r_a_tov = lport->r_a_tov;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending RRQ orig "
+ "io = %p, orig_xid = 0x%x\n", aborted_io_req,
+ aborted_io_req->xid);
+ memset(&rrq, 0, sizeof(rrq));
+
+ cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO);
+ if (!cb_arg) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for "
+ "RRQ\n");
+ rc = -ENOMEM;
+ goto rrq_err;
+ }
+
+ cb_arg->aborted_io_req = aborted_io_req;
+
+ rrq.rrq_cmd = ELS_RRQ;
+ hton24(rrq.rrq_s_id, sid);
+ rrq.rrq_ox_id = htons(aborted_io_req->xid);
+ rrq.rrq_rx_id =
+ htons(aborted_io_req->task->tstorm_st_context.read_write.rx_id);
+
+ rc = qedf_initiate_els(fcport, ELS_RRQ, &rrq, sizeof(rrq),
+ qedf_rrq_compl, cb_arg, r_a_tov);
+
+rrq_err:
+ if (rc) {
+ QEDF_ERR(&(qedf->dbg_ctx), "RRQ failed - release orig io "
+ "req 0x%x\n", aborted_io_req->xid);
+ kfree(cb_arg);
+ kref_put(&aborted_io_req->refcount, qedf_release_cmd);
+ }
+ return rc;
+}
+
+static void qedf_process_l2_frame_compl(struct qedf_rport *fcport,
+ struct fc_frame *fp,
+ u16 l2_oxid)
+{
+ struct fc_lport *lport = fcport->qedf->lport;
+ struct fc_frame_header *fh;
+ u32 crc;
+
+ fh = (struct fc_frame_header *)fc_frame_header_get(fp);
+
+ /* Set the OXID we return to what libfc used */
+ if (l2_oxid != FC_XID_UNKNOWN)
+ fh->fh_ox_id = htons(l2_oxid);
+
+ /* Setup header fields */
+ fh->fh_r_ctl = FC_RCTL_ELS_REP;
+ fh->fh_type = FC_TYPE_ELS;
+ /* Last sequence, end sequence */
+ fh->fh_f_ctl[0] = 0x98;
+ hton24(fh->fh_d_id, lport->port_id);
+ hton24(fh->fh_s_id, fcport->rdata->ids.port_id);
+ fh->fh_rx_id = 0xffff;
+
+ /* Set frame attributes */
+ crc = fcoe_fc_crc(fp);
+ fc_frame_init(fp);
+ fr_dev(fp) = lport;
+ fr_sof(fp) = FC_SOF_I3;
+ fr_eof(fp) = FC_EOF_T;
+ fr_crc(fp) = cpu_to_le32(~crc);
+
+ /* Send completed request to libfc */
+ fc_exch_recv(lport, fp);
+}
+
+/*
+ * In instances where an ELS command times out we may need to restart the
+ * rport by logging out and then logging back in.
+ */
+void qedf_restart_rport(struct qedf_rport *fcport)
+{
+ struct fc_lport *lport;
+ struct fc_rport_priv *rdata;
+ u32 port_id;
+ unsigned long flags;
+
+ if (!fcport) {
+ QEDF_ERR(NULL, "fcport is NULL.\n");
+ return;
+ }
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+ if (test_bit(QEDF_RPORT_IN_RESET, &fcport->flags) ||
+ !test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) ||
+ test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ QEDF_ERR(&(fcport->qedf->dbg_ctx), "fcport %p already in reset or not offloaded.\n",
+ fcport);
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ return;
+ }
+
+ /* Set that we are now in reset */
+ set_bit(QEDF_RPORT_IN_RESET, &fcport->flags);
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+
+ rdata = fcport->rdata;
+ if (rdata && !kref_get_unless_zero(&rdata->kref)) {
+ fcport->rdata = NULL;
+ rdata = NULL;
+ }
+
+ if (rdata && rdata->rp_state == RPORT_ST_READY) {
+ lport = fcport->qedf->lport;
+ port_id = rdata->ids.port_id;
+ QEDF_ERR(&(fcport->qedf->dbg_ctx),
+ "LOGO port_id=%x.\n", port_id);
+ fc_rport_logoff(rdata);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ mutex_lock(&lport->disc.disc_mutex);
+ /* Recreate the rport and log back in */
+ rdata = fc_rport_create(lport, port_id);
+ mutex_unlock(&lport->disc.disc_mutex);
+ if (rdata)
+ fc_rport_login(rdata);
+ fcport->rdata = rdata;
+ }
+ clear_bit(QEDF_RPORT_IN_RESET, &fcport->flags);
+}
+
+static void qedf_l2_els_compl(struct qedf_els_cb_arg *cb_arg)
+{
+ struct qedf_ioreq *els_req;
+ struct qedf_rport *fcport;
+ struct qedf_mp_req *mp_req;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh, *mp_fc_hdr;
+ void *resp_buf, *fc_payload;
+ u32 resp_len;
+ u16 l2_oxid;
+
+ l2_oxid = cb_arg->l2_oxid;
+ els_req = cb_arg->io_req;
+
+ if (!els_req) {
+ QEDF_ERR(NULL, "els_req is NULL.\n");
+ goto free_arg;
+ }
+
+ /*
+ * If we are flushing the command just free the cb_arg as none of the
+ * response data will be valid.
+ */
+ if (els_req->event == QEDF_IOREQ_EV_ELS_FLUSH) {
+ QEDF_ERR(NULL, "els_req xid=0x%x event is flush.\n",
+ els_req->xid);
+ goto free_arg;
+ }
+
+ fcport = els_req->fcport;
+ mp_req = &(els_req->mp_req);
+ mp_fc_hdr = &(mp_req->resp_fc_hdr);
+ resp_len = mp_req->resp_len;
+ resp_buf = mp_req->resp_buf;
+
+ /*
+ * If a middle path ELS command times out, don't try to return
+ * the command but rather do any internal cleanup and then libfc
+ * timeout the command and clean up its internal resources.
+ */
+ if (els_req->event == QEDF_IOREQ_EV_ELS_TMO) {
+ /*
+ * If ADISC times out, libfc will timeout the exchange and then
+ * try to send a PLOGI which will timeout since the session is
+ * still offloaded. Force libfc to logout the session which
+ * will offload the connection and allow the PLOGI response to
+ * flow over the LL2 path.
+ */
+ if (cb_arg->op == ELS_ADISC)
+ qedf_restart_rport(fcport);
+ return;
+ }
+
+ if (sizeof(struct fc_frame_header) + resp_len > QEDF_PAGE_SIZE) {
+ QEDF_ERR(&(fcport->qedf->dbg_ctx), "resp_len is "
+ "beyond page size.\n");
+ goto free_arg;
+ }
+
+ fp = fc_frame_alloc(fcport->qedf->lport, resp_len);
+ if (!fp) {
+ QEDF_ERR(&(fcport->qedf->dbg_ctx),
+ "fc_frame_alloc failure.\n");
+ return;
+ }
+
+ /* Copy frame header from firmware into fp */
+ fh = (struct fc_frame_header *)fc_frame_header_get(fp);
+ memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header));
+
+ /* Copy payload from firmware into fp */
+ fc_payload = fc_frame_payload_get(fp, resp_len);
+ memcpy(fc_payload, resp_buf, resp_len);
+
+ QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Completing OX_ID 0x%x back to libfc.\n", l2_oxid);
+ qedf_process_l2_frame_compl(fcport, fp, l2_oxid);
+
+free_arg:
+ kfree(cb_arg);
+}
+
+int qedf_send_adisc(struct qedf_rport *fcport, struct fc_frame *fp)
+{
+ struct fc_els_adisc *adisc;
+ struct fc_frame_header *fh;
+ struct fc_lport *lport = fcport->qedf->lport;
+ struct qedf_els_cb_arg *cb_arg = NULL;
+ struct qedf_ctx *qedf;
+ uint32_t r_a_tov = lport->r_a_tov;
+ int rc;
+
+ qedf = fcport->qedf;
+ fh = fc_frame_header_get(fp);
+
+ cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO);
+ if (!cb_arg) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for "
+ "ADISC\n");
+ rc = -ENOMEM;
+ goto adisc_err;
+ }
+ cb_arg->l2_oxid = ntohs(fh->fh_ox_id);
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Sending ADISC ox_id=0x%x.\n", cb_arg->l2_oxid);
+
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+
+ rc = qedf_initiate_els(fcport, ELS_ADISC, adisc, sizeof(*adisc),
+ qedf_l2_els_compl, cb_arg, r_a_tov);
+
+adisc_err:
+ if (rc) {
+ QEDF_ERR(&(qedf->dbg_ctx), "ADISC failed.\n");
+ kfree(cb_arg);
+ }
+ return rc;
+}
+
+static void qedf_srr_compl(struct qedf_els_cb_arg *cb_arg)
+{
+ struct qedf_ioreq *orig_io_req;
+ struct qedf_ioreq *srr_req;
+ struct qedf_mp_req *mp_req;
+ struct fc_frame_header *mp_fc_hdr, *fh;
+ struct fc_frame *fp;
+ void *resp_buf, *fc_payload;
+ u32 resp_len;
+ struct fc_lport *lport;
+ struct qedf_ctx *qedf;
+ int refcount;
+ u8 opcode;
+
+ srr_req = cb_arg->io_req;
+ qedf = srr_req->fcport->qedf;
+ lport = qedf->lport;
+
+ orig_io_req = cb_arg->aborted_io_req;
+
+ if (!orig_io_req) {
+ QEDF_ERR(NULL, "orig_io_req is NULL.\n");
+ goto out_free;
+ }
+
+ clear_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags);
+
+ if (srr_req->event != QEDF_IOREQ_EV_ELS_TMO &&
+ srr_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT)
+ cancel_delayed_work_sync(&orig_io_req->timeout_work);
+
+ refcount = kref_read(&orig_io_req->refcount);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p,"
+ " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n",
+ orig_io_req, orig_io_req->xid, srr_req->xid, refcount);
+
+ /* If a SRR times out, simply free resources */
+ if (srr_req->event == QEDF_IOREQ_EV_ELS_TMO) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "ELS timeout rec_xid=0x%x.\n", srr_req->xid);
+ goto out_put;
+ }
+
+ /* Normalize response data into struct fc_frame */
+ mp_req = &(srr_req->mp_req);
+ mp_fc_hdr = &(mp_req->resp_fc_hdr);
+ resp_len = mp_req->resp_len;
+ resp_buf = mp_req->resp_buf;
+
+ fp = fc_frame_alloc(lport, resp_len);
+ if (!fp) {
+ QEDF_ERR(&(qedf->dbg_ctx),
+ "fc_frame_alloc failure.\n");
+ goto out_put;
+ }
+
+ /* Copy frame header from firmware into fp */
+ fh = (struct fc_frame_header *)fc_frame_header_get(fp);
+ memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header));
+
+ /* Copy payload from firmware into fp */
+ fc_payload = fc_frame_payload_get(fp, resp_len);
+ memcpy(fc_payload, resp_buf, resp_len);
+
+ opcode = fc_frame_payload_op(fp);
+ switch (opcode) {
+ case ELS_LS_ACC:
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "SRR success.\n");
+ break;
+ case ELS_LS_RJT:
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS,
+ "SRR rejected.\n");
+ qedf_initiate_abts(orig_io_req, true);
+ break;
+ }
+
+ fc_frame_free(fp);
+out_put:
+ /* Put reference for original command since SRR completed */
+ kref_put(&orig_io_req->refcount, qedf_release_cmd);
+out_free:
+ kfree(cb_arg);
+}
+
+static int qedf_send_srr(struct qedf_ioreq *orig_io_req, u32 offset, u8 r_ctl)
+{
+ struct fcp_srr srr;
+ struct qedf_ctx *qedf;
+ struct qedf_rport *fcport;
+ struct fc_lport *lport;
+ struct qedf_els_cb_arg *cb_arg = NULL;
+ u32 r_a_tov;
+ int rc;
+
+ if (!orig_io_req) {
+ QEDF_ERR(NULL, "orig_io_req is NULL.\n");
+ return -EINVAL;
+ }
+
+ fcport = orig_io_req->fcport;
+
+ /* Check that fcport is still offloaded */
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(NULL, "fcport is no longer offloaded.\n");
+ return -EINVAL;
+ }
+
+ if (!fcport->qedf) {
+ QEDF_ERR(NULL, "fcport->qedf is NULL.\n");
+ return -EINVAL;
+ }
+
+ /* Take reference until SRR command completion */
+ kref_get(&orig_io_req->refcount);
+
+ qedf = fcport->qedf;
+ lport = qedf->lport;
+ r_a_tov = lport->r_a_tov;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending SRR orig_io=%p, "
+ "orig_xid=0x%x\n", orig_io_req, orig_io_req->xid);
+ memset(&srr, 0, sizeof(srr));
+
+ cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO);
+ if (!cb_arg) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for "
+ "SRR\n");
+ rc = -ENOMEM;
+ goto srr_err;
+ }
+
+ cb_arg->aborted_io_req = orig_io_req;
+
+ srr.srr_op = ELS_SRR;
+ srr.srr_ox_id = htons(orig_io_req->xid);
+ srr.srr_rx_id = htons(orig_io_req->rx_id);
+ srr.srr_rel_off = htonl(offset);
+ srr.srr_r_ctl = r_ctl;
+
+ rc = qedf_initiate_els(fcport, ELS_SRR, &srr, sizeof(srr),
+ qedf_srr_compl, cb_arg, r_a_tov);
+
+srr_err:
+ if (rc) {
+ QEDF_ERR(&(qedf->dbg_ctx), "SRR failed - release orig_io_req"
+ "=0x%x\n", orig_io_req->xid);
+ kfree(cb_arg);
+ /* If we fail to queue SRR, send ABTS to orig_io */
+ qedf_initiate_abts(orig_io_req, true);
+ kref_put(&orig_io_req->refcount, qedf_release_cmd);
+ } else
+ /* Tell other threads that SRR is in progress */
+ set_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags);
+
+ return rc;
+}
+
+static void qedf_initiate_seq_cleanup(struct qedf_ioreq *orig_io_req,
+ u32 offset, u8 r_ctl)
+{
+ struct qedf_rport *fcport;
+ unsigned long flags;
+ struct qedf_els_cb_arg *cb_arg;
+ struct fcoe_wqe *sqe;
+ u16 sqe_idx;
+
+ fcport = orig_io_req->fcport;
+
+ QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Doing sequence cleanup for xid=0x%x offset=%u.\n",
+ orig_io_req->xid, offset);
+
+ cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO);
+ if (!cb_arg) {
+ QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to allocate cb_arg "
+ "for sequence cleanup\n");
+ return;
+ }
+
+ /* Get reference for cleanup request */
+ kref_get(&orig_io_req->refcount);
+
+ orig_io_req->cmd_type = QEDF_SEQ_CLEANUP;
+ cb_arg->offset = offset;
+ cb_arg->r_ctl = r_ctl;
+ orig_io_req->cb_arg = cb_arg;
+
+ qedf_cmd_timer_set(fcport->qedf, orig_io_req,
+ QEDF_CLEANUP_TIMEOUT * HZ);
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+
+ sqe_idx = qedf_get_sqe_idx(fcport);
+ sqe = &fcport->sq[sqe_idx];
+ memset(sqe, 0, sizeof(struct fcoe_wqe));
+ orig_io_req->task_params->sqe = sqe;
+
+ init_initiator_sequence_recovery_fcoe_task(orig_io_req->task_params,
+ offset);
+ qedf_ring_doorbell(fcport);
+
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+}
+
+void qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf,
+ struct fcoe_cqe *cqe, struct qedf_ioreq *io_req)
+{
+ int rc;
+ struct qedf_els_cb_arg *cb_arg;
+
+ cb_arg = io_req->cb_arg;
+
+ /* If we timed out just free resources */
+ if (io_req->event == QEDF_IOREQ_EV_ELS_TMO || !cqe) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "cqe is NULL or timeout event (0x%x)", io_req->event);
+ goto free;
+ }
+
+ /* Kill the timer we put on the request */
+ cancel_delayed_work_sync(&io_req->timeout_work);
+
+ rc = qedf_send_srr(io_req, cb_arg->offset, cb_arg->r_ctl);
+ if (rc)
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to send SRR, I/O will "
+ "abort, xid=0x%x.\n", io_req->xid);
+free:
+ kfree(cb_arg);
+ kref_put(&io_req->refcount, qedf_release_cmd);
+}
+
+static bool qedf_requeue_io_req(struct qedf_ioreq *orig_io_req)
+{
+ struct qedf_rport *fcport;
+ struct qedf_ioreq *new_io_req;
+ unsigned long flags;
+ bool rc = false;
+
+ fcport = orig_io_req->fcport;
+ if (!fcport) {
+ QEDF_ERR(NULL, "fcport is NULL.\n");
+ goto out;
+ }
+
+ if (!orig_io_req->sc_cmd) {
+ QEDF_ERR(&(fcport->qedf->dbg_ctx), "sc_cmd is NULL for "
+ "xid=0x%x.\n", orig_io_req->xid);
+ goto out;
+ }
+
+ new_io_req = qedf_alloc_cmd(fcport, QEDF_SCSI_CMD);
+ if (!new_io_req) {
+ QEDF_ERR(&(fcport->qedf->dbg_ctx), "Could not allocate new "
+ "io_req.\n");
+ goto out;
+ }
+
+ new_io_req->sc_cmd = orig_io_req->sc_cmd;
+
+ /*
+ * This keeps the sc_cmd struct from being returned to the tape
+ * driver and being requeued twice. We do need to put a reference
+ * for the original I/O request since we will not do a SCSI completion
+ * for it.
+ */
+ orig_io_req->sc_cmd = NULL;
+ kref_put(&orig_io_req->refcount, qedf_release_cmd);
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+
+ /* kref for new command released in qedf_post_io_req on error */
+ if (qedf_post_io_req(fcport, new_io_req)) {
+ QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to post io_req\n");
+ /* Return SQE to pool */
+ atomic_inc(&fcport->free_sqes);
+ } else {
+ QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Reissued SCSI command from orig_xid=0x%x on "
+ "new_xid=0x%x.\n", orig_io_req->xid, new_io_req->xid);
+ /*
+ * Abort the original I/O but do not return SCSI command as
+ * it has been reissued on another OX_ID.
+ */
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ qedf_initiate_abts(orig_io_req, false);
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+out:
+ return rc;
+}
+
+
+static void qedf_rec_compl(struct qedf_els_cb_arg *cb_arg)
+{
+ struct qedf_ioreq *orig_io_req;
+ struct qedf_ioreq *rec_req;
+ struct qedf_mp_req *mp_req;
+ struct fc_frame_header *mp_fc_hdr, *fh;
+ struct fc_frame *fp;
+ void *resp_buf, *fc_payload;
+ u32 resp_len;
+ struct fc_lport *lport;
+ struct qedf_ctx *qedf;
+ int refcount;
+ enum fc_rctl r_ctl;
+ struct fc_els_ls_rjt *rjt;
+ struct fc_els_rec_acc *acc;
+ u8 opcode;
+ u32 offset, e_stat;
+ struct scsi_cmnd *sc_cmd;
+ bool srr_needed = false;
+
+ rec_req = cb_arg->io_req;
+ qedf = rec_req->fcport->qedf;
+ lport = qedf->lport;
+
+ orig_io_req = cb_arg->aborted_io_req;
+
+ if (!orig_io_req) {
+ QEDF_ERR(NULL, "orig_io_req is NULL.\n");
+ goto out_free;
+ }
+
+ if (rec_req->event != QEDF_IOREQ_EV_ELS_TMO &&
+ rec_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT)
+ cancel_delayed_work_sync(&orig_io_req->timeout_work);
+
+ refcount = kref_read(&orig_io_req->refcount);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p,"
+ " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n",
+ orig_io_req, orig_io_req->xid, rec_req->xid, refcount);
+
+ /* If a REC times out, free resources */
+ if (rec_req->event == QEDF_IOREQ_EV_ELS_TMO) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Got TMO event, orig_io_req %p orig_io_xid=0x%x.\n",
+ orig_io_req, orig_io_req->xid);
+ goto out_put;
+ }
+
+ /* Normalize response data into struct fc_frame */
+ mp_req = &(rec_req->mp_req);
+ mp_fc_hdr = &(mp_req->resp_fc_hdr);
+ resp_len = mp_req->resp_len;
+ acc = resp_buf = mp_req->resp_buf;
+
+ fp = fc_frame_alloc(lport, resp_len);
+ if (!fp) {
+ QEDF_ERR(&(qedf->dbg_ctx),
+ "fc_frame_alloc failure.\n");
+ goto out_put;
+ }
+
+ /* Copy frame header from firmware into fp */
+ fh = (struct fc_frame_header *)fc_frame_header_get(fp);
+ memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header));
+
+ /* Copy payload from firmware into fp */
+ fc_payload = fc_frame_payload_get(fp, resp_len);
+ memcpy(fc_payload, resp_buf, resp_len);
+
+ opcode = fc_frame_payload_op(fp);
+ if (opcode == ELS_LS_RJT) {
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ if (!rjt) {
+ QEDF_ERR(&qedf->dbg_ctx, "payload get failed");
+ goto out_free_frame;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Received LS_RJT for REC: er_reason=0x%x, "
+ "er_explan=0x%x.\n", rjt->er_reason, rjt->er_explan);
+ /*
+ * The following response(s) mean that we need to reissue the
+ * request on another exchange. We need to do this without
+ * informing the upper layers lest it cause an application
+ * error.
+ */
+ if ((rjt->er_reason == ELS_RJT_LOGIC ||
+ rjt->er_reason == ELS_RJT_UNAB) &&
+ rjt->er_explan == ELS_EXPL_OXID_RXID) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Handle CMD LOST case.\n");
+ qedf_requeue_io_req(orig_io_req);
+ }
+ } else if (opcode == ELS_LS_ACC) {
+ offset = ntohl(acc->reca_fc4value);
+ e_stat = ntohl(acc->reca_e_stat);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Received LS_ACC for REC: offset=0x%x, e_stat=0x%x.\n",
+ offset, e_stat);
+ if (e_stat & ESB_ST_SEQ_INIT) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Target has the seq init\n");
+ goto out_free_frame;
+ }
+ sc_cmd = orig_io_req->sc_cmd;
+ if (!sc_cmd) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "sc_cmd is NULL for xid=0x%x.\n",
+ orig_io_req->xid);
+ goto out_free_frame;
+ }
+ /* SCSI write case */
+ if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ if (offset == orig_io_req->data_xfer_len) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "WRITE - response lost.\n");
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ srr_needed = true;
+ offset = 0;
+ } else {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "WRITE - XFER_RDY/DATA lost.\n");
+ r_ctl = FC_RCTL_DD_DATA_DESC;
+ /* Use data from warning CQE instead of REC */
+ offset = orig_io_req->tx_buf_off;
+ }
+ /* SCSI read case */
+ } else {
+ if (orig_io_req->rx_buf_off ==
+ orig_io_req->data_xfer_len) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "READ - response lost.\n");
+ srr_needed = true;
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ offset = 0;
+ } else {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "READ - DATA lost.\n");
+ /*
+ * For read case we always set the offset to 0
+ * for sequence recovery task.
+ */
+ offset = 0;
+ r_ctl = FC_RCTL_DD_SOL_DATA;
+ }
+ }
+
+ if (srr_needed)
+ qedf_send_srr(orig_io_req, offset, r_ctl);
+ else
+ qedf_initiate_seq_cleanup(orig_io_req, offset, r_ctl);
+ }
+
+out_free_frame:
+ fc_frame_free(fp);
+out_put:
+ /* Put reference for original command since REC completed */
+ kref_put(&orig_io_req->refcount, qedf_release_cmd);
+out_free:
+ kfree(cb_arg);
+}
+
+/* Assumes kref is already held by caller */
+int qedf_send_rec(struct qedf_ioreq *orig_io_req)
+{
+
+ struct fc_els_rec rec;
+ struct qedf_rport *fcport;
+ struct fc_lport *lport;
+ struct qedf_els_cb_arg *cb_arg = NULL;
+ struct qedf_ctx *qedf;
+ uint32_t sid;
+ uint32_t r_a_tov;
+ int rc;
+
+ if (!orig_io_req) {
+ QEDF_ERR(NULL, "orig_io_req is NULL.\n");
+ return -EINVAL;
+ }
+
+ fcport = orig_io_req->fcport;
+
+ /* Check that fcport is still offloaded */
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(NULL, "fcport is no longer offloaded.\n");
+ return -EINVAL;
+ }
+
+ if (!fcport->qedf) {
+ QEDF_ERR(NULL, "fcport->qedf is NULL.\n");
+ return -EINVAL;
+ }
+
+ /* Take reference until REC command completion */
+ kref_get(&orig_io_req->refcount);
+
+ qedf = fcport->qedf;
+ lport = qedf->lport;
+ sid = fcport->sid;
+ r_a_tov = lport->r_a_tov;
+
+ memset(&rec, 0, sizeof(rec));
+
+ cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO);
+ if (!cb_arg) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for "
+ "REC\n");
+ rc = -ENOMEM;
+ goto rec_err;
+ }
+
+ cb_arg->aborted_io_req = orig_io_req;
+
+ rec.rec_cmd = ELS_REC;
+ hton24(rec.rec_s_id, sid);
+ rec.rec_ox_id = htons(orig_io_req->xid);
+ rec.rec_rx_id =
+ htons(orig_io_req->task->tstorm_st_context.read_write.rx_id);
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending REC orig_io=%p, "
+ "orig_xid=0x%x rx_id=0x%x\n", orig_io_req,
+ orig_io_req->xid, rec.rec_rx_id);
+ rc = qedf_initiate_els(fcport, ELS_REC, &rec, sizeof(rec),
+ qedf_rec_compl, cb_arg, r_a_tov);
+
+rec_err:
+ if (rc) {
+ QEDF_ERR(&(qedf->dbg_ctx), "REC failed - release orig_io_req"
+ "=0x%x\n", orig_io_req->xid);
+ kfree(cb_arg);
+ kref_put(&orig_io_req->refcount, qedf_release_cmd);
+ }
+ return rc;
+}
diff --git a/drivers/scsi/qedf/qedf_fip.c b/drivers/scsi/qedf/qedf_fip.c
new file mode 100644
index 000000000000..ad6a56ce72a8
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_fip.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include "qedf.h"
+
+extern const struct qed_fcoe_ops *qed_ops;
+/*
+ * FIP VLAN functions that will eventually move to libfcoe.
+ */
+
+void qedf_fcoe_send_vlan_req(struct qedf_ctx *qedf)
+{
+ struct sk_buff *skb;
+ char *eth_fr;
+ struct fip_vlan *vlan;
+#define MY_FIP_ALL_FCF_MACS ((__u8[6]) { 1, 0x10, 0x18, 1, 0, 2 })
+ static u8 my_fcoe_all_fcfs[ETH_ALEN] = MY_FIP_ALL_FCF_MACS;
+ unsigned long flags = 0;
+ int rc;
+
+ skb = dev_alloc_skb(sizeof(struct fip_vlan));
+ if (!skb) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Failed to allocate skb.\n");
+ return;
+ }
+
+ eth_fr = (char *)skb->data;
+ vlan = (struct fip_vlan *)eth_fr;
+
+ memset(vlan, 0, sizeof(*vlan));
+ ether_addr_copy(vlan->eth.h_source, qedf->mac);
+ ether_addr_copy(vlan->eth.h_dest, my_fcoe_all_fcfs);
+ vlan->eth.h_proto = htons(ETH_P_FIP);
+
+ vlan->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
+ vlan->fip.fip_op = htons(FIP_OP_VLAN);
+ vlan->fip.fip_subcode = FIP_SC_VL_REQ;
+ vlan->fip.fip_dl_len = htons(sizeof(vlan->desc) / FIP_BPW);
+
+ vlan->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC;
+ vlan->desc.mac.fd_desc.fip_dlen = sizeof(vlan->desc.mac) / FIP_BPW;
+ ether_addr_copy(vlan->desc.mac.fd_mac, qedf->mac);
+
+ vlan->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
+ vlan->desc.wwnn.fd_desc.fip_dlen = sizeof(vlan->desc.wwnn) / FIP_BPW;
+ put_unaligned_be64(qedf->lport->wwnn, &vlan->desc.wwnn.fd_wwn);
+
+ skb_put(skb, sizeof(*vlan));
+ skb->protocol = htons(ETH_P_FIP);
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Sending FIP VLAN "
+ "request.");
+
+ if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Cannot send vlan request "
+ "because link is not up.\n");
+
+ kfree_skb(skb);
+ return;
+ }
+
+ set_bit(QED_LL2_XMIT_FLAGS_FIP_DISCOVERY, &flags);
+ rc = qed_ops->ll2->start_xmit(qedf->cdev, skb, flags);
+ if (rc) {
+ QEDF_ERR(&qedf->dbg_ctx, "start_xmit failed rc = %d.\n", rc);
+ kfree_skb(skb);
+ return;
+ }
+
+}
+
+static void qedf_fcoe_process_vlan_resp(struct qedf_ctx *qedf,
+ struct sk_buff *skb)
+{
+ struct fip_header *fiph;
+ struct fip_desc *desc;
+ u16 vid = 0;
+ ssize_t rlen;
+ size_t dlen;
+
+ fiph = (struct fip_header *)(((void *)skb->data) + 2 * ETH_ALEN + 2);
+
+ rlen = ntohs(fiph->fip_dl_len) * 4;
+ desc = (struct fip_desc *)(fiph + 1);
+ while (rlen > 0) {
+ dlen = desc->fip_dlen * FIP_BPW;
+ switch (desc->fip_dtype) {
+ case FIP_DT_VLAN:
+ vid = ntohs(((struct fip_vlan_desc *)desc)->fd_vlan);
+ break;
+ }
+ desc = (struct fip_desc *)((char *)desc + dlen);
+ rlen -= dlen;
+ }
+
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Dropping VLAN response as link is down.\n");
+ return;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "VLAN response, "
+ "vid=0x%x.\n", vid);
+
+ if (vid > 0 && qedf->vlan_id != vid) {
+ qedf_set_vlan_id(qedf, vid);
+
+ /* Inform waiter that it's ok to call fcoe_ctlr_link up() */
+ if (!completion_done(&qedf->fipvlan_compl))
+ complete(&qedf->fipvlan_compl);
+ }
+}
+
+void qedf_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+ struct qedf_ctx *qedf = container_of(fip, struct qedf_ctx, ctlr);
+ struct ethhdr *eth_hdr;
+ struct fip_header *fiph;
+ u16 op, vlan_tci = 0;
+ u8 sub;
+ int rc = -1;
+
+ if (!test_bit(QEDF_LL2_STARTED, &qedf->flags)) {
+ QEDF_WARN(&(qedf->dbg_ctx), "LL2 not started\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2);
+ eth_hdr = (struct ethhdr *)skb_mac_header(skb);
+ op = ntohs(fiph->fip_op);
+ sub = fiph->fip_subcode;
+
+ /*
+ * Add VLAN tag to non-offload FIP frame based on current stored VLAN
+ * for FIP/FCoE traffic.
+ */
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), qedf->vlan_id);
+
+ /* Get VLAN ID from skb for printing purposes */
+ __vlan_hwaccel_get_tag(skb, &vlan_tci);
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame send: "
+ "dest=%pM op=%x sub=%x vlan=%04x.", eth_hdr->h_dest, op, sub,
+ vlan_tci);
+ if (qedf_dump_frames)
+ print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, skb->len, false);
+
+ rc = qed_ops->ll2->start_xmit(qedf->cdev, skb, 0);
+ if (rc) {
+ QEDF_ERR(&qedf->dbg_ctx, "start_xmit failed rc = %d.\n", rc);
+ kfree_skb(skb);
+ return;
+ }
+}
+
+static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
+
+/* Process incoming FIP frames. */
+void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb)
+{
+ struct ethhdr *eth_hdr;
+ struct fip_header *fiph;
+ struct fip_desc *desc;
+ struct fip_mac_desc *mp;
+ struct fip_wwn_desc *wp;
+ struct fip_vn_desc *vp;
+ size_t rlen, dlen;
+ u16 op;
+ u8 sub;
+ bool fcf_valid = false;
+ /* Default is to handle CVL regardless of fabric id descriptor */
+ bool fabric_id_valid = true;
+ bool fc_wwpn_valid = false;
+ u64 switch_name;
+ u16 vlan = 0;
+
+ eth_hdr = (struct ethhdr *)skb_mac_header(skb);
+ fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2);
+ op = ntohs(fiph->fip_op);
+ sub = fiph->fip_subcode;
+
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2,
+ "FIP frame received: skb=%p fiph=%p source=%pM destn=%pM op=%x sub=%x vlan=%04x",
+ skb, fiph, eth_hdr->h_source, eth_hdr->h_dest, op,
+ sub, vlan);
+ if (qedf_dump_frames)
+ print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, skb->len, false);
+
+ if (!ether_addr_equal(eth_hdr->h_dest, qedf->mac) &&
+ !ether_addr_equal(eth_hdr->h_dest, fcoe_all_enode) &&
+ !ether_addr_equal(eth_hdr->h_dest, qedf->data_src_addr)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2,
+ "Dropping FIP type 0x%x pkt due to destination MAC mismatch dest_mac=%pM ctlr.dest_addr=%pM data_src_addr=%pM.\n",
+ op, eth_hdr->h_dest, qedf->mac,
+ qedf->data_src_addr);
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Handle FIP VLAN resp in the driver */
+ if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) {
+ qedf_fcoe_process_vlan_resp(qedf, skb);
+ kfree_skb(skb);
+ } else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Clear virtual "
+ "link received.\n");
+
+ /* Check that an FCF has been selected by fcoe */
+ if (qedf->ctlr.sel_fcf == NULL) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Dropping CVL since FCF has not been selected "
+ "yet.");
+ kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * We need to loop through the CVL descriptors to determine
+ * if we want to reset the fcoe link
+ */
+ rlen = ntohs(fiph->fip_dl_len) * FIP_BPW;
+ desc = (struct fip_desc *)(fiph + 1);
+ while (rlen >= sizeof(*desc)) {
+ dlen = desc->fip_dlen * FIP_BPW;
+ switch (desc->fip_dtype) {
+ case FIP_DT_MAC:
+ mp = (struct fip_mac_desc *)desc;
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Switch fd_mac=%pM.\n", mp->fd_mac);
+ if (ether_addr_equal(mp->fd_mac,
+ qedf->ctlr.sel_fcf->fcf_mac))
+ fcf_valid = true;
+ break;
+ case FIP_DT_NAME:
+ wp = (struct fip_wwn_desc *)desc;
+ switch_name = get_unaligned_be64(&wp->fd_wwn);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Switch fd_wwn=%016llx fcf_switch_name=%016llx.\n",
+ switch_name,
+ qedf->ctlr.sel_fcf->switch_name);
+ if (switch_name ==
+ qedf->ctlr.sel_fcf->switch_name)
+ fc_wwpn_valid = true;
+ break;
+ case FIP_DT_VN_ID:
+ fabric_id_valid = false;
+ vp = (struct fip_vn_desc *)desc;
+
+ QEDF_ERR(&qedf->dbg_ctx,
+ "CVL vx_port fd_fc_id=0x%x fd_mac=%pM fd_wwpn=%016llx.\n",
+ ntoh24(vp->fd_fc_id), vp->fd_mac,
+ get_unaligned_be64(&vp->fd_wwpn));
+ /* Check for vx_port wwpn OR Check vx_port
+ * fabric ID OR Check vx_port MAC
+ */
+ if ((get_unaligned_be64(&vp->fd_wwpn) ==
+ qedf->wwpn) ||
+ (ntoh24(vp->fd_fc_id) ==
+ qedf->lport->port_id) ||
+ (ether_addr_equal(vp->fd_mac,
+ qedf->data_src_addr))) {
+ fabric_id_valid = true;
+ }
+ break;
+ default:
+ /* Ignore anything else */
+ break;
+ }
+ desc = (struct fip_desc *)((char *)desc + dlen);
+ rlen -= dlen;
+ }
+
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "fcf_valid=%d fabric_id_valid=%d fc_wwpn_valid=%d.\n",
+ fcf_valid, fabric_id_valid, fc_wwpn_valid);
+ if (fcf_valid && fabric_id_valid && fc_wwpn_valid)
+ qedf_ctx_soft_reset(qedf->lport);
+ kfree_skb(skb);
+ } else {
+ /* Everything else is handled by libfcoe */
+ __skb_pull(skb, ETH_HLEN);
+ fcoe_ctlr_recv(&qedf->ctlr, skb);
+ }
+}
+
+u8 *qedf_get_src_mac(struct fc_lport *lport)
+{
+ struct qedf_ctx *qedf = lport_priv(lport);
+
+ return qedf->data_src_addr;
+}
diff --git a/drivers/scsi/qedf/qedf_hsi.h b/drivers/scsi/qedf/qedf_hsi.h
new file mode 100644
index 000000000000..ecd5cb53b750
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_hsi.h
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#ifndef __QEDF_HSI__
+#define __QEDF_HSI__
+/*
+ * Add include to common target
+ */
+#include <linux/qed/common_hsi.h>
+
+/*
+ * Add include to common storage target
+ */
+#include <linux/qed/storage_common.h>
+
+/*
+ * Add include to common fcoe target for both eCore and protocol driver
+ */
+#include <linux/qed/fcoe_common.h>
+
+
+/*
+ * FCoE CQ element ABTS information
+ */
+struct fcoe_abts_info {
+ u8 r_ctl /* R_CTL in the ABTS response frame */;
+ u8 reserved0;
+ __le16 rx_id;
+ __le32 reserved2[2];
+ __le32 fc_payload[3] /* ABTS FC payload response frame */;
+};
+
+
+/*
+ * FCoE class type
+ */
+enum fcoe_class_type {
+ FCOE_TASK_CLASS_TYPE_3,
+ FCOE_TASK_CLASS_TYPE_2,
+ MAX_FCOE_CLASS_TYPE
+};
+
+
+/*
+ * FCoE CMDQ element control information
+ */
+struct fcoe_cmdqe_control {
+ __le16 conn_id;
+ u8 num_additional_cmdqes;
+ u8 cmdType;
+ /* true for ABTS request cmdqe. used in Target mode */
+#define FCOE_CMDQE_CONTROL_ABTSREQCMD_MASK 0x1
+#define FCOE_CMDQE_CONTROL_ABTSREQCMD_SHIFT 0
+#define FCOE_CMDQE_CONTROL_RESERVED1_MASK 0x7F
+#define FCOE_CMDQE_CONTROL_RESERVED1_SHIFT 1
+ u8 reserved2[4];
+};
+
+/*
+ * FCoE control + payload CMDQ element
+ */
+struct fcoe_cmdqe {
+ struct fcoe_cmdqe_control hdr;
+ u8 fc_header[24];
+ __le32 fcp_cmd_payload[8];
+};
+
+
+
+/*
+ * FCP RSP flags
+ */
+struct fcoe_fcp_rsp_flags {
+ u8 flags;
+#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID_MASK 0x1
+#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID_SHIFT 0
+#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID_MASK 0x1
+#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID_SHIFT 1
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER_MASK 0x1
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER_SHIFT 2
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER_MASK 0x1
+#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER_SHIFT 3
+#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ_MASK 0x1
+#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ_SHIFT 4
+#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS_MASK 0x7
+#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS_SHIFT 5
+};
+
+/*
+ * FCoE CQ element response information
+ */
+struct fcoe_cqe_rsp_info {
+ struct fcoe_fcp_rsp_flags rsp_flags;
+ u8 scsi_status_code;
+ __le16 retry_delay_timer;
+ __le32 fcp_resid;
+ __le32 fcp_sns_len;
+ __le32 fcp_rsp_len;
+ __le16 rx_id;
+ u8 fw_error_flags;
+#define FCOE_CQE_RSP_INFO_FW_UNDERRUN_MASK 0x1 /* FW detected underrun */
+#define FCOE_CQE_RSP_INFO_FW_UNDERRUN_SHIFT 0
+#define FCOE_CQE_RSP_INFO_RESREVED_MASK 0x7F
+#define FCOE_CQE_RSP_INFO_RESREVED_SHIFT 1
+ u8 reserved;
+ __le32 fw_residual /* Residual bytes calculated by FW */;
+};
+
+/*
+ * FCoE CQ element Target completion information
+ */
+struct fcoe_cqe_target_info {
+ __le16 rx_id;
+ __le16 reserved0;
+ __le32 reserved1[5];
+};
+
+/*
+ * FCoE error/warning reporting entry
+ */
+struct fcoe_err_report_entry {
+ __le32 err_warn_bitmap_lo /* Error bitmap lower 32 bits */;
+ __le32 err_warn_bitmap_hi /* Error bitmap higher 32 bits */;
+ /* Buffer offset the beginning of the Sequence last transmitted */
+ __le32 tx_buf_off;
+ /* Buffer offset from the beginning of the Sequence last received */
+ __le32 rx_buf_off;
+ __le16 rx_id /* RX_ID of the associated task */;
+ __le16 reserved1;
+ __le32 reserved2;
+};
+
+/*
+ * FCoE CQ element middle path information
+ */
+struct fcoe_cqe_midpath_info {
+ __le32 data_placement_size;
+ __le16 rx_id;
+ __le16 reserved0;
+ __le32 reserved1[4];
+};
+
+/*
+ * FCoE CQ element unsolicited information
+ */
+struct fcoe_unsolic_info {
+ /* BD information: Physical address and opaque data */
+ struct scsi_bd bd_info;
+ __le16 conn_id /* Connection ID the frame is associated to */;
+ __le16 pkt_len /* Packet length */;
+ u8 reserved1[4];
+};
+
+/*
+ * FCoE warning reporting entry
+ */
+struct fcoe_warning_report_entry {
+ /* BD information: Physical address and opaque data */
+ struct scsi_bd bd_info;
+ /* Buffer offset the beginning of the Sequence last transmitted */
+ __le32 buf_off;
+ __le16 rx_id /* RX_ID of the associated task */;
+ __le16 reserved1;
+};
+
+/*
+ * FCoE CQ element information
+ */
+union fcoe_cqe_info {
+ struct fcoe_cqe_rsp_info rsp_info /* Response completion information */;
+ /* Target completion information */
+ struct fcoe_cqe_target_info target_info;
+ /* Error completion information */
+ struct fcoe_err_report_entry err_info;
+ struct fcoe_abts_info abts_info /* ABTS completion information */;
+ /* Middle path completion information */
+ struct fcoe_cqe_midpath_info midpath_info;
+ /* Unsolicited packet completion information */
+ struct fcoe_unsolic_info unsolic_info;
+ /* Warning completion information (Rec Tov expiration) */
+ struct fcoe_warning_report_entry warn_info;
+};
+
+/*
+ * FCoE CQ element
+ */
+struct fcoe_cqe {
+ __le32 cqe_data;
+ /* The task identifier (OX_ID) to be completed */
+#define FCOE_CQE_TASK_ID_MASK 0xFFFF
+#define FCOE_CQE_TASK_ID_SHIFT 0
+ /*
+ * The CQE type: 0x0 Indicating on a pending work request completion.
+ * 0x1 - Indicating on an unsolicited event notification. use enum
+ * fcoe_cqe_type (use enum fcoe_cqe_type)
+ */
+#define FCOE_CQE_CQE_TYPE_MASK 0xF
+#define FCOE_CQE_CQE_TYPE_SHIFT 16
+#define FCOE_CQE_RESERVED0_MASK 0xFFF
+#define FCOE_CQE_RESERVED0_SHIFT 20
+ __le16 reserved1;
+ __le16 fw_cq_prod;
+ union fcoe_cqe_info cqe_info;
+};
+
+/*
+ * FCoE CQE type
+ */
+enum fcoe_cqe_type {
+ /* solicited response on a R/W or middle-path SQE */
+ FCOE_GOOD_COMPLETION_CQE_TYPE,
+ FCOE_UNSOLIC_CQE_TYPE /* unsolicited packet, RQ consumed */,
+ FCOE_ERROR_DETECTION_CQE_TYPE /* timer expiration, validation error */,
+ FCOE_WARNING_CQE_TYPE /* rec_tov or rr_tov timer expiration */,
+ FCOE_EXCH_CLEANUP_CQE_TYPE /* task cleanup completed */,
+ FCOE_ABTS_CQE_TYPE /* ABTS received and task cleaned */,
+ FCOE_DUMMY_CQE_TYPE /* just increment SQ CONS */,
+ /* Task was completed wight after sending a pkt to the target */
+ FCOE_LOCAL_COMP_CQE_TYPE,
+ MAX_FCOE_CQE_TYPE
+};
+
+/*
+ * FCoE fast path error codes
+ */
+enum fcoe_fp_error_warning_code {
+ FCOE_ERROR_CODE_XFER_OOO_RO /* XFER error codes */,
+ FCOE_ERROR_CODE_XFER_RO_NOT_ALIGNED,
+ FCOE_ERROR_CODE_XFER_NULL_BURST_LEN,
+ FCOE_ERROR_CODE_XFER_RO_GREATER_THAN_DATA2TRNS,
+ FCOE_ERROR_CODE_XFER_INVALID_PAYLOAD_SIZE,
+ FCOE_ERROR_CODE_XFER_TASK_TYPE_NOT_WRITE,
+ FCOE_ERROR_CODE_XFER_PEND_XFER_SET,
+ FCOE_ERROR_CODE_XFER_OPENED_SEQ,
+ FCOE_ERROR_CODE_XFER_FCTL,
+ FCOE_ERROR_CODE_FCP_RSP_BIDI_FLAGS_SET /* FCP RSP error codes */,
+ FCOE_ERROR_CODE_FCP_RSP_INVALID_LENGTH_FIELD,
+ FCOE_ERROR_CODE_FCP_RSP_INVALID_SNS_FIELD,
+ FCOE_ERROR_CODE_FCP_RSP_INVALID_PAYLOAD_SIZE,
+ FCOE_ERROR_CODE_FCP_RSP_PEND_XFER_SET,
+ FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ,
+ FCOE_ERROR_CODE_FCP_RSP_FCTL,
+ FCOE_ERROR_CODE_FCP_RSP_LAST_SEQ_RESET,
+ FCOE_ERROR_CODE_FCP_RSP_CONF_REQ_NOT_SUPPORTED_YET,
+ FCOE_ERROR_CODE_DATA_OOO_RO /* FCP DATA error codes */,
+ FCOE_ERROR_CODE_DATA_EXCEEDS_DEFINED_MAX_FRAME_SIZE,
+ FCOE_ERROR_CODE_DATA_EXCEEDS_DATA2TRNS,
+ FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET,
+ FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET,
+ FCOE_ERROR_CODE_DATA_EOFN_END_SEQ_SET,
+ FCOE_ERROR_CODE_DATA_EOFT_END_SEQ_RESET,
+ FCOE_ERROR_CODE_DATA_TASK_TYPE_NOT_READ,
+ FCOE_ERROR_CODE_DATA_FCTL_INITIATIR,
+ FCOE_ERROR_CODE_MIDPATH_INVALID_TYPE /* Middle path error codes */,
+ FCOE_ERROR_CODE_MIDPATH_SOFI3_SEQ_ACTIVE_SET,
+ FCOE_ERROR_CODE_MIDPATH_SOFN_SEQ_ACTIVE_RESET,
+ FCOE_ERROR_CODE_MIDPATH_EOFN_END_SEQ_SET,
+ FCOE_ERROR_CODE_MIDPATH_EOFT_END_SEQ_RESET,
+ FCOE_ERROR_CODE_MIDPATH_REPLY_FCTL,
+ FCOE_ERROR_CODE_MIDPATH_INVALID_REPLY,
+ FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_RCTL,
+ FCOE_ERROR_CODE_COMMON_MIDDLE_FRAME_WITH_PAD /* Common error codes */,
+ FCOE_ERROR_CODE_COMMON_SEQ_INIT_IN_TCE,
+ FCOE_ERROR_CODE_COMMON_FC_HDR_RX_ID_MISMATCH,
+ FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT,
+ FCOE_ERROR_CODE_COMMON_DATA_FC_HDR_FCP_TYPE_MISMATCH,
+ FCOE_ERROR_CODE_COMMON_DATA_NO_MORE_SGES,
+ FCOE_ERROR_CODE_COMMON_OPTIONAL_FC_HDR,
+ FCOE_ERROR_CODE_COMMON_READ_TCE_OX_ID_TOO_BIG,
+ FCOE_ERROR_CODE_COMMON_DATA_WAS_NOT_TRANSMITTED,
+ FCOE_ERROR_CODE_COMMON_TASK_DDF_RCTL_INFO_FIELD,
+ FCOE_ERROR_CODE_COMMON_TASK_INVALID_RCTL,
+ FCOE_ERROR_CODE_COMMON_TASK_RCTL_GENERAL_MISMATCH,
+ FCOE_ERROR_CODE_E_D_TOV_TIMER_EXPIRATION /* Timer error codes */,
+ FCOE_WARNING_CODE_REC_TOV_TIMER_EXPIRATION /* Timer error codes */,
+ FCOE_ERROR_CODE_RR_TOV_TIMER_EXPIRATION /* Timer error codes */,
+ /* ABTSrsp pckt arrived unexpected */
+ FCOE_ERROR_CODE_ABTS_REPLY_UNEXPECTED,
+ FCOE_ERROR_CODE_TARGET_MODE_FCP_RSP,
+ FCOE_ERROR_CODE_TARGET_MODE_FCP_XFER,
+ FCOE_ERROR_CODE_TARGET_MODE_DATA_TASK_TYPE_NOT_WRITE,
+ FCOE_ERROR_CODE_DATA_FCTL_TARGET,
+ FCOE_ERROR_CODE_TARGET_DATA_SIZE_NO_MATCH_XFER,
+ FCOE_ERROR_CODE_TARGET_DIF_CRC_CHECKSUM_ERROR,
+ FCOE_ERROR_CODE_TARGET_DIF_REF_TAG_ERROR,
+ FCOE_ERROR_CODE_TARGET_DIF_APP_TAG_ERROR,
+ MAX_FCOE_FP_ERROR_WARNING_CODE
+};
+
+
+/*
+ * FCoE RESPQ element
+ */
+struct fcoe_respqe {
+ __le16 ox_id /* OX_ID that is located in the FCP_RSP FC header */;
+ __le16 rx_id /* RX_ID that is located in the FCP_RSP FC header */;
+ __le32 additional_info;
+/* PARAM that is located in the FCP_RSP FC header */
+#define FCOE_RESPQE_PARAM_MASK 0xFFFFFF
+#define FCOE_RESPQE_PARAM_SHIFT 0
+/* Indication whther its Target-auto-rsp mode or not */
+#define FCOE_RESPQE_TARGET_AUTO_RSP_MASK 0xFF
+#define FCOE_RESPQE_TARGET_AUTO_RSP_SHIFT 24
+};
+
+
+/*
+ * FCoE slow path error codes
+ */
+enum fcoe_sp_error_code {
+ /* Error codes for Error Reporting in slow path flows */
+ FCOE_ERROR_CODE_SLOW_PATH_TOO_MANY_FUNCS,
+ FCOE_ERROR_SLOW_PATH_CODE_NO_LICENSE,
+ MAX_FCOE_SP_ERROR_CODE
+};
+
+/*
+ * FCoE task TX state
+ */
+enum fcoe_task_tx_state {
+ /* Initiate state after driver has initialized the task */
+ FCOE_TASK_TX_STATE_NORMAL,
+ /* Updated by TX path after complete transmitting unsolicited packet */
+ FCOE_TASK_TX_STATE_UNSOLICITED_COMPLETED,
+ /*
+ * Updated by TX path after start processing the task requesting the
+ * cleanup/abort operation
+ */
+ FCOE_TASK_TX_STATE_CLEAN_REQ,
+ FCOE_TASK_TX_STATE_ABTS /* Updated by TX path during abort procedure */,
+ /* Updated by TX path during exchange cleanup procedure */
+ FCOE_TASK_TX_STATE_EXCLEANUP,
+ /*
+ * Updated by TX path during exchange cleanup continuation task
+ * procedure
+ */
+ FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_WRITE_CONT,
+ /* Updated by TX path during exchange cleanup first xfer procedure */
+ FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_WRITE,
+ /* Updated by TX path during exchange cleanup read task in Target */
+ FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_READ_OR_RSP,
+ /* Updated by TX path during target exchange cleanup procedure */
+ FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_WRITE_LAST_CYCLE,
+ /* Updated by TX path during sequence recovery procedure */
+ FCOE_TASK_TX_STATE_SEQRECOVERY,
+ MAX_FCOE_TASK_TX_STATE
+};
+
+#endif /* __QEDF_HSI__ */
diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c
new file mode 100644
index 000000000000..fcfc3bed02c6
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_io.c
@@ -0,0 +1,2595 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+#include "qedf.h"
+#include <scsi/scsi_tcq.h>
+
+void qedf_cmd_timer_set(struct qedf_ctx *qedf, struct qedf_ioreq *io_req,
+ unsigned int timer_msec)
+{
+ queue_delayed_work(qedf->timer_work_queue, &io_req->timeout_work,
+ msecs_to_jiffies(timer_msec));
+}
+
+static void qedf_cmd_timeout(struct work_struct *work)
+{
+
+ struct qedf_ioreq *io_req =
+ container_of(work, struct qedf_ioreq, timeout_work.work);
+ struct qedf_ctx *qedf;
+ struct qedf_rport *fcport;
+
+ fcport = io_req->fcport;
+ if (io_req->fcport == NULL) {
+ QEDF_INFO(NULL, QEDF_LOG_IO, "fcport is NULL.\n");
+ return;
+ }
+
+ qedf = fcport->qedf;
+
+ switch (io_req->cmd_type) {
+ case QEDF_ABTS:
+ if (qedf == NULL) {
+ QEDF_INFO(NULL, QEDF_LOG_IO,
+ "qedf is NULL for ABTS xid=0x%x.\n",
+ io_req->xid);
+ return;
+ }
+
+ QEDF_ERR((&qedf->dbg_ctx), "ABTS timeout, xid=0x%x.\n",
+ io_req->xid);
+ /* Cleanup timed out ABTS */
+ qedf_initiate_cleanup(io_req, true);
+ complete(&io_req->abts_done);
+
+ /*
+ * Need to call kref_put for reference taken when initiate_abts
+ * was called since abts_compl won't be called now that we've
+ * cleaned up the task.
+ */
+ kref_put(&io_req->refcount, qedf_release_cmd);
+
+ /* Clear in abort bit now that we're done with the command */
+ clear_bit(QEDF_CMD_IN_ABORT, &io_req->flags);
+
+ /*
+ * Now that the original I/O and the ABTS are complete see
+ * if we need to reconnect to the target.
+ */
+ qedf_restart_rport(fcport);
+ break;
+ case QEDF_ELS:
+ if (!qedf) {
+ QEDF_INFO(NULL, QEDF_LOG_IO,
+ "qedf is NULL for ELS xid=0x%x.\n",
+ io_req->xid);
+ return;
+ }
+ /* ELS request no longer outstanding since it timed out */
+ clear_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+
+ kref_get(&io_req->refcount);
+ /*
+ * Don't attempt to clean an ELS timeout as any subseqeunt
+ * ABTS or cleanup requests just hang. For now just free
+ * the resources of the original I/O and the RRQ
+ */
+ QEDF_ERR(&(qedf->dbg_ctx), "ELS timeout, xid=0x%x.\n",
+ io_req->xid);
+ qedf_initiate_cleanup(io_req, true);
+ io_req->event = QEDF_IOREQ_EV_ELS_TMO;
+ /* Call callback function to complete command */
+ if (io_req->cb_func && io_req->cb_arg) {
+ io_req->cb_func(io_req->cb_arg);
+ io_req->cb_arg = NULL;
+ }
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ break;
+ case QEDF_SEQ_CLEANUP:
+ QEDF_ERR(&(qedf->dbg_ctx), "Sequence cleanup timeout, "
+ "xid=0x%x.\n", io_req->xid);
+ qedf_initiate_cleanup(io_req, true);
+ io_req->event = QEDF_IOREQ_EV_ELS_TMO;
+ qedf_process_seq_cleanup_compl(qedf, NULL, io_req);
+ break;
+ default:
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Hit default case, xid=0x%x.\n", io_req->xid);
+ break;
+ }
+}
+
+void qedf_cmd_mgr_free(struct qedf_cmd_mgr *cmgr)
+{
+ struct io_bdt *bdt_info;
+ struct qedf_ctx *qedf = cmgr->qedf;
+ size_t bd_tbl_sz;
+ u16 min_xid = 0;
+ u16 max_xid = (FCOE_PARAMS_NUM_TASKS - 1);
+ int num_ios;
+ int i;
+ struct qedf_ioreq *io_req;
+
+ num_ios = max_xid - min_xid + 1;
+
+ /* Free fcoe_bdt_ctx structures */
+ if (!cmgr->io_bdt_pool) {
+ QEDF_ERR(&qedf->dbg_ctx, "io_bdt_pool is NULL.\n");
+ goto free_cmd_pool;
+ }
+
+ bd_tbl_sz = QEDF_MAX_BDS_PER_CMD * sizeof(struct scsi_sge);
+ for (i = 0; i < num_ios; i++) {
+ bdt_info = cmgr->io_bdt_pool[i];
+ if (bdt_info->bd_tbl) {
+ dma_free_coherent(&qedf->pdev->dev, bd_tbl_sz,
+ bdt_info->bd_tbl, bdt_info->bd_tbl_dma);
+ bdt_info->bd_tbl = NULL;
+ }
+ }
+
+ /* Destroy io_bdt pool */
+ for (i = 0; i < num_ios; i++) {
+ kfree(cmgr->io_bdt_pool[i]);
+ cmgr->io_bdt_pool[i] = NULL;
+ }
+
+ kfree(cmgr->io_bdt_pool);
+ cmgr->io_bdt_pool = NULL;
+
+free_cmd_pool:
+
+ for (i = 0; i < num_ios; i++) {
+ io_req = &cmgr->cmds[i];
+ kfree(io_req->sgl_task_params);
+ kfree(io_req->task_params);
+ /* Make sure we free per command sense buffer */
+ if (io_req->sense_buffer)
+ dma_free_coherent(&qedf->pdev->dev,
+ QEDF_SCSI_SENSE_BUFFERSIZE, io_req->sense_buffer,
+ io_req->sense_buffer_dma);
+ cancel_delayed_work_sync(&io_req->rrq_work);
+ }
+
+ /* Free command manager itself */
+ vfree(cmgr);
+}
+
+static void qedf_handle_rrq(struct work_struct *work)
+{
+ struct qedf_ioreq *io_req =
+ container_of(work, struct qedf_ioreq, rrq_work.work);
+
+ atomic_set(&io_req->state, QEDFC_CMD_ST_RRQ_ACTIVE);
+ qedf_send_rrq(io_req);
+
+}
+
+struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf)
+{
+ struct qedf_cmd_mgr *cmgr;
+ struct io_bdt *bdt_info;
+ struct qedf_ioreq *io_req;
+ u16 xid;
+ int i;
+ int num_ios;
+ u16 min_xid = 0;
+ u16 max_xid = (FCOE_PARAMS_NUM_TASKS - 1);
+
+ /* Make sure num_queues is already set before calling this function */
+ if (!qedf->num_queues) {
+ QEDF_ERR(&(qedf->dbg_ctx), "num_queues is not set.\n");
+ return NULL;
+ }
+
+ if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Invalid min_xid 0x%x and "
+ "max_xid 0x%x.\n", min_xid, max_xid);
+ return NULL;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "min xid 0x%x, max xid "
+ "0x%x.\n", min_xid, max_xid);
+
+ num_ios = max_xid - min_xid + 1;
+
+ cmgr = vzalloc(sizeof(struct qedf_cmd_mgr));
+ if (!cmgr) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc cmd mgr.\n");
+ return NULL;
+ }
+
+ cmgr->qedf = qedf;
+ spin_lock_init(&cmgr->lock);
+
+ /*
+ * Initialize I/O request fields.
+ */
+ xid = 0;
+
+ for (i = 0; i < num_ios; i++) {
+ io_req = &cmgr->cmds[i];
+ INIT_DELAYED_WORK(&io_req->timeout_work, qedf_cmd_timeout);
+
+ io_req->xid = xid++;
+
+ INIT_DELAYED_WORK(&io_req->rrq_work, qedf_handle_rrq);
+
+ /* Allocate DMA memory to hold sense buffer */
+ io_req->sense_buffer = dma_alloc_coherent(&qedf->pdev->dev,
+ QEDF_SCSI_SENSE_BUFFERSIZE, &io_req->sense_buffer_dma,
+ GFP_KERNEL);
+ if (!io_req->sense_buffer) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Failed to alloc sense buffer.\n");
+ goto mem_err;
+ }
+
+ /* Allocate task parameters to pass to f/w init funcions */
+ io_req->task_params = kzalloc(sizeof(*io_req->task_params),
+ GFP_KERNEL);
+ if (!io_req->task_params) {
+ QEDF_ERR(&(qedf->dbg_ctx),
+ "Failed to allocate task_params for xid=0x%x\n",
+ i);
+ goto mem_err;
+ }
+
+ /*
+ * Allocate scatter/gather list info to pass to f/w init
+ * functions.
+ */
+ io_req->sgl_task_params = kzalloc(
+ sizeof(struct scsi_sgl_task_params), GFP_KERNEL);
+ if (!io_req->sgl_task_params) {
+ QEDF_ERR(&(qedf->dbg_ctx),
+ "Failed to allocate sgl_task_params for xid=0x%x\n",
+ i);
+ goto mem_err;
+ }
+ }
+
+ /* Allocate pool of io_bdts - one for each qedf_ioreq */
+ cmgr->io_bdt_pool = kmalloc_array(num_ios, sizeof(struct io_bdt *),
+ GFP_KERNEL);
+
+ if (!cmgr->io_bdt_pool) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc io_bdt_pool.\n");
+ goto mem_err;
+ }
+
+ for (i = 0; i < num_ios; i++) {
+ cmgr->io_bdt_pool[i] = kmalloc(sizeof(struct io_bdt),
+ GFP_KERNEL);
+ if (!cmgr->io_bdt_pool[i]) {
+ QEDF_WARN(&(qedf->dbg_ctx),
+ "Failed to alloc io_bdt_pool[%d].\n", i);
+ goto mem_err;
+ }
+ }
+
+ for (i = 0; i < num_ios; i++) {
+ bdt_info = cmgr->io_bdt_pool[i];
+ bdt_info->bd_tbl = dma_alloc_coherent(&qedf->pdev->dev,
+ QEDF_MAX_BDS_PER_CMD * sizeof(struct scsi_sge),
+ &bdt_info->bd_tbl_dma, GFP_KERNEL);
+ if (!bdt_info->bd_tbl) {
+ QEDF_WARN(&(qedf->dbg_ctx),
+ "Failed to alloc bdt_tbl[%d].\n", i);
+ goto mem_err;
+ }
+ }
+ atomic_set(&cmgr->free_list_cnt, num_ios);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "cmgr->free_list_cnt=%d.\n",
+ atomic_read(&cmgr->free_list_cnt));
+
+ return cmgr;
+
+mem_err:
+ qedf_cmd_mgr_free(cmgr);
+ return NULL;
+}
+
+struct qedf_ioreq *qedf_alloc_cmd(struct qedf_rport *fcport, u8 cmd_type)
+{
+ struct qedf_ctx *qedf = fcport->qedf;
+ struct qedf_cmd_mgr *cmd_mgr = qedf->cmd_mgr;
+ struct qedf_ioreq *io_req = NULL;
+ struct io_bdt *bd_tbl;
+ u16 xid;
+ uint32_t free_sqes;
+ int i;
+ unsigned long flags;
+
+ free_sqes = atomic_read(&fcport->free_sqes);
+
+ if (!free_sqes) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "Returning NULL, free_sqes=%d.\n",
+ free_sqes);
+ goto out_failed;
+ }
+
+ /* Limit the number of outstanding R/W tasks */
+ if ((atomic_read(&fcport->num_active_ios) >=
+ NUM_RW_TASKS_PER_CONNECTION)) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "Returning NULL, num_active_ios=%d.\n",
+ atomic_read(&fcport->num_active_ios));
+ goto out_failed;
+ }
+
+ /* Limit global TIDs certain tasks */
+ if (atomic_read(&cmd_mgr->free_list_cnt) <= GBL_RSVD_TASKS) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "Returning NULL, free_list_cnt=%d.\n",
+ atomic_read(&cmd_mgr->free_list_cnt));
+ goto out_failed;
+ }
+
+ spin_lock_irqsave(&cmd_mgr->lock, flags);
+ for (i = 0; i < FCOE_PARAMS_NUM_TASKS; i++) {
+ io_req = &cmd_mgr->cmds[cmd_mgr->idx];
+ cmd_mgr->idx++;
+ if (cmd_mgr->idx == FCOE_PARAMS_NUM_TASKS)
+ cmd_mgr->idx = 0;
+
+ /* Check to make sure command was previously freed */
+ if (!io_req->alloc)
+ break;
+ }
+
+ if (i == FCOE_PARAMS_NUM_TASKS) {
+ spin_unlock_irqrestore(&cmd_mgr->lock, flags);
+ goto out_failed;
+ }
+
+ if (test_bit(QEDF_CMD_DIRTY, &io_req->flags))
+ QEDF_ERR(&qedf->dbg_ctx,
+ "io_req found to be dirty ox_id = 0x%x.\n",
+ io_req->xid);
+
+ /* Clear any flags now that we've reallocated the xid */
+ io_req->flags = 0;
+ io_req->alloc = 1;
+ spin_unlock_irqrestore(&cmd_mgr->lock, flags);
+
+ atomic_inc(&fcport->num_active_ios);
+ atomic_dec(&fcport->free_sqes);
+ xid = io_req->xid;
+ atomic_dec(&cmd_mgr->free_list_cnt);
+
+ io_req->cmd_mgr = cmd_mgr;
+ io_req->fcport = fcport;
+
+ /* Clear any stale sc_cmd back pointer */
+ io_req->sc_cmd = NULL;
+ io_req->lun = -1;
+
+ /* Hold the io_req against deletion */
+ kref_init(&io_req->refcount); /* ID: 001 */
+ atomic_set(&io_req->state, QEDFC_CMD_ST_IO_ACTIVE);
+
+ /* Bind io_bdt for this io_req */
+ /* Have a static link between io_req and io_bdt_pool */
+ bd_tbl = io_req->bd_tbl = cmd_mgr->io_bdt_pool[xid];
+ if (bd_tbl == NULL) {
+ QEDF_ERR(&(qedf->dbg_ctx), "bd_tbl is NULL, xid=%x.\n", xid);
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ goto out_failed;
+ }
+ bd_tbl->io_req = io_req;
+ io_req->cmd_type = cmd_type;
+ io_req->tm_flags = 0;
+
+ /* Reset sequence offset data */
+ io_req->rx_buf_off = 0;
+ io_req->tx_buf_off = 0;
+ io_req->rx_id = 0xffff; /* No OX_ID */
+
+ return io_req;
+
+out_failed:
+ /* Record failure for stats and return NULL to caller */
+ qedf->alloc_failures++;
+ return NULL;
+}
+
+static void qedf_free_mp_resc(struct qedf_ioreq *io_req)
+{
+ struct qedf_mp_req *mp_req = &(io_req->mp_req);
+ struct qedf_ctx *qedf = io_req->fcport->qedf;
+ uint64_t sz = sizeof(struct scsi_sge);
+
+ /* clear tm flags */
+ if (mp_req->mp_req_bd) {
+ dma_free_coherent(&qedf->pdev->dev, sz,
+ mp_req->mp_req_bd, mp_req->mp_req_bd_dma);
+ mp_req->mp_req_bd = NULL;
+ }
+ if (mp_req->mp_resp_bd) {
+ dma_free_coherent(&qedf->pdev->dev, sz,
+ mp_req->mp_resp_bd, mp_req->mp_resp_bd_dma);
+ mp_req->mp_resp_bd = NULL;
+ }
+ if (mp_req->req_buf) {
+ dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE,
+ mp_req->req_buf, mp_req->req_buf_dma);
+ mp_req->req_buf = NULL;
+ }
+ if (mp_req->resp_buf) {
+ dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE,
+ mp_req->resp_buf, mp_req->resp_buf_dma);
+ mp_req->resp_buf = NULL;
+ }
+}
+
+void qedf_release_cmd(struct kref *ref)
+{
+ struct qedf_ioreq *io_req =
+ container_of(ref, struct qedf_ioreq, refcount);
+ struct qedf_cmd_mgr *cmd_mgr = io_req->cmd_mgr;
+ struct qedf_rport *fcport = io_req->fcport;
+ unsigned long flags;
+
+ if (io_req->cmd_type == QEDF_SCSI_CMD) {
+ QEDF_WARN(&fcport->qedf->dbg_ctx,
+ "Cmd released called without scsi_done called, io_req %p xid=0x%x.\n",
+ io_req, io_req->xid);
+ WARN_ON(io_req->sc_cmd);
+ }
+
+ if (io_req->cmd_type == QEDF_ELS ||
+ io_req->cmd_type == QEDF_TASK_MGMT_CMD)
+ qedf_free_mp_resc(io_req);
+
+ atomic_inc(&cmd_mgr->free_list_cnt);
+ atomic_dec(&fcport->num_active_ios);
+ atomic_set(&io_req->state, QEDF_CMD_ST_INACTIVE);
+ if (atomic_read(&fcport->num_active_ios) < 0) {
+ QEDF_WARN(&(fcport->qedf->dbg_ctx), "active_ios < 0.\n");
+ WARN_ON(1);
+ }
+
+ /* Increment task retry identifier now that the request is released */
+ io_req->task_retry_identifier++;
+ io_req->fcport = NULL;
+
+ clear_bit(QEDF_CMD_DIRTY, &io_req->flags);
+ io_req->cpu = 0;
+ spin_lock_irqsave(&cmd_mgr->lock, flags);
+ io_req->fcport = NULL;
+ io_req->alloc = 0;
+ spin_unlock_irqrestore(&cmd_mgr->lock, flags);
+}
+
+static int qedf_map_sg(struct qedf_ioreq *io_req)
+{
+ struct scsi_cmnd *sc = io_req->sc_cmd;
+ struct Scsi_Host *host = sc->device->host;
+ struct fc_lport *lport = shost_priv(host);
+ struct qedf_ctx *qedf = lport_priv(lport);
+ struct scsi_sge *bd = io_req->bd_tbl->bd_tbl;
+ struct scatterlist *sg;
+ int byte_count = 0;
+ int sg_count = 0;
+ int bd_count = 0;
+ u32 sg_len;
+ u64 addr;
+ int i = 0;
+
+ sg_count = dma_map_sg(&qedf->pdev->dev, scsi_sglist(sc),
+ scsi_sg_count(sc), sc->sc_data_direction);
+ sg = scsi_sglist(sc);
+
+ io_req->sge_type = QEDF_IOREQ_UNKNOWN_SGE;
+
+ if (sg_count <= 8 || io_req->io_req_flags == QEDF_READ)
+ io_req->sge_type = QEDF_IOREQ_FAST_SGE;
+
+ scsi_for_each_sg(sc, sg, sg_count, i) {
+ sg_len = (u32)sg_dma_len(sg);
+ addr = (u64)sg_dma_address(sg);
+
+ /*
+ * Intermediate s/g element so check if start address
+ * is page aligned. Only required for writes and only if the
+ * number of scatter/gather elements is 8 or more.
+ */
+ if (io_req->sge_type == QEDF_IOREQ_UNKNOWN_SGE && (i) &&
+ (i != (sg_count - 1)) && sg_len < QEDF_PAGE_SIZE)
+ io_req->sge_type = QEDF_IOREQ_SLOW_SGE;
+
+ bd[bd_count].sge_addr.lo = cpu_to_le32(U64_LO(addr));
+ bd[bd_count].sge_addr.hi = cpu_to_le32(U64_HI(addr));
+ bd[bd_count].sge_len = cpu_to_le32(sg_len);
+
+ bd_count++;
+ byte_count += sg_len;
+ }
+
+ /* To catch a case where FAST and SLOW nothing is set, set FAST */
+ if (io_req->sge_type == QEDF_IOREQ_UNKNOWN_SGE)
+ io_req->sge_type = QEDF_IOREQ_FAST_SGE;
+
+ if (byte_count != scsi_bufflen(sc))
+ QEDF_ERR(&(qedf->dbg_ctx), "byte_count = %d != "
+ "scsi_bufflen = %d, task_id = 0x%x.\n", byte_count,
+ scsi_bufflen(sc), io_req->xid);
+
+ return bd_count;
+}
+
+static int qedf_build_bd_list_from_sg(struct qedf_ioreq *io_req)
+{
+ struct scsi_cmnd *sc = io_req->sc_cmd;
+ struct scsi_sge *bd = io_req->bd_tbl->bd_tbl;
+ int bd_count;
+
+ if (scsi_sg_count(sc)) {
+ bd_count = qedf_map_sg(io_req);
+ if (bd_count == 0)
+ return -ENOMEM;
+ } else {
+ bd_count = 0;
+ bd[0].sge_addr.lo = bd[0].sge_addr.hi = 0;
+ bd[0].sge_len = 0;
+ }
+ io_req->bd_tbl->bd_valid = bd_count;
+
+ return 0;
+}
+
+static void qedf_build_fcp_cmnd(struct qedf_ioreq *io_req,
+ struct fcp_cmnd *fcp_cmnd)
+{
+ struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
+
+ /* fcp_cmnd is 32 bytes */
+ memset(fcp_cmnd, 0, FCP_CMND_LEN);
+
+ /* 8 bytes: SCSI LUN info */
+ if (io_req->cmd_type == QEDF_TASK_MGMT_CMD)
+ int_to_scsilun(io_req->tm_lun,
+ (struct scsi_lun *)&fcp_cmnd->fc_lun);
+ else
+ int_to_scsilun(sc_cmd->device->lun,
+ (struct scsi_lun *)&fcp_cmnd->fc_lun);
+
+ /* 4 bytes: flag info */
+ fcp_cmnd->fc_pri_ta = 0;
+ fcp_cmnd->fc_tm_flags = io_req->tm_flags;
+ fcp_cmnd->fc_flags = io_req->io_req_flags;
+ fcp_cmnd->fc_cmdref = 0;
+
+ /* Populate data direction */
+ if (io_req->cmd_type == QEDF_TASK_MGMT_CMD) {
+ fcp_cmnd->fc_flags |= FCP_CFL_RDDATA;
+ } else {
+ if (sc_cmd->sc_data_direction == DMA_TO_DEVICE)
+ fcp_cmnd->fc_flags |= FCP_CFL_WRDATA;
+ else if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE)
+ fcp_cmnd->fc_flags |= FCP_CFL_RDDATA;
+ }
+
+ fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
+
+ /* 16 bytes: CDB information */
+ if (io_req->cmd_type != QEDF_TASK_MGMT_CMD)
+ memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len);
+
+ /* 4 bytes: FCP data length */
+ fcp_cmnd->fc_dl = htonl(io_req->data_xfer_len);
+}
+
+static void qedf_init_task(struct qedf_rport *fcport, struct fc_lport *lport,
+ struct qedf_ioreq *io_req, struct fcoe_task_context *task_ctx,
+ struct fcoe_wqe *sqe)
+{
+ enum fcoe_task_type task_type;
+ struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
+ struct io_bdt *bd_tbl = io_req->bd_tbl;
+ u8 fcp_cmnd[32];
+ u32 tmp_fcp_cmnd[8];
+ int bd_count = 0;
+ struct qedf_ctx *qedf = fcport->qedf;
+ uint16_t cq_idx = smp_processor_id() % qedf->num_queues;
+ struct regpair sense_data_buffer_phys_addr;
+ u32 tx_io_size = 0;
+ u32 rx_io_size = 0;
+ int i, cnt;
+
+ /* Note init_initiator_rw_fcoe_task memsets the task context */
+ io_req->task = task_ctx;
+ memset(task_ctx, 0, sizeof(struct fcoe_task_context));
+ memset(io_req->task_params, 0, sizeof(struct fcoe_task_params));
+ memset(io_req->sgl_task_params, 0, sizeof(struct scsi_sgl_task_params));
+
+ /* Set task type bassed on DMA directio of command */
+ if (io_req->cmd_type == QEDF_TASK_MGMT_CMD) {
+ task_type = FCOE_TASK_TYPE_READ_INITIATOR;
+ } else {
+ if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ task_type = FCOE_TASK_TYPE_WRITE_INITIATOR;
+ tx_io_size = io_req->data_xfer_len;
+ } else {
+ task_type = FCOE_TASK_TYPE_READ_INITIATOR;
+ rx_io_size = io_req->data_xfer_len;
+ }
+ }
+
+ /* Setup the fields for fcoe_task_params */
+ io_req->task_params->context = task_ctx;
+ io_req->task_params->sqe = sqe;
+ io_req->task_params->task_type = task_type;
+ io_req->task_params->tx_io_size = tx_io_size;
+ io_req->task_params->rx_io_size = rx_io_size;
+ io_req->task_params->conn_cid = fcport->fw_cid;
+ io_req->task_params->itid = io_req->xid;
+ io_req->task_params->cq_rss_number = cq_idx;
+ io_req->task_params->is_tape_device = fcport->dev_type;
+
+ /* Fill in information for scatter/gather list */
+ if (io_req->cmd_type != QEDF_TASK_MGMT_CMD) {
+ bd_count = bd_tbl->bd_valid;
+ io_req->sgl_task_params->sgl = bd_tbl->bd_tbl;
+ io_req->sgl_task_params->sgl_phys_addr.lo =
+ U64_LO(bd_tbl->bd_tbl_dma);
+ io_req->sgl_task_params->sgl_phys_addr.hi =
+ U64_HI(bd_tbl->bd_tbl_dma);
+ io_req->sgl_task_params->num_sges = bd_count;
+ io_req->sgl_task_params->total_buffer_size =
+ scsi_bufflen(io_req->sc_cmd);
+ if (io_req->sge_type == QEDF_IOREQ_SLOW_SGE)
+ io_req->sgl_task_params->small_mid_sge = 1;
+ else
+ io_req->sgl_task_params->small_mid_sge = 0;
+ }
+
+ /* Fill in physical address of sense buffer */
+ sense_data_buffer_phys_addr.lo = U64_LO(io_req->sense_buffer_dma);
+ sense_data_buffer_phys_addr.hi = U64_HI(io_req->sense_buffer_dma);
+
+ /* fill FCP_CMND IU */
+ qedf_build_fcp_cmnd(io_req, (struct fcp_cmnd *)tmp_fcp_cmnd);
+
+ /* Swap fcp_cmnd since FC is big endian */
+ cnt = sizeof(struct fcp_cmnd) / sizeof(u32);
+ for (i = 0; i < cnt; i++) {
+ tmp_fcp_cmnd[i] = cpu_to_be32(tmp_fcp_cmnd[i]);
+ }
+ memcpy(fcp_cmnd, tmp_fcp_cmnd, sizeof(struct fcp_cmnd));
+
+ init_initiator_rw_fcoe_task(io_req->task_params,
+ io_req->sgl_task_params,
+ sense_data_buffer_phys_addr,
+ io_req->task_retry_identifier, fcp_cmnd);
+
+ /* Increment SGL type counters */
+ if (io_req->sge_type == QEDF_IOREQ_SLOW_SGE)
+ qedf->slow_sge_ios++;
+ else
+ qedf->fast_sge_ios++;
+}
+
+void qedf_init_mp_task(struct qedf_ioreq *io_req,
+ struct fcoe_task_context *task_ctx, struct fcoe_wqe *sqe)
+{
+ struct qedf_mp_req *mp_req = &(io_req->mp_req);
+ struct qedf_rport *fcport = io_req->fcport;
+ struct qedf_ctx *qedf = io_req->fcport->qedf;
+ struct fc_frame_header *fc_hdr;
+ struct fcoe_tx_mid_path_params task_fc_hdr;
+ struct scsi_sgl_task_params tx_sgl_task_params;
+ struct scsi_sgl_task_params rx_sgl_task_params;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Initializing MP task for cmd_type=%d\n",
+ io_req->cmd_type);
+
+ qedf->control_requests++;
+
+ memset(&tx_sgl_task_params, 0, sizeof(struct scsi_sgl_task_params));
+ memset(&rx_sgl_task_params, 0, sizeof(struct scsi_sgl_task_params));
+ memset(task_ctx, 0, sizeof(struct fcoe_task_context));
+ memset(&task_fc_hdr, 0, sizeof(struct fcoe_tx_mid_path_params));
+
+ /* Setup the task from io_req for easy reference */
+ io_req->task = task_ctx;
+
+ /* Setup the fields for fcoe_task_params */
+ io_req->task_params->context = task_ctx;
+ io_req->task_params->sqe = sqe;
+ io_req->task_params->task_type = FCOE_TASK_TYPE_MIDPATH;
+ io_req->task_params->tx_io_size = io_req->data_xfer_len;
+ /* rx_io_size tells the f/w how large a response buffer we have */
+ io_req->task_params->rx_io_size = PAGE_SIZE;
+ io_req->task_params->conn_cid = fcport->fw_cid;
+ io_req->task_params->itid = io_req->xid;
+ /* Return middle path commands on CQ 0 */
+ io_req->task_params->cq_rss_number = 0;
+ io_req->task_params->is_tape_device = fcport->dev_type;
+
+ fc_hdr = &(mp_req->req_fc_hdr);
+ /* Set OX_ID and RX_ID based on driver task id */
+ fc_hdr->fh_ox_id = io_req->xid;
+ fc_hdr->fh_rx_id = htons(0xffff);
+
+ /* Set up FC header information */
+ task_fc_hdr.parameter = fc_hdr->fh_parm_offset;
+ task_fc_hdr.r_ctl = fc_hdr->fh_r_ctl;
+ task_fc_hdr.type = fc_hdr->fh_type;
+ task_fc_hdr.cs_ctl = fc_hdr->fh_cs_ctl;
+ task_fc_hdr.df_ctl = fc_hdr->fh_df_ctl;
+ task_fc_hdr.rx_id = fc_hdr->fh_rx_id;
+ task_fc_hdr.ox_id = fc_hdr->fh_ox_id;
+
+ /* Set up s/g list parameters for request buffer */
+ tx_sgl_task_params.sgl = mp_req->mp_req_bd;
+ tx_sgl_task_params.sgl_phys_addr.lo = U64_LO(mp_req->mp_req_bd_dma);
+ tx_sgl_task_params.sgl_phys_addr.hi = U64_HI(mp_req->mp_req_bd_dma);
+ tx_sgl_task_params.num_sges = 1;
+ /* Set PAGE_SIZE for now since sg element is that size ??? */
+ tx_sgl_task_params.total_buffer_size = io_req->data_xfer_len;
+ tx_sgl_task_params.small_mid_sge = 0;
+
+ /* Set up s/g list parameters for request buffer */
+ rx_sgl_task_params.sgl = mp_req->mp_resp_bd;
+ rx_sgl_task_params.sgl_phys_addr.lo = U64_LO(mp_req->mp_resp_bd_dma);
+ rx_sgl_task_params.sgl_phys_addr.hi = U64_HI(mp_req->mp_resp_bd_dma);
+ rx_sgl_task_params.num_sges = 1;
+ /* Set PAGE_SIZE for now since sg element is that size ??? */
+ rx_sgl_task_params.total_buffer_size = PAGE_SIZE;
+ rx_sgl_task_params.small_mid_sge = 0;
+
+
+ /*
+ * Last arg is 0 as previous code did not set that we wanted the
+ * fc header information.
+ */
+ init_initiator_midpath_unsolicited_fcoe_task(io_req->task_params,
+ &task_fc_hdr,
+ &tx_sgl_task_params,
+ &rx_sgl_task_params, 0);
+}
+
+/* Presumed that fcport->rport_lock is held */
+u16 qedf_get_sqe_idx(struct qedf_rport *fcport)
+{
+ uint16_t total_sqe = (fcport->sq_mem_size)/(sizeof(struct fcoe_wqe));
+ u16 rval;
+
+ rval = fcport->sq_prod_idx;
+
+ /* Adjust ring index */
+ fcport->sq_prod_idx++;
+ fcport->fw_sq_prod_idx++;
+ if (fcport->sq_prod_idx == total_sqe)
+ fcport->sq_prod_idx = 0;
+
+ return rval;
+}
+
+void qedf_ring_doorbell(struct qedf_rport *fcport)
+{
+ struct fcoe_db_data dbell = { 0 };
+
+ dbell.agg_flags = 0;
+
+ dbell.params |= DB_DEST_XCM << FCOE_DB_DATA_DEST_SHIFT;
+ dbell.params |= DB_AGG_CMD_SET << FCOE_DB_DATA_AGG_CMD_SHIFT;
+ dbell.params |= DQ_XCM_FCOE_SQ_PROD_CMD <<
+ FCOE_DB_DATA_AGG_VAL_SEL_SHIFT;
+
+ dbell.sq_prod = fcport->fw_sq_prod_idx;
+ /* wmb makes sure that the BDs data is updated before updating the
+ * producer, otherwise FW may read old data from the BDs.
+ */
+ wmb();
+ barrier();
+ writel(*(u32 *)&dbell, fcport->p_doorbell);
+ /*
+ * Fence required to flush the write combined buffer, since another
+ * CPU may write to the same doorbell address and data may be lost
+ * due to relaxed order nature of write combined bar.
+ */
+ wmb();
+}
+
+static void qedf_trace_io(struct qedf_rport *fcport, struct qedf_ioreq *io_req,
+ int8_t direction)
+{
+ struct qedf_ctx *qedf = fcport->qedf;
+ struct qedf_io_log *io_log;
+ struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qedf->io_trace_lock, flags);
+
+ io_log = &qedf->io_trace_buf[qedf->io_trace_idx];
+ io_log->direction = direction;
+ io_log->task_id = io_req->xid;
+ io_log->port_id = fcport->rdata->ids.port_id;
+ io_log->lun = sc_cmd->device->lun;
+ io_log->op = sc_cmd->cmnd[0];
+ io_log->lba[0] = sc_cmd->cmnd[2];
+ io_log->lba[1] = sc_cmd->cmnd[3];
+ io_log->lba[2] = sc_cmd->cmnd[4];
+ io_log->lba[3] = sc_cmd->cmnd[5];
+ io_log->bufflen = scsi_bufflen(sc_cmd);
+ io_log->sg_count = scsi_sg_count(sc_cmd);
+ io_log->result = sc_cmd->result;
+ io_log->jiffies = jiffies;
+ io_log->refcount = kref_read(&io_req->refcount);
+
+ if (direction == QEDF_IO_TRACE_REQ) {
+ /* For requests we only care abot the submission CPU */
+ io_log->req_cpu = io_req->cpu;
+ io_log->int_cpu = 0;
+ io_log->rsp_cpu = 0;
+ } else if (direction == QEDF_IO_TRACE_RSP) {
+ io_log->req_cpu = io_req->cpu;
+ io_log->int_cpu = io_req->int_cpu;
+ io_log->rsp_cpu = smp_processor_id();
+ }
+
+ io_log->sge_type = io_req->sge_type;
+
+ qedf->io_trace_idx++;
+ if (qedf->io_trace_idx == QEDF_IO_TRACE_SIZE)
+ qedf->io_trace_idx = 0;
+
+ spin_unlock_irqrestore(&qedf->io_trace_lock, flags);
+}
+
+int qedf_post_io_req(struct qedf_rport *fcport, struct qedf_ioreq *io_req)
+{
+ struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
+ struct Scsi_Host *host = sc_cmd->device->host;
+ struct fc_lport *lport = shost_priv(host);
+ struct qedf_ctx *qedf = lport_priv(lport);
+ struct fcoe_task_context *task_ctx;
+ u16 xid;
+ struct fcoe_wqe *sqe;
+ u16 sqe_idx;
+
+ /* Initialize rest of io_req fileds */
+ io_req->data_xfer_len = scsi_bufflen(sc_cmd);
+ qedf_priv(sc_cmd)->io_req = io_req;
+ io_req->sge_type = QEDF_IOREQ_FAST_SGE; /* Assume fast SGL by default */
+
+ /* Record which cpu this request is associated with */
+ io_req->cpu = smp_processor_id();
+
+ if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ io_req->io_req_flags = QEDF_READ;
+ qedf->input_requests++;
+ } else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ io_req->io_req_flags = QEDF_WRITE;
+ qedf->output_requests++;
+ } else {
+ io_req->io_req_flags = 0;
+ qedf->control_requests++;
+ }
+
+ xid = io_req->xid;
+
+ /* Build buffer descriptor list for firmware from sg list */
+ if (qedf_build_bd_list_from_sg(io_req)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "BD list creation failed.\n");
+ /* Release cmd will release io_req, but sc_cmd is assigned */
+ io_req->sc_cmd = NULL;
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ return -EAGAIN;
+ }
+
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) ||
+ test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Session not offloaded yet.\n");
+ /* Release cmd will release io_req, but sc_cmd is assigned */
+ io_req->sc_cmd = NULL;
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ return -EINVAL;
+ }
+
+ /* Record LUN number for later use if we need them */
+ io_req->lun = (int)sc_cmd->device->lun;
+
+ /* Obtain free SQE */
+ sqe_idx = qedf_get_sqe_idx(fcport);
+ sqe = &fcport->sq[sqe_idx];
+ memset(sqe, 0, sizeof(struct fcoe_wqe));
+
+ /* Get the task context */
+ task_ctx = qedf_get_task_mem(&qedf->tasks, xid);
+ if (!task_ctx) {
+ QEDF_WARN(&(qedf->dbg_ctx), "task_ctx is NULL, xid=%d.\n",
+ xid);
+ /* Release cmd will release io_req, but sc_cmd is assigned */
+ io_req->sc_cmd = NULL;
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ return -EINVAL;
+ }
+
+ qedf_init_task(fcport, lport, io_req, task_ctx, sqe);
+
+ /* Ring doorbell */
+ qedf_ring_doorbell(fcport);
+
+ /* Set that command is with the firmware now */
+ set_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+
+ if (qedf_io_tracing && io_req->sc_cmd)
+ qedf_trace_io(fcport, io_req, QEDF_IO_TRACE_REQ);
+
+ return false;
+}
+
+int
+qedf_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd)
+{
+ struct fc_lport *lport = shost_priv(host);
+ struct qedf_ctx *qedf = lport_priv(lport);
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct qedf_rport *fcport;
+ struct qedf_ioreq *io_req;
+ int rc = 0;
+ int rval;
+ unsigned long flags = 0;
+ int num_sgs = 0;
+
+ num_sgs = scsi_sg_count(sc_cmd);
+ if (scsi_sg_count(sc_cmd) > QEDF_MAX_BDS_PER_CMD) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Number of SG elements %d exceeds what hardware limitation of %d.\n",
+ num_sgs, QEDF_MAX_BDS_PER_CMD);
+ sc_cmd->result = DID_ERROR;
+ scsi_done(sc_cmd);
+ return 0;
+ }
+
+ if (test_bit(QEDF_UNLOADING, &qedf->flags) ||
+ test_bit(QEDF_DBG_STOP_IO, &qedf->flags)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Returning DNC as unloading or stop io, flags 0x%lx.\n",
+ qedf->flags);
+ sc_cmd->result = DID_NO_CONNECT << 16;
+ scsi_done(sc_cmd);
+ return 0;
+ }
+
+ if (!qedf->pdev->msix_enabled) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "Completing sc_cmd=%p DID_NO_CONNECT as MSI-X is not enabled.\n",
+ sc_cmd);
+ sc_cmd->result = DID_NO_CONNECT << 16;
+ scsi_done(sc_cmd);
+ return 0;
+ }
+
+ rval = fc_remote_port_chkready(rport);
+ if (rval) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "fc_remote_port_chkready failed=0x%x for port_id=0x%06x.\n",
+ rval, rport->port_id);
+ sc_cmd->result = rval;
+ scsi_done(sc_cmd);
+ return 0;
+ }
+
+ /* Retry command if we are doing a qed drain operation */
+ if (test_bit(QEDF_DRAIN_ACTIVE, &qedf->flags)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, "Drain active.\n");
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto exit_qcmd;
+ }
+
+ if (lport->state != LPORT_ST_READY ||
+ atomic_read(&qedf->link_state) != QEDF_LINK_UP) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, "Link down.\n");
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto exit_qcmd;
+ }
+
+ /* rport and tgt are allocated together, so tgt should be non-NULL */
+ fcport = (struct qedf_rport *)&rp[1];
+
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) ||
+ test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ /*
+ * Session is not offloaded yet. Let SCSI-ml retry
+ * the command.
+ */
+ rc = SCSI_MLQUEUE_TARGET_BUSY;
+ goto exit_qcmd;
+ }
+
+ atomic_inc(&fcport->ios_to_queue);
+
+ if (fcport->retry_delay_timestamp) {
+ /* Take fcport->rport_lock for resetting the delay_timestamp */
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+ if (time_after(jiffies, fcport->retry_delay_timestamp)) {
+ fcport->retry_delay_timestamp = 0;
+ } else {
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ /* If retry_delay timer is active, flow off the ML */
+ rc = SCSI_MLQUEUE_TARGET_BUSY;
+ atomic_dec(&fcport->ios_to_queue);
+ goto exit_qcmd;
+ }
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ }
+
+ io_req = qedf_alloc_cmd(fcport, QEDF_SCSI_CMD);
+ if (!io_req) {
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ atomic_dec(&fcport->ios_to_queue);
+ goto exit_qcmd;
+ }
+
+ io_req->sc_cmd = sc_cmd;
+
+ /* Take fcport->rport_lock for posting to fcport send queue */
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+ if (qedf_post_io_req(fcport, io_req)) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Unable to post io_req\n");
+ /* Return SQE to pool */
+ atomic_inc(&fcport->free_sqes);
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ }
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ atomic_dec(&fcport->ios_to_queue);
+
+exit_qcmd:
+ return rc;
+}
+
+static void qedf_parse_fcp_rsp(struct qedf_ioreq *io_req,
+ struct fcoe_cqe_rsp_info *fcp_rsp)
+{
+ struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
+ struct qedf_ctx *qedf = io_req->fcport->qedf;
+ u8 rsp_flags = fcp_rsp->rsp_flags.flags;
+ int fcp_sns_len = 0;
+ int fcp_rsp_len = 0;
+ uint8_t *rsp_info, *sense_data;
+
+ io_req->fcp_status = FC_GOOD;
+ io_req->fcp_resid = 0;
+ if (rsp_flags & (FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER |
+ FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER))
+ io_req->fcp_resid = fcp_rsp->fcp_resid;
+
+ io_req->scsi_comp_flags = rsp_flags;
+ io_req->cdb_status = fcp_rsp->scsi_status_code;
+
+ if (rsp_flags &
+ FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID)
+ fcp_rsp_len = fcp_rsp->fcp_rsp_len;
+
+ if (rsp_flags &
+ FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID)
+ fcp_sns_len = fcp_rsp->fcp_sns_len;
+
+ io_req->fcp_rsp_len = fcp_rsp_len;
+ io_req->fcp_sns_len = fcp_sns_len;
+ rsp_info = sense_data = io_req->sense_buffer;
+
+ /* fetch fcp_rsp_code */
+ if ((fcp_rsp_len == 4) || (fcp_rsp_len == 8)) {
+ /* Only for task management function */
+ io_req->fcp_rsp_code = rsp_info[3];
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "fcp_rsp_code = %d\n", io_req->fcp_rsp_code);
+ /* Adjust sense-data location. */
+ sense_data += fcp_rsp_len;
+ }
+
+ if (fcp_sns_len > SCSI_SENSE_BUFFERSIZE) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "Truncating sense buffer\n");
+ fcp_sns_len = SCSI_SENSE_BUFFERSIZE;
+ }
+
+ /* The sense buffer can be NULL for TMF commands */
+ if (sc_cmd && sc_cmd->sense_buffer) {
+ memset(sc_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ if (fcp_sns_len)
+ memcpy(sc_cmd->sense_buffer, sense_data,
+ fcp_sns_len);
+ }
+}
+
+static void qedf_unmap_sg_list(struct qedf_ctx *qedf, struct qedf_ioreq *io_req)
+{
+ struct scsi_cmnd *sc = io_req->sc_cmd;
+
+ if (io_req->bd_tbl->bd_valid && sc && scsi_sg_count(sc)) {
+ dma_unmap_sg(&qedf->pdev->dev, scsi_sglist(sc),
+ scsi_sg_count(sc), sc->sc_data_direction);
+ io_req->bd_tbl->bd_valid = 0;
+ }
+}
+
+void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req)
+{
+ struct scsi_cmnd *sc_cmd;
+ struct fcoe_cqe_rsp_info *fcp_rsp;
+ struct qedf_rport *fcport;
+ int refcount;
+ u16 scope, qualifier = 0;
+ u8 fw_residual_flag = 0;
+ unsigned long flags = 0;
+ u16 chk_scope = 0;
+
+ if (!io_req)
+ return;
+ if (!cqe)
+ return;
+
+ if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) ||
+ test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags) ||
+ test_bit(QEDF_CMD_IN_ABORT, &io_req->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "io_req xid=0x%x already in cleanup or abort processing or already completed.\n",
+ io_req->xid);
+ return;
+ }
+
+ sc_cmd = io_req->sc_cmd;
+ fcp_rsp = &cqe->cqe_info.rsp_info;
+
+ if (!sc_cmd) {
+ QEDF_WARN(&(qedf->dbg_ctx), "sc_cmd is NULL!\n");
+ return;
+ }
+
+ if (!qedf_priv(sc_cmd)->io_req) {
+ QEDF_WARN(&(qedf->dbg_ctx),
+ "io_req is NULL, returned in another context.\n");
+ return;
+ }
+
+ if (!sc_cmd->device) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Device for sc_cmd %p is NULL.\n", sc_cmd);
+ return;
+ }
+
+ if (!scsi_cmd_to_rq(sc_cmd)->q) {
+ QEDF_WARN(&(qedf->dbg_ctx), "request->q is NULL so request "
+ "is not valid, sc_cmd=%p.\n", sc_cmd);
+ return;
+ }
+
+ fcport = io_req->fcport;
+
+ /*
+ * When flush is active, let the cmds be completed from the cleanup
+ * context
+ */
+ if (test_bit(QEDF_RPORT_IN_TARGET_RESET, &fcport->flags) ||
+ (test_bit(QEDF_RPORT_IN_LUN_RESET, &fcport->flags) &&
+ sc_cmd->device->lun == (u64)fcport->lun_reset_lun)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Dropping good completion xid=0x%x as fcport is flushing",
+ io_req->xid);
+ return;
+ }
+
+ qedf_parse_fcp_rsp(io_req, fcp_rsp);
+
+ qedf_unmap_sg_list(qedf, io_req);
+
+ /* Check for FCP transport error */
+ if (io_req->fcp_rsp_len > 3 && io_req->fcp_rsp_code) {
+ QEDF_ERR(&(qedf->dbg_ctx),
+ "FCP I/O protocol failure xid=0x%x fcp_rsp_len=%d "
+ "fcp_rsp_code=%d.\n", io_req->xid, io_req->fcp_rsp_len,
+ io_req->fcp_rsp_code);
+ sc_cmd->result = DID_BUS_BUSY << 16;
+ goto out;
+ }
+
+ fw_residual_flag = GET_FIELD(cqe->cqe_info.rsp_info.fw_error_flags,
+ FCOE_CQE_RSP_INFO_FW_UNDERRUN);
+ if (fw_residual_flag) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Firmware detected underrun: xid=0x%x fcp_rsp.flags=0x%02x fcp_resid=%d fw_residual=0x%x lba=%02x%02x%02x%02x.\n",
+ io_req->xid, fcp_rsp->rsp_flags.flags,
+ io_req->fcp_resid,
+ cqe->cqe_info.rsp_info.fw_residual, sc_cmd->cmnd[2],
+ sc_cmd->cmnd[3], sc_cmd->cmnd[4], sc_cmd->cmnd[5]);
+
+ if (io_req->cdb_status == 0)
+ sc_cmd->result = (DID_ERROR << 16) | io_req->cdb_status;
+ else
+ sc_cmd->result = (DID_OK << 16) | io_req->cdb_status;
+
+ /*
+ * Set resid to the whole buffer length so we won't try to resue
+ * any previously data.
+ */
+ scsi_set_resid(sc_cmd, scsi_bufflen(sc_cmd));
+ goto out;
+ }
+
+ switch (io_req->fcp_status) {
+ case FC_GOOD:
+ if (io_req->cdb_status == 0) {
+ /* Good I/O completion */
+ sc_cmd->result = DID_OK << 16;
+ } else {
+ refcount = kref_read(&io_req->refcount);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "%d:0:%d:%lld xid=0x%0x op=0x%02x "
+ "lba=%02x%02x%02x%02x cdb_status=%d "
+ "fcp_resid=0x%x refcount=%d.\n",
+ qedf->lport->host->host_no, sc_cmd->device->id,
+ sc_cmd->device->lun, io_req->xid,
+ sc_cmd->cmnd[0], sc_cmd->cmnd[2], sc_cmd->cmnd[3],
+ sc_cmd->cmnd[4], sc_cmd->cmnd[5],
+ io_req->cdb_status, io_req->fcp_resid,
+ refcount);
+ sc_cmd->result = (DID_OK << 16) | io_req->cdb_status;
+
+ if (io_req->cdb_status == SAM_STAT_TASK_SET_FULL ||
+ io_req->cdb_status == SAM_STAT_BUSY) {
+ /*
+ * Check whether we need to set retry_delay at
+ * all based on retry_delay module parameter
+ * and the status qualifier.
+ */
+
+ /* Upper 2 bits */
+ scope = fcp_rsp->retry_delay_timer & 0xC000;
+ /* Lower 14 bits */
+ qualifier = fcp_rsp->retry_delay_timer & 0x3FFF;
+
+ if (qedf_retry_delay)
+ chk_scope = 1;
+ /* Record stats */
+ if (io_req->cdb_status ==
+ SAM_STAT_TASK_SET_FULL)
+ qedf->task_set_fulls++;
+ else
+ qedf->busy++;
+ }
+ }
+ if (io_req->fcp_resid)
+ scsi_set_resid(sc_cmd, io_req->fcp_resid);
+
+ if (chk_scope == 1) {
+ if ((scope == 1 || scope == 2) &&
+ (qualifier > 0 && qualifier <= 0x3FEF)) {
+ /* Check we don't go over the max */
+ if (qualifier > QEDF_RETRY_DELAY_MAX) {
+ qualifier = QEDF_RETRY_DELAY_MAX;
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "qualifier = %d\n",
+ (fcp_rsp->retry_delay_timer &
+ 0x3FFF));
+ }
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Scope = %d and qualifier = %d",
+ scope, qualifier);
+ /* Take fcport->rport_lock to
+ * update the retry_delay_timestamp
+ */
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+ fcport->retry_delay_timestamp =
+ jiffies + (qualifier * HZ / 10);
+ spin_unlock_irqrestore(&fcport->rport_lock,
+ flags);
+
+ } else {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "combination of scope = %d and qualifier = %d is not handled in qedf.\n",
+ scope, qualifier);
+ }
+ }
+ break;
+ default:
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "fcp_status=%d.\n",
+ io_req->fcp_status);
+ break;
+ }
+
+out:
+ if (qedf_io_tracing)
+ qedf_trace_io(fcport, io_req, QEDF_IO_TRACE_RSP);
+
+ /*
+ * We wait till the end of the function to clear the
+ * outstanding bit in case we need to send an abort
+ */
+ clear_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+
+ io_req->sc_cmd = NULL;
+ qedf_priv(sc_cmd)->io_req = NULL;
+ scsi_done(sc_cmd);
+ kref_put(&io_req->refcount, qedf_release_cmd);
+}
+
+/* Return a SCSI command in some other context besides a normal completion */
+void qedf_scsi_done(struct qedf_ctx *qedf, struct qedf_ioreq *io_req,
+ int result)
+{
+ struct scsi_cmnd *sc_cmd;
+ int refcount;
+
+ if (!io_req) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, "io_req is NULL\n");
+ return;
+ }
+
+ if (test_and_set_bit(QEDF_CMD_ERR_SCSI_DONE, &io_req->flags)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "io_req:%p scsi_done handling already done\n",
+ io_req);
+ return;
+ }
+
+ /*
+ * We will be done with this command after this call so clear the
+ * outstanding bit.
+ */
+ clear_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+
+ sc_cmd = io_req->sc_cmd;
+
+ if (!sc_cmd) {
+ QEDF_WARN(&(qedf->dbg_ctx), "sc_cmd is NULL!\n");
+ return;
+ }
+
+ if (!virt_addr_valid(sc_cmd)) {
+ QEDF_ERR(&qedf->dbg_ctx, "sc_cmd=%p is not valid.", sc_cmd);
+ goto bad_scsi_ptr;
+ }
+
+ if (!qedf_priv(sc_cmd)->io_req) {
+ QEDF_WARN(&(qedf->dbg_ctx),
+ "io_req is NULL, returned in another context.\n");
+ return;
+ }
+
+ if (!sc_cmd->device) {
+ QEDF_ERR(&qedf->dbg_ctx, "Device for sc_cmd %p is NULL.\n",
+ sc_cmd);
+ goto bad_scsi_ptr;
+ }
+
+ if (!virt_addr_valid(sc_cmd->device)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Device pointer for sc_cmd %p is bad.\n", sc_cmd);
+ goto bad_scsi_ptr;
+ }
+
+ if (!sc_cmd->sense_buffer) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "sc_cmd->sense_buffer for sc_cmd %p is NULL.\n",
+ sc_cmd);
+ goto bad_scsi_ptr;
+ }
+
+ if (!virt_addr_valid(sc_cmd->sense_buffer)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "sc_cmd->sense_buffer for sc_cmd %p is bad.\n",
+ sc_cmd);
+ goto bad_scsi_ptr;
+ }
+
+ qedf_unmap_sg_list(qedf, io_req);
+
+ sc_cmd->result = result << 16;
+ refcount = kref_read(&io_req->refcount);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "%d:0:%d:%lld: Completing "
+ "sc_cmd=%p result=0x%08x op=0x%02x lba=0x%02x%02x%02x%02x, "
+ "allowed=%d retries=%d refcount=%d.\n",
+ qedf->lport->host->host_no, sc_cmd->device->id,
+ sc_cmd->device->lun, sc_cmd, sc_cmd->result, sc_cmd->cmnd[0],
+ sc_cmd->cmnd[2], sc_cmd->cmnd[3], sc_cmd->cmnd[4],
+ sc_cmd->cmnd[5], sc_cmd->allowed, sc_cmd->retries,
+ refcount);
+
+ /*
+ * Set resid to the whole buffer length so we won't try to resue any
+ * previously read data
+ */
+ scsi_set_resid(sc_cmd, scsi_bufflen(sc_cmd));
+
+ if (qedf_io_tracing)
+ qedf_trace_io(io_req->fcport, io_req, QEDF_IO_TRACE_RSP);
+
+ io_req->sc_cmd = NULL;
+ qedf_priv(sc_cmd)->io_req = NULL;
+ scsi_done(sc_cmd);
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ return;
+
+bad_scsi_ptr:
+ /*
+ * Clear the io_req->sc_cmd backpointer so we don't try to process
+ * this again
+ */
+ io_req->sc_cmd = NULL;
+ kref_put(&io_req->refcount, qedf_release_cmd); /* ID: 001 */
+}
+
+/*
+ * Handle warning type CQE completions. This is mainly used for REC timer
+ * popping.
+ */
+void qedf_process_warning_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req)
+{
+ int rval, i;
+ struct qedf_rport *fcport = io_req->fcport;
+ u64 err_warn_bit_map;
+ u8 err_warn = 0xff;
+
+ if (!cqe) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "cqe is NULL for io_req %p xid=0x%x\n",
+ io_req, io_req->xid);
+ return;
+ }
+
+ QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "Warning CQE, "
+ "xid=0x%x\n", io_req->xid);
+ QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx),
+ "err_warn_bitmap=%08x:%08x\n",
+ le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_hi),
+ le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_lo));
+ QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "tx_buff_off=%08x, "
+ "rx_buff_off=%08x, rx_id=%04x\n",
+ le32_to_cpu(cqe->cqe_info.err_info.tx_buf_off),
+ le32_to_cpu(cqe->cqe_info.err_info.rx_buf_off),
+ le32_to_cpu(cqe->cqe_info.err_info.rx_id));
+
+ /* Normalize the error bitmap value to an just an unsigned int */
+ err_warn_bit_map = (u64)
+ ((u64)cqe->cqe_info.err_info.err_warn_bitmap_hi << 32) |
+ (u64)cqe->cqe_info.err_info.err_warn_bitmap_lo;
+ for (i = 0; i < 64; i++) {
+ if (err_warn_bit_map & (u64)((u64)1 << i)) {
+ err_warn = i;
+ break;
+ }
+ }
+
+ /* Check if REC TOV expired if this is a tape device */
+ if (fcport->dev_type == QEDF_RPORT_TYPE_TAPE) {
+ if (err_warn ==
+ FCOE_WARNING_CODE_REC_TOV_TIMER_EXPIRATION) {
+ QEDF_ERR(&(qedf->dbg_ctx), "REC timer expired.\n");
+ if (!test_bit(QEDF_CMD_SRR_SENT, &io_req->flags)) {
+ io_req->rx_buf_off =
+ cqe->cqe_info.err_info.rx_buf_off;
+ io_req->tx_buf_off =
+ cqe->cqe_info.err_info.tx_buf_off;
+ io_req->rx_id = cqe->cqe_info.err_info.rx_id;
+ rval = qedf_send_rec(io_req);
+ /*
+ * We only want to abort the io_req if we
+ * can't queue the REC command as we want to
+ * keep the exchange open for recovery.
+ */
+ if (rval)
+ goto send_abort;
+ }
+ return;
+ }
+ }
+
+send_abort:
+ init_completion(&io_req->abts_done);
+ rval = qedf_initiate_abts(io_req, true);
+ if (rval)
+ QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n");
+}
+
+/* Cleanup a command when we receive an error detection completion */
+void qedf_process_error_detect(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req)
+{
+ int rval;
+
+ if (io_req == NULL) {
+ QEDF_INFO(NULL, QEDF_LOG_IO, "io_req is NULL.\n");
+ return;
+ }
+
+ if (io_req->fcport == NULL) {
+ QEDF_INFO(NULL, QEDF_LOG_IO, "fcport is NULL.\n");
+ return;
+ }
+
+ if (!cqe) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "cqe is NULL for io_req %p\n", io_req);
+ return;
+ }
+
+ QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "Error detection CQE, "
+ "xid=0x%x\n", io_req->xid);
+ QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx),
+ "err_warn_bitmap=%08x:%08x\n",
+ le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_hi),
+ le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_lo));
+ QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "tx_buff_off=%08x, "
+ "rx_buff_off=%08x, rx_id=%04x\n",
+ le32_to_cpu(cqe->cqe_info.err_info.tx_buf_off),
+ le32_to_cpu(cqe->cqe_info.err_info.rx_buf_off),
+ le32_to_cpu(cqe->cqe_info.err_info.rx_id));
+
+ /* When flush is active, let the cmds be flushed out from the cleanup context */
+ if (test_bit(QEDF_RPORT_IN_TARGET_RESET, &io_req->fcport->flags) ||
+ (test_bit(QEDF_RPORT_IN_LUN_RESET, &io_req->fcport->flags) &&
+ io_req->sc_cmd->device->lun == (u64)io_req->fcport->lun_reset_lun)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Dropping EQE for xid=0x%x as fcport is flushing",
+ io_req->xid);
+ return;
+ }
+
+ if (qedf->stop_io_on_error) {
+ qedf_stop_all_io(qedf);
+ return;
+ }
+
+ init_completion(&io_req->abts_done);
+ rval = qedf_initiate_abts(io_req, true);
+ if (rval)
+ QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n");
+}
+
+static void qedf_flush_els_req(struct qedf_ctx *qedf,
+ struct qedf_ioreq *els_req)
+{
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "Flushing ELS request xid=0x%x refcount=%d.\n", els_req->xid,
+ kref_read(&els_req->refcount));
+
+ /*
+ * Need to distinguish this from a timeout when calling the
+ * els_req->cb_func.
+ */
+ els_req->event = QEDF_IOREQ_EV_ELS_FLUSH;
+
+ clear_bit(QEDF_CMD_OUTSTANDING, &els_req->flags);
+
+ /* Cancel the timer */
+ cancel_delayed_work_sync(&els_req->timeout_work);
+
+ /* Call callback function to complete command */
+ if (els_req->cb_func && els_req->cb_arg) {
+ els_req->cb_func(els_req->cb_arg);
+ els_req->cb_arg = NULL;
+ }
+
+ /* Release kref for original initiate_els */
+ kref_put(&els_req->refcount, qedf_release_cmd);
+}
+
+/* A value of -1 for lun is a wild card that means flush all
+ * active SCSI I/Os for the target.
+ */
+void qedf_flush_active_ios(struct qedf_rport *fcport, u64 lun)
+{
+ struct qedf_ioreq *io_req;
+ struct qedf_ctx *qedf;
+ struct qedf_cmd_mgr *cmd_mgr;
+ int i, rc;
+ unsigned long flags;
+ int flush_cnt = 0;
+ int wait_cnt = 100;
+ int refcount = 0;
+
+ if (!fcport) {
+ QEDF_ERR(NULL, "fcport is NULL\n");
+ return;
+ }
+
+ /* Check that fcport is still offloaded */
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(NULL, "fcport is no longer offloaded.\n");
+ return;
+ }
+
+ qedf = fcport->qedf;
+
+ if (!qedf) {
+ QEDF_ERR(NULL, "qedf is NULL.\n");
+ return;
+ }
+
+ /* Only wait for all commands to be queued in the Upload context */
+ if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags) &&
+ (lun == -1)) {
+ while (atomic_read(&fcport->ios_to_queue)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Waiting for %d I/Os to be queued\n",
+ atomic_read(&fcport->ios_to_queue));
+ if (wait_cnt == 0) {
+ QEDF_ERR(NULL,
+ "%d IOs request could not be queued\n",
+ atomic_read(&fcport->ios_to_queue));
+ }
+ msleep(20);
+ wait_cnt--;
+ }
+ }
+
+ cmd_mgr = qedf->cmd_mgr;
+
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Flush active i/o's num=0x%x fcport=0x%p port_id=0x%06x scsi_id=%d.\n",
+ atomic_read(&fcport->num_active_ios), fcport,
+ fcport->rdata->ids.port_id, fcport->rport->scsi_target_id);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, "Locking flush mutex.\n");
+
+ mutex_lock(&qedf->flush_mutex);
+ if (lun == -1) {
+ set_bit(QEDF_RPORT_IN_TARGET_RESET, &fcport->flags);
+ } else {
+ set_bit(QEDF_RPORT_IN_LUN_RESET, &fcport->flags);
+ fcport->lun_reset_lun = lun;
+ }
+
+ for (i = 0; i < FCOE_PARAMS_NUM_TASKS; i++) {
+ io_req = &cmd_mgr->cmds[i];
+
+ if (!io_req)
+ continue;
+ if (!io_req->fcport)
+ continue;
+
+ spin_lock_irqsave(&cmd_mgr->lock, flags);
+
+ if (io_req->alloc) {
+ if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags)) {
+ if (io_req->cmd_type == QEDF_SCSI_CMD)
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Allocated but not queued, xid=0x%x\n",
+ io_req->xid);
+ }
+ spin_unlock_irqrestore(&cmd_mgr->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&cmd_mgr->lock, flags);
+ continue;
+ }
+
+ if (io_req->fcport != fcport)
+ continue;
+
+ /* In case of ABTS, CMD_OUTSTANDING is cleared on ABTS response,
+ * but RRQ is still pending.
+ * Workaround: Within qedf_send_rrq, we check if the fcport is
+ * NULL, and we drop the ref on the io_req to clean it up.
+ */
+ if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags)) {
+ refcount = kref_read(&io_req->refcount);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Not outstanding, xid=0x%x, cmd_type=%d refcount=%d.\n",
+ io_req->xid, io_req->cmd_type, refcount);
+ /* If RRQ work has been queue, try to cancel it and
+ * free the io_req
+ */
+ if (atomic_read(&io_req->state) ==
+ QEDFC_CMD_ST_RRQ_WAIT) {
+ if (cancel_delayed_work_sync
+ (&io_req->rrq_work)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Putting reference for pending RRQ work xid=0x%x.\n",
+ io_req->xid);
+ /* ID: 003 */
+ kref_put(&io_req->refcount,
+ qedf_release_cmd);
+ }
+ }
+ continue;
+ }
+
+ /* Only consider flushing ELS during target reset */
+ if (io_req->cmd_type == QEDF_ELS &&
+ lun == -1) {
+ rc = kref_get_unless_zero(&io_req->refcount);
+ if (!rc) {
+ QEDF_ERR(&(qedf->dbg_ctx),
+ "Could not get kref for ELS io_req=0x%p xid=0x%x.\n",
+ io_req, io_req->xid);
+ continue;
+ }
+ qedf_initiate_cleanup(io_req, false);
+ flush_cnt++;
+ qedf_flush_els_req(qedf, io_req);
+
+ /*
+ * Release the kref and go back to the top of the
+ * loop.
+ */
+ goto free_cmd;
+ }
+
+ if (io_req->cmd_type == QEDF_ABTS) {
+ /* ID: 004 */
+ rc = kref_get_unless_zero(&io_req->refcount);
+ if (!rc) {
+ QEDF_ERR(&(qedf->dbg_ctx),
+ "Could not get kref for abort io_req=0x%p xid=0x%x.\n",
+ io_req, io_req->xid);
+ continue;
+ }
+ if (lun != -1 && io_req->lun != lun)
+ goto free_cmd;
+
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Flushing abort xid=0x%x.\n", io_req->xid);
+
+ if (cancel_delayed_work_sync(&io_req->rrq_work)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Putting ref for cancelled RRQ work xid=0x%x.\n",
+ io_req->xid);
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ }
+
+ if (cancel_delayed_work_sync(&io_req->timeout_work)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Putting ref for cancelled tmo work xid=0x%x.\n",
+ io_req->xid);
+ qedf_initiate_cleanup(io_req, true);
+ /* Notify eh_abort handler that ABTS is
+ * complete
+ */
+ complete(&io_req->abts_done);
+ clear_bit(QEDF_CMD_IN_ABORT, &io_req->flags);
+ /* ID: 002 */
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ }
+ flush_cnt++;
+ goto free_cmd;
+ }
+
+ if (!io_req->sc_cmd)
+ continue;
+ if (!io_req->sc_cmd->device) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Device backpointer NULL for sc_cmd=%p.\n",
+ io_req->sc_cmd);
+ /* Put reference for non-existent scsi_cmnd */
+ io_req->sc_cmd = NULL;
+ qedf_initiate_cleanup(io_req, false);
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ continue;
+ }
+
+ /*
+ * Use kref_get_unless_zero in the unlikely case the command
+ * we're about to flush was completed in the normal SCSI path
+ */
+ rc = kref_get_unless_zero(&io_req->refcount);
+ if (!rc) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Could not get kref for "
+ "io_req=0x%p xid=0x%x\n", io_req, io_req->xid);
+ continue;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
+ "Cleanup xid=0x%x.\n", io_req->xid);
+ flush_cnt++;
+
+ /* Cleanup task and return I/O mid-layer */
+ qedf_initiate_cleanup(io_req, true);
+
+free_cmd:
+ kref_put(&io_req->refcount, qedf_release_cmd); /* ID: 004 */
+ }
+
+ wait_cnt = 60;
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Flushed 0x%x I/Os, active=0x%x.\n",
+ flush_cnt, atomic_read(&fcport->num_active_ios));
+ /* Only wait for all commands to complete in the Upload context */
+ if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags) &&
+ (lun == -1)) {
+ while (atomic_read(&fcport->num_active_ios)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Flushed 0x%x I/Os, active=0x%x cnt=%d.\n",
+ flush_cnt,
+ atomic_read(&fcport->num_active_ios),
+ wait_cnt);
+ if (wait_cnt == 0) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Flushed %d I/Os, active=%d.\n",
+ flush_cnt,
+ atomic_read(&fcport->num_active_ios));
+ for (i = 0; i < FCOE_PARAMS_NUM_TASKS; i++) {
+ io_req = &cmd_mgr->cmds[i];
+ if (io_req->fcport &&
+ io_req->fcport == fcport) {
+ refcount =
+ kref_read(&io_req->refcount);
+ set_bit(QEDF_CMD_DIRTY,
+ &io_req->flags);
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Outstanding io_req =%p xid=0x%x flags=0x%lx, sc_cmd=%p refcount=%d cmd_type=%d.\n",
+ io_req, io_req->xid,
+ io_req->flags,
+ io_req->sc_cmd,
+ refcount,
+ io_req->cmd_type);
+ }
+ }
+ WARN_ON(1);
+ break;
+ }
+ msleep(500);
+ wait_cnt--;
+ }
+ }
+
+ clear_bit(QEDF_RPORT_IN_LUN_RESET, &fcport->flags);
+ clear_bit(QEDF_RPORT_IN_TARGET_RESET, &fcport->flags);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, "Unlocking flush mutex.\n");
+ mutex_unlock(&qedf->flush_mutex);
+}
+
+/*
+ * Initiate a ABTS middle path command. Note that we don't have to initialize
+ * the task context for an ABTS task.
+ */
+int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts)
+{
+ struct fc_lport *lport;
+ struct qedf_rport *fcport = io_req->fcport;
+ struct fc_rport_priv *rdata;
+ struct qedf_ctx *qedf;
+ u16 xid;
+ int rc = 0;
+ unsigned long flags;
+ struct fcoe_wqe *sqe;
+ u16 sqe_idx;
+ int refcount = 0;
+
+ /* Sanity check qedf_rport before dereferencing any pointers */
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(NULL, "tgt not offloaded\n");
+ rc = 1;
+ goto out;
+ }
+
+ qedf = fcport->qedf;
+ rdata = fcport->rdata;
+
+ if (!rdata || !kref_get_unless_zero(&rdata->kref)) {
+ QEDF_ERR(&qedf->dbg_ctx, "stale rport\n");
+ rc = 1;
+ goto out;
+ }
+
+ lport = qedf->lport;
+
+ if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "link is not ready\n");
+ rc = 1;
+ goto drop_rdata_kref;
+ }
+
+ if (atomic_read(&qedf->link_down_tmo_valid) > 0) {
+ QEDF_ERR(&(qedf->dbg_ctx), "link_down_tmo active.\n");
+ rc = 1;
+ goto drop_rdata_kref;
+ }
+
+ /* Ensure room on SQ */
+ if (!atomic_read(&fcport->free_sqes)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "No SQ entries available\n");
+ rc = 1;
+ goto drop_rdata_kref;
+ }
+
+ if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx, "fcport is uploading.\n");
+ rc = 1;
+ goto drop_rdata_kref;
+ }
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+ if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) ||
+ test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags) ||
+ test_bit(QEDF_CMD_IN_ABORT, &io_req->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "io_req xid=0x%x sc_cmd=%p already in cleanup or abort processing or already completed.\n",
+ io_req->xid, io_req->sc_cmd);
+ rc = 1;
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ goto drop_rdata_kref;
+ }
+
+ /* Set the command type to abort */
+ io_req->cmd_type = QEDF_ABTS;
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+
+ kref_get(&io_req->refcount);
+
+ xid = io_req->xid;
+ qedf->control_requests++;
+ qedf->packet_aborts++;
+
+ io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts;
+
+ set_bit(QEDF_CMD_IN_ABORT, &io_req->flags);
+ refcount = kref_read(&io_req->refcount);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_SCSI_TM,
+ "ABTS io_req xid = 0x%x refcount=%d\n",
+ xid, refcount);
+
+ qedf_cmd_timer_set(qedf, io_req, QEDF_ABORT_TIMEOUT);
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+
+ sqe_idx = qedf_get_sqe_idx(fcport);
+ sqe = &fcport->sq[sqe_idx];
+ memset(sqe, 0, sizeof(struct fcoe_wqe));
+ io_req->task_params->sqe = sqe;
+
+ init_initiator_abort_fcoe_task(io_req->task_params);
+ qedf_ring_doorbell(fcport);
+
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+
+drop_rdata_kref:
+ kref_put(&rdata->kref, fc_rport_destroy);
+out:
+ return rc;
+}
+
+void qedf_process_abts_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req)
+{
+ uint32_t r_ctl;
+ int rc;
+ struct qedf_rport *fcport = io_req->fcport;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "Entered with xid = "
+ "0x%x cmd_type = %d\n", io_req->xid, io_req->cmd_type);
+
+ r_ctl = cqe->cqe_info.abts_info.r_ctl;
+
+ /* This was added at a point when we were scheduling abts_compl &
+ * cleanup_compl on different CPUs and there was a possibility of
+ * the io_req to be freed from the other context before we got here.
+ */
+ if (!fcport) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Dropping ABTS completion xid=0x%x as fcport is NULL",
+ io_req->xid);
+ return;
+ }
+
+ /*
+ * When flush is active, let the cmds be completed from the cleanup
+ * context
+ */
+ if (test_bit(QEDF_RPORT_IN_TARGET_RESET, &fcport->flags) ||
+ test_bit(QEDF_RPORT_IN_LUN_RESET, &fcport->flags)) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Dropping ABTS completion xid=0x%x as fcport is flushing",
+ io_req->xid);
+ return;
+ }
+
+ if (!cancel_delayed_work(&io_req->timeout_work)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Wasn't able to cancel abts timeout work.\n");
+ }
+
+ switch (r_ctl) {
+ case FC_RCTL_BA_ACC:
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM,
+ "ABTS response - ACC Send RRQ after R_A_TOV\n");
+ io_req->event = QEDF_IOREQ_EV_ABORT_SUCCESS;
+ rc = kref_get_unless_zero(&io_req->refcount); /* ID: 003 */
+ if (!rc) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_SCSI_TM,
+ "kref is already zero so ABTS was already completed or flushed xid=0x%x.\n",
+ io_req->xid);
+ return;
+ }
+ /*
+ * Dont release this cmd yet. It will be relesed
+ * after we get RRQ response
+ */
+ queue_delayed_work(qedf->dpc_wq, &io_req->rrq_work,
+ msecs_to_jiffies(qedf->lport->r_a_tov));
+ atomic_set(&io_req->state, QEDFC_CMD_ST_RRQ_WAIT);
+ break;
+ /* For error cases let the cleanup return the command */
+ case FC_RCTL_BA_RJT:
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM,
+ "ABTS response - RJT\n");
+ io_req->event = QEDF_IOREQ_EV_ABORT_FAILED;
+ break;
+ default:
+ QEDF_ERR(&(qedf->dbg_ctx), "Unknown ABTS response\n");
+ break;
+ }
+
+ clear_bit(QEDF_CMD_IN_ABORT, &io_req->flags);
+
+ if (io_req->sc_cmd) {
+ if (!io_req->return_scsi_cmd_on_abts)
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_SCSI_TM,
+ "Not call scsi_done for xid=0x%x.\n",
+ io_req->xid);
+ if (io_req->return_scsi_cmd_on_abts)
+ qedf_scsi_done(qedf, io_req, DID_ERROR);
+ }
+
+ /* Notify eh_abort handler that ABTS is complete */
+ complete(&io_req->abts_done);
+
+ kref_put(&io_req->refcount, qedf_release_cmd);
+}
+
+int qedf_init_mp_req(struct qedf_ioreq *io_req)
+{
+ struct qedf_mp_req *mp_req;
+ struct scsi_sge *mp_req_bd;
+ struct scsi_sge *mp_resp_bd;
+ struct qedf_ctx *qedf = io_req->fcport->qedf;
+ dma_addr_t addr;
+ uint64_t sz;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_MP_REQ, "Entered.\n");
+
+ mp_req = (struct qedf_mp_req *)&(io_req->mp_req);
+ memset(mp_req, 0, sizeof(struct qedf_mp_req));
+
+ if (io_req->cmd_type != QEDF_ELS) {
+ mp_req->req_len = sizeof(struct fcp_cmnd);
+ io_req->data_xfer_len = mp_req->req_len;
+ } else
+ mp_req->req_len = io_req->data_xfer_len;
+
+ mp_req->req_buf = dma_alloc_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE,
+ &mp_req->req_buf_dma, GFP_KERNEL);
+ if (!mp_req->req_buf) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc MP req buffer\n");
+ qedf_free_mp_resc(io_req);
+ return -ENOMEM;
+ }
+
+ mp_req->resp_buf = dma_alloc_coherent(&qedf->pdev->dev,
+ QEDF_PAGE_SIZE, &mp_req->resp_buf_dma, GFP_KERNEL);
+ if (!mp_req->resp_buf) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc TM resp "
+ "buffer\n");
+ qedf_free_mp_resc(io_req);
+ return -ENOMEM;
+ }
+
+ /* Allocate and map mp_req_bd and mp_resp_bd */
+ sz = sizeof(struct scsi_sge);
+ mp_req->mp_req_bd = dma_alloc_coherent(&qedf->pdev->dev, sz,
+ &mp_req->mp_req_bd_dma, GFP_KERNEL);
+ if (!mp_req->mp_req_bd) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc MP req bd\n");
+ qedf_free_mp_resc(io_req);
+ return -ENOMEM;
+ }
+
+ mp_req->mp_resp_bd = dma_alloc_coherent(&qedf->pdev->dev, sz,
+ &mp_req->mp_resp_bd_dma, GFP_KERNEL);
+ if (!mp_req->mp_resp_bd) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc MP resp bd\n");
+ qedf_free_mp_resc(io_req);
+ return -ENOMEM;
+ }
+
+ /* Fill bd table */
+ addr = mp_req->req_buf_dma;
+ mp_req_bd = mp_req->mp_req_bd;
+ mp_req_bd->sge_addr.lo = U64_LO(addr);
+ mp_req_bd->sge_addr.hi = U64_HI(addr);
+ mp_req_bd->sge_len = QEDF_PAGE_SIZE;
+
+ /*
+ * MP buffer is either a task mgmt command or an ELS.
+ * So the assumption is that it consumes a single bd
+ * entry in the bd table
+ */
+ mp_resp_bd = mp_req->mp_resp_bd;
+ addr = mp_req->resp_buf_dma;
+ mp_resp_bd->sge_addr.lo = U64_LO(addr);
+ mp_resp_bd->sge_addr.hi = U64_HI(addr);
+ mp_resp_bd->sge_len = QEDF_PAGE_SIZE;
+
+ return 0;
+}
+
+/*
+ * Last ditch effort to clear the port if it's stuck. Used only after a
+ * cleanup task times out.
+ */
+static void qedf_drain_request(struct qedf_ctx *qedf)
+{
+ if (test_bit(QEDF_DRAIN_ACTIVE, &qedf->flags)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "MCP drain already active.\n");
+ return;
+ }
+
+ /* Set bit to return all queuecommand requests as busy */
+ set_bit(QEDF_DRAIN_ACTIVE, &qedf->flags);
+
+ /* Call qed drain request for function. Should be synchronous */
+ qed_ops->common->drain(qedf->cdev);
+
+ /* Settle time for CQEs to be returned */
+ msleep(100);
+
+ /* Unplug and continue */
+ clear_bit(QEDF_DRAIN_ACTIVE, &qedf->flags);
+}
+
+/*
+ * Returns SUCCESS if the cleanup task does not timeout, otherwise return
+ * FAILURE.
+ */
+int qedf_initiate_cleanup(struct qedf_ioreq *io_req,
+ bool return_scsi_cmd_on_abts)
+{
+ struct qedf_rport *fcport;
+ struct qedf_ctx *qedf;
+ int tmo = 0;
+ int rc = SUCCESS;
+ unsigned long flags;
+ struct fcoe_wqe *sqe;
+ u16 sqe_idx;
+ int refcount = 0;
+
+ fcport = io_req->fcport;
+ if (!fcport) {
+ QEDF_ERR(NULL, "fcport is NULL.\n");
+ return SUCCESS;
+ }
+
+ /* Sanity check qedf_rport before dereferencing any pointers */
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(NULL, "tgt not offloaded\n");
+ return SUCCESS;
+ }
+
+ qedf = fcport->qedf;
+ if (!qedf) {
+ QEDF_ERR(NULL, "qedf is NULL.\n");
+ return SUCCESS;
+ }
+
+ if (io_req->cmd_type == QEDF_ELS) {
+ goto process_els;
+ }
+
+ if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) ||
+ test_and_set_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "io_req xid=0x%x already in "
+ "cleanup processing or already completed.\n",
+ io_req->xid);
+ return SUCCESS;
+ }
+ set_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags);
+
+process_els:
+ /* Ensure room on SQ */
+ if (!atomic_read(&fcport->free_sqes)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "No SQ entries available\n");
+ /* Need to make sure we clear the flag since it was set */
+ clear_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags);
+ return FAILED;
+ }
+
+ if (io_req->cmd_type == QEDF_CLEANUP) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "io_req=0x%x is already a cleanup command cmd_type=%d.\n",
+ io_req->xid, io_req->cmd_type);
+ clear_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags);
+ return SUCCESS;
+ }
+
+ refcount = kref_read(&io_req->refcount);
+
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO,
+ "Entered xid=0x%x sc_cmd=%p cmd_type=%d flags=0x%lx refcount=%d fcport=%p port_id=0x%06x\n",
+ io_req->xid, io_req->sc_cmd, io_req->cmd_type, io_req->flags,
+ refcount, fcport, fcport->rdata->ids.port_id);
+
+ /* Cleanup cmds re-use the same TID as the original I/O */
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+ io_req->cmd_type = QEDF_CLEANUP;
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts;
+
+ init_completion(&io_req->cleanup_done);
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+
+ sqe_idx = qedf_get_sqe_idx(fcport);
+ sqe = &fcport->sq[sqe_idx];
+ memset(sqe, 0, sizeof(struct fcoe_wqe));
+ io_req->task_params->sqe = sqe;
+
+ init_initiator_cleanup_fcoe_task(io_req->task_params);
+ qedf_ring_doorbell(fcport);
+
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+
+ tmo = wait_for_completion_timeout(&io_req->cleanup_done,
+ QEDF_CLEANUP_TIMEOUT * HZ);
+
+ if (!tmo) {
+ rc = FAILED;
+ /* Timeout case */
+ QEDF_ERR(&(qedf->dbg_ctx), "Cleanup command timeout, "
+ "xid=%x.\n", io_req->xid);
+ clear_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags);
+ /* Issue a drain request if cleanup task times out */
+ QEDF_ERR(&(qedf->dbg_ctx), "Issuing MCP drain request.\n");
+ qedf_drain_request(qedf);
+ }
+
+ /* If it TASK MGMT handle it, reference will be decreased
+ * in qedf_execute_tmf
+ */
+ if (io_req->tm_flags == FCP_TMF_LUN_RESET ||
+ io_req->tm_flags == FCP_TMF_TGT_RESET) {
+ clear_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+ io_req->sc_cmd = NULL;
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ complete(&io_req->tm_done);
+ }
+
+ if (io_req->sc_cmd) {
+ if (!io_req->return_scsi_cmd_on_abts)
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_SCSI_TM,
+ "Not call scsi_done for xid=0x%x.\n",
+ io_req->xid);
+ if (io_req->return_scsi_cmd_on_abts)
+ qedf_scsi_done(qedf, io_req, DID_ERROR);
+ }
+
+ if (rc == SUCCESS)
+ io_req->event = QEDF_IOREQ_EV_CLEANUP_SUCCESS;
+ else
+ io_req->event = QEDF_IOREQ_EV_CLEANUP_FAILED;
+
+ return rc;
+}
+
+void qedf_process_cleanup_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req)
+{
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "Entered xid = 0x%x\n",
+ io_req->xid);
+
+ clear_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags);
+
+ /* Complete so we can finish cleaning up the I/O */
+ complete(&io_req->cleanup_done);
+}
+
+static int qedf_execute_tmf(struct qedf_rport *fcport, u64 tm_lun,
+ uint8_t tm_flags)
+{
+ struct qedf_ioreq *io_req;
+ struct fcoe_task_context *task;
+ struct qedf_ctx *qedf = fcport->qedf;
+ struct fc_lport *lport = qedf->lport;
+ int rc = 0;
+ uint16_t xid;
+ int tmo = 0;
+ unsigned long flags;
+ struct fcoe_wqe *sqe;
+ u16 sqe_idx;
+
+ if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "fcport not offloaded\n");
+ rc = FAILED;
+ goto no_flush;
+ }
+
+ io_req = qedf_alloc_cmd(fcport, QEDF_TASK_MGMT_CMD);
+ if (!io_req) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Failed TMF");
+ rc = -EAGAIN;
+ goto no_flush;
+ }
+
+ if (tm_flags == FCP_TMF_LUN_RESET)
+ qedf->lun_resets++;
+ else if (tm_flags == FCP_TMF_TGT_RESET)
+ qedf->target_resets++;
+
+ /* Initialize rest of io_req fields */
+ io_req->sc_cmd = NULL;
+ io_req->fcport = fcport;
+ io_req->cmd_type = QEDF_TASK_MGMT_CMD;
+
+ /* Set TM flags */
+ io_req->io_req_flags = QEDF_READ;
+ io_req->data_xfer_len = 0;
+ io_req->tm_flags = tm_flags;
+
+ /* Default is to return a SCSI command when an error occurs */
+ io_req->return_scsi_cmd_on_abts = false;
+ io_req->tm_lun = tm_lun;
+
+ /* Obtain exchange id */
+ xid = io_req->xid;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "TMF io_req xid = "
+ "0x%x\n", xid);
+
+ /* Initialize task context for this IO request */
+ task = qedf_get_task_mem(&qedf->tasks, xid);
+
+ init_completion(&io_req->tm_done);
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+
+ /* Record which cpu this request is associated with */
+ io_req->cpu = smp_processor_id();
+
+ sqe_idx = qedf_get_sqe_idx(fcport);
+ sqe = &fcport->sq[sqe_idx];
+ memset(sqe, 0, sizeof(struct fcoe_wqe));
+
+ qedf_init_task(fcport, lport, io_req, task, sqe);
+ qedf_ring_doorbell(fcport);
+
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+
+ set_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+ tmo = wait_for_completion_timeout(&io_req->tm_done,
+ QEDF_TM_TIMEOUT * HZ);
+
+ if (!tmo) {
+ rc = FAILED;
+ QEDF_ERR(&(qedf->dbg_ctx), "wait for tm_cmpl timeout!\n");
+ /* Clear outstanding bit since command timed out */
+ clear_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+ io_req->sc_cmd = NULL;
+ } else {
+ /* Check TMF response code */
+ if (io_req->fcp_rsp_code == 0)
+ rc = SUCCESS;
+ else
+ rc = FAILED;
+ }
+ /*
+ * Double check that fcport has not gone into an uploading state before
+ * executing the command flush for the LUN/target.
+ */
+ if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "fcport is uploading, not executing flush.\n");
+ goto no_flush;
+ }
+ /* We do not need this io_req any more */
+ kref_put(&io_req->refcount, qedf_release_cmd);
+
+
+ if (tm_flags == FCP_TMF_LUN_RESET)
+ qedf_flush_active_ios(fcport, tm_lun);
+ else
+ qedf_flush_active_ios(fcport, -1);
+
+no_flush:
+ if (rc != SUCCESS) {
+ QEDF_ERR(&(qedf->dbg_ctx), "task mgmt command failed...\n");
+ rc = FAILED;
+ } else {
+ QEDF_ERR(&(qedf->dbg_ctx), "task mgmt command success...\n");
+ rc = SUCCESS;
+ }
+ return rc;
+}
+
+int qedf_initiate_tmf(struct fc_rport *rport, u64 lun, u8 tm_flags)
+{
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct qedf_rport *fcport = (struct qedf_rport *)&rp[1];
+ struct qedf_ctx *qedf = fcport->qedf;
+ struct fc_lport *lport = rp->local_port;
+ int rc = SUCCESS;
+ struct fc_rport_priv *rdata = fcport->rdata;
+
+ QEDF_ERR(NULL,
+ "tm_flags 0x%x target_id = 0x%x lun=%llu\n",
+ tm_flags, rport->scsi_target_id, lun);
+
+ if (!rdata || !kref_get_unless_zero(&rdata->kref)) {
+ QEDF_ERR(NULL, "stale rport\n");
+ return FAILED;
+ }
+
+ QEDF_ERR(NULL, "portid=%06x tm_flags =%s\n", rdata->ids.port_id,
+ (tm_flags == FCP_TMF_TGT_RESET) ? "TARGET RESET" :
+ "LUN RESET");
+
+ rc = fc_block_rport(rport);
+ if (rc)
+ goto tmf_err;
+
+ if (!qedf) {
+ QEDF_ERR(NULL, "qedf is NULL.\n");
+ rc = FAILED;
+ goto tmf_err;
+ }
+
+ if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx, "Connection is getting uploaded.\n");
+ rc = SUCCESS;
+ goto tmf_err;
+ }
+
+ if (test_bit(QEDF_UNLOADING, &qedf->flags) ||
+ test_bit(QEDF_DBG_STOP_IO, &qedf->flags)) {
+ rc = SUCCESS;
+ goto tmf_err;
+ }
+
+ if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
+ QEDF_ERR(&(qedf->dbg_ctx), "link is not ready\n");
+ rc = FAILED;
+ goto tmf_err;
+ }
+
+ if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ if (!fcport->rdata)
+ QEDF_ERR(&qedf->dbg_ctx, "fcport %p is uploading.\n",
+ fcport);
+ else
+ QEDF_ERR(&qedf->dbg_ctx,
+ "fcport %p port_id=%06x is uploading.\n",
+ fcport, fcport->rdata->ids.port_id);
+ rc = FAILED;
+ goto tmf_err;
+ }
+
+ rc = qedf_execute_tmf(fcport, lun, tm_flags);
+
+tmf_err:
+ kref_put(&rdata->kref, fc_rport_destroy);
+ return rc;
+}
+
+void qedf_process_tmf_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
+ struct qedf_ioreq *io_req)
+{
+ struct fcoe_cqe_rsp_info *fcp_rsp;
+
+ clear_bit(QEDF_CMD_OUTSTANDING, &io_req->flags);
+
+ fcp_rsp = &cqe->cqe_info.rsp_info;
+ qedf_parse_fcp_rsp(io_req, fcp_rsp);
+
+ complete(&io_req->tm_done);
+}
+
+void qedf_process_unsol_compl(struct qedf_ctx *qedf, uint16_t que_idx,
+ struct fcoe_cqe *cqe)
+{
+ unsigned long flags;
+ uint16_t pktlen = cqe->cqe_info.unsolic_info.pkt_len;
+ u32 payload_len, crc;
+ struct fc_frame_header *fh;
+ struct fc_frame *fp;
+ struct qedf_io_work *io_work;
+ u32 bdq_idx;
+ void *bdq_addr;
+ struct scsi_bd *p_bd_info;
+
+ p_bd_info = &cqe->cqe_info.unsolic_info.bd_info;
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL,
+ "address.hi=%x, address.lo=%x, opaque_data.hi=%x, opaque_data.lo=%x, bdq_prod_idx=%u, len=%u\n",
+ le32_to_cpu(p_bd_info->address.hi),
+ le32_to_cpu(p_bd_info->address.lo),
+ le32_to_cpu(p_bd_info->opaque.fcoe_opaque.hi),
+ le32_to_cpu(p_bd_info->opaque.fcoe_opaque.lo),
+ qedf->bdq_prod_idx, pktlen);
+
+ bdq_idx = le32_to_cpu(p_bd_info->opaque.fcoe_opaque.lo);
+ if (bdq_idx >= QEDF_BDQ_SIZE) {
+ QEDF_ERR(&(qedf->dbg_ctx), "bdq_idx is out of range %d.\n",
+ bdq_idx);
+ goto increment_prod;
+ }
+
+ bdq_addr = qedf->bdq[bdq_idx].buf_addr;
+ if (!bdq_addr) {
+ QEDF_ERR(&(qedf->dbg_ctx), "bdq_addr is NULL, dropping "
+ "unsolicited packet.\n");
+ goto increment_prod;
+ }
+
+ if (qedf_dump_frames) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL,
+ "BDQ frame is at addr=%p.\n", bdq_addr);
+ print_hex_dump(KERN_WARNING, "bdq ", DUMP_PREFIX_OFFSET, 16, 1,
+ (void *)bdq_addr, pktlen, false);
+ }
+
+ /* Allocate frame */
+ payload_len = pktlen - sizeof(struct fc_frame_header);
+ fp = fc_frame_alloc(qedf->lport, payload_len);
+ if (!fp) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate fp.\n");
+ goto increment_prod;
+ }
+
+ /* Copy data from BDQ buffer into fc_frame struct */
+ fh = (struct fc_frame_header *)fc_frame_header_get(fp);
+ memcpy(fh, (void *)bdq_addr, pktlen);
+
+ QEDF_WARN(&qedf->dbg_ctx,
+ "Processing Unsolicated frame, src=%06x dest=%06x r_ctl=0x%x type=0x%x cmd=%02x\n",
+ ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl,
+ fh->fh_type, fc_frame_payload_op(fp));
+
+ /* Initialize the frame so libfc sees it as a valid frame */
+ crc = fcoe_fc_crc(fp);
+ fc_frame_init(fp);
+ fr_dev(fp) = qedf->lport;
+ fr_sof(fp) = FC_SOF_I3;
+ fr_eof(fp) = FC_EOF_T;
+ fr_crc(fp) = cpu_to_le32(~crc);
+
+ /*
+ * We need to return the frame back up to libfc in a non-atomic
+ * context
+ */
+ io_work = mempool_alloc(qedf->io_mempool, GFP_ATOMIC);
+ if (!io_work) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate "
+ "work for I/O completion.\n");
+ fc_frame_free(fp);
+ goto increment_prod;
+ }
+ memset(io_work, 0, sizeof(struct qedf_io_work));
+
+ INIT_WORK(&io_work->work, qedf_fp_io_handler);
+
+ /* Copy contents of CQE for deferred processing */
+ memcpy(&io_work->cqe, cqe, sizeof(struct fcoe_cqe));
+
+ io_work->qedf = qedf;
+ io_work->fp = fp;
+
+ queue_work_on(smp_processor_id(), qedf_io_wq, &io_work->work);
+increment_prod:
+ spin_lock_irqsave(&qedf->hba_lock, flags);
+
+ /* Increment producer to let f/w know we've handled the frame */
+ qedf->bdq_prod_idx++;
+
+ /* Producer index wraps at uint16_t boundary */
+ if (qedf->bdq_prod_idx == 0xffff)
+ qedf->bdq_prod_idx = 0;
+
+ writew(qedf->bdq_prod_idx, qedf->bdq_primary_prod);
+ readw(qedf->bdq_primary_prod);
+ writew(qedf->bdq_prod_idx, qedf->bdq_secondary_prod);
+ readw(qedf->bdq_secondary_prod);
+
+ spin_unlock_irqrestore(&qedf->hba_lock, flags);
+}
diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c
new file mode 100644
index 000000000000..7792e00800ae
--- /dev/null
+++ b/drivers/scsi/qedf/qedf_main.c
@@ -0,0 +1,4237 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QLogic FCoE Offload Driver
+ * Copyright (c) 2016-2018 Cavium Inc.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/highmem.h>
+#include <linux/crc32.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/kthread.h>
+#include <linux/phylink.h>
+#include <scsi/libfc.h>
+#include <scsi/scsi_host.h>
+#include <scsi/fc_frame.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/cpu.h>
+#include "qedf.h"
+#include "qedf_dbg.h"
+#include <uapi/linux/pci_regs.h>
+
+const struct qed_fcoe_ops *qed_ops;
+
+static int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id);
+static void qedf_remove(struct pci_dev *pdev);
+static void qedf_shutdown(struct pci_dev *pdev);
+static void qedf_schedule_recovery_handler(void *dev);
+static void qedf_recovery_handler(struct work_struct *work);
+static int qedf_suspend(struct pci_dev *pdev, pm_message_t state);
+
+/*
+ * Driver module parameters.
+ */
+static unsigned int qedf_dev_loss_tmo = 60;
+module_param_named(dev_loss_tmo, qedf_dev_loss_tmo, int, S_IRUGO);
+MODULE_PARM_DESC(dev_loss_tmo, " dev_loss_tmo setting for attached "
+ "remote ports (default 60)");
+
+uint qedf_debug = QEDF_LOG_INFO;
+module_param_named(debug, qedf_debug, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug, " Debug mask. Pass '1' to enable default debugging"
+ " mask");
+
+static uint qedf_fipvlan_retries = 60;
+module_param_named(fipvlan_retries, qedf_fipvlan_retries, int, S_IRUGO);
+MODULE_PARM_DESC(fipvlan_retries, " Number of FIP VLAN requests to attempt "
+ "before giving up (default 60)");
+
+static uint qedf_fallback_vlan = QEDF_FALLBACK_VLAN;
+module_param_named(fallback_vlan, qedf_fallback_vlan, int, S_IRUGO);
+MODULE_PARM_DESC(fallback_vlan, " VLAN ID to try if fip vlan request fails "
+ "(default 1002).");
+
+static int qedf_default_prio = -1;
+module_param_named(default_prio, qedf_default_prio, int, S_IRUGO);
+MODULE_PARM_DESC(default_prio, " Override 802.1q priority for FIP and FCoE"
+ " traffic (value between 0 and 7, default 3).");
+
+uint qedf_dump_frames;
+module_param_named(dump_frames, qedf_dump_frames, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_frames, " Print the skb data of FIP and FCoE frames "
+ "(default off)");
+
+static uint qedf_queue_depth;
+module_param_named(queue_depth, qedf_queue_depth, int, S_IRUGO);
+MODULE_PARM_DESC(queue_depth, " Sets the queue depth for all LUNs discovered "
+ "by the qedf driver. Default is 0 (use OS default).");
+
+uint qedf_io_tracing;
+module_param_named(io_tracing, qedf_io_tracing, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(io_tracing, " Enable logging of SCSI requests/completions "
+ "into trace buffer. (default off).");
+
+static uint qedf_max_lun = MAX_FIBRE_LUNS;
+module_param_named(max_lun, qedf_max_lun, int, S_IRUGO);
+MODULE_PARM_DESC(max_lun, " Sets the maximum luns per target that the driver "
+ "supports. (default 0xffffffff)");
+
+uint qedf_link_down_tmo;
+module_param_named(link_down_tmo, qedf_link_down_tmo, int, S_IRUGO);
+MODULE_PARM_DESC(link_down_tmo, " Delays informing the fcoe transport that the "
+ "link is down by N seconds.");
+
+bool qedf_retry_delay;
+module_param_named(retry_delay, qedf_retry_delay, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(retry_delay, " Enable/disable handling of FCP_RSP IU retry "
+ "delay handling (default off).");
+
+static bool qedf_dcbx_no_wait;
+module_param_named(dcbx_no_wait, qedf_dcbx_no_wait, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dcbx_no_wait, " Do not wait for DCBX convergence to start "
+ "sending FIP VLAN requests on link up (Default: off).");
+
+static uint qedf_dp_module;
+module_param_named(dp_module, qedf_dp_module, uint, S_IRUGO);
+MODULE_PARM_DESC(dp_module, " bit flags control for verbose printk passed "
+ "qed module during probe.");
+
+static uint qedf_dp_level = QED_LEVEL_NOTICE;
+module_param_named(dp_level, qedf_dp_level, uint, S_IRUGO);
+MODULE_PARM_DESC(dp_level, " printk verbosity control passed to qed module "
+ "during probe (0-3: 0 more verbose).");
+
+static bool qedf_enable_recovery = true;
+module_param_named(enable_recovery, qedf_enable_recovery,
+ bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_recovery, "Enable/disable recovery on driver/firmware "
+ "interface level errors 0 = Disabled, 1 = Enabled (Default: 1).");
+
+struct workqueue_struct *qedf_io_wq;
+
+static struct fcoe_percpu_s qedf_global;
+static DEFINE_SPINLOCK(qedf_global_lock);
+
+static struct kmem_cache *qedf_io_work_cache;
+
+void qedf_set_vlan_id(struct qedf_ctx *qedf, int vlan_id)
+{
+ int vlan_id_tmp = 0;
+
+ vlan_id_tmp = vlan_id | (qedf->prio << VLAN_PRIO_SHIFT);
+ qedf->vlan_id = vlan_id_tmp;
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Setting vlan_id=0x%04x prio=%d.\n",
+ vlan_id_tmp, qedf->prio);
+}
+
+/* Returns true if we have a valid vlan, false otherwise */
+static bool qedf_initiate_fipvlan_req(struct qedf_ctx *qedf)
+{
+
+ while (qedf->fipvlan_retries--) {
+ /* This is to catch if link goes down during fipvlan retries */
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) {
+ QEDF_ERR(&qedf->dbg_ctx, "Link not up.\n");
+ return false;
+ }
+
+ if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx, "Driver unloading.\n");
+ return false;
+ }
+
+ if (qedf->vlan_id > 0) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "vlan = 0x%x already set, calling ctlr_link_up.\n",
+ qedf->vlan_id);
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_UP)
+ fcoe_ctlr_link_up(&qedf->ctlr);
+ return true;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Retry %d.\n", qedf->fipvlan_retries);
+ init_completion(&qedf->fipvlan_compl);
+ qedf_fcoe_send_vlan_req(qedf);
+ wait_for_completion_timeout(&qedf->fipvlan_compl, 1 * HZ);
+ }
+
+ return false;
+}
+
+static void qedf_handle_link_update(struct work_struct *work)
+{
+ struct qedf_ctx *qedf =
+ container_of(work, struct qedf_ctx, link_update.work);
+ int rc;
+
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Entered. link_state=%d.\n",
+ atomic_read(&qedf->link_state));
+
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) {
+ rc = qedf_initiate_fipvlan_req(qedf);
+ if (rc)
+ return;
+
+ if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Link is down, resetting vlan_id.\n");
+ qedf->vlan_id = 0;
+ return;
+ }
+
+ /*
+ * If we get here then we never received a repsonse to our
+ * fip vlan request so set the vlan_id to the default and
+ * tell FCoE that the link is up
+ */
+ QEDF_WARN(&(qedf->dbg_ctx), "Did not receive FIP VLAN "
+ "response, falling back to default VLAN %d.\n",
+ qedf_fallback_vlan);
+ qedf_set_vlan_id(qedf, qedf_fallback_vlan);
+
+ /*
+ * Zero out data_src_addr so we'll update it with the new
+ * lport port_id
+ */
+ eth_zero_addr(qedf->data_src_addr);
+ fcoe_ctlr_link_up(&qedf->ctlr);
+ } else if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) {
+ /*
+ * If we hit here and link_down_tmo_valid is still 1 it means
+ * that link_down_tmo timed out so set it to 0 to make sure any
+ * other readers have accurate state.
+ */
+ atomic_set(&qedf->link_down_tmo_valid, 0);
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Calling fcoe_ctlr_link_down().\n");
+ fcoe_ctlr_link_down(&qedf->ctlr);
+ if (qedf_wait_for_upload(qedf) == false)
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Could not upload all sessions.\n");
+ /* Reset the number of FIP VLAN retries */
+ qedf->fipvlan_retries = qedf_fipvlan_retries;
+ }
+}
+
+#define QEDF_FCOE_MAC_METHOD_GRANGED_MAC 1
+#define QEDF_FCOE_MAC_METHOD_FCF_MAP 2
+#define QEDF_FCOE_MAC_METHOD_FCOE_SET_MAC 3
+static void qedf_set_data_src_addr(struct qedf_ctx *qedf, struct fc_frame *fp)
+{
+ u8 *granted_mac;
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ u8 fc_map[3];
+ int method = 0;
+
+ /* Get granted MAC address from FIP FLOGI payload */
+ granted_mac = fr_cb(fp)->granted_mac;
+
+ /*
+ * We set the source MAC for FCoE traffic based on the Granted MAC
+ * address from the switch.
+ *
+ * If granted_mac is non-zero, we used that.
+ * If the granted_mac is zeroed out, created the FCoE MAC based on
+ * the sel_fcf->fc_map and the d_id fo the FLOGI frame.
+ * If sel_fcf->fc_map is 0 then we use the default FCF-MAC plus the
+ * d_id of the FLOGI frame.
+ */
+ if (!is_zero_ether_addr(granted_mac)) {
+ ether_addr_copy(qedf->data_src_addr, granted_mac);
+ method = QEDF_FCOE_MAC_METHOD_GRANGED_MAC;
+ } else if (qedf->ctlr.sel_fcf->fc_map != 0) {
+ hton24(fc_map, qedf->ctlr.sel_fcf->fc_map);
+ qedf->data_src_addr[0] = fc_map[0];
+ qedf->data_src_addr[1] = fc_map[1];
+ qedf->data_src_addr[2] = fc_map[2];
+ qedf->data_src_addr[3] = fh->fh_d_id[0];
+ qedf->data_src_addr[4] = fh->fh_d_id[1];
+ qedf->data_src_addr[5] = fh->fh_d_id[2];
+ method = QEDF_FCOE_MAC_METHOD_FCF_MAP;
+ } else {
+ fc_fcoe_set_mac(qedf->data_src_addr, fh->fh_d_id);
+ method = QEDF_FCOE_MAC_METHOD_FCOE_SET_MAC;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "QEDF data_src_mac=%pM method=%d.\n", qedf->data_src_addr, method);
+}
+
+static void qedf_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
+ void *arg)
+{
+ struct fc_exch *exch = fc_seq_exch(seq);
+ struct fc_lport *lport = exch->lp;
+ struct qedf_ctx *qedf = lport_priv(lport);
+
+ if (!qedf) {
+ QEDF_ERR(NULL, "qedf is NULL.\n");
+ return;
+ }
+
+ /*
+ * If ERR_PTR is set then don't try to stat anything as it will cause
+ * a crash when we access fp.
+ */
+ if (IS_ERR(fp)) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "fp has IS_ERR() set.\n");
+ goto skip_stat;
+ }
+
+ /* Log stats for FLOGI reject */
+ if (fc_frame_payload_op(fp) == ELS_LS_RJT)
+ qedf->flogi_failed++;
+ else if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
+ /* Set the source MAC we will use for FCoE traffic */
+ qedf_set_data_src_addr(qedf, fp);
+ qedf->flogi_pending = 0;
+ }
+
+ /* Complete flogi_compl so we can proceed to sending ADISCs */
+ complete(&qedf->flogi_compl);
+
+skip_stat:
+ /* Report response to libfc */
+ fc_lport_flogi_resp(seq, fp, lport);
+}
+
+static struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did,
+ struct fc_frame *fp, unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *,
+ void *),
+ void *arg, u32 timeout)
+{
+ struct qedf_ctx *qedf = lport_priv(lport);
+
+ /*
+ * Intercept FLOGI for statistic purposes. Note we use the resp
+ * callback to tell if this is really a flogi.
+ */
+ if (resp == fc_lport_flogi_resp) {
+ qedf->flogi_cnt++;
+ qedf->flogi_pending++;
+
+ if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx, "Driver unloading\n");
+ qedf->flogi_pending = 0;
+ }
+
+ if (qedf->flogi_pending >= QEDF_FLOGI_RETRY_CNT) {
+ schedule_delayed_work(&qedf->stag_work, 2);
+ return NULL;
+ }
+
+ return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp,
+ arg, timeout);
+ }
+
+ return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
+}
+
+int qedf_send_flogi(struct qedf_ctx *qedf)
+{
+ struct fc_lport *lport;
+ struct fc_frame *fp;
+
+ lport = qedf->lport;
+
+ if (!lport->tt.elsct_send) {
+ QEDF_ERR(&qedf->dbg_ctx, "tt.elsct_send not set.\n");
+ return -EINVAL;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
+ if (!fp) {
+ QEDF_ERR(&(qedf->dbg_ctx), "fc_frame_alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
+ "Sending FLOGI to reestablish session with switch.\n");
+ lport->tt.elsct_send(lport, FC_FID_FLOGI, fp,
+ ELS_FLOGI, qedf_flogi_resp, lport, lport->r_a_tov);
+
+ init_completion(&qedf->flogi_compl);
+
+ return 0;
+}
+
+/*
+ * This function is called if link_down_tmo is in use. If we get a link up and
+ * link_down_tmo has not expired then use just FLOGI/ADISC to recover our
+ * sessions with targets. Otherwise, just call fcoe_ctlr_link_up().
+ */
+static void qedf_link_recovery(struct work_struct *work)
+{
+ struct qedf_ctx *qedf =
+ container_of(work, struct qedf_ctx, link_recovery.work);
+ struct fc_lport *lport = qedf->lport;
+ struct fc_rport_priv *rdata;
+ bool rc;
+ int retries = 30;
+ int rval, i;
+ struct list_head rdata_login_list;
+
+ INIT_LIST_HEAD(&rdata_login_list);
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Link down tmo did not expire.\n");
+
+ /*
+ * Essentially reset the fcoe_ctlr here without affecting the state
+ * of the libfc structs.
+ */
+ qedf->ctlr.state = FIP_ST_LINK_WAIT;
+ fcoe_ctlr_link_down(&qedf->ctlr);
+
+ /*
+ * Bring the link up before we send the fipvlan request so libfcoe
+ * can select a new fcf in parallel
+ */
+ fcoe_ctlr_link_up(&qedf->ctlr);
+
+ /* Since the link when down and up to verify which vlan we're on */
+ qedf->fipvlan_retries = qedf_fipvlan_retries;
+ rc = qedf_initiate_fipvlan_req(qedf);
+ /* If getting the VLAN fails, set the VLAN to the fallback one */
+ if (!rc)
+ qedf_set_vlan_id(qedf, qedf_fallback_vlan);
+
+ /*
+ * We need to wait for an FCF to be selected due to the
+ * fcoe_ctlr_link_up other the FLOGI will be rejected.
+ */
+ while (retries > 0) {
+ if (qedf->ctlr.sel_fcf) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "FCF reselected, proceeding with FLOGI.\n");
+ break;
+ }
+ msleep(500);
+ retries--;
+ }
+
+ if (retries < 1) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Exhausted retries waiting for "
+ "FCF selection.\n");
+ return;
+ }
+
+ rval = qedf_send_flogi(qedf);
+ if (rval)
+ return;
+
+ /* Wait for FLOGI completion before proceeding with sending ADISCs */
+ i = wait_for_completion_timeout(&qedf->flogi_compl,
+ qedf->lport->r_a_tov);
+ if (i == 0) {
+ QEDF_ERR(&(qedf->dbg_ctx), "FLOGI timed out.\n");
+ return;
+ }
+
+ /*
+ * Call lport->tt.rport_login which will cause libfc to send an
+ * ADISC since the rport is in state ready.
+ */
+ mutex_lock(&lport->disc.disc_mutex);
+ list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
+ if (kref_get_unless_zero(&rdata->kref)) {
+ fc_rport_login(rdata);
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
+ }
+ mutex_unlock(&lport->disc.disc_mutex);
+}
+
+static void qedf_update_link_speed(struct qedf_ctx *qedf,
+ struct qed_link_output *link)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(sup_caps);
+ struct fc_lport *lport = qedf->lport;
+
+ lport->link_speed = FC_PORTSPEED_UNKNOWN;
+ lport->link_supported_speeds = FC_PORTSPEED_UNKNOWN;
+
+ /* Set fc_host link speed */
+ switch (link->speed) {
+ case 10000:
+ lport->link_speed = FC_PORTSPEED_10GBIT;
+ break;
+ case 25000:
+ lport->link_speed = FC_PORTSPEED_25GBIT;
+ break;
+ case 40000:
+ lport->link_speed = FC_PORTSPEED_40GBIT;
+ break;
+ case 50000:
+ lport->link_speed = FC_PORTSPEED_50GBIT;
+ break;
+ case 100000:
+ lport->link_speed = FC_PORTSPEED_100GBIT;
+ break;
+ case 20000:
+ lport->link_speed = FC_PORTSPEED_20GBIT;
+ break;
+ default:
+ lport->link_speed = FC_PORTSPEED_UNKNOWN;
+ break;
+ }
+
+ /*
+ * Set supported link speed by querying the supported
+ * capabilities of the link.
+ */
+
+ phylink_zero(sup_caps);
+ phylink_set(sup_caps, 10000baseT_Full);
+ phylink_set(sup_caps, 10000baseKX4_Full);
+ phylink_set(sup_caps, 10000baseR_FEC);
+ phylink_set(sup_caps, 10000baseCR_Full);
+ phylink_set(sup_caps, 10000baseSR_Full);
+ phylink_set(sup_caps, 10000baseLR_Full);
+ phylink_set(sup_caps, 10000baseLRM_Full);
+ phylink_set(sup_caps, 10000baseKR_Full);
+
+ if (linkmode_intersects(link->supported_caps, sup_caps))
+ lport->link_supported_speeds |= FC_PORTSPEED_10GBIT;
+
+ phylink_zero(sup_caps);
+ phylink_set(sup_caps, 25000baseKR_Full);
+ phylink_set(sup_caps, 25000baseCR_Full);
+ phylink_set(sup_caps, 25000baseSR_Full);
+
+ if (linkmode_intersects(link->supported_caps, sup_caps))
+ lport->link_supported_speeds |= FC_PORTSPEED_25GBIT;
+
+ phylink_zero(sup_caps);
+ phylink_set(sup_caps, 40000baseLR4_Full);
+ phylink_set(sup_caps, 40000baseKR4_Full);
+ phylink_set(sup_caps, 40000baseCR4_Full);
+ phylink_set(sup_caps, 40000baseSR4_Full);
+
+ if (linkmode_intersects(link->supported_caps, sup_caps))
+ lport->link_supported_speeds |= FC_PORTSPEED_40GBIT;
+
+ phylink_zero(sup_caps);
+ phylink_set(sup_caps, 50000baseKR2_Full);
+ phylink_set(sup_caps, 50000baseCR2_Full);
+ phylink_set(sup_caps, 50000baseSR2_Full);
+
+ if (linkmode_intersects(link->supported_caps, sup_caps))
+ lport->link_supported_speeds |= FC_PORTSPEED_50GBIT;
+
+ phylink_zero(sup_caps);
+ phylink_set(sup_caps, 100000baseKR4_Full);
+ phylink_set(sup_caps, 100000baseSR4_Full);
+ phylink_set(sup_caps, 100000baseCR4_Full);
+ phylink_set(sup_caps, 100000baseLR4_ER4_Full);
+
+ if (linkmode_intersects(link->supported_caps, sup_caps))
+ lport->link_supported_speeds |= FC_PORTSPEED_100GBIT;
+
+ phylink_zero(sup_caps);
+ phylink_set(sup_caps, 20000baseKR2_Full);
+
+ if (linkmode_intersects(link->supported_caps, sup_caps))
+ lport->link_supported_speeds |= FC_PORTSPEED_20GBIT;
+
+ if (lport->host && lport->host->shost_data)
+ fc_host_supported_speeds(lport->host) =
+ lport->link_supported_speeds;
+}
+
+static void qedf_bw_update(void *dev)
+{
+ struct qedf_ctx *qedf = (struct qedf_ctx *)dev;
+ struct qed_link_output link;
+
+ /* Get the latest status of the link */
+ qed_ops->common->get_link(qedf->cdev, &link);
+
+ if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Ignore link update, driver getting unload.\n");
+ return;
+ }
+
+ if (link.link_up) {
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_UP)
+ qedf_update_link_speed(qedf, &link);
+ else
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Ignore bw update, link is down.\n");
+
+ } else {
+ QEDF_ERR(&qedf->dbg_ctx, "link_up is not set.\n");
+ }
+}
+
+static void qedf_link_update(void *dev, struct qed_link_output *link)
+{
+ struct qedf_ctx *qedf = (struct qedf_ctx *)dev;
+
+ /*
+ * Prevent race where we're removing the module and we get link update
+ * for qed.
+ */
+ if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Ignore link update, driver getting unload.\n");
+ return;
+ }
+
+ if (link->link_up) {
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) {
+ QEDF_INFO((&qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Ignoring link up event as link is already up.\n");
+ return;
+ }
+ QEDF_ERR(&(qedf->dbg_ctx), "LINK UP (%d GB/s).\n",
+ link->speed / 1000);
+
+ /* Cancel any pending link down work */
+ cancel_delayed_work(&qedf->link_update);
+
+ atomic_set(&qedf->link_state, QEDF_LINK_UP);
+ qedf_update_link_speed(qedf, link);
+
+ if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE ||
+ qedf_dcbx_no_wait) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "DCBx done.\n");
+ if (atomic_read(&qedf->link_down_tmo_valid) > 0)
+ queue_delayed_work(qedf->link_update_wq,
+ &qedf->link_recovery, 0);
+ else
+ queue_delayed_work(qedf->link_update_wq,
+ &qedf->link_update, 0);
+ atomic_set(&qedf->link_down_tmo_valid, 0);
+ }
+
+ } else {
+ QEDF_ERR(&(qedf->dbg_ctx), "LINK DOWN.\n");
+
+ atomic_set(&qedf->link_state, QEDF_LINK_DOWN);
+ atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING);
+ /*
+ * Flag that we're waiting for the link to come back up before
+ * informing the fcoe layer of the event.
+ */
+ if (qedf_link_down_tmo > 0) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Starting link down tmo.\n");
+ atomic_set(&qedf->link_down_tmo_valid, 1);
+ }
+ qedf->vlan_id = 0;
+ qedf_update_link_speed(qedf, link);
+ queue_delayed_work(qedf->link_update_wq, &qedf->link_update,
+ qedf_link_down_tmo * HZ);
+ }
+}
+
+
+static void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type)
+{
+ struct qedf_ctx *qedf = (struct qedf_ctx *)dev;
+ u8 tmp_prio;
+
+ QEDF_ERR(&(qedf->dbg_ctx), "DCBx event valid=%d enabled=%d fcoe "
+ "prio=%d.\n", get->operational.valid, get->operational.enabled,
+ get->operational.app_prio.fcoe);
+
+ if (get->operational.enabled && get->operational.valid) {
+ /* If DCBX was already negotiated on link up then just exit */
+ if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "DCBX already set on link up.\n");
+ return;
+ }
+
+ atomic_set(&qedf->dcbx, QEDF_DCBX_DONE);
+
+ /*
+ * Set the 8021q priority in the following manner:
+ *
+ * 1. If a modparam is set use that
+ * 2. If the value is not between 0..7 use the default
+ * 3. Use the priority we get from the DCBX app tag
+ */
+ tmp_prio = get->operational.app_prio.fcoe;
+ if (qedf_default_prio > -1)
+ qedf->prio = qedf_default_prio;
+ else if (tmp_prio > 7) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "FIP/FCoE prio %d out of range, setting to %d.\n",
+ tmp_prio, QEDF_DEFAULT_PRIO);
+ qedf->prio = QEDF_DEFAULT_PRIO;
+ } else
+ qedf->prio = tmp_prio;
+
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_UP &&
+ !qedf_dcbx_no_wait) {
+ if (atomic_read(&qedf->link_down_tmo_valid) > 0)
+ queue_delayed_work(qedf->link_update_wq,
+ &qedf->link_recovery, 0);
+ else
+ queue_delayed_work(qedf->link_update_wq,
+ &qedf->link_update, 0);
+ atomic_set(&qedf->link_down_tmo_valid, 0);
+ }
+ }
+
+}
+
+static u32 qedf_get_login_failures(void *cookie)
+{
+ struct qedf_ctx *qedf;
+
+ qedf = (struct qedf_ctx *)cookie;
+ return qedf->flogi_failed;
+}
+
+static struct qed_fcoe_cb_ops qedf_cb_ops = {
+ .common = {
+ .link_update = qedf_link_update,
+ .bw_update = qedf_bw_update,
+ .schedule_recovery_handler = qedf_schedule_recovery_handler,
+ .dcbx_aen = qedf_dcbx_handler,
+ .get_generic_tlv_data = qedf_get_generic_tlv_data,
+ .get_protocol_tlv_data = qedf_get_protocol_tlv_data,
+ .schedule_hw_err_handler = qedf_schedule_hw_err_handler,
+ }
+};
+
+/*
+ * Various transport templates.
+ */
+
+static struct scsi_transport_template *qedf_fc_transport_template;
+static struct scsi_transport_template *qedf_fc_vport_transport_template;
+
+/*
+ * SCSI EH handlers
+ */
+static int qedf_eh_abort(struct scsi_cmnd *sc_cmd)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ struct fc_lport *lport;
+ struct qedf_ctx *qedf;
+ struct qedf_ioreq *io_req;
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct fc_rport_priv *rdata;
+ struct qedf_rport *fcport = NULL;
+ int rc = FAILED;
+ int wait_count = 100;
+ int refcount = 0;
+ int rval;
+ int got_ref = 0;
+
+ lport = shost_priv(sc_cmd->device->host);
+ qedf = (struct qedf_ctx *)lport_priv(lport);
+
+ /* rport and tgt are allocated together, so tgt should be non-NULL */
+ fcport = (struct qedf_rport *)&rp[1];
+ rdata = fcport->rdata;
+ if (!rdata || !kref_get_unless_zero(&rdata->kref)) {
+ QEDF_ERR(&qedf->dbg_ctx, "stale rport, sc_cmd=%p\n", sc_cmd);
+ rc = SUCCESS;
+ goto out;
+ }
+
+
+ io_req = qedf_priv(sc_cmd)->io_req;
+ if (!io_req) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "sc_cmd not queued with lld, sc_cmd=%p op=0x%02x, port_id=%06x\n",
+ sc_cmd, sc_cmd->cmnd[0],
+ rdata->ids.port_id);
+ rc = SUCCESS;
+ goto drop_rdata_kref;
+ }
+
+ rval = kref_get_unless_zero(&io_req->refcount); /* ID: 005 */
+ if (rval)
+ got_ref = 1;
+
+ /* If we got a valid io_req, confirm it belongs to this sc_cmd. */
+ if (!rval || io_req->sc_cmd != sc_cmd) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Freed/Incorrect io_req, io_req->sc_cmd=%p, sc_cmd=%p, port_id=%06x, bailing out.\n",
+ io_req->sc_cmd, sc_cmd, rdata->ids.port_id);
+
+ goto drop_rdata_kref;
+ }
+
+ if (fc_remote_port_chkready(rport)) {
+ refcount = kref_read(&io_req->refcount);
+ QEDF_ERR(&qedf->dbg_ctx,
+ "rport not ready, io_req=%p, xid=0x%x sc_cmd=%p op=0x%02x, refcount=%d, port_id=%06x\n",
+ io_req, io_req->xid, sc_cmd, sc_cmd->cmnd[0],
+ refcount, rdata->ids.port_id);
+
+ goto drop_rdata_kref;
+ }
+
+ rc = fc_block_rport(rport);
+ if (rc)
+ goto drop_rdata_kref;
+
+ if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Connection uploading, xid=0x%x., port_id=%06x\n",
+ io_req->xid, rdata->ids.port_id);
+ while (io_req->sc_cmd && (wait_count != 0)) {
+ msleep(100);
+ wait_count--;
+ }
+ if (wait_count) {
+ QEDF_ERR(&qedf->dbg_ctx, "ABTS succeeded\n");
+ rc = SUCCESS;
+ } else {
+ QEDF_ERR(&qedf->dbg_ctx, "ABTS failed\n");
+ rc = FAILED;
+ }
+ goto drop_rdata_kref;
+ }
+
+ if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
+ QEDF_ERR(&qedf->dbg_ctx, "link not ready.\n");
+ goto drop_rdata_kref;
+ }
+
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Aborting io_req=%p sc_cmd=%p xid=0x%x fp_idx=%d, port_id=%06x.\n",
+ io_req, sc_cmd, io_req->xid, io_req->fp_idx,
+ rdata->ids.port_id);
+
+ if (qedf->stop_io_on_error) {
+ qedf_stop_all_io(qedf);
+ rc = SUCCESS;
+ goto drop_rdata_kref;
+ }
+
+ init_completion(&io_req->abts_done);
+ rval = qedf_initiate_abts(io_req, true);
+ if (rval) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n");
+ /*
+ * If we fail to queue the ABTS then return this command to
+ * the SCSI layer as it will own and free the xid
+ */
+ rc = SUCCESS;
+ qedf_scsi_done(qedf, io_req, DID_ERROR);
+ goto drop_rdata_kref;
+ }
+
+ wait_for_completion(&io_req->abts_done);
+
+ if (io_req->event == QEDF_IOREQ_EV_ABORT_SUCCESS ||
+ io_req->event == QEDF_IOREQ_EV_ABORT_FAILED ||
+ io_req->event == QEDF_IOREQ_EV_CLEANUP_SUCCESS) {
+ /*
+ * If we get a reponse to the abort this is success from
+ * the perspective that all references to the command have
+ * been removed from the driver and firmware
+ */
+ rc = SUCCESS;
+ } else {
+ /* If the abort and cleanup failed then return a failure */
+ rc = FAILED;
+ }
+
+ if (rc == SUCCESS)
+ QEDF_ERR(&(qedf->dbg_ctx), "ABTS succeeded, xid=0x%x.\n",
+ io_req->xid);
+ else
+ QEDF_ERR(&(qedf->dbg_ctx), "ABTS failed, xid=0x%x.\n",
+ io_req->xid);
+
+drop_rdata_kref:
+ kref_put(&rdata->kref, fc_rport_destroy);
+out:
+ if (got_ref)
+ kref_put(&io_req->refcount, qedf_release_cmd);
+ return rc;
+}
+
+static int qedf_eh_target_reset(struct scsi_cmnd *sc_cmd)
+{
+ struct scsi_target *starget = scsi_target(sc_cmd->device);
+ struct fc_rport *rport = starget_to_rport(starget);
+
+ QEDF_ERR(NULL, "TARGET RESET Issued...");
+ return qedf_initiate_tmf(rport, 0, FCP_TMF_TGT_RESET);
+}
+
+static int qedf_eh_device_reset(struct scsi_cmnd *sc_cmd)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+
+ QEDF_ERR(NULL, "LUN RESET Issued...\n");
+ return qedf_initiate_tmf(rport, sc_cmd->device->lun, FCP_TMF_LUN_RESET);
+}
+
+bool qedf_wait_for_upload(struct qedf_ctx *qedf)
+{
+ struct qedf_rport *fcport;
+ int wait_cnt = 120;
+
+ while (wait_cnt--) {
+ if (atomic_read(&qedf->num_offloads))
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Waiting for all uploads to complete num_offloads = 0x%x.\n",
+ atomic_read(&qedf->num_offloads));
+ else
+ return true;
+ msleep(500);
+ }
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(fcport, &qedf->fcports, peers) {
+ if (test_bit(QEDF_RPORT_SESSION_READY,
+ &fcport->flags)) {
+ if (fcport->rdata)
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Waiting for fcport %p portid=%06x.\n",
+ fcport, fcport->rdata->ids.port_id);
+ } else {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Waiting for fcport %p.\n", fcport);
+ }
+ }
+
+ rcu_read_unlock();
+ return false;
+}
+
+/* Performs soft reset of qedf_ctx by simulating a link down/up */
+void qedf_ctx_soft_reset(struct fc_lport *lport)
+{
+ struct qedf_ctx *qedf;
+ struct qed_link_output if_link;
+
+ qedf = lport_priv(lport);
+
+ if (lport->vport) {
+ clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags);
+ printk_ratelimited("Cannot issue host reset on NPIV port.\n");
+ return;
+ }
+
+ qedf->flogi_pending = 0;
+ /* For host reset, essentially do a soft link up/down */
+ atomic_set(&qedf->link_state, QEDF_LINK_DOWN);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Queuing link down work.\n");
+ queue_delayed_work(qedf->link_update_wq, &qedf->link_update,
+ 0);
+
+ if (qedf_wait_for_upload(qedf) == false) {
+ QEDF_ERR(&qedf->dbg_ctx, "Could not upload all sessions.\n");
+ WARN_ON(atomic_read(&qedf->num_offloads));
+ }
+
+ /* Before setting link up query physical link state */
+ qed_ops->common->get_link(qedf->cdev, &if_link);
+ /* Bail if the physical link is not up */
+ if (!if_link.link_up) {
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Physical link is not up.\n");
+ clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags);
+ return;
+ }
+ /* Flush and wait to make sure link down is processed */
+ flush_delayed_work(&qedf->link_update);
+ msleep(500);
+
+ atomic_set(&qedf->link_state, QEDF_LINK_UP);
+ qedf->vlan_id = 0;
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Queue link up work.\n");
+ queue_delayed_work(qedf->link_update_wq, &qedf->link_update,
+ 0);
+ clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags);
+}
+
+/* Reset the host by gracefully logging out and then logging back in */
+static int qedf_eh_host_reset(struct scsi_cmnd *sc_cmd)
+{
+ struct fc_lport *lport;
+ struct qedf_ctx *qedf;
+
+ lport = shost_priv(sc_cmd->device->host);
+ qedf = lport_priv(lport);
+
+ if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN ||
+ test_bit(QEDF_UNLOADING, &qedf->flags))
+ return FAILED;
+
+ QEDF_ERR(&(qedf->dbg_ctx), "HOST RESET Issued...");
+
+ qedf_ctx_soft_reset(lport);
+
+ return SUCCESS;
+}
+
+static int qedf_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
+{
+ if (qedf_queue_depth) {
+ scsi_change_queue_depth(sdev, qedf_queue_depth);
+ }
+
+ return 0;
+}
+
+static const struct scsi_host_template qedf_host_template = {
+ .module = THIS_MODULE,
+ .name = QEDF_MODULE_NAME,
+ .this_id = -1,
+ .cmd_per_lun = 32,
+ .max_sectors = 0xffff,
+ .queuecommand = qedf_queuecommand,
+ .shost_groups = qedf_host_groups,
+ .eh_abort_handler = qedf_eh_abort,
+ .eh_device_reset_handler = qedf_eh_device_reset, /* lun reset */
+ .eh_target_reset_handler = qedf_eh_target_reset, /* target reset */
+ .eh_host_reset_handler = qedf_eh_host_reset,
+ .sdev_configure = qedf_sdev_configure,
+ .dma_boundary = QED_HW_DMA_BOUNDARY,
+ .sg_tablesize = QEDF_MAX_BDS_PER_CMD,
+ .can_queue = FCOE_PARAMS_NUM_TASKS,
+ .change_queue_depth = scsi_change_queue_depth,
+ .cmd_size = sizeof(struct qedf_cmd_priv),
+};
+
+static int qedf_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+{
+ int rc;
+
+ spin_lock(&qedf_global_lock);
+ rc = fcoe_get_paged_crc_eof(skb, tlen, &qedf_global);
+ spin_unlock(&qedf_global_lock);
+
+ return rc;
+}
+
+static struct qedf_rport *qedf_fcport_lookup(struct qedf_ctx *qedf, u32 port_id)
+{
+ struct qedf_rport *fcport;
+ struct fc_rport_priv *rdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(fcport, &qedf->fcports, peers) {
+ rdata = fcport->rdata;
+ if (rdata == NULL)
+ continue;
+ if (rdata->ids.port_id == port_id) {
+ rcu_read_unlock();
+ return fcport;
+ }
+ }
+ rcu_read_unlock();
+
+ /* Return NULL to caller to let them know fcport was not found */
+ return NULL;
+}
+
+/* Transmits an ELS frame over an offloaded session */
+static int qedf_xmit_l2_frame(struct qedf_rport *fcport, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ int rc = 0;
+
+ fh = fc_frame_header_get(fp);
+ if ((fh->fh_type == FC_TYPE_ELS) &&
+ (fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_ADISC:
+ qedf_send_adisc(fcport, fp);
+ rc = 1;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * qedf_xmit - qedf FCoE frame transmit function
+ */
+static int qedf_xmit(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_lport *base_lport;
+ struct qedf_ctx *qedf;
+ struct ethhdr *eh;
+ struct fcoe_crc_eof *cp;
+ struct sk_buff *skb;
+ struct fc_frame_header *fh;
+ struct fcoe_hdr *hp;
+ u8 sof, eof;
+ u32 crc;
+ unsigned int hlen, tlen, elen;
+ int wlen;
+ struct fc_lport *tmp_lport;
+ struct fc_lport *vn_port = NULL;
+ struct qedf_rport *fcport;
+ int rc;
+ u16 vlan_tci = 0;
+
+ qedf = (struct qedf_ctx *)lport_priv(lport);
+
+ fh = fc_frame_header_get(fp);
+ skb = fp_skb(fp);
+
+ /* Filter out traffic to other NPIV ports on the same host */
+ if (lport->vport)
+ base_lport = shost_priv(vport_to_shost(lport->vport));
+ else
+ base_lport = lport;
+
+ /* Flag if the destination is the base port */
+ if (base_lport->port_id == ntoh24(fh->fh_d_id)) {
+ vn_port = base_lport;
+ } else {
+ /* Got through the list of vports attached to the base_lport
+ * and see if we have a match with the destination address.
+ */
+ list_for_each_entry(tmp_lport, &base_lport->vports, list) {
+ if (tmp_lport->port_id == ntoh24(fh->fh_d_id)) {
+ vn_port = tmp_lport;
+ break;
+ }
+ }
+ }
+ if (vn_port && ntoh24(fh->fh_d_id) != FC_FID_FLOGI) {
+ struct fc_rport_priv *rdata = NULL;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
+ "Dropping FCoE frame to %06x.\n", ntoh24(fh->fh_d_id));
+ kfree_skb(skb);
+ rdata = fc_rport_lookup(lport, ntoh24(fh->fh_d_id));
+ if (rdata) {
+ rdata->retries = lport->max_rport_retry_count;
+ kref_put(&rdata->kref, fc_rport_destroy);
+ }
+ return -EINVAL;
+ }
+ /* End NPIV filtering */
+
+ if (!qedf->ctlr.sel_fcf) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (!test_bit(QEDF_LL2_STARTED, &qedf->flags)) {
+ QEDF_WARN(&(qedf->dbg_ctx), "LL2 not started\n");
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) {
+ QEDF_WARN(&(qedf->dbg_ctx), "qedf link down\n");
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
+ if (fcoe_ctlr_els_send(&qedf->ctlr, lport, skb))
+ return 0;
+ }
+
+ /* Check to see if this needs to be sent on an offloaded session */
+ fcport = qedf_fcport_lookup(qedf, ntoh24(fh->fh_d_id));
+
+ if (fcport && test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ rc = qedf_xmit_l2_frame(fcport, fp);
+ /*
+ * If the frame was successfully sent over the middle path
+ * then do not try to also send it over the LL2 path
+ */
+ if (rc)
+ return 0;
+ }
+
+ sof = fr_sof(fp);
+ eof = fr_eof(fp);
+
+ elen = sizeof(struct ethhdr);
+ hlen = sizeof(struct fcoe_hdr);
+ tlen = sizeof(struct fcoe_crc_eof);
+ wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
+
+ skb->ip_summed = CHECKSUM_NONE;
+ crc = fcoe_fc_crc(fp);
+
+ /* copy port crc and eof to the skb buff */
+ if (skb_is_nonlinear(skb)) {
+ skb_frag_t *frag;
+
+ if (qedf_get_paged_crc_eof(skb, tlen)) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
+ cp = kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag);
+ } else {
+ cp = skb_put(skb, tlen);
+ }
+
+ memset(cp, 0, sizeof(*cp));
+ cp->fcoe_eof = eof;
+ cp->fcoe_crc32 = cpu_to_le32(~crc);
+ if (skb_is_nonlinear(skb)) {
+ kunmap_atomic(cp);
+ cp = NULL;
+ }
+
+
+ /* adjust skb network/transport offsets to match mac/fcoe/port */
+ skb_push(skb, elen + hlen);
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb->mac_len = elen;
+ skb->protocol = htons(ETH_P_FCOE);
+
+ /*
+ * Add VLAN tag to non-offload FCoE frame based on current stored VLAN
+ * for FIP/FCoE traffic.
+ */
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), qedf->vlan_id);
+
+ /* fill up mac and fcoe headers */
+ eh = eth_hdr(skb);
+ eh->h_proto = htons(ETH_P_FCOE);
+ if (qedf->ctlr.map_dest)
+ fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
+ else
+ /* insert GW address */
+ ether_addr_copy(eh->h_dest, qedf->ctlr.dest_addr);
+
+ /* Set the source MAC address */
+ ether_addr_copy(eh->h_source, qedf->data_src_addr);
+
+ hp = (struct fcoe_hdr *)(eh + 1);
+ memset(hp, 0, sizeof(*hp));
+ if (FC_FCOE_VER)
+ FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
+ hp->fcoe_sof = sof;
+
+ /*update tx stats */
+ this_cpu_inc(lport->stats->TxFrames);
+ this_cpu_add(lport->stats->TxWords, wlen);
+
+ /* Get VLAN ID from skb for printing purposes */
+ __vlan_hwaccel_get_tag(skb, &vlan_tci);
+
+ /* send down to lld */
+ fr_dev(fp) = lport;
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FCoE frame send: "
+ "src=%06x dest=%06x r_ctl=%x type=%x vlan=%04x.\n",
+ ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl, fh->fh_type,
+ vlan_tci);
+ if (qedf_dump_frames)
+ print_hex_dump(KERN_WARNING, "fcoe: ", DUMP_PREFIX_OFFSET, 16,
+ 1, skb->data, skb->len, false);
+ rc = qed_ops->ll2->start_xmit(qedf->cdev, skb, 0);
+ if (rc) {
+ QEDF_ERR(&qedf->dbg_ctx, "start_xmit failed rc = %d.\n", rc);
+ kfree_skb(skb);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qedf_alloc_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport)
+{
+ int rval = 0;
+ u32 *pbl;
+ dma_addr_t page;
+ int num_pages;
+
+ /* Calculate appropriate queue and PBL sizes */
+ fcport->sq_mem_size = SQ_NUM_ENTRIES * sizeof(struct fcoe_wqe);
+ fcport->sq_mem_size = ALIGN(fcport->sq_mem_size, QEDF_PAGE_SIZE);
+ fcport->sq_pbl_size = (fcport->sq_mem_size / QEDF_PAGE_SIZE) *
+ sizeof(void *);
+ fcport->sq_pbl_size = fcport->sq_pbl_size + QEDF_PAGE_SIZE;
+
+ fcport->sq = dma_alloc_coherent(&qedf->pdev->dev, fcport->sq_mem_size,
+ &fcport->sq_dma, GFP_KERNEL);
+ if (!fcport->sq) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send queue.\n");
+ rval = 1;
+ goto out;
+ }
+
+ fcport->sq_pbl = dma_alloc_coherent(&qedf->pdev->dev,
+ fcport->sq_pbl_size,
+ &fcport->sq_pbl_dma, GFP_KERNEL);
+ if (!fcport->sq_pbl) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send queue PBL.\n");
+ rval = 1;
+ goto out_free_sq;
+ }
+
+ /* Create PBL */
+ num_pages = fcport->sq_mem_size / QEDF_PAGE_SIZE;
+ page = fcport->sq_dma;
+ pbl = (u32 *)fcport->sq_pbl;
+
+ while (num_pages--) {
+ *pbl = U64_LO(page);
+ pbl++;
+ *pbl = U64_HI(page);
+ pbl++;
+ page += QEDF_PAGE_SIZE;
+ }
+
+ return rval;
+
+out_free_sq:
+ dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size, fcport->sq,
+ fcport->sq_dma);
+out:
+ return rval;
+}
+
+static void qedf_free_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport)
+{
+ if (fcport->sq_pbl)
+ dma_free_coherent(&qedf->pdev->dev, fcport->sq_pbl_size,
+ fcport->sq_pbl, fcport->sq_pbl_dma);
+ if (fcport->sq)
+ dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size,
+ fcport->sq, fcport->sq_dma);
+}
+
+static int qedf_offload_connection(struct qedf_ctx *qedf,
+ struct qedf_rport *fcport)
+{
+ struct qed_fcoe_params_offload conn_info;
+ u32 port_id;
+ int rval;
+ uint16_t total_sqe = (fcport->sq_mem_size / sizeof(struct fcoe_wqe));
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offloading connection "
+ "portid=%06x.\n", fcport->rdata->ids.port_id);
+ rval = qed_ops->acquire_conn(qedf->cdev, &fcport->handle,
+ &fcport->fw_cid, &fcport->p_doorbell);
+ if (rval) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Could not acquire connection "
+ "for portid=%06x.\n", fcport->rdata->ids.port_id);
+ rval = 1; /* For some reason qed returns 0 on failure here */
+ goto out;
+ }
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "portid=%06x "
+ "fw_cid=%08x handle=%d.\n", fcport->rdata->ids.port_id,
+ fcport->fw_cid, fcport->handle);
+
+ memset(&conn_info, 0, sizeof(struct qed_fcoe_params_offload));
+
+ /* Fill in the offload connection info */
+ conn_info.sq_pbl_addr = fcport->sq_pbl_dma;
+
+ conn_info.sq_curr_page_addr = (dma_addr_t)(*(u64 *)fcport->sq_pbl);
+ conn_info.sq_next_page_addr =
+ (dma_addr_t)(*(u64 *)(fcport->sq_pbl + 8));
+
+ /* Need to use our FCoE MAC for the offload session */
+ ether_addr_copy(conn_info.src_mac, qedf->data_src_addr);
+
+ ether_addr_copy(conn_info.dst_mac, qedf->ctlr.dest_addr);
+
+ conn_info.tx_max_fc_pay_len = fcport->rdata->maxframe_size;
+ conn_info.e_d_tov_timer_val = qedf->lport->e_d_tov;
+ conn_info.rec_tov_timer_val = 3; /* I think this is what E3 was */
+ conn_info.rx_max_fc_pay_len = fcport->rdata->maxframe_size;
+
+ /* Set VLAN data */
+ conn_info.vlan_tag = qedf->vlan_id <<
+ FCOE_CONN_OFFLOAD_RAMROD_DATA_VLAN_ID_SHIFT;
+ conn_info.vlan_tag |=
+ qedf->prio << FCOE_CONN_OFFLOAD_RAMROD_DATA_PRIORITY_SHIFT;
+ conn_info.flags |= (FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_MASK <<
+ FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_SHIFT);
+
+ /* Set host port source id */
+ port_id = fc_host_port_id(qedf->lport->host);
+ fcport->sid = port_id;
+ conn_info.s_id.addr_hi = (port_id & 0x000000FF);
+ conn_info.s_id.addr_mid = (port_id & 0x0000FF00) >> 8;
+ conn_info.s_id.addr_lo = (port_id & 0x00FF0000) >> 16;
+
+ conn_info.max_conc_seqs_c3 = fcport->rdata->max_seq;
+
+ /* Set remote port destination id */
+ port_id = fcport->rdata->rport->port_id;
+ conn_info.d_id.addr_hi = (port_id & 0x000000FF);
+ conn_info.d_id.addr_mid = (port_id & 0x0000FF00) >> 8;
+ conn_info.d_id.addr_lo = (port_id & 0x00FF0000) >> 16;
+
+ conn_info.def_q_idx = 0; /* Default index for send queue? */
+
+ /* Set FC-TAPE specific flags if needed */
+ if (fcport->dev_type == QEDF_RPORT_TYPE_TAPE) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN,
+ "Enable CONF, REC for portid=%06x.\n",
+ fcport->rdata->ids.port_id);
+ conn_info.flags |= 1 <<
+ FCOE_CONN_OFFLOAD_RAMROD_DATA_B_CONF_REQ_SHIFT;
+ conn_info.flags |=
+ ((fcport->rdata->sp_features & FC_SP_FT_SEQC) ? 1 : 0) <<
+ FCOE_CONN_OFFLOAD_RAMROD_DATA_B_REC_VALID_SHIFT;
+ }
+
+ rval = qed_ops->offload_conn(qedf->cdev, fcport->handle, &conn_info);
+ if (rval) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Could not offload connection "
+ "for portid=%06x.\n", fcport->rdata->ids.port_id);
+ goto out_free_conn;
+ } else
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offload "
+ "succeeded portid=%06x total_sqe=%d.\n",
+ fcport->rdata->ids.port_id, total_sqe);
+
+ spin_lock_init(&fcport->rport_lock);
+ atomic_set(&fcport->free_sqes, total_sqe);
+ return 0;
+out_free_conn:
+ qed_ops->release_conn(qedf->cdev, fcport->handle);
+out:
+ return rval;
+}
+
+#define QEDF_TERM_BUFF_SIZE 10
+static void qedf_upload_connection(struct qedf_ctx *qedf,
+ struct qedf_rport *fcport)
+{
+ void *term_params;
+ dma_addr_t term_params_dma;
+
+ /* Term params needs to be a DMA coherent buffer as qed shared the
+ * physical DMA address with the firmware. The buffer may be used in
+ * the receive path so we may eventually have to move this.
+ */
+ term_params = dma_alloc_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE,
+ &term_params_dma, GFP_KERNEL);
+ if (!term_params)
+ return;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Uploading connection "
+ "port_id=%06x.\n", fcport->rdata->ids.port_id);
+
+ qed_ops->destroy_conn(qedf->cdev, fcport->handle, term_params_dma);
+ qed_ops->release_conn(qedf->cdev, fcport->handle);
+
+ dma_free_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE, term_params,
+ term_params_dma);
+}
+
+static void qedf_cleanup_fcport(struct qedf_ctx *qedf,
+ struct qedf_rport *fcport)
+{
+ struct fc_rport_priv *rdata = fcport->rdata;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Cleaning up portid=%06x.\n",
+ fcport->rdata->ids.port_id);
+
+ /* Flush any remaining i/o's before we upload the connection */
+ qedf_flush_active_ios(fcport, -1);
+
+ if (test_and_clear_bit(QEDF_RPORT_SESSION_READY, &fcport->flags))
+ qedf_upload_connection(qedf, fcport);
+ qedf_free_sq(qedf, fcport);
+ fcport->rdata = NULL;
+ fcport->qedf = NULL;
+ kref_put(&rdata->kref, fc_rport_destroy);
+}
+
+/*
+ * This event_callback is called after successful completion of libfc
+ * initiated target login. qedf can proceed with initiating the session
+ * establishment.
+ */
+static void qedf_rport_event_handler(struct fc_lport *lport,
+ struct fc_rport_priv *rdata,
+ enum fc_rport_event event)
+{
+ struct qedf_ctx *qedf = lport_priv(lport);
+ struct fc_rport *rport = rdata->rport;
+ struct fc_rport_libfc_priv *rp;
+ struct qedf_rport *fcport;
+ u32 port_id;
+ int rval;
+ unsigned long flags;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "event = %d, "
+ "port_id = 0x%x\n", event, rdata->ids.port_id);
+
+ switch (event) {
+ case RPORT_EV_READY:
+ if (!rport) {
+ QEDF_WARN(&(qedf->dbg_ctx), "rport is NULL.\n");
+ break;
+ }
+
+ rp = rport->dd_data;
+ fcport = (struct qedf_rport *)&rp[1];
+ fcport->qedf = qedf;
+
+ if (atomic_read(&qedf->num_offloads) >= QEDF_MAX_SESSIONS) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Not offloading "
+ "portid=0x%x as max number of offloaded sessions "
+ "reached.\n", rdata->ids.port_id);
+ return;
+ }
+
+ /*
+ * Don't try to offload the session again. Can happen when we
+ * get an ADISC
+ */
+ if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Session already "
+ "offloaded, portid=0x%x.\n",
+ rdata->ids.port_id);
+ return;
+ }
+
+ if (rport->port_id == FC_FID_DIR_SERV) {
+ /*
+ * qedf_rport structure doesn't exist for
+ * directory server.
+ * We should not come here, as lport will
+ * take care of fabric login
+ */
+ QEDF_WARN(&(qedf->dbg_ctx), "rport struct does not "
+ "exist for dir server port_id=%x\n",
+ rdata->ids.port_id);
+ break;
+ }
+
+ if (rdata->spp_type != FC_TYPE_FCP) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Not offloading since spp type isn't FCP\n");
+ break;
+ }
+ if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Not FCP target so not offloading\n");
+ break;
+ }
+
+ /* Initial reference held on entry, so this can't fail */
+ kref_get(&rdata->kref);
+ fcport->rdata = rdata;
+ fcport->rport = rport;
+
+ rval = qedf_alloc_sq(qedf, fcport);
+ if (rval) {
+ qedf_cleanup_fcport(qedf, fcport);
+ break;
+ }
+
+ /* Set device type */
+ if (rdata->flags & FC_RP_FLAGS_RETRY &&
+ rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET &&
+ !(rdata->ids.roles & FC_RPORT_ROLE_FCP_INITIATOR)) {
+ fcport->dev_type = QEDF_RPORT_TYPE_TAPE;
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "portid=%06x is a TAPE device.\n",
+ rdata->ids.port_id);
+ } else {
+ fcport->dev_type = QEDF_RPORT_TYPE_DISK;
+ }
+
+ rval = qedf_offload_connection(qedf, fcport);
+ if (rval) {
+ qedf_cleanup_fcport(qedf, fcport);
+ break;
+ }
+
+ /* Add fcport to list of qedf_ctx list of offloaded ports */
+ spin_lock_irqsave(&qedf->hba_lock, flags);
+ list_add_rcu(&fcport->peers, &qedf->fcports);
+ spin_unlock_irqrestore(&qedf->hba_lock, flags);
+
+ /*
+ * Set the session ready bit to let everyone know that this
+ * connection is ready for I/O
+ */
+ set_bit(QEDF_RPORT_SESSION_READY, &fcport->flags);
+ atomic_inc(&qedf->num_offloads);
+
+ break;
+ case RPORT_EV_LOGO:
+ case RPORT_EV_FAILED:
+ case RPORT_EV_STOP:
+ port_id = rdata->ids.port_id;
+ if (port_id == FC_FID_DIR_SERV)
+ break;
+
+ if (rdata->spp_type != FC_TYPE_FCP) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "No action since spp type isn't FCP\n");
+ break;
+ }
+ if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "Not FCP target so no action\n");
+ break;
+ }
+
+ if (!rport) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
+ "port_id=%x - rport notcreated Yet!!\n", port_id);
+ break;
+ }
+ rp = rport->dd_data;
+ /*
+ * Perform session upload. Note that rdata->peers is already
+ * removed from disc->rports list before we get this event.
+ */
+ fcport = (struct qedf_rport *)&rp[1];
+
+ spin_lock_irqsave(&fcport->rport_lock, flags);
+ /* Only free this fcport if it is offloaded already */
+ if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) &&
+ !test_bit(QEDF_RPORT_UPLOADING_CONNECTION,
+ &fcport->flags)) {
+ set_bit(QEDF_RPORT_UPLOADING_CONNECTION,
+ &fcport->flags);
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ qedf_cleanup_fcport(qedf, fcport);
+ /*
+ * Remove fcport to list of qedf_ctx list of offloaded
+ * ports
+ */
+ spin_lock_irqsave(&qedf->hba_lock, flags);
+ list_del_rcu(&fcport->peers);
+ spin_unlock_irqrestore(&qedf->hba_lock, flags);
+
+ clear_bit(QEDF_RPORT_UPLOADING_CONNECTION,
+ &fcport->flags);
+ atomic_dec(&qedf->num_offloads);
+ } else {
+ spin_unlock_irqrestore(&fcport->rport_lock, flags);
+ }
+ break;
+
+ case RPORT_EV_NONE:
+ break;
+ }
+}
+
+static void qedf_abort_io(struct fc_lport *lport)
+{
+ /* NO-OP but need to fill in the template */
+}
+
+static void qedf_fcp_cleanup(struct fc_lport *lport)
+{
+ /*
+ * NO-OP but need to fill in template to prevent a NULL
+ * function pointer dereference during link down. I/Os
+ * will be flushed when port is uploaded.
+ */
+}
+
+static struct libfc_function_template qedf_lport_template = {
+ .frame_send = qedf_xmit,
+ .fcp_abort_io = qedf_abort_io,
+ .fcp_cleanup = qedf_fcp_cleanup,
+ .rport_event_callback = qedf_rport_event_handler,
+ .elsct_send = qedf_elsct_send,
+};
+
+static void qedf_fcoe_ctlr_setup(struct qedf_ctx *qedf)
+{
+ fcoe_ctlr_init(&qedf->ctlr, FIP_MODE_AUTO);
+
+ qedf->ctlr.send = qedf_fip_send;
+ qedf->ctlr.get_src_addr = qedf_get_src_mac;
+ ether_addr_copy(qedf->ctlr.ctl_src_addr, qedf->mac);
+}
+
+static void qedf_setup_fdmi(struct qedf_ctx *qedf)
+{
+ struct fc_lport *lport = qedf->lport;
+ u8 buf[8];
+ int pos;
+ uint32_t i;
+
+ /*
+ * fdmi_enabled needs to be set for libfc
+ * to execute FDMI registration
+ */
+ lport->fdmi_enabled = 1;
+
+ /*
+ * Setup the necessary fc_host attributes to that will be used to fill
+ * in the FDMI information.
+ */
+
+ /* Get the PCI-e Device Serial Number Capability */
+ pos = pci_find_ext_capability(qedf->pdev, PCI_EXT_CAP_ID_DSN);
+ if (pos) {
+ pos += 4;
+ for (i = 0; i < 8; i++)
+ pci_read_config_byte(qedf->pdev, pos + i, &buf[i]);
+
+ snprintf(fc_host_serial_number(lport->host),
+ FC_SERIAL_NUMBER_SIZE,
+ "%02X%02X%02X%02X%02X%02X%02X%02X",
+ buf[7], buf[6], buf[5], buf[4],
+ buf[3], buf[2], buf[1], buf[0]);
+ } else
+ snprintf(fc_host_serial_number(lport->host),
+ FC_SERIAL_NUMBER_SIZE, "Unknown");
+
+ snprintf(fc_host_manufacturer(lport->host),
+ FC_SERIAL_NUMBER_SIZE, "%s", "Marvell Semiconductor Inc.");
+
+ if (qedf->pdev->device == QL45xxx) {
+ snprintf(fc_host_model(lport->host),
+ FC_SYMBOLIC_NAME_SIZE, "%s", "QL45xxx");
+
+ snprintf(fc_host_model_description(lport->host),
+ FC_SYMBOLIC_NAME_SIZE, "%s",
+ "Marvell FastLinQ QL45xxx FCoE Adapter");
+ }
+
+ if (qedf->pdev->device == QL41xxx) {
+ snprintf(fc_host_model(lport->host),
+ FC_SYMBOLIC_NAME_SIZE, "%s", "QL41xxx");
+
+ snprintf(fc_host_model_description(lport->host),
+ FC_SYMBOLIC_NAME_SIZE, "%s",
+ "Marvell FastLinQ QL41xxx FCoE Adapter");
+ }
+
+ snprintf(fc_host_hardware_version(lport->host),
+ FC_VERSION_STRING_SIZE, "Rev %d", qedf->pdev->revision);
+
+ snprintf(fc_host_driver_version(lport->host),
+ FC_VERSION_STRING_SIZE, "%s", QEDF_VERSION);
+
+ snprintf(fc_host_firmware_version(lport->host),
+ FC_VERSION_STRING_SIZE, "%d.%d.%d.%d",
+ FW_MAJOR_VERSION, FW_MINOR_VERSION, FW_REVISION_VERSION,
+ FW_ENGINEERING_VERSION);
+
+ snprintf(fc_host_vendor_identifier(lport->host),
+ FC_VENDOR_IDENTIFIER, "%s", "Marvell");
+
+}
+
+static int qedf_lport_setup(struct qedf_ctx *qedf)
+{
+ struct fc_lport *lport = qedf->lport;
+
+ lport->link_up = 0;
+ lport->max_retry_count = QEDF_FLOGI_RETRY_CNT;
+ lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT;
+ lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+ FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+ lport->boot_time = jiffies;
+ lport->e_d_tov = 2 * 1000;
+ lport->r_a_tov = 10 * 1000;
+
+ /* Set NPIV support */
+ lport->does_npiv = 1;
+ fc_host_max_npiv_vports(lport->host) = QEDF_MAX_NPIV;
+
+ fc_set_wwnn(lport, qedf->wwnn);
+ fc_set_wwpn(lport, qedf->wwpn);
+
+ if (fcoe_libfc_config(lport, &qedf->ctlr, &qedf_lport_template, 0)) {
+ QEDF_ERR(&qedf->dbg_ctx,
+ "fcoe_libfc_config failed.\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate the exchange manager */
+ fc_exch_mgr_alloc(lport, FC_CLASS_3, FCOE_PARAMS_NUM_TASKS,
+ 0xfffe, NULL);
+
+ if (fc_lport_init_stats(lport))
+ return -ENOMEM;
+
+ /* Finish lport config */
+ fc_lport_config(lport);
+
+ /* Set max frame size */
+ fc_set_mfs(lport, QEDF_MFS);
+ fc_host_maxframe_size(lport->host) = lport->mfs;
+
+ /* Set default dev_loss_tmo based on module parameter */
+ fc_host_dev_loss_tmo(lport->host) = qedf_dev_loss_tmo;
+
+ /* Set symbolic node name */
+ if (qedf->pdev->device == QL45xxx)
+ snprintf(fc_host_symbolic_name(lport->host), 256,
+ "Marvell FastLinQ 45xxx FCoE v%s", QEDF_VERSION);
+
+ if (qedf->pdev->device == QL41xxx)
+ snprintf(fc_host_symbolic_name(lport->host), 256,
+ "Marvell FastLinQ 41xxx FCoE v%s", QEDF_VERSION);
+
+ qedf_setup_fdmi(qedf);
+
+ return 0;
+}
+
+/*
+ * NPIV functions
+ */
+
+static int qedf_vport_libfc_config(struct fc_vport *vport,
+ struct fc_lport *lport)
+{
+ lport->link_up = 0;
+ lport->qfull = 0;
+ lport->max_retry_count = QEDF_FLOGI_RETRY_CNT;
+ lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT;
+ lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+ FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+ lport->boot_time = jiffies;
+ lport->e_d_tov = 2 * 1000;
+ lport->r_a_tov = 10 * 1000;
+ lport->does_npiv = 1; /* Temporary until we add NPIV support */
+
+ /* Allocate stats for vport */
+ if (fc_lport_init_stats(lport))
+ return -ENOMEM;
+
+ /* Finish lport config */
+ fc_lport_config(lport);
+
+ /* offload related configuration */
+ lport->crc_offload = 0;
+ lport->seq_offload = 0;
+ lport->lro_enabled = 0;
+ lport->lro_xid = 0;
+ lport->lso_max = 0;
+
+ return 0;
+}
+
+static int qedf_vport_create(struct fc_vport *vport, bool disabled)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fc_lport *vn_port;
+ struct qedf_ctx *base_qedf = lport_priv(n_port);
+ struct qedf_ctx *vport_qedf;
+
+ char buf[32];
+ int rc = 0;
+
+ rc = fcoe_validate_vport_create(vport);
+ if (rc) {
+ fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
+ QEDF_WARN(&(base_qedf->dbg_ctx), "Failed to create vport, "
+ "WWPN (0x%s) already exists.\n", buf);
+ return rc;
+ }
+
+ if (atomic_read(&base_qedf->link_state) != QEDF_LINK_UP) {
+ QEDF_WARN(&(base_qedf->dbg_ctx), "Cannot create vport "
+ "because link is not up.\n");
+ return -EIO;
+ }
+
+ vn_port = libfc_vport_create(vport, sizeof(struct qedf_ctx));
+ if (!vn_port) {
+ QEDF_WARN(&(base_qedf->dbg_ctx), "Could not create lport "
+ "for vport.\n");
+ return -ENOMEM;
+ }
+
+ fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
+ QEDF_ERR(&(base_qedf->dbg_ctx), "Creating NPIV port, WWPN=%s.\n",
+ buf);
+
+ /* Copy some fields from base_qedf */
+ vport_qedf = lport_priv(vn_port);
+ memcpy(vport_qedf, base_qedf, sizeof(struct qedf_ctx));
+
+ /* Set qedf data specific to this vport */
+ vport_qedf->lport = vn_port;
+ /* Use same hba_lock as base_qedf */
+ vport_qedf->hba_lock = base_qedf->hba_lock;
+ vport_qedf->pdev = base_qedf->pdev;
+ vport_qedf->cmd_mgr = base_qedf->cmd_mgr;
+ init_completion(&vport_qedf->flogi_compl);
+ INIT_LIST_HEAD(&vport_qedf->fcports);
+ INIT_DELAYED_WORK(&vport_qedf->stag_work, qedf_stag_change_work);
+
+ rc = qedf_vport_libfc_config(vport, vn_port);
+ if (rc) {
+ QEDF_ERR(&(base_qedf->dbg_ctx), "Could not allocate memory "
+ "for lport stats.\n");
+ goto err;
+ }
+
+ fc_set_wwnn(vn_port, vport->node_name);
+ fc_set_wwpn(vn_port, vport->port_name);
+ vport_qedf->wwnn = vn_port->wwnn;
+ vport_qedf->wwpn = vn_port->wwpn;
+
+ vn_port->host->transportt = qedf_fc_vport_transport_template;
+ vn_port->host->can_queue = FCOE_PARAMS_NUM_TASKS;
+ vn_port->host->max_lun = qedf_max_lun;
+ vn_port->host->sg_tablesize = QEDF_MAX_BDS_PER_CMD;
+ vn_port->host->max_cmd_len = QEDF_MAX_CDB_LEN;
+ vn_port->host->max_id = QEDF_MAX_SESSIONS;
+
+ rc = scsi_add_host(vn_port->host, &vport->dev);
+ if (rc) {
+ QEDF_WARN(&base_qedf->dbg_ctx,
+ "Error adding Scsi_Host rc=0x%x.\n", rc);
+ goto err;
+ }
+
+ /* Set default dev_loss_tmo based on module parameter */
+ fc_host_dev_loss_tmo(vn_port->host) = qedf_dev_loss_tmo;
+
+ /* Init libfc stuffs */
+ memcpy(&vn_port->tt, &qedf_lport_template,
+ sizeof(qedf_lport_template));
+ fc_exch_init(vn_port);
+ fc_elsct_init(vn_port);
+ fc_lport_init(vn_port);
+ fc_disc_init(vn_port);
+ fc_disc_config(vn_port, vn_port);
+
+
+ /* Allocate the exchange manager */
+ shost = vport_to_shost(vport);
+ n_port = shost_priv(shost);
+ fc_exch_mgr_list_clone(n_port, vn_port);
+
+ /* Set max frame size */
+ fc_set_mfs(vn_port, QEDF_MFS);
+
+ fc_host_port_type(vn_port->host) = FC_PORTTYPE_UNKNOWN;
+
+ if (disabled) {
+ fc_vport_set_state(vport, FC_VPORT_DISABLED);
+ } else {
+ vn_port->boot_time = jiffies;
+ fc_fabric_login(vn_port);
+ fc_vport_setlink(vn_port);
+ }
+
+ /* Set symbolic node name */
+ if (base_qedf->pdev->device == QL45xxx)
+ snprintf(fc_host_symbolic_name(vn_port->host), 256,
+ "Marvell FastLinQ 45xxx FCoE v%s", QEDF_VERSION);
+
+ if (base_qedf->pdev->device == QL41xxx)
+ snprintf(fc_host_symbolic_name(vn_port->host), 256,
+ "Marvell FastLinQ 41xxx FCoE v%s", QEDF_VERSION);
+
+ /* Set supported speed */
+ fc_host_supported_speeds(vn_port->host) = n_port->link_supported_speeds;
+
+ /* Set speed */
+ vn_port->link_speed = n_port->link_speed;
+
+ /* Set port type */
+ fc_host_port_type(vn_port->host) = FC_PORTTYPE_NPIV;
+
+ /* Set maxframe size */
+ fc_host_maxframe_size(vn_port->host) = n_port->mfs;
+
+ QEDF_INFO(&(base_qedf->dbg_ctx), QEDF_LOG_NPIV, "vn_port=%p.\n",
+ vn_port);
+
+ /* Set up debug context for vport */
+ vport_qedf->dbg_ctx.host_no = vn_port->host->host_no;
+ vport_qedf->dbg_ctx.pdev = base_qedf->pdev;
+
+ return 0;
+
+err:
+ scsi_host_put(vn_port->host);
+ return rc;
+}
+
+static int qedf_vport_destroy(struct fc_vport *vport)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fc_lport *vn_port = vport->dd_data;
+ struct qedf_ctx *qedf = lport_priv(vn_port);
+
+ if (!qedf) {
+ QEDF_ERR(NULL, "qedf is NULL.\n");
+ goto out;
+ }
+
+ /* Set unloading bit on vport qedf_ctx to prevent more I/O */
+ set_bit(QEDF_UNLOADING, &qedf->flags);
+
+ mutex_lock(&n_port->lp_mutex);
+ list_del(&vn_port->list);
+ mutex_unlock(&n_port->lp_mutex);
+
+ fc_fabric_logoff(vn_port);
+ fc_lport_destroy(vn_port);
+
+ /* Detach from scsi-ml */
+ fc_remove_host(vn_port->host);
+ scsi_remove_host(vn_port->host);
+
+ /*
+ * Only try to release the exchange manager if the vn_port
+ * configuration is complete.
+ */
+ if (vn_port->state == LPORT_ST_READY)
+ fc_exch_mgr_free(vn_port);
+
+ /* Free memory used by statistical counters */
+ fc_lport_free_stats(vn_port);
+
+ /* Release Scsi_Host */
+ scsi_host_put(vn_port->host);
+
+out:
+ return 0;
+}
+
+static int qedf_vport_disable(struct fc_vport *vport, bool disable)
+{
+ struct fc_lport *lport = vport->dd_data;
+
+ if (disable) {
+ fc_vport_set_state(vport, FC_VPORT_DISABLED);
+ fc_fabric_logoff(lport);
+ } else {
+ lport->boot_time = jiffies;
+ fc_fabric_login(lport);
+ fc_vport_setlink(lport);
+ }
+ return 0;
+}
+
+/*
+ * During removal we need to wait for all the vports associated with a port
+ * to be destroyed so we avoid a race condition where libfc is still trying
+ * to reap vports while the driver remove function has already reaped the
+ * driver contexts associated with the physical port.
+ */
+static void qedf_wait_for_vport_destroy(struct qedf_ctx *qedf)
+{
+ struct fc_host_attrs *fc_host = shost_to_fc_host(qedf->lport->host);
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV,
+ "Entered.\n");
+ while (fc_host->npiv_vports_inuse > 0) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV,
+ "Waiting for all vports to be reaped.\n");
+ msleep(1000);
+ }
+}
+
+/**
+ * qedf_fcoe_reset - Resets the fcoe
+ *
+ * @shost: shost the reset is from
+ *
+ * Returns: always 0
+ */
+static int qedf_fcoe_reset(struct Scsi_Host *shost)
+{
+ struct fc_lport *lport = shost_priv(shost);
+
+ qedf_ctx_soft_reset(lport);
+ return 0;
+}
+
+static void qedf_get_host_port_id(struct Scsi_Host *shost)
+{
+ struct fc_lport *lport = shost_priv(shost);
+
+ fc_host_port_id(shost) = lport->port_id;
+}
+
+static struct fc_host_statistics *qedf_fc_get_host_stats(struct Scsi_Host
+ *shost)
+{
+ struct fc_host_statistics *qedf_stats;
+ struct fc_lport *lport = shost_priv(shost);
+ struct qedf_ctx *qedf = lport_priv(lport);
+ struct qed_fcoe_stats *fw_fcoe_stats;
+
+ qedf_stats = fc_get_host_stats(shost);
+
+ /* We don't collect offload stats for specific NPIV ports */
+ if (lport->vport)
+ goto out;
+
+ fw_fcoe_stats = kmalloc(sizeof(struct qed_fcoe_stats), GFP_KERNEL);
+ if (!fw_fcoe_stats) {
+ QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate memory for "
+ "fw_fcoe_stats.\n");
+ goto out;
+ }
+
+ mutex_lock(&qedf->stats_mutex);
+
+ /* Query firmware for offload stats */
+ qed_ops->get_stats(qedf->cdev, fw_fcoe_stats);
+
+ /*
+ * The expectation is that we add our offload stats to the stats
+ * being maintained by libfc each time the fc_get_host_status callback
+ * is invoked. The additions are not carried over for each call to
+ * the fc_get_host_stats callback.
+ */
+ qedf_stats->tx_frames += fw_fcoe_stats->fcoe_tx_data_pkt_cnt +
+ fw_fcoe_stats->fcoe_tx_xfer_pkt_cnt +
+ fw_fcoe_stats->fcoe_tx_other_pkt_cnt;
+ qedf_stats->rx_frames += fw_fcoe_stats->fcoe_rx_data_pkt_cnt +
+ fw_fcoe_stats->fcoe_rx_xfer_pkt_cnt +
+ fw_fcoe_stats->fcoe_rx_other_pkt_cnt;
+ qedf_stats->fcp_input_megabytes +=
+ do_div(fw_fcoe_stats->fcoe_rx_byte_cnt, 1000000);
+ qedf_stats->fcp_output_megabytes +=
+ do_div(fw_fcoe_stats->fcoe_tx_byte_cnt, 1000000);
+ qedf_stats->rx_words += fw_fcoe_stats->fcoe_rx_byte_cnt / 4;
+ qedf_stats->tx_words += fw_fcoe_stats->fcoe_tx_byte_cnt / 4;
+ qedf_stats->invalid_crc_count +=
+ fw_fcoe_stats->fcoe_silent_drop_pkt_crc_error_cnt;
+ qedf_stats->dumped_frames =
+ fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt;
+ qedf_stats->error_frames +=
+ fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt;
+ qedf_stats->fcp_input_requests += qedf->input_requests;
+ qedf_stats->fcp_output_requests += qedf->output_requests;
+ qedf_stats->fcp_control_requests += qedf->control_requests;
+ qedf_stats->fcp_packet_aborts += qedf->packet_aborts;
+ qedf_stats->fcp_frame_alloc_failures += qedf->alloc_failures;
+
+ mutex_unlock(&qedf->stats_mutex);
+ kfree(fw_fcoe_stats);
+out:
+ return qedf_stats;
+}
+
+static struct fc_function_template qedf_fc_transport_fn = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_active_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .get_host_port_id = qedf_get_host_port_id,
+ .show_host_port_id = 1,
+ .show_host_supported_speeds = 1,
+ .get_host_speed = fc_get_host_speed,
+ .show_host_speed = 1,
+ .show_host_port_type = 1,
+ .get_host_port_state = fc_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_symbolic_name = 1,
+
+ /*
+ * Tell FC transport to allocate enough space to store the backpointer
+ * for the associate qedf_rport struct.
+ */
+ .dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) +
+ sizeof(struct qedf_rport)),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .show_host_fabric_name = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+ .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .get_fc_host_stats = qedf_fc_get_host_stats,
+ .issue_fc_host_lip = qedf_fcoe_reset,
+ .vport_create = qedf_vport_create,
+ .vport_delete = qedf_vport_destroy,
+ .vport_disable = qedf_vport_disable,
+ .bsg_request = fc_lport_bsg_request,
+};
+
+static struct fc_function_template qedf_fc_vport_transport_fn = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_active_fc4s = 1,
+ .show_host_maxframe_size = 1,
+ .show_host_port_id = 1,
+ .show_host_supported_speeds = 1,
+ .get_host_speed = fc_get_host_speed,
+ .show_host_speed = 1,
+ .show_host_port_type = 1,
+ .get_host_port_state = fc_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_symbolic_name = 1,
+ .dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) +
+ sizeof(struct qedf_rport)),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .show_host_fabric_name = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+ .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .get_fc_host_stats = fc_get_host_stats,
+ .issue_fc_host_lip = qedf_fcoe_reset,
+ .bsg_request = fc_lport_bsg_request,
+};
+
+static bool qedf_fp_has_work(struct qedf_fastpath *fp)
+{
+ struct qedf_ctx *qedf = fp->qedf;
+ struct global_queue *que;
+ struct qed_sb_info *sb_info = fp->sb_info;
+ struct status_block *sb = sb_info->sb_virt;
+ u16 prod_idx;
+
+ /* Get the pointer to the global CQ this completion is on */
+ que = qedf->global_queues[fp->sb_id];
+
+ /* Be sure all responses have been written to PI */
+ rmb();
+
+ /* Get the current firmware producer index */
+ prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI];
+
+ return (que->cq_prod_idx != prod_idx);
+}
+
+/*
+ * Interrupt handler code.
+ */
+
+/* Process completion queue and copy CQE contents for deferred processesing
+ *
+ * Return true if we should wake the I/O thread, false if not.
+ */
+static bool qedf_process_completions(struct qedf_fastpath *fp)
+{
+ struct qedf_ctx *qedf = fp->qedf;
+ struct qed_sb_info *sb_info = fp->sb_info;
+ struct status_block *sb = sb_info->sb_virt;
+ struct global_queue *que;
+ u16 prod_idx;
+ struct fcoe_cqe *cqe;
+ struct qedf_io_work *io_work;
+ unsigned int cpu;
+ struct qedf_ioreq *io_req = NULL;
+ u16 xid;
+ u16 new_cqes;
+ u32 comp_type;
+
+ /* Get the current firmware producer index */
+ prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI];
+
+ /* Get the pointer to the global CQ this completion is on */
+ que = qedf->global_queues[fp->sb_id];
+
+ /* Calculate the amount of new elements since last processing */
+ new_cqes = (prod_idx >= que->cq_prod_idx) ?
+ (prod_idx - que->cq_prod_idx) :
+ 0x10000 - que->cq_prod_idx + prod_idx;
+
+ /* Save producer index */
+ que->cq_prod_idx = prod_idx;
+
+ while (new_cqes) {
+ fp->completions++;
+ cqe = &que->cq[que->cq_cons_idx];
+
+ comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) &
+ FCOE_CQE_CQE_TYPE_MASK;
+
+ /*
+ * Process unsolicited CQEs directly in the interrupt handler
+ * sine we need the fastpath ID
+ */
+ if (comp_type == FCOE_UNSOLIC_CQE_TYPE) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL,
+ "Unsolicated CQE.\n");
+ qedf_process_unsol_compl(qedf, fp->sb_id, cqe);
+ /*
+ * Don't add a work list item. Increment consumer
+ * consumer index and move on.
+ */
+ goto inc_idx;
+ }
+
+ xid = cqe->cqe_data & FCOE_CQE_TASK_ID_MASK;
+ io_req = &qedf->cmd_mgr->cmds[xid];
+
+ /*
+ * Figure out which percpu thread we should queue this I/O
+ * on.
+ */
+ if (!io_req)
+ /* If there is not io_req associated with this CQE
+ * just queue it on CPU 0
+ */
+ cpu = 0;
+ else {
+ cpu = io_req->cpu;
+ io_req->int_cpu = smp_processor_id();
+ }
+
+ io_work = mempool_alloc(qedf->io_mempool, GFP_ATOMIC);
+ if (!io_work) {
+ QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate "
+ "work for I/O completion.\n");
+ continue;
+ }
+ memset(io_work, 0, sizeof(struct qedf_io_work));
+
+ INIT_WORK(&io_work->work, qedf_fp_io_handler);
+
+ /* Copy contents of CQE for deferred processing */
+ memcpy(&io_work->cqe, cqe, sizeof(struct fcoe_cqe));
+
+ io_work->qedf = fp->qedf;
+ io_work->fp = NULL; /* Only used for unsolicited frames */
+
+ queue_work_on(cpu, qedf_io_wq, &io_work->work);
+
+inc_idx:
+ que->cq_cons_idx++;
+ if (que->cq_cons_idx == fp->cq_num_entries)
+ que->cq_cons_idx = 0;
+ new_cqes--;
+ }
+
+ return true;
+}
+
+
+/* MSI-X fastpath handler code */
+static irqreturn_t qedf_msix_handler(int irq, void *dev_id)
+{
+ struct qedf_fastpath *fp = dev_id;
+
+ if (!fp) {
+ QEDF_ERR(NULL, "fp is null.\n");
+ return IRQ_HANDLED;
+ }
+ if (!fp->sb_info) {
+ QEDF_ERR(NULL, "fp->sb_info in null.");
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Disable interrupts for this status block while we process new
+ * completions
+ */
+ qed_sb_ack(fp->sb_info, IGU_INT_DISABLE, 0 /*do not update*/);
+
+ while (1) {
+ qedf_process_completions(fp);
+
+ if (qedf_fp_has_work(fp) == 0) {
+ /* Update the sb information */
+ qed_sb_update_sb_idx(fp->sb_info);
+
+ /* Check for more work */
+ rmb();
+
+ if (qedf_fp_has_work(fp) == 0) {
+ /* Re-enable interrupts */
+ qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, 1);
+ return IRQ_HANDLED;
+ }
+ }
+ }
+
+ /* Do we ever want to break out of above loop? */
+ return IRQ_HANDLED;
+}
+
+/* simd handler for MSI/INTa */
+static void qedf_simd_int_handler(void *cookie)
+{
+ /* Cookie is qedf_ctx struct */
+ struct qedf_ctx *qedf = (struct qedf_ctx *)cookie;
+
+ QEDF_WARN(&(qedf->dbg_ctx), "qedf=%p.\n", qedf);
+}
+
+#define QEDF_SIMD_HANDLER_NUM 0
+static void qedf_sync_free_irqs(struct qedf_ctx *qedf)
+{
+ int i;
+ u16 vector_idx = 0;
+ u32 vector;
+
+ if (qedf->int_info.msix_cnt) {
+ for (i = 0; i < qedf->int_info.used_cnt; i++) {
+ vector_idx = i * qedf->dev_info.common.num_hwfns +
+ qed_ops->common->get_affin_hwfn_idx(qedf->cdev);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Freeing IRQ #%d vector_idx=%d.\n",
+ i, vector_idx);
+ vector = qedf->int_info.msix[vector_idx].vector;
+ synchronize_irq(vector);
+ irq_set_affinity_hint(vector, NULL);
+ irq_set_affinity_notifier(vector, NULL);
+ free_irq(vector, &qedf->fp_array[i]);
+ }
+ } else
+ qed_ops->common->simd_handler_clean(qedf->cdev,
+ QEDF_SIMD_HANDLER_NUM);
+
+ qedf->int_info.used_cnt = 0;
+ qed_ops->common->set_fp_int(qedf->cdev, 0);
+}
+
+static int qedf_request_msix_irq(struct qedf_ctx *qedf)
+{
+ int i, rc, cpu;
+ u16 vector_idx = 0;
+ u32 vector;
+
+ cpu = cpumask_first(cpu_online_mask);
+ for (i = 0; i < qedf->num_queues; i++) {
+ vector_idx = i * qedf->dev_info.common.num_hwfns +
+ qed_ops->common->get_affin_hwfn_idx(qedf->cdev);
+ QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
+ "Requesting IRQ #%d vector_idx=%d.\n",
+ i, vector_idx);
+ vector = qedf->int_info.msix[vector_idx].vector;
+ rc = request_irq(vector, qedf_msix_handler, 0, "qedf",
+ &qedf->fp_array[i]);
+
+ if (rc) {
+ QEDF_WARN(&(qedf->dbg_ctx), "request_irq failed.\n");
+ qedf_sync_free_irqs(qedf);
+ return rc;
+ }
+
+ qedf->int_info.used_cnt++;
+ rc = irq_set_affinity_hint(vector, get_cpu_mask(cpu));
+ cpu = cpumask_next(cpu, cpu_online_mask);
+ }
+
+ return 0;
+}
+
+static int qedf_setup_int(struct qedf_ctx *qedf)
+{
+ int rc = 0;
+
+ /*
+ * Learn interrupt configuration
+ */
+ rc = qed_ops->common->set_fp_int(qedf->cdev, num_online_cpus());
+ if (rc <= 0)
+ return 0;
+
+ rc = qed_ops->common->get_fp_int(qedf->cdev, &qedf->int_info);
+ if (rc)
+ return 0;
+
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of msix_cnt = "
+ "0x%x num of cpus = 0x%x\n", qedf->int_info.msix_cnt,
+ num_online_cpus());
+
+ if (qedf->int_info.msix_cnt)
+ return qedf_request_msix_irq(qedf);
+
+ qed_ops->common->simd_handler_config(qedf->cdev, &qedf,
+ QEDF_SIMD_HANDLER_NUM, qedf_simd_int_handler);
+ qedf->int_info.used_cnt = 1;
+
+ QEDF_ERR(&qedf->dbg_ctx,
+ "Cannot load driver due to a lack of MSI-X vectors.\n");
+ return -EINVAL;
+}
+
+/* Main function for libfc frame reception */
+static void qedf_recv_frame(struct qedf_ctx *qedf,
+ struct sk_buff *skb)
+{
+ u32 fr_len;
+ struct fc_lport *lport;
+ struct fc_frame_header *fh;
+ struct fcoe_crc_eof crc_eof;
+ struct fc_frame *fp;
+ u8 *mac = NULL;
+ u8 *dest_mac = NULL;
+ struct fcoe_hdr *hp;
+ struct qedf_rport *fcport;
+ struct fc_lport *vn_port;
+ u32 f_ctl;
+
+ lport = qedf->lport;
+ if (lport == NULL || lport->state == LPORT_ST_DISABLED) {
+ QEDF_WARN(NULL, "Invalid lport struct or lport disabled.\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ if (skb_is_nonlinear(skb))
+ skb_linearize(skb);
+ mac = eth_hdr(skb)->h_source;
+ dest_mac = eth_hdr(skb)->h_dest;
+
+ /* Pull the header */
+ hp = (struct fcoe_hdr *)skb->data;
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+ skb_pull(skb, sizeof(struct fcoe_hdr));
+ fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+ fp = (struct fc_frame *)skb;
+ fc_frame_init(fp);
+ fr_dev(fp) = lport;
+ fr_sof(fp) = hp->fcoe_sof;
+ if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+ QEDF_INFO(NULL, QEDF_LOG_LL2, "skb_copy_bits failed.\n");
+ kfree_skb(skb);
+ return;
+ }
+ fr_eof(fp) = crc_eof.fcoe_eof;
+ fr_crc(fp) = crc_eof.fcoe_crc32;
+ if (pskb_trim(skb, fr_len)) {
+ QEDF_INFO(NULL, QEDF_LOG_LL2, "pskb_trim failed.\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ fh = fc_frame_header_get(fp);
+
+ /*
+ * Invalid frame filters.
+ */
+
+ if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
+ fh->fh_type == FC_TYPE_FCP) {
+ /* Drop FCP data. We dont this in L2 path */
+ kfree_skb(skb);
+ return;
+ }
+ if (fh->fh_r_ctl == FC_RCTL_ELS_REQ &&
+ fh->fh_type == FC_TYPE_ELS) {
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_LOGO:
+ if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
+ /* drop non-FIP LOGO */
+ kfree_skb(skb);
+ return;
+ }
+ break;
+ }
+ }
+
+ if (fh->fh_r_ctl == FC_RCTL_BA_ABTS) {
+ /* Drop incoming ABTS */
+ kfree_skb(skb);
+ return;
+ }
+
+ if (ntoh24(&dest_mac[3]) != ntoh24(fh->fh_d_id)) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
+ "FC frame d_id mismatch with MAC %pM.\n", dest_mac);
+ kfree_skb(skb);
+ return;
+ }
+
+ if (qedf->ctlr.state) {
+ if (!ether_addr_equal(mac, qedf->ctlr.dest_addr)) {
+ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
+ "Wrong source address: mac:%pM dest_addr:%pM.\n",
+ mac, qedf->ctlr.dest_addr);
+ kfree_skb(skb);